Skip to main content

wtfrost/
bip340.rs

1use p256k1::{
2    field,
3    point::{Error as PointError, Point, G},
4    scalar::Scalar,
5};
6
7use crate::{common::Signature, compute};
8
9/// Errors from BIP-340 operations
10#[derive(Debug)]
11pub enum Error {
12    /// Point R is odd
13    OddR,
14    /// Error doing point operations
15    Point(PointError),
16}
17
18/// A SchnorrProof in BIP-340 format
19#[allow(non_snake_case)]
20#[derive(Debug, PartialEq, Eq)]
21pub struct SchnorrProof {
22    /// The schnorr public commitment (FROST Signature R)
23    pub r: field::Element,
24    /// The schnorr response (FROST Signature z)
25    pub s: Scalar,
26}
27
28impl SchnorrProof {
29    /// Construct a BIP-340 schnorr proof from a FROST signature
30    pub fn new(sig: &Signature) -> Result<Self, Error> {
31        if !sig.R.has_even_y() {
32            Err(Error::OddR)
33        } else {
34            Ok(Self {
35                r: sig.R.x(),
36                s: sig.z,
37            })
38        }
39    }
40
41    /// Verify a BIP-340 schnorr proof
42    #[allow(non_snake_case)]
43    pub fn verify(&self, public_key: &field::Element, msg: &[u8]) -> bool {
44        let Y = match Point::lift_x(public_key) {
45            Ok(Y) => Y,
46            Err(_) => return false,
47        };
48        let R = match Point::lift_x(&self.r) {
49            Ok(R) => R,
50            Err(_) => return false,
51        };
52        let c = compute::challenge(&Y, &R, msg);
53        let Rp = self.s * G - c * Y;
54
55        Rp.x() == self.r
56    }
57
58    /// Serialize this proof into a 64-byte buffer
59    pub fn to_bytes(&self) -> [u8; 64] {
60        let mut bytes = [0u8; 64];
61
62        bytes[0..32].copy_from_slice(&self.r.to_bytes());
63        bytes[32..64].copy_from_slice(&self.s.to_bytes());
64
65        bytes
66    }
67}
68
69impl From<[u8; 64]> for SchnorrProof {
70    fn from(bytes: [u8; 64]) -> Self {
71        let mut r_bytes = [0u8; 32];
72        let mut s_bytes = [0u8; 32];
73
74        r_bytes.copy_from_slice(&bytes[0..32]);
75        s_bytes.copy_from_slice(&bytes[32..64]);
76
77        Self {
78            r: field::Element::from(r_bytes),
79            s: Scalar::from(s_bytes),
80        }
81    }
82}
83
84/// Helper functions for tests
85pub mod test_helpers {
86    use crate::{
87        common::{PolyCommitment, PublicNonce, SignatureShare},
88        errors::DkgError,
89        traits, Point,
90    };
91
92    use hashbrown::HashMap;
93    use rand_core::{CryptoRng, RngCore};
94
95    /// Run a distributed key generation round
96    #[allow(non_snake_case)]
97    pub fn dkg<RNG: RngCore + CryptoRng, Signer: traits::Signer>(
98        signers: &mut [Signer],
99        rng: &mut RNG,
100    ) -> Result<Vec<PolyCommitment>, HashMap<usize, DkgError>> {
101        let mut A: Vec<PolyCommitment> = signers
102            .iter()
103            .flat_map(|s| s.get_poly_commitments(rng))
104            .collect();
105
106        // keep trying until the group key has even y coord
107        loop {
108            let group_key = A.iter().fold(Point::new(), |s, a| s + a.A[0]);
109            if group_key.has_even_y() {
110                break;
111            }
112
113            for signer in signers.iter_mut() {
114                signer.reset_polys(rng);
115            }
116
117            A = signers
118                .iter()
119                .flat_map(|s| s.get_poly_commitments(rng))
120                .collect();
121        }
122
123        let mut private_shares = HashMap::new();
124        for signer in signers.iter() {
125            for (signer_id, signer_shares) in signer.get_shares() {
126                private_shares.insert(signer_id, signer_shares);
127            }
128        }
129
130        let mut secret_errors = HashMap::new();
131        for signer in signers.iter_mut() {
132            if let Err(signer_secret_errors) = signer.compute_secrets(&private_shares, &A) {
133                secret_errors.extend(signer_secret_errors.into_iter());
134            }
135        }
136
137        if secret_errors.is_empty() {
138            Ok(A)
139        } else {
140            Err(secret_errors)
141        }
142    }
143
144    /// Run a signing round for the passed `msg`
145    #[allow(non_snake_case)]
146    pub fn sign<RNG: RngCore + CryptoRng, Signer: traits::Signer>(
147        msg: &[u8],
148        signers: &mut [Signer],
149        rng: &mut RNG,
150    ) -> (Vec<PublicNonce>, Vec<SignatureShare>) {
151        let signer_ids: Vec<usize> = signers.iter().map(|s| s.get_id()).collect();
152        let key_ids: Vec<usize> = signers.iter().flat_map(|s| s.get_key_ids()).collect();
153        let mut nonces: Vec<PublicNonce> =
154            signers.iter_mut().flat_map(|s| s.gen_nonces(rng)).collect();
155
156        loop {
157            let (_, R) = Signer::compute_intermediate(msg, &signer_ids, &key_ids, &nonces);
158            if R.has_even_y() {
159                break;
160            }
161            nonces = signers.iter_mut().flat_map(|s| s.gen_nonces(rng)).collect();
162        }
163
164        let shares = signers
165            .iter()
166            .flat_map(|s| s.sign(msg, &signer_ids, &key_ids, &nonces))
167            .collect();
168
169        (nonces, shares)
170    }
171}
172
173#[cfg(test)]
174mod test {
175    use super::{test_helpers, SchnorrProof};
176
177    use crate::{traits::Signer, v1, v2};
178    use rand_core::OsRng;
179
180    #[test]
181    #[allow(non_snake_case)]
182    fn test_schnorr_sign_verify_v1() {
183        let mut rng = OsRng::default();
184
185        // First create and verify a frost signature
186        let msg = "It was many and many a year ago".as_bytes();
187        let N: usize = 10;
188        let T: usize = 7;
189        let signer_ids: Vec<Vec<usize>> = [
190            [0, 1, 2].to_vec(),
191            [3, 4].to_vec(),
192            [5, 6, 7].to_vec(),
193            [8, 9].to_vec(),
194        ]
195        .to_vec();
196        let mut signers: Vec<v1::Signer> = signer_ids
197            .iter()
198            .map(|ids| v1::Signer::new(ids, N, T, &mut rng))
199            .collect();
200
201        let A = match test_helpers::dkg(&mut signers, &mut rng) {
202            Ok(A) => A,
203            Err(secret_errors) => {
204                panic!("Got secret errors from DKG: {:?}", secret_errors);
205            }
206        };
207
208        let mut S = [signers[0].clone(), signers[1].clone(), signers[3].clone()].to_vec();
209        let mut sig_agg =
210            v1::SignatureAggregator::new(N, T, A.clone()).expect("aggregator ctor failed");
211
212        let (nonces, sig_shares) = test_helpers::sign(&msg, &mut S, &mut rng);
213        let sig = match sig_agg.sign(&msg, &nonces, &sig_shares) {
214            Err(e) => panic!("Aggregator sign failed: {:?}", e),
215            Ok(sig) => sig,
216        };
217
218        // now create a SchnorrProof from the frost signature
219        let proof = SchnorrProof::new(&sig).unwrap();
220
221        assert!(proof.verify(&sig_agg.poly[0].x(), msg));
222
223        // now ser/de the proof
224        let proof_bytes = proof.to_bytes();
225        let proof_deser = SchnorrProof::from(proof_bytes);
226
227        assert_eq!(proof, proof_deser);
228        assert!(proof_deser.verify(&sig_agg.poly[0].x(), msg));
229    }
230
231    #[test]
232    #[allow(non_snake_case)]
233    fn test_schnorr_sign_verify_v2() {
234        let mut rng = OsRng::default();
235
236        // First create and verify a frost signature
237        let msg = "It was many and many a year ago".as_bytes();
238        let Nk: usize = 10;
239        let Np: usize = 4;
240        let T: usize = 7;
241        let signer_ids: Vec<Vec<usize>> = [
242            [0, 1, 2].to_vec(),
243            [3, 4].to_vec(),
244            [5, 6, 7].to_vec(),
245            [8, 9].to_vec(),
246        ]
247        .to_vec();
248        let mut signers: Vec<v2::Signer> = signer_ids
249            .iter()
250            .enumerate()
251            .map(|(id, ids)| v2::Signer::new(id, ids, Np, Nk, T, &mut rng))
252            .collect();
253
254        let A = match test_helpers::dkg(&mut signers, &mut rng) {
255            Ok(A) => A,
256            Err(secret_errors) => {
257                panic!("Got secret errors from DKG: {:?}", secret_errors);
258            }
259        };
260
261        let mut S = [signers[0].clone(), signers[1].clone(), signers[3].clone()].to_vec();
262        let key_ids = S
263            .iter()
264            .flat_map(|s| s.get_key_ids())
265            .collect::<Vec<usize>>();
266        let mut sig_agg =
267            v2::SignatureAggregator::new(Nk, T, A.clone()).expect("aggregator ctor failed");
268
269        let (nonces, sig_shares) = test_helpers::sign(&msg, &mut S, &mut rng);
270        let sig = match sig_agg.sign(&msg, &nonces, &sig_shares, &key_ids) {
271            Err(e) => panic!("Aggregator sign failed: {:?}", e),
272            Ok(sig) => sig,
273        };
274
275        // now create a SchnorrProof from the frost signature
276        let proof = SchnorrProof::new(&sig).unwrap();
277
278        assert!(proof.verify(&sig_agg.poly[0].x(), msg));
279
280        // now ser/de the proof
281        let proof_bytes = proof.to_bytes();
282        let proof_deser = SchnorrProof::from(proof_bytes);
283
284        assert_eq!(proof, proof_deser);
285        assert!(proof_deser.verify(&sig_agg.poly[0].x(), msg));
286    }
287}