Kernel Crypto框架

579次阅读  |  发布于4年以前

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

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

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

一、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)。

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。

3)struct crypto_instance

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

4)struct crypto_spawn

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

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衍生类的操作方法。

6)struct crypto_tfm

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

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中已支持的所有算法的使用方式做介绍,这部分读者可直接参考算法自检中的代码实现。

参考文献

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8