use rand_core::RngCore; use group::{ ff::{Field, PrimeFieldBits}, Group, prime::PrimeGroup, }; use crate::prime_field::{test_prime_field, test_prime_field_bits}; /// Test equality. pub fn test_eq() { assert_eq!(G::identity(), G::identity(), "identity != identity"); assert_eq!(G::generator(), G::generator(), "generator != generator"); assert!(G::identity() != G::generator(), "identity == generator"); } /// Test identity. pub fn test_identity() { assert!(bool::from(G::identity().is_identity()), "identity wasn't identity"); assert!( bool::from((G::identity() + G::identity()).is_identity()), "identity + identity wasn't identity" ); assert!( bool::from((G::generator() - G::generator()).is_identity()), "generator - generator wasn't identity" ); assert!(!bool::from(G::generator().is_identity()), "is_identity claimed generator was identity"); } /// Sanity check the generator. pub fn test_generator() { assert!(G::generator() != G::identity(), "generator was identity"); assert!( (G::generator() + G::generator()) != G::generator(), "generator added to itself was identity" ); } /// Test doubling of group elements. pub fn test_double() { assert!(bool::from(G::identity().double().is_identity()), "identity.double() wasn't identity"); assert_eq!( G::generator() + G::generator(), G::generator().double(), "generator + generator != generator.double()" ); } /// Test addition. pub fn test_add() { assert_eq!(G::identity() + G::identity(), G::identity(), "identity + identity != identity"); assert_eq!(G::identity() + G::generator(), G::generator(), "identity + generator != generator"); assert_eq!(G::generator() + G::identity(), G::generator(), "generator + identity != generator"); let two = G::generator().double(); assert_eq!(G::generator() + G::generator(), two, "generator + generator != two"); let four = two.double(); assert_eq!( G::generator() + G::generator() + G::generator() + G::generator(), four, "generator + generator + generator + generator != four" ); } /// Test summation. pub fn test_sum() { assert_eq!( [G::generator(), G::generator()].iter().sum::(), G::generator().double(), "[generator, generator].sum() != two" ); assert_eq!( [G::generator().double(), G::generator()].iter().sum::(), G::generator().double() + G::generator(), "[generator.double(), generator].sum() != three" ); } /// Test negation. pub fn test_neg() { assert_eq!(G::identity(), G::identity().neg(), "identity != -identity"); assert_eq!( G::generator() + G::generator().neg(), G::identity(), "generator + -generator != identity" ); } /// Test subtraction. pub fn test_sub() { assert_eq!(G::generator() - G::generator(), G::identity(), "generator - generator != identity"); let two = G::generator() + G::generator(); assert_eq!(two - G::generator(), G::generator(), "two - one != one"); } /// Test scalar multiplication pub fn test_mul() { assert_eq!(G::generator() * G::Scalar::from(0), G::identity(), "generator * 0 != identity"); assert_eq!(G::generator() * G::Scalar::from(1), G::generator(), "generator * 1 != generator"); assert_eq!( G::generator() * G::Scalar::from(2), G::generator() + G::generator(), "generator * 2 != generator + generator" ); assert_eq!(G::identity() * G::Scalar::from(2), G::identity(), "identity * 2 != identity"); } /// Test `((order - 1) * G) + G == identity`. pub fn test_order() { let minus_one = G::generator() * (G::Scalar::ZERO - G::Scalar::ONE); assert!(minus_one != G::identity(), "(modulus - 1) * G was identity"); assert_eq!(minus_one + G::generator(), G::identity(), "((modulus - 1) * G) + G wasn't identity"); } /// Test random. pub fn test_random(rng: &mut R) { let a = G::random(&mut *rng); assert!(!bool::from(a.is_identity()), "random returned identity"); // Run up to 128 times so small groups, which may occasionally return the same element twice, // are statistically unlikely to fail // Groups of order <= 2 will always fail this test due to lack of distinct elements to sample // from let mut pass = false; for _ in 0 .. 128 { let b = G::random(&mut *rng); assert!(!bool::from(b.is_identity()), "random returned identity"); // This test passes if a distinct element is returned at least once if b != a { pass = true; } } assert!(pass, "random always returned the same value"); } /// Run all tests on groups implementing Group. pub fn test_group(rng: &mut R) { test_prime_field::(rng); test_eq::(); test_identity::(); test_generator::(); test_double::(); test_add::(); test_sum::(); test_neg::(); test_sub::(); test_mul::(); test_order::(); test_random::(rng); } /// Test encoding and decoding of group elements. pub fn test_encoding() { let test = |point: G, msg| { let bytes = point.to_bytes(); let mut repr = G::Repr::default(); repr.as_mut().copy_from_slice(bytes.as_ref()); assert_eq!(point, G::from_bytes(&repr).unwrap(), "{msg} couldn't be encoded and decoded"); assert_eq!( point, G::from_bytes_unchecked(&repr).unwrap(), "{msg} couldn't be encoded and decoded", ); }; test(G::identity(), "identity"); test(G::generator(), "generator"); test(G::generator() + G::generator(), "(generator * 2)"); } /// Run all tests on groups implementing PrimeGroup (Group + GroupEncoding). pub fn test_prime_group(rng: &mut R) { test_group::(rng); test_encoding::(); } /// Run all tests offered by this crate on the group. pub fn test_prime_group_bits(rng: &mut R) where G::Scalar: PrimeFieldBits, { test_prime_field_bits::(rng); test_prime_group::(rng); } // Run these tests against k256/p256 // This ensures that these tests are well formed and won't error for valid implementations, // assuming the validity of k256/p256 // While k256 and p256 may be malformed in a way which coincides with a faulty test, this is // considered unlikely // The other option, not running against any libraries, would leave faulty tests completely // undetected #[test] fn test_k256() { test_prime_group_bits::<_, k256::ProjectivePoint>(&mut rand_core::OsRng); } #[test] fn test_p256() { test_prime_group_bits::<_, p256::ProjectivePoint>(&mut rand_core::OsRng); } #[test] fn test_bls12_381() { test_prime_group_bits::<_, bls12_381::G1Projective>(&mut rand_core::OsRng); test_prime_group_bits::<_, bls12_381::G2Projective>(&mut rand_core::OsRng); } #[test] fn test_pallas_vesta() { test_prime_group_bits::<_, pasta_curves::pallas::Point>(&mut rand_core::OsRng); test_prime_group_bits::<_, pasta_curves::vesta::Point>(&mut rand_core::OsRng); }