免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 27263 | 回复: 23

Linux加密框架设计与实现(第一部份) [复制链接]

论坛徽章:
0
发表于 2011-12-07 09:39 |显示全部楼层
本帖最后由 独孤九贱 于 2011-12-07 09:55 编辑

好久没有在内核版发贴,主要是迫于生计压力,每天忙于糊口,要过年了,发个贴子,骗点分。

声明:文中所述观点,仅为笔者一家之言,并不保证其正确性、完整性,仅供讨论学习之用。如有转载,请注明出处与作者。

文章太长了,受发贴长度限制,全文被分成几个部份,这是第一部份,后续部份会陆续贴出来。

一、        前言
Linux加密框架是内核安全子系统的重要组成部份,同时,它又一个的独立子系统形式出现,从它出现在内核根目录下的crypto/就可以看出其地位了。
Crypto实现较为复杂,其主要体现在其OOP的设计思路和高度的对像抽像与封装模型,作者展现了其出色的架构设计水准和面向对像的抽像能力。本文力图从加密框架的重要应用,即IPSec(xfrm)的两个重要协议AH和ESP对加密框架的使用,展现其设计与实现。
内核版本:2.6.31.13

二、        算法模版
1.        模版的基本概念
算法模版是加密框架的第一个重要概念。内核中有很多算法是动态生成的,例如cbc(des)算法。内核并不存在这样的算法,它事实上是cbc和des的组合,但是内核加密框架从统一抽像管理的角度。将cbc(des)看做一个算法,在实际使用时动态分配并向内核注册该算法。这样,可以将cbc抽像为一个模版,它可以同任意的加密算法进行组合。算法模版使用结构crypto_template来描述,其结构原型:
  1. struct crypto_template {
  2.         struct list_head list;                                //模版链表成员,用于注册
  3.         struct hlist_head instances;                //算法实例链表首部
  4.         struct module *module;                                //模块指针

  5.         struct crypto_instance *(*alloc)(struct rtattr **tb);                //算法实例分配
  6.         void (*free)(struct crypto_instance *inst);                                        //算法实例释放

  7.         char name[CRYPTO_MAX_ALG_NAME];                //模版名称
  8. };
复制代码
例如,一个名为cbc的算法模版,可以用它来动态分配cbc(des),cbc(twofish)……诸如此类。

crypto/algapi.c下包含了模版的一些常用操作。最为常见的就是模版的注册与注销,其实质是对以crypto_template_list为首的链表的操作过程:
  1. static LIST_HEAD(crypto_template_list);

  2. int crypto_register_template(struct crypto_template *tmpl)
  3. {
  4.         struct crypto_template *q;
  5.         int err = -EEXIST;

  6.         down_write(&crypto_alg_sem);

  7.         //遍历crypto_template_list,看当前模板是否被注册
  8.         list_for_each_entry(q, &crypto_template_list, list) {
  9.                 if (q == tmpl)
  10.                         goto out;
  11.         }

  12.         //注册之
  13.         list_add(&tmpl->list, &crypto_template_list);
  14.         //事件通告
  15.         crypto_notify(CRYPTO_MSG_TMPL_REGISTER, tmpl);
  16.         err = 0;
  17. out:
  18.         up_write(&crypto_alg_sem);
  19.         return err;
  20. }
  21. EXPORT_SYMBOL_GPL(crypto_register_template);
复制代码
注销算法模版,除了模版本身,还有一个重要的内容是处理算法模版产生的算法实例,关于算法实例,后文详述。
  1. void crypto_unregister_template(struct crypto_template *tmpl)
  2. {
  3.         struct crypto_instance *inst;
  4.         struct hlist_node *p, *n;
  5.         struct hlist_head *list;
  6.         LIST_HEAD(users);

  7.         down_write(&crypto_alg_sem);

  8.         BUG_ON(list_empty(&tmpl->list));
  9.         //注销算法模版,并重新初始化模版的list成员
  10.         list_del_init(&tmpl->list);

  11.         //首先移除模版上的所有算法实例
  12.         list = &tmpl->instances;
  13.         hlist_for_each_entry(inst, p, list, list) {
  14.                 int err = crypto_remove_alg(&inst->alg, &users);
  15.                 BUG_ON(err);
  16.         }

  17.         crypto_notify(CRYPTO_MSG_TMPL_UNREGISTER, tmpl);

  18.         up_write(&crypto_alg_sem);
  19.        
  20.         //释放模版的所有算法实例分配的内存
  21.         hlist_for_each_entry_safe(inst, p, n, list, list) {
  22.                 BUG_ON(atomic_read(&inst->alg.cra_refcnt) != 1);
  23.                 tmpl->free(inst);
  24.         }
  25.         crypto_remove_final(&users);
  26. }
  27. EXPORT_SYMBOL_GPL(crypto_unregister_template);
复制代码
2.        算法模版的查找
  1. crypto_lookup_template函数根据名称,查找相应的模版:

  2. struct crypto_template *crypto_lookup_template(const char *name)
  3. {
  4.         return try_then_request_module(__crypto_lookup_template(name), name);
  5. }
复制代码
__crypto_lookup_template完成实质的模版模找工作,而try_then_request_module则尝试动态插入相应的内核模块,如果需要的话:
  1. static struct crypto_template *__crypto_lookup_template(const char *name)
  2. {
  3.         struct crypto_template *q, *tmpl = NULL;

  4.         down_read(&crypto_alg_sem);
  5.         //遍历crypto_template_list链,匹备模版名称
  6.         list_for_each_entry(q, &crypto_template_list, list) {
  7.                 if (strcmp(q->name, name))
  8.                         continue;
  9.                 //查找命中,需要对其增加引用,以防止其正在使用时,模块被卸载。完成该操作后返回查找到的模版
  10.                 if (unlikely(!crypto_tmpl_get(q)))
  11.                         continue;

  12.                 tmpl = q;
  13.                 break;
  14.         }
  15.         up_read(&crypto_alg_sem);

  16.         return tmpl;
  17. }
复制代码
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函数进行请求探测:
  1. struct crypto_alg *crypto_alg_mod_lookup(const char *name, u32 type, u32 mask)
  2. {
  3.         ……
  4.         ok = crypto_probing_notify(CRYPTO_MSG_ALG_REQUEST, larval);
  5.         ……
  6. }
复制代码
请求是通过通知链表来通告的:
  1. int crypto_probing_notify(unsigned long val, void *v)
  2. {
  3.         int ok;

  4.         ok = blocking_notifier_call_chain(&crypto_chain, val, v);
  5.         if (ok == NOTIFY_DONE) {
  6.                 request_module("cryptomgr");
  7.                 ok = blocking_notifier_call_chain(&crypto_chain, val, v);
  8.         }

  9.         return ok;
  10. }
复制代码
在algboss.c中注册了一个名为cryptomgr_notifier的通告块结构,其通告处理函数为cryptomgr_notify:
  1. static struct notifier_block cryptomgr_notifier = {
  2.         .notifier_call = cryptomgr_notify,
  3. };

  4. static int __init cryptomgr_init(void)
  5. {
  6.         return crypto_register_notifier(&cryptomgr_notifier);
  7. }

  8. static void __exit cryptomgr_exit(void)
  9. {
  10.         int err = crypto_unregister_notifier(&cryptomgr_notifier);
  11.         BUG_ON(err);
  12. }
