基于哈希函数的签名,Part-1

回首近几年,我有幸经历了两个相互冲突、却又使人着迷的时代潮流变迁。第一个潮流变迁是:专家学者们耗费四十年设计的密码学,终于派上用场;从信息加密、电话安全、到加密数字货币,咱们能够在生活的方方面面发现使用密码学的例子。算法

第二个潮流变迁是:全部密码学家已经作好准备,迎接以上美好的幻灭。数组

正文开始以前我得重申一下,本文所讲的不是所谓量子计算启示录(末日预言),也不是要讲 21 世纪密码学的成功。咱们要谈论的是另外一件未成定局的事情——密码学有史以来最简单的(也是最酷炫的)技术之一:基于散列函数的签名。安全

在 20 世纪 70 年代末,Leslie Lamport 发明了基于哈希函数(Hash Function,又称散列函数)的签名 ,并通过 Ralph Merkle 等人进一步改进。然后的不少年,这被视为密码学领域一滩有趣的“死水”,由于除了相应地产生冗长的(对比其余复杂方案)签名,基于哈希函数的签名好像没有什么做用。然而近几年来,这项技术彷佛有了复苏的迹象。这很大程度归因于它的特性——不一样于其余基于RSA或离散对数假设的签名,哈希函数签名被视为能够抵抗量子计算攻击(如 Shor’s 算法)。函数

首先,咱们进行一些背景介绍。工具

背景:哈希函数和签名方法

在正式介绍哈希函数签名以前,首先你得知道密码学中的哈希函数是什么。哈希函数能够接受一串字符(任意长度)做为输入,通过“消化”后,产生固定长度的输出。常见的密码学哈希运算,像是 SHA二、SHA3 或 Blake2 等,经运算会产生长度介于 256 ~ 512 位的输出。优化

一个函数 H(.) 要被称做“密码学”哈希函数,必须知足一些安全性的要求。这些要求有不少,不过咱们主要聚焦在如下三个方面:加密

  1. 抗-原像攻击 Pre-image resistance (或俗称“单向性”):给定输出 Y=H(X),想要找到对应的输入 X 使得 H(X)=Y 是一件“极度费时”的工做。(这里固然存在许多例外,但最棒的部分在于,不论 X 属于什么分布,找到 X 的时间成本和暴力搜寻相同。)
  2. 抗-次原像攻击:这和前者有些微的差异。给定输入 X,对于攻击者来讲,要找到另外一个 X’ 使得 H(X)=H(X’) 是很是困难的。
  3. 抗-碰撞:很难找到两个输入 X1, X2,使得 H(X1)=H(X2)。要注意的是,这个假设的条件比 抗-次原像攻击还要严苛。由于攻击者能够从无垠的选择中寻找任意两个输入。

咱们相信全部本文提到的哈希函数示例都能提供上述的全部特性。换言之,没有任何可行的(甚至是概念上的)方法能破解它。固然这种状况也是会变的,若是破解的方法被找到,咱们固然会当即停用哈希函数(稍后会讨论关于量子计算攻击的特例)。翻译

咱们的目标是使用哈希函数构造数字签名方案,所以简要回顾数字签名这个词能带来很大的帮助。设计

数字签名方法源于公钥的使用,使用者(签署人)生成一对密钥:公钥和私钥。使用者自行保管私钥,并可以用私钥“签署”任何消息,从而产生相应的数字签名。任何一个持有公钥的人都能验证该消息正确性和相关签名。cdn

从安全的角度来讲,咱们但愿签名是不可伪造的,或是说“存在不可伪造性”。这意味着攻击者(没有私钥控制权的人)没法在某段消息上伪造你的签名。有关数字签名安全的更多定义请参阅这里。

Lamport 一次性签名

在 1979 年,一位名叫 Leslie Lamport 的数学家发明了世界上第一个基于哈希函数的签名。Lamport 发现只要使用简单的哈希函数,或是单向函数,就能够构建出很是强大的数字签名方法。

