diff --git a/period_1/03-cryptography/306_HW2_Signature_SaveLoadSignedMsg/Signature.py b/period_1/03-cryptography/306_HW2_Signature_SaveLoadSignedMsg/Signature.py new file mode 100644 index 0000000..eebc643 --- /dev/null +++ b/period_1/03-cryptography/306_HW2_Signature_SaveLoadSignedMsg/Signature.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +"""Asymmetric Cryptography -> Digital Signature: Homework + +The goal of this homework is to learn how to store and load asymmetric keys of different users on a disk. +In addition, to sign and verify messages using those keys. Furthermore, it is required to encrypt keys before saving using a password. +In this implementation the passed message as an argument is a string. Proper encoding and decoding is need before usage. +When signing a message the RSA sign-function requires a specific hash like SHA256, and padding such as PSS. +RSA verify function calculates the message hash. Decrypt the signature then compares both values to verify. +Be aware that verification must use the same algorithm values as signing to correctly verify the signature. + +Your task is to: + * locate the TODOs in this file + * complete the missing part from the code + * run the test of this tutorial located in same folder. + +To test run 'Signature_t.py' in your command line + +Notes: + * do not change class structure or method signature to not break unit tests + * visit this url for more information on this topic: + https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/ +""" + +from cryptography.exceptions import * +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives import serialization +import pickle + +def generate_keys(): + private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048) + public_key = private_key.public_key() + return private_key, public_key + +# TODO 1: Sign a passed message using a given private key +# Make sure the message is encoded correctly before signing +# Signing and verifying algorithms must be the same +def sign(message, private_key): + # Make sure the message is encoded correctly before signing + message = message.encode() + return private_key.sign( + message, + padding.PSS( + mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH + ), + hashes.SHA256() + ) + +# TODO 2: Verify a signature for a message using a given public key +# Make sure the message is decoded correctly before verifying +# Signing and verifying algorithms values must be the same +def verify(message, signature, public_key): + try: + message = message.encode() + public_key.verify( + signature, + message, + padding.PSS( + mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH + ), + hashes.SHA256() + ) + return True + except: + return False + +# TODO 3: Store the list of keys into a given file. +# In this implementation passwords are used for additional security +# Make sure of proper PEM encoding before serialization +def save_keys(keys_file_name, keys, pw): + keys_ser_list = [] + print(pw) + private_key = keys[0] + public_key = keys[1] + + prv_ser = private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.BestAvailableEncryption(pw.encode('utf-8')) + ) + pbc_ser = public_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo + ) + + keys_ser_list.append((prv_ser, pbc_ser)) + + savefile = open(keys_file_name, "wb") + pickle.dump(keys_ser_list, savefile) + savefile.close() + +# TODO 4: Load asymmetric keys from a given file and return those keys as a tuple +# In this implementation passwords are used for additional security +# Make sure of proper PEM decoding when deserializing +def load_keys(keys_file_name, pw): + loadfile = open(keys_file_name, "rb") + + keys_ser_list = pickle.load(loadfile) + loadfile.close() + + prv_ser = keys_ser_list[0][0] + pbc_ser = keys_ser_list[0][1] + try: + private_key = serialization.load_pem_private_key( + prv_ser, + password=pw.encode('utf-8') + ) + public_key = serialization.load_pem_public_key( + pbc_ser + ) + return private_key, public_key + except: + return None, None + diff --git a/period_1/03-cryptography/306_HW2_Signature_SaveLoadSignedMsg/Signature_t.py b/period_1/03-cryptography/306_HW2_Signature_SaveLoadSignedMsg/Signature_t.py new file mode 100644 index 0000000..45535d4 --- /dev/null +++ b/period_1/03-cryptography/306_HW2_Signature_SaveLoadSignedMsg/Signature_t.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +""" +This test case will verify if the provided solution by a student for Signature_a.py is correct. +""" +from Signature import * + +if __name__ == '__main__': + + # Generate asymmetric keys for different users + alex_prv, alex_pbc = generate_keys() + mike_prv, mike_pbc = generate_keys() + rose_prv, rose_pbc = generate_keys() + + # Use a password to secure keys during storage + pws = input("Enter a password for saving the keys: ") + + save_keys('alex.keys', (alex_prv, alex_pbc), pws) + save_keys('mike.keys', (mike_prv, mike_pbc), pws) + save_keys('rose.keys', (rose_prv, rose_pbc), pws) + + print('keys are successfully saved.') + + alex_message = 'pay 10 euro to mike' + mike_message = 'pay 5 euro to josé' + rose_message = 'Hello, this is rose!' + + # Sign each user's message with his private key + alex_signature = sign(alex_message, alex_prv) + mike_signature = sign(mike_message, mike_prv) + rose_signature = sign(rose_message, rose_prv) + + # Load user's asymmetric keys from storage + pwl = input("Enter the password for loading the keys: ") + (_, alex_pbc_loaded) = load_keys('alex.keys', pwl) + (_, mike_pbc_loaded) = load_keys('mike.keys', pwl) + (_, rose_pbc_loaded) = load_keys('rose.keys', pwl) + + if all([alex_pbc_loaded, mike_pbc_loaded, rose_pbc_loaded]): + + # Check if each user's message can correctly be verified + for p in ['alex', 'mike', 'rose']: + r_message = input(f"What was {p}'s message? ") + + (_, pbc_loaded) = load_keys(p+'.keys', pwl) + sig = globals()[p+'_signature'] + correct = verify(r_message, sig, pbc_loaded) + if correct: + print('Great! You correctly entered the message.') + else: + print('Oops! The message you entered is not correct') + else: + print('Error in loading the keys') \ No newline at end of file