diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index 80d2fc84c..ca1fbfb4f 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -2,7 +2,7 @@ Entries are listed in reverse chronological order. -## Unreleases +## Unreleased ### Breaking Changes @@ -12,6 +12,13 @@ Entries are listed in reverse chronological order. the default behaviour is now as if `cheater-detection` was enabled. If you explicitly *did not enable* it, you can avoid cheater detection by calling `aggregate_custom()` with `CheaterDetection::Disabled`. +* The `std` and `nightly` features were removed from all crates +* Renamed `frost_core::keys::refresh::refresh_dkg_part_1` to `refresh_dkg_part1`. +* Fixed the crate-specific versions of the `refresh` module to be non-generic. + +### Additional changes + +* Added DKG refresh functions to the crate-specific `refresh` modules. ## 2.2.0 diff --git a/frost-core/src/keys.rs b/frost-core/src/keys.rs index 7219143c5..cc6fe7af4 100644 --- a/frost-core/src/keys.rs +++ b/frost-core/src/keys.rs @@ -381,6 +381,11 @@ where pub(crate) fn coefficients(&self) -> &[CoefficientCommitment] { &self.0 } + + /// Return the threshold associated with this commitment. + pub(crate) fn min_signers(&self) -> u16 { + self.0.len() as u16 + } } /// A secret share generated by performing a (t-out-of-n) secret sharing scheme, @@ -563,6 +568,7 @@ pub fn split( header: Header::default(), verifying_shares, verifying_key, + min_signers: Some(min_signers), }; // Apply post-processing @@ -706,7 +712,7 @@ where signing_share: secret_share.signing_share, verifying_share, verifying_key, - min_signers: secret_share.commitment.0.len() as u16, + min_signers: secret_share.commitment.min_signers(), }) } } @@ -716,7 +722,7 @@ where /// /// Used for verification purposes before publishing a signature. #[derive(Clone, Debug, PartialEq, Eq, Getters)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct PublicKeyPackage { @@ -728,6 +734,12 @@ pub struct PublicKeyPackage { pub(crate) verifying_shares: BTreeMap, VerifyingShare>, /// The joint public key for the entire group. pub(crate) verifying_key: VerifyingKey, + /// The minimum number of signers (threshold) required for the group. + /// This can be None in packages created with `frost_core` prior to 3.0.0. + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + #[cfg_attr(feature = "serde", serde(default))] + #[getter(copy)] + pub(crate) min_signers: Option, } impl PublicKeyPackage @@ -738,11 +750,26 @@ where pub fn new( verifying_shares: BTreeMap, VerifyingShare>, verifying_key: VerifyingKey, + min_signers: u16, + ) -> Self { + Self::new_internal(verifying_shares, verifying_key, Some(min_signers)) + } + + /// Create a new [`PublicKeyPackage`] instance, allowing not specifying the + /// number of signers. This is used internally, in particular for testing + /// old [`PublicKeyPackage`]s that do not encode the `min_signers`. + #[cfg_attr(feature = "internals", visibility::make(pub))] + #[cfg_attr(docsrs, doc(cfg(feature = "internals")))] + pub(crate) fn new_internal( + verifying_shares: BTreeMap, VerifyingShare>, + verifying_key: VerifyingKey, + min_signers: Option, ) -> Self { Self { header: Header::default(), verifying_shares, verifying_key, + min_signers, } } @@ -761,6 +788,7 @@ where Ok(PublicKeyPackage::new( verifying_keys, VerifyingKey::from_commitment(commitment)?, + commitment.min_signers(), )) } @@ -777,6 +805,11 @@ where let group_commitment = sum_commitments(&commitments)?; Self::from_commitment(&identifiers, &group_commitment) } + + /// Return the maximum number of signers. + pub fn max_signers(&self) -> u16 { + self.verifying_shares.len() as u16 + } } #[cfg(feature = "serialization")] diff --git a/frost-core/src/keys/dkg.rs b/frost-core/src/keys/dkg.rs index 6f48c3e80..5f56256e2 100644 --- a/frost-core/src/keys/dkg.rs +++ b/frost-core/src/keys/dkg.rs @@ -491,7 +491,7 @@ pub fn part2( } for package in round1_packages.values() { - if package.commitment.0.len() != secret_package.min_signers as usize { + if package.commitment.min_signers() != secret_package.min_signers { return Err(Error::IncorrectNumberOfCommitments); } } diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index 07495163c..4df4e6a47 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -18,7 +18,7 @@ //! //! For the DKG approach, the flow is very similar to [DKG //! itself](`https://frost.zfnd.org/tutorial/dkg.html`). Each participant calls -//! [`refresh_dkg_part_1()`], keeps the returned secret package and sends the +//! [`refresh_dkg_part1()`], keeps the returned secret package and sends the //! returned package to other participants. Then each participants calls //! [`refresh_dkg_part2()`] and sends the returned packages to the other //! participants. Finally each participant calls [`refresh_dkg_shares()`]. @@ -61,6 +61,14 @@ pub fn compute_refreshing_shares( identifiers: &[Identifier], rng: &mut R, ) -> Result<(Vec>, PublicKeyPackage), Error> { + // Validate min_signers. It's OK if the min_signers is missing, because + // we validate it again in `refresh_share()`. + if let Some(package_min_signers) = pub_key_package.min_signers { + if min_signers != package_min_signers { + return Err(Error::InvalidMinSigners); + } + } + // Validate inputs if identifiers.len() != max_signers as usize { return Err(Error::IncorrectNumberOfIdentifiers); @@ -110,6 +118,7 @@ pub fn compute_refreshing_shares( header: pub_key_package.header, verifying_shares: refreshed_verifying_shares, verifying_key: pub_key_package.verifying_key, + min_signers: Some(pub_key_package.min_signers.unwrap_or(min_signers)), }; Ok((refreshing_shares_minus_identity, refreshed_pub_key_package)) @@ -168,7 +177,7 @@ pub fn refresh_share( /// It returns the [`round1::SecretPackage`] that must be kept in memory /// by the participant for the other steps, and the [`round1::Package`] that /// must be sent to each other participant in the refresh run. -pub fn refresh_dkg_part_1( +pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, @@ -460,6 +469,7 @@ pub fn refresh_dkg_shares( header: old_pub_key_package.header, verifying_shares: new_verifying_shares, verifying_key: old_pub_key_package.verifying_key, + min_signers: Some(round2_secret_package.min_signers), }; let key_package = KeyPackage { diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index 926c11c06..8c1b54f03 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -758,7 +758,15 @@ pub fn verify_signature_share( // In order to reuse `pre_aggregate()`, we need to create some "dummy" containers let signature_shares = BTreeMap::from([(identifier, *signature_share)]); let verifying_shares = BTreeMap::from([(identifier, *verifying_share)]); - let public_key_package = PublicKeyPackage::new(verifying_shares, *verifying_key); + let public_key_package = PublicKeyPackage { + verifying_shares, + verifying_key: *verifying_key, + // Use None since we don't have the min_signers value here. This + // can only cause problems if the `pre_aggregate` function relies on it. + // This has been documented in `pre_aggregate()`. + min_signers: None, + header: Header::default(), + }; let (signing_package, signature_shares, pubkeys) = ::pre_aggregate(signing_package, &signature_shares, &public_key_package)?; diff --git a/frost-core/src/serialization.rs b/frost-core/src/serialization.rs index 5fc642f95..01ca843c7 100644 --- a/frost-core/src/serialization.rs +++ b/frost-core/src/serialization.rs @@ -1,9 +1,22 @@ //! Serialization support. +#[cfg(feature = "serde")] +use alloc::collections::BTreeMap; +use alloc::string::String; use alloc::vec::Vec; +#[cfg(feature = "serde")] +use core::fmt::Formatter; +#[cfg(feature = "serde")] +use core::marker::PhantomData; use zeroize::Zeroize; +#[cfg(feature = "serde")] +use crate::keys::PublicKeyPackage; +#[cfg(feature = "serde")] +use crate::keys::VerifyingShare; use crate::{Ciphersuite, FieldError}; +#[cfg(feature = "serde")] +use crate::{Header, Identifier, VerifyingKey}; use crate::{Element, Error, Field, Group}; @@ -240,3 +253,239 @@ impl serde::Deserialize<'de>, C: Ciphersuite> Deserialize for T { postcard::from_bytes(bytes).map_err(|_| Error::DeserializationError) } } + +/// Custom deserializer for PublicKeyPackage, which allows a non-existing +/// `min_signers` field for the `postcard` encoding. +#[cfg(feature = "serde")] +impl<'de, C: Ciphersuite> serde::Deserialize<'de> for PublicKeyPackage +where + C: Ciphersuite, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use core::fmt; + + // The following are copied from the `serde::Deserialize` derive, and + // are required to support `visit_map()` which in turn is required for + // `serde_json`. + + enum Field { + Field0, + Field1, + Field2, + Field3, + } + + struct FieldVisitor; + + impl<'de> serde::de::Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting(&self, __formatter: &mut Formatter) -> fmt::Result { + Formatter::write_str(__formatter, "field identifier") + } + + fn visit_u64<__E>(self, __value: u64) -> Result + where + __E: serde::de::Error, + { + match __value { + 0u64 => Ok(Field::Field0), + 1u64 => Ok(Field::Field1), + 2u64 => Ok(Field::Field2), + 3u64 => Ok(Field::Field3), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Unsigned(__value), + &"field index 0 <= i < 4", + )), + } + } + + fn visit_str<__E>(self, __value: &str) -> Result + where + __E: serde::de::Error, + { + match __value { + "header" => Ok(Field::Field0), + "verifying_shares" => Ok(Field::Field1), + "verifying_key" => Ok(Field::Field2), + "min_signers" => Ok(Field::Field3), + _ => Err(serde::de::Error::unknown_field(__value, FIELDS)), + } + } + + fn visit_bytes<__E>(self, __value: &[u8]) -> Result + where + __E: serde::de::Error, + { + match __value { + b"header" => Ok(Field::Field0), + b"verifying_shares" => Ok(Field::Field1), + b"verifying_key" => Ok(Field::Field2), + b"min_signers" => Ok(Field::Field3), + _ => { + let __value = &String::from_utf8_lossy(__value); + Err(serde::de::Error::unknown_field(__value, FIELDS)) + } + } + } + } + + impl<'de> serde::Deserialize<'de> for Field { + #[inline] + fn deserialize<__D>(__deserializer: __D) -> Result + where + __D: serde::Deserializer<'de>, + { + serde::Deserializer::deserialize_identifier(__deserializer, FieldVisitor) + } + } + + struct Visitor { + marker: PhantomData, + } + + impl<'de, C: Ciphersuite> serde::de::Visitor<'de> for Visitor + where + C: Ciphersuite, + { + type Value = PublicKeyPackage; + + fn expecting(&self, fmt: &mut Formatter) -> core::fmt::Result { + Formatter::write_str(fmt, "struct PublicKeyPackage") + } + + // Postcard serializes structs as sequences, so we override + // `visit_seq` to deserialize the struct from a sequence of elements. + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + // Read the first three fields as usual. + + let header = seq.next_element::>()?.ok_or_else(|| { + serde::de::Error::invalid_length( + 0usize, + &"struct PublicKeyPackage with 4 elements", + ) + })?; + let verifying_shares = seq + .next_element::, VerifyingShare>>()? + .ok_or_else(|| { + serde::de::Error::invalid_length( + 1usize, + &"struct PublicKeyPackage with 4 elements", + ) + })?; + let verifying_key = seq.next_element::>()?.ok_or_else(|| { + serde::de::Error::invalid_length( + 2usize, + &"struct PublicKeyPackage with 4 elements", + ) + })?; + + // For the `min_signers` field, fill it with None if + // `next_element()` fails (i.e. there are no other elements) + let min_signers = match seq.next_element::>() { + Ok(Some(min_signers)) => min_signers, + _ => None, + }; + + Ok(PublicKeyPackage { + header, + verifying_shares, + verifying_key, + min_signers, + }) + } + + // Again this is copied from the `serde::Deserialize` derive; + // the only change is not requiring `min_signers` to be present. + fn visit_map<__A>(self, mut __map: __A) -> Result + where + __A: serde::de::MapAccess<'de>, + { + let mut __field0: Option> = None; + let mut __field1: Option, VerifyingShare>> = None; + let mut __field2: Option> = None; + let mut __field3: Option> = None; + while let Some(__key) = serde::de::MapAccess::next_key::(&mut __map)? { + match __key { + Field::Field0 => { + if Option::is_some(&__field0) { + return Err(<__A::Error as serde::de::Error>::duplicate_field( + "header", + )); + } + __field0 = + Some(serde::de::MapAccess::next_value::>(&mut __map)?); + } + Field::Field1 => { + if Option::is_some(&__field1) { + return Err(<__A::Error as serde::de::Error>::duplicate_field( + "verifying_shares", + )); + } + __field1 = Some(serde::de::MapAccess::next_value::< + BTreeMap, VerifyingShare>, + >(&mut __map)?); + } + Field::Field2 => { + if Option::is_some(&__field2) { + return Err(<__A::Error as serde::de::Error>::duplicate_field( + "verifying_key", + )); + } + __field2 = Some(serde::de::MapAccess::next_value::>( + &mut __map, + )?); + } + Field::Field3 => { + if Option::is_some(&__field3) { + return Err(<__A::Error as serde::de::Error>::duplicate_field( + "min_signers", + )); + } + __field3 = + Some(serde::de::MapAccess::next_value::>(&mut __map)?); + } + } + } + let __field0 = match __field0 { + Some(__field0) => __field0, + None => Err(<__A::Error as serde::de::Error>::missing_field("header"))?, + }; + let __field1 = match __field1 { + Some(__field1) => __field1, + None => Err(<__A::Error as serde::de::Error>::missing_field( + "verifying_shares", + ))?, + }; + let __field2 = match __field2 { + Some(__field2) => __field2, + None => Err(<__A::Error as serde::de::Error>::missing_field( + "verifying_key", + ))?, + }; + let __field3 = __field3.unwrap_or_default(); + Ok(PublicKeyPackage { + header: __field0, + verifying_shares: __field1, + verifying_key: __field2, + min_signers: __field3, + }) + } + } + + const FIELDS: &[&str] = &["header", "verifying_shares", "verifying_key", "min_signers"]; + deserializer.deserialize_struct( + "PublicKeyPackage", + FIELDS, + Visitor { + marker: PhantomData::, + }, + ) + } +} diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index 5b40e4204..a2c0df8c1 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -542,9 +542,9 @@ where // will have all the participant's packages. let mut key_packages = BTreeMap::new(); - // Map of the verifying key of each participant. + // Map of the verifying share of each participant. // Used by the signing test that follows. - let mut verifying_keys = BTreeMap::new(); + let mut verifying_shares = BTreeMap::new(); // The group public key, used by the signing test that follows. let mut verifying_key = None; // For each participant, store the set of verifying keys they have computed. @@ -572,7 +572,7 @@ where &received_round2_packages[&participant_identifier], ) .unwrap(); - verifying_keys.insert(participant_identifier, key_package.verifying_share); + verifying_shares.insert(participant_identifier, key_package.verifying_share); // Test if all verifying_key are equal if let Some(previous_verifying_key) = verifying_key { assert_eq!(previous_verifying_key, key_package.verifying_key) @@ -585,10 +585,14 @@ where // Test if the set of verifying keys is correct for all participants. for verifying_keys_for_participant in pubkey_packages_by_participant.values() { - assert!(verifying_keys_for_participant.verifying_shares == verifying_keys); + assert!(verifying_keys_for_participant.verifying_shares == verifying_shares); } - let pubkeys = frost::keys::PublicKeyPackage::new(verifying_keys, verifying_key.unwrap()); + let pubkeys = pubkey_packages_by_participant + .first_key_value() + .unwrap() + .1 + .clone(); // Proceed with the signing test. check_sign(min_signers, key_packages, rng, pubkeys).unwrap() diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index 83b67b5fa..f9b58dbe2 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -5,7 +5,7 @@ use rand_core::{CryptoRng, RngCore}; use crate::keys::dkg::{round1, round2}; use crate::keys::generate_with_dealer; use crate::keys::refresh::{ - compute_refreshing_shares, refresh_dkg_part2, refresh_dkg_part_1, refresh_share, + compute_refreshing_shares, refresh_dkg_part1, refresh_dkg_part2, refresh_share, }; #[cfg(feature = "serialization")] use crate::keys::{PublicKeyPackage, SecretShare}; @@ -296,33 +296,15 @@ pub fn check_refresh_shares_with_dealer_fails_with_different_min_signers< // Trusted Dealer generates zero keys and new public key package - let (zero_shares, _new_pub_key_package) = compute_refreshing_shares( + let r = compute_refreshing_shares( pub_key_package, NEW_MAX_SIGNERS, NEW_MIN_SIGNERS, &remaining_ids, &mut rng, - ) - .unwrap(); - - // Each participant refreshes their share - - let mut new_shares = BTreeMap::new(); - - for i in 0..remaining_ids.len() { - let identifier = remaining_ids[i]; - let current_share = &old_key_packages[&identifier]; - let new_share = refresh_share(zero_shares[i].clone(), current_share); - new_shares.insert(identifier, new_share); - } - - assert!( - new_shares - .iter() - .all(|(_, v)| v.is_err() && matches!(v, Err(Error::InvalidMinSigners))), - "{:?}", - new_shares ); + + assert_eq!(r, Err(Error::InvalidMinSigners)); } /// Test FROST signing with DKG with a Ciphersuite. @@ -387,7 +369,7 @@ where // In practice, each participant will perform this on their own environments. for participant_identifier in remaining_ids.clone() { let (round1_secret_package, round1_package) = - refresh_dkg_part_1(participant_identifier, max_signers, min_signers, &mut rng).unwrap(); + refresh_dkg_part1(participant_identifier, max_signers, min_signers, &mut rng).unwrap(); // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. @@ -474,9 +456,9 @@ where // will have all the participant's packages. let mut key_packages = BTreeMap::new(); - // Map of the verifying key of each participant. + // Map of the verifying share of each participant. // Used by the signing test that follows. - let mut verifying_keys = BTreeMap::new(); + let mut verifying_shares = BTreeMap::new(); // The group public key, used by the signing test that follows. let mut verifying_key = None; // For each participant, store the set of verifying keys they have computed. @@ -506,7 +488,7 @@ where old_key_packages[&participant_identifier].clone(), ) .unwrap(); - verifying_keys.insert(participant_identifier, key_package.verifying_share); + verifying_shares.insert(participant_identifier, key_package.verifying_share); // Test if all verifying_key are equal if let Some(previous_verifying_key) = verifying_key { assert_eq!(previous_verifying_key, key_package.verifying_key) @@ -519,10 +501,14 @@ where // Test if the set of verifying keys is correct for all participants. for verifying_keys_for_participant in pubkey_packages_by_participant.values() { - assert!(verifying_keys_for_participant.verifying_shares == verifying_keys); + assert!(verifying_keys_for_participant.verifying_shares == verifying_shares); } - let pubkeys = frost::keys::PublicKeyPackage::new(verifying_keys, verifying_key.unwrap()); + let pubkeys = pubkey_packages_by_participant + .first_key_value() + .unwrap() + .1 + .clone(); // Proceed with the signing test. check_sign(min_signers, key_packages, rng, pubkeys).unwrap() @@ -594,7 +580,7 @@ pub fn check_refresh_shares_with_dkg_smaller_threshold< // In practice, each participant will perform this on their own environments. for participant_identifier in remaining_ids.clone() { let (round1_secret_package, round1_package) = - refresh_dkg_part_1(participant_identifier, max_signers, min_signers, &mut rng).unwrap(); + refresh_dkg_part1(participant_identifier, max_signers, min_signers, &mut rng).unwrap(); // Store the participant's secret package for later use. // In practice each participant will store it in their own environment. diff --git a/frost-core/src/tests/vectors.rs b/frost-core/src/tests/vectors.rs index 950fe9a94..e278d0838 100644 --- a/frost-core/src/tests/vectors.rs +++ b/frost-core/src/tests/vectors.rs @@ -293,7 +293,8 @@ pub fn check_sign_with_test_vectors(json_vectors: &Value) { .map(|(i, key_package)| (i, *key_package.verifying_share())) .collect(); - let pubkey_package = frost::keys::PublicKeyPackage::new(verifying_shares, verifying_key); + let pubkey_package = + frost::keys::PublicKeyPackage::new(verifying_shares, verifying_key, min_signers as u16); //////////////////////////////////////////////////////////////////////////// // Aggregation: collects the signing shares from all participants, diff --git a/frost-core/src/tests/vectors_dkg.rs b/frost-core/src/tests/vectors_dkg.rs index 02d0c85ad..90fafef26 100644 --- a/frost-core/src/tests/vectors_dkg.rs +++ b/frost-core/src/tests/vectors_dkg.rs @@ -176,6 +176,7 @@ fn build_public_key_package(json_vectors: &Value) -> PublicKeyPa let mut verifying_shares = BTreeMap::new(); let max_participants = json_vectors["config"]["MAX_PARTICIPANTS"].as_u64().unwrap() as u8; + let min_participants = json_vectors["config"]["MIN_PARTICIPANTS"].as_u64().unwrap() as u8; for i in 1..=max_participants { let participant_id: Identifier = (inputs[i.to_string()]["identifier"].as_u64().unwrap() @@ -196,6 +197,7 @@ fn build_public_key_package(json_vectors: &Value) -> PublicKeyPa header: Header::default(), verifying_shares, verifying_key, + min_signers: Some(min_participants as u16), } } diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs index 321d64f1c..9ae94319f 100644 --- a/frost-core/src/traits.rs +++ b/frost-core/src/traits.rs @@ -295,9 +295,9 @@ pub trait Ciphersuite: Copy + PartialEq + Debug + 'static + Send + Sync { /// Optional. Pre-process [`crate::aggregate()`] and /// [`crate::verify_signature_share()`] inputs. In the latter case, "dummy" /// container BTreeMap and PublicKeyPackage are passed with the relevant - /// values. The default implementation returns them as-is. [`Cow`] is used - /// so implementations can choose to return the same passed reference or a - /// modified clone. + /// values (PublicKeyPackage.min_signers will be None). The default + /// implementation returns them as-is. [`Cow`] is used so implementations + /// can choose to return the same passed reference or a modified clone. #[allow(clippy::type_complexity)] fn pre_aggregate<'a>( signing_package: &'a SigningPackage, diff --git a/frost-ed25519/src/keys/refresh.rs b/frost-ed25519/src/keys/refresh.rs index 3e26bcb76..36797d959 100644 --- a/frost-ed25519/src/keys/refresh.rs +++ b/frost-ed25519/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-ed25519/tests/helpers/samples.rs b/frost-ed25519/tests/helpers/samples.rs index e1d87e169..3ce2f8af6 100644 --- a/frost-ed25519/tests/helpers/samples.rs +++ b/frost-ed25519/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-ed25519/tests/integration_tests.rs b/frost-ed25519/tests/integration_tests.rs index 649576c24..d43664a62 100644 --- a/frost-ed25519/tests/integration_tests.rs +++ b/frost-ed25519/tests/integration_tests.rs @@ -117,7 +117,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -150,7 +150,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-ed25519/tests/recreation_tests.rs b/frost-ed25519/tests/recreation_tests.rs index 5ae8964e4..4261cb458 100644 --- a/frost-ed25519/tests/recreation_tests.rs +++ b/frost-ed25519/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-ed25519/tests/serde_tests.rs b/frost-ed25519/tests/serde_tests.rs index 9f722797b..a36c278b9 100644 --- a/frost-ed25519/tests/serde_tests.rs +++ b/frost-ed25519/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "2a00000000000000000000000000000000000000000000000000000000000000": "5866666666666666666666666666666666666666666666666666666666666666" }, - "verifying_key": "5866666666666666666666666666666666666666666666666666666666666666" + "verifying_key": "5866666666666666666666666666666666666666666666666666666666666666", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-ED25519-SHA512-v1" + }, + "verifying_shares": { + "2a00000000000000000000000000000000000000000000000000000000000000": "5866666666666666666666666666666666666666666666666666666666666666" + }, + "verifying_key": "5866666666666666666666666666666666666666666666666666666666666666" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-ed25519/tests/serialization_tests.rs b/frost-ed25519/tests/serialization_tests.rs index 7fe52b912..6af901684 100644 --- a/frost-ed25519/tests/serialization_tests.rs +++ b/frost-ed25519/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-ed25519/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-ed25519/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 000000000..342de36f3 --- /dev/null +++ b/frost-ed25519/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-ed25519/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00b169f0da012a00000000000000000000000000000000000000000000000000000000000000586666666666666666666666666666666666666666666666666666666666666658666666666666666666666666666666666666666666666666666666666666660102 diff --git a/frost-ed448/src/keys/refresh.rs b/frost-ed448/src/keys/refresh.rs index 3e26bcb76..36797d959 100644 --- a/frost-ed448/src/keys/refresh.rs +++ b/frost-ed448/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-ed448/tests/helpers/samples.rs b/frost-ed448/tests/helpers/samples.rs index 4994b515d..d4e045217 100644 --- a/frost-ed448/tests/helpers/samples.rs +++ b/frost-ed448/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-ed448/tests/integration_tests.rs b/frost-ed448/tests/integration_tests.rs index 601228dce..d22705c76 100644 --- a/frost-ed448/tests/integration_tests.rs +++ b/frost-ed448/tests/integration_tests.rs @@ -117,7 +117,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -150,7 +150,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-ed448/tests/recreation_tests.rs b/frost-ed448/tests/recreation_tests.rs index 44b7abfe8..a0a457aa5 100644 --- a/frost-ed448/tests/recreation_tests.rs +++ b/frost-ed448/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-ed448/tests/serde_tests.rs b/frost-ed448/tests/serde_tests.rs index 3b5c667a4..db380eed1 100644 --- a/frost-ed448/tests/serde_tests.rs +++ b/frost-ed448/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" }, - "verifying_key": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" + "verifying_key": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-ED448-SHAKE256-v1" + }, + "verifying_shares": { + "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" + }, + "verifying_key": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-ed448/tests/serialization_tests.rs b/frost-ed448/tests/serialization_tests.rs index e6a177ec4..e9810a6b1 100644 --- a/frost-ed448/tests/serialization_tests.rs +++ b/frost-ed448/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-ed448/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-ed448/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 000000000..fcfcff41b --- /dev/null +++ b/frost-ed448/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-ed448/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +005a064cfd012a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f690014fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f69000102 diff --git a/frost-p256/src/keys/refresh.rs b/frost-p256/src/keys/refresh.rs index 3e26bcb76..36797d959 100644 --- a/frost-p256/src/keys/refresh.rs +++ b/frost-p256/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-p256/tests/helpers/samples.rs b/frost-p256/tests/helpers/samples.rs index be57e472e..df9407b9d 100644 --- a/frost-p256/tests/helpers/samples.rs +++ b/frost-p256/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-p256/tests/integration_tests.rs b/frost-p256/tests/integration_tests.rs index 1fd354b0d..8a9a92ed1 100644 --- a/frost-p256/tests/integration_tests.rs +++ b/frost-p256/tests/integration_tests.rs @@ -117,7 +117,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -150,7 +150,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-p256/tests/recreation_tests.rs b/frost-p256/tests/recreation_tests.rs index 0f4dbf7dd..e5816bfe2 100644 --- a/frost-p256/tests/recreation_tests.rs +++ b/frost-p256/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-p256/tests/serde_tests.rs b/frost-p256/tests/serde_tests.rs index c14758146..7091bd7b3 100644 --- a/frost-p256/tests/serde_tests.rs +++ b/frost-p256/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "000000000000000000000000000000000000000000000000000000000000002a": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296" }, - "verifying_key": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296" + "verifying_key": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-P256-SHA256-v1" + }, + "verifying_shares": { + "000000000000000000000000000000000000000000000000000000000000002a": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296" + }, + "verifying_key": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-p256/tests/serialization_tests.rs b/frost-p256/tests/serialization_tests.rs index b83b12ee7..dbd32cec3 100644 --- a/frost-p256/tests/serialization_tests.rs +++ b/frost-p256/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-p256/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-p256/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 000000000..158419f6e --- /dev/null +++ b/frost-p256/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-p256/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00a132f0c901000000000000000000000000000000000000000000000000000000000000002a036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2960102 diff --git a/frost-rerandomized/src/lib.rs b/frost-rerandomized/src/lib.rs index 78ee354ba..4099893b4 100644 --- a/frost-rerandomized/src/lib.rs +++ b/frost-rerandomized/src/lib.rs @@ -111,9 +111,10 @@ impl Randomize for PublicKeyPackage { }) .collect(); - Ok(PublicKeyPackage::new( + Ok(PublicKeyPackage::new_internal( randomized_verifying_shares, randomized_params.randomized_verifying_key, + self.min_signers(), )) } } diff --git a/frost-ristretto255/src/keys/refresh.rs b/frost-ristretto255/src/keys/refresh.rs index 3e26bcb76..36797d959 100644 --- a/frost-ristretto255/src/keys/refresh.rs +++ b/frost-ristretto255/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-ristretto255/tests/helpers/samples.rs b/frost-ristretto255/tests/helpers/samples.rs index d6f58b90d..0b0a6e304 100644 --- a/frost-ristretto255/tests/helpers/samples.rs +++ b/frost-ristretto255/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-ristretto255/tests/integration_tests.rs b/frost-ristretto255/tests/integration_tests.rs index d16f682da..8ff8af167 100644 --- a/frost-ristretto255/tests/integration_tests.rs +++ b/frost-ristretto255/tests/integration_tests.rs @@ -118,7 +118,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -151,7 +151,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-ristretto255/tests/recreation_tests.rs b/frost-ristretto255/tests/recreation_tests.rs index a5974a965..fc6947b29 100644 --- a/frost-ristretto255/tests/recreation_tests.rs +++ b/frost-ristretto255/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-ristretto255/tests/serde_tests.rs b/frost-ristretto255/tests/serde_tests.rs index faf1769ae..d73e846ef 100644 --- a/frost-ristretto255/tests/serde_tests.rs +++ b/frost-ristretto255/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "2a00000000000000000000000000000000000000000000000000000000000000": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" }, - "verifying_key": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" + "verifying_key": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-RISTRETTO255-SHA512-v1" + }, + "verifying_shares": { + "2a00000000000000000000000000000000000000000000000000000000000000": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" + }, + "verifying_key": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-ristretto255/tests/serialization_tests.rs b/frost-ristretto255/tests/serialization_tests.rs index 1aa96a25a..641bb4746 100644 --- a/frost-ristretto255/tests/serialization_tests.rs +++ b/frost-ristretto255/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-ristretto255/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-ristretto255/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 000000000..8a5fd3195 --- /dev/null +++ b/frost-ristretto255/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-ristretto255/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00d76ecff5012a00000000000000000000000000000000000000000000000000000000000000e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d760102 diff --git a/frost-secp256k1-tr/src/keys/refresh.rs b/frost-secp256k1-tr/src/keys/refresh.rs index 3e26bcb76..36797d959 100644 --- a/frost-secp256k1-tr/src/keys/refresh.rs +++ b/frost-secp256k1-tr/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs index cc012d019..d4e5f7edc 100644 --- a/frost-secp256k1-tr/src/lib.rs +++ b/frost-secp256k1-tr/src/lib.rs @@ -645,7 +645,7 @@ pub mod keys { (*i, vs) }) .collect(); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, self.min_signers()) } else { self } @@ -766,7 +766,11 @@ pub mod keys { (*i, vs) }) .collect(); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal( + verifying_shares, + verifying_key, + public_key_package.min_signers(), + ) } } diff --git a/frost-secp256k1-tr/tests/helpers/samples.rs b/frost-secp256k1-tr/tests/helpers/samples.rs index 337ae709f..e94ae01ee 100644 --- a/frost-secp256k1-tr/tests/helpers/samples.rs +++ b/frost-secp256k1-tr/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-secp256k1-tr/tests/integration_tests.rs b/frost-secp256k1-tr/tests/integration_tests.rs index fe5007dad..11db8b2bb 100644 --- a/frost-secp256k1-tr/tests/integration_tests.rs +++ b/frost-secp256k1-tr/tests/integration_tests.rs @@ -118,7 +118,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -151,7 +151,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-secp256k1-tr/tests/recreation_tests.rs b/frost-secp256k1-tr/tests/recreation_tests.rs index f6f71ea00..bcdd3df4b 100644 --- a/frost-secp256k1-tr/tests/recreation_tests.rs +++ b/frost-secp256k1-tr/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-secp256k1-tr/tests/serde_tests.rs b/frost-secp256k1-tr/tests/serde_tests.rs index 62a70e70c..e13cf1d9a 100644 --- a/frost-secp256k1-tr/tests/serde_tests.rs +++ b/frost-secp256k1-tr/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" }, - "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "verifying_shares": { + "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }, + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-secp256k1-tr/tests/serialization_tests.rs b/frost-secp256k1-tr/tests/serialization_tests.rs index d38bc5ece..997501730 100644 --- a/frost-secp256k1-tr/tests/serialization_tests.rs +++ b/frost-secp256k1-tr/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 000000000..b147c34cf --- /dev/null +++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1-tr/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00230f8ab301000000000000000000000000000000000000000000000000000000000000002a0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980102 diff --git a/frost-secp256k1/src/keys/refresh.rs b/frost-secp256k1/src/keys/refresh.rs index 3e26bcb76..36797d959 100644 --- a/frost-secp256k1/src/keys/refresh.rs +++ b/frost-secp256k1/src/keys/refresh.rs @@ -5,14 +5,14 @@ use crate::{ frost, keys::dkg::{round1, round2}, - Ciphersuite, CryptoRng, Error, Identifier, RngCore, + CryptoRng, Error, Identifier, RngCore, }; use alloc::{collections::btree_map::BTreeMap, vec::Vec}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refer to [`frost_core::keys::refresh::compute_refreshing_shares`]. -pub fn compute_refreshing_shares( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, @@ -29,21 +29,21 @@ pub fn compute_refreshing_shares( } /// Refer to [`frost_core::keys::refresh::refresh_share`]. -pub fn refresh_share( +pub fn refresh_share( zero_share: SecretShare, current_share: &KeyPackage, ) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } -/// Refer to [`frost_core::keys::refresh::refresh_dkg_part_1`]. +/// Refer to [`frost_core::keys::refresh::refresh_dkg_part1`]. pub fn refresh_dkg_part1( identifier: Identifier, max_signers: u16, min_signers: u16, mut rng: R, ) -> Result<(round1::SecretPackage, round1::Package), Error> { - frost::keys::refresh::refresh_dkg_part_1(identifier, max_signers, min_signers, &mut rng) + frost::keys::refresh::refresh_dkg_part1(identifier, max_signers, min_signers, &mut rng) } /// Refer to [`frost_core::keys::refresh::refresh_dkg_part2`]. diff --git a/frost-secp256k1/tests/helpers/samples.rs b/frost-secp256k1/tests/helpers/samples.rs index a69632d29..2b1bb8ef5 100644 --- a/frost-secp256k1/tests/helpers/samples.rs +++ b/frost-secp256k1/tests/helpers/samples.rs @@ -104,7 +104,19 @@ pub fn public_key_package() -> PublicKeyPackage { let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); - PublicKeyPackage::new(verifying_shares, verifying_key) + PublicKeyPackage::new_internal(verifying_shares, verifying_key, None) +} + +/// Generate a sample PublicKeyPackage with `min_signers`. +pub fn public_key_package_new() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element.as_ref()).unwrap(); + let serialized_element = ::Group::serialize(&element1()).unwrap(); + let verifying_key = VerifyingKey::deserialize(serialized_element.as_ref()).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key, 2) } /// Generate a sample round1::SecretPackage. diff --git a/frost-secp256k1/tests/integration_tests.rs b/frost-secp256k1/tests/integration_tests.rs index 63ba125f5..e2c111c47 100644 --- a/frost-secp256k1/tests/integration_tests.rs +++ b/frost-secp256k1/tests/integration_tests.rs @@ -117,7 +117,7 @@ fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_s Identifier::try_from(4).unwrap(), Identifier::try_from(5).unwrap(), ]; - let min_signers = 3; + let min_signers = 2; let max_signers = 3; let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; @@ -150,7 +150,7 @@ fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { let rng = rand::rngs::OsRng; let identifiers = vec![Identifier::try_from(1).unwrap()]; - let min_signers = 3; + let min_signers = 2; let max_signers = 1; let error = Error::InvalidMaxSigners; diff --git a/frost-secp256k1/tests/recreation_tests.rs b/frost-secp256k1/tests/recreation_tests.rs index e61b30333..f72c154c1 100644 --- a/frost-secp256k1/tests/recreation_tests.rs +++ b/frost-secp256k1/tests/recreation_tests.rs @@ -101,8 +101,25 @@ fn check_public_key_package_recreation() { let verifying_shares = public_key_package.verifying_shares(); let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers(); - let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + let new_public_key_package = + PublicKeyPackage::new_internal(verifying_shares.clone(), *verifying_key, min_signers); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_new_recreation() { + let public_key_package = samples::public_key_package_new(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + let min_signers = public_key_package.min_signers().unwrap(); + + let new_public_key_package = + PublicKeyPackage::new(verifying_shares.clone(), *verifying_key, min_signers); assert!(public_key_package == new_public_key_package); } diff --git a/frost-secp256k1/tests/serde_tests.rs b/frost-secp256k1/tests/serde_tests.rs index 82a0735d4..5b02fdaeb 100644 --- a/frost-secp256k1/tests/serde_tests.rs +++ b/frost-secp256k1/tests/serde_tests.rs @@ -434,7 +434,7 @@ fn check_key_package_serialization() { #[test] fn check_public_key_package_serialization() { - let public_key_package = samples::public_key_package(); + let public_key_package = samples::public_key_package_new(); let json = serde_json::to_string_pretty(&public_key_package).unwrap(); println!("{}", json); @@ -450,11 +450,27 @@ fn check_public_key_package_serialization() { "verifying_shares": { "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" }, - "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "min_signers": 2 }"#; let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); assert!(public_key_package == decoded_public_key_package); + // Old version without min_signers + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-v1" + }, + "verifying_shares": { + "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }, + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package.verifying_key() == decoded_public_key_package.verifying_key()); + assert!(public_key_package.verifying_shares() == decoded_public_key_package.verifying_shares()); + let invalid_json = "{}"; assert!(serde_json::from_str::(invalid_json).is_err()); diff --git a/frost-secp256k1/tests/serialization_tests.rs b/frost-secp256k1/tests/serialization_tests.rs index ec8d6f8ef..08e53d3e4 100644 --- a/frost-secp256k1/tests/serialization_tests.rs +++ b/frost-secp256k1/tests/serialization_tests.rs @@ -82,6 +82,17 @@ fn check_public_key_package_postcard_serialization() { ); } +#[test] +fn check_public_key_package_new_postcard_serialization() { + let public_key_package = samples::public_key_package_new(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + #[test] fn check_round1_secret_package_postcard_serialization() { let round1_secret_package = samples::round1_secret_package(); diff --git a/frost-secp256k1/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap b/frost-secp256k1/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap new file mode 100644 index 000000000..0435fdaa1 --- /dev/null +++ b/frost-secp256k1/tests/snapshots/serialization_tests__check_public_key_package_new_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00eed6b1b101000000000000000000000000000000000000000000000000000000000000002a0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980102