Skip to content

Commit 10128c8

Browse files
committed
Randomize context on creation
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. We can better assist users by making APIs that are hard to mis-use. Add an enum to the context constructor that forces the use to make a decision on the additional side channel attack protection we provide. This makes the API a little harder to use because users must now read the docs but this cost offsets the likely-hood of users ignoring this important feature. This is quite an invasive change because every user of the secp256k1 library will have to update the context constructor call sites and read what this enum does. Is this worth it? Resolves: #225
1 parent 6e7035b commit 10128c8

File tree

3 files changed

+74
-25
lines changed

3 files changed

+74
-25
lines changed

examples/generate_keys.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use secp256k1::rand::rngs::OsRng;
44
use secp256k1::{PublicKey, Secp256k1, SecretKey};
55

66
fn main() {
7-
let secp = Secp256k1::new();
7+
let secp = Secp256k1::new(SideChannelProtection::Randomize);
88
let mut rng = OsRng::new().unwrap();
99
// First option:
1010
let (seckey, pubkey) = secp.generate_keypair(&mut rng);

src/context.rs

+66-20
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,18 @@ pub mod global {
2929
/// A global, static context to avoid repeatedly creating contexts where one can't be passed
3030
///
3131
/// If the global-context feature is enabled (and not just the global-context-less-secure),
32-
/// this will have been randomized.
32+
/// this will have been randomized for additional defense-in-depth side channel protection.
3333
pub static SECP256K1: &GlobalContext = &GlobalContext { __private: () };
3434

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

38+
#[allow(unused_mut)] // mut is unused when "global-context" is not enabled.
3839
fn deref(&self) -> &Self::Target {
3940
static ONCE: Once = Once::new();
4041
static mut CONTEXT: Option<Secp256k1<All>> = None;
4142
ONCE.call_once(|| unsafe {
42-
let mut ctx = Secp256k1::new();
43+
let mut ctx = Secp256k1::new(SideChannelProtection::NoRandomize);
4344
#[cfg(feature = "global-context")]
4445
{
4546
ctx.randomize(&mut rand::thread_rng());
@@ -166,46 +167,57 @@ mod alloc_only {
166167
}
167168

168169
impl<C: Context> Secp256k1<C> {
169-
/// Lets you create a context in a generic manner(sign/verify/all)
170-
pub fn gen_new() -> Secp256k1<C> {
170+
/// Lets you create a context in a generic manner(sign/verify/all).
171+
///
172+
/// If the `rand` feature is enabled we randomize the context using `thread_rng`.
173+
pub fn gen_new(opt: SideChannelProtection) -> Secp256k1<C> {
171174
#[cfg(target_arch = "wasm32")]
172175
ffi::types::sanity_checks_for_wasm();
173176

174177
let size = unsafe { ffi::secp256k1_context_preallocated_size(C::FLAGS) };
175178
let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap();
176179
let ptr = unsafe {alloc::alloc(layout)};
177-
Secp256k1 {
180+
let mut secp = Secp256k1 {
178181
ctx: unsafe { ffi::secp256k1_context_preallocated_create(ptr as *mut c_void, C::FLAGS) },
179182
phantom: PhantomData,
180183
size,
184+
};
185+
186+
match opt {
187+
SideChannelProtection::SeededRandomize(seed) => secp.seeded_randomize(seed),
188+
SideChannelProtection::NoRandomize => {},
189+
#[cfg(feature = "rand")]
190+
SideChannelProtection::Randomize => secp.randomize(&mut rand::thread_rng()),
181191
}
192+
193+
secp
182194
}
183195
}
184196

185197
impl Secp256k1<All> {
186-
/// Creates a new Secp256k1 context with all capabilities
187-
pub fn new() -> Secp256k1<All> {
188-
Secp256k1::gen_new()
198+
/// Creates a new Secp256k1 context with all capabilities.
199+
///
200+
/// If the `rand` feature is enabled we randomize the context using `thread_rng`.
201+
pub fn new(opt: SideChannelProtection) -> Secp256k1<All> {
202+
Secp256k1::gen_new(opt)
189203
}
190204
}
191205

192206
impl Secp256k1<SignOnly> {
193-
/// Creates a new Secp256k1 context that can only be used for signing
194-
pub fn signing_only() -> Secp256k1<SignOnly> {
195-
Secp256k1::gen_new()
207+
/// Creates a new Secp256k1 context that can only be used for signing.
208+
///
209+
/// If the `rand` feature is enabled we randomize the context using `thread_rng`.
210+
pub fn signing_only(opt: SideChannelProtection) -> Secp256k1<SignOnly> {
211+
Secp256k1::gen_new(opt)
196212
}
197213
}
198214

199215
impl Secp256k1<VerifyOnly> {
200-
/// Creates a new Secp256k1 context that can only be used for verification
201-
pub fn verification_only() -> Secp256k1<VerifyOnly> {
202-
Secp256k1::gen_new()
203-
}
204-
}
205-
206-
impl Default for Secp256k1<All> {
207-
fn default() -> Self {
208-
Self::new()
216+
/// Creates a new Secp256k1 context that can only be used for verification.
217+
///
218+
/// If the `rand` feature is enabled we randomize the context using `thread_rng`.
219+
pub fn verification_only(opt: SideChannelProtection) -> Secp256k1<VerifyOnly> {
220+
Secp256k1::gen_new(opt)
209221
}
210222
}
211223

@@ -221,6 +233,40 @@ mod alloc_only {
221233
}
222234
}
223235
}
236+
237+
/// When initializing the context we support additional defense-in-depth side channel
238+
/// protection, which re-blinds certain operations on secret key data.
239+
///
240+
/// # Examples
241+
///
242+
/// If your application already depends on `rand` and/or you do not wish to use the re-exported
243+
/// secp2561 version of `rand` i.e., no transient dependency on `rand`.
244+
///```
245+
/// use rand::thread_rng();
246+
/// use secp256k1::Secp256k1;
247+
///
248+
/// let mut rng = thread_rng().unwrap();
249+
/// let mut seed = [0u8; 32];
250+
/// rng.fill_bytes(&mut seed);
251+
///
252+
/// let _ = Secp256k1::new(SeededRandomize(&seed));
253+
/// ```
254+
/// If you are ok with adding the transient dependency on `rand` from secp256k1.
255+
/// ```
256+
/// use secp256k1::Secp256k1;
257+
///
258+
/// let _ = Secp256k1::new(Randomize);
259+
/// ```
260+
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
261+
pub enum SideChannelProtection<'a> { // FIXME: Does this name overstate the side channel risk?
262+
/// Uses seed to randomize context (calls `seeded_randomize(seed)`).
263+
SeededRandomize(&'a [u8; 32]),
264+
/// Disables randomization (additional defense-in-depth sidechannel protection).
265+
NoRandomize,
266+
/// Uses thread_rng to randomize context (calls `secp.randomize()`).
267+
#[cfg(feature = "rand")]
268+
Randomize,
269+
}
224270
}
225271

226272
impl<'buf> Signing for SignOnlyPreallocated<'buf> {}

src/lib.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@
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 `rand`
24+
//! feature. If you are willing to use the `rand` dependency, we have enabled an
25+
//! additional defense-in-depth side channel protection for our context objects,
26+
//! which re-blinds certain operations on secret key data. To de/serialize
27+
//! objects with serde, compile with "serde". **Important**: `serde` encoding is
28+
//! **not** the same as consensus encoding!
2629
//!
2730
//! Where possible, the bindings use the Rust type system to ensure that
2831
//! API usage errors are impossible. For example, the library uses context
@@ -117,7 +120,7 @@
117120
//!
118121
//! * `std` - use standard Rust library, enabled by default.
119122
//! * `alloc` - use the `alloc` standard Rust library to provide heap allocations.
120-
//! * `rand` - use `rand` library to provide random generator (e.g. to generate keys).
123+
//! * `rand` - use `rand` library to provide randomness (e.g. to randomize contexts).
121124
//! * `rand-std` - use `rand` library with its `std` feature enabled. (Implies `rand`.)
122125
//! * `recovery` - enable functions that can compute the public key from signature.
123126
//! * `lowmemory` - optimize the library for low-memory environments.

0 commit comments

Comments
 (0)