Coverage for thirdparty/sha256.py: 83%
89 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
1#!/usr/bin/python3
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.
25__base__ = 'https://github.com/thomdixon/pysha2/blob/master/sha2/sha256.py'
26__author__ = 'Lukas Prokop'
27__license__ = 'MIT'
29import copy
30import struct
31import binascii
33F32 = 0xFFFFFFFF
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]
52_h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
53 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]
56def _pad(msglen):
57 mdi = msglen & 0x3F
58 length = struct.pack('!Q', msglen << 3)
60 if mdi < 56:
61 padlen = 55 - mdi
62 else:
63 padlen = 119 - mdi
65 return b'\x80' + (b'\x00' * padlen) + length
68def _rotr(x, y):
69 return ((x >> y) | (x << (32 - y))) & F32
72def _maj(x, y, z):
73 return (x & y) ^ (x & z) ^ (y & z)
76def _ch(x, y, z):
77 return (x & y) ^ ((~x) & z)
80class SHA256:
81 _output_size = 8
82 blocksize = 1
83 block_size = 64
84 digest_size = 32
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)
92 self.update(m)
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)
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])
112 def _compress(self, c):
113 w = [0] * 64
114 w[0:16] = struct.unpack('!16L', c)
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
121 a, b, c, d, e, f, g, h = self._h
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]
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
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
141 def update(self, m):
142 if not m:
143 return
145 self._counter += len(m)
146 m = self._cache + m
148 for i in range(0, len(m) // 64):
149 self._compress(m[64 * i:64 * (i + 1)])
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):]
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)
164 def hexdigest(self):
165 return binascii.hexlify(self.digest()).decode('ascii')
168if __name__ == '__main__':
169 def check(msg, sig):
170 m = SHA256()
171 m.update(msg.encode('ascii'))
172 print(m.hexdigest() == sig)
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 }
194 for inp, out in tests.items():
195 check(inp, out)