Path: blob/master/attacks/rsa/known_crt_exponents.py
2589 views
import logging1import os2import sys3from math import ceil4from math import gcd5from math import log67from sage.all import Zmod8from sage.all import is_prime910path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(os.path.abspath(__file__)))))11if sys.path[1] != path:12sys.path.insert(1, path)1314from shared import ceil_div15from shared.small_roots import herrmann_may16from shared.small_roots import howgrave_graham171819def _get_possible_primes(e, d):20logging.debug(f"Looking for possible primes for {e = }, {d = }")21mul = e * d - 122for k in range(3, e):23if mul % k == 0:24p = (mul // k) + 125if is_prime(p):26yield p272829def attack(e_start, e_end, N=None, dp=None, dq=None, p_bit_length=None, q_bit_length=None):30"""31Generates possible prime factors for a modulus, if d_p and/or d_q are known.32More information: Campagna M., Sethi A., "Key Recovery Method for CRT Implementation of RSA"33:param e_start: the start value of the public exponent (inclusive)34:param e_end: the end value of the public exponent (exclusive)35:param N: the modulus, will be used to check the factors if not None (default: None)36:param dp: the d exponent for p, will be used to generate possible factors for p if not None (default: None)37:param dq: the d exponent for q, will be used to generate possible factors for q if not None (default: None)38:param p_bit_length: the bit length of p, will be used to check possible factors for p if not None (default: None)39:param q_bit_length: the bit length of q, will be used to check possible factors for q if not None (default: None)40:return: a generator generating tuples containing possible prime factors41"""42assert not (dp is None and dq is None), "At least one of the CRT private exponents should be known."4344if dp is not None and dq is not None:45for e in range(e_start, e_end, 2):46for p in _get_possible_primes(e, dp):47for q in _get_possible_primes(e, dq):48if (N is None or p * q == N) and (p_bit_length is None or p.bit_length() == p_bit_length) and (q_bit_length is None or q.bit_length() == q_bit_length):49yield p, q5051return None5253if dp is not None:54for e in range(e_start, e_end, 2):55for p in _get_possible_primes(e, dp):56if p_bit_length is None or p.bit_length() == p_bit_length:57if N is None:58yield p59elif N % p == 0:60yield p, N // p6162return None6364if dq is not None:65for e in range(e_start, e_end, 2):66for q in _get_possible_primes(e, dq):67if q_bit_length is None or q.bit_length() == q_bit_length:68if N is None:69yield q70elif N % q == 0:71yield q, N // q7273return None747576def _factor_msb(N, e, dpM, dp_unknown_lsb, k, m, t):77logging.info(f"Trying {k = }")78g = gcd(e, k * N)79x = Zmod(k * N)["x"].gen()80f = x + (e * dpM * 2 ** dp_unknown_lsb + k - 1) * pow(e, -1, k // g * N)81X = 2 ** dp_unknown_lsb82logging.info(f"Trying {m = }, {t = }...")83for x0, in howgrave_graham.modular_univariate(f, k * N, m, t, X):84dp = int(f(x0))85p = gcd(dp, N)86if N % p == 0:87return p, N // p888990def _factor_lsb(N, e, dpL, dpL_bit_length, dp_unknown_msb, k, m, t):91logging.info(f"Trying {k = }")92g = gcd(2 ** dpL_bit_length * e, k * N)93x = Zmod(k * N)["x"].gen()94f = x + (e * dpL + k - 1) * pow(2 ** dpL_bit_length * e, -1, k // g * N)95X = 2 ** dp_unknown_msb96logging.info(f"Trying {m = }, {t = }...")97for x0, in howgrave_graham.modular_univariate(f, k * N, m, t, X):98dp = int(f(x0))99p = gcd(dp, N)100if N % p == 0:101return p, N // p102103104def attack_partial(N, e, partial_dp, partial_dq, m=None, t=None, check_bounds=True):105"""106Recovers the prime factors from a modulus if the most or least significant bits of dp and dq are known.107More information: Alexander M., Julian N., Santanu S., "Approximate Divisor Multiples - Factoring with Only a Third of the Secret CRT-Exponents"108:param N: the modulus109:param e: the exponent110:param partial_dp: d mod (p - 1) (PartialInteger)111:param partial_dq: d mod (q - 1) (PartialInteger)112:param m: the parameter m for small roots (default: automatically calculated using beta = 0.5 and epsilon = 0.125)113:param t: the parameter t for small roots (default: automatically calculated using beta = 0.5 and epsilon = 0.125)114:param check_bounds: perform bounds check (default: True)115:return: a tuple containing the prime factors, or None if the factors were not found116"""117alpha = log(e, N)118dp_bit_length = partial_dp.bit_length119dq_bit_length = partial_dq.bit_length120assert dp_bit_length == dq_bit_length, "dp and dq should be of equal bit length."121122beta = 0.5123epsilon = 0.125124m = ceil(max(beta ** 2 / epsilon, 7 * beta)) if m is None else m125t = int((1 / beta - 1) * m) if t is None else t126127dpM, dpM_bit_length = partial_dp.get_known_msb()128dqM, dqM_bit_length = partial_dq.get_known_msb()129if dpM_bit_length > 0 and dqM_bit_length > 0:130# Section 3.1.131dp_unknown_lsb = partial_dp.get_unknown_lsb()132dq_unknown_lsb = partial_dq.get_unknown_lsb()133delta = log(max(2 ** dp_unknown_lsb, 2 ** dq_unknown_lsb), N)134assert not check_bounds or delta < min(1 / 4 + alpha, 1 / 2 - 2 * alpha), f"Bounds check failed ({delta} < {min(1 / 4 + alpha, 1 / 2 - 2 * alpha)})."135136x = Zmod(e)["x"].gen()137A_ = ceil_div(2 ** (dp_unknown_lsb + dq_unknown_lsb) * e ** 2 * dpM * dqM, N)138139# First case.140f = x ** 2 - (1 - A_ * (N - 1)) * x + A_141for k, _ in f.roots():142if k == 0:143continue144145factors = _factor_msb(N, e, dpM, dp_unknown_lsb, int(k), m, t)146if factors:147return factors148factors = _factor_msb(N, e, dqM, dq_unknown_lsb, int(k), m, t)149if factors:150return factors151152# Second case.153f = x ** 2 + (1 - A_ * (N - 1) + e) * x + A_154for k, _ in f.roots():155if k == 0:156continue157158factors = _factor_msb(N, e, dpM, dp_unknown_lsb, int(k), m, t)159if factors:160return factors161factors = _factor_msb(N, e, dqM, dq_unknown_lsb, int(k), m, t)162if factors:163return factors164165dpL, dpL_bit_length = partial_dp.get_known_lsb()166dqL, dqL_bit_length = partial_dq.get_known_lsb()167if dpL_bit_length > 0 and dqL_bit_length > 0:168# Section 3.2.169dp_unknown_msb = partial_dp.get_unknown_msb()170dq_unknown_msb = partial_dq.get_unknown_msb()171assert dpL_bit_length == dqL_bit_length, "dp and dq LSB should be of equal bit length."172delta = log(max(2 ** dp_unknown_msb, 2 ** dq_unknown_msb), N)173assert not check_bounds or delta < min(1 / 4 + alpha, 1 / 2 - 2 * alpha), f"Bounds check failed ({delta} < {min(1 / 4 + alpha, 1 / 2 - 2 * alpha)})."174175i = dpL_bit_length176pr = Zmod(2 ** i * e)["x, y"]177x, y = pr.gens()178A = -e ** 2 * dpL * dqL + e * dpL + e * dqL - 1179f = (N - 1) * x * y - (e * dqL - 1) * x - (e * dpL - 1) * y + A180g = f * pow((N - 1) // gcd(N - 1, e * 2 ** i), -1, 2 ** i * e)181for k, l in herrmann_may.modular_bivariate(g, 2 ** i * e, 2, 2, e, e):182if k == 0 or l == 0:183continue184185factors = _factor_lsb(N, e, dpL, dpL_bit_length, dp_unknown_msb, int(k), m, t)186if factors:187return factors188factors = _factor_lsb(N, e, dqL, dqL_bit_length, dq_unknown_msb, int(l), m, t)189if factors:190return factors191192return None193194195