From 9adc7698db0e099743625d975a2637962401ab2b Mon Sep 17 00:00:00 2001 From: LLFourn Date: Mon, 13 Jun 2022 14:59:16 +0800 Subject: [PATCH 1/8] [musig] keypairs never need negation Since we do the negation check when we create them --- schnorr_fun/src/musig.rs | 52 +++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/schnorr_fun/src/musig.rs b/schnorr_fun/src/musig.rs index 30440d4a..69086d3d 100644 --- a/schnorr_fun/src/musig.rs +++ b/schnorr_fun/src/musig.rs @@ -34,9 +34,9 @@ //! // start the signing session //! let session = musig.start_sign_session(&keylist, nonces, message).unwrap(); //! // sign with our single local keypair -//! let my_sig = musig.sign(&keylist, 0, my_keypair.secret_key(), my_nonce, &session); -//! # let p2_sig = musig.sign(&keylist, 1, kp2.secret_key(), p2_nonce, &session); -//! # let p3_sig = musig.sign(&keylist, 2, kp3.secret_key(), p3_nonce, &session); +//! let my_sig = musig.sign(&keylist, 0, &my_keypair, my_nonce, &session); +//! # let p2_sig = musig.sign(&keylist, 1, &kp2, p2_nonce, &session); +//! # let p3_sig = musig.sign(&keylist, 2, &kp3, p3_nonce, &session); //! // receive p2_sig and p3_sig from somewhere and check they're valid //! assert!(musig.verify_partial_signature(&keylist, &session, 1, p2_sig)); //! assert!(musig.verify_partial_signature(&keylist, &session, 2, p3_sig)); @@ -60,7 +60,7 @@ //! [the excellent paper]: https://eprint.iacr.org/2020/1261.pdf //! [secp256k1-zkp]: https://github.com/ElementsProject/secp256k1-zkp/pull/131 pub use crate::binonce::{Nonce, NonceKeyPair}; -use crate::{adaptor::EncryptedSignature, Message, Schnorr, Signature, Vec}; +use crate::{adaptor::EncryptedSignature, KeyPair, Message, Schnorr, Signature, Vec}; use secp256kfun::{ derive_nonce, digest::{generic_array::typenum::U32, Digest}, @@ -479,21 +479,23 @@ impl + Clone, NG> MuSig> { &self, keylist: &KeyList, my_index: u32, - secret: &Scalar, + keypair: &KeyPair, local_secret_nonce: NonceKeyPair, session: &SignSession, ) -> Scalar { let c = session.c; let b = session.b; - - let x = secret; - let X = g!(secret * G).normalize(); + let x = keypair.secret_key(); + assert_eq!( + keypair.public_key(), + keylist.keys().nth(my_index as usize).unwrap(), + "key at index {} didn't match", + my_index + ); let mut a = keylist.coefs[my_index as usize]; - let (_, my_key_needs_negation) = X.into_point_with_even_y(); - let (_, agg_key_needs_negation) = keylist.agg_key.into_point_with_even_y(); + let agg_key_needs_negation = !keylist.agg_key.is_y_even(); - let total_negation = - my_key_needs_negation ^ agg_key_needs_negation ^ keylist.accum_needs_negation; + let total_negation = agg_key_needs_negation ^ keylist.accum_needs_negation; a.conditional_negate(total_negation); let [mut r1, mut r2] = local_secret_nonce.secret.clone(); @@ -683,7 +685,7 @@ mod test { ) .unwrap(); - let p1_sig = musig.sign(&keylist1, 0, &keypair1.sk, p1_nonce, &p1_session); + let p1_sig = musig.sign(&keylist1, 0, &keypair1, p1_nonce, &p1_session); assert!(musig.verify_partial_signature(&keylist1, &p1_session, 0, p1_sig)); dbg!(&p1_session, &p2_session); @@ -693,9 +695,9 @@ mod test { assert!(musig.verify_partial_signature(&keylist1, &p2_session, 0, p1_sig)); assert!(musig.verify_partial_signature(&keylist1, &p3_session, 0, p1_sig)); - let p2_sig = musig.sign(&keylist1, 1, &keypair2.sk, p2_nonce, &p2_session); + let p2_sig = musig.sign(&keylist1, 1, &keypair2, p2_nonce, &p2_session); assert!(musig.verify_partial_signature(&keylist1, &p1_session, 1, p2_sig)); - let p3_sig = musig.sign(&keylist1, 2, &keypair3.sk, p3_nonce, &p3_session); + let p3_sig = musig.sign(&keylist1, 2, &keypair3, p3_nonce, &p3_session); assert!(musig.verify_partial_signature(&keylist1, &p1_session, 2, p3_sig)); let partial_sigs = [p1_sig, p2_sig, p3_sig]; @@ -784,9 +786,9 @@ mod test { &encryption_key ) .unwrap(); - let p1_sig = musig.sign(&keylist, 0, &keypair1.sk, p1_nonce, &mut p1_session); - let p2_sig = musig.sign(&keylist, 1, &keypair2.sk, p2_nonce, &mut p2_session); - let p3_sig = musig.sign(&keylist, 2, &keypair3.sk, p3_nonce, &mut p3_session); + let p1_sig = musig.sign(&keylist, 0, &keypair1, p1_nonce, &mut p1_session); + let p2_sig = musig.sign(&keylist, 1, &keypair2, p2_nonce, &mut p2_session); + let p3_sig = musig.sign(&keylist, 2, &keypair3, p3_nonce, &mut p3_session); assert!(musig.verify_partial_signature(&keylist2, &p2_session, 0, p1_sig)); assert!(musig.verify_partial_signature(&keylist, &p1_session, 0, p1_sig)); @@ -975,7 +977,7 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 0, &keypair.sk, sec_nonce.clone(), &sign_session); + let sig = musig.sign(&keylist, 0, &keypair, sec_nonce.clone(), &sign_session); assert_eq!(sig, expected[0]); { @@ -991,7 +993,7 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 1, &keypair.sk, sec_nonce.clone(), &sign_session); + let sig = musig.sign(&keylist, 1, &keypair, sec_nonce.clone(), &sign_session); assert_eq!(sig, expected[1]); } @@ -1008,7 +1010,7 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 2, &keypair.sk, sec_nonce.clone(), &sign_session); + let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); assert_eq!(sig, expected[2]); } } @@ -1153,7 +1155,7 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 2, &keypair.sk, sec_nonce.clone(), &sign_session); + let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); assert_eq!(sig, expected[0]); } @@ -1171,7 +1173,7 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 2, &keypair.sk, sec_nonce.clone(), &sign_session); + let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); assert_eq!(sig, expected[0]); } @@ -1190,7 +1192,7 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 2, &keypair.sk, sec_nonce.clone(), &sign_session); + let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); assert_eq!(sig, expected[2]); } @@ -1212,7 +1214,7 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 2, &keypair.sk, sec_nonce.clone(), &sign_session); + let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); assert_eq!(sig, expected[3]); } } From 7858a00d256bbaacb19a569e11e9f5b5fb357bbf Mon Sep 17 00:00:00 2001 From: LLFourn Date: Tue, 14 Jun 2022 13:50:05 +0800 Subject: [PATCH 2/8] Two-phase approach to key aggregation so that before finalizing into an "AggKey" you can apply ordinary b32 type tweaks. After finalization you apply only "XOnly" taproot style tweaks. --- schnorr_fun/src/musig.rs | 446 ++++++++++++++++++++++----------------- 1 file changed, 251 insertions(+), 195 deletions(-) diff --git a/schnorr_fun/src/musig.rs b/schnorr_fun/src/musig.rs index 69086d3d..ce171eb9 100644 --- a/schnorr_fun/src/musig.rs +++ b/schnorr_fun/src/musig.rs @@ -124,20 +124,14 @@ pub struct KeyList { agg_key: Point, /// The tweak on the aggregate key tweak: Scalar, - /// Whether this aggregate key needs negation. - accum_needs_negation: bool, } impl KeyList { - /// The `XOnly` aggregated key for the keylist. - pub fn agg_public_key(&self) -> XOnly { - self.agg_key.to_xonly() - } - /// The aggregated key for the keylist as a `Point`. - pub fn agg_verification_key(&self) -> Point { - let (k, _) = self.agg_key.into_point_with_even_y(); - k + /// The pre-finalized aggregate key + pub fn agg_public_key(&self) -> Point { + self.agg_key } + /// An iterator over the **public keys** of each party in the keylist. pub fn keys(&self) -> impl Iterator + '_ { self.parties.iter().map(|xonly| *xonly) @@ -154,42 +148,74 @@ impl KeyList { /// Returns a new MuSig KeyList with the same parties but a different aggregated public key. /// In the erroneous case that the tweak is exactly equal to the negation of the aggregate /// secret key it returns `None`. - pub fn tweak( - self, - tweak: Scalar, - is_xonly: bool, - ) -> Option { - // An accum_needs_negation is calculated for the keylist to track negation of secrets - // Keys may be negated if the tweak is x-only, which then may update accum_needs_negation - // The overall negation for signing is the combination - // my_key_needs_negation ^ agg_key_needs_negation ^ keylist.accum_needs_negation; - // See [`MuSig::sign`] - - // Do not negate unless tweak is_xonly and current agg key needs negation - // If the tweak is not x-only then do not negate - let (_, prev_agg_needs_negation) = self.agg_key.mark::()?.into_point_with_even_y(); - let g_negation = is_xonly & prev_agg_needs_negation; - let maybe_negated_agg_key = self.agg_key.conditional_negate(g_negation); - - // new aggregate key with tweak - let agg_key = g!(maybe_negated_agg_key + tweak * G) - .normalize() - .mark::()?; - - // store accumulated tweak - let mut existing_tweak = self.tweak.clone(); - existing_tweak.conditional_negate(g_negation); - let tweak = s!(existing_tweak + tweak).mark::(); - - // update accum_needs_negation - let accum_needs_negation = self.accum_needs_negation ^ g_negation; + pub fn tweak(self, tweak: Scalar) -> Option { + let agg_key = g!(self.agg_key + tweak * G).normalize().mark::()?; + let tweak = s!(self.tweak + tweak).mark::(); Some(KeyList { parties: self.parties.clone(), coefs: self.coefs.clone(), agg_key, tweak, - accum_needs_negation, + }) + } + + pub fn into_agg_key(self) -> AggKey { + let (agg_key, needs_negation) = self.agg_key.into_point_with_even_y(); + let mut tweak = self.tweak; + tweak.conditional_negate(needs_negation); + AggKey { + parties: self.parties, + coefs: self.coefs, + needs_negation, + tweak, + agg_key, + } + } +} + +/// A [`KeyList`] that has been converted into a final aggregate `XOnly` key. +#[derive(Debug, Clone)] +pub struct AggKey { + /// The parties involved in the key aggregation. + parties: Vec, + /// The coefficients of each key + coefs: Vec>, + /// Whether the secret keys needs to be negated when signing + needs_negation: bool, + /// The tweaks that have been applied + tweak: Scalar, + /// + agg_key: Point, +} + +impl AggKey { + /// The pre-finalized aggregate key + pub fn agg_public_key(&self) -> Point { + self.agg_key + } + + /// An iterator over the **public keys** of each party in the keylist. + pub fn keys(&self) -> impl Iterator + '_ { + self.parties.iter().map(|xonly| *xonly) + } + + /// Applies an "XOnly" tweak to the aggregate key + pub fn tweak(self, tweak: Scalar) -> Option { + let (new_agg_key, needs_negation) = g!(self.agg_key + tweak * G) + .normalize() + .mark::()? + .into_point_with_even_y(); + let mut new_tweak = s!(self.tweak + tweak).mark::(); + new_tweak.conditional_negate(needs_negation); + let needs_negation = self.needs_negation ^ needs_negation; + + Some(Self { + parties: self.parties, + coefs: self.coefs, + needs_negation, + tweak: new_tweak, + agg_key: new_agg_key, }) } } @@ -250,7 +276,6 @@ impl + Clone, S> MuSig { coefs, agg_key: agg_key.mark::(), tweak: Scalar::zero().mark::(), - accum_needs_negation: false, } } } @@ -277,7 +302,7 @@ impl + Clone, NG: NonceGen> MuSig> /// [`Deterministic`]: secp256kfun::nonce::Deterministic /// [`start_sign_session`]: Self::start_sign_session /// [`NonceKeyPair`]: schnorr_fun::binonce::NonceKeyPair - pub fn gen_nonces(&self, secret: &Scalar, keylist: &KeyList, sid: &[u8]) -> NonceKeyPair { + pub fn gen_nonces(&self, secret: &Scalar, keylist: &AggKey, sid: &[u8]) -> NonceKeyPair { let r1 = derive_nonce!( nonce_gen => self.schnorr.nonce_gen(), secret => secret, @@ -362,15 +387,15 @@ impl + Clone, NG> MuSig> { /// /// # Panics /// - /// Panics if number of nonces does not align with the parties in `keylist`. + /// Panics if number of nonces does not align with the parties in `agg_key`. pub fn start_sign_session( &self, - keylist: &KeyList, + agg_key: &AggKey, nonces: Vec, message: Message<'_, Public>, ) -> Option { let (b, c, public_nonces, R, nonce_needs_negation) = - self._start_sign_session(keylist, nonces, message, &Point::zero())?; + self._start_sign_session(agg_key, nonces, message, &Point::zero())?; Some(SignSession { b, c, @@ -398,18 +423,18 @@ impl + Clone, NG> MuSig> { /// # Panics /// /// Panics if number of local or remote nonces passed in does not align with the parties in - /// `keylist`. + /// `agg_key`. /// /// [`adaptor`]: crate::adaptor pub fn start_encrypted_sign_session( &self, - keylist: &KeyList, + agg_key: &AggKey, nonces: Vec, message: Message<'_, Public>, encryption_key: &Point, ) -> Option> { let (b, c, public_nonces, R, nonce_needs_negation) = - self._start_sign_session(keylist, nonces, message, encryption_key)?; + self._start_sign_session(agg_key, nonces, message, encryption_key)?; Some(SignSession { b, c, @@ -424,7 +449,7 @@ impl + Clone, NG> MuSig> { fn _start_sign_session( &self, - keylist: &KeyList, + agg_key: &AggKey, nonces: Vec, message: Message<'_, Public>, encryption_key: &Point, @@ -453,7 +478,11 @@ impl + Clone, NG> MuSig> { let b = { let H = self.nonce_coeff_hash.clone(); - Scalar::from_hash(H.add(agg_Rs).add(keylist.agg_public_key()).add(message)) + Scalar::from_hash( + H.add(agg_Rs) + .add(agg_key.agg_public_key().to_xonly()) + .add(message), + ) } .mark::<(Public, Zero)>(); @@ -469,7 +498,7 @@ impl + Clone, NG> MuSig> { let c = self .schnorr - .challenge(R.to_xonly(), keylist.agg_public_key(), message); + .challenge(R.to_xonly(), agg_key.agg_public_key().to_xonly(), message); Some((b, c, Rs, R, r_needs_negation)) } @@ -477,7 +506,7 @@ impl + Clone, NG> MuSig> { /// Generates a partial signature (or partial encrypted signature depending on `T`) for the local_secret_nonce. pub fn sign( &self, - keylist: &KeyList, + agg_key: &AggKey, my_index: u32, keypair: &KeyPair, local_secret_nonce: NonceKeyPair, @@ -488,16 +517,13 @@ impl + Clone, NG> MuSig> { let x = keypair.secret_key(); assert_eq!( keypair.public_key(), - keylist.keys().nth(my_index as usize).unwrap(), + agg_key.keys().nth(my_index as usize).unwrap(), "key at index {} didn't match", my_index ); - let mut a = keylist.coefs[my_index as usize]; - let agg_key_needs_negation = !keylist.agg_key.is_y_even(); - - let total_negation = agg_key_needs_negation ^ keylist.accum_needs_negation; + let mut a = agg_key.coefs[my_index as usize]; - a.conditional_negate(total_negation); + a.conditional_negate(agg_key.needs_negation); let [mut r1, mut r2] = local_secret_nonce.secret.clone(); r1.conditional_negate(session.nonce_needs_negation); r2.conditional_negate(session.nonce_needs_negation); @@ -514,7 +540,7 @@ impl + Clone, NG> MuSig> { /// Panics when `index` is equal to or greater than the number of parties in the keylist. pub fn verify_partial_signature( &self, - keylist: &KeyList, + agg_key: &AggKey, session: &SignSession, index: usize, partial_sig: Scalar, @@ -522,16 +548,15 @@ impl + Clone, NG> MuSig> { let c = session.c; let b = session.b; let s = &partial_sig; - let a = keylist.coefs[index].clone(); + let a = agg_key.coefs[index].clone(); - let (_, agg_needs_negation) = keylist.agg_key.into_point_with_even_y(); - - let X = keylist + let X = agg_key .keys() .nth(index) .unwrap() .to_point() - .conditional_negate(keylist.accum_needs_negation ^ agg_needs_negation); + .conditional_negate(agg_key.needs_negation); + let [R1, R2] = &session.public_nonces[index].0; g!((c * a) * X + R1 + b * R2 - s * G).is_zero() } @@ -545,11 +570,11 @@ impl + Clone, NG> MuSig> { /// [`verify_partial_signature`]: Self::verify_partial_signature pub fn combine_partial_signatures( &self, - keylist: &KeyList, + agg_key: &AggKey, session: &SignSession, partial_sigs: impl IntoIterator>, ) -> Signature { - let (R, s) = self._combine_partial_signatures(keylist, &session, partial_sigs); + let (R, s) = self._combine_partial_signatures(agg_key, &session, partial_sigs); Signature { R: R.to_xonly(), s } } @@ -562,12 +587,12 @@ impl + Clone, NG> MuSig> { /// [`verify_partial_signature`]: Self::verify_partial_signature pub fn combine_partial_encrypted_signatures( &self, - keylist: &KeyList, + agg_key: &AggKey, session: &SignSession, partial_encrypted_sigs: impl IntoIterator>, ) -> EncryptedSignature { let (R, s_hat) = - self._combine_partial_signatures(keylist, &session, partial_encrypted_sigs); + self._combine_partial_signatures(agg_key, &session, partial_encrypted_sigs); EncryptedSignature { R, s_hat, @@ -577,22 +602,17 @@ impl + Clone, NG> MuSig> { fn _combine_partial_signatures( &self, - keylist: &KeyList, + agg_key: &AggKey, session: &SignSession, partial_sigs: impl IntoIterator>, ) -> (Point, Scalar) { - let (_, tweaked_key_needs_negation) = keylist.agg_key.into_point_with_even_y(); - let mut negated_tweak = keylist.tweak.clone(); - - let total_negation = tweaked_key_needs_negation; - negated_tweak.conditional_negate(total_negation); - - let ck = s!(session.c * negated_tweak); let sum_s = partial_sigs .into_iter() .reduce(|acc, s| s!(acc + s).mark::()) .unwrap_or(Scalar::zero().mark::()); - let s = s!(sum_s + ck).mark::(); + + let s = s!(sum_s + agg_key.tweak * session.c).mark::(); + (session.R, s) } } @@ -613,6 +633,8 @@ mod test { fn test_end_to_end(sk1 in any::(), sk2 in any::(), sk3 in any::(), + pre_tweak1 in option::of(any::>()), + pre_tweak2 in option::of(any::>()), tweak1 in option::of(any::>()), tweak2 in option::of(any::>()), ) { @@ -644,20 +666,34 @@ mod test { keypair3.public_key(), ]); + for tweak in [pre_tweak1, pre_tweak2] { + if let Some(tweak) = tweak { + keylist1 = keylist1.tweak(tweak).unwrap(); + keylist2 = keylist2.tweak(tweak).unwrap(); + keylist3 = keylist3.tweak(tweak).unwrap(); + } + } + + + let mut agg_key1 = keylist1.into_agg_key(); + let mut agg_key2 = keylist2.into_agg_key(); + let mut agg_key3 = keylist3.into_agg_key(); + for tweak in [tweak1, tweak2] { if let Some(tweak) = tweak { - keylist1 = keylist1.tweak(tweak, true).unwrap(); - keylist2 = keylist2.tweak(tweak, true).unwrap(); - keylist3 = keylist3.tweak(tweak, true).unwrap(); + agg_key1 = agg_key1.tweak(tweak).unwrap(); + agg_key2 = agg_key2.tweak(tweak).unwrap(); + agg_key3 = agg_key3.tweak(tweak).unwrap(); } } - assert_eq!(keylist1.agg_public_key(), keylist2.agg_public_key()); - assert_eq!(keylist1.agg_public_key(), keylist3.agg_public_key()); + assert_eq!(agg_key1.agg_public_key(), agg_key2.agg_public_key()); + assert_eq!(agg_key1.agg_public_key(), agg_key3.agg_public_key()); - let p1_nonce = musig.gen_nonces(&keypair1.sk, &keylist1, b"test"); - let p2_nonce = musig.gen_nonces(&keypair2.sk, &keylist1, b"test"); - let p3_nonce = musig.gen_nonces(&keypair3.sk, &keylist1, b"test"); + + let p1_nonce = musig.gen_nonces(&keypair1.sk, &agg_key1, b"test"); + let p2_nonce = musig.gen_nonces(&keypair2.sk, &agg_key2, b"test"); + let p3_nonce = musig.gen_nonces(&keypair3.sk, &agg_key3, b"test"); let nonces = vec![p1_nonce.public, p2_nonce.public, p3_nonce.public]; let message = @@ -665,57 +701,55 @@ mod test { let p1_session = musig .start_sign_session( - &keylist1, + &agg_key1, nonces.clone(), message, ) .unwrap(); let p2_session = musig .start_sign_session( - &keylist2, + &agg_key2, nonces.clone(), message, ) .unwrap(); let p3_session = musig .start_sign_session( - &keylist3, + &agg_key3, nonces.clone(), message, ) .unwrap(); - let p1_sig = musig.sign(&keylist1, 0, &keypair1, p1_nonce, &p1_session); + let p1_sig = musig.sign(&agg_key1, 0, &keypair1, p1_nonce, &p1_session); - assert!(musig.verify_partial_signature(&keylist1, &p1_session, 0, p1_sig)); - dbg!(&p1_session, &p2_session); - dbg!(&p1_sig); + assert!(musig.verify_partial_signature(&agg_key1, &p1_session, 0, p1_sig)); assert_eq!(p1_session, p2_session); - assert!(musig.verify_partial_signature(&keylist1, &p2_session, 0, p1_sig)); - assert!(musig.verify_partial_signature(&keylist1, &p3_session, 0, p1_sig)); + assert!(musig.verify_partial_signature(&agg_key1, &p2_session, 0, p1_sig)); + assert!(musig.verify_partial_signature(&agg_key1, &p3_session, 0, p1_sig)); - let p2_sig = musig.sign(&keylist1, 1, &keypair2, p2_nonce, &p2_session); - assert!(musig.verify_partial_signature(&keylist1, &p1_session, 1, p2_sig)); - let p3_sig = musig.sign(&keylist1, 2, &keypair3, p3_nonce, &p3_session); - assert!(musig.verify_partial_signature(&keylist1, &p1_session, 2, p3_sig)); + let p2_sig = musig.sign(&agg_key1, 1, &keypair2, p2_nonce, &p2_session); + assert!(musig.verify_partial_signature(&agg_key1, &p1_session, 1, p2_sig)); + let p3_sig = musig.sign(&agg_key1, 2, &keypair3, p3_nonce, &p3_session); + assert!(musig.verify_partial_signature(&agg_key1, &p1_session, 2, p3_sig)); let partial_sigs = [p1_sig, p2_sig, p3_sig]; - let sig_p1 = musig.combine_partial_signatures(&keylist1, &p1_session, partial_sigs); - let sig_p2 = musig.combine_partial_signatures(&keylist1, &p2_session, partial_sigs); - let sig_p3 = musig.combine_partial_signatures(&keylist1, &p3_session, partial_sigs); + let sig_p1 = musig.combine_partial_signatures(&agg_key1, &p1_session, partial_sigs); + let sig_p2 = musig.combine_partial_signatures(&agg_key1, &p2_session, partial_sigs); + let sig_p3 = musig.combine_partial_signatures(&agg_key1, &p3_session, partial_sigs); assert_eq!(sig_p1, sig_p2); assert_eq!(sig_p1, sig_p3); assert!(musig .schnorr - .verify(&keylist1.agg_verification_key(), message, &sig_p1)); + .verify(&agg_key1.agg_public_key(), message, &sig_p1)); assert!(musig .schnorr - .verify(&keylist1.agg_verification_key(), message, &sig_p2)); + .verify(&agg_key1.agg_public_key(), message, &sig_p2)); assert!(musig .schnorr - .verify(&keylist1.agg_verification_key(), message, &sig_p3)); + .verify(&agg_key1.agg_public_key(), message, &sig_p3)); } #[test] @@ -738,33 +772,32 @@ mod test { .new_keypair(sk3); let encryption_key = musig.schnorr.encryption_key_for(&y); - let keylist = musig.new_keylist(vec![ - keypair1.public_key(), - keypair2.public_key(), - keypair3.public_key(), - ]); - let keylist2 = musig.new_keylist(vec![ - keypair1.public_key(), - keypair2.public_key(), - keypair3.public_key(), - ]); - let keylist3 = musig.new_keylist(vec![ - keypair1.public_key(), - keypair2.public_key(), - keypair3.public_key(), - ]); - assert_eq!(keylist.agg_public_key(), keylist2.agg_public_key()); + let agg_key = musig.new_keylist(vec![ + keypair1.public_key(), + keypair2.public_key(), + keypair3.public_key(), + ]).into_agg_key(); + let agg_key2 = musig.new_keylist(vec![ + keypair1.public_key(), + keypair2.public_key(), + keypair3.public_key(), + ]).into_agg_key(); + let agg_key3 = musig.new_keylist(vec![ + keypair1.public_key(), + keypair2.public_key(), + keypair3.public_key(), + ]).into_agg_key(); - let p1_nonce = musig.gen_nonces(&keypair1.sk, &keylist, b"test"); - let p2_nonce = musig.gen_nonces(&keypair2.sk, &keylist2, b"test"); - let p3_nonce = musig.gen_nonces(&keypair3.sk, &keylist3, b"test"); + let p1_nonce = musig.gen_nonces(&keypair1.sk, &agg_key, b"test"); + let p2_nonce = musig.gen_nonces(&keypair2.sk, &agg_key2, b"test"); + let p3_nonce = musig.gen_nonces(&keypair3.sk, &agg_key3, b"test"); let nonces = vec![p1_nonce.public, p2_nonce.public, p3_nonce.public]; let message = Message::::plain("test", b"Chancellor on brink of second bailout for banks"); let mut p1_session = musig .start_encrypted_sign_session( - &keylist, + &agg_key, nonces.clone(), message, &encryption_key @@ -772,7 +805,7 @@ mod test { .unwrap(); let mut p2_session = musig .start_encrypted_sign_session( - &keylist2, + &agg_key2, nonces.clone(), message, &encryption_key @@ -780,34 +813,34 @@ mod test { .unwrap(); let mut p3_session = musig .start_encrypted_sign_session( - &keylist3, + &agg_key3, nonces, message, &encryption_key ) .unwrap(); - let p1_sig = musig.sign(&keylist, 0, &keypair1, p1_nonce, &mut p1_session); - let p2_sig = musig.sign(&keylist, 1, &keypair2, p2_nonce, &mut p2_session); - let p3_sig = musig.sign(&keylist, 2, &keypair3, p3_nonce, &mut p3_session); + let p1_sig = musig.sign(&agg_key, 0, &keypair1, p1_nonce, &mut p1_session); + let p2_sig = musig.sign(&agg_key, 1, &keypair2, p2_nonce, &mut p2_session); + let p3_sig = musig.sign(&agg_key, 2, &keypair3, p3_nonce, &mut p3_session); - assert!(musig.verify_partial_signature(&keylist2, &p2_session, 0, p1_sig)); - assert!(musig.verify_partial_signature(&keylist, &p1_session, 0, p1_sig)); + assert!(musig.verify_partial_signature(&agg_key2, &p2_session, 0, p1_sig)); + assert!(musig.verify_partial_signature(&agg_key, &p1_session, 0, p1_sig)); let partial_sigs = vec![p1_sig, p2_sig, p3_sig]; - let combined_sig_p1 = musig.combine_partial_encrypted_signatures(&keylist, &p1_session, partial_sigs.clone()); - let combined_sig_p2 = musig.combine_partial_encrypted_signatures(&keylist2, &p2_session, partial_sigs.clone()); - let combined_sig_p3 = musig.combine_partial_encrypted_signatures(&keylist3, &p3_session, partial_sigs); + let combined_sig_p1 = musig.combine_partial_encrypted_signatures(&agg_key, &p1_session, partial_sigs.clone()); + let combined_sig_p2 = musig.combine_partial_encrypted_signatures(&agg_key2, &p2_session, partial_sigs.clone()); + let combined_sig_p3 = musig.combine_partial_encrypted_signatures(&agg_key3, &p3_session, partial_sigs); assert_eq!(combined_sig_p1, combined_sig_p2); assert_eq!(combined_sig_p1, combined_sig_p3); assert!(musig .schnorr - .verify_encrypted_signature(&keylist.agg_verification_key(), &encryption_key, message, &combined_sig_p1)); + .verify_encrypted_signature(&agg_key.agg_public_key(), &encryption_key, message, &combined_sig_p1)); assert!(musig .schnorr - .verify_encrypted_signature(&keylist2.agg_verification_key(), &encryption_key, message, &combined_sig_p2)); + .verify_encrypted_signature(&agg_key2.agg_public_key(), &encryption_key, message, &combined_sig_p2)); assert!(musig .schnorr - .verify_encrypted_signature(&keylist2.agg_verification_key(), &encryption_key, message, &combined_sig_p3)); + .verify_encrypted_signature(&agg_key2.agg_public_key(), &encryption_key, message, &combined_sig_p3)); } } @@ -862,20 +895,30 @@ mod test { let musig = MuSig::>>::default(); assert_eq!( - musig.new_keylist(vec![X[0], X[1], X[2]]).agg_public_key(), + musig + .new_keylist(vec![X[0], X[1], X[2]]) + .into_agg_key() + .agg_public_key(), expected[0] ); assert_eq!( - musig.new_keylist(vec![X[2], X[1], X[0]]).agg_public_key(), + musig + .new_keylist(vec![X[2], X[1], X[0]]) + .into_agg_key() + .agg_public_key(), expected[1] ); assert_eq!( - musig.new_keylist(vec![X[0], X[0], X[0]]).agg_public_key(), + musig + .new_keylist(vec![X[0], X[0], X[0]]) + .into_agg_key() + .agg_public_key(), expected[2] ); assert_eq!( musig .new_keylist(vec![X[0], X[0], X[1], X[1]]) + .into_agg_key() .agg_public_key(), expected[3] ); @@ -964,27 +1007,29 @@ mod test { Nonce([-sec_nonce.public.0[0], -sec_nonce.public.0[1]]), ); let message = Message::::raw(&msg); - let keylist = musig.new_keylist(vec![keypair.pk, X1, X2]); - - let sign_session = musig - .start_sign_session( - &keylist, - vec![ - sec_nonce.public(), - remote_nonce1.clone(), - remote_nonce2.clone(), - ], - message, - ) - .unwrap(); - let sig = musig.sign(&keylist, 0, &keypair, sec_nonce.clone(), &sign_session); - assert_eq!(sig, expected[0]); + { + let agg_key = musig.new_keylist(vec![keypair.pk, X1, X2]).into_agg_key(); + + let sign_session = musig + .start_sign_session( + &agg_key, + vec![ + sec_nonce.public(), + remote_nonce1.clone(), + remote_nonce2.clone(), + ], + message, + ) + .unwrap(); + let sig = musig.sign(&agg_key, 0, &keypair, sec_nonce.clone(), &sign_session); + assert_eq!(sig, expected[0]); + } { - let keylist = musig.new_keylist(vec![X1, keypair.pk, X2]); + let agg_key = musig.new_keylist(vec![X1, keypair.pk, X2]).into_agg_key(); let sign_session = musig .start_sign_session( - &keylist, + &agg_key, vec![ remote_nonce1.clone(), sec_nonce.public(), @@ -993,15 +1038,15 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 1, &keypair, sec_nonce.clone(), &sign_session); + let sig = musig.sign(&agg_key, 1, &keypair, sec_nonce.clone(), &sign_session); assert_eq!(sig, expected[1]); } { - let keylist = musig.new_keylist(vec![X1, X2, keypair.pk]); + let agg_key = musig.new_keylist(vec![X1, X2, keypair.pk]).into_agg_key(); let sign_session = musig .start_sign_session( - &keylist, + &agg_key, vec![ remote_nonce1.clone(), remote_nonce2.clone(), @@ -1010,7 +1055,7 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); + let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); assert_eq!(sig, expected[2]); } } @@ -1142,11 +1187,14 @@ mod test { let message = Message::::raw(&msg); { - let mut keylist = musig.new_keylist(vec![X1, X2, keypair.pk]); - keylist = keylist.tweak(tweaks[0].clone(), true).unwrap(); + let agg_key = musig + .new_keylist(vec![X1, X2, keypair.pk]) + .into_agg_key() + .tweak(tweaks[0].clone()) + .unwrap(); let sign_session = musig .start_sign_session( - &keylist, + &agg_key, vec![ remote_nonce1.clone(), remote_nonce2.clone(), @@ -1155,16 +1203,19 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); + let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); assert_eq!(sig, expected[0]); } { - let mut keylist = musig.new_keylist(vec![X1, X2, keypair.pk]); - keylist = keylist.tweak(tweaks[0].clone(), true).unwrap(); + let agg_key = musig + .new_keylist(vec![X1, X2, keypair.pk]) + .into_agg_key() + .tweak(tweaks[0].clone()) + .unwrap(); let sign_session = musig .start_sign_session( - &keylist, + &agg_key, vec![ remote_nonce1.clone(), remote_nonce2.clone(), @@ -1173,39 +1224,22 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); + let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); assert_eq!(sig, expected[0]); } { - let mut keylist = musig.new_keylist(vec![X1, X2, keypair.pk]); - keylist = keylist.tweak(tweaks[0].clone(), false).unwrap(); - keylist = keylist.tweak(tweaks[1].clone(), true).unwrap(); - let sign_session = musig - .start_sign_session( - &keylist, - vec![ - remote_nonce1.clone(), - remote_nonce2.clone(), - sec_nonce.public(), - ], - message, - ) + let agg_key = musig + .new_keylist(vec![X1, X2, keypair.pk]) + .tweak(tweaks[0].clone()) + .unwrap() + .into_agg_key() + .tweak(tweaks[1].clone()) .unwrap(); - let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); - assert_eq!(sig, expected[2]); - } - - { - let mut keylist = musig.new_keylist(vec![X1, X2, keypair.pk]); - keylist = keylist.tweak(tweaks[0].clone(), true).unwrap(); - keylist = keylist.tweak(tweaks[1].clone(), false).unwrap(); - keylist = keylist.tweak(tweaks[2].clone(), true).unwrap(); - keylist = keylist.tweak(tweaks[3].clone(), false).unwrap(); let sign_session = musig .start_sign_session( - &keylist, + &agg_key, vec![ remote_nonce1.clone(), remote_nonce2.clone(), @@ -1214,8 +1248,30 @@ mod test { message, ) .unwrap(); - let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); - assert_eq!(sig, expected[3]); + let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); + assert_eq!(sig, expected[2]); } + + // { + // let mut keylist = musig.new_keylist(vec![X1, X2, keypair.pk]); + // keylist = keylist.tweak(tweaks[0].clone(), true).unwrap(); + // keylist = keylist.tweak(tweaks[1].clone(), false).unwrap(); + // keylist = keylist.tweak(tweaks[2].clone(), true).unwrap(); + // keylist = keylist.tweak(tweaks[3].clone(), false).unwrap(); + + // let sign_session = musig + // .start_sign_session( + // &keylist, + // vec![ + // remote_nonce1.clone(), + // remote_nonce2.clone(), + // sec_nonce.public(), + // ], + // message, + // ) + // .unwrap(); + // let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); + // assert_eq!(sig, expected[3]); + // } } } From 30576405e512302f7e505f5fd23480578190a924 Mon Sep 17 00:00:00 2001 From: LLFourn Date: Tue, 14 Jun 2022 17:50:52 +0800 Subject: [PATCH 3/8] Renaming and better docs --- schnorr_fun/src/musig.rs | 199 +++++++++++++++++++++------------------ 1 file changed, 107 insertions(+), 92 deletions(-) diff --git a/schnorr_fun/src/musig.rs b/schnorr_fun/src/musig.rs index ce171eb9..1eb21930 100644 --- a/schnorr_fun/src/musig.rs +++ b/schnorr_fun/src/musig.rs @@ -7,7 +7,7 @@ //! use sha2::Sha256; //! // use sha256 with deterministic nonce generation //! let musig = MuSig::>>::default(); -//! // create a keylist +//! // create a keypair //! use schnorr_fun::fun::Scalar; //! let my_keypair = musig //! .schnorr @@ -18,34 +18,35 @@ //! # let kp3 = musig.schnorr.new_keypair(Scalar::random(&mut rand::thread_rng())); //! # let public_key3 = kp3.public_key(); //! // recieve the public keys of all other participants to form the aggregate key. -//! let keylist = musig.new_keylist(vec![public_key1, public_key2, public_key3]); -//! let agg_key = keylist.agg_public_key(); +//! let agg_key = musig +//! .new_agg_key(vec![public_key1, public_key2, public_key3]) +//! .into_bip340_key(); //! //! // create a unique nonce, and send the public nonce to other parties. -//! let my_nonce = musig.gen_nonces(my_keypair.secret_key(), &keylist, b"session-id-1337"); +//! let my_nonce = musig.gen_nonces(my_keypair.secret_key(), &agg_key, b"session-id-1337"); //! let my_public_nonce = my_nonce.public(); -//! # let p2_nonce = musig.gen_nonces(kp2.secret_key(), &keylist, b"session-id-1337"); +//! # let p2_nonce = musig.gen_nonces(kp2.secret_key(), &agg_key, b"session-id-1337"); //! # let p2_public_nonce = p2_nonce.public(); -//! # let p3_nonce = musig.gen_nonces(kp3.secret_key(), &keylist, b"session-id-1337"); +//! # let p3_nonce = musig.gen_nonces(kp3.secret_key(), &agg_key, b"session-id-1337"); //! # let p3_public_nonce = p3_nonce.public(); //! // collect the public nonces from the other two parties //! let nonces = vec![my_public_nonce, p2_public_nonce, p3_public_nonce]; //! let message = Message::plain("my-app", b"chancellor on brink of second bailout for banks"); //! // start the signing session -//! let session = musig.start_sign_session(&keylist, nonces, message).unwrap(); +//! let session = musig.start_sign_session(&agg_key, nonces, message).unwrap(); //! // sign with our single local keypair -//! let my_sig = musig.sign(&keylist, 0, &my_keypair, my_nonce, &session); -//! # let p2_sig = musig.sign(&keylist, 1, &kp2, p2_nonce, &session); -//! # let p3_sig = musig.sign(&keylist, 2, &kp3, p3_nonce, &session); +//! let my_sig = musig.sign(&agg_key, 0, &my_keypair, my_nonce, &session); +//! # let p2_sig = musig.sign(&agg_key, 1, &kp2, p2_nonce, &session); +//! # let p3_sig = musig.sign(&agg_key, 2, &kp3, p3_nonce, &session); //! // receive p2_sig and p3_sig from somewhere and check they're valid -//! assert!(musig.verify_partial_signature(&keylist, &session, 1, p2_sig)); -//! assert!(musig.verify_partial_signature(&keylist, &session, 2, p3_sig)); +//! assert!(musig.verify_partial_signature(&agg_key, &session, 1, p2_sig)); +//! assert!(musig.verify_partial_signature(&agg_key, &session, 2, p3_sig)); //! // combine them with ours into the final signature -//! let sig = musig.combine_partial_signatures(&keylist, &session, [my_sig, p2_sig, p3_sig]); +//! let sig = musig.combine_partial_signatures(&agg_key, &session, [my_sig, p2_sig, p3_sig]); //! // check it's a valid normal Schnorr signature //! musig //! .schnorr -//! .verify(&keylist.agg_verification_key(), message, &sig); +//! .verify(&agg_key.agg_public_key(), message, &sig); //! ``` //! //! ## Description @@ -109,13 +110,13 @@ impl MuSig> { /// A list of keys aggregated into a single key. /// -/// Created using [`MuSig::new_keylist`]. +/// Created using [`MuSig::new_agg_key`]. /// -/// The `KeyList` can't be serialized but it's very efficient to re-create it from the initial list of keys. +/// The `AggKey` can't be serialized but it's very efficient to re-create it from the initial list of keys. /// -/// [`MuSig::new_keylist`] +/// [`MuSig::new_agg_key`] #[derive(Debug, Clone)] -pub struct KeyList { +pub struct AggKey { /// The parties involved in the key aggregation. parties: Vec, /// The coefficients of each key @@ -126,33 +127,36 @@ pub struct KeyList { tweak: Scalar, } -impl KeyList { - /// The pre-finalized aggregate key - pub fn agg_public_key(&self) -> Point { +impl AggKey { + /// The aggregate key prior to converting to a BIP340 `XOnly` key. + pub fn agg_key(&self) -> Point { self.agg_key } - /// An iterator over the **public keys** of each party in the keylist. + /// An iterator over the **public keys** of each party in the aggregate key. pub fn keys(&self) -> impl Iterator + '_ { self.parties.iter().map(|xonly| *xonly) } - /// Tweak the aggregate MuSig public key with a scalar so that the resulting key is equal to the - /// existing key plus `tweak * G`. The tweak mutates the public key while still allowing - /// the original set of signers to sign under the new key. + /// Add a scalar `tweak` to aggregate MuSig public key. /// - /// This is how you embed a taproot commitment into a key. + /// The resulting key is equal to the existing key plus `tweak * G`. The tweak mutates the + /// public key while still allowing the original set of signers to sign under the new key. + /// This function is appropriate for doing [BIP32] tweaks before calling `into_bip340_key`. + /// It **is not** appropriate for doing taproot tweaking which must be done on a [`Bip340AggKey`]. /// /// ## Return value /// - /// Returns a new MuSig KeyList with the same parties but a different aggregated public key. /// In the erroneous case that the tweak is exactly equal to the negation of the aggregate /// secret key it returns `None`. + /// + /// [BIP32]: https://bips.xyz/32 + /// [`Bip340AggKey`]: crate::musig::Bip340AggKey pub fn tweak(self, tweak: Scalar) -> Option { let agg_key = g!(self.agg_key + tweak * G).normalize().mark::()?; let tweak = s!(self.tweak + tweak).mark::(); - Some(KeyList { + Some(AggKey { parties: self.parties.clone(), coefs: self.coefs.clone(), agg_key, @@ -160,11 +164,14 @@ impl KeyList { }) } - pub fn into_agg_key(self) -> AggKey { + /// Convert the key into an `Bip340AggKey`. + /// + /// This is the BIP340 compatible version of the key which you can put in a segwitv1 output and create BIP340 signatures under. + pub fn into_bip340_key(self) -> Bip340AggKey { let (agg_key, needs_negation) = self.agg_key.into_point_with_even_y(); let mut tweak = self.tweak; tweak.conditional_negate(needs_negation); - AggKey { + Bip340AggKey { parties: self.parties, coefs: self.coefs, needs_negation, @@ -174,9 +181,11 @@ impl KeyList { } } -/// A [`KeyList`] that has been converted into a final aggregate `XOnly` key. +/// A [`AggKey`] that has been converted into a [BIP340] `XOnly` key. +/// +/// [BIP340]: https://bips.xyz/340 #[derive(Debug, Clone)] -pub struct AggKey { +pub struct Bip340AggKey { /// The parties involved in the key aggregation. parties: Vec, /// The coefficients of each key @@ -189,13 +198,13 @@ pub struct AggKey { agg_key: Point, } -impl AggKey { - /// The pre-finalized aggregate key +impl Bip340AggKey { + /// The aggregate key as a `Point` pub fn agg_public_key(&self) -> Point { self.agg_key } - /// An iterator over the **public keys** of each party in the keylist. + /// An iterator over the **public keys** of each party in the agg_key. pub fn keys(&self) -> impl Iterator + '_ { self.parties.iter().map(|xonly| *xonly) } @@ -221,7 +230,7 @@ impl AggKey { } impl + Clone, S> MuSig { - /// Generates a new key list from a list of parties. + /// Generates a new aggregated key from a list of individual keys. /// /// Each party can be local (you know the secret key) or remote (you only know the public key). /// @@ -241,9 +250,9 @@ impl + Clone, S> MuSig { /// let my_keypair = musig.schnorr.new_keypair(my_secret_key); /// let my_public_key = my_keypair.public_key(); /// // Note the keys have to come in the same order on the other side! - /// let keylist = musig.new_keylist(vec![their_public_key, my_public_key]); + /// let agg_key = musig.new_agg_key(vec![their_public_key, my_public_key]); /// ``` - pub fn new_keylist(&self, parties: Vec) -> KeyList { + pub fn new_agg_key(&self, parties: Vec) -> AggKey { let keys = parties.clone(); let coeff_hash = { let L = self.pk_hash.clone().add(&keys[..]).finalize(); @@ -271,7 +280,7 @@ impl + Clone, S> MuSig { let agg_key = crate::fun::op::lincomb(coefs.iter(), points.iter()) .expect_nonzero("computationally unreachable: linear combination of hash randomised points cannot add to zero"); - KeyList { + AggKey { parties, coefs, agg_key: agg_key.mark::(), @@ -281,7 +290,7 @@ impl + Clone, S> MuSig { } impl + Clone, NG: NonceGen> MuSig> { - /// Generate nonces for your local keys in keylist. + /// Generate nonces for signing under your aggregate key. /// /// It is very important to carefully consider the implications of your choice of underlying /// [`NonceGen`]. @@ -302,16 +311,16 @@ impl + Clone, NG: NonceGen> MuSig> /// [`Deterministic`]: secp256kfun::nonce::Deterministic /// [`start_sign_session`]: Self::start_sign_session /// [`NonceKeyPair`]: schnorr_fun::binonce::NonceKeyPair - pub fn gen_nonces(&self, secret: &Scalar, keylist: &AggKey, sid: &[u8]) -> NonceKeyPair { + pub fn gen_nonces(&self, secret: &Scalar, agg_key: &Bip340AggKey, sid: &[u8]) -> NonceKeyPair { let r1 = derive_nonce!( nonce_gen => self.schnorr.nonce_gen(), secret => secret, - public => [ b"r1", keylist.agg_public_key(), sid] + public => [ b"r1", agg_key.agg_public_key(), sid] ); let r2 = derive_nonce!( nonce_gen => self.schnorr.nonce_gen(), secret => secret, - public => [ b"r2", keylist.agg_public_key(), sid] + public => [ b"r2", agg_key.agg_public_key(), sid] ); let R1 = g!(r1 * G).normalize(); let R2 = g!(r2 * G).normalize(); @@ -390,7 +399,7 @@ impl + Clone, NG> MuSig> { /// Panics if number of nonces does not align with the parties in `agg_key`. pub fn start_sign_session( &self, - agg_key: &AggKey, + agg_key: &Bip340AggKey, nonces: Vec, message: Message<'_, Public>, ) -> Option { @@ -428,7 +437,7 @@ impl + Clone, NG> MuSig> { /// [`adaptor`]: crate::adaptor pub fn start_encrypted_sign_session( &self, - agg_key: &AggKey, + agg_key: &Bip340AggKey, nonces: Vec, message: Message<'_, Public>, encryption_key: &Point, @@ -449,7 +458,7 @@ impl + Clone, NG> MuSig> { fn _start_sign_session( &self, - agg_key: &AggKey, + agg_key: &Bip340AggKey, nonces: Vec, message: Message<'_, Public>, encryption_key: &Point, @@ -506,7 +515,7 @@ impl + Clone, NG> MuSig> { /// Generates a partial signature (or partial encrypted signature depending on `T`) for the local_secret_nonce. pub fn sign( &self, - agg_key: &AggKey, + agg_key: &Bip340AggKey, my_index: u32, keypair: &KeyPair, local_secret_nonce: NonceKeyPair, @@ -533,14 +542,14 @@ impl + Clone, NG> MuSig> { #[must_use] /// Verifies a partial signature (or partial encrypted signature depending on `T`). /// - /// You must provide the `index` of the party (the index of the key in `keylist`). + /// You must provide the `index` of the party (the index of the key in `agg_key`). /// /// # Panics /// - /// Panics when `index` is equal to or greater than the number of parties in the keylist. + /// Panics when `index` is equal to or greater than the number of parties in the agg_key. pub fn verify_partial_signature( &self, - agg_key: &AggKey, + agg_key: &Bip340AggKey, session: &SignSession, index: usize, partial_sig: Scalar, @@ -570,7 +579,7 @@ impl + Clone, NG> MuSig> { /// [`verify_partial_signature`]: Self::verify_partial_signature pub fn combine_partial_signatures( &self, - agg_key: &AggKey, + agg_key: &Bip340AggKey, session: &SignSession, partial_sigs: impl IntoIterator>, ) -> Signature { @@ -587,7 +596,7 @@ impl + Clone, NG> MuSig> { /// [`verify_partial_signature`]: Self::verify_partial_signature pub fn combine_partial_encrypted_signatures( &self, - agg_key: &AggKey, + agg_key: &Bip340AggKey, session: &SignSession, partial_encrypted_sigs: impl IntoIterator>, ) -> EncryptedSignature { @@ -602,7 +611,7 @@ impl + Clone, NG> MuSig> { fn _combine_partial_signatures( &self, - agg_key: &AggKey, + agg_key: &Bip340AggKey, session: &SignSession, partial_sigs: impl IntoIterator>, ) -> (Point, Scalar) { @@ -650,17 +659,17 @@ mod test { .schnorr .new_keypair(sk3); - let mut keylist1 = musig.new_keylist(vec![ + let mut agg_key1 = musig.new_agg_key(vec![ keypair1.public_key(), keypair2.public_key(), keypair3.public_key(), ]); - let mut keylist2 = musig.new_keylist(vec![ + let mut agg_key2 = musig.new_agg_key(vec![ keypair1.public_key(), keypair2.public_key(), keypair3.public_key(), ]); - let mut keylist3 = musig.new_keylist(vec![ + let mut agg_key3 = musig.new_agg_key(vec![ keypair1.public_key(), keypair2.public_key(), keypair3.public_key(), @@ -668,16 +677,16 @@ mod test { for tweak in [pre_tweak1, pre_tweak2] { if let Some(tweak) = tweak { - keylist1 = keylist1.tweak(tweak).unwrap(); - keylist2 = keylist2.tweak(tweak).unwrap(); - keylist3 = keylist3.tweak(tweak).unwrap(); + agg_key1 = agg_key1.tweak(tweak).unwrap(); + agg_key2 = agg_key2.tweak(tweak).unwrap(); + agg_key3 = agg_key3.tweak(tweak).unwrap(); } } - let mut agg_key1 = keylist1.into_agg_key(); - let mut agg_key2 = keylist2.into_agg_key(); - let mut agg_key3 = keylist3.into_agg_key(); + let mut agg_key1 = agg_key1.into_bip340_key(); + let mut agg_key2 = agg_key2.into_bip340_key(); + let mut agg_key3 = agg_key3.into_bip340_key(); for tweak in [tweak1, tweak2] { if let Some(tweak) = tweak { @@ -772,21 +781,21 @@ mod test { .new_keypair(sk3); let encryption_key = musig.schnorr.encryption_key_for(&y); - let agg_key = musig.new_keylist(vec![ + let agg_key = musig.new_agg_key(vec![ keypair1.public_key(), keypair2.public_key(), keypair3.public_key(), - ]).into_agg_key(); - let agg_key2 = musig.new_keylist(vec![ + ]).into_bip340_key(); + let agg_key2 = musig.new_agg_key(vec![ keypair1.public_key(), keypair2.public_key(), keypair3.public_key(), - ]).into_agg_key(); - let agg_key3 = musig.new_keylist(vec![ + ]).into_bip340_key(); + let agg_key3 = musig.new_agg_key(vec![ keypair1.public_key(), keypair2.public_key(), keypair3.public_key(), - ]).into_agg_key(); + ]).into_bip340_key(); let p1_nonce = musig.gen_nonces(&keypair1.sk, &agg_key, b"test"); let p2_nonce = musig.gen_nonces(&keypair2.sk, &agg_key2, b"test"); @@ -896,29 +905,29 @@ mod test { let musig = MuSig::>>::default(); assert_eq!( musig - .new_keylist(vec![X[0], X[1], X[2]]) - .into_agg_key() + .new_agg_key(vec![X[0], X[1], X[2]]) + .into_bip340_key() .agg_public_key(), expected[0] ); assert_eq!( musig - .new_keylist(vec![X[2], X[1], X[0]]) - .into_agg_key() + .new_agg_key(vec![X[2], X[1], X[0]]) + .into_bip340_key() .agg_public_key(), expected[1] ); assert_eq!( musig - .new_keylist(vec![X[0], X[0], X[0]]) - .into_agg_key() + .new_agg_key(vec![X[0], X[0], X[0]]) + .into_bip340_key() .agg_public_key(), expected[2] ); assert_eq!( musig - .new_keylist(vec![X[0], X[0], X[1], X[1]]) - .into_agg_key() + .new_agg_key(vec![X[0], X[0], X[1], X[1]]) + .into_bip340_key() .agg_public_key(), expected[3] ); @@ -1008,7 +1017,9 @@ mod test { ); let message = Message::::raw(&msg); { - let agg_key = musig.new_keylist(vec![keypair.pk, X1, X2]).into_agg_key(); + let agg_key = musig + .new_agg_key(vec![keypair.pk, X1, X2]) + .into_bip340_key(); let sign_session = musig .start_sign_session( @@ -1026,7 +1037,9 @@ mod test { } { - let agg_key = musig.new_keylist(vec![X1, keypair.pk, X2]).into_agg_key(); + let agg_key = musig + .new_agg_key(vec![X1, keypair.pk, X2]) + .into_bip340_key(); let sign_session = musig .start_sign_session( &agg_key, @@ -1043,7 +1056,9 @@ mod test { } { - let agg_key = musig.new_keylist(vec![X1, X2, keypair.pk]).into_agg_key(); + let agg_key = musig + .new_agg_key(vec![X1, X2, keypair.pk]) + .into_bip340_key(); let sign_session = musig .start_sign_session( &agg_key, @@ -1188,8 +1203,8 @@ mod test { { let agg_key = musig - .new_keylist(vec![X1, X2, keypair.pk]) - .into_agg_key() + .new_agg_key(vec![X1, X2, keypair.pk]) + .into_bip340_key() .tweak(tweaks[0].clone()) .unwrap(); let sign_session = musig @@ -1209,8 +1224,8 @@ mod test { { let agg_key = musig - .new_keylist(vec![X1, X2, keypair.pk]) - .into_agg_key() + .new_agg_key(vec![X1, X2, keypair.pk]) + .into_bip340_key() .tweak(tweaks[0].clone()) .unwrap(); let sign_session = musig @@ -1230,10 +1245,10 @@ mod test { { let agg_key = musig - .new_keylist(vec![X1, X2, keypair.pk]) + .new_agg_key(vec![X1, X2, keypair.pk]) .tweak(tweaks[0].clone()) .unwrap() - .into_agg_key() + .into_bip340_key() .tweak(tweaks[1].clone()) .unwrap(); @@ -1253,15 +1268,15 @@ mod test { } // { - // let mut keylist = musig.new_keylist(vec![X1, X2, keypair.pk]); - // keylist = keylist.tweak(tweaks[0].clone(), true).unwrap(); - // keylist = keylist.tweak(tweaks[1].clone(), false).unwrap(); - // keylist = keylist.tweak(tweaks[2].clone(), true).unwrap(); - // keylist = keylist.tweak(tweaks[3].clone(), false).unwrap(); + // let mut agg_key = musig.new_agg_key(vec![X1, X2, keypair.pk]); + // agg_key = agg_key.tweak(tweaks[0].clone(), true).unwrap(); + // agg_key = agg_key.tweak(tweaks[1].clone(), false).unwrap(); + // agg_key = agg_key.tweak(tweaks[2].clone(), true).unwrap(); + // agg_key = agg_key.tweak(tweaks[3].clone(), false).unwrap(); // let sign_session = musig // .start_sign_session( - // &keylist, + // &agg_key, // vec![ // remote_nonce1.clone(), // remote_nonce2.clone(), @@ -1270,7 +1285,7 @@ mod test { // message, // ) // .unwrap(); - // let sig = musig.sign(&keylist, 2, &keypair, sec_nonce.clone(), &sign_session); + // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); // assert_eq!(sig, expected[3]); // } } From 9ed82a295739eb66589aac75ea378284ccb33b6f Mon Sep 17 00:00:00 2001 From: LLFourn Date: Fri, 1 Jul 2022 08:57:03 +0800 Subject: [PATCH 4/8] Change input keys to ordinary points instead of xonly see: https://github.com/jonasnick/bips/issues/32 for motivation --- schnorr_fun/src/musig.rs | 966 ++++++++++++++++++++------------------- 1 file changed, 499 insertions(+), 467 deletions(-) diff --git a/schnorr_fun/src/musig.rs b/schnorr_fun/src/musig.rs index 1eb21930..9dd43293 100644 --- a/schnorr_fun/src/musig.rs +++ b/schnorr_fun/src/musig.rs @@ -9,13 +9,11 @@ //! let musig = MuSig::>>::default(); //! // create a keypair //! use schnorr_fun::fun::Scalar; -//! let my_keypair = musig -//! .schnorr -//! .new_keypair(Scalar::random(&mut rand::thread_rng())); +//! let my_keypair = musig.new_keypair(Scalar::random(&mut rand::thread_rng())); //! let public_key1 = my_keypair.public_key(); -//! # let kp2 = musig.schnorr.new_keypair(Scalar::random(&mut rand::thread_rng())); +//! # let kp2 = musig.new_keypair(Scalar::random(&mut rand::thread_rng())); //! # let public_key2 = kp2.public_key(); -//! # let kp3 = musig.schnorr.new_keypair(Scalar::random(&mut rand::thread_rng())); +//! # let kp3 = musig.new_keypair(Scalar::random(&mut rand::thread_rng())); //! # let public_key3 = kp3.public_key(); //! // recieve the public keys of all other participants to form the aggregate key. //! let agg_key = musig @@ -61,7 +59,7 @@ //! [the excellent paper]: https://eprint.iacr.org/2020/1261.pdf //! [secp256k1-zkp]: https://github.com/ElementsProject/secp256k1-zkp/pull/131 pub use crate::binonce::{Nonce, NonceKeyPair}; -use crate::{adaptor::EncryptedSignature, KeyPair, Message, Schnorr, Signature, Vec}; +use crate::{adaptor::EncryptedSignature, Message, Schnorr, Signature, Vec}; use secp256kfun::{ derive_nonce, digest::{generic_array::typenum::U32, Digest}, @@ -69,7 +67,7 @@ use secp256kfun::{ hash::{HashAdd, Tagged}, marker::*, nonce::NonceGen, - s, Point, Scalar, XOnly, G, + s, Point, Scalar, G, }; /// The MuSig context. @@ -85,6 +83,19 @@ pub struct MuSig { } impl MuSig { + /// Create a new [`KeyPair`] + /// + /// This is a convenient way of just doing: + /// + /// ``` + /// # let secret_key = schnorr_fun::fun::Scalar::random(&mut rand::thread_rng()); + /// use schnorr_fun::musig::KeyPair; + /// let keypair = KeyPair::new(secret_key); + /// ``` + pub fn new_keypair(&self, secret_key: Scalar) -> KeyPair { + KeyPair::new(secret_key) + } + fn _new(schnorr: S) -> Self { Self { pk_hash: H::default().tagged(b"KeyAgg list"), @@ -118,7 +129,7 @@ impl MuSig> { #[derive(Debug, Clone)] pub struct AggKey { /// The parties involved in the key aggregation. - parties: Vec, + parties: Vec, /// The coefficients of each key coefs: Vec>, /// The aggregate key @@ -134,8 +145,8 @@ impl AggKey { } /// An iterator over the **public keys** of each party in the aggregate key. - pub fn keys(&self) -> impl Iterator + '_ { - self.parties.iter().map(|xonly| *xonly) + pub fn keys(&self) -> impl Iterator + '_ { + self.parties.iter().map(|point| *point) } /// Add a scalar `tweak` to aggregate MuSig public key. @@ -187,7 +198,7 @@ impl AggKey { #[derive(Debug, Clone)] pub struct Bip340AggKey { /// The parties involved in the key aggregation. - parties: Vec, + parties: Vec, /// The coefficients of each key coefs: Vec>, /// Whether the secret keys needs to be negated when signing @@ -205,8 +216,8 @@ impl Bip340AggKey { } /// An iterator over the **public keys** of each party in the agg_key. - pub fn keys(&self) -> impl Iterator + '_ { - self.parties.iter().map(|xonly| *xonly) + pub fn keys(&self) -> impl Iterator + '_ { + self.parties.iter().map(|point| *point) } /// Applies an "XOnly" tweak to the aggregate key @@ -244,15 +255,15 @@ impl + Clone, S> MuSig { /// Schnorr, /// }; /// # let my_secret_key = Scalar::random(&mut rand::thread_rng()); - /// # let their_public_key = XOnly::random(&mut rand::thread_rng()); + /// # let their_public_key = Point::random(&mut rand::thread_rng()); /// use sha2::Sha256; /// let musig = MuSig::>>::default(); - /// let my_keypair = musig.schnorr.new_keypair(my_secret_key); + /// let my_keypair = musig.new_keypair(my_secret_key); /// let my_public_key = my_keypair.public_key(); /// // Note the keys have to come in the same order on the other side! /// let agg_key = musig.new_agg_key(vec![their_public_key, my_public_key]); /// ``` - pub fn new_agg_key(&self, parties: Vec) -> AggKey { + pub fn new_agg_key(&self, parties: Vec) -> AggKey { let keys = parties.clone(); let coeff_hash = { let L = self.pk_hash.clone().add(&keys[..]).finalize(); @@ -275,9 +286,8 @@ impl + Clone, S> MuSig { .mark::() }) .collect::>(); - let points = keys.into_iter().map(|x| x.to_point()).collect::>(); - let agg_key = crate::fun::op::lincomb(coefs.iter(), points.iter()) + let agg_key = crate::fun::op::lincomb(coefs.iter(), parties.iter()) .expect_nonzero("computationally unreachable: linear combination of hash randomised points cannot add to zero"); AggKey { @@ -563,7 +573,6 @@ impl + Clone, NG> MuSig> { .keys() .nth(index) .unwrap() - .to_point() .conditional_negate(agg_key.needs_negation); let [R1, R2] = &session.public_nonces[index].0; @@ -626,6 +635,35 @@ impl + Clone, NG> MuSig> { } } +#[derive(Clone, Debug, PartialEq)] +/// A MuSig key pair. +/// +/// Note that the public key is a ordinary point rather than an `XOnly` key like in Schnorr. +pub struct KeyPair { + secret_key: Scalar, + public_key: Point, +} + +impl KeyPair { + /// Create a new MuSig key + pub fn new(secret_key: Scalar) -> Self { + Self { + public_key: g!(secret_key * G).normalize(), + secret_key, + } + } + + /// Get the secret key + pub fn secret_key(&self) -> &Scalar { + &self.secret_key + } + + /// Get the public key + pub fn public_key(&self) -> Point { + self.public_key + } +} + #[cfg(test)] mod test { use crate::adaptor::Adaptor; @@ -650,13 +688,10 @@ mod test { let schnorr = Schnorr::::new(Deterministic::::default()); let musig = MuSig::new(schnorr); let keypair1 = musig - .schnorr .new_keypair(sk1); let keypair2 = musig - .schnorr .new_keypair(sk2); let keypair3 = musig - .schnorr .new_keypair(sk3); let mut agg_key1 = musig.new_agg_key(vec![ @@ -700,9 +735,9 @@ mod test { assert_eq!(agg_key1.agg_public_key(), agg_key3.agg_public_key()); - let p1_nonce = musig.gen_nonces(&keypair1.sk, &agg_key1, b"test"); - let p2_nonce = musig.gen_nonces(&keypair2.sk, &agg_key2, b"test"); - let p3_nonce = musig.gen_nonces(&keypair3.sk, &agg_key3, b"test"); + let p1_nonce = musig.gen_nonces(keypair1.secret_key(), &agg_key1, b"test"); + let p2_nonce = musig.gen_nonces(keypair2.secret_key(), &agg_key2, b"test"); + let p3_nonce = musig.gen_nonces(keypair3.secret_key(), &agg_key3, b"test"); let nonces = vec![p1_nonce.public, p2_nonce.public, p3_nonce.public]; let message = @@ -771,13 +806,10 @@ mod test { let schnorr = Schnorr::::new(Deterministic::::default()); let musig = MuSig::new(schnorr); let keypair1 = musig - .schnorr .new_keypair(sk1); let keypair2 = musig - .schnorr .new_keypair(sk2); let keypair3 = musig - .schnorr .new_keypair(sk3); let encryption_key = musig.schnorr.encryption_key_for(&y); @@ -797,9 +829,9 @@ mod test { keypair3.public_key(), ]).into_bip340_key(); - let p1_nonce = musig.gen_nonces(&keypair1.sk, &agg_key, b"test"); - let p2_nonce = musig.gen_nonces(&keypair2.sk, &agg_key2, b"test"); - let p3_nonce = musig.gen_nonces(&keypair3.sk, &agg_key3, b"test"); + let p1_nonce = musig.gen_nonces(keypair1.secret_key(), &agg_key, b"test"); + let p2_nonce = musig.gen_nonces(keypair2.secret_key(), &agg_key2, b"test"); + let p3_nonce = musig.gen_nonces(keypair3.secret_key(), &agg_key3, b"test"); let nonces = vec![p1_nonce.public, p2_nonce.public, p3_nonce.public]; let message = Message::::plain("test", b"Chancellor on brink of second bailout for banks"); @@ -853,440 +885,440 @@ mod test { } } - #[test] - fn test_key_agg() { - let X1 = XOnly::from_bytes([ - 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, - 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, - 0xBC, 0xE0, 0x36, 0xF9, - ]) - .unwrap(); - let X2 = XOnly::from_bytes([ - 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, - 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, - 0x50, 0x2B, 0xA6, 0x59, - ]) - .unwrap(); - let X3 = XOnly::from_bytes([ - 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, 0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80, - 0xA8, 0xE3, 0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7, 0xAD, 0x33, 0x83, 0x68, - 0xD0, 0x38, 0xCA, 0x66, - ]) - .unwrap(); - let X = vec![X1, X2, X3]; - - let expected: Vec = vec![ - XOnly::from_bytes([ - 0xE5, 0x83, 0x01, 0x40, 0x51, 0x21, 0x95, 0xD7, 0x4C, 0x83, 0x07, 0xE3, 0x96, 0x37, - 0xCB, 0xE5, 0xFB, 0x73, 0x0E, 0xBE, 0xAB, 0x80, 0xEC, 0x51, 0x4C, 0xF8, 0x8A, 0x87, - 0x7C, 0xEE, 0xEE, 0x0B, - ]) - .unwrap(), - XOnly::from_bytes([ - 0xD7, 0x0C, 0xD6, 0x9A, 0x26, 0x47, 0xF7, 0x39, 0x09, 0x73, 0xDF, 0x48, 0xCB, 0xFA, - 0x2C, 0xCC, 0x40, 0x7B, 0x8B, 0x2D, 0x60, 0xB0, 0x8C, 0x5F, 0x16, 0x41, 0x18, 0x5C, - 0x79, 0x98, 0xA2, 0x90, - ]) - .unwrap(), - XOnly::from_bytes([ - 0x81, 0xA8, 0xB0, 0x93, 0x91, 0x2C, 0x9E, 0x48, 0x14, 0x08, 0xD0, 0x97, 0x76, 0xCE, - 0xFB, 0x48, 0xAE, 0xB8, 0xB6, 0x54, 0x81, 0xB6, 0xBA, 0xAF, 0xB3, 0xC5, 0x81, 0x01, - 0x06, 0x71, 0x7B, 0xEB, - ]) - .unwrap(), - XOnly::from_bytes([ - 0x2E, 0xB1, 0x88, 0x51, 0x88, 0x7E, 0x7B, 0xDC, 0x5E, 0x83, 0x0E, 0x89, 0xB1, 0x9D, - 0xDB, 0xC2, 0x80, 0x78, 0xF1, 0xFA, 0x88, 0xAA, 0xD0, 0xAD, 0x01, 0xCA, 0x06, 0xFE, - 0x4F, 0x80, 0x21, 0x0B, - ]) - .unwrap(), - ]; - - let musig = MuSig::>>::default(); - assert_eq!( - musig - .new_agg_key(vec![X[0], X[1], X[2]]) - .into_bip340_key() - .agg_public_key(), - expected[0] - ); - assert_eq!( - musig - .new_agg_key(vec![X[2], X[1], X[0]]) - .into_bip340_key() - .agg_public_key(), - expected[1] - ); - assert_eq!( - musig - .new_agg_key(vec![X[0], X[0], X[0]]) - .into_bip340_key() - .agg_public_key(), - expected[2] - ); - assert_eq!( - musig - .new_agg_key(vec![X[0], X[0], X[1], X[1]]) - .into_bip340_key() - .agg_public_key(), - expected[3] - ); - } - - #[test] - fn test_sign_vectors() { - let X1 = XOnly::from_bytes([ - 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, - 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, - 0xBC, 0xE0, 0x36, 0xF9, - ]) - .unwrap(); - let X2 = XOnly::from_bytes([ - 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, - 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, - 0x50, 0x2B, 0xA6, 0x59, - ]) - .unwrap(); - - let sec_nonce = NonceKeyPair::from_bytes([ - 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, - 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, - 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, - 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, - 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, - ]) - .unwrap(); - - let agg_pubnonce = Nonce::from_bytes([ - 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, - 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, - 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, - 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, - 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9, - ]) - .unwrap(); - - let sk = Scalar::from_bytes([ - 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, - 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, - 0xD1, 0x00, 0x76, 0x71, - ]) - .unwrap() - .mark::() - .unwrap(); - - let msg = [ - 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, - 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, - 0xDD, 0xF3, 0xC0, 0xCF, - ]; - - let expected: Vec = vec![ - Scalar::from_bytes([ - 0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E, - 0x90, 0xC2, 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12, - 0x40, 0x3B, 0x4D, 0x3B, - ]) - .unwrap() - .mark::() - .unwrap(), - Scalar::from_bytes([ - 0x2D, 0xF6, 0x7B, 0xFF, 0xF1, 0x8E, 0x3D, 0xE7, 0x97, 0xE1, 0x3C, 0x64, 0x75, 0xC9, - 0x63, 0x04, 0x81, 0x38, 0xDA, 0xEC, 0x5C, 0xB2, 0x0A, 0x35, 0x7C, 0xEC, 0xA7, 0xC8, - 0x42, 0x42, 0x95, 0xEA, - ]) - .unwrap() - .mark::() - .unwrap(), - Scalar::from_bytes([ - 0x0D, 0x5B, 0x65, 0x1E, 0x6D, 0xE3, 0x4A, 0x29, 0xA1, 0x2D, 0xE7, 0xA8, 0xB4, 0x18, - 0x3B, 0x4A, 0xE6, 0xA7, 0xF7, 0xFB, 0xE1, 0x5C, 0xDC, 0xAF, 0xA4, 0xA3, 0xD1, 0xBC, - 0xAA, 0xBC, 0x75, 0x17, - ]) - .unwrap() - .mark::() - .unwrap(), - ]; - - let musig = MuSig::>>::default(); - let keypair = musig.schnorr.new_keypair(sk); - - let (remote_nonce1, remote_nonce2) = ( - agg_pubnonce, - Nonce([-sec_nonce.public.0[0], -sec_nonce.public.0[1]]), - ); - let message = Message::::raw(&msg); - { - let agg_key = musig - .new_agg_key(vec![keypair.pk, X1, X2]) - .into_bip340_key(); - - let sign_session = musig - .start_sign_session( - &agg_key, - vec![ - sec_nonce.public(), - remote_nonce1.clone(), - remote_nonce2.clone(), - ], - message, - ) - .unwrap(); - let sig = musig.sign(&agg_key, 0, &keypair, sec_nonce.clone(), &sign_session); - assert_eq!(sig, expected[0]); - } - - { - let agg_key = musig - .new_agg_key(vec![X1, keypair.pk, X2]) - .into_bip340_key(); - let sign_session = musig - .start_sign_session( - &agg_key, - vec![ - remote_nonce1.clone(), - sec_nonce.public(), - remote_nonce2.clone(), - ], - message, - ) - .unwrap(); - let sig = musig.sign(&agg_key, 1, &keypair, sec_nonce.clone(), &sign_session); - assert_eq!(sig, expected[1]); - } - - { - let agg_key = musig - .new_agg_key(vec![X1, X2, keypair.pk]) - .into_bip340_key(); - let sign_session = musig - .start_sign_session( - &agg_key, - vec![ - remote_nonce1.clone(), - remote_nonce2.clone(), - sec_nonce.public(), - ], - message, - ) - .unwrap(); - let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); - assert_eq!(sig, expected[2]); - } - } - - #[test] - fn test_tweak_vectors() { - let X1 = XOnly::from_bytes([ - 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, - 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, - 0xBC, 0xE0, 0x36, 0xF9, - ]) - .unwrap(); - let X2 = XOnly::from_bytes([ - 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, - 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, - 0x50, 0x2B, 0xA6, 0x59, - ]) - .unwrap(); - - let sec_nonce = NonceKeyPair::from_bytes([ - 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, - 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, - 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, - 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, - 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, - ]) - .unwrap(); - - let agg_pubnonce = Nonce::from_bytes([ - 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, - 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, - 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, - 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, - 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9, - ]) - .unwrap(); - - let sk = Scalar::from_bytes([ - 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, - 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, - 0xD1, 0x00, 0x76, 0x71, - ]) - .unwrap() - .mark::() - .unwrap(); - - let tweaks: Vec = vec![ - Scalar::from_bytes([ - 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, - 0x72, 0x3D, 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, 0x80, 0x2B, 0x26, 0x3C, - 0xDF, 0xCD, 0x83, 0xBB, - ]) - .unwrap() - .mark::() - .unwrap(), - Scalar::from_bytes([ - 0xAE, 0x2E, 0xA7, 0x97, 0xCC, 0x0F, 0xE7, 0x2A, 0xC5, 0xB9, 0x7B, 0x97, 0xF3, 0xC6, - 0x95, 0x7D, 0x7E, 0x41, 0x99, 0xA1, 0x67, 0xA5, 0x8E, 0xB0, 0x8B, 0xCA, 0xFF, 0xDA, - 0x70, 0xAC, 0x04, 0x55, - ]) - .unwrap() - .mark::() - .unwrap(), - Scalar::from_bytes([ - 0xF5, 0x2E, 0xCB, 0xC5, 0x65, 0xB3, 0xD8, 0xBE, 0xA2, 0xDF, 0xD5, 0xB7, 0x5A, 0x4F, - 0x45, 0x7E, 0x54, 0x36, 0x98, 0x09, 0x32, 0x2E, 0x41, 0x20, 0x83, 0x16, 0x26, 0xF2, - 0x90, 0xFA, 0x87, 0xE0, - ]) - .unwrap() - .mark::() - .unwrap(), - Scalar::from_bytes([ - 0x19, 0x69, 0xAD, 0x73, 0xCC, 0x17, 0x7F, 0xA0, 0xB4, 0xFC, 0xED, 0x6D, 0xF1, 0xF7, - 0xBF, 0x99, 0x07, 0xE6, 0x65, 0xFD, 0xE9, 0xBA, 0x19, 0x6A, 0x74, 0xFE, 0xD0, 0xA3, - 0xCF, 0x5A, 0xEF, 0x9D, - ]) - .unwrap() - .mark::() - .unwrap(), - ]; - let msg = [ - 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, - 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, - 0xDD, 0xF3, 0xC0, 0xCF, - ]; - - let expected: Vec = vec![ - Scalar::from_bytes([ - 0x5E, 0x24, 0xC7, 0x49, 0x6B, 0x56, 0x5D, 0xEB, 0xC3, 0xB9, 0x63, 0x9E, 0x6F, 0x13, - 0x04, 0xA2, 0x15, 0x97, 0xF9, 0x60, 0x3D, 0x3A, 0xB0, 0x5B, 0x49, 0x13, 0x64, 0x17, - 0x75, 0xE1, 0x37, 0x5B, - ]) - .unwrap() - .mark::() - .unwrap(), - Scalar::from_bytes([ - 0x78, 0x40, 0x8D, 0xDC, 0xAB, 0x48, 0x13, 0xD1, 0x39, 0x4C, 0x97, 0xD4, 0x93, 0xEF, - 0x10, 0x84, 0x19, 0x5C, 0x1D, 0x4B, 0x52, 0xE6, 0x3E, 0xCD, 0x7B, 0xC5, 0x99, 0x16, - 0x44, 0xE4, 0x4D, 0xDD, - ]) - .unwrap() - .mark::() - .unwrap(), - Scalar::from_bytes([ - 0xC3, 0xA8, 0x29, 0xA8, 0x14, 0x80, 0xE3, 0x6E, 0xC3, 0xAB, 0x05, 0x29, 0x64, 0x50, - 0x9A, 0x94, 0xEB, 0xF3, 0x42, 0x10, 0x40, 0x3D, 0x16, 0xB2, 0x26, 0xA6, 0xF1, 0x6E, - 0xC8, 0x5B, 0x73, 0x57, - ]) - .unwrap() - .mark::() - .unwrap(), - Scalar::from_bytes([ - 0x8C, 0x44, 0x73, 0xC6, 0xA3, 0x82, 0xBD, 0x3C, 0x4A, 0xD7, 0xBE, 0x59, 0x81, 0x8D, - 0xA5, 0xED, 0x7C, 0xF8, 0xCE, 0xC4, 0xBC, 0x21, 0x99, 0x6C, 0xFD, 0xA0, 0x8B, 0xB4, - 0x31, 0x6B, 0x8B, 0xC7, - ]) - .unwrap() - .mark::() - .unwrap(), - ]; - - let musig = MuSig::>>::default(); - let keypair = musig.schnorr.new_keypair(sk); - - let (remote_nonce1, remote_nonce2) = ( - agg_pubnonce, - Nonce([-sec_nonce.public.0[0], -sec_nonce.public.0[1]]), - ); - let message = Message::::raw(&msg); - - { - let agg_key = musig - .new_agg_key(vec![X1, X2, keypair.pk]) - .into_bip340_key() - .tweak(tweaks[0].clone()) - .unwrap(); - let sign_session = musig - .start_sign_session( - &agg_key, - vec![ - remote_nonce1.clone(), - remote_nonce2.clone(), - sec_nonce.public(), - ], - message, - ) - .unwrap(); - let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); - assert_eq!(sig, expected[0]); - } - - { - let agg_key = musig - .new_agg_key(vec![X1, X2, keypair.pk]) - .into_bip340_key() - .tweak(tweaks[0].clone()) - .unwrap(); - let sign_session = musig - .start_sign_session( - &agg_key, - vec![ - remote_nonce1.clone(), - remote_nonce2.clone(), - sec_nonce.public(), - ], - message, - ) - .unwrap(); - let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); - assert_eq!(sig, expected[0]); - } - - { - let agg_key = musig - .new_agg_key(vec![X1, X2, keypair.pk]) - .tweak(tweaks[0].clone()) - .unwrap() - .into_bip340_key() - .tweak(tweaks[1].clone()) - .unwrap(); - - let sign_session = musig - .start_sign_session( - &agg_key, - vec![ - remote_nonce1.clone(), - remote_nonce2.clone(), - sec_nonce.public(), - ], - message, - ) - .unwrap(); - let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); - assert_eq!(sig, expected[2]); - } - - // { - // let mut agg_key = musig.new_agg_key(vec![X1, X2, keypair.pk]); - // agg_key = agg_key.tweak(tweaks[0].clone(), true).unwrap(); - // agg_key = agg_key.tweak(tweaks[1].clone(), false).unwrap(); - // agg_key = agg_key.tweak(tweaks[2].clone(), true).unwrap(); - // agg_key = agg_key.tweak(tweaks[3].clone(), false).unwrap(); - - // let sign_session = musig - // .start_sign_session( - // &agg_key, - // vec![ - // remote_nonce1.clone(), - // remote_nonce2.clone(), - // sec_nonce.public(), - // ], - // message, - // ) - // .unwrap(); - // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); - // assert_eq!(sig, expected[3]); - // } - } + // #[test] + // fn test_key_agg() { + // let X1 = XOnly::from_bytes([ + // 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, + // 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, + // 0xBC, 0xE0, 0x36, 0xF9, + // ]) + // .unwrap(); + // let X2 = XOnly::from_bytes([ + // 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, + // 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, + // 0x50, 0x2B, 0xA6, 0x59, + // ]) + // .unwrap(); + // let X3 = XOnly::from_bytes([ + // 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, 0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80, + // 0xA8, 0xE3, 0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7, 0xAD, 0x33, 0x83, 0x68, + // 0xD0, 0x38, 0xCA, 0x66, + // ]) + // .unwrap(); + // let X = vec![X1, X2, X3]; + + // let expected: Vec = vec![ + // XOnly::from_bytes([ + // 0xE5, 0x83, 0x01, 0x40, 0x51, 0x21, 0x95, 0xD7, 0x4C, 0x83, 0x07, 0xE3, 0x96, 0x37, + // 0xCB, 0xE5, 0xFB, 0x73, 0x0E, 0xBE, 0xAB, 0x80, 0xEC, 0x51, 0x4C, 0xF8, 0x8A, 0x87, + // 0x7C, 0xEE, 0xEE, 0x0B, + // ]) + // .unwrap(), + // XOnly::from_bytes([ + // 0xD7, 0x0C, 0xD6, 0x9A, 0x26, 0x47, 0xF7, 0x39, 0x09, 0x73, 0xDF, 0x48, 0xCB, 0xFA, + // 0x2C, 0xCC, 0x40, 0x7B, 0x8B, 0x2D, 0x60, 0xB0, 0x8C, 0x5F, 0x16, 0x41, 0x18, 0x5C, + // 0x79, 0x98, 0xA2, 0x90, + // ]) + // .unwrap(), + // XOnly::from_bytes([ + // 0x81, 0xA8, 0xB0, 0x93, 0x91, 0x2C, 0x9E, 0x48, 0x14, 0x08, 0xD0, 0x97, 0x76, 0xCE, + // 0xFB, 0x48, 0xAE, 0xB8, 0xB6, 0x54, 0x81, 0xB6, 0xBA, 0xAF, 0xB3, 0xC5, 0x81, 0x01, + // 0x06, 0x71, 0x7B, 0xEB, + // ]) + // .unwrap(), + // XOnly::from_bytes([ + // 0x2E, 0xB1, 0x88, 0x51, 0x88, 0x7E, 0x7B, 0xDC, 0x5E, 0x83, 0x0E, 0x89, 0xB1, 0x9D, + // 0xDB, 0xC2, 0x80, 0x78, 0xF1, 0xFA, 0x88, 0xAA, 0xD0, 0xAD, 0x01, 0xCA, 0x06, 0xFE, + // 0x4F, 0x80, 0x21, 0x0B, + // ]) + // .unwrap(), + // ]; + + // let musig = MuSig::>>::default(); + // assert_eq!( + // musig + // .new_agg_key(vec![X[0], X[1], X[2]]) + // .into_bip340_key() + // .agg_public_key(), + // expected[0] + // ); + // assert_eq!( + // musig + // .new_agg_key(vec![X[2], X[1], X[0]]) + // .into_bip340_key() + // .agg_public_key(), + // expected[1] + // ); + // assert_eq!( + // musig + // .new_agg_key(vec![X[0], X[0], X[0]]) + // .into_bip340_key() + // .agg_public_key(), + // expected[2] + // ); + // assert_eq!( + // musig + // .new_agg_key(vec![X[0], X[0], X[1], X[1]]) + // .into_bip340_key() + // .agg_public_key(), + // expected[3] + // ); + // } + + // #[test] + // fn test_sign_vectors() { + // let X1 = XOnly::from_bytes([ + // 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, + // 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, + // 0xBC, 0xE0, 0x36, 0xF9, + // ]) + // .unwrap(); + // let X2 = XOnly::from_bytes([ + // 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, + // 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, + // 0x50, 0x2B, 0xA6, 0x59, + // ]) + // .unwrap(); + + // let sec_nonce = NonceKeyPair::from_bytes([ + // 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, + // 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, + // 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, + // 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, + // 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, + // ]) + // .unwrap(); + + // let agg_pubnonce = Nonce::from_bytes([ + // 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, + // 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, + // 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, + // 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, + // 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9, + // ]) + // .unwrap(); + + // let sk = Scalar::from_bytes([ + // 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, + // 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, + // 0xD1, 0x00, 0x76, 0x71, + // ]) + // .unwrap() + // .mark::() + // .unwrap(); + + // let msg = [ + // 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, + // 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, + // 0xDD, 0xF3, 0xC0, 0xCF, + // ]; + + // let expected: Vec = vec![ + // Scalar::from_bytes([ + // 0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E, + // 0x90, 0xC2, 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12, + // 0x40, 0x3B, 0x4D, 0x3B, + // ]) + // .unwrap() + // .mark::() + // .unwrap(), + // Scalar::from_bytes([ + // 0x2D, 0xF6, 0x7B, 0xFF, 0xF1, 0x8E, 0x3D, 0xE7, 0x97, 0xE1, 0x3C, 0x64, 0x75, 0xC9, + // 0x63, 0x04, 0x81, 0x38, 0xDA, 0xEC, 0x5C, 0xB2, 0x0A, 0x35, 0x7C, 0xEC, 0xA7, 0xC8, + // 0x42, 0x42, 0x95, 0xEA, + // ]) + // .unwrap() + // .mark::() + // .unwrap(), + // Scalar::from_bytes([ + // 0x0D, 0x5B, 0x65, 0x1E, 0x6D, 0xE3, 0x4A, 0x29, 0xA1, 0x2D, 0xE7, 0xA8, 0xB4, 0x18, + // 0x3B, 0x4A, 0xE6, 0xA7, 0xF7, 0xFB, 0xE1, 0x5C, 0xDC, 0xAF, 0xA4, 0xA3, 0xD1, 0xBC, + // 0xAA, 0xBC, 0x75, 0x17, + // ]) + // .unwrap() + // .mark::() + // .unwrap(), + // ]; + + // let musig = MuSig::>>::default(); + // let keypair = musig.schnorr.new_keypair(sk); + + // let (remote_nonce1, remote_nonce2) = ( + // agg_pubnonce, + // Nonce([-sec_nonce.public.0[0], -sec_nonce.public.0[1]]), + // ); + // let message = Message::::raw(&msg); + // { + // let agg_key = musig + // .new_agg_key(vec![keypair.pk, X1, X2]) + // .into_bip340_key(); + + // let sign_session = musig + // .start_sign_session( + // &agg_key, + // vec![ + // sec_nonce.public(), + // remote_nonce1.clone(), + // remote_nonce2.clone(), + // ], + // message, + // ) + // .unwrap(); + // let sig = musig.sign(&agg_key, 0, &keypair, sec_nonce.clone(), &sign_session); + // assert_eq!(sig, expected[0]); + // } + + // { + // let agg_key = musig + // .new_agg_key(vec![X1, keypair.pk, X2]) + // .into_bip340_key(); + // let sign_session = musig + // .start_sign_session( + // &agg_key, + // vec![ + // remote_nonce1.clone(), + // sec_nonce.public(), + // remote_nonce2.clone(), + // ], + // message, + // ) + // .unwrap(); + // let sig = musig.sign(&agg_key, 1, &keypair, sec_nonce.clone(), &sign_session); + // assert_eq!(sig, expected[1]); + // } + + // { + // let agg_key = musig + // .new_agg_key(vec![X1, X2, keypair.pk]) + // .into_bip340_key(); + // let sign_session = musig + // .start_sign_session( + // &agg_key, + // vec![ + // remote_nonce1.clone(), + // remote_nonce2.clone(), + // sec_nonce.public(), + // ], + // message, + // ) + // .unwrap(); + // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); + // assert_eq!(sig, expected[2]); + // } + // } + + // #[test] + // fn test_tweak_vectors() { + // let X1 = XOnly::from_bytes([ + // 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, + // 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, + // 0xBC, 0xE0, 0x36, 0xF9, + // ]) + // .unwrap(); + // let X2 = XOnly::from_bytes([ + // 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, + // 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, + // 0x50, 0x2B, 0xA6, 0x59, + // ]) + // .unwrap(); + + // let sec_nonce = NonceKeyPair::from_bytes([ + // 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, + // 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, + // 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, + // 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, + // 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, + // ]) + // .unwrap(); + + // let agg_pubnonce = Nonce::from_bytes([ + // 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, + // 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, + // 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, + // 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, + // 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9, + // ]) + // .unwrap(); + + // let sk = Scalar::from_bytes([ + // 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, + // 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, + // 0xD1, 0x00, 0x76, 0x71, + // ]) + // .unwrap() + // .mark::() + // .unwrap(); + + // let tweaks: Vec = vec![ + // Scalar::from_bytes([ + // 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, + // 0x72, 0x3D, 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, 0x80, 0x2B, 0x26, 0x3C, + // 0xDF, 0xCD, 0x83, 0xBB, + // ]) + // .unwrap() + // .mark::() + // .unwrap(), + // Scalar::from_bytes([ + // 0xAE, 0x2E, 0xA7, 0x97, 0xCC, 0x0F, 0xE7, 0x2A, 0xC5, 0xB9, 0x7B, 0x97, 0xF3, 0xC6, + // 0x95, 0x7D, 0x7E, 0x41, 0x99, 0xA1, 0x67, 0xA5, 0x8E, 0xB0, 0x8B, 0xCA, 0xFF, 0xDA, + // 0x70, 0xAC, 0x04, 0x55, + // ]) + // .unwrap() + // .mark::() + // .unwrap(), + // Scalar::from_bytes([ + // 0xF5, 0x2E, 0xCB, 0xC5, 0x65, 0xB3, 0xD8, 0xBE, 0xA2, 0xDF, 0xD5, 0xB7, 0x5A, 0x4F, + // 0x45, 0x7E, 0x54, 0x36, 0x98, 0x09, 0x32, 0x2E, 0x41, 0x20, 0x83, 0x16, 0x26, 0xF2, + // 0x90, 0xFA, 0x87, 0xE0, + // ]) + // .unwrap() + // .mark::() + // .unwrap(), + // Scalar::from_bytes([ + // 0x19, 0x69, 0xAD, 0x73, 0xCC, 0x17, 0x7F, 0xA0, 0xB4, 0xFC, 0xED, 0x6D, 0xF1, 0xF7, + // 0xBF, 0x99, 0x07, 0xE6, 0x65, 0xFD, 0xE9, 0xBA, 0x19, 0x6A, 0x74, 0xFE, 0xD0, 0xA3, + // 0xCF, 0x5A, 0xEF, 0x9D, + // ]) + // .unwrap() + // .mark::() + // .unwrap(), + // ]; + // let msg = [ + // 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, + // 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, + // 0xDD, 0xF3, 0xC0, 0xCF, + // ]; + + // let expected: Vec = vec![ + // Scalar::from_bytes([ + // 0x5E, 0x24, 0xC7, 0x49, 0x6B, 0x56, 0x5D, 0xEB, 0xC3, 0xB9, 0x63, 0x9E, 0x6F, 0x13, + // 0x04, 0xA2, 0x15, 0x97, 0xF9, 0x60, 0x3D, 0x3A, 0xB0, 0x5B, 0x49, 0x13, 0x64, 0x17, + // 0x75, 0xE1, 0x37, 0x5B, + // ]) + // .unwrap() + // .mark::() + // .unwrap(), + // Scalar::from_bytes([ + // 0x78, 0x40, 0x8D, 0xDC, 0xAB, 0x48, 0x13, 0xD1, 0x39, 0x4C, 0x97, 0xD4, 0x93, 0xEF, + // 0x10, 0x84, 0x19, 0x5C, 0x1D, 0x4B, 0x52, 0xE6, 0x3E, 0xCD, 0x7B, 0xC5, 0x99, 0x16, + // 0x44, 0xE4, 0x4D, 0xDD, + // ]) + // .unwrap() + // .mark::() + // .unwrap(), + // Scalar::from_bytes([ + // 0xC3, 0xA8, 0x29, 0xA8, 0x14, 0x80, 0xE3, 0x6E, 0xC3, 0xAB, 0x05, 0x29, 0x64, 0x50, + // 0x9A, 0x94, 0xEB, 0xF3, 0x42, 0x10, 0x40, 0x3D, 0x16, 0xB2, 0x26, 0xA6, 0xF1, 0x6E, + // 0xC8, 0x5B, 0x73, 0x57, + // ]) + // .unwrap() + // .mark::() + // .unwrap(), + // Scalar::from_bytes([ + // 0x8C, 0x44, 0x73, 0xC6, 0xA3, 0x82, 0xBD, 0x3C, 0x4A, 0xD7, 0xBE, 0x59, 0x81, 0x8D, + // 0xA5, 0xED, 0x7C, 0xF8, 0xCE, 0xC4, 0xBC, 0x21, 0x99, 0x6C, 0xFD, 0xA0, 0x8B, 0xB4, + // 0x31, 0x6B, 0x8B, 0xC7, + // ]) + // .unwrap() + // .mark::() + // .unwrap(), + // ]; + + // let musig = MuSig::>>::default(); + // let keypair = musig.schnorr.new_keypair(sk); + + // let (remote_nonce1, remote_nonce2) = ( + // agg_pubnonce, + // Nonce([-sec_nonce.public.0[0], -sec_nonce.public.0[1]]), + // ); + // let message = Message::::raw(&msg); + + // { + // let agg_key = musig + // .new_agg_key(vec![X1, X2, keypair.pk]) + // .into_bip340_key() + // .tweak(tweaks[0].clone()) + // .unwrap(); + // let sign_session = musig + // .start_sign_session( + // &agg_key, + // vec![ + // remote_nonce1.clone(), + // remote_nonce2.clone(), + // sec_nonce.public(), + // ], + // message, + // ) + // .unwrap(); + // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); + // assert_eq!(sig, expected[0]); + // } + + // { + // let agg_key = musig + // .new_agg_key(vec![X1, X2, keypair.pk]) + // .into_bip340_key() + // .tweak(tweaks[0].clone()) + // .unwrap(); + // let sign_session = musig + // .start_sign_session( + // &agg_key, + // vec![ + // remote_nonce1.clone(), + // remote_nonce2.clone(), + // sec_nonce.public(), + // ], + // message, + // ) + // .unwrap(); + // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); + // assert_eq!(sig, expected[0]); + // } + + // { + // let agg_key = musig + // .new_agg_key(vec![X1, X2, keypair.pk]) + // .tweak(tweaks[0].clone()) + // .unwrap() + // .into_bip340_key() + // .tweak(tweaks[1].clone()) + // .unwrap(); + + // let sign_session = musig + // .start_sign_session( + // &agg_key, + // vec![ + // remote_nonce1.clone(), + // remote_nonce2.clone(), + // sec_nonce.public(), + // ], + // message, + // ) + // .unwrap(); + // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); + // assert_eq!(sig, expected[2]); + // } + + // // { + // // let mut agg_key = musig.new_agg_key(vec![X1, X2, keypair.pk]); + // // agg_key = agg_key.tweak(tweaks[0].clone(), true).unwrap(); + // // agg_key = agg_key.tweak(tweaks[1].clone(), false).unwrap(); + // // agg_key = agg_key.tweak(tweaks[2].clone(), true).unwrap(); + // // agg_key = agg_key.tweak(tweaks[3].clone(), false).unwrap(); + + // // let sign_session = musig + // // .start_sign_session( + // // &agg_key, + // // vec![ + // // remote_nonce1.clone(), + // // remote_nonce2.clone(), + // // sec_nonce.public(), + // // ], + // // message, + // // ) + // // .unwrap(); + // // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); + // // assert_eq!(sig, expected[3]); + // // } + // } } From ff2919fd819db7591b76e85c5335e6226880d6d5 Mon Sep 17 00:00:00 2001 From: LLFourn Date: Tue, 16 Aug 2022 15:00:48 +0800 Subject: [PATCH 5/8] Add keypair module to secp256kfun crate So other crates can use it. --- secp256kfun/src/keypair.rs | 152 +++++++++++++++++++++++++++++++++++++ secp256kfun/src/lib.rs | 2 + 2 files changed, 154 insertions(+) create mode 100644 secp256kfun/src/keypair.rs diff --git a/secp256kfun/src/keypair.rs b/secp256kfun/src/keypair.rs new file mode 100644 index 00000000..7dd77e02 --- /dev/null +++ b/secp256kfun/src/keypair.rs @@ -0,0 +1,152 @@ +use crate::{g, marker::*, Point, Scalar, XOnly, G}; +/// A secret and public key pair. +/// +/// The secret key is a [`Scalar`] and the public key is the [`Point`] resulting from multiplying the scalar by [`G`]. +/// +/// ``` +/// use secp256kfun::{KeyPair, Scalar}; +/// let my_secret_key = Scalar::random(&mut rand::thread_rng()); +/// let my_keypair = KeyPair::new(my_secret_key); +/// ``` +/// +/// [`Scalar`]: crate::Scalar +/// [`G`]: crate::G +/// [`Point`]: crate::Point +#[derive(Clone, Debug, PartialEq)] +pub struct KeyPair { + sk: Scalar, + pk: Point, +} + +impl KeyPair { + /// Create a new `KeyPair` from a `secret_key`. + pub fn new(secret_key: Scalar) -> Self { + Self { + pk: g!(secret_key * G).normalize(), + sk: secret_key, + } + } + + /// Returns a reference to the secret key. + pub fn secret_key(&self) -> &Scalar { + &self.sk + } + + /// The public key + pub fn public_key(&self) -> Point { + self.pk + } + + /// Gets a reference to the keypair as a tuple + /// + /// # Example + /// ``` + /// use secp256kfun::{KeyPair, Scalar}; + /// let keypair = KeyPair::new(Scalar::random(&mut rand::thread_rng())); + /// let (secret_key, public_key) = keypair.as_tuple(); + pub fn as_tuple(&self) -> (&Scalar, Point) { + (&self.sk, self.pk) + } +} + +/// A secret and public key pair where the public key is an [`XOnly`]. +/// +/// [`XOnly`]: crate::XOnly +/// [`Scalar`]: crate::Scalar +#[derive(Clone, Debug, PartialEq)] +pub struct XOnlyKeyPair { + sk: Scalar, + pk: XOnly, +} + +impl XOnlyKeyPair { + /// Converts a non-zero scalar to a keypair by interpreting it as a secret key, generating + /// the corresponding public key by multiplying it by [`G`] and dropping the y-coordinate. + /// + /// **The secret key in the resulting keypair is not guaranteed to be the same + /// as the input**. For half the input values the result will be the + /// negation of it. This happens because the corresponding [`Point`] may not + /// have an y-coordinate that is even (see [`EvenY`]) + /// + /// # Example + /// ``` + /// use secp256kfun::{g, s, Scalar, XOnlyKeyPair, G}; + /// + /// let original_secret_key = Scalar::random(&mut rand::thread_rng()); + /// let keypair = XOnlyKeyPair::new(original_secret_key.clone()); + /// + /// assert!( + /// &original_secret_key == keypair.secret_key() + /// || &-original_secret_key == keypair.secret_key() + /// ); + /// assert!(g!({ keypair.secret_key() } * G).normalize().is_y_even()); + /// assert_eq!( + /// g!({ keypair.secret_key() } * G), + /// keypair.public_key().to_point() + /// ); + /// ``` + /// + /// [`Point`]: crate::Point + /// [`EvenY`]: crate::marker::EvenY + pub fn new(mut secret_key: Scalar) -> Self { + let pk = XOnly::from_scalar_mul(&G, &mut secret_key); + Self { sk: secret_key, pk } + } + + /// Returns a reference to the secret key. + /// + /// The secret key will always correspond to a point with an even y-coordinate when multiplied + /// by [`G`] (regardless of what was passed into [`XOnlyKeyPair::new`]). + pub fn secret_key(&self) -> &Scalar { + &self.sk + } + + /// The public key as an `XOnly` point. + pub fn public_key(&self) -> XOnly { + self.pk + } + + /// Gets a reference to the keypair as a tuple + /// + /// # Example + /// ``` + /// use secp256kfun::{XOnlyKeyPair, Scalar}; + /// let keypair = XOnlyKeyPair::new(Scalar::random(&mut rand::thread_rng())); + /// let (secret_key, public_key) = keypair.as_tuple(); + pub fn as_tuple(&self) -> (&Scalar, XOnly) { + (&self.sk, self.pk) + } + + /// Deprecated + #[deprecated(note = "use .public_key().to_point() instead")] + pub fn verification_key(&self) -> Point { + self.public_key().to_point() + } +} + +impl From for (Scalar, XOnly) { + fn from(kp: XOnlyKeyPair) -> Self { + (kp.sk, kp.pk) + } +} + +impl From for KeyPair { + fn from(xonly: XOnlyKeyPair) -> Self { + Self { + sk: xonly.sk, + pk: xonly.pk.to_point().mark::(), + } + } +} + +impl From for XOnlyKeyPair { + fn from(kp: KeyPair) -> Self { + let mut sk = kp.sk; + let (pk, needs_negation) = kp.pk.into_point_with_even_y(); + sk.conditional_negate(needs_negation); + Self { + sk, + pk: pk.to_xonly(), + } + } +} diff --git a/secp256kfun/src/lib.rs b/secp256kfun/src/lib.rs index 28fb4928..d68411c0 100755 --- a/secp256kfun/src/lib.rs +++ b/secp256kfun/src/lib.rs @@ -22,6 +22,7 @@ pub use digest; pub use rand_core; pub use subtle; +mod keypair; mod point; mod scalar; mod slice; @@ -33,6 +34,7 @@ mod backend; pub mod marker; pub mod op; +pub use keypair::*; pub use point::Point; pub use scalar::Scalar; pub use slice::Slice; From 54c59d8eeee587a03e5da36f32163f2eba07a73f Mon Sep 17 00:00:00 2001 From: LLFourn Date: Tue, 16 Aug 2022 15:06:41 +0800 Subject: [PATCH 6/8] [musig] Pass the test vectors from spec PR PR is here: https://github.com/jonasnick/bips/pull/37 Notes: - It required allowing for binonce::Nonces with zero points - We now do the "replace by G" if the agg nonce is infinity from the spec - I replaced the keypair types with the new ones from main lib - We can't pass all the test vectors because we don't have the same APIs --- schnorr_fun/Cargo.toml | 1 + schnorr_fun/README.md | 4 +- schnorr_fun/benches/bench_schnorr.rs | 2 +- schnorr_fun/src/adaptor/mod.rs | 14 +- schnorr_fun/src/binonce.rs | 77 +- schnorr_fun/src/keypair.rs | 72 -- schnorr_fun/src/lib.rs | 4 +- schnorr_fun/src/musig.rs | 682 ++++-------------- schnorr_fun/src/schnorr.rs | 30 +- ...st-vectors.csv => bip340-test-vectors.csv} | 0 schnorr_fun/tests/bip340.rs | 2 +- schnorr_fun/tests/musig/key_agg_vectors.json | 88 +++ .../tests/musig/nonce_agg_vectors.json | 51 ++ .../tests/musig/nonce_gen_vectors.json | 28 + schnorr_fun/tests/musig/sig_agg_vectors.json | 86 +++ .../tests/musig/sign_verify_vectors.json | 172 +++++ schnorr_fun/tests/musig/tweak_vectors.json | 75 ++ schnorr_fun/tests/musig_keyagg.rs | 99 +++ schnorr_fun/tests/musig_sign_verify.rs | 173 +++++ schnorr_fun/tests/musig_tweak.rs | 141 ++++ 20 files changed, 1133 insertions(+), 668 deletions(-) delete mode 100644 schnorr_fun/src/keypair.rs rename schnorr_fun/tests/{test-vectors.csv => bip340-test-vectors.csv} (100%) create mode 100644 schnorr_fun/tests/musig/key_agg_vectors.json create mode 100644 schnorr_fun/tests/musig/nonce_agg_vectors.json create mode 100644 schnorr_fun/tests/musig/nonce_gen_vectors.json create mode 100644 schnorr_fun/tests/musig/sig_agg_vectors.json create mode 100644 schnorr_fun/tests/musig/sign_verify_vectors.json create mode 100644 schnorr_fun/tests/musig/tweak_vectors.json create mode 100644 schnorr_fun/tests/musig_keyagg.rs create mode 100644 schnorr_fun/tests/musig_sign_verify.rs create mode 100644 schnorr_fun/tests/musig_tweak.rs diff --git a/schnorr_fun/Cargo.toml b/schnorr_fun/Cargo.toml index 6d8d2e1d..b8f23efa 100644 --- a/schnorr_fun/Cargo.toml +++ b/schnorr_fun/Cargo.toml @@ -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] diff --git a/schnorr_fun/README.md b/schnorr_fun/README.md index a3091b27..aaa1f7cd 100644 --- a/schnorr_fun/README.md +++ b/schnorr_fun/README.md @@ -43,7 +43,7 @@ let message = Message::::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)); ``` @@ -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. diff --git a/schnorr_fun/benches/bench_schnorr.rs b/schnorr_fun/benches/bench_schnorr.rs index 67d2a91e..d8f76f78 100755 --- a/schnorr_fun/benches/bench_schnorr.rs +++ b/schnorr_fun/benches/bench_schnorr.rs @@ -41,7 +41,7 @@ fn verify_schnorr(c: &mut Criterion) { { let message = Message::::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)) }); diff --git a/schnorr_fun/src/adaptor/mod.rs b/schnorr_fun/src/adaptor/mod.rs index ba9893b7..d4eaa4fa 100644 --- a/schnorr_fun/src/adaptor/mod.rs +++ b/schnorr_fun/src/adaptor/mod.rs @@ -20,7 +20,7 @@ //! let nonce_gen = nonce::Synthetic::>::default(); //! let schnorr = Schnorr::::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::::plain("text-bitcoin", b"send 1 BTC to Bob"); @@ -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; @@ -72,7 +72,7 @@ pub trait EncryptedSign { /// [synopsis]: crate::adaptor#synopsis fn encrypted_sign( &self, - signing_keypair: &KeyPair, + signing_keypair: &XOnlyKeyPair, encryption_key: &Point, message: Message<'_, impl Secrecy>, ) -> EncryptedSignature; @@ -85,7 +85,7 @@ where { fn encrypted_sign( &self, - signing_key: &KeyPair, + signing_key: &XOnlyKeyPair, encryption_key: &Point, message: Message<'_, impl Secrecy>, ) -> EncryptedSignature { @@ -299,7 +299,7 @@ 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::::plain("test", b"give 100 coins to Bob".as_ref()); @@ -307,7 +307,7 @@ mod test { schnorr.encrypted_sign(&signing_keypair, &encryption_key, message); assert!(schnorr.verify_encrypted_signature( - &signing_keypair.verification_key(), + &verification_key, &encryption_key, message, &encrypted_signature, diff --git a/schnorr_fun/src/binonce.rs b/schnorr_fun/src/binonce.rs index 9fe5f737..3fa57b40 100644 --- a/schnorr_fun/src/binonce.rs +++ b/schnorr_fun/src/binonce.rs @@ -7,10 +7,58 @@ use secp256kfun::{g, marker::*, 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(pub [Point; 2]); + +impl Nonce { + /// 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 { + fn deser(bytes: &[u8]) -> Option> { + Point::from_slice(&bytes) + .map(|p| p.mark::()) + .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])) + } + + /// 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::() + .map(|p| p.to_bytes()) + .unwrap_or([0u8; 33]), + ); + bytes[33..].copy_from_slice( + &self.0[1] + .mark::() + .map(|p| p.to_bytes()) + .unwrap_or([0u8; 33]), + ); + bytes + } +} -impl Nonce { +impl Nonce { /// Reads the pair of nonces from 66 bytes (two 33-byte serialized points). pub fn from_bytes(bytes: [u8; 66]) -> Option { let R1 = Point::from_slice(&bytes[..33])?; @@ -25,7 +73,9 @@ impl Nonce { bytes[33..].copy_from_slice(self.0[1].to_bytes().as_ref()); bytes } +} +impl Nonce { /// Negate the two nonces pub fn conditional_negate(&mut self, needs_negation: bool) { self.0[0] = self.0[0].conditional_negate(needs_negation); @@ -35,13 +85,26 @@ impl Nonce { secp256kfun::impl_fromstr_deserialize! { name => "public nonce pair", - fn from_bytes(bytes: [u8;66]) -> Option { - Nonce::from_bytes(bytes) + fn from_bytes(bytes: [u8;66]) -> Option> { + Nonce::::from_bytes(bytes) + } +} + +secp256kfun::impl_display_serialize! { + fn to_bytes(nonce: &Nonce) -> [u8;66] { + nonce.to_bytes() + } +} + +secp256kfun::impl_fromstr_deserialize! { + name => "public nonce pair", + fn from_bytes(bytes: [u8;66]) -> Option> { + Nonce::::from_bytes(bytes) } } secp256kfun::impl_display_serialize! { - fn to_bytes(nonce: &Nonce) -> [u8;66] { + fn to_bytes(nonce: &Nonce) -> [u8;66] { nonce.to_bytes() } } @@ -54,7 +117,7 @@ secp256kfun::impl_display_serialize! { #[derive(Debug, Clone, PartialEq)] pub struct NonceKeyPair { /// The public nonce - pub public: Nonce, + pub public: Nonce, /// The secret nonce pub secret: [Scalar; 2], } @@ -97,7 +160,7 @@ impl NonceKeyPair { } /// Get the public portion of the nonce key pair (share this!) - pub fn public(&self) -> Nonce { + pub fn public(&self) -> Nonce { self.public } } diff --git a/schnorr_fun/src/keypair.rs b/schnorr_fun/src/keypair.rs deleted file mode 100644 index 6693ce87..00000000 --- a/schnorr_fun/src/keypair.rs +++ /dev/null @@ -1,72 +0,0 @@ -use secp256kfun::{marker::*, Point, Scalar, XOnly}; - -/// A secret and public key-pair for generating Schnorr signatures. -/// -/// The `KeyPair` struct is exists because it is more efficient to pre-compute the public key and -/// pass it in rather pass it in when signing with the same key multiple times. -/// -/// Create a `KeyPair` from a [`Schnorr`] instance. -/// -/// ``` -/// # use schnorr_fun::{fun::Scalar, Schnorr}; -/// # let schnorr = schnorr_fun::test_instance!(); -/// let my_secret_key = Scalar::random(&mut rand::thread_rng()); -/// let my_keypair = schnorr.new_keypair(my_secret_key); -/// ``` -/// -/// [`Schnorr`]: crate::Schnorr -#[derive(Clone, Debug, PartialEq)] -pub struct KeyPair { - pub(crate) sk: Scalar, - pub(crate) pk: XOnly, -} - -impl KeyPair { - /// Returns a reference to the secret key. - pub fn secret_key(&self) -> &Scalar { - &self.sk - } - - /// The public key - pub fn public_key(&self) -> XOnly { - self.pk - } - - /// Gets a reference to the key-pair as a tuple - /// - /// # Example - /// ``` - /// # use schnorr_fun::{Schnorr, fun::Scalar}; - /// # let keypair = schnorr_fun::test_instance!().new_keypair(Scalar::one()); - /// let (secret_key, public_key) = keypair.as_tuple(); - pub fn as_tuple(&self) -> (&Scalar, XOnly) { - (&self.sk, self.pk) - } - - /// Returns the full `Point` for the public key which is used in [`verify`]. - /// - /// This is just a descriptive short version of: - /// - /// ``` - /// # use schnorr_fun::{fun::Scalar, Schnorr}; - /// # let keypair = schnorr_fun::test_instance!().new_keypair(Scalar::random(&mut rand::thread_rng())); - /// let verification_key = keypair.public_key().to_point(); - /// # assert_eq!(keypair.verification_key(), keypair.public_key().to_point()) - /// ``` - /// [`verify`]: crate::Schnorr::verify - pub fn verification_key(&self) -> Point { - self.pk.to_point() - } -} - -impl From for (Scalar, XOnly) { - fn from(kp: KeyPair) -> Self { - (kp.sk, kp.pk) - } -} - -impl AsRef for KeyPair { - fn as_ref(&self) -> &XOnly { - &self.pk - } -} diff --git a/schnorr_fun/src/lib.rs b/schnorr_fun/src/lib.rs index ff4b0000..2efcba2a 100755 --- a/schnorr_fun/src/lib.rs +++ b/schnorr_fun/src/lib.rs @@ -23,7 +23,7 @@ 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; @@ -31,8 +31,6 @@ 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; diff --git a/schnorr_fun/src/musig.rs b/schnorr_fun/src/musig.rs index 9dd43293..4f4b521b 100644 --- a/schnorr_fun/src/musig.rs +++ b/schnorr_fun/src/musig.rs @@ -3,10 +3,12 @@ //! ## Synopsis //! //! ``` -//! use schnorr_fun::{musig::MuSig, nonce::Deterministic, Message, Schnorr}; +//! use schnorr_fun::{musig, nonce::Deterministic, Message, Schnorr}; //! use sha2::Sha256; -//! // use sha256 with deterministic nonce generation -//! let musig = MuSig::>>::default(); +//! // use sha256 with deterministic nonce generation -- be careful! +//! let musig = musig::new_with_deterministic_nonces::(); +//! // use synthetic nonces with randomness from ThredRng -- harder to make a mistake. +//! let musig = musig::new_with_synthetic_nonces::(); //! // create a keypair //! use schnorr_fun::fun::Scalar; //! let my_keypair = musig.new_keypair(Scalar::random(&mut rand::thread_rng())); @@ -18,7 +20,7 @@ //! // recieve the public keys of all other participants to form the aggregate key. //! let agg_key = musig //! .new_agg_key(vec![public_key1, public_key2, public_key3]) -//! .into_bip340_key(); +//! .into_xonly_key(); //! //! // create a unique nonce, and send the public nonce to other parties. //! let my_nonce = musig.gen_nonces(my_keypair.secret_key(), &agg_key, b"session-id-1337"); @@ -31,11 +33,11 @@ //! let nonces = vec![my_public_nonce, p2_public_nonce, p3_public_nonce]; //! let message = Message::plain("my-app", b"chancellor on brink of second bailout for banks"); //! // start the signing session -//! let session = musig.start_sign_session(&agg_key, nonces, message).unwrap(); +//! let session = musig.start_sign_session(&agg_key, nonces, message); //! // sign with our single local keypair -//! let my_sig = musig.sign(&agg_key, 0, &my_keypair, my_nonce, &session); -//! # let p2_sig = musig.sign(&agg_key, 1, &kp2, p2_nonce, &session); -//! # let p3_sig = musig.sign(&agg_key, 2, &kp3, p3_nonce, &session); +//! let my_sig = musig.sign(&agg_key, &session, 0, &my_keypair, my_nonce); +//! # let p2_sig = musig.sign(&agg_key, &session, 1, &kp2, p2_nonce); +//! # let p3_sig = musig.sign(&agg_key, &session, 2, &kp3, p3_nonce); //! // receive p2_sig and p3_sig from somewhere and check they're valid //! assert!(musig.verify_partial_signature(&agg_key, &session, 1, p2_sig)); //! assert!(musig.verify_partial_signature(&agg_key, &session, 2, p3_sig)); @@ -53,7 +55,7 @@ //! key that requires all of the corresponding secret keys to authorize a signature under the aggregate key. //! //! See [the excellent paper] for the abstract details of the protocol and security proofs. -//! **⚠ THIS IS EXPERIMENTAL AND NOT COMPATIBLE WITH THE [DRAFT SPECIFICATION](https://github.com/jonasnick/bips/blob/musig2/bip-musig2.mediawiki) ⚠** +//! **⚠ THIS IS EXPERIMENTAL⚠** it is currently compatible with [this PR](https://github.com/jonasnick/bips/pull/37) to the specification. //! //! //! [the excellent paper]: https://eprint.iacr.org/2020/1261.pdf @@ -66,8 +68,9 @@ use secp256kfun::{ g, hash::{HashAdd, Tagged}, marker::*, - nonce::NonceGen, - s, Point, Scalar, G, + nonce::{self, NonceGen}, + rand_core::{CryptoRng, RngCore}, + s, KeyPair, Point, Scalar, G, }; /// The MuSig context. @@ -83,15 +86,9 @@ pub struct MuSig { } impl MuSig { - /// Create a new [`KeyPair`] + /// Create a new keypair. /// - /// This is a convenient way of just doing: - /// - /// ``` - /// # let secret_key = schnorr_fun::fun::Scalar::random(&mut rand::thread_rng()); - /// use schnorr_fun::musig::KeyPair; - /// let keypair = KeyPair::new(secret_key); - /// ``` + /// A shorthand for [`KeyPair::new`]. pub fn new_keypair(&self, secret_key: Scalar) -> KeyPair { KeyPair::new(secret_key) } @@ -128,8 +125,8 @@ impl MuSig> { /// [`MuSig::new_agg_key`] #[derive(Debug, Clone)] pub struct AggKey { - /// The parties involved in the key aggregation. - parties: Vec, + /// The keys involved in the key aggregation. + keys: Vec, /// The coefficients of each key coefs: Vec>, /// The aggregate key @@ -146,14 +143,14 @@ impl AggKey { /// An iterator over the **public keys** of each party in the aggregate key. pub fn keys(&self) -> impl Iterator + '_ { - self.parties.iter().map(|point| *point) + self.keys.iter().map(|point| *point) } /// Add a scalar `tweak` to aggregate MuSig public key. /// /// The resulting key is equal to the existing key plus `tweak * G`. The tweak mutates the /// public key while still allowing the original set of signers to sign under the new key. - /// This function is appropriate for doing [BIP32] tweaks before calling `into_bip340_key`. + /// This function is appropriate for doing [BIP32] tweaks before calling `into_xonly_key`. /// It **is not** appropriate for doing taproot tweaking which must be done on a [`Bip340AggKey`]. /// /// ## Return value @@ -168,7 +165,7 @@ impl AggKey { let tweak = s!(self.tweak + tweak).mark::(); Some(AggKey { - parties: self.parties.clone(), + keys: self.keys.clone(), coefs: self.coefs.clone(), agg_key, tweak, @@ -178,12 +175,12 @@ impl AggKey { /// Convert the key into an `Bip340AggKey`. /// /// This is the BIP340 compatible version of the key which you can put in a segwitv1 output and create BIP340 signatures under. - pub fn into_bip340_key(self) -> Bip340AggKey { + pub fn into_xonly_key(self) -> Bip340AggKey { let (agg_key, needs_negation) = self.agg_key.into_point_with_even_y(); let mut tweak = self.tweak; tweak.conditional_negate(needs_negation); Bip340AggKey { - parties: self.parties, + keys: self.keys, coefs: self.coefs, needs_negation, tweak, @@ -197,8 +194,8 @@ impl AggKey { /// [BIP340]: https://bips.xyz/340 #[derive(Debug, Clone)] pub struct Bip340AggKey { - /// The parties involved in the key aggregation. - parties: Vec, + /// The keys involved in the key aggregation. + keys: Vec, /// The coefficients of each key coefs: Vec>, /// Whether the secret keys needs to be negated when signing @@ -217,7 +214,7 @@ impl Bip340AggKey { /// An iterator over the **public keys** of each party in the agg_key. pub fn keys(&self) -> impl Iterator + '_ { - self.parties.iter().map(|point| *point) + self.keys.iter().map(|point| *point) } /// Applies an "XOnly" tweak to the aggregate key @@ -231,7 +228,7 @@ impl Bip340AggKey { let needs_negation = self.needs_negation ^ needs_negation; Some(Self { - parties: self.parties, + keys: self.keys, coefs: self.coefs, needs_negation, tweak: new_tweak, @@ -263,8 +260,7 @@ impl + Clone, S> MuSig { /// // Note the keys have to come in the same order on the other side! /// let agg_key = musig.new_agg_key(vec![their_public_key, my_public_key]); /// ``` - pub fn new_agg_key(&self, parties: Vec) -> AggKey { - let keys = parties.clone(); + pub fn new_agg_key(&self, keys: Vec) -> AggKey { let coeff_hash = { let L = self.pk_hash.clone().add(&keys[..]).finalize(); self.coeff_hash.clone().add(L.as_slice()) @@ -287,11 +283,11 @@ impl + Clone, S> MuSig { }) .collect::>(); - let agg_key = crate::fun::op::lincomb(coefs.iter(), parties.iter()) + let agg_key = crate::fun::op::lincomb(coefs.iter(), keys.iter()) .expect_nonzero("computationally unreachable: linear combination of hash randomised points cannot add to zero"); AggKey { - parties, + keys, coefs, agg_key: agg_key.mark::(), tweak: Scalar::zero().mark::(), @@ -314,13 +310,14 @@ impl + Clone, NG: NonceGen> MuSig> /// sessions with nonces generated from the same `sid`. If you do your secret key will be /// recoverable from the two partial signatures you created with the same nonce. /// - /// Note that the API allows you to BYO nonces by creating `NonceKeyPair`s manually. + /// Note that you daon't have to use this API. You can create [`NonceKeyPair`]s manually in your + /// own application specific way (but be careful!). /// /// [`NonceGen`]: secp256kfun::nonce::NonceGen /// [`Synthetic`]: secp256kfun::nonce::Synthetic /// [`Deterministic`]: secp256kfun::nonce::Deterministic /// [`start_sign_session`]: Self::start_sign_session - /// [`NonceKeyPair`]: schnorr_fun::binonce::NonceKeyPair + /// [`NonceKeyPair`]: crate::binonce::NonceKeyPair pub fn gen_nonces(&self, secret: &Scalar, agg_key: &Bip340AggKey, sid: &[u8]) -> NonceKeyPair { let r1 = derive_nonce!( nonce_gen => self.schnorr.nonce_gen(), @@ -368,13 +365,6 @@ pub struct Adaptor { /// Created by [`start_sign_session`] or [`start_encrypted_sign_session`]. /// The type parameter records whether you are trying to jointly generate a signature or an adaptor signature. /// -/// ## Security -/// -/// This struct has **secret nonces** in it up until you call [`sign`]. If a malicious party -/// gains access to it before and you generate a partial signature with this session they -/// will be able to recover your secret key. If this is a concern simply avoid serializing this -/// struct (until you've cleared it) and recreate it only when you need it. -/// /// [`start_sign_session`]: MuSig::start_sign_session /// [`start_encrypted_sign_session`]: MuSig::start_encrypted_sign_session /// [`sign`]: MuSig::sign @@ -406,23 +396,23 @@ impl + Clone, NG> MuSig> { /// /// # Panics /// - /// Panics if number of nonces does not align with the parties in `agg_key`. + /// Panics if number of nonces does not align with the keys in `agg_key`. pub fn start_sign_session( &self, agg_key: &Bip340AggKey, nonces: Vec, message: Message<'_, Public>, - ) -> Option { + ) -> SignSession { let (b, c, public_nonces, R, nonce_needs_negation) = - self._start_sign_session(agg_key, nonces, message, &Point::zero())?; - Some(SignSession { + self._start_sign_session(agg_key, nonces, message, &Point::zero()); + SignSession { b, c, public_nonces, R, nonce_needs_negation, signing_type: Ordinary, - }) + } } /// Start an encrypted signing session. @@ -441,7 +431,7 @@ impl + Clone, NG> MuSig> { /// /// # Panics /// - /// Panics if number of local or remote nonces passed in does not align with the parties in + /// Panics if number of local or remote nonces passed in does not align with the keys in /// `agg_key`. /// /// [`adaptor`]: crate::adaptor @@ -450,10 +440,10 @@ impl + Clone, NG> MuSig> { agg_key: &Bip340AggKey, nonces: Vec, message: Message<'_, Public>, - encryption_key: &Point, + encryption_key: &Point, ) -> Option> { let (b, c, public_nonces, R, nonce_needs_negation) = - self._start_sign_session(agg_key, nonces, message, encryption_key)?; + self._start_sign_session(agg_key, nonces, message, encryption_key); Some(SignSession { b, c, @@ -472,13 +462,13 @@ impl + Clone, NG> MuSig> { nonces: Vec, message: Message<'_, Public>, encryption_key: &Point, - ) -> Option<( + ) -> ( Scalar, Scalar, Vec, Point, bool, - )> { + ) { let mut Rs = nonces; let agg_Rs = Rs .iter() @@ -488,65 +478,68 @@ impl + Clone, NG> MuSig> { g!({ acc[1] } + { nonce.0[1] }), ] }); - let agg_Rs = [ - g!({ agg_Rs[0] } + encryption_key) - .normalize() - .mark::()?, - agg_Rs[1].normalize().mark::()?, - ]; + let agg_Rs = Nonce::([ + g!({ agg_Rs[0] } + encryption_key).normalize(), + agg_Rs[1].normalize(), + ]); let b = { let H = self.nonce_coeff_hash.clone(); Scalar::from_hash( - H.add(agg_Rs) + H.add(agg_Rs.to_bytes()) .add(agg_key.agg_public_key().to_xonly()) .add(message), ) } .mark::<(Public, Zero)>(); - let (R, r_needs_negation) = g!({ agg_Rs[0] } + b * { agg_Rs[1] } ) + let (R, r_needs_negation) = g!({ agg_Rs.0[0] } + b * { agg_Rs.0[1] }) .normalize() - .expect_nonzero("computationally unreachable: one of the coefficients is a hash output that commits to both point") + .mark::() + .unwrap_or_else(|| { + // if final nonce is zero we set it to generator as in MuSig spec + debug_assert!(G.is_y_even()); + G.clone().mark::() + }) .into_point_with_even_y(); - for R in &mut Rs { - R.0[0] = R.0[0].conditional_negate(r_needs_negation); - R.0[1] = R.0[1].conditional_negate(r_needs_negation); + for R_i in &mut Rs { + R_i.0[0] = R_i.0[0].conditional_negate(r_needs_negation); + R_i.0[1] = R_i.0[1].conditional_negate(r_needs_negation); } let c = self .schnorr .challenge(R.to_xonly(), agg_key.agg_public_key().to_xonly(), message); - Some((b, c, Rs, R, r_needs_negation)) + (b, c, Rs, R, r_needs_negation) } /// Generates a partial signature (or partial encrypted signature depending on `T`) for the local_secret_nonce. pub fn sign( &self, agg_key: &Bip340AggKey, - my_index: u32, + session: &SignSession, + my_index: usize, keypair: &KeyPair, local_secret_nonce: NonceKeyPair, - session: &SignSession, ) -> Scalar { - let c = session.c; - let b = session.b; - let x = keypair.secret_key(); assert_eq!( keypair.public_key(), - agg_key.keys().nth(my_index as usize).unwrap(), + agg_key.keys().nth(my_index).unwrap(), "key at index {} didn't match", my_index ); - let mut a = agg_key.coefs[my_index as usize]; + let c = session.c; + let b = session.b; + let x_i = keypair.secret_key(); + let mut a = agg_key.coefs[my_index]; a.conditional_negate(agg_key.needs_negation); let [mut r1, mut r2] = local_secret_nonce.secret.clone(); r1.conditional_negate(session.nonce_needs_negation); r2.conditional_negate(session.nonce_needs_negation); - s!(c * a * x + r1 + b * r2).mark::<(Public, Zero)>() + s!(c * a * x_i + r1 + b * r2).mark::<(Public, Zero)>() } #[must_use] @@ -556,7 +549,7 @@ impl + Clone, NG> MuSig> { /// /// # Panics /// - /// Panics when `index` is equal to or greater than the number of parties in the agg_key. + /// Panics when `index` is equal to or greater than the number of keys in the agg_key. pub fn verify_partial_signature( &self, agg_key: &Bip340AggKey, @@ -566,17 +559,17 @@ impl + Clone, NG> MuSig> { ) -> bool { let c = session.c; let b = session.b; - let s = &partial_sig; + let s_i = &partial_sig; let a = agg_key.coefs[index].clone(); - let X = agg_key + let X_i = agg_key .keys() .nth(index) .unwrap() .conditional_negate(agg_key.needs_negation); let [R1, R2] = &session.public_nonces[index].0; - g!((c * a) * X + R1 + b * R2 - s * G).is_zero() + g!((c * a) * X_i + R1 + b * R2 - s_i * G).is_zero() } /// Combines all the partial signatures into a single `Signature`. @@ -635,33 +628,48 @@ impl + Clone, NG> MuSig> { } } -#[derive(Clone, Debug, PartialEq)] -/// A MuSig key pair. +/// Constructor for a MuSig instance using deterministic nonce generation. +/// +/// If you use deterministic nonce generation you will have to provide a unique session id to every signing session. +/// The advantage is that you will be able to regenerate the same nonces at a later point from [`MuSig::gen_nonces`]. /// -/// Note that the public key is a ordinary point rather than an `XOnly` key like in Schnorr. -pub struct KeyPair { - secret_key: Scalar, - public_key: Point, +/// ``` +/// use schnorr_fun::musig; +/// let musig = musig::new_with_deterministic_nonces::(); +/// ``` +pub fn new_with_deterministic_nonces>( +) -> MuSig>> { + MuSig::default() } -impl KeyPair { - /// Create a new MuSig key - pub fn new(secret_key: Scalar) -> Self { - Self { - public_key: g!(secret_key * G).normalize(), - secret_key, - } - } - - /// Get the secret key - pub fn secret_key(&self) -> &Scalar { - &self.secret_key - } +/// Constructor for a MuSig instance using synthetic nonce generation. +/// +/// Sythetic nonce generation mixes in external randomness into nonce generation which means you +/// don't need a unique session id for each signing session to guarantee security. The disadvantage +/// is that you may have to store and recall somehow the nonces generated from +/// [`MuSig::gen_nonces`]. +/// +/// ``` +/// use schnorr_fun::musig; +/// let musig = musig::new_with_deterministic_nonces::(); +/// ``` +pub fn new_with_synthetic_nonces( +) -> MuSig>>> +where + H: Tagged + Digest, + R: CryptoRng + RngCore + Default, +{ + MuSig::default() +} - /// Get the public key - pub fn public_key(&self) -> Point { - self.public_key - } +/// Create a MuSig instance which does not handle nonce generation. +/// +/// You can still sign with this instance but you you will have to generate nonces in your own way. +pub fn new_without_nonce_generation() -> MuSig> +where + H: Tagged + Digest, +{ + MuSig::default() } #[cfg(test)] @@ -677,7 +685,7 @@ mod test { proptest! { #[test] - fn test_end_to_end(sk1 in any::(), + fn proptest_sign_verify(sk1 in any::(), sk2 in any::(), sk3 in any::(), pre_tweak1 in option::of(any::>()), @@ -719,9 +727,9 @@ mod test { } - let mut agg_key1 = agg_key1.into_bip340_key(); - let mut agg_key2 = agg_key2.into_bip340_key(); - let mut agg_key3 = agg_key3.into_bip340_key(); + let mut agg_key1 = agg_key1.into_xonly_key(); + let mut agg_key2 = agg_key2.into_xonly_key(); + let mut agg_key3 = agg_key3.into_xonly_key(); for tweak in [tweak1, tweak2] { if let Some(tweak) = tweak { @@ -748,24 +756,21 @@ mod test { &agg_key1, nonces.clone(), message, - ) - .unwrap(); + ); let p2_session = musig .start_sign_session( &agg_key2, nonces.clone(), message, - ) - .unwrap(); + ); let p3_session = musig .start_sign_session( &agg_key3, nonces.clone(), message, - ) - .unwrap(); + ); - let p1_sig = musig.sign(&agg_key1, 0, &keypair1, p1_nonce, &p1_session); + let p1_sig = musig.sign(&agg_key1, &p1_session, 0, &keypair1, p1_nonce); assert!(musig.verify_partial_signature(&agg_key1, &p1_session, 0, p1_sig)); assert_eq!(p1_session, p2_session); @@ -773,9 +778,9 @@ mod test { assert!(musig.verify_partial_signature(&agg_key1, &p2_session, 0, p1_sig)); assert!(musig.verify_partial_signature(&agg_key1, &p3_session, 0, p1_sig)); - let p2_sig = musig.sign(&agg_key1, 1, &keypair2, p2_nonce, &p2_session); + let p2_sig = musig.sign(&agg_key1, &p2_session, 1, &keypair2, p2_nonce); assert!(musig.verify_partial_signature(&agg_key1, &p1_session, 1, p2_sig)); - let p3_sig = musig.sign(&agg_key1, 2, &keypair3, p3_nonce, &p3_session); + let p3_sig = musig.sign(&agg_key1, &p3_session, 2, &keypair3, p3_nonce); assert!(musig.verify_partial_signature(&agg_key1, &p1_session, 2, p3_sig)); let partial_sigs = [p1_sig, p2_sig, p3_sig]; @@ -817,17 +822,17 @@ mod test { keypair1.public_key(), keypair2.public_key(), keypair3.public_key(), - ]).into_bip340_key(); + ]).into_xonly_key(); let agg_key2 = musig.new_agg_key(vec![ keypair1.public_key(), keypair2.public_key(), keypair3.public_key(), - ]).into_bip340_key(); + ]).into_xonly_key(); let agg_key3 = musig.new_agg_key(vec![ keypair1.public_key(), keypair2.public_key(), keypair3.public_key(), - ]).into_bip340_key(); + ]).into_xonly_key(); let p1_nonce = musig.gen_nonces(keypair1.secret_key(), &agg_key, b"test"); let p2_nonce = musig.gen_nonces(keypair2.secret_key(), &agg_key2, b"test"); @@ -860,9 +865,9 @@ mod test { &encryption_key ) .unwrap(); - let p1_sig = musig.sign(&agg_key, 0, &keypair1, p1_nonce, &mut p1_session); - let p2_sig = musig.sign(&agg_key, 1, &keypair2, p2_nonce, &mut p2_session); - let p3_sig = musig.sign(&agg_key, 2, &keypair3, p3_nonce, &mut p3_session); + let p1_sig = musig.sign(&agg_key, &mut p1_session, 0, &keypair1, p1_nonce); + let p2_sig = musig.sign(&agg_key, &mut p2_session, 1, &keypair2, p2_nonce); + let p3_sig = musig.sign(&agg_key, &mut p3_session, 2, &keypair3, p3_nonce); assert!(musig.verify_partial_signature(&agg_key2, &p2_session, 0, p1_sig)); assert!(musig.verify_partial_signature(&agg_key, &p1_session, 0, p1_sig)); @@ -884,441 +889,4 @@ mod test { .verify_encrypted_signature(&agg_key2.agg_public_key(), &encryption_key, message, &combined_sig_p3)); } } - - // #[test] - // fn test_key_agg() { - // let X1 = XOnly::from_bytes([ - // 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, - // 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, - // 0xBC, 0xE0, 0x36, 0xF9, - // ]) - // .unwrap(); - // let X2 = XOnly::from_bytes([ - // 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, - // 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, - // 0x50, 0x2B, 0xA6, 0x59, - // ]) - // .unwrap(); - // let X3 = XOnly::from_bytes([ - // 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, 0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80, - // 0xA8, 0xE3, 0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7, 0xAD, 0x33, 0x83, 0x68, - // 0xD0, 0x38, 0xCA, 0x66, - // ]) - // .unwrap(); - // let X = vec![X1, X2, X3]; - - // let expected: Vec = vec![ - // XOnly::from_bytes([ - // 0xE5, 0x83, 0x01, 0x40, 0x51, 0x21, 0x95, 0xD7, 0x4C, 0x83, 0x07, 0xE3, 0x96, 0x37, - // 0xCB, 0xE5, 0xFB, 0x73, 0x0E, 0xBE, 0xAB, 0x80, 0xEC, 0x51, 0x4C, 0xF8, 0x8A, 0x87, - // 0x7C, 0xEE, 0xEE, 0x0B, - // ]) - // .unwrap(), - // XOnly::from_bytes([ - // 0xD7, 0x0C, 0xD6, 0x9A, 0x26, 0x47, 0xF7, 0x39, 0x09, 0x73, 0xDF, 0x48, 0xCB, 0xFA, - // 0x2C, 0xCC, 0x40, 0x7B, 0x8B, 0x2D, 0x60, 0xB0, 0x8C, 0x5F, 0x16, 0x41, 0x18, 0x5C, - // 0x79, 0x98, 0xA2, 0x90, - // ]) - // .unwrap(), - // XOnly::from_bytes([ - // 0x81, 0xA8, 0xB0, 0x93, 0x91, 0x2C, 0x9E, 0x48, 0x14, 0x08, 0xD0, 0x97, 0x76, 0xCE, - // 0xFB, 0x48, 0xAE, 0xB8, 0xB6, 0x54, 0x81, 0xB6, 0xBA, 0xAF, 0xB3, 0xC5, 0x81, 0x01, - // 0x06, 0x71, 0x7B, 0xEB, - // ]) - // .unwrap(), - // XOnly::from_bytes([ - // 0x2E, 0xB1, 0x88, 0x51, 0x88, 0x7E, 0x7B, 0xDC, 0x5E, 0x83, 0x0E, 0x89, 0xB1, 0x9D, - // 0xDB, 0xC2, 0x80, 0x78, 0xF1, 0xFA, 0x88, 0xAA, 0xD0, 0xAD, 0x01, 0xCA, 0x06, 0xFE, - // 0x4F, 0x80, 0x21, 0x0B, - // ]) - // .unwrap(), - // ]; - - // let musig = MuSig::>>::default(); - // assert_eq!( - // musig - // .new_agg_key(vec![X[0], X[1], X[2]]) - // .into_bip340_key() - // .agg_public_key(), - // expected[0] - // ); - // assert_eq!( - // musig - // .new_agg_key(vec![X[2], X[1], X[0]]) - // .into_bip340_key() - // .agg_public_key(), - // expected[1] - // ); - // assert_eq!( - // musig - // .new_agg_key(vec![X[0], X[0], X[0]]) - // .into_bip340_key() - // .agg_public_key(), - // expected[2] - // ); - // assert_eq!( - // musig - // .new_agg_key(vec![X[0], X[0], X[1], X[1]]) - // .into_bip340_key() - // .agg_public_key(), - // expected[3] - // ); - // } - - // #[test] - // fn test_sign_vectors() { - // let X1 = XOnly::from_bytes([ - // 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, - // 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, - // 0xBC, 0xE0, 0x36, 0xF9, - // ]) - // .unwrap(); - // let X2 = XOnly::from_bytes([ - // 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, - // 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, - // 0x50, 0x2B, 0xA6, 0x59, - // ]) - // .unwrap(); - - // let sec_nonce = NonceKeyPair::from_bytes([ - // 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, - // 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, - // 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, - // 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, - // 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, - // ]) - // .unwrap(); - - // let agg_pubnonce = Nonce::from_bytes([ - // 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, - // 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, - // 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, - // 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, - // 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9, - // ]) - // .unwrap(); - - // let sk = Scalar::from_bytes([ - // 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, - // 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, - // 0xD1, 0x00, 0x76, 0x71, - // ]) - // .unwrap() - // .mark::() - // .unwrap(); - - // let msg = [ - // 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, - // 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, - // 0xDD, 0xF3, 0xC0, 0xCF, - // ]; - - // let expected: Vec = vec![ - // Scalar::from_bytes([ - // 0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E, - // 0x90, 0xC2, 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12, - // 0x40, 0x3B, 0x4D, 0x3B, - // ]) - // .unwrap() - // .mark::() - // .unwrap(), - // Scalar::from_bytes([ - // 0x2D, 0xF6, 0x7B, 0xFF, 0xF1, 0x8E, 0x3D, 0xE7, 0x97, 0xE1, 0x3C, 0x64, 0x75, 0xC9, - // 0x63, 0x04, 0x81, 0x38, 0xDA, 0xEC, 0x5C, 0xB2, 0x0A, 0x35, 0x7C, 0xEC, 0xA7, 0xC8, - // 0x42, 0x42, 0x95, 0xEA, - // ]) - // .unwrap() - // .mark::() - // .unwrap(), - // Scalar::from_bytes([ - // 0x0D, 0x5B, 0x65, 0x1E, 0x6D, 0xE3, 0x4A, 0x29, 0xA1, 0x2D, 0xE7, 0xA8, 0xB4, 0x18, - // 0x3B, 0x4A, 0xE6, 0xA7, 0xF7, 0xFB, 0xE1, 0x5C, 0xDC, 0xAF, 0xA4, 0xA3, 0xD1, 0xBC, - // 0xAA, 0xBC, 0x75, 0x17, - // ]) - // .unwrap() - // .mark::() - // .unwrap(), - // ]; - - // let musig = MuSig::>>::default(); - // let keypair = musig.schnorr.new_keypair(sk); - - // let (remote_nonce1, remote_nonce2) = ( - // agg_pubnonce, - // Nonce([-sec_nonce.public.0[0], -sec_nonce.public.0[1]]), - // ); - // let message = Message::::raw(&msg); - // { - // let agg_key = musig - // .new_agg_key(vec![keypair.pk, X1, X2]) - // .into_bip340_key(); - - // let sign_session = musig - // .start_sign_session( - // &agg_key, - // vec![ - // sec_nonce.public(), - // remote_nonce1.clone(), - // remote_nonce2.clone(), - // ], - // message, - // ) - // .unwrap(); - // let sig = musig.sign(&agg_key, 0, &keypair, sec_nonce.clone(), &sign_session); - // assert_eq!(sig, expected[0]); - // } - - // { - // let agg_key = musig - // .new_agg_key(vec![X1, keypair.pk, X2]) - // .into_bip340_key(); - // let sign_session = musig - // .start_sign_session( - // &agg_key, - // vec![ - // remote_nonce1.clone(), - // sec_nonce.public(), - // remote_nonce2.clone(), - // ], - // message, - // ) - // .unwrap(); - // let sig = musig.sign(&agg_key, 1, &keypair, sec_nonce.clone(), &sign_session); - // assert_eq!(sig, expected[1]); - // } - - // { - // let agg_key = musig - // .new_agg_key(vec![X1, X2, keypair.pk]) - // .into_bip340_key(); - // let sign_session = musig - // .start_sign_session( - // &agg_key, - // vec![ - // remote_nonce1.clone(), - // remote_nonce2.clone(), - // sec_nonce.public(), - // ], - // message, - // ) - // .unwrap(); - // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); - // assert_eq!(sig, expected[2]); - // } - // } - - // #[test] - // fn test_tweak_vectors() { - // let X1 = XOnly::from_bytes([ - // 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, - // 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, - // 0xBC, 0xE0, 0x36, 0xF9, - // ]) - // .unwrap(); - // let X2 = XOnly::from_bytes([ - // 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, - // 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, - // 0x50, 0x2B, 0xA6, 0x59, - // ]) - // .unwrap(); - - // let sec_nonce = NonceKeyPair::from_bytes([ - // 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, - // 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, - // 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, - // 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, - // 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, - // ]) - // .unwrap(); - - // let agg_pubnonce = Nonce::from_bytes([ - // 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, - // 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, - // 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, - // 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, - // 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9, - // ]) - // .unwrap(); - - // let sk = Scalar::from_bytes([ - // 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, - // 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, - // 0xD1, 0x00, 0x76, 0x71, - // ]) - // .unwrap() - // .mark::() - // .unwrap(); - - // let tweaks: Vec = vec![ - // Scalar::from_bytes([ - // 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, - // 0x72, 0x3D, 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, 0x80, 0x2B, 0x26, 0x3C, - // 0xDF, 0xCD, 0x83, 0xBB, - // ]) - // .unwrap() - // .mark::() - // .unwrap(), - // Scalar::from_bytes([ - // 0xAE, 0x2E, 0xA7, 0x97, 0xCC, 0x0F, 0xE7, 0x2A, 0xC5, 0xB9, 0x7B, 0x97, 0xF3, 0xC6, - // 0x95, 0x7D, 0x7E, 0x41, 0x99, 0xA1, 0x67, 0xA5, 0x8E, 0xB0, 0x8B, 0xCA, 0xFF, 0xDA, - // 0x70, 0xAC, 0x04, 0x55, - // ]) - // .unwrap() - // .mark::() - // .unwrap(), - // Scalar::from_bytes([ - // 0xF5, 0x2E, 0xCB, 0xC5, 0x65, 0xB3, 0xD8, 0xBE, 0xA2, 0xDF, 0xD5, 0xB7, 0x5A, 0x4F, - // 0x45, 0x7E, 0x54, 0x36, 0x98, 0x09, 0x32, 0x2E, 0x41, 0x20, 0x83, 0x16, 0x26, 0xF2, - // 0x90, 0xFA, 0x87, 0xE0, - // ]) - // .unwrap() - // .mark::() - // .unwrap(), - // Scalar::from_bytes([ - // 0x19, 0x69, 0xAD, 0x73, 0xCC, 0x17, 0x7F, 0xA0, 0xB4, 0xFC, 0xED, 0x6D, 0xF1, 0xF7, - // 0xBF, 0x99, 0x07, 0xE6, 0x65, 0xFD, 0xE9, 0xBA, 0x19, 0x6A, 0x74, 0xFE, 0xD0, 0xA3, - // 0xCF, 0x5A, 0xEF, 0x9D, - // ]) - // .unwrap() - // .mark::() - // .unwrap(), - // ]; - // let msg = [ - // 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, - // 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, - // 0xDD, 0xF3, 0xC0, 0xCF, - // ]; - - // let expected: Vec = vec![ - // Scalar::from_bytes([ - // 0x5E, 0x24, 0xC7, 0x49, 0x6B, 0x56, 0x5D, 0xEB, 0xC3, 0xB9, 0x63, 0x9E, 0x6F, 0x13, - // 0x04, 0xA2, 0x15, 0x97, 0xF9, 0x60, 0x3D, 0x3A, 0xB0, 0x5B, 0x49, 0x13, 0x64, 0x17, - // 0x75, 0xE1, 0x37, 0x5B, - // ]) - // .unwrap() - // .mark::() - // .unwrap(), - // Scalar::from_bytes([ - // 0x78, 0x40, 0x8D, 0xDC, 0xAB, 0x48, 0x13, 0xD1, 0x39, 0x4C, 0x97, 0xD4, 0x93, 0xEF, - // 0x10, 0x84, 0x19, 0x5C, 0x1D, 0x4B, 0x52, 0xE6, 0x3E, 0xCD, 0x7B, 0xC5, 0x99, 0x16, - // 0x44, 0xE4, 0x4D, 0xDD, - // ]) - // .unwrap() - // .mark::() - // .unwrap(), - // Scalar::from_bytes([ - // 0xC3, 0xA8, 0x29, 0xA8, 0x14, 0x80, 0xE3, 0x6E, 0xC3, 0xAB, 0x05, 0x29, 0x64, 0x50, - // 0x9A, 0x94, 0xEB, 0xF3, 0x42, 0x10, 0x40, 0x3D, 0x16, 0xB2, 0x26, 0xA6, 0xF1, 0x6E, - // 0xC8, 0x5B, 0x73, 0x57, - // ]) - // .unwrap() - // .mark::() - // .unwrap(), - // Scalar::from_bytes([ - // 0x8C, 0x44, 0x73, 0xC6, 0xA3, 0x82, 0xBD, 0x3C, 0x4A, 0xD7, 0xBE, 0x59, 0x81, 0x8D, - // 0xA5, 0xED, 0x7C, 0xF8, 0xCE, 0xC4, 0xBC, 0x21, 0x99, 0x6C, 0xFD, 0xA0, 0x8B, 0xB4, - // 0x31, 0x6B, 0x8B, 0xC7, - // ]) - // .unwrap() - // .mark::() - // .unwrap(), - // ]; - - // let musig = MuSig::>>::default(); - // let keypair = musig.schnorr.new_keypair(sk); - - // let (remote_nonce1, remote_nonce2) = ( - // agg_pubnonce, - // Nonce([-sec_nonce.public.0[0], -sec_nonce.public.0[1]]), - // ); - // let message = Message::::raw(&msg); - - // { - // let agg_key = musig - // .new_agg_key(vec![X1, X2, keypair.pk]) - // .into_bip340_key() - // .tweak(tweaks[0].clone()) - // .unwrap(); - // let sign_session = musig - // .start_sign_session( - // &agg_key, - // vec![ - // remote_nonce1.clone(), - // remote_nonce2.clone(), - // sec_nonce.public(), - // ], - // message, - // ) - // .unwrap(); - // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); - // assert_eq!(sig, expected[0]); - // } - - // { - // let agg_key = musig - // .new_agg_key(vec![X1, X2, keypair.pk]) - // .into_bip340_key() - // .tweak(tweaks[0].clone()) - // .unwrap(); - // let sign_session = musig - // .start_sign_session( - // &agg_key, - // vec![ - // remote_nonce1.clone(), - // remote_nonce2.clone(), - // sec_nonce.public(), - // ], - // message, - // ) - // .unwrap(); - // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); - // assert_eq!(sig, expected[0]); - // } - - // { - // let agg_key = musig - // .new_agg_key(vec![X1, X2, keypair.pk]) - // .tweak(tweaks[0].clone()) - // .unwrap() - // .into_bip340_key() - // .tweak(tweaks[1].clone()) - // .unwrap(); - - // let sign_session = musig - // .start_sign_session( - // &agg_key, - // vec![ - // remote_nonce1.clone(), - // remote_nonce2.clone(), - // sec_nonce.public(), - // ], - // message, - // ) - // .unwrap(); - // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); - // assert_eq!(sig, expected[2]); - // } - - // // { - // // let mut agg_key = musig.new_agg_key(vec![X1, X2, keypair.pk]); - // // agg_key = agg_key.tweak(tweaks[0].clone(), true).unwrap(); - // // agg_key = agg_key.tweak(tweaks[1].clone(), false).unwrap(); - // // agg_key = agg_key.tweak(tweaks[2].clone(), true).unwrap(); - // // agg_key = agg_key.tweak(tweaks[3].clone(), false).unwrap(); - - // // let sign_session = musig - // // .start_sign_session( - // // &agg_key, - // // vec![ - // // remote_nonce1.clone(), - // // remote_nonce2.clone(), - // // sec_nonce.public(), - // // ], - // // message, - // // ) - // // .unwrap(); - // // let sig = musig.sign(&agg_key, 2, &keypair, sec_nonce.clone(), &sign_session); - // // assert_eq!(sig, expected[3]); - // // } - // } } diff --git a/schnorr_fun/src/schnorr.rs b/schnorr_fun/src/schnorr.rs index ba7a42b0..885e00c5 100644 --- a/schnorr_fun/src/schnorr.rs +++ b/schnorr_fun/src/schnorr.rs @@ -6,9 +6,9 @@ use crate::{ hash::{HashAdd, Tagged}, marker::*, nonce::{AddTag, NonceGen}, - s, Point, Scalar, XOnly, G, + s, Point, Scalar, XOnly, XOnlyKeyPair, G, }, - KeyPair, Message, Signature, + Message, Signature, }; /// An instance of a [BIP-340] style Schnorr signature scheme. @@ -117,9 +117,9 @@ where /// b"Chancellor on brink of second bailout for banks", /// ); /// let signature = schnorr.sign(&keypair, message); - /// assert!(schnorr.verify(&keypair.verification_key(), message, &signature)); + /// assert!(schnorr.verify(&keypair.public_key().to_point(), message, &signature)); /// ``` - pub fn sign(&self, keypair: &KeyPair, message: Message<'_, impl Secrecy>) -> Signature { + pub fn sign(&self, keypair: &XOnlyKeyPair, message: Message<'_, impl Secrecy>) -> Signature { let (x, X) = keypair.as_tuple(); let mut r = derive_nonce!( @@ -148,18 +148,12 @@ impl + Clone> Schnorr { pub fn challenge_hash(&self) -> CH { self.challenge_hash.clone() } - /// Converts a non-zero scalar to a key-pair by interpreting it as a secret key. - /// - /// **The secret key in the resulting key is not guaranteed to be the same - /// as the input**. For half the input values the result will be the - /// negation of it. This happens because the corresponding [`Point`] may not - /// have an y-coordinate that is even (see [`EvenY`]) + + /// Create a new signing keypair. /// - /// [`Point`]: crate::fun::Point - /// [`EvenY`]: crate::fun::marker::EvenY - pub fn new_keypair(&self, mut sk: Scalar) -> KeyPair { - let pk = XOnly::from_scalar_mul(&G, &mut sk); - KeyPair { sk, pk } + /// Short form of [`XOnlyKeyPair::new`]. + pub fn new_keypair(&self, sk: Scalar) -> XOnlyKeyPair { + XOnlyKeyPair::new(sk) } /// Produces the Fiat-Shamir challenge for a Schnorr signature in the form specified by [BIP-340]. @@ -184,7 +178,7 @@ impl + Clone> Schnorr { /// let challenge = schnorr.challenge(R, keypair.public_key(), message); /// let s = s!(r + challenge * { keypair.secret_key() }); /// let signature = Signature { R, s }; - /// assert!(schnorr.verify(&keypair.verification_key(), message, &signature)); + /// assert!(schnorr.verify(&keypair.public_key().to_point(), message, &signature)); /// ``` /// /// [BIP-340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki @@ -305,7 +299,7 @@ pub mod test { ); let signature = schnorr.sign(&keypair, msg); let anticipated_signature = schnorr.anticipate_signature( - &keypair.verification_key(), + &keypair.public_key().to_point(), &signature.R.to_point(), msg, ); @@ -329,7 +323,7 @@ pub mod test { let signature_3 = schnorr.sign(&keypair_1, msg_rtrtnoon); let signature_4 = schnorr.sign(&keypair_2, msg_atkdwn); - assert!(schnorr.verify(&keypair_1.verification_key(), msg_atkdwn, &signature_1)); + assert!(schnorr.verify(&keypair_1.public_key().to_point(), msg_atkdwn, &signature_1)); assert_eq!(signature_1, signature_2); if keypair_1 != keypair_2 { assert_ne!(signature_3.R, signature_1.R); diff --git a/schnorr_fun/tests/test-vectors.csv b/schnorr_fun/tests/bip340-test-vectors.csv similarity index 100% rename from schnorr_fun/tests/test-vectors.csv rename to schnorr_fun/tests/bip340-test-vectors.csv diff --git a/schnorr_fun/tests/bip340.rs b/schnorr_fun/tests/bip340.rs index 5bb20409..9dae0412 100644 --- a/schnorr_fun/tests/bip340.rs +++ b/schnorr_fun/tests/bip340.rs @@ -9,7 +9,7 @@ use schnorr_fun::{ }; use sha2::Sha256; -static BIP340_CSV: &'static str = include_str!("./test-vectors.csv"); +static BIP340_CSV: &'static str = include_str!("./bip340-test-vectors.csv"); struct AuxRng<'a>(&'a [u8]); diff --git a/schnorr_fun/tests/musig/key_agg_vectors.json b/schnorr_fun/tests/musig/key_agg_vectors.json new file mode 100644 index 00000000..b2e623de --- /dev/null +++ b/schnorr_fun/tests/musig/key_agg_vectors.json @@ -0,0 +1,88 @@ +{ + "pubkeys": [ + "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + "03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + "023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66", + "020000000000000000000000000000000000000000000000000000000000000005", + "02FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", + "04F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + "03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9" + ], + "tweaks": [ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", + "252E4BD67410A76CDF933D30EAA1608214037F1B105A013ECCD3C5C184A6110B" + ], + "valid_test_cases": [ + { + "key_indices": [0, 1, 2], + "expected": "90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C" + }, + { + "key_indices": [2, 1, 0], + "expected": "6204DE8B083426DC6EAF9502D27024D53FC826BF7D2012148A0575435DF54B2B" + }, + { + "key_indices": [0, 0, 0], + "expected": "B436E3BAD62B8CD409969A224731C193D051162D8C5AE8B109306127DA3AA935" + }, + { + "key_indices": [0, 0, 1, 1], + "expected": "69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E" + } + ], + "error_test_cases": [ + { + "key_indices": [0, 3], + "tweak_indices": [], + "is_xonly": [], + "error": { + "type": "invalid_contribution", + "signer": 1, + "contrib": "pubkey" + }, + "comment": "Invalid public key" + }, + { + "key_indices": [0, 4], + "tweak_indices": [], + "is_xonly": [], + "error": { + "type": "invalid_contribution", + "signer": 1, + "contrib": "pubkey" + }, + "comment": "Public key exceeds field size" + }, + { + "key_indices": [5, 0], + "tweak_indices": [], + "is_xonly": [], + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubkey" + }, + "comment": "First byte of public key is not 2 or 3" + }, + { + "key_indices": [0, 1], + "tweak_indices": [0], + "is_xonly": [true], + "error": { + "type": "value", + "message": "The tweak must be less than n." + }, + "comment": "Tweak is out of range" + }, + { + "key_indices": [6], + "tweak_indices": [1], + "is_xonly": [false], + "error": { + "type": "value", + "message": "The result of tweaking cannot be infinity." + }, + "comment": "Intermediate tweaking result is point at infinity" + } + ] +} diff --git a/schnorr_fun/tests/musig/nonce_agg_vectors.json b/schnorr_fun/tests/musig/nonce_agg_vectors.json new file mode 100644 index 00000000..1c04b881 --- /dev/null +++ b/schnorr_fun/tests/musig/nonce_agg_vectors.json @@ -0,0 +1,51 @@ +{ + "pnonces": [ + "020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E66603BA47FBC1834437B3212E89A84D8425E7BF12E0245D98262268EBDCB385D50641", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833", + "020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E6660279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60379BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "04FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B831", + "03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A602FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30" + ], + "valid_test_cases": [ + { + "pnonce_indices": [0, 1], + "expected": "035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B024725377345BDE0E9C33AF3C43C0A29A9249F2F2956FA8CFEB55C8573D0262DC8" + }, + { + "pnonce_indices": [2, 3], + "expected": "035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B000000000000000000000000000000000000000000000000000000000000000000", + "comment": "Sum of second points encoded in the nonces is point at infinity which is serialized as 33 zero bytes" + } + ], + "error_test_cases": [ + { + "pnonce_indices": [0, 4], + "error": { + "type": "invalid_contribution", + "signer": 1, + "contrib": "pubnonce" + }, + "comment": "Public nonce from signer 1 is invalid due wrong tag, 0x04, in the first half" + }, + { + "pnonce_indices": [5, 1], + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubnonce" + }, + "comment": "Public nonce from signer 0 is invalid because the second half does not correspond to an X coordinate" + }, + { + "pnonce_indices": [6, 1], + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubnonce" + }, + "comment": "Public nonce from signer 0 is invalid because second half exceeds field size" + } + ] +} diff --git a/schnorr_fun/tests/musig/nonce_gen_vectors.json b/schnorr_fun/tests/musig/nonce_gen_vectors.json new file mode 100644 index 00000000..ff6b1b94 --- /dev/null +++ b/schnorr_fun/tests/musig/nonce_gen_vectors.json @@ -0,0 +1,28 @@ +{ + "test_cases": [ + { + "rand_": "0000000000000000000000000000000000000000000000000000000000000000", + "sk": "0202020202020202020202020202020202020202020202020202020202020202", + "aggpk": "0707070707070707070707070707070707070707070707070707070707070707", + "msg": "0101010101010101010101010101010101010101010101010101010101010101", + "extra_in": "0808080808080808080808080808080808080808080808080808080808080808", + "expected": "BC6C683EBBCC39DCB3C29B3D010D2AAA7C86CFB562FC41ED9A460EE061013E75FB4AD2F0B816713269800D018803906D5481E00A940EAB4F4AC49B4A372EB0F4" + }, + { + "rand_": "0000000000000000000000000000000000000000000000000000000000000000", + "sk": "0202020202020202020202020202020202020202020202020202020202020202", + "aggpk": "0707070707070707070707070707070707070707070707070707070707070707", + "msg": "", + "extra_in": "0808080808080808080808080808080808080808080808080808080808080808", + "expected": "AAC4BFD707F4953B4063851D7E4AAD5C59D5D0BFB0E71012788A85698B5ACF8F11834D5051928424BA501C8CD064F3F942F8D4A07D8A2ED79F153E4ABD9EBBE9" + }, + { + "rand_": "0000000000000000000000000000000000000000000000000000000000000000", + "sk": null, + "aggpk": null, + "msg": null, + "extra_in": null, + "expected": "7B3B5A002356471AF0E961DE2549C121BD0D48ABCEEDC6E034BDDF86AD3E0A187ECEE674CEF7364B0BC4BEEFB8B66CAD89F98DE2F8C5A5EAD5D1D1E4BD7D04CD" + } + ] +} diff --git a/schnorr_fun/tests/musig/sig_agg_vectors.json b/schnorr_fun/tests/musig/sig_agg_vectors.json new file mode 100644 index 00000000..7ae9444f --- /dev/null +++ b/schnorr_fun/tests/musig/sig_agg_vectors.json @@ -0,0 +1,86 @@ +{ + "pubkeys": [ + "03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9", + "02D2DC6F5DF7C56ACF38C7FA0AE7A759AE30E19B37359DFDE015872324C7EF6E05", + "03C7FB101D97FF930ACD0C6760852EF64E69083DE0B06AC6335724754BB4B0522C", + "02352433B21E7E05D3B452B81CAE566E06D2E003ECE16D1074AABA4289E0E3D581" + ], + "pnonces": [ + "0300A32F8548F59C533F55DB9754E3C0BA3C2544F085649FDCE42B8BD3F244C2CA0384449BED61004E8863452A38534E91875516C3CC543122CE2BE1F31845025588", + "03F66B072A869BC2A57D776D487151D707E82B4F1B885066A589858C1BF3871DB603ED391C9658AB6031A96ACBD5E2D9FEC465EFDC8C0D0B765C9B9F3579D520FB6F", + "03A5791CA078E278126EF457C25B5C835F7282C0A47BDBF464BA35C3769427D5CD034D40350F8A5590985E38AAEFC3C695DF671C2E5498E2B60C082C546E06ECAF78", + "020DE6382B8C0550E8174D5263B981224EBCFEF7706588B6936177FEB68E639B8C02BA5F18DDB3487AD087F63CEF7D7818AC8ECA3D6B736113FF36FB25D113F514F6", + "031883080513BB69B31367F9A7B5F4E81246C627060A7414B7F137FA8459F261990345445505F158EDCFDF0D4BF26E04E018C143BF76B5D457AE57DF06CA41371DF0", + "0300028E83123E7FAB1E1F230547CE8B96CC23F13197312972DE72AACBA98EF9870274C2D8566E9E021AA7E2DDDA01B52AE670E0742418F147610528B65ACDB4D0B3" + ], + "tweaks": [ + "B511DA492182A91B0FFB9A98020D55F260AE86D7ECBD0399C7383D59A5F2AF7C", + "A815FE049EE3C5AAB66310477FBC8BCCCAC2F3395F59F921C364ACD78A2F48DC", + "75448A87274B056468B977BE06EB1E9F657577B7320B0A3376EA51FD420D18A8" + ], + "psigs": [ + "7918521F42E5727FE2E82D802876E0C8844336FDA1B58C82696A55B0188C8B3D", + "599044037AE15C4A99FB94F022B48E7AB215BF703954EC0B83D0E06230476001", + "F05BE3CA783AD1FAF68C5059B43F859BFD4EBB0242459DF2C6BF013F4217F7E7", + "BF85B2A751066466C24A5E7FA6C90DBAADAC2DF1F0BB48546AE239E340437CEB", + "142076B034A7401123EFB07E2317DF819B86B3FFA17180DDD093997D018270D0", + "B7A0C7F5B325B7993925E56B60F53EF8198169F31E1AF7E62BBEF1C5DCD1BA22", + "C717ECA32C148CE8EB8882CD9656DF9C64929DCAE9AF798E381B1E888DDF0F8F", + "5988823E78488D8005311E16E5EA67AF70514CB44F5A5CD51FFA262BEEAA21CE", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141" + ], + "msg": "599C67EA410D005B9DA90817CF03ED3B1C868E4DA4EDF00A5880B0082C237869", + "valid_test_cases": [ + { + "aggnonce": "02BC34CDF6FA1298D7B6A126812FAD0739005BC44E45C21276EEFE41AAF841C86F03F3562AED52243BB99F43D1677DB59F0FEFB961633997F7AC924B78FBD0B0334F", + "nonce_indices": [0, 1], + "key_indices": [0, 1], + "tweak_indices": [], + "is_xonly": [], + "psig_indices": [0, 1], + "expected": "CA3C28729659E50F829F55DC5DB1DE88A05D1702B4165B85F95B627FC57733F8D2A89622BDC6CECA7CE3C2704B2B6F433658F66DDB0A788DED3B361248D3EB3E" + }, + { + "aggnonce": "035538518B8043CF4EACD0E701A80657B741C0E6445EC1D6C6177964D22C642971030CFE657EC882F4E08E751B883A78AC1491B30FC86CB57AF2DFF012C2BE6DF1F2", + "nonce_indices": [0, 2], + "key_indices": [0, 2], + "tweak_indices": [], + "is_xonly": [], + "psig_indices": [2, 3], + "expected": "3997A11DFF76349532CF25E761365EA1D4F24B62EB23A12A9DAABD5976C3DB9FAFE19671C9413661B8D6AED95B089357F04C0C0D83B8460B71CEDC95B2253391" + }, + { + "aggnonce": "024366775E6FFBEBBB954225936BAED71A3884C7933B18225088D19E7AF12D8D5D028D79A520B347B793FFE897A7EB79A4366A3FDCDC652C243FAC3976B3D6DF8AB2", + "nonce_indices": [0, 3], + "key_indices": [0, 2], + "tweak_indices": [0], + "is_xonly": [false], + "psig_indices": [4, 5], + "expected": "5AF759C2839B7FEE59D31DAB800F82FC21258457773A3B1F69F5228C80CAD4317EA39AD756601030E4D4051B7C9A25AB4DE7CB39BED26E0A03A1B2ED5B747F7F" + }, + { + "aggnonce": "03B25098C6D0B72DC5717314AF26C126609B4776AA468553DD4354EE20B216B227027D242E9203499173A74E286C1F796F2711E171EE937706BBEA2F4DB10C4E6809", + "nonce_indices": [0, 4], + "key_indices": [0, 3], + "tweak_indices": [0, 1, 2], + "is_xonly": [true, false, true], + "psig_indices": [6, 7], + "expected": "B495A478F91D6E10BF08A156E46D9E62B4C5399C1AEDDA1A9D306F06AFB8A52F2C078FD6B50DDBC33BFFE583C3C1E3D0D5E52891E190101C70D2278BCA943457" + } + ], + "error_test_cases": [ + { + "aggnonce": "03B25098C6D0B72DC5717314AF26C126609B4776AA468553DD4354EE20B216B227027D242E9203499173A74E286C1F796F2711E171EE937706BBEA2F4DB10C4E6809", + "nonce_indices": [0, 4], + "key_indices": [0, 3], + "tweak_indices": [0, 1, 2], + "is_xonly": [true, false, true], + "psig_indices": [7, 8], + "error": { + "type": "invalid_contribution", + "signer": 1 + }, + "comment": "Partial signature is invalid because it exceeds group size" + } + ] +} diff --git a/schnorr_fun/tests/musig/sign_verify_vectors.json b/schnorr_fun/tests/musig/sign_verify_vectors.json new file mode 100644 index 00000000..01683c1c --- /dev/null +++ b/schnorr_fun/tests/musig/sign_verify_vectors.json @@ -0,0 +1,172 @@ +{ + "sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671", + "pubkeys": [ + "03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9", + "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA661", + "020000000000000000000000000000000000000000000000000000000000000007" + ], + "secnonce": "508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7", + "pnonces": [ + "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480", + "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046", + "0237C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0387BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480", + "020000000000000000000000000000000000000000000000000000000000000009" + ], + "aggnonces": [ + "028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "048465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9", + "028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61020000000000000000000000000000000000000000000000000000000000000009", + "028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD6102FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30" + ], + "msgs": [ + "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF", + "" + ], + "valid_test_cases": [ + { + "key_indices": [0, 1, 2], + "nonce_indices": [0, 1, 2], + "aggnonce_index": 0, + "msg_index": 0, + "signer_index": 0, + "expected": "012ABBCB52B3016AC03AD82395A1A415C48B93DEF78718E62A7A90052FE224FB" + }, + { + "key_indices": [1, 0, 2], + "nonce_indices": [1, 0, 2], + "aggnonce_index": 0, + "msg_index": 0, + "signer_index": 1, + "expected": "9FF2F7AAA856150CC8819254218D3ADEEB0535269051897724F9DB3789513A52" + }, + { + "key_indices": [1, 2, 0], + "nonce_indices": [1, 2, 0], + "aggnonce_index": 0, + "msg_index": 0, + "signer_index": 2, + "expected": "FA23C359F6FAC4E7796BB93BC9F0532A95468C539BA20FF86D7C76ED92227900" + }, + { + "key_indices": [0, 1], + "nonce_indices": [0, 3], + "aggnonce_index": 1, + "msg_index": 0, + "signer_index": 0, + "expected": "AE386064B26105404798F75DE2EB9AF5EDA5387B064B83D049CB7C5E08879531", + "comment": "Both halves of aggregate nonce correspond to point at infinity" + }, + { + "key_indices": [0, 1, 2], + "nonce_indices": [0, 1, 2], + "aggnonce_index": 0, + "msg_index": 1, + "signer_index": 0, + "expected": "D7D63FFD644CCDA4E62BC2BC0B1D02DD32A1DC3030E155195810231D1037D82D", + "comment": "Empty message" + } + ], + "sign_error_test_cases": [ + { + "key_indices": [1, 0, 3], + "aggnonce_index": 0, + "msg_index": 0, + "error": { + "type": "invalid_contribution", + "signer": 2, + "contrib": "pubkey" + }, + "comment": "Signer 2 provided an invalid public key" + }, + { + "key_indices": [1, 2, 0], + "aggnonce_index": 2, + "msg_index": 0, + "error": { + "type": "invalid_contribution", + "signer": null, + "contrib": "aggnonce" + }, + "comment": "Aggregate nonce is invalid due wrong tag, 0x04, in the first half" + }, + { + "key_indices": [1, 2, 0], + "aggnonce_index": 3, + "msg_index": 0, + "error": { + "type": "invalid_contribution", + "signer": null, + "contrib": "aggnonce" + }, + "comment": "Aggregate nonce is invalid because the second half does not correspond to an X coordinate" + }, + { + "key_indices": [1, 2, 0], + "aggnonce_index": 4, + "msg_index": 0, + "error": { + "type": "invalid_contribution", + "signer": null, + "contrib": "aggnonce" + }, + "comment": "Aggregate nonce is invalid because second half exceeds field size" + } + ], + "verify_fail_test_cases": [ + { + "sig": "97AC833ADCB1AFA42EBF9E0725616F3C9A0D5B614F6FE283CEAAA37A8FFAF406", + "key_indices": [0, 1, 2], + "nonce_indices": [0, 1, 2], + "msg_index": 0, + "signer_index": 0, + "comment": "Wrong signature (which is equal to the negation of valid signature)" + }, + { + "sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B", + "key_indices": [0, 1, 2], + "nonce_indices": [0, 1, 2], + "msg_index": 0, + "signer_index": 1, + "comment": "Wrong signer" + }, + { + "sig": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", + "key_indices": [0, 1, 2], + "nonce_indices": [0, 1, 2], + "msg_index": 0, + "signer_index": 0, + "comment": "Signature exceeds group size" + } + ], + "verify_error_test_cases": [ + { + "sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B", + "key_indices": [0, 1, 2], + "nonce_indices": [4, 1, 2], + "msg_index": 0, + "signer_index": 0, + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubnonce" + }, + "comment": "Invalid pubnonce" + }, + { + "sig": "68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B", + "key_indices": [3, 1, 2], + "nonce_indices": [0, 1, 2], + "msg_index": 0, + "signer_index": 0, + "error": { + "type": "invalid_contribution", + "signer": 0, + "contrib": "pubkey" + }, + "comment": "Invalid pubkey" + } + ] +} diff --git a/schnorr_fun/tests/musig/tweak_vectors.json b/schnorr_fun/tests/musig/tweak_vectors.json new file mode 100644 index 00000000..820e3fb3 --- /dev/null +++ b/schnorr_fun/tests/musig/tweak_vectors.json @@ -0,0 +1,75 @@ +{ + "sk": "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671", + "pubkeys": [ + "03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9", + "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659" + ], + "secnonce": "508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7", + "pnonces": [ + "0337C87821AFD50A8644D820A8F3E02E499C931865C2360FB43D0A0D20DAFE07EA0287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480", + "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046" + ], + "aggnonce": "028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9", + "tweaks": [ + "E8F791FF9225A2AF0102AFFF4A9A723D9612A682A25EBE79802B263CDFCD83BB", + "AE2EA797CC0FE72AC5B97B97F3C6957D7E4199A167A58EB08BCAFFDA70AC0455", + "F52ECBC565B3D8BEA2DFD5B75A4F457E54369809322E4120831626F290FA87E0", + "1969AD73CC177FA0B4FCED6DF1F7BF9907E665FDE9BA196A74FED0A3CF5AEF9D", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141" + ], + "msg": "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF", + "valid_test_cases": [ + { + "key_indices": [1, 2, 0], + "nonce_indices": [1, 2, 0], + "tweak_indices": [0], + "is_xonly": [true], + "signer_index": 2, + "expected": "E28A5C66E61E178C2BA19DB77B6CF9F7E2F0F56C17918CD13135E60CC848FE91", + "comment": "A single x-only tweak" + }, + { + "key_indices": [1, 2, 0], + "nonce_indices": [1, 2, 0], + "tweak_indices": [0], + "is_xonly": [false], + "signer_index": 2, + "expected": "38B0767798252F21BF5702C48028B095428320F73A4B14DB1E25DE58543D2D2D", + "comment": "A single plain tweak" + }, + { + "key_indices": [1, 2, 0], + "nonce_indices": [1, 2, 0], + "tweak_indices": [0, 1], + "is_xonly": [false, true], + "signer_index": 2, + "expected": "408A0A21C4A0F5DACAF9646AD6EB6FECD7F7A11F03ED1F48DFFF2185BC2C2408", + "comment": "A plain tweak followed by an x-only tweak" + }, + { + "key_indices": [1, 2, 0], + "nonce_indices": [1, 2, 0], + "tweak_indices": [0, 1, 2, 3], + "is_xonly": [true, false, true, false], + "signer_index": 2, + "expected": "B255FDCAC27B40C7CE7848E2D3B7BF5EA0ED756DA81565AC804CCCA3E1D5D239", + "comment": "Four tweaks: x-only, plain, x-only, plain" + } + ], + "error_test_cases": [ + { + "key_indices": [1, 2, 0], + "nonce_indices": [1, 2, 0], + "tweak_indices": [4], + "is_xonly": [false], + "signer_index": 2, + "error": { + "type": "value", + "message": "The tweak must be less than n." + }, + "comment": "Tweak is invalid because it exceeds group size" + } + ] +} diff --git a/schnorr_fun/tests/musig_keyagg.rs b/schnorr_fun/tests/musig_keyagg.rs new file mode 100644 index 00000000..068a91f2 --- /dev/null +++ b/schnorr_fun/tests/musig_keyagg.rs @@ -0,0 +1,99 @@ +#![cfg(feature = "serde")] +use schnorr_fun::{ + fun::{marker::*, Point, Scalar}, + musig, +}; +static TEST_JSON: &'static str = include_str!("musig/key_agg_vectors.json"); +use serde_crate as serde; + +#[derive(serde::Deserialize, Clone, Copy, Debug)] +#[serde(crate = "serde_crate", untagged)] +pub enum Maybe { + Valid(T), + Invalid(&'static str), +} + +impl Maybe { + fn unwrap(self) -> T { + match self { + Maybe::Valid(t) => t, + Maybe::Invalid(string) => panic!("unwrapped an invalid Maybe: {}", string), + } + } +} +#[derive(serde::Deserialize)] +#[serde(crate = "serde_crate")] +pub struct TestCases { + #[serde(bound(deserialize = "Maybe: serde::de::Deserialize<'de>"))] + pubkeys: Vec>, + #[serde(default)] + #[serde(bound(deserialize = "Maybe>: serde::de::Deserialize<'de>"))] + tweaks: Vec>>, + valid_test_cases: Vec, + error_test_cases: Vec, +} + +#[derive(serde::Deserialize)] +#[serde(crate = "serde_crate")] +pub struct TestCase { + key_indices: Vec, + #[serde(default)] + tweak_indices: Vec, + #[serde(default)] + is_xonly: Vec, + #[allow(dead_code)] + error: Option, + expected: Option>, +} + +#[test] +fn musig_key_agg() { + let test_cases = serde_json::from_str::(TEST_JSON).unwrap(); + + for test_case in &test_cases.valid_test_cases { + run_test(&test_cases, test_case); + } + + for test_case in &test_cases.error_test_cases { + let result = std::panic::catch_unwind(|| { + run_test(&test_cases, test_case); + }); + + assert!(result.is_err()); + } +} + +fn run_test(test_cases: &TestCases, test_case: &TestCase) { + let musig = musig::new_without_nonce_generation::(); + + let pubkeys = test_case + .key_indices + .iter() + .map(|i| test_cases.pubkeys[*i].unwrap()) + .collect(); + + let mut agg_key = musig.new_agg_key(pubkeys); + + let mut tweaks = test_case + .tweak_indices + .iter() + .map(|i| test_cases.tweaks[*i].unwrap()); + + let mut tweak_is_xonly = test_case.is_xonly.clone(); + + while tweak_is_xonly.get(0) == Some(&false) { + tweak_is_xonly.remove(0); + agg_key = agg_key.tweak(tweaks.next().unwrap()).unwrap(); + } + + let mut agg_key = agg_key.into_xonly_key(); + + while tweak_is_xonly.get(0) == Some(&true) { + tweak_is_xonly.remove(0); + agg_key = agg_key.tweak(tweaks.next().unwrap()).unwrap(); + } + + if let Some(expected) = &test_case.expected { + assert_eq!(agg_key.agg_public_key(), *expected); + } +} diff --git a/schnorr_fun/tests/musig_sign_verify.rs b/schnorr_fun/tests/musig_sign_verify.rs new file mode 100644 index 00000000..46095e3c --- /dev/null +++ b/schnorr_fun/tests/musig_sign_verify.rs @@ -0,0 +1,173 @@ +#![cfg(feature = "serde")] +use schnorr_fun::{ + binonce, + fun::{marker::*, Point, Scalar}, + musig::{self, NonceKeyPair}, + Message, +}; +static TEST_JSON: &'static str = include_str!("musig/sign_verify_vectors.json"); +use secp256kfun::hex; +use serde_crate as serde; + +#[derive(serde::Deserialize, Clone, Copy, Debug)] +#[serde(crate = "serde_crate", untagged)] +pub enum Maybe { + Valid(T), + Invalid(&'static str), +} + +impl Maybe { + fn unwrap(self) -> T { + match self { + Maybe::Valid(t) => t, + Maybe::Invalid(string) => panic!("unwrapped an invalid Maybe: {}", string), + } + } +} +#[derive(serde::Deserialize)] +#[serde(crate = "serde_crate")] +pub struct TestCases { + sk: Scalar, + secnonce: NonceKeyPair, + #[serde(bound(deserialize = "Maybe: serde::de::Deserialize<'de>"))] + pubkeys: Vec>, + #[serde(bound(deserialize = "Maybe: serde::de::Deserialize<'de>"))] + pnonces: Vec>, + #[serde(bound(deserialize = "Maybe>: serde::de::Deserialize<'de>"))] + aggnonces: Vec>>, + msgs: Vec, + #[serde(bound(deserialize = "TestCase: serde::de::Deserialize<'de>"))] + valid_test_cases: Vec, + verify_error_test_cases: Vec, + verify_fail_test_cases: Vec, + sign_error_test_cases: Vec, +} + +#[derive(serde::Deserialize)] +#[serde(crate = "serde_crate")] +pub struct TestCase { + #[serde(bound(deserialize = "Maybe>: serde::de::Deserialize<'de>"))] + sig: Option>>, + key_indices: Vec, + nonce_indices: Option>, + aggnonce_index: Option, + msg_index: usize, + signer_index: Option, + expected: Option, + #[allow(dead_code)] + error: Option, +} + +#[test] +fn musig_sign_verify() { + let test_cases = serde_json::from_str::(TEST_JSON).unwrap(); + let musig = musig::new_without_nonce_generation::(); + let keypair = musig.new_keypair(test_cases.sk.clone()); + + for test_case in &test_cases.valid_test_cases { + let pubkeys = test_case + .key_indices + .iter() + .map(|i| test_cases.pubkeys[*i].unwrap()) + .collect(); + let pubnonces = test_case + .nonce_indices + .clone() + .unwrap() + .iter() + .map(|i| test_cases.pnonces[*i].unwrap()) + .collect(); + let _aggnonce = test_cases.aggnonces[test_case.aggnonce_index.unwrap()].unwrap(); + let msg = hex::decode(&test_cases.msgs[test_case.msg_index]).unwrap(); + let agg_key = musig.new_agg_key(pubkeys).into_xonly_key(); + let session = musig.start_sign_session(&agg_key, pubnonces, Message::raw(&msg[..])); + let partial_sig = musig.sign( + &agg_key, + &session, + test_case.signer_index.unwrap(), + &keypair, + test_cases.secnonce.clone(), + ); + assert_eq!(partial_sig, test_case.expected.clone().unwrap()); + assert!(musig.verify_partial_signature( + &agg_key, + &session, + test_case.signer_index.unwrap(), + partial_sig + )); + } + + for test_case in &test_cases.sign_error_test_cases { + let result = std::panic::catch_unwind(|| { + let pubkeys = test_case + .key_indices + .iter() + .map(|i| test_cases.pubkeys[*i].unwrap()) + .collect(); + let _agg_key = musig.new_agg_key(pubkeys).into_xonly_key(); + let _msg = hex::decode(&test_cases.msgs[test_case.msg_index]).unwrap(); + let _aggnonce = test_cases.aggnonces[test_case.aggnonce_index.unwrap()].unwrap(); + }); + + assert!(result.is_err()); + } + + for test_case in &test_cases.verify_fail_test_cases { + let sig = test_case.sig.unwrap(); + let result = std::panic::catch_unwind(|| { + let partial_sig = sig.unwrap(); + let pubkeys = test_case + .key_indices + .iter() + .map(|i| test_cases.pubkeys[*i].unwrap()) + .collect(); + + let agg_key = musig.new_agg_key(pubkeys).into_xonly_key(); + + let pubnonces = test_case + .nonce_indices + .clone() + .unwrap() + .iter() + .map(|i| test_cases.pnonces[*i].unwrap()) + .collect(); + + let msg = hex::decode(&test_cases.msgs[test_case.msg_index]).unwrap(); + let session = musig.start_sign_session(&agg_key, pubnonces, Message::raw(&msg[..])); + + assert!(musig.verify_partial_signature( + &agg_key, + &session, + test_case.signer_index.unwrap(), + partial_sig + )); + }); + + assert!(result.is_err()); + } + + for test_case in &test_cases.verify_error_test_cases { + let sig = test_case.sig.unwrap(); + + let result = std::panic::catch_unwind(|| { + let _partial_sig = sig.unwrap(); + let _pubkeys = test_case + .key_indices + .iter() + .map(|i| test_cases.pubkeys[*i].unwrap()) + .collect::>(); + + let _pubnonces = test_case + .nonce_indices + .clone() + .unwrap() + .iter() + .map(|i| test_cases.pnonces[*i].unwrap()) + .collect::>(); + + let _msg = hex::decode(&test_cases.msgs[test_case.msg_index]).unwrap(); + }); + + assert!(result.is_err()); + } +} diff --git a/schnorr_fun/tests/musig_tweak.rs b/schnorr_fun/tests/musig_tweak.rs new file mode 100644 index 00000000..93181918 --- /dev/null +++ b/schnorr_fun/tests/musig_tweak.rs @@ -0,0 +1,141 @@ +#![allow(warnings)] +#![cfg(feature = "serde")] +use std::{rc::Rc, sync::Arc}; + +use schnorr_fun::{ + binonce, + fun::{marker::*, Point, Scalar}, + musig::{self, NonceKeyPair}, + Message, +}; +static TEST_JSON: &'static str = include_str!("musig/tweak_vectors.json"); +use secp256kfun::hex; +use serde_crate as serde; + +#[derive(serde::Deserialize, Clone, Debug)] +#[serde(crate = "serde_crate", untagged)] +pub enum Maybe { + Valid(T), + Invalid(&'static str), +} + +impl Maybe { + fn unwrap(self) -> T { + match self { + Maybe::Valid(t) => t, + Maybe::Invalid(string) => panic!("unwrapped an invalid Maybe: {}", string), + } + } +} +impl Copy for Maybe {} + +#[derive(serde::Deserialize)] +#[serde(crate = "serde_crate")] +pub struct TestCases { + sk: Scalar, + secnonce: NonceKeyPair, + #[serde(bound(deserialize = "Maybe: serde::de::Deserialize<'de>"))] + pubkeys: Vec>, + #[serde(bound(deserialize = "Maybe: serde::de::Deserialize<'de>"))] + pnonces: Vec>, + aggnonce: binonce::Nonce, + #[serde(bound(deserialize = "Maybe>: serde::de::Deserialize<'de>"))] + tweaks: Vec>>, + msg: String, + valid_test_cases: Vec, + error_test_cases: Vec, +} + +#[derive(serde::Deserialize)] +#[serde(crate = "serde_crate")] +pub struct TestCase { + #[serde(bound(deserialize = "Maybe>: serde::de::Deserialize<'de>"))] + sig: Option>>, + key_indices: Vec, + nonce_indices: Option>, + tweak_indices: Vec, + is_xonly: Vec, + signer_index: Option, + expected: Option, + #[allow(dead_code)] + error: Option, +} + +#[test] +fn musig_tweak_tests() { + let test_cases = serde_json::from_str::(TEST_JSON).unwrap(); + + for test_case in &test_cases.valid_test_cases { + run_test(&test_cases, test_case); + } + + for test_case in &test_cases.error_test_cases { + let result = std::panic::catch_unwind(|| { + run_test(&test_cases, test_case); + }); + assert!(result.is_err()); + } +} + +fn run_test(test_cases: &TestCases, test_case: &TestCase) { + let musig = musig::new_without_nonce_generation::(); + let msg = hex::decode(&test_cases.msg).unwrap(); + let keypair = musig.new_keypair(test_cases.sk.clone()); + + let pubkeys = test_case + .key_indices + .iter() + .map(|i| test_cases.pubkeys[*i].unwrap()) + .collect(); + let pubnonces = test_case + .nonce_indices + .clone() + .unwrap() + .iter() + .map(|i| test_cases.pnonces[*i].unwrap()) + .collect(); + + let mut tweaks = test_case + .tweak_indices + .iter() + .map(|i| test_cases.tweaks[*i].unwrap()); + + let mut tweak_is_xonly = test_case.is_xonly.clone(); + let mut agg_key = musig.new_agg_key(pubkeys); + + while tweak_is_xonly.get(0) == Some(&false) { + tweak_is_xonly.remove(0); + agg_key = agg_key.tweak(tweaks.next().unwrap()).unwrap(); + } + + let mut agg_key = agg_key.into_xonly_key(); + + while tweak_is_xonly.get(0) == Some(&true) { + tweak_is_xonly.remove(0); + agg_key = agg_key.tweak(tweaks.next().unwrap()).unwrap(); + } + + if !tweak_is_xonly.is_empty() { + // XXX: we can't run this test because it does an plain tweak after an xonly tweak + return; + } + + let session = musig.start_sign_session(&agg_key, pubnonces, Message::raw(&msg[..])); + let partial_sig = musig.sign( + &agg_key, + &session, + test_case.signer_index.unwrap(), + &keypair, + test_cases.secnonce.clone(), + ); + + if let Some(expected) = test_case.expected.clone() { + assert_eq!(partial_sig, expected); + } + assert!(musig.verify_partial_signature( + &agg_key, + &session, + test_case.signer_index.unwrap(), + partial_sig + )); +} From 22bd6732aea5605c8f1d5df38475af6f84919dcf Mon Sep 17 00:00:00 2001 From: LLFourn Date: Tue, 16 Aug 2022 15:29:01 +0800 Subject: [PATCH 7/8] s/Bip340AggKey/XOnlyAggKey/g --- schnorr_fun/src/musig.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/schnorr_fun/src/musig.rs b/schnorr_fun/src/musig.rs index 4f4b521b..edb53d8b 100644 --- a/schnorr_fun/src/musig.rs +++ b/schnorr_fun/src/musig.rs @@ -151,7 +151,7 @@ impl AggKey { /// The resulting key is equal to the existing key plus `tweak * G`. The tweak mutates the /// public key while still allowing the original set of signers to sign under the new key. /// This function is appropriate for doing [BIP32] tweaks before calling `into_xonly_key`. - /// It **is not** appropriate for doing taproot tweaking which must be done on a [`Bip340AggKey`]. + /// It **is not** appropriate for doing taproot tweaking which must be done on a [`XOnlyAggKey`]. /// /// ## Return value /// @@ -159,7 +159,7 @@ impl AggKey { /// secret key it returns `None`. /// /// [BIP32]: https://bips.xyz/32 - /// [`Bip340AggKey`]: crate::musig::Bip340AggKey + /// [`XOnlyAggKey`]: crate::musig::XOnlyAggKey pub fn tweak(self, tweak: Scalar) -> Option { let agg_key = g!(self.agg_key + tweak * G).normalize().mark::()?; let tweak = s!(self.tweak + tweak).mark::(); @@ -172,14 +172,14 @@ impl AggKey { }) } - /// Convert the key into an `Bip340AggKey`. + /// Convert the key into an `XOnlyAggKey`. /// /// This is the BIP340 compatible version of the key which you can put in a segwitv1 output and create BIP340 signatures under. - pub fn into_xonly_key(self) -> Bip340AggKey { + pub fn into_xonly_key(self) -> XOnlyAggKey { let (agg_key, needs_negation) = self.agg_key.into_point_with_even_y(); let mut tweak = self.tweak; tweak.conditional_negate(needs_negation); - Bip340AggKey { + XOnlyAggKey { keys: self.keys, coefs: self.coefs, needs_negation, @@ -193,7 +193,7 @@ impl AggKey { /// /// [BIP340]: https://bips.xyz/340 #[derive(Debug, Clone)] -pub struct Bip340AggKey { +pub struct XOnlyAggKey { /// The keys involved in the key aggregation. keys: Vec, /// The coefficients of each key @@ -206,7 +206,7 @@ pub struct Bip340AggKey { agg_key: Point, } -impl Bip340AggKey { +impl XOnlyAggKey { /// The aggregate key as a `Point` pub fn agg_public_key(&self) -> Point { self.agg_key @@ -318,7 +318,7 @@ impl + Clone, NG: NonceGen> MuSig> /// [`Deterministic`]: secp256kfun::nonce::Deterministic /// [`start_sign_session`]: Self::start_sign_session /// [`NonceKeyPair`]: crate::binonce::NonceKeyPair - pub fn gen_nonces(&self, secret: &Scalar, agg_key: &Bip340AggKey, sid: &[u8]) -> NonceKeyPair { + pub fn gen_nonces(&self, secret: &Scalar, agg_key: &XOnlyAggKey, sid: &[u8]) -> NonceKeyPair { let r1 = derive_nonce!( nonce_gen => self.schnorr.nonce_gen(), secret => secret, @@ -399,7 +399,7 @@ impl + Clone, NG> MuSig> { /// Panics if number of nonces does not align with the keys in `agg_key`. pub fn start_sign_session( &self, - agg_key: &Bip340AggKey, + agg_key: &XOnlyAggKey, nonces: Vec, message: Message<'_, Public>, ) -> SignSession { @@ -437,7 +437,7 @@ impl + Clone, NG> MuSig> { /// [`adaptor`]: crate::adaptor pub fn start_encrypted_sign_session( &self, - agg_key: &Bip340AggKey, + agg_key: &XOnlyAggKey, nonces: Vec, message: Message<'_, Public>, encryption_key: &Point, @@ -458,7 +458,7 @@ impl + Clone, NG> MuSig> { fn _start_sign_session( &self, - agg_key: &Bip340AggKey, + agg_key: &XOnlyAggKey, nonces: Vec, message: Message<'_, Public>, encryption_key: &Point, @@ -518,7 +518,7 @@ impl + Clone, NG> MuSig> { /// Generates a partial signature (or partial encrypted signature depending on `T`) for the local_secret_nonce. pub fn sign( &self, - agg_key: &Bip340AggKey, + agg_key: &XOnlyAggKey, session: &SignSession, my_index: usize, keypair: &KeyPair, @@ -552,7 +552,7 @@ impl + Clone, NG> MuSig> { /// Panics when `index` is equal to or greater than the number of keys in the agg_key. pub fn verify_partial_signature( &self, - agg_key: &Bip340AggKey, + agg_key: &XOnlyAggKey, session: &SignSession, index: usize, partial_sig: Scalar, @@ -581,7 +581,7 @@ impl + Clone, NG> MuSig> { /// [`verify_partial_signature`]: Self::verify_partial_signature pub fn combine_partial_signatures( &self, - agg_key: &Bip340AggKey, + agg_key: &XOnlyAggKey, session: &SignSession, partial_sigs: impl IntoIterator>, ) -> Signature { @@ -598,7 +598,7 @@ impl + Clone, NG> MuSig> { /// [`verify_partial_signature`]: Self::verify_partial_signature pub fn combine_partial_encrypted_signatures( &self, - agg_key: &Bip340AggKey, + agg_key: &XOnlyAggKey, session: &SignSession, partial_encrypted_sigs: impl IntoIterator>, ) -> EncryptedSignature { @@ -613,7 +613,7 @@ impl + Clone, NG> MuSig> { fn _combine_partial_signatures( &self, - agg_key: &Bip340AggKey, + agg_key: &XOnlyAggKey, session: &SignSession, partial_sigs: impl IntoIterator>, ) -> (Point, Scalar) { From a1d25d79967de1acbef00ab2e0b7c615373b13ff Mon Sep 17 00:00:00 2001 From: LLFourn Date: Tue, 16 Aug 2022 17:46:58 +0800 Subject: [PATCH 8/8] [musig] Extract nonce generation to binonce Add a method to `NonceKeyPair` so we can use it in FROST in future. --- schnorr_fun/src/binonce.rs | 51 ++++++++++++++++++++++- schnorr_fun/src/message.rs | 8 ++++ schnorr_fun/src/musig.rs | 84 +++++++++++++++----------------------- secp256kfun/src/slice.rs | 5 +++ 4 files changed, 95 insertions(+), 53 deletions(-) diff --git a/schnorr_fun/src/binonce.rs b/schnorr_fun/src/binonce.rs index 3fa57b40..74c607cd 100644 --- a/schnorr_fun/src/binonce.rs +++ b/schnorr_fun/src/binonce.rs @@ -4,7 +4,8 @@ //! 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. /// @@ -163,6 +164,54 @@ impl NonceKeyPair { pub fn public(&self) -> Nonce { 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>, + message: Option>, + ) -> 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! { diff --git a/schnorr_fun/src/message.rs b/schnorr_fun/src/message.rs index b35765fe..8083b049 100644 --- a/schnorr_fun/src/message.rs +++ b/schnorr_fun/src/message.rs @@ -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 HashInto for Message<'_, S> { diff --git a/schnorr_fun/src/musig.rs b/schnorr_fun/src/musig.rs index edb53d8b..ba4510da 100644 --- a/schnorr_fun/src/musig.rs +++ b/schnorr_fun/src/musig.rs @@ -23,11 +23,11 @@ //! .into_xonly_key(); //! //! // create a unique nonce, and send the public nonce to other parties. -//! let my_nonce = musig.gen_nonces(my_keypair.secret_key(), &agg_key, b"session-id-1337"); +//! let my_nonce = musig.gen_nonces(my_keypair.secret_key(), b"session-id-1337", Some(agg_key.agg_public_key()), None); //! let my_public_nonce = my_nonce.public(); -//! # let p2_nonce = musig.gen_nonces(kp2.secret_key(), &agg_key, b"session-id-1337"); +//! # let p2_nonce = musig.gen_nonces(kp2.secret_key(), b"session-id-1337", Some(agg_key.agg_public_key()), None); //! # let p2_public_nonce = p2_nonce.public(); -//! # let p3_nonce = musig.gen_nonces(kp3.secret_key(), &agg_key, b"session-id-1337"); +//! # let p3_nonce = musig.gen_nonces(kp3.secret_key(), b"session-id-1337", Some(agg_key.agg_public_key()), None); //! # let p3_public_nonce = p3_nonce.public(); //! // collect the public nonces from the other two parties //! let nonces = vec![my_public_nonce, p2_public_nonce, p3_public_nonce]; @@ -63,7 +63,6 @@ pub use crate::binonce::{Nonce, NonceKeyPair}; use crate::{adaptor::EncryptedSignature, Message, Schnorr, Signature, Vec}; use secp256kfun::{ - derive_nonce, digest::{generic_array::typenum::U32, Digest}, g, hash::{HashAdd, Tagged}, @@ -296,45 +295,26 @@ impl + Clone, S> MuSig { } impl + Clone, NG: NonceGen> MuSig> { - /// Generate nonces for signing under your aggregate key. + /// Generate nonces for signing. /// - /// It is very important to carefully consider the implications of your choice of underlying - /// [`NonceGen`]. + /// This method should be used carefully. + /// This calls [`NonceKeyPair::generate`] internally with the `MuSig` instance's `NonceGen`. + /// See documentation for that for more usage info. /// - /// Using a [`Synthetic`] nonce generator will mean you don't have to worry about passing a - /// unique `sid` (session id) to this function for each signing session. The downside is that - /// you must recall the result of `gen_nonces` somewhere and store it for use when you want to - /// start the signing session with [`start_sign_session`]. - /// - /// Using a [`Deterministic`] nonce generator means you **must** never start two signing - /// sessions with nonces generated from the same `sid`. If you do your secret key will be - /// recoverable from the two partial signatures you created with the same nonce. - /// - /// Note that you daon't have to use this API. You can create [`NonceKeyPair`]s manually in your - /// own application specific way (but be careful!). - /// - /// [`NonceGen`]: secp256kfun::nonce::NonceGen - /// [`Synthetic`]: secp256kfun::nonce::Synthetic - /// [`Deterministic`]: secp256kfun::nonce::Deterministic - /// [`start_sign_session`]: Self::start_sign_session - /// [`NonceKeyPair`]: crate::binonce::NonceKeyPair - pub fn gen_nonces(&self, secret: &Scalar, agg_key: &XOnlyAggKey, sid: &[u8]) -> NonceKeyPair { - let r1 = derive_nonce!( - nonce_gen => self.schnorr.nonce_gen(), - secret => secret, - public => [ b"r1", agg_key.agg_public_key(), sid] - ); - let r2 = derive_nonce!( - nonce_gen => self.schnorr.nonce_gen(), - secret => secret, - public => [ b"r2", agg_key.agg_public_key(), sid] - ); - let R1 = g!(r1 * G).normalize(); - let R2 = g!(r2 * G).normalize(); - NonceKeyPair { - public: Nonce([R1, R2]), - secret: [r1, r2], - } + /// [`NonceKeyPair::generate`]: crate::binonce::NonceKeyPair::generate + pub fn gen_nonces( + &self, + secret: &Scalar, + session_id: &[u8], + public_key: Option>, + message: Option>, + ) -> NonceKeyPair { + NonceKeyPair::generate(self.nonce_gen(), secret, session_id, public_key, message) + } + + /// Gets the nonce generator from the underlying Schnorr instance. + pub fn nonce_gen(&self) -> &NG { + self.schnorr.nonce_gen() } } @@ -504,8 +484,7 @@ impl + Clone, NG> MuSig> { .into_point_with_even_y(); for R_i in &mut Rs { - R_i.0[0] = R_i.0[0].conditional_negate(r_needs_negation); - R_i.0[1] = R_i.0[1].conditional_negate(r_needs_negation); + R_i.conditional_negate(r_needs_negation); } let c = self @@ -742,14 +721,14 @@ mod test { assert_eq!(agg_key1.agg_public_key(), agg_key2.agg_public_key()); assert_eq!(agg_key1.agg_public_key(), agg_key3.agg_public_key()); + let message = + Message::::plain("test", b"Chancellor on brink of second bailout for banks"); - let p1_nonce = musig.gen_nonces(keypair1.secret_key(), &agg_key1, b"test"); - let p2_nonce = musig.gen_nonces(keypair2.secret_key(), &agg_key2, b"test"); - let p3_nonce = musig.gen_nonces(keypair3.secret_key(), &agg_key3, b"test"); + let p1_nonce = musig.gen_nonces(keypair1.secret_key(), b"test", Some(agg_key1.agg_public_key()), Some(message)); + let p2_nonce = musig.gen_nonces(keypair2.secret_key(), b"test", Some(agg_key2.agg_public_key()), Some(message)); + let p3_nonce = musig.gen_nonces(keypair3.secret_key(), b"test", Some(agg_key3.agg_public_key()), Some(message)); let nonces = vec![p1_nonce.public, p2_nonce.public, p3_nonce.public]; - let message = - Message::::plain("test", b"Chancellor on brink of second bailout for banks"); let p1_session = musig .start_sign_session( @@ -834,13 +813,14 @@ mod test { keypair3.public_key(), ]).into_xonly_key(); - let p1_nonce = musig.gen_nonces(keypair1.secret_key(), &agg_key, b"test"); - let p2_nonce = musig.gen_nonces(keypair2.secret_key(), &agg_key2, b"test"); - let p3_nonce = musig.gen_nonces(keypair3.secret_key(), &agg_key3, b"test"); - let nonces = vec![p1_nonce.public, p2_nonce.public, p3_nonce.public]; let message = Message::::plain("test", b"Chancellor on brink of second bailout for banks"); + let p1_nonce = musig.gen_nonces(keypair1.secret_key(), b"test" ,Some(agg_key.agg_public_key()), Some(message)); + let p2_nonce = musig.gen_nonces(keypair2.secret_key(), b"test", Some(agg_key2.agg_public_key()), Some(message)); + let p3_nonce = musig.gen_nonces(keypair3.secret_key(), b"test", Some(agg_key3.agg_public_key()), Some(message)); + let nonces = vec![p1_nonce.public, p2_nonce.public, p3_nonce.public]; + let mut p1_session = musig .start_encrypted_sign_session( &agg_key, diff --git a/secp256kfun/src/slice.rs b/secp256kfun/src/slice.rs index 3b768dc0..122cd3df 100644 --- a/secp256kfun/src/slice.rs +++ b/secp256kfun/src/slice.rs @@ -68,6 +68,11 @@ impl<'a, S> Slice<'a, S> { secrecy: PhantomData::, } } + + /// Gets the inner slice + pub fn as_inner(self) -> &'a [u8] { + &self.inner + } } impl<'a, S> From> for &'a [u8] {