Py学习  »  区块链

区块链中的加密算法:Hash算法之SHA256算法

区块链技术学习 • 4 年前 • 783 次点击  

来自:博客园,作者:勋爵

链接:https://www.cnblogs.com/X-knight/p/9136455.html


为了为保证存储于区块链中的信息的安全与完整,区块链中使用了包含密码哈希函数和椭圆曲线公钥密码技术在内的大量的现代密码学技术,同时,这些密码学技术也被用于设计基于工作量证明的共识算法并识别用户。


在前边的文章中已经系统的讲述了密码学中的哈希算法,在本节,将会给大家介绍Hash算法在区块链中的应用!


概念回顾:


哈希函数:是一类数学函数,可以在有限合理的时间内,将任意长度的消息压缩为固定长度的二进制串,其输出值称为哈希值,也称为散列值。


以哈希函数为基础构造的哈希算法,在现代密码学中扮演着重要的角色,常用于实现数据完整性和实体认证,同时也构成多种密码体制和协议的安全保障。


碰撞是与哈希函数相关的重要概念,体现着哈希函数的安全性,所谓碰撞是指两个不同的消息在同一个哈希函数作用下,具有相同的哈希值。


哈希函数的安全性是指在现有的计算资源(包括时间、空间、资金等)下,找到一个碰撞是不可行的。


区块链中的加密算法······即将开始!


 比特币中的加密算法:


在比特币系统中使用了两个密码学Hash函数,一个是SHA256 和 RIPEMD160。


其中:RIPEMD160主要用于生成比特币地址,我们着重分析比特币中用得最多的SHA256算法。


SHA历史介绍


SHA256属于著名的SHA家族一员。SHA(Secure Hash Algorithm,安全哈希算法)是一类由美国国家标准与技术研究院(NIST)发布的密码哈希函数。


正式名称为SHA的第一个成员发布于1993年,两年之后,著名的SHA-1发布,之后另外的4种变体相继发布,包括SHA224、SHA256、SHA384和SHA512,这些

算法也被称作SHA2。SHA256算法是SHA2算法簇中的一类。对于长度小于264位的消息,SHA256会产生一个256位的消息摘要。SHA256具有密码哈希函数的一般特性。



那么比特币中的SHA256又是何方神圣?它的设计原理是什么?代码又如何实现呢?


SHA256又是何方神圣?


SHA256是构造区块链所用的主要密码哈希函数。无论是区块的头部信息还是交易数据,都使用这个哈希函数去计算相关数据的哈希值,以保证数据的完整性。同时,在比特币系统中,基于寻找给定前缀的SHA256哈希值,设计了工作量证明的共识机制;SHA256也被用于构造比特币地址,即用来识别不同的用户。 


SHA256是一个Merkle-Damgard结构的迭代哈希函数,其计算过程分为两个阶段:消息的预处理和主循环。在消息的预处理阶段,主要完成消息的填充和扩展填充,将所输入的原始消息转化为n个512比特的消息块,之后对每个消息块利用SHA256压缩函数进行处理。这个计算流程是一个迭代计算的过程,当最后1个消息块(第n块)处理完毕以后,最终的输出值就是所输入的原始消息的SHA256值。


在比特币系统中,SHA256算法的一个主要用途是完成PoW(工作量证明)计算。按照比特币的设计初衷,PoW要求钱包(节点)数和算力值大致匹配,因为需要通过CPU的计算能力来进行投票。然而随着人们对SHA256的计算由CPU逐渐升级到GPU,到FPGA,直至到ASIC矿机,这使得节点数和PoW算力也渐渐失配。解决这个问题的一个思路是引入另外的一些哈希函数来实现PoW。


scrypt算法最早用于基于口令的密钥生成,该算法进行多次带参数的SHA256计算,即基于SHA256的消息认证码计算,这类计算需要大量的内存支持。采用scrypt算法进行PoW计算,将PoW计算由已有的拼算力在一定程度上转化为拼内存,能够使得节点数和PoW的计算力的失配现象得到缓解。莱特币就是采用scrypt算法完成PoW计算的。


SHA3算法是2012年10月由NIST所选定的下一代密码哈希算法。在遴选SHA3算法过程中人们提出了一系列的候选算法,包括了BLAKE、Grostl、JH、Keccak、Skein、ECHO、Luffa、BMW、CubeHash、SHAvite、SMID等,最后胜出的是Keccak算法。达世币(DASH,原名暗黑币,DarkCoin)定义了顺序调用上述11个哈希算法的X11算法,并利用这个算法完成PoW计算。同样,由于采用了X11算法,使得节点数和PoW的计算力能够保持一定程度上的匹配。


