Linux加密框架設計與實現(轉)


一、        前言

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 (== 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等函數進行查找,並使用該算法了。
 
三、        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結構,核心的操作是設置其對應的算法實例、算法,以及一個加入算法的鏈表的過程。
 
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。
 
http://bbs.chinaunix.net/thread-3627341-1-1.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM