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 3 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
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
4 changes: 3 additions & 1 deletion 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 Expand Up @@ -556,7 +558,7 @@ impl<C: CipherSuite> SignatureAggregator<C, Initial<'_>> {
}

// Ensure that our new state is ordered and deduplicated.
self.state.signers = self.get_signers().clone();
let _ = self.get_signers();

for signer in &self.state.signers {
if self
Expand Down
143 changes: 141 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,134 @@ 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 N_PARAM: u32 = 5;
Nashtare marked this conversation as resolved.
Show resolved Hide resolved
const T_PARAM: u32 = 3;
Nashtare marked this conversation as resolved.
Show resolved Hide resolved

let threshold_parameters = ThresholdParameters::new(N_PARAM, T_PARAM);

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

for i in 1..=N_PARAM {
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..N_PARAM).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..N_PARAM 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..N_PARAM 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(N_PARAM as usize);
let mut signers_secret_comshares = Vec::<SecretCommShareList>::with_capacity(N_PARAM as usize);

for i in 0..T_PARAM {
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..T_PARAM {
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..T_PARAM {
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