elliptic-curve_factorization_method.pl 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. #!/usr/bin/perl
  2. # The elliptic-curve factorization method (ECM), due to Hendrik Lenstra.
  3. # Algorithm presented in the below video:
  4. # https://www.youtube.com/watch?v=2JlpeQWtGH8
  5. # See also:
  6. # https://en.wikipedia.org/wiki/Lenstra_elliptic-curve_factorization
  7. use 5.020;
  8. use strict;
  9. use warnings;
  10. use Math::GMPz qw();
  11. use experimental qw(signatures);
  12. use ntheory qw(is_prime_power logint gcd);
  13. use Math::Prime::Util::GMP qw(primes invmod);
  14. sub ecm ($N, $zrange = 100, $plimit = 10000) {
  15. if (is_prime_power($N, \my $p)) {
  16. return $p;
  17. }
  18. my @primes = @{primes($plimit)};
  19. foreach my $z (-$zrange .. $zrange) {
  20. my $x = 0;
  21. my $y = 1;
  22. foreach my $p (@primes) {
  23. my $k = $p**logint($plimit, $p);
  24. my ($xn, $yn);
  25. my ($sx, $sy, $t) = ($x, $y, $k);
  26. my $first = 1;
  27. while ($t) {
  28. if ($t & 1) {
  29. if ($first) {
  30. ($xn, $yn) = ($sx, $sy);
  31. $first = 0;
  32. }
  33. else {
  34. my $u = invmod($sx - $xn, $N);
  35. if (not defined $u) {
  36. my $d = gcd($sx - $xn, $N);
  37. $d == $N ? last : return $d;
  38. }
  39. $u = Math::GMPz->new($u);
  40. my $L = ($u * ($sy - $yn)) % $N;
  41. my $xs = ($L * $L - $xn - $sx) % $N;
  42. $yn = ($L * ($xn - $xs) - $yn) % $N;
  43. $xn = $xs;
  44. }
  45. }
  46. my $u = invmod(2 * $sy, $N);
  47. if (not defined $u) {
  48. my $d = gcd(2 * $sy, $N);
  49. $d == $N ? last : return $d;
  50. }
  51. $u = Math::GMPz->new($u);
  52. my $L = ($u * (3 * $sx * $sx + $z)) % $N;
  53. my $x2 = ($L * $L - 2 * $sx) % $N;
  54. $sy = ($L * ($sx - $x2) - $sy) % $N;
  55. $sx = $x2;
  56. $sy || return $N;
  57. $t >>= 1;
  58. }
  59. ($x, $y) = ($xn, $yn);
  60. }
  61. }
  62. return $N; # failed
  63. }
  64. if (@ARGV) {
  65. my ($str, $B1, $B2) = @ARGV;
  66. my $n = Math::GMPz->new($str);
  67. printf("[*] Factoring: %s (%d digits)...\n", $n, length("$n"));
  68. my $factor = ecm($n, $B1 // 100, $B2 // 1000);
  69. if ($factor > 1 and $factor < $n) {
  70. say "`-> found factor: $factor";
  71. exit 0;
  72. }
  73. else {
  74. say "`-> no factor found...";
  75. exit 1;
  76. }
  77. }
  78. say ecm(Math::GMPz->new("14304849576137459"));
  79. say ecm(79710615566344993);
  80. say ecm(Math::GMPz->new(2)**128 + 1); # takes ~3.4 seconds