SHA-256算法原理和硬件实现

#声明:本文创作内容含代码均为个人创作所得,允许学习、传阅,不得用于商业用途#

#本文包含SHA-256从算法到硬件实现的全部# 

#实践会让你更加透彻理解算法原理,才能让知识灌入大脑#

在开始前请先容许我MMP 联邦信息处理标准,因为你们给出的PDF有误,导致我denbug到了凌晨两点半。

SHA-256整个算法架构和SM3一样,第一步拼接,第二步迭代,区别是迭代过程中的对数据逻辑运算不一样,这里算法我就简单说明,大家可以和专栏里面SM3对比起来看。

一:背景

SHA(Secury  Hash Standrad)是一种信息摘要码(MDC),主要应用信息传输,加解密,存储等信息领域,简单理解就是任何一组消息,都可以压缩成一个固定位宽的长度数据,只要消息中的任何1 比特发生改变,该消息的hash值就会发生变化。

比如,我们从某网页下载一个软件包,一般该网页会给出这个软件包的摘要值,当我们下载后,可以计算下载后软件包的摘要值,并与官网比较,看是否是一样,如果一样,则可以认为下载过程中,没有出现信息错误,如果摘要值不一样,则信息在通过网络传输出现了误码情况

SHA有多种标准,如SHA-1,SHA-256,SHA-384,SHA-512等,这些不同标准的区别在于生成的摘要值的长度不一样,SHA-256生成摘要长度为256比特,这些不同标准的算法实现不一样,但算法架构一样,那为啥出现这么多种标准呢?因为应用场景对安全性的要求不一样;我们可以简单认为一段消息的摘要值是确定的,消息改变了,摘要值也改变,但如果消息很长,其中某些比特出现错位,其消息摘要值不变,这种概率是存在的,但很小;不同标准对应的概率不一样。

二 算法说明

我们以SHA-256来说明

第一步填充:

目的是为了确保消息的bit长度是512的倍数

先把bit 1 附加在消息尾部,然后再附加上k个 bit 0,使得 L+ K+ 1 = 448 mode 512,这里L是消息二进制的bit长度;然后再把L转化为一个64 bit 二进制数,如果二进制数位宽不够,则高位补0,然后把这 64bit 附加在上一步操作后的消息的尾部

例如下图:

说明:这里我们消息为 ‘abc’,一个三个字母,这里每个字母的ASIIC的表示是8个bit,那消息的bit 长度是24,十进制24用二进制表示就是11000,那填充后的消息长度是:8+8+8+423+64 = 512

第二步模块分解:

消息按照每512bit来作为一组,我们把每个512bit 分成16个字(32bit),根据这16个字,我们迭代出64个字W0,W1, ...,W63,  即{W0, W1,W2...,W15} = 512bit block消息

其中W0~ W15就是一组消息里面的16个字,然后剩下的W16~W63根据下面公式迭代:

                        W_{i}=\sigma_1{}(W_{i-2})+W_{i-7}+\sigma_0{}(W_{i-15})+W_{i-16}           i=16.17...63

每一组512bit消息都要这么多迭代,假设有M个512bit消息,那就会有M组Wi,其中i=0~~63;为啥要每组要迭代出64个Wi呢?因为每组512bit消息需要经过64轮迭代,每一轮迭代需要一个Wi,所以上面每512bit消息生成了64个Wi;

说明:公式里的sigma0,sigma1是一个函数,具体形式后面给出

第三步迭代计算

填充后的消息块我们用M^{^{0}}M^{^{1}},...,M^{^{n}}来表示,每个M是512bit,前个 i 个M数据块对应的hash值为H^{^{n}},初始的256bit  hash值用如下8个字来表示,上角标对应数据块标记,下角标对应hash的字

每一轮的迭代过程如下:

 For k = 16 to 63

       W_{i}=\sigma_1{}(W_{i-2})+W_{i-7}+\sigma_0{}(W_{i-15})+W_{i-16}      注释:先迭代出所需的64个Wi

(a,b,c,d,e,f,g,h)=(H_{0},H_{1},H_{2},H_{3},H_{4},H_{5},H_{6},H_{7},)  注释:将上一轮的hash赋给中间变量

