Random RSA/miller-rabin/euclidean
This commit is contained in:
parent
74d60a2cb5
commit
ffad3d5118
3 changed files with 270 additions and 0 deletions
78
py/milrab.py
Normal file
78
py/milrab.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
import random
|
||||
import time
|
||||
|
||||
|
||||
def lcg(seed, a=1664525, c=1013904223, m=2**32):
|
||||
while True:
|
||||
seed = (a * seed + c) % m
|
||||
yield seed # Produces an infinite stream of numbers
|
||||
|
||||
|
||||
rand_gen = lcg(42)
|
||||
|
||||
|
||||
def randint(a, b) -> int:
|
||||
return next(rand_gen) % (b - a + 1) + a
|
||||
|
||||
|
||||
def milrab(n: int, iter: int = 5) -> bool:
|
||||
"Miller-Rabin probabilistic primality test"
|
||||
|
||||
# These should need no further introduction
|
||||
if n < 2:
|
||||
return False
|
||||
if n in (2, 3):
|
||||
return True
|
||||
if n % 2 == 0:
|
||||
return False
|
||||
|
||||
# Keep in mind that here we know that m will be an even number
|
||||
m = n - 1
|
||||
k = 0
|
||||
|
||||
# Executed at least once
|
||||
while m % 2 == 0:
|
||||
m //= 2 # Int divide for int result type
|
||||
k += 1
|
||||
|
||||
for _ in range(iter):
|
||||
a = randint(2, n - 2) # Any integer in range 1 < a < n - 1
|
||||
b = pow(a, m, n)
|
||||
|
||||
# Valid only for the first iteration
|
||||
if b == 1 or b == n - 1:
|
||||
continue # Likely a prime
|
||||
|
||||
# The rest, we start from 0
|
||||
for _ in range(k - 1):
|
||||
b = pow(b, 2, n)
|
||||
if b == n - 1:
|
||||
break # May be prime
|
||||
if b == 1: # For sure not prime
|
||||
return False
|
||||
|
||||
else: # Python way to branch if loop never calls break
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
assert milrab(53, 1) == True
|
||||
assert milrab(53, 2) == True
|
||||
assert milrab(53, 3) == True
|
||||
assert milrab(53, 4) == True
|
||||
assert milrab(203, 10) == False
|
||||
|
||||
assert milrab(10, 4) == False
|
||||
|
||||
start = time.perf_counter()
|
||||
primes = []
|
||||
for p in range(2**15, 2**16):
|
||||
if milrab(p, 10):
|
||||
primes.append(p)
|
||||
end = time.perf_counter()
|
||||
print(f"Time taken: {end - start:.6f} seconds")
|
||||
|
||||
|
||||
for k in range(10):
|
||||
assert milrab(53, k) == True
|
95
py/rsa.py
Normal file
95
py/rsa.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
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}")
|
97
py/sketch.py
Normal file
97
py/sketch.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
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
|
||||
|
||||
|
||||
assert miller_rabin(0) == False
|
||||
assert miller_rabin(1) == False
|
||||
assert miller_rabin(2) == True
|
||||
assert miller_rabin(3) == True
|
||||
assert miller_rabin(4) == False
|
||||
|
||||
|
||||
def primitive_isprime(num):
|
||||
for n in range(2, int(num**0.5) + 1):
|
||||
if num % n == 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
assert not miller_rabin(8)
|
||||
assert miller_rabin(11)
|
||||
|
||||
|
||||
def mod_inverse(a, m):
|
||||
return pow(a, -1, m)
|
||||
|
||||
|
||||
assert mod_inverse(3, 7) == 5
|
||||
assert mod_inverse(10, 17) == 12
|
||||
assert mod_inverse(7, 13) == 2
|
||||
assert mod_inverse(65537, 3445361432) is not None
|
||||
|
||||
|
||||
def gcd(x, y):
|
||||
while y:
|
||||
x, y = y, x % y
|
||||
return abs(x)
|
||||
|
||||
|
||||
assert gcd(3, 9) == 3
|
||||
assert gcd(3, 4) == 1
|
||||
|
||||
p = 60737
|
||||
q = 56713
|
||||
assert miller_rabin(p)
|
||||
assert miller_rabin(q)
|
||||
|
||||
n = p * q
|
||||
phi_n = (p - 1) * (q - 1)
|
||||
assert not miller_rabin(n)
|
||||
assert not miller_rabin(phi_n)
|
||||
|
||||
e = 65537
|
||||
assert e == (1 << 16) | 0x1
|
||||
assert gcd(e, phi_n) == 1
|
||||
|
||||
d = mod_inverse(e, phi_n)
|
||||
assert d is not None and (e * d) % phi_n == 1
|
||||
|
||||
m = 69
|
||||
|
||||
c = pow(69, e, n)
|
||||
dec = pow(c, d, n)
|
||||
|
||||
print("Ciphertext: ", c)
|
||||
print("Decrypted: ", dec)
|
||||
|
||||
assert dec == m
|
Loading…
Add table
Reference in a new issue