def gen_GL_2(poly, degree): """Return generator of Galois Field's GF(p^degree) based on primitive polynomial poly in Zn.""" # Order of multiplicative subgroup pn1 = (1 << degree) - 1 if utils.millerRabin(pn1): q = [pn1] else: q = utils.findPrimeFactors(pn1) config.ELEMENTS = [i for i in range(1 << degree)] genList = config.ELEMENTS goodGen = None for gen in genList: # α(x)^(p^n-1) % f(x) == 1 firstTest = poly_exp_mod_2(gen, pn1, poly) if firstTest == 1: isGood = True for elt in q: # α(x)^((p^n-1)/q) % f(x) != 1, for all q that are prime factors of p^n-1 secondTest = poly_exp_mod_2(gen, pn1 / elt, poly) # DO NOT REPLACE WITH 'if secondTest', i don't know why but intA doesn't work otherwise. if secondTest == 1: isGood = False if isGood: goodGen = gen break return goodGen
def inv(a: int, m: int, Verbose: bool = False): """ Returns inverse of a mod m. If a and m are prime to each other, then there is an a^(-1) such that a^(-1) * a is congruent to 1 mod m. """ if ut.euclid(a, m) != 1: if Verbose: print( f"gcd({a}, {m}) = {ut.euclid(a, m)} != 1 thus you cannot get an invert of {a}." ) raise ValueError( f"gcd({a}, {m}) != 1 thus you cannot get an invert of {a}.") # a modular multiplicative inverse can be found directly elif a == 0: if Verbose: print( f"a = 0 and 0 cannot have multiplicative inverse ( 0 * nothing = 1 ) ." ) raise ValueError("0 cannot have multiplicative inverse.") elif ut.millerRabin(m) and m % a != 0: # A simple consequence of Fermat's little theorem is that if p is prime and does not divide a # then a^−1 ≡ a^(p − 2) (mod p) is the multiplicative if Verbose: print( f"From Fermat's little theorem, because {m} is prime and does not divide {a} so: {a}^-1 = {a}^({m}-2) mod {m}" ) u = ut.square_and_multiply(a, m - 2, m) elif ut.coprime(a, m) and m < (1 << 20): #From Euler's theorem, if a and n are coprime, then a^−1 ≡ a^(φ(n) − 1) (mod n). if Verbose: print( f"From Euler's theorem, because {a} and {m} are coprime -> {a}^-1 = {a}^(phi({m})-1) mod {m}" ) u = ut.square_and_multiply(a, phi(m, 1, 1, Verbose) - 1, m) else: if Verbose: print( f"Modular inverse u solves the given equation: a.u+m.v=1.\n Let's use the euclid extended algorithm tho." ) # Modular inverse u solves the given equation: a.u+m.v=1 # n number of iterations _, u, _, _, _ = ut.euclid_ext(a, m, Verbose) if u < 0: u += m if Verbose: return u, f"u = {u} + {m}k, k in Z" else: return u
def randomPrime(nBits: int = 512, gen=None, condition=lambda p: p == p, k: int = 1, Verbose=False): """ Return generated prime numbers with bitlength nBits. Stops after the generation of k prime numbers. You can verify a condition with condition method. """ # Generate random odd number of nBits assert nBits >= 8 and nBits <= 4096 if not gen: gen = randomInt def find(Verbose: bool): maybe = gen(0, nBits) if Verbose: print("Finding one who respect given condition(s).") while not condition(maybe): maybe = gen(0, nBits) if Verbose: print("Found !") return maybe maybe = find(Verbose) b = k primes = [] while k > 0: if millerRabin(maybe): primes.append(maybe) k -= 1 maybe = find(Verbose) if b: return primes[0] return primes
def legendreSymbol(a: int, p: int, quadraticList=None): """ https://en.wikipedia.org/wiki/Legendre_symbol LegendreSymbol of quadratics Residues is always 1 except for 0 and 1. """ assert ut.millerRabin(p) if quadraticList == None: quadraticList = quadraticsResidues(p) if a % p == 0: return 0 elif a % p != 0 and a in quadraticList: return 1 else: return -1
def safePrime_worker(nBits: int = 1024, randomFunction=None, easyGenerator: bool = False, Verbose: bool = False, flag=None, returnL: list = []): """ Function executed on each process for safe prime generation """ import ressources.interactions as it from multiprocessing import Manager if not flag: flag = Manager().Value("i", 0) if easyGenerator: if Verbose: print("Easy generator choosen.") p_filter = lambda p: p % 3 == 2 and ( p % 12 == 1 or p % 12 == 11 ) # p = 1 mod 12 make 11 as primitive root else: p_filter = lambda p: p % 3 == 2 while not bool(flag.value): # For faster searching # Calculate 2q +1 and (q-1)//2 # Return Sophie Germain's prime according to what is prime. if randomFunction is None: randomFunction = randomInt q = randomPrime(nBits, randomFunction, p_filter, 1) p1 = 2 * q + 1 p2 = (q - 1) // 2 if Verbose: it.clear() print(f"Prime {q} candidate's.") if millerRabin(p1): if Verbose: print("q is prime and 2q +1 too.") print(f"\nSophie Germain prime's: {q}\n") sophieGermain_prime, safe_prime = q, p1 # Safe prime found flag.value = 1 returnL.append((safe_prime, sophieGermain_prime)) elif millerRabin(p2): if Verbose: print("q is prime and (q-1)/2 too.") print(f"\nSophie Germain prime's: {p2}\n") sophieGermain_prime, safe_prime = p2, q # Safe prime found flag.value = 1 returnL.append((safe_prime, sophieGermain_prime)) else: if Verbose: print( "But 2 * him + 1 and (him - 1) / 2 doesn't seem to be primes...\n" )
def phi(n: int, m: int = 1, k: int = 1, Verbose: bool = False): """ Totient recurcive function for integer n. Can compute: phi(n*m) with phi(n, m). phi(p^k) with phi(p, 1, k) """ if Verbose: print(f"\n----------- phi(n={n}, m={m}, k={k})--------------") ## Special cases ## twoN = n // 2 if m != 1: d = ut.euclid(n, m) if Verbose: print(f"gcd({n}, {m}) = {d}") print(f"phi({n})*phi({m})*({d}/phi({d}))") return phi(n, 1, k, Verbose) * phi(m, 1, k, Verbose) * int( (d / phi(d, 1, k, Verbose))) elif k != 1: # phi(n^k) = n ^(k-1) * phi(n) mult = ut.square_and_multiply(n, k - 1) if Verbose: print(f"phi(n^k) = n ^(k-1) * phi(n) = {mult} * phi({n})") return mult * phi(n, 1, 1, Verbose) else: if n >= 0 and n <= 123: # Fastest results for common totients (sequence A000010 in the OEIS) totients = [ 0, 1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18, 8, 12, 10, 22, 8, 20, 12, 18, 12, 28, 8, 30, 16, 20, 16, 24, 12, 36, 18, 24, 16, 40, 12, 42, 20, 24, 22, 46, 16, 42, 20, 32, 24, 52, 18, 40, 24, 36, 28, 58, 16, 60, 30, 36, 32, 48, 20, 66, 32, 44, 24, 70, 24, 72, 36, 40, 36, 60, 24, 78, 32, 54, 40, 82, 24, 64, 42, 56, 40, 88, 24, 72, 44, 60, 46, 72, 32, 96, 42, 60, 40, 100, 32, 102, 48, 48, 52, 106, 36, 108, 40, 72, 48, 112, 36, 88, 56, 72, 58, 96, 32, 110, 60, 80, 60, 100, 36, 126, 64, 84, 48, 130, 40, 108, 66, 72, 64, 136, 44, 138, 44, 138, 48, 92, 70, 120 ] r = totients[n] if Verbose: print(f"\nCommon totient phi({n}) = {r}") return r elif ut.millerRabin(n): # n is a prime number so phi(p) = (p-1) # p^(k-1) * phi(p) = p^(k-1) * (p-1) if Verbose: print(f"\n{n} is a prime number so phi(p) = (p-1)") return (n - 1) # If even: elif not twoN & 1: if Verbose: print(f"\nphi({n}) = phi(2*{twoN}) = 2 * phi({twoN}).") return 2 * phi(twoN, m, k, Verbose) ## Special cases ## else: if Verbose: print(f"\nLet's calculate phi({n}) with prime factors way.\n") tot = n if Verbose: print("Generating primes factors ...\n") for factor in ut.findPrimeFactors(n): if Verbose: print(f"Factor : {factor}") tot -= tot // factor return tot
def isGenerator(e: int, n: int, lagrangeWay: bool = True, printOther: bool = False, Verbose: bool = False): """ Returns whether an element e is generator of Zn. A unit g ∈ Zn* is called a generator or primitive root of Zn* if for every a ∈ Zn* we have g^k = a for some integer k. In other words, if we start with g, and keep multiplying by g eventually we see every element. By definition, g is a generator of Zn* if and only if that cycling does not occur before these n−1 iterations. lagrangeway is by default set to true. Base on the fact than as a consequence of Lagrange's theorem, ordn(e) always divides φ(n). If ordn(e) is actually equal to φ(n), and therefore as large as possible, then e is called a primitive root modulo n. """ if lagrangeWay: totient = phi(n, 1, 1, Verbose) order = multiplicativeOrder(e, n, False, Verbose) if Verbose: print( f"\nIf phi({n}) is equal to mutliplicative order of {e} modulo {n} then it's a generator of {n}." ) print(f"phi(n) = {totient} and order(e, n) = {order}.\n") if totient == order: if printOther: if Verbose: print(f"There are {phi(phi(n))} generators in Z{n}.") print(f"{e} is the a generator of Z{n}.\n") return True, findOtherGenerators(e, n, Verbose) return True else: return False else: if not ut.millerRabin(n): # Slowest way, keeped to verify results elements = [] for i in range(1, n): t = ut.square_and_multiply(e, i, n) if t in elements: continue else: if Verbose: print(f"\n{e}^{i} = {t} mod {n}") #if cycling occurs if t == 1 and t in elements: return False else: elements.append(t) if printOther: if Verbose: print(f"There are {phi(phi(n))} generators in Z{n}.") print( f"{e} is the a generator of Z{n} with elements: {elements}\n" ) return True, findOtherGenerators(e, n, Verbose) if Verbose: print(f"There are {phi(phi(n))} generators in Z{n}.") print( f"{e} is the a generator of Z{n} with elements: {elements}\n" ) return True elif e % n != 0: # We can test if some g not divisible by p is a generator of Zp* # by checking if g^k mod p != 1 # with k = (p-1)/q for q each of prime factors of p-1 L = ut.findPrimeFactors(n - 1) if Verbose: print( f"{e} doesn't divide {n}, let's check if {e}^k mod {n} != 1." ) print( f"With k = ({n} -1 ) / q for q each of prime factors of {n}-1 ( = {L})." ) for k in L: t = ut.square_and_multiply(e, k, n) if Verbose: print(f"\n{e}^{k} = {t} mod {n}") if t == 1: return False if printOther: if Verbose: print(f"There are {phi(phi(n))} generators in Z{n}.") return True, findOtherGenerators(e, n, Verbose) return True
def primitiveRoot(n: int, totient=None, Verbose: bool = False): """ Returns primitive root modulo n. https://en.wikipedia.org/wiki/Fermat%27s_little_theorem """ if totient == None: totient = phi(n, 1, 1, Verbose) if Verbose: print(f"Phi({n}) = {totient}\n") if n > 3 and ut.millerRabin(n): q = (n - 1) // 2 if Verbose: print(f"\n{n} is a prime number.") # To deal with safe prime if ut.millerRabin(q): if Verbose: print(f"{n} is a Safe Prime! Because {q} prime too.") print("Choose random number between g [2, p-1]") import random as rd g = rd.randrange(2, n) if Verbose: print( "To verify if g is a generator, you have to verify than g^2 and g^q are differents from 1." ) while 1 in [ ut.square_and_multiply(g, 2, n), ut.square_and_multiply(g, q, n) ]: g = rd.randrange(2, n) else: if Verbose: print(f"{g} is a generator of Z{n}*.") return g else: if Verbose: print(f"Let's find all prime factors of {totient}:") s = ut.findPrimeFactors(totient) if Verbose: print("\n-----------------------------") print(f"{n} is prime and prime factors of totient are: {s} ") print( f"Computing all g^(phi({n})/p_i) mod {n} with p_i prime factors." ) print( f"If all the calculated values are different from 1, then g is a primitive root." ) print("-----------------------------") for e in range(2, totient + 1): # Iterate through all prime factors of phi. # and check if we found a power with value 1 flag = False for it in s: t = ut.square_and_multiply(e, totient // it, n) if Verbose: print(f" {e}^(phi({n})/{it}) mod {n} = {t} ") if t == 1: flag = True break if not flag: if Verbose: print(f"Generator is: {e}") return e # If no primitive root found return -1 else: if Verbose: print( f"\nAccording to Euler's theorem, a is a primitive root mod {n} if and only if the multiplicative order of a is ϕ(n) = {totient}." ) for e in range(1, n): o = multiplicativeOrder(e, n, Verbose) if Verbose: print(f"\nMultiplicative order({e}, {n}) = {o} \n") if o == totient: if Verbose: print(f"Generator is: {e}") return e # If no primitive root found if Verbose: print( f"Since there is no number whose order is {totient}, there are no pritive roots modulo {n}." ) return -1