第一集 哈希签名始祖 Lamport 方案及其改进版本(Merkle,Winternitz)
第二集 哈希签名树 Merkle Tree Authentication
第三集 HORS Hash to Obtain Random Subset 2002、DBS算法 2008
第四集 GMSS、XMSS、Multi Tree XMSS
第五集 SPHINCS 2015 SPHINCS+ 2019(完结撒花)
一、为什么提出 Tree Authentication
在第一集中,我们一直忽略了一个问题。
设想一下,假如有一个Hacker(黑客),在截获到Sender发送给Receiver的公钥之后,把这个公钥改成Hacker自己的公钥,然后再发给Receiver,那么Receiver就会误认为这是Sender的公钥。
所以我们之前所说放方案,其安全性都需要建立在一个前提下:Receiver需要确认公钥确实是Sender发出的。在古代,这里确认的方法就是两人当面确认,相当于Sender当着Receiver的面签上自己的名字,这样Receiver才能确认这是Sender的字迹。而在数字时代,这里常见的方法就是数字证书,这里就暂时不详细讲解什么是数字证书。
按照之前的算法,每发一条消息,都要发送一个公钥,就要确认一次(两人就要见面一次)。那为什么不直接在两人见面的时候传输信息呢?弄这个签名机制不是多此一举吗?
对此,Merkle就想出了一种方法,只需要确认一次,就能传输多个信息。
二、Merkle Tree
Merkle 在1990年《A CERTIFIED DIGITAL SIGNATURE》中的第六节“6. Tree Authentication”提出了这个方案。
基于上面提到的问题,Merkle想到了用“树”这个结构,把所有的“要确认的公钥”合成一个“要确认的公钥”。
Mekle在论文中还说了:除了哈希签名这个领域以外,Merkle Tree还可以应用到各种各样的验证方法中。
2.1 Merkle Tree 在哈希签名应用中的 全流程
-
确认要发送的消息:Sender需要发送N条消息(N一般为2的正整数次幂,如2,4,8,16……),这个N条消息已经确认了。产生N个私钥 x i , i ∈ [ 1 , N ] x_i, i \in [1,N] xi,i∈[1,N],每个私钥 x i x_i xi都经过哈希函数得到对应的公钥 y i = H ( x i ) y_i=H(x_i) yi=H(xi)
-
构造Merkle树:所有的公钥 y i y_i yi构成叶子节点,两个相邻的叶子节点之间用某种方法“结合”后,再次经过哈希函数,得到它们两个的父节点。这里的“结合”有很多种方法,有时候是使用“或”操作,有时候是先用“或”操作,再跟一个随机数进行“异或”操作,如下图:
最终通过计算我们可以得到根节点的值。 -
Receiver确认阶段:这个环节是新增的,在第一集中是被我们忽略掉的。Sender“当面”与Receiver确认整个树的根节点的值。Receiver记下这个值,命名为root。完毕。
-
Sender发送信息:Sender需要给Receiver传输三样东西。整颗树(叶子节点就是公钥),私钥(也就是签名,因为我们目前讲到的所有哈希签名方案都是把私钥当作签名的),消息(massage)。而这个传输过程不需要保证“当面”。也就是说,即使Hacker(黑客)在这个传输过程中,截取并且修改里面任意一项,Receiver都可以发现内容被篡改。
-
Receiver接收并验证:Receiver从根节点开始验证。首先,确认根节点下面的两个子节点是否正确。对这两个字子节点进行“结合”后经过哈希函数,得到一个哈希值。如果这个哈希值就是之前和Sender当面确认的root,说明这两个字节点是Sender发送过来的;否则说明这两个字节点被人篡改过。后面的节点以此类推,直到验证完整颗树。
2.2 Merkle Tree的安全性
如上图所示:
假设Hacker截获了Sender发送的内容,试图篡改其中的第6条信息
x
6
x_6
x6,同时也要修改对应的
y
6
y_6
y6,然后将这个篡改后的内容发送给Receiver。注意,Hacker只篡改了
y
6
y_6
y6这个叶子节点,树上的其他地方都没有修改
Receiver收到后,开始验证。按下面的顺序验证:
N1,3等于root,N1,3验证完毕。
将N2,2和N3,2“结合”后,经过哈希函数,得到的哈希值,等于N1,3。N2,2和N3,2验证完毕。(也就是可以确认N2,2和N3,2是Sender本人发送过来的)
N4,1和N5,1,N6,1和N7,1,y1和y2,y3和y4同上,验证完毕。
到此为止,Receiver认为,上面提到的所有节点,都是Sender发送过来的,事实也是如此。
验证y5和y6,将y5和y6“结合”后,经过哈希函数,得到的哈希值。Receiver发现,这个哈希值不等于N6,1,他认为,y5和y6中至少有一个消息不是Sender发来的。
这样Receiver就发现了被篡改的部分。
我们再思考这种情况:
如果N6,1也被Hacker篡改了呢,Hacker重新计算y5(Sender)和y6(Hacker)得到的哈希值,把N6,1改成这个哈希值。
显然,Receiver在用N3,2验证N6,1和N7,1的时候,发现N6,1和N7,1计算出来的哈希值不等于N3,2,就会发现,N6,1和N7,1至少有一个被篡改过。
综上所述,整个树只要有一个地方被修改过,都会被发现出来。要传输完整的正确的树,只有Sender能做到。
这就保证了该算法的安全性。
2.3 Merkle自己的一些改进
在验证的时候,我们不需要每个叶子节点都从根节点开始验证,可以使用我们刚刚用到的方法逐层验证,或者是按照massage块的顺序,使用回溯法进行验证(Merkle的方法)
从根节点开始验证
按照massage块的顺序,使用回溯法进行验证
三、一些疑惑和解答
在学习Merkle Tree的时候,我对此方案感到很疑惑:此方案没有减小公钥和私钥的大小,反而新增了一个数结构,使得传输的信息更多了。验证的速度也没提升。
那这个算法到底有什么优点呢?
有的文章说“这种方法可以让一个公钥使用多次,再也不是一次性的签名了”。但我觉得这种说法很具有误导性,其实哈希签名算法走出“一次性”这个阶段不是在Merkle这里实现的(在第三集中会讲到真正的可以多次使用的哈希签名方案)。我理解的多次使用的方案是,在你发送了消息过后,还可以使用之前的密钥对来进行签名,比如RSA算法。显然Merkle Tree不满足这个条件。因为在你计算root之前,就需要把所有叶子节点的信息都确认下来,你可以分多次传输这些信息,但是你不能再次使用这些密钥对来对新的信息进行签名。因此这种方案是一次性的签名方案。
你可能还会看到有的文章会说,这种方法减小了公钥的大小。你会感到很疑惑:公钥不还是那些叶子节点吗,这些叶子节点还是要发送给Receiver的呀,并没有减少,反而因为树结构的加入使得整个内容变大的,我们可以把整个树看作是公钥,那么公钥的大小应该是增大了才对啊。
其实这种说法所说的“公钥”不是叶子节点的公钥,而是Receiver和Sender要“当前”确认的公钥,就是root。
现在你应该明白了这个算法的意义所在了。
现在回过头来再看看“Merkle树在哈希签名方案中具有的优势”你就能明白了。
Merkle树在哈希签名方案中具有以下几个优势:
-
效率:Merkle树可以高效地验证大量数据的完整性。通过将消息分割为固定大小的数据块,并使用哈希算法计算每个数据块的哈希值,可以快速生成树的内部节点和根节点的哈希值。这样,在验证消息的完整性时,只需要比较少量的哈希值而不是整个消息,从而提高了效率。
-
安全性:Merkle树可以提供强大的数据完整性保护。根节点的哈希值代表了整个数据集的完整性,即使只有一个叶子节点的数据发生了改变,也会导致根节点的哈希值完全不同。因此,通过比较根节点的哈希值,可以有效地检测到数据是否被篡改。
-
紧凑性:Merkle树可以将大量数据以紧凑的方式表示。通过使用哈希值作为叶子节点,Merkle树可以将数据集的完整性表示为一个相对较小的哈希值。这对于在网络传输中发送和存储数据非常有用,可以节省带宽和存储空间。
这里说到的“只需要比较少量的哈希值” “可以节省带宽和存储空间”都是在说Receiver和Sender需要“当前”确认的公钥。
如果还是用第一集中讲到的方法,每个叶子节点都需要Receiver和Sender“当前”确认。需要注意的是,这个“当面”我一直加了引号,因为我们在数字时代,不能做到真正的当面,只能够通过“数字证书”等方法来确认。这些确认方法一般是通过多次签名方案来传输这个root(公钥)的,所说就涉及到了“带宽和存储空间”。
这样一对比就能明白这个方案的意义所在。
四、Merkle Tree真正发挥作用的地方
我们现在所说的Merkle Tree,不能在“传输阶段”把公钥压缩。根本原因有两个:
- 要把所有的叶子节点都公布,才能得到最后的根节点来进行验证。
- 有没有一种办法,只公布一个叶子节点,最后也能计算出根节点呢?即使有,也没有必要这么做,因为既然所有的叶子节点都已经是对消息进行了签名了的,每次公布几个和全部都公布并不会改变被签名了的消息,没有什么实质性的影响。
到了后面第三集讲解的树的DBS算法和HORS方案,分别解决了第1点和第2点。
-
树的DBS算法使得无需公布所有的叶子节点,就能计算出根节点,通过公布“验证路径”的方法来计算出根节点。
-
HORS方案是革命性的方案,它让哈希签名从OTS变成了可以实现FTS,而且公钥的产生不再依赖于消息。HORS可以先产生公钥,公布出去,然后再用这一个公钥多次签名多条不同的消息。这就给了我们每次只公布一个叶子节点的理由,我们可以每签名一条消息,就公布一个叶子节点,而且由于公钥是不变的,因此树的根节点也不会改变,还是在同一颗树下,解决了第2个问题。
有了HORS和DBS的加入,使得Merkle Tree公钥可以真正的压缩成只有一个根节点。