From 1b2f581ac10b80b39c565eb74eab45678c840fc2 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 20 Sep 2021 20:02:57 +0100 Subject: [PATCH] Move square-root operations from `FieldExt` into a separate trait --- CHANGELOG.md | 7 +++ src/arithmetic/fields.rs | 65 +++++++++++++---------- src/fields/fp.rs | 111 +++++++++++++++++++-------------------- src/fields/fq.rs | 111 +++++++++++++++++++-------------------- 4 files changed, 155 insertions(+), 139 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 093430f..f36eca8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,17 @@ and this project adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- `pasta_curves::arithmetic::SqrtRatio` trait, extending `ff::PrimeField` with + square roots of ratios. This trait is likely to be moved into the `ff` crate + in a future release (once we're satisfied with it). + ### Removed - `pasta_curves::arithmetic`: - `Field` re-export (`pasta_curves::group::ff::Field` is equivalent). - `FieldExt::ROOT_OF_UNITY` (use `ff::PrimeField::root_of_unity` instead). + - `FieldExt::{T_MINUS1_OVER2, pow_by_t_minus1_over2, sqrt_alt, sqrt_ratio}` + (moved to `SqrtRatio` trait). ## [0.2.1] - 2021-09-17 ### Changed diff --git a/src/arithmetic/fields.rs b/src/arithmetic/fields.rs index cb1befe..e2488fa 100644 --- a/src/arithmetic/fields.rs +++ b/src/arithmetic/fields.rs @@ -21,19 +21,53 @@ use std::{ const_assert!(size_of::() >= 4); +/// A trait that exposes additional operations related to calculating square roots of +/// prime-order finite fields. +#[cfg(feature = "std")] +pub trait SqrtRatio: ff::PrimeField { + /// The value $(T-1)/2$ such that $2^S \cdot T = p - 1$ with $T$ odd. + const T_MINUS1_OVER2: [u64; 4]; + + /// Raise this field element to the power [`Self::T_MINUS1_OVER2`]. + /// + /// Field implementations may override this to use an efficient addition chain. + fn pow_by_t_minus1_over2(&self) -> Self { + ff::Field::pow_vartime(&self, &Self::T_MINUS1_OVER2) + } + + /// Computes: + /// + /// - $(\textsf{true}, \sqrt{\textsf{num}/\textsf{div}})$, if $\textsf{num}$ and + /// $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is a square in the + /// field; + /// - $(\textsf{true}, 0)$, if $\textsf{num}$ is zero; + /// - $(\textsf{false}, 0)$, if $\textsf{num}$ is nonzero and $\textsf{div}$ is zero; + /// - $(\textsf{false}, \sqrt{G_S \cdot \textsf{num}/\textsf{div}})$, if + /// $\textsf{num}$ and $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is + /// a nonsquare in the field; + /// + /// where $G_S$ ([`ff::PrimeField::root_of_unity`]) is a generator of the order $2^S$ + /// subgroup (and therefore a nonsquare). + /// + /// The choice of root from sqrt is unspecified. + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self); + + /// Equivalent to `Self::sqrt_ratio(self, one())`. + fn sqrt_alt(&self) -> (Choice, Self) { + Self::sqrt_ratio(self, &Self::one()) + } +} + /// This trait is a common interface for dealing with elements of a finite /// field. #[cfg(feature = "std")] -pub trait FieldExt: ff::PrimeField + From + Ord + Group { +pub trait FieldExt: SqrtRatio + From + Ord + Group { /// Modulus of the field written as a string for display purposes const MODULUS: &'static str; /// Inverse of `PrimeField::root_of_unity()` const ROOT_OF_UNITY_INV: Self; - /// The value $(T-1)/2$ such that $2^S \cdot T = p - 1$ with $T$ odd. - const T_MINUS1_OVER2: [u64; 4]; - /// Generator of the $t-order$ multiplicative subgroup const DELTA: Self; @@ -50,23 +84,6 @@ pub trait FieldExt: ff::PrimeField + From + Ord + Group { /// Element of multiplicative order $3$. const ZETA: Self; - /// Computes: - /// - /// * (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field; - /// * (true, 0), if num is zero; - /// * (false, 0), if num is nonzero and div is zero; - /// * (false, sqrt(ROOT_OF_UNITY * num/div)), if num and div are nonzero and num/div is a nonsquare in the field; - /// - /// where ROOT_OF_UNITY is a generator of the order 2^n subgroup (and therefore a nonsquare). - /// - /// The choice of root from sqrt is unspecified. - fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self); - - /// Equivalent to sqrt_ratio(self, one()). - fn sqrt_alt(&self) -> (Choice, Self) { - Self::sqrt_ratio(self, &Self::one()) - } - /// This computes a random element of the field using system randomness. fn rand() -> Self { Self::random(rand::rngs::OsRng) @@ -127,12 +144,6 @@ pub trait FieldExt: ff::PrimeField + From + Ord + Group { /// Gets the lower 32 bits of this field element when expressed /// canonically. fn get_lower_32(&self) -> u32; - - /// Raise this field element to the power T_MINUS1_OVER2. - /// Field implementations may override this to use an efficient addition chain. - fn pow_by_t_minus1_over2(&self) -> Self { - ff::Field::pow_vartime(&self, &Self::T_MINUS1_OVER2) - } } /// Tonelli–Shanks' square-root algorithm for `p mod 16 = 1`. diff --git a/src/fields/fp.rs b/src/fields/fp.rs index 6097f94..8c1baf3 100644 --- a/src/fields/fp.rs +++ b/src/fields/fp.rs @@ -15,7 +15,7 @@ use ff::{FieldBits, PrimeFieldBits}; use crate::arithmetic::{adc, mac, sbb}; #[cfg(feature = "std")] -use crate::arithmetic::{FieldExt, Group, SqrtTables}; +use crate::arithmetic::{FieldExt, Group, SqrtRatio, SqrtTables}; /// This represents an element of $\mathbb{F}_p$ where /// @@ -232,6 +232,14 @@ const DELTA: Fp = Fp::from_raw([ 0x0a757d0f0006ab6c, ]); +/// `(t - 1) // 2` where t * 2^s + 1 = p with t odd. +const T_MINUS1_OVER2: [u64; 4] = [ + 0x04a6_7c8d_cc96_9876, + 0x0000_0000_1123_4c7e, + 0x0000_0000_0000_0000, + 0x0000_0000_2000_0000, +]; + impl Default for Fp { #[inline] fn default() -> Self { @@ -513,15 +521,7 @@ impl ff::Field for Fp { } #[cfg(not(feature = "std"))] - crate::arithmetic::sqrt_tonelli_shanks( - self, - &[ - 0x04a6_7c8d_cc96_9876, - 0x0000_0000_1123_4c7e, - 0x0000_0000_0000_0000, - 0x0000_0000_2000_0000, - ], - ) + crate::arithmetic::sqrt_tonelli_shanks(self, &T_MINUS1_OVER2) } /// Computes the multiplicative inverse of this element, @@ -669,6 +669,50 @@ lazy_static! { static ref FP_TABLES: SqrtTables = SqrtTables::new(0x11BE, 1098); } +#[cfg(feature = "std")] +impl SqrtRatio for Fp { + const T_MINUS1_OVER2: [u64; 4] = T_MINUS1_OVER2; + + fn pow_by_t_minus1_over2(&self) -> Self { + let sqr = |x: Fp, i: u32| (0..i).fold(x, |x, _| x.square()); + + let r10 = self.square(); + let r11 = r10 * self; + let r110 = r11.square(); + let r111 = r110 * self; + let r1001 = r111 * r10; + let r1101 = r111 * r110; + let ra = sqr(*self, 129) * self; + let rb = sqr(ra, 7) * r1001; + let rc = sqr(rb, 7) * r1101; + let rd = sqr(rc, 4) * r11; + let re = sqr(rd, 6) * r111; + let rf = sqr(re, 3) * r111; + let rg = sqr(rf, 10) * r1001; + let rh = sqr(rg, 5) * r1001; + let ri = sqr(rh, 4) * r1001; + let rj = sqr(ri, 3) * r111; + let rk = sqr(rj, 4) * r1001; + let rl = sqr(rk, 5) * r11; + let rm = sqr(rl, 4) * r111; + let rn = sqr(rm, 4) * r11; + let ro = sqr(rn, 6) * r1001; + let rp = sqr(ro, 5) * r1101; + let rq = sqr(rp, 4) * r11; + let rr = sqr(rq, 7) * r111; + let rs = sqr(rr, 3) * r11; + rs.square() // rt + } + + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + FP_TABLES.sqrt_ratio(num, div) + } + + fn sqrt_alt(&self) -> (Choice, Self) { + FP_TABLES.sqrt_alt(self) + } +} + #[cfg(feature = "std")] impl FieldExt for Fp { const MODULUS: &'static str = @@ -679,12 +723,6 @@ impl FieldExt for Fp { 0xb4ed8e647196dad1, 0x2cd5282c53116b5c, ]); - const T_MINUS1_OVER2: [u64; 4] = [ - 0x04a67c8dcc969876, - 0x0000000011234c7e, - 0x0000000000000000, - 0x20000000, - ]; const DELTA: Self = DELTA; const TWO_INV: Self = Fp::from_raw([ 0xcc96987680000001, @@ -706,14 +744,6 @@ impl FieldExt for Fp { 0x12ccca834acdba71, ]); - fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { - FP_TABLES.sqrt_ratio(num, div) - } - - fn sqrt_alt(&self) -> (Choice, Self) { - FP_TABLES.sqrt_alt(self) - } - fn from_u64(v: u64) -> Self { Fp::from_raw([v as u64, 0, 0, 0]) } @@ -757,37 +787,6 @@ impl FieldExt for Fp { tmp.0[0] as u32 } - - fn pow_by_t_minus1_over2(&self) -> Self { - let sqr = |x: Fp, i: u32| (0..i).fold(x, |x, _| x.square()); - - let r10 = self.square(); - let r11 = r10 * self; - let r110 = r11.square(); - let r111 = r110 * self; - let r1001 = r111 * r10; - let r1101 = r111 * r110; - let ra = sqr(*self, 129) * self; - let rb = sqr(ra, 7) * r1001; - let rc = sqr(rb, 7) * r1101; - let rd = sqr(rc, 4) * r11; - let re = sqr(rd, 6) * r111; - let rf = sqr(re, 3) * r111; - let rg = sqr(rf, 10) * r1001; - let rh = sqr(rg, 5) * r1001; - let ri = sqr(rh, 4) * r1001; - let rj = sqr(ri, 3) * r111; - let rk = sqr(rj, 4) * r1001; - let rl = sqr(rk, 5) * r11; - let rm = sqr(rl, 4) * r111; - let rn = sqr(rm, 4) * r11; - let ro = sqr(rn, 6) * r1001; - let rp = sqr(ro, 5) * r1101; - let rq = sqr(rp, 4) * r11; - let rr = sqr(rq, 7) * r111; - let rs = sqr(rr, 3) * r11; - rs.square() // rt - } } #[cfg(all(test, feature = "std"))] @@ -833,7 +832,7 @@ fn test_sqrt() { fn test_pow_by_t_minus1_over2() { // NB: TWO_INV is standing in as a "random" field element let v = (Fp::TWO_INV).pow_by_t_minus1_over2(); - assert!(v == ff::Field::pow_vartime(&Fp::TWO_INV, &Fp::T_MINUS1_OVER2)); + assert!(v == ff::Field::pow_vartime(&Fp::TWO_INV, &T_MINUS1_OVER2)); } #[cfg(feature = "std")] diff --git a/src/fields/fq.rs b/src/fields/fq.rs index d72ac36..7ea2498 100644 --- a/src/fields/fq.rs +++ b/src/fields/fq.rs @@ -15,7 +15,7 @@ use ff::{FieldBits, PrimeFieldBits}; use crate::arithmetic::{adc, mac, sbb}; #[cfg(feature = "std")] -use crate::arithmetic::{FieldExt, Group, SqrtTables}; +use crate::arithmetic::{FieldExt, Group, SqrtRatio, SqrtTables}; /// This represents an element of $\mathbb{F}_q$ where /// @@ -232,6 +232,14 @@ const DELTA: Fq = Fq::from_raw([ 0x2237d54423724166, ]); +/// `(t - 1) // 2` where t * 2^s + 1 = p with t odd. +const T_MINUS1_OVER2: [u64; 4] = [ + 0x04ca_546e_c623_7590, + 0x0000_0000_1123_4c7e, + 0x0000_0000_0000_0000, + 0x0000_0000_2000_0000, +]; + impl Default for Fq { #[inline] fn default() -> Self { @@ -513,15 +521,7 @@ impl ff::Field for Fq { } #[cfg(not(feature = "std"))] - crate::arithmetic::sqrt_tonelli_shanks( - self, - &[ - 0x04ca_546e_c623_7590, - 0x0000_0000_1123_4c7e, - 0x0000_0000_0000_0000, - 0x0000_0000_2000_0000, - ], - ) + crate::arithmetic::sqrt_tonelli_shanks(self, &T_MINUS1_OVER2) } /// Computes the multiplicative inverse of this element, @@ -669,6 +669,50 @@ lazy_static! { static ref FQ_TABLES: SqrtTables = SqrtTables::new(0x116A9E, 1206); } +#[cfg(feature = "std")] +impl SqrtRatio for Fq { + const T_MINUS1_OVER2: [u64; 4] = T_MINUS1_OVER2; + + fn pow_by_t_minus1_over2(&self) -> Self { + let sqr = |x: Fq, i: u32| (0..i).fold(x, |x, _| x.square()); + + let s10 = self.square(); + let s11 = s10 * self; + let s111 = s11.square() * self; + let s1001 = s111 * s10; + let s1011 = s1001 * s10; + let s1101 = s1011 * s10; + let sa = sqr(*self, 129) * self; + let sb = sqr(sa, 7) * s1001; + let sc = sqr(sb, 7) * s1101; + let sd = sqr(sc, 4) * s11; + let se = sqr(sd, 6) * s111; + let sf = sqr(se, 3) * s111; + let sg = sqr(sf, 10) * s1001; + let sh = sqr(sg, 4) * s1001; + let si = sqr(sh, 5) * s1001; + let sj = sqr(si, 5) * s1001; + let sk = sqr(sj, 3) * s1001; + let sl = sqr(sk, 4) * s1011; + let sm = sqr(sl, 4) * s1011; + let sn = sqr(sm, 5) * s11; + let so = sqr(sn, 4) * self; + let sp = sqr(so, 5) * s11; + let sq = sqr(sp, 4) * s111; + let sr = sqr(sq, 5) * s1011; + let ss = sqr(sr, 3) * self; + sqr(ss, 4) // st + } + + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + FQ_TABLES.sqrt_ratio(num, div) + } + + fn sqrt_alt(&self) -> (Choice, Self) { + FQ_TABLES.sqrt_alt(self) + } +} + #[cfg(feature = "std")] impl FieldExt for Fq { const MODULUS: &'static str = @@ -679,12 +723,6 @@ impl FieldExt for Fq { 0xf4c8f353124086c1, 0x2235e1a7415bf936, ]); - const T_MINUS1_OVER2: [u64; 4] = [ - 0x04ca546ec6237590, - 0x0000000011234c7e, - 0x0000000000000000, - 0x20000000, - ]; const DELTA: Self = DELTA; const TWO_INV: Self = Fq::from_raw([ 0xc623759080000001, @@ -706,14 +744,6 @@ impl FieldExt for Fq { 0x06819a58283e528e, ]); - fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { - FQ_TABLES.sqrt_ratio(num, div) - } - - fn sqrt_alt(&self) -> (Choice, Self) { - FQ_TABLES.sqrt_alt(self) - } - fn from_u64(v: u64) -> Self { Fq::from_raw([v as u64, 0, 0, 0]) } @@ -757,37 +787,6 @@ impl FieldExt for Fq { tmp.0[0] as u32 } - - fn pow_by_t_minus1_over2(&self) -> Self { - let sqr = |x: Fq, i: u32| (0..i).fold(x, |x, _| x.square()); - - let s10 = self.square(); - let s11 = s10 * self; - let s111 = s11.square() * self; - let s1001 = s111 * s10; - let s1011 = s1001 * s10; - let s1101 = s1011 * s10; - let sa = sqr(*self, 129) * self; - let sb = sqr(sa, 7) * s1001; - let sc = sqr(sb, 7) * s1101; - let sd = sqr(sc, 4) * s11; - let se = sqr(sd, 6) * s111; - let sf = sqr(se, 3) * s111; - let sg = sqr(sf, 10) * s1001; - let sh = sqr(sg, 4) * s1001; - let si = sqr(sh, 5) * s1001; - let sj = sqr(si, 5) * s1001; - let sk = sqr(sj, 3) * s1001; - let sl = sqr(sk, 4) * s1011; - let sm = sqr(sl, 4) * s1011; - let sn = sqr(sm, 5) * s11; - let so = sqr(sn, 4) * self; - let sp = sqr(so, 5) * s11; - let sq = sqr(sp, 4) * s111; - let sr = sqr(sq, 5) * s1011; - let ss = sqr(sr, 3) * self; - sqr(ss, 4) // st - } } #[cfg(all(test, feature = "std"))] @@ -833,7 +832,7 @@ fn test_sqrt() { fn test_pow_by_t_minus1_over2() { // NB: TWO_INV is standing in as a "random" field element let v = (Fq::TWO_INV).pow_by_t_minus1_over2(); - assert!(v == ff::Field::pow_vartime(&Fq::TWO_INV, &Fq::T_MINUS1_OVER2)); + assert!(v == ff::Field::pow_vartime(&Fq::TWO_INV, &T_MINUS1_OVER2)); } #[cfg(feature = "std")]