From cfa8c990aa166623d4c596f2a4eb5638ab8a8848 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 7 Feb 2024 13:26:06 +0100 Subject: [PATCH] refactor: avoid using crypto primitives directly, part 2 --- ferveo/src/api.rs | 8 +-- ferveo/src/dkg.rs | 25 +++++--- ferveo/src/lib.rs | 99 +++++++++++++++++------------ ferveo/src/pvss.rs | 94 +++++++++++++--------------- ferveo/src/refresh.rs | 141 ++++++++++++++++++++++++++++++++---------- 5 files changed, 234 insertions(+), 133 deletions(-) diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs index ad9d23b6..01c6f0ab 100644 --- a/ferveo/src/api.rs +++ b/ferveo/src/api.rs @@ -351,8 +351,8 @@ impl AggregatedTranscript { self.0.make_decryption_share_simple_precomputed( &ciphertext_header.0, aad, - &validator_keypair.decryption_key, - dkg.0.me.share_index as usize, + validator_keypair, + dkg.0.me.share_index, &dkg.0.domain_points(), &dkg.0.pvss_params.g_inv(), ) @@ -368,8 +368,8 @@ impl AggregatedTranscript { let share = self.0.make_decryption_share_simple( &ciphertext_header.0, aad, - &validator_keypair.decryption_key, - dkg.0.me.share_index as usize, + validator_keypair, + dkg.0.me.share_index, &dkg.0.pvss_params.g_inv(), )?; let domain_point = dkg.0.get_domain_point(dkg.0.me.share_index)?; diff --git a/ferveo/src/dkg.rs b/ferveo/src/dkg.rs index b309449e..bc4acdfd 100644 --- a/ferveo/src/dkg.rs +++ b/ferveo/src/dkg.rs @@ -11,8 +11,8 @@ use serde_with::serde_as; use crate::{ aggregate, assert_no_share_duplicates, AggregatedPvss, Error, - EthereumAddress, PubliclyVerifiableParams, PubliclyVerifiableSS, Result, - Validator, + EthereumAddress, PrivateKeyShare, PubliclyVerifiableParams, + PubliclyVerifiableSS, Result, Validator, }; #[derive(Copy, Clone, Debug, Serialize, Deserialize)] @@ -216,13 +216,24 @@ impl PubliclyVerifiableDkg { self.domain.elements().take(self.validators.len()).collect() } - /// `payload` is the content of the message + /// Return a private key for the share_index + pub fn get_private_key_share( + &self, + keypair: &ferveo_common::Keypair, + ) -> Result> { + // TODO: Use self.aggregate upon simplifying Message handling + let pvss_list = self.vss.values().cloned().collect::>(); + aggregate(&pvss_list) + .unwrap() + .decrypt_private_key_share(keypair, self.me.share_index) + } + pub fn verify_message( &self, sender: &Validator, - payload: &Message, + message: &Message, ) -> Result<()> { - match payload { + match message { Message::Deal(pvss) if matches!( self.state, @@ -650,8 +661,8 @@ mod test_aggregation { use crate::{dkg::*, test_common::*, DkgState, Message}; /// Test that if the security threshold is met, we can create a final key - #[test_case(4,4; "number of validators equal to the number of shares")] - #[test_case(4,6; "number of validators greater than the number of shares")] + #[test_case(4, 4; "number of validators equal to the number of shares")] + #[test_case(4, 6; "number of validators greater than the number of shares")] fn test_aggregate(shares_num: u32, validators_num: u32) { let security_threshold = shares_num - 1; let (mut dkg, _) = setup_dealt_dkg_with_n_validators( diff --git a/ferveo/src/lib.rs b/ferveo/src/lib.rs index 0e9cc3ca..8ef4c8e7 100644 --- a/ferveo/src/lib.rs +++ b/ferveo/src/lib.rs @@ -171,8 +171,8 @@ mod test_dkg_full { .make_decryption_share_simple( ciphertext_header, aad, - &validator_keypair.decryption_key, - validator.share_index as usize, + validator_keypair, + validator.share_index, &dkg.pvss_params.g_inv(), ) .unwrap() @@ -280,8 +280,8 @@ mod test_dkg_full { .make_decryption_share_simple_precomputed( &ciphertext.header().unwrap(), AAD, - &validator_keypair.decryption_key, - validator.share_index as usize, + validator_keypair, + validator.share_index, &domain_points, &dkg.pvss_params.g_inv(), ) @@ -305,7 +305,8 @@ mod test_dkg_full { assert_eq!(plaintext, MSG); } - #[test_case(4, 4; "number of validators equal to the number of shares")] + #[test_case(4, 4; "number of shares (validators) is a power of 2")] + #[test_case(7, 7; "number of shares (validators) is not a power of 2")] #[test_case(4, 6; "number of validators greater than the number of shares")] fn test_dkg_simple_tdec_share_verification( shares_num: u32, @@ -376,12 +377,21 @@ mod test_dkg_full { )); } - #[test] - fn test_dkg_simple_tdec_share_recovery() { + #[test_case(4, 4; "number of shares (validators) is a power of 2")] + #[test_case(7, 7; "number of shares (validators) is not a power of 2")] + #[test_case(4, 6; "number of validators greater than the number of shares")] + fn test_dkg_simple_tdec_share_recovery( + shares_num: u32, + validators_num: u32, + ) { let rng = &mut test_rng(); + let security_threshold = shares_num / 2 + 1; - let (dkg, validator_keypairs) = - setup_dealt_dkg_with(SECURITY_THRESHOLD, SHARES_NUM); + let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators( + security_threshold, + shares_num, + validators_num, + ); let public_key = &dkg.public_key(); let ciphertext = ferveo_tdec::encrypt::( SecretBox::new(MSG.to_vec()), @@ -400,6 +410,7 @@ mod test_dkg_full { ); // Remove one participant from the contexts and all nested structure + // TODO: Improve by removing a random participant and/or adding test cases let removed_validator_addr = dkg.validators.keys().last().unwrap().clone(); let mut remaining_validators = dkg.validators.clone(); @@ -427,7 +438,7 @@ mod test_dkg_full { &domain_points, &dkg.pvss_params.h.into_affine(), &x_r, - dkg.dkg_params.security_threshold() as usize, + dkg.dkg_params.security_threshold(), rng, ); (v_addr.clone(), deltas_i) @@ -451,19 +462,18 @@ mod test_dkg_full { .collect(); // Each validator uses their decryption key to update their share - let decryption_key = validator_keypairs + let validator_keypair = validator_keypairs .get(validator.share_index as usize) - .unwrap() - .decryption_key; + .unwrap(); // Creates updated private key shares - // TODO: Why not using dkg.aggregate()? + // TODO: Use self.aggregate upon simplifying Message handling let pvss_list = dkg.vss.values().cloned().collect::>(); let pvss_aggregated = aggregate(&pvss_list).unwrap(); pvss_aggregated .make_updated_private_key_share( - &decryption_key, - validator.share_index as usize, + validator_keypair, + validator.share_index, updates_for_participant.as_slice(), ) .unwrap() @@ -488,7 +498,7 @@ mod test_dkg_full { .iter() .enumerate() .map(|(share_index, validator_keypair)| { - // TODO: Why not using dkg.aggregate()? + // TODO: Use self.aggregate upon simplifying Message handling let pvss_list = dkg.vss.values().cloned().collect::>(); let pvss_aggregated = aggregate(&pvss_list).unwrap(); @@ -496,8 +506,8 @@ mod test_dkg_full { .make_decryption_share_simple( &ciphertext.header().unwrap(), AAD, - &validator_keypair.decryption_key, - share_index, + validator_keypair, + share_index as u32, &dkg.pvss_params.g_inv(), ) .unwrap() @@ -509,7 +519,7 @@ mod test_dkg_full { decryption_shares.push( DecryptionShareSimple::create( &new_validator_decryption_key, - &recovered_key_share, + &recovered_key_share.0, &ciphertext.header().unwrap(), AAD, &dkg.pvss_params.g_inv(), @@ -518,14 +528,15 @@ mod test_dkg_full { ); domain_points.push(x_r); - assert_eq!(domain_points.len(), SHARES_NUM as usize); - assert_eq!(decryption_shares.len(), SHARES_NUM as usize); + assert_eq!(domain_points.len(), validators_num as usize); + assert_eq!(decryption_shares.len(), validators_num as usize); - // Maybe parametrize this test with [1..] and [..threshold] - let domain_points = &domain_points[1..]; - let decryption_shares = &decryption_shares[1..]; - assert_eq!(domain_points.len(), SECURITY_THRESHOLD as usize); - assert_eq!(decryption_shares.len(), SECURITY_THRESHOLD as usize); + // TODO: Maybe parametrize this test with [1..] and [..threshold] + let domain_points = &domain_points[..security_threshold as usize]; + let decryption_shares = + &decryption_shares[..security_threshold as usize]; + assert_eq!(domain_points.len(), security_threshold as usize); + assert_eq!(decryption_shares.len(), security_threshold as usize); let lagrange = ferveo_tdec::prepare_combine_simple::(domain_points); let new_shared_secret = ferveo_tdec::share_combine_simple::( @@ -539,12 +550,21 @@ mod test_dkg_full { ); } - #[test] - fn test_dkg_simple_tdec_share_refreshing() { + #[test_case(4, 4; "number of shares (validators) is a power of 2")] + #[test_case(7, 7; "number of shares (validators) is not a power of 2")] + #[test_case(4, 6; "number of validators greater than the number of shares")] + fn test_dkg_simple_tdec_share_refreshing( + shares_num: u32, + validators_num: u32, + ) { let rng = &mut test_rng(); + let security_threshold = shares_num / 2 + 1; - let (dkg, validator_keypairs) = - setup_dealt_dkg_with(SECURITY_THRESHOLD, SHARES_NUM); + let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators( + security_threshold, + shares_num, + validators_num, + ); let public_key = &dkg.public_key(); let ciphertext = ferveo_tdec::encrypt::( SecretBox::new(MSG.to_vec()), @@ -571,7 +591,7 @@ mod test_dkg_full { ShareRefreshUpdate::make_share_updates_for_refresh( &dkg.domain_points(), &dkg.pvss_params.h.into_affine(), - dkg.dkg_params.security_threshold() as usize, + dkg.dkg_params.security_threshold(), rng, ); (v_addr.clone(), deltas_i) @@ -598,19 +618,18 @@ mod test_dkg_full { .collect(); // Each validator uses their decryption key to update their share - let decryption_key = validator_keypairs + let validator_keypair = validator_keypairs .get(validator.share_index as usize) - .unwrap() - .decryption_key; + .unwrap(); // Creates updated private key shares - // TODO: Why not using dkg.aggregate()? + // TODO: Use self.aggregate upon simplifying Message handling let pvss_list = dkg.vss.values().cloned().collect::>(); let pvss_aggregated = aggregate(&pvss_list).unwrap(); pvss_aggregated .make_updated_private_key_share( - &decryption_key, - validator.share_index as usize, + validator_keypair, + validator.share_index, updates_for_participant.as_slice(), ) .unwrap() @@ -640,10 +659,10 @@ mod test_dkg_full { .collect(); let lagrange = ferveo_tdec::prepare_combine_simple::( - &dkg.domain_points()[..SECURITY_THRESHOLD as usize], + &dkg.domain_points()[..security_threshold as usize], ); let new_shared_secret = ferveo_tdec::share_combine_simple::( - &decryption_shares[..SECURITY_THRESHOLD as usize], + &decryption_shares[..security_threshold as usize], &lagrange, ); diff --git a/ferveo/src/pvss.rs b/ferveo/src/pvss.rs index 1d68622e..39919f9a 100644 --- a/ferveo/src/pvss.rs +++ b/ferveo/src/pvss.rs @@ -6,9 +6,9 @@ use ark_poly::{ polynomial::univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Polynomial, }; +use ferveo_common::Keypair; use ferveo_tdec::{ - prepare_combine_simple, CiphertextHeader, DecryptionSharePrecomputed, - DecryptionShareSimple, + CiphertextHeader, DecryptionSharePrecomputed, DecryptionShareSimple, }; use itertools::Itertools; use rand::RngCore; @@ -24,6 +24,7 @@ use crate::{ }; /// These are the blinded evaluations of shares of a single random polynomial +// TODO: Are these really blinded like in tdec or encrypted? pub type ShareEncryptions = ::G2Affine; /// Marker struct for unaggregated PVSS transcripts @@ -302,82 +303,75 @@ impl PubliclyVerifiableSS { pub fn decrypt_private_key_share( &self, - validator_decryption_key: &E::ScalarField, - share_index: usize, + validator_keypair: &Keypair, + share_index: u32, ) -> Result> { // Decrypt private key share https://nikkolasg.github.io/ferveo/pvss.html#validator-decryption-of-private-key-shares - let private_key_share = self - .shares - .get(share_index) - .ok_or(Error::InvalidShareIndex(share_index as u32))? - .mul( - validator_decryption_key - .inverse() - .expect("Validator decryption key must have an inverse"), - ) - .into_affine(); - // TODO: Consider adding a from trait to simplify this conversion let private_key_share = - ferveo_tdec::PrivateKeyShare { private_key_share }; - Ok(PrivateKeyShare(private_key_share)) + self.shares + .get(share_index as usize) + .ok_or(Error::InvalidShareIndex(share_index))? + .mul( + validator_keypair.decryption_key.inverse().expect( + "Validator decryption key must have an inverse", + ), + ) + .into_affine(); + Ok(PrivateKeyShare(ferveo_tdec::PrivateKeyShare { + private_key_share, + })) } + /// Make a decryption share (simple variant) for a given ciphertext + /// With this method, we wrap the PrivateKeyShare method to avoid exposing the private key share pub fn make_decryption_share_simple( &self, ciphertext: &CiphertextHeader, aad: &[u8], - validator_decryption_key: &E::ScalarField, - share_index: usize, + validator_keypair: &Keypair, + share_index: u32, g_inv: &E::G1Prepared, ) -> Result> { - let private_key_share = self - .decrypt_private_key_share(validator_decryption_key, share_index)?; - DecryptionShareSimple::create( - validator_decryption_key, - &private_key_share.0, - ciphertext, - aad, - g_inv, - ) - .map_err(|e| e.into()) + self.decrypt_private_key_share(validator_keypair, share_index)? + .make_decryption_share_simple( + ciphertext, + aad, + validator_keypair, + g_inv, + ) } + /// Make a decryption share (precomputed variant) for a given ciphertext + /// With this method, we wrap the PrivateKeyShare method to avoid exposing the private key share pub fn make_decryption_share_simple_precomputed( &self, ciphertext_header: &CiphertextHeader, aad: &[u8], - validator_decryption_key: &E::ScalarField, - share_index: usize, + validator_keypair: &Keypair, + share_index: u32, domain_points: &[E::ScalarField], g_inv: &E::G1Prepared, ) -> Result> { - let private_key_share = self - .decrypt_private_key_share(validator_decryption_key, share_index)?; - - // We use the `prepare_combine_simple` function to precompute the lagrange coefficients - let lagrange_coeffs = prepare_combine_simple::(domain_points); - - DecryptionSharePrecomputed::new( - share_index, - validator_decryption_key, - &private_key_share.0, - ciphertext_header, - aad, - &lagrange_coeffs[share_index], - g_inv, - ) - .map_err(|e| e.into()) + self.decrypt_private_key_share(validator_keypair, share_index)? + .make_decryption_share_simple_precomputed( + ciphertext_header, + aad, + validator_keypair, + share_index, + domain_points, + g_inv, + ) } pub fn make_updated_private_key_share( &self, - validator_decryption_key: &E::ScalarField, - share_index: usize, + validator_keypair: &Keypair, + share_index: u32, share_updates: &[impl PrivateKeyShareUpdate], ) -> Result> { // Retrieve the private key share and apply the updates Ok(self - .decrypt_private_key_share(validator_decryption_key, share_index)? + .decrypt_private_key_share(validator_keypair, share_index)? .make_updated_key_share(share_updates)) } } diff --git a/ferveo/src/refresh.rs b/ferveo/src/refresh.rs index 761481fa..126e281a 100644 --- a/ferveo/src/refresh.rs +++ b/ferveo/src/refresh.rs @@ -3,17 +3,31 @@ use std::{ops::Mul, usize}; use ark_ec::{pairing::Pairing, CurveGroup}; use ark_ff::Zero; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial}; -use ferveo_tdec::lagrange_basis_at; +use ferveo_common::Keypair; +use ferveo_tdec::{ + lagrange_basis_at, prepare_combine_simple, CiphertextHeader, + DecryptionSharePrecomputed, DecryptionShareSimple, +}; use itertools::zip_eq; use rand_core::RngCore; use zeroize::ZeroizeOnDrop; +use crate::{Error, Result}; + +// TODO: Rename refresh.rs to key_share.rs? + type InnerPrivateKeyShare = ferveo_tdec::PrivateKeyShare; /// Private key share held by a participant in the DKG protocol. #[derive(Debug, Clone, PartialEq, Eq, ZeroizeOnDrop)] pub struct PrivateKeyShare(pub InnerPrivateKeyShare); +impl PrivateKeyShare { + pub fn new(private_key_share: InnerPrivateKeyShare) -> Self { + Self(private_key_share) + } +} + impl PrivateKeyShare { /// From PSS paper, section 4.2.3, (https://link.springer.com/content/pdf/10.1007/3-540-44750-4_27.pdf) pub fn make_updated_key_share( @@ -36,19 +50,64 @@ impl PrivateKeyShare { x_r: &E::ScalarField, domain_points: &[E::ScalarField], updated_private_shares: &[UpdatedPrivateKeyShare], - ) -> ferveo_tdec::PrivateKeyShare { + ) -> PrivateKeyShare { // Interpolate new shares to recover y_r let lagrange = lagrange_basis_at::(domain_points, x_r); let prods = zip_eq(updated_private_shares, lagrange) .map(|(y_j, l)| y_j.0.private_key_share.mul(l)); let y_r = prods.fold(E::G2::zero(), |acc, y_j| acc + y_j); - ferveo_tdec::PrivateKeyShare { + PrivateKeyShare(ferveo_tdec::PrivateKeyShare { private_key_share: y_r.into_affine(), - } + }) + } + + pub fn make_decryption_share_simple( + &self, + ciphertext: &CiphertextHeader, + aad: &[u8], + validator_keypair: &Keypair, + g_inv: &E::G1Prepared, + ) -> Result> { + DecryptionShareSimple::create( + &validator_keypair.decryption_key, + &self.0, + ciphertext, + aad, + g_inv, + ) + .map_err(|e| e.into()) + } + + pub fn make_decryption_share_simple_precomputed( + &self, + ciphertext_header: &CiphertextHeader, + aad: &[u8], + validator_keypair: &Keypair, + share_index: u32, + domain_points: &[E::ScalarField], + g_inv: &E::G1Prepared, + ) -> Result> { + // In precomputed variant, we offload the some of the decryption related computation to the server-side: + // We use the `prepare_combine_simple` function to precompute the lagrange coefficients + let lagrange_coeffs = prepare_combine_simple::(domain_points); + let lagrange_coeff = &lagrange_coeffs + .get(share_index as usize) + .ok_or(Error::InvalidShareIndex(share_index))?; + DecryptionSharePrecomputed::new( + share_index as usize, + &validator_keypair.decryption_key, + &self.0, + ciphertext_header, + aad, + lagrange_coeff, + g_inv, + ) + .map_err(|e| e.into()) } } /// An updated private key share, resulting from an intermediate step in a share recovery or refresh operation. +// TODO: After recovery, should we replace existing private key shares with updated ones? #[derive(Debug, Clone, PartialEq, Eq, ZeroizeOnDrop)] pub struct UpdatedPrivateKeyShare(InnerPrivateKeyShare); @@ -88,7 +147,7 @@ impl ShareRecoveryUpdate { domain_points: &[E::ScalarField], h: &E::G2Affine, x_r: &E::ScalarField, - threshold: usize, + threshold: u32, rng: &mut impl RngCore, ) -> Vec> { // Update polynomial has root at x_r @@ -120,7 +179,7 @@ impl ShareRefreshUpdate { pub fn make_share_updates_for_refresh( domain_points: &[E::ScalarField], h: &E::G2Affine, - threshold: usize, + threshold: u32, rng: &mut impl RngCore, ) -> Vec> { // Update polynomial has root at 0 @@ -147,7 +206,7 @@ fn prepare_share_updates_with_root( domain_points: &[E::ScalarField], h: &E::G2Affine, root: &E::ScalarField, - threshold: usize, + threshold: u32, rng: &mut impl RngCore, ) -> Vec> { // Generate a new random polynomial with defined root @@ -168,12 +227,13 @@ fn prepare_share_updates_with_root( /// Generate a random polynomial with a given root fn make_random_polynomial_with_root( - degree: usize, + degree: u32, root: &E::ScalarField, rng: &mut impl RngCore, ) -> DensePolynomial { // [c_0, c_1, ..., c_{degree}] (Random polynomial) - let mut poly = DensePolynomial::::rand(degree, rng); + let mut poly = + DensePolynomial::::rand(degree as usize, rng); // [0, c_1, ... , c_{degree}] (We zeroize the free term) poly[0] = E::ScalarField::zero(); @@ -184,7 +244,7 @@ fn make_random_polynomial_with_root( // Evaluating the polynomial at the root should result in 0 debug_assert!(poly.evaluate(root) == E::ScalarField::zero()); - debug_assert!(poly.coeffs.len() == degree + 1); + debug_assert!(poly.coeffs.len() == (degree + 1) as usize); poly } @@ -199,16 +259,19 @@ mod tests_refresh { test_common::setup_simple, PrivateDecryptionContextSimple, }; use rand_core::RngCore; - use test_case::test_matrix; + use test_case::{test_case, test_matrix}; use crate::{ test_common::*, PrivateKeyShare, ShareRecoveryUpdate, ShareRefreshUpdate, UpdatedPrivateKeyShare, }; + /// Using tdec test utilities here instead of PVSS to test the internals of the shared key recovery + // TODO: Can I fix that using combine_shared_secret? + fn make_updated_private_key_shares( rng: &mut R, - threshold: usize, + threshold: u32, x_r: &Fr, remaining_participants: &[PrivateDecryptionContextSimple], ) -> Vec> { @@ -257,13 +320,20 @@ mod tests_refresh { /// Ñ parties (where t <= Ñ <= N) jointly execute a "share recovery" algorithm, and the output is 1 new share. /// The new share is intended to restore a previously existing share, e.g., due to loss or corruption. - #[test_matrix([4, 7, 11, 16])] - fn tdec_simple_variant_share_recovery_at_selected_point(shares_num: usize) { + #[test_case(4, 4; "number of shares (validators) is a power of 2")] + #[test_case(7, 7; "number of shares (validators) is not a power of 2")] + fn tdec_simple_variant_share_recovery_at_selected_point( + shares_num: u32, + _validators_num: u32, + ) { let rng = &mut test_rng(); let security_threshold = shares_num * 2 / 3; - let (_, _, mut contexts) = - setup_simple::(security_threshold, shares_num, rng); + let (_, _, mut contexts) = setup_simple::( + security_threshold as usize, + shares_num as usize, + rng, + ); // Prepare participants @@ -274,7 +344,8 @@ mod tests_refresh { .last() .unwrap() .domain; - let original_private_key_share = selected_participant.private_key_share; + let original_private_key_share = + PrivateKeyShare(selected_participant.private_key_share); // Remove the selected participant from the contexts and all nested structures let mut remaining_participants = contexts; @@ -299,8 +370,8 @@ mod tests_refresh { let new_private_key_share = PrivateKeyShare::recover_share_from_updated_private_shares( &x_r, - &domain_points[..security_threshold], - &updated_private_key_shares[..security_threshold], + &domain_points[..security_threshold as usize], + &updated_private_key_shares[..security_threshold as usize], ); assert_eq!(new_private_key_share, original_private_key_share); @@ -310,8 +381,9 @@ mod tests_refresh { let incorrect_private_key_share = PrivateKeyShare::recover_share_from_updated_private_shares( &x_r, - &domain_points[..(security_threshold - 1)], - &updated_private_key_shares[..(security_threshold - 1)], + &domain_points[..(security_threshold - 1) as usize], + &updated_private_key_shares + [..(security_threshold - 1) as usize], ); assert_ne!(incorrect_private_key_share, original_private_key_share); @@ -319,13 +391,17 @@ mod tests_refresh { /// Ñ parties (where t <= Ñ <= N) jointly execute a "share recovery" algorithm, and the output is 1 new share. /// The new share is independent from the previously existing shares. We can use this to on-board a new participant into an existing cohort. - #[test_matrix([4, 7, 11, 16])] - fn tdec_simple_variant_share_recovery_at_random_point(shares_num: usize) { + #[test_case(4, 4; "number of shares (validators) is a power of 2")] + #[test_case(7, 7; "number of shares (validators) is not a power of 2")] + fn tdec_simple_variant_share_recovery_at_random_point( + shares_num: u32, + _validators_num: u32, + ) { let rng = &mut test_rng(); let threshold = shares_num * 2 / 3; let (_, shared_private_key, mut contexts) = - setup_simple::(threshold, shares_num, rng); + setup_simple::(threshold as usize, shares_num as usize, rng); // Prepare participants @@ -358,8 +434,8 @@ mod tests_refresh { let recovered_private_key_share = PrivateKeyShare::recover_share_from_updated_private_shares( &x_r, - &domain_points[..threshold], - &share_recovery_fragmetns[..threshold], + &domain_points[..threshold as usize], + &share_recovery_fragmetns[..threshold as usize], ); let mut private_shares = contexts @@ -370,7 +446,8 @@ mod tests_refresh { // Finally, let's recreate the shared private key from some original shares and the recovered one domain_points.push(x_r); - private_shares.push(recovered_private_key_share); + private_shares.push(recovered_private_key_share.0.clone()); + // This is a workaround for a type mismatch - We need to convert the private shares to updated private shares // This is just to test that we are able to recover the shared private key from the updated private shares let updated_private_key_shares = private_shares @@ -382,11 +459,11 @@ mod tests_refresh { let new_shared_private_key = PrivateKeyShare::recover_share_from_updated_private_shares( &ScalarField::zero(), - &domain_points[start_from..], - &updated_private_key_shares[start_from..], + &domain_points[start_from as usize..], + &updated_private_key_shares[start_from as usize..], ); - assert_eq!(shared_private_key, new_shared_private_key); + assert_eq!(shared_private_key, new_shared_private_key.0); } /// Ñ parties (where t <= Ñ <= N) jointly execute a "share refresh" algorithm. @@ -415,7 +492,7 @@ mod tests_refresh { ShareRefreshUpdate::::make_share_updates_for_refresh( domain_points, &h, - threshold, + threshold as u32, rng, ); (p.index, deltas_i) @@ -447,6 +524,6 @@ mod tests_refresh { &refreshed_shares[..threshold], ); - assert_eq!(private_key_share, new_shared_private_key); + assert_eq!(private_key_share, new_shared_private_key.0); } }