EITF55-project1/py/rsa.py
2025-02-16 09:11:06 +01:00

95 lines
2.1 KiB
Python

from Crypto.Util.number import getPrime, inverse
import random
def miller_rabin(n, k=5):
if n == 2 or n == 3:
return True
if n <= 1 or n % 2 == 0:
return False
# Write n-1 as d * 2^r
r = 0
d = n - 1
while d % 2 == 0:
d //= 2
r += 1
# Perform the test k times
for _ in range(k):
a = random.randint(2, n - 2)
x = pow(a, d, n) # x = a^d % n
if x == 1 or x == n - 1:
continue
# Keep squaring x and check for n-1
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
# Euclidean algorithm
def extended_gcd(a, b):
"""Computes the GCD of a and b, along with coefficients x and y such that ax + by = gcd(a, b)."""
if b == 0:
return a, 1, 0
g, x1, y1 = extended_gcd(b, a % b)
x = y1
y = x1 - (a // b) * y1
return g, x, y
def mod_inverse(a, m):
g, x, _ = extended_gcd(a, m)
if g != 1: # a and m must be coprime
raise ValueError(f"{a} has no modular inverse modulo {m}")
return x % m
def my_prime(bits: int = 1024) -> int:
i = random.randint(1 << (bits - 1), 1 << bits)
return i if miller_rabin(i) else my_prime(bits)
print(my_prime(8))
# Key Generation
def generate_keys(bits: int = 1024) -> tuple[tuple[int, int], tuple[int, int]]:
p = my_prime(bits // 2)
q = my_prime(bits // 2)
n = p * q
phi = (p - 1) * (q - 1)
e = 65537 # Common public exponent
d = inverse(e, phi)
return (e, n), (d, n) # Public, Private keys
# Encryption
def encrypt(msg: int, pubkey: tuple[int, int]) -> int:
e, n = pubkey
return pow(msg, e, n)
# Decryption
def decrypt(cipher: int, privkey: tuple[int, int]) -> int:
d, n = privkey
return pow(cipher, d, n)
# Example Usage
pub, priv = generate_keys()
pub = (177349751, 2144805071)
priv = (1698859991, 2144805071)
message = 42
ciphertext = encrypt(message, pub)
plaintext = decrypt(ciphertext, priv)
print(f"Message: {message}, Ciphertext: {ciphertext}, Decrypted: {plaintext}")