use ark_bls12_381::{Bls12_381, G1Affine, G2Affine};
use ark_ec::{pairing::Pairing, AffineRepr};
use ark_serialize::CanonicalDeserialize;
use ark_std::{ops::Neg, Zero};
#[derive(Debug, PartialEq)]
pub enum CryptoError {
DeserializeG1Failure,
DeserializeG2Failure,
HashToCurveFailure,
SerializeG1Failure,
SerializeG2Failure,
InvalidSignature,
InvalidBuffer,
}
#[inline]
pub(crate) fn fast_pairing_opt(signature: G1Affine, q: G2Affine, r: G1Affine, s: G2Affine) -> bool {
let looped = Bls12_381::multi_miller_loop([signature.neg(), r], [q, s]);
if let Some(exp) = Bls12_381::final_exponentiation(looped) {
return exp.is_zero();
}
false
}
pub fn zero_on_g1() -> G1Affine {
G1Affine::zero()
}
pub(crate) fn decode_g1(mut bytes: &[u8]) -> Result<G1Affine, CryptoError> {
G1Affine::deserialize_compressed(&mut bytes).map_err(|_| CryptoError::DeserializeG1Failure)
}
pub(crate) fn decode_g2(mut bytes: &[u8]) -> Result<G2Affine, CryptoError> {
G2Affine::deserialize_compressed(&mut bytes).map_err(|_| CryptoError::DeserializeG2Failure)
}
#[cfg(test)]
pub mod tests {
use super::*;
use ark_bls12_381::{G1Projective, G2Projective};
use ark_ec::{AffineRepr, CurveGroup, PrimeGroup};
use ark_serialize::CanonicalSerialize;
use ark_std::{rand::thread_rng, UniformRand};
#[test]
fn test_fast_pairing_opt_valid_pairing() {
let mut rng = thread_rng();
let g1 = G1Projective::generator();
let g2 = G2Projective::generator();
let scalar = ark_bls12_381::Fr::rand(&mut rng);
let sig = (g1 * scalar).into_affine();
let q = g2.into_affine();
let r = g1.into_affine();
let s = (g2 * scalar).into_affine();
assert!(fast_pairing_opt(sig, q, r, s));
}
#[test]
fn test_fast_pairing_opt_invalid_pairing() {
let mut rng = thread_rng();
let g1 = G1Projective::generator();
let g2 = G2Projective::generator();
let sig = (g1 * ark_bls12_381::Fr::rand(&mut rng)).into_affine();
let q = g2.into_affine();
let r = g1.into_affine();
let s = (g2 * ark_bls12_381::Fr::rand(&mut rng)).into_affine();
assert!(!fast_pairing_opt(sig, q, r, s));
}
#[test]
fn test_fast_pairing_opt_zero_result() {
let g1_zero = G1Projective::zero().into_affine();
let g2_zero = G2Projective::zero().into_affine();
assert!(fast_pairing_opt(g1_zero, g2_zero, g1_zero, g2_zero));
}
#[test]
fn test_zero_on_g1() {
let zero_point = zero_on_g1();
assert!(zero_point.is_zero(), "zero_on_g1 should return the identity element (zero)");
}
#[test]
fn test_decode_g1_roundtrip() {
let point = zero_on_g1();
let mut serialized = Vec::new();
point.serialize_compressed(&mut serialized).unwrap();
let decoded_point = decode_g1(&serialized).expect("Decoding should succeed");
assert_eq!(point, decoded_point, "Decoded G1 point should equal the original point");
}
#[test]
fn test_decode_g1_invalid() {
let invalid_bytes = b"invalid bytes";
let result = decode_g1(invalid_bytes);
assert!(result.is_err(), "Decoding invalid G1 bytes should return an error");
assert_eq!(result, Err(CryptoError::DeserializeG1Failure));
}
#[test]
fn test_decode_g2_roundtrip() {
let point = G2Affine::generator();
let mut serialized = Vec::new();
point.serialize_compressed(&mut serialized).unwrap();
let decoded_point = decode_g2(&serialized).expect("Decoding should succeed");
assert_eq!(
point, decoded_point,
"Decoded G2 point should equal the original generator point"
);
}
#[test]
fn test_decode_g2_invalid() {
let invalid_bytes = b"invalid bytes";
let result = decode_g2(invalid_bytes);
assert!(result.is_err(), "Decoding invalid G2 bytes should return an error");
assert_eq!(result, Err(CryptoError::DeserializeG2Failure));
}
}