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