复制代码
这样,当有算法被使用的时候,会调用通告块的处理函数cryptomgr_notify,因为此时的消息是CRYPTO_MSG_ALG_REQUEST,所以cryptomgr_schedule_probe进行算法的探测:
  1. static int cryptomgr_notify(struct notifier_block *this, unsigned long msg,
  2.                             void *data)
  3. {
  4.         switch (msg) {
  5.         case CRYPTO_MSG_ALG_REQUEST:
  6.                 return cryptomgr_schedule_probe(data);
  7. ……

  8.         return NOTIFY_DONE;
  9. }
复制代码
cryptomgr_schedule_probe启动一个名为cryptomgr_probe的内核线程来进行算法模版的探测:
  1. static int cryptomgr_schedule_probe(struct crypto_larval *larval)
  2. {
  3.         ……
  4.         //构造param,以供后面使用
  5.         ……
  6.         thread = kthread_run(cryptomgr_probe, param, "cryptomgr_probe");
  7.         ……
  8. }
复制代码
cryptomgr_probe完成具体的算法探测过程:
  1. static int cryptomgr_probe(void *data)
  2. {
  3.         struct cryptomgr_param *param = data;
  4.         struct crypto_template *tmpl;
  5.         struct crypto_instance *inst;
  6.         int err;

  7.         //查找算法模版
  8.         tmpl = crypto_lookup_template(param->template);
  9.         if (!tmpl)
  10.                 goto err;

  11.         //循环调用模版的alloc函数分配算法实列,并将模版注册之
  12.         //这里值得注意的是循环的条件,当返回码为-EAGAIN时,会循环再次尝试
  13.         //这样使用的一个场景后面会分析到
  14.         do {
  15.                 inst = tmpl->alloc(param->tb);
  16.                 if (IS_ERR(inst))
  17.                         err = PTR_ERR(inst);
  18.                 else if ((err = crypto_register_instance(tmpl, inst)))
  19.                         tmpl->free(inst);
  20.         } while (err == -EAGAIN && !signal_pending(current));

  21.         //查找中会增加引用,这里已经用完了释放之
  22.         crypto_tmpl_put(tmpl);

  23.         if (err)
  24.                 goto err;

  25. out:
  26.         kfree(param);
  27.         module_put_and_exit(0);

  28. err:
  29.         crypto_larval_error(param->larval, param->otype, param->omask);
  30.         goto out;
  31. }
复制代码
理解了算法的注册与查找后,再来理解这个函数就非常容易了,其核心在do{}while循环中,包含了算法实例的分配和注册动作。针对每一种算法模版,其alloc动作不尽一致。后文会对xfrm使用的算法模版一一阐述。

为什么不把“算法实例”直接称之为“算法”,这是因为实例包含了更多的内容,其由结构struct crypto_instance可以看出:
  1. struct crypto_instance {
  2.         struct crypto_alg alg;                        //对应的算法名称

  3.         struct crypto_template *tmpl;        //所属的算法模版
  4.         struct hlist_node list;                        //链表成员

  5.         void *__ctx[] CRYPTO_MINALIGN_ATTR;                //上下文信息指针
  6. };
复制代码
内核使用struct crypto_alg描述一个算法(该结构在后文使用时再来分析),可见一个算法实例除了包含其对应的算法,还包含更多的内容。

当分配成功后,cryptomgr_probe会调用crypto_register_instance将其注册,以期将来可以顺利地找到并使用它:
  1. int crypto_register_instance(struct crypto_template *tmpl,
  2.                              struct crypto_instance *inst)
  3. {
  4.         struct crypto_larval *larval;
  5.         int err;

  6.         //对算法进行合法性检查,并构造完整的驱动名称
  7.         err = crypto_check_alg(&inst->alg);
  8.         if (err)
  9.                 goto err;

  10.         //设置算法内核模块指针指向所属模版
  11.         inst->alg.cra_module = tmpl->module;

  12.         down_write(&crypto_alg_sem);

  13.         //注册算法实例对应的算法
  14.         larval = __crypto_register_alg(&inst->alg);
  15.         if (IS_ERR(larval))
  16.                 goto unlock;

  17.         //成功后,将算法再注册到所属的模版上面
  18.         hlist_add_head(&inst->list, &tmpl->instances);
  19.         //设置模版指针
  20.         inst->tmpl = tmpl;

  21. unlock:
  22.         up_write(&crypto_alg_sem);

  23.         err = PTR_ERR(larval);
  24.         if (IS_ERR(larval))
  25.                 goto err;

  26.         crypto_wait_for_test(larval);
  27.         err = 0;

  28. err:
  29.         return err;
  30. }
复制代码
注册的一个重要工作,就是调用__crypto_register_alg将实例所对应的算法注册到加密框架子系统中。算法注册成功后,上层调用者就可以调用crypto_alg_mod_lookup等函数进行查找,并使用该算法了。

论坛徽章:
0
发表于 2011-12-07 09:50 |显示全部楼层
三、        HMAC
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.        算法模版的注册与注销
  1. static struct crypto_template hmac_tmpl = {
  2.         .name = "hmac",
  3.         .alloc = hmac_alloc,
  4.         .free = hmac_free,
  5.         .module = THIS_MODULE,
  6. };
复制代码
  1. static int __init hmac_module_init(void)
  2. {
  3.         return crypto_register_template(&hmac_tmpl);
  4. }
复制代码
  1. static void __exit hmac_module_exit(void)
  2. {
  3.         crypto_unregister_template(&hmac_tmpl);
  4. }
复制代码
模版的注册与注销前文已经描述过了。

2.        算法实例的分配
当一个算法需要被使用却查找不到的时候,会尝试调用其模版对应分配相应的算法实列,这也适用于hmac,其alloc函数指针指向hmac_alloc:
  1. static struct crypto_instance * hmac_alloc (struct rtattr **tb)
  2. {
  3.         struct crypto_instance *inst;
  4.         struct crypto_alg *alg;
  5.         int err;
  6.         int ds;

  7.         //类型检查,所属算法必需为hash类型
  8.         err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_HASH);
  9.         if (err)
  10.                 return ERR_PTR(err);

  11.         //根据参数名称,查找相应的子算法,如md5,shax等
  12.         alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_HASH,
  13.                                   CRYPTO_ALG_TYPE_HASH_MASK);
  14.         //查找失败
  15.         if (IS_ERR(alg))
  16.                 return ERR_CAST(alg);

  17.         //初始化算法实例
  18.         inst = ERR_PTR(-EINVAL);
  19.        
  20.         //计算算法实列的消息摘要大小(输出大小)
  21.         ds = alg->cra_type == &crypto_hash_type ?
  22.              alg->cra_hash.digestsize :
  23.              alg->cra_type ?
  24.              __crypto_shash_alg(alg)->digestsize :
  25.              alg->cra_digest.dia_digestsize;
  26.         if (ds > alg->cra_blocksize)
  27.                 goto out_put_alg;

  28.         //分配一个算法实列,这样,一个新的算法,如hmac(md5)就横空出世了
  29.         inst = crypto_alloc_instance("hmac", alg);
  30.         //分配失败
  31.         if (IS_ERR(inst))
  32.                 goto out_put_alg;

  33.         //初始化算法实例,其相应的成员等于其子算法中的对应成员
  34.         //类型
  35.         inst->alg.cra_flags = CRYPTO_ALG_TYPE_HASH;
  36.         //优先级
  37.         inst->alg.cra_priority = alg->cra_priority;
  38.         //计算消息摘要的块长度(输入大小)
  39.         inst->alg.cra_blocksize = alg->cra_blocksize;
  40.         //对齐掩码
  41.         inst->alg.cra_alignmask = alg->cra_alignmask;
  42.         //类型指针指向crypto_hash_type
  43.         inst->alg.cra_type = &crypto_hash_type;
  44.         //消息摘要大小
  45.         inst->alg.cra_hash.digestsize = ds;

  46.         //计算算法所需的上下文空间大小
  47.         inst->alg.cra_ctxsize = sizeof(struct hmac_ctx) +
  48.                                 ALIGN(inst->alg.cra_blocksize * 2 + ds,
  49.                                       sizeof(void *));

  50.         //初始化和退出函数
  51.         inst->alg.cra_init = hmac_init_tfm;
  52.         inst->alg.cra_exit = hmac_exit_tfm;

  53.         //置相应hash算法的操作函数,包含hash函数标准的init/update/final和digest/setkey
  54.         inst->alg.cra_hash.init = hmac_init;
  55.         inst->alg.cra_hash.update = hmac_update;
  56.         inst->alg.cra_hash.final = hmac_final;
  57.         //消息摘要函数
  58.         inst->alg.cra_hash.digest = hmac_digest;
  59.         //setkey(密钥设置函数)
  60.         inst->alg.cra_hash.setkey = hmac_setkey;

  61. out_put_alg:
  62.         crypto_mod_put(alg);
  63.         return inst;
  64. }
复制代码
每个模版的alloc动作虽不同,但是它们基本上遵循一些共性的操作:
1、        合法性检验,如类型检查;
2、        取得其子算法(即被模版所包裹的算法,如hmac(md5)中,就是md5)的算法指针;
3、        调用crypto_alloc_instance分配一个相应的算法实列;
4、        对分配成功的算法实例进行实始化,这也是理解该算法实例最核心的部份,因为它初始化算法运行所需的一些必要参数和虚函数指针;

crypto_alloc_instance(algapi.c) 函数用于分配一个算法实例,这个函数有两个重要功能,一个是分配内存空间,另一个是初始化spawn。
  1. //name: 模版名称
  2. //alg:模版的子算法
  3. struct crypto_instance *crypto_alloc_instance(const char *name,
  4.                                               struct crypto_alg *alg)
  5. {
  6.         struct crypto_instance *inst;
  7.         struct crypto_spawn *spawn;
  8.         int err;

  9.         //分配一个算法实例,crypto_instance结构的最后一个成员ctx是一个指针变量,所以,在分配空间的时候,在其尾部追加相应的空间,可以使用ctx访问之。
  10.         //另一个重要的概念是,算法实例中包含了算法,这个分配,同时也完成了算法实例对应的算法的分配工作。
  11.         inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL);
  12.         if (!inst)
  13.                 return ERR_PTR(-ENOMEM);

  14.         err = -ENAMETOOLONG;
  15. //构造完成的算法名称
  16.         if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME, "%s(%s)", name,
  17.                      alg->cra_name) >= CRYPTO_MAX_ALG_NAME)
  18.                 goto err_free_inst;

  19.         //构造完整的算法驱动名称
  20.         if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s(%s)",
  21.                      name, alg->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
  22.                 goto err_free_inst;

  23.         //spawn指向算法实例的上下文成员,可以这样做是因为__ctx是一个可变长的成员,在分配实例的时候,
  24.         //在尾部增加了一个spawn的空间
  25.         spawn = crypto_instance_ctx(inst);
  26.         //初始化spawn
  27.         err = crypto_init_spawn(spawn, alg, inst,
  28.                                 CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_ASYNC);

  29.         if (err)
  30.                 goto err_free_inst;

  31.         return inst;

  32. err_free_inst:
  33.         kfree(inst);
  34.         return ERR_PTR(err);
  35. }
复制代码
crypto_instance_ctx取出算法实例的ctx指针,返回值是void *,这意味着可以根具不同的需要,将其转换为所需的类型:
  1. static inline void *crypto_instance_ctx(struct crypto_instance *inst)
  2. {
  3.         return inst->__ctx;
  4. }
复制代码
一个算法实例被分配成员后,其会被注册至加密子系统,这样,一个算法,例如,hmac(md5)就可以直接被使用了。

3.        待孵化的卵
        已经看到了从模版到算法实例的第一层抽像,每个算法在每一次被使用时,它们的运行环境不尽相同,例如,可能会拥有不同的密钥。将算法看成一个类,则在每一次运行调用时,需要为它产生一个“对像”,这在内核中被称为transform,简称为tfm。后文会详细看到分配一个tfm的过程,现在引入这一概念,主要是为了分析spawn。
