diff options
| author | Sebastiano Tronto <sebastiano@tronto.net> | 2025-06-24 10:12:02 +0200 |
|---|---|---|
| committer | Sebastiano Tronto <sebastiano@tronto.net> | 2025-06-24 10:12:02 +0200 |
| commit | 81135442e901fb50bfb7e69961127a9cf273aa4d (patch) | |
| tree | 348b2e7eedcdbbe28927a8ec8ac1b071b188e7a4 /code/rust | |
| parent | d52d1c3c1c91f00a6a36a8bf7bc60f4dfc352a0e (diff) | |
| download | ecm-master.tar.gz ecm-master.zip | |
Diffstat (limited to '')
| -rw-r--r-- | code/rust/Cargo.toml | 10 | ||||
| -rw-r--r-- | code/rust/src/main.rs | 106 |
2 files changed, 116 insertions, 0 deletions
diff --git a/code/rust/Cargo.toml b/code/rust/Cargo.toml new file mode 100644 index 0000000..13d1df8 --- /dev/null +++ b/code/rust/Cargo.toml | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | [package] | ||
| 2 | name = "ecm" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2024" | ||
| 5 | |||
| 6 | [dependencies] | ||
| 7 | rand = "=0.8.5" | ||
| 8 | num-integer = "=0.1.46" | ||
| 9 | num-traits = "=0.2.19" | ||
| 10 | num-bigint = { version = "=0.4.6", features = ["rand"] } | ||
diff --git a/code/rust/src/main.rs b/code/rust/src/main.rs new file mode 100644 index 0000000..ded1f59 --- /dev/null +++ b/code/rust/src/main.rs | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | use std::env; | ||
| 2 | use std::fmt; | ||
| 3 | use std::cmp; | ||
| 4 | use rand::Rng; | ||
| 5 | use num_integer::Integer; | ||
| 6 | use num_traits::identities::{Zero, One}; | ||
| 7 | use num_bigint::{BigInt, RandomBits}; | ||
| 8 | |||
| 9 | #[derive(Clone)] | ||
| 10 | enum Point { | ||
| 11 | NonZero((BigInt, BigInt)), | ||
| 12 | Zero | ||
| 13 | } | ||
| 14 | |||
| 15 | impl fmt::Display for Point { | ||
| 16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
| 17 | match self { | ||
| 18 | Point::NonZero((x, y)) => write!(f, "({x}, {y})"), | ||
| 19 | Point::Zero => write!(f, "∞") | ||
| 20 | } | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | fn inverse_mod(a: &BigInt, n: &BigInt) -> Result<BigInt, BigInt> { | ||
| 25 | let egcd = a.extended_gcd(n); | ||
| 26 | if egcd.gcd.is_one() { Ok(egcd.x) } else { Err(egcd.gcd) } | ||
| 27 | } | ||
| 28 | |||
| 29 | fn ec_sum(p: &Point, q: &Point, a: &BigInt, n: &BigInt) -> Result<Point, BigInt> { | ||
| 30 | // TODO: this should use references | ||
| 31 | let (px, py) = if let Point::NonZero((x, y)) = p { | ||
| 32 | (x, y) | ||
| 33 | } else { | ||
| 34 | return Ok(q.clone()); | ||
| 35 | }; | ||
| 36 | |||
| 37 | let (qx, qy) = if let Point::NonZero((x, y)) = q { | ||
| 38 | (x, y) | ||
| 39 | } else { | ||
| 40 | return Ok(p.clone()); | ||
| 41 | }; | ||
| 42 | |||
| 43 | if ((px - qx) % n).is_zero() && ((py + qy) % n).is_zero() { | ||
| 44 | return Ok(Point::Zero); | ||
| 45 | } | ||
| 46 | |||
| 47 | let k = if px != qx { | ||
| 48 | (py - qy) * inverse_mod(&(px - qx), n)? | ||
| 49 | } else { | ||
| 50 | (3 * px * px + a) * inverse_mod(&(py + qy), n)? | ||
| 51 | }; | ||
| 52 | |||
| 53 | let x = &k * &k - px - qx; | ||
| 54 | let y = k * (px - &x) - py; | ||
| 55 | |||
| 56 | Ok(Point::NonZero((x.clone() % n, y.clone() % n))) | ||
| 57 | } | ||
| 58 | |||
| 59 | fn ec_mul(m: &BigInt, p: &Point, a: &BigInt, n: &BigInt) -> Result<Point, BigInt> { | ||
| 60 | if m.is_zero() { | ||
| 61 | return Ok(Point::Zero); | ||
| 62 | } | ||
| 63 | |||
| 64 | if (m % BigInt::from(2)).is_zero() { | ||
| 65 | ec_mul(&(m / 2), &ec_sum(p, p, a, n)?, a, n) | ||
| 66 | } else { | ||
| 67 | ec_sum(p, &ec_mul(&(m - 1), p, a, n)?, a, n) | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | |||
| 72 | // Elliptic curve factorization method | ||
| 73 | // If n is prime, this method goes into an infinite loop | ||
| 74 | fn find_factor(n: &BigInt) -> BigInt { | ||
| 75 | let mut rng = rand::thread_rng(); | ||
| 76 | let bound = cmp::max(n.sqrt().sqrt().sqrt() + 2, BigInt::from(256)); | ||
| 77 | |||
| 78 | loop { | ||
| 79 | let a = rng.sample::<BigInt, _>(RandomBits::new(256)) % n; | ||
| 80 | let x = rng.sample::<BigInt, _>(RandomBits::new(256)) % n; | ||
| 81 | let y = rng.sample::<BigInt, _>(RandomBits::new(256)) % n; | ||
| 82 | let mut p = Point::NonZero((x, y)); | ||
| 83 | |||
| 84 | let mut m = BigInt::from(2); | ||
| 85 | while m < bound && !matches!(p, Point::Zero) { | ||
| 86 | match ec_mul(&m, &p, &a, n) { | ||
| 87 | Err(f) => { | ||
| 88 | println!("Factor {f} found with a = {a}, m = {m} and p = {p}"); | ||
| 89 | return f; | ||
| 90 | }, | ||
| 91 | Ok(new_p) => p = new_p | ||
| 92 | } | ||
| 93 | m += 1; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | fn main() { | ||
| 99 | let args: Vec<String> = env::args().collect(); | ||
| 100 | if let Ok(n) = args[1].parse::<BigInt>() { | ||
| 101 | let f = find_factor(&n); | ||
| 102 | println!("{} = {} * {}", &n, &f, &n / &f); | ||
| 103 | } else { | ||
| 104 | println!("Invalid argument {} (cannot convert to number)", args[1]); | ||
| 105 | } | ||
| 106 | } | ||
