Kernel Crypto框架

随着数字时代的发展,每天都有海量的数据产生,并且用户也越来越重视个人隐私数据的安全,从某种意义上讲,用户个人数据的价值正逐步高于设备本身。实现数据安全保护的基础是【密钥 + 加密算法】;对于加密算法,kernel其实早在linux-2.5.45版本中就引入了crypto基础能力。本篇文章主要讲关于kernel crypto算法框架,以及结合它在文件系统加密这一场景中的应用,分析内部的实现细节,以便读者对crypto框架有相关的认识,并能基于它做开发。

本篇文章的所有代码都是围绕Linux-4.19,以OOP思想进行阐述,大家可以结合源码进行阅读。

为方便理解,我们约定一些术语:

  • 数据转换(transformation/TFM):不管是对数据做加/解密,还是做hash,都定义成是对数据所做的转换操作。

  • 算法:本文当中的算法并不特指加/解密算法。

一、Crypto子系统简介

1.功能

Kernel crypto是内核实现的一套通用crypto算法框架,是一个独立的子系统,源码在kernel/crypto下,它实现了对算法的统一管理,并提供出统一的数据处理接口给其他子系统使用;因此基于这套框架,我们不仅可以使用kernel已有的crypto算法对数据做转换,还能自行扩展添加算法。

Kernel crypto 当前实现了对称加解密,非对称加解密,认证加解密,hash,Hmac,DRBG伪随机数生成算法和压缩算法。

2.适用场景

Kernel crypto主要用于kernel层的安全特性实现,但在user-space也可以通过系统调用的方式来使用它;因为在Linux-2.6.38中已经通过socket (addr family: AF_ALG)方式导出接口到user-space.

在开发时如要快速确认kernel中是否支持某种算法,可以cat /proc/crypto 查看,如下图1.1。

图1.1 kernel支持的加密算法

name代表算法名称,hmac是对应的模式(抽象成template);priority代表算法的优先级(在相同名称下,数字越大代表优先级越高,默认使用高优先级的算法);selftest代表开机算法自检结果;type指算法类型;async指异步方式调用;blocksize指最小单个数据处理块大小;min keysize和max keysize指算法的最小/最大密钥长度;ivsize指算法的IV初始向量长度。

*selftest之后的所有字段其实都是crypto_type->show()所提供的,后面会提及。

3.整体架构

下面通过一张图来展示crypto的整体框架。

图1.2 kernel crypto框架

crypto core是最基本骨架 ,它提供crypto的核心组件(包括crypto_alg,crypto_template的管理,cryptd内核线程等);基于crypto core,内核实现了8类常用的算法,DRBG伪随机数算法,Hash算法,SKCIPHER对称加解密算法,AKCIPHER非对称加解密算法,AEAD认证加密算法,HMAC算法,COMPRESS压缩算法,KPP密钥协商算法。

一些用于secure的硬件模块(如hw_rng硬件随机数产生器,qce硬加密模块)的驱动程序,会通过crypto core提供的算法注册接口(crypto_register_alg)将其注册到crypto子系统中,并且在注册时会对算法做静态正确性自检,并在/proc/crypto中的selftest中呈现到userspace。除了注册到crypto子系统以外,驱动也可以通过VFS以设置节点形式提供给用户空间使用(如/dev/qce,/dev/hw_rng)。

Crypto core通过socket方式,将kernel层的算法能力提供给用户空间。

二、Crypto核心数据结构及逻辑实现

算法本质上就是一些对数据进行逻辑处理的函数,在应用层的实现普遍是直接实现一个加解密接口,应用程序直接调用,对使用者来说极为方便。但在kernel中,使用起来会较为复杂些,因为它需要考虑到算法的易扩展性,通用性,易维护性等,因此对算法做了高度的抽象化;所以要理解kernel crypto的设计思想,必须要理解它的几个核心struct。

1.核心数据结构

在软件界有句话写的很实在:”软件=文件+程序,程序=数据结构+算法”;由此能看出弄清数据结构的重要性。

Kernel crypto中基本所有操作都是围绕着几个核心数据结构展开:struct crypto_alg,struct crypto_template,struct crypto_instance,struct crypto_tfm,struct crypto_type。其他算法都可以基于它们做扩展。例如struct skcipher_alg,struct shash_alg都是继承自struct crypto_alg,见下图2.1:

图2.1 crypto数据结构uml类图

下面对这6个结构体做相关说明:

1)struct crypto_template

算法模板,一般在module_init时通过调用crypto_register_template接口注册到crypto_template_list链表中。

在算法加密中,分块加密模式分为很多种,以对称加解密为例,有CBC,ECB,GCM,CTR,XTS,而这些加密模式适用于所有的对称加密算法,如AES,DES;因此kernel就将加密模式抽象成模板,在开发新的算法时只需要实现单个block的数据处理(加密,hmac等);在申请使用算法时,我们通过算法名来组合出相应的算法(kernel会将组合出来的算法动态注册到crypto子系统),格式为template(single block cipher),例如cbc(aes),ecb(des)。

  • list用于模块的crypto_template_list链表管理;

  • instance用于管理当前模板下所有的crypto_instance;

  • alloc接口用于申请算法实例;

  • free用于释放算法实例;

2)struct crypto_alg