加密或认证算法,在调用时,都需要分配其算法对应的tfm,在分配算法实例的同时,并没有为之分配相应的tfm结构,这是因为真正的算法还没有被调用,这并不是进行tfm结构分配的最佳地点。在初始化算法实例的时候,加密框架使用了XXX_spawn_XXX函数簇来解决这一问题。这样的算法对像,被称为spawn(卵)。也就是说,在算法实例分配的时候,只是下了一个蛋(设置好spawn),等到合适的时候来对其进行孵化,这个“合适的时候”,通常指为调用算法实际使用的时候。

在crypto_alloc_instance分配算法实例的时候,就顺便分配了spawn,然后调用crypto_init_spawn对其进行初始化:
  1. int crypto_init_spawn(struct crypto_spawn *spawn, struct crypto_alg *alg,
  2.                       struct crypto_instance *inst, u32 mask)
  3. {
  4.         int err = -EAGAIN;
  5.        
  6.         //初始化其成员
  7.         spawn->inst = inst;
  8.         spawn->mask = mask;

  9.         down_write(&crypto_alg_sem);
  10.         if (!crypto_is_moribund(alg)) {
  11.                 //加入链表,每个spawn,都被加入到算法的cra_users链,即算做算法的一个用户
  12.                 list_add(&spawn->list, &alg->cra_users);
  13.                 //spawn的alg成员指针指向当前成员,这就方便引用了
  14.                 spawn->alg = alg;
  15.                 err = 0;
  16.         }
  17.         up_write(&crypto_alg_sem);

  18.         return err;
  19. }
复制代码
所以,所谓算法的spawn的初始化,就是初始化crypto_spawn结构,核心的操作是设置其对应的算法实例、算法,以及一个加入算法的链表的过程。

论坛徽章:
0
发表于 2011-12-07 09:51 |显示全部楼层
4.        算法的初始化
有了算法实例,仅表示内核拥有这一种“算法”——加引号的意思是说,它可能并不以类似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描述:
  1. struct crypto_tfm {

  2.         u32 crt_flags;
  3.        
  4.         union {
  5.                 struct ablkcipher_tfm ablkcipher;
  6.                 struct aead_tfm aead;
  7.                 struct blkcipher_tfm blkcipher;
  8.                 struct cipher_tfm cipher;
  9.                 struct hash_tfm hash;
  10.                 struct ahash_tfm ahash;
  11.                 struct compress_tfm compress;
  12.                 struct rng_tfm rng;
  13.         } crt_u;

  14.         void (*exit)(struct crypto_tfm *tfm);
  15.        
  16.         struct crypto_alg *__crt_alg;

  17.         void *__crt_ctx[] CRYPTO_MINALIGN_ATTR;
  18. };
复制代码
这些成员的作用,将在后面一一看到,值得注意的是,针对每种算法不同,结构定义了一个名为crt_u的联合体,以对应每种算法的tfm的具体操作,例如加密/解密,求hash,压缩/解压等,加密框架引入了一组名为xxx_tfm的结构封装,xxx表示算法类型,也就是crt_u成员。其定义如下:
  1. struct ablkcipher_tfm {
  2.         int (*setkey)(struct crypto_ablkcipher *tfm, const u8 *key,
  3.                       unsigned int keylen);
  4.         int (*encrypt)(struct ablkcipher_request *req);
  5.         int (*decrypt)(struct ablkcipher_request *req);
  6.         int (*givencrypt)(struct skcipher_givcrypt_request *req);
  7.         int (*givdecrypt)(struct skcipher_givcrypt_request *req);

  8.         struct crypto_ablkcipher *base;

  9.         unsigned int ivsize;
  10.         unsigned int reqsize;
  11. };

  12. struct aead_tfm {
  13.         int (*setkey)(struct crypto_aead *tfm, const u8 *key,
  14.                       unsigned int keylen);
  15.         int (*encrypt)(struct aead_request *req);
  16.         int (*decrypt)(struct aead_request *req);
  17.         int (*givencrypt)(struct aead_givcrypt_request *req);
  18.         int (*givdecrypt)(struct aead_givcrypt_request *req);

  19.         struct crypto_aead *base;

  20.         unsigned int ivsize;
  21.         unsigned int authsize;
  22.         unsigned int reqsize;
  23. };

  24. struct blkcipher_tfm {
  25.         void *iv;
  26.         int (*setkey)(struct crypto_tfm *tfm, const u8 *key,
  27.                       unsigned int keylen);
  28.         int (*encrypt)(struct blkcipher_desc *desc, struct scatterlist *dst,
  29.                        struct scatterlist *src, unsigned int nbytes);
  30.         int (*decrypt)(struct blkcipher_desc *desc, struct scatterlist *dst,
  31.                        struct scatterlist *src, unsigned int nbytes);
  32. };

  33. struct cipher_tfm {
  34.         int (*cit_setkey)(struct crypto_tfm *tfm,
  35.                           const u8 *key, unsigned int keylen);
  36.         void (*cit_encrypt_one)(struct crypto_tfm *tfm, u8 *dst, const u8 *src);
  37.         void (*cit_decrypt_one)(struct crypto_tfm *tfm, u8 *dst, const u8 *src);
  38. };

  39. struct hash_tfm {
  40.         int (*init)(struct hash_desc *desc);
  41.         int (*update)(struct hash_desc *desc,
  42.                       struct scatterlist *sg, unsigned int nsg);
  43.         int (*final)(struct hash_desc *desc, u8 *out);
  44.         int (*digest)(struct hash_desc *desc, struct scatterlist *sg,
  45.                       unsigned int nsg, u8 *out);
  46.         int (*setkey)(struct crypto_hash *tfm, const u8 *key,
  47.                       unsigned int keylen);
  48.         unsigned int digestsize;
  49. };

  50. struct ahash_tfm {
  51.         int (*init)(struct ahash_request *req);
  52.         int (*update)(struct ahash_request *req);
  53.         int (*final)(struct ahash_request *req);
  54.         int (*digest)(struct ahash_request *req);
  55.         int (*setkey)(struct crypto_ahash *tfm, const u8 *key,
  56.                         unsigned int keylen);

  57.         unsigned int digestsize;
  58.         unsigned int reqsize;
  59. };

  60. struct compress_tfm {
  61.         int (*cot_compress)(struct crypto_tfm *tfm,
  62.                             const u8 *src, unsigned int slen,
  63.                             u8 *dst, unsigned int *dlen);
  64.         int (*cot_decompress)(struct crypto_tfm *tfm,
  65.                               const u8 *src, unsigned int slen,
  66.                               u8 *dst, unsigned int *dlen);
  67. };

  68. struct rng_tfm {
  69.         int (*rng_gen_random)(struct crypto_rng *tfm, u8 *rdata,
  70.                               unsigned int dlen);
  71.         int (*rng_reset)(struct crypto_rng *tfm, u8 *seed, unsigned int slen);
  72. };
复制代码
为了直接访问这些成员,定义了如下宏:
  1. #define crt_ablkcipher        crt_u.ablkcipher
  2. #define crt_aead        crt_u.aead
  3. #define crt_blkcipher        crt_u.blkcipher
  4. #define crt_cipher        crt_u.cipher
  5. #define crt_hash        crt_u.hash
  6. #define crt_ahash        crt_u.ahash
  7. #define crt_compress        crt_u.compress
  8. #define crt_rng                crt_u.rng
复制代码
这样,要访问hash算法的hash成员,就可以直接使用crt_hash,而不是crt_u.hash。

每种算法访问tfm都使用了二次封装,例如:
  1. struct crypto_ablkcipher {
  2.         struct crypto_tfm base;
  3. };

  4. struct crypto_aead {
  5.         struct crypto_tfm base;
  6. };

  7. struct crypto_blkcipher {
  8.         struct crypto_tfm base;
  9. };

  10. struct crypto_cipher {
  11.         struct crypto_tfm base;
  12. };

  13. struct crypto_comp {
  14.         struct crypto_tfm base;
  15. };

  16. struct crypto_hash {
  17.         struct crypto_tfm base;
  18. };

  19. struct crypto_rng {
  20.         struct crypto_tfm base;
  21. };
复制代码
其base成员就是相应算法的tfm。因为它们拥有相应的起始地址,可以很方便地强制类型转换来操作,内核为此专门定义了一组函数,以hash为例,完成这一工作的是crypto_hash_cast:
  1. static inline struct crypto_hash *__crypto_hash_cast(struct crypto_tfm *tfm)
  2. {
  3.         return (struct crypto_hash *)tfm;
  4. }

  5. static inline struct crypto_hash *crypto_hash_cast(struct crypto_tfm *tfm)
  6. {
  7.         BUG_ON((crypto_tfm_alg_type(tfm) ^ CRYPTO_ALG_TYPE_HASH) &
  8.                CRYPTO_ALG_TYPE_HASH_MASK);
  9.         return __crypto_hash_cast(tfm);
  10. }
复制代码
当然,针对各种不同的算法,还有许多不同的XXX_cast函数。这些cast函数,将tfm强制转换为其所属的算法类型的封装结构。

4.2 tfm的分配
对于算法的实始化,其核心功能就是分配一个tfm,并设置其上下文环境,例如密钥等参数,然后初始化上述struct xxx_tfm结构。对于hash类的算法来讲,分配tfm是由crypto_alloc_hash(crypt.h) 这个API来完成的,以AH为例,在其初始化过程中有:
  1. static int ah_init_state(struct xfrm_state *x)
  2. {
  3.         struct crypto_hash *tfm;
  4. ……
  5. tfm = crypto_alloc_hash(x->aalg->alg_name, 0, CRYPTO_ALG_ASYNC);
  6.         if (IS_ERR(tfm))
  7.                 goto error;
  8. ……
  9. }
