在后量子密码学(post-quantum cryptography)的版图中,格基(lattice-based)方案因其优异的性能占据了主流位置,而基于编码(code-based)和基于同源(isogeny-based)的方案也各有拥趸。然而,有一类签名方案以其极端的保守性和简洁的安全假设独树一帜——这就是哈希基签名(hash-based signature)。哈希基签名的安全性完全且仅仅建立在哈希函数的安全性之上:只要底层哈希函数是安全的,签名方案就是安全的。这一特性使其成为对抗未知密码分析突破的最后防线,也使其在 NIST 后量子标准化进程中被选定为 SLH-DSA(即 SPHINCS+)标准。本文将从最简单的 Lamport 一次签名出发,逐步构建到工业级的无状态签名方案 SPHINCS+,完整展现哈希基签名四十余年的演化历程。
一、哈希基签名的设计哲学
密码学方案的安全性总是建立在某种计算困难假设之上。RSA 依赖大整数分解的困难性,椭圆曲线依赖离散对数问题,格基方案依赖最短向量问题(SVP)或学习带误差问题(LWE)的困难性。这些假设虽然经过了长期检验,但它们本质上都是”结构化”假设——一旦数学上出现突破(如 Shor 算法对分解和离散对数的攻击),整个方案便会崩塌。
哈希基签名的设计哲学截然不同。它追求的是最小假设原则(minimal assumption):方案的安全性仅依赖于一个密码学原语——哈希函数。具体而言,哈希基签名通常要求底层哈希函数满足以下一种或多种性质:单向性(one-wayness,即给定 \(y = H(x)\),难以找到 \(x\))、抗第二原像性(second preimage resistance,即给定 \(x\),难以找到 \(x' \neq x\) 使得 \(H(x') = H(x)\))以及抗碰撞性(collision resistance,即难以找到任意一对 \(x \neq x'\) 使得 \(H(x) = H(x')\))。
这种最小假设带来了独特的优势。首先,哈希函数是密码学中研究最为深入、应用最为广泛的原语之一,SHA-2 和 SHA-3 家族经历了数十年的公开密码分析,其安全性有着极高的置信度。其次,即便量子计算机全面实现,Grover 算法对哈希函数的攻击也仅能将安全级别减半——对于 256 位的哈希函数,量子攻击后仍保留 128 位安全性,这与经典安全级别的折损相比是非常温和的。最后,哈希基签名的安全证明通常是紧致的(tight reduction),即方案的安全性可以直接且高效地归约到哈希函数的安全性,中间几乎没有损失。
当然,最小假设也带来了代价。哈希基签名的密钥和签名尺寸通常远大于格基方案,签名速度也往往较慢。这是一种安全性与效率之间的经典权衡。正因如此,哈希基签名特别适合那些对安全性要求极高、而对签名尺寸和速度不太敏感的应用场景,例如固件签名(firmware signing)、证书颁发机构(CA)的根证书签名以及代码签名(code signing)。
二、Lamport 一次签名
哈希基签名的故事始于 1979 年 Leslie Lamport 提出的一次签名方案(one-time signature, OTS)。Lamport 签名的构造之简洁令人叹为观止,它清晰地展示了如何仅用哈希函数的单向性来实现数字签名。
构造细节
假设我们要签名的消息经哈希后得到一个 \(n\) 位的摘要。Lamport 签名的密钥生成过程如下:对于摘要的每一位 \(i\)(\(i = 0, 1, \ldots, n-1\)),随机生成两个 \(n\) 位的秘密值 \(sk_i^0\) 和 \(sk_i^1\),然后分别计算其哈希值 \(pk_i^0 = H(sk_i^0)\) 和 \(pk_i^1 = H(sk_i^1)\)。私钥是全部 \(2n\) 个秘密值的集合,公钥是全部 \(2n\) 个哈希值的集合。
签名时,对消息 \(m\) 先计算其 \(n\) 位摘要 \(d = H(m)\)。然后,对于摘要的第 \(i\) 位,若该位为 0,则公开 \(sk_i^0\);若该位为 1,则公开 \(sk_i^1\)。签名就是这 \(n\) 个被选择公开的秘密值。
验证时,验证者对签名中的每个值计算哈希,并与公钥中对应位置的值进行比较。若全部匹配,则签名有效。
安全性证明
Lamport 签名的安全性可以直接归约到哈希函数的单向性。假设存在一个伪造者能够在看到一条消息的合法签名后,伪造另一条不同消息的签名。由于两条消息的哈希摘要至少有一位不同,那么在该位上,伪造者需要找到一个他从未见过的秘密值 \(sk_i^b\) 的原像——而公钥中恰好包含了 \(H(sk_i^b)\)。这直接构成了对哈希函数单向性的攻击。因此,只要哈希函数是单向的,Lamport 签名就是存在性不可伪造的(existentially unforgeable)。
需要特别强调的是,Lamport 签名是严格的一次签名:同一密钥对只能安全地签署一条消息。如果用同一密钥签署两条不同的消息,攻击者可能会收集到同一位置的 \(sk_i^0\) 和 \(sk_i^1\),从而完全控制该位的签名能力。当足够多的位被暴露后,攻击者就能伪造任意消息的签名。
密钥与签名尺寸分析
对于 256 位安全级别(\(n = 256\)),每个秘密值和哈希值都是 256 位(32 字节)。私钥包含 \(2 \times 256 = 512\) 个 32 字节的值,总大小为 16384 字节(16 KB)。公钥同样是 16 KB。签名包含 256 个 32 字节的值,总大小为 8192 字节(8 KB)。这些尺寸在现代标准下是巨大的,也是哈希基签名面临的核心挑战之一。
以下是一个完整的 Lamport 一次签名 Python 实现,展示了密钥生成、签名和验证的全过程:
import hashlib
import os
def lamport_keygen(n=256):
"""生成 Lamport 一次签名密钥对。
Args:
n: 哈希摘要的位数,决定安全级别。
Returns:
(sk, pk): 私钥和公钥,均为长度 n 的列表,
每个元素是一对 32 字节值。
"""
sk = []
pk = []
for _ in range(n):
s0 = os.urandom(32)
s1 = os.urandom(32)
p0 = hashlib.sha256(s0).digest()
p1 = hashlib.sha256(s1).digest()
sk.append((s0, s1))
pk.append((p0, p1))
return sk, pk
def lamport_sign(sk, message: bytes):
"""使用 Lamport 私钥签名消息。
Args:
sk: 私钥列表。
message: 待签名的消息字节串。
Returns:
sig: 长度为 n 的签名列表,每个元素为 32 字节。
"""
digest = hashlib.sha256(message).digest()
sig = []
for i in range(256):
byte_idx, bit_idx = divmod(i, 8)
bit = (digest[byte_idx] >> (7 - bit_idx)) & 1
sig.append(sk[i][bit])
return sig
def lamport_verify(pk, message: bytes, sig):
"""验证 Lamport 签名。
Args:
pk: 公钥列表。
message: 原始消息字节串。
sig: 签名列表。
Returns:
bool: 签名是否有效。
"""
digest = hashlib.sha256(message).digest()
for i in range(256):
byte_idx, bit_idx = divmod(i, 8)
bit = (digest[byte_idx] >> (7 - bit_idx)) & 1
if hashlib.sha256(sig[i]).digest() != pk[i][bit]:
return False
return True
# 演示
if __name__ == "__main__":
sk, pk = lamport_keygen()
msg = "哈希基签名示例消息".encode("utf-8")
signature = lamport_sign(sk, msg)
assert lamport_verify(pk, msg, signature), "合法签名验证失败"
tampered = "篡改后的消息".encode("utf-8")
assert not lamport_verify(pk, tampered, signature), "篡改消息不应通过验证"
print("Lamport 一次签名:签名与验证均通过")三、Winternitz OTS(WOTS+)
Lamport 签名虽然概念清晰,但其密钥和签名尺寸过大。1979 年 Robert Winternitz 提出了一种时间-空间权衡(time-memory tradeoff)的改进方案,被称为 Winternitz 一次签名(Winternitz One-Time Signature, WOTS)。其核心思想是:不再逐位签名,而是将消息摘要按若干位分组,每组使用一条哈希链来编码。
参数 \(w\) 与哈希链
WOTS 引入了一个关键参数 \(w\)(Winternitz 参数),通常取 \(w = 4, 16, 256\) 等 2 的幂次。消息摘要被分成若干个 \(\log_2 w\) 位的块,每个块的值范围为 \([0, w-1]\)。对于每个块,密钥生成时随机选取一个种子值 \(sk_i\),然后对其迭代哈希 \(w - 1\) 次,得到哈希链的末端值作为公钥分量:\(pk_i = H^{w-1}(sk_i)\)。
签名时,若第 \(i\) 个块的值为 \(v_i\),则公开哈希链的第 \(v_i\) 个中间值:\(\sigma_i = H^{v_i}(sk_i)\)。验证者收到 \(\sigma_i\) 后,再对其迭代哈希 \(w - 1 - v_i\) 次,检查结果是否等于 \(pk_i\):\(H^{w-1-v_i}(\sigma_i) = pk_i\)。
为了防止攻击者通过对签名值继续哈希来伪造更大块值的签名,WOTS 还引入了校验和(checksum)机制。校验和的值等于 \(\sum_i (w - 1 - v_i)\),它确保了如果攻击者将某个块的值增大(即沿哈希链向前推进),校验和必然减小,而减小校验和对应的块值需要反向计算哈希——这正是哈希函数的单向性所阻止的。
WOTS+ 的改进
原始 WOTS 方案使用简单的迭代哈希 \(H(H(\cdots H(x)))\),这在安全性证明中需要较强的假设。2013 年 Andreas Hülsing 提出了 WOTS+(WOTS-plus),其核心改进是在每次哈希迭代中引入一个随机掩码(bitmask)和一个密钥(key),将链式哈希变为 \(H(k_j \| (r_j \oplus x_j))\),其中 \(k_j\) 和 \(r_j\) 是公开的伪随机值。这种改进使得安全性证明只需要哈希函数满足单向性和抗第二原像性(而非抗碰撞性),从而降低了安全假设的强度。
尺寸权衡
\(w\) 参数控制着签名尺寸与签名速度之间的权衡。当 \(w\) 增大时,每个块编码更多位,所需的链数减少,签名尺寸变小;但每条链的长度增加,签名和验证所需的哈希计算次数增多。以 256 位安全级别为例:当 \(w = 16\) 时,消息链数为 64,校验和链数约为 3,总共约 67 个 32 字节值,签名大小约 2144 字节——相比 Lamport 的 8192 字节减少了约 74%。当 \(w = 256\) 时,签名尺寸进一步缩小至约 1088 字节,但每次签名或验证平均需要计算约 4000 次哈希。在实际部署中,\(w = 16\) 是最常见的选择,它在尺寸和速度之间取得了较好的平衡。
四、Merkle 树签名
一次签名方案的根本局限在于每个密钥只能使用一次。为了将 OTS 扩展为可多次签名的方案,Ralph Merkle 在 1979 年提出了基于二叉树的构造——Merkle 树签名(Merkle tree signature, MSS)。
基本构造
Merkle 树签名的核心思想是:预先生成 \(2^h\) 个 OTS 密钥对(其中 \(h\) 为树的高度),将所有 OTS 公钥作为叶子节点构建一棵二叉哈希树(Merkle 树)。树的根节点哈希值作为整个方案的公钥。签名时,依次使用第 \(i\) 个 OTS 密钥签名消息,并附上从该叶子节点到根节点的认证路径(authentication path)——即沿路径上每个兄弟节点的哈希值。
验证者收到签名后,首先验证 OTS 签名的有效性,然后利用认证路径从叶子节点逐层向上计算,最终检查计算得到的根节点哈希值是否与公钥一致。认证路径的长度恰好等于树的高度 \(h\),因此验证所需的额外空间开销仅为 \(O(h)\)——这是对数级别的。
认证路径的直觉
为了理解认证路径的作用,可以这样想:公钥(树根)是对所有 \(2^h\) 个 OTS 公钥的一种”压缩承诺”(commitment)。认证路径允许验证者确认某个特定的 OTS 公钥确实是这棵树的一片叶子,从而将 OTS 签名的有效性提升为整个方案的有效性。这与区块链中 Merkle 证明的原理完全一致。
尺寸分析
Merkle 树签名的总签名大小等于一个 OTS 签名的大小加上认证路径的大小。以 WOTS+(\(w = 16\), \(n = 256\))为底层 OTS、树高 \(h = 20\) 为例:OTS 签名约 2144 字节,认证路径为 \(20 \times 32 = 640\) 字节,总签名大约 2784 字节。公钥仅为一个 32 字节的树根哈希值。这样一个方案可以签署 \(2^{20} \approx 100\) 万条消息。
Merkle 树签名的主要问题在于状态管理:签名者必须精确地跟踪哪些 OTS 密钥已经被使用,绝不能重复使用同一个 OTS 密钥。如果因为软件错误、系统崩溃或备份恢复导致状态回滚,已使用的 OTS 密钥可能被再次使用,从而危及安全性。这一问题催生了两个方向的研究:一是优化有状态方案的工程实现,二是寻求无状态的替代方案。
五、XMSS 与 LMS
有状态哈希基签名方案在 2010 年代得到了标准化。IETF 发布了两个重要的 RFC 文档:RFC 8391 定义了 XMSS(eXtended Merkle Signature Scheme)及其多树变体 XMSS^MT,RFC 8554 定义了 LMS(Leighton-Micali Signature)及其层次化变体 HSS(Hierarchical Signature System)。这两个方案在结构上非常相似,都基于 Merkle 树和 WOTS+ 一次签名,但在具体实现细节上有所不同。
XMSS 的设计要点
XMSS 在 Merkle 树的基础上引入了几项重要改进。首先,它使用了一种称为 L-tree 的结构来压缩 WOTS+ 公钥:将 WOTS+ 公钥的多个分量通过一棵小型哈希树压缩为单个哈希值,作为 Merkle 树的叶子。其次,XMSS 在所有哈希计算中引入了地址方案(address scheme),确保树中不同位置的哈希计算使用不同的参数,从而避免了多目标攻击(multi-target attack)。最后,XMSS 的安全性证明仅依赖哈希函数的抗第二原像性和伪随机性,而不需要抗碰撞性,这是一个比直觉上更弱的假设。
XMSS^MT(多树变体)将一棵高度为 \(h\) 的大树分解为 \(d\) 层高度为 \(h/d\) 的小树。顶层树的叶子不再是消息签名用的 OTS 公钥,而是下一层子树的根哈希值。只有最底层的叶子才用于签名消息。这种层次化结构的好处是:密钥生成时不需要一次性计算所有 \(2^h\) 个 OTS 密钥,而是可以按需生成子树,大幅降低了密钥生成的时间和空间开销。
LMS/HSS 的设计要点
LMS 的设计更加简洁直接。它同样基于 Merkle 树和 WOTS+(在 LMS 标准中称为 LM-OTS),但省略了 L-tree 压缩步骤,直接对 WOTS+ 公钥的所有分量进行哈希后作为叶子节点。HSS 是 LMS 的层次化扩展,与 XMSS^MT 的思路一致。NIST 在其 SP 800-208 文档中同时推荐了 XMSS 和 LMS 用于有状态哈希基签名的部署。
BDS 树遍历算法
在实际部署中,Merkle 树签名面临的一个实际问题是认证路径的高效计算。朴素方法需要存储整棵树或在每次签名时重新计算大量节点。Buchmann-Dahmen-Szydlo(BDS)算法提供了一种优雅的解决方案:通过维护少量缓存节点和在每次签名后执行少量增量计算,使得每次签名的计算量和存储量都保持在 \(O(h)\) 级别。BDS 算法是 XMSS 参考实现中使用的核心树遍历算法。
状态管理的挑战
有状态方案最大的部署挑战是状态管理。签名者必须维护一个计数器,记录当前已使用的叶子索引。这个计数器必须满足以下要求:持久性(不能因系统崩溃而丢失)、单调性(只能递增,不能回滚)和唯一性(在分布式环境中,不同节点不能使用相同的索引)。在实践中,这意味着每次签名前必须将新的计数器值写入非易失性存储(如硬件安全模块 HSM),并确保写入成功后才进行签名。虚拟机快照、数据库备份恢复等操作都可能导致状态回滚,从而引发安全风险。
正是这些严苛的工程要求,推动了无状态哈希基签名方案的研究。
六、SPHINCS+ / SLH-DSA 架构
SPHINCS+(读作”Sphincs plus”)是目前最成熟的无状态哈希基签名方案,由 Daniel J. Bernstein 等人在 2015 年提出(最初版本称为 SPHINCS),并在 2019 年发布了改进的 SPHINCS+ 版本。2024 年,NIST 将 SPHINCS+ 标准化为 SLH-DSA(Stateless Hash-based Digital Signature Algorithm),编号为 FIPS 205。
无状态的核心思想
SPHINCS+ 实现无状态的关键思想是:不再依赖有序使用叶子节点的计数器,而是通过伪随机选择来决定使用哪个叶子。具体而言,签名时使用一个确定性(或带有可选随机化的)伪随机函数,根据消息和私钥种子计算出一个随机索引,用该索引对应的 OTS 密钥进行签名。由于底层树足够大(典型高度为 60 至 68 层),两次签名使用同一叶子的概率可以忽略不计。
超树(Hypertree)结构
SPHINCS+ 的核心数据结构是超树(hypertree),这是一棵多层嵌套的 Merkle 树。超树的总高度为 \(h\),被分为 \(d\) 层,每层包含高度为 \(h/d\) 的 XMSS 风格的子树。从顶层到底层,每一层的 WOTS+ 签名用于认证下一层子树的根节点。最底层的子树叶子指向 FORS 签名的公钥。
FORS:少次签名(Few-time Signature)
SPHINCS+ 中一个关键创新是 FORS(Forest of Random Subsets)签名。FORS 是一种少次签名方案(few-time signature),它取代了传统 Merkle 树方案中最底层的一次签名。FORS 的安全性允许同一密钥被使用少数几次(而非仅一次),但如果使用次数过多,安全性会逐渐降低。
FORS 的构造如下:维护 \(k\) 棵高度为 \(a\) 的小型 Merkle 树,每棵树有 \(2^a\) 个叶子。签名时,消息摘要被分成 \(k\) 个 \(a\) 位的块,每个块的值指定从对应树中选取哪个叶子。签名包含被选中的 \(k\) 个叶子值及其认证路径,FORS 公钥是所有 \(k\) 棵树根的哈希。参数 \(k\) 和 \(a\) 的选取需要确保即使同一 FORS 密钥被使用若干次,攻击者也无法收集到足够的信息来伪造签名。
完整签名流程
将上述组件组合在一起,SPHINCS+ 的签名流程如下:
- 给定消息 \(m\) 和私钥种子,使用伪随机函数生成一个随机值 \(R\) 和叶子索引 \(idx\)。
- 计算消息摘要 \(md = H(R \| pk \| m)\),其中 \(pk\) 是公钥。
- 使用索引 \(idx\) 对应的 FORS 密钥对摘要进行 FORS 签名,得到 FORS 签名和 FORS 公钥。
- 将 FORS 公钥作为最底层超树的叶子,使用 WOTS+ 和认证路径逐层向上签名,直到超树的根节点。
- 最终签名由随机值 \(R\)、FORS 签名和超树中所有层的 WOTS+ 签名与认证路径组成。
验证者则按相反的顺序:先验证 FORS 签名,再从底层到顶层逐层验证超树,最终检查顶层树根是否与公钥一致。
七、SPHINCS+ 参数选择
NIST 在 FIPS 205(SLH-DSA)中定义了 12 组参数集,按安全级别(128/192/256 位)、速度偏好(f 表示快速签名、s 表示小签名)和底层哈希函数(SHA-256 或 SHAKE256)进行组合。
参数集概览
以 SHA-256 为底层哈希函数的六组参数集为例:
SLH-DSA-SHA2-128f 的参数为 \(n = 16\), \(h = 66\), \(d = 22\), \(k = 33\), \(a = 6\), \(w = 16\)。该参数集优化签名速度,签名大小约为 17088 字节,公钥 32 字节,签名速度较快但签名尺寸较大。
SLH-DSA-SHA2-128s 的参数为 \(n = 16\), \(h = 63\), \(d = 7\), \(k = 14\), \(a = 12\), \(w = 16\)。该参数集优化签名大小,签名约为 7856 字节,但签名速度显著变慢。
SLH-DSA-SHA2-192f 和 SLH-DSA-SHA2-192s 分别将安全级别提升至 192 位,\(n = 24\),签名大小分别约为 35664 字节和 16224 字节。
SLH-DSA-SHA2-256f 和 SLH-DSA-SHA2-256s 提供 256 位安全级别,\(n = 32\),签名大小分别约为 49856 字节和 29792 字节。
对应的 SHAKE 变体(SLH-DSA-SHAKE-128f 等)使用 SHAKE256 作为底层哈希函数,参数结构和签名尺寸与 SHA-256 变体相同,但在某些平台上 SHAKE 的计算性能可能不同。
f 与 s 变体的权衡
f(fast)变体使用更多层的超树(更大的 \(d\) 值),这意味着每层子树更矮、生成更快,但需要更多层的 WOTS+ 签名,导致签名尺寸更大。s(small)变体则相反:更少的层数意味着每层子树更高、生成更慢,但总签名中包含的 WOTS+ 签名数更少,签名尺寸更小。
具体而言,SLH-DSA-SHA2-128f 的签名时间约为数毫秒,而 SLH-DSA-SHA2-128s 的签名时间可能达到数百毫秒。验证速度方面,两者差异不大,通常在亚毫秒至数毫秒级别。因此,f 变体适合对签名延迟敏感的场景,s 变体适合对带宽和存储敏感的场景。
参数选择建议
对于大多数应用场景,128 位安全级别已经足够——它提供了与 AES-128 等价的安全强度。192 位和 256 位参数集主要面向合规性要求(如某些政府机构要求 256 位安全级别)或需要极长安全生命周期的场景。在 f 和 s 之间的选择取决于应用的具体需求:如果签名是离线操作(如固件发布),签名时间不敏感,可以选择 s 变体以节省存储和带宽;如果需要在线实时签名,则 f 变体更为合适。
八、安全性与性能分析
哈希函数的安全要求
SPHINCS+ 的安全性依赖于底层哈希函数的以下性质:
伪随机函数(PRF)安全性:用于从种子派生密钥和随机值。 单向性:WOTS+ 和 FORS 的安全性核心。 抗第二原像性:Merkle 树认证路径的安全性基础。 在标准模型(standard model)下,SPHINCS+ 的安全证明不需要抗碰撞性假设。这一点至关重要,因为对于 \(n\) 位的哈希函数,碰撞攻击的复杂度为 \(O(2^{n/2})\)(生日攻击),而原像攻击的复杂度为 \(O(2^n)\),抗碰撞性是一个更强的假设。SPHINCS+ 避免依赖抗碰撞性,使得在相同哈希输出长度下可以获得更高的安全级别。
多目标攻击的防御
在大规模部署中,攻击者可能同时攻击多个签名实例。如果系统中有 \(q\) 个签名者,攻击者对哈希函数原像的搜索空间增大了 \(q\) 倍,这就是多目标攻击(multi-target attack)。SPHINCS+ 通过在所有哈希计算中嵌入唯一的地址参数(包括树的层号、树内索引和链内位置),将每个哈希实例”域分离”(domain separation),使得多目标攻击的优势消失。具体而言,攻击者对某个特定位置的哈希值进行原像攻击时,无法利用其他位置的哈希值作为”跳板”。
性能基准
在现代 x86-64 处理器上(带 AVX2 指令集),SPHINCS+ 的典型性能如下:
对于 SLH-DSA-SHA2-128f:密钥生成约 0.5 毫秒,签名约 5-10 毫秒,验证约 0.5-1 毫秒。签名大小 17088 字节,公钥 32 字节,私钥 64 字节。
对于 SLH-DSA-SHA2-128s:密钥生成约 3 毫秒,签名约 80-200 毫秒,验证约 1-3 毫秒。签名大小 7856 字节。
作为对比,ML-DSA-65(基于格的 Dilithium 方案)的签名大小约 3293 字节,签名时间约 0.1 毫秒,验证时间约 0.05 毫秒。可以看到,SPHINCS+ 在签名尺寸上比 ML-DSA 大数倍,签名速度慢一到两个数量级,但验证速度的差距相对较小。SPHINCS+ 的优势在于其安全假设更为保守——只要 SHA-256 或 SHAKE256 是安全的,方案就是安全的,而 ML-DSA 还需要格问题的困难性假设成立。
量子安全性分析
在量子计算模型下,Grover 算法可以将哈希函数原像搜索的复杂度从 \(O(2^n)\) 降低到 \(O(2^{n/2})\)。因此,为了在量子环境下维持 128 位安全级别,需要使用 256 位的哈希输出。SPHINCS+ 的参数设计已经考虑了这一因素:128 位安全级别的参数集使用 128 位(16 字节)的哈希输出,对应经典 128 位安全、量子约 64 位安全。对于需要 128 位量子安全的场景,应选择 256 位参数集。但需要注意的是,Grover 算法在实际量子硬件上的加速效果受到诸多限制(如并行化困难、纠错开销),实际威胁可能低于理论分析所示。
九、部署建议
何时选择 SLH-DSA 而非 ML-DSA
NIST 后量子标准化同时推出了 ML-DSA(基于格)和 SLH-DSA(基于哈希)两种签名标准。在大多数应用场景中,ML-DSA 因其更小的签名尺寸和更快的速度是默认推荐。但以下场景应优先考虑 SLH-DSA:
当安全假设的保守性至关重要时:如果应用的安全生命周期长达数十年(如根 CA 证书、长期档案签名),选择 SLH-DSA 可以对冲格问题在未来被攻破的风险。当签名频率低但安全要求极高时:固件签名、BIOS 签名等场景通常只需要签名少量固件镜像,签名尺寸和速度不是瓶颈。当法规要求多样化算法部署时:某些安全框架要求使用多种不同假设的算法,以避免单点失败。
固件签名用例
固件签名是 SLH-DSA 最典型的应用场景之一。固件更新的频率通常为每月甚至每季度一次,单次签名耗时数百毫秒完全可以接受。固件镜像通常通过带外(out-of-band)渠道分发,17 KB 或 50 KB 的签名附加在数十 MB 的固件镜像上,尺寸开销可以忽略。更重要的是,固件签名的验证通常发生在资源受限的嵌入式设备上,而 SLH-DSA 的验证速度相对较快,哈希运算也容易在硬件中高效实现。
在固件签名的具体部署中,建议选择 SLH-DSA-SHA2-128s 或 SLH-DSA-SHA2-256s 参数集(取决于安全级别要求),以最小化签名尺寸。私钥应存储在硬件安全模块(HSM)中,签名操作在安全环境中离线完成。
混合部署策略
在后量子迁移的过渡期,混合签名(hybrid signature)是一种审慎的部署策略。混合签名将一个经典签名(如 ECDSA 或 Ed25519)与一个后量子签名(如 SLH-DSA)组合在一起,只有当两个签名都被验证通过时才接受。这样即使后量子方案存在未知弱点,经典签名仍能提供安全保障;反过来,即使量子计算机攻破了经典签名,后量子签名仍然有效。
混合部署的具体方式包括:在 X.509 证书中使用复合签名(composite signature),在 TLS 握手中同时协商经典和后量子算法,或在代码签名中附加双重签名。IETF 正在制定相关标准草案(如 draft-ietf-lamps-pq-composite-sigs),以规范混合签名的编码和验证流程。
对于已经部署了 ML-DSA 的系统,可以考虑将 SLH-DSA 作为备用算法(fallback algorithm)。如果未来格基方案遭遇密码分析突破,可以快速切换到 SLH-DSA 而无需重新设计整个密码基础设施。这种”算法敏捷性”(crypto agility)在后量子时代是系统设计的关键原则。
实施注意事项
部署 SLH-DSA 时需要注意以下几点:随机数生成的质量至关重要——私钥种子和签名过程中的随机化值都必须来自高质量的随机源。尽管 SPHINCS+ 支持确定性签名(避免了对随机数生成器的依赖),但 NIST 在 FIPS 205 中推荐使用带有额外随机化的签名模式,以提供对侧信道攻击(side-channel attack)的额外防护。此外,SPHINCS+ 的签名尺寸在某些受限协议中可能成为问题——例如在 DNSSEC 中,较大的签名会增加 DNS 响应的大小,可能导致 UDP 分片或回退到 TCP,影响性能。在这类场景中,需要仔细评估 SPHINCS+ 是否适用,或者是否应选择签名更小的 ML-DSA。
总而言之,哈希基签名以其无与伦比的安全假设保守性,在后量子密码学的工具箱中占据了不可替代的位置。从 Lamport 的一次签名到 SPHINCS+ 的无状态超树架构,四十余年的研究将一个简单的”哈希函数可以做签名”的想法发展成了工业级的标准方案。理解哈希基签名的构造原理和适用场景,是密码学工程师在后量子迁移中做出明智决策的重要基础。
十、有状态与无状态哈希基签名运维对照清单
在实际部署哈希基签名时,有状态方案(XMSS/LMS)与无状态方案(SPHINCS+/SLH-DSA)在运维复杂度上存在本质差异。以下清单从工程运维角度梳理两类方案的关键考量。
有状态方案(XMSS / LMS)运维清单:
- 状态管理:每次签名后必须更新并持久化状态索引;状态丢失或回滚将导致一次性密钥重用,安全性彻底崩溃
- 并发控制:多实例或多线程环境中必须对状态索引实施严格的互斥访问;建议将签名操作集中到单一可信服务
- 备份策略:状态备份必须与签名操作原子同步——不可使用快照恢复,否则可能重用已消耗的叶节点
- 容量规划:签名总次数在密钥生成时即已确定(由树高度决定);超出容量后必须更换密钥对
- 签名尺寸:较小(XMSS: 约 2.5 KB;LMS: 约 4-8 KB),适合带宽受限场景
- 验证速度:快——仅需若干次哈希运算
- 适用场景:固件签名、安全启动、证书签发等低频签名场景
- 灾难恢复:状态重用即为密钥泄露——一旦检测到状态不一致,必须立即吊销密钥并重新签发所有相关证书
无状态方案(SPHINCS+ / SLH-DSA)运维清单:
- 状态管理:无需维护任何状态;签名操作完全无副作用,可自由并发
- 并发控制:无特殊要求;天然支持多实例、多线程并行签名
- 备份策略:仅需备份私钥种子——与传统非对称密钥管理无异
- 容量规划:无签名次数限制
- 签名尺寸:较大(SLH-DSA-128s: 约 7.9 KB;SLH-DSA-128f: 约 17.1 KB),可能影响网络传输效率
- 验证速度:中等——需要更多哈希运算(尤其是
-f参数集) - 适用场景:通用签名需求、需要简单运维的后量子备用方案
- 灾难恢复:标准密钥管理流程即可;无状态相关的特殊风险
笔者认为,有状态哈希基签名的状态管理问题,本质上是一个伪装成密码学问题的分布式系统问题。表面上看,XMSS 和 LMS 的安全性证明简洁优雅——只要不重复使用一次性密钥,安全性就仅依赖于哈希函数的抗原像性。但”不重复使用”这个看似简单的要求,在真实的生产环境中却异常棘手:数据库崩溃后的状态恢复、主备切换时的状态同步、分布式 HSM 集群中的状态一致性——这些都是经典的分布式一致性问题,而非密码学问题。值得注意的是,这与分布式系统中”exactly-once 语义”的难题高度类似:我们需要确保每个叶节点恰好被使用一次,而这在存在网络分区和节点故障的环境中是出了名的困难。从工程实践来看,除非你的组织有成熟的分布式系统工程能力和专用的 HSM 基础设施,否则 SPHINCS+(SLH-DSA)的”大签名但零运维风险”往往是更明智的选择。用签名尺寸换取运维简单性,在大多数场景下是一笔划算的交易。
一个值得深思的现象是,哈希基签名是后量子密码学中的”蟑螂”——不是因为它丑陋,而是因为它几乎不可能被杀死。所有其他后量子签名方案(ML-DSA 基于格、BIKE/HQC 基于编码、SQISign 基于同源)的安全性都依赖于特定的数学困难问题,而这些问题的困难性虽然被广泛相信但尚未被数十年的密码分析完全验证。相比之下,哈希基签名的安全性实质上只依赖于一个假设:底层哈希函数是抗碰撞的(对于有状态方案甚至只需要抗第二原像性)。这是整个密码学中最弱、最被充分理解、最经得起时间考验的假设。如果格假设被攻破,ML-DSA 就死了;如果编码假设被攻破,BIKE 就死了;如果同源假设被攻破(SIDH 已经发生了这一幕),相关方案就死了——但只要 SHA-256 或 SHAKE-256 还站得住,哈希基签名就安然无恙。这种”假设最小化”的安全哲学,使得 XMSS/LMS/SPHINCS+ 成为后量子密码迁移中最稳健的”保底方案”——即便所有新型后量子假设都在未来某天被推翻,哈希基签名依然屹立不倒。
密码学百科系列 · 第 59 篇