def factorize_from_d(d: int, e: int, n: int): """ Factorizes the secret primes from the private key `d`. Parameters: d (int): Private key. e (int): Public exponent. n (int): Modulus. Returns: RSA: Full RSA instance. """ import random k = d * e - 1 p = None q = None while not p: g = random.randint(2, n - 1) t = k while t % 2 == 0: t = t // 2 x = pow(g, t, n) if x > 1 and gcd(x - 1, n) > 1: p = gcd(x - 1, n) q = n // p break return RSA(0, p=p, q=q, e=e)
def check_full_period(self) -> bool: """ Checks whether the LCG will achieve a full period with its current parameters. Returns: bool: Whether or not it will acheive a full period. References: https://en.wikipedia.org/wiki/Linear_congruential_generator#Period_length """ # Technically, this achieves m-1 if is_prime(self.m) and self.c == 0 and is_primitive_root( self.a, self.m): return True # Maximially m/4 elif is_power_of_two(self.m) and self.c == 0: return False else: # 1. m and c are relatively prime relatively_prime = gcd(self.m, self.c) == 1 # 2. a-1 is divisible by all prime factors of m factors = [factor for factor in factorint(self.m)] divisible_by_all_factors = all([((self.a - 1) % factor) == 0 for factor in factors]) # 3. a-1 is divisible by 4 if m is divisible by 4 divisible_by_four = True if self.m % 4 == 0: divisible_by_four = (self.a - 1) % 4 == 0 return relatively_prime and divisible_by_all_factors and divisible_by_four
def is_invertible(self) -> bool: """ Determines if the element is invertible. Returns: bool: Whether the element is invertible. """ from samson.math.general import gcd return gcd(self.val, self.ring.quotient) == self.ring.ring.one()
def square_free_decomposition(self) -> list: """ Decomposes a Polynomial into its square-free factors. Used as the first step in factorization. https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields#Square-free_factorization Returns: list: Square-free factors of self. Examples: >>> from samson.math.all import Polynomial, ZZ >>> from samson.math.symbols import Symbol >>> x = Symbol('x') >>> _ = ZZ[x] >>> poly = 3*x**3+x**7-x**18 >>> poly.square_free_decomposition() {<Polynomial: x**15 + ZZ(-1)*x**4 + ZZ(-3), coeff_ring=ZZ>: 1, <Polynomial: x, coeff_ring=ZZ>: 3} """ f = self.monic() c = gcd(f, f.derivative().monic()).monic() w = f / c factors = {} i = 1 while w != self.ring.one(): y = gcd(w, c).monic() fac = (w / y).monic() if fac != self.ring.one(): add_or_increment(factors, fac, i) w, c, i = y, c / y, i + 1 if c != self.ring.one(): c = c.trunc_kth_root(self.coeff_ring.characteristic) new_facs = c.square_free_decomposition() for new_fac in new_facs: add_or_increment(factors, new_fac, self.coeff_ring.characteristic) return factors
def distinct_degree_factorization(self) -> list: """ Factors a Polynomial into factors of different degrees. https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields#Distinct-degree_factorization Returns: list: Distinct-degree factors of self. """ from samson.math.general import frobenius_map, frobenius_monomial_base f = self f_star = f S = [] i = 1 x = self.symbol x_poly = f.ring(x) while f_star.degree() > 2 * i: if not f_star.is_monic(): f_star = f_star.monic() # Calculate P(x**q**i - x) bases = frobenius_monomial_base(f_star) h = frobenius_map(bases[1], f_star, bases=bases) for _ in range(i - 1): h = frobenius_map(h, f_star, bases=bases) g = gcd(f_star, h - x_poly).monic() if g != self.ring.one(): S.append((g, i)) f_star /= g i += 1 if f_star != self.ring.one(): S.append((f_star, f_star.degree())) if not S: return [(f, 1)] else: return S
def factorize_from_faulty_crt(message: int, faulty_sig: int, e: int, n: int): """ Factorize the secret primes from a faulty signature produced with CRT-optimized RSA. Parameters: message (int): Message. faulty_sig (int): Faulty signature of `message`. e (int): Public exponent. n (int): Modulus. Returns: RSA: Cracked RSA instance. """ q = gcd(pow(faulty_sig, e, n) - message, n) p = n // q return RSA(0, p=p, q=q, e=e)
def encrypt(self, plaintext: bytes) -> int: """ Encrypts `plaintext`. Parameters: plaintext (bytes): Plaintext. Returns: int: Ciphertext. """ m = Bytes.wrap(plaintext).to_int() assert m < self.n r = random_int(self.n) while gcd(r, self.n) != 1: r = random_int(self.n) n_sqr = self.n ** 2 return pow(self.g, m, n_sqr) * pow(r, self.n, n_sqr)
def factorize_from_shared_p(n1: int, n2: int, e: int): """ Factorizes the moduli of two instances that share a common secret prime. See `Batch GCD`. Parameters: n1 (int): Modulus of the first instance. n2 (int): Modulus of the second instance. e (int): Public exponent. Returns: (RSA, RSA): Both cracked RSA instances. """ assert n1 != n2 # Find shared `p` p = gcd(n1, n2) q1 = n1 // p q2 = n2 // p return (RSA(0, p=p, q=q1, e=e), RSA(0, p=p, q=q2, e=e))
def is_irreducible(self) -> bool: """ Determines if a Polynomial is irreducible over its ring. https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields#Rabin's_test_of_irreducibility https://github.com/sympy/sympy/blob/d1301c58be7ee4cd12fd28f1c5cd0b26322ed277/sympy/polys/galoistools.py Returns: bool: Whether or not the Polynomial is irreducible over its ring. """ from samson.math.general import frobenius_map, frobenius_monomial_base n = self.degree() if n <= 1: return True x = self.symbol f = self.monic() P = self.ring subgroups = {n // fac for fac in factor_int(n)} bases = frobenius_monomial_base(f) h = bases[1] x_poly = P(x) one = P.one() for idx in range(1, n): if idx in subgroups: if gcd(f, h - x_poly).monic() != one: return False h = frobenius_map(h, f, bases=bases) return h == x_poly
def equal_degree_factorization(self, d: int, subgroup_divisor: int = None) -> list: """ Factors a Polynomial into factors of equal degrees. Parameters: d (int): Degree to factor into. subgroup_divisor (int): Smallest divisor of `order - 1`. Returns: list: Equal-degree factors of self. """ from samson.math.general import frobenius_map, frobenius_monomial_base from samson.math.symbols import oo f = self.monic() n = f.degree() r = n // d S = [f] f_quot = f.ring / f if self.coeff_ring.order != oo: q = self.coeff_ring.order if not subgroup_divisor: subgroup_divisor = [f for f in factor_int((q - 1))][0] exponent = (q - 1) // subgroup_divisor one = self.ring.one() bases = frobenius_monomial_base(f) irreducibility_cache = {} if n < d or self.is_irreducible(): return S try: while len(S) < r and (not irreducibility_cache or not all( [irreducibility_cache[poly] for poly in S])): h = f.ring.random(f) g = gcd(h, f).monic() if g == one: h = f_quot(h) j = h for _ in range(d - 1): j = frobenius_map(j, f, bases=bases) h *= j g = (h**exponent).val - one for u in S: if u.degree() <= d or (u in irreducibility_cache and irreducibility_cache[u]): continue gcd_g_u = gcd(g, u).monic() if gcd_g_u != one and gcd_g_u != u: S.remove(u) if u in irreducibility_cache: del irreducibility_cache[u] u_gcd_g_u = u / gcd_g_u S.extend([gcd_g_u, u_gcd_g_u]) # Cache irreducibility results irreducibility_cache[gcd_g_u] = gcd_g_u.is_irreducible( ) irreducibility_cache[ u_gcd_g_u] = u_gcd_g_u.is_irreducible() except KeyboardInterrupt: pass return S
def __init__(self, bits: int = None, p: int = None, q: int = None, e: int = 65537, n: int = None): """ Parameters: bits (int): Number of bits for strength and capacity. p (int): Secret prime modulus. q (int): Secret prime modulus. e (int): Public exponent. n (int): Public modulus. """ Primitive.__init__(self) self.e = e phi = 0 if p and q: phi = lcm(p - 1, q - 1) self.n = p * q if gcd(self.e, phi) != 1: raise Exception("Invalid 'p' and 'q': GCD(e, phi) != 1") bits = p.bit_length() + q.bit_length() elif n: self.n = n else: next_p = p next_q = q # Take into account the bits needed to complete `bits` if `p` or `q` are already defined if p: q_bits = bits - p.bit_length() else: q_bits = bits // 2 if q: p_bits = bits - q.bit_length() else: p_bits = bits // 2 # Find the primes while gcd(self.e, phi) != 1 or next_p == next_q: if not p: next_p = find_prime(p_bits) if not q: next_q = find_prime(q_bits) phi = lcm(next_p - 1, next_q - 1) p = next_p q = next_q self.n = p * q self.p = p self.q = q self.phi = phi self.bits = bits if self.p and self.q: self.d = mod_inv(self.e, phi) self.alt_d = mod_inv(self.e, (self.p - 1) * (self.q - 1)) self.dP = self.d % (self.p - 1) self.dQ = self.d % (self.q - 1) self.Qi = mod_inv(self.q, self.p) else: self.d = None self.alt_d = None self.pub = (self.e, self.n) self.priv = (self.d, self.n)