sha256 长度大于 56 时输出错误

sha256 incorrect output when the length is more than 56

我正在尝试学习 sha256 的每一步,所以我用 python 编写了一个代码,它使用二进制字符串而不是数学运算来完全理解它。

当输入少于 56 个字符时,代码工作正常,但当输入等于或多于 56 个字符时,输出不正确:

例如当输入是: ba7816bf8f01cfea414140de5dae2223bda12ae1a5324sfdserew3ra

而不是1caa150674ab1aed030dc69f9b86dbcbc412e6e1dd20344eeaa21687acae7789

我得到8492782cc396d4454980c9b63f127c5730da7d838822f8f37b1c7705d2630b88

代码:

Operations.py

  class Operations:
        def add(self, data1, data2):
              x = data1
              y = data2

              carry = 0
              result = ""

              for i in range(len(data1) -1, -1, -1):
                    r = carry
                    r += 1 if x[i] == '1' else 0
                    r += 1 if y[i] == '1' else 0
                    result = ('1' if r % 2 == 1 else '0') + result
                    carry = 0 if r < 2 else 1

              if carry != 0: result = '1' + result

              return result[-len(data1):]

        def xor(self, data1, data2):
              result = ""
              for i in range(len(data1)):
                    temp1 = data1[i]
                    temp2 = data2[i]
                    if (temp1 == "0" and temp2 == "0") or (temp1 == "1" and temp2 == "1"):
                          result += "0"
                    else:
                          result += "1"

              return result

        def shiftRight(self, data, turn):
              result = "0" * turn + data
              return result[:len(data)]

        def rotateRight(self, data, turn):
              result = None
              for i in range(turn):
                    if result:
                          temp = result[-1]
                          result = (temp + result)[:len(data)]
                    else:
                          temp = data[-1]
                          result = (temp + data)[:len(data)]

              return result

Functions.py

  from operations import Operations

  class Functions(Operations):
        def sigma0(self, data): # Lowercase sigma
              temp1 = self.rotateRight(data, 7)
              temp2 = self.rotateRight(data, 18)
              temp3 = self.shiftRight(data, 3)
              result = self.xor(temp3, self.xor(temp1, temp2))
              return result

        def sigma1(self, data): # Lowercase sigma
              temp1 = self.rotateRight(data, 17)
              temp2 = self.rotateRight(data, 19)
              temp3 = self.shiftRight(data, 10)
              result = self.xor(temp3, self.xor(temp1, temp2))
              return result

        def gamma0(self, data): # Uppercase sigma
              temp1 = self.rotateRight(data, 2)
              temp2 = self.rotateRight(data, 13)
              temp3 = self.rotateRight(data, 22)
              result = self.xor(temp3, self.xor(temp1, temp2))
              return result

        def gamma1(self, data): # Uppercase sigma
              temp1 = self.rotateRight(data, 6)
              temp2 = self.rotateRight(data, 11)
              temp3 = self.rotateRight(data, 25)
              result = self.xor(temp3, self.xor(temp1, temp2))
              return result

        def choice(self, x, y, z):
              result = ""
              for i in range(len(x)):
                    result += y[i] if x[i] == "1" else z[i]

              return result

        def majority(self, x, y, z):
              result = ""
              for i in range(len(x)):
                    temp0 = 0
                    temp1 = 0

                    temp0 += 1 if x[i] == "0" else 0 
                    temp1 += 1 if x[i] == "1" else 0

                    temp0 += 1 if y[i] == "0" else 0                  
                    temp1 += 1 if y[i] == "1" else 0

                    temp0 += 1 if z[i] == "0" else 0
                    temp1 += 1 if z[i] == "1" else 0
                    
                    if temp0 > temp1:
                          result += "0"
                    else:
                          result += "1"

              return result