复制代码
AH调用crypto_alloc_hash为SA中指定的算法(如hmac(md5))分配一个tfm,第二个参数为0,第三个参数指明了AH使用异步模式。
  1. static inline struct crypto_hash *crypto_alloc_hash(const char *alg_name,
  2.                                                     u32 type, u32 mask)
  3. {
  4.         //初始化相应的类型的掩码
  5.         type &= ~CRYPTO_ALG_TYPE_MASK;                //清除类型的CRYPTO_ALG_TYPE_MASK位
  6.         mask &= ~CRYPTO_ALG_TYPE_MASK;                //清除掩码的CRYPTO_ALG_TYPE_MASK位
  7.         type |= CRYPTO_ALG_TYPE_HASH;                //置类型CRYPTO_ALG_TYPE_HASH位
  8.         mask |= CRYPTO_ALG_TYPE_HASH_MASK;        //置掩码CRYPTO_ALG_TYPE_HASH_MASK位

  9.         //最终的分配函数是crypto_alloc_base,它分配一个base(每个算法的tfm),再将其强制类型转换为所需要结构类型
  10.         return __crypto_hash_cast(crypto_alloc_base(alg_name, type, mask));
  11. }
复制代码
crypto_alloc_base首先检查相应的算法是否存在,对于hmac(md5)这个例子,xfrm在SA的增加中,会触发相应的算法查找,最终会调用hmac模版的alloc分配算法实例(当然也包括算法本身),然后向内核注册算法及算法实例,所以,查找会命中。接下来的工作,是调用tfm的核心分配函数__crypto_alloc_tfm进行分配,其实现如下:
  1. struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask)
  2. {
  3.         struct crypto_tfm *tfm;
  4.         int err;

  5.         for (;;) {
  6.                 struct crypto_alg *alg;

  7.                 //根据算法名称,查找相应的算法,它会首先尝试已经加载的算法,如果失败,也会尝试
  8.                 //动态插入内核模块
  9.                 alg = crypto_alg_mod_lookup(alg_name, type, mask);
  10.                 //查找失败,返回退出循环
  11.                 if (IS_ERR(alg)) {
  12.                         err = PTR_ERR(alg);
  13.                         goto err;
  14.                 }

  15.                 //查找成功,为算法分配tfm
  16.                 tfm = __crypto_alloc_tfm(alg, type, mask);
  17.                 //分配成功,返回之
  18.                 if (!IS_ERR(tfm))
  19.                         return tfm;

  20.                 //释放引用计算,因为查找会增加引用
  21.                 crypto_mod_put(alg);
  22.                 //获取返回错误值,根据其值,决定是否要继续尝试
  23.                 err = PTR_ERR(tfm);

  24. err:
  25.                 if (err != -EAGAIN)
  26.                         break;
  27.                 if (signal_pending(current)) {
  28.                         err = -EINTR;
  29.                         break;
  30.                 }
  31.         }

  32.         return ERR_PTR(err);
  33. }
复制代码
__crypto_alloc_tfm是内核加密框架中又一重要的函数,它完成了对算法tfm的分配和初始化的工作:
  1. struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
  2.                                       u32 mask)
  3. {
  4.         struct crypto_tfm *tfm = NULL;
  5.         unsigned int tfm_size;
  6.         int err = -ENOMEM;

  7.         //计算tfm所需的空间大小,它包括了tfm结构本身和算法上下文大小
  8.         tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, type, mask);
  9.         //分配tfm
  10.         tfm = kzalloc(tfm_size, GFP_KERNEL);
  11.         if (tfm == NULL)
  12.                 goto out_err;

  13.         //__crt_alg成员指向其所属的算法,对于hmac而言,它就是hmac(xxx),例如hmac(md5)
  14.         tfm->__crt_alg = alg;

  15.         //初始化tfm选项
  16.         err = crypto_init_ops(tfm, type, mask);
  17.         if (err)
  18.                 goto out_free_tfm;

  19.         //调用算法的初始化函数,初始化tfm,这有个先决条件是tfm本身没有exit函数的实现
  20.         if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm)))
  21.                 goto cra_init_failed;

  22.         goto out;

  23. cra_init_failed:
  24.         crypto_exit_ops(tfm);
  25. out_free_tfm:
  26.         if (err == -EAGAIN)
  27.                 crypto_shoot_alg(alg);
  28.         kfree(tfm);
  29. out_err:
  30.         tfm = ERR_PTR(err);
  31. out:
  32.         return tfm;
  33. }
复制代码
crypto_init_ops负责初始化tfm的选项,对于一个真正的算法(例如md5、dst)和一个伪算法(我说的“伪”,是指由模版动态分配的,如hmac(xxx), authenc(xxx,xxx)),因为并不存在这样的算法,只是内核的一个抽像,故称为"伪",它们的初始化过程是截然不同的。一个伪算法,它都设置了其所属的类型cra_type,例如,对于hmac(xxx)而言,它指向了crypto_hash_type。这样,初始化时,实质上调用的是其所属类型的init函数:
  1. static int crypto_init_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
  2. {
  3.         //获取tfm所属算法的所属类型
  4.         const struct crypto_type *type_obj = tfm->__crt_alg->cra_type;

  5.         //如果设置了类型,调用类型的init
  6.         if (type_obj)
  7.                 return type_obj->init(tfm, type, mask);

  8.         //否则,判断算法的类型,调用相应的初始化函数,这些在不同的算法实现中分析
  9.         switch (crypto_tfm_alg_type(tfm)) {
  10.         case CRYPTO_ALG_TYPE_CIPHER:
  11.                 return crypto_init_cipher_ops(tfm);
  12.                
  13.         case CRYPTO_ALG_TYPE_DIGEST:
  14.                 if ((mask & CRYPTO_ALG_TYPE_HASH_MASK) !=
  15.                     CRYPTO_ALG_TYPE_HASH_MASK)
  16.                         return crypto_init_digest_ops_async(tfm);
  17.                 else
  18.                         return crypto_init_digest_ops(tfm);

  19.         case CRYPTO_ALG_TYPE_COMPRESS:
  20.                 return crypto_init_compress_ops(tfm);
  21.        
  22.         default:
  23.                 break;
  24.         }
  25.        
  26.         BUG();
  27.         return -EINVAL;
  28. }
复制代码
算法类型的概念很好理解,因为若干个hmac(xxx)都拥有一此相同的类型属性(其它伪算法同样如此),所以可以将它们抽像管理。
对于hash类型的算法而言,它们拥有一个共同的类型crypto_hash_type,其定义在hash.c中:
  1. const struct crypto_type crypto_hash_type = {
  2.         .ctxsize = crypto_hash_ctxsize,
  3.         .init = crypto_init_hash_ops,
  4. #ifdef CONFIG_PROC_FS
  5.         .show = crypto_hash_show,
  6. #endif
  7. };
复制代码
它的init函数指针指向crypto_init_hash_ops:
  1. static int crypto_init_hash_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
  2. {
  3.         struct hash_alg *alg = &tfm->__crt_alg->cra_hash;
  4.        
  5.         //其消息摘要大小不同超过1/8个页面
  6.         if (alg->digestsize > PAGE_SIZE / 8)
  7.                 return -EINVAL;

  8.         //根据掩码位,判断是同步初始化还是异步,对于crypto_alloc_hash调用下来的而言,它
  9.         //设置了CRYPTO_ALG_TYPE_HASH_MASK位,所以是同步初始化
  10.         if ((mask & CRYPTO_ALG_TYPE_HASH_MASK) != CRYPTO_ALG_TYPE_HASH_MASK)
  11.                 return crypto_init_hash_ops_async (tfm);
  12.         else
  13.                 return crypto_init_hash_ops_sync(tfm);
  14. }
复制代码
在我们AH的例子中,AH使用了异步模式,所以crypto_init_hash_ops_async会被调用。

前述hash_tfm结构封装了hash类型的算法的通用的操作:
  1. struct hash_tfm {
  2.         int (*init)(struct hash_desc *desc);
  3.         int (*update)(struct hash_desc *desc,
  4.                       struct scatterlist *sg, unsigned int nsg);
  5.         int (*final)(struct hash_desc *desc, u8 *out);
  6.         int (*digest)(struct hash_desc *desc, struct scatterlist *sg,
  7.                       unsigned int nsg, u8 *out);
  8.         int (*setkey)(struct crypto_hash *tfm, const u8 *key,
  9.                       unsigned int keylen);
  10.         unsigned int digestsize;
  11. };
复制代码
先来看同步模式的初始化操作,crypto_init_hash_ops_sync函数负责初始化这一结构:
  1. static int crypto_init_hash_ops_sync(struct crypto_tfm *tfm)
  2. {
  3.         struct hash_tfm *crt = &tfm->crt_hash;
  4.         struct hash_alg *alg = &tfm->__crt_alg->cra_hash;

  5.         //置tfm相应操作为算法本身的对应操作,
  6.         //对于hmac(xxx)算法而言,这些东东在hmac_alloc中已经初始化过了,也就是hmac_init等函数
  7.         crt->init       = alg->init;
  8.         crt->update     = alg->update;
  9.         crt->final      = alg->final;
  10.         crt->digest     = alg->digest;
  11.         crt->setkey     = hash_setkey;
  12.         crt->digestsize = alg->digestsize;

  13.         return 0;
  14. }
复制代码
异步模式则稍有不同,它使用了hash类型算法的通用函数:
  1. static int crypto_init_hash_ops_async(struct crypto_tfm *tfm)
  2. {
  3.         struct ahash_tfm *crt = &tfm->crt_ahash;
  4.         struct hash_alg  *alg = &tfm->__crt_alg->cra_hash;

  5.         crt->init       = hash_async_init;
  6.         crt->update     = hash_async_update;
  7.         crt->final      = hash_async_final;
  8.         crt->digest     = hash_async_digest;
  9.         crt->setkey     = hash_async_setkey;
  10.         crt->digestsize = alg->digestsize;

  11.         return 0;
  12. }
