Skip to content

Instantly share code, notes, and snippets.

@ychennay
Created August 25, 2019 02:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ychennay/0dbc2b5a3df878ae58db09fc093f775b to your computer and use it in GitHub Desktop.
Save ychennay/0dbc2b5a3df878ae58db09fc093f775b to your computer and use it in GitHub Desktop.
Public-Key Encryption Exchange and Encryption Algorithm Example
from math import sqrt
import base64
from itertools import count, islice
import random
from typing import List
from dataclasses import dataclass
from Crypto.Cipher import AES
import hashlib
from Crypto.Random import get_random_bytes
def is_prime(n):
return n > 1 and all(n%i for i in islice(count(2), int(sqrt(n)-1)))
nonce = get_random_bytes(15)
# generates prime numbers between 1 and 10000
primes = [i for i in range(1,10000) if is_prime(i)]
@dataclass
class PublicKey:
g: int
p: int
value: int
@dataclass
class PrivateKey:
g: int
p: int
value: int
class AESCipher:
@staticmethod
def encrypt(message: str, key: str):
hex_key = hashlib.sha256(key.encode()).hexdigest()[:16].encode('utf-8')
obj = AES.new(hex_key, AES.MODE_OCB, nonce=nonce)
return obj.encrypt_and_digest(message.encode())
@staticmethod
def decrypt(ciphertext, mac, key):
hex_key = hashlib.sha256(key.encode()).hexdigest()[:16].encode('utf-8')
cipher = AES.new(hex_key, AES.MODE_OCB, nonce=nonce)
return cipher.decrypt_and_verify(ciphertext, mac).decode()
class User:
def __init__(self, name:str, private_value):
self.private_value: int = private_value
print(f"\n{name}'s private value is {private_value}.")
self.name = name
self.public_keys: Dict[str, PublicKey] = {}
self.p = self.generate_p()
self.g = self.generate_g()
self.compute_public_key_value()
self.shared_secrets: Dict[str, int] = {}
@property
def public_key(self)-> PublicKey:
return self.public_keys[self.name]
def generate_p(self, lower_bound: int = 1000, upper_bound: int = 10000):
filtered_primes: List[int] = list(filter(lambda prime: lower_bound <= prime <= upper_bound, primes))
p = random.choice(filtered_primes)
print(f"{self.name} generated {p} for p.")
return p
def generate_g(self, lower_bound: int = 2, upper_bound: int = 50):
filtered_primes: List[int] = list(filter(lambda prime: lower_bound <= prime <= upper_bound, primes))
while True:
g = random.choice(filtered_primes)
if self.p and g < self.p:
print(f"{self.name} generated {g} for g.")
return g
def compute_public_key_value(self, save_state: bool = True)-> int:
public_key_value = (self.g ** self.private_value) % self.p
if save_state:
self.public_keys[self.name] = PublicKey(self.g, self.p, public_key_value)
return public_key_value
def receive_key(self, value, party_name: str)-> None:
print(f"{self.name} received {party_name}'s public key: {value}")
self.public_keys[party_name] = value
self.compute_shared_secret(party_name)
def compute_shared_secret(self, name: str)-> int:
other_public_key: PublicKey = self.public_keys[name]
shared_secret = (other_public_key.value ** self.private_value) % other_public_key.p
self.shared_secrets[name] = shared_secret
def encrypt_message(self, receiver: str, message: str):
public_value = self.public_keys[receiver].g ** self.private_value % self.public_keys[receiver].p
K = self.public_keys[receiver].value ** self.private_value % self.public_keys[receiver].p
ciphertext, mac = AESCipher.encrypt(message, key=str(K))
return ciphertext, mac, public_value
def send_message(self, receiver: str, message: str):
print(f"{self.name} wants to send a message of '{message}' to {receiver}")
ciphertext, mac, public_value = self.encrypt_message(receiver, message)
return {
"sender": self.name,
"ciphertext": ciphertext,
"mac": mac,
"public_value": public_value
}
def receive_message(self, sender: str, ciphertext, mac, public_value):
print(f"{self.name} received a message: {ciphertext} from {sender}")
decryption_key = (public_value ** self.private_value) % self.p
decrypted_message: str = AESCipher.decrypt(ciphertext, mac, key=str(decryption_key))
print(f"After decrypting message, plain text is '{decrypted_message}'")
return decrypted_message
if __name__ == "__main__":
alice = User(name="Alice", private_value=92)
bob = User(name="Bob", private_value=15)
# exchange public keys
alice.receive_key(bob.public_key, bob.name)
bob.receive_key(alice.public_key, alice.name)
message = bob.send_message(receiver="Alice", message="Hello, Alice.")
alice.receive_message(**message)
'''
output of python public-key.py:
Alice's private value is 92.
Alice generated 1103 for p.
Alice generated 11 for g.
Bob's private value is 15.
Bob generated 2699 for p.
Bob generated 5 for g.
Alice received Bob's public key: PublicKey(g=5, p=2699, value=1319)
Bob received Alice's public key: PublicKey(g=11, p=1103, value=564)
Bob wants to send a message of 'Hello, Alice.' to Alice
Alice received a message: b"\xb3\x17\x02\xad1'\xe7!\xab&\xfb\xed\x0b" from Bob
After decrypting message, plain text is 'Hello, Alice.
'''
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment