Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement MuSig2 multisignature scheme #80

Merged
merged 10 commits into from
Jan 13, 2022
4 changes: 2 additions & 2 deletions schnorr_fun/src/adaptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ where
// key before decrypting it
r.conditional_negate(needs_negation);

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

EncryptedSignature {
Expand Down Expand Up @@ -216,7 +216,7 @@ where
// !needs_negation => R_hat = R - Y
let R_hat = g!(R + { Y.conditional_negate(!needs_negation) });

let c = self.challenge(&R.to_xonly(), &X.to_xonly(), message);
let c = self.challenge(R.to_xonly(), X.to_xonly(), message);

R_hat == g!(s_hat * { self.G() } - c * X)
}
Expand Down
8 changes: 4 additions & 4 deletions schnorr_fun/src/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ impl KeyPair {
}

/// Returns a reference to the public key.
pub fn public_key(&self) -> &XOnly {
&self.pk
pub fn public_key(&self) -> XOnly {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any rationale on why you go this way (by value instead ref?)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XOnly is Copy so I think it's conventional to not return references to Copy things. On top of that There's no reason to leak the implementation that keyPair stores an XOnly (it could store a Point<EvenY> instead).

(that comment needs to change though).

self.pk
}

/// Gets a reference to the key-pair as a tuple
Expand All @@ -39,8 +39,8 @@ impl KeyPair {
/// # 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)
pub fn as_tuple(&self) -> (&Scalar, XOnly) {
(&self.sk, self.pk)
}

/// Returns the full `Point<EvenY>` for the public key which is used in [`verify`].
Expand Down
17 changes: 6 additions & 11 deletions schnorr_fun/src/schnorr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ where
);

let R = XOnly::from_scalar_mul(&self.G, &mut r);
let c = self.challenge(&R, X, message);
let c = self.challenge(R, X, message);
let s = s!(r + c * x).mark::<Public>();

Signature { R, s }
Expand Down Expand Up @@ -168,20 +168,15 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
/// let keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
/// let mut r = Scalar::random(&mut rand::thread_rng());
/// let R = XOnly::from_scalar_mul(schnorr.G(), &mut r);
/// let challenge = schnorr.challenge(&R, keypair.public_key(), message);
/// 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));
/// ```
///
/// [BIP-340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
/// [`Secrecy`]: secp256kfun::marker::Secrecy
pub fn challenge<S: Secrecy>(
&self,
R: &XOnly,
X: &XOnly,
m: Message<'_, S>,
) -> Scalar<S, Zero> {
pub fn challenge<S: Secrecy>(&self, R: XOnly, X: XOnly, m: Message<'_, S>) -> Scalar<S, Zero> {
let hash = self.challenge_hash.clone();
let challenge = Scalar::from_hash(hash.add(R).add(X).add(&m));

Expand Down Expand Up @@ -228,9 +223,9 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
) -> bool {
let X = public_key;
let (R, s) = signature.as_tuple();
let c = self.challenge(R, &X.to_xonly(), message);
let c = self.challenge(R, X.to_xonly(), message);
let R_implied = g!(s * self.G - c * X).mark::<Normal>();
R_implied == *R
R_implied == R
}

/// _Anticipates_ a Schnorr signature given the nonce `R` that will be used ahead of time.
Expand All @@ -242,7 +237,7 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
R: &Point<EvenY, impl Secrecy>,
m: Message<'_, impl Secrecy>,
) -> Point<Jacobian, Public, Zero> {
let c = self.challenge(&R.to_xonly(), &X.to_xonly(), m);
let c = self.challenge(R.to_xonly(), X.to_xonly(), m);
g!(R + c * X)
}
}
Expand Down
4 changes: 2 additions & 2 deletions schnorr_fun/src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ impl<S> Signature<S> {
/// # let signature = schnorr_fun::Signature::random(&mut rand::thread_rng());
/// let (R, s) = signature.as_tuple();
/// ```
pub fn as_tuple(&self) -> (&XOnly, &Scalar<S, Zero>) {
(&self.R, &self.s)
pub fn as_tuple(&self) -> (XOnly, &Scalar<S, Zero>) {
(self.R, &self.s)
}

/// Marks the signature with a [`Secrecy`]. If it is marked as `Secret` the
Expand Down
2 changes: 1 addition & 1 deletion schnorr_fun/tests/bip340.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn signing_test_vectors() {
let secret_key = Scalar::<Secret, NonZero>::from_str(line[1]).unwrap();
let expected_public_key = XOnly::from_str(line[2]).unwrap();
let keypair = bip340.new_keypair(secret_key);
assert_eq!(keypair.public_key(), &expected_public_key);
assert_eq!(keypair.public_key(), expected_public_key);
let message = hex::decode(line[4]).unwrap();
let signature = bip340.sign(&keypair, Message::<Public>::raw(&message));
let expected_signature = Signature::<Public>::from_str(line[5]).unwrap();
Expand Down