强大的前提是,用户只须要作一次签名的动做就能保证安全性!后续会作更详细的阐述。

为了更好的讨论,咱们假设如下条件:一个哈希函数,它能接受 256 位的输入并产生 256 位的输出; SHA256 哈希函数就是个绝佳的示范工具;咱们也须要能产生随机输入的方法。

假设咱们的目标是对 256 位的消息进行签名。要获得咱们须要的密钥,首先须要生成随机的 512 个位字符串,每一个位字符串长度为 256 位。为了便于理解,咱们将这些字串列为两个独立的表,并以符号代指:

sk0= sk10, sk20, …,sk2560

sk1= sk11, sk21, …,sk2561

咱们以列表 (sk~0~, sk~1~) 表示用来签名的

密钥
。接下来为了生成公钥,咱们将随机的位字符串经过 H(.) 进行哈希运算,获得公钥以下表:

pk0= H(sk10), H(sk20), …,H(sk2560)

pk1= H(sk11), H(sk21), …,H(sk2561)

如今咱们能够将公钥 (pk~0~,pk~1~) 公布给全部人知道。好比说,咱们能够把公钥发给朋友,嵌入证书中,或是发布在 Keybase 上。

接着咱们使用密钥对 256 位消息 M 进行签名。首先咱们得将消息 M 重现为独立的 256 位元(Bit,又称“比特”):

M1, M2, …, M256 ∈ {0, 1}

签名算法的其他部分很是简单。咱们从消息 M 的第 1 位至第 256 位,逐一相应在密钥列表中的其中一个密钥上取出字符串。而所选密钥取决于咱们要签名的消息每一位(bit)的值。

具体一点地说,对于 i = [1,256],若是第 i 位的消息位元 Mi = 0,咱们会从 sk0 表中选择第 i 个字符 (ski0) ,做为咱们签名的一部分;若是第 i 位的消息位元 Mi = 1,咱们则从 sk1 表进行前述过程(即,若是咱们要对消息 M 中的第 3 位进行签名,而该位值为 0,则使用 sk0 中的第三位,sk03,做为咱们签名的一部分)。对每一个消息位元完成此操做后,咱们将选中的字符串链接,获得签名。

过程如图示说明,由于部分过程化简,密钥和消息长度只有 8 个 bit(位元)。要注意的是,每一个色块表明的都是不一样的随机 256 位字符串。

当某个用户(已经知道公钥 (pk0, pk1))收到消息 M 和签名,她可以轻易地验证这个签名。咱们以 si 表示签名中第 i 个组成部分,用户可以检查相应的消息 Mi 并计算哈希值 H(si) 。若是 Mi = 0 ,则哈希值必须匹配公钥 pk0 中的元素;若是 Mi = 1 ,则哈希值必须匹配公钥 pk1 中的元素。

若是签名中的每一个元素通过哈希运算后,都能找到对应的正确部分的公钥,咱们就会说这个签名是有效的。如下是验证过程图示,签名中至少有一个签名元素:

若是你开始以为 Lamport 的计划有些疯狂,你既是对的,也是错的。

首先探讨下这个数字签名方法的弊端。咱们会发现, Lamport 方法的签名和密钥实在太大了,大约有数千 bits。并且更要命的是,这个方法存在严重的安全局限:每一个密钥只能被用来签名一个消息,因此 Lamport 方法做为“一次性签名” 在这里被拿来举例。

这种安全局限为何存在呢?回想一下, Lamport 签名代表了在各个消息位元上可能的两个密钥之一。假如只须要签署一条信息,这个签名方法彻底没问题。然而,若是我签署了两条在每个对应位置 i 的 bit 值都不一样的消息,而后连同密钥一块儿发送出去,这可能致使大问题!

假设攻击者从不一样的消息获得两个有效的签名,她便可以发起 “混合搭配(mix and match)”攻击,成功伪造签署第三条我从未签名过的信息。如下图示说明这个攻击过程:

