use secp256kfun::{hash::Hash32, nonce::NoNonces};
use crate::{
Message, Signature,
fun::{
KeyPair, derive_nonce,
hash::{HashAdd, Tag},
nonce::{self, NonceGen},
prelude::*,
rand_core,
},
};
#[derive(Clone, Debug, PartialEq)]
pub struct Schnorr<CH, NG = NoNonces> {
nonce_gen: NG,
challenge_hash: CH,
}
impl<H: Hash32> Schnorr<H, NoNonces> {
pub fn verify_only() -> Self {
Self::new(NoNonces)
}
}
impl<CH, NG> Schnorr<CH, NG> {
pub fn nonce_gen(&self) -> &NG {
&self.nonce_gen
}
}
impl<CH, NG> Schnorr<CH, NG>
where
CH: Tag + Default,
NG: Tag,
{
pub fn new(nonce_gen: NG) -> Self {
Self {
nonce_gen: nonce_gen.tag(b"BIP0340"),
challenge_hash: CH::default().tag(b"BIP0340/challenge"),
}
}
}
impl<CH, NG> Default for Schnorr<CH, NG>
where
CH: Default + Tag,
NG: Default + Tag,
{
fn default() -> Self {
Self::new(NG::default())
}
}
impl<CH, NG> Schnorr<CH, NG>
where
CH: Hash32,
NG: NonceGen,
{
pub fn sign(&self, keypair: &KeyPair<EvenY>, message: Message<'_>) -> Signature {
let (x, X) = keypair.as_tuple();
let mut r = derive_nonce!(
nonce_gen => self.nonce_gen(),
secret => x,
public => [X, message]
);
let R = Point::even_y_from_scalar_mul(G, &mut r);
let c = self.challenge(&R, &X, message);
let s = s!(r + c * x).public();
Signature { R, s }
}
}
impl<NG, CH: Hash32> Schnorr<CH, NG> {
pub fn challenge_hash(&self) -> CH {
self.challenge_hash.clone()
}
pub fn new_keypair(&self, sk: Scalar) -> KeyPair<EvenY> {
KeyPair::new_xonly(sk)
}
pub fn challenge(
&self,
R: &Point<EvenY, impl Secrecy>,
X: &Point<EvenY, impl Secrecy>,
m: Message<'_>,
) -> Scalar<Public, Zero> {
let hash = self.challenge_hash.clone();
let challenge = Scalar::from_hash(hash.add(R).add(X).add(m));
challenge
.mark_zero()
.public()
}
#[must_use]
pub fn verify(
&self,
public_key: &Point<EvenY, impl Secrecy>,
message: Message<'_>,
signature: &Signature,
) -> bool {
let X = public_key;
let (R, s) = signature.as_tuple();
let c = self.challenge(&R, X, message);
let R_implied = g!(s * G - c * X);
R_implied == R
}
pub fn anticipate_signature(
&self,
X: &Point<EvenY, impl Secrecy>,
R: &Point<EvenY, impl Secrecy>,
m: Message<'_>,
) -> Point<NonNormal, Public, Zero> {
let c = self.challenge(R, X, m);
g!(R + c * X)
}
}
pub fn new_with_deterministic_nonces<H>() -> Schnorr<H, nonce::Deterministic<H>>
where
H: Hash32,
{
Schnorr::default()
}
pub fn new_with_synthetic_nonces<H, R>() -> Schnorr<H, nonce::Synthetic<H, nonce::GlobalRng<R>>>
where
H: Hash32,
R: rand_core::RngCore + Default + Clone,
{
Schnorr::default()
}
#[cfg(test)]
mod test {
use crate::fun::nonce::Deterministic;
use super::*;
use crate::fun::proptest::prelude::*;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test as test;
#[test]
fn deterministic_nonces_for_different_message_kinds() {
use core::str::FromStr;
use sha2::Sha256;
let schnorr = Schnorr::<Sha256, _>::new(Deterministic::<Sha256>::default());
let x =
Scalar::from_str("18451f9e08af9530814243e202a4a977130e672079f5c14dcf15bd4dee723072")
.unwrap();
let keypair = schnorr.new_keypair(x);
assert_ne!(
schnorr.sign(&keypair, Message::raw(b"foo")).R,
schnorr.sign(&keypair, Message::new("one", b"foo")).R
);
assert_ne!(
schnorr.sign(&keypair, Message::new("one", b"foo")).R,
schnorr.sign(&keypair, Message::new("two", b"foo")).R
);
assert_eq!(schnorr.sign(&keypair, Message::raw(b"foo")), Signature::from_str("fe9e5d0319d5d221988d6fd7fe1c4bedd2fb4465f592f1002f461503332a266977bb4a0b00c00d07072c796212cbea0957ebaaa5139143761c45d997ebe36cbe").unwrap());
}
proptest! {
#[test]
fn anticipated_signature_on_should_correspond_to_actual_signature(sk in any::<Scalar>()) {
let schnorr = crate::new_with_deterministic_nonces::<sha2::Sha256>();
let keypair = schnorr.new_keypair(sk);
let msg = Message::new(
"test",
b"Chancellor on brink of second bailout for banks",
);
let signature = schnorr.sign(&keypair, msg);
let anticipated_signature = schnorr.anticipate_signature(
&keypair.public_key(),
&signature.R,
msg,
);
assert_eq!(
anticipated_signature,
g!(signature.s * G),
"should anticipate the same value as actual signature"
)
}
#[test]
fn sign_deterministic(s1 in any::<Scalar>(), s2 in any::<Scalar>()) {
let schnorr = crate::new_with_deterministic_nonces::<sha2::Sha256>();
let keypair_1 = schnorr.new_keypair(s1);
let keypair_2 = schnorr.new_keypair(s2);
let msg_atkdwn = Message::new("test", b"attack at dawn");
let msg_rtrtnoon = Message::new("test", b"retreat at noon");
let signature_1 = schnorr.sign(&keypair_1, msg_atkdwn);
let signature_2 = schnorr.sign(&keypair_1, msg_atkdwn);
let signature_3 = schnorr.sign(&keypair_1, msg_rtrtnoon);
let signature_4 = schnorr.sign(&keypair_2, msg_atkdwn);
assert!(schnorr.verify(&keypair_1.public_key(), msg_atkdwn, &signature_1));
assert_eq!(signature_1, signature_2);
if keypair_1 != keypair_2 {
assert_ne!(signature_3.R, signature_1.R);
assert_ne!(signature_1.R, signature_4.R);
}
}
}
}