Skip to content

Commit

Permalink
Merge pull request #100 from LLFourn/musig2-ordinary-points
Browse files Browse the repository at this point in the history
[musig2] use plain points as inputs
  • Loading branch information
LLFourn authored Aug 17, 2022
2 parents 83f5328 + a1d25d7 commit f8250a2
Show file tree
Hide file tree
Showing 24 changed files with 1,570 additions and 804 deletions.
1 change: 1 addition & 0 deletions schnorr_fun/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ bincode = "1.0"
sha2 = "0.9"
secp256kfun = { path = "../secp256kfun", version = "0.7.1", default-features = false, features = ["alloc", "libsecp_compat", "proptest"] }
secp256k1 = { version = "0.22", features = ["std", "global-context"]}
serde_json = "1"


[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions schnorr_fun/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ let message = Message::<Public>::plain("the-times-of-london", b"Chancellor on br
// Sign the message with our keypair
let signature = schnorr.sign(&keypair, message);
// Get the verifier's key
let verification_key = keypair.verification_key();
let verification_key = keypair.public_key().to_point();
// Check it's valid 🍿
assert!(schnorr.verify(&verification_key, message, &signature));
```
Expand All @@ -53,7 +53,7 @@ assert!(schnorr.verify(&verification_key, message, &signature));
- BIP-340 compliant signing and verification
- Adaptor signatures
- compatibility with `rust-secp256k1`'s `schnorrsig` module with `libsecp_compat` feature.
- [MuSig2] implementation compatible with [secp256k1-zkp]
- [MuSig2] implementation compatible with [this PR](https://github.com/jonasnick/bips/pull/37) of the spec.
- Feature flags
- `serde`: for serde implementations for signatures
- `libsecp_compat`: for `From` implementations between `rust-secp256k1`'s Schnorr signatures.
Expand Down
2 changes: 1 addition & 1 deletion schnorr_fun/benches/bench_schnorr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fn verify_schnorr(c: &mut Criterion) {
{
let message = Message::<Public>::raw(MESSAGE);
let sig = schnorr.sign(&keypair, message);
let verification_key = &keypair.verification_key();
let verification_key = &keypair.public_key().to_point();
group.bench_function("fun::schnorr_verify", |b| {
b.iter(|| schnorr.verify(&verification_key, message, &sig))
});
Expand Down
14 changes: 7 additions & 7 deletions schnorr_fun/src/adaptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
//! let nonce_gen = nonce::Synthetic::<Sha256, nonce::GlobalRng<ThreadRng>>::default();
//! let schnorr = Schnorr::<Sha256, _>::new(nonce_gen);
//! let signing_keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
//! let verification_key = signing_keypair.verification_key();
//! let verification_key = signing_keypair.public_key().to_point();
//! let decryption_key = Scalar::random(&mut rand::thread_rng());
//! let encryption_key = schnorr.encryption_key_for(&decryption_key);
//! let message = Message::<Public>::plain("text-bitcoin", b"send 1 BTC to Bob");
Expand Down Expand Up @@ -54,9 +54,9 @@ use crate::{
g,
marker::*,
nonce::NonceGen,
s, Point, Scalar, G,
s, Point, Scalar, XOnlyKeyPair, G,
},
KeyPair, Message, Schnorr, Signature,
Message, Schnorr, Signature,
};
mod encrypted_signature;
pub use encrypted_signature::EncryptedSignature;
Expand All @@ -72,7 +72,7 @@ pub trait EncryptedSign {
/// [synopsis]: crate::adaptor#synopsis
fn encrypted_sign(
&self,
signing_keypair: &KeyPair,
signing_keypair: &XOnlyKeyPair,
encryption_key: &Point<impl Normalized, impl Secrecy>,
message: Message<'_, impl Secrecy>,
) -> EncryptedSignature;
Expand All @@ -85,7 +85,7 @@ where
{
fn encrypted_sign(
&self,
signing_key: &KeyPair,
signing_key: &XOnlyKeyPair,
encryption_key: &Point<impl Normalized, impl Secrecy>,
message: Message<'_, impl Secrecy>,
) -> EncryptedSignature {
Expand Down Expand Up @@ -299,15 +299,15 @@ mod test {
decryption_key: Scalar,
) {
let signing_keypair = schnorr.new_keypair(secret_key);
let verification_key = signing_keypair.verification_key();
let verification_key = signing_keypair.public_key().to_point();
let encryption_key = schnorr.encryption_key_for(&decryption_key);
let message = Message::<Public>::plain("test", b"give 100 coins to Bob".as_ref());

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

assert!(schnorr.verify_encrypted_signature(
&signing_keypair.verification_key(),
&verification_key,
&encryption_key,
message,
&encrypted_signature,
Expand Down
128 changes: 120 additions & 8 deletions schnorr_fun/src/binonce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,62 @@
//! Your public nonces are derived from scalars which must be kept secret.
//! Derived binonces should be unique and and must not be reused for signing under any circumstances
//! as this can leak your secret key.
use secp256kfun::{g, marker::*, Point, Scalar, G};
use crate::Message;
use secp256kfun::{derive_nonce, g, marker::*, nonce::NonceGen, Point, Scalar, G};

/// A nonce (pair of points) that each party must share with the others in the first stage of signing.
///
/// The type argument determines whether the nonces can be `Zero` or not. The [musig
/// spec](https://github.com/jonasnick/bips/pull/21) specifies that the aggregate nonce is allowed
/// to be zero to avoid having to abort the protocol in this case.
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Nonce(pub [Point; 2]);
pub struct Nonce<Z = NonZero>(pub [Point<Normal, Public, Z>; 2]);

impl Nonce<Zero> {
/// Reads the pair of nonces from 66 bytes (two 33-byte serialized points).
///
/// If either pair of 33 bytes is `[0u8;32]` that point is interpreted as `Zero`.
pub fn from_bytes(bytes: [u8; 66]) -> Option<Self> {
fn deser(bytes: &[u8]) -> Option<Point<Normal, Public, Zero>> {
Point::from_slice(&bytes)
.map(|p| p.mark::<Zero>())
.or_else(|| {
if bytes == [0u8; 33].as_slice() {
Some(Point::zero())
} else {
None
}
})
}

let R1 = deser(&bytes[33..])?;
let R2 = deser(&bytes[..33])?;

Some(Nonce([R1, R2]))
}

impl Nonce {
/// Serializes a public nonce as as 66 bytes (two 33-byte serialized points).
///
/// If either point is `Zero` it will be serialized as `[0u8;32]`.
pub fn to_bytes(self) -> [u8; 66] {
let mut bytes = [0u8; 66];
bytes[..33].copy_from_slice(
&self.0[0]
.mark::<NonZero>()
.map(|p| p.to_bytes())
.unwrap_or([0u8; 33]),
);
bytes[33..].copy_from_slice(
&self.0[1]
.mark::<NonZero>()
.map(|p| p.to_bytes())
.unwrap_or([0u8; 33]),
);
bytes
}
}

impl Nonce<NonZero> {
/// Reads the pair of nonces from 66 bytes (two 33-byte serialized points).
pub fn from_bytes(bytes: [u8; 66]) -> Option<Self> {
let R1 = Point::from_slice(&bytes[..33])?;
Expand All @@ -25,7 +74,9 @@ impl Nonce {
bytes[33..].copy_from_slice(self.0[1].to_bytes().as_ref());
bytes
}
}

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

secp256kfun::impl_fromstr_deserialize! {
name => "public nonce pair",
fn from_bytes(bytes: [u8;66]) -> Option<Nonce> {
Nonce::from_bytes(bytes)
fn from_bytes(bytes: [u8;66]) -> Option<Nonce<Zero>> {
Nonce::<Zero>::from_bytes(bytes)
}
}

secp256kfun::impl_display_serialize! {
fn to_bytes(nonce: &Nonce<Zero>) -> [u8;66] {
nonce.to_bytes()
}
}

secp256kfun::impl_fromstr_deserialize! {
name => "public nonce pair",
fn from_bytes(bytes: [u8;66]) -> Option<Nonce<NonZero>> {
Nonce::<NonZero>::from_bytes(bytes)
}
}

secp256kfun::impl_display_serialize! {
fn to_bytes(nonce: &Nonce) -> [u8;66] {
fn to_bytes(nonce: &Nonce<NonZero>) -> [u8;66] {
nonce.to_bytes()
}
}
Expand All @@ -54,7 +118,7 @@ secp256kfun::impl_display_serialize! {
#[derive(Debug, Clone, PartialEq)]
pub struct NonceKeyPair {
/// The public nonce
pub public: Nonce,
pub public: Nonce<NonZero>,
/// The secret nonce
pub secret: [Scalar; 2],
}
Expand Down Expand Up @@ -97,9 +161,57 @@ impl NonceKeyPair {
}

/// Get the public portion of the nonce key pair (share this!)
pub fn public(&self) -> Nonce {
pub fn public(&self) -> Nonce<NonZero> {
self.public
}

/// Generate a new `NonceKeyPair` from application data.
///
/// Each nonce generated must only be passed to [`MuSig::sign`] once.
///
/// You must always pass in a:
///
/// - `nonce_gen`: [`NonceGen`] containing the underlying algorithm for generating the nonce
/// - `secret`: The secret scalar whose secrecy depends on the uniquness of the nonces generated.
/// - `session_id`: Some application defined identifier for the signing session that the resulting nonce will be used in.
///
/// How important the `session_id` is depends on whether you add a `message` and whether you are using randomness in your `nonce_gen`.
/// If you are using a deterministic `nonce_gen` it is crucial that this is set to a unique value for each signing session.
/// If your application doesn't naturally provide you with a unique value store a counter.
///
/// Optionally you may pass in `public_key` and `message` which should be passed in when available.
///
/// [`MuSig::sign`]: crate::musig::MuSig::sign
pub fn generate(
nonce_gen: &impl NonceGen,
secret: &Scalar,
session_id: &[u8],
public_key: Option<Point<impl Normalized>>,
message: Option<Message<'_>>,
) -> Self {
let message = message.unwrap_or(Message::raw(b""));
let msg_len = (message.len() as u64).to_be_bytes();
let sid_len = (session_id.len() as u64).to_be_bytes();
let pk_bytes = public_key.map(|p| p.to_bytes()).unwrap_or([0u8; 33]);
let r1 = derive_nonce!(
nonce_gen => nonce_gen,
secret => secret,
public => [ b"r1", pk_bytes, msg_len, message, sid_len, session_id]
);
let r2 = derive_nonce!(
nonce_gen => nonce_gen,
secret => secret,
public => [ b"r2", pk_bytes, msg_len, message, sid_len, session_id]
);

let R1 = g!(r1 * G).normalize();
let R2 = g!(r2 * G).normalize();

NonceKeyPair {
public: Nonce([R1, R2]),
secret: [r1, r2],
}
}
}

secp256kfun::impl_fromstr_deserialize! {
Expand Down
72 changes: 0 additions & 72 deletions schnorr_fun/src/keypair.rs

This file was deleted.

4 changes: 1 addition & 3 deletions schnorr_fun/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,14 @@ pub use secp256kfun as fun;
pub use secp256kfun::nonce;

/// binonces for Musig and FROST
mod binonce;
pub mod binonce;
// musig needs vecs
#[cfg(feature = "alloc")]
pub mod musig;

mod signature;
pub use signature::Signature;
pub mod adaptor;
mod keypair;
pub use keypair::KeyPair;
mod schnorr;
pub use schnorr::*;
mod message;
Expand Down
8 changes: 8 additions & 0 deletions schnorr_fun/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ impl<'a, 'b, S: Secrecy> Message<'a, S> {
app_tag: Some(app_tag),
}
}

/// Length of the message as it is hashed
pub fn len(&self) -> usize {
match self.app_tag {
Some(_) => 64 + self.bytes.as_inner().len(),
None => self.bytes.as_inner().len(),
}
}
}

impl<S> HashInto for Message<'_, S> {
Expand Down
Loading

0 comments on commit f8250a2

Please sign in to comment.