1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
use crate::{ fun::{ self, derive_nonce, digest::{generic_array::typenum::U32, Digest}, g, hash::{AddTag, HashAdd, Tagged}, marker::*, nonce::{NonceChallengeBundle, NonceGen}, s, Point, Scalar, Slice, XOnly, }, KeyPair, Signature, }; /// An instance of a [BIP-340] style Schnorr signature scheme. /// /// Each instance is defined by its: /// - `G`: Public generator (usually [`G`]) /// - `challenge_hash`: The hash function instance that is used to produce the [_Fiat-Shamir_] challenge. /// - `nonce_gen`: The [`NonceGen`] used to hash the signing inputs (and perhaps additional randomness) to produce the secret nonce. /// /// [_Fiat-Shamir_]: https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic /// [`G`]: crate::fun::G /// [`NonceGen<H>`]: crate::fun::hash::NonceGen /// [BIP-340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki #[derive(Clone)] pub struct Schnorr<CH, NG = (), GT = BasePoint> { /// The generator point the Schnorr signature scheme is defined with. G: Point<GT>, /// The [`NonceGen`] and NonceChalengeBundlesh. /// /// [`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. /// /// [`Schnorr`]: crate::Schnorr #[derive(Debug, Clone, Copy)] pub enum MessageKind { /// Sign a pre-hashed message. /// This is used by [BIP-341] to authorize transactions. /// If you also want to sign hashed messages in your applicatin you should use a [_tagged hash_] specific to your application. /// /// [BIP-341]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki /// [_tagged hash_]: crate::fun::hash::Tagged Prehashed, /// Sign an ordinary variable length message. 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. 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, }, } impl<H: Digest<OutputSize = U32> + Tagged> Schnorr<H, (), BasePoint> { /// Create a new instance that doesn't /// /// Creates a `Schnorr` instance to verifying signatures of a particular [`MessageKind`]. /// The instance will use the standard value of [`G`]. /// /// # Examples /// /// ``` /// use schnorr_fun::{MessageKind, Schnorr}; /// // An instance that can verify Bitcoin Taproot transactions /// let taproot_schnorr = Schnorr::<sha2::Sha256>::verify_only(MessageKind::Prehashed); /// // An instance for verifying transactions in your application /// let myapp_schnorr = Schnorr::<sha2::Sha256>::verify_only(MessageKind::Plain { tag: "myapp" }); /// ``` /// [`MessageKind`]: crate::MessageKind /// [`G`]: crate::fun::G pub fn verify_only(msgkind: MessageKind) -> Self { Self::new((), msgkind) } } impl<CH, NG> Schnorr<CH, NG, BasePoint> where CH: Digest<OutputSize = U32> + Tagged, NG: AddTag, { /// Creates a instance capable of signing and verifying. /// /// /// # Examples /// ``` /// use rand::rngs::ThreadRng; /// use schnorr_fun::{ /// nonce::{Deterministic, GlobalRng, Synthetic}, /// MessageKind, Schnorr, /// }; /// use sha2::Sha256; /// // Use synthetic nonces (preferred) /// let nonce_gen = Synthetic::<Sha256, GlobalRng<ThreadRng>>::default(); /// // Use deterministic nonces. /// let nonce_gen = Deterministic::<Sha256>::default(); /// // Sign pre-hashed messges as in BIP-341. /// let schnorr = Schnorr::<Sha256, _>::new(nonce_gen.clone(), MessageKind::Prehashed); /// // Sign ordinary messages in your own application. /// let schnorr = Schnorr::<Sha256, _>::new(nonce_gen, MessageKind::Plain { tag: "my-app" }); /// ``` pub fn new(nonce_gen: NG, msgkind: MessageKind) -> Self { let mut nonce_challenge_bundle = NonceChallengeBundle { challenge_hash: CH::default(), nonce_gen, } .add_protocol_tag("BIP0340"); 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, } } } impl<NG, CH, GT> Schnorr<CH, NG, GT> where CH: Digest<OutputSize = U32> + Clone, NG: NonceGen, { /// Sign a message using a secret key and a particular nonce derivation scheme. /// /// # Examples /// /// ``` /// use schnorr_fun::{ /// fun::{marker::*, Scalar}, /// MessageKind, /// }; /// # let schnorr = schnorr_fun::test_instance!(); /// let keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng())); /// let message = b"Chancellor on brink of second bailout for banks" /// .as_ref() /// .mark::<Public>(); /// let signature = schnorr.sign(&keypair, message); /// assert!(schnorr.verify(&keypair.verification_key(), message, &signature)); /// ``` pub fn sign(&self, keypair: &KeyPair, message: Slice<'_, impl Secrecy>) -> Signature { let (x, X) = keypair.as_tuple(); let mut r = derive_nonce!( nonce_gen => self.nonce_gen(), secret => x, public => [X, message] ); let R = XOnly::from_scalar_mul(&self.G, &mut r); let c = self.challenge(&R, X, message); let s = s!(r + c * x).mark::<Public>(); Signature { R, s } } /// Returns the [`NonceGen`] instance being used to genreate nonces. /// /// [`NonceGen`]: crate::nonce::NonceGen pub fn nonce_gen(&self) -> &NG { &self.nonce_challenge_bundle.nonce_gen } } impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> { /// Returns the generator point being used for the scheme. pub fn G(&self) -> &Point<GT> { &self.G } /// Returns the challenge hash being used to sign/verify signatures pub fn challenge_hash(&self) -> CH { self.nonce_challenge_bundle.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`]) /// /// [`Point`]: crate::fun::Point /// [`EvenY`]: crate::fun::marker::EvenY pub fn new_keypair(&self, mut sk: Scalar) -> KeyPair { let pk = XOnly::from_scalar_mul(&self.G, &mut sk); KeyPair { sk, pk } } /// Produces the Fiat-Shamir challenge for a Schnorr signature in the form specified by BIP-340. /// /// Concretely computes the hash `H(R || X || m)`. /// /// # Example /// /// Here's how you could use this to roll your own signatures. /// /// ``` /// use schnorr_fun::{ /// fun::{marker::*, s, Scalar, XOnly}, /// Schnorr, Signature, /// }; /// # let schnorr = schnorr_fun::test_instance!(); /// let message = b"we rolled our own sign!".as_ref().mark::<Public>(); /// 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 s = s!(r + challenge * { keypair.secret_key() }); /// let signature = Signature { R, s }; /// assert!(schnorr.verify(&keypair.verification_key(), message, &signature)); /// ``` 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 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 .mark::<Zero>() // The resulting challenge should take the secrecy of the message .mark::<S>() } /// Verifies a signature on a message under a given public key. Note that a full /// `Point<EvenY,..>` is passed in rather than a `XOnly` because it's more efficient /// for repeated verification (where as `XOnly` is more efficient for repeated /// signing). /// /// For an example see the [Synopsis](crate#synopsis) #[must_use] pub fn verify( &self, public_key: &Point<EvenY, impl Secrecy>, message: Slice<'_, impl Secrecy>, signature: &Signature<impl Secrecy>, ) -> bool { let X = public_key; let (R, s) = signature.as_tuple(); let c = self.challenge(R, &X.to_xonly(), message); let R_tmp = g!(s * self.G - c * X).mark::<Normal>(); R_tmp == *R } /// _Anticipates_ a Schnorr signature given the nonce `R` that will be used ahead of time. /// Deterministically returns the group element that corresponds to the scalar value of the /// signature. i.e `R + c * X` pub fn anticipate_signature( &self, X: &Point<EvenY, impl Secrecy>, R: &Point<EvenY, impl Secrecy>, m: Slice<'_, impl Secrecy>, ) -> Point<Jacobian, Public, Zero> { let c = self.challenge(&R.to_xonly(), &X.to_xonly(), m); g!(R + c * X) } } #[cfg(test)] pub mod test { use fun::nonce::Deterministic; use super::*; use crate::fun::TEST_SOUNDNESS; crate::fun::test_plus_wasm! { fn anticipated_signature_on_should_correspond_to_actual_signature() { for _ in 0..TEST_SOUNDNESS { let schnorr = crate::test_instance!(); let keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng())); let msg = b"Chancellor on brink of second bailout for banks".as_ref().mark::<Public>(); let signature = schnorr.sign(&keypair,msg); let anticipated_signature = schnorr.anticipate_signature( &keypair.verification_key(), &signature.R.to_point(), msg, ); assert_eq!( anticipated_signature, g!(signature.s * schnorr.G), "should anticipate the same value as actual signature" ) } } fn sign_deterministic() { let schnorr = crate::test_instance!(); for _ in 0..TEST_SOUNDNESS { let keypair_1 = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng())); let keypair_2 = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng())); let msg_atkdwn = b"attack at dawn".as_ref().mark::<Public>(); let msg_rtrtnoon = b"retreat at noon".as_ref().mark::<Public>(); 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.verification_key(), msg_atkdwn, &signature_1 )); assert_eq!(signature_1, signature_2); assert_ne!(signature_3.R, signature_1.R); 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()); } } }