From 10e779f53ae114aa2afada4d7eeb6beeafed7dd8 Mon Sep 17 00:00:00 2001 From: Tobin Harding Date: Wed, 26 Jan 2022 10:32:13 +1100 Subject: [PATCH] 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 --- examples/generate_keys.rs | 2 +- src/context.rs | 86 ++++++++++++++++++++++++++++++--------- src/lib.rs | 11 +++-- 3 files changed, 74 insertions(+), 25 deletions(-) diff --git a/examples/generate_keys.rs b/examples/generate_keys.rs index ae1ec2b84..34a34e8b6 100644 --- a/examples/generate_keys.rs +++ b/examples/generate_keys.rs @@ -4,7 +4,7 @@ use secp256k1::rand::rngs::OsRng; use secp256k1::{PublicKey, Secp256k1, SecretKey}; fn main() { - let secp = Secp256k1::new(); + let secp = Secp256k1::new(SideChannelProtection::Randomize); let mut rng = OsRng::new().unwrap(); // First option: let (seckey, pubkey) = secp.generate_keypair(&mut rng); diff --git a/src/context.rs b/src/context.rs index f89bbe96d..e37695102 100644 --- a/src/context.rs +++ b/src/context.rs @@ -29,17 +29,18 @@ pub mod global { /// A global, static context to avoid repeatedly creating contexts where one can't be passed /// /// If the global-context feature is enabled (and not just the global-context-less-secure), - /// this will have been randomized. + /// this will have been randomized for additional defense-in-depth side channel protection. pub static SECP256K1: &GlobalContext = &GlobalContext { __private: () }; impl Deref for GlobalContext { type Target = Secp256k1; + #[allow(unused_mut)] // mut is unused when "global-context" is not enabled. fn deref(&self) -> &Self::Target { static ONCE: Once = Once::new(); static mut CONTEXT: Option> = None; ONCE.call_once(|| unsafe { - let mut ctx = Secp256k1::new(); + let mut ctx = Secp256k1::new(SideChannelProtection::NoRandomize); #[cfg(feature = "global-context")] { ctx.randomize(&mut rand::thread_rng()); @@ -166,46 +167,57 @@ mod alloc_only { } impl Secp256k1 { - /// Lets you create a context in a generic manner(sign/verify/all) - pub fn gen_new() -> Secp256k1 { + /// Lets you create a context in a generic manner(sign/verify/all). + /// + /// If the `rand` feature is enabled we randomize the context using `thread_rng`. + pub fn gen_new(opt: SideChannelProtection) -> Secp256k1 { #[cfg(target_arch = "wasm32")] ffi::types::sanity_checks_for_wasm(); let size = unsafe { ffi::secp256k1_context_preallocated_size(C::FLAGS) }; let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); let ptr = unsafe {alloc::alloc(layout)}; - Secp256k1 { + let mut secp = Secp256k1 { ctx: unsafe { ffi::secp256k1_context_preallocated_create(ptr as *mut c_void, C::FLAGS) }, phantom: PhantomData, size, + }; + + match opt { + SideChannelProtection::SeededRandomize(seed) => secp.seeded_randomize(seed), + SideChannelProtection::NoRandomize => {}, + #[cfg(feature = "rand")] + SideChannelProtection::Randomize => secp.randomize(&mut rand::thread_rng()), } + + secp } } impl Secp256k1 { - /// Creates a new Secp256k1 context with all capabilities - pub fn new() -> Secp256k1 { - Secp256k1::gen_new() + /// Creates a new Secp256k1 context with all capabilities. + /// + /// If the `rand` feature is enabled we randomize the context using `thread_rng`. + pub fn new(opt: SideChannelProtection) -> Secp256k1 { + Secp256k1::gen_new(opt) } } impl Secp256k1 { - /// Creates a new Secp256k1 context that can only be used for signing - pub fn signing_only() -> Secp256k1 { - Secp256k1::gen_new() + /// Creates a new Secp256k1 context that can only be used for signing. + /// + /// If the `rand` feature is enabled we randomize the context using `thread_rng`. + pub fn signing_only(opt: SideChannelProtection) -> Secp256k1 { + Secp256k1::gen_new(opt) } } impl Secp256k1 { - /// Creates a new Secp256k1 context that can only be used for verification - pub fn verification_only() -> Secp256k1 { - Secp256k1::gen_new() - } - } - - impl Default for Secp256k1 { - fn default() -> Self { - Self::new() + /// Creates a new Secp256k1 context that can only be used for verification. + /// + /// If the `rand` feature is enabled we randomize the context using `thread_rng`. + pub fn verification_only(opt: SideChannelProtection) -> Secp256k1 { + Secp256k1::gen_new(opt) } } @@ -221,6 +233,40 @@ mod alloc_only { } } } + + /// When initializing the context we support additional defense-in-depth side channel + /// protection, which re-blinds certain operations on secret key data. + /// + /// # Examples + /// + /// If your application already depends on `rand` and/or you do not wish to use the re-exported + /// secp2561 version of `rand` i.e., no transient dependency on `rand`. + ///``` + /// use rand::thread_rng(); + /// use secp256k1::Secp256k1; + /// + /// let mut rng = thread_rng().unwrap(); + /// let mut seed = [0u8; 32]; + /// rng.fill_bytes(&mut seed); + /// + /// let _ = Secp256k1::new(SeededRandomize(&seed)); + /// ``` + /// If you are ok with adding the transient dependency on `rand` from secp256k1. + /// ``` + /// use secp256k1::Secp256k1; + /// + /// let _ = Secp256k1::new(Randomize); + /// ``` + #[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] + pub enum SideChannelProtection<'a> { // FIXME: Does this name overstate the side channel risk? + /// Uses seed to randomize context (calls `seeded_randomize(seed)`). + SeededRandomize(&'a [u8; 32]), + /// Disables randomization (additional defense-in-depth sidechannel protection). + NoRandomize, + /// Uses thread_rng to randomize context (calls `secp.randomize()`). + #[cfg(feature = "rand")] + Randomize, + } } impl<'buf> Signing for SignOnlyPreallocated<'buf> {} diff --git a/src/lib.rs b/src/lib.rs index de38e3970..4f0dab9fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,9 +20,12 @@ //! and its derivatives. //! //! To minimize dependencies, some functions are feature-gated. To generate -//! random keys or to re-randomize a context object, compile with the "rand" -//! feature. To de/serialize objects with serde, compile with "serde". -//! **Important**: `serde` encoding is **not** the same as consensus encoding! +//! random keys or to re-randomize a context object, compile with the `rand` +//! feature. If you are willing to use the `rand` dependency, we have enabled an +//! additional defense-in-depth side channel 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 //! API usage errors are impossible. For example, the library uses context @@ -117,7 +120,7 @@ //! //! * `std` - use standard Rust library, enabled by default. //! * `alloc` - use the `alloc` standard Rust library to provide heap allocations. -//! * `rand` - use `rand` library to provide random generator (e.g. to generate keys). +//! * `rand` - use `rand` library to provide randomness (e.g. to randomize contexts). //! * `rand-std` - use `rand` library with its `std` feature enabled. (Implies `rand`.) //! * `recovery` - enable functions that can compute the public key from signature. //! * `lowmemory` - optimize the library for low-memory environments.