复制代码
不论是同步还是异步,算法的tfm都得到的相应的初始化。回到__crypto_alloc_tfm中来,__crypto_alloc_tfm函数的最后一步是调用算法的cra_init函数(如果它存在的话),对于hmac(xxx)而言,它在分配的时候指向hmac_init_tfm。hmac_init_tfm的主要工作就是对hmac(xxx)的spawn进行孵化操作。还记得“待孵化的卵”吗?前面讲了只是初始化它,现在到了孵化的时候了。
  1. static int hmac_init_tfm(struct crypto_tfm *tfm)
  2. {
  3.         struct crypto_hash *hash;
  4.         //因为算法实例的第一个成员就是alg,在注册算法时,就是注册的它,所以可以很方便地通过tfm的__crt_alg强制类型转换得到对应的算法实例
  5.         struct crypto_instance *inst = (void *)tfm->__crt_alg;
  6.         //取得算法实例的__ctx域,也就是spawn
  7.         struct crypto_spawn *spawn = crypto_instance_ctx(inst);
  8.         //取得tfm的上下文指针
  9.         struct hmac_ctx *ctx = hmac_ctx(__crypto_hash_cast(tfm));

  10.         //对hmac(xxx)进行孵化,以hmac(md5)为例,这将得到一个md5算法的tfm,当然,通过强制类型转换,它被封装在结构crypto_hash中
  11.         hash = crypto_spawn_hash(spawn);
  12.         if (IS_ERR(hash))
  13.                 return PTR_ERR(hash);

  14.         //设置子算法指向孵化的tfm
  15.         ctx->child = hash;
  16.         return 0;
  17. }
复制代码
crypto_spawn_hash展示了如何对hash算法簇进行spawn的孵化操作:
  1. static inline struct crypto_hash *crypto_spawn_hash(struct crypto_spawn *spawn)
  2. {
  3.         //初始化孵化所需的类型和掩码
  4.         u32 type = CRYPTO_ALG_TYPE_HASH;
  5.         u32 mask = CRYPTO_ALG_TYPE_HASH_MASK;

  6.         //调用crypto_spawn_tfm孵化一个tfm,并强制类型转换
  7.         return __crypto_hash_cast(crypto_spawn_tfm(spawn, type, mask));
  8. }
复制代码
最后的任务交给了crypto_spawn_tfm函数,它为算法孵化一个tfm,因为spawn的alg成员指向了所要孵化的算法,使得这一操作很容易实现:
  1. struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn, u32 type,
  2.                                     u32 mask)
  3. {
  4.         struct crypto_alg *alg;
  5.         struct crypto_alg *alg2;
  6.         struct crypto_tfm *tfm;

  7.         down_read(&crypto_alg_sem);
  8.         //要孵化的spawn所属的算法
  9.         alg = spawn->alg;
  10.         alg2 = alg;
  11.         //查找算法所属模块
  12.         if (alg2)
  13.                 alg2 = crypto_mod_get(alg2);
  14.         up_read(&crypto_alg_sem);

  15.         //如果其所属模块没了,则标注算法为DYING,出错退回
  16.         if (!alg2) {
  17.                 if (alg)
  18.                         crypto_shoot_alg(alg);
  19.                 return ERR_PTR(-EAGAIN);
  20.         }

  21.         //初始化tfm
  22.         tfm = ERR_PTR(-EINVAL);
  23.         //验证掩码标志位
  24.         if (unlikely((alg->cra_flags ^ type) & mask))
  25.                 goto out_put_alg;

  26.         //为算法分配相应的tfm,这样,一个算法的spawn就孵化完成了
  27.         tfm = __crypto_alloc_tfm(alg, type, mask);
  28.         if (IS_ERR(tfm))
  29.                 goto out_put_alg;

  30.         return tfm;

  31. out_put_alg:
  32.         crypto_mod_put(alg);
  33.         return tfm;
  34. }
复制代码
又绕回了__crypto_alloc_tfm函数,其实现之前已经分析过了,对于一个普通的算法(非模版产生的算法,如md5),其初始化工作略有不同,在了解其初始化工作之前,需要对一个实际的算法作了解。

顺例说一句,内核的这种抽像管理方式,功能异常地强大,可以想像,它可以抽像更多层的嵌套。所以hmac(xxx)中,xxx不一定就是一个md5之类,可能还是一层形如xxx(xxx)的抽像,理论上,它可以像变形金刚一样。

4.3 小结一下
本节分析了一个算法的tfm是如何生成的,因为算法可以是多层的组装,在生成上层算法的同时,它也要为其所包含的算法分配tfm,这一过程称之为spawn。

论坛徽章:
0
发表于 2011-12-07 09:52 |显示全部楼层
5.        MD5的tfm分配
5.1 md5结构
内核加密框架中,所有的hash算法用结构shash_alg描述,它封装了crypto_alg和hash函数所需的一些特别的元素,其定义如下:
  1. struct shash_alg {
  2.         //hash函数的各种操作
  3.         int (*init)(struct shash_desc *desc);
  4.         int (*reinit)(struct shash_desc *desc);
  5.         int (*update)(struct shash_desc *desc, const u8 *data,
  6.                       unsigned int len);
  7.         int (*final)(struct shash_desc *desc, u8 *out);
  8.         int (*finup)(struct shash_desc *desc, const u8 *data,
  9.                      unsigned int len, u8 *out);
  10.         int (*digest)(struct shash_desc *desc, const u8 *data,
  11.                       unsigned int len, u8 *out);
  12.         int (*setkey)(struct crypto_shash *tfm, const u8 *key,
  13.                       unsigned int keylen);
  14.        
  15.         //算法所需的私有空间大小
  16.         unsigned int descsize;
  17.         //摘要大小
  18.         unsigned int digestsize;

  19.         //封装的算法结构
  20.         struct crypto_alg base;
  21. };
复制代码
md5算法结构定义在md5.c中:
  1. static struct shash_alg alg = {
  2.         .digestsize        =        MD5_DIGEST_SIZE,                //md5算法输出16字节定长的摘要信息
  3.         .init                =        md5_init,                                        //算法初始化函数
  4.         .update                =        md5_update,                        //算法核心函数
  5.         .final                =        md5_final,                        //结束处理
  6.         .descsize        =        sizeof(struct md5_ctx),                //私有的上下文结构大小
  7.         .base                =        {                                                                //base成员,crypto_alg结构
  8.                 .cra_name        =        "md5",                                                //名称
  9.                 .cra_flags        =        CRYPTO_ALG_TYPE_SHASH,                //类型标志
  10.                 .cra_blocksize        =        MD5_HMAC_BLOCK_SIZE,        //HMAC块大小
  11.                 .cra_module        =        THIS_MODULE,                                //所属模块
  12.         }
  13. };
复制代码
5.2 算法的注册与注销
//初始化注册之
  1. static int __init md5_mod_init(void)
  2. {
  3.         return crypto_register_shash(&alg);
  4. }
复制代码
//注销函数
  1. static void __exit md5_mod_fini(void)
  2. {
  3.         crypto_unregister_shash(&alg);
  4. }
复制代码
crypto_register_shash实质上还是调用统一接口crypto_register_alg注册,在注册之前,针对算法的特性,做了一些检查之类的动作:
  1. int crypto_register_shash(struct shash_alg *alg)
  2. {
  3.         //内核使用crypto_alg结构低层抽像描述一个算法,hash算法使用结构struct shash_alg来进行高层封装,
  4.         //所以后者包含了前者,这是通过其base成员实现的。base成员的部份子成员在初始化时已经赋值。
  5.         struct crypto_alg *base = &alg->base;

  6.         //合法性验证,要求digestsize和descsize大小不能超过8分之一的页面
  7.         if (alg->digestsize > PAGE_SIZE / 8 ||
  8.             alg->descsize > PAGE_SIZE / 8)
  9.                 return -EINVAL;

  10.         //当前算法指向一个上层的名称crypo_shash_type,它是所有hash算法的类型的上层统一类型抽像,
  11.         //与hmac(xxx)不同的是,后者是crypto_hash_type
  12.         base->cra_type = &crypto_shash_type;
  13.         //置算法的标志位
  14.         base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
  15.         base->cra_flags |= CRYPTO_ALG_TYPE_SHASH;

  16.         //注册算法
  17.         return crypto_register_alg(base);
  18. }
复制代码
通用的算法注册接口是crypto_register_alg,这个函数在算法实例的注册中已经看到过,只是当时并不是展开分析它的一个好时机:
  1. int crypto_register_alg(struct crypto_alg *alg)
  2. {
  3.         struct crypto_larval *larval;
  4.         int err;

  5.         //合法性检查,如果合法,构造算法的驱动完整名称,它以“-generic”为后缀
  6.         err = crypto_check_alg(alg);
  7.         if (err)
  8.                 return err;

  9.         down_write(&crypto_alg_sem);
  10.         larval = __crypto_register_alg(alg);
  11.         up_write(&crypto_alg_sem);

  12.         if (IS_ERR(larval))
  13.                 return PTR_ERR(larval);

  14.         crypto_wait_for_test(larval);
  15.         return 0;
  16. }
复制代码
所有的算法都注册到一个全局算法链表crypto_alg_list,crypto_alg有一个链表成员cra_list,用于注册。注册的实质,就是一个链表插入操作。

特别地,对于每一个算法,都有一个与之对应的larval(幼虫???),larval本身也是一个算法,只是其标志位标注为CRYPTO_ALG_LARVAL,它同样在注册算法的同时,也被注册到crypto_alg_list。每个larval用结构crypto_larval描述:
  1. struct crypto_larval {
  2.         struct crypto_alg alg;                        //算法结构
  3.         struct crypto_alg *adult;                        //成年人^o^,指向其所属的算法
  4.         struct completion completion;        //???
  5.         u32 mask;                                        //掩码位
  6. };