设计原理


下面介绍SHA算法计算消息摘要的原理:


对于任意长度(按bit计算)的消息,SHA256都会产生一个32个字节长度数据,称作消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据是否发生改变,即验证其完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。


SHA算法有如下特性: 

1. 不可以从消息摘要中复原信息; 
2. 两个不同的消息不会产生同样的消息摘要。


一、术语和概念


(一)位(Bit),字节(Byte)和字(Word)

SHA始终把消息当成一个位(bit)字符串来处理。本文中,一个“字”(Word)是32位,而一个“字节”(Byte)是8位。比如,字符串“abc”可以被转换成一个位字符串:01100001 01100010 01100011。它也可以被表示成16进制字符串:0x616263.


二、SHA256算法描述


(一)补位

信息必须进行补位,以使其长度在对512取模以后的余数是448。也就是说,(补位后的消息长度)Q2 = 448。即使长度已经满足对512取模后余数是448,补位也必须要进行。


补位是这样进行的:先补一个1,然后再补0,直到长度满足对512取模后余数是448。总而言之,补位是至少补一位,最多补512位。以信息“abc”为例显示补位的过程。 

原始信息:01100001 01100010 01100011

补位第一步:0110000101100010 01100011 1

首先补一个“1”

补位第二步:0110000101100010 01100011 10…..0

然后补423个“0”


我们可以把最后补位完成后的数据用16进制写成下面的样子 





    
61626380 0000000000000000 00000000

00000000 0000000000000000 00000000

00000000 0000000000000000 00000000

00000000 00000000


现在,数据的长度是448了,我们可以进行下一步操作。


(二)补长度

所谓的补长度是将原始数据的长度补到已经进行了补位操作的消息后面。通常用一个64位的数据来表示原始消息的长度。如果消息长度不大于2^64,那么第一个字就是0。在进行了补长度的操作以后,整个消息就变成下面这样了(16进制格式) 


61626380 0000000000000000 00000000

00000000 0000000000000000 00000000

00000000 0000000000000000 00000000

00000000 0000000000000000 00000018


(三)使用的常量

在SHA256算法中,用到64个常量,这些常量是对自然数中前64个质数的立方根的小数部分取前32bit而来。这64个常量如下:


       428a2f98 71374491 b5c0fbcf e9b5dba5 
        3956c25b 59f111f1 923f82a4 ab1c5ed5 
        d807aa98 12835b01 243185be 550c7dc3 
        72be5d74 80deb1fe 9bdc06a7 c19bf174 
        e49b69c1 efbe4786 0fc19dc6 240ca1cc 
        2de92c6f 4a7484aa 5cb0a9dc 76f988da 
        983e5152 a831c66d b00327c8 bf597fc7 
        c6e00bf3 d5a79147 06ca6351 14292967 
        27b70a85 2e1b2138 4d2c6dfc 53380d13 
        650a7354 766a0abb 81c2c92e 92722c85 
        a2bfe8a1 a81a664b c24b8b70 c76c51a3 
        d192e819 d6990624 f40e3585 106aa070 
        19a4c116 1e376c08 274877434b0bcb5  
        391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3 
        748f82ee 78 a5636f 84c87814 8cc70208 
        90befffa a4506ceb bef9a3f7 c67178f2


该算法使用了六种基本逻辑函数,由64 步迭代运算组成。每步都以256-bit 缓存值ABCDEFGH 为输入,然后更新缓存内容。 

每步使用一个32bit 常数值Kt 和一个32bit Wt。 


(四)六种基本逻辑函数 


CH(x, y, z) = (x AND y) XOR ( (NOT x) AND z)  
MAJ( x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z)  
BSIG0(x) = ROTR^2(x) XOR ROTR^13(x) XOR ROTR^22(x)  
BSIG1(x) = ROTR^6(x) XOR ROTR^11(x) XOR ROTR^25(x)  
SSIG0(x) = ROTR^7(x) XOR ROTR^18(x) XOR SHR^3(x)  
SSIG1(x) = ROTR^17(x) XOR ROTR^19(x) XOR SHR^10(x) 


其中 x、y、z皆为32bit的字。 
ROTR^2(x)是对x进行循环右移2位。


