Skip to content

Commit 017aaf3

Browse files
authored
Merge pull request #80 from LLFourn/musig2
Implement MuSig2 multisignature scheme
2 parents c121f10 + 1150026 commit 017aaf3

18 files changed

+1590
-156
lines changed

schnorr_fun/src/adaptor/mod.rs

+52-45
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ use crate::{
5454
g,
5555
marker::*,
5656
nonce::NonceGen,
57-
s, Point, Scalar,
57+
s, Point, Scalar, G,
5858
},
5959
KeyPair, Message, Schnorr, Signature,
6060
};
@@ -78,7 +78,7 @@ pub trait EncryptedSign {
7878
) -> EncryptedSignature;
7979
}
8080

81-
impl<NG, CH, GT> EncryptedSign for Schnorr<CH, NG, GT>
81+
impl<NG, CH> EncryptedSign for Schnorr<CH, NG>
8282
where
8383
CH: Digest<OutputSize = U32> + Clone,
8484
NG: NonceGen,
@@ -98,7 +98,7 @@ where
9898
public => [X, Y, message]
9999
);
100100

101-
let R = g!(r * { self.G() } + Y)
101+
let R = g!(r * G + Y)
102102
// R_hat = r * G is sampled pseudorandomly for every Y which means R_hat + Y is also
103103
// be pseudoranodm and therefore will not be zero.
104104
// NOTE: Crucially we add Y to the nonce derivation to ensure this is true.
@@ -110,7 +110,7 @@ where
110110
// key before decrypting it
111111
r.conditional_negate(needs_negation);
112112

113-
let c = self.challenge(&R.to_xonly(), X, message);
113+
let c = self.challenge(R.to_xonly(), X, message);
114114
let s_hat = s!(r + c * x).mark::<Public>();
115115

116116
EncryptedSignature {
@@ -188,12 +188,12 @@ pub trait Adaptor {
188188
) -> Option<Scalar>;
189189
}
190190

191-
impl<CH, NG, GT> Adaptor for Schnorr<CH, NG, GT>
191+
impl<CH, NG> Adaptor for Schnorr<CH, NG>
192192
where
193193
CH: Digest<OutputSize = U32> + Clone,
194194
{
195195
fn encryption_key_for(&self, decryption_key: &Scalar) -> Point {
196-
g!(decryption_key * { self.G() }).normalize()
196+
g!(decryption_key * G).normalize()
197197
}
198198

199199
#[must_use]
@@ -216,9 +216,9 @@ where
216216
// !needs_negation => R_hat = R - Y
217217
let R_hat = g!(R + { Y.conditional_negate(!needs_negation) });
218218

219-
let c = self.challenge(&R.to_xonly(), &X.to_xonly(), message);
219+
let c = self.challenge(R.to_xonly(), X.to_xonly(), message);
220220

221-
R_hat == g!(s_hat * { self.G() } - c * X)
221+
R_hat == g!(s_hat * G - c * X)
222222
}
223223

