From 6ce506a79d27ac110e1e7332911092b222ed136a Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sun, 10 Jul 2022 15:20:42 -0400 Subject: [PATCH] Add an implementation of Ed25519 FieldElements --- crypto/dalek-ff-group/Cargo.toml | 1 + crypto/dalek-ff-group/src/field.rs | 178 +++++++++++++++++++++++++++++ crypto/dalek-ff-group/src/lib.rs | 40 ++++--- 3 files changed, 203 insertions(+), 16 deletions(-) create mode 100644 crypto/dalek-ff-group/src/field.rs diff --git a/crypto/dalek-ff-group/Cargo.toml b/crypto/dalek-ff-group/Cargo.toml index 3b78578f..a0625ca1 100644 --- a/crypto/dalek-ff-group/Cargo.toml +++ b/crypto/dalek-ff-group/Cargo.toml @@ -17,4 +17,5 @@ subtle = "2.4" ff = "0.12" group = "0.12" +crypto-bigint = "0.4" curve25519-dalek = "3.2" diff --git a/crypto/dalek-ff-group/src/field.rs b/crypto/dalek-ff-group/src/field.rs new file mode 100644 index 00000000..2e7db7ca --- /dev/null +++ b/crypto/dalek-ff-group/src/field.rs @@ -0,0 +1,178 @@ +use core::ops::{Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign}; + +use rand_core::RngCore; + +use subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable}; +use crypto_bigint::{Encoding, U256, U512}; + +use ff::{Field, PrimeField, FieldBits, PrimeFieldBits}; + +use crate::{choice, from_wrapper, from_uint}; + +const FIELD_MODULUS: U256 = U256::from_be_hex( + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" +); + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub struct FieldElement(U256); + +pub const SQRT_M1: FieldElement = FieldElement( + U256::from_be_hex("2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0") +); + +macro_rules! math { + ($Op: ident, $op_fn: ident, $Assign: ident, $assign_fn: ident, $function: expr) => { + impl $Op for FieldElement { + type Output = Self; + fn $op_fn(self, other: FieldElement) -> Self::Output { + Self($function(&self.0, &other.0, &FIELD_MODULUS)) + } + } + impl $Assign for FieldElement { + fn $assign_fn(&mut self, other: FieldElement) { + self.0 = $function(&self.0, &other.0, &FIELD_MODULUS); + } + } + impl<'a> $Op<&'a FieldElement> for FieldElement { + type Output = Self; + fn $op_fn(self, other: &'a FieldElement) -> Self::Output { + Self($function(&self.0, &other.0, &FIELD_MODULUS)) + } + } + impl<'a> $Assign<&'a FieldElement> for FieldElement { + fn $assign_fn(&mut self, other: &'a FieldElement) { + self.0 = $function(&self.0, &other.0, &FIELD_MODULUS); + } + } + } +} +math!(Add, add, AddAssign, add_assign, U256::add_mod); +math!(Sub, sub, SubAssign, sub_assign, U256::sub_mod); +math!( + Mul, mul, + MulAssign, mul_assign, + |a, b, _: &U256| { + #[allow(non_snake_case)] + let WIDE_MODULUS: U512 = U512::from((U256::ZERO, FIELD_MODULUS)); + debug_assert_eq!(FIELD_MODULUS.to_le_bytes()[..], WIDE_MODULUS.to_le_bytes()[.. 32]); + + let wide = U256::mul_wide(a, b); + U256::from_le_slice( + &U512::from((wide.1, wide.0)).reduce(&WIDE_MODULUS).unwrap().to_le_bytes()[.. 32] + ) + } +); + +impl Neg for FieldElement { + type Output = Self; + fn neg(self) -> Self::Output { Self(self.0.neg_mod(&FIELD_MODULUS)) } +} + +impl ConstantTimeEq for FieldElement { + fn ct_eq(&self, other: &Self) -> Choice { self.0.ct_eq(&other.0) } +} + +impl ConditionallySelectable for FieldElement { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + FieldElement(U256::conditional_select(&a.0, &b.0, choice)) + } +} + +impl Field for FieldElement { + fn random(mut rng: impl RngCore) -> Self { + let mut bytes = [0; 64]; + rng.fill_bytes(&mut bytes); + + #[allow(non_snake_case)] + let WIDE_MODULUS: U512 = U512::from((U256::ZERO, FIELD_MODULUS)); + debug_assert_eq!(FIELD_MODULUS.to_le_bytes()[..], WIDE_MODULUS.to_le_bytes()[.. 32]); + + FieldElement( + U256::from_le_slice( + &U512::from_be_bytes(bytes).reduce(&WIDE_MODULUS).unwrap().to_le_bytes()[.. 32] + ) + ) + } + + fn zero() -> Self { Self(U256::ZERO) } + fn one() -> Self { Self(U256::ONE) } + fn square(&self) -> Self { *self * self } + fn double(&self) -> Self { *self + self } + + fn invert(&self) -> CtOption { + CtOption::new(self.pow(-FieldElement(U256::from(2u64))), !self.is_zero()) + } + + fn sqrt(&self) -> CtOption { + let c1 = SQRT_M1; + let c2 = FIELD_MODULUS.saturating_add(&U256::from(3u8)).checked_div(&U256::from(8u8)).unwrap(); + + let tv1 = self.pow(FieldElement(c2)); + let tv2 = tv1 * c1; + let res = Self::conditional_select(&tv2, &tv1, tv1.square().ct_eq(self)); + debug_assert_eq!(res * res, *self); + CtOption::new(Self::conditional_select(&tv2, &tv1, tv1.square().ct_eq(self)), 1.into()) + } + + fn is_zero(&self) -> Choice { self.0.ct_eq(&U256::ZERO) } + fn cube(&self) -> Self { *self * self * self } + fn pow_vartime>(&self, _exp: S) -> Self { unimplemented!() } +} + +from_uint!(FieldElement, U256); + +impl PrimeField for FieldElement { + type Repr = [u8; 32]; + const NUM_BITS: u32 = 255; + const CAPACITY: u32 = 254; + fn from_repr(bytes: [u8; 32]) -> CtOption { + let res = Self(U256::from_le_bytes(bytes)); + CtOption::new(res, res.0.add_mod(&U256::ZERO, &FIELD_MODULUS).ct_eq(&res.0)) + } + fn to_repr(&self) -> [u8; 32] { self.0.to_le_bytes() } + + const S: u32 = 2; + fn is_odd(&self) -> Choice { unimplemented!() } + fn multiplicative_generator() -> Self { 2u64.into() } + fn root_of_unity() -> Self { + FieldElement( + U256::from_be_hex("2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0") + ) + } +} + +impl PrimeFieldBits for FieldElement { + type ReprBits = [u8; 32]; + + fn to_le_bits(&self) -> FieldBits { + self.to_repr().into() + } + + fn char_le_bits() -> FieldBits { + FIELD_MODULUS.to_le_bytes().into() + } +} + +impl FieldElement { + pub fn from_square(value: [u8; 32]) -> FieldElement { + let value = U256::from_le_bytes(value); + FieldElement(value) * FieldElement(value) + } + + pub fn pow(&self, other: FieldElement) -> FieldElement { + let mut res = FieldElement(U256::ONE); + let mut m = *self; + for bit in other.to_le_bits() { + res *= FieldElement::conditional_select(&FieldElement(U256::ONE), &m, choice(bit)); + m *= m; + } + res + } +} + +#[test] +fn test_mul() { + assert_eq!(FieldElement(FIELD_MODULUS) * FieldElement::one(), FieldElement::zero()); + assert_eq!(FieldElement(FIELD_MODULUS) * FieldElement::one().double(), FieldElement::zero()); + assert_eq!(FieldElement(SQRT_M1).square(), -FieldElement::one()); +} diff --git a/crypto/dalek-ff-group/src/lib.rs b/crypto/dalek-ff-group/src/lib.rs index 66146d1b..1e3d12b2 100644 --- a/crypto/dalek-ff-group/src/lib.rs +++ b/crypto/dalek-ff-group/src/lib.rs @@ -32,6 +32,8 @@ use dalek::{ use ff::{Field, PrimeField, FieldBits, PrimeFieldBits}; use group::{Group, GroupEncoding, prime::PrimeGroup}; +pub mod field; + // Convert a boolean to a Choice in a *presumably* constant time manner fn choice(value: bool) -> Choice { let bit = value as u8; @@ -120,11 +122,33 @@ macro_rules! math { } } +#[doc(hidden)] +#[macro_export] +macro_rules! from_wrapper { + ($wrapper: ident, $inner: ident, $uint: ident) => { + impl From<$uint> for $wrapper { + fn from(a: $uint) -> $wrapper { Self($inner::from(a)) } + } + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! from_uint { + ($wrapper: ident, $inner: ident) => { + from_wrapper!($wrapper, $inner, u8); + from_wrapper!($wrapper, $inner, u16); + from_wrapper!($wrapper, $inner, u32); + from_wrapper!($wrapper, $inner, u64); + } +} + /// Wrapper around the dalek Scalar type #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] pub struct Scalar(pub DScalar); deref_borrow!(Scalar, DScalar); math!(Scalar, Scalar, Scalar); +from_uint!(Scalar, DScalar); impl Scalar { /// Perform wide reduction on a 64-byte array to create a Scalar without bias @@ -170,22 +194,6 @@ impl Field for Scalar { fn pow_vartime>(&self, _exp: S) -> Self { unimplemented!() } } -impl From for Scalar { - fn from(a: u8) -> Scalar { Self(DScalar::from(a)) } -} - -impl From for Scalar { - fn from(a: u16) -> Scalar { Self(DScalar::from(a)) } -} - -impl From for Scalar { - fn from(a: u32) -> Scalar { Self(DScalar::from(a)) } -} - -impl From for Scalar { - fn from(a: u64) -> Scalar { Self(DScalar::from(a)) } -} - impl PrimeField for Scalar { type Repr = [u8; 32]; const NUM_BITS: u32 = 253;