refactor Lagrange coefficient computation (#436)

* refactor Lagrange coefficient computation

* A line

* Apply suggestions from code review

Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>

* address review comments; make compute_lagrange_coefficients() not pub by default

---------

Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>
This commit is contained in:
Conrado Gouvea 2023-07-19 13:47:09 -03:00 committed by GitHub
parent 46527085d5
commit 9b5d88da72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 132 additions and 114 deletions

View File

@ -24,8 +24,14 @@ pub enum Error<C: Ciphersuite> {
#[error("Malformed identifier is unserializable.")] #[error("Malformed identifier is unserializable.")]
MalformedIdentifier, MalformedIdentifier,
/// This identifier is duplicated. /// This identifier is duplicated.
#[error("Duplicated identifier.")] #[error("Duplicated identifiers.")]
DuplicatedIdentifier, DuplicatedIdentifiers,
/// This identifier does not belong to a participant in the signing process.
#[error("Unknown identifier.")]
UnknownIdentifier,
/// Incorrect number of identifiers.
#[error("Incorrect number of identifiers.")]
IncorrectNumberOfIdentifiers,
/// The encoding of a signing key was malformed. /// The encoding of a signing key was malformed.
#[error("Malformed signing key encoding.")] #[error("Malformed signing key encoding.")]
MalformedSigningKey, MalformedSigningKey,
@ -125,8 +131,10 @@ where
| Error::DKGNotSupported | Error::DKGNotSupported
| Error::FieldError(_) | Error::FieldError(_)
| Error::GroupError(_) | Error::GroupError(_)
| Error::DuplicatedIdentifier | Error::DuplicatedIdentifiers
| Error::InvalidCoefficient | Error::InvalidCoefficient
| Error::UnknownIdentifier
| Error::IncorrectNumberOfIdentifiers
| Error::IdentifierDerivationNotSupported => None, | Error::IdentifierDerivationNotSupported => None,
} }
} }

View File

@ -11,7 +11,7 @@
//! Sharing, where shares are generated using Shamir Secret Sharing. //! Sharing, where shares are generated using Shamir Secret Sharing.
use std::{ use std::{
collections::{BTreeMap, HashMap}, collections::{BTreeMap, BTreeSet, HashMap},
fmt::{self, Debug}, fmt::{self, Debug},
ops::Index, ops::Index,
}; };
@ -148,34 +148,69 @@ where
} }
} }
/// Generates the lagrange coefficient for the i'th participant. /// Generates a lagrange coefficient.
///
/// The Lagrange polynomial for a set of points (x_j, y_j) for 0 <= j <= k
/// is ∑_{i=0}^k y_i._i(x), where _i(x) is the Lagrange basis polynomial:
///
/// _i(x) = ∏_{0≤j≤k; j≠i} (x - x_j) / (x_i - x_j).
///
/// This computes _j(x) for the set of points `xs` and for the j corresponding
/// to the given xj.
///
/// If `x` is None, it uses 0 for it (since Identifiers can't be 0)
#[cfg_attr(feature = "internals", visibility::make(pub))]
fn compute_lagrange_coefficient<C: Ciphersuite>(
x_set: &BTreeSet<Identifier<C>>,
x: Option<Identifier<C>>,
x_i: Identifier<C>,
) -> Result<Scalar<C>, Error<C>> {
if x_set.is_empty() {
return Err(Error::IncorrectNumberOfIdentifiers);
}
let mut num = <<C::Group as Group>::Field>::one();
let mut den = <<C::Group as Group>::Field>::one();
let mut x_i_found = false;
for x_j in x_set.iter() {
if x_i == *x_j {
x_i_found = true;
continue;
}
if let Some(x) = x {
num *= x - *x_j;
den *= x_i - *x_j;
} else {
// Both signs inverted just to avoid requiring Neg (-*xj)
num *= *x_j;
den *= *x_j - x_i;
}
}
if !x_i_found {
return Err(Error::UnknownIdentifier);
}
Ok(num
* <<C::Group as Group>::Field>::invert(&den).map_err(|_| Error::DuplicatedIdentifiers)?)
}
/// Generates the lagrange coefficient for the i'th participant (for `signer_id`).
#[cfg_attr(feature = "internals", visibility::make(pub))] #[cfg_attr(feature = "internals", visibility::make(pub))]
fn derive_interpolating_value<C: Ciphersuite>( fn derive_interpolating_value<C: Ciphersuite>(
signer_id: &Identifier<C>, signer_id: &Identifier<C>,
signing_package: &SigningPackage<C>, signing_package: &SigningPackage<C>,
) -> Result<Scalar<C>, Error<C>> { ) -> Result<Scalar<C>, Error<C>> {
let zero = <<C::Group as Group>::Field>::zero(); compute_lagrange_coefficient(
&signing_package
let mut num = <<C::Group as Group>::Field>::one(); .signing_commitments()
let mut den = <<C::Group as Group>::Field>::one(); .keys()
.cloned()
for commitment_identifier in signing_package.signing_commitments().keys() { .collect(),
if *commitment_identifier == *signer_id { None,
continue; *signer_id,
} )
num *= *commitment_identifier;
den *= *commitment_identifier - *signer_id;
}
if den == zero {
return Err(Error::DuplicatedShares);
}
let lagrange_coeff = num * <<C::Group as Group>::Field>::invert(&den).unwrap();
Ok(lagrange_coeff)
} }
/// Generated by the coordinator of the signing operation and distributed to /// Generated by the coordinator of the signing operation and distributed to

