Skip to content

Commit f8250a2

Browse files
authored
Merge pull request #100 from LLFourn/musig2-ordinary-points
[musig2] use plain points as inputs
2 parents 83f5328 + a1d25d7 commit f8250a2

24 files changed

+1570
-804
lines changed

schnorr_fun/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ bincode = "1.0"
2727
sha2 = "0.9"
2828
secp256kfun = { path = "../secp256kfun", version = "0.7.1", default-features = false, features = ["alloc", "libsecp_compat", "proptest"] }
2929
secp256k1 = { version = "0.22", features = ["std", "global-context"]}
30+
serde_json = "1"
3031

3132

3233
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]

schnorr_fun/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ let message = Message::<Public>::plain("the-times-of-london", b"Chancellor on br
4343
// Sign the message with our keypair
4444
let signature = schnorr.sign(&keypair, message);
4545
// Get the verifier's key
46-
let verification_key = keypair.verification_key();
46+
let verification_key = keypair.public_key().to_point();
4747
// Check it's valid 🍿
4848
assert!(schnorr.verify(&verification_key, message, &signature));
4949
```
@@ -53,7 +53,7 @@ assert!(schnorr.verify(&verification_key, message, &signature));
5353
- BIP-340 compliant signing and verification
5454
- Adaptor signatures
5555
- compatibility with `rust-secp256k1`'s `schnorrsig` module with `libsecp_compat` feature.
56-
- [MuSig2] implementation compatible with [secp256k1-zkp]
56+
- [MuSig2] implementation compatible with [this PR](https://github.com/jonasnick/bips/pull/37) of the spec.
5757
- Feature flags
5858
- `serde`: for serde implementations for signatures
5959
- `libsecp_compat`: for `From` implementations between `rust-secp256k1`'s Schnorr signatures.

schnorr_fun/benches/bench_schnorr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ fn verify_schnorr(c: &mut Criterion) {
4141
{
4242
let message = Message::<Public>::raw(MESSAGE);
4343
let sig = schnorr.sign(&keypair, message);
44-
let verification_key = &keypair.verification_key();
44+
let verification_key = &keypair.public_key().to_point();
4545
group.bench_function("fun::schnorr_verify", |b| {
4646
b.iter(|| schnorr.verify(&verification_key, message, &sig))
4747
});

schnorr_fun/src/adaptor/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
//! let nonce_gen = nonce::Synthetic::<Sha256, nonce::GlobalRng<ThreadRng>>::default();
2121
//! let schnorr = Schnorr::<Sha256, _>::new(nonce_gen);
2222
//! let signing_keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
23-
//! let verification_key = signing_keypair.verification_key();
23+
//! let verification_key = signing_keypair.public_key().to_point();
2424
//! let decryption_key = Scalar::random(&mut rand::thread_rng());
2525
//! let encryption_key = schnorr.encryption_key_for(&decryption_key);
2626
//! let message = Message::<Public>::plain("text-bitcoin", b"send 1 BTC to Bob");
@@ -54,9 +54,9 @@ use crate::{
5454
g,
5555
marker::*,
5656
nonce::NonceGen,
57-
s, Point, Scalar, G,
57+
s, Point, Scalar, XOnlyKeyPair, G,
5858
},
59-
KeyPair, Message, Schnorr, Signature,
59+
Message, Schnorr, Signature,
6060
};
6161
mod encrypted_signature;
6262
pub use encrypted_signature::EncryptedSignature;
@@ -72,7 +72,7 @@ pub trait EncryptedSign {
7272
/// [synopsis]: crate::adaptor#synopsis
7373
fn encrypted_sign(
7474
&self,
75-
signing_keypair: &KeyPair,
75+
signing_keypair: &XOnlyKeyPair,
7676
encryption_key: &Point<impl Normalized, impl Secrecy>,
7777
message: Message<'_, impl Secrecy>,
7878
) -> EncryptedSignature;
@@ -85,7 +85,7 @@ where
8585
{
8686
fn encrypted_sign(
8787
&self,
88-
signing_key: &KeyPair,
88+
signing_key: &XOnlyKeyPair,
8989
encryption_key: &Point<impl Normalized, impl Secrecy>,
9090
message: Message<'_, impl Secrecy>,
9191
) -> EncryptedSignature {
@@ -299,15 +299,15 @@ mod test {
299299
decryption_key: Scalar,
300300
) {
301301
let signing_keypair = schnorr.new_keypair(secret_key);
302-
let verification_key = signing_keypair.verification_key();
302+
let verification_key = signing_keypair.public_key().to_point();
303303
let encryption_key = schnorr.encryption_key_for(&decryption_key);
304304
let message = Message::<Public>::plain("test", b"give 100 coins to Bob".as_ref());
305305

306306
let encrypted_signature =
307307
schnorr.encrypted_sign(&signing_keypair, &encryption_key, message);
308308

309309
assert!(schnorr.verify_encrypted_signature(
310-
&signing_keypair.verification_key(),
310+
&verification_key,
311311
&encryption_key,
312312
message,
313313
&encrypted_signature,

schnorr_fun/src/binonce.rs

Lines changed: 120 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,62 @@
44
//! Your public nonces are derived from scalars which must be kept secret.
55
//! Derived binonces should be unique and and must not be reused for signing under any circumstances
66
//! as this can leak your secret key.
7-
use secp256kfun::{g, marker::*, Point, Scalar, G};
7+
use crate::Message;
8+
use secp256kfun::{derive_nonce, g, marker::*, nonce::NonceGen, Point, Scalar, G};
89

910
/// A nonce (pair of points) that each party must share with the others in the first stage of signing.
11+
///
12+
/// The type argument determines whether the nonces can be `Zero` or not. The [musig
13+
/// spec](https://github.com/jonasnick/bips/pull/21) specifies that the aggregate nonce is allowed
14+
/// to be zero to avoid having to abort the protocol in this case.
1015
#[derive(Clone, Copy, PartialEq, Debug)]
11-
pub struct Nonce(pub [Point; 2]);
16+
pub struct Nonce<Z = NonZero>(pub [Point<Normal, Public, Z>; 2]);
17+
18+
impl Nonce<Zero> {
19+
/// Reads the pair of nonces from 66 bytes (two 33-byte serialized points).
20+
///
21+
/// If either pair of 33 bytes is `[0u8;32]` that point is interpreted as `Zero`.
22+
pub fn from_bytes(bytes: [u8; 66]) -> Option<Self> {
23+
fn deser(bytes: &[u8]) -> Option<Point<Normal, Public, Zero>> {
24+
Point::from_slice(&bytes)
25+
.map(|p| p.mark::<Zero>())
26+
.or_else(|| {
27+
if bytes == [0u8; 33].as_slice() {
28+
Some(Point::zero())
29+
} else {
30+
None
31+
}
32+
})
33+
}
34+
35+
let R1 = deser(&bytes[33..])?;
36+
let R2 = deser(&bytes[..33])?;
37+
38+
Some(Nonce([R1, R2]))
39+
}
1240

13-
impl Nonce {
41+
/// Serializes a public nonce as as 66 bytes (two 33-byte serialized points).
42+
///
43+
/// If either point is `Zero` it will be serialized as `[0u8;32]`.
44+
pub fn to_bytes(self) -> [u8; 66] {
45+
let mut bytes = [0u8; 66];
46+
bytes[..33].copy_from_slice(
47+
&self.0[0]
48+
.mark::<NonZero>()
49+
.map(|p| p.to_bytes())
50+
.unwrap_or([0u8; 33]),
51+
);
52+
bytes[33..].copy_from_slice(
53+
&self.0[1]
54+
.mark::<NonZero>()
55+
.map(|p| p.to_bytes())
56+
.unwrap_or([0u8; 33]),
57+
);
58+
bytes
59+
}
60+
}
61+
62+
impl Nonce<NonZero> {
1463
/// Reads the pair of nonces from 66 bytes (two 33-byte serialized points).
1564
pub fn from_bytes(bytes: [u8; 66]) -> Option<Self> {
1665
let R1 = Point::from_slice(&bytes[..33])?;
@@ -25,7 +74,9 @@ impl Nonce {
2574
bytes[33..].copy_from_slice(self.0[1].to_bytes().as_ref());
2675
bytes
2776
}
77+
}
2878

79+
impl<Z> Nonce<Z> {
2980
/// Negate the two nonces
3081
pub fn conditional_negate(&mut self, needs_negation: bool) {
3182
self.0[0] = self.0[0].conditional_negate(needs_negation);
@@ -35,13 +86,26 @@ impl Nonce {
3586

3687
secp256kfun::impl_fromstr_deserialize! {
3788
name => "public nonce pair",
38-
fn from_bytes(bytes: [u8;66]) -> Option<Nonce> {
39-
Nonce::from_bytes(bytes)
89+
fn from_bytes(bytes: [u8;66]) -> Option<Nonce<Zero>> {
90+
Nonce::<Zero>::from_bytes(bytes)
91+
}
92+
}
93+
94+
secp256kfun::impl_display_serialize! {
95+
fn to_bytes(nonce: &Nonce<Zero>) -> [u8;66] {
96+
nonce.to_bytes()
97+
}
98+
}
99+
100+
secp256kfun::impl_fromstr_deserialize! {
101+
name => "public nonce pair",
102+
fn from_bytes(bytes: [u8;66]) -> Option<Nonce<NonZero>> {
103+
Nonce::<NonZero>::from_bytes(bytes)
40104
}
41105
}
42106

43107
secp256kfun::impl_display_serialize! {
44-
fn to_bytes(nonce: &Nonce) -> [u8;66] {
108+
fn to_bytes(nonce: &Nonce<NonZero>) -> [u8;66] {
45109
nonce.to_bytes()
46110
}
47111
}
@@ -54,7 +118,7 @@ secp256kfun::impl_display_serialize! {
54118
#[derive(Debug, Clone, PartialEq)]
55119
pub struct NonceKeyPair {
56120
/// The public nonce
57-
pub public: Nonce,
121+
pub public: Nonce<NonZero>,
58122
/// The secret nonce
59123
pub secret: [Scalar; 2],
60124
}
@@ -97,9 +161,57 @@ impl NonceKeyPair {
97161
}
98162

99163
/// Get the public portion of the nonce key pair (share this!)
100-
pub fn public(&self) -> Nonce {
164+
pub fn public(&self) -> Nonce<NonZero> {
101165
self.public
102166
}
167+
168+
/// Generate a new `NonceKeyPair` from application data.
169+
///
170+
/// Each nonce generated must only be passed to [`MuSig::sign`] once.
171+
///
172+
/// You must always pass in a:
173+
///
174+
/// - `nonce_gen`: [`NonceGen`] containing the underlying algorithm for generating the nonce
175+
/// - `secret`: The secret scalar whose secrecy depends on the uniquness of the nonces generated.
176+
/// - `session_id`: Some application defined identifier for the signing session that the resulting nonce will be used in.
177+
///
178+
/// How important the `session_id` is depends on whether you add a `message` and whether you are using randomness in your `nonce_gen`.
179+
/// If you are using a deterministic `nonce_gen` it is crucial that this is set to a unique value for each signing session.
180+
/// If your application doesn't naturally provide you with a unique value store a counter.
181+
///
182+
/// Optionally you may pass in `public_key` and `message` which should be passed in when available.
183+
///
184+
/// [`MuSig::sign`]: crate::musig::MuSig::sign
185+
pub fn generate(
186+
nonce_gen: &impl NonceGen,
187+
secret: &Scalar,
188+
session_id: &[u8],
189+
public_key: Option<Point<impl Normalized>>,
190+
message: Option<Message<'_>>,
191+
) -> Self {
192+
let message = message.unwrap_or(Message::raw(b""));
193+
let msg_len = (message.len() as u64).to_be_bytes();
194+
let sid_len = (session_id.len() as u64).to_be_bytes();
195+
let pk_bytes = public_key.map(|p| p.to_bytes()).unwrap_or([0u8; 33]);
196+
let r1 = derive_nonce!(
197+
nonce_gen => nonce_gen,
198+
secret => secret,
199+
public => [ b"r1", pk_bytes, msg_len, message, sid_len, session_id]
200+
);
201+
let r2 = derive_nonce!(
202+
nonce_gen => nonce_gen,
203+
secret => secret,
204+
public => [ b"r2", pk_bytes, msg_len, message, sid_len, session_id]
205+
);
206+
207+
let R1 = g!(r1 * G).normalize();
208+
let R2 = g!(r2 * G).normalize();
209+
210+
NonceKeyPair {
211+
public: Nonce([R1, R2]),
212+
secret: [r1, r2],
213+
}
214+
}
103215
}
104216

105217
secp256kfun::impl_fromstr_deserialize! {

schnorr_fun/src/keypair.rs

Lines changed: 0 additions & 72 deletions
This file was deleted.

schnorr_fun/src/lib.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,14 @@ pub use secp256kfun as fun;
2323
pub use secp256kfun::nonce;
2424

2525
/// binonces for Musig and FROST
26-
mod binonce;
26+
pub mod binonce;
2727
// musig needs vecs
2828
#[cfg(feature = "alloc")]
2929
pub mod musig;
3030

3131
mod signature;
3232
pub use signature::Signature;
3333
pub mod adaptor;
34-
mod keypair;
35-
pub use keypair::KeyPair;
3634
mod schnorr;
3735
pub use schnorr::*;
3836
mod message;

schnorr_fun/src/message.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ impl<'a, 'b, S: Secrecy> Message<'a, S> {
3636
app_tag: Some(app_tag),
3737
}
3838
}
39+
40+
/// Length of the message as it is hashed
41+
pub fn len(&self) -> usize {
42+
match self.app_tag {
43+
Some(_) => 64 + self.bytes.as_inner().len(),
44+
None => self.bytes.as_inner().len(),
45+
}
46+
}
3947
}
4048

4149
impl<S> HashInto for Message<'_, S> {

0 commit comments

Comments
 (0)