Skip to content

Commit d942bf5

Browse files
committed
clearer synopsis, docs, comments
1 parent 71d0f1a commit d942bf5

File tree

1 file changed

+63
-56
lines changed

1 file changed

+63
-56
lines changed

schnorr_fun/src/frost.rs

+63-56
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! ## FROST multisignature scheme
22
//!
3-
//! The FROST (Flexible Round-Optimize Schnorr Threshold) multisignature scheme allows you aggregate
3+
//! The FROST (Flexible Round-Optimized Schnorr Threshold) multisignature scheme allows you aggregate
44
//! multiple public keys into a single public key. To sign a message under this public key, a threshold t-of-n secret keys
55
//! must use a common set of nonces to each produce a signature share. These signature shares are then combined
66
//! to form a signature that is valid under the aggregate key.
@@ -23,26 +23,27 @@
2323
//! let frost = Frost::new(Schnorr::<Sha256, Deterministic<Sha256>>::new(
2424
//! Deterministic::<Sha256>::default(),
2525
//! ));
26-
//! // create a random secret scalar poly with two coefficients,
27-
//! // corresponding to FROST multisig with a threshold of two
26+
//! // to create a FROST multisig with a threshold of two, each participant creates
27+
//! // a random secret scalar polynomial with two coefficients.
2828
//! let scalar_poly = ScalarPoly::random(2, &mut rand::thread_rng());
2929
//! # let scalar_poly2 = ScalarPoly::random(2, &mut rand::thread_rng());
3030
//! # let scalar_poly3 = ScalarPoly::random(2, &mut rand::thread_rng());
3131
//! // share our public point poly, and recieve the point polys from other participants
3232
//! # let point_poly2 = scalar_poly2.to_point_poly();
3333
//! # let point_poly3 = scalar_poly3.to_point_poly();
3434
//! let point_polys = vec![scalar_poly.to_point_poly(), point_poly2, point_poly3];
35-
//! // create secret shares and proof of possession using our secret scalar poly
35+
//! // create secret shares and proofs-of-possession using our secret scalar polynomial
3636
//! let keygen = frost.new_keygen(point_polys).unwrap();
3737
//! let (shares, pop) = frost.create_shares(&keygen, scalar_poly);
3838
//! # let (shares2, pop2) = frost.create_shares(&keygen, scalar_poly2);
3939
//! # let (shares3, pop3) = frost.create_shares(&keygen, scalar_poly3);
40-
//! // send shares at index i and all proofs-of-possession to each other participant i,
40+
//! // send the shares at index i and all proofs-of-possession to each other participant i,
4141
//! // and recieve our shares from each other participant as well as their proofs-of-possession.
4242
//! let recieved_shares = vec![shares[0].clone(), shares2[0].clone(), shares3[0].clone()];
4343
//! # let recieved_shares3 = vec![shares[2].clone(), shares2[2].clone(), shares3[2].clone()];
4444
//! let proofs_of_possession = vec![pop, pop2, pop3];
45-
//! // finish keygen by calculating our secret share of the joint FROST key
45+
//! // finish keygen by verifying the shares we recieved as well as proofs-of-possession
46+
//! // and calulate our secret share of the joint FROST key
4647
//! let (secret_share, frost_key) = frost
4748
//! .finish_keygen(
4849
//! keygen.clone(),
@@ -59,8 +60,8 @@
5960
//! # proofs_of_possession.clone(),
6061
//! # )
6162
//! # .unwrap();
62-
//! // for signing we must have a unique session ID to derive nonces.
63-
//! // we should include all the information that is publicly available.
63+
//! // for signing we must have a unique session ID to derive nonces such that nonces
64+
//! // are never reused. For gen_nonce we use all information that is publicly available.
6465
//! let verification_shares_bytes: Vec<_> = frost_key
6566
//! .verification_shares
6667
//! .iter()
@@ -92,23 +93,21 @@
9293
//! // start a sign session with these nonces for this message
9394
//! let session = frost.start_sign_session(&frost_key, nonces, Message::plain("test", b"test"));
9495
//! # let session3 = frost.start_sign_session(&frost_key, nonces3, Message::plain("test", b"test"));
95-
//! // create a partial signature
96+
//! // create a partial signature using our secret share and secret nonce
9697
//! let sig = frost.sign(&frost_key, &session, 0, &secret_share, nonce);
9798
//! # let sig3 = frost.sign(&frost_key, &session3, 2, &secret_share3, nonce3);
9899
//! // recieve partial signature(s) from other participant(s) and verify
99100
//! assert!(frost.verify_signature_share(&frost_key, &session, 2, sig3));
100-
//! // combine signature
101+
//! // combine signature shares into a single signature that is valid under the joint key
101102
//! let combined_sig = frost.combine_signature_shares(&frost_key, &session, vec![sig, sig3]);
102103
//! assert!(frost.schnorr.verify(
103104
//! &frost_key.joint_public_key,
104105
//! Message::<Public>::plain("test", b"test"),
105106
//! &combined_sig
106107
//! ));
107108
//! ```
108-
use crate::{
109-
musig::{Nonce, NonceKeyPair},
110-
Message, Schnorr, Signature, Vec,
111-
};
109+
pub use crate::binonce::{Nonce, NonceKeyPair};
110+
use crate::{Message, Schnorr, Signature, Vec};
112111
use core::iter;
113112
use rand_core::{CryptoRng, RngCore};
114113
use secp256kfun::{
@@ -123,10 +122,13 @@ use secp256kfun::{
123122
use std::collections::BTreeMap;
124123

125124
/// The FROST context.
125+
/// H: hash for challenges and creating a keygen_id
126+
/// NG: hash for nonce generation
126127
#[derive(Clone)]
127128
pub struct Frost<H, NG: AddTag> {
128-
/// The instance of the Schnorr signature scheme
129+
/// The instance of the Schnorr signature scheme.
129130
pub schnorr: Schnorr<H, NG>,
131+
/// The hash used to generate the keygen_id.
130132
keygen_id_hash: H,
131133
}
132134

@@ -172,6 +174,8 @@ impl ScalarPoly {
172174

173175
/// Create a scalar polynomial where the first coefficient is a specified secret and
174176
/// the remaining coefficients are random.
177+
///
178+
/// This will be used for creating secret polynomials with known & reproducable secrets.
175179
pub fn random_using_secret(
176180
n_coefficients: u32,
177181
secret: Scalar,
@@ -219,7 +223,7 @@ pub struct PointPoly<Z = NonZero>(
219223
);
220224

221225
impl<Z> PointPoly<Z> {
222-
/// Evaluate the polynomial at position x.
226+
/// Evaluate the point polynomial at position x.
223227
pub fn eval(&self, x: u32) -> Point<Jacobian, Public, Zero> {
224228
let x = Scalar::from(x)
225229
.expect_nonzero("must be non-zero")
@@ -234,13 +238,15 @@ impl<Z> PointPoly<Z> {
234238

235239
/// Combine a vector of point polynomials into a joint polynomial.
236240
fn combine(mut polys: impl Iterator<Item = Self>) -> PointPoly<Zero> {
241+
// take the first point polynomial and collect its coefficients
237242
let mut combined_poly = polys
238243
.next()
239244
.expect("cannot combine empty list of polys")
240245
.0
241246
.into_iter()
242247
.map(|p| p.mark::<(Jacobian, Zero)>())
243248
.collect::<Vec<_>>();
249+
// add the coefficients of the remaining polys
244250
for poly in polys {
245251
for (combined_point, point) in combined_poly.iter_mut().zip(poly.0) {
246252
*combined_point = g!({ *combined_point } + point);
@@ -279,16 +285,16 @@ impl KeyGen {
279285
}
280286
}
281287

282-
/// First round errors
288+
/// First round keygen errors
283289
#[derive(Debug, Clone)]
284290
pub enum NewKeyGenError {
285291
/// Received polynomial is of differing length.
286292
PolyDifferentLength(usize),
287293
/// Number of parties is less than the length of polynomials specifying the threshold.
288294
NotEnoughParties,
289-
/// Frost key is zero. Should be impossible, or maliciously chosen.
295+
/// Frost key is zero. This should be impossible, likely has been maliciously chosen.
290296
ZeroFrostKey,
291-
/// Verification share is zero. Should be impossible, or maliciously chosen.
297+
/// Verification share is zero. This should be impossible, likely has been maliciously chosen.
292298
ZeroVerificationShare,
293299
}
294300

@@ -298,8 +304,8 @@ impl core::fmt::Display for NewKeyGenError {
298304
match self {
299305
PolyDifferentLength(i) => write!(f, "polynomial commitment from party at index {} was a different length", i),
300306
NotEnoughParties => write!(f, "the number of parties was less than the threshold"),
301-
ZeroFrostKey => write!(f, "The joint FROST key was zero. This means one of the parties was possibly malicious and you are not protecting against this properly"),
302-
ZeroVerificationShare => write!(f, "One of the verification shares was malicious so we must abort the protocol"),
307+
ZeroFrostKey => write!(f, "The joint FROST key was zero. This should be impossible, one party is acting maliciously."),
308+
ZeroVerificationShare => write!(f, "Zero verification share. This should be impossible, one party is acting maliciously."),
303309
}
304310
}
305311
}
@@ -320,10 +326,15 @@ impl core::fmt::Display for FinishKeyGenError {
320326
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
321327
use FinishKeyGenError::*;
322328
match self {
323-
InvalidShare(i) => write!(f, "the share provided by party at index {} was invalid", i),
329+
InvalidShare(i) => write!(
330+
f,
331+
"the secret share at index {} does not match the expected evaluation \
332+
of their point polynomial at our index. Check that the order and our index is correct",
333+
i
334+
),
324335
&InvalidProofOfPossession(i) => write!(
325336
f,
326-
"the proof of possession provided by party at index {} was invalid",
337+
"the proof of possession provided by party at index {} was invalid, check ordering.",
327338
i
328339
),
329340
}
@@ -341,15 +352,15 @@ impl std::error::Error for FinishKeyGenError {}
341352
serde(crate = "serde_crate")
342353
)]
343354
pub struct FrostKey {
344-
/// The joint public key of the FROST multisignature
355+
/// The joint public key of the FROST multisignature.
345356
pub joint_public_key: Point<EvenY>,
346-
/// Everyone else's point poly evaluated at your index, used in partial signature validation.
357+
/// Everyone else's point polynomial evaluated at your index, used in partial signature validation.
347358
pub verification_shares: Vec<Point>,
348359
/// Number of partial signatures required to create a combined signature under this key.
349360
pub threshold: u32,
350361
/// Taproot tweak applied to this FROST key, tracks the aggregate tweak.
351362
tweak: Scalar<Public, Zero>,
352-
/// Whether the secrets need negation in order to sign for the X-Only key
363+
/// Whether the secrets need negation in order to sign for the X-Only key.
353364
needs_negation: bool,
354365
}
355366

@@ -369,26 +380,25 @@ impl FrostKey {
369380
///
370381
/// This is how you embed a taproot commitment into a key.
371382
///
372-
/// Also updates whether the FROST key needs negation.
373-
/// XOR of existing FROST key needs_negation and new tweaked key needs_negation.
374-
/// If both need negation, they will cancel out.
375-
///
376-
/// Public key
377-
/// X = (b*x) * G
378-
/// where b = 1 or -1
379-
/// For a tweak t: X' = X + t * G.
380-
/// If X' needs negation then we need secret
381-
/// -(b*x + t) = -b*x - t
382-
/// So new b = -b and t = -t.
383-
/// If X' doesn't need negation, leave b as is.
384-
/// i.e. previous needs_negation XOR new needs_negation.
385-
///
386383
/// ## Return value
387384
///
388385
/// Returns a new FrostKey with the same parties but a different aggregated public key.
389386
/// In the erroneous case that the tweak is exactly equal to the negation of the aggregate
390387
/// secret key it returns `None`.
391388
pub fn tweak(&mut self, tweak: Scalar<impl Secrecy, impl ZeroChoice>) -> Option<Self> {
389+
// Also updates whether the FROST key needs negation.
390+
// XOR of existing FROST key needs_negation and new tweaked key needs_negation.
391+
// If both need negation, they will cancel out.
392+
//
393+
// Public key
394+
// X = (b*x) * G
395+
// where b = 1 or -1
396+
// For a tweak t: X' = X + t * G.
397+
// If X' needs negation then we need secret
398+
// -(b*x + t) = -b*x - t
399+
// So new b = -b and t = -t.
400+
// If X' doesn't need negation, leave b as is.
401+
// i.e. previous needs_negation XOR new needs_negation.
392402
let new_tweak = s!(0 + tweak).mark::<Public>();
393403
let (joint_public_key, tweaked_needs_negation) = g!(self.joint_public_key + new_tweak * G)
394404
.mark::<NonZero>()?
@@ -426,9 +436,8 @@ impl<H: Digest<OutputSize = U32> + Clone, NG: AddTag + NonceGen> Frost<H, NG> {
426436
/// Secret shares are created for every other participant by evaluating our secret polynomial
427437
/// at their participant index. f(i) for 1<=i<=n.
428438
///
429-
/// Each secret share f(i) needs to be securely communicated to participant i.
430-
///
431-
/// Also creates a proof of possession for the first coefficient of our secret scalar polynomial.
439+
/// Each secret share f(i) needs to be securely communicated to participant i. Additionally
440+
/// we share a proof of possession for the first coefficient in our secret scalar polynomial.
432441
///
433442
/// ## Return value
434443
///
@@ -454,11 +463,11 @@ impl<H: Digest<OutputSize = U32> + Clone, NG: AddTag + NonceGen> Frost<H, NG> {
454463
}
455464

456465
impl<H: Digest<OutputSize = U32> + Clone, NG: AddTag> Frost<H, NG> {
457-
/// Verify a proof of possession against a participant's point polynomial
466+
/// Verify a proof of possession against a participant's committed point polynomial
458467
///
459468
/// ## Return value
460469
///
461-
/// Returns `bool` true if the proof of possession matches this point poly,
470+
/// Returns `bool` true if the proof of possession matches the point polynomial
462471
fn verify_pop(&self, KeyGen: &KeyGen, point_poly: &PointPoly, pop: Signature) -> bool {
463472
let (even_poly_point, _) = point_poly.0[0].into_point_with_even_y();
464473

@@ -473,13 +482,13 @@ impl<H: Digest<OutputSize = U32> + Clone, NG: AddTag> Frost<H, NG> {
473482
impl<H: Digest<OutputSize = U32> + Clone, NG: AddTag> Frost<H, NG> {
474483
/// Collect all the public polynomials into a KeyGen session with a FrostKey.
475484
///
476-
/// Takes a vector of point polynomials with your polynomial at index 0.
485+
/// Takes a vector of point polynomials to use for this FrostKey
477486
///
478487
/// Also prepares a vector of verification shares for later.
479488
///
480489
/// ## Return value
481490
///
482-
/// Returns a KeyGen
491+
/// Returns a KeyGen containing a FrostKey
483492
pub fn new_keygen(&self, point_polys: Vec<PointPoly>) -> Result<KeyGen, NewKeyGenError> {
484493
let len_first_poly = point_polys[0].poly_len();
485494
{
@@ -578,16 +587,15 @@ impl<H: Digest<OutputSize = U32> + Clone, NG: AddTag> Frost<H, NG> {
578587
}
579588

580589
let total_secret_share = total_secret_share.expect_nonzero(
581-
"since verification shares are non-zero, corresponding secret shares cannot be zero",
590+
"since verification shares are non-zero, the total secret share cannot be zero",
582591
);
583592

584593
Ok((total_secret_share, KeyGen.frost_key))
585594
}
586595
}
587596

588597
/// Calculate the lagrange coefficient for participant with index x_j and other signers indexes x_ms
589-
pub fn lagrange_lambda(x_j: u32, x_ms: &[u32]) -> Scalar {
590-
// TODO change to a single inverse https://people.maths.ox.ac.uk/trefethen/barycentric.pdf (?)
598+
fn lagrange_lambda(x_j: u32, x_ms: &[u32]) -> Scalar {
591599
let x_j = Scalar::from(x_j).expect_nonzero("target xcoord can not be zero");
592600
x_ms.iter()
593601
.map(|x_m| Scalar::from(*x_m).expect_nonzero("index can not be zero"))
@@ -627,10 +635,8 @@ impl<H: Digest<OutputSize = U32> + Clone, NG: NonceGen + AddTag> Frost<H, NG> {
627635
) -> SignSession {
628636
let mut nonce_map: BTreeMap<_, _> =
629637
nonces.into_iter().map(|(i, nonce)| (i, nonce)).collect();
630-
// assert_eq!(nonces.len(), nonce_map.len());
631-
assert!(frost_key.threshold <= nonce_map.len() as u32);
632638

633-
let agg_nonces_R1_R2: Vec<Point> = nonce_map
639+
let agg_nonce_points: [Point; 2] = nonce_map
634640
.iter()
635641
.fold([Point::zero().mark::<Jacobian>(); 2], |acc, (_, nonce)| {
636642
[
@@ -645,9 +651,10 @@ impl<H: Digest<OutputSize = U32> + Clone, NG: NonceGen + AddTag> Frost<H, NG> {
645651
.mark::<NonZero>()
646652
.expect("aggregate nonce should be non-zero")
647653
})
648-
.collect();
654+
.collect::<Vec<_>>()
655+
.try_into()
656+
.expect("there are only R1 and R2, collecting cant fail");
649657

650-
let agg_nonce_points: [Point; 2] = [agg_nonces_R1_R2[0], agg_nonces_R1_R2[1]];
651658
let binding_coeff = Scalar::from_hash(
652659
self.schnorr
653660
.challenge_hash()
@@ -837,7 +844,7 @@ mod test {
837844
dbg!(threshold, n_parties);
838845
assert!(threshold <= n_parties);
839846

840-
// create some scalar poly for each party
847+
// create some scalar polynomial for each party
841848
let mut scalar_polys = vec![];
842849
for i in 1..=n_parties {
843850
let scalar_poly = (1..=threshold).map(|j| Scalar::from_non_zero_u32(NonZeroU32::new(i*j).expect("starts from 1"))).collect();

0 commit comments

Comments
 (0)