所有货币都需要一些方法来控制供应,并强制执行各种安全属性以防止作弊。在法定货币方面,像中央银行这样的组织控制货币供应量,并对实体货币增加防伪功能。这些安全功能提高了对攻击者的防范能力,但是他们不可能不赚钱地进行伪造。最终,执法对于阻止人们违反制度规则是必要的。
加密数字货币也必须采取安全措施,防止人们篡改系统状态,并避免对不同的人造成不一致的陈述。如果Alice说服Bob,她给他一个数字货币,例如,她不应该说服卡罗尔,向卡罗尔支付同一个数字货币。与法定货币不同的是,加密货币的安全规则需要纯粹在技术上执行,而不依赖于中央机构。
顾名思义,加密货币大量使用加密技术。在加密数字货币系统本身的机制上,密码学提供了一个安全的编码规则体系。我们可以用它来防止篡改和避免模棱两可的陈述,以及将数字协议用于创建编码新货币单位的规则。在我们可以正确理解加密货币之前,我们需要深入研究密码学赖以依赖的基础。
密码学是一个深入的学术研究领域,它利用许多先进的数学技术,以巧妙而微妙的方式理解。幸运的是,比特币只依赖于一些相对简单和知名的加密结构。在本章中,我们将专门研究加密散列和数字签名,证明对构建加密货币非常有用的两个原语。未来章节将介绍更复杂的加密方案,例如零知识证明,用于比特币扩展和修改的提议。
一旦我们学习了必要的加密原语,我们将讨论一些用来构建加密货币的方式。我们将在本章中介绍一些简单的加密货币示例,演示我们需要处理的一些设计挑战。
1.1加密Hash函数
我们需要理解的第一个加密原语是加密散列函数(Hash function)。一个加密散列函数(Hash function)具有以下三个属性:
·它的输入可以是任意大小的字符串。
·它产生一个固定大小的输出。为了使本章中的讨论具体化,我们将假设它是一个256位大小的输出。然而,我们的讨论适用于任何输出大小,只要它足够大。
·这意味着对于给定的输入字符串,可以在合理的时间内计算哈希函数的输出。从技术上讲,计算一个n位字符串的散列应该有一个O(n)的运行时间。
这些属性定义了一个通用哈希函数,一个可用于构建数据结构(如哈希表)的函数。我们将专注于加密散列函数(Hash functions)。对于密码安全的哈希函数,我们将要求它具有以下三个附加属性:(1)防撞;(2)隐匿;(3)友好的谜题。
我们将更仔细地研究每一个属性,以了解为什么有这样一个函数的行为是有用的。研究密码学的读者应该意识到,本书中哈希函数的处理与标准加密教科书有点不同。特别是谜题友好的属性不是对加密哈希函数的一般要求,而是对特定加密货币的有用属性。
防碰撞性:如果不可能找到两个值x和y,使得x ≠ y,而H(x)= H(y),哈希函数H被认为是防碰撞的。
属性1:防碰撞性。我们从加密哈希函数中需要的第一个属性就是它的防碰撞。当两个不同的输入产生相同的输出时,会发生碰撞。如果没有人可以发现碰撞,哈希函数H(.)则具有防碰撞性。
图1.1哈希碰撞。x和y是不同的值,但是当输入到哈希函数H中时,它们产生相同的输出。
请注意,我们说没有人可以找到碰撞,但是我们没有说没有碰撞存在。实际上,我们确实知道碰撞存在的事实,我们可以通过一个简单的计数参数来证明这一点。哈希函数的输入空间包含所有长度的所有字符串,但输出空间只包含特定且固定长度的字符串。因为输入空间大于输出空间(实际上,输入空间是无穷大的,而输出空间是有限的),所以必须有输入字符串映射到相同的输出字符串。事实上,根据“鸽子原则”,映射到任何特定输出的可能输入必然将是非常大的。
图1.2由于输入数量超过输出数量,我们保证必须至少有一个输出,能让哈希函数映射到多个输入。
现在,为了使事情变得更糟,我们说它不可能发现碰撞。然而,有些方法可以保证发现碰撞。考虑以下简单的方法来查找具有256位输出大小的哈希函数的碰撞:挑选2的256次方加1个不同的值,计算它们中每一个的hash值,并检查是否有两个一样的输出。由于我们选择了比可能输出更多的输入,所以当应用哈希函数时,它们中的一些必须发生碰撞。
上面的方法保证会找到碰撞。但是,如果我们选择随机输入并计算哈希值,那么在检查2的256次方加1个输入之前,我们会高概率的发现碰撞。事实上,如果我们随机选择2的130次方加1个输入,结果会有99.8%的概率至少有两个会碰撞。事实上,我们可以通过只是粗略地检查可能的输出数量的平方根来找到冲突,这现象在概率上被称为第二天悖论。在本章末尾家庭作业的问题中,我们将对此进行更详细的研究。
这种碰撞检测算法适用于每个哈希函数。但是,问题在于这需要很长很长的时间。对于具有256位输出的哈希函数,在最坏的情况下,您必须计算哈希函数2的256次方加1次,平均大约2的128次方次。这当然是一个天文数字 ——如果一台计算机每秒计算10,000次hash值,那么需要超过一千万(10的27次方)年的时间来计算2的128次方个hash值!以另一方式来思考这个问题,我们可以这样说,如果人类制造的每一台计算机从宇宙诞生以来都开始计算,到目前为止,他们发现冲突的几率仍然是非常渺小的。如此之小,比地球在接下来的两秒钟内被一颗巨大的流星摧毁的几率都要小得多。
因此,我们看到了一种通用但不切实际的算法来找寻任意哈希函数的碰撞。一个更难的问题是:是否有其他方法可以在特定的哈希函数上使用,以便找到碰撞?换句话说,虽然通用碰撞检测算法是不可行的,但仍然可能有其他一些可以有效地找到特定哈希函数碰撞的算法。
例如,考虑以下哈希函数:
该函数满足我们对散列函数的要求,因为它接受任何长度的输入,返回一个固定大小的输出(256位),并且是有效可计算的。这个函数还有一个找到碰撞的有效方法。请注意,此函数只返回输入的最后256位。一个碰撞的值会是3和3加2的256次方。这个简单的例子说明,尽管我们的通用碰撞检测方法在实践中是不可用的,但至少存在一些确实有效的碰撞检测方法的哈希函数。
然而对于其他哈希函数,我们不知道是否存在这样的方法。我们怀疑他们是抗碰撞的。但是,没有任何哈希函数被证明是抗碰撞的。我们在实践中依赖的加密哈希函数只是人们尝试的功能,真的很难找到碰撞,且从未成功过。在某些情况下,如旧的MD5哈希函数,最终在多年的工作中发现了冲突,导致功能过时,被实用界逐步抛弃。所以我们选择相信那些是抗碰撞的。
应用:消息摘要现在我们知道抗碰撞是什么,合乎逻辑的问题是:抗碰撞有什么用?这里有一个应用:这里有一个应用:如果我们知道抗碰撞哈希函数H的两个输入x和y是不同的,那么可以安全地假定它们的hash值H(x)和H(y)是不同的,如果有人知道一个x和y是不同的,但是具有相同的hash值,这将违反我们的假设,这样的H是抗碰撞的。
这个论据允许我们使用hash输出作为消息摘要。考虑SecureBox,一个经过身份验证的在线文件存储系统,允许用户在下载文件时上传文件并确保其完整性。假设Alice上传的文件很大,想要稍后能验证她下载的文件与上传的文件是一样的。一种方法是在将整个大文件保存在本地,并直接将其与下载的文件进行比较。虽然这样做有效,但它在很大程度上违背了上传它的目的;如果Alice需要访问文件的本地副本以确保其完整性,则可以直接使用本地副本。
无碰撞的哈希为这个问题提供了一个优雅而高效的解决方案。Alice只需要记住原始文件的hash值。当她以后从SecureBox下载文件时,她会计算下载的文件的hash值并将其与存储的文件进行比较。如果哈希值是相同的,那么她可以得出结论,文件确实是她上传的,如果它们不同,那么Alice可以断定该文件已被篡改。因此,记住哈希可以让她在传输过程中或在SecureBox的服务器上检测文件的意外损坏,而且还可以由服务器义务修改文件。面对其他实体潜在的恶意行为,这是加密技术为我们提供保证的核心。
Hash用作消息的固定长度摘要或明确的摘要。这给了我们以一个非常有效的方式来记住我们以前看到的事情,并再次认出它们。而整个文件可能已经是千兆字节长,hash却是固定的长度,在我们的例子中hash函数有256位字节。这大大降低了我们的存储需求。在本章后面和整本书中,我们将看到使用哈希作为消息摘要的有用应用程序。
属性2:隐匿性从哈希函数中得到想要的第二个属性是隐匿。隐匿属性声明如果我们给出hash函数y= H(x)的输出,则没有可行的方法来确定输入x是什么。问题是,该属性不能以声明的形式存在。考虑以下简单的例子:我们要做一个掷硬币的实验。如果硬币翻转的结果是头,我们要宣布字符串的哈希值是“头”。如果结果是尾,那么我们要宣布字符串的哈希值是“尾”。
然后我们问一个没有看到硬币翻转,只看到哈希输出值的对手,让他弄清楚字符串是散列的(我们很快就会知道为什么我们可能想玩这样的游戏)。作为回应,他们将简单地计算字符串“头”和字符串“尾”的哈希值,并且他们可以看到他们被给予了哪一个。所以,在短短几步之后,他们可以弄清楚输入的是什么。
对手能够猜出这个字符串是因为x只有两个可能的值,对手很容易尝试这两个值。为了能够实现隐匿属性,需要注意的是没有特别可能的x值,也就是说x必须在某种意义上从分散的集合中选择。如果从这样一个集合中选择x,那么尝试x值可能有几个值的方法将不起作用。
最大的问题是:当我们想要的值不像我们的“头”和“尾”的实验那样,我们可以实现隐匿的属性吗?幸运的是,答案是肯定的!所以我们或许可以隐藏,甚至一个输入可以不通过将其连接到另一个相关联的输入来传播。我们现在可以稍微更精确地说明我们隐匿的意思(双垂直条‖表示连接)。
隐匿 hash函数H是隐匿的,如果:从具有高的最小熵的概率分布中选择秘密值r,当给定H(r||x)时,找到x是不可行的。
在信息理论中,最小熵是衡量结果可预测性的一个指标,高最小熵捕捉到分