diff --git a/src/bn256/curve.rs b/src/bn256/curve.rs index 6f0d3e67..4549183b 100644 --- a/src/bn256/curve.rs +++ b/src/bn256/curve.rs @@ -199,23 +199,27 @@ impl G1 { } #[cfg(test)] -mod tests { - use crate::arithmetic::CurveEndo; - use crate::bn256::{Fr, G1, G2}; - use crate::CurveExt; - use ff::Field; - use ff::{PrimeField, WithSmallOrderMulGroup}; - use rand_core::OsRng; - - #[test] - fn test_hash_to_curve() { - crate::tests::curve::hash_to_curve_test::(); - } - - #[test] - fn test_map_to_curve() { - crate::tests::curve::svdw_map_to_curve_test::( - G1::SVDW_Z, +mod test { + use super::*; + crate::curve_testing_suite!(G1, G2); + crate::curve_testing_suite!(G1, "hash_to_curve"); + crate::curve_testing_suite!(G1, "endo_consistency"); + crate::curve_testing_suite!( + G1, + "endo", + // Optional `z_other` param. `z_other` is 3-roots of unity, similar to `ZETA`. + // Reference: https://github.com/privacy-scaling-explorations/halo2curves/blob/main/src/bn256/fr.rs#L145-L151 + [ + 0x8b17ea66b99c90dd, + 0x5bfc41088d8daaa7, + 0xb3c4d79d41a91758, + 0x00, + ] + ); + crate::curve_testing_suite!( + G1, + "svdw_map_to_curve", + ( // Precomputed constants taken from https://github.com/ConsenSys/gnark-crypto/blob/441dc0ffe639294b8d09e394f24ba7575577229c/internal/generator/config/bn254.go#L26-L32. [ "4", @@ -260,55 +264,27 @@ mod tests { "0x1ac201a542feca15e77f30370da183514dc99d8a0b2c136d64ede35cd0b51dc0", ), ), - ], - ); - } - - #[test] - fn test_curve() { - crate::tests::curve::curve_tests::(); - crate::tests::curve::curve_tests::(); - } - - #[test] - fn test_endo() { - let z_impl = Fr::ZETA; - let z_other = Fr::from_raw([ - 0x8b17ea66b99c90dd, - 0x5bfc41088d8daaa7, - 0xb3c4d79d41a91758, - 0x00, - ]); - assert_eq!(z_impl * z_impl + z_impl, -Fr::ONE); - assert_eq!(z_other * z_other + z_other, -Fr::ONE); - - let g = G1::generator(); - assert_eq!(g * Fr::ZETA, g.endo()); - let g = G2::generator(); - assert_eq!(g * Fr::ZETA, g.endo()); - for _ in 0..100000 { - let k = Fr::random(OsRng); - let (k1, k1_neg, k2, k2_neg) = G1::decompose_scalar(&k); - if k1_neg & k2_neg { - assert_eq!(k, -Fr::from_u128(k1) + Fr::ZETA * Fr::from_u128(k2)) - } else if k1_neg { - assert_eq!(k, -Fr::from_u128(k1) - Fr::ZETA * Fr::from_u128(k2)) - } else if k2_neg { - assert_eq!(k, Fr::from_u128(k1) + Fr::ZETA * Fr::from_u128(k2)) - } else { - assert_eq!(k, Fr::from_u128(k1) - Fr::ZETA * Fr::from_u128(k2)) - } - } - } - - #[test] - fn test_serialization() { - crate::tests::curve::random_serialization_test::(); - crate::tests::curve::random_serialization_test::(); - #[cfg(feature = "derive_serde")] - { - crate::tests::curve::random_serde_test::(); - crate::tests::curve::random_serde_test::(); - } - } + ] + ) + ); + crate::curve_testing_suite!( + G1, + "constants", + Fq::MODULUS, + G1_A, + G1_B, + G1_GENERATOR_X, + G1_GENERATOR_Y, + Fr::MODULUS + ); + crate::curve_testing_suite!( + G2, + "constants", + Fq2::MODULUS, + G2_A, + G2_B, + G2_GENERATOR_X, + G2_GENERATOR_Y, + Fr::MODULUS + ); } diff --git a/src/grumpkin/curve.rs b/src/grumpkin/curve.rs index 50e23bd0..f0ddf86a 100644 --- a/src/grumpkin/curve.rs +++ b/src/grumpkin/curve.rs @@ -90,58 +90,19 @@ impl G1 { } #[cfg(test)] -mod tests { - use crate::arithmetic::CurveEndo; - use crate::grumpkin::{Fr, G1}; - use crate::CurveExt; - use ff::{Field, PrimeField, WithSmallOrderMulGroup}; - use rand_core::OsRng; - - #[test] - fn test_hash_to_curve() { - crate::tests::curve::hash_to_curve_test::(); - } - - #[test] - fn test_curve() { - crate::tests::curve::curve_tests::(); - } - - #[test] - fn test_endo() { - let z_impl = Fr::ZETA; - let z_other = Fr::from_raw([ - 0xe4bd44e5607cfd48, - 0xc28f069fbb966e3d, - 0x5e6dd9e7e0acccb0, - 0x30644e72e131a029, - ]); - - assert_eq!(z_impl * z_impl + z_impl, -Fr::ONE); - assert_eq!(z_other * z_other + z_other, -Fr::ONE); - - let g = G1::generator(); - assert_eq!(g * Fr::ZETA, g.endo()); - - for _ in 0..100000 { - let k = Fr::random(OsRng); - let (k1, k1_neg, k2, k2_neg) = G1::decompose_scalar(&k); - if k1_neg & k2_neg { - assert_eq!(k, -Fr::from_u128(k1) + Fr::ZETA * Fr::from_u128(k2)) - } else if k1_neg { - assert_eq!(k, -Fr::from_u128(k1) - Fr::ZETA * Fr::from_u128(k2)) - } else if k2_neg { - assert_eq!(k, Fr::from_u128(k1) + Fr::ZETA * Fr::from_u128(k2)) - } else { - assert_eq!(k, Fr::from_u128(k1) - Fr::ZETA * Fr::from_u128(k2)) - } - } - } - - #[test] - fn test_serialization() { - crate::tests::curve::random_serialization_test::(); - #[cfg(feature = "derive_serde")] - crate::tests::curve::random_serde_test::(); - } +mod test { + use super::*; + crate::curve_testing_suite!(G1); + crate::curve_testing_suite!(G1, "endo_consistency"); + crate::curve_testing_suite!(G1, "endo"); + crate::curve_testing_suite!( + G1, + "constants", + Fq::MODULUS, + G1_A, + G1_B, + G1_GENERATOR_X, + G1_GENERATOR_Y, + Fr::MODULUS + ); } diff --git a/src/pluto_eris/curve.rs b/src/pluto_eris/curve.rs index 95b61adb..9b86c69c 100644 --- a/src/pluto_eris/curve.rs +++ b/src/pluto_eris/curve.rs @@ -242,46 +242,40 @@ new_curve_impl!( |_, _| unimplemented!(), ); -#[test] -fn test_curve_pluto() { - crate::tests::curve::curve_tests::(); -} -#[test] -fn test_curve_eris() { - crate::tests::curve::curve_tests::(); -} -#[test] -fn test_curve_triton() { - crate::tests::curve::curve_tests::(); -} - -#[test] -fn test_serialization() { - crate::tests::curve::random_serialization_test::(); - crate::tests::curve::random_serialization_test::(); - crate::tests::curve::random_serialization_test::(); - #[cfg(feature = "derive_serde")] - crate::tests::curve::random_serde_test::(); - #[cfg(feature = "derive_serde")] - crate::tests::curve::random_serde_test::(); - #[cfg(feature = "derive_serde")] - crate::tests::curve::random_serde_test::(); -} - -#[test] -fn test_hash_to_curve() { - crate::tests::curve::hash_to_curve_test::(); - crate::tests::curve::hash_to_curve_test::(); -} - -#[test] -fn test_endo_consistency() { - let g = Eris::generator(); - assert_eq!(g * Fp::ZETA, g.endo()); - - let g = G1::generator(); - assert_eq!(g * Fq::ZETA, g.endo()); - - let g = G2::generator(); - assert_eq!(g * Fq::ZETA, g.endo()); +#[cfg(test)] +mod test { + use super::*; + crate::curve_testing_suite!(G1, Eris, G2); + crate::curve_testing_suite!(G1, Eris, "hash_to_curve"); + crate::curve_testing_suite!(G1, Eris, "endo_consistency"); + crate::curve_testing_suite!( + G1, + "constants", + Fp::MODULUS, + PLUTO_A, + PLUTO_B, + G1_GENERATOR_X, + G1_GENERATOR_Y, + Fq::MODULUS + ); + crate::curve_testing_suite!( + Eris, + "constants", + Fq::MODULUS, + ERIS_A, + ERIS_B, + ERIS_GENERATOR_X, + ERIS_GENERATOR_Y, + Fp::MODULUS + ); + crate::curve_testing_suite!( + G2, + "constants", + Fp2::MODULUS, + TRITON_A, + TRITON_B, + G2_GENERATOR_X, + G2_GENERATOR_Y, + Fq::MODULUS + ); } diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index 23e1946e..a8bf5bca 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -269,89 +269,19 @@ pub(crate) fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { } #[cfg(test)] -mod tests { +mod test { use super::*; - - #[test] - fn test_curve() { - crate::tests::curve::curve_tests::(); - } - - #[test] - fn test_hash_to_curve() { - crate::tests::curve::hash_to_curve_test::(); - } - - #[test] - fn test_serialization() { - crate::tests::curve::random_serialization_test::(); - #[cfg(feature = "derive_serde")] - crate::tests::curve::random_serde_test::(); - } - - #[test] - fn test_endo_consistency() { - let g = Secp256k1::generator(); - assert_eq!(g * Fq::ZETA, g.endo()); - } - - #[test] - fn ecdsa_example() { - use crate::group::Curve; - use crate::CurveAffine; - use ff::FromUniformBytes; - use rand_core::OsRng; - - fn mod_n(x: Fp) -> Fq { - let mut x_repr = [0u8; 32]; - x_repr.copy_from_slice(x.to_repr().as_ref()); - let mut x_bytes = [0u8; 64]; - x_bytes[..32].copy_from_slice(&x_repr[..]); - Fq::from_uniform_bytes(&x_bytes) - } - - let g = Secp256k1::generator(); - - for _ in 0..1000 { - // Generate a key pair - let sk = Fq::random(OsRng); - let pk = (g * sk).to_affine(); - - // Generate a valid signature - // Suppose `m_hash` is the message hash - let msg_hash = Fq::random(OsRng); - - let (r, s) = { - // Draw arandomness - let k = Fq::random(OsRng); - let k_inv = k.invert().unwrap(); - - // Calculate `r` - let r_point = (g * k).to_affine().coordinates().unwrap(); - let x = r_point.x(); - let r = mod_n(*x); - - // Calculate `s` - let s = k_inv * (msg_hash + (r * sk)); - - (r, s) - }; - - { - // Verify - let s_inv = s.invert().unwrap(); - let u_1 = msg_hash * s_inv; - let u_2 = r * s_inv; - - let v_1 = g * u_1; - let v_2 = pk * u_2; - - let r_point = (v_1 + v_2).to_affine().coordinates().unwrap(); - let x_candidate = r_point.x(); - let r_candidate = mod_n(*x_candidate); - - assert_eq!(r, r_candidate); - } - } - } + crate::curve_testing_suite!(Secp256k1); + crate::curve_testing_suite!(Secp256k1, "endo_consistency"); + crate::curve_testing_suite!(Secp256k1, "ecdsa_example"); + crate::curve_testing_suite!( + Secp256k1, + "constants", + Fp::MODULUS, + SECP_A, + SECP_B, + SECP_GENERATOR_X, + SECP_GENERATOR_Y, + Fq::MODULUS + ); } diff --git a/src/secp256r1/curve.rs b/src/secp256r1/curve.rs index e592d82b..fbd1c538 100644 --- a/src/secp256r1/curve.rs +++ b/src/secp256r1/curve.rs @@ -92,82 +92,18 @@ impl Secp256r1 { } #[cfg(test)] -mod tests { +mod test { use super::*; - use crate::group::Curve; - use crate::secp256r1::{Fp, Fq, Secp256r1}; - use ff::FromUniformBytes; - use rand_core::OsRng; - - #[test] - fn test_hash_to_curve() { - crate::tests::curve::hash_to_curve_test::(); - } - - #[test] - fn test_curve() { - crate::tests::curve::curve_tests::(); - } - - #[test] - fn test_serialization() { - crate::tests::curve::random_serialization_test::(); - #[cfg(feature = "derive_serde")] - crate::tests::curve::random_serde_test::(); - } - - #[test] - fn ecdsa_example() { - fn mod_n(x: Fp) -> Fq { - let mut x_repr = [0u8; 32]; - x_repr.copy_from_slice(x.to_repr().as_ref()); - let mut x_bytes = [0u8; 64]; - x_bytes[..32].copy_from_slice(&x_repr[..]); - Fq::from_uniform_bytes(&x_bytes) - } - - let g = Secp256r1::generator(); - - for _ in 0..1000 { - // Generate a key pair - let sk = Fq::random(OsRng); - let pk = (g * sk).to_affine(); - - // Generate a valid signature - // Suppose `m_hash` is the message hash - let msg_hash = Fq::random(OsRng); - - let (r, s) = { - // Draw arandomness - let k = Fq::random(OsRng); - let k_inv = k.invert().unwrap(); - - // Calculate `r` - let r_point = (g * k).to_affine().coordinates().unwrap(); - let x = r_point.x(); - let r = mod_n(*x); - - // Calculate `s` - let s = k_inv * (msg_hash + (r * sk)); - - (r, s) - }; - - { - // Verify - let s_inv = s.invert().unwrap(); - let u_1 = msg_hash * s_inv; - let u_2 = r * s_inv; - - let v_1 = g * u_1; - let v_2 = pk * u_2; - - let r_point = (v_1 + v_2).to_affine().coordinates().unwrap(); - let x_candidate = r_point.x(); - let r_candidate = mod_n(*x_candidate); - - assert_eq!(r, r_candidate); - } - } - } + crate::curve_testing_suite!(Secp256r1); + crate::curve_testing_suite!(Secp256r1, "ecdsa_example"); + crate::curve_testing_suite!( + Secp256r1, + "constants", + Fp::MODULUS, + SECP_A, + SECP_B, + SECP_GENERATOR_X, + SECP_GENERATOR_Y, + Fq::MODULUS + ); } diff --git a/src/secq256k1/curve.rs b/src/secq256k1/curve.rs index cc605fa4..726eae8a 100644 --- a/src/secq256k1/curve.rs +++ b/src/secq256k1/curve.rs @@ -72,33 +72,18 @@ impl Secq256k1 { } #[cfg(test)] -mod tests { - use crate::secq256k1::Fq; - use crate::CurveExt; - use ff::WithSmallOrderMulGroup; - - use super::Secq256k1; - - #[test] - fn test_hash_to_curve() { - crate::tests::curve::hash_to_curve_test::(); - } - - #[test] - fn test_curve() { - crate::tests::curve::curve_tests::(); - } - - #[test] - fn test_endo_consistency() { - let g = Secq256k1::generator(); - assert_eq!(g * Fq::ZETA, g.endo()); - } - - #[test] - fn test_serialization() { - crate::tests::curve::random_serialization_test::(); - #[cfg(feature = "derive_serde")] - crate::tests::curve::random_serde_test::(); - } +mod test { + use super::*; + crate::curve_testing_suite!(Secq256k1); + crate::curve_testing_suite!(Secq256k1, "endo_consistency"); + crate::curve_testing_suite!( + Secq256k1, + "constants", + Fq::MODULUS, + SECQ_A, + SECQ_B, + SECQ_GENERATOR_X, + SECQ_GENERATOR_Y, + Fp::MODULUS + ); } diff --git a/src/tests/curve.rs b/src/tests/curve.rs index 9554e875..86bed834 100644 --- a/src/tests/curve.rs +++ b/src/tests/curve.rs @@ -1,383 +1,545 @@ #![allow(clippy::eq_op)] -use crate::ff::Field; -use crate::ff_ext::Legendre; -use crate::group::prime::PrimeCurveAffine; -use crate::{group::GroupEncoding, serde::SerdeObject}; -use crate::{hash_to_curve, CurveAffine, CurveExt}; -use ff::PrimeField; -use num_bigint::BigUint; -use num_traits::Num; -use rand_core::{OsRng, RngCore}; -use std::borrow::Cow; -use std::iter; - -#[cfg(feature = "derive_serde")] -use serde::{Deserialize, Serialize}; - -pub fn curve_tests() { - is_on_curve::(); - equality::(); - projective_to_affine_affine_to_projective::(); - projective_addition::(); - mixed_addition::(); - multiplication::(); - batch_normalize::(); - serdes::(); -} +#[macro_export] +macro_rules! curve_testing_suite { + ($($curve: ident),*) => { + macro_rules! is_on_curve { + ($c: ident) => { + assert!(bool::from($c::identity().is_on_curve())); + assert!(bool::from($c::generator().is_on_curve())); + + for _ in 0..100 { + let point = $c::random(OsRng); + assert!(bool::from(point.is_on_curve())); + let affine_point: <$c as CurveExt>::AffineExt = point.into(); + assert!(bool::from(affine_point.is_on_curve())); + } + } + } -fn serdes() { - assert!(bool::from( - G::from_bytes(&G::identity().to_bytes()) - .unwrap() - .is_identity() - )); - assert!(bool::from( - G::AffineExt::from_bytes(&G::AffineExt::identity().to_bytes()) - .unwrap() - .is_identity() - )); - for _ in 0..100 { - let projective_point = G::random(OsRng); - let affine_point: G::AffineExt = projective_point.into(); - let projective_repr = projective_point.to_bytes(); - let affine_repr = affine_point.to_bytes(); - - println!( - "{:?} \n{:?}", - projective_repr.as_ref(), - affine_repr.as_ref() - ); - - let projective_point_rec = G::from_bytes(&projective_repr).unwrap(); - let projective_point_rec_unchecked = G::from_bytes(&projective_repr).unwrap(); - let affine_point_rec = G::AffineExt::from_bytes(&affine_repr).unwrap(); - let affine_point_rec_unchecked = G::AffineExt::from_bytes(&affine_repr).unwrap(); - - assert_eq!(projective_point, projective_point_rec); - assert_eq!(projective_point, projective_point_rec_unchecked); - assert_eq!(affine_point, affine_point_rec); - assert_eq!(affine_point, affine_point_rec_unchecked); - } -} + macro_rules! equality { + ($c: ident) => { + let a = $c::generator(); + let b = $c::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); -#[cfg(feature = "derive_serde")] -pub fn random_serde_test() -where - G: SerdeObject + CurveExt + Serialize + for<'de> Deserialize<'de>, - G::AffineExt: SerdeObject + Serialize + for<'de> Deserialize<'de>, -{ - for _ in 0..100 { - let projective_point = G::random(OsRng); - let affine_point: G::AffineExt = projective_point.into(); - { - let affine_bytes = bincode::serialize(&affine_point).unwrap(); - let reader = std::io::Cursor::new(affine_bytes); - let affine_point_rec: G::AffineExt = bincode::deserialize_from(reader).unwrap(); - assert_eq!(projective_point.to_affine(), affine_point_rec); - assert_eq!(affine_point, affine_point_rec); + for _ in 0..100 { + let a = $c::random(OsRng); + let b = $c::random(OsRng); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + + let a: <$c as CurveExt>::AffineExt = a.into(); + let b: <$c as CurveExt>::AffineExt = b.into(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + } + } } - { - let affine_json = serde_json::to_string(&affine_point).unwrap(); - let reader = std::io::Cursor::new(affine_json); - let affine_point_rec: G::AffineExt = serde_json::from_reader(reader).unwrap(); - assert_eq!(affine_point, affine_point_rec); + + macro_rules! projective_affine_roundtrip { + ($c: ident) => { + let a = $c::generator(); + let b = $c::identity(); + + assert!(bool::from(<$c as CurveExt>::AffineExt::from(a).is_on_curve())); + assert!(!bool::from(<$c as CurveExt>::AffineExt::from(a).is_identity())); + assert!(bool::from(<$c as CurveExt>::AffineExt::from(b).is_on_curve())); + assert!(bool::from(<$c as CurveExt>::AffineExt::from(b).is_identity())); + + let a = <$c as CurveExt>::AffineExt::generator(); + let b = <$c as CurveExt>::AffineExt::identity(); + + assert!(bool::from($c::from(a).is_on_curve())); + assert!(!bool::from($c::from(a).is_identity())); + assert!(bool::from($c::from(b).is_on_curve())); + assert!(bool::from($c::from(b).is_identity())); + } } - { - let projective_bytes = bincode::serialize(&projective_point).unwrap(); - let reader = std::io::Cursor::new(projective_bytes); - let projective_point_rec: G = bincode::deserialize_from(reader).unwrap(); - assert_eq!(projective_point, projective_point_rec); + + macro_rules! projective_addition { + ($c: ident) => { + let a = $c::identity(); + let b = $c::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + let c = a - b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + + let a = $c::identity(); + let a = -a; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(a.is_identity())); + + let a = $c::random(OsRng); + assert!(a == a + $c::identity()); + assert!(a == $c::identity() + a); + assert!(-a == $c::identity() - a); + + let a = $c::identity(); + let a = a.double(); + assert!(bool::from(c.is_on_curve())); + assert!(bool::from(a.is_identity())); + + let a = $c::generator(); + let a = a.double(); + assert!(bool::from(c.is_on_curve())); + assert_eq!(a, $c::generator() + $c::generator()); + + let a = $c::random(OsRng); + assert!(a.double() - a == a); + + let a = $c::random(OsRng); + let b = $c::random(OsRng); + let c = $c::random(OsRng); + assert!(a + b == b + a); + assert!(a - b == -(b - a)); + assert!(c + (a + b) == a + (c + b)); + assert!((a - b) - c == (a - c) - b); + + let a = $c::generator().double().double(); // 4P + let b = $c::generator().double(); // 2P + let c = a + b; + + let mut d = $c::generator(); + for _ in 0..5 { + d += $c::generator(); + } + + assert!(c == d); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + } } - { - let projective_json = serde_json::to_string(&projective_point).unwrap(); - let reader = std::io::Cursor::new(projective_json); - let projective_point_rec: G = serde_json::from_reader(reader).unwrap(); - assert_eq!(projective_point, projective_point_rec); + + macro_rules! mixed_addition { + ($c: ident) => { + let a = $c::identity(); + let b = <$c as group::Curve>::AffineRepr::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + let c = a - b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + + let a = $c::identity(); + let a = -a; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(a.is_identity())); + let a = <$c as CurveExt>::AffineExt::identity(); + let a = -a; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(a.is_identity())); + + let a: <$c as CurveExt>::AffineExt = $c::random(OsRng).into(); + assert!(a.to_curve() == a + <$c as CurveExt>::AffineExt::identity()); + + let a = $c::random(OsRng); + assert!(a.double() - a == a); + + let a = $c::random(OsRng); + let b: <$c as CurveExt>::AffineExt = $c::random(OsRng).into(); + let c0 = a + b; + let c1 = a + $c::from(b); + assert_eq!(c0, c1); + } } - } -} -pub fn random_serialization_test() -where - G: SerdeObject, - G::AffineExt: SerdeObject, -{ - for _ in 0..100 { - let projective_point = G::random(OsRng); - let affine_point: G::AffineExt = projective_point.into(); - - let projective_bytes = projective_point.to_raw_bytes(); - let projective_point_rec = G::from_raw_bytes(&projective_bytes).unwrap(); - assert_eq!(projective_point, projective_point_rec); - let mut buf = Vec::new(); - projective_point.write_raw(&mut buf).unwrap(); - let projective_point_rec = G::read_raw(&mut &buf[..]).unwrap(); - assert_eq!(projective_point, projective_point_rec); - - let affine_bytes = affine_point.to_raw_bytes(); - let affine_point_rec = G::AffineExt::from_raw_bytes(&affine_bytes).unwrap(); - assert_eq!(affine_point, affine_point_rec); - let mut buf = Vec::new(); - affine_point.write_raw(&mut buf).unwrap(); - let affine_point_rec = G::AffineExt::read_raw(&mut &buf[..]).unwrap(); - assert_eq!(affine_point, affine_point_rec); - } -} + macro_rules! multiplication { + ($c: ident) => { + for _ in 1..1000 { + let s1 = <$c as CurveExt>::ScalarExt::random(OsRng); + let s2 = <$c as CurveExt>::ScalarExt::random(OsRng); -fn is_on_curve() { - assert!(bool::from(G::identity().is_on_curve())); - assert!(bool::from(G::generator().is_on_curve())); - assert!(bool::from(G::identity().is_on_curve())); - assert!(bool::from(G::generator().is_on_curve())); - - for _ in 0..100 { - let point = G::random(OsRng); - assert!(bool::from(point.is_on_curve())); - let affine_point: G::AffineExt = point.into(); - assert!(bool::from(affine_point.is_on_curve())); - } -} + let t0 = $c::identity() * s1; + assert!(bool::from(t0.is_identity())); -fn equality() { - let a = G::generator(); - let b = G::identity(); + let a = $c::random(OsRng); + let t0 = a * <$c as CurveExt>::ScalarExt::ONE; + assert_eq!(a, t0); - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); + let t0 = a * <$c as CurveExt>::ScalarExt::ZERO; + assert!(bool::from(t0.is_identity())); - for _ in 0..100 { - let a = G::random(OsRng); - let b = G::random(OsRng); + let t0 = a * s1 + a * s2; - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); + let s3 = s1 + s2; + let t1 = a * s3; - let a: G::AffineExt = a.into(); - let b: G::AffineExt = b.into(); + assert_eq!(t0, t1); - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); - } -} + let mut t0 = a * s1; + let mut t1 = a * s2; + t0 += t1; + let s3 = s1 + s2; + t1 = a * s3; + assert_eq!(t0, t1); + } + } + } -fn projective_to_affine_affine_to_projective() { - let a = G::generator(); - let b = G::identity(); + macro_rules! batch_normalize { + ($c: ident) => { + let a = $c::generator().double(); + let b = a.double(); + let c = b.double(); + + for a_identity in (0..1).map(|n| n == 1) { + for b_identity in (0..1).map(|n| n == 1) { + for c_identity in (0..1).map(|n| n == 1) { + let mut v = [a, b, c]; + if a_identity { + v[0] = $c::identity() + } + if b_identity { + v[1] = $c::identity() + } + if c_identity { + v[2] = $c::identity() + } + + let mut t = [ + <$c as CurveExt>::AffineExt::identity(), + <$c as CurveExt>::AffineExt::identity(), + <$c as CurveExt>::AffineExt::identity(), + ]; + let expected = [ + <$c as CurveExt>::AffineExt::from(v[0]), + <$c as CurveExt>::AffineExt::from(v[1]), + <$c as CurveExt>::AffineExt::from(v[2]), + ]; + + $c::batch_normalize(&v[..], &mut t[..]); + + assert_eq!(&t[..], &expected[..]); + } + } + } + } + } - assert!(bool::from(G::AffineExt::from(a).is_on_curve())); - assert!(!bool::from(G::AffineExt::from(a).is_identity())); - assert!(bool::from(G::AffineExt::from(b).is_on_curve())); - assert!(bool::from(G::AffineExt::from(b).is_identity())); + macro_rules! serdes { + ($c: ident) => { + assert!(bool::from( + $c::from_bytes(&$c::identity().to_bytes()) + .unwrap() + .is_identity() + )); + assert!(bool::from( + <$c as CurveExt>::AffineExt::from_bytes(&<$c as CurveExt>::AffineExt::identity().to_bytes()) + .unwrap() + .is_identity() + )); + for _ in 0..100 { + let projective_point = $c::random(OsRng); + let affine_point: <$c as CurveExt>::AffineExt = projective_point.into(); + let projective_repr = projective_point.to_bytes(); + let affine_repr = affine_point.to_bytes(); + let projective_point_rec = $c::from_bytes(&projective_repr).unwrap(); + let projective_point_rec_unchecked = $c::from_bytes(&projective_repr).unwrap(); + let affine_point_rec = <$c as CurveExt>::AffineExt::from_bytes(&affine_repr).unwrap(); + let affine_point_rec_unchecked = <$c as CurveExt>::AffineExt::from_bytes(&affine_repr).unwrap(); + + assert_eq!(projective_point, projective_point_rec); + assert_eq!(projective_point, projective_point_rec_unchecked); + assert_eq!(affine_point, affine_point_rec); + assert_eq!(affine_point, affine_point_rec_unchecked); + } + } + } - let a = G::AffineExt::generator(); - let b = G::AffineExt::identity(); + macro_rules! random_serialization_test { + ($c: ident) => { + for _ in 0..100 { + let projective_point = $c::random(OsRng); + let affine_point: <$c as CurveExt>::AffineExt = projective_point.into(); + + let projective_bytes = projective_point.to_raw_bytes(); + let projective_point_rec = $c::from_raw_bytes(&projective_bytes).unwrap(); + assert_eq!(projective_point, projective_point_rec); + let mut buf = Vec::new(); + projective_point.write_raw(&mut buf).unwrap(); + let projective_point_rec = $c::read_raw(&mut &buf[..]).unwrap(); + assert_eq!(projective_point, projective_point_rec); + + let affine_bytes = affine_point.to_raw_bytes(); + let affine_point_rec = <$c as CurveExt>::AffineExt::from_raw_bytes(&affine_bytes).unwrap(); + assert_eq!(affine_point, affine_point_rec); + let mut buf = Vec::new(); + affine_point.write_raw(&mut buf).unwrap(); + let affine_point_rec = <$c as CurveExt>::AffineExt::read_raw(&mut &buf[..]).unwrap(); + assert_eq!(affine_point, affine_point_rec); + } + } + } - assert!(bool::from(G::from(a).is_on_curve())); - assert!(!bool::from(G::from(a).is_identity())); - assert!(bool::from(G::from(b).is_on_curve())); - assert!(bool::from(G::from(b).is_identity())); -} + #[cfg(feature = "derive_serde")] + macro_rules! random_serde_test { + ($c: ident) => { + for _ in 0..100 { + let projective_point = $c::random(OsRng); + let affine_point: <$c as CurveExt>::AffineExt = projective_point.into(); + { + let affine_bytes = bincode::serialize(&affine_point).unwrap(); + let reader = std::io::Cursor::new(affine_bytes); + let affine_point_rec: <$c as CurveExt>::AffineExt = bincode::deserialize_from(reader).unwrap(); + assert_eq!(projective_point.to_affine(), affine_point_rec); + assert_eq!(affine_point, affine_point_rec); + } + { + let affine_json = serde_json::to_string(&affine_point).unwrap(); + let reader = std::io::Cursor::new(affine_json); + let affine_point_rec: <$c as CurveExt>::AffineExt = serde_json::from_reader(reader).unwrap(); + assert_eq!(affine_point, affine_point_rec); + } + { + let projective_bytes = bincode::serialize(&projective_point).unwrap(); + let reader = std::io::Cursor::new(projective_bytes); + let projective_point_rec: $c = bincode::deserialize_from(reader).unwrap(); + assert_eq!(projective_point, projective_point_rec); + } + { + let projective_json = serde_json::to_string(&projective_point).unwrap(); + let reader = std::io::Cursor::new(projective_json); + let projective_point_rec: $c = serde_json::from_reader(reader).unwrap(); + assert_eq!(projective_point, projective_point_rec); + } + } + } + } -fn projective_addition() { - let a = G::identity(); - let b = G::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - let c = a - b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - - let a = G::identity(); - let a = -a; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(a.is_identity())); - - let a = G::random(OsRng); - assert!(a == a + G::identity()); - assert!(a == G::identity() + a); - assert!(-a == G::identity() - a); - - let a = G::identity(); - let a = a.double(); - assert!(bool::from(c.is_on_curve())); - assert!(bool::from(a.is_identity())); - - let a = G::generator(); - let a = a.double(); - assert!(bool::from(c.is_on_curve())); - assert_eq!(a, G::generator() + G::generator()); - - let a = G::random(OsRng); - assert!(a.double() - a == a); - - let a = G::random(OsRng); - let b = G::random(OsRng); - let c = G::random(OsRng); - assert!(a + b == b + a); - assert!(a - b == -(b - a)); - assert!(c + (a + b) == a + (c + b)); - assert!((a - b) - c == (a - c) - b); - - let a = G::generator().double().double(); // 4P - let b = G::generator().double(); // 2P - let c = a + b; - - let mut d = G::generator(); - for _ in 0..5 { - d += G::generator(); - } - - assert!(c == d); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); -} + use crate::ff::Field; + use crate::group::prime::PrimeCurveAffine; + use crate::{group::GroupEncoding, serde::SerdeObject}; + use crate::{CurveAffine, CurveExt}; + use rand_core::OsRng; + + #[test] + fn test_curve() { + $( + is_on_curve!($curve); + equality!($curve); + projective_affine_roundtrip!($curve); + projective_addition!($curve); + mixed_addition!($curve); + multiplication!($curve); + batch_normalize!($curve); + serdes!($curve); + )* + } -fn mixed_addition() { - let a = G::identity(); - let b = G::AffineRepr::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - let c = a - b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - - let a = G::identity(); - let a = -a; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(a.is_identity())); - let a = G::AffineExt::identity(); - let a = -a; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(a.is_identity())); - - let a: G::AffineExt = G::random(OsRng).into(); - assert!(a.to_curve() == a + G::AffineExt::identity()); - - let a = G::random(OsRng); - assert!(a.double() - a == a); - - let a = G::random(OsRng); - let b: G::AffineExt = G::random(OsRng).into(); - let c0 = a + b; - let c1 = a + G::from(b); - assert_eq!(c0, c1); -} + #[test] + fn test_serialization() { + $( + random_serialization_test!($curve); + #[cfg(feature = "derive_serde")] + random_serde_test!($curve); + )* + } + }; -fn batch_normalize() { - let a = G::generator().double(); - let b = a.double(); - let c = b.double(); - - for a_identity in (0..1).map(|n| n == 1) { - for b_identity in (0..1).map(|n| n == 1) { - for c_identity in (0..1).map(|n| n == 1) { - let mut v = [a, b, c]; - if a_identity { - v[0] = G::identity() - } - if b_identity { - v[1] = G::identity() - } - if c_identity { - v[2] = G::identity() + ($($curve: ident),*, "hash_to_curve") => { + macro_rules! hash_to_curve_test { + ($c: ident) => { + let hasher = $c::hash_to_curve("test"); + let mut rng = OsRng; + for _ in 0..1000 { + let message = iter::repeat_with(|| rng.next_u32().to_be_bytes()) + .take(32) + .flatten() + .collect::>(); + assert!(bool::from(hasher(&message).is_on_curve())); } + } + } - let mut t = [ - G::AffineExt::identity(), - G::AffineExt::identity(), - G::AffineExt::identity(), - ]; - let expected = [ - G::AffineExt::from(v[0]), - G::AffineExt::from(v[1]), - G::AffineExt::from(v[2]), - ]; + #[test] + fn test_hash_to_curve() { + use rand_core::{OsRng, RngCore}; + use std::iter; + $( + hash_to_curve_test!($curve); + )* + } + }; - G::batch_normalize(&v[..], &mut t[..]); + ($($curve: ident),*, "endo_consistency") => { + #[test] + fn test_endo_consistency() { + use rand_core::OsRng; + $( + let g = $curve::generator(); + assert_eq!(g * <$curve as CurveExt>::ScalarExt::ZETA, g.endo()); + + for _ in 0..100 { + let g = $curve::random(OsRng); + assert_eq!(g * <$curve as CurveExt>::ScalarExt::ZETA, g.endo()); + } + )* + } + }; - assert_eq!(&t[..], &expected[..]); + ($curve: ident, "endo" $(, $z_other_raw: expr)*) => { + #[test] + fn test_endo() { + use rand_core::OsRng; + + let z_impl = <$curve as CurveExt>::ScalarExt::ZETA; + assert_eq!(z_impl * z_impl + z_impl, -<$curve as CurveExt>::ScalarExt::ONE); + $( + let z_other = <$curve as CurveExt>::ScalarExt::from_raw($z_other_raw as [u64; 4]); + assert_eq!(z_other * z_other + z_other, -<$curve as CurveExt>::ScalarExt::ONE); + )* + + for _ in 0..100000 { + let k = <$curve as CurveExt>::ScalarExt::random(OsRng); + let (k1, k1_neg, k2, k2_neg) = $curve::decompose_scalar(&k); + if k1_neg & k2_neg { + assert_eq!(k, -<$curve as CurveExt>::ScalarExt::from_u128(k1) + <$curve as CurveExt>::ScalarExt::ZETA * <$curve as CurveExt>::ScalarExt::from_u128(k2)) + } else if k1_neg { + assert_eq!(k, -<$curve as CurveExt>::ScalarExt::from_u128(k1) - <$curve as CurveExt>::ScalarExt::ZETA * <$curve as CurveExt>::ScalarExt::from_u128(k2)) + } else if k2_neg { + assert_eq!(k, <$curve as CurveExt>::ScalarExt::from_u128(k1) + <$curve as CurveExt>::ScalarExt::ZETA * <$curve as CurveExt>::ScalarExt::from_u128(k2)) + } else { + assert_eq!(k, <$curve as CurveExt>::ScalarExt::from_u128(k1) - <$curve as CurveExt>::ScalarExt::ZETA * <$curve as CurveExt>::ScalarExt::from_u128(k2)) + } } } - } -} + }; -fn multiplication() { - for _ in 1..1000 { - let s1 = G::ScalarExt::random(OsRng); - let s2 = G::ScalarExt::random(OsRng); + ($curve: ident, "ecdsa_example") => { + #[test] + fn ecdsa_example() { + use ff::FromUniformBytes; + use rand_core::OsRng; + + fn mod_n(x: <$curve as CurveExt>::Base) -> <$curve as CurveExt>::ScalarExt { + let mut x_repr = [0u8; 32]; + x_repr.copy_from_slice(x.to_repr().as_ref()); + let mut x_bytes = [0u8; 64]; + x_bytes[..32].copy_from_slice(&x_repr[..]); + <$curve as CurveExt>::ScalarExt::from_uniform_bytes(&x_bytes) + } - let t0 = G::identity() * s1; - assert!(bool::from(t0.is_identity())); + let g = $curve::generator(); - let a = G::random(OsRng); - let t0 = a * G::ScalarExt::ONE; - assert_eq!(a, t0); + for _ in 0..1000 { + // Generate a key pair + let sk = <$curve as CurveExt>::ScalarExt::random(OsRng); + let pk = (g * sk).to_affine(); - let t0 = a * G::ScalarExt::ZERO; - assert!(bool::from(t0.is_identity())); + // Generate a valid signature + // Suppose `m_hash` is the message hash + let msg_hash = <$curve as CurveExt>::ScalarExt::random(OsRng); - let t0 = a * s1 + a * s2; + let (r, s) = { + // Draw arandomness + let k = <$curve as CurveExt>::ScalarExt::random(OsRng); + let k_inv = k.invert().unwrap(); - let s3 = s1 + s2; - let t1 = a * s3; + // Calculate `r` + let r_point = (g * k).to_affine().coordinates().unwrap(); + let x = r_point.x(); + let r = mod_n(*x); - assert_eq!(t0, t1); + // Calculate `s` + let s = k_inv * (msg_hash + (r * sk)); - let mut t0 = a * s1; - let mut t1 = a * s2; - t0 += t1; - let s3 = s1 + s2; - t1 = a * s3; - assert_eq!(t0, t1); - } -} + (r, s) + }; -pub fn hash_to_curve_test() { - let hasher = G::hash_to_curve("test"); - let mut rng = OsRng; - for _ in 0..1000 { - let message = iter::repeat_with(|| rng.next_u32().to_be_bytes()) - .take(32) - .flatten() - .collect::>(); - assert!(bool::from(hasher(&message).is_on_curve())); - } -} + { + // Verify + let s_inv = s.invert().unwrap(); + let u_1 = msg_hash * s_inv; + let u_2 = r * s_inv; + + let v_1 = g * u_1; + let v_2 = pk * u_2; + + let r_point = (v_1 + v_2).to_affine().coordinates().unwrap(); + let x_candidate = r_point.x(); + let r_candidate = mod_n(*x_candidate); -fn fe_from_str(string: impl AsRef) -> F { - let string = string.as_ref(); - let oct = if let Some(hex) = string.strip_prefix("0x") { - Cow::Owned(BigUint::from_str_radix(hex, 16).unwrap().to_string()) - } else { - Cow::Borrowed(string) + assert_eq!(r, r_candidate); + } + } + } + }; + + ($curve: ident, "svdw_map_to_curve", ($precomputed_constants: expr, $test_vector: expr)) => { + #[test] + fn test_map_to_curve() { + use crate::ff_ext::Legendre; + use crate::{hash_to_curve, CurveAffine, CurveExt}; + use ff::PrimeField; + use num_bigint::BigUint; + use num_traits::Num; + use std::borrow::Cow; + + fn fe_from_str(string: impl AsRef) -> F { + let string = string.as_ref(); + let oct = if let Some(hex) = string.strip_prefix("0x") { + Cow::Owned(BigUint::from_str_radix(hex, 16).unwrap().to_string()) + } else { + Cow::Borrowed(string) + }; + F::from_str_vartime(&oct).unwrap() + } + + fn svdw_map_to_curve_test( + z: G::Base, + precomputed_constants: [&'static str; 4], + test_vector: impl IntoIterator, + ) where + ::Base: Legendre, + { + let [c1, c2, c3, c4] = hash_to_curve::svdw_precomputed_constants::(z); + assert_eq!([c1, c2, c3, c4], precomputed_constants.map(fe_from_str)); + for (u, (x, y)) in test_vector.into_iter() { + let u = fe_from_str(u); + let expected = G::AffineExt::from_xy(fe_from_str(x), fe_from_str(y)).unwrap(); + let output = hash_to_curve::svdw_map_to_curve::(u, c1, c2, c3, c4, z).to_affine(); + assert_eq!(output, expected); + } + } + + svdw_map_to_curve_test::<$curve>($curve::SVDW_Z, $precomputed_constants, $test_vector); + } }; - F::from_str_vartime(&oct).unwrap() -} -pub fn svdw_map_to_curve_test( - z: G::Base, - precomputed_constants: [&'static str; 4], - test_vector: impl IntoIterator, -) where - ::Base: Legendre, -{ - let [c1, c2, c3, c4] = hash_to_curve::svdw_precomputed_constants::(z); - assert_eq!([c1, c2, c3, c4], precomputed_constants.map(fe_from_str)); - for (u, (x, y)) in test_vector.into_iter() { - let u = fe_from_str(u); - let expected = G::AffineExt::from_xy(fe_from_str(x), fe_from_str(y)).unwrap(); - let output = hash_to_curve::svdw_map_to_curve::(u, c1, c2, c3, c4, z).to_affine(); - assert_eq!(output, expected); - } + ($curve: ident, "constants", $p: expr, $a: expr, $b: expr, $gen_x: expr, $gen_y: expr, $order: expr) => { + #[test] + #[allow(non_snake_case)] + fn $curve() { + assert!($p == <$curve as CurveExt>::Base::MODULUS); + + let a = $curve::a(); + let b = $curve::b(); + assert!(a == $a); + assert!(b == $b); + + let generator = $curve::generator(); + let generator_affine: <$curve as CurveExt>::AffineExt = generator.into(); + assert!(generator_affine.x == $gen_x); + assert!(generator_affine.y == $gen_y); + + assert!($order == <$curve as CurveExt>::ScalarExt::MODULUS); + } + }; }