尧图精选

OpenSSL 中的 SLH-DSA(SLH-DSA)状态哈希签名实现设计详解

🕒 发布时间:2026/9/10 17:08:39 📁 来源:尧图网络
OpenSSL 中的 SLH-DSASLH-DSA状态哈希签名实现设计详解【免费下载链接】opensslGeneral purpose TLS and crypto library项目地址: https://gitcode.com/GitHub_Trending/ope/openssl导读SLH-DSAStateless Hash-Based Digital Signature Algorithm无状态基于哈希的数字签名算法是 NIST FIPS 205 标准定义的抗量子签名方案其安全性仅依赖于哈希函数的抗碰撞/抗原像特性不依赖任何数论困难问题因此被视为 RSA/ECDSA 在量子时代的后继者之一。本文以 OpenSSL 仓库中的设计文档 doc/designs/slh-dsa.md 为核心骨架结合 crypto/slh_dsa 目录下的真实源码实现系统讲解 SLH-DSA 的 12 组参数集、密钥内部结构、哈希函数分层、Pure/Pre-Hash 两种消息编码方式、EVP 签名 API 使用方式以及常量时间安全考量帮助读者从会用到懂原理完整掌握 OpenSSL 中 SLH-DSA 的实现全貌。一、概述FIPS 205 与 OpenSSL 的落地方式FIPS 205Stateless Hash-Based Digital Signature Standard明确定义了 SLH-DSA 的绝大部分需求并为全部算法提供了完整伪代码。因此OpenSSL 在实现时并不需要重新发明算法细节而是聚焦于三件事如何在 OpenSSL 的 Provider/EVP 架构下组织 12 组参数、如何复用哈希基础设施EVP_MD、EVP_MAC、SHAKE避免代码重复、如何将签名/验签流程接入既有的 EVP API 与命令行工具。在 OpenSSL 中SLH-DSA 的实现代码集中在底层算法crypto/slh_dsa含 slh_wots.c、slh_xmss.c、slh_hypertree.c、slh_fors.c、slh_hash.c、slh_adrs.c、slh_params.c、slh_dsa_key.c、slh_dsa_hash_ctx.c 等Provider 层providers/implementations/signature/slh_dsa_sig.c 与 providers/implementations/keymgmt/slh_dsa_kmgmt.c测试test/slh_dsa_test.c、test/recipes/30-test_slh_dsa.t下文所有代码细节均可在上述文件中找到对应实现。二、SLH-DSA 参数与函数12 组参数集的统一建模2.1 十二组参数集与命名FIPS 205 第 11 节定义了12 组参数集。OpenSSL 的 key manager 与签名函数也一一对应12 个 key manager 12 个签名函数命名形如SLH-DSA-SHA2-128s、SLH-DSA-SHAKE-128f。后缀sslow/small代表签名更小但速度更慢ffast代表速度更快但签名更大。这 12 组参数在 crypto/slh_dsa/slh_params.c 中以静态表形式定义参数集nhdhakm安全类别公钥字节数签名字节数SLH-DSA-SHA2-128s1663791214301327856SLH-DSA-SHAKE-128s1663791214301327856SLH-DSA-SHA2-128f16662236333413217088SLH-DSA-SHAKE-128f16662236333413217088SLH-DSA-SHA2-192s24637914173934816224SLH-DSA-SHAKE-192s24637914173934816224SLH-DSA-SHA2-192f24662238334234835664SLH-DSA-SHAKE-192f24662238334234835664SLH-DSA-SHA2-256s32648814224756429792SLH-DSA-SHAKE-256s32648814224756429792SLH-DSA-SHA2-256f32681749354956449856SLH-DSA-SHAKE-256f32681749354956449856表中 n 为哈希输出长度16/24/32 字节h 为整棵超树总高度d 为树分层数h h/d 为单棵 Merkle 树高度a 为 FORS 树高度k 为 FORS 树数量m 为 H_MSG() 输出长度。lgww16log₂w4对所有参数集相同因此源码中直接省略。这些常量在 slh_params.h 的SLH_DSA_PARAMS结构体中统一定义。2.2 7 个哈希函数与 SHA2/SHAKE 分支设计文档指出SLH-DSA 内部共涉及7 个哈希函数SHAKE 系参数集非常简单全部复用SHAKE-256 这一种 XOF即使名字里带 SHAKE-128 也是如此SHA2 系参数集复杂得多需要HMAC、MGF1 以及摘要算法的组合且存在两组不同的函数集合第 1 组用于安全类别 1第 2 组用于安全类别 3 与 5区别在于 SHA-256 与 SHA-512 的选择以及补零位数的上界。SHA2 中补零位数上界在两个头文件中体现安全类别 1OSSL_SLH_DSA_SHA2_NUM_ZEROS_H_AND_T_BOUND1 64见 slh_params.h安全类别 3/5OSSL_SLH_DSA_SHA2_NUM_ZEROS_H_AND_T_BOUND2 128见 slh_params.c。2.3 ADRS 对象两种编码长度部分哈希函数需要一个ADRSAddress对象来标识其在状态树中的地址SHAKE 算法为32 字节SHA2 算法因采用压缩格式为22 字节因此两者的 ADRS 构造/设置函数是不同的。这一差异在 slh_adrs.c 与 slh_dsa_key.h 中以SLH_ADRS_FUNC函数指针组的形式抽象密钥对象中持有对应参数集的 ADRS 函数指针。2.4 核心抽象SLH_DSA_KEY 与 SLH_DSA_HASH_CTX设计文档的核心结论是与其把代码重复 12 遍不如把差异收进数据对象。因此SLH_DSA_KEY定义于 crypto/slh_dsa/slh_dsa_key.h保存哈希函数指针、ADRS 函数指针、参数常量params以及预取的算法对象EVP_MD *md、EVP_MD *md_sha512、EVP_MAC *hmac。SLH_DSA_HASH_CTX定义于 crypto/slh_dsa/slh_dsa_local.h引用 key并包含每次操作各自的哈希上下文对象底层 SHAKE 对象shactx、预哈希了 PK.seed 的shactx_pkseed、工作区scratch以及 SHA2 系需要的EVP_MAC_CTX *hmac_ctx用于 PRFmsg。中间哈希状态集中在同一对象中释放时统一擦除。该SLH_DSA_HASH_CTX被传给几乎所有底层函数WOTS、XMSS、超树、FORS并在 Provider 的签名上下文PROV_SLH_DSA_CTX中分配。可见 providers/implementations/signature/slh_dsa_sig.c 的PROV_SLH_DSA_CTX结构其中包含key不拥有所有权、hash_ctx、context_string、add_random、msg_encode、deterministic等字段。三、SLH-DSA 密钥结构公钥与私钥的存储布局3.1 文档给出的设计结构设计文档给出了密钥结构的核心设计struct slh_dsa_key_st { /* The public key consists of a SEED and ROOT values each of size |n| */ uint8_t pub[SLH_DSA_MAX_KEYLEN]; /* The private key consists of a SEED and PRF values of size |n| */ uint8_t priv[SLH_DSA_MAX_KEYLEN]; size_t key_len; /* This value is set to 2 * n if there is a public key */ /* contains the algorithm name and constants such as |n| */ const SLH_DSA_PARAMS *params; int has_priv; /* Set to 1 if there is a private key component */ ... };要点如下私钥与公钥各包含 2 个大小为 n 的元素公钥 PK.seed || PK.root私钥 SK.seed || SK.prf不同算法的 n 不同16/24/32因此代码统一使用最大尺寸的缓冲区密钥每部分最多 64 字节即SLH_DSA_MAX_KEYLENkey_len与has_priv两个字段用于判断密钥是否已加载公/私钥元素params通过算法名解析得到参数集ossl_slh_dsa_params_get()见 slh_params.c。3.2 实际源码布局实际实现slh_dsa_key.h对布局做了一点演进私钥缓冲区扩大为uint8_t priv[4 * SLH_DSA_MAX_N]四个 n 大小的槽位依次存放SK.seed、SK.prf、PK.seed、PK.rootpub指针初始为 NULL加载密钥后指向priv[n * 2]。宏定义清晰表达了这一布局#define SLH_DSA_SK_SEED(key) ((key)-priv) #define SLH_DSA_SK_PRF(key) ((key)-priv (key)-params-n) #define SLH_DSA_PK_SEED(key) ((key)-priv (key)-params-n * 2) #define SLH_DSA_PK_ROOT(key) ((key)-priv (key)-params-n * 3)3.3 与 FIPS 205 的一个关键差异设计文档特别强调FIPS 205 中 SLH-DSA 私钥内含公钥而 OpenSSL 将两者分开存储因此要构成有效密钥必须始终存在公钥。加载时若只有私钥而没有公钥则密钥不完整——这与 X25519 等可从私钥派生出公钥的算法不同SLH-DSA 的公钥 ROOT 分量并不能直接从私钥元素推出编码私钥时也必须携带公钥。3.4 密钥生成流程密钥生成过程先用DRBG产生私钥以及半个公钥PK.seed再依据这些值计算出公钥的root分量。设计文档特别指出ACVP 测试场景下这些值通过OSSL_SIGNATURE_PARAM_TEST_ENTROPY即 ENTROPY 参数外部注入并假定从数据fromdata加载时不会出现只有一半公钥的情况若确有此类需求应走密钥生成操作。该参数与其余可设参数一并定义在 providers/implementations/signature/slh_dsa_sig.inc.in[OSSL_SIGNATURE_PARAM_CONTEXT_STRING, context, octet_string], [OSSL_SIGNATURE_PARAM_TEST_ENTROPY, entropy, octet_string], [OSSL_SIGNATURE_PARAM_DETERMINISTIC, det, int], [OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, msgenc, int],四、Pure 与 Pre-Hash 签名生成两种消息编码4.1 Pure SLH-DSA默认普通签名流程称为Pure SLH-DSA Signature Generation消息在内部编码为0x00 || len(ctx) || ctx || message其中ctx为可选的上下文串长度为 0x00..0xFF。设计文档说明ACVP 测试要求支持不对消息做任何编码的能力这一开关由可设置参数OSSL_SIGNATURE_PARAM_MESSAGE_ENCODINGmsgenc控制。在 slh_dsa_sig.c 中可见两种模式常量#define SLH_DSA_MESSAGE_ENCODE_RAW 0 /* 不对消息编码 */ #define SLH_DSA_MESSAGE_ENCODE_PURE 1 /* 默认Pure 编码 */Provider 新建签名上下文时默认设置为SLH_DSA_MESSAGE_ENCODE_PURE见 slh_dsa_sig.c。4.2 Pre-Hash SLH-DSA暂不支持Pre-Hash 变体的消息编码为0x01 || len(ctx) || ctx || digest_OID || H(message)设计文档指出该场景的适用情形是编码后的消息由外部来源提供。但由于它与 OpenSSL 现有 EVP API 的契合度不高当前版本尚未支持Pre-Hash 变体。这是文档明确声明的现状读者不应将其误认为已实现的功能。4.3 签名时的随机化与确定性签名上下文还支持两类与消息编码正交的行为随机化签名非确定性模式下每次签名调用会通过RAND_priv_bytes_ex()生成 n 字节的临时随机数见 slh_dsa_sig.c用完立即用OPENSSL_cleanse()擦除确定性签名通过OSSL_SIGNATURE_PARAM_DETERMINISTIC开启熵注入OSSL_SIGNATURE_PARAM_TEST_ENTROPY注入的add_random长度必须严格等于 n否则报错见 slh_dsa_sig.c这正对应 ACVP 测试需求。五、签名 APIEVP 层接入方式5.1 推荐的 one-shot API由于 SLH-DSA 只需 one-shot 实现、且消息不做摘要设计文档指定使用EVP_PKEY_sign_message_init(), EVP_PKEY_sign(), EVP_PKEY_verify_message_init(), EVP_PKEY_verify().在 Provider 侧这对应OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT / SIGN / VERIFY_MESSAGE_INIT / VERIFY四个 dispatch 函数均已在 slh_dsa_sig.c 的MAKE_SIGNATURE_FUNCTIONS宏中为全部 12 个算法名注册。5.2 兼容层Digest 系列 API 的限制出于向后兼容EVP_DigestSignInit_ex()、EVP_DigestSign()、EVP_DigestVerifyInit_ex()、EVP_DigestVerify()也可使用但传入的mdname必须为 NULL此时行为与上面的 one-shot API 等价传入非 NULL 摘要会直接报错。这一约束在 Provider 层有明确实现slh_dsa_sig.cif (mdname ! NULL mdname[0] ! \0) { ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, Explicit digest not supported for SLH-DSA operations); return 0; }5.3 两个必须满足的 getter 契约设计文档要求两处 getter 配合key manager getter中OSSL_PKEY_PARAM_MANDATORY_DIGEST必须返回空串向调用方表明无需也不接受摘要signature context getter中OSSL_SIGNATURE_PARAM_ALGORITHM_ID返回 DER 编码的 AlgorithmIdentifier。后者在 slh_dsa_sig.c 通过WPACKET调用ossl_DER_w_algorithmIdentifier_SLH_DSA()生成。5.4 命令行支持由于上述 API 接入OpenSSL 命令行工具如openssl pkeyutl、openssl req等天然支持 SLH-DSA 算法名如SLH-DSA-SHA2-128s、SLH-DSA-SHAKE-128f。相关测试脚本 test/recipes/30-test_slh_dsa.t 与 test/recipes/30-test_evp.t 覆盖了密钥生成、DER 编解码与签名验签路径。六、缓冲区使用约定PACKET 与 WPACKET 的分工SLH-DSA 内部有大量大小为 n16/24/32 之一的缓冲区用于密钥元素与哈希值。设计文档明确规定这类固定小缓冲区不使用 PACKET直接以裸缓冲区传递输出方向如签名生成使用WPACKET顺序写入输入方向如读取签名数据使用PACKET顺序读取。这一约定在底层函数签名中清晰可见例如 slh_dsa_local.h 中 WOTS 的签名与验签函数分别以WPACKET *sig_wpkt和PACKET *sig_rpkt作为签名缓冲区接口超树ossl_slh_ht_sign/ossl_slh_ht_verify与 FORSossl_slh_fors_sign/ossl_slh_fors_pk_from_sig同样遵循此约定。七、常量时间考虑哈希签名方案的天然优势设计文档指出由于SLH-DSA 的安全性只依赖哈希函数不涉及任何依赖秘密数据的分支如大整数取模中的条件减法因此预期不存在常量时间泄露问题。源码中的少量if语句仅用于检测哈希操作的失败这些错误会沿函数调用栈逐层向上传播相关函数均以__owur标注返回值必须被检查。文档明确说明这些错误在正常情况下不应发生因此不会影响算法安全。此外slh_dsa_local.h 中的设计也体现了秘密数据的处理纪律中间哈希状态可能由秘密派生的中间值集中在scratch工作区中随SLH_DSA_HASH_CTX释放时统一擦除避免秘密残留在栈上散落各处。八、小结一份可追踪的实现蓝图将设计文档与源码对照可以得出 OpenSSL 中 SLH-DSA 实现的完整蓝图12 组参数集收敛为一张静态参数表slh_params.c配合n/h/d/h/a/k/m等常量驱动全部算法7 个哈希函数通过 SHAKE-256SHAKE 系与 HMAC/MGF1/摘要SHA2 系两条路径实现差异由 slh_hash.c 中的SLH_HASH_FUNC抽象ADRS 的 32/22 字节差异由SLH_ADRS_FUNC函数指针组封装密钥采用priv[4n]单缓冲区布局 pub指针强制有公钥才有效的不变量签名/验签通过 one-shot EVP API 接入Digest 系列 API 仅作兼容且强制mdnameNULL缓冲输出用 WPACKET、输入用 PACKET固定小缓冲区直传安全上不依赖常量时间技巧仅需传播哈希失败错误并统一擦除中间状态。以上每一层都可以在 crypto/slh_dsa 与 providers/implementations/signature/slh_dsa_sig.c 中找到对应实现读者可沿此路径深入阅读也可通过 test/slh_dsa_test.c 观察密钥生成、fromdata 加载与签名验签的完整调用示例。【免费下载链接】opensslGeneral purpose TLS and crypto library项目地址: https://gitcode.com/GitHub_Trending/ope/openssl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →