From b93dd21459f32f6952d06454f33482ff81186d3b Mon Sep 17 00:00:00 2001 From: Studiex <80968515+Studiex@users.noreply.github.com> Date: Sat, 23 Oct 2021 00:09:06 +0400 Subject: [PATCH 01/10] Add files via upload --- hashes/sha256.py | 169 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 hashes/sha256.py diff --git a/hashes/sha256.py b/hashes/sha256.py new file mode 100644 index 000000000000..2d26df2f66f3 --- /dev/null +++ b/hashes/sha256.py @@ -0,0 +1,169 @@ +# 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/ --> in detail +https://en.wikipedia.org/wiki/SHA-2 --> Pseudocode +""" + +import argparse +import struct + + +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): + self.data = data + + # fmt: off + + # Initialize hash values + self.h = [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 + ] + + # fmt: on + + self.preprocessing() + self.final_hash() + + def preprocessing(self): + padding = b"\x80" + (b"\x00" * (63 - (len(self.data) + 8) % 64)) + big_endian_integer = struct.pack(">Q", (len(self.data) * 8)) + self.preprocessed_data = self.data + padding + big_endian_integer + + def final_hash(self): + # 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.h + + 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.h = [ + ((element + mutated_hash_values[index]) % 0x100000000) + for index, element in enumerate(self.h) + ] + + self.hash = "".join([hex(value)[2:].zfill(8) for value in self.h]) + + 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) + + +def main(): + """ + Provides option 'string' or 'file' to take input + and prints the calculated SHA-256 hash + """ + 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() + + import doctest + + doctest.testmod() From 71510c90a96e9bfbc789bb9ca018c27685c6eeca Mon Sep 17 00:00:00 2001 From: Studiex <80968515+Studiex@users.noreply.github.com> Date: Sat, 23 Oct 2021 00:59:32 +0400 Subject: [PATCH 02/10] Update sha256.py --- hashes/sha256.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/hashes/sha256.py b/hashes/sha256.py index 2d26df2f66f3..7df7c5bd9a10 100644 --- a/hashes/sha256.py +++ b/hashes/sha256.py @@ -16,6 +16,8 @@ import argparse import struct +import unittest +import hashlib # used only inside Test class class SHA256: @@ -29,7 +31,7 @@ class SHA256: 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9' """ - def __init__(self, data: bytes): + def __init__(self, data: bytes) -> None: self.data = data # fmt: off @@ -54,12 +56,12 @@ def __init__(self, data: bytes): self.preprocessing() self.final_hash() - def preprocessing(self): + def preprocessing(self) -> None: padding = b"\x80" + (b"\x00" * (63 - (len(self.data) + 8) % 64)) big_endian_integer = struct.pack(">Q", (len(self.data) * 8)) self.preprocessed_data = self.data + padding + big_endian_integer - def final_hash(self): + def final_hash(self) -> None: # Convert into blocks of 64 bytes self.blocks = [ self.preprocessed_data[x : (x + 64)] @@ -130,11 +132,24 @@ def ror(self, value: int, rotations: int) -> int: return (0xFFFFFFFF & (value << (32 - rotations))) | (value >> rotations) -def main(): +class SHA256HashTest(unittest.TestCase): + """ + Test class for the SHA256 class. Inherits the TestCase class from unittest + """ + + def testMatchHashes(self) -> None: + msg = bytes("Test String", "utf-8") + self.assertEqual(SHA256(msg).hash, hashlib.sha256(msg).hexdigest()) + + +def main() -> None: """ Provides option 'string' or 'file' to take input and prints the calculated SHA-256 hash """ + + # unittest.main() + parser = argparse.ArgumentParser() parser.add_argument( "-s", From 0ca7015f9ab7429b0b905e1086a7ef3a3d2262f1 Mon Sep 17 00:00:00 2001 From: Studiex <80968515+Studiex@users.noreply.github.com> Date: Sat, 23 Oct 2021 01:02:15 +0400 Subject: [PATCH 03/10] Update sha256.py --- hashes/sha256.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hashes/sha256.py b/hashes/sha256.py index 7df7c5bd9a10..bc0280844671 100644 --- a/hashes/sha256.py +++ b/hashes/sha256.py @@ -150,6 +150,11 @@ def main() -> None: # unittest.main() + import doctest + + doctest.testmod() + + parser = argparse.ArgumentParser() parser.add_argument( "-s", @@ -178,7 +183,3 @@ def main() -> None: if __name__ == "__main__": main() - - import doctest - - doctest.testmod() From af5888726f76ed3099efff62a39e0c4d9121981a Mon Sep 17 00:00:00 2001 From: Studiex <80968515+Studiex@users.noreply.github.com> Date: Sat, 23 Oct 2021 01:04:59 +0400 Subject: [PATCH 04/10] Update sha256.py --- hashes/sha256.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hashes/sha256.py b/hashes/sha256.py index bc0280844671..506692ff7f2d 100644 --- a/hashes/sha256.py +++ b/hashes/sha256.py @@ -137,7 +137,7 @@ class SHA256HashTest(unittest.TestCase): Test class for the SHA256 class. Inherits the TestCase class from unittest """ - def testMatchHashes(self) -> None: + def test_match_hashes(self) -> None: msg = bytes("Test String", "utf-8") self.assertEqual(SHA256(msg).hash, hashlib.sha256(msg).hexdigest()) @@ -148,7 +148,7 @@ def main() -> None: and prints the calculated SHA-256 hash """ - # unittest.main() + unittest.main() import doctest From 1ea7ff0f854f951e2ef4bcd482c26a05415bcb98 Mon Sep 17 00:00:00 2001 From: Studiex <80968515+Studiex@users.noreply.github.com> Date: Sat, 23 Oct 2021 11:38:22 +0400 Subject: [PATCH 05/10] Update sha256.py --- hashes/sha256.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hashes/sha256.py b/hashes/sha256.py index 506692ff7f2d..67dc40819819 100644 --- a/hashes/sha256.py +++ b/hashes/sha256.py @@ -17,7 +17,6 @@ import argparse import struct import unittest -import hashlib # used only inside Test class class SHA256: @@ -53,13 +52,13 @@ def __init__(self, data: bytes) -> None: # fmt: on - self.preprocessing() + self.preprocessed_data = self.preprocessing() self.final_hash() - def preprocessing(self) -> None: + def preprocessing(self) -> bytes: padding = b"\x80" + (b"\x00" * (63 - (len(self.data) + 8) % 64)) big_endian_integer = struct.pack(">Q", (len(self.data) * 8)) - self.preprocessed_data = self.data + padding + big_endian_integer + return self.data + padding + big_endian_integer def final_hash(self) -> None: # Convert into blocks of 64 bytes @@ -138,6 +137,8 @@ class SHA256HashTest(unittest.TestCase): """ def test_match_hashes(self) -> None: + import hashlib + msg = bytes("Test String", "utf-8") self.assertEqual(SHA256(msg).hash, hashlib.sha256(msg).hexdigest()) @@ -148,13 +149,12 @@ def main() -> None: and prints the calculated SHA-256 hash """ - unittest.main() + # unittest.main() import doctest doctest.testmod() - parser = argparse.ArgumentParser() parser.add_argument( "-s", From bf416b0f983ba47aa69cf6f03dfc29fda616aa30 Mon Sep 17 00:00:00 2001 From: Studiex <80968515+Studiex@users.noreply.github.com> Date: Sat, 23 Oct 2021 13:04:36 +0400 Subject: [PATCH 06/10] Update sha256.py --- hashes/sha256.py | 87 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 13 deletions(-) diff --git a/hashes/sha256.py b/hashes/sha256.py index 67dc40819819..62dc7d8d9ef9 100644 --- a/hashes/sha256.py +++ b/hashes/sha256.py @@ -33,25 +33,86 @@ class SHA256: def __init__(self, data: bytes) -> None: self.data = data - # fmt: off - # Initialize hash values - self.h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19] + self.h = [ + 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 + 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, ] - # fmt: on - self.preprocessed_data = self.preprocessing() self.final_hash() From cf4aca831b080a92545cd4a3dfbdc47d76ccbf81 Mon Sep 17 00:00:00 2001 From: Studiex <80968515+Studiex@users.noreply.github.com> Date: Sat, 23 Oct 2021 13:51:09 +0400 Subject: [PATCH 07/10] Update sha256.py --- hashes/sha256.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/hashes/sha256.py b/hashes/sha256.py index 62dc7d8d9ef9..1a2e5c3a3154 100644 --- a/hashes/sha256.py +++ b/hashes/sha256.py @@ -7,11 +7,12 @@ 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" + 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/ --> in detail -https://en.wikipedia.org/wiki/SHA-2 --> Pseudocode +https://qvault.io/cryptography/how-sha-2-works-step-by-step-sha-256/ +https://en.wikipedia.org/wiki/SHA-2 """ import argparse @@ -124,7 +125,7 @@ def preprocessing(self) -> bytes: def final_hash(self) -> None: # Convert into blocks of 64 bytes self.blocks = [ - self.preprocessed_data[x : (x + 64)] + self.preprocessed_data[x:(x + 64)] for x in range(0, len(self.preprocessed_data), 64) ] @@ -189,7 +190,9 @@ 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) + return ( + (0xFFFFFFFF & (value << (32 - rotations))) | (value >> rotations) + ) class SHA256HashTest(unittest.TestCase): From e00ceb969e9e144c806bc1c7c9cadc3878550e30 Mon Sep 17 00:00:00 2001 From: Studiex <80968515+Studiex@users.noreply.github.com> Date: Sat, 23 Oct 2021 17:01:28 +0400 Subject: [PATCH 08/10] Update sha256.py --- hashes/sha256.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hashes/sha256.py b/hashes/sha256.py index 1a2e5c3a3154..c2b60daa06c4 100644 --- a/hashes/sha256.py +++ b/hashes/sha256.py @@ -125,7 +125,7 @@ def preprocessing(self) -> bytes: def final_hash(self) -> None: # Convert into blocks of 64 bytes self.blocks = [ - self.preprocessed_data[x:(x + 64)] + self.preprocessed_data[x : x + 64] for x in range(0, len(self.preprocessed_data), 64) ] @@ -190,9 +190,7 @@ 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) - ) + return 0xFFFFFFFF & (value << (32 - rotations)) | (value >> rotations) class SHA256HashTest(unittest.TestCase): From 32ed61844bdefeb572d43de3e18f1d1f52a8183a Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 23 Oct 2021 15:11:43 +0200 Subject: [PATCH 09/10] Update sha256.py --- hashes/sha256.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/hashes/sha256.py b/hashes/sha256.py index c2b60daa06c4..5853705b7e9a 100644 --- a/hashes/sha256.py +++ b/hashes/sha256.py @@ -35,7 +35,7 @@ def __init__(self, data: bytes) -> None: self.data = data # Initialize hash values - self.h = [ + self.hashes = [ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, @@ -114,13 +114,14 @@ def __init__(self, data: bytes) -> None: 0xC67178F2, ] - self.preprocessed_data = self.preprocessing() + self.preprocessed_data = self.preprocessing(self.data) self.final_hash() - def preprocessing(self) -> bytes: - padding = b"\x80" + (b"\x00" * (63 - (len(self.data) + 8) % 64)) - big_endian_integer = struct.pack(">Q", (len(self.data) * 8)) - return self.data + padding + big_endian_integer + @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 @@ -135,7 +136,7 @@ def final_hash(self) -> None: # add 48 0-ed integers words += [0] * 48 - a, b, c, d, e, f, g, h = self.h + a, b, c, d, e, f, g, h = self.hashes for index in range(0, 64): if index > 15: @@ -179,12 +180,12 @@ def final_hash(self) -> None: mutated_hash_values = [a, b, c, d, e, f, g, h] # Modify final values - self.h = [ + self.hashes = [ ((element + mutated_hash_values[index]) % 0x100000000) - for index, element in enumerate(self.h) + for index, element in enumerate(self.hashes) ] - self.hash = "".join([hex(value)[2:].zfill(8) for value in self.h]) + self.hash = "".join([hex(value)[2:].zfill(8) for value in self.hashes]) def ror(self, value: int, rotations: int) -> int: """ From aabdff30189b397ddbec76e7656ec9e9f8b33484 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 23 Oct 2021 15:16:28 +0200 Subject: [PATCH 10/10] @staticmethod def preprocessing(data: bytes) -> bytes: --- hashes/sha256.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hashes/sha256.py b/hashes/sha256.py index 5853705b7e9a..9d4f250fe353 100644 --- a/hashes/sha256.py +++ b/hashes/sha256.py @@ -24,10 +24,10 @@ class SHA256: """ Class to contain the entire pipeline for SHA1 Hashing Algorithm - >>> SHA256(bytes('Python', 'utf-8')).hash + >>> SHA256(b'Python').hash '18885f27b5af9012df19e496460f9294d5ab76128824c6f993787004f6d9a7db' - >>> SHA256(bytes('hello world', 'utf-8')).hash + >>> SHA256(b'hello world').hash 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9' """ @@ -118,7 +118,7 @@ def __init__(self, data: bytes) -> None: self.final_hash() @staticmethod - def preprocessing(data) -> bytes: + def preprocessing(data: bytes) -> 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