From 535a49bad95859d65be667b124754b8a9890e2f6 Mon Sep 17 00:00:00 2001 From: Alexander Munch-Hansen Date: Wed, 24 Feb 2021 00:16:40 +0100 Subject: [PATCH] initial --- main.py | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..9a7712e --- /dev/null +++ b/main.py @@ -0,0 +1,182 @@ +#!/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)) +