一、密码学和加密货币简介
所有的货币都需要某种方式来控制供给,并加强各种安全属性来防止欺诈。在法定货币中,像中央银行这样的组织控制着货币供应,并给实物货币增加了防伪功能。这些安全特征提高了攻击者的门槛,但它们并没有使货币无法伪造。归根结底,执法对于阻止人们违反制度规则是必要的。
加密货币也必须有安全措施,防止人们篡改系统状态和模棱两可(即向不同的人做出相互不一致的声明)。例如,如果 Alice 说服 Bob 她付给他一枚数字硬币,她应该不能说服 Carol 她付给她同样的硬币。但与法定货币不同,加密货币的安全规则需要纯粹通过技术手段来执行,而不依赖于中央机构。
顾名思义,加密货币大量使用密码学。密码术提供了一种在系统本身中对加密货币系统的规则进行安全编码的机制。我们可以用它来防止篡改和模棱两可,以及在数学协议中编码,创造新的货币单位的规则。因此,在我们能够正确理解加密货币之前,我们需要深入研究它们所依赖的加密基础。
密码学是一个深入的学术研究领域,它使用了许多众所周知的微妙而复杂的高级数学技术。幸运的是,比特币只依赖于少数相对简单且众所周知的加密构造。在这一章中,我们专门研究加密哈希和数字签名,这两种原语被证明对构建加密货币很有用。后面的章节介绍了更复杂的加密方案,如零知识证明,用于比特币的扩展和修改。
一旦介绍了必要的加密原语,我们将讨论一些使用它们来构建加密货币的方法。我们将用简单加密货币的例子来完成这一章,这些例子说明了一些需要解决的设计挑战。
1.1。加密哈希函数
我们需要理解的第一个密码原语是一个密码散列函数。散列函数是一个数学函数,具有以下三个属性:
它的输入可以是任何大小的任何字符串。
它产生固定大小的输出。为了使本章的讨论具体化,我们将假设输出大小为 256 位。然而,我们的讨论适用于任何输出大小,只要它足够大。
它是高效可计算的。直观地说,这意味着对于给定的输入字符串,您可以在合理的时间内计算出哈希函数的输出。更专业地说,计算一个 n 位字符串的散列应该有一个运行时间为 O ( n )。
这些属性定义了一个通用的哈希函数,可以用来构建一个数据结构,比如哈希表。我们将专门关注加密散列函数。对于密码安全的散列函数,我们要求它具有以下三个附加特性:(1)抗冲突性,(2)隐藏性,以及(3)谜题友好性。
我们将更仔细地研究这些属性,以理解为什么拥有一个满足它们的函数是有用的。学过密码学的读者应该知道,本书中对散列函数的处理与标准密码学教科书中的有点不同。特别是,谜题友好属性不是加密哈希函数的一般要求,而是对加密货币特别有用的一个要求。
特性 1:耐碰撞性
我们需要的加密散列函数的第一个特性是它是抗冲突的。当两个不同的输入产生相同的输出时,就会发生冲突。一个哈希函数 H ()是抗碰撞的,如果没人能发现碰撞(图 1.1 )。形式上:
碰撞阻力。如果找不到两个值 x 和 y ,使得x≦y,而H(x)=H(y),则称哈希函数 H 是抗冲突的。
Collision resistance. A hash function H is said to be collision resistant if it is infeasible to find two values, x and y, such that x ≠ y, yet H(x) = H(y).
注意,我们说“没有人能发现”碰撞,但我们并没有说不存在碰撞。实际上,任何哈希函数都存在冲突,我们可以通过一个简单的计数参数来证明这一点。散列函数的输入空间包含所有长度的所有字符串,而输出空间只包含特定固定长度的字符串。因为输入空间大于输出空间(的确,输入空间是无限的,而输出空间是有限的),必然有输入字符串映射到同一个输出字符串。事实上,会有一些输出会映射到无限多的可能输入(图 1.2 )。
图 1.1。哈希冲突。 x 和 y 是不同的值,但是当输入到哈希函数 H 时,它们产生相同的输出。
更糟糕的是,我们说不可能找到碰撞。然而,有一些方法可以保证找到冲突。考虑下面这个简单的方法来寻找一个输出大小为 256 位的散列函数的冲突:挑选 2 个 ^(256 个) + 1 个不同的值,计算每个值的散列,并检查任意两个输出是否相等。因为我们选择的输入比可能的输出多,所以当您应用散列函数时,它们中的某一对肯定会发生冲突。
上面的方法保证能发现冲突。但是如果我们选择随机输入并计算哈希值,我们会在检查 2 ^(256) + 1 个输入之前很久就发现有很高概率的冲突。事实上,如果我们随机选择 2 个 ^(130) + 1 个输入,结果显示至少有 99.8%的几率其中两个会发生碰撞。我们可以通过粗略地检查可能输出数量的平方根来发现冲突,这是由一种概率现象引起的,这种现象被称为生日悖论。在作业问题中(参见本书的在线补充材料,可在http://press.princeton.edu/titles/10908.html找到),我们对此进行了更详细的检查。
图 1.2。碰撞的必然性。因为输入的数量超过了输出的数量,所以我们保证至少有一个输出,哈希函数将不止一个输入映射到这个输出。
这种冲突检测算法适用于每个哈希函数。但是,当然,问题是这需要很长时间。对于一个 256 位输出的散列函数,在最坏的情况下,您必须计算散列函数 2 ^(256) + 1 次,平均大约 2 ^(128) 次。这当然是一个天文数字——如果一台计算机每秒计算 10,000 个散列,那么计算 2 个 128 个 128 个散列将需要超过一千万亿年的时间!换一种方式来思考这个问题,我们可以说,如果人类制造的每一台计算机都从宇宙开始就一直在计算,那么它们现在发现碰撞的几率仍然微乎其微。小到远远小于地球在接下来的两秒钟内被一颗巨大的流星毁灭的几率。
因此,我们找到了一种通用但不切实际的算法来为任何散列函数寻找冲突。一个更困难的问题是:有没有其他方法可以用于特定的哈希函数来发现冲突?换句话说,尽管使用通用冲突检测算法是不可行的,但是可能有一些其他算法可以有效地发现特定散列函数的冲突。
例如,考虑下面的散列函数:
这个函数满足我们对散列函数的要求,因为它接受任何长度的输入,返回固定大小的输出(256 位),并且是可有效计算的。但是这个函数也有一个寻找碰撞的有效方法。注意,这个函数只返回输入的最后 256 位。那么,一个冲突将是值 3 和 3 + 2 ^(256) 。这个简单的例子说明,即使我们的通用冲突检测方法在实践中不可用,至少有一些散列函数存在有效的冲突检测方法。
*H*(*x*)= *x* mod 2256
然而对于其他哈希函数,我们不知道是否存在这样的方法。我们怀疑它们是耐碰撞的。然而,没有哈希函数被证明是抗冲突的。我们在实践中所依赖的加密散列函数,只是人们已经非常非常努力地试图寻找冲突但还没有成功的函数。所以我们选择相信这些是耐碰撞的。(在某些情况下,例如被称为 MD5 的散列函数,在多年的工作之后,最终发现了冲突,导致该函数被弃用并逐渐退出实际使用。)
应用:消息摘要
既然我们知道碰撞阻力是什么,合乎逻辑的问题是:它有什么用?这里有一个应用:如果我们知道一个抗冲突哈希函数 H 的两个输入 x 和 y 是不同的,那么可以有把握地假设它们的哈希 H ( x )和 H ( y )是不同的——如果有人知道一个不同的 x 和 y
这个参数允许我们使用散列输出作为消息摘要。以 SecureBox 为例,它是一个经过认证的在线文件存储系统,允许用户上传文件,并在下载文件时确保文件的完整性。假设 Alice 上传了非常大的文件,她希望以后能够验证她下载的文件与她上传的文件是否相同。一种方法是将整个大文件保存在本地,并直接与她下载的文件进行比较。虽然这很有效,但它在很大程度上违背了上传它的初衷;如果 Alice 需要访问文件的本地副本以确保其完整性,她可以直接使用本地副本。
抗冲突散列为这个问题提供了一个优雅而有效的解决方案。Alice 只需要记住原始文件的散列。当她后来从 SecureBox 下载文件时,她计算下载文件的散列,并将其与她存储的文件进行比较。如果散列相同,那么她可以断定该文件确实是她上传的同一文件,但是如果它们不同,那么 Alice 可以断定该文件已经被篡改。因此,记住哈希不仅可以让她检测到文件在传输过程中或在 SecureBox 服务器上的意外损坏,还可以检测到服务器对文件的故意修改。面对其他实体的潜在恶意行为时,这种保证是密码学赋予我们的核心。
散列作为消息的固定长度摘要,或明确的摘要。这给了我们一种非常有效的方法来记住我们以前见过的东西,并再次认出它们。尽管整个文件可能有几十亿字节长,但是散列的长度是固定的——在我们的例子中,散列函数为 256 位。这大大降低了我们的存储需求。在本章的后半部分以及整本书中,我们将会看到使用散列作为消息摘要的应用程序。
属性 2:隐藏
我们想从散列函数中得到的第二个属性是它隐藏了。隐藏属性断言,如果我们得到散列函数y=H(x)的输出,就没有可行的方法来计算出输入 x 是什么。问题是,这个性质不可能以陈述的形式为真。考虑下面这个简单的例子:我们要做一个抛硬币的实验。如果抛硬币的结果是正面,我们将宣布字符串“正面”的散列值如果结果是 tails,我们将宣布字符串“tails”的散列值
然后我们请一个人,一个对手,他没有看到抛硬币,但只看到了这个散列输出,来找出被散列的字符串是什么(我们很快就会明白为什么我们会想玩这样的游戏)。作为响应,他们将简单地计算字符串“头”的散列和字符串“尾”的散列,并且他们可以看到给他们的是哪一个。因此,只需几个步骤,他们就可以计算出输入是什么。
对手能够猜出字符串是什么,因为只有两个值 x 是可能的,对手很容易尝试这两个值。为了能够实现隐藏属性,必须不存在特别可能的 x 的值。也就是说, x 必须从一个集合中选择,在某种意义上,这个集合非常分散。如果从这样的集合中选择 x ,这种尝试几个特别可能的 x 值的方法就行不通了。
最大的问题是:当我们想要的值不是像在我们的“正面”和“反面”实验中那样来自一个展开的集合时,我们能实现隐藏属性吗?还好,答案是肯定的!我们甚至可以隐藏一个没有展开的输入,方法是将它与另一个展开的输入连接起来。我们现在可以稍微更精确地定义隐藏的含义(双竖线‖表示连接)。
The big question is: Can we achieve the hiding property when the values that we want do not come from a spread-out set as in our “heads” and “tails” experiment? Fortunately, the answer is yes! We can hide even an input that’s not spread out by concatenating it with another input that is spread out. We can now be slightly more precise about what we mean by hiding (the double vertical bar ‖ denotes concatenation).
隐藏。如果当从具有高最小熵的概率分布中选择秘密值 r 时,那么,给定H(r‖x),找到 x 是不可行的,则称散列函数 H 是隐藏的。
Hiding. A hash function H is said to be hiding if when a secret value r is chosen from a probability distribution that has high min-entropy, then, given H(r ‖ x), it is infeasible to find x.
在信息论中,最小熵是对结果可预测程度的度量,高最小熵抓住了分布(即随机变量)非常分散的直观想法。这具体意味着,当我们从分布中取样时,没有可能出现的特定值。因此,对于一个具体的例子,如果从所有 256 位长的字符串中均匀地选择 r ,那么以概率 1/2 ^(256) 选择任何特定的字符串,这是一个无穷小的值。
应用:承诺
现在让我们来看看隐藏属性的一个应用。特别是,我们想要做的是一个叫做承诺的东西。承诺是一种数字模拟,取一个价值,把它封在一个信封里,然后把信封放在桌子上,让每个人都能看到。当你这样做的时候,你已经把自己托付给信封里的东西了。但是你没有打开它,所以即使你承诺了一个值,这个值对其他人来说仍然是一个秘密。稍后,您可以打开信封,展示您之前承诺的价值。
Now let’s look at an application of the hiding property. In particular, what we want to do is something called a commitment. A commitment is the digital analog of taking a value, sealing it in an envelope, and putting that envelope out on the table where everyone can see it. When you do that, you’ve committed yourself to what’s inside the envelope. But you haven’t opened it, so even though you’ve committed to a value, the value remains a secret from everyone else. Later, you can open the envelope and reveal the value that you committed to earlier.
承诺方案。承诺方案由两种算法组成:
com := commit( msg,nonce)commit 函数将一条消息和一个秘密随机值(称为 nonce )作为输入,并返回一个提交。
verify( com,msg,nonce)verify 函数将承诺、nonce 和消息作为输入。如果 com == commit( msg,nonce )则返回 true,否则返回 false。
我们要求以下两个安全属性成立:
隐藏:给定 com ,找不到 msg 。
绑定:找两对(消息,随机数)和(消息′,随机数′)使得消息≦消息′和提交(消息,随机数 ) ==提交(消息【t77)
• Binding: It is infeasible to find two pairs (msg, nonce) and (msg′, nonce′) such that msg ≠ msg′ and commit(msg, nonce) == commit(msg′, nonce′).
要使用承诺方案,我们首先需要生成一个随机的随机数。然后,我们将 commit 函数与 msg 、被提交的值一起应用于这个 nonce,并发布提交 com 。这个阶段类似于将密封的信封放在桌子上。稍后,如果我们想要揭示我们先前承诺的值,我们发布我们用来创建该承诺的随机随机数,以及消息 msg 。现在任何人都可以证实 msg 确实是之前提交的消息。这个阶段类似于打开信封。
每次你提交一个值,选择一个新的随机值 nonce 是很重要的。在密码学中,术语 nonce 用于指代只能使用一次的值。
这两个安全属性决定了算法实际上就像密封和打开信封一样。首先,鉴于 com 的承诺,有人看着信封想不出里面的信息是什么。第二个属性是它具有约束力。这确保了当你对信封里的东西做出承诺时,你不能在以后改变主意。也就是说,找到两个不同的消息是不可行的,因此您可以提交一个消息,然后声称您提交了另一个消息。
那么我们怎么知道这两个属性成立呢?在我们回答这个问题之前,我们需要讨论一下我们将如何实际实现一个承诺方案。我们可以使用加密散列函数来做到这一点。考虑以下承诺方案:
为了提交消息,我们生成一个随机的 256 位随机数。然后,我们连接 nonce 和消息,并返回这个连接值的散列作为提交。为了进行验证,有人将计算他们与消息连接在一起的随机数的相同散列。他们将检查结果是否与他们看到的承诺相同。
再看一下我们的承诺方案所需的两个属性。如果我们将提交和验证以及H(nonce‖msg)的实例化替换为 com ,那么这些属性将变成:
commit(*msg, nonce*) := *H*(*nonce* ‖ *msg*),
where *nonce* is a random 256-bit value
隐藏:给定H(noncemsg,找 msg 是不可行的。
绑定:找两对( msg,nonce )和(msg′,nonce′)使得msg≦msg′和H(noncemsg)=(
承诺的隐藏属性正是我们散列函数所需要的隐藏属性。如果键被选择为一个随机的 256 位值,那么隐藏属性表明,如果我们散列键和消息的连接,那么从散列输出中恢复消息是不可行的。事实证明,绑定属性是由底层 hash 函数的抗冲突属性所隐含的。如果散列函数是抗冲突的,那么找到不同的值 msg 和msg’使得H(nonce‵msg)=H(nonce’msg’)是不可行的,因为这些值(注意,相反的含义不成立。也就是说,有可能你能找到碰撞,但是没有一个是H(nonce‖msg)= =H(nonce′‖msg*′)的形式。例如,如果您只能找到一个冲突,其中两个不同的 nonces 为相同的消息生成相同的承诺,那么承诺方案仍然是绑定的,但是底层哈希函数是不抗冲突的。)
因此,如果 H 是一个既抗冲突又隐藏的散列函数,这个承诺方案将会起作用,因为它将具有必要的安全属性。
属性 3:益智友好
我们需要散列函数的第三个安全属性是它们对谜题友好。这个属性有点复杂。我们首先解释这个属性的技术要求是什么,然后给出一个应用来说明为什么这个属性是有用的。
Property 3: Puzzle Friendliness
益智友好。如果对于每一个可能的 n 位输出值 y ,如果从具有高最小熵的分布中选择 k ,那么不可能找到 x 使得H(k‖x,则称散列函数 H 是难题友好的
直观地说,如果有人希望散列函数具有某个特定的输出值 y ,并且如果输入的一部分是以适当的随机方式选择的,那么很难找到另一个正好符合该目标的值。
应用:搜索拼图
让我们考虑一个应用程序来说明这个属性的用处。在这个应用程序中,我们将构建一个搜索难题,这是一个数学问题,需要搜索一个非常大的空间才能找到答案。特别是,搜索难题没有捷径。也就是说,除了在那么大的空间中搜索之外,没有其他方法可以找到有效的解决方案。
Intuitively, if someone wants to target the hash function to have some particular output value y, and if part of the input has been chosen in a suitably randomized way, then it’s very difficult to find another value that hits exactly that target.
搜索谜题。一个搜索谜题包括
哈希函数 H ,
从高最小熵分布中选择的值 id (我们称之为 puzzle-ID ),以及
目标设置 Y 。
这个难题的一个解决方案是一个值, x ,这样
• a target set Y.
直觉是这样的:如果 H 有一个 n 位输出,那么它可以取 2 个 ^(n) 值中的任何一个。解决这个难题需要找到一个输入,使得输出落在集合 Y 内,该集合通常比所有输出的集合小得多。 Y 的大小决定了拼图的难度。如果 Y 是所有 n 位字符串的集合,那么这个谜题是平凡的,而如果 Y 只有一个元素,那么这个谜题是最难的。难题 ID 具有高最小熵确保没有捷径。相反,如果 ID 的某个特定值是可能的,那么有人就可以作弊,比如说,用那个 ID 预先计算谜题的解。
如果一个散列函数是字谜友好的,那么对于这个字谜没有比尝试随机值 x 更好的解决策略。因此,如果我们想提出一个难以解决的难题,我们可以这样做,只要我们能以一种适当的随机方式生成难题 id。我们稍后将使用这个想法,当我们从第 2 章开始讨论比特币挖掘时——挖掘是一种计算难题。
SHA-256
我们已经讨论了哈希函数的三个属性以及每个属性的一个应用。现在让我们来讨论一个在本书中会经常用到的散列函数。存在许多哈希函数,但这是比特币主要使用的一种,而且是一种非常好的使用方法。它叫 SHA-256 。
回想一下,我们要求散列函数可以处理任意长度的输入。幸运的是,只要我们能够构建一个适用于固定长度输入的散列函数,就有一个通用的方法将其转换成适用于任意长度输入的散列函数。它被称为默克尔-达姆加德变换。SHA-256 是许多使用这种方法的常用散列函数之一。在普通术语中,底层的固定长度防冲突散列函数被称为压缩函数。已经证明,如果底层压缩函数是抗冲突的,那么整个散列函数也是抗冲突的。
Merkle-damg rd 变换非常简单。假设压缩函数接受长度为 m 的输入并产生一个长度为 n 的输出。散列函数的输入可以是任何大小,被分成长度为m–n的块。构造工作如下:将每个块与前一个块的输出一起传递给压缩函数。注意,输入长度将是(m–n)+n=m,这是压缩函数的输入长度。对于没有先前块输出的第一块,我们使用一个初始化向量(图 1.3 中的 IV)。这个数字在每次调用 hash 函数时都会重复使用,实际上您可以在标准文档中查找它。最后一个块的输出是您返回的结果。
图 1.3。SHA-256 哈希函数(简体)。SHA-256 使用 Merkle-damg rd 变换将固定长度的防冲突压缩函数转换为接受任意长度输入的哈希函数。输入经过填充,因此其长度是 512 位的倍数。IV 代表初始化向量。
建模哈希函数
散列函数是密码学中的瑞士军刀:它们在各种各样的应用程序中都有一席之地。这种多样性的另一面是,不同的应用程序需要稍微不同的哈希函数属性来确保安全性。众所周知,很难确定散列函数属性的列表,这将导致全面的可证明的安全性。
在本文中,我们选择了三个对比特币和其他加密货币中哈希函数的使用方式至关重要的属性。即使在这个空间中,也不是所有这些属性对于散列函数的每次使用都是必要的。例如,谜题友好性只在比特币挖掘中重要,我们将会看到。
安全系统的设计者经常认输,将散列函数建模为对每个可能的输入输出独立随机值的函数。在密码学中,使用这种“随机预言模型”来证明安全性仍然是有争议的。不管一个人在这场辩论中的立场如何,推理如何将我们在应用程序中需要的安全属性简化为底层原语的基本属性,对于构建安全系统来说是一项有价值的智力练习。我们在这一章的介绍旨在帮助你学习这一技能。
SHA-256 使用压缩函数,接受 768 位输入,产生 256 位输出。块大小是 512 位。参见图 1.3 了解 SHA-256 的工作方式。
我们已经讨论了哈希函数,具有特殊属性的加密哈希函数,这些属性的应用,以及我们在比特币中使用的特定哈希函数。在下一节中,我们将讨论使用哈希函数构建更复杂的数据结构的方法,这些数据结构在像比特币这样的分布式系统中使用。
1.2。哈希指针和数据结构
在本节中,我们将讨论哈希指针及其应用。散列指针是一种数据结构,在我们考虑的许多系统中都很有用。hash 指针只是一个指针,指向一些信息与信息的加密散列一起存储的位置。常规指针为您提供了一种检索信息的方法,而散列指针也允许您验证信息没有被更改(图 1.4 )。
图 1.4。哈希指针。哈希指针是指向数据存储位置的指针,以及在某个固定时间点该数据值的加密哈希。
我们可以使用散列指针来构建各种数据结构。直观地说,我们可以采用一个熟悉的使用指针的数据结构,比如一个链表或一个二叉查找树,并像我们通常所做的那样,用散列指针而不是普通指针来实现它。
区块链
图 1.5 显示了一个使用哈希指针的链表。我们称这种数据结构为区块链。在一个有一系列块的常规链表中,每个块都有数据和一个指向链表中前一个块的指针。但是在区块链中,前一个块指针将被替换为散列指针。因此,每个块不仅告诉我们前一个块的值在哪里,而且还包含该值的摘要,这允许我们验证该值没有被更改。我们存储列表的头部,它只是一个指向最近数据块的常规散列指针。
区块链的一个用例是防篡改日志。也就是说,我们希望构建一个日志数据结构来存储数据,并允许我们将数据追加到日志的末尾。但是,如果有人修改了日志中较早出现的数据,我们将检测到这种变化。
图 1.5。区块链。区块链是一个用散列指针而不是指针构建的链表。
为了理解为什么区块链具有这种显而易见的篡改特性,让我们来看看如果对手想要在链的中间篡改数据会发生什么。具体来说,对手的目标是以这样一种方式做到这一点,即只记得区块链头部哈希指针的人将无法检测到篡改。为了实现这个目标,对手改变了某个块 k 的数据。由于数据已经改变,块 k + 1 中的散列,即整个块 k 的散列,将不会匹配。请记住,我们从统计上保证新的散列不会匹配更改的内容,因为散列函数是抗冲突的。因此我们将检测块 k 中的新数据和块 k + 1 中的散列指针之间的不一致。当然,对手也可以通过改变下一个块的散列来继续试图掩盖这种改变。对手可以继续这样做,但是当她到达列表的顶部时,这个策略将失败。具体来说,只要我们把 hash 指针存储在列表头一个对手无法更改的地方,她就无法更改任何块而不被检测到(图 1.6 )。
图 1.6。防篡改日志。如果对手修改区块链中任何地方的数据,将导致下一个块中的散列指针不正确。如果我们存储列表的头部,那么即使对手修改所有指针以与修改的数据一致,头部指针也将是不正确的,并且我们可以检测到篡改。
结果是,如果对手想要篡改整个链中任何地方的数据,为了保持故事的一致性,她将不得不从头到尾篡改散列指针。她最终会遇到一个障碍,因为她无法篡改列表的开头。因此,通过记住这个散列指针,我们基本上确定了整个列表的篡改明显的散列。所以我们可以像这样构建一个区块链,包含我们想要的任意多的块,回到列表开头的某个特殊块,我们称之为创世纪块。
您可能已经注意到,区块链结构与第 1.1 节中讨论的 Merkle-damg rd 结构相似。事实上,它们非常相似,同样的安全理由也适用于它们。
Merkle 树
我们可以使用散列指针构建的另一个有用的数据结构是二叉树。带有散列指针的二叉树被称为 Merkle 树 ( 图 1.7 ),以其发明者 Ralph Merkle 命名。假设我们有一些包含数据的块。这些木块构成了我们树的叶子。我们将这些数据块分成两两一组,然后为每一组构建一个包含两个散列指针的数据结构,每个块一个。这些数据结构构成了树的下一层。我们依次将它们分成两个一组,并为每一对创建一个新的数据结构,其中包含每一对的散列。我们继续这样做,直到我们到达一个单独的块,树根。
图 1.7。默克尔树。在 Merkle 树中,数据块成对分组,每个数据块的散列存储在父节点中。父节点依次成对分组,它们的散列存储在树的上一层。这种模式继续沿着树向上,直到我们到达根节点。
和以前一样,我们只记得一个散列指针:在本例中,是树的根处的那个。我们现在能够遍历指向列表中任何一点的散列指针。这使我们能够确保数据没有被篡改,因为正如我们在区块链中看到的那样,如果对手篡改了树底部的某个数据块,他的更改将导致上一级的哈希指针不匹配,即使他继续篡改树中更高的其他数据块,更改最终也会传播到顶部,在那里他将无法篡改我们存储的哈希指针。因此,只要记住顶部的哈希指针,任何篡改任何数据的企图都会被检测到。
成员资格证明
Merkle 树的另一个很好的特性是,不像我们之前建立的区块链,它们允许一个简洁的成员证明。假设有人想证明某个数据块是 Merkle 树的成员。像往常一样,我们只记得根。然后,他们需要向我们展示这个数据块,以及从数据块到根的路径上的数据块。我们可以忽略树的其余部分,因为这条路径上的块足以让我们验证一直到树根的散列。参见图 1.8 了解其工作原理。
如果树中有 n 个节点,只需要显示大约 log( n )项。由于每一步只需要计算子块的散列值,我们需要大约 log( n )时间来验证它。因此,即使 Merkle 树包含大量的块,我们仍然可以在相对较短的时间内证明成员资格。因此,验证在时间和空间中运行,时间和空间是树中节点数量的对数。
图 1.8。会员证明。要证明数据块包含在树中,只需显示从该数据块到根的路径中的数据块。
一棵排序的 Merkle 树仅仅是一棵 Merkle 树,我们把底部的块取出来,用某种排序函数对它们进行排序。这可以是字母顺序、词典顺序、数字顺序或其他约定的顺序。
非会员证明
使用排序的 Merkle 树,可以在对数时间和空间中验证非成员关系。也就是说,我们可以证明特定的块不在 Merkle 树中。我们这样做的方法很简单,只需在有问题的项目之前显示项目的路径,在有问题的项目之后显示项目的路径。如果这两个项目在树中是连续的,那么这可以证明所讨论的项目没有被包括在内——因为如果它被包括在内,它将需要在所显示的两个项目之间,但是它们之间没有空格,因为它们是连续的。
我们已经讨论了在链表和二叉树中使用散列指针,但是更普遍的是,我们可以在任何基于指针的数据结构中使用散列指针,只要该数据结构没有循环。如果数据结构中有循环,那么我们就不能让所有的散列匹配起来。如果你想一想,在一个非循环的数据结构中,我们可以从树叶附近开始,或者从没有任何指针的事物附近开始,计算这些事物的散列,然后回到起点。但是在一个有循环的结构中,没有终点,我们可以从起点开始计算。
考虑另一个例子,我们可以用散列指针构建一个有向无环图,我们将能够非常有效地验证该图中的成员资格。这也很容易计算。以这种方式使用散列指针是一个常见的技巧,你会在分布式数据结构的上下文中和我们在本章后面(第 1.5 节)和整本书中讨论的算法中反复看到。
1.3。数字签名
在这一节中,我们来看看数字签名。这是第二个密码原语,以及哈希函数,我们需要它作为第 1.5 节中关于加密货币讨论的构建模块。数字签名应该是纸上手写签名的数字模拟。我们希望数字签名有两个特性与手写签名相似。首先,只有你可以签名,但是任何看到它的人都可以验证它的有效性。第二,我们希望签名与特定的文档相关联,这样签名就不能用于表示您同意或认可不同的文档。对于手写签名,后一个属性类似于确保某人不能将你的签名从一个文档中剪下并粘贴到另一个文档的底部。
我们如何使用加密技术以数字形式构建它呢?首先,让我们把上面的直观讨论稍微具体化一点。这将允许我们对数字签名方案进行更好的推理,并讨论它们的安全性。
1.3. DIGITAL SIGNATURES
数字签名方案。数字签名方案由以下三种算法组成:
( sk,PK):= generate keys(keysize)generate keys 方法取一个密钥大小,生成一个密钥对。秘密密钥 sk 被秘密保存,用于签署信息。pk 是你给每个人的公共验证密钥。任何有这把钥匙的人都可以验证你的签名。
sig := sign( sk,message)sign 方法将一条消息和一个密钥 sk 作为输入,并在 sk 下为消息输出一个签名。
isValid := verify( pk,message,SIG)verify 方法接受一条消息、一个签名和一个公钥作为输入。它返回一个布尔值, isValid ,如果 sig 是公钥 pk 下消息的有效签名,则为真,否则为假。
我们要求以下两个属性成立:
有效签名必须验证:verify( pk,message ),sign( sk,message)= = true。
签名是不可伪造的。
We require that the following two properties hold:
我们注意到 generateKeys 和 sign 可以是随机算法。事实上,generateKeys 最好是随机化的,因为它应该为不同的人生成不同的密钥。相反,验证将总是确定性的。
图 1.9。不可伪造游戏。攻击者和挑战者玩不可伪造游戏。如果攻击者能够成功地输出他以前没有见过的消息上的签名,他就赢了。如果他不能这样做,挑战者就赢了,数字签名方案是不可伪造的。维特·迪菲(右)的照片,裁剪,凯文·博切克。2.0 版知识共享协议下的许可。
现在让我们更详细地检查我们需要的数字签名方案的两个属性。第一个属性很简单——有效的签名必须是可验证的。如果我用我的秘密密钥 sk 对一条消息进行签名,然后有人试图用我的公开密钥 pk 对同一条消息验证该签名,那么该签名必须正确验证。这个属性是签名有用的基本要求。
不可伪造性。第二个要求是伪造签名在计算上是不可行的。也就是说,知道你的公钥并看到你在其他消息上的签名的对手不能在他没有看到你签名的消息上伪造你的签名。这种不可伪造性通常是根据我们与对手玩的游戏来形式化的。游戏的使用在密码安全证明中相当普遍。
在不可伪造性游戏中,一个对手声称他可以伪造签名,一个挑战者测试这个声明(图 1.9 )。我们做的第一件事是使用 generateKeys 生成一个秘密签名密钥和一个相应的公共验证密钥。我们把秘密密钥给挑战者,把公开密钥给挑战者和对手。所以对手只知道公开的信息,他的任务是试图伪造一个信息。挑战者知道秘密钥匙。所以他可以签名。
直觉上,这个游戏的设置符合真实世界的条件。真正的攻击者可能会在不同的文档上看到潜在受害者的有效签名。如果对攻击者有用,攻击者甚至可以操纵受害者签署看起来无害的文件。
为了在我们的游戏中模拟这一点,我们允许对手在他选择的一些文档上签名,只要他想,只要猜测的次数是合理的。为了直观地理解我们所谓的合理猜测次数,我们允许对手尝试 100 万次猜测,但不是 2 次。在渐近术语中,我们允许对手尝试作为密钥大小的多项式函数的多次猜测,但不能更多(例如,他不能尝试指数多次猜测)。
一旦对手认为他已经看到了足够多的签名,那么他就会选择一些消息,试图伪造一个签名。对 M 的唯一限制是,它必须是对手之前没有看到签名的消息(因为这样他显然可以发回给他的签名)。挑战者运行验证算法来确定攻击者产生的签名是否是在公共验证密钥下在 M 上的有效签名。如果验证成功,对手赢得游戏。
我们说签名方案是不可伪造的,当且仅当,不管对手使用什么算法,他成功伪造消息的机会非常小——小到我们可以假设它在实践中永远不会发生。
实际问题
要将算法思想转化为可以实现的数字签名机制,必须做几件实际的事情。例如,许多签名算法都是随机化的(特别是比特币中使用的算法),因此我们需要一个好的随机性来源。这个要求的重要性不能被高估,因为糟糕的随机性会使你原本安全的算法变得不安全。
另一个实际问题是消息的大小。在实践中,您能够签名的消息大小是有限制的,因为真正的方案将对有限长度的位串进行操作。有一个简单的方法可以绕过这个限制:对消息的散列签名,而不是对消息本身签名。如果我们使用具有 256 位输出的加密散列函数,那么只要我们的签名方案可以签名 256 位消息,我们就可以有效地签名任意长度的消息。正如我们所讨论的,以这种方式使用消息的散列作为消息摘要是安全的,因为散列函数是抗冲突的。
我们稍后会用到的另一个技巧是你可以给一个散列指针签名。如果您对一个散列指针进行签名,那么签名就覆盖或保护了整个结构——不仅仅是散列指针本身,而是散列指针链所指向的一切。例如,如果您要对位于区块链末尾的哈希指针进行签名,结果将是您实际上对整个区块链进行了数字签名。
ECDSA
现在让我们进入具体细节。比特币使用一种特殊的数字签名方案,称为椭圆曲线数字签名算法 (ECDSA)。ECDSA 是美国政府标准,是早期 DSA 算法的更新,适用于使用椭圆曲线。这些算法多年来已经接受了大量的密码分析,通常被认为是安全的。
更具体地说,比特币在标准椭圆曲线 secp256k1 上使用 ECDSA,估计可以提供 128 位的安全性(即破解这种算法与执行 2 ^(128) 对称密钥加密操作一样困难,比如调用哈希函数)。这条曲线虽然是公布的标准,但在比特币之外很少使用;使用 ECDSA 的其他应用程序(如用于安全网页浏览的 TLS 协议中的密钥交换)通常使用更常见的 secp256r1 曲线。这只是比特币的一个怪癖,因为它是由 Satoshi(见前言)在系统的早期规范中选择的,现在很难改变。
我们不会深入研究 ECDSA 如何工作的所有细节,因为这涉及到一些复杂的数学,理解它对于本书的其余部分是不必要的。如果你对细节感兴趣,可以参考本章末尾的延伸阅读部分。然而,了解各种数量的大小可能是有用的:
私钥:
256 位
公钥,未压缩:
| 512 位 | 压缩的公钥: | | 257 位 | 要签名的消息: | | 256 位 | 签名: | | 512 位 | 请注意,即使 ECDSA 在技术上只能对 256 位长的消息进行签名,这也不是问题:消息在被签名之前总是被散列,因此实际上任何大小的消息都可以被有效地签名。 | | 使用 ECDSA,一个好的随机源是必不可少的,因为一个坏的随机源可能会泄露您的密钥。直觉告诉我们,如果在生成密钥时使用不良随机性,那么生成的密钥很可能不安全。但是 ECDSA 的一个怪癖是,即使你只在签名时使用坏的随机性,并且你使用你的非常好的密钥,坏的签名也会泄露你的私钥。(对于那些熟悉 DSA 的人来说,这是 DSA 中的一个普遍现象,并不是椭圆曲线变体所特有的。)然后游戏就结束了:如果你泄露了你的私钥,对手就可以伪造你的签名。因此,在实践中,我们需要特别小心地使用好的随机性。使用不良的随机性来源是其他安全系统的常见缺陷。 | 这就完成了我们对作为加密原语的数字签名的讨论。在下一节中,我们将讨论数字签名的一些应用,这些应用最终将有助于构建加密货币。 |
1.4。作为身份的公钥
让我们来看一个伴随数字签名的好技巧。这个想法是获取一个公钥,一个来自数字签名方案的公共验证密钥,然后将其等同于一个人或系统中一个参与者的身份。如果您看到一条消息,其签名在公钥 pk 下验证正确,那么您可以将此视为声明该消息的 pk 。你可以把公钥想象成一个参与者,或者系统中的一方,他们可以通过签署声明来发表声明。从这个角度来看,公钥是一个身份。对于一个为身份 pk 说话的人来说,他必须知道相应的密钥 sk 。
加密货币和加密
如果你一直在等待发现比特币使用的是哪种加密算法,很抱歉让你失望了。比特币没有加密,因为没有什么需要加密,我们会看到。加密只是现代密码术可能实现的一套丰富技术中的一种。它们中的许多,例如承诺方案,涉及以某种方式隐藏信息,但是它们不同于加密。
将公钥视为身份的结果是,您可以随时创建新的身份——您只需通过我们的数字签名方案中的 generateKeys 操作创建一个新的新鲜密钥对 sk 和 pk 。这个 pk 就是你可以使用的新公开身份, sk 就是对应的只有你自己知道的,让你代表身份 pk 说话的密钥。实际上,您可以使用 pk 的散列作为您的身份,因为公钥很大。如果你这样做了,那么为了验证消息来自你的身份,你必须检查(1) pk 确实散列到你的身份,以及(2)消息在公钥 pk 下验证。
此外,默认情况下,您的公钥 pk 基本上看起来是随机的,没有人能够通过检查 pk 来揭开您的真实身份。(当然,一旦你开始使用这个身份发表声明,这些声明可能会泄露信息,让别人将 pk 与你的现实世界身份联系起来。我们稍后将对此进行更详细的讨论。)你可以生成一个看起来随机的新鲜身份,就像人群中的一张脸,只受你控制。
分散式身份管理
这就引出了分散式身份管理的概念。您可以自己注册为用户,而不是在系统中有一个注册用户的中心机构。你不需要被发给一个用户名,也不需要通知别人你将使用一个特定的名字。如果您想要一个新的身份,您可以随时生成一个,并且您可以创建任意多个。如果你喜欢被五个不同的名字称呼,没问题!就做五个身份。如果你想暂时匿名,你可以创建一个新的身份,使用一段时间,然后扔掉它。所有这些事情都可以通过分散的身份管理来实现,事实上,这就是比特币处理身份的方式。在比特币行话中,这些身份被称为地址。你会经常在比特币和加密货币的上下文中听到“地址”这个术语,它实际上只是一个公钥的散列。这是某人凭空捏造的身份,是这种分散身份管理方案的一部分。
安全性和随机性
你可以在没有中央权威的情况下产生一个身份的想法似乎是违反直觉的。毕竟,如果别人运气好,生成了和你一样的密钥,他们就不能偷你的比特币吗?
答案是,别人生成和你一样的 256 位密钥的概率很小,我们在实践中不用担心。对于所有的意图和目的,我们保证它永远不会发生。
更一般地说,与初学者认为概率系统不可预测且难以推理的直觉相反,通常情况下情况正好相反——统计理论允许我们精确量化我们感兴趣的事件的可能性,并对这种系统的行为做出自信的断言。
但是有一个微妙之处:概率保证只有在随机生成密钥时才成立。随机性的产生通常是真实系统中的弱点。如果两个用户的计算机使用相同的随机源或使用可预测的随机,那么理论上的保证不再适用。因此,为了确保实际保证与理论保证相匹配,在生成密钥时使用良好的随机性来源是至关重要的。
乍一看,分散的身份管理似乎带来了极大的匿名性和隐私性。毕竟,你可以自己创建一个看起来随机的身份,而不用告诉任何人你的真实身份。但事情没那么简单。随着时间的推移,你创造的身份会产生一系列的陈述。人们看到这些陈述,从而知道拥有这个身份的人做了某种系列的动作。他们可以开始将这些点联系起来,利用这一系列的动作来推断你在现实世界中的身份。随着时间的推移,观察者可以将这些观察联系在一起,并做出推断,从而得出这样的结论,“哎呀,这个人的行为很像乔。也许这个人就是乔。”
换句话说,在比特币中,你不需要明确注册或透露你的真实身份,但你的行为模式本身可能会识别身份。这是比特币等加密货币的基本隐私问题,事实上我们将在第 6 章专门讨论这个问题。
1.5。两种简单的加密货币
现在让我们从密码学转移到加密货币。吃我们的加密蔬菜将在这里开始得到回报,我们将逐渐看到各个部分如何组合在一起,以及为什么像哈希函数和数字签名这样的加密操作实际上是有用的。在本节中,我们将讨论两种非常简单的加密货币。当然,这本书其余部分的大部分内容都需要详细解释比特币的工作原理。
酸奶油
这两种货币中的第一种是 Goofycoin ,这是我们可以想象的最简单的加密货币。搞笑只有两条规则。第一条规则是,一个指定的实体,高飞,可以随时创造新的硬币,这些新创造的硬币属于他。
为了创建一个硬币,高飞生成了一个他以前从未生成过的唯一的硬币 ID uniqueCoinID,并构造了字符串 CreateCoin [uniqueCoinID]。然后,他用自己的秘密签名密钥计算这个字符串的数字签名。这根线,加上高飞的签名,就是一枚硬币。任何人都可以验证硬币包含了高飞的 CreateCoin 语句的有效签名,因此是有效硬币。
Goofycoin 的第二条规则是,无论谁拥有一枚硬币,都可以把它转让给别人。转移硬币不仅仅是将硬币数据结构发送给接收者,而是使用加密操作来完成。
假设高飞想把他创造的一枚硬币转让给爱丽丝。为此,他创建了一个新的语句,写着“将此支付给 Alice ”,其中“this”是一个散列指针,它引用了有问题的硬币。正如我们前面看到的,身份实际上只是公钥,所以“Alice”指的是 Alice 的公钥。最后,高飞在代表语句的字符串上签名。因为高飞是最初拥有那枚硬币的人,所以他必须签署任何花费那枚硬币的交易。一旦这个代表高飞交易的数据结构被他签署,爱丽丝就拥有了硬币。她可以向任何人证明她拥有硬币,因为她可以用高飞的有效签名来呈现数据结构。此外,它指向高飞拥有的一枚有效硬币。所以硬币的有效性和所有权在系统中不言而喻。
一旦爱丽丝拥有了硬币,她就可以依次使用它。为此,她创建了一个语句,上面写着“将这个支付给 Bob 的公钥”,其中“这个”是指向她所拥有的硬币的散列指针。当然,爱丽丝签署了这份声明。任何人在看到这枚硬币时,都可以证明鲍勃是它的主人。他们可以沿着哈希指针链追溯到硬币的创建,并验证在每一步,合法的所有者都签署了一份声明,上面写着“将此硬币支付给[新所有者]”(图 1.10 )。
总而言之,Goofycoin 的规则是:
高飞可以创造新的硬币,只需签署一份声明,说明他正在制造一个具有唯一硬币 ID 的新硬币。
无论谁拥有一枚硬币,都可以通过签署一份声明将它传递给其他人,声明中写道:“将这枚硬币传递给 X”(其中 X 被指定为公钥)。
任何人都可以通过沿着哈希指针链追溯到高飞创造的硬币来验证硬币的有效性,同时验证所有签名。
当然,Goofycoin 有一个基本的安全问题。假设 Alice 通过将她签名的声明发送给 Bob 而将她的硬币传递给 Bob,但是没有告诉其他任何人。她可以再写一份签名声明付给恰克同样的钱。对查克来说,这似乎是一笔完全合法的交易,现在他是硬币的主人了。鲍勃和查克看起来都有理由声称自己是这枚硬币的主人。这被称为双重消费攻击——爱丽丝花了同一个硬币两次。凭直觉,我们知道硬币不应该这样工作。
图 1.10。古菲金币。这里展示的是一枚硬币,它被创造出来(底部)并被使用了两次(中间和顶部)。
事实上,重复消费攻击是任何加密货币都必须解决的关键问题之一。Goofycoin 没有解决双重消费攻击,因此它是不安全的。Goofycoin 很简单,其转移硬币的机制实际上类似于比特币,但因为不安全,所以作为加密货币并不充分。
Scroogecoin
为了解决重复消费问题,我们将设计另一种加密货币,名为 Scroogecoin 。Scroogecoin 是基于 Goofycoin 构建的,但是在数据结构方面有点复杂。
第一个关键思想是,一个名为 Scrooge 的指定实体发布一个包含所有交易历史的只附加分类帐。仅附加属性确保写入此分类帐的任何数据将永远保留在分类帐中。如果分类账真的只是附加的,我们可以用它来防止重复支出,方法是要求所有交易在被接受之前都要记入分类账。这样,如果硬币以前被送到不同的所有者,它将被公开记录。
为了实现这种仅附加的功能,Scrooge 可以构建一个区块链(在第 1.2 节中讨论的数据结构),他将对其进行数字签名。它由一系列数据块组成,每个数据块中有一个交易(实际上,作为一种优化,我们实际上会将多个交易放在同一个数据块中,就像比特币一样。)每个块都有事务的 ID、事务的内容和指向前一个块的散列指针。Scrooge 对最终的哈希指针进行数字签名,该指针绑定了整个结构中的所有数据,他将签名与区块链一起发布(图 1.11 )。
图 1.11。区块链硬币。
在 Scroogecoin 中,只有在 Scrooge 签署的区块链中,交易才有效。任何人都可以通过检查 Scrooge 在记录交易的块上的签名来验证交易是由 Scrooge 签署的。Scrooge 确保他不认可试图双倍花费已经花掉的硬币的交易。
除了让 Scrooge 为每个块签名之外,为什么我们还需要一个带有散列指针的区块链呢?这确保了仅追加属性。如果 Scrooge 尝试添加或删除某个事务,或者更改现有的事务,由于哈希指针的原因,它将影响所有后续的块。只要有人在监视 Scrooge 发布的最新哈希指针,变化就会很明显,很容易捕捉到。在 Scrooge 单独签名块的系统中,你必须跟踪 Scrooge 发出的每一个签名。区块链使得任何两个人都很容易验证他们观察到了由 Scrooge 签名的相同的交易历史。
在 Scroogecoin 中,有两种交易。第一种是 CreateCoins,这就像高飞在 Goofycoin 中制造新硬币的操作一样。对于 Scroogecoin,我们将稍微扩展一下语义,允许在一个事务中创建多个硬币(图 1.12 )。
图 1.12。创建硬币交易记录。这个 CreateCoins 事务创建多个硬币。每枚硬币在交易中都有一个序列号。每枚硬币也有价值;它值一定数量的金币。最后,每枚硬币都有一个接收者,这是一个公钥,当硬币被创建时,它会获取硬币。于是 CreateCoins 创造了多个不同价值的新币,分配给作为初始拥有者的人。我们用 CoinIDs 来指代硬币。硬币 ID 是交易 ID 和该交易中硬币序列号的组合。
图 1.13。PayCoins 交易。
根据定义,如果由 Scrooge 签名,CreateCoins 交易总是有效的。我们不会担心斯克罗吉什么时候有权创造多少硬币,就像我们在《傻瓜硬币》中不担心高飞是如何被选为允许创造硬币的实体一样。
第二种交易是 PayCoins。它会消耗一些硬币(即销毁它们)并创造出总价值相同的新硬币。新硬币可能属于不同的人(公钥)。这笔交易必须由每个投币的人签字。因此,如果你是这笔交易中将要消费的一枚硬币的所有者,那么你需要对交易进行数字签名,以表明你可以使用这枚硬币。
Scroogecoin 的规则规定,如果 PayCoins 交易满足四个条件,则该交易有效:
消费的硬币是有效的,即它们是在以前的交易中创建的。
消费的硬币在之前的交易中没有被消费过。也就是说,这不是一个双重花费的交易。
此交易产生的硬币总价值等于投入的硬币总价值。也就是说,只有守财奴才能创造新的价值。
交易由交易中消费的所有硬币的所有者有效签署。
如果满足这些条件,那么这个 PayCoins 交易是有效的,Scrooge 会接受它(图 1.13 )。他将把它附加到区块链后记入分类账,之后每个人都可以看到这笔交易已经发生。只有在这一点上,参与者才能接受交易已经实际发生。在它被发布之前,它可能会被一个重复花费的交易抢占,即使它被前三个条件验证。
这个系统中的硬币是不可变的——它们永远不会改变、细分或组合。每枚硬币都是在一次交易中被创造出来,然后在另一次交易中被消费掉。但是我们可以通过使用交易获得与能够细分或组合硬币相同的效果。例如,为了细分一枚硬币,Alice 创建了一个新的交易,该交易消耗了一枚硬币,然后产生了两枚总价值相同的新硬币。那两枚新硬币可以转让给她。因此,尽管硬币在这个系统中是不可变的,但它拥有一个没有不可变硬币的系统的所有灵活性。
现在我们来看看 Scroogecoin 的核心问题。Scroogecoin 的作用在于人们可以看到哪些硬币是有效的。它可以防止重复消费,因为每个人都可以看到区块链,看到所有的交易都是有效的,每枚硬币只被消费一次。但问题是斯克罗吉——他的影响力太大了。他不能制造假交易,因为他不能伪造别人的签名。但他可以停止认可一些用户的交易,拒绝向他们提供服务,并使他们的硬币无法兑现。如果 Scrooge 很贪婪(正如他的同名中篇小说所暗示的),他可以拒绝发布交易,除非他们将一些委托交易费转给他。斯克罗吉当然也可以为自己创造尽可能多的新硬币。或者史克鲁奇会对整个系统感到厌倦,完全停止更新区块链。
这里的问题是集权。尽管斯克罗吉对这个系统很满意,但我们作为它的使用者,可能并不满意。虽然 Scroogecoin 似乎是一个不切实际的提议,但许多早期的密码系统研究都假设确实会有一些中央可信机构,通常被称为银行。毕竟,大多数真实世界的货币都有一个可信的发行者(通常是政府造币厂)负责创造货币并决定哪些纸币有效。然而,拥有中央授权的加密货币在实践中基本上没有成功。这有很多原因,但事后看来,很难让人们接受一种中央集权的加密货币。
因此,为了改进 Scroogecoin 并创建一个可行的系统,我们需要解决的核心技术挑战是:我们能去 Scrooge-ify 系统吗?也就是说,我们能摆脱那个集权的吝啬鬼形象吗?我们能否拥有一种加密货币,它在许多方面像 Scroogecoin 一样运行,但没有任何中央可信机构?
要做到这一点,我们需要弄清楚所有用户如何能够同意将一个发布的区块链作为所有交易的权威历史。他们必须都同意哪些事务是有效的,哪些事务已经实际发生。他们还需要能够以分散的方式分配 id。最后,新硬币的铸造也需要分散化。如果我们能解决这些问题,那么我们就能建立一种类似于 Scroogecoin 但没有中央集权的货币。事实上,这将是一个很像比特币的系统。
延伸阅读
史蒂文·利维的《T3 加密》是一部有趣的非技术性作品,讲述了现代加密技术的发展及其背后的人们:
现代密码学是一个理论性很强的领域。密码学家使用数学以正式的方式定义原语、协议及其所需的安全属性,并基于广泛接受的关于特定数学任务的计算难度的假设来证明它们是安全的。在本章中,我们用直观的语言讨论了散列函数和数字签名。对于有兴趣以更数学的方式更详细地探索这些和其他加密概念的读者,请参见:
有关应用加密的介绍,请参见:
仔细阅读定义 SHA-256 的美国国家标准与技术研究所(NIST)标准是一种很好的方式,可以直观地了解加密标准是什么样的:
Levy, Steven. *Crypto: How the Code Rebels Beat the Government—Saving Privacy in the Digital Age*. London: Penguin, 2001.
最后,本文描述了 ECDSA 签名算法的标准化版本:
Katz, Jonathan, and Yehuda Lindell. *Introduction to Modern Cryptography*, second edition. Boca Raton, FL: CRC Press, 2014.
For an introduction to applied cryptography, see:
Ferguson, Niels, Bruce Schneier, and Tadayoshi Kohno. *Cryptography Engineering: Design Principles and Practical Applications*. Hoboken, NJ: John Wiley & Sons, 2012.
Perusing the National Institute of Standards and Technology (NIST) standard that defines SHA-256 is a good way to develop an intuition for what cryptographic standards look like:
NIST. “Secure Hash Standards, Federal Information Processing Standards Publication.” FIPS PUB 180-4\. Information Technology Laboratory, NIST, Gaithersburg, MD, 2008.
Finally, here’s the paper describing the standardized version of the ECDSA signature algorithm:
Johnson, Don, Alfred Menezes, and Scott Vanstone. “The Elliptic Curve Digital Signature Algorithm (ECDSA).” *International Journal of Information Security* 1(1), 2001: 36–63.