linux crypto cbc 接口,linux內核cryto接口的實現以及與openssl的比較

linux內核實現了crypto接口,用於類似IPSec之類要在內核中實現的與操作系統綁定的安全機制,如果不是用於這樣的機制,不要使用內核中的crypto接口,總的來說,linux的crypto中最重要的結構體有兩個:crypto_tfm和crypto_alg

struct crypto_tfm {

u32 crt_flags;

union {

struct cipher_tfm cipher;

struct digest_tfm digest;

struct compress_tfm compress;

} crt_u; //該聯合體指出linux實現了cipher,digest和compress三類算法,每一類中有許多種算法,注意這個聯合的每一個都僅僅實現了一大類算法的實現封裝,並不是具體的實現。

struct crypto_alg *__crt_alg;

char __crt_ctx[] __attribute__ ((__aligned__));

};

下面這個結構體才是具體算法的實現,上面crypto_tfm中的crt_u中一系列函數都是對下面結構體中cra_u中一系列函數的封裝:

struct crypto_alg {

struct list_head cra_list;

u32 cra_flags;  //這個flags很重要

unsigned int cra_blocksize;

unsigned int cra_ctxsize;

unsigned int cra_alignmask;

int cra_priority;

char cra_name[CRYPTO_MAX_ALG_NAME];

char cra_driver_name[CRYPTO_MAX_ALG_NAME];

union {

struct cipher_alg cipher;

struct digest_alg digest;

struct compress_alg compress;

} cra_u;

int (*cra_init)(struct crypto_tfm *tfm);

void (*cra_exit)(struct crypto_tfm *tfm);

struct module *cra_module;

};

上面的兩個結構中都有一個聯合體,事實上每個聯合體字段都是一個完整的算法實現,而且三個算法類型幾乎沒有任何共同點可言,對於摘要類算法來說,回調函數是init,update,final等,而對於cipher而言,就是encrypt和decrypt等,linux實際上是利用聯合體的性質來將三類算法封裝到了一個結構體中。

crt_u里面的回調函數和cra_u中的回調函數名稱幾乎一模一樣,但是它們的層次不同,crt中的函數實現了一大類算法的運行邏輯,比如cipher中的des中的塊應該怎么分割等等,雖然對於摘要算法,sha1或者別的什么的算法邏輯沒有什么區別,但是對於cipher來講就不是這樣了,同一種算法可能擁有ecb,cbc,fcb等不同的模式,於是就來了個中間層,這個中間層就是上面的聯合體crt_u。

最后,linux將所有的三大類算法組織成了並列的鏈表,在注冊算法的時候要通過crypto_register_alg將一個crypto_alg結構體注冊進內核,就是說摘要和加密算法不做區別,只有在crypto_alloc_tfm的時候才會根據算法的名稱和flags來確定這個算法到底是做什么的,判斷的依據就是crypto_alg中的cra_name字段和cra_flags字段。

從上述結構體和代碼可以看出,linux對加crypto的算法的組織要遠比openssl的簡單,這也許是linux內核運行效率決定的吧。下面看一下重要的crypto_alloc_tfm函數,很多工作都在這個函數里面完成,比如初始化函數的執行邏輯,也就是初始化crt_u的一堆函數指針,順便說一下,如果你把crypto_alg理解成一個靜態的結構的話,那么crypto_tfm就是一個動態的容器,這種動靜結合的方式在優秀的開源代碼中很常見,都是用靜態的結構初始化動態的結構,然后再根據一些策略邏輯完成動態結構的初始化,有時候動態結構就是一個用於解除耦合的橋梁,比如一些過程的controler,有的時候它就是一個上下文環境或者說是一個容器,比如命名為xxx_ctx的結構體,對於linux內核的cryptoAPI,crypto_tfm可以說是一個橋梁,也可以說是一個容器,一個controler,具體是什么不重要,重要的是代碼靈活了:

struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags)

{

struct crypto_tfm *tfm = NULL;

struct crypto_alg *alg;

unsigned int tfm_size;

alg = crypto_alg_mod_lookup(name);//根據名稱來查找算法實現

tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, flags);

tfm = kzalloc(tfm_size, GFP_KERNEL);

tfm->__crt_alg = alg;

crypto_init_flags(tfm, flags);

crypto_init_ops(tfm);

alg->cra_init(tfm);

...

return tfm;

}

crypto_init_ops函數完成了具體實現的封裝,也就是說它初始化了crypto_tfm結構體中crt_u中的一系列函數指針:

static int crypto_init_ops(struct crypto_tfm *tfm)

{

switch (crypto_tfm_alg_type(tfm)) {

case CRYPTO_ALG_TYPE_CIPHER:

return crypto_init_cipher_ops(tfm);//初始化cipher封裝函數,下面例子中以digest來說明,它在這里的調用邏輯和cipher原理是一樣的,都是在crypto_init_ops實現具體實現的封裝過程。

...

}

下面是使用linux內核中cryptoAPI實現的digest的一個例子的大致過程,流程和openssl的evp系列是差不多的,只是在涉及算法實現的點上有差異:

char *Kern_Digest(const void *data, size_t count,

unsigned char *md, unsigned int *size, const char *name)

{

struct crypto_tfm *tfm;

struct scatterlist sg[1];

tfm = crypto_alloc_tfm(name, 0);

sg_init_one(sg, data, count); //此往下,摘要算法的實現已經確定了,接下來只剩下回調函數的調用了

crypto_digest_init(tfm);

crypto_digest_update(tfm, sg, 1);

crypto_digest_final(tfm, md);

if (size != NULL)

*size = tfm->cra_digest.dia_digestsize;

crypto_free_tfm(tfm);

}

crypto_digest_xxx系列函數僅僅是具體實現的封裝,以init為例:

static inline void crypto_digest_init(struct crypto_tfm *tfm)

{

tfm->crt_digest.dit_init(tfm);//dit_init依然是一個封裝,它在crypto_init_ops中被初始化,這個dit_YYY系列的封裝才是有意義的封裝。

}

這種封裝解除了調用者和實現者之間的耦合。下面是openssl中的digest的實現過程:

int EVP_Digest(const void *data, size_t count,

unsigned char *md, unsigned int *size, const EVP_MD *type, ENGINE *impl)

{  //傳入的type參數其實只是一個空殼,僅僅包含了nid字段是有效的,對於EVP_MD來說就是type字段,因為別的字段具體是什么還要看engine的實現,所以此處的參數type可以僅僅理解成一個算法id。

EVP_MD_CTX ctx;

EVP_MD_CTX_init(&ctx);

M_EVP_MD_CTX_set_flags(&ctx,EVP_MD_CTX_FLAG_ONESHOT);

ret=EVP_DigestInit_ex(&ctx, type, impl)

&& EVP_DigestUpdate(&ctx, data, count)

&& EVP_DigestFinal_ex(&ctx, md, size);

EVP_MD_CTX_cleanup(&ctx);

...

}

在對外呈現的接口上,openssl和linux是一致的,openssl需要一個算法的id,而linux需要一個算法的名稱,所不同的是,openssl有engine的支持,這樣同一個算法就可以指定不同的實現,engine作為算法名稱或者id和算法實現之間的橋梁存在,解除了它們之間的耦合。而linux由於沒有engine,那么只要給定了一個算法名稱,比如sha1,那么只能得到唯一的實現,雖然內核可能通過增加優先級字段來影響算法實現的被選中率,但是這種做法遠沒有openssl的engine靈活,但是反過來想一下,內核中的機制都是比較穩定的,不經常變動的,而且linux本身就不提倡你在內核完成一些功能,除非萬不得已,因此linux的crypto的設計也算是圓滿了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值