#!/usr/bin/env python3 import math import random from itertools import combinations def euclid(a, b): """returns the Greatest Common Divisor of a and b. Inspired by https://en.wikipedia.org/wiki/Euclidean_algorithm#Implementations""" a = abs(a) b = abs(b) if a < b: a, b = b, a while b != 0: a, b = b, a % b return a def coPrime(x, y): """returns 'True' if the values in the list L are all co-prime otherwise, it returns 'False'. """ if euclid(x, y) != 1: return False return True def iterative_extended_euclid(a, b): """ returns the GCD (in old_r) of a and b as well as the "Bezout's Identity" such that a*old_s + b*old_t = GCD(a,b). Algorithm is adopted from https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Pseudocode """ old_r, r = a, b old_s, s = 1, 0 old_t, t = 0, 1 while r != 0: quotient = old_r // r old_r, r = r, old_r - (quotient * r) old_s, s = s, old_s - (quotient * s) old_t, t = t, old_t - (quotient * t) print(old_r, old_s, old_t) return old_r, old_s, old_t def modInv(a, m): """returns the multiplicative inverse of a in modulo m as a positive value between zero and m-1 Adopted from https://en.wikipedia.org/wiki/Modular_multiplicative_inverse#Extended_Euclidean_algorithm """ if coPrime(a, m): linear_combination = iterative_extended_euclid(a, m) print(linear_combination[1] % m) return linear_combination[1] % m else: return 0 def miller_rabin(n, k): # Implementation uses the Miller-Rabin Primality Test # The optimal number of rounds for this test is 40 # See http://stackoverflow.com/questions/6325576/how-many-iterations-of-rabin-miller-should-i-use-for-cryptographic-safe-primes # for justification # If number is even, it's a composite number if n == 2: return True if n % 2 == 0: return False r, s = 0, n - 1 while s % 2 == 0: r += 1 s //= 2 for _ in range(k): a = random.randrange(2, n - 1) x = pow(a, s, n) if x == 1 or x == n - 1: continue for _ in range(r - 1): x = pow(x, 2, n) if x == n - 1: break else: return False return True def findAPrime(a, b, k): """Return a pseudo prime number roughly between a and b, (could be larger than b). """ x = random.randint(a, b) while True: if miller_rabin(x, k): return x else: x += 1 def newKey(a, b, k): """ Try to find two large pseudo primes roughly between a and b. Generate public and private keys for RSA encryption.""" p = findAPrime(a, b, k) while True: q = findAPrime(a, b, k) if q != p: break n = p * q m = (p - 1) * (q - 1) # Compute phi(n) for n=pq where p and q are prime # Find and e that is coprime to phi(n) to be used in the public key while True: e = random.randint(1, m) if coPrime(e, m): break # Let d be the modular inverse to e, to be used as private key d = modInv(e, m) return (n, e, d) def encrypt(message, modN, e): """given a string message, public keys and blockSize, encrypt using RSA algorithms.""" return pow(message, e, modN) def decrypt(secret, modN, d): """reverse function of encrypt""" return pow(secret, d, modN) if __name__ == '__main__': n, e, d = newKey(2**40, 2 ** 41, 20) message = 35274764 print("original message is {}".format(message)) print("-"*80) cipher = encrypt(message, n, e) print("cipher text is {}".format(cipher)) print("-"*80) deciphered = decrypt(cipher, n, d) print("decrypted message is {}".format(deciphered))