crypto_alg是个基类,任何算法都可以基于它派生出衍生类;每个算法都对应着一个struct crypto_alg实例,一般在module_init中调用crypto_register_alg接口将具体的crypto_alg对象添加到crypto_alg_list链表中。这里有一个很重要的数据成员cra_u,因为它体现了kernel crypto架构设计者的设计思想:它将四种比较常用的算法类型的处理方式抽象到基类当中,即如果你要添加的算法为这4类,就只需要实现这4类算法所对应的方法,如果不是这4类当中,就需要在基类上做派生,实现特定的crypto_type。

  • cra_list:是用作链表管理

  • cra_users:此算法被引用的所有crypto_spawn实例链表。

  • cra_blocksize:是单个处理数据块大小

  • cra_ctxsize:为transformation context大小

  • cra_alignmask:指待处理数据buffer的对齐要求

  • cra_priority:是当前算法优先级

  • cra_refcnt:为当前算法的引用计数

  • cra_name和cra_driver_name:分别指代算法名及驱动名

  • cra_type:指算法类型;cra_u将四大类算法类型进行了统一。

  • cra_init:是用于每次数据操作上下文前的初始化,比如在硬件加密中,会实现此接口对相关寄存器做初始化;cra_exit则与前者相反。

  • cra_destroy:是用于crypto在kernel中注销的相关操作。

3)struct crypto_instance

这个结构体是代表kernel通过template动态创建的算法实例,并且会与crypto_template相关联,可以看到这里的alg并不是个指针。它是通过template->alloc()创建的,创建的同时,会将算法name初始化。

  • __ctx:当前只指向crypto_spawn,我个人理解可能是架构设计者考虑到未来扩展性,就将crypto_spawn与crypto_instance拆分开来了。

4)struct crypto_spawn

通过模板动态生成的算法实例的一部分。

  • list:添加到crypto_alg->cra_users链表中。

  • frontend:见下文。

5)struct crypto_type

crypto_type就是用于重载crypto_alg中的cra_u中的各个类中的成员函数,当通过crypto_alloc_base,crypto_create_tfm等接口申请相应的crypto的TFM上下文时,若有传入crypto_type参数,TFM优先使用crypto_type中的init_tfm成员函数去初始化crypto_tfm衍生类的操作方法。

  • ctxsize:获取当前算法类型TFM上下文大小(crypto_tfm+crypto_tfm.__crt_ctx)

  • extsize:获取当前算法类型TFM上下文大小(即crypto_tfm衍生类的大小)。

  • init:一般为空(功能与init_tfm类似,通常在后者中初始化TFM)。

  • init_tfm:顾名思义,初始化TFM。

  • show:呈现当前算法类型的基本信息,/proc/crypto后半段信息就是从这获取的。

  • free:释放crypto_instance。

6)struct crypto_tfm

具体算法处理(transformation)上下文的实例,里面会将此次算法上下文的key,IV等信息设置到__crt_ctx中。

  • crt_u:算法的operation,kernel会在__crypto_alloc_tfm接口中关联到crypto_type或xxx_alg中的实现方法。

2.通过用例介绍crypto子系统逻辑

那我们在使用kernel中的算法时,框架内部是如何做处理的呢?下面通过一个例子来说明这个问题。在文件系统加密(FBE)中通过kernel crypto做密钥派生。

背景:在Android 7.0时,引入了文件加密功能,所谓文件加密,即每个文件都用不同的key对文件进行加密。

原理:密钥派生中,使用了crypto中的ecb(aes)算法通过类密钥及inode.nonce派生出每个文件的密钥。

具体实现在kernel/fs/crypto/keyinfo.c,如下图2.2所示:

图2.2 crypto在FBE中的应用

1)申请“ecb(aes)”算法的tfm上下文。这里会涉及到“算法动态注册”,即如果在crypto_alg_list链表中没有找到name为”ecb(aes)”的crypto_alg对象,那crypto子系统会通过一个名为”cryptomgr_probe”的内核线程查找到name为“ecb”的crypto_template对象,以及查找到name为”aes”的crypto_alg对象,动态创建出一个name为“ecb(aes)”的crypto_alg并注册到链表当中。调用流程如图2.3

图2.3 算法动态注册流程

在获取到了crypto_alg后,就会申请crypto_tfm,并用crypto_alg->cra_init()或crypto_type->init_tfm()对其进行初始化(主要是当前算法的各个函数指针)。

2) 在tfm上下文中申请一个数据处理请求(req)。一个tfm中,可以做多次数据加解密。这里只是申请内存,并关联到tfm的操作。

3) 设置密钥到tfm->__crt_ctx中。

4) 把待加密数据信息放入req当中。

5)以异步方式调用crypto_skcipher_encrypt对req做加密处理,线程在此会block一段时间,直到req请求被处理完成。因此不能在中断上下文中使用。

3.算法自检

出于安全性考虑,FIPS等相关标准要求在系统开机时必须做算法正确性自检,如果自检失败,则停止系统的启动;因此算法自检这部分自然也在框架中实现了。(源码位于:kernel/crypto/testmgr.c)

Kernel中的算法自检为静态自检,即给定输入参数,及正确结果,如果算法计算出来的结果与正确结果不匹配,则自检失败。

算法自检的时间点固定为每个crypto_alg注册的时候,具体流程如下图2.4所示:

图2.4 算法自检流程

三、总结

Kernel crypto在设计时进行了高度的抽象化,本篇文章主要通过一些核心数据结构,并采用OOP角度来揭示各个struct的功能及之间的关系,以便读者能了解架构设计者的初衷。由于篇幅所限,本文未对kernel中已支持的所有算法的使用方式做介绍,这部分读者可直接参考算法自检中的代码实现。

参考文献

[1] Kernel Crypto API Architecture:https://kernel.org/doc/html/crypto/architecture.html

[2] Linux kernel 4.19源代码

扫码关注
“内核工匠”微信公众号
Linux 内核黑科技 | 技术文章 | 精选教程
  • 6
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OPPO内核工匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值