diff --git a/frost-core/CHANGELOG.md b/frost-core/CHANGELOG.md index 099a3a96..140229a7 100644 --- a/frost-core/CHANGELOG.md +++ b/frost-core/CHANGELOG.md @@ -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 diff --git a/frost-core/src/frost/keys.rs b/frost-core/src/frost/keys.rs index 2b7e624d..7f21646e 100644 --- a/frost-core/src/frost/keys.rs +++ b/frost-core/src/frost/keys.rs @@ -787,8 +787,8 @@ pub(crate) fn generate_secret_shares( 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( /// 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( - secret_shares: &[SecretShare], + key_packages: &[KeyPackage], ) -> Result, Error> { - 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 = <::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 }) diff --git a/frost-core/src/tests/ciphersuite_generic.rs b/frost-core/src/tests/ciphersuite_generic.rs index bb1f5b7e..ac78e428 100644 --- a/frost-core/src/tests/ciphersuite_generic.rs +++ b/frost-core/src/tests/ciphersuite_generic.rs @@ -41,12 +41,14 @@ pub fn check_share_generation(mut rng: R ) .unwrap(); - for secret_share in secret_shares.iter() { - assert!(secret_share.verify().is_ok()); - } + let key_packages: Vec> = secret_shares + .iter() + .cloned() + .map(|s| s.try_into().unwrap()) + .collect(); assert_eq!( - frost::keys::reconstruct::(&secret_shares) + frost::keys::reconstruct::(&key_packages) .unwrap() .serialize() .as_ref(), @@ -60,11 +62,16 @@ pub fn check_share_generation(mut rng: R Error::IncorrectNumberOfShares ); - let mut secret_shares = secret_shares; - secret_shares[0] = secret_shares[1].clone(); + assert_eq!( + frost::keys::reconstruct::(&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::(&secret_shares).unwrap_err(), + frost::keys::reconstruct::(&key_packages).unwrap_err(), Error::DuplicatedIdentifier ); } diff --git a/frost-ed25519/src/lib.rs b/frost-ed25519/src/lib.rs index bfa982fb..47343084 100644 --- a/frost-ed25519/src/lib.rs +++ b/frost-ed25519/src/lib.rs @@ -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 { + pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result { frost::keys::reconstruct(secret_shares) } diff --git a/frost-ed448/src/lib.rs b/frost-ed448/src/lib.rs index d62f8182..e0adf39b 100644 --- a/frost-ed448/src/lib.rs +++ b/frost-ed448/src/lib.rs @@ -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 { + pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result { frost::keys::reconstruct(secret_shares) } diff --git a/frost-p256/src/lib.rs b/frost-p256/src/lib.rs index 7e3a90b5..6fe8c5f2 100644 --- a/frost-p256/src/lib.rs +++ b/frost-p256/src/lib.rs @@ -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 { + pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result { frost::keys::reconstruct(secret_shares) } diff --git a/frost-ristretto255/src/lib.rs b/frost-ristretto255/src/lib.rs index bb2771cb..32c1e6ef 100644 --- a/frost-ristretto255/src/lib.rs +++ b/frost-ristretto255/src/lib.rs @@ -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 { + pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result { frost::keys::reconstruct(secret_shares) } diff --git a/frost-secp256k1/src/lib.rs b/frost-secp256k1/src/lib.rs index a7a6edbf..22fb4c1d 100644 --- a/frost-secp256k1/src/lib.rs +++ b/frost-secp256k1/src/lib.rs @@ -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 { + pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result { frost::keys::reconstruct(secret_shares) }