Skip to content

Commit

Permalink
Merge pull request #500 from Chia-Network/aggregate-verift-gt
Browse files Browse the repository at this point in the history
expose aggregate_verify_gt() in chia-bls
  • Loading branch information
arvidn authored Apr 30, 2024
2 parents 023e1cf + 8cc9a40 commit 25fd0a5
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 8 deletions.
40 changes: 36 additions & 4 deletions crates/chia-bls/benches/verify.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use chia_bls::secret_key::SecretKey;
use chia_bls::signature;
use chia_bls::signature::sign;
use chia_bls::{
aggregate_verify, aggregate_verify_gt, hash_to_g2, sign, GTElement, PublicKey, SecretKey,
Signature,
};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
Expand All @@ -17,14 +19,44 @@ fn verify_benchmark(c: &mut Criterion) {
let sig_small = sign(&sk, msg_small);
let sig_large = sign(&sk, msg_large);

let mut agg_sig = Signature::default();
let mut gts = Vec::<GTElement>::new();
let mut pks = Vec::<PublicKey>::new();
for idx in 0..1000 {
let derived = sk.derive_hardened(idx as u32);
let pk = derived.public_key();
let sig = sign(&derived, msg_small);
agg_sig.aggregate(&sig);

let mut augmented = pk.to_bytes().to_vec();
augmented.extend_from_slice(msg_small);
gts.push(hash_to_g2(augmented.as_slice()).pair(&pk));
pks.push(pk);
}

c.bench_function("aggregate_verify_gt, small msg", |b| {
b.iter(|| {
assert!(aggregate_verify_gt(&agg_sig, &gts));
});
});

c.bench_function("aggregate_verify, small msg", |b| {
b.iter(|| {
assert!(aggregate_verify(
&agg_sig,
pks.iter().map(|pk| (pk, &msg_small[..]))
));
});
});

c.bench_function("verify, small msg", |b| {
b.iter(|| {
signature::verify(&sig_small, &pk, black_box(&msg_small));
assert!(signature::verify(&sig_small, &pk, black_box(&msg_small)));
});
});
c.bench_function("verify, 4kiB msg", |b| {
b.iter(|| {
signature::verify(&sig_large, &pk, black_box(&msg_large));
assert!(signature::verify(&sig_large, &pk, black_box(&msg_large)));
});
});
}
Expand Down
17 changes: 15 additions & 2 deletions crates/chia-bls/src/gtelement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::fmt;
use std::hash::{Hash, Hasher};
use std::io::Cursor;
use std::mem::MaybeUninit;
use std::ops::MulAssign;
use std::ops::{Mul, MulAssign};

#[cfg(feature = "py-bindings")]
use chia_py_streamable_macro::PyStreamable;
Expand All @@ -19,7 +19,8 @@ use pyo3::exceptions::PyValueError;
#[cfg(feature = "py-bindings")]
use pyo3::{pyclass, pymethods, IntoPy, PyAny, PyObject, PyResult, Python};

#[cfg_attr(feature = "py-bindings", pyclass, derive(PyStreamable, Clone))]
#[cfg_attr(feature = "py-bindings", pyclass, derive(PyStreamable))]
#[derive(Clone)]
pub struct GTElement(pub(crate) blst_fp12);

impl GTElement {
Expand Down Expand Up @@ -93,6 +94,18 @@ impl MulAssign<&GTElement> for GTElement {
}
}

impl Mul<&GTElement> for &GTElement {
type Output = GTElement;
fn mul(self, rhs: &GTElement) -> GTElement {
let gt = unsafe {
let mut gt = MaybeUninit::<blst_fp12>::uninit();
blst_fp12_mul(gt.as_mut_ptr(), &self.0, &rhs.0);
gt.assume_init()
};
GTElement(gt)
}
}

#[cfg(feature = "py-bindings")]
impl ToJsonDict for GTElement {
fn to_json_dict(&self, py: Python) -> pyo3::PyResult<PyObject> {
Expand Down
4 changes: 2 additions & 2 deletions crates/chia-bls/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ pub use gtelement::GTElement;
pub use public_key::{hash_to_g1, hash_to_g1_with_dst, PublicKey};
pub use secret_key::SecretKey;
pub use signature::{
aggregate, aggregate_pairing, aggregate_verify, hash_to_g2, hash_to_g2_with_dst, sign,
sign_raw, verify, Signature,
aggregate, aggregate_pairing, aggregate_verify, aggregate_verify_gt, hash_to_g2,
hash_to_g2_with_dst, sign, sign_raw, verify, Signature,
};

pub type G1Element = PublicKey;
Expand Down
77 changes: 77 additions & 0 deletions crates/chia-bls/src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ impl Signature {
}
}

// validate a series of public keys (G1 points) and G2 points. These points are
// paired and the resulting GT points are multiplied. If the resulting GT point
// is the identity, the function returns true, otherwise false. To validate an
// aggregate signature, include the G1 generator and the signature as one of the
// pairs.
pub fn aggregate_pairing<G1: Borrow<PublicKey>, G2: Borrow<Signature>, I>(data: I) -> bool
where
I: IntoIterator<Item = (G1, G2)>,
Expand Down Expand Up @@ -361,6 +366,8 @@ pub fn hash_to_g2_with_dst(msg: &[u8], dst: &[u8]) -> Signature {
Signature(p2)
}

// aggregate the signatures into a single one. It can then be validated using
// aggregate_verify()
pub fn aggregate<Sig: Borrow<Signature>, I>(sigs: I) -> Signature
where
I: IntoIterator<Item = Sig>,
Expand All @@ -373,6 +380,8 @@ where
ret
}

// verify a signature given a single public key and message using the augmented
// scheme, i.e. the public key is pre-pended to the message before hashed to G2.
pub fn verify<Msg: AsRef<[u8]>>(sig: &Signature, key: &PublicKey, msg: Msg) -> bool {
unsafe {
let mut pubkey_affine = MaybeUninit::<blst_p1_affine>::uninit();
Expand Down Expand Up @@ -400,6 +409,9 @@ pub fn verify<Msg: AsRef<[u8]>>(sig: &Signature, key: &PublicKey, msg: Msg) -> b
}
}

// verify an aggregate signature given all public keys and messages.
// Messages will been augmented with the public key.
// returns true if the signature is valid.
pub fn aggregate_verify<Pk: Borrow<PublicKey>, Msg: Borrow<[u8]>, I>(
sig: &Signature,
data: I,
Expand Down Expand Up @@ -475,6 +487,31 @@ where
}
}

// verify an aggregate signature by pre-paired public keys and messages.
// Messages having been augmented and hashed to G2 and then paired with the G1
// public key.
// returns true if the signature is valid.
pub fn aggregate_verify_gt<Gt: Borrow<GTElement>, I>(sig: &Signature, data: I) -> bool
where
I: IntoIterator<Item = Gt>,
{
if !sig.is_valid() {
return false;
}

let mut data = data.into_iter();
let Some(agg) = data.next() else {
return *sig == Signature::default();
};

let mut agg = agg.borrow().clone();
for gt in data {
agg *= gt.borrow();
}

agg == sig.pair(&PublicKey::generator())
}

// Signs msg using sk without augmenting the message with the public key. This
// function is used when the caller augments the message with some other public
// key
Expand All @@ -496,6 +533,8 @@ pub fn sign_raw<Msg: AsRef<[u8]>>(sk: &SecretKey, msg: Msg) -> Signature {
Signature(p2)
}

// Signs msg using sk using the augmented scheme, meaning the public key is
// pre-pended to msg befire signing.
pub fn sign<Msg: AsRef<[u8]>>(sk: &SecretKey, msg: Msg) -> Signature {
let mut aug_msg = sk.public_key().to_bytes().to_vec();
aug_msg.extend_from_slice(msg.as_ref());
Expand Down Expand Up @@ -684,6 +723,44 @@ mod tests {
assert!(aggregate_pairing(pairs.into_iter().rev()));
}

#[rstest]
fn test_aggregate_gt_signature(#[values(0, 1, 2, 3, 4, 5, 100)] num_keys: usize) {
let sk_hex = "52d75c4707e39595b27314547f9723e5530c01198af3fc5849d9a7af65631efb";
let sk = SecretKey::from_bytes(&<[u8; 32]>::from_hex(sk_hex).unwrap()).unwrap();
let msg = b"foobar";
let mut agg = Signature::default();
let mut gts = Vec::<GTElement>::new();
let mut pks = Vec::<PublicKey>::new();
for idx in 0..num_keys {
let derived = sk.derive_hardened(idx as u32);
let pk = derived.public_key();
let sig = sign(&derived, msg);
agg.aggregate(&sig);
gts.push(aug_msg_to_g2(&pk, msg).pair(&pk));
pks.push(pk);
}

assert!(aggregate_verify_gt(&agg, &gts));
assert!(aggregate_verify(&agg, pks.iter().map(|pk| (pk, &msg[..]))));

// the order of the GTElements does not matter
for _ in 0..num_keys {
gts.rotate_right(1);
pks.rotate_right(1);
assert!(aggregate_verify_gt(&agg, &gts));
assert!(aggregate_verify(&agg, pks.iter().map(|pk| (pk, &msg[..]))));
}
for _ in 0..num_keys {
gts.rotate_right(1);
pks.rotate_right(1);
assert!(!aggregate_verify_gt(&agg, &gts[1..]));
assert!(!aggregate_verify(
&agg,
pks[1..].iter().map(|pk| (pk, &msg[..]))
));
}
}

#[test]
fn test_aggregate_duplicate_signature() {
let sk_hex = "52d75c4707e39595b27314547f9723e5530c01198af3fc5849d9a7af65631efb";
Expand Down

0 comments on commit 25fd0a5

Please sign in to comment.