An implementation of Schnorr signatures on the Ristretto group for both single and threshold numbers of signers (FROST).
Creating a key with a trusted dealer and splitting into shares; then signing a message and aggregating the signature. Note that the example just simulates a distributed scenario in a single thread and it abstracts away any communication between peers.
```rust
use frostristretto255 as frost; use rand::threadrng; use std::collections::{BTreeMap, HashMap};
let mut rng = threadrng(); let maxsigners = 5; let minsigners = 3; let (shares, pubkeypackage) = frost::keys::generatewithdealer( maxsigners, minsigners, frost::keys::IdentifierList::Default, &mut rng, )?;
// Verifies the secret shares from the dealer and store them in a HashMap. // In practice, the KeyPackages must be sent to its respective participants // through a confidential and authenticated channel. let mut key_packages: HashMap<_, _> = HashMap::new();
for (identifier, secretshare) in shares { # // ANCHOR: tkgverify let keypackage = frost::keys::KeyPackage::tryfrom(secretshare)?; # // ANCHOREND: tkgverify keypackages.insert(identifier, key_package); }
let mut noncesmap = HashMap::new(); let mut commitmentsmap = BTreeMap::new();
//////////////////////////////////////////////////////////////////////////// // Round 1: generating nonces and signing commitments for each participant ////////////////////////////////////////////////////////////////////////////
// In practice, each iteration of this loop will be executed by its respective participant. for participantindex in 1..(minsigners as u16 + 1) { let participantidentifier = participantindex.tryinto().expect("should be nonzero"); let keypackage = &keypackages[&participantidentifier]; // Generate one (1) nonce and one SigningCommitments instance for each // participant, up to threshold. # // ANCHOR: round1commit let (nonces, commitments) = frost::round1::commit( keypackages[&participantidentifier].secretshare(), &mut rng, ); # // ANCHOREND: round1commit // In practice, the nonces must be kept by the participant to use in the // next round, while the commitment must be sent to the coordinator // (or to every other participant if there is no coordinator) using // an authenticated channel. noncesmap.insert(participantidentifier, nonces); commitmentsmap.insert(participantidentifier, commitments); }
// This is what the signature aggregator / coordinator needs to do: // - decide what message to sign // - take one (unused) commitment per signing participant let mut signature_shares = HashMap::new();
let message = "message to sign".as_bytes();
let signingpackage = frost::SigningPackage::new(commitmentsmap, message);
//////////////////////////////////////////////////////////////////////////// // Round 2: each participant generates their signature share ////////////////////////////////////////////////////////////////////////////
// In practice, each iteration of this loop will be executed by its respective participant. for participantidentifier in noncesmap.keys() { let keypackage = &keypackages[participant_identifier];
let nonces = &nonces_map[participant_identifier];
// Each participant generates their signature share.
# // ANCHOR: round2_sign
let signature_share = frost::round2::sign(&signing_package, nonces, key_package)?;
# // ANCHOR_END: round2_sign
// In practice, the signature share must be sent to the Coordinator
// using an authenticated channel.
signature_shares.insert(*participant_identifier, signature_share);
}
//////////////////////////////////////////////////////////////////////////// // Aggregation: collects the signing shares from all participants, // generates the final signature. ////////////////////////////////////////////////////////////////////////////
// Aggregate (also verifies the signature shares)
let groupsignature = frost::aggregate(&signingpackage, &signatureshares, &pubkeypackage)?;
// Check that the threshold signature can be verified by the group public // key (the verification key).
let issignaturevalid = pubkeypackage .grouppublic() .verify(message, &groupsignature) .isok();
assert!(issignaturevalid);
```