Linux加密框架是内核安全子系统的重要组成部份,同时,它又一个的独立子系统形式出现,从它出现在内核根目录下的crypto/就可以看出其地位了。
Crypto实现较为复杂,其主要体现在其OOP的设计思路和高度的对像抽像与封装模型,作者展现了其出色的架构设计水准和面向对像的抽像能力。本文力图从加密框架的重要应用,即IPSec(xfrm)的两个重要协议AH和ESP对加密框架的使用,展现其设计与实现。
内核版本:2.6.31.13
二、 算法模版
1. 模版的基本概念
算法模版是加密框架的第一个重要概念。内核中有很多算法是动态生成的,例如cbc(des)算法。内核并不存在这样的算法,它事实上是cbc和des的组合,但是内核加密框架从统一抽像管理的角度。将cbc(des)看做一个算法,在实际使用时动态分配并向内核注册该算法。这样,可以将cbc抽像为一个模版,它可以同任意的加密算法进行组合。算法模版使用结构crypto_template来描述,其结构原型:
点击(此处)折叠或打开
- 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的算法模版,可以用它来动态分配cbc(des),cbc(twofish)……诸如此类。
crypto/algapi.c下包含了模版的一些常用操作。最为常见的就是模版的注册与注销,其实质是对以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,看当前模板是否被注册
- 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);
注销算法模版,除了模版本身,还有一个重要的内容是处理算法模版产生的算法实例,关于算法实例,后文详述。
点击(此处)折叠或打开
- 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);
- //释放模版的所有算法实例分配的内存
- 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函数根据名称,查找相应的模版:
- struct crypto_template *crypto_lookup_template(const char *name)
- {
- return try_then_request_module(__crypto_lookup_template(name), name);
- }
__crypto_lookup_template完成实质的模版模找工作,而try_then_request_module则尝试动态插入相应的内核模块,如果需要的话:
点击(此处)折叠或打开
- 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. 模版的算法实例分配时机
模版可以看做一个静态的概念,其只有被动态创建后才具有生命力,本文将模版通过alloc分配创建的算法(对像)称为“实例(instance)”。
算法模版的核心作用是,上层调用者构造一个完整合法的算法名称,如hmac(md5),触发模版的alloc动作,为该名称分配一个算法实例,类似于为类实例化一个对像,最终的目的还是使用算法本身。对于xfrm来说,一个典型的算法模版的实例分配触发流程如下所述:
xfrm包裹了一层加密框架支持,参后文“ xfrm加密框架”一节,其算法查找函数为xfrm_find_algo,它调用crypto_has_alg函数进行算法的查找,以验证自己支持的算法是否被内核支持,如xfrm支持cbc(des),但此时并不知道内核是否有这个算法(如果该算法首次被使用,则还没有分配算法实例)。crypto_has_alg会调用crypto_alg_mod_lookup完成查找工作,crypto_alg_mod_lookup函数查找不命中,会调用crypto_probing_notify函数进行请求探测:
点击(此处)折叠或打开
- 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;
- }
点击(此处)折叠或打开
- 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);
- }
这样,当有算法被使用的时候,会调用通告块的处理函数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的内核线程来进行算法模版的探测:
点击(此处)折叠或打开
- static int cryptomgr_schedule_probe(struct crypto_larval *larval)
- {
- ……
- //构造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;
- //循环调用模版的alloc函数分配算法实列,并将模版注册之
- //这里值得注意的是循环的条件,当返回码为-EAGAIN时,会循环再次尝试
- //这样使用的一个场景后面会分析到
- 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));
- //查找中会增加引用,这里已经用完了释放之
- 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;
- }
理解了算法的注册与查找后,再来理解这个函数就非常容易了,其核心在do{}while循环中,包含了算法实例的分配和注册动作。针对每一种算法模版,其alloc动作不尽一致。后文会对xfrm使用的算法模版一一阐述。
为什么不把“算法实例”直接称之为“算法”,这是因为实例包含了更多的内容,其由结构struct crypto_instance可以看出:
点击(此处)折叠或打开
- struct crypto_instance {
- struct crypto_alg alg; //对应的算法名称
- struct crypto_template *tmpl; //所属的算法模版
- struct hlist_node list; //链表成员
- void *__ctx[] CRYPTO_MINALIGN_ATTR; //上下文信息指针
- };
内核使用struct crypto_alg描述一个算法(该结构在后文使用时再来分析),可见一个算法实例除了包含其对应的算法,还包含更多的内容。
当分配成功后,cryptomgr_probe会调用crypto_register_instance将其注册,以期将来可以顺利地找到并使用它:
点击(此处)折叠或打开
- int crypto_register_instance(struct crypto_template *tmpl,
- struct crypto_instance *inst)
- {
- struct crypto_larval *larval;
- int err;
- //对算法进行合法性检查,并构造完整的驱动名称
- err = crypto_check_alg(&inst->alg);
- if (err)
- goto err;
- //设置算法内核模块指针指向所属模版
- inst->alg.cra_module = tmpl->module;
- down_write(&crypto_alg_sem);
- //注册算法实例对应的算法
- larval = __crypto_register_alg(&inst->alg);
- if (IS_ERR(larval))
- goto unlock;
- //成功后,将算法再注册到所属的模版上面
- hlist_add_head(&inst->list, &tmpl->instances);
- //设置模版指针
- 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;
- }
注册的一个重要工作,就是调用__crypto_register_alg将实例所对应的算法注册到加密框架子系统中。算法注册成功后,上层调用者就可以调用crypto_alg_mod_lookup等函数进行查找,并使用该算法了。
MAC(消息认证码)与hash函数非常相似,只是生成固定长度的消息摘要时需要秘密的密钥而已。
HAMC是密钥相关的哈希运算消息认证码(keyed-Hash Message Authentication Code),HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。具体的算法描述详见:http://baike.baidu.com/view/1136366.htm?fr=ala0_1。
根据HMAC的特点(可以和类似md5、sha等hash算法组合,构造出hmac(md5)这样的算法),Linux 加密框架将其抽像为一个算法模版。本章将假设上层调用者使用了名为hmac(md5)的算法,展示这一算法是如何被构造、初始化及调用以实现数据验证的。
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);
- }
模版的注册与注销前文已经描述过了。
2. 算法实例的分配
当一个算法需要被使用却查找不到的时候,会尝试调用其模版对应分配相应的算法实列,这也适用于hmac,其alloc函数指针指向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);
- //根据参数名称,查找相应的子算法,如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;
- //初始化算法实例,其相应的成员等于其子算法中的对应成员
- //类型
- inst->alg.cra_flags = CRYPTO_ALG_TYPE_HASH;
- //优先级
- 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 *));
- //初始化和退出函数
- inst->alg.cra_init = hmac_init_tfm;
- inst->alg.cra_exit = hmac_exit_tfm;
- //置相应hash算法的操作函数,包含hash函数标准的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;
- //消息摘要函数
- inst->alg.cra_hash.digest = hmac_digest;
- //setkey(密钥设置函数)
- inst->alg.cra_hash.setkey = hmac_setkey;
- out_put_alg:
- crypto_mod_put(alg);
- return inst;
- }
每个模版的alloc动作虽不同,但是它们基本上遵循一些共性的操作:
1、 合法性检验,如类型检查;
2、 取得其子算法(即被模版所包裹的算法,如hmac(md5)中,就是md5)的算法指针;
3、 调用crypto_alloc_instance分配一个相应的算法实列;
4、 对分配成功的算法实例进行实始化,这也是理解该算法实例最核心的部份,因为它初始化算法运行所需的一些必要参数和虚函数指针;
crypto_alloc_instance(algapi.c) 函数用于分配一个算法实例,这个函数有两个重要功能,一个是分配内存空间,另一个是初始化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结构的最后一个成员ctx是一个指针变量,所以,在分配空间的时候,在其尾部追加相应的空间,可以使用ctx访问之。
- //另一个重要的概念是,算法实例中包含了算法,这个分配,同时也完成了算法实例对应的算法的分配工作。
- inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL);
- if (!inst)
- return ERR_PTR(-ENOMEM);
- err = -ENAMETOOLONG;
- //构造完成的算法名称
- if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME, "%s(%s)", name,
- alg->cra_name) >= CRYPTO_MAX_ALG_NAME)
- goto err_free_inst;
- //构造完整的算法驱动名称
- 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 *,这意味着可以根具不同的需要,将其转换为所需的类型:
点击(此处)折叠或打开
- static inline void *crypto_instance_ctx(struct crypto_instance *inst)
- {
- return inst->__ctx;
- }
一个算法实例被分配成员后,其会被注册至加密子系统,这样,一个算法,例如,hmac(md5)就可以直接被使用了。
3. 待孵化的卵
已经看到了从模版到算法实例的第一层抽像,每个算法在每一次被使用时,它们的运行环境不尽相同,例如,可能会拥有不同的密钥。将算法看成一个类,则在每一次运行调用时,需要为它产生一个“对像”,这在内核中被称为transform,简称为tfm。后文会详细看到分配一个tfm的过程,现在引入这一概念,主要是为了分析spawn。
加密或认证算法,在调用时,都需要分配其算法对应的tfm,在分配算法实例的同时,并没有为之分配相应的tfm结构,这是因为真正的算法还没有被调用,这并不是进行tfm结构分配的最佳地点。在初始化算法实例的时候,加密框架使用了XXX_spawn_XXX函数簇来解决这一问题。这样的算法对像,被称为spawn(卵)。也就是说,在算法实例分配的时候,只是下了一个蛋(设置好spawn),等到合适的时候来对其进行孵化,这个“合适的时候”,通常指为调用算法实际使用的时候。
在crypto_alloc_instance分配算法实例的时候,就顺便分配了spawn,然后调用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成员指针指向当前成员,这就方便引用了
- spawn->alg = alg;
- err = 0;
- }
- up_write(&crypto_alg_sem);
- return err;
- }
所以,所谓算法的spawn的初始化,就是初始化crypto_spawn结构,核心的操作是设置其对应的算法实例、算法,以及一个加入算法的链表的过程。
有了算法实例,仅表示内核拥有这一种“算法”——加引号的意思是说,它可能并不以类似md5.c这样的源代码形式存现,而是通过模版动态创建的。实际要使用该算法,需要为算法分配“运行的对像”,即tfm。
4.1 tfm
内核加密框架中,使用结构crypto_alg来描述一个算法,每一个算法(实例)相当于一个类,在实际的使用环境中,需要为它分配一个对像,在内核加密框架中,这个“对像”被称为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是加密框架中一个极为重要的概念,它由结构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;
- };
这些成员的作用,将在后面一一看到,值得注意的是,针对每种算法不同,结构定义了一个名为crt_u的联合体,以对应每种算法的tfm的具体操作,例如加密/解密,求hash,压缩/解压等,加密框架引入了一组名为xxx_tfm的结构封装,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
每种算法访问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成员就是相应算法的tfm。因为它们拥有相应的起始地址,可以很方便地强制类型转换来操作,内核为此专门定义了一组函数,以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);
- }
当然,针对各种不同的算法,还有许多不同的XXX_cast函数。这些cast函数,将tfm强制转换为其所属的算法类型的封装结构。
4.2 tfm的分配
对于算法的实始化,其核心功能就是分配一个tfm,并设置其上下文环境,例如密钥等参数,然后初始化上述struct xxx_tfm结构。对于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调用crypto_alloc_hash为SA中指定的算法(如hmac(md5))分配一个tfm,第二个参数为0,第三个参数指明了AH使用异步模式。
点击(此处)折叠或打开
- static inline struct crypto_hash *crypto_alloc_hash(const char *alg_name,
- u32 type, u32 mask)
- {
- //初始化相应的类型的掩码
- type &= ~CRYPTO_ALG_TYPE_MASK; //清除类型的CRYPTO_ALG_TYPE_MASK位
- mask &= ~CRYPTO_ALG_TYPE_MASK; //清除掩码的CRYPTO_ALG_TYPE_MASK位
- type |