Linux 内核对称密钥加密 API(skcipher)完全指南:从算法注册到加解密请求处理 📅 发布时间:2026/9/16 18:32:07 👁 浏览次数: Linux 内核对称密钥加密 APIskcipher完全指南从算法注册到加解密请求处理【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux对称密钥加密Symmetric Key Cipher是 Linux 内核 crypto 子系统中最核心、应用最广泛的能力涵盖 ECB/CBC/CTR/XTS 等分组密码模式以及 AES、SM4 等具体算法。本文以 Documentation/crypto/api-skcipher.rst 为骨架结合内核源码系统讲解三层 API算法定义crypto_alg/cipher_alg、skcipher 句柄 APIcrypto_skcipher_*与请求句柄 APIskcipher_request_*并介绍面向单块操作的低层Single Block Cipher API。读完本文你将掌握在模块内编写可复用的加解密代码、正确使用 IV/密钥/散列表scatterlist、以及理解异步回调机制的完整实战方案。一、API 全景skcipher 在 crypto 子系统中的位置内核 crypto 子系统把所有算法统称为变换transformation一个变换句柄变量通常命名为tfm。对称密钥加密 API 面向类型为CRYPTO_ALG_TYPE_SKCIPHER的密码算法在/proc/crypto中类型列为skcipher它封装了分组密码block cipher及其工作模式mode并同时支持同步与异步两种执行方式。从 include/linux/crypto.h 可以看到各算法类型的编码#define CRYPTO_ALG_TYPE_MASK 0x0000000f #define CRYPTO_ALG_TYPE_CIPHER 0x00000001 #define CRYPTO_ALG_TYPE_AEAD 0x00000003 #define CRYPTO_ALG_TYPE_LSKCIPHER 0x00000004 #define CRYPTO_ALG_TYPE_SKCIPHER 0x00000005对应的 API 分层如下层核心类型主要函数前缀适用场景skcipher 句柄 APIstruct crypto_skciphercrypto_alloc_skcipher、crypto_skcipher_*大多数内核模块按算法名申请变换执行多块加解密skcipher 请求句柄 APIstruct skcipher_requestskcipher_request_*携带每次操作的 IV、src/dst 散列表与异步回调单块密码 APIstruct crypto_ciphercrypto_alloc_cipher、crypto_cipher_*模板template内部逐块调用底层分组原语skcipher 是对底层分组密码加上模式的封装crypto_skcipher_encrypt()内部在未设置 key 时直接返回-ENOKEY见 crypto/skcipher.c保证使用者必须先正确调用crypto_skcipher_setkey()。二、块密码算法定义crypto_alg 与 cipher_alg2.1 通用算法定义 struct crypto_algstruct crypto_alg描述所有类型的变换算法其 kernel-doc 文档位于 include/linux/crypto.h。关键字段及语义cra_flags描述变换的标志使用CRYPTO_ALG_*系列宏用于精细刻画算法特性cra_blocksize该变换可处理的最小单元字节数使用者必须遵守非哈希类变换若尝试处理小于cra_blocksize的块会返回错误cra_ctxsize变换上下文transformation context所需内存大小crypto API 据此为上下文分配内存cra_alignmask算法对输入/输出缓冲区对齐要求减 1按字节。当调用方提供的缓冲区未按此对齐时crypto API 会自动分配对齐的临时缓冲区对散列表而言仅当算法使用skcipher_walk辅助函数时才会做这种重对齐但重对齐有性能开销因此建议算法尽量将 alignmask 设为 0使用方也应主动按 alignmask 对齐缓冲区以避免被重排cra_reqsize该算法的请求上下文大小cra_priority同一cra_name存在多个实现时内核选用优先级最高者cra_name算法通用名可被多个实现复用内核据此查找变换提供者cra_driver_name变换提供者的唯一名称通常包含芯片/提供者名与算法名cra_type指向struct crypto_type的指针如crypto_skcipher_type、crypto_ahash_type等提供各变换类型公共回调cipher类型该字段可为空cra_u实现变换的回调联合体按cra_type/cra_flags选择填充对应结构skcipher 通过cra_cipher即cra_u.cipher关联单块对称密码定义cra_module实现所属模块置为THIS_MODULE。struct crypto_alg是模块化算法实现的定义通过crypto_register_alg()与crypto_unregister_alg()注册/注销见 include/linux/crypto.h 的 DOC 说明。其中cra_list、cra_users、cra_refcnt、cra_destroy为内部字段实现方不得使用cra_init/cra_exit已被标记为废弃Deprecated, do not use。2.2 单块对称密码定义 struct cipher_algstruct cipher_alginclude/linux/crypto.h描述单块对称密码原语所有字段必填cia_min_keysize/cia_max_keysize算法支持的最小/最大密钥长度字节。文档要求设为预定义值可通过git grep _MIN_KEY_SIZE include/crypto/与git grep _MAX_KEY_SIZE include/crypto/查询。例如 AES 在 include/crypto/aes.h 中定义为AES_MIN_KEY_SIZE 16、AES_MAX_KEY_SIZE 32cia_setkey为变换设置密钥可把密钥编程进硬件或存入变换上下文备用。该函数会修改变换上下文且可能被多次调用每次须正确重编程同时负责校验密钥长度合法性cia_encrypt/cia_decrypt加密/解密单个数据块块大小必须等于cra_blocksize不能处理更小的块输入输出缓冲区至少cra_blocksize字节且按cra_alignmask对齐未对齐时由 crypto API 重对齐分配新缓冲→拷贝→处理→拷回→释放。注意该函数不得修改变换上下文因为它可能被多个并发请求并行调用若密钥存储在上下文中需在此函数内重新编程进硬件。struct cipher_alg { unsigned int cia_min_keysize; unsigned int cia_max_keysize; int (*cia_setkey)(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen); void (*cia_encrypt)(struct crypto_tfm *tfm, u8 *dst, const u8 *src); void (*cia_decrypt)(struct crypto_tfm *tfm, u8 *dst, const u8 *src); };三、Symmetric Key Cipher API句柄的分配与使用该 API 文档位于 include/crypto/skcipher.h。其核心模型是异步操作在函数返回前不保证完成请求被调度到独立的内核线程执行并通过进程调度器在不同 CPU 上负载均衡调用方必须提供回调函数请求完成时由内核 crypto API 以变换句柄调用该回调。为支撑异步除句柄外还必须通过填充skcipher_request结构补充附加信息。状态由 tfm 句柄维护一个 tfm 可跨多次调用复用也可并行使用。对异步调用调用方专有的上下文数据可通过请求结构引用除 IV 外crypto API 本身不使用这些状态但驱动程序实现者在回调里需要区分是哪一次操作完成了当并行发起多个请求时这正是请求结构中私有上下文的意义。3.1 分配与释放struct crypto_skcipher *crypto_alloc_skcipher(const char *alg_name, u32 type, u32 mask);alg_nameskcipher 的cra_name通用名或cra_driver_name驱动名如xts(aes)type/mask指定密码类型与掩码通常传 0成功返回已分配的struct crypto_skcipher句柄失败时IS_ERR()为真用PTR_ERR()取错误码。释放使用crypto_free_skcipher()该函数会清零zeroize并释放句柄若传入 NULL 或错误指针则不做任何事。对应的查询函数crypto_has_skcipher()返回该算法是否已知bool。底层实现上crypto_alloc_skcipher()通过crypto_alloc_tfm()配合crypto_skcipher_type完成分配crypto_has_skcipher()通过crypto_type_has_alg()查询见 crypto/skcipher.c。3.2 查询属性句柄分配后可用如下内联函数查询算法特性函数返回内容crypto_skcipher_ivsize(tfm)IV 大小字节无需 IV 的密码返回 0crypto_skcipher_blocksize(tfm)块大小用于为加解密结果分配内存crypto_skcipher_chunksize(tfm)块大小但对 CTR 这类块大小为 1的流式密码仍要求按底层块大小增量更新IV 没有子块粒度此值即该约束下的chunk sizecrypto_skcipher_statesize(tfm)内部状态大小某些算法无法仅凭 IV 串联分块处理需要复制内部状态才能增量处理数据crypto_skcipher_min_keysize/max_keysize算法支持的密钥长度范围字节crypto_skcipher_reqtfm(req)从请求结构中取回所属的 skcipher 句柄crypto_skcipher_reqsize(tfm)请求数据结构的字节大小含算法私有请求区3.3 设置密钥int crypto_skcipher_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen);密钥长度决定密码类型许多分组密码按密钥长度区分模式如 AES-128 / AES-192 / AES-256——对 AES 句柄提供 16 字节密钥即执行 AES-128。成功返回 0失败返回负错误码。实现上crypto/skcipher.c会校验密钥长度合法性、按 alignmask 对齐密钥缓冲并在成功后清除CRYPTO_TFM_NEED_KEY标志若未设置密钥就调用加解密crypto_skcipher_encrypt()/decrypt()会直接返回-ENOKEY。3.4 加解密入口int crypto_skcipher_encrypt(struct skcipher_request *req); int crypto_skcipher_decrypt(struct skcipher_request *req);两者都只接受一个skcipher_request句柄所有操作信息数据缓冲、IV、回调都封装在请求结构中。成功返回 0失败返回负错误码。四、Symmetric Key Cipher Request Handle请求句柄的组装skcipher_request数据结构见 include/crypto/skcipher.h包含对称密码操作所需的全部指针密码句柄可被多个请求实例共享、明文/密文指针、异步回调函数等。它的作用类似请求侧句柄正如crypto_skcipher句柄服务于crypto_skcipher_*系列调用skcipher_request句柄服务于skcipher_request_*系列调用DOC 说明见 include/crypto/skcipher.h。请求结构关键成员cryptlen要加解密的字节数iv初始化向量IV指针大小须与crypto_skcipher_ivsize()一致src/dst源/目的 scatter-gather 链表base底层异步请求struct crypto_async_request__ctx私有上下文数据起始处CRYPTO_MINALIGN_ATTR对齐。4.1 分配与释放struct skcipher_request *skcipher_request_alloc(struct crypto_skcipher *tfm, gfp_t gfp); void skcipher_request_free(struct skcipher_request *req);分配时会把tfm注册进请求结构底层实现skcipher_request_alloc_noprof按sizeof(struct skcipher_request) crypto_skcipher_reqsize(tfm)分配内存见 include/crypto/skcipher.h成功返回请求句柄内存不足返回 NULL。释放时使用kfree_sensitive()先清零再释放防止密钥/密文残留在内存。另有skcipher_request_zero()可显式清零整个请求。4.2 绑定变换与设置回调void skcipher_request_set_tfm(struct skcipher_request *req, struct crypto_skcipher *tfm);允许调用方把请求结构里已绑定的 skcipher 句柄替换为另一个req-base.tfm被更新。void skcipher_request_set_callback(struct skcipher_request *req, u32 flags, crypto_completion_t compl, void *data);注册请求完成时触发的回调。参数说明flags填 0 或以下标志的按位或CRYPTO_TFM_REQ_MAY_BACKLOG请求队列允许积压等待队列可超过初始最大值CRYPTO_TFM_REQ_MAY_SLEEP请求处理过程允许睡眠compl回调函数指针模板固定为void callback_function(struct crypto_async_request *req, int error)data指向调用方私有内存的指针crypto API 不使用它仅透传给回调函数。由于回调是异步触发的它可能需要访问发起操作的那套数据结构正是通过crypto_async_request中的data字段取得该引用例如配合DECLARE_CRYPTO_WAIT(wait)与crypto_req_done把异步转成同步等待。4.3 设置数据缓冲void skcipher_request_set_crypt(struct skcipher_request *req, struct scatterlist *src, struct scatterlist *dst, unsigned int cryptlen, void *iv);设置源/目的散列表、处理字节数与 IV。语义加密src 是明文dst 是密文解密角色反转——src 是密文dst 是明文iv大小必须符合crypto_skcipher_ivsize()cryptlen必须遵守 chunk 约束见 3.2 节chunksize。五、完整实战AES-256-XTS 加解密示例以下示例来自 Documentation/crypto/api-samples.rst 的 skcipher 代码段原文完整继承它用 AES-256-XTS 加密数据所有输入均为随机字节、原地in-place加密、假设运行在可睡眠上下文static int test_skcipher(void) { struct crypto_skcipher *tfm NULL; struct skcipher_request *req NULL; u8 *data NULL; const size_t datasize 512; /* data size in bytes */ struct scatterlist sg; DECLARE_CRYPTO_WAIT(wait); u8 iv[16]; /* AES-256-XTS takes a 16-byte IV */ u8 key[64]; /* AES-256-XTS takes a 64-byte key */ int err; /* * Allocate a tfm (a transformation object) and set the key. * * In real-world use, a tfm and key are typically used for many * encryption/decryption operations. But in this example, well just do a * single encryption operation with it (which is not very efficient). */ tfm crypto_alloc_skcipher(xts(aes), 0, 0); if (IS_ERR(tfm)) { pr_err(Error allocating xts(aes) handle: %ld\n, PTR_ERR(tfm)); return PTR_ERR(tfm); } get_random_bytes(key, sizeof(key)); err crypto_skcipher_setkey(tfm, key, sizeof(key)); if (err) { pr_err(Error setting key: %d\n, err); goto out; } /* Allocate a request object */ req skcipher_request_alloc(tfm, GFP_KERNEL); if (!req) { err -ENOMEM; goto out; } /* Prepare the input data */ data kmalloc(datasize, GFP_KERNEL); if (!data) { err -ENOMEM; goto out; } get_random_bytes(data, datasize); get_random_bytes(iv, sizeof(iv)); /* * Encrypt the data in-place. * * For simplicity, in this example we wait for the request to complete * before proceeding, even if the underlying implementation is asynchronous. * * To decrypt instead of encrypt, just change crypto_skcipher_encrypt() to * crypto_skcipher_decrypt(). */ sg_init_one(sg, data, datasize); skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, crypto_req_done, wait); skcipher_request_set_crypt(req, sg, sg, datasize, iv); err crypto_wait_req(crypto_skcipher_encrypt(req), wait); if (err) { pr_err(Error encrypting data: %d\n, err); goto out; } pr_debug(Encryption was successful\n); out: crypto_free_skcipher(tfm); skcipher_request_free(req); kfree(data); return err; }要点解读算法命名xts(aes)是模板语法表示以 AES 为底层原语的 XTS 模式64 字节密钥AES-256-XTS 双密钥与 16 字节 IV 由算法自身定义约束原地加解密skcipher_request_set_crypt(req, sg, sg, ...)使 src 与 dst 指向同一散列表异步转同步DECLARE_CRYPTO_WAIT(wait)crypto_req_done回调 crypto_wait_req()是内核模块中最常用的同步等待异步完成模式避免在不知道底层实现是同步还是异步的情况下编写两条代码路径错误处理IS_ERR(tfm)区分句柄指针与错误码goto out统一走释放路径crypto_free_skcipher/skcipher_request_free对 NULL 安全解密只需一行改动把crypto_skcipher_encrypt(req)换成crypto_skcipher_decrypt(req)同时密钥/IV 保持不变。六、Single Block Cipher API面向模板的单块原语单块密码 API文档见 include/crypto/internal/cipher.h面向CRYPTO_ALG_TYPE_CIPHER类型算法/proc/crypto中类型为cipher。它只提供基本密码原语不包含任何链式chaining操作与 IV 处理其目的是支撑模板或其他只需要逐块执行密码操作的场景——模板按块调用底层原语自行处理输入/输出数据与链式逻辑。struct crypto_cipher *crypto_alloc_cipher(const char *alg_name, u32 type, u32 mask); void crypto_free_cipher(struct crypto_cipher *tfm); int crypto_has_cipher(const char *alg_name, u32 type, u32 mask);注意crypto_alloc_cipher()/crypto_has_cipher()内部会强制把 type 归一到CRYPTO_ALG_TYPE_CIPHER见 include/crypto/internal/cipher.h因此调用时type/mask通常传 0 即可。unsigned int crypto_cipher_blocksize(struct crypto_cipher *tfm); int crypto_cipher_setkey(struct crypto_cipher *tfm, const u8 *key, unsigned int keylen); void crypto_cipher_encrypt_one(struct crypto_cipher *tfm, u8 *dst, const u8 *src); void crypto_cipher_decrypt_one(struct crypto_cipher *tfm, u8 *dst, const u8 *src);使用约束crypto_cipher_blocksize()返回块大小调用方据此为输入/输出分配至少一个块的内存crypto_cipher_setkey()中密钥长度决定密码类型16 字节 AES 密钥即 AES-128成功返回 0失败返回负值crypto_cipher_encrypt_one()/decrypt_one()每次只处理一个块明文/密文缓冲区必须至少一个块大小且通常要按crypto_cipher_alignmask()对齐未对齐时由 API 内部重对齐。该 API 为同步调用完成后数据直接写入dst。七、实现细节与进阶话题7.1 skcipher 的异步与散列表遍历skcipher 数据以 scatterlist 形式给出驱动/通用实现通过struct skcipher_walkcrypto/skcipher.c 中的skcipher_walk_*系列逐段遍历处理跨页、非对齐缓冲等复杂情况。skcipher_walk_done()每步返回剩余字节数并负责更新 walk 状态skcipher_next_slow()/skcipher_next_copy()/skcipher_next_fast()分别对应慢速分配对齐临时缓冲、拷贝与快速页内对齐直接映射三条路径——这正是 2.1 节 alignmask 语义在 skcipher 层的落地只有使用 skcipher_walk 辅助函数的实现才会自动处理未对齐散列表。7.2 lskcipher线性对称密码接口同一头文件中还定义了面向线性内存的struct lskcipher_alg与crypto_lskcipher_*系列如crypto_alloc_lskcipher、crypto_lskcipher_encrypt。其加解密签名直接操作src/dst线性缓冲siv参数为 IV 状态长度等于ivsize statesizeIV 在前返回值为未处理的剩余字节数成功时 0便于长度不是块倍数且非最终块final false时留待下一次调用。crypto_skcipher_encrypt()在底层若检测到cra_type ! crypto_skcipher_type会自动转调crypto_lskcipher_encrypt_sg()见 crypto/skcipher.c说明 lskcipher 与 skcipher 在框架层是兼容打通的。请求标志位CRYPTO_SKCIPHER_REQ_CONT续传与CRYPTO_SKCIPHER_REQ_NOTFINAL非最终块用于指示分段处理语义include/crypto/skcipher.h。7.3 算法提供者视角skcipher_alg 字段实现 skcipher 算法的驱动需要填充struct skcipher_alginclude/crypto/skcipher.hsetkey/encrypt/decrypt核心回调语义与cipher_alg对应字段一致encrypt/decrypt 处理一个 scatterlist 块序列不得修改变换上下文因为同一变换对象可能被并行调用export/import导出/导入进行中变换的部分状态用于处理一部分数据后保存中间结果、之后多次恢复复用的场景此过程不做数据处理init/exit变换对象初始化/反初始化init仅在实例化时上下文分配后调用一次可在其中按硬件要求放置软件回退software fallbackwalksize等于 chunk size但当算法能并行处理多个 chunk 时效率更高可设为 chunk size 的整数倍公共字段SKCIPHER_ALG_COMMONmin_keysize、max_keysize、ivsize、chunksize、statesize、base来自struct skcipher_alg_common除ivsize外全部必填。7.4 生命周期总结三段式使用范式结合 Documentation/crypto/intro.rst 的定义任何 skcipher 使用方都遵循同一三段式生命周期初始化crypto_alloc_skcipher()分配句柄 →crypto_skcipher_setkey()设置密钥 →每次操作时skcipher_request_alloc()分配请求执行skcipher_request_set_callback()设回调、skcipher_request_set_crypt()设 src/dst/len/iv →crypto_skcipher_encrypt()/decrypt()必要时用crypto_wait_req()同步等待销毁skcipher_request_free()释放请求、crypto_free_skcipher()释放句柄两者都会清零内存。相关源码与文档索引算法注册与查询实现见 crypto/api.cskcipher 核心实现见 crypto/skcipher.c自测程序见 crypto/tcrypt.c 与 crypto/testmgr.cAPI 总览见 Documentation/crypto/api.rst 与 Documentation/crypto/intro.rst。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考