commit c6ecfa305b41036a41fbd7c57148fbc260814aff Author: Kaushik Narayan R Date: Sun Oct 29 16:18:39 2023 -0700 Initial commit Completed DH-AES part yesterday diff --git a/DH-AES/DH-AES-Project.pdf b/DH-AES/DH-AES-Project.pdf new file mode 100644 index 0000000..5be42f7 Binary files /dev/null and b/DH-AES/DH-AES-Project.pdf differ diff --git a/DH-AES/README.md b/DH-AES/README.md new file mode 100644 index 0000000..03e4671 --- /dev/null +++ b/DH-AES/README.md @@ -0,0 +1,33 @@ +# Diffie-Hellman and AES encryption + +## Setup and requirements + +- Recommended Python version: 3.10.5 or later +- Install packages from requirements.txt + +## Run program + +The specified example's inputs: + +```shell +python3 dh_aes.py "A2 2D 93 61 7F DC 0D 8E C6 3E A7 74 51 1B 24 B2" 251 465 255 1311 2101864342 8995936589171851885163650660432521853327227178155593274584417851704581358902 "F2 2C 95 FC 6B 98 BE 40 AE AD 9C 07 20 3B B3 9F F8 2F 6D 2D 69 D6 5D 40 0A 75 45 80 45 F2 DE C8 6E C0 FF 33 A4 97 8A AF 4A CD 6E 50 86 AA 3E DF" AfYw7Z6RzU9ZaGUloPhH3QpfA1AXWxnCGAXAwk3f6MoTx +``` + +This gives the attached outputs: +![Output for above command](dh_aes_output.png "Output for above command") + +For a more verbose output, include the `--verbose` flag: + +```shell +python3 dh_aes.py "A2 2D 93 61 7F DC 0D 8E C6 3E A7 74 51 1B 24 B2" 251 465 255 1311 2101864342 8995936589171851885163650660432521853327227178155593274584417851704581358902 "F2 2C 95 FC 6B 98 BE 40 AE AD 9C 07 20 3B B3 9F F8 2F 6D 2D 69 D6 5D 40 0A 75 45 80 45 F2 DE C8 6E C0 FF 33 A4 97 8A AF 4A CD 6E 50 86 AA 3E DF" AfYw7Z6RzU9ZaGUloPhH3QpfA1AXWxnCGAXAwk3f6MoTx --verbose +``` + +![More verbose output](dh_aes_verbose_output.png "Verbose output") + +### Notes and inferences + +- Recent versions (2.2+) of Python automatically handle large numbers +- Little-endian vs big-endian: given inputs are little-endian byte arrays, so when converting keys from integers to bytes, we convert to little-endian byte order +- `cryptography` package is used here for AES - `pip install cryptography` +- Used AES-256 algorithm - CBC mode of operation, using provided initialization vector (IV is commonly prepended to ciphertext in practice) +- [Library reference for implementation](https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/#cryptography.hazmat.primitives.ciphers.Cipher) diff --git a/DH-AES/dh_aes.py b/DH-AES/dh_aes.py new file mode 100644 index 0000000..58d23b1 --- /dev/null +++ b/DH-AES/dh_aes.py @@ -0,0 +1,102 @@ +import os +import argparse + +from cryptography.hazmat.primitives import padding +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + + +def prettyBytes(bytestr: bytes) -> str: + """Prints byte strings like `\\x3d\\xe9\\xb7` as `3D E9 B7`""" + return " ".join([bytestr[i : i + 1].hex().upper() for i in range(0, len(bytestr))]) + + +def buildExpNum(exponent: int, constant: int, base: int = 2) -> int: + return pow(base, exponent) - constant + + +def calculateSharedKey( + g_e: int, g_c: int, N_e: int, N_c: int, x: int, gy_modN: int +) -> int: + """Diffie-Hellman shared key computation (single-party)""" + # g = build_exp_num(g_e, g_c) # not needed since gy_modN is given + N = buildExpNum(N_e, N_c) + return pow(gy_modN, x, N) + + +def encrypt(plaintext: bytes, key: int, iv: bytearray) -> bytes: + """Encryption using AES-256 in CBC mode""" + key_byte_length = 32 + cipher = Cipher( + algorithms.AES(key.to_bytes(key_byte_length, byteorder="little")), modes.CBC(iv) + ) + + padder = padding.PKCS7(algorithms.AES.block_size).padder() + padded_plaintext = padder.update(plaintext) + padder.finalize() + + encryptor = cipher.encryptor() + return encryptor.update(padded_plaintext) + encryptor.finalize() + + +def decrypt(ciphertext: bytes, key: int, iv: bytearray) -> bytes: + """Decryption using AES-256 in CBC mode""" + key_byte_length = 32 + cipher = Cipher( + algorithms.AES(key.to_bytes(key_byte_length, byteorder="little")), modes.CBC(iv) + ) + + decryptor = cipher.decryptor() + return decryptor.update(ciphertext) + decryptor.finalize() + + +def main(args: argparse.Namespace) -> None: + # pre-processing inputs + iv = bytearray.fromhex(args.initialization_vector) + g_e = int(args.g_e) + g_c = int(args.g_c) + N_e = int(args.N_e) + N_c = int(args.N_c) + x = int(args.x) + gy_modN = int(args.gy_modN) + ciphertext = bytearray.fromhex(args.ciphertext) + plaintext = str.encode(args.plaintext, "utf-8") + verbose = args.verbose + + shared_key = calculateSharedKey(g_e, g_c, N_e, N_c, x, gy_modN) + if verbose: + print(("-" * os.get_terminal_size().columns) + "\n") + print("Shared key\t\t", shared_key) + print(("-" * os.get_terminal_size().columns) + "\n") + + new_pt = decrypt(ciphertext, shared_key, iv) + if verbose: + print("Given ciphertext\t", prettyBytes(ciphertext)) + print("Calculated plaintext\t", new_pt.decode("utf-8")) + print(("-" * os.get_terminal_size().columns) + "\n") + + new_ct = encrypt(plaintext, shared_key, iv) + if verbose: + print("Given plaintext\t\t", args.plaintext) + print("Calculated ciphertext\t", prettyBytes(new_ct)) + print(("-" * os.get_terminal_size().columns) + "\n") + + if not verbose: + print(f"{new_pt.decode('utf-8')}, {prettyBytes(new_ct)}") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="DHKE keygen and AES encryption/decryption" + ) + parser.add_argument("initialization_vector", help="") + parser.add_argument("g_e", help="Exponent (base 2), for g") + parser.add_argument("g_c", help="Constant to be subtracted, for g") + parser.add_argument("N_e", help="Exponent (base 2), for N") + parser.add_argument("N_c", help="Constant to be subtracted, for N") + parser.add_argument("x", help="Alice's private key value") + parser.add_argument("gy_modN", help="g^y modulo N, computed by Bob") + parser.add_argument("ciphertext", help="Ciphertext to be decrypted") + parser.add_argument("plaintext", help="Plaintext to be encrypted") + parser.add_argument("-v", "--verbose", action="store_true") + args = parser.parse_args() + + main(args) diff --git a/DH-AES/dh_aes_output.png b/DH-AES/dh_aes_output.png new file mode 100644 index 0000000..161bc29 Binary files /dev/null and b/DH-AES/dh_aes_output.png differ diff --git a/DH-AES/dh_aes_verbose_output.png b/DH-AES/dh_aes_verbose_output.png new file mode 100644 index 0000000..8b4e3df Binary files /dev/null and b/DH-AES/dh_aes_verbose_output.png differ diff --git a/DH-AES/requirements.txt b/DH-AES/requirements.txt new file mode 100644 index 0000000..8f7fd54 --- /dev/null +++ b/DH-AES/requirements.txt @@ -0,0 +1 @@ +cryptography==41.0.5 \ No newline at end of file