Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,9 @@ exclude = ["tests", "contrib"]
[features]
default = ["std"]
std = ["alloc", "secp256k1-sys/std", "rand?/std", "rand?/std_rng", "rand?/thread_rng"]
# allow use of Secp256k1::new and related API that requires an allocator
alloc = ["secp256k1-sys/alloc"]
recovery = ["secp256k1-sys/recovery"]
lowmemory = ["secp256k1-sys/lowmemory"]
global-context = ["std"]
# disable re-randomization of the global context, which provides some
# defense-in-depth against sidechannel attacks. You should only use
# this feature if you expect the `rand` crate's thread_rng to panic.
# (If you are sure the `rand` and `std` features will not be enabled, e.g.
# if you are doing a no-std build, then this feature does nothing
# and is not necessary.)
global-context-less-secure = ["global-context"]
arbitrary = ["dep:arbitrary"]

[dependencies]
Expand Down
4 changes: 2 additions & 2 deletions contrib/test_vars.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
# shellcheck disable=SC2034

# Test all these features with "std" enabled.
FEATURES_WITH_STD="global-context global-context-less-secure lowmemory rand recovery serde"
FEATURES_WITH_STD="lowmemory rand recovery serde"

# Test all these features without "std" enabled.
FEATURES_WITHOUT_STD="global-context global-context-less-secure lowmemory rand recovery serde alloc"
FEATURES_WITHOUT_STD="lowmemory rand recovery serde alloc"

# Run these examples.
EXAMPLES="sign_verify:std sign_verify_recovery:std,recovery generate_keys:rand,std"
2 changes: 1 addition & 1 deletion githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fi
git diff-index --check --cached "$against" -- || exit 1

# Check that code lints cleanly.
cargo clippy --features=rand,std,recovery,lowmemory,global-context --all-targets -- -D warnings || exit 1
cargo clippy --features=rand,std,recovery,lowmemory --all-targets -- -D warnings || exit 1

