Improve examples (#160)
* add trusted dealer example * add example for each ciphersuite-specific crate * simplify example * improve example; use ? instead of unwrap
This commit is contained in:
parent
6df6e32221
commit
7b83737137
93
README.md
93
README.md
|
@ -30,98 +30,5 @@ scratch. End-users should not use `frost-core` if they want to sign and verify s
|
||||||
should use the crate specific to their ciphersuite/curve parameters that uses `frost-core` as a
|
should use the crate specific to their ciphersuite/curve parameters that uses `frost-core` as a
|
||||||
dependency.
|
dependency.
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use std::{collections::HashMap, convert::TryFrom};
|
|
||||||
|
|
||||||
use rand::thread_rng;
|
|
||||||
|
|
||||||
use frost_ristretto255::frost;
|
|
||||||
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
let numsigners = 5;
|
|
||||||
let threshold = 3;
|
|
||||||
let (shares, pubkeys) =
|
|
||||||
frost::keys::keygen_with_dealer(numsigners, threshold, &mut rng).unwrap();
|
|
||||||
|
|
||||||
// Verifies the secret shares from the dealer
|
|
||||||
let key_packages: Vec<frost::keys::KeyPackage> = shares
|
|
||||||
.into_iter()
|
|
||||||
.map(|share| frost::keys::KeyPackage::try_from(share).unwrap())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut nonces: HashMap<u16, Vec<frost::round1::SigningNonces>> = HashMap::new();
|
|
||||||
let mut commitments: HashMap<u16, Vec<frost::round1::SigningCommitments>> = HashMap::new();
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Round 1: generating nonces and signing commitments for each participant
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
for participant_index in 1..(threshold + 1) {
|
|
||||||
// Generate one (1) nonce and one SigningCommitments instance for each
|
|
||||||
// participant, up to _threshold_.
|
|
||||||
let (nonce, commitment) = frost::round1::commit(participant_index as u16, &mut rng);
|
|
||||||
nonces.insert(participant_index as u16, nonce);
|
|
||||||
commitments.insert(participant_index as u16, 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 signature_shares: Vec<frost::round2::SignatureShare> = Vec::new();
|
|
||||||
let message = "message to sign".as_bytes();
|
|
||||||
let comms = commitments.clone().into_values().flatten().collect();
|
|
||||||
let signing_package = frost::SigningPackage::new(comms, message.to_vec());
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Round 2: each participant generates their signature share
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
for participant_index in nonces.keys() {
|
|
||||||
let key_package = key_packages
|
|
||||||
.iter()
|
|
||||||
.find(|key_package| *participant_index == key_package.index)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let nonces_to_use = nonces.get(participant_index).unwrap()[0];
|
|
||||||
|
|
||||||
// Each participant generates their signature share.
|
|
||||||
let signature_share =
|
|
||||||
frost::round2::sign(&signing_package, &nonces_to_use, key_package).unwrap();
|
|
||||||
signature_shares.push(signature_share);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Aggregation: collects the signing shares from all participants,
|
|
||||||
// generates the final signature.
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Aggregate (also verifies the signature shares)
|
|
||||||
let group_signature_res = frost::aggregate(&signing_package, &signature_shares[..], &pubkeys);
|
|
||||||
|
|
||||||
assert!(group_signature_res.is_ok());
|
|
||||||
|
|
||||||
let group_signature = group_signature_res.unwrap();
|
|
||||||
|
|
||||||
// Check that the threshold signature can be verified by the group public
|
|
||||||
// key (the verification key).
|
|
||||||
assert!(pubkeys
|
|
||||||
.group_public
|
|
||||||
.verify(message, &group_signature)
|
|
||||||
.is_ok());
|
|
||||||
|
|
||||||
// Check that the threshold signature can be verified by the group public
|
|
||||||
// key (the verification key) from SharePackage.group_public
|
|
||||||
for (participant_index, _) in nonces.clone() {
|
|
||||||
let key_package = key_packages.get(participant_index as usize).unwrap();
|
|
||||||
|
|
||||||
assert!(key_package
|
|
||||||
.group_public
|
|
||||||
.verify(message, &group_signature)
|
|
||||||
.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,5 +15,8 @@ released. The APIs and types in `frost-core` are subject to change.
|
||||||
implementations for different ciphersuites / curves without having to implement all of FROST from
|
implementations for different ciphersuites / curves without having to implement all of FROST from
|
||||||
scratch. End-users should not use `frost-core` if they want to sign and verify signatures, they
|
scratch. End-users should not use `frost-core` if they want to sign and verify signatures, they
|
||||||
should use the crate specific to their ciphersuite/curve parameters that uses `frost-core` as a
|
should use the crate specific to their ciphersuite/curve parameters that uses `frost-core` as a
|
||||||
dependency, such as `frost-ristretto255`.
|
dependency, such as [`frost_ristretto255`](../frost_ristretto255).
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
See ciphersuite-specific crates, e.g. [`frost_ristretto255`](../frost_ristretto255).
|
||||||
|
|
|
@ -92,7 +92,7 @@ fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
|
||||||
// This is what the signature aggregator / coordinator needs to do:
|
// This is what the signature aggregator / coordinator needs to do:
|
||||||
// - decide what message to sign
|
// - decide what message to sign
|
||||||
// - take one (unused) commitment per signing participant
|
// - take one (unused) commitment per signing participant
|
||||||
let mut signature_shares: Vec<frost::round2::SignatureShare<C>> = Vec::new();
|
let mut signature_shares = Vec::new();
|
||||||
let message = "message to sign".as_bytes();
|
let message = "message to sign".as_bytes();
|
||||||
let comms = commitments.clone().into_values().collect();
|
let comms = commitments.clone().into_values().collect();
|
||||||
let signing_package = frost::SigningPackage::new(comms, message.to_vec());
|
let signing_package = frost::SigningPackage::new(comms, message.to_vec());
|
||||||
|
|
|
@ -1,32 +1,98 @@
|
||||||
An implementation of Schnorr signatures on the Ristretto group for both single and threshold numbers
|
An implementation of Schnorr signatures on the Ed25519 curve for both single and threshold numbers
|
||||||
of signers (FROST).
|
of signers (FROST).
|
||||||
|
|
||||||
## Examples
|
## 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.
|
||||||
|
|
||||||
Creating a `Signature` with a single signer, serializing and deserializing it, and verifying the
|
|
||||||
signature:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use frost_ed25519 as frost;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use frost_ed25519::*;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
let msg = b"Hello!";
|
let mut rng = thread_rng();
|
||||||
|
let max_signers = 5;
|
||||||
|
let min_signers = 3;
|
||||||
|
let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||||
|
|
||||||
// Generate a secret key and sign the message
|
// Verifies the secret shares from the dealer and store them in a HashMap.
|
||||||
let sk = SigningKey::new(thread_rng());
|
// In practice, the KeyPackages must be sent to its respective participants
|
||||||
let sig = sk.sign(thread_rng(), msg);
|
// through a confidential and authenticated channel.
|
||||||
|
let key_packages: HashMap<_, _> = shares
|
||||||
|
.into_iter()
|
||||||
|
.map(|share| Ok((share.identifier, frost::keys::KeyPackage::try_from(share)?)))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
// Types can be converted to raw byte arrays using `from_bytes`/`to_bytes`
|
let mut nonces = HashMap::new();
|
||||||
let sig_bytes = sig.to_bytes();
|
let mut commitments = HashMap::new();
|
||||||
let pk_bytes = VerifyingKey::from(&sk).to_bytes();
|
|
||||||
|
|
||||||
// Deserialize and verify the signature.
|
////////////////////////////////////////////////////////////////////////////
|
||||||
let sig = Signature::from_bytes(sig_bytes)?;
|
// Round 1: generating nonces and signing commitments for each participant
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
assert!(
|
// In practice, each iteration of this loop will be executed by its respective participant.
|
||||||
VerifyingKey::from_bytes(pk_bytes)
|
for participant_index in 1..(min_signers as u16 + 1) {
|
||||||
.and_then(|pk| pk.verify(msg, &sig))
|
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||||
.is_ok()
|
// Generate one (1) nonce and one SigningCommitments instance for each
|
||||||
);
|
// participant, up to _threshold_.
|
||||||
# Ok::<(), Error>(())
|
let (nonce, commitment) = frost::round1::commit(
|
||||||
|
participant_identifier,
|
||||||
|
key_packages[&participant_identifier].secret_share(),
|
||||||
|
&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(participant_identifier, nonce);
|
||||||
|
commitments.insert(participant_identifier, 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 signature_shares = Vec::new();
|
||||||
|
let message = "message to sign".as_bytes();
|
||||||
|
let comms = commitments.clone().into_values().collect();
|
||||||
|
// 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 signing_package = frost::SigningPackage::new(comms, message.to_vec());
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Round 2: each participant generates their signature share
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// In practice, each iteration of this loop will be executed by its respective participant.
|
||||||
|
for participant_identifier in nonces.keys() {
|
||||||
|
let key_package = &key_packages[participant_identifier];
|
||||||
|
|
||||||
|
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 group_signature = frost::aggregate(&signing_package, &signature_shares[..], &pubkeys)?;
|
||||||
|
|
||||||
|
// Check that the threshold signature can be verified by the group public
|
||||||
|
// key (the verification key).
|
||||||
|
assert!(pubkeys
|
||||||
|
.group_public
|
||||||
|
.verify(message, &group_signature)
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
|
# Ok::<(), frost::Error>(())
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,32 +1,98 @@
|
||||||
An implementation of Schnorr signatures on the P-256 curve for both single and threshold numbers
|
An implementation of Schnorr signatures on the P-256 curve for both single and threshold numbers
|
||||||
of signers (FROST).
|
of signers (FROST).
|
||||||
|
|
||||||
## Examples
|
## 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.
|
||||||
|
|
||||||
Creating a `Signature` with a single signer, serializing and deserializing it, and verifying the
|
|
||||||
signature:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use frost_p256 as frost;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use frost_p256::*;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
let msg = b"Hello!";
|
let mut rng = thread_rng();
|
||||||
|
let max_signers = 5;
|
||||||
|
let min_signers = 3;
|
||||||
|
let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||||
|
|
||||||
// Generate a secret key and sign the message
|
// Verifies the secret shares from the dealer and store them in a HashMap.
|
||||||
let sk = SigningKey::new(thread_rng());
|
// In practice, the KeyPackages must be sent to its respective participants
|
||||||
let sig = sk.sign(thread_rng(), msg);
|
// through a confidential and authenticated channel.
|
||||||
|
let key_packages: HashMap<_, _> = shares
|
||||||
|
.into_iter()
|
||||||
|
.map(|share| Ok((share.identifier, frost::keys::KeyPackage::try_from(share)?)))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
// Types can be converted to raw byte arrays using `from_bytes`/`to_bytes`
|
let mut nonces = HashMap::new();
|
||||||
let sig_bytes = sig.to_bytes();
|
let mut commitments = HashMap::new();
|
||||||
let pk_bytes = VerifyingKey::from(&sk).to_bytes();
|
|
||||||
|
|
||||||
// Deserialize and verify the signature.
|
////////////////////////////////////////////////////////////////////////////
|
||||||
let sig = Signature::from_bytes(sig_bytes)?;
|
// Round 1: generating nonces and signing commitments for each participant
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
assert!(
|
// In practice, each iteration of this loop will be executed by its respective participant.
|
||||||
VerifyingKey::from_bytes(pk_bytes)
|
for participant_index in 1..(min_signers as u16 + 1) {
|
||||||
.and_then(|pk| pk.verify(msg, &sig))
|
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||||
.is_ok()
|
// Generate one (1) nonce and one SigningCommitments instance for each
|
||||||
);
|
// participant, up to _threshold_.
|
||||||
# Ok::<(), Error>(())
|
let (nonce, commitment) = frost::round1::commit(
|
||||||
|
participant_identifier,
|
||||||
|
key_packages[&participant_identifier].secret_share(),
|
||||||
|
&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(participant_identifier, nonce);
|
||||||
|
commitments.insert(participant_identifier, 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 signature_shares = Vec::new();
|
||||||
|
let message = "message to sign".as_bytes();
|
||||||
|
let comms = commitments.clone().into_values().collect();
|
||||||
|
// 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 signing_package = frost::SigningPackage::new(comms, message.to_vec());
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Round 2: each participant generates their signature share
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// In practice, each iteration of this loop will be executed by its respective participant.
|
||||||
|
for participant_identifier in nonces.keys() {
|
||||||
|
let key_package = &key_packages[participant_identifier];
|
||||||
|
|
||||||
|
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 group_signature = frost::aggregate(&signing_package, &signature_shares[..], &pubkeys)?;
|
||||||
|
|
||||||
|
// Check that the threshold signature can be verified by the group public
|
||||||
|
// key (the verification key).
|
||||||
|
assert!(pubkeys
|
||||||
|
.group_public
|
||||||
|
.verify(message, &group_signature)
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
|
# Ok::<(), frost::Error>(())
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,32 +1,98 @@
|
||||||
An implementation of Schnorr signatures on the Ristretto group for both single and threshold numbers
|
An implementation of Schnorr signatures on the Ristretto group for both single and threshold numbers
|
||||||
of signers (FROST).
|
of signers (FROST).
|
||||||
|
|
||||||
## Examples
|
## 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.
|
||||||
|
|
||||||
Creating a `Signature` with a single signer, serializing and deserializing it, and verifying the
|
|
||||||
signature:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use frost_ristretto255 as frost;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use frost_ristretto255::*;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
let msg = b"Hello!";
|
let mut rng = thread_rng();
|
||||||
|
let max_signers = 5;
|
||||||
|
let min_signers = 3;
|
||||||
|
let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)?;
|
||||||
|
|
||||||
// Generate a secret key and sign the message
|
// Verifies the secret shares from the dealer and store them in a HashMap.
|
||||||
let sk = SigningKey::new(thread_rng());
|
// In practice, the KeyPackages must be sent to its respective participants
|
||||||
let sig = sk.sign(thread_rng(), msg);
|
// through a confidential and authenticated channel.
|
||||||
|
let key_packages: HashMap<_, _> = shares
|
||||||
|
.into_iter()
|
||||||
|
.map(|share| Ok((share.identifier, frost::keys::KeyPackage::try_from(share)?)))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
// Types can be converted to raw byte arrays using `from_bytes`/`to_bytes`
|
let mut nonces = HashMap::new();
|
||||||
let sig_bytes = sig.to_bytes();
|
let mut commitments = HashMap::new();
|
||||||
let pk_bytes = VerifyingKey::from(&sk).to_bytes();
|
|
||||||
|
|
||||||
// Deserialize and verify the signature.
|
////////////////////////////////////////////////////////////////////////////
|
||||||
let sig = Signature::from_bytes(sig_bytes)?;
|
// Round 1: generating nonces and signing commitments for each participant
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
assert!(
|
// In practice, each iteration of this loop will be executed by its respective participant.
|
||||||
VerifyingKey::from_bytes(pk_bytes)
|
for participant_index in 1..(min_signers as u16 + 1) {
|
||||||
.and_then(|pk| pk.verify(msg, &sig))
|
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||||
.is_ok()
|
// Generate one (1) nonce and one SigningCommitments instance for each
|
||||||
);
|
// participant, up to _threshold_.
|
||||||
# Ok::<(), Error>(())
|
let (nonce, commitment) = frost::round1::commit(
|
||||||
|
participant_identifier,
|
||||||
|
key_packages[&participant_identifier].secret_share(),
|
||||||
|
&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(participant_identifier, nonce);
|
||||||
|
commitments.insert(participant_identifier, 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 signature_shares = Vec::new();
|
||||||
|
let message = "message to sign".as_bytes();
|
||||||
|
let comms = commitments.clone().into_values().collect();
|
||||||
|
// 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 signing_package = frost::SigningPackage::new(comms, message.to_vec());
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Round 2: each participant generates their signature share
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// In practice, each iteration of this loop will be executed by its respective participant.
|
||||||
|
for participant_identifier in nonces.keys() {
|
||||||
|
let key_package = &key_packages[participant_identifier];
|
||||||
|
|
||||||
|
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 group_signature = frost::aggregate(&signing_package, &signature_shares[..], &pubkeys)?;
|
||||||
|
|
||||||
|
// Check that the threshold signature can be verified by the group public
|
||||||
|
// key (the verification key).
|
||||||
|
assert!(pubkeys
|
||||||
|
.group_public
|
||||||
|
.verify(message, &group_signature)
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
|
# Ok::<(), frost::Error>(())
|
||||||
```
|
```
|
||||||
|
|
|
@ -108,6 +108,23 @@ fn write_docs(
|
||||||
fs::write(filename, code).unwrap();
|
fs::write(filename, code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copy a file into a new one, replacing the strings in `original_strings`
|
||||||
|
/// by the respective one in `replacement_strings` in the process.
|
||||||
|
fn copy_and_replace(
|
||||||
|
origin_filename: &str,
|
||||||
|
destination_filename: &str,
|
||||||
|
original_strings: &[&str],
|
||||||
|
replacement_strings: &[&str],
|
||||||
|
) {
|
||||||
|
let mut text = fs::read_to_string(origin_filename).unwrap();
|
||||||
|
|
||||||
|
for (from, to) in std::iter::zip(original_strings, replacement_strings) {
|
||||||
|
text = text.replace(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::write(destination_filename, text).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let docs = read_docs(
|
let docs = read_docs(
|
||||||
"frost-ristretto255/src/lib.rs",
|
"frost-ristretto255/src/lib.rs",
|
||||||
|
@ -115,6 +132,9 @@ fn main() {
|
||||||
);
|
);
|
||||||
let old_suite_names_doc = &["FROST(ristretto255, SHA-512)"];
|
let old_suite_names_doc = &["FROST(ristretto255, SHA-512)"];
|
||||||
|
|
||||||
|
let readme_filename = "frost-ristretto255/README.md";
|
||||||
|
let original_strings = &["frost_ristretto255", "Ristretto group"];
|
||||||
|
|
||||||
// To add a new ciphersuite, just copy this call and replace the required strings.
|
// To add a new ciphersuite, just copy this call and replace the required strings.
|
||||||
|
|
||||||
write_docs(
|
write_docs(
|
||||||
|
@ -124,6 +144,12 @@ fn main() {
|
||||||
old_suite_names_doc,
|
old_suite_names_doc,
|
||||||
&["FROST(P-256, SHA-256)"],
|
&["FROST(P-256, SHA-256)"],
|
||||||
);
|
);
|
||||||
|
copy_and_replace(
|
||||||
|
readme_filename,
|
||||||
|
"frost-p256/README.md",
|
||||||
|
original_strings,
|
||||||
|
&["frost_p256", "P-256 curve"],
|
||||||
|
);
|
||||||
|
|
||||||
write_docs(
|
write_docs(
|
||||||
&docs,
|
&docs,
|
||||||
|
@ -132,4 +158,10 @@ fn main() {
|
||||||
old_suite_names_doc,
|
old_suite_names_doc,
|
||||||
&["FROST(Ed25519, SHA-512)"],
|
&["FROST(Ed25519, SHA-512)"],
|
||||||
);
|
);
|
||||||
|
copy_and_replace(
|
||||||
|
readme_filename,
|
||||||
|
"frost-ed25519/README.md",
|
||||||
|
original_strings,
|
||||||
|
&["frost_ed25519", "Ed25519 curve"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue