New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementation of SHA-256 using Python #5532
Changes from 10 commits
b93dd21
71510c9
0ca7015
af58887
1ea7ff0
bf416b0
cf4aca8
9824a7c
e00ceb9
32ed618
aabdff3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
# Author: M. Yathurshan | ||
# Black Formatter: True | ||
|
||
""" | ||
Implementation of SHA256 Hash function in a Python class and provides utilities | ||
to find hash of string or hash of text from a file. | ||
|
||
Usage: python sha256.py --string "Hello World!!" | ||
python sha256.py --file "hello_world.txt" | ||
When run without any arguments, | ||
it prints the hash of the string "Hello World!! Welcome to Cryptography" | ||
|
||
References: | ||
https://qvault.io/cryptography/how-sha-2-works-step-by-step-sha-256/ | ||
https://en.wikipedia.org/wiki/SHA-2 | ||
""" | ||
|
||
import argparse | ||
import struct | ||
import unittest | ||
|
||
|
||
class SHA256: | ||
""" | ||
Class to contain the entire pipeline for SHA1 Hashing Algorithm | ||
|
||
>>> SHA256(bytes('Python', 'utf-8')).hash | ||
'18885f27b5af9012df19e496460f9294d5ab76128824c6f993787004f6d9a7db' | ||
|
||
>>> SHA256(bytes('hello world', 'utf-8')).hash | ||
'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9' | ||
""" | ||
|
||
def __init__(self, data: bytes) -> None: | ||
self.data = data | ||
|
||
# Initialize hash values | ||
self.hashes = [ | ||
0x6A09E667, | ||
0xBB67AE85, | ||
0x3C6EF372, | ||
0xA54FF53A, | ||
0x510E527F, | ||
0x9B05688C, | ||
0x1F83D9AB, | ||
0x5BE0CD19, | ||
] | ||
|
||
# Initialize round constants | ||
self.round_constants = [ | ||
0x428A2F98, | ||
0x71374491, | ||
0xB5C0FBCF, | ||
0xE9B5DBA5, | ||
0x3956C25B, | ||
0x59F111F1, | ||
0x923F82A4, | ||
0xAB1C5ED5, | ||
0xD807AA98, | ||
0x12835B01, | ||
0x243185BE, | ||
0x550C7DC3, | ||
0x72BE5D74, | ||
0x80DEB1FE, | ||
0x9BDC06A7, | ||
0xC19BF174, | ||
0xE49B69C1, | ||
0xEFBE4786, | ||
0x0FC19DC6, | ||
0x240CA1CC, | ||
0x2DE92C6F, | ||
0x4A7484AA, | ||
0x5CB0A9DC, | ||
0x76F988DA, | ||
0x983E5152, | ||
0xA831C66D, | ||
0xB00327C8, | ||
0xBF597FC7, | ||
0xC6E00BF3, | ||
0xD5A79147, | ||
0x06CA6351, | ||
0x14292967, | ||
0x27B70A85, | ||
0x2E1B2138, | ||
0x4D2C6DFC, | ||
0x53380D13, | ||
0x650A7354, | ||
0x766A0ABB, | ||
0x81C2C92E, | ||
0x92722C85, | ||
0xA2BFE8A1, | ||
0xA81A664B, | ||
0xC24B8B70, | ||
0xC76C51A3, | ||
0xD192E819, | ||
0xD6990624, | ||
0xF40E3585, | ||
0x106AA070, | ||
0x19A4C116, | ||
0x1E376C08, | ||
0x2748774C, | ||
0x34B0BCB5, | ||
0x391C0CB3, | ||
0x4ED8AA4A, | ||
0x5B9CCA4F, | ||
0x682E6FF3, | ||
0x748F82EE, | ||
0x78A5636F, | ||
0x84C87814, | ||
0x8CC70208, | ||
0x90BEFFFA, | ||
0xA4506CEB, | ||
0xBEF9A3F7, | ||
0xC67178F2, | ||
] | ||
|
||
self.preprocessed_data = self.preprocessing(self.data) | ||
self.final_hash() | ||
|
||
@staticmethod | ||
def preprocessing(data) -> bytes: | ||
padding = b"\x80" + (b"\x00" * (63 - (len(data) + 8) % 64)) | ||
big_endian_integer = struct.pack(">Q", (len(data) * 8)) | ||
return data + padding + big_endian_integer | ||
|
||
def final_hash(self) -> None: | ||
# Convert into blocks of 64 bytes | ||
self.blocks = [ | ||
self.preprocessed_data[x : x + 64] | ||
for x in range(0, len(self.preprocessed_data), 64) | ||
] | ||
|
||
for block in self.blocks: | ||
# Convert the given block into a list of 4 byte integers | ||
words = list(struct.unpack(">16L", block)) | ||
# add 48 0-ed integers | ||
words += [0] * 48 | ||
|
||
a, b, c, d, e, f, g, h = self.hashes | ||
|
||
for index in range(0, 64): | ||
if index > 15: | ||
# modify the zero-ed indexes at the end of the array | ||
s0 = ( | ||
self.ror(words[index - 15], 7) | ||
^ self.ror(words[index - 15], 18) | ||
^ (words[index - 15] >> 3) | ||
) | ||
s1 = ( | ||
self.ror(words[index - 2], 17) | ||
^ self.ror(words[index - 2], 19) | ||
^ (words[index - 2] >> 10) | ||
) | ||
|
||
words[index] = ( | ||
words[index - 16] + s0 + words[index - 7] + s1 | ||
) % 0x100000000 | ||
|
||
# Compression | ||
S1 = self.ror(e, 6) ^ self.ror(e, 11) ^ self.ror(e, 25) | ||
ch = (e & f) ^ ((~e & (0xFFFFFFFF)) & g) | ||
temp1 = ( | ||
h + S1 + ch + self.round_constants[index] + words[index] | ||
) % 0x100000000 | ||
S0 = self.ror(a, 2) ^ self.ror(a, 13) ^ self.ror(a, 22) | ||
maj = (a & b) ^ (a & c) ^ (b & c) | ||
temp2 = (S0 + maj) % 0x100000000 | ||
|
||
h, g, f, e, d, c, b, a = ( | ||
g, | ||
f, | ||
e, | ||
((d + temp1) % 0x100000000), | ||
c, | ||
b, | ||
a, | ||
((temp1 + temp2) % 0x100000000), | ||
) | ||
|
||
mutated_hash_values = [a, b, c, d, e, f, g, h] | ||
|
||
# Modify final values | ||
self.hashes = [ | ||
((element + mutated_hash_values[index]) % 0x100000000) | ||
for index, element in enumerate(self.hashes) | ||
] | ||
|
||
self.hash = "".join([hex(value)[2:].zfill(8) for value in self.hashes]) | ||
|
||
def ror(self, value: int, rotations: int) -> int: | ||
""" | ||
Right rotate a given unsigned number by a certain amount of rotations | ||
""" | ||
return 0xFFFFFFFF & (value << (32 - rotations)) | (value >> rotations) | ||
|
||
|
||
class SHA256HashTest(unittest.TestCase): | ||
""" | ||
Test class for the SHA256 class. Inherits the TestCase class from unittest | ||
""" | ||
|
||
def test_match_hashes(self) -> None: | ||
Yathu2007 marked this conversation as resolved.
Show resolved
Hide resolved
Yathu2007 marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file |
||
import hashlib | ||
|
||
msg = bytes("Test String", "utf-8") | ||
self.assertEqual(SHA256(msg).hash, hashlib.sha256(msg).hexdigest()) | ||
|
||
|
||
def main() -> None: | ||
Yathu2007 marked this conversation as resolved.
Show resolved
Hide resolved
Yathu2007 marked this conversation as resolved.
Show resolved
Hide resolved
Yathu2007 marked this conversation as resolved.
Show resolved
Hide resolved
Yathu2007 marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file |
||
""" | ||
Provides option 'string' or 'file' to take input | ||
and prints the calculated SHA-256 hash | ||
""" | ||
|
||
# unittest.main() | ||
|
||
import doctest | ||
|
||
doctest.testmod() | ||
|
||
parser = argparse.ArgumentParser() | ||
parser.add_argument( | ||
"-s", | ||
"--string", | ||
dest="input_string", | ||
default="Hello World!! Welcome to Cryptography", | ||
help="Hash the string", | ||
) | ||
parser.add_argument( | ||
"-f", "--file", dest="input_file", help="Hash contents of a file" | ||
) | ||
|
||
args = parser.parse_args() | ||
|
||
input_string = args.input_string | ||
|
||
# hash input should be a bytestring | ||
if args.input_file: | ||
with open(args.input_file, "rb") as f: | ||
hash_input = f.read() | ||
else: | ||
hash_input = bytes(input_string, "utf-8") | ||
|
||
print(SHA256(hash_input).hash) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please provide type hint for the parameter:
data