diff --git a/Cargo.lock b/Cargo.lock index e3161adc..17584f8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4541,7 +4541,7 @@ dependencies = [ [[package]] name = "modular-frost" -version = "0.2.2" +version = "0.2.3" dependencies = [ "dalek-ff-group", "dleq", diff --git a/crypto/frost/Cargo.toml b/crypto/frost/Cargo.toml index 5e52e288..0d816030 100644 --- a/crypto/frost/Cargo.toml +++ b/crypto/frost/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "modular-frost" -version = "0.2.2" +version = "0.2.3" description = "Modular implementation of FROST over ff/group" license = "MIT" repository = "https://github.com/serai-dex/serai" diff --git a/crypto/frost/README.md b/crypto/frost/README.md index fbc8bd41..3032ea8c 100644 --- a/crypto/frost/README.md +++ b/crypto/frost/README.md @@ -10,4 +10,4 @@ integrating with existing systems. This library offers ciphersuites compatible with the [IETF draft](https://github.com/cfrg/draft-irtf-cfrg-frost). Currently, version -8 is supported. +10 is supported. diff --git a/crypto/frost/src/algorithm.rs b/crypto/frost/src/algorithm.rs index 4ba64d8c..28adaa1d 100644 --- a/crypto/frost/src/algorithm.rs +++ b/crypto/frost/src/algorithm.rs @@ -8,8 +8,10 @@ use transcript::Transcript; use crate::{Curve, FrostError, FrostView, schnorr}; pub use schnorr::SchnorrSignature; -/// Algorithm to use FROST with. +/// Algorithm trait usable by the FROST signing machine to produce signatures.. pub trait Algorithm: Clone { + /// The transcript format this algorithm uses. This likely should NOT be the IETF-compatible + /// transcript included in this crate. type Transcript: Transcript + Clone + Debug; /// The resulting type of the signatures this algorithm will produce. type Signature: Clone + PartialEq + Debug; @@ -58,7 +60,8 @@ pub trait Algorithm: Clone { fn verify_share(&self, verification_share: C::G, nonces: &[Vec], share: C::F) -> bool; } -// Transcript which will create an IETF compliant serialization for the binding factor +/// IETF-compliant transcript. This is incredibly naive and should not be used within larger +/// protocols. #[derive(Clone, Debug)] pub struct IetfTranscript(Vec); impl Transcript for IetfTranscript { @@ -83,13 +86,15 @@ impl Transcript for IetfTranscript { } } +/// HRAm usable by the included Schnorr signature algorithm to generate challenges. pub trait Hram: Clone { - /// HRAM function to generate a challenge. + /// HRAm function to generate a challenge. /// H2 from the IETF draft, despite having a different argument set (not being pre-formatted). #[allow(non_snake_case)] fn hram(R: &C::G, A: &C::G, m: &[u8]) -> C::F; } +/// IETF-compliant Schnorr signature algorithm ((R, s) where s = r + cx). #[derive(Clone)] pub struct Schnorr> { transcript: IetfTranscript, @@ -109,7 +114,6 @@ impl> Schnorr { } } -/// Implementation of Schnorr signatures for use with FROST. impl> Algorithm for Schnorr { type Transcript = IetfTranscript; type Signature = SchnorrSignature; diff --git a/crypto/frost/src/curve/dalek.rs b/crypto/frost/src/curve/dalek.rs index 97460d0d..f6b94b77 100644 --- a/crypto/frost/src/curve/dalek.rs +++ b/crypto/frost/src/curve/dalek.rs @@ -74,7 +74,7 @@ dalek_curve!( IetfRistrettoHram, RistrettoPoint, b"ristretto", - b"FROST-RISTRETTO255-SHA512-v8", + b"FROST-RISTRETTO255-SHA512-v10", b"chal", ); @@ -85,6 +85,6 @@ dalek_curve!( IetfEd25519Hram, EdwardsPoint, b"edwards25519", - b"FROST-ED25519-SHA512-v8", + b"FROST-ED25519-SHA512-v10", b"", ); diff --git a/crypto/frost/src/curve/ed448.rs b/crypto/frost/src/curve/ed448.rs index 954464d9..373c24a3 100644 --- a/crypto/frost/src/curve/ed448.rs +++ b/crypto/frost/src/curve/ed448.rs @@ -7,7 +7,7 @@ use minimal_ed448::{scalar::Scalar, point::Point}; use crate::{curve::Curve, algorithm::Hram}; -const CONTEXT: &[u8] = b"FROST-ED448-SHAKE256-v8"; +const CONTEXT: &[u8] = b"FROST-ED448-SHAKE256-v10"; #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] pub struct Ed448; diff --git a/crypto/frost/src/curve/kp256.rs b/crypto/frost/src/curve/kp256.rs index cefa6b94..b9d1a820 100644 --- a/crypto/frost/src/curve/kp256.rs +++ b/crypto/frost/src/curve/kp256.rs @@ -92,7 +92,7 @@ macro_rules! kp_curve { } #[cfg(feature = "p256")] -kp_curve!("p256", p256, P256, IetfP256Hram, b"P-256", b"FROST-P256-SHA256-v8"); +kp_curve!("p256", p256, P256, IetfP256Hram, b"P-256", b"FROST-P256-SHA256-v10"); #[cfg(feature = "secp256k1")] kp_curve!( @@ -101,5 +101,5 @@ kp_curve!( Secp256k1, IetfSecp256k1Hram, b"secp256k1", - b"FROST-secp256k1-SHA256-v8" + b"FROST-secp256k1-SHA256-v10" ); diff --git a/crypto/frost/src/key_gen.rs b/crypto/frost/src/key_gen.rs index 74f1cffe..2cbb00f9 100644 --- a/crypto/frost/src/key_gen.rs +++ b/crypto/frost/src/key_gen.rs @@ -249,12 +249,14 @@ fn complete_r2( Ok(FrostCore { params, secret_share, group_key: stripes[0], verification_shares }) } +/// State machine to begin the key generation protocol. pub struct KeyGenMachine { params: FrostParams, context: String, _curve: PhantomData, } +/// Advancement of the key generation state machine. #[derive(Zeroize)] pub struct SecretShareMachine { #[zeroize(skip)] @@ -272,6 +274,7 @@ impl Drop for SecretShareMachine { } impl ZeroizeOnDrop for SecretShareMachine {} +/// Final step of the key generation protocol. #[derive(Zeroize)] pub struct KeyMachine { #[zeroize(skip)] diff --git a/crypto/frost/src/lib.rs b/crypto/frost/src/lib.rs index 2d62de2c..636f8315 100644 --- a/crypto/frost/src/lib.rs +++ b/crypto/frost/src/lib.rs @@ -11,7 +11,7 @@ //! //! This library offers ciphersuites compatible with the //! [IETF draft](https://github.com/cfrg/draft-irtf-cfrg-frost). Currently, version -//! 8 is supported. +//! 10 is supported. use core::fmt::{self, Debug}; use std::{io::Read, sync::Arc, collections::HashMap}; @@ -111,6 +111,7 @@ impl FrostParams { } } +/// Various errors possible during key generation/signing. #[derive(Copy, Clone, Error, Debug)] pub enum FrostError { #[error("a parameter was 0 (required {0}, participants {1})")] diff --git a/crypto/frost/src/promote.rs b/crypto/frost/src/promote.rs index 79bf5000..c879f056 100644 --- a/crypto/frost/src/promote.rs +++ b/crypto/frost/src/promote.rs @@ -60,6 +60,7 @@ fn transcript(key: G, i: u16) -> RecommendedTranscript { transcript } +/// Proof of valid promotion to another generator. #[derive(Clone, Copy)] pub struct GeneratorProof { share: C::G, @@ -80,18 +81,21 @@ impl GeneratorProof { } } +/// Promote a set of keys from one curve to another, where the elliptic curve is the same. +/// Since the Curve trait additionally specifies a generator, this provides an O(n) way to update +/// the generator used with keys. The key generation protocol itself is exponential. pub struct GeneratorPromotion { base: FrostKeys, proof: GeneratorProof, _c2: PhantomData, } -/// Promote a set of keys from one generator to another. -// The linear DLEq proofs are much more efficient than an exponential key gen impl GeneratorPromotion where C2: Curve, { + /// Begin promoting keys from one curve to another. Returns a proof this share was properly + /// promoted. pub fn promote( rng: &mut R, base: FrostKeys, @@ -110,6 +114,7 @@ where (GeneratorPromotion { base, proof, _c2: PhantomData:: }, proof) } + /// Complete promotion by taking in the proofs from all other participants. pub fn complete( self, proofs: &HashMap>, diff --git a/crypto/frost/src/schnorr.rs b/crypto/frost/src/schnorr.rs index b47b4370..da229145 100644 --- a/crypto/frost/src/schnorr.rs +++ b/crypto/frost/src/schnorr.rs @@ -11,6 +11,7 @@ use multiexp::BatchVerifier; use crate::Curve; +/// A Schnorr signature of the form (R, s) where s = r + cx. #[allow(non_snake_case)] #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct SchnorrSignature { diff --git a/crypto/frost/src/sign.rs b/crypto/frost/src/sign.rs index 229c49e4..e0efc822 100644 --- a/crypto/frost/src/sign.rs +++ b/crypto/frost/src/sign.rs @@ -191,7 +191,10 @@ fn sign_with_share>( // Parse the commitments for l in ¶ms.view.included { { - params.algorithm.transcript().append_message(b"participant", &l.to_be_bytes()); + params + .algorithm + .transcript() + .append_message(b"participant", C::F::from(u64::from(*l)).to_repr().as_ref()); } // While this doesn't note which nonce/basepoint this is for, those are expected to be @@ -274,7 +277,7 @@ fn sign_with_share>( // Generate the per-signer binding factors for (l, commitments) in B.iter_mut() { let mut rho_transcript = rho_transcript.clone(); - rho_transcript.append_message(b"participant", &l.to_be_bytes()); + rho_transcript.append_message(b"participant", C::F::from(u64::from(*l)).to_repr().as_ref()); commitments.1 = C::hash_binding_factor(rho_transcript.challenge(b"rho").as_ref()); } @@ -365,6 +368,7 @@ fn complete>( Err(FrostError::InternalError("everyone had a valid share yet the signature was still invalid")) } +/// Trait for the initial state machine of a two-round signing protocol. pub trait PreprocessMachine { type Signature: Clone + PartialEq + fmt::Debug; type SignMachine: SignMachine; @@ -374,6 +378,7 @@ pub trait PreprocessMachine { fn preprocess(self, rng: &mut R) -> (Self::SignMachine, Vec); } +/// Trait for the second machine of a two-round signing protocol. pub trait SignMachine { type SignatureMachine: SignatureMachine; @@ -387,6 +392,7 @@ pub trait SignMachine { ) -> Result<(Self::SignatureMachine, Vec), FrostError>; } +/// Trait for the final machine of a two-round signing protocol. pub trait SignatureMachine { /// Complete signing. /// Takes in everyone elses' shares. Returns the signature. @@ -398,11 +404,13 @@ pub struct AlgorithmMachine> { params: Params, } +/// Next step of the state machine for the signing process. pub struct AlgorithmSignMachine> { params: Params, preprocess: PreprocessPackage, } +/// Final step of the state machine for the signing process. pub struct AlgorithmSignatureMachine> { params: Params, sign: Package, diff --git a/crypto/frost/src/tests/literal/dalek.rs b/crypto/frost/src/tests/literal/dalek.rs index 524b0f6b..b35e54e4 100644 --- a/crypto/frost/src/tests/literal/dalek.rs +++ b/crypto/frost/src/tests/literal/dalek.rs @@ -24,20 +24,20 @@ fn ristretto_vectors() { included: &[1, 3], nonces: &[ [ - "1eaee906e0554a5e533415e971eefa909f3c614c7c75e27f381b0270a9afe308", - "16175fc2e7545baf7180e8f5b6e1e73c4f2769323cc76754bdd79fe93ab0bd0b", + "de3e8f526dcb51a1b9b48cc284aeca27c385aa3ba1a92a0c8440d51e1a1d2f00", + "fa8dca5ec7a05d5a7b782be847ba3dde1509de1dbcf0569fc980cff795db5404", ], [ - "48d78b8c2de1a515513f9d3fc464a19a72304fac522f17cc647706cb22c21403", - "5c0f10966b3f1386660a87de0fafd69decbe9ffae1a152a88b7d83bb4fb1c908", + "e07061a9ab6735de9a75b0c64f086c5b999894611d0cdc03f85c4e87c8aae602", + "38b17578e8e6ad4077071ce6b0bf9cb85ac35fee7868dcb6d9bfa97f0e153e0e", ], ], sig_shares: &[ - "5ae13621ebeef844e39454eb3478a50c4531d25939e1065f44f5b04a8535090e", - "aa432dcf274a9441c205e76fe43497be99efe374f9853477bd5add2075f6970c", + "a5f046916a6a111672111e47f9825586e1188da8a0f3b7c61f2b6b432c636e07", + "4c175c7e43bd197980c2021774036eb288f54179f079fbf21b7d2f9f52846401", ], - sig: "9c407badb8cacf10f306d94e31fb2a71d6a8398039802b4d80a1278472397206".to_owned() + - "17516e93f8d57a2ecffd43b83ab35db6de20b6ce32673bd601508e6bfa2ba10a", + sig: "94b11def3f919503c3544452ad2a59f198f64cc323bd758bb1c65b42032a7473".to_owned() + + "f107a30fae272b8ff2d3205e6d86c3386a0ecf21916db3b93ba89ae27ee7d208", }, ); } @@ -61,20 +61,20 @@ fn ed25519_vectors() { included: &[1, 3], nonces: &[ [ - "1c406170127e33142b8611bc02bf14d5909e49d5cb87150eff3ec9804212920c", - "5be4edde8b7acd79528721191626810c94fbc2bcc814b7a67d301fbd7fc16e07", + "4e64f59e90a3b9cdce346fae68eb0e459532c8ca1ad59a566c3ee2c67bf0100b", + "470c660895c6db164ee6564120eec71023fa5297f09c663bb8171646c5632d00", ], [ - "795f87122f05b7efc4b1a52f435c3d28597411b1a6fec198ce9c818c5451c401", - "c9193aaef37bc074ea3286d0361c815f7201bf764cd9e7d8bb4eb5ecca840a09", + "6fc516495dbb364b807cdd0c2e5e3f58aa4914a53fed33cc340033979bb07304", + "0837e770a88147d41ff39138ca23b35d6cf303a4f148294755ede4b7e760d701", ], ], sig_shares: &[ - "1f16a3989b4aa2cc3782a503331b9a21d7ba56c9c5455d06981b5425306c9d01", - "4c8f33c301c05871b434a686847d5818417a01e50a59e9e7fddaefde7d244207", + "3f2eb12735e5b39da97e884a6caadf6bb83f1efcec709d6f66333d0d67ebe707", + "79e572b8632fbb928519dd2eff793de8784a56d582ae48c807d39b0dc5b93509", ], - sig: "1aff2259ecb59cfcbb36ae77e02a9b134422abeae47cf7ff56c85fdf90932b18".to_owned() + - "6ba5d65b9d0afb3decb64b8ab798f239183558aed09e46ee95f64304ae90df08", + sig: "e31e69a4e10d5ca2307c4a0d12cd86e3fceee550e55cb5b3f47c7ad6dbb38884".to_owned() + + "cb3f2e837eb15cd858fb6dd68c2a3e3f318a74d16f1fe6376e06d91a2ca51d01", }, ); } diff --git a/crypto/frost/src/tests/literal/ed448.rs b/crypto/frost/src/tests/literal/ed448.rs index 218bf269..82e762b6 100644 --- a/crypto/frost/src/tests/literal/ed448.rs +++ b/crypto/frost/src/tests/literal/ed448.rs @@ -86,47 +86,47 @@ fn ed448_non_ietf() { nonces: &[ [ concat!( - "f770bcb22f3c0acac7f09d3b757f13f31b53489776ede2cff944b4c0", - "cd28bb7dfbd33809e87201e152beeb552292eb748efa5267fa2dcd20", + "06f2e15b05d29a50f0686a890259f4dcf66147a80809ed9e50926f5f", + "173fe23a0627561efa003724dc270effc47a30bc4d80aba30725401d", "00" ), concat!( - "d3196c7f14b1a99f1715053c00fa3a30b0fe9cbeb461068c262b1714", - "78458a15598cc1c33cd415a766577996a6efcc520c411abf0280c816", + "e0482e611c34f191d1c13a09bc8bbf4bda68db4de32aa7908849b02b", + "a912cfba46c805e2d8560ab9437e343e1dde6b481a2bae527e111b2c", "00" ), ], [ concat!( - "9172a7cea56b7f564ed93116adf078ee013e4160e2687489ea580bc6", - "f034f10e58db0b0cdf98bf1d3c85b2eb1f30b8b6df57b3611d205d2e", + "295c56447c070157e6bc3c83ed2afca194569e07d0ad27d28a40dec2", + "c4107c07d507db20da1be62ea6976b8e53ab5d26e225c663f2e71511", "00" ), concat!( - "3b60b4dc036b21441620a36c84b0ec780267a9275b411a495b182dc6", - "bfc812d1a21d93142d375b7ed80314d1693b61c1f42e20c575a4530e", + "b97303a6c5ab12b6ad310834361033a19d99dfdf93109da721da35c3", + "abbc5f29df33b3402692bef9f005bb8ea00af5ba20cc688360fd8831", "00" ), ], ], sig_shares: &[ concat!( - "95aeb18a46bac9e239d8eb51a7168da25a000d8a6938e26446c36e5d", - "b88eff9523e0b09934558ddc8b2679bf2f10ed66415df1eb6e38a507", + "5b65641e27007ec71509c6af5cf8527eb01fee5b2b07d8beecf6646e", + "b7e7e27d85119b74f895b56ba7561834a1b0c42639b122160a0b6208", "00" ), concat!( - "521672ae547cd95b94a9be55b72a0dfb6938715230304d39017f5a54", - "f1333a96da50a0759eea78bdb6b670c8243dbe706cd388763fe4c50b", + "821b7ac04d7c01d970b0b3ba4ae8f737a5bac934aed1600b1cad7601", + "1c240629bce6a4671a1b6f572cec708ec161a72a5ca04e50eabdfc25", "00" ), ], sig: concat!( - "f1c2605fc0b724696dff10d2df0ac28939f40dc3d9ba864605462355", - "c139229de643a6580e5807994cfcab0796644571c501cab00e85056a", + "c7ad7ad9fcfeef9d1492361ba641400bd3a3c8335a83cdffbdd8867d", + "2849bb4419dcc3e594baa731081a1a00cd3dea9219a81ecba4646e95", "00", - "e7c423399b36a33ece81aaa75e419a9dc4387edc99682f9e4742c9b1", - "a9c2392cfe30510fd33f069a42dde987544dabd7ad307a62ae1c6b13", + "dd80dede747c7fa086b9796aa7e04ab655dab790d9d838ca08a4db6f", + "d30be9a641f83fdc12b124c3d34289c262126c5195517166f4c85e2e", "00" ) .to_string(), diff --git a/crypto/frost/src/tests/literal/kp256.rs b/crypto/frost/src/tests/literal/kp256.rs index 64c52c2a..b0b581d2 100644 --- a/crypto/frost/src/tests/literal/kp256.rs +++ b/crypto/frost/src/tests/literal/kp256.rs @@ -27,20 +27,20 @@ fn secp256k1_vectors() { included: &[1, 3], nonces: &[ [ - "95f352cf568508bce96ef3cb816bf9229eb521ca9c2aff6a4fe8b86bf49ae16f", - "c675aea50ff2510ae6b0fcb55432b97ad0b55a28b959bacb0e8b466dbf43dd26", + "36d5c4185c40b02b5e4673e2531a10e6ff9883840a68ec08dbeb896467e21355", + "7b3f573ca0a28f9f94522be4748df0ed04de8a83085aff4be7b01aa53fb6ac1b", ], [ - "b5089ebf363630d3477711005173c1419f4f40514f7287b4ca6ff110967a2d70", - "5e50ce9975cfc6164e85752f52094b11091fdbca846a9c245fdbfa4bab1ae28c", + "ba4f8b8e587b2c9fc61a6156885f0bc67654b5e068c9e7749f75c09a98f17c13", + "316de06639051ac7869e5ac4458eda1fef90ce93fa3c490556c4192e4fa550d0", ], ], sig_shares: &[ - "280c44c6c37cd64c7f5a552ae8416a57d21c115cab524dbff5fbcebbf5c0019d", - "e372bca35133a80ca140dcac2125c966b763a934678f40e09fb8b0ae9d4aee1b", + "f9ee00d5ac0c746b751dde99f71d86f8f0300a81bd0336ca6649ef597239e13f", + "61048ca334ac6a6cb59d6b3ea2b25b7098e204adc09e2f88b024531b081d1d6f", ], - sig: "0364b02292a4b0e61f849f4d6fac0e67c2f698a21e1cba9e4a5b8fa535f2f9310d".to_owned() + - "0b7f016a14b07e59209b31d7096733bfced0ddaa6398ee64d5e220ddc2d4ae77", + sig: "023cf76388f92d403aa937af2e3cb3e7a2350e40400c16a282e330af2c60eeb85a".to_owned() + + "5af28d78e0b8ded82abb49d899cfe26ace633248ce58c617569be3e7aa20bd6d", }, ); } @@ -64,20 +64,20 @@ fn p256_vectors() { included: &[1, 3], nonces: &[ [ - "e9165dad654fc20a9e31ca6f32ac032ec327b551a50e8ac5cf25f5c4c9e20757", - "e9059a232598a0fba0e495a687580e624ab425337c3221246fb2c716905bc9e7", + "9aa66350b0f72b27ce4668323b4280cd49709177ed8373977c22a75546c9995d", + "bd8b05d7fd0ff5a5ed65b1f105478f7718a981741fa8fa9b55ac6d3c8fc59a05", ], [ - "b9d136e29eb758bd77cb83c317ac4e336cf8cda830c089deddf6d5ec81da9884", - "5261e2d00ce227e67bb9b38990294e2c82970f335b2e6d9f1d07a72ba43d01f0", + "4c1aec8e84c496b80af98415fada2e6a4b1f902d4bc6c9682699b8aeffd97419", + "eeaf5ef7af01e55050fb8acafc9c9306ef1cc13214677ba33e7bc51e8677e892", ], ], sig_shares: &[ - "bdaa275f10ca57e3a3a9a7a0d95aeabb517897d8482873a8f9713d458f94756f", - "0e8fd85386939e8974a8748e66641df0fe043323c52487a2b10b8a397897de21", + "ec5b8ab47d55903698492a07bb322ab6e7d3cf32581dcedf43c4fa18b46f3e10", + "c97da3580560e88725a8e393d46fee18ecd2e00148e5e303d4a510fae9c11da5", ], - sig: "03c41521412528dce484c35b6b9b7cc8150102ab3e4bdf858d702270c05098e6c6".to_owned() + - "cc39ffb2975df66d18521c2f3fbf08ac4f7ccafc0d4cfb4baa7cc77f082c5390", + sig: "036b3eba585ff5d40df29893fb6f60572803aef97800cfaaaa5cf0f0f19d8237f7".to_owned() + + "b5d92e0d82b678bcbdf20d9b8fa218d017bfb485f9ec135e24b04050a1cd3664", }, ); } diff --git a/crypto/frost/src/tests/mod.rs b/crypto/frost/src/tests/mod.rs index ce954be8..72f6ccc5 100644 --- a/crypto/frost/src/tests/mod.rs +++ b/crypto/frost/src/tests/mod.rs @@ -24,9 +24,12 @@ pub mod vectors; #[cfg(test)] mod literal; +/// Constant amount of participants to use when testing. pub const PARTICIPANTS: u16 = 5; +/// Constant threshold of participants to use when signing. pub const THRESHOLD: u16 = ((PARTICIPANTS / 3) * 2) + 1; +/// Clone a map without a specific value. pub fn clone_without( map: &HashMap, without: &K, @@ -36,6 +39,7 @@ pub fn clone_without( res } +/// Generate FROST keys (as FrostCore objects) for tests. pub fn core_gen(rng: &mut R) -> HashMap> { let mut machines = HashMap::new(); let mut commitments = HashMap::new(); @@ -91,10 +95,12 @@ pub fn core_gen(rng: &mut R) -> HashMap>() } +/// Generate FROST keys for tests. pub fn key_gen(rng: &mut R) -> HashMap> { core_gen(rng).drain().map(|(i, core)| (i, FrostKeys::new(core))).collect() } +/// Recover the secret from a collection of keys. pub fn recover(keys: &HashMap>) -> C::F { let first = keys.values().next().expect("no keys provided"); assert!(keys.len() >= first.params().t().into(), "not enough keys provided"); @@ -107,6 +113,7 @@ pub fn recover(keys: &HashMap>) -> C::F { group_private } +/// Spawn algorithm machines for a random selection of signers, each executing the given algorithm. pub fn algorithm_machines>( rng: &mut R, algorithm: A, @@ -136,6 +143,7 @@ pub fn algorithm_machines>( .collect() } +/// Execute the signing protocol. pub fn sign( rng: &mut R, mut machines: HashMap,