Skip to content

Commit dee685d

Browse files
authored
Refresh Shares with DKG (#766)
* Refresh shares with DKG (#663) * Add verification step for round2_packages for refreshing shares with DKG (#663) * Clean up clippy issues for correct indexing with refreshing shares with DKG (#663) * Import refresh tests for all ciphersuites (#663) * Fix formatting (#663)
1 parent d6c9f73 commit dee685d

File tree

11 files changed

+498
-9
lines changed

11 files changed

+498
-9
lines changed

frost-core/src/keys/refresh.rs

Lines changed: 265 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@ use alloc::collections::BTreeMap;
88
use alloc::vec::Vec;
99

1010
use crate::{
11+
keys::dkg::{compute_proof_of_knowledge, round1, round2},
1112
keys::{
12-
generate_coefficients, generate_secret_shares, validate_num_of_signers,
13-
CoefficientCommitment, PublicKeyPackage, SigningKey, SigningShare, VerifyingShare,
13+
evaluate_polynomial, generate_coefficients, generate_secret_polynomial,
14+
generate_secret_shares, validate_num_of_signers, CoefficientCommitment, PublicKeyPackage,
15+
SigningKey, SigningShare, VerifyingShare,
1416
},
15-
Ciphersuite, CryptoRng, Error, Field, Group, Identifier, RngCore,
17+
Ciphersuite, CryptoRng, Error, Field, Group, Header, Identifier, RngCore,
1618
};
1719

18-
use super::{KeyPackage, SecretShare, VerifiableSecretSharingCommitment};
20+
use core::iter;
21+
22+
use super::{dkg::round1::Package, KeyPackage, SecretShare, VerifiableSecretSharingCommitment};
1923

2024
/// Generates new zero key shares and a public key package using a trusted
2125
/// dealer Building a new public key package is done by taking the verifying
@@ -114,3 +118,260 @@ pub fn refresh_share<C: Ciphersuite>(
114118

115119
Ok(new_key_package)
116120
}
121+
122+
/// Part 1 of refresh share with DKG. A refreshing_key is generated and a new package and secret_package are generated.
123+
/// The identity commitment is removed from the packages.
124+
pub fn refresh_dkg_part_1<C: Ciphersuite, R: RngCore + CryptoRng>(
125+
identifier: Identifier<C>,
126+
max_signers: u16,
127+
min_signers: u16,
128+
mut rng: R,
129+
) -> Result<(round1::SecretPackage<C>, round1::Package<C>), Error<C>> {
130+
validate_num_of_signers::<C>(min_signers, max_signers)?;
131+
132+
// Build refreshing shares
133+
let refreshing_key = SigningKey {
134+
scalar: <<C::Group as Group>::Field>::zero(),
135+
};
136+
137+
// Round 1, Step 1
138+
let coefficients = generate_coefficients::<C, R>(min_signers as usize - 1, &mut rng);
139+
140+
let (coefficients, commitment) =
141+
generate_secret_polynomial(&refreshing_key, max_signers, min_signers, coefficients)?;
142+
143+
// Remove identity element from coefficients
144+
let mut coeff_comms = commitment.0;
145+
coeff_comms.remove(0);
146+
let commitment = VerifiableSecretSharingCommitment::new(coeff_comms.clone());
147+
148+
let proof_of_knowledge =
149+
compute_proof_of_knowledge(identifier, &coefficients, &commitment, &mut rng)?;
150+
151+
let secret_package = round1::SecretPackage {
152+
identifier,
153+
coefficients: coefficients.clone(),
154+
commitment: commitment.clone(),
155+
min_signers,
156+
max_signers,
157+
};
158+
let package = round1::Package {
159+
header: Header::default(),
160+
commitment,
161+
proof_of_knowledge,
162+
};
163+
164+
Ok((secret_package, package))
165+
}
166+
167+
/// Part 2 of refresh share with DKG. The identity commitment needs to be added back into the secret package.
168+
pub fn refresh_dkg_part2<C: Ciphersuite>(
169+
mut secret_package: round1::SecretPackage<C>,
170+
round1_packages: &BTreeMap<Identifier<C>, round1::Package<C>>,
171+
) -> Result<
172+
(
173+
round2::SecretPackage<C>,
174+
BTreeMap<Identifier<C>, round2::Package<C>>,
175+
),
176+
Error<C>,
177+
> {
178+
if round1_packages.len() != (secret_package.max_signers - 1) as usize {
179+
return Err(Error::IncorrectNumberOfPackages);
180+
}
181+
182+
// The identity commitment needs to be added to the VSS commitment for secret package
183+
let identity_commitment: Vec<CoefficientCommitment<C>> =
184+
vec![CoefficientCommitment::new(C::Group::identity())];
185+
186+
let refreshing_secret_share_commitments: Vec<CoefficientCommitment<C>> = identity_commitment
187+
.into_iter()
188+
.chain(secret_package.commitment.0.clone())
189+
.collect();
190+
191+
secret_package.commitment =
192+
VerifiableSecretSharingCommitment::<C>::new(refreshing_secret_share_commitments);
193+
194+
let mut round2_packages = BTreeMap::new();
195+
196+
for (sender_identifier, round1_package) in round1_packages {
197+
// The identity commitment needs to be added to the VSS commitment for every round 1 package
198+
let identity_commitment: Vec<CoefficientCommitment<C>> =
199+
vec![CoefficientCommitment::new(C::Group::identity())];
200+
201+
let refreshing_share_commitments: Vec<CoefficientCommitment<C>> = identity_commitment
202+
.into_iter()
203+
.chain(round1_package.commitment.0.clone())
204+
.collect();
205+
206+
if refreshing_share_commitments.clone().len() != secret_package.min_signers as usize {
207+
return Err(Error::IncorrectNumberOfCommitments);
208+
}
209+
210+
let ell = *sender_identifier;
211+
212+
// Round 1, Step 5
213+
// We don't need to verify the proof of knowledge
214+
215+
// Round 2, Step 1
216+
//
217+
// > Each P_i securely sends to each other participant P_ℓ a secret share (ℓ, f_i(ℓ)),
218+
// > deleting f_i and each share afterward except for (i, f_i(i)),
219+
// > which they keep for themselves.
220+
let signing_share = SigningShare::from_coefficients(&secret_package.coefficients, ell);
221+
222+
round2_packages.insert(
223+
ell,
224+
round2::Package {
225+
header: Header::default(),
226+
signing_share,
227+
},
228+
);
229+
}
230+
let fii = evaluate_polynomial(secret_package.identifier, &secret_package.coefficients);
231+
232+
Ok((
233+
round2::SecretPackage {
234+
identifier: secret_package.identifier,
235+
commitment: secret_package.commitment,
236+
secret_share: fii,
237+
min_signers: secret_package.min_signers,
238+
max_signers: secret_package.max_signers,
239+
},
240+
round2_packages,
241+
))
242+
}
243+
244+
/// This is the step that actually refreshes the shares. New public key packages
245+
/// and key packages are created.
246+
pub fn refresh_dkg_shares<C: Ciphersuite>(
247+
round2_secret_package: &round2::SecretPackage<C>,
248+
round1_packages: &BTreeMap<Identifier<C>, round1::Package<C>>,
249+
round2_packages: &BTreeMap<Identifier<C>, round2::Package<C>>,
250+
old_pub_key_package: PublicKeyPackage<C>,
251+
old_key_package: KeyPackage<C>,
252+
) -> Result<(KeyPackage<C>, PublicKeyPackage<C>), Error<C>> {
253+
// Add identity commitment back into round1_packages
254+
let mut new_round_1_packages = BTreeMap::new();
255+
for (sender_identifier, round1_package) in round1_packages {
256+
// The identity commitment needs to be added to the VSS commitment for every round 1 package
257+
let identity_commitment: Vec<CoefficientCommitment<C>> =
258+
vec![CoefficientCommitment::new(C::Group::identity())];
259+
260+
let refreshing_share_commitments: Vec<CoefficientCommitment<C>> = identity_commitment
261+
.into_iter()
262+
.chain(round1_package.commitment.0.clone())
263+
.collect();
264+
265+
let new_commitments =
266+
VerifiableSecretSharingCommitment::<C>::new(refreshing_share_commitments);
267+
268+
let new_round_1_package = Package {
269+
header: round1_package.header,
270+
commitment: new_commitments,
271+
proof_of_knowledge: round1_package.proof_of_knowledge,
272+
};
273+
274+
new_round_1_packages.insert(*sender_identifier, new_round_1_package);
275+
}
276+
277+
if new_round_1_packages.len() != (round2_secret_package.max_signers - 1) as usize {
278+
return Err(Error::IncorrectNumberOfPackages);
279+
}
280+
if new_round_1_packages.len() != round2_packages.len() {
281+
return Err(Error::IncorrectNumberOfPackages);
282+
}
283+
if new_round_1_packages
284+
.keys()
285+
.any(|id| !round2_packages.contains_key(id))
286+
{
287+
return Err(Error::IncorrectPackage);
288+
}
289+
290+
let mut signing_share = <<C::Group as Group>::Field>::zero();
291+
292+
for (sender_identifier, round2_package) in round2_packages {
293+
// Round 2, Step 2
294+
//
295+
// > Each P_i verifies their shares by calculating:
296+
// > g^{f_ℓ(i)} ≟ ∏^{t−1}_{k=0} φ^{i^k mod q}_{ℓk}, aborting if the
297+
// > check fails.
298+
let ell = *sender_identifier;
299+
let f_ell_i = round2_package.signing_share;
300+
301+
let commitment = &new_round_1_packages
302+
.get(&ell)
303+
.ok_or(Error::PackageNotFound)?
304+
.commitment;
305+
306+
// The verification is exactly the same as the regular SecretShare verification;
307+
// however the required components are in different places.
308+
// Build a temporary SecretShare so what we can call verify().
309+
let secret_share = SecretShare {
310+
header: Header::default(),
311+
identifier: round2_secret_package.identifier,
312+
signing_share: f_ell_i,
313+
commitment: commitment.clone(),
314+
};
315+
316+
// Verify the share. We don't need the result.
317+
let _ = secret_share.verify()?;
318+
319+
// Round 2, Step 3
320+
//
321+
// > Each P_i calculates their long-lived private signing share by computing
322+
// > s_i = ∑^n_{ℓ=1} f_ℓ(i), stores s_i securely, and deletes each f_ℓ(i).
323+
signing_share = signing_share + f_ell_i.to_scalar();
324+
}
325+
326+
signing_share = signing_share + round2_secret_package.secret_share;
327+
328+
// Build new signing share
329+
let old_signing_share = old_key_package.signing_share.to_scalar();
330+
signing_share = signing_share + old_signing_share;
331+
let signing_share = SigningShare::new(signing_share);
332+
333+
// Round 2, Step 4
334+
//
335+
// > Each P_i calculates their public verification share Y_i = g^{s_i}.
336+
let verifying_share = signing_share.into();
337+
338+
let commitments: BTreeMap<_, _> = new_round_1_packages
339+
.iter()
340+
.map(|(id, package)| (*id, &package.commitment))
341+
.chain(iter::once((
342+
round2_secret_package.identifier,
343+
&round2_secret_package.commitment,
344+
)))
345+
.collect();
346+
347+
let zero_shares_public_key_package = PublicKeyPackage::from_dkg_commitments(&commitments)?;
348+
349+
let mut new_verifying_shares = BTreeMap::new();
350+
351+
for (identifier, verifying_share) in zero_shares_public_key_package.verifying_shares {
352+
let new_verifying_share = verifying_share.to_element()
353+
+ old_pub_key_package
354+
.verifying_shares
355+
.get(&identifier)
356+
.ok_or(Error::UnknownIdentifier)?
357+
.to_element();
358+
new_verifying_shares.insert(identifier, VerifyingShare::new(new_verifying_share));
359+
}
360+
361+
let public_key_package = PublicKeyPackage {
362+
header: old_pub_key_package.header,
363+
verifying_shares: new_verifying_shares,
364+
verifying_key: old_pub_key_package.verifying_key,
365+
};
366+
367+
let key_package = KeyPackage {
368+
header: Header::default(),
369+
identifier: round2_secret_package.identifier,
370+
signing_share,
371+
verifying_share,
372+
verifying_key: public_key_package.verifying_key,
373+
min_signers: round2_secret_package.min_signers,
374+
};
375+
376+
Ok((key_package, public_key_package))
377+
}

frost-core/src/tests/ciphersuite_generic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ fn check_part3_errors<C: Ciphersuite>(
566566
}
567567

568568
/// Check that calling dkg::part3() with distinct sets of participants fail.
569-
fn check_part3_different_participants<C: Ciphersuite>(
569+
pub fn check_part3_different_participants<C: Ciphersuite>(
570570
max_signers: u16,
571571
round2_secret_packages: BTreeMap<Identifier<C>, frost::keys::dkg::round2::SecretPackage<C>>,
572572
received_round1_packages: BTreeMap<

0 commit comments

Comments
 (0)