Skip to content

Commit 08e679e

Browse files
author
brentru
committed
update license, example, add sha1 module
1 parent 2fc6a9b commit 08e679e

File tree

3 files changed

+211
-10
lines changed

3 files changed

+211
-10
lines changed

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,25 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2121
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2222
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2323
SOFTWARE.
24+
25+
26+
The MIT License (MIT)
27+
28+
Copyright (c) 2013-2015 AJ Alt
29+
30+
Permission is hereby granted, free of charge, to any person obtaining a copy of
31+
this software and associated documentation files (the "Software"), to deal in
32+
the Software without restriction, including without limitation the rights to
33+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
34+
the Software, and to permit persons to whom the Software is furnished to do so,
35+
subject to the following conditions:
36+
37+
The above copyright notice and this permission notice shall be included in all
38+
copies or substantial portions of the Software.
39+
40+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
42+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
43+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
44+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
45+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

adafruit_hashlib/_sha1.py

Lines changed: 170 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
21
# The MIT License (MIT)
32
#
4-
# Brent Rubell for Adafruit Industries, 2019
3+
# Copyright (c) 2013-2015 AJ Alt
4+
# Modified by Brent Rubell for Adafruit Industries, 2019
55
#
66
# Permission is hereby granted, free of charge, to any person obtaining a copy
77
# of this software and associated documentation files (the "Software"), to deal
@@ -24,10 +24,174 @@
2424
`_sha1.py`
2525
======================================================
2626
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
2834
"""
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+
29115
# pylint: disable=too-few-public-methods, invalid-name
30116
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()])

examples/hashlib_simpletest.py

100644100755
Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@
44
# Bytes-to-encode
55
byte_string = b"CircuitPython"
66

7+
# Create a SHA-224 message
8+
print("--SHA1--")
9+
m = hashlib.sha1()
10+
# Update the hash object with byte_string
11+
m.update(byte_string)
12+
# Obtain the digest, digest size, and block size
13+
print(
14+
"Msg Digest: {}\nMsg Digest Size: {}\nMsg Block Size: {}".format(
15+
m.hexdigest(), m.digest_size, m.block_size))
16+
# Validate the digest against CPython3 hashlib-sha1
17+
assert (
18+
m.hexdigest() == "62c6e222ccd72f21b8ce0c61f42860d6c70954c0"
19+
), "Digest does not match expected string."
20+
21+
722
# Create a SHA-224 message
823
print("--SHA224--")
924
m = hashlib.sha224()
@@ -13,7 +28,7 @@
1328
print(
1429
"Msg Digest: {}\nMsg Digest Size: {}\nMsg Block Size: {}".format(
1530
m.hexdigest(), m.digest_size, m.block_size))
16-
# Validate the digest
31+
# Validate the digest against CPython hashlib-sha224
1732
assert (
1833
m.hexdigest() == "744535a10879be6b18bbcdd135032891346f530a7845d580f7869f36"
1934
), "Digest does not match expected string."
@@ -26,7 +41,7 @@
2641
# Obtain the digest, digest size, and block size
2742
print("Msg Digest: {}\nMsg Digest Size: {}\nMsg Block Size: {}".format(
2843
m.hexdigest(), m.digest_size, m.block_size))
29-
# Validate the digest
44+
# Validate the digest against CPython hashlib-sha256
3045
assert (
3146
m.hexdigest() == "3ce8334ca39e66afb9c37d571da4caad68ab4a8bcbd6d584f75e4268e36c0954"
3247
), "Digest does not match expected string."
@@ -39,7 +54,7 @@
3954
# Obtain the digest, digest size, and block size
4055
print("Msg Digest: {}\nMsg Digest Size: {}\nMsg Block Size: {}".format(
4156
m.hexdigest(), m.digest_size, m.block_size))
42-
# Validate the digest
57+
# Validate the digest against CPython hashlib-sha384
4358
assert (
4459
m.hexdigest() == "7a12f0815f5511b8ba52c67922d1ae86dfd9bfcc4e0799ad89a9f01fc526c8f074ddb5948c06db9893536f2e65c7621b"
4560
), "Digest does not match expected string."
@@ -52,7 +67,7 @@
5267
# Obtain the digest, digest size, and block size
5368
print("Msg Digest: {}\nMsg Digest Size: {}\nMsg Block Size: {}".format(
5469
m.hexdigest(), m.digest_size, m.block_size))
55-
# Validate the digest
70+
# Validate the digest against CPython hashlib-sha512
5671
assert (
5772
m.hexdigest() == "20a88a9b04aa490e457f8980e57331bc85c4d6ca30735a9e502f817e74011a9ece07078e53adf70c232ac91f6c79d4cd6cc69426cd77535645fe9016a71122c2"
5873
), "Digest does not match expected string."

0 commit comments

Comments
 (0)