Path: blob/master/Public Key/Factoring/cm_factor.sage
336 views
import sys1from utils import *23sys.setrecursionlimit(100000000)45def cm_factor(N, D, B=32, debug=True):6"""7Implements a simplified version of Cheng's 4p - 1 elliptic curve complex multiplication based factorization algorithm.8Targets moduli where one (or many) prime factors satisfies the equality 4p_i - 1 = D * s^2, where D is a squarefree integer9and s is a randomly generated one between an upper and lower bound.1011Original paper title: I want to break square-free: The 4p - 1 factorization method and its RSA backdoor viability12Link: https://crocs.fi.muni.cz/_media/public/papers/2019-secrypt-sedlacek.pdf1314:param N: integer to be factored15:param D: squarefree integer satisfying 4p - 1 = D * s^216:param B: number of iterations to run the algorithm for17:param debug: switches debugging information on/off18:return: a tuple corresponding to p, q, or failure (-1)19"""2021# If D is not squarefree then we terminate immediately.22assert D.is_squarefree(), "D must be squarefree."2324# Computes the -Dth Hilbert polynomial modulo N and quotient ring Q = Z_N[x] / <H_{-D, N}>2526Z_N = Zmod(N)27P = Z_N[x]28H = P(hilbert_class_polynomial(-D))29Q = P.quotient_ring(H)3031# j is the equivalence class corresponding to [X] in Q.32j = Q(x)3334# The paper claims that we can treat both 1728 - j and H as polynomials in Z_N[X] and calculate the inverse of35# 1728 - j and H via egcd. This doesn't quite work off the shelves, so we instead accomplish this by treating36# 1728 - j as an element of Q = Z_N[x] / <H_{-D, n}> and lifting it back into the base ring.37# Sage implements this via the .lift() method.3839if debug:40print("Calculating inverse mod of 1728 - j with H...")4142try:43k = j * polynomial_inv_mod((1728 - j).lift(), H)44except ValueError as e:45r = gcd(int(e.args[1].lc()), N)46return int(r), int(N // r)4748# Constructs an elliptic curve described by the equation y^2 = x^3 + ax + b where a = 3k and b = 2k over Q.49# This is so we can calculate the division polynomial psi_n(a, b, x_i) later.50E = EllipticCurve(Q, [3*k, 2*k])5152for i in range(B):5354# Obtains a random element from Z_N for the division polynomial.55x_i = Z_N.random_element()5657if debug:58print("Calculating division polynomial with x_i...")5960# Calculates the division polynomial modulo n: psi_n(a, b, x_i)61z = E.division_polynomial(N, x=Q(x_i))6263# If our egcd implementation throws an exception, then we know that the r polynomial during the computation64# has no inverse; we return this polynomial in our exception and then take the gcd of its leading coefficient with N.65# Otherwise we take the gcd of d and N normally.66try:67d, _, _ = polynomial_egcd(z.lift(), H)68except ValueError as e:69r = gcd(int(e.args[1].lc()), N)70return int(r), int(N // r)7172r = gcd(int(d), N)7374# Ensure that the factorization is nontrivial: i.e. r != 1 and r != N75if 1 < r < N:76return r, N // r7778return -17980if __name__ == "__main__":81D = 41982# Generates a modulus N = pq where 4p - 1 = Ds^2.8384print("Generating modulus...")85p = generate_cm_prime(D)86q = random_prime(1<<1024, proof=False)8788N = p * q89print("Factoring...")90r, s = cm_factor(N, D)91assert r * s == N92print(f"Factorization successful! N = {r} * {s}")9394