diff --git a/Cargo.lock b/Cargo.lock index 37c7bcdd2c3..82222a876c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4038,7 +4038,7 @@ dependencies = [ [[package]] name = "mithril-stm" -version = "0.3.42" +version = "0.3.43" dependencies = [ "bincode", "blake2 0.10.6", diff --git a/mithril-stm/CHANGELOG.md b/mithril-stm/CHANGELOG.md index 48940ac9a4a..35b5637181f 100644 --- a/mithril-stm/CHANGELOG.md +++ b/mithril-stm/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.3.43 (05-04-2025) + +### Added + +- Added a `bls_multi_signature` module and multi-signature functionality covered by its submodules. + ## 0.3.41 (20-03-2025) ### Added diff --git a/mithril-stm/Cargo.toml b/mithril-stm/Cargo.toml index 88fd067fd0a..f36d2fab915 100644 --- a/mithril-stm/Cargo.toml +++ b/mithril-stm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-stm" -version = "0.3.42" +version = "0.3.43" edition = { workspace = true } authors = { workspace = true } homepage = { workspace = true } diff --git a/mithril-stm/benches/multi_sig.rs b/mithril-stm/benches/multi_sig.rs index 7200eaf94fe..594873efbbc 100644 --- a/mithril-stm/benches/multi_sig.rs +++ b/mithril-stm/benches/multi_sig.rs @@ -1,6 +1,6 @@ use blake2::{digest::consts::U64, Blake2b, Digest}; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; -use mithril_stm::multi_sig::{Signature, SigningKey, VerificationKey}; +use mithril_stm::bls_multi_signature::{Signature, SigningKey, VerificationKey}; use rand_chacha::ChaCha20Rng; use rand_core::{RngCore, SeedableRng}; diff --git a/mithril-stm/src/bls_multi_signature/helper.rs b/mithril-stm/src/bls_multi_signature/helper.rs new file mode 100644 index 00000000000..cae2976e66e --- /dev/null +++ b/mithril-stm/src/bls_multi_signature/helper.rs @@ -0,0 +1,95 @@ +pub(crate) mod unsafe_helpers { + use crate::bls_multi_signature::{ProofOfPossession, VerificationKey}; + use crate::error::MultiSignatureError; + use crate::error::MultiSignatureError::SerializationError; + use blst::min_sig::{PublicKey as BlstVk, SecretKey as BlstSk, Signature as BlstSig}; + use blst::{ + blst_fp12, blst_fp12_finalverify, blst_p1, blst_p1_affine, blst_p1_affine_generator, + blst_p1_compress, blst_p1_from_affine, blst_p1_to_affine, blst_p1_uncompress, blst_p2, + blst_p2_affine, blst_p2_affine_generator, blst_p2_from_affine, blst_p2_to_affine, + blst_scalar, blst_sk_to_pk_in_g1, + }; + + /// Check manually if the pairing `e(g1,mvk) = e(k2,g2)` holds. + pub(crate) fn verify_pairing(vk: &VerificationKey, pop: &ProofOfPossession) -> bool { + unsafe { + let g1_p = *blst_p1_affine_generator(); + let mvk_p = std::mem::transmute::(vk.to_blst_vk()); + let ml_lhs = blst_fp12::miller_loop(&mvk_p, &g1_p); + + let mut k2_p = blst_p1_affine::default(); + blst_p1_to_affine(&mut k2_p, &pop.to_k2()); + let g2_p = *blst_p2_affine_generator(); + let ml_rhs = blst_fp12::miller_loop(&g2_p, &k2_p); + + blst_fp12_finalverify(&ml_lhs, &ml_rhs) + } + } + + pub(crate) fn compress_p1(k2: &blst_p1) -> [u8; 48] { + let mut bytes = [0u8; 48]; + unsafe { blst_p1_compress(bytes.as_mut_ptr(), k2) } + bytes + } + + pub(crate) fn uncompress_p1(bytes: &[u8]) -> Result { + unsafe { + if bytes.len() == 48 { + let mut point = blst_p1_affine::default(); + let mut out = blst_p1::default(); + blst_p1_uncompress(&mut point, bytes.as_ptr()); + blst_p1_from_affine(&mut out, &point); + Ok(out) + } else { + Err(SerializationError) + } + } + } + + pub(crate) fn scalar_to_pk_in_g1(sk: &BlstSk) -> blst_p1 { + unsafe { + let sk_scalar = std::mem::transmute::<&BlstSk, &blst_scalar>(sk); + let mut out = blst_p1::default(); + blst_sk_to_pk_in_g1(&mut out, sk_scalar); + out + } + } + + pub(crate) fn vk_from_p2_affine(vk: &VerificationKey) -> blst_p2 { + unsafe { + let mut projective_p2 = blst_p2::default(); + blst_p2_from_affine( + &mut projective_p2, + &std::mem::transmute::(vk.to_blst_vk()), + ); + projective_p2 + } + } + + pub(crate) fn sig_to_p1(sig: &BlstSig) -> blst_p1 { + unsafe { + let mut projective_p1 = blst_p1::default(); + blst_p1_from_affine( + &mut projective_p1, + &std::mem::transmute::(*sig), + ); + projective_p1 + } + } + + pub(crate) fn p2_affine_to_vk(grouped_vks: &blst_p2) -> BlstVk { + unsafe { + let mut affine_p2 = blst_p2_affine::default(); + blst_p2_to_affine(&mut affine_p2, grouped_vks); + std::mem::transmute::(affine_p2) + } + } + + pub(crate) fn p1_affine_to_sig(grouped_sigs: &blst_p1) -> BlstSig { + unsafe { + let mut affine_p1 = blst_p1_affine::default(); + blst_p1_to_affine(&mut affine_p1, grouped_sigs); + std::mem::transmute::(affine_p1) + } + } +} diff --git a/mithril-stm/src/bls_multi_signature/mod.rs b/mithril-stm/src/bls_multi_signature/mod.rs new file mode 100644 index 00000000000..0da8959d156 --- /dev/null +++ b/mithril-stm/src/bls_multi_signature/mod.rs @@ -0,0 +1,293 @@ +//! BLST Multi-signature module + +pub(super) mod helper; +mod proof_of_possession; +mod signature; +mod signing_key; +mod verification_key; + +pub use crate::bls_multi_signature::proof_of_possession::ProofOfPossession; +pub use crate::bls_multi_signature::signature::Signature; +pub use crate::bls_multi_signature::signing_key::SigningKey; +pub use crate::bls_multi_signature::verification_key::{VerificationKey, VerificationKeyPoP}; + +use serde::de::Visitor; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +/// String used to generate the proofs of possession. +pub(crate) const POP: &[u8] = b"PoP"; + +// --------------------------------------------------------------------- +// Serde implementation +// --------------------------------------------------------------------- + +macro_rules! impl_serde { + ($st:ty,$visitor:ident,$size:expr) => { + impl Serialize for $st { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeTuple; + let mut seq = serializer.serialize_tuple($size)?; + for e in self.to_bytes().iter() { + seq.serialize_element(e)?; + } + seq.end() + } + } + + impl<'de> Deserialize<'de> for $st { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct $visitor; + + impl<'de> Visitor<'de> for $visitor { + type Value = $st; + + fn expecting( + &self, + formatter: &mut ::core::fmt::Formatter, + ) -> ::core::fmt::Result { + formatter + .write_str(format!("a multi signature {}", stringify!($st)).as_str()) + } + + fn visit_seq(self, mut seq: A) -> Result<$st, A::Error> + where + A: serde::de::SeqAccess<'de>, + { + let mut bytes = [0u8; $size]; + for i in 0..$size { + bytes[i] = + seq.next_element()?.ok_or(serde::de::Error::invalid_length( + i, + &format!("expected bytes{}", $size.to_string()).as_str(), + ))?; + } + <$st>::from_bytes(&bytes).map_err(|_| { + serde::de::Error::custom( + &format!("deserialization failed [{}]", stringify!($st)).as_str(), + ) + }) + } + } + + deserializer.deserialize_tuple($size, $visitor) + } + } + }; +} +impl_serde!(SigningKey, SigningKeyVisitor, 32); +impl_serde!(VerificationKey, VerificationKeyVisitor, 96); +impl_serde!(ProofOfPossession, ProofOfPossessionVisitor, 96); +impl_serde!(Signature, SignatureVisitor, 48); + +#[cfg(test)] +mod tests { + use super::*; + use crate::bls_multi_signature::helper::unsafe_helpers::{p1_affine_to_sig, p2_affine_to_vk}; + use crate::error::{MultiSignatureError, RegisterError}; + use crate::key_reg::KeyReg; + use blst::{blst_p1, blst_p2}; + use proptest::prelude::*; + use rand_chacha::ChaCha20Rng; + use rand_core::SeedableRng; + + impl PartialEq for SigningKey { + fn eq(&self, other: &Self) -> bool { + self.to_blst_sk().to_bytes() == other.to_blst_sk().to_bytes() + } + } + + impl Eq for SigningKey {} + + proptest! { + #![proptest_config(ProptestConfig::with_cases(1000))] + + #[test] + fn test_sig( + msg in prop::collection::vec(any::(), 1..128), + seed in any::<[u8;32]>(), + ) { + let sk = SigningKey::gen(&mut ChaCha20Rng::from_seed(seed)); + let vk = VerificationKey::from(&sk); + let sig = sk.sign(&msg); + + sig.verify(&msg, &vk).unwrap(); + } + + #[test] + fn test_invalid_sig(msg in prop::collection::vec(any::(), 1..128), seed in any::<[u8;32]>()) { + let mut rng = ChaCha20Rng::from_seed(seed); + let sk1 = SigningKey::gen(&mut rng); + let vk1 = VerificationKey::from(&sk1); + let sk2 = SigningKey::gen(&mut rng); + let fake_sig = sk2.sign(&msg); + + let result = fake_sig.verify(&msg, &vk1); + assert_eq!(result, Err(MultiSignatureError::SignatureInvalid(fake_sig))); + } + + #[test] + fn test_infinity_sig(msg in prop::collection::vec(any::(), 1..128), seed in any::<[u8;32]>()) { + let mut rng = ChaCha20Rng::from_seed(seed); + let sk = SigningKey::gen(&mut rng); + let vk = VerificationKey::from(&sk); + + let p1 = blst_p1::default(); + let sig_infinity = Signature(p1_affine_to_sig(&p1)); + + let result = sig_infinity.verify(&msg, &vk); + assert_eq!(result, Err(MultiSignatureError::SignatureInfinity(sig_infinity))); + } + + #[test] + fn test_infinity_vk(seed in any::<[u8;32]>()) { + let mut rng = ChaCha20Rng::from_seed(seed); + let sk = SigningKey::gen(&mut rng); + let pop = ProofOfPossession::from(&sk); + + let p2 = blst_p2::default(); + let vk_infinity = VerificationKey(p2_affine_to_vk(&p2)); + let vkpop_infinity = VerificationKeyPoP { vk: vk_infinity, pop }; + + let result = vkpop_infinity.check(); + assert_eq!(result, Err(MultiSignatureError::VerificationKeyInfinity(Box::new(vkpop_infinity.vk)))); + } + + #[test] + fn test_keyreg_with_infinity_vk(num_sigs in 2..16usize, seed in any::<[u8;32]>()) { + let mut rng = ChaCha20Rng::from_seed(seed); + let mut kr = KeyReg::init(); + + let sk = SigningKey::gen(&mut rng); + let pop = ProofOfPossession::from(&sk); + let p2 = blst_p2::default(); + let vk_infinity = VerificationKey(p2_affine_to_vk(&p2)); + let vkpop_infinity = VerificationKeyPoP { vk: vk_infinity, pop }; + + for _ in 0..num_sigs { + let sk = SigningKey::gen(&mut rng); + let vkpop = VerificationKeyPoP::from(&sk); + let _ = kr.register(1, vkpop); + } + + let result = kr.register(1, vkpop_infinity); + assert_eq!(result, Err(RegisterError::VerificationKeyInfinity(Box::new(vkpop_infinity.vk)))); + } + + #[test] + fn test_aggregate_sig(msg in prop::collection::vec(any::(), 1..128), + num_sigs in 2..16, + seed in any::<[u8;32]>(), + ) { + let mut rng = ChaCha20Rng::from_seed(seed); + let mut mvks = Vec::new(); + let mut sigs = Vec::new(); + for _ in 0..num_sigs { + let sk = SigningKey::gen(&mut rng); + let vk = VerificationKey::from(&sk); + let sig = sk.sign(&msg); + assert!(sig.verify(&msg, &vk).is_ok()); + sigs.push(sig); + mvks.push(vk); + } + + let result = Signature::verify_aggregate(&msg, &mvks, &sigs); + assert!(result.is_ok(), "Aggregate verification failed {result:?}"); + } + + #[test] + fn test_eval_sanity_check(msg in prop::collection::vec(any::(), 1..128), + idx in any::(), + seed in any::<[u8;32]>()) { + let sk = SigningKey::gen(&mut ChaCha20Rng::from_seed(seed)); + let sig = sk.sign(&msg); + sig.eval(&msg, idx); + } + + #[test] + fn serialize_deserialize_vk(seed in any::()) { + let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed); + let sk = SigningKey::gen(&mut rng); + let vk = VerificationKey::from(&sk); + let vk_bytes = vk.to_bytes(); + let vk2 = VerificationKey::from_bytes(&vk_bytes).unwrap(); + assert_eq!(vk, vk2); + let vkpop = VerificationKeyPoP::from(&sk); + let vkpop_bytes = vkpop.to_bytes(); + let vkpop2: VerificationKeyPoP = VerificationKeyPoP::from_bytes(&vkpop_bytes).unwrap(); + assert_eq!(vkpop, vkpop2); + + // Now we test serde + let encoded = bincode::serialize(&vk).unwrap(); + assert_eq!(encoded, vk_bytes); + let decoded: VerificationKey = bincode::deserialize(&encoded).unwrap(); + assert_eq!(vk, decoded); + let encoded = bincode::serialize(&vkpop).unwrap(); + let decoded: VerificationKeyPoP = bincode::deserialize(&encoded).unwrap(); + assert_eq!(vkpop, decoded); + } + + #[test] + fn serialize_deserialize_sk(seed in any::()) { + let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed); + let sk = SigningKey::gen(&mut rng); + let sk_bytes: [u8; 32] = sk.to_bytes(); + let sk2 = SigningKey::from_bytes(&sk_bytes).unwrap(); + assert_eq!(sk, sk2); + + // Now we test serde + let encoded = bincode::serialize(&sk).unwrap(); + let decoded: SigningKey = bincode::deserialize(&encoded).unwrap(); + assert_eq!(sk, decoded); + + // See that it is consistent with raw serialisation + let decoded_bytes: SigningKey = bincode::deserialize(&sk_bytes).unwrap(); + assert_eq!(sk, decoded_bytes); + } + + #[test] + fn batch_verify(num_batches in 2..10usize, + seed in any::<[u8;32]>(), + ) { + let mut rng = ChaCha20Rng::from_seed(seed); + let num_sigs = 10; + let mut batch_msgs = Vec::new(); + let mut batch_vk = Vec::new(); + let mut batch_sig = Vec::new(); + for _ in 0..num_batches { + let mut msg = [0u8; 32]; + rng.fill_bytes(&mut msg); + let mut mvks = Vec::new(); + let mut sigs = Vec::new(); + for _ in 0..num_sigs { + let sk = SigningKey::gen(&mut rng); + let vk = VerificationKey::from(&sk); + let sig = sk.sign(&msg); + sigs.push(sig); + mvks.push(vk); + } + assert!(Signature::verify_aggregate(&msg, &mvks, &sigs).is_ok()); + let (agg_vk, agg_sig) = Signature::aggregate(&mvks, &sigs).unwrap(); + batch_msgs.push(msg.to_vec()); + batch_vk.push(agg_vk); + batch_sig.push(agg_sig); + } + assert!(Signature::batch_verify_aggregates(&batch_msgs, &batch_vk, &batch_sig).is_ok()); + + // If we have an invalid signature, the batch verification will fail + let mut msg = [0u8; 32]; + rng.fill_bytes(&mut msg); + let sk = SigningKey::gen(&mut rng); + let fake_sig = sk.sign(&msg); + batch_sig[0] = fake_sig; + + let batch_result = Signature::batch_verify_aggregates(&batch_msgs, &batch_vk, &batch_sig); + assert_eq!(batch_result, Err(MultiSignatureError::BatchInvalid)); + } + } +} diff --git a/mithril-stm/src/bls_multi_signature/proof_of_possession.rs b/mithril-stm/src/bls_multi_signature/proof_of_possession.rs new file mode 100644 index 00000000000..46121cbe546 --- /dev/null +++ b/mithril-stm/src/bls_multi_signature/proof_of_possession.rs @@ -0,0 +1,69 @@ +use crate::bls_multi_signature::helper::unsafe_helpers::{ + compress_p1, scalar_to_pk_in_g1, uncompress_p1, +}; +use crate::bls_multi_signature::signing_key::SigningKey; +use crate::bls_multi_signature::POP; +use crate::error::{blst_err_to_mithril, MultiSignatureError}; +use blst::blst_p1; +use blst::min_sig::Signature as BlstSig; + +/// MultiSig proof of possession, which contains two elements from G1. However, +/// the two elements have different types: `k1` is represented as a BlstSig +/// as it has the same structure, and this facilitates its verification. On +/// the other hand, `k2` is a G1 point, as it does not share structure with +/// the BLS signature, and we need to have an ad-hoc verification mechanism. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ProofOfPossession { + k1: BlstSig, + k2: blst_p1, +} + +impl ProofOfPossession { + /// Convert to a 96 byte string. + /// + /// # Layout + /// The layout of a `MspPoP` encoding is + /// * K1 (G1 point) + /// * K2 (G1 point) + pub fn to_bytes(self) -> [u8; 96] { + let mut pop_bytes = [0u8; 96]; + pop_bytes[..48].copy_from_slice(&self.k1.to_bytes()); + + pop_bytes[48..].copy_from_slice(&compress_p1(&self.k2)[..]); + pop_bytes + } + + /// Deserialize a byte string to a `PublicKeyPoP`. + pub fn from_bytes(bytes: &[u8]) -> Result { + let k1 = match BlstSig::from_bytes(&bytes[..48]) { + Ok(key) => key, + Err(e) => { + return Err(blst_err_to_mithril(e, None, None) + .expect_err("If it passed, blst returns and error different to SUCCESS.")) + } + }; + + let k2 = uncompress_p1(&bytes[48..96])?; + + Ok(Self { k1, k2 }) + } + + pub(crate) fn to_k1(self) -> BlstSig { + self.k1 + } + + pub(crate) fn to_k2(self) -> blst_p1 { + self.k2 + } +} + +impl From<&SigningKey> for ProofOfPossession { + /// Convert a secret key into an `MspPoP`. This is performed by computing + /// `k1 = H_G1(b"PoP" || mvk)` and `k2 = g1 * sk` where `H_G1` hashes into + /// `G1` and `g1` is the generator in `G1`. + fn from(sk: &SigningKey) -> Self { + let k1 = sk.to_blst_sk().sign(POP, &[], &[]); + let k2 = scalar_to_pk_in_g1(&sk.to_blst_sk()); + Self { k1, k2 } + } +} diff --git a/mithril-stm/src/bls_multi_signature/signature.rs b/mithril-stm/src/bls_multi_signature/signature.rs new file mode 100644 index 00000000000..70e1fb11174 --- /dev/null +++ b/mithril-stm/src/bls_multi_signature/signature.rs @@ -0,0 +1,203 @@ +use crate::bls_multi_signature::helper::unsafe_helpers::{ + p1_affine_to_sig, p2_affine_to_vk, sig_to_p1, vk_from_p2_affine, +}; +use crate::bls_multi_signature::verification_key::VerificationKey; +use crate::error::{blst_err_to_mithril, MultiSignatureError}; +use crate::stm::Index; +use blake2::Blake2b; +use blake2::{Blake2b512, Digest}; +use blst::min_sig::{AggregateSignature, PublicKey as BlstVk, Signature as BlstSig}; +use blst::{blst_p1, blst_p2, p1_affines, p2_affines}; +use digest::consts::U16; +use std::cmp::Ordering; +use std::iter::Sum; + +/// MultiSig signature, which is a wrapper over the `BlstSig` type. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Signature(pub BlstSig); + +impl Signature { + /// Verify a signature against a verification key. + pub fn verify(&self, msg: &[u8], mvk: &VerificationKey) -> Result<(), MultiSignatureError> { + blst_err_to_mithril( + self.0.validate(true).map_or_else( + |e| e, + |_| { + self.0 + .verify(false, msg, &[], &[], &mvk.to_blst_vk(), false) + }, + ), + Some(*self), + None, + ) + } + + /// Dense mapping function indexed by the index to be evaluated. + /// We hash the signature to produce a 64 bytes integer. + /// The return value of this function refers to + /// `ev = H("map" || msg || index || σ) <- MSP.Eval(msg,index,σ)` given in paper. + pub fn eval(&self, msg: &[u8], index: Index) -> [u8; 64] { + let hasher = Blake2b512::new() + .chain_update(b"map") + .chain_update(msg) + .chain_update(index.to_le_bytes()) + .chain_update(self.to_bytes()) + .finalize(); + + let mut output = [0u8; 64]; + output.copy_from_slice(hasher.as_slice()); + + output + } + + /// Convert an `Signature` to its compressed byte representation. + pub fn to_bytes(self) -> [u8; 48] { + self.0.to_bytes() + } + + /// Convert a string of bytes into a `MspSig`. + /// + /// # Error + /// Returns an error if the byte string does not represent a point in the curve. + pub fn from_bytes(bytes: &[u8]) -> Result { + match BlstSig::sig_validate(&bytes[..48], true) { + Ok(sig) => Ok(Self(sig)), + Err(e) => Err(blst_err_to_mithril(e, None, None) + .expect_err("If deserialization is not successful, blst returns and error different to SUCCESS.")) + } + } + + /// Compare two signatures. Used for PartialOrd impl, used to rank signatures. The comparison + /// function can be anything, as long as it is consistent across different nodes. + fn cmp_msp_sig(&self, other: &Self) -> Ordering { + let self_bytes = self.to_bytes(); + let other_bytes = other.to_bytes(); + let mut result = Ordering::Equal; + + for (i, j) in self_bytes.iter().zip(other_bytes.iter()) { + result = i.cmp(j); + if result != Ordering::Equal { + return result; + } + } + result + } + + /// Aggregate a slice of verification keys and Signatures by first hashing the + /// signatures into random scalars, and multiplying the signature and verification + /// key with the resulting value. This follows the steps defined in Figure 6, + /// `Aggregate` step. + pub fn aggregate( + vks: &[VerificationKey], + sigs: &[Signature], + ) -> Result<(VerificationKey, Signature), MultiSignatureError> { + if vks.len() != sigs.len() || vks.is_empty() { + return Err(MultiSignatureError::AggregateSignatureInvalid); + } + + if vks.len() < 2 { + return Ok((vks[0], sigs[0])); + } + + let mut hashed_sigs = Blake2b::::new(); + for sig in sigs { + hashed_sigs.update(sig.to_bytes()); + } + + // First we generate the scalars + let mut scalars = Vec::with_capacity(vks.len() * 128); + let mut signatures = Vec::with_capacity(vks.len()); + for (index, sig) in sigs.iter().enumerate() { + let mut hasher = hashed_sigs.clone(); + hasher.update(index.to_be_bytes()); + signatures.push(sig.0); + scalars.extend_from_slice(hasher.finalize().as_slice()); + } + + let transmuted_vks: Vec = vks.iter().map(vk_from_p2_affine).collect(); + let transmuted_sigs: Vec = signatures.iter().map(sig_to_p1).collect(); + + let grouped_vks = p2_affines::from(transmuted_vks.as_slice()); + let grouped_sigs = p1_affines::from(transmuted_sigs.as_slice()); + + let aggr_vk: BlstVk = p2_affine_to_vk(&grouped_vks.mult(&scalars, 128)); + let aggr_sig: BlstSig = p1_affine_to_sig(&grouped_sigs.mult(&scalars, 128)); + + Ok((VerificationKey(aggr_vk), Signature(aggr_sig))) + } + + /// Verify a set of signatures with their corresponding verification keys using the + /// aggregation mechanism of Figure 6. + pub fn verify_aggregate( + msg: &[u8], + vks: &[VerificationKey], + sigs: &[Signature], + ) -> Result<(), MultiSignatureError> { + let (aggr_vk, aggr_sig) = Self::aggregate(vks, sigs)?; + + blst_err_to_mithril( + aggr_sig + .0 + .verify(false, msg, &[], &[], &aggr_vk.to_blst_vk(), false), + Some(aggr_sig), + None, + ) + } + + /// Batch verify several sets of signatures with their corresponding verification keys. + #[cfg(feature = "batch-verify-aggregates")] + pub fn batch_verify_aggregates( + msgs: &[Vec], + vks: &[VerificationKey], + sigs: &[Signature], + ) -> Result<(), MultiSignatureError> { + let batched_sig: BlstSig = match AggregateSignature::aggregate( + &(sigs.iter().map(|sig| &sig.0).collect::>()), + false, + ) { + Ok(sig) => BlstSig::from_aggregate(&sig), + Err(e) => return blst_err_to_mithril(e, None, None), + }; + + let p2_vks: Vec = vks.iter().map(|vk| vk.to_blst_vk()).collect(); + let p2_vks_ref: Vec<&BlstVk> = p2_vks.iter().collect(); + let slice_msgs = msgs + .iter() + .map(|msg| msg.as_slice()) + .collect::>(); + + blst_err_to_mithril( + batched_sig.aggregate_verify(false, &slice_msgs, &[], &p2_vks_ref, false), + None, + None, + ) + .map_err(|_| MultiSignatureError::BatchInvalid) + } +} + +impl<'a> Sum<&'a Self> for Signature { + fn sum(iter: I) -> Self + where + I: Iterator, + { + let signatures: Vec<&BlstSig> = iter.map(|x| &x.0).collect(); + assert!(!signatures.is_empty(), "One cannot add an empty vector"); + let aggregate = AggregateSignature::aggregate(&signatures, false) + .expect("An MspSig is always a valid signature. This function only fails if signatures is empty or if the signatures are invalid, none of which can happen.") + .to_signature(); + + Self(aggregate) + } +} + +impl PartialOrd for Signature { + fn partial_cmp(&self, other: &Self) -> Option { + Some(std::cmp::Ord::cmp(self, other)) + } +} + +impl Ord for Signature { + fn cmp(&self, other: &Self) -> Ordering { + self.cmp_msp_sig(other) + } +} diff --git a/mithril-stm/src/bls_multi_signature/signing_key.rs b/mithril-stm/src/bls_multi_signature/signing_key.rs new file mode 100644 index 00000000000..45a938b3dcb --- /dev/null +++ b/mithril-stm/src/bls_multi_signature/signing_key.rs @@ -0,0 +1,47 @@ +use crate::bls_multi_signature::signature::Signature; +use crate::error::{blst_err_to_mithril, MultiSignatureError}; +use blst::min_sig::SecretKey as BlstSk; +use rand_core::{CryptoRng, RngCore}; + +/// MultiSig secret key, which is a wrapper over the BlstSk type from the blst +/// library. +#[derive(Debug, Clone)] +pub struct SigningKey(pub BlstSk); + +impl SigningKey { + /// Generate a secret key + pub fn gen(rng: &mut (impl RngCore + CryptoRng)) -> Self { + let mut ikm = [0u8; 32]; + rng.fill_bytes(&mut ikm); + SigningKey( + BlstSk::key_gen(&ikm, &[]) + .expect("Error occurs when the length of ikm < 32. This will not happen here."), + ) + } + + /// Sign a message with the given secret key + pub fn sign(&self, msg: &[u8]) -> Signature { + Signature(self.0.sign(msg, &[], &[])) + } + + /// Convert the secret key into byte string. + pub fn to_bytes(&self) -> [u8; 32] { + self.0.to_bytes() + } + + /// Convert a string of bytes into a `SigningKey`. + /// + /// # Error + /// Fails if the byte string represents a scalar larger than the group order. + pub fn from_bytes(bytes: &[u8]) -> Result { + match BlstSk::from_bytes(&bytes[..32]) { + Ok(sk) => Ok(Self(sk)), + Err(e) => Err(blst_err_to_mithril(e, None, None) + .expect_err("If deserialization is not successful, blst returns and error different to SUCCESS.")) + } + } + + pub(crate) fn to_blst_sk(&self) -> BlstSk { + self.0.clone() + } +} diff --git a/mithril-stm/src/bls_multi_signature/verification_key.rs b/mithril-stm/src/bls_multi_signature/verification_key.rs new file mode 100644 index 00000000000..97c07b1bb48 --- /dev/null +++ b/mithril-stm/src/bls_multi_signature/verification_key.rs @@ -0,0 +1,186 @@ +use crate::bls_multi_signature::helper::unsafe_helpers::verify_pairing; +use crate::bls_multi_signature::proof_of_possession::ProofOfPossession; +use crate::bls_multi_signature::signing_key::SigningKey; +use crate::bls_multi_signature::POP; +use crate::error::{blst_err_to_mithril, MultiSignatureError}; +use blst::min_sig::{AggregatePublicKey, PublicKey as BlstVk}; +use blst::BLST_ERROR; +use serde::{Deserialize, Serialize}; +use std::{ + cmp::Ordering, + fmt::{Display, Formatter}, + hash::{Hash, Hasher}, + iter::Sum, +}; + +/// MultiSig verification key, which is a wrapper over the BlstVk (element in G2) +/// from the blst library. +#[derive(Debug, Clone, Copy, Default)] +pub struct VerificationKey(pub BlstVk); + +impl VerificationKey { + /// Convert an `VerificationKey` to its compressed byte representation. + pub fn to_bytes(self) -> [u8; 96] { + self.0.to_bytes() + } + + /// Convert a compressed byte string into a `VerificationKey`. + /// + /// # Error + /// This function fails if the bytes do not represent a compressed point of the prime + /// order subgroup of the curve Bls12-381. + pub fn from_bytes(bytes: &[u8]) -> Result { + match BlstVk::key_validate(&bytes[..96]) { + Ok(vk) => Ok(Self(vk)), + Err(e) => Err(blst_err_to_mithril(e, None, None) + .expect_err("If deserialization is not successful, blst returns and error different to SUCCESS.")) + } + } + + /// Compare two `VerificationKey`. Used for PartialOrd impl, used to order signatures. The comparison + /// function can be anything, as long as it is consistent. + fn cmp_msp_mvk(&self, other: &VerificationKey) -> Ordering { + let self_bytes = self.to_bytes(); + let other_bytes = other.to_bytes(); + let mut result = Ordering::Equal; + + for (i, j) in self_bytes.iter().zip(other_bytes.iter()) { + result = i.cmp(j); + if result != Ordering::Equal { + return result; + } + } + + result + } + + pub(crate) fn to_blst_vk(self) -> BlstVk { + self.0 + } +} + +impl Display for VerificationKey { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.to_bytes()) + } +} + +impl Hash for VerificationKey { + fn hash(&self, state: &mut H) { + Hash::hash_slice(&self.to_bytes(), state) + } +} + +impl PartialEq for VerificationKey { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for VerificationKey {} + +impl PartialOrd for VerificationKey { + fn partial_cmp(&self, other: &Self) -> Option { + Some(std::cmp::Ord::cmp(self, other)) + } +} + +impl Ord for VerificationKey { + fn cmp(&self, other: &Self) -> Ordering { + self.cmp_msp_mvk(other) + } +} + +impl<'a> Sum<&'a Self> for VerificationKey { + fn sum(iter: I) -> Self + where + I: Iterator, + { + let keys: Vec<&BlstVk> = iter.map(|x| &x.0).collect(); + + assert!(!keys.is_empty(), "One cannot add an empty vector"); + let aggregate_key = AggregatePublicKey::aggregate(&keys, false) + .expect("An MspMvk is always a valid key. This function only fails if keys is empty or if the keys are invalid, none of which can happen.") + .to_public_key(); + + Self(aggregate_key) + } +} + +impl From<&SigningKey> for VerificationKey { + /// Convert a secret key into an `MspMvk`. This is performed by computing + /// `MspMvk = g2 * sk`, where `g2` is the generator in G2. We can use the + /// blst built-in function `sk_to_pk`. + fn from(sk: &SigningKey) -> Self { + VerificationKey(sk.to_blst_sk().sk_to_pk()) + } +} + +/// MultiSig public key, contains the verification key and the proof of possession. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub struct VerificationKeyPoP { + /// The verification key. + pub vk: VerificationKey, + /// Proof of Possession. + pub pop: ProofOfPossession, +} + +impl VerificationKeyPoP { + /// if `e(k1,g2) = e(H_G1("PoP" || mvk),mvk)` and `e(g1,mvk) = e(k2,g2)` + /// are both true, return 1. The first part is a signature verification + /// of message "PoP", while the second we need to compute the pairing + /// manually. + // If we are really looking for performance improvements, we can combine the + // two final exponentiations (for verifying k1 and k2) into a single one. + pub fn check(&self) -> Result<(), MultiSignatureError> { + match self.vk.to_blst_vk().validate() { + Ok(_) => { + let result = verify_pairing(&self.vk, &self.pop); + if !(self + .pop + .to_k1() + .verify(false, POP, &[], &[], &self.vk.to_blst_vk(), false) + == BLST_ERROR::BLST_SUCCESS + && result) + { + return Err(MultiSignatureError::KeyInvalid(Box::new(*self))); + } + Ok(()) + } + Err(e) => blst_err_to_mithril(e, None, Some(self.vk)), + } + } + + /// Convert to a 144 byte string. + /// + /// # Layout + /// The layout of a `PublicKeyPoP` encoding is + /// * Public key + /// * Proof of Possession + pub fn to_bytes(self) -> [u8; 192] { + let mut vkpop_bytes = [0u8; 192]; + vkpop_bytes[..96].copy_from_slice(&self.vk.to_bytes()); + vkpop_bytes[96..].copy_from_slice(&self.pop.to_bytes()); + vkpop_bytes + } + + /// Deserialize a byte string to a `PublicKeyPoP`. + pub fn from_bytes(bytes: &[u8]) -> Result { + let mvk = VerificationKey::from_bytes(&bytes[..96])?; + + let pop = ProofOfPossession::from_bytes(&bytes[96..])?; + + Ok(Self { vk: mvk, pop }) + } +} + +impl From<&SigningKey> for VerificationKeyPoP { + /// Convert a secret key into a `VerificationKeyPoP` by simply converting to a + /// `MspMvk` and `MspPoP`. + fn from(sk: &SigningKey) -> Self { + Self { + vk: sk.into(), + pop: sk.into(), + } + } +} diff --git a/mithril-stm/src/error.rs b/mithril-stm/src/error.rs index 754f7e6aaa8..2ba12c2eb03 100644 --- a/mithril-stm/src/error.rs +++ b/mithril-stm/src/error.rs @@ -3,7 +3,7 @@ use crate::merkle_tree::{BatchPath, Path}; use blake2::digest::{Digest, FixedOutput}; use { - crate::multi_sig::{Signature, VerificationKey, VerificationKeyPoP}, + crate::bls_multi_signature::{Signature, VerificationKey, VerificationKeyPoP}, blst::BLST_ERROR, }; diff --git a/mithril-stm/src/key_reg.rs b/mithril-stm/src/key_reg.rs index 96bb7abc782..20f142d051c 100644 --- a/mithril-stm/src/key_reg.rs +++ b/mithril-stm/src/key_reg.rs @@ -1,8 +1,8 @@ //! Key registration functionality. use super::stm::Stake; +use crate::bls_multi_signature::{VerificationKey, VerificationKeyPoP}; use crate::error::RegisterError; use crate::merkle_tree::{MTLeaf, MerkleTree}; -use crate::multi_sig::{VerificationKey, VerificationKeyPoP}; use blake2::digest::{Digest, FixedOutput}; use std::collections::hash_map::Entry; use std::collections::HashMap; @@ -81,7 +81,7 @@ pub struct ClosedKeyReg { #[cfg(test)] mod tests { use super::*; - use crate::multi_sig::SigningKey; + use crate::bls_multi_signature::SigningKey; use blake2::{digest::consts::U32, Blake2b}; use proptest::collection::vec; use proptest::prelude::*; diff --git a/mithril-stm/src/lib.rs b/mithril-stm/src/lib.rs index ef07dcb8450..bb66b877895 100644 --- a/mithril-stm/src/lib.rs +++ b/mithril-stm/src/lib.rs @@ -16,7 +16,7 @@ pub use crate::error::{ }; #[cfg(feature = "benchmark-internals")] -pub mod multi_sig; +pub mod bls_multi_signature; #[cfg(not(feature = "benchmark-internals"))] -mod multi_sig; +mod bls_multi_signature; diff --git a/mithril-stm/src/merkle_tree/leaf.rs b/mithril-stm/src/merkle_tree/leaf.rs index 4448383ac4c..808735cf5c7 100644 --- a/mithril-stm/src/merkle_tree/leaf.rs +++ b/mithril-stm/src/merkle_tree/leaf.rs @@ -1,5 +1,5 @@ +use crate::bls_multi_signature::VerificationKey; use crate::error::MerkleTreeError; -use crate::multi_sig::VerificationKey; use crate::stm::{Stake, StmVerificationKey}; use blake2::Blake2b; use digest::consts::U32; diff --git a/mithril-stm/src/merkle_tree/tree.rs b/mithril-stm/src/merkle_tree/tree.rs index 9b50a6f8a7c..ed164d9c376 100644 --- a/mithril-stm/src/merkle_tree/tree.rs +++ b/mithril-stm/src/merkle_tree/tree.rs @@ -221,7 +221,7 @@ impl MerkleTree { #[cfg(test)] mod tests { use super::*; - use crate::multi_sig::VerificationKey; + use crate::bls_multi_signature::VerificationKey; use blake2::{digest::consts::U32, Blake2b}; use proptest::collection::vec; use proptest::prelude::*; diff --git a/mithril-stm/src/multi_sig.rs b/mithril-stm/src/multi_sig.rs deleted file mode 100644 index d34e15ef02c..00000000000 --- a/mithril-stm/src/multi_sig.rs +++ /dev/null @@ -1,840 +0,0 @@ -//! Base multi-signature scheme, used as a primitive for STM. -//! See Section 2.4 of [the paper](https://eprint.iacr.org/2021/916). -//! This module uses the `blst` library as a backend for pairings. - -use crate::error::{blst_err_to_mithril, MultiSignatureError}; -use crate::stm::Index; -use blake2::{digest::consts::U16, Blake2b, Blake2b512, Digest}; -use unsafe_helpers::*; - -// We use `min_sig` resulting in signatures of 48 bytes and public keys of -// 96. We can switch that around if desired by using `min_vk`. -use blst::min_sig::{ - AggregatePublicKey, AggregateSignature, PublicKey as BlstVk, SecretKey as BlstSk, - Signature as BlstSig, -}; -use blst::{blst_p1, blst_p2, p1_affines, p2_affines, BLST_ERROR}; - -use rand_core::{CryptoRng, RngCore}; -use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; -use std::{ - cmp::Ordering, - fmt::{Display, Formatter}, - hash::{Hash, Hasher}, - iter::Sum, -}; -/// String used to generate the proofs of possession. -const POP: &[u8] = b"PoP"; - -/// MultiSig secret key, which is a wrapper over the BlstSk type from the blst -/// library. -#[derive(Debug, Clone)] -pub struct SigningKey(BlstSk); - -impl SigningKey { - /// Generate a secret key - pub fn gen(rng: &mut (impl RngCore + CryptoRng)) -> Self { - let mut ikm = [0u8; 32]; - rng.fill_bytes(&mut ikm); - SigningKey( - BlstSk::key_gen(&ikm, &[]) - .expect("Error occurs when the length of ikm < 32. This will not happen here."), - ) - } - - /// Sign a message with the given secret key - pub fn sign(&self, msg: &[u8]) -> Signature { - Signature(self.0.sign(msg, &[], &[])) - } - - /// Convert the secret key into byte string. - pub fn to_bytes(&self) -> [u8; 32] { - self.0.to_bytes() - } - - /// Convert a string of bytes into a `SigningKey`. - /// - /// # Error - /// Fails if the byte string represents a scalar larger than the group order. - pub fn from_bytes(bytes: &[u8]) -> Result { - match BlstSk::from_bytes(&bytes[..32]) { - Ok(sk) => Ok(Self(sk)), - Err(e) => Err(blst_err_to_mithril(e, None, None) - .expect_err("If deserialization is not successful, blst returns and error different to SUCCESS.")) - } - } -} - -/// MultiSig verification key, which is a wrapper over the BlstVk (element in G2) -/// from the blst library. -#[derive(Debug, Clone, Copy, Default)] -pub struct VerificationKey(BlstVk); - -impl VerificationKey { - /// Convert an `VerificationKey` to its compressed byte representation. - pub fn to_bytes(self) -> [u8; 96] { - self.0.to_bytes() - } - - /// Convert a compressed byte string into a `VerificationKey`. - /// - /// # Error - /// This function fails if the bytes do not represent a compressed point of the prime - /// order subgroup of the curve Bls12-381. - pub fn from_bytes(bytes: &[u8]) -> Result { - match BlstVk::key_validate(&bytes[..96]) { - Ok(vk) => Ok(Self(vk)), - Err(e) => Err(blst_err_to_mithril(e, None, None) - .expect_err("If deserialization is not successful, blst returns and error different to SUCCESS.")) - } - } - - /// Compare two `VerificationKey`. Used for PartialOrd impl, used to order signatures. The comparison - /// function can be anything, as long as it is consistent. - fn cmp_msp_mvk(&self, other: &VerificationKey) -> Ordering { - let self_bytes = self.to_bytes(); - let other_bytes = other.to_bytes(); - let mut result = Ordering::Equal; - - for (i, j) in self_bytes.iter().zip(other_bytes.iter()) { - result = i.cmp(j); - if result != Ordering::Equal { - return result; - } - } - - result - } -} - -impl Display for VerificationKey { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.to_bytes()) - } -} - -impl Hash for VerificationKey { - fn hash(&self, state: &mut H) { - Hash::hash_slice(&self.to_bytes(), state) - } -} - -impl PartialEq for VerificationKey { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for VerificationKey {} - -impl PartialOrd for VerificationKey { - fn partial_cmp(&self, other: &Self) -> Option { - Some(std::cmp::Ord::cmp(self, other)) - } -} - -impl Ord for VerificationKey { - fn cmp(&self, other: &Self) -> Ordering { - self.cmp_msp_mvk(other) - } -} - -impl<'a> Sum<&'a Self> for VerificationKey { - fn sum(iter: I) -> Self - where - I: Iterator, - { - let keys: Vec<&BlstVk> = iter.map(|x| &x.0).collect(); - - assert!(!keys.is_empty(), "One cannot add an empty vector"); - let aggregate_key = AggregatePublicKey::aggregate(&keys, false) - .expect("An MspMvk is always a valid key. This function only fails if keys is empty or if the keys are invalid, none of which can happen.") - .to_public_key(); - - Self(aggregate_key) - } -} - -impl From<&SigningKey> for VerificationKey { - /// Convert a secret key into an `MspMvk`. This is performed by computing - /// `MspMvk = g2 * sk`, where `g2` is the generator in G2. We can use the - /// blst built-in function `sk_to_pk`. - fn from(sk: &SigningKey) -> Self { - VerificationKey(sk.0.sk_to_pk()) - } -} - -/// MultiSig proof of possession, which contains two elements from G1. However, -/// the two elements have different types: `k1` is represented as a BlstSig -/// as it has the same structure, and this facilitates its verification. On -/// the other hand, `k2` is a G1 point, as it does not share structure with -/// the BLS signature, and we need to have an ad-hoc verification mechanism. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ProofOfPossession { - k1: BlstSig, - k2: blst_p1, -} - -impl ProofOfPossession { - /// Convert to a 96 byte string. - /// - /// # Layout - /// The layout of a `MspPoP` encoding is - /// * K1 (G1 point) - /// * K2 (G1 point) - pub fn to_bytes(self) -> [u8; 96] { - let mut pop_bytes = [0u8; 96]; - pop_bytes[..48].copy_from_slice(&self.k1.to_bytes()); - - pop_bytes[48..].copy_from_slice(&compress_p1(&self.k2)[..]); - pop_bytes - } - - /// Deserialize a byte string to a `PublicKeyPoP`. - pub fn from_bytes(bytes: &[u8]) -> Result { - let k1 = match BlstSig::from_bytes(&bytes[..48]) { - Ok(key) => key, - Err(e) => { - return Err(blst_err_to_mithril(e, None, None) - .expect_err("If it passed, blst returns and error different to SUCCESS.")) - } - }; - - let k2 = uncompress_p1(&bytes[48..96])?; - - Ok(Self { k1, k2 }) - } -} - -impl From<&SigningKey> for ProofOfPossession { - /// Convert a secret key into an `MspPoP`. This is performed by computing - /// `k1 = H_G1(b"PoP" || mvk)` and `k2 = g1 * sk` where `H_G1` hashes into - /// `G1` and `g1` is the generator in `G1`. - fn from(sk: &SigningKey) -> Self { - let k1 = sk.0.sign(POP, &[], &[]); - let k2 = scalar_to_pk_in_g1(sk); - - Self { k1, k2 } - } -} - -/// MultiSig public key, contains the verification key and the proof of possession. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct VerificationKeyPoP { - /// The verification key. - pub vk: VerificationKey, - /// Proof of Possession. - pub pop: ProofOfPossession, -} - -impl VerificationKeyPoP { - /// if `e(k1,g2) = e(H_G1("PoP" || mvk),mvk)` and `e(g1,mvk) = e(k2,g2)` - /// are both true, return 1. The first part is a signature verification - /// of message "PoP", while the second we need to compute the pairing - /// manually. - // If we are really looking for performance improvements, we can combine the - // two final exponentiations (for verifying k1 and k2) into a single one. - pub fn check(&self) -> Result<(), MultiSignatureError> { - match self.vk.0.validate() { - Ok(_) => { - let result = verify_pairing(&self.vk, &self.pop); - if !(self.pop.k1.verify(false, POP, &[], &[], &self.vk.0, false) - == BLST_ERROR::BLST_SUCCESS - && result) - { - return Err(MultiSignatureError::KeyInvalid(Box::new(*self))); - } - Ok(()) - } - Err(e) => blst_err_to_mithril(e, None, Some(self.vk)), - } - } - - /// Convert to a 144 byte string. - /// - /// # Layout - /// The layout of a `PublicKeyPoP` encoding is - /// * Public key - /// * Proof of Possession - pub fn to_bytes(self) -> [u8; 192] { - let mut vkpop_bytes = [0u8; 192]; - vkpop_bytes[..96].copy_from_slice(&self.vk.to_bytes()); - vkpop_bytes[96..].copy_from_slice(&self.pop.to_bytes()); - vkpop_bytes - } - - /// Deserialize a byte string to a `PublicKeyPoP`. - pub fn from_bytes(bytes: &[u8]) -> Result { - let mvk = VerificationKey::from_bytes(&bytes[..96])?; - - let pop = ProofOfPossession::from_bytes(&bytes[96..])?; - - Ok(Self { vk: mvk, pop }) - } -} - -impl From<&SigningKey> for VerificationKeyPoP { - /// Convert a secret key into a `VerificationKeyPoP` by simply converting to a - /// `MspMvk` and `MspPoP`. - fn from(sk: &SigningKey) -> Self { - Self { - vk: sk.into(), - pop: sk.into(), - } - } -} - -/// MultiSig signature, which is a wrapper over the `BlstSig` type. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Signature(BlstSig); - -impl Signature { - /// Verify a signature against a verification key. - pub fn verify(&self, msg: &[u8], mvk: &VerificationKey) -> Result<(), MultiSignatureError> { - blst_err_to_mithril( - self.0.validate(true).map_or_else( - |e| e, - |_| self.0.verify(false, msg, &[], &[], &mvk.0, false), - ), - Some(*self), - None, - ) - } - - /// Dense mapping function indexed by the index to be evaluated. - /// We hash the signature to produce a 64 bytes integer. - /// The return value of this function refers to - /// `ev = H("map" || msg || index || σ) <- MSP.Eval(msg,index,σ)` given in paper. - pub fn eval(&self, msg: &[u8], index: Index) -> [u8; 64] { - let hasher = Blake2b512::new() - .chain_update(b"map") - .chain_update(msg) - .chain_update(index.to_le_bytes()) - .chain_update(self.to_bytes()) - .finalize(); - - let mut output = [0u8; 64]; - output.copy_from_slice(hasher.as_slice()); - - output - } - - /// Convert an `Signature` to its compressed byte representation. - pub fn to_bytes(self) -> [u8; 48] { - self.0.to_bytes() - } - - /// Convert a string of bytes into a `MspSig`. - /// - /// # Error - /// Returns an error if the byte string does not represent a point in the curve. - pub fn from_bytes(bytes: &[u8]) -> Result { - match BlstSig::sig_validate(&bytes[..48], true) { - Ok(sig) => Ok(Self(sig)), - Err(e) => Err(blst_err_to_mithril(e, None, None) - .expect_err("If deserialization is not successful, blst returns and error different to SUCCESS.")) - } - } - - /// Compare two signatures. Used for PartialOrd impl, used to rank signatures. The comparison - /// function can be anything, as long as it is consistent across different nodes. - fn cmp_msp_sig(&self, other: &Self) -> Ordering { - let self_bytes = self.to_bytes(); - let other_bytes = other.to_bytes(); - let mut result = Ordering::Equal; - - for (i, j) in self_bytes.iter().zip(other_bytes.iter()) { - result = i.cmp(j); - if result != Ordering::Equal { - return result; - } - } - result - } - - /// Aggregate a slice of verification keys and Signatures by first hashing the - /// signatures into random scalars, and multiplying the signature and verification - /// key with the resulting value. This follows the steps defined in Figure 6, - /// `Aggregate` step. - pub fn aggregate( - vks: &[VerificationKey], - sigs: &[Signature], - ) -> Result<(VerificationKey, Signature), MultiSignatureError> { - if vks.len() != sigs.len() || vks.is_empty() { - return Err(MultiSignatureError::AggregateSignatureInvalid); - } - - if vks.len() < 2 { - return Ok((vks[0], sigs[0])); - } - - let mut hashed_sigs = Blake2b::::new(); - for sig in sigs { - hashed_sigs.update(sig.to_bytes()); - } - - // First we generate the scalars - let mut scalars = Vec::with_capacity(vks.len() * 128); - let mut signatures = Vec::with_capacity(vks.len()); - for (index, sig) in sigs.iter().enumerate() { - let mut hasher = hashed_sigs.clone(); - hasher.update(index.to_be_bytes()); - signatures.push(sig.0); - scalars.extend_from_slice(hasher.finalize().as_slice()); - } - - let transmuted_vks: Vec = vks.iter().map(vk_from_p2_affine).collect(); - let transmuted_sigs: Vec = signatures.iter().map(sig_to_p1).collect(); - - let grouped_vks = p2_affines::from(transmuted_vks.as_slice()); - let grouped_sigs = p1_affines::from(transmuted_sigs.as_slice()); - - let aggr_vk: BlstVk = p2_affine_to_vk(&grouped_vks.mult(&scalars, 128)); - let aggr_sig: BlstSig = p1_affine_to_sig(&grouped_sigs.mult(&scalars, 128)); - - Ok((VerificationKey(aggr_vk), Signature(aggr_sig))) - } - - /// Verify a set of signatures with their corresponding verification keys using the - /// aggregation mechanism of Figure 6. - pub fn verify_aggregate( - msg: &[u8], - vks: &[VerificationKey], - sigs: &[Signature], - ) -> Result<(), MultiSignatureError> { - let (aggr_vk, aggr_sig) = Self::aggregate(vks, sigs)?; - - blst_err_to_mithril( - aggr_sig.0.verify(false, msg, &[], &[], &aggr_vk.0, false), - Some(aggr_sig), - None, - ) - } - - /// Batch verify several sets of signatures with their corresponding verification keys. - #[cfg(feature = "batch-verify-aggregates")] - pub fn batch_verify_aggregates( - msgs: &[Vec], - vks: &[VerificationKey], - sigs: &[Signature], - ) -> Result<(), MultiSignatureError> { - let batched_sig: BlstSig = match AggregateSignature::aggregate( - &(sigs.iter().map(|sig| &sig.0).collect::>()), - false, - ) { - Ok(sig) => BlstSig::from_aggregate(&sig), - Err(e) => return blst_err_to_mithril(e, None, None), - }; - - let p2_vks: Vec<&BlstVk> = vks.iter().map(|vk| &vk.0).collect(); - let slice_msgs = msgs - .iter() - .map(|msg| msg.as_slice()) - .collect::>(); - - blst_err_to_mithril( - batched_sig.aggregate_verify(false, &slice_msgs, &[], &p2_vks, false), - None, - None, - ) - .map_err(|_| MultiSignatureError::BatchInvalid) - } -} - -impl<'a> Sum<&'a Self> for Signature { - fn sum(iter: I) -> Self - where - I: Iterator, - { - let signatures: Vec<&BlstSig> = iter.map(|x| &x.0).collect(); - assert!(!signatures.is_empty(), "One cannot add an empty vector"); - let aggregate = AggregateSignature::aggregate(&signatures, false) - .expect("An MspSig is always a valid signature. This function only fails if signatures is empty or if the signatures are invalid, none of which can happen.") - .to_signature(); - - Self(aggregate) - } -} - -impl PartialOrd for Signature { - fn partial_cmp(&self, other: &Self) -> Option { - Some(std::cmp::Ord::cmp(self, other)) - } -} - -impl Ord for Signature { - fn cmp(&self, other: &Self) -> Ordering { - self.cmp_msp_sig(other) - } -} - -// --------------------------------------------------------------------- -// Serde implementation -// --------------------------------------------------------------------- - -macro_rules! impl_serde { - ($st:ty,$visitor:ident,$size:expr) => { - impl Serialize for $st { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - use serde::ser::SerializeTuple; - let mut seq = serializer.serialize_tuple($size)?; - for e in self.to_bytes().iter() { - seq.serialize_element(e)?; - } - seq.end() - } - } - - impl<'de> Deserialize<'de> for $st { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct $visitor; - - impl<'de> Visitor<'de> for $visitor { - type Value = $st; - - fn expecting( - &self, - formatter: &mut ::core::fmt::Formatter, - ) -> ::core::fmt::Result { - formatter - .write_str(format!("a multi signature {}", stringify!($st)).as_str()) - } - - fn visit_seq(self, mut seq: A) -> Result<$st, A::Error> - where - A: serde::de::SeqAccess<'de>, - { - let mut bytes = [0u8; $size]; - for i in 0..$size { - bytes[i] = - seq.next_element()?.ok_or(serde::de::Error::invalid_length( - i, - &format!("expected bytes{}", $size.to_string()).as_str(), - ))?; - } - <$st>::from_bytes(&bytes).map_err(|_| { - serde::de::Error::custom( - &format!("deserialization failed [{}]", stringify!($st)).as_str(), - ) - }) - } - } - - deserializer.deserialize_tuple($size, $visitor) - } - } - }; -} -impl_serde!(SigningKey, SigningKeyVisitor, 32); -impl_serde!(VerificationKey, VerificationKeyVisitor, 96); -impl_serde!(ProofOfPossession, ProofOfPossessionVisitor, 96); -impl_serde!(Signature, SignatureVisitor, 48); - -// --------------------------------------------------------------------- -// Unsafe helpers -// --------------------------------------------------------------------- - -mod unsafe_helpers { - use super::*; - use crate::error::MultiSignatureError::SerializationError; - use blst::{ - blst_fp12, blst_fp12_finalverify, blst_p1_affine, blst_p1_affine_generator, - blst_p1_compress, blst_p1_from_affine, blst_p1_to_affine, blst_p1_uncompress, - blst_p2_affine, blst_p2_affine_generator, blst_p2_from_affine, blst_p2_to_affine, - blst_scalar, blst_sk_to_pk_in_g1, - }; - - /// Check manually if the pairing `e(g1,mvk) = e(k2,g2)` holds. - pub(crate) fn verify_pairing(vk: &VerificationKey, pop: &ProofOfPossession) -> bool { - unsafe { - let g1_p = *blst_p1_affine_generator(); - let mvk_p = std::mem::transmute::(vk.0); - let ml_lhs = blst_fp12::miller_loop(&mvk_p, &g1_p); - - let mut k2_p = blst_p1_affine::default(); - blst_p1_to_affine(&mut k2_p, &pop.k2); - let g2_p = *blst_p2_affine_generator(); - let ml_rhs = blst_fp12::miller_loop(&g2_p, &k2_p); - - blst_fp12_finalverify(&ml_lhs, &ml_rhs) - } - } - - pub(crate) fn compress_p1(k2: &blst_p1) -> [u8; 48] { - let mut bytes = [0u8; 48]; - unsafe { blst_p1_compress(bytes.as_mut_ptr(), k2) } - bytes - } - - pub(crate) fn uncompress_p1(bytes: &[u8]) -> Result { - unsafe { - if bytes.len() == 48 { - let mut point = blst_p1_affine::default(); - let mut out = blst_p1::default(); - blst_p1_uncompress(&mut point, bytes.as_ptr()); - blst_p1_from_affine(&mut out, &point); - Ok(out) - } else { - Err(SerializationError) - } - } - } - - pub(crate) fn scalar_to_pk_in_g1(sk: &SigningKey) -> blst_p1 { - unsafe { - let sk_scalar = std::mem::transmute::<&BlstSk, &blst_scalar>(&sk.0); - let mut out = blst_p1::default(); - blst_sk_to_pk_in_g1(&mut out, sk_scalar); - out - } - } - - pub(crate) fn vk_from_p2_affine(vk: &VerificationKey) -> blst_p2 { - unsafe { - let mut projective_p2 = blst_p2::default(); - blst_p2_from_affine( - &mut projective_p2, - &std::mem::transmute::(vk.0), - ); - projective_p2 - } - } - - pub(crate) fn sig_to_p1(sig: &BlstSig) -> blst_p1 { - unsafe { - let mut projective_p1 = blst_p1::default(); - blst_p1_from_affine( - &mut projective_p1, - &std::mem::transmute::(*sig), - ); - projective_p1 - } - } - - pub(crate) fn p2_affine_to_vk(grouped_vks: &blst_p2) -> BlstVk { - unsafe { - let mut affine_p2 = blst_p2_affine::default(); - blst_p2_to_affine(&mut affine_p2, grouped_vks); - std::mem::transmute::(affine_p2) - } - } - - pub(crate) fn p1_affine_to_sig(grouped_sigs: &blst_p1) -> BlstSig { - unsafe { - let mut affine_p1 = blst_p1_affine::default(); - blst_p1_to_affine(&mut affine_p1, grouped_sigs); - std::mem::transmute::(affine_p1) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::error::RegisterError; - use crate::key_reg::KeyReg; - use proptest::prelude::*; - use rand_chacha::ChaCha20Rng; - use rand_core::SeedableRng; - - impl PartialEq for SigningKey { - fn eq(&self, other: &Self) -> bool { - self.0.to_bytes() == other.0.to_bytes() - } - } - - impl Eq for SigningKey {} - - proptest! { - #![proptest_config(ProptestConfig::with_cases(1000))] - - #[test] - fn test_sig( - msg in prop::collection::vec(any::(), 1..128), - seed in any::<[u8;32]>(), - ) { - let sk = SigningKey::gen(&mut ChaCha20Rng::from_seed(seed)); - let vk = VerificationKey::from(&sk); - let sig = sk.sign(&msg); - - sig.verify(&msg, &vk).unwrap(); - } - - #[test] - fn test_invalid_sig(msg in prop::collection::vec(any::(), 1..128), seed in any::<[u8;32]>()) { - let mut rng = ChaCha20Rng::from_seed(seed); - let sk1 = SigningKey::gen(&mut rng); - let vk1 = VerificationKey::from(&sk1); - let sk2 = SigningKey::gen(&mut rng); - let fake_sig = sk2.sign(&msg); - - let result = fake_sig.verify(&msg, &vk1); - assert_eq!(result, Err(MultiSignatureError::SignatureInvalid(fake_sig))); - } - - #[test] - fn test_infinity_sig(msg in prop::collection::vec(any::(), 1..128), seed in any::<[u8;32]>()) { - let mut rng = ChaCha20Rng::from_seed(seed); - let sk = SigningKey::gen(&mut rng); - let vk = VerificationKey::from(&sk); - - let p1 = blst_p1::default(); - let sig_infinity = Signature(p1_affine_to_sig(&p1)); - - let result = sig_infinity.verify(&msg, &vk); - assert_eq!(result, Err(MultiSignatureError::SignatureInfinity(sig_infinity))); - } - - #[test] - fn test_infinity_vk(seed in any::<[u8;32]>()) { - let mut rng = ChaCha20Rng::from_seed(seed); - let sk = SigningKey::gen(&mut rng); - let pop = ProofOfPossession::from(&sk); - - let p2 = blst_p2::default(); - let vk_infinity = VerificationKey(p2_affine_to_vk(&p2)); - let vkpop_infinity = VerificationKeyPoP { vk: vk_infinity, pop }; - - let result = vkpop_infinity.check(); - assert_eq!(result, Err(MultiSignatureError::VerificationKeyInfinity(Box::new(vkpop_infinity.vk)))); - } - - #[test] - fn test_keyreg_with_infinity_vk(num_sigs in 2..16usize, seed in any::<[u8;32]>()) { - let mut rng = ChaCha20Rng::from_seed(seed); - let mut kr = KeyReg::init(); - - let sk = SigningKey::gen(&mut rng); - let pop = ProofOfPossession::from(&sk); - let p2 = blst_p2::default(); - let vk_infinity = VerificationKey(p2_affine_to_vk(&p2)); - let vkpop_infinity = VerificationKeyPoP { vk: vk_infinity, pop }; - - for _ in 0..num_sigs { - let sk = SigningKey::gen(&mut rng); - let vkpop = VerificationKeyPoP::from(&sk); - let _ = kr.register(1, vkpop); - } - - let result = kr.register(1, vkpop_infinity); - assert_eq!(result, Err(RegisterError::VerificationKeyInfinity(Box::new(vkpop_infinity.vk)))); - } - - #[test] - fn test_aggregate_sig(msg in prop::collection::vec(any::(), 1..128), - num_sigs in 2..16, - seed in any::<[u8;32]>(), - ) { - let mut rng = ChaCha20Rng::from_seed(seed); - let mut mvks = Vec::new(); - let mut sigs = Vec::new(); - for _ in 0..num_sigs { - let sk = SigningKey::gen(&mut rng); - let vk = VerificationKey::from(&sk); - let sig = sk.sign(&msg); - assert!(sig.verify(&msg, &vk).is_ok()); - sigs.push(sig); - mvks.push(vk); - } - - let result = Signature::verify_aggregate(&msg, &mvks, &sigs); - assert!(result.is_ok(), "Aggregate verification failed {result:?}"); - } - - #[test] - fn test_eval_sanity_check(msg in prop::collection::vec(any::(), 1..128), - idx in any::(), - seed in any::<[u8;32]>()) { - let sk = SigningKey::gen(&mut ChaCha20Rng::from_seed(seed)); - let sig = sk.sign(&msg); - sig.eval(&msg, idx); - } - - #[test] - fn serialize_deserialize_vk(seed in any::()) { - let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed); - let sk = SigningKey::gen(&mut rng); - let vk = VerificationKey::from(&sk); - let vk_bytes = vk.to_bytes(); - let vk2 = VerificationKey::from_bytes(&vk_bytes).unwrap(); - assert_eq!(vk, vk2); - let vkpop = VerificationKeyPoP::from(&sk); - let vkpop_bytes = vkpop.to_bytes(); - let vkpop2: VerificationKeyPoP = VerificationKeyPoP::from_bytes(&vkpop_bytes).unwrap(); - assert_eq!(vkpop, vkpop2); - - // Now we test serde - let encoded = bincode::serialize(&vk).unwrap(); - assert_eq!(encoded, vk_bytes); - let decoded: VerificationKey = bincode::deserialize(&encoded).unwrap(); - assert_eq!(vk, decoded); - let encoded = bincode::serialize(&vkpop).unwrap(); - let decoded: VerificationKeyPoP = bincode::deserialize(&encoded).unwrap(); - assert_eq!(vkpop, decoded); - } - - #[test] - fn serialize_deserialize_sk(seed in any::()) { - let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed); - let sk = SigningKey::gen(&mut rng); - let sk_bytes: [u8; 32] = sk.to_bytes(); - let sk2 = SigningKey::from_bytes(&sk_bytes).unwrap(); - assert_eq!(sk, sk2); - - // Now we test serde - let encoded = bincode::serialize(&sk).unwrap(); - let decoded: SigningKey = bincode::deserialize(&encoded).unwrap(); - assert_eq!(sk, decoded); - - // See that it is consistent with raw serialisation - let decoded_bytes: SigningKey = bincode::deserialize(&sk_bytes).unwrap(); - assert_eq!(sk, decoded_bytes); - } - - #[test] - fn batch_verify(num_batches in 2..10usize, - seed in any::<[u8;32]>(), - ) { - let mut rng = ChaCha20Rng::from_seed(seed); - let num_sigs = 10; - let mut batch_msgs = Vec::new(); - let mut batch_vk = Vec::new(); - let mut batch_sig = Vec::new(); - for _ in 0..num_batches { - let mut msg = [0u8; 32]; - rng.fill_bytes(&mut msg); - let mut mvks = Vec::new(); - let mut sigs = Vec::new(); - for _ in 0..num_sigs { - let sk = SigningKey::gen(&mut rng); - let vk = VerificationKey::from(&sk); - let sig = sk.sign(&msg); - sigs.push(sig); - mvks.push(vk); - } - assert!(Signature::verify_aggregate(&msg, &mvks, &sigs).is_ok()); - let (agg_vk, agg_sig) = Signature::aggregate(&mvks, &sigs).unwrap(); - batch_msgs.push(msg.to_vec()); - batch_vk.push(agg_vk); - batch_sig.push(agg_sig); - } - assert!(Signature::batch_verify_aggregates(&batch_msgs, &batch_vk, &batch_sig).is_ok()); - - // If we have an invalid signature, the batch verification will fail - let mut msg = [0u8; 32]; - rng.fill_bytes(&mut msg); - let sk = SigningKey::gen(&mut rng); - let fake_sig = sk.sign(&msg); - batch_sig[0] = fake_sig; - - let batch_result = Signature::batch_verify_aggregates(&batch_msgs, &batch_vk, &batch_sig); - assert_eq!(batch_result, Err(MultiSignatureError::BatchInvalid)); - } - } -} diff --git a/mithril-stm/src/stm.rs b/mithril-stm/src/stm.rs index 73c23da0777..0baba7467cf 100644 --- a/mithril-stm/src/stm.rs +++ b/mithril-stm/src/stm.rs @@ -106,6 +106,7 @@ //! # } //! ``` +use crate::bls_multi_signature::{Signature, SigningKey, VerificationKey, VerificationKeyPoP}; use crate::eligibility_check::ev_lt_phi; use crate::error::{ AggregationError, CoreVerifierError, RegisterError, StmAggregateSignatureError, @@ -113,7 +114,6 @@ use crate::error::{ }; use crate::key_reg::{ClosedKeyReg, RegParty}; use crate::merkle_tree::{BatchPath, MTLeaf, MerkleTreeCommitmentBatchCompat}; -use crate::multi_sig::{Signature, SigningKey, VerificationKey, VerificationKeyPoP}; use blake2::digest::{Digest, FixedOutput}; use rand_core::{CryptoRng, RngCore}; use serde::ser::SerializeTuple;