From 31de6a1bac41c1dec842b4386da68c4426dbf8b6 Mon Sep 17 00:00:00 2001 From: Tibo-lg Date: Wed, 19 Aug 2020 15:13:44 +0900 Subject: [PATCH] Update rust-secp256k1 to add ecdsa-adaptor/schnorr --- Cargo.toml | 4 +- src/constants.rs | 18 ++ src/ecdsa_adaptor.rs | 419 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 17 ++ src/schnorrsig.rs | 434 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 890 insertions(+), 2 deletions(-) create mode 100644 src/ecdsa_adaptor.rs create mode 100644 src/schnorrsig.rs diff --git a/Cargo.toml b/Cargo.toml index 05ee0d0d5..210d648ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "secp256k1" -version = "0.17.2" +version = "0.17.3-adaptor.0" authors = [ "Dawid Ciężarkiewicz ", "Andrew Poelstra " ] license = "CC0-1.0" @@ -38,7 +38,7 @@ external-symbols = ["secp256k1-sys/external-symbols"] fuzztarget = ["secp256k1-sys/fuzztarget"] [dependencies] -secp256k1-sys = { version = "0.1.1", default-features = false, path = "./secp256k1-sys" } +secp256k1-sys = { version = "0.1.3-adaptor.0", default-features = false, path = "./secp256k1-sys" } [dev-dependencies] rand = "0.6" diff --git a/src/constants.rs b/src/constants.rs index cb185e4fc..c5ba59a00 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -34,6 +34,24 @@ pub const MAX_SIGNATURE_SIZE: usize = 72; /// The maximum size of a compact signature pub const COMPACT_SIGNATURE_SIZE: usize = 64; +/// Size of an adaptor signature +pub const ADAPTOR_SIGNATURE_SIZE: usize = 65; + +/// Size of an adaptor proof +pub const ADAPTOR_PROOF_SIZE: usize = 97; + +/// Size of a schnorr signature +pub const SCHNORR_SIGNATURE_SIZE: usize = 64; + +/// Size of a schnorr signature +pub const SCHNORR_NONCE_SIZE: usize = 32; + +/// Size of a x-only public key +pub const X_ONLY_PUBLIC_KEY_SIZE: usize = 64; + +/// 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/ecdsa_adaptor.rs b/src/ecdsa_adaptor.rs new file mode 100644 index 000000000..07592a0c8 --- /dev/null +++ b/src/ecdsa_adaptor.rs @@ -0,0 +1,419 @@ +//! # ECDSA Adaptor +//! Support for ECDSA based adaptor signatures. +//! + +use super::{from_hex, Error}; +use core::{fmt, str}; +use ffi::{self, CPtr}; +use {constants, PublicKey, Secp256k1, SecretKey}; +use {Message, Signing}; +use {Signature, Verification}; + +/// An adaptor signature. +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct AdaptorSignature(ffi::AdaptorSignature); + +impl fmt::Debug for AdaptorSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::LowerHex for AdaptorSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for ch in self.0.iter().enumerate() { + write!(f, "{:02x}", ch.1)?; + } + Ok(()) + } +} + +impl fmt::Display for AdaptorSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for AdaptorSignature { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut res = [0; constants::ADAPTOR_SIGNATURE_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::ADAPTOR_SIGNATURE_SIZE) => { + AdaptorSignature::from_slice(&res[0..constants::ADAPTOR_SIGNATURE_SIZE]) + } + _ => Err(Error::InvalidAdaptorSignature), + } + } +} + +impl From for AdaptorSignature { + #[inline] + fn from(adaptor_sig: ffi::AdaptorSignature) -> AdaptorSignature { + AdaptorSignature(adaptor_sig) + } +} + +impl CPtr for AdaptorSignature { + type Target = ffi::AdaptorSignature; + 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() + } +} + +impl AdaptorSignature { + /// Creates an AdaptorSignature directly from a slice + #[inline] + pub fn from_slice(data: &[u8]) -> Result { + match data.len() { + constants::ADAPTOR_SIGNATURE_SIZE => { + let mut ret = [0; constants::ADAPTOR_SIGNATURE_SIZE]; + ret[..].copy_from_slice(data); + Ok(AdaptorSignature(ffi::AdaptorSignature::from_array(ret))) + } + _ => Err(Error::InvalidAdaptorSignature), + } + } + + /// Obtains a raw const pointer suitable for use with FFI functions + #[inline] + pub fn as_ptr(&self) -> *const ffi::AdaptorSignature { + &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::AdaptorSignature { + &mut self.0 as *mut _ + } +} + +/// Proof to verify an adaptor signature. +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct AdaptorProof(ffi::AdaptorProof); + +impl fmt::Debug for AdaptorProof { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::LowerHex for AdaptorProof { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for ch in self.0.iter().enumerate() { + write!(f, "{:02x}", ch.1)?; + } + Ok(()) + } +} + +impl fmt::Display for AdaptorProof { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for AdaptorProof { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut res = [0; constants::ADAPTOR_PROOF_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::ADAPTOR_PROOF_SIZE) => { + AdaptorProof::from_slice(&res[0..constants::ADAPTOR_PROOF_SIZE]) + } + _ => Err(Error::InvalidAdaptorProof), + } + } +} + +impl From for AdaptorProof { + #[inline] + fn from(adaptor_proof: ffi::AdaptorProof) -> AdaptorProof { + AdaptorProof(adaptor_proof) + } +} + +impl CPtr for AdaptorProof { + type Target = ffi::AdaptorProof; + 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() + } +} + +impl AdaptorProof { + /// Creates an AdaptorProof directly from a slice + #[inline] + pub fn from_slice(data: &[u8]) -> Result { + match data.len() { + constants::ADAPTOR_PROOF_SIZE => { + let mut ret = [0; constants::ADAPTOR_PROOF_SIZE]; + ret[..].copy_from_slice(data); + Ok(AdaptorProof(ffi::AdaptorProof::from_array(ret))) + } + _ => Err(Error::InvalidAdaptorSignature), + } + } + + /// Obtains a raw const pointer suitable for use with FFI functions + #[inline] + pub fn as_ptr(&self) -> *const ffi::AdaptorProof { + &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::AdaptorProof { + &mut self.0 as *mut _ + } +} + +impl Secp256k1 { + /// Creates an adaptor signature along with a proof to verify the adaptor signature. + pub fn adaptor_sign( + &self, + msg: &Message, + sk: &SecretKey, + adaptor: &PublicKey, + ) -> (AdaptorSignature, AdaptorProof) { + let mut adaptor_sig = ffi::AdaptorSignature::new(); + let mut adaptor_proof = ffi::AdaptorProof::new(); + + unsafe { + assert_eq!( + 1, + ffi::secp256k1_ecdsa_adaptor_sign( + self.ctx, + &mut adaptor_sig, + &mut adaptor_proof, + sk.as_c_ptr(), + adaptor.as_c_ptr(), + msg.as_c_ptr() + ) + ); + } + + ( + AdaptorSignature::from(adaptor_sig), + AdaptorProof::from(adaptor_proof), + ) + } + + /// Creates an ECDSA signature from an adaptor signature and an adaptor secret. + pub fn adaptor_adapt( + &self, + adaptor_secret: &SecretKey, + adaptor_sig: &AdaptorSignature, + ) -> Signature { + let mut signature = ffi::Signature::new(); + + unsafe { + assert_eq!( + 1, + ffi::secp256k1_ecdsa_adaptor_adapt( + self.ctx, + &mut signature, + adaptor_secret.as_c_ptr(), + adaptor_sig.as_c_ptr() + ) + ); + } + + Signature::from(signature) + } + + /// Extracts the adaptor secret from the complete signature and the adaptor signature. + pub fn adaptor_extract_secret( + &self, + sig: &Signature, + adaptor_sig: &AdaptorSignature, + adaptor: &PublicKey, + ) -> Result { + let mut data: [u8; constants::SECRET_KEY_SIZE] = [0; constants::SECRET_KEY_SIZE]; + + unsafe { + assert_eq!( + 1, + ffi::secp256k1_ecdsa_adaptor_extract_secret( + self.ctx, + data.as_mut_c_ptr(), + sig.as_c_ptr(), + adaptor_sig.as_c_ptr(), + adaptor.as_c_ptr() + ) + ); + } + + SecretKey::from_slice(&data) + } +} + +impl Secp256k1 { + /// Verifies that the adaptor secret can be extracted from the adaptor signature and the completed ECDSA signature. + pub fn adaptor_verify( + &self, + msg: &Message, + adaptor_sig: &AdaptorSignature, + pubkey: &PublicKey, + adaptor: &PublicKey, + adaptor_proof: &AdaptorProof, + ) -> Result<(), Error> { + unsafe { + let res = ffi::secp256k1_ecdsa_adaptor_sig_verify( + self.ctx, + adaptor_sig.as_c_ptr(), + pubkey.as_c_ptr(), + msg.as_c_ptr(), + adaptor.as_c_ptr(), + adaptor_proof.as_c_ptr(), + ); + return if res == 1 { + Ok(()) + } else { + Err(Error::IncorrectSignature) + }; + } + } +} + +#[cfg(test)] +mod tests { + use super::super::{from_hex, Message, PublicKey, Secp256k1, SecretKey, Signature}; + use super::{AdaptorProof, AdaptorSignature}; + use rand::thread_rng; + use std::str::FromStr; + + macro_rules! hex { + ($hex:expr) => {{ + let mut result = vec![0; $hex.len() / 2]; + from_hex($hex, &mut result).expect("valid hex string"); + result + }}; + } + + #[test] + fn test_adaptor() { + let secp = Secp256k1::new(); + + let (seckey, pubkey) = secp.generate_keypair(&mut thread_rng()); + let (adaptor_secret, adaptor) = secp.generate_keypair(&mut thread_rng()); + let msg = Message::from_slice(&[2u8; 32]).unwrap(); + let (adaptor_sig, adaptor_proof) = secp.adaptor_sign(&msg, &seckey, &adaptor); + + assert!(secp + .adaptor_verify(&msg, &adaptor_sig, &pubkey, &adaptor, &adaptor_proof) + .is_ok()); + assert!(!secp + .adaptor_verify(&msg, &adaptor_sig, &adaptor, &pubkey, &adaptor_proof) + .is_ok()); + let sig = secp.adaptor_adapt(&adaptor_secret, &adaptor_sig); + assert!(secp.verify(&msg, &sig, &pubkey).is_ok()); + } + + #[test] + fn test_adapt_with_schnorr_sig() { + let secp = Secp256k1::new(); + let (oracle_sk, oracle_pk) = secp.generate_keypair(&mut thread_rng()); + let (oracle_k, oracle_r_pk) = secp.generate_keypair(&mut thread_rng()); + let (sk, pk) = secp.generate_keypair(&mut thread_rng()); + let msg = Message::from_slice(&[2u8; 32]).unwrap(); + + let adaptor_point = secp + .schnorr_compute_sig_point(&msg, &oracle_r_pk.into(), &oracle_pk) + .unwrap(); + let (adaptor_sig, _) = secp.adaptor_sign(&msg, &sk, &adaptor_point); + + let oracle_sig = secp + .schnorr_sign_with_nonce(&msg, &oracle_sk, &oracle_k.into()) + .unwrap(); + let (_, adaptor_secret) = oracle_sig.decompose().unwrap(); + + let adapted_sig = secp.adaptor_adapt(&adaptor_secret, &adaptor_sig); + + assert!(secp.verify(&msg, &adapted_sig, &pk).is_ok()); + } + + #[test] + fn test_adaptor_sign() { + let secp = Secp256k1::new(); + let hex_msg = hex!("024BDD11F2144E825DB05759BDD9041367A420FAD14B665FD08AF5B42056E5E2"); + let msg = Message::from_slice(&hex_msg).unwrap(); + let adaptor = PublicKey::from_str( + "038D48057FC4CE150482114D43201B333BF3706F3CD527E8767CEB4B443AB5D349", + ) + .unwrap(); + let sk = + SecretKey::from_str("90AC0D5DC0A1A9AB352AFB02005A5CC6C4DF0DA61D8149D729FF50DB9B5A5215") + .unwrap(); + let expected_adaptor_sig = AdaptorSignature::from_str("00CBE0859638C3600EA1872ED7A55B8182A251969F59D7D2DA6BD4AFEDF25F5021A49956234CBBBBEDE8CA72E0113319C84921BF1224897A6ABD89DC96B9C5B208").unwrap(); + let expected_adaptor_proof = AdaptorProof::from_str("00B02472BE1BA09F5675488E841A10878B38C798CA63EFF3650C8E311E3E2EBE2E3B6FEE5654580A91CC5149A71BF25BCBEAE63DEA3AC5AD157A0AB7373C3011D0FC2592A07F719C5FC1323F935569ECD010DB62F045E965CC1D564EB42CCE8D6D").unwrap(); + + let (adaptor_sig, adaptor_proof) = secp.adaptor_sign(&msg, &sk, &adaptor); + + assert_eq!(expected_adaptor_sig, adaptor_sig); + assert_eq!(expected_adaptor_proof, adaptor_proof); + } + + #[test] + fn test_adaptor_verify() { + let secp = Secp256k1::new(); + let hex_msg = hex!("024BDD11F2144E825DB05759BDD9041367A420FAD14B665FD08AF5B42056E5E2"); + let msg = Message::from_slice(&hex_msg).unwrap(); + let adaptor = PublicKey::from_str( + "038D48057FC4CE150482114D43201B333BF3706F3CD527E8767CEB4B443AB5D349", + ) + .unwrap(); + let adaptor_sig = AdaptorSignature::from_str("00CBE0859638C3600EA1872ED7A55B8182A251969F59D7D2DA6BD4AFEDF25F5021A49956234CBBBBEDE8CA72E0113319C84921BF1224897A6ABD89DC96B9C5B208").unwrap(); + let adaptor_proof = AdaptorProof::from_str("00B02472BE1BA09F5675488E841A10878B38C798CA63EFF3650C8E311E3E2EBE2E3B6FEE5654580A91CC5149A71BF25BCBEAE63DEA3AC5AD157A0AB7373C3011D0FC2592A07F719C5FC1323F935569ECD010DB62F045E965CC1D564EB42CCE8D6D").unwrap(); + let pubkey = PublicKey::from_str( + "03490CEC9A53CD8F2F664AEA61922F26EE920C42D2489778BB7C9D9ECE44D149A7", + ) + .unwrap(); + + assert!(secp + .adaptor_verify(&msg, &adaptor_sig, &pubkey, &adaptor, &adaptor_proof) + .is_ok()); + } + + #[test] + fn test_adaptor_adapt() { + let secp = Secp256k1::new(); + let secret = + SecretKey::from_str("475697A71A74FF3F2A8F150534E9B67D4B0B6561FAB86FCAA51F8C9D6C9DB8C6") + .unwrap(); + let adaptor_sig = AdaptorSignature::from_str("01099C91AA1FE7F25C41085C1D3C9E73FE04A9D24DAC3F9C2172D6198628E57F47BB90E2AD6630900B69F55674C8AD74A419E6CE113C10A21A79345A6E47BC74C1").unwrap(); + let expected_signature = Signature::from_str("30440220099C91AA1FE7F25C41085C1D3C9E73FE04A9D24DAC3F9C2172D6198628E57F4702204D13456E98D8989043FD4674302CE90C432E2F8BB0269F02C72AAFEC60B72DE1").unwrap(); + + let signature = secp.adaptor_adapt(&secret, &adaptor_sig); + + assert_eq!(expected_signature, signature); + } + + #[test] + fn test_adaptor_extract_secret() { + let secp = Secp256k1::new(); + + let sig = Signature::from_str("30440220099C91AA1FE7F25C41085C1D3C9E73FE04A9D24DAC3F9C2172D6198628E57F4702204D13456E98D8989043FD4674302CE90C432E2F8BB0269F02C72AAFEC60B72DE1").unwrap(); + let adaptor_sig = AdaptorSignature::from_str("01099C91AA1FE7F25C41085C1D3C9E73FE04A9D24DAC3F9C2172D6198628E57F47BB90E2AD6630900B69F55674C8AD74A419E6CE113C10A21A79345A6E47BC74C1").unwrap(); + let adaptor = PublicKey::from_str( + "038D48057FC4CE150482114D43201B333BF3706F3CD527E8767CEB4B443AB5D349", + ) + .unwrap(); + let expected_secret = + SecretKey::from_str("475697A71A74FF3F2A8F150534E9B67D4B0B6561FAB86FCAA51F8C9D6C9DB8C6") + .unwrap(); + + let secret = secp + .adaptor_extract_secret(&sig, &adaptor_sig, &adaptor) + .unwrap(); + + assert_eq!(expected_secret, secret); + } +} diff --git a/src/lib.rs b/src/lib.rs index a60a2b5ca..9aa8c6fe3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -162,7 +162,9 @@ mod macros; mod context; pub mod constants; pub mod ecdh; +pub mod ecdsa_adaptor; pub mod key; +pub mod schnorrsig; #[cfg(feature = "recovery")] pub mod recovery; @@ -496,6 +498,16 @@ pub enum Error { InvalidTweak, /// Didn't pass enough memory to context creation with preallocated memory NotEnoughMemory, + /// Bad adaptor signature + InvalidAdaptorSignature, + /// Bad adaptor proof + InvalidAdaptorProof, + /// Bad x-only public key + InvalidXOnlyPublicKey, + /// Bad key pair + InvalidKeyPair, + /// Bad Schnorr nonce + InvalidSchnorrNonce, } impl Error { @@ -509,6 +521,11 @@ impl Error { Error::InvalidRecoveryId => "secp: bad recovery id", Error::InvalidTweak => "secp: bad tweak", Error::NotEnoughMemory => "secp: not enough memory allocated", + Error::InvalidAdaptorSignature => "secp: malformed adaptor signature", + Error::InvalidAdaptorProof => "secp: malformed adaptor proof", + Error::InvalidXOnlyPublicKey => "secp: malformed x-only public key", + Error::InvalidKeyPair => "secp: malformed key pair", + Error::InvalidSchnorrNonce => "secp: malformed schnorr nonce", } } } diff --git a/src/schnorrsig.rs b/src/schnorrsig.rs new file mode 100644 index 000000000..b2720d9bf --- /dev/null +++ b/src/schnorrsig.rs @@ -0,0 +1,434 @@ +//! # Schnorrsig +//! Support for Schnorr signatures. +//! + +use super::{from_hex, Error}; +use core::{fmt, str}; +use ffi::{self, CPtr}; +use {constants, PublicKey, Secp256k1, SecretKey}; +use {Message, Signing}; + +/// Represents a schnorr signature. +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct SchnorrSignature(ffi::SchnorrSignature); + +impl fmt::Debug for SchnorrSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::LowerHex for SchnorrSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for ch in self.0.iter().enumerate() { + write!(f, "{:02x}", ch.1)?; + } + 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::InvalidSignature), + } + } +} + +impl From for SchnorrSignature { + #[inline] + fn from(adaptor_sig: ffi::SchnorrSignature) -> SchnorrSignature { + SchnorrSignature(adaptor_sig) + } +} + +impl CPtr for SchnorrSignature { + type Target = ffi::SchnorrSignature; + 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() + } +} + +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(ffi::SchnorrSignature::from_array(ret))) + } + _ => Err(Error::InvalidSignature), + } + } + + /// Obtains a raw const pointer suitable for use with FFI functions + #[inline] + pub fn as_ptr(&self) -> *const ffi::SchnorrSignature { + &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::SchnorrSignature { + &mut self.0 as *mut _ + } + + /// Decompose a schnnorr signature into a nonce and a secret key + pub fn decompose(&self) -> Result<(SchnorrNonce, SecretKey), Error> { + Ok(( + SchnorrNonce::from_slice(&self.0[0..32])?, + SecretKey::from_slice(&self.0[32..64])?, + )) + } +} + +/// Represents a schnorr signature. +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct SchnorrNonce(ffi::SchnorrNonce); + +impl fmt::Debug for SchnorrNonce { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::LowerHex for SchnorrNonce { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for ch in self.0.iter().enumerate() { + write!(f, "{:02x}", ch.1)?; + } + Ok(()) + } +} + +impl fmt::Display for SchnorrNonce { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for SchnorrNonce { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut res = [0; constants::SCHNORR_NONCE_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::SCHNORR_NONCE_SIZE) => { + SchnorrNonce::from_slice(&res[0..constants::SCHNORR_NONCE_SIZE]) + } + _ => Err(Error::InvalidSchnorrNonce), + } + } +} + +impl From for SchnorrNonce { + #[inline] + fn from(nonce: ffi::SchnorrNonce) -> SchnorrNonce { + SchnorrNonce(nonce) + } +} + +impl From for SchnorrNonce { + #[inline] + fn from(sk: SecretKey) -> SchnorrNonce { + SchnorrNonce::from_slice(&sk[0..constants::SCHNORR_NONCE_SIZE]).unwrap() + } +} + +impl From for SchnorrNonce { + #[inline] + fn from(pubkey: PublicKey) -> SchnorrNonce { + SchnorrNonce::from_slice(&pubkey.serialize()[1..33]).unwrap() + } +} + +impl CPtr for SchnorrNonce { + type Target = ffi::SchnorrNonce; + 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() + } +} + +impl SchnorrNonce { + /// Creates an SchnorrNonce directly from a slice + #[inline] + pub fn from_slice(data: &[u8]) -> Result { + match data.len() { + constants::SCHNORR_NONCE_SIZE => { + let mut ret = [0; constants::SCHNORR_NONCE_SIZE]; + ret[..].copy_from_slice(data); + Ok(SchnorrNonce(ffi::SchnorrNonce::from_array(ret))) + } + _ => Err(Error::InvalidSchnorrNonce), + } + } + + /// Obtains a raw const pointer suitable for use with FFI functions + #[inline] + pub fn as_ptr(&self) -> *const ffi::SchnorrNonce { + &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::SchnorrNonce { + &mut self.0 as *mut _ + } +} + +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(Error::InvalidSecretKey); + } + + let mut sig = ffi::SchnorrSignature::new(); + assert_eq!( + 1, + ffi::secp256k1_schnorrsig_sign( + self.ctx, + &mut sig, + msg.as_c_ptr(), + &keypair, + ffi::secp256k1_nonce_function_bip340, + aux_rand.as_c_ptr() as *const ffi::types::c_void + ) + ); + + Ok(SchnorrSignature(sig)) + } + } + + /// Create a schnorr signature using the provided nonce. + pub fn schnorr_sign_with_nonce( + &self, + msg: &Message, + sk: &SecretKey, + nonce: &SecretKey, + ) -> 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(Error::InvalidSecretKey); + } + + let mut sig = ffi::SchnorrSignature::new(); + assert_eq!( + 1, + ffi::secp256k1_schnorrsig_sign( + self.ctx, + &mut sig, + msg.as_c_ptr(), + &keypair, + ffi::constant_nonce_fn, + nonce.as_c_ptr() as *const ffi::types::c_void + ) + ); + + Ok(SchnorrSignature(sig)) + } + } + + /// Computes a point fo a Schnorr signature. + pub fn schnorr_compute_sig_point( + &self, + msg: &Message, + nonce: &SchnorrNonce, + pubkey: &PublicKey, + ) -> Result { + unsafe { + let mut xonly_pubkey = ffi::XOnlyPublicKey::new(); + let mut pk_parity: ffi::types::c_int = 0; + let mut ret = ffi::secp256k1_xonly_pubkey_from_pubkey( + self.ctx, + &mut xonly_pubkey, + &mut pk_parity, + pubkey.as_c_ptr(), + ); + + if ret == 0 { + return Err(Error::InvalidPublicKey); + } + + let mut sigpoint = ffi::PublicKey::new(); + + ret = ffi::secp256k1_schnorrsig_compute_sigpoint( + self.ctx, + &mut sigpoint, + msg.as_c_ptr(), + nonce.as_c_ptr(), + &xonly_pubkey, + ); + + if ret == 0 { + return Err(Error::InvalidXOnlyPublicKey); + } + + Ok(PublicKey::from(sigpoint)) + } + } + + /// Verify a schnorr signature. + pub fn schnorr_verify( + &self, + sig: &SchnorrSignature, + msg: &Message, + pubkey: &PublicKey, + ) -> Result<(), Error> { + unsafe { + let mut xonly_pubkey = ffi::XOnlyPublicKey::new(); + let mut pk_parity: ffi::types::c_int = 0; + let mut ret = ffi::secp256k1_xonly_pubkey_from_pubkey( + self.ctx, + &mut xonly_pubkey, + &mut pk_parity, + pubkey.as_c_ptr(), + ); + + if ret == 0 { + return Err(Error::InvalidPublicKey); + } + + ret = ffi::secp256k1_schnorrsig_verify( + self.ctx, + sig.as_c_ptr(), + msg.as_c_ptr(), + &xonly_pubkey, + ); + + return if ret == 1 { + Ok(()) + } else { + Err(Error::InvalidSignature) + }; + } + } +} + +#[cfg(test)] +mod tests { + use super::super::{from_hex, Message, PublicKey, Secp256k1, SecretKey}; + use super::{SchnorrNonce, SchnorrSignature}; + use std::convert::TryInto; + use std::str::FromStr; + + macro_rules! hex { + ($hex:expr) => {{ + let mut result = vec![0; $hex.len() / 2]; + from_hex($hex, &mut result).expect("valid hex string"); + result + }}; + } + + #[test] + fn test_schnorr_sign() { + let secp = Secp256k1::new(); + + let hex_msg = hex!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); + let msg = Message::from_slice(&hex_msg).unwrap(); + let sk = + SecretKey::from_str("688C77BC2D5AAFF5491CF309D4753B732135470D05B7B2CD21ADD0744FE97BEF") + .unwrap(); + let aux_rand: Box<[u8; 32]> = + hex!("02CCE08E913F22A36C5648D6405A2C7C50106E7AA2F1649E381C7F09D16B80AB") + .into_boxed_slice() + .try_into() + .unwrap(); + let expected_sig = SchnorrSignature::from_str("F14D7E54FF58C5D019CE9986BE4A0E8B7D643BD08EF2CDF1099E1A457865B5477C988C51634A8DC955950A58FF5DC8C506DDB796121E6675946312680C26CF33").unwrap(); + + let sig = secp.schnorr_sign(&msg, &sk, &*aux_rand).unwrap(); + + assert_eq!(expected_sig, sig); + } + + #[test] + fn test_schnorr_sign_with_nonce() { + let secp = Secp256k1::new(); + + let hex_msg = hex!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); + let msg = Message::from_slice(&hex_msg).unwrap(); + let sk = + SecretKey::from_str("688C77BC2D5AAFF5491CF309D4753B732135470D05B7B2CD21ADD0744FE97BEF") + .unwrap(); + let nonce = + SecretKey::from_str("8C8CA771D3C25EB38DE7401818EEDA281AC5446F5C1396148F8D9D67592440FE") + .unwrap(); + let expected_sig = SchnorrSignature::from_str("5DA618C1936EC728E5CCFF29207F1680DCF4146370BDCFAB0039951B91E3637A50A2A860B130D009405511C3EAFE943E157A0DF2C2020E3E50DF05ADB175332F").unwrap(); + + let sig = secp.schnorr_sign_with_nonce(&msg, &sk, &nonce).unwrap(); + + assert_eq!(expected_sig, sig); + } + + #[test] + fn test_schnorr_compute_sig_point() { + let secp = Secp256k1::new(); + + let hex_msg = hex!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); + let msg = Message::from_slice(&hex_msg).unwrap(); + let nonce = SchnorrNonce::from_str( + "F14D7E54FF58C5D019CE9986BE4A0E8B7D643BD08EF2CDF1099E1A457865B547", + ) + .unwrap(); + let pubkey = PublicKey::from_str( + "02B33CC9EDC096D0A83416964BD3C6247B8FECD256E4EFA7870D2C854BDEB33390", + ) + .unwrap(); + + let expected_point = PublicKey::from_str( + "020D17280B8D2C2BD3B597B4446419C151DC237353D0FB9EC03D4EB7E8DE7EE0A8", + ) + .unwrap(); + + let point = secp + .schnorr_compute_sig_point(&msg, &nonce, &pubkey) + .unwrap(); + + assert_eq!(expected_point, point); + } + + #[test] + fn test_schnorr_verify() { + let secp = Secp256k1::new(); + + let hex_msg = hex!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); + let msg = Message::from_slice(&hex_msg).unwrap(); + let sig = SchnorrSignature::from_str("F14D7E54FF58C5D019CE9986BE4A0E8B7D643BD08EF2CDF1099E1A457865B5477C988C51634A8DC955950A58FF5DC8C506DDB796121E6675946312680C26CF33").unwrap(); + let pubkey = PublicKey::from_str( + "02B33CC9EDC096D0A83416964BD3C6247B8FECD256E4EFA7870D2C854BDEB33390", + ) + .unwrap(); + + assert!(secp.schnorr_verify(&sig, &msg, &pubkey).is_ok()); + } +}