For  i = 0 to 63 然后再计算本轮的hash

                                公式 1-1

 (H_{0},H_{1},H_{2},H_{3},H_{4},H_{5},H_{6},H_{7},) +=(a,b,c,d,e,f,g,h) 

注释:本轮的hash值等于上一轮的hash对应加上a, b, c, d, e, f, g, h 

经过N次迭代后得到最终的hash值 H----256bit

上式 1-1 里面的K是64个32bit的常数,他们是前64个质素的立方根的小数部分的前32bit,

举个例子,第一个质数为2,立方根为1.25992104...., 我们将其小数部分用二进制数表示,为

428a2f98。因为这64个字是常数,所以直接拿来用就可以,如下图:

且上述初始化hash值为前8个质数的平方根的小数部分的32bit,也是这么计算得来的

上式1-1里的那些函数的具体形式如下:

其中RotR(X, n)是X循环右移n位,ShR(X,n)是右移n位,切记注意注意注意

^ 为 bit 与,\bigoplus 为bit异或,- 为 bit 取反

注意注意注意:前面公式中的 + 是基于模2^32 的加法运算,这有区别于GF(2, 32),切记,切记,切记,而 与、异或、取反是基于GF(2, 32)

三 python 代码如下

说明:python代码中消息可以是字符,也可以是16进制数,参数mode来配置,

python中给出了三个消息的例子,读者可以在代码中加入打印,来调试代码

通过抄写来理解算法原理