View File

@ -2,7 +2,7 @@
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{BTreeSet, HashMap, HashSet},
convert::TryFrom, convert::TryFrom,
default::Default, default::Default,
fmt::{self, Debug}, fmt::{self, Debug},
@ -23,6 +23,8 @@ use crate::{
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use crate::{ElementSerialization, ScalarSerialization}; use crate::{ElementSerialization, ScalarSerialization};
use super::compute_lagrange_coefficient;
pub mod dkg; pub mod dkg;
pub mod repairable; pub mod repairable;
@ -716,7 +718,7 @@ pub(crate) fn generate_secret_shares<C: Ciphersuite>(
let identifiers_set: HashSet<_> = identifiers.iter().collect(); let identifiers_set: HashSet<_> = identifiers.iter().collect();
if identifiers_set.len() != identifiers.len() { if identifiers_set.len() != identifiers.len() {
return Err(Error::DuplicatedIdentifier); return Err(Error::DuplicatedIdentifiers);
} }
for id in identifiers { for id in identifiers {
@ -754,39 +756,20 @@ pub fn reconstruct<C: Ciphersuite>(
let mut secret = <<C::Group as Group>::Field>::zero(); let mut secret = <<C::Group as Group>::Field>::zero();
// Compute the Lagrange coefficients let identifiers: BTreeSet<_> = secret_shares
for (i_idx, i, secret_share) in secret_shares
.iter() .iter()
.enumerate() .map(|s| s.identifier())
.map(|(idx, s)| (idx, s.identifier, s)) .cloned()
{ .collect();
let mut num = <<C::Group as Group>::Field>::one();
let mut den = <<C::Group as Group>::Field>::one();
for (j_idx, j) in secret_shares if identifiers.len() != secret_shares.len() {
.iter() return Err(Error::DuplicatedIdentifiers);
.enumerate() }
.map(|(idx, s)| (idx, s.identifier))
{
if j_idx == i_idx {
continue;
}
// numerator *= j // Compute the Lagrange coefficients
num *= j; for secret_share in secret_shares.iter() {
let lagrange_coefficient =
// denominator *= j - i compute_lagrange_coefficient(&identifiers, None, secret_share.identifier)?;
den *= j - i;
}
// If at this step, the denominator is zero in the scalar field, there must be a duplicate
// secret share.
if den == <<C::Group as Group>::Field>::zero() {
return Err(Error::DuplicatedShares);
}
// Save numerator * 1/denominator in the scalar field
let lagrange_coefficient = num * <<C::Group as Group>::Field>::invert(&den).unwrap();
// Compute y = f(0) via polynomial interpolation of these t-of-n solutions ('points) of f // 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.value.0);

View File

@ -4,9 +4,12 @@
//! The RTS is used to help a signer (participant) repair their lost share. This is achieved //! The RTS is used to help a signer (participant) repair their lost share. This is achieved
//! using a subset of the other signers know here as `helpers`. //! using a subset of the other signers know here as `helpers`.
use std::collections::HashMap; use std::collections::{BTreeSet, HashMap};
use crate::{frost::Identifier, Ciphersuite, CryptoRng, Field, Group, RngCore, Scalar}; use crate::{
frost::{compute_lagrange_coefficient, Identifier},
Ciphersuite, CryptoRng, Error, Field, Group, RngCore, Scalar,
};
use super::{generate_coefficients, SecretShare, SigningShare, VerifiableSecretSharingCommitment}; use super::{generate_coefficients, SecretShare, SigningShare, VerifiableSecretSharingCommitment};
@ -22,10 +25,18 @@ pub fn repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(
share_i: &SecretShare<C>, share_i: &SecretShare<C>,
rng: &mut R, rng: &mut R,
participant: Identifier<C>, participant: Identifier<C>,
) -> HashMap<Identifier<C>, Scalar<C>> { ) -> Result<HashMap<Identifier<C>, Scalar<C>>, Error<C>> {
if helpers.is_empty() {
return Err(Error::IncorrectNumberOfIdentifiers);
}
let xset: BTreeSet<_> = helpers.iter().cloned().collect();
if xset.len() != helpers.len() {
return Err(Error::DuplicatedIdentifiers);
}
let rand_val: Vec<Scalar<C>> = generate_coefficients::<C, R>(helpers.len() - 1, rng); let rand_val: Vec<Scalar<C>> = generate_coefficients::<C, R>(helpers.len() - 1, rng);
compute_last_random_value(helpers, share_i, &rand_val, participant) compute_last_random_value(&xset, share_i, &rand_val, participant)
} }
/// Compute the last delta value given the (generated uniformly at random) remaining ones /// Compute the last delta value given the (generated uniformly at random) remaining ones
@ -33,13 +44,13 @@ pub fn repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(
/// ///
/// Returns a HashMap mapping which value should be sent to which participant. /// Returns a HashMap mapping which value should be sent to which participant.
fn compute_last_random_value<C: Ciphersuite>( fn compute_last_random_value<C: Ciphersuite>(
helpers: &[Identifier<C>], helpers: &BTreeSet<Identifier<C>>,
share_i: &SecretShare<C>, share_i: &SecretShare<C>,
random_values: &Vec<Scalar<C>>, random_values: &Vec<Scalar<C>>,
participant: Identifier<C>, participant: Identifier<C>,
) -> HashMap<Identifier<C>, Scalar<C>> { ) -> Result<HashMap<Identifier<C>, Scalar<C>>, Error<C>> {
// Calculate Lagrange Coefficient for helper_i // Calculate Lagrange Coefficient for helper_i
let zeta_i = compute_lagrange_coefficient(helpers, participant, share_i.identifier); let zeta_i = compute_lagrange_coefficient(helpers, Some(participant), share_i.identifier)?;
let lhs = zeta_i * share_i.value.0; let lhs = zeta_i * share_i.value.0;
@ -55,32 +66,9 @@ fn compute_last_random_value<C: Ciphersuite>(
sum_i_deltas = sum_i_deltas + *v; sum_i_deltas = sum_i_deltas + *v;
} }
out.insert(helpers[helpers.len() - 1], lhs - sum_i_deltas); out.insert(*helpers.last().unwrap(), lhs - sum_i_deltas);
out Ok(out)
}
/// Compute the i-th Lagrange coefficient evaluated at `participant`, i.e.
/// computes `zeta_i` such that f(participant) is the sum of all `zeta_i * share_i`
/// for each `i` in `helpers`.
pub fn compute_lagrange_coefficient<C: Ciphersuite>(
helpers: &[Identifier<C>],
participant: Identifier<C>,
helper_i: Identifier<C>,
) -> Scalar<C> {
let mut num = <<C::Group as Group>::Field>::one();
let mut den = <<C::Group as Group>::Field>::one();
for j in helpers.iter() {
if helper_i == *j {
continue;
}
num *= participant - *j;
den *= helper_i - *j;
}
num * <<C::Group as Group>::Field>::invert(&den).unwrap()
} }
/// Communication round /// Communication round

View File

@ -55,7 +55,7 @@ pub fn check_share_generation<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R
assert_eq!( assert_eq!(
frost::keys::reconstruct::<C>(&secret_shares).unwrap_err(), frost::keys::reconstruct::<C>(&secret_shares).unwrap_err(),
Error::DuplicatedShares Error::DuplicatedIdentifiers
); );
} }
@ -372,7 +372,7 @@ pub fn check_sign_with_dealer_and_identifiers<C: Ciphersuite, R: RngCore + Crypt
&mut rng, &mut rng,
) )
.unwrap_err(); .unwrap_err();
assert_eq!(err, Error::DuplicatedIdentifier); assert_eq!(err, Error::DuplicatedIdentifiers);
// Check correct case // Check correct case

View File

@ -8,12 +8,9 @@ use serde_json::Value;
use crate::{ use crate::{
frost::{ frost::{
self, self, compute_lagrange_coefficient,
keys::{ keys::{
repairable::{ repairable::{repair_share_step_1, repair_share_step_2, repair_share_step_3},
compute_lagrange_coefficient, repair_share_step_1, repair_share_step_2,
repair_share_step_3,
},
PublicKeyPackage, SecretShare, SigningShare, PublicKeyPackage, SecretShare, SigningShare,
}, },
Identifier, Identifier,
@ -58,9 +55,12 @@ pub fn check_rts<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
// Each helper generates random values for each helper // Each helper generates random values for each helper
let helper_1_deltas = repair_share_step_1(&helpers, helper_1, &mut rng, participant.identifier); let helper_1_deltas =
let helper_4_deltas = repair_share_step_1(&helpers, helper_4, &mut rng, participant.identifier); repair_share_step_1(&helpers, helper_1, &mut rng, participant.identifier).unwrap();
let helper_5_deltas = repair_share_step_1(&helpers, helper_5, &mut rng, participant.identifier); let helper_4_deltas =
repair_share_step_1(&helpers, helper_4, &mut rng, participant.identifier).unwrap();
let helper_5_deltas =
repair_share_step_1(&helpers, helper_5, &mut rng, participant.identifier).unwrap();
// Each helper calculates their sigma from the random values received from the other helpers // Each helper calculates their sigma from the random values received from the other helpers
@ -130,10 +130,14 @@ pub fn check_repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng
]; ];
// Generate deltas for helper 4 // Generate deltas for helper 4
let deltas = repair_share_step_1(&helpers, helper_4, &mut rng, participant.identifier); let deltas = repair_share_step_1(&helpers, helper_4, &mut rng, participant.identifier).unwrap();
let lagrange_coefficient = let lagrange_coefficient = compute_lagrange_coefficient(
compute_lagrange_coefficient(&helpers, participant.identifier, helpers[1]); &helpers.iter().cloned().collect(),
Some(participant.identifier),
helpers[1],
)
.unwrap();
let mut rhs = <<C::Group as Group>::Field>::zero(); let mut rhs = <<C::Group as Group>::Field>::zero();
for (_k, v) in deltas { for (_k, v) in deltas {

View File

@ -9,8 +9,8 @@ use std::collections::HashMap;
// This is imported separately to make `gencode` work. // This is imported separately to make `gencode` work.
// (if it were below, the position of the import would vary between ciphersuites // (if it were below, the position of the import would vary between ciphersuites
// after `cargo fmt`) // after `cargo fmt`)
use crate::Ed25519Sha512;
use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar};
use crate::{Ed25519Sha512, Error};
use super::{SecretShare, VerifiableSecretSharingCommitment}; use super::{SecretShare, VerifiableSecretSharingCommitment};
@ -26,7 +26,7 @@ pub fn repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(
share_i: &SecretShare, share_i: &SecretShare,
rng: &mut R, rng: &mut R,
participant: Identifier, participant: Identifier,
) -> HashMap<Identifier, Scalar> { ) -> Result<HashMap<Identifier, Scalar>, Error> {
frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant)
} }

View File

@ -9,8 +9,8 @@ use std::collections::HashMap;
// This is imported separately to make `gencode` work. // This is imported separately to make `gencode` work.
// (if it were below, the position of the import would vary between ciphersuites // (if it were below, the position of the import would vary between ciphersuites
// after `cargo fmt`) // after `cargo fmt`)
use crate::Ed448Shake256;
use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar};
use crate::{Ed448Shake256, Error};
use super::{SecretShare, VerifiableSecretSharingCommitment}; use super::{SecretShare, VerifiableSecretSharingCommitment};
@ -26,7 +26,7 @@ pub fn repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(
share_i: &SecretShare, share_i: &SecretShare,
rng: &mut R, rng: &mut R,
participant: Identifier, participant: Identifier,
) -> HashMap<Identifier, Scalar> { ) -> Result<HashMap<Identifier, Scalar>, Error> {
frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant)
} }

