From eb453b8227f6600600f215ff2d515abe5733e2dd Mon Sep 17 00:00:00 2001 From: Tobin Harding Date: Tue, 8 Feb 2022 06:11:05 +0000 Subject: [PATCH] Add global context API Our API often involves a `Secp256k1` parameter, when users enable the `global-context` feature they must then pass `SECP256K1` into these functions. This is kind of clunky since the global is by definition available everywhere. Make the API more ergonomic for `global-context` builds by adding various API functions/methods that use the global context implicitly. --- src/ecdsa/mod.rs | 11 ++++++++++ src/ecdsa/recovery.rs | 9 ++++++++ src/key.rs | 49 +++++++++++++++++++++++++++++++++++++++++++ src/schnorr.rs | 11 ++++++++++ 4 files changed, 80 insertions(+) diff --git a/src/ecdsa/mod.rs b/src/ecdsa/mod.rs index f7baa71..1c593e3 100644 --- a/src/ecdsa/mod.rs +++ b/src/ecdsa/mod.rs @@ -12,6 +12,9 @@ mod recovery; #[cfg_attr(docsrs, doc(cfg(feature = "recovery")))] pub use self::recovery::{RecoveryId, RecoverableSignature}; +#[cfg(feature = "global-context")] +use SECP256K1; + /// An ECDSA signature #[derive(Copy, Clone, PartialEq, Eq)] pub struct Signature(pub(crate) ffi::Signature); @@ -269,6 +272,14 @@ impl Signature { } ret } + + /// Verifies an ECDSA signature for `msg` using `pk` and the global [`SECP256K1`] context. + #[inline] + #[cfg(feature = "global-context")] + #[cfg_attr(docsrs, doc(cfg(feature = "global-context")))] + pub fn verify(&self, msg: &Message, pk: &PublicKey) -> Result<(), Error> { + SECP256K1.verify_ecdsa(msg, self, pk) + } } impl CPtr for Signature { diff --git a/src/ecdsa/recovery.rs b/src/ecdsa/recovery.rs index ca367de..b70e1e7 100644 --- a/src/ecdsa/recovery.rs +++ b/src/ecdsa/recovery.rs @@ -121,6 +121,15 @@ impl RecoverableSignature { Signature(ret) } } + + /// Determines the public key for which this [`Signature`] is valid for `msg`. Requires a + /// verify-capable context. + #[inline] + #[cfg(feature = "global-context")] + #[cfg_attr(docsrs, doc(cfg(feature = "global-context")))] + pub fn recover(&self, msg: &Message) -> Result { + SECP256K1.recover_ecdsa(msg, self) + } } diff --git a/src/key.rs b/src/key.rs index 45d631c..2f5ecc9 100644 --- a/src/key.rs +++ b/src/key.rs @@ -28,6 +28,11 @@ use Verification; use constants; use ffi::{self, CPtr}; +#[cfg(feature = "global-context")] +use {Message, ecdsa, SECP256K1}; +#[cfg(all(feature = "global-context", feature = "rand-std"))] +use schnorr; + /// Secret 256-bit key used as `x` in an ECDSA signature. /// /// # Examples @@ -279,6 +284,14 @@ impl SecretKey { } } } + + /// Constructs an ECDSA signature for `msg` using the global [`SECP256K1`] context. + #[inline] + #[cfg(feature = "global-context")] + #[cfg_attr(docsrs, doc(cfg(feature = "global-context")))] + pub fn sign_ecdsa(&self, msg: Message) -> ecdsa::Signature { + SECP256K1.sign_ecdsa(&msg, self) + } } #[cfg(feature = "serde")] @@ -349,6 +362,14 @@ impl PublicKey { } } + /// Creates a new public key from a [`SecretKey`] and the global [`SECP256K1`] context. + #[inline] + #[cfg(feature = "global-context")] + #[cfg_attr(docsrs, doc(cfg(feature = "global-context")))] + pub fn from_secret_key_global(sk: &SecretKey) -> PublicKey { + PublicKey::from_secret_key(&SECP256K1, sk) + } + /// Creates a public key directly from a slice. #[inline] pub fn from_slice(data: &[u8]) -> Result { @@ -738,6 +759,18 @@ impl KeyPair { } } + /// Creates a Schnorr [`KeyPair`] directly from a secret key string and the global [`SECP256K1`] context. + /// + /// # Errors + /// + /// [`Error::InvalidSecretKey`] if corresponding public key for the provided secret key is not even. + #[inline] + #[cfg(feature = "global-context")] + #[cfg_attr(docsrs, doc(cfg(feature = "global-context")))] + pub fn from_seckey_str_global(s: &str) -> Result { + KeyPair::from_seckey_str(SECP256K1, s) + } + /// Generates a new random secret key. /// # Examples /// @@ -768,6 +801,14 @@ impl KeyPair { } } + /// Generates a new random secret key using the global [`SECP256K1`] context. + #[inline] + #[cfg(all(feature = "global-context", feature = "rand"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "global-context", feature = "rand"))))] + pub fn new_global(rng: &mut R) -> KeyPair { + KeyPair::new(SECP256K1, rng) + } + /// Serializes the key pair as a secret key byte value. #[inline] pub fn serialize_secret(&self) -> [u8; constants::SECRET_KEY_SIZE] { @@ -830,6 +871,14 @@ impl KeyPair { pub fn public_key(&self) -> XOnlyPublicKey { XOnlyPublicKey::from_keypair(self) } + + /// Constructs an schnorr signature for `msg` using the global [`SECP256K1`] context. + #[inline] + #[cfg(all(feature = "global-context", feature = "rand-std"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "global-context", feature = "rand-std"))))] + pub fn sign_schnorr(&self, msg: Message) -> schnorr::Signature { + SECP256K1.sign_schnorr(&msg, self) + } } impl From for SecretKey { diff --git a/src/schnorr.rs b/src/schnorr.rs index c709573..e2be4ec 100644 --- a/src/schnorr.rs +++ b/src/schnorr.rs @@ -13,6 +13,9 @@ use ffi::{self, CPtr}; use {constants, Secp256k1}; use {Message, Signing, Verification, KeyPair, XOnlyPublicKey}; +#[cfg(all(feature = "global-context", feature = "rand-std"))] +use SECP256K1; + /// Represents a Schnorr signature. pub struct Signature([u8; constants::SCHNORRSIG_SIGNATURE_SIZE]); impl_array_newtype!(Signature, u8, constants::SCHNORRSIG_SIGNATURE_SIZE); @@ -88,6 +91,14 @@ impl Signature { _ => Err(Error::InvalidSignature), } } + + /// Verifies a schnorr signature for `msg` using `pk` and the global [`SECP256K1`] context. + #[inline] + #[cfg(all(feature = "global-context", feature = "rand-std"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "global-context", feature = "rand-std"))))] + pub fn verify(&self, msg: &Message, pk: &XOnlyPublicKey) -> Result<(), Error> { + SECP256K1.verify_schnorr(self, msg, pk) + } } impl Secp256k1 {