Skip to content

Commit 86447ee

Browse files
committed
Merge #385: Randomize context on creation
8339ca5 Add documentation guiding users towards randomization (Tobin Harding) cf1496b Add documentation about rand-std feature (Tobin Harding) 1693d51 Randomize context on creation (Tobin Harding) a0465ea 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](#388 (comment)) 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: ACK 8339ca5 Tree-SHA512: e74fe9a6eaf8ac40e4e06997602006eb8ca95216b5bc6dca3f5f96b5b4d3bf8610d851d8f1ef5c199ab7fbe85b34d162f2ee0073647f45105a486d20d8c0722a
2 parents 7a3736a + 8339ca5 commit 86447ee

File tree

4 files changed

+73
-27
lines changed

4 files changed

+73
-27
lines changed

Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ alloc = []
2626
rand-std = ["rand/std"]
2727
recovery = ["secp256k1-sys/recovery"]
2828
lowmemory = ["secp256k1-sys/lowmemory"]
29-
global-context = ["std", "rand-std", "global-context-less-secure"]
30-
global-context-less-secure = []
29+
global-context = ["std"]
3130

3231
[dependencies]
3332
secp256k1-sys = { version = "0.4.2", default-features = false, path = "./secp256k1-sys" }

src/context.rs

+58-13
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ use Secp256k1;
99
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
1010
pub use self::alloc_only::*;
1111

12-
#[cfg(all(feature = "global-context-less-secure", feature = "std"))]
13-
#[cfg_attr(docsrs, doc(cfg(any(feature = "global-context", feature = "global-context-less-secure"))))]
12+
#[cfg(all(feature = "global-context", feature = "std"))]
13+
#[cfg_attr(docsrs, doc(cfg(all(feature = "global-context", feature = "std"))))]
1414
/// Module implementing a singleton pattern for a global `Secp256k1` context
1515
pub mod global {
16-
#[cfg(feature = "global-context")]
16+
#[cfg(feature = "rand-std")]
1717
use rand;
1818

1919
use std::ops::Deref;
@@ -26,22 +26,29 @@ pub mod global {
2626
__private: (),
2727
}
2828

29-
/// A global, static context to avoid repeatedly creating contexts where one can't be passed
29+
/// A global static context to avoid repeatedly creating contexts.
3030
///
31-
/// If the global-context feature is enabled (and not just the global-context-less-secure),
32-
/// this will have been randomized.
31+
/// If `rand-std` feature is enabled, context will have been randomized using `thread_rng`.
32+
///
33+
/// ```
34+
/// # #[cfg(all(feature = "global-context", feature = "rand-std"))] {
35+
/// use secp256k1::{PublicKey, SECP256K1};
36+
/// use secp256k1::rand::thread_rng;
37+
/// let _ = SECP256K1.generate_keypair(&mut thread_rng());
38+
/// # }
39+
/// ```
3340
pub static SECP256K1: &GlobalContext = &GlobalContext { __private: () };
3441

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

38-
#[allow(unused_mut)] // Unused when "global-context" is not enabled.
45+
#[allow(unused_mut)] // Unused when `rand-std` is not enabled.
3946
fn deref(&self) -> &Self::Target {
4047
static ONCE: Once = Once::new();
4148
static mut CONTEXT: Option<Secp256k1<All>> = None;
4249
ONCE.call_once(|| unsafe {
4350
let mut ctx = Secp256k1::new();
44-
#[cfg(feature = "global-context")]
51+
#[cfg(feature = "rand-std")]
4552
{
4653
ctx.randomize(&mut rand::thread_rng());
4754
}
@@ -108,6 +115,9 @@ mod alloc_only {
108115
#[cfg(not(feature = "std"))]
109116
use alloc::alloc;
110117

118+
#[cfg(feature = "rand-std")]
119+
use rand;
120+
111121
impl private::Sealed for SignOnly {}
112122
impl private::Sealed for All {}
113123
impl private::Sealed for VerifyOnly {}
@@ -167,38 +177,73 @@ mod alloc_only {
167177
}
168178

169179
impl<C: Context> Secp256k1<C> {
170-
/// Lets you create a context in a generic manner(sign/verify/all)
180+
/// Lets you create a context in a generic manner (sign/verify/all).
181+
///
182+
/// If `rand-std` feature is enabled, context will have been randomized using `thread_rng`.
183+
/// If `rand-std` feature is not enabled please consider randomizing the context as follows:
184+
/// ```
185+
/// # #[cfg(all(feature = "rand-std", any(feature = "alloc", feature = "std")))] {
186+
/// # use secp256k1::Secp256k1;
187+
/// # use secp256k1::rand::{thread_rng, RngCore};
188+
/// let mut ctx = Secp256k1::new();
189+
/// # let mut rng = thread_rng();
190+
/// # let mut seed = [0u8; 32];
191+
/// # rng.fill_bytes(&mut seed);
192+
/// // let seed = <32 bytes of random data>
193+
/// ctx.seeded_randomize(&seed);
194+
/// # }
195+
/// ```
196+
#[allow(unused_mut)] // Unused when `rand-std` is not enabled.
171197
pub fn gen_new() -> Secp256k1<C> {
172198
#[cfg(target_arch = "wasm32")]
173199
ffi::types::sanity_checks_for_wasm();
174200

175201
let size = unsafe { ffi::secp256k1_context_preallocated_size(C::FLAGS) };
176202
let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap();
177203
let ptr = unsafe {alloc::alloc(layout)};
178-
Secp256k1 {
204+
let mut ctx = Secp256k1 {
179205
ctx: unsafe { ffi::secp256k1_context_preallocated_create(ptr as *mut c_void, C::FLAGS) },
180206
phantom: PhantomData,
181207
size,
208+
};
209+
210+
#[cfg(feature = "rand-std")]
211+
{
212+
ctx.randomize(&mut rand::thread_rng());
182213
}
214+
215+
ctx
183216
}
184217
}
185218

186219
impl Secp256k1<All> {
187-
/// Creates a new Secp256k1 context with all capabilities
220+
/// Creates a new Secp256k1 context with all capabilities.
221+
///
222+
/// If `rand-std` feature is enabled, context will have been randomized using `thread_rng`.
223+
/// If `rand-std` feature is not enabled please consider randomizing the context (see docs
224+
/// for `Secp256k1::gen_new()`).
188225
pub fn new() -> Secp256k1<All> {
189226
Secp256k1::gen_new()
190227
}
191228
}
192229

193230
impl Secp256k1<SignOnly> {
194-
/// Creates a new Secp256k1 context that can only be used for signing
231+
/// Creates a new Secp256k1 context that can only be used for signing.
232+
///
233+
/// If `rand-std` feature is enabled, context will have been randomized using `thread_rng`.
234+
/// If `rand-std` feature is not enabled please consider randomizing the context (see docs
235+
/// for `Secp256k1::gen_new()`).
195236
pub fn signing_only() -> Secp256k1<SignOnly> {
196237
Secp256k1::gen_new()
197238
}
198239
}
199240

200241
impl Secp256k1<VerifyOnly> {
201-
/// Creates a new Secp256k1 context that can only be used for verification
242+
/// Creates a new Secp256k1 context that can only be used for verification.
243+
///
244+
/// If `rand-std` feature is enabled, context will have been randomized using `thread_rng`.
245+
/// If `rand-std` feature is not enabled please consider randomizing the context (see docs
246+
/// for `Secp256k1::gen_new()`).
202247
pub fn verification_only() -> Secp256k1<VerifyOnly> {
203248
Secp256k1::gen_new()
204249
}

src/key.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ impl Ord for PublicKey {
641641
/// feature active. This is due to security considerations, see the [`serde_keypair`] documentation
642642
/// for details.
643643
///
644-
/// If the `serde` and `global-context[-less-secure]` features are active `KeyPair`s can be serialized and
644+
/// If the `serde` and `global-context` features are active `KeyPair`s can be serialized and
645645
/// deserialized by annotating them with `#[serde(with = "secp256k1::serde_keypair")]`
646646
/// inside structs or enums for which [`Serialize`] and [`Deserialize`] are being derived.
647647
///
@@ -1320,7 +1320,7 @@ impl<'de> ::serde::Deserialize<'de> for XOnlyPublicKey {
13201320
///
13211321
/// [`SecretKey`]: crate::SecretKey
13221322
/// [global context]: crate::SECP256K1
1323-
#[cfg(all(feature = "global-context-less-secure", feature = "serde"))]
1323+
#[cfg(all(feature = "global-context", feature = "serde"))]
13241324
pub mod serde_keypair {
13251325
use serde::{Deserialize, Deserializer, Serialize, Serializer};
13261326
use key::KeyPair;
@@ -1924,7 +1924,7 @@ mod test {
19241924
}
19251925

19261926
#[test]
1927-
#[cfg(all(feature = "global-context-less-secure", feature = "serde"))]
1927+
#[cfg(all(feature = "global-context", feature = "serde"))]
19281928
fn test_serde_keypair() {
19291929
use serde::{Deserialize, Deserializer, Serialize, Serializer};
19301930
use serde_test::{Configure, Token, assert_tokens};

src/lib.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@
2020
//! and its derivatives.
2121
//!
2222
//! To minimize dependencies, some functions are feature-gated. To generate
23-
//! random keys or to re-randomize a context object, compile with the "rand"
24-
//! feature. To de/serialize objects with serde, compile with "serde".
25-
//! **Important**: `serde` encoding is **not** the same as consensus encoding!
23+
//! random keys or to re-randomize a context object, compile with the
24+
//! `rand-std` feature. If you are willing to use the `rand-std` feature, we
25+
//! have enabled an additional defense-in-depth sidechannel protection for
26+
//! our context objects, which re-blinds certain operations on secret key
27+
//! data. To de/serialize objects with serde, compile with "serde".
28+
//! **Important**: `serde` encoding is **not** the same as consensus
29+
//! encoding!
2630
//!
2731
//! Where possible, the bindings use the Rust type system to ensure that
2832
//! API usage errors are impossible. For example, the library uses context
@@ -125,9 +129,7 @@
125129
//! * `rand-std` - use `rand` library with its `std` feature enabled. (Implies `rand`.)
126130
//! * `recovery` - enable functions that can compute the public key from signature.
127131
//! * `lowmemory` - optimize the library for low-memory environments.
128-
//! * `global-context` - enable use of global secp256k1 context. (Implies `std`, `rand-std` and
129-
//! `global-context-less-secure`.)
130-
//! * `global-context-less-secure` - enables global context without extra sidechannel protection.
132+
//! * `global-context` - enable use of global secp256k1 context (implies `std`).
131133
//! * `serde` - implements serialization and deserialization for types in this crate using `serde`.
132134
//! **Important**: `serde` encoding is **not** the same as consensus encoding!
133135
//! * `bitcoin_hashes` - enables interaction with the `bitcoin-hashes` crate (e.g. conversions).
@@ -195,8 +197,8 @@ use core::marker::PhantomData;
195197
use core::{mem, fmt, str};
196198
use ffi::{CPtr, types::AlignedType};
197199

198-
#[cfg(feature = "global-context-less-secure")]
199-
#[cfg_attr(docsrs, doc(cfg(any(feature = "global-context", feature = "global-context-less-secure"))))]
200+
#[cfg(feature = "global-context")]
201+
#[cfg_attr(docsrs, doc(cfg(any(feature = "global-context", feature = "global-context"))))]
200202
pub use context::global::SECP256K1;
201203

202204
#[cfg(feature = "bitcoin_hashes")]
@@ -955,7 +957,7 @@ mod tests {
955957

956958
}
957959

958-
#[cfg(feature = "global-context-less-secure")]
960+
#[cfg(feature = "global-context")]
959961
#[test]
960962
fn test_global_context() {
961963
use super::SECP256K1;

0 commit comments

Comments
 (0)