这个问题的严重程度取决于你签名的消息的相异程度,以及有多少消息被攻击者给截获了。但总的来讲,这确定不是件好事。

让咱们总结一下 Lamport 签名方法;它很简单、快速,但它在实际应用上还有不少不足之处。或许咱们能够作一点优化?

从一次性签名到屡次签名:基于默克尔树 (Merkle’s tree) 的签名

Lamport 签名方法是个好的开端,可是没法用单一密钥签名多条信息,是它最大的弊端。Martin Hellman 的学生 Ralph Merkle 由此获得大量启发,他很快地想到了一个聪明的解决办法。

虽然咱们不打算在这里展开解释默克尔方法的步骤,咱们仍是来试着理清 Ralph 的想法。

咱们如今的目标是用 Lamport 签名方法签署 N 条信息。最直观的方法是,以最初的 Lamport 方法生成 N 个不一样的密钥对,而后将全部公钥关联起来,集合成一个超巨大的 mega-key。(mega-key是我现编的术语。)

若是签名者继续拿着这么一把密钥集合,她就能够对 N 条不一样消息进行签名,严格上来说这也只是一把 Lamport 密钥。看起来,这样就解决了密钥重用的问题。验证者也有对应的公钥可以验证全部收到的消息。没有任何的 Lamport 密钥被使用两次。

很明显的,这种方法很糟糕,由于时间成本过高了。

具体地说,上述这种天真的方法中,为了达到要求的签名次数,签名者必须分发比普通 Lamport 公钥还要大数倍的公钥(签名者还要继续拿着一样巨大的私钥)。人们极可能会对这种结果感到不满,也会反思有没有办法避免这种负做用产生。接下来,让咱们进入 Merkle 方法。

Merkle 方法但愿能找到一个能签署多条不一样消息的方法,同时避免公钥的成本线性激增。Merkle 方法的实现以下:

  1. 首先,生成 N 个独立的 Lamport 密钥,咱们以 (PK1, SK1), …, (PKN, SKN) 表示之。
  2. 接下来,将每个公钥分别放到 Merkle hash tree (见下图),并计算根节点哈希值。这个根节点就会成为Merkle签名方法中的 “主公钥”。
  3. 签名者报关所有的 Lamport 密钥(公钥和私钥),用于签名。

关于 Merkle tree 的更多描述请点击这里。概略地说,Merkle 方法提供了一种能收集不一样的值,并用一个 “根” 哈希(例子中使用的哈希函数,长度为 256 bits)表明所收集的值的方法。给出这个根哈希,就能简单“证实” 某个元素存在于这个给出的哈希树。并且这个证实的大小和叶节点数量成对数关系。

Merkle tree,来自维基百科的解释。Lamport 公钥被放进叶节点中,而后根节点成为主公钥。

要签名的时候,签名者从 Merkle tree 中直接选择公钥,并用对应的 Lamport 密钥签名。接着她将获得的签名结果链接 Lamport 公钥并附上“Merkle 证实”。Merkle root 能够来佐证该默克尔树中包含选中的公钥(即整个方法使用的公钥)。最后签名者将整个集合看成消息签名发送出去。

(验证者只要直接将这个“签名”分别解压为 Lamport 签名、 Lamport 公钥、 Merkle 证实,就能进行验证。验证者可以依靠拿到的 Lamport 公钥验证 Lamport 签名,并用 Merkle 证实这把公钥的确存在于 Merkle tree 中。只要知足这三个条件,验证者就能确信签名是有效的。)

这个方法的缺点是会将“签名”大小增长两倍以上。不过,如今 Merkle 方法主要的公钥只是一串简单的哈希值,使得这个方法比上面提到的原始 Lamport 方法更为简洁。

