From f577300d28db7cce0d604a94c43db6776f04712d Mon Sep 17 00:00:00 2001 From: Sam Frengley Date: Fri, 19 Jun 2026 16:49:06 +0200 Subject: [PATCH] Added capabilities of compile time TS constants --- ec/tests/point_tests.rs | 9 +-- ec/tests/point_weierstrass_tests.rs | 9 +-- fp/examples/fp_demo.rs | 35 +--------- fp/src/_doctest_support.rs | 12 ---- fp/src/_ts_consts.rs | 104 ++++++++++++++++++++++++++++ fp/src/fp_ext.rs | 53 ++++++-------- fp/src/lib.rs | 3 + fp/tests/fp_ext_tests.rs | 21 +----- 8 files changed, 133 insertions(+), 113 deletions(-) create mode 100644 fp/src/_ts_consts.rs diff --git a/ec/tests/point_tests.rs b/ec/tests/point_tests.rs index cddfc54..8d5ad8e 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::{Uint, const_prime_monty_params}; +use crypto_bigint::{const_prime_monty_params, Uint}; use fp::field_ops::FieldOps; use fp::fp_element::FpElement; @@ -135,16 +135,9 @@ impl IrreduciblePoly for QuadPoly { } impl TonelliShanksConstants for TSQuad { - // Still only need 1 limb for 19^2 - const ORDER: Uint<1> = Uint::<1>::from_u64(360); - const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - 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)] } - const S: u64 = 3; - const T: Uint<1> = Uint::<1>::from_u64(45); } type F19_2 = FpExt; diff --git a/ec/tests/point_weierstrass_tests.rs b/ec/tests/point_weierstrass_tests.rs index cddfc54..8d5ad8e 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::{Uint, const_prime_monty_params}; +use crypto_bigint::{const_prime_monty_params, Uint}; use fp::field_ops::FieldOps; use fp::fp_element::FpElement; @@ -135,16 +135,9 @@ impl IrreduciblePoly for QuadPoly { } impl TonelliShanksConstants for TSQuad { - // Still only need 1 limb for 19^2 - const ORDER: Uint<1> = Uint::<1>::from_u64(360); - const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - 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)] } - const S: u64 = 3; - const T: Uint<1> = Uint::<1>::from_u64(45); } type F19_2 = FpExt; diff --git a/fp/examples/fp_demo.rs b/fp/examples/fp_demo.rs index 31c91d6..011c746 100644 --- a/fp/examples/fp_demo.rs +++ b/fp/examples/fp_demo.rs @@ -2,7 +2,7 @@ //! //! Run with: cargo run --bin demo //! -use crypto_bigint::{Uint, const_prime_monty_params}; +use crypto_bigint::{const_prime_monty_params, Uint}; // Pull in the library types use fp::f2_element::F2Element; @@ -38,15 +38,10 @@ impl IrreduciblePoly for Poly19Quad { // Tonelli-Shanks constants for F_{19^2} // |F_{19^2}*| = 19^2 - 1 = 360 = 2^3 * 45 +// so we have to supply 2^3 root of unity struct TS19Quad; impl TonelliShanksConstants for TS19Quad { - 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)] } @@ -80,13 +75,6 @@ struct TS97283Quad; impl TonelliShanksConstants for TS97283Quad { // p^2 - 1 = 9463982088 = 2^3 * 1182997761 - const ORDER: Uint<1> = Uint::<1>::from_u64(9_463_982_088); - const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(4_731_991_044); - const S: u64 = 3; - const T: Uint<1> = Uint::<1>::from_u64(1_182_997_761); - const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(591_498_880); - const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); - fn root_of_unity() -> [FpElement; 2] { // An element of exact order 8 in F_{p^2} [Fp97283::from_u64(96_901), Fp97283::from_u64(382)] @@ -116,13 +104,6 @@ struct TS97283Cubic; impl TonelliShanksConstants for TS97283Cubic { // p^3 - 1 = 920684569564186 = 2^1 * 460342284782093 - const ORDER: Uint<1> = Uint::<1>::from_u64(920_684_569_564_186); - const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(460_342_284_782_093); - const S: u64 = 1; - const T: Uint<1> = Uint::<1>::from_u64(460_342_284_782_093); - const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(230_171_142_391_046); - const TWOSM1: Uint<1> = Uint::<1>::from_u64(1); - fn root_of_unity() -> [FpElement; 3] { // Same convention as your F19^3 example when S = 1 [ @@ -158,18 +139,6 @@ struct TS97283Quartic; impl TonelliShanksConstants for TS97283Quartic { // p^4 - 1 = 89566956980912803920 = 2^4 * 5597934811307050245 - const ORDER: Uint<2> = Uint::<2>::from_words([0xdafdc0d3fc005050, 0x0000000000000004]); - - 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 PROJENATOR_EXP: Uint<2> = Uint::<2>::from_words([0x26d7ee069fe00282, 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} [ diff --git a/fp/src/_doctest_support.rs b/fp/src/_doctest_support.rs index 2b8be6b..bfb39ec 100644 --- a/fp/src/_doctest_support.rs +++ b/fp/src/_doctest_support.rs @@ -19,12 +19,6 @@ pub mod _doctest_fp_ext { } 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)] } @@ -52,12 +46,6 @@ pub mod _doctest_field_ops { } 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)] } diff --git a/fp/src/_ts_consts.rs b/fp/src/_ts_consts.rs new file mode 100644 index 0000000..e08de71 --- /dev/null +++ b/fp/src/_ts_consts.rs @@ -0,0 +1,104 @@ +//! Helper `const` functions for computing Tonelli--Shanks constants + +use crypto_bigint::{ + modular::{ConstMontyForm, ConstPrimeMontyParams}, + Uint, +}; + +/// Puts a `Uint` into a `Uint` where $B > A$. +/// +/// # Arguments +/// +/// * `x` - An integer. (Type: `Uint`) +/// +/// # Returns +/// +/// Coerces `x` into a `Uint`. (Type: `Uint`) +const fn put_into_bigger_unit(x: Uint) -> Uint { + let input = x.to_words(); + let mut output = [0u64; B]; + let mut i = 0; + while i < A && i < B { + output[i] = input[i]; + i += 1; + } + Uint::::from_words(output) +} + +/// Gets the field order $q = p^M$ +/// +/// # Returns +/// +/// The order of the field. (Type: `Uint`) +const fn field_order() -> Uint +where + MOD: ConstPrimeMontyParams, +{ + let minus_one = ConstMontyForm::::ZERO.sub(&ConstMontyForm::::ONE); + let p_minus_1: Uint = minus_one.retrieve(); + let p = p_minus_1.wrapping_add(&Uint::::ONE); + let p = put_into_bigger_unit::(p); + + let mut q = Uint::::ONE; + let mut i = 0; + while i < M { + q = q.wrapping_mul(&p); + i += 1; + } + q +} + +/// The size $p^M - 1$ of the finite field's multiplicative group. +/// +/// # Returns +/// +/// The number $p^M - 1$ as a `Uint`. (Type: `Uint`). +pub const fn multiplicative_group_order( +) -> Uint +where + MOD: ConstPrimeMontyParams, +{ + field_order::().wrapping_sub(&Uint::::ONE) +} + +/// Compute the 2-adic valuation of a `UInt` +/// +/// # Arguments +/// +/// * `x` - Integer. (Type: `&Uint`) +/// +/// # Returns +/// +/// The 2-adic valuation of $x$. That is the integer $v$ such that $x +/// = y 2^v$ with $y$ odd. (Type: `u64`) +pub const fn two_adic_valuation(x: &Uint) -> u64 { + let words = x.to_words(); + let mut i = 0; + while i < N { + let w = words[i]; + if w != 0 { + return (i as u64) * 64 + w.trailing_zeros() as u64; + } + i += 1; + } + 0 +} + +/// Computes $2^{\texttt{shift}}$ +/// +/// # Arguments +/// +/// * `shift` - An integer (Type: `u64`). +/// +/// # Returns +/// +/// The value $2^{\texttt{shift}}$ (Type: `Uint`). +pub const fn one_shl(shift: u64) -> Uint { + let mut words = [0u64; N]; + let word = (shift / 64) as usize; + let bit = shift % 64; + if word < N { + words[word] = 1u64 << bit; + } + Uint::::from_words(words) +} diff --git a/fp/src/fp_ext.rs b/fp/src/fp_ext.rs index 0afb765..53258aa 100644 --- a/fp/src/fp_ext.rs +++ b/fp/src/fp_ext.rs @@ -36,18 +36,8 @@ //! /* 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 +//! // 2^S root of unity where +//! // (p^2 - 1) = 2^S * (odd) //! fn root_of_unity() -> [FpElement; 2] { //! [Fp19::from_u64(3), Fp19::from_u64(3)] //! } @@ -96,6 +86,7 @@ use crate::fp_element::FpElement; use crypto_bigint::{modular::ConstPrimeMontyParams, Uint}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; +use crate::_ts_consts::{multiplicative_group_order, one_shl, two_adic_valuation}; // =========================================================================== // IrreduciblePoly — one thing callers need to implement for a new field // =========================================================================== @@ -183,6 +174,9 @@ implement for a new field /// listed below all are derived by writing $p^M - 1 = 2^S T$ for some /// odd $T$. /// +/// The implementation is such that everything other than a $2^S$-root +/// of unity will be computed at compile time automatically. +/// /// # Example: $\mathbb{F}_{19^2}$ /// Note that we have a factorisation $19^2 - 1 = 2^3 \cdot 45$ /// ``` @@ -199,13 +193,8 @@ implement for a new field /// 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 @@ -213,13 +202,20 @@ implement for a new field /// [Fp19::from_u64(3), Fp19::from_u64(3)] /// } /// } +/// +/// /* Alternatively we can do some of the work for you */ +/// struct TSQuadAuto; +/// impl TonelliShanksConstants for TSQuadAuto { +/// // 2^S root of unity +/// fn root_of_unity() -> [FpElement; 2] { +/// [Fp19::from_u64(3), Fp19::from_u64(3)] +/// } +/// } /// ``` /// /// # Todo /// /// - [ ] Implement `root_of_unity` as a `const`. -/// - [ ] Compute these constants at compile time to bury this from -/// the end user. pub trait TonelliShanksConstants: 'static where @@ -227,31 +223,30 @@ where { /// The order of the multiplicative $\mathbb{F}\_{p^M}^*$, that is /// the integer $p^M - 1$. - const ORDER: Uint; + const ORDER: Uint = multiplicative_group_order::(); /// Half of `ORDER`, that is the integer $(p^M - 1) / 2$. - const HALF_ORDER: Uint; + const HALF_ORDER: Uint = Self::ORDER.wrapping_shr(1); /// 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; + const S: u64 = two_adic_valuation(&Self::ORDER); /// 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; + const T: Uint = Self::ORDER.wrapping_shr(Self::S as u32); /// The integer $2^{S - 1}$. - const TWOSM1: Uint; + const TWOSM1: Uint = one_shl::(Self::S - 1); /// The integer $(T - 1) / 2$ which is the exponent of the /// "projenator" in the Tonelli--Shanks algorithm. - const PROJENATOR_EXP: Uint; + const PROJENATOR_EXP: Uint = Self::T.wrapping_sub(&Uint::::ONE).wrapping_shr(1); /// 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]; } @@ -293,12 +288,6 @@ where /// /* 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); -/// 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)] /// } diff --git a/fp/src/lib.rs b/fp/src/lib.rs index 045d797..58b7acd 100644 --- a/fp/src/lib.rs +++ b/fp/src/lib.rs @@ -12,6 +12,9 @@ //! └── lib.rs //! ``` +/// Helper functions for Tonelli--Shanks +pub mod _ts_consts; + /// Binary base field $\mathbb{F}_2$ and its arithmetic. pub mod f2_element; /// Binary extension fields $\mathbb{F}\_{2^m}$ built from irreducible polynomials. diff --git a/fp/tests/fp_ext_tests.rs b/fp/tests/fp_ext_tests.rs index 697430b..995c7ef 100644 --- a/fp/tests/fp_ext_tests.rs +++ b/fp/tests/fp_ext_tests.rs @@ -1,4 +1,4 @@ -use crypto_bigint::{Uint, const_prime_monty_params}; +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}; // ← was missing IrreduciblePoly @@ -19,12 +19,6 @@ impl IrreduciblePoly for QuadPoly { impl TonelliShanksConstants for TSQuad { // Still only need 1 limb for $19^2$ - 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)] } @@ -62,13 +56,6 @@ impl IrreduciblePoly for Poly97283Cubic { impl TonelliShanksConstants for TS97283Cubic { // p^3 - 1 = 920684569564186 = 2 * 460342284782093 - const ORDER: Uint<1> = Uint::<1>::from_u64(920_684_569_564_186); - const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(460_342_284_782_093); - const S: u64 = 1; - const T: Uint<1> = Uint::<1>::from_u64(460_342_284_782_093); - const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(230_171_142_391_046); - const TWOSM1: Uint<1> = Uint::<1>::from_u64(1); - fn root_of_unity() -> [FpElement; 3] { [Fp97283::one(), Fp97283::zero(), Fp97283::zero()] } @@ -511,12 +498,6 @@ impl IrreduciblePoly for CubicPoly { impl TonelliShanksConstants for TSCubic { // Still only need 1 limb for 19^3 - const ORDER: Uint<1> = Uint::<1>::from_u64(6858); - const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(3429); - const S: u64 = 1; - const T: Uint<1> = Uint::<1>::from_u64(3429); - const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(1714); - const TWOSM1: Uint<1> = Uint::<1>::from_u64(1); fn root_of_unity() -> [FpElement; 3] { [fp(1), fp(0), fp(0)] }