Path: blob/master/attacks/factorization/coppersmith.py
2589 views
import logging1import os2import sys3from math import ceil4from math import log5from math import pi6from math import sqrt78from sage.all import ZZ9from sage.all import Zmod1011path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(os.path.abspath(__file__)))))12if sys.path[1] != path:13sys.path.insert(1, path)1415from shared.small_roots import coron_direct16from shared.small_roots import herrmann_may_multivariate17from shared.small_roots import howgrave_graham181920def factorize_p(N, partial_p, beta=0.5, epsilon=0.125, m=None, t=None):21"""22Recover the prime factors from a modulus using Coppersmith's method and bits of one prime factor p are known.23More information: May A., "New RSA Vulnerabilities Using Lattice Reduction Methods" (Section 3.2)24More information: Herrmann M., May A., "Solving Linear Equations Modulo Divisors: On Factoring Given Any Bits" (Section 3 and 4)25:param N: the modulus26:param partial_p: the partial prime factor p (PartialInteger)27:param beta: the parameter beta (default: 0.5)28:param epsilon: the parameter epsilon (default: 0.125)29:param m: the number of normal shifts to use (default: automatically computed using beta and epsilon)30:param t: the number of additional shifts to use (default: automatically computed using beta and epsilon)31:return: a tuple containing the prime factors, or None if the factors could not be found32"""33n = partial_p.unknowns34assert n > 035if n == 1:36m = ceil(max(beta ** 2 / epsilon, 7 * beta)) if m is None else m37t = int((1 / beta - 1) * m) if t is None else t38small_roots = howgrave_graham.modular_univariate39elif n == 2:40m = ceil((3 * beta * (1 + sqrt(1 - beta))) / epsilon) if m is None else m41t = int((1 - sqrt(1 - beta)) * m) if t is None else t42small_roots = herrmann_may_multivariate.modular_multivariate43else:44m = ceil((n * (1 / pi * (1 - beta) ** (-0.278465) - beta * log(1 - beta))) / epsilon) if m is None else m45t = int((1 - (1 - beta) ** (1 / n)) * m) if t is None else t46small_roots = herrmann_may_multivariate.modular_multivariate4748x = Zmod(N)[tuple(f"x{i}" for i in range(n))].gens()49f = partial_p.sub(x)50X = partial_p.get_unknown_bounds()51logging.info(f"Trying {m = }, {t = }...")52for roots in small_roots(f, N, m, t, X):53p = partial_p.sub(roots)54if 1 < p < N and N % p == 0:55return p, N // p5657return None585960def factorize_pq(N, partial_p, partial_q, k=None):61"""62Recover the prime factors from a modulus using Coppersmith's method and bits of both prime factors p and q are known.63:param N: the modulus64:param partial_p: the partial prime factor p (PartialInteger)65:param partial_q: the partial prime factor q (PartialInteger)66:param k: the number of shifts to use for Coron's method, must be set if the total number of unknown components is two (default: None)67:return: a tuple containing the prime factors, or None if the factors could not be found68"""69np = partial_p.unknowns70nq = partial_q.unknowns71assert np > 0 and nq > 07273x = ZZ[tuple(f"x{i}" for i in range(np + nq))].gens()74f = partial_p.sub(x[:np]) * partial_q.sub(x[np:]) - N75Xp = partial_p.get_unknown_bounds()76Xq = partial_q.get_unknown_bounds()7778if np == 1 and nq == 1:79assert k is not None, "k must be set if the total number of unknown components is two."80logging.info(f"Trying {k = }...")81for x0, x1 in coron_direct.integer_bivariate(f, k, Xp[0], Xq[0]):82p = partial_p.sub([x0])83q = partial_q.sub([x1])84if p * q == N:85return p, q86else:87# TODO: Jochemsz-May multivariate integer roots?88# Or "Factoring RSA Modulus with Known Bits from Both p and q: A Lattice Method"?89pass9091return None929394