def attack(self, publickey, cipher=[], progress=True): """ Pisano(mersenne) period factorization algorithm optimal for keys sub 70 bits in less than a minute. The attack is very similar to londahl's """ Fib = Fibonacci(progress=progress) with timeout(self.timeout): try: B1, B2 = ( pow(10, (ilog10(publickey.n) // 2) - 4), 0, ) # Arbitrary selected bounds, biger b2 is more faster but more failed factorizations. try: r = Fib.factorization(publickey.n, B1, B2) except OverflowError: r = None if r is not None: publickey.p, publickey.q = r priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) return (None, None) except TimeoutError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Run attack with Euler method""" if not hasattr(publickey, "p"): publickey.p = None if not hasattr(publickey, "q"): publickey.q = None # Euler attack with timeout(self.timeout): try: try: if (publickey.n - 1) % 4 == 0: euler_res = self.euler(publickey.n) else: self.logger.error( "[!] Public key modulus must be congruent 1 mod 4 to work with euler method." ) return (None, None) except: return (None, None) if euler_res and len(euler_res) > 1: publickey.p, publickey.q = euler_res if publickey.q is not None: priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) except TimeoutError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Run fermat attack with a timeout""" try: with timeout(seconds=self.timeout): try: publickey.p, publickey.q = self.fermat(publickey.n) except TimeoutError: return (None, None) except FactorizationError: return (None, None) if publickey.p is not None and publickey.q is not None: try: priv_key = PrivateKey( n=publickey.n, p=int(publickey.p), q=int(publickey.q), e=int(publickey.e), ) return (priv_key, None) except ValueError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Run dixon attack with a timeout""" try: with timeout(seconds=self.timeout): try: if publickey.n <= 10**10: publickey.p, publickey.q = dixon_factor(publickey.n) else: self.logger.info( "[-] Dixon is too slow for pubkeys > 10^10...") return (None, None) except TimeoutError: return (None, None) except FactorizationError: return (None, None) if publickey.p is not None and publickey.q is not None: try: priv_key = PrivateKey( n=publickey.n, p=int(publickey.p), q=int(publickey.q), e=int(publickey.e), ) return (priv_key, None) except ValueError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Run attack with Pollard P1""" if not hasattr(publickey, "p"): publickey.p = None if not hasattr(publickey, "q"): publickey.q = None with timeout(self.timeout): try: # Pollard P-1 attack try: poll_res = self.pollard_P_1(publickey.n, progress) except RecursionError: return (None, None) if poll_res and len(poll_res) > 1: publickey.p, publickey.q = poll_res if publickey.q is not None: priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) except TimeoutError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Qi Cheng - A New Class of Unsafe Primes""" try: sageresult = int( subprocess.check_output( [ "sage", "%s/sage/qicheng.sage" % rootpath, str(publickey.n) ], timeout=self.timeout, stderr=subprocess.DEVNULL, )) except (subprocess.CalledProcessError, subprocess.TimeoutExpired, ValueError): return (None, None) if sageresult > 0: p = sageresult q = publickey.n // sageresult priv_key = PrivateKey(int(p), int(q), int(publickey.e), int(publickey.n)) return (priv_key, None) else: return (None, None)
def attack(self, publickey, cipher=[], progress=True): """binary polinomial factoring""" try: sageresult = str( subprocess.check_output( [ "sage", "%s/sage/binary_polinomial_factoring.sage" % rootpath, str(publickey.n), ], timeout=self.timeout, stderr=subprocess.DEVNULL, )).split(" ") except (subprocess.CalledProcessError, subprocess.TimeoutExpired): return (None, None) try: p = int(sageresult[0]) except ValueError: return (None, None) if p > 0: q = publickey.n // p priv_key = PrivateKey(int(p), int(q), int(publickey.e), int(publickey.n)) return (priv_key, None) else: return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Run tests against primorial +-1 composites""" with timeout(self.timeout): try: limit = 10000 prime = 1 primorial = 1 p = q = None for x in tqdm(range(0, limit), disable=(not progress)): prime = next_prime(prime) primorial *= prime primorial_p1 = [primorial - 1, primorial + 1] g0, g1 = gcd(primorial_p1[0], publickey.n), gcd(primorial_p1[1], publickey.n) if 1 < g0 < publickey.n: p = publickey.n // g0 q = g0 break if 1 < g1 < publickey.n: p = publickey.n // g1 q = g1 break if p is not None and q is not None: priv_key = PrivateKey(int(p), int(q), int(publickey.e), int(publickey.n)) return (priv_key, None) return (None, None) except TimeoutError: return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Factor n if mininum of crt exponent is small enough""" try: p = int( subprocess.check_output( [ "sage", "%s/sage/small_crt_exp.sage" % rootpath, str(publickey.n), str(publickey.e), str(1 << 32), # Default upper bound of min(d_p, d_q) ], timeout=self.timeout, stderr=subprocess.DEVNULL, )) except (subprocess.CalledProcessError, subprocess.TimeoutExpired): return (None, None) if p > 0: q = publickey.n // p privatekey = PrivateKey( p=p, q=q, e=publickey.e, n=publickey.n, ) return (privatekey, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Use boneh durfee method, should return a d value, else returns 0 only works if the sageworks() function returned True many of these problems will be solved by the wiener attack module but perhaps some will fall through to here """ try: sageresult = int( subprocess.check_output( [ "sage", "%s/sage/boneh_durfee.sage" % rootpath, str(publickey.n), str(publickey.e), ], timeout=self.timeout, stderr=subprocess.DEVNULL, )) except (subprocess.CalledProcessError, subprocess.TimeoutExpired): return (None, None) if sageresult > 0: tmp_priv = RSA.construct( (int(publickey.n), int(publickey.e), int(sageresult)), ) publickey.p = tmp_priv.p publickey.q = tmp_priv.q privatekey = PrivateKey( p=int(publickey.p), q=int(publickey.q), e=int(publickey.e), n=int(publickey.n), ) return (privatekey, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): if is_roca_vulnerable(publickey.n): try: sageresult = subprocess.check_output( [ "sage", "%s/sage/roca_attack.py" % rootpath, str(publickey.n) ], timeout=self.timeout, stderr=subprocess.DEVNULL, ) except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e: return (None, None) if b"FAIL" not in sageresult and b":" in sageresult: sageresult = sageresult.decode("utf-8").strip() p, q = map(int, sageresult.split(":")) priv_key = PrivateKey(int(p), int(q), int(publickey.e), int(publickey.n)) return (priv_key, None) else: return (None, None) else: self.logger.info("[-] This key is not roca, skiping test...") return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Try to factorize using yafu""" with timeout(self.timeout): try: if publickey.n.bit_length() > 1024: self.logger.error( "[!] Warning: Modulus too large for SIQS attack module" ) return (None, None) siqsobj = SiqsAttack(publickey.n, self.timeout) if siqsobj.testyafu(): siqsobj.doattack() else: return (None, None) if siqsobj.p and siqsobj.q: publickey.q = siqsobj.q publickey.p = siqsobj.p priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) except TimeoutError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Code/idea from Renaud Lifchitz's talk 15 ways to break RSA security @ OPCDE17 only works if the sageworks() function returned True """ try: r = subprocess.check_output( [ "sage", "%s/sage/smallfraction.sage" % rootpath, str(publickey.n) ], timeout=self.timeout, stderr=subprocess.DEVNULL, ) sageresult = int(r) if sageresult > 0: publickey.p = sageresult publickey.q = publickey.n // publickey.p priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) except (subprocess.CalledProcessError, subprocess.TimeoutExpired): return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Run tests against mersenne composites""" with timeout(self.timeout): try: p = q = None for i in tqdm(range(2, ilog2(publickey.n)), disable=(not progress)): i2 = 2**i mersenne = [i2 - 1, i2 + 1] g0, g1 = gcd(mersenne[0], publickey.n), gcd(mersenne[1], publickey.n) if 1 < g0 < publickey.n: p = publickey.n // g0 q = g0 break if 1 < g1 < publickey.n: p = publickey.n // g1 q = g1 break if p is not None and q is not None: priv_key = PrivateKey(int(p), int(q), int(publickey.e), int(publickey.n)) return (priv_key, None) return (None, None) except TimeoutError: return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Run attack with Pollard Rho""" if not hasattr(publickey, "p"): publickey.p = None if not hasattr(publickey, "q"): publickey.q = None # pollard Rho attack with timeout(self.timeout): try: try: poll_res = self.pollard_rho(publickey.n) except RecursionError: print("RecursionError") return (None, None) if poll_res != None: publickey.p = poll_res publickey.q = publickey.n // publickey.p if publickey.q is not None: priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) except TimeoutError: return (None, None) except TypeError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): if is_roca_vulnerable(publickey.n): if getpubkeysz(publickey.n) <= 512: necaresult = subprocess.check_output( ["neca", "%s" % publickey.n], timeout=self.timeout, stderr=subprocess.DEVNULL, ) necaresult_l = necaresult.decode("utf8").split("\n") if b"FAIL" not in necaresult and b"*" in necaresult: for line in necaresult_l: r0 = line.find("N = ") r1 = line.find(" * ") if r0 > -1 and r1 > -1: p, q = list(map(int, line.split("=")[1].split("*"))) priv_key = PrivateKey( int(p), int(q), int(publickey.e), int(publickey.n) ) return (priv_key, None) else: return (None, None) else: self.logger.info( "[-] This key is roca but > 512 bits, try with roca attack..." ) return (None, None) else: self.logger.info("[-] This key is not roca, skiping test...") return (None, None)
def attack(self, publickey, cipher=[], progress=True): if not hasattr(publickey, "p"): publickey.p = None if not hasattr(publickey, "q"): publickey.q = None # solve with z3 theorem prover with timeout(self.timeout): try: try: z3_res = self.z3_solve(publickey.n, self.timeout) except: self.logger.warning("[!] z3: Internal Error.") return (None, None) if z3_res and len(z3_res) > 1: p, q = z3_res publickey.p = p publickey.q = q if publickey.q is not None: priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) else: return (None, None) except TimeoutError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Factors available online?""" try: wa_enabled = True import wolframalpha app_id = os.environ.get("WA_API_KEY") wa_enabled = app_id != None except Exception: self.logger.warning( "[!] Wolfram Alpha is not enabled, install the librairies.") wa_enabled = False if not wa_enabled: self.logger.warning( "[!] Wolfram Alpha is not enabled, check if ENV WA_API_KEY is set." ) self.logger.warning( "[!] follow: https://products.wolframalpha.com/api/documentation/" ) self.logger.warning("[!] export WA_API_KEY=XXXXXX-XXXXXXXXXX") self.wa_client = None return (None, None) else: self.wa_client = wolframalpha.Client(app_id) with timeout(self.timeout): try: factors = self.wa_query_factors(publickey.n) self.logger.info("Factors: %s" % str(factors)) if factors != None and len(factors) > 1: publickey.q = factors[ -1] # Let it be the last prime wich is the bigger one publickey.p = publickey.n // publickey.q priv_key = PrivateKey( p=int(publickey.p), q=int(publickey.q), e=int(publickey.e), n=int(publickey.n), ) return (priv_key, None) else: return (None, None) except Exception as e: self.logger.error( "[*] wolfram alpha could not get a factorization.") self.logger.debug(str(e)) return (None, None) except TimeoutError: return (None, None)
def attack(self, publickey, cipher=[], progress=True): """use elliptic curve method, may return a prime or may never return only works if the sageworks() function returned True """ try: try: if self.ecmdigits is not None: sageresult = int( subprocess.check_output( [ "sage", "%s/sage/ecm.sage" % rootpath, str(publickey.n), str(self.ecmdigits), ], timeout=self.timeout, stderr=subprocess.DEVNULL, )) else: sageresult = int( subprocess.check_output( [ "sage", "%s/sage/ecm.sage" % rootpath, str(publickey.n) ], timeout=self.timeout, stderr=subprocess.DEVNULL, )) except (subprocess.CalledProcessError, subprocess.TimeoutExpired): return (None, None) if sageresult > 0: publickey.p = sageresult publickey.q = publickey.n // publickey.p try: priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) except NotImplementedError: return (None, None) return (None, None) except KeyboardInterrupt: pass return (None, None)
def comfact(self, cipher, publickey): for c in cipher: commonfactor = gcd(publickey.n, s2n(c)) if commonfactor > 1: publickey.q = commonfactor publickey.p = publickey.n // publickey.q priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Try an attack where q < 100,000, from BKPCTF2016 - sourcekris""" with timeout(self.timeout): try: for prime in primes(100000): if publickey.n % prime == 0: publickey.q = prime publickey.p = publickey.n // publickey.q priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) except TimeoutError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Wiener's attack""" with timeout(self.timeout): try: wiener = WienerAttack(publickey.n, publickey.e, progress) if wiener.p is not None and wiener.q is not None: publickey.p = wiener.p publickey.q = wiener.q priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) except TimeoutError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Run tests against fermat composites""" with timeout(self.timeout): try: limit = 10000 p = q = None for x in tqdm(range(1, limit), disable=(not progress)): f = gcd(fib(x), publickey.n) if 1 < f < publickey.n: p = publickey.n // f q = f break if p is not None and q is not None: priv_key = PrivateKey(int(p), int(q), int(publickey.e), int(publickey.n)) return (priv_key, None) return (None, None) except TimeoutError: return (None, None)
def attack(self, publickey, cipher=[], progress=True): """cm_factor attack""" D_candidates = [3, 11, 19, 43, 67, 163] sageresult = 0 for D_candidate in tqdm(D_candidates, disable=(not progress)): try: sageresult = subprocess.check_output( [ "sage", "%s/sage/cm_factor.sage" % rootpath, "-N", str(publickey.n), "-D", str(D_candidate), ], timeout=self.timeout, stderr=subprocess.DEVNULL, ) if sageresult == b"Factorization failed\n": continue X = str(sageresult).replace("'", "").split("\\n") X = list(filter(lambda x: x.find(" * ") > 0, X)) if len(X) == 0: continue sageresult = int(X[0].split(" ")[0]) break except ( subprocess.CalledProcessError, subprocess.TimeoutExpired, ValueError, ): continue if isinstance(sageresult, int): if sageresult > 0: p = sageresult q = publickey.n // sageresult priv_key = PrivateKey(int(p), int(q), int(publickey.e), int(publickey.n)) return (priv_key, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Do nothing, used for multi-key attacks that succeeded so we just print the private key without spending any time factoring """ londahl_b = 20000000 with timeout(self.timeout): try: factors = self.close_factor(publickey.n, londahl_b, progress) if factors is not None: p, q = factors priv_key = PrivateKey( int(p), int(q), int(publickey.e), int(publickey.n) ) return (priv_key, None) else: return (None, None) except TimeoutError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """System primes in crypto constants""" with timeout(self.timeout): try: primes = load_system_consts() for prp in tqdm(primes, disable=(not progress)): g = gcd(publickey.n, prp) if publickey.n > g > 1: publickey.q = g publickey.p = publickey.n // publickey.q priv_key = PrivateKey( int(publickey.p), int(publickey.q), int(publickey.e), int(publickey.n), ) return (priv_key, None) except TimeoutError: return (None, None) return (None, None)
def attack(self, publickeys, cipher=[]): """Common factor attack""" if not isinstance(publickeys, list): return (None, None) with timeout(self.timeout): try: pubs = [pub.n for pub in publickeys] # Try to find the gcd between each pair of moduli and resolve the private keys if gcd > 1 priv_keys = [] M = ProductTree(pubs) for i in range(0, len(pubs) - 1): pub = pubs[i] x = publickeys[i] R = M // pub g = gcd(pub, R) if pub > g > 1: try: p = g q = pub // g x.p = p x.q = q # update each attackobj with a private_key priv_key_1 = PrivateKey( int(x.p), int(x.q), int(x.e), int(x.n) ) priv_keys.append(priv_key_1) self.logger.info( "[*] Found common factor in modulus for " + x.filename ) except ValueError: continue except TimeoutError: return (None, None) priv_keys = list(set(priv_keys)) if len(priv_keys) == 0: priv_keys = None return (priv_keys, None)
def attack(self, publickey, cipher=[], progress=True): """ "primes" of the form 31337 - 313333337 - see ekoparty 2015 "rsa 2070" not all numbers in this form are prime but some are (25 digit is prime) """ with timeout(self.timeout): try: maxlen = 25 # max number of digits in the final integer for i in tqdm(range(maxlen - 4), disable=(not progress)): prime = int("3133" + ("3" * i) + "7") if publickey.n % prime == 0: publickey.p = prime publickey.q = publickey.n // publickey.p priv_key = PrivateKey( p=int(publickey.p), q=int(publickey.q), e=int(publickey.e), n=int(publickey.n), ) return (priv_key, None) except TimeoutError: return (None, None) return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Factors available online?""" with timeout(self.timeout): try: url_1 = "http://factordb.com/index.php?query=%i" url_2 = "http://factordb.com/index.php?id=%s" s = requests.Session() r = s.get(url_1 % publickey.n, verify=False) regex = re.compile(r"index\.php\?id\=([0-9]+)", re.IGNORECASE) ids = regex.findall(r.text) # check if only 1 factor is returned if len(ids) == 2: # theres a chance that the only factor returned is prime, and so we can derive the priv key from it regex = re.compile(r"<td>P<\/td>") prime = regex.findall(r.text) if len(prime) == 1: # n is prime, so lets get the key from it d = invmod(publickey.e, publickey.n - 1) # construct key using only n and d priv_key = PrivateKey( e=int(publickey.e), n=int(publickey.n), d=d ) return (priv_key, None) elif len(ids) == 3: try: regex = re.compile(r'value="([0-9\^\-]+)"', re.IGNORECASE) p_id = ids[1] r_1 = s.get(url_2 % p_id, verify=False) key_p = regex.findall(r_1.text)[0] publickey.p = ( int(key_p) if key_p.isdigit() else self.solveforp(key_p) ) q_id = ids[2] r_2 = s.get(url_2 % q_id, verify=False) key_q = regex.findall(r_2.text)[0] publickey.q = ( int(key_q) if key_q.isdigit() else self.solveforp(key_q) ) if publickey.n != int(publickey.p) * int(publickey.q): return (None, None) except IndexError: return (None, None) try: priv_key = PrivateKey( p=int(publickey.p), q=int(publickey.q), e=int(publickey.e), n=int(publickey.n), ) except ValueError: return (None, None) return (priv_key, None) elif len(ids) > 3: phi = 1 for p in ids[1:]: phi *= int(p) - 1 d = invmod(publickey.e, phi) plains = [] if cipher is not None and len(cipher) > 0: for c in cipher: int_big = int.from_bytes(c, "big") plain1 = powmod(int_big, d, publickey.n) plains.append(long_to_bytes(plain1)) return (None, plains) return (None, None) except NotImplementedError: return (None, None) except TimeoutError: return (None, None)
def attack(self, publickey, cipher=[], progress=True): """Run tests against mersenne primes""" with timeout(self.timeout): try: p = q = None mersenne_tab = [ 2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279, 2203, 2281, 3217, 4253, 4423, 9689, 9941, 11213, 19937, 21701, 23209, 44497, 86243, 110503, 132049, 216091, 756839, 859433, 1257787, 1398269, 2976221, 3021377, 6972593, 13466917, 20336011, 24036583, 25964951, 30402457, 32582657, 37156667, 42643801, 43112609, 57885161, 74207281, 77232917, 82589933, ] i = getpubkeysz(publickey.n) for mersenne_prime in tqdm(mersenne_tab, disable=(not progress)): if mersenne_prime <= i: m = (1 << mersenne_prime) - 1 if publickey.n % m == 0: p = m q = publickey.n // p break else: break if p is not None and q is not None: priv_key = PrivateKey(int(p), int(q), int(publickey.e), int(publickey.n)) return (priv_key, None) return (None, None) except TimeoutError: return (None, None)