Add a dedicated crate for testing ff/group implementors

Provides extensive testing for dalek-ff-group and ed448.

Also includes a fix for an observed bug in ed448.
This commit is contained in:
Luke Parker
2022-12-24 15:09:09 -05:00
parent 6e518f5c22
commit 445bb3786e
18 changed files with 701 additions and 336 deletions

View File

@@ -32,3 +32,5 @@ dalek-ff-group = { path = "../dalek-ff-group", version = "^0.1.2" }
[dev-dependencies]
hex-literal = "0.3"
hex = "0.4"
ff-group-tests = { path = "../ff-group-tests" }

View File

@@ -38,7 +38,7 @@ macro_rules! field {
impl Neg for $FieldName {
type Output = $FieldName;
fn neg(self) -> $FieldName {
$MODULUS - self
Self(self.0.neg_mod(&$MODULUS.0))
}
}
@@ -104,29 +104,10 @@ macro_rules! field {
}
fn sqrt(&self) -> CtOption<Self> {
unimplemented!()
}
fn is_zero(&self) -> Choice {
self.0.ct_eq(&U512::ZERO)
}
fn cube(&self) -> Self {
self.square() * self
}
fn pow_vartime<S: AsRef<[u64]>>(&self, exp: S) -> Self {
let mut sum = Self::one();
let mut accum = *self;
for (_, num) in exp.as_ref().iter().enumerate() {
let mut num = *num;
for _ in 0 .. 64 {
if (num & 1) == 1 {
sum *= accum;
}
num >>= 1;
accum *= accum;
}
}
sum
const MOD_1_4: $FieldName =
Self($MODULUS.0.saturating_add(&U512::from_u8(1)).wrapping_div(&U512::from_u8(4)));
let res = self.pow(MOD_1_4);
CtOption::new(res, res.square().ct_eq(self))
}
}

View File

@@ -30,30 +30,7 @@ pub(crate) const Q_4: FieldElement =
field!(FieldElement, MODULUS, WIDE_MODULUS, 448);
#[test]
fn repr() {
assert_eq!(FieldElement::from_repr(FieldElement::one().to_repr()).unwrap(), FieldElement::one());
}
#[test]
fn one_two() {
assert_eq!(FieldElement::one() * FieldElement::one().double(), FieldElement::from(2u8));
assert_eq!(
FieldElement::from_repr(FieldElement::from(2u8).to_repr()).unwrap(),
FieldElement::from(2u8)
);
}
#[test]
fn pow() {
assert_eq!(FieldElement::one().pow(FieldElement::one()), FieldElement::one());
let two = FieldElement::one().double();
assert_eq!(two.pow(two), two.double());
let three = two + FieldElement::one();
assert_eq!(three.pow(three), three * three * three);
}
#[test]
fn invert() {
assert_eq!(FieldElement::one().invert().unwrap(), FieldElement::one());
fn test_field() {
// TODO: Move to test_prime_field_bits once the impl is finished
ff_group_tests::prime_field::test_prime_field::<FieldElement>();
}

View File

@@ -1,3 +1,4 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![no_std]
mod backend;

View File

@@ -309,31 +309,38 @@ impl GroupEncoding for Point {
impl PrimeGroup for Point {}
#[test]
fn identity() {
assert_eq!(Point::from_bytes(&Point::identity().to_bytes()).unwrap(), Point::identity());
assert_eq!(Point::identity() + Point::identity(), Point::identity());
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_encoding::<Point>();
}
#[test]
fn addition_multiplication_serialization() {
let mut accum = Point::identity();
for x in 1 .. 10 {
accum += Point::generator();
let mul = Point::generator() * Scalar::from(u8::try_from(x).unwrap());
assert_eq!(accum, mul);
assert_eq!(Point::from_bytes(&mul.to_bytes()).unwrap(), mul);
}
}
#[rustfmt::skip]
#[test]
fn torsion() {
use generic_array::GenericArray;
// Uses the originally suggested generator which had torsion
let old_y = FieldElement::from_repr(
hex_literal::hex!(
"12796c1532041525945f322e414d434467cfd5c57c9a9af2473b27758c921c4828b277ca5f2891fc4f3d79afdf29a64c72fb28b59c16fa5100"
).into(),
)
let old_y = FieldElement::from_repr(*GenericArray::from_slice(
&hex::decode(
"\
12796c1532041525945f322e414d434467cfd5c57c9a9af2473b2775\
8c921c4828b277ca5f2891fc4f3d79afdf29a64c72fb28b59c16fa51\
00",
)
.unwrap(),
))
.unwrap();
let old = Point { x: -recover_x(old_y).unwrap(), y: old_y, z: FieldElement::one() };
assert!(bool::from(!old.is_torsion_free()));
@@ -382,6 +389,7 @@ a401cd9df24632adfe6b418dc942d8a091817dd8bd70e1c72ba52f3c\
);
}
// Checks random won't infinitely loop
#[test]
fn random() {
Point::random(&mut rand_core::OsRng);

View File

@@ -33,6 +33,7 @@ impl Scalar {
}
#[test]
fn invert() {
assert_eq!(Scalar::one().invert().unwrap(), Scalar::one());
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>();
}