Add repair share functionality (#281)

* Add compute random value function for repair share functionality (#41)

This is step 1 of 3

* Add compute random value function for repair share functionality for each ciphersuite (#41)

* Add compute_sum_of_random_values function for repair share functionality (#41)

This is step 2 of 3

* Add recover_share function for repair share functionality (#41)

This is step 3 of 3

* Add communication rounds functions for repair share functionality for each ciphersuite (#41)

Add compute_sum_of_random_variables function
Add recover_share function

* Fix recover_share tests so they test the right thing

Fix secp256 recover share test values
Fix ristretto255 recover share test values
Fix ristretto255 compute sum of random values test values

* Rewrite compute_random_values to generate_random_values for repair share functionality (#41)

Test generate_random_values directly
End to end test to be added in another commit
Updated gendoc to use original file values to fix clippy complaints

* Rename functions and update documentation for repair (#41)

* Add end to end test for repair share (#41)

Fix lagrange coefficient calculation

Co-authored-by: conrado <conradoplg@gmail.com>

* Fix formatting (#41)

* Remove comment (#41)

Co-authored-by: Conrado Gouvea <conrado@zfnd.org>

* Update documentation for step 1 of RTS (#41)

Co-authored-by: Conrado Gouvea <conrado@zfnd.org>

* Update documentation for method of computing step 1 of RTS (#41)

Co-authored-by: Conrado Gouvea <conrado@zfnd.org>

* Rename repair share functions (#41)

* Improve documentation for Repairable Threshold Scheme (#41)

* Remove unecessary code from repairable tests (#41)

* Update repairable documentation

Co-authored-by: Conrado Gouvea <conrado@zfnd.org>

* Rename steps in repairable to be more consistent with DKG pattern (#41)

* Update gitignore (#41)

* Update repairable to use new keygen_with_dealer signature (#41)

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

---------

Co-authored-by: conrado <conradoplg@gmail.com>
Co-authored-by: Conrado Gouvea <conrado@zfnd.org>
This commit is contained in:
natalie 2023-04-24 16:27:14 +01:00 committed by GitHub
parent 6554f2ba85
commit 0b981618cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 792 additions and 0 deletions

View File

@ -21,6 +21,7 @@ use crate::{
};
pub mod dkg;
pub mod repairable;
/// Return a vector of randomly generated polynomial coefficients ([`Scalar`]s).
pub(crate) fn generate_coefficients<C: Ciphersuite, R: RngCore + CryptoRng>(

View File

@ -0,0 +1,133 @@
//! 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::HashMap;
use crate::{frost::Identifier, Ciphersuite, CryptoRng, Field, Group, 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>,
) -> HashMap<Identifier<C>, Scalar<C>> {
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 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: &[Identifier<C>],
share_i: &SecretShare<C>,
random_values: &Vec<Scalar<C>>,
participant: Identifier<C>,
) -> HashMap<Identifier<C>, Scalar<C>> {
// Calculate Lagrange Coefficient for helper_i
let zeta_i = compute_lagrange_coefficient(helpers, participant, share_i.identifier);
let lhs = zeta_i * share_i.value.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[helpers.len() - 1], lhs - sum_i_deltas);
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
///
/// `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 {
identifier,
value: SigningShare(share),
commitment: commitment.clone(),
}
}

View File

@ -8,6 +8,7 @@ use crate::Ciphersuite;
pub mod batch;
pub mod proptests;
pub mod repairable;
pub mod vectors;
/// Test share generation with a Ciphersuite

View File

@ -0,0 +1,190 @@
//! 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::keygen_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::keygen_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_helper_functions: &Value) {
let values = &repair_share_helper_functions["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_helper_functions: &Value,
) {
// Generate shares
let max_signers = 5;
let min_signers = 3;
let (shares, _pubkeys): (HashMap<Identifier<C>, SecretShare<C>>, PublicKeyPackage<C>) =
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng).unwrap();
let sigmas: &Value = &repair_share_helper_functions["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);
}

View File

@ -0,0 +1,44 @@
/// Repairable Threshold Scheme
#![doc = include_str!("../../repairable.md")]
use std::collections::HashMap;
use crate::{frost::Identifier, Ciphersuite, CryptoRng, Field, Group, RngCore, Scalar};
use super::{SecretShare};
/// Generate random values for each helper - 1 for use in computing the value for the final helper
pub fn repair_share_step_1<R: RngCore + CryptoRng>(
helpers: &[Identifier<C>],
share_i: &SecretShare<C>,
zeta_i: Scalar<C>,
rng: &mut R,
) -> HashMap<Identifier<C>, Scalar<C>> {
frost::keys::repairable::repair_share_step_1(identifier, max_signers, min_signers, &mut rng)
}
/// # Communication round:
/// # Helper i sends deltas_i[j] to helper j
/// # deltas_j: values received by j in the communication round
/// # Output: sigma_j
pub fn repair_share_step_2<C: Ciphersuite>(deltas_j: &[Scalar<C>]) -> Scalar<C> {
frost::keys::repairable::repair_share_step_2(deltas_j)
}
/// # Communication round
/// # Helper j sends sigma_j to signer r
/// # sigmas: all sigma_j received from each helper j
/// # Output: share_r: r's secret share
pub fn repair_share_step_3<C: Ciphersuite>(
sigmas: &[Scalar<C>],
identifier: Identifier<C>,
commitment: &VerifiableSecretSharingCommitment<C>,
) -> SecretShare<C> {
frost::keys::repairable::repair_share(sigmas, identifier, commitment)
}

View File

@ -2,7 +2,9 @@ use frost_ed25519::*;
use curve25519_dalek::{edwards::EdwardsPoint, traits::Identity};
use ed25519_dalek::Verifier;
use lazy_static::lazy_static;
use rand::thread_rng;
use serde_json::Value;
fn verify_signature(
msg: &[u8],
@ -84,3 +86,36 @@ fn check_deserialize_non_prime_order() {
let r = <Ed25519Sha512 as Ciphersuite>::Group::deserialize(&encoded_point);
assert_eq!(r, Err(GroupError::InvalidNonPrimeOrderElement));
}
#[test]
fn check_repair_share_step_1() {
let rng = thread_rng();
frost_core::tests::repairable::check_repair_share_step_1::<Ed25519Sha512, _>(rng);
}
lazy_static! {
pub static ref REPAIR_SHARE: Value =
serde_json::from_str(include_str!("repair-share.json").trim()).unwrap();
}
#[test]
fn check_repair_share_step_2() {
frost_core::tests::repairable::check_repair_share_step_2::<Ed25519Sha512>(&REPAIR_SHARE);
}
#[test]
fn check_repair_share() {
let rng = thread_rng();
frost_core::tests::repairable::check_repair_share_step_3::<Ed25519Sha512, _>(
rng,
&REPAIR_SHARE,
);
}
#[test]
fn check_rts() {
let rng = thread_rng();
frost_core::tests::repairable::check_rts::<Ed25519Sha512, _>(rng);
}

View File

@ -0,0 +1,15 @@
{
"scalar_generation": {
"random_scalar_1": "1847f6c4a85096e5dbc9e200c9691c5164f8e276d32d4a54ebaf4275474a1403",
"random_scalar_2": "eac5595269d108812eaa865bf62c703a2c128a61fa3bd4dc837b9314bc515204",
"random_scalar_3": "5b3b6084e41c273a39a8d9bbbd87fbcd626c07030142bf78c6c91247bf175700",
"random_scalar_sum": "5d48b09bf63ec6a0431c43187d1e8859f37674dbceabdda935f5e8d0c2b3bd07"
},
"sigma_generation": {
"sigma_1": "ec3aa83140065181d75b746bfd6bbbbaf212bdfbb3a91670f924d1ca899cbc0c",
"sigma_2": "5dd288d659e0a2dd3ef7523a9cc4f80f4a7f919e9980005c7fbec0961d3fb500",
"sigma_3": "3e62e7461db9ca1ed2f1549a8114bbc87fa9242ce0012ed3f9ac9dcf23f4c30a",
"sigma_4": "684c44e7aba416a1982a8db8ec2a3095f5cc6a3f958a4716b69ae76524dd7200",
"sigma_sum": "02e866d948e1c2c6aad2b1552976c013b208de05c3b68cb5282b1797efaca808"
}
}

View File

@ -0,0 +1,44 @@
/// Repairable Threshold Scheme
#![doc = include_str!("../../repairable.md")]
use std::collections::HashMap;
use crate::{frost::Identifier, Ciphersuite, CryptoRng, Field, Group, RngCore, Scalar};
use super::{SecretShare};
/// Generate random values for each helper - 1 for use in computing the value for the final helper
pub fn repair_share_step_1<R: RngCore + CryptoRng>(
helpers: &[Identifier<C>],
share_i: &SecretShare<C>,
zeta_i: Scalar<C>,
rng: &mut R,
) -> HashMap<Identifier<C>, Scalar<C>> {
frost::keys::repairable::repair_share_step_1(identifier, max_signers, min_signers, &mut rng)
}
/// # Communication round:
/// # Helper i sends deltas_i[j] to helper j
/// # deltas_j: values received by j in the communication round
/// # Output: sigma_j
pub fn repair_share_step_2<C: Ciphersuite>(deltas_j: &[Scalar<C>]) -> Scalar<C> {
frost::keys::repairable::repair_share_step_2(deltas_j)
}
/// # Communication round
/// # Helper j sends sigma_j to signer r
/// # sigmas: all sigma_j received from each helper j
/// # Output: share_r: r's secret share
pub fn repair_share_step_3<C: Ciphersuite>(
sigmas: &[Scalar<C>],
identifier: Identifier<C>,
commitment: &VerifiableSecretSharingCommitment<C>,
) -> SecretShare<C> {
frost::keys::repairable::repair_share(sigmas, identifier, commitment)
}

View File

@ -1,7 +1,9 @@
use ed448_goldilocks::curve::ExtendedPoint;
use frost_ed448::*;
use lazy_static::lazy_static;
use rand::thread_rng;
use serde_json::Value;
#[test]
fn check_sign_with_dealer() {
@ -73,3 +75,36 @@ fn check_deserialize_non_prime_order() {
let r = <Ed448Shake256 as Ciphersuite>::Group::deserialize(&encoded_point);
assert_eq!(r, Err(GroupError::InvalidNonPrimeOrderElement));
}
#[test]
fn check_repair_share_step_1() {
let rng = thread_rng();
frost_core::tests::repairable::check_repair_share_step_1::<Ed448Shake256, _>(rng);
}
lazy_static! {
pub static ref REPAIR_SHARE: Value =
serde_json::from_str(include_str!("repair-share.json").trim()).unwrap();
}
#[test]
fn check_repair_share_step_2() {
frost_core::tests::repairable::check_repair_share_step_2::<Ed448Shake256>(&REPAIR_SHARE);
}
#[test]
fn check_repair_share() {
let rng = thread_rng();
frost_core::tests::repairable::check_repair_share_step_3::<Ed448Shake256, _>(
rng,
&REPAIR_SHARE,
);
}
#[test]
fn check_rts() {
let rng = thread_rng();
frost_core::tests::repairable::check_rts::<Ed448Shake256, _>(rng);
}

View File

@ -0,0 +1,15 @@
{
"scalar_generation": {
"random_scalar_1": "eb544ffc1486f87effb0edb05612d8a7f86f78e78fedbe0e12245dc42058c207c40fcf7bcc1aeb45e7d3e1432029749aa0a952d657fec40c00",
"random_scalar_2": "f2c074dad7fd1b69338fb88a1b5283b4612eba895139d790914645f5f394013033bec754b83da4e841bd0ca5a4b8a4937fa08bdcc0a5711300",
"random_scalar_3": "7bd09ea1e62b16132a4a96afc427a24055ff5b8b6d3e0e4921753968d271bcdfb5e4c093d8a74233e6e95ca8fc0922c63bb4ac3ba913da3800",
"random_scalar_sum": "65a10acd40edb1d707fb765dc4c9907b1f67b84d058a5524dbbb11a5e75e8017adb257645d00d2610f7b4b91c1eb3af45bfe8aeec1b7101900"
},
"sigma_generation": {
"sigma_1": "aec016b902d5ba5d3d9ddcff116cbb7cd836c3e88f247125eaf0a37b066e5f26dfa1b0e23e700d7abba22be0ea4c8e862846d0bb3b94df0900",
"sigma_2": "43c4a604ac2d53d0255afea625e72e0de85a7b9972e62574ef67b418f946fa4e27df04f80a7258daa64f7304f8c1eebd85302bde9742a81400",
"sigma_3": "cfc940c8fc206b6770c399c1de1e1dc6bab542ae83b95fd4d6e41ef6bba68c5c499aa7d77032f48f10698125bcf8d374ad441c834f9a4b0500",
"sigma_4": "7a74c45ed0c72d567bc1b70c5f22bb64a7cf5426371f5570da6a4dc897a03f78942e4c364e49dc1854b638e953bbafb35509a23132519c3100",
"sigma_sum": "477e6a39e9282ec8f9ec66e702d2559392e0ffa77308fd19a184fad553fc254ae449a9e8085e36fdc61159f3f2c2006db1c4b94e55c26f1500"
}
}

View File

@ -0,0 +1,44 @@
/// Repairable Threshold Scheme
#![doc = include_str!("../../repairable.md")]
use std::collections::HashMap;
use crate::{frost::Identifier, Ciphersuite, CryptoRng, Field, Group, RngCore, Scalar};
use super::{SecretShare};
/// Generate random values for each helper - 1 for use in computing the value for the final helper
pub fn repair_share_step_1<R: RngCore + CryptoRng>(
helpers: &[Identifier<C>],
share_i: &SecretShare<C>,
zeta_i: Scalar<C>,
rng: &mut R,
) -> HashMap<Identifier<C>, Scalar<C>> {
frost::keys::repairable::repair_share_step_1(identifier, max_signers, min_signers, &mut rng)
}
/// # Communication round:
/// # Helper i sends deltas_i[j] to helper j
/// # deltas_j: values received by j in the communication round
/// # Output: sigma_j
pub fn repair_share_step_2<C: Ciphersuite>(deltas_j: &[Scalar<C>]) -> Scalar<C> {
frost::keys::repairable::repair_share_step_2(deltas_j)
}
/// # Communication round
/// # Helper j sends sigma_j to signer r
/// # sigmas: all sigma_j received from each helper j
/// # Output: share_r: r's secret share
pub fn repair_share_step_3<C: Ciphersuite>(
sigmas: &[Scalar<C>],
identifier: Identifier<C>,
commitment: &VerifiableSecretSharingCommitment<C>,
) -> SecretShare<C> {
frost::keys::repairable::repair_share(sigmas, identifier, commitment)
}

View File

@ -1,5 +1,7 @@
use frost_p256::*;
use lazy_static::lazy_static;
use rand::thread_rng;
use serde_json::Value;
#[test]
fn check_sign_with_dealer() {
@ -64,3 +66,33 @@ fn check_deserialize_non_canonical() {
let r = <P256Sha256 as Ciphersuite>::Group::deserialize(&encoded_point);
assert_eq!(r, Err(GroupError::MalformedElement));
}
#[test]
fn check_repair_share_step_1() {
let rng = thread_rng();
frost_core::tests::repairable::check_repair_share_step_1::<P256Sha256, _>(rng);
}
lazy_static! {
pub static ref REPAIR_SHARE: Value =
serde_json::from_str(include_str!("repair-share.json").trim()).unwrap();
}
#[test]
fn check_repair_share_step_2() {
frost_core::tests::repairable::check_repair_share_step_2::<P256Sha256>(&REPAIR_SHARE);
}
#[test]
fn check_repair_share() {
let rng = thread_rng();
frost_core::tests::repairable::check_repair_share_step_3::<P256Sha256, _>(rng, &REPAIR_SHARE);
}
#[test]
fn check_rts() {
let rng = thread_rng();
frost_core::tests::repairable::check_rts::<P256Sha256, _>(rng);
}

View File

@ -0,0 +1,15 @@
{
"scalar_generation": {
"random_scalar_1": "72deefbbf904c44fe6bb41ad8fcd23e6de2bd950aa2bdd03a8d84fcf29a86e21",
"random_scalar_2": "63d3c1867568bcf26c2c5525cbe2af1f689adfb0f7c8c2e769c1f8bca37e0e8f",
"random_scalar_3": "90a1e9902329edc21bd8c15ceafa901ac028a543b4d06011667a29dfc377e5dd",
"random_scalar_sum": "67549ad391976f036ec0583046aa63214a086397afad6177855aa7a8943b3d3c"
},
"sigma_generation": {
"sigma_1": "6234563e6c285de9e50569948c4648ef8205294a18f994a8f61e8c9f92c1b194",
"sigma_2": "0c9f57342d4f20f05b3c61ac049230cad852ab541ef46394778aff6e7ff55700",
"sigma_3": "1b50eb6a1b27bfbed21076913ca2892bc35a8be1c9f61e2cfa2e9c708869c049",
"sigma_4": "44260f9f457d96bd0dcdcd9b83c45231bca28ecc5ab52dee9cf59f6b361c520c",
"sigma_sum": "ce4aa87bfa1cd55620200f6d513f5517da54ef4c5c99445904cdc7e9d13d1ae9"
}
}

View File

@ -0,0 +1,44 @@
/// Repairable Threshold Scheme
#![doc = include_str!("../../repairable.md")]
use std::collections::HashMap;
use crate::{frost::Identifier, Ciphersuite, CryptoRng, Field, Group, RngCore, Scalar};
use super::{SecretShare};
/// Generate random values for each helper - 1 for use in computing the value for the final helper
pub fn repair_share_step_1<R: RngCore + CryptoRng>(
helpers: &[Identifier<C>],
share_i: &SecretShare<C>,
zeta_i: Scalar<C>,
rng: &mut R,
) -> HashMap<Identifier<C>, Scalar<C>> {
frost::keys::repairable::repair_share_step_1(identifier, max_signers, min_signers, &mut rng)
}
/// # Communication round:
/// # Helper i sends deltas_i[j] to helper j
/// # deltas_j: values received by j in the communication round
/// # Output: sigma_j
pub fn repair_share_step_2<C: Ciphersuite>(deltas_j: &[Scalar<C>]) -> Scalar<C> {
frost::keys::repairable::repair_share_step_2(deltas_j)
}
/// # Communication round
/// # Helper j sends sigma_j to signer r
/// # sigmas: all sigma_j received from each helper j
/// # Output: share_r: r's secret share
pub fn repair_share_step_3<C: Ciphersuite>(
sigmas: &[Scalar<C>],
identifier: Identifier<C>,
commitment: &VerifiableSecretSharingCommitment<C>,
) -> SecretShare<C> {
frost::keys::repairable::repair_share(sigmas, identifier, commitment)
}

View File

@ -1,6 +1,8 @@
use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity};
use frost_ristretto255::*;
use lazy_static::lazy_static;
use rand::thread_rng;
use serde_json::Value;
#[test]
fn check_sign_with_dealer() {
@ -37,3 +39,36 @@ fn check_deserialize_identity() {
let r = <Ristretto255Sha512 as Ciphersuite>::Group::deserialize(&encoded_identity);
assert_eq!(r, Err(GroupError::InvalidIdentityElement));
}
lazy_static! {
pub static ref REPAIR_SHARE: Value =
serde_json::from_str(include_str!("repair-share.json").trim()).unwrap();
}
#[test]
fn check_repair_share_step_1() {
let rng = thread_rng();
frost_core::tests::repairable::check_repair_share_step_1::<Ristretto255Sha512, _>(rng);
}
#[test]
fn check_repair_share_step_2() {
frost_core::tests::repairable::check_repair_share_step_2::<Ristretto255Sha512>(&REPAIR_SHARE);
}
#[test]
fn check_repair_share_step_3() {
let rng = thread_rng();
frost_core::tests::repairable::check_repair_share_step_3::<Ristretto255Sha512, _>(
rng,
&REPAIR_SHARE,
);
}
#[test]
fn check_rts() {
let rng = thread_rng();
frost_core::tests::repairable::check_rts::<Ristretto255Sha512, _>(rng);
}

View File

@ -0,0 +1,15 @@
{
"scalar_generation": {
"random_scalar_1": "7e835f4c5453aea47a09e3925bfa23876de6fc9f92e6a7f8f1b45fe273fb850e",
"random_scalar_2": "d79cbd35ae36865692f2f31f9ab6fa9109757accb18f10b416a6c99828b82707",
"random_scalar_3": "73398badf5e53422d721e5acf901be840dbacb7c0c644cb171bd169df360550d",
"random_scalar_sum": "eeb1bc75c3a9446d37e4cc1932bf1e74841543e950da045e7a18401890140303"
},
"sigma_generation": {
"sigma_1": "19280c3694fc2881346c73fa9024cf3b710d1bc4651b01ac47e982a0c13ef004",
"sigma_2": "bb8e111c18df8ad1af6710bb5845ff510a96cc37ceb99079cabe635ba0d3dd08",
"sigma_3": "e37d76cbcbc44c14ef38043bff3b2154467095f0a7c6922444a1f09e4d563f09",
"sigma_4": "898ea31783f13def4fb0cd9d97d24a18adb48050ed8c7b2af24d246f45b28e0c",
"sigma_sum": "661b4c7bc6cb19a676836648c3847cd06ec8fd3cc928a0744897fb09f51a9c03"
}
}

View File

@ -0,0 +1,44 @@
/// Repairable Threshold Scheme
#![doc = include_str!("../../repairable.md")]
use std::collections::HashMap;
use crate::{frost::Identifier, Ciphersuite, CryptoRng, Field, Group, RngCore, Scalar};
use super::{SecretShare};
/// Generate random values for each helper - 1 for use in computing the value for the final helper
pub fn repair_share_step_1<R: RngCore + CryptoRng>(
helpers: &[Identifier<C>],
share_i: &SecretShare<C>,
zeta_i: Scalar<C>,
rng: &mut R,
) -> HashMap<Identifier<C>, Scalar<C>> {
frost::keys::repairable::repair_share_step_1(identifier, max_signers, min_signers, &mut rng)
}
/// # Communication round:
/// # Helper i sends deltas_i[j] to helper j
/// # deltas_j: values received by j in the communication round
/// # Output: sigma_j
pub fn repair_share_step_2<C: Ciphersuite>(deltas_j: &[Scalar<C>]) -> Scalar<C> {
frost::keys::repairable::repair_share_step_2(deltas_j)
}
/// # Communication round
/// # Helper j sends sigma_j to signer r
/// # sigmas: all sigma_j received from each helper j
/// # Output: share_r: r's secret share
pub fn repair_share_step_3<C: Ciphersuite>(
sigmas: &[Scalar<C>],
identifier: Identifier<C>,
commitment: &VerifiableSecretSharingCommitment<C>,
) -> SecretShare<C> {
frost::keys::repairable::repair_share(sigmas, identifier, commitment)
}

View File

@ -1,5 +1,7 @@
use frost_secp256k1::*;
use lazy_static::lazy_static;
use rand::thread_rng;
use serde_json::Value;
#[test]
fn check_sign_with_dealer() {
@ -64,3 +66,36 @@ fn check_deserialize_non_canonical() {
let r = <Secp256K1Sha256 as Ciphersuite>::Group::deserialize(&encoded_point);
assert_eq!(r, Err(GroupError::MalformedElement));
}
#[test]
fn check_repair_share_step_1() {
let rng = thread_rng();
frost_core::tests::repairable::check_repair_share_step_1::<Secp256K1Sha256, _>(rng);
}
lazy_static! {
pub static ref REPAIR_SHARE: Value =
serde_json::from_str(include_str!("repair-share.json").trim()).unwrap();
}
#[test]
fn check_repair_share_step_2() {
frost_core::tests::repairable::check_repair_share_step_2::<Secp256K1Sha256>(&REPAIR_SHARE);
}
#[test]
fn check_repair_share() {
let rng = thread_rng();
frost_core::tests::repairable::check_repair_share_step_3::<Secp256K1Sha256, _>(
rng,
&REPAIR_SHARE,
);
}
#[test]
fn check_rts() {
let rng = thread_rng();
frost_core::tests::repairable::check_rts::<Secp256K1Sha256, _>(rng);
}

View File

@ -0,0 +1,15 @@
{
"scalar_generation": {
"random_scalar_1": "1847f6c4a85096e5dbc9e200c9691c5164f8e276d32d4a54ebaf4275474a1403",
"random_scalar_2": "eac5595269d108812eaa865bf62c703a2c128a61fa3bd4dc837b9314bc515204",
"random_scalar_3": "5b3b6084e41c273a39a8d9bbbd87fbcd626c07030142bf78c6c91247bf175700",
"random_scalar_sum": "5e48b09bf63dc6a1441d42187d1d885a38c896f51f633e6e76218944f27c7bc6"
},
"sigma_generation": {
"sigma_1": "ec3aa83140065181d75b746bfd6bbbbaf212bdfbb3a91670f924d1ca899cbc0c",
"sigma_2": "5dd288d659e0a2dd3ef7523a9cc4f80f4a7f919e9980005c7fbec0961d3fb500",
"sigma_3": "3e62e7461db9ca1ed2f1549a8114bbc87fa9242ce0012ed3f9ac9dcf23f4c30a",
"sigma_4": "684c44e7aba416a1982a8db8ec2a3095f5cc6a3f958a4716b69ae76524dd7200",
"sigma_sum": "f0bc5d356344d51f816ea8fa076fa029f7590120136bec7c6958b9081f7864d5"
}
}