复制代码
crypto_larval_alloc函数完成算法的larval的分配:
  1. struct crypto_larval *crypto_larval_alloc(const char *name, u32 type, u32 mask)
  2. {
  3.         struct crypto_larval *larval;

  4.         //分配larval
  5.         larval = kzalloc(sizeof(*larval), GFP_KERNEL);
  6.         if (!larval)
  7.                 return ERR_PTR(-ENOMEM);

  8.         //置掩码位与对应的adult算法一致
  9.         larval->mask = mask;
  10.         //算法标志位置CRYPTO_ALG_LARVAL
  11.         larval->alg.cra_flags = CRYPTO_ALG_LARVAL | type;
  12.         //算法优先级衡为1
  13.         larval->alg.cra_priority = -1;
  14.         //置destroy函数
  15.         larval->alg.cra_destroy = crypto_larval_destroy;

  16.         //拷贝算法名称
  17.         strlcpy(larval->alg.cra_name, name, CRYPTO_MAX_ALG_NAME);
  18.         //初始化???
  19.         init_completion(&larval->completion);

  20.         return larval;
  21. }
复制代码
__crypto_register_alg函数完成算法及其larval的注册:
  1. static struct crypto_larval *__crypto_register_alg(struct crypto_alg *alg)
  2. {
  3.         struct crypto_alg *q;
  4.         struct crypto_larval *larval;
  5.         int ret = -EAGAIN;
  6.        
  7.         //判断算法是否处于DEAD状态,CRYPTO_ALG_DEAD
  8.         if (crypto_is_dead(alg))
  9.                 goto err;

  10.         //初始化链表,准备注册
  11.         INIT_LIST_HEAD(&alg->cra_users);

  12.         /* No cheating! */
  13.         //清除CRYPTO_ALG_TESTED标志位
  14.         alg->cra_flags &= ~CRYPTO_ALG_TESTED;

  15.         //初始化返回值:“已经存在”
  16.         ret = -EEXIST;

  17.         //置引用计算器
  18.         atomic_set(&alg->cra_refcnt, 1);
  19.         //遍历算法链表,查找算法是否存在
  20.         list_for_each_entry(q, &crypto_alg_list, cra_list) {
  21.                 //已经存在
  22.                 if (q == alg)
  23.                         goto err;

  24.                 //如果遍历到的算法处于垂死状态(CRYPTO_ALG_DEAD | CRYPTO_ALG_DYING),忽略之
  25.                 if (crypto_is_moribund(q))
  26.                         continue;

  27.                 //如果遍历到的算法是否是一个larval,若是,且刚好与待注册算法名称匹备,出错返回,否则忽略之
  28.                 if (crypto_is_larval(q)) {
  29.                         if (!strcmp(alg->cra_driver_name, q->cra_driver_name))
  30.                                 goto err;
  31.                         continue;
  32.                 }

  33.                 //判断是否已经注册
  34.                 if (!strcmp(q->cra_driver_name, alg->cra_name) ||
  35.                     !strcmp(q->cra_name, alg->cra_driver_name))
  36.                         goto err;
  37.         }

  38.         //分配larval
  39.         larval = crypto_larval_alloc(alg->cra_name,
  40.                                      alg->cra_flags | CRYPTO_ALG_TESTED, 0);
  41.         if (IS_ERR(larval))
  42.                 goto out;

  43.         ret = -ENOENT;
  44.         //adult指针指向所属算法
  45.         larval->adult = crypto_mod_get(alg);
  46.         if (!larval->adult)
  47.                 goto free_larval;

  48.         //初始化引用计数器
  49.         atomic_set(&larval->alg.cra_refcnt, 1);
  50.         //拷贝驱动名称
  51.         memcpy(larval->alg.cra_driver_name, alg->cra_driver_name,
  52.                CRYPTO_MAX_ALG_NAME);
  53.         //置优先级
  54.         larval->alg.cra_priority = alg->cra_priority;

  55.         //注册算法与算法的larval算法
  56.         list_add(&alg->cra_list, &crypto_alg_list);
  57.         list_add(&larval->alg.cra_list, &crypto_alg_list);

  58. out:       
  59.         return larval;

  60. free_larval:
  61.         kfree(larval);
  62. err:
  63.         larval = ERR_PTR(ret);
  64.         goto out;
  65. }
复制代码
有注册就有注销,crypto_unregister_shash函数完成相应的注销操作,类似地,它是crypto_unregister_alg的包裹:
  1. int crypto_unregister_shash(struct shash_alg *alg)
  2. {
  3.         return crypto_unregister_alg(&alg->base);
  4. }
复制代码
  1. int crypto_unregister_alg(struct crypto_alg *alg)
  2. {
  3.         int ret;
  4.         LIST_HEAD(list);
  5.        
  6.         //crypto_remove_alg将算法从全局链中删除,并清空算法所有的spawn
  7.         down_write(&crypto_alg_sem);
  8.         ret = crypto_remove_alg(alg, &list);
  9.         up_write(&crypto_alg_sem);

  10.         //都没有看到返回大于0的地方
  11.         if (ret)
  12.                 return ret;

  13.         //验证引用计数器
  14.         BUG_ON(atomic_read(&alg->cra_refcnt) != 1);
  15.         //如果有destroy,调用之
  16.         if (alg->cra_destroy)
  17.                 alg->cra_destroy(alg);

  18.         crypto_remove_final(&list);
  19.         return 0;
  20. }
复制代码
5.3 分配tfm
在hmac(md5)中调用crypto_spawn_tfm进行算法的孵化,这将调用__crypto_alloc_tfm为真实hash算法分配tfm。因为__crypto_alloc_tfm函数已经分析过了,对于md5算法而言,重点需要关注的就是其选项初始化和cra_init函数。

在注册md5算法(或其它hash算法时),设置了算法相应的类型:
  1. base->cra_type = &crypto_shash_type;

  2. crypto_shash_type定义在crypto/shash.c中,其定义如下:
  3. static const struct crypto_type crypto_shash_type = {
  4.         .ctxsize = crypto_shash_ctxsize,
  5.         .extsize = crypto_shash_extsize,
  6.         .init = crypto_init_shash_ops,
  7.         .init_tfm = crypto_shash_init_tfm,
  8. #ifdef CONFIG_PROC_FS
  9.         .show = crypto_shash_show,
  10. #endif
  11.         .maskclear = ~CRYPTO_ALG_TYPE_MASK,
  12.         .maskset = CRYPTO_ALG_TYPE_MASK,
  13.         .type = CRYPTO_ALG_TYPE_SHASH,
  14.         .tfmsize = offsetof(struct crypto_shash, base),
  15. };
复制代码
这里关注的是它的init函数,同hmac类似,选项初始化将触发crypto_shash_type的crypto_init_shash_ops函数:
  1. static int crypto_init_shash_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
  2. {
  3.         switch (mask & CRYPTO_ALG_TYPE_MASK) {
  4.         case CRYPTO_ALG_TYPE_HASH_MASK:
  5.                 return crypto_init_shash_ops_compat(tfm);
  6.         case CRYPTO_ALG_TYPE_AHASH_MASK:
  7.                 return crypto_init_shash_ops_async(tfm);
  8.         }

  9.         return -EINVAL;
  10. }
复制代码
crypto_init_shash_ops函数根据掩码位判断是否使用异步初始化,在上层算法孵化过程中,crypto_spawn_hash函数设置了相应的类型和掩码标志:
  1.         u32 type = CRYPTO_ALG_TYPE_HASH;
  2.         u32 mask = CRYPTO_ALG_TYPE_HASH_MASK;
复制代码
所以,如果是由crypto_spawn_hash触发的spawn动作,这crypto_init_shash_ops_compat
函数将被调用:
  1. static int crypto_init_shash_ops_compat(struct crypto_tfm *tfm)
  2. {
  3.         struct hash_tfm *crt = &tfm->crt_hash;                                //事实上是crt_u.hash
  4.         struct crypto_alg *calg = tfm->__crt_alg;                        //tfm对应的算法,这里也就是md5算法对应的crypto_alg结构
  5.         struct shash_alg *alg = __crypto_shash_alg(calg);        //上层封装的hash算法,hash算法结构shash_alg中通过base字段封装了crypto_alg
  6.         struct shash_desc *desc = crypto_tfm_ctx(tfm);                //取得tfm上下文成员,即__crt_ctx
  7.         struct crypto_shash *shash;                                                        //crypto_hash是hash算法的tfm上层封装

  8.         //尝试插入算法模块,如果是以模块编译存在的话
  9.         if (!crypto_mod_get(calg))
  10.                 return -EAGAIN;

  11.         //再为算法分配一个tfm,用于设置tfm的__crt_ctx成员
  12.         shash = crypto_create_tfm(calg, &crypto_shash_type);
  13.         if (IS_ERR(shash)) {
  14.                 crypto_mod_put(calg);
  15.                 return PTR_ERR(shash);
  16.         }
  17.        
  18.         //desc实质上指向的是tfm的上下文成员,它的空间在分配tfm时候已经分配,所以这里可以直接地使用。
  19.         //__crt_ctx是一个void *类型,这里被强制转换为名为desc的struct shash_desc,其第一个成员又是tfm(结构crypto_shash)
  20.         //这里指向分配到的shash
  21.         desc->tfm = shash;
  22.         //注册tfm的exit动作
  23.         tfm->exit = crypto_exit_shash_ops_compat;

  24.         //设置tfm的hash操作的各种动作
  25.         crt->init = shash_compat_init;
  26.         crt->update = shash_compat_update;
  27.         crt->final  = shash_compat_final;
  28.         crt->digest = shash_compat_digest;
  29.         crt->setkey = shash_compat_setkey;

  30.         crt->digestsize = alg->digestsize;

  31.         return 0;
  32. }
