//! An ECDLP instance Q = d·G on a prime-order curve, plus the optional order-3 automorphism data. use crate::curve::{Counters, Curve, Pt}; use crate::field::Modn; #[derive(Clone, Copy, Debug)] pub struct Instance { pub curve: Curve, pub n: Modn, pub g: Pt, pub q: Pt, /// β (Montgomery form) with (βx, y) = λ·(x, y); only for a = 0, p ≡ 1 (mod 3), n ≡ 1 (mod 3) pub beta: Option, pub lambda: Option, } impl Instance { #[allow(clippy::too_many_arguments)] pub fn new(p: u64, a: u64, b: u64, n: u64, gx: u64, gy: u64, qx: u64, qy: u64) -> Result { let curve = Curve::new(p, a, b); let g = curve.point_from_std(gx, gy); let q = curve.point_from_std(qx, qy); if !curve.is_on_curve(&g) { return Err("G is not on the curve".into()); } if !curve.is_on_curve(&q) { return Err("Q is not on the curve".into()); } if g.inf { return Err("G must not be the point at infinity".into()); } let mut c = Counters::default(); if !curve.mul(n, &g, &mut c).inf { return Err("n·G != O: n is not the order of G".into()); } Ok(Instance { curve, n: Modn::new(n), g, q, beta: None, lambda: None }) } /// Install (β, λ) and verify φ(G) = (β·gx, gy) = λ·G on this instance; abort otherwise. pub fn with_aut3(mut self, beta_std: u64, lambda: u64) -> Result { let f = &self.curve.f; if self.curve.a_std != 0 { return Err("--aut3 requires a = 0 (j-invariant 0)".into()); } if f.p % 3 != 1 || self.n.n % 3 != 1 { return Err("--aut3 requires p ≡ 1 (mod 3) and n ≡ 1 (mod 3)".into()); } if beta_std == 0 || beta_std >= f.p || beta_std == 1 { return Err("beta must satisfy 1 < beta < p".into()); } if lambda <= 1 || lambda >= self.n.n { return Err("lambda must satisfy 1 < lambda < n".into()); } if f.pow_plain(beta_std, 3) != 1 { return Err("beta^3 != 1 mod p".into()); } if self.n.pow(lambda, 3) != 1 { return Err("lambda^3 != 1 mod n".into()); } let beta = f.to_mont(beta_std); let phi_g = Pt::new(f.mul(beta, self.g.x), self.g.y); let mut c = Counters::default(); let lam_g = self.curve.mul(lambda, &self.g, &mut c); if !self.curve.eq(&phi_g, &lam_g) { return Err("(beta*gx, gy) != lambda*G: wrong (beta, lambda) pair for this G".into()); } self.beta = Some(beta); self.lambda = Some(lambda); Ok(self) } /// Check d·G == Q (counted in `c`). pub fn verify(&self, d: u64, c: &mut Counters) -> bool { let r = self.curve.mul(d % self.n.n, &self.g, c); self.curve.eq(&r, &self.q) } /// ⌈log2 n⌉ pub fn bits(&self) -> u32 { 64 - self.n.n.leading_zeros() } } /// Integer square root (floor) of a u128. pub fn isqrt(n: u128) -> u128 { if n < 2 { return n; } let mut x = (n as f64).sqrt() as u128; // float only as a starting guess; corrected exactly below while x * x > n { x -= 1; } while (x + 1) * (x + 1) <= n { x += 1; } x } /// ⌈√n⌉ pub fn isqrt_ceil(n: u128) -> u128 { let r = isqrt(n); if r * r == n { r } else { r + 1 } } /// 64-bit mixing function (splitmix64 finaliser) used for partition / distinguished-point tests. #[inline(always)] pub fn mix64(x: u64) -> u64 { let mut z = x.wrapping_add(0x9E3779B97F4A7C15); z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9); z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB); z ^ (z >> 31) } /// Partition index in [0, r) from the high 32 bits of mix64(x) (multiply-high, no division). #[inline(always)] pub fn partition(x: u64, r: u64) -> u32 { (((mix64(x) >> 32) * r) >> 32) as u32 } /// Distinguished-point predicate: the low `dp_bits` bits of mix64(x) are zero. #[inline(always)] pub fn is_distinguished(x: u64, dp_mask: u64) -> bool { (mix64(x) & dp_mask) == 0 }