#![allow(non_snake_case)]
use crate::error::BBSPlusError;
use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup, Group};
use ark_ff::{fields::Field, PrimeField};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::{
collections::BTreeMap, fmt::Debug, ops::Mul, rand::RngCore, vec::Vec, UniformRand, Zero,
};
use crate::{
prelude::PreparedSignatureParamsG1,
setup::{PreparedPublicKeyG2, PublicKeyG1, SecretKey, SignatureParamsG1, SignatureParamsG2},
};
use dock_crypto_utils::{expect_equality, serde_utils::*, signature::MultiMessageSignatureParams};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use zeroize::{Zeroize, ZeroizeOnDrop};
macro_rules! impl_signature_struct {
( $name:ident, $group:ident ) => {
#[serde_as]
#[derive(
Clone,
Debug,
PartialEq,
Eq,
CanonicalSerialize,
CanonicalDeserialize,
Serialize,
Deserialize,
Zeroize,
ZeroizeOnDrop,
)]
pub struct $name<E: Pairing> {
#[serde_as(as = "ArkObjectBytes")]
pub A: E::$group,
#[serde_as(as = "ArkObjectBytes")]
pub e: E::ScalarField,
#[serde_as(as = "ArkObjectBytes")]
pub s: E::ScalarField,
}
};
}
impl_signature_struct!(SignatureG1, G1Affine);
impl_signature_struct!(SignatureG2, G2Affine);
macro_rules! impl_signature_alg {
( $name:ident, $params:ident, $pk:ident, $sig_group_proj:ident, $sig_group_affine:ident, $verif_params:ident ) => {
impl<E: Pairing> $name<E> {
pub fn new<R: RngCore>(
rng: &mut R,
messages: &[E::ScalarField],
sk: &SecretKey<E::ScalarField>,
params: &$params<E>,
) -> Result<Self, BBSPlusError> {
if messages.is_empty() {
return Err(BBSPlusError::NoMessageToSign);
}
expect_equality!(
messages.len(),
params.supported_message_count(),
BBSPlusError::MessageCountIncompatibleWithSigParams
);
let msg_map: BTreeMap<usize, &E::ScalarField> =
messages.iter().enumerate().map(|(i, e)| (i, e)).collect();
Self::new_with_committed_messages(
rng,
&E::$sig_group_affine::zero(),
msg_map,
sk,
params,
)
}
pub fn new_with_committed_messages<R: RngCore>(
rng: &mut R,
commitment: &E::$sig_group_affine,
uncommitted_messages: BTreeMap<usize, &E::ScalarField>,
sk: &SecretKey<E::ScalarField>,
params: &$params<E>,
) -> Result<Self, BBSPlusError> {
if uncommitted_messages.is_empty() {
return Err(BBSPlusError::NoMessageToSign);
}
if uncommitted_messages.len() > params.supported_message_count() {
return Err(BBSPlusError::MessageCountIncompatibleWithSigParams(
uncommitted_messages.len(),
params.supported_message_count(),
));
}
let s = E::ScalarField::rand(rng);
let b = params.b(uncommitted_messages, &s)?;
let mut e = E::ScalarField::rand(rng);
while (e + sk.0).is_zero() {
e = E::ScalarField::rand(rng)
}
let e_plus_x_inv = (e + sk.0).inverse().unwrap();
let commitment_plus_b = b + commitment;
let A = commitment_plus_b.mul_bigint(e_plus_x_inv.into_bigint());
Ok(Self {
A: A.into_affine(),
e,
s,
})
}
pub fn is_non_zero(&self) -> bool {
!self.A.is_zero()
}
pub fn unblind(self, blinding: &E::ScalarField) -> Self {
Self {
A: self.A,
s: self.s + blinding,
e: self.e,
}
}
pub fn pre_verify(
&self,
messages: &[E::ScalarField],
params: &$verif_params<E>,
) -> Result<E::$sig_group_proj, BBSPlusError> {
if messages.is_empty() {
return Err(BBSPlusError::NoMessageToSign);
}
expect_equality!(
messages.len(),
params.supported_message_count(),
BBSPlusError::MessageCountIncompatibleWithSigParams
);
if !self.is_non_zero() {
return Err(BBSPlusError::ZeroSignature);
}
params.b(messages.iter().enumerate(), &self.s)
}
}
};
}
impl_signature_alg!(
SignatureG1,
SignatureParamsG1,
PublicKeyG2,
G1,
G1Affine,
PreparedSignatureParamsG1
);
impl_signature_alg!(
SignatureG2,
SignatureParamsG2,
PublicKeyG1,
G2,
G2Affine,
SignatureParamsG2
);
impl<E: Pairing> SignatureG1<E> {
pub fn verify(
&self,
messages: &[E::ScalarField],
pk: impl Into<PreparedPublicKeyG2<E>>,
params: impl Into<PreparedSignatureParamsG1<E>>,
) -> Result<(), BBSPlusError> {
let params = params.into();
let b = self.pre_verify(messages, ¶ms)?;
let Aeb = self.A.mul(self.e) - b;
if !E::multi_pairing(
[
E::G1Prepared::from(self.A),
E::G1Prepared::from(Aeb.into_affine()),
],
[pk.into().0, params.g2],
)
.is_zero()
{
return Err(BBSPlusError::InvalidSignature);
}
Ok(())
}
}
impl<E: Pairing> SignatureG2<E> {
pub fn verify(
&self,
messages: &[E::ScalarField],
pk: &PublicKeyG1<E>,
params: &SignatureParamsG2<E>,
) -> Result<(), BBSPlusError> {
let b = self.pre_verify(messages, params)?;
let g2_e = params.g2.mul_bigint(self.e.into_bigint());
if !E::multi_pairing(
[
E::G1Prepared::from((g2_e + pk.0).into_affine()),
E::G1Prepared::from((-(params.g2.into_group())).into_affine()),
],
[E::G2Prepared::from(self.A), E::G2Prepared::from(b)],
)
.is_zero()
{
return Err(BBSPlusError::InvalidSignature);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
setup::{KeypairG1, KeypairG2},
test_serialization,
};
use ark_bls12_381::{Bls12_381, G1Affine, G2Affine};
use ark_std::{
rand::{rngs::StdRng, SeedableRng},
UniformRand,
};
use std::{collections::HashSet, time::Instant};
type Fr = <Bls12_381 as Pairing>::ScalarField;
macro_rules! params_and_pk_for_g1_sig {
($params:expr, $pk:expr) => {
(
PreparedSignatureParamsG1::from($params),
PreparedPublicKeyG2::from($pk),
)
};
}
macro_rules! params_and_pk_for_g2_sig {
($params:expr, $pk:expr) => {
(&$params, &$pk)
};
}
macro_rules! test_sig_verif {
($keypair:ident, $params:ident, $sig:ident, $rng:ident, $message_count: ident, $messages: ident, $group: ident, $verif_params_and_pk: tt) => {
let params = $params::<Bls12_381>::generate_using_rng(&mut $rng, $message_count);
let keypair = $keypair::<Bls12_381>::generate_using_rng(&mut $rng, ¶ms);
let public_key = &keypair.public_key;
let start = Instant::now();
let sig = $sig::<Bls12_381>::new(&mut $rng, &$messages, &keypair.secret_key, ¶ms)
.unwrap();
println!(
"Time to sign multi-message of size {} is {:?}",
$message_count,
start.elapsed()
);
assert!(params.is_valid());
assert!(public_key.is_valid());
let (verif_params, verif_pk) =
$verif_params_and_pk!(params.clone(), public_key.clone());
let mut zero_sig = sig.clone();
zero_sig.A = $group::zero();
assert!(zero_sig.verify(&$messages, verif_pk, verif_params).is_err());
let (verif_params, verif_pk) =
$verif_params_and_pk!(params.clone(), public_key.clone());
let start = Instant::now();
sig.verify(&$messages, verif_pk, verif_params).unwrap();
println!(
"Time to verify signature over multi-message of size {} is {:?}",
$message_count,
start.elapsed()
);
drop(sig);
let blinding = Fr::rand(&mut $rng);
let mut committed_indices = HashSet::new();
committed_indices.insert(0);
committed_indices.insert(1);
committed_indices.insert(4);
committed_indices.insert(9);
let committed_messages = committed_indices
.iter()
.map(|i| (*i, &$messages[*i]))
.collect::<BTreeMap<_, _>>();
let commitment = params
.commit_to_messages(committed_messages, &blinding)
.unwrap();
let mut uncommitted_messages = BTreeMap::new();
for (i, msg) in $messages.iter().enumerate() {
if committed_indices.contains(&i) {
continue;
}
uncommitted_messages.insert(i, msg);
}
let blinded_sig = $sig::<Bls12_381>::new_with_committed_messages(
&mut $rng,
&commitment,
uncommitted_messages,
&keypair.secret_key,
¶ms,
)
.unwrap();
let (verif_params, verif_pk) =
$verif_params_and_pk!(params.clone(), public_key.clone());
assert!(blinded_sig
.verify(&$messages, verif_pk, verif_params)
.is_err());
let (verif_params, verif_pk) =
$verif_params_and_pk!(params.clone(), public_key.clone());
let sig = blinded_sig.unblind(&blinding);
sig.verify(&$messages, verif_pk, verif_params).unwrap();
test_serialization!($sig<Bls12_381>, sig);
drop(sig);
};
}
#[test]
fn signature_verification() {
let mut rng = StdRng::seed_from_u64(0u64);
let message_count = 20;
let messages: Vec<Fr> = (0..message_count).map(|_| Fr::rand(&mut rng)).collect();
println!("Signature in Group G1");
{
test_sig_verif!(
KeypairG2,
SignatureParamsG1,
SignatureG1,
rng,
message_count,
messages,
G1Affine,
params_and_pk_for_g1_sig
);
}
println!("Signature in Group G2");
{
test_sig_verif!(
KeypairG1,
SignatureParamsG2,
SignatureG2,
rng,
message_count,
messages,
G2Affine,
params_and_pk_for_g2_sig
);
}
}
}