Expand description
FROST: Flexible Round-Optimized Schnorr Threshold Signatures
FROST is a threshold signature scheme that produces standard Schnorr signatures with only 2 rounds of communication. It’s significantly more efficient than threshold ECDSA while maintaining strong security properties.
§Features
- 2-round signing protocol: More efficient than threshold ECDSA (3+ rounds)
- Standard Schnorr signatures: Output is indistinguishable from single-party Schnorr
- Robust against rogue-key attacks: Using proof-of-possession
- Supports any t-of-n threshold: Flexible threshold configurations
- Dealer-free key generation: Based on Pedersen DKG
§Protocol Overview
-
Key Generation Phase (one-time setup):
- Run distributed key generation (DKG) to create shares
- Each participant gets a secret share and the group public key
-
Preprocessing Phase (can be done in advance):
- Each signer generates commitment pairs (d, e) and sends commitments
- Stores (d, e, D, E) for later use
-
Signing Round 1:
- Coordinator selects signing set and message
- Each signer reveals one commitment pair (D_i, E_i)
-
Signing Round 2:
- Coordinator computes binding value and challenge
- Each signer computes partial signature z_i
- Coordinator aggregates into full signature (R, z)
§Example
use chie_crypto::frost::{FrostKeygen, FrostSigner, aggregate_frost_signatures, verify_frost_signature};
// Setup: 2-of-3 threshold
let threshold = 2;
let num_signers = 3;
// Key generation (one-time setup)
let mut keygen = FrostKeygen::new(threshold, num_signers);
let shares = keygen.generate_shares();
// Create signers
let mut signers: Vec<_> = shares.iter().enumerate()
.map(|(i, share)| FrostSigner::new(i + 1, share.clone(), keygen.group_public_key()))
.collect();
// Preprocessing: Generate nonce commitments
for signer in &mut signers {
signer.preprocess();
}
let message = b"Transaction data";
// Signing Round 1: Collect nonce commitments from threshold signers
let signing_set = vec![1, 2]; // Use signers 1 and 2
let commitments: Vec<_> = signing_set.iter()
.map(|&id| signers[id - 1].get_nonce_commitment())
.collect();
// Signing Round 2: Generate partial signatures
let partial_sigs: Vec<_> = signing_set.iter()
.map(|&id| signers[id - 1].sign(message, &signing_set, &commitments))
.collect::<Result<Vec<_>, _>>()
.unwrap();
// Aggregate into final signature
let signature = aggregate_frost_signatures(
message,
&signing_set,
&commitments,
&partial_sigs,
).unwrap();
// Verify with group public key
assert!(verify_frost_signature(&keygen.group_public_key(), message, &signature).is_ok());Structs§
- Frost
Keygen - FROST key generation using Pedersen DKG
- Frost
Nonce Commitment - Nonce commitment pair for FROST preprocessing
- Frost
Secret Share - Secret share for a FROST participant
- Frost
Signer - FROST signer instance
- Partial
Signature - Partial signature from a FROST signer
Enums§
Functions§
- aggregate_
frost_ signatures - Aggregate partial signatures into a full Schnorr signature
- verify_
frost_ signature - Verify a FROST signature