From db8dccd9b75c9e9f59ba6fcd9c79974119255e2e Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Fri, 24 Apr 2026 12:21:31 +0200 Subject: [PATCH 01/14] merge --- ec/examples/ec_demo.rs | 4 +- ec/src/curve_edwards.rs | 30 ++---------- ec/src/curve_hessian.rs | 18 +++---- ec/src/curve_jacobi_intersection.rs | 6 +-- ec/src/curve_jacobi_quartic.rs | 13 ++--- ec/src/curve_legendre.rs | 10 ++-- ec/src/curve_montgomery.rs | 21 ++++---- ec/src/curve_ops.rs | 4 +- ec/src/curve_twisted_hessian.rs | 2 +- ec/src/curve_weierstrass.rs | 13 ++--- ec/src/lib.rs | 24 +++++----- ec/src/point_edwards.rs | 4 +- ec/src/point_hessian.rs | 40 +++++++--------- ec/src/point_jacobi_intersection.rs | 5 +- ec/src/point_jacobi_quartic.rs | 3 +- ec/src/point_legendre.rs | 16 ++++--- ec/src/point_montgomery.rs | 23 ++++----- ec/src/point_ops.rs | 2 +- ec/src/point_twisted_hessian.rs | 4 +- ec/src/point_weierstrass.rs | 12 ++--- ec/tests/curve_edwards_test.rs | 26 ++++++---- ec/tests/curve_montgomery_tests.rs | 8 +++- ec/tests/curve_weierstrass_test.rs | 5 +- ec/tests/curves_legendre_tests.rs | 9 ++-- ec/tests/curves_weierstrass_test.rs | 5 +- ec/tests/field_adapter.rs | 4 +- ec/tests/hessian_tests.rs | 9 +++- ec/tests/jacobi_example_instantiation.rs | 4 +- ec/tests/jacobi_intersection_vectors.rs | 30 +++++++++--- ec/tests/jacobi_quartic_vectors.rs | 42 ++++++++++++---- ec/tests/jacobi_tests.rs | 4 +- ec/tests/point_edwards_test.rs | 53 +++++++++++--------- ec/tests/point_legendre_tests.rs | 15 ++---- ec/tests/point_montgomery_tests.rs | 2 +- ec/tests/point_tests.rs | 2 +- ec/tests/point_weierstrass_tests.rs | 4 +- ec/tests/twisted_hessian_tests.rs | 7 ++- fp/examples/fp_demo.rs | 22 +++------ fp/src/f2_element.rs | 18 ++----- fp/src/f2_ext.rs | 12 ++--- fp/src/fp_element.rs | 12 ++--- fp/src/fp_ext.rs | 30 +++++------- fp/tests/f2_ext_tests.rs | 19 +++----- fp/tests/f2_tests.rs | 25 ++++++++-- fp/tests/fp_ext_tests.rs | 21 ++------ fp/tests/fp_tests.rs | 12 ++--- isogeny/src/isogeny_ops.rs | 61 +++++++++++------------- isogeny/src/lib.rs | 2 +- isogeny/src/montgomery.rs | 4 +- isogeny/tests/isogeny_tests.rs | 1 - protocol/src/scalar.rs | 4 +- 51 files changed, 344 insertions(+), 382 deletions(-) diff --git a/ec/examples/ec_demo.rs b/ec/examples/ec_demo.rs index 1b8df36..5c53e43 100644 --- a/ec/examples/ec_demo.rs +++ b/ec/examples/ec_demo.rs @@ -1,4 +1,4 @@ -use crypto_bigint::{const_prime_monty_params, Uint}; +use crypto_bigint::{Uint, const_prime_monty_params}; use ec::curve_edwards::EdwardsCurve; use ec::curve_jacobi_intersection::JacobiIntersectionCurve; @@ -64,4 +64,4 @@ fn main() { // Need a != 0, 1. let ji = JacobiIntersectionCurve::new(fp(2)); show_curve("Jacobi intersection", &ji, &mut rng); -} \ No newline at end of file +} diff --git a/ec/src/curve_edwards.rs b/ec/src/curve_edwards.rs index f37ff72..3e523ae 100644 --- a/ec/src/curve_edwards.rs +++ b/ec/src/curve_edwards.rs @@ -28,10 +28,10 @@ //! - Binary Edwards curves (characteristic $2$): Bernstein–Lange–Rezaeian Farashahi (2008) //! - Odd characteristic: -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -use fp::{ref_field_impl, ref_field_trait_impl}; use core::fmt; use fp::field_ops::{FieldOps, FieldRandom}; +use fp::{ref_field_impl, ref_field_trait_impl}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use crate::curve_ops::Curve; use crate::point_edwards::EdwardsPoint; @@ -48,7 +48,6 @@ pub struct EdwardsCurve { pub d2: F, } - impl fmt::Display for EdwardsCurve where F: FieldOps + fmt::Display, @@ -82,7 +81,6 @@ where } } - ref_field_impl! { impl EdwardsCurve { /// Construct an odd-characteristic Edwards curve `x² + y² = 1 + d·x²·y²`. @@ -181,26 +179,7 @@ ref_field_impl! { } } - - - - - - - - - - - - - - - - - - - -ref_field_trait_impl!{ +ref_field_trait_impl! { impl Curve for EdwardsCurve { type BaseField = F; type Point = EdwardsPoint; @@ -261,7 +240,6 @@ ref_field_trait_impl!{ } } - // --------------------------------------------------------------------------- // Constant-time functionalities // --------------------------------------------------------------------------- @@ -299,4 +277,4 @@ where fn ct_ne(&self, other: &Self) -> Choice { !self.ct_eq(other) } -} \ No newline at end of file +} diff --git a/ec/src/curve_hessian.rs b/ec/src/curve_hessian.rs index 604dad0..78d290c 100644 --- a/ec/src/curve_hessian.rs +++ b/ec/src/curve_hessian.rs @@ -45,14 +45,14 @@ //! - EFD, projective Hessian formulas: //! -use core::fmt; -use fp::field_ops::{FieldOps, FieldRandom}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -use fp::{ref_field_impl, ref_field_trait_impl}; use crate::curve_ops::Curve; use crate::curve_weierstrass::WeierstrassCurve; use crate::point_hessian::HessianPoint; use crate::point_weierstrass::AffinePoint; +use core::fmt; +use fp::field_ops::{FieldOps, FieldRandom}; +use fp::{ref_field_impl, ref_field_trait_impl}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; /// A generalized Hessian curve /// @@ -82,7 +82,7 @@ where } } -ref_field_impl!{ +ref_field_impl! { impl HessianCurve { /// Construct a generalized Hessian curve /// @@ -176,8 +176,7 @@ ref_field_impl!{ } } - -ref_field_trait_impl!{ +ref_field_trait_impl! { impl Curve for HessianCurve { type BaseField = F; type Point = HessianPoint; @@ -220,7 +219,6 @@ ref_field_trait_impl!{ } } - impl ConditionallySelectable for HessianCurve where F: FieldOps + Copy, @@ -256,8 +254,7 @@ where } } - -ref_field_impl!{ +ref_field_impl! { impl HessianCurve { /// Return the EFD Hessian parameter `delta` for the ordinary Hessian model /// @@ -404,4 +401,3 @@ ref_field_impl!{ } } } - diff --git a/ec/src/curve_jacobi_intersection.rs b/ec/src/curve_jacobi_intersection.rs index 334acd6..cc39ace 100644 --- a/ec/src/curve_jacobi_intersection.rs +++ b/ec/src/curve_jacobi_intersection.rs @@ -19,10 +19,10 @@ //! use core::fmt; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::{FieldOps, FieldRandom}; use fp::{ref_field_impl, ref_field_trait_impl}; use rand::{CryptoRng, Rng}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use crate::curve_ops::Curve; use crate::curve_weierstrass::WeierstrassCurve; @@ -100,7 +100,7 @@ ref_field_impl! { } } -ref_field_impl!{ +ref_field_impl! { impl JacobiIntersectionCurve { /// Sample a random affine point on this Jacobi‑intersection curve using the /// provided RNG. @@ -183,4 +183,4 @@ where fn ct_ne(&self, other: &Self) -> Choice { !self.ct_eq(other) } -} \ No newline at end of file +} diff --git a/ec/src/curve_jacobi_quartic.rs b/ec/src/curve_jacobi_quartic.rs index 16044e2..ab1cb8a 100644 --- a/ec/src/curve_jacobi_quartic.rs +++ b/ec/src/curve_jacobi_quartic.rs @@ -16,9 +16,9 @@ //! `d(a²-d) ≠ 0`. use core::fmt; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::{FieldOps, FieldRandom}; use fp::{ref_field_impl, ref_field_trait_impl}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use crate::curve_ops::Curve; use crate::point_jacobi_quartic::JacobiQuarticPoint; @@ -48,11 +48,7 @@ where self.a, self.d ) } else { - write!( - f, - "y^2 = ({})x^4 + 2({})x^2 + 1", - self.d, self.a - ) + write!(f, "y^2 = ({})x^4 + 2({})x^2 + 1", self.d, self.a) } } } @@ -108,7 +104,7 @@ ref_field_impl! { } } -ref_field_impl!{ +ref_field_impl! { impl JacobiQuarticCurve { /// Sample a random affine point on this Jacobi quartic using the provided RNG. /// @@ -134,7 +130,6 @@ ref_field_impl!{ } } - ref_field_trait_impl! { impl Curve for JacobiQuarticCurve { type BaseField = F; @@ -223,4 +218,4 @@ where fn ct_ne(&self, other: &Self) -> Choice { !self.ct_eq(other) } -} \ No newline at end of file +} diff --git a/ec/src/curve_legendre.rs b/ec/src/curve_legendre.rs index 41c189d..c16d313 100644 --- a/ec/src/curve_legendre.rs +++ b/ec/src/curve_legendre.rs @@ -46,12 +46,12 @@ //! - HongFeng Wu and RongQuan Feng, //! *On the isomorphism classes of Legendre elliptic curves over finite fields*, //! Sci. China Math. 54(9) (2011), 1885–1890. +use crate::curve_ops::Curve; +use crate::curve_weierstrass::WeierstrassCurve; +use crate::point_legendre::LegendrePoint; use core::fmt; use fp::field_ops::{FieldOps, FieldRandom}; use fp::{ref_field_impl, ref_field_trait_impl}; -use crate::curve_ops::Curve; -use crate::point_legendre::LegendrePoint; -use crate::curve_weierstrass::WeierstrassCurve; /// A Legendre elliptic curve over a field `F`. /// @@ -73,7 +73,7 @@ pub struct LegendreCurve { pub lambda: F, } -ref_field_impl!{ +ref_field_impl! { impl LegendreCurve { /// Construct the Legendre curve $y^2 = x(x-1)(x-\lambda).$ pub fn new(lambda: F) -> Self { @@ -280,7 +280,7 @@ where } } -ref_field_trait_impl!{ +ref_field_trait_impl! { impl Curve for LegendreCurve { type BaseField = F; type Point = LegendrePoint; diff --git a/ec/src/curve_montgomery.rs b/ec/src/curve_montgomery.rs index 19136fc..e0d025c 100644 --- a/ec/src/curve_montgomery.rs +++ b/ec/src/curve_montgomery.rs @@ -35,9 +35,9 @@ //! scalar multiplication. use core::fmt; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -use fp::{ref_field_impl, ref_field_trait_impl}; use fp::field_ops::{FieldOps, FieldRandom}; +use fp::{ref_field_impl, ref_field_trait_impl}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use crate::curve_ops::Curve; use crate::point_montgomery::KummerPoint; @@ -55,7 +55,6 @@ pub struct MontgomeryCurve { pub b: F, } - impl fmt::Display for MontgomeryCurve where F: FieldOps + fmt::Display, @@ -93,7 +92,7 @@ where } } -ref_field_impl!{ +ref_field_impl! { impl MontgomeryCurve { // ------------------------------------------------------------------- // Constructor @@ -162,7 +161,6 @@ ref_field_impl! { } } - // ------------------------------------------------------------------- // Curve predicates // ------------------------------------------------------------------- @@ -256,7 +254,6 @@ ref_field_trait_impl! { } } - // --------------------------------------------------------------------------- // Constant-time functionalities // --------------------------------------------------------------------------- @@ -266,9 +263,9 @@ where F: FieldOps + Copy, { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Self{ - a: F::conditional_select(& a.a, &b.a, choice), - b: F::conditional_select(& a.b, &b.b, choice), + Self { + a: F::conditional_select(&a.a, &b.a, choice), + b: F::conditional_select(&a.b, &b.b, choice), } } @@ -291,5 +288,7 @@ where self.a.ct_eq(&other.a) & self.b.ct_eq(&other.b) } - fn ct_ne(&self, other: &Self) -> Choice { !self.ct_eq(other) } -} \ No newline at end of file + fn ct_ne(&self, other: &Self) -> Choice { + !self.ct_eq(other) + } +} diff --git a/ec/src/curve_ops.rs b/ec/src/curve_ops.rs index 843bf50..e094e8c 100644 --- a/ec/src/curve_ops.rs +++ b/ec/src/curve_ops.rs @@ -7,8 +7,8 @@ //! Each concrete curve type chooses its base field and its native point type //! through associated types. -use fp::field_ops::FieldOps; use crate::point_ops::PointOps; +use fp::field_ops::FieldOps; /// Generic elliptic-curve model. /// @@ -48,4 +48,4 @@ pub trait Curve: Sized + Clone + PartialEq + Eq { fn identity(&self) -> Self::Point { Self::Point::identity(self) } -} \ No newline at end of file +} diff --git a/ec/src/curve_twisted_hessian.rs b/ec/src/curve_twisted_hessian.rs index dce6f83..3a9aaee 100644 --- a/ec/src/curve_twisted_hessian.rs +++ b/ec/src/curve_twisted_hessian.rs @@ -22,7 +22,7 @@ //! //! The twisted Hessian curve is nonsingular when //! -//! $$a \neq 0 \quad\text{and}\quad d^3 \neq a.$$ +//! $$a \neq 0 \quad\text{and}\quad d^3 \neq a.$$ //! //! # References //! diff --git a/ec/src/curve_weierstrass.rs b/ec/src/curve_weierstrass.rs index 8af0838..ba5f0f0 100644 --- a/ec/src/curve_weierstrass.rs +++ b/ec/src/curve_weierstrass.rs @@ -22,8 +22,8 @@ use core::fmt; use fp::field_ops::{FieldOps, FieldRandom}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::{ref_field_fns, ref_field_impl, ref_field_trait_impl}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use crate::curve_ops::Curve; use crate::point_weierstrass::AffinePoint; @@ -179,7 +179,7 @@ ref_field_impl! { } } -ref_field_impl!{ +ref_field_impl! { impl WeierstrassCurve { /// Sample a random affine point on this curve using the provided RNG. /// @@ -231,7 +231,6 @@ ref_field_impl!{ } } - // ------------------------------------------------------------------- // Discriminant private helpers (Silverman, §III.1) // ------------------------------------------------------------------- @@ -355,7 +354,7 @@ ref_field_fns! { // Invariants attached to the model // ------------------------------------------------------------------- -ref_field_impl!{ +ref_field_impl! { impl WeierstrassCurve { /// Returns the invariant $b_2 = a_1^2 + 4a_2$. pub fn b2(&self) -> F { @@ -390,12 +389,11 @@ ref_field_impl!{ } } - // ------------------------------------------------------------------- // Curve predicates // ------------------------------------------------------------------- -ref_field_trait_impl!{ +ref_field_trait_impl! { impl Curve for WeierstrassCurve { type BaseField = F; type Point = AffinePoint; @@ -430,7 +428,6 @@ ref_field_trait_impl!{ } } - // --------------------------------------------------------------------------- // Constant-time functionalities // --------------------------------------------------------------------------- @@ -481,4 +478,4 @@ where fn ct_ne(&self, other: &Self) -> Choice { !self.ct_eq(other) } -} \ No newline at end of file +} diff --git a/ec/src/lib.rs b/ec/src/lib.rs index 8d729a2..db45852 100644 --- a/ec/src/lib.rs +++ b/ec/src/lib.rs @@ -8,23 +8,23 @@ extern crate core; pub mod curve_ops; pub mod point_ops; -pub mod curve_weierstrass; -pub mod point_weierstrass; -pub mod point_montgomery; -pub mod curve_montgomery; -pub mod point_edwards; pub mod curve_edwards; -pub mod curve_jacobi_quartic; -pub mod point_jacobi_quartic; -pub mod curve_jacobi_intersection; -pub mod point_jacobi_intersection; pub mod curve_hessian; -pub mod point_hessian; -/// Point used in the Legendre form. -pub mod point_legendre; +pub mod curve_jacobi_intersection; +pub mod curve_jacobi_quartic; ///! Elliptic curve definition in Legendre form. pub mod curve_legendre; +pub mod curve_montgomery; /// Twisted Hessian curves. pub mod curve_twisted_hessian; +pub mod curve_weierstrass; +pub mod point_edwards; +pub mod point_hessian; +pub mod point_jacobi_intersection; +pub mod point_jacobi_quartic; +/// Point used in the Legendre form. +pub mod point_legendre; +pub mod point_montgomery; /// Projective points on twisted Hessian curves. pub mod point_twisted_hessian; +pub mod point_weierstrass; diff --git a/ec/src/point_edwards.rs b/ec/src/point_edwards.rs index 0030851..61dcd7b 100644 --- a/ec/src/point_edwards.rs +++ b/ec/src/point_edwards.rs @@ -371,7 +371,7 @@ ref_field_impl! { // PointOps bridge // --------------------------------------------------------------------------- -ref_field_trait_impl!{ +ref_field_trait_impl! { impl PointOps for EdwardsPoint { type BaseField = F; type Curve = EdwardsCurve; @@ -393,4 +393,4 @@ ref_field_trait_impl!{ } } -} \ No newline at end of file +} diff --git a/ec/src/point_hessian.rs b/ec/src/point_hessian.rs index 30927a3..ba3fa95 100644 --- a/ec/src/point_hessian.rs +++ b/ec/src/point_hessian.rs @@ -71,14 +71,18 @@ where write!(f, "({}, {})", x_aff, y_aff) } } else if f.alternate() { - write!(f, "HessianPoint {{ X:Y:Z = ({}:{}:{}) }}", self.x, self.y, self.z) + write!( + f, + "HessianPoint {{ X:Y:Z = ({}:{}:{}) }}", + self.x, self.y, self.z + ) } else { write!(f, "({}:{}:{})", self.x, self.y, self.z) } } } -ref_field_trait_impl!{ +ref_field_trait_impl! { impl PartialEq for HessianPoint { /// Equality of projective points. fn eq(&self, other: &Self) -> bool { @@ -95,7 +99,7 @@ ref_field_trait_impl!{ } } -ref_field_trait_impl!{ +ref_field_trait_impl! { impl Eq for HessianPoint {} } @@ -141,27 +145,20 @@ impl HessianPoint { /// Return `true` if this is the invalid projective triple `(0:0:0)`. pub fn is_zero_projective(&self) -> bool { - bool::from(self.x.is_zero()) - && bool::from(self.y.is_zero()) - && bool::from(self.z.is_zero()) + bool::from(self.x.is_zero()) && bool::from(self.y.is_zero()) && bool::from(self.z.is_zero()) } /// Convert a finite projective point to affine coordinates. pub fn to_affine(&self) -> Option<(F, F)> { - self.z - .invert() - .into_option() - .map(|zinv| { - ( - ::mul(&self.x, &zinv), - ::mul(&self.y, &zinv), - ) - }) + self.z.invert().into_option().map(|zinv| { + ( + ::mul(&self.x, &zinv), + ::mul(&self.y, &zinv), + ) + }) } } - - impl ConditionallySelectable for HessianPoint where F: FieldOps + Copy, @@ -187,7 +184,7 @@ where } } -ref_field_trait_impl!{ +ref_field_trait_impl! { impl ConstantTimeEq for HessianPoint { fn ct_eq(&self, other: &Self) -> Choice { let self_zero = self.is_zero_projective(); @@ -207,7 +204,7 @@ ref_field_trait_impl!{ } } -ref_field_impl!{ +ref_field_impl! { impl HessianPoint { /// Negation on a Hessian curve: /// @@ -326,7 +323,7 @@ ref_field_impl!{ } } -ref_field_trait_impl!{ +ref_field_trait_impl! { impl PointOps for HessianPoint { type BaseField = F; type Curve = HessianCurve; @@ -349,8 +346,7 @@ ref_field_trait_impl!{ } } - -ref_field_trait_impl!{ +ref_field_trait_impl! { impl PointAdd for HessianPoint { fn add(&self, other: &Self, curve: &Self::Curve) -> Self { HessianPoint::::add(self, other, curve) diff --git a/ec/src/point_jacobi_intersection.rs b/ec/src/point_jacobi_intersection.rs index 0ccde71..3c6f4da 100644 --- a/ec/src/point_jacobi_intersection.rs +++ b/ec/src/point_jacobi_intersection.rs @@ -28,7 +28,6 @@ pub struct JacobiIntersectionPoint { pub d: F, } - impl fmt::Display for JacobiIntersectionPoint where F: FieldOps + fmt::Display, @@ -143,7 +142,6 @@ where } } - ref_field_impl! { impl JacobiIntersectionPoint { /// Negation: `-(s, c, d) = (-s, c, d)`. @@ -236,7 +234,6 @@ ref_field_impl! { } } - ref_field_trait_impl! { impl PointOps for JacobiIntersectionPoint { type BaseField = F; @@ -266,4 +263,4 @@ ref_field_trait_impl! { JacobiIntersectionPoint::::add(self, other, curve) } } -} \ No newline at end of file +} diff --git a/ec/src/point_jacobi_quartic.rs b/ec/src/point_jacobi_quartic.rs index cb37457..439c906 100644 --- a/ec/src/point_jacobi_quartic.rs +++ b/ec/src/point_jacobi_quartic.rs @@ -59,7 +59,6 @@ impl PartialEq for JacobiQuarticPoint { impl Eq for JacobiQuarticPoint {} - impl JacobiQuarticPoint { /// Constructs a finite affine point `(x, y)`. pub fn new(x: F, y: F) -> Self { @@ -275,4 +274,4 @@ ref_field_trait_impl! { JacobiQuarticPoint::::add(self, other, curve) } } -} \ No newline at end of file +} diff --git a/ec/src/point_legendre.rs b/ec/src/point_legendre.rs index 3438d8b..fb414e4 100644 --- a/ec/src/point_legendre.rs +++ b/ec/src/point_legendre.rs @@ -70,8 +70,8 @@ use core::fmt; use crate::curve_legendre::LegendreCurve; use crate::point_ops::{PointAdd, PointOps}; use fp::field_ops::FieldOps; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::{ref_field_impl, ref_field_trait_impl}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; /// A point on a Legendre elliptic curve over `F`. /// @@ -127,7 +127,11 @@ impl LegendrePoint { /// [`LegendreCurve::contains`] or [`crate::curve_ops::Curve::is_on_curve`] /// if validation is needed. pub fn new(x: F, y: F) -> Self { - Self { x, y, infinity: false } + Self { + x, + y, + infinity: false, + } } /// Returns the identity element `O`. @@ -206,7 +210,7 @@ where } } -ref_field_impl!{ +ref_field_impl! { impl LegendrePoint { /// /// For a finite affine point `(x, y)` on a Legendre curve, the inverse is @@ -338,7 +342,7 @@ ref_field_impl!{ } -ref_field_trait_impl!{ +ref_field_trait_impl! { impl PointOps for LegendrePoint { type BaseField = F; type Curve = LegendreCurve; @@ -361,10 +365,10 @@ ref_field_trait_impl!{ } } -ref_field_trait_impl!{ +ref_field_trait_impl! { impl PointAdd for LegendrePoint { fn add(&self, other: &Self, curve: &Self::Curve) -> Self { LegendrePoint::::add(self, other, curve) } } -} \ No newline at end of file +} diff --git a/ec/src/point_montgomery.rs b/ec/src/point_montgomery.rs index fa49b34..b3eb8a3 100644 --- a/ec/src/point_montgomery.rs +++ b/ec/src/point_montgomery.rs @@ -29,14 +29,13 @@ //! - scalar multiplication via the Montgomery ladder. use core::fmt; -use subtle::{Choice, CtOption, ConditionallySelectable, ConstantTimeEq}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::curve_montgomery::MontgomeryCurve; use crate::point_ops::PointOps; use fp::field_ops::FieldOps; use fp::{ref_field_impl, ref_field_trait_impl}; - /// A point on the Kummer line of a Montgomery curve, represented by `(X : Z)`. /// /// The affine x-coordinate is `x = X / Z` when `Z ≠ 0`. @@ -76,7 +75,6 @@ ref_field_trait_impl! { { } } - impl fmt::Display for KummerPoint where F: FieldOps + Copy + fmt::Display, @@ -95,11 +93,7 @@ where "KummerPoint {{ X:Z = ({}:{}), x = {} }}", self.x, self.z, x_aff ), - None => write!( - f, - "KummerPoint {{ X:Z = ({}:{}) }}", - self.x, self.z - ), + None => write!(f, "KummerPoint {{ X:Z = ({}:{}) }}", self.x, self.z), } } else { write!(f, "({}:{})", self.x, self.z) @@ -107,28 +101,29 @@ where } } - // --------------------------------------------------------------------------- // Constructors // --------------------------------------------------------------------------- - impl KummerPoint { /// Construct a projective x-line point `(X : Z)` without validation. pub fn new(x: F, z: F) -> Self { assert!(!bool::from(x.mul(&z).is_zero())); - Self{ x, z } + Self { x, z } } /// Construct the finite x-line point corresponding to the affine /// x-coordinate `x`, i.e. `(x : 1)`. pub fn from_x(x: F) -> Self { - Self{ x, z: F::one() } + Self { x, z: F::one() } } /// The image of the identity point on the Kummer line. pub fn identity() -> Self { - Self{ x: F::one(), z: F::zero()} + Self { + x: F::one(), + z: F::zero(), + } } /// Return `true` if this point is the image of identity. @@ -141,10 +136,8 @@ impl KummerPoint { let z_inv = self.z.invert(); z_inv.map(|zinv| self.x.mul(&zinv)) } - } - // --------------------------------------------------------------------------- // Constant-time functionalities // --------------------------------------------------------------------------- diff --git a/ec/src/point_ops.rs b/ec/src/point_ops.rs index a69fc9f..e5e6c13 100644 --- a/ec/src/point_ops.rs +++ b/ec/src/point_ops.rs @@ -14,7 +14,7 @@ use subtle::ConditionallySelectable; /// need access to the curve parameters. The clean abstraction boundary is a /// method-based API taking `&Self::Curve` explicitly. /// -pub trait PointOps: Clone + ConditionallySelectable { +pub trait PointOps: Clone + ConditionallySelectable { /// The base field $\mathbb{F}_{p^M}$ type BaseField: FieldOps; diff --git a/ec/src/point_twisted_hessian.rs b/ec/src/point_twisted_hessian.rs index 09d2aa0..770d9cf 100644 --- a/ec/src/point_twisted_hessian.rs +++ b/ec/src/point_twisted_hessian.rs @@ -121,9 +121,7 @@ impl TwistedHessianPoint { /// Return `true` if this is the invalid projective triple `(0:0:0)`. pub fn is_zero_projective(&self) -> bool { - bool::from(self.x.is_zero()) - && bool::from(self.y.is_zero()) - && bool::from(self.z.is_zero()) + bool::from(self.x.is_zero()) && bool::from(self.y.is_zero()) && bool::from(self.z.is_zero()) } /// Return `true` if the point lies on the line at infinity. diff --git a/ec/src/point_weierstrass.rs b/ec/src/point_weierstrass.rs index 31cdfcb..ab4a064 100644 --- a/ec/src/point_weierstrass.rs +++ b/ec/src/point_weierstrass.rs @@ -31,8 +31,8 @@ use core::fmt; use crate::curve_weierstrass::WeierstrassCurve; use crate::point_ops::PointOps; use fp::field_ops::FieldOps; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::{ref_field_impl, ref_field_trait_impl, ref_field_trait_impl_path}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; /// An affine point on a Weierstrass elliptic curve over `F`. /// @@ -85,13 +85,7 @@ where } } - - -impl Eq for AffinePoint -where - F: FieldOps + ConstantTimeEq, -{ -} +impl Eq for AffinePoint where F: FieldOps + ConstantTimeEq {} // --------------------------------------------------------------------------- // Constructors @@ -409,4 +403,4 @@ ref_field_trait_impl_path! { AffinePoint::::add(self, other, curve) } } -} \ No newline at end of file +} diff --git a/ec/tests/curve_edwards_test.rs b/ec/tests/curve_edwards_test.rs index f207a82..447c9fa 100644 --- a/ec/tests/curve_edwards_test.rs +++ b/ec/tests/curve_edwards_test.rs @@ -16,14 +16,14 @@ //! R.=GF(2)[]; F.=GF(2^4,modulus=x^4+x+1); assert (a^3).trace()==1 //! ``` -use crypto_bigint::{const_prime_monty_params, Uint}; +use crypto_bigint::{Uint, const_prime_monty_params}; use fp::field_ops::FieldOps; use fp::fp_element::FpElement; use ec::curve_edwards::EdwardsCurve; -use ec::point_edwards::EdwardsPoint; use ec::curve_ops::Curve; +use ec::point_edwards::EdwardsPoint; // =========================================================================== // Field definitions @@ -31,21 +31,31 @@ use ec::curve_ops::Curve; const_prime_monty_params!(Fp761Mod, Uint<1>, "00000000000002F9", 2); type F761 = FpElement; -fn fp761(n: u64) -> F761 { F761::from_u64(n) } +fn fp761(n: u64) -> F761 { + F761::from_u64(n) +} const_prime_monty_params!(Fp65537Mod, Uint<1>, "0000000000010001", 3); type F65537 = FpElement; -fn fp65537(n: u64) -> F65537 { F65537::from_u64(n) } +fn fp65537(n: u64) -> F65537 { + F65537::from_u64(n) +} -use fp::f2_ext::{F2Ext, BinaryIrreducible}; +use fp::f2_ext::{BinaryIrreducible, F2Ext}; struct Poly2_4; impl BinaryIrreducible<1> for Poly2_4 { - fn modulus() -> Uint<1> { Uint::from_u64(0x13) } - fn degree() -> usize { 4 } + fn modulus() -> Uint<1> { + Uint::from_u64(0x13) + } + fn degree() -> usize { + 4 + } } type GF16 = F2Ext<1, Poly2_4>; -fn gf(n: u64) -> GF16 { GF16::from_u64(n) } +fn gf(n: u64) -> GF16 { + GF16::from_u64(n) +} // =========================================================================== // 1. F₇₆₁, d = 3 diff --git a/ec/tests/curve_montgomery_tests.rs b/ec/tests/curve_montgomery_tests.rs index ecd2e22..1c09617 100644 --- a/ec/tests/curve_montgomery_tests.rs +++ b/ec/tests/curve_montgomery_tests.rs @@ -94,7 +94,11 @@ fn montgomery_known_x_coordinates_are_on_curve() { for x in all_x_coords_montgomery_19(fp(3), fp(5)) { let p = KummerPoint::from_x(x); - assert!(c.is_on_curve(&p), "x = {:?} should lie on the Montgomery curve", x); + assert!( + c.is_on_curve(&p), + "x = {:?} should lie on the Montgomery curve", + x + ); } } @@ -127,4 +131,4 @@ fn montgomery_j_invariant_matches_formula() { let expected = &num * &den_inv; assert_eq!(c.j_invariant(), expected); -} \ No newline at end of file +} diff --git a/ec/tests/curve_weierstrass_test.rs b/ec/tests/curve_weierstrass_test.rs index 66f8245..166730a 100644 --- a/ec/tests/curve_weierstrass_test.rs +++ b/ec/tests/curve_weierstrass_test.rs @@ -2,11 +2,10 @@ // Unit tests // --------------------------------------------------------------------------- - use crypto_bigint::{Uint, const_prime_monty_params}; -use fp::fp_element::FpElement; -use fp::field_ops::FieldOps; use ec::curve_weierstrass::WeierstrassCurve; +use fp::field_ops::FieldOps; +use fp::fp_element::FpElement; const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); type F19 = FpElement; diff --git a/ec/tests/curves_legendre_tests.rs b/ec/tests/curves_legendre_tests.rs index b4c80bc..7f17be0 100644 --- a/ec/tests/curves_legendre_tests.rs +++ b/ec/tests/curves_legendre_tests.rs @@ -1,9 +1,9 @@ use crypto_bigint::{Uint, const_prime_monty_params}; -use fp::fp_element::FpElement; -use fp::field_ops::FieldOps; use ec::curve_legendre::LegendreCurve; use ec::curve_ops::Curve; use ec::point_legendre::LegendrePoint; +use fp::field_ops::FieldOps; +use fp::fp_element::FpElement; const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); type F19 = FpElement; @@ -49,7 +49,6 @@ fn legendre_curve_visible_two_torsion_is_on_curve() { let c = curve(); let three = F19::from(FpElement::from_u64(3)); - assert!(c.is_on_curve(&LegendrePoint::new(F19::zero(), F19::zero()))); assert!(c.is_on_curve(&LegendrePoint::new(F19::one(), F19::zero()))); assert!(c.is_on_curve(&LegendrePoint::new(three, F19::zero()))); @@ -70,7 +69,6 @@ fn legendre_curve_j_invariant_matches_closed_formula() { println!("lambda = {}", lambda.as_uint().to_words()[0]); let two_five_six = F19::from(FpElement::from_u64(256)); - let lambda_sq = ::square(&lambda); let t = &(&lambda_sq - &lambda) + &F19::one(); println!("t = {}", t.as_uint().to_words()[0]); @@ -87,7 +85,6 @@ fn legendre_curve_j_invariant_matches_closed_formula() { assert_eq!(c.j_invariant(), expected); } - #[test] fn legendre_to_general_weierstrass_matches_a_invariants() { let c = curve(); // e.g. λ = 3 over F_19 @@ -134,4 +131,4 @@ fn legendre_short_weierstrass_coordinate_shift_works() { let yw = p.y; assert!(w.contains(&xw, &yw)); -} \ No newline at end of file +} diff --git a/ec/tests/curves_weierstrass_test.rs b/ec/tests/curves_weierstrass_test.rs index 66f8245..166730a 100644 --- a/ec/tests/curves_weierstrass_test.rs +++ b/ec/tests/curves_weierstrass_test.rs @@ -2,11 +2,10 @@ // Unit tests // --------------------------------------------------------------------------- - use crypto_bigint::{Uint, const_prime_monty_params}; -use fp::fp_element::FpElement; -use fp::field_ops::FieldOps; use ec::curve_weierstrass::WeierstrassCurve; +use fp::field_ops::FieldOps; +use fp::fp_element::FpElement; const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); type F19 = FpElement; diff --git a/ec/tests/field_adapter.rs b/ec/tests/field_adapter.rs index cc8bc24..a94ad7a 100644 --- a/ec/tests/field_adapter.rs +++ b/ec/tests/field_adapter.rs @@ -1,6 +1,6 @@ //! Concrete field adapter for the Jacobi vector tests. -use crypto_bigint::{const_prime_monty_params, Uint}; +use crypto_bigint::{Uint, const_prime_monty_params}; use fp::fp_element::FpElement; // Use the same pattern as your Weierstrass tests, but over GF(101). @@ -13,4 +13,4 @@ pub type F = FpElement; pub fn f(n: i64) -> F { let p = 101i64; F::from_u64(n.rem_euclid(p) as u64) -} \ No newline at end of file +} diff --git a/ec/tests/hessian_tests.rs b/ec/tests/hessian_tests.rs index c43c9c2..23be843 100644 --- a/ec/tests/hessian_tests.rs +++ b/ec/tests/hessian_tests.rs @@ -127,7 +127,12 @@ fn hessian_double_matches_add_self() { let curve = complete_curve(); for p in all_points(&curve) { - assert_eq!(p.double(&curve), p.add(&p, &curve), "[2]P mismatch for P={:?}", p); + assert_eq!( + p.double(&curve), + p.add(&p, &curve), + "[2]P mismatch for P={:?}", + p + ); } } @@ -187,4 +192,4 @@ fn hessian_weierstrass_birational_roundtrip() { assert!(curve.is_on_curve(&back)); assert_eq!(p, back); -} \ No newline at end of file +} diff --git a/ec/tests/jacobi_example_instantiation.rs b/ec/tests/jacobi_example_instantiation.rs index 7297142..9bbb5de 100644 --- a/ec/tests/jacobi_example_instantiation.rs +++ b/ec/tests/jacobi_example_instantiation.rs @@ -2,10 +2,10 @@ //! //! Copy this file to your crate's `tests/` directory, then replace the adapter. -#[path = "jacobi_quartic_vectors.rs"] -mod jacobi_quartic_vectors; #[path = "jacobi_intersection_vectors.rs"] mod jacobi_intersection_vectors; +#[path = "jacobi_quartic_vectors.rs"] +mod jacobi_quartic_vectors; // Replace this line with your real adapter file. // #[path = "field_adapter.rs"] diff --git a/ec/tests/jacobi_intersection_vectors.rs b/ec/tests/jacobi_intersection_vectors.rs index c775594..00412c6 100644 --- a/ec/tests/jacobi_intersection_vectors.rs +++ b/ec/tests/jacobi_intersection_vectors.rs @@ -63,7 +63,10 @@ macro_rules! jacobi_intersection_test_suite { let p = ji_p(); let q = ji_q(); - assert_eq!(p.double(&curve), JacobiIntersectionPoint::new(jif(22), jif(74), jif(89))); + assert_eq!( + p.double(&curve), + JacobiIntersectionPoint::new(jif(22), jif(74), jif(89)) + ); assert_eq!(q.double(&curve), ji_p()); } @@ -85,10 +88,22 @@ macro_rules! jacobi_intersection_test_suite { assert_eq!(p.scalar_mul(&[0], &curve), ji_id()); assert_eq!(p.scalar_mul(&[1], &curve), ji_p()); - assert_eq!(p.scalar_mul(&[2], &curve), JacobiIntersectionPoint::new(jif(22), jif(74), jif(89))); - assert_eq!(p.scalar_mul(&[3], &curve), JacobiIntersectionPoint::new(jif(52), jif(78), jif(42))); - assert_eq!(p.scalar_mul(&[5], &curve), JacobiIntersectionPoint::new(jif(57), jif(40), jif(88))); - assert_eq!(p.scalar_mul(&[8], &curve), JacobiIntersectionPoint::new(jif(44), jif(40), jif(88))); + assert_eq!( + p.scalar_mul(&[2], &curve), + JacobiIntersectionPoint::new(jif(22), jif(74), jif(89)) + ); + assert_eq!( + p.scalar_mul(&[3], &curve), + JacobiIntersectionPoint::new(jif(52), jif(78), jif(42)) + ); + assert_eq!( + p.scalar_mul(&[5], &curve), + JacobiIntersectionPoint::new(jif(57), jif(40), jif(88)) + ); + assert_eq!( + p.scalar_mul(&[8], &curve), + JacobiIntersectionPoint::new(jif(44), jif(40), jif(88)) + ); assert_eq!(p.scalar_mul(&[13], &curve), ji_id()); } @@ -106,7 +121,10 @@ macro_rules! jacobi_intersection_test_suite { let q = ji_q(); let r = p.double(&curve); - assert_eq!(p.add(&q, &curve).add(&r, &curve), p.add(&q.add(&r, &curve), &curve)); + assert_eq!( + p.add(&q, &curve).add(&r, &curve), + p.add(&q.add(&r, &curve), &curve) + ); } }; } diff --git a/ec/tests/jacobi_quartic_vectors.rs b/ec/tests/jacobi_quartic_vectors.rs index 1d9b49b..385284a 100644 --- a/ec/tests/jacobi_quartic_vectors.rs +++ b/ec/tests/jacobi_quartic_vectors.rs @@ -20,7 +20,7 @@ #[macro_export] macro_rules! jacobi_quartic_test_suite { - ($ec:ident, $field_ty:ty, $lift:path) => { + ($ec:ident, $field_ty:ty, $lift:path) => { use $ec::curve_jacobi_quartic::JacobiQuarticCurve; use $ec::point_jacobi_quartic::JacobiQuarticPoint; @@ -99,13 +99,34 @@ macro_rules! jacobi_quartic_test_suite { let curve = jq_curve(); let p = jq_p(); - assert_eq!(p.scalar_mul(&[0], &curve), JacobiQuarticPoint::new(jqf(0), jqf(1))); - assert_eq!(p.scalar_mul(&[1], &curve), JacobiQuarticPoint::new(jqf(8), jqf(94))); - assert_eq!(p.scalar_mul(&[2], &curve), JacobiQuarticPoint::new(jqf(92), jqf(35))); - assert_eq!(p.scalar_mul(&[3], &curve), JacobiQuarticPoint::new(jqf(46), jqf(37))); - assert_eq!(p.scalar_mul(&[5], &curve), JacobiQuarticPoint::new(jqf(89), jqf(11))); - assert_eq!(p.scalar_mul(&[8], &curve), JacobiQuarticPoint::new(jqf(21), jqf(50))); - assert_eq!(p.scalar_mul(&[16], &curve), JacobiQuarticPoint::new(jqf(93), jqf(94))); + assert_eq!( + p.scalar_mul(&[0], &curve), + JacobiQuarticPoint::new(jqf(0), jqf(1)) + ); + assert_eq!( + p.scalar_mul(&[1], &curve), + JacobiQuarticPoint::new(jqf(8), jqf(94)) + ); + assert_eq!( + p.scalar_mul(&[2], &curve), + JacobiQuarticPoint::new(jqf(92), jqf(35)) + ); + assert_eq!( + p.scalar_mul(&[3], &curve), + JacobiQuarticPoint::new(jqf(46), jqf(37)) + ); + assert_eq!( + p.scalar_mul(&[5], &curve), + JacobiQuarticPoint::new(jqf(89), jqf(11)) + ); + assert_eq!( + p.scalar_mul(&[8], &curve), + JacobiQuarticPoint::new(jqf(21), jqf(50)) + ); + assert_eq!( + p.scalar_mul(&[16], &curve), + JacobiQuarticPoint::new(jqf(93), jqf(94)) + ); assert_eq!(p.scalar_mul(&[17], &curve), jq_id()); } @@ -123,7 +144,10 @@ macro_rules! jacobi_quartic_test_suite { let q = jq_q(); let r = p.double(&curve); - assert_eq!(p.add(&q, &curve).add(&r, &curve), p.add(&q.add(&r, &curve), &curve)); + assert_eq!( + p.add(&q, &curve).add(&r, &curve), + p.add(&q.add(&r, &curve), &curve) + ); } #[test] diff --git a/ec/tests/jacobi_tests.rs b/ec/tests/jacobi_tests.rs index 447ea42..071a502 100644 --- a/ec/tests/jacobi_tests.rs +++ b/ec/tests/jacobi_tests.rs @@ -8,10 +8,8 @@ mod jacobi_intersection_vectors; #[path = "field_adapter.rs"] mod field_adapter; -use field_adapter::{f, F}; +use field_adapter::{F, f}; // Because the suites use #[macro_export], call them directly: jacobi_quartic_test_suite!(ec, F, f); jacobi_intersection_test_suite!(ec, F, f); - - diff --git a/ec/tests/point_edwards_test.rs b/ec/tests/point_edwards_test.rs index 56d2afa..f165ddc 100644 --- a/ec/tests/point_edwards_test.rs +++ b/ec/tests/point_edwards_test.rs @@ -5,14 +5,14 @@ //! 2. F₆₅₅₃₇ (p = 65537, d = 3, non-square → complete) //! 3. GF(2⁴) (d₁ = 1, d₂ = α³ = 8, Tr(d₂) = 1 → complete) -use crypto_bigint::{const_prime_monty_params, Uint}; +use crypto_bigint::{Uint, const_prime_monty_params}; use fp::field_ops::FieldOps; use fp::fp_element::FpElement; use ec::curve_edwards::EdwardsCurve; -use ec::point_edwards::EdwardsPoint; use ec::curve_ops::Curve; +use ec::point_edwards::EdwardsPoint; // =========================================================================== // Field definitions @@ -20,21 +20,31 @@ use ec::curve_ops::Curve; const_prime_monty_params!(Fp761Mod, Uint<1>, "00000000000002F9", 2); type F761 = FpElement; -fn fp761(n: u64) -> F761 { F761::from_u64(n) } +fn fp761(n: u64) -> F761 { + F761::from_u64(n) +} const_prime_monty_params!(Fp65537Mod, Uint<1>, "0000000000010001", 3); type F65537 = FpElement; -fn fp65537(n: u64) -> F65537 { F65537::from_u64(n) } +fn fp65537(n: u64) -> F65537 { + F65537::from_u64(n) +} -use fp::f2_ext::{F2Ext, BinaryIrreducible}; +use fp::f2_ext::{BinaryIrreducible, F2Ext}; struct Poly2_4; impl BinaryIrreducible<1> for Poly2_4 { - fn modulus() -> Uint<1> { Uint::from_u64(0x13) } - fn degree() -> usize { 4 } + fn modulus() -> Uint<1> { + Uint::from_u64(0x13) + } + fn degree() -> usize { + 4 + } } type GF16 = F2Ext<1, Poly2_4>; -fn gf(n: u64) -> GF16 { GF16::from_u64(n) } +fn gf(n: u64) -> GF16 { + GF16::from_u64(n) +} // =========================================================================== // Helpers @@ -131,7 +141,9 @@ fn f761_negate_then_add() { let neg = p.negate(&c); assert!( p.add(&neg, &c).is_identity(), - "P + (-P) ≠ O for ({},{})", xv, yv, + "P + (-P) ≠ O for ({},{})", + xv, + yv, ); } } @@ -180,10 +192,7 @@ fn f761_associativity() { let p = EdwardsPoint::new(fp761(pts[0].0), fp761(pts[0].1)); let q = EdwardsPoint::new(fp761(pts[1].0), fp761(pts[1].1)); let r = EdwardsPoint::new(fp761(pts[2].0), fp761(pts[2].1)); - assert_eq!( - p.add(&q, &c).add(&r, &c), - p.add(&q.add(&r, &c), &c), - ); + assert_eq!(p.add(&q, &c).add(&r, &c), p.add(&q.add(&r, &c), &c),); } #[test] @@ -194,7 +203,8 @@ fn f761_scalar_mul_order() { let p = EdwardsPoint::new(fp761(pts[0].0), fp761(pts[0].1)); assert!( p.scalar_mul(&[order], &c).is_identity(), - "[{}]P should be O", order, + "[{}]P should be O", + order, ); } @@ -274,10 +284,7 @@ fn f65537_associativity() { let p = EdwardsPoint::new(fp65537(1), fp65537(0)); let q = p.double(&c); let r = q.double(&c); - assert_eq!( - p.add(&q, &c).add(&r, &c), - p.add(&q.add(&r, &c), &c), - ); + assert_eq!(p.add(&q, &c).add(&r, &c), p.add(&q.add(&r, &c), &c),); } #[test] @@ -406,7 +413,8 @@ fn gf16_scalar_mul_order() { for p in &pts { assert!( p.scalar_mul(&[order], &c).is_identity(), - "[{}]P should be O", order, + "[{}]P should be O", + order, ); } } @@ -444,7 +452,8 @@ fn gf16_w_double() { let w_expected = &dbl.x + &dbl.y; let w_got = EdwardsPoint::::w_double(&w, &c); assert_eq!( - w_got, w_expected, + w_got, + w_expected, "w-double mismatch for P=({:?},{:?})", p.x.as_uint().to_words()[0], p.y.as_uint().to_words()[0], @@ -460,8 +469,8 @@ fn gf16_w_diff_add() { // Pick P, Q such that Q-P is also known let p = &pts[1]; let q = &pts[2]; - let diff = q.add(&p.negate(&c), &c); // Q - P - let sum = p.add(q, &c); // P + Q + let diff = q.add(&p.negate(&c), &c); // Q - P + let sum = p.add(q, &c); // P + Q let w1 = &diff.x + &diff.y; let w2 = &p.x + &p.y; diff --git a/ec/tests/point_legendre_tests.rs b/ec/tests/point_legendre_tests.rs index 3c40089..ddb5529 100644 --- a/ec/tests/point_legendre_tests.rs +++ b/ec/tests/point_legendre_tests.rs @@ -7,7 +7,7 @@ //! This curve has 16 rational points in total, and the point P = (2,6) //! has order 8. The visible 2-torsion points are (0,0), (1,0), and (3,0). -use crypto_bigint::{const_prime_monty_params, Uint}; +use crypto_bigint::{Uint, const_prime_monty_params}; use fp::field_ops::FieldOps; use fp::fp_element::FpElement; @@ -92,11 +92,7 @@ fn f19_negate_then_add_for_all_points() { let c = curve(); for p in all_legendre_19(&c) { let neg = p.negate(&c); - assert!( - p.add(&neg, &c).is_identity(), - "P + (-P) != O for P = {}", - p - ); + assert!(p.add(&neg, &c).is_identity(), "P + (-P) != O for P = {}", p); } } @@ -188,10 +184,7 @@ fn f19_associativity_small_sample() { let q = pts[1]; let r = pts[2]; - assert_eq!( - p.add(&q, &c).add(&r, &c), - p.add(&q.add(&r, &c), &c), - ); + assert_eq!(p.add(&q, &c).add(&r, &c), p.add(&q.add(&r, &c), &c),); } #[test] @@ -235,4 +228,4 @@ fn f19_chain_on_curve() { acc = acc.add(&p, &c); assert!(c.is_on_curve(&acc)); } -} \ No newline at end of file +} diff --git a/ec/tests/point_montgomery_tests.rs b/ec/tests/point_montgomery_tests.rs index 9b6c262..356bdf1 100644 --- a/ec/tests/point_montgomery_tests.rs +++ b/ec/tests/point_montgomery_tests.rs @@ -1,4 +1,4 @@ -use crypto_bigint::{const_prime_monty_params, Uint}; +use crypto_bigint::{Uint, const_prime_monty_params}; use fp::field_ops::FieldOps; use fp::fp_element::FpElement; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; diff --git a/ec/tests/point_tests.rs b/ec/tests/point_tests.rs index bde710d..cddfc54 100644 --- a/ec/tests/point_tests.rs +++ b/ec/tests/point_tests.rs @@ -5,7 +5,7 @@ //! 2. Short Weierstrass over a quadratic extension (F₁₉²) //! 3. General Weierstrass over a binary extension field (F₂⁴) -use crypto_bigint::{const_prime_monty_params, Uint}; +use crypto_bigint::{Uint, const_prime_monty_params}; use fp::field_ops::FieldOps; use fp::fp_element::FpElement; diff --git a/ec/tests/point_weierstrass_tests.rs b/ec/tests/point_weierstrass_tests.rs index 749360b..cddfc54 100644 --- a/ec/tests/point_weierstrass_tests.rs +++ b/ec/tests/point_weierstrass_tests.rs @@ -5,7 +5,7 @@ //! 2. Short Weierstrass over a quadratic extension (F₁₉²) //! 3. General Weierstrass over a binary extension field (F₂⁴) -use crypto_bigint::{const_prime_monty_params, Uint}; +use crypto_bigint::{Uint, const_prime_monty_params}; use fp::field_ops::FieldOps; use fp::fp_element::FpElement; @@ -363,4 +363,4 @@ fn gf16_scalar_mul_order() { group_order, ); } -} \ No newline at end of file +} diff --git a/ec/tests/twisted_hessian_tests.rs b/ec/tests/twisted_hessian_tests.rs index cdc2ef5..d11fed3 100644 --- a/ec/tests/twisted_hessian_tests.rs +++ b/ec/tests/twisted_hessian_tests.rs @@ -98,7 +98,12 @@ fn twisted_hessian_double_matches_add_self() { let curve = test_curve(); for p in all_points(&curve) { - assert_eq!(p.double(&curve), p.add(&p, &curve), "[2]P mismatch for P={:?}", p); + assert_eq!( + p.double(&curve), + p.add(&p, &curve), + "[2]P mismatch for P={:?}", + p + ); } } diff --git a/fp/examples/fp_demo.rs b/fp/examples/fp_demo.rs index 36c17cd..31c91d6 100644 --- a/fp/examples/fp_demo.rs +++ b/fp/examples/fp_demo.rs @@ -2,8 +2,7 @@ //! //! Run with: cargo run --bin demo //! -use crypto_bigint::{const_prime_monty_params, Uint}; - +use crypto_bigint::{Uint, const_prime_monty_params}; // Pull in the library types use fp::f2_element::F2Element; @@ -55,7 +54,6 @@ impl TonelliShanksConstants for TS19Quad { type Fp19_2 = FpExt; - // =================================================================== // 5. Prime field F_97283 // =================================================================== @@ -160,22 +158,17 @@ struct TS97283Quartic; impl TonelliShanksConstants for TS97283Quartic { // p^4 - 1 = 89566956980912803920 = 2^4 * 5597934811307050245 - const ORDER: Uint<2> = - Uint::<2>::from_words([0xdafdc0d3fc005050, 0x0000000000000004]); + const ORDER: Uint<2> = Uint::<2>::from_words([0xdafdc0d3fc005050, 0x0000000000000004]); - const HALF_ORDER: Uint<2> = - Uint::<2>::from_words([0x6d7ee069fe002828, 0x0000000000000002]); + const HALF_ORDER: Uint<2> = Uint::<2>::from_words([0x6d7ee069fe002828, 0x0000000000000002]); const S: u64 = 4; - const T: Uint<2> = - Uint::<2>::from_words([0x4dafdc0d3fc00505, 0x0000000000000000]); + const T: Uint<2> = Uint::<2>::from_words([0x4dafdc0d3fc00505, 0x0000000000000000]); - const PROJENATOR_EXP: Uint<2> = - Uint::<2>::from_words([0x26d7ee069fe00282, 0x0000000000000000]); + const PROJENATOR_EXP: Uint<2> = Uint::<2>::from_words([0x26d7ee069fe00282, 0x0000000000000000]); - const TWOSM1: Uint<2> = - Uint::<2>::from_words([0x0000000000000008, 0x0000000000000000]); + const TWOSM1: Uint<2> = Uint::<2>::from_words([0x0000000000000008, 0x0000000000000000]); fn root_of_unity() -> [FpElement; 4] { // An element of exact order 16 in F_{p^4} @@ -324,5 +317,4 @@ fn main() { println!(" sanity a = {a4}"); println!(" sanity 1/a = {a4_inv}"); println!(" a * 1/a = {} (should be 1)", &a4 * &a4_inv); - -} \ No newline at end of file +} diff --git a/fp/src/f2_element.rs b/fp/src/f2_element.rs index 0c7cab4..ac37a36 100644 --- a/fp/src/f2_element.rs +++ b/fp/src/f2_element.rs @@ -89,9 +89,7 @@ impl ConstantTimeEq for F2Element { // Operator overloads // --------------------------------------------------------------------------- -impl<'a, 'b> Add<&'b F2Element> -for &'a F2Element -{ +impl<'a, 'b> Add<&'b F2Element> for &'a F2Element { type Output = F2Element; fn add(self, rhs: &'b F2Element) -> Self::Output { @@ -99,9 +97,7 @@ for &'a F2Element } } -impl<'a, 'b> Sub<&'b F2Element> -for &'a F2Element -{ +impl<'a, 'b> Sub<&'b F2Element> for &'a F2Element { type Output = F2Element; fn sub(self, rhs: &'b F2Element) -> Self::Output { @@ -109,10 +105,7 @@ for &'a F2Element } } - -impl<'a, 'b> Mul<&'b F2Element> -for &'a F2Element -{ +impl<'a, 'b> Mul<&'b F2Element> for &'a F2Element { type Output = F2Element; fn mul(self, rhs: &'b F2Element) -> Self::Output { @@ -120,8 +113,7 @@ for &'a F2Element } } -impl<'a> Neg for &'a F2Element -{ +impl<'a> Neg for &'a F2Element { type Output = F2Element; fn neg(self) -> Self::Output { @@ -129,8 +121,6 @@ impl<'a> Neg for &'a F2Element } } - - // --------------------------------------------------------------------------- // FieldOps implementation // --------------------------------------------------------------------------- diff --git a/fp/src/f2_ext.rs b/fp/src/f2_ext.rs index c088958..9e38e89 100644 --- a/fp/src/f2_ext.rs +++ b/fp/src/f2_ext.rs @@ -6,7 +6,6 @@ use crypto_bigint::Uint; use std::marker::PhantomData; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - // --------------------------------------------------------------------------- // IrreduciblePoly — the only thing callers need to implement for a new field // --------------------------------------------------------------------------- @@ -348,8 +347,7 @@ where // Operator overloads (delegate to the FieldOps methods below) // =========================================================================== -impl<'a, 'b, const LIMBS: usize, P> Add<&'b F2Ext> -for &'a F2Ext +impl<'a, 'b, const LIMBS: usize, P> Add<&'b F2Ext> for &'a F2Ext where P: BinaryIrreducible, { @@ -360,8 +358,7 @@ where } } -impl<'a, 'b, const LIMBS: usize, P> Sub<&'b F2Ext> -for &'a F2Ext +impl<'a, 'b, const LIMBS: usize, P> Sub<&'b F2Ext> for &'a F2Ext where P: BinaryIrreducible, { @@ -372,9 +369,7 @@ where } } - -impl<'a, 'b, const LIMBS: usize, P> Mul<&'b F2Ext> -for &'a F2Ext +impl<'a, 'b, const LIMBS: usize, P> Mul<&'b F2Ext> for &'a F2Ext where P: BinaryIrreducible, { @@ -396,7 +391,6 @@ where } } - // =========================================================================== // FieldOps implementation // =========================================================================== diff --git a/fp/src/fp_element.rs b/fp/src/fp_element.rs index 12cd28c..6d8f4e0 100644 --- a/fp/src/fp_element.rs +++ b/fp/src/fp_element.rs @@ -6,8 +6,8 @@ use std::fmt; use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom}; use crypto_bigint::{ - modular::{ConstMontyForm, ConstPrimeMontyParams}, NonZero, RandomMod, Uint, + modular::{ConstMontyForm, ConstPrimeMontyParams}, }; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -196,8 +196,7 @@ where // Operator overloads // --------------------------------------------------------------------------- -impl<'a, 'b, MOD, const LIMBS: usize> Add<&'b FpElement> -for &'a FpElement +impl<'a, 'b, MOD, const LIMBS: usize> Add<&'b FpElement> for &'a FpElement where MOD: ConstPrimeMontyParams, { @@ -208,8 +207,7 @@ where } } -impl<'a, 'b, MOD, const LIMBS: usize> Sub<&'b FpElement> -for &'a FpElement +impl<'a, 'b, MOD, const LIMBS: usize> Sub<&'b FpElement> for &'a FpElement where MOD: ConstPrimeMontyParams, { @@ -220,9 +218,7 @@ where } } - -impl<'a, 'b, MOD, const LIMBS: usize> Mul<&'b FpElement> -for &'a FpElement +impl<'a, 'b, MOD, const LIMBS: usize> Mul<&'b FpElement> for &'a FpElement where MOD: ConstPrimeMontyParams, { diff --git a/fp/src/fp_ext.rs b/fp/src/fp_ext.rs index bc2581f..1e15119 100644 --- a/fp/src/fp_ext.rs +++ b/fp/src/fp_ext.rs @@ -97,7 +97,7 @@ use std::marker::PhantomData; use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom}; use crate::fp_element::FpElement; -use crypto_bigint::{modular::ConstPrimeMontyParams, Uint}; +use crypto_bigint::{Uint, modular::ConstPrimeMontyParams}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; // =========================================================================== @@ -697,8 +697,7 @@ where // =========================================================================== impl<'a, 'b, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> -Add<&'b FpExt> -for &'a FpExt + Add<&'b FpExt> for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, @@ -711,8 +710,7 @@ where } impl<'a, 'b, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> -Sub<&'b FpExt> -for &'a FpExt + Sub<&'b FpExt> for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, @@ -725,8 +723,7 @@ where } impl<'a, 'b, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> -Mul<&'b FpExt> -for &'a FpExt + Mul<&'b FpExt> for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, @@ -738,9 +735,8 @@ where } } -impl<'a, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> -Neg -for &'a FpExt +impl<'a, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> Neg + for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, @@ -752,9 +748,6 @@ where } } - - - // =========================================================================== // Helper functions for Tonelli-Shanks algorithm // =========================================================================== @@ -785,9 +778,9 @@ fn ts_loop TSCONSTS: TonelliShanksConstants, { let mut v = TSCONSTS::S as u32; - *x = &*x * w ; // to take the prod of a &mut one has to dereference it (so it isn't mut anymore) and then reference to it + *x = &*x * w; // to take the prod of a &mut one has to dereference it (so it isn't mut anymore) and then reference to it let mut b = &*x * w; - //x.mul(*w); + //x.mul(*w); let mut z = FpExt::new(TSCONSTS::root_of_unity()); for max_v in (1..=TSCONSTS::S as u32).rev() { @@ -1070,13 +1063,13 @@ where let is_invertible = !self.is_zero(); let x = rhs * &(self * self); - //self.mul(self).mul(*rhs); + //self.mul(self).mul(*rhs); let (myinv, mysqrt) = x.inverse_and_sqrt(); let myinv_value = myinv.unwrap_or(Self::zero()); let mysqrt_value = mysqrt.unwrap_or(Self::zero()); let inv_value = self * &(rhs * &myinv_value); - let sqrt_value = &inv_value * &mysqrt_value; + let sqrt_value = &inv_value * &mysqrt_value; let inv_is_some = is_invertible & myinv.is_some(); @@ -1113,8 +1106,7 @@ where let ans_value = &self.square() * &(&myinv_value * &mysqrt_value); let inv_is_some = myinv.is_some(); - let ans_is_some = - inv_is_some & mysqrt.is_some() & (mysqrt_value.square()).ct_eq(&x); + let ans_is_some = inv_is_some & mysqrt.is_some() & (mysqrt_value.square()).ct_eq(&x); CtOption::new(ans_value, ans_is_some) } diff --git a/fp/tests/f2_ext_tests.rs b/fp/tests/f2_ext_tests.rs index 66bd4af..ec93636 100644 --- a/fp/tests/f2_ext_tests.rs +++ b/fp/tests/f2_ext_tests.rs @@ -29,7 +29,6 @@ impl BinaryIrreducible<8> for F2511Poly { type GF2_511 = F2Ext<8, F2511Poly>; - #[test] fn random_elements_test() { let mut rng = rand::rng(); @@ -103,7 +102,7 @@ fn inv_zero_is_none() { fn add_coefficient_wise() { let a = elt(0b1_1111); // x^4 + x^3 + x^2 + x + 1 let b = elt(0b101_0111); // x^6 + x^4 + x^2 + x + 1 - let c = &a + &b; // x^6 + x^3 = 0b100_1000 + let c = &a + &b; // x^6 + x^3 = 0b100_1000 assert_eq!(c, elt(0b100_1000)); let d = elt(0b10_1010); // x^5 + x^3 + x @@ -170,17 +169,15 @@ fn mul_concrete_values() { fn mul_commutativity() { let a = elt(0b1_1001); // x^4 + x^3 + 1 let b = elt(0b101_0011); // x^6 + x^4 + x + 1 - assert_eq!(&a * &b, &b * &a);} + assert_eq!(&a * &b, &b * &a); +} #[test] fn mul_associativity() { let a = elt(0b1_1001); // x^4 + x^3 + 1 let b = elt(0b101_0011); // x^6 + x^4 + x + 1 let c = FieldOps::mul(&a, &b); // x^7 + x^5 + x^4 + x^3 + x = 0b1011_1010 - assert_eq!( - &(&a * &b) * &c, - &a * &(&b * &c) - ); + assert_eq!(&(&a * &b) * &c, &a * &(&b * &c)); } #[test] @@ -188,16 +185,14 @@ fn mul_distributivity() { let a = elt(0b1_1001); // x^4 + x^3 + 1 let b = elt(0b101_0011); // x^6 + x^4 + x + 1 let c = &a * &b; // x^7 + x^5 + x^4 + x^3 + x = 0b1011_1010 - assert_eq!( - &a * &(&b + &c), - &(&a * &b) + &(&a * &c) - ); + assert_eq!(&a * &(&b + &c), &(&a * &b) + &(&a * &c)); } #[test] fn square_equals_mul_self() { let a = elt(0b1_1001); // x^4 + x^3 + 1 - assert_eq!(a.square(), &a * &a);} + assert_eq!(a.square(), &a * &a); +} // ----------------------------------------------------------------------- // Inversion diff --git a/fp/tests/f2_tests.rs b/fp/tests/f2_tests.rs index 88c7b37..e72bfac 100644 --- a/fp/tests/f2_tests.rs +++ b/fp/tests/f2_tests.rs @@ -29,17 +29,32 @@ mod tests { fn add_mod_2() { // 0 + 1 ≡ 1 (mod 2) // 1 + 1 ≡ 0 (mod 2) - assert_eq!(&F2Element::from_u64(0) + &F2Element::from_u64(1), F2Element::ONE); - assert_eq!(&F2Element::from_u64(1) + &F2Element::from_u64(1), F2Element::ZERO); + assert_eq!( + &F2Element::from_u64(0) + &F2Element::from_u64(1), + F2Element::ONE + ); + assert_eq!( + &F2Element::from_u64(1) + &F2Element::from_u64(1), + F2Element::ZERO + ); } #[test] fn mul_mod_2() { // 0 * 0 ≡ 0 (mod 2) // 0 * 1 ≡ 0 (mod 2) // 1 * 1 ≡ 1 (mod 2) - assert_eq!(&F2Element::from_u64(0) * &F2Element::from_u64(0), F2Element::ZERO); - assert_eq!(&F2Element::from_u64(0) * &F2Element::from_u64(1), F2Element::ZERO); - assert_eq!(&F2Element::from_u64(1) * &F2Element::from_u64(1), F2Element::ONE); + assert_eq!( + &F2Element::from_u64(0) * &F2Element::from_u64(0), + F2Element::ZERO + ); + assert_eq!( + &F2Element::from_u64(0) * &F2Element::from_u64(1), + F2Element::ZERO + ); + assert_eq!( + &F2Element::from_u64(1) * &F2Element::from_u64(1), + F2Element::ONE + ); } #[test] fn neg_mod_2() { diff --git a/fp/tests/fp_ext_tests.rs b/fp/tests/fp_ext_tests.rs index 725ba4e..697430b 100644 --- a/fp/tests/fp_ext_tests.rs +++ b/fp/tests/fp_ext_tests.rs @@ -1,4 +1,4 @@ -use crypto_bigint::{const_prime_monty_params, Uint}; +use crypto_bigint::{Uint, const_prime_monty_params}; use fp::field_ops::{FieldOps, FieldRandom}; use fp::fp_element::FpElement; use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; // ← was missing IrreduciblePoly @@ -32,8 +32,6 @@ impl TonelliShanksConstants for TSQuad { type F19_2 = FpExt; - - fn fp(n: u64) -> Fp19 { Fp19::from_u64(n) } @@ -78,7 +76,6 @@ impl TonelliShanksConstants for TS97283Cubic { type Fp97283_3 = FpExt; - #[test] fn random_elements_test() { let mut rng = rand::rng(); @@ -215,10 +212,7 @@ fn mul_associativity() { let a = el(2, 1); let b = el(3, 4); let c = el(5, 6); - assert_eq!( - &(&a * &b) * &c, - &a * &(&b * &c) - ); + assert_eq!(&(&a * &b) * &c, &a * &(&b * &c)); } #[test] @@ -226,10 +220,7 @@ fn mul_distributivity() { let a = el(3, 2); let b = el(1, 5); let c = el(4, 7); - assert_eq!( - &a * &(&b + &c), - &(&a * &b) + &(&a * &c) - ); + assert_eq!(&a * &(&b + &c), &(&a * &b) + &(&a * &c)); } #[test] @@ -567,9 +558,7 @@ fn cubic_mul_commutativity() { #[test] fn cubic_invert_correctness() { let a = el3(1, 2, 3); - assert!(bool::from( - (&a * &a.invert().expect("invertible")).is_one() - )); + assert!(bool::from((&a * &a.invert().expect("invertible")).is_one())); } #[test] @@ -689,5 +678,3 @@ fn sqrtratio_nr_cubic() { let mysqrt = a.sqrt_ratio(&b); assert!(bool::from(mysqrt.is_none())); } - - diff --git a/fp/tests/fp_tests.rs b/fp/tests/fp_tests.rs index 6412293..8f0028a 100644 --- a/fp/tests/fp_tests.rs +++ b/fp/tests/fp_tests.rs @@ -6,10 +6,10 @@ //! - NO `mod tests { }` block (no module nesting needed) //! - Trait methods require the trait to be in scope with `use` -use crypto_bigint::{const_prime_monty_params, Uint}; +use crypto_bigint::{Uint, const_prime_monty_params}; -use fp::field_ops::FieldOps; // brings zero/one/is_zero/invert/… into scope -use fp::fp_element::FpElement; // the concrete type +use fp::field_ops::FieldOps; // brings zero/one/is_zero/invert/… into scope +use fp::fp_element::FpElement; // the concrete type // --------------------------------------------------------------------------- // Define the modulus p = 19 at compile time. @@ -17,7 +17,7 @@ use fp::fp_element::FpElement; // the concrete type // const_prime_monty_params!(TypeName, UintType, hex_modulus, label) // --------------------------------------------------------------------------- //0000000000000013 -> 19 in Hexadecimal -// 2 is a generator (primitive root) of Z/pZ +// 2 is a generator (primitive root) of Z/pZ const_prime_monty_params!(Fp19Modulus, Uint<1>, "0000000000000013", 2); type F19 = FpElement; @@ -89,7 +89,7 @@ fn double() { #[test] fn inv_works() { // 7 × 7^-1 \equiv 1 (mod 19) - let a = F19::from_u64(7); + let a = F19::from_u64(7); let inv = a.invert().unwrap(); assert_eq!((&a * &inv).as_limbs()[0], 1); } @@ -130,4 +130,4 @@ fn sqrt_of_qr() { let four = F19::from_u64(4); let root = four.sqrt().expect("4 is a QR mod 19"); assert_eq!((&root * &root).as_limbs()[0], 4); -} \ No newline at end of file +} diff --git a/isogeny/src/isogeny_ops.rs b/isogeny/src/isogeny_ops.rs index 34af953..d6760a1 100644 --- a/isogeny/src/isogeny_ops.rs +++ b/isogeny/src/isogeny_ops.rs @@ -33,9 +33,9 @@ //! So we keep both associated types explicit instead of assuming that the //! same point type is used on both sides. -use fp::field_ops::FieldOps; -use ec::point_ops::PointOps; use ec::curve_ops::Curve; +use ec::point_ops::PointOps; +use fp::field_ops::FieldOps; use subtle::Choice; /// Generic interface for an isogeny. @@ -62,16 +62,10 @@ pub trait IsogenyOps: Clone { type CodomainCurve: Curve; /// Native point representation used when evaluating points on the domain. - type DomainPoint: PointOps< - Curve = Self::DomainCurve, - BaseField = Self::BaseField, - >; + type DomainPoint: PointOps; /// Native point representation produced when evaluating the isogeny. - type CodomainPoint: PointOps< - Curve = Self::CodomainCurve, - BaseField = Self::BaseField, - >; + type CodomainPoint: PointOps; /// Return the domain curve of the isogeny. fn domain(&self) -> &Self::DomainCurve; @@ -122,12 +116,12 @@ pub trait DualIsogenyOps: IsogenyOps { /// Note how domain and codomain are swapped, and likewise the point /// representations on the source and target. type Dual: IsogenyOps< - BaseField = Self::BaseField, - DomainCurve = Self::CodomainCurve, - CodomainCurve = Self::DomainCurve, - DomainPoint = Self::CodomainPoint, - CodomainPoint = Self::DomainPoint, - >; + BaseField = Self::BaseField, + DomainCurve = Self::CodomainCurve, + CodomainCurve = Self::DomainCurve, + DomainPoint = Self::CodomainPoint, + CodomainPoint = Self::DomainPoint, + >; /// Return the dual isogeny. fn dual(&self) -> Self::Dual; @@ -167,25 +161,24 @@ pub struct CompositeIsogeny { impl IsogenyOps for CompositeIsogeny where -// The first map is an arbitrary isogeny + // The first map is an arbitrary isogeny I1: IsogenyOps, - -// The second map must start where the first one ends. -// -// Concretely, if -// first : E -> E' -// then we require -// second : E' -> E'' -// -// This means: -// - same base field, -// - domain curve of `second` = codomain curve of `first`, -// - domain point type of `second` = codomain point type of `first`. + // The second map must start where the first one ends. + // + // Concretely, if + // first : E -> E' + // then we require + // second : E' -> E'' + // + // This means: + // - same base field, + // - domain curve of `second` = codomain curve of `first`, + // - domain point type of `second` = codomain point type of `first`. I2: IsogenyOps< - BaseField = I1::BaseField, - DomainCurve = I1::CodomainCurve, - DomainPoint = I1::CodomainPoint, - >, + BaseField = I1::BaseField, + DomainCurve = I1::CodomainCurve, + DomainPoint = I1::CodomainPoint, + >, { /// The composite map is still defined over the same base field. type BaseField = I1::BaseField; @@ -229,4 +222,4 @@ where // So the composite is separable exactly when both factors are. self.first.is_separable() & self.second.is_separable() } -} \ No newline at end of file +} diff --git a/isogeny/src/lib.rs b/isogeny/src/lib.rs index 60ab3b5..cf9bbdd 100644 --- a/isogeny/src/lib.rs +++ b/isogeny/src/lib.rs @@ -6,6 +6,6 @@ //! * `ec` – elliptic curve group law pub mod isogeny; -pub mod kernel; mod isogeny_ops; +pub mod kernel; mod montgomery; diff --git a/isogeny/src/montgomery.rs b/isogeny/src/montgomery.rs index c10020c..7cebcbe 100644 --- a/isogeny/src/montgomery.rs +++ b/isogeny/src/montgomery.rs @@ -55,7 +55,7 @@ ref_field_impl! { xk } } - + fn new_codomain_helper(domain: & MontgomeryCurve, xk: KummerPoint) -> (F, F) { todo!() } @@ -81,4 +81,4 @@ ref_field_impl! { -*/ \ No newline at end of file +*/ diff --git a/isogeny/tests/isogeny_tests.rs b/isogeny/tests/isogeny_tests.rs index 66395ed..3fed852 100644 --- a/isogeny/tests/isogeny_tests.rs +++ b/isogeny/tests/isogeny_tests.rs @@ -1,2 +1 @@ //! Integration tests for the `isogeny` crate. - diff --git a/protocol/src/scalar.rs b/protocol/src/scalar.rs index 43d4801..e182299 100644 --- a/protocol/src/scalar.rs +++ b/protocol/src/scalar.rs @@ -49,7 +49,9 @@ impl SecretScalar { impl Default for SecretScalar { fn default() -> Self { - Self { limbs: [0u64; LIMBS] } + Self { + limbs: [0u64; LIMBS], + } } } From 821ad1b04687d0635abdffcff26f811099b62f51 Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Sun, 26 Apr 2026 11:29:39 +0200 Subject: [PATCH 02/14] Improved docs for finite fields --- fp/src/f2_element.rs | 79 ++++++++--- fp/src/f2_ext.rs | 75 +++++++++-- fp/src/fp_ext.rs | 304 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 394 insertions(+), 64 deletions(-) diff --git a/fp/src/f2_element.rs b/fp/src/f2_element.rs index d6245c4..2acc063 100644 --- a/fp/src/f2_element.rs +++ b/fp/src/f2_element.rs @@ -1,5 +1,4 @@ -//! Base binary field element $\mathbb{F}_2 = \mathbb{Z} / -//! 2\mathbb{Z}$ +//! The binary field $\mathbb{F}_2 = \mathbb{Z} / 2\mathbb{Z}$ use crate::field_ops::FieldFromRepr; use crate::field_ops::{FieldOps, FieldRandom}; @@ -18,17 +17,26 @@ pub struct F2Element { // --------------------------------------------------------------------------- impl F2Element { - /// The constant zero + /// The constant $0 \in \mathbb{F}_2$ (type: `Self`) pub const ZERO: Self = Self { value: Uint::<1>::ZERO, }; - /// The constant one + /// The constant $1 \in \mathbb{F}_2$ (type: `Self`) pub const ONE: Self = Self { value: Uint::<1>::ONE, }; - /// Create a new element of $\mathbb{F}_2$ + /// Create a new element of $\mathbb{F}_2$ from a `Uint<1>` + /// + /// # Arguments + /// + /// * `x` - An integer (type: `Uint<1>`) + /// + /// # Returns + /// + /// An element of $\mathbb{F}_2$, the reduction of `x` mod 2 + /// (type: `Self`) fn new(x: Uint<1>) -> Self { Self { value: x & Uint::<1>::ONE, @@ -36,16 +44,43 @@ impl F2Element { } /// Create a new element of $\mathbb{F}_2$ from a `u64` + /// + /// # Arguments + /// + /// * `x` - An integer (type: `u64`) + /// + /// # Returns + /// + /// An element of $\mathbb{F}_2$, the reduction of `x` mod 2 + /// (type: `Self`) pub fn from_u64(x: u64) -> Self { Self::new(Uint::from(x & 1)) } /// Get the `Uint<1>` from an element of $\mathbb{F}_2$ + /// + /// # Arguments + /// + /// * `x` - An element of $\mathbb{F}_2$ (type: `Self`) + /// + /// # Returns + /// + /// The integer in $\{ 0, 1 \}$ reducing to `x` mod 2 (type: + /// `Uint<1>`) pub fn value(&self) -> Uint<1> { self.value } /// Get the `u8` from an element of $\mathbb{F}_2$ + /// + /// # Arguments + /// + /// * `x` - An element of $\mathbb{F}_2$ (type: `Self`) + /// + /// # Returns + /// + /// The integer in $\{ 0, 1 \}$ reducing to `x` mod 2 (type: + /// `u8`) pub fn as_u8(&self) -> u8 { self.value.to_words()[0] as u8 } @@ -89,9 +124,7 @@ impl ConstantTimeEq for F2Element { // Operator overloads // --------------------------------------------------------------------------- -impl<'a, 'b> Add<&'b F2Element> -for &'a F2Element -{ +impl<'a, 'b> Add<&'b F2Element> for &'a F2Element { type Output = F2Element; fn add(self, rhs: &'b F2Element) -> Self::Output { @@ -99,9 +132,7 @@ for &'a F2Element } } -impl<'a, 'b> Sub<&'b F2Element> -for &'a F2Element -{ +impl<'a, 'b> Sub<&'b F2Element> for &'a F2Element { type Output = F2Element; fn sub(self, rhs: &'b F2Element) -> Self::Output { @@ -109,10 +140,7 @@ for &'a F2Element } } - -impl<'a, 'b> Mul<&'b F2Element> -for &'a F2Element -{ +impl<'a, 'b> Mul<&'b F2Element> for &'a F2Element { type Output = F2Element; fn mul(self, rhs: &'b F2Element) -> Self::Output { @@ -120,8 +148,7 @@ for &'a F2Element } } -impl<'a> Neg for &'a F2Element -{ +impl<'a> Neg for &'a F2Element { type Output = F2Element; fn neg(self) -> Self::Output { @@ -129,8 +156,6 @@ impl<'a> Neg for &'a F2Element } } - - // --------------------------------------------------------------------------- // FieldOps implementation // --------------------------------------------------------------------------- @@ -139,6 +164,7 @@ impl FieldOps for F2Element { fn zero() -> Self { Self::ZERO } + fn one() -> Self { Self::ONE } @@ -146,30 +172,40 @@ impl FieldOps for F2Element { fn from_u64(x: u64) -> Self { Self::from_u64(x) } + fn is_zero(&self) -> Choice { Self::ct_eq(self, &Self::zero()) } + fn is_one(&self) -> Choice { Self::ct_eq(self, &Self::one()) } + fn negate(&self) -> Self { *self } + fn add(&self, rhs: &Self) -> Self { Self::new(self.value ^ rhs.value) } + fn sub(&self, rhs: &Self) -> Self { Self::new(self.value ^ rhs.value) } + fn mul(&self, rhs: &Self) -> Self { Self::new(self.value & rhs.value) } + fn square(&self) -> Self { + // x = x^2 for every x in F_2 *self - } // x = x^2 for every x in F_2 + } + fn double(&self) -> Self { Self::ZERO } + fn invert(&self) -> CtOption { let is_invertible = !self.is_zero(); CtOption::new(*self, is_invertible) @@ -178,9 +214,11 @@ impl FieldOps for F2Element { fn frobenius(&self) -> Self { *self } + fn norm(&self) -> Self { *self } + fn trace(&self) -> Self { *self } @@ -197,6 +235,7 @@ impl FieldOps for F2Element { fn characteristic() -> Vec { vec![2] } + fn degree() -> u32 { 1 } diff --git a/fp/src/f2_ext.rs b/fp/src/f2_ext.rs index c088958..62cc15d 100644 --- a/fp/src/f2_ext.rs +++ b/fp/src/f2_ext.rs @@ -6,17 +6,50 @@ use crypto_bigint::Uint; use std::marker::PhantomData; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - // --------------------------------------------------------------------------- // IrreduciblePoly — the only thing callers need to implement for a new field // --------------------------------------------------------------------------- -/// An irreducible polynomial over $\mathbb{F}_2$. +/// Irreducible polynomial defined over $\mathbb{F}_{2}$ +/// +/// # Required Methods +/// +/// * `modulus` - The polynomial defining the extension field +/// implemented as a `Uint` one box for each coefficient +/// * `degree` - The degree of the extension. +/// +/// # Examples +/// +/// ``` +/// # use crypto_bigint::Uint; +/// # use fp::f2_element::F2Element; +/// # use fp::f2_ext::{BinaryIrreducible, F2Ext}; +/// struct MyPoly; +/// +/// impl BinaryIrreducible<1> for MyPoly { +/// fn modulus() -> Uint<1> { +/// Uint::<1>::from_u64(0b1_0001_1011) // x^8 + x^4 + x^3 + x + 1 +/// } +/// +/// fn degree() -> usize { +/// 8usize +/// } +/// } +/// ``` pub trait BinaryIrreducible: 'static { - /// Full polynomial bitmask, including the leading term x^m + /// Full polynomial bitmask, including the leading term $x^m$ + /// + /// # Returns + /// + /// The irreducible polynomial (type: `Uint`). fn modulus() -> Uint; /// Degree m of the irreducible polynomial + /// + /// # Returns + /// + /// The degree of the irreducible polynomial `modulus` (type: + /// `usize`) fn degree() -> usize; } @@ -24,7 +57,30 @@ pub trait BinaryIrreducible: 'static { // F2Ext — element of F_{2^M} // --------------------------------------------------------------------------- -/// An extension of $\mathbb{F}_2$ given by a polynomial `P`. +/// An extension of $\mathbb{F}_2$ given by an irreducible binary +/// polynomial $P \in \mathbb{F}_2\[x\]$. +/// +/// # Examples +/// +/// ``` +/// # use crypto_bigint::Uint; +/// # use fp::f2_element::F2Element; +/// # use fp::f2_ext::{BinaryIrreducible, F2Ext}; +/// struct F2511Poly; +/// +/// impl BinaryIrreducible<8> for F2511Poly { +/// fn modulus() -> Uint<8> { +/// let one = Uint::<8>::from_u64(1); +/// (one << 511) | (one << 10) | one +/// } +/// +/// fn degree() -> usize { +/// 511 +/// } +/// } +/// +/// type GF2_511 = F2Ext<8, F2511Poly>; +/// ``` pub struct F2Ext where P: BinaryIrreducible, @@ -348,8 +404,7 @@ where // Operator overloads (delegate to the FieldOps methods below) // =========================================================================== -impl<'a, 'b, const LIMBS: usize, P> Add<&'b F2Ext> -for &'a F2Ext +impl<'a, 'b, const LIMBS: usize, P> Add<&'b F2Ext> for &'a F2Ext where P: BinaryIrreducible, { @@ -360,8 +415,7 @@ where } } -impl<'a, 'b, const LIMBS: usize, P> Sub<&'b F2Ext> -for &'a F2Ext +impl<'a, 'b, const LIMBS: usize, P> Sub<&'b F2Ext> for &'a F2Ext where P: BinaryIrreducible, { @@ -372,9 +426,7 @@ where } } - -impl<'a, 'b, const LIMBS: usize, P> Mul<&'b F2Ext> -for &'a F2Ext +impl<'a, 'b, const LIMBS: usize, P> Mul<&'b F2Ext> for &'a F2Ext where P: BinaryIrreducible, { @@ -396,7 +448,6 @@ where } } - // =========================================================================== // FieldOps implementation // =========================================================================== diff --git a/fp/src/fp_ext.rs b/fp/src/fp_ext.rs index bc2581f..f9e5d15 100644 --- a/fp/src/fp_ext.rs +++ b/fp/src/fp_ext.rs @@ -156,6 +156,12 @@ where MOD: ConstPrimeMontyParams, { /// Non-leading coefficients `[c_0, c_1, ..., c_{M-1}]` of the defining polynomial. + /// + /// # Returns + /// + /// The non-leading coefficients of the defining polynomial of the + /// field $\mathbb{F}\_{p^M} = \mathbb{F}_p\[x\] / (f(x))$ (type: + /// `[FpElement; M]`) fn modulus() -> [FpElement; M]; } @@ -203,6 +209,7 @@ implement for a new field /// /// # Todo /// +/// - [ ] Implement `root_of_unity` as a `const`. /// - [ ] Compute these constants at compile time to bury this from /// the end user. pub trait TonelliShanksConstants: @@ -210,25 +217,34 @@ pub trait TonelliShanksConstants, { - /// Multiplicative group order $p^M - 1$ + /// The order of the multiplicative $\mathbb{F}\_{p^M}^*$, that is + /// the integer $p^M - 1$. const ORDER: Uint; - /// Half mult group order $(p^M - 1) / 2$ + /// Half of `ORDER`, that is the integer $(p^M - 1) / 2$. const HALF_ORDER: Uint; - /// Write $p^M - 1 = 2^S T$ with $T$ odd + /// The constant $S$ in the 2-primary factorisation of `ORDER`, + /// that is where $p^M - 1 = 2^S T$ with $T$ odd. const S: u64; - /// Write $p^M - 1 = 2^S T$ with $T$ odd + /// The constant $T$ in the 2-primary factorisation of `ORDER`, + /// that is where $p^M - 1 = 2^S T$ with $T$ odd. const T: Uint; - /// Constant $2^{S - 1}$ + /// The integer $2^{S - 1}$. const TWOSM1: Uint; - /// Projenator exponent of the TS algorithm this is $(T - 1) / 2$ + /// The integer $(T - 1) / 2$ which is the exponent of the + /// "projenator" in the Tonelli--Shanks algorithm. const PROJENATOR_EXP: Uint; - /// $2^S$ root of unity + /// A choice of $2^S$ root of unity in $\mathbb{F}\_{p^M}$, + /// respresented as a vector of `FpElement` of length $M$. + /// + /// # Todo + /// + /// - [ ] Write this to be a `const`. fn root_of_unity() -> [FpElement; M]; } @@ -239,16 +255,55 @@ where /// An element of the extension field $\mathbb{F}_{p^M} = /// \mathbb{F}_p\[x\] / (f(x))$. /// -/// `P` is a zero-size marker type implementing [`IrreduciblePoly`]. -/// `M` is the extension degree (number of base-field coefficients stored). -/// `N` is the number limbs needed to store p^M +/// # Generics +/// +/// * `P` is a zero-size marker type implementing [`IrreduciblePoly`]. +/// * `M` is the extension degree (number of base-field coefficients +/// stored). +/// * `N` is the number limbs needed to store $p^M$. +/// +/// # Examples +/// +/// ``` +/// use crypto_bigint::{const_prime_monty_params, Uint}; +/// use fp::field_ops::FieldOps; +/// use fp::fp_element::FpElement; +/// use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// +/// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); +/// type Fp19 = FpElement; +/// +/// struct QuadPoly; +/// struct TSQuad; +/// +/// impl IrreduciblePoly for QuadPoly { +/// fn modulus() -> [Fp19; 2] { +/// [Fp19::one(), Fp19::zero()] +/// } +/// } +/// +/// impl TonelliShanksConstants for TSQuad { +/// const ORDER: Uint<1> = Uint::<1>::from_u64(360); +/// const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); +/// const S: u64 = 3; +/// const T: Uint<1> = Uint::<1>::from_u64(45); +/// const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); +/// const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); +/// fn root_of_unity() -> [FpElement; 2] { +/// [Fp19::from_u64(3), Fp19::from_u64(3)] +/// } +/// } +/// +/// type F19_2 = FpExt; +/// ``` pub struct FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, TSCONSTS: TonelliShanksConstants, { - /// Coefficients in ascending degree, that is `coeffs[i]` is + /// Coefficients of the element $\sum_{i=0}^{M-1} c_i x^{i} \in + /// \mathbb{F}\_{p^M}$ in ascending degree. That is, `coeffs[i]` is /// the coefficient of $x^i$ (zero indexed). pub coeffs: [FpElement; M], _phantom: PhantomData

, @@ -266,7 +321,52 @@ where P: IrreduciblePoly, TSCONSTS: TonelliShanksConstants, { - /// Construct from a coefficient array `[a_0, ..., a_{M-1}]`. + /// Construct an element $\sum_{i=0}^{M-1} c_i x^i \in + /// \mathbb{F}\_{p^M} = \mathbb{F}\_{p}\[x\] / (f(x))$ from a + /// coefficient array $[c_0, ..., c_{M-1}]$. + /// + /// # Arguments + /// + /// * `coeffs` - Coefficients of the element (type: `[FpElement; M]`). + /// + /// # Returns + /// + /// A constructed element of $\mathbb{F}\_{p^M}$ (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use crypto_bigint::{const_prime_monty_params, Uint}; + /// # use fp::field_ops::{FieldOps, FieldRandom}; + /// # use fp::fp_element::FpElement; + /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; + /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); + /// # type Fp19 = FpElement; + /// # + /// # struct QuadPoly; + /// # struct TSQuad; + /// # + /// # impl IrreduciblePoly for QuadPoly { + /// # fn modulus() -> [Fp19; 2] { + /// # [Fp19::one(), Fp19::zero()] + /// # } + /// # } + /// # + /// # impl TonelliShanksConstants for TSQuad { + /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); + /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); + /// # const S: u64 = 3; + /// # const T: Uint<1> = Uint::<1>::from_u64(45); + /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); + /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); + /// # fn root_of_unity() -> [FpElement; 2] { + /// # [Fp19::from_u64(3), Fp19::from_u64(3)] + /// # } + /// # } + /// # + /// # type F19_2 = FpExt; + /// let a = F19_2::new([Fp19::from_u64(3), Fp19::from_u64(5)]); // a = 3 + 5 x + /// ``` pub fn new(coeffs: [FpElement; M]) -> Self { Self { coeffs, @@ -275,14 +375,162 @@ where } } - /// Embed a base-field element as $a + 0x + ... + 0x^{M-1}$. + /// Returns an element $\sum_{i=0}^{M-1} c_i x^i \in + /// \mathbb{F}\_{p^M} = \mathbb{F}\_{p}\[x\] / (f(x))$ from a + /// coefficient array $[c_0, ..., c_{M-1}]$ of `u64` integers. + /// + /// # Arguments + /// + /// * `coeffs` - The coefficient array (type: `[u64; M]`). + /// + /// # Returns + /// + /// The element $\sum_{i=0}^{M-1} c_i x^i \in \mathbb{F}\_{p^M}$ + /// (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use crypto_bigint::{const_prime_monty_params, Uint}; + /// # use fp::field_ops::{FieldOps, FieldRandom}; + /// # use fp::fp_element::FpElement; + /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; + /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); + /// # type Fp19 = FpElement; + /// # + /// # struct QuadPoly; + /// # struct TSQuad; + /// # + /// # impl IrreduciblePoly for QuadPoly { + /// # fn modulus() -> [Fp19; 2] { + /// # [Fp19::one(), Fp19::zero()] + /// # } + /// # } + /// # + /// # impl TonelliShanksConstants for TSQuad { + /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); + /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); + /// # const S: u64 = 3; + /// # const T: Uint<1> = Uint::<1>::from_u64(45); + /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); + /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); + /// # fn root_of_unity() -> [FpElement; 2] { + /// # [Fp19::from_u64(3), Fp19::from_u64(3)] + /// # } + /// # } + /// # + /// # type F19_2 = FpExt; + /// let a = F19_2::new([Fp19::from_u64(3), Fp19::from_u64(5)]); + /// let b = F19_2::from_u64([3, 5]); + /// assert_eq!(a, b); + /// ``` + pub fn from_u64(coeffs: [u64; M]) -> Self { + let coeffs_fp = std::array::from_fn(|i| FpElement::from_u64(coeffs[i])); + Self::new(coeffs_fp) + } + + /// Embed an element of the base-field $\mathbb{F}\_p$ as $a + 0x + + /// \dots + 0x^{M-1} \in \mathbb{F}\_{p^M}$. + /// + /// # Arguments + /// + /// * `a` - Element of $\mathbb{F}_p$ (type: `FpElement`). + /// + /// # Returns + /// + /// The element `a` embedded in $\mathbb{F}\_{p^M}$ in the unique + /// way (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use crypto_bigint::{const_prime_monty_params, Uint}; + /// # use fp::field_ops::{FieldOps, FieldRandom}; + /// # use fp::fp_element::FpElement; + /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; + /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); + /// # type Fp19 = FpElement; + /// # + /// # struct QuadPoly; + /// # struct TSQuad; + /// # + /// # impl IrreduciblePoly for QuadPoly { + /// # fn modulus() -> [Fp19; 2] { + /// # [Fp19::one(), Fp19::zero()] + /// # } + /// # } + /// # + /// # impl TonelliShanksConstants for TSQuad { + /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); + /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); + /// # const S: u64 = 3; + /// # const T: Uint<1> = Uint::<1>::from_u64(45); + /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); + /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); + /// # fn root_of_unity() -> [FpElement; 2] { + /// # [Fp19::from_u64(3), Fp19::from_u64(3)] + /// # } + /// # } + /// # + /// # type F19_2 = FpExt; + /// let a = F19_2::from_base(Fp19::from_u64(3)); + /// let b = F19_2::from_u64([3, 0]); + /// assert_eq!(a, b); // a = 3 + 0 x + /// ``` pub fn from_base(a: FpElement) -> Self { let mut coeffs = std::array::from_fn(|_| FpElement::zero()); coeffs[0] = a; Self::new(coeffs) } - /// Return the coefficient of $x^i$. + /// Return the coefficient of $x^i$ in the representation of + /// `self` in $\mathbb{F}\_{p^M} = \mathbb{F}\_{p}\[x\] / (f(x))$. + /// + /// # Arguments + /// + /// * `i` - The exponent of $x$ (type: `usize`). + /// + /// # Returns + /// + /// The coefficient of $x^i$ as an element of $\mathbb{F}\_p$ + /// (type: `&FpElement`). + /// + /// # Examples + /// + /// ``` + /// # use crypto_bigint::{const_prime_monty_params, Uint}; + /// # use fp::field_ops::{FieldOps, FieldRandom}; + /// # use fp::fp_element::FpElement; + /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; + /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); + /// # type Fp19 = FpElement; + /// # + /// # struct QuadPoly; + /// # struct TSQuad; + /// # + /// # impl IrreduciblePoly for QuadPoly { + /// # fn modulus() -> [Fp19; 2] { + /// # [Fp19::one(), Fp19::zero()] + /// # } + /// # } + /// # + /// # impl TonelliShanksConstants for TSQuad { + /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); + /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); + /// # const S: u64 = 3; + /// # const T: Uint<1> = Uint::<1>::from_u64(45); + /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); + /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); + /// # fn root_of_unity() -> [FpElement; 2] { + /// # [Fp19::from_u64(3), Fp19::from_u64(3)] + /// # } + /// # } + /// # + /// # type F19_2 = FpExt; + /// let a = F19_2::from_u64([3, 5]); + /// assert_eq!(*a.coeff(0), Fp19::from_u64(3)); + /// assert_eq!(*a.coeff(1), Fp19::from_u64(5)); + /// ``` pub fn coeff(&self, i: usize) -> &FpElement { &self.coeffs[i] } @@ -697,8 +945,7 @@ where // =========================================================================== impl<'a, 'b, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> -Add<&'b FpExt> -for &'a FpExt + Add<&'b FpExt> for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, @@ -711,8 +958,7 @@ where } impl<'a, 'b, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> -Sub<&'b FpExt> -for &'a FpExt + Sub<&'b FpExt> for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, @@ -725,8 +971,7 @@ where } impl<'a, 'b, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> -Mul<&'b FpExt> -for &'a FpExt + Mul<&'b FpExt> for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, @@ -738,9 +983,8 @@ where } } -impl<'a, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> -Neg -for &'a FpExt +impl<'a, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> Neg + for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, @@ -752,9 +996,6 @@ where } } - - - // =========================================================================== // Helper functions for Tonelli-Shanks algorithm // =========================================================================== @@ -785,9 +1026,9 @@ fn ts_loop TSCONSTS: TonelliShanksConstants, { let mut v = TSCONSTS::S as u32; - *x = &*x * w ; // to take the prod of a &mut one has to dereference it (so it isn't mut anymore) and then reference to it + *x = &*x * w; // to take the prod of a &mut one has to dereference it (so it isn't mut anymore) and then reference to it let mut b = &*x * w; - //x.mul(*w); + //x.mul(*w); let mut z = FpExt::new(TSCONSTS::root_of_unity()); for max_v in (1..=TSCONSTS::S as u32).rev() { @@ -1070,13 +1311,13 @@ where let is_invertible = !self.is_zero(); let x = rhs * &(self * self); - //self.mul(self).mul(*rhs); + //self.mul(self).mul(*rhs); let (myinv, mysqrt) = x.inverse_and_sqrt(); let myinv_value = myinv.unwrap_or(Self::zero()); let mysqrt_value = mysqrt.unwrap_or(Self::zero()); let inv_value = self * &(rhs * &myinv_value); - let sqrt_value = &inv_value * &mysqrt_value; + let sqrt_value = &inv_value * &mysqrt_value; let inv_is_some = is_invertible & myinv.is_some(); @@ -1113,8 +1354,7 @@ where let ans_value = &self.square() * &(&myinv_value * &mysqrt_value); let inv_is_some = myinv.is_some(); - let ans_is_some = - inv_is_some & mysqrt.is_some() & (mysqrt_value.square()).ct_eq(&x); + let ans_is_some = inv_is_some & mysqrt.is_some() & (mysqrt_value.square()).ct_eq(&x); CtOption::new(ans_value, ans_is_some) } From 13daf5d88c7d46ccd283e1b5db94010346fd060d Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Sun, 26 Apr 2026 11:41:06 +0200 Subject: [PATCH 03/14] Fixed merge --- ec/src/lib.rs | 12 +----------- ec/src/point_twisted_hessian.rs | 4 ++-- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/ec/src/lib.rs b/ec/src/lib.rs index ca56c73..d6a1d4d 100644 --- a/ec/src/lib.rs +++ b/ec/src/lib.rs @@ -10,10 +10,9 @@ pub mod point_ops; pub mod curve_edwards; pub mod curve_hessian; -<<<<<<< HEAD pub mod curve_jacobi_intersection; pub mod curve_jacobi_quartic; -///! Elliptic curve definition in Legendre form. +/// Elliptic curve definition in Legendre form. pub mod curve_legendre; pub mod curve_montgomery; /// Twisted Hessian curves. @@ -29,12 +28,3 @@ pub mod point_montgomery; /// Projective points on twisted Hessian curves. pub mod point_twisted_hessian; pub mod point_weierstrass; -======= -pub mod point_hessian; -pub mod point_legendre; -pub mod curve_legendre; -pub mod curve_twisted_hessian; -pub mod point_twisted_hessian; -pub mod num_utils; -pub mod point_order_ops; ->>>>>>> main diff --git a/ec/src/point_twisted_hessian.rs b/ec/src/point_twisted_hessian.rs index b2b29a2..ee52669 100644 --- a/ec/src/point_twisted_hessian.rs +++ b/ec/src/point_twisted_hessian.rs @@ -15,7 +15,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use crate::curve_twisted_hessian::TwistedHessianCurve; use crate::point_ops::{PointAdd, PointOps}; -use fp::field_ops::{FieldOps}; +use fp::field_ops::FieldOps; use fp::{ref_field_impl, ref_field_trait_impl}; /// A projective point `(X:Y:Z)` on a twisted Hessian curve. @@ -306,4 +306,4 @@ ref_field_trait_impl! { TwistedHessianPoint::::add(self, other, curve) } } -} \ No newline at end of file +} From 11110b0bb4a764b55af2d6b7953d1782270f7531 Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Sun, 26 Apr 2026 11:45:01 +0200 Subject: [PATCH 04/14] Fixed weird issue with loaded modules --- ec/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ec/src/lib.rs b/ec/src/lib.rs index d6a1d4d..a7e1bcb 100644 --- a/ec/src/lib.rs +++ b/ec/src/lib.rs @@ -12,19 +12,17 @@ pub mod curve_edwards; pub mod curve_hessian; pub mod curve_jacobi_intersection; pub mod curve_jacobi_quartic; -/// Elliptic curve definition in Legendre form. pub mod curve_legendre; pub mod curve_montgomery; -/// Twisted Hessian curves. pub mod curve_twisted_hessian; pub mod curve_weierstrass; +pub mod num_utils; pub mod point_edwards; pub mod point_hessian; pub mod point_jacobi_intersection; pub mod point_jacobi_quartic; -/// Point used in the Legendre form. pub mod point_legendre; pub mod point_montgomery; -/// Projective points on twisted Hessian curves. +pub mod point_order_ops; pub mod point_twisted_hessian; pub mod point_weierstrass; From 7963292e854a0f78217551160919ddc87b6873b5 Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Sun, 26 Apr 2026 11:57:59 +0200 Subject: [PATCH 05/14] resolving #70 --- fp/src/fp_ext.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/fp/src/fp_ext.rs b/fp/src/fp_ext.rs index 8a56c91..0299eb8 100644 --- a/fp/src/fp_ext.rs +++ b/fp/src/fp_ext.rs @@ -97,7 +97,7 @@ use std::marker::PhantomData; use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom}; use crate::fp_element::FpElement; -use crypto_bigint::{Uint, modular::ConstPrimeMontyParams}; +use crypto_bigint::{modular::ConstPrimeMontyParams, Uint}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; // =========================================================================== @@ -429,6 +429,60 @@ where Self::new(coeffs_fp) } + /// Returns an element $\sum_{i=0}^{M-1} c_i x^i \in + /// \mathbb{F}\_{p^M} = \mathbb{F}\_{p}\[x\] / (f(x))$ from a + /// coefficient array $[c_0, ..., c_{M-1}]$ of `u64` integers. + /// + /// # Arguments + /// + /// * `coeffs` - The coefficient array (type: `[u64; M]`). + /// + /// # Returns + /// + /// The element $\sum_{i=0}^{M-1} c_i x^i \in \mathbb{F}\_{p^M}$ + /// (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use crypto_bigint::{const_prime_monty_params, Uint}; + /// # use fp::field_ops::{FieldOps, FieldRandom}; + /// # use fp::fp_element::FpElement; + /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; + /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); + /// # type Fp19 = FpElement; + /// # + /// # struct QuadPoly; + /// # struct TSQuad; + /// # + /// # impl IrreduciblePoly for QuadPoly { + /// # fn modulus() -> [Fp19; 2] { + /// # [Fp19::one(), Fp19::zero()] + /// # } + /// # } + /// # + /// # impl TonelliShanksConstants for TSQuad { + /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); + /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); + /// # const S: u64 = 3; + /// # const T: Uint<1> = Uint::<1>::from_u64(45); + /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); + /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); + /// # fn root_of_unity() -> [FpElement; 2] { + /// # [Fp19::from_u64(3), Fp19::from_u64(3)] + /// # } + /// # } + /// # + /// # type F19_2 = FpExt; + /// let a = F19_2::from_u64([3, 5]); + /// let b = F19_2::from_uint([Uint::<1>::from_u64(3), Uint::<1>::from_u64(5)]); + /// assert_eq!(a, b); + /// ``` + pub fn from_uint(coeffs: [Uint; M]) -> Self { + let coeffs_fp = std::array::from_fn(|i| FpElement::from_uint(coeffs[i])); + Self::new(coeffs_fp) + } + /// Embed an element of the base-field $\mathbb{F}\_p$ as $a + 0x + /// \dots + 0x^{M-1} \in \mathbb{F}\_{p^M}$. /// From 258740c15dfad50215ac8b46b352549d0ba5d41d Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Sun, 26 Apr 2026 12:01:11 +0200 Subject: [PATCH 06/14] fix broken doc --- fp/src/fp_ext.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fp/src/fp_ext.rs b/fp/src/fp_ext.rs index 82ba760..875df5e 100644 --- a/fp/src/fp_ext.rs +++ b/fp/src/fp_ext.rs @@ -97,7 +97,7 @@ use std::marker::PhantomData; use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom}; use crate::fp_element::FpElement; -use crypto_bigint::{Uint, modular::ConstPrimeMontyParams}; +use crypto_bigint::{modular::ConstPrimeMontyParams, Uint}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; // =========================================================================== @@ -431,11 +431,12 @@ where /// Returns an element $\sum_{i=0}^{M-1} c_i x^i \in /// \mathbb{F}\_{p^M} = \mathbb{F}\_{p}\[x\] / (f(x))$ from a - /// coefficient array $[c_0, ..., c_{M-1}]$ of `u64` integers. + /// coefficient array $[c_0, ..., c_{M-1}]$ of `Uint` + /// integers. /// /// # Arguments /// - /// * `coeffs` - The coefficient array (type: `[u64; M]`). + /// * `coeffs` - The coefficient array (type: `[Uint; M]`). /// /// # Returns /// From d2c2b9a1ba98ee29e7761d7be242dafb0e42a4a2 Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Mon, 27 Apr 2026 11:03:25 +0200 Subject: [PATCH 07/14] Added doctesting capabilities --- fp/src/_doctest_support.rs | 34 +++++++ fp/src/fp_element.rs | 2 +- fp/src/fp_ext.rs | 190 +++++++------------------------------ fp/src/lib.rs | 3 + 4 files changed, 70 insertions(+), 159 deletions(-) create mode 100644 fp/src/_doctest_support.rs diff --git a/fp/src/_doctest_support.rs b/fp/src/_doctest_support.rs new file mode 100644 index 0000000..6a3238d --- /dev/null +++ b/fp/src/_doctest_support.rs @@ -0,0 +1,34 @@ +//! Hidded modules only used for testing in the documentation + +pub mod _doctest_fp_ext { + use crate::field_ops::FieldOps; + use crate::fp_element::FpElement; + use crate::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; + use crypto_bigint::{const_prime_monty_params, Uint}; + + const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); + pub type Fp19 = FpElement; + + pub struct QuadPoly; + pub struct TSQuad; + + impl IrreduciblePoly for QuadPoly { + fn modulus() -> [Fp19; 2] { + [Fp19::one(), Fp19::zero()] + } + } + + impl TonelliShanksConstants for TSQuad { + const ORDER: Uint<1> = Uint::<1>::from_u64(360); + const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); + const S: u64 = 3; + const T: Uint<1> = Uint::<1>::from_u64(45); + const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); + const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); + fn root_of_unity() -> [FpElement; 2] { + [Fp19::from_u64(3), Fp19::from_u64(3)] + } + } + + pub type F19_2 = FpExt; +} diff --git a/fp/src/fp_element.rs b/fp/src/fp_element.rs index 32b80d8..60e6b00 100644 --- a/fp/src/fp_element.rs +++ b/fp/src/fp_element.rs @@ -6,8 +6,8 @@ use std::fmt; use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom}; use crypto_bigint::{ - NonZero, RandomMod, Uint, modular::{ConstMontyForm, ConstPrimeMontyParams}, + NonZero, RandomMod, Uint, }; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; diff --git a/fp/src/fp_ext.rs b/fp/src/fp_ext.rs index 875df5e..01b6137 100644 --- a/fp/src/fp_ext.rs +++ b/fp/src/fp_ext.rs @@ -118,12 +118,16 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; /// /// The polynomial $f(x) = x^2 + 1$ is irreducible over $\mathbb{F}_{19}$. /// ``` -/// # use crypto_bigint::{const_prime_monty_params, Uint}; -/// # use fp::fp_element::FpElement; -/// # use fp::field_ops::FieldOps; -/// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// use crypto_bigint::{const_prime_monty_params, Uint}; +/// use fp::fp_element::FpElement; +/// use fp::field_ops::FieldOps; +/// use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// +/// /* Setup the underlying prime field */ /// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); /// type Fp19 = FpElement; +/// +/// /* Define an irreducible polynomial */ /// struct MyQuadPoly; /// impl IrreduciblePoly for MyQuadPoly { /// fn modulus() -> [FpElement; 2] { @@ -137,11 +141,15 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; /// /// Note that $f(x) = x^3 + 17$ is irreducible over $\mathbb{F}_{19}$. /// ``` -/// # use crypto_bigint::{const_prime_monty_params, Uint}; -/// # use fp::fp_element::FpElement; -/// # use fp::field_ops::FieldOps; -/// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// use crypto_bigint::{const_prime_monty_params, Uint}; +/// use fp::fp_element::FpElement; +/// use fp::field_ops::FieldOps; +/// use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// +/// /* Setup the underlying prime field */ /// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); +/// +/// /* Define an irreducible polynomial */ /// type Fp19 = FpElement; /// struct MyCubicPoly; /// impl IrreduciblePoly for MyCubicPoly { @@ -182,11 +190,15 @@ implement for a new field /// # Example: $\mathbb{F}_{19^2}$ /// Note that we have a factorisation $19^2 - 1 = 2^3 \cdot 45$ /// ``` -/// # use crypto_bigint::{const_prime_monty_params, Uint}; -/// # use fp::fp_element::FpElement; -/// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// use crypto_bigint::{const_prime_monty_params, Uint}; +/// use fp::fp_element::FpElement; +/// use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// +/// /* Setup the underlying prime field */ /// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); /// type Fp19 = FpElement; +/// +/// /* Write out the precomputed Tonelli--Shanks constants */ /// struct TSQuad; /// impl TonelliShanksConstants for TSQuad { /// // p^2 - 1 @@ -270,18 +282,20 @@ where /// use fp::fp_element::FpElement; /// use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; /// +/// /* Setup the underlying prime field */ /// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); /// type Fp19 = FpElement; /// +/// /* Setup the irreducible poynomial */ /// struct QuadPoly; -/// struct TSQuad; -/// /// impl IrreduciblePoly for QuadPoly { /// fn modulus() -> [Fp19; 2] { /// [Fp19::one(), Fp19::zero()] /// } /// } /// +/// /* Write out the precomputed Tonelli--Shanks constants */ +/// struct TSQuad; /// impl TonelliShanksConstants for TSQuad { /// const ORDER: Uint<1> = Uint::<1>::from_u64(360); /// const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); @@ -336,35 +350,7 @@ where /// # Examples /// /// ``` - /// # use crypto_bigint::{const_prime_monty_params, Uint}; - /// # use fp::field_ops::{FieldOps, FieldRandom}; - /// # use fp::fp_element::FpElement; - /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; - /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); - /// # type Fp19 = FpElement; - /// # - /// # struct QuadPoly; - /// # struct TSQuad; - /// # - /// # impl IrreduciblePoly for QuadPoly { - /// # fn modulus() -> [Fp19; 2] { - /// # [Fp19::one(), Fp19::zero()] - /// # } - /// # } - /// # - /// # impl TonelliShanksConstants for TSQuad { - /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); - /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - /// # const S: u64 = 3; - /// # const T: Uint<1> = Uint::<1>::from_u64(45); - /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); - /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); - /// # fn root_of_unity() -> [FpElement; 2] { - /// # [Fp19::from_u64(3), Fp19::from_u64(3)] - /// # } - /// # } - /// # - /// # type F19_2 = FpExt; + /// # use fp::_doctest_support::_doctest_fp_ext::*; /// let a = F19_2::new([Fp19::from_u64(3), Fp19::from_u64(5)]); // a = 3 + 5 x /// ``` pub fn new(coeffs: [FpElement; M]) -> Self { @@ -391,35 +377,7 @@ where /// # Examples /// /// ``` - /// # use crypto_bigint::{const_prime_monty_params, Uint}; - /// # use fp::field_ops::{FieldOps, FieldRandom}; - /// # use fp::fp_element::FpElement; - /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; - /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); - /// # type Fp19 = FpElement; - /// # - /// # struct QuadPoly; - /// # struct TSQuad; - /// # - /// # impl IrreduciblePoly for QuadPoly { - /// # fn modulus() -> [Fp19; 2] { - /// # [Fp19::one(), Fp19::zero()] - /// # } - /// # } - /// # - /// # impl TonelliShanksConstants for TSQuad { - /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); - /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - /// # const S: u64 = 3; - /// # const T: Uint<1> = Uint::<1>::from_u64(45); - /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); - /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); - /// # fn root_of_unity() -> [FpElement; 2] { - /// # [Fp19::from_u64(3), Fp19::from_u64(3)] - /// # } - /// # } - /// # - /// # type F19_2 = FpExt; + /// # use fp::_doctest_support::_doctest_fp_ext::*; /// let a = F19_2::new([Fp19::from_u64(3), Fp19::from_u64(5)]); /// let b = F19_2::from_u64([3, 5]); /// assert_eq!(a, b); @@ -446,35 +404,7 @@ where /// # Examples /// /// ``` - /// # use crypto_bigint::{const_prime_monty_params, Uint}; - /// # use fp::field_ops::{FieldOps, FieldRandom}; - /// # use fp::fp_element::FpElement; - /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; - /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); - /// # type Fp19 = FpElement; - /// # - /// # struct QuadPoly; - /// # struct TSQuad; - /// # - /// # impl IrreduciblePoly for QuadPoly { - /// # fn modulus() -> [Fp19; 2] { - /// # [Fp19::one(), Fp19::zero()] - /// # } - /// # } - /// # - /// # impl TonelliShanksConstants for TSQuad { - /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); - /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - /// # const S: u64 = 3; - /// # const T: Uint<1> = Uint::<1>::from_u64(45); - /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); - /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); - /// # fn root_of_unity() -> [FpElement; 2] { - /// # [Fp19::from_u64(3), Fp19::from_u64(3)] - /// # } - /// # } - /// # - /// # type F19_2 = FpExt; + /// # use fp::_doctest_support::_doctest_fp_ext::*; /// let a = F19_2::from_u64([3, 5]); /// let b = F19_2::from_uint([Uint::<1>::from_u64(3), Uint::<1>::from_u64(5)]); /// assert_eq!(a, b); @@ -499,35 +429,7 @@ where /// # Examples /// /// ``` - /// # use crypto_bigint::{const_prime_monty_params, Uint}; - /// # use fp::field_ops::{FieldOps, FieldRandom}; - /// # use fp::fp_element::FpElement; - /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; - /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); - /// # type Fp19 = FpElement; - /// # - /// # struct QuadPoly; - /// # struct TSQuad; - /// # - /// # impl IrreduciblePoly for QuadPoly { - /// # fn modulus() -> [Fp19; 2] { - /// # [Fp19::one(), Fp19::zero()] - /// # } - /// # } - /// # - /// # impl TonelliShanksConstants for TSQuad { - /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); - /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - /// # const S: u64 = 3; - /// # const T: Uint<1> = Uint::<1>::from_u64(45); - /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); - /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); - /// # fn root_of_unity() -> [FpElement; 2] { - /// # [Fp19::from_u64(3), Fp19::from_u64(3)] - /// # } - /// # } - /// # - /// # type F19_2 = FpExt; + /// # use fp::_doctest_support::_doctest_fp_ext::*; /// let a = F19_2::from_base(Fp19::from_u64(3)); /// let b = F19_2::from_u64([3, 0]); /// assert_eq!(a, b); // a = 3 + 0 x @@ -553,35 +455,7 @@ where /// # Examples /// /// ``` - /// # use crypto_bigint::{const_prime_monty_params, Uint}; - /// # use fp::field_ops::{FieldOps, FieldRandom}; - /// # use fp::fp_element::FpElement; - /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; - /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); - /// # type Fp19 = FpElement; - /// # - /// # struct QuadPoly; - /// # struct TSQuad; - /// # - /// # impl IrreduciblePoly for QuadPoly { - /// # fn modulus() -> [Fp19; 2] { - /// # [Fp19::one(), Fp19::zero()] - /// # } - /// # } - /// # - /// # impl TonelliShanksConstants for TSQuad { - /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); - /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - /// # const S: u64 = 3; - /// # const T: Uint<1> = Uint::<1>::from_u64(45); - /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); - /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); - /// # fn root_of_unity() -> [FpElement; 2] { - /// # [Fp19::from_u64(3), Fp19::from_u64(3)] - /// # } - /// # } - /// # - /// # type F19_2 = FpExt; + /// # use fp::_doctest_support::_doctest_fp_ext::*; /// let a = F19_2::from_u64([3, 5]); /// assert_eq!(*a.coeff(0), Fp19::from_u64(3)); /// assert_eq!(*a.coeff(1), Fp19::from_u64(5)); diff --git a/fp/src/lib.rs b/fp/src/lib.rs index c787936..045d797 100644 --- a/fp/src/lib.rs +++ b/fp/src/lib.rs @@ -22,3 +22,6 @@ pub mod field_ops; pub mod fp_element; /// Prime-field extension towers and related helper traits. pub mod fp_ext; + +#[doc(hidden)] +pub mod _doctest_support; From 211777e45e582dc882690bf12e85e21409245f56 Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Mon, 27 Apr 2026 13:35:21 +0200 Subject: [PATCH 08/14] More testing :) --- fp/src/fp_ext.rs | 320 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 245 insertions(+), 75 deletions(-) diff --git a/fp/src/fp_ext.rs b/fp/src/fp_ext.rs index 01b6137..65913eb 100644 --- a/fp/src/fp_ext.rs +++ b/fp/src/fp_ext.rs @@ -25,10 +25,6 @@ //! const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); //! type Fp19 = FpElement; //! -//! fn fp(n: u64) -> Fp19 { -//! Fp19::from_u64(n) -//! } -//! //! /* Setput the irreducible polynomial */ //! struct QuadPoly; //! impl IrreduciblePoly for QuadPoly { @@ -61,9 +57,9 @@ //! type F19_2 = FpExt; //! //! /* Some standard tests */ -//! let a = F19_2::new([fp(3), fp(2)]); -//! let ainv = F19_2::new([fp(9), fp(13)]); -//! let asquare = F19_2::new([fp(5), fp(12)]); +//! let a = F19_2::from_u64_vec([3, 2]); +//! let ainv = F19_2::from_u64_vec([9, 13]); +//! let asquare = F19_2::from_u64_vec([5, 12]); //! assert_eq!(a.invert().unwrap(), ainv); //! assert_eq!(a.square(), asquare); //! assert_eq!(asquare.sqrt().unwrap().square(), asquare); @@ -379,10 +375,10 @@ where /// ``` /// # use fp::_doctest_support::_doctest_fp_ext::*; /// let a = F19_2::new([Fp19::from_u64(3), Fp19::from_u64(5)]); - /// let b = F19_2::from_u64([3, 5]); + /// let b = F19_2::from_u64_vec([3, 5]); /// assert_eq!(a, b); /// ``` - pub fn from_u64(coeffs: [u64; M]) -> Self { + pub fn from_u64_vec(coeffs: [u64; M]) -> Self { let coeffs_fp = std::array::from_fn(|i| FpElement::from_u64(coeffs[i])); Self::new(coeffs_fp) } @@ -405,7 +401,8 @@ where /// /// ``` /// # use fp::_doctest_support::_doctest_fp_ext::*; - /// let a = F19_2::from_u64([3, 5]); + /// # use crypto_bigint::Uint; // unclear why this is needed, it should be imported above + /// let a = F19_2::from_u64_vec([3, 5]); /// let b = F19_2::from_uint([Uint::<1>::from_u64(3), Uint::<1>::from_u64(5)]); /// assert_eq!(a, b); /// ``` @@ -431,7 +428,7 @@ where /// ``` /// # use fp::_doctest_support::_doctest_fp_ext::*; /// let a = F19_2::from_base(Fp19::from_u64(3)); - /// let b = F19_2::from_u64([3, 0]); + /// let b = F19_2::from_u64_vec([3, 0]); /// assert_eq!(a, b); // a = 3 + 0 x /// ``` pub fn from_base(a: FpElement) -> Self { @@ -456,7 +453,7 @@ where /// /// ``` /// # use fp::_doctest_support::_doctest_fp_ext::*; - /// let a = F19_2::from_u64([3, 5]); + /// let a = F19_2::from_u64_vec([3, 5]); /// assert_eq!(*a.coeff(0), Fp19::from_u64(3)); /// assert_eq!(*a.coeff(1), Fp19::from_u64(5)); /// ``` @@ -1040,8 +1037,31 @@ where })) } + /// /// Schoolbook multiplication followed by reduction modulo $f(x)$. /// + /// # Arguments + /// + /// * `rhs` - An element of $\mathbb{F}\_{p^M}$ to multiply by + /// (type: `&Self`). + /// + /// # Returns + /// + /// The product `self * rhs` (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let b = F19_2::from_u64_vec([7, 11]); + /// let prod = F19_2::from_u64_vec([4, 11]); + /// assert_eq!(a.mul(&b), prod); + /// ``` + /// + /// # Note + /// /// Product has degree $\leq 2M−2$, then each high-degree term is /// replaced using $x^M \equiv −\sum_j \texttt{modulus}\[j\] x^j$ /// until all degrees are below $M$. @@ -1064,6 +1084,23 @@ where /// Inversion via polynomial extended GCD. /// + /// # Returns + /// + /// Either the inverse of `self` or `none` if `self == 0` (type: + /// `CtOption`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let inv = F19_2::from_u64_vec([4, 6]); + /// assert_eq!(a.invert().unwrap(), inv); + /// ``` + /// + /// # Note + /// /// Finds $s$ such that $\texttt{self} \times s \equiv 1 \pmod{f}$ /// by computing $\gcd(\texttt{self}, f) = g$ (a nonzero constant /// if `self` is nonzero) and then returning $s g^{-1} \pmod{f}$. @@ -1092,21 +1129,60 @@ where // --- Frobenius ---------------------------------------------------------- - /// `φ_p(a) = a^p` — the p-power Frobenius endomorphism. + /// Computes the $p$-power Frobenius endomorphism $\phi_p(a) = a^p$. + /// + /// # Returns + /// + /// The $p$-power of `self` (type: Self) + /// + /// # Examples /// - /// Computed via square-and-multiply using the characteristic p retrieved - /// from the base field. + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let frob_a = F19_2::from_u64_vec([3, 14]); + /// assert_eq!(a.frobenius(), frob_a); + /// ``` + /// + /// # Note + /// + /// Computed via square-and-multiply using the characteristic $p$ + /// retrieved from the base field. fn frobenius(&self) -> Self { let p = FpElement::::characteristic(); - ::pow(self, &p) + // can use pow_vartime since the exponent is always the same + ::pow_vartime(self, &p) } // --- Norm and trace ----------------------------------------------------- - /// `N_{Fp^M/Fp}(a) = ∏_{i=0}^{M-1} φ_p^i(a)` — product of all Galois conjugates. + /// Computes the norm of `self` to $\mathbb{F}\_{p}$ embedded as an element + /// of $\mathbb{F}\_{p^M}$. + /// + /// As a reminder the norm is defined as the product over all the + /// Galois conjugates that is + /// $$ N\_{\mathbb{F}\_{p^M}/\mathbb{F}\_p}(a) = \prod\_{i=0}^{M-1} \phi\_p^i(a) $$ + /// + /// # Returns + /// + /// The norm of self (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let nm_a = F19_2::from_u64_vec([15, 0]); + /// assert_eq!(a.norm(), nm_a); + /// ``` /// - /// The result lies in Fp (all higher coefficients are 0), but is returned - /// embedded in Fp^M for uniformity with the [`FieldOps`] signature. + /// # Note + /// + /// The result lies in $\mathbb{F}\_p$ (all higher coefficients + /// are 0), but is returned embedded in $\mathbb{F}\_{p^M}$ for + /// uniformity with the [`FieldOps`] signature. fn norm(&self) -> Self { let mut result = self.clone(); let mut conj = self.frobenius(); @@ -1117,9 +1193,32 @@ where result } - /// `Tr_{Fp^M/Fp}(a) = Σ_{i=0}^{M-1} φ_p^i(a)` — sum of all Galois conjugates. + /// Computes the trace of `self` to $\mathbb{F}\_{p}$ embedded as an element + /// of $\mathbb{F}\_{p^M}$. + /// + /// As a reminder the trace is defined as the sum over all the + /// Galois conjugates that is + /// $$ \mathrm{tr}\_{\mathbb{F}\_{p^M}/\mathbb{F}\_p}(a) = \sum\_{i=0}^{M-1} \phi\_p^i(a) $$ + /// + /// # Returns /// - /// Like `norm`, the result lies in Fp but is returned embedded in Fp^M. + /// The trace of self (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let tr_a = F19_2::from_u64_vec([6, 0]); + /// assert_eq!(a.trace(), tr_a); + /// ``` + /// + /// # Note + /// + /// The result lies in $\mathbb{F}\_p$ (all higher coefficients + /// are 0), but is returned embedded in $\mathbb{F}\_{p^M}$ for + /// uniformity with the [`FieldOps`] signature. fn trace(&self) -> Self { let mut result = self.clone(); let mut conj = self.frobenius(); @@ -1132,25 +1231,37 @@ where // --- Square root -------------------------------------------------------- - /// Tonelli--Shanks squareroot algorithm + /// Computes the square root of `self` /// - /// Implementation of the Tonelli--Shanks square root algorithm. Requires - /// only a factorisation as $p^M - 1 = 2^K N$ so can compute this at - /// compile time by truncating zeros. + /// # Returns /// - /// # Arguments + /// $\sqrt{\texttt{self}}$ a choice of squareroot (type: `Self`) /// - /// * `&self` - An element of Fp^M (type: &self) + /// # Examples /// - /// # Returns + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 11]); + /// let sqrt_a = a.sqrt().unwrap(); + /// assert_eq!(sqrt_a.mul(&sqrt_a), a); + /// ``` /// - /// `self^(1/2)` a choice of squareroot (type: Self) + /// # Note + /// + /// This is an implementation of the Tonelli--Shanks squareroot + /// algorithm. + /// + /// # TODO + /// + /// - [ ] Generate the addition chain for the projenator exponent + /// this is constant time since the projenator exponent is + /// always(!) the same (for a fixed field). fn sqrt(&self) -> CtOption { let mut x = *self; let exp = TSCONSTS::PROJENATOR_EXP; let exp_limbs = exp.as_limbs().map(|limb| limb.0); - // TODO: generate the addition chain for this specific constant - // this is constant time since exp_limbs is always(!) the same + let w = x.pow_vartime(&exp_limbs); ts_loop(&mut x, &w); CtOption::new( @@ -1159,19 +1270,32 @@ where ) } - /// Inverse and sqrt in one exponentiation + /// Returns both the inverse and sqrt of `self` /// - /// Computes the inverse and squareroot of `self` in one - /// exponentiation using the tricks in Scott's article + /// # Returns /// - /// # Arguments + /// $(1/\texttt{self}, \sqrt{\texttt{self}})$ which is + /// `self.invert()` and `self.sqrt()` (type: `CtOption`, + /// `CtOption`) + /// + /// # Examples /// - /// * `&self` - An element of Fp^M (type: &self) + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 11]); + /// let (inv_a, sqrt_a) = a.inverse_and_sqrt(); + /// let inv_a = inv_a.unwrap(); + /// let sqrt_a = sqrt_a.unwrap(); + /// assert!(bool::from(inv_a.mul(&a).is_one())); + /// assert_eq!(sqrt_a.mul(&sqrt_a), a); + /// ``` /// - /// # Returns + /// # Note /// - /// `(myinv, mysqrt)` which is `self.invert()` and `self.sqrt()` - /// (type: `CtOption`, `CtOption`) + /// Computes the inverse and squareroot of `self` in one + /// exponentiation using the tricks in Scott's article + /// . fn inverse_and_sqrt(&self) -> (CtOption, CtOption) { let is_invertible = !self.is_zero(); @@ -1189,7 +1313,6 @@ where let e1_limbs = e1.as_limbs().map(|limb| limb.0); // Compute x^(2^(S - 1) - 1) * (x * w^4)^(2^(S - 1)) - // pow_vartime? let first_term = self.pow_vartime(&e1_limbs); let xw4 = self * &w.pow_vartime(&[4 as u64]); @@ -1206,36 +1329,65 @@ where ) } - /// Inverse of squareroot of `self` in 1 exponentiation + /// Computes the inverse and squareroot of `self`. /// - /// Computes 1/sqrt(self) using the trick from Mike Scott's - /// "Tricks of the trade" article Section 2 - /// + /// # Returns /// - /// # Arguments + /// $1 / \sqrt{\texttt{self}}$, the inverse of the squareroot of + /// `self` (type: `CtOption`) /// - /// * `&self` - Description of &self (type: self) + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 11]); + /// let inv_sqrt_a = a.inv_sqrt(); + /// let inv_sqrt_a = inv_sqrt_a.unwrap(); + /// assert_eq!(inv_sqrt_a.mul(&inv_sqrt_a), a.invert().unwrap()); + /// ``` /// - /// # Returns + /// # Note /// - /// The inverse of the squareroot of `self` (type: `CtOption`) + /// Computes $1/\sqrt{\texttt{self}$ using the trick from Scott's + /// "Tricks of the trade" article Section 2 + /// fn inv_sqrt(&self) -> CtOption { let (inv, sqrt) = self.inverse_and_sqrt(); inv.and_then(|a| sqrt.map(|b| &a * &b)) } - /// Inverse of `self` and squareroot of `rhs` in 1 exponentiation + /// Inverse of `self` and squareroot of `rhs`. /// - /// Computes `1/self` and `rhs.sqrt()` simulaineously using the - /// trick from Mike Scott's "Tricks of the trade" article Section - /// 2 + /// # Arguments + /// + /// * `rhs` - element of $\mathbb{F}\_{p^M}$ (type: `&Self`) /// /// # Returns /// - /// The inverse of `self` and square root fo `rhs`. Theq former is - /// none if and only if `self` is nonzero and the latter is not - /// none if and only if there exists a squareroot of `rhs` in FpM - /// (type: (`CtOption`, `CtOption`)) + /// The pair $(1 / \texttt{self}, \sqrt{\texttt{rhs}})$. The + /// former is none if and only if `self` is nonzero and the latter + /// is none if and only if there is no squareroot of `rhs` in + /// $\mathbb{F}\_{p^M}$ (type: (`CtOption`, + /// `CtOption`)) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 11]); + /// let b = F19_2::from_u64_vec([2, 11]); + /// let (inv_a, sqrt_b) = a.invertme_sqrtother(&b); + /// let inv_a = inv_a.unwrap(); + /// let sqrt_b = sqrt_b.unwrap(); + /// assert!(bool::from(inv_a.mul(&a).is_one())); + /// assert_eq!(sqrt_b.square(), b); + /// ``` + /// + /// # Note + /// + /// Computes `1/self` and `rhs.sqrt()` simulaineously using the + /// trick from Scott's article Section 2 + /// fn invertme_sqrtother(&self, rhs: &Self) -> (CtOption, CtOption) { let is_invertible = !self.is_zero(); @@ -1260,20 +1412,33 @@ where /// Computes the squareroot of a ratio `self/rhs` /// - /// Computes `sqrt(self/rhs)` in one exponentiation using the - /// trick from Mike Scott's "Tricks of the trade" article Section - /// 2 - /// /// # Arguments - /// - /// * `&self` - Element of FpM (type: self) - /// * `rhs` - Element of FpM (type: &Self) + /// * `rhs` - Element of $\mathbb{F}\_{p^M}$ (type: `&Self`) /// /// # Returns /// - /// The squareroot of the ratio `self/rhs` is not none if and only - /// if `rhs` is invertible and the ratio has an FpM squareroot - /// (type: `CtOption`) + /// $\sqrt{\texttt{self}/\texttt{rhs}}$, the squareroot of the + /// ratio which is not none if and only if `rhs` is invertible and + /// the ratio has an $\mathbb{F}\_{p^M}$ rational squareroot (type: + /// `CtOption`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 11]); + /// let b = F19_2::from_u64_vec([2, 11]); + /// let sqrt_a_over_b = a.sqrt_ratio(&b); + /// let sqrt_a_over_b = sqrt_a_over_b.unwrap(); + /// assert_eq!(sqrt_a_over_b.square().mul(&b), a); + /// ``` + /// + /// # Note + /// + /// Computes $\sqrt{\texttt{self}/\texttt{rhs}}$ in one + /// exponentiation using the trick from Scott's "Tricks of the + /// trade" article Section 2 fn sqrt_ratio(&self, rhs: &Self) -> CtOption { let x = self * &(&(self.square()) * rhs); let (myinv, mysqrt) = x.inverse_and_sqrt(); @@ -1288,20 +1453,25 @@ where CtOption::new(ans_value, ans_is_some) } - /// a is a QR in Fp^M iff a^{(p^M-1)/2} = 1. + /// Implements the "Legendre symbol" which is 1 if and only if + /// `self` is a quadratic residue in $\mathbb{F}\_{p^M}$. /// - /// Implements the "Legendre symbol" which is 1 if and only if we - /// have a quadratic residue in FpM - /// WARNING: Not constant time if `self` is zero + /// As a reminder the "Legendre symbol" for $a \in \mathbb{F}\_{p^M}$ is defined as + /// $$ \begin{cases} 0 & \text{ if $a = 0$,} \\\\ 1 & \text{if $a$ + /// is a QR,} \\\\ -1 & \text{if $a$ is not a QR.} \end{cases} $$ /// - /// # Arguments + /// # Returns /// - /// * `&self` - Element of FpM (type: self) + /// The Legendre symbol of `self` (type: `i8`) /// - /// # Returns + /// # WARNING + /// + /// Not constant time if `self` is zero + /// + /// # Note /// - /// Either `0` if `&self` is `0`, `1` if `&self` is a QR or `-1` if - /// `&self` is not a QR. (type: i8) + /// Implemented using the fact that $a$ is a QR in + /// $\mathbb{F}\_{p^M}$ if and only if $a^{(p^M-1)/2} = 1$. fn legendre(&self) -> i8 { let exp = TSCONSTS::HALF_ORDER; let exp_limbs = exp.as_limbs().map(|limb| limb.0); From c5cc65df31330ceaf4155fab2bea85a78d1eb73d Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Tue, 28 Apr 2026 11:11:32 +0200 Subject: [PATCH 09/14] more tests --- .github/workflows/rust.yml | 2 + ec/src/curve_weierstrass.rs | 58 ++++++++++++++++--- ec/tests/curves_legendre_tests.rs | 3 +- ec/tests/hessian_tests.rs | 4 +- ec/tests/twisted_hessian_tests.rs | 3 +- isogeny/src/isogeny_ops.rs | 92 ++++++++++++------------------- 6 files changed, 93 insertions(+), 69 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6cdec81..2794275 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -23,5 +23,7 @@ jobs: run: cargo build --verbose - name: Run tests run: cargo test --verbose + env: + RUSTFLAGS: "-D warnings" - name: Doc builds run: cargo ourdoc diff --git a/ec/src/curve_weierstrass.rs b/ec/src/curve_weierstrass.rs index ba5f0f0..88bd070 100644 --- a/ec/src/curve_weierstrass.rs +++ b/ec/src/curve_weierstrass.rs @@ -8,17 +8,56 @@ //! y^2 + a_1 xy + a_3 y = x^3 + a_2 x^2 + a_4 x + a_6 //! $$ //! -//! This form is valid in *any* characteristic, including characteristic $2$. +//! This form is valid in *any* characteristic (provided the +//! discriminant is nonzero), including characteristic $2$. This is +//! enabled via the constructor [`WeierstrassCurve::new`]. //! //! # Short Weierstrass specialisation //! -//! When $\mathrm{char}(F) \ne 2, 3$ the curve can be brought to the simpler +//! When $\mathrm{char}(F) \ne 2, 3$ the curve can be brought to the +//! simpler //! //! $$ -//! y^2 = x^3 + ax + b \quad (a_1 = a_2 = a_3 = 0,\; a_4 = a,\; a_6 = b) +//! y^2 = x^3 + ax + b //! $$ //! -//! via the convenience constructor [`WeierstrassCurve::new_short`]. +//! so that $a_1 = a_2 = a_3 = 0$, $a_4 = a$ and a_6 = b$. This is +//! enabled via the constructor [`WeierstrassCurve::new_short`]. +//! +//! # Examples +//! +//! ``` +//! use crypto_bigint::{Uint, const_prime_monty_params}; +//! use ec::curve_weierstrass::WeierstrassCurve; +//! use fp::field_ops::FieldOps; +//! use fp::fp_element::FpElement; +//! +//! const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); +//! type F19 = FpElement; +//! +//! fn fp(n: u64) -> F19 { +//! F19::from_u64(n) +//! } +//! +//! /* y^2 = x^3 + 2*x + 3 */ +//! let c = WeierstrassCurve::new_short(fp(2), fp(3)); +//! let c_too = WeierstrassCurve::new(fp(0), fp(0), fp(0), fp(2), fp(3)); +//! +//! /* They're the same curve */ +//! assert_eq!(c, c_too); +//! +//! /* The a-invariants are correct */ +//! assert_eq!(c.a4, fp(2)); +//! assert_eq!(c.a6, fp(3)); +//! +//! /* Curve is smooth */ +//! assert!(!bool::from(c.discriminant().is_zero())); +//! +//! /* (1,5) is a point on c */ +//! assert!(c.contains(&fp(1), &fp(5))); +//! +//! /* (0,0) is not a point on c */ +//! assert!(!c.contains(&fp(0), &fp(0))); use core::fmt; use fp::field_ops::{FieldOps, FieldRandom}; @@ -38,15 +77,15 @@ use crate::point_weierstrass::AffinePoint; /// case simply has $a_1 = a_2 = a_3 = 0$. #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct WeierstrassCurve { - /// a-invariant + /// $a_1$-invariant pub a1: F, - /// a-invariant + /// $a_2$-invariant pub a2: F, - /// a-invariant + /// $a_3$-invariant pub a3: F, - /// a-invariant + /// $a_4$-invariant pub a4: F, - /// a-invariant + /// $a_6$-invariant pub a6: F, } @@ -130,6 +169,7 @@ ref_field_impl! { // ------------------------------------------------------------------- /// Returns the five $a$-invariants $[a_1, a_2, a_3, a_4, a_6]$. + #[allow(clippy::clone_on_copy)] // want to signal that the a_invariants are copied pub fn a_invariants(&self) -> [F; 5] { [ self.a1.clone(), diff --git a/ec/tests/curves_legendre_tests.rs b/ec/tests/curves_legendre_tests.rs index 7f17be0..a9ba59a 100644 --- a/ec/tests/curves_legendre_tests.rs +++ b/ec/tests/curves_legendre_tests.rs @@ -1,4 +1,4 @@ -use crypto_bigint::{Uint, const_prime_monty_params}; +use crypto_bigint::{const_prime_monty_params, Uint}; use ec::curve_legendre::LegendreCurve; use ec::curve_ops::Curve; use ec::point_legendre::LegendrePoint; @@ -119,6 +119,7 @@ fn legendre_short_weierstrass_coordinate_shift_works() { let jc = c.j_invariant(); let w = c.to_short_weierstrass(); let jw = w.j_invariant(); + assert_eq!(jc, jw); let p = LegendrePoint::new(fp(2), fp(6)); assert!(c.is_on_curve(&p)); diff --git a/ec/tests/hessian_tests.rs b/ec/tests/hessian_tests.rs index 23be843..c2752fa 100644 --- a/ec/tests/hessian_tests.rs +++ b/ec/tests/hessian_tests.rs @@ -1,6 +1,6 @@ //! Integration tests for generalized Hessian curves. -use crypto_bigint::{Uint, const_prime_monty_params}; +use crypto_bigint::{const_prime_monty_params, Uint}; use ec::curve_hessian::HessianCurve; use ec::curve_ops::Curve; @@ -181,6 +181,8 @@ fn hessian_weierstrass_birational_roundtrip() { let wc = curve.to_weierstrass_curve_with_zeta(zeta).unwrap(); let jw = wc.j_invariant(); + assert_eq!(jc, jw); + let wp = curve .map_point_to_weierstrass_with_zeta(&p, zeta) .expect("forward birational map"); diff --git a/ec/tests/twisted_hessian_tests.rs b/ec/tests/twisted_hessian_tests.rs index d11fed3..bf10c62 100644 --- a/ec/tests/twisted_hessian_tests.rs +++ b/ec/tests/twisted_hessian_tests.rs @@ -1,10 +1,9 @@ //! Integration tests for twisted Hessian curves. -use crypto_bigint::{Uint, const_prime_monty_params}; +use crypto_bigint::{const_prime_monty_params, Uint}; use ec::curve_ops::Curve; use ec::curve_twisted_hessian::TwistedHessianCurve; -use ec::point_ops::{PointAdd, PointOps}; use ec::point_twisted_hessian::TwistedHessianPoint; use fp::field_ops::FieldOps; use fp::fp_element::FpElement; diff --git a/isogeny/src/isogeny_ops.rs b/isogeny/src/isogeny_ops.rs index d6760a1..bf5b3fc 100644 --- a/isogeny/src/isogeny_ops.rs +++ b/isogeny/src/isogeny_ops.rs @@ -2,13 +2,10 @@ //! //! # Overview //! -//! An isogeny is a morphism of elliptic curves -//! -//! ```text -//! φ : E -> E' -//! ``` -//! -//! that is also a group homomorphism. +//! An isogeny is a finite morphism of curves +//! $$\phi : E -> E'$$ +//! for which $\phi(O\_E) = O\_{E'}$. This implies that $\phi$ is also +//! a group homomorphism. //! //! This trait is meant to capture the **minimal common interface** //! that any concrete isogeny implementation should expose: @@ -28,7 +25,7 @@ //! For example: //! - the domain could use affine Weierstrass points, //! - the codomain could use projective points, -//! - or an x-only Montgomery/Kummer representation. +//! - or an $x$-only Montgomery/Kummer representation. //! //! So we keep both associated types explicit instead of assuming that the //! same point type is used on both sides. @@ -41,24 +38,22 @@ use subtle::Choice; /// Generic interface for an isogeny. /// /// An implementation of this trait represents a map -/// -/// ```text -/// φ : E -> E' -/// ``` -/// +/// $$\phi : E \to E'$$ /// together with the information needed to evaluate it on points. /// +/// # Note +/// /// The trait is intentionally small: it only describes the common /// functionality of isogenies, without forcing a particular internal -/// representation (kernel-based, Vélu formulas, x-only formulas, chains, etc.). +/// representation (kernel-based, Vélu formulas, $x$-only formulas, chains, etc.). pub trait IsogenyOps: Clone { /// Base field over which both the domain and codomain curves are defined. type BaseField: FieldOps; - /// Domain curve `E` of the isogeny `φ : E -> E'`. + /// Domain curve $E$ of the isogeny $\phi : E \to E'$. type DomainCurve: Curve; - /// Codomain curve `E'` of the isogeny `φ : E -> E'`. + /// Codomain curve $E'$ of the isogeny $\phi : E \to E'$. type CodomainCurve: Curve; /// Native point representation used when evaluating points on the domain. @@ -75,16 +70,13 @@ pub trait IsogenyOps: Clone { /// Return the degree of the isogeny. /// - /// For a composition `ψ ∘ φ`, the degree should be - /// - /// ```text - /// deg(ψ ∘ φ) = deg(ψ) deg(φ). - /// ``` + /// For a composition $\phi \circ \phi$, the degree should be + /// $$ \deg(\psi \circ \phi) = \deg(\psi) $deg(\phi)$$ fn degree(&self) -> u64; /// Evaluate the isogeny at a point of the domain curve. /// - /// If `φ : E -> E'` and `P ∈ E`, this returns `φ(P) ∈ E'`. + /// If $\phi : E \to E'$ and $P \in E$, this returns $\phi(P) \in E'$. fn evaluate(&self, p: &Self::DomainPoint) -> Self::CodomainPoint; /// Return whether the isogeny is separable. @@ -97,17 +89,9 @@ pub trait IsogenyOps: Clone { /// Extension trait for isogenies whose dual isogeny is available. /// /// If -/// -/// ```text -/// φ : E -> E' -/// ``` -/// +/// $$\phi : E \to E'$$ /// is an isogeny, then its dual -/// -/// ```text -/// φ^∨ : E' -> E -/// ``` -/// +/// $$\phi^\vee : E \to E'$$ /// goes in the opposite direction, has the same degree, and satisfies /// the standard duality relations. pub trait DualIsogenyOps: IsogenyOps { @@ -116,12 +100,12 @@ pub trait DualIsogenyOps: IsogenyOps { /// Note how domain and codomain are swapped, and likewise the point /// representations on the source and target. type Dual: IsogenyOps< - BaseField = Self::BaseField, - DomainCurve = Self::CodomainCurve, - CodomainCurve = Self::DomainCurve, - DomainPoint = Self::CodomainPoint, - CodomainPoint = Self::DomainPoint, - >; + BaseField = Self::BaseField, + DomainCurve = Self::CodomainCurve, + CodomainCurve = Self::DomainCurve, + DomainPoint = Self::CodomainPoint, + CodomainPoint = Self::DomainPoint, + >; /// Return the dual isogeny. fn dual(&self) -> Self::Dual; @@ -130,23 +114,19 @@ pub trait DualIsogenyOps: IsogenyOps { /// A wrapper representing the composition of two isogenies. /// /// If -/// -/// ```text -/// first : E -> E' -/// second : E' -> E'', -/// ``` +/// $$\texttt{first} : E \to E'$$ +/// $$\texttt{second} : E' \to E''$$ /// /// then `CompositeIsogeny { first, second }` represents /// -/// ```text -/// second ∘ first : E -> E''. -/// ``` +/// $$ \texttt{second} \circ \texttt{first} : E -> E''.$$ /// -/// # Why a wrapper? +/// # Note /// -/// This is a very convenient generic representation of composition: -/// instead of trying to "simplify" the composite into a single special -/// formula, we just store the two maps and evaluate them one after the other. +/// Why a wrapper? This is a very convenient generic representation of +/// composition: instead of trying to "simplify" the composite into a +/// single special formula, we just store the two maps and evaluate +/// them one after the other. /// /// This is often the cleanest first design, especially when different /// concrete isogeny families may be composed together. @@ -175,18 +155,18 @@ where // - domain curve of `second` = codomain curve of `first`, // - domain point type of `second` = codomain point type of `first`. I2: IsogenyOps< - BaseField = I1::BaseField, - DomainCurve = I1::CodomainCurve, - DomainPoint = I1::CodomainPoint, - >, + BaseField = I1::BaseField, + DomainCurve = I1::CodomainCurve, + DomainPoint = I1::CodomainPoint, + >, { /// The composite map is still defined over the same base field. type BaseField = I1::BaseField; - /// The domain of `second ∘ first` is the domain of `first`. + /// The domain of $\texttt{second} \circ \texttt{first}$ is the domain of `first`. type DomainCurve = I1::DomainCurve; - /// The codomain of `second ∘ first` is the codomain of `second`. + /// The codomain of \texttt{second} \circ \texttt{first} is the codomain of `second`. type CodomainCurve = I2::CodomainCurve; /// Points fed into the composite are points of the domain of `first`. From 2f89aaabe5b2ca35d8f6644f404bf6b465837c21 Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Tue, 28 Apr 2026 11:16:27 +0200 Subject: [PATCH 10/14] fix deadcode error --- isogeny/src/isogeny_ops.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/isogeny/src/isogeny_ops.rs b/isogeny/src/isogeny_ops.rs index bf5b3fc..ce0c462 100644 --- a/isogeny/src/isogeny_ops.rs +++ b/isogeny/src/isogeny_ops.rs @@ -45,7 +45,9 @@ use subtle::Choice; /// /// The trait is intentionally small: it only describes the common /// functionality of isogenies, without forcing a particular internal -/// representation (kernel-based, Vélu formulas, $x$-only formulas, chains, etc.). +/// representation (kernel-based, Vélu formulas, $x$-only formulas, +/// chains, etc.). +#[expect(dead_code)] // TODO: REMOVE pub trait IsogenyOps: Clone { /// Base field over which both the domain and codomain curves are defined. type BaseField: FieldOps; @@ -94,6 +96,7 @@ pub trait IsogenyOps: Clone { /// $$\phi^\vee : E \to E'$$ /// goes in the opposite direction, has the same degree, and satisfies /// the standard duality relations. +#[expect(dead_code)] // TODO: REMOVE pub trait DualIsogenyOps: IsogenyOps { /// Type of the dual isogeny. /// @@ -131,6 +134,7 @@ pub trait DualIsogenyOps: IsogenyOps { /// This is often the cleanest first design, especially when different /// concrete isogeny families may be composed together. #[derive(Clone)] +#[expect(dead_code)] // TODO: REMOVE pub struct CompositeIsogeny { /// The first isogeny in the composition. pub first: I1, From 4f31f85979004ff615afbd5d9d35789d903f41eb Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Tue, 28 Apr 2026 11:21:33 +0200 Subject: [PATCH 11/14] fix deadcode error --- isogeny/src/isogeny_ops.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/isogeny/src/isogeny_ops.rs b/isogeny/src/isogeny_ops.rs index ce0c462..7f1ba3d 100644 --- a/isogeny/src/isogeny_ops.rs +++ b/isogeny/src/isogeny_ops.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] // TODO: Make the code not dead... //! Generic isogeny abstraction. //! //! # Overview @@ -47,7 +48,6 @@ use subtle::Choice; /// functionality of isogenies, without forcing a particular internal /// representation (kernel-based, Vélu formulas, $x$-only formulas, /// chains, etc.). -#[expect(dead_code)] // TODO: REMOVE pub trait IsogenyOps: Clone { /// Base field over which both the domain and codomain curves are defined. type BaseField: FieldOps; @@ -96,7 +96,6 @@ pub trait IsogenyOps: Clone { /// $$\phi^\vee : E \to E'$$ /// goes in the opposite direction, has the same degree, and satisfies /// the standard duality relations. -#[expect(dead_code)] // TODO: REMOVE pub trait DualIsogenyOps: IsogenyOps { /// Type of the dual isogeny. /// @@ -134,7 +133,6 @@ pub trait DualIsogenyOps: IsogenyOps { /// This is often the cleanest first design, especially when different /// concrete isogeny families may be composed together. #[derive(Clone)] -#[expect(dead_code)] // TODO: REMOVE pub struct CompositeIsogeny { /// The first isogeny in the composition. pub first: I1, From 786b8c04614f456f9ec93f0d4627fb98248c9c04 Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Sun, 3 May 2026 12:10:11 +0200 Subject: [PATCH 12/14] Better documentation of `FieldOps` --- fp/src/_doctest_support.rs | 33 ++ fp/src/field_ops.rs | 781 +++++++++++++++++++++++++++++++------ fp/src/fp_element.rs | 35 ++ fp/src/fp_ext.rs | 13 +- 4 files changed, 731 insertions(+), 131 deletions(-) diff --git a/fp/src/_doctest_support.rs b/fp/src/_doctest_support.rs index 6a3238d..b4503da 100644 --- a/fp/src/_doctest_support.rs +++ b/fp/src/_doctest_support.rs @@ -32,3 +32,36 @@ pub mod _doctest_fp_ext { pub type F19_2 = FpExt; } + +pub mod _doctest_field_ops { + use crate::field_ops::FieldOps; + use crate::fp_element::FpElement; + use crate::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; + use crypto_bigint::{const_prime_monty_params, Uint}; + + const_prime_monty_params!(Fp19Modulus, Uint<1>, "0000000000000013", 2); + pub type F19 = FpElement; + + pub struct QuadPoly; + pub struct TSQuad; + + impl IrreduciblePoly for QuadPoly { + fn modulus() -> [F19; 2] { + [F19::one(), F19::zero()] + } + } + + impl TonelliShanksConstants for TSQuad { + const ORDER: Uint<1> = Uint::<1>::from_u64(360); + const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); + const S: u64 = 3; + const T: Uint<1> = Uint::<1>::from_u64(45); + const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); + const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); + fn root_of_unity() -> [FpElement; 2] { + [F19::from_u64(3), F19::from_u64(3)] + } + } + + pub type F19_2 = FpExt; +} diff --git a/fp/src/field_ops.rs b/fp/src/field_ops.rs index 3cf031e..f78d95e 100644 --- a/fp/src/field_ops.rs +++ b/fp/src/field_ops.rs @@ -1,4 +1,5 @@ -//! Core trait that every field element in the tower must implement. +//! The [`FieldOps`](self::field_ops::FieldOps) trait is the core +//! trait that every field must implement. use subtle::{Choice, ConditionallySelectable, CtOption}; @@ -16,76 +17,395 @@ pub trait FieldRandom: Sized { /// This trait abstracts the operations needed by the prime-field layer, /// extension fields, and higher-level elliptic-curve code. /// -/// It combines: -/// - basic ring-style operations (`+`, `-`, `*`, unary `-`); -/// - distinguished constants `zero()` and `one()`; -/// - predicates such as `is_zero()` and `is_one()`; -/// - field-specific operations such as inversion, Frobenius, norm, trace, -/// square roots, and Legendre-symbol-style square testing; -/// - constant-time conditional selection via `subtle::ConditionallySelectable`. +/// # Required Methods +/// +/// * `zero` - Returns the constant zero. +/// * `one` - Returns the constant one. +/// * `from_u64` - Coerces as `u64` into the field. +/// * `is_zero` - Checks if element is zero using constant time `Choice`. +/// * `is_one` - Checks if element is zero using constant time `Choice`. +/// * `negate` - Returns $-\texttt{self}$. +/// * `add` - Returns $\texttt{self} + \texttt{rhs}$. +/// * `sub` - Returns $\texttt{self} - \texttt{rhs}$. +/// * `mul` - Returns $\texttt{self} * \texttt{rhs}$. +/// * `square` - Returns $\texttt{self}^2$. +/// * `double` - Returns $2 * \texttt{self}$. +/// * `invert` - Returns $\texttt{self}^{-1}$. +/// * `frobenius` - Returns $\texttt{self}^{\mathrm{char} \mathbb{F}\_{q}}$. +/// * `norm` - Returns $\mathrm{Norm}\_{\mathbb{F}\_{q} / +/// \mathbb{F}\_{p}} \texttt{self}$. +/// * `trace` - Returns $\mathrm{Tr}\_{\mathbb{F}\_{q} / +/// \mathbb{F}\_{p}} \texttt{self}$. +/// * `sqrt` - Returns $\sqrt{\texttt{self}}$. +/// * `legendre` - Returns the "Legendre symbol" which is 1 if and +/// only if $\texttt{self}$ is square. +/// * `characteristic` - Returns the characteristic of the field. +/// * `degree` - Returns $[\mathbb{F}\_{p^M} : \mathbb{F}_p]$ +/// +/// # Provided Methods +/// +/// * `div` - Returns $\texttt{self} / \texttt{rhs}$. +/// * `pow_vartime` - Returns $\texttt{self}^\texttt{pow}$ using square and +/// multiply. +/// * `pow` - Computes $\texttt{self}^\texttt{pow}$ in constant time using a +/// Montgomery ladder. +/// * `frobenius_pow` - Computes $\texttt{self}^{p^k}$ +/// * `inverse_and_sqrt` - Computes $\texttt{self}^{-1}$ and $\sqrt{\texttt{self}}$. +/// * `inv_sqrt` - Computes $\sqrt{\texttt{self}^{-1}}$. +/// * `invertme_sqrtother` - Computes $\texttt{self}^{-1}$ and $\sqrt{\texttt{rhs}}$. +/// * `sqrt_ratio` - Computes $\sqrt{\texttt{self} / \texttt{rhs}}$. +/// +/// Scalars used by `pow_vartime` and `pow` are encoded as +/// little-endian `u64` limbs, matching the convention used elsewhere +/// in the library. +/// +/// # Note /// /// The trait is intentionally separate from [`FieldRandom`], so downstream code /// that only needs deterministic arithmetic does not need to depend on `rand`. /// -/// Scalars used by exponentiation methods are encoded as little-endian `u64` -/// limbs, matching the convention used elsewhere in the library. +/// # Examples +/// +/// ## The field $\mathbb{F}\_{19}$ +/// +/// ``` +/// use crypto_bigint::{const_prime_monty_params, Uint}; +/// use fp::fp_element::FpElement; +/// use fp::field_ops::FieldOps; +/// +/// /* +/// We will set up the field F_19 using FpElement, see the docs +/// of fp_element::FpElement. +/// */ +/// +/// const_prime_monty_params!(Fp19Modulus, Uint<1>, "0000000000000013", 2); +/// type F19 = FpElement; +/// +/// /* Some standard tests */ +/// let a = F19::from_u64(17); +/// let b = F19::from_u64(5); +/// assert_eq!(a.add(&b), F19::from_u64(3)); +/// assert_eq!(a.sub(&b), F19::from_u64(12)); +/// assert_eq!(a.mul(&b), F19::from_u64(9)); +/// assert_eq!(a.invert().unwrap(), F19::from_u64(9)); +/// ``` +/// +/// ## The field $\mathbb{F}\_{19^2}$ +/// +/// ``` +/// # use crypto_bigint::{const_prime_monty_params, Uint}; +/// # use fp::fp_element::FpElement; +/// # use fp::field_ops::FieldOps; +/// # const_prime_monty_params!(Fp19Modulus, Uint<1>, "0000000000000013", 2); +/// # type F19 = FpElement; +/// /* Continuing from the above example */ +/// use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// +/// /* Setput the irreducible polynomial */ +/// struct QuadPoly; +/// impl IrreduciblePoly for QuadPoly { +/// fn modulus() -> [F19; 2] { +/// [F19::one(), F19::zero()] +/// } +/// } +/// +/// /* Setup the Tonelli--Shanks constants */ +/// struct TSQuad; +/// impl TonelliShanksConstants for TSQuad { +/// // p^2 - 1 +/// const ORDER: Uint<1> = Uint::<1>::from_u64(360); +/// // (p^2 - 1) / 2 +/// const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); +/// // p^2 - 1 = 2^S * T with T odd +/// const S: u64 = 3; +/// const T: Uint<1> = Uint::<1>::from_u64(45); +/// // (T - 1) / 2 +/// const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); +/// // 2^(S - 1) +/// const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); +/// // 2^S root of unity +/// fn root_of_unity() -> [FpElement; 2] { +/// [F19::from_u64(3), F19::from_u64(3)] +/// } +/// } +/// +/// /* Define the extension field */ +/// type F19_2 = FpExt; +/// +/// /* Some standard tests */ +/// /* Some standard tests */ +/// let a = F19_2::from_u64(17); +/// let b = F19_2::from_u64(5); +/// assert_eq!(a.add(&b), F19_2::from_u64(3)); +/// assert_eq!(a.sub(&b), F19_2::from_u64(12)); +/// assert_eq!(a.mul(&b), F19_2::from_u64(9)); +/// assert_eq!(a.invert().unwrap(), F19_2::from_u64(9)); +/// ``` pub trait FieldOps: Sized + Clone + PartialEq + Eq + Default + ConditionallySelectable { /// Create the constant zero + /// + /// # Returns + /// + /// The constant $0 \in \mathbb{F}\_q$ (type: `Self`). fn zero() -> Self; - /// Create the constant one + /// Create the constant one + /// + /// # Returns + /// + /// The constant $1 \in \mathbb{F}\_q$ (type: `Self`). fn one() -> Self; - /// Check if element is zero + /// Convert `u64` to the field. + /// + /// # Arguments + /// + /// * `x` an 64 bit integer (type: `u64`). + /// + /// # Returns + /// + /// The element $x \in \mathbb{F}\_p$ embedded in $\mathbb{F}\_q$ + /// (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let my_zero = F19::from_u64(0); + /// assert_eq!(my_zero, F19::zero()); + /// let my_one = F19::from_u64(1); + /// assert_eq!(my_one, F19::one()); + /// ``` + fn from_u64(x: u64) -> Self; + + /// Checks if an element is zero + /// + /// # Returns + /// + /// `true` if and only if `self` is zero (type: `Choice`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let x = F19::zero(); + /// assert!(bool::from(x.is_zero())); + /// ``` fn is_zero(&self) -> Choice; - /// Check if element is one + /// Checks if an element is one + /// + /// # Returns + /// + /// `true` if and only if `self` is one (type: `Choice`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let x = F19::one(); + /// assert!(bool::from(x.is_one())); + /// ``` fn is_one(&self) -> Choice; /// Negate `self` to `-self` + /// + /// # Returns + /// + /// $-\texttt{self}$ (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let x = F19::from_u64(17); + /// assert_eq!(x.negate(), F19::from_u64(2)); + /// ``` fn negate(&self) -> Self; - /// Add `rhs` to `self` + /// Returns $\texttt{self} + \texttt{rhs}$. + /// + /// # Arguments + /// + /// * `rhs` element of $\mathbb{F}\_{q}$ (type: `&Self`). + /// + /// # Returns + /// + /// $\texttt{self} + \texttt{rhs}$ (type: `Self`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(10); + /// let b = F19::from_u64(11); + /// let a_add_b = a.add(&b); + /// assert_eq!(a_add_b, F19::from_u64(2)); + /// ``` fn add(&self, rhs: &Self) -> Self; - /// Sub `rhs` from `self` + /// Returns $\texttt{self} - \texttt{rhs}$. + /// + /// # Arguments + /// + /// * `rhs` element of $\mathbb{F}\_{q}$ (type: `&Self`). + /// + /// # Returns + /// + /// $\texttt{self} - \texttt{rhs}$ (type: `Self`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(4); + /// let b = F19::from_u64(8); + /// let a_sub_b = a.sub(&b); + /// assert_eq!(a_sub_b, F19::from_u64(15)); + /// ``` fn sub(&self, rhs: &Self) -> Self; - /// Multipliy `self` by `rhs` + /// Returns $\texttt{self} * \texttt{rhs}$. + /// + /// # Arguments + /// + /// * `rhs` element of $\mathbb{F}\_{q}$ (type: `&Self`). + /// + /// # Returns + /// + /// $\texttt{self} * \texttt{rhs}$ (type: `Self`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(4); + /// let b = F19::from_u64(3); + /// let a_times_b = a.mul(&b); + /// assert_eq!(a_times_b, F19::from_u64(12)); + /// ``` fn mul(&self, rhs: &Self) -> Self; - /// Square `self` + /// Returns $\texttt{self}^2$. + /// + /// # Returns + /// + /// $\texttt{self}^2$ (type: `Self`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(4); + /// let a_sq = a.square(); + /// assert_eq!(a_sq, F19::from_u64(16)); + /// ``` fn square(&self) -> Self; - /// Double `self` + /// Returns $2 * \texttt{self}$. + /// + /// # Returns + /// + /// $2 * \texttt{self}$ (type: `Self`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(4); + /// let a_doub = a.double(); + /// assert_eq!(a_doub, F19::from_u64(8)); + /// ``` fn double(&self) -> Self; - /// Invert `self` + /// Returns $\texttt{self}^{-1}$. + /// + /// # Returns + /// + /// $\texttt{self}^{-1}$ which is none if and only if `self` is + /// nonzero (type: `CtOption`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(2); + /// let a_inv = a.invert().unwrap(); + /// assert_eq!(a_inv, F19::from_u64(10)); + /// let z = F19::zero(); + /// let z_inv = z.invert(); + /// assert!(bool::from(z_inv.is_none())); + /// ``` fn invert(&self) -> CtOption; - /// Divide `self` by `rhs` + /// Returns $\texttt{self} / \texttt{rhs}$. + /// + /// # Arguments + /// + /// * `rhs` element of $\mathbb{F}\_{q}$ (type: `&Self`). + /// + /// # Returns + /// + /// $\texttt{self} / \texttt{rhs}$ which is none if and only if + /// `rhs` is nonzero (type: `CtOption`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(4); + /// let b = F19::from_u64(2); + /// let a_div_b = a.div(&b).unwrap(); + /// assert_eq!(a_div_b, F19::from_u64(2)); + /// let z = F19::zero(); + /// let fail = a.div(&z); + /// assert!(bool::from(fail.is_none())); + /// ``` fn div(&self, rhs: &Self) -> CtOption { rhs.invert().map(|inv| self.mul(&inv)) } - /// `self^exp` using square-and multiply (litte-endian bit order) + /// Computes $\texttt{self}^\texttt{exp}$ in vartime. /// /// # Arguments /// - /// * `&self` - Finite field element (type: `Self`) - /// * `exp` - Exponent (type: &[u64]) + /// * `exp` - Exponent (type: `&[u64]`) /// /// # Returns /// - /// `&self^exp` (type: `Self`) + /// `self^exp` (type: `Self`) /// - /// # Warnings + /// # WARNING /// /// This function is constant time only if two exponents `exp1` /// and `exp2` are equal as [u64] (i.e., they are the same number /// AND the same number of limbs) - // - // # Notes + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(3); + /// let a_cubed = a.pow_vartime(&[3]); + /// assert_eq!(a_cubed, F19::from_u64(8)); + /// ``` + /// + /// # Notes + /// + /// The provided function is implemented using square-and + /// multiply (litte-endian bit order). // // `::mul` is used instead of // `result.mul(&base)` because `FieldOps` requires `Mul Self { let mut result = Self::one(); let mut base = self.clone(); @@ -157,10 +489,46 @@ pub trait FieldOps: Sized + Clone + PartialEq + Eq + Default + ConditionallySele result } - /// Compute `self^p` the frobenius acting on `self` + /// Compute $\texttt{self}^p$ the Frobenius endomorphism + /// + /// # Returns + /// + /// $\texttt{self}^p$ (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(9); + /// assert_eq!(a.frobenius(), a); + /// let b = F19_2::from_u64_vec([0,1]); // The element i with i^2 = -1 + /// assert_eq!(b.frobenius(), b.negate()); + /// ``` fn frobenius(&self) -> Self; - /// Compute `self^{p^k}` a power of the frobenius + /// Compute $\texttt{self}^{p^k}$ a power of the Frobenius + /// endomorphism + /// + /// # Arguments + /// + /// * `k` a positive integer (type: `u32`). + /// + /// # Returns + /// + /// $\texttt{self}^{p^k}$ (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(9); + /// assert_eq!(a.frobenius_pow(3), a); + /// let b = F19_2::from_u64_vec([0,1]); // The element i with i^2 = -1 + /// assert_eq!(b.frobenius_pow(2), b); + /// assert_eq!(b.frobenius_pow(3), b.frobenius()); + /// ``` fn frobenius_pow(&self, k: u32) -> Self { let mut result = self.clone(); for _ in 0..k { @@ -169,71 +537,161 @@ pub trait FieldOps: Sized + Clone + PartialEq + Eq + Default + ConditionallySele result } - /// Compute the norm of `self` down to $\mathbb{F}_p$ (as an - /// element of type `Self`) + /// Computes the norm of `self` to $\mathbb{F}\_{p}$ embedded as an element + /// of $\mathbb{F}\_{q}$. + /// + /// As a reminder the norm is defined as the product over all the + /// Galois conjugates that is + /// $$ N\_{\mathbb{F}\_{q}/\mathbb{F}\_p}(a) = \prod\_{\sigma \in \mathrm{Gal}} \sigma(a) $$ + /// + /// # Returns + /// + /// The norm of self (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let nm_a = F19_2::from_u64_vec([15, 0]); + /// assert_eq!(a.norm(), nm_a); + /// ``` fn norm(&self) -> Self; - /// Compute the trace of `self` down to $\mathbb{F}_p$ (as an - /// element of type `Self`) + /// Computes the trace of `self` to $\mathbb{F}\_{p}$ embedded as an element + /// of $\mathbb{F}\_{q}$. + /// + /// As a reminder the trace is defined as the sum over all the + /// Galois conjugates that is + /// $$ \mathrm{tr}\_{\mathbb{F}\_{q}/\mathbb{F}\_p}(a) = \sum\_{\sigma \in \mathrm{Gal}} \sigma(a) $$ + /// + /// # Returns + /// + /// The trace of self (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let tr_a = F19_2::from_u64_vec([6, 0]); + /// assert_eq!(a.trace(), tr_a); + /// ``` fn trace(&self) -> Self; - /// Returns a squareroot if it exists + /// Computes the square root of `self` /// - /// # Arguments + /// # Returns /// - /// * `&self` - Element of $\mathbb{F}_{p^M}$ (type: `Self`) + /// $\sqrt{\texttt{self}}$ a choice of squareroot which is not + /// `none` if the squareroot exists (type: `CtOption`) /// - /// # Returns + /// # Examples /// - /// * $\sqrt{\texttt{self}}$ which is not `none` if and only if - /// the square root of `self` exists (type: `CtOption`) + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(9); + /// let sqrt_a = a.sqrt().unwrap(); + /// assert_eq!(sqrt_a.mul(&sqrt_a), a); + /// ``` fn sqrt(&self) -> CtOption; - /// Computes the inverse and square root of `self` + /// Returns both the inverse and sqrt of `self` /// - /// # Arguments + /// # Returns /// - /// * `&self` - Element of $\mathbb{F}_{p^M}$ (type: `Self`) + /// $(1/\texttt{self}, \sqrt{\texttt{self}})$ which is + /// `self.invert()` and `self.sqrt()` (type: `CtOption`, + /// `CtOption`) /// - /// # Returns + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(9); + /// let (inv_a, sqrt_a) = a.inverse_and_sqrt(); + /// let inv_a = inv_a.unwrap(); + /// let sqrt_a = sqrt_a.unwrap(); + /// assert!(bool::from(inv_a.mul(&a).is_one())); + /// assert_eq!(sqrt_a.mul(&sqrt_a), a); + /// ``` + /// + /// # Note /// - /// * `(inv, sqrt)` the inverse and the square root of `self`. The - /// former is none if and only if nonzero and the latter is not - /// none if and only if there exists a squareroot in - /// $\mathbb{F}_{p^M}$ (type: `(CtOption, CtOption)`) + /// The default implementation just combines the `sqrt` and + /// `invert` methods. This is provided as a separate method so + /// that a motivated user can implement tricks such as those found + /// in Section 2 of . See e.g., + /// implementation in [`FpExt`](crate::fp_ext::FpExt). fn inverse_and_sqrt(&self) -> (CtOption, CtOption) { (self.invert(), self.sqrt()) } - /// Computes the square root the inverse of `self` + /// Computes the inverse and squareroot of `self`. /// - /// # Arguments + /// # Returns /// - /// * `&self` - Element of $\mathbb{F}_{p^M}$ (type: `Self`) + /// $1 / \sqrt{\texttt{self}}$, the inverse of the squareroot of + /// `self` (type: `CtOption`) /// - /// # Returns + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(9); + /// let inv_sqrt_a = a.inv_sqrt(); + /// let inv_sqrt_a = inv_sqrt_a.unwrap(); + /// assert_eq!(inv_sqrt_a.mul(&inv_sqrt_a), a.invert().unwrap()); + /// ``` /// - /// * $1 / \sqrt{\texttt{self}}$. The is not none if and only if - /// `self` is both nonzero there exists a squareroot in - /// $\mathbb{F}_{p^M}$ (type: `CtOption`) + /// # Note + /// + /// The default implementation just combines the `sqrt` and + /// `invert` methods. This is provided as a separate method so + /// that a motivated user can implement tricks such as those found + /// in Section 2 of . See e.g., + /// implementation in [`FpExt`](crate::fp_ext::FpExt). fn inv_sqrt(&self) -> CtOption { self.sqrt().and_then(|s| s.invert()) } - /// Computes the inverse of `self` and square root of `rhs` + /// Inverse of `self` and squareroot of `rhs`. /// /// # Arguments /// - /// * `&self` - Element of $\mathbb{F}_{p^M}$ (type: `Self`) - /// * `rhs` - Element of $\mathbb{F}_{p^M}$ (type: &Self) + /// * `rhs` - element of $\mathbb{F}\_{p^M}$ (type: `&Self`) /// /// # Returns /// - /// * `(inv, sqrt)` where `inv` is the inverse of `self` `sqrt` is - /// the square root fo `rhs`. `inv` is `none` if and only if - /// `self` is zero and the `sqrt` is not none if and only if - /// there exists a squareroot of `rhs` in $\mathbb{F}_{p^M}$ - /// (type: `(CtOption, CtOption)`) + /// $(1 / \texttt{self}, \sqrt{\texttt{rhs}})$, the former is none + /// if and only if `self` is nonzero and the latter is none if and + /// only if there is no squareroot of `rhs` in $\mathbb{F}\_{p^M}$ + /// (type: (`CtOption`, `CtOption`)) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(9); + /// let b = F19::from_u64(4); + /// let (inv_a, sqrt_b) = a.invertme_sqrtother(&b); + /// let inv_a = inv_a.unwrap(); + /// let sqrt_b = sqrt_b.unwrap(); + /// assert!(bool::from(inv_a.mul(&a).is_one())); + /// assert_eq!(sqrt_b.square(), b); + /// ``` + /// + /// # Note + /// + /// The default implementation just combines the `sqrt` and + /// `invert` methods. This is provided as a separate method so + /// that a motivated user can implement tricks such as those found + /// in Section 2 of . See e.g., + /// implementation in [`FpExt`](crate::fp_ext::FpExt). fn invertme_sqrtother(&self, rhs: &Self) -> (CtOption, CtOption) { (self.invert(), rhs.sqrt()) } @@ -241,31 +699,106 @@ pub trait FieldOps: Sized + Clone + PartialEq + Eq + Default + ConditionallySele /// Computes the squareroot of a ratio `self/rhs` /// /// # Arguments - /// - /// * `&self` - Element of $\mathbb{F}_{p^M}$ (type: `Self`) - /// * `rhs` - Element of $\mathbb{F}_{p^M}$ (type: `&Self`) + /// * `rhs` - Element of $\mathbb{F}\_{p^M}$ (type: `&Self`) /// /// # Returns /// - /// * $\sqrt{\texttt{self} / \texttt{rhs}}$ which is not `none` if and only - /// both `rhs` is invertible and the ratio has a rational squareroot - /// (type: `(CtOption, CtOption))` + /// $\sqrt{\texttt{self}/\texttt{rhs}}$, the squareroot of the + /// ratio which is not none if and only if `rhs` is invertible and + /// the ratio has an $\mathbb{F}\_{p^M}$ rational squareroot (type: + /// `CtOption`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(9); + /// let b = F19::from_u64(4); + /// let sqrt_a_over_b = a.sqrt_ratio(&b); + /// let sqrt_a_over_b = sqrt_a_over_b.unwrap(); + /// assert_eq!(sqrt_a_over_b.square().mul(&b), a); + /// ``` + /// + /// # Note + /// + /// The default implementation just combines the `sqrt` and + /// `invert` methods. This is provided as a separate method so + /// that a motivated user can implement tricks such as those found + /// in Section 2 of . See e.g., + /// implementation in [`FpExt`](crate::fp_ext::FpExt). fn sqrt_ratio(&self, rhs: &Self) -> CtOption { rhs.invert().and_then(|inv_rhs| self.mul(&inv_rhs).sqrt()) } - /// Computes the "Legendre symbol" i.e., if 0,1,-1 depending if - /// `self` is 0, a square or a nonsquare. + /// Implements the "Legendre symbol" which is 1 if and only if + /// `self` is a quadratic residue in $\mathbb{F}\_{q}$. + /// + /// As a reminder the "Legendre symbol" for $a \in \mathbb{F}\_{q}$ is defined as + /// $$ \begin{cases} 0 & \text{ if $a = 0$,} \\\\ 1 & \text{if $a$ + /// is a square,} \\\\ -1 & \text{if $a$ is not a square.} \end{cases} $$ + /// + /// # Returns + /// + /// The Legendre symbol of `self` (type: `i8`) + /// + /// # WARNING + /// + /// Not required to be constant time if `self` is zero + /// + /// # Examples + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19::from_u64(9); + /// let b = F19::from_u64(2); + /// let c = F19::zero(); + /// let ls_a = a.legendre(); + /// let ls_b = b.legendre(); + /// let ls_c = c.legendre(); + /// assert_eq!(ls_a, 1_i8); + /// assert_eq!(ls_b, -1_i8); + /// assert_eq!(ls_c, 0_i8); + /// ``` fn legendre(&self) -> i8; /// Returns the characteristic of the field. + /// + /// # Returns + /// + /// The characteristic of $\mathbb{F}\_{q}$ as 64 bit limbs (type: + /// `Vec`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let p = F19::characteristic(); + /// let p_2 = F19_2::characteristic(); + /// assert_eq!(p, p_2); + /// assert_eq!(p[0], 19_u64); + /// ``` fn characteristic() -> Vec; - /// Returns the extension degree of the field. + /// Returns the degree of the field over $\mathbb{F}_p$. + /// + /// # Returns + /// + /// The degree of $\mathbb{F}\_{q} / \mathbb{F}\_{p}$ (type: + /// `u32`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::FieldOps; + /// let d_1 = F19::degree(); + /// let d_2 = F19_2::degree(); + /// assert_eq!(d_1, 1_u32); + /// assert_eq!(d_2, 2_u32); + /// ``` fn degree() -> u32; - - /// Convert u64 to the field. - fn from_u64(x: u64) -> Self; } /// Trait for field types that can be constructed from a field-specific /// representation. @@ -313,10 +846,10 @@ macro_rules! ref_field_impl { impl<$F> $Ty where $F: $crate::field_ops::FieldOps, - for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, - for<'a> &'a $F: std::ops::Neg, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, { $($body)* } @@ -326,10 +859,10 @@ macro_rules! ref_field_impl { impl<$F: $First $(+ $Rest)*> $Ty where $F: $crate::field_ops::FieldOps, - for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, - for<'a> &'a $F: std::ops::Neg, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, { $($body)* } @@ -370,10 +903,10 @@ macro_rules! ref_field_trait_impl { impl<$F> $Trait for $Ty where $F: $crate::field_ops::FieldOps, - for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, - for<'a> &'a $F: std::ops::Neg, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, { $($body)* } @@ -383,10 +916,10 @@ macro_rules! ref_field_trait_impl { impl<$F: $First $(+ $Rest)*> $Trait for $Ty where $F: $crate::field_ops::FieldOps, - for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, - for<'a> &'a $F: std::ops::Neg, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, { $($body)* } @@ -434,10 +967,10 @@ macro_rules! ref_field_trait_impl_path { impl<$F> $Trait for $Ty where $F: $crate::field_ops::FieldOps, - for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, - for<'a> &'a $F: std::ops::Neg, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, { $($body)* } @@ -447,10 +980,10 @@ macro_rules! ref_field_trait_impl_path { impl<$F: $First $(+ $Rest)*> $Trait for $Ty where $F: $crate::field_ops::FieldOps, - for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, - for<'a> &'a $F: std::ops::Neg, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, { $($body)* } @@ -477,10 +1010,10 @@ macro_rules! ref_field_fn { fn $name<$F>($($args)*) -> $Ret where $F: $crate::field_ops::FieldOps, - for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, - for<'a> &'a $F: std::ops::Neg, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, $body }; @@ -488,10 +1021,10 @@ macro_rules! ref_field_fn { fn $name<$F: $First $(+ $Rest)*>($($args)*) -> $Ret where $F: $crate::field_ops::FieldOps, - for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, - for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, - for<'a> &'a $F: std::ops::Neg, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, $body }; } @@ -521,24 +1054,24 @@ macro_rules! ref_field_fns { ( $(#[$meta:meta])* fn $name:ident<$F:ident> ( $($args:tt)* ) -> $Ret:ty $body:block - $($rest:tt)* + $($rest:tt)* ) => { $(#[$meta])* - $crate::ref_field_fn! { - fn $name<$F>($($args)*) -> $Ret $body - } + $crate::ref_field_fn! { + fn $name<$F>($($args)*) -> $Ret $body + } $crate::ref_field_fns! { $($rest)* } }; ( $(#[$meta:meta])* fn $name:ident<$F:ident : $First:ident $(+ $Rest:ident)*> ( $($args:tt)* ) -> $Ret:ty $body:block - $($rest:tt)* + $($rest:tt)* ) => { $(#[$meta])* - $crate::ref_field_fn! { - fn $name<$F: $First $(+ $Rest)*>($($args)*) -> $Ret $body - } + $crate::ref_field_fn! { + fn $name<$F: $First $(+ $Rest)*>($($args)*) -> $Ret $body + } $crate::ref_field_fns! { $($rest)* } }; } diff --git a/fp/src/fp_element.rs b/fp/src/fp_element.rs index 60e6b00..3e10a3b 100644 --- a/fp/src/fp_element.rs +++ b/fp/src/fp_element.rs @@ -1,5 +1,38 @@ //! Base prime-field element $\mathbb{F}_p = \mathbb{Z} / p\mathbb{Z}$ //! backed by `crypto-bigint`. +//! +//! # Overview +//! +//! Given an odd prime $p$ supplies $\mathbb{F}_p$ the finite field with +//! $p$ elements. +//! +//! This module provides a single generic type [`FpElement`](self::fp_element::FpElement). +//! +//! # Example +//! +//! ``` +//! use crypto_bigint::{const_prime_monty_params, Uint}; +//! use fp::fp_element::FpElement; +//! use fp::field_ops::FieldOps; +//! +//! /* +//! We will set up the field F_19 so note that 0000000000000013 is +//! equal to 19 in Hexadecimal and that 2 is a generator (primitive +//! root) of (Z/pZ)^* +//! */ +//! +//! const_prime_monty_params!(Fp19Modulus, Uint<1>, "0000000000000013", 2); +//! type F19 = FpElement; +//! +//! /* Some standard tests */ +//! let a = F19::from_u64(17); +//! let b = F19::from_u64(5); +//! assert_eq!((&a + &b).as_limbs()[0], 3); +//! assert_eq!((&a - &b).as_limbs()[0], 12); +//! assert_eq!((&a * &b).as_limbs()[0], 9); +//! assert_eq!((a.invert().unwrap()).as_limbs()[0], 9); +//! ``` use core::ops::{Add, Mul, Neg, Sub}; use std::fmt; @@ -14,6 +47,8 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; /// An element of the prime field $\mathbb{F}_p = /// \mathbb{Z}/p\mathbb{Z}$, stored in Montgomery form. /// +/// # Note +/// /// The internal value uses `crypto-bigint`'s [`ConstMontyForm`], so arithmetic /// is performed in Montgomery representation while the public constructors and /// accessors accept and return canonical integers. diff --git a/fp/src/fp_ext.rs b/fp/src/fp_ext.rs index 65913eb..33856f5 100644 --- a/fp/src/fp_ext.rs +++ b/fp/src/fp_ext.rs @@ -1037,7 +1037,6 @@ where })) } - /// /// Schoolbook multiplication followed by reduction modulo $f(x)$. /// /// # Arguments @@ -1235,7 +1234,8 @@ where /// /// # Returns /// - /// $\sqrt{\texttt{self}}$ a choice of squareroot (type: `Self`) + /// $\sqrt{\texttt{self}}$ a choice of squareroot which is not + /// `none` if the squareroot exists (type: `CtOption`) /// /// # Examples /// @@ -1363,11 +1363,10 @@ where /// /// # Returns /// - /// The pair $(1 / \texttt{self}, \sqrt{\texttt{rhs}})$. The - /// former is none if and only if `self` is nonzero and the latter - /// is none if and only if there is no squareroot of `rhs` in - /// $\mathbb{F}\_{p^M}$ (type: (`CtOption`, - /// `CtOption`)) + /// $(1 / \texttt{self}, \sqrt{\texttt{rhs}})$, the former is none + /// if and only if `self` is nonzero and the latter is none if and + /// only if there is no squareroot of `rhs` in $\mathbb{F}\_{p^M}$ + /// (type: (`CtOption`, `CtOption`)) /// /// # Examples /// From 8a1197a58e67a5365536d59ca68b661134f847f1 Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Sun, 3 May 2026 16:38:43 +0200 Subject: [PATCH 13/14] More doctesting --- fp/src/_doctest_support.rs | 19 ++++ fp/src/f2_ext.rs | 193 ++++++++++++++++++++++++++++++++++--- fp/src/field_ops.rs | 43 ++++++++- fp/src/fp_element.rs | 2 +- 4 files changed, 240 insertions(+), 17 deletions(-) diff --git a/fp/src/_doctest_support.rs b/fp/src/_doctest_support.rs index b4503da..2b8be6b 100644 --- a/fp/src/_doctest_support.rs +++ b/fp/src/_doctest_support.rs @@ -65,3 +65,22 @@ pub mod _doctest_field_ops { pub type F19_2 = FpExt; } + +pub mod _doctest_f2_ext { + use crate::f2_ext::{BinaryIrreducible, F2Ext}; + use crypto_bigint::Uint; + + pub struct F4Poly; + + impl BinaryIrreducible<1> for F4Poly { + fn modulus() -> Uint<1> { + Uint::<1>::from_u64(0b111) // x^2 + x + 1 + } + + fn degree() -> usize { + 2usize + } + } + + pub type F4 = F2Ext<1, F4Poly>; +} diff --git a/fp/src/f2_ext.rs b/fp/src/f2_ext.rs index 62cc15d..a00a818 100644 --- a/fp/src/f2_ext.rs +++ b/fp/src/f2_ext.rs @@ -1,4 +1,38 @@ //! Generic binary fields $\mathbb{F}\_{2^m} = \mathbb{F}\_2\[x\] / (f(x))$ +//! +//! # Examples +//! +//! ``` +//! use crypto_bigint::Uint; +//! use fp::f2_element::F2Element; +//! use fp::f2_ext::{BinaryIrreducible, F2Ext}; +//! use fp::field_ops::FieldOps; +//! +//! /* Make the finite field F_4 */ +//! struct F4Poly; +//! impl BinaryIrreducible<1> for F4Poly { +//! fn modulus() -> Uint<1> { +//! Uint::<1>::from_u64(0b111) // x^2 + x + 1 +//! } +//! +//! fn degree() -> usize { +//! 2usize +//! } +//! } +//! type F4 = F2Ext<1, F4Poly>; +//! +//! /* Elements are written down by binary integers */ +//! let zero = F4::from_u64(0); +//! let one = F4::from_u64(1); +//! let a = F4::from_u64(0b11); +//! let b = F4::from_u64(0b10); +//! +//! let also_zero = F4::from_u64(0b111); // x^2 + x + 1 = 0 +//! let also_one = F4::from_u64(0b1000); // x^3 = x*x^2 = x^2 + x = 1 +//! assert_eq!(also_zero, zero); +//! assert_eq!(also_one, one); +//! assert_eq!(a.mul(&b), one); +//! ``` use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom}; use core::ops::{Add, Mul, Neg, Sub}; @@ -21,9 +55,10 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; /// # Examples /// /// ``` -/// # use crypto_bigint::Uint; -/// # use fp::f2_element::F2Element; -/// # use fp::f2_ext::{BinaryIrreducible, F2Ext}; +/// use crypto_bigint::Uint; +/// use fp::f2_element::F2Element; +/// use fp::f2_ext::{BinaryIrreducible, F2Ext}; +/// /// struct MyPoly; /// /// impl BinaryIrreducible<1> for MyPoly { @@ -63,11 +98,25 @@ pub trait BinaryIrreducible: 'static { /// # Examples /// /// ``` -/// # use crypto_bigint::Uint; -/// # use fp::f2_element::F2Element; -/// # use fp::f2_ext::{BinaryIrreducible, F2Ext}; -/// struct F2511Poly; +/// use crypto_bigint::Uint; +/// use fp::f2_element::F2Element; +/// use fp::f2_ext::{BinaryIrreducible, F2Ext}; +/// +/// /* Make the finite field F_4 */ +/// struct F4Poly; +/// impl BinaryIrreducible<1> for F4Poly { +/// fn modulus() -> Uint<1> { +/// Uint::<1>::from_u64(0b111) // x^2 + x + 1 +/// } /// +/// fn degree() -> usize { +/// 2usize +/// } +/// } +/// type F4 = F2Ext<1, F4Poly>; +/// +/// /* Make the finite field F_{2^511} */ +/// struct F2511Poly; /// impl BinaryIrreducible<8> for F2511Poly { /// fn modulus() -> Uint<8> { /// let one = Uint::<8>::from_u64(1); @@ -78,8 +127,7 @@ pub trait BinaryIrreducible: 'static { /// 511 /// } /// } -/// -/// type GF2_511 = F2Ext<8, F2511Poly>; +/// type F2_511 = F2Ext<8, F2511Poly>; /// ``` pub struct F2Ext where @@ -98,30 +146,145 @@ impl F2Ext where P: BinaryIrreducible, { - /// Make an element from the limbs - pub fn new(x: Uint) -> Self { + /// Make an element of the extension from the limbs + /// + /// # Arguments + /// + /// * `a` - An integer written in binary as $a_0 \dots a_{64 * + /// \texttt{LIMBS}}$ and padded with 0s if nessicary (type: + /// `Uint`). + /// + /// # Returns + /// + /// The element $\sum_{i=0}^M a_i x^i \in \mathbb{F}\_{2}\[x\] / + /// (f(x)) = \mathbb{F}\_{2^M}$ (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_f2_ext::*; + /// # use crypto_bigint::Uint; + /// # use fp::field_ops::FieldOps; + /// let a = F4::new(Uint::<1>::from_u64(0b111)); // x^2 + x + 1 should be zero + /// let b = F4::new(Uint::<1>::from_u64(0b11)); // x + 1 = x^2 + /// let c = F4::new(Uint::<1>::from_u64(0b100)); // x^2 = x + 1 + /// let d = F4::new(Uint::<1>::from_u64(0b1000)); // x^3 = x^2 * x = x^2 + x = 1 + /// assert!(bool::from(a.is_zero())); + /// assert_eq!(b, c); + /// assert!(bool::from(d.is_one())); + /// ``` + pub fn new(a: Uint) -> Self { Self { - value: reduce::(x), + value: reduce::(a), _phantom: PhantomData, } } - /// Make an element from a `u64` + /// Make an element of the extension from a `u64` + /// + /// # Arguments + /// + /// * `a` - An integer written in binary as $a_0 \dots a_{64}$ and + /// padded with 0s to 64 bits if nessicary (type: `u64`). + /// + /// # Returns + /// + /// The element $\sum_{i=0}^{64} a_i x^i \in \mathbb{F}\_{2}\[x\] / + /// (f(x)) = \mathbb{F}\_{2^M}$ (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_f2_ext::*; + /// # use crypto_bigint::Uint; + /// # use fp::field_ops::FieldOps; + /// let a = F4::from_u64(0b111); // x^2 + x + 1 should be zero + /// let b = F4::from_u64(0b11); // x + 1 should be x^2 + /// let c = F4::from_u64(0b100); // x + 1 should be x^2 + /// let d = F4::from_u64(0b1000); // x^3 = x^2 * x = x^2 + x = 1 + /// assert!(bool::from(a.is_zero())); + /// assert_eq!(b, c); + /// assert!(bool::from(d.is_one())); + /// ``` pub fn from_u64(x: u64) -> Self { Self::new(Uint::from(x)) } - /// Make an element from a `Uint` + /// Make an element of the extension from the limbs + /// + /// # Arguments + /// + /// * `a` - An integer written in binary as $a_0 a_1 a_2 \dots + /// a_{M}$ and then padded to the nearest 64 bits (type: + /// `Uint`). + /// + /// # Returns + /// + /// The element $\sum_{i=0}^M a_i x^i \in \mathbb{F}\_{2}\[x\] / + /// (f(x)) = \mathbb{F}\_{2^M}$ (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_f2_ext::*; + /// # use crypto_bigint::Uint; + /// # use fp::field_ops::FieldOps; + /// let a = F4::from_uint(Uint::<1>::from_u64(0b111)); // x^2 + x + 1 should be zero + /// let b = F4::from_uint(Uint::<1>::from_u64(0b11)); // x + 1 = x^2 + /// let c = F4::from_uint(Uint::<1>::from_u64(0b100)); // x^2 = x + 1 + /// let d = F4::from_uint(Uint::<1>::from_u64(0b1000)); // x^3 = x^2 * x = x^2 + x = 1 + /// assert!(bool::from(a.is_zero())); + /// assert_eq!(b, c); + /// assert!(bool::from(d.is_one())); + /// ``` + /// + /// # Note + /// + /// This is just an alias for [`F2Ext::new`] pub fn from_uint(x: Uint) -> Self { Self::new(x) } - /// Get a `Unit` from an element + /// Get a `Uint` from an element, inverting [`F2Ext::new`]. + /// + /// # Returns + /// + /// The integer $a$ of at most $M$ bits such that applying `new` + /// gives `self`. That is, if $a$ is written in binary as $a_0 + /// \dots a_M$ then $\texttt{self} = \sum_{i=0}^M a_i x^i \in + /// \mathbb{F}\_{2}\[x\] / (f(x)) = \mathbb{F}\_{2^M}$ (type: + /// `Uint`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_f2_ext::*; + /// # use crypto_bigint::Uint; + /// # use fp::field_ops::FieldOps; + /// let a = F4::from_uint(Uint::<1>::from_u64(0b11)); // x + 1 + /// let b = F4::from_uint(Uint::<1>::from_u64(0b1000)); // x^3 = x^2 * x = x^2 + x = 1 + /// assert_eq!(a.as_uint(), Uint::<1>::from_u64(0b11)); + /// assert_eq!(b.as_uint(), Uint::<1>::from_u64(0b1)); + /// ``` pub fn as_uint(&self) -> Uint { self.value } /// Get the degree of the field extension + /// + /// # Returns + /// + /// The degree of the field extension (type: `usize`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_f2_ext::*; + /// # use crypto_bigint::Uint; + /// # use fp::field_ops::FieldOps; + /// let d = F4::degree(); + /// assert_eq!(d, 2_usize); + /// ``` pub fn degree() -> usize { P::degree() } diff --git a/fp/src/field_ops.rs b/fp/src/field_ops.rs index f78d95e..bac7ec1 100644 --- a/fp/src/field_ops.rs +++ b/fp/src/field_ops.rs @@ -7,8 +7,49 @@ use subtle::{Choice, ConditionallySelectable, CtOption}; /// /// Separated from [`FieldOps`] so that downstream code that doesn't /// need randomness is free of the `rand` dependency. +/// +/// # Required Methods +/// +/// * `random` - Generate a random field element. +/// +/// # Examples +/// +/// ``` +/// use crypto_bigint::{const_prime_monty_params, Uint}; +/// use fp::fp_element::FpElement; +/// use fp::field_ops::{FieldOps, FieldRandom}; +/// +/// /* +/// We will set up the field F_19 using FpElement, see the docs +/// of fp_element::FpElement. +/// */ +/// +/// const_prime_monty_params!(Fp19Modulus, Uint<1>, "0000000000000013", 2); +/// type F19 = FpElement; +/// +/// let mut rng = rand::rng(); +/// let a = F19::random(&mut rng); +/// ``` pub trait FieldRandom: Sized { - /// Sample a uniformly random element using a cryptographic RNG. + /// Sample a uniformly random element using a cryptographic random + /// number generator. + /// + /// # Arguments + /// + /// * `rng` a cryptographic rng (type: `&mut (impl rand::CryptoRng + rand::Rng)`) + /// + /// # Returns + /// + /// A random element of $\mathbb{F}\_{q}$ (type: `Self`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_field_ops::*; + /// # use fp::field_ops::{FieldOps, FieldRandom}; + /// let mut rng = rand::rng(); + /// let a = F19::random(&mut rng); + /// ``` fn random(rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self; } diff --git a/fp/src/fp_element.rs b/fp/src/fp_element.rs index 3e10a3b..e6bf528 100644 --- a/fp/src/fp_element.rs +++ b/fp/src/fp_element.rs @@ -408,7 +408,7 @@ impl FieldRandom for FpElement where MOD: ConstPrimeMontyParams, { - /// Sample a uniformly random element of Fp using a CSPRNG. + /// Sample a uniformly random element of $\mathbb{F}\_p$ using a CSPRNG. fn random(rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self { let minus_one = ConstMontyForm::::ZERO - ConstMontyForm::::ONE; let p_minus_1: Uint = minus_one.retrieve(); From c2d8e7200550c203d96c5f84b743368c9618bee7 Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Mon, 4 May 2026 10:03:27 +0200 Subject: [PATCH 14/14] Improved docs for --- fp/src/f2_element.rs | 56 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/fp/src/f2_element.rs b/fp/src/f2_element.rs index 2acc063..28f54af 100644 --- a/fp/src/f2_element.rs +++ b/fp/src/f2_element.rs @@ -1,4 +1,18 @@ //! The binary field $\mathbb{F}_2 = \mathbb{Z} / 2\mathbb{Z}$ +//! +//! # Examples +//! +//! ``` +//! use fp::f2_element::F2Element; +//! use fp::field_ops::FieldOps; +//! use crypto_bigint::Uint; +//! +//! let x = F2Element::from_u64(0); +//! let y = F2Element::from_u64(1); +//! assert!(bool::from(x.is_zero())); +//! assert!(bool::from(y.is_one())); +//! assert_eq!(F2Element::characteristic(), [2]); +//! ``` use crate::field_ops::FieldFromRepr; use crate::field_ops::{FieldOps, FieldRandom}; @@ -28,15 +42,6 @@ impl F2Element { }; /// Create a new element of $\mathbb{F}_2$ from a `Uint<1>` - /// - /// # Arguments - /// - /// * `x` - An integer (type: `Uint<1>`) - /// - /// # Returns - /// - /// An element of $\mathbb{F}_2$, the reduction of `x` mod 2 - /// (type: `Self`) fn new(x: Uint<1>) -> Self { Self { value: x & Uint::<1>::ONE, @@ -53,11 +58,22 @@ impl F2Element { /// /// An element of $\mathbb{F}_2$, the reduction of `x` mod 2 /// (type: `Self`) + /// + /// # Examples + /// + /// ``` + /// # use fp::f2_element::F2Element; + /// # use fp::field_ops::FieldOps; + /// # use crypto_bigint::Uint; + /// let x = F2Element::from_u64(1); + /// let y = F2Element::from_u64(7); + /// assert_eq!(x, y); + /// ``` pub fn from_u64(x: u64) -> Self { Self::new(Uint::from(x & 1)) } - /// Get the `Uint<1>` from an element of $\mathbb{F}_2$ + /// Get the `Uint<1>` from an element of $\mathbb{F}_2$ i.e., /// /// # Arguments /// @@ -67,6 +83,15 @@ impl F2Element { /// /// The integer in $\{ 0, 1 \}$ reducing to `x` mod 2 (type: /// `Uint<1>`) + /// + /// ``` + /// # use fp::f2_element::F2Element; + /// # use fp::field_ops::FieldOps; + /// # use crypto_bigint::Uint; + /// let x = F2Element::from_u64(7); + /// let my_int = x.value(); + /// assert_eq!(my_int, Uint::<1>::from_u64(1)); + /// ``` pub fn value(&self) -> Uint<1> { self.value } @@ -80,7 +105,16 @@ impl F2Element { /// # Returns /// /// The integer in $\{ 0, 1 \}$ reducing to `x` mod 2 (type: - /// `u8`) + /// `Uint<1>`) + /// + /// ``` + /// # use fp::f2_element::F2Element; + /// # use fp::field_ops::FieldOps; + /// # use crypto_bigint::Uint; + /// let x = F2Element::from_u64(45); + /// let my_int = x.as_u8(); + /// assert_eq!(my_int, 1_u8); + /// ``` pub fn as_u8(&self) -> u8 { self.value.to_words()[0] as u8 }