diff --git a/Cargo.toml b/Cargo.toml index 64724a99..3eb620ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ test-case = "3.3.1" blst = "0.3.13" time-elapsed = "0.1.0" + [lints.rust] missing-copy-implementations = "warn" missing-debug-implementations = "warn" diff --git a/examples/simple_example.rs b/examples/simple_example.rs new file mode 100644 index 00000000..3aef04b2 --- /dev/null +++ b/examples/simple_example.rs @@ -0,0 +1,68 @@ +//! Centralized Telescope example with BLS signatures + +mod simple_threshold_signature; + +use crate::simple_threshold_signature::signature::Signature; +use crate::simple_threshold_signature::signer::Signer; +use crate::simple_threshold_signature::threshold_signature::ThresholdSignature; +use alba::centralized_telescope::Telescope; +use blst::min_sig::PublicKey; +use rand_chacha::ChaCha20Rng; +use rand_core::{RngCore, SeedableRng}; + +const DATA_LENGTH: usize = 48; +type Element = [u8; DATA_LENGTH]; + +fn main() { + let mut rng = ChaCha20Rng::from_seed(Default::default()); + let mut msg = [0u8; 16]; + rng.fill_bytes(&mut msg); + + println!("\n-------------- ALBA with Multi-Signature ---------------"); + println!("--------------------------------------------------------"); + + // Define telescope parameters + let nb_elements: u64 = 1_000; + let soundness_param = 128.0; + let completeness_param = 128.0; + let set_size = nb_elements.saturating_mul(80).div_ceil(100); + let lower_bound = nb_elements.saturating_mul(20).div_ceil(100); + + println!(" - Soundness parameter: {soundness_param}"); + println!(" - Completeness parameter: {completeness_param}"); + println!(" - Prover set size: {set_size}"); + println!(" - Lower bound: {lower_bound}"); + + // Create the telescope structure + let alba = Telescope::create(soundness_param, completeness_param, set_size, lower_bound); + + let mut public_key_list: Vec<(usize, PublicKey)> = Vec::with_capacity(nb_elements as usize); + let mut signature_list: Vec = Vec::with_capacity(set_size as usize); + + // Generate `nb_elements` signers and `set_size` signatures + for i in 0..nb_elements as usize { + let signer = Signer::new(&mut rng); + public_key_list.push((i, signer.verification_key)); + if i < set_size as usize { + signature_list.push(signer.sign(&msg, i)); + } + } + println!("--------------------------------------------------------"); + println!(" -- {nb_elements} (sk, pk) are generated."); + println!(" -- {set_size} signatures are generated."); + + println!("--------------------------------------------------------"); + println!("----------- Generating Alba multi-signature. -----------"); + let (threshold_signature, indices) = + ThresholdSignature::aggregate(&signature_list, &alba, &public_key_list); + println!("-- Alba multi-signature is generated."); + println!("--------------------------------------------------------"); + println!("----------- Verifying Alba multi-signature. ------------"); + + if threshold_signature.verify(&msg, &alba, &public_key_list, &indices) { + println!("-- Verification successful."); + } else { + println!("-- Verification failed."); + } + println!("--------------------------------------------------------"); +} diff --git a/examples/simple_threshold_signature/mod.rs b/examples/simple_threshold_signature/mod.rs new file mode 100644 index 00000000..b18e8dfc --- /dev/null +++ b/examples/simple_threshold_signature/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod signature; +pub(crate) mod signer; +pub(crate) mod threshold_signature; diff --git a/examples/simple_threshold_signature/signature.rs b/examples/simple_threshold_signature/signature.rs new file mode 100644 index 00000000..9f46a298 --- /dev/null +++ b/examples/simple_threshold_signature/signature.rs @@ -0,0 +1,10 @@ +use blst::min_sig::Signature as BlsSignature; + +/// Single signature +#[derive(Debug, Clone)] +pub(crate) struct Signature { + /// Signature of type bls signature + pub(crate) signature: BlsSignature, + /// Verification index of the signer + pub(crate) index: usize, +} diff --git a/examples/simple_threshold_signature/signer.rs b/examples/simple_threshold_signature/signer.rs new file mode 100644 index 00000000..d3a9832e --- /dev/null +++ b/examples/simple_threshold_signature/signer.rs @@ -0,0 +1,31 @@ +use crate::simple_threshold_signature::signature::Signature; +use blst::min_sig::{PublicKey, SecretKey}; +use rand_core::{CryptoRng, RngCore}; + +pub(crate) struct Signer { + signing_key: SecretKey, + pub(crate) verification_key: PublicKey, +} + +impl Signer { + /// Create a pair of bls signing key and verification key + pub(crate) fn new(rng: &mut (impl RngCore + CryptoRng)) -> Self { + let mut ikm = [0u8; 32]; + rng.fill_bytes(&mut ikm); + let sk = SecretKey::key_gen(&ikm, &[]) + .expect("Error occurs when the length of ikm < 32. This will not happen here."); + let pk: PublicKey = sk.sk_to_pk(); + Self { + signing_key: sk, + verification_key: pk, + } + } + + /// Sign given message. Return the signature and given signer index. + pub(crate) fn sign(&self, msg: &[u8], index: usize) -> Signature { + Signature { + signature: self.signing_key.sign(msg, &[], &[]), + index, + } + } +} diff --git a/examples/simple_threshold_signature/threshold_signature.rs b/examples/simple_threshold_signature/threshold_signature.rs new file mode 100644 index 00000000..601ad872 --- /dev/null +++ b/examples/simple_threshold_signature/threshold_signature.rs @@ -0,0 +1,114 @@ +use crate::simple_threshold_signature::signature::Signature; +use crate::Element; +use alba::centralized_telescope::proof::Proof; +use alba::centralized_telescope::Telescope; +use blst::min_sig::{PublicKey, Signature as BlsSignature}; +use blst::BLST_ERROR; + +pub(crate) struct ThresholdSignature { + proof: Proof, +} + +impl ThresholdSignature { + // Create the Alba proof. Convert signatures to bytes and use them as the prover set. Create Alba proof with the + // prover set. Collect signatures by converting proof elements to bls signatures. Find each signature's index and + // collect them in a list. Return alba proof and the indices. + pub(crate) fn aggregate( + signatures: &[Signature], + alba: &Telescope, + public_key_list: &[(usize, PublicKey)], + ) -> (Self, Vec) { + // Convert signatures to bytes and collect as the prover set. + let prover_set = signatures + .iter() + .map(|s| s.signature.to_bytes()) + .collect::>(); + + println!("-- Creating alba proof. "); + // Create alba proof with the prover set + let proof = alba.prove(&prover_set).unwrap(); + println!("-- Alba proof created: "); + println!( + " - Numbers of retries done to find the proof: {}", + proof.retry_counter + ); + println!( + " - Index of the searched subtree to find the proof: {}", + proof.search_counter + ); + println!( + " - Number of elements in the proof sequence: {}", + proof.element_sequence.len() + ); + + // Convert proof elements to signatures to obtain their indexes + let proof_signatures: Vec = proof + .element_sequence + .iter() + .filter_map(|element| BlsSignature::from_bytes(element).ok()) + .collect(); + + // Collect the indices of the signatures that create alba proof. + let mut indices = Vec::with_capacity(proof_signatures.len()); + for sig in &proof_signatures { + if let Some(signature_entry) = signatures.iter().find(|entry| entry.signature == *sig) { + if public_key_list + .iter() + .any(|entry| entry.0 == signature_entry.index) + { + indices.push(signature_entry.index); + } + } + } + (Self { proof }, indices) + } + + /// Validates individual signatures in the threshold signature + /// This function verifies each individual signature separately. + /// The verification could also be done by using aggregation from blst library as follows: + /// - Aggregate signatures with: `AggregateSignature::aggregate`, + /// - Aggregate public keys with: `AggregatePublicKey::aggregate`, + /// - Convert aggregate signature to a signature and aggregate public key to a public key + /// - Verify the signature with public key against given message. + fn validate_signatures( + &self, + msg: &[u8], + public_key_list: &[(usize, PublicKey)], + indices: &[usize], + ) -> bool { + let mut signatures = Vec::with_capacity(self.proof.element_sequence.len()); + // Get the bls signatures from byte representation + for sig_bytes in &self.proof.element_sequence { + let Ok(signature) = BlsSignature::from_bytes(sig_bytes.as_slice()) else { + return false; + }; + signatures.push(signature); + } + + // Find the public key from the public key lest for the corresponding index (and signature) + // Verify the signature with this public key against given message. + for (signature, &index) in signatures.iter().zip(indices.iter()) { + if let Some((_, public_key)) = public_key_list.iter().find(|(idx, _)| *idx == index) { + if signature.verify(false, msg, &[], &[], public_key, false) + != BLST_ERROR::BLST_SUCCESS + { + return false; + } + } else { + return false; + } + } + true + } + + /// Verify `ThresholdSignature` by validating the signatures included in alba proof and verifying the alba proof. + pub(crate) fn verify( + &self, + msg: &[u8], + alba: &Telescope, + public_key_list: &[(usize, PublicKey)], + indices: &[usize], + ) -> bool { + self.validate_signatures(msg, public_key_list, indices) && alba.verify(&self.proof) + } +}