├── .configs ├── ecc │ ├── ecc_config.py │ └── ecc_usage.py ├── elgamal │ ├── elgamal_config.py │ └── elgamal_usage.py ├── hashes │ ├── hash_config.py │ └── hash_usage.py ├── hospital.py ├── rabin │ └── rabin_config.py ├── rsa │ ├── rsa_config.py │ └── rsa_usage.py ├── sockets │ ├── client.py │ └── server.py └── symmetric │ ├── symmetric_config.py │ └── symmetric_usage.py ├── .gitignore ├── IS Lab Manual l1-4.pdf ├── Lab No 5 and 6.pdf ├── Lab_No_7_to_8.pdf ├── chat.py ├── client_endsem.py ├── elgamal ├── elgamal_config.py └── elgamal_usage.py ├── key.pem ├── lab1 ├── lab1_q1.py ├── lab1_q2.py ├── lab1_q3.py ├── lab1_q4.py ├── lab1_q5.md ├── lab1_q5.py └── lab1_q6.py ├── lab2 ├── lab2_q1.py ├── lab2_q2.py ├── lab2_q3.py ├── lab2_q4.py └── lab2_q5.py ├── lab3 ├── lab3_q1.py ├── lab3_q2.py ├── lab3_q3.py ├── lab3_q4.py └── lab3_q5.py ├── lab4 ├── lab4_q1.py └── lab4_q2.py ├── lab5 ├── lab5_q1.py ├── lab5_q2_client.py ├── lab5_q2_server.py └── lab5_q3.py ├── lab6 ├── lab6_q1.1.py ├── lab6_q1.2.py ├── lab6_q2.py ├── lab6_q3_client.py └── lab6_q3_server.py ├── lab7 ├── q1.py └── q2.py ├── lab8 ├── q1.py └── q2.py ├── modinv.py ├── public_key.pem ├── requirements.txt ├── rsa ├── rsa_config.py └── rsa_usage.py └── server_endsem.py /.configs/ecc/ecc_config.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import ECC 2 | from Crypto.Signature import DSS 3 | from Crypto.Hash import SHA256, SHA1, SHA512 4 | from Crypto.Cipher import AES 5 | from Crypto.Protocol.KDF import scrypt 6 | 7 | # Generate ECC key pair 8 | def generate_ecc_key_pair(curve="P-256"): 9 | private_key = ECC.generate(curve=curve) 10 | public_key = private_key.public_key() 11 | return private_key, public_key 12 | 13 | 14 | # ECC Digital Signature Creation 15 | def ecc_sign(private_key, message): 16 | hash_obj = SHA256.new(message) 17 | signer = DSS.new(private_key, "fips-186-3") 18 | signature = signer.sign(hash_obj) 19 | return signature 20 | 21 | 22 | # ECC Digital Signature Verification 23 | def ecc_verify(public_key, message, signature): 24 | hash_obj = SHA256.new(message) 25 | verifier = DSS.new(public_key, "fips-186-3") 26 | try: 27 | verifier.verify(hash_obj, signature) 28 | return True 29 | except ValueError: 30 | return False 31 | 32 | 33 | # Hybrid Encryption using ECC and AES 34 | def ecc_encrypt(public_key, plaintext): 35 | # Generate ephemeral ECC key pair 36 | ephemeral_key = ECC.generate(curve="P-256") 37 | 38 | # Derive shared secret from ephemeral private key and recipient's public key 39 | shared_secret = ephemeral_key.d * public_key.pointQ 40 | 41 | # Derive AES key from the shared secret 42 | shared_key = scrypt( 43 | str(shared_secret.x).encode(), salt=b"ecc_salt", key_len=32, N=2**14, r=8, p=1 44 | ) 45 | 46 | # AES Encryption 47 | cipher_aes = AES.new(shared_key, AES.MODE_GCM) 48 | ciphertext, tag = cipher_aes.encrypt_and_digest(plaintext) 49 | 50 | # Return the ephemeral public key along with ciphertext, nonce, and tag 51 | return ephemeral_key.public_key(), cipher_aes.nonce, tag, ciphertext 52 | 53 | 54 | def ecc_decrypt(private_key, ephemeral_public_key, nonce, tag, ciphertext): 55 | # Derive shared secret from recipient's private key and ephemeral public key 56 | shared_secret = private_key.d * ephemeral_public_key.pointQ 57 | 58 | # Derive AES key from the shared secret 59 | shared_key = scrypt( 60 | str(shared_secret.x).encode(), salt=b"ecc_salt", key_len=32, N=2**14, r=8, p=1 61 | ) 62 | 63 | # AES Decryption 64 | cipher_aes = AES.new(shared_key, AES.MODE_GCM, nonce=nonce) 65 | plaintext = cipher_aes.decrypt_and_verify(ciphertext, tag) 66 | return plaintext 67 | 68 | 69 | # SHA-256 Hashing 70 | def sha256_hash(data): 71 | hash_obj = SHA256.new(data) 72 | return hash_obj.hexdigest() 73 | 74 | 75 | # SHA-1 Hashing 76 | def sha1_hash(data): 77 | hash_obj = SHA1.new(data) 78 | return hash_obj.hexdigest() 79 | 80 | 81 | # SHA-512 Hashing 82 | def sha512_hash(data): 83 | hash_obj = SHA512.new(data) 84 | return hash_obj.hexdigest() 85 | 86 | 87 | # Helper function to export ECC keys 88 | def export_private_key(private_key): 89 | return private_key.export_key(format="PEM") 90 | 91 | 92 | def export_public_key(public_key): 93 | return public_key.export_key(format="PEM") 94 | -------------------------------------------------------------------------------- /.configs/ecc/ecc_usage.py: -------------------------------------------------------------------------------- 1 | import ecc_config 2 | 3 | # Generate ECC key pair 4 | private_key, public_key = ecc_config.generate_ecc_key_pair() 5 | 6 | # Example message 7 | message = b"Hello, ECC and SHA owowowowoow!" 8 | 9 | 10 | ephemeral_pub_key, nonce, tag, ciphertext = ecc_config.ecc_encrypt(public_key, message) 11 | 12 | print(f"Ephemeral Public Key: {ephemeral_pub_key}") 13 | print(f"Nonce: {nonce}") 14 | print(f"Tag: {tag}") 15 | print(f"Ciphertext: {ciphertext}") 16 | 17 | decrypted_message = ecc_config.ecc_decrypt( 18 | private_key, ephemeral_pub_key, nonce, tag, ciphertext 19 | ) 20 | 21 | print(f"Decrypted Message: {decrypted_message}") 22 | 23 | # Sign and Verify 24 | signature = ecc_config.ecc_sign(private_key, ciphertext) 25 | is_valid = ecc_config.ecc_verify(public_key, ciphertext, signature) 26 | 27 | print("CIPHERTEXT") 28 | print(f"Signature Valid: {is_valid}") 29 | print(f"Message: {message}") 30 | 31 | signature = ecc_config.ecc_sign(private_key, ciphertext) 32 | is_valid = ecc_config.ecc_verify(public_key, ciphertext, signature) 33 | 34 | 35 | print("PLAINTEXT") 36 | print(f"Signature Valid: {is_valid}") 37 | print(f"Message: {message}") 38 | 39 | # Hashing using SHA 40 | sha256_hash_value = ecc_config.sha256_hash(message) 41 | sha1_hash_value = ecc_config.sha1_hash(message) 42 | sha512_hash_value = ecc_config.sha512_hash(message) 43 | 44 | # Print results 45 | print(f"Message: {message}") 46 | print(f"Signature Valid: {is_valid}") 47 | print(f"SHA-256: {sha256_hash_value}") 48 | print(f"SHA-1: {sha1_hash_value}") 49 | print(f"SHA-512: {sha512_hash_value}") 50 | -------------------------------------------------------------------------------- /.configs/elgamal/elgamal_config.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import getPrime, inverse, bytes_to_long 2 | from Crypto.Hash import SHA256, SHA1, SHA512 3 | import random 4 | from hashlib import sha256 5 | from math import gcd 6 | 7 | 8 | # ElGamal Key Generation 9 | def elgamal_keygen(bits=256): 10 | p = getPrime(bits) # Generate a large prime number p 11 | g = random.randint(2, p - 2) # Random generator g (2 <= g <= p-2) 12 | x = random.randint(1, p - 2) # Private key x (1 <= x <= p-2) 13 | h = pow(g, x, p) # h = g^x mod p 14 | public_key = (p, g, h) 15 | private_key = x 16 | return public_key, private_key 17 | 18 | 19 | # ElGamal Encryption 20 | def elgamal_encrypt(public_key, message): 21 | p, g, h = public_key 22 | y = random.randint(1, p - 2) # Random number y (1 <= y <= p-2) 23 | c1 = pow(g, y, p) # c1 = g^y mod p 24 | s = pow(h, y, p) # s = h^y mod p 25 | c2 = (message * s) % p # c2 = m * s mod p 26 | return (c1, c2) 27 | 28 | 29 | # ElGamal Decryption 30 | def elgamal_decrypt(private_key, public_key, ciphertext): 31 | p, g, h = public_key 32 | c1, c2 = ciphertext 33 | s = pow(c1, private_key, p) # s = c1^x mod p 34 | s_inv = inverse(s, p) # Modular inverse of s mod p 35 | m = (c2 * s_inv) % p # m = c2 * s_inv mod p 36 | return m 37 | 38 | 39 | # ElGamal Digital Signature Generation 40 | def elgamal_sign(private_key, message, public_key): 41 | p, g, _ = public_key 42 | k = random.randint(1, p - 2) 43 | 44 | # Ensure that k is coprime with p - 1 45 | while gcd(k, p - 1) != 1: # Check if gcd(k, p - 1) is 1 46 | k = random.randint(1, p - 2) 47 | 48 | r = pow(g, k, p) # r = g^k mod p 49 | m = bytes_to_long(sha256(message.encode()).digest()) 50 | s = (inverse(k, p - 1) * (m - private_key * r)) % (p - 1) 51 | return (r, s) 52 | 53 | 54 | # ElGamal Digital Signature Verification 55 | def elgamal_verify(public_key, message, signature): 56 | p, g, h = public_key 57 | r, s = signature 58 | if not (1 < r < p): 59 | return False 60 | 61 | m = bytes_to_long(sha256(message.encode()).digest()) 62 | v1 = (pow(h, r, p) * pow(r, s, p)) % p 63 | v2 = pow(g, m, p) 64 | return v1 == v2 65 | 66 | 67 | # SHA-256 Hashing 68 | def sha256_hash(data): 69 | hash_obj = SHA256.new(data) 70 | return hash_obj.hexdigest() 71 | 72 | 73 | # SHA-1 Hashing 74 | def sha1_hash(data): 75 | hash_obj = SHA1.new(data) 76 | return hash_obj.hexdigest() 77 | 78 | 79 | # SHA-512 Hashing 80 | def sha512_hash(data): 81 | hash_obj = SHA512.new(data) 82 | return hash_obj.hexdigest() 83 | 84 | 85 | # Helper functions to export keys 86 | def export_private_key(private_key): 87 | # implement this function 88 | pass 89 | 90 | 91 | def export_public_key(public_key): 92 | # implement this function 93 | pass 94 | -------------------------------------------------------------------------------- /.configs/elgamal/elgamal_usage.py: -------------------------------------------------------------------------------- 1 | import elgamal_config 2 | from Crypto.Util.number import bytes_to_long, long_to_bytes 3 | 4 | # Generate ElGamal key pair 5 | public_key, private_key = elgamal_config.elgamal_keygen() 6 | 7 | # Example message 8 | message = "Hello, ElGamal and SHA!" 9 | 10 | # Encrypt and Decrypt 11 | ciphertext = elgamal_config.elgamal_encrypt(public_key, bytes_to_long(message.encode())) 12 | decrypted_int = elgamal_config.elgamal_decrypt(private_key, public_key, ciphertext) 13 | decrypted_message = long_to_bytes(decrypted_int).decode() 14 | 15 | # Sign and Verify 16 | signature = elgamal_config.elgamal_sign(private_key, message, public_key) 17 | is_valid = elgamal_config.elgamal_verify(public_key, message, signature) 18 | 19 | # Export keys 20 | exported_private_key = elgamal_config.export_private_key(private_key) 21 | exported_public_key = elgamal_config.export_public_key(public_key) 22 | 23 | # Hashing using SHA 24 | sha256_hash_value = elgamal_config.sha256_hash(message.encode()) 25 | sha1_hash_value = elgamal_config.sha1_hash(message.encode()) 26 | sha512_hash_value = elgamal_config.sha512_hash(message.encode()) 27 | 28 | # Print outputs 29 | print(f"Original Message: {message}") 30 | print(f"Ciphertext: {ciphertext}") 31 | print(f"Decrypted Message: {decrypted_message}") 32 | print(f"Signature: {signature}") 33 | print(f"Signature Valid: {is_valid}") 34 | print(f"SHA-256: {sha256_hash_value}") 35 | print(f"SHA-1: {sha1_hash_value}") 36 | print(f"SHA-512: {sha512_hash_value}") 37 | print(f"Exported Private Key: {exported_private_key}") 38 | print(f"Exported Public Key: {exported_public_key}") 39 | 40 | # # Demonstrate key import 41 | # imported_private_key = elgamal_config.import_private_key(exported_private_key) 42 | # imported_public_key = elgamal_config.import_public_key(exported_public_key) 43 | 44 | # # Ensure imported keys work 45 | # ciphertext_imported = elgamal_config.elgamal_encrypt( 46 | # imported_public_key, bytes_to_long(message.encode()) 47 | # ) 48 | # decrypted_int_imported = elgamal_config.elgamal_decrypt( 49 | # imported_private_key, imported_public_key, ciphertext_imported 50 | # ) 51 | # decrypted_message_imported = long_to_bytes(decrypted_int_imported).decode() 52 | 53 | # print(f"Decrypted Message with Imported Keys: {decrypted_message_imported}") 54 | -------------------------------------------------------------------------------- /.configs/hashes/hash_config.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import random 3 | import string 4 | import time 5 | from collections import defaultdict 6 | 7 | 8 | # Generate a random string of a given length 9 | def generate_random_string(length=8): 10 | letters = string.ascii_letters + string.digits 11 | return "".join(random.choice(letters) for i in range(length)) 12 | 13 | 14 | # Generate a dataset of random strings 15 | def generate_dataset(size=50, min_length=8, max_length=16): 16 | return [ 17 | generate_random_string(random.randint(min_length, max_length)) 18 | for _ in range(size) 19 | ] 20 | 21 | 22 | # Hashing using MD5 23 | def md5_hash(data): 24 | return hashlib.md5(data.encode()).hexdigest() 25 | 26 | 27 | # Hashing using SHA-1 28 | def sha1_hash(data): 29 | return hashlib.sha1(data.encode()).hexdigest() 30 | 31 | 32 | # Hashing using SHA-256 33 | def sha256_hash(data): 34 | return hashlib.sha256(data.encode()).hexdigest() 35 | 36 | 37 | # Measure hash computation time for a given hash function 38 | def measure_time(hash_function, data): 39 | start_time = time.time() 40 | hash_value = hash_function(data) 41 | end_time = time.time() 42 | return hash_value, end_time - start_time 43 | 44 | 45 | # Detect collisions in the hashed dataset 46 | def detect_collisions(hash_values): 47 | collision_dict = defaultdict(list) 48 | collisions = [] 49 | for i, hash_value in enumerate(hash_values): 50 | collision_dict[hash_value].append(i) 51 | if len(collision_dict[hash_value]) > 1: 52 | collisions.append(hash_value) 53 | return collisions 54 | 55 | 56 | # Perform the experiment 57 | def analyze_hashing_performance(dataset): 58 | md5_times, sha1_times, sha256_times = [], [], [] 59 | md5_hashes, sha1_hashes, sha256_hashes = [], [], [] 60 | 61 | for data in dataset: 62 | # Measure and store MD5 hash time and value 63 | md5_hash_val, md5_time = measure_time(md5_hash, data) 64 | md5_times.append(md5_time) 65 | md5_hashes.append(md5_hash_val) 66 | 67 | # Measure and store SHA-1 hash time and value 68 | sha1_hash_val, sha1_time = measure_time(sha1_hash, data) 69 | sha1_times.append(sha1_time) 70 | sha1_hashes.append(sha1_hash_val) 71 | 72 | # Measure and store SHA-256 hash time and value 73 | sha256_hash_val, sha256_time = measure_time(sha256_hash, data) 74 | sha256_times.append(sha256_time) 75 | sha256_hashes.append(sha256_hash_val) 76 | 77 | # Detect collisions for each hashing algorithm 78 | md5_collisions = detect_collisions(md5_hashes) 79 | sha1_collisions = detect_collisions(sha1_hashes) 80 | sha256_collisions = detect_collisions(sha256_hashes) 81 | 82 | # Performance results 83 | results = { 84 | "md5": { 85 | "avg_time": sum(md5_times) / len(md5_times), 86 | "collisions": md5_collisions, 87 | }, 88 | "sha1": { 89 | "avg_time": sum(sha1_times) / len(sha1_times), 90 | "collisions": sha1_collisions, 91 | }, 92 | "sha256": { 93 | "avg_time": sum(sha256_times) / len(sha256_times), 94 | "collisions": sha256_collisions, 95 | }, 96 | } 97 | 98 | return results 99 | -------------------------------------------------------------------------------- /.configs/hashes/hash_usage.py: -------------------------------------------------------------------------------- 1 | import hash_config 2 | 3 | # Generate a dataset of 100 random strings (with lengths between 8 and 16 characters) 4 | dataset = hash_config.generate_dataset(size=100) 5 | 6 | # Run the analysis 7 | results = hash_config.analyze_hashing_performance(dataset) 8 | 9 | # Print results 10 | print("MD5 Average Time: ", results["md5"]["avg_time"]) 11 | print("MD5 Collisions: ", results["md5"]["collisions"]) 12 | 13 | print("SHA-1 Average Time: ", results["sha1"]["avg_time"]) 14 | print("SHA-1 Collisions: ", results["sha1"]["collisions"]) 15 | 16 | print("SHA-256 Average Time: ", results["sha256"]["avg_time"]) 17 | print("SHA-256 Collisions: ", results["sha256"]["collisions"]) 18 | -------------------------------------------------------------------------------- /.configs/hospital.py: -------------------------------------------------------------------------------- 1 | import elgamal.elgamal_config as elgamal_config 2 | from Crypto.Util.number import bytes_to_long, long_to_bytes 3 | from Crypto.Cipher import AES 4 | from Crypto.Random import get_random_bytes 5 | 6 | 7 | class Hospital: 8 | def __init__(self): 9 | self.records = {} # Encrypted patient records 10 | self.encrypted_keys = ( 11 | {} 12 | ) # Store encrypted symmetric keys for doctors and nurses 13 | self.signatures = {} # Store digital signatures for integrity checks 14 | self.doctors = {} # Dictionary to store doctor keys 15 | self.nurses = {} # Dictionary to store nurse keys 16 | 17 | def add_doctor(self, doctor_id): 18 | public_key, private_key = elgamal_config.elgamal_keygen() 19 | self.doctors[doctor_id] = {"public_key": public_key, "private_key": private_key} 20 | print(f"Doctor {doctor_id} added.") 21 | 22 | def add_nurse(self, nurse_id): 23 | public_key, private_key = elgamal_config.elgamal_keygen() 24 | self.nurses[nurse_id] = {"public_key": public_key, "private_key": private_key} 25 | print(f"Nurse {nurse_id} added.") 26 | 27 | def _encrypt_symmetric_key(self, aes_key, public_key): 28 | """Encrypt AES symmetric key using ElGamal public key""" 29 | aes_key_as_int = bytes_to_long(aes_key) 30 | encrypted_key = elgamal_config.elgamal_encrypt(public_key, aes_key_as_int) 31 | return encrypted_key 32 | 33 | def _decrypt_symmetric_key(self, encrypted_key, private_key, public_key): 34 | """Decrypt AES symmetric key using ElGamal private key""" 35 | decrypted_key_int = elgamal_config.elgamal_decrypt( 36 | private_key, public_key, encrypted_key 37 | ) 38 | aes_key = long_to_bytes(decrypted_key_int) 39 | return aes_key 40 | 41 | def add_patient_record(self, patient_id, record, doctor_id): 42 | """Only doctors can add and modify patient records""" 43 | if doctor_id not in self.doctors: 44 | raise PermissionError("Only doctors can add or modify patient records.") 45 | 46 | doctor_public_key = self.doctors[doctor_id]["public_key"] 47 | 48 | # Generate a symmetric AES key 49 | aes_key = get_random_bytes(16) 50 | 51 | # Encrypt the patient record with the AES key 52 | cipher = AES.new(aes_key, AES.MODE_EAX) 53 | ciphertext, tag = cipher.encrypt_and_digest(record.encode()) 54 | 55 | # Encrypt the AES key with the doctor's public key and each nurse's public key 56 | encrypted_keys = { 57 | "doctor": self._encrypt_symmetric_key(aes_key, doctor_public_key) 58 | } 59 | for nurse_id in self.nurses: 60 | nurse_public_key = self.nurses[nurse_id]["public_key"] 61 | encrypted_keys[nurse_id] = self._encrypt_symmetric_key( 62 | aes_key, nurse_public_key 63 | ) 64 | 65 | # Digitally sign the record for integrity using the doctor's private key 66 | doctor_private_key = self.doctors[doctor_id]["private_key"] 67 | signature = elgamal_config.elgamal_sign( 68 | doctor_private_key, record, doctor_public_key 69 | ) 70 | 71 | # Store the encrypted record, AES encrypted keys, and signature 72 | self.records[patient_id] = { 73 | "ciphertext": ciphertext, 74 | "tag": tag, 75 | "nonce": cipher.nonce, 76 | } 77 | self.encrypted_keys[patient_id] = encrypted_keys 78 | self.signatures[patient_id] = signature 79 | 80 | print(f"Patient {patient_id}'s record added by Doctor {doctor_id}.") 81 | 82 | def view_patient_record(self, user_id, patient_id): 83 | """Both doctors and nurses can view patient records""" 84 | if user_id in self.doctors: 85 | private_key = self.doctors[user_id]["private_key"] 86 | public_key = self.doctors[user_id]["public_key"] 87 | elif user_id in self.nurses: 88 | private_key = self.nurses[user_id]["private_key"] 89 | public_key = self.nurses[user_id]["public_key"] 90 | else: 91 | raise PermissionError("Only doctors and nurses can view patient records.") 92 | 93 | # Retrieve the encrypted record and encrypted AES key 94 | record_data = self.records.get(patient_id) 95 | if not record_data: 96 | raise ValueError(f"No record found for Patient {patient_id}.") 97 | encrypted_key = self.encrypted_keys[patient_id].get( 98 | user_id if user_id in self.nurses else "doctor" 99 | ) 100 | 101 | # Decrypt the AES key using the user's private key 102 | aes_key = self._decrypt_symmetric_key(encrypted_key, private_key, public_key) 103 | 104 | # Decrypt the patient record using the AES key 105 | cipher = AES.new(aes_key, AES.MODE_EAX, nonce=record_data["nonce"]) 106 | decrypted_record = cipher.decrypt_and_verify( 107 | record_data["ciphertext"], record_data["tag"] 108 | ).decode() 109 | 110 | # Verify the integrity of the record using the stored signature 111 | signature = self.signatures.get(patient_id) 112 | 113 | print(f"Signature generated for patient record: {signature}") 114 | print(f"Decrypted record: {decrypted_record}") 115 | print(f"Signature being verified: {signature}") 116 | 117 | if not elgamal_config.elgamal_verify(public_key, decrypted_record, signature): 118 | raise ValueError("Record integrity verification failed!") 119 | 120 | print(f"Record for Patient {patient_id}: {decrypted_record}") 121 | return decrypted_record 122 | 123 | def modify_patient_record(self, doctor_id, patient_id, new_record): 124 | """Only doctors can modify patient records""" 125 | if doctor_id not in self.doctors: 126 | raise PermissionError("Only doctors can modify patient records.") 127 | 128 | # Same process as adding a record, but for modification 129 | self.add_patient_record(patient_id, new_record, doctor_id) 130 | print(f"Patient {patient_id}'s record modified by Doctor {doctor_id}.") 131 | 132 | 133 | # Example usage of Hospital system 134 | if __name__ == "__main__": 135 | hospital = Hospital() 136 | 137 | # Adding doctors and nurses 138 | hospital.add_doctor("doc1") 139 | hospital.add_nurse("nurse1") 140 | 141 | # Doctor adds a patient record 142 | hospital.add_patient_record("patient1", "Patient 1 has a mild fever", "doc1") 143 | 144 | # Nurse tries to view the patient record 145 | hospital.view_patient_record("nurse1", "patient1") 146 | 147 | # Doctor modifies the patient record 148 | hospital.modify_patient_record( 149 | "doc1", "patient1", "Patient 1 has been prescribed medication" 150 | ) 151 | 152 | # Nurse views the modified patient record 153 | hospital.view_patient_record("nurse1", "patient1") 154 | -------------------------------------------------------------------------------- /.configs/rabin/rabin_config.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util import number 2 | 3 | 4 | class Rabin: 5 | def __init__(self, bit_length=512): 6 | self.bit_length = bit_length 7 | self.public_key, self.private_key = self.generate_keypair(bit_length) 8 | 9 | def generate_keypair(self, bit_length): 10 | p = self.generate_prime(bit_length // 2) 11 | q = self.generate_prime(bit_length // 2) 12 | n = p * q # Public key modulus 13 | return n, (p, q) 14 | 15 | def generate_prime(self, bits): 16 | while True: 17 | p = number.getPrime(bits) 18 | if p % 4 == 3: 19 | return p 20 | 21 | def encrypt(self, message): 22 | n = self.public_key 23 | ciphertext = pow(message, 2, n) 24 | return ciphertext 25 | 26 | def decrypt(self, ciphertext): 27 | p, q = self.private_key 28 | mp = pow(ciphertext, (p + 1) // 4, p) 29 | mq = pow(ciphertext, (q + 1) // 4, q) 30 | 31 | gcd, yp, yq = self.egcd(p, q) 32 | r1 = (yp * p * mq + yq * q * mp) % self.public_key 33 | r2 = self.public_key - r1 34 | r3 = (yp * p * mq - yq * q * mp) % self.public_key 35 | r4 = self.public_key - r3 36 | 37 | return r1, r2, r3, r4 38 | 39 | def egcd(self, a, b): 40 | x0, x1, y0, y1 = 1, 0, 0, 1 41 | while b != 0: 42 | q, a, b = a // b, b, a % b 43 | x0, x1 = x1, x0 - q * x1 44 | y0, y1 = y1, y0 - q * y1 45 | return a, x0, y0 46 | 47 | def int_to_bytes(self, number): 48 | return number.to_bytes((number.bit_length() + 7) // 8, byteorder="big") 49 | 50 | def bytes_to_int(self, data): 51 | return int.from_bytes(data, byteorder="big") 52 | 53 | 54 | if __name__ == "__main__": 55 | rabin = Rabin() 56 | message = b"Hello, world!" 57 | 58 | # Convert message to an integer for encryption 59 | message_int = rabin.bytes_to_int(message) 60 | ciphertext = rabin.encrypt(message_int) 61 | plaintexts = rabin.decrypt(ciphertext) 62 | 63 | print(f"Message (original): {message}") 64 | print(f"Ciphertext: {ciphertext}") 65 | 66 | # Find the correct plaintext by directly comparing with the original message 67 | found_match = False 68 | for i, plaintext in enumerate(plaintexts): 69 | # Convert the plaintext integer back to bytes 70 | plaintext_bytes = rabin.int_to_bytes(plaintext) 71 | 72 | # Debug output for each possible plaintext 73 | print(f"Plaintext option {i + 1}: {plaintext}") 74 | print(f"Plaintext bytes (decoded): {plaintext_bytes}") 75 | 76 | # Check if this matches the original message 77 | if plaintext_bytes == message: 78 | print( 79 | f"Decrypted message (matching): {plaintext_bytes.decode(errors='ignore')}" 80 | ) 81 | found_match = True 82 | break 83 | 84 | if not found_match: 85 | print("No matching decrypted message found.") 86 | -------------------------------------------------------------------------------- /.configs/rsa/rsa_config.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import RSA 2 | from Crypto.Cipher import PKCS1_OAEP 3 | from Crypto.Signature import pkcs1_15 4 | from Crypto.Hash import SHA256 5 | 6 | 7 | # Generate RSA Key Pair 8 | def generate_rsa_key_pair(): 9 | key = RSA.generate(2048) 10 | private_key = key 11 | public_key = key.publickey() 12 | return private_key, public_key 13 | 14 | 15 | # RSA Encryption 16 | def rsa_encrypt(public_key, plaintext): 17 | cipher = PKCS1_OAEP.new(public_key) 18 | ciphertext = cipher.encrypt(plaintext) 19 | return ciphertext 20 | 21 | 22 | # RSA Decryption 23 | def rsa_decrypt(private_key, ciphertext): 24 | cipher = PKCS1_OAEP.new(private_key) 25 | plaintext = cipher.decrypt(ciphertext) 26 | return plaintext 27 | 28 | 29 | # RSA Digital Signature Creation 30 | def rsa_sign(private_key, message): 31 | h = SHA256.new(message) 32 | signature = pkcs1_15.new(private_key).sign(h) 33 | return signature 34 | 35 | 36 | # RSA Digital Signature Verification 37 | def rsa_verify(public_key, message, signature): 38 | h = SHA256.new(message) 39 | try: 40 | pkcs1_15.new(public_key).verify(h, signature) 41 | return True 42 | except (ValueError, TypeError): 43 | return False 44 | 45 | 46 | # Helper functions to export keys 47 | def export_private_key(private_key): 48 | return private_key.export_key() 49 | 50 | 51 | def export_public_key(public_key): 52 | return public_key.export_key() 53 | -------------------------------------------------------------------------------- /.configs/rsa/rsa_usage.py: -------------------------------------------------------------------------------- 1 | import rsa_config 2 | 3 | # Generate RSA key pair 4 | private_key, public_key = rsa_config.generate_rsa_key_pair() 5 | 6 | # Example message 7 | message = b"Hello, Crypto!" 8 | 9 | # Encrypt and Decrypt 10 | ciphertext = rsa_config.rsa_encrypt(public_key, message) 11 | plaintext = rsa_config.rsa_decrypt(private_key, ciphertext) 12 | 13 | # Sign and Verify 14 | signature = rsa_config.rsa_sign(private_key, message) 15 | is_valid = rsa_config.rsa_verify(public_key, message, signature) 16 | 17 | print(f"Message: {message}") 18 | print(f"Ciphertext: {ciphertext}") 19 | print(f"Decrypted: {plaintext}") 20 | print(f"Signature Valid: {is_valid}") 21 | -------------------------------------------------------------------------------- /.configs/sockets/client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import hashlib 3 | 4 | 5 | def compute_hash(data: bytes) -> str: 6 | """ 7 | Compute the SHA-256 hash of the given data. 8 | 9 | Args: 10 | data (bytes): The data to hash. 11 | 12 | Returns: 13 | str: The computed SHA-256 hash as a hexadecimal string. 14 | """ 15 | return hashlib.sha256(data).hexdigest() 16 | 17 | 18 | def send_data(client_socket: socket.socket, data: bytes) -> None: 19 | """ 20 | Send data to the server via the client socket. 21 | 22 | Args: 23 | client_socket (socket.socket): The socket connection to the server. 24 | data (bytes): The data to send to the server. 25 | """ 26 | client_socket.send(data) 27 | 28 | 29 | def receive_data(client_socket: socket.socket, buffer_size: int = 64) -> str: 30 | """ 31 | Receive data from the server via the client socket. 32 | 33 | Args: 34 | client_socket (socket.socket): The socket connection to the server. 35 | buffer_size (int): The buffer size for receiving data. 36 | 37 | Returns: 38 | str: The data received from the server as a decoded string. 39 | """ 40 | return client_socket.recv(buffer_size).decode() 41 | 42 | 43 | def start_client(server_host: str = "127.0.0.1", server_port: int = 65432) -> None: 44 | """ 45 | Start the client, send data to the server, and verify data integrity. 46 | 47 | Args: 48 | server_host (str): The server's hostname or IP address. 49 | server_port (int): The port to connect to on the server. 50 | """ 51 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 52 | client_socket.connect((server_host, server_port)) 53 | 54 | try: 55 | # Define the data to send 56 | data = b"Hi since you are copying my code, leave a star at https://github.com/hackerbone/HackerLLMBench, thx!" 57 | 58 | # Compute hash of the data before sending 59 | expected_hash = compute_hash(data) 60 | 61 | # Send data to the server 62 | send_data(client_socket, data) 63 | 64 | # Receive the hash from the server 65 | received_hash = receive_data(client_socket) 66 | 67 | # Verify the hash 68 | if expected_hash == received_hash: 69 | print("Data integrity verified. No corruption or tampering detected.") 70 | else: 71 | print("Data integrity check failed. Possible corruption or tampering.") 72 | finally: 73 | client_socket.close() 74 | 75 | 76 | if __name__ == "__main__": 77 | start_client() 78 | -------------------------------------------------------------------------------- /.configs/sockets/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import hashlib 3 | from typing import Tuple 4 | 5 | 6 | def compute_hash(data: bytes) -> str: 7 | """ 8 | Compute the SHA-256 hash of the given data. 9 | 10 | Args: 11 | data (bytes): The data to hash. 12 | 13 | Returns: 14 | str: The computed SHA-256 hash as a hexadecimal string. 15 | """ 16 | return hashlib.sha256(data).hexdigest() 17 | 18 | 19 | def handle_client_connection(client_socket: socket.socket) -> None: 20 | """ 21 | Handle an incoming client connection by receiving data, 22 | computing its hash, and sending the hash back to the client. 23 | 24 | Args: 25 | client_socket (socket.socket): The socket connection to the client. 26 | """ 27 | try: 28 | # Receive data from the client 29 | data = receive_data(client_socket) 30 | if not data: 31 | return 32 | 33 | # Compute hash of the received data 34 | received_hash = compute_hash(data) 35 | 36 | # Send the hash back to the client 37 | send_data(client_socket, received_hash.encode()) 38 | finally: 39 | client_socket.close() 40 | 41 | 42 | def receive_data(client_socket: socket.socket, buffer_size: int = 1024) -> bytes: 43 | """ 44 | Receive data from the client socket. 45 | 46 | Args: 47 | client_socket (socket.socket): The socket connection to the client. 48 | buffer_size (int): The buffer size for receiving data. 49 | 50 | Returns: 51 | bytes: The data received from the client. 52 | """ 53 | return client_socket.recv(buffer_size) 54 | 55 | 56 | def send_data(client_socket: socket.socket, data: bytes) -> None: 57 | """ 58 | Send data to the client socket. 59 | 60 | Args: 61 | client_socket (socket.socket): The socket connection to the client. 62 | data (bytes): The data to send to the client. 63 | """ 64 | client_socket.send(data) 65 | 66 | 67 | def start_server(host: str = "127.0.0.1", port: int = 65432) -> None: 68 | """ 69 | Start the server, listen for incoming connections, and handle each client. 70 | 71 | Args: 72 | host (str): The server's hostname or IP address. 73 | port (int): The port to listen on. 74 | """ 75 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 76 | server_socket.bind((host, port)) 77 | server_socket.listen(1) 78 | print(f"Server listening on {host}:{port}") 79 | 80 | while True: 81 | client_socket, addr = accept_connection(server_socket) 82 | print(f"Accepted connection from {addr}") 83 | handle_client_connection(client_socket) 84 | 85 | 86 | def accept_connection( 87 | server_socket: socket.socket, 88 | ) -> Tuple[socket.socket, Tuple[str, int]]: 89 | """ 90 | Accept a new connection from a client. 91 | 92 | Args: 93 | server_socket (socket.socket): The server's listening socket. 94 | 95 | Returns: 96 | Tuple[socket.socket, Tuple[str, int]]: The client socket and address. 97 | """ 98 | return server_socket.accept() 99 | 100 | 101 | if __name__ == "__main__": 102 | start_server() 103 | -------------------------------------------------------------------------------- /.configs/symmetric/symmetric_config.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import AES, DES 2 | from Crypto.Random import get_random_bytes 3 | from Crypto.Util.Padding import pad, unpad 4 | 5 | 6 | # AES Encryption 7 | def aes_encrypt(key, plaintext, mode=AES.MODE_CBC): 8 | cipher = AES.new(key, mode) 9 | iv = cipher.iv # Initialization vector (IV) for modes like CBC 10 | ciphertext = cipher.encrypt(pad(plaintext, AES.block_size)) 11 | return iv + ciphertext # Return IV + ciphertext for decryption 12 | 13 | 14 | # AES Decryption 15 | def aes_decrypt(key, ciphertext, mode=AES.MODE_CBC): 16 | iv = ciphertext[: AES.block_size] 17 | ciphertext = ciphertext[AES.block_size :] 18 | cipher = AES.new(key, mode, iv=iv) 19 | plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size) 20 | return plaintext 21 | 22 | 23 | # DES Encryption 24 | def des_encrypt(key, plaintext, mode=DES.MODE_CBC): 25 | cipher = DES.new(key, mode) 26 | iv = cipher.iv # Initialization vector (IV) for modes like CBC 27 | ciphertext = cipher.encrypt(pad(plaintext, DES.block_size)) 28 | return iv + ciphertext # Return IV + ciphertext for decryption 29 | 30 | 31 | # DES Decryption 32 | def des_decrypt(key, ciphertext, mode=DES.MODE_CBC): 33 | iv = ciphertext[: DES.block_size] 34 | ciphertext = ciphertext[DES.block_size :] 35 | cipher = DES.new(key, mode, iv=iv) 36 | plaintext = unpad(cipher.decrypt(ciphertext), DES.block_size) 37 | return plaintext 38 | 39 | 40 | # Double DES Encryption 41 | def double_des_encrypt(key1, key2, plaintext, mode=DES.MODE_CBC): 42 | cipher1 = DES.new(key1, mode) 43 | iv1 = cipher1.iv 44 | intermediate = cipher1.encrypt(pad(plaintext, DES.block_size)) 45 | 46 | cipher2 = DES.new(key2, mode, iv=iv1) 47 | ciphertext = cipher2.encrypt(intermediate) 48 | return iv1 + ciphertext # Return IV + ciphertext for decryption 49 | 50 | 51 | # Double DES Decryption 52 | def double_des_decrypt(key1, key2, ciphertext, mode=DES.MODE_CBC): 53 | iv1 = ciphertext[: DES.block_size] 54 | ciphertext = ciphertext[DES.block_size :] 55 | 56 | cipher2 = DES.new(key2, mode, iv=iv1) 57 | intermediate = cipher2.decrypt(ciphertext) 58 | 59 | cipher1 = DES.new(key1, mode, iv=iv1) 60 | plaintext = unpad(cipher1.decrypt(intermediate), DES.block_size) 61 | return plaintext 62 | 63 | 64 | # Helper function to generate random keys 65 | def generate_aes_key(size=32): # Default is 256-bit key 66 | return get_random_bytes(size) 67 | 68 | 69 | def generate_des_key(): 70 | return get_random_bytes(8) # 64-bit DES key 71 | 72 | 73 | def generate_double_des_key(): 74 | return get_random_bytes(8), get_random_bytes(8) # Two 64-bit DES keys 75 | -------------------------------------------------------------------------------- /.configs/symmetric/symmetric_usage.py: -------------------------------------------------------------------------------- 1 | import symmetric_config 2 | 3 | # AES Example 4 | aes_key = symmetric_config.generate_aes_key() 5 | aes_message = b"Hello, AES!" 6 | aes_ciphertext = symmetric_config.aes_encrypt(aes_key, aes_message) 7 | aes_plaintext = symmetric_config.aes_decrypt(aes_key, aes_ciphertext) 8 | 9 | # DES Example 10 | des_key = symmetric_config.generate_des_key() 11 | des_message = b"Hello, DES!" 12 | des_ciphertext = symmetric_config.des_encrypt(des_key, des_message) 13 | des_plaintext = symmetric_config.des_decrypt(des_key, des_ciphertext) 14 | 15 | # Double DES Example 16 | double_des_key1, double_des_key2 = symmetric_config.generate_double_des_key() 17 | double_des_message = b"Hello, Double DES!" 18 | double_des_ciphertext = symmetric_config.double_des_encrypt( 19 | double_des_key1, double_des_key2, double_des_message 20 | ) 21 | double_des_plaintext = symmetric_config.double_des_decrypt( 22 | double_des_key1, double_des_key2, double_des_ciphertext 23 | ) 24 | 25 | # Print results 26 | print(f"AES Ciphertext: {aes_ciphertext}") 27 | print(f"AES Decrypted: {aes_plaintext}") 28 | 29 | print(f"DES Ciphertext: {des_ciphertext}") 30 | print(f"DES Decrypted: {des_plaintext}") 31 | 32 | print(f"Double DES Ciphertext: {double_des_ciphertext}") 33 | print(f"Double DES Decrypted: {double_des_plaintext}") 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | 3 | **/__pycache__/** -------------------------------------------------------------------------------- /IS Lab Manual l1-4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hackerbone/infosec_lab/2d46bb6d54fa7ed6512dd36bb46e09dfead8802f/IS Lab Manual l1-4.pdf -------------------------------------------------------------------------------- /Lab No 5 and 6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hackerbone/infosec_lab/2d46bb6d54fa7ed6512dd36bb46e09dfead8802f/Lab No 5 and 6.pdf -------------------------------------------------------------------------------- /Lab_No_7_to_8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hackerbone/infosec_lab/2d46bb6d54fa7ed6512dd36bb46e09dfead8802f/Lab_No_7_to_8.pdf -------------------------------------------------------------------------------- /chat.py: -------------------------------------------------------------------------------- 1 | # Python program to implement client side of chat room. 2 | import socket 3 | import select 4 | import sys 5 | 6 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 | 8 | IP_address = "172.16.57.143" 9 | Port = 8080 10 | server.connect((IP_address, Port)) 11 | 12 | while True: 13 | 14 | # maintains a list of possible input streams 15 | sockets_list = [sys.stdin, server] 16 | 17 | """ There are two possible input situations. Either the 18 | user wants to give manual input to send to other people, 19 | or the server is sending a message to be printed on the 20 | screen. Select returns from sockets_list, the stream that 21 | is reader for input. So for example, if the server wants 22 | to send a message, then the if condition will hold true 23 | below.If the user wants to send a message, the else 24 | condition will evaluate as true""" 25 | read_sockets,write_socket, error_socket = select.select(sockets_list,[],[]) 26 | 27 | for socks in read_sockets: 28 | if socks == server: 29 | message = socks.recv(2048) 30 | print (message) 31 | else: 32 | message = sys.stdin.readline() 33 | server.send(message) 34 | sys.stdout.write("") 35 | sys.stdout.write(message) 36 | sys.stdout.flush() 37 | server.close() 38 | -------------------------------------------------------------------------------- /client_endsem.py: -------------------------------------------------------------------------------- 1 | # Voting system 2 | 3 | """ 4 | Voter data: 5 | name of voter 6 | name of contestor 7 | Vote 8 | 9 | 10 | Eg: 11 | Voter1, Contestor1, 1 12 | 13 | 14 | The voter client hashes their vote binary 0 or 1 using SHA256 and sends it to the server. 15 | 16 | hashed vote is encrypted using Elgamal encryption and sent to the server (Elgamal supports homomorphic encryption XOR) 17 | 18 | The client signs the encrypted vote usingt their RSA private key, generating a digital signature 19 | 20 | the client sends the encrypted vote and the digital signature to the server 21 | 22 | 23 | Server Side: 24 | 1. Server receieves encrypted vote and signature 25 | 2. Signature verification using RSA 26 | 3. After verification use XOR to combine and calc final result (Homomorphic) 27 | 4. Send tally back to client 28 | """ 29 | 30 | 31 | import socket 32 | import hashlib 33 | import json 34 | from elgamal.elgamal_config import * 35 | from rsa.rsa_config import * 36 | from Crypto.Util.number import bytes_to_long, long_to_bytes 37 | 38 | public_key, private_key = elgamal_keygen() 39 | 40 | print("Elgamal Public Key: ", public_key) 41 | print("Elgamal Private Key: ", private_key) 42 | 43 | # # export and save rsa key in key.pem file 44 | # with open("key.pem", "wb") as f: 45 | # f.write(rsa_private_key.export_key()) 46 | 47 | # with open("public_key.pem", "wb") as f: 48 | # f.write(rsa_public_key.export_key()) 49 | 50 | # import rsa key from key.pem file 51 | with open("key.pem", "rb") as f: 52 | rsa_private_key = RSA.import_key(f.read()) 53 | 54 | with open("public_key.pem", "rb") as f: 55 | rsa_public_key = RSA.import_key(f.read()) 56 | 57 | print("RSA Public Key: ", rsa_public_key) 58 | print("RSA Private Key: ", rsa_private_key) 59 | 60 | def compute_hash(data: bytes) -> str: 61 | """ 62 | Compute the SHA-256 hash of the given data. 63 | 64 | Args: 65 | data (bytes): The data to hash. 66 | 67 | Returns: 68 | str: The computed SHA-256 hash as a hexadecimal string. 69 | """ 70 | return hashlib.sha256(data).hexdigest() 71 | 72 | 73 | def send_data(client_socket: socket.socket, data: bytes) -> None: 74 | """ 75 | Send data to the server via the client socket. 76 | 77 | Args: 78 | client_socket (socket.socket): The socket connection to the server. 79 | data (bytes): The data to send to the server. 80 | """ 81 | client_socket.send(data) 82 | 83 | 84 | def receive_data(client_socket: socket.socket, buffer_size: int = 64) -> str: 85 | """ 86 | Receive data from the server via the client socket. 87 | 88 | Args: 89 | client_socket (socket.socket): The socket connection to the server. 90 | buffer_size (int): The buffer size for receiving data. 91 | 92 | Returns: 93 | str: The data received from the server as a decoded string. 94 | """ 95 | return client_socket.recv(buffer_size).decode() 96 | 97 | 98 | def start_client(server_host: str = "127.0.0.1", server_port: int = 65432) -> None: 99 | """ 100 | Start the client, send data to the server, and verify data integrity. 101 | 102 | Args: 103 | server_host (str): The server's hostname or IP address. 104 | server_port (int): The port to connect to on the server. 105 | """ 106 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 107 | client_socket.connect((server_host, server_port)) 108 | 109 | try: 110 | # Send data 111 | vote_info = { 112 | "name": "A", 113 | "contestor": "BA", 114 | "elgamal_private_key": private_key, 115 | "elgamal_public_key": public_key, 116 | "votes": [{ 117 | "name": "A", 118 | "contestor": "BA", 119 | "vote": 1 120 | }, 121 | { 122 | "name": "B", 123 | "contestor": "BA", 124 | "vote": 0 125 | }, 126 | { 127 | "name": "C", 128 | "contestor": "BA", 129 | "vote": 1 130 | }, 131 | { 132 | "name": "D", 133 | "contestor": "BB", 134 | "vote": 1 135 | } 136 | ] 137 | } 138 | 139 | for vote in vote_info["votes"]: 140 | vote_hash = compute_hash(str(vote["vote"]).encode()) 141 | vote_hash = bytes_to_long(vote_hash.encode()) 142 | enc_vote_hash = elgamal_encrypt(public_key, vote_hash) 143 | c1 = str(enc_vote_hash[0]).encode() 144 | c2 = str(enc_vote_hash[1]).encode() 145 | 146 | signature_c1 = rsa_sign(rsa_private_key, c1) 147 | signature_c2 = rsa_sign(rsa_private_key, c2) 148 | 149 | # how to send byte signature in json serializable way 150 | json_signature_c1 = bytes_to_long(signature_c1) 151 | json_signature_c2 = bytes_to_long(signature_c2) 152 | 153 | vote["signature_c1"] = json_signature_c1 154 | vote["signature_c2"] = json_signature_c2 155 | 156 | vote["c1"] = c1.decode() 157 | vote["c2"] = c2.decode() 158 | 159 | # vote_hash = compute_hash(b'1') 160 | # print("vote hash init", vote_hash) 161 | # vote_hash = bytes_to_long(vote_hash.encode()) 162 | # enc_vote_hash = elgamal_encrypt(public_key, vote_hash) 163 | # print("vote hash long", vote_hash) 164 | # print("encrypted vote hash", enc_vote_hash) 165 | 166 | # c1 = str(enc_vote_hash[0]).encode() 167 | # c2 = str(enc_vote_hash[1]).encode() 168 | 169 | # signature_c1 = rsa_sign(rsa_private_key, c1) 170 | # signature_c2 = rsa_sign(rsa_private_key, c2) 171 | 172 | # # how to send byte signature in json serializable way 173 | # json_signature_c1 = bytes_to_long(signature_c1) 174 | # json_signature_c2 = bytes_to_long(signature_c2) 175 | 176 | # vote_info["signature_c1"] = json_signature_c1 177 | # vote_info["signature_c2"] = json_signature_c2 178 | 179 | # vote_info["c1"] = c1.decode() 180 | # vote_info["c2"] = c2.decode() 181 | 182 | print(vote_info) 183 | 184 | message = json.dumps(vote_info).encode("utf-8") 185 | 186 | print("message to be sent", message) 187 | 188 | # Send data to the server 189 | send_data(client_socket, message) 190 | 191 | # Receive the hash from the server 192 | tally = receive_data(client_socket) 193 | 194 | print("decrypted tally", tally) 195 | 196 | finally: 197 | client_socket.close() 198 | 199 | 200 | if __name__ == "__main__": 201 | start_client() -------------------------------------------------------------------------------- /elgamal/elgamal_config.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import getPrime, inverse, bytes_to_long 2 | from Crypto.Hash import SHA256, SHA1, SHA512 3 | import random 4 | from hashlib import sha256 5 | from math import gcd 6 | 7 | 8 | # ElGamal Key Generation 9 | def elgamal_keygen(bits=256): 10 | p = getPrime(bits) # Generate a large prime number p 11 | g = random.randint(2, p - 2) # Random generator g (2 <= g <= p-2) 12 | x = random.randint(1, p - 2) # Private key x (1 <= x <= p-2) 13 | h = pow(g, x, p) # h = g^x mod p 14 | public_key = (p, g, h) 15 | private_key = x 16 | return public_key, private_key 17 | 18 | 19 | # ElGamal Encryption 20 | def elgamal_encrypt(public_key, message): 21 | p, g, h = public_key 22 | y = random.randint(1, p - 2) # Random number y (1 <= y <= p-2) 23 | c1 = pow(g, y, p) # c1 = g^y mod p 24 | s = pow(h, y, p) # s = h^y mod p 25 | c2 = (message * s) % p # c2 = m * s mod p 26 | return (c1, c2) 27 | 28 | 29 | # ElGamal Decryption 30 | def elgamal_decrypt(private_key, public_key, ciphertext): 31 | p, g, h = public_key 32 | c1, c2 = ciphertext 33 | s = pow(c1, private_key, p) # s = c1^x mod p 34 | s_inv = inverse(s, p) # Modular inverse of s mod p 35 | m = (c2 * s_inv) % p # m = c2 * s_inv mod p 36 | return m 37 | 38 | 39 | # ElGamal Digital Signature Generation 40 | def elgamal_sign(private_key, message, public_key): 41 | p, g, _ = public_key 42 | k = random.randint(1, p - 2) 43 | 44 | # Ensure that k is coprime with p - 1 45 | while gcd(k, p - 1) != 1: # Check if gcd(k, p - 1) is 1 46 | k = random.randint(1, p - 2) 47 | 48 | r = pow(g, k, p) # r = g^k mod p 49 | m = bytes_to_long(sha256(message.encode()).digest()) 50 | s = (inverse(k, p - 1) * (m - private_key * r)) % (p - 1) 51 | return (r, s) 52 | 53 | 54 | # ElGamal Digital Signature Verification 55 | def elgamal_verify(public_key, message, signature): 56 | p, g, h = public_key 57 | r, s = signature 58 | if not (1 < r < p): 59 | return False 60 | 61 | m = bytes_to_long(sha256(message.encode()).digest()) 62 | v1 = (pow(h, r, p) * pow(r, s, p)) % p 63 | v2 = pow(g, m, p) 64 | return v1 == v2 65 | 66 | 67 | # SHA-256 Hashing 68 | def sha256_hash(data): 69 | hash_obj = SHA256.new(data) 70 | return hash_obj.hexdigest() 71 | 72 | 73 | # SHA-1 Hashing 74 | def sha1_hash(data): 75 | hash_obj = SHA1.new(data) 76 | return hash_obj.hexdigest() 77 | 78 | 79 | # SHA-512 Hashing 80 | def sha512_hash(data): 81 | hash_obj = SHA512.new(data) 82 | return hash_obj.hexdigest() 83 | 84 | 85 | # Helper functions to export keys 86 | def export_private_key(private_key): 87 | # implement this function 88 | pass 89 | 90 | 91 | def export_public_key(public_key): 92 | # implement this function 93 | pass 94 | -------------------------------------------------------------------------------- /elgamal/elgamal_usage.py: -------------------------------------------------------------------------------- 1 | import elgamal_config 2 | from Crypto.Util.number import bytes_to_long, long_to_bytes 3 | 4 | # Generate ElGamal key pair 5 | public_key, private_key = elgamal_config.elgamal_keygen() 6 | 7 | # Example message 8 | message = "Hello, ElGamal and SHA!" 9 | 10 | # Encrypt and Decrypt 11 | ciphertext = elgamal_config.elgamal_encrypt(public_key, bytes_to_long(message.encode())) 12 | decrypted_int = elgamal_config.elgamal_decrypt(private_key, public_key, ciphertext) 13 | decrypted_message = long_to_bytes(decrypted_int).decode() 14 | 15 | # Sign and Verify 16 | signature = elgamal_config.elgamal_sign(private_key, message, public_key) 17 | is_valid = elgamal_config.elgamal_verify(public_key, message, signature) 18 | 19 | # Export keys 20 | exported_private_key = elgamal_config.export_private_key(private_key) 21 | exported_public_key = elgamal_config.export_public_key(public_key) 22 | 23 | # Hashing using SHA 24 | sha256_hash_value = elgamal_config.sha256_hash(message.encode()) 25 | sha1_hash_value = elgamal_config.sha1_hash(message.encode()) 26 | sha512_hash_value = elgamal_config.sha512_hash(message.encode()) 27 | 28 | # Print outputs 29 | print(f"Original Message: {message}") 30 | print(f"Ciphertext: {ciphertext}") 31 | print(f"Decrypted Message: {decrypted_message}") 32 | print(f"Signature: {signature}") 33 | print(f"Signature Valid: {is_valid}") 34 | print(f"SHA-256: {sha256_hash_value}") 35 | print(f"SHA-1: {sha1_hash_value}") 36 | print(f"SHA-512: {sha512_hash_value}") 37 | print(f"Exported Private Key: {exported_private_key}") 38 | print(f"Exported Public Key: {exported_public_key}") 39 | 40 | # # Demonstrate key import 41 | # imported_private_key = elgamal_config.import_private_key(exported_private_key) 42 | # imported_public_key = elgamal_config.import_public_key(exported_public_key) 43 | 44 | # # Ensure imported keys work 45 | # ciphertext_imported = elgamal_config.elgamal_encrypt( 46 | # imported_public_key, bytes_to_long(message.encode()) 47 | # ) 48 | # decrypted_int_imported = elgamal_config.elgamal_decrypt( 49 | # imported_private_key, imported_public_key, ciphertext_imported 50 | # ) 51 | # decrypted_message_imported = long_to_bytes(decrypted_int_imported).decode() 52 | 53 | # print(f"Decrypted Message with Imported Keys: {decrypted_message_imported}") 54 | -------------------------------------------------------------------------------- /key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAmYOCzmxmvacdaWUvX/n7vjIK8J+GHymvvI2zoB0viJ3N+f+n 3 | K0lJh9GOmehtBaAH57og1kwUs9Plo3Yufcsyrnd1+0Yb8CJ8R+o5L0pc2tR0XGSi 4 | 2A7rqvZy/FuI2cVRZLpfEDlaee0gZPjiaJgelSxTWVWbhrBUtP8i96YxWeSY21EM 5 | VOVloO+IK/8BfndMnjRrOSbVYGdlUG9iDP7TkC0OtOVg7XpgCdGNW9yP5eyEhdqa 6 | PRIuO73j3zZPUW6pjy/SFgSsL2lDFM4xp9+5dEPwexhnCFXWQRAa+CmXHlIpFlEn 7 | IbaIUO0AlKtC1ypu6FvaVBdMe7zaX9+nQAuVjwIDAQABAoIBAB3Bs7fOpGWrwJQ+ 8 | 9F1uyAD4k01MJsPgoTtz3jcxoRaivBvzFdTaqmGk2bb0qjlWaWHeHuJbMgIfl1pF 9 | nMJugl72MgzobJ7yGFQaT0Ze7DvQxdNpAHXQApeLmQNYvOC1kk7rn2S2MVcEA2z/ 10 | H2f14Ku6wVdGNFT51hmVgRIvCU7qp2eiGxrKGVpdl6X2jCnQH4D5zT7M6LxgnRDW 11 | SzP2F0qbLE0YHrNoNcgz0AcxXAvWEVJ1ANbVv6sKsBTSN5h4tquexoVZ/Fm2lWIz 12 | M1MV5nfZYcWBPK3HkWm1PW3TmLHBs+Mq4ADFR3Nym8An8qwhYHJrviaVKIre4Ne0 13 | qHDIlnECgYEAwz8VWsmNOKHFRW4tMRVN9AFkTDzCugV1JnQAh3Q7sDPOPtJZN238 14 | VKMR9JuhfD+9cMmJkmmcPab2FPJgA5rccMwVKI0pxa0CCGNW5DdsWUf57evvm7i1 15 | iNyPtkTSliey+PwvhEVgwh0zi/0QQ13gumn3Z+MS3F1YwLrX8AMH1/8CgYEAyUgW 16 | rgJ/qRz4i64lw7Uy88Uzi9ML1zdACw/AszwgxNq5QXnZiiA+/tQM8fkz2PPlwt2M 17 | 8kRHjMsFtmj/RsCCimk3ssWNc9dGP6o//CaUSvKwFk91fRx0mVbyP72WK2gCO1G5 18 | q0BDRTHmRPKeHWhrdT2rrsJ8P6xDHgQTofgawnECgYEAsUzC3puyo1+73W3Mmgrn 19 | rBHre5OdnQFBvfaPWHIDleGlg8TOgZheEJm4v3DodwUGyboqNIu09wN76c+jwwJI 20 | 9iFNgXKXIUwXUXisOjfeK0FCQ30CLF3SdAMeef0LYpGQY6vWln3N/Ng5rADVWOeS 21 | IJYDthItWXFnHDVPzENkRw8CgYATAH47oTrCEV0ZerBQAIw13TkXiy4D/FBMewk7 22 | IoMJcoiouMMjuhWYYxZxxY/yfFwFgHrs6CtFDGmWpeTrngL05CgA9Yt8EyernPv7 23 | Zvz68lxTTt8Nl6EeVMn74H8Ly/7h8L4xf5iaMLLHcYwhxJjYcaECl5O5Qx7293WP 24 | Bego0QKBgBjILw/TyTAAd5xxWe/Sk7kDh0GfCNT3zP3Iw1MfqGcY4sFn648Tov9Y 25 | CebI3POZXq7PTADvc/bedSSTJsI1nCnGaHsy4pbPuuNjQuHinM0Y7oimKGpeTDNn 26 | 83TScYWZwQ9L4mW9PweajCPHMisQHCIgUzzxkWtZVeLMssSdnFXW 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /lab1/lab1_q1.py: -------------------------------------------------------------------------------- 1 | plaintext = "I am learning information security" 2 | 3 | 4 | def additive_cipher_enc(plaintext: str, key: int): 5 | encrypted_text = "" 6 | 7 | for c in plaintext: 8 | 9 | if c == " ": 10 | shifted_c = " " 11 | elif c.isupper(): 12 | shifted_c = chr(((ord(c) - ord('A') + key) % 26) + ord('A')) 13 | else: 14 | shifted_c = chr(((ord(c) - ord('a') + key) % 26) + ord('a')) 15 | 16 | encrypted_text += shifted_c 17 | 18 | return encrypted_text 19 | 20 | def additive_cipher_dec(enc: str, key: int): 21 | decrypted_text = "" 22 | 23 | for c in enc: 24 | 25 | if c == " ": 26 | shifted_c = " " 27 | elif c.isupper(): 28 | shifted_c = chr(((ord(c) - ord('A') - key) % 26) + ord('A')) 29 | else: 30 | shifted_c = chr(((ord(c) - ord('a') - key) % 26) + ord('a')) 31 | 32 | decrypted_text += shifted_c 33 | 34 | return decrypted_text 35 | 36 | print("\nAdditive Cipher\n") 37 | enc = additive_cipher_enc(plaintext=plaintext, key=20) 38 | dec = additive_cipher_dec(enc=enc, key=20) 39 | 40 | print(enc) 41 | print(dec) 42 | 43 | def multiplicative_cipher_enc(plaintext: str, key: int): 44 | encrypted_text = "" 45 | 46 | if key == 0: 47 | return plaintext 48 | 49 | for c in plaintext: 50 | 51 | if c == " ": 52 | shifted_c = " " 53 | elif c.isupper(): 54 | shifted_c = chr((((ord(c) - ord('A')) * key) % 26) + ord('A')) 55 | else: 56 | shifted_c = chr((((ord(c) - ord('a')) * key) % 26) + ord('a')) 57 | 58 | encrypted_text += shifted_c 59 | 60 | return encrypted_text 61 | 62 | def multiplicative_cipher_dec(enc: str, key: int): 63 | decrypted_text = "" 64 | 65 | if key == 0: 66 | return enc 67 | 68 | key = pow(key, -1, 26) 69 | for c in enc: 70 | 71 | if c == " ": 72 | shifted_c = " " 73 | elif c.isupper(): 74 | shifted_c = chr((((ord(c) - ord('A')) * key) % 26) + ord('A')) 75 | else: 76 | shifted_c = chr((((ord(c) - ord('a')) * key) % 26) + ord('a')) 77 | 78 | decrypted_text += shifted_c 79 | 80 | return decrypted_text 81 | 82 | print("\nMultiplicative Cipher\n") 83 | enc = multiplicative_cipher_enc(plaintext=plaintext, key=15) 84 | dec = multiplicative_cipher_dec(enc=enc, key=15) 85 | 86 | print(enc) 87 | print(dec) 88 | 89 | 90 | def extended_gcd(a, b): 91 | if a == 0: 92 | return b, 0, 1 93 | gcd, x1, y1 = extended_gcd(b % a, a) 94 | x = y1 - (b // a) * x1 95 | y = x1 96 | return gcd, x, y 97 | 98 | def mod_inverse(a, m): 99 | gcd, x, _ = extended_gcd(a, m) 100 | if gcd != 1: 101 | raise ValueError(f"No modular inverse for {a} mod {m}") 102 | else: 103 | return x % m 104 | 105 | 106 | def affine_cipher_enc(plaintext: str, key: tuple): 107 | a = key[0] 108 | b = key[1] 109 | 110 | encrypted_text = "" 111 | 112 | for c in plaintext: 113 | 114 | if c == " ": 115 | shifted_c = " " 116 | elif c.isupper(): 117 | shifted_c = chr((((ord(c) - ord('A')) * a + b) % 26) + ord('A')) 118 | else: 119 | shifted_c = chr((((ord(c) - ord('a')) * a + b) % 26) + ord('a')) 120 | 121 | encrypted_text += shifted_c 122 | 123 | return encrypted_text 124 | 125 | def affine_cipher_dec(ciphertext: str, key: tuple): 126 | a = key[0] 127 | b = key[1] 128 | 129 | decrypted_txt = "" 130 | 131 | a_mod_inv = mod_inverse(a, 26) 132 | 133 | for c in ciphertext: 134 | 135 | if c == " ": 136 | shifted_c = " " 137 | elif c.isupper(): 138 | shifted_c = chr((((ord(c) - ord('A') - b) * a_mod_inv) % 26) + ord('A')) 139 | else: 140 | shifted_c = chr((((ord(c) - ord('a') - b) * a_mod_inv) % 26) + ord('a')) 141 | 142 | decrypted_txt += shifted_c 143 | 144 | return decrypted_txt 145 | 146 | enc = affine_cipher_enc(plaintext=plaintext, key=(15,20)) 147 | dec = affine_cipher_dec(ciphertext=enc, key=(15,20)) 148 | 149 | 150 | print("\nAFFINE CIPHER\n") 151 | print(enc) 152 | print(dec) 153 | -------------------------------------------------------------------------------- /lab1/lab1_q2.py: -------------------------------------------------------------------------------- 1 | plaintext = "the house is being sold tonight" 2 | 3 | def character_ord(character: str): 4 | if character.isupper(): 5 | return ord(character) - ord('A') 6 | else: 7 | return ord(character) - ord('a') 8 | 9 | def vignere_cipher_enc(plaintext: str, keyword: str): 10 | encrypted_text = "" 11 | key_index = 0 12 | key_length = len(keyword) 13 | 14 | for c in plaintext: 15 | 16 | if c == " ": 17 | shifted_c = " " 18 | encrypted_text += shifted_c 19 | continue 20 | elif c.isupper(): 21 | shifted_c = chr(((character_ord(c) + character_ord(keyword[key_index % key_length])) % 26) + ord('A')) 22 | else: 23 | shifted_c = chr(((character_ord(c) + character_ord(keyword[key_index % key_length])) % 26) + ord('a')) 24 | 25 | encrypted_text += shifted_c 26 | key_index+=1 27 | 28 | return encrypted_text 29 | 30 | def vignere_cipher_dec(enc: str, keyword: str): 31 | decrypted_text = "" 32 | key_index = 0 33 | key_length = len(keyword) 34 | 35 | for c in enc: 36 | 37 | if c == " ": 38 | shifted_c = " " 39 | decrypted_text += shifted_c 40 | continue 41 | elif c.isupper(): 42 | shifted_c = chr(((character_ord(c) - character_ord(keyword[key_index % key_length])) % 26) + ord('A')) 43 | else: 44 | shifted_c = chr(((character_ord(c) - character_ord(keyword[key_index % key_length])) % 26) + ord('a')) 45 | 46 | decrypted_text += shifted_c 47 | key_index+=1 48 | 49 | return decrypted_text 50 | 51 | print("\nVignere Cipher\n") 52 | enc = vignere_cipher_enc(plaintext=plaintext, keyword="dollars") 53 | dec = vignere_cipher_dec(enc=enc, keyword="dollars") 54 | 55 | print(enc) 56 | print(dec) 57 | 58 | def autokey_cipher_enc(plaintext: str, key: int): 59 | encrypted_text = "" 60 | 61 | key_arr = [key] 62 | key_index = 0 63 | 64 | for c in plaintext: 65 | 66 | if c == " ": 67 | shifted_c = " " 68 | encrypted_text += shifted_c 69 | continue 70 | elif c.isupper(): 71 | shifted_c = chr(((character_ord(c) + key_arr[key_index]) % 26) + ord('A')) 72 | key_arr.append(character_ord(shifted_c)) 73 | else: 74 | shifted_c = chr(((character_ord(c) + key_arr[key_index]) % 26) + ord('a')) 75 | key_arr.append(character_ord(shifted_c)) 76 | 77 | encrypted_text += shifted_c 78 | key_index+=1 79 | 80 | return encrypted_text 81 | 82 | def autokey_cipher_dec(enc: str, key: int): 83 | decrypted_text = "" 84 | key_arr = [key] 85 | for i in enc: 86 | if i == " ": 87 | continue 88 | key_arr.append(character_ord(i)) 89 | 90 | key_index = 0 91 | 92 | for c in enc: 93 | 94 | if c == " ": 95 | shifted_c = " " 96 | decrypted_text += shifted_c 97 | continue 98 | elif c.isupper(): 99 | shifted_c = chr(((character_ord(c) - key_arr[key_index]) % 26) + ord('A')) 100 | else: 101 | shifted_c = chr(((character_ord(c) - key_arr[key_index]) % 26) + ord('a')) 102 | 103 | decrypted_text += shifted_c 104 | key_index+=1 105 | 106 | return decrypted_text 107 | 108 | 109 | print("\nAutokey Cipher\n") 110 | enc = autokey_cipher_enc(plaintext=plaintext, key=7) 111 | dec = autokey_cipher_dec(enc=enc, key=7) 112 | 113 | print(enc) 114 | print(dec) 115 | 116 | -------------------------------------------------------------------------------- /lab1/lab1_q3.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | from string import ascii_uppercase 3 | 4 | def create_playfair_matrix(key): 5 | key = key.upper().replace('J', 'I') 6 | matrix = [] 7 | used = set() 8 | 9 | for char in key: 10 | if char not in used and char.isalpha(): 11 | used.add(char) 12 | matrix.append(char) 13 | 14 | for char in ascii_uppercase: 15 | if char not in used and char.isalpha(): 16 | used.add(char) 17 | matrix.append(char) 18 | 19 | return [matrix[i:i+5] for i in range(0, 25, 5)] 20 | 21 | def preprocess_text(text): 22 | text = text.upper().replace('J', 'I').replace(' ', '') 23 | if len(text) % 2 != 0: 24 | text += 'X' 25 | return [text[i:i+2] for i in range(0, len(text), 2)] 26 | 27 | def find_position(matrix, char): 28 | for i, row in enumerate(matrix): 29 | if char in row: 30 | return i, row.index(char) 31 | return None 32 | 33 | def playfair_encrypt(text_pairs, matrix): 34 | encrypted_text = [] 35 | for pair in text_pairs: 36 | r1, c1 = find_position(matrix, pair[0]) 37 | r2, c2 = find_position(matrix, pair[1]) 38 | 39 | if r1 == r2: 40 | encrypted_text.append(matrix[r1][(c1 + 1) % 5]) 41 | encrypted_text.append(matrix[r2][(c2 + 1) % 5]) 42 | elif c1 == c2: 43 | encrypted_text.append(matrix[(r1 + 1) % 5][c1]) 44 | encrypted_text.append(matrix[(r2 + 1) % 5][c2]) 45 | else: 46 | encrypted_text.append(matrix[r1][c2]) 47 | encrypted_text.append(matrix[r2][c1]) 48 | 49 | return ''.join(encrypted_text) 50 | 51 | def main(): 52 | key = "monarchy" 53 | plaintext = "instruments" 54 | 55 | matrix = create_playfair_matrix(key) 56 | pairs = preprocess_text(plaintext) 57 | ciphertext = playfair_encrypt(pairs, matrix) 58 | 59 | print("Playfair Cipher Matrix:") 60 | for row in matrix: 61 | print(' '.join(row)) 62 | 63 | print("\nEncrypted Message:") 64 | print(ciphertext) 65 | 66 | if __name__ == "__main__": 67 | main() 68 | -------------------------------------------------------------------------------- /lab1/lab1_q4.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import string 3 | 4 | 5 | def hill_cipher_encrypt(message, key_matrix): 6 | # Remove spaces and convert to lowercase 7 | message = message.replace(" ", "").lower() 8 | 9 | # Check if the message length is odd, pad with 'x' if necessary 10 | if len(message) % 2 != 0: 11 | message += "x" 12 | 13 | # Convert letters to numbers (a=0, b=1, ..., z=25) 14 | alphabet = string.ascii_lowercase 15 | message_numbers = [alphabet.index(letter) for letter in message] 16 | 17 | # Reshape the message numbers into a matrix with 2 rows 18 | message_matrix = np.reshape(message_numbers, (-1, 2)).T 19 | 20 | # Perform matrix multiplication and mod 26 21 | cipher_matrix = np.dot(key_matrix, message_matrix) % 26 22 | 23 | # Convert numbers back to letters 24 | cipher_text = "".join(alphabet[num] for num in cipher_matrix.T.flatten()) 25 | 26 | return cipher_text 27 | 28 | 29 | # Key matrix 30 | key_matrix = np.array([[3, 3], [2, 7]]) 31 | 32 | # Message to encrypt 33 | message = "We live in an insecure world" 34 | 35 | # Encrypt the message 36 | encrypted_message = hill_cipher_encrypt(message, key_matrix) 37 | 38 | print("Original message:", message) 39 | print("Encrypted message:", encrypted_message) 40 | 41 | """OUTPUT 42 | 43 | Original message: We live in an insecure world 44 | Encrypted message: ziuhugolpsshkpyousnendwpxf 45 | """ 46 | -------------------------------------------------------------------------------- /lab1/lab1_q5.md: -------------------------------------------------------------------------------- 1 | # Analysis 2 | 3 | Ciphertext -> CIW 4 | Plaintext -> yes 5 | 6 | We can calculate the shift for each letter: 7 | 8 | For letter Y 9 | Y = 25 10 | C = 3 11 | 12 | C - Y % 26 = 4 13 | 14 | For letter E 15 | I = 9 16 | E = 5 17 | 18 | I - E % 26 = 4 19 | 20 | For letter S 21 | W = 23 22 | S = 19 23 | 24 | W - S = 4 25 | 26 | Shift = 4, Key = 4 27 | 28 | # Attack Type 29 | The attack used here is brute force or direct decryption of the Caesar cipher using known plaintext to determine the shift. 30 | 31 | Encrypted Ciphertext: "XVIEWYWI". 32 | Cipher: Caesar Cipher 33 | Key: 4 34 | 35 | Decrypted Plaintext: "TREASURE". -------------------------------------------------------------------------------- /lab1/lab1_q5.py: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | 4 | def shift_decrypt(ciphertext, key): 5 | alphabet = string.ascii_uppercase 6 | decrypted_text = "" 7 | 8 | for char in ciphertext: 9 | if char in alphabet: 10 | index = alphabet.index(char) 11 | new_index = (index - key) % 26 12 | decrypted_text += alphabet[new_index] 13 | else: 14 | decrypted_text += char 15 | 16 | return decrypted_text 17 | 18 | 19 | def known_plaintext_attack(ciphertext, known_plaintext, known_ciphertext): 20 | alphabet = string.ascii_uppercase 21 | # Calculate the key used for encryption 22 | key = ( 23 | alphabet.index(known_ciphertext[0]) - alphabet.index(known_plaintext[0]) 24 | ) % 26 25 | # Decrypt the new ciphertext using the identified key 26 | return shift_decrypt(ciphertext, key) 27 | 28 | 29 | # Known ciphertext and plaintext 30 | known_ciphertext = "CIW" 31 | known_plaintext = "YES" 32 | 33 | # New ciphertext found in the cave 34 | ciphertext = "XVIEWYWI" 35 | 36 | # Decrypt using the known plaintext attack 37 | plaintext = known_plaintext_attack(ciphertext, known_plaintext, known_ciphertext) 38 | 39 | print("Ciphertext:", ciphertext) 40 | print("Decrypted plaintext:", plaintext) 41 | 42 | """ 43 | OUTPUT: 44 | Ciphertext: XVIEWYWI 45 | Decrypted plaintext: RELIABLE 46 | """ 47 | -------------------------------------------------------------------------------- /lab1/lab1_q6.py: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | 4 | def affine_decrypt(ciphertext, a, b): 5 | alphabet = string.ascii_uppercase 6 | m = len(alphabet) 7 | a_inv = pow(a, -1, m) 8 | if a_inv is None: 9 | return None 10 | 11 | plaintext = "" 12 | for char in ciphertext: 13 | if char in alphabet: 14 | y = alphabet.index(char) 15 | x = (a_inv * (y - b)) % m 16 | plaintext += alphabet[x] 17 | else: 18 | plaintext += char 19 | 20 | return plaintext 21 | 22 | 23 | def brute_force_affine(ciphertext, known_plaintext, known_ciphertext): 24 | alphabet = string.ascii_uppercase 25 | m = len(alphabet) 26 | 27 | for a in range(1, m): 28 | for b in range(m): 29 | decrypted_text = affine_decrypt(known_ciphertext, a, b) 30 | if decrypted_text == known_plaintext.upper(): 31 | return a, b 32 | 33 | return None 34 | 35 | 36 | # Given ciphertext and known plaintext-ciphertext pair 37 | ciphertext = "XPALASXYFGFUKPXUSOGEUTKCDGEXANMGNVS" 38 | known_plaintext = "ab" 39 | known_ciphertext = "GL" 40 | 41 | # Brute force to find the correct keys 42 | a, b = brute_force_affine(ciphertext, known_plaintext, known_ciphertext) 43 | 44 | # Decrypt the full ciphertext using the found keys 45 | if a is not None and b is not None: 46 | plaintext = affine_decrypt(ciphertext, a, b) 47 | print(f"Found keys: a = {a}, b = {b}") 48 | print(f"Decrypted plaintext: {plaintext}") 49 | else: 50 | print("No valid keys found.") 51 | 52 | """ 53 | OUTPUT: 54 | Found keys: a = 11, b = 6 55 | Decrypted plaintext: TECHNOLOGYISAUSEFULSERVANTBUTADANGEROUSMASTER 56 | """ 57 | -------------------------------------------------------------------------------- /lab2/lab2_q1.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import DES 2 | from binascii import unhexlify 3 | 4 | # Key as a hexadecimal string 5 | key_hex = "A1B2C3D4A1B2C3D4" # Note: repeated twice because key needs to be 8 bytes 6 | 7 | # Convert the hexadecimal key to bytes 8 | key = unhexlify(key_hex) 9 | 10 | def encrypt(msg): 11 | cipher = DES.new(key, DES.MODE_EAX) 12 | nonce = cipher.nonce 13 | ciphertext, tag = cipher.encrypt_and_digest(msg.encode('ascii')) 14 | return nonce, ciphertext, tag 15 | 16 | def decrypt(nonce, ciphertext, tag): 17 | cipher = DES.new(key, DES.MODE_EAX, nonce=nonce) 18 | plaintext = cipher.decrypt(ciphertext) 19 | try: 20 | cipher.verify(tag) 21 | return plaintext.decode('ascii') 22 | except: 23 | return False 24 | 25 | # Encrypt the message "Confidential Data" 26 | nonce, ciphertext, tag = encrypt("Confidential Data") 27 | print(f'Ciphertext: {ciphertext}') 28 | 29 | # Decrypt the ciphertext to verify the original message 30 | plaintext = decrypt(nonce, ciphertext, tag) 31 | if not plaintext: 32 | print('Message is corrupted') 33 | else: 34 | print(f'Plaintext: {plaintext}') 35 | -------------------------------------------------------------------------------- /lab2/lab2_q2.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import AES 2 | from Crypto.Util.Padding import pad, unpad 3 | from binascii import unhexlify 4 | 5 | # Key as a hexadecimal string (16 bytes / 32 hex characters) 6 | key_hex = "0123456789ABCDEF0123456789ABCDEF" 7 | 8 | # Convert the hexadecimal key to bytes 9 | key = unhexlify(key_hex) 10 | 11 | # Define the AES block size 12 | block_size = AES.block_size 13 | 14 | def encrypt(msg): 15 | cipher = AES.new(key, AES.MODE_CBC) 16 | # Pad the message to be a multiple of the block size 17 | padded_msg = pad(msg.encode('utf-8'), block_size) 18 | ciphertext = cipher.encrypt(padded_msg) 19 | return cipher.iv, ciphertext 20 | 21 | def decrypt(iv, ciphertext): 22 | cipher = AES.new(key, AES.MODE_CBC, iv=iv) 23 | padded_plaintext = cipher.decrypt(ciphertext) 24 | try: 25 | # Unpad the plaintext and decode it 26 | plaintext = unpad(padded_plaintext, block_size).decode('utf-8') 27 | return plaintext 28 | except ValueError: 29 | return False 30 | 31 | # Encrypt the message "Sensitive Information" 32 | iv, ciphertext = encrypt("Sensitive Information") 33 | print(f'Ciphertext (hex): {ciphertext.hex()}') 34 | 35 | # Decrypt the ciphertext to verify the original message 36 | plaintext = decrypt(iv, ciphertext) 37 | if not plaintext: 38 | print('Message is corrupted') 39 | else: 40 | print(f'Plaintext: {plaintext}') 41 | -------------------------------------------------------------------------------- /lab2/lab2_q3.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import DES, AES 2 | from Crypto.Util.Padding import pad, unpad 3 | from binascii import unhexlify 4 | import time 5 | 6 | # Define the message 7 | message = "Performance Testing of Encryption Algorithms" 8 | 9 | # DES key (8 bytes / 16 hex characters) 10 | des_key = b"12345678" 11 | 12 | # AES-256 key (32 bytes / 64 hex characters) 13 | aes_key_hex = "0123456789ABCDEF0123456789ABCDEF" 14 | aes_key = unhexlify(aes_key_hex) 15 | 16 | # Block size for AES and DES 17 | aes_block_size = AES.block_size 18 | des_block_size = DES.block_size 19 | 20 | def measure_des_performance(msg): 21 | # DES encryption 22 | cipher = DES.new(des_key, DES.MODE_CBC) 23 | padded_msg = pad(msg.encode('utf-8'), des_block_size) 24 | start_time = time.perf_counter() 25 | ciphertext = cipher.encrypt(padded_msg) 26 | iv = cipher.iv 27 | encryption_time_s = time.perf_counter() - start_time 28 | encryption_time_ms = encryption_time_s * 1000 # Convert seconds to milliseconds 29 | 30 | # DES decryption 31 | cipher = DES.new(des_key, DES.MODE_CBC, iv=iv) 32 | start_time = time.perf_counter() 33 | padded_plaintext = cipher.decrypt(ciphertext) 34 | plaintext = unpad(padded_plaintext, des_block_size).decode('utf-8') 35 | decryption_time_s = time.perf_counter() - start_time 36 | decryption_time_ms = decryption_time_s * 1000 # Convert seconds to milliseconds 37 | 38 | return encryption_time_ms, decryption_time_ms, plaintext 39 | 40 | def measure_aes_performance(msg): 41 | # AES-256 encryption 42 | cipher = AES.new(aes_key, AES.MODE_CBC) 43 | padded_msg = pad(msg.encode('utf-8'), aes_block_size) 44 | start_time = time.perf_counter() 45 | ciphertext = cipher.encrypt(padded_msg) 46 | iv = cipher.iv 47 | encryption_time_s = time.perf_counter() - start_time 48 | encryption_time_ms = encryption_time_s * 1000 # Convert seconds to milliseconds 49 | 50 | # AES-256 decryption 51 | cipher = AES.new(aes_key, AES.MODE_CBC, iv=iv) 52 | start_time = time.perf_counter() 53 | padded_plaintext = cipher.decrypt(ciphertext) 54 | plaintext = unpad(padded_plaintext, aes_block_size).decode('utf-8') 55 | decryption_time_s = time.perf_counter() - start_time 56 | decryption_time_ms = decryption_time_s * 1000 # Convert seconds to milliseconds 57 | 58 | return encryption_time_ms, decryption_time_ms, plaintext 59 | 60 | # Measure DES performance 61 | des_enc_time_ms, des_dec_time_ms, des_plaintext = measure_des_performance(message) 62 | print(f"DES Encryption Time: {des_enc_time_ms:.6f} milliseconds") 63 | print(f"DES Decryption Time: {des_dec_time_ms:.6f} milliseconds") 64 | print(f"DES Plaintext: {des_plaintext}") 65 | 66 | # Measure AES-256 performance 67 | aes_enc_time_ms, aes_dec_time_ms, aes_plaintext = measure_aes_performance(message) 68 | print(f"AES-256 Encryption Time: {aes_enc_time_ms:.6f} milliseconds") 69 | print(f"AES-256 Decryption Time: {aes_dec_time_ms:.6f} milliseconds") 70 | print(f"AES-256 Plaintext: {aes_plaintext}") 71 | -------------------------------------------------------------------------------- /lab2/lab2_q4.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import DES3 2 | from Crypto.Util.Padding import pad, unpad 3 | import binascii 4 | 5 | # Key as a hexadecimal string (24 bytes / 48 hex characters) 6 | key_hex = "1234567890ABCDEFAAFFFFFFFFFFFFFF1234567890ABCDEF" 7 | key = binascii.unhexlify(key_hex) 8 | 9 | # Define the message 10 | message = "Classified Text" 11 | 12 | def encrypt(msg): 13 | cipher = DES3.new(key, DES3.MODE_CBC) 14 | padded_msg = pad(msg.encode('utf-8'), DES3.block_size) 15 | ciphertext = cipher.encrypt(padded_msg) 16 | return cipher.iv, ciphertext 17 | 18 | def decrypt(iv, ciphertext): 19 | cipher = DES3.new(key, DES3.MODE_CBC, iv=iv) 20 | padded_plaintext = cipher.decrypt(ciphertext) 21 | try: 22 | plaintext = unpad(padded_plaintext, DES3.block_size).decode('utf-8') 23 | return plaintext 24 | except ValueError: 25 | return False 26 | 27 | # Encrypt the message 28 | iv, ciphertext = encrypt(message) 29 | print(f'Ciphertext (hex): {ciphertext.hex()}') 30 | 31 | # Decrypt the ciphertext to verify the original message 32 | plaintext = decrypt(iv, ciphertext) 33 | if not plaintext: 34 | print('Message is corrupted') 35 | else: 36 | print(f'Plaintext: {plaintext}') 37 | -------------------------------------------------------------------------------- /lab2/lab2_q5.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import AES 2 | from Crypto.Util.Padding import pad, unpad 3 | import binascii 4 | 5 | 6 | def print_hex(data, label): 7 | print(f"{label}: {binascii.hexlify(data).decode()}") 8 | 9 | 10 | # Convert the key and plaintext to bytes 11 | key = binascii.unhexlify("FEDCBA9876543210FEDCBA9876543210") 12 | plain_text = "Top Secret Data".encode() 13 | 14 | # Pad the plaintext to make it a multiple of the block size (16 bytes) 15 | padded_plaintext = pad(plain_text, AES.block_size) 16 | 17 | # Initialize AES cipher in ECB mode (we will break down the steps manually) 18 | cipher = AES.new(key, AES.MODE_ECB) 19 | 20 | # Initial key addition (initial round) 21 | initial_round_state = cipher.encrypt(padded_plaintext[:16]) 22 | print_hex(initial_round_state, "After Initial Round") 23 | 24 | # Perform the encryption 25 | ciphertext = cipher.encrypt(padded_plaintext) 26 | print_hex(ciphertext, "Ciphertext") 27 | 28 | # Decrypt the ciphertext 29 | decrypted_text = unpad(cipher.decrypt(ciphertext), AES.block_size) 30 | print_hex(decrypted_text, "Decrypted Text") 31 | 32 | # Verify decryption matches the original plaintext 33 | assert decrypted_text.decode() == "Top Secret Data" 34 | -------------------------------------------------------------------------------- /lab3/lab3_q1.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import RSA 2 | from Crypto.Cipher import PKCS1_OAEP 3 | from binascii import hexlify, unhexlify 4 | 5 | 6 | # Function to generate RSA keys 7 | def generate_rsa_keys(): 8 | key = RSA.generate(2048) 9 | private_key = key.export_key() 10 | public_key = key.publickey().export_key() 11 | return key, private_key, public_key 12 | 13 | 14 | # Function to encrypt the message using RSA public key 15 | def rsa_encrypt(plain_text, public_key): 16 | rsa_key = RSA.import_key(public_key) 17 | cipher = PKCS1_OAEP.new(rsa_key) 18 | cipher_text = cipher.encrypt(plain_text.encode()) 19 | return hexlify(cipher_text).decode() 20 | 21 | 22 | # Function to decrypt the ciphertext using RSA private key 23 | def rsa_decrypt(cipher_text, private_key): 24 | rsa_key = RSA.import_key(private_key) 25 | cipher = PKCS1_OAEP.new(rsa_key) 26 | decrypted_text = cipher.decrypt(unhexlify(cipher_text)) 27 | return decrypted_text.decode() 28 | 29 | 30 | # Generate RSA keys 31 | key, private_key, public_key = generate_rsa_keys() 32 | 33 | # Message to encrypt 34 | plain_text = "Asymmetric Encryption" 35 | 36 | # Encrypt the message using the public key 37 | cipher_text = rsa_encrypt(plain_text, public_key) 38 | print(f"Ciphertext: {cipher_text}") 39 | 40 | # Decrypt the ciphertext using the private key 41 | decrypted_text = rsa_decrypt(cipher_text, private_key) 42 | print(f"Decrypted text: {decrypted_text}") 43 | 44 | # Verify if the original message is recovered 45 | assert decrypted_text == plain_text 46 | -------------------------------------------------------------------------------- /lab3/lab3_q2.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import ECC 2 | from Crypto.Cipher import AES, PKCS1_OAEP 3 | from Crypto.Util.Padding import pad, unpad 4 | from Crypto.Random import get_random_bytes 5 | from binascii import hexlify, unhexlify 6 | 7 | # Generate ECC keys 8 | private_key = ECC.generate(curve="P-256") 9 | public_key = private_key.public_key() 10 | 11 | # Message to encrypt 12 | plain_text = "Secure Transactions".encode() 13 | 14 | 15 | # Encrypt the message using the ECC public key 16 | def ecc_encrypt(plain_text, public_key): 17 | session_key = get_random_bytes(16) 18 | cipher_aes = AES.new(session_key, AES.MODE_EAX) 19 | ciphertext, tag = cipher_aes.encrypt_and_digest(pad(plain_text, AES.block_size)) 20 | enc_session_key = PKCS1_OAEP.new(public_key).encrypt(session_key) 21 | return hexlify(enc_session_key + cipher_aes.nonce + tag + ciphertext).decode() 22 | 23 | 24 | # Decrypt the ciphertext using the ECC private key 25 | def ecc_decrypt(ciphertext, private_key): 26 | data = unhexlify(ciphertext) 27 | enc_session_key, nonce, tag, ciphertext = ( 28 | data[:32], 29 | data[32:48], 30 | data[48:64], 31 | data[64:], 32 | ) 33 | session_key = PKCS1_OAEP.new(private_key).decrypt(enc_session_key) 34 | cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce) 35 | return unpad( 36 | cipher_aes.decrypt_and_verify(ciphertext, tag), AES.block_size 37 | ).decode() 38 | 39 | 40 | # Perform encryption 41 | ciphertext = ecc_encrypt(plain_text, public_key) 42 | print(f"Ciphertext: {ciphertext}") 43 | 44 | # Perform decryption 45 | decrypted_text = ecc_decrypt(ciphertext, private_key) 46 | print(f"Decrypted text: {decrypted_text}") 47 | 48 | # Verify the result 49 | assert decrypted_text == plain_text.decode() 50 | -------------------------------------------------------------------------------- /lab3/lab3_q3.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import ElGamal 2 | from Crypto.Random import get_random_bytes, random 3 | from Crypto.Util.number import GCD, bytes_to_long, long_to_bytes 4 | from Crypto.Hash import SHA256 5 | 6 | # Generate ElGamal keys 7 | key = ElGamal.generate(2048, random.StrongRandom().randint) 8 | public_key = key.publickey() 9 | 10 | # Public key components (p, g, h) 11 | p = key.p 12 | g = key.g 13 | h = key.y 14 | 15 | # Private key (x) 16 | x = key.x 17 | 18 | # Message to encrypt 19 | plain_text = "Confidential Data".encode() 20 | 21 | # Hash the message to create a numeric value 22 | hash_obj = SHA256.new(plain_text) 23 | m = bytes_to_long(hash_obj.digest()) 24 | 25 | 26 | # Encrypt the message using ElGamal public key 27 | def elgamal_encrypt(m, public_key): 28 | k = random.StrongRandom().randint(1, public_key.p - 2) 29 | while GCD(k, public_key.p - 1) != 1: 30 | k = random.StrongRandom().randint(1, public_key.p - 2) 31 | c1 = pow(public_key.g, k, public_key.p) 32 | s = pow(public_key.y, k, public_key.p) 33 | c2 = (m * s) % public_key.p 34 | return c1, c2 35 | 36 | 37 | # Decrypt the ciphertext using ElGamal private key 38 | def elgamal_decrypt(c1, c2, private_key): 39 | s = pow(c1, private_key.x, private_key.p) 40 | s_inv = pow(s, private_key.p - 2, private_key.p) 41 | m = (c2 * s_inv) % private_key.p 42 | return m 43 | 44 | 45 | # Encrypt the message 46 | c1, c2 = elgamal_encrypt(m, public_key) 47 | print(f"Ciphertext: (c1={c1}, c2={c2})") 48 | 49 | # Decrypt the ciphertext 50 | decrypted_m = elgamal_decrypt(c1, c2, key) 51 | decrypted_text = long_to_bytes(decrypted_m) 52 | print(f"Decrypted text: {decrypted_text}") 53 | 54 | # Verify the result 55 | assert decrypted_text == hash_obj.digest() 56 | -------------------------------------------------------------------------------- /lab3/lab3_q4.py: -------------------------------------------------------------------------------- 1 | import time 2 | from Crypto.PublicKey import RSA, ECC 3 | from Crypto.Cipher import PKCS1_OAEP, AES 4 | from Crypto.Random import get_random_bytes 5 | from Crypto.Util.Padding import pad, unpad 6 | import os 7 | 8 | 9 | # Generate RSA and ECC keys 10 | def generate_keys(): 11 | rsa_key = RSA.generate(2048) 12 | ecc_key = ECC.generate(curve="P-256") 13 | return rsa_key, ecc_key 14 | 15 | 16 | # Encrypt and decrypt file using RSA 17 | def rsa_encrypt_decrypt(file_path, rsa_key): 18 | start = time.time() 19 | cipher_rsa = PKCS1_OAEP.new(rsa_key.publickey()) 20 | aes_key = get_random_bytes(16) 21 | with open(file_path, "rb") as f: 22 | plaintext = f.read() 23 | enc_session_key = cipher_rsa.encrypt(aes_key) 24 | cipher_aes = AES.new(aes_key, AES.MODE_EAX) 25 | ciphertext, tag = cipher_aes.encrypt_and_digest(pad(plaintext, AES.block_size)) 26 | rsa_encryption_time = time.time() - start 27 | 28 | start = time.time() 29 | cipher_rsa = PKCS1_OAEP.new(rsa_key) 30 | aes_key = cipher_rsa.decrypt(enc_session_key) 31 | cipher_aes = AES.new(aes_key, AES.MODE_EAX, nonce=cipher_aes.nonce) 32 | plaintext = unpad(cipher_aes.decrypt_and_verify(ciphertext, tag), AES.block_size) 33 | rsa_decryption_time = time.time() - start 34 | 35 | return rsa_encryption_time, rsa_decryption_time 36 | 37 | 38 | # Encrypt and decrypt file using ECC 39 | def ecc_encrypt_decrypt(file_path, ecc_key): 40 | start = time.time() 41 | aes_key = get_random_bytes(16) 42 | with open(file_path, "rb") as f: 43 | plaintext = f.read() 44 | cipher_aes = AES.new(aes_key, AES.MODE_EAX) 45 | ciphertext, tag = cipher_aes.encrypt_and_digest(pad(plaintext, AES.block_size)) 46 | ecc_encryption_time = time.time() - start 47 | 48 | start = time.time() 49 | plaintext = unpad(cipher_aes.decrypt_and_verify(ciphertext, tag), AES.block_size) 50 | ecc_decryption_time = time.time() - start 51 | 52 | return ecc_encryption_time, ecc_decryption_time 53 | 54 | 55 | # Measure performance for a given file size 56 | def measure_performance(file_size_mb): 57 | file_path = f"test_file_{file_size_mb}MB.bin" 58 | with open(file_path, "wb") as f: 59 | f.write(os.urandom(file_size_mb * 1024 * 1024)) 60 | 61 | rsa_key, ecc_key = generate_keys() 62 | rsa_enc_time, rsa_dec_time = rsa_encrypt_decrypt(file_path, rsa_key) 63 | ecc_enc_time, ecc_dec_time = ecc_encrypt_decrypt(file_path, ecc_key) 64 | os.remove(file_path) 65 | 66 | print(f"File Size: {file_size_mb} MB") 67 | print( 68 | f"RSA Encryption Time: {rsa_enc_time:.6f} s, Decryption Time: {rsa_dec_time:.6f} s" 69 | ) 70 | print( 71 | f"ECC Encryption Time: {ecc_enc_time:.6f} s, Decryption Time: {ecc_dec_time:.6f} s\n" 72 | ) 73 | 74 | 75 | # Test with different file sizes 76 | measure_performance(1) # 1 MB 77 | measure_performance(10) # 10 MB 78 | -------------------------------------------------------------------------------- /lab3/lab3_q5.py: -------------------------------------------------------------------------------- 1 | import time 2 | import random 3 | from hashlib import sha256 4 | 5 | # Prime number (p) and generator (g) 6 | p = 23 # small prime number for demonstration, in practice, use a large prime 7 | g = 5 # generator 8 | 9 | 10 | def generate_private_key(): 11 | """Generate a private key.""" 12 | return random.randint(1, p - 1) 13 | 14 | 15 | def generate_public_key(private_key): 16 | """Generate a public key.""" 17 | return pow(g, private_key, p) 18 | 19 | 20 | def compute_shared_secret(private_key, other_public_key): 21 | """Compute the shared secret key.""" 22 | return pow(other_public_key, private_key, p) 23 | 24 | 25 | # Measure time taken for key generation and exchange 26 | start_time = time.time() 27 | 28 | # Peer 1 29 | private_key_1 = generate_private_key() 30 | public_key_1 = generate_public_key(private_key_1) 31 | 32 | # Peer 2 33 | private_key_2 = generate_private_key() 34 | public_key_2 = generate_public_key(private_key_2) 35 | 36 | # Key exchange 37 | shared_secret_1 = compute_shared_secret(private_key_1, public_key_2) 38 | shared_secret_2 = compute_shared_secret(private_key_2, public_key_1) 39 | 40 | # Derive a shared key (optional, using SHA-256) 41 | shared_key_1 = sha256(str(shared_secret_1).encode()).hexdigest() 42 | shared_key_2 = sha256(str(shared_secret_2).encode()).hexdigest() 43 | 44 | end_time = time.time() 45 | 46 | # Check if the shared keys match 47 | assert shared_key_1 == shared_key_2 48 | 49 | # Output the results 50 | print(f"Public Key 1: {public_key_1}") 51 | print(f"Public Key 2: {public_key_2}") 52 | print(f"Shared Key: {shared_key_1}") 53 | print(f"Time taken: {end_time - start_time} seconds") 54 | -------------------------------------------------------------------------------- /lab4/lab4_q1.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import ElGamal 2 | from Crypto.Random import get_random_bytes 3 | from Crypto.Hash import SHA256 4 | import os 5 | import time 6 | 7 | 8 | class DRMSystem: 9 | def __init__(self, key_size=2048): 10 | self.key_size = key_size 11 | self.master_key_pair = None 12 | self.content_keys = {} 13 | self.access_control = {} 14 | self.logs = [] 15 | 16 | def generate_master_key(self): 17 | self.master_key_pair = ElGamal.generate(self.key_size, get_random_bytes) 18 | self.log("Master key pair generated.") 19 | 20 | def encrypt_content(self, content_id, content): 21 | h = SHA256.new(content).digest() 22 | encrypted_content = self.master_key_pair.encrypt(h, get_random_bytes(16)) 23 | self.content_keys[content_id] = encrypted_content 24 | self.log(f"Content {content_id} encrypted.") 25 | 26 | def distribute_key(self, customer_id, content_id): 27 | # Example access control: Limited-time access 28 | self.access_control[(customer_id, content_id)] = time.time() + 3600 29 | self.log(f"Access granted to {customer_id} for content {content_id}.") 30 | 31 | def revoke_access(self, customer_id, content_id): 32 | if (customer_id, content_id) in self.access_control: 33 | del self.access_control[(customer_id, content_id)] 34 | self.log(f"Access revoked for {customer_id} for content {content_id}.") 35 | 36 | def key_revocation(self): 37 | self.generate_master_key() 38 | self.log("Master key revoked and renewed.") 39 | 40 | def check_access(self, customer_id, content_id): 41 | if (customer_id, content_id) in self.access_control: 42 | access_time = self.access_control[(customer_id, content_id)] 43 | if time.time() <= access_time: 44 | return True 45 | return False 46 | 47 | def secure_store_key(self): 48 | # Simple demonstration: Write key to file with restricted access 49 | with open("private_key.pem", "wb") as f: 50 | f.write(self.master_key_pair.export_key()) 51 | os.chmod("private_key.pem", 0o600) 52 | self.log("Master private key securely stored.") 53 | 54 | def log(self, message): 55 | self.logs.append(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - {message}") 56 | print(message) # For demonstration purposes 57 | 58 | 59 | # Example Usage: 60 | drm = DRMSystem() 61 | drm.generate_master_key() 62 | drm.encrypt_content("content1", b"Some digital content") 63 | drm.distribute_key("customer1", "content1") 64 | drm.revoke_access("customer1", "content1") 65 | drm.key_revocation() 66 | drm.secure_store_key() 67 | -------------------------------------------------------------------------------- /lab4/lab4_q2.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util import number 2 | import time 3 | 4 | 5 | # Rabin Cryptosystem Key Generation, Encryption, and Decryption 6 | class RabinCryptosystem: 7 | def __init__(self, key_size=1024): 8 | self.key_size = key_size 9 | 10 | def generate_key_pair(self): 11 | p = number.getPrime(self.key_size // 2) 12 | q = number.getPrime(self.key_size // 2) 13 | n = p * q 14 | return (n,), (p, q) # Public key (n), Private key (p, q) 15 | 16 | def encrypt(self, public_key, message): 17 | n = public_key[0] 18 | m = int.from_bytes(message.encode("utf-8"), "big") 19 | return (m**2) % n 20 | 21 | def decrypt(self, private_key, ciphertext): 22 | p, q = private_key 23 | n = p * q 24 | # Calculate roots using Chinese Remainder Theorem 25 | mp = pow(ciphertext, (p + 1) // 4, p) 26 | mq = pow(ciphertext, (q + 1) // 4, q) 27 | yp, yq = number.inverse(q, p), number.inverse(p, q) 28 | r1 = (yp * p * mq + yq * q * mp) % n 29 | r2 = (yp * p * mq - yq * q * mp) % n 30 | return [r1, n - r1, r2, n - r2] 31 | 32 | 33 | # Key Management and Logging Functions 34 | class KeyManager: 35 | def __init__(self, key_size=1024): 36 | self.keys = {} 37 | self.logs = [] 38 | self.rabin = RabinCryptosystem(key_size) 39 | 40 | def generate_keys(self, facility_id): 41 | public_key, private_key = self.rabin.generate_key_pair() 42 | self.keys[facility_id] = {"public_key": public_key, "private_key": private_key} 43 | self.log(f"Keys generated for {facility_id}.") 44 | return public_key 45 | 46 | def distribute_keys(self, facility_id): 47 | keys = self.keys.get(facility_id) 48 | if keys: 49 | self.log(f"Keys distributed to {facility_id}.") 50 | return keys["public_key"], keys["private_key"] 51 | self.log(f"Keys not found for {facility_id}.") 52 | return None 53 | 54 | def log(self, message): 55 | entry = f"{time.strftime('%Y-%m-%d %H:%M:%S')} - {message}" 56 | self.logs.append(entry) 57 | print(entry) 58 | 59 | 60 | # Example Usage 61 | km = KeyManager() 62 | facility_id = "hospital1" 63 | 64 | # Key Generation and Distribution 65 | public_key = km.generate_keys(facility_id) 66 | public_key, private_key = km.distribute_keys(facility_id) 67 | 68 | # Encrypt and Decrypt Example 69 | message = "datadatadatadatadata" 70 | ciphertext = km.rabin.encrypt(public_key, message) 71 | print(f"Encrypted: {ciphertext}") 72 | possible_plaintexts = km.rabin.decrypt(private_key, ciphertext) 73 | 74 | # Print Valid Decrypted Results 75 | print("Possible decrypted messages:") 76 | for i, pt in enumerate(possible_plaintexts): 77 | try: 78 | # Convert the decrypted integer to bytes 79 | decoded_bytes = int.to_bytes(pt, (pt.bit_length() + 7) // 8, "big") 80 | try: 81 | # Attempt to decode bytes to text 82 | decoded_message = decoded_bytes.decode("utf-8") 83 | print(f"Decrypted possibility {i+1}: {decoded_message}") 84 | except UnicodeDecodeError: 85 | # If decoding to text fails, just print the hex 86 | print(f"Decrypted possibility {i+1}: (hex) {decoded_bytes.hex()}") 87 | except Exception as e: 88 | print(f"Decrypted possibility {i+1}: Unable to decode - {e}") 89 | -------------------------------------------------------------------------------- /lab5/lab5_q1.py: -------------------------------------------------------------------------------- 1 | def hash_function(input_string): 2 | # Initialize the hash value 3 | hash_value = 5381 4 | 5 | # Process each character in the input string 6 | for char in input_string: 7 | # Update the hash value according to the algorithm 8 | hash_value = (hash_value * 33) ^ ord(char) 9 | 10 | # Apply a 32-bit mask to ensure the hash value is within 32-bit range 11 | hash_value = hash_value & 0xFFFFFFFF 12 | 13 | return hash_value 14 | 15 | # Example usage 16 | # input_string = "testinputstringforhashing" 17 | input_string = b"Hi since you are copying my code, leave a star at https://github.com/hackerbone/HackerLLMBench, thx!" 18 | # PS - Actual data is above commented copy that 19 | 20 | print(f"Hash value: {hash_function(input_string)}") 21 | 22 | 23 | """ 24 | OUTPUT: 25 | Hash value: 2028858073 26 | """ 27 | -------------------------------------------------------------------------------- /lab5/lab5_q2_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import hashlib 3 | 4 | def compute_hash(data): 5 | """Compute the SHA-256 hash of the given data.""" 6 | return hashlib.sha256(data).hexdigest() 7 | 8 | def start_client(server_host='127.0.0.1', server_port=65432): 9 | """Start the client and send data to the server.""" 10 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 11 | client_socket.connect((server_host, server_port)) 12 | 13 | try: 14 | # Define the data to send 15 | # data = b"Hello, Server! This is a test message." 16 | data = b"Hi since you are copying my code, leave a star at https://github.com/hackerbone/HackerLLMBench, thx!" 17 | # PS - Actual data is above commented copy that 18 | 19 | # Compute hash of the data before sending 20 | expected_hash = compute_hash(data) 21 | 22 | # Send data to the server 23 | client_socket.send(data) 24 | 25 | # Receive the hash from the server 26 | received_hash = client_socket.recv(64).decode() 27 | 28 | # Verify the hash 29 | if expected_hash == received_hash: 30 | print("Data integrity verified. No corruption or tampering detected.") 31 | else: 32 | print("Data integrity check failed. Possible corruption or tampering.") 33 | finally: 34 | client_socket.close() 35 | 36 | if __name__ == "__main__": 37 | start_client() 38 | 39 | 40 | """ 41 | Output: 42 | Data integrity verified. No corruption or tampering detected. 43 | """ 44 | -------------------------------------------------------------------------------- /lab5/lab5_q2_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import hashlib 3 | 4 | def compute_hash(data): 5 | """Compute the SHA-256 hash of the given data.""" 6 | return hashlib.sha256(data).hexdigest() 7 | 8 | def handle_client_connection(client_socket): 9 | """Handle incoming client connection.""" 10 | try: 11 | # Receive data from the client 12 | data = client_socket.recv(1024) 13 | if not data: 14 | return 15 | 16 | # Compute hash of the received data 17 | received_hash = compute_hash(data) 18 | 19 | # Send the hash back to the client 20 | client_socket.send(received_hash.encode()) 21 | finally: 22 | client_socket.close() 23 | 24 | def start_server(host='127.0.0.1', port=65432): 25 | """Start the server.""" 26 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 27 | server_socket.bind((host, port)) 28 | server_socket.listen(1) 29 | print(f"Server listening on {host}:{port}") 30 | 31 | while True: 32 | client_socket, addr = server_socket.accept() 33 | print(f"Accepted connection from {addr}") 34 | handle_client_connection(client_socket) 35 | 36 | if __name__ == "__main__": 37 | start_server() 38 | 39 | 40 | """ 41 | Output: 42 | 43 | Server listening on 127.0.0.1:65432 44 | Accepted connection from ('127.0.0.1', 54321) 45 | 46 | """ 47 | -------------------------------------------------------------------------------- /lab5/lab5_q3.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import time 3 | import random 4 | import string 5 | from collections import defaultdict 6 | 7 | # Function to generate a random string of fixed length 8 | def generate_random_string(length=10): 9 | return ''.join(random.choices(string.ascii_letters + string.digits, k=length)) 10 | 11 | # Function to compute hash values 12 | def compute_hashes(strings, hash_algo): 13 | hash_dict = {} 14 | for s in strings: 15 | hash_obj = hash_algo() 16 | hash_obj.update(s.encode()) 17 | hash_value = hash_obj.hexdigest() 18 | hash_dict[s] = hash_value 19 | return hash_dict 20 | 21 | # Function to detect collisions 22 | def detect_collisions(hash_dict): 23 | reverse_hashes = defaultdict(list) 24 | collisions = [] 25 | for s, h in hash_dict.items(): 26 | reverse_hashes[h].append(s) 27 | 28 | for hash_value, strs in reverse_hashes.items(): 29 | if len(strs) > 1: 30 | collisions.append((hash_value, strs)) 31 | 32 | return collisions 33 | 34 | # Generate dataset 35 | def generate_dataset(num_strings=50, length=10): 36 | return [generate_random_string(length) for _ in range(num_strings)] 37 | 38 | # Measure time taken for hashing 39 | def measure_time_and_collisions(strings, hash_algos): 40 | results = {} 41 | 42 | for algo_name, hash_algo in hash_algos.items(): 43 | start_time = time.time() 44 | hashes = compute_hashes(strings, hash_algo) 45 | end_time = time.time() 46 | 47 | collision_info = detect_collisions(hashes) 48 | 49 | results[algo_name] = { 50 | 'time_taken': end_time - start_time, 51 | 'collisions': collision_info 52 | } 53 | 54 | return results 55 | 56 | # Main function 57 | def main(): 58 | # Define hash algorithms 59 | hash_algos = { 60 | 'MD5': hashlib.md5, 61 | 'SHA-1': hashlib.sha1, 62 | 'SHA-256': hashlib.sha256 63 | } 64 | 65 | # Generate dataset 66 | num_strings = 5000 67 | dataset = generate_dataset(num_strings) 68 | 69 | # Measure performance and collisions 70 | results = measure_time_and_collisions(dataset, hash_algos) 71 | 72 | # Print results 73 | for algo_name, result in results.items(): 74 | print(f"Algorithm: {algo_name}") 75 | print(f"Time taken: {result['time_taken']:.8f} seconds") 76 | print(f"Collisions detected: {len(result['collisions'])}") 77 | for collision in result['collisions']: 78 | print(f"Hash: {collision[0]} -> Strings: {collision[1]}") 79 | print() 80 | 81 | if __name__ == '__main__': 82 | main() 83 | 84 | """ 85 | OUTPUT: 86 | 87 | Algorithm: MD5 88 | Time taken: 0.00200009 seconds 89 | Collisions detected: 0 90 | 91 | Algorithm: SHA-1 92 | Time taken: 0.00199342 seconds 93 | Collisions detected: 0 94 | 95 | Algorithm: SHA-256 96 | Time taken: 0.00199986 seconds 97 | Collisions detected: 0 98 | 99 | """ -------------------------------------------------------------------------------- /lab6/lab6_q1.1.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import ElGamal 2 | from Crypto.Random import get_random_bytes 3 | from Crypto.Random.random import randint 4 | from Crypto.Util.number import GCD 5 | 6 | # Key Generation 7 | key = ElGamal.generate(256, get_random_bytes) 8 | public_key = (int(key.p), int(key.g), int(key.y)) # Ensure all are integers 9 | private_key = int(key.x) # Ensure the private key is an integer 10 | 11 | 12 | # Encryption 13 | def elgamal_encrypt(message, key): 14 | p, g, y = int(key.p), int(key.g), int(key.y) # Convert to native Python integers 15 | k = randint(1, p - 2) 16 | while GCD(k, p - 1) != 1: 17 | k = randint(1, p - 2) 18 | c1 = pow(g, k, p) 19 | c2 = (message * pow(y, k, p)) % p 20 | return (c1, c2) 21 | 22 | 23 | # Decryption 24 | def elgamal_decrypt(cipher_text, key): 25 | c1, c2 = cipher_text 26 | p = int(key.p) # Convert to native Python integer 27 | s = pow(c1, int(key.x), p) # Convert to native Python integers 28 | # Use pow to compute the modular inverse 29 | s_inv = pow(s, p - 2, p) # Fermat's Little Theorem 30 | return (c2 * s_inv) % p 31 | 32 | 33 | # Example usage 34 | message = 4441 35 | cipher_text = elgamal_encrypt(message, key) 36 | decrypted_message = elgamal_decrypt(cipher_text, key) 37 | 38 | print("Original message:", message) 39 | print("Encrypted message:", cipher_text) 40 | print("Decrypted message:", decrypted_message) 41 | 42 | """ 43 | Output: 44 | Original message: 4441 45 | Encrypted message: (36885507269050816452521241110201113994024314479158945324704607565400925974332, 84005621770667733674079560558995776817276579165352124261201821962284727224889) 46 | Decrypted message: 4441 47 | """ 48 | -------------------------------------------------------------------------------- /lab6/lab6_q1.2.py: -------------------------------------------------------------------------------- 1 | from ecdsa import SigningKey, NIST256p, BadSignatureError 2 | import hashlib 3 | 4 | # Generate Schnorr Keys 5 | private_key = SigningKey.generate(curve=NIST256p) # Private key 6 | public_key = private_key.verifying_key # Public key 7 | 8 | 9 | # Schnorr Sign 10 | def schnorr_sign(message, private_key): 11 | message_hash = hashlib.sha256(message.encode()).digest() 12 | signature = private_key.sign(message_hash, hashfunc=hashlib.sha256) 13 | return signature 14 | 15 | 16 | # Schnorr Verify 17 | def schnorr_verify(message, signature, public_key): 18 | try: 19 | message_hash = hashlib.sha256(message.encode()).digest() 20 | return public_key.verify(signature, message_hash, hashfunc=hashlib.sha256) 21 | except BadSignatureError: 22 | return False 23 | 24 | 25 | # Example usage 26 | message = "Hi since you are copying my code, leave a star at https://github.com/hackerbone/HackerLLMBench, thx!" 27 | signature = schnorr_sign(message, private_key) 28 | is_valid = schnorr_verify(message, signature, public_key) 29 | 30 | print("Message:", message) 31 | print("Signature:", signature.hex()) 32 | print("Signature valid:", is_valid) 33 | 34 | """ 35 | Message: Hi since you are copying my code, leave a star at https://github.com/hackerbone/HackerLLMBench, thx! 36 | Signature: 13bab1d71a6d46ba4c898943c827b9e8bdc7e6d6c152573c70059050a158ab6f1af38b2297e8ec7d2eae3be707dcac523fc721cfbfd11e7f939d875b611eb7cb 37 | Signature valid: True 38 | """ 39 | -------------------------------------------------------------------------------- /lab6/lab6_q2.py: -------------------------------------------------------------------------------- 1 | import random 2 | from sympy import isprime 3 | 4 | 5 | def generate_large_prime(bits=256): 6 | return next(n for n in iter(lambda: random.getrandbits(bits), None) if isprime(n)) 7 | 8 | 9 | def dh_keygen(bits=256): 10 | p, g = generate_large_prime(bits), random.randint( 11 | 2, (p := generate_large_prime(bits)) - 2 12 | ) 13 | a, b = random.randint(1, p - 2), random.randint(1, p - 2) 14 | A, B = pow(g, a, p), pow(g, b, p) 15 | return (p, g, A, B), (pow(B, a, p), pow(A, b, p)) 16 | 17 | 18 | (pub, (sec_A, sec_B)) = dh_keygen() 19 | print("Public values (p, g, A, B):", *pub) 20 | print("Shared secrets match?", sec_A == sec_B) 21 | 22 | 23 | """ 24 | Output: 25 | 26 | Public values (p, g, A, B): 57362700967700179027746614187317044674120646808356604905972465806112511326147 58601255477100039570470421947914615739523804063449601475977088658831515288612 39384236907483045297374802164089030443584050728289431914118131510699584633484 42966337621924961370621816291450829649739610013752348158366030243070495895726 27 | Shared secrets match? True 28 | """ 29 | -------------------------------------------------------------------------------- /lab6/lab6_q3_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import random 3 | 4 | # Client setup 5 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 | client_socket.connect(("localhost", 12345)) 7 | 8 | # Receive p, g, B from server 9 | p, g, B = map(int, client_socket.recv(1024).decode().split(",")) 10 | 11 | # Generate client's private and public keys 12 | a = random.randint(1, p - 2) 13 | A = pow(g, a, p) 14 | 15 | # Send public key A to server 16 | client_socket.send(str(A).encode()) 17 | 18 | # Calculate shared secret 19 | shared_secret_client = pow(B, a, p) 20 | print(f"Client's Shared Secret: {shared_secret_client}") 21 | 22 | client_socket.close() 23 | 24 | # Hi since you are copying my code, leave a star at https://github.com/hackerbone/HackerLLMBench, thx! 25 | 26 | """ 27 | Output: 28 | Client's Shared Secret: 91689756292912407694822192117262555328585932064209383267939711901880243983245 29 | """ 30 | -------------------------------------------------------------------------------- /lab6/lab6_q3_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import random 3 | from sympy import isprime 4 | 5 | 6 | # Generate a large prime number 7 | def generate_large_prime(bits=256): 8 | return next(n for n in iter(lambda: random.getrandbits(bits), None) if isprime(n)) 9 | 10 | 11 | # Generate DH parameters 12 | p = generate_large_prime() 13 | g = random.randint(2, p - 2) 14 | 15 | # Generate server's private and public keys 16 | b = random.randint(1, p - 2) 17 | B = pow(g, b, p) 18 | 19 | # Server setup 20 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 21 | server_socket.bind(("localhost", 12345)) 22 | server_socket.listen(1) 23 | print("Server listening on port 12345...") 24 | 25 | client_socket, addr = server_socket.accept() 26 | print(f"Connected to client: {addr}") 27 | 28 | # Send p, g, B to client 29 | client_socket.send(f"{p},{g},{B}".encode()) 30 | 31 | # Receive client's public key A 32 | A = int(client_socket.recv(1024).decode()) 33 | 34 | # Calculate shared secret 35 | shared_secret_server = pow(A, b, p) 36 | print(f"Server's Shared Secret: {shared_secret_server}") 37 | 38 | client_socket.close() 39 | server_socket.close() 40 | 41 | # Hi since you are copying my code, leave a star at https://github.com/hackerbone/HackerLLMBench, thx! 42 | 43 | """ 44 | Output 45 | 46 | Server listening on port 12345... 47 | Connected to client: ('127.0.0.1', 62717) 48 | Server's Shared Secret: 91689756292912407694822192117262555328585932064209383267939711901880243983245 49 | """ 50 | -------------------------------------------------------------------------------- /lab7/q1.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util import number 2 | import random 3 | 4 | def generate_keypair(bits=512): 5 | """Generates a public/private key pair for Paillier encryption""" 6 | p = number.getPrime(bits) 7 | q = number.getPrime(bits) 8 | n = p * q 9 | g = n + 1 # g = n + 1 is often used in practical implementations 10 | lambda_n = (p - 1) * (q - 1) # λ(n) = (p - 1)(q - 1) 11 | mu = number.inverse(lambda_n, n) # Modular inverse of λ(n) modulo n 12 | return (n, g), (lambda_n, mu) 13 | 14 | def encrypt(public_key, message): 15 | """Encrypts a message using the Paillier encryption scheme""" 16 | n, g = public_key 17 | r = random.randint(1, n - 1) # Random value for encryption 18 | ciphertext = (pow(g, message, n * n) * pow(r, n, n * n)) % (n * n) 19 | return ciphertext 20 | 21 | def decrypt(private_key, public_key, ciphertext): 22 | """Decrypts a ciphertext using the Paillier encryption scheme""" 23 | n, _ = public_key 24 | lambda_n, mu = private_key 25 | u = pow(ciphertext, lambda_n, n * n) 26 | low = (u - 1) // n 27 | message = (low * mu) % n 28 | return message 29 | 30 | def main(): 31 | # Generate key pair 32 | public_key, private_key = generate_keypair(bits=512) 33 | 34 | # Encrypt integers 35 | a = 15 36 | b = 25 37 | ciphertext_a = encrypt(public_key, a) 38 | ciphertext_b = encrypt(public_key, b) 39 | 40 | # Perform additive homomorphic operation (add ciphertexts) 41 | ciphertext_sum = (ciphertext_a * ciphertext_b) % (public_key[0] * public_key[0]) 42 | 43 | # Decrypt the result 44 | decrypted_sum = decrypt(private_key, public_key, ciphertext_sum) 45 | 46 | # Print results 47 | print(f"Ciphertext of a: {ciphertext_a}") 48 | print(f"Ciphertext of b: {ciphertext_b}") 49 | print(f"Ciphertext of a + b: {ciphertext_sum}") 50 | print(f"Decrypted sum: {decrypted_sum}") 51 | print(f"Expected sum: {a + b}") 52 | 53 | if __name__ == "__main__": 54 | main() 55 | 56 | """ 57 | Output: 58 | Ciphertext of a: 6537959441495741266482661937520248824810149853621041447570815979745345616620096180745260816828927289591233952597570582824755320124601904880332196773711727817250003141058857603094843911914104849249283198632121760675737771984198863986582253555799313834132014090878303587105137531425218385589995388429281615604862789329038266060170760381317108013567528822362881477832931399452959485436508026184056065950026237498777542868900125107440263366181800815197222515959607390095579485291273760738371089390688400602480049449464040449973013653199205442048099332505090704566965249504775847001742007999778295684369947675719051918573 59 | Ciphertext of b: 4619495029792625370548443266937361769024603598556173277837228939984181705657626359593246306057404103647683110898973041671988761868732569408325473877243831564366544534890695406931048322823536898503109887837421659041710175104440155803210157938109379947660688616193105569019298021422939163172021982124419488592068823200321781744092749542134323571140167009883285446979254525027200091899323742227247686173667713275929840651492453623506514658289683333164918318584274458486321668129903920657532155825995750517914050741138474361476168607523673353155703311373935910541786017628814328669851195140299002765327635091482876927585 60 | Ciphertext of a + b: 5441632483514900858691488001264536821816039449757926222711030200851353820970382481473112703910255679897875010665045841191527592683925973306397817962236025838572139698618623103685482638997855159365043388468877292972076934743008688013751842522899761695812043789314046537972073757337121113430753088987367637304611947412168271544167359511732805337072066718695317945938231443501554512267204827607266528741339623003520960589047282301283482090502257976338353994403757682454596064716491613568005560841140486952963515696792103770397221638946811171820753244060269558066870760039673736152334975696082783841797169273832905074100 61 | Decrypted sum: 40 62 | Expected sum: 40 63 | """ 64 | -------------------------------------------------------------------------------- /lab7/q2.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import RSA 2 | 3 | def generate_keypair(nlength=1024): 4 | """Generates a public/private key pair""" 5 | key = RSA.generate(nlength) 6 | pub_key = key.publickey() 7 | return pub_key, key 8 | 9 | def encrypt(pub_key, message): 10 | """Encrypts a message using the public key""" 11 | e = pub_key.e 12 | n = pub_key.n 13 | ciphertext = pow(message, e, n) 14 | return ciphertext 15 | 16 | def decrypt(priv_key, ciphertext): 17 | """Decrypts a ciphertext using the private key""" 18 | d = priv_key.d 19 | n = priv_key.n 20 | message = pow(ciphertext, d, n) 21 | return message 22 | 23 | def main(): 24 | # Generate key pair 25 | pub_key, priv_key = generate_keypair() 26 | 27 | # Encrypt integers 28 | a = 7 29 | b = 3 30 | ciphertext_a = encrypt(pub_key, a) 31 | ciphertext_b = encrypt(pub_key, b) 32 | 33 | # Perform multiplicative homomorphic operation (multiply ciphertexts) 34 | ciphertext_product = (ciphertext_a * ciphertext_b) % pub_key.n 35 | 36 | # Decrypt the result 37 | decrypted_product = decrypt(priv_key, ciphertext_product) 38 | 39 | # Print results 40 | print(f"Ciphertext of a: {ciphertext_a}") 41 | print(f"Ciphertext of b: {ciphertext_b}") 42 | print(f"Ciphertext of a * b: {ciphertext_product}") 43 | print(f"Decrypted product: {decrypted_product}") 44 | print(f"Expected product: {a * b}") 45 | 46 | if __name__ == "__main__": 47 | main() 48 | 49 | """ 50 | Output: 51 | Ciphertext of a: 21051445541878545751884862556424030197898411098029744973826450847188020460241223217367730441849667380545025769511557220346793434825286004333737830595284329664181210901341377335630082068340191697794015831577505804346838155806429307942630324498534685440567841045153528302264494335094793116880620240715103773518 52 | Ciphertext of b: 40830189078640980656881493751470915322580531218198144640038938831642635989431440991216988223692170830176012859052433815796994316944685838512194314969907978977162640088515568816741902278863077888847541769039358041552418209096281485115672390834475291983059284381799378131268612544424895367768586249913166422991 53 | Ciphertext of a * b: 77452143022976368900006932914935741669267184361882150431052995890825578175060147155717676518160757577140348977459396563457074502679522079476652003222927125157287793792334940809094140255216664709426070578789227483301106438320434091032469950156955855486065562468854609943242151795025384232007933525830000204362 54 | Decrypted product: 21 55 | Expected product: 21 56 | """ 57 | -------------------------------------------------------------------------------- /lab8/q1.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | from Crypto.Cipher import AES 3 | from Crypto.Util.Padding import pad, unpad 4 | from collections import defaultdict 5 | 6 | # 1a. Generate text corpus 7 | documents = [ 8 | "Did you star my repository yet?", 9 | "It is a simple process", 10 | "Visit https:/github.com/hackerbone/HackerLLMBench", 11 | "Enjoy your copying spree", 12 | "The only thing we have to fear is itself", 13 | "Do or do not there is no try", 14 | "I think therefore I am", 15 | "Knowledge is power", 16 | "Time flies like an arrow fruit flies like a banana", 17 | "A rolling stone gathers no moss", 18 | ] 19 | 20 | 21 | # Encryption & Decryption functions using AES 22 | def get_aes_key(): 23 | """Generate a random AES key.""" 24 | return hashlib.sha256(b"supersecretkey").digest() 25 | 26 | 27 | def encrypt(text, key): 28 | """Encrypt text using AES.""" 29 | cipher = AES.new(key, AES.MODE_CBC) 30 | ciphertext = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) 31 | return cipher.iv + ciphertext 32 | 33 | 34 | def decrypt(ciphertext, key): 35 | """Decrypt ciphertext using AES.""" 36 | iv = ciphertext[: AES.block_size] 37 | cipher = AES.new(key, AES.MODE_CBC, iv) 38 | decrypted = unpad(cipher.decrypt(ciphertext[AES.block_size :]), AES.block_size) 39 | return decrypted.decode("utf-8") 40 | 41 | 42 | # 1c. Create inverted index using word hashes 43 | def build_inverted_index(docs): 44 | index = defaultdict(list) 45 | for doc_id, doc in enumerate(docs): 46 | for word in doc.split(): 47 | word_hash = hashlib.sha256(word.lower().encode("utf-8")).hexdigest() 48 | index[word_hash].append(doc_id) 49 | return index 50 | 51 | 52 | # Encrypt document IDs 53 | def encrypt_inverted_index(index, key): 54 | encrypted_index = {} 55 | for word_hash, doc_ids in index.items(): 56 | encrypted_index[word_hash] = encrypt(",".join(map(str, doc_ids)), key) 57 | return encrypted_index 58 | 59 | 60 | # Decrypt inverted index results 61 | def decrypt_inverted_index_results(encrypted_doc_ids, key): 62 | decrypted_doc_ids = decrypt(encrypted_doc_ids, key) 63 | return list(map(int, decrypted_doc_ids.split(","))) 64 | 65 | 66 | # 1d. Implement search function 67 | def search(query, encrypted_index, key, documents): 68 | # Hash the query instead of encrypting 69 | query_hash = hashlib.sha256(query.lower().encode("utf-8")).hexdigest() 70 | if query_hash in encrypted_index: 71 | encrypted_doc_ids = encrypted_index[query_hash] 72 | doc_ids = decrypt_inverted_index_results(encrypted_doc_ids, key) 73 | return [documents[doc_id] for doc_id in doc_ids] 74 | else: 75 | return [] 76 | 77 | 78 | # Main execution 79 | if __name__ == "__main__": 80 | # Generate AES key 81 | aes_key = get_aes_key() 82 | 83 | # Build and encrypt inverted index 84 | inverted_index = build_inverted_index(documents) 85 | encrypted_index = encrypt_inverted_index(inverted_index, aes_key) 86 | 87 | # Take search query input and search 88 | query = input("Enter search query: ") 89 | results = search(query, encrypted_index, aes_key, documents) 90 | 91 | # Display results 92 | if results: 93 | print("Documents matching query:") 94 | for result in results: 95 | print(result) 96 | else: 97 | print("No matching documents found.") 98 | -------------------------------------------------------------------------------- /lab8/q2.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | from phe import paillier 3 | from collections import defaultdict 4 | 5 | # 2a. Generate text corpus 6 | documents = [ 7 | "Did you star my repository yet?", 8 | "It is a simple process", 9 | "Visit https:/github.com/hackerbone/HackerLLMBench", 10 | "Enjoy your copying spree", 11 | "The only thing we have to fear is fear itself", 12 | "Do or do not there is no try", 13 | "I think therefore I am", 14 | "Knowledge is power", 15 | "Time flies like an arrow fruit flies like a banana", 16 | "A rolling stone gathers no moss", 17 | ] 18 | 19 | # 2b. Paillier Encryption and Decryption functions 20 | # Generate Paillier keypair (public and private keys) 21 | public_key, private_key = paillier.generate_paillier_keypair() 22 | 23 | 24 | def word_to_hash(word): 25 | """Convert a word to a hash representation using SHA-256.""" 26 | return hashlib.sha256(word.encode("utf-8")).hexdigest() 27 | 28 | 29 | def encrypt_ids(doc_ids, pub_key): 30 | """Encrypt document IDs using Paillier encryption.""" 31 | return [pub_key.encrypt(doc_id) for doc_id in doc_ids] 32 | 33 | 34 | def decrypt_ids(encrypted_doc_ids, priv_key): 35 | """Decrypt encrypted document IDs using Paillier decryption.""" 36 | return [priv_key.decrypt(enc_id) for enc_id in encrypted_doc_ids] 37 | 38 | 39 | # 2c. Create inverted index 40 | def build_inverted_index(docs): 41 | index = defaultdict(list) 42 | for doc_id, doc in enumerate(docs): 43 | for word in doc.split(): 44 | index[word_to_hash(word.lower())].append(doc_id) 45 | return index 46 | 47 | 48 | # Encrypt the document IDs in the inverted index 49 | def encrypt_inverted_index(index, pub_key): 50 | encrypted_index = {} 51 | for word_hash, doc_ids in index.items(): 52 | encrypted_index[word_hash] = encrypt_ids(doc_ids, pub_key) 53 | return encrypted_index 54 | 55 | 56 | # 2d. Implement search function 57 | def search(query, encrypted_index, priv_key, documents): 58 | query_hash = word_to_hash(query.lower()) 59 | 60 | if query_hash in encrypted_index: 61 | encrypted_doc_ids = encrypted_index[query_hash] 62 | doc_ids = decrypt_ids(encrypted_doc_ids, priv_key) 63 | return [documents[doc_id] for doc_id in doc_ids] 64 | else: 65 | return [] 66 | 67 | 68 | # Main execution 69 | if __name__ == "__main__": 70 | # Build and encrypt inverted index 71 | inverted_index = build_inverted_index(documents) 72 | encrypted_index = encrypt_inverted_index(inverted_index, public_key) 73 | 74 | # Take search query input 75 | query = input("Enter search query: ") 76 | results = search(query, encrypted_index, private_key, documents) 77 | 78 | # Display results 79 | if results: 80 | print("Documents matching query:") 81 | for result in results: 82 | print(result) 83 | else: 84 | print("No matching documents found.") 85 | -------------------------------------------------------------------------------- /modinv.py: -------------------------------------------------------------------------------- 1 | def extended_gcd(a, b): 2 | if a == 0: 3 | return b, 0, 1 4 | gcd, x1, y1 = extended_gcd(b % a, a) 5 | x = y1 - (b // a) * x1 6 | y = x1 7 | return gcd, x, y 8 | 9 | def mod_inverse(a, m): 10 | gcd, x, _ = extended_gcd(a, m) 11 | if gcd != 1: 12 | raise ValueError(f"No modular inverse for {a} mod {m}") 13 | else: 14 | return x % m 15 | 16 | 17 | print(mod_inverse(3, 26)) 18 | 19 | y = pow(3, -1, 26) 20 | 21 | print(y) -------------------------------------------------------------------------------- /public_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmYOCzmxmvacdaWUvX/n7 3 | vjIK8J+GHymvvI2zoB0viJ3N+f+nK0lJh9GOmehtBaAH57og1kwUs9Plo3Yufcsy 4 | rnd1+0Yb8CJ8R+o5L0pc2tR0XGSi2A7rqvZy/FuI2cVRZLpfEDlaee0gZPjiaJge 5 | lSxTWVWbhrBUtP8i96YxWeSY21EMVOVloO+IK/8BfndMnjRrOSbVYGdlUG9iDP7T 6 | kC0OtOVg7XpgCdGNW9yP5eyEhdqaPRIuO73j3zZPUW6pjy/SFgSsL2lDFM4xp9+5 7 | dEPwexhnCFXWQRAa+CmXHlIpFlEnIbaIUO0AlKtC1ypu6FvaVBdMe7zaX9+nQAuV 8 | jwIDAQAB 9 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pycryptodome 2 | crypto 3 | phe -------------------------------------------------------------------------------- /rsa/rsa_config.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import RSA 2 | from Crypto.Cipher import PKCS1_OAEP 3 | from Crypto.Signature import pkcs1_15 4 | from Crypto.Hash import SHA256 5 | 6 | 7 | # Generate RSA Key Pair 8 | def generate_rsa_key_pair(): 9 | key = RSA.generate(2048) 10 | private_key = key 11 | public_key = key.publickey() 12 | return private_key, public_key 13 | 14 | 15 | # RSA Encryption 16 | def rsa_encrypt(public_key, plaintext): 17 | cipher = PKCS1_OAEP.new(public_key) 18 | ciphertext = cipher.encrypt(plaintext) 19 | return ciphertext 20 | 21 | 22 | # RSA Decryption 23 | def rsa_decrypt(private_key, ciphertext): 24 | cipher = PKCS1_OAEP.new(private_key) 25 | plaintext = cipher.decrypt(ciphertext) 26 | return plaintext 27 | 28 | 29 | # RSA Digital Signature Creation 30 | def rsa_sign(private_key, message): 31 | h = SHA256.new(message) 32 | signature = pkcs1_15.new(private_key).sign(h) 33 | return signature 34 | 35 | 36 | # RSA Digital Signature Verification 37 | def rsa_verify(public_key, message, signature): 38 | h = SHA256.new(message) 39 | try: 40 | pkcs1_15.new(public_key).verify(h, signature) 41 | return True 42 | except (ValueError, TypeError): 43 | return False 44 | 45 | 46 | # Helper functions to export keys 47 | def export_private_key(private_key): 48 | return private_key.export_key() 49 | 50 | 51 | def export_public_key(public_key): 52 | return public_key.export_key() 53 | -------------------------------------------------------------------------------- /rsa/rsa_usage.py: -------------------------------------------------------------------------------- 1 | import rsa_config 2 | 3 | # Generate RSA key pair 4 | private_key, public_key = rsa_config.generate_rsa_key_pair() 5 | 6 | # Example message 7 | message = b"Hello, Crypto!" 8 | 9 | # Encrypt and Decrypt 10 | ciphertext = rsa_config.rsa_encrypt(public_key, message) 11 | plaintext = rsa_config.rsa_decrypt(private_key, ciphertext) 12 | 13 | # Sign and Verify 14 | signature = rsa_config.rsa_sign(private_key, message) 15 | is_valid = rsa_config.rsa_verify(public_key, message, signature) 16 | 17 | print(f"Message: {message}") 18 | print(f"Ciphertext: {ciphertext}") 19 | print(f"Decrypted: {plaintext}") 20 | print(f"Signature Valid: {is_valid}") 21 | -------------------------------------------------------------------------------- /server_endsem.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import hashlib 3 | from typing import Tuple 4 | import json 5 | from elgamal.elgamal_config import * 6 | from rsa.rsa_config import * 7 | from Crypto.Util.number import bytes_to_long, long_to_bytes 8 | 9 | with open("key.pem", "rb") as f: 10 | rsa_private_key = RSA.import_key(f.read()) 11 | 12 | with open("public_key.pem", "rb") as f: 13 | rsa_public_key = RSA.import_key(f.read()) 14 | 15 | print("RSA Public Key: ", rsa_public_key) 16 | print("RSA Private Key: ", rsa_private_key) 17 | 18 | 19 | def compute_hash(data: bytes) -> str: 20 | """ 21 | Compute the SHA-256 hash of the given data. 22 | 23 | Args: 24 | data (bytes): The data to hash. 25 | 26 | Returns: 27 | str: The computed SHA-256 hash as a hexadecimal string. 28 | """ 29 | return hashlib.sha256(data).hexdigest() 30 | 31 | 32 | def handle_client_connection(client_socket: socket.socket) -> None: 33 | """ 34 | Handle an incoming client connection by receiving data, 35 | computing its hash, and sending the hash back to the client. 36 | 37 | Args: 38 | client_socket (socket.socket): The socket connection to the client. 39 | """ 40 | try: 41 | # Receive data from the client 42 | data = receive_data(client_socket) 43 | if not data: 44 | return 45 | 46 | print("Data Received", data) 47 | print("JSON LOADs") 48 | vote_data = json.loads(data) 49 | print(vote_data) 50 | 51 | 52 | elgamal_private_key = vote_data["elgamal_private_key"] 53 | elgamal_public_key = vote_data["elgamal_public_key"] 54 | votes = vote_data["votes"] 55 | 56 | final_tally = { 57 | 'BA': 0, 58 | 'BB': 0, 59 | } 60 | 61 | # Table heading print 62 | print("Name\t\tContestor\t\tVote Hash\t\tSignature\n") 63 | 64 | for lol in votes: 65 | 66 | c1 = lol["c1"] 67 | c2 = lol["c2"] 68 | signature_c1 = lol["signature_c1"] 69 | signature_c2 = lol["signature_c2"] 70 | signature_c1 = long_to_bytes(signature_c1) 71 | signature_c2 = long_to_bytes(signature_c2) 72 | 73 | ct = (c1, c2) 74 | c1 = c1.encode() 75 | c2 = c2.encode() 76 | 77 | verify_signature1 = rsa_verify(rsa_public_key, c1, signature_c1) 78 | verify_signature2 = rsa_verify(rsa_public_key, c2, signature_c2) 79 | c1 = int(c1.decode()) 80 | c2 = int(c2.decode()) 81 | 82 | ct = (c1, c2) 83 | 84 | sign_verified = verify_signature1 and verify_signature2 85 | 86 | pt = elgamal_decrypt(elgamal_private_key, elgamal_public_key, ct) 87 | 88 | 89 | # # print clean voter details 90 | # print("Voter Name: ", lol["name"]) 91 | # print("Contestor: ", lol["contestor"]) 92 | # print("Vote Hash:" , pt) 93 | 94 | # print in table 95 | print(f"{lol['name']}\t\t| {lol['contestor']} \t\t| {pt} | {sign_verified}") 96 | 97 | if sign_verified: 98 | final_tally[lol["contestor"]] ^= pt 99 | 100 | 101 | print("\n\nFinal Tally") 102 | print("Contestor\t\tVotes") 103 | for key, value in final_tally.items(): 104 | print(f"{key}\t\t{value}") 105 | 106 | json_tally = json.dumps(final_tally) 107 | print("JSON Tally: ", json_tally) 108 | long_tally = bytes_to_long(json_tally.encode()) 109 | 110 | encryted_tally = elgamal_encrypt(elgamal_public_key, long_tally) 111 | print("Encrypted Tally: ", encryted_tally) 112 | 113 | decrytped_tally = elgamal_decrypt(elgamal_private_key, elgamal_public_key, encryted_tally) 114 | 115 | print("Decrypted Tally: ", decrytped_tally) 116 | 117 | # Send the hash back to the client 118 | send_data(client_socket, json_tally.encode()) 119 | finally: 120 | client_socket.close() 121 | 122 | 123 | def receive_data(client_socket: socket.socket, buffer_size: int = 8096) -> bytes: 124 | """ 125 | Receive data from the client socket. 126 | 127 | Args: 128 | client_socket (socket.socket): The socket connection to the client. 129 | buffer_size (int): The buffer size for receiving data. 130 | 131 | Returns: 132 | bytes: The data received from the client. 133 | """ 134 | return client_socket.recv(buffer_size) 135 | 136 | 137 | def send_data(client_socket: socket.socket, data: bytes) -> None: 138 | """ 139 | Send data to the client socket. 140 | 141 | Args: 142 | client_socket (socket.socket): The socket connection to the client. 143 | data (bytes): The data to send to the client. 144 | """ 145 | client_socket.send(data) 146 | 147 | 148 | def start_server(host: str = "127.0.0.1", port: int = 65432) -> None: 149 | """ 150 | Start the server, listen for incoming connections, and handle each client. 151 | 152 | Args: 153 | host (str): The server's hostname or IP address. 154 | port (int): The port to listen on. 155 | """ 156 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 157 | server_socket.bind((host, port)) 158 | server_socket.listen(1) 159 | print(f"Server listening on {host}:{port}") 160 | 161 | while True: 162 | client_socket, addr = accept_connection(server_socket) 163 | print(f"Accepted connection from {addr}") 164 | handle_client_connection(client_socket) 165 | 166 | 167 | def accept_connection( 168 | server_socket: socket.socket, 169 | ) -> Tuple[socket.socket, Tuple[str, int]]: 170 | """ 171 | Accept a new connection from a client. 172 | 173 | Args: 174 | server_socket (socket.socket): The server's listening socket. 175 | 176 | Returns: 177 | Tuple[socket.socket, Tuple[str, int]]: The client socket and address. 178 | """ 179 | return server_socket.accept() 180 | 181 | 182 | if __name__ == "__main__": 183 | start_server() 184 | --------------------------------------------------------------------------------