An implementation of Schnorr signatures on the P-256 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 use frostp256 as frost; use rand::threadrng; use std::collections::HashMap;

let mut rng = threadrng(); let maxsigners = 5; let minsigners = 3; let (shares, pubkeys) = frost::keys::generatewithdealer(maxsigners, min_signers, &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 (k, v) in shares { let keypackage = frost::keys::KeyPackage::tryfrom(v)?; keypackages.insert(k, keypackage); }

let mut nonces = HashMap::new(); let mut commitments = 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"); // Generate one (1) nonce and one SigningCommitments instance for each // participant, up to _threshold. let (nonce, commitment) = frost::round1::commit( participantidentifier, keypackages[&participantidentifier].secretshare(), &mut rng, ); // In practice, the nonces and commitment must be sent to the coordinator // (or to every other participant if there is no coordinator) using // an authenticated channel. nonces.insert(participantidentifier, nonce); commitments.insert(participantidentifier, commitment); }

// 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 message = "message to sign".asbytes(); let comms = commitments.clone().intovalues().collect(); // In practice, the SigningPackage must be sent to all participants // involved in the current signing (at least minsigners participants), // using an authenticate channel (and confidential if the message is secret). let signingpackage = frost::SigningPackage::new(comms, message.tovec());

//////////////////////////////////////////////////////////////////////////// // 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 nonces.keys() { let keypackage = &keypackages[participantidentifier];

let nonces_to_use = &nonces[participant_identifier];

// Each participant generates their signature share.
let signature_share = frost::round2::sign(&signing_package, nonces_to_use, key_package)?;

// 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) let groupsignature = frost::aggregate(&signingpackage, &signature_shares[..], &pubkeys)?;

// Check that the threshold signature can be verified by the group public // key (the verification key). assert!(pubkeys .grouppublic .verify(message, &groupsignature) .is_ok());

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

```