use core::borrow::Borrow;
use alloc::collections::BTreeMap;
use ark_ec::AffineRepr;
use ark_ff::field_hashers::{DefaultFieldHasher, HashToField};
use ark_serialize::CanonicalSerialize;
use digest::FixedOutputReset;
use ark_ec::CurveGroup;
use alloc::vec;
use alloc::vec::Vec;
use super::*;
pub type PublicKeyProjective<E> = <E as EngineBLS>::PublicKeyGroup;
pub type PublicKeyAffine<E> = <<E as EngineBLS>::PublicKeyGroup as CurveGroup>::Affine;
pub type SignatureProjective<E> = <E as EngineBLS>::SignatureGroup;
pub type SignatureAffine<E> = <<E as EngineBLS>::SignatureGroup as CurveGroup>::Affine;
fn verify_normalized<E: EngineBLS>(
affine_publickeys: &[PublicKeyAffine<E>],
affine_messages: &[SignatureAffine<E>],
affine_signature: SignatureAffine<E>,
) -> bool {
if affine_publickeys.is_empty() {
return false;
}
if !E::verify_signature_in_signature_subgroup(&affine_signature) {
return false;
}
let prepared_sig = E::prepare_signature(affine_signature);
let mut prepared = Vec::with_capacity(affine_publickeys.len());
for (pk, m) in affine_publickeys.iter().zip(affine_messages) {
if !E::verify_public_key_in_public_key_subgroup(pk) {
return false;
}
prepared.push((E::prepare_public_key(*pk), E::prepare_signature(*m)));
}
E::verify_prepared(prepared_sig, prepared.iter())
}
fn collect_messages_and_publickeys<S: Signed>(
s: S,
) -> (
SignatureProjective<S::E>,
Vec<PublicKeyProjective<S::E>>,
Vec<SignatureProjective<S::E>>,
) {
let signature = s.signature().0;
let itr = s.messages_and_publickeys();
let l = {
let (lower, upper) = itr.size_hint();
upper.unwrap_or(lower)
};
let mut publickeys = Vec::with_capacity(l);
let mut messages = Vec::with_capacity(l);
for (message, publickey) in itr {
publickeys.push(publickey.public_key().0);
messages.push(message.borrow().hash_to_signature_curve::<S::E>());
}
(signature, publickeys, messages)
}
fn merge_by_signer<E: EngineBLS>(
affine_publickeys: Vec<PublicKeyAffine<E>>,
messages: Vec<SignatureProjective<E>>,
) -> (Vec<PublicKeyAffine<E>>, Vec<SignatureProjective<E>>) {
type PkMsg<E> = (PublicKeyAffine<E>, SignatureProjective<E>);
let mut pks_n_ms: BTreeMap<Vec<u8>, PkMsg<E>> = BTreeMap::new();
for (pk, m) in affine_publickeys.into_iter().zip(messages) {
let mut pk_bytes = vec![0; pk.uncompressed_size()];
pk.serialize_uncompressed(&mut pk_bytes[..]).unwrap();
pks_n_ms
.entry(pk_bytes)
.and_modify(|(_, m0)| *m0 += m)
.or_insert((pk, m));
}
pks_n_ms.into_values().unzip()
}
fn merge_by_signer_with_aux<E: EngineBLS>(
affine_publickeys: Vec<PublicKeyAffine<E>>,
aux_keys: Vec<SignatureProjective<E>>,
messages: Vec<SignatureProjective<E>>,
) -> Option<(
Vec<PublicKeyAffine<E>>,
Vec<SignatureAffine<E>>,
Vec<SignatureProjective<E>>,
)> {
type PkAuxMsg<E> = (
PublicKeyAffine<E>,
SignatureAffine<E>,
SignatureProjective<E>,
);
let mut map: BTreeMap<Vec<u8>, PkAuxMsg<E>> = BTreeMap::new();
for ((pk, aux), m) in affine_publickeys.into_iter().zip(aux_keys).zip(messages) {
let aux_affine = aux.into_affine();
let mut pk_bytes = vec![0; pk.uncompressed_size()];
pk.serialize_uncompressed(&mut pk_bytes[..]).unwrap();
match map.entry(pk_bytes) {
alloc::collections::btree_map::Entry::Occupied(mut e) => {
let (_, existing_aux, existing_msg) = e.get_mut();
if *existing_aux != aux_affine {
return None;
}
*existing_msg += m;
}
alloc::collections::btree_map::Entry::Vacant(e) => {
e.insert((pk, aux_affine, m));
}
}
}
let mut pks = Vec::with_capacity(map.len());
let mut auxs = Vec::with_capacity(map.len());
let mut msgs = Vec::with_capacity(map.len());
for (pk, aux, m) in map.into_values() {
pks.push(pk);
auxs.push(aux);
msgs.push(m);
}
Some((pks, auxs, msgs))
}
fn normalize_publickeys<E: EngineBLS>(
publickeys: &[PublicKeyProjective<E>],
batch_normalize: bool,
) -> Vec<PublicKeyAffine<E>> {
if batch_normalize {
E::PublicKeyGroup::normalize_batch(publickeys)
} else {
publickeys.iter().map(|pk| pk.into_affine()).collect()
}
}
fn normalize_messages_and_signature<E: EngineBLS>(
mut messages: Vec<SignatureProjective<E>>,
signature: SignatureProjective<E>,
) -> (Vec<SignatureAffine<E>>, SignatureAffine<E>) {
messages.push(signature);
let mut affine = E::SignatureGroup::normalize_batch(&messages);
let signature = affine.pop().unwrap();
(affine, signature)
}
pub fn verify_unoptimized<S: Signed>(s: S) -> bool {
let affine_signature = s.signature().0.into();
if !S::E::verify_signature_in_signature_subgroup(&affine_signature) {
return false;
}
let signature = S::E::prepare_signature(affine_signature);
let mut prepared = Vec::new();
for (message, public_key) in s.messages_and_publickeys() {
let pk_affine: PublicKeyAffine<S::E> = public_key.public_key().0.into();
if !S::E::verify_public_key_in_public_key_subgroup(&pk_affine) {
return false;
}
prepared.push((
S::E::prepare_public_key(pk_affine),
S::E::prepare_signature(message.borrow().hash_to_signature_curve::<S::E>()),
));
}
if prepared.is_empty() {
return false;
}
S::E::verify_prepared(signature, prepared.iter())
}
pub fn verify_simple<S: Signed>(s: S) -> bool {
let (signature, publickeys, messages) = collect_messages_and_publickeys(s);
let affine_pks = PublicKeyProjective::<S::E>::normalize_batch(&publickeys);
let (affine_msgs, affine_sig) = normalize_messages_and_signature::<S::E>(messages, signature);
verify_normalized::<S::E>(&affine_pks, &affine_msgs, affine_sig)
}
pub fn verify_with_distinct_messages<S: Signed>(signed: S, normalize_public_keys: bool) -> bool {
let (signature, publickeys, messages) = collect_messages_and_publickeys(signed);
let affine_publickeys = normalize_publickeys::<S::E>(&publickeys, normalize_public_keys);
let (merged_pks, merged_msgs) = merge_by_signer::<S::E>(affine_publickeys, messages);
let (affine_msgs, affine_sig) =
normalize_messages_and_signature::<S::E>(merged_msgs, signature);
verify_normalized::<S::E>(&merged_pks, &affine_msgs, affine_sig)
}
pub fn verify_using_aggregated_auxiliary_public_keys<
S: Signed,
H: FixedOutputReset + Default + Clone,
>(
signed: S,
normalize_public_keys: bool,
) -> bool {
let mut signature = signed.signature().0;
let mut signature_as_bytes = vec![0; signature.compressed_size()];
signature
.serialize_compressed(&mut signature_as_bytes[..])
.expect("compressed size has been alocated");
let itr = signed.messages_and_publickeys();
let l = {
let (lower, upper) = itr.size_hint();
upper.unwrap_or(lower)
};
let mut publickeys = Vec::with_capacity(l);
let mut aux_keys = Vec::with_capacity(l);
let mut messages = Vec::with_capacity(l);
for (m, pk) in itr {
publickeys.push(pk.public_key().0);
aux_keys.push(pk.public_key_in_signature_group().0);
messages.push(m.borrow().hash_to_signature_curve::<S::E>());
}
let affine_publickeys = normalize_publickeys::<S::E>(&publickeys, normalize_public_keys);
let (merged_pks, merged_aux, mut merged_msgs) =
match merge_by_signer_with_aux::<S::E>(affine_publickeys, aux_keys, messages) {
Some(v) => v,
None => return false,
};
let hasher = <DefaultFieldHasher<H> as HashToField<<S::E as EngineBLS>::Scalar>>::new(&[]);
let n = merged_pks.len();
let affine_merged_msgs =
<<S::E as EngineBLS>::SignatureGroup as CurveGroup>::normalize_batch(&merged_msgs);
let entry_size = 2 * <S::E as EngineBLS>::SIGNATURE_SERIALIZED_SIZE
+ <S::E as EngineBLS>::PUBLICKEY_SERIALIZED_SIZE;
let transcript_size = <S::E as EngineBLS>::SIGNATURE_SERIALIZED_SIZE + n * entry_size;
let mut seed = Vec::with_capacity(transcript_size + core::mem::size_of::<u64>());
seed.extend_from_slice(&signature_as_bytes);
for i in 0..n {
affine_merged_msgs[i]
.serialize_compressed(&mut seed)
.expect("compressed size known");
merged_aux[i]
.serialize_compressed(&mut seed)
.expect("compressed size known");
merged_pks[i]
.serialize_compressed(&mut seed)
.expect("compressed size known");
}
let transcript_len = seed.len();
for i in 0..n {
seed.truncate(transcript_len);
if i > 0 {
let index = (i as u64) + 1;
seed.extend_from_slice(&index.to_be_bytes());
}
let pseudo_random_scalar: <S::E as EngineBLS>::Scalar =
hasher.hash_to_field::<1>(&seed[..])[0];
signature += merged_aux[i] * pseudo_random_scalar;
merged_msgs[i] +=
<S::E as EngineBLS>::SignatureGroupAffine::generator() * pseudo_random_scalar;
}
let (affine_msgs, affine_sig) =
normalize_messages_and_signature::<S::E>(merged_msgs, signature);
verify_normalized::<S::E>(&merged_pks, &affine_msgs, affine_sig)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::single::SignedMessage;
use crate::{Keypair, Message, PublicKey, Signature, UsualBLS};
use ark_bls12_381::{Bls12_381, Fq, Fq2, G1Affine, G2Affine};
use ark_ec::{AffineRepr, CurveGroup, PrimeGroup};
use ark_ff::{BitIteratorBE, One, PrimeField, UniformRand};
use rand::SeedableRng;
use rand::rngs::StdRng;
type EB = UsualBLS<Bls12_381, ark_bls12_381::Config>;
fn mul_by_int_no_glv<G: PrimeGroup + Copy, S: AsRef<[u64]>>(point: G, scalar: S) -> G {
let mut result = G::zero();
for b in BitIteratorBE::without_leading_zeros(scalar) {
result.double_in_place();
if b {
result += point;
}
}
result
}
fn cofactor_subgroup_g1() -> G1Affine {
let mut rng = StdRng::from_seed([42u8; 32]);
let r = <ark_bls12_381::Fr as PrimeField>::MODULUS;
loop {
let x = Fq::rand(&mut rng);
let Some(point) = G1Affine::get_point_from_x_unchecked(x, false) else {
continue;
};
let projected = mul_by_int_no_glv(point.into_group(), r).into_affine();
if !projected.is_zero() {
return projected;
}
}
}
fn cofactor_subgroup_g2() -> G2Affine {
let mut rng = StdRng::from_seed([43u8; 32]);
let r = <ark_bls12_381::Fr as PrimeField>::MODULUS;
loop {
let x = Fq2::rand(&mut rng);
let Some(point) = G2Affine::get_point_from_x_unchecked(x, false) else {
continue;
};
let projected = mul_by_int_no_glv(point.into_group(), r).into_affine();
if !projected.is_zero() {
return projected;
}
}
}
fn signed_with_g1_cofactor_pk() -> SignedMessage<EB> {
let message = Message::new(b"ctx", b"test message");
let mut keypair = Keypair::<EB>::generate(StdRng::from_seed([0u8; 32]));
let signed = keypair.signed_message(&message);
let sk = keypair.to_vartime().secret.0;
let q_g1: <EB as EngineBLS>::PublicKeyGroup = cofactor_subgroup_g1().into();
let bad_pk = q_g1 * sk + signed.publickey.0;
SignedMessage {
message: signed.message,
publickey: PublicKey::<EB>(bad_pk),
signature: signed.signature,
}
}
fn signed_with_g2_cofactor_sig() -> SignedMessage<EB> {
let message = Message::new(b"ctx", b"test message");
let mut keypair = Keypair::<EB>::generate(StdRng::from_seed([0u8; 32]));
let signed = keypair.signed_message(&message);
let sk = keypair.to_vartime().secret.0;
let q_g2: <EB as EngineBLS>::SignatureGroup = cofactor_subgroup_g2().into();
let bad_sig = q_g2 * sk + signed.signature.0;
SignedMessage {
message: signed.message,
publickey: signed.publickey,
signature: Signature::<EB>(bad_sig),
}
}
#[test]
fn verify_simple_single_signature() {
let good = Message::new(b"ctx", b"test message");
let mut keypair = Keypair::<EB>::generate(StdRng::from_seed([0u8; 32]));
let signed = keypair.signed_message(&good);
assert!(verify_simple(&signed));
}
#[test]
fn verify_simple_rejects_wrong_message() {
let good = Message::new(b"ctx", b"test message");
let bad = Message::new(b"ctx", b"wrong message");
let mut keypair = Keypair::<EB>::generate(StdRng::from_seed([0u8; 32]));
let sig = keypair.sign(&good);
let wrong_signed = single::SignedMessage {
message: bad,
publickey: keypair.public,
signature: sig,
};
assert!(!verify_simple(&wrong_signed));
}
#[test]
fn verify_unoptimized_single_signature() {
let good = Message::new(b"ctx", b"test message");
let mut keypair = Keypair::<EB>::generate(StdRng::from_seed([0u8; 32]));
let signed = keypair.signed_message(&good);
assert!(verify_unoptimized(&signed));
}
#[test]
fn cofactor_points_pair_to_one() {
use ark_ec::pairing::Pairing;
let q_g1 = cofactor_subgroup_g1();
let g2_gen = G2Affine::generator();
let p1 = Bls12_381::pairing(q_g1, g2_gen);
assert!(p1.0.is_one(), "e(Q_g1, g2_gen) = {:?} != 1", p1.0);
}
#[test]
fn verify_prepared_accepts_cofactor_components() {
let bad = signed_with_g1_cofactor_pk();
let prepared_pk = <EB as EngineBLS>::prepare_public_key(bad.publickey.0);
let prepared_msg =
<EB as EngineBLS>::prepare_signature(bad.message.hash_to_signature_curve::<EB>());
let prepared_sig = <EB as EngineBLS>::prepare_signature(bad.signature.0);
let pairs = [(prepared_pk, prepared_msg)];
assert!(<EB as EngineBLS>::verify_prepared(
prepared_sig,
pairs.iter()
));
}
#[test]
fn verify_simple_rejects_g1_cofactor_pk() {
assert!(!verify_simple(&signed_with_g1_cofactor_pk()));
}
#[test]
fn verify_unoptimized_rejects_g1_cofactor_pk() {
assert!(!verify_unoptimized(&signed_with_g1_cofactor_pk()));
}
#[test]
fn verify_with_distinct_messages_rejects_g1_cofactor_pk() {
assert!(!verify_with_distinct_messages(
&signed_with_g1_cofactor_pk(),
true
));
}
#[test]
fn verify_simple_rejects_g2_cofactor_sig() {
assert!(!verify_simple(&signed_with_g2_cofactor_sig()));
}
#[test]
fn verify_unoptimized_rejects_g2_cofactor_sig() {
assert!(!verify_unoptimized(&signed_with_g2_cofactor_sig()));
}
#[test]
fn verify_with_distinct_messages_rejects_g2_cofactor_sig() {
assert!(!verify_with_distinct_messages(
&signed_with_g2_cofactor_sig(),
true
));
}
}