diff --git a/benches/sign.rs b/benches/sign.rs index bca8bd6..a925aa4 100644 --- a/benches/sign.rs +++ b/benches/sign.rs @@ -158,14 +158,16 @@ fn criterion_benchmark(c: &mut Criterion) { let mut aggregator = SignatureAggregator::new(params, group_key, &message[..]); for i in 1..THRESHOLD_OF_PARTICIPANTS + 1 { - aggregator.include_signer( - i, - participants_public_comshares[(i - 1) as usize].commitments[0], - &participants_secret_keys[(i - 1) as usize].to_public(), - ); + aggregator + .include_signer( + i, + participants_public_comshares[(i - 1) as usize].commitments[0], + &participants_secret_keys[(i - 1) as usize].to_public(), + ) + .unwrap(); } - let signers = aggregator.get_signers().clone(); + let signers = aggregator.signers().to_vec(); let message_hash = Secp256k1Sha256::h4(&message[..]); let message_hash_copy = message_hash; diff --git a/src/error.rs b/src/error.rs index 8f463ae..4e58e86 100644 --- a/src/error.rs +++ b/src/error.rs @@ -27,6 +27,8 @@ pub enum Error { ComplaintVerificationError, /// The index of a participant is zero IndexIsZero, + /// The index of a signer does not match the index in the public key + IndexMismatch(u32, u32), /// GroupVerifyingKey generation failure InvalidGroupKey, /// Invalid NiZK proof of knowledge @@ -45,6 +47,8 @@ pub enum Error { InvalidMSMParameters, /// Too many invalid participants, with their indices TooManyInvalidParticipants(Vec), + /// Too many unique signers given the [`crate::parameters::ThresholdParameters`]. + TooManySigners(usize, u32), /// The participant is missing commitment shares MissingCommitmentShares, /// Invalid binding factor @@ -76,10 +80,22 @@ impl core::fmt::Display for Error { Error::ShareVerificationError => write!(f, "The secret share is not correct."), Error::ComplaintVerificationError => write!(f, "The complaint is not correct."), Error::IndexIsZero => write!(f, "The indexs of a participant cannot be 0."), + Error::IndexMismatch(participant_idx, pubkey_idx) => write!( + f, + "Index mismatch between participant index ({}) and the public key index ({}).", + participant_idx, pubkey_idx + ), Error::InvalidGroupKey => write!( f, "Could not generate a valid group key with the given commitments." ), + Error::TooManySigners(signers, n_param) => { + write!( + f, + "Too many signers ({}) given the DKG instance parameters (total participants set to {}).", + signers, n_param + ) + } Error::InvalidProofOfKnowledge => write!( f, "The NiZK proof of knowledge of the secret key is not correct." diff --git a/src/lib.rs b/src/lib.rs index 9404638..d1b8bf0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1455,7 +1455,7 @@ //! # //! # aggregator.include_signer(1, alice_public_comshares.commitments[0], &alice_public_key); //! # aggregator.include_signer(3, carol_public_comshares.commitments[0], &carol_public_key); -//! let signers = aggregator.get_signers(); +//! let signers = aggregator.signers(); //! # Ok(()) } //! # fn main() { assert!(do_test().is_ok()); } //! ``` @@ -1530,7 +1530,7 @@ //! # aggregator.include_signer(1, alice_public_comshares.commitments[0], &alice_public_key); //! # aggregator.include_signer(3, carol_public_comshares.commitments[0], &carol_public_key); //! # -//! # let signers = aggregator.get_signers(); +//! # let signers = aggregator.signers(); //! # let message_hash = Secp256k1Sha256::h4(&message[..]); //! //! let alice_partial = alice_secret_key.sign( diff --git a/src/sign/signature.rs b/src/sign/signature.rs index 5d2381b..6b104bb 100644 --- a/src/sign/signature.rs +++ b/src/sign/signature.rs @@ -350,7 +350,7 @@ pub(crate) struct AggregatorState { /// The protocol instance parameters. pub(crate) parameters: ThresholdParameters, /// The set of signing participants for this round. - pub(crate) signers: Vec>, + signers: Vec>, /// The signer's public keys for verifying their [`PartialThresholdSignature`]. pub(crate) public_keys: IndividualPublicKeys, /// The partial signatures from individual participants which have been @@ -368,11 +368,26 @@ pub(crate) struct AggregatorState { #[derive(Debug)] pub struct SignatureAggregator { /// The aggregator's actual state, shared across types. - pub(crate) state: Box>, + state: Box>, /// The aggregator's additional state. pub(crate) aggregator: A, } +impl SignatureAggregator +where + C: CipherSuite, + A: Aggregator, +{ + /// Get the list of participating [`Signer`]s. + /// + /// # Returns + /// + /// A reference to the internal list of [`Signer`]s. + pub fn signers(&self) -> &[Signer] { + &self.state.signers + } +} + /// The initial state for a [`SignatureAggregator`], which may include invalid /// or non-sensical data. #[derive(Debug)] @@ -453,43 +468,38 @@ impl SignatureAggregator> { /// [`SignatureAggregator.include_partial_signature`], otherwise the signing /// procedure will fail. /// - /// # Panics - /// - /// If the [`signer.participant_index`] doesn't match the [`public_key.index`] . + /// # Errors if the [`signer.participant_index`] does not match the + /// [`public_key.index`] . pub fn include_signer( &mut self, participant_index: u32, published_commitment_share: (C::G, C::G), public_key: &IndividualVerifyingKey, - ) { - assert_eq!(participant_index, public_key.index, - "Tried to add signer with participant index {}, but public key is for participant with index {}", - participant_index, public_key.index); + ) -> FrostResult { + if participant_index != public_key.index { + return Err(Error::IndexMismatch(participant_index, public_key.index)); + } self.state.signers.push(Signer { participant_index, published_commitment_share, }); - self.state - .public_keys - .insert(public_key.index, public_key.share); - } - /// Get the list of partipating signers. - /// It will internally sort the signers by indices, remove any duplicate, - /// and then return a reference to the updated internal list. - /// - /// # Returns - /// - /// A `&Vec` of the participating signers in this round. - pub fn get_signers(&'_ mut self) -> &'_ Vec> { + // Deduplicate partipating [`Signer`]s and check that there are no + // more signers than participants as set in the [`ThresholdParameters`]. self.state.signers.sort(); self.state.signers.dedup(); + if self.state.signers.len() > self.state.parameters.n as usize { + return Err(Error::TooManySigners( + self.state.signers.len(), + self.state.parameters.n, + )); + } - // Sanity check - assert!(self.state.signers.len() <= self.state.parameters.n as usize); - - &self.state.signers + self.state + .public_keys + .insert(public_key.index, public_key.share); + Ok(()) } /// Helper function to get the remaining signers who were expected to sign, @@ -505,10 +515,10 @@ impl SignatureAggregator> { /// A sorted [`Vec`] of unique [`Signer`]s who have yet to contribute their /// partial signatures. #[must_use] - pub fn get_remaining_signers(&self) -> Vec> { - let mut remaining_signers = Vec::with_capacity(self.state.signers.len()); + pub fn remaining_signers(&self) -> Vec> { + let mut remaining_signers = Vec::with_capacity(self.signers().len()); - for signer in &self.state.signers { + for signer in self.signers() { if self .state .partial_signatures @@ -541,9 +551,9 @@ impl SignatureAggregator> { /// If the [`BTreeMap`] contains a key for [`0`, this indicates that /// the aggregator did not have \(( t' \)) partial signers /// s.t. \(( t \le t' \le n \)). - pub fn finalize(mut self) -> FrostResult>> { + pub fn finalize(self) -> FrostResult>> { let mut misbehaving_participants = Vec::new(); - let remaining_signers = self.get_remaining_signers(); + let remaining_signers = self.remaining_signers(); // We're reporting missing partial signatures which // could possibly be the fault of the aggregator, but here we explicitly @@ -557,10 +567,7 @@ impl SignatureAggregator> { } } - // Ensure that our new state is ordered and deduplicated. - let _ = self.get_signers(); - - for signer in &self.state.signers { + for signer in self.signers() { if self .state .public_keys @@ -594,8 +601,8 @@ impl SignatureAggregator> { /// signers and a description of their misbehaviour. pub fn aggregate(&self) -> FrostResult> { let binding_factor_list = - compute_binding_factors(self.aggregator.message_hash.as_ref(), &self.state.signers)?; - let group_commitment = compute_group_commitment(&self.state.signers, &binding_factor_list)?; + compute_binding_factors(self.aggregator.message_hash.as_ref(), self.signers())?; + let group_commitment = compute_group_commitment(self.signers(), &binding_factor_list)?; let challenge = compute_challenge::( &group_commitment, &self.state.group_key, @@ -613,7 +620,7 @@ impl SignatureAggregator> { // We first combine all partial signatures together, to remove the need for individual // signature verification in case the final group signature is valid. - for signer in &self.state.signers { + for signer in self.signers() { let partial_sig = self .state .partial_signatures @@ -636,7 +643,7 @@ impl SignatureAggregator> { Ok(signature) } else { let mut misbehaving_participants = Vec::new(); - for signer in &self.state.signers { + for signer in self.signers() { // This cannot error, since the attempted division by zero in // the calculation of the Lagrange interpolation cannot happen, // because we use the typestate pattern, @@ -665,7 +672,7 @@ impl SignatureAggregator> { let participant_commitment = commitment_for_participant( signer.participant_index, self.aggregator.message_hash.as_ref(), - &self.state.signers, + self.signers(), )?; if check != participant_commitment + (pk_i.mul(challenge * lambda)) { @@ -731,9 +738,10 @@ mod test { let mut aggregator = SignatureAggregator::new(params, group_key, &message[..]); - aggregator.include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()); + aggregator + .include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); let p1_partial = p1_sk @@ -742,7 +750,7 @@ mod test { &group_key, &mut p1_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); @@ -772,9 +780,10 @@ mod test { let mut aggregator = SignatureAggregator::new(params, group_key, &message[..]); - aggregator.include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()); + aggregator + .include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); let p1_partial = p1_sk @@ -783,7 +792,7 @@ mod test { &group_key, &mut p1_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); @@ -809,9 +818,10 @@ mod test { let mut aggregator = SignatureAggregator::new(params, group_key, &message[..]); - aggregator.include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()); + aggregator + .include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); let p1_partial = p1_sk @@ -820,7 +830,7 @@ mod test { &group_key, &mut p1_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); @@ -852,11 +862,16 @@ mod test { let mut aggregator = SignatureAggregator::new(params, group_key, &message[..]); - aggregator.include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()); - aggregator.include_signer(3, p3_public_comshares.commitments[0], &p3_sk.to_public()); - aggregator.include_signer(4, p4_public_comshares.commitments[0], &p4_sk.to_public()); + aggregator + .include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()) + .unwrap(); + aggregator + .include_signer(3, p3_public_comshares.commitments[0], &p3_sk.to_public()) + .unwrap(); + aggregator + .include_signer(4, p4_public_comshares.commitments[0], &p4_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); let p1_partial = p1_sk @@ -865,7 +880,7 @@ mod test { &group_key, &mut p1_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); let p3_partial = p3_sk @@ -874,7 +889,7 @@ mod test { &group_key, &mut p3_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); let p4_partial = p4_sk @@ -883,7 +898,7 @@ mod test { &group_key, &mut p4_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); @@ -914,10 +929,13 @@ mod test { let mut aggregator = SignatureAggregator::new(params, group_key, &message[..]); - aggregator.include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()); - aggregator.include_signer(2, p2_public_comshares.commitments[0], &p2_sk.to_public()); + aggregator + .include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()) + .unwrap(); + aggregator + .include_signer(2, p2_public_comshares.commitments[0], &p2_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); let p1_partial = p1_sk @@ -926,7 +944,7 @@ mod test { &group_key, &mut p1_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); let p2_partial = p2_sk @@ -935,7 +953,7 @@ mod test { &group_key, &mut p2_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); @@ -972,10 +990,13 @@ mod test { let mut aggregator = SignatureAggregator::new(params, group_key, &message[..]); - aggregator.include_signer(1, d1_public_comshares.commitments[0], &d1_sk.to_public()); - aggregator.include_signer(2, d2_public_comshares.commitments[0], &d2_sk.to_public()); + aggregator + .include_signer(1, d1_public_comshares.commitments[0], &d1_sk.to_public()) + .unwrap(); + aggregator + .include_signer(2, d2_public_comshares.commitments[0], &d2_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); let d1_partial = d1_sk @@ -984,7 +1005,7 @@ mod test { &group_key, &mut d1_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); let d2_partial = d2_sk @@ -993,7 +1014,7 @@ mod test { &group_key, &mut d2_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); @@ -1018,10 +1039,13 @@ mod test { let mut aggregator = SignatureAggregator::new(params, group_key, &message[..]); - aggregator.include_signer(1, s1_public_comshares.commitments[0], &s1_sk.to_public()); - aggregator.include_signer(2, s2_public_comshares.commitments[0], &s2_sk.to_public()); + aggregator + .include_signer(1, s1_public_comshares.commitments[0], &s1_sk.to_public()) + .unwrap(); + aggregator + .include_signer(2, s2_public_comshares.commitments[0], &s2_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); let s1_partial = s1_sk @@ -1030,7 +1054,7 @@ mod test { &group_key, &mut s1_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); let s2_partial = s2_sk @@ -1039,7 +1063,7 @@ mod test { &group_key, &mut s2_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); @@ -1085,10 +1109,13 @@ mod test { let mut aggregator = SignatureAggregator::new(d_params, group_key, &message[..]); - aggregator.include_signer(1, d1_public_comshares.commitments[0], &d1_sk.to_public()); - aggregator.include_signer(2, d2_public_comshares.commitments[0], &d2_sk.to_public()); + aggregator + .include_signer(1, d1_public_comshares.commitments[0], &d1_sk.to_public()) + .unwrap(); + aggregator + .include_signer(2, d2_public_comshares.commitments[0], &d2_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); let d1_partial = d1_sk @@ -1097,7 +1124,7 @@ mod test { &group_key, &mut d1_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); let d2_partial = d2_sk @@ -1106,7 +1133,7 @@ mod test { &group_key, &mut d2_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); @@ -1133,11 +1160,16 @@ mod test { let mut aggregator = SignatureAggregator::new(s_params, group_key, &message[..]); - aggregator.include_signer(1, s1_public_comshares.commitments[0], &s1_sk.to_public()); - aggregator.include_signer(2, s2_public_comshares.commitments[0], &s2_sk.to_public()); - aggregator.include_signer(3, s3_public_comshares.commitments[0], &s3_sk.to_public()); + aggregator + .include_signer(1, s1_public_comshares.commitments[0], &s1_sk.to_public()) + .unwrap(); + aggregator + .include_signer(2, s2_public_comshares.commitments[0], &s2_sk.to_public()) + .unwrap(); + aggregator + .include_signer(3, s3_public_comshares.commitments[0], &s3_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); let s1_partial = s1_sk @@ -1146,7 +1178,7 @@ mod test { &group_key, &mut s1_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); let s2_partial = s2_sk @@ -1155,7 +1187,7 @@ mod test { &group_key, &mut s2_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); let s3_partial = s3_sk @@ -1164,7 +1196,7 @@ mod test { &group_key, &mut s3_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); @@ -1211,11 +1243,17 @@ mod test { &message[..], ); - aggregator.include_signer(2, p2_public_comshares.commitments[0], &p2_sk.to_public()); - aggregator.include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()); - aggregator.include_signer(2, p2_public_comshares.commitments[0], &p2_sk.to_public()); + aggregator + .include_signer(2, p2_public_comshares.commitments[0], &p2_sk.to_public()) + .unwrap(); + aggregator + .include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()) + .unwrap(); + aggregator + .include_signer(2, p2_public_comshares.commitments[0], &p2_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); + let signers = aggregator.signers(); // The signers should be deduplicated. assert!(signers.len() == 2); @@ -1479,23 +1517,28 @@ mod test { let mut aggregator = SignatureAggregator::new(params, group_key, &message[..]); - aggregator.include_signer( - 2, - p2_public_comshares.commitments[0], - &p2_secret_key.to_public(), - ); - aggregator.include_signer( - 3, - p3_public_comshares.commitments[0], - &p3_secret_key.to_public(), - ); - aggregator.include_signer( - 5, - p5_public_comshares.commitments[0], - &p5_secret_key.to_public(), - ); + aggregator + .include_signer( + 2, + p2_public_comshares.commitments[0], + &p2_secret_key.to_public(), + ) + .unwrap(); + aggregator + .include_signer( + 3, + p3_public_comshares.commitments[0], + &p3_secret_key.to_public(), + ) + .unwrap(); + aggregator + .include_signer( + 5, + p5_public_comshares.commitments[0], + &p5_secret_key.to_public(), + ) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); let p2_partial = p2_secret_key @@ -1504,7 +1547,7 @@ mod test { &group_key, &mut p2_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); let p3_partial = p3_secret_key @@ -1513,7 +1556,7 @@ mod test { &group_key, &mut p3_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); let p5_partial = p5_secret_key @@ -1522,7 +1565,7 @@ mod test { &group_key, &mut p5_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); @@ -1554,10 +1597,13 @@ mod test { let mut aggregator = SignatureAggregator::new(params, group_key, &message[..]); - aggregator.include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()); - aggregator.include_signer(2, p2_public_comshares.commitments[0], &p2_sk.to_public()); + aggregator + .include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()) + .unwrap(); + aggregator + .include_signer(2, p2_public_comshares.commitments[0], &p2_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); let p1_partial = p1_sk @@ -1566,7 +1612,7 @@ mod test { &group_key, &mut p1_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); let p2_partial = p2_sk @@ -1575,7 +1621,7 @@ mod test { &group_key, &mut p2_secret_comshares, 0, - signers, + aggregator.signers(), ) .unwrap(); diff --git a/tests/ice_frost.rs b/tests/ice_frost.rs index ca4326a..43bde77 100644 --- a/tests/ice_frost.rs +++ b/tests/ice_frost.rs @@ -233,13 +233,18 @@ fn signing_and_verification_3_out_of_5() { let mut aggregator = SignatureAggregator::new(params, group_key, &message[..]); - aggregator.include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()); - aggregator.include_signer(3, p3_public_comshares.commitments[0], &p3_sk.to_public()); - aggregator.include_signer(4, p4_public_comshares.commitments[0], &p4_sk.to_public()); + aggregator + .include_signer(1, p1_public_comshares.commitments[0], &p1_sk.to_public()) + .unwrap(); + aggregator + .include_signer(3, p3_public_comshares.commitments[0], &p3_sk.to_public()) + .unwrap(); + aggregator + .include_signer(4, p4_public_comshares.commitments[0], &p4_sk.to_public()) + .unwrap(); - let signers = aggregator.get_signers(); let message_hash = Secp256k1Sha256::h4(&message[..]); - + let signers = aggregator.signers(); let p1_partial = p1_sk .sign( &message_hash, @@ -385,14 +390,15 @@ fn resharing_from_non_frost_key() { let mut aggregator = SignatureAggregator::new(threshold_parameters, frost_pk, &message[..]); for i in 0..THRESHOLD_OF_PARTICIPANTS { - aggregator.include_signer( - signers[i as usize].index, - signers_public_comshares[i as usize].commitments[0], - &signers_secret_keys[i as usize].to_public(), - ); + aggregator + .include_signer( + signers[i as usize].index, + signers_public_comshares[i as usize].commitments[0], + &signers_secret_keys[i as usize].to_public(), + ) + .unwrap(); } - let participating_signers = aggregator.get_signers().clone(); let message_hash = Secp256k1Sha256::h4(&message[..]); for i in 0..THRESHOLD_OF_PARTICIPANTS { @@ -402,7 +408,7 @@ fn resharing_from_non_frost_key() { &frost_pk, &mut signers_secret_comshares[i as usize], 0, - &participating_signers, + aggregator.signers(), ) .unwrap(); aggregator.include_partial_signature(&pi_partial_signature);