最后还有个优化部分,密码学强度的伪随机数发生器可以输出生成各式各样的密钥,同时“压缩”密钥数据自己。这使得原先庞大的位元(显然是随机的)可以转换为简短的“种子(seed)”。

很赞啦!

让签名和密钥更有效率一点

Merkle 方法使得一次性签名转变为 N 次性签名。构造这种方法仍然须要基于某些一次性签名方法,好比 Lamport 方法;但不幸的是,Lamport 方法的(带宽)成本仍相对高昂。

有两种主要的方法能够下降这些成本。第一种也是 Merkle 提出的;为了更好的解释许多强大的签名方法,咱们优先说明这项技术。

回想一下 Lamport 方法,要对一条 256 位的消息进行签名,咱们须要一个包含 512 个独立密钥(和公钥)位串的向量,签名自己就是 256 个密钥位串的集合。(这些数字会被须要签名的消息位元激活,位元能够是 “0” 或 “1” ,所以须要从两张不一样的密钥表中提取适合的密钥元素。 )

这里引起了新的思考:若是咱们不对全部的消息位元进行签名,会怎么样呢?

更详细点说,在 Lamport 方法中,咱们经过输出密钥位串对一条消息的每一个位元进行签名——不管它的值是什么。若是咱们不要同时签名一条消息中 0 和 1 的位元,而是只签名 1 的位元,那又会如何呢?这么作可以将公钥和私钥的大小减半,由于咱们能够彻底丢掉整条 sk0 列。

如今咱们只有单一列位串的密钥 sk1,…,sk256,对消息的每一个位元 Mi = 1咱们都会输出一个字符串 ski;对于消息的每一个位元 Mi = 0咱们都会输出……无(由于许多消息都会包含不少的 0 位元,这么作能缩减签名大小,这些 0 位元将再也不带来任何成本)。

这种方法的明显缺陷是:它

极度
不安全,因此请不要这么作!

举例来讲,假设有个攻击者观察到一条已经被签名的消息,消息开头是“1111…”。如今攻击者想要在不破坏签名的状况下,将消息编辑成“0000…”,只须要删掉这条签名中的几个组成部分便可!简言之,虽然要将 0 位元“翻转” 成 1 位元很困难,但反之要将 1 换成 0 就很是简单了。

如今有了个解决办法,并且它很是巧妙。

让咱们接着瞧瞧。虽然没法避免攻击者将消息中的 1 改为 0 ,但咱们能发现这些改动。只要将一个简单的“校验和(checksum)”附加到消息上,而后将消息和校验和一块儿签名。对于签名验证者来讲,她必须验证整份签名的两个值,也须要肯定收到的校验和是正确的。

咱们使用的校验和很是小:它由简单的二进制整数组成,表示原始消息中的全部 0 位元数。

若是攻击者试图修改消息内容(或是校验和),使得部分 1 位元变成 0 位元,并无手段能够阻止她。可是这种攻击会增长消息中的 0 位元数,这会使得校验和无效,验证者从而会拒绝这个签名。

​固然,机智的攻击者可能还会试图混淆校验和(校验和也和消息一块儿被签名),增长校验和的整数值来匹配她篡改的位元数。然而最关键的是,由于校验和是二进制整数,若是要增长校验和的值,攻击者势必得将一些 0 位元转换成 1 位元。又由于校验和也被签过名,这种签名方法从源头阻止这种转换(将 0 换成 1),所以攻击者没法得逞。

(若是你继续记录下去,的确会增长被签名的“消息”的大小。在咱们的例子中,一条 256 位的消息的校验和,须要额外的 8 位元及增长相应的签名成本。不过,若是这条消息包含许多 0 位元,这么作对于缩减签名大小仍然很是有效。)

原文连接: https://blog.cryptographyengineering.com/2018/04/07/hash-based-signatures-an-illustrated-primer/

做者: Matthew Green

翻译&校对: Ian Liu & 阿剑

以太中文网经受权转载

相关文章
相关标签/搜索