Path: blob/master/attacks/rsa/wiener_attack_lattice.py
2589 views
import logging1import os2import sys3from itertools import combinations4from math import isqrt5from math import prod67from sage.all import RR8from sage.all import ZZ9from sage.all import matrix1011path = 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 attacks.factorization import known_phi16from shared.lattice import shortest_vectors17from shared.small_roots import aono18from shared.small_roots import reduce_lattice192021def attack(N, e):22"""23Recovers the prime factors of a modulus and the private exponent if the private exponent is too small.24More information: Nguyen P. Q., "Public-Key Cryptanalysis"25:param N: the modulus26:param e: the public exponent27:return: a tuple containing the prime factors and the private exponent, or None if the private exponent was not found28"""29s = isqrt(N)30L = matrix(ZZ, [[e, s], [N, 0]])3132for v in shortest_vectors(L):33d = v[1] // s34k = abs(v[0] - e * d) // N35d = abs(d)36if pow(pow(2, e, N), d, N) != 2:37continue3839phi = (e * d - 1) // k40factors = known_phi.factorize(N, phi)41if factors:42return *factors, int(d)4344return None454647# Construct R_{u, v} for a specific monomial.48def _construct_relation(N, monomial, x):49vars = monomial.variables()50l = [x[i] for i in range(x.index(vars[-1]) + 1)]51R = 152u = 053v = 054i = len(vars)55for var in vars:56if var != x[0] and var < l[0] and len(l) >= 2 * i:57# Guo equation58R *= l[0] - var59l.pop(0)60v += 161else:62# Wiener equation63R *= var - N64u += 16566l.remove(var)67i -= 16869return R, u, v707172def attack_multiple_exponents_1(N, e, alpha):73"""74Recovers the prime factors of a modulus given multiple public exponents with small corresponding private exponents.75More information: Howgrave-Graham N., Seifert J., "Extending Wiener’s Attack in the Presence of Many Decrypting Exponents"76:param N: the modulus77:param e: the public exponent78:param alpha: the bound on the private exponents (i.e. d < N^alpha)79:return: a tuple containing the prime factors, or None if the prime factors were not found80"""81n = len(e)82pr = ZZ[",".join(f"x{i}" for i in range(n))]83x = pr.gens()8485monomials = [1]86for i, xi in enumerate(x):87monomials.append(xi)88for j in range(i):89for comb in combinations(x[:i], j + 1):90monomials.append(prod(comb) * xi)9192L = matrix(ZZ, len(monomials))93exp_a = [n]94exp_b = [0]95for col, monomial in enumerate(monomials):96if col == 0:97L[0, 0] = 198continue99100R, u, v = _construct_relation(N, monomial, x)101for row, monomial in enumerate(monomials):102if row == 0:103L[0, col] = R.constant_coefficient()104else:105L[row, col] = R.monomial_coefficient(monomial) * monomial(*e)106107exp_a.append(n - v)108exp_b.append(u / 2)109110max_a = max(exp_a)111max_b = max(exp_b)112D = matrix(ZZ, len(monomials))113for i, (a, b) in enumerate(zip(exp_a, exp_b)):114D[i, i] = int(RR(N) ** ((max_a - a) * alpha + (max_b - b)))115116L = L * D117L_ = reduce_lattice(L)118b = L.solve_left(L_[0])119phi = round(b[1] / b[0] * e[0])120return known_phi.factorize(N, phi)121122123def attack_multiple_exponents_2(N, e, d_bit_length, m=1):124"""125Recovers the prime factors of a modulus given multiple public exponents with small corresponding private exponents.126More information: Aono Y., "Minkowski sum based lattice construction for multivariate simultaneous Coppersmith’s technique and applications to RSA" (Section 4)127:param N: the modulus128:param e: the public exponent129:param d_bit_length: the bit length of the private exponents130:param m: the m value to use for the small roots method (default: 1)131:return: a tuple containing the prime factors, or None if the prime factors were not found132"""133l = len(e)134assert len(set(e)) == l, "All public exponents must be distinct"135assert l >= 1, "At least one public exponent is required."136137pr = ZZ[",".join(f"x{i}" for i in range(l)) + ",y"]138gens = pr.gens()139x = gens[:-1]140y = gens[-1]141F = [-1 + x[k] * (y + N) for k in range(l)]142X = [2 ** d_bit_length for _ in range(l)]143Y = 3 * isqrt(N)144logging.info(f"Trying {m = }...")145for roots in aono.integer_multivariate(F, e, m, X + [Y]):146phi = roots[y] + N147factors = known_phi.factorize(N, phi)148if factors:149return factors150151return None152153154