30分钟看懂WOTS+一次性签名:用hashsigs-ts讲透后量子密码核心原理
【免费下载链接】hashsigs-tsHash-based signatures in typescript, WOTS+项目地址: https://gitcode.com/gh_mirrors/ha/hashsigs-ts
hashsigs-ts 是一个用 TypeScript 实现的后量子密码哈希签名库,完整实现了 WOTS+(Winternitz One-Time Signature Plus)一次性签名算法。如果你一直听说"量子计算机要破解现有密码",却想真正搞懂基于哈希的签名到底靠什么抗量子,这篇文章就是你的入门指南——不堆公式,用这个只有 400 行核心代码的开源项目,把原理一次讲透。
为什么后量子密码里有个"土办法"?
大多数数字签名(RSA、ECDSA)的安全性建立在"大数分解""椭圆曲线离散对数"这类数学难题上。而量子计算机上的 Shor 算法恰好能高效解决这些难题——这是"后量子密码(PQC)"诞生的原因。
但还有一个"土办法"完全绕开了这些难题:哈希函数(hash-based signatures)。它的安全性只依赖一件事——哈希函数是单向的:
给定输出,几乎不可能反推出输入。
这件事连量子计算机都很难加速破解,所以基于哈希的签名被公认为后量子密码里理论基础最扎实的方案,也是 NIST PQC 标准化中 XMSS/XMSS^MT(RFC 8391)的底层技术。
而 WOTS+ 就是这类算法的"最小可用单元"——名字里的One-Time(一次性)是它最大的特点,也是理解它的关键。
WOTS+ 核心原理:一条"只能往前走"的链
哈希链:单向门的核心
想象一条链条,每一环都是上一环做哈希的结果:
sk → H(sk) → H(H(sk)) → H(H(H(sk))) → ... → 终点- 私钥:链条的起点
sk - 公钥:链条的终点(公开,任何人都知道)
- 签名:消息告诉你"从第几环出发",你就把那一环的值交出去
验证方拿到签名值后,沿着链继续做哈希,直到链条终点,看是否等于公开的公钥。对上了,说明签名者确实知道链条起点(私钥)。
妙处在于:从第 5 环你无法反推出第 3 环,所以提前拿到某个中间值,等于什么都没拿到。
"一次性"从何而来?
问题在于:如果你用同一个链条给两条消息签名,交出了第 3 环和第 4 环,攻击者就能从第 3 环出发走到终点,再一路"倒退"逼近起点,最终复原私钥。
⚠️ 所以 WOTS+ 的公钥只能对一条消息签名一次,这就是 One-Time 的由来。
两条增强:"+" 和 Checksum
WOTS+ 相比原始 WOTS 做了两处加固:
| 增强 | 做法 | 目的 |
|---|---|---|
| Checksum(校验和) | 把"每个链用了多少步"的补数做校验和,追加到签名末尾 | 防止攻击者篡改消息让链"滑步",窃取更早的链节点 |
| 随机化元素 | 用公开种子派生出一组随机掩码,与链上每个节点 XOR 后再哈希 | 防止"任意状态"攻击,让每条链的推进路径都不同 |
在 hashsigs-ts 中,这两处分别实现在 checksum 校验和计算 和 generateRandomizationElements 随机化元素生成。
走进 hashsigs-ts:结构极简,参数讲究
项目结构非常清爽,核心逻辑就一个文件:
- 核心算法:src/wotsplus.ts ——
WOTSPlus类,约 400 行 - 对外导出:src/index.ts —— 只导出
WOTSPlus类和HashFunction类型 - 单元测试:src/wotsplus.test.ts
- 测试向量:test/test_vectors/wotsplus_keccak256.json(含 Keccak-256 公钥分段、签名、随机化元素的完整十六进制数据)
关键参数:w = 16 的取舍
WOTS+ 有一个调参核心w(源码中叫 chainLen,遵循 XMSS RFC 8391 只允许 4 或 16)。它决定把消息按 4 进制还是 16 进制切片,每片对应一条哈希链:
| 参数 | w = 4 | w = 16 |
|---|---|---|
| 链的数量(32字节哈希下) | 128 条 | 64 条 |
| 签名大小 | 更大 | 更小 ✅ |
| 签名耗时 | 更快 ✅ | 更慢 |
| 安全性 | 更高 | 略低 |
默认w = 16+ Keccak-256 时,构造函数 自动算出:64 条消息链 + 3 条校验和链 = 67 个签名块,签名 2144 字节、公钥仅 64 字节。这就是哈希签名的经典特征:公钥极小、签名较大——非常适合签名一次、验证百万次的场景。
三步用起来的完整流程
hashsigs-ts 的 API 只有三个方法,与原理一一对应:
import { WOTSPlus } from 'hashsigs-ts'; import { keccak_256 } from '@noble/hashes/sha3'; const wotsPlus = new WOTSPlus(keccak_256); // ① 选哈希函数(w 默认 16) const { publicKey, privateKey } = wotsPlus.generateKeyPair(privateSeed, publicSeed); // ② 生成密钥对 const signature = wotsPlus.sign(privateKey, publicSeed, messageHash); // ③ 签名 wotsPlus.verify(publicKey, messageHash, signature); // ④ 验证,返回 true/false对应源码入口:generateKeyPair 生成密钥对、sign 签名、verify 验证。
三个容易踩的坑(源码中都有显式校验):
- 消息必须是 32 字节——因为签名前先把消息哈希成固定长度(见 messageLen 注释),任何长度都先过一遍哈希;
- 公钥 = 32字节公开种子 + 32字节公钥哈希,种子用来重建随机化元素,所以验证方不需要额外状态;
chainLen必须是 2 的幂且为 4 或 16,否则会直接抛错(见 validateParameters)。
测试向量:跨语言对拍的依据
test/test_vectors/wotsplus_keccak256.json 里存放了其他语言实现产出的密钥/签名/随机化元素,测试用例 会逐条用verify和verifyWithRandomizationElements复算,保证 TypeScript 实现与参考实现字节级一致——这也是理解每条链结构最直观的"教材":publicKeySegments数组正好 67 个 32 字节分段,与参数表完全吻合。
WOTS+ 的短板,谁来补?
诚实地说,WOTS+ 有两个工程痛点:
- 一次性限制:签一条消息就要换一套公钥,实际系统不可能手动换;
- 签名较大:2144 字节对某些场景偏重。
标准答案是分层树结构(XMSS / XMSS^MT):把树每一层的节点都用 WOTS+ 签名,父节点公钥覆盖所有子节点,通过"路径"实现一次性使用与公钥验证。可以这样理解两者关系:
WOTS+ 是砖,XMSS 是用它盖的房子。hashsigs-ts 专注把"砖"做到位,树结构留给你按需搭建。
另外,由于私钥只是种子哈希、状态极简,这类签名天然适合智能合约、嵌入式设备、浏览器前端等资源受限环境——这也正是 TypeScript 实现的实用价值。
写在最后:30 分钟复盘
用一张图串起全文:
消息 (任意长度) │ Keccak-256 哈希 ▼ 32 字节消息哈希 │ 按 16 进制切成 64 片 + 3 片校验和 ▼ 67 条哈希链,各取第 x_i 环 → 2144 字节签名 │ 验证:沿链走到终点,比对 64 字节公钥 ▼ 信任 ✅一句话总结:WOTS+ 用"哈希链只能往前走"的单向性换取了对量子的免疫,用"校验和 + 随机化"堵住了篡改漏洞;代价是一次性使用和较大的签名。而 hashsigs-ts 用 400 行代码把这三件事写得一清二楚——读懂 src/wotsplus.ts,你就拿到了后量子密码哈希签名路线的第一把钥匙。🔑
- 项目说明:README.md
- 构建与测试配置:tsup.config.ts、vitest.config.ts
- 许可协议:COPYING(AGPL-3.0-or-later)
【免费下载链接】hashsigs-tsHash-based signatures in typescript, WOTS+项目地址: https://gitcode.com/gh_mirrors/ha/hashsigs-ts
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考