Skip to content
This repository has been archived by the owner on Oct 31, 2024. It is now read-only.

Add test case for resharing from outside of FROST #36

Merged
merged 7 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/dkg/key_generation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,8 +666,9 @@ impl<C: CipherSuite> DistributedKeyGeneration<RoundOne, C> {
///
/// # Inputs
///
/// * The protocol new instance [`ThresholdParameters`]. These parameters can
/// be different from the previous ICE-FROST session using the same group key.
/// * The protocol old instance [`ThresholdParameters`]. These parameters can
/// be different from the current ICE-FROST session which will be having the
/// same group key.
/// * This participant's [`DiffieHellmanPrivateKey`].
/// * This participant's `index`.
/// * The list of `dealers`. These are the participants of the previous ICE-FROST
Expand Down
10 changes: 9 additions & 1 deletion src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,15 @@ impl<C: CipherSuite> Drop for IndividualSigningKey<C> {
}

impl<C: CipherSuite> IndividualSigningKey<C> {
/// Derive the corresponding public key for this secret key.
/// Outputs an [`IndividualSigningKey`] from an isolated secret key.
/// This can be useful for single parties owning a public key for
/// Schnorr signatures outside of an ICE-FROST context and who would
/// like to reshare its corresponding secret key to a set of participants.
pub fn from_single_key(key: <C::G as Group>::ScalarField) -> Self {
Self { index: 1, key }
}

/// Derives the corresponding public key for this secret key.
pub fn to_public(&self) -> IndividualVerifyingKey<C> {
let share = C::G::generator() * self.key;

Expand Down
2 changes: 2 additions & 0 deletions src/sign/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,8 @@ impl<C: CipherSuite> SignatureAggregator<C, Initial<'_>> {
}

/// Get the list of partipating signers.
/// It will internally sort the signers by indices, remove any duplicate,
/// and then return a reference to the updated internal list.
///
/// # Returns
///
Expand Down
146 changes: 144 additions & 2 deletions tests/ice_frost.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
//! Integration tests for ICE-FROST.

use ark_ec::Group;
use ark_ff::UniformRand;
use ice_frost::keys::{DiffieHellmanPrivateKey, GroupVerifyingKey, IndividualSigningKey};
use rand::rngs::OsRng;

use ice_frost::CipherSuite;

use ice_frost::dkg::{DistributedKeyGeneration, Participant};
use ice_frost::dkg::{DistributedKeyGeneration, EncryptedSecretShare, Participant, RoundOne};
use ice_frost::parameters::ThresholdParameters;
use ice_frost::sign::generate_commitment_share_lists;
use ice_frost::sign::{
generate_commitment_share_lists, PublicCommitmentShareList, SecretCommitmentShareList,
};

use ice_frost::sign::SignatureAggregator;

Expand All @@ -15,6 +20,9 @@ use ice_frost::testing::Secp256k1Sha256;
type ParticipantDKG = Participant<Secp256k1Sha256>;
type Dkg<T> = DistributedKeyGeneration<T, Secp256k1Sha256>;

type PublicCommShareList = PublicCommitmentShareList<Secp256k1Sha256>;
type SecretCommShareList = SecretCommitmentShareList<Secp256k1Sha256>;

#[test]
fn signing_and_verification_3_out_of_5() {
let params = ThresholdParameters::new(5, 3);
Expand Down Expand Up @@ -272,3 +280,137 @@ fn signing_and_verification_3_out_of_5() {
assert!(verification_result1.is_ok());
assert!(verification_result2.is_ok());
}

#[test]
fn resharing_from_non_frost_key() {
type SchnorrSecretKey = <<Secp256k1Sha256 as CipherSuite>::G as Group>::ScalarField;
type SchnorrPublicKey = <Secp256k1Sha256 as CipherSuite>::G;

// A single party outside of any ICE-FROST scenario, who owns a keypair to perform
// Schnorr signatures.
let mut rng = OsRng;
let single_party_sk: SchnorrSecretKey = SchnorrSecretKey::rand(&mut rng);
let single_party_pk: SchnorrPublicKey =
<<Secp256k1Sha256 as CipherSuite>::G>::generator() * single_party_sk;

// Converts this party's keys into ICE-FROST format, simulating a 1-out-of-1 setup.
let simulated_parameters = ThresholdParameters::new(1, 1);
let frost_sk = IndividualSigningKey::from_single_key(single_party_sk);
let frost_pk = GroupVerifyingKey::new(single_party_pk);

// Start a resharing phase from this single party to a set of new participants.
const NUMBER_OF_PARTICIPANTS: u32 = 5;
const THRESHOLD_OF_PARTICIPANTS: u32 = 3;

let threshold_parameters =
ThresholdParameters::new(NUMBER_OF_PARTICIPANTS, THRESHOLD_OF_PARTICIPANTS);

let mut signers = Vec::<Participant<Secp256k1Sha256>>::new();
let mut signers_dh_secret_keys = Vec::<DiffieHellmanPrivateKey<Secp256k1Sha256>>::new();

for i in 1..=NUMBER_OF_PARTICIPANTS {
let (p, dh_sk) =
Participant::<Secp256k1Sha256>::new_signer(threshold_parameters, i, rng).unwrap();

signers.push(p);
signers_dh_secret_keys.push(dh_sk);
}

let mut signers_encrypted_secret_shares: Vec<Vec<EncryptedSecretShare<Secp256k1Sha256>>> =
(0..NUMBER_OF_PARTICIPANTS).map(|_| Vec::new()).collect();

let mut signers_states_1 = Vec::<Dkg<_>>::new();
let mut signers_states_2 = Vec::<Dkg<_>>::new();

let (single_dealer, dealer_encrypted_shares_for_signers, _participant_lists) =
Participant::reshare(threshold_parameters, &frost_sk, &signers, rng).unwrap();

for i in 0..NUMBER_OF_PARTICIPANTS as usize {
let (signer_state, _participant_lists) =
DistributedKeyGeneration::<RoundOne, Secp256k1Sha256>::new(
simulated_parameters,
Nashtare marked this conversation as resolved.
Show resolved Hide resolved
&signers_dh_secret_keys[i],
signers[i].index,
&[single_dealer.clone()],
rng,
)
.unwrap();
signers_states_1.push(signer_state);
}

for (i, shares) in signers_encrypted_secret_shares.iter_mut().enumerate() {
let share_for_signer = dealer_encrypted_shares_for_signers
.get(&(i as u32 + 1))
.unwrap()
.clone();
*shares = vec![share_for_signer];
}

for i in 0..NUMBER_OF_PARTICIPANTS as usize {
let (si_state, complaints) = signers_states_1[i]
.clone()
.to_round_two(&signers_encrypted_secret_shares[i], rng)
.unwrap();
assert!(complaints.is_empty());

signers_states_2.push(si_state);
}

let mut signers_secret_keys = Vec::<IndividualSigningKey<Secp256k1Sha256>>::new();

for signers_state in &signers_states_2 {
let (si_group_key, si_sk) = signers_state.clone().finish().unwrap();
signers_secret_keys.push(si_sk);

// Assert that each signer's individual group key matches the converted
// single's party public key.
assert!(si_group_key == frost_pk);
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
}

let message = b"This is a test of the tsunami alert system. This is only a test.";
Nashtare marked this conversation as resolved.
Show resolved Hide resolved

let mut signers_public_comshares =
Vec::<PublicCommShareList>::with_capacity(NUMBER_OF_PARTICIPANTS as usize);
let mut signers_secret_comshares =
Vec::<SecretCommShareList>::with_capacity(NUMBER_OF_PARTICIPANTS as usize);

for i in 0..THRESHOLD_OF_PARTICIPANTS {
let (pi_public_comshares, pi_secret_comshares) =
generate_commitment_share_lists(&mut OsRng, &signers_secret_keys[i as usize], 1)
.unwrap();
signers_public_comshares.push(pi_public_comshares);
signers_secret_comshares.push(pi_secret_comshares);
}

let mut aggregator = SignatureAggregator::new(threshold_parameters, frost_pk, &message[..]);

for i in 0..THRESHOLD_OF_PARTICIPANTS {
aggregator.include_signer(
signers[i as usize].index,
signers_public_comshares[i as usize].commitments[0],
&signers_secret_keys[i as usize].to_public(),
);
}

let participating_signers = aggregator.get_signers().clone();
let message_hash = Secp256k1Sha256::h4(&message[..]);

for i in 0..THRESHOLD_OF_PARTICIPANTS {
let pi_partial_signature = signers_secret_keys[i as usize]
.sign(
&message_hash,
&frost_pk,
&mut signers_secret_comshares[i as usize],
0,
&participating_signers,
)
.unwrap();
aggregator.include_partial_signature(&pi_partial_signature);
}

let aggregator = aggregator.finalize().unwrap();

let threshold_signature = aggregator.aggregate().unwrap();

assert!(threshold_signature.verify(&frost_pk, &message_hash).is_ok());
Nashtare marked this conversation as resolved.
Show resolved Hide resolved
}
Loading