From 8e96abae39281e0cccd60ac6e8cd1a0b913f9334 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 9 Sep 2021 18:49:47 +1000 Subject: [PATCH 01/12] Make `key` module private We re-export all structs residing in that module. There is no reason to expose the internal module structure of the library publicly. --- src/lib.rs | 4 +++- src/secret.rs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dc4092ecf..1a99aa7b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,9 +143,10 @@ mod macros; #[macro_use] mod secret; mod context; +mod key; + pub mod constants; pub mod ecdh; -pub mod key; pub mod schnorrsig; #[cfg(feature = "recovery")] pub mod recovery; @@ -154,6 +155,7 @@ mod serde_util; pub use key::SecretKey; pub use key::PublicKey; +pub use key::ONE_KEY; pub use context::*; use core::marker::PhantomData; use core::ops::Deref; diff --git a/src/secret.rs b/src/secret.rs index 34153c22e..f7fc22e6f 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -90,7 +90,7 @@ impl SecretKey { /// # Example /// /// ``` - /// use secp256k1::key::ONE_KEY; + /// use secp256k1::ONE_KEY; /// let key = ONE_KEY; /// // Normal display hides value /// assert_eq!( @@ -123,7 +123,7 @@ impl KeyPair { /// # Example /// /// ``` - /// use secp256k1::key::ONE_KEY; + /// use secp256k1::ONE_KEY; /// use secp256k1::schnorrsig::KeyPair; /// use secp256k1::Secp256k1; /// From 52d0554423bc9f3bb95c32770c9b74d90e6087e5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 9 Sep 2021 19:06:26 +1000 Subject: [PATCH 02/12] Fully qualify Error to simplify imports --- src/lib.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1a99aa7b6..960675e87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -886,8 +886,7 @@ mod tests { use key::{SecretKey, PublicKey}; use super::{from_hex, to_hex}; use super::constants; - use super::{Secp256k1, Signature, Message}; - use super::Error::{InvalidMessage, IncorrectSignature, InvalidSignature}; + use super::{Secp256k1, Signature, Message, Error}; use ffi::{self, types::AlignedType}; use context::*; @@ -1193,20 +1192,20 @@ mod tests { let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); let msg = Message::from_slice(&msg).unwrap(); - assert_eq!(s.verify(&msg, &sig, &pk), Err(IncorrectSignature)); + assert_eq!(s.verify(&msg, &sig, &pk), Err(Error::IncorrectSignature)); } #[test] fn test_bad_slice() { assert_eq!(Signature::from_der(&[0; constants::MAX_SIGNATURE_SIZE + 1]), - Err(InvalidSignature)); + Err(Error::InvalidSignature)); assert_eq!(Signature::from_der(&[0; constants::MAX_SIGNATURE_SIZE]), - Err(InvalidSignature)); + Err(Error::InvalidSignature)); assert_eq!(Message::from_slice(&[0; constants::MESSAGE_SIZE - 1]), - Err(InvalidMessage)); + Err(Error::InvalidMessage)); assert_eq!(Message::from_slice(&[0; constants::MESSAGE_SIZE + 1]), - Err(InvalidMessage)); + Err(Error::InvalidMessage)); assert!(Message::from_slice(&[0; constants::MESSAGE_SIZE]).is_ok()); assert!(Message::from_slice(&[1; constants::MESSAGE_SIZE]).is_ok()); } @@ -1253,7 +1252,7 @@ mod tests { let msg = Message::from_slice(&msg[..]).unwrap(); // without normalization we expect this will fail - assert_eq!(secp.verify(&msg, &sig, &pk), Err(IncorrectSignature)); + assert_eq!(secp.verify(&msg, &sig, &pk), Err(Error::IncorrectSignature)); // after normalization it should pass sig.normalize_s(); assert_eq!(secp.verify(&msg, &sig, &pk), Ok(())); From 49c7e214868fe72a3a2eaf0f17e74f95302c81e6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 9 Sep 2021 19:07:00 +1000 Subject: [PATCH 03/12] Prefer `use super::*` import over manually picking items Tests are usually placed next to the code they are testing. As such, importing `super::*` is a good starting point. --- src/lib.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 960675e87..02c10f121 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -879,16 +879,11 @@ fn to_hex<'a>(src: &[u8], target: &'a mut [u8]) -> Result<&'a str, ()> { #[cfg(test)] mod tests { + use super::*; use rand::{RngCore, thread_rng}; use std::str::FromStr; use std::marker::PhantomData; - - use key::{SecretKey, PublicKey}; - use super::{from_hex, to_hex}; - use super::constants; - use super::{Secp256k1, Signature, Message, Error}; - use ffi::{self, types::AlignedType}; - use context::*; + use ffi::types::AlignedType; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; From c47ead9967326bfaf3dc7a15bcf0b902001a1405 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 9 Sep 2021 19:25:06 +1000 Subject: [PATCH 04/12] Move `Signature` and `SerializedSignature` to new `ecdsa` module With the introduction of Schnorr signatures, exporting a `Signature` type without any further qualification is ambiguous. To minimize the ambiguity, the `ecdsa` module is public which should encourage users to refer to its types as `ecdsa::Signature` and `ecdsa::SerializedSignature`. To reduce ambiguity in the APIs on `Secp256k1`, we deprecate several fucntions and introduce new variants that explicitly mention the use of the ECDSA signature algorithm. Due to the move of `Signature` and `SerializedSignature` to a new module, this patch is a breaking change. The impact is minimal though and fixing the compile errors encourages a qualified naming of the type. --- examples/sign_verify.rs | 10 +- examples/sign_verify_recovery.rs | 13 +- no_std_test/src/main.rs | 14 +- src/ecdsa/mod.rs | 307 ++++++++++++++++++ src/{ => ecdsa}/recovery.rs | 33 +- src/lib.rs | 516 +++++++++---------------------- 6 files changed, 483 insertions(+), 410 deletions(-) create mode 100644 src/ecdsa/mod.rs rename src/{ => ecdsa}/recovery.rs (92%) diff --git a/examples/sign_verify.rs b/examples/sign_verify.rs index ca474a8d2..20dfde303 100644 --- a/examples/sign_verify.rs +++ b/examples/sign_verify.rs @@ -2,22 +2,22 @@ extern crate bitcoin_hashes; extern crate secp256k1; use bitcoin_hashes::{sha256, Hash}; -use secp256k1::{Error, Message, PublicKey, Secp256k1, SecretKey, Signature, Signing, Verification}; +use secp256k1::{Error, Message, PublicKey, Secp256k1, SecretKey, ecdsa, Signing, Verification}; fn verify(secp: &Secp256k1, msg: &[u8], sig: [u8; 64], pubkey: [u8; 33]) -> Result { let msg = sha256::Hash::hash(msg); let msg = Message::from_slice(&msg)?; - let sig = Signature::from_compact(&sig)?; + let sig = ecdsa::Signature::from_compact(&sig)?; let pubkey = PublicKey::from_slice(&pubkey)?; - Ok(secp.verify(&msg, &sig, &pubkey).is_ok()) + Ok(secp.verify_ecdsa(&msg, &sig, &pubkey).is_ok()) } -fn sign(secp: &Secp256k1, msg: &[u8], seckey: [u8; 32]) -> Result { +fn sign(secp: &Secp256k1, msg: &[u8], seckey: [u8; 32]) -> Result { let msg = sha256::Hash::hash(msg); let msg = Message::from_slice(&msg)?; let seckey = SecretKey::from_slice(&seckey)?; - Ok(secp.sign(&msg, &seckey)) + Ok(secp.sign_ecdsa(&msg, &seckey)) } fn main() { diff --git a/examples/sign_verify_recovery.rs b/examples/sign_verify_recovery.rs index 1644be4a0..627bbdc13 100644 --- a/examples/sign_verify_recovery.rs +++ b/examples/sign_verify_recovery.rs @@ -3,23 +3,22 @@ extern crate bitcoin_hashes; extern crate secp256k1; use bitcoin_hashes::{sha256, Hash}; -use secp256k1::recovery::{RecoverableSignature, RecoveryId}; -use secp256k1::{Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification}; +use secp256k1::{Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification, ecdsa}; fn recover(secp: &Secp256k1,msg: &[u8],sig: [u8; 64],recovery_id: u8) -> Result { let msg = sha256::Hash::hash(msg); let msg = Message::from_slice(&msg)?; - let id = RecoveryId::from_i32(recovery_id as i32)?; - let sig = RecoverableSignature::from_compact(&sig, id)?; + let id = ecdsa::RecoveryId::from_i32(recovery_id as i32)?; + let sig = ecdsa::RecoverableSignature::from_compact(&sig, id)?; - secp.recover(&msg, &sig) + secp.recover_ecdsa(&msg, &sig) } -fn sign_recovery(secp: &Secp256k1, msg: &[u8], seckey: [u8; 32]) -> Result { +fn sign_recovery(secp: &Secp256k1, msg: &[u8], seckey: [u8; 32]) -> Result { let msg = sha256::Hash::hash(msg); let msg = Message::from_slice(&msg)?; let seckey = SecretKey::from_slice(&seckey)?; - Ok(secp.sign_recoverable(&msg, &seckey)) + Ok(secp.sign_ecdsa_recoverable(&msg, &seckey)) } fn main() { diff --git a/no_std_test/src/main.rs b/no_std_test/src/main.rs index f6ecd9517..d245ce9dd 100644 --- a/no_std_test/src/main.rs +++ b/no_std_test/src/main.rs @@ -106,14 +106,14 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize { let public_key = PublicKey::from_secret_key(&secp, &secret_key); let message = Message::from_slice(&[0xab; 32]).expect("32 bytes"); - let sig = secp.sign(&message, &secret_key); - assert!(secp.verify(&message, &sig, &public_key).is_ok()); + let sig = secp.sign_ecdsa(&message, &secret_key); + assert!(secp.verify_ecdsa(&message, &sig, &public_key).is_ok()); - let rec_sig = secp.sign_recoverable(&message, &secret_key); - assert!(secp.verify(&message, &rec_sig.to_standard(), &public_key).is_ok()); - assert_eq!(public_key, secp.recover(&message, &rec_sig).unwrap()); + let rec_sig = secp.sign_ecdsa_recoverable(&message, &secret_key); + assert!(secp.verify_ecdsa(&message, &rec_sig.to_standard(), &public_key).is_ok()); + assert_eq!(public_key, secp.recover_ecdsa(&message, &rec_sig).unwrap()); let (rec_id, data) = rec_sig.serialize_compact(); - let new_rec_sig = recovery::RecoverableSignature::from_compact(&data, rec_id).unwrap(); + let new_rec_sig = ecdsa::RecoverableSignature::from_compact(&data, rec_id).unwrap(); assert_eq!(rec_sig, new_rec_sig); let mut cbor_ser = [0u8; 100]; @@ -121,7 +121,7 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize { let mut ser = Serializer::new(writer); sig.serialize(&mut ser).unwrap(); let size = ser.into_inner().bytes_written(); - let new_sig: Signature = de::from_mut_slice(&mut cbor_ser[..size]).unwrap(); + let new_sig: ecdsa::Signature = de::from_mut_slice(&mut cbor_ser[..size]).unwrap(); assert_eq!(sig, new_sig); let _ = SharedSecret::new(&public_key, &secret_key); diff --git a/src/ecdsa/mod.rs b/src/ecdsa/mod.rs new file mode 100644 index 000000000..cd88c0436 --- /dev/null +++ b/src/ecdsa/mod.rs @@ -0,0 +1,307 @@ +//! Structs and functionality related to the ECDSA signature algorithm. + +use core::{fmt, str, ops}; +use Error; +use ffi::CPtr; +use ffi; +use from_hex; + +#[cfg(feature = "recovery")] +mod recovery; + +#[cfg(feature = "recovery")] +pub use self::recovery::{RecoveryId, RecoverableSignature}; + +/// An ECDSA signature +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Signature(pub(crate) ffi::Signature); + +/// A DER serialized Signature +#[derive(Copy, Clone)] +pub struct SerializedSignature { + data: [u8; 72], + len: usize, +} + +impl fmt::Debug for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let sig = self.serialize_der(); + for v in sig.iter() { + write!(f, "{:02x}", v)?; + } + Ok(()) + } +} + +impl str::FromStr for Signature { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut res = [0u8; 72]; + match from_hex(s, &mut res) { + Ok(x) => Signature::from_der(&res[0..x]), + _ => Err(Error::InvalidSignature), + } + } +} + +impl Default for SerializedSignature { + fn default() -> SerializedSignature { + SerializedSignature { + data: [0u8; 72], + len: 0, + } + } +} + +impl PartialEq for SerializedSignature { + fn eq(&self, other: &SerializedSignature) -> bool { + self.data[..self.len] == other.data[..other.len] + } +} + +impl AsRef<[u8]> for SerializedSignature { + fn as_ref(&self) -> &[u8] { + &self.data[..self.len] + } +} + +impl ops::Deref for SerializedSignature { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + &self.data[..self.len] + } +} + +impl Eq for SerializedSignature {} + +impl SerializedSignature { + /// Get a pointer to the underlying data with the specified capacity. + pub(crate) fn get_data_mut_ptr(&mut self) -> *mut u8 { + self.data.as_mut_ptr() + } + + /// Get the capacity of the underlying data buffer. + pub fn capacity(&self) -> usize { + self.data.len() + } + + /// Get the len of the used data. + pub fn len(&self) -> usize { + self.len + } + + /// Set the length of the object. + pub(crate) fn set_len(&mut self, len: usize) { + self.len = len; + } + + /// Convert the serialized signature into the Signature struct. + /// (This DER deserializes it) + pub fn to_signature(&self) -> Result { + Signature::from_der(&self) + } + + /// Create a SerializedSignature from a Signature. + /// (this DER serializes it) + pub fn from_signature(sig: &Signature) -> SerializedSignature { + sig.serialize_der() + } + + /// Check if the space is zero. + pub fn is_empty(&self) -> bool { self.len() == 0 } +} + +impl Signature { + #[inline] + /// Converts a DER-encoded byte slice to a signature + pub fn from_der(data: &[u8]) -> Result { + if data.is_empty() {return Err(Error::InvalidSignature);} + + unsafe { + let mut ret = ffi::Signature::new(); + if ffi::secp256k1_ecdsa_signature_parse_der( + ffi::secp256k1_context_no_precomp, + &mut ret, + data.as_c_ptr(), + data.len() as usize, + ) == 1 + { + Ok(Signature(ret)) + } else { + Err(Error::InvalidSignature) + } + } + } + + /// Converts a 64-byte compact-encoded byte slice to a signature + pub fn from_compact(data: &[u8]) -> Result { + if data.len() != 64 { + return Err(Error::InvalidSignature) + } + + unsafe { + let mut ret = ffi::Signature::new(); + if ffi::secp256k1_ecdsa_signature_parse_compact( + ffi::secp256k1_context_no_precomp, + &mut ret, + data.as_c_ptr(), + ) == 1 + { + Ok(Signature(ret)) + } else { + Err(Error::InvalidSignature) + } + } + } + + /// Converts a "lax DER"-encoded byte slice to a signature. This is basically + /// only useful for validating signatures in the Bitcoin blockchain from before + /// 2016. It should never be used in new applications. This library does not + /// support serializing to this "format" + pub fn from_der_lax(data: &[u8]) -> Result { + if data.is_empty() {return Err(Error::InvalidSignature);} + + unsafe { + let mut ret = ffi::Signature::new(); + if ffi::ecdsa_signature_parse_der_lax( + ffi::secp256k1_context_no_precomp, + &mut ret, + data.as_c_ptr(), + data.len() as usize, + ) == 1 + { + Ok(Signature(ret)) + } else { + Err(Error::InvalidSignature) + } + } + } + + /// Normalizes a signature to a "low S" form. In ECDSA, signatures are + /// of the form (r, s) where r and s are numbers lying in some finite + /// field. The verification equation will pass for (r, s) iff it passes + /// for (r, -s), so it is possible to ``modify'' signatures in transit + /// by flipping the sign of s. This does not constitute a forgery since + /// the signed message still cannot be changed, but for some applications, + /// changing even the signature itself can be a problem. Such applications + /// require a "strong signature". It is believed that ECDSA is a strong + /// signature except for this ambiguity in the sign of s, so to accommodate + /// these applications libsecp256k1 will only accept signatures for which + /// s is in the lower half of the field range. This eliminates the + /// ambiguity. + /// + /// However, for some systems, signatures with high s-values are considered + /// valid. (For example, parsing the historic Bitcoin blockchain requires + /// this.) For these applications we provide this normalization function, + /// which ensures that the s value lies in the lower half of its range. + pub fn normalize_s(&mut self) { + unsafe { + // Ignore return value, which indicates whether the sig + // was already normalized. We don't care. + ffi::secp256k1_ecdsa_signature_normalize( + ffi::secp256k1_context_no_precomp, + self.as_mut_c_ptr(), + self.as_c_ptr(), + ); + } + } + + /// Obtains a raw pointer suitable for use with FFI functions + #[inline] + pub fn as_ptr(&self) -> *const ffi::Signature { + &self.0 + } + + /// Obtains a raw mutable pointer suitable for use with FFI functions + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut ffi::Signature { + &mut self.0 + } + + #[inline] + /// Serializes the signature in DER format + pub fn serialize_der(&self) -> SerializedSignature { + let mut ret = SerializedSignature::default(); + let mut len: usize = ret.capacity(); + unsafe { + let err = ffi::secp256k1_ecdsa_signature_serialize_der( + ffi::secp256k1_context_no_precomp, + ret.get_data_mut_ptr(), + &mut len, + self.as_c_ptr(), + ); + debug_assert!(err == 1); + ret.set_len(len); + } + ret + } + + #[inline] + /// Serializes the signature in compact format + pub fn serialize_compact(&self) -> [u8; 64] { + let mut ret = [0u8; 64]; + unsafe { + let err = ffi::secp256k1_ecdsa_signature_serialize_compact( + ffi::secp256k1_context_no_precomp, + ret.as_mut_c_ptr(), + self.as_c_ptr(), + ); + debug_assert!(err == 1); + } + ret + } +} + +impl CPtr for Signature { + type Target = ffi::Signature; + + 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 signature from a FFI signature +impl From for Signature { + #[inline] + fn from(sig: ffi::Signature) -> Signature { + Signature(sig) + } +} + +#[cfg(feature = "serde")] +impl ::serde::Serialize for Signature { + fn serialize(&self, s: S) -> Result { + if s.is_human_readable() { + s.collect_str(self) + } else { + s.serialize_bytes(&self.serialize_der()) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> ::serde::Deserialize<'de> for Signature { + fn deserialize>(d: D) -> Result { + if d.is_human_readable() { + d.deserialize_str(::serde_util::FromStrVisitor::new( + "a hex string representing a DER encoded Signature" + )) + } else { + d.deserialize_bytes(::serde_util::BytesVisitor::new( + "raw byte stream, that represents a DER encoded Signature", + Signature::from_der + )) + } + } +} diff --git a/src/recovery.rs b/src/ecdsa/recovery.rs similarity index 92% rename from src/recovery.rs rename to src/ecdsa/recovery.rs index 21217cba1..55c9e7215 100644 --- a/src/recovery.rs +++ b/src/ecdsa/recovery.rs @@ -19,13 +19,11 @@ use core::ptr; use key; -use super::{Secp256k1, Message, Error, Signature, Verification, Signing}; use super::ffi as super_ffi; -pub use key::SecretKey; -pub use key::PublicKey; use self::super_ffi::CPtr; - use ffi::recovery as ffi; +use super::*; +use {Verification, Secp256k1, Signing, Message}; /// A tag used for recovering the public key from a compact signature #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -148,9 +146,14 @@ impl From for RecoverableSignature { impl Secp256k1 { /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce /// Requires a signing-capable context. - pub fn sign_recoverable(&self, msg: &Message, sk: &key::SecretKey) - -> RecoverableSignature { + #[deprecated(since = "0.21.0", note = "Use sign_ecdsa_recoverable instead.")] + pub fn sign_recoverable(&self, msg: &Message, sk: &key::SecretKey) -> RecoverableSignature { + self.sign_ecdsa_recoverable(msg, sk) + } + /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce + /// Requires a signing-capable context. + pub fn sign_ecdsa_recoverable(&self, msg: &Message, sk: &key::SecretKey) -> RecoverableSignature { let mut ret = ffi::RecoverableSignature::new(); unsafe { // We can assume the return value because it's not possible to construct @@ -175,7 +178,14 @@ impl Secp256k1 { impl Secp256k1 { /// Determines the public key for which `sig` is a valid signature for /// `msg`. Requires a verify-capable context. - pub fn recover(&self, msg: &Message, sig: &RecoverableSignature) + #[deprecated(since = "0.21.0", note = "Use recover_ecdsa instead.")] + pub fn recover(&self, msg: &Message, sig: &RecoverableSignature) -> Result { + self.recover_ecdsa(msg, sig) + } + + /// Determines the public key for which `sig` is a valid signature for + /// `msg`. Requires a verify-capable context. + pub fn recover_ecdsa(&self, msg: &Message, sig: &RecoverableSignature) -> Result { unsafe { @@ -192,12 +202,9 @@ impl Secp256k1 { #[cfg(test)] mod tests { + use super::*; use rand::{RngCore, thread_rng}; - use key::SecretKey; - use super::{RecoveryId, RecoverableSignature}; - use super::super::{Secp256k1, Message}; - use super::super::Error::{IncorrectSignature, InvalidSignature}; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; @@ -275,7 +282,7 @@ mod tests { let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); let msg = Message::from_slice(&msg).unwrap(); - assert_eq!(s.verify(&msg, &sig, &pk), Err(IncorrectSignature)); + assert_eq!(s.verify_ecdsa(&msg, &sig, &pk), Err(Error::IncorrectSignature)); let recovered_key = s.recover(&msg, &sigr).unwrap(); assert!(recovered_key != pk); @@ -306,7 +313,7 @@ mod tests { // Zero is not a valid sig let sig = RecoverableSignature::from_compact(&[0; 64], RecoveryId(0)).unwrap(); - assert_eq!(s.recover(&msg, &sig), Err(InvalidSignature)); + assert_eq!(s.recover(&msg, &sig), Err(Error::InvalidSignature)); // ...but 111..111 is let sig = RecoverableSignature::from_compact(&[1; 64], RecoveryId(0)).unwrap(); assert!(s.recover(&msg, &sig).is_ok()); diff --git a/src/lib.rs b/src/lib.rs index 02c10f121..b007b944b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,8 +47,8 @@ //! let (secret_key, public_key) = secp.generate_keypair(&mut rng); //! let message = Message::from_hashed_data::("Hello World!".as_bytes()); //! -//! let sig = secp.sign(&message, &secret_key); -//! assert!(secp.verify(&message, &sig, &public_key).is_ok()); +//! let sig = secp.sign_ecdsa(&message, &secret_key); +//! assert!(secp.verify_ecdsa(&message, &sig, &public_key).is_ok()); //! # } //! ``` //! @@ -66,14 +66,14 @@ //! // See the above example for how to use this library together with bitcoin_hashes. //! let message = Message::from_slice(&[0xab; 32]).expect("32 bytes"); //! -//! let sig = secp.sign(&message, &secret_key); -//! assert!(secp.verify(&message, &sig, &public_key).is_ok()); +//! let sig = secp.sign_ecdsa(&message, &secret_key); +//! assert!(secp.verify_ecdsa(&message, &sig, &public_key).is_ok()); //! ``` //! //! Users who only want to verify signatures can use a cheaper context, like so: //! //! ```rust -//! use secp256k1::{Secp256k1, Message, Signature, PublicKey}; +//! use secp256k1::{Secp256k1, Message, ecdsa, PublicKey}; //! //! let secp = Secp256k1::verification_only(); //! @@ -92,7 +92,7 @@ //! 0xd5, 0x44, 0x53, 0xcf, 0x6e, 0x82, 0xb4, 0x50, //! ]).expect("messages must be 32 bytes and are expected to be hashes"); //! -//! let sig = Signature::from_compact(&[ +//! let sig = ecdsa::Signature::from_compact(&[ //! 0xdc, 0x4d, 0xc2, 0x64, 0xa9, 0xfe, 0xf1, 0x7a, //! 0x3f, 0x25, 0x34, 0x49, 0xcf, 0x8c, 0x39, 0x7a, //! 0xb6, 0xf1, 0x6f, 0xb3, 0xd6, 0x3d, 0x86, 0x94, @@ -104,7 +104,7 @@ //! ]).expect("compact signatures are 64 bytes; DER signatures are 68-72 bytes"); //! //! # #[cfg(not(fuzzing))] -//! assert!(secp.verify(&message, &sig, &public_key).is_ok()); +//! assert!(secp.verify_ecdsa(&message, &sig, &public_key).is_ok()); //! ``` //! //! Observe that the same code using, say [`signing_only`](struct.Secp256k1.html#method.signing_only) @@ -147,9 +147,8 @@ mod key; pub mod constants; pub mod ecdh; +pub mod ecdsa; pub mod schnorrsig; -#[cfg(feature = "recovery")] -pub mod recovery; #[cfg(feature = "serde")] mod serde_util; @@ -158,7 +157,6 @@ pub use key::PublicKey; pub use key::ONE_KEY; pub use context::*; use core::marker::PhantomData; -use core::ops::Deref; use core::{mem, fmt, ptr, str}; use ffi::{CPtr, types::AlignedType}; @@ -168,44 +166,6 @@ pub use context::global::SECP256K1; #[cfg(feature = "bitcoin_hashes")] use hashes::Hash; -/// An ECDSA signature -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct Signature(ffi::Signature); - -/// A DER serialized Signature -#[derive(Copy, Clone)] -pub struct SerializedSignature { - data: [u8; 72], - len: usize, -} - -impl fmt::Debug for Signature { -fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) -} -} - -impl fmt::Display for Signature { -fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let sig = self.serialize_der(); - for v in sig.iter() { - write!(f, "{:02x}", v)?; - } - Ok(()) -} -} - -impl str::FromStr for Signature { -type Err = Error; -fn from_str(s: &str) -> Result { - let mut res = [0u8; 72]; - match from_hex(s, &mut res) { - Ok(x) => Signature::from_der(&res[0..x]), - _ => Err(Error::InvalidSignature), - } -} -} - /// Trait describing something that promises to be a 32-byte random number; in particular, /// it has negligible probability of being zero or overflowing the group order. Such objects /// may be converted to `Message`s without any error paths. @@ -235,231 +195,6 @@ impl ThirtyTwoByteHash for hashes::sha256t::Hash { } } -impl SerializedSignature { - /// Get a pointer to the underlying data with the specified capacity. - pub(crate) fn get_data_mut_ptr(&mut self) -> *mut u8 { - self.data.as_mut_ptr() - } - - /// Get the capacity of the underlying data buffer. - pub fn capacity(&self) -> usize { - self.data.len() - } - - /// Get the len of the used data. - pub fn len(&self) -> usize { - self.len - } - - /// Set the length of the object. - pub(crate) fn set_len(&mut self, len: usize) { - self.len = len; - } - - /// Convert the serialized signature into the Signature struct. - /// (This DER deserializes it) - pub fn to_signature(&self) -> Result { - Signature::from_der(&self) - } - - /// Create a SerializedSignature from a Signature. - /// (this DER serializes it) - pub fn from_signature(sig: &Signature) -> SerializedSignature { - sig.serialize_der() - } - - /// Check if the space is zero. - pub fn is_empty(&self) -> bool { self.len() == 0 } -} - -impl Signature { -#[inline] - /// Converts a DER-encoded byte slice to a signature - pub fn from_der(data: &[u8]) -> Result { - if data.is_empty() {return Err(Error::InvalidSignature);} - - unsafe { - let mut ret = ffi::Signature::new(); - if ffi::secp256k1_ecdsa_signature_parse_der( - ffi::secp256k1_context_no_precomp, - &mut ret, - data.as_c_ptr(), - data.len() as usize, - ) == 1 - { - Ok(Signature(ret)) - } else { - Err(Error::InvalidSignature) - } - } - } - - /// Converts a 64-byte compact-encoded byte slice to a signature - pub fn from_compact(data: &[u8]) -> Result { - if data.len() != 64 { - return Err(Error::InvalidSignature) - } - - unsafe { - let mut ret = ffi::Signature::new(); - if ffi::secp256k1_ecdsa_signature_parse_compact( - ffi::secp256k1_context_no_precomp, - &mut ret, - data.as_c_ptr(), - ) == 1 - { - Ok(Signature(ret)) - } else { - Err(Error::InvalidSignature) - } - } - } - - /// Converts a "lax DER"-encoded byte slice to a signature. This is basically - /// only useful for validating signatures in the Bitcoin blockchain from before - /// 2016. It should never be used in new applications. This library does not - /// support serializing to this "format" - pub fn from_der_lax(data: &[u8]) -> Result { - if data.is_empty() {return Err(Error::InvalidSignature);} - - unsafe { - let mut ret = ffi::Signature::new(); - if ffi::ecdsa_signature_parse_der_lax( - ffi::secp256k1_context_no_precomp, - &mut ret, - data.as_c_ptr(), - data.len() as usize, - ) == 1 - { - Ok(Signature(ret)) - } else { - Err(Error::InvalidSignature) - } - } - } - - /// Normalizes a signature to a "low S" form. In ECDSA, signatures are - /// of the form (r, s) where r and s are numbers lying in some finite - /// field. The verification equation will pass for (r, s) iff it passes - /// for (r, -s), so it is possible to ``modify'' signatures in transit - /// by flipping the sign of s. This does not constitute a forgery since - /// the signed message still cannot be changed, but for some applications, - /// changing even the signature itself can be a problem. Such applications - /// require a "strong signature". It is believed that ECDSA is a strong - /// signature except for this ambiguity in the sign of s, so to accommodate - /// these applications libsecp256k1 will only accept signatures for which - /// s is in the lower half of the field range. This eliminates the - /// ambiguity. - /// - /// However, for some systems, signatures with high s-values are considered - /// valid. (For example, parsing the historic Bitcoin blockchain requires - /// this.) For these applications we provide this normalization function, - /// which ensures that the s value lies in the lower half of its range. - pub fn normalize_s(&mut self) { - unsafe { - // Ignore return value, which indicates whether the sig - // was already normalized. We don't care. - ffi::secp256k1_ecdsa_signature_normalize( - ffi::secp256k1_context_no_precomp, - self.as_mut_c_ptr(), - self.as_c_ptr(), - ); - } - } - - /// Obtains a raw pointer suitable for use with FFI functions - #[inline] - pub fn as_ptr(&self) -> *const ffi::Signature { - &self.0 - } - - /// Obtains a raw mutable pointer suitable for use with FFI functions - #[inline] - pub fn as_mut_ptr(&mut self) -> *mut ffi::Signature { - &mut self.0 - } - - #[inline] - /// Serializes the signature in DER format - pub fn serialize_der(&self) -> SerializedSignature { - let mut ret = SerializedSignature::default(); - let mut len: usize = ret.capacity(); - unsafe { - let err = ffi::secp256k1_ecdsa_signature_serialize_der( - ffi::secp256k1_context_no_precomp, - ret.get_data_mut_ptr(), - &mut len, - self.as_c_ptr(), - ); - debug_assert!(err == 1); - ret.set_len(len); - } - ret - } - - #[inline] - /// Serializes the signature in compact format - pub fn serialize_compact(&self) -> [u8; 64] { - let mut ret = [0u8; 64]; - unsafe { - let err = ffi::secp256k1_ecdsa_signature_serialize_compact( - ffi::secp256k1_context_no_precomp, - ret.as_mut_c_ptr(), - self.as_c_ptr(), - ); - debug_assert!(err == 1); - } - ret - } -} - -impl CPtr for Signature { - type Target = ffi::Signature; - 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 signature from a FFI signature -impl From for Signature { - #[inline] - fn from(sig: ffi::Signature) -> Signature { - Signature(sig) - } -} - - -#[cfg(feature = "serde")] -impl ::serde::Serialize for Signature { - fn serialize(&self, s: S) -> Result { - if s.is_human_readable() { - s.collect_str(self) - } else { - s.serialize_bytes(&self.serialize_der()) - } - } -} - -#[cfg(feature = "serde")] -impl<'de> ::serde::Deserialize<'de> for Signature { - fn deserialize>(d: D) -> Result { - if d.is_human_readable() { - d.deserialize_str(serde_util::FromStrVisitor::new( - "a hex string representing a DER encoded Signature" - )) - } else { - d.deserialize_bytes(serde_util::BytesVisitor::new( - "raw byte stream, that represents a DER encoded Signature", - Signature::from_der - )) - } - } -} - /// A (hashed) message input to an ECDSA signature pub struct Message([u8; constants::MESSAGE_SIZE]); impl_array_newtype!(Message, u8, constants::MESSAGE_SIZE); @@ -575,41 +310,10 @@ unsafe impl Send for Secp256k1 {} // The API does not permit any mutation of `Secp256k1` objects except through `&mut` references unsafe impl Sync for Secp256k1 {} - impl PartialEq for Secp256k1 { fn eq(&self, _other: &Secp256k1) -> bool { true } } -impl Default for SerializedSignature { - fn default() -> SerializedSignature { - SerializedSignature { - data: [0u8; 72], - len: 0, - } - } -} - -impl PartialEq for SerializedSignature { - fn eq(&self, other: &SerializedSignature) -> bool { - self.data[..self.len] == other.data[..other.len] - } -} - -impl AsRef<[u8]> for SerializedSignature { - fn as_ref(&self) -> &[u8] { - &self.data[..self.len] - } -} - -impl Deref for SerializedSignature { - type Target = [u8]; - fn deref(&self) -> &[u8] { - &self.data[..self.len] - } -} - -impl Eq for SerializedSignature {} - impl Eq for Secp256k1 { } impl Drop for Secp256k1 { @@ -706,9 +410,14 @@ impl Secp256k1 { /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce /// Requires a signing-capable context. - pub fn sign(&self, msg: &Message, sk: &key::SecretKey) - -> Signature { + #[deprecated(since = "0.21.0", note = "Use sign_ecdsa instead.")] + pub fn sign(&self, msg: &Message, sk: &key::SecretKey) -> ecdsa::Signature { + self.sign_ecdsa(msg, sk) + } + /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce + /// Requires a signing-capable context. + pub fn sign_ecdsa(&self, msg: &Message, sk: &key::SecretKey) -> ecdsa::Signature { unsafe { let mut ret = ffi::Signature::new(); // We can assume the return value because it's not possible to construct @@ -716,14 +425,14 @@ impl Secp256k1 { assert_eq!(ffi::secp256k1_ecdsa_sign(self.ctx, &mut ret, msg.as_c_ptr(), sk.as_c_ptr(), ffi::secp256k1_nonce_function_rfc6979, ptr::null()), 1); - Signature::from(ret) + ecdsa::Signature::from(ret) } } fn sign_grind_with_check( &self, msg: &Message, - sk: &key::SecretKey, - check: impl Fn(&ffi::Signature) -> bool) -> Signature { + sk: &SecretKey, + check: impl Fn(&ffi::Signature) -> bool) -> ecdsa::Signature { let mut entropy_p : *const ffi::types::c_void = ptr::null(); let mut counter : u32 = 0; let mut extra_entropy = [0u8; 32]; @@ -736,7 +445,7 @@ impl Secp256k1 { sk.as_c_ptr(), ffi::secp256k1_nonce_function_rfc6979, entropy_p), 1); if check(&ret) { - return Signature::from(ret); + return ecdsa::Signature::from(ret); } counter += 1; @@ -751,7 +460,7 @@ impl Secp256k1 { // When fuzzing, these checks will usually spinloop forever, so just short-circuit them. #[cfg(fuzzing)] - return Signature::from(ret); + return ecdsa::Signature::from(ret); } } } @@ -762,7 +471,18 @@ impl Secp256k1 { /// of signing operation performed by this function is exponential in the /// number of bytes grinded. /// Requires a signing capable context. - pub fn sign_grind_r(&self, msg: &Message, sk: &key::SecretKey, bytes_to_grind: usize) -> Signature { + #[deprecated(since = "0.21.0", note = "Use sign_ecdsa_grind_r instead.")] + pub fn sign_grind_r(&self, msg: &Message, sk: &SecretKey, bytes_to_grind: usize) -> ecdsa::Signature { + self.sign_ecdsa_grind_r(msg, sk, bytes_to_grind) + } + + /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce + /// and "grinds" the nonce by passing extra entropy if necessary to produce + /// a signature that is less than 71 - bytes_to_grund bytes. The number + /// of signing operation performed by this function is exponential in the + /// number of bytes grinded. + /// Requires a signing capable context. + pub fn sign_ecdsa_grind_r(&self, msg: &Message, sk: &SecretKey, bytes_to_grind: usize) -> ecdsa::Signature { let len_check = |s : &ffi::Signature| der_length_check(s, 71 - bytes_to_grind); return self.sign_grind_with_check(msg, sk, len_check); } @@ -773,7 +493,18 @@ impl Secp256k1 { /// signature implementation of bitcoin core. In average, this function /// will perform two signing operations. /// Requires a signing capable context. - pub fn sign_low_r(&self, msg: &Message, sk: &key::SecretKey) -> Signature { + #[deprecated(since = "0.21.0", note = "Use sign_ecdsa_grind_r instead.")] + pub fn sign_low_r(&self, msg: &Message, sk: &SecretKey) -> ecdsa::Signature { + return self.sign_grind_with_check(msg, sk, compact_sig_has_zero_first_bit) + } + + /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce + /// and "grinds" the nonce by passing extra entropy if necessary to produce + /// a signature that is less than 71 bytes and compatible with the low r + /// signature implementation of bitcoin core. In average, this function + /// will perform two signing operations. + /// Requires a signing capable context. + pub fn sign_ecdsa_low_r(&self, msg: &Message, sk: &SecretKey) -> ecdsa::Signature { return self.sign_grind_with_check(msg, sk, compact_sig_has_zero_first_bit) } @@ -816,7 +547,36 @@ impl Secp256k1 { /// # } /// ``` #[inline] - pub fn verify(&self, msg: &Message, sig: &Signature, pk: &key::PublicKey) -> Result<(), Error> { + #[deprecated(since = "0.21.0", note = "Use verify_ecdsa instead")] + pub fn verify(&self, msg: &Message, sig: &ecdsa::Signature, pk: &PublicKey) -> Result<(), Error> { + self.verify_ecdsa(msg, sig, pk) + } + + /// Checks that `sig` is a valid ECDSA signature for `msg` using the public + /// key `pubkey`. Returns `Ok(())` on success. Note that this function cannot + /// be used for Bitcoin consensus checking since there may exist signatures + /// which OpenSSL would verify but not libsecp256k1, or vice-versa. Requires a + /// verify-capable context. + /// + /// ```rust + /// # #[cfg(feature="rand")] { + /// # use secp256k1::rand::rngs::OsRng; + /// # use secp256k1::{Secp256k1, Message, Error}; + /// # + /// # let secp = Secp256k1::new(); + /// # let mut rng = OsRng::new().expect("OsRng"); + /// # let (secret_key, public_key) = secp.generate_keypair(&mut rng); + /// # + /// let message = Message::from_slice(&[0xab; 32]).expect("32 bytes"); + /// let sig = secp.sign_ecdsa(&message, &secret_key); + /// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Ok(())); + /// + /// let message = Message::from_slice(&[0xcd; 32]).expect("32 bytes"); + /// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(Error::IncorrectSignature)); + /// # } + /// ``` + #[inline] + pub fn verify_ecdsa(&self, msg: &Message, sig: &ecdsa::Signature, pk: &PublicKey) -> Result<(), Error> { unsafe { if ffi::secp256k1_ecdsa_verify(self.ctx, sig.as_c_ptr(), msg.as_c_ptr(), pk.as_c_ptr()) == 0 { Err(Error::IncorrectSignature) @@ -911,12 +671,12 @@ mod tests { let (sk, pk) = full.generate_keypair(&mut thread_rng()); let msg = Message::from_slice(&[2u8; 32]).unwrap(); // Try signing - assert_eq!(sign.sign(&msg, &sk), full.sign(&msg, &sk)); - let sig = full.sign(&msg, &sk); + assert_eq!(sign.sign_ecdsa(&msg, &sk), full.sign_ecdsa(&msg, &sk)); + let sig = full.sign_ecdsa(&msg, &sk); // Try verifying - assert!(vrfy.verify(&msg, &sig, &pk).is_ok()); - assert!(full.verify(&msg, &sig, &pk).is_ok()); + assert!(vrfy.verify_ecdsa(&msg, &sig, &pk).is_ok()); + assert!(full.verify_ecdsa(&msg, &sig, &pk).is_ok()); drop(full);drop(sign);drop(vrfy); @@ -940,12 +700,12 @@ mod tests { let (sk, pk) = full.generate_keypair(&mut thread_rng()); let msg = Message::from_slice(&[2u8; 32]).unwrap(); // Try signing - assert_eq!(sign.sign(&msg, &sk), full.sign(&msg, &sk)); - let sig = full.sign(&msg, &sk); + assert_eq!(sign.sign_ecdsa(&msg, &sk), full.sign_ecdsa(&msg, &sk)); + let sig = full.sign_ecdsa(&msg, &sk); // Try verifying - assert!(vrfy.verify(&msg, &sig, &pk).is_ok()); - assert!(full.verify(&msg, &sig, &pk).is_ok()); + assert!(vrfy.verify_ecdsa(&msg, &sig, &pk).is_ok()); + assert!(full.verify_ecdsa(&msg, &sig, &pk).is_ok()); unsafe { ManuallyDrop::drop(&mut full); @@ -984,12 +744,12 @@ mod tests { let (sk, pk) = full.generate_keypair(&mut thread_rng()); let msg = Message::from_slice(&[2u8; 32]).unwrap(); // Try signing - assert_eq!(sign.sign(&msg, &sk), full.sign(&msg, &sk)); - let sig = full.sign(&msg, &sk); + assert_eq!(sign.sign_ecdsa(&msg, &sk), full.sign_ecdsa(&msg, &sk)); + let sig = full.sign_ecdsa(&msg, &sk); // Try verifying - assert!(vrfy.verify(&msg, &sig, &pk).is_ok()); - assert!(full.verify(&msg, &sig, &pk).is_ok()); + assert!(vrfy.verify_ecdsa(&msg, &sig, &pk).is_ok()); + assert!(full.verify_ecdsa(&msg, &sig, &pk).is_ok()); } #[test] @@ -1006,12 +766,12 @@ mod tests { let (sk, pk) = full.generate_keypair(&mut thread_rng()); // Try signing - assert_eq!(sign.sign(&msg, &sk), full.sign(&msg, &sk)); - let sig = full.sign(&msg, &sk); + assert_eq!(sign.sign_ecdsa(&msg, &sk), full.sign_ecdsa(&msg, &sk)); + let sig = full.sign_ecdsa(&msg, &sk); // Try verifying - assert!(vrfy.verify(&msg, &sig, &pk).is_ok()); - assert!(full.verify(&msg, &sig, &pk).is_ok()); + assert!(vrfy.verify_ecdsa(&msg, &sig, &pk).is_ok()); + assert!(full.verify_ecdsa(&msg, &sig, &pk).is_ok()); // Check that we can produce keys from slices with no precomputation let (pk_slice, sk_slice) = (&pk.serialize(), &sk[..]); @@ -1032,19 +792,19 @@ mod tests { let msg = Message::from_slice(&msg).unwrap(); let (sk, _) = s.generate_keypair(&mut thread_rng()); - let sig1 = s.sign(&msg, &sk); + let sig1 = s.sign_ecdsa(&msg, &sk); let der = sig1.serialize_der(); - let sig2 = Signature::from_der(&der[..]).unwrap(); + let sig2 = ecdsa::Signature::from_der(&der[..]).unwrap(); assert_eq!(sig1, sig2); let compact = sig1.serialize_compact(); - let sig2 = Signature::from_compact(&compact[..]).unwrap(); + let sig2 = ecdsa::Signature::from_compact(&compact[..]).unwrap(); assert_eq!(sig1, sig2); - assert!(Signature::from_compact(&der[..]).is_err()); - assert!(Signature::from_compact(&compact[0..4]).is_err()); - assert!(Signature::from_der(&compact[..]).is_err()); - assert!(Signature::from_der(&der[0..4]).is_err()); + assert!(ecdsa::Signature::from_compact(&der[..]).is_err()); + assert!(ecdsa::Signature::from_compact(&compact[0..4]).is_err()); + assert!(ecdsa::Signature::from_der(&compact[..]).is_err()); + assert!(ecdsa::Signature::from_der(&der[0..4]).is_err()); } } @@ -1054,27 +814,27 @@ mod tests { let byte_str = hex!(hex_str); assert_eq!( - Signature::from_der(&byte_str).expect("byte str decode"), - Signature::from_str(&hex_str).expect("byte str decode") + ecdsa::Signature::from_der(&byte_str).expect("byte str decode"), + ecdsa::Signature::from_str(&hex_str).expect("byte str decode") ); - let sig = Signature::from_str(&hex_str).expect("byte str decode"); + let sig = ecdsa::Signature::from_str(&hex_str).expect("byte str decode"); assert_eq!(&sig.to_string(), hex_str); assert_eq!(&format!("{:?}", sig), hex_str); - assert!(Signature::from_str( + assert!(ecdsa::Signature::from_str( "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a\ 72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab4" ).is_err()); - assert!(Signature::from_str( + assert!(ecdsa::Signature::from_str( "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a\ 72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab" ).is_err()); - assert!(Signature::from_str( + assert!(ecdsa::Signature::from_str( "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a\ 72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eabxx" ).is_err()); - assert!(Signature::from_str( + assert!(ecdsa::Signature::from_str( "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a\ 72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab45\ 72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab45\ @@ -1085,7 +845,7 @@ mod tests { // 71 byte signature let hex_str = "30450221009d0bad576719d32ae76bedb34c774866673cbde3f4e12951555c9408e6ce774b02202876e7102f204f6bfee26c967c3926ce702cf97d4b010062e193f763190f6776"; - let sig = Signature::from_str(&hex_str).expect("byte str decode"); + let sig = ecdsa::Signature::from_str(&hex_str).expect("byte str decode"); assert_eq!(&format!("{}", sig), hex_str); } @@ -1094,7 +854,7 @@ mod tests { macro_rules! check_lax_sig( ($hex:expr) => ({ let sig = hex!($hex); - assert!(Signature::from_der_lax(&sig[..]).is_ok()); + assert!(ecdsa::Signature::from_der_lax(&sig[..]).is_ok()); }) ); @@ -1108,7 +868,7 @@ mod tests { } #[test] - fn sign_and_verify() { + fn sign_and_verify_ecdsa() { let mut s = Secp256k1::new(); s.randomize(&mut thread_rng()); @@ -1118,12 +878,12 @@ mod tests { let msg = Message::from_slice(&msg).unwrap(); let (sk, pk) = s.generate_keypair(&mut thread_rng()); - let sig = s.sign(&msg, &sk); - assert_eq!(s.verify(&msg, &sig, &pk), Ok(())); - let low_r_sig = s.sign_low_r(&msg, &sk); - assert_eq!(s.verify(&msg, &low_r_sig, &pk), Ok(())); - let grind_r_sig = s.sign_grind_r(&msg, &sk, 1); - assert_eq!(s.verify(&msg, &grind_r_sig, &pk), Ok(())); + let sig = s.sign_ecdsa(&msg, &sk); + assert_eq!(s.verify_ecdsa(&msg, &sig, &pk), Ok(())); + let low_r_sig = s.sign_ecdsa_low_r(&msg, &sk); + assert_eq!(s.verify_ecdsa(&msg, &low_r_sig, &pk), Ok(())); + let grind_r_sig = s.sign_ecdsa_grind_r(&msg, &sk, 1); + assert_eq!(s.verify_ecdsa(&msg, &grind_r_sig, &pk), Ok(())); let compact = sig.serialize_compact(); if compact[0] < 0x80 { assert_eq!(sig, low_r_sig); @@ -1160,13 +920,13 @@ mod tests { for key in wild_keys.iter().map(|k| SecretKey::from_slice(&k[..]).unwrap()) { for msg in wild_msgs.iter().map(|m| Message::from_slice(&m[..]).unwrap()) { - let sig = s.sign(&msg, &key); - let low_r_sig = s.sign_low_r(&msg, &key); - let grind_r_sig = s.sign_grind_r(&msg, &key, 1); + let sig = s.sign_ecdsa(&msg, &key); + let low_r_sig = s.sign_ecdsa_low_r(&msg, &key); + let grind_r_sig = s.sign_ecdsa_grind_r(&msg, &key, 1); let pk = PublicKey::from_secret_key(&s, &key); - assert_eq!(s.verify(&msg, &sig, &pk), Ok(())); - assert_eq!(s.verify(&msg, &low_r_sig, &pk), Ok(())); - assert_eq!(s.verify(&msg, &grind_r_sig, &pk), Ok(())); + assert_eq!(s.verify_ecdsa(&msg, &sig, &pk), Ok(())); + assert_eq!(s.verify_ecdsa(&msg, &low_r_sig, &pk), Ok(())); + assert_eq!(s.verify_ecdsa(&msg, &grind_r_sig, &pk), Ok(())); } } } @@ -1182,19 +942,19 @@ mod tests { let (sk, pk) = s.generate_keypair(&mut thread_rng()); - let sig = s.sign(&msg, &sk); + let sig = s.sign_ecdsa(&msg, &sk); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); let msg = Message::from_slice(&msg).unwrap(); - assert_eq!(s.verify(&msg, &sig, &pk), Err(Error::IncorrectSignature)); + assert_eq!(s.verify_ecdsa(&msg, &sig, &pk), Err(Error::IncorrectSignature)); } #[test] fn test_bad_slice() { - assert_eq!(Signature::from_der(&[0; constants::MAX_SIGNATURE_SIZE + 1]), + assert_eq!(ecdsa::Signature::from_der(&[0; constants::MAX_SIGNATURE_SIZE + 1]), Err(Error::InvalidSignature)); - assert_eq!(Signature::from_der(&[0; constants::MAX_SIGNATURE_SIZE]), + assert_eq!(ecdsa::Signature::from_der(&[0; constants::MAX_SIGNATURE_SIZE]), Err(Error::InvalidSignature)); assert_eq!(Message::from_slice(&[0; constants::MESSAGE_SIZE - 1]), @@ -1242,15 +1002,15 @@ mod tests { let msg = hex!("a4965ca63b7d8562736ceec36dfa5a11bf426eb65be8ea3f7a49ae363032da0d"); let secp = Secp256k1::new(); - let mut sig = Signature::from_der(&sig[..]).unwrap(); + let mut sig = ecdsa::Signature::from_der(&sig[..]).unwrap(); let pk = PublicKey::from_slice(&pk[..]).unwrap(); let msg = Message::from_slice(&msg[..]).unwrap(); // without normalization we expect this will fail - assert_eq!(secp.verify(&msg, &sig, &pk), Err(Error::IncorrectSignature)); + assert_eq!(secp.verify_ecdsa(&msg, &sig, &pk), Err(Error::IncorrectSignature)); // after normalization it should pass sig.normalize_s(); - assert_eq!(secp.verify(&msg, &sig, &pk), Ok(())); + assert_eq!(secp.verify_ecdsa(&msg, &sig, &pk), Ok(())); } #[test] @@ -1261,9 +1021,9 @@ mod tests { let msg = Message::from_slice(&msg).unwrap(); let sk = SecretKey::from_str("57f0148f94d13095cfda539d0da0d1541304b678d8b36e243980aab4e1b7cead").unwrap(); let expected_sig = hex!("047dd4d049db02b430d24c41c7925b2725bcd5a85393513bdec04b4dc363632b1054d0180094122b380f4cfa391e6296244da773173e78fc745c1b9c79f7b713"); - let expected_sig = Signature::from_compact(&expected_sig).unwrap(); + let expected_sig = ecdsa::Signature::from_compact(&expected_sig).unwrap(); - let sig = secp.sign_low_r(&msg, &sk); + let sig = secp.sign_ecdsa_low_r(&msg, &sk); assert_eq!(expected_sig, sig); } @@ -1275,9 +1035,9 @@ mod tests { let msg = hex!("ef2d5b9a7c61865a95941d0f04285420560df7e9d76890ac1b8867b12ce43167"); let msg = Message::from_slice(&msg).unwrap(); let sk = SecretKey::from_str("848355d75fe1c354cf05539bb29b2015f1863065bcb6766b44d399ab95c3fa0b").unwrap(); - let expected_sig = Signature::from_str("304302202ffc447100d518c8ba643d11f3e6a83a8640488e7d2537b1954b942408be6ea3021f26e1248dd1e52160c3a38af9769d91a1a806cab5f9d508c103464d3c02d6e1").unwrap(); + let expected_sig = ecdsa::Signature::from_str("304302202ffc447100d518c8ba643d11f3e6a83a8640488e7d2537b1954b942408be6ea3021f26e1248dd1e52160c3a38af9769d91a1a806cab5f9d508c103464d3c02d6e1").unwrap(); - let sig = secp.sign_grind_r(&msg, &sk, 2); + let sig = secp.sign_ecdsa_grind_r(&msg, &sk, 2); assert_eq!(expected_sig, sig); } @@ -1292,7 +1052,7 @@ mod tests { let msg = Message::from_slice(&[1; 32]).unwrap(); let sk = SecretKey::from_slice(&[2; 32]).unwrap(); - let sig = s.sign(&msg, &sk); + let sig = s.sign_ecdsa(&msg, &sk); static SIG_BYTES: [u8; 71] = [ 48, 69, 2, 33, 0, 157, 11, 173, 87, 103, 25, 211, 42, 231, 107, 237, 179, 76, 119, 72, 102, 103, 60, 189, 227, 244, 225, 41, 81, 85, 92, 148, @@ -1329,8 +1089,8 @@ mod tests { let pk = PublicKey::from_secret_key(&SECP256K1, &sk); // Check usage as self - let sig = SECP256K1.sign(&msg, &sk); - assert!(SECP256K1.verify(&msg, &sig, &pk).is_ok()); + let sig = SECP256K1.sign_ecdsa(&msg, &sk); + assert!(SECP256K1.verify_ecdsa(&msg, &sig, &pk).is_ok()); } #[cfg(feature = "bitcoin_hashes")] @@ -1402,7 +1162,7 @@ mod benches { } #[bench] - pub fn bench_sign(bh: &mut Bencher) { + pub fn bench_sign_ecdsa(bh: &mut Bencher) { let s = Secp256k1::new(); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); @@ -1410,22 +1170,22 @@ mod benches { let (sk, _) = s.generate_keypair(&mut thread_rng()); bh.iter(|| { - let sig = s.sign(&msg, &sk); + let sig = s.sign_ecdsa(&msg, &sk); black_box(sig); }); } #[bench] - pub fn bench_verify(bh: &mut Bencher) { + pub fn bench_verify_ecdsa(bh: &mut Bencher) { let s = Secp256k1::new(); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); let msg = Message::from_slice(&msg).unwrap(); let (sk, pk) = s.generate_keypair(&mut thread_rng()); - let sig = s.sign(&msg, &sk); + let sig = s.sign_ecdsa(&msg, &sk); bh.iter(|| { - let res = s.verify(&msg, &sig, &pk).unwrap(); + let res = s.verify_ecdsa(&msg, &sig, &pk).unwrap(); black_box(res); }); } From 2e0e731664daf956f29b55ea3511f774d7b78a82 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 9 Sep 2021 19:34:48 +1000 Subject: [PATCH 05/12] Move `KeyPair` to `key` module The `KeyPair` type is semantically unrelated to the schnorr signature algorithm. --- src/key.rs | 177 ++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 1 + src/schnorrsig.rs | 171 +------------------------------------------- src/secret.rs | 4 +- 4 files changed, 177 insertions(+), 176 deletions(-) diff --git a/src/key.rs b/src/key.rs index 04bfd25d6..4da3818fa 100644 --- a/src/key.rs +++ b/src/key.rs @@ -134,9 +134,9 @@ impl SecretKey { } } - /// Creates a new secret key using data from BIP-340 [`::schnorrsig::KeyPair`] + /// Creates a new secret key using data from BIP-340 [`KeyPair`] #[inline] - pub fn from_keypair(keypair: &::schnorrsig::KeyPair) -> Self { + pub fn from_keypair(keypair: &KeyPair) -> Self { let mut sk = [0u8; constants::SECRET_KEY_SIZE]; unsafe { let ret = ffi::secp256k1_keypair_sec( @@ -297,9 +297,9 @@ impl PublicKey { } } - /// Creates a new compressed public key key using data from BIP-340 [`::schnorrsig::KeyPair`] + /// Creates a new compressed public key key using data from BIP-340 [`KeyPair`] #[inline] - pub fn from_keypair(keypair: &::schnorrsig::KeyPair) -> Self { + pub fn from_keypair(keypair: &KeyPair) -> Self { unsafe { let mut pk = ffi::PublicKey::new(); let ret = ffi::secp256k1_keypair_pub( @@ -505,6 +505,175 @@ impl Ord for PublicKey { } } +/// Opaque data structure that holds a keypair consisting of a secret and a public key. +#[derive(Clone)] +pub struct KeyPair(ffi::KeyPair); +impl_display_secret!(KeyPair); + +impl KeyPair { + /// Obtains a raw const pointer suitable for use with FFI functions + #[inline] + pub fn as_ptr(&self) -> *const ffi::KeyPair { + &self.0 + } + + /// Obtains a raw mutable pointer suitable for use with FFI functions + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut ffi::KeyPair { + &mut self.0 + } + + /// Creates a Schnorr KeyPair directly from generic Secp256k1 secret key + /// + /// # Panic + /// + /// Panics if internal representation of the provided [`SecretKey`] does not hold correct secret + /// key value obtained from Secp256k1 library previously, specifically when secret key value is + /// out-of-range (0 or in excess of the group order). + #[inline] + pub fn from_secret_key( + secp: &Secp256k1, + sk: SecretKey, + ) -> KeyPair { + unsafe { + let mut kp = ffi::KeyPair::new(); + if ffi::secp256k1_keypair_create(secp.ctx, &mut kp, sk.as_c_ptr()) == 1 { + KeyPair(kp) + } else { + panic!("the provided secret key is invalid: it is corrupted or was not produced by Secp256k1 library") + } + } + } + + /// Creates a Schnorr KeyPair directly from a secret key slice. + /// + /// # Errors + /// + /// [`Error::InvalidSecretKey`] if the provided data has an incorrect length, exceeds Secp256k1 + /// field `p` value or the corresponding public key is not even. + #[inline] + pub fn from_seckey_slice( + secp: &Secp256k1, + data: &[u8], + ) -> Result { + if data.is_empty() || data.len() != constants::SECRET_KEY_SIZE { + return Err(Error::InvalidSecretKey); + } + + unsafe { + let mut kp = ffi::KeyPair::new(); + if ffi::secp256k1_keypair_create(secp.ctx, &mut kp, data.as_c_ptr()) == 1 { + Ok(KeyPair(kp)) + } else { + Err(Error::InvalidSecretKey) + } + } + } + + /// Creates a Schnorr KeyPair directly from a secret key string + /// + /// # Errors + /// + /// [`Error::InvalidSecretKey`] if corresponding public key for the provided secret key is not even. + #[inline] + pub fn from_seckey_str(secp: &Secp256k1, s: &str) -> Result { + let mut res = [0u8; constants::SECRET_KEY_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::SECRET_KEY_SIZE) => { + KeyPair::from_seckey_slice(secp, &res[0..constants::SECRET_KEY_SIZE]) + } + _ => Err(Error::InvalidPublicKey), + } + } + + /// Creates a new random secret key. Requires compilation with the "rand" feature. + #[inline] + #[cfg(any(test, feature = "rand"))] + pub fn new(secp: &Secp256k1, rng: &mut R) -> KeyPair { + let mut random_32_bytes = || { + let mut ret = [0u8; 32]; + rng.fill_bytes(&mut ret); + ret + }; + let mut data = random_32_bytes(); + unsafe { + let mut keypair = ffi::KeyPair::new(); + while ffi::secp256k1_keypair_create(secp.ctx, &mut keypair, data.as_c_ptr()) == 0 { + data = random_32_bytes(); + } + KeyPair(keypair) + } + } + + /// Serialize the key pair as a secret key byte value + #[inline] + pub fn serialize_secret(&self) -> [u8; constants::SECRET_KEY_SIZE] { + *SecretKey::from_keypair(self).as_ref() + } + + /// Tweak a keypair by adding the given tweak to the secret key and updating the public key + /// accordingly. + /// + /// Will return an error if the resulting key would be invalid or if the tweak was not a 32-byte + /// length slice. + /// + /// NB: Will not error if the tweaked public key has an odd value and can't be used for + /// BIP 340-342 purposes. + // TODO: Add checked implementation + #[inline] + pub fn tweak_add_assign( + &mut self, + secp: &Secp256k1, + tweak: &[u8], + ) -> Result<(), Error> { + if tweak.len() != 32 { + return Err(Error::InvalidTweak); + } + + unsafe { + let err = ffi::secp256k1_keypair_xonly_tweak_add( + secp.ctx, + &mut self.0, + tweak.as_c_ptr(), + ); + + if err == 1 { + Ok(()) + } else { + Err(Error::InvalidTweak) + } + } + } +} + +impl From for SecretKey { + #[inline] + fn from(pair: KeyPair) -> Self { + SecretKey::from_keypair(&pair) + } +} + +impl<'a> From<&'a KeyPair> for SecretKey { + #[inline] + fn from(pair: &'a KeyPair) -> Self { + SecretKey::from_keypair(pair) + } +} + +impl From for PublicKey { + #[inline] + fn from(pair: KeyPair) -> Self { + PublicKey::from_keypair(&pair) + } +} + +impl<'a> From<&'a KeyPair> for PublicKey { + #[inline] + fn from(pair: &'a KeyPair) -> Self { + PublicKey::from_keypair(pair) + } +} + #[cfg(test)] mod test { use Secp256k1; diff --git a/src/lib.rs b/src/lib.rs index b007b944b..17c006cbd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,6 +155,7 @@ mod serde_util; pub use key::SecretKey; pub use key::PublicKey; pub use key::ONE_KEY; +pub use key::KeyPair; pub use context::*; use core::marker::PhantomData; use core::{mem, fmt, ptr, str}; diff --git a/src/schnorrsig.rs b/src/schnorrsig.rs index e750b160b..00de83df8 100644 --- a/src/schnorrsig.rs +++ b/src/schnorrsig.rs @@ -12,7 +12,7 @@ use core::{fmt, ptr, str}; use ffi::{self, CPtr}; use {constants, Secp256k1}; use {Message, Signing, Verification}; -use SecretKey; +use KeyPair; /// Represents a Schnorr signature. pub struct Signature([u8; constants::SCHNORRSIG_SIGNATURE_SIZE]); @@ -74,11 +74,6 @@ impl str::FromStr for Signature { } } -/// Opaque data structure that holds a keypair consisting of a secret and a public key. -#[derive(Clone)] -pub struct KeyPair(ffi::KeyPair); -impl_display_secret!(KeyPair); - /// A Schnorr public key, used for verification of Schnorr signatures #[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] pub struct PublicKey(ffi::XOnlyPublicKey); @@ -127,142 +122,6 @@ impl Signature { } } -impl KeyPair { - /// Obtains a raw const pointer suitable for use with FFI functions - #[inline] - pub fn as_ptr(&self) -> *const ffi::KeyPair { - &self.0 - } - - /// Obtains a raw mutable pointer suitable for use with FFI functions - #[inline] - pub fn as_mut_ptr(&mut self) -> *mut ffi::KeyPair { - &mut self.0 - } - - /// Creates a Schnorr KeyPair directly from generic Secp256k1 secret key - /// - /// # Panic - /// - /// Panics if internal representation of the provided [`SecretKey`] does not hold correct secret - /// key value obtained from Secp256k1 library previously, specifically when secret key value is - /// out-of-range (0 or in excess of the group order). - #[inline] - pub fn from_secret_key( - secp: &Secp256k1, - sk: ::key::SecretKey, - ) -> KeyPair { - unsafe { - let mut kp = ffi::KeyPair::new(); - if ffi::secp256k1_keypair_create(secp.ctx, &mut kp, sk.as_c_ptr()) == 1 { - KeyPair(kp) - } else { - panic!("the provided secret key is invalid: it is corrupted or was not produced by Secp256k1 library") - } - } - } - - /// Creates a Schnorr KeyPair directly from a secret key slice. - /// - /// # Errors - /// - /// [`Error::InvalidSecretKey`] if the provided data has an incorrect length, exceeds Secp256k1 - /// field `p` value or the corresponding public key is not even. - #[inline] - pub fn from_seckey_slice( - secp: &Secp256k1, - data: &[u8], - ) -> Result { - if data.is_empty() || data.len() != constants::SECRET_KEY_SIZE { - return Err(Error::InvalidSecretKey); - } - - unsafe { - let mut kp = ffi::KeyPair::new(); - if ffi::secp256k1_keypair_create(secp.ctx, &mut kp, data.as_c_ptr()) == 1 { - Ok(KeyPair(kp)) - } else { - Err(Error::InvalidSecretKey) - } - } - } - - /// Creates a Schnorr KeyPair directly from a secret key string - /// - /// # Errors - /// - /// [`Error::InvalidSecretKey`] if corresponding public key for the provided secret key is not even. - #[inline] - pub fn from_seckey_str(secp: &Secp256k1, s: &str) -> Result { - let mut res = [0u8; constants::SECRET_KEY_SIZE]; - match from_hex(s, &mut res) { - Ok(constants::SECRET_KEY_SIZE) => { - KeyPair::from_seckey_slice(secp, &res[0..constants::SECRET_KEY_SIZE]) - } - _ => Err(Error::InvalidPublicKey), - } - } - - /// Creates a new random secret key. Requires compilation with the "rand" feature. - #[inline] - #[cfg(any(test, feature = "rand"))] - pub fn new(secp: &Secp256k1, rng: &mut R) -> KeyPair { - let mut random_32_bytes = || { - let mut ret = [0u8; 32]; - rng.fill_bytes(&mut ret); - ret - }; - let mut data = random_32_bytes(); - unsafe { - let mut keypair = ffi::KeyPair::new(); - while ffi::secp256k1_keypair_create(secp.ctx, &mut keypair, data.as_c_ptr()) == 0 { - data = random_32_bytes(); - } - KeyPair(keypair) - } - } - - /// Serialize the key pair as a secret key byte value - #[inline] - pub fn serialize_secret(&self) -> [u8; constants::SECRET_KEY_SIZE] { - *SecretKey::from_keypair(self).as_ref() - } - - /// Tweak a keypair by adding the given tweak to the secret key and updating the public key - /// accordingly. - /// - /// Will return an error if the resulting key would be invalid or if the tweak was not a 32-byte - /// length slice. - /// - /// NB: Will not error if the tweaked public key has an odd value and can't be used for - /// BIP 340-342 purposes. - // TODO: Add checked implementation - #[inline] - pub fn tweak_add_assign( - &mut self, - secp: &Secp256k1, - tweak: &[u8], - ) -> Result<(), Error> { - if tweak.len() != 32 { - return Err(Error::InvalidTweak); - } - - unsafe { - let err = ffi::secp256k1_keypair_xonly_tweak_add( - secp.ctx, - &mut self.0, - tweak.as_c_ptr(), - ); - - if err == 1 { - Ok(()) - } else { - Err(Error::InvalidTweak) - } - } - } -} - impl PublicKey { /// Obtains a raw const pointer suitable for use with FFI functions #[inline] @@ -475,34 +334,6 @@ impl<'de> ::serde::Deserialize<'de> for PublicKey { } } -impl From for SecretKey { - #[inline] - fn from(pair: KeyPair) -> Self { - SecretKey::from_keypair(&pair) - } -} - -impl<'a> From<&'a KeyPair> for SecretKey { - #[inline] - fn from(pair: &'a KeyPair) -> Self { - SecretKey::from_keypair(pair) - } -} - -impl From for ::key::PublicKey { - #[inline] - fn from(pair: KeyPair) -> Self { - ::key::PublicKey::from_keypair(&pair) - } -} - -impl<'a> From<&'a KeyPair> for ::key::PublicKey { - #[inline] - fn from(pair: &'a KeyPair) -> Self { - ::key::PublicKey::from_keypair(pair) - } -} - impl Secp256k1 { fn schnorrsig_sign_helper( &self, diff --git a/src/secret.rs b/src/secret.rs index f7fc22e6f..564e17e24 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -15,7 +15,7 @@ //! Helpers for displaying secret values use ::core::fmt; -use ::{SecretKey, schnorrsig::KeyPair, to_hex}; +use ::{SecretKey, KeyPair, to_hex}; use constants::SECRET_KEY_SIZE; macro_rules! impl_display_secret { @@ -124,7 +124,7 @@ impl KeyPair { /// /// ``` /// use secp256k1::ONE_KEY; - /// use secp256k1::schnorrsig::KeyPair; + /// use secp256k1::KeyPair; /// use secp256k1::Secp256k1; /// /// let secp = Secp256k1::new(); From 87d936a765cb0b67d795fa4f84a900f7bece2a89 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 9 Sep 2021 19:37:31 +1000 Subject: [PATCH 06/12] Rename `schnorr::PublicKey` to `schnorr::XOnlyPublicKey` The public key is unrelated to the signature algorithm. It will be moved out of the module in another commit. For ease of review, the renamed is kept separate. --- src/schnorrsig.rs | 102 +++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/src/schnorrsig.rs b/src/schnorrsig.rs index 00de83df8..62d90d82f 100644 --- a/src/schnorrsig.rs +++ b/src/schnorrsig.rs @@ -74,11 +74,11 @@ impl str::FromStr for Signature { } } -/// A Schnorr public key, used for verification of Schnorr signatures +/// A x-only public key, used for verification of Schnorr signatures #[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] -pub struct PublicKey(ffi::XOnlyPublicKey); +pub struct XOnlyPublicKey(ffi::XOnlyPublicKey); -impl fmt::LowerHex for PublicKey { +impl fmt::LowerHex for XOnlyPublicKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let ser = self.serialize(); for ch in &ser[..] { @@ -88,19 +88,19 @@ impl fmt::LowerHex for PublicKey { } } -impl fmt::Display for PublicKey { +impl fmt::Display for XOnlyPublicKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) } } -impl str::FromStr for PublicKey { +impl str::FromStr for XOnlyPublicKey { type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { let mut res = [0u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE]; match from_hex(s, &mut res) { Ok(constants::SCHNORRSIG_PUBLIC_KEY_SIZE) => { - PublicKey::from_slice(&res[0..constants::SCHNORRSIG_PUBLIC_KEY_SIZE]) + XOnlyPublicKey::from_slice(&res[0..constants::SCHNORRSIG_PUBLIC_KEY_SIZE]) } _ => Err(Error::InvalidPublicKey), } @@ -122,7 +122,7 @@ impl Signature { } } -impl PublicKey { +impl XOnlyPublicKey { /// Obtains a raw const pointer suitable for use with FFI functions #[inline] pub fn as_ptr(&self) -> *const ffi::XOnlyPublicKey { @@ -137,7 +137,7 @@ impl PublicKey { /// Creates a new Schnorr public key from a Schnorr key pair. #[inline] - pub fn from_keypair(secp: &Secp256k1, keypair: &KeyPair) -> PublicKey { + pub fn from_keypair(secp: &Secp256k1, keypair: &KeyPair) -> XOnlyPublicKey { let mut pk_parity = 0; unsafe { let mut xonly_pk = ffi::XOnlyPublicKey::new(); @@ -148,7 +148,7 @@ impl PublicKey { keypair.as_ptr(), ); debug_assert_eq!(ret, 1); - PublicKey(xonly_pk) + XOnlyPublicKey(xonly_pk) } } @@ -159,7 +159,7 @@ impl PublicKey { /// Returns [`Error::InvalidPublicKey`] if the length of the data slice is not 32 bytes or the /// slice does not represent a valid Secp256k1 point x coordinate #[inline] - pub fn from_slice(data: &[u8]) -> Result { + pub fn from_slice(data: &[u8]) -> Result { if data.is_empty() || data.len() != constants::SCHNORRSIG_PUBLIC_KEY_SIZE { return Err(Error::InvalidPublicKey); } @@ -172,7 +172,7 @@ impl PublicKey { data.as_c_ptr(), ) == 1 { - Ok(PublicKey(pk)) + Ok(XOnlyPublicKey(pk)) } else { Err(Error::InvalidPublicKey) } @@ -270,7 +270,7 @@ impl PublicKey { } } -impl CPtr for PublicKey { +impl CPtr for XOnlyPublicKey { type Target = ffi::XOnlyPublicKey; fn as_c_ptr(&self) -> *const Self::Target { self.as_ptr() @@ -282,15 +282,15 @@ impl CPtr for PublicKey { } /// Creates a new Schnorr public key from a FFI x-only public key -impl From for PublicKey { +impl From for XOnlyPublicKey { #[inline] - fn from(pk: ffi::XOnlyPublicKey) -> PublicKey { - PublicKey(pk) + fn from(pk: ffi::XOnlyPublicKey) -> XOnlyPublicKey { + XOnlyPublicKey(pk) } } -impl From<::key::PublicKey> for PublicKey { - fn from(src: ::key::PublicKey) -> PublicKey { +impl From<::key::PublicKey> for XOnlyPublicKey { + fn from(src: ::key::PublicKey) -> XOnlyPublicKey { unsafe { let mut pk = ffi::XOnlyPublicKey::new(); assert_eq!( @@ -302,13 +302,13 @@ impl From<::key::PublicKey> for PublicKey { src.as_c_ptr(), ) ); - PublicKey(pk) + XOnlyPublicKey(pk) } } } #[cfg(feature = "serde")] -impl ::serde::Serialize for PublicKey { +impl ::serde::Serialize for XOnlyPublicKey { fn serialize(&self, s: S) -> Result { if s.is_human_readable() { s.collect_str(self) @@ -319,7 +319,7 @@ impl ::serde::Serialize for PublicKey { } #[cfg(feature = "serde")] -impl<'de> ::serde::Deserialize<'de> for PublicKey { +impl<'de> ::serde::Deserialize<'de> for XOnlyPublicKey { fn deserialize>(d: D) -> Result { if d.is_human_readable() { d.deserialize_str(super::serde_util::FromStrVisitor::new( @@ -328,7 +328,7 @@ impl<'de> ::serde::Deserialize<'de> for PublicKey { } else { d.deserialize_bytes(super::serde_util::BytesVisitor::new( "raw 32 bytes schnorr public key", - PublicKey::from_slice + XOnlyPublicKey::from_slice )) } } @@ -411,7 +411,7 @@ impl Secp256k1 { &self, sig: &Signature, msg: &Message, - pubkey: &PublicKey, + pubkey: &XOnlyPublicKey, ) -> Result<(), Error> { unsafe { let ret = ffi::secp256k1_schnorrsig_verify( @@ -439,9 +439,9 @@ impl Secp256k1 { pub fn generate_schnorrsig_keypair( &self, rng: &mut R, - ) -> (KeyPair, PublicKey) { + ) -> (KeyPair, XOnlyPublicKey) { let sk = KeyPair::new(self, rng); - let pubkey = PublicKey::from_keypair(self, &sk); + let pubkey = XOnlyPublicKey::from_keypair(self, &sk); (sk, pubkey) } } @@ -450,7 +450,7 @@ impl Secp256k1 { mod tests { use super::super::Error::InvalidPublicKey; use super::super::{constants, from_hex, All, Message, Secp256k1}; - use super::{KeyPair, PublicKey, Signature}; + use super::{KeyPair, XOnlyPublicKey, Signature}; use rand::{rngs::ThreadRng, thread_rng, Error, ErrorKind, RngCore}; use rand_core::impls; use std::iter; @@ -548,7 +548,7 @@ mod tests { let msg = Message::from_slice(&hex_msg).unwrap(); let sig = Signature::from_str("6470FD1303DDA4FDA717B9837153C24A6EAB377183FC438F939E0ED2B620E9EE5077C4A8B8DCA28963D772A94F5F0DDF598E1C47C137F91933274C7C3EDADCE8").unwrap(); let pubkey = - PublicKey::from_str("B33CC9EDC096D0A83416964BD3C6247B8FECD256E4EFA7870D2C854BDEB33390") + XOnlyPublicKey::from_str("B33CC9EDC096D0A83416964BD3C6247B8FECD256E4EFA7870D2C854BDEB33390") .unwrap(); assert!(secp.schnorrsig_verify(&sig, &msg, &pubkey).is_ok()); @@ -556,9 +556,9 @@ mod tests { #[test] fn test_pubkey_from_slice() { - assert_eq!(PublicKey::from_slice(&[]), Err(InvalidPublicKey)); - assert_eq!(PublicKey::from_slice(&[1, 2, 3]), Err(InvalidPublicKey)); - let pk = PublicKey::from_slice(&[ + assert_eq!(XOnlyPublicKey::from_slice(&[]), Err(InvalidPublicKey)); + assert_eq!(XOnlyPublicKey::from_slice(&[1, 2, 3]), Err(InvalidPublicKey)); + let pk = XOnlyPublicKey::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, @@ -571,7 +571,7 @@ mod tests { let secp = Secp256k1::new(); let (_, pubkey) = secp.generate_schnorrsig_keypair(&mut thread_rng()); let ser = pubkey.serialize(); - let pubkey2 = PublicKey::from_slice(&ser).unwrap(); + let pubkey2 = XOnlyPublicKey::from_slice(&ser).unwrap(); assert_eq!(pubkey, pubkey2); } @@ -584,35 +584,35 @@ mod tests { assert_eq!(SecretKey::from_str(sk_str).unwrap(), sk); let pk = ::key::PublicKey::from_keypair(&keypair); assert_eq!(::key::PublicKey::from_secret_key(&secp, &sk), pk); - let xpk = PublicKey::from_keypair(&secp, &keypair); - assert_eq!(PublicKey::from(pk), xpk); + let xpk = XOnlyPublicKey::from_keypair(&secp, &keypair); + assert_eq!(XOnlyPublicKey::from(pk), xpk); } #[test] fn test_pubkey_from_bad_slice() { // Bad sizes assert_eq!( - PublicKey::from_slice(&[0; constants::SCHNORRSIG_PUBLIC_KEY_SIZE - 1]), + XOnlyPublicKey::from_slice(&[0; constants::SCHNORRSIG_PUBLIC_KEY_SIZE - 1]), Err(InvalidPublicKey) ); assert_eq!( - PublicKey::from_slice(&[0; constants::SCHNORRSIG_PUBLIC_KEY_SIZE + 1]), + XOnlyPublicKey::from_slice(&[0; constants::SCHNORRSIG_PUBLIC_KEY_SIZE + 1]), Err(InvalidPublicKey) ); // Bad parse assert_eq!( - PublicKey::from_slice(&[0xff; constants::SCHNORRSIG_PUBLIC_KEY_SIZE]), + XOnlyPublicKey::from_slice(&[0xff; constants::SCHNORRSIG_PUBLIC_KEY_SIZE]), Err(InvalidPublicKey) ); // In fuzzing mode restrictions on public key validity are much more // relaxed, thus the invalid check below is expected to fail. #[cfg(not(fuzzing))] assert_eq!( - PublicKey::from_slice(&[0x55; constants::SCHNORRSIG_PUBLIC_KEY_SIZE]), + XOnlyPublicKey::from_slice(&[0x55; constants::SCHNORRSIG_PUBLIC_KEY_SIZE]), Err(InvalidPublicKey) ); - assert_eq!(PublicKey::from_slice(&[]), Err(InvalidPublicKey)); + assert_eq!(XOnlyPublicKey::from_slice(&[]), Err(InvalidPublicKey)); } #[test] @@ -630,43 +630,43 @@ mod tests { // In fuzzing mode secret->public key derivation is different, so // hard-code the epected result. #[cfg(not(fuzzing))] - let pk = PublicKey::from_keypair(&s, &sk); + let pk = XOnlyPublicKey::from_keypair(&s, &sk); #[cfg(fuzzing)] - let pk = PublicKey::from_slice(&[0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, 0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d, 0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54, 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66]).expect("pk"); + let pk = XOnlyPublicKey::from_slice(&[0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, 0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d, 0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54, 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66]).expect("pk"); assert_eq!( pk.to_string(), "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166" ); assert_eq!( - PublicKey::from_str("18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166") + XOnlyPublicKey::from_str("18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166") .unwrap(), pk ); - assert!(PublicKey::from_str( + assert!(XOnlyPublicKey::from_str( "00000000000000000000000000000000000000000000000000000000000000000" ) .is_err()); - assert!(PublicKey::from_str( + assert!(XOnlyPublicKey::from_str( "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd16601" ) .is_err()); - assert!(PublicKey::from_str( + assert!(XOnlyPublicKey::from_str( "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd16" ) .is_err()); - assert!(PublicKey::from_str( + assert!(XOnlyPublicKey::from_str( "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1" ) .is_err()); - assert!(PublicKey::from_str( + assert!(XOnlyPublicKey::from_str( "xx18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1" ) .is_err()); let long_str: String = iter::repeat('a').take(1024 * 1024).collect(); - assert!(PublicKey::from_str(&long_str).is_err()); + assert!(XOnlyPublicKey::from_str(&long_str).is_err()); } #[test] @@ -734,7 +734,7 @@ mod tests { static PK_STR: &'static str = "\ 18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166\ "; - let pk = PublicKey::from_slice(&PK_BYTES).unwrap(); + let pk = XOnlyPublicKey::from_slice(&PK_BYTES).unwrap(); assert_tokens(&sig.compact(), &[Token::BorrowedBytes(&SIG_BYTES[..])]); assert_tokens(&sig.compact(), &[Token::Bytes(&SIG_BYTES[..])]); @@ -763,7 +763,7 @@ mod tests { let orig_pk = pk; kp.tweak_add_assign(&s, &tweak).expect("Tweak error"); let parity = pk.tweak_add_assign(&s, &tweak).expect("Tweak error"); - assert_eq!(PublicKey::from_keypair(&s, &kp), pk); + assert_eq!(XOnlyPublicKey::from_keypair(&s, &kp), pk); assert!(orig_pk.tweak_add_check(&s, &pk, parity, tweak)); } } @@ -779,8 +779,8 @@ mod tests { ) .unwrap(); - let pk1 = PublicKey::from(kpk1); - let pk2 = PublicKey::from(kpk2); + let pk1 = XOnlyPublicKey::from(kpk1); + let pk2 = XOnlyPublicKey::from(kpk2); assert_eq!(pk1.serialize()[..], kpk1.serialize()[1..]); assert_eq!(pk2.serialize()[..], kpk2.serialize()[1..]); From d4fb819d80a3eb6dfabedf85dbb57348adf67f31 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 9 Sep 2021 19:41:11 +1000 Subject: [PATCH 07/12] Move `XOnlyPublicKey` to `key` module --- src/key.rs | 247 ++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 1 + src/schnorrsig.rs | 248 +--------------------------------------------- 3 files changed, 248 insertions(+), 248 deletions(-) diff --git a/src/key.rs b/src/key.rs index 4da3818fa..ca9ba2849 100644 --- a/src/key.rs +++ b/src/key.rs @@ -17,7 +17,7 @@ #[cfg(any(test, feature = "rand"))] use rand::Rng; -use core::{fmt, str}; +use core::{fmt, ptr, str}; use super::{from_hex, Secp256k1}; use super::Error::{self, InvalidPublicKey, InvalidPublicKeySum, InvalidSecretKey}; @@ -674,6 +674,251 @@ impl<'a> From<&'a KeyPair> for PublicKey { } } +/// A x-only public key, used for verification of Schnorr signatures and serialized according to BIP-340. +#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] +pub struct XOnlyPublicKey(ffi::XOnlyPublicKey); + +impl fmt::LowerHex for XOnlyPublicKey { + 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 XOnlyPublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for XOnlyPublicKey { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut res = [0u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::SCHNORRSIG_PUBLIC_KEY_SIZE) => { + XOnlyPublicKey::from_slice(&res[0..constants::SCHNORRSIG_PUBLIC_KEY_SIZE]) + } + _ => Err(Error::InvalidPublicKey), + } + } +} + +impl XOnlyPublicKey { + /// Obtains a raw const pointer suitable for use with FFI functions + #[inline] + pub fn as_ptr(&self) -> *const ffi::XOnlyPublicKey { + &self.0 + } + + /// 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 + } + + /// Creates a new Schnorr public key from a Schnorr key pair. + #[inline] + pub fn from_keypair(secp: &Secp256k1, keypair: &KeyPair) -> XOnlyPublicKey { + let mut pk_parity = 0; + unsafe { + let mut xonly_pk = ffi::XOnlyPublicKey::new(); + let ret = ffi::secp256k1_keypair_xonly_pub( + secp.ctx, + &mut xonly_pk, + &mut pk_parity, + keypair.as_ptr(), + ); + debug_assert_eq!(ret, 1); + XOnlyPublicKey(xonly_pk) + } + } + + /// Creates a Schnorr public key directly from a slice + /// + /// # Errors + /// + /// Returns [`Error::InvalidPublicKey`] if the length of the data slice is not 32 bytes or the + /// slice does not represent a valid Secp256k1 point x coordinate + #[inline] + pub fn from_slice(data: &[u8]) -> Result { + if data.is_empty() || data.len() != constants::SCHNORRSIG_PUBLIC_KEY_SIZE { + return Err(Error::InvalidPublicKey); + } + + unsafe { + let mut pk = ffi::XOnlyPublicKey::new(); + if ffi::secp256k1_xonly_pubkey_parse( + ffi::secp256k1_context_no_precomp, + &mut pk, + data.as_c_ptr(), + ) == 1 + { + Ok(XOnlyPublicKey(pk)) + } else { + Err(Error::InvalidPublicKey) + } + } + } + + #[inline] + /// Serialize the key as a byte-encoded x coordinate value (32 bytes). + pub fn serialize(&self) -> [u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE] { + let mut ret = [0u8; constants::SCHNORRSIG_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 + } + + /// Tweak an x-only PublicKey by adding the generator multiplied with the given tweak to it. + /// + /// Returns a boolean representing the parity of the tweaked key, which can be provided to + /// `tweak_add_check` which can be used to verify a tweak more efficiently than regenerating + /// it and checking equality. Will return an error if the resulting key would be invalid or + /// if the tweak was not a 32-byte length slice. + pub fn tweak_add_assign( + &mut self, + secp: &Secp256k1, + tweak: &[u8], + ) -> Result { + if tweak.len() != 32 { + return Err(Error::InvalidTweak); + } + + unsafe { + let mut pubkey = ffi::PublicKey::new(); + let mut err = ffi::secp256k1_xonly_pubkey_tweak_add( + secp.ctx, + &mut pubkey, + self.as_c_ptr(), + tweak.as_c_ptr(), + ); + + if err != 1 { + return Err(Error::InvalidTweak); + } + + let mut parity: ::secp256k1_sys::types::c_int = 0; + err = ffi::secp256k1_xonly_pubkey_from_pubkey( + secp.ctx, + &mut self.0, + &mut parity, + &pubkey, + ); + + if err == 0 { + Err(Error::InvalidPublicKey) + } else { + Ok(parity != 0) + } + } + } + + /// Verify that a tweak produced by `tweak_add_assign` was computed correctly + /// + /// Should be called on the original untweaked key. Takes the tweaked key and + /// output parity from `tweak_add_assign` as input. + /// + /// Currently this is not much more efficient than just recomputing the tweak + /// and checking equality. However, in future this API will support batch + /// verification, which is significantly faster, so it is wise to design + /// protocols with this in mind. + pub fn tweak_add_check( + &self, + secp: &Secp256k1, + tweaked_key: &Self, + tweaked_parity: bool, + tweak: [u8; 32], + ) -> bool { + let tweaked_ser = tweaked_key.serialize(); + unsafe { + let err = ffi::secp256k1_xonly_pubkey_tweak_add_check( + secp.ctx, + tweaked_ser.as_c_ptr(), + if tweaked_parity { 1 } else { 0 }, + &self.0, + tweak.as_c_ptr(), + ); + + err == 1 + } + } +} + +impl CPtr for XOnlyPublicKey { + 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 XOnlyPublicKey { + #[inline] + fn from(pk: ffi::XOnlyPublicKey) -> XOnlyPublicKey { + XOnlyPublicKey(pk) + } +} + +impl From<::key::PublicKey> for XOnlyPublicKey { + fn from(src: ::key::PublicKey) -> XOnlyPublicKey { + unsafe { + let mut pk = ffi::XOnlyPublicKey::new(); + assert_eq!( + 1, + ffi::secp256k1_xonly_pubkey_from_pubkey( + ffi::secp256k1_context_no_precomp, + &mut pk, + ptr::null_mut(), + src.as_c_ptr(), + ) + ); + XOnlyPublicKey(pk) + } + } +} + +#[cfg(feature = "serde")] +impl ::serde::Serialize for XOnlyPublicKey { + fn serialize(&self, s: S) -> Result { + if s.is_human_readable() { + s.collect_str(self) + } else { + s.serialize_bytes(&self.serialize()) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> ::serde::Deserialize<'de> for XOnlyPublicKey { + fn deserialize>(d: D) -> Result { + if d.is_human_readable() { + d.deserialize_str(super::serde_util::FromStrVisitor::new( + "a hex string representing 32 byte schnorr public key" + )) + } else { + d.deserialize_bytes(super::serde_util::BytesVisitor::new( + "raw 32 bytes schnorr public key", + XOnlyPublicKey::from_slice + )) + } + } +} + #[cfg(test)] mod test { use Secp256k1; diff --git a/src/lib.rs b/src/lib.rs index 17c006cbd..ad50782c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,6 +156,7 @@ pub use key::SecretKey; pub use key::PublicKey; pub use key::ONE_KEY; pub use key::KeyPair; +pub use key::XOnlyPublicKey; pub use context::*; use core::marker::PhantomData; use core::{mem, fmt, ptr, str}; diff --git a/src/schnorrsig.rs b/src/schnorrsig.rs index 62d90d82f..3224dc823 100644 --- a/src/schnorrsig.rs +++ b/src/schnorrsig.rs @@ -11,8 +11,7 @@ use super::{from_hex, Error}; use core::{fmt, ptr, str}; use ffi::{self, CPtr}; use {constants, Secp256k1}; -use {Message, Signing, Verification}; -use KeyPair; +use {Message, Signing, KeyPair, XOnlyPublicKey}; /// Represents a Schnorr signature. pub struct Signature([u8; constants::SCHNORRSIG_SIGNATURE_SIZE]); @@ -74,39 +73,6 @@ impl str::FromStr for Signature { } } -/// A x-only public key, used for verification of Schnorr signatures -#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] -pub struct XOnlyPublicKey(ffi::XOnlyPublicKey); - -impl fmt::LowerHex for XOnlyPublicKey { - 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 XOnlyPublicKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::LowerHex::fmt(self, f) - } -} - -impl str::FromStr for XOnlyPublicKey { - type Err = Error; - fn from_str(s: &str) -> Result { - let mut res = [0u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE]; - match from_hex(s, &mut res) { - Ok(constants::SCHNORRSIG_PUBLIC_KEY_SIZE) => { - XOnlyPublicKey::from_slice(&res[0..constants::SCHNORRSIG_PUBLIC_KEY_SIZE]) - } - _ => Err(Error::InvalidPublicKey), - } - } -} - impl Signature { /// Creates a Signature directly from a slice #[inline] @@ -122,218 +88,6 @@ impl Signature { } } -impl XOnlyPublicKey { - /// Obtains a raw const pointer suitable for use with FFI functions - #[inline] - pub fn as_ptr(&self) -> *const ffi::XOnlyPublicKey { - &self.0 - } - - /// 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 - } - - /// Creates a new Schnorr public key from a Schnorr key pair. - #[inline] - pub fn from_keypair(secp: &Secp256k1, keypair: &KeyPair) -> XOnlyPublicKey { - let mut pk_parity = 0; - unsafe { - let mut xonly_pk = ffi::XOnlyPublicKey::new(); - let ret = ffi::secp256k1_keypair_xonly_pub( - secp.ctx, - &mut xonly_pk, - &mut pk_parity, - keypair.as_ptr(), - ); - debug_assert_eq!(ret, 1); - XOnlyPublicKey(xonly_pk) - } - } - - /// Creates a Schnorr public key directly from a slice - /// - /// # Errors - /// - /// Returns [`Error::InvalidPublicKey`] if the length of the data slice is not 32 bytes or the - /// slice does not represent a valid Secp256k1 point x coordinate - #[inline] - pub fn from_slice(data: &[u8]) -> Result { - if data.is_empty() || data.len() != constants::SCHNORRSIG_PUBLIC_KEY_SIZE { - return Err(Error::InvalidPublicKey); - } - - unsafe { - let mut pk = ffi::XOnlyPublicKey::new(); - if ffi::secp256k1_xonly_pubkey_parse( - ffi::secp256k1_context_no_precomp, - &mut pk, - data.as_c_ptr(), - ) == 1 - { - Ok(XOnlyPublicKey(pk)) - } else { - Err(Error::InvalidPublicKey) - } - } - } - - #[inline] - /// Serialize the key as a byte-encoded x coordinate value (32 bytes). - pub fn serialize(&self) -> [u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE] { - let mut ret = [0u8; constants::SCHNORRSIG_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 - } - - /// Tweak an x-only PublicKey by adding the generator multiplied with the given tweak to it. - /// - /// Returns a boolean representing the parity of the tweaked key, which can be provided to - /// `tweak_add_check` which can be used to verify a tweak more efficiently than regenerating - /// it and checking equality. Will return an error if the resulting key would be invalid or - /// if the tweak was not a 32-byte length slice. - pub fn tweak_add_assign( - &mut self, - secp: &Secp256k1, - tweak: &[u8], - ) -> Result { - if tweak.len() != 32 { - return Err(Error::InvalidTweak); - } - - unsafe { - let mut pubkey = ffi::PublicKey::new(); - let mut err = ffi::secp256k1_xonly_pubkey_tweak_add( - secp.ctx, - &mut pubkey, - self.as_c_ptr(), - tweak.as_c_ptr(), - ); - - if err != 1 { - return Err(Error::InvalidTweak); - } - - let mut parity: ::secp256k1_sys::types::c_int = 0; - err = ffi::secp256k1_xonly_pubkey_from_pubkey( - secp.ctx, - &mut self.0, - &mut parity, - &pubkey, - ); - - if err == 0 { - Err(Error::InvalidPublicKey) - } else { - Ok(parity != 0) - } - } - } - - /// Verify that a tweak produced by `tweak_add_assign` was computed correctly - /// - /// Should be called on the original untweaked key. Takes the tweaked key and - /// output parity from `tweak_add_assign` as input. - /// - /// Currently this is not much more efficient than just recomputing the tweak - /// and checking equality. However, in future this API will support batch - /// verification, which is significantly faster, so it is wise to design - /// protocols with this in mind. - pub fn tweak_add_check( - &self, - secp: &Secp256k1, - tweaked_key: &Self, - tweaked_parity: bool, - tweak: [u8; 32], - ) -> bool { - let tweaked_ser = tweaked_key.serialize(); - unsafe { - let err = ffi::secp256k1_xonly_pubkey_tweak_add_check( - secp.ctx, - tweaked_ser.as_c_ptr(), - if tweaked_parity { 1 } else { 0 }, - &self.0, - tweak.as_c_ptr(), - ); - - err == 1 - } - } -} - -impl CPtr for XOnlyPublicKey { - 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 XOnlyPublicKey { - #[inline] - fn from(pk: ffi::XOnlyPublicKey) -> XOnlyPublicKey { - XOnlyPublicKey(pk) - } -} - -impl From<::key::PublicKey> for XOnlyPublicKey { - fn from(src: ::key::PublicKey) -> XOnlyPublicKey { - unsafe { - let mut pk = ffi::XOnlyPublicKey::new(); - assert_eq!( - 1, - ffi::secp256k1_xonly_pubkey_from_pubkey( - ffi::secp256k1_context_no_precomp, - &mut pk, - ptr::null_mut(), - src.as_c_ptr(), - ) - ); - XOnlyPublicKey(pk) - } - } -} - -#[cfg(feature = "serde")] -impl ::serde::Serialize for XOnlyPublicKey { - fn serialize(&self, s: S) -> Result { - if s.is_human_readable() { - s.collect_str(self) - } else { - s.serialize_bytes(&self.serialize()) - } - } -} - -#[cfg(feature = "serde")] -impl<'de> ::serde::Deserialize<'de> for XOnlyPublicKey { - fn deserialize>(d: D) -> Result { - if d.is_human_readable() { - d.deserialize_str(super::serde_util::FromStrVisitor::new( - "a hex string representing 32 byte schnorr public key" - )) - } else { - d.deserialize_bytes(super::serde_util::BytesVisitor::new( - "raw 32 bytes schnorr public key", - XOnlyPublicKey::from_slice - )) - } - } -} - impl Secp256k1 { fn schnorrsig_sign_helper( &self, From 760559c70ebea79e9140729f023760ba0c7b3ded Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 9 Sep 2021 19:42:17 +1000 Subject: [PATCH 08/12] Rename `schnorrsig` module to `schnorr` Schnorr is commenly known as a signature algorithm, we don't need to restate that in the name of the module. --- src/lib.rs | 2 +- src/{schnorrsig.rs => schnorr.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{schnorrsig.rs => schnorr.rs} (100%) diff --git a/src/lib.rs b/src/lib.rs index ad50782c5..d1bf62464 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -148,7 +148,7 @@ mod key; pub mod constants; pub mod ecdh; pub mod ecdsa; -pub mod schnorrsig; +pub mod schnorr; #[cfg(feature = "serde")] mod serde_util; diff --git a/src/schnorrsig.rs b/src/schnorr.rs similarity index 100% rename from src/schnorrsig.rs rename to src/schnorr.rs From e0c3bb28c4fafb1758ccecd56ea8a7b9d9887bd4 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 9 Sep 2021 19:48:13 +1000 Subject: [PATCH 09/12] Rename schnorr functions on `Secp256k1` to match naming of ecdsa The naming scheme we employ is `{sign,verify, ...}_{ecdsa,schnorr}`. --- src/schnorr.rs | 73 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/src/schnorr.rs b/src/schnorr.rs index 3224dc823..07ac8963e 100644 --- a/src/schnorr.rs +++ b/src/schnorr.rs @@ -117,26 +117,56 @@ impl Secp256k1 { /// generator to generate the auxiliary random data. /// Requires compilation with "rand-std" feature. #[cfg(any(test, feature = "rand-std"))] + #[deprecated(since = "0.21.0", note = "Use sign_schnorr instead.")] pub fn schnorrsig_sign(&self, msg: &Message, keypair: &KeyPair) -> Signature { + self.sign_schnorr(msg, keypair) + } + + /// Create a schnorr signature internally using the ThreadRng random number + /// generator to generate the auxiliary random data. + /// Requires compilation with "rand-std" feature. + #[cfg(any(test, feature = "rand-std"))] + pub fn sign_schnorr(&self, msg: &Message, keypair: &KeyPair) -> Signature { let mut rng = thread_rng(); - self.schnorrsig_sign_with_rng(msg, keypair, &mut rng) + self.sign_schnorr_with_rng(msg, keypair, &mut rng) } /// Create a schnorr signature without using any auxiliary random data. + #[deprecated(since = "0.21.0", note = "Use sign_schnorr_no_aux_rand instead.")] pub fn schnorrsig_sign_no_aux_rand( &self, msg: &Message, keypair: &KeyPair, + ) -> Signature { + self.sign_schnorr_no_aux_rand(msg, keypair) + } + + /// Create a schnorr signature without using any auxiliary random data. + pub fn sign_schnorr_no_aux_rand( + &self, + msg: &Message, + keypair: &KeyPair, ) -> Signature { self.schnorrsig_sign_helper(msg, keypair, ptr::null()) } /// Create a Schnorr signature using the given auxiliary random data. + #[deprecated(since = "0.21.0", note = "Use sign_schnorr_with_aux_rand instead.")] pub fn schnorrsig_sign_with_aux_rand( &self, msg: &Message, keypair: &KeyPair, aux_rand: &[u8; 32], + ) -> Signature { + self.sign_schnorr_with_aux_rand(msg, keypair, aux_rand) + } + + /// Create a Schnorr signature using the given auxiliary random data. + pub fn sign_schnorr_with_aux_rand( + &self, + msg: &Message, + keypair: &KeyPair, + aux_rand: &[u8; 32], ) -> Signature { self.schnorrsig_sign_helper( msg, @@ -149,11 +179,25 @@ impl Secp256k1 { /// generate the auxiliary random data. Requires compilation with "rand" /// feature. #[cfg(any(test, feature = "rand"))] + #[deprecated(since = "0.21.0", note = "Use sign_schnorr_with_rng instead.")] pub fn schnorrsig_sign_with_rng( &self, msg: &Message, keypair: &KeyPair, rng: &mut R, + ) -> Signature { + self.sign_schnorr_with_rng(msg, keypair, rng) + } + + /// Create a schnorr signature using the given random number generator to + /// generate the auxiliary random data. Requires compilation with "rand" + /// feature. + #[cfg(any(test, feature = "rand"))] + pub fn sign_schnorr_with_rng( + &self, + msg: &Message, + keypair: &KeyPair, + rng: &mut R, ) -> Signature { let mut aux = [0u8; 32]; rng.fill_bytes(&mut aux); @@ -161,11 +205,22 @@ impl Secp256k1 { } /// Verify a Schnorr signature. + #[deprecated(since = "0.21.0", note = "Use verify_schnorr instead.")] pub fn schnorrsig_verify( &self, sig: &Signature, msg: &Message, pubkey: &XOnlyPublicKey, + ) -> Result<(), Error> { + self.verify_schnorr(sig, msg, pubkey) + } + + /// Verify a Schnorr signature. + pub fn verify_schnorr( + &self, + sig: &Signature, + msg: &Message, + pubkey: &XOnlyPublicKey, ) -> Result<(), Error> { unsafe { let ret = ffi::secp256k1_schnorrsig_verify( @@ -237,7 +292,7 @@ mod tests { let sig = sign(&secp, &msg, &seckey, &mut rng); - assert!(secp.schnorrsig_verify(&sig, &msg, &pubkey).is_ok()); + assert!(secp.verify_schnorr(&sig, &msg, &pubkey).is_ok()); } } @@ -246,28 +301,28 @@ mod tests { test_schnorrsig_sign_helper(|secp, msg, seckey, rng| { let mut aux_rand = [0u8; 32]; rng.fill_bytes(&mut aux_rand); - secp.schnorrsig_sign_with_aux_rand(msg, seckey, &aux_rand) + secp.sign_schnorr_with_aux_rand(msg, seckey, &aux_rand) }) } #[test] fn test_schnorrsig_sign_with_rng_verify() { test_schnorrsig_sign_helper(|secp, msg, seckey, mut rng| { - secp.schnorrsig_sign_with_rng(msg, seckey, &mut rng) + secp.sign_schnorr_with_rng(msg, seckey, &mut rng) }) } #[test] fn test_schnorrsig_sign_verify() { test_schnorrsig_sign_helper(|secp, msg, seckey, _| { - secp.schnorrsig_sign(msg, seckey) + secp.sign_schnorr(msg, seckey) }) } #[test] fn test_schnorrsig_sign_no_aux_rand_verify() { test_schnorrsig_sign_helper(|secp, msg, seckey, _| { - secp.schnorrsig_sign_no_aux_rand(msg, seckey) + secp.sign_schnorr_no_aux_rand(msg, seckey) }) } @@ -288,7 +343,7 @@ mod tests { let expected_sig = Signature::from_str("6470FD1303DDA4FDA717B9837153C24A6EAB377183FC438F939E0ED2B620E9EE5077C4A8B8DCA28963D772A94F5F0DDF598E1C47C137F91933274C7C3EDADCE8").unwrap(); let sig = secp - .schnorrsig_sign_with_aux_rand(&msg, &sk, &aux_rand); + .sign_schnorr_with_aux_rand(&msg, &sk, &aux_rand); assert_eq!(expected_sig, sig); } @@ -305,7 +360,7 @@ mod tests { XOnlyPublicKey::from_str("B33CC9EDC096D0A83416964BD3C6247B8FECD256E4EFA7870D2C854BDEB33390") .unwrap(); - assert!(secp.schnorrsig_verify(&sig, &msg, &pubkey).is_ok()); + assert!(secp.verify_schnorr(&sig, &msg, &pubkey).is_ok()); } #[test] @@ -469,7 +524,7 @@ mod tests { let keypair = KeyPair::from_seckey_slice(&s, &[2; 32]).unwrap(); let aux = [3u8; 32]; let sig = s - .schnorrsig_sign_with_aux_rand(&msg, &keypair, &aux); + .sign_schnorr_with_aux_rand(&msg, &keypair, &aux); static SIG_BYTES: [u8; constants::SCHNORRSIG_SIGNATURE_SIZE] = [ 0x14, 0xd0, 0xbf, 0x1a, 0x89, 0x53, 0x50, 0x6f, 0xb4, 0x60, 0xf5, 0x8b, 0xe1, 0x41, 0xaf, 0x76, 0x7f, 0xd1, 0x12, 0x53, 0x5f, 0xb3, 0x92, 0x2e, 0xf2, 0x17, 0x30, 0x8e, From ce4427747d0b688681d0aa281cd0e2716231848c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 9 Sep 2021 19:58:59 +1000 Subject: [PATCH 10/12] Move ECDSA functionality into ECDSA module --- src/ecdh.rs | 4 +- src/ecdsa/mod.rs | 203 ++++++++++++++++++++++++++++++++++++++++++++++- src/key.rs | 2 +- src/lib.rs | 200 +--------------------------------------------- 4 files changed, 205 insertions(+), 204 deletions(-) diff --git a/src/ecdh.rs b/src/ecdh.rs index a2aa121f4..de7531438 100644 --- a/src/ecdh.rs +++ b/src/ecdh.rs @@ -169,8 +169,8 @@ impl SharedSecret { #[cfg(test)] mod tests { + use super::*; use rand::thread_rng; - use super::SharedSecret; use super::super::Secp256k1; #[cfg(target_arch = "wasm32")] @@ -224,7 +224,7 @@ mod tests { let x = [5u8; 32]; let y = [7u8; 32]; let mut output = [0u8; 64]; - let res = unsafe { super::c_callback(output.as_mut_ptr(), x.as_ptr(), y.as_ptr(), ::ptr::null_mut()) }; + let res = unsafe { super::c_callback(output.as_mut_ptr(), x.as_ptr(), y.as_ptr(), ptr::null_mut()) }; assert_eq!(res, 1); let mut new_x = [0u8; 32]; let mut new_y = [0u8; 32]; diff --git a/src/ecdsa/mod.rs b/src/ecdsa/mod.rs index cd88c0436..0bc68f63f 100644 --- a/src/ecdsa/mod.rs +++ b/src/ecdsa/mod.rs @@ -1,10 +1,9 @@ //! Structs and functionality related to the ECDSA signature algorithm. -use core::{fmt, str, ops}; -use Error; +use core::{fmt, str, ops, ptr, mem}; + +use {Signing, Verification, Message, PublicKey, Secp256k1, SecretKey, from_hex, Error, ffi}; use ffi::CPtr; -use ffi; -use from_hex; #[cfg(feature = "recovery")] mod recovery; @@ -305,3 +304,199 @@ impl<'de> ::serde::Deserialize<'de> for Signature { } } } + +impl Secp256k1 { + + /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce + /// Requires a signing-capable context. + #[deprecated(since = "0.21.0", note = "Use sign_ecdsa instead.")] + pub fn sign(&self, msg: &Message, sk: &SecretKey) -> Signature { + self.sign_ecdsa(msg, sk) + } + + /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce + /// Requires a signing-capable context. + pub fn sign_ecdsa(&self, msg: &Message, sk: &SecretKey) -> Signature { + unsafe { + let mut ret = ffi::Signature::new(); + // We can assume the return value because it's not possible to construct + // an invalid signature from a valid `Message` and `SecretKey` + assert_eq!(ffi::secp256k1_ecdsa_sign(self.ctx, &mut ret, msg.as_c_ptr(), + sk.as_c_ptr(), ffi::secp256k1_nonce_function_rfc6979, + ptr::null()), 1); + Signature::from(ret) + } + } + + fn sign_grind_with_check( + &self, msg: &Message, + sk: &SecretKey, + check: impl Fn(&ffi::Signature) -> bool) -> Signature { + let mut entropy_p : *const ffi::types::c_void = ptr::null(); + let mut counter : u32 = 0; + let mut extra_entropy = [0u8; 32]; + loop { + unsafe { + let mut ret = ffi::Signature::new(); + // We can assume the return value because it's not possible to construct + // an invalid signature from a valid `Message` and `SecretKey` + assert_eq!(ffi::secp256k1_ecdsa_sign(self.ctx, &mut ret, msg.as_c_ptr(), + sk.as_c_ptr(), ffi::secp256k1_nonce_function_rfc6979, + entropy_p), 1); + if check(&ret) { + return Signature::from(ret); + } + + counter += 1; + // From 1.32 can use `to_le_bytes` instead + let le_counter = counter.to_le(); + let le_counter_bytes : [u8; 4] = mem::transmute(le_counter); + for (i, b) in le_counter_bytes.iter().enumerate() { + extra_entropy[i] = *b; + } + + entropy_p = extra_entropy.as_ptr() as *const ffi::types::c_void; + + // When fuzzing, these checks will usually spinloop forever, so just short-circuit them. + #[cfg(fuzzing)] + return Signature::from(ret); + } + } + } + + /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce + /// and "grinds" the nonce by passing extra entropy if necessary to produce + /// a signature that is less than 71 - bytes_to_grund bytes. The number + /// of signing operation performed by this function is exponential in the + /// number of bytes grinded. + /// Requires a signing capable context. + #[deprecated(since = "0.21.0", note = "Use sign_ecdsa_grind_r instead.")] + pub fn sign_grind_r(&self, msg: &Message, sk: &SecretKey, bytes_to_grind: usize) -> Signature { + self.sign_ecdsa_grind_r(msg, sk, bytes_to_grind) + } + + /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce + /// and "grinds" the nonce by passing extra entropy if necessary to produce + /// a signature that is less than 71 - bytes_to_grund bytes. The number + /// of signing operation performed by this function is exponential in the + /// number of bytes grinded. + /// Requires a signing capable context. + pub fn sign_ecdsa_grind_r(&self, msg: &Message, sk: &SecretKey, bytes_to_grind: usize) -> Signature { + let len_check = |s : &ffi::Signature| der_length_check(s, 71 - bytes_to_grind); + return self.sign_grind_with_check(msg, sk, len_check); + } + + /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce + /// and "grinds" the nonce by passing extra entropy if necessary to produce + /// a signature that is less than 71 bytes and compatible with the low r + /// signature implementation of bitcoin core. In average, this function + /// will perform two signing operations. + /// Requires a signing capable context. + #[deprecated(since = "0.21.0", note = "Use sign_ecdsa_grind_r instead.")] + pub fn sign_low_r(&self, msg: &Message, sk: &SecretKey) -> Signature { + return self.sign_grind_with_check(msg, sk, compact_sig_has_zero_first_bit) + } + + /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce + /// and "grinds" the nonce by passing extra entropy if necessary to produce + /// a signature that is less than 71 bytes and compatible with the low r + /// signature implementation of bitcoin core. In average, this function + /// will perform two signing operations. + /// Requires a signing capable context. + pub fn sign_ecdsa_low_r(&self, msg: &Message, sk: &SecretKey) -> Signature { + return self.sign_grind_with_check(msg, sk, compact_sig_has_zero_first_bit) + } +} + +impl Secp256k1 { + /// Checks that `sig` is a valid ECDSA signature for `msg` using the public + /// key `pubkey`. Returns `Ok(())` on success. Note that this function cannot + /// be used for Bitcoin consensus checking since there may exist signatures + /// which OpenSSL would verify but not libsecp256k1, or vice-versa. Requires a + /// verify-capable context. + /// + /// ```rust + /// # #[cfg(feature="rand")] { + /// # use secp256k1::rand::rngs::OsRng; + /// # use secp256k1::{Secp256k1, Message, Error}; + /// # + /// # let secp = Secp256k1::new(); + /// # let mut rng = OsRng::new().expect("OsRng"); + /// # let (secret_key, public_key) = secp.generate_keypair(&mut rng); + /// # + /// let message = Message::from_slice(&[0xab; 32]).expect("32 bytes"); + /// let sig = secp.sign(&message, &secret_key); + /// assert_eq!(secp.verify(&message, &sig, &public_key), Ok(())); + /// + /// let message = Message::from_slice(&[0xcd; 32]).expect("32 bytes"); + /// assert_eq!(secp.verify(&message, &sig, &public_key), Err(Error::IncorrectSignature)); + /// # } + /// ``` + #[inline] + #[deprecated(since = "0.21.0", note = "Use verify_ecdsa instead")] + pub fn verify(&self, msg: &Message, sig: &Signature, pk: &PublicKey) -> Result<(), Error> { + self.verify_ecdsa(msg, sig, pk) + } + + /// Checks that `sig` is a valid ECDSA signature for `msg` using the public + /// key `pubkey`. Returns `Ok(())` on success. Note that this function cannot + /// be used for Bitcoin consensus checking since there may exist signatures + /// which OpenSSL would verify but not libsecp256k1, or vice-versa. Requires a + /// verify-capable context. + /// + /// ```rust + /// # #[cfg(feature="rand")] { + /// # use secp256k1::rand::rngs::OsRng; + /// # use secp256k1::{Secp256k1, Message, Error}; + /// # + /// # let secp = Secp256k1::new(); + /// # let mut rng = OsRng::new().expect("OsRng"); + /// # let (secret_key, public_key) = secp.generate_keypair(&mut rng); + /// # + /// let message = Message::from_slice(&[0xab; 32]).expect("32 bytes"); + /// let sig = secp.sign_ecdsa(&message, &secret_key); + /// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Ok(())); + /// + /// let message = Message::from_slice(&[0xcd; 32]).expect("32 bytes"); + /// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(Error::IncorrectSignature)); + /// # } + /// ``` + #[inline] + pub fn verify_ecdsa(&self, msg: &Message, sig: &Signature, pk: &PublicKey) -> Result<(), Error> { + unsafe { + if ffi::secp256k1_ecdsa_verify(self.ctx, sig.as_c_ptr(), msg.as_c_ptr(), pk.as_c_ptr()) == 0 { + Err(Error::IncorrectSignature) + } else { + Ok(()) + } + } + } +} + +pub(crate) fn compact_sig_has_zero_first_bit(sig: &ffi::Signature) -> bool { + let mut compact = [0u8; 64]; + unsafe { + let err = ffi::secp256k1_ecdsa_signature_serialize_compact( + ffi::secp256k1_context_no_precomp, + compact.as_mut_c_ptr(), + sig, + ); + debug_assert!(err == 1); + } + compact[0] < 0x80 +} + +pub(crate) fn der_length_check(sig: &ffi::Signature, max_len: usize) -> bool { + let mut ser_ret = [0u8; 72]; + let mut len: usize = ser_ret.len(); + unsafe { + let err = ffi::secp256k1_ecdsa_signature_serialize_der( + ffi::secp256k1_context_no_precomp, + ser_ret.as_mut_c_ptr(), + &mut len, + sig, + ); + debug_assert!(err == 1); + } + len <= max_len +} diff --git a/src/key.rs b/src/key.rs index ca9ba2849..132e1208c 100644 --- a/src/key.rs +++ b/src/key.rs @@ -589,7 +589,7 @@ impl KeyPair { /// Creates a new random secret key. Requires compilation with the "rand" feature. #[inline] #[cfg(any(test, feature = "rand"))] - pub fn new(secp: &Secp256k1, rng: &mut R) -> KeyPair { + pub fn new(secp: &Secp256k1, rng: &mut R) -> KeyPair { let mut random_32_bytes = || { let mut ret = [0u8; 32]; rng.fill_bytes(&mut ret); diff --git a/src/lib.rs b/src/lib.rs index d1bf62464..a96387fc3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -159,7 +159,7 @@ pub use key::KeyPair; pub use key::XOnlyPublicKey; pub use context::*; use core::marker::PhantomData; -use core::{mem, fmt, ptr, str}; +use core::{mem, fmt, str}; use ffi::{CPtr, types::AlignedType}; #[cfg(feature = "global-context-less-secure")] @@ -380,136 +380,7 @@ impl Secp256k1 { } } -fn der_length_check(sig: &ffi::Signature, max_len: usize) -> bool { - let mut ser_ret = [0u8; 72]; - let mut len: usize = ser_ret.len(); - unsafe { - let err = ffi::secp256k1_ecdsa_signature_serialize_der( - ffi::secp256k1_context_no_precomp, - ser_ret.as_mut_c_ptr(), - &mut len, - sig, - ); - debug_assert!(err == 1); - } - len <= max_len -} - -fn compact_sig_has_zero_first_bit(sig: &ffi::Signature) -> bool { - let mut compact = [0u8; 64]; - unsafe { - let err = ffi::secp256k1_ecdsa_signature_serialize_compact( - ffi::secp256k1_context_no_precomp, - compact.as_mut_c_ptr(), - sig, - ); - debug_assert!(err == 1); - } - compact[0] < 0x80 -} - impl Secp256k1 { - - /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce - /// Requires a signing-capable context. - #[deprecated(since = "0.21.0", note = "Use sign_ecdsa instead.")] - pub fn sign(&self, msg: &Message, sk: &key::SecretKey) -> ecdsa::Signature { - self.sign_ecdsa(msg, sk) - } - - /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce - /// Requires a signing-capable context. - pub fn sign_ecdsa(&self, msg: &Message, sk: &key::SecretKey) -> ecdsa::Signature { - unsafe { - let mut ret = ffi::Signature::new(); - // We can assume the return value because it's not possible to construct - // an invalid signature from a valid `Message` and `SecretKey` - assert_eq!(ffi::secp256k1_ecdsa_sign(self.ctx, &mut ret, msg.as_c_ptr(), - sk.as_c_ptr(), ffi::secp256k1_nonce_function_rfc6979, - ptr::null()), 1); - ecdsa::Signature::from(ret) - } - } - - fn sign_grind_with_check( - &self, msg: &Message, - sk: &SecretKey, - check: impl Fn(&ffi::Signature) -> bool) -> ecdsa::Signature { - let mut entropy_p : *const ffi::types::c_void = ptr::null(); - let mut counter : u32 = 0; - let mut extra_entropy = [0u8; 32]; - loop { - unsafe { - let mut ret = ffi::Signature::new(); - // We can assume the return value because it's not possible to construct - // an invalid signature from a valid `Message` and `SecretKey` - assert_eq!(ffi::secp256k1_ecdsa_sign(self.ctx, &mut ret, msg.as_c_ptr(), - sk.as_c_ptr(), ffi::secp256k1_nonce_function_rfc6979, - entropy_p), 1); - if check(&ret) { - return ecdsa::Signature::from(ret); - } - - counter += 1; - // From 1.32 can use `to_le_bytes` instead - let le_counter = counter.to_le(); - let le_counter_bytes : [u8; 4] = mem::transmute(le_counter); - for (i, b) in le_counter_bytes.iter().enumerate() { - extra_entropy[i] = *b; - } - - entropy_p = extra_entropy.as_ptr() as *const ffi::types::c_void; - - // When fuzzing, these checks will usually spinloop forever, so just short-circuit them. - #[cfg(fuzzing)] - return ecdsa::Signature::from(ret); - } - } - } - - /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce - /// and "grinds" the nonce by passing extra entropy if necessary to produce - /// a signature that is less than 71 - bytes_to_grund bytes. The number - /// of signing operation performed by this function is exponential in the - /// number of bytes grinded. - /// Requires a signing capable context. - #[deprecated(since = "0.21.0", note = "Use sign_ecdsa_grind_r instead.")] - pub fn sign_grind_r(&self, msg: &Message, sk: &SecretKey, bytes_to_grind: usize) -> ecdsa::Signature { - self.sign_ecdsa_grind_r(msg, sk, bytes_to_grind) - } - - /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce - /// and "grinds" the nonce by passing extra entropy if necessary to produce - /// a signature that is less than 71 - bytes_to_grund bytes. The number - /// of signing operation performed by this function is exponential in the - /// number of bytes grinded. - /// Requires a signing capable context. - pub fn sign_ecdsa_grind_r(&self, msg: &Message, sk: &SecretKey, bytes_to_grind: usize) -> ecdsa::Signature { - let len_check = |s : &ffi::Signature| der_length_check(s, 71 - bytes_to_grind); - return self.sign_grind_with_check(msg, sk, len_check); - } - - /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce - /// and "grinds" the nonce by passing extra entropy if necessary to produce - /// a signature that is less than 71 bytes and compatible with the low r - /// signature implementation of bitcoin core. In average, this function - /// will perform two signing operations. - /// Requires a signing capable context. - #[deprecated(since = "0.21.0", note = "Use sign_ecdsa_grind_r instead.")] - pub fn sign_low_r(&self, msg: &Message, sk: &SecretKey) -> ecdsa::Signature { - return self.sign_grind_with_check(msg, sk, compact_sig_has_zero_first_bit) - } - - /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce - /// and "grinds" the nonce by passing extra entropy if necessary to produce - /// a signature that is less than 71 bytes and compatible with the low r - /// signature implementation of bitcoin core. In average, this function - /// will perform two signing operations. - /// Requires a signing capable context. - pub fn sign_ecdsa_low_r(&self, msg: &Message, sk: &SecretKey) -> ecdsa::Signature { - return self.sign_grind_with_check(msg, sk, compact_sig_has_zero_first_bit) - } - /// Generates a random keypair. Convenience function for `key::SecretKey::new` /// and `key::PublicKey::from_secret_key`; call those functions directly for /// batch key generation. Requires a signing-capable context. Requires compilation @@ -524,71 +395,6 @@ impl Secp256k1 { } } -impl Secp256k1 { - /// Checks that `sig` is a valid ECDSA signature for `msg` using the public - /// key `pubkey`. Returns `Ok(())` on success. Note that this function cannot - /// be used for Bitcoin consensus checking since there may exist signatures - /// which OpenSSL would verify but not libsecp256k1, or vice-versa. Requires a - /// verify-capable context. - /// - /// ```rust - /// # #[cfg(feature="rand")] { - /// # use secp256k1::rand::rngs::OsRng; - /// # use secp256k1::{Secp256k1, Message, Error}; - /// # - /// # let secp = Secp256k1::new(); - /// # let mut rng = OsRng::new().expect("OsRng"); - /// # let (secret_key, public_key) = secp.generate_keypair(&mut rng); - /// # - /// let message = Message::from_slice(&[0xab; 32]).expect("32 bytes"); - /// let sig = secp.sign(&message, &secret_key); - /// assert_eq!(secp.verify(&message, &sig, &public_key), Ok(())); - /// - /// let message = Message::from_slice(&[0xcd; 32]).expect("32 bytes"); - /// assert_eq!(secp.verify(&message, &sig, &public_key), Err(Error::IncorrectSignature)); - /// # } - /// ``` - #[inline] - #[deprecated(since = "0.21.0", note = "Use verify_ecdsa instead")] - pub fn verify(&self, msg: &Message, sig: &ecdsa::Signature, pk: &PublicKey) -> Result<(), Error> { - self.verify_ecdsa(msg, sig, pk) - } - - /// Checks that `sig` is a valid ECDSA signature for `msg` using the public - /// key `pubkey`. Returns `Ok(())` on success. Note that this function cannot - /// be used for Bitcoin consensus checking since there may exist signatures - /// which OpenSSL would verify but not libsecp256k1, or vice-versa. Requires a - /// verify-capable context. - /// - /// ```rust - /// # #[cfg(feature="rand")] { - /// # use secp256k1::rand::rngs::OsRng; - /// # use secp256k1::{Secp256k1, Message, Error}; - /// # - /// # let secp = Secp256k1::new(); - /// # let mut rng = OsRng::new().expect("OsRng"); - /// # let (secret_key, public_key) = secp.generate_keypair(&mut rng); - /// # - /// let message = Message::from_slice(&[0xab; 32]).expect("32 bytes"); - /// let sig = secp.sign_ecdsa(&message, &secret_key); - /// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Ok(())); - /// - /// let message = Message::from_slice(&[0xcd; 32]).expect("32 bytes"); - /// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(Error::IncorrectSignature)); - /// # } - /// ``` - #[inline] - pub fn verify_ecdsa(&self, msg: &Message, sig: &ecdsa::Signature, pk: &PublicKey) -> Result<(), Error> { - unsafe { - if ffi::secp256k1_ecdsa_verify(self.ctx, sig.as_c_ptr(), msg.as_c_ptr(), pk.as_c_ptr()) == 0 { - Err(Error::IncorrectSignature) - } else { - Ok(()) - } - } - } -} - /// Utility function used to parse hex into a target u8 buffer. Returns /// the number of bytes converted or an error if it encounters an invalid /// character or unexpected end of string. @@ -894,9 +700,9 @@ mod tests { assert_ne!(sig, low_r_sig); } #[cfg(not(fuzzing))] // mocked sig generation doesn't produce low-R sigs - assert!(super::compact_sig_has_zero_first_bit(&low_r_sig.0)); + assert!(ecdsa::compact_sig_has_zero_first_bit(&low_r_sig.0)); #[cfg(not(fuzzing))] // mocked sig generation doesn't produce low-R sigs - assert!(super::der_length_check(&grind_r_sig.0, 70)); + assert!(ecdsa::der_length_check(&grind_r_sig.0, 70)); } } From c5c95513f23bfbc7db29ce9baa6068117ddc301b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 9 Sep 2021 20:01:59 +1000 Subject: [PATCH 11/12] Move helper function below usage This is not C89. We can declare the more important things first. --- src/schnorr.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/schnorr.rs b/src/schnorr.rs index 07ac8963e..0441cad0f 100644 --- a/src/schnorr.rs +++ b/src/schnorr.rs @@ -277,25 +277,6 @@ mod tests { }}; } - fn test_schnorrsig_sign_helper( - sign: fn(&Secp256k1, &Message, &KeyPair, &mut ThreadRng) -> Signature, - ) { - let secp = Secp256k1::new(); - - let mut rng = thread_rng(); - let (seckey, pubkey) = secp.generate_schnorrsig_keypair(&mut rng); - let mut msg = [0u8; 32]; - - for _ in 0..100 { - rng.fill_bytes(&mut msg); - let msg = Message::from_slice(&msg).unwrap(); - - let sig = sign(&secp, &msg, &seckey, &mut rng); - - assert!(secp.verify_schnorr(&sig, &msg, &pubkey).is_ok()); - } - } - #[test] fn test_schnorrsig_sign_with_aux_rand_verify() { test_schnorrsig_sign_helper(|secp, msg, seckey, rng| { @@ -326,6 +307,25 @@ mod tests { }) } + fn test_schnorrsig_sign_helper( + sign: fn(&Secp256k1, &Message, &KeyPair, &mut ThreadRng) -> Signature, + ) { + let secp = Secp256k1::new(); + + let mut rng = thread_rng(); + let (seckey, pubkey) = secp.generate_schnorrsig_keypair(&mut rng); + let mut msg = [0u8; 32]; + + for _ in 0..100 { + rng.fill_bytes(&mut msg); + let msg = Message::from_slice(&msg).unwrap(); + + let sig = sign(&secp, &msg, &seckey, &mut rng); + + assert!(secp.verify_schnorr(&sig, &msg, &pubkey).is_ok()); + } + } + #[test] #[cfg(not(fuzzing))] // fixed sig vectors can't work with fuzz-sigs fn test_schnorrsig_sign() { From d244b4d747c60822a14f07e37166c8e3f45e1cd0 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 11 Nov 2021 13:41:57 +1100 Subject: [PATCH 12/12] Fix typo in docs --- src/key.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/key.rs b/src/key.rs index 132e1208c..b5c5f3c03 100644 --- a/src/key.rs +++ b/src/key.rs @@ -297,7 +297,7 @@ impl PublicKey { } } - /// Creates a new compressed public key key using data from BIP-340 [`KeyPair`] + /// Creates a new compressed public key using data from BIP-340 [`KeyPair`]. #[inline] pub fn from_keypair(keypair: &KeyPair) -> Self { unsafe {