├── scripts ├── Hash │ ├── lea.py │ └── sha1_collision.py ├── Mathmathics │ ├── crt.py │ ├── nth_root.sage │ └── cube_root.sage ├── RSA │ ├── common_modular_attack.py │ ├── partial_m_msb.sage │ ├── hastad_broadcast.py │ ├── fermat_factorization.py │ ├── partial_d.sage │ ├── partial_p_msb.sage │ ├── partial_p_lsb.sage │ ├── LSBOracle.py │ └── boneh_durfee.sage ├── Cryptosystem │ ├── SchmidtSamoa.py │ ├── Paillier.py │ └── OkamotoUchiyama.py └── factorization │ ├── factordb.py │ ├── williams_pp1.py │ └── pollard.py ├── .gitIgnore └── Readme.md /scripts/Hash/lea.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitIgnore: -------------------------------------------------------------------------------- 1 | *.sage.py 2 | .DS_Store 3 | __pycache__ 4 | remove_useless.sh 5 | -------------------------------------------------------------------------------- /scripts/Mathmathics/crt.py: -------------------------------------------------------------------------------- 1 | import operator 2 | from functools import reduce 3 | from Crypto.Util.number import inverse 4 | 5 | def crt(remainders, modules): 6 | """ 7 | Solving Chinese Remainder Theorem 8 | @modules and @remainders are lists. 9 | """ 10 | x = 0 11 | N = reduce(operator.mul, modules) 12 | for i, module in enumerate(modules): 13 | if module == 1: 14 | continue 15 | Ni = N // module 16 | b = inverse(Ni, module) 17 | x += remainders[i] * Ni * b 18 | return x % N -------------------------------------------------------------------------------- /scripts/RSA/common_modular_attack.py: -------------------------------------------------------------------------------- 1 | 2 | def common_modular(set1,set2): 3 | """ 4 | Common Modular Attack, if you get c1 = m^e1 % N , c2 = m^e2 % N 5 | Given two set of ( N, e, c ) 6 | return plaintext 7 | """ 8 | n1,e1,c1 = set1 9 | n2,e2,c2 = set2 10 | if n1 != n2 : 11 | print("[-] Common Modular Attack Fail, n1 != n2") 12 | return 13 | if gcd(e1,e2) != 1 : 14 | print("[-] Common Modular Attack Fail, gcd(e1,e2) != 1") 15 | return 16 | a,b = xgcd(e1,e2) 17 | t1 = pow(c1,abs(a),n1) 18 | t2 = pow(c2,abs(b),n2) 19 | if a < 0 : t1 = invmod(t1,n1) 20 | if b < 0 : t2 = invmod(t2,n2) 21 | return t1*t2 % n1 22 | -------------------------------------------------------------------------------- /scripts/RSA/partial_m_msb.sage: -------------------------------------------------------------------------------- 1 | # partial_m.sage 2 | n = 0x00bef498e6eb2cffe71312da47ab89d2c47db7438ea2cfa992ddddbc2a01978001fc51e286e6ebf028396cdb8b3323c60e6b9d50cd84187cf7f48e3875a2f0890f70b02333ad89db2923863ce146562286f63fb0a1d0198e3a6862ba5ac12e85a5c6d0d27cb1c81bdf69cc5bc95b8001a2f744517f9437b4ddd5a076fc0e9a5de1a7a268c40f31aa29e8dc27c0b3a182299ca7a9335b4bd4585452f6107c238e486c98dd73a5f9862e9e80b152f53381c72f897107551c281259ac3ee32c4b4f46cc03127d1bf699acd0266f3c6729253c70da0c69b1560fa172735709866b375b6eba294e1ce8b46fba798ba380080b4bf9603998cac199d9cd46e30ae8da9e7f 3 | e = 3 4 | 5 | m = randrange(n) 6 | c = pow(m, e, n) 7 | 8 | beta = 1 9 | epsilon = beta^2/7 10 | 11 | nbits = n.nbits() 12 | kbits = floor(nbits*(beta^2/e-epsilon)) 13 | mbar = m & (2^nbits-2^kbits) 14 | print("upper %d bits (of %d bits) is given" % (nbits-kbits, nbits)) 15 | 16 | PR. = PolynomialRing(Zmod(n)) 17 | f = (mbar + x)^e - c 18 | 19 | print(m) 20 | x0 = f.small_roots(X=2^kbits, beta=1)[0] 21 | print(mbar + x0) -------------------------------------------------------------------------------- /scripts/RSA/hastad_broadcast.py: -------------------------------------------------------------------------------- 1 | import operator 2 | from gmpy2 import iroot 3 | from functools import reduce 4 | from Crypto.Util.number import inverse 5 | 6 | def crt(remainders, modules): 7 | """ 8 | Solving Chinese Remainder Theorem 9 | @modules and @remainders are lists. 10 | """ 11 | x = 0 12 | N = reduce(operator.mul, modules) 13 | for i, module in enumerate(modules): 14 | if module == 1: 15 | continue 16 | Ni = N // module 17 | b = inverse(Ni, module) 18 | x += remainders[i] * Ni * b 19 | return x % N 20 | 21 | def hastad_broadcast(cipher, module, exponent): 22 | """ 23 | Chinese Remainder 24 | If the same message, encrypt by same exponent but different module 25 | hastad broadcast attack may solved it. 26 | 27 | usage : hastad_broadcast([C1,C2,C3],[N1,N2,N3]) 28 | return : C = M^e mod (N1*N2*N3) 29 | 30 | if M^e < N1*N2*N3 : solved. 31 | """ 32 | assert len(cipher) == len(module), "Amount of (cipher, modulo) pair unmatch." 33 | C = crt(cipher,module) 34 | m, root = iroot(C, exponent) 35 | if root == True : 36 | return int(m) -------------------------------------------------------------------------------- /scripts/Mathmathics/nth_root.sage: -------------------------------------------------------------------------------- 1 | def get_phi(N): 2 | phi = N 3 | for f in factor(N): 4 | phi = phi * (1 - 1 / f[0]) 5 | return phi 6 | 7 | def get_roots(r,c,mod): 8 | rems = [] 9 | if gcd( get_phi(mod), r) == 1: 10 | d = inverse_mod( r,get_phi(mod) ) 11 | rems.append(int(pow(c, d, mod))) 12 | else: 13 | g = GF(mod).multiplicative_generator() 14 | u = int(g ** ((mod-1)/r)) 15 | r1 = int(rth_root(r,c, mod)) 16 | for i in range(r): 17 | rems.append( int(r1 * pow(u, i, mod) % mod) ) 18 | return rems 19 | 20 | def rth_root(c,p,root): 21 | rems = get_roots(root, c%p, p) 22 | for m in rems : 23 | if pow(m,root,p) != c : 24 | print('%d = m^%d mod %d, m has no integer solutions.' % (c,root,p) ) 25 | exit() 26 | return rems 27 | 28 | if __name__ == "__main__": 29 | 30 | # # Debug 31 | # p = random_prime(2^128-1,False,2^127) 32 | # x = randint(2^127,2^128) % p 33 | # e = random_prime(15) 34 | # c = pow(x,e,p) 35 | # print(f"{x}^{e} mod {p} = {c}") 36 | # rems = rth_root(c,p,e) 37 | # print(f'x = {rems}') 38 | # assert x in rems 39 | 40 | # Example 41 | print("x^e mod p = c") 42 | c = int(input("c = ")) 43 | p = int(input("p = ")) 44 | e = int(input("e = ")) 45 | rems = rth_root(c,p,e) 46 | print(f"x = {rems}") -------------------------------------------------------------------------------- /scripts/RSA/fermat_factorization.py: -------------------------------------------------------------------------------- 1 | def nroot(x, n): 2 | """ 3 | Return truncated n'th root of x. 4 | """ 5 | if n < 0: 6 | raise ValueError("can't extract negative root") 7 | 8 | if n == 0: 9 | raise ValueError("can't extract zero root") 10 | 11 | sign = 1 12 | if x < 0: 13 | sign = -1 14 | x = -x 15 | if n % 2 == 0: 16 | raise ValueError("can't extract even root of negative") 17 | 18 | high = 1 19 | while high ** n <= x: 20 | high <<= 1 21 | 22 | low = high >> 1 23 | while low < high: 24 | mid = (low + high) >> 1 25 | mr = mid ** n 26 | if mr == x: 27 | return (mid, True) 28 | elif low < mid and mr < x: 29 | low = mid 30 | elif high > mid and mr > x: 31 | high = mid 32 | else : 33 | return (sign * mid, False) 34 | return (sign * (mid + 1) , False) 35 | 36 | 37 | def fermat_factorization(N) : 38 | """ 39 | Fermat's factorization for close p and q 40 | link : https://en.wikipedia.org/wiki/Fermat%27s_factorization_method 41 | """ 42 | a = nroot(N,2)[0] 43 | b2 = a*a - N 44 | b = nroot(N,2)[0] 45 | count = 0 46 | while b*b != b2: 47 | a = a + 1 48 | b2 = a*a - N 49 | b = nroot(b2,2)[0] 50 | count += 1 51 | p=a+b 52 | q=a-b 53 | return p, q 54 | -------------------------------------------------------------------------------- /scripts/RSA/partial_d.sage: -------------------------------------------------------------------------------- 1 | # partial_d.sage 2 | def partial_p(p0, kbits, n): 3 | PR. = PolynomialRing(Zmod(n)) 4 | nbits = n.nbits() 5 | f = 2^kbits*x + p0 6 | f = f.monic() 7 | roots = f.small_roots(X=2^(nbits//2-kbits), beta=0.3) # find root < 2^(nbits//2-kbits) with factor >= n^0.3 8 | if roots: 9 | x0 = roots[0] 10 | p = gcd(2^kbits*x0 + p0, n) 11 | return ZZ(p) 12 | 13 | def find_p(d0, kbits, e, n): 14 | P = var('P') 15 | for k in range(1, e+1): 16 | results = solve_mod([e*d0*P - k*(n-P+1)*P + k*n == P], 2^kbits) 17 | for x in results: 18 | p0 = ZZ(x[0]) 19 | p = partial_p(p0, kbits, n) 20 | if p: 21 | return p 22 | 23 | if __name__ == '__main__': 24 | print("start!") 25 | n = 123541066875660402939610015253549618669091153006444623444081648798612931426804474097249983622908131771026653322601466480170685973651622700515979315988600405563682920330486664845273165214922371767569956347920192959023447480720231820595590003596802409832935911909527048717061219934819426128006895966231433690709 26 | e = 97 27 | nbits = n.nbits() 28 | kbits = 300 29 | d0 = 48553333005218622988737502487331247543207235050962932759743329631099614121360173210513133 30 | print("lower %d bits (of %d bits) is given" % (kbits, nbits)) 31 | 32 | p = find_p(d0, kbits, e, n) 33 | print("found p: %d" % p) -------------------------------------------------------------------------------- /scripts/Mathmathics/cube_root.sage: -------------------------------------------------------------------------------- 1 | def cube_root(c, q): 2 | F = FiniteField(q) 3 | R. = PolynomialRing(F,'x') 4 | while 1: 5 | a = F.random_element() 6 | b = F.random_element() 7 | fx = x**3 - a*x**2 + b*x - c 8 | fc = list(factor(fx)) 9 | if len(fc) <= 1: 10 | root = pow(x, (q**2+q+1)//3, fx) 11 | root %= x 12 | return int(root) 13 | 14 | def cube_roots(c,mod): 15 | c = c % mod 16 | rems = [] 17 | if gcd( (mod-1), 3) == 1: 18 | d = inverse_mod(3, mod - 1) 19 | rems.append( int(pow(c, d, mod)) ) 20 | else: 21 | g = GF(mod).multiplicative_generator() 22 | u = int(g ** ((mod-1)//3)) 23 | r1 = int(cube_root(c, mod)) 24 | for i in range(3): 25 | rems.append( int(r1 * pow(u, i, mod) % mod) ) 26 | for m in rems : 27 | if pow(m,3,p) != c : 28 | print('%d = m^3 mod %d, m has no integer solutions.' % (c,p) ) 29 | exit() 30 | return rems 31 | 32 | 33 | if __name__ == '__main__': 34 | 35 | # Debug 36 | # p = random_prime(2^128-1,False,2^127) 37 | # x = randint(2^127,2^128) % p 38 | # c = pow(x, 3, p) 39 | # print(f"{x}^{3} mod {p} = {c}") 40 | # rems = cube_roots(c%p, p) 41 | # print(f'x = {rems}') 42 | # assert x in rems 43 | 44 | print("x^3 mod p = c") 45 | c = int(input("c = ")) 46 | p = int(input("p = ")) 47 | rems = cube_roots(c%p,p) 48 | print(f"x = {rems}") 49 | -------------------------------------------------------------------------------- /scripts/RSA/partial_p_msb.sage: -------------------------------------------------------------------------------- 1 | "Find P with only knowning 50% of MSB (least significant bit)" 2 | 3 | def recover_lsb(n, p, debug=False) : 4 | beta = 0.5 5 | epsilon = beta^2/7 6 | kbits = floor(n.nbits()*(beta^2-epsilon)) 7 | PR. = PolynomialRing(Zmod(n)) 8 | f = x + p 9 | x0 = f.small_roots(X=2^kbits, beta=0.3)[0] # find root < 2^kbits with factor >= n^0.3 10 | return x0 11 | 12 | if __name__ == "__main__": 13 | # # Testing 14 | # p = random_prime(2^512-1,True,2^511) 15 | # q = random_prime(2^512-1,True,2^511) 16 | # n = p*q 17 | # pbits = p.nbits() 18 | # kbits = floor(n.nbits()*(beta^2-epsilon)) 19 | # partial_p = p & (2^pbits-2^kbits) 20 | # print("upper %d bits (of %d bits) given" % (pbits-kbits, pbits)) 21 | # x0 = recover_lsb(n, partial_p) 22 | 23 | # Usage 24 | n = 144577323082341606781087333127652195614928653924628840063283124688666697172079299540987986466905508888459466234427758008685453349603672093268364681219809070052759188387414913364503551677980960440032525534198537772481074574240349700392333150907937512241296276227852496435058553681077786863331924405426219248647 25 | partial_p = 11043285040234897370108230348414076720909958796181348046213933603334639323065878778927432781023976397098528035583181448962487564184116786691066219045322752 26 | x0 = recover_lsb(n, partial_p) 27 | print(f"known: {partial_p}") 28 | print(f"Missing LSB: {x0}") 29 | print(f"p = {x0 + partial_p}") 30 | print(f"isPrime: {is_prime(x0 + partial_p)}") -------------------------------------------------------------------------------- /scripts/Cryptosystem/SchmidtSamoa.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import GCD, inverse, getPrime 2 | 3 | lcm = lambda a, b: a * b // GCD(a, b) 4 | 5 | class SchmidtSamoa: 6 | """ 7 | ref: https://en.wikipedia.org/wiki/Schmidt-Samoa_cryptosystem 8 | 9 | SchmidtSamoa(tup) : 10 | @tup : (n,) for public key, n=p*p*q 11 | : (n,p,q) for private key, generate (d,module) 12 | 13 | """ 14 | def __init__(self,tup): 15 | "Choose two large distinct primes p and q and compute N = (p**2) * q" 16 | if len(tup) == 1 : 17 | self.pubkey = tup[0] 18 | self.privkey = None 19 | elif len(tup) == 3 : 20 | n, p, q = tup 21 | assert n == pow(p,2)*q, 'Input private key raise error.' 22 | self.pubkey = n 23 | self.privkey = (inverse(self.pubkey, lcm(p-1,q-1)), p*q) 24 | else : 25 | raise ValueError('SchmidtSamoa(tup) : \n\t@tup : \n\t\t(n) for public key \n\t\t(n,p,q) for private key') 26 | 27 | def encrypt(self,m): 28 | "c = m**N % N ( N = p**2 * q)" 29 | return pow(m,self.pubkey,self.pubkey) 30 | 31 | def decrypt(self,c): 32 | """ 33 | m = c**d % pq 34 | """ 35 | if self.privkey : 36 | d , module = self.privkey 37 | return pow(c,d,module) 38 | else : 39 | raise ValueError('Could not decrypt without privkey.') 40 | 41 | @classmethod 42 | def generate(cls,bits): 43 | p = getPrime(bits//3) 44 | q = getPrime(bits//3+ bits%3) 45 | n = p * p * q 46 | return cls((n,p,q)) 47 | 48 | if __name__ == "__main__": 49 | schmidtSamoa = SchmidtSamoa.generate(1024) 50 | m = getPrime(500) 51 | c = schmidtSamoa.encrypt(m) 52 | assert m == schmidtSamoa.decrypt(c) -------------------------------------------------------------------------------- /scripts/Cryptosystem/Paillier.py: -------------------------------------------------------------------------------- 1 | import random 2 | from Crypto.Util.number import getPrime, getRandomRange, inverse, GCD 3 | 4 | class Paillier: 5 | """ 6 | ref: https://en.wikipedia.org/wiki/Paillier_cryptosystem 7 | 8 | Paillier(tup) : 9 | @tup : (n,g) for public key, n=p*q, g is a random integer in field n*n 10 | : (n,g,p,q) for private key, generate (mu, λ) 11 | 12 | """ 13 | def __init__(self,tup): 14 | "Choose two large distinct primes p and q and compute N = (p**2) * q" 15 | if len(tup) == 2 : 16 | self.pubkey = tup 17 | elif len(tup) == 4 : 18 | n, g = tup[:2] 19 | p, q = tup[2:] 20 | assert n == p*q, 'Input private key raise error.' 21 | self.pubkey = (n,g) 22 | λ = (p-1)*(q-1) 23 | mu = inverse(self._L(pow(g,λ,n*n)),n) 24 | self.privkey = (n,λ,mu) 25 | else : 26 | raise ValueError('Paillier(tup) : \n\t@tup : \n\t\t(n,g) for public key \n\t\t(n,g,p,q) for private key') 27 | 28 | def _L(self, u) : 29 | return (u - 1) // self.pubkey[0] 30 | 31 | def encrypt(self,m): 32 | """ 33 | return (g^m * r^n) % n^2 34 | """ 35 | n,g = self.pubkey 36 | mods = n*n 37 | gm = pow(g,m,mods) 38 | r = 0 39 | while GCD(r,n) != 1 : 40 | r = random.randint(0,n-1) 41 | return (gm * pow(r,n,mods)) % mods 42 | 43 | def decrypt(self, c): 44 | n,λ,mu = self.privkey 45 | mes = pow(c,λ,n*n) 46 | mes = self._L(mes)*mu % n 47 | return mes 48 | 49 | @classmethod 50 | def generate(cls,bits): 51 | p = getPrime(bits//2) 52 | q = getPrime(bits//2+ bits%2) 53 | g = getRandomRange(0, (p*q)**2) 54 | n = p * q 55 | return cls((n,g,p,q)) 56 | 57 | if __name__ == "__main__": 58 | 59 | paillier = Paillier.generate(1024) 60 | m = getPrime(1000) 61 | c = paillier.encrypt(m) 62 | assert m == paillier.decrypt(c) -------------------------------------------------------------------------------- /scripts/RSA/partial_p_lsb.sage: -------------------------------------------------------------------------------- 1 | "Find P with only knowning 50% of LSB (least significant bit)" 2 | 3 | def recover_msb(n, p, known_bits, debug=False) : 4 | beta = 0.5 5 | epsilon = beta^2/7 6 | PR. = PolynomialRing(Zmod(n)) 7 | f = x*(2^kbits) + p 8 | f = f.monic() 9 | x = f.small_roots(X=2^(pbits-kbits), beta=0.3)[0] # find root < 2^kbits with factor >= n^0.3 10 | return x 11 | 12 | if __name__ == "__main__": 13 | # # # Testing 14 | # p = random_prime(2^512-1,True,2^511) 15 | # q = random_prime(2^512-1,True,2^511) 16 | # n = p*q 17 | # pbits = p.nbits() 18 | # kbits = 300 19 | # partial_p = p & (2^kbits-1) 20 | # print(f"known p : {partial_p}, {len(bin(partial_p))}") 21 | # print("lower %d bits (of %d bits) is given" % (kbits, pbits)) 22 | # print(partial_p) 23 | # x = recover_msb(n, partial_p, kbits) 24 | # print ((x[0]<= 3 : 23 | self.n = n 24 | self.g = g 25 | self.h = h 26 | self.pubkey = (n,g,h) 27 | if len(tup) == 5 : 28 | p, q = tup[3:5] 29 | self.p = p 30 | self.q = q 31 | assert n == pow(p,2) * q, 'Input private key raise error.' 32 | self.privkey = (g,p,q) 33 | else : 34 | raise ValueError('OkamotoUchiyama(tup) : \n\t@tup : \n\t\t(n,g,h) for public key \n\t\t(n,g,h,p,q) for private key') 35 | 36 | def encrypt(self,m): 37 | """ 38 | return (g ** m * h ** r) % n 39 | """ 40 | n,g,h = self.pubkey 41 | r = getRandomRange(1,self.n-1) 42 | return pow(g,m,n) * pow(h,r,n) % n 43 | 44 | def decrypt(self,c): 45 | g,p,q = self.privkey 46 | return self.logarithm(pow(c,p-1,p**2)) * inverse(self.logarithm(pow(g,p-1,p**2)),p)% p 47 | 48 | def logarithm(self,x): 49 | 'return L(X) = (X-1) // p' 50 | return (x-1) // self.p 51 | 52 | @classmethod 53 | def generate(cls,bits): 54 | p = getPrime(bits//3) 55 | q = getPrime(bits//3+ bits%3) 56 | n = p**2 * q 57 | while True: 58 | g = getRandomRange(1, n-1) 59 | g_p = pow(g, p-1, p**2) 60 | if pow(g_p, p, p**2) == 1: 61 | break 62 | h = pow(g,n,n) 63 | return cls((n,g,h,p,q)) 64 | 65 | if __name__ == "__main__": 66 | okamotoUchiyama = OkamotoUchiyama.generate(1024) 67 | m = getPrime(333) 68 | c = okamotoUchiyama.encrypt(m) 69 | assert m == okamotoUchiyama.decrypt(c) -------------------------------------------------------------------------------- /scripts/RSA/LSBOracle.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class LSBOracle: 4 | 5 | def __init__(self, n, c, e, oracle_modulus=2): 6 | """ 7 | n is the module, 8 | """ 9 | self.upper_bound = n 10 | self.lower_bound = 0 11 | self.n = n 12 | self.e = e 13 | self.c = c 14 | self.counter = 0 15 | self.modulus = oracle_modulus 16 | 17 | def set_modulus(self, oracle_modulus): 18 | self.modulus = oracle_modulus 19 | 20 | def update_bound(self, bits_val): 21 | jump = self.modulus 22 | for i in range(jump): 23 | if bits_val == ((-self.n * i) % jump) : 24 | upper_bound = self.upper_bound 25 | lower_bound = self.lower_bound 26 | self.upper_bound = lower_bound + ((upper_bound - lower_bound) * (i + 1) // jump + 1) 27 | self.lower_bound = lower_bound + ((upper_bound - lower_bound) * i // jump) 28 | print(f'bound: {self.lower_bound} ~ {self.upper_bound})') 29 | 30 | def get_bound(self): 31 | return (self.upper_bound,self.lower_bound) 32 | 33 | def set_bound(self,bound): 34 | self.upper_bound, self.lower_bound = bound 35 | 36 | def history(self): 37 | return self.history 38 | 39 | def start(self): 40 | mul = pow(self.modulus, self.e, self.n) 41 | try : 42 | for _ in range(self.counter, int(math.log(self.n, self.modulus))): 43 | c = (mul * self.c) % self.n 44 | bits_val = self.oracle(c) 45 | self.update_bound(bits_val) 46 | self.c = c 47 | self.counter += self.modulus 48 | except Exception as e: 49 | print(e) 50 | print("Something stop Finding ...") 51 | print(f'bound: {self.lower_bound} ~ {self.upper_bound})') 52 | 53 | def oracle(self, c): 54 | raise NotImplementedError 55 | 56 | 57 | if __name__ == "__main__": 58 | 59 | # Example 60 | 61 | from pwn import * 62 | 63 | r = remote('localhost', 10000) 64 | n = int(r.recvline().strip(b'n = ').strip()) 65 | c = int(r.recvline().strip(b'c = ').strip()) 66 | e = 65537 67 | 68 | def oracle(c): 69 | r.sendline(str(c)) 70 | return int(r.recvline().strip(b'm % 3 = ').strip()) 71 | 72 | lsb = LSBOracle(n,c,e,3) 73 | lsb.oracle = oracle 74 | lsb.start() -------------------------------------------------------------------------------- /scripts/factorization/factordb.py: -------------------------------------------------------------------------------- 1 | 2 | import requests 3 | from bs4 import BeautifulSoup 4 | from functools import reduce 5 | 6 | def factordb(n): 7 | 8 | url = 'http://factordb.com/index.php?query={}'.format(n) 9 | td = BeautifulSoup(requests.get(url).text,'html.parser').select('td') 10 | states = list(td[11].strings)[0].strip(" ") 11 | print({ 12 | 'C' : "[ ] Composite, Still no factors known.", 13 | "CF" : "[ ] Composite, factors known, If number is small, You can try it again.", 14 | 'FF' : "[+] Composite, fully factored", 15 | "P" : "[+] Definitely prime", 16 | "PRP" : "[ ] Probably prime", 17 | "U" : "[-] Factordb Search Failed", 18 | "Unit" : "[-] 1 is nothing.", 19 | "N" : "This number is not in database (and was not added due to your settings)" 20 | }[states]) 21 | 22 | if not states in ['FF','P','PRP','CF'] : 23 | return None 24 | 25 | factor = '' 26 | ss = list(td[13].strings) 27 | for i,s in enumerate(ss): 28 | if ('.' in s) : 29 | for a in td[13].select('a') : 30 | if s == a.string: 31 | temp1 = requests.get('http://factordb.com/'+ a['href']) 32 | tsoup = BeautifulSoup(temp1.text,'html.parser') 33 | temp2 = requests.get('http://factordb.com/'+ tsoup.select('td')[12].a['href']) 34 | tsoup2 = BeautifulSoup(temp2.text,'html.parser') 35 | for dnum in tsoup2.select('td')[-1].strings: 36 | factor += dnum.strip('\n') 37 | break 38 | 39 | elif s[0] != '<' : 40 | factor += s 41 | else : 42 | pass 43 | 44 | pair = {} 45 | 46 | _, factors = factor.split(' = ') 47 | if ')^' in factors : 48 | factors, exp = factors.split('^') 49 | factors = factors.strip('\(').strip('\)') 50 | pair = { x:int(exp) for x in list(map(int,factors.split(' · ')))} 51 | elif '^' in factors : 52 | for f in factors.split(' · '): 53 | if '^' in f : 54 | num,exp = f.split('^') 55 | pair[int(num)] = int(exp) 56 | else : 57 | pair[int(f)] = int(1) 58 | else : 59 | pair = { x:1 for x in list(map(int,factors.split(' · ')))} 60 | 61 | assert n == reduce( lambda x, y: x*y, [pow(k,v) for k,v in pair.items()],1), 'factordb function failed....' 62 | return pair 63 | 64 | if __name__ == "__main__": 65 | print(factordb(1983429174039147129347801923749127489127048971930471289374019273491)) -------------------------------------------------------------------------------- /scripts/factorization/williams_pp1.py: -------------------------------------------------------------------------------- 1 | from math import gcd, isqrt, log 2 | from Crypto.Util.number import isPrime 3 | 4 | 5 | def primegen(): 6 | # From primefac 7 | """ 8 | Generates primes lazily via the sieve of Eratosthenes 9 | Input: none 10 | Output: 11 | Sequence of integers 12 | Examples: 13 | >>> list(takewhile(lambda x: x < 100, primegen())) 14 | [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97] 15 | """ 16 | yield 2; yield 3; yield 5; yield 7; yield 11; yield 13 17 | ps = primegen() # yay recursion 18 | p = next(ps) and next(ps) 19 | q, sieve, n = p**2, {}, 13 20 | while True: 21 | if n not in sieve: 22 | if n < q: yield n 23 | else: 24 | _next, step = q + 2*p, 2*p 25 | while _next in sieve: _next += step 26 | sieve[_next] = step 27 | p = next(ps) 28 | q = p**2 29 | else: 30 | step = sieve.pop(n) 31 | _next = n + step 32 | while _next in sieve: _next += step 33 | sieve[_next] = step 34 | n += 2 35 | 36 | def williams_pp1(n): 37 | """ 38 | Williams' p+1 integer factoring algorithm 39 | Input: 40 | n -- integer to factor 41 | Output: 42 | Integer. A nontrivial factor of n. 43 | Example: 44 | >>> williams_pp1(315951348188966255352482641444979927) 45 | 12403590655726899403 46 | """ 47 | factors = [] 48 | counter = 0 49 | if isPrime(n) : return n 50 | while True: 51 | v = counter 52 | for p in primegen(): 53 | e = int(log(isqrt(n), p)) 54 | if e == 0: break 55 | for _ in range(e): 56 | # Multiplies along a Lucas sequence modulo n 57 | v1, v2 = v, (v**2 - 2) % n 58 | for bit in bin(p)[3:]: 59 | if bit == "0" : 60 | v1, v2 = ((v1**2 - 2) % n, (v1*v2 - v) % n) 61 | else : 62 | v1, v2 = ((v1*v2 - v) % n, (v2**2 - 2) % n) 63 | v = v1 64 | g = gcd(v - 2, n) 65 | if 1 < g < n: 66 | if gcd(n, g) != 1 : 67 | n = n//g 68 | print(f'factor found :{g}') 69 | factors.append(g) 70 | if isPrime(n) : 71 | factors.append(n) 72 | return factors 73 | if n == 1: return factors 74 | if g == n: break 75 | counter += 1 76 | v = counter 77 | 78 | 79 | if __name__ == "__main__": 80 | import random 81 | from functools import reduce 82 | from operator import mul 83 | ps = primegen() 84 | small_primes = [next(ps) for _ in range(1000)] 85 | while True : 86 | p = reduce(mul,[random.choice(small_primes) for i in range(50)]) - 1 87 | if isPrime(p) : 88 | break 89 | while True : 90 | q = reduce(mul,[random.choice(small_primes) for i in range(50)]) - 1 91 | if isPrime(q) : 92 | break 93 | n = p*q 94 | print(f"p = {p}") 95 | print(f"q = {q}") 96 | print(williams_pp1(p*q)) -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Function 2 | 3 | ### XOR function 4 | 5 | ```python 6 | xor = lambda a, b: bytes(ai ^ bi for ai, bi in zip(a, b)) 7 | ``` 8 | 9 | ## Hash 10 | 11 | * [Length extension attack (LEA)](https://gist.github.com/maojui/9b078194c43835e2947b39192f0537c1) 12 | * [SHA-1 Collision](https://gist.github.com/maojui/b33420c5b27f250fe94ec7e085ca4c42) 13 | 14 | ## RSA 15 | 16 | 17 | ### Factorize modular 18 | 19 | #### Tools 20 | 21 | * [FactorDB](http://factordb.com/) 22 | * [Integer factorization calculator](https://www.alpertron.com.ar/ECM.HTM) 23 | * [YAFU Docker (Yet Another Factoring Utility)](https://hub.docker.com/r/eyjhb/yafu) 24 | 25 | #### Scripts 26 | 27 | * [factordb API](./scripts/factorization/factordb.py) - API for getting well-known prime in FactorDB 28 | * [Pollard's rho](./scripts/factorization/pollard.py) - Efficient factor N when (p - 1) is smooth 29 | * [William's p+1](./scripts/factorization/pollard.py) - Efficient factor N when (p + 1) is smooth 30 | * [Fermat factorization](./scripts/factorization/fermat_factorization.py) - Fermat Factorization : when p, q are close. 31 | * [Factors a number using the Elliptic Curve Method](https://github.com/martingkelly/pyecm/blob/master/pyecm.py) 32 | 33 | ### with low exponent 34 | 35 | * [Hastad's Broadcast](./scripts/RSA/hastad_broadcast.py) - 36 | * [Bleichenbacher](http://www.dsi.unive.it/~focardi/RSA-padding-oracle/) 37 | 38 | ### with huge exponent 39 | 40 | * [Boneh and Durfee Attack](https://gist.github.com/maojui/fa9b5dfd37460bfacff65a7e65eaa177) 41 | * [Wiener Attack](https://gist.github.com/maojui/fad0a9a9899de482a66f08ffa7f4d510) 42 | 43 | ### Others attack 44 | 45 | * [Common Modular Attack](./scripts/RSA/fermat_factorization.py) 46 | 47 | ### Factoring with Partial Information 48 | 49 | * [Partial Prime](https://gist.github.com/maojui/8bf7b9d76c52c049286025e8de2ba1a8) 50 | * [Partial Private Key](https://gist.github.com/maojui/bd55d98d310bab770a6a0681078b444e) 51 | 52 | ## SSL certificate using python 53 | 54 | ```python 55 | from Crypto.PublicKey import RSA 56 | key = RSA.importKey(open('publickey.pem','r').read()) 57 | ``` 58 | 59 | ### Converting Using OpenSSL 60 | 61 | Source : [stackoverflow](https://stackoverflow.com/questions/13732826/convert-pem-to-crt-and-key) 62 | 63 | Below commands allow you to convert certificates and keys to different formats to make them compatible with specific types of servers or software. 64 | 65 | * Convert a PEM file to (.der, .crt) 66 | 67 | ``` 68 | openssl x509 -outform der -in certificate.pem -out certificate.der # DER 69 | openssl x509 -outform der -in certificate.pem -out certificate.crt # CRT 70 | openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt -certfile CACert.crt # PFX, P12 71 | openssl crl2pkcs7 -nocrl -certfile certificate.cer -out certificate.p7b -certfile CACert.cer # P7B 72 | ``` 73 | 74 | * Convert a DER file (.crt .cer .der) to PEM 75 | 76 | ``` 77 | openssl x509 -inform der -in certificate.crt -out certificate.pem 78 | openssl x509 -inform der -in certificate.cer -out certificate.pem 79 | openssl x509 -inform der -in certificate.der -out certificate.pem 80 | ``` 81 | 82 | * Convert a PKCS#12 file (.pfx .p12) containing a private key and certificates to PEM 83 | 84 | ``` 85 | openssl pkcs12 -in keyStore.pfx -out keyStore.pem -nodes 86 | ``` 87 | 88 | ## Cryptosystem 89 | 90 | * [Paillier](./scripts/cryptosystem/Paillier.py) 91 | * [SchmidtSamoa](./scripts/cryptosystem/SchmidtSamoa.py) 92 | * [OkamotoUchiyama](./scripts/cryptosystem/OkamotoUchiyama.py) 93 | 94 | ## NTRU 95 | 96 | * [NTRU with weak parameters](https://gist.github.com/maojui/0bab62c95979fe0ff7dcd67e55d1d6f4) 97 | 98 | ## Mathmatics 99 | 100 | * [Find nth_roots on modulo p](https://gist.github.com/maojui/61c10d93db220e9c77b20d274969e363) 101 | * [Chinese remainder theorem](./scripts/Mathmathics/crt.py) 102 | -------------------------------------------------------------------------------- /scripts/factorization/pollard.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import GCD, isPrime 2 | 3 | def primegen(): 4 | """ 5 | Generates primes lazily via the sieve of Eratosthenes 6 | Input: none 7 | Output: 8 | Sequence of integers 9 | Examples: 10 | >>> list(takewhile(lambda x: x < 100, primegen())) 11 | [2, 3, 5, 7, 11, 13, 8, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97] 12 | """ 13 | yield 2; yield 3; yield 5; yield 7; yield 11; yield 13 14 | ps = primegen() # yay recursion 15 | p = next(ps) and next(ps) 16 | q, sieve, n = p**2, {}, 13 17 | while True: 18 | if n not in sieve: 19 | if n < q: yield n 20 | else: 21 | _next, step = q + 2*p, 2*p 22 | while _next in sieve: _next += step 23 | sieve[_next] = step 24 | p = next(ps) 25 | q = p**2 26 | else: 27 | step = sieve.pop(n) 28 | _next = n + step 29 | while _next in sieve: _next += step 30 | sieve[_next] = step 31 | n += 2 32 | 33 | def pollard_rho(N, f=None): 34 | """ 35 | Pollard's rho algorithm can fail for some inputs. 36 | Simply change the f or seed values if it fails. 37 | """ 38 | x, y, d = 2, 2, 1 39 | if f == None : 40 | f = lambda x: (x*x+1) % N 41 | tmp = [] 42 | while d <= 1: 43 | x = f(x) 44 | if not x in tmp : 45 | tmp.append(x) 46 | else: 47 | break 48 | y = f(f(y)) 49 | d = GCD(x - y, N) 50 | if d != 1 : 51 | if 1 < d < N : return d 52 | if d == N : return None 53 | return d 54 | 55 | def pollard_pm1(N): 56 | """ 57 | For one of N's prime p, 58 | 1. If (p-1) is a K-smooth number. (k is small) 59 | 2. When all (p-1)'s factor were iterated by b 60 | Then it can be factorization by pollard_pm1. 61 | 62 | ** It may failed if the degree of (p-1)'s factors is greater than 1. ** 63 | """ 64 | if isPrime(N): 65 | return N 66 | a = 2 67 | primes = primegen() 68 | for b in primes : 69 | try : 70 | a = pow(a, b, N) 71 | p = GCD(a - 1, N) 72 | if 1 < p < N: 73 | return p 74 | except : 75 | print("Pollard P-1 Failed.") 76 | return 0 77 | 78 | def pollard_brute(N): 79 | """ 80 | For one of N's prime p, 81 | 1. If (p-1) is a K-smooth number. (k is small) 82 | 2. When all (p-1)'s factor were iterated by b 83 | Then it can be factorization by pollard_pm1. 84 | """ 85 | a = 2 86 | b = 1 87 | factors = [] 88 | if isPrime(N) : return N 89 | try : 90 | while True: 91 | a = pow(a, b, N) 92 | p = GCD(a - 1, N) 93 | if 1 < p < N: 94 | factors.append(p) 95 | print(f"factor found: {p}") 96 | q = N // p 97 | if isPrime(q) : 98 | print(f"factor found: {q}") 99 | factors.append(q) 100 | return factors 101 | b += 1 102 | except: 103 | return factors 104 | 105 | 106 | if __name__ == "__main__": 107 | 108 | import random 109 | from functools import reduce 110 | from operator import mul 111 | ps = primegen() 112 | small_primes = [next(ps) for _ in range(1000)] 113 | while True : 114 | ps = [random.choice(small_primes) for i in range(30)] 115 | p = reduce(mul, ps) + 1 116 | if isPrime(p) : 117 | break 118 | while True : 119 | qs = [random.choice(small_primes) for i in range(30)] 120 | q = reduce(mul,qs) + 1 121 | if isPrime(q) : 122 | break 123 | n = p*q 124 | 125 | 126 | print(f"p = {p}") 127 | print(f"q = {q}") 128 | print(pollard_brute(p*q)) 129 | print(pollard_pm1(p*q)) 130 | print(pollard_rho(455459)) -------------------------------------------------------------------------------- /scripts/Hash/sha1_collision.py: -------------------------------------------------------------------------------- 1 | #! coding:utf-8 2 | import os 3 | from PIL import Image 4 | from hashlib import sha1 5 | from Crypto.Util.number import long_to_bytes as n2s 6 | 7 | def sha1_collision(image1,image2) : 8 | ''' 9 | Usage: input two image, open in bytes or just a string path 10 | 11 | Original : sonickun/sha1-collider, https://github.com/sonickun/sha1-collider/blob/master/collider.py 12 | ''' 13 | if type(image1) == str : 14 | img1 = open(image1, "rb").read() 15 | elif type(image1) == bytes : 16 | img1 = image1 17 | images1 = 'temp1' 18 | else : 19 | assert False, "Unknown type" 20 | 21 | if type(image2) == str : 22 | img2 = open(image2, "rb").read() 23 | elif type(image2) == bytes : 24 | img2 = image2 25 | images2 = 'temp2' 26 | else : 27 | assert False, "Unknown type" 28 | 29 | # Check JPEG format 30 | if s2n(img1[:2]) != 0xffd8 or s2n(img2[:2]) != 0xffd8: 31 | print("Image is not JPEG format.") 32 | sys.exit(1) 33 | 34 | size1 = Image.open(image1).size 35 | size2 = Image.open(image2).size 36 | print("Image size:", size1) 37 | 38 | # Resize the image if different sizes 39 | if size1 != size2 : 40 | new = Image.open(image2).resize(size1) 41 | new.save('temp.jpg') 42 | img2 = open('temp.jpg', "rb").read() 43 | os.remove('temp.jpg') 44 | print("Resized:", image2) 45 | 46 | pdf_header = n2s(0x255044462D312E330A25E2E3CFD30A0A0A312030206F626A0A3C3C2F57696474682032203020522F4865696768742033203020522F547970652034203020522F537562747970652035203020522F46696C7465722036203020522F436F6C6F7253706163652037203020522F4C656E6774682038203020522F42697473506572436F6D706F6E656E7420383E3E0A73747265616D0A) 47 | jpg_header = n2s(0xFFD8FFFE00245348412D3120697320646561642121212121852FEC092339759C39B1A1C63C4C97E1FFFE01) 48 | 49 | # Collision blocks (This is the only part of the files which is different) 50 | collision_block1 = n2s(0x7F46DC93A6B67E013B029AAA1DB2560B45CA67D688C7F84B8C4C791FE02B3DF614F86DB1690901C56B45C1530AFEDFB76038E972722FE7AD728F0E4904E046C230570FE9D41398ABE12EF5BC942BE33542A4802D98B5D70F2A332EC37FAC3514E74DDC0F2CC1A874CD0C78305A21566461309789606BD0BF3F98CDA8044629A1) 51 | collision_block2 = n2s(0x7346DC9166B67E118F029AB621B2560FF9CA67CCA8C7F85BA84C79030C2B3DE218F86DB3A90901D5DF45C14F26FEDFB3DC38E96AC22FE7BD728F0E45BCE046D23C570FEB141398BB552EF5A0A82BE331FEA48037B8B5D71F0E332EDF93AC3500EB4DDC0DECC1A864790C782C76215660DD309791D06BD0AF3F98CDA4BC4629B1) 52 | 53 | prefix1 = pdf_header + jpg_header + collision_block1 54 | prefix2 = pdf_header + jpg_header + collision_block2 55 | 56 | data = b'' 57 | data += b'\x00' * 242 58 | 59 | # JPEG comment 60 | n = (8 + len(img1)) 61 | data += b'\xff\xfe' + n2s(n>>8) + n2s(n & 0xff) 62 | 63 | data += b'\x00' * 8 64 | data += img1[2:] 65 | data += img2[2:] 66 | data += b'endstream\nendobj\n\n' 67 | 68 | # Cross-reference Table 69 | xref = b'xref\n' 70 | xref += b'0 13 \n' 71 | xref += b'0000000000 65535 f \n' 72 | xref += b'0000000017 00000 n \n' 73 | xref += b'%010d 00000 n \n' % len(prefix1+data) 74 | 75 | # width 76 | data += b'2 0 obj\n%010d\nendobj\n\n' % size1[0] 77 | xref += b'%010d 00000 n \n' % len(prefix1+data) 78 | 79 | # height 80 | data += b'3 0 obj\n%010d\nendobj\n\n' % size1[1] 81 | xref += b'%010d 00000 n \n' % len(prefix1+data) 82 | data += b'4 0 obj\n/XObject\nendobj\n\n' 83 | xref += b'%010d 00000 n \n' % len(prefix1+data) 84 | data += b'5 0 obj\n/Image\nendobj\n\n' 85 | xref += b'%010d 00000 n \n' % len(prefix1+data) 86 | data += b'6 0 obj\n/DCTDecode\nendobj\n\n' 87 | xref += b'%010d 00000 n \n' % len(prefix1+data) 88 | data += b'7 0 obj\n/DeviceRGB\nendobj\n\n' 89 | xref += b'%010d 00000 n \n' % len(prefix1+data) 90 | 91 | # JPEG size 92 | data += b'8 0 obj\n%010d\nendobj\n\n' % len(img1+img2) 93 | xref += b'%010d 00000 n \n' % len(prefix1+data) 94 | data += b'9 0 obj\n<<\n /Type /Catalog\n /Pages 10 0 R\n>>\nendobj\n\n' 95 | xref += b'%010d 00000 n \n' % len(prefix1+data) 96 | data += b'10 0 obj\n<<\n /Type /Pages\n /Count 1\n /Kids [11 0 R]\n>>\nendobj\n\n' 97 | xref += b'%010d 00000 n \n' % len(prefix1+data) 98 | data += b'11 0 obj\n<<\n /Type /Page\n /Parent 10 0 R\n /MediaBox [0 0 %010d %010d]\n /CropBox [0 0 %010d %010d]\n /Contents 12 0 R\n /Resources\n <<\n /XObject <>\n >>\n>>\nendobj\n\n' % (size1[0], size1[1], size1[0], size1[1]) 99 | xref += b'%010d 00000 n \n' % len(prefix1+data) 100 | data += b'12 0 obj\n<>\nstream\nq\n %010d 0 0 %010d 0 0 cm\n /Im0 Do\nQ\nendstream\nendobj\n\n' % (size1[0], size1[1]) 101 | 102 | xref_pos = len(prefix1 + data) 103 | data += xref 104 | trailer = b'\ntrailer << /Root 9 0 R /Size 13>>\n\nstartxref\n%010d\n%%%%EOF\n' % xref_pos 105 | 106 | data += trailer 107 | 108 | outfile1 = prefix1 + data 109 | outfile2 = prefix2 + data 110 | 111 | # Check SHA-1 collision 112 | digest1 = sha1(outfile1).hexdigest() 113 | digest2 = sha1(outfile2).hexdigest() 114 | 115 | assert digest1 == digest2, "SHA1 Collision Failed." 116 | print(digest1,image1+"-collision.pdf") 117 | print(digest2,image2+"-collision.pdf") 118 | 119 | open(image1 + "-collision.pdf", "wb").write(outfile1) 120 | open(image2 + "-collision.pdf", "wb").write(outfile2) 121 | 122 | print("Successfully Generated Collision PDF !!!") 123 | print("SHA1 for collisions pdf : %s" % digest1) 124 | 125 | return True 126 | -------------------------------------------------------------------------------- /scripts/RSA/boneh_durfee.sage: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | # Original: https://github.com/mimoo/RSA-and-LLL-attacks/blob/master/boneh_durfee.sage 4 | 5 | ############################################ 6 | # Config 7 | ########################################## 8 | 9 | """ 10 | Setting debug to true will display more informations 11 | about the lattice, the bounds, the vectors... 12 | """ 13 | debug = True 14 | 15 | """ 16 | Setting strict to true will stop the algorithm (and 17 | return (-1, -1)) if we don't have a correct 18 | upperbound on the determinant. Note that this 19 | doesn't necesseraly mean that no solutions 20 | will be found since the theoretical upperbound is 21 | usualy far away from actual results. That is why 22 | you should probably use `strict = False` 23 | """ 24 | strict = False 25 | 26 | """ 27 | This is experimental, but has provided remarkable results 28 | so far. It tries to reduce the lattice as much as it can 29 | while keeping its efficiency. I see no reason not to use 30 | this option, but if things don't work, you should try 31 | disabling it 32 | """ 33 | helpful_only = True 34 | dimension_min = 7 # stop removing if lattice reaches that dimension 35 | 36 | ############################################ 37 | # Functions 38 | ########################################## 39 | 40 | # display stats on helpful vectors 41 | def helpful_vectors(BB, modulus): 42 | nothelpful = 0 43 | for ii in range(BB.dimensions()[0]): 44 | if BB[ii,ii] >= modulus: 45 | nothelpful += 1 46 | 47 | print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful") 48 | 49 | # display matrix picture with 0 and X 50 | def matrix_overview(BB, bound): 51 | for ii in range(BB.dimensions()[0]): 52 | a = ('%02d ' % ii) 53 | for jj in range(BB.dimensions()[1]): 54 | a += '0' if BB[ii,jj] == 0 else 'X' 55 | if BB.dimensions()[0] < 60: 56 | a += ' ' 57 | if BB[ii, ii] >= bound: 58 | a += '~' 59 | print(a) 60 | 61 | # tries to remove unhelpful vectors 62 | # we start at current = n-1 (last vector) 63 | def remove_unhelpful(BB, monomials, bound, current): 64 | # end of our recursive function 65 | if current == -1 or BB.dimensions()[0] <= dimension_min: 66 | return BB 67 | 68 | # we start by checking from the end 69 | for ii in range(current, -1, -1): 70 | # if it is unhelpful: 71 | if BB[ii, ii] >= bound: 72 | affected_vectors = 0 73 | affected_vector_index = 0 74 | # let's check if it affects other vectors 75 | for jj in range(ii + 1, BB.dimensions()[0]): 76 | # if another vector is affected: 77 | # we increase the count 78 | if BB[jj, ii] != 0: 79 | affected_vectors += 1 80 | affected_vector_index = jj 81 | 82 | # level:0 83 | # if no other vectors end up affected 84 | # we remove it 85 | if affected_vectors == 0: 86 | print("* removing unhelpful vector", ii) 87 | BB = BB.delete_columns([ii]) 88 | BB = BB.delete_rows([ii]) 89 | monomials.pop(ii) 90 | BB = remove_unhelpful(BB, monomials, bound, ii-1) 91 | return BB 92 | 93 | # level:1 94 | # if just one was affected we check 95 | # if it is affecting someone else 96 | elif affected_vectors == 1: 97 | affected_deeper = True 98 | for kk in range(affected_vector_index + 1, BB.dimensions()[0]): 99 | # if it is affecting even one vector 100 | # we give up on this one 101 | if BB[kk, affected_vector_index] != 0: 102 | affected_deeper = False 103 | # remove both it if no other vector was affected and 104 | # this helpful vector is not helpful enough 105 | # compared to our unhelpful one 106 | if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]): 107 | print("* removing unhelpful vectors", ii, "and", affected_vector_index) 108 | BB = BB.delete_columns([affected_vector_index, ii]) 109 | BB = BB.delete_rows([affected_vector_index, ii]) 110 | monomials.pop(affected_vector_index) 111 | monomials.pop(ii) 112 | BB = remove_unhelpful(BB, monomials, bound, ii-1) 113 | return BB 114 | # nothing happened 115 | return BB 116 | 117 | """ 118 | Returns: 119 | * 0,0 if it fails 120 | * -1,-1 if `strict=true`, and determinant doesn't bound 121 | * x0,y0 the solutions of `pol` 122 | """ 123 | def boneh_durfee_algorithm(pol, modulus, mm, tt, XX, YY): 124 | """ 125 | Boneh and Durfee revisited by Herrmann and May 126 | 127 | finds a solution if: 128 | * d < N^delta 129 | * |x| < e^delta 130 | * |y| < e^0.5 131 | whenever delta < 1 - sqrt(2)/2 ~ 0.292 132 | """ 133 | 134 | # substitution (Herrman and May) 135 | PR. = PolynomialRing(ZZ) 136 | Q = PR.quotient(x*y + 1 - u) # u = xy + 1 137 | polZ = Q(pol).lift() 138 | 139 | UU = XX*YY + 1 140 | 141 | # x-shifts 142 | gg = [] 143 | for kk in range(mm + 1): 144 | for ii in range(mm - kk + 1): 145 | xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk 146 | gg.append(xshift) 147 | gg.sort() 148 | 149 | # x-shifts list of monomials 150 | monomials = [] 151 | for polynomial in gg: 152 | for monomial in polynomial.monomials(): 153 | if monomial not in monomials: 154 | monomials.append(monomial) 155 | monomials.sort() 156 | 157 | # y-shifts (selected by Herrman and May) 158 | for jj in range(1, tt + 1): 159 | for kk in range(floor(mm/tt) * jj, mm + 1): 160 | yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk) 161 | yshift = Q(yshift).lift() 162 | gg.append(yshift) # substitution 163 | 164 | # y-shifts list of monomials 165 | for jj in range(1, tt + 1): 166 | for kk in range(floor(mm/tt) * jj, mm + 1): 167 | monomials.append(u^kk * y^jj) 168 | 169 | # construct lattice B 170 | nn = len(monomials) 171 | BB = Matrix(ZZ, nn) 172 | for ii in range(nn): 173 | BB[ii, 0] = gg[ii](0, 0, 0) 174 | for jj in range(1, ii + 1): 175 | if monomials[jj] in gg[ii].monomials(): 176 | BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY) 177 | 178 | # Prototype to reduce the lattice 179 | if helpful_only: 180 | # automatically remove 181 | BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1) 182 | # reset dimension 183 | nn = BB.dimensions()[0] 184 | if nn == 0: 185 | print("failure") 186 | return 0,0 187 | 188 | # check if vectors are helpful 189 | if debug: 190 | helpful_vectors(BB, modulus^mm) 191 | 192 | # check if determinant is correctly bounded 193 | det = BB.det() 194 | bound = modulus^(mm*nn) 195 | if det >= bound: 196 | print("We do not have det < bound. Solutions might not be found.") 197 | print("Try with highers m and t.") 198 | if debug: 199 | diff = (log(det) - log(bound)) / log(2) 200 | print("size det(L) - size e^(m*n) = ", floor(diff)) 201 | if strict: 202 | return -1, -1 203 | else: 204 | print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)") 205 | 206 | # display the lattice basis 207 | if debug: 208 | matrix_overview(BB, modulus^mm) 209 | 210 | # LLL 211 | if debug: 212 | print("optimizing basis of the lattice via LLL, this can take a long time") 213 | 214 | BB = BB.LLL() 215 | 216 | if debug: 217 | print("LLL is done!") 218 | 219 | # transform vector i & j -> polynomials 1 & 2 220 | if debug: 221 | print("looking for independent vectors in the lattice") 222 | found_polynomials = False 223 | 224 | for pol1_idx in range(nn - 1): 225 | for pol2_idx in range(pol1_idx + 1, nn): 226 | # for i and j, create the two polynomials 227 | PR. = PolynomialRing(ZZ) 228 | pol1 = pol2 = 0 229 | for jj in range(nn): 230 | pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY) 231 | pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY) 232 | 233 | # resultant 234 | PR. = PolynomialRing(ZZ) 235 | rr = pol1.resultant(pol2) 236 | 237 | # are these good polynomials? 238 | if rr.is_zero() or rr.monomials() == [1]: 239 | continue 240 | else: 241 | print("found them, using vectors", pol1_idx, "and", pol2_idx) 242 | found_polynomials = True 243 | break 244 | if found_polynomials: 245 | break 246 | 247 | if not found_polynomials: 248 | print("no independant vectors could be found. This should very rarely happen...") 249 | return 0, 0 250 | 251 | rr = rr(q, q) 252 | 253 | # solutions 254 | soly = rr.roots() 255 | 256 | if len(soly) == 0: 257 | print("Your prediction (delta) is too small") 258 | return 0, 0 259 | 260 | soly = soly[0][0] 261 | ss = pol1(q, soly) 262 | solx = ss.roots()[0][0] 263 | 264 | # 265 | return solx, soly 266 | 267 | def boneh_durfee(N, e, delta=.29, m=8): 268 | ############################################ 269 | # How To Use This Script 270 | ########################################## 271 | 272 | # 273 | # The problem to solve (edit the following values) 274 | # 275 | delta = .29 # this means that d < N^delta 276 | m = 8 # size of the lattice (bigger the better/slower) 277 | 278 | # 279 | # Lattice (tweak those values) 280 | # 281 | 282 | # you need to be a lattice master to tweak these 283 | t = int((1-2*delta) * m) # optimization from Herrmann and May 284 | X = 2*floor(N^delta) # this _might_ be too much 285 | Y = floor(N^(1/2)) # correct if p, q are ~ same size 286 | 287 | # 288 | # Don't touch anything below 289 | # 290 | 291 | # Problem put in equation 292 | P. = PolynomialRing(ZZ) 293 | A = int((N+1)/2) 294 | pol = 1 + x * (A + y) 295 | 296 | # 297 | # Find the solutions! 298 | # 299 | 300 | # Checking bounds 301 | if debug: 302 | print("=== checking values ===") 303 | print("* delta:", delta) 304 | print("* delta < 0.292", delta < 0.292) 305 | print("* size of e:", int(log(e)/log(2))) 306 | print("* size of N:", int(log(N)/log(2))) 307 | print("* m:", m, ", t:", t) 308 | 309 | # boneh_durfee 310 | if debug: 311 | print("=== running algorithm ===") 312 | start_time = time.time() 313 | 314 | solx, soly = boneh_durfee_algorithm(pol, e, m, t, X, Y) 315 | 316 | # found a solution? 317 | if solx > 0: 318 | print("=== solution found ===") 319 | if False: 320 | print("x:", solx) 321 | print("y:", soly) 322 | 323 | d = int(pol(solx, soly) / e) 324 | print("private key found:", d) 325 | else: 326 | print("=== no solution was found ===") 327 | 328 | if debug: 329 | print("=== %s seconds ===" % (time.time() - start_time)) 330 | 331 | if __name__ == "__main__": 332 | 333 | N = 0x665166804cd78e8197073f65f58bca14e019982245fcc7cad74535e948a4e0258b2e919bf3720968a00e5240c5e1d6b8831d8fec300d969fccec6cce11dde826d3fbe0837194f2dc64194c78379440671563c6c75267f0286d779e6d91d3e9037c642a860a894d8c45b7ed564d341501cedf260d3019234f2964ccc6c56b6de8a4f66667e9672a03f6c29d95100cdf5cb363d66f2131823a953621680300ab3a2eb51c12999b6d4249dde499055584925399f3a8c7a4a5a21f095878e80bbc772f785d2cbf70a87c6b854eb566e1e1beb7d4ac6eb46023b3dc7fdf34529a40f5fc5797f9c15c54ed4cb018c072168e9c30ca3602e00ea4047d2e5686c6eb37b9 334 | e = 0x2c998e57bc651fe4807443dbb3e794711ca22b473d7792a64b7a326538dc528a17c79c72e425bf29937e47b2d6f6330ee5c13bfd8564b50e49132d47befd0ee2e85f4bfe2c9452d62ef838d487c099b3d7c80f14e362b3d97ca4774f1e4e851d38a4a834b077ded3d40cd20ddc45d57581beaa7b4d299da9dec8a1f361c808637238fa368e07c7d08f5654c7b2f8a90d47857e9b9c0a81a46769f6307d5a4442707afb017959d9a681fa1dc8d97565e55f02df34b04a3d0a0bf98b7798d7084db4b3f6696fa139f83ada3dc70d0b4c57bf49f530dec938096071f9c4498fdef9641dfbfe516c985b27d1748cc6ce1a4beb1381fb165a3d14f61032e0f76f095d 335 | print(boneh_durfee(N,e, delta=.29, m=8)) 336 | --------------------------------------------------------------------------------