复制代码
与hmac(xxx)初始化选项相比有两个重要的不同:
1、tfm对应的算法已经没有spawn,它调用crypto_create_tfm为tfm再次分配了一个tfm,可以通过其上下文成员,强制类型转换为
shash_desc结构后来访问;
2、tfm登记注册了exit动作,指向crypto_exit_shash_ops_compat;

简单地来看一下crypto_create_tfm函数,这个函数创建一个tfm,名为create,而非alloc,它有两个参数,一个是所属的算法,一个是所属的类型(称为前端-frontend):
  1. void *crypto_create_tfm(struct crypto_alg *alg,
  2.                         const struct crypto_type *frontend)
  3. {
  4.         char *mem;
  5.         struct crypto_tfm *tfm = NULL;
  6.         unsigned int tfmsize;
  7.         unsigned int total;
  8.         int err = -ENOMEM;

  9.         //计算需要的总大小:前端类型的大小,加自身结构的大小,加一个扩展空间,扩展大小根据具体算法而定
  10.         tfmsize = frontend->tfmsize;
  11.         total = tfmsize + sizeof(*tfm) + frontend->extsize(alg, frontend);

  12.         //分配之
  13.         mem = kzalloc(total, GFP_KERNEL);
  14.         if (mem == NULL)
  15.                 goto out_err;

  16.         //跳过前端大小
  17.         tfm = (struct crypto_tfm *)(mem + tfmsize);
  18.         //关联实际所属的算法
  19.         tfm->__crt_alg = alg;
  20.        
  21.         //调用前端类型的实始化tfm函数,对于crypto_shash_type来讲,它是crypto_shash_init_tfm
  22.         err = frontend->init_tfm(tfm, frontend);
  23.         if (err)
  24.                 goto out_free_tfm;

  25.         //初始化tfm,如果算法有初始化函数的话,则调用初始化函数初始tfm。
  26.         if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm)))
  27.                 goto cra_init_failed;

  28.         goto out;

  29. cra_init_failed:
  30.         crypto_exit_ops(tfm);
  31. out_free_tfm:
  32.         if (err == -EAGAIN)
  33.                 crypto_shoot_alg(alg);
  34.         kfree(mem);
  35. out_err:
  36.         mem = ERR_PTR(err);
  37. out:
  38.         return mem;
  39. }
复制代码
对于新的tfm来讲,它的初始化选项函数是crypto_shash_init_tfm函数:
  1. static int crypto_shash_init_tfm(struct crypto_tfm *tfm,
  2.                                  const struct crypto_type *frontend)
  3. {
  4.         return 0;
  5. }
复制代码
shash并没有实现这一函数。

另外,算法的cra_init函数将被调用,它会对新的tfm进行初始化操作。在md5初始化的时候,没有设置其base成员的cra_init。

回到最上层的tfm的分配过程中,在初始化选项完成后,因为它的exit已经登记注册为crypto_exit_shash_ops_compat,所以它的cra_init初始化操作也不会被触发。

6.        密钥设置
经过以上步骤,算法hmac(md5)的tfm已经分配完成了,下一步就是为它设置密钥及其它参数,为数据到来做好准备。以ah协议为例:
  1.         tfm = crypto_alloc_hash(x->aalg->alg_name, 0, CRYPTO_ALG_ASYNC);
  2.         if (IS_ERR(tfm))
  3.                 goto error;

  4.         ahp->tfm = tfm;
  5.         if (crypto_hash_setkey(tfm, x->aalg->alg_key,
  6.                                (x->aalg->alg_key_len + 7) / 8))
  7.                 goto error;
复制代码
对于hash算法簇,crypto_hash_setkey在最上层封装了密钥设置操作:
  1. static inline int crypto_hash_setkey(struct crypto_hash *hash,
  2.                                      const u8 *key, unsigned int keylen)
  3. {
  4.         return crypto_hash_crt(hash)->setkey(hash, key, keylen);
  5. }
复制代码
上层调用者调用crypto_hash_setkey 进行密钥设置,它包含了三个参数:

        算法的tfm
        密钥
        密钥长度

这将会最终调用hmac(md5)算法的tfm的crt_hash(它是hash_tfm结构,封装了算法的具体操作)setkey函数,
对于异步模式而言,setkey函数指针在crypto_init_hash_ops_async中被指向了hash_async_setkey函数:
  1. static int hash_async_setkey(struct crypto_ahash *tfm_async, const u8 *key,
  2.                         unsigned int keylen)
  3. {
  4.         //取得算法的tfm
  5. struct crypto_tfm  *tfm      = crypto_ahash_tfm(tfm_async);
  6. //强制类型转换为hash算法簇的tfm
  7.         struct crypto_hash *tfm_hash = __crypto_hash_cast(tfm);
  8.         //取得对应的算法
  9.         struct hash_alg    *alg      = &tfm->__crt_alg->cra_hash;
  10.        
  11.         //调用算法的setkey
  12.         return alg->setkey(tfm_hash, key, keylen);
  13. }
复制代码
对于hmac(md5)而言,算法的setkey函数指针是在模版分配算法实例时初始化的:
inst->alg.cra_hash.setkey = hmac_setkey;

hmac_setkey函数完成算法的密钥设置工作:
  1. static int hmac_setkey(struct crypto_hash *parent,
  2.                        const u8 *inkey, unsigned int keylen)
  3. {
  4.         int bs = crypto_hash_blocksize(parent);                        //取得bs
  5.         int ds = crypto_hash_digestsize(parent);                //取得ds
  6.         char *ipad = crypto_hash_ctx_aligned(parent);        //取得ctx空间,ipad指向这段区域
  7.         char *opad = ipad + bs;                                                        //opad跳过bs
  8.         char *digest = opad + bs;                                                //digest再跳过一个bs
  9.         struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *));
  10.         struct crypto_hash *tfm = ctx->child;
  11.         unsigned int i;

  12.         //hmac算法的密钥长度不能超过bs,并且,一般建议其长度不小于ds。这里处理keylen大于bs的情况,需要
  13.         //重新计算一个密钥和密钥长度,计算的过程就是对密钥求hash签名,把签名值做为密钥。
  14.         if (keylen > bs) {
  15.                 struct hash_desc desc;
  16.                 struct scatterlist tmp;
  17.                 int tmplen;
  18.                 int err;

  19.                 desc.tfm = tfm;
  20.                 desc.flags = crypto_hash_get_flags(parent);
  21.                 desc.flags &= CRYPTO_TFM_REQ_MAY_SLEEP;

  22.                 err = crypto_hash_init(&desc);
  23.                 if (err)
  24.                         return err;

  25.                 tmplen = bs * 2 + ds;
  26.                 sg_init_one(&tmp, ipad, tmplen);

  27.                 for (; keylen > tmplen; inkey += tmplen, keylen -= tmplen) {
  28.                         memcpy(ipad, inkey, tmplen);
  29.                         err = crypto_hash_update(&desc, &tmp, tmplen);
  30.                         if (err)
  31.                                 return err;
  32.                 }

  33.                 if (keylen) {
  34.                         memcpy(ipad, inkey, keylen);
  35.                         err = crypto_hash_update(&desc, &tmp, keylen);
  36.                         if (err)
  37.                                 return err;
  38.                 }

  39.                 err = crypto_hash_final(&desc, digest);
  40.                 if (err)
  41.                         return err;

  42.                 inkey = digest;
  43.                 keylen = ds;
  44.         }

  45.         //构造算法密钥,先拷贝输入的密钥
  46.         memcpy(ipad, inkey, keylen);
  47.         //然后补齐bs -keylen,在尾部填充0
  48.         memset(ipad + keylen, 0, bs - keylen);
  49.         //拷贝ipad至opad
  50.         memcpy(opad, ipad, bs);

  51.         //hmac算法本身需要两个参数ipad和opad,分别表示0x36和0x5c重复bs次,
  52.         //然后它将密钥补齐0后,与ipad和opad求异或,这里作者实现是直接将密钥拷贝
  53.         //到ipad/opad后,直接求异或,减少了一个变量的使用。
  54.         for (i = 0; i < bs; i++) {
  55.                 ipad[i] ^= 0x36;
  56.                 opad[i] ^= 0x5c;
  57.         }

  58.         return 0;
  59. }
复制代码

论坛徽章:
0
发表于 2011-12-07 09:53 |显示全部楼层
8.        MD5的实现
上一节展示了init/update/final的三个步骤,对于md5算法而言,其相应的实现为md5_init/ md5_update/ md5_final:
  1. static struct shash_alg alg = {
  2.         .digestsize        =        MD5_DIGEST_SIZE,
  3.         .init                =        md5_init,
  4.         .update                =        md5_update,
  5.         .final                =        md5_final,
  6.         .descsize        =        sizeof(struct md5_ctx),
  7.         .base                =        {
  8.                 .cra_name        =        "md5",
  9.                 .cra_flags        =        CRYPTO_ALG_TYPE_SHASH,
  10.                 .cra_blocksize        =        MD5_HMAC_BLOCK_SIZE,
  11.                 .cra_module        =        THIS_MODULE,
  12.         }
  13. };
复制代码
8.1 md5_init
md5初始化MD缓存冲:一个用于计算hash值的128位缓冲区。这个缓冲区由4个32位的寄存器A、B、C、D。其值分别为:
A: 01 23 45 67
B: 89 ab cd ef
C: fe dc ba 98
D: 76 54 32 10

