设计要点
算法的分类注册
CE 硬件实现了多组不同类型的加密算法加速单元,分别对应内核加密子系统中的几种类型加密算法。在驱动实现时,根据不同的算法类型,将 CE 硬件抽象出三个不同的算法加速器:
-
对称密钥算法加速器
-
非对称密钥算法加速器
-
消息摘要算法加速器
驱动按照不同的算法加速器进行资源分配和实现,每个算法加速器支持多种不同的具体算法,并且将具体算法注册到加密子系统。
struct skcipher_alg alg = {
.base.cra_name = "ecb(aes)",
.base.cra_driver_name = "ecb-aes-aic",
.base.cra_priority = 400,
.base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_ALLOCATES_MEMORY,
.base.cra_blocksize = AES_BLOCK_SIZE,
.base.cra_ctxsize = sizeof(struct aic_skcipher_tfm_ctx),
.base.cra_alignmask = 0,
.base.cra_module = THIS_MODULE,
.init = aic_skcipher_alg_init,
.exit = aic_skcipher_alg_exit,
.setkey = aic_skcipher_alg_setkey,
.decrypt = aic_skcipher_aes_ecb_decrypt,
.encrypt = aic_skcipher_aes_ecb_encrypt,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.ivsize = 0,
};
各驱动和算法实现模块,通过下列接口向加密子系统注册算法。
int crypto_register_skcipher(struct skcipher_alg *alg);
void crypto_unregister_skcipher(struct skcipher_alg *alg);
int crypto_register_akcipher(struct akcipher_alg *alg);
void crypto_unregister_akcipher(struct akcipher_alg *alg);
int crypto_register_ahash(struct ahash_alg *alg);
void crypto_unregister_ahash(struct ahash_alg *alg);
int crypto_register_aead(struct aead_alg *alg);
void crypto_unregister_aead(struct aead_alg *alg);
int crypto_register_kpp(struct kpp_alg *alg);
void crypto_unregister_kpp(struct kpp_alg *alg);
int crypto_register_rng(struct rng_alg *alg);
void crypto_unregister_rng(struct rng_alg *alg);
struct crypto_skcipher *
crypto_alloc_skcipher(const char *alg_name, u32 type, u32 mask);
struct skcipher_request *
skcipher_request_alloc(struct crypto_skcipher *tfm, gfp_t gfp);
int crypto_skcipher_encrypt(struct skcipher_request *req);
int crypto_skcipher_decrypt(struct skcipher_request *req);
可以留意,以对对称密钥算法为例,向加密子系统注册算法实例时,使用的结构体为 struct skciper_alg, 用户 API 使用时,使用的结构体为 struct crypto_skcipher 。这里的区别是,前者是对内, 是具体算法的实现。后者是对外,代表一个对称密钥算法。
异步调用和处理
为了支持更广泛的应用场景,CE 的算法驱动需要实现异步调用,即每一个请求调用,都会立刻返回, 然后通过注册的回调函数来获取请求处理完成的通知。
要实现异步调用需要为每一个加速器实现对应的任务队列,以及相应的执行线程。内核加密子系统提供的公共模块 crypto_engine
已经实现了对应的功能,只需为每个加速器创建 crypto_engine
即可。
如上图所示,当算法驱动接收到一个数据处理请求时,只需做一些基本的标记工作,然后将该请求转发给对应的 crypto_engine
进行管理。crypto_engine
包含一个任务队列,以及一个工作线程。
工作线程总是检查当前队列是否有待处理的任务,如果有任务需要处理,则对当前任务按顺序调用对应的回调函数:
回调函数 | 说明 |
---|---|
prepare(…) | 准备硬件以及对将要送给硬件的数据进行预处理 |
do_one_request(…) | 启动硬件,处理数据 |
硬件完成处理之后,在对一个的 IRQ 处理线程中处理输出数据,并且调用该请求的回调函数,以及释放本次数据处理请求所申请的资源。
CE 的每一个算法处理单元对应一个 crypto_engine
, 即有:skcipher engine,akcipher
engine,hash engine
eFuse 密钥和安全 SRAM
安全 SRAM 是 CE 中的一块专用 SRAM,该 SRAM 与其他模块安全隔离,仅 CE 可以访问, 因此用其保存的密钥和数据可以保证不被其他模块窃取。
安全 SRAM 的设计目的是要解决密钥的本地存储的安全问题。在一些数据加密的应用场景中,用户生成了一个密钥, 并且使用该密钥对数据进行加密。本地存储了加密后的数据,但是密钥要如何保存才安全又成了新的问题。 如果明文保存在本地,则很容易被窃取。
使用安全 SRAM 如何解决密钥的本地存储的安全问题?具体做法是:
-
本地不保存明文密钥,只保存经过 eFuse 密钥加密后的密钥数据(eFuse 密钥 CPU 不可读,仅 CE 可读)
-
需要使用密钥时,首先将加密后的密钥数据,解密到安全 SRAM,CE 再从安全 SRAM 读取密钥明文
在需要使用安全 SRAM 进行加解密处理时,需要完成下列操作:
-
用户指定一种对称密钥算法,指定 eFuse 密钥,对加密后的密钥数据进行解密
-
用户指定解密后的明文密钥输出的安全 SRAM 位置
-
配置 CE 使用特定安全 SRAM 中的明文密钥,对数据进行加解密处理
问题:
该流程是 AIC CE 特有,用户提供了更多的输入信息,中间多了密钥的解密、安全 SRAM 的管理等。 该处理流程如何融入到内核加密子系统的算法处理流程成为了问题。
为了很好的对接内核加密子系统,并且方便用户使用,CE 驱动采取的方案是:
-
将安全 SRAM 的使用场景具体化,限制到具体的应用需求
-
将使用安全 SRAM 的算法抽象为一种特殊的算法,注册到内核加密子系统中
-
算法的处理过程中首先进行一个密钥的解密,然后再进行数据的处理
具体实现是为每一个场景实现一个对应的特殊算法,如为需要使用 eFuse HUK 进行密钥解密的 AES ECB 算法,实现一个名为 huk-protected(ecb(aes)) 的算法,并且注册到内核加密子系统中。
当用户指定使用该算法时:
-
对应的驱动总是先申请一块安全 SRAM 空间
-
使用 eFuse HUK 对用户所提供的密钥数据进行解密,并输出到安全 SRAM 空间
-
然后指定 CE 使用安全 SRAM 中生成的明文密钥,对数据进行处理
通过这种方式,既可以让用户选择符合条件的处理算法,又避免了用户参与处理 eFuse 密钥等额外流程, 还与当前内核加密子系统中其他算法的使用流程保持一致,用户只要指定正确的名字即可使用这些特殊算法。
当前 CE 驱动为下列几个应用场景定义了特殊算法。
-
数据安全保护:将数据与设备型号加密绑定
eFuse SSK 密钥,一型一密(厂商定义,一个型号共用相同密码),通过
ssk-protected(ecb(aes))
和ssk-protected(cbc(aes))
算法加密的数据,结合本地密钥可在相同型号的机器上进行解密。 -
数据安全保护:将数据与具体设备加密绑定
eFuse HUK 密钥,一机一密(芯片出厂时随机生成,每台唯一),通过
huk-proteced(ecb(aes))
和huk-proteced(cbc(aes))
算法加密的数据,只能在当前设备可以解密。huk-proteced(cts(aes))
和huk-proteced(xts(aes))
可用于当前设备的文件系统加密, 保证加密后的文件系统只有当前设备可以解密使用。 -
设备身份安全认证
RSA 算法可以用于设备身份认证,前提是设备可以安全的保存其特有的私钥。
AIC 的方案中可以使用 eFuse 密钥 PNK、PSK 对私钥进行加密保存在设备本地,然后使用
pnk-proteced(rsa)
算法,或者pskx-proteced(rsa)
算法,将对应的私钥解密到安全 SRAM 中使用。PNK、PSK 是仅 CE 可访问的安全 eFuse 空间,可根据实际情况,分配给不同的厂商/用户使用。 当用户需要对设备进行身份认证时,可使用这些算法。
Fallback 机制
当用户使用指定的 CE 算法时,遇到一些 CE 无法支持的边角情况,此时需要通过 Fallback 机制, 使用软件实现的算法完成用户指定的数据处理任务。
目前可能需要使用 Fallback 机制的是 RSA 算法。
RSA 算法共有 5 种密钥长度,但是目前 CE 仅支持三种(512、1024、2048),当用户需要使用 3072, 4096 比特的密钥时,需要使用 Fallback 机制,使用软件计算。
内核补丁
如前面所述,内核加密子系统通过 AF_ALG Socket 接口向用户空间程序提供了部分算法服务,包括下面四中类型的算法:
-
SKCIPHER 对称密钥类算法,如 AES、DES 等算法
-
AEAD 关联数据的认证加密类算法,如 GCM-AES, CCM-AES 等算法
-
HASH 消息摘要类算法,如 MD5,SHA-256 等算法
-
RNG 随机数类算法
默认情况下,非对称密钥算法,如 RSA、ECC 等算法内核并没有提供接口给用户空间程序使用。这里有部分原因是这类算法运算量大,在应用中不会用来直接对数据进行处理,仅用于对小量的关键数据进行加解密,因此直接使用用户空间的算法库效率更高,避免了系统调用等的额外开销。
但是提供非对称密钥算法的接口在一些情况下是有意义的,比如平台支持非对称密钥算法的硬件加速,并且运算速度明显比 CPU 计算更快。或者硬件提供基于非对称密钥算法的额外安全功能,比如 AIC 的 CE 可以提供基于 RSA 算法的硬件设备身份安全认证功能,用户空间程序需要有接口可以使用 CE 的 RSA 算法加速器。
虽然主线的内核并没有提供非对称密钥算法的 AF_ALG 接口,但是社区中有相关接口的补丁。Libkcapi 是一个对内核加密子系统 AF_ALG 接口进行封装的开源库,该库将 AF_ALG 接口封装成用户空间更容易使用的 API 接口,并且为若干内核版本提供了非对称密钥的 AF_ALG 接口补丁,通过使用这些补丁,用户空间程序可以使用内核中的非对称密钥算法。
相关的信息链接: