* Partial move to ff 0.13

It turns out the newly released k256 0.12 isn't on ff 0.13, preventing further
work at this time.

* Update all crates to work on ff 0.13

The provided curves still need to be expanded to fit the new API.

* Finish adding dalek-ff-group ff 0.13 constants

* Correct FieldElement::product definition

Also stops exporting macros.

* Test most new parts of ff 0.13

* Additionally test ff-group-tests with BLS12-381 and the pasta curves

We only tested curves from RustCrypto. Now we test a curve offered by zk-crypto,
the group behind ff/group, and the pasta curves, which is by Zcash (though
Zcash developers are also behind zk-crypto).

* Finish Ed448

Fully specifies all constants, passes all tests in ff-group-tests, and finishes moving to ff-0.13.

* Add RustCrypto/elliptic-curves to allowed git repos

Needed due to k256/p256 incorrectly defining product.

* Finish writing ff 0.13 tests

* Add additional comments to dalek

* Further comments

* Update ethereum-serai to ff 0.13
This commit is contained in:
Luke Parker
2023-03-28 04:38:01 -04:00
committed by GitHub
parent a9f6300e86
commit 79aff5d4c8
59 changed files with 865 additions and 429 deletions

View File

@@ -20,14 +20,12 @@ rand_core = "0.6"
zeroize = { version = "^1.5", features = ["zeroize_derive"] }
subtle = "^2.4"
ff = { version = "0.12", features = ["bits"] }
group = "0.12"
ff = "0.13"
group = "0.13"
generic-array = "0.14"
crypto-bigint = { version = "0.5", features = ["zeroize"] }
dalek-ff-group = { path = "../dalek-ff-group", version = "0.3" }
[dev-dependencies]
hex = "0.4"

View File

@@ -3,8 +3,7 @@
Inefficient, barebones implementation of Ed448 bound to the ff/group API,
rejecting torsion to achieve a PrimeGroup definition. This likely should not be
used and was only done so another library under Serai could confirm its
completion. It is minimally tested, yet should be correct for what it has. The
functions it doesn't have are marked `unimplemented!()`. This has not undergone
auditing.
completion. It is minimally tested, yet should be correct for what it has. This
has not undergone auditing.
constant time and no_std.

View File

@@ -23,11 +23,70 @@ pub(crate) fn u8_from_bool(bit_ref: &mut bool) -> u8 {
res
}
#[doc(hidden)]
#[macro_export]
macro_rules! math_op {
(
$Value: ident,
$Other: ident,
$Op: ident,
$op_fn: ident,
$Assign: ident,
$assign_fn: ident,
$function: expr
) => {
impl $Op<$Other> for $Value {
type Output = $Value;
fn $op_fn(self, other: $Other) -> Self::Output {
Self($function(self.0, other.0))
}
}
impl $Assign<$Other> for $Value {
fn $assign_fn(&mut self, other: $Other) {
self.0 = $function(self.0, other.0);
}
}
impl<'a> $Op<&'a $Other> for $Value {
type Output = $Value;
fn $op_fn(self, other: &'a $Other) -> Self::Output {
Self($function(self.0, other.0))
}
}
impl<'a> $Assign<&'a $Other> for $Value {
fn $assign_fn(&mut self, other: &'a $Other) {
self.0 = $function(self.0, other.0);
}
}
};
}
macro_rules! from_wrapper {
($wrapper: ident, $inner: ident, $uint: ident) => {
impl From<$uint> for $wrapper {
fn from(a: $uint) -> $wrapper {
Self($inner::from(a))
}
}
};
}
macro_rules! field {
($FieldName: ident, $MODULUS: ident, $WIDE_MODULUS: ident, $NUM_BITS: literal) => {
use core::ops::{DerefMut, Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign};
(
$FieldName: ident,
$MODULUS_STR: ident,
$MODULUS: ident,
$WIDE_MODULUS: ident,
$NUM_BITS: literal,
$TWO_INV: expr,
$MULTIPLICATIVE_GENERATOR: literal,
$ROOT_OF_UNITY_INV: expr,
$DELTA: expr,
) => {
use core::{
ops::{DerefMut, Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign},
iter::{Sum, Product},
};
use subtle::{Choice, CtOption, ConstantTimeEq, ConstantTimeLess, ConditionallySelectable};
use rand_core::RngCore;
@@ -35,12 +94,7 @@ macro_rules! field {
use generic_array::{typenum::U57, GenericArray};
use crypto_bigint::{Integer, NonZero, Encoding};
use group::ff::{Field, PrimeField, FieldBits, PrimeFieldBits};
// Needed to publish for some reason? Yet not actually needed
#[allow(unused_imports)]
use dalek_ff_group::{from_wrapper, math_op};
use dalek_ff_group::{constant_time, from_uint, math};
use ff::{Field, PrimeField, FieldBits, PrimeFieldBits, helpers::sqrt_ratio_generic};
use $crate::backend::u8_from_bool;
@@ -48,15 +102,37 @@ macro_rules! field {
U512::from_le_slice(&x.rem(&NonZero::new($WIDE_MODULUS).unwrap()).to_le_bytes()[.. 64])
}
constant_time!($FieldName, U512);
math!(
$FieldName,
$FieldName,
|x, y| U512::add_mod(&x, &y, &$MODULUS.0),
|x, y| U512::sub_mod(&x, &y, &$MODULUS.0),
|x, y| reduce(U1024::from(U512::mul_wide(&x, &y)))
);
from_uint!($FieldName, U512);
impl ConstantTimeEq for $FieldName {
fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0)
}
}
impl ConditionallySelectable for $FieldName {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
$FieldName(U512::conditional_select(&a.0, &b.0, choice))
}
}
math_op!($FieldName, $FieldName, Add, add, AddAssign, add_assign, |x, y| U512::add_mod(
&x,
&y,
&$MODULUS.0
));
math_op!($FieldName, $FieldName, Sub, sub, SubAssign, sub_assign, |x, y| U512::sub_mod(
&x,
&y,
&$MODULUS.0
));
math_op!($FieldName, $FieldName, Mul, mul, MulAssign, mul_assign, |x, y| reduce(U1024::from(
U512::mul_wide(&x, &y)
)));
from_wrapper!($FieldName, U512, u8);
from_wrapper!($FieldName, U512, u16);
from_wrapper!($FieldName, U512, u32);
from_wrapper!($FieldName, U512, u64);
from_wrapper!($FieldName, U512, u128);
impl Neg for $FieldName {
type Output = $FieldName;
@@ -104,18 +180,15 @@ macro_rules! field {
}
impl Field for $FieldName {
const ZERO: Self = Self(U512::ZERO);
const ONE: Self = Self(U512::ONE);
fn random(mut rng: impl RngCore) -> Self {
let mut bytes = [0; 128];
rng.fill_bytes(&mut bytes);
$FieldName(reduce(U1024::from_le_slice(bytes.as_ref())))
}
fn zero() -> Self {
Self(U512::ZERO)
}
fn one() -> Self {
Self(U512::ONE)
}
fn square(&self) -> Self {
*self * self
}
@@ -134,12 +207,32 @@ macro_rules! field {
let res = self.pow(MOD_1_4);
CtOption::new(res, res.square().ct_eq(self))
}
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
sqrt_ratio_generic(num, div)
}
}
impl PrimeField for $FieldName {
type Repr = GenericArray<u8, U57>;
const MODULUS: &'static str = $MODULUS_STR;
const NUM_BITS: u32 = $NUM_BITS;
const CAPACITY: u32 = $NUM_BITS - 1;
const TWO_INV: Self = $FieldName(U512::from_le_hex($TWO_INV));
const MULTIPLICATIVE_GENERATOR: Self = Self(U512::from_u8($MULTIPLICATIVE_GENERATOR));
// True for both the Ed448 Scalar field and FieldElement field
const S: u32 = 1;
// Both fields have their root of unity as -1
const ROOT_OF_UNITY: Self = Self($MODULUS.0.saturating_sub(&U512::from_u8(1)));
const ROOT_OF_UNITY_INV: Self = $FieldName(U512::from_le_hex($ROOT_OF_UNITY_INV));
const DELTA: Self = $FieldName(U512::from_le_hex($DELTA));
fn from_repr(bytes: Self::Repr) -> CtOption<Self> {
let res = $FieldName(U512::from_le_slice(&[bytes.as_ref(), [0; 7].as_ref()].concat()));
CtOption::new(res, res.0.ct_lt(&$MODULUS.0))
@@ -150,17 +243,9 @@ macro_rules! field {
repr
}
// True for both the Ed448 Scalar field and FieldElement field
const S: u32 = 1;
fn is_odd(&self) -> Choice {
self.0.is_odd()
}
fn multiplicative_generator() -> Self {
unimplemented!()
}
fn root_of_unity() -> Self {
unimplemented!()
}
}
impl PrimeFieldBits for $FieldName {
@@ -176,5 +261,37 @@ macro_rules! field {
MODULUS.to_le_bits()
}
}
impl Sum<$FieldName> for $FieldName {
fn sum<I: Iterator<Item = $FieldName>>(iter: I) -> $FieldName {
let mut res = $FieldName::ZERO;
for item in iter {
res += item;
}
res
}
}
impl<'a> Sum<&'a $FieldName> for $FieldName {
fn sum<I: Iterator<Item = &'a $FieldName>>(iter: I) -> $FieldName {
iter.cloned().sum()
}
}
impl Product<$FieldName> for $FieldName {
fn product<I: Iterator<Item = $FieldName>>(iter: I) -> $FieldName {
let mut res = $FieldName::ONE;
for item in iter {
res *= item;
}
res
}
}
impl<'a> Product<&'a $FieldName> for $FieldName {
fn product<I: Iterator<Item = &'a $FieldName>>(iter: I) -> $FieldName {
iter.cloned().product()
}
}
};
}

