1
-
2
1
# The MIT License (MIT)
3
2
#
4
- # Brent Rubell for Adafruit Industries, 2019
3
+ # Copyright (c) 2013-2015 AJ Alt
4
+ # Modified by Brent Rubell for Adafruit Industries, 2019
5
5
#
6
6
# Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
# of this software and associated documentation files (the "Software"), to deal
24
24
`_sha1.py`
25
25
======================================================
26
26
SHA1 Hash Algorithm.
27
- * Author(s): Brent Rubell
27
+
28
+ Pure-Python implementation by AJ Alt
29
+ https://github.com/ajalt/python-sha1/blob/master/sha1.py
30
+
31
+ Modified by Brent Rubell, 2019
32
+
33
+ * Author(s): AJ Alt, Brent Rubell
28
34
"""
35
+ import struct
36
+ from micropython import const
37
+ from io import BytesIO
38
+
39
+ SHA_BLOCKSIZE = 64
40
+ SHA_DIGESTSIZE = 20
41
+
42
+ # initial hash value [5.3.1]
43
+ K0 = const (0x5A827999 )
44
+ K1 = const (0x6ED9EBA1 )
45
+ K2 = const (0x8F1BBCDC )
46
+ K3 = const (0xCA62C1D6 )
47
+
48
+ def _getbuf (s ):
49
+ if isinstance (s , str ):
50
+ return s .encode ('ascii' )
51
+ return bytes (s )
52
+
53
+
54
+ def _left_rotate (n , b ):
55
+ """Left rotate a 32-bit integer, n, by b bits.
56
+ :param int n: 32-bit integer
57
+ :param int b: Desired rotation amount, in bits.
58
+ """
59
+ return ((n << b ) | (n >> (32 - b ))) & 0xffffffff
60
+
61
+
62
+ def _hash_computation (chunk , h0 , h1 , h2 , h3 , h4 ):
63
+ """Processes 64-bit chunk of data and returns new digest variables.
64
+ Per FIPS [6.1.2]
65
+ :param bytes bytearray chunk: 64-bit bytearray
66
+ :param list h_tuple: List of hash values for the chunk
67
+ """
68
+ assert len (chunk ) == 64 , "Chunk should be 64-bits"
69
+
70
+ w = [0 ] * 80
71
+
72
+ # Break chunk into sixteen 4-byte big-endian words w[i]
73
+ for i in range (16 ):
74
+ w [i ] = struct .unpack (b'>I' , chunk [i * 4 :i * 4 + 4 ])[0 ]
75
+
76
+ # Extend the sixteen 4-byte words into eighty 4-byte words
77
+ for i in range (16 , 80 ):
78
+ w [i ] = _left_rotate (w [i - 3 ] ^ w [i - 8 ] ^ w [i - 14 ] ^ w [i - 16 ], 1 )
79
+
80
+ # Init. hash values for chunk
81
+ a = h0
82
+ b = h1
83
+ c = h2
84
+ d = h3
85
+ e = h4
86
+
87
+ for i in range (80 ):
88
+ if 0 <= i <= 19 :
89
+ # Use alternative 1 for f from FIPS PB 180-1 to avoid bitwise not
90
+ f = d ^ (b & (c ^ d ))
91
+ k = K0
92
+ elif 20 <= i <= 39 :
93
+ f = b ^ c ^ d
94
+ k = K1
95
+ elif 40 <= i <= 59 :
96
+ f = (b & c ) | (b & d ) | (c & d )
97
+ k = K2
98
+ elif 60 <= i <= 79 :
99
+ f = b ^ c ^ d
100
+ k = K3
101
+
102
+ a , b , c , d , e = ((_left_rotate (a , 5 ) + f + e + k + w [i ]) & 0xffffffff ,
103
+ a , _left_rotate (b , 30 ), c , d )
104
+
105
+ # Add to chunk's hash result so far
106
+ h0 = (h0 + a ) & 0xffffffff
107
+ h1 = (h1 + b ) & 0xffffffff
108
+ h2 = (h2 + c ) & 0xffffffff
109
+ h3 = (h3 + d ) & 0xffffffff
110
+ h4 = (h4 + e ) & 0xffffffff
111
+
112
+ return h0 , h1 , h2 , h3 , h4
113
+
114
+
29
115
# pylint: disable=too-few-public-methods, invalid-name
30
116
class sha1 ():
31
- """SHA1 hash algorithm."""
32
- def __init__ (self , s = None ):
33
- raise NotImplementedError ("SHA1 digests not currently implemented in this module." )
117
+ digest_size = SHA_DIGESTSIZE
118
+ block_size = SHA_BLOCKSIZE
119
+ name = "sha1"
120
+ def __init__ (self , s = None ):
121
+ """Construct a SHA-1 hash object.
122
+
123
+ """
124
+ # Initial Digest Variables
125
+ self ._h = (0x67452301 ,
126
+ 0xEFCDAB89 ,
127
+ 0x98BADCFE ,
128
+ 0x10325476 ,
129
+ 0xC3D2E1F0 )
130
+
131
+ self ._unprocessed = b''
132
+ self ._msg_byte_len = 0
133
+
134
+ def _create_digest (self ):
135
+ """Returns finalized digest variables for the data processed so far.
136
+
137
+ """
138
+ # pre-processing
139
+ message = self ._unprocessed
140
+ message_len = self ._msg_byte_len + len (message )
141
+
142
+ # add trailing '1' bit (+ 0's padding) to string [§5.1.1]
143
+ message += b'\x80 '
144
+
145
+ # append 0 <= k < 512 bits '0', so that the resulting message length (in bytes)
146
+ # is congruent to 56 (mod 64)
147
+ message += b'\x00 ' * ((56 - (message_len + 1 ) % 64 ) % 64 )
148
+
149
+
150
+ # append ml, the original message length, as a 64-bit big-endian integer.
151
+ message_bit_length = message_len * 8
152
+ message += struct .pack (b'>Q' , message_bit_length )
153
+
154
+ # Process the final chunk
155
+ # At this point, the length of the message is either 64 or 128 bytes.
156
+ h = _hash_computation (message [:64 ], * self ._h )
157
+ if len (message ) == 64 :
158
+ return h
159
+ return _hash_computation (message [64 :], * h )
160
+
161
+ def update (self , data ):
162
+ """Updates the hash object with bytes-like object, data.
163
+ :param bytes data: bytearray or bytes object
164
+
165
+ """
166
+ # if we get a string, convert to a bytearray objects
167
+ data = _getbuf (data )
168
+
169
+ # switch input to bytesio api for easier reading
170
+ if isinstance (data , (bytes , bytearray )):
171
+ data = BytesIO (data )
172
+
173
+ # Try to build a chunk out of the unprocessed data, if any
174
+ chunk = self ._unprocessed + data .read (64 - len (self ._unprocessed ))
175
+
176
+ while len (chunk ) == 64 :
177
+ self ._h = _hash_computation (chunk , * self ._h )
178
+ self ._msg_byte_len += 64
179
+ # read the next 64 bytes
180
+ chunk = data .read (64 )
181
+
182
+ self ._unprocessed = chunk
183
+ return self
184
+
185
+ def digest (self ):
186
+ """Returns the digest of the data passed to the update()
187
+ method so far.
188
+
189
+ """
190
+ return b'' .join (struct .pack (b'>I' , h ) for h in self ._create_digest ())
191
+
192
+ def hexdigest (self ):
193
+ """Like digest() except the digest is returned as a string object of
194
+ double length, containing only hexadecimal digits.
195
+
196
+ """
197
+ return '' .join (['%.2x' % i for i in self .digest ()])
0 commit comments