Skip to content

返回学习路线

HASH-6:证明一个值,再认证一份公钥 ​

本任务给出六个独立但相互连接的实验。完成后,你应能自己验证一个位置的值、减少多个位置的共同证明、证明固定键处为空,再解释一次性签名为什么能验证、为什么不能随意复用,以及认证树如何把一批一次性公钥接到同一个根。

输入、约定与交付 ​

主线先读认证树和Lamport一次性签名,再组合成Merkle树签名。树侧的两条分支是多重证明与稀疏空叶证明;链侧是Winternitz校验和。

交出六份记录:认证路径坐标与哈希、共享证明前沿、空叶更新前后根、Lamport揭示矩阵、Winternitz四条链步数、树签名索引状态轨迹。每份都要指出验证依赖的可信输入,以及改变哪一条条件会使结论失效。

以下固定H为SHA-256,摘要32字节;u32为4字节大端整数,u8为1字节。标签写成十六进制字节,不是ASCII字符串。例如10表示单个字节0x10。符号“拼接”始终拼接原始字节,不能拼接摘要的十六进制文本。

教学秘密通过公开标签确定性产生,任何人都能重算。它们只用于核对算法与攻击轨迹,不是安全密钥生成,也不是标准LMS、LM-OTS或WOTS+实现。安全定理另外假设独立秘密采样及相应困难函数族。

任务一:索引2的值C ​

列表是8个单字节ASCII值A至H。叶编码为 00 || u32(i) || u32(长度) || 值,内部高度j的编码为 01 || u8(j) || 左摘要 || 右摘要。高度从叶0开始。可信参数h=3及根为:

text
f024263693113d02f6123e0261af76188412417fc377c1360bfcac0413b59de7

请证明索引2处为C。其叶输入字节是 00 00000002 00000001 43。认证路径依次提供:

text
T(0,3) = 02b580d3b39e9b8cb3df816a1dc5b579d4a4d172815a845c3be8e1f057a1e8d8
T(1,0) = d4795c8480e8ee6b644be7acd316c0ceea800c668f48aaec92451094856f9472
T(2,1) = 2c3c09a57cd1abff8bad84595ac7c5aae19e7e63f9accf3bcbf57be6b454ff9a

核对。 2的二进制最低位依次为0、1、0,因此当前值依次在左、右、左;算出CD、ABCD和根。共1次叶哈希与3次父哈希,路径含3个摘要。改索引、逆转路径或改变高度都应拒绝。

迁移。 索引5的兄弟为(0,4)、(1,3)、(2,0),当前值方向为右、左、右。若值F改成f,只改该叶与3个祖先。若攻击者把根也一并换掉,新的自洽路径并不能认证原发布者的列表。

任务二:同时证明B、C、G ​

目标索引为{1,2,6}。请逐层写已知节点与还缺的兄弟,不直接拼接三份单叶路径。

核对。 第0层已知{1,2,6},需{0,3,7};第1层已知{0,1,3},需{2};第2层已知{0,1},不再需要外来摘要。最终只传A、D、H与EF子树,共4个摘要,替代三份路径的9个摘要。实际验证是3次叶哈希与6次父哈希,共9次,不是4次。

迁移。 对{2,3}只需(1,0)、(2,1),共2个摘要、5次哈希;对全部8叶,外来摘要为0,仍需15次哈希。解释为什么重复坐标、覆盖本地已算目标、缺失项与未使用尾随项都应被规范验证器拒绝。

任务三:键010不存在 ​

换一棵独立的三位键映射树。空叶为H(02),占用叶为 H(03 || u32(键) || u32(值长度) || 值),内部为 H(04 || u8(高度) || 左 || 右)。初始只有 001→red 与 110→blue。

核对。 010的非成员路径从空叶开始,兄弟为011空叶、000至001子树、100至111子树。根为:

text
61ca45696ba490a5073adadb861cecc97ea7afa1e8584d25c45c70d851364d6c

共有7个非默认显式节点,另外保存4个默认摘要。插入 010→green 后新根为:

text
7bfb47ea9d196ef40ac6ee61bfc40bd70ad7f18370ca1dba801377b33400808e

旧空叶证明不能通过新根;删除该键则恢复旧根。先前根和新根的时间顺序,需要外部版本或历史合同,不由单个内容摘要证明。

