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 562f02e..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")] @@ -338,9 +351,7 @@ impl PublicKey { /// # } /// ``` #[inline] - pub fn from_secret_key(secp: &Secp256k1, - sk: &SecretKey) - -> PublicKey { + pub fn from_secret_key(secp: &Secp256k1,sk: &SecretKey) -> PublicKey { unsafe { let mut pk = ffi::PublicKey::new(); // We can assume the return value because it's not possible to construct @@ -351,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 { @@ -740,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 /// @@ -770,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] { @@ -832,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/lib.rs b/src/lib.rs index c007c98..159f637 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,22 @@ //! # } //! ``` //! +//! If the "global-context" feature is enabled you have access to an alternate API. +//! +//! ```rust +//! # #[cfg(all(feature="global-context", feature = "std", feature="rand-std", features = "bitcoin_hashes"))] { +//! use secp256k1::rand::thread_rng; +//! use secp256k1::{generate_keypair, Message}; +//! use secp256k1::hashes::sha256; +//! +//! let (secret_key, public_key) = generate_keypair(&mut thread_rng()); +//! let message = Message::from_hashed_data::("Hello World!".as_bytes()); +//! +//! let sig = secret_key.sign_ecdsa(&message, &secret_key); +//! assert!(sig.verify(&message, &public_key).is_ok()); +//! # } +//! ``` +//! //! The above code requires `rust-secp256k1` to be compiled with the `rand-std` and `bitcoin_hashes` //! feature enabled, to get access to [`generate_keypair`](struct.Secp256k1.html#method.generate_keypair) //! Alternately, keys and messages can be parsed from slices, like @@ -198,7 +214,7 @@ use core::{mem, fmt, str}; use ffi::{CPtr, types::AlignedType}; #[cfg(feature = "global-context")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "global-context", feature = "global-context"))))] +#[cfg_attr(docsrs, doc(cfg(feature = "global-context")))] pub use context::global::SECP256K1; #[cfg(feature = "bitcoin_hashes")] @@ -458,6 +474,14 @@ impl Secp256k1 { } } +/// Generates a random keypair 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 generate_keypair(rng: &mut R) -> (key::SecretKey, key::PublicKey) { + SECP256K1.generate_keypair(rng) +} + /// 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. @@ -960,8 +984,6 @@ mod tests { #[cfg(feature = "global-context")] #[test] fn test_global_context() { - use super::SECP256K1; - let sk_data = hex!("e6dd32f8761625f105c39a39f19370b3521d845a12456d60ce44debd0a362641"); let sk = SecretKey::from_slice(&sk_data).unwrap(); let msg_data = hex!("a4965ca63b7d8562736ceec36dfa5a11bf426eb65be8ea3f7a49ae363032da0d"); 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 {