Edit online

设计要点

4 Dec 2024
Read time: 4 minute(s)

算法的分类注册

CE 硬件实现了多组不同类型的加密算法加速单元,分别对应内核加密子系统中的几种类型加密算法。在驱动实现时,根据不同的算法类型,将 CE 硬件抽象出三个不同的算法加速器:

  1. 对称密钥算法加速器

  2. 非对称密钥算法加速器

  3. 消息摘要算法加速器

驱动按照不同的算法加速器进行资源分配和实现,每个算法加速器支持多种不同的具体算法,并且将具体算法注册到加密子系统。


ce_alg_and_accel

1. CE 算法分类
驱动为 每一个 CE 算法实现一个实例,然后注册到内核加密子系统。 内核加密子系统使用链表的方式管理所有注册的算法,后续的使用者通过两个名字( cra_name, cra_driver_name )可以查找到对应的算法。 例如:
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);

ce_subsystem_alg_list

2. 加密子系统的算法列表
使用时,使用者需要使用对应的 API,创建对应算法的数据处理实例,然后使用对应类型算法的接口,进行数据的处理。如对称密钥算法使用下列的接口。
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 即可。


ce_async_call

3. 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 如何解决密钥的本地存储的安全问题?具体做法是:

  1. 本地不保存明文密钥,只保存经过 eFuse 密钥加密后的密钥数据(eFuse 密钥 CPU 不可读,仅 CE 可读)

  2. 需要使用密钥时,首先将加密后的密钥数据,解密到安全 SRAM,CE 再从安全 SRAM 读取密钥明文


secure_sram_1

4. 安全密钥的生成

在需要使用安全 SRAM 进行加解密处理时,需要完成下列操作:

  1. 用户指定一种对称密钥算法,指定 eFuse 密钥,对加密后的密钥数据进行解密

  2. 用户指定解密后的明文密钥输出的安全 SRAM 位置

  3. 配置 CE 使用特定安全 SRAM 中的明文密钥,对数据进行加解密处理

问题:

该流程是 AIC CE 特有,用户提供了更多的输入信息,中间多了密钥的解密、安全 SRAM 的管理等。 该处理流程如何融入到内核加密子系统的算法处理流程成为了问题。

为了很好的对接内核加密子系统,并且方便用户使用,CE 驱动采取的方案是:

  1. 将安全 SRAM 的使用场景具体化,限制到具体的应用需求

  2. 将使用安全 SRAM 的算法抽象为一种特殊的算法,注册到内核加密子系统中

  3. 算法的处理过程中首先进行一个密钥的解密,然后再进行数据的处理

具体实现是为每一个场景实现一个对应的特殊算法,如为需要使用 eFuse HUK 进行密钥解密的 AES ECB 算法,实现一个名为 huk-protected(ecb(aes)) 的算法,并且注册到内核加密子系统中。

当用户指定使用该算法时:

  1. 对应的驱动总是先申请一块安全 SRAM 空间

  2. 使用 eFuse HUK 对用户所提供的密钥数据进行解密,并输出到安全 SRAM 空间

  3. 然后指定 CE 使用安全 SRAM 中生成的明文密钥,对数据进行处理


secure_sram_2

5. 算法使用安全 SRAM 的示意图

通过这种方式,既可以让用户选择符合条件的处理算法,又避免了用户参与处理 eFuse 密钥等额外流程, 还与当前内核加密子系统中其他算法的使用流程保持一致,用户只要指定正确的名字即可使用这些特殊算法。

当前 CE 驱动为下列几个应用场景定义了特殊算法。

  1. 数据安全保护:将数据与设备型号加密绑定

    eFuse SSK 密钥,一型一密(厂商定义,一个型号共用相同密码),通过 ssk-protected(ecb(aes))ssk-protected(cbc(aes)) 算法加密的数据,结合本地密钥可在相同型号的机器上进行解密。

  2. 数据安全保护:将数据与具体设备加密绑定

    eFuse HUK 密钥,一机一密(芯片出厂时随机生成,每台唯一),通过 huk-proteced(ecb(aes))huk-proteced(cbc(aes)) 算法加密的数据,只能在当前设备可以解密。

    huk-proteced(cts(aes))huk-proteced(xts(aes)) 可用于当前设备的文件系统加密, 保证加密后的文件系统只有当前设备可以解密使用。

  3. 设备身份安全认证

    RSA 算法可以用于设备身份认证,前提是设备可以安全的保存其特有的私钥。

    AIC 的方案中可以使用 eFuse 密钥 PNK、PSK 对私钥进行加密保存在设备本地,然后使用 pnk-proteced(rsa) 算法,或者 pskx-proteced(rsa) 算法,将对应的私钥解密到安全 SRAM 中使用。

    PNK、PSK 是仅 CE 可访问的安全 eFuse 空间,可根据实际情况,分配给不同的厂商/用户使用。 当用户需要对设备进行身份认证时,可使用这些算法。


secure_sram_3

6. 使用安全 SRAM 的特殊算法

Fallback 机制

当用户使用指定的 CE 算法时,遇到一些 CE 无法支持的边角情况,此时需要通过 Fallback 机制, 使用软件实现的算法完成用户指定的数据处理任务。

目前可能需要使用 Fallback 机制的是 RSA 算法。

RSA 算法共有 5 种密钥长度,但是目前 CE 仅支持三种(512、1024、2048),当用户需要使用 3072, 4096 比特的密钥时,需要使用 Fallback 机制,使用软件计算。

内核补丁

如前面所述,内核加密子系统通过 AF_ALG Socket 接口向用户空间程序提供了部分算法服务,包括下面四中类型的算法:

  1. SKCIPHER 对称密钥类算法,如 AES、DES 等算法

  2. AEAD 关联数据的认证加密类算法,如 GCM-AES, CCM-AES 等算法

  3. HASH 消息摘要类算法,如 MD5,SHA-256 等算法

  4. RNG 随机数类算法

默认情况下,非对称密钥算法,如 RSA、ECC 等算法内核并没有提供接口给用户空间程序使用。这里有部分原因是这类算法运算量大,在应用中不会用来直接对数据进行处理,仅用于对小量的关键数据进行加解密,因此直接使用用户空间的算法库效率更高,避免了系统调用等的额外开销。

但是提供非对称密钥算法的接口在一些情况下是有意义的,比如平台支持非对称密钥算法的硬件加速,并且运算速度明显比 CPU 计算更快。或者硬件提供基于非对称密钥算法的额外安全功能,比如 AIC 的 CE 可以提供基于 RSA 算法的硬件设备身份安全认证功能,用户空间程序需要有接口可以使用 CE 的 RSA 算法加速器。

虽然主线的内核并没有提供非对称密钥算法的 AF_ALG 接口,但是社区中有相关接口的补丁。Libkcapi 是一个对内核加密子系统 AF_ALG 接口进行封装的开源库,该库将 AF_ALG 接口封装成用户空间更容易使用的 API 接口,并且为若干内核版本提供了非对称密钥的 AF_ALG 接口补丁,通过使用这些补丁,用户空间程序可以使用内核中的非对称密钥算法。

相关的信息链接:

  1. https://www.chronox.de/libkcapi.html
  2. https://github.com/smuellerDD/libkcapi