# Check that there are no formatting issues.
cargo +nightly fmt --check || exit 1
4 changes: 3 additions & 1 deletion src/context/internal_nostd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod self_contained_context {
const MAX_PREALLOC_SIZE: usize = 16; // measured at 208 bytes on Andrew's 64-bit system

/// A secp256k1 context object which can be allocated on the stack or in static storage.
#[derive(Debug)]
pub struct SelfContainedContext(
[MaybeUninit<AlignedType>; MAX_PREALLOC_SIZE],
Option<NonNull<ffi::Context>>,
Expand Down Expand Up @@ -73,7 +74,8 @@ mod self_contained_context {
// because we need a const constructor.)
pub(super) use self_contained_context::SelfContainedContext;

static SECP256K1: SpinLock<SelfContainedContext> = SpinLock::<SelfContainedContext>::new();
/// A global static context to avoid repeatedly creating contexts.
pub static SECP256K1: SpinLock<SelfContainedContext> = SpinLock::<SelfContainedContext>::new();

/// Borrows the global context and does some operation on it.
///
Expand Down
5 changes: 4 additions & 1 deletion src/context/internal_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use secp256k1_sys as ffi;
use crate::{All, Context, Secp256k1};

thread_local! {
static SECP256K1: RefCell<Secp256k1<All>> = RefCell::new(Secp256k1::new());
/// A global static context to avoid repeatedly creating contexts.
///
/// If `rand` feature is enabled, context will have been randomized using `rng`.
pub static SECP256K1: RefCell<Secp256k1<All>> = RefCell::new(Secp256k1::new());
}

/// Borrows the global context and does some operation on it.
Expand Down
49 changes: 4 additions & 45 deletions src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,9 @@ mod internal;
#[cfg(not(feature = "std"))]
mod spinlock;

pub use internal::{rerandomize_global_context, with_global_context, with_raw_global_context};

#[cfg(all(feature = "global-context", feature = "std"))]
/// Module implementing a singleton pattern for a global `Secp256k1` context.
pub mod global {

use std::ops::Deref;
use std::sync::Once;

use crate::{All, Secp256k1};

/// Proxy struct for global `SECP256K1` context.
#[derive(Debug, Copy, Clone)]
pub struct GlobalContext {
__private: (),
}

/// A global static context to avoid repeatedly creating contexts.
///
/// If `rand` and `std` feature is enabled, context will have been randomized using
/// `rng`.
pub static SECP256K1: &GlobalContext = &GlobalContext { __private: () };

impl Deref for GlobalContext {
type Target = Secp256k1<All>;

#[allow(unused_mut)] // Unused when `rand` + `std` is not enabled.
#[allow(static_mut_refs)] // The "proper" way to do this is with OnceLock (MSRV 1.70) or LazyLock (MSRV 1.80)
// See https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html
fn deref(&self) -> &Self::Target {
static ONCE: Once = Once::new();
static mut CONTEXT: Option<Secp256k1<All>> = None;
ONCE.call_once(|| unsafe {
CONTEXT = Some(Secp256k1::new());
});
unsafe { CONTEXT.as_ref().unwrap() }
}
}
}
pub use internal::{
rerandomize_global_context, with_global_context, with_raw_global_context, SECP256K1,
};

/// A trait for all kinds of contexts that lets you define the exact flags and a function to
/// deallocate memory. It isn't possible to implement this for types outside this crate.
Expand Down Expand Up @@ -213,12 +177,7 @@ mod alloc_only {
phantom: PhantomData,
};

#[cfg(all(
not(target_arch = "wasm32"),
feature = "rand",
feature = "std",
not(feature = "global-context-less-secure")
))]
#[cfg(all(not(target_arch = "wasm32"), feature = "rand", feature = "std",))]
{
ctx.randomize(&mut rand::rng());
}
Expand Down
2 changes: 2 additions & 0 deletions src/context/spinlock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const MAX_SPINLOCK_ATTEMPTS: usize = 128;
// new stack-local context object if we are unable to obtain a lock on the
// global one. This is slow and loses the defense-in-depth "rerandomization"
// anti-sidechannel measure, but it is better than deadlocking..
#[derive(Debug)]
pub struct SpinLock<T> {
flag: AtomicBool,
// Invariant: if this is non-None, then the store is valid and can be
Expand Down Expand Up @@ -83,6 +84,7 @@ impl<T> SpinLock<T> {
}

/// Drops the lock when it goes out of scope.
#[derive(Debug)]
pub struct SpinLockGuard<'a, T> {
lock: &'a SpinLock<T>,
}
Expand Down
1 change: 0 additions & 1 deletion src/ecdsa/recovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ impl RecoverableSignature {
/// Determines the public key for which this [`Signature`] is valid for `msg`. Requires a
/// verify-capable context.
#[inline]
#[cfg(feature = "global-context")]
pub fn recover(&self, msg: impl Into<Message>) -> Result<key::PublicKey, Error> {
self.recover_ecdsa(msg)
}
Expand Down
25 changes: 4 additions & 21 deletions src/key/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,6 @@ impl PublicKey {
#[inline]
pub fn from_ellswift(ellswift: ElligatorSwift) -> PublicKey { ElligatorSwift::decode(ellswift) }

/// Creates a new public key from a [`SecretKey`].
#[inline]
#[cfg(feature = "global-context")]
#[deprecated(since = "TBD", note = "use from_secret_key instead")]
pub fn from_secret_key_global(sk: &SecretKey) -> PublicKey { PublicKey::from_secret_key(sk) }

/// Creates a public key directly from a slice.
#[inline]
pub fn from_slice(data: &[u8]) -> Result<PublicKey, Error> {
Expand Down Expand Up @@ -460,7 +454,7 @@ impl<'de> serde::Deserialize<'de> for PublicKey {
///
/// # Serde support
///
/// Implements de/serialization with the `serde` and `global-context` features enabled. Serializes
/// Implements de/serialization with the `serde` and feature enabled. Serializes
/// the secret bytes only. We treat the byte value as a tuple of 32 `u8`s for non-human-readable
/// formats. This representation is optimal for some formats (e.g. [`bincode`]) however other
/// formats may be less optimal (e.g. [`cbor`]). For human-readable formats we use a hex string.
Expand Down Expand Up @@ -552,16 +546,6 @@ impl Keypair {
#[deprecated(note = "use FromStr or parse instead")]
pub fn from_seckey_str(s: &str) -> Result<Self, Error> { s.parse() }

/// Creates a [`Keypair`] directly from a secret key string.
///
/// # Errors
///
/// [`Error::InvalidSecretKey`] if the string does not consist of exactly 64 hex characters,
/// or if the encoded number is an invalid scalar.
#[inline]
#[deprecated(note = "use FromStr or parse instead")]
pub fn from_seckey_str_global(s: &str) -> Result<Keypair, Error> { s.parse() }

/// Generates a new random key pair.
/// # Examples
///
Expand Down Expand Up @@ -606,7 +590,7 @@ impl Keypair {

/// Generates a new random secret key.
#[inline]
#[cfg(all(feature = "global-context", feature = "rand"))]
#[cfg(feature = "rand")]
#[deprecated(since = "TBD", note = "use Keypair::new instead")]
pub fn new_global<R: ::rand::Rng + ?Sized>(rng: &mut R) -> Keypair { Keypair::new(rng) }

Expand Down Expand Up @@ -1939,13 +1923,12 @@ mod test {
}

#[test]
#[cfg(all(feature = "global-context", feature = "serde"))]
#[cfg(feature = "serde")]
fn test_serde_keypair() {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_test::{assert_tokens, Configure, Token};

use crate::key::Keypair;
use crate::SECP256K1;

#[rustfmt::skip]
static SK_BYTES: [u8; 32] = [
Expand Down Expand Up @@ -2115,7 +2098,7 @@ mod test {

#[test]
#[cfg(not(secp256k1_fuzz))]
#[cfg(all(feature = "global-context", feature = "serde"))]
#[cfg(feature = "serde")]
fn test_serde_x_only_pubkey() {
use serde_test::{assert_tokens, Configure, Token};

Expand Down
29 changes: 3 additions & 26 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,26 +49,6 @@
//! # }
//! ```
//!
//! If the "global-context" feature is enabled you have access to an alternate API.
//!
//! ```rust
//! # #[cfg(all(feature = "global-context", feature = "rand", feature = "std"))] {
//! use secp256k1::{rand, Message};
//!
//! // See previous example regarding this constant.
//! const HELLO_WORLD_SHA2: [u8; 32] = [
//! 0x31, 0x5f, 0x5b, 0xdb, 0x76, 0xd0, 0x78, 0xc4, 0x3b, 0x8a, 0xc0, 0x06, 0x4e, 0x4a, 0x01, 0x64,
//! 0x61, 0x2b, 0x1f, 0xce, 0x77, 0xc8, 0x69, 0x34, 0x5b, 0xfc, 0x94, 0xc7, 0x58, 0x94, 0xed, 0xd3,
//! ];
//!
//! let (secret_key, public_key) = secp256k1::generate_keypair(&mut rand::rng());
//! let message = Message::from_digest(HELLO_WORLD_SHA2);
//!
//! let sig = secret_key.sign_ecdsa(message);
//! assert!(sig.verify(message, &public_key).is_ok());
//! # }
//! ```
//!
//! The above code requires `rust-secp256k1` to be compiled with the `rand`, `hashes`, and `std`
//! feature enabled, to get access to [`generate_keypair`](struct.Secp256k1.html#method.generate_keypair)
//! Alternately, keys and messages can be parsed from slices, like
Expand Down Expand Up @@ -139,7 +119,6 @@
//! * `hashes` - use the `hashes` library.
//! * `recovery` - enable functions that can compute the public key from signature.
//! * `lowmemory` - optimize the library for low-memory environments.
//! * `global-context` - enable use of global secp256k1 context (implies `std`).
//! * `serde` - implements serialization and deserialization for types in this crate using `serde`.
//! **Important**: `serde` encoding is **not** the same as consensus encoding!
//!
Expand Down Expand Up @@ -191,8 +170,7 @@ use crate::ffi::CPtr;
#[rustfmt::skip] // Keep public re-exports separate.
pub use secp256k1_sys as ffi;

#[cfg(all(feature = "global-context", feature = "std"))]
pub use crate::context::global::{self, SECP256K1};
pub use crate::context::SECP256K1;
#[cfg(feature = "alloc")]
pub use crate::context::{All, SignOnly, VerifyOnly};
#[doc(inline)]
Expand Down Expand Up @@ -423,12 +401,12 @@ pub fn generate_keypair<R: rand::Rng + ?Sized>(rng: &mut R) -> (key::SecretKey,
/// Constructor for unit testing. (Calls `generate_keypair` if all
/// the relevant features are on to get coverage of that functoin.)
#[cfg(test)]
#[cfg(all(feature = "global-context", feature = "rand", feature = "std"))]
#[cfg(all(feature = "rand", feature = "std"))]
fn test_random_keypair() -> (key::SecretKey, key::PublicKey) { generate_keypair(&mut rand::rng()) }

/// Constructor for unit testing.
#[cfg(test)]
#[cfg(not(all(feature = "global-context", feature = "rand", feature = "std")))]
#[cfg(not(all(feature = "rand", feature = "std")))]
fn test_random_keypair() -> (key::SecretKey, key::PublicKey) {
let sk = SecretKey::test_random();
let pk = key::PublicKey::from_secret_key(&sk);
Expand Down Expand Up @@ -964,7 +942,6 @@ mod tests {
assert_tokens(&sig.readable(), &[Token::String(SIG_STR)]);
}

#[cfg(feature = "global-context")]
#[test]
fn test_global_context() {
let sk_data = hex!("e6dd32f8761625f105c39a39f19370b3521d845a12456d60ce44debd0a362641");
Expand Down
1 change: 0 additions & 1 deletion src/schnorr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ impl Signature {

/// Verifies a schnorr signature for `msg` using `pk`.
#[inline]
#[cfg(feature = "global-context")]
pub fn verify(&self, msg: &[u8], pk: &XOnlyPublicKey) -> Result<(), Error> {
verify(self, msg, pk)
}
Expand Down
3 changes: 1 addition & 2 deletions src/secret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,8 @@ impl Keypair {
/// ```
/// # #[cfg(feature = "std")] {
/// # use std::str::FromStr;
/// use secp256k1::{Keypair, Secp256k1, SecretKey};
/// use secp256k1::{Keypair, SecretKey};
///
/// let secp = Secp256k1::new();
/// let key = SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap();
/// let key = Keypair::from_secret_key(&key);
/// // Here we explicitly display the secret value:
Expand Down
5 changes: 1 addition & 4 deletions tests/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ extern crate bincode;
extern crate secp256k1;
extern crate serde_cbor;

#[cfg(feature = "global-context")]
use secp256k1::Keypair;
use secp256k1::{musig, PublicKey, SecretKey, XOnlyPublicKey};
use secp256k1::{musig, Keypair, PublicKey, SecretKey, XOnlyPublicKey};

// Arbitrary key data.

Expand Down Expand Up @@ -95,7 +93,6 @@ fn bincode_public_key() {
}

#[test]
#[cfg(feature = "global-context")]
fn bincode_keypair() {
let kp = Keypair::from_seckey_byte_array(SK_BYTES).expect("failed to create keypair");
let ser = bincode::serialize(&kp).unwrap();
Expand Down
Loading