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

191 lines
6.9 KiB
Rust

//! Test for Repairable Threshold Scheme
use std::collections::HashMap;
use debugless_unwrap::DebuglessUnwrap;
use rand_core::{CryptoRng, RngCore};
use serde_json::Value;
use crate::{
frost::{
self,
keys::{
repairable::{
compute_lagrange_coefficient, repair_share_step_1, repair_share_step_2,
repair_share_step_3,
},
PublicKeyPackage, SecretShare, SigningShare,
},
Identifier,
},
Ciphersuite, Field, Group, Scalar,
};
/// We want to test that recover share matches the original share
pub fn check_rts<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
// Compute shares
////////////////////////////////////////////////////////////////////////////
// Key generation
////////////////////////////////////////////////////////////////////////////
let max_signers = 5;
let min_signers = 3;
let (shares, _pubkeys): (HashMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>) =
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
// Try to recover a share
// Signer 2 will lose their share
// Signer 1, 4 and 5 will help signer 2 to recover their share
let helper_1 = &shares[&Identifier::try_from(1).unwrap()];
let helper_4 = &shares[&Identifier::try_from(4).unwrap()];
let helper_5 = &shares[&Identifier::try_from(5).unwrap()];
let participant = &shares[&Identifier::try_from(2).unwrap()];
let helpers: [Identifier<C>; 3] = [
helper_1.identifier,
helper_4.identifier,
helper_5.identifier,
];
// 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_4_deltas = repair_share_step_1(&helpers, helper_4, &mut rng, participant.identifier);
let helper_5_deltas = repair_share_step_1(&helpers, helper_5, &mut rng, participant.identifier);
// Each helper calculates their sigma from the random values received from the other helpers
let helper_1_sigma: Scalar<C> = repair_share_step_2::<C>(&[
helper_1_deltas[&helpers[0]],
helper_4_deltas[&helpers[0]],
helper_5_deltas[&helpers[0]],
]);
let helper_4_sigma: Scalar<C> = repair_share_step_2::<C>(&[
helper_1_deltas[&helpers[1]],
helper_4_deltas[&helpers[1]],
helper_5_deltas[&helpers[1]],
]);
let helper_5_sigma: Scalar<C> = repair_share_step_2::<C>(&[
helper_1_deltas[&helpers[2]],
helper_4_deltas[&helpers[2]],
helper_5_deltas[&helpers[2]],
]);
// The participant wishing to recover their share sums the sigmas sent from all helpers
let participant_recovered_share = repair_share_step_3(
&[helper_1_sigma, helper_4_sigma, helper_5_sigma],
participant.identifier,
&participant.commitment,
);
// TODO: assert on commitment equality as well once updates have been made to VerifiableSecretSharingCommitment
assert!(participant.secret() == participant_recovered_share.secret())
}
fn generate_scalar_from_byte_string<C: Ciphersuite>(
bs: &str,
) -> <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar {
let decoded = hex::decode(bs).unwrap();
let out = <<C::Group as Group>::Field>::deserialize(&decoded.try_into().debugless_unwrap());
out.unwrap()
}
/// Test repair_share_step_1
pub fn check_repair_share_step_1<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
// Compute shares
let max_signers = 5;
let min_signers = 3;
let (shares, _pubkeys): (HashMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>) =
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
// Signer 2 will lose their share
// Signers (helpers) 1, 4 and 5 will help signer 2 (participant) to recover their share
let helper_1 = &shares[&Identifier::try_from(1).unwrap()];
let helper_4 = &shares[&Identifier::try_from(4).unwrap()];
let helper_5 = &shares[&Identifier::try_from(5).unwrap()];
let participant = &shares[&Identifier::try_from(2).unwrap()];
let helpers: [Identifier<C>; 3] = [
helper_1.identifier,
helper_4.identifier,
helper_5.identifier,
];
// Generate deltas for helper 4
let deltas = repair_share_step_1(&helpers, helper_4, &mut rng, participant.identifier);
let lagrange_coefficient =
compute_lagrange_coefficient(&helpers, participant.identifier, helpers[1]);
let mut rhs = <<C::Group as Group>::Field>::zero();
for (_k, v) in deltas {
rhs = rhs + v;
}
let lhs = lagrange_coefficient * helper_4.value.0;
assert!(lhs == rhs)
}
/// Test repair_share_step_2
pub fn check_repair_share_step_2<C: Ciphersuite>(repair_share_helpers: &Value) {
let values = &repair_share_helpers["scalar_generation"];
let value_1 =
generate_scalar_from_byte_string::<C>(values["random_scalar_1"].as_str().unwrap());
let value_2 =
generate_scalar_from_byte_string::<C>(values["random_scalar_2"].as_str().unwrap());
let value_3 =
generate_scalar_from_byte_string::<C>(values["random_scalar_3"].as_str().unwrap());
let expected: Scalar<C> = repair_share_step_2::<C>(&[value_1, value_2, value_3]);
let actual: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar =
generate_scalar_from_byte_string::<C>(values["random_scalar_sum"].as_str().unwrap());
assert!(actual == expected);
}
/// Test repair_share
pub fn check_repair_share_step_3<C: Ciphersuite, R: RngCore + CryptoRng>(
mut rng: R,
repair_share_helpers: &Value,
) {
// Generate shares
let max_signers = 5;
let min_signers = 3;
let (shares, _pubkeys): (HashMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>) =
frost::keys::generate_with_dealer(max_signers, min_signers, &mut rng).unwrap();
let sigmas: &Value = &repair_share_helpers["sigma_generation"];
let sigma_1 = generate_scalar_from_byte_string::<C>(sigmas["sigma_1"].as_str().unwrap());
let sigma_2 = generate_scalar_from_byte_string::<C>(sigmas["sigma_2"].as_str().unwrap());
let sigma_3 = generate_scalar_from_byte_string::<C>(sigmas["sigma_3"].as_str().unwrap());
let sigma_4 = generate_scalar_from_byte_string::<C>(sigmas["sigma_4"].as_str().unwrap());
let commitment = (shares[&Identifier::try_from(1).unwrap()].commitment).clone();
let expected = repair_share_step_3::<C>(
&[sigma_1, sigma_2, sigma_3, sigma_4],
Identifier::try_from(2).unwrap(),
&commitment,
);
let actual_sigma: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar =
generate_scalar_from_byte_string::<C>(sigmas["sigma_sum"].as_str().unwrap());
let actual: SecretShare<C> = SecretShare {
identifier: Identifier::try_from(2).unwrap(),
value: SigningShare(actual_sigma),
commitment,
};
assert!(actual.value == expected.value);
}