From af48c745a15b6abba917d7cdd060dcc47a281cbd Mon Sep 17 00:00:00 2001 From: natalie Date: Wed, 29 May 2024 10:49:42 +0100 Subject: [PATCH 01/19] Add refresh shares with dealer functionality (#245) --- frost-core/src/keys.rs | 1 + frost-core/src/keys/refresh.rs | 119 ++++++++++++++++ frost-core/src/tests.rs | 1 + frost-core/src/tests/refresh.rs | 128 ++++++++++++++++++ frost-ed25519/src/keys/refresh.rs | 30 ++++ frost-ed25519/tests/integration_tests.rs | 78 +++++++++++ frost-ed448/src/keys/refresh.rs | 30 ++++ frost-ed448/tests/integration_tests.rs | 78 +++++++++++ frost-p256/src/keys/refresh.rs | 30 ++++ frost-p256/src/lib.rs | 1 + frost-p256/tests/integration_tests.rs | 78 +++++++++++ frost-ristretto255/src/keys/refresh.rs | 30 ++++ frost-ristretto255/src/lib.rs | 1 + frost-ristretto255/tests/integration_tests.rs | 78 +++++++++++ frost-secp256k1/src/keys/refresh.rs | 30 ++++ frost-secp256k1/tests/integration_tests.rs | 78 +++++++++++ gencode/src/main.rs | 1 + 17 files changed, 792 insertions(+) create mode 100644 frost-core/src/keys/refresh.rs create mode 100644 frost-core/src/tests/refresh.rs create mode 100644 frost-ed25519/src/keys/refresh.rs create mode 100644 frost-ed448/src/keys/refresh.rs create mode 100644 frost-p256/src/keys/refresh.rs create mode 100644 frost-ristretto255/src/keys/refresh.rs create mode 100644 frost-secp256k1/src/keys/refresh.rs diff --git a/frost-core/src/keys.rs b/frost-core/src/keys.rs index 8b7b9c68..095b5f66 100644 --- a/frost-core/src/keys.rs +++ b/frost-core/src/keys.rs @@ -29,6 +29,7 @@ use crate::serialization::{Deserialize, Serialize}; use super::compute_lagrange_coefficient; pub mod dkg; +pub mod refresh; pub mod repairable; /// Sum the commitments from all participants in a distributed key generation diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs new file mode 100644 index 00000000..f292eb78 --- /dev/null +++ b/frost-core/src/keys/refresh.rs @@ -0,0 +1,119 @@ +//! Refresh Shares +//! +//! Implements the functionality to refresh a share. This requires the participation +//! of all the remaining signers. This can be done using a Trusted Dealer or +//! DKG (not yet implemented) + +use std::collections::BTreeMap; + +use crate::{ + keys::{ + generate_coefficients, generate_secret_shares, validate_num_of_signers, + CoefficientCommitment, PublicKeyPackage, SigningKey, SigningShare, VerifyingShare, + }, + Ciphersuite, CryptoRng, Error, Field, Group, Identifier, RngCore, Scalar, +}; + +use super::{SecretShare, VerifiableSecretSharingCommitment}; + +/// Refreshes shares using a trusted dealer +pub fn refresh_shares_with_dealer( + old_shares: BTreeMap, SecretShare>, + old_pub_key_package: PublicKeyPackage, + max_signers: u16, + min_signers: u16, + identifiers: &[Identifier], + rng: &mut R, +) -> Result<(BTreeMap, SecretShare>, PublicKeyPackage), Error> { + // Validate inputs + + if identifiers.len() != max_signers as usize { + return Err(Error::IncorrectNumberOfIdentifiers); + } + + validate_num_of_signers(min_signers, max_signers)?; + + // Build zero key shares + + let zero_key = SigningKey { + scalar: <::Field>::zero(), + }; + + let coefficients = generate_coefficients::(min_signers as usize - 1, rng); + + let zero_key_shares = generate_secret_shares( + &zero_key, + max_signers, + min_signers, + coefficients, + identifiers, + )?; + + // Build new shares and public key package + + let mut new_shares: BTreeMap, SecretShare> = BTreeMap::new(); + let mut verifying_shares: BTreeMap, VerifyingShare> = BTreeMap::new(); + + for share in zero_key_shares { + let signer_public = SigningShare::into(share.signing_share); + verifying_shares.insert(share.identifier, signer_public); + + let old_share = old_shares.get(&share.identifier); + + match old_share { + Some(old_share) => new_shares.insert( + share.identifier, + add_secret_shares::(share.clone(), old_share)?, + ), + None => return Err(Error::UnknownIdentifier), + }; + } + + let pub_key_package = PublicKeyPackage:: { + header: old_pub_key_package.header, + verifying_shares, + verifying_key: old_pub_key_package.verifying_key, + }; + + Ok((new_shares, pub_key_package)) +} + +fn add_secret_shares( + zero_share: SecretShare, + old_share: &SecretShare, +) -> Result, Error> { + let signing_share: Scalar = + zero_share.signing_share.to_scalar() + old_share.signing_share.to_scalar(); + + let zero_commitments = zero_share.commitment.0; + let old_commitments = old_share.commitment.0.clone(); + + let mut commitments: Vec> = Vec::with_capacity(zero_commitments.len()); + + if old_commitments.len() >= zero_commitments.len() { + for i in 0..zero_commitments.len() { + if let (Some(zero_commitment), Some(old_commitment)) = + (zero_commitments.get(i), old_commitments.get(i)) + { + commitments.push(CoefficientCommitment::new( + zero_commitment.0 + old_commitment.0, + )); + } else { + return Err(Error::IncorrectNumberOfCommitments); + } + } + } else { + return Err(Error::MissingCommitment); + } + + let commitment = VerifiableSecretSharingCommitment::new(commitments); + + let signing_share = SigningShare::new(signing_share); + + Ok(SecretShare { + header: zero_share.header, + identifier: zero_share.identifier, + signing_share, + commitment, + }) +} diff --git a/frost-core/src/tests.rs b/frost-core/src/tests.rs index d2b1b712..e4855c90 100644 --- a/frost-core/src/tests.rs +++ b/frost-core/src/tests.rs @@ -7,6 +7,7 @@ pub mod ciphersuite_generic; pub mod coefficient_commitment; pub mod helpers; pub mod proptests; +pub mod refresh; pub mod repairable; pub mod vectors; pub mod vectors_dkg; diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs new file mode 100644 index 00000000..5127cfe1 --- /dev/null +++ b/frost-core/src/tests/refresh.rs @@ -0,0 +1,128 @@ +//! Test for Refreshing shares + +use std::collections::BTreeMap; + +use rand_core::{CryptoRng, RngCore}; + +use crate::{self as frost}; +use crate::{ + keys::{refresh::refresh_shares_with_dealer, PublicKeyPackage, SecretShare}, + Ciphersuite, Error, Identifier, SigningKey, +}; + +use super::ciphersuite_generic::check_sign; + +/// We want to test that recover share matches the original share +pub fn check_refresh_shares_with_dealer(mut rng: R) { + // Compute shares + + //////////////////////////////////////////////////////////////////////////// + // Old Key generation + //////////////////////////////////////////////////////////////////////////// + + let max_signers = 5; + let min_signers = 3; + let (old_shares, pub_key_package) = frost::keys::generate_with_dealer( + max_signers, + min_signers, + frost::keys::IdentifierList::Default, + &mut rng, + ) + .unwrap(); + + // Try to refresh shares + // Signer 2 will be removed and Signers 1, 3, 4 & 5 will remain + + //////////////////////////////////////////////////////////////////////////// + // New Key generation + //////////////////////////////////////////////////////////////////////////// + + let remaining_ids = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + + const NEW_MAX_SIGNERS: u16 = 4; + + let (shares, new_pub_key_package) = refresh_shares_with_dealer( + old_shares, + pub_key_package, + NEW_MAX_SIGNERS, + min_signers, + &remaining_ids, + &mut rng, + ) + .unwrap(); + + let mut key_packages: BTreeMap, frost::keys::KeyPackage> = + BTreeMap::new(); + + for (k, v) in shares { + let key_package = frost::keys::KeyPackage::try_from(v).unwrap(); + key_packages.insert(k, key_package); + } + check_sign(min_signers, key_packages, rng, new_pub_key_package).unwrap(); +} + +/// Check refesh shares with dealer errors +pub fn check_refresh_shares_with_dealer_fails_with_invalid_signers< + C: Ciphersuite, + R: RngCore + CryptoRng, +>( + new_max_signers: u16, + min_signers: u16, + identifiers: &[Identifier], + error: Error, + mut rng: R, +) { + let (old_shares, pub_key_package) = build_old_shares::(5, 2, &mut rng); + let out = refresh_shares_with_dealer( + old_shares, + pub_key_package, + new_max_signers, + min_signers, + identifiers, + &mut rng, + ); + + assert!(out.is_err()); + assert!(out == Err(error)) +} + +fn build_old_shares( + max_signers: u16, + min_signers: u16, + mut rng: &mut R, +) -> (BTreeMap, SecretShare>, PublicKeyPackage) { + // Compute shares + + //////////////////////////////////////////////////////////////////////////// + // Key generation + //////////////////////////////////////////////////////////////////////////// + + let mut bytes = [0; 64]; + rng.fill_bytes(&mut bytes); + + let key = SigningKey::new(&mut rng); + + let (old_shares, pub_key_package): ( + BTreeMap, SecretShare>, + PublicKeyPackage, + ) = frost::keys::split( + &key, + max_signers, + min_signers, + frost::keys::IdentifierList::Default, + &mut rng, + ) + .unwrap(); + + // Try to refresh shares + // Signer 2 will be removed and Signers 1, 3, 4 & 5 will remain + + // Rerun key generation + + (old_shares, pub_key_package) +} diff --git a/frost-ed25519/src/keys/refresh.rs b/frost-ed25519/src/keys/refresh.rs new file mode 100644 index 00000000..da4b1593 --- /dev/null +++ b/frost-ed25519/src/keys/refresh.rs @@ -0,0 +1,30 @@ +//! Refresh Shares +//! +//! Implements the functionality to refresh a share. This requires the participation +//! of all the remaining signers. This can be done using a Trusted Dealer or +//! DKG (not yet implemented) + +use std::collections::BTreeMap; + +use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; + +use super::{PublicKeyPackage, SecretShare}; + +/// Refresh shares using a trusted dealer +pub fn refresh_shares_with_dealer( + current_shares: BTreeMap, + old_pub_key_package: PublicKeyPackage, + max_signers: u16, + min_signers: u16, + identifiers: &[Identifier], + mut rng: &mut R, +) -> Result<(BTreeMap, PublicKeyPackage), Error> { + frost::keys::refresh::refresh_shares_with_dealer( + current_shares, + old_pub_key_package, + max_signers, + min_signers, + identifiers, + &mut rng, + ) +} diff --git a/frost-ed25519/tests/integration_tests.rs b/frost-ed25519/tests/integration_tests.rs index 1421079a..36e7498d 100644 --- a/frost-ed25519/tests/integration_tests.rs +++ b/frost-ed25519/tests/integration_tests.rs @@ -64,6 +64,84 @@ fn check_rts() { frost_core::tests::repairable::check_rts::(rng); } +#[test] +fn check_refresh_shares_with_dealer() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 1; + let max_signers = 4; + let error = Error::InvalidMinSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ed25519Sha512, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 3; + let max_signers = 3; + let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ed25519Sha512, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 6; + let max_signers = 4; + let error: frost_core::Error = Error::InvalidMinSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ed25519Sha512, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { + let rng = thread_rng(); + let identifiers = vec![Identifier::try_from(1).unwrap()]; + let min_signers = 3; + let max_signers = 1; + let error = Error::InvalidMaxSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ed25519Sha512, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + #[test] fn check_sign_with_dealer() { let rng = thread_rng(); diff --git a/frost-ed448/src/keys/refresh.rs b/frost-ed448/src/keys/refresh.rs new file mode 100644 index 00000000..da4b1593 --- /dev/null +++ b/frost-ed448/src/keys/refresh.rs @@ -0,0 +1,30 @@ +//! Refresh Shares +//! +//! Implements the functionality to refresh a share. This requires the participation +//! of all the remaining signers. This can be done using a Trusted Dealer or +//! DKG (not yet implemented) + +use std::collections::BTreeMap; + +use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; + +use super::{PublicKeyPackage, SecretShare}; + +/// Refresh shares using a trusted dealer +pub fn refresh_shares_with_dealer( + current_shares: BTreeMap, + old_pub_key_package: PublicKeyPackage, + max_signers: u16, + min_signers: u16, + identifiers: &[Identifier], + mut rng: &mut R, +) -> Result<(BTreeMap, PublicKeyPackage), Error> { + frost::keys::refresh::refresh_shares_with_dealer( + current_shares, + old_pub_key_package, + max_signers, + min_signers, + identifiers, + &mut rng, + ) +} diff --git a/frost-ed448/tests/integration_tests.rs b/frost-ed448/tests/integration_tests.rs index 3409a7e0..1ad4f5fc 100644 --- a/frost-ed448/tests/integration_tests.rs +++ b/frost-ed448/tests/integration_tests.rs @@ -64,6 +64,84 @@ fn check_rts() { frost_core::tests::repairable::check_rts::(rng); } +#[test] +fn check_refresh_shares_with_dealer() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 1; + let max_signers = 4; + let error = Error::InvalidMinSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ed448Shake256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 3; + let max_signers = 3; + let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ed448Shake256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 6; + let max_signers = 4; + let error: frost_core::Error = Error::InvalidMinSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ed448Shake256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { + let rng = thread_rng(); + let identifiers = vec![Identifier::try_from(1).unwrap()]; + let min_signers = 3; + let max_signers = 1; + let error = Error::InvalidMaxSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ed448Shake256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + #[test] fn check_sign_with_dealer() { let rng = thread_rng(); diff --git a/frost-p256/src/keys/refresh.rs b/frost-p256/src/keys/refresh.rs new file mode 100644 index 00000000..da4b1593 --- /dev/null +++ b/frost-p256/src/keys/refresh.rs @@ -0,0 +1,30 @@ +//! Refresh Shares +//! +//! Implements the functionality to refresh a share. This requires the participation +//! of all the remaining signers. This can be done using a Trusted Dealer or +//! DKG (not yet implemented) + +use std::collections::BTreeMap; + +use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; + +use super::{PublicKeyPackage, SecretShare}; + +/// Refresh shares using a trusted dealer +pub fn refresh_shares_with_dealer( + current_shares: BTreeMap, + old_pub_key_package: PublicKeyPackage, + max_signers: u16, + min_signers: u16, + identifiers: &[Identifier], + mut rng: &mut R, +) -> Result<(BTreeMap, PublicKeyPackage), Error> { + frost::keys::refresh::refresh_shares_with_dealer( + current_shares, + old_pub_key_package, + max_signers, + min_signers, + identifiers, + &mut rng, + ) +} diff --git a/frost-p256/src/lib.rs b/frost-p256/src/lib.rs index 2c54b10f..13bb7703 100644 --- a/frost-p256/src/lib.rs +++ b/frost-p256/src/lib.rs @@ -343,6 +343,7 @@ pub mod keys { pub type VerifiableSecretSharingCommitment = frost::keys::VerifiableSecretSharingCommitment

; pub mod dkg; + pub mod refresh; pub mod repairable; } diff --git a/frost-p256/tests/integration_tests.rs b/frost-p256/tests/integration_tests.rs index f2573353..31adb26d 100644 --- a/frost-p256/tests/integration_tests.rs +++ b/frost-p256/tests/integration_tests.rs @@ -64,6 +64,84 @@ fn check_rts() { frost_core::tests::repairable::check_rts::(rng); } +#[test] +fn check_refresh_shares_with_dealer() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 1; + let max_signers = 4; + let error = Error::InvalidMinSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + P256Sha256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 3; + let max_signers = 3; + let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + P256Sha256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 6; + let max_signers = 4; + let error: frost_core::Error = Error::InvalidMinSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + P256Sha256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { + let rng = thread_rng(); + let identifiers = vec![Identifier::try_from(1).unwrap()]; + let min_signers = 3; + let max_signers = 1; + let error = Error::InvalidMaxSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + P256Sha256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + #[test] fn check_sign_with_dealer() { let rng = thread_rng(); diff --git a/frost-ristretto255/src/keys/refresh.rs b/frost-ristretto255/src/keys/refresh.rs new file mode 100644 index 00000000..da4b1593 --- /dev/null +++ b/frost-ristretto255/src/keys/refresh.rs @@ -0,0 +1,30 @@ +//! Refresh Shares +//! +//! Implements the functionality to refresh a share. This requires the participation +//! of all the remaining signers. This can be done using a Trusted Dealer or +//! DKG (not yet implemented) + +use std::collections::BTreeMap; + +use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; + +use super::{PublicKeyPackage, SecretShare}; + +/// Refresh shares using a trusted dealer +pub fn refresh_shares_with_dealer( + current_shares: BTreeMap, + old_pub_key_package: PublicKeyPackage, + max_signers: u16, + min_signers: u16, + identifiers: &[Identifier], + mut rng: &mut R, +) -> Result<(BTreeMap, PublicKeyPackage), Error> { + frost::keys::refresh::refresh_shares_with_dealer( + current_shares, + old_pub_key_package, + max_signers, + min_signers, + identifiers, + &mut rng, + ) +} diff --git a/frost-ristretto255/src/lib.rs b/frost-ristretto255/src/lib.rs index f6a608c4..2ee67d9d 100644 --- a/frost-ristretto255/src/lib.rs +++ b/frost-ristretto255/src/lib.rs @@ -312,6 +312,7 @@ pub mod keys { pub type VerifiableSecretSharingCommitment = frost::keys::VerifiableSecretSharingCommitment; pub mod dkg; + pub mod refresh; pub mod repairable; } diff --git a/frost-ristretto255/tests/integration_tests.rs b/frost-ristretto255/tests/integration_tests.rs index bacc9fb4..bcc0fe2b 100644 --- a/frost-ristretto255/tests/integration_tests.rs +++ b/frost-ristretto255/tests/integration_tests.rs @@ -64,6 +64,84 @@ fn check_rts() { frost_core::tests::repairable::check_rts::(rng); } +#[test] +fn check_refresh_shares_with_dealer() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 1; + let max_signers = 4; + let error = Error::InvalidMinSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ristretto255Sha512, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 3; + let max_signers = 3; + let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ristretto255Sha512, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 6; + let max_signers = 4; + let error: frost_core::Error = Error::InvalidMinSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ristretto255Sha512, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { + let rng = thread_rng(); + let identifiers = vec![Identifier::try_from(1).unwrap()]; + let min_signers = 3; + let max_signers = 1; + let error = Error::InvalidMaxSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ristretto255Sha512, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + #[test] fn check_sign_with_dealer() { let rng = thread_rng(); diff --git a/frost-secp256k1/src/keys/refresh.rs b/frost-secp256k1/src/keys/refresh.rs new file mode 100644 index 00000000..da4b1593 --- /dev/null +++ b/frost-secp256k1/src/keys/refresh.rs @@ -0,0 +1,30 @@ +//! Refresh Shares +//! +//! Implements the functionality to refresh a share. This requires the participation +//! of all the remaining signers. This can be done using a Trusted Dealer or +//! DKG (not yet implemented) + +use std::collections::BTreeMap; + +use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; + +use super::{PublicKeyPackage, SecretShare}; + +/// Refresh shares using a trusted dealer +pub fn refresh_shares_with_dealer( + current_shares: BTreeMap, + old_pub_key_package: PublicKeyPackage, + max_signers: u16, + min_signers: u16, + identifiers: &[Identifier], + mut rng: &mut R, +) -> Result<(BTreeMap, PublicKeyPackage), Error> { + frost::keys::refresh::refresh_shares_with_dealer( + current_shares, + old_pub_key_package, + max_signers, + min_signers, + identifiers, + &mut rng, + ) +} diff --git a/frost-secp256k1/tests/integration_tests.rs b/frost-secp256k1/tests/integration_tests.rs index 58ba3e08..3c988fce 100644 --- a/frost-secp256k1/tests/integration_tests.rs +++ b/frost-secp256k1/tests/integration_tests.rs @@ -64,6 +64,84 @@ fn check_rts() { frost_core::tests::repairable::check_rts::(rng); } +#[test] +fn check_refresh_shares_with_dealer() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 1; + let max_signers = 4; + let error = Error::InvalidMinSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_unequal_num_identifiers_and_max_signers() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 3; + let max_signers = 3; + let error: frost_core::Error = Error::IncorrectNumberOfIdentifiers; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_min_signers_greater_than_max() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + let min_signers = 6; + let max_signers = 4; + let error: frost_core::Error = Error::InvalidMinSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { + let rng = thread_rng(); + let identifiers = vec![Identifier::try_from(1).unwrap()]; + let min_signers = 3; + let max_signers = 1; + let error = Error::InvalidMaxSigners; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + #[test] fn check_sign_with_dealer() { let rng = thread_rng(); diff --git a/gencode/src/main.rs b/gencode/src/main.rs index 0806901e..eda52d4e 100644 --- a/gencode/src/main.rs +++ b/gencode/src/main.rs @@ -321,6 +321,7 @@ fn main() -> ExitCode { "README.md", "dkg.md", "src/keys/dkg.rs", + "src/keys/refresh.rs", "src/keys/repairable.rs", "src/tests/batch.rs", "src/tests/coefficient_commitment.rs", From dcbadd61f553cfafc8dba6715b748e206eb52485 Mon Sep 17 00:00:00 2001 From: natalie Date: Tue, 11 Jun 2024 14:42:30 +0100 Subject: [PATCH 02/19] Change refresh share API (#245) Split refresh_shares_with_dealer into calculate_zero_key and refresh_share --- frost-core/src/keys/refresh.rs | 33 ++++++++------------------ frost-core/src/tests/refresh.rs | 30 ++++++++++++++++------- frost-ed25519/src/keys/refresh.rs | 20 +++++++++------- frost-ed448/src/keys/refresh.rs | 20 +++++++++------- frost-p256/src/keys/refresh.rs | 20 +++++++++------- frost-ristretto255/src/keys/refresh.rs | 20 +++++++++------- frost-secp256k1/src/keys/refresh.rs | 20 +++++++++------- 7 files changed, 92 insertions(+), 71 deletions(-) diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index f292eb78..fd9e5403 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -17,14 +17,13 @@ use crate::{ use super::{SecretShare, VerifiableSecretSharingCommitment}; /// Refreshes shares using a trusted dealer -pub fn refresh_shares_with_dealer( - old_shares: BTreeMap, SecretShare>, +pub fn calculate_zero_key( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], rng: &mut R, -) -> Result<(BTreeMap, SecretShare>, PublicKeyPackage), Error> { +) -> Result<(Vec>, PublicKeyPackage), Error> { // Validate inputs if identifiers.len() != max_signers as usize { @@ -41,7 +40,7 @@ pub fn refresh_shares_with_dealer( let coefficients = generate_coefficients::(min_signers as usize - 1, rng); - let zero_key_shares = generate_secret_shares( + let zero_shares = generate_secret_shares( &zero_key, max_signers, min_signers, @@ -49,24 +48,11 @@ pub fn refresh_shares_with_dealer( identifiers, )?; - // Build new shares and public key package - - let mut new_shares: BTreeMap, SecretShare> = BTreeMap::new(); let mut verifying_shares: BTreeMap, VerifyingShare> = BTreeMap::new(); - for share in zero_key_shares { + for share in zero_shares.clone() { let signer_public = SigningShare::into(share.signing_share); verifying_shares.insert(share.identifier, signer_public); - - let old_share = old_shares.get(&share.identifier); - - match old_share { - Some(old_share) => new_shares.insert( - share.identifier, - add_secret_shares::(share.clone(), old_share)?, - ), - None => return Err(Error::UnknownIdentifier), - }; } let pub_key_package = PublicKeyPackage:: { @@ -75,18 +61,19 @@ pub fn refresh_shares_with_dealer( verifying_key: old_pub_key_package.verifying_key, }; - Ok((new_shares, pub_key_package)) + Ok((zero_shares, pub_key_package)) } -fn add_secret_shares( +/// Each participant refreshed their shares +pub fn refresh_share( zero_share: SecretShare, - old_share: &SecretShare, + current_share: &SecretShare, ) -> Result, Error> { let signing_share: Scalar = - zero_share.signing_share.to_scalar() + old_share.signing_share.to_scalar(); + zero_share.signing_share.to_scalar() + current_share.signing_share.to_scalar(); let zero_commitments = zero_share.commitment.0; - let old_commitments = old_share.commitment.0.clone(); + let old_commitments = current_share.commitment.0.clone(); let mut commitments: Vec> = Vec::with_capacity(zero_commitments.len()); diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index 5127cfe1..e81e8659 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -4,9 +4,10 @@ use std::collections::BTreeMap; use rand_core::{CryptoRng, RngCore}; +use crate::keys::refresh::{calculate_zero_key, refresh_share}; use crate::{self as frost}; use crate::{ - keys::{refresh::refresh_shares_with_dealer, PublicKeyPackage, SecretShare}, + keys::{PublicKeyPackage, SecretShare}, Ciphersuite, Error, Identifier, SigningKey, }; @@ -46,8 +47,9 @@ pub fn check_refresh_shares_with_dealer( const NEW_MAX_SIGNERS: u16 = 4; - let (shares, new_pub_key_package) = refresh_shares_with_dealer( - old_shares, + // Trusted Dealer generates zero keys + + let (zero_shares, new_pub_key_package) = calculate_zero_key( pub_key_package, NEW_MAX_SIGNERS, min_signers, @@ -56,11 +58,24 @@ pub fn check_refresh_shares_with_dealer( ) .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_shares[&identifier]; + new_shares.insert( + identifier, + refresh_share(zero_shares[i].clone(), current_share), + ); + } + let mut key_packages: BTreeMap, frost::keys::KeyPackage> = BTreeMap::new(); - for (k, v) in shares { - let key_package = frost::keys::KeyPackage::try_from(v).unwrap(); + for (k, v) in new_shares { + let key_package = frost::keys::KeyPackage::try_from(v.unwrap()).unwrap(); key_packages.insert(k, key_package); } check_sign(min_signers, key_packages, rng, new_pub_key_package).unwrap(); @@ -77,9 +92,8 @@ pub fn check_refresh_shares_with_dealer_fails_with_invalid_signers< error: Error, mut rng: R, ) { - let (old_shares, pub_key_package) = build_old_shares::(5, 2, &mut rng); - let out = refresh_shares_with_dealer( - old_shares, + let (_old_shares, pub_key_package) = build_old_shares::(5, 2, &mut rng); + let out = calculate_zero_key( pub_key_package, new_max_signers, min_signers, diff --git a/frost-ed25519/src/keys/refresh.rs b/frost-ed25519/src/keys/refresh.rs index da4b1593..4702c9af 100644 --- a/frost-ed25519/src/keys/refresh.rs +++ b/frost-ed25519/src/keys/refresh.rs @@ -4,23 +4,19 @@ //! of all the remaining signers. This can be done using a Trusted Dealer or //! DKG (not yet implemented) -use std::collections::BTreeMap; - use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; use super::{PublicKeyPackage, SecretShare}; -/// Refresh shares using a trusted dealer -pub fn refresh_shares_with_dealer( - current_shares: BTreeMap, +/// Refreshes shares using a trusted dealer +pub fn calculate_zero_key( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, -) -> Result<(BTreeMap, PublicKeyPackage), Error> { - frost::keys::refresh::refresh_shares_with_dealer( - current_shares, +) -> Result<(Vec, PublicKeyPackage), Error> { + frost::keys::refresh::calculate_zero_key( old_pub_key_package, max_signers, min_signers, @@ -28,3 +24,11 @@ pub fn refresh_shares_with_dealer( &mut rng, ) } + +/// Each participant refreshed their shares +pub fn refresh_share( + zero_share: SecretShare, + current_share: &SecretShare, +) -> Result { + frost::keys::refresh::refresh_share(zero_share, current_share) +} diff --git a/frost-ed448/src/keys/refresh.rs b/frost-ed448/src/keys/refresh.rs index da4b1593..4702c9af 100644 --- a/frost-ed448/src/keys/refresh.rs +++ b/frost-ed448/src/keys/refresh.rs @@ -4,23 +4,19 @@ //! of all the remaining signers. This can be done using a Trusted Dealer or //! DKG (not yet implemented) -use std::collections::BTreeMap; - use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; use super::{PublicKeyPackage, SecretShare}; -/// Refresh shares using a trusted dealer -pub fn refresh_shares_with_dealer( - current_shares: BTreeMap, +/// Refreshes shares using a trusted dealer +pub fn calculate_zero_key( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, -) -> Result<(BTreeMap, PublicKeyPackage), Error> { - frost::keys::refresh::refresh_shares_with_dealer( - current_shares, +) -> Result<(Vec, PublicKeyPackage), Error> { + frost::keys::refresh::calculate_zero_key( old_pub_key_package, max_signers, min_signers, @@ -28,3 +24,11 @@ pub fn refresh_shares_with_dealer( &mut rng, ) } + +/// Each participant refreshed their shares +pub fn refresh_share( + zero_share: SecretShare, + current_share: &SecretShare, +) -> Result { + frost::keys::refresh::refresh_share(zero_share, current_share) +} diff --git a/frost-p256/src/keys/refresh.rs b/frost-p256/src/keys/refresh.rs index da4b1593..4702c9af 100644 --- a/frost-p256/src/keys/refresh.rs +++ b/frost-p256/src/keys/refresh.rs @@ -4,23 +4,19 @@ //! of all the remaining signers. This can be done using a Trusted Dealer or //! DKG (not yet implemented) -use std::collections::BTreeMap; - use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; use super::{PublicKeyPackage, SecretShare}; -/// Refresh shares using a trusted dealer -pub fn refresh_shares_with_dealer( - current_shares: BTreeMap, +/// Refreshes shares using a trusted dealer +pub fn calculate_zero_key( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, -) -> Result<(BTreeMap, PublicKeyPackage), Error> { - frost::keys::refresh::refresh_shares_with_dealer( - current_shares, +) -> Result<(Vec, PublicKeyPackage), Error> { + frost::keys::refresh::calculate_zero_key( old_pub_key_package, max_signers, min_signers, @@ -28,3 +24,11 @@ pub fn refresh_shares_with_dealer( &mut rng, ) } + +/// Each participant refreshed their shares +pub fn refresh_share( + zero_share: SecretShare, + current_share: &SecretShare, +) -> Result { + frost::keys::refresh::refresh_share(zero_share, current_share) +} diff --git a/frost-ristretto255/src/keys/refresh.rs b/frost-ristretto255/src/keys/refresh.rs index da4b1593..4702c9af 100644 --- a/frost-ristretto255/src/keys/refresh.rs +++ b/frost-ristretto255/src/keys/refresh.rs @@ -4,23 +4,19 @@ //! of all the remaining signers. This can be done using a Trusted Dealer or //! DKG (not yet implemented) -use std::collections::BTreeMap; - use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; use super::{PublicKeyPackage, SecretShare}; -/// Refresh shares using a trusted dealer -pub fn refresh_shares_with_dealer( - current_shares: BTreeMap, +/// Refreshes shares using a trusted dealer +pub fn calculate_zero_key( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, -) -> Result<(BTreeMap, PublicKeyPackage), Error> { - frost::keys::refresh::refresh_shares_with_dealer( - current_shares, +) -> Result<(Vec, PublicKeyPackage), Error> { + frost::keys::refresh::calculate_zero_key( old_pub_key_package, max_signers, min_signers, @@ -28,3 +24,11 @@ pub fn refresh_shares_with_dealer( &mut rng, ) } + +/// Each participant refreshed their shares +pub fn refresh_share( + zero_share: SecretShare, + current_share: &SecretShare, +) -> Result { + frost::keys::refresh::refresh_share(zero_share, current_share) +} diff --git a/frost-secp256k1/src/keys/refresh.rs b/frost-secp256k1/src/keys/refresh.rs index da4b1593..4702c9af 100644 --- a/frost-secp256k1/src/keys/refresh.rs +++ b/frost-secp256k1/src/keys/refresh.rs @@ -4,23 +4,19 @@ //! of all the remaining signers. This can be done using a Trusted Dealer or //! DKG (not yet implemented) -use std::collections::BTreeMap; - use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; use super::{PublicKeyPackage, SecretShare}; -/// Refresh shares using a trusted dealer -pub fn refresh_shares_with_dealer( - current_shares: BTreeMap, +/// Refreshes shares using a trusted dealer +pub fn calculate_zero_key( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, -) -> Result<(BTreeMap, PublicKeyPackage), Error> { - frost::keys::refresh::refresh_shares_with_dealer( - current_shares, +) -> Result<(Vec, PublicKeyPackage), Error> { + frost::keys::refresh::calculate_zero_key( old_pub_key_package, max_signers, min_signers, @@ -28,3 +24,11 @@ pub fn refresh_shares_with_dealer( &mut rng, ) } + +/// Each participant refreshed their shares +pub fn refresh_share( + zero_share: SecretShare, + current_share: &SecretShare, +) -> Result { + frost::keys::refresh::refresh_share(zero_share, current_share) +} From 2a7242734bbedb58c60fdfa53fa553fb22856ae3 Mon Sep 17 00:00:00 2001 From: natalie Date: Thu, 27 Jun 2024 11:36:42 +0100 Subject: [PATCH 03/19] Fix serialisation error with refresh share (#245) Add serialisation test --- frost-core/src/keys/refresh.rs | 33 ++++++-- frost-core/src/tests/refresh.rs | 75 ++++++++++++++++++- frost-ed25519/tests/integration_tests.rs | 9 +++ frost-ed448/tests/integration_tests.rs | 9 +++ frost-p256/tests/integration_tests.rs | 9 +++ frost-ristretto255/tests/integration_tests.rs | 10 +++ frost-secp256k1/tests/integration_tests.rs | 9 +++ 7 files changed, 146 insertions(+), 8 deletions(-) diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index fd9e5403..a4e1f838 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -49,10 +49,19 @@ pub fn calculate_zero_key( )?; let mut verifying_shares: BTreeMap, VerifyingShare> = BTreeMap::new(); + let mut zero_shares_minus_identity: Vec> = Vec::new(); for share in zero_shares.clone() { let signer_public = SigningShare::into(share.signing_share); verifying_shares.insert(share.identifier, signer_public); + let mut coefficients = share.commitment.0; + coefficients.remove(0); + zero_shares_minus_identity.push(SecretShare { + header: share.header, + identifier: share.identifier, + signing_share: share.signing_share, + commitment: VerifiableSecretSharingCommitment::new(coefficients), + }); } let pub_key_package = PublicKeyPackage:: { @@ -61,10 +70,11 @@ pub fn calculate_zero_key( verifying_key: old_pub_key_package.verifying_key, }; - Ok((zero_shares, pub_key_package)) + Ok((zero_shares_minus_identity, pub_key_package)) } -/// Each participant refreshed their shares +/// Each participant refreshes their shares +/// This is done by taking the `zero_share` received from the trusted dealer and adding it to the original share pub fn refresh_share( zero_share: SecretShare, current_share: &SecretShare, @@ -72,15 +82,24 @@ pub fn refresh_share( let signing_share: Scalar = zero_share.signing_share.to_scalar() + current_share.signing_share.to_scalar(); - let zero_commitments = zero_share.commitment.0; + // The identity commitment needs to be added to the VSS commitment + let identity_commitment: Vec> = + vec![(CoefficientCommitment(::identity()))]; + + let zero_commitments_without_id = zero_share.commitment.0; let old_commitments = current_share.commitment.0.clone(); - let mut commitments: Vec> = Vec::with_capacity(zero_commitments.len()); + let zero_commitment: Vec> = identity_commitment + .into_iter() + .chain(zero_commitments_without_id.clone()) + .collect(); + + let mut commitments: Vec> = Vec::with_capacity(zero_commitment.len()); - if old_commitments.len() >= zero_commitments.len() { - for i in 0..zero_commitments.len() { + if old_commitments.len() >= zero_commitment.len() { + for i in 0..zero_commitment.len() { if let (Some(zero_commitment), Some(old_commitment)) = - (zero_commitments.get(i), old_commitments.get(i)) + (zero_commitment.get(i), old_commitments.get(i)) { commitments.push(CoefficientCommitment::new( zero_commitment.0 + old_commitment.0, diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index e81e8659..bd55a617 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -7,7 +7,7 @@ use rand_core::{CryptoRng, RngCore}; use crate::keys::refresh::{calculate_zero_key, refresh_share}; use crate::{self as frost}; use crate::{ - keys::{PublicKeyPackage, SecretShare}, + keys::{KeyPackage, PublicKeyPackage, SecretShare}, Ciphersuite, Error, Identifier, SigningKey, }; @@ -140,3 +140,76 @@ fn build_old_shares( (old_shares, pub_key_package) } + +/// Check serialisation +pub fn check_refresh_shares_with_dealer_serialisation( + mut rng: R, +) { + // Compute shares + + //////////////////////////////////////////////////////////////////////////// + // Old Key generation + //////////////////////////////////////////////////////////////////////////// + + let max_signers = 5; + let min_signers = 3; + let (_old_shares, pub_key_package) = frost::keys::generate_with_dealer( + max_signers, + min_signers, + frost::keys::IdentifierList::Default, + &mut rng, + ) + .unwrap(); + + //////////////////////////////////////////////////////////////////////////// + // New Key generation + // + // Zero key is calculated by trusted dealer + // Signer 2 will be removed and Signers 1, 3, 4 & 5 will remain + //////////////////////////////////////////////////////////////////////////// + + let remaining_ids = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + + const NEW_MAX_SIGNERS: u16 = 4; + + let (zero_shares, new_pub_key_package) = calculate_zero_key( + pub_key_package, + NEW_MAX_SIGNERS, + min_signers, + &remaining_ids, + &mut rng, + ) + .unwrap(); + + // Trusted dealer serialises zero shares and key package + + let zero_shares_serialised = SecretShare::::serialize(&zero_shares[0]); + + assert!(zero_shares_serialised.is_ok()); + + let new_pub_key_package_serialised = PublicKeyPackage::::serialize(&new_pub_key_package); + + assert!(new_pub_key_package_serialised.is_ok()); + + // Participant 1 deserialises zero share and key package + + let zero_share = SecretShare::::deserialize(&zero_shares_serialised.unwrap()); + + assert!(zero_share.is_ok()); + + let new_pub_key_package = + PublicKeyPackage::::deserialize(&new_pub_key_package_serialised.unwrap()); + + assert!(new_pub_key_package.is_ok()); + + // Participant 1 checks Key Package can be created from Secret Share + + let key_package = KeyPackage::::try_from(zero_share.unwrap()); + + assert!(key_package.is_ok()); +} diff --git a/frost-ed25519/tests/integration_tests.rs b/frost-ed25519/tests/integration_tests.rs index 36e7498d..3330d982 100644 --- a/frost-ed25519/tests/integration_tests.rs +++ b/frost-ed25519/tests/integration_tests.rs @@ -71,6 +71,15 @@ fn check_refresh_shares_with_dealer() { frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); } +#[test] +fn check_refresh_shares_with_dealer_serialisation() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::( + rng, + ); +} + #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { let rng = thread_rng(); diff --git a/frost-ed448/tests/integration_tests.rs b/frost-ed448/tests/integration_tests.rs index 1ad4f5fc..766f65fc 100644 --- a/frost-ed448/tests/integration_tests.rs +++ b/frost-ed448/tests/integration_tests.rs @@ -71,6 +71,15 @@ fn check_refresh_shares_with_dealer() { frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); } +#[test] +fn check_refresh_shares_with_dealer_serialisation() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::( + rng, + ); +} + #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { let rng = thread_rng(); diff --git a/frost-p256/tests/integration_tests.rs b/frost-p256/tests/integration_tests.rs index 31adb26d..4015e780 100644 --- a/frost-p256/tests/integration_tests.rs +++ b/frost-p256/tests/integration_tests.rs @@ -71,6 +71,15 @@ fn check_refresh_shares_with_dealer() { frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); } +#[test] +fn check_refresh_shares_with_dealer_serialisation() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::( + rng, + ); +} + #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { let rng = thread_rng(); diff --git a/frost-ristretto255/tests/integration_tests.rs b/frost-ristretto255/tests/integration_tests.rs index bcc0fe2b..ecb0d2d7 100644 --- a/frost-ristretto255/tests/integration_tests.rs +++ b/frost-ristretto255/tests/integration_tests.rs @@ -71,6 +71,16 @@ fn check_refresh_shares_with_dealer() { frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); } +#[test] +fn check_refresh_shares_with_dealer_serialisation() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::< + Ristretto255Sha512, + _, + >(rng); +} + #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { let rng = thread_rng(); diff --git a/frost-secp256k1/tests/integration_tests.rs b/frost-secp256k1/tests/integration_tests.rs index 3c988fce..3a6b1767 100644 --- a/frost-secp256k1/tests/integration_tests.rs +++ b/frost-secp256k1/tests/integration_tests.rs @@ -71,6 +71,15 @@ fn check_refresh_shares_with_dealer() { frost_core::tests::refresh::check_refresh_shares_with_dealer::(rng); } +#[test] +fn check_refresh_shares_with_dealer_serialisation() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer_serialisation::( + rng, + ); +} + #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { let rng = thread_rng(); From f2e6d3207aa5ab15deb0cb892ae2233886900cfe Mon Sep 17 00:00:00 2001 From: natalie Date: Thu, 27 Jun 2024 11:58:33 +0100 Subject: [PATCH 04/19] Fix serialisation errors after updates (#245) Fixed some typos --- frost-core/src/keys/refresh.rs | 4 ++-- frost-core/src/serialization.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index a4e1f838..bcfce275 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -84,7 +84,7 @@ pub fn refresh_share( // The identity commitment needs to be added to the VSS commitment let identity_commitment: Vec> = - vec![(CoefficientCommitment(::identity()))]; + vec![CoefficientCommitment::new(C::Group::identity())]; let zero_commitments_without_id = zero_share.commitment.0; let old_commitments = current_share.commitment.0.clone(); @@ -102,7 +102,7 @@ pub fn refresh_share( (zero_commitment.get(i), old_commitments.get(i)) { commitments.push(CoefficientCommitment::new( - zero_commitment.0 + old_commitment.0, + zero_commitment.0 .0 + old_commitment.0 .0, )); } else { return Err(Error::IncorrectNumberOfCommitments); diff --git a/frost-core/src/serialization.rs b/frost-core/src/serialization.rs index a442e58b..fe1df7b8 100644 --- a/frost-core/src/serialization.rs +++ b/frost-core/src/serialization.rs @@ -83,12 +83,12 @@ impl SerializableElement where C: Ciphersuite, { - /// Serialize an Element. Returns and error if it's the identity. + /// Serialize an Element. Returns an error if it's the identity. pub fn serialize(&self) -> Result, Error> { Ok(::serialize(&self.0)?.as_ref().to_vec()) } - /// Deserialized an Element. Returns an error if it's malformed or is the + /// Deserialize an Element. Returns an error if it's malformed or is the /// identity. pub fn deserialize(bytes: &[u8]) -> Result> { let serialized: ::Serialization = bytes From a640cfd28a1cffcdea3847a901a485594e327487 Mon Sep 17 00:00:00 2001 From: natalie Date: Thu, 27 Jun 2024 14:56:41 +0100 Subject: [PATCH 05/19] Update refresh_share to accept and return a KeyPackage instead of SecretShare (#245) --- frost-core/src/keys/refresh.rs | 58 ++++++++--------- frost-core/src/tests/refresh.rs | 88 ++++++++++++++------------ frost-ed25519/src/keys/refresh.rs | 6 +- frost-ed448/src/keys/refresh.rs | 6 +- frost-p256/src/keys/refresh.rs | 6 +- frost-ristretto255/src/keys/refresh.rs | 6 +- frost-secp256k1/src/keys/refresh.rs | 6 +- 7 files changed, 89 insertions(+), 87 deletions(-) diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index bcfce275..5a32005f 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -9,12 +9,12 @@ use std::collections::BTreeMap; use crate::{ keys::{ generate_coefficients, generate_secret_shares, validate_num_of_signers, - CoefficientCommitment, PublicKeyPackage, SigningKey, SigningShare, VerifyingShare, + CoefficientCommitment, PublicKeyPackage, SigningKey, SigningShare, VerifyingShare }, Ciphersuite, CryptoRng, Error, Field, Group, Identifier, RngCore, Scalar, }; -use super::{SecretShare, VerifiableSecretSharingCommitment}; +use super::{KeyPackage, SecretShare, VerifiableSecretSharingCommitment}; /// Refreshes shares using a trusted dealer pub fn calculate_zero_key( @@ -77,49 +77,43 @@ pub fn calculate_zero_key( /// This is done by taking the `zero_share` received from the trusted dealer and adding it to the original share pub fn refresh_share( zero_share: SecretShare, - current_share: &SecretShare, -) -> Result, Error> { - let signing_share: Scalar = - zero_share.signing_share.to_scalar() + current_share.signing_share.to_scalar(); + current_key_package: &KeyPackage, +) -> Result, Error> { // The identity commitment needs to be added to the VSS commitment let identity_commitment: Vec> = - vec![CoefficientCommitment::new(C::Group::identity())]; + vec![CoefficientCommitment::new(C::Group::identity())]; let zero_commitments_without_id = zero_share.commitment.0; - let old_commitments = current_share.commitment.0.clone(); let zero_commitment: Vec> = identity_commitment .into_iter() .chain(zero_commitments_without_id.clone()) .collect(); - let mut commitments: Vec> = Vec::with_capacity(zero_commitment.len()); - - if old_commitments.len() >= zero_commitment.len() { - for i in 0..zero_commitment.len() { - if let (Some(zero_commitment), Some(old_commitment)) = - (zero_commitment.get(i), old_commitments.get(i)) - { - commitments.push(CoefficientCommitment::new( - zero_commitment.0 .0 + old_commitment.0 .0, - )); - } else { - return Err(Error::IncorrectNumberOfCommitments); - } - } - } else { - return Err(Error::MissingCommitment); - } + let zero_share = SecretShare{ + header: zero_share.header, + identifier: zero_share.identifier, + signing_share: zero_share.signing_share, + commitment: VerifiableSecretSharingCommitment::::new(zero_commitment) + }; - let commitment = VerifiableSecretSharingCommitment::new(commitments); + // verify zero_share secret share + let zero_key_package = KeyPackage::::try_from(zero_share).unwrap(); // TODO - let signing_share = SigningShare::new(signing_share); - Ok(SecretShare { - header: zero_share.header, - identifier: zero_share.identifier, - signing_share, - commitment, + + let signing_share: Scalar = + zero_key_package.signing_share.to_scalar() + current_key_package.signing_share.to_scalar(); + + // let key_package = {current_key_package, signing_share}; // TODO + + Ok(KeyPackage { + verifying_share: current_key_package.verifying_share, + verifying_key: current_key_package.verifying_key, + min_signers: current_key_package.min_signers, + header: current_key_package.header, + identifier: current_key_package.identifier, + signing_share: SigningShare::new(signing_share), }) } diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index bd55a617..37a7ea0f 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -4,11 +4,12 @@ use std::collections::BTreeMap; use rand_core::{CryptoRng, RngCore}; +use crate::keys::generate_with_dealer; use crate::keys::refresh::{calculate_zero_key, refresh_share}; use crate::{self as frost}; use crate::{ keys::{KeyPackage, PublicKeyPackage, SecretShare}, - Ciphersuite, Error, Identifier, SigningKey, + Ciphersuite, Error, Identifier, }; use super::ciphersuite_generic::check_sign; @@ -31,13 +32,20 @@ pub fn check_refresh_shares_with_dealer( ) .unwrap(); - // Try to refresh shares - // Signer 2 will be removed and Signers 1, 3, 4 & 5 will remain + let mut old_key_packages: BTreeMap, frost::keys::KeyPackage> = + BTreeMap::new(); + + for (k, v) in old_shares { + let key_package = frost::keys::KeyPackage::try_from(v).unwrap(); + old_key_packages.insert(k, key_package); + } //////////////////////////////////////////////////////////////////////////// // New Key generation //////////////////////////////////////////////////////////////////////////// + // Signer 2 will be removed and Signers 1, 3, 4 & 5 will remain + let remaining_ids = vec![ Identifier::try_from(1).unwrap(), Identifier::try_from(3).unwrap(), @@ -64,7 +72,7 @@ pub fn check_refresh_shares_with_dealer( for i in 0..remaining_ids.len() { let identifier = remaining_ids[i]; - let current_share = &old_shares[&identifier]; + let current_share = &old_key_packages[&identifier]; new_shares.insert( identifier, refresh_share(zero_shares[i].clone(), current_share), @@ -92,7 +100,7 @@ pub fn check_refresh_shares_with_dealer_fails_with_invalid_signers< error: Error, mut rng: R, ) { - let (_old_shares, pub_key_package) = build_old_shares::(5, 2, &mut rng); + let (_old_shares, pub_key_package) = generate_with_dealer::(5, 2, frost::keys::IdentifierList::Default, &mut rng).unwrap(); let out = calculate_zero_key( pub_key_package, new_max_signers, @@ -105,41 +113,41 @@ pub fn check_refresh_shares_with_dealer_fails_with_invalid_signers< assert!(out == Err(error)) } -fn build_old_shares( - max_signers: u16, - min_signers: u16, - mut rng: &mut R, -) -> (BTreeMap, SecretShare>, PublicKeyPackage) { - // Compute shares - - //////////////////////////////////////////////////////////////////////////// - // Key generation - //////////////////////////////////////////////////////////////////////////// - - let mut bytes = [0; 64]; - rng.fill_bytes(&mut bytes); - - let key = SigningKey::new(&mut rng); - - let (old_shares, pub_key_package): ( - BTreeMap, SecretShare>, - PublicKeyPackage, - ) = frost::keys::split( - &key, - max_signers, - min_signers, - frost::keys::IdentifierList::Default, - &mut rng, - ) - .unwrap(); - - // Try to refresh shares - // Signer 2 will be removed and Signers 1, 3, 4 & 5 will remain - - // Rerun key generation - - (old_shares, pub_key_package) -} +// fn build_old_shares( +// max_signers: u16, +// min_signers: u16, +// mut rng: &mut R, +// ) -> (BTreeMap, SecretShare>, PublicKeyPackage) { +// // Compute shares + +// //////////////////////////////////////////////////////////////////////////// +// // Key generation +// //////////////////////////////////////////////////////////////////////////// + +// let mut bytes = [0; 64]; +// rng.fill_bytes(&mut bytes); + +// let key = SigningKey::new(&mut rng); + +// let (old_shares, pub_key_package): ( +// BTreeMap, SecretShare>, +// PublicKeyPackage, +// ) = frost::keys::split( +// &key, +// max_signers, +// min_signers, +// frost::keys::IdentifierList::Default, +// &mut rng, +// ) +// .unwrap(); + +// // Try to refresh shares +// // Signer 2 will be removed and Signers 1, 3, 4 & 5 will remain + +// // Rerun key generation + +// (old_shares, pub_key_package) +// } /// Check serialisation pub fn check_refresh_shares_with_dealer_serialisation( diff --git a/frost-ed25519/src/keys/refresh.rs b/frost-ed25519/src/keys/refresh.rs index 4702c9af..303c2580 100644 --- a/frost-ed25519/src/keys/refresh.rs +++ b/frost-ed25519/src/keys/refresh.rs @@ -6,7 +6,7 @@ use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; -use super::{PublicKeyPackage, SecretShare}; +use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refreshes shares using a trusted dealer pub fn calculate_zero_key( @@ -28,7 +28,7 @@ pub fn calculate_zero_key( /// Each participant refreshed their shares pub fn refresh_share( zero_share: SecretShare, - current_share: &SecretShare, -) -> Result { + current_share: &KeyPackage, +) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } diff --git a/frost-ed448/src/keys/refresh.rs b/frost-ed448/src/keys/refresh.rs index 4702c9af..303c2580 100644 --- a/frost-ed448/src/keys/refresh.rs +++ b/frost-ed448/src/keys/refresh.rs @@ -6,7 +6,7 @@ use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; -use super::{PublicKeyPackage, SecretShare}; +use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refreshes shares using a trusted dealer pub fn calculate_zero_key( @@ -28,7 +28,7 @@ pub fn calculate_zero_key( /// Each participant refreshed their shares pub fn refresh_share( zero_share: SecretShare, - current_share: &SecretShare, -) -> Result { + current_share: &KeyPackage, +) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } diff --git a/frost-p256/src/keys/refresh.rs b/frost-p256/src/keys/refresh.rs index 4702c9af..303c2580 100644 --- a/frost-p256/src/keys/refresh.rs +++ b/frost-p256/src/keys/refresh.rs @@ -6,7 +6,7 @@ use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; -use super::{PublicKeyPackage, SecretShare}; +use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refreshes shares using a trusted dealer pub fn calculate_zero_key( @@ -28,7 +28,7 @@ pub fn calculate_zero_key( /// Each participant refreshed their shares pub fn refresh_share( zero_share: SecretShare, - current_share: &SecretShare, -) -> Result { + current_share: &KeyPackage, +) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } diff --git a/frost-ristretto255/src/keys/refresh.rs b/frost-ristretto255/src/keys/refresh.rs index 4702c9af..303c2580 100644 --- a/frost-ristretto255/src/keys/refresh.rs +++ b/frost-ristretto255/src/keys/refresh.rs @@ -6,7 +6,7 @@ use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; -use super::{PublicKeyPackage, SecretShare}; +use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refreshes shares using a trusted dealer pub fn calculate_zero_key( @@ -28,7 +28,7 @@ pub fn calculate_zero_key( /// Each participant refreshed their shares pub fn refresh_share( zero_share: SecretShare, - current_share: &SecretShare, -) -> Result { + current_share: &KeyPackage, +) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } diff --git a/frost-secp256k1/src/keys/refresh.rs b/frost-secp256k1/src/keys/refresh.rs index 4702c9af..303c2580 100644 --- a/frost-secp256k1/src/keys/refresh.rs +++ b/frost-secp256k1/src/keys/refresh.rs @@ -6,7 +6,7 @@ use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; -use super::{PublicKeyPackage, SecretShare}; +use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refreshes shares using a trusted dealer pub fn calculate_zero_key( @@ -28,7 +28,7 @@ pub fn calculate_zero_key( /// Each participant refreshed their shares pub fn refresh_share( zero_share: SecretShare, - current_share: &SecretShare, -) -> Result { + current_share: &KeyPackage, +) -> Result { frost::keys::refresh::refresh_share(zero_share, current_share) } From fdde54a1766a645048084760d854acd70a8a934d Mon Sep 17 00:00:00 2001 From: natalie Date: Thu, 4 Jul 2024 13:41:00 +0100 Subject: [PATCH 06/19] Tidy up refresh share functionality (#245) --- README.md | 1 + frost-core/src/keys/refresh.rs | 32 +++++++------------ frost-core/src/tests/refresh.rs | 56 ++++++--------------------------- 3 files changed, 22 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 1774125d..8f22461a 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Besides FROST itself, this repository also provides: - Distributed key generation as specified in the original paper [FROST20](https://eprint.iacr.org/2020/852.pdf); - Repairable Threshold Scheme (RTS) from ['A Survey and Refinement of Repairable Threshold Schemes'](https://eprint.iacr.org/2017/1155) which allows a participant to recover a lost share with the help of a threshold of other participants; - Rerandomized FROST (paper under review). +- Refresh Share functionality using a Trusted Dealer. This can be used to refresh the shares of the participants or to remove a participant. ## Getting Started diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index 5a32005f..0c54f870 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -9,9 +9,9 @@ use std::collections::BTreeMap; use crate::{ keys::{ generate_coefficients, generate_secret_shares, validate_num_of_signers, - CoefficientCommitment, PublicKeyPackage, SigningKey, SigningShare, VerifyingShare + CoefficientCommitment, PublicKeyPackage, SigningKey, SigningShare, VerifyingShare, }, - Ciphersuite, CryptoRng, Error, Field, Group, Identifier, RngCore, Scalar, + Ciphersuite, CryptoRng, Error, Field, Group, Identifier, RngCore, }; use super::{KeyPackage, SecretShare, VerifiableSecretSharingCommitment}; @@ -79,10 +79,9 @@ pub fn refresh_share( zero_share: SecretShare, current_key_package: &KeyPackage, ) -> Result, Error> { - // The identity commitment needs to be added to the VSS commitment let identity_commitment: Vec> = - vec![CoefficientCommitment::new(C::Group::identity())]; + vec![CoefficientCommitment::new(C::Group::identity())]; let zero_commitments_without_id = zero_share.commitment.0; @@ -91,29 +90,22 @@ pub fn refresh_share( .chain(zero_commitments_without_id.clone()) .collect(); - let zero_share = SecretShare{ + let zero_share = SecretShare { header: zero_share.header, identifier: zero_share.identifier, signing_share: zero_share.signing_share, - commitment: VerifiableSecretSharingCommitment::::new(zero_commitment) + commitment: VerifiableSecretSharingCommitment::::new(zero_commitment), }; // verify zero_share secret share - let zero_key_package = KeyPackage::::try_from(zero_share).unwrap(); // TODO - - + let zero_key_package = KeyPackage::::try_from(zero_share)?; - let signing_share: Scalar = - zero_key_package.signing_share.to_scalar() + current_key_package.signing_share.to_scalar(); + let signing_share: SigningShare = SigningShare::new( + zero_key_package.signing_share.to_scalar() + current_key_package.signing_share.to_scalar(), + ); - // let key_package = {current_key_package, signing_share}; // TODO + let mut new_key_package = current_key_package.clone(); + new_key_package.signing_share = signing_share; - Ok(KeyPackage { - verifying_share: current_key_package.verifying_share, - verifying_key: current_key_package.verifying_key, - min_signers: current_key_package.min_signers, - header: current_key_package.header, - identifier: current_key_package.identifier, - signing_share: SigningShare::new(signing_share), - }) + Ok(new_key_package) } diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index 37a7ea0f..4f04c6f8 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -24,7 +24,7 @@ pub fn check_refresh_shares_with_dealer( let max_signers = 5; let min_signers = 3; - let (old_shares, pub_key_package) = frost::keys::generate_with_dealer( + let (old_shares, pub_key_package) = generate_with_dealer( max_signers, min_signers, frost::keys::IdentifierList::Default, @@ -32,11 +32,10 @@ pub fn check_refresh_shares_with_dealer( ) .unwrap(); - let mut old_key_packages: BTreeMap, frost::keys::KeyPackage> = - BTreeMap::new(); + let mut old_key_packages: BTreeMap, KeyPackage> = BTreeMap::new(); for (k, v) in old_shares { - let key_package = frost::keys::KeyPackage::try_from(v).unwrap(); + let key_package = KeyPackage::try_from(v).unwrap(); old_key_packages.insert(k, key_package); } @@ -79,12 +78,10 @@ pub fn check_refresh_shares_with_dealer( ); } - let mut key_packages: BTreeMap, frost::keys::KeyPackage> = - BTreeMap::new(); + let mut key_packages: BTreeMap, KeyPackage> = BTreeMap::new(); for (k, v) in new_shares { - let key_package = frost::keys::KeyPackage::try_from(v.unwrap()).unwrap(); - key_packages.insert(k, key_package); + key_packages.insert(k, v.unwrap()); } check_sign(min_signers, key_packages, rng, new_pub_key_package).unwrap(); } @@ -100,7 +97,8 @@ pub fn check_refresh_shares_with_dealer_fails_with_invalid_signers< error: Error, mut rng: R, ) { - let (_old_shares, pub_key_package) = generate_with_dealer::(5, 2, frost::keys::IdentifierList::Default, &mut rng).unwrap(); + let (_old_shares, pub_key_package) = + generate_with_dealer::(5, 2, frost::keys::IdentifierList::Default, &mut rng).unwrap(); let out = calculate_zero_key( pub_key_package, new_max_signers, @@ -113,42 +111,6 @@ pub fn check_refresh_shares_with_dealer_fails_with_invalid_signers< assert!(out == Err(error)) } -// fn build_old_shares( -// max_signers: u16, -// min_signers: u16, -// mut rng: &mut R, -// ) -> (BTreeMap, SecretShare>, PublicKeyPackage) { -// // Compute shares - -// //////////////////////////////////////////////////////////////////////////// -// // Key generation -// //////////////////////////////////////////////////////////////////////////// - -// let mut bytes = [0; 64]; -// rng.fill_bytes(&mut bytes); - -// let key = SigningKey::new(&mut rng); - -// let (old_shares, pub_key_package): ( -// BTreeMap, SecretShare>, -// PublicKeyPackage, -// ) = frost::keys::split( -// &key, -// max_signers, -// min_signers, -// frost::keys::IdentifierList::Default, -// &mut rng, -// ) -// .unwrap(); - -// // Try to refresh shares -// // Signer 2 will be removed and Signers 1, 3, 4 & 5 will remain - -// // Rerun key generation - -// (old_shares, pub_key_package) -// } - /// Check serialisation pub fn check_refresh_shares_with_dealer_serialisation( mut rng: R, @@ -161,7 +123,7 @@ pub fn check_refresh_shares_with_dealer_serialisation Date: Thu, 4 Jul 2024 13:42:36 +0100 Subject: [PATCH 07/19] Add refresh share functionality to Book (#245) Diagram is still to be added --- book/src/frost.md | 10 +++++++++ book/src/tutorial/refreshing.png | 0 book/src/tutorial/refreshing_shares.md | 28 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 book/src/tutorial/refreshing.png create mode 100644 book/src/tutorial/refreshing_shares.md diff --git a/book/src/frost.md b/book/src/frost.md index dd7591ee..dbdbb4f7 100644 --- a/book/src/frost.md +++ b/book/src/frost.md @@ -83,6 +83,16 @@ is still free to start the process with only 2 participants if they wish. Signature verification is carried out as normal with single-party signatures, along with the signed message and the group verifying key as inputs. +## Refreshing + +Refreshing shares starts with a Trusted Dealer who calculated a zero key to +send to each participant who can then refresh their shares and create a new +key package. + +```admonish note +This is also possible via Distributed Key Generation but this has not yet been +implemented. +``` ## Ciphersuites diff --git a/book/src/tutorial/refreshing.png b/book/src/tutorial/refreshing.png new file mode 100644 index 00000000..e69de29b diff --git a/book/src/tutorial/refreshing_shares.md b/book/src/tutorial/refreshing_shares.md new file mode 100644 index 00000000..cf898b83 --- /dev/null +++ b/book/src/tutorial/refreshing_shares.md @@ -0,0 +1,28 @@ +# Refreshing Shares using a Trusted Dealer + +The diagram below shows the refresh share process. Dashed lines +represent data being sent through an [authenticated and confidential communication +channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). + + + +The Trusted Dealer needs to first run `calculate_zero_key` where the new SecretShares are generated and then verified. +This is done with +[`KeyPackage::try_from()`](https://docs.rs/frost-core/latest/frost_core/frost/keys/struct.KeyPackage.html#method.try_from): +`caluclate_zero_key` returns a new SecretShare and PublicKeyPackage +Each new `SecretShare` and `PublicKeyPackage` must then be sent via an [**authenticated** and +**confidential** channel +](https://frost.zfnd.org/terminology.html#peer-to-peer-channel) for each +participant, who must verify the package to obtain a `KeyPackage` which contains +their signing share, verifying share and group verifying key. + +Each Participant then runs `refresh_share` to generate a new `KeyPackage`. + +```admonish danger +The refreshed `KeyPackage` contents must be stored securely and the original +`KeyPackage` should be deleted. For example: + +- Make sure other users in the system can't read it; +- If possible, use the OS secure storage such that the package + contents can only be opened with the user's password or biometrics. +``` From 1f0d92fd897c6f3f339a54952b44982d7b44d4b8 Mon Sep 17 00:00:00 2001 From: natalie Date: Wed, 10 Jul 2024 10:31:24 +0100 Subject: [PATCH 08/19] Update book for rereshing shares with trusted dealer (#245) --- book/src/SUMMARY.md | 1 + .../{refreshing_shares.md => refreshing-shares.md} | 0 frost-core/src/tests/refresh.rs | 7 ++++--- 3 files changed, 5 insertions(+), 3 deletions(-) rename book/src/tutorial/{refreshing_shares.md => refreshing-shares.md} (100%) diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 4cf983c7..3970f8e0 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -7,6 +7,7 @@ - [Trusted Dealer Key Generation](tutorial/trusted-dealer.md) - [Signing](tutorial/signing.md) - [Distributed Key Generation](tutorial/dkg.md) + - [Refreshing Shares](tutorial/refreshing-shares.md) - [User Documentation](user.md) - [Serialization Format](user/serialization.md) - [FROST with Zcash](zcash.md) diff --git a/book/src/tutorial/refreshing_shares.md b/book/src/tutorial/refreshing-shares.md similarity index 100% rename from book/src/tutorial/refreshing_shares.md rename to book/src/tutorial/refreshing-shares.md diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index 4f04c6f8..b1b5c45e 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -32,7 +32,8 @@ pub fn check_refresh_shares_with_dealer( ) .unwrap(); - let mut old_key_packages: BTreeMap, KeyPackage> = BTreeMap::new(); + let mut old_key_packages: BTreeMap, KeyPackage> = + BTreeMap::new(); for (k, v) in old_shares { let key_package = KeyPackage::try_from(v).unwrap(); @@ -78,7 +79,8 @@ pub fn check_refresh_shares_with_dealer( ); } - let mut key_packages: BTreeMap, KeyPackage> = BTreeMap::new(); + let mut key_packages: BTreeMap, KeyPackage> = + BTreeMap::new(); for (k, v) in new_shares { key_packages.insert(k, v.unwrap()); @@ -115,7 +117,6 @@ pub fn check_refresh_shares_with_dealer_fails_with_invalid_signers< pub fn check_refresh_shares_with_dealer_serialisation( mut rng: R, ) { - // Compute shares //////////////////////////////////////////////////////////////////////////// // Old Key generation From 6bb17f2826a01f56b313c928415961765151dc1f Mon Sep 17 00:00:00 2001 From: natalie Date: Thu, 18 Jul 2024 11:31:57 +0100 Subject: [PATCH 09/19] Add new verifying shares calculation for refresh shares (#245) Add tests for invalid identifiers when refreshing shares --- frost-core/src/keys/refresh.rs | 21 +++- frost-core/src/lib.rs | 101 ++++++++++-------- frost-core/src/tests/ciphersuite_generic.rs | 50 +++++++-- frost-core/src/tests/refresh.rs | 99 +++++++++++++---- frost-ed25519/tests/integration_tests.rs | 29 +++++ frost-ed448/tests/integration_tests.rs | 29 +++++ frost-p256/tests/integration_tests.rs | 29 +++++ frost-ristretto255/tests/integration_tests.rs | 29 +++++ frost-secp256k1/tests/integration_tests.rs | 29 +++++ 9 files changed, 336 insertions(+), 80 deletions(-) diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index 0c54f870..b1d8c0f7 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -16,7 +16,9 @@ use crate::{ use super::{KeyPackage, SecretShare, VerifiableSecretSharingCommitment}; -/// Refreshes shares using a trusted dealer +/// Generates new zero key shares and a public key package using a trusted dealer +/// Building a new public key package is done by taking the verifying shares from the new public key package and adding +/// them to the original verifying shares pub fn calculate_zero_key( old_pub_key_package: PublicKeyPackage, max_signers: u16, @@ -25,7 +27,6 @@ pub fn calculate_zero_key( rng: &mut R, ) -> Result<(Vec>, PublicKeyPackage), Error> { // Validate inputs - if identifiers.len() != max_signers as usize { return Err(Error::IncorrectNumberOfIdentifiers); } @@ -33,7 +34,6 @@ pub fn calculate_zero_key( validate_num_of_signers(min_signers, max_signers)?; // Build zero key shares - let zero_key = SigningKey { scalar: <::Field>::zero(), }; @@ -52,8 +52,19 @@ pub fn calculate_zero_key( let mut zero_shares_minus_identity: Vec> = Vec::new(); for share in zero_shares.clone() { - let signer_public = SigningShare::into(share.signing_share); - verifying_shares.insert(share.identifier, signer_public); + let zero_verifying_share: VerifyingShare = SigningShare::into(share.signing_share); + + let old_verifying_share = old_pub_key_package.verifying_shares.get(&share.identifier); + + match old_verifying_share { + Some(old_verifying_share) => { + let verifying_share = + zero_verifying_share.to_element() + old_verifying_share.to_element(); + verifying_shares.insert(share.identifier, VerifyingShare::new(verifying_share)); + } + None => return Err(Error::UnknownIdentifier), + }; + let mut coefficients = share.commitment.0; coefficients.remove(0); zero_shares_minus_identity.push(SecretShare { diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index b54f3cc3..c7cc3df6 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -573,6 +573,7 @@ where if signing_package.signing_commitments().len() != signature_shares.len() { return Err(Error::UnknownIdentifier); } + if !signing_package.signing_commitments().keys().all(|id| { #[cfg(feature = "cheater-detection")] return signature_shares.contains_key(id) && pubkeys.verifying_shares().contains_key(id); @@ -586,7 +587,6 @@ where // binding factor. let binding_factor_list: BindingFactorList = compute_binding_factor_list(signing_package, &pubkeys.verifying_key, &[])?; - // Compute the group commitment from signing commitments produced in round one. let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?; @@ -616,48 +616,14 @@ where // This approach is more efficient since we don't need to verify all shares // if the aggregate signature is valid (which should be the common case). #[cfg(feature = "cheater-detection")] - if let Err(err) = verification_result { - // Compute the per-message challenge. - let challenge = crate::challenge::( - &group_commitment.0, - &pubkeys.verifying_key, - signing_package.message().as_slice(), + if verification_result.is_err() { + detect_cheater( + group_commitment, + pubkeys, + signing_package, + signature_shares, + &binding_factor_list, )?; - - // Verify the signature shares. - for (signature_share_identifier, signature_share) in signature_shares { - // Look up the public key for this signer, where `signer_pubkey` = _G.ScalarBaseMult(s[i])_, - // and where s[i] is a secret share of the constant term of _f_, the secret polynomial. - let signer_pubkey = pubkeys - .verifying_shares - .get(signature_share_identifier) - .ok_or(Error::UnknownIdentifier)?; - - // Compute Lagrange coefficient. - let lambda_i = derive_interpolating_value(signature_share_identifier, signing_package)?; - - let binding_factor = binding_factor_list - .get(signature_share_identifier) - .ok_or(Error::UnknownIdentifier)?; - - // Compute the commitment share. - let R_share = signing_package - .signing_commitment(signature_share_identifier) - .ok_or(Error::UnknownIdentifier)? - .to_group_commitment_share(binding_factor); - - // Compute relation values to verify this signature share. - signature_share.verify( - *signature_share_identifier, - &R_share, - signer_pubkey, - lambda_i, - &challenge, - )?; - } - - // We should never reach here; but we return the verification error to be safe. - return Err(err); } #[cfg(not(feature = "cheater-detection"))] @@ -665,3 +631,54 @@ where Ok(signature) } + +/// Optional cheater detection feature +/// Each share is verified to find the cheater +fn detect_cheater( + group_commitment: GroupCommitment, + pubkeys: &keys::PublicKeyPackage, + signing_package: &SigningPackage, + signature_shares: &BTreeMap, round2::SignatureShare>, + binding_factor_list: &BindingFactorList, +) -> Result<(), Error> { + // Compute the per-message challenge. + let challenge = crate::challenge::( + &group_commitment.0, + &pubkeys.verifying_key, + signing_package.message().as_slice(), + )?; + + // Verify the signature shares. + for (signature_share_identifier, signature_share) in signature_shares { + // Look up the public key for this signer, where `signer_pubkey` = _G.ScalarBaseMult(s[i])_, + // and where s[i] is a secret share of the constant term of _f_, the secret polynomial. + let signer_pubkey = pubkeys + .verifying_shares + .get(signature_share_identifier) + .ok_or(Error::UnknownIdentifier)?; + + // Compute Lagrange coefficient. + let lambda_i = derive_interpolating_value(signature_share_identifier, signing_package)?; + + let binding_factor = binding_factor_list + .get(signature_share_identifier) + .ok_or(Error::UnknownIdentifier)?; + + // Compute the commitment share. + let R_share = signing_package + .signing_commitment(signature_share_identifier) + .ok_or(Error::UnknownIdentifier)? + .to_group_commitment_share(binding_factor); + + // Compute relation values to verify this signature share. + signature_share.verify( + *signature_share_identifier, + &R_share, + signer_pubkey, + lambda_i, + &challenge, + )?; + } + + Ok(()) +} diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index a6219340..623d7324 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -4,8 +4,10 @@ use alloc::collections::BTreeMap; use crate as frost; +use crate::round2::SignatureShare; use crate::{ - keys::PublicKeyPackage, Error, Field, Group, Identifier, Signature, SigningKey, VerifyingKey, + keys::PublicKeyPackage, Error, Field, Group, Identifier, Signature, SigningKey, SigningPackage, + VerifyingKey, }; use alloc::borrow::ToOwned; use alloc::vec::Vec; @@ -222,7 +224,7 @@ pub fn check_sign( // - take one (unused) commitment per signing participant let mut signature_shares = BTreeMap::new(); let message = "message to sign".as_bytes(); - let signing_package = frost::SigningPackage::new(commitments_map, message); + let signing_package = SigningPackage::new(commitments_map, message); //////////////////////////////////////////////////////////////////////////// // Round 2: each participant generates their signature share @@ -249,19 +251,18 @@ pub fn check_sign( // generates the final signature. //////////////////////////////////////////////////////////////////////////// - #[cfg(not(feature = "cheater-detection"))] - let pubkey_package = PublicKeyPackage { - header: pubkey_package.header, - verifying_shares: BTreeMap::new(), - verifying_key: pubkey_package.verifying_key, - }; - check_aggregate_errors( signing_package.clone(), signature_shares.clone(), pubkey_package.clone(), ); + check_verifying_shares( + pubkey_package.clone(), + signing_package.clone(), + signature_shares.clone(), + ); + // Aggregate (also verifies the signature shares) let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?; @@ -313,6 +314,13 @@ fn check_aggregate_errors( signature_shares: BTreeMap, frost::round2::SignatureShare>, pubkey_package: frost::keys::PublicKeyPackage, ) { + #[cfg(not(feature = "cheater-detection"))] + let pubkey_package = PublicKeyPackage { + header: pubkey_package.header, + verifying_shares: BTreeMap::new(), + verifying_key: pubkey_package.verifying_key, + }; + #[cfg(feature = "cheater-detection")] check_aggregate_corrupted_share( signing_package.clone(), @@ -745,7 +753,7 @@ pub fn check_sign_with_missing_identifier( + pubkeys: PublicKeyPackage, + signing_package: SigningPackage, + mut signature_shares: BTreeMap, SignatureShare>, +) { + let one = <::Group as Group>::Field::one(); + + // Corrupt last share + let id = *signature_shares.keys().last().unwrap(); + *signature_shares.get_mut(&id).unwrap() = + SignatureShare::new(signature_shares[&id].to_scalar() + one); + + let e = frost::aggregate(&signing_package, &signature_shares, &pubkeys).unwrap_err(); + assert_eq!(e.culprit(), Some(id)); + assert_eq!(e, Error::InvalidSignatureShare { culprit: id }); +} diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index b1b5c45e..8ff6a90e 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -22,18 +22,17 @@ pub fn check_refresh_shares_with_dealer( // Old Key generation //////////////////////////////////////////////////////////////////////////// - let max_signers = 5; - let min_signers = 3; + const MAX_SIGNERS: u16 = 5; + const MIN_SIGNERS: u16 = 3; let (old_shares, pub_key_package) = generate_with_dealer( - max_signers, - min_signers, + MAX_SIGNERS, + MIN_SIGNERS, frost::keys::IdentifierList::Default, &mut rng, ) .unwrap(); - let mut old_key_packages: BTreeMap, KeyPackage> = - BTreeMap::new(); + let mut old_key_packages: BTreeMap, KeyPackage> = BTreeMap::new(); for (k, v) in old_shares { let key_package = KeyPackage::try_from(v).unwrap(); @@ -55,12 +54,12 @@ pub fn check_refresh_shares_with_dealer( const NEW_MAX_SIGNERS: u16 = 4; - // Trusted Dealer generates zero keys + // Trusted Dealer generates zero keys and new public key package let (zero_shares, new_pub_key_package) = calculate_zero_key( pub_key_package, NEW_MAX_SIGNERS, - min_signers, + MIN_SIGNERS, &remaining_ids, &mut rng, ) @@ -73,22 +72,19 @@ pub fn check_refresh_shares_with_dealer( for i in 0..remaining_ids.len() { let identifier = remaining_ids[i]; let current_share = &old_key_packages[&identifier]; - new_shares.insert( - identifier, - refresh_share(zero_shares[i].clone(), current_share), - ); + let new_share = refresh_share(zero_shares[i].clone(), current_share); + new_shares.insert(identifier, new_share); } - let mut key_packages: BTreeMap, KeyPackage> = - BTreeMap::new(); + let mut key_packages: BTreeMap, KeyPackage> = BTreeMap::new(); for (k, v) in new_shares { key_packages.insert(k, v.unwrap()); } - check_sign(min_signers, key_packages, rng, new_pub_key_package).unwrap(); + check_sign(MIN_SIGNERS, key_packages, rng, new_pub_key_package).unwrap(); } -/// Check refesh shares with dealer errors +/// We want to check that shares are refreshed with valid signers pub fn check_refresh_shares_with_dealer_fails_with_invalid_signers< C: Ciphersuite, R: RngCore + CryptoRng, @@ -113,20 +109,79 @@ pub fn check_refresh_shares_with_dealer_fails_with_invalid_signers< assert!(out == Err(error)) } +/// We want to test that refresh share fails if the identifiers don't match the +/// identifiers in the public key package +pub fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package< + C: Ciphersuite, + R: RngCore + CryptoRng, +>( + mut rng: R, +) { + // Compute shares + + //////////////////////////////////////////////////////////////////////////// + // Old Key generation + //////////////////////////////////////////////////////////////////////////// + + const MAX_SIGNERS: u16 = 3; + const MIN_SIGNERS: u16 = 2; + let (old_shares, incorrect_pub_key_package) = generate_with_dealer( + MAX_SIGNERS, + MIN_SIGNERS, + frost::keys::IdentifierList::Default, + &mut rng, + ) + .unwrap(); + + let mut old_key_packages: BTreeMap, KeyPackage> = BTreeMap::new(); + + for (k, v) in old_shares { + let key_package = KeyPackage::try_from(v).unwrap(); + old_key_packages.insert(k, key_package); + } + + //////////////////////////////////////////////////////////////////////////// + // New Key generation + //////////////////////////////////////////////////////////////////////////// + + // Signer 2 will be removed and Signers 1, 3, 4 & 5 will remain + + let remaining_ids = vec![ + Identifier::try_from(1).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(5).unwrap(), + ]; + + const NEW_MAX_SIGNERS: u16 = 4; + + // Trusted Dealer generates zero keys and new public key package + + let e = calculate_zero_key( + incorrect_pub_key_package, + NEW_MAX_SIGNERS, + MIN_SIGNERS, + &remaining_ids, + &mut rng, + ) + .unwrap_err(); + + assert_eq!(e, Error::UnknownIdentifier) +} + /// Check serialisation pub fn check_refresh_shares_with_dealer_serialisation( mut rng: R, ) { - //////////////////////////////////////////////////////////////////////////// // Old Key generation //////////////////////////////////////////////////////////////////////////// - let max_signers = 5; - let min_signers = 3; + const MAX_SIGNERS: u16 = 5; + const MIN_SIGNERS: u16 = 3; let (_old_shares, pub_key_package) = generate_with_dealer( - max_signers, - min_signers, + MAX_SIGNERS, + MIN_SIGNERS, frost::keys::IdentifierList::Default, &mut rng, ) @@ -151,7 +206,7 @@ pub fn check_refresh_shares_with_dealer_serialisation(rng); +} + #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { let rng = thread_rng(); @@ -151,6 +161,25 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { >(max_signers, min_signers, &identifiers, error, rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(8).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(6).unwrap(), + ]; + let min_signers = 2; + let max_signers = 4; + let error = Error::UnknownIdentifier; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ed25519Sha512, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + #[test] fn check_sign_with_dealer() { let rng = thread_rng(); diff --git a/frost-ed448/tests/integration_tests.rs b/frost-ed448/tests/integration_tests.rs index 766f65fc..70061503 100644 --- a/frost-ed448/tests/integration_tests.rs +++ b/frost-ed448/tests/integration_tests.rs @@ -80,6 +80,16 @@ fn check_refresh_shares_with_dealer_serialisation() { ); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::< + Ed448Shake256, + _, + >(rng); +} + #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { let rng = thread_rng(); @@ -151,6 +161,25 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { >(max_signers, min_signers, &identifiers, error, rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(8).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(6).unwrap(), + ]; + let min_signers = 2; + let max_signers = 4; + let error = Error::UnknownIdentifier; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ed448Shake256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + #[test] fn check_sign_with_dealer() { let rng = thread_rng(); diff --git a/frost-p256/tests/integration_tests.rs b/frost-p256/tests/integration_tests.rs index 4015e780..8d44312d 100644 --- a/frost-p256/tests/integration_tests.rs +++ b/frost-p256/tests/integration_tests.rs @@ -80,6 +80,16 @@ fn check_refresh_shares_with_dealer_serialisation() { ); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::< + P256Sha256, + _, + >(rng); +} + #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { let rng = thread_rng(); @@ -151,6 +161,25 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { >(max_signers, min_signers, &identifiers, error, rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(8).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(6).unwrap(), + ]; + let min_signers = 2; + let max_signers = 4; + let error = Error::UnknownIdentifier; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + P256Sha256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + #[test] fn check_sign_with_dealer() { let rng = thread_rng(); diff --git a/frost-ristretto255/tests/integration_tests.rs b/frost-ristretto255/tests/integration_tests.rs index ecb0d2d7..af536ac3 100644 --- a/frost-ristretto255/tests/integration_tests.rs +++ b/frost-ristretto255/tests/integration_tests.rs @@ -81,6 +81,16 @@ fn check_refresh_shares_with_dealer_serialisation() { >(rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::< + Ristretto255Sha512, + _, + >(rng); +} + #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { let rng = thread_rng(); @@ -152,6 +162,25 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { >(max_signers, min_signers, &identifiers, error, rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(8).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(6).unwrap(), + ]; + let min_signers = 2; + let max_signers = 4; + let error = Error::UnknownIdentifier; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Ristretto255Sha512, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + #[test] fn check_sign_with_dealer() { let rng = thread_rng(); diff --git a/frost-secp256k1/tests/integration_tests.rs b/frost-secp256k1/tests/integration_tests.rs index 3a6b1767..9581384b 100644 --- a/frost-secp256k1/tests/integration_tests.rs +++ b/frost-secp256k1/tests/integration_tests.rs @@ -80,6 +80,16 @@ fn check_refresh_shares_with_dealer_serialisation() { ); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package() { + let rng = thread_rng(); + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_public_key_package::< + Secp256K1Sha256, + _, + >(rng); +} + #[test] fn check_refresh_shares_with_dealer_fails_with_invalid_min_signers() { let rng = thread_rng(); @@ -151,6 +161,25 @@ fn check_refresh_shares_with_dealer_fails_with_invalid_max_signers() { >(max_signers, min_signers, &identifiers, error, rng); } +#[test] +fn check_refresh_shares_with_dealer_fails_with_invalid_identifier() { + let rng = thread_rng(); + let identifiers = vec![ + Identifier::try_from(8).unwrap(), + Identifier::try_from(3).unwrap(), + Identifier::try_from(4).unwrap(), + Identifier::try_from(6).unwrap(), + ]; + let min_signers = 2; + let max_signers = 4; + let error = Error::UnknownIdentifier; + + frost_core::tests::refresh::check_refresh_shares_with_dealer_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(max_signers, min_signers, &identifiers, error, rng); +} + #[test] fn check_sign_with_dealer() { let rng = thread_rng(); From fd3886eccd04378a3a2becd0012094fa13d44813 Mon Sep 17 00:00:00 2001 From: natalie Date: Thu, 18 Jul 2024 11:44:47 +0100 Subject: [PATCH 10/19] Rename calculate_zero_key to compute_refreshing_shares (#245) --- book/src/tutorial/refreshing-shares.md | 2 +- frost-core/src/keys/refresh.rs | 2 +- frost-core/src/tests/refresh.rs | 10 +++++----- frost-ed25519/src/keys/refresh.rs | 4 ++-- frost-ed448/src/keys/refresh.rs | 4 ++-- frost-p256/src/keys/refresh.rs | 4 ++-- frost-ristretto255/src/keys/refresh.rs | 4 ++-- frost-secp256k1/src/keys/refresh.rs | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/book/src/tutorial/refreshing-shares.md b/book/src/tutorial/refreshing-shares.md index cf898b83..0ef7c8e7 100644 --- a/book/src/tutorial/refreshing-shares.md +++ b/book/src/tutorial/refreshing-shares.md @@ -6,7 +6,7 @@ channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). -The Trusted Dealer needs to first run `calculate_zero_key` where the new SecretShares are generated and then verified. +The Trusted Dealer needs to first run `compute_refreshing_shares` where the new SecretShares are generated and then verified. This is done with [`KeyPackage::try_from()`](https://docs.rs/frost-core/latest/frost_core/frost/keys/struct.KeyPackage.html#method.try_from): `caluclate_zero_key` returns a new SecretShare and PublicKeyPackage diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index b1d8c0f7..b6619785 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -19,7 +19,7 @@ use super::{KeyPackage, SecretShare, VerifiableSecretSharingCommitment}; /// Generates new zero key shares and a public key package using a trusted dealer /// Building a new public key package is done by taking the verifying shares from the new public key package and adding /// them to the original verifying shares -pub fn calculate_zero_key( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, diff --git a/frost-core/src/tests/refresh.rs b/frost-core/src/tests/refresh.rs index 8ff6a90e..29330b90 100644 --- a/frost-core/src/tests/refresh.rs +++ b/frost-core/src/tests/refresh.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use rand_core::{CryptoRng, RngCore}; use crate::keys::generate_with_dealer; -use crate::keys::refresh::{calculate_zero_key, refresh_share}; +use crate::keys::refresh::{compute_refreshing_shares, refresh_share}; use crate::{self as frost}; use crate::{ keys::{KeyPackage, PublicKeyPackage, SecretShare}, @@ -56,7 +56,7 @@ pub fn check_refresh_shares_with_dealer( // Trusted Dealer generates zero keys and new public key package - let (zero_shares, new_pub_key_package) = calculate_zero_key( + let (zero_shares, new_pub_key_package) = compute_refreshing_shares( pub_key_package, NEW_MAX_SIGNERS, MIN_SIGNERS, @@ -97,7 +97,7 @@ pub fn check_refresh_shares_with_dealer_fails_with_invalid_signers< ) { let (_old_shares, pub_key_package) = generate_with_dealer::(5, 2, frost::keys::IdentifierList::Default, &mut rng).unwrap(); - let out = calculate_zero_key( + let out = compute_refreshing_shares( pub_key_package, new_max_signers, min_signers, @@ -157,7 +157,7 @@ pub fn check_refresh_shares_with_dealer_fails_with_invalid_public_key_package< // Trusted Dealer generates zero keys and new public key package - let e = calculate_zero_key( + let e = compute_refreshing_shares( incorrect_pub_key_package, NEW_MAX_SIGNERS, MIN_SIGNERS, @@ -203,7 +203,7 @@ pub fn check_refresh_shares_with_dealer_serialisation( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, ) -> Result<(Vec, PublicKeyPackage), Error> { - frost::keys::refresh::calculate_zero_key( + frost::keys::refresh::compute_refreshing_shares( old_pub_key_package, max_signers, min_signers, diff --git a/frost-ed448/src/keys/refresh.rs b/frost-ed448/src/keys/refresh.rs index 303c2580..b336a9c8 100644 --- a/frost-ed448/src/keys/refresh.rs +++ b/frost-ed448/src/keys/refresh.rs @@ -9,14 +9,14 @@ use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refreshes shares using a trusted dealer -pub fn calculate_zero_key( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, ) -> Result<(Vec, PublicKeyPackage), Error> { - frost::keys::refresh::calculate_zero_key( + frost::keys::refresh::compute_refreshing_shares( old_pub_key_package, max_signers, min_signers, diff --git a/frost-p256/src/keys/refresh.rs b/frost-p256/src/keys/refresh.rs index 303c2580..b336a9c8 100644 --- a/frost-p256/src/keys/refresh.rs +++ b/frost-p256/src/keys/refresh.rs @@ -9,14 +9,14 @@ use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refreshes shares using a trusted dealer -pub fn calculate_zero_key( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, ) -> Result<(Vec, PublicKeyPackage), Error> { - frost::keys::refresh::calculate_zero_key( + frost::keys::refresh::compute_refreshing_shares( old_pub_key_package, max_signers, min_signers, diff --git a/frost-ristretto255/src/keys/refresh.rs b/frost-ristretto255/src/keys/refresh.rs index 303c2580..b336a9c8 100644 --- a/frost-ristretto255/src/keys/refresh.rs +++ b/frost-ristretto255/src/keys/refresh.rs @@ -9,14 +9,14 @@ use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refreshes shares using a trusted dealer -pub fn calculate_zero_key( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, ) -> Result<(Vec, PublicKeyPackage), Error> { - frost::keys::refresh::calculate_zero_key( + frost::keys::refresh::compute_refreshing_shares( old_pub_key_package, max_signers, min_signers, diff --git a/frost-secp256k1/src/keys/refresh.rs b/frost-secp256k1/src/keys/refresh.rs index 303c2580..b336a9c8 100644 --- a/frost-secp256k1/src/keys/refresh.rs +++ b/frost-secp256k1/src/keys/refresh.rs @@ -9,14 +9,14 @@ use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; use super::{KeyPackage, PublicKeyPackage, SecretShare}; /// Refreshes shares using a trusted dealer -pub fn calculate_zero_key( +pub fn compute_refreshing_shares( old_pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], mut rng: &mut R, ) -> Result<(Vec, PublicKeyPackage), Error> { - frost::keys::refresh::calculate_zero_key( + frost::keys::refresh::compute_refreshing_shares( old_pub_key_package, max_signers, min_signers, From 95dac775e1b75673de6f93dcb0e6c8001e40e6ae Mon Sep 17 00:00:00 2001 From: natalie Date: Thu, 18 Jul 2024 11:50:29 +0100 Subject: [PATCH 11/19] Import Vec from the alloc crate (#245) This is to be compatible with the no_std attribute --- frost-core/src/keys/refresh.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index b6619785..7ba8c8ef 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -4,6 +4,7 @@ //! of all the remaining signers. This can be done using a Trusted Dealer or //! DKG (not yet implemented) +use alloc::vec::Vec; use std::collections::BTreeMap; use crate::{ From 07118797fe109c8f29d3348f8862bd0067059df2 Mon Sep 17 00:00:00 2001 From: natalie Date: Thu, 18 Jul 2024 11:55:59 +0100 Subject: [PATCH 12/19] Use alloc crate instead of std for refresh shares (#245) --- frost-core/src/keys/refresh.rs | 2 +- frost-ed25519/src/keys/refresh.rs | 1 + frost-ed448/src/keys/refresh.rs | 1 + frost-p256/src/keys/refresh.rs | 1 + frost-ristretto255/src/keys/refresh.rs | 1 + frost-secp256k1/src/keys/refresh.rs | 1 + 6 files changed, 6 insertions(+), 1 deletion(-) diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index 7ba8c8ef..81fc5262 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -5,7 +5,7 @@ //! DKG (not yet implemented) use alloc::vec::Vec; -use std::collections::BTreeMap; +use alloc::collections::BTreeMap; use crate::{ keys::{ diff --git a/frost-ed25519/src/keys/refresh.rs b/frost-ed25519/src/keys/refresh.rs index b336a9c8..c270fc20 100644 --- a/frost-ed25519/src/keys/refresh.rs +++ b/frost-ed25519/src/keys/refresh.rs @@ -5,6 +5,7 @@ //! DKG (not yet implemented) use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; +use alloc::vec::Vec; use super::{KeyPackage, PublicKeyPackage, SecretShare}; diff --git a/frost-ed448/src/keys/refresh.rs b/frost-ed448/src/keys/refresh.rs index b336a9c8..c270fc20 100644 --- a/frost-ed448/src/keys/refresh.rs +++ b/frost-ed448/src/keys/refresh.rs @@ -5,6 +5,7 @@ //! DKG (not yet implemented) use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; +use alloc::vec::Vec; use super::{KeyPackage, PublicKeyPackage, SecretShare}; diff --git a/frost-p256/src/keys/refresh.rs b/frost-p256/src/keys/refresh.rs index b336a9c8..c270fc20 100644 --- a/frost-p256/src/keys/refresh.rs +++ b/frost-p256/src/keys/refresh.rs @@ -5,6 +5,7 @@ //! DKG (not yet implemented) use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; +use alloc::vec::Vec; use super::{KeyPackage, PublicKeyPackage, SecretShare}; diff --git a/frost-ristretto255/src/keys/refresh.rs b/frost-ristretto255/src/keys/refresh.rs index b336a9c8..c270fc20 100644 --- a/frost-ristretto255/src/keys/refresh.rs +++ b/frost-ristretto255/src/keys/refresh.rs @@ -5,6 +5,7 @@ //! DKG (not yet implemented) use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; +use alloc::vec::Vec; use super::{KeyPackage, PublicKeyPackage, SecretShare}; diff --git a/frost-secp256k1/src/keys/refresh.rs b/frost-secp256k1/src/keys/refresh.rs index b336a9c8..c270fc20 100644 --- a/frost-secp256k1/src/keys/refresh.rs +++ b/frost-secp256k1/src/keys/refresh.rs @@ -5,6 +5,7 @@ //! DKG (not yet implemented) use crate::{frost, Ciphersuite, CryptoRng, Error, Identifier, RngCore}; +use alloc::vec::Vec; use super::{KeyPackage, PublicKeyPackage, SecretShare}; From 0a829a993df5ae607400ca63a02c69e64586fa8b Mon Sep 17 00:00:00 2001 From: natalie Date: Thu, 18 Jul 2024 11:57:01 +0100 Subject: [PATCH 13/19] Fix fmt error (#245) --- frost-core/src/keys/refresh.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index 81fc5262..cbf981ff 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -4,8 +4,8 @@ //! of all the remaining signers. This can be done using a Trusted Dealer or //! DKG (not yet implemented) -use alloc::vec::Vec; use alloc::collections::BTreeMap; +use alloc::vec::Vec; use crate::{ keys::{ From b1731b6b6bde99a162607bec25faca3c0d948d86 Mon Sep 17 00:00:00 2001 From: natalie Date: Thu, 18 Jul 2024 14:14:43 +0100 Subject: [PATCH 14/19] Refactoring refresh shares functionality (#245) --- frost-core/src/keys/refresh.rs | 57 +++++++++++++++------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index cbf981ff..f3f762cf 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -21,7 +21,7 @@ use super::{KeyPackage, SecretShare, VerifiableSecretSharingCommitment}; /// Building a new public key package is done by taking the verifying shares from the new public key package and adding /// them to the original verifying shares pub fn compute_refreshing_shares( - old_pub_key_package: PublicKeyPackage, + pub_key_package: PublicKeyPackage, max_signers: u16, min_signers: u16, identifiers: &[Identifier], @@ -31,18 +31,16 @@ pub fn compute_refreshing_shares( if identifiers.len() != max_signers as usize { return Err(Error::IncorrectNumberOfIdentifiers); } - validate_num_of_signers(min_signers, max_signers)?; - // Build zero key shares - let zero_key = SigningKey { + // Build refreshing shares + let refreshing_key = SigningKey { scalar: <::Field>::zero(), }; let coefficients = generate_coefficients::(min_signers as usize - 1, rng); - - let zero_shares = generate_secret_shares( - &zero_key, + let refreshing_shares = generate_secret_shares( + &refreshing_key, max_signers, min_signers, coefficients, @@ -50,17 +48,17 @@ pub fn compute_refreshing_shares( )?; let mut verifying_shares: BTreeMap, VerifyingShare> = BTreeMap::new(); - let mut zero_shares_minus_identity: Vec> = Vec::new(); + let mut refreshing_shares_minus_identity: Vec> = Vec::new(); - for share in zero_shares.clone() { - let zero_verifying_share: VerifyingShare = SigningShare::into(share.signing_share); + for share in refreshing_shares.clone() { + let refreshing_verifying_share: VerifyingShare = SigningShare::into(share.signing_share); - let old_verifying_share = old_pub_key_package.verifying_shares.get(&share.identifier); + let old_verifying_share = pub_key_package.verifying_shares.get(&share.identifier); match old_verifying_share { Some(old_verifying_share) => { let verifying_share = - zero_verifying_share.to_element() + old_verifying_share.to_element(); + refreshing_verifying_share.to_element() + old_verifying_share.to_element(); verifying_shares.insert(share.identifier, VerifyingShare::new(verifying_share)); } None => return Err(Error::UnknownIdentifier), @@ -68,7 +66,7 @@ pub fn compute_refreshing_shares( let mut coefficients = share.commitment.0; coefficients.remove(0); - zero_shares_minus_identity.push(SecretShare { + refreshing_shares_minus_identity.push(SecretShare { header: share.header, identifier: share.identifier, signing_share: share.signing_share, @@ -76,44 +74,39 @@ pub fn compute_refreshing_shares( }); } - let pub_key_package = PublicKeyPackage:: { - header: old_pub_key_package.header, + let refreshed_pub_key_package = PublicKeyPackage:: { + header: pub_key_package.header, verifying_shares, - verifying_key: old_pub_key_package.verifying_key, + verifying_key: pub_key_package.verifying_key, }; - Ok((zero_shares_minus_identity, pub_key_package)) + Ok((refreshing_shares_minus_identity, refreshed_pub_key_package)) } /// Each participant refreshes their shares -/// This is done by taking the `zero_share` received from the trusted dealer and adding it to the original share +/// This is done by taking the `refreshing_share` received from the trusted dealer and adding it to the original share pub fn refresh_share( - zero_share: SecretShare, + mut refreshing_share: SecretShare, current_key_package: &KeyPackage, ) -> Result, Error> { // The identity commitment needs to be added to the VSS commitment let identity_commitment: Vec> = vec![CoefficientCommitment::new(C::Group::identity())]; - let zero_commitments_without_id = zero_share.commitment.0; - - let zero_commitment: Vec> = identity_commitment + let refreshing_share_commitments: Vec> = identity_commitment .into_iter() - .chain(zero_commitments_without_id.clone()) + .chain(refreshing_share.commitment.0.clone()) .collect(); - let zero_share = SecretShare { - header: zero_share.header, - identifier: zero_share.identifier, - signing_share: zero_share.signing_share, - commitment: VerifiableSecretSharingCommitment::::new(zero_commitment), - }; + refreshing_share.commitment = + VerifiableSecretSharingCommitment::::new(refreshing_share_commitments); - // verify zero_share secret share - let zero_key_package = KeyPackage::::try_from(zero_share)?; + // Verify refreshing_share secret share + let refreshed_share_package = KeyPackage::::try_from(refreshing_share)?; let signing_share: SigningShare = SigningShare::new( - zero_key_package.signing_share.to_scalar() + current_key_package.signing_share.to_scalar(), + refreshed_share_package.signing_share.to_scalar() + + current_key_package.signing_share.to_scalar(), ); let mut new_key_package = current_key_package.clone(); From b71db3161aed6187bf25660306a1bea49c2f50e8 Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Thu, 18 Jul 2024 10:41:05 -0300 Subject: [PATCH 15/19] cleanups during review --- frost-core/src/keys/refresh.rs | 44 ++++++++++----------- frost-core/src/tests/ciphersuite_generic.rs | 6 ++- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/frost-core/src/keys/refresh.rs b/frost-core/src/keys/refresh.rs index f3f762cf..53a3cdd9 100644 --- a/frost-core/src/keys/refresh.rs +++ b/frost-core/src/keys/refresh.rs @@ -17,9 +17,10 @@ use crate::{ use super::{KeyPackage, SecretShare, VerifiableSecretSharingCommitment}; -/// Generates new zero key shares and a public key package using a trusted dealer -/// Building a new public key package is done by taking the verifying shares from the new public key package and adding -/// them to the original verifying shares +/// Generates new zero key shares and a public key package using a trusted +/// dealer Building a new public key package is done by taking the verifying +/// shares from the new public key package and adding them to the original +/// verifying shares pub fn compute_refreshing_shares( pub_key_package: PublicKeyPackage, max_signers: u16, @@ -47,44 +48,43 @@ pub fn compute_refreshing_shares( identifiers, )?; - let mut verifying_shares: BTreeMap, VerifyingShare> = BTreeMap::new(); + let mut refreshed_verifying_shares: BTreeMap, VerifyingShare> = + BTreeMap::new(); let mut refreshing_shares_minus_identity: Vec> = Vec::new(); - for share in refreshing_shares.clone() { + for mut share in refreshing_shares { let refreshing_verifying_share: VerifyingShare = SigningShare::into(share.signing_share); - let old_verifying_share = pub_key_package.verifying_shares.get(&share.identifier); + let verifying_share = pub_key_package.verifying_shares.get(&share.identifier); - match old_verifying_share { - Some(old_verifying_share) => { - let verifying_share = - refreshing_verifying_share.to_element() + old_verifying_share.to_element(); - verifying_shares.insert(share.identifier, VerifyingShare::new(verifying_share)); + match verifying_share { + Some(verifying_share) => { + let refreshed_verifying_share = + refreshing_verifying_share.to_element() + verifying_share.to_element(); + refreshed_verifying_shares.insert( + share.identifier, + VerifyingShare::new(refreshed_verifying_share), + ); } None => return Err(Error::UnknownIdentifier), }; - let mut coefficients = share.commitment.0; - coefficients.remove(0); - refreshing_shares_minus_identity.push(SecretShare { - header: share.header, - identifier: share.identifier, - signing_share: share.signing_share, - commitment: VerifiableSecretSharingCommitment::new(coefficients), - }); + share.commitment.0.remove(0); + refreshing_shares_minus_identity.push(share); } let refreshed_pub_key_package = PublicKeyPackage:: { header: pub_key_package.header, - verifying_shares, + verifying_shares: refreshed_verifying_shares, verifying_key: pub_key_package.verifying_key, }; Ok((refreshing_shares_minus_identity, refreshed_pub_key_package)) } -/// Each participant refreshes their shares -/// This is done by taking the `refreshing_share` received from the trusted dealer and adding it to the original share +/// Each participant refreshes their shares This is done by taking the +/// `refreshing_share` received from the trusted dealer and adding it to the +/// original share pub fn refresh_share( mut refreshing_share: SecretShare, current_key_package: &KeyPackage, diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index 623d7324..4528e9b7 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -840,8 +840,10 @@ pub fn check_sign_with_incorrect_commitments( pubkeys: PublicKeyPackage, signing_package: SigningPackage, From 22323c1d3384283cf88e7627bd7e7aec08e6b5ee Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Thu, 18 Jul 2024 18:48:32 -0300 Subject: [PATCH 16/19] Update book/src/tutorial/refreshing-shares.md --- book/src/tutorial/refreshing-shares.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/tutorial/refreshing-shares.md b/book/src/tutorial/refreshing-shares.md index 0ef7c8e7..04c22725 100644 --- a/book/src/tutorial/refreshing-shares.md +++ b/book/src/tutorial/refreshing-shares.md @@ -9,7 +9,7 @@ channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). The Trusted Dealer needs to first run `compute_refreshing_shares` where the new SecretShares are generated and then verified. This is done with [`KeyPackage::try_from()`](https://docs.rs/frost-core/latest/frost_core/frost/keys/struct.KeyPackage.html#method.try_from): -`caluclate_zero_key` returns a new SecretShare and PublicKeyPackage +`compute_refreshing_shares` returns a new SecretShare and PublicKeyPackage Each new `SecretShare` and `PublicKeyPackage` must then be sent via an [**authenticated** and **confidential** channel ](https://frost.zfnd.org/terminology.html#peer-to-peer-channel) for each From 453bdc2f4a0f515aa007d31033c2c6add3a8a3cb Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Thu, 18 Jul 2024 19:26:12 -0300 Subject: [PATCH 17/19] update docs --- book/src/frost.md | 29 +++++++++++++++++++++++--- book/src/tutorial/refreshing-shares.md | 26 ++++++++++++++--------- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/book/src/frost.md b/book/src/frost.md index dbdbb4f7..1daa881f 100644 --- a/book/src/frost.md +++ b/book/src/frost.md @@ -83,11 +83,34 @@ is still free to start the process with only 2 participants if they wish. Signature verification is carried out as normal with single-party signatures, along with the signed message and the group verifying key as inputs. +## Repairing + +Repairing shares allow participants to help another participant recover their +share if they have lost it, or also issue a new share to a new participant +(while keeping the same threshold). + +The repair share functionality requires a threshold of participants to work. +For example, in a 2-of-3 scenario, two participants can help the third recover +their share, or they could issue a new share to move to a 2-of-4 group. + +The functionality works in such a way that each participant running the repair +share function is not able to obtain the share that is being recovered or +issued. + ## Refreshing -Refreshing shares starts with a Trusted Dealer who calculated a zero key to -send to each participant who can then refresh their shares and create a new -key package. +Refreshing shares allow participants (or a subset of them) to update their +shares in a way that maintains the same group public key. Some applications are: + +- Make it harder for attackers to compromise the shares. For example, in a + 2-of-3 threshold scenario, if an attacker steals one participant's device and + all participants refresh their shares, the attacker will need to start over + and steal two shares instead of just one more. +- Remove a participant from the group. For example, in a 2-of-3 threshold + scenario, if two participants decide to remove the third they can both refresh + their shares and the third participant would no longer be able to participate + in signing sessions with the others. (They can also then use the repair share + functionality to issue a new share and move from 2-of-2 back to 2-of-3.) ```admonish note This is also possible via Distributed Key Generation but this has not yet been diff --git a/book/src/tutorial/refreshing-shares.md b/book/src/tutorial/refreshing-shares.md index 04c22725..cf75b3da 100644 --- a/book/src/tutorial/refreshing-shares.md +++ b/book/src/tutorial/refreshing-shares.md @@ -6,23 +6,29 @@ channel](https://frost.zfnd.org/terminology.html#peer-to-peer-channel). -The Trusted Dealer needs to first run `compute_refreshing_shares` where the new SecretShares are generated and then verified. -This is done with -[`KeyPackage::try_from()`](https://docs.rs/frost-core/latest/frost_core/frost/keys/struct.KeyPackage.html#method.try_from): -`compute_refreshing_shares` returns a new SecretShare and PublicKeyPackage -Each new `SecretShare` and `PublicKeyPackage` must then be sent via an [**authenticated** and -**confidential** channel +The Trusted Dealer needs to first run `compute_refreshing_shares()` which +returns SecretShares (the "refreshing shares") and a PublicKeyPackage. Each +`SecretShare` must then be sent along with the `PublicKeyPackage` via an +[**authenticated** and **confidential** channel ](https://frost.zfnd.org/terminology.html#peer-to-peer-channel) for each -participant, who must verify the package to obtain a `KeyPackage` which contains -their signing share, verifying share and group verifying key. +participant. -Each Participant then runs `refresh_share` to generate a new `KeyPackage`. +Each Participant then runs `refresh_share()` to generate a new `KeyPackage` +which will replace their old `KeyPackage`; they must also replace their old +`PublicKeyPackage` with the one sent by the Trusted Dealer. ```admonish danger -The refreshed `KeyPackage` contents must be stored securely and the original +The refreshed `KeyPackage` contents must be stored securely and the original `KeyPackage` should be deleted. For example: - Make sure other users in the system can't read it; - If possible, use the OS secure storage such that the package contents can only be opened with the user's password or biometrics. ``` + +```admonish danger +Applications should first ensure that all participants who refreshed their +`KeyPackages` were actually able to do so successfully, before deleting their old +`KeyPackages`. How this is done is up to the application; it might require +sucessfully generating a signature with all of those participants. +``` From b73e6e3d7679aea228d4cd59b5f114bfcf14bae2 Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Thu, 18 Jul 2024 19:26:37 -0300 Subject: [PATCH 18/19] always return error in detect_cheater --- frost-core/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index c7cc3df6..873b8d88 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -680,5 +680,6 @@ fn detect_cheater( )?; } - Ok(()) + // We should never reach here; but we return an error to be safe. + Err(Error::InvalidSignature) } From 52806ec4182b311e0cb194f7d8c31588d4ac60bb Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Thu, 18 Jul 2024 19:28:59 -0300 Subject: [PATCH 19/19] add changelog entry --- frost-core/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index abd9af08..515941b5 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -4,6 +4,9 @@ Entries are listed in reverse chronological order. ## Unreleased +* Added refresh share functionality for trusted dealer: + `frost_core::keys::refresh::{compute_refreshing_shares, refresh_share}` + ## 2.0.0-rc.0 * Changed the `deserialize()` function of Elements and structs containing