change reconstruct() to take KeyPackages; validate size (#523)

change reconstruct() to take KeyPackages; validate size
This commit is contained in:
Conrado Gouvea 2023-09-11 18:51:33 -03:00 committed by GitHub
parent 366cc96877
commit 9752182fa1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 42 additions and 21 deletions

View File

@ -11,6 +11,8 @@ Entries are listed in reverse chronological order.
running previous versions.
* A new `min_signers` field was added to `KeyPackage`, which changes its
`new()` method and its serde serialization.
* `reconstruct()` was changed to take a slice of `KeyPackage`s instead of
`SecretShare`s since users are expect to store the former and not the latter.
## Released

View File

@ -787,8 +787,8 @@ pub(crate) fn generate_secret_shares<C: Ciphersuite>(
Ok(secret_shares)
}
/// Recompute the secret from at least `min_signers` secret shares
/// using Lagrange interpolation.
/// Recompute the secret from at least `min_signers` secret shares (inside
/// [`KeyPackage`]s) using Lagrange interpolation.
///
/// This can be used if for some reason the original key must be restored; e.g.
/// if threshold signing is not required anymore.
@ -797,34 +797,46 @@ pub(crate) fn generate_secret_shares<C: Ciphersuite>(
/// able to generate signatures only using the shares, without having to
/// reconstruct the original key.
///
/// The caller is responsible for providing at least `min_signers` shares;
/// The caller is responsible for providing at least `min_signers` packages;
/// if less than that is provided, a different key will be returned.
pub fn reconstruct<C: Ciphersuite>(
secret_shares: &[SecretShare<C>],
key_packages: &[KeyPackage<C>],
) -> Result<SigningKey<C>, Error<C>> {
if secret_shares.is_empty() {
if key_packages.is_empty() {
return Err(Error::IncorrectNumberOfShares);
}
// There is no obvious way to get `min_signers` in order to validate the
// size of `secret_shares`. Since that is just a best-effort validation,
// we don't need to worry too much about adversarial situations where people
// lie about min_signers, so just get the minimum value out of all of them.
let min_signers = key_packages
.iter()
.map(|k| k.min_signers)
.min()
.expect("should not be empty since that was just tested");
if key_packages.len() < min_signers as usize {
return Err(Error::IncorrectNumberOfShares);
}
let mut secret = <<C::Group as Group>::Field>::zero();
let identifiers: BTreeSet<_> = secret_shares
let identifiers: BTreeSet<_> = key_packages
.iter()
.map(|s| s.identifier())
.cloned()
.collect();
if identifiers.len() != secret_shares.len() {
if identifiers.len() != key_packages.len() {
return Err(Error::DuplicatedIdentifier);
}
// Compute the Lagrange coefficients
for secret_share in secret_shares.iter() {
for secret_share in key_packages.iter() {
let lagrange_coefficient =
compute_lagrange_coefficient(&identifiers, None, secret_share.identifier)?;
// Compute y = f(0) via polynomial interpolation of these t-of-n solutions ('points) of f
secret = secret + (lagrange_coefficient * secret_share.value.0);
secret = secret + (lagrange_coefficient * secret_share.secret_share().0);
}
Ok(SigningKey { scalar: secret })

View File

@ -41,12 +41,14 @@ pub fn check_share_generation<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R
)
.unwrap();
for secret_share in secret_shares.iter() {
assert!(secret_share.verify().is_ok());
}
let key_packages: Vec<frost::keys::KeyPackage<C>> = secret_shares
.iter()
.cloned()
.map(|s| s.try_into().unwrap())
.collect();
assert_eq!(
frost::keys::reconstruct::<C>(&secret_shares)
frost::keys::reconstruct::<C>(&key_packages)
.unwrap()
.serialize()
.as_ref(),
@ -60,11 +62,16 @@ pub fn check_share_generation<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R
Error::IncorrectNumberOfShares
);
let mut secret_shares = secret_shares;
secret_shares[0] = secret_shares[1].clone();
assert_eq!(
frost::keys::reconstruct::<C>(&key_packages[0..1]).unwrap_err(),
Error::IncorrectNumberOfShares
);
let mut key_packages = key_packages;
key_packages[0] = key_packages[1].clone();
assert_eq!(
frost::keys::reconstruct::<C>(&secret_shares).unwrap_err(),
frost::keys::reconstruct::<C>(&key_packages).unwrap_err(),
Error::DuplicatedIdentifier
);
}

View File

@ -266,7 +266,7 @@ pub mod keys {
///
/// The caller is responsible for providing at least `min_signers` shares;
/// if less than that is provided, a different key will be returned.
pub fn reconstruct(secret_shares: &[SecretShare]) -> Result<SigningKey, Error> {
pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result<SigningKey, Error> {
frost::keys::reconstruct(secret_shares)
}

View File

@ -260,7 +260,7 @@ pub mod keys {
///
/// The caller is responsible for providing at least `min_signers` shares;
/// if less than that is provided, a different key will be returned.
pub fn reconstruct(secret_shares: &[SecretShare]) -> Result<SigningKey, Error> {
pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result<SigningKey, Error> {
frost::keys::reconstruct(secret_shares)
}

View File

@ -292,7 +292,7 @@ pub mod keys {
///
/// The caller is responsible for providing at least `min_signers` shares;
/// if less than that is provided, a different key will be returned.
pub fn reconstruct(secret_shares: &[SecretShare]) -> Result<SigningKey, Error> {
pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result<SigningKey, Error> {
frost::keys::reconstruct(secret_shares)
}

View File

@ -251,7 +251,7 @@ pub mod keys {
///
/// The caller is responsible for providing at least `min_signers` shares;
/// if less than that is provided, a different key will be returned.
pub fn reconstruct(secret_shares: &[SecretShare]) -> Result<SigningKey, Error> {
pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result<SigningKey, Error> {
frost::keys::reconstruct(secret_shares)
}

View File

@ -291,7 +291,7 @@ pub mod keys {
///
/// The caller is responsible for providing at least `min_signers` shares;
/// if less than that is provided, a different key will be returned.
pub fn reconstruct(secret_shares: &[SecretShare]) -> Result<SigningKey, Error> {
pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result<SigningKey, Error> {
frost::keys::reconstruct(secret_shares)
}