Path: blob/master/attacks/rsa/partial_key_exposure.py
2589 views
import logging1import os2import sys3from math import ceil4from math import gcd5from math import sqrt67from sage.all import QQ8from sage.all import RR9from sage.all import ZZ10from sage.all import Zmod11from sage.all import is_prime1213path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(os.path.abspath(__file__)))))14if sys.path[1] != path:15sys.path.insert(1, path)1617from attacks.factorization import known_phi18from shared.hensel import hensel_roots19from shared.small_roots import blomer_may20from shared.small_roots import ernst21from shared.small_roots import howgrave_graham222324def _bdf_corollary_1(e, f, N, m, t, X):25for x0, in howgrave_graham.modular_univariate(f, N, m, t, X):26p = int(f(x0))27if 1 < p < N and N % p == 0:28q = N // p29phi = (p - 1) * (q - 1)30yield p, q, pow(e, -1, phi)313233def _bdf_theorem_6(N, e, d_bit_length, d1, d1_bit_length):34d0 = d1 << (d_bit_length - d1_bit_length)35k_ = (e * d0 - 1) // N36logging.info("Generating solutions for k candidates...")37for k in range(k_ - 40, k_ + 40):38yield d0, k394041def _bdf_3(N, e, d_bit_length, d0, d0_bit_length, r, m, t):42n = N.bit_length()43logging.info(f"Trying {m = }, {t = }...")44p = ZZ["p"].gen()45x = Zmod(N)["x"].gen()46X = int(2 * RR(N) ** (1 / 2) / r) # Equivalent to 2^(n / 2 + 1) / r47logging.info("Generating solutions for k candidates...")48for k in range(1, e):49f = k * p ** 2 + (e * d0 - (1 + k * (N + 1))) * p + k * N50for p0 in hensel_roots(f, 2, d0_bit_length):51f = x * r + p052for p_, q_, d_ in _bdf_corollary_1(e, f, N, m, t, X):53return p_, q_, d_5455return None565758def _bdf_4_1(N, e, d_bit_length, d1, d1_bit_length, m, t):59logging.info(f"Trying {m = }, {t = }...")60p = Zmod(e)["p"].gen()61x = Zmod(N)["x"].gen()62X = int(2 * RR(N) ** (1 / 2) / e) # Equivalent to 2^(n / 2 + 1) / e63for _, k in _bdf_theorem_6(N, e, d_bit_length, d1, d1_bit_length):64f = k * p ** 2 - (1 + k * (N + 1)) * p + k * N65for p0 in f.roots(multiplicities=False):66f = x * e + int(p0)67for p_, q_, d_ in _bdf_corollary_1(e, f, N, m, t, X):68return p_, q_, d_6970return None717273def _bdf_4_2(N, e, d_bit_length, d1, d1_bit_length):74for d0, k in _bdf_theorem_6(N, e, d_bit_length, d1, d1_bit_length):75if gcd(e, k) != 1:76continue7778d1 = pow(e, -1, k)79for v in range(ceil(e / k) + 1):80d2 = int(QQ(d0) / k + v - QQ(d1) / k)81d = k * d2 + d182if pow(pow(2, e, N), d, N) == 2:83phi = (e * d - 1) // k84factors = known_phi.factorize(N, phi)85if factors:86return *factors, d8788return None899091def _bdf_4_3(N, e, d_bit_length, d0, d0_bit_length, d1, d1_bit_length, r, m, t):92logging.info(f"Trying {m = }, {t = }...")93p = ZZ["p"].gen()94x = Zmod(N)["x"].gen()95X = int(2 * RR(N) ** (1 / 2) / r) # Equivalent to 2^(n / 2 + 1) / r96for _, k in _bdf_theorem_6(N, e, d_bit_length, d1, d1_bit_length):97f = k * p ** 2 + (e * d0 - (1 + k * (N + 1))) * p + k * N98for p0 in hensel_roots(f, 2, d0_bit_length):99f = x * r + p0100for p_, q_, d_ in _bdf_corollary_1(e, f, N, m, t, X):101return p_, q_, d_102103return None104105106def _bm_4(N, e, d_bit_length, d1, d1_bit_length, m, t):107d_ = d1 << (d_bit_length - d1_bit_length)108k_ = (e * d_ - 1) // (N + 1)109110x, y, z = ZZ["x", "y", "z"].gens()111f = e * x + (k_ + y) * z + e * d_ - 1112X = 2 ** (d_bit_length - d1_bit_length) # Equivalent to N^delta113Y = int(4 * e / RR(N) ** (1 / 2)) # Equivalent to 4N^(alpha - 1 / 2)114Z = int(3 * RR(N) ** (1 / 2))115logging.info(f"Trying {m = }, {t = }...")116for x0, y0, z0 in blomer_may.modular_trivariate(f, N, m, t, X, Y, Z):117d = d_ + x0118phi = N - z0119if pow(pow(2, e, N), d, N) == 2:120factors = known_phi.factorize(N, phi)121if factors:122return *factors, d123124return None125126127def _bm_6(N, e, d_bit_length, d0, d0_bit_length, M, m, t):128y, z = ZZ["y", "z"].gens()129f = y * (N - z) - e * d0 + 1130Y = e # Equivalent to N^alpha131Z = int(3 * RR(N) ** (1 / 2))132logging.info(f"Trying {m = }, {t = }...")133for y0, z0 in blomer_may.modular_bivariate(f, e * M, m, t, Y, Z):134phi = N - z0135d = pow(e, -1, phi)136if pow(pow(2, e, N), d, N) == 2:137factors = known_phi.factorize(N, phi)138if factors:139return *factors, d140141return None142143144def _ernst_4_1_1(N, e, d_bit_length, d1, d1_bit_length, m, t):145d_ = d1 << (d_bit_length - d1_bit_length)146R = e * d_ - 1147148x, y, z = ZZ["x", "y", "z"].gens()149f = e * x - N * y + y * z + R150X = 2 ** (d_bit_length - d1_bit_length) # Equivalent to N^delta151Y = 2 ** d_bit_length # Equivalent to N^beta152Z = int(3 * RR(N) ** (1 / 2))153W = N * Y154logging.info(f"Trying {m = }, {t = }...")155for x0, y0, z0 in ernst.integer_trivariate_1(f, m, t, W, X, Y, Z):156d = d_ + x0157phi = N - z0158if pow(pow(2, e, N), d, N) == 2:159factors = known_phi.factorize(N, phi)160if factors:161return *factors, d162163return None164165166def _ernst_4_1_2(N, e, d_bit_length, d1, d1_bit_length, m, t):167d_ = d1 << (d_bit_length - d1_bit_length)168k_ = (e * d_ - 1) // N169R = e * d_ - 1 - k_ * N170171x, y, z = ZZ["x", "y", "z"].gens()172f = e * x - N * y + y * z + k_ * z + R173X = 2 ** (d_bit_length - d1_bit_length) # Equivalent to N^delta174Y = 4 * int(max(2 ** (d_bit_length - d1_bit_length), 2 ** d_bit_length / RR(N) ** (1 / 2))) # Equivalent to 4N^max(delta, beta - 1 / 2)175Z = int(3 * RR(N) ** (1 / 2))176W = N * Y177logging.info(f"Trying {m = }, {t = }...")178for x0, y0, z0 in ernst.integer_trivariate_2(f, m, t, W, X, Y, Z):179d = d_ + x0180phi = N - z0181if pow(pow(2, e, N), d, N) == 2:182factors = known_phi.factorize(N, phi)183if factors:184return *factors, d185186return None187188189def _ernst_4_2(N, e, d_bit_length, d1, d1_bit_length, m, t):190d_ = d1 << (d_bit_length - d1_bit_length)191k_ = (e * d_ - 1) // N192R = e * d_ - 1 - k_ * N193194x, y, z = ZZ["x", "y", "z"].gens()195f = e * x - N * y + y * z + k_ * z + R196X = 2 ** (d_bit_length - d1_bit_length) # Equivalent to N^delta197Y = 4 * int(max((e * 2 ** (d_bit_length - d1_bit_length)) / N, e / RR(N) ** (1 / 2))) # Equivalent to 4N^max(alpha + delta - 1, alpha - 1 / 2)198Z = int(3 * RR(N) ** (1 / 2))199W = N * Y200logging.info(f"Trying {m = }, {t = }...")201for x0, y0, z0 in ernst.integer_trivariate_2(f, m, t, W, X, Y, Z):202d = d_ + x0203phi = N - z0204if pow(pow(2, e, N), d, N) == 2:205factors = known_phi.factorize(N, phi)206if factors:207return *factors, d208209return None210211212def _ernst_4_3(N, e, d_bit_length, d0, d0_bit_length, M, m, t):213R = e * d0 - 1214215x, y, z = ZZ["x", "y", "z"].gens()216f = e * M * x - N * y + y * z + R217X = 2 ** (d_bit_length - d0_bit_length) # Equivalent to N^delta218Y = 2 ** d_bit_length # Equivalent to N^beta219Z = int(3 * RR(N) ** (1 / 2))220W = N * Y221logging.info(f"Trying {m = }, {t = }...")222for x0, y0, z0 in ernst.integer_trivariate_1(f, m, t, W, X, Y, Z):223d = x0 * M + d0224phi = N - z0225if pow(pow(2, e, N), d, N) == 2:226factors = known_phi.factorize(N, phi)227if factors:228return *factors, d229230return None231232233def attack(N, e, partial_d, factor_e=True, m=1, t=None):234"""235Recovers the prime factors of a modulus and the private exponent if part of the private exponent is known.236More information: Boneh D., Durfee G., Frankel Y., "An Attack on RSA Given a Small Fraction of the Private Key Bits"237More information: Blomer J., May A., "New Partial Key Exposure Attacks on RSA"238More information: Ernst M. et al., "Partial Key Exposure Attacks on RSA Up to Full Size Exponents"239:param N: the modulus240:param e: the public exponent241:param partial_d: the partial private exponent d (PartialInteger)242:param factor_e: whether we should attempt to factor e (for BDF) if it is not prime (default: True)243:param m: the m value to use for the small roots method (default: 1)244:param t: the t value to use for the small roots method (default: automatically computed using m)245:return: a tuple containing the prime factors and the private exponent, or None if the private exponent was not found246"""247d_bit_length = partial_d.bit_length248d0, d0_bit_length = partial_d.get_known_lsb()249d1, d1_bit_length = partial_d.get_known_msb()250assert d0_bit_length > 0 or d1_bit_length > 0, "At least some lsb or msb of d must be known."251252n = N.bit_length()253# Subtract one here, because 2^t < e < 2^(t + 1).254t_ = e.bit_length() - 1255alpha = t_ / n256beta = d_bit_length / n257assert beta >= 0.25, "Use Wiener's or the Boneh-Durfee attack if d is very small."258259if d0_bit_length > 0 and d1_bit_length > 0:260# Known lsbs and msbs.261M = 2 ** d0_bit_length262263if 1 <= t_ <= n / 2 and d0_bit_length >= n / 4 and d1_bit_length >= t_:264logging.info("Using Boneh-Durfee-Frankel (Section 4.3)...")265assert t is not None, "t can not be None for Boneh-Durfee-Frankel small roots."266return _bdf_4_3(N, e, d_bit_length, d0, d0_bit_length, d1, d1_bit_length, M, m, t)267268logging.info("No attacks were found to fit the provided parameters (known lsbs and msbs).")269return None270271if d0_bit_length > 0:272# Known lsbs.273M = 2 ** d0_bit_length274delta = (d_bit_length - d0_bit_length) / n275276if e < RR(N) ** (7 / 8) and RR(N) ** (1 / 6 + 1 / 3 * sqrt(1 + 6 * alpha)) <= M:277logging.info("Using Blomer-May (Section 6)...")278t = int((2 / 3 * (1 - delta - alpha) / (2 * alpha - 1))) if t is None else t279return _bm_6(N, e, d_bit_length, d0, d0_bit_length, M, m, t)280281if delta <= 5 / 6 - 1 / 3 * sqrt(1 + 6 * beta):282logging.info("Using Ernst (Section 4.3)...")283t = int((1 / 2 - delta) * m) if t is None else t284return _ernst_4_3(N, e, d_bit_length, d0, d0_bit_length, M, m, t)285286# Last resort method: enumerate possible k values (very slow if e is too large).287if d0_bit_length >= n / 4:288logging.info("Using Boneh-Durfee-Frankel (Section 3)...")289assert t is not None, "t can not be None for Boneh-Durfee-Frankel small roots."290return _bdf_3(N, e, d_bit_length, d0, d0_bit_length, M, m, t)291292logging.info("No attacks were found to fit the provided parameters (known lsbs).")293return None294295if d1_bit_length > 0:296delta = (d_bit_length - d1_bit_length) / n297298if n / 4 <= t_ <= n / 2 and d1_bit_length >= t_ and (is_prime(e) or factor_e):299logging.info("Using Boneh-Durfee-Frankel (Section 4.1)...")300assert t is not None, "t can not be None for Boneh-Durfee-Frankel small roots."301return _bdf_4_1(N, e, d_bit_length, d1, d1_bit_length, m, t)302303if 0 <= t_ <= n / 2 and d1_bit_length >= n - t_:304logging.info("Using Boneh-Durfee-Frankel (Section 4.2)...")305return _bdf_4_2(N, e, d_bit_length, d1, d1_bit_length)306307# Blomer-May Section 4 is superseded by Ernst Section 4.2.308# if 1 / 2 < alpha <= (sqrt(6) - 1) / 2 and delta <= 1 / 8 * (5 - 2 * alpha - sqrt(36 * alpha ** 2 + 12 * alpha - 15)):309# logging.info("Using Blomer-May (Section 4)...")310# t = int((2 / 3 * (1 - delta - alpha) / (2 * alpha - 1))) if t is None else t311# return _bm_4(N, e, d_bit_length, d1, d1_bit_length, m, t)312313margin4_1_1 = 5 / 6 - 1 / 3 * sqrt(1 + 6 * beta) - delta314margin4_1_2 = (3 / 16 - delta) if beta <= 11 / 16 else (1 / 3 + 1 / 3 * beta - 1 / 3 * sqrt(4 * beta ** 2 + 2 * beta - 2) - delta)315if margin4_1_1 > max(0, margin4_1_2):316logging.info("Using Ernst (Section 4.1.1)...")317t = int((1 / 2 - delta) * m) if t is None else t318return _ernst_4_1_1(N, e, d_bit_length, d1, d1_bit_length, m, t)319320if margin4_1_2 > max(0, margin4_1_1):321logging.info("Using Ernst (Section 4.1.2)...")322gamma = max(delta, beta - 1 / 2)323t = int(((1 / 2 - delta - gamma) / (2 * gamma)) * m) if t is None else t324return _ernst_4_1_2(N, e, d_bit_length, d1, d1_bit_length, m, t)325326if alpha > 1 / 2 and delta <= 1 / 3 + 1 / 3 * alpha - 1 / 3 * sqrt(4 * alpha ** 2 + 2 * alpha - 2):327logging.info("Using Ernst (Section 4.2)...")328gamma = max(alpha + delta - 1, alpha - 1 / 2)329t = int(((1 / 2 - delta - gamma) / (2 * gamma)) * m) if t is None else t330return _ernst_4_2(N, e, d_bit_length, d1, d1_bit_length, m, t)331332logging.info("No attacks were found to fit the provided parameters (known msbs).")333return None334335logging.info("No attacks were found to fit the provided parameters.")336return None337338339