运算逻辑,如图所示:



(五)计算消息摘要


基本思想:就是将消息分成N个512bit的数据块,哈希初值H(0)经过第一个数据块得到H(1),H(1)经过第二个数据块得到H(2),……,依次处理,最后得到H(N),然后将H(N)的8个32bit连接成256bit消息摘要。


I、哈希初值H(0)

SHA256算法中用到的哈希初值H(0)如下:





    
     H(0)0 = 6a09e667 
        H(0)1 
bb67ae85  
        H(0)2 
3c6ef372 
        H(0)3 
a54ff53a 
        H(0)4 
510e527
        H(0)5 
9b05688c 
        H(0)6 
1f83d9ab 
        H(0)7 
5be0cd19


注:这些初值是对自然数中前8个质数3、5、7、11等的平方根的小数部分取前32bit而来。


II、 计算过程中用到的三种中间值


    16432bit字的message schedule标记为w0、w1、…、w63。
    2832bit字的工作变量标记为a、b、c、d、e、f、g。
    3、包括832bit字的哈希值标记为H(i)0、…、H(i)7



III、 工作流程


原始消息分为N个512bit的消息块。每个消息块分成16个32bit的字标记为M(i)0、M(i)1、M(i)2、…、M(i)15然后对这N个消息块依次进行如下处理





    
            For i=1 to N
        1)   For t = 0 to 15 
                     Wt = M(i)t 
                 For t = 16 to 63 

                     Wt = SSIG1(W(t-2)) + W(t-7) + SSIG0(t-15) + W(t-16
         2)  a = H(i-1)0 
                b = H(i-1)1 
                c = H(i-1)2 
                d = H(i-1)3 
                e = H(i-1)4 
                f = H(i-1)5 
                g = H(i-1)6 
                h = H(i-1)7
         3For t = 0 to 63 
                    T1 = h + BSIG1(e) + CH(e,f,g) + Kt + Wt 
                    T2 = BSIG0(a) + MAJ(a,b,c) 
                    h = g
                    g = f 
                    f = e 
                    e = d + T1 
                    d = c 
                    c = b 
                    b = a 
                    a = T1 + T2

           4)H(i)0 = a + H(i-1)0 
                 H(i)1 = b + H(i-1)1 
                 H(i)2 = c + H(i-1)2 
                 H(i)3 = d + H(i-1)3 
                 H(i)4  = e + H(i-1)4 
                 H(i)5 = f + H(i-1)5  
                 H(i)6 = g + H(i-1)6 
                 H(i)7 = h + H(i-1)7


对N个消息块依次进行以上四步操作后将最后得到的H(N)0、H(N)1、H(N)2、…、H(N)7串联起来即可得到最后的256bit消息摘要。


代码实现


I 调用库函数:


下面的示例计算 data 的SHA256哈希值,并将它存储在 result 中。此示例假定存在一个预定义的常数 DATA_SIZE:


C#的代码示例:


1 byte[] result;
2 byte[] data = new byte[DATA_SIZE];
3 SHA256 shaM = new SHA256Managed();
4 result  = shaM.ComputeHash(data);


Java的代码示例:


1 ubyteresult[];
2 ubyte data[] = new ubyte[DATA_SIZE];
3 SHA256 shaM  = new SHA256Managed();
4 result  = shaM.ComputeHash(data);


SQL的代码示例:


SELECT sha2(data,256);


PHP的代码示例:


$result=hash('sha256'

$data);


II 自己编写代码实现:

前面我们已经说明了SHA-256的计算过程,接下来我们将这一过程代码化。同样的首先定义一个上下文的结构。


 1 /** 定义SHA-256哈希操作的内容信息结构体 */
 2 typedef struct SHA256Context {
 3   uint32_t Intermediate_Hash[SHA256HashSize/4]; /* 信息摘要 */
 4   uint32_t Length_High;                         /* 按位计算的信息长度高字 */
 5   uint32_t Length_Low;                          /* 按位计算的信息长度低字 */
 6   int_least16_t Message_Block_Index;            /* 信息分组数组的索引 */
 7   uint8_t Message_Block[SHA256_Message_Block_Size];/* 512位信息分组 */
 8   int Computed;                                 /* 摘要计算标识 */
 9   int Corrupted;                                /* 信息摘要损坏标识 */
10 } SHA256Context;


