Skip to content

Commit

Permalink
Example - Centralized Telescope with BLS Signatures (#114)
Browse files Browse the repository at this point in the history
## Content
This PR introduces an example demonstrating the ALBA Threshold Signature
scheme with the following workflow:

### Workflow
1. **Define Telescope Parameters**:
   - Set `nb_elements` to 1,000.
   - Configure `soundness_param` and `completeness_param` as 128.0.
- Calculate the prover set size (`set_size`) as 80% of `nb_elements` and
the lower bound as 20%.
   - Initialize the Telescope with these parameters.
2. **Initialize Signers**:
   - Create a list of candidate `Signer`s with randomly generated keys.
3. **Start Registration**:
   - Open a new `Registration`.
- Select and register a subset of candidate signers, tracking the number
of successfully registered signers.
4. **Close Registration**:
- Finalize the registration by computing the checksum of registered
keys.
   - Update registered signers with the computed checksum.
5. **Generate Individual Signatures**:
   - Select a subset of registered signers.
- Generate `IndividualSignature`s for each based on the input message.
6. **Generate ALBA Threshold Signature**:
- Aggregate valid individual signatures to create an
`AlbaThresholdSignature`.
   - Print details of the generated threshold signature if successful.
7. **Verify ALBA Threshold Signature**:
- Validate the generated threshold signature using the Telescope,
registration, and input message.
   - Print whether the verification succeeded or failed.

### Purpose
This example provides a detailed implementation of the ALBA Threshold
Signature workflow, showcasing the integration of Telescope parameters,
signer registration, individual signature generation, and threshold
signature verification.


## Pre-submit checklist

- Branch
    - [x] Commit sequence broadly makes sense
    - [x] Key commits have useful messages
- PR
    - [x] No clippy warnings in the CI
    - [x] Self-reviewed the diff
    - [x] Useful pull request description
    - [x] Reviewer requested


## Issue(s)
Closes #78

---------

Co-authored-by: Raphael <raphael.r.toledo@gmail.com>
  • Loading branch information
curiecrypt and rrtoledo authored Jan 23, 2025
1 parent b5629b0 commit e2990b9
Show file tree
Hide file tree
Showing 8 changed files with 780 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ include = ["**/*.rs", "Cargo.toml", "README.md", ".gitignore"]

[dependencies]
blake2 = "0.10.6"
rand = "0.8"

[dev-dependencies]
rand_core = "0.6.4"
rand_chacha = "0.3.1"
test-case = "3.3.1"
blst = "0.3.13"
time-elapsed = "0.1.0"

[lints.rust]
missing-copy-implementations = "warn"
Expand Down
84 changes: 84 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Centralized Telescope with BLS Signatures

## Workflow: ALBA Threshold Signature Example
1. **Define Telescope Parameters**:
- Set the number of elements (`nb_elements`) to 1,000.
- Specify `soundness_param` and `completeness_param` as 128.0 each.
- Calculate the prover set size (`set_size`) as 80% of `nb_elements` and the lower bound as 20%.
- Initialize the Telescope with these parameters.
2. **Initialize Signers**:
- Create a list of candidate signers (`Signer`s) with randomly generated keys.
3. **Start Registration**:
- Open a new `Registration`.
- Select and register a subset of candidate signers, updating the count of successfully registered signers.
4. **Close Registration**:
- Finalize the registration by computing the checksum of registered keys.
- Update each registered signer with the computed checksum.
5. **Generate Individual Signatures**:
- Select a subset of registered signers.
- Generate `IndividualSignature`s for each selected signer based on the input message.
6. **Generate ALBA Threshold Signature**:
- Use valid individual signatures to create an `AlbaThresholdSignature`.
- If successful, print details of the generated threshold signature.
7. **Verify ALBA Threshold Signature**:
- Verify the generated threshold signature using the Telescope parameters, registration, and input message.
- Print whether the verification succeeded or failed.

## Code structure
### Signer
- Signer structure covering signing key, verification key, registration index and registration checksum.
- Initialization of a signer is handled by `new` function.
- It generates a signing key and its verification key.
- The index is set to `0` since the signer is not registered yet.
- The checksum is not initialized since the registration is not closed.
- An initialized signer can be registered with `register` function.
- If the registration is not already closed and the signer is not already registered, the registration can be done.
- The verification key is added to the registration table with the next index.
- The `index` field of the signer is updated with the registration index.
- After the registration is closed, a registered signer is updated with the `get_closed_registration`.
- If the registration is closed and the key of the signer exists in the registered keys, `checksum` field of the signer is updated.

### Registration
- Registration includes registered keys as a `BTreeMap` and the checksum of all registered keys.
- New registration is initialized by `new` function with an empty `BTreeMap`. Checksum is not initialized in this phase.
- Registration process is closed with `close` function if the registration is not already closed. It is done by hashing all verification keys registered into the `checksum`.

### Signature
- A single signature is represented with `IndividualSignature`.
- It contains the BLS signature and the index of the signer of signature.
- An `IndividualSignature` is verified by `verify` function with given verification key, against `commitment = Hash(checksum||msg)`.

### Helpers
- Get commitment:
- Instead of signing only the message, the signature is generated by signing a commitment.
- This is the helper function to compute a commitment by hashing `(checksum || msg)`.
- Collect valid signatures:
- The prover set is built on the individual signatures.
- Each individual signature is validated by checking if the signer of a signature is registered and the signature is valid.
- Each valid signature is converted to its byte representation.
- A hashmap, including the byte representation of a valid signature and its registration index is returned.
- Validate signatures:
- The verifier needs to validate signatures that creates the alba proof. The process is as follows:
- Convert each element in the element sequence of alba proof to a bls signature.
- Aggregate collected signatures by calling `AggregateSignature::aggregate`.
- Collect verification keys by using the indices. Get each verification key from registration if its index is included in `AlbaThresholdSignature.indices`.
- Aggregate collected verification keys by calling `AggregatePublicKey::aggregate`.
- Convert aggregated signature to a signature and aggregated public key to a public key.
- Verify them using `verify` function of bls signatures against the commitment.

### ALBA Threshold Signature
- It includes the alba proof, indices of the elements exist in the alba proof, and the commitment.
- The `prove` function:
- Checks whether the registration is closed,
- Creates the commitment,
- Collects the valid signatures with `collect_valid_signatures`,
- If the length of valid signatures is less than the set size provided, abort the process,
- Creates the prover set by collecting only the signatures from the hash map of valid signatures,
- Creates the alba proof,
- Collects the registration indices of the elements that are in the alba proof by using valid signatures hashmap.
- Returns the alba proof, indices, and the commitment.
- The `verify` function:
- Checks whether the registration is closed,
- Creates the commitment and compares it with the provided commitment,
- Verifies the signatures that are included in the alba proof by calling `validate_signatures` function.
- Verifies the alba proof.
115 changes: 115 additions & 0 deletions examples/aggregate_signature/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use crate::aggregate_signature::registration::Registration;
use crate::aggregate_signature::signature::IndividualSignature;
use crate::{AlbaThresholdSignature, Element};
use blake2::digest::{Update, VariableOutput};
use blake2::Blake2bVar;
use blst::min_sig::{AggregatePublicKey, AggregateSignature, PublicKey, Signature};
use blst::BLST_ERROR;
use std::collections::HashMap;

/// Helper function to compute a commitment by hashing `(checksum || msg)`
pub(crate) fn get_commitment<const N: usize>(checksum: &[u8], msg: &[u8]) -> [u8; N] {
let mut hasher = Blake2bVar::new(N).expect("Invalid hash size");
let mut commitment = [0u8; N];

hasher.update(checksum);
hasher.update(msg);
hasher
.finalize_variable(&mut commitment)
.expect("Hash finalization failed");
commitment
}

/// Collect valid signatures by verifying each individual signature.
/// Returns a hashmap of valid signature bytes to their corresponding index.
pub(crate) fn collect_valid_signatures<const N: usize>(
signature_list: &[IndividualSignature],
registration: &Registration,
msg: &[u8],
) -> HashMap<Element, usize> {
let mut valid_signatures = HashMap::new();

match &registration.checksum {
Some(checksum) => {
for sig in signature_list {
if let Some(verification_key) = registration.registered_keys.get(&sig.index) {
if sig.verify::<N>(checksum, msg, verification_key) {
valid_signatures.insert(sig.signature.to_bytes(), sig.index);
}
} else {
println!("Warning: No verification key found for index {}", sig.index);
}
}
}
None => {
println!("Error: Registration is not closed. Cannot verify signatures.");
}
}
valid_signatures
}

/// Validate the signatures in `alba_threshold_signature` against the provided message and registration.
/// Returns `true` if all signatures are valid and correctly aggregated, `false` otherwise.
pub(crate) fn validate_signatures(
alba_threshold_signature: &AlbaThresholdSignature,
registration: &Registration,
commitment: &[u8],
) -> bool {
let mut signatures = Vec::with_capacity(alba_threshold_signature.proof.element_sequence.len());
for sig_bytes in &alba_threshold_signature.proof.element_sequence {
if let Ok(signature) = Signature::from_bytes(sig_bytes.as_slice()) {
signatures.push(signature);
} else {
println!("Error: Failed to parse signature from bytes.");
return false;
}
}
let signature_refs: Vec<&Signature> = signatures.iter().collect();
let aggregate_signature =
if let Ok(agg_sig) = AggregateSignature::aggregate(signature_refs.as_slice(), false) {
agg_sig.to_signature()
} else {
println!("Error: Failed to aggregate signatures.");
return false;
};

let public_key_refs: Vec<&PublicKey> = alba_threshold_signature
.indices
.iter()
.filter_map(|index| registration.registered_keys.get(index))
.collect();

if public_key_refs.len() != signature_refs.len() {
println!("Error: Mismatch between public keys and signatures count.");
return false;
}

let aggregate_public_key =
if let Ok(agg_pk) = AggregatePublicKey::aggregate(public_key_refs.as_slice(), false) {
agg_pk.to_public_key()
} else {
println!("Error: Failed to aggregate public keys.");
return false;
};

let result =
aggregate_signature.verify(false, commitment, &[], &[], &aggregate_public_key, false);
if result == BLST_ERROR::BLST_SUCCESS {
true
} else {
println!("Error: Aggregate signature verification failed.");
false
}
}

pub(crate) fn ats_size<const N: usize>(ats: &AlbaThresholdSignature) -> usize {
let nb_elements = ats.indices.len();
let size_indices = nb_elements.saturating_mul(8);
let size_elements = nb_elements.saturating_mul(N);
let size_commitment = ats.commitment.len();

size_indices
.saturating_add(size_elements)
.saturating_add(size_commitment)
.saturating_add(16)
}
4 changes: 4 additions & 0 deletions examples/aggregate_signature/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub(crate) mod helpers;
pub(crate) mod registration;
pub(crate) mod signature;
pub(crate) mod signer;
45 changes: 45 additions & 0 deletions examples/aggregate_signature/registration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use blake2::digest::{Update, VariableOutput};
use blake2::Blake2bVar;
use blst::min_sig::PublicKey;
use std::collections::BTreeMap;

type Keys = BTreeMap<usize, PublicKey>;

/// Structure for registration functionality.
#[derive(Debug, Clone)]
pub(crate) struct Registration {
/// Registered keys
pub(crate) registered_keys: Keys,
/// Checksum of registry data
pub(crate) checksum: Option<Vec<u8>>,
}

impl Registration {
/// Initialize key registration
pub(crate) fn new() -> Self {
Self {
registered_keys: BTreeMap::new(),
checksum: None,
}
}

/// Close the registration by computing a checksum if it is not already closed.
pub(crate) fn close<const N: usize>(&mut self) {
if self.checksum.is_some() {
println!("Registration is already closed.");
return;
}

let mut hasher = Blake2bVar::new(N).expect("Invalid hash size");
let mut hash_output = vec![0u8; N];

for key in self.registered_keys.values() {
hasher.update(key.to_bytes().as_slice());
}

hasher
.finalize_variable(&mut hash_output)
.expect("Hash finalization failed");
self.checksum = Some(hash_output);
}
}
28 changes: 28 additions & 0 deletions examples/aggregate_signature/signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::aggregate_signature::helpers::get_commitment;
use blst::min_sig::{PublicKey, Signature};
use blst::BLST_ERROR;

/// Single signature
#[derive(Debug, Clone)]
pub(crate) struct IndividualSignature {
/// Signature of type bls signature
pub(crate) signature: Signature,
/// Registration index of the signer
pub(crate) index: usize,
}

impl IndividualSignature {
/// Verify signature against `commitment = Hash(checksum || msg)`
pub(crate) fn verify<const N: usize>(
&self,
checksum: &[u8],
msg: &[u8],
verification_key: &PublicKey,
) -> bool {
let commitment = get_commitment::<N>(checksum, msg);
let result = self
.signature
.verify(false, &commitment, &[], &[], verification_key, false);
result == BLST_ERROR::BLST_SUCCESS
}
}
89 changes: 89 additions & 0 deletions examples/aggregate_signature/signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use crate::aggregate_signature::helpers::get_commitment;
use crate::aggregate_signature::registration::Registration;
use crate::aggregate_signature::signature::IndividualSignature;
use blst::min_sig::{PublicKey, SecretKey};
use rand_core::{CryptoRng, RngCore};

/// Threshold signature signer
#[derive(Debug, Clone)]
pub(crate) struct Signer {
/// Signature generation key
signing_key: SecretKey,
/// Signature verification key
verification_key: PublicKey,
/// Registration index of the signer
pub(crate) index: usize,
/// Closed registration checksum
pub(crate) checksum: Option<Vec<u8>>,
}

impl Signer {
/// Create signing key and verification key for a signer
pub(crate) fn init(rng: &mut (impl RngCore + CryptoRng)) -> Self {
let mut ikm = [0u8; 32];
rng.fill_bytes(&mut ikm);

let sk = SecretKey::key_gen(&ikm, &[])
.expect("Error: Key generation failed due to insufficient IKM length. This should never happen.");
let vk = sk.sk_to_pk();

Self {
signing_key: sk,
verification_key: vk,
index: 0,
checksum: None,
}
}

/// Register signer's verification key. If the registration is not closed, i.e., no checksum found and the
/// signer's verification key is already registered return `None`. Otherwise, insert new verification key with a
/// new index. Return the `RegisteredSigner`.
pub(crate) fn register(&mut self, registration: &mut Registration) -> bool {
if registration.checksum.is_none() {
if registration
.registered_keys
.values()
.any(|v| *v == self.verification_key)
{
println!("Error: Key already registered!");
return false;
}
let index = registration.registered_keys.len().saturating_add(1);
registration
.registered_keys
.insert(index, self.verification_key);
self.index = index;
true
} else {
println!("Error: Cannot register, registration is closed!");
false
}
}

/// Get closed registration. Update the registered signer with the checksum of registration.
pub(crate) fn get_closed_registration(&mut self, registration: &Registration) {
match &registration.checksum {
Some(checksum) => {
if registration.registered_keys.contains_key(&self.index) {
self.checksum = Some(checksum.clone());
} else {
println!("Error: Registration is closed, but the signer is not registered.");
}
}
None => {
println!("Error: Registration is not closed.");
}
}
}

/// Create an individual signature by signing `commitment = Hash(checksum || msg)`
pub(crate) fn sign<const N: usize>(&self, msg: &[u8]) -> Option<IndividualSignature> {
let checksum = self.checksum.as_ref()?;
let commitment = get_commitment::<N>(checksum, msg);

Some(IndividualSignature {
signature: self.signing_key.sign(&commitment, &[], &[]),
index: self.index,
})
}
}
Loading

0 comments on commit e2990b9

Please sign in to comment.