1+ # Copyright (c) 2021 Pieter Wuille
2+ # Distributed under the MIT software license, see the accompanying
3+ # file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+ """
5+ Pure Python RIPEMD160 implementation. Note that this impelentation is not constant time.
6+ Original source: https://github.com/bitcoin/bitcoin/pull/23716
7+ """
8+
9+ # Message schedule indexes for the left path.
10+ ML = [
11+ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ,
12+ 7 , 4 , 13 , 1 , 10 , 6 , 15 , 3 , 12 , 0 , 9 , 5 , 2 , 14 , 11 , 8 ,
13+ 3 , 10 , 14 , 4 , 9 , 15 , 8 , 1 , 2 , 7 , 0 , 6 , 13 , 11 , 5 , 12 ,
14+ 1 , 9 , 11 , 10 , 0 , 8 , 12 , 4 , 13 , 3 , 7 , 15 , 14 , 5 , 6 , 2 ,
15+ 4 , 0 , 5 , 9 , 7 , 12 , 2 , 10 , 14 , 1 , 3 , 8 , 11 , 6 , 15 , 13
16+ ]
17+
18+ # Message schedule indexes for the right path.
19+ MR = [
20+ 5 , 14 , 7 , 0 , 9 , 2 , 11 , 4 , 13 , 6 , 15 , 8 , 1 , 10 , 3 , 12 ,
21+ 6 , 11 , 3 , 7 , 0 , 13 , 5 , 10 , 14 , 15 , 8 , 12 , 4 , 9 , 1 , 2 ,
22+ 15 , 5 , 1 , 3 , 7 , 14 , 6 , 9 , 11 , 8 , 12 , 2 , 10 , 0 , 4 , 13 ,
23+ 8 , 6 , 4 , 1 , 3 , 11 , 15 , 0 , 5 , 12 , 2 , 13 , 9 , 7 , 10 , 14 ,
24+ 12 , 15 , 10 , 4 , 1 , 5 , 8 , 7 , 6 , 2 , 13 , 14 , 0 , 3 , 9 , 11
25+ ]
26+
27+ # Rotation counts for the left path.
28+ RL = [
29+ 11 , 14 , 15 , 12 , 5 , 8 , 7 , 9 , 11 , 13 , 14 , 15 , 6 , 7 , 9 , 8 ,
30+ 7 , 6 , 8 , 13 , 11 , 9 , 7 , 15 , 7 , 12 , 15 , 9 , 11 , 7 , 13 , 12 ,
31+ 11 , 13 , 6 , 7 , 14 , 9 , 13 , 15 , 14 , 8 , 13 , 6 , 5 , 12 , 7 , 5 ,
32+ 11 , 12 , 14 , 15 , 14 , 15 , 9 , 8 , 9 , 14 , 5 , 6 , 8 , 6 , 5 , 12 ,
33+ 9 , 15 , 5 , 11 , 6 , 8 , 13 , 12 , 5 , 12 , 13 , 14 , 11 , 8 , 5 , 6
34+ ]
35+
36+ # Rotation counts for the right path.
37+ RR = [
38+ 8 , 9 , 9 , 11 , 13 , 15 , 15 , 5 , 7 , 7 , 8 , 11 , 14 , 14 , 12 , 6 ,
39+ 9 , 13 , 15 , 7 , 12 , 8 , 9 , 11 , 7 , 7 , 12 , 7 , 6 , 15 , 13 , 11 ,
40+ 9 , 7 , 15 , 11 , 8 , 6 , 6 , 14 , 12 , 13 , 5 , 14 , 13 , 13 , 7 , 5 ,
41+ 15 , 5 , 8 , 11 , 14 , 14 , 6 , 14 , 6 , 9 , 12 , 9 , 12 , 5 , 15 , 8 ,
42+ 8 , 5 , 12 , 9 , 12 , 5 , 14 , 6 , 8 , 13 , 6 , 5 , 15 , 13 , 11 , 11
43+ ]
44+
45+ # K constants for the left path.
46+ KL = [0 , 0x5a827999 , 0x6ed9eba1 , 0x8f1bbcdc , 0xa953fd4e ]
47+
48+ # K constants for the right path.
49+ KR = [0x50a28be6 , 0x5c4dd124 , 0x6d703ef3 , 0x7a6d76e9 , 0 ]
50+
51+
52+ def fi (x , y , z , i ):
53+ """The f1, f2, f3, f4, and f5 functions from the specification."""
54+ if i == 0 :
55+ return x ^ y ^ z
56+ elif i == 1 :
57+ return (x & y ) | (~ x & z )
58+ elif i == 2 :
59+ return (x | ~ y ) ^ z
60+ elif i == 3 :
61+ return (x & z ) | (y & ~ z )
62+ elif i == 4 :
63+ return x ^ (y | ~ z )
64+ else :
65+ assert False
66+
67+
68+ def rol (x , i ):
69+ """Rotate the bottom 32 bits of x left by i bits."""
70+ return ((x << i ) | ((x & 0xffffffff ) >> (32 - i ))) & 0xffffffff
71+
72+
73+ def compress (h0 , h1 , h2 , h3 , h4 , block ):
74+ """Compress state (h0, h1, h2, h3, h4) with block."""
75+ # Left path variables.
76+ al , bl , cl , dl , el = h0 , h1 , h2 , h3 , h4
77+ # Right path variables.
78+ ar , br , cr , dr , er = h0 , h1 , h2 , h3 , h4
79+ # Message variables.
80+ x = [int .from_bytes (block [4 * i :4 * (i + 1 )], 'little' ) for i in range (16 )]
81+
82+ # Iterate over the 80 rounds of the compression.
83+ for j in range (80 ):
84+ rnd = j >> 4
85+ # Perform left side of the transformation.
86+ al = rol (al + fi (bl , cl , dl , rnd ) + x [ML [j ]] + KL [rnd ], RL [j ]) + el
87+ al , bl , cl , dl , el = el , al , bl , rol (cl , 10 ), dl
88+ # Perform right side of the transformation.
89+ ar = rol (ar + fi (br , cr , dr , 4 - rnd ) + x [MR [j ]] + KR [rnd ], RR [j ]) + er
90+ ar , br , cr , dr , er = er , ar , br , rol (cr , 10 ), dr
91+
92+ # Compose old state, left transform, and right transform into new state.
93+ return h1 + cl + dr , h2 + dl + er , h3 + el + ar , h4 + al + br , h0 + bl + cr
94+
95+
96+ def ripemd160 (data ):
97+ """Compute the RIPEMD-160 hash of data."""
98+ # Initialize state.
99+ state = (0x67452301 , 0xefcdab89 , 0x98badcfe , 0x10325476 , 0xc3d2e1f0 )
100+ # Process full 64-byte blocks in the input.
101+ for b in range (len (data ) >> 6 ):
102+ state = compress (* state , data [64 * b :64 * (b + 1 )])
103+ # Construct final blocks (with padding and size).
104+ pad = b"\x80 " + b"\x00 " * ((119 - len (data )) & 63 )
105+ fin = data [len (data ) & ~ 63 :] + pad + (8 * len (data )).to_bytes (8 , 'little' )
106+ # Process final blocks.
107+ for b in range (len (fin ) >> 6 ):
108+ state = compress (* state , fin [64 * b :64 * (b + 1 )])
109+ # Produce output.
110+ return b"" .join ((h & 0xffffffff ).to_bytes (4 , 'little' ) for h in state )
0 commit comments