An implementation of Schnorr signatures on the Ed25519 curve for both single and threshold numbers of signers (FROST).

Example: key generation with trusted dealer and FROST signing

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

// ANCHOR: tkg_gen

use frosted25519 as frost; use rand::threadrng; use std::collections::HashMap;

let mut rng = threadrng(); let maxsigners = 5; let minsigners = 3; let (shares, pubkeypackage) = frost::keys::generatewithdealer(maxsigners, minsigners, &mut rng)?;

// ANCHOREND: tkggen

// 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 = HashMap::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( participantidentifier, keypackage.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 signatureshares = Vec::new(); let commitmentsreceived = commitmentsmap.clone().intovalues().collect();

// ANCHOR: round2_package

let message = "message to sign".as_bytes();

// In practice, the SigningPackage must be sent to all participants

// involved in the current signing (at least min_signers participants),

// using an authenticate channel (and confidential if the message is secret).

let signingpackage = frost::SigningPackage::new(commitmentsreceived, message);

// ANCHOREND: round2package

//////////////////////////////////////////////////////////////////////////// // 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.push(signature_share);

}

//////////////////////////////////////////////////////////////////////////// // Aggregation: collects the signing shares from all participants, // generates the final signature. ////////////////////////////////////////////////////////////////////////////

// Aggregate (also verifies the signature shares)

// ANCHOR: aggregate

let groupsignature = frost::aggregate(&signingpackage, &signatureshares[..], &pubkeypackage)?;

// ANCHOR_END: aggregate

// Check that the threshold signature can be verified by the group public // key (the verification key).

// ANCHOR: verify

let issignaturevalid = pubkeypackage .grouppublic() .verify(message, &groupsignature) .isok();

// ANCHOR_END: verify

assert!(issignaturevalid);

Ok::<(), frost::Error>(())

```