├── Parker - Elliptic Curves and Lenstra's Factorization.pdf ├── README.md └── lenstra.py /Parker - Elliptic Curves and Lenstra's Factorization.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delta003/lenstra_algorithm/HEAD/Parker - Elliptic Curves and Lenstra's Factorization.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lenstra's algorithm 2 | 3 | Lenstra's Factorization Algorithm using Elliptic Curves. Python implementation. 4 | 5 | Written for Cryptography class at Faculty of Computing in Belgrade (RAF). 6 | -------------------------------------------------------------------------------- /lenstra.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from random import randint 3 | from fractions import gcd 4 | 5 | 6 | # Sieve of Eratosthenes 7 | def primes(n): 8 | b = [True] * (n + 1) 9 | ps = [] 10 | for p in xrange(2, n + 1): 11 | if b[p]: 12 | ps.append(p) 13 | for i in xrange(p, n + 1, p): 14 | b[i] = False 15 | return ps 16 | 17 | 18 | # Finds modular inverse 19 | # Returns inverse, unused helper and gcd 20 | def modular_inv(a, b): 21 | if b == 0: 22 | return 1, 0, a 23 | q, r = divmod(a, b) 24 | x, y, g = modular_inv(b, r) 25 | return y, x - q * y, g 26 | 27 | 28 | # Addition in Elliptic curve modulo m space 29 | def elliptic_add(p, q, a, b, m): 30 | # If one point is infinity, return other one 31 | if p[2] == 0: return q 32 | if q[2] == 0: return p 33 | if p[0] == q[0]: 34 | if (p[1] + q[1]) % m == 0: 35 | return 0, 1, 0 # Infinity 36 | num = (3 * p[0] * p[0] + a) % m 37 | denom = (2 * p[1]) % m 38 | else: 39 | num = (q[1] - p[1]) % m 40 | denom = (q[0] - p[0]) % m 41 | inv, _, g = modular_inv(denom, m) 42 | # Unable to find inverse, arithmetic breaks 43 | if g > 1: 44 | return 0, 0, denom # Failure 45 | z = (num * inv * num * inv - p[0] - q[0]) % m 46 | return z, (num * inv * (p[0] - z) - p[1]) % m, 1 47 | 48 | 49 | # Multiplication (repeated addition and doubling) 50 | def elliptic_mul(k, p, a, b, m): 51 | r = (0, 1, 0) # Infinity 52 | while k > 0: 53 | # p is failure, return it 54 | if p[2] > 1: 55 | return p 56 | if k % 2 == 1: 57 | r = elliptic_add(p, r, a, b, m) 58 | k = k // 2 59 | p = elliptic_add(p, p, a, b, m) 60 | return r 61 | 62 | 63 | # Lenstra's algorithm for factoring 64 | # Limit specifies the amount of work permitted 65 | def lenstra(n, limit): 66 | g = n 67 | while g == n: 68 | # Randomized x and y 69 | q = randint(0, n - 1), randint(0, n - 1), 1 70 | # Randomized curve coefficient a, computed b 71 | a = randint(0, n - 1) 72 | b = (q[1] * q[1] - q[0] * q[0] * q[0] - a * q[0]) % n 73 | g = gcd(4 * a * a * a + 27 * b * b, n) # singularity check 74 | # If we got lucky, return lucky factor 75 | if g > 1: 76 | return g 77 | # increase k step by step until lcm(1, ..., limit) 78 | for p in primes(limit): 79 | pp = p 80 | while pp < limit: 81 | q = elliptic_mul(p, q, a, b, n) 82 | # Elliptic arithmetic breaks 83 | if q[2] > 1: 84 | return gcd(q[2], n) 85 | pp = p * pp 86 | return False 87 | 88 | 89 | # Command line tool 90 | def main(): 91 | parser = argparse.ArgumentParser(description = 'Process arguments') 92 | parser.add_argument('--n', type = int, 93 | help = 'number to factor') 94 | parser.add_argument('--limit', type = int, default = 1000, 95 | help = 'work limit (default = 1000)') 96 | args = parser.parse_args() 97 | print lenstra(args.n, args.limit) 98 | 99 | 100 | if __name__ == '__main__': 101 | main() 102 | --------------------------------------------------------------------------------