迁移。 键存在但值为零长度字节串,仍使用03标签,绝不是02空叶。再证明011为空,兄弟坐标应为(0,2)、(1,0)、(2,1)。普通无序列表里没看见某个值,不能据此提交相同的非成员证明。

任务四:Lamport的公开与未公开两侧 ​

用4列、每列两份独立秘密的抽象模型签1010,函数实例为 F(x)=H(10||x)。教学夹具按 H(f0 || u32(namespace) || u32(i) || u8(b)) 生成每项,主例namespace=0。请先画出选中的列侧,再读下载结果中的完整字节。

核对。 揭示(0,1)、(1,0)、(2,1)、(3,0)。公钥8项共256字节,签名4项共128字节,验证做4次F。若故意再用同钥签0000与1111,两份签名暴露全部8项,拼出0101即可伪造未请求的新消息,无需反演。

迁移。 只拿到0011与0101的签名,两者在两列不同,可拼四条消息,其中0001与0111是新的。对比只有一列不同的情况,说明不能夸称任意两次查询都立刻产生同一种拼接攻击。最后写出一查询归约的2ℓ猜测空间,并指出为什么嵌入的挑战原像不能出现在需要回答的旧消息那一侧。

任务五:校验和怎样拦住前推 ​

取b=4、t=2,经典链函数为 f(x)=H(20||x)。消息[1,1]的校验和为4,固定两位表示[1,0],完整数字为[1,1,1,0]。

核对。 四条签名链分别前进1、1、1、0步,验证再前进2、2、2、3步;公钥生成12次f,签名3次,验证9次。若无校验和,第一链再算一次就能把消息改成[2,1]。有校验和时新完整向量为[2,1,0,3],第三链必须从1退到0,不能只由继续计算现有链点得到。

迁移。 [0,3]的完整向量为[0,3,0,3],签验各6次;[3,3]仍须带两位[0,0]校验和。给出“消息坐标无下降则补数和严格减小”的证明,再说明这只排除逐坐标前推,并没有证明普通单向函数在其迭代输出分布上仍单向。

任务六:一次失败也要保留索引消耗 ​

用四对不同Lamport教学密钥,namespace依次为100、101、102、103。把每份公钥按 u32(4) 后逐列0侧、1侧的顺序串联,作为四叶认证树的值。可信h=2、ℓ=4,根为:

text
6e4304b913204768203ceb208683f6b2ae8566b4f43d931e297f988a121be2ff

请求依次为:签1010;签1111但保留索引后失败;签0101;签1100;再签0000。请交出每步next以及实际返回索引。

核对。 返回索引为0、2、3,1烧掉,最终next=4,末次请求因耗尽而拒绝。这个行为模拟“原子且不可回滚的保留”合同;检查器并没有在真实掉电、并发或恢复备份时实现该合同。

每份签名同时带叶公钥、一次性签名及2个兄弟摘要。按4字节索引和4字节公钥位数计,共456字节;验证共7次底层函数/哈希,长公钥叶输入的字节成本另计。接收者可以重复验证同一份签名;这不等于签名者可以重复使用同一索引签新消息。

实际反例。 故意把next回滚为0,在同一叶先后签0000和1111,拼出0101并附原路径。完整树验证仍接受,说明认证树不修复OTS复用。安全归约的两条出口是“另一公钥打开同位置造成树碰撞”和“真实公钥下的新消息造成OTS伪造”,后一条依赖该叶只被查询一次。

运行与证据范围 ​

下载标准库检查器与完整JSON结果,运行:

sh
python3 foundations-hash-signatures-checker.py
python3 -O foundations-hash-signatures-checker.py

两种模式输出相同;检查使用显式异常,不依赖可被-O禁用的assert。程序不读取网络或真实密钥,不修改机器安全设置,所有状态都在内存中。

除主例外,脚本检查高度0至5的63条认证路径、8叶全部255个非空多重证明集合、三位键全部256个占用图的2048个查询、4bit Lamport的256个消息/签名匹配项、Winternitz的240个不同消息向量对,并执行重复索引伪造与12项结构错误拒绝。Lamport匹配矩阵是彼此独立的假想单次验证测试,不是一个允许安全复用同钥16次的签名工作流。

这些有限测试核对实现和具体反例。抗碰撞、单向、迭代单向等条件仍是各正文明确列出的密码假设,公开确定夹具没有任何实用保密性。