posts - 1,  comments - 1,  trackbacks - 0

比特币实现中的哈希算法

   可以说比特币的整个实现就是建立在已有的甚至存在多年的计算机科学领域里的技术或概念的整合,其中哈希算法在比特币中的应用几乎是方方面面,主要包括SHA256和RIPEMD160,比特币将这两个哈希算法的应用组合成两个函数:hash256(d)=sha256(sha256(d))和hash160(d)=ripemd160(sha256(d)),其中d为待哈希的字节数组,两者分别生成256位(32字节)和160位(20字节)的16进制数值。hash256主要用于生成标志符,如区块ID,交易ID等,而hash160主要用于生成比特币地址。

值得一提的是,为什么两个函数都是做两次哈希呢?对于hash160比较认同的答案是ripemd160可以使得生成的地址更短,但是只做ripemd160一次哈希可能会存在安全漏洞所以同时使用sha256起到安全加固;至于hash256使用两次sha256哈希算法的原因来源于sha1算法,由于一次sha1哈希存在被生日攻击(birthday attack)的风险,所以当使用sha1运算时一种有效方式就是做两次sha1哈希,sha256本身并不存在生日攻击漏洞,但是防御性的使用两次sha256哈希借鉴于sha1.

 

默克树

   对于例如SPV这种本身只保存区块头信息而没有交易信息的比特币轻节点是如何验证一笔交易存在于哪一个区块的呢--默克树。(图片来自Mastering bitcoin)

默克树的基本原理就是将叶子节点两两配对做哈希运算(如果叶子节点为奇数,那么将最后一个叶子复制)生成父节点,不断迭代这一过程最终生成唯一的根节点merkle root。如果要验证一个叶子节点是否存在于默克树中只需要传入一个该节点到根节点路径merkle path,而SPV比特币节点只需保存root节点即可。例如,依上图如果想验证k交易是否在于该区块,我们只需要传递路径HL, HIJ, HMNOP和 HABCDEFGH

 

椭圆曲线函数EC(Elliptic Curve)

   比特币使用公钥加密的方式保护个人隐私,并且选择椭圆曲线函数来实现,为什么选EC的综合原因不太清楚,不过至少我觉得它足够安全也足够高效。在比特币的实现上,EC在三个方面发挥作用,密钥对生成,私钥签名和签名验证。

   椭圆曲线的数学表达式为y2=x3+ax+b,椭圆曲线有两个重要特性,1.任意一条非垂直的直线与曲线相交于两点,那该直线必与曲线相交于第三点;2.任意一条非垂直的曲线的切线必与曲线相交于另一点。依据这两个特性,令点Q与P为曲线上的点,得到如下定义,加操作:经过Q和P的直线与曲线相交于第三点R’,那么Q+P=R,其中R为R’点对于x坐标轴的对称点;同理当移动直线使得Q与P点不断逼近并重合为一点D,那么此时直线相切与曲线,根据特征2,与曲线交于一点R’,不难得出D+D=R,其中R为R’点对于x坐标轴的对称点。乘操作:令Q=aP,假设a=3就有:

Q = 3P
Q = P + 2P

这样,乘操作被分解为两个加操作,即交线加和切线加。椭圆曲线图(图片来自Mastering bitcoin):

   现在来看看比特币协议里的椭圆曲线特征,比特币使用的曲线版本中a=0,b=7,即y2=x3+7,同时为了保证函数取值是在一个有限的区间内,所以在实际的EC的应用中会对结果做模运算以期获得只定范围的结果,例如,令模数为7,8 mod 7 = 1,那么x mod 7的取值限制在0到6。比特币协议中的EC有如下的参数设定:

模数 p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F 
基点 G= 04 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798 483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8
序数 n = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141

这些参数的设定经过大量的研究,复杂而精妙,这些都巨大的数值,使得逆向运算不可能,简单的说p为曲线的取值区间,n为我们可以取得的最大私钥值,即私钥必须小于n值,依照上图,计算公钥的过程就是私钥与G点的乘法运算。G点与n的取值依据是,当私钥的取值无限接近于n时与椭圆曲线相交的直线的斜率无限接近于垂直。

该来看看椭圆曲线如何在比特币三个应用方面发挥作用的了。

1.密钥对生成

   正如上面提到的,公钥=私钥xG,就是将G点累加私钥值的次数,这里有个小问题是,上面的曲线图展示的是连续区间内的点分布情况,但当取模以后,我们需要用特定的公式达到目的,设Q和P为曲线上两点,那么两点相加交于曲线上的R点为:

d = (Qy - Py) / (Qx - Px)
Rx = d2 - Px - Qx
Ry = d (Px - Rx) - Py

在两点重合在一个点Q的切线情况下交点R的计算公式变为:

d = (3Qx2 + a) / 2Qy
Rx = d2 - 2Qx
Ry = d (Qx - Rx) - Qy

正如上文提到的曲线乘操作,乘法的过程就是把操作拆分成众多的切线与交线加的操作,即分别使用以上两个公式计算的过程。

2.使用私钥为数据签名

为了隐私保护,交易中使用私钥签名而非私钥来验证一笔unspent的归属,抛开比特币的使用环境,那么签名操作就是使用私钥dA加密一段数据z,具体如下:

(1).选择一个数k范围在大于0小于n(上文中的序数,即私钥的上限)

(2).计算点p(x,y)=k*G

(3).计算r=x mod n ,如果r段是0,返回第一步重新选择

(4).计算s=(z+r*dA)/k mod n,如果s段是0,返回第一步重新计算

(5).生成了数字签名signature(r,s)

3.数字签名验证

比特币交易的链式操作就是不断的私钥签名和公钥验签的过程,有了上面的signature,验签过程如下(令公钥为dP):

(1).验证r和s段都在0和n之前

(2).计算w=s-1 mod n

(3).计算u1=z*w mod n

(4).计算u2=r*w mod n

(5).计算p(x,y)=u1*G+u2*dP

(6).验证r==x mod n,如果等式不成立,那么验证失败

至于验证过程为什么有效,可以参考这里,简单来说就是将公钥dP展开为dA*G,然后将u1和u2定义分别带入求得。

 

posted on 2017-04-19 11:47 earthback 阅读(...) 评论(...) 编辑 收藏