实现SHA256Context结构的初始化,为后续的计算过程做准备。


 1 static SHAStatusCode SHA224_256Reset(SHA256Context *context, uint32_t *H0)
 2 {
 3   if (!context) return shaNull;
 4   context->Length_High = context->Length_Low = 0;
 5   context->Message_Block_Index = 0;
 6    context->Intermediate_Hash[0] = H0[0];
 7   context->Intermediate_Hash[1] = H0[1];
 8   context->Intermediate_Hash[2] = H0[2];
 9   context->Intermediate_Hash[3] = H0[3];
10   context->Intermediate_Hash[4] = H0[4];
11   context->Intermediate_Hash[5] = H0[5];
12   context->Intermediate_Hash[6] = H0[6];
13   context->Intermediate_Hash[7] = H0[7];
14   context->Computed = 0;
15   context->Corrupted = shaSuccess;
16   return shaSuccess;
17 }


接下来实现信息分组的输入,这个函数接受一个字节数组作为下一个消息分组以便进行处理。


 1 SHAStatusCode SHA256Input(SHA256Context *context, const uint8_t *message_array,unsigned int length)
 2 {
 3   if (!context) return shaNull;
 4   if (!length) return shaSuccess;
 5   if (!message_array) return shaNull;
 6   if (context->Computed) return context->Corrupted = shaStateError;
 7   if (context->Corrupted) return context->Corrupted;
 8   while (length--)
 9   {
10     context->Message_Block[context->Message_Block_Index++] =*message_array;
11     if ((SHA224_256AddLength(context, 8) == shaSuccess) &&(context->Message_Block_Index == SHA256_Message_Block_Size))
12       SHA224_256ProcessMessageBlock(context);
13     message_array++;
14   }
15   return context->Corrupted;
16 }


当然还需要一个消息处理及最终摘要输出的函数,这个函数将返回一个224位或者256位的信息摘要到调用者给定的Message_Digest数组。返回的信息摘要,第一个元素索引为0,最后一个元素索引为27(SHA-2244)或者31(SHA-256)。


 1 static SHAStatusCode SHA224_256ResultN(SHA256Context *context,uint8_t Message_Digest[ ], int HashSize)
 2 
{
 3   int i;
 4   if (!context) return shaNull;
 5   if (!Message_Digest) return shaNull;
 6   if (context->Corrupted) return context->Corrupted;
 7   if (!context->Computed)
 8     SHA224_256Finalize(context, 0x80);
 9   for (i = 0; i 10     Message_Digest[i] = (uint8_t)(context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ));
11   return shaSuccess;
12 }



区块链中的哈希指针链


哈希指针是一类数据结构,除了包含通常的指针外,还包含一些数据信息以及与这些信息相关的密码哈希值,这就使得正常的指针可用于取回信息,哈希指针用于验证信息是否发生改变。区块链就可以看作一类使用哈希指针的链表,如下图所示。


哈希指针链


这个链表链接一系列的区块,每个区块包含数据以及指向表中前一个区块的指针。区块链中,前一个区块指针由哈希指针所替换,因此每个区块不仅仅告诉前一个区块的位置,也提供一个哈希值去验证这个区块所包含的数据是否发生改变。


可以利用区块链去构造一个防篡改的日志系统。在这个系统中,基于区块链的日志节点链表被用来存储数据,链表节点通过哈希指针链接,新节点追加在日志链表的尾部。同时,日志链表的头哈希指针所指向的头节点内容不可改变。若日志链表中的某个节点的数据被篡改,则系统能够检测出来。


假定攻击者改变了节点k的数据,由于其后继节点k+1存储了节点k的哈希值,由于密码哈希函数的抗碰撞性,通过简单地计算节点k的数据的哈希值,就能发现计算出的值与节点k+1的哈希指针值不一致,于是可以断定节点k或节点k+1的信息被篡改。当然,攻击者可能能够连续改变前一个节点的哈希值来掩盖不同,但这个策略在处理日志链表的头节点时将会失败。特别地,一旦将链表头部的哈希指针存储在不能改变的地方,攻击者将不能改变任何节点而不被发觉。


因此,若攻击者想在日志链表中的任意位置改变数据,为保持一致性,他必须向表头方向修改所有的哈希指针,最终由于不能改变链表头部而失败。因此,只需单个哈希指针,基本上就能保证整个链表的哈希值的一致性,从而达到防篡改的目的。



●编号226,输入编号直达本文

●输入m获取文章目录


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/35304
 
783 次点击