Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example - Centralized Telescope with BLS Signatures #114

Merged
merged 82 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
51d994c
simple bls signature
curiecrypt Dec 17, 2024
2d3d6d6
example with bls signatures v0
curiecrypt Dec 18, 2024
158395d
to bytes for sig
curiecrypt Dec 19, 2024
4f897da
centralized telescope with bls sig, basic flow
curiecrypt Dec 19, 2024
f364f7e
Merge branch 'main' into curiecrypt/example-centralized
curiecrypt Dec 19, 2024
036f467
registration feature
curiecrypt Dec 20, 2024
c492c0a
clippy
curiecrypt Dec 20, 2024
a9a563b
clippy warnings
curiecrypt Dec 20, 2024
d143670
prover
curiecrypt Dec 20, 2024
bc95a15
added struct AlbaThresholdProof
curiecrypt Dec 20, 2024
ea2eeb9
clippy warnings
curiecrypt Dec 20, 2024
bd151d4
verify alba proof
curiecrypt Dec 23, 2024
9faf078
clippy warnings
curiecrypt Dec 23, 2024
0f61dc5
re-structure the examples
curiecrypt Dec 23, 2024
0c09939
clippy warnings
curiecrypt Dec 23, 2024
57d60c8
Apply suggestions from code review
curiecrypt Jan 2, 2025
1d835ef
resolving centralized_telescope
curiecrypt Jan 2, 2025
718dfcc
resolving aggregate
curiecrypt Jan 2, 2025
c63483a
changed HashMap to BTreeSet for registered keys
curiecrypt Jan 2, 2025
b3aeba3
enhancements over new registration
curiecrypt Jan 3, 2025
a15fd73
clippy warnings
curiecrypt Jan 3, 2025
9237bcf
renaming the module and candidate
curiecrypt Jan 3, 2025
ef9affc
cargo fmt
curiecrypt Jan 3, 2025
cf33101
enhancements
curiecrypt Jan 3, 2025
8e0b3d5
clippy warnings
curiecrypt Jan 3, 2025
a1ac0fe
rename new to init for signer
curiecrypt Jan 3, 2025
13cb0b2
Signature struct as BlstSignature
curiecrypt Jan 3, 2025
a815d9b
try batch verification of bls v0
curiecrypt Jan 3, 2025
08b3873
unsafe helpers for signature aggregation
curiecrypt Jan 3, 2025
35dae91
batch verify aggregate
curiecrypt Jan 3, 2025
3df7bf6
clippy error and warnings
curiecrypt Jan 3, 2025
c24c21b
clippy error
curiecrypt Jan 3, 2025
3984938
remove allow dead code
curiecrypt Jan 3, 2025
72b7e0d
saturating add
curiecrypt Jan 3, 2025
a994074
cargo fmt
curiecrypt Jan 3, 2025
4b61ead
style and comments
curiecrypt Jan 7, 2025
bac3cdd
alba threshold signature including only proof signatures
curiecrypt Jan 7, 2025
7684e52
clippy warnings
curiecrypt Jan 7, 2025
1ff1e1e
Merge branch 'main' into curiecrypt/example-centralized
curiecrypt Jan 9, 2025
eb201b6
Merge branch 'main' into curiecrypt/example-centralized
curiecrypt Jan 10, 2025
c024de1
data length to 48
curiecrypt Jan 10, 2025
da836fa
registration with BTreeMap
curiecrypt Jan 10, 2025
1392fbd
simplify
curiecrypt Jan 14, 2025
5455640
revise registration
curiecrypt Jan 14, 2025
a16cb9a
revise signer
curiecrypt Jan 14, 2025
f701982
revise helpers
curiecrypt Jan 14, 2025
c839f4f
revise main
curiecrypt Jan 14, 2025
3942f0b
clippy warnings
curiecrypt Jan 14, 2025
738f250
clippy error and warnings
curiecrypt Jan 14, 2025
0aee2e5
readme added
curiecrypt Jan 14, 2025
f034c73
correct the format
curiecrypt Jan 14, 2025
73c4bbe
Apply suggestions from code review
curiecrypt Jan 15, 2025
6c67fdf
fmt
curiecrypt Jan 15, 2025
cfde2f3
revise registration
curiecrypt Jan 15, 2025
21828e3
removing commitment from ATS
curiecrypt Jan 15, 2025
d6c6f89
update readme
curiecrypt Jan 15, 2025
df7eec6
some basic tests
curiecrypt Jan 16, 2025
d98331f
some stylish prints
curiecrypt Jan 16, 2025
8adffa3
clippy
curiecrypt Jan 16, 2025
b80b2dc
Merge branch 'main' into curiecrypt/example-centralized
curiecrypt Jan 20, 2025
b73c638
merge signers
curiecrypt Jan 21, 2025
67400a0
Merge branch 'main' into curiecrypt/example-centralized
curiecrypt Jan 21, 2025
9e9f78e
Merge branch 'main' into curiecrypt/example-centralized
curiecrypt Jan 21, 2025
cf2d1f2
new structure adjustments
curiecrypt Jan 21, 2025
05504ad
Merge branch 'main' into curiecrypt/example-centralized
curiecrypt Jan 21, 2025
63f1b48
revising basic tests
curiecrypt Jan 21, 2025
cc8ae80
revising for clippy
curiecrypt Jan 21, 2025
89b5062
clippy
curiecrypt Jan 21, 2025
bf90550
fmt
curiecrypt Jan 21, 2025
431c3ae
change visibility in new signer
curiecrypt Jan 21, 2025
14b3dda
readme update
curiecrypt Jan 21, 2025
e7ad937
re-include commitment to ATS
curiecrypt Jan 21, 2025
0549f7e
clippy
curiecrypt Jan 21, 2025
3026533
revise readme
curiecrypt Jan 21, 2025
8c696e2
minor correction
curiecrypt Jan 21, 2025
868d9da
Merge branch 'main' into curiecrypt/example-centralized
curiecrypt Jan 21, 2025
f647d4a
Apply suggestions from code review
curiecrypt Jan 21, 2025
9690ddf
review suggestions: assert for al tests
curiecrypt Jan 21, 2025
4c73af9
3000 SPOs
curiecrypt Jan 22, 2025
b543fc8
realistic params
curiecrypt Jan 23, 2025
c0d00c5
measure time
curiecrypt Jan 23, 2025
f3d37f6
time and size
curiecrypt Jan 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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**:
curiecrypt marked this conversation as resolved.
Show resolved Hide resolved
- 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();
rrtoledo marked this conversation as resolved.
Show resolved Hide resolved
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
rrtoledo marked this conversation as resolved.
Show resolved Hide resolved
.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.");
}
curiecrypt marked this conversation as resolved.
Show resolved Hide resolved
}
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
Loading