use ecgfp5::{
curve::Point as PorninPoint, field::GFp5 as PorninFp5, scalar::Scalar as PorninScalar,
};
use proptest::prelude::*;
use rstest::rstest;
use super::{
curve::{Point, Scalar},
field::Fp5,
fixtures::{arb_fp5, arb_fp5_nonzero, arb_scalar, arb_scalar_nonzero},
};
fn fp5_to_pornin(ours: Fp5) -> PorninFp5 {
let (gfp5, ok) = PorninFp5::decode(&ours.to_le_bytes());
assert_eq!(
ok,
u64::MAX,
"our canonical Fp5 bytes must decode under Pornin's reference",
);
gfp5
}
fn scalar_to_pornin(ours: Scalar) -> PorninScalar {
PorninScalar::decode_reduce(&ours.to_le_bytes())
}
proptest! {
#[rstest]
fn prop_fp5_add_matches_pornin(a in arb_fp5(), b in arb_fp5()) {
let ours = (a + b).to_le_bytes();
let theirs = (fp5_to_pornin(a) + fp5_to_pornin(b)).encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_fp5_sub_matches_pornin(a in arb_fp5(), b in arb_fp5()) {
let ours = (a - b).to_le_bytes();
let theirs = (fp5_to_pornin(a) - fp5_to_pornin(b)).encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_fp5_mul_matches_pornin(a in arb_fp5(), b in arb_fp5()) {
let ours = (a * b).to_le_bytes();
let theirs = (fp5_to_pornin(a) * fp5_to_pornin(b)).encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_fp5_neg_matches_pornin(a in arb_fp5()) {
let ours = (-a).to_le_bytes();
let theirs = (-fp5_to_pornin(a)).encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_fp5_invert_matches_pornin(a in arb_fp5_nonzero()) {
let ours = a.invert().to_le_bytes();
let theirs = fp5_to_pornin(a).invert().encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_scalar_add_matches_pornin(a in arb_scalar(), b in arb_scalar()) {
let ours = (a + b).to_le_bytes();
let theirs = (scalar_to_pornin(a) + scalar_to_pornin(b)).encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_scalar_sub_matches_pornin(a in arb_scalar(), b in arb_scalar()) {
let ours = (a - b).to_le_bytes();
let theirs = (scalar_to_pornin(a) - scalar_to_pornin(b)).encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_scalar_mul_matches_pornin(a in arb_scalar(), b in arb_scalar()) {
let ours = (a * b).to_le_bytes();
let theirs = (scalar_to_pornin(a) * scalar_to_pornin(b)).encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_scalar_neg_matches_pornin(a in arb_scalar()) {
let ours = (-a).to_le_bytes();
let theirs = (-scalar_to_pornin(a)).encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_point_decode_matches_pornin(w in arb_fp5()) {
let ours = Point::decode(w);
let pornin_input = fp5_to_pornin(w);
let (theirs_pt, theirs_ok) = PorninPoint::decode(pornin_input);
match (ours, theirs_ok) {
(Some(ours_pt), u64::MAX) => {
prop_assert_eq!(ours_pt.encode().to_le_bytes(), theirs_pt.encode().encode());
}
(None, 0) => {} (ours_state, theirs_mask) => {
prop_assert!(
false,
"decode disagreement: ours={:?} theirs_ok={:#x}",
ours_state.is_some(),
theirs_mask,
);
}
}
}
#[rstest]
fn prop_point_mulgen_matches_pornin(s in arb_scalar()) {
let ours = (Point::GENERATOR * s).encode().to_le_bytes();
let theirs = PorninPoint::mulgen(scalar_to_pornin(s)).encode().encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_point_mulgen_var_matches_pornin(s in arb_scalar()) {
let ours = Point::mulgen(s).encode().to_le_bytes();
let theirs = PorninPoint::mulgen(scalar_to_pornin(s)).encode().encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_point_mulgen_ct_matches_pornin(s in arb_scalar()) {
let ours = Point::mulgen_ct(s).encode().to_le_bytes();
let theirs = PorninPoint::mulgen(scalar_to_pornin(s)).encode().encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_point_double_matches_pornin(seed in arb_scalar()) {
let ours_base = Point::GENERATOR * seed;
let theirs_base = PorninPoint::mulgen(scalar_to_pornin(seed));
let ours = ours_base.double().encode().to_le_bytes();
let theirs = theirs_base.double().encode().encode();
prop_assert_eq!(ours, theirs);
}
#[rstest]
fn prop_point_add_matches_pornin(s1 in arb_scalar(), s2 in arb_scalar()) {
let ours_p1 = Point::GENERATOR * s1;
let ours_p2 = Point::GENERATOR * s2;
let theirs_p1 = PorninPoint::mulgen(scalar_to_pornin(s1));
let theirs_p2 = PorninPoint::mulgen(scalar_to_pornin(s2));
let ours = (ours_p1 + ours_p2).encode().to_le_bytes();
let theirs = (theirs_p1 + theirs_p2).encode().encode();
prop_assert_eq!(ours, theirs);
}
}
proptest! {
#![proptest_config(ProptestConfig {
cases: 64,
..ProptestConfig::default()
})]
#[rstest]
fn prop_scalar_mul_on_arbitrary_base_matches_pornin(
seed in arb_scalar_nonzero(),
s in arb_scalar(),
) {
let ours_base = Point::GENERATOR * seed;
let theirs_base = PorninPoint::mulgen(scalar_to_pornin(seed));
let ours = (ours_base * s).encode().to_le_bytes();
let theirs = (theirs_base * scalar_to_pornin(s)).encode().encode();
prop_assert_eq!(ours, theirs);
}
}