def primitive_root(n): """ Finds a primitive root modulo n using Gauss's algorithm and some related facts. """ fact = factorize(n) if len(fact) == 1: p, e = fact[0] if p == 2: if e <= 2: return n // 2 return pr = _primitive_root(p) if e > 1 and pow(pr, p - 1, p ** 2) == 1: pr += p return pr elif len(fact) == 2: if fact[0] != (2, 1): return p, e = fact[1] pr = _primitive_root(p) if e > 1 and pow(pr, p - 1, p ** 2) == 1: pr += p if pr % 2 == 0: pr += p ** e return pr
def algebras(m_maxs): """ Generator for the possible algebras. Args: m_maxs: Maximum possible m values given p and d as a tuple e.g. (m, p, d). Returns: A 5-tuple ( m, p, d, phi_m, nSlots ) for the algebra. Usage: >>> print( '\\n'.join(map(str,algebras( ((24, 5, 2),) )))) (2, 5, 1, 1, 1) (4, 5, 1, 2, 2) (8, 5, 2, 4, 2) (3, 5, 2, 2, 1) (6, 5, 2, 2, 1) (12, 5, 2, 4, 2) (24, 5, 2, 8, 4) """ # Generate the divisors for each max_m for m_max,p,d in m_maxs: factors = numth.factorize(m_max) for mFactors in numth.divisorsFactors(factors): d_real = d m = numth.calcDivisor(mFactors) if m == 1: continue phi_m = numth.phi(mFactors) # Correct for order of p in mod m a.k.a. d phimFactors = numth.factorize(phi_m) for e in sorted(numth.divisors(phimFactors)): if p**e % m == 1: d_real = e break nSlots, r = divmod(phi_m, d_real) if r != 0: raise ArithmeticError("Fractional nslots should not exist.") # SolnObj just a 5-tuple ( m, p, d, phi_m, nSlots ) yield (m,p,d_real,phi_m,nSlots)
def _log_pohlig_hellman(g, a, p): """ For internal use only - see log() for other purposes. Finds the discreet logarithm log_g(a) modulo p using the Pohlig-Hellman algorithm. """ o = order(g, p) fact = factorize(o) rems, mods = [], [] for q, e in fact: d = q ** e ne = o // d rem = _log_pohlig_hellman_pp(pow(g, ne, p), pow(a, ne, p), p, q, e) rems.append(rem) mods.append(d) return solve_mult_cong(zip(rems, mods))[0]
def pow_mod(a, e, n): """ Computes a^e mod n using Carmichael's function. """ fact = factorize(n) rems, mods = [], [] for p, exp in fact: d = p ** exp mods.append(d) if a % p == 0 and e > exp: rems.append(0) else: rems.append(pow(a % d, e % carmichael(d), d)) if len(rems) > 1: R = solve_mult_cong(zip(rems, mods))[0] else: R = rems[0] return R
def solve_quadr(a, b, c, n): """ Finds the solutions of the congruence aX^2 + bX + c = 0 (mod n). """ rems, mods = [], [] for q, e in factorize(n): rem = _solve_quadr_pk(a, b, c, q, e) if not rem: rems = [] break rems.append(rem) mods.append(q ** e) if len(rems) > 1: sols = [solve_mult_cong(zip(rs, mods))[0] for rs in product(*rems)] elif len(rems) == 1: sols = rems[0] else: sols = [] return sols
def root(n, a, m): """ Finds the solutions of the equation x^n = a (mod m). """ rems, mods = [], [] for q, e in factorize(m): if q == 2 and e > 2: r = _root_p2(n, a, e) else: r = _root_pp(n, a, q, e) if r is None: return rems.append(r) mods.append(q ** e) if len(rems) > 1: sols = [solve_mult_cong(zip(rs, mods))[0] for rs in product(*rems)] else: sols = rems[0] return sols
def parsePrimeRange(rangeStr): """ Parses the ranges and numbers given to it, but only of primes. Args: rangeStr: a string e.g. '2-5,7,10-11' Returns: A list of prime numbers to try out. Usage: >>> parsePrimeRange('2-5,7,10-11') [2, 3, 5, 7, 11] """ p_prime = list(\ filter(lambda x: numth.factorize(x)[x] == 1, \ parseRange(rangeStr))\ ) if len(p_prime) == 0: raise argparse.ArgumentTypeError("No primes found in range given.") return p_prime
if __name__ == "__main__": parser = argparse.ArgumentParser(description="") parser.add_argument("-p", type=parsePrimeRange, default="2", help="Prime number for plaintext space.") parser.add_argument("-d", type=parseRange, default="1", help="How many coefficients in a slot (the order of p in ZmStar).") parser.add_argument("--self-test", action="store_true", help="Run doctest on itself.") args = parser.parse_args() # Run doctest instead of normal operation if args.self_test: import doctest print(doctest.testmod()) exit(0) # Generator for m_max(s) m_maxs = [ (p**d - 1, p, d) for p in args.p for d in args.d ] # SolnObj just a 5-tuple ( m, p, d, phi_m, nSlots ) solns = sorted( algebras(m_maxs) ) m_max = max(m_maxs) minWidths = [ math.ceil(math.log10(t)) for t in m_max ] minWidths.append( math.ceil(math.log10(numth.phi(numth.factorize(m_max[0]))))) minWidths.append(minWidths[-1]) printTable( ('m','p','d','phi(m)','nSlots'), solns, minWidths)