一、? ? ? ? 前言
Linux加密框架是內(nèi)核安全子系統(tǒng)的重要組成部份,同時,它又一個的獨立子系統(tǒng)形式出現(xiàn),從它出現(xiàn)在內(nèi)核根目錄下的crypto/就可以看出其地位了。
Crypto實現(xiàn)較為復(fù)雜,其主要體現(xiàn)在其OOP的設(shè)計思路和高度的對像抽像與封裝模型,作者展現(xiàn)了其出色的架構(gòu)設(shè)計水準(zhǔn)和面向?qū)ο竦某橄衲芰?。本文力圖從加密框架的重要應(yīng)用,即IPSec(xfrm)的兩個重要協(xié)議AH和ESP對加密框架的使用,展現(xiàn)其設(shè)計與實現(xiàn)。
內(nèi)核版本:2.6.31.13
二、? ? ? ? 算法模版
1.? ? ? ? 模版的基本概念
算法模版是加密框架的第一個重要概念。內(nèi)核中有很多算法是動態(tài)生成的,例如cbc(des)算法。內(nèi)核并不存在這樣的算法,它事實上是cbc和des的組合,但是內(nèi)核加密框架從統(tǒng)一抽像管理的角度。將cbc(des)看做一個算法,在實際使用時動態(tài)分配并向內(nèi)核注冊該算法。這樣,可以將cbc抽像為一個模版,它可以同任意的加密算法進行組合。算法模版使用結(jié)構(gòu)crypto_template來描述,其結(jié)構(gòu)原型:
點擊(此處)折疊或打開
struct crypto_template?{
struct list_head list;?//模版鏈表成員,用于注冊
struct hlist_head instances;?//算法實例鏈表首部
struct module?*module;?//模塊指針
struct crypto_instance?*(*alloc)(struct rtattr?**tb);?//算法實例分配
void?(*free)(struct crypto_instance?*inst);?//算法實例釋放
char name[CRYPTO_MAX_ALG_NAME];?//模版名稱
};
例如,一個名為cbc的算法模版,可以用它來動態(tài)分配cbc(des),cbc(twofish)……諸如此類。
crypto/algapi.c下包含了模版的一些常用操作。最為常見的就是模版的注冊與注銷,其實質(zhì)是對以crypto_template_list為首的鏈表的操作過程:
點擊(此處)折疊或打開
static LIST_HEAD(crypto_template_list);
int?crypto_register_template(struct crypto_template?*tmpl)
{
struct crypto_template?*q;
int?err?=?-EEXIST;
down_write(&crypto_alg_sem);
//遍歷crypto_template_list,看當(dāng)前模板是否被注冊
list_for_each_entry(q,?&crypto_template_list,?list)?{
if?(q?==?tmpl)
goto out;
}
//注冊之
list_add(&tmpl->list,?&crypto_template_list);
//事件通告
crypto_notify(CRYPTO_MSG_TMPL_REGISTER,?tmpl);
err?=?0;
out:
up_write(&crypto_alg_sem);
return?err;
}
EXPORT_SYMBOL_GPL(crypto_register_template);
注銷算法模版,除了模版本身,還有一個重要的內(nèi)容是處理算法模版產(chǎn)生的算法實例,關(guān)于算法實例,后文詳述。
點擊(此處)折疊或打開
void crypto_unregister_template(struct crypto_template?*tmpl)
{
struct crypto_instance?*inst;
struct hlist_node?*p,?*n;
struct hlist_head?*list;
LIST_HEAD(users);
down_write(&crypto_alg_sem);
BUG_ON(list_empty(&tmpl->list));
//注銷算法模版,并重新初始化模版的list成員
list_del_init(&tmpl->list);
//首先移除模版上的所有算法實例
list?=?&tmpl->instances;
hlist_for_each_entry(inst,?p,?list,?list)?{
int?err?=?crypto_remove_alg(&inst->alg,?&users);
BUG_ON(err);
}
crypto_notify(CRYPTO_MSG_TMPL_UNREGISTER,?tmpl);
up_write(&crypto_alg_sem);
//釋放模版的所有算法實例分配的內(nèi)存
hlist_for_each_entry_safe(inst,?p,?n,?list,?list)?{
BUG_ON(atomic_read(&inst->alg.cra_refcnt)?!=?1);
tmpl->free(inst);
}
crypto_remove_final(&users);
}
EXPORT_SYMBOL_GPL(crypto_unregister_template);
2.? ? ? ? 算法模版的查找
點擊(此處)折疊或打開
crypto_lookup_template函數(shù)根據(jù)名稱,查找相應(yīng)的模版:
struct crypto_template?*crypto_lookup_template(const?char?*name)
{
return try_then_request_module(__crypto_lookup_template(name),?name);
}
__crypto_lookup_template完成實質(zhì)的模版模找工作,而try_then_request_module則嘗試動態(tài)插入相應(yīng)的內(nèi)核模塊,如果需要的話:
點擊(此處)折疊或打開
static struct crypto_template?*__crypto_lookup_template(const?char?*name)
{
struct crypto_template?*q,?*tmpl?=?NULL;
down_read(&crypto_alg_sem);
//遍歷crypto_template_list鏈,匹備模版名稱
list_for_each_entry(q,?&crypto_template_list,?list)?{
if?(strcmp(q->name,?name))
continue;
//查找命中,需要對其增加引用,以防止其正在使用時,模塊被卸載。完成該操作后返回查找到的模版
if?(unlikely(!crypto_tmpl_get(q)))
continue;
tmpl?=?q;
break;
}
up_read(&crypto_alg_sem);
return tmpl;
}
3.? ? ? ? 模版的算法實例分配時機
模版可以看做一個靜態(tài)的概念,其只有被動態(tài)創(chuàng)建后才具有生命力,本文將模版通過alloc分配創(chuàng)建的算法(對像)稱為“實例(instance)”。
算法模版的核心作用是,上層調(diào)用者構(gòu)造一個完整合法的算法名稱,如hmac(md5),觸發(fā)模版的alloc動作,為該名稱分配一個算法實例,類似于為類實例化一個對像,最終的目的還是使用算法本身。對于xfrm來說,一個典型的算法模版的實例分配觸發(fā)流程如下所述:
xfrm包裹了一層加密框架支持,參后文“ xfrm加密框架”一節(jié),其算法查找函數(shù)為xfrm_find_algo,它調(diào)用crypto_has_alg函數(shù)進行算法的查找,以驗證自己支持的算法是否被內(nèi)核支持,如xfrm支持cbc(des),但此時并不知道內(nèi)核是否有這個算法(如果該算法首次被使用,則還沒有分配算法實例)。crypto_has_alg會調(diào)用crypto_alg_mod_lookup完成查找工作,crypto_alg_mod_lookup函數(shù)查找不命中,會調(diào)用crypto_probing_notify函數(shù)進行請求探測:
點擊(此處)折疊或打開
struct crypto_alg?*crypto_alg_mod_lookup(const?char?*name,?u32 type,?u32 mask)
{
……
ok?=?crypto_probing_notify(CRYPTO_MSG_ALG_REQUEST,?larval);
……
}
請求是通過通知鏈表來通告的:
點擊(此處)折疊或打開
int?crypto_probing_notify(unsigned long val,?void?*v)
{
int?ok;
ok?=?blocking_notifier_call_chain(&crypto_chain,?val,?v);
if?(ok?==?NOTIFY_DONE)?{
request_module("cryptomgr");
ok?=?blocking_notifier_call_chain(&crypto_chain,?val,?v);
}
return ok;
}
在algboss.c中注冊了一個名為cryptomgr_notifier的通告塊結(jié)構(gòu),其通告處理函數(shù)為cryptomgr_notify
點擊(此處)折疊或打開
static struct notifier_block cryptomgr_notifier?=?{
.notifier_call?=?cryptomgr_notify,
};
static?int?__init cryptomgr_init(void)
{
return crypto_register_notifier(&cryptomgr_notifier);
}
static void __exit cryptomgr_exit(void)
{
int?err?=?crypto_unregister_notifier(&cryptomgr_notifier);
BUG_ON(err);
}
這樣,當(dāng)有算法被使用的時候,會調(diào)用通告塊的處理函數(shù)cryptomgr_notify,因為此時的消息是CRYPTO_MSG_ALG_REQUEST,所以cryptomgr_schedule_probe進行算法的探測:
點擊(此處)折疊或打開
static?int?cryptomgr_notify(struct notifier_block?*this,?unsigned long msg,
void?*data)
{
switch?(msg)?{
case?CRYPTO_MSG_ALG_REQUEST:
return cryptomgr_schedule_probe(data);
……
return NOTIFY_DONE;
}
cryptomgr_schedule_probe啟動一個名為cryptomgr_probe的內(nèi)核線程來進行算法模版的探測:
點擊(此處)折疊或打開
static?int?cryptomgr_schedule_probe(struct crypto_larval?*larval)
{
……
//構(gòu)造param,以供后面使用
……
thread?=?kthread_run(cryptomgr_probe,?param,?"cryptomgr_probe");
……
}
cryptomgr_probe完成具體的算法探測過程:
點擊(此處)折疊或打開
static?int?cryptomgr_probe(void?*data)
{
struct cryptomgr_param?*param?=?data;
struct crypto_template?*tmpl;
struct crypto_instance?*inst;
int?err;
//查找算法模版
tmpl?=?crypto_lookup_template(param->template);
if?(!tmpl)
goto?err;
//循環(huán)調(diào)用模版的alloc函數(shù)分配算法實列,并將模版注冊之
//這里值得注意的是循環(huán)的條件,當(dāng)返回碼為-EAGAIN時,會循環(huán)再次嘗試
//這樣使用的一個場景后面會分析到
do?{
inst?=?tmpl->alloc(param->tb);
if?(IS_ERR(inst))
err?=?PTR_ERR(inst);
else?if?((err?=?crypto_register_instance(tmpl,?inst)))
tmpl->free(inst);
}?while?(err?==?-EAGAIN?&&?!signal_pending(current));
//查找中會增加引用,這里已經(jīng)用完了釋放之
crypto_tmpl_put(tmpl);
if?(err)
goto?err;
out:
kfree(param);
module_put_and_exit(0);
err:
crypto_larval_error(param->larval,?param->otype,?param->omask);
goto out;
}
理解了算法的注冊與查找后,再來理解這個函數(shù)就非常容易了,其核心在do{}while循環(huán)中,包含了算法實例的分配和注冊動作。針對每一種算法模版,其alloc動作不盡一致。后文會對xfrm使用的算法模版一一闡述。
為什么不把“算法實例”直接稱之為“算法”,這是因為實例包含了更多的內(nèi)容,其由結(jié)構(gòu)struct crypto_instance可以看出:
點擊(此處)折疊或打開
struct crypto_instance?{
struct crypto_alg alg;?//對應(yīng)的算法名稱
struct crypto_template?*tmpl;?//所屬的算法模版
struct hlist_node list;?//鏈表成員
void?*__ctx[]?CRYPTO_MINALIGN_ATTR;?//上下文信息指針
};
內(nèi)核使用struct crypto_alg描述一個算法(該結(jié)構(gòu)在后文使用時再來分析),可見一個算法實例除了包含其對應(yīng)的算法,還包含更多的內(nèi)容。
當(dāng)分配成功后,cryptomgr_probe會調(diào)用crypto_register_instance將其注冊,以期將來可以順利地找到并使用它:
點擊(此處)折疊或打開
int?crypto_register_instance(struct crypto_template?*tmpl,
struct crypto_instance?*inst)
{
struct crypto_larval?*larval;
int?err;
//對算法進行合法性檢查,并構(gòu)造完整的驅(qū)動名稱
err?=?crypto_check_alg(&inst->alg);
if?(err)
goto?err;
//設(shè)置算法內(nèi)核模塊指針指向所屬模版
inst->alg.cra_module?=?tmpl->module;
down_write(&crypto_alg_sem);
//注冊算法實例對應(yīng)的算法
larval?=?__crypto_register_alg(&inst->alg);
if?(IS_ERR(larval))
goto unlock;
//成功后,將算法再注冊到所屬的模版上面
hlist_add_head(&inst->list,?&tmpl->instances);
//設(shè)置模版指針
inst->tmpl?=?tmpl;
unlock:
up_write(&crypto_alg_sem);
err?=?PTR_ERR(larval);
if?(IS_ERR(larval))
goto?err;
crypto_wait_for_test(larval);
err?=?0;
err:
return?err;
}
注冊的一個重要工作,就是調(diào)用__crypto_register_alg將實例所對應(yīng)的算法注冊到加密框架子系統(tǒng)中。算法注冊成功后,上層調(diào)用者就可以調(diào)用crypto_alg_mod_lookup等函數(shù)進行查找,并使用該算法了。
三、? ? ? ? HMAC
MAC(消息認(rèn)證碼)與hash函數(shù)非常相似,只是生成固定長度的消息摘要時需要秘密的密鑰而已。
HAMC是密鑰相關(guān)的哈希運算消息認(rèn)證碼(keyed-Hash Message Authentication Code),HMAC運算利用哈希算法,以一個密鑰和一個消息為輸入,生成一個消息摘要作為輸出。具體的算法描述詳見:http://baike.baidu.com/view/1136366.htm?fr=ala0_1。
根據(jù)HMAC的特點(可以和類似md5、sha等hash算法組合,構(gòu)造出hmac(md5)這樣的算法),Linux 加密框架將其抽像為一個算法模版。本章將假設(shè)上層調(diào)用者使用了名為hmac(md5)的算法,展示這一算法是如何被構(gòu)造、初始化及調(diào)用以實現(xiàn)數(shù)據(jù)驗證的。
1.? ? ? ? 算法模版的注冊與注銷
點擊(此處)折疊或打開
static struct crypto_template hmac_tmpl?=?{
.name?=?"hmac",
.alloc?=?hmac_alloc,
.free?=?hmac_free,
.module?=?THIS_MODULE,
};
點擊(此處)折疊或打開
static?int?__init hmac_module_init(void)
{
return crypto_register_template(&hmac_tmpl);
}
點擊(此處)折疊或打開
static void __exit hmac_module_exit(void)
{
crypto_unregister_template(&hmac_tmpl);
}
模版的注冊與注銷前文已經(jīng)描述過了。
2.? ? ? ? 算法實例的分配
當(dāng)一個算法需要被使用卻查找不到的時候,會嘗試調(diào)用其模版對應(yīng)分配相應(yīng)的算法實列,這也適用于hmac,其alloc函數(shù)指針指向hmac_alloc:
點擊(此處)折疊或打開
static struct crypto_instance?*?hmac_alloc?(struct rtattr?**tb)
{
struct crypto_instance?*inst;
struct crypto_alg?*alg;
int?err;
int?ds;
//類型檢查,所屬算法必需為hash類型
err?=?crypto_check_attr_type(tb,?CRYPTO_ALG_TYPE_HASH);
if?(err)
return ERR_PTR(err);
//根據(jù)參數(shù)名稱,查找相應(yīng)的子算法,如md5,shax等
alg?=?crypto_get_attr_alg(tb,?CRYPTO_ALG_TYPE_HASH,
CRYPTO_ALG_TYPE_HASH_MASK);
//查找失敗
if?(IS_ERR(alg))
return ERR_CAST(alg);
//初始化算法實例
inst?=?ERR_PTR(-EINVAL);
//計算算法實列的消息摘要大小(輸出大小)
ds?=?alg->cra_type?==?&crypto_hash_type??
alg->cra_hash.digestsize?:
alg->cra_type??
__crypto_shash_alg(alg)->digestsize?:
alg->cra_digest.dia_digestsize;
if?(ds?>?alg->cra_blocksize)
goto out_put_alg;
//分配一個算法實列,這樣,一個新的算法,如hmac(md5)就橫空出世了
inst?=?crypto_alloc_instance("hmac",?alg);
//分配失敗
if?(IS_ERR(inst))
goto out_put_alg;
//初始化算法實例,其相應(yīng)的成員等于其子算法中的對應(yīng)成員
//類型
inst->alg.cra_flags?=?CRYPTO_ALG_TYPE_HASH;
//優(yōu)先級
inst->alg.cra_priority?=?alg->cra_priority;
//計算消息摘要的塊長度(輸入大小)
inst->alg.cra_blocksize?=?alg->cra_blocksize;
//對齊掩碼
inst->alg.cra_alignmask?=?alg->cra_alignmask;
//類型指針指向crypto_hash_type
inst->alg.cra_type?=?&crypto_hash_type;
//消息摘要大小
inst->alg.cra_hash.digestsize?=?ds;
//計算算法所需的上下文空間大小
inst->alg.cra_ctxsize?=?sizeof(struct hmac_ctx)?+
ALIGN(inst->alg.cra_blocksize?*?2?+?ds,
sizeof(void?*));
//初始化和退出函數(shù)
inst->alg.cra_init?=?hmac_init_tfm;
inst->alg.cra_exit?=?hmac_exit_tfm;
//置相應(yīng)hash算法的操作函數(shù),包含hash函數(shù)標(biāo)準(zhǔn)的init/update/final和digest/setkey
inst->alg.cra_hash.init?=?hmac_init;
inst->alg.cra_hash.update?=?hmac_update;
inst->alg.cra_hash.final?=?hmac_final;
//消息摘要函數(shù)
inst->alg.cra_hash.digest?=?hmac_digest;
//setkey(密鑰設(shè)置函數(shù))
inst->alg.cra_hash.setkey?=?hmac_setkey;
out_put_alg:
crypto_mod_put(alg);
return inst;
}
每個模版的alloc動作雖不同,但是它們基本上遵循一些共性的操作:
1、? ? ? ? 合法性檢驗,如類型檢查;
2、? ? ? ? 取得其子算法(即被模版所包裹的算法,如hmac(md5)中,就是md5)的算法指針;?
3、? ? ? ? 調(diào)用crypto_alloc_instance分配一個相應(yīng)的算法實列;
4、? ? ? ? 對分配成功的算法實例進行實始化,這也是理解該算法實例最核心的部份,因為它初始化算法運行所需的一些必要參數(shù)和虛函數(shù)指針;
crypto_alloc_instance(algapi.c) 函數(shù)用于分配一個算法實例,這個函數(shù)有兩個重要功能,一個是分配內(nèi)存空間,另一個是初始化spawn。
點擊(此處)折疊或打開
//name:?模版名稱?
//alg:模版的子算法
struct crypto_instance?*crypto_alloc_instance(const?char?*name,
struct crypto_alg?*alg)
{
struct crypto_instance?*inst;
struct crypto_spawn?*spawn;
int?err;
//分配一個算法實例,crypto_instance結(jié)構(gòu)的最后一個成員ctx是一個指針變量,所以,在分配空間的時候,在其尾部追加相應(yīng)的空間,可以使用ctx訪問之。
//另一個重要的概念是,算法實例中包含了算法,這個分配,同時也完成了算法實例對應(yīng)的算法的分配工作。
inst?=?kzalloc(sizeof(*inst)?+?sizeof(*spawn),?GFP_KERNEL);
if?(!inst)
return ERR_PTR(-ENOMEM);
err?=?-ENAMETOOLONG;
//構(gòu)造完成的算法名稱
if?(snprintf(inst->alg.cra_name,?CRYPTO_MAX_ALG_NAME,?"%s(%s)",?name,
alg->cra_name)?>=?CRYPTO_MAX_ALG_NAME)
goto err_free_inst;
//構(gòu)造完整的算法驅(qū)動名稱
if?(snprintf(inst->alg.cra_driver_name,?CRYPTO_MAX_ALG_NAME,?"%s(%s)",
name,?alg->cra_driver_name)?>=?CRYPTO_MAX_ALG_NAME)
goto err_free_inst;
//spawn指向算法實例的上下文成員,可以這樣做是因為__ctx是一個可變長的成員,在分配實例的時候,
//在尾部增加了一個spawn的空間
spawn?=?crypto_instance_ctx(inst);
//初始化spawn
err?=?crypto_init_spawn(spawn,?alg,?inst,
CRYPTO_ALG_TYPE_MASK?|?CRYPTO_ALG_ASYNC);
if?(err)
goto err_free_inst;
return inst;
err_free_inst:
kfree(inst);
return ERR_PTR(err);
}
crypto_instance_ctx取出算法實例的ctx指針,返回值是void *,這意味著可以根具不同的需要,將其轉(zhuǎn)換為所需的類型:
點擊(此處)折疊或打開
static inline void?*crypto_instance_ctx(struct crypto_instance?*inst)
{
return inst->__ctx;
}
一個算法實例被分配成員后,其會被注冊至加密子系統(tǒng),這樣,一個算法,例如,hmac(md5)就可以直接被使用了。
3.? ? ? ? 待孵化的卵
? ? ? ? 已經(jīng)看到了從模版到算法實例的第一層抽像,每個算法在每一次被使用時,它們的運行環(huán)境不盡相同,例如,可能會擁有不同的密鑰。將算法看成一個類,則在每一次運行調(diào)用時,需要為它產(chǎn)生一個“對像”,這在內(nèi)核中被稱為transform,簡稱為tfm。后文會詳細(xì)看到分配一個tfm的過程,現(xiàn)在引入這一概念,主要是為了分析spawn。
加密或認(rèn)證算法,在調(diào)用時,都需要分配其算法對應(yīng)的tfm,在分配算法實例的同時,并沒有為之分配相應(yīng)的tfm結(jié)構(gòu),這是因為真正的算法還沒有被調(diào)用,這并不是進行tfm結(jié)構(gòu)分配的最佳地點。在初始化算法實例的時候,加密框架使用了XXX_spawn_XXX函數(shù)簇來解決這一問題。這樣的算法對像,被稱為spawn(卵)。也就是說,在算法實例分配的時候,只是下了一個蛋(設(shè)置好spawn),等到合適的時候來對其進行孵化,這個“合適的時候”,通常指為調(diào)用算法實際使用的時候。
在crypto_alloc_instance分配算法實例的時候,就順便分配了spawn,然后調(diào)用crypto_init_spawn對其進行初始化:
點擊(此處)折疊或打開
int?crypto_init_spawn(struct crypto_spawn?*spawn,?struct crypto_alg?*alg,
struct crypto_instance?*inst,?u32 mask)
{
int?err?=?-EAGAIN;
//初始化其成員
spawn->inst?=?inst;
spawn->mask?=?mask;
down_write(&crypto_alg_sem);
if?(!crypto_is_moribund(alg))?{
//加入鏈表,每個spawn,都被加入到算法的cra_users鏈,即算做算法的一個用戶
list_add(&spawn->list,?&alg->cra_users);
//spawn的alg成員指針指向當(dāng)前成員,這就方便引用了
spawn->alg?=?alg;
err?=?0;
}
up_write(&crypto_alg_sem);
return?err;
}
所以,所謂算法的spawn的初始化,就是初始化crypto_spawn結(jié)構(gòu),核心的操作是設(shè)置其對應(yīng)的算法實例、算法,以及一個加入算法的鏈表的過程。
4.? ? ? ? 算法的初始化
有了算法實例,僅表示內(nèi)核擁有這一種“算法”——加引號的意思是說,它可能并不以類似md5.c這樣的源代碼形式存現(xiàn),而是通過模版動態(tài)創(chuàng)建的。實際要使用該算法,需要為算法分配“運行的對像”,即tfm。
4.1? ? ? ? tfm
內(nèi)核加密框架中,使用結(jié)構(gòu)crypto_alg來描述一個算法,每一個算法(實例)相當(dāng)于一個類,在實際的使用環(huán)境中,需要為它分配一個對像,在內(nèi)核加密框架中,這個“對像”被稱為transform(簡稱tfm)。transform意味“變換”,可能譯為“蛻變”更為合適。作者對它的注釋是:
/*
* Transforms: user-instantiated objects which encapsulate algorithms
* and core processing logic.??Managed via crypto_alloc_*() and
* crypto_free_*(), as well as the various helpers below.
……
*/
tfm是加密框架中一個極為重要的概念,它由結(jié)構(gòu)crypto_tfm描述:
點擊(此處)折疊或打開
struct crypto_tfm?{
u32 crt_flags;
union?{
struct ablkcipher_tfm ablkcipher;
struct aead_tfm aead;
struct blkcipher_tfm blkcipher;
struct cipher_tfm cipher;
struct hash_tfm hash;
struct ahash_tfm ahash;
struct compress_tfm compress;
struct rng_tfm rng;
}?crt_u;
void?(*exit)(struct crypto_tfm?*tfm);
struct crypto_alg?*__crt_alg;
void?*__crt_ctx[]?CRYPTO_MINALIGN_ATTR;
};
這些成員的作用,將在后面一一看到,值得注意的是,針對每種算法不同,結(jié)構(gòu)定義了一個名為crt_u的聯(lián)合體,以對應(yīng)每種算法的tfm的具體操作,例如加密/解密,求hash,壓縮/解壓等,加密框架引入了一組名為xxx_tfm的結(jié)構(gòu)封裝,xxx表示算法類型,也就是crt_u成員。其定義如下:
點擊(此處)折疊或打開
struct ablkcipher_tfm?{
int?(*setkey)(struct crypto_ablkcipher?*tfm,?const?u8?*key,
unsigned?int?keylen);
int?(*encrypt)(struct ablkcipher_request?*req);
int?(*decrypt)(struct ablkcipher_request?*req);
int?(*givencrypt)(struct skcipher_givcrypt_request?*req);
int?(*givdecrypt)(struct skcipher_givcrypt_request?*req);
struct crypto_ablkcipher?*base;
unsigned?int?ivsize;
unsigned?int?reqsize;
};
struct aead_tfm?{
int?(*setkey)(struct crypto_aead?*tfm,?const?u8?*key,
unsigned?int?keylen);
int?(*encrypt)(struct aead_request?*req);
int?(*decrypt)(struct aead_request?*req);
int?(*givencrypt)(struct aead_givcrypt_request?*req);
int?(*givdecrypt)(struct aead_givcrypt_request?*req);
struct crypto_aead?*base;
unsigned?int?ivsize;
unsigned?int?authsize;
unsigned?int?reqsize;
};
struct blkcipher_tfm?{
void?*iv;
int?(*setkey)(struct crypto_tfm?*tfm,?const?u8?*key,
unsigned?int?keylen);
int?(*encrypt)(struct blkcipher_desc?*desc,?struct scatterlist?*dst,
struct scatterlist?*src,?unsigned?int?nbytes);
int?(*decrypt)(struct blkcipher_desc?*desc,?struct scatterlist?*dst,
struct scatterlist?*src,?unsigned?int?nbytes);
};
struct cipher_tfm?{
int?(*cit_setkey)(struct crypto_tfm?*tfm,
const?u8?*key,?unsigned?int?keylen);
void?(*cit_encrypt_one)(struct crypto_tfm?*tfm,?u8?*dst,?const?u8?*src);
void?(*cit_decrypt_one)(struct crypto_tfm?*tfm,?u8?*dst,?const?u8?*src);
};
struct hash_tfm?{
int?(*init)(struct hash_desc?*desc);
int?(*update)(struct hash_desc?*desc,
struct scatterlist?*sg,?unsigned?int?nsg);
int?(*final)(struct hash_desc?*desc,?u8?*out);
int?(*digest)(struct hash_desc?*desc,?struct scatterlist?*sg,
unsigned?int?nsg,?u8?*out);
int?(*setkey)(struct crypto_hash?*tfm,?const?u8?*key,
unsigned?int?keylen);
unsigned?int?digestsize;
};
struct ahash_tfm?{
int?(*init)(struct ahash_request?*req);
int?(*update)(struct ahash_request?*req);
int?(*final)(struct ahash_request?*req);
int?(*digest)(struct ahash_request?*req);
int?(*setkey)(struct crypto_ahash?*tfm,?const?u8?*key,
unsigned?int?keylen);
unsigned?int?digestsize;
unsigned?int?reqsize;
};
struct compress_tfm?{
int?(*cot_compress)(struct crypto_tfm?*tfm,
const?u8?*src,?unsigned?int?slen,
u8?*dst,?unsigned?int?*dlen);
int?(*cot_decompress)(struct crypto_tfm?*tfm,
const?u8?*src,?unsigned?int?slen,
u8?*dst,?unsigned?int?*dlen);
};
struct rng_tfm?{
int?(*rng_gen_random)(struct crypto_rng?*tfm,?u8?*rdata,
unsigned?int?dlen);
int?(*rng_reset)(struct crypto_rng?*tfm,?u8?*seed,?unsigned?int?slen);
};
為了直接訪問這些成員,定義了如下宏:
點擊(此處)折疊或打開
#define crt_ablkcipher crt_u.ablkcipher
#define crt_aead crt_u.aead
#define crt_blkcipher crt_u.blkcipher
#define crt_cipher crt_u.cipher
#define crt_hash crt_u.hash
#define crt_ahash crt_u.ahash
#define crt_compress crt_u.compress
#define crt_rng crt_u.rng
這樣,要訪問hash算法的hash成員,就可以直接使用crt_hash,而不是crt_u.hash。
每種算法訪問tfm都使用了二次封裝,例如:
點擊(此處)折疊或打開
struct crypto_ablkcipher?{
struct crypto_tfm base;
};
struct crypto_aead?{
struct crypto_tfm base;
};
struct crypto_blkcipher?{
struct crypto_tfm base;
};
struct crypto_cipher?{
struct crypto_tfm base;
};
struct crypto_comp?{
struct crypto_tfm base;
};
struct crypto_hash?{
struct crypto_tfm base;
};
struct crypto_rng?{
struct crypto_tfm base;
};
其base成員就是相應(yīng)算法的tfm。因為它們擁有相應(yīng)的起始地址,可以很方便地強制類型轉(zhuǎn)換來操作,內(nèi)核為此專門定義了一組函數(shù),以hash為例,完成這一工作的是crypto_hash_cast:
點擊(此處)折疊或打開
static inline struct crypto_hash?*__crypto_hash_cast(struct crypto_tfm?*tfm)
{
return?(struct crypto_hash?*)tfm;
}
static inline struct crypto_hash?*crypto_hash_cast(struct crypto_tfm?*tfm)
{
BUG_ON((crypto_tfm_alg_type(tfm)?^ CRYPTO_ALG_TYPE_HASH)?&
CRYPTO_ALG_TYPE_HASH_MASK);
return __crypto_hash_cast(tfm);
}
當(dāng)然,針對各種不同的算法,還有許多不同的XXX_cast函數(shù)。這些cast函數(shù),將tfm強制轉(zhuǎn)換為其所屬的算法類型的封裝結(jié)構(gòu)。
4.2 tfm的分配
對于算法的實始化,其核心功能就是分配一個tfm,并設(shè)置其上下文環(huán)境,例如密鑰等參數(shù),然后初始化上述struct xxx_tfm結(jié)構(gòu)。對于hash類的算法來講,分配tfm是由crypto_alloc_hash(crypt.h) 這個API來完成的,以AH為例,在其初始化過程中有:
點擊(此處)折疊或打開
static?int?ah_init_state(struct xfrm_state?*x)
{
struct crypto_hash?*tfm;
……
tfm?=?crypto_alloc_hash(x->aalg->alg_name,?0,?CRYPTO_ALG_ASYNC);
if?(IS_ERR(tfm))
goto?error;
……
}
AH調(diào)用crypto_alloc_hash為SA中指定的算法(如hmac(md5))分配一個tfm,第二個參數(shù)為0,第三個參數(shù)指明了AH使用異步模式。
點擊(此處)折疊或打開
static inline struct crypto_hash?*crypto_alloc_hash(const?char?*alg_name,
u32 type,?u32 mask)
{
//初始化相應(yīng)的類型的掩碼
type?&=?~CRYPTO_ALG_TYPE_MASK;?//清除類型的CRYPTO_ALG_TYPE_MASK位
mask?&=?~CRYPTO_ALG_TYPE_MASK;?//清除掩碼的CRYPTO_ALG_TYPE_MASK位
type?|=?CRYPTO_ALG_TYPE_HASH;?//置類型CRYPTO_ALG_TYPE_HASH位
mask?|=?CRYPTO_ALG_TYPE_HASH_MASK;?//置掩碼CRYPTO_ALG_TYPE_HASH_MASK位
//最終的分配函數(shù)是crypto_alloc_base,它分配一個base(每個算法的tfm),再將其強制類型轉(zhuǎn)換為所需要結(jié)構(gòu)類型
return __crypto_hash_cast(crypto_alloc_base(alg_name,?type,?mask));
}
crypto_alloc_base首先檢查相應(yīng)的算法是否存在,對于hmac(md5)這個例子,xfrm在SA的增加中,會觸發(fā)相應(yīng)的算法查找,最終會調(diào)用hmac模版的alloc分配算法實例(當(dāng)然也包括算法本身),然后向內(nèi)核注冊算法及算法實例,所以,查找會命中。接下來的工作,是調(diào)用tfm的核心分配函數(shù)__crypto_alloc_tfm進行分配,其實現(xiàn)如下:
點擊(此處)折疊或打開
struct crypto_tfm?*crypto_alloc_base(const?char?*alg_name,?u32 type,?u32 mask)
{
struct crypto_tfm?*tfm;
int?err;
for?(;;)?{
struct crypto_alg?*alg;
//根據(jù)算法名稱,查找相應(yīng)的算法,它會首先嘗試已經(jīng)加載的算法,如果失敗,也會嘗試
//動態(tài)插入內(nèi)核模塊
alg?=?crypto_alg_mod_lookup(alg_name,?type,?mask);
//查找失敗,返回退出循環(huán)
if?(IS_ERR(alg))?{
err?=?PTR_ERR(alg);
goto?err;
}
//查找成功,為算法分配tfm
tfm?=?__crypto_alloc_tfm(alg,?type,?mask);
//分配成功,返回之
if?(!IS_ERR(tfm))
return tfm;
//釋放引用計算,因為查找會增加引用
crypto_mod_put(alg);
//獲取返回錯誤值,根據(jù)其值,決定是否要繼續(xù)嘗試
err?=?PTR_ERR(tfm);
err:
if?(err?!=?-EAGAIN)
break;
if?(signal_pending(current))?{
err?=?-EINTR;
break;
}
}
return ERR_PTR(err);
}
__crypto_alloc_tfm是內(nèi)核加密框架中又一重要的函數(shù),它完成了對算法tfm的分配和初始化的工作:
點擊(此處)折疊或打開
struct crypto_tfm?*__crypto_alloc_tfm(struct crypto_alg?*alg,?u32 type,
u32 mask)
{
struct crypto_tfm?*tfm?=?NULL;
unsigned?int?tfm_size;
int?err?=?-ENOMEM;
//計算tfm所需的空間大小,它包括了tfm結(jié)構(gòu)本身和算法上下文大小
tfm_size?=?sizeof(*tfm)?+?crypto_ctxsize(alg,?type,?mask);
//分配tfm
tfm?=?kzalloc(tfm_size,?GFP_KERNEL);
if?(tfm?==?NULL)
goto out_err;
//__crt_alg成員指向其所屬的算法,對于hmac而言,它就是hmac(xxx),例如hmac(md5)
tfm->__crt_alg?=?alg;
//初始化tfm選項
err?=?crypto_init_ops(tfm,?type,?mask);
if?(err)
goto out_free_tfm;
//調(diào)用算法的初始化函數(shù),初始化tfm,這有個先決條件是tfm本身沒有exit函數(shù)的實現(xiàn)
if?(!tfm->exit?&&?alg->cra_init?&&?(err?=?alg->cra_init(tfm)))
goto cra_init_failed;
goto out;
cra_init_failed:
crypto_exit_ops(tfm);
out_free_tfm:
if?(err?==?-EAGAIN)
crypto_shoot_alg(alg);
kfree(tfm);
out_err:
tfm?=?ERR_PTR(err);
out:
return tfm;
}
crypto_init_ops負(fù)責(zé)初始化tfm的選項,對于一個真正的算法(例如md5、dst)和一個偽算法(我說的“偽”,是指由模版動態(tài)分配的,如hmac(xxx), authenc(xxx,xxx)),因為并不存在這樣的算法,只是內(nèi)核的一個抽像,故稱為"偽",它們的初始化過程是截然不同的。一個偽算法,它都設(shè)置了其所屬的類型cra_type,例如,對于hmac(xxx)而言,它指向了crypto_hash_type。這樣,初始化時,實質(zhì)上調(diào)用的是其所屬類型的init函數(shù):
點擊(此處)折疊或打開
static?int?crypto_init_ops(struct crypto_tfm?*tfm,?u32 type,?u32 mask)
{
//獲取tfm所屬算法的所屬類型
const?struct crypto_type?*type_obj?=?tfm->__crt_alg->cra_type;
//如果設(shè)置了類型,調(diào)用類型的init
if?(type_obj)
return type_obj->init(tfm,?type,?mask);
//否則,判斷算法的類型,調(diào)用相應(yīng)的初始化函數(shù),這些在不同的算法實現(xiàn)中分析
switch?(crypto_tfm_alg_type(tfm))?{
case?CRYPTO_ALG_TYPE_CIPHER:
return crypto_init_cipher_ops(tfm);
case?CRYPTO_ALG_TYPE_DIGEST:
if?((mask?&?CRYPTO_ALG_TYPE_HASH_MASK)?!=
CRYPTO_ALG_TYPE_HASH_MASK)
return crypto_init_digest_ops_async(tfm);
else
return crypto_init_digest_ops(tfm);
case?CRYPTO_ALG_TYPE_COMPRESS:
return crypto_init_compress_ops(tfm);
default:
break;
}
BUG();
return?-EINVAL;
}
算法類型的概念很好理解,因為若干個hmac(xxx)都擁有一此相同的類型屬性(其它偽算法同樣如此),所以可以將它們抽像管理。
對于hash類型的算法而言,它們擁有一個共同的類型crypto_hash_type,其定義在hash.c中:
點擊(此處)折疊或打開
const?struct crypto_type crypto_hash_type?=?{
.ctxsize?=?crypto_hash_ctxsize,
.init?=?crypto_init_hash_ops,
#ifdef CONFIG_PROC_FS
.show?=?crypto_hash_show,
#endif
};
它的init函數(shù)指針指向crypto_init_hash_ops:
點擊(此處)折疊或打開
static?int?crypto_init_hash_ops(struct crypto_tfm?*tfm,?u32 type,?u32 mask)
{
struct hash_alg?*alg?=?&tfm->__crt_alg->cra_hash;
//其消息摘要大小不同超過1/8個頁面
if?(alg->digestsize?>?PAGE_SIZE?/?8)
return?-EINVAL;
//根據(jù)掩碼位,判斷是同步初始化還是異步,對于crypto_alloc_hash調(diào)用下來的而言,它
//設(shè)置了CRYPTO_ALG_TYPE_HASH_MASK位,所以是同步初始化
if?((mask?&?CRYPTO_ALG_TYPE_HASH_MASK)?!=?CRYPTO_ALG_TYPE_HASH_MASK)
return crypto_init_hash_ops_async?(tfm);
else
return crypto_init_hash_ops_sync(tfm);
}
在我們AH的例子中,AH使用了異步模式,所以crypto_init_hash_ops_async會被調(diào)用。
前述hash_tfm結(jié)構(gòu)封裝了hash類型的算法的通用的操作:
點擊(此處)折疊或打開
struct hash_tfm?{
int?(*init)(struct hash_desc?*desc);
int?(*update)(struct hash_desc?*desc,
struct scatterlist?*sg,?unsigned?int?nsg);
int?(*final)(struct hash_desc?*desc,?u8?*out);
int?(*digest)(struct hash_desc?*desc,?struct scatterlist?*sg,
unsigned?int?nsg,?u8?*out);
int?(*setkey)(struct crypto_hash?*tfm,?const?u8?*key,
unsigned?int?keylen);
unsigned?int?digestsize;
};
先來看同步模式的初始化操作,crypto_init_hash_ops_sync函數(shù)負(fù)責(zé)初始化這一結(jié)構(gòu):
點擊(此處)折疊或打開
static?int?crypto_init_hash_ops_sync(struct crypto_tfm?*tfm)
{
struct hash_tfm?*crt?=?&tfm->crt_hash;
struct hash_alg?*alg?=?&tfm->__crt_alg->cra_hash;
//置tfm相應(yīng)操作為算法本身的對應(yīng)操作,
//對于hmac(xxx)算法而言,這些東東在hmac_alloc中已經(jīng)初始化過了,也就是hmac_init等函數(shù)
crt->init?=?alg->init;
crt->update?=?alg->update;
crt->final?=?alg->final;
crt->digest?=?alg->digest;
crt->setkey?=?hash_setkey;
crt->digestsize?=?alg->digestsize;
return 0;
}
異步模式則稍有不同,它使用了hash類型算法的通用函數(shù):
點擊(此處)折疊或打開
static?int?crypto_init_hash_ops_async(struct crypto_tfm?*tfm)
{
struct ahash_tfm?*crt?=?&tfm->crt_ahash;
struct hash_alg?*alg?=?&tfm->__crt_alg->cra_hash;
crt->init?=?hash_async_init;
crt->update?=?hash_async_update;
crt->final?=?hash_async_final;
crt->digest?=?hash_async_digest;
crt->setkey?=?hash_async_setkey;
crt->digestsize?=?alg->digestsize;
return 0;
}
不論是同步還是異步,算法的tfm都得到的相應(yīng)的初始化?;氐絖_crypto_alloc_tfm中來,__crypto_alloc_tfm函數(shù)的最后一步是調(diào)用算法的cra_init函數(shù)(如果它存在的話),對于hmac(xxx)而言,它在分配的時候指向hmac_init_tfm。hmac_init_tfm的主要工作就是對hmac(xxx)的spawn進行孵化操作。還記得“待孵化的卵”嗎?前面講了只是初始化它,現(xiàn)在到了孵化的時候了
點擊(此處)折疊或打開
static?int?hmac_init_tfm(struct crypto_tfm?*tfm)
{
struct crypto_hash?*hash;
//因為算法實例的第一個成員就是alg,在注冊算法時,就是注冊的它,所以可以很方便地通過tfm的__crt_alg強制類型轉(zhuǎn)換得到對應(yīng)的算法實例
struct crypto_instance?*inst?=?(void?*)tfm->__crt_alg;
//取得算法實例的__ctx域,也就是spawn
struct crypto_spawn?*spawn?=?crypto_instance_ctx(inst);
//取得tfm的上下文指針
struct hmac_ctx?*ctx?=?hmac_ctx(__crypto_hash_cast(tfm));
//對hmac(xxx)進行孵化,以hmac(md5)為例,這將得到一個md5算法的tfm,當(dāng)然,通過強制類型轉(zhuǎn)換,它被封裝在結(jié)構(gòu)crypto_hash中
hash?=?crypto_spawn_hash(spawn);
if?(IS_ERR(hash))
return PTR_ERR(hash);
//設(shè)置子算法指向孵化的tfm
ctx->child?=?hash;
return 0;
}
crypto_spawn_hash展示了如何對hash算法簇進行spawn的孵化操作:
點擊(此處)折疊或打開
static inline struct crypto_hash?*crypto_spawn_hash(struct crypto_spawn?*spawn)
{
//初始化孵化所需的類型和掩碼
u32 type?=?CRYPTO_ALG_TYPE_HASH;
u32 mask?=?CRYPTO_ALG_TYPE_HASH_MASK;
//調(diào)用crypto_spawn_tfm孵化一個tfm,并強制類型轉(zhuǎn)換
return __crypto_hash_cast(crypto_spawn_tfm(spawn,?type,?mask));
}
最后的任務(wù)交給了crypto_spawn_tfm函數(shù),它為算法孵化一個tfm,因為spawn的alg成員指向了所要孵化的算法,使得這一操作很容易實現(xiàn)
點擊(此處)折疊或打開
struct crypto_tfm?*crypto_spawn_tfm(struct crypto_spawn?*spawn,?u32 type,
u32 mask)
{
struct crypto_alg?*alg;
struct crypto_alg?*alg2;
struct crypto_tfm?*tfm;
down_read(&crypto_alg_sem);
//要孵化的spawn所屬的算法
alg?=?spawn->alg;
alg2?=?alg;
//查找算法所屬模塊
if?(alg2)
alg2?=?crypto_mod_get(alg2);
up_read(&crypto_alg_sem);
//如果其所屬模塊沒了,則標(biāo)注算法為DYING,出錯退回
if?(!alg2)?{
if?(alg)
crypto_shoot_alg(alg);
return ERR_PTR(-EAGAIN);
}
//初始化tfm
tfm?=?ERR_PTR(-EINVAL);
//驗證掩碼標(biāo)志位
if?(unlikely((alg->cra_flags ^ type)?&?mask))
goto out_put_alg;
//為算法分配相應(yīng)的tfm,這樣,一個算法的spawn就孵化完成了
tfm?=?__crypto_alloc_tfm(alg,?type,?mask);
if?(IS_ERR(tfm))
goto out_put_alg;
return tfm;
out_put_alg:
crypto_mod_put(alg);
return tfm;
}
又繞回了__crypto_alloc_tfm函數(shù),其實現(xiàn)之前已經(jīng)分析過了,對于一個普通的算法(非模版產(chǎn)生的算法,如md5),其初始化工作略有不同,在了解其初始化工作之前,需要對一個實際的算法作了解。
順例說一句,內(nèi)核的這種抽像管理方式,功能異常地強大,可以想像,它可以抽像更多層的嵌套。所以hmac(xxx)中,xxx不一定就是一個md5之類,可能還是一層形如xxx(xxx)的抽像,理論上,它可以像變形金剛一樣。
4.3 小結(jié)一下
本節(jié)分析了一個算法的tfm是如何生成的,因為算法可以是多層的組裝,在生成上層算法的同時,它也要為其所包含的算法分配tfm,這一過程稱之為spawn。
?
評論
查看更多