#!/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""" 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(l): """returns 'True' if the values in the list L are all co-prime otherwise, it returns 'False'. """ for i, j in combinations(l, 2): if euclid(i, j) != 1: return False return True def extendedEuclid(a, b): """return a tuple of three values: x, y and z, such that x is the GCD of a and b, and x = y * a + z * b""" if a == 0: return b, 0, 1 else: g, y, x = extendedEuclid(b % a, a) return g, x - (b // a) * y, y def modInv(a, m): """returns the multiplicative inverse of a in modulo m as a positive value between zero and m-1""" # notice that a and m need to co-prime to each other. if coPrime([a, m]): linearCombination = extendedEuclid(a, m) return linearCombination[1] % m else: return 0 def extractTwos(m): """m is a positive integer. A tuple (s, d) of integers is returned such that m = (2 ** s) * d.""" # the problem can be reduced to counting how many '0's there are in # the end of bin(m). This can be done this way: m & a stretch of '1's # which can be represent as (2 ** n) - 1. assert m >= 0 i = 0 while m & (2 ** i) == 0: i += 1 return i, m >> i def int2baseTwo(x): """x is a positive integer. Convert it to base two as a list of integers in reverse order as a list.""" # repeating x >>= 1 and x & 1 will do the trick assert x >= 0 bitInverse = [] while x != 0: bitInverse.append(x & 1) x >>= 1 return bitInverse def millerRabin(n, k): """ Miller Rabin pseudo-prime test return True means likely a prime, (how sure about that, depending on k) return False means definitely a composite. Raise assertion error when n, k are not positive integers and n is not 1 """ assert n >= 1 # ensure n is bigger than 1 assert k > 0 # ensure k is a positive integer so everything down here makes sense if n == 2: return True # make sure to return True if n == 2 if n % 2 == 0: return False # immediately return False for all the even numbers bigger than 2 extract2 = extractTwos(n - 1) s = extract2[0] d = extract2[1] assert 2 ** s * d == n - 1 def tryComposite(a): """Inner function which will inspect whether a given witness will reveal the true identity of n. Will only be called within millerRabin""" x = pow(a,d,n) if x == 1 or x == n - 1: return None else: for j in range(1, s): x = pow(x,2,n) if x == 1: return False elif x == n - 1: return None return False for i in range(0, k): a = random.randint(2, n - 2) if tryComposite(a) == False: return False return True # actually, we should return probably true. def findAPrime(a, b, k): """Return a pseudo prime number roughly between a and b, (could be larger than b). Raise ValueError if cannot find a pseudo prime after 10 * ln(x) + 3 tries. """ x = random.randint(a, b) for i in range(0, int(10 * math.log(x) + 3)): if millerRabin(x, k): return x else: x += 1 raise ValueError 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. Raises ValueError if it fails to find one""" try: p = findAPrime(a, b, k) while True: q = findAPrime(a, b, k) if q != p: break except: raise ValueError n = p * q m = (p - 1) * (q - 1) while True: e = random.randint(1, m) if coPrime([e, m]): break d = modInv(e, m) return (n, e, d) def encrypt(message, modN, e, blockSize): """given a string message, public keys and blockSize, encrypt using RSA algorithms.""" return pow(message, e, modN) def decrypt(secret, modN, d, blockSize): """reverse function of encrypt""" return pow(secret, d, modN) if __name__ == '__main__': n, e, d = newKey(10 ** 100, 10 ** 101, 50) message = 35274764 print("original message is {}".format(message)) print("-"*80) cipher = encrypt(message, n, e, 15) print("cipher text is {}".format(cipher)) print("-"*80) deciphered = decrypt(cipher, n, d, 15) print("decrypted message is {}".format(deciphered))