fields: Ensure that trait impl dependencies match trait bounds

The `FieldExt` trait was originally the only trait implemented in this
crate. When we added `ff` support, we reworked `FieldExt` to be an
extension trait on top of `ff::PrimeField`, but left the existing impls
in `FieldExt`. This resulted in some circular dependencies that prevent
us from making `FieldExt` conditional (e.g. for no-std support).

This commit removes the cycles like so:

- `ff::PrimeField::{from_repr, to_repr}` were implemented as calls to
  `FieldExt::{from_bytes, to_bytes}`. The field encoding/decoding logic
  is moved into the `ff::PrimeField` trait impl, and `FieldExt` now
  calls into `ff::PrimeField`.

- `ff::Field::sqrt` was implemented in terms of `FieldExt::sqrt_alt`.
  Given that the latter is a trivial wrapper around the `SqrtTables`
  implementation, we duplicate the call to eliminate the cycle.

- `ff::Field::random` used `FieldExt::from_bytes_wide`, which wraps
  either `Fp::from_u512` or `Fq::from_u512`. We now use these internal
  methods directly.
This commit is contained in:
Jack Grigg 2021-09-20 16:34:28 +01:00
parent 87488ec1f6
commit 6a47700b1d
2 changed files with 98 additions and 94 deletions

View file

@ -466,10 +466,16 @@ impl Group for Fp {
impl ff::Field for Fp {
fn random(mut rng: impl RngCore) -> Self {
let mut random_bytes = [0; 64];
rng.fill_bytes(&mut random_bytes[..]);
Self::from_bytes_wide(&random_bytes)
Self::from_u512([
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
])
}
fn zero() -> Self {
@ -491,7 +497,7 @@ impl ff::Field for Fp {
/// Computes the square root of this element, if it exists.
fn sqrt(&self) -> CtOption<Self> {
let (is_square, res) = self.sqrt_alt();
let (is_square, res) = FP_TABLES.sqrt_alt(self);
CtOption::new(res, is_square)
}
@ -535,15 +541,47 @@ impl ff::PrimeField for Fp {
const S: u32 = S;
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
Self::from_bytes(&repr)
let mut tmp = Fp([0, 0, 0, 0]);
tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap());
tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap());
tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap());
tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap());
// Try to subtract the modulus
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
// If the element is smaller than MODULUS then the
// subtraction will underflow, producing a borrow value
// of 0xffff...ffff. Otherwise, it'll be zero.
let is_some = (borrow as u8) & 1;
// Convert to Montgomery form by computing
// (a.R^0 * R^2) / R = a.R
tmp *= &R2;
CtOption::new(tmp, Choice::from(is_some))
}
fn to_repr(&self) -> Self::Repr {
self.to_bytes()
// Turn into canonical form by computing
// (a.R) / R = a
let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res
}
fn is_odd(&self) -> Choice {
Choice::from(self.to_bytes()[0] & 1)
Choice::from(self.to_repr()[0] & 1)
}
fn multiplicative_generator() -> Self {
@ -551,7 +589,7 @@ impl ff::PrimeField for Fp {
}
fn root_of_unity() -> Self {
Self::ROOT_OF_UNITY
ROOT_OF_UNITY
}
}
@ -660,48 +698,12 @@ impl FieldExt for Fp {
Fp::from_raw([v as u64, (v >> 64) as u64, 0, 0])
}
/// Attempts to convert a little-endian byte representation of
/// a scalar into a `Fp`, failing if the input is not canonical.
fn from_bytes(bytes: &[u8; 32]) -> CtOption<Fp> {
let mut tmp = Fp([0, 0, 0, 0]);
tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap());
tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap());
tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap());
// Try to subtract the modulus
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
// If the element is smaller than MODULUS then the
// subtraction will underflow, producing a borrow value
// of 0xffff...ffff. Otherwise, it'll be zero.
let is_some = (borrow as u8) & 1;
// Convert to Montgomery form by computing
// (a.R^0 * R^2) / R = a.R
tmp *= &R2;
CtOption::new(tmp, Choice::from(is_some))
<Self as ff::PrimeField>::from_repr(*bytes)
}
/// Converts an element of `Fp` into a byte representation in
/// little-endian byte order.
fn to_bytes(&self) -> [u8; 32] {
// Turn into canonical form by computing
// (a.R) / R = a
let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res
<Self as ff::PrimeField>::to_repr(self)
}
/// Converts a 512-bit little endian integer into

View file

@ -466,10 +466,16 @@ impl Group for Fq {
impl ff::Field for Fq {
fn random(mut rng: impl RngCore) -> Self {
let mut random_bytes = [0; 64];
rng.fill_bytes(&mut random_bytes[..]);
Self::from_bytes_wide(&random_bytes)
Self::from_u512([
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
])
}
fn zero() -> Self {
@ -491,7 +497,7 @@ impl ff::Field for Fq {
/// Computes the square root of this element, if it exists.
fn sqrt(&self) -> CtOption<Self> {
let (is_square, res) = self.sqrt_alt();
let (is_square, res) = FQ_TABLES.sqrt_alt(self);
CtOption::new(res, is_square)
}
@ -535,15 +541,47 @@ impl ff::PrimeField for Fq {
const S: u32 = S;
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
Self::from_bytes(&repr)
let mut tmp = Fq([0, 0, 0, 0]);
tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap());
tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap());
tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap());
tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap());
// Try to subtract the modulus
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
// If the element is smaller than MODULUS then the
// subtraction will underflow, producing a borrow value
// of 0xffff...ffff. Otherwise, it'll be zero.
let is_some = (borrow as u8) & 1;
// Convert to Montgomery form by computing
// (a.R^0 * R^2) / R = a.R
tmp *= &R2;
CtOption::new(tmp, Choice::from(is_some))
}
fn to_repr(&self) -> Self::Repr {
self.to_bytes()
// Turn into canonical form by computing
// (a.R) / R = a
let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res
}
fn is_odd(&self) -> Choice {
Choice::from(self.to_bytes()[0] & 1)
Choice::from(self.to_repr()[0] & 1)
}
fn multiplicative_generator() -> Self {
@ -551,7 +589,7 @@ impl ff::PrimeField for Fq {
}
fn root_of_unity() -> Self {
Self::ROOT_OF_UNITY
ROOT_OF_UNITY
}
}
@ -660,48 +698,12 @@ impl FieldExt for Fq {
Fq::from_raw([v as u64, (v >> 64) as u64, 0, 0])
}
/// Attempts to convert a little-endian byte representation of
/// a scalar into a `Fq`, failing if the input is not canonical.
fn from_bytes(bytes: &[u8; 32]) -> CtOption<Fq> {
let mut tmp = Fq([0, 0, 0, 0]);
tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap());
tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap());
tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap());
// Try to subtract the modulus
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
// If the element is smaller than MODULUS then the
// subtraction will underflow, producing a borrow value
// of 0xffff...ffff. Otherwise, it'll be zero.
let is_some = (borrow as u8) & 1;
// Convert to Montgomery form by computing
// (a.R^0 * R^2) / R = a.R
tmp *= &R2;
CtOption::new(tmp, Choice::from(is_some))
<Self as ff::PrimeField>::from_repr(*bytes)
}
/// Converts an element of `Fq` into a byte representation in
/// little-endian byte order.
fn to_bytes(&self) -> [u8; 32] {
// Turn into canonical form by computing
// (a.R) / R = a
let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res
<Self as ff::PrimeField>::to_repr(self)
}
/// Converts a 512-bit little endian integer into