Main.py

  from math import ceil
  from copy import copy
  from functions import Functions

  _k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
        0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
        0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
        0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
        0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
        0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
        0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
        0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
        0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
        0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
        0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
        0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
        0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
        0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
        0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
        0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]

  _h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
        0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]

  class SHA256(Functions):
        blocks = []

        def __init__(self):
              global _k, _h            
              _k = [f'{i:b}'.zfill(32) for i in _k]
              _h = [f'{i:b}'.zfill(32) for i in _h]

        def message_to_blocks(self, message):
              chunk = 56
              data = [format(ord(x), 'b').zfill(8) for x in message]

              for i in range(ceil(len(data) / chunk)):
                    self.blocks.append(data[chunk * i:chunk * (i + 1)])

                    self.blocks[i] = ''.join(self.blocks[i])
                    length = f'{len(self.blocks[i]):b}'
                    self.blocks[i] += '1'
                    self.blocks[i] = self.blocks[i].ljust(512, '0')

                    # add length to last 64 bit
                    self.blocks[i] = self.blocks[i][:-len(length)]
                    self.blocks[i] += length

        def message_schedule(self, data):
              schedule = []
              n = 32
              # first 16 words
              schedule = [(data[i:i+n]) for i in range(0, len(data), n)]
              # generate the rest
              for i in range(16, 64):
                    temp1 = self.sigma1(schedule[-2])
                    temp2 = self.sigma0(schedule[-15])
                    result = self.add(temp1, self.add(schedule[-7], self.add(temp2, schedule[-16])))
                    schedule.append(result)

              return schedule

        def compress(self):
              for block in self.blocks:
                    temp_h = copy(_h)
                    _w = self.message_schedule(block)
                    for i in range(64):
                          T1 = [self.gamma1(_h[4]), self.choice(_h[4], _h[5], _h[6]), _h[7], _k[i], _w[i]]
                          T1 = self.add(T1[0], self.add(T1[1], self.add(T1[2], self.add(T1[3], T1[4]))))

                          T2 = [self.gamma0(_h[0]), self.majority(_h[0], _h[1], _h[2])]
                          T2 = self.add(T2[0], T2[1])
                          
                          # shift all constants down
                          _h[7] = _h[6] # h
                          _h[6] = _h[5] # g
                          _h[5] = _h[4] # f
                          _h[4] = _h[3] # e
                          _h[3] = _h[2] # d
                          _h[2] = _h[1] # c
                          _h[1] = _h[0] # b

                          # compress
                          _h[0] = self.add(T1, T2)
                          _h[4] = self.add(_h[4], T1)

                    # add with initial values
                    _h[0] = self.add(_h[0], temp_h[0])
                    _h[1] = self.add(_h[1], temp_h[1])
                    _h[2] = self.add(_h[2], temp_h[2])
                    _h[3] = self.add(_h[3], temp_h[3])
                    _h[4] = self.add(_h[4], temp_h[4])
                    _h[5] = self.add(_h[5], temp_h[5])
                    _h[6] = self.add(_h[6], temp_h[6])
                    _h[7] = self.add(_h[7], temp_h[7])

              return self.digest(_h)

        def digest(self, hashes):
              final_hash = ""
              for hash in hashes:
                    t = hex(int(hash, 2))
                    final_hash += t[2:]

              return final_hash

  a = SHA256()
  a.message_to_blocks("ba7816bf8f01cfea414140de5dae2223bda12ae1a5324sfdserew3ra")
  print(a.compress())

The code works fine when the input is less than 56 characters but when it's equal or more than 56 characters the output is incorrect

这部分有问题:

length = f'{len(self.blocks[i]):b}'
self.blocks[i] += '1'
self.blocks[i] = self.blocks[i].ljust(512, '0')
# add length to last 64 bit
self.blocks[i] = self.blocks[i][:-len(length)]
self.blocks[i] += length

错误在于零填充的计算方式与非 64 位宽度的长度相结合。实际上,您的代码用零填充最后一个消息块,因此它的长度为 512 位,然后用未填充的二进制长度 (111000000) 替换最后的 n 位,但这在某些情况下无法正常工作其中零填充小于 64 位。

结果是代码将总消息输入处理为:

01100010011000010011011100111000 00110001001101100110001001100110 00111000011001100011000000110001 01100011011001100110010101100001 00110100001100010011010000110001 00110100001100000110010001100101 00110101011001000110000101100101 00110010001100100011001000110011 01100010011001000110000100110001 00110010011000010110010100110001 01100001001101010011001100110010 00110100011100110110011001100100 01110011011001010111001001100101 01110111001100110111001001100001 10000000000000000000000000000000 00000000000000000000000111000000

而不是:

01100010011000010011011100111000 00110001001101100110001001100110 00111000011001100011000000110001 01100011011001100110010101100001 00110100001100010011010000110001 00110100001100000110010001100101 00110101011001000110000101100101 00110010001100100011001000110011 01100010011001000110000100110001 00110010011000010110010100110001 01100001001101010011001100110010 00110100011100110110011001100100 01110011011001010111001001100101 01110111001100110111001001100001 10000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000111000000