计算机存储数据的方式为 0 或 1 ,最小单元为比特 (bit) 。2 的 256 次方可表示完宇宙中任何可观察到粒子,理论上在计算机中通过 256 个高低电平阵列可模拟所有的数据。

加密领域的散列函数

散列函数 (hash function) 接受输入并生成特定长度的输出。数据被散列函数处理的过程我们定义为散列化 (Hashing),散列函数的输出称为散列,不同特征的散列函数指的是不同长度的输出。下文中提到的散列函数均指使用一种输出长度为 256 位(32字节)的散列函数,当然也存在比这更短或更长输出的散列函数。另外存在多种输出长度为 256 位的散列函数,在这里我们不关心具体使用哪种散列函数。

不同大小的文件,譬如视频文件、邮件文本等通过散列化之后输出长度固定为 256 位的二进制,在某种层度上散列化近似不可逆的压缩。安全的加密散列函数一个重要的特点就是不可逆,这意味着从输出中几乎不可能,或者说在数学和计算上不可能推导出原始输入。也就是说通过散列不能找出散列函数接收的输入数据。

通过散列函数可以达成两个优势:

  1. 不同大小的文件散列化的过程需要的时间基本一样
  2. 散列化的结果是随机的

在这里解释第二点,譬如我们以字符串 passowrd1 和 passowrd2 作为散列函数的输入,虽然输入只有最后一个字符有差异,但输出结果却截然不同。

如果你想尝试找出一个散列的原始输入,只能通过尝试所有的输入组合进行散列化直到找出与目标散列一致的输出,但问题是如果输入为随机性,要找到这个随机数将要消耗无穷大的时间。

虽然从输出反向推导输入变得特别困难并耗费巨大的时间,但正向推导从输入到散列化结束仅仅需要极短的时间,我们就可以通过一个散列函数,不到一秒的时间就能把任何大小容量的输入散列出一个固定长度的输出。

抗碰撞是加密散列函数属性之一,也就是说两个不同的输入通过散列函数处理之后不可能有相同的输出。

区块链和散列

区块链的多个技术细节都用到了散列:区块链地址、交易散列、块散列、工作量证明、数据压缩、默克尔树等。

  1. 区块链中的地址就是通过公钥散列而来,以太坊的账户地址通过 Keccak-256 散列推导而来,比特币地址通过 SHA2-256RIPEMD160 散列算法推导计算。在区块链中,散列函数抗碰撞性的重要性体现在如果两个人生成相同的地址,那么他们俩都可以花掉在这个地址上的钱。
  2. 签名是区块链的基础。就像支票签名一样,加密的签名校验交易的有效性。区块链通过地址对应的私钥和交易数据散列化实现签名。
  3. 现实中我们表示某笔交易可能会这样描述:甲给在某年某月给乙转一个比特币,但在区块链中,交易散列 (transaction hash) 也就是我们经常提到的 txid 表示区块链中的交易。通过交易 ID ,我们可以在区块浏览器上获取交易详情。
  4. 同样,由于散列函数抗碰撞特性,不同的区块数据(输入)散列之后不可能生成相同的散列串,区块使用散列串作为唯一标识。
  5. 工作量证明 (PoW) 算法在比特币中就是找到一个块的散列哈希。这个散列串必须满足小于动态调整(为了保证出块速度在十分钟左右)的指定散列。比特币使用 Hashcash 工作量证明算法(最初用来防止垃圾邮件的工作量证明算法),采用区块头和从 0 开始的计数器作为输入散列输出,在输出结果没有满足动态调整(原始的 Hashcash 实现中,它的要求是 “一个哈希的前 20 位必须是 0”)的散列之前,计数器不断增加。计数器称为 nonce 。当 nonce 被找出之后,区块校验马上被确认并全网广播,形成最新共识和最新账本。
  6. 散列可以把数据不可逆地压缩成指定长度的散列串。因为数据在区块链中永久存储,所以大容量的数据在区块链账本中存储在经济学角度上来看是非常昂贵的。因为散列有压缩数据的特性,所以区块链在电子数据领域具有时间戳的使用场景(可以衍生到版权),例如你在某个时间把照片作为输入生成散列串记录在区块链中,一年后你可以通过提供该照片作为输入散列后和之前之前散列出来的散列串对比,如果能匹配上则证明你在一年前就拥有这张照片。
  7. 默克尔树在区块链中把块中的所有交易 ID 通过散列后得到根散列串,可应用在 SPV 中。

总结

散列用于安全地标识和鉴定数据。安全的加密散列函数必须具备不可逆推导、快速正向推导和抗碰撞特性。256 比特已经超越宇宙中的原子数,结合固定长度输出的特性,散列可以作为任何数据的唯一标识。散列可以使用 64 个字符表示(16进制),这足以用来作为数据的标示符,散列在区块链中用来标识块、交易和地址。所以,这样的特性可以解决电子数据领域的版权验证问题。

推荐阅读