95 lines
2.1 KiB
Python
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}")
|