Merge rust-bitcoin/rust-secp256k1#385: Randomize context on creation
8339ca5706
Add documentation guiding users towards randomization (Tobin Harding)cf1496b64e
Add documentation about rand-std feature (Tobin Harding)1693d51ce7
Randomize context on creation (Tobin Harding)a0465ea279
Remove feature global-context-less-secure (Tobin Harding) Pull request description: Currently it is easy for users to mis-use our API because they may not know that `randomize()` should be called after context creation for maximum defence against side channel attacks. This PR entails the first two parts of the plan outlined in #388. The commit messages are a bit light of information as to _why_ we are doing this so please see #388 for more context. In light of @real-or-random's [comment](https://github.com/rust-bitcoin/rust-secp256k1/issues/388#issuecomment-1026613592) about verification contexts the randomization is done in `gen_new` i.e., for _all_ contexts not just signing ones. Also, I think we should add some docs about exactly _what_ randomization buys the user and what it costs. I do not know exactly what this is, can someone please write a sentence or two that we can include in the docs to `gen_new`? @TheBlueMatt please review patch 4. Resolves: #225 **Note**: This is a total re-write of the original PR, most of the discussion below is stale. Of note, the additional API that takes a seed during construction is not implemented here. ACKs for top commit: apoelstra: ACK8339ca5706
Tree-SHA512: e74fe9a6eaf8ac40e4e06997602006eb8ca95216b5bc6dca3f5f96b5b4d3bf8610d851d8f1ef5c199ab7fbe85b34d162f2ee0073647f45105a486d20d8c0722a
This commit is contained in:
commit
86447eea20
|
@ -26,8 +26,7 @@ alloc = []
|
||||||
rand-std = ["rand/std"]
|
rand-std = ["rand/std"]
|
||||||
recovery = ["secp256k1-sys/recovery"]
|
recovery = ["secp256k1-sys/recovery"]
|
||||||
lowmemory = ["secp256k1-sys/lowmemory"]
|
lowmemory = ["secp256k1-sys/lowmemory"]
|
||||||
global-context = ["std", "rand-std", "global-context-less-secure"]
|
global-context = ["std"]
|
||||||
global-context-less-secure = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
secp256k1-sys = { version = "0.4.2", default-features = false, path = "./secp256k1-sys" }
|
secp256k1-sys = { version = "0.4.2", default-features = false, path = "./secp256k1-sys" }
|
||||||
|
|
|
@ -9,11 +9,11 @@ use Secp256k1;
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
|
||||||
pub use self::alloc_only::*;
|
pub use self::alloc_only::*;
|
||||||
|
|
||||||
#[cfg(all(feature = "global-context-less-secure", feature = "std"))]
|
#[cfg(all(feature = "global-context", feature = "std"))]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "global-context", feature = "global-context-less-secure"))))]
|
#[cfg_attr(docsrs, doc(cfg(all(feature = "global-context", feature = "std"))))]
|
||||||
/// Module implementing a singleton pattern for a global `Secp256k1` context
|
/// Module implementing a singleton pattern for a global `Secp256k1` context
|
||||||
pub mod global {
|
pub mod global {
|
||||||
#[cfg(feature = "global-context")]
|
#[cfg(feature = "rand-std")]
|
||||||
use rand;
|
use rand;
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -26,22 +26,29 @@ pub mod global {
|
||||||
__private: (),
|
__private: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A global, static context to avoid repeatedly creating contexts where one can't be passed
|
/// A global static context to avoid repeatedly creating contexts.
|
||||||
///
|
///
|
||||||
/// If the global-context feature is enabled (and not just the global-context-less-secure),
|
/// If `rand-std` feature is enabled, context will have been randomized using `thread_rng`.
|
||||||
/// this will have been randomized.
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(all(feature = "global-context", feature = "rand-std"))] {
|
||||||
|
/// use secp256k1::{PublicKey, SECP256K1};
|
||||||
|
/// use secp256k1::rand::thread_rng;
|
||||||
|
/// let _ = SECP256K1.generate_keypair(&mut thread_rng());
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub static SECP256K1: &GlobalContext = &GlobalContext { __private: () };
|
pub static SECP256K1: &GlobalContext = &GlobalContext { __private: () };
|
||||||
|
|
||||||
impl Deref for GlobalContext {
|
impl Deref for GlobalContext {
|
||||||
type Target = Secp256k1<All>;
|
type Target = Secp256k1<All>;
|
||||||
|
|
||||||
#[allow(unused_mut)] // Unused when "global-context" is not enabled.
|
#[allow(unused_mut)] // Unused when `rand-std` is not enabled.
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
static ONCE: Once = Once::new();
|
static ONCE: Once = Once::new();
|
||||||
static mut CONTEXT: Option<Secp256k1<All>> = None;
|
static mut CONTEXT: Option<Secp256k1<All>> = None;
|
||||||
ONCE.call_once(|| unsafe {
|
ONCE.call_once(|| unsafe {
|
||||||
let mut ctx = Secp256k1::new();
|
let mut ctx = Secp256k1::new();
|
||||||
#[cfg(feature = "global-context")]
|
#[cfg(feature = "rand-std")]
|
||||||
{
|
{
|
||||||
ctx.randomize(&mut rand::thread_rng());
|
ctx.randomize(&mut rand::thread_rng());
|
||||||
}
|
}
|
||||||
|
@ -108,6 +115,9 @@ mod alloc_only {
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use alloc::alloc;
|
use alloc::alloc;
|
||||||
|
|
||||||
|
#[cfg(feature = "rand-std")]
|
||||||
|
use rand;
|
||||||
|
|
||||||
impl private::Sealed for SignOnly {}
|
impl private::Sealed for SignOnly {}
|
||||||
impl private::Sealed for All {}
|
impl private::Sealed for All {}
|
||||||
impl private::Sealed for VerifyOnly {}
|
impl private::Sealed for VerifyOnly {}
|
||||||
|
@ -167,7 +177,23 @@ mod alloc_only {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Context> Secp256k1<C> {
|
impl<C: Context> Secp256k1<C> {
|
||||||
/// Lets you create a context in a generic manner(sign/verify/all)
|
/// Lets you create a context in a generic manner (sign/verify/all).
|
||||||
|
///
|
||||||
|
/// If `rand-std` feature is enabled, context will have been randomized using `thread_rng`.
|
||||||
|
/// If `rand-std` feature is not enabled please consider randomizing the context as follows:
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(all(feature = "rand-std", any(feature = "alloc", feature = "std")))] {
|
||||||
|
/// # use secp256k1::Secp256k1;
|
||||||
|
/// # use secp256k1::rand::{thread_rng, RngCore};
|
||||||
|
/// let mut ctx = Secp256k1::new();
|
||||||
|
/// # let mut rng = thread_rng();
|
||||||
|
/// # let mut seed = [0u8; 32];
|
||||||
|
/// # rng.fill_bytes(&mut seed);
|
||||||
|
/// // let seed = <32 bytes of random data>
|
||||||
|
/// ctx.seeded_randomize(&seed);
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[allow(unused_mut)] // Unused when `rand-std` is not enabled.
|
||||||
pub fn gen_new() -> Secp256k1<C> {
|
pub fn gen_new() -> Secp256k1<C> {
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
ffi::types::sanity_checks_for_wasm();
|
ffi::types::sanity_checks_for_wasm();
|
||||||
|
@ -175,30 +201,49 @@ mod alloc_only {
|
||||||
let size = unsafe { ffi::secp256k1_context_preallocated_size(C::FLAGS) };
|
let size = unsafe { ffi::secp256k1_context_preallocated_size(C::FLAGS) };
|
||||||
let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap();
|
let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap();
|
||||||
let ptr = unsafe {alloc::alloc(layout)};
|
let ptr = unsafe {alloc::alloc(layout)};
|
||||||
Secp256k1 {
|
let mut ctx = Secp256k1 {
|
||||||
ctx: unsafe { ffi::secp256k1_context_preallocated_create(ptr as *mut c_void, C::FLAGS) },
|
ctx: unsafe { ffi::secp256k1_context_preallocated_create(ptr as *mut c_void, C::FLAGS) },
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
size,
|
size,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "rand-std")]
|
||||||
|
{
|
||||||
|
ctx.randomize(&mut rand::thread_rng());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Secp256k1<All> {
|
impl Secp256k1<All> {
|
||||||
/// Creates a new Secp256k1 context with all capabilities
|
/// Creates a new Secp256k1 context with all capabilities.
|
||||||
|
///
|
||||||
|
/// If `rand-std` feature is enabled, context will have been randomized using `thread_rng`.
|
||||||
|
/// If `rand-std` feature is not enabled please consider randomizing the context (see docs
|
||||||
|
/// for `Secp256k1::gen_new()`).
|
||||||
pub fn new() -> Secp256k1<All> {
|
pub fn new() -> Secp256k1<All> {
|
||||||
Secp256k1::gen_new()
|
Secp256k1::gen_new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Secp256k1<SignOnly> {
|
impl Secp256k1<SignOnly> {
|
||||||
/// Creates a new Secp256k1 context that can only be used for signing
|
/// Creates a new Secp256k1 context that can only be used for signing.
|
||||||
|
///
|
||||||
|
/// If `rand-std` feature is enabled, context will have been randomized using `thread_rng`.
|
||||||
|
/// If `rand-std` feature is not enabled please consider randomizing the context (see docs
|
||||||
|
/// for `Secp256k1::gen_new()`).
|
||||||
pub fn signing_only() -> Secp256k1<SignOnly> {
|
pub fn signing_only() -> Secp256k1<SignOnly> {
|
||||||
Secp256k1::gen_new()
|
Secp256k1::gen_new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Secp256k1<VerifyOnly> {
|
impl Secp256k1<VerifyOnly> {
|
||||||
/// Creates a new Secp256k1 context that can only be used for verification
|
/// Creates a new Secp256k1 context that can only be used for verification.
|
||||||
|
///
|
||||||
|
/// If `rand-std` feature is enabled, context will have been randomized using `thread_rng`.
|
||||||
|
/// If `rand-std` feature is not enabled please consider randomizing the context (see docs
|
||||||
|
/// for `Secp256k1::gen_new()`).
|
||||||
pub fn verification_only() -> Secp256k1<VerifyOnly> {
|
pub fn verification_only() -> Secp256k1<VerifyOnly> {
|
||||||
Secp256k1::gen_new()
|
Secp256k1::gen_new()
|
||||||
}
|
}
|
||||||
|
|
|
@ -641,7 +641,7 @@ impl Ord for PublicKey {
|
||||||
/// feature active. This is due to security considerations, see the [`serde_keypair`] documentation
|
/// feature active. This is due to security considerations, see the [`serde_keypair`] documentation
|
||||||
/// for details.
|
/// for details.
|
||||||
///
|
///
|
||||||
/// If the `serde` and `global-context[-less-secure]` features are active `KeyPair`s can be serialized and
|
/// If the `serde` and `global-context` features are active `KeyPair`s can be serialized and
|
||||||
/// deserialized by annotating them with `#[serde(with = "secp256k1::serde_keypair")]`
|
/// deserialized by annotating them with `#[serde(with = "secp256k1::serde_keypair")]`
|
||||||
/// inside structs or enums for which [`Serialize`] and [`Deserialize`] are being derived.
|
/// inside structs or enums for which [`Serialize`] and [`Deserialize`] are being derived.
|
||||||
///
|
///
|
||||||
|
@ -1320,7 +1320,7 @@ impl<'de> ::serde::Deserialize<'de> for XOnlyPublicKey {
|
||||||
///
|
///
|
||||||
/// [`SecretKey`]: crate::SecretKey
|
/// [`SecretKey`]: crate::SecretKey
|
||||||
/// [global context]: crate::SECP256K1
|
/// [global context]: crate::SECP256K1
|
||||||
#[cfg(all(feature = "global-context-less-secure", feature = "serde"))]
|
#[cfg(all(feature = "global-context", feature = "serde"))]
|
||||||
pub mod serde_keypair {
|
pub mod serde_keypair {
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use key::KeyPair;
|
use key::KeyPair;
|
||||||
|
@ -1924,7 +1924,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(all(feature = "global-context-less-secure", feature = "serde"))]
|
#[cfg(all(feature = "global-context", feature = "serde"))]
|
||||||
fn test_serde_keypair() {
|
fn test_serde_keypair() {
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use serde_test::{Configure, Token, assert_tokens};
|
use serde_test::{Configure, Token, assert_tokens};
|
||||||
|
|
20
src/lib.rs
20
src/lib.rs
|
@ -20,9 +20,13 @@
|
||||||
//! and its derivatives.
|
//! and its derivatives.
|
||||||
//!
|
//!
|
||||||
//! To minimize dependencies, some functions are feature-gated. To generate
|
//! To minimize dependencies, some functions are feature-gated. To generate
|
||||||
//! random keys or to re-randomize a context object, compile with the "rand"
|
//! random keys or to re-randomize a context object, compile with the
|
||||||
//! feature. To de/serialize objects with serde, compile with "serde".
|
//! `rand-std` feature. If you are willing to use the `rand-std` feature, we
|
||||||
//! **Important**: `serde` encoding is **not** the same as consensus encoding!
|
//! have enabled an additional defense-in-depth sidechannel protection for
|
||||||
|
//! our context objects, which re-blinds certain operations on secret key
|
||||||
|
//! data. To de/serialize objects with serde, compile with "serde".
|
||||||
|
//! **Important**: `serde` encoding is **not** the same as consensus
|
||||||
|
//! encoding!
|
||||||
//!
|
//!
|
||||||
//! Where possible, the bindings use the Rust type system to ensure that
|
//! Where possible, the bindings use the Rust type system to ensure that
|
||||||
//! API usage errors are impossible. For example, the library uses context
|
//! API usage errors are impossible. For example, the library uses context
|
||||||
|
@ -125,9 +129,7 @@
|
||||||
//! * `rand-std` - use `rand` library with its `std` feature enabled. (Implies `rand`.)
|
//! * `rand-std` - use `rand` library with its `std` feature enabled. (Implies `rand`.)
|
||||||
//! * `recovery` - enable functions that can compute the public key from signature.
|
//! * `recovery` - enable functions that can compute the public key from signature.
|
||||||
//! * `lowmemory` - optimize the library for low-memory environments.
|
//! * `lowmemory` - optimize the library for low-memory environments.
|
||||||
//! * `global-context` - enable use of global secp256k1 context. (Implies `std`, `rand-std` and
|
//! * `global-context` - enable use of global secp256k1 context (implies `std`).
|
||||||
//! `global-context-less-secure`.)
|
|
||||||
//! * `global-context-less-secure` - enables global context without extra sidechannel protection.
|
|
||||||
//! * `serde` - implements serialization and deserialization for types in this crate using `serde`.
|
//! * `serde` - implements serialization and deserialization for types in this crate using `serde`.
|
||||||
//! **Important**: `serde` encoding is **not** the same as consensus encoding!
|
//! **Important**: `serde` encoding is **not** the same as consensus encoding!
|
||||||
//! * `bitcoin_hashes` - enables interaction with the `bitcoin-hashes` crate (e.g. conversions).
|
//! * `bitcoin_hashes` - enables interaction with the `bitcoin-hashes` crate (e.g. conversions).
|
||||||
|
@ -195,8 +197,8 @@ use core::marker::PhantomData;
|
||||||
use core::{mem, fmt, str};
|
use core::{mem, fmt, str};
|
||||||
use ffi::{CPtr, types::AlignedType};
|
use ffi::{CPtr, types::AlignedType};
|
||||||
|
|
||||||
#[cfg(feature = "global-context-less-secure")]
|
#[cfg(feature = "global-context")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "global-context", feature = "global-context-less-secure"))))]
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "global-context", feature = "global-context"))))]
|
||||||
pub use context::global::SECP256K1;
|
pub use context::global::SECP256K1;
|
||||||
|
|
||||||
#[cfg(feature = "bitcoin_hashes")]
|
#[cfg(feature = "bitcoin_hashes")]
|
||||||
|
@ -955,7 +957,7 @@ mod tests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "global-context-less-secure")]
|
#[cfg(feature = "global-context")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_global_context() {
|
fn test_global_context() {
|
||||||
use super::SECP256K1;
|
use super::SECP256K1;
|
||||||
|
|
Loading…
Reference in New Issue