#!/usr/bin/env python3 from random import randint, seed, getrandbits from math import log2 from Cryptotools.Utils.utils import gcd def isPrimeNumber(p): """ Check if the number p is a prime number or not. The function iterate until p is achieve. This function is not the efficient way to determine if p is prime or a composite. To identify if p is prime or not, the function check the result of the Euclidean Division has a remainder. If yes, it's means it's not a prime number Args: p (Integer): number if possible prime or not Returns: Return a boolean if the number p is a prime number or not. True if yes """ for i in range(2, p): if p % i == 0: return False return True # https://people.csail.mit.edu/rivest/Rsapaper.pdf # https://arxiv.org/pdf/1912.11546 # https://link.springer.com/content/pdf/10.1007/3-540-44499-8_27.pdf # https://link.springer.com/article/10.1007/BF00202269 # https://www.geeksforgeeks.org/dsa/how-to-generate-large-prime-numbers-for-rsa-algorithm/ # https://crypto.stackexchange.com/questions/20548/generation-of-strong-primes # https://crypto.stackexchange.com/questions/71/how-can-i-generate-large-prime-numbers-for-rsa def getPrimeNumber(n, safe=True): """ This function generate a large prime number based on "A method for Obtaining Digital Signatures and Public-Key Cryptosystems" Section B. How to Find Large Prime Numbers https://dl.acm.org/doi/pdf/10.1145/359340.359342 Args: n (Integer): The size of the prime number. Must be a multiple of 64 safe (Boolean): When generating the prime number, the function must find a safe prime number, based on the Germain primes (2p + 1 is also a prime number) Returns: Return the prime number """ if n % 64 != 0: print("Must be multiple of 64") return # from sys import getsizeof upper = getrandbits(n) lower = upper >> 7 r = randint(lower, upper) while r % 2 == 0: r += 1 # Now, we are going to compute r as prime number i = 100 while 1: # Check if it's a prime number if _millerRabinTest(r): break # TODO: it's dirty, need to change that # i = int(log2(r)) # i *= randint(2, 50) r += i # print(f"{i} {r}") i += 1 # print(_fermatLittleTheorem(r)) # print(getsizeof(r)) return r def getSmallPrimeNumber(n) -> int: """ This function is deprecated Args: n (Integer): Get the small prime number until n Returns: Return the first prime number found """ is_prime = True while True: for i in range(2, n): # If n is divisible by i, it's not a prime, we break the loop if n % i == 0: is_prime = False break if is_prime: break is_prime = True n = n + 1 p = n return p def get_prime_numbers(n) -> list: """ This function get all prime number of the n Args: n (Integer): find all prime number until n is achieves Returns: Return a list of prime numbers """ l = list() for i in range(2, n): if n % i == 0: l.append(i) return l def get_n_prime_numbers(n) -> list: """ This function return a list of n prime numbers Args: n (Integer): the range, must be an integer Returns: Return a list of n prime numbers """ l = list() count = 2 index = 0 while index < n: is_prime = True for x in range(2, count): if count % x == 0: is_prime = False break if is_prime: l.append(count) index += 1 count += 1 return l def are_coprime(p1, p2): """ This function check if p1 and p2 are coprime Args: p1 (list): list of prime numbers of the first number p2 (list): list of prime number of the second number Returns: Return a boolean result """ r = True for entry in p1: if entry in p2: r = False break return r def sieveOfEratosthenes(n) -> list: """ This function build a list of prime number based on the Sieve of Erastosthenes Args: n (Integer): Interate until n is achives Returns: Return a list of all prime numbers to n """ if n < 1: return list() eratost = dict() for i in range(2, n): eratost[i] = True for i in range(2, n): if eratost[i]: for j in range(i*i, n, i): eratost[j] = False sieve = list() for i in range(2, n): if eratost[i]: sieve.append(i) return sieve def _fermatLittleTheorem(n) -> bool: """ The Fermat's little theorem. if n is a prime number, any number from 0 to n- 1 is a multiple of n Args: n (Integer): Check if n is prime or not Returns: Return True if the number a is a multiple of n, otherwise it's False """ a = randint(1, n - 1) # We compute a ** (n - 1) % n if pow(a, n - 1, n) == 1: return True return False def _millerRabinTest(n) -> bool: """ This function execute a Miller Rabin test For the algorithm, it's based on the pseudo-algo provided by this document: * https://www.cs.cornell.edu/courses/cs4820/2010sp/handouts/MillerRabin.pdf The Mille-Rabin test is an efficient way to determine if n is prime or not Args: n (Integer): Check if n is a prime number Returns: Return a boolean. True for a prime otherwise, it's a composite number """ if n < 2 or (n > 2 and n % 2 == 0): # If n is even, it's a composite return False k = 0 q = n - 1 #while (q & 1) == 0: # k += 1 # q >>= 1 while q % 2 == 0: k += 1 q //= 2 # We choose a: a < n - 1 for _ in range(40): a = randint(2, n - 1) # We compute a ** q % n x = pow(a, q, n) # If it's a composite number if x == 1 or x == n - 1: continue for _ in range(k): z = pow(x, 2, n) if z == 1 or z == n - 1: return False else: return False return True def sophieGermainPrime(p) -> bool: """ Check if the number p is a safe prime number: 2p + 1 is also a prime Args: p (Integer): Possible prime number Returns: Return True if p is a safe prime number, otherwise it's False """ pp = 2 * p + 1 return _millerRabinTest(pp) def isSafePrime(n) -> bool: """ This function has not been implemented yet, but check if the number n is a safe prime number. This function will test different properties of the possible prime number n Args: n (Integer): the prime number to check Returns: Return a Boolean if the prime number n is safe or not. True if yes, otherwise it's False """ if n.bit_length() >= 256: return True # Do Sophie Germain's test return False