View File

@ -9,8 +9,8 @@ use std::collections::HashMap;
// This is imported separately to make `gencode` work. // This is imported separately to make `gencode` work.
// (if it were below, the position of the import would vary between ciphersuites // (if it were below, the position of the import would vary between ciphersuites
// after `cargo fmt`) // after `cargo fmt`)
use crate::P256Sha256;
use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar};
use crate::{Error, P256Sha256};
use super::{SecretShare, VerifiableSecretSharingCommitment}; use super::{SecretShare, VerifiableSecretSharingCommitment};
@ -26,7 +26,7 @@ pub fn repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(
share_i: &SecretShare, share_i: &SecretShare,
rng: &mut R, rng: &mut R,
participant: Identifier, participant: Identifier,
) -> HashMap<Identifier, Scalar> { ) -> Result<HashMap<Identifier, Scalar>, Error> {
frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant)
} }

View File

@ -9,8 +9,8 @@ use std::collections::HashMap;
// This is imported separately to make `gencode` work. // This is imported separately to make `gencode` work.
// (if it were below, the position of the import would vary between ciphersuites // (if it were below, the position of the import would vary between ciphersuites
// after `cargo fmt`) // after `cargo fmt`)
use crate::Ristretto255Sha512;
use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar};
use crate::{Error, Ristretto255Sha512};
use super::{SecretShare, VerifiableSecretSharingCommitment}; use super::{SecretShare, VerifiableSecretSharingCommitment};
@ -26,7 +26,7 @@ pub fn repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(
share_i: &SecretShare, share_i: &SecretShare,
rng: &mut R, rng: &mut R,
participant: Identifier, participant: Identifier,
) -> HashMap<Identifier, Scalar> { ) -> Result<HashMap<Identifier, Scalar>, Error> {
frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant)
} }

View File

@ -9,8 +9,8 @@ use std::collections::HashMap;
// This is imported separately to make `gencode` work. // This is imported separately to make `gencode` work.
// (if it were below, the position of the import would vary between ciphersuites // (if it were below, the position of the import would vary between ciphersuites
// after `cargo fmt`) // after `cargo fmt`)
use crate::Secp256K1Sha256;
use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar};
use crate::{Error, Secp256K1Sha256};
use super::{SecretShare, VerifiableSecretSharingCommitment}; use super::{SecretShare, VerifiableSecretSharingCommitment};
@ -26,7 +26,7 @@ pub fn repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(
share_i: &SecretShare, share_i: &SecretShare,
rng: &mut R, rng: &mut R,
participant: Identifier, participant: Identifier,
) -> HashMap<Identifier, Scalar> { ) -> Result<HashMap<Identifier, Scalar>, Error> {
frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant)
} }