224224
fn decrypt_signature(
@@ -257,7 +257,7 @@ where
257257

258258
let mut y = s!(s - s_hat);
259259
y.conditional_negate(*needs_negation);
260-
let implied_encryption_key = g!(y * { self.G() });
260+
let implied_encryption_key = g!(y * G);
261261

262262
if implied_encryption_key == *encryption_key {
263263
Some(y.expect_nonzero("unreachable - encryption_key is NonZero and y*G equals it"))
@@ -271,48 +271,55 @@ where
271271
mod test {
272272

273273
use super::*;
274-
use crate::nonce::{self, Deterministic};
275-
use secp256kfun::TEST_SOUNDNESS;
274+
use crate::nonce::{Deterministic, GlobalRng, Synthetic};
275+
use rand::rngs::ThreadRng;
276+
use secp256kfun::proptest::prelude::*;
277+
use sha2::Sha256;
276278
#[cfg(target_arch = "wasm32")]
277279
use wasm_bindgen_test::wasm_bindgen_test as test;
278280

279-
fn test_schnorr<NG: NonceGen>(schnorr: Schnorr<sha2::Sha256, NG>) {
280-
for _ in 0..TEST_SOUNDNESS {
281-
let signing_keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
282-
let verification_key = signing_keypair.verification_key();
283-
let decryption_key = Scalar::random(&mut rand::thread_rng());
284-
let encryption_key = schnorr.encryption_key_for(&decryption_key);
285-
let message = Message::<Public>::plain("test", b"give 100 coins to Bob".as_ref());
286-
287-
let encrypted_signature =
288-
schnorr.encrypted_sign(&signing_keypair, &encryption_key, message);
289-
290-
assert!(schnorr.verify_encrypted_signature(
291-
&signing_keypair.verification_key(),
292-
&encryption_key,
293-
message,
294-
&encrypted_signature,
295-
));
281+
proptest! {
282+
#[test]
283+
fn signing_tests_deterministic(secret_key in any::<Scalar>(), decryption_key in any::<Scalar>()) {
284+
let schnorr = Schnorr::<Sha256, Deterministic<Sha256>>::default();
285+
test_it(schnorr, secret_key, decryption_key);
286+
}
296287

297-
let decryption_key = decryption_key.mark::<Public>();
298-
let signature =
299-
schnorr.decrypt_signature(decryption_key.clone(), encrypted_signature.clone());
300-
assert!(schnorr.verify(&verification_key, message, &signature));
301-
let rec_decryption_key = schnorr
302-
.recover_decryption_key(&encryption_key, &encrypted_signature, &signature)
303-
.expect("recovery works");
304-
assert_eq!(rec_decryption_key, decryption_key);
288+
#[test]
289+
fn signing_tests_synthetic(secret_key in any::<Scalar>(), decryption_key in any::<Scalar>()) {
290+
let schnorr = Schnorr::<Sha256, Synthetic<Sha256, GlobalRng<ThreadRng>>>::default();
291+
test_it(schnorr, secret_key, decryption_key);
305292
}
293+
306294
}
307295

308-
#[test]
309-
fn sign_plain_message() {
310-
use rand::rngs::ThreadRng;
311-
use sha2::Sha256;
312-
test_schnorr(Schnorr::new(Deterministic::<Sha256>::default()));
313-
test_schnorr(Schnorr::new(nonce::Synthetic::<
314-
Sha256,
315-
nonce::GlobalRng<ThreadRng>,
316-
>::default()));
296+
fn test_it<NG: NonceGen>(
297+
schnorr: Schnorr<Sha256, NG>,
298+
secret_key: Scalar,
299+
decryption_key: Scalar,
300+
) {
301+
let signing_keypair = schnorr.new_keypair(secret_key);
302+
let verification_key = signing_keypair.verification_key();
303+
let encryption_key = schnorr.encryption_key_for(&decryption_key);
304+
let message = Message::<Public>::plain("test", b"give 100 coins to Bob".as_ref());
305+
306+
let encrypted_signature =
307+
schnorr.encrypted_sign(&signing_keypair, &encryption_key, message);
308+
309+
assert!(schnorr.verify_encrypted_signature(
310+
&signing_keypair.verification_key(),
311+
&encryption_key,
312+
message,
313+
&encrypted_signature,
314+
));
315+
316+
let decryption_key = decryption_key.mark::<Public>();
317+
let signature =
318+
schnorr.decrypt_signature(decryption_key.clone(), encrypted_signature.clone());
319+
assert!(schnorr.verify(&verification_key, message, &signature));
320+
let rec_decryption_key = schnorr
321+
.recover_decryption_key(&encryption_key, &encrypted_signature, &signature)
322+
.expect("recovery works");
323+
assert_eq!(rec_decryption_key, decryption_key);
317324
}
318325
}

schnorr_fun/src/keypair.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use secp256kfun::{marker::*, Point, Scalar, XOnly};
1515
/// ```
1616
///
1717
/// [`Schnorr`]: crate::Schnorr
18-
#[derive(Clone, Debug)]
18+
#[derive(Clone, Debug, PartialEq)]
1919
pub struct KeyPair {
2020
pub(crate) sk: Scalar,
2121
pub(crate) pk: XOnly,
@@ -27,9 +27,9 @@ impl KeyPair {
2727
&self.sk
2828
}
2929

30-
/// Returns a reference to the public key.
31-
pub fn public_key(&self) -> &XOnly {
32-
&self.pk
30+
/// The public key
31+
pub fn public_key(&self) -> XOnly {
32+
self.pk
3333
}
3434

3535
/// Gets a reference to the key-pair as a tuple
@@ -39,8 +39,8 @@ impl KeyPair {
3939
/// # use schnorr_fun::{Schnorr, fun::Scalar};
4040
/// # let keypair = schnorr_fun::test_instance!().new_keypair(Scalar::one());
4141
/// let (secret_key, public_key) = keypair.as_tuple();
42-
pub fn as_tuple(&self) -> (&Scalar, &XOnly) {
43-
(&self.sk, &self.pk)
42+
pub fn as_tuple(&self) -> (&Scalar, XOnly) {
43+
(&self.sk, self.pk)
4444
}
4545

4646
/// Returns the full `Point<EvenY>` for the public key which is used in [`verify`].

schnorr_fun/src/lib.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,30 @@
22
#![no_std]
33
#![allow(non_snake_case)]
44
#![doc = include_str!("../README.md")]
5-
#![deny(warnings, missing_docs)]
5+
#![warn(missing_docs)]
6+
67
#[cfg(all(feature = "alloc", not(feature = "std")))]
78
#[macro_use]
89
extern crate alloc;
10+
#[cfg(all(feature = "alloc", not(feature = "std")))]
11+
pub(crate) use alloc::vec::Vec;
912

1013
#[cfg(feature = "std")]
1114
#[macro_use]
1215
extern crate std;
16+
#[cfg(feature = "std")]
17+
pub(crate) use std::vec::Vec;
1318

1419
#[cfg(feature = "serde")]
1520
extern crate serde_crate as serde;
1621

1722
pub use secp256kfun as fun;
1823
pub use secp256kfun::nonce;
24+
25+
// musig needs vecs
26+
#[cfg(feature = "alloc")]
27+
pub mod musig;
28+
1929
mod signature;
2030
pub use signature::Signature;
2131
pub mod adaptor;

schnorr_fun/src/message.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl<'a, 'b, S: Secrecy> Message<'a, S> {
2929
///
3030
/// [here]: https://github.com/sipa/bips/issues/207#issuecomment-673681901
3131
pub fn plain(app_tag: &'static str, bytes: &'a [u8]) -> Self {
32-
assert!(app_tag.len() <= 64, "tag must not be 64 bytes or less");
32+
assert!(app_tag.len() <= 64, "tag must be 64 bytes or less");
3333
assert!(!app_tag.is_empty(), "tag must not be empty");
3434
Message {
3535
bytes: bytes.mark::<S>(),
@@ -39,7 +39,7 @@ impl<'a, 'b, S: Secrecy> Message<'a, S> {
3939
}
4040

4141
impl<S> HashInto for Message<'_, S> {
42-
fn hash_into(&self, hash: &mut impl Digest) {
42+
fn hash_into(self, hash: &mut impl Digest) {
4343
if let Some(prefix) = self.app_tag {
4444
let mut padded_prefix = [0u8; 64];
4545
padded_prefix[..prefix.len()].copy_from_slice(prefix.as_bytes());

0 commit comments

Comments
 (0)