View File

@@ -2,12 +2,15 @@ use zeroize::Zeroize;
use crypto_bigint::{U512, U1024};
use crate::field;
/// Ed448 field element.
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)]
pub struct FieldElement(pub(crate) U512);
const MODULUS_STR: &str = concat!(
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
);
// 2**448 - 2**224 - 1
pub(crate) const MODULUS: FieldElement = FieldElement(U512::from_be_hex(concat!(
"00000000000000",
@@ -22,16 +25,34 @@ const WIDE_MODULUS: U1024 = U1024::from_be_hex(concat!(
"00000000000000",
"00",
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
));
pub(crate) const Q_4: FieldElement =
FieldElement(MODULUS.0.saturating_add(&U512::ONE).wrapping_div(&U512::from_u8(4)));
field!(FieldElement, MODULUS, WIDE_MODULUS, 448);
field!(
FieldElement,
MODULUS_STR,
MODULUS,
WIDE_MODULUS,
448,
concat!(
"00000000000000000000000000000000000000000000000000000080ffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000",
),
7,
concat!(
"fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000",
),
concat!(
"3100000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
),
);
#[test]
fn test_field() {
// TODO: Move to test_prime_field_bits once the impl is finished
ff_group_tests::prime_field::test_prime_field::<_, FieldElement>(&mut rand_core::OsRng);
ff_group_tests::prime_field::test_prime_field_bits::<_, FieldElement>(&mut rand_core::OsRng);
}

View File

@@ -2,6 +2,7 @@
#![no_std]
#![doc = include_str!("../README.md")]
#[macro_use]
mod backend;
mod scalar;

View File

@@ -37,13 +37,13 @@ fn recover_x(y: FieldElement) -> CtOption<FieldElement> {
let ysq = y.square();
#[allow(non_snake_case)]
let D_ysq = D * ysq;
(D_ysq - FieldElement::one()).invert().and_then(|inverted| {
let temp = (ysq - FieldElement::one()) * inverted;
(D_ysq - FieldElement::ONE).invert().and_then(|inverted| {
let temp = (ysq - FieldElement::ONE) * inverted;
let mut x = temp.pow(Q_4);
x.conditional_negate(x.is_odd());
let xsq = x.square();
CtOption::new(x, (xsq + ysq).ct_eq(&(FieldElement::one() + (xsq * D_ysq))))
CtOption::new(x, (xsq + ysq).ct_eq(&(FieldElement::ONE + (xsq * D_ysq))))
})
}
@@ -56,7 +56,7 @@ pub struct Point {
}
lazy_static! {
static ref G: Point = Point { x: recover_x(G_Y).unwrap(), y: G_Y, z: FieldElement::one() };
static ref G: Point = Point { x: recover_x(G_Y).unwrap(), y: G_Y, z: FieldElement::ONE };
}
impl ConstantTimeEq for Point {
@@ -180,7 +180,7 @@ impl Group for Point {
}
}
fn identity() -> Self {
Point { x: FieldElement::zero(), y: FieldElement::one(), z: FieldElement::one() }
Point { x: FieldElement::ZERO, y: FieldElement::ONE, z: FieldElement::ONE }
}
fn generator() -> Self {
*G
@@ -291,7 +291,7 @@ impl GroupEncoding for Point {
recover_x(y).and_then(|mut x| {
x.conditional_negate(x.is_odd().ct_eq(&!sign));
let not_negative_zero = !(x.is_zero() & sign);
let point = Point { x, y, z: FieldElement::one() };
let point = Point { x, y, z: FieldElement::ONE };
CtOption::new(point, not_negative_zero & point.is_torsion_free())
})
})
@@ -317,22 +317,7 @@ impl PrimeGroup for Point {}
#[test]
fn test_group() {
// TODO: Move to test_prime_group_bits once the impl is finished
use ff_group_tests::group::*;
test_eq::<Point>();
test_identity::<Point>();
test_generator::<Point>();
test_double::<Point>();
test_add::<Point>();
test_sum::<Point>();
test_neg::<Point>();
test_sub::<Point>();
test_mul::<Point>();
test_order::<Point>();
test_random::<_, Point>(&mut rand_core::OsRng);
test_encoding::<Point>();
ff_group_tests::group::test_prime_group_bits::<_, Point>(&mut rand_core::OsRng);
}
#[test]
@@ -350,7 +335,7 @@ fn torsion() {
.unwrap(),
))
.unwrap();
let old = Point { x: -recover_x(old_y).unwrap(), y: old_y, z: FieldElement::one() };
let old = Point { x: -recover_x(old_y).unwrap(), y: old_y, z: FieldElement::ONE };
assert!(bool::from(!old.is_torsion_free()));
}

View File

@@ -2,12 +2,15 @@ use zeroize::Zeroize;
use crypto_bigint::{U512, U1024};
use crate::field;
/// Ed448 Scalar field element.
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)]
pub struct Scalar(pub(crate) U512);
const MODULUS_STR: &str = concat!(
"3fffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3",
);
// 2**446 - 13818066809895115352007386748515426880336692474882178609894547503885
pub(crate) const MODULUS: Scalar = Scalar(U512::from_be_hex(concat!(
"00000000000000",
@@ -25,7 +28,26 @@ const WIDE_MODULUS: U1024 = U1024::from_be_hex(concat!(
"7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3",
));
field!(Scalar, MODULUS, WIDE_MODULUS, 446);
field!(
Scalar,
MODULUS_STR,
MODULUS,
WIDE_MODULUS,
446,
concat!(
"7a22ac554961bc91aac7e2463961b610481b6bd7a46d27e2f41165beffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffff1f0000000000000000",
),
2,
concat!(
"f24458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffff3f0000000000000000",
),
concat!(
"0400000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
),
);
impl Scalar {
/// Perform a wide reduction to obtain a non-biased Scalar.
@@ -35,7 +57,6 @@ impl Scalar {
}
#[test]
fn test_scalar_field() {
// TODO: Move to test_prime_field_bits once the impl is finished
ff_group_tests::prime_field::test_prime_field::<_, Scalar>(&mut rand_core::OsRng);
fn test_scalar() {
ff_group_tests::prime_field::test_prime_field_bits::<_, Scalar>(&mut rand_core::OsRng);
}