八、一次性签名方案
一次性签名(OTS)方案是由三种算法组成的:一种用于生成一次性密钥对,一种用于计算一次性签名,还有一种用于签名验证。一个OTS方案的实例有一个特定的密钥对(P,S),其中P是公钥,S是私钥。
OTS方案和Merkle树(如下所述)都使用哈希函数。一个重要问题是,同一哈希函数是否可以安全地用于这两种结构。事实上,通过在每个哈希中包括一点额外的数据(这被称为域分离domain separation),我们基本上可以把一个散列函数当作许多不同的散列函数。换句话说,如果我们使用SHA-256
来生成OTS实例,我们仍然可以安全地使用SHA-256
来构建Merkle树。
多次或完整,基于哈希的签名方案使用哈希树来有效地结合OTS方案的许多实例。
九、基于哈希的密码学是如何工作的?
我们现在将讨论Merkle如何使用二进制树-如图1所示的二进制树-结合许多个OTS来创建一个基于哈希的多次签名方案的公钥。虽然从这些OTS中构建树的初始步骤(可以离线完成)与许多其他抗量子的构建相比通常很慢,但签名却很快。
十、二叉树
在一棵标准的二叉树中,所有的节点(除了最上面的节点)都是成对出现的,它们上面有一个节点,从最下面的节点到最上面的节点的距离总是相同的。另一个节点的正上方是其父节点,父节点的正下方是其子节点,一对具有相同父节点的子节点被称为兄弟姐妹节点。例如,在图2和图3中,N(1,0)
和N(1,1)
是兄弟姐妹节点。它们也是N(2,0)
的子节点;也就是说,N(2,0)
是它们的父节点。
最上面的节点被称为根节点。树的底部没有子节点的节点被称为叶节点。叶节点表示为L0, ....
,图2中的L7
。
一个节点的级别是它与底部的距离。我们的意思是叶子节点有0级,图2中的节点N(j,i)
有j级。根节点的级别,通常表示为h
,称为树的高度。例如,图2中的树的高度为3。Merkle使用二进制树来组合OTS,更具体地说:每个叶子节点来自一个(不同的)OTS实例的公钥,而树上的每个其他节点都是由它的两个子节点计算出来的。我们现在将描述这些节点是如何使用加密散列函数计算的。
十一、加密哈希函数(Cryptographic Hash Functions)
简单地说,加密哈希函数H是一个将任意数量的数据映射到一个合理的、通常是固定长度的输出的函数,在这种情况下,实际上不可能找到一个映射到特定输出的输入(或两个给出相同输出的输入)。
直观来讲,我们可以认为默克尔树是使用哈希函数将一个有序的数值集压缩成一个单一的数值,其方式是很容易证明一个数值属于原来的数值集。更具体地说,Merkle树可以从O的公钥的一个有序集合P0 ...Pm
的OTS的公钥和哈希函数H,以如下方式构造:
-
每个叶子节点是一个OTS公钥的哈希输出。换句话说,让底部一行的第i个条目为
L(i) = H(P(i))
;见图2。 -
树上的每一个其他节点都是其两个子节点的哈希值(连接)。例如,如图3
N(1,0) = H(L(0) || L(1)) and N(2,1)= H(N(1,0) || N(1,1) )
通用表达如下:
N(1,i)= H(L2(i) || L2(i+1)) and N(j+1,i)= H(N(j,2i) || N(j,2i+1)))
Merkle签名算法的公钥是根节点。在图2中,根节点(公钥)是N(3,0)
哈希树是Merkle树的一个概括,其中P(i)
是任意数据而不是OTS公钥,见图2。
由于你无法找到哈希函数的逆运算,所以实际上不可能从树中较高的节点中找到树中较低的节点。因此,给定树中的任何一组节点,特别是给定根节点,都不可能找出关于OTS签名钥匙的信息。
十二、验证路径
请注意,对于任何用于创建Merkle树的P(i)
,都有一条从叶子节点L(i)
到根节点(公钥)的唯一路径。例如,在图4中,从L(2)
到顶部的路径是用红色画的。给定P(i)
,如果你能构建一个这种形式的路径,那么这几乎可以肯定地证明P(i)
是用来创建Merkle树的值之一。这源于这样一个事实,即找到具有特定输出的哈希函数H的输入在计算上是不可行的,因此你不能从树中较高的节点找到树中较低的节点。
然而,我们实际上是利用叶子到根的路径节点的同级节点来检查路径是否被合法地构建。出于这个原因,我们引入了P(i)
的认证路径的概念,即从L(i)
到根节点的路径中的兄弟节点的有序集合。在图4中,P(2)
的认证路径(蓝色)是L(3), N(1,0), N(2,1)
。给出P(i)
(一个OTS的公钥),以及P(i)
的认证路径,我们可以验证(认证)P(i)
对应的是一个叶子节点。也就是说,如果P(i)
确实被用于生成树,那么鉴于认证路径,重建从P(i)
到根节点的路径应该很简单。
参照图4,我们可以证明P(2)
被用来创建Merkle树的公钥,只需给它的认证路径(蓝色)L(3), N(1,0) , N(2,1)
,通过构建一个从P(2)
到根节点(公钥)的路径。
要做到这一点,我们只需检查值:
H(P(2)),
H(L(3) || H(P(2))),
H(N(1,0) || H(L(3) || H(P(2)))),
H(N(2,1) || H(N(1,0) || H(L(3) || H(P(2)))))
给出一个路径,其中最后一个值H(N(2,1) || H(N(1,0) || H(L(3) || H(P(2)))))
是多次签名算法的公钥(即根节点)。由于P(2)
实际上是用来构建Merkle树的,所以构建H(N(2,1) || H(N(1,0) || H(L(3) || H(P(2))))) = N(3,0)
。
如果上述计算得到了公钥,那么我们就证明了P(2)
是最初用于创建哈希树的OTS密钥之一(因为找到一个非法的认证路径需要解决寻找哈希函数的反图像这一计算上难以解决的问题)。
十三、基于状态哈希的签名方案一览
多次方案的一般构造总结如下。
-
密匙生成(Secret Key Generation)
创建
m = 2^h
个OTS公私钥对(Pi, Si)
。直观地讲,我们可以认为多次秘钥(many-time scheme)是生成OTS密钥对所需的材料。 -
公钥生成(Public Key Generation)
为
P1,......,Pm
创建如上所述的哈希树,根节点是基于哈希的签名方案的公钥。 -
签名(Signatures)
为了签署一个信息,选择一个以前从未使用过的索引i。用
Si
(OTS签名密钥)对消息进行签名,得到一次性签名,并计算出Pi
的认证路径。该信息的签名是一次性签名以及Pi
的认证路径。 -
验证(Verification)
为了验证一个消息的签名,我们首先使用消息和运行一次性验证方案。接下来,检查
Pi
的认证路径是否提供了一个从Pi
到基于哈希的签名的公钥(根节点)的有效路径。如果是这样,则接受该消息和签名为真实的。 -
时间/空间的权衡(Time/Space Tradeoffs)
由于树可以从
P1 ...Pm
生成,存储整个树并不总是必要的。决定存储多少树以及如何管理树,会导致各种CPU/内存等资源消耗的权衡。此外,所有的密钥P1...Pm
也可以从一个单一的短种子再生,进一步减少所需的长期存储量。 -
签名的数量(Number of Signatures)
如果树的高度是
h
,那么它可以用来签署多达2^h
的信息。 -
有状态的签名(Stateful Signatures)
由于每个OTS签名密钥最多只能使用一次,在一个有状态的基于哈希的签名方案中,跟踪哪些一次性密钥对被使用是很重要的。
参考文献:
[1] David Cooper, Daniel Apon, Quynh Dang, Michael Davidson, Morris Dworkin, and Carl Miller. Recommendation for stateful hash-based signature schemes. Technical report, National Institute of Standards and Technology, 2019.
[2] Andreas Hülsing et al. SPHINCS+. NIST Round 2 Submissions for Post-Quantum Cryptography Standardization, 2019.
[3] A Hülsing, D Butin, S Gazdag, J Rijneveld, and A Mohaisen. XMSS: eXtended Merkle Signature Scheme. Crypto Forum Research Group RFC. rfc-editor.org/info/rfc8391, 2018.
[4] David McGrew, Michael Curcio, and Scott Fluhrer. Leighton-Micali hash-based signatures. Crypto Forum Research Group RFC. rfceditor. org/info/rfc8554, 2019.
[5] Ralph Merkle. Secrecy, authentication, and public key systems. Ph. D. Thesis, Stanford University, 1979.