Coverage for thirdparty/sha256.py: 83%

89 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-07-10 13:43 +0000

1#!/usr/bin/python3 

2 

3# The MIT License (MIT) 

4# 

5# Copyright (c) 2021 RSK Labs Ltd 

6# 

7# Permission is hereby granted, free of charge, to any person obtaining a copy of 

8# this software and associated documentation files (the "Software"), to deal in 

9# the Software without restriction, including without limitation the rights to 

10# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 

11# of the Software, and to permit persons to whom the Software is furnished to do 

12# so, subject to the following conditions: 

13# 

14# The above copyright notice and this permission notice shall be included in all 

15# copies or substantial portions of the Software. 

16# 

17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 

18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 

20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 

21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 

22# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 

23# SOFTWARE. 

24 

25__base__ = 'https://github.com/thomdixon/pysha2/blob/master/sha2/sha256.py' 

26__author__ = 'Lukas Prokop' 

27__license__ = 'MIT' 

28 

29import copy 

30import struct 

31import binascii 

32 

33F32 = 0xFFFFFFFF 

34 

35_k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 

36 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 

37 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 

38 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 

39 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 

40 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 

41 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 

42 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 

43 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 

44 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 

45 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 

46 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 

47 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 

48 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 

49 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 

50 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2] 

51 

52_h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 

53 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19] 

54 

55 

56def _pad(msglen): 

57 mdi = msglen & 0x3F 

58 length = struct.pack('!Q', msglen << 3) 

59 

60 if mdi < 56: 

61 padlen = 55 - mdi 

62 else: 

63 padlen = 119 - mdi 

64 

65 return b'\x80' + (b'\x00' * padlen) + length 

66 

67 

68def _rotr(x, y): 

69 return ((x >> y) | (x << (32 - y))) & F32 

70 

71 

72def _maj(x, y, z): 

73 return (x & y) ^ (x & z) ^ (y & z) 

74 

75 

76def _ch(x, y, z): 

77 return (x & y) ^ ((~x) & z) 

78 

79 

80class SHA256: 

81 _output_size = 8 

82 blocksize = 1 

83 block_size = 64 

84 digest_size = 32 

85 

86 def __init__(self, m=None): 

87 self._counter = 0 

88 self._cache = b'' 

89 self._k = copy.deepcopy(_k) 

90 self._h = copy.deepcopy(_h) 

91 

92 self.update(m) 

93 

94 # Sets the midstate of the digest. 

95 # Based off org.bouncycastle.crypto.digests.SHA256Digest 

96 # method 'getEncodedState' (as of version 1.59). 

97 # This state should be exactly 52 bytes long 

98 # The first 8 bytes will be ignored. 

99 # The following 8 bytes will represent the counter (unsigned big-endian) 

100 # The following 32 bytes will represent the current hash (8 4-byte big-endian words) 

101 # The last 4 bytes will be ignored. 

102 def set_midstate(self, state): 

103 if type(state) != bytes or len(state) != 52: 

104 raise ValueError("Invalid state given: %s" % state) 

105 

106 self._counter = struct.unpack('>Q', state[8:16])[0] 

107 self._h = [] 

108 for i in range(8): 

109 start = 16+i*4 

110 self._h.append(struct.unpack('>I', state[start:start+4])[0]) 

111 

112 def _compress(self, c): 

113 w = [0] * 64 

114 w[0:16] = struct.unpack('!16L', c) 

115 

116 for i in range(16, 64): 

117 s0 = _rotr(w[i-15], 7) ^ _rotr(w[i-15], 18) ^ (w[i-15] >> 3) 

118 s1 = _rotr(w[i-2], 17) ^ _rotr(w[i-2], 19) ^ (w[i-2] >> 10) 

119 w[i] = (w[i-16] + s0 + w[i-7] + s1) & F32 

120 

121 a, b, c, d, e, f, g, h = self._h 

122 

123 for i in range(64): 

124 s0 = _rotr(a, 2) ^ _rotr(a, 13) ^ _rotr(a, 22) 

125 t2 = s0 + _maj(a, b, c) 

126 s1 = _rotr(e, 6) ^ _rotr(e, 11) ^ _rotr(e, 25) 

127 t1 = h + s1 + _ch(e, f, g) + self._k[i] + w[i] 

128 

129 h = g 

130 g = f 

131 f = e 

132 e = (d + t1) & F32 

133 d = c 

134 c = b 

135 b = a 

136 a = (t1 + t2) & F32 

137 

138 for i, (x, y) in enumerate(zip(self._h, [a, b, c, d, e, f, g, h])): 

139 self._h[i] = (x + y) & F32 

140 

141 def update(self, m): 

142 if not m: 

143 return 

144 

145 self._counter += len(m) 

146 m = self._cache + m 

147 

148 for i in range(0, len(m) // 64): 

149 self._compress(m[64 * i:64 * (i + 1)]) 

150 

151 # Bugfix: cache must be empty when the message size 

152 # is a multiple of 64 (i.e., all the message has been consumed) 

153 if len(m) % 64 == 0: 

154 self._cache = b'' 

155 else: 

156 self._cache = m[-(len(m) % 64):] 

157 

158 def digest(self): 

159 r = copy.deepcopy(self) 

160 r.update(_pad(self._counter)) 

161 data = [struct.pack('!L', i) for i in r._h[:self._output_size]] 

162 return b''.join(data) 

163 

164 def hexdigest(self): 

165 return binascii.hexlify(self.digest()).decode('ascii') 

166 

167 

168if __name__ == '__main__': 

169 def check(msg, sig): 

170 m = SHA256() 

171 m.update(msg.encode('ascii')) 

172 print(m.hexdigest() == sig) 

173 

174 tests = { 

175 "": 

176 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 

177 "a": 

178 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb', 

179 "abc": 

180 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', 

181 "message digest": 

182 'f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650', 

183 "abcdefghijklmnopqrstuvwxyz": 

184 '71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73', 

185 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789": 

186 'db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0', 

187 ("12345678901234567890123456789012345678901234567890123456789" 

188 "012345678901234567890"): 

189 'f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e', 

190 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 

191 'ffe054fe7ae0cb6dc65c3af9b61d5209f439851db43d0ba5997337df154668eb' 

192 } 

193 

194 for inp, out in tests.items(): 

195 check(inp, out)