Skip to content

Commit

Permalink
Add refresh shares with dealer functionality (#245)
Browse files Browse the repository at this point in the history
  • Loading branch information
natalieesk committed May 29, 2024
1 parent 3ebe7a1 commit e450420
Show file tree
Hide file tree
Showing 17 changed files with 792 additions and 0 deletions.
1 change: 1 addition & 0 deletions frost-core/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
119 changes: 119 additions & 0 deletions frost-core/src/keys/refresh.rs
Original file line number Diff line number Diff line change
@@ -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<C: Ciphersuite, R: RngCore + CryptoRng>(
old_shares: BTreeMap<Identifier<C>, SecretShare<C>>,
old_pub_key_package: PublicKeyPackage<C>,
max_signers: u16,
min_signers: u16,
identifiers: &[Identifier<C>],
rng: &mut R,
) -> Result<(BTreeMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>), Error<C>> {
// 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: <<C::Group as Group>::Field>::zero(),
};

let coefficients = generate_coefficients::<C, R>(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<Identifier<C>, SecretShare<C>> = BTreeMap::new();
let mut verifying_shares: BTreeMap<Identifier<C>, VerifyingShare<C>> = 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::<C>(share.clone(), old_share)?,
),
None => return Err(Error::UnknownIdentifier),
};
}

let pub_key_package = PublicKeyPackage::<C> {
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<C: Ciphersuite>(
zero_share: SecretShare<C>,
old_share: &SecretShare<C>,
) -> Result<SecretShare<C>, Error<C>> {
let signing_share: Scalar<C> =
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<CoefficientCommitment<C>> = 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,
})
}
1 change: 1 addition & 0 deletions frost-core/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
128 changes: 128 additions & 0 deletions frost-core/src/tests/refresh.rs
Original file line number Diff line number Diff line change
@@ -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<C: Ciphersuite, R: RngCore + CryptoRng>(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::Identifier<C>, frost::keys::KeyPackage<C>> =
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<C>],
error: Error<C>,
mut rng: R,
) {
let (old_shares, pub_key_package) = build_old_shares::<C, R>(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<C: Ciphersuite, R: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
mut rng: &mut R,
) -> (BTreeMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>) {
// 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<Identifier<C>, SecretShare<C>>,
PublicKeyPackage<C>,
) = 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)
}
30 changes: 30 additions & 0 deletions frost-ed25519/src/keys/refresh.rs
Original file line number Diff line number Diff line change
@@ -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<C: Ciphersuite, R: RngCore + CryptoRng>(
current_shares: BTreeMap<Identifier, SecretShare>,
old_pub_key_package: PublicKeyPackage,
max_signers: u16,
min_signers: u16,
identifiers: &[Identifier],
mut rng: &mut R,
) -> Result<(BTreeMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::refresh::refresh_shares_with_dealer(
current_shares,
old_pub_key_package,
max_signers,
min_signers,
identifiers,
&mut rng,
)
}
78 changes: 78 additions & 0 deletions frost-ed25519/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,84 @@ fn check_rts() {
frost_core::tests::repairable::check_rts::<Ed25519Sha512, _>(rng);
}

#[test]
fn check_refresh_shares_with_dealer() {
let rng = thread_rng();

frost_core::tests::refresh::check_refresh_shares_with_dealer::<Ed25519Sha512, _>(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<Ed25519Sha512> = 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<Ed25519Sha512> = 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();
Expand Down
Loading

0 comments on commit e450420

Please sign in to comment.