每一个调用md5算法的实列,使用一个struct shash_desc结构的__ctx成员来存储上述4个寄存器。在实际操作过程中,使用结构struct md5_ctx来描述之:
  1. struct shash_desc {
  2.         struct crypto_shash *tfm;
  3.         u32 flags;

  4.         void *__ctx[] CRYPTO_MINALIGN_ATTR;
  5. };

  6. #define MD5_BLOCK_WORDS                16
  7. #define MD5_HASH_WORDS                4

  8. struct md5_ctx {
  9.         u32 hash[MD5_HASH_WORDS];                //寄存器槽位
  10.         u32 block[MD5_BLOCK_WORDS];                //MD5每次处理的数据块,一共是16 * 4 * 8 = 512位
  11.         u64 byte_count;                                        //计数器,标志一共处理了多少字节数据
  12. };
复制代码
md5_init初始化的工作,主要就是初始化上述四个寄存器:
  1. static int md5_init(struct shash_desc *desc)
  2. {
  3.         struct md5_ctx *mctx = shash_desc_ctx(desc);

  4.         mctx->hash[0] = 0x67452301;
  5.         mctx->hash[1] = 0xefcdab89;
  6.         mctx->hash[2] = 0x98badcfe;
  7.         mctx->hash[3] = 0x10325476;
  8.         mctx->byte_count = 0;

  9.         return 0;
  10. }
复制代码
8.2 md5_update
  1. static int md5_update(struct shash_desc *desc, const u8 *data, unsigned int len)
  2. {
  3.         struct md5_ctx *mctx = shash_desc_ctx(desc);
  4.         const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);

  5.         //累计计数器
  6.         mctx->byte_count += len;

  7.         if (avail > len) {
  8.                 memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
  9.                        data, len);
  10.                 return 0;
  11.         }

  12.         memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
  13.                data, avail);

  14.         md5_transform_helper(mctx);
  15.         data += avail;
  16.         len -= avail;

  17.         //md5每次按sizeof(mctx->block)大小来处理数据块(64字节或512位)
  18.         //这个循环中,每次从data从拷贝512位出来至mctx->block,做为算法的输入项。
  19.         //然后调用md5_transform_helper进行四轮转换,直接数据被处理完
  20.         while (len >= sizeof(mctx->block)) {
  21.                 //取数据块
  22.                 memcpy(mctx->block, data, sizeof(mctx->block));
  23.                 //四轮转换
  24.                 md5_transform_helper(mctx);
  25.                 //data跳过该块向后移至下一个待处理的数据块开始位置
  26.                 data += sizeof(mctx->block);
  27.                 //数据长度递减块大小
  28.                 len -= sizeof(mctx->block);
  29.         }
  30.        
  31.         //将最后没有处理完的拷贝出来,这里需要注意的是,
  32.         //block中的数据后面是有一部份为空的(如果处理长不是刚才为块的倍数的话)
  33.         memcpy(mctx->block, data, len);

  34.         return 0;
  35. }
复制代码
8.3 md5_final
因为最后还有一部份不足512位的数据块,算法要求对其进行补齐:
  1. static int md5_final(struct shash_desc *desc, u8 *out)
  2. {
  3.         struct md5_ctx *mctx = shash_desc_ctx(desc);
  4.         //如update中所述,block中有一部份为空,则要寻找补齐位置,
  5.         //所以,这里要计算一个offset,寻找补齐位置。
  6.         //在update函数中,都是按512位(64字节,0x40)为块单位进行处理的。这里要计算后面为空的开始偏移,
  7.         //只需要用总的处理数来 & (0x40 - 1 = 0x3f)计算即可。
  8.         const unsigned int offset = mctx->byte_count & 0x3f;
  9.         //p指向空白待补齐部份
  10.         char *p = (char *)mctx->block + offset;
  11.         //计算需要补齐的字节数,md5算法对补齐的要求是,补齐后的消息长度比512位的整数倍少64,即448,即56字节,
  12.         //所以这里计算是用56去减,+1是因为有一个字节要特殊处理,即紧跟着那一句。
  13.         int padding = 56 - (offset + 1);

  14.         //md5对于补齐填充的内容要求是,一个位的1和若干位的0,所以这里先将第一个字节置为0x80(二进制为10000000),
  15.         //即先填充了第一个字节
  16.         *p++ = 0x80;
  17.         //pading小于0,意味着剩余的数据大于448位,但是小于512位。
  18.         if (padding < 0) {
  19.                 //在这种情况下,仍然需要填充,pading为负值,但最大不会超过8过字节(如果是==8,
  20.                 //即刚好为512的整数部,在update中就被处理了。
  21.                 //所以,这里使用padding + sizeof(u64),计算出从offset到512位的结束位置那片区域,将其置0。
  22.                 memset(p, 0x00, padding + sizeof (u64));
  23.                 //这样,又有一个完整的block了,再对齐进行四轮转换。
  24.                 md5_transform_helper(mctx);
  25.                 //置p为block开始位置,填弃总是有的:这里重置padding为56,则表示整片448字节全填充。加上前一个
  26.                 //memset,最极端的例子总共填充了 7 * 8 + 56 * 8。加上前面那个1字节,md5的补充操作,填充位数是1 - 512。
  27.                 p = (char *)mctx->block;
  28.                 padding = 56;
  29.         }

  30.         //补齐填充0,这样,整个block的前448处理完毕,还剩最后64位,接下来附加一个长度信息
  31.         memset(p, 0, padding);
  32.         //置最后64位。在填充的结果后面附加一个以64位二进制表示的填充前信息长度,这样,左移右移。。。??
  33.         mctx->block[14] = mctx->byte_count << 3;
  34.         mctx->block[15] = mctx->byte_count >> 29;
  35.        
  36.         //进行最后一轮转换,转换前要进行le32到cpu的字节序转换
  37.         le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
  38.                           sizeof(u64)) / sizeof(u32));
  39.         md5_transform(mctx->hash, mctx->block);
  40.        
  41.         //将四个寄存器中的值拷贝出来,做为返回值,拷贝之前,需要进行顺序转换
  42.         cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(u32));
  43.         memcpy(out, mctx->hash, sizeof(mctx->hash));
  44.        
  45.         //清空mctx,留待后用
  46.         memset(mctx, 0, sizeof(*mctx));

  47.         return 0;
  48. }
复制代码
8.4 著名的md5四轮转换
  1. static inline void md5_transform_helper(struct md5_ctx *ctx)
  2. {
  3.         //将小头序列的数据转转换为cpu序列,可见所有的数据处理都是以cpu序列来做的,
  4.         //所以,最后在取输出值的时候,应该做相应的逆运算
  5.         le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(u32));
  6.         //进行md5四轮转换
  7.         md5_transform(ctx->hash, ctx->block);
  8. }


  9. #define F1(x, y, z)        (z ^ (x & (y ^ z)))
  10. #define F2(x, y, z)        F1(z, x, y)
  11. #define F3(x, y, z)        (x ^ y ^ z)
  12. #define F4(x, y, z)        (y ^ (x | ~z))

  13. #define MD5STEP(f, w, x, y, z, in, s) \
  14.         (w += f(x, y, z) + in, w = (w<<s | w>>(32-s)) + x)
  15.        
  16. static void md5_transform(u32 *hash, u32 const *in)
  17. {
  18.         u32 a, b, c, d;

  19.         a = hash[0];
  20.         b = hash[1];
  21.         c = hash[2];
  22.         d = hash[3];

  23.         MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
  24.         MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
  25.         MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
  26.         MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
  27.         MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
  28.         MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
  29.         MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
  30.         MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
  31.         MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
  32.         MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
  33.         MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
  34.         MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
  35.         MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
  36.         MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
  37.         MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
  38.         MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);

  39.         MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
  40.         MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
  41.         MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
  42.         MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
  43.         MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
  44.         MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
  45.         MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
  46.         MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
  47.         MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
  48.         MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
  49.         MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
  50.         MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
  51.         MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
  52.         MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
  53.         MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
  54.         MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);

  55.         MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
  56.         MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
  57.         MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
  58.         MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
  59.         MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
  60.         MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
  61.         MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
  62.         MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
  63.         MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
  64.         MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
  65.         MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
  66.         MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
  67.         MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
  68.         MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
  69.         MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
  70.         MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);

  71.         MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
  72.         MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
  73.         MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
  74.         MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
  75.         MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
  76.         MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
  77.         MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
  78.         MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
  79.         MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
  80.         MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
  81.         MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
  82.         MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
  83.         MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
  84.         MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
  85.         MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
  86.         MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);

  87.         hash[0] += a;
  88.         hash[1] += b;
  89.         hash[2] += c;
  90.         hash[3] += d;
  91. }
复制代码
9.        小结
选择hmac(md5)做为分析的切入口,主要是因为它比较简单,只有两层。但是它完成地展示了Linux加密框架的整个流程:
        模版的注册
        算法实例的分配及注册
        tfm的分配和spawn的孵化
        密钥设置
        算法的具体执行流程

未完,待续......

论坛徽章:
0
发表于 2011-12-07 10:43 |显示全部楼层
顶一个

论坛徽章:
1
拜羊年徽章
日期:2015-03-03 16:15:43
发表于 2011-12-07 13:29 |显示全部楼层
你做的东西很先进,是作什么国防尖端的吗?

论坛徽章:
0
发表于 2011-12-07 15:12 |显示全部楼层
你做的东西很先进,是作什么国防尖端的吗?
linuxfellow 发表于 2011-12-07 13:29


晕,这是内核的实现,我只是分析一下而已

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2011-12-07 15:33 |显示全部楼层
回复 1# 独孤九贱
九贱兄每到年底都为网友倾情奉献贺岁精华帖,非常感谢!

论坛徽章:
0
发表于 2011-12-08 14:12 |显示全部楼层
先收藏着,以备后用
感谢LZ
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP