From 0f85dc662e0ccf6bdcd047379677fc2be8011117 Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Thu, 20 Nov 2025 19:31:35 +0300 Subject: [PATCH 1/4] membershi_commitment module containing merkle_tree --- mithril-stm/src/aggregate_signature/aggregate_key.rs | 2 +- mithril-stm/src/aggregate_signature/basic_verifier.rs | 2 +- mithril-stm/src/aggregate_signature/mod.rs | 2 +- mithril-stm/src/aggregate_signature/proof/concatenation.rs | 2 +- mithril-stm/src/aggregate_signature/signature.rs | 2 +- mithril-stm/src/key_registration.rs | 2 +- mithril-stm/src/lib.rs | 3 ++- .../src/{ => membership_commitment}/merkle_tree/commitment.rs | 4 +++- .../src/{ => membership_commitment}/merkle_tree/leaf.rs | 0 .../src/{ => membership_commitment}/merkle_tree/mod.rs | 0 .../src/{ => membership_commitment}/merkle_tree/path.rs | 0 .../src/{ => membership_commitment}/merkle_tree/tree.rs | 2 +- mithril-stm/src/membership_commitment/mod.rs | 3 +++ 13 files changed, 15 insertions(+), 9 deletions(-) rename mithril-stm/src/{ => membership_commitment}/merkle_tree/commitment.rs (98%) rename mithril-stm/src/{ => membership_commitment}/merkle_tree/leaf.rs (100%) rename mithril-stm/src/{ => membership_commitment}/merkle_tree/mod.rs (100%) rename mithril-stm/src/{ => membership_commitment}/merkle_tree/path.rs (100%) rename mithril-stm/src/{ => membership_commitment}/merkle_tree/tree.rs (99%) create mode 100644 mithril-stm/src/membership_commitment/mod.rs diff --git a/mithril-stm/src/aggregate_signature/aggregate_key.rs b/mithril-stm/src/aggregate_signature/aggregate_key.rs index e7be9328cf6..215a8e9d9dd 100644 --- a/mithril-stm/src/aggregate_signature/aggregate_key.rs +++ b/mithril-stm/src/aggregate_signature/aggregate_key.rs @@ -1,7 +1,7 @@ use blake2::digest::{Digest, FixedOutput}; use serde::{Deserialize, Serialize}; -use crate::merkle_tree::{MerkleBatchPath, MerkleTreeBatchCommitment}; +use crate::membership_commitment::{MerkleBatchPath, MerkleTreeBatchCommitment}; use crate::{ClosedKeyRegistration, Stake}; /// Stm aggregate key (batch compatible), which contains the merkle tree commitment and the total stake of the system. diff --git a/mithril-stm/src/aggregate_signature/basic_verifier.rs b/mithril-stm/src/aggregate_signature/basic_verifier.rs index cad572f0176..97d2d614fc0 100644 --- a/mithril-stm/src/aggregate_signature/basic_verifier.rs +++ b/mithril-stm/src/aggregate_signature/basic_verifier.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use crate::bls_multi_signature::{BlsSignature, BlsVerificationKey}; use crate::key_registration::RegisteredParty; -use crate::merkle_tree::MerkleTreeLeaf; +use crate::membership_commitment::MerkleTreeLeaf; use crate::{ AggregationError, Index, Parameters, SingleSignature, SingleSignatureWithRegisteredParty, Stake, StmResult, diff --git a/mithril-stm/src/aggregate_signature/mod.rs b/mithril-stm/src/aggregate_signature/mod.rs index 65b009189ee..866fae66bb9 100644 --- a/mithril-stm/src/aggregate_signature/mod.rs +++ b/mithril-stm/src/aggregate_signature/mod.rs @@ -28,7 +28,7 @@ mod tests { Initializer, KeyRegistration, Parameters, Signer, SingleSignature, SingleSignatureWithRegisteredParty, Stake, bls_multi_signature::BlsVerificationKey, }; - use crate::{StmResult, merkle_tree::MerkleBatchPath}; + use crate::{StmResult, membership_commitment::MerkleBatchPath}; type Sig = AggregateSignature; type D = Blake2b; diff --git a/mithril-stm/src/aggregate_signature/proof/concatenation.rs b/mithril-stm/src/aggregate_signature/proof/concatenation.rs index f619b359efe..577773e0693 100644 --- a/mithril-stm/src/aggregate_signature/proof/concatenation.rs +++ b/mithril-stm/src/aggregate_signature/proof/concatenation.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::aggregate_signature::clerk::Clerk; use crate::bls_multi_signature::{BlsSignature, BlsVerificationKey}; use crate::key_registration::RegisteredParty; -use crate::merkle_tree::MerkleBatchPath; +use crate::membership_commitment::MerkleBatchPath; use crate::{ AggregateSignatureError, AggregateVerificationKey, BasicVerifier, Parameters, SingleSignature, SingleSignatureWithRegisteredParty, StmResult, diff --git a/mithril-stm/src/aggregate_signature/signature.rs b/mithril-stm/src/aggregate_signature/signature.rs index dc8c6fb3a77..0e7c40c4206 100644 --- a/mithril-stm/src/aggregate_signature/signature.rs +++ b/mithril-stm/src/aggregate_signature/signature.rs @@ -7,7 +7,7 @@ use blake2::digest::{Digest, FixedOutput}; use serde::{Deserialize, Serialize}; use crate::error::AggregateSignatureError; -use crate::merkle_tree::MerkleBatchPath; +use crate::membership_commitment::MerkleBatchPath; use crate::{AggregateVerificationKey, Parameters, StmResult}; use super::ConcatenationProof; diff --git a/mithril-stm/src/key_registration.rs b/mithril-stm/src/key_registration.rs index 319325ba463..11c2d828ee3 100644 --- a/mithril-stm/src/key_registration.rs +++ b/mithril-stm/src/key_registration.rs @@ -9,7 +9,7 @@ use blake2::digest::{Digest, FixedOutput}; use crate::bls_multi_signature::{BlsVerificationKey, BlsVerificationKeyProofOfPossession}; use crate::error::RegisterError; -use crate::merkle_tree::{MerkleTree, MerkleTreeLeaf}; +use crate::membership_commitment::{MerkleTree, MerkleTreeLeaf}; use crate::{Stake, StmResult}; /// Stores a registered party with its public key and the associated stake. diff --git a/mithril-stm/src/lib.rs b/mithril-stm/src/lib.rs index 9030ebd3993..d47648990fe 100644 --- a/mithril-stm/src/lib.rs +++ b/mithril-stm/src/lib.rs @@ -116,7 +116,8 @@ mod bls_multi_signature; mod eligibility_check; mod error; mod key_registration; -mod merkle_tree; +mod membership_commitment; +// mod merkle_tree; mod parameters; mod participant; mod single_signature; diff --git a/mithril-stm/src/merkle_tree/commitment.rs b/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs similarity index 98% rename from mithril-stm/src/merkle_tree/commitment.rs rename to mithril-stm/src/membership_commitment/merkle_tree/commitment.rs index 131a02c8ebb..2144d18d629 100644 --- a/mithril-stm/src/merkle_tree/commitment.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/commitment.rs @@ -5,7 +5,9 @@ use serde::{Deserialize, Serialize}; use crate::StmResult; use crate::error::MerkleTreeError; -use crate::merkle_tree::{MerkleBatchPath, MerklePath, MerkleTreeLeaf, parent, sibling}; +use crate::membership_commitment::merkle_tree::{ + MerkleBatchPath, MerklePath, MerkleTreeLeaf, parent, sibling, +}; use anyhow::{Context, anyhow}; /// `MerkleTree` commitment. /// This structure differs from `MerkleTree` in that it does not contain all elements, which are not always necessary. diff --git a/mithril-stm/src/merkle_tree/leaf.rs b/mithril-stm/src/membership_commitment/merkle_tree/leaf.rs similarity index 100% rename from mithril-stm/src/merkle_tree/leaf.rs rename to mithril-stm/src/membership_commitment/merkle_tree/leaf.rs diff --git a/mithril-stm/src/merkle_tree/mod.rs b/mithril-stm/src/membership_commitment/merkle_tree/mod.rs similarity index 100% rename from mithril-stm/src/merkle_tree/mod.rs rename to mithril-stm/src/membership_commitment/merkle_tree/mod.rs diff --git a/mithril-stm/src/merkle_tree/path.rs b/mithril-stm/src/membership_commitment/merkle_tree/path.rs similarity index 100% rename from mithril-stm/src/merkle_tree/path.rs rename to mithril-stm/src/membership_commitment/merkle_tree/path.rs diff --git a/mithril-stm/src/merkle_tree/tree.rs b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs similarity index 99% rename from mithril-stm/src/merkle_tree/tree.rs rename to mithril-stm/src/membership_commitment/merkle_tree/tree.rs index 68c947f1504..c0ededbf624 100644 --- a/mithril-stm/src/merkle_tree/tree.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::StmResult; use crate::error::MerkleTreeError; -use crate::merkle_tree::{ +use crate::membership_commitment::merkle_tree::{ MerkleBatchPath, MerklePath, MerkleTreeBatchCommitment, MerkleTreeCommitment, MerkleTreeLeaf, left_child, parent, right_child, sibling, }; diff --git a/mithril-stm/src/membership_commitment/mod.rs b/mithril-stm/src/membership_commitment/mod.rs new file mode 100644 index 00000000000..3f2e7669206 --- /dev/null +++ b/mithril-stm/src/membership_commitment/mod.rs @@ -0,0 +1,3 @@ +mod merkle_tree; + +pub use merkle_tree::*; From 954a1d2d4add20f9652953b20bad46b4c299ed41 Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Thu, 20 Nov 2025 20:14:30 +0300 Subject: [PATCH 2/4] signature_scheme module containing bls and schnorr --- .../src/aggregate_signature/basic_verifier.rs | 2 +- mithril-stm/src/aggregate_signature/mod.rs | 2 +- .../proof/concatenation.rs | 2 +- .../src/aggregate_signature/signature.rs | 2 +- mithril-stm/src/error.rs | 2 +- mithril-stm/src/key_registration.rs | 4 +- mithril-stm/src/lib.rs | 5 +- .../membership_commitment/merkle_tree/leaf.rs | 2 +- .../membership_commitment/merkle_tree/tree.rs | 2 +- mithril-stm/src/participant/initializer.rs | 2 +- mithril-stm/src/participant/signer.rs | 2 +- .../bls_multi_signature/helper.rs | 100 ++++++ .../bls_multi_signature/mod.rs | 307 ++++++++++++++++++ .../proof_of_possession.rs | 105 ++++++ .../bls_multi_signature/signature.rs | 247 ++++++++++++++ .../bls_multi_signature/signing_key.rs | 83 +++++ .../bls_multi_signature/verification_key.rs | 257 +++++++++++++++ mithril-stm/src/signature_scheme/mod.rs | 4 + .../signature_scheme/schnorr_signature/mod.rs | 1 + mithril-stm/src/single_signature/signature.rs | 4 +- .../signature_registered_party.rs | 2 +- 21 files changed, 1120 insertions(+), 17 deletions(-) create mode 100644 mithril-stm/src/signature_scheme/bls_multi_signature/helper.rs create mode 100644 mithril-stm/src/signature_scheme/bls_multi_signature/mod.rs create mode 100644 mithril-stm/src/signature_scheme/bls_multi_signature/proof_of_possession.rs create mode 100644 mithril-stm/src/signature_scheme/bls_multi_signature/signature.rs create mode 100644 mithril-stm/src/signature_scheme/bls_multi_signature/signing_key.rs create mode 100644 mithril-stm/src/signature_scheme/bls_multi_signature/verification_key.rs create mode 100644 mithril-stm/src/signature_scheme/mod.rs create mode 100644 mithril-stm/src/signature_scheme/schnorr_signature/mod.rs diff --git a/mithril-stm/src/aggregate_signature/basic_verifier.rs b/mithril-stm/src/aggregate_signature/basic_verifier.rs index 97d2d614fc0..c8145684211 100644 --- a/mithril-stm/src/aggregate_signature/basic_verifier.rs +++ b/mithril-stm/src/aggregate_signature/basic_verifier.rs @@ -1,9 +1,9 @@ use anyhow::{Context, anyhow}; use std::collections::{BTreeMap, HashMap, HashSet}; -use crate::bls_multi_signature::{BlsSignature, BlsVerificationKey}; use crate::key_registration::RegisteredParty; use crate::membership_commitment::MerkleTreeLeaf; +use crate::signature_scheme::{BlsSignature, BlsVerificationKey}; use crate::{ AggregationError, Index, Parameters, SingleSignature, SingleSignatureWithRegisteredParty, Stake, StmResult, diff --git a/mithril-stm/src/aggregate_signature/mod.rs b/mithril-stm/src/aggregate_signature/mod.rs index 866fae66bb9..aab7afcd003 100644 --- a/mithril-stm/src/aggregate_signature/mod.rs +++ b/mithril-stm/src/aggregate_signature/mod.rs @@ -26,7 +26,7 @@ mod tests { use crate::{ AggregateSignature, AggregateSignatureType, AggregationError, BasicVerifier, Clerk, Initializer, KeyRegistration, Parameters, Signer, SingleSignature, - SingleSignatureWithRegisteredParty, Stake, bls_multi_signature::BlsVerificationKey, + SingleSignatureWithRegisteredParty, Stake, signature_scheme::BlsVerificationKey, }; use crate::{StmResult, membership_commitment::MerkleBatchPath}; diff --git a/mithril-stm/src/aggregate_signature/proof/concatenation.rs b/mithril-stm/src/aggregate_signature/proof/concatenation.rs index 577773e0693..63642ac98cc 100644 --- a/mithril-stm/src/aggregate_signature/proof/concatenation.rs +++ b/mithril-stm/src/aggregate_signature/proof/concatenation.rs @@ -4,9 +4,9 @@ use blake2::digest::{Digest, FixedOutput}; use serde::{Deserialize, Serialize}; use crate::aggregate_signature::clerk::Clerk; -use crate::bls_multi_signature::{BlsSignature, BlsVerificationKey}; use crate::key_registration::RegisteredParty; use crate::membership_commitment::MerkleBatchPath; +use crate::signature_scheme::{BlsSignature, BlsVerificationKey}; use crate::{ AggregateSignatureError, AggregateVerificationKey, BasicVerifier, Parameters, SingleSignature, SingleSignatureWithRegisteredParty, StmResult, diff --git a/mithril-stm/src/aggregate_signature/signature.rs b/mithril-stm/src/aggregate_signature/signature.rs index 0e7c40c4206..390f7f8ea36 100644 --- a/mithril-stm/src/aggregate_signature/signature.rs +++ b/mithril-stm/src/aggregate_signature/signature.rs @@ -211,7 +211,7 @@ mod tests { use rand_chacha::ChaCha20Rng; use rand_core::SeedableRng; - use crate::bls_multi_signature::{BlsSigningKey, BlsVerificationKeyProofOfPossession}; + use crate::signature_scheme::{BlsSigningKey, BlsVerificationKeyProofOfPossession}; use crate::{ AggregateSignature, AggregateSignatureType, Clerk, ClosedKeyRegistration, KeyRegistration, Parameters, Signer, diff --git a/mithril-stm/src/error.rs b/mithril-stm/src/error.rs index 800777d2514..b8e01ddb581 100644 --- a/mithril-stm/src/error.rs +++ b/mithril-stm/src/error.rs @@ -4,7 +4,7 @@ use blst::BLST_ERROR; use crate::StmResult; use crate::aggregate_signature::AggregateSignatureType; -use crate::bls_multi_signature::{ +use crate::signature_scheme::{ BlsSignature, BlsVerificationKey, BlsVerificationKeyProofOfPossession, }; diff --git a/mithril-stm/src/key_registration.rs b/mithril-stm/src/key_registration.rs index 11c2d828ee3..55655221cb0 100644 --- a/mithril-stm/src/key_registration.rs +++ b/mithril-stm/src/key_registration.rs @@ -7,9 +7,9 @@ use std::{ use anyhow::anyhow; use blake2::digest::{Digest, FixedOutput}; -use crate::bls_multi_signature::{BlsVerificationKey, BlsVerificationKeyProofOfPossession}; use crate::error::RegisterError; use crate::membership_commitment::{MerkleTree, MerkleTreeLeaf}; +use crate::signature_scheme::{BlsVerificationKey, BlsVerificationKeyProofOfPossession}; use crate::{Stake, StmResult}; /// Stores a registered party with its public key and the associated stake. @@ -94,7 +94,7 @@ mod tests { use rand_chacha::ChaCha20Rng; use rand_core::SeedableRng; - use crate::bls_multi_signature::BlsSigningKey; + use crate::signature_scheme::BlsSigningKey; use super::*; diff --git a/mithril-stm/src/lib.rs b/mithril-stm/src/lib.rs index d47648990fe..3bca5aaafce 100644 --- a/mithril-stm/src/lib.rs +++ b/mithril-stm/src/lib.rs @@ -112,14 +112,13 @@ //! ``` mod aggregate_signature; -mod bls_multi_signature; mod eligibility_check; mod error; mod key_registration; mod membership_commitment; -// mod merkle_tree; mod parameters; mod participant; +mod signature_scheme; mod single_signature; pub use aggregate_signature::{ @@ -134,7 +133,7 @@ pub use participant::{Initializer, Signer, VerificationKey, VerificationKeyProof pub use single_signature::{SingleSignature, SingleSignatureWithRegisteredParty}; #[cfg(feature = "benchmark-internals")] -pub use bls_multi_signature::{ +pub use signature_scheme::{ BlsProofOfPossession, BlsSignature, BlsSigningKey, BlsVerificationKey, BlsVerificationKeyProofOfPossession, }; diff --git a/mithril-stm/src/membership_commitment/merkle_tree/leaf.rs b/mithril-stm/src/membership_commitment/merkle_tree/leaf.rs index de01655d850..b5def0edf27 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/leaf.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/leaf.rs @@ -3,8 +3,8 @@ use std::cmp::Ordering; use serde::{Deserialize, Serialize}; use crate::StmResult; -use crate::bls_multi_signature::BlsVerificationKey; use crate::error::MerkleTreeError; +use crate::signature_scheme::BlsVerificationKey; use crate::{Stake, VerificationKey}; /// The values that are committed in the Merkle Tree. /// Namely, a verified `VerificationKey` and its corresponding stake. diff --git a/mithril-stm/src/membership_commitment/merkle_tree/tree.rs b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs index c0ededbf624..12cc7bfd3b3 100644 --- a/mithril-stm/src/membership_commitment/merkle_tree/tree.rs +++ b/mithril-stm/src/membership_commitment/merkle_tree/tree.rs @@ -270,7 +270,7 @@ impl MerkleTree { #[cfg(test)] mod tests { use super::*; - use crate::bls_multi_signature::BlsVerificationKey; + use crate::signature_scheme::BlsVerificationKey; use blake2::{Blake2b, digest::consts::U32}; use proptest::collection::vec; use proptest::prelude::*; diff --git a/mithril-stm/src/participant/initializer.rs b/mithril-stm/src/participant/initializer.rs index 5471e25adfb..992443602f7 100644 --- a/mithril-stm/src/participant/initializer.rs +++ b/mithril-stm/src/participant/initializer.rs @@ -3,7 +3,7 @@ use digest::FixedOutput; use rand_core::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; -use crate::bls_multi_signature::{BlsSigningKey, BlsVerificationKeyProofOfPossession}; +use crate::signature_scheme::{BlsSigningKey, BlsVerificationKeyProofOfPossession}; use crate::{Parameters, RegisterError, Signer, Stake}; use crate::{StmResult, key_registration::*}; use anyhow::anyhow; diff --git a/mithril-stm/src/participant/signer.rs b/mithril-stm/src/participant/signer.rs index da9a06f8d60..ca9fa091ca3 100644 --- a/mithril-stm/src/participant/signer.rs +++ b/mithril-stm/src/participant/signer.rs @@ -1,8 +1,8 @@ use blake2::digest::{Digest, FixedOutput}; -use crate::bls_multi_signature::{BlsSignature, BlsSigningKey, BlsVerificationKey}; use crate::eligibility_check::is_lottery_won; use crate::key_registration::ClosedKeyRegistration; +use crate::signature_scheme::{BlsSignature, BlsSigningKey, BlsVerificationKey}; use crate::{Parameters, SingleSignature, Stake}; /// Wrapper of the MultiSignature Verification key diff --git a/mithril-stm/src/signature_scheme/bls_multi_signature/helper.rs b/mithril-stm/src/signature_scheme/bls_multi_signature/helper.rs new file mode 100644 index 00000000000..a254e6846bf --- /dev/null +++ b/mithril-stm/src/signature_scheme/bls_multi_signature/helper.rs @@ -0,0 +1,100 @@ +pub(crate) mod unsafe_helpers { + use anyhow::anyhow; + 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, + min_sig::{PublicKey as BlstVk, SecretKey as BlstSk, Signature as BlstSig}, + }; + + use crate::error::MultiSignatureError::SerializationError; + use crate::{ + StmResult, + signature_scheme::{BlsProofOfPossession, BlsVerificationKey}, + }; + + /// Check manually if the pairing `e(g1,mvk) = e(k2,g2)` holds. + pub(crate) fn verify_pairing(vk: &BlsVerificationKey, pop: &BlsProofOfPossession) -> bool { + unsafe { + let g1_p = *blst_p1_affine_generator(); + let mvk_p = + std::mem::transmute::(vk.to_blst_verification_key()); + 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.get_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]) -> StmResult { + 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(anyhow!(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: &BlsVerificationKey) -> blst_p2 { + unsafe { + let mut projective_p2 = blst_p2::default(); + blst_p2_from_affine( + &mut projective_p2, + &std::mem::transmute::(vk.to_blst_verification_key()), + ); + 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/signature_scheme/bls_multi_signature/mod.rs b/mithril-stm/src/signature_scheme/bls_multi_signature/mod.rs new file mode 100644 index 00000000000..5167e6d66d8 --- /dev/null +++ b/mithril-stm/src/signature_scheme/bls_multi_signature/mod.rs @@ -0,0 +1,307 @@ +//! BLST Multi-signature module + +pub(super) mod helper; +mod proof_of_possession; +mod signature; +mod signing_key; +mod verification_key; + +pub use proof_of_possession::*; +pub use signature::*; +pub use signing_key::*; +pub use verification_key::*; + +use serde::{ + de::Visitor, + {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!(BlsSigningKey, SigningKeyVisitor, 32); +impl_serde!(BlsVerificationKey, VerificationKeyVisitor, 96); +impl_serde!(BlsProofOfPossession, ProofOfPossessionVisitor, 96); +impl_serde!(BlsSignature, SignatureVisitor, 48); + +#[cfg(test)] +mod tests { + use blst::{blst_p1, blst_p2}; + use proptest::prelude::*; + use rand_chacha::ChaCha20Rng; + use rand_core::{RngCore, SeedableRng}; + + use crate::RegisterError; + use crate::error::MultiSignatureError; + use crate::key_registration::KeyRegistration; + use crate::signature_scheme::helper::unsafe_helpers::{p1_affine_to_sig, p2_affine_to_vk}; + + use super::*; + + impl PartialEq for BlsSigningKey { + fn eq(&self, other: &Self) -> bool { + self.to_blst_secret_key().to_bytes() == other.to_blst_secret_key().to_bytes() + } + } + + impl Eq for BlsSigningKey {} + + 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 = BlsSigningKey::generate(&mut ChaCha20Rng::from_seed(seed)); + let vk = BlsVerificationKey::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 = BlsSigningKey::generate(&mut rng); + let vk1 = BlsVerificationKey::from(&sk1); + let sk2 = BlsSigningKey::generate(&mut rng); + let fake_sig = sk2.sign(&msg); + + let error = fake_sig.verify(&msg, &vk1).expect_err("Fake signature should not be verified"); + + assert!( + matches!( + error.downcast_ref::(), + Some(MultiSignatureError::SignatureInvalid(_)) + ), + "Unexpected error: {error:?}"); + } + + #[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 = BlsSigningKey::generate(&mut rng); + let vk = BlsVerificationKey::from(&sk); + + let p1 = blst_p1::default(); + let sig_infinity = BlsSignature(p1_affine_to_sig(&p1)); + + let error = sig_infinity.verify(&msg, &vk).expect_err("Verification should fail"); + assert!( + matches!( + error.downcast_ref::(), + Some(MultiSignatureError::SignatureInfinity(_)) + ), + "Unexpected error: {error:?}"); + } + + #[test] + fn test_infinity_vk(seed in any::<[u8;32]>()) { + let mut rng = ChaCha20Rng::from_seed(seed); + let sk = BlsSigningKey::generate(&mut rng); + let pop = BlsProofOfPossession::from(&sk); + + let p2 = blst_p2::default(); + let vk_infinity = BlsVerificationKey(p2_affine_to_vk(&p2)); + let vkpop_infinity = BlsVerificationKeyProofOfPossession { vk: vk_infinity, pop }; + + let error = vkpop_infinity.verify_proof_of_possession().expect_err("VK pop infinity should fail"); + assert!( + matches!( + error.downcast_ref::(), + Some(MultiSignatureError::VerificationKeyInfinity(_)) + ), + "Unexpected error: {error:?}"); + } + + #[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 = KeyRegistration::init(); + + let sk = BlsSigningKey::generate(&mut rng); + let pop = BlsProofOfPossession::from(&sk); + let p2 = blst_p2::default(); + let vk_infinity = BlsVerificationKey(p2_affine_to_vk(&p2)); + let vkpop_infinity = BlsVerificationKeyProofOfPossession { vk: vk_infinity, pop }; + + for _ in 0..num_sigs { + let sk = BlsSigningKey::generate(&mut rng); + let vkpop = BlsVerificationKeyProofOfPossession::from(&sk); + let _ = kr.register(1, vkpop); + } + + let error = kr.register(1, vkpop_infinity).expect_err("VK pop infinity should not be registered"); + + assert!( + matches!( + error.downcast_ref::(), + Some(RegisterError::KeyInvalid(_)) + ), + "Unexpected error: {error:?}"); + } + + #[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 = BlsSigningKey::generate(&mut rng); + let vk = BlsVerificationKey::from(&sk); + let sig = sk.sign(&msg); + assert!(sig.verify(&msg, &vk).is_ok()); + sigs.push(sig); + mvks.push(vk); + } + + let result = BlsSignature::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 = BlsSigningKey::generate(&mut ChaCha20Rng::from_seed(seed)); + let sig = sk.sign(&msg); + sig.evaluate_dense_mapping(&msg, idx); + } + + #[test] + fn serialize_deserialize_vk(seed in any::()) { + let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed); + let sk = BlsSigningKey::generate(&mut rng); + let vk = BlsVerificationKey::from(&sk); + let vk_bytes = vk.to_bytes(); + let vk2 = BlsVerificationKey::from_bytes(&vk_bytes).unwrap(); + assert_eq!(vk, vk2); + let vkpop = BlsVerificationKeyProofOfPossession::from(&sk); + let vkpop_bytes = vkpop.to_bytes(); + let vkpop2: BlsVerificationKeyProofOfPossession = BlsVerificationKeyProofOfPossession::from_bytes(&vkpop_bytes).unwrap(); + assert_eq!(vkpop, vkpop2); + } + + #[test] + fn serialize_deserialize_sk(seed in any::()) { + let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed); + let sk = BlsSigningKey::generate(&mut rng); + let sk_bytes: [u8; 32] = sk.to_bytes(); + let sk2 = BlsSigningKey::from_bytes(&sk_bytes).unwrap(); + assert_eq!(sk, sk2); + } + + #[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 = BlsSigningKey::generate(&mut rng); + let vk = BlsVerificationKey::from(&sk); + let sig = sk.sign(&msg); + sigs.push(sig); + mvks.push(vk); + } + assert!(BlsSignature::verify_aggregate(&msg, &mvks, &sigs).is_ok()); + let (agg_vk, agg_sig) = BlsSignature::aggregate(&mvks, &sigs).unwrap(); + batch_msgs.push(msg.to_vec()); + batch_vk.push(agg_vk); + batch_sig.push(agg_sig); + } + assert!(BlsSignature::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 = BlsSigningKey::generate(&mut rng); + let fake_sig = sk.sign(&msg); + batch_sig[0] = fake_sig; + + let error = BlsSignature::batch_verify_aggregates(&batch_msgs, &batch_vk, &batch_sig).expect_err("Batch verify should fail"); + assert!( + matches!( + error.downcast_ref::(), + Some(MultiSignatureError::BatchInvalid) + ), + "Unexpected error: {error:?}"); + } + } +} diff --git a/mithril-stm/src/signature_scheme/bls_multi_signature/proof_of_possession.rs b/mithril-stm/src/signature_scheme/bls_multi_signature/proof_of_possession.rs new file mode 100644 index 00000000000..af86d06e626 --- /dev/null +++ b/mithril-stm/src/signature_scheme/bls_multi_signature/proof_of_possession.rs @@ -0,0 +1,105 @@ +use blst::{blst_p1, min_sig::Signature as BlstSig}; + +use crate::error::{MultiSignatureError, blst_error_to_stm_error}; +use crate::{ + StmResult, + signature_scheme::{ + BlsSigningKey, POP, + helper::unsafe_helpers::{compress_p1, scalar_to_pk_in_g1, uncompress_p1}, + }, +}; + +/// 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 BlsProofOfPossession { + k1: BlstSig, + k2: blst_p1, +} + +impl BlsProofOfPossession { + /// 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]) -> StmResult { + let k1 = match BlstSig::from_bytes( + bytes.get(..48).ok_or(MultiSignatureError::SerializationError)?, + ) { + Ok(key) => key, + Err(e) => { + return Err(blst_error_to_stm_error(e, None, None) + .expect_err("If it passed, blst returns and error different to SUCCESS.")); + } + }; + + let k2 = uncompress_p1(bytes.get(48..96).ok_or(MultiSignatureError::SerializationError)?)?; + + Ok(Self { k1, k2 }) + } + + pub(crate) fn get_k1(self) -> BlstSig { + self.k1 + } + + pub(crate) fn get_k2(self) -> blst_p1 { + self.k2 + } +} + +impl From<&BlsSigningKey> for BlsProofOfPossession { + /// 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: &BlsSigningKey) -> Self { + let k1 = sk.to_blst_secret_key().sign(POP, &[], &[]); + let k2 = scalar_to_pk_in_g1(&sk.to_blst_secret_key()); + Self { k1, k2 } + } +} + +#[cfg(test)] +mod tests { + mod golden { + + use rand_chacha::ChaCha20Rng; + use rand_core::SeedableRng; + + use crate::signature_scheme::{BlsProofOfPossession, BlsSigningKey}; + + const GOLDEN_JSON: &str = r#"[168,50,233,193,15,136,65,72,123,148,129,176,38,198,209,47,28,204,176,144,57,251,42,28,66,76,89,97,158,63,54,198,194,176,135,221,14,185,197,225,202,98,243,74,233,225,143,151,147,177,170,117,66,165,66,62,33,216,232,75,68,114,195,22,100,65,44,198,4,166,102,233,253,240,59,175,60,117,142,114,140,122,17,87,110,187,1,17,10,195,154,13,249,86,54,226]"#; + + fn golden_value() -> BlsProofOfPossession { + let mut rng = ChaCha20Rng::from_seed([0u8; 32]); + let sk = BlsSigningKey::generate(&mut rng); + BlsProofOfPossession::from(&sk) + } + + #[test] + fn golden_conversions() { + let value = serde_json::from_str(GOLDEN_JSON) + .expect("This JSON deserialization should not fail"); + assert_eq!(golden_value(), value); + + let serialized = + serde_json::to_string(&value).expect("This JSON serialization should not fail"); + let golden_serialized = serde_json::to_string(&golden_value()) + .expect("This JSON serialization should not fail"); + assert_eq!(golden_serialized, serialized); + } + } +} diff --git a/mithril-stm/src/signature_scheme/bls_multi_signature/signature.rs b/mithril-stm/src/signature_scheme/bls_multi_signature/signature.rs new file mode 100644 index 00000000000..406687fd661 --- /dev/null +++ b/mithril-stm/src/signature_scheme/bls_multi_signature/signature.rs @@ -0,0 +1,247 @@ +use anyhow::{Context, anyhow}; +use std::{cmp::Ordering, iter::Sum}; + +use blake2::{Blake2b, Blake2b512, Digest}; +use blst::{ + blst_p1, blst_p2, + min_sig::{AggregateSignature, PublicKey as BlstVk, Signature as BlstSig}, + p1_affines, p2_affines, +}; +use digest::consts::U16; + +use crate::{ + Index, + error::{MultiSignatureError, blst_error_to_stm_error}, +}; +use crate::{ + StmResult, + signature_scheme::{ + BlsVerificationKey, + helper::unsafe_helpers::{p1_affine_to_sig, p2_affine_to_vk, sig_to_p1, vk_from_p2_affine}, + }, +}; + +/// MultiSig signature, which is a wrapper over the `BlstSig` type. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BlsSignature(pub BlstSig); + +impl BlsSignature { + /// Verify a signature against a verification key. + pub fn verify(&self, msg: &[u8], mvk: &BlsVerificationKey) -> StmResult<()> { + blst_error_to_stm_error( + self.0.validate(true).map_or_else( + |e| e, + |_| { + self.0 + .verify(false, msg, &[], &[], &mvk.to_blst_verification_key(), 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(crate) fn evaluate_dense_mapping(&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); + + 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]) -> StmResult { + let bytes = bytes.get(..48).ok_or(MultiSignatureError::SerializationError)?; + match BlstSig::sig_validate(bytes, true) { + Ok(sig) => Ok(Self(sig)), + Err(e) => Err(blst_error_to_stm_error(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 compare_signatures(&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: &[BlsVerificationKey], + sigs: &[BlsSignature], + ) -> StmResult<(BlsVerificationKey, BlsSignature)> { + if vks.len() != sigs.len() || vks.is_empty() { + return Err(anyhow!(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()); + } + + 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((BlsVerificationKey(aggr_vk), BlsSignature(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: &[BlsVerificationKey], + sigs: &[BlsSignature], + ) -> StmResult<()> { + let (aggr_vk, aggr_sig) = Self::aggregate(vks, sigs).with_context(|| "Multi signature verification failed in aggregation of verification keys and signatures.")?; + + blst_error_to_stm_error( + aggr_sig.0.verify( + false, + msg, + &[], + &[], + &aggr_vk.to_blst_verification_key(), + false, + ), + Some(aggr_sig), + None, + ) + } + + /// Batch verify several sets of signatures with their corresponding verification keys. + pub fn batch_verify_aggregates( + msgs: &[Vec], + vks: &[BlsVerificationKey], + sigs: &[BlsSignature], + ) -> StmResult<()> { + 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_error_to_stm_error(e, None, None), + }; + + let p2_vks: Vec = vks.iter().map(|vk| vk.to_blst_verification_key()).collect(); + let p2_vks_ref: Vec<&BlstVk> = p2_vks.iter().collect(); + let slice_msgs = msgs.iter().map(|msg| msg.as_slice()).collect::>(); + + blst_error_to_stm_error( + batched_sig.aggregate_verify(false, &slice_msgs, &[], &p2_vks_ref, false), + None, + None, + ) + .map_err(|_| anyhow!(MultiSignatureError::BatchInvalid)) + } +} + +impl<'a> Sum<&'a Self> for BlsSignature { + 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 BlsSignature { + fn partial_cmp(&self, other: &Self) -> Option { + Some(std::cmp::Ord::cmp(self, other)) + } +} + +impl Ord for BlsSignature { + fn cmp(&self, other: &Self) -> Ordering { + self.compare_signatures(other) + } +} + +#[cfg(test)] +mod tests { + mod golden { + + use rand_chacha::ChaCha20Rng; + use rand_core::SeedableRng; + + use crate::signature_scheme::{BlsSignature, BlsSigningKey}; + + const GOLDEN_JSON: &str = r#"[132,95,124,197,185,105,193,171,114,182,52,171,205,119,202,188,2,213,61,125,219,242,10,131,53,219,53,197,157,42,152,194,234,161,244,204,2,134,47,179,176,49,200,232,120,241,180,246]"#; + + fn golden_value() -> BlsSignature { + let mut rng = ChaCha20Rng::from_seed([0u8; 32]); + let sk = BlsSigningKey::generate(&mut rng); + let msg = [0u8; 32]; + sk.sign(&msg) + } + + #[test] + fn golden_conversions() { + let value = serde_json::from_str(GOLDEN_JSON) + .expect("This JSON deserialization should not fail"); + assert_eq!(golden_value(), value); + + let serialized = + serde_json::to_string(&value).expect("This JSON serialization should not fail"); + let golden_serialized = serde_json::to_string(&golden_value()) + .expect("This JSON serialization should not fail"); + assert_eq!(golden_serialized, serialized); + } + } +} diff --git a/mithril-stm/src/signature_scheme/bls_multi_signature/signing_key.rs b/mithril-stm/src/signature_scheme/bls_multi_signature/signing_key.rs new file mode 100644 index 00000000000..46d728767df --- /dev/null +++ b/mithril-stm/src/signature_scheme/bls_multi_signature/signing_key.rs @@ -0,0 +1,83 @@ +use blst::min_sig::SecretKey as BlstSk; +use rand_core::{CryptoRng, RngCore}; + +use crate::StmResult; +use crate::error::{MultiSignatureError, blst_error_to_stm_error}; +use crate::signature_scheme::BlsSignature; + +/// MultiSig secret key, which is a wrapper over the BlstSk type from the blst +/// library. +#[derive(Debug, Clone)] +pub struct BlsSigningKey(pub BlstSk); + +impl BlsSigningKey { + /// Generate a secret key + pub fn generate(rng: &mut (impl RngCore + CryptoRng)) -> Self { + let mut ikm = [0u8; 32]; + rng.fill_bytes(&mut ikm); + BlsSigningKey( + 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]) -> BlsSignature { + BlsSignature(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]) -> StmResult { + let bytes = bytes.get(..32).ok_or(MultiSignatureError::SerializationError)?; + match BlstSk::from_bytes(bytes) { + Ok(sk) => Ok(Self(sk)), + Err(e) => Err(blst_error_to_stm_error(e, None, None) + .expect_err("If deserialization is not successful, blst returns and error different to SUCCESS.")) + } + } + + pub(crate) fn to_blst_secret_key(&self) -> BlstSk { + self.0.clone() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + mod golden { + + use rand_chacha::ChaCha20Rng; + use rand_core::SeedableRng; + + use super::*; + + const GOLDEN_JSON: &str = r#"[64, 129, 87, 121, 27, 239, 221, 215, 2, 103, 45, 207, 207, 201, 157, 163, 81, 47, 156, 14, 168, 24, 137, 15, 203, 106, 183, 73, 88, 14, 242, 207]"#; + + fn golden_value() -> BlsSigningKey { + let mut rng = ChaCha20Rng::from_seed([0u8; 32]); + BlsSigningKey::generate(&mut rng) + } + + #[test] + fn golden_conversions() { + let value = serde_json::from_str(GOLDEN_JSON) + .expect("This JSON deserialization should not fail"); + assert_eq!(golden_value(), value); + + let serialized = + serde_json::to_string(&value).expect("This JSON serialization should not fail"); + let golden_serialized = serde_json::to_string(&golden_value()) + .expect("This JSON serialization should not fail"); + assert_eq!(golden_serialized, serialized); + } + } +} diff --git a/mithril-stm/src/signature_scheme/bls_multi_signature/verification_key.rs b/mithril-stm/src/signature_scheme/bls_multi_signature/verification_key.rs new file mode 100644 index 00000000000..418a0fcb86c --- /dev/null +++ b/mithril-stm/src/signature_scheme/bls_multi_signature/verification_key.rs @@ -0,0 +1,257 @@ +use anyhow::anyhow; +use std::{ + cmp::Ordering, + fmt::{Display, Formatter}, + hash::{Hash, Hasher}, + iter::Sum, +}; + +use blst::{ + BLST_ERROR, + min_sig::{AggregatePublicKey, PublicKey as BlstVk}, +}; +use serde::{Deserialize, Serialize}; + +use crate::error::{MultiSignatureError, blst_error_to_stm_error}; +use crate::{ + StmResult, + signature_scheme::{ + BlsProofOfPossession, BlsSigningKey, POP, helper::unsafe_helpers::verify_pairing, + }, +}; + +/// MultiSig verification key, which is a wrapper over the BlstVk (element in G2) +/// from the blst library. +#[derive(Debug, Clone, Copy, Default)] +pub struct BlsVerificationKey(pub BlstVk); + +impl BlsVerificationKey { + /// 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]) -> StmResult { + let bytes = bytes.get(..96).ok_or(MultiSignatureError::SerializationError)?; + match BlstVk::key_validate(bytes) { + Ok(vk) => Ok(Self(vk)), + Err(e) => Err(blst_error_to_stm_error(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 compare_verification_keys(&self, other: &BlsVerificationKey) -> 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_verification_key(self) -> BlstVk { + self.0 + } +} + +impl Display for BlsVerificationKey { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.to_bytes()) + } +} + +impl Hash for BlsVerificationKey { + fn hash(&self, state: &mut H) { + Hash::hash_slice(&self.to_bytes(), state) + } +} + +impl PartialEq for BlsVerificationKey { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for BlsVerificationKey {} + +impl PartialOrd for BlsVerificationKey { + fn partial_cmp(&self, other: &Self) -> Option { + Some(std::cmp::Ord::cmp(self, other)) + } +} + +impl Ord for BlsVerificationKey { + fn cmp(&self, other: &Self) -> Ordering { + self.compare_verification_keys(other) + } +} + +impl<'a> Sum<&'a Self> for BlsVerificationKey { + 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<&BlsSigningKey> for BlsVerificationKey { + /// 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: &BlsSigningKey) -> Self { + BlsVerificationKey(sk.to_blst_secret_key().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 BlsVerificationKeyProofOfPossession { + /// The verification key. + pub vk: BlsVerificationKey, + /// Proof of Possession. + pub pop: BlsProofOfPossession, +} + +impl BlsVerificationKeyProofOfPossession { + /// 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(crate) fn verify_proof_of_possession(&self) -> StmResult<()> { + match self.vk.to_blst_verification_key().validate() { + Ok(_) => { + let result = verify_pairing(&self.vk, &self.pop); + if !(self.pop.get_k1().verify( + false, + POP, + &[], + &[], + &self.vk.to_blst_verification_key(), + false, + ) == BLST_ERROR::BLST_SUCCESS + && result) + { + return Err(anyhow!(MultiSignatureError::KeyInvalid(Box::new(*self)))); + } + Ok(()) + } + Err(e) => blst_error_to_stm_error(e, None, Some(self.vk)), + } + } + + /// 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. + #[deprecated( + since = "0.5.0", + note = "The verification of the proof of possession is not part of the public API any more" + )] + pub fn check(&self) -> StmResult<()> { + Self::verify_proof_of_possession(self) + } + + /// 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 `BlsVerificationKeyProofOfPossession`. + pub fn from_bytes(bytes: &[u8]) -> StmResult { + let mvk = BlsVerificationKey::from_bytes( + bytes.get(..96).ok_or(MultiSignatureError::SerializationError)?, + )?; + + let pop = BlsProofOfPossession::from_bytes( + bytes.get(96..).ok_or(MultiSignatureError::SerializationError)?, + )?; + + Ok(Self { vk: mvk, pop }) + } +} + +impl From<&BlsSigningKey> for BlsVerificationKeyProofOfPossession { + /// Convert a secret key into a `BlsVerificationKeyProofOfPossession` by simply converting to a + /// `MspMvk` and `MspPoP`. + fn from(sk: &BlsSigningKey) -> Self { + Self { + vk: sk.into(), + pop: sk.into(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + mod golden { + + use rand_chacha::ChaCha20Rng; + use rand_core::SeedableRng; + + use super::*; + + const GOLDEN_JSON: &str = r#" + { + "vk": [143, 161, 255, 48, 78, 57, 204, 220, 25, 221, 164, 252, 248, 14, 56, 126, 186, 135, 228, 188, 145, 181, 52, 200, 97, 99, 213, 46, 0, 199, 193, 89, 187, 88, 29, 135, 173, 244, 86, 36, 83, 54, 67, 164, 6, 137, 94, 72, 6, 105, 128, 128, 93, 48, 176, 11, 4, 246, 138, 48, 180, 133, 90, 142, 192, 24, 193, 111, 142, 31, 76, 111, 110, 234, 153, 90, 208, 192, 31, 124, 95, 102, 49, 158, 99, 52, 220, 165, 94, 251, 68, 69, 121, 16, 224, 194], + "pop": [168, 50, 233, 193, 15, 136, 65, 72, 123, 148, 129, 176, 38, 198, 209, 47, 28, 204, 176, 144, 57, 251, 42, 28, 66, 76, 89, 97, 158, 63, 54, 198, 194, 176, 135, 221, 14, 185, 197, 225, 202, 98, 243, 74, 233, 225, 143, 151, 147, 177, 170, 117, 66, 165, 66, 62, 33, 216, 232, 75, 68, 114, 195, 22, 100, 65, 44, 198, 4, 166, 102, 233, 253, 240, 59, 175, 60, 117, 142, 114, 140, 122, 17, 87, 110, 187, 1, 17, 10, 195, 154, 13, 249, 86, 54, 226] + } + "#; + + fn golden_value() -> BlsVerificationKeyProofOfPossession { + let mut rng = ChaCha20Rng::from_seed([0u8; 32]); + let sk = BlsSigningKey::generate(&mut rng); + BlsVerificationKeyProofOfPossession { + vk: BlsVerificationKey::from(&sk), + pop: BlsProofOfPossession::from(&sk), + } + } + + #[test] + fn golden_conversions() { + let value = serde_json::from_str(GOLDEN_JSON) + .expect("This JSON deserialization should not fail"); + assert_eq!(golden_value(), value); + + let serialized = + serde_json::to_string(&value).expect("This JSON serialization should not fail"); + let golden_serialized = serde_json::to_string(&golden_value()) + .expect("This JSON serialization should not fail"); + assert_eq!(golden_serialized, serialized); + } + } +} diff --git a/mithril-stm/src/signature_scheme/mod.rs b/mithril-stm/src/signature_scheme/mod.rs new file mode 100644 index 00000000000..97cd5a5fef3 --- /dev/null +++ b/mithril-stm/src/signature_scheme/mod.rs @@ -0,0 +1,4 @@ +mod bls_multi_signature; +mod schnorr_signature; + +pub use bls_multi_signature::*; diff --git a/mithril-stm/src/signature_scheme/schnorr_signature/mod.rs b/mithril-stm/src/signature_scheme/schnorr_signature/mod.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/mithril-stm/src/signature_scheme/schnorr_signature/mod.rs @@ -0,0 +1 @@ + diff --git a/mithril-stm/src/single_signature/signature.rs b/mithril-stm/src/single_signature/signature.rs index e55d6cab48d..4f42a5b2095 100644 --- a/mithril-stm/src/single_signature/signature.rs +++ b/mithril-stm/src/single_signature/signature.rs @@ -6,8 +6,8 @@ use std::{ use blake2::digest::{Digest, FixedOutput}; use serde::{Deserialize, Serialize}; -use crate::bls_multi_signature::BlsSignature; use crate::eligibility_check::is_lottery_won; +use crate::signature_scheme::BlsSignature; use crate::{ AggregateVerificationKey, Index, Parameters, SignatureError, Stake, StmResult, VerificationKey, }; @@ -207,7 +207,7 @@ mod tests { use rand_chacha::ChaCha20Rng; use rand_core::SeedableRng; - use crate::bls_multi_signature::{BlsSigningKey, BlsVerificationKeyProofOfPossession}; + use crate::signature_scheme::{BlsSigningKey, BlsVerificationKeyProofOfPossession}; use crate::{ClosedKeyRegistration, KeyRegistration, Parameters, Signer, SingleSignature}; type D = Blake2b; diff --git a/mithril-stm/src/single_signature/signature_registered_party.rs b/mithril-stm/src/single_signature/signature_registered_party.rs index b3db1bd6e16..dbaf280a1d3 100644 --- a/mithril-stm/src/single_signature/signature_registered_party.rs +++ b/mithril-stm/src/single_signature/signature_registered_party.rs @@ -60,7 +60,7 @@ mod tests { use rand_chacha::ChaCha20Rng; use rand_core::SeedableRng; - use crate::bls_multi_signature::{BlsSigningKey, BlsVerificationKeyProofOfPossession}; + use crate::signature_scheme::{BlsSigningKey, BlsVerificationKeyProofOfPossession}; use crate::{ ClosedKeyRegistration, KeyRegistration, Parameters, Signer, SingleSignatureWithRegisteredParty, From a54779e6df8096da6b76ff013eba3d1f5f35f795 Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Thu, 20 Nov 2025 20:49:09 +0300 Subject: [PATCH 3/4] proof_system module containing concatenation --- mithril-stm/src/aggregate_signature/clerk.rs | 2 +- mithril-stm/src/aggregate_signature/mod.rs | 2 - .../proof/concatenation.rs | 249 -------------- .../src/aggregate_signature/proof/mod.rs | 3 - .../src/aggregate_signature/signature.rs | 4 +- mithril-stm/src/bls_multi_signature/helper.rs | 100 ------ mithril-stm/src/bls_multi_signature/mod.rs | 307 ------------------ .../proof_of_possession.rs | 105 ------ .../src/bls_multi_signature/signature.rs | 247 -------------- .../src/bls_multi_signature/signing_key.rs | 83 ----- .../bls_multi_signature/verification_key.rs | 257 --------------- mithril-stm/src/lib.rs | 1 + 12 files changed, 3 insertions(+), 1357 deletions(-) delete mode 100644 mithril-stm/src/aggregate_signature/proof/concatenation.rs delete mode 100644 mithril-stm/src/aggregate_signature/proof/mod.rs delete mode 100644 mithril-stm/src/bls_multi_signature/helper.rs delete mode 100644 mithril-stm/src/bls_multi_signature/mod.rs delete mode 100644 mithril-stm/src/bls_multi_signature/proof_of_possession.rs delete mode 100644 mithril-stm/src/bls_multi_signature/signature.rs delete mode 100644 mithril-stm/src/bls_multi_signature/signing_key.rs delete mode 100644 mithril-stm/src/bls_multi_signature/verification_key.rs diff --git a/mithril-stm/src/aggregate_signature/clerk.rs b/mithril-stm/src/aggregate_signature/clerk.rs index c540305c4b1..fddbe9feb01 100644 --- a/mithril-stm/src/aggregate_signature/clerk.rs +++ b/mithril-stm/src/aggregate_signature/clerk.rs @@ -1,7 +1,7 @@ use crate::{ AggregateSignature, AggregateSignatureType, AggregateVerificationKey, ClosedKeyRegistration, Index, Parameters, Signer, SingleSignature, Stake, StmResult, VerificationKey, - aggregate_signature::ConcatenationProof, + proof_system::ConcatenationProof, }; use anyhow::Context; use blake2::digest::{Digest, FixedOutput}; diff --git a/mithril-stm/src/aggregate_signature/mod.rs b/mithril-stm/src/aggregate_signature/mod.rs index aab7afcd003..a03c46af85a 100644 --- a/mithril-stm/src/aggregate_signature/mod.rs +++ b/mithril-stm/src/aggregate_signature/mod.rs @@ -1,13 +1,11 @@ mod aggregate_key; mod basic_verifier; mod clerk; -mod proof; mod signature; pub use aggregate_key::*; pub use basic_verifier::*; pub use clerk::*; -pub use proof::*; pub use signature::*; #[cfg(test)] diff --git a/mithril-stm/src/aggregate_signature/proof/concatenation.rs b/mithril-stm/src/aggregate_signature/proof/concatenation.rs deleted file mode 100644 index 63642ac98cc..00000000000 --- a/mithril-stm/src/aggregate_signature/proof/concatenation.rs +++ /dev/null @@ -1,249 +0,0 @@ -use anyhow::Context; -use blake2::digest::{Digest, FixedOutput}; - -use serde::{Deserialize, Serialize}; - -use crate::aggregate_signature::clerk::Clerk; -use crate::key_registration::RegisteredParty; -use crate::membership_commitment::MerkleBatchPath; -use crate::signature_scheme::{BlsSignature, BlsVerificationKey}; -use crate::{ - AggregateSignatureError, AggregateVerificationKey, BasicVerifier, Parameters, SingleSignature, - SingleSignatureWithRegisteredParty, StmResult, -}; - -/// `ConcatenationProof` uses the "concatenation" proving system (as described in Section 4.3 of the original paper.) -/// This means that the aggregated signature contains a vector with all individual signatures. -/// BatchPath is also a part of the aggregate signature which covers path for all signatures. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(bound( - serialize = "MerkleBatchPath: Serialize", - deserialize = "MerkleBatchPath: Deserialize<'de>" -))] -pub struct ConcatenationProof { - pub(crate) signatures: Vec, - /// The list of unique merkle tree nodes that covers path for all signatures. - pub batch_proof: MerkleBatchPath, -} - -impl ConcatenationProof { - /// Aggregate a set of signatures for their corresponding indices. - /// - /// This function first deduplicates the repeated signatures, and if there are enough signatures, it collects the merkle tree indexes of unique signatures. - /// The list of merkle tree indexes is used to create a batch proof, to prove that all signatures are from eligible signers. - /// - /// It returns an instance of `ConcatenationProof`. - pub fn aggregate_signatures( - clerk: &Clerk, - sigs: &[SingleSignature], - msg: &[u8], - ) -> StmResult> { - let sig_reg_list = sigs - .iter() - .map(|sig| SingleSignatureWithRegisteredParty { - sig: sig.clone(), - reg_party: clerk.closed_reg.reg_parties[sig.signer_index as usize], - }) - .collect::>(); - - let avk = AggregateVerificationKey::from(&clerk.closed_reg); - let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg); - let mut unique_sigs = BasicVerifier::select_valid_signatures_for_k_indices( - &clerk.closed_reg.total_stake, - &clerk.params, - &msgp, - &sig_reg_list, - ) - .with_context( - || "Failed to aggregate unique signatures during selection for the k indices.", - )?; - - unique_sigs.sort_unstable(); - - let mt_index_list = unique_sigs - .iter() - .map(|sig_reg| sig_reg.sig.signer_index as usize) - .collect::>(); - - let batch_proof = clerk - .closed_reg - .merkle_tree - .compute_merkle_tree_batch_path(mt_index_list); - - Ok(Self { - signatures: unique_sigs, - batch_proof, - }) - } - - /// Verify all checks from signatures, except for the signature verification itself. - /// - /// Indices and quorum are checked by `BasicVerifier::preliminary_verify` with `msgp`. - /// It collects leaves from signatures and checks the batch proof. - /// After batch proof is checked, it collects and returns the signatures and - /// verification keys to be used by aggregate verification. - fn preliminary_verify( - &self, - msg: &[u8], - avk: &AggregateVerificationKey, - parameters: &Parameters, - ) -> StmResult<(Vec, Vec)> { - let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg); - BasicVerifier::preliminary_verify( - &avk.get_total_stake(), - &self.signatures, - parameters, - &msgp, - ) - .with_context(|| "Preliminary verification of aggregate signatures failed.")?; - - let leaves = self - .signatures - .iter() - .map(|r| r.reg_party) - .collect::>(); - - avk.get_merkle_tree_batch_commitment() - .verify_leaves_membership_from_batch_path(&leaves, &self.batch_proof) - .with_context(|| "Batch proof is invalid in preliminary verification.")?; - - Ok(BasicVerifier::collect_signatures_verification_keys( - &self.signatures, - )) - } - - /// Verify concatenation proof, by checking that - /// * each signature contains only valid indices, - /// * the lottery is indeed won by each one of them, - /// * the merkle tree path is valid, - /// * the aggregate signature validates with respect to the aggregate verification key - /// (aggregation is computed using functions `MSP.BKey` and `MSP.BSig` as described in Section 2.4 of the paper). - pub fn verify( - &self, - msg: &[u8], - avk: &AggregateVerificationKey, - parameters: &Parameters, - ) -> StmResult<()> { - let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg); - let (sigs, vks) = self - .preliminary_verify(msg, avk, parameters) - .with_context(|| "Aggregate signature verification failed")?; - - BlsSignature::verify_aggregate(msgp.as_slice(), &vks, &sigs) - .with_context(|| "Aggregate signature verification failed")?; - Ok(()) - } - - /// Batch verify a set of signatures, with different messages and avks. - pub fn batch_verify( - stm_signatures: &[Self], - msgs: &[Vec], - avks: &[AggregateVerificationKey], - parameters: &[Parameters], - ) -> StmResult<()> { - let batch_size = stm_signatures.len(); - assert_eq!( - batch_size, - msgs.len(), - "Number of messages should correspond to size of the batch" - ); - assert_eq!( - batch_size, - avks.len(), - "Number of avks should correspond to size of the batch" - ); - assert_eq!( - batch_size, - parameters.len(), - "Number of parameters should correspond to size of the batch" - ); - - let mut aggr_sigs = Vec::with_capacity(batch_size); - let mut aggr_vks = Vec::with_capacity(batch_size); - for (idx, sig_group) in stm_signatures.iter().enumerate() { - sig_group.preliminary_verify(&msgs[idx], &avks[idx], ¶meters[idx])?; - let grouped_sigs: Vec = - sig_group.signatures.iter().map(|sig_reg| sig_reg.sig.sigma).collect(); - let grouped_vks: Vec = sig_group - .signatures - .iter() - .map(|sig_reg| sig_reg.reg_party.0) - .collect(); - - let (aggr_vk, aggr_sig) = BlsSignature::aggregate(&grouped_vks, &grouped_sigs).unwrap(); - aggr_sigs.push(aggr_sig); - aggr_vks.push(aggr_vk); - } - - let concat_msgs: Vec> = msgs - .iter() - .zip(avks.iter()) - .map(|(msg, avk)| avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg)) - .collect(); - - BlsSignature::batch_verify_aggregates(&concat_msgs, &aggr_vks, &aggr_sigs)?; - Ok(()) - } - - /// Convert concatenation proof to bytes - /// # Layout - /// * Number of the pairs of Signatures and Registered Parties (SigRegParty) (as u64) - /// * Pairs of Signatures and Registered Parties (prefixed with their size as u64) - /// * Batch proof - pub fn to_bytes(&self) -> Vec { - let mut out = Vec::new(); - out.extend_from_slice(&u64::try_from(self.signatures.len()).unwrap().to_be_bytes()); - for sig_reg in &self.signatures { - out.extend_from_slice(&u64::try_from(sig_reg.to_bytes().len()).unwrap().to_be_bytes()); - out.extend_from_slice(&sig_reg.to_bytes()); - } - let proof = &self.batch_proof; - out.extend_from_slice(&proof.to_bytes()); - - out - } - - ///Extract a concatenation proof from a byte slice. - pub fn from_bytes(bytes: &[u8]) -> StmResult> { - let mut bytes_index = 0; - - let mut u64_bytes = [0u8; 8]; - u64_bytes.copy_from_slice( - bytes - .get(bytes_index..bytes_index + 8) - .ok_or(AggregateSignatureError::SerializationError)?, - ); - let total_sigs = usize::try_from(u64::from_be_bytes(u64_bytes)) - .map_err(|_| AggregateSignatureError::SerializationError)?; - bytes_index += 8; - - let mut sig_reg_list = Vec::with_capacity(total_sigs); - for _ in 0..total_sigs { - u64_bytes.copy_from_slice( - bytes - .get(bytes_index..bytes_index + 8) - .ok_or(AggregateSignatureError::SerializationError)?, - ); - let sig_reg_size = usize::try_from(u64::from_be_bytes(u64_bytes)) - .map_err(|_| AggregateSignatureError::SerializationError)?; - let sig_reg = SingleSignatureWithRegisteredParty::from_bytes::( - bytes - .get(bytes_index + 8..bytes_index + 8 + sig_reg_size) - .ok_or(AggregateSignatureError::SerializationError)?, - )?; - bytes_index += 8 + sig_reg_size; - sig_reg_list.push(sig_reg); - } - - let batch_proof = MerkleBatchPath::from_bytes( - bytes - .get(bytes_index..) - .ok_or(AggregateSignatureError::SerializationError)?, - )?; - - Ok(ConcatenationProof { - signatures: sig_reg_list, - batch_proof, - }) - } -} diff --git a/mithril-stm/src/aggregate_signature/proof/mod.rs b/mithril-stm/src/aggregate_signature/proof/mod.rs deleted file mode 100644 index 77d1e9f4481..00000000000 --- a/mithril-stm/src/aggregate_signature/proof/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod concatenation; - -pub use concatenation::*; diff --git a/mithril-stm/src/aggregate_signature/signature.rs b/mithril-stm/src/aggregate_signature/signature.rs index 390f7f8ea36..5f8e4b3741e 100644 --- a/mithril-stm/src/aggregate_signature/signature.rs +++ b/mithril-stm/src/aggregate_signature/signature.rs @@ -8,9 +8,7 @@ use serde::{Deserialize, Serialize}; use crate::error::AggregateSignatureError; use crate::membership_commitment::MerkleBatchPath; -use crate::{AggregateVerificationKey, Parameters, StmResult}; - -use super::ConcatenationProof; +use crate::{AggregateVerificationKey, Parameters, StmResult, proof_system::ConcatenationProof}; /// The type of STM aggregate signature. #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] diff --git a/mithril-stm/src/bls_multi_signature/helper.rs b/mithril-stm/src/bls_multi_signature/helper.rs deleted file mode 100644 index bdc09056fee..00000000000 --- a/mithril-stm/src/bls_multi_signature/helper.rs +++ /dev/null @@ -1,100 +0,0 @@ -pub(crate) mod unsafe_helpers { - use anyhow::anyhow; - 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, - min_sig::{PublicKey as BlstVk, SecretKey as BlstSk, Signature as BlstSig}, - }; - - use crate::error::MultiSignatureError::SerializationError; - use crate::{ - StmResult, - bls_multi_signature::{BlsProofOfPossession, BlsVerificationKey}, - }; - - /// Check manually if the pairing `e(g1,mvk) = e(k2,g2)` holds. - pub(crate) fn verify_pairing(vk: &BlsVerificationKey, pop: &BlsProofOfPossession) -> bool { - unsafe { - let g1_p = *blst_p1_affine_generator(); - let mvk_p = - std::mem::transmute::(vk.to_blst_verification_key()); - 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.get_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]) -> StmResult { - 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(anyhow!(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: &BlsVerificationKey) -> blst_p2 { - unsafe { - let mut projective_p2 = blst_p2::default(); - blst_p2_from_affine( - &mut projective_p2, - &std::mem::transmute::(vk.to_blst_verification_key()), - ); - 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 deleted file mode 100644 index 9f0ba22cc01..00000000000 --- a/mithril-stm/src/bls_multi_signature/mod.rs +++ /dev/null @@ -1,307 +0,0 @@ -//! BLST Multi-signature module - -pub(super) mod helper; -mod proof_of_possession; -mod signature; -mod signing_key; -mod verification_key; - -pub use proof_of_possession::*; -pub use signature::*; -pub use signing_key::*; -pub use verification_key::*; - -use serde::{ - de::Visitor, - {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!(BlsSigningKey, SigningKeyVisitor, 32); -impl_serde!(BlsVerificationKey, VerificationKeyVisitor, 96); -impl_serde!(BlsProofOfPossession, ProofOfPossessionVisitor, 96); -impl_serde!(BlsSignature, SignatureVisitor, 48); - -#[cfg(test)] -mod tests { - use blst::{blst_p1, blst_p2}; - use proptest::prelude::*; - use rand_chacha::ChaCha20Rng; - use rand_core::{RngCore, SeedableRng}; - - use crate::RegisterError; - use crate::bls_multi_signature::helper::unsafe_helpers::{p1_affine_to_sig, p2_affine_to_vk}; - use crate::error::MultiSignatureError; - use crate::key_registration::KeyRegistration; - - use super::*; - - impl PartialEq for BlsSigningKey { - fn eq(&self, other: &Self) -> bool { - self.to_blst_secret_key().to_bytes() == other.to_blst_secret_key().to_bytes() - } - } - - impl Eq for BlsSigningKey {} - - 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 = BlsSigningKey::generate(&mut ChaCha20Rng::from_seed(seed)); - let vk = BlsVerificationKey::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 = BlsSigningKey::generate(&mut rng); - let vk1 = BlsVerificationKey::from(&sk1); - let sk2 = BlsSigningKey::generate(&mut rng); - let fake_sig = sk2.sign(&msg); - - let error = fake_sig.verify(&msg, &vk1).expect_err("Fake signature should not be verified"); - - assert!( - matches!( - error.downcast_ref::(), - Some(MultiSignatureError::SignatureInvalid(_)) - ), - "Unexpected error: {error:?}"); - } - - #[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 = BlsSigningKey::generate(&mut rng); - let vk = BlsVerificationKey::from(&sk); - - let p1 = blst_p1::default(); - let sig_infinity = BlsSignature(p1_affine_to_sig(&p1)); - - let error = sig_infinity.verify(&msg, &vk).expect_err("Verification should fail"); - assert!( - matches!( - error.downcast_ref::(), - Some(MultiSignatureError::SignatureInfinity(_)) - ), - "Unexpected error: {error:?}"); - } - - #[test] - fn test_infinity_vk(seed in any::<[u8;32]>()) { - let mut rng = ChaCha20Rng::from_seed(seed); - let sk = BlsSigningKey::generate(&mut rng); - let pop = BlsProofOfPossession::from(&sk); - - let p2 = blst_p2::default(); - let vk_infinity = BlsVerificationKey(p2_affine_to_vk(&p2)); - let vkpop_infinity = BlsVerificationKeyProofOfPossession { vk: vk_infinity, pop }; - - let error = vkpop_infinity.verify_proof_of_possession().expect_err("VK pop infinity should fail"); - assert!( - matches!( - error.downcast_ref::(), - Some(MultiSignatureError::VerificationKeyInfinity(_)) - ), - "Unexpected error: {error:?}"); - } - - #[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 = KeyRegistration::init(); - - let sk = BlsSigningKey::generate(&mut rng); - let pop = BlsProofOfPossession::from(&sk); - let p2 = blst_p2::default(); - let vk_infinity = BlsVerificationKey(p2_affine_to_vk(&p2)); - let vkpop_infinity = BlsVerificationKeyProofOfPossession { vk: vk_infinity, pop }; - - for _ in 0..num_sigs { - let sk = BlsSigningKey::generate(&mut rng); - let vkpop = BlsVerificationKeyProofOfPossession::from(&sk); - let _ = kr.register(1, vkpop); - } - - let error = kr.register(1, vkpop_infinity).expect_err("VK pop infinity should not be registered"); - - assert!( - matches!( - error.downcast_ref::(), - Some(RegisterError::KeyInvalid(_)) - ), - "Unexpected error: {error:?}"); - } - - #[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 = BlsSigningKey::generate(&mut rng); - let vk = BlsVerificationKey::from(&sk); - let sig = sk.sign(&msg); - assert!(sig.verify(&msg, &vk).is_ok()); - sigs.push(sig); - mvks.push(vk); - } - - let result = BlsSignature::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 = BlsSigningKey::generate(&mut ChaCha20Rng::from_seed(seed)); - let sig = sk.sign(&msg); - sig.evaluate_dense_mapping(&msg, idx); - } - - #[test] - fn serialize_deserialize_vk(seed in any::()) { - let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed); - let sk = BlsSigningKey::generate(&mut rng); - let vk = BlsVerificationKey::from(&sk); - let vk_bytes = vk.to_bytes(); - let vk2 = BlsVerificationKey::from_bytes(&vk_bytes).unwrap(); - assert_eq!(vk, vk2); - let vkpop = BlsVerificationKeyProofOfPossession::from(&sk); - let vkpop_bytes = vkpop.to_bytes(); - let vkpop2: BlsVerificationKeyProofOfPossession = BlsVerificationKeyProofOfPossession::from_bytes(&vkpop_bytes).unwrap(); - assert_eq!(vkpop, vkpop2); - } - - #[test] - fn serialize_deserialize_sk(seed in any::()) { - let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed); - let sk = BlsSigningKey::generate(&mut rng); - let sk_bytes: [u8; 32] = sk.to_bytes(); - let sk2 = BlsSigningKey::from_bytes(&sk_bytes).unwrap(); - assert_eq!(sk, sk2); - } - - #[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 = BlsSigningKey::generate(&mut rng); - let vk = BlsVerificationKey::from(&sk); - let sig = sk.sign(&msg); - sigs.push(sig); - mvks.push(vk); - } - assert!(BlsSignature::verify_aggregate(&msg, &mvks, &sigs).is_ok()); - let (agg_vk, agg_sig) = BlsSignature::aggregate(&mvks, &sigs).unwrap(); - batch_msgs.push(msg.to_vec()); - batch_vk.push(agg_vk); - batch_sig.push(agg_sig); - } - assert!(BlsSignature::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 = BlsSigningKey::generate(&mut rng); - let fake_sig = sk.sign(&msg); - batch_sig[0] = fake_sig; - - let error = BlsSignature::batch_verify_aggregates(&batch_msgs, &batch_vk, &batch_sig).expect_err("Batch verify should fail"); - assert!( - matches!( - error.downcast_ref::(), - Some(MultiSignatureError::BatchInvalid) - ), - "Unexpected error: {error:?}"); - } - } -} diff --git a/mithril-stm/src/bls_multi_signature/proof_of_possession.rs b/mithril-stm/src/bls_multi_signature/proof_of_possession.rs deleted file mode 100644 index ae645143db2..00000000000 --- a/mithril-stm/src/bls_multi_signature/proof_of_possession.rs +++ /dev/null @@ -1,105 +0,0 @@ -use blst::{blst_p1, min_sig::Signature as BlstSig}; - -use crate::error::{MultiSignatureError, blst_error_to_stm_error}; -use crate::{ - StmResult, - bls_multi_signature::{ - BlsSigningKey, POP, - helper::unsafe_helpers::{compress_p1, scalar_to_pk_in_g1, uncompress_p1}, - }, -}; - -/// 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 BlsProofOfPossession { - k1: BlstSig, - k2: blst_p1, -} - -impl BlsProofOfPossession { - /// 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]) -> StmResult { - let k1 = match BlstSig::from_bytes( - bytes.get(..48).ok_or(MultiSignatureError::SerializationError)?, - ) { - Ok(key) => key, - Err(e) => { - return Err(blst_error_to_stm_error(e, None, None) - .expect_err("If it passed, blst returns and error different to SUCCESS.")); - } - }; - - let k2 = uncompress_p1(bytes.get(48..96).ok_or(MultiSignatureError::SerializationError)?)?; - - Ok(Self { k1, k2 }) - } - - pub(crate) fn get_k1(self) -> BlstSig { - self.k1 - } - - pub(crate) fn get_k2(self) -> blst_p1 { - self.k2 - } -} - -impl From<&BlsSigningKey> for BlsProofOfPossession { - /// 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: &BlsSigningKey) -> Self { - let k1 = sk.to_blst_secret_key().sign(POP, &[], &[]); - let k2 = scalar_to_pk_in_g1(&sk.to_blst_secret_key()); - Self { k1, k2 } - } -} - -#[cfg(test)] -mod tests { - mod golden { - - use rand_chacha::ChaCha20Rng; - use rand_core::SeedableRng; - - use crate::bls_multi_signature::{BlsProofOfPossession, BlsSigningKey}; - - const GOLDEN_JSON: &str = r#"[168,50,233,193,15,136,65,72,123,148,129,176,38,198,209,47,28,204,176,144,57,251,42,28,66,76,89,97,158,63,54,198,194,176,135,221,14,185,197,225,202,98,243,74,233,225,143,151,147,177,170,117,66,165,66,62,33,216,232,75,68,114,195,22,100,65,44,198,4,166,102,233,253,240,59,175,60,117,142,114,140,122,17,87,110,187,1,17,10,195,154,13,249,86,54,226]"#; - - fn golden_value() -> BlsProofOfPossession { - let mut rng = ChaCha20Rng::from_seed([0u8; 32]); - let sk = BlsSigningKey::generate(&mut rng); - BlsProofOfPossession::from(&sk) - } - - #[test] - fn golden_conversions() { - let value = serde_json::from_str(GOLDEN_JSON) - .expect("This JSON deserialization should not fail"); - assert_eq!(golden_value(), value); - - let serialized = - serde_json::to_string(&value).expect("This JSON serialization should not fail"); - let golden_serialized = serde_json::to_string(&golden_value()) - .expect("This JSON serialization should not fail"); - assert_eq!(golden_serialized, serialized); - } - } -} diff --git a/mithril-stm/src/bls_multi_signature/signature.rs b/mithril-stm/src/bls_multi_signature/signature.rs deleted file mode 100644 index 8c428b9da3c..00000000000 --- a/mithril-stm/src/bls_multi_signature/signature.rs +++ /dev/null @@ -1,247 +0,0 @@ -use anyhow::{Context, anyhow}; -use std::{cmp::Ordering, iter::Sum}; - -use blake2::{Blake2b, Blake2b512, Digest}; -use blst::{ - blst_p1, blst_p2, - min_sig::{AggregateSignature, PublicKey as BlstVk, Signature as BlstSig}, - p1_affines, p2_affines, -}; -use digest::consts::U16; - -use crate::{ - Index, - error::{MultiSignatureError, blst_error_to_stm_error}, -}; -use crate::{ - StmResult, - bls_multi_signature::{ - BlsVerificationKey, - helper::unsafe_helpers::{p1_affine_to_sig, p2_affine_to_vk, sig_to_p1, vk_from_p2_affine}, - }, -}; - -/// MultiSig signature, which is a wrapper over the `BlstSig` type. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct BlsSignature(pub BlstSig); - -impl BlsSignature { - /// Verify a signature against a verification key. - pub fn verify(&self, msg: &[u8], mvk: &BlsVerificationKey) -> StmResult<()> { - blst_error_to_stm_error( - self.0.validate(true).map_or_else( - |e| e, - |_| { - self.0 - .verify(false, msg, &[], &[], &mvk.to_blst_verification_key(), 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(crate) fn evaluate_dense_mapping(&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); - - 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]) -> StmResult { - let bytes = bytes.get(..48).ok_or(MultiSignatureError::SerializationError)?; - match BlstSig::sig_validate(bytes, true) { - Ok(sig) => Ok(Self(sig)), - Err(e) => Err(blst_error_to_stm_error(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 compare_signatures(&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: &[BlsVerificationKey], - sigs: &[BlsSignature], - ) -> StmResult<(BlsVerificationKey, BlsSignature)> { - if vks.len() != sigs.len() || vks.is_empty() { - return Err(anyhow!(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()); - } - - 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((BlsVerificationKey(aggr_vk), BlsSignature(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: &[BlsVerificationKey], - sigs: &[BlsSignature], - ) -> StmResult<()> { - let (aggr_vk, aggr_sig) = Self::aggregate(vks, sigs).with_context(|| "Multi signature verification failed in aggregation of verification keys and signatures.")?; - - blst_error_to_stm_error( - aggr_sig.0.verify( - false, - msg, - &[], - &[], - &aggr_vk.to_blst_verification_key(), - false, - ), - Some(aggr_sig), - None, - ) - } - - /// Batch verify several sets of signatures with their corresponding verification keys. - pub fn batch_verify_aggregates( - msgs: &[Vec], - vks: &[BlsVerificationKey], - sigs: &[BlsSignature], - ) -> StmResult<()> { - 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_error_to_stm_error(e, None, None), - }; - - let p2_vks: Vec = vks.iter().map(|vk| vk.to_blst_verification_key()).collect(); - let p2_vks_ref: Vec<&BlstVk> = p2_vks.iter().collect(); - let slice_msgs = msgs.iter().map(|msg| msg.as_slice()).collect::>(); - - blst_error_to_stm_error( - batched_sig.aggregate_verify(false, &slice_msgs, &[], &p2_vks_ref, false), - None, - None, - ) - .map_err(|_| anyhow!(MultiSignatureError::BatchInvalid)) - } -} - -impl<'a> Sum<&'a Self> for BlsSignature { - 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 BlsSignature { - fn partial_cmp(&self, other: &Self) -> Option { - Some(std::cmp::Ord::cmp(self, other)) - } -} - -impl Ord for BlsSignature { - fn cmp(&self, other: &Self) -> Ordering { - self.compare_signatures(other) - } -} - -#[cfg(test)] -mod tests { - mod golden { - - use rand_chacha::ChaCha20Rng; - use rand_core::SeedableRng; - - use crate::bls_multi_signature::{BlsSignature, BlsSigningKey}; - - const GOLDEN_JSON: &str = r#"[132,95,124,197,185,105,193,171,114,182,52,171,205,119,202,188,2,213,61,125,219,242,10,131,53,219,53,197,157,42,152,194,234,161,244,204,2,134,47,179,176,49,200,232,120,241,180,246]"#; - - fn golden_value() -> BlsSignature { - let mut rng = ChaCha20Rng::from_seed([0u8; 32]); - let sk = BlsSigningKey::generate(&mut rng); - let msg = [0u8; 32]; - sk.sign(&msg) - } - - #[test] - fn golden_conversions() { - let value = serde_json::from_str(GOLDEN_JSON) - .expect("This JSON deserialization should not fail"); - assert_eq!(golden_value(), value); - - let serialized = - serde_json::to_string(&value).expect("This JSON serialization should not fail"); - let golden_serialized = serde_json::to_string(&golden_value()) - .expect("This JSON serialization should not fail"); - assert_eq!(golden_serialized, serialized); - } - } -} diff --git a/mithril-stm/src/bls_multi_signature/signing_key.rs b/mithril-stm/src/bls_multi_signature/signing_key.rs deleted file mode 100644 index d3f0a18d85d..00000000000 --- a/mithril-stm/src/bls_multi_signature/signing_key.rs +++ /dev/null @@ -1,83 +0,0 @@ -use blst::min_sig::SecretKey as BlstSk; -use rand_core::{CryptoRng, RngCore}; - -use crate::StmResult; -use crate::bls_multi_signature::signature::BlsSignature; -use crate::error::{MultiSignatureError, blst_error_to_stm_error}; - -/// MultiSig secret key, which is a wrapper over the BlstSk type from the blst -/// library. -#[derive(Debug, Clone)] -pub struct BlsSigningKey(pub BlstSk); - -impl BlsSigningKey { - /// Generate a secret key - pub fn generate(rng: &mut (impl RngCore + CryptoRng)) -> Self { - let mut ikm = [0u8; 32]; - rng.fill_bytes(&mut ikm); - BlsSigningKey( - 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]) -> BlsSignature { - BlsSignature(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]) -> StmResult { - let bytes = bytes.get(..32).ok_or(MultiSignatureError::SerializationError)?; - match BlstSk::from_bytes(bytes) { - Ok(sk) => Ok(Self(sk)), - Err(e) => Err(blst_error_to_stm_error(e, None, None) - .expect_err("If deserialization is not successful, blst returns and error different to SUCCESS.")) - } - } - - pub(crate) fn to_blst_secret_key(&self) -> BlstSk { - self.0.clone() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - mod golden { - - use rand_chacha::ChaCha20Rng; - use rand_core::SeedableRng; - - use super::*; - - const GOLDEN_JSON: &str = r#"[64, 129, 87, 121, 27, 239, 221, 215, 2, 103, 45, 207, 207, 201, 157, 163, 81, 47, 156, 14, 168, 24, 137, 15, 203, 106, 183, 73, 88, 14, 242, 207]"#; - - fn golden_value() -> BlsSigningKey { - let mut rng = ChaCha20Rng::from_seed([0u8; 32]); - BlsSigningKey::generate(&mut rng) - } - - #[test] - fn golden_conversions() { - let value = serde_json::from_str(GOLDEN_JSON) - .expect("This JSON deserialization should not fail"); - assert_eq!(golden_value(), value); - - let serialized = - serde_json::to_string(&value).expect("This JSON serialization should not fail"); - let golden_serialized = serde_json::to_string(&golden_value()) - .expect("This JSON serialization should not fail"); - assert_eq!(golden_serialized, serialized); - } - } -} diff --git a/mithril-stm/src/bls_multi_signature/verification_key.rs b/mithril-stm/src/bls_multi_signature/verification_key.rs deleted file mode 100644 index f9e0182d6ce..00000000000 --- a/mithril-stm/src/bls_multi_signature/verification_key.rs +++ /dev/null @@ -1,257 +0,0 @@ -use anyhow::anyhow; -use std::{ - cmp::Ordering, - fmt::{Display, Formatter}, - hash::{Hash, Hasher}, - iter::Sum, -}; - -use blst::{ - BLST_ERROR, - min_sig::{AggregatePublicKey, PublicKey as BlstVk}, -}; -use serde::{Deserialize, Serialize}; - -use crate::error::{MultiSignatureError, blst_error_to_stm_error}; -use crate::{ - StmResult, - bls_multi_signature::{ - BlsProofOfPossession, BlsSigningKey, POP, helper::unsafe_helpers::verify_pairing, - }, -}; - -/// MultiSig verification key, which is a wrapper over the BlstVk (element in G2) -/// from the blst library. -#[derive(Debug, Clone, Copy, Default)] -pub struct BlsVerificationKey(pub BlstVk); - -impl BlsVerificationKey { - /// 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]) -> StmResult { - let bytes = bytes.get(..96).ok_or(MultiSignatureError::SerializationError)?; - match BlstVk::key_validate(bytes) { - Ok(vk) => Ok(Self(vk)), - Err(e) => Err(blst_error_to_stm_error(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 compare_verification_keys(&self, other: &BlsVerificationKey) -> 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_verification_key(self) -> BlstVk { - self.0 - } -} - -impl Display for BlsVerificationKey { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.to_bytes()) - } -} - -impl Hash for BlsVerificationKey { - fn hash(&self, state: &mut H) { - Hash::hash_slice(&self.to_bytes(), state) - } -} - -impl PartialEq for BlsVerificationKey { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for BlsVerificationKey {} - -impl PartialOrd for BlsVerificationKey { - fn partial_cmp(&self, other: &Self) -> Option { - Some(std::cmp::Ord::cmp(self, other)) - } -} - -impl Ord for BlsVerificationKey { - fn cmp(&self, other: &Self) -> Ordering { - self.compare_verification_keys(other) - } -} - -impl<'a> Sum<&'a Self> for BlsVerificationKey { - 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<&BlsSigningKey> for BlsVerificationKey { - /// 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: &BlsSigningKey) -> Self { - BlsVerificationKey(sk.to_blst_secret_key().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 BlsVerificationKeyProofOfPossession { - /// The verification key. - pub vk: BlsVerificationKey, - /// Proof of Possession. - pub pop: BlsProofOfPossession, -} - -impl BlsVerificationKeyProofOfPossession { - /// 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(crate) fn verify_proof_of_possession(&self) -> StmResult<()> { - match self.vk.to_blst_verification_key().validate() { - Ok(_) => { - let result = verify_pairing(&self.vk, &self.pop); - if !(self.pop.get_k1().verify( - false, - POP, - &[], - &[], - &self.vk.to_blst_verification_key(), - false, - ) == BLST_ERROR::BLST_SUCCESS - && result) - { - return Err(anyhow!(MultiSignatureError::KeyInvalid(Box::new(*self)))); - } - Ok(()) - } - Err(e) => blst_error_to_stm_error(e, None, Some(self.vk)), - } - } - - /// 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. - #[deprecated( - since = "0.5.0", - note = "The verification of the proof of possession is not part of the public API any more" - )] - pub fn check(&self) -> StmResult<()> { - Self::verify_proof_of_possession(self) - } - - /// 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 `BlsVerificationKeyProofOfPossession`. - pub fn from_bytes(bytes: &[u8]) -> StmResult { - let mvk = BlsVerificationKey::from_bytes( - bytes.get(..96).ok_or(MultiSignatureError::SerializationError)?, - )?; - - let pop = BlsProofOfPossession::from_bytes( - bytes.get(96..).ok_or(MultiSignatureError::SerializationError)?, - )?; - - Ok(Self { vk: mvk, pop }) - } -} - -impl From<&BlsSigningKey> for BlsVerificationKeyProofOfPossession { - /// Convert a secret key into a `BlsVerificationKeyProofOfPossession` by simply converting to a - /// `MspMvk` and `MspPoP`. - fn from(sk: &BlsSigningKey) -> Self { - Self { - vk: sk.into(), - pop: sk.into(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - mod golden { - - use rand_chacha::ChaCha20Rng; - use rand_core::SeedableRng; - - use super::*; - - const GOLDEN_JSON: &str = r#" - { - "vk": [143, 161, 255, 48, 78, 57, 204, 220, 25, 221, 164, 252, 248, 14, 56, 126, 186, 135, 228, 188, 145, 181, 52, 200, 97, 99, 213, 46, 0, 199, 193, 89, 187, 88, 29, 135, 173, 244, 86, 36, 83, 54, 67, 164, 6, 137, 94, 72, 6, 105, 128, 128, 93, 48, 176, 11, 4, 246, 138, 48, 180, 133, 90, 142, 192, 24, 193, 111, 142, 31, 76, 111, 110, 234, 153, 90, 208, 192, 31, 124, 95, 102, 49, 158, 99, 52, 220, 165, 94, 251, 68, 69, 121, 16, 224, 194], - "pop": [168, 50, 233, 193, 15, 136, 65, 72, 123, 148, 129, 176, 38, 198, 209, 47, 28, 204, 176, 144, 57, 251, 42, 28, 66, 76, 89, 97, 158, 63, 54, 198, 194, 176, 135, 221, 14, 185, 197, 225, 202, 98, 243, 74, 233, 225, 143, 151, 147, 177, 170, 117, 66, 165, 66, 62, 33, 216, 232, 75, 68, 114, 195, 22, 100, 65, 44, 198, 4, 166, 102, 233, 253, 240, 59, 175, 60, 117, 142, 114, 140, 122, 17, 87, 110, 187, 1, 17, 10, 195, 154, 13, 249, 86, 54, 226] - } - "#; - - fn golden_value() -> BlsVerificationKeyProofOfPossession { - let mut rng = ChaCha20Rng::from_seed([0u8; 32]); - let sk = BlsSigningKey::generate(&mut rng); - BlsVerificationKeyProofOfPossession { - vk: BlsVerificationKey::from(&sk), - pop: BlsProofOfPossession::from(&sk), - } - } - - #[test] - fn golden_conversions() { - let value = serde_json::from_str(GOLDEN_JSON) - .expect("This JSON deserialization should not fail"); - assert_eq!(golden_value(), value); - - let serialized = - serde_json::to_string(&value).expect("This JSON serialization should not fail"); - let golden_serialized = serde_json::to_string(&golden_value()) - .expect("This JSON serialization should not fail"); - assert_eq!(golden_serialized, serialized); - } - } -} diff --git a/mithril-stm/src/lib.rs b/mithril-stm/src/lib.rs index 3bca5aaafce..7385a621432 100644 --- a/mithril-stm/src/lib.rs +++ b/mithril-stm/src/lib.rs @@ -118,6 +118,7 @@ mod key_registration; mod membership_commitment; mod parameters; mod participant; +mod proof_system; mod signature_scheme; mod single_signature; From 1de66849019e797b263b913158dc40935f6e593a Mon Sep 17 00:00:00 2001 From: curiecrypt Date: Thu, 20 Nov 2025 20:52:07 +0300 Subject: [PATCH 4/4] include missing folders --- mithril-stm/src/proof_system/concatenation.rs | 248 ++++++++++++++++++ mithril-stm/src/proof_system/mod.rs | 3 + 2 files changed, 251 insertions(+) create mode 100644 mithril-stm/src/proof_system/concatenation.rs create mode 100644 mithril-stm/src/proof_system/mod.rs diff --git a/mithril-stm/src/proof_system/concatenation.rs b/mithril-stm/src/proof_system/concatenation.rs new file mode 100644 index 00000000000..af9d3887fb9 --- /dev/null +++ b/mithril-stm/src/proof_system/concatenation.rs @@ -0,0 +1,248 @@ +use anyhow::Context; +use blake2::digest::{Digest, FixedOutput}; + +use serde::{Deserialize, Serialize}; + +use crate::key_registration::RegisteredParty; +use crate::membership_commitment::MerkleBatchPath; +use crate::signature_scheme::{BlsSignature, BlsVerificationKey}; +use crate::{ + AggregateSignatureError, AggregateVerificationKey, BasicVerifier, Clerk, Parameters, + SingleSignature, SingleSignatureWithRegisteredParty, StmResult, +}; + +/// `ConcatenationProof` uses the "concatenation" proving system (as described in Section 4.3 of the original paper.) +/// This means that the aggregated signature contains a vector with all individual signatures. +/// BatchPath is also a part of the aggregate signature which covers path for all signatures. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(bound( + serialize = "MerkleBatchPath: Serialize", + deserialize = "MerkleBatchPath: Deserialize<'de>" +))] +pub struct ConcatenationProof { + pub(crate) signatures: Vec, + /// The list of unique merkle tree nodes that covers path for all signatures. + pub batch_proof: MerkleBatchPath, +} + +impl ConcatenationProof { + /// Aggregate a set of signatures for their corresponding indices. + /// + /// This function first deduplicates the repeated signatures, and if there are enough signatures, it collects the merkle tree indexes of unique signatures. + /// The list of merkle tree indexes is used to create a batch proof, to prove that all signatures are from eligible signers. + /// + /// It returns an instance of `ConcatenationProof`. + pub fn aggregate_signatures( + clerk: &Clerk, + sigs: &[SingleSignature], + msg: &[u8], + ) -> StmResult> { + let sig_reg_list = sigs + .iter() + .map(|sig| SingleSignatureWithRegisteredParty { + sig: sig.clone(), + reg_party: clerk.closed_reg.reg_parties[sig.signer_index as usize], + }) + .collect::>(); + + let avk = AggregateVerificationKey::from(&clerk.closed_reg); + let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg); + let mut unique_sigs = BasicVerifier::select_valid_signatures_for_k_indices( + &clerk.closed_reg.total_stake, + &clerk.params, + &msgp, + &sig_reg_list, + ) + .with_context( + || "Failed to aggregate unique signatures during selection for the k indices.", + )?; + + unique_sigs.sort_unstable(); + + let mt_index_list = unique_sigs + .iter() + .map(|sig_reg| sig_reg.sig.signer_index as usize) + .collect::>(); + + let batch_proof = clerk + .closed_reg + .merkle_tree + .compute_merkle_tree_batch_path(mt_index_list); + + Ok(Self { + signatures: unique_sigs, + batch_proof, + }) + } + + /// Verify all checks from signatures, except for the signature verification itself. + /// + /// Indices and quorum are checked by `BasicVerifier::preliminary_verify` with `msgp`. + /// It collects leaves from signatures and checks the batch proof. + /// After batch proof is checked, it collects and returns the signatures and + /// verification keys to be used by aggregate verification. + fn preliminary_verify( + &self, + msg: &[u8], + avk: &AggregateVerificationKey, + parameters: &Parameters, + ) -> StmResult<(Vec, Vec)> { + let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg); + BasicVerifier::preliminary_verify( + &avk.get_total_stake(), + &self.signatures, + parameters, + &msgp, + ) + .with_context(|| "Preliminary verification of aggregate signatures failed.")?; + + let leaves = self + .signatures + .iter() + .map(|r| r.reg_party) + .collect::>(); + + avk.get_merkle_tree_batch_commitment() + .verify_leaves_membership_from_batch_path(&leaves, &self.batch_proof) + .with_context(|| "Batch proof is invalid in preliminary verification.")?; + + Ok(BasicVerifier::collect_signatures_verification_keys( + &self.signatures, + )) + } + + /// Verify concatenation proof, by checking that + /// * each signature contains only valid indices, + /// * the lottery is indeed won by each one of them, + /// * the merkle tree path is valid, + /// * the aggregate signature validates with respect to the aggregate verification key + /// (aggregation is computed using functions `MSP.BKey` and `MSP.BSig` as described in Section 2.4 of the paper). + pub fn verify( + &self, + msg: &[u8], + avk: &AggregateVerificationKey, + parameters: &Parameters, + ) -> StmResult<()> { + let msgp = avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg); + let (sigs, vks) = self + .preliminary_verify(msg, avk, parameters) + .with_context(|| "Aggregate signature verification failed")?; + + BlsSignature::verify_aggregate(msgp.as_slice(), &vks, &sigs) + .with_context(|| "Aggregate signature verification failed")?; + Ok(()) + } + + /// Batch verify a set of signatures, with different messages and avks. + pub fn batch_verify( + stm_signatures: &[Self], + msgs: &[Vec], + avks: &[AggregateVerificationKey], + parameters: &[Parameters], + ) -> StmResult<()> { + let batch_size = stm_signatures.len(); + assert_eq!( + batch_size, + msgs.len(), + "Number of messages should correspond to size of the batch" + ); + assert_eq!( + batch_size, + avks.len(), + "Number of avks should correspond to size of the batch" + ); + assert_eq!( + batch_size, + parameters.len(), + "Number of parameters should correspond to size of the batch" + ); + + let mut aggr_sigs = Vec::with_capacity(batch_size); + let mut aggr_vks = Vec::with_capacity(batch_size); + for (idx, sig_group) in stm_signatures.iter().enumerate() { + sig_group.preliminary_verify(&msgs[idx], &avks[idx], ¶meters[idx])?; + let grouped_sigs: Vec = + sig_group.signatures.iter().map(|sig_reg| sig_reg.sig.sigma).collect(); + let grouped_vks: Vec = sig_group + .signatures + .iter() + .map(|sig_reg| sig_reg.reg_party.0) + .collect(); + + let (aggr_vk, aggr_sig) = BlsSignature::aggregate(&grouped_vks, &grouped_sigs).unwrap(); + aggr_sigs.push(aggr_sig); + aggr_vks.push(aggr_vk); + } + + let concat_msgs: Vec> = msgs + .iter() + .zip(avks.iter()) + .map(|(msg, avk)| avk.get_merkle_tree_batch_commitment().concatenate_with_message(msg)) + .collect(); + + BlsSignature::batch_verify_aggregates(&concat_msgs, &aggr_vks, &aggr_sigs)?; + Ok(()) + } + + /// Convert concatenation proof to bytes + /// # Layout + /// * Number of the pairs of Signatures and Registered Parties (SigRegParty) (as u64) + /// * Pairs of Signatures and Registered Parties (prefixed with their size as u64) + /// * Batch proof + pub fn to_bytes(&self) -> Vec { + let mut out = Vec::new(); + out.extend_from_slice(&u64::try_from(self.signatures.len()).unwrap().to_be_bytes()); + for sig_reg in &self.signatures { + out.extend_from_slice(&u64::try_from(sig_reg.to_bytes().len()).unwrap().to_be_bytes()); + out.extend_from_slice(&sig_reg.to_bytes()); + } + let proof = &self.batch_proof; + out.extend_from_slice(&proof.to_bytes()); + + out + } + + ///Extract a concatenation proof from a byte slice. + pub fn from_bytes(bytes: &[u8]) -> StmResult> { + let mut bytes_index = 0; + + let mut u64_bytes = [0u8; 8]; + u64_bytes.copy_from_slice( + bytes + .get(bytes_index..bytes_index + 8) + .ok_or(AggregateSignatureError::SerializationError)?, + ); + let total_sigs = usize::try_from(u64::from_be_bytes(u64_bytes)) + .map_err(|_| AggregateSignatureError::SerializationError)?; + bytes_index += 8; + + let mut sig_reg_list = Vec::with_capacity(total_sigs); + for _ in 0..total_sigs { + u64_bytes.copy_from_slice( + bytes + .get(bytes_index..bytes_index + 8) + .ok_or(AggregateSignatureError::SerializationError)?, + ); + let sig_reg_size = usize::try_from(u64::from_be_bytes(u64_bytes)) + .map_err(|_| AggregateSignatureError::SerializationError)?; + let sig_reg = SingleSignatureWithRegisteredParty::from_bytes::( + bytes + .get(bytes_index + 8..bytes_index + 8 + sig_reg_size) + .ok_or(AggregateSignatureError::SerializationError)?, + )?; + bytes_index += 8 + sig_reg_size; + sig_reg_list.push(sig_reg); + } + + let batch_proof = MerkleBatchPath::from_bytes( + bytes + .get(bytes_index..) + .ok_or(AggregateSignatureError::SerializationError)?, + )?; + + Ok(ConcatenationProof { + signatures: sig_reg_list, + batch_proof, + }) + } +} diff --git a/mithril-stm/src/proof_system/mod.rs b/mithril-stm/src/proof_system/mod.rs new file mode 100644 index 00000000000..77d1e9f4481 --- /dev/null +++ b/mithril-stm/src/proof_system/mod.rs @@ -0,0 +1,3 @@ +mod concatenation; + +pub use concatenation::*;