def factorize(n, M, m, t): """ Recovers the prime factors from a modulus using the ROCA method. More information: Nemec M. et al., "The Return of Coppersmith’s Attack: Practical Factorization of Widely Used RSA Moduli" :param n: the modulus :param M: the primorial used to generate the primes :param m: the m parameter for Coppersmith's method :param t: the t parameter for Coppersmith's method :return: a tuple containing the prime factors """ logging.debug("Generating M'...") M_ = _greedy_find_M_(n, M) ZmodM_ = Zmod(M_) e = ZmodM_(65537) c_ = ZmodM_(n).log(e) ord_ = e.multiplicative_order() x = Zmod(n)["x"].gen() X = int(2 * n**0.5 // M_) logging.debug("Starting exhaustive a' search...") for a_ in range(c_ // 2, (c_ + ord_) // 2 + 1): f = M_ * x + int(e**a_) for k_ in modular_univariate(f, n, m, t, X): p = int(f(k_)) if n % p == 0: return p, n // p
def build_matrix(P, M, c=1000): factors_M = factor(M) rows = [] # add logarithms for p in P: row = [] for q, e in factors_M: row.extend(Zmod(q**e)(p).generalised_log() ) # generalised_log() uses unit_gens() generators row = [c * x for x in row] # multiply logs by a large constant to help LLL rows.append(row) height = len(rows) width = len(rows[0]) # add unit matrix for i, row in enumerate(rows): row.extend([1 if j == i else 0 for j in range(0, height)]) # add group orders generators_M = [g for q, e in factors_M for g in Zmod(q**e).unit_gens()] for i, g in enumerate(generators_M): rows.append([ g.multiplicative_order() * c if j == i else 0 for j in range(0, width + height) ]) return Matrix(rows)
def roca(n): keySize = n.bit_length() if keySize <= 960: M_prime = 0x1B3E6C9433A7735FA5FC479FFE4027E13BEA m = 5 elif 992 <= keySize <= 1952: M_prime = ( 0x24683144F41188C2B1D6A217F81F12888E4E6513C43F3F60E72AF8BD9728807483425D1E ) m = 4 print("Have you several days/months to spend on this ?") elif 1984 <= keySize <= 3936: M_prime = 0x16928DC3E47B44DAF289A60E80E1FC6BD7648D7EF60D1890F3E0A9455EFE0ABDB7A748131413CEBD2E36A76A355C1B664BE462E115AC330F9C13344F8F3D1034A02C23396E6 m = 7 print("You'll change computer before this scripts ends...") elif 3968 <= keySize <= 4096: print("Just no.") return None else: print("Invalid key size: {}".format(keySize)) return None beta = 0.1 a3 = Zmod(M_prime)(n).log(65537) order = Zmod(M_prime)(65537).multiplicative_order() inf = a3 >> 1 sup = (a3 + order) >> 1 # Upper bound for the small root x0 XX = floor(2 * n ** 0.5 / M_prime) invmod_Mn = inverse_mod(M_prime, n) # Create the polynom f(x) F = PolynomialRing(Zmod(n), implementation="NTL", names=("x",)) (x,) = F._first_ngens(1) # Search 10 000 values at a time, using multiprocess # too big chunks is slower, too small chunks also chunk_size = 10000 for inf_a in range(inf, sup, chunk_size): # create an array with the parameter for the solve function inputs = [ ((M_prime, n, a, m, XX, invmod_Mn, F, x, beta), {}) for a in range(inf_a, inf_a + chunk_size) ] # the sage builtin multiprocessing stuff from sage.parallel.multiprocessing_sage import parallel_iter from multiprocessing import cpu_count for k, val in parallel_iter(cpu_count(), solve, inputs): if val: p = val[0] q = val[1] print("{}:{}".format(p, q)) return val return "Fail"
def attack(n, e, c, bitsize, msb_known, msb, lsb_known, lsb, m_start=1): """ Recovers the plaintext from the ciphertext if some bits of the plaintext are known, using Coppersmith's method. :param n: the modulus :param e: the public exponent (should be "small": 3, 5, or 7 work best) :param c: the encrypted message :param bitsize: the amount of bits of the plaintext :param msb_known: the amount of known most significant bits of the plaintext :param msb: the known most significant bits of the plaintext :param lsb_known: the amount of known least significant bits of the plaintext :param lsb: the known least significant bits of the plaintext :param m_start: the m value to start at for the Howgrave-Graham small roots method (default: 1) :return: the plaintext """ x = Zmod(n)["x"].gen() f = (msb * 2**(bitsize - msb_known) + x * 2**lsb_known + lsb)**e - c bound = 2**(bitsize - msb_known - lsb_known) - 1 m = m_start while True: t = m logging.debug(f"Trying m = {m}, t = {t}...") for root in modular_univariate(f, n, m, t, bound): return msb * 2**(bitsize - msb_known) + root * 2**lsb_known + lsb m += 1
def neg_fdd_div(D): s = 0 for d in divisors(D): if is_fundamental_discriminant(-d): if Zmod(d)(D / d).is_square(): s += 1 return s
def compute_max_M_(M, ord_): for p in _prime_power_divisors(M): ordp = Zmod(p)(65537).multiplicative_order() if ord_ % ordp != 0: M //= p return M
def CMorbits(D): s = sum([ 2**(len(prime_divisors(old_div(D, d)))) for d in divisors(D) if is_fundamental_discriminant(-d) and Zmod(d) (old_div(D, d)).is_square() ]) + 1 return s
def __init__(self, ec, P=None, dmap=None, order=None, pairing="weil", k=None, seed=None): self.ec = ec self.P = P self.distortion = self._deco(dmap) if dmap == None: self.distortion = self._ext if order == None: self.order = P.order() else: self.order = order self.pairing = pairing ord = self.ec.base_ring().cardinality() if k == None: k = Zmod(self.order)(ord).multiplicative_order() self.k = k random.seed(seed) self.t = random.randint(2, self.order - 1) base = FiniteField(ord**self.k, 'b') self.hom = Hom(self.ec.base_ring(), base)(base.gen()**((ord**self.k - 1) / (ord - 1))) self.ec2 = EllipticCurve(map(int, self.ec.a_invariants())).change_ring(base)
def decrypt(c, d, n): n = int(n) size = n.bit_length() // 2 c_high, c_low = c b = (c_low**2 - c_high**3) % n EC = EllipticCurve(Zmod(n), [0, b]) m_high, m_low = (EC((c_high, c_low)) * d).xy() m_high, m_low = int(m_high), int(m_low) return (m_high << size) | m_low
def compare_formulas_2a(D, k): d1 = dimension_new_cusp_forms(kronecker_character(D), k) if D < 0: D = -D d2 = RR(1 / pi * sqrt(D) * sum([ log(d) * sigma(D / d, 0) for d in divisors(D) if Zmod(d)(D / d).is_square() and is_fundamental_discriminant(-d) ])) return d1 - d2
def _polynomial_gcd_crt(a, b, modulus): gs = [] ps = [] for p, _ in factor(modulus): zmodp = Zmod(p) gs.append(_polynomial_gcd(a.change_ring(zmodp), b.change_ring(zmodp)).change_ring(ZZ)) ps.append(p) return gs[0] if len(gs) == 1 else crt(gs, ps)
def factorize(n, D): """ Recovers the prime factors from a modulus using Cheng's elliptic curve complex multiplication method. More information: Sedlacek V. et al., "I want to break square-free: The 4p - 1 factorization method and its RSA backdoor viability" :param n: the modulus :param D: the discriminant to use to generate the Hilbert polynomial :return: a tuple containing the prime factors """ assert D % 8 == 3, "D should be square-free" zmodn = Zmod(n) pr = zmodn["x"] H = pr(hilbert_class_polynomial(-D)) Q = pr.quotient(H) j = Q.gen() try: k = j * _polynomial_inverse((1728 - j).lift(), H) except ArithmeticError as err: # If some polynomial was not invertible during XGCD calculation, we can factor n. p = gcd(int(err.args[1].lc()), n) return int(p), int(n // p) E = EllipticCurve(Q, [3 * k, 2 * k]) while True: x = zmodn.random_element() logging.debug(f"Calculating division polynomial of Q{x}...") z = E.division_polynomial(n, x=Q(x)) try: d, _, _ = _polynomial_xgcd(z.lift(), H) except ArithmeticError as err: # If some polynomial was not invertible during XGCD calculation, we can factor n. p = gcd(int(err.args[1].lc()), n) return int(p), int(n // p) p = gcd(int(d), n) if 1 < p < n: return int(p), int(n // p)
def search_global_symbols(n, D): rank = 2 + n sign = 2 - n csymbols = list() # a list of canonical symbols to avoid duplicates D = (-1)**n * D symbols = all_symbols(sign, rank, D) global_symbols = [] for sym in symbols: #print sym for p in sym.keys(): prank = sum([s[1] for s in sym[p]]) v = sum([s[0] * s[1] for s in sym[p]]) Dp = D // (p**v) if prank != rank: eps = (Dp * Integer(prod([s[2] for s in sym[p]]))).kronecker(p) if p == 2: if eps == -1: eps = 3 sym[p].insert(0, [0, rank - prank, eps, 0, 0]) else: if eps == -1: for x in Zmod(p): if not x.is_square(): eps = x break sym[p].insert(0, [0, rank - prank, eps]) symbol = GenusSymbol_global_ring(MatrixSpace(ZZ, rank, rank).one()) symbol._local_symbols = [ Genus_Symbol_p_adic_ring(p, syms) for p, syms in sym.iteritems() ] symbol._signature = (2, n) #print symbol._local_symbols isglob = is_GlobalGenus(symbol) if isglob: #print "GLOBAL SYMBOL:" #print symbol._local_symbols #return symbol #for s in symbol._local_symbols: # s = s.canonical_symbol() append = True for j, s in enumerate(symbol._local_symbols): if s._prime == 2: sc = deepcopy(symbol) sc._local_symbols[j] = sc._local_symbols[ j].canonical_symbol() if csymbols.count(sc) > 0: append = False else: csymbols.append(sc) break if append: global_symbols.append(symbol) return global_symbols
def _recover_modulus_and_multiplier(polynomials, modulus_bitsize, modulus=None, multiplier=None): for combination in combinations(polynomials, 3): P0 = combination[0] P1 = combination[1] P2 = combination[2] possible_modulus = gcd(P0.resultant(P1), P1.resultant(P2), P0.resultant(P2)) if (modulus is None and possible_modulus.bit_length() == modulus_bitsize) or possible_modulus == modulus: if multiplier is None: g = _polynomial_gcd_crt(P0, _polynomial_gcd_crt(P1, P2, possible_modulus), possible_modulus) for possible_multiplier in g.change_ring(Zmod(possible_modulus)).roots(multiplicities=False): yield int(possible_modulus), int(possible_multiplier) else: yield int(possible_modulus), multiplier
def attack(n, e, c1, c2, f1, f2): """ Recovers the shared secret from two plaintext messages encrypted with the same modulus. :param n: the modulus :param e: the public exponent :param c1: the ciphertext of the first encryption :param c2: the ciphertext of the second encryption :param f1: the function encoding the shared secret into the first plaintext :param f2: the function encoding the shared secret into the second plaintext :return: the shared secret """ x = Zmod(n)["x"].gen() g1 = f1(x)**e - c1 g2 = f2(x)**e - c2 g = -_polynomial_gcd(g1, g2).monic() return g[0]
def _greedy_find_M_(n, M): ord = Zmod(M)(65537).multiplicative_order() while True: best_r = 0 best_ord_ = ord best_M_ = M for p in _prime_power_divisors(ord): ord_ = ord // p M_ = compute_max_M_(M, ord_) r = (log2(ord) - log2(ord_)) / (log2(M) - log2(M_)) if r > best_r: best_r = r best_ord_ = ord_ best_M_ = M_ if log2(best_M_) < log2(n) / 4: return M ord = best_ord_ M = best_M_
def attack(n, e, bitsize, lsb_known=0, lsb=0, delta=0.25, m_start=1): """ Recovers the prime factors if the private exponent is too small. More information: Boneh D., Durfee G., "Cryptanalysis of RSA with Private Key d Less than N^0.292" This implementation exploits knowledge of least significant bits of prime factors, if available. :param n: the modulus :param e: the public exponent :param bitsize: the amount of bits of the prime factors :param lsb_known: the amount of known least significant bits of one of the prime factors :param lsb: the known least significant bits of one of the prime factors :param delta: a predicted bound on the private exponent (d < n^delta) (default: 0.25) :param m_start: the m value to start at for the Herrmann-May small roots method (default: 1) :return: a tuple containing the prime factors """ x, y = Zmod(e)["x, y"].gens() # Use additional information about factors to speed up Boneh-Durfee p_lsb = lsb q_lsb = (pow(lsb, -1, 2**lsb_known) * (n % 2**lsb_known)) % (2**lsb_known) a = ((n >> lsb_known) + pow(2, -lsb_known, e) * (p_lsb * q_lsb - p_lsb - q_lsb + 1)) f = x * (a + y) + pow(2, -lsb_known, e) xbound = int(e**RealNumber(delta)) ybound = int(2**(bitsize - lsb_known + 1)) m = m_start while True: t = int(m * (1 - 2 * delta)) logging.debug(f"Trying m = {m}, t = {t}...") for xroot, yroot in modular_bivariate(f, e, m, t, xbound, ybound): z = xroot * (a + yroot) + pow(2, -lsb_known, e) if z % e == 0: s = (n + 1 + pow(xroot, -1, e)) % e p = ZZ["p"].gen() f = p**2 - s * p + n for proot, _ in f.roots(): proot = int(proot) if n % proot == 0: return proot, n // proot m += 1
def solve(M, n, a, m): # I need to import it in the function otherwise multiprocessing doesn't find it in its context from sage_functions import coppersmith_howgrave_univariate base = int(65537) # the known part of p: 65537^a * M^-1 (mod N) known = int(pow(base, a, M) * inverse_mod(M, n)) # Create the polynom f(x) F = PolynomialRing(Zmod(n), implementation="NTL", names=("x",)) (x,) = F._first_ngens(1) pol = x + known beta = 0.1 t = m + 1 # Upper bound for the small root x0 XX = floor(2 * n ** 0.5 / M) # Find a small root (x0 = k) using Coppersmith's algorithm roots = coppersmith_howgrave_univariate(pol, n, beta, m, t, XX) # There will be no roots for an incorrect guess of a. for k in roots: # reconstruct p from the recovered k p = int(k * M + pow(base, a, M)) if n % p == 0: return p, n // p
def attack(n, e, bitsize, lsb_known, lsb, m_start=1): """ Recovers the prime factors of a modulus and the private exponent using Coppersmith's method if part of the private exponent is known. More information: Boneh D., Durfee G., Frankel Y., "An Attack on RSA Given a Small Fraction of the Private Key Bits" :param n: the modulus :param e: the public exponent (should be "small": 3, 5, or 7 work best) :param bitsize: the amount of bits of the prime factors :param lsb_known: the amount of known least significant bits of the private exponent :param lsb: the known least significant bits of the private exponent :param m_start: the m value to start at for the Howgrave-Graham small roots method (default: 1) :return: a tuple containing the prime factors of the modulus and the private exponent """ logging.debug("Generating solutions for k candidates...") x = var("x") solutions = [] for k in range(1, e + 1): solutions += solve_mod( k * x**2 + (e * lsb - k * (n + 1) - 1) * x + k * n == 0, 2**lsb_known) x = Zmod(n)["x"].gen() bound = 2**(bitsize - lsb_known) - 1 m = m_start while True: t = m logging.debug(f"Trying m = {m}, t = {t}...") for s in solutions: p_lsb = int(s[0]) f = x * 2**lsb_known + p_lsb for root in modular_univariate(f, n, m, t, bound): p = root * 2**lsb_known + p_lsb if p != 0 and n % p == 0: q = n // p return p, q, pow(e, -1, (p - 1) * (q - 1)) m += 1
def is_global(M, r, s, return_symbol=False): r""" Test if the FiniteQuadraticModule M can be represented by a Z-lattice of signature ``(r,s)``. INPUT: -``M`` -- FiniteQuadraticModule - ``r`` -- positive integer - ``s`` -- positive integer OUTPUT: - boolean """ J = M.jordan_decomposition() symbols = {} n = r + s sig = r - s for A in J: p = A[1][0] if p not in symbols: symbols[p] = list() sym = list(A[1][1:len(A[1])]) if p == 2: if len(A[1]) == 4: sym.append(0) sym.append(0) else: if sym[3].kronecker(2) == sym[2]: det = sym[3] % 8 else: if sym[2] == -1: det = 3 else: det = 1 sym = [sym[0], sym[1], det, 1, sym[3] % 8] #print sym #if sym[1]==1: # if sym[2].kronecker(2)==sym[4].kronecker(2): # sym[2]=sym[4] # else: # return False #print p, sym symbols[p].append(sym) D = M.order() * (-1)**s for p in symbols.keys(): prank = sum([sym[1] for sym in symbols[p]]) v = sum([sym[0] * sym[1] for sym in symbols[p]]) Dp = D // (p**v) if prank != n: eps = (Dp * Integer(prod([sym[2] for sym in symbols[p]]))).kronecker(p) if p == 2: if eps == -1: eps = 3 symbols[p].append([0, n - prank, eps, 0, 0]) else: if eps == -1: for x in Zmod(p): if not x.is_square(): eps = x break symbols[p].append([0, n - prank, eps]) symbol = GenusSymbol_global_ring(MatrixSpace(ZZ, r + s, r + s).one()) symbol._local_symbols = [ Genus_Symbol_p_adic_ring(p, syms) for p, syms in symbols.items() ] symbol._signature = (r, s) #print r,s, symbol._local_symbols isglob = is_GlobalGenus(symbol) if return_symbol: return symbol, isglob else: return isglob
def compare_formulas_2(D, k): d1 = RR(abs(D)) / RR(6) if D < 0: D = -D s1 = RR( sqrt(abs(D)) * sum([ log(d) for d in divisors(D) if is_fundamental_discriminant(-d) and kronecker(-d, D / d) == 1 ])) d2 = RR((2 / (sqrt(3) * pi)) * s1) return d1 - d2, d2, RR(2 * sqrt(D) * log(D) / pi) A3 = lambda N: sum( [kronecker(-N, x) for x in Zmod(N) if x**2 + x + Zmod(N)(1) == 0]) def compare_formulas_2a(D, k): d1 = dimension_new_cusp_forms(kronecker_character(D), k) if D < 0: D = -D d2 = RR(1 / pi * sqrt(D) * sum([ log(d) * sigma(D / d, 0) for d in divisors(D) if Zmod(d)(D / d).is_square() and is_fundamental_discriminant(-d) ])) return d1 - d2 def compare_formulas_3(D, k): d1 = dimension_cusp_forms(kronecker_character(D), k)
from sage.all import EllipticCurve, GF, Zmod, ZZ import socketserver import os import signal from Crypto.Util.number import bytes_to_long from hashlib import sha256 # Dual_EC_Drbg parameters taken from: # https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-90a.pdf # Section A.1.1 EC = EllipticCurve( GF(0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff), [-3, 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b]) n = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 assert EC.cardinality() == n Zn = Zmod(n) G = EC((0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5)) P = G Q = EC((0xc97445f45cdef9f0d3e05e1e585fc297235b82b5be8ff3efca67c59852018192, 0xb28ef557ba31dfcbdd21ac46e2a91e3c304f44cb87058ada2cb815151e610046)) class DualEcDrbg(object): """ Dual Elliptic Curve Deterministic Random Bit Generator """ def __init__(self, seed): self.s = ZZ(bytes_to_long(seed)) def next_bits(self):
def search_global_symbols(n, D): rank = 2 + n sign = 2 - n csymbols = list() # a list of canonical symbols to avoid duplicates symbols = list() #print D D = (-1)**n * D fac = Integer(D).factor() symbols = list() for p, v in fac: psymbols = list() parts = partitions(v) Dp = D // (p**v) for vs in parts: #print "partition:", vs l = list() # list of p-symbols corresponding to the partition vs if len(vs) <= rank: exponents = Set(list(vs)) # now we set up a list ll for each vv in the partition vs # that contains an entry for each possibility # and then update l with ll (see below) if p == 2: for vv in exponents: mult = vs.count(vv) ll = list() for t in [0, 1]: # even(0) or odd(1) type for det in [1, 3, 5, 7]: # the possible determinants if mult % 2 == 0 and t == 0: ll.append([vv, mult, det, 0, 0]) if mult == 1: odds = [det] elif mult == 2: if det in [1, 7]: odds = [0, 2, 6] else: odds = [2, 4, 6] else: odds = [ o for o in range(8) if o % 2 == mult % 2 ] for oddity in odds: if t == 1: ll.append([vv, mult, det, 1, oddity]) #else: #ll.append([vv,1,det,0,0]) #if mult % 2 == 0 and mult>2: # for x in range(1,Integer(mult)/Integer(2)): # if mult-2*x==2 and det in [1,7] and oddity not in [0,2,6]: # continue # elif mult-2*x==2 and det in [3,5] and oddity not in [2,4,6]: # continue # ll.append([[vv,2*x,det,0,0],[vv,mult-2*x,det,1,oddity]]) #print "ll:\n",ll if len(l) == 0: for t in ll: if type(t[0]) == list: l.append({p: t}) else: l.append({p: [t]}) else: newl = list() for t in ll: for sym in l: newsym = deepcopy(sym) #print newsym if type(t[0]) == list: newsym[p] = newsym[p] + t else: newsym[p].append(t) #print newsym newl.append(newsym) #print l l = newl #print "l:\n",l else: for vv in exponents: ll = [[vv, vs.count(vv), 1], [vv, vs.count(vv), -1]] if len(l) == 0: for t in ll: l.append({p: [t]}) else: newl = list() for t in ll: for sym in l: sym[p].append(t) newl.append(sym) l = newl #print "l=\n",l #print "psymbols=\n",psymbols #print psymbols+l psymbols = psymbols + l if len(symbols) == 0: symbols = psymbols else: symbols_new = list() for sym in symbols: for psym in psymbols: newsym = deepcopy(sym) newsym.update(psym) symbols_new.append(newsym) symbols = symbols_new global_symbols = [] for sym in symbols: #print sym for p in sym.keys(): prank = sum([s[1] for s in sym[p]]) v = sum([s[0] * s[1] for s in sym[p]]) Dp = D // (p**v) if prank != rank: eps = (Dp * Integer(prod([s[2] for s in sym[p]]))).kronecker(p) if p == 2: if eps == -1: eps = 3 sym[p].insert(0, [0, rank - prank, eps, 0, 0]) else: if eps == -1: for x in Zmod(p): if not x.is_square(): eps = x break sym[p].insert(0, [0, rank - prank, eps]) symbol = GenusSymbol_global_ring(MatrixSpace(ZZ, rank, rank).one()) symbol._local_symbols = [ Genus_Symbol_p_adic_ring(p, syms) for p, syms in sym.items() ] symbol._signature = (2, n) #print symbol._local_symbols isglob = is_GlobalGenus(symbol) if isglob: #print "GLOBAL SYMBOL:" #print symbol._local_symbols #return symbol #for s in symbol._local_symbols: # s = s.canonical_symbol() append = True for j, s in enumerate(symbol._local_symbols): if s._prime == 2: sc = deepcopy(symbol) sc._local_symbols[j] = sc._local_symbols[ j].canonical_symbol() if csymbols.count(sc) > 0: append = False else: csymbols.append(sc) break if append: global_symbols.append(symbol) return global_symbols