class Sha256:
    """  all function is based on sting, thr string is 32bit hex value """

    K_PARAMETER = ['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', '2748774c', '34b0bcb5', '391c0cb3', '4ed8aa4a', '5b9cca4f', '682e6ff3',
                   '748f82ee', '78a5636f', '84c87814', '8cc70208', '90befffa', 'a4506ceb', 'bef9a3f7', 'c67178f2']

    H_PARAMETER = ['6a09e667', 'bb67ae85', '3c6ef372', 'a54ff53a', '510e527f', '9b05688c','1f83d9ab', '5be0cd19']

    def shift_right(self, x, k):
        # suppose data is 32 bit -- word
        k = k % 32
        return (x >> k) & 0xFFFFFFFF

    def rotate_right(self, x, k):
        # suppose data is 32 bit --word
        k = k % 32
        return  ((x >> k) & 0xFFFFFFFF) | ((x & 0xFFFFFFFF) << (32-k)) & 0xFFFFFFFF

    def ch_function(self, x, y, z):
        tmp = (x & y & 0xFFFFFFFF) ^ ( (0xFFFFFFFF - (x & 0xFFFFFFFF)) & z) & 0xFFFFFFFF
        return tmp

    def mag_function(self, x, y, z):
        tmp = (x & y & 0xFFFFFFFF) ^ (x & z & 0xFFFFFFFF) ^ (y & z & 0xFFFFFFFF) & 0xFFFFFFFF
        return tmp

    def sigma_0_b(self, x):
        tmp = self.rotate_right(x, 2) ^ self.rotate_right(x, 13) ^ self.rotate_right(x, 22) & 0xFFFFFFFF
        return tmp

    def sigma_1_b(self, x):
        tmp = self.rotate_right(x, 6) ^ self.rotate_right(x, 11) ^ self.rotate_right(x, 25) & 0xFFFFFFFF
        return tmp

    def sigma_0_s(self, x):
        tmp = self.rotate_right(x, 7) ^ self.rotate_right(x, 18) ^ self.shift_right(x, 3) & 0xFFFFFFFF
        return tmp

    def sigma_1_s(self, x):
        tmp = self.rotate_right(x, 17) ^ self.rotate_right(x, 19) ^ self.shift_right(x, 10) & 0xFFFFFFFF
        return tmp

    def str2byte(self, string):
        hex_array = []
        string_encode = string.encode()
        for char in string:
            hex_array.append(int(ord(char)))
        return hex_array

    def num2byte(self, string):
        hex_array = []
        string_len = len(string)
        if (string_len % 2):
            string = '0' + string
        for i in range(int(string_len / 2)):
            hex_array.append(int(string[i * 2:i * 2 + 2], 16))
        return hex_array

    def padding(self, string, num):
        if (num == 0):
            data = self.str2byte(string)
            bit_len = len(string) * 8  # each num is 8 bit
        else:
            data = self.num2byte(string)
            bit_len = len(data) * 8  # each num is 8 bit
        string_0_len = 512 - (bit_len + 1 + 64) % 512
        string_0 = '1' + ('0' * string_0_len)  # internal bin
        string_64_bit = '0' * (64 - (len(bin(bit_len)) - 2)) + bin(bit_len)[2:]  # last 64bit
        padding_bit = string_0 + string_64_bit
        for i in range(int(len(padding_bit) / 8)):  # message to 16 word(32bit)
            data.append(int(padding_bit[8 * i:8 * (i + 1)], 2))
        # translate to word, cause we are based on word to calculate
        message = ['']
        for i in range(int(len(data)/4)):
            message.append('{:02x}'.format(data[4*i]) + '{:02x}'.format(data[4*i+1]) + '{:02x}'.format(data[4*i+2]) + '{:02x}'.format(data[4*i+3]))
        return message[1:]

    def sha_iteration(self, date, mode):
        message = self.padding(date, mode)
        len_message = int(len(message)/16)  # how much num of 512bit are string

        hash_value = [0] * (len_message * 8 + 8)
        # hash_value[0:8] = [a, b, c, d, e, f, g, h]
        hash_value[0:8] =[int(self.H_PARAMETER[0], 16), int(self.H_PARAMETER[1], 16), int(self.H_PARAMETER[2], 16), int(self.H_PARAMETER[3], 16), int(self.H_PARAMETER[4], 16), int(self.H_PARAMETER[5], 16), int(self.H_PARAMETER[6], 16), int(self.H_PARAMETER[7], 16)]
        for i in range(len_message):
            [a, b, c, d, e, f, g, h] = hash_value[8*i:8*(i+1)]
            message_block = message[16*i:16*(i+1)]
            w = [''] * 64
            w[0:16] = message_block
            for j in range(16, 64):
                w[j] = '{:08x}'.format(self.sigma_1_s(int(w[j-2], 16)) + int(w[j-7], 16) + self.sigma_0_s( int( w[j-15], 16))  + int(w[j-16], 16) & 0xFFFFFFFF)

            for t in range(64):
                T1 = ( h + self.sigma_1_b(e) + self.ch_function(e, f, g) + int(self.K_PARAMETER[t], 16) + int(w[t], 16)  ) & 0xFFFFFFFF
                T2 = (self.sigma_0_b(a) + self.mag_function(a, b, c)) & 0xFFFFFFFF
                h = g
                g = f
                f = e
                e = (d + T1) & 0xFFFFFFFF
                d = c
                c = b
                b = a
                a = (T1 + T2) & 0xFFFFFFFF
                # print(t, hex(a), hex(b), hex(c), hex(d), hex(e), hex(f), hex(g), hex(h))

            hash_value[(i + 1) * 8 + 0] = (a + hash_value[8 * i + 0] ) & 0xFFFFFFFF
            hash_value[(i + 1) * 8 + 1] = (b + hash_value[8 * i + 1] ) & 0xFFFFFFFF
            hash_value[(i + 1) * 8 + 2] = (c + hash_value[8 * i + 2] ) & 0xFFFFFFFF
            hash_value[(i + 1) * 8 + 3] = (d + hash_value[8 * i + 3] ) & 0xFFFFFFFF
            hash_value[(i + 1) * 8 + 4] = (e + hash_value[8 * i + 4] ) & 0xFFFFFFFF
            hash_value[(i + 1) * 8 + 5] = (f + hash_value[8 * i + 5] ) & 0xFFFFFFFF
            hash_value[(i + 1) * 8 + 6] = (g + hash_value[8 * i + 6] ) & 0xFFFFFFFF
            hash_value[(i + 1) * 8 + 7] = (h + hash_value[8 * i + 7] ) & 0xFFFFFFFF

        return hash_value[len_message * 8:len_message*8 +8]




if __name__ == '__main__':
    get_class = Sha256()
    # hash_256 = get_class.sha_iteration('abc', 0)
    # hash_256 = get_class.sha_iteration('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 0)
    hash_256 = get_class.sha_iteration('a'* 1000000, 0)
    for i in range(len(hash_256)):
        print(hex(hash_256[i]))
    print(hash_256)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值