1
- //! ## Description
1
+ //! ## FROST multisignature scheme
2
2
//!
3
- //! The FROST (Flexible Round-Optimize Schnorr Threshold) multisignature scheme lets you aggregate
4
- //! multiple public keys into a single public key that requires some threshold t-of-n secret keys to
5
- //! sign a signature under the aggregate key.
3
+ //! The FROST (Flexible Round-Optimize Schnorr Threshold) multisignature scheme allows you aggregate
4
+ //! multiple public keys into a single public key. To sign a message under this public key, a threshold t-of-n secret keys
5
+ //! must use a common set of nonces to each produce a signature share. These signature shares are then combined
6
+ //! to form a signature that is valid under the aggregate key.
6
7
//!
7
- //! This implementation has NOT yet been made compatible with other existing implementations
8
- //! [secp256k1-zkp]: https://github.com/ElementsProject/secp256k1-zkp/pull/138
8
+ //! This implementation has **not yet** been made compatible with other existing FROST implementations
9
+ //! (notably [secp256k1-zkp]).
9
10
//!
10
- //! See MuSig in this repository, the [FROST paper] and [Security of Multi- and Threshold Signatures].
11
+ //! For reference see the [FROST paper], the MuSig implementation in this repository, and also [Security of Multi- and Threshold Signatures].
11
12
//!
12
- //! [FROST paper]: https://eprint.iacr.org/2020/852.pdf
13
- //! [Security of Multi- and Threshold Signatures]: https://eprint.iacr.org/2021/1375.pdf
13
+ //! [secp256k1-zkp]: <https://github.com/ElementsProject/secp256k1-zkp/pull/138>
14
+ //! [FROST paper]: <https://eprint.iacr.org/2020/852.pdf>
15
+ //! [Security of Multi- and Threshold Signatures]: <https://eprint.iacr.org/2021/1375.pdf>
16
+ //!
17
+ //! ## Synopsis
18
+ //!
19
+ //! ```
20
+ //! use schnorr_fun::{frost::{Frost, ScalarPoly}, Schnorr, Message, nonce::Deterministic, fun::marker::Public};
21
+ //! use sha2::Sha256;
22
+ //! // use SHA256 with deterministic nonce generation
23
+ //! let frost = Frost::new(Schnorr::<Sha256, Deterministic<Sha256>>::new(
24
+ //! Deterministic::<Sha256>::default(),
25
+ //! ));
26
+ //! // create a random secret scalar poly with two coefficients,
27
+ //! // corresponding to FROST multisig with a threshold of two
28
+ //! let scalar_poly = ScalarPoly::random(2, &mut rand::thread_rng());
29
+ //! # let scalar_poly2 = ScalarPoly::random(2, &mut rand::thread_rng());
30
+ //! # let scalar_poly3 = ScalarPoly::random(2, &mut rand::thread_rng());
31
+ //! // share our public point poly, and recieve the point polys from other participants
32
+ //! # let point_poly2 = scalar_poly2.to_point_poly();
33
+ //! # let point_poly3 = scalar_poly3.to_point_poly();
34
+ //! 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
36
+ //! let keygen = frost.new_keygen(point_polys).unwrap();
37
+ //! let (shares, pop) = frost.create_shares(&keygen, scalar_poly);
38
+ //! # let (shares2, pop2) = frost.create_shares(&keygen, scalar_poly2);
39
+ //! # 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,
41
+ //! // and recieve our shares from each other participant as well as their proofs-of-possession.
42
+ //! let recieved_shares = vec![shares[0].clone(), shares2[0].clone(), shares3[0].clone()];
43
+ //! # let recieved_shares3 = vec![shares[2].clone(), shares2[2].clone(), shares3[2].clone()];
44
+ //! let proofs_of_possession = vec![pop, pop2, pop3];
45
+ //! // finish keygen by calculating our secret share of the joint FROST key
46
+ //! let (secret_share, frost_key) = frost
47
+ //! .finish_keygen(
48
+ //! keygen.clone(),
49
+ //! 0,
50
+ //! recieved_shares,
51
+ //! proofs_of_possession.clone(),
52
+ //! )
53
+ //! .unwrap();
54
+ //! # let (secret_share3, _frost_key3) = frost
55
+ //! # .finish_keygen(
56
+ //! # keygen.clone(),
57
+ //! # 2,
58
+ //! # recieved_shares3,
59
+ //! # proofs_of_possession.clone(),
60
+ //! # )
61
+ //! # .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.
64
+ //! let verification_shares_bytes: Vec<_> = frost_key
65
+ //! .verification_shares
66
+ //! .iter()
67
+ //! .map(|share| share.to_bytes())
68
+ //! .collect();
69
+ //! // create a unique session ID for this signing session
70
+ //! let sid = [
71
+ //! frost_key.joint_public_key.to_bytes().as_slice(),
72
+ //! verification_shares_bytes.concat().as_slice(),
73
+ //! b"frost-very-unique-id".as_slice(),
74
+ //! b"0".as_slice(),
75
+ //! ]
76
+ //! .concat();
77
+ //! # let sid3 = [
78
+ //! # frost_key.joint_public_key.to_bytes().as_slice(),
79
+ //! # verification_shares_bytes.concat().as_slice(),
80
+ //! # b"frost-very-unique-id".as_slice(),
81
+ //! # b"2".as_slice(),
82
+ //! # ]
83
+ //! # .concat();
84
+ //! // generate nonces for this signing session
85
+ //! let nonce = frost.gen_nonce(&secret_share, &sid);
86
+ //! # let nonce3 = frost.gen_nonce(&secret_share3, &sid3);
87
+ //! // share your public nonce with the other signing participant(s)
88
+ //! # let recieved_nonce3 = nonce3.public();
89
+ //! // recieve public nonces from other participants with their index
90
+ //! let nonces = vec![(0, nonce.public()), (2, recieved_nonce3)];
91
+ //! # let nonces3 = vec![(0, nonce.public()), (2, recieved_nonce3)];
92
+ //! // start a sign session with these nonces for this message
93
+ //! let session = frost.start_sign_session(&frost_key, nonces, Message::plain("test", b"test"));
94
+ //! # let session3 = frost.start_sign_session(&frost_key, nonces3, Message::plain("test", b"test"));
95
+ //! // create a partial signature
96
+ //! let sig = frost.sign(&frost_key, &session, 0, &secret_share, nonce);
97
+ //! # let sig3 = frost.sign(&frost_key, &session3, 2, &secret_share3, nonce3);
98
+ //! // recieve partial signature(s) from other participant(s) and verify
99
+ //! assert!(frost.verify_signature_share(&frost_key, &session, 2, sig3));
100
+ //! // combine signature
101
+ //! let combined_sig = frost.combine_signature_shares(&frost_key, &session, vec![sig, sig3]);
102
+ //! assert!(frost.schnorr.verify(
103
+ //! &frost_key.joint_public_key,
104
+ //! Message::<Public>::plain("test", b"test"),
105
+ //! &combined_sig
106
+ //! ));
107
+ //! ```
14
108
use crate :: {
15
109
musig:: { Nonce , NonceKeyPair } ,
16
110
Message , Schnorr , Signature , Vec ,
@@ -31,7 +125,8 @@ use std::collections::BTreeMap;
31
125
/// The FROST context.
32
126
#[ derive( Clone ) ]
33
127
pub struct Frost < H , NG : AddTag > {
34
- schnorr : Schnorr < H , NG > ,
128
+ /// The instance of the Schnorr signature scheme
129
+ pub schnorr : Schnorr < H , NG > ,
35
130
keygen_id_hash : H ,
36
131
}
37
132
@@ -246,10 +341,15 @@ impl std::error::Error for FinishKeyGenError {}
246
341
serde( crate = "serde_crate" )
247
342
) ]
248
343
pub struct FrostKey {
249
- joint_public_key : Point < EvenY > ,
250
- verification_shares : Vec < Point > ,
251
- threshold : u32 ,
344
+ /// The joint public key of the FROST multisignature
345
+ pub joint_public_key : Point < EvenY > ,
346
+ /// Everyone else's point poly evaluated at your index, used in partial signature validation.
347
+ pub verification_shares : Vec < Point > ,
348
+ /// Number of partial signatures required to create a combined signature under this key.
349
+ pub threshold : u32 ,
350
+ /// Taproot tweak applied to this FROST key, tracks the aggregate tweak.
252
351
tweak : Scalar < Public , Zero > ,
352
+ /// Whether the secrets need negation in order to sign for the X-Only key
253
353
needs_negation : bool ,
254
354
}
255
355
@@ -657,7 +757,7 @@ impl<H: Digest<OutputSize = U32> + Clone, NG: NonceGen + AddTag> Frost<H, NG> {
657
757
///
658
758
/// ## Return value
659
759
///
660
- /// Returns a combined schnorr [`schnorr_fun::signature:: Signature`] for the message.
760
+ /// Returns a combined schnorr [`Signature`] for the message.
661
761
/// Valid against the joint public key.
662
762
pub fn combine_signature_shares (
663
763
& self ,
0 commit comments