diff --git a/src/constants.rs b/src/constants.rs index cb185e4fc..5dca6f442 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -34,6 +34,15 @@ pub const MAX_SIGNATURE_SIZE: usize = 72; /// The maximum size of a compact signature pub const COMPACT_SIGNATURE_SIZE: usize = 64; +/// Size of a schnorr signature +pub const SCHNORR_SIGNATURE_SIZE: usize = 64; + +/// Size of a x-only public key +pub const SCHNORR_PUBLIC_KEY_SIZE: usize = 32; + +/// Size of a key pair +pub const KEY_PAIR_SIZE: usize = 96; + /// The Prime for the secp256k1 field element. pub const FIELD_SIZE: [u8; 32] = [ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, diff --git a/src/lib.rs b/src/lib.rs index 4d887a0c1..0ce68a34c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -169,6 +169,7 @@ mod context; pub mod constants; pub mod ecdh; pub mod key; +pub mod schnorrsig; #[cfg(feature = "recovery")] pub mod recovery; @@ -550,6 +551,12 @@ pub enum Error { InvalidTweak, /// Didn't pass enough memory to context creation with preallocated memory NotEnoughMemory, + /// Bad Schnorr signature + InvalidSchnorrSignature, + /// Bad Schnorr public key + InvalidSchnorrPublicKey, + /// Bad key pair + InvalidKeyPair, } impl Error { @@ -563,6 +570,9 @@ impl Error { Error::InvalidRecoveryId => "secp: bad recovery id", Error::InvalidTweak => "secp: bad tweak", Error::NotEnoughMemory => "secp: not enough memory allocated", + Error::InvalidSchnorrSignature => "secp: malformed Schnorr signature", + Error::InvalidSchnorrPublicKey => "secp: malformed x-only public key", + Error::InvalidKeyPair => "secp: malformed key pair", } } } diff --git a/src/schnorrsig.rs b/src/schnorrsig.rs new file mode 100644 index 000000000..33a01423f --- /dev/null +++ b/src/schnorrsig.rs @@ -0,0 +1,471 @@ +//! # Schnorrsig +//! Support for Schnorr signatures. +//! + +#[cfg(any(test, feature = "rand"))] +use rand::Rng; + +use super::Error::{InvalidSchnorrPublicKey, InvalidSchnorrSignature, InvalidSecretKey}; +use super::{from_hex, Error}; +use core::{fmt, str}; +use ffi::{self, CPtr}; +use {constants, Secp256k1, SecretKey}; +use {Message, Signing}; + +/// Represents a schnorr signature. +pub struct SchnorrSignature([u8; constants::SCHNORR_SIGNATURE_SIZE]); +impl_array_newtype!(SchnorrSignature, u8, constants::SCHNORR_SIGNATURE_SIZE); +impl_pretty_debug!(SchnorrSignature); + +impl fmt::LowerHex for SchnorrSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for ch in &self.0[..] { + write!(f, "{:02x}", ch)?; + } + Ok(()) + } +} + +impl fmt::Display for SchnorrSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for SchnorrSignature { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut res = [0; constants::SCHNORR_SIGNATURE_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::SCHNORR_SIGNATURE_SIZE) => { + SchnorrSignature::from_slice(&res[0..constants::SCHNORR_SIGNATURE_SIZE]) + } + _ => Err(Error::InvalidSchnorrSignature), + } + } +} + +/// A Secp256k1 public key, used for verification of signatures +#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] +pub struct SchnorrPublicKey(ffi::XOnlyPublicKey); + +impl fmt::LowerHex for SchnorrPublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let ser = self.serialize(); + for ch in &ser[..] { + write!(f, "{:02x}", *ch)?; + } + Ok(()) + } +} + +impl fmt::Display for SchnorrPublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for SchnorrPublicKey { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut res = [0; constants::SCHNORR_PUBLIC_KEY_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::SCHNORR_PUBLIC_KEY_SIZE) => { + SchnorrPublicKey::from_slice(&res[0..constants::SCHNORR_PUBLIC_KEY_SIZE]) + } + _ => Err(InvalidSchnorrPublicKey), + } + } +} + +impl SchnorrSignature { + /// Creates an SchnorrSignature directly from a slice + #[inline] + pub fn from_slice(data: &[u8]) -> Result { + match data.len() { + constants::SCHNORR_SIGNATURE_SIZE => { + let mut ret = [0; constants::SCHNORR_SIGNATURE_SIZE]; + ret[..].copy_from_slice(data); + Ok(SchnorrSignature(ret)) + } + _ => Err(InvalidSchnorrSignature), + } + } +} + +impl SchnorrPublicKey { + /// Obtains a raw const pointer suitable for use with FFI functions + #[inline] + pub fn as_ptr(&self) -> *const ffi::XOnlyPublicKey { + &self.0 as *const _ + } + + /// Obtains a raw mutable pointer suitable for use with FFI functions + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut ffi::XOnlyPublicKey { + &mut self.0 as *mut _ + } + + /// Creates a new Schnorr public key from a secret key. + #[inline] + pub fn from_secret_key(secp: &Secp256k1, sk: &SecretKey) -> SchnorrPublicKey { + let mut keypair = ffi::KeyPair::new(); + let mut xonly_pk = ffi::XOnlyPublicKey::new(); + let mut pk_parity = 0; + unsafe { + let mut ret = ffi::secp256k1_keypair_create(secp.ctx, &mut keypair, sk.as_c_ptr()); + debug_assert_eq!(ret, 1); + ret = + ffi::secp256k1_keypair_xonly_pub(secp.ctx, &mut xonly_pk, &mut pk_parity, &keypair); + debug_assert_eq!(ret, 1); + } + SchnorrPublicKey(xonly_pk) + } + + /// Creates a Schnorr public key directly from a slice + #[inline] + pub fn from_slice(data: &[u8]) -> Result { + if data.is_empty() { + return Err(InvalidSchnorrPublicKey); + } + + let mut pk = ffi::XOnlyPublicKey::new(); + unsafe { + if ffi::secp256k1_xonly_pubkey_parse( + ffi::secp256k1_context_no_precomp, + &mut pk, + data.as_c_ptr(), + ) == 1 + { + Ok(SchnorrPublicKey(pk)) + } else { + Err(InvalidSchnorrPublicKey) + } + } + } + + #[inline] + /// Serialize the key as a byte-encoded pair of values. In compressed form + /// the y-coordinate is represented by only a single bit, as x determines + /// it up to one bit. + pub fn serialize(&self) -> [u8; constants::SCHNORR_PUBLIC_KEY_SIZE] { + let mut ret = [0; constants::SCHNORR_PUBLIC_KEY_SIZE]; + + unsafe { + let err = ffi::secp256k1_xonly_pubkey_serialize( + ffi::secp256k1_context_no_precomp, + ret.as_mut_c_ptr(), + self.as_c_ptr(), + ); + debug_assert_eq!(err, 1); + } + ret + } +} + +impl CPtr for SchnorrPublicKey { + type Target = ffi::XOnlyPublicKey; + fn as_c_ptr(&self) -> *const Self::Target { + self.as_ptr() + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + self.as_mut_ptr() + } +} + +/// Creates a new Schnorr public key from a FFI x-only public key +impl From for SchnorrPublicKey { + #[inline] + fn from(pk: ffi::XOnlyPublicKey) -> SchnorrPublicKey { + SchnorrPublicKey(pk) + } +} + +#[cfg(feature = "serde")] +impl ::serde::Serialize for SchnorrPublicKey { + fn serialize(&self, s: S) -> Result { + if s.is_human_readable() { + s.collect_str(self) + } else { + s.serialize_bytes(&self.serialize()) + } + } +} + +impl Secp256k1 { + /// Create a schnorr signature. + pub fn schnorr_sign( + &self, + msg: &Message, + sk: &SecretKey, + aux_rand: &[u8; 32], + ) -> Result { + unsafe { + let mut keypair = ffi::KeyPair::new(); + let ret = ffi::secp256k1_keypair_create(self.ctx, &mut keypair, sk.as_c_ptr()); + if ret == 0 { + return Err(InvalidSecretKey); + } + + let mut sig = [0u8; constants::SCHNORR_SIGNATURE_SIZE]; + assert_eq!( + 1, + ffi::secp256k1_schnorrsig_sign( + self.ctx, + sig.as_mut_c_ptr(), + msg.as_c_ptr(), + &keypair, + ffi::secp256k1_nonce_function_bip340, + aux_rand.as_c_ptr() as *const ffi::types::c_void + ) + ); + + Ok(SchnorrSignature(sig)) + } + } + + /// Verify a schnorr signature. + pub fn schnorr_verify( + &self, + sig: &SchnorrSignature, + msg: &Message, + pubkey: &SchnorrPublicKey, + ) -> Result<(), Error> { + unsafe { + let ret = ffi::secp256k1_schnorrsig_verify( + self.ctx, + sig.as_c_ptr(), + msg.as_c_ptr(), + pubkey.as_c_ptr(), + ); + + return if ret == 1 { + Ok(()) + } else { + Err(Error::InvalidSchnorrSignature) + }; + } + } + + /// Generates a random schnorr keypair. Convenience function for `key::SecretKey::new` + /// and `key::SchnorrPublicKey::from_secret_key`; call those functions directly for + /// batch key generation. Requires a signing-capable context. Requires compilation + /// with the "rand" feature. + #[inline] + #[cfg(any(test, feature = "rand"))] + pub fn generate_schnorr_keypair( + &self, + rng: &mut R, + ) -> (SecretKey, SchnorrPublicKey) { + let sk = SecretKey::new(rng); + let schnorr_pubkey = SchnorrPublicKey::from_secret_key(self, &sk); + (sk, schnorr_pubkey) + } +} + +#[cfg(test)] +mod tests { + use super::super::Error::InvalidSchnorrPublicKey; + use super::super::{constants, from_hex, Message, Secp256k1, SecretKey}; + use super::{SchnorrPublicKey, SchnorrSignature}; + use rand::{thread_rng, Error, ErrorKind, RngCore}; + use rand_core::impls; + use std::iter; + use std::str::FromStr; + + macro_rules! hex_32 { + ($hex:expr) => {{ + let mut result = [0; 32]; + from_hex($hex, &mut result).expect("valid hex string"); + result + }}; + } + + #[test] + fn test_schnorr() { + let secp = Secp256k1::new(); + + let mut rng = thread_rng(); + let (seckey, schnorr_pubkey) = secp.generate_schnorr_keypair(&mut rng); + let mut aux_rand = [0; 32]; + let mut msg = [0; 32]; + + for _ in 0..100 { + rng.fill_bytes(&mut aux_rand); + rng.fill_bytes(&mut msg); + let msg = Message::from_slice(&msg).unwrap(); + + let sig = secp.schnorr_sign(&msg, &seckey, &aux_rand).unwrap(); + + assert!(secp.schnorr_verify(&sig, &msg, &schnorr_pubkey).is_ok()); + } + } + + #[test] + fn test_schnorr_sign() { + let secp = Secp256k1::new(); + + let hex_msg = hex_32!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); + let msg = Message::from_slice(&hex_msg).unwrap(); + let sk = + SecretKey::from_str("688C77BC2D5AAFF5491CF309D4753B732135470D05B7B2CD21ADD0744FE97BEF") + .unwrap(); + let aux_rand: [u8; 32] = + hex_32!("02CCE08E913F22A36C5648D6405A2C7C50106E7AA2F1649E381C7F09D16B80AB"); + let expected_sig = SchnorrSignature::from_str("6470FD1303DDA4FDA717B9837153C24A6EAB377183FC438F939E0ED2B620E9EE5077C4A8B8DCA28963D772A94F5F0DDF598E1C47C137F91933274C7C3EDADCE8").unwrap(); + + let sig = secp.schnorr_sign(&msg, &sk, &aux_rand).unwrap(); + + assert_eq!(expected_sig, sig); + } + + #[test] + fn test_schnorr_verify() { + let secp = Secp256k1::new(); + + let hex_msg = hex_32!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); + let msg = Message::from_slice(&hex_msg).unwrap(); + let sig = SchnorrSignature::from_str("6470FD1303DDA4FDA717B9837153C24A6EAB377183FC438F939E0ED2B620E9EE5077C4A8B8DCA28963D772A94F5F0DDF598E1C47C137F91933274C7C3EDADCE8").unwrap(); + let schnorr_pubkey = SchnorrPublicKey::from_str( + "B33CC9EDC096D0A83416964BD3C6247B8FECD256E4EFA7870D2C854BDEB33390", + ) + .unwrap(); + + assert!(secp.schnorr_verify(&sig, &msg, &schnorr_pubkey).is_ok()); + } + + #[test] + fn schnorr_pubkey_from_slice() { + assert_eq!( + SchnorrPublicKey::from_slice(&[]), + Err(InvalidSchnorrPublicKey) + ); + assert_eq!( + SchnorrPublicKey::from_slice(&[1, 2, 3]), + Err(InvalidSchnorrPublicKey) + ); + let schnorr_pk = SchnorrPublicKey::from_slice(&[ + 0xB3, 0x3C, 0xC9, 0xED, 0xC0, 0x96, 0xD0, 0xA8, 0x34, 0x16, 0x96, 0x4B, 0xD3, 0xC6, + 0x24, 0x7B, 0x8F, 0xEC, 0xD2, 0x56, 0xE4, 0xEF, 0xA7, 0x87, 0x0D, 0x2C, 0x85, 0x4B, + 0xDE, 0xB3, 0x33, 0x90, + ]); + assert!(schnorr_pk.is_ok()); + } + + #[test] + fn schnorr_pubkey_serialize_roundtrip() { + let secp = Secp256k1::new(); + let (_, schnorr_pubkey) = secp.generate_schnorr_keypair(&mut thread_rng()); + let ser = schnorr_pubkey.serialize(); + let schnorr_pubkey2 = SchnorrPublicKey::from_slice(&ser).unwrap(); + assert_eq!(schnorr_pubkey, schnorr_pubkey2); + } + + #[test] + fn test_schnorr_pubkey_from_bad_slice() { + // Bad sizes + assert_eq!( + SchnorrPublicKey::from_slice(&[0; constants::SCHNORR_PUBLIC_KEY_SIZE - 1]), + Err(InvalidSchnorrPublicKey) + ); + assert_eq!( + SchnorrPublicKey::from_slice(&[0; constants::SCHNORR_PUBLIC_KEY_SIZE + 1]), + Err(InvalidSchnorrPublicKey) + ); + + // Bad parse + assert_eq!( + SchnorrPublicKey::from_slice(&[0xff; constants::SCHNORR_PUBLIC_KEY_SIZE]), + Err(InvalidSchnorrPublicKey) + ); + assert_eq!( + SchnorrPublicKey::from_slice(&[0x55; constants::SCHNORR_PUBLIC_KEY_SIZE]), + Err(InvalidSchnorrPublicKey) + ); + assert_eq!( + SchnorrPublicKey::from_slice(&[]), + Err(InvalidSchnorrPublicKey) + ); + } + + #[test] + fn test_schnorr_pubkey_display_output() { + static SK_BYTES: [u8; 32] = [ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, + ]; + + let s = Secp256k1::signing_only(); + let sk = SecretKey::from_slice(&SK_BYTES).expect("sk"); + let pk = SchnorrPublicKey::from_secret_key(&s, &sk); + + assert_eq!( + pk.to_string(), + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166" + ); + assert_eq!( + SchnorrPublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166" + ) + .unwrap(), + pk + ); + + assert!(SchnorrPublicKey::from_str( + "00000000000000000000000000000000000000000000000000000000000000000" + ) + .is_err()); + assert!(SchnorrPublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd16601" + ) + .is_err()); + assert!(SchnorrPublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd16" + ) + .is_err()); + assert!(SchnorrPublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1" + ) + .is_err()); + assert!(SchnorrPublicKey::from_str( + "xx18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1" + ) + .is_err()); + + let long_str: String = iter::repeat('a').take(1024 * 1024).collect(); + assert!(SchnorrPublicKey::from_str(&long_str).is_err()); + } + + #[test] + fn test_schnorr_pubkey_serialize() { + struct DumbRng(u32); + impl RngCore for DumbRng { + fn next_u32(&mut self) -> u32 { + self.0 = self.0.wrapping_add(1); + self.0 + } + fn next_u64(&mut self) -> u64 { + self.next_u32() as u64 + } + fn try_fill_bytes(&mut self, _dest: &mut [u8]) -> Result<(), Error> { + Err(Error::new(ErrorKind::Unavailable, "not implemented")) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_next(self, dest); + } + } + + let s = Secp256k1::new(); + let (_, schnorr_pubkey) = s.generate_schnorr_keypair(&mut DumbRng(0)); + assert_eq!( + &schnorr_pubkey.serialize()[..], + &[ + 124, 121, 49, 14, 253, 63, 197, 50, 39, 194, 107, 17, 193, 219, 108, 154, 126, 9, + 181, 248, 2, 12, 149, 233, 198, 71, 149, 134, 250, 184, 154, 229 + ][..] + ); + } +}