diff --git a/CHANGELOG.md b/CHANGELOG.md index d5df23a..768dde2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to Rust's notion of - `FieldExt::from_u64` (use `From for ff::PrimeField` instead). - `FieldExt::{from_bytes, read, to_bytes, write}` (use `ff::PrimeField::{from_repr, to_repr}` instead). + - `FieldExt::rand` (use `ff::Field::random` instead). ## [0.2.1] - 2021-09-17 ### Changed diff --git a/Cargo.toml b/Cargo.toml index 50b86c2..bbbe635 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,8 @@ subtle = { version = "2.3", default-features = false } lazy_static = { version = "1.4.0", optional = true } [features] -default = ["bits", "std"] +default = ["bits", "sqrt-table", "std"] +alloc = ["group/alloc"] bits = ["ff/bits"] -std = ["group/alloc", "lazy_static", "rand/getrandom"] +sqrt-table = ["alloc", "lazy_static"] +std = ["alloc"] diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 0fef7f5..e96a074 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -10,13 +10,11 @@ mod fields; pub(crate) use fields::*; pub use curves::*; -#[cfg(feature = "std")] pub use fields::*; /// This represents an element of a group with basic operations that can be /// performed. This allows an FFT implementation (for example) to operate /// generically over either a field or elliptic curve group. -#[cfg(feature = "std")] pub trait Group: Copy + Clone + Send + Sync + 'static { /// The group is assumed to be of prime order $p$. `Scalar` is the /// associated scalar field of size $p$. diff --git a/src/arithmetic/curves.rs b/src/arithmetic/curves.rs index d954893..4bd128f 100644 --- a/src/arithmetic/curves.rs +++ b/src/arithmetic/curves.rs @@ -23,6 +23,7 @@ use std::{ /// Currently requires the `std` feature flag because of `hash_to_curve`, and /// `CurveAffine::{read, write}`. #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub trait CurveExt: PrimeCurve::AffineExt> + group::Group::ScalarExt> @@ -90,6 +91,7 @@ pub trait CurveExt: /// This trait is the affine counterpart to `Curve` and is used for /// serialization, storage in memory, and inspection of $x$ and $y$ coordinates. #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub trait CurveAffine: PrimeCurveAffine< Scalar = ::ScalarExt, @@ -145,6 +147,7 @@ pub trait CurveAffine: /// The affine coordinates of a point on an elliptic curve. #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[derive(Clone, Copy, Debug, Default)] pub struct Coordinates { pub(crate) x: C::Base, diff --git a/src/arithmetic/fields.rs b/src/arithmetic/fields.rs index dbea4f8..3f47a89 100644 --- a/src/arithmetic/fields.rs +++ b/src/arithmetic/fields.rs @@ -4,22 +4,21 @@ use core::mem::size_of; use static_assertions::const_assert; -use subtle::Choice; +use subtle::{Choice, ConditionallySelectable, CtOption}; -#[cfg(not(feature = "std"))] -use subtle::CtOption; - -#[cfg(feature = "std")] use super::Group; -#[cfg(feature = "std")] -use std::{assert, boxed::Box, convert::TryInto, marker::PhantomData, vec::Vec}; +use core::assert; + +#[cfg(feature = "sqrt-table")] +use alloc::{boxed::Box, vec::Vec}; +#[cfg(feature = "sqrt-table")] +use core::{convert::TryInto, marker::PhantomData}; 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]; @@ -54,7 +53,40 @@ pub trait SqrtRatio: ff::PrimeField { /// 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); + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + // General implementation: + // + // a = num * inv0(div) + // = { 0 if div is zero + // { num/div otherwise + // + // b = G_S * a + // = { 0 if div is zero + // { G_S*num/div otherwise + // + // Since G_S is non-square, a and b are either both zero (and both square), or + // only one of them is square. We can therefore choose the square root to return + // based on whether a is square, but for the boolean output we need to handle the + // num != 0 && div == 0 case specifically. + + let a = div.invert().unwrap_or_else(Self::zero) * num; + let b = a * Self::root_of_unity(); + let sqrt_a = a.sqrt(); + let sqrt_b = b.sqrt(); + + let num_is_zero = num.is_zero(); + let div_is_zero = div.is_zero(); + let is_square = sqrt_a.is_some(); + let is_nonsquare = sqrt_b.is_some(); + assert!(bool::from( + num_is_zero | div_is_zero | (is_square ^ is_nonsquare) + )); + + ( + is_square & !(!num_is_zero & div_is_zero), + CtOption::conditional_select(&sqrt_b, &sqrt_a, is_square).unwrap(), + ) + } /// Equivalent to `Self::sqrt_ratio(self, one())`. fn sqrt_alt(&self) -> (Choice, Self) { @@ -64,7 +96,6 @@ pub trait SqrtRatio: ff::PrimeField { /// This trait is a common interface for dealing with elements of a finite /// field. -#[cfg(feature = "std")] pub trait FieldExt: SqrtRatio + From + Ord + Group { /// Modulus of the field written as a string for display purposes const MODULUS: &'static str; @@ -81,11 +112,6 @@ pub trait FieldExt: SqrtRatio + From + Ord + Group { /// Element of multiplicative order $3$. const ZETA: Self; - /// 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_u128(v: u128) -> Self; @@ -118,12 +144,13 @@ pub trait FieldExt: SqrtRatio + From + Ord + Group { /// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) /// /// `tm1d2` should be set to `(t - 1) // 2`, where `t = (modulus - 1) >> F::S`. -#[cfg(not(feature = "std"))] +#[cfg(not(feature = "sqrt-table"))] +#[cfg_attr(docsrs, doc(cfg(not(feature = "sqrt-table"))))] pub(crate) fn sqrt_tonelli_shanks>( f: &F, tm1d2: S, ) -> CtOption { - use subtle::{ConditionallySelectable, ConstantTimeEq}; + use subtle::ConstantTimeEq; // w = self^((t - 1) // 2) let w = f.pow_vartime(tm1d2); @@ -164,7 +191,8 @@ pub(crate) fn sqrt_tonelli_shanks>( } /// Parameters for a perfect hash function used in square root computation. -#[cfg(feature = "std")] +#[cfg(feature = "sqrt-table")] +#[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))] #[derive(Debug)] struct SqrtHasher { hash_xor: u32, @@ -172,7 +200,7 @@ struct SqrtHasher { marker: PhantomData, } -#[cfg(feature = "std")] +#[cfg(feature = "sqrt-table")] impl SqrtHasher { /// Returns a perfect hash of x for use with SqrtTables::inv. fn hash(&self, x: &F) -> usize { @@ -185,7 +213,8 @@ impl SqrtHasher { } /// Tables used for square root computation. -#[cfg(feature = "std")] +#[cfg(feature = "sqrt-table")] +#[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))] #[derive(Debug)] pub struct SqrtTables { hasher: SqrtHasher, @@ -196,11 +225,11 @@ pub struct SqrtTables { g3: Box<[F; 129]>, } -#[cfg(feature = "std")] +#[cfg(feature = "sqrt-table")] impl SqrtTables { /// Build tables given parameters for the perfect hash. pub fn new(hash_xor: u32, hash_mod: usize) -> Self { - use std::vec; + use alloc::vec; let hasher = SqrtHasher { hash_xor, diff --git a/src/curves.rs b/src/curves.rs index 8661033..7a0db20 100644 --- a/src/curves.rs +++ b/src/curves.rs @@ -103,6 +103,7 @@ macro_rules! new_curve_impl { } #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl group::WnafGroup for $name { fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { // Copied from bls12_381::g1, should be updated. diff --git a/src/fields/fp.rs b/src/fields/fp.rs index d27928f..17be5d6 100644 --- a/src/fields/fp.rs +++ b/src/fields/fp.rs @@ -6,16 +6,16 @@ use ff::PrimeField; use rand::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -#[cfg(feature = "std")] +#[cfg(feature = "sqrt-table")] use lazy_static::lazy_static; #[cfg(feature = "bits")] use ff::{FieldBits, PrimeFieldBits}; -use crate::arithmetic::{adc, mac, sbb}; +use crate::arithmetic::{adc, mac, sbb, FieldExt, Group, SqrtRatio}; -#[cfg(feature = "std")] -use crate::arithmetic::{FieldExt, Group, SqrtRatio, SqrtTables}; +#[cfg(feature = "sqrt-table")] +use crate::arithmetic::SqrtTables; /// This represents an element of $\mathbb{F}_p$ where /// @@ -224,7 +224,6 @@ const ROOT_OF_UNITY: Fp = Fp::from_raw([ /// GENERATOR^{2^s} where t * 2^s + 1 = p /// with t odd. In other words, this /// is a t root of unity. -#[cfg(feature = "std")] const DELTA: Fp = Fp::from_raw([ 0x6a6ccd20dd7b9ba2, 0xf5e4f3f13eee5636, @@ -463,7 +462,6 @@ impl<'a> From<&'a Fp> for [u8; 32] { } } -#[cfg(feature = "std")] impl Group for Fp { type Scalar = Fp; @@ -514,13 +512,13 @@ impl ff::Field for Fp { /// Computes the square root of this element, if it exists. fn sqrt(&self) -> CtOption { - #[cfg(feature = "std")] + #[cfg(feature = "sqrt-table")] { let (is_square, res) = FP_TABLES.sqrt_alt(self); CtOption::new(res, is_square) } - #[cfg(not(feature = "std"))] + #[cfg(not(feature = "sqrt-table"))] crate::arithmetic::sqrt_tonelli_shanks(self, &T_MINUS1_OVER2) } @@ -623,6 +621,7 @@ type ReprBits = [u32; 8]; type ReprBits = [u64; 4]; #[cfg(feature = "bits")] +#[cfg_attr(docsrs, doc(cfg(feature = "bits")))] impl PrimeFieldBits for Fp { type ReprBits = ReprBits; @@ -663,13 +662,13 @@ impl PrimeFieldBits for Fp { } } -#[cfg(feature = "std")] +#[cfg(feature = "sqrt-table")] lazy_static! { // The perfect hash parameters are found by `squareroottab.sage` in zcash/pasta. + #[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))] 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; @@ -711,16 +710,17 @@ impl SqrtRatio for Fp { tmp.0[0] as u32 } + #[cfg(feature = "sqrt-table")] fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { FP_TABLES.sqrt_ratio(num, div) } + #[cfg(feature = "sqrt-table")] fn sqrt_alt(&self) -> (Choice, Self) { FP_TABLES.sqrt_alt(self) } } -#[cfg(feature = "std")] impl FieldExt for Fp { const MODULUS: &'static str = "0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001"; @@ -770,7 +770,7 @@ impl FieldExt for Fp { } } -#[cfg(all(test, feature = "std"))] +#[cfg(test)] use ff::Field; #[test] @@ -788,7 +788,6 @@ fn test_inv() { assert_eq!(inv, INV); } -#[cfg(feature = "std")] #[test] fn test_sqrt() { // NB: TWO_INV is standing in as a "random" field element @@ -796,7 +795,6 @@ fn test_sqrt() { assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV); } -#[cfg(feature = "std")] #[test] fn test_pow_by_t_minus1_over2() { // NB: TWO_INV is standing in as a "random" field element @@ -804,7 +802,6 @@ fn test_pow_by_t_minus1_over2() { assert!(v == ff::Field::pow_vartime(&Fp::TWO_INV, &T_MINUS1_OVER2)); } -#[cfg(feature = "std")] #[test] 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 @@ -851,7 +848,6 @@ fn test_sqrt_ratio_and_alt() { assert!(v == expected); } -#[cfg(feature = "std")] #[test] fn test_zeta() { assert_eq!( @@ -867,7 +863,6 @@ fn test_zeta() { assert!(c == Fp::one()); } -#[cfg(feature = "std")] #[test] fn test_root_of_unity() { assert_eq!( @@ -876,19 +871,16 @@ 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()); } -#[cfg(feature = "std")] #[test] fn test_inv_2() { assert_eq!(Fp::TWO_INV, Fp::from(2).invert().unwrap()); } -#[cfg(feature = "std")] #[test] fn test_delta() { assert_eq!(Fp::DELTA, GENERATOR.pow(&[1u64 << Fp::S, 0, 0, 0])); diff --git a/src/fields/fq.rs b/src/fields/fq.rs index f7111be..618bf54 100644 --- a/src/fields/fq.rs +++ b/src/fields/fq.rs @@ -6,16 +6,16 @@ use ff::PrimeField; use rand::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -#[cfg(feature = "std")] +#[cfg(feature = "sqrt-table")] use lazy_static::lazy_static; #[cfg(feature = "bits")] use ff::{FieldBits, PrimeFieldBits}; -use crate::arithmetic::{adc, mac, sbb}; +use crate::arithmetic::{adc, mac, sbb, FieldExt, Group, SqrtRatio}; -#[cfg(feature = "std")] -use crate::arithmetic::{FieldExt, Group, SqrtRatio, SqrtTables}; +#[cfg(feature = "sqrt-table")] +use crate::arithmetic::SqrtTables; /// This represents an element of $\mathbb{F}_q$ where /// @@ -224,7 +224,6 @@ const ROOT_OF_UNITY: Fq = Fq::from_raw([ /// GENERATOR^{2^s} where t * 2^s + 1 = q /// with t odd. In other words, this /// is a t root of unity. -#[cfg(feature = "std")] const DELTA: Fq = Fq::from_raw([ 0x8494392472d1683c, 0xe3ac3376541d1140, @@ -463,7 +462,6 @@ impl<'a> From<&'a Fq> for [u8; 32] { } } -#[cfg(feature = "std")] impl Group for Fq { type Scalar = Fq; @@ -514,13 +512,13 @@ impl ff::Field for Fq { /// Computes the square root of this element, if it exists. fn sqrt(&self) -> CtOption { - #[cfg(feature = "std")] + #[cfg(feature = "sqrt-table")] { let (is_square, res) = FQ_TABLES.sqrt_alt(self); CtOption::new(res, is_square) } - #[cfg(not(feature = "std"))] + #[cfg(not(feature = "sqrt-table"))] crate::arithmetic::sqrt_tonelli_shanks(self, &T_MINUS1_OVER2) } @@ -663,13 +661,13 @@ impl PrimeFieldBits for Fq { } } -#[cfg(feature = "std")] +#[cfg(feature = "sqrt-table")] lazy_static! { // The perfect hash parameters are found by `squareroottab.sage` in zcash/pasta. + #[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))] 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; @@ -711,16 +709,17 @@ impl SqrtRatio for Fq { tmp.0[0] as u32 } + #[cfg(feature = "sqrt-table")] fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { FQ_TABLES.sqrt_ratio(num, div) } + #[cfg(feature = "sqrt-table")] fn sqrt_alt(&self) -> (Choice, Self) { FQ_TABLES.sqrt_alt(self) } } -#[cfg(feature = "std")] impl FieldExt for Fq { const MODULUS: &'static str = "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001"; @@ -770,7 +769,7 @@ impl FieldExt for Fq { } } -#[cfg(all(test, feature = "std"))] +#[cfg(test)] use ff::Field; #[test] @@ -788,7 +787,6 @@ fn test_inv() { assert_eq!(inv, INV); } -#[cfg(feature = "std")] #[test] fn test_sqrt() { // NB: TWO_INV is standing in as a "random" field element @@ -796,7 +794,6 @@ fn test_sqrt() { assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV); } -#[cfg(feature = "std")] #[test] fn test_pow_by_t_minus1_over2() { // NB: TWO_INV is standing in as a "random" field element @@ -804,7 +801,6 @@ fn test_pow_by_t_minus1_over2() { assert!(v == ff::Field::pow_vartime(&Fq::TWO_INV, &T_MINUS1_OVER2)); } -#[cfg(feature = "std")] #[test] 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 @@ -851,7 +847,6 @@ fn test_sqrt_ratio_and_alt() { assert!(v == expected); } -#[cfg(feature = "std")] #[test] fn test_zeta() { assert_eq!( @@ -866,7 +861,6 @@ fn test_zeta() { assert!(c == Fq::one()); } -#[cfg(feature = "std")] #[test] fn test_root_of_unity() { assert_eq!( @@ -875,19 +869,16 @@ 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()); } -#[cfg(feature = "std")] #[test] fn test_inv_2() { assert_eq!(Fq::TWO_INV, Fq::from(2).invert().unwrap()); } -#[cfg(feature = "std")] #[test] fn test_delta() { assert_eq!(Fq::DELTA, GENERATOR.pow(&[1u64 << Fq::S, 0, 0, 0])); diff --git a/src/lib.rs b/src/lib.rs index add7d5d..c87d047 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,9 @@ #![deny(missing_docs)] #![deny(unsafe_code)] +#[cfg(feature = "alloc")] +extern crate alloc; + #[cfg(any(feature = "std", test))] #[macro_use] extern crate std;