def fermat(self, n): """Fermat attack""" a = b = isqrt(n) b2 = pow(a, 2) - n while pow(b, 2) != b2: a += 1 b2 = pow(a, 2) - n b = isqrt(b2) p, q = (a + b), (a - b) assert n == p * q return p, q
def pollard_P_1(self, n, progress=True): """Pollard P1 implementation""" z = [] logn = math.log(int(isqrt(n))) prime = primes(997) for j in range(0, len(prime)): primej = prime[j] logp = math.log(primej) for i in range(1, int(logn / logp) + 1): z.append(primej) try: for pp in tqdm(prime, disable=(not progress)): i = 0 x = pp while 1: x = powmod(x, z[i], n) i = i + 1 y = gcd(n, x - 1) if y != 1: p = y q = n // y return p, q if i >= len(z): return 0, None except TypeError: return 0, None
def __init__(self, n, e, progress=True): """Constructor""" self.d = None self.p = None self.q = None sys.setrecursionlimit(100000) frac = self.rational_to_contfrac(e, n) convergents = self.convergents_from_contfrac(frac, progress) for (k, d) in tqdm(convergents, disable=(not progress)): if k != 0: ed1 = e * d - 1 phi = ed1 // k if ed1 - (k * phi) == 0: # same as ed1 % k == 0 s = n - phi + 1 discr = pow(s, 2) - (n << 2) # same as s**2 - 4*n if discr >= 0: t = isqrt(discr) if pow(t, 2) == discr: if (s + t) & 1 == 0: self.d = d x = Symbol("x") roots = solve(pow(x, 2) - s * x + n, x) if len(roots) == 2: self.p = roots[0] self.q = roots[1] break
def SQUFOF(N): s = int(isqrt(N) + 0.5) L = int(2 * isqrt(2 * s)) if s ** 2 == N: return s for k in range(0, len(multiplier)): D = multiplier[k] * N Po = Pprev = P = isqrt(D) Qprev = 1 Q = D - pow(Po, 2) B = 3 * L c0 = True i = 2 while c0: b = int((Po + P) // Q) P = b * Q - P q = Q Q = Qprev + b * (Pprev - P) r = int(isqrt(Q) + 0.5) if not (i & 1) and (pow(r, 2) == Q): break Qprev = q Pprev = P i += 1 c0 = i <= B b = (Po - P) // r Pprev = P = b * r + P Qprev = r Q = (D - pow(Pprev, 2)) // Qprev i = 0 c1 = True while c1: b = int((Po + P) // Q) Pprev = P P = b * Q - P q = Q Q = Qprev + b * (Pprev - P) Qprev = q i += 1 c1 = P != Pprev r = gcd(N, Qprev) if 1 < r < N: return r, N // r return -1
def euler(self, n): if n & 1 == 0: return (n >> 1, 2) if n > 2 else (2, 1) end = isqrt(n) a = 0 solutionsFound = [] firstb = -1 while a < end and len(solutionsFound) < 2: bsquare = n - pow(a, 2) if bsquare > 0: b = isqrt(bsquare) if (pow(b, 2) == bsquare) and (a != firstb) and (b != firstb): firstb = b solutionsFound.append([b, a]) a += 1 if len(solutionsFound) < 2: return None a = solutionsFound[0][0] b = solutionsFound[0][1] c = solutionsFound[1][0] d = solutionsFound[1][1] k = pow(gcd(a - c, d - b), 2) h = pow(gcd(a + c, d + b), 2) m = pow(gcd(a + c, d - b), 2) l = pow(gcd(a - c, d + b), 2) p, q = gcd(k + h, n), gcd(l + m, n) if n > p > 1: return p, n // p if n > q > 1: return q, n // q
def z3_solve(self, n, timeout_amount): """Integer factorization using z3 theorem prover implementation: We can factor composite integers by SAT solving the model N=PQ directly using the clasuse (n==p*q), wich gives a lot of degree of freedom to z3, so we want to contraint the search space. Since every composite number n=pq, there always exists some p>sqrt(n) and q<sqrt(n). We can safely asume the divisor p is in the range n > p >= next_prime(sqrt(n)) if this later clause doesn't hold and sqrt(p) is prime the number is a perfect square. We can also asume that p and q are alyaws odd otherwise our whole composite is even. Not all composite numbers generate a valid model that z3 can SAT. SAT solving is efficient with low bit count set in the factors, the complexity of the algorithm grows exponential with every bit set. The problem of SAT solving integer factorization still is NP complete, making this just a showcase. Don't expect big gains. """ s = Solver() s.set("timeout", timeout_amount * 1000) p = Int("p") q = Int("q") i = int(isqrt(n)) np = int(next_prime(i)) s.add( p * q == n, n > p, n > q, p >= np, q < i, q > 1, p > 1, q % 2 != 0, p % 2 != 0, ) try: s_check_output = s.check() if s_check_output == sat: res = s.model() P, Q = res[p].as_long(), res[q].as_long() assert P * Q == n return P, Q else: return None, None except: return None, None
def dixon_factor(N, B=7, explain=False): if is_prime(N): return N, 1 start = isqrt(N) if (start**2) == N: return start, start base = primes(B) lqbf = pow(base[-1], 2) + 1 QBF = bitarray.bitarray(lqbf) # This is our quasi-bloom-filter basej2N = [] for j in range(0, len(base)): p = powmod(base[j], 2, N) basej2N.append(p) QBF[p] = 1 # We populate our quasi-bloom-filter i = start while i < N: i2N = pow(i, 2, N) if i2N < lqbf and QBF[i2N] == 1: for k in range(0, len(base)): if QBF[basej2N[k]] == 1: # if i2N == basej2N[k]: # this is replaced with a quasi-bloom-filter f = gcd(i - base[k], N) if explain: print("N = %d" % N) print("%d = isqrt(N)" % start) print("%d = pow(%d,2,n)" % (i2N, i)) print("%d = pow(%d,2,n)" % (basej2N[k], base[k])) print("%d - %d = %d" % (i, base[k], f)) print("%d = gcd(%d - % d, N)" % (f, i, base[k])) print("%d = gcd(%d + % d, N)" % (gcd(i + base[k], N), i, base[k])) if 1 < f < N: return f, N // f i += 1 return None, None
def close_factor(self, n, b, progress=True): # approximate phi phi_approx = n - 2 * isqrt(n) + 1 # create a look-up table look_up = {} z = 1 for i in tqdm(range(0, b + 1), disable=(not progress)): look_up[z] = i z = (z << 1) % n # check the table mu = invmod(powmod(2, phi_approx, n), n) fac = powmod(2, b, n) for i in tqdm(range(0, b + 1), disable=(not progress)): if mu in look_up: phi = phi_approx + (look_up[mu] - (i * b)) r = trivial_factorization_with_n_phi(n, phi) if r != None: return r mu = (mu * fac) % n else: return None