Skip to content

Commit

Permalink
Use new BIP340 domain separation scheme
Browse files Browse the repository at this point in the history
Abandoned the old one (mention by real-or-random) to the one suggested
here:

sipa/bips#207 (comment)
  • Loading branch information
LLFourn committed Sep 15, 2020
1 parent 0c6f5c7 commit 73b2fe9
Showing 1 changed file with 48 additions and 5 deletions.
53 changes: 48 additions & 5 deletions schnorr_fun/src/schnorr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub struct Schnorr<CH, NG = (), GT = BasePoint> {
///
/// [`NonceGen`]: crate::nonce::NonceGen
nonce_challenge_bundle: NonceChallengeBundle<CH, NG>,
application_tag: Option<[u8; 64]>,
}

/// Describes the kind of messages that will be signed with a [`Schnorr`] instance.
Expand All @@ -48,7 +49,12 @@ pub enum MessageKind {
Plain {
/// You must provide a tag to separate signatures from your application
/// from other applications. If two [`Schnorr`] instances are created
/// with a different `tag` then a signature valid for one will never be valid for the other.
/// with a different `tag` then a signature valid for one will never be
/// valid for the other. It will also never be valid for `Prehashed`
/// instances. The specific method of domain separation used is
/// described [here].
///
/// [here]: https://github.com/sipa/bips/issues/207#issuecomment-673681901
tag: &'static str,
},
}
Expand Down Expand Up @@ -107,13 +113,24 @@ where
}
.add_protocol_tag("BIP0340");

if let MessageKind::Plain { tag } = msgkind {
nonce_challenge_bundle = nonce_challenge_bundle.add_application_tag(tag);
}
let application_tag = match msgkind {
MessageKind::Prehashed => None,
MessageKind::Plain { tag } => {
assert!(tag.len() <= 64);
// Only add the application tag to nonce gen since we will be
// separately adding the tag to the challenge hash in self.challenge()
nonce_challenge_bundle.nonce_gen =
nonce_challenge_bundle.nonce_gen.add_application_tag(tag);
let mut application_tag = [0u8; 64];
application_tag[..tag.len()].copy_from_slice(tag.as_bytes());
Some(application_tag)
}
};

Self {
G: fun::G.clone(),
nonce_challenge_bundle,
application_tag,
}
}
}
Expand Down Expand Up @@ -213,7 +230,14 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
/// ```
pub fn challenge<S: Secrecy>(&self, R: &XOnly, X: &XOnly, m: Slice<'_, S>) -> Scalar<S, Zero> {
let hash = self.nonce_challenge_bundle.challenge_hash.clone();
let challenge = Scalar::from_hash(hash.add(R).add(X).add(&m));
let mut hash = hash.add(R).add(X);

if let Some(tag) = self.application_tag {
hash.update(&tag[..]);
}

let challenge = Scalar::from_hash(hash.add(&m));

challenge
// Since the challenge pre-image is adversarially controlled we
// conservatively allow for it to be zero
Expand Down Expand Up @@ -258,6 +282,8 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {

#[cfg(test)]
pub mod test {
use fun::nonce::Deterministic;

use super::*;
use crate::fun::TEST_SOUNDNESS;
crate::fun::test_plus_wasm! {
Expand Down Expand Up @@ -304,5 +330,22 @@ pub mod test {
assert_ne!(signature_1.R, signature_4.R);
}
}

fn deterministic_nonces_for_different_message_kinds() {
use sha2::Sha256;
let schnorr_1 = Schnorr::<Sha256,_>::new(Deterministic::<Sha256>::default(), MessageKind::Prehashed);
let schnorr_2 = Schnorr::<Sha256,_>::new(Deterministic::<Sha256>::default(), MessageKind::Plain { tag: "two" });
let schnorr_3 = Schnorr::<Sha256,_>::new(Deterministic::<Sha256>::default(), MessageKind::Plain { tag: "three" });
let x = Scalar::from_str("18451f9e08af9530814243e202a4a977130e672079f5c14dcf15bd4dee723072").unwrap();
let keypair = schnorr_1.new_keypair(x);
let message = b"foo".as_ref().mark::<Public>();
assert_ne!(schnorr_1.sign(&keypair, message).R, schnorr_2.sign(&keypair, message).R);
assert_ne!(schnorr_1.sign(&keypair, message).R, schnorr_3.sign(&keypair, message).R);

// make sure deterministic signatures don't change
use core::str::FromStr;
assert_eq!(schnorr_1.sign(&keypair, message), Signature::<Public>::from_str("fe9e5d0319d5d221988d6fd7fe1c4bedd2fb4465f592f1002f461503332a266977bb4a0b00c00d07072c796212cbea0957ebaaa5139143761c45d997ebe36cbe").unwrap());
assert_eq!(schnorr_2.sign(&keypair, message), Signature::<Public>::from_str("6cad3863f4d01494ce4c40f3422e4916c616356d730bc4ffe33e386f038b328ba1dc9621e626992c2612f33cdb35f4be4badc464c1f4bf3de15517e7aedcf615").unwrap());
}
}
}

0 comments on commit 73b2fe9

Please sign in to comment.