diff --git a/CHANGELOG.md b/CHANGELOG.md index cde56d0..d5df23a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,21 @@ 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, get_lower_32, sqrt_alt,` + `sqrt_ratio}` (moved to `SqrtRatio` trait). + - `FieldExt::{RESCUE_ALPHA, RESCUE_INVALPHA}` + - `FieldExt::from_u64` (use `From for ff::PrimeField` instead). + - `FieldExt::{from_bytes, read, to_bytes, write}` + (use `ff::PrimeField::{from_repr, to_repr}` instead). ## [0.2.1] - 2021-09-17 ### Changed diff --git a/src/arithmetic.rs b/src/arithmetic.rs index bb5fb1d..0fef7f5 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -4,8 +4,6 @@ //! This module is temporary, and the extension traits defined here are expected to be //! upstreamed into the `ff` and `group` crates after some refactoring. -pub use ff::Field; - mod curves; mod fields; diff --git a/src/arithmetic/curves.rs b/src/arithmetic/curves.rs index 0088945..d954893 100644 --- a/src/arithmetic/curves.rs +++ b/src/arithmetic/curves.rs @@ -12,7 +12,6 @@ use super::{FieldExt, Group}; #[cfg(feature = "std")] use std::{ boxed::Box, - cmp, io::{self, Read, Write}, ops::{Add, Mul, Sub}, }; @@ -28,8 +27,6 @@ pub trait CurveExt: PrimeCurve::AffineExt> + group::Group::ScalarExt> + Default - + PartialEq - + cmp::Eq + ConditionallySelectable + ConstantTimeEq + From<::Affine> diff --git a/src/arithmetic/fields.rs b/src/arithmetic/fields.rs index 53554fc..dbea4f8 100644 --- a/src/arithmetic/fields.rs +++ b/src/arithmetic/fields.rs @@ -4,106 +4,91 @@ use core::mem::size_of; use static_assertions::const_assert; -use subtle::{Choice, CtOption}; +use subtle::Choice; + +#[cfg(not(feature = "std"))] +use subtle::CtOption; #[cfg(feature = "std")] use super::Group; #[cfg(feature = "std")] -use std::{ - assert, - boxed::Box, - convert::TryInto, - io::{self, Read, Write}, - marker::PhantomData, - vec::Vec, -}; +use std::{assert, boxed::Box, convert::TryInto, marker::PhantomData, vec::Vec}; 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) + } + + /// Gets the lower 32 bits of this field element when expressed + /// canonically. + fn get_lower_32(&self) -> u32; + + /// 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$ is a non-square. + /// + /// For `pasta_curves`, $G_S$ is currently [`ff::PrimeField::root_of_unity`], a + /// generator of the order $2^S$ subgroup. Users of this crate should not rely on this + /// generator being fixed; it may be changed in future crate versions to simplify the + /// implementation of the SSWU hash-to-curve algorithm. + /// + /// 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; - /// Generator of the $2^S$ multiplicative subgroup - const ROOT_OF_UNITY: Self; - - /// Inverse of `ROOT_OF_UNITY` + /// 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; /// Inverse of $2$ in the field. const TWO_INV: Self; - /// Ideally the smallest prime $\alpha$ such that gcd($p - 1$, $\alpha$) = $1$ - const RESCUE_ALPHA: u64; - - /// $RESCUE_INVALPHA \cdot RESCUE_ALPHA = 1 \mod p - 1$ such that - /// `(a^RESCUE_ALPHA)^RESCUE_INVALPHA = a`. - const RESCUE_INVALPHA: [u64; 4]; - /// 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) } - /// Obtains a field element congruent to the integer `v`. - fn from_u64(v: u64) -> Self; - /// Obtains a field element congruent to the integer `v`. fn from_u128(v: u128) -> Self; - /// Converts this field element to its normalized, little endian byte - /// representation. - fn to_bytes(&self) -> [u8; 32]; - - /// Writes this element in its normalized, little endian form into a buffer. - fn write(&self, writer: &mut W) -> io::Result<()> { - let compressed = self.to_bytes(); - writer.write_all(&compressed[..]) - } - - /// Attempts to obtain a field element from its normalized, little endian - /// byte representation. - fn from_bytes(bytes: &[u8; 32]) -> CtOption; - - /// Reads a normalized, little endian represented field element from a - /// buffer. - fn read(reader: &mut R) -> io::Result { - let mut compressed = [0u8; 32]; - reader.read_exact(&mut compressed[..])?; - Option::from(Self::from_bytes(&compressed)) - .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point encoding in proof")) - } - /// Obtains a field element that is congruent to the provided little endian /// byte representation of an integer. fn from_bytes_wide(bytes: &[u8; 64]) -> Self; @@ -126,16 +111,6 @@ pub trait FieldExt: ff::PrimeField + From + Ord + Group { /// Gets the lower 128 bits of this field element when expressed /// canonically. fn get_lower_128(&self) -> u128; - - /// 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`. @@ -233,7 +208,7 @@ impl SqrtTables { marker: PhantomData, }; - let mut gtab = (0..4).scan(F::ROOT_OF_UNITY, |gi, _| { + let mut gtab = (0..4).scan(F::root_of_unity(), |gi, _| { // gi == ROOT_OF_UNITY^(256^i) let gtab_i: Vec = (0..256) .scan(F::one(), |acc, _| { @@ -331,7 +306,7 @@ impl SqrtTables { let sqdiv = res.square() * div; let is_square = (sqdiv - num).is_zero(); - let is_nonsquare = (sqdiv - F::ROOT_OF_UNITY * num).is_zero(); + let is_nonsquare = (sqdiv - F::root_of_unity() * num).is_zero(); assert!(bool::from( num.is_zero() | div.is_zero() | (is_square ^ is_nonsquare) )); @@ -348,7 +323,7 @@ impl SqrtTables { let sq = res.square(); let is_square = (sq - u).is_zero(); - let is_nonsquare = (sq - F::ROOT_OF_UNITY * u).is_zero(); + let is_nonsquare = (sq - F::root_of_unity() * u).is_zero(); assert!(bool::from(u.is_zero() | (is_square ^ is_nonsquare))); (is_square, res) diff --git a/src/curves.rs b/src/curves.rs index d402de5..8661033 100644 --- a/src/curves.rs +++ b/src/curves.rs @@ -1100,7 +1100,7 @@ impl Ep { 0x4000000000000000, ]); - /// `(F::ROOT_OF_UNITY.invert().unwrap() * z).sqrt().unwrap()` + /// `(F::root_of_unity().invert().unwrap() * z).sqrt().unwrap()` pub const THETA: Fp = Fp::from_raw([ 0xca330bcc09ac318e, 0x51f64fc4dc888857, @@ -1200,7 +1200,7 @@ impl Eq { 0x4000000000000000, ]); - /// `(F::ROOT_OF_UNITY.invert().unwrap() * z).sqrt().unwrap()` + /// `(F::root_of_unity().invert().unwrap() * z).sqrt().unwrap()` pub const THETA: Fq = Fq::from_raw([ 0x632cae9872df1b5d, 0x38578ccadf03ac27, diff --git a/src/fields/fp.rs b/src/fields/fp.rs index 0650751..d27928f 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, @@ -627,7 +627,7 @@ impl PrimeFieldBits for Fp { type ReprBits = ReprBits; fn to_le_bits(&self) -> FieldBits { - let bytes = self.to_bytes(); + let bytes = self.to_repr(); #[cfg(not(target_pointer_width = "64"))] let limbs = [ @@ -670,94 +670,8 @@ lazy_static! { } #[cfg(feature = "std")] -impl FieldExt for Fp { - const MODULUS: &'static str = - "0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001"; - const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; - const ROOT_OF_UNITY_INV: Self = Fp::from_raw([ - 0xf0b87c7db2ce91f6, - 0x84a0a1d8859f066f, - 0xb4ed8e647196dad1, - 0x2cd5282c53116b5c, - ]); - const T_MINUS1_OVER2: [u64; 4] = [ - 0x04a67c8dcc969876, - 0x0000000011234c7e, - 0x0000000000000000, - 0x20000000, - ]; - const DELTA: Self = DELTA; - const TWO_INV: Self = Fp::from_raw([ - 0xcc96987680000001, - 0x11234c7e04a67c8d, - 0x0000000000000000, - 0x2000000000000000, - ]); - const RESCUE_ALPHA: u64 = 5; - const RESCUE_INVALPHA: [u64; 4] = [ - 0xe0f0f3f0cccccccd, - 0x4e9ee0c9a10a60e2, - 0x3333333333333333, - 0x3333333333333333, - ]; - const ZETA: Self = Fp::from_raw([ - 0x1dad5ebdfdfe4ab9, - 0x1d1f8bd237ad3149, - 0x2caad5dc57aab1b0, - 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]) - } - - fn from_u128(v: u128) -> Self { - Fp::from_raw([v as u64, (v >> 64) as u64, 0, 0]) - } - - fn from_bytes(bytes: &[u8; 32]) -> CtOption { - ::from_repr(*bytes) - } - - fn to_bytes(&self) -> [u8; 32] { - ::to_repr(self) - } - - /// Converts a 512-bit little endian integer into - /// a `Fp` by reducing by the modulus. - fn from_bytes_wide(bytes: &[u8; 64]) -> Fp { - Fp::from_u512([ - u64::from_le_bytes(bytes[0..8].try_into().unwrap()), - u64::from_le_bytes(bytes[8..16].try_into().unwrap()), - u64::from_le_bytes(bytes[16..24].try_into().unwrap()), - u64::from_le_bytes(bytes[24..32].try_into().unwrap()), - u64::from_le_bytes(bytes[32..40].try_into().unwrap()), - u64::from_le_bytes(bytes[40..48].try_into().unwrap()), - u64::from_le_bytes(bytes[48..56].try_into().unwrap()), - u64::from_le_bytes(bytes[56..64].try_into().unwrap()), - ]) - } - - fn get_lower_128(&self) -> u128 { - let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - u128::from(tmp.0[0]) | (u128::from(tmp.0[1]) << 64) - } - - fn get_lower_32(&self) -> u32 { - // TODO: don't reduce, just hash the Montgomery form. (Requires rebuilding perfect hash table.) - let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - tmp.0[0] as u32 - } +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()); @@ -789,6 +703,71 @@ impl FieldExt for Fp { let rs = sqr(rr, 3) * r11; rs.square() // rt } + + fn get_lower_32(&self) -> u32 { + // TODO: don't reduce, just hash the Montgomery form. (Requires rebuilding perfect hash table.) + let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + tmp.0[0] as u32 + } + + 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 = + "0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001"; + const ROOT_OF_UNITY_INV: Self = Fp::from_raw([ + 0xf0b87c7db2ce91f6, + 0x84a0a1d8859f066f, + 0xb4ed8e647196dad1, + 0x2cd5282c53116b5c, + ]); + const DELTA: Self = DELTA; + const TWO_INV: Self = Fp::from_raw([ + 0xcc96987680000001, + 0x11234c7e04a67c8d, + 0x0000000000000000, + 0x2000000000000000, + ]); + const ZETA: Self = Fp::from_raw([ + 0x1dad5ebdfdfe4ab9, + 0x1d1f8bd237ad3149, + 0x2caad5dc57aab1b0, + 0x12ccca834acdba71, + ]); + + fn from_u128(v: u128) -> Self { + Fp::from_raw([v as u64, (v >> 64) as u64, 0, 0]) + } + + /// Converts a 512-bit little endian integer into + /// a `Fp` by reducing by the modulus. + fn from_bytes_wide(bytes: &[u8; 64]) -> Fp { + Fp::from_u512([ + u64::from_le_bytes(bytes[0..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + u64::from_le_bytes(bytes[16..24].try_into().unwrap()), + u64::from_le_bytes(bytes[24..32].try_into().unwrap()), + u64::from_le_bytes(bytes[32..40].try_into().unwrap()), + u64::from_le_bytes(bytes[40..48].try_into().unwrap()), + u64::from_le_bytes(bytes[48..56].try_into().unwrap()), + u64::from_le_bytes(bytes[56..64].try_into().unwrap()), + ]) + } + + fn get_lower_128(&self) -> u128 { + let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + u128::from(tmp.0[0]) | (u128::from(tmp.0[1]) << 64) + } } #[cfg(all(test, feature = "std"))] @@ -809,18 +788,6 @@ fn test_inv() { assert_eq!(inv, INV); } -#[cfg(feature = "std")] -#[test] -fn test_rescue() { - // NB: TWO_INV is standing in as a "random" field element - assert_eq!( - Fp::TWO_INV - .pow_vartime(&[Fp::RESCUE_ALPHA, 0, 0, 0]) - .pow_vartime(&Fp::RESCUE_INVALPHA), - Fp::TWO_INV - ); -} - #[cfg(feature = "std")] #[test] fn test_sqrt() { @@ -834,7 +801,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")] @@ -842,9 +809,9 @@ fn test_pow_by_t_minus1_over2() { fn test_sqrt_ratio_and_alt() { // (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field let num = (Fp::TWO_INV).square(); - let div = Fp::from_u64(25); + let div = Fp::from(25); let div_inverse = div.invert().unwrap(); - let expected = Fp::TWO_INV * Fp::from_u64(5).invert().unwrap(); + let expected = Fp::TWO_INV * Fp::from(5).invert().unwrap(); let (is_square, v) = Fp::sqrt_ratio(&num, &div); assert!(bool::from(is_square)); assert!(v == expected || (-v) == expected); @@ -854,8 +821,8 @@ fn test_sqrt_ratio_and_alt() { assert!(v_alt == v); // (false, sqrt(ROOT_OF_UNITY * num/div)), if num and div are nonzero and num/div is a nonsquare in the field - let num = num * Fp::ROOT_OF_UNITY; - let expected = Fp::TWO_INV * Fp::ROOT_OF_UNITY * Fp::from_u64(5).invert().unwrap(); + let num = num * Fp::root_of_unity(); + let expected = Fp::TWO_INV * Fp::root_of_unity() * Fp::from(5).invert().unwrap(); let (is_square, v) = Fp::sqrt_ratio(&num, &div); assert!(!bool::from(is_square)); assert!(v == expected || (-v) == expected); @@ -904,7 +871,7 @@ fn test_zeta() { #[test] fn test_root_of_unity() { assert_eq!( - Fp::ROOT_OF_UNITY.pow_vartime(&[1 << Fp::S, 0, 0, 0]), + Fp::root_of_unity().pow_vartime(&[1 << Fp::S, 0, 0, 0]), Fp::one() ); } @@ -912,7 +879,7 @@ fn test_root_of_unity() { #[cfg(feature = "std")] #[test] fn test_inv_root_of_unity() { - assert_eq!(Fp::ROOT_OF_UNITY_INV, Fp::ROOT_OF_UNITY.invert().unwrap()); + assert_eq!(Fp::ROOT_OF_UNITY_INV, Fp::root_of_unity().invert().unwrap()); } #[cfg(feature = "std")] diff --git a/src/fields/fq.rs b/src/fields/fq.rs index 1a137fc..f7111be 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, @@ -627,7 +627,7 @@ impl PrimeFieldBits for Fq { type ReprBits = ReprBits; fn to_le_bits(&self) -> FieldBits { - let bytes = self.to_bytes(); + let bytes = self.to_repr(); #[cfg(not(target_pointer_width = "64"))] let limbs = [ @@ -670,94 +670,8 @@ lazy_static! { } #[cfg(feature = "std")] -impl FieldExt for Fq { - const MODULUS: &'static str = - "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001"; - const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; - const ROOT_OF_UNITY_INV: Self = Fq::from_raw([ - 0x57eecda0a84b6836, - 0x4ad38b9084b8a80c, - 0xf4c8f353124086c1, - 0x2235e1a7415bf936, - ]); - const T_MINUS1_OVER2: [u64; 4] = [ - 0x04ca546ec6237590, - 0x0000000011234c7e, - 0x0000000000000000, - 0x20000000, - ]; - const DELTA: Self = DELTA; - const TWO_INV: Self = Fq::from_raw([ - 0xc623759080000001, - 0x11234c7e04ca546e, - 0x0000000000000000, - 0x2000000000000000, - ]); - const RESCUE_ALPHA: u64 = 5; - const RESCUE_INVALPHA: [u64; 4] = [ - 0xd69f2280cccccccd, - 0x4e9ee0c9a143ba4a, - 0x3333333333333333, - 0x3333333333333333, - ]; - const ZETA: Self = Fq::from_raw([ - 0x2aa9d2e050aa0e4f, - 0x0fed467d47c033af, - 0x511db4d81cf70f5a, - 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]) - } - - fn from_u128(v: u128) -> Self { - Fq::from_raw([v as u64, (v >> 64) as u64, 0, 0]) - } - - fn from_bytes(bytes: &[u8; 32]) -> CtOption { - ::from_repr(*bytes) - } - - fn to_bytes(&self) -> [u8; 32] { - ::to_repr(self) - } - - /// Converts a 512-bit little endian integer into - /// a `Fq` by reducing by the modulus. - fn from_bytes_wide(bytes: &[u8; 64]) -> Fq { - Fq::from_u512([ - u64::from_le_bytes(bytes[0..8].try_into().unwrap()), - u64::from_le_bytes(bytes[8..16].try_into().unwrap()), - u64::from_le_bytes(bytes[16..24].try_into().unwrap()), - u64::from_le_bytes(bytes[24..32].try_into().unwrap()), - u64::from_le_bytes(bytes[32..40].try_into().unwrap()), - u64::from_le_bytes(bytes[40..48].try_into().unwrap()), - u64::from_le_bytes(bytes[48..56].try_into().unwrap()), - u64::from_le_bytes(bytes[56..64].try_into().unwrap()), - ]) - } - - fn get_lower_128(&self) -> u128 { - let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - u128::from(tmp.0[0]) | (u128::from(tmp.0[1]) << 64) - } - - fn get_lower_32(&self) -> u32 { - // TODO: don't reduce, just hash the Montgomery form. (Requires rebuilding perfect hash table.) - let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - tmp.0[0] as u32 - } +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()); @@ -789,6 +703,71 @@ impl FieldExt for Fq { let ss = sqr(sr, 3) * self; sqr(ss, 4) // st } + + fn get_lower_32(&self) -> u32 { + // TODO: don't reduce, just hash the Montgomery form. (Requires rebuilding perfect hash table.) + let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + tmp.0[0] as u32 + } + + 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 = + "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001"; + const ROOT_OF_UNITY_INV: Self = Fq::from_raw([ + 0x57eecda0a84b6836, + 0x4ad38b9084b8a80c, + 0xf4c8f353124086c1, + 0x2235e1a7415bf936, + ]); + const DELTA: Self = DELTA; + const TWO_INV: Self = Fq::from_raw([ + 0xc623759080000001, + 0x11234c7e04ca546e, + 0x0000000000000000, + 0x2000000000000000, + ]); + const ZETA: Self = Fq::from_raw([ + 0x2aa9d2e050aa0e4f, + 0x0fed467d47c033af, + 0x511db4d81cf70f5a, + 0x06819a58283e528e, + ]); + + fn from_u128(v: u128) -> Self { + Fq::from_raw([v as u64, (v >> 64) as u64, 0, 0]) + } + + /// Converts a 512-bit little endian integer into + /// a `Fq` by reducing by the modulus. + fn from_bytes_wide(bytes: &[u8; 64]) -> Fq { + Fq::from_u512([ + u64::from_le_bytes(bytes[0..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + u64::from_le_bytes(bytes[16..24].try_into().unwrap()), + u64::from_le_bytes(bytes[24..32].try_into().unwrap()), + u64::from_le_bytes(bytes[32..40].try_into().unwrap()), + u64::from_le_bytes(bytes[40..48].try_into().unwrap()), + u64::from_le_bytes(bytes[48..56].try_into().unwrap()), + u64::from_le_bytes(bytes[56..64].try_into().unwrap()), + ]) + } + + fn get_lower_128(&self) -> u128 { + let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + u128::from(tmp.0[0]) | (u128::from(tmp.0[1]) << 64) + } } #[cfg(all(test, feature = "std"))] @@ -809,18 +788,6 @@ fn test_inv() { assert_eq!(inv, INV); } -#[cfg(feature = "std")] -#[test] -fn test_rescue() { - // NB: TWO_INV is standing in as a "random" field element - assert_eq!( - Fq::TWO_INV - .pow_vartime(&[Fq::RESCUE_ALPHA, 0, 0, 0]) - .pow_vartime(&Fq::RESCUE_INVALPHA), - Fq::TWO_INV - ); -} - #[cfg(feature = "std")] #[test] fn test_sqrt() { @@ -834,7 +801,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")] @@ -842,9 +809,9 @@ fn test_pow_by_t_minus1_over2() { fn test_sqrt_ratio_and_alt() { // (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field let num = (Fq::TWO_INV).square(); - let div = Fq::from_u64(25); + let div = Fq::from(25); let div_inverse = div.invert().unwrap(); - let expected = Fq::TWO_INV * Fq::from_u64(5).invert().unwrap(); + let expected = Fq::TWO_INV * Fq::from(5).invert().unwrap(); let (is_square, v) = Fq::sqrt_ratio(&num, &div); assert!(bool::from(is_square)); assert!(v == expected || (-v) == expected); @@ -854,8 +821,8 @@ fn test_sqrt_ratio_and_alt() { assert!(v_alt == v); // (false, sqrt(ROOT_OF_UNITY * num/div)), if num and div are nonzero and num/div is a nonsquare in the field - let num = num * Fq::ROOT_OF_UNITY; - let expected = Fq::TWO_INV * Fq::ROOT_OF_UNITY * Fq::from_u64(5).invert().unwrap(); + let num = num * Fq::root_of_unity(); + let expected = Fq::TWO_INV * Fq::root_of_unity() * Fq::from(5).invert().unwrap(); let (is_square, v) = Fq::sqrt_ratio(&num, &div); assert!(!bool::from(is_square)); assert!(v == expected || (-v) == expected); @@ -903,7 +870,7 @@ fn test_zeta() { #[test] fn test_root_of_unity() { assert_eq!( - Fq::ROOT_OF_UNITY.pow_vartime(&[1 << Fq::S, 0, 0, 0]), + Fq::root_of_unity().pow_vartime(&[1 << Fq::S, 0, 0, 0]), Fq::one() ); } @@ -911,7 +878,7 @@ fn test_root_of_unity() { #[cfg(feature = "std")] #[test] fn test_inv_root_of_unity() { - assert_eq!(Fq::ROOT_OF_UNITY_INV, Fq::ROOT_OF_UNITY.invert().unwrap()); + assert_eq!(Fq::ROOT_OF_UNITY_INV, Fq::root_of_unity().invert().unwrap()); } #[cfg(feature = "std")] diff --git a/src/hashtocurve.rs b/src/hashtocurve.rs index e09f3e7..3661265 100644 --- a/src/hashtocurve.rs +++ b/src/hashtocurve.rs @@ -171,11 +171,7 @@ pub fn map_to_curve_simple_swu, I: CurveExt