frost/frost-core/src/frost/keys/repairable.rs

130 lines
4.1 KiB
Rust

//! Repairable Threshold Scheme
//!
//! Implements the Repairable Threshold Scheme (RTS) from <https://eprint.iacr.org/2017/1155>.
//! 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`.
use std::collections::{BTreeSet, HashMap};
use crate::{
frost::{compute_lagrange_coefficient, Identifier},
Ciphersuite, CryptoRng, Error, Field, Group, Header, RngCore, Scalar,
};
use super::{generate_coefficients, SecretShare, SigningShare, VerifiableSecretSharingCommitment};
/// Step 1 of RTS.
///
/// Generates the "delta" values from `helper_i` to help `participant` recover their share
/// where `helpers` contains the identifiers of all the helpers (including `helper_i`), and `share_i`
/// is the share of `helper_i`.
///
/// Returns a HashMap mapping which value should be sent to which participant.
pub fn repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(
helpers: &[Identifier<C>],
share_i: &SecretShare<C>,
rng: &mut R,
participant: Identifier<C>,
) -> Result<HashMap<Identifier<C>, Scalar<C>>, Error<C>> {
if helpers.len() < 2 {
return Err(Error::InvalidMinSigners);
}
if helpers.is_empty() {
return Err(Error::IncorrectNumberOfIdentifiers);
}
let xset: BTreeSet<_> = helpers.iter().cloned().collect();
if xset.len() != helpers.len() {
return Err(Error::DuplicatedIdentifier);
}
let rand_val: Vec<Scalar<C>> = generate_coefficients::<C, R>(helpers.len() - 1, rng);
compute_last_random_value(&xset, share_i, &rand_val, participant)
}
/// Compute the last delta value given the (generated uniformly at random) remaining ones
/// since they all must add up to `zeta_i * share_i`.
///
/// Returns a HashMap mapping which value should be sent to which participant.
fn compute_last_random_value<C: Ciphersuite>(
helpers: &BTreeSet<Identifier<C>>,
share_i: &SecretShare<C>,
random_values: &Vec<Scalar<C>>,
participant: Identifier<C>,
) -> Result<HashMap<Identifier<C>, Scalar<C>>, Error<C>> {
// Calculate Lagrange Coefficient for helper_i
let zeta_i = compute_lagrange_coefficient(helpers, Some(participant), share_i.identifier)?;
let lhs = zeta_i * share_i.signing_share.0;
let mut out: HashMap<Identifier<C>, Scalar<C>> = helpers
.iter()
.copied()
.zip(random_values.iter().copied())
.collect();
let mut sum_i_deltas = <<C::Group as Group>::Field>::zero();
for v in random_values {
sum_i_deltas = sum_i_deltas + *v;
}
out.insert(
*helpers.last().ok_or(Error::IncorrectNumberOfIdentifiers)?,
lhs - sum_i_deltas,
);
Ok(out)
}
/// Communication round
///
/// `helper_i` sends 1 `delta_j` to all other helpers (j)
/// `helper_i` retains 1 `delta_j`
/// Step 2 of RTS.
///
/// Generates the `sigma` values from all `deltas` received from `helpers`
/// to help `participant` recover their share.
/// `sigma` is the sum of all received `delta` and the `delta_i` generated for `helper_i`.
///
/// Returns a scalar
pub fn repair_share_step_2<C: Ciphersuite>(deltas_j: &[Scalar<C>]) -> Scalar<C> {
let mut sigma_j = <<C::Group as Group>::Field>::zero();
for d in deltas_j {
sigma_j = sigma_j + *d;
}
sigma_j
}
/// Communication round
///
/// `helper_j` sends 1 `sigma_j` to the `participant` repairing their share.
/// Step 3 of RTS
///
/// The `participant` sums all `sigma_j` received to compute the `share`. The `SecretShare`
/// is made up of the `identifier`and `commitment` of the `participant` as well as the
/// `value` which is the `SigningShare`.
pub fn repair_share_step_3<C: Ciphersuite>(
sigmas: &[Scalar<C>],
identifier: Identifier<C>,
commitment: &VerifiableSecretSharingCommitment<C>,
) -> SecretShare<C> {
let mut share = <<C::Group as Group>::Field>::zero();
for s in sigmas {
share = share + *s;
}
SecretShare {
header: Header::default(),
identifier,
signing_share: SigningShare(share),
commitment: commitment.clone(),
}
}