Skip to content

Commit

Permalink
Randomize context on creation
Browse files Browse the repository at this point in the history
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: rust-bitcoin#225
  • Loading branch information
tcharding committed Jan 25, 2022
1 parent 86baad5 commit 10e779f
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 25 deletions.
2 changes: 1 addition & 1 deletion examples/generate_keys.rs
Expand Up @@ -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);
Expand Down
86 changes: 66 additions & 20 deletions src/context.rs
Expand Up @@ -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<All>;

#[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<Secp256k1<All>> = 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());
Expand Down Expand Up @@ -166,46 +167,57 @@ mod alloc_only {
}

impl<C: Context> Secp256k1<C> {
/// Lets you create a context in a generic manner(sign/verify/all)
pub fn gen_new() -> Secp256k1<C> {
/// 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<C> {
#[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<All> {
/// Creates a new Secp256k1 context with all capabilities
pub fn new() -> Secp256k1<All> {
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<All> {
Secp256k1::gen_new(opt)
}
}

impl Secp256k1<SignOnly> {
/// Creates a new Secp256k1 context that can only be used for signing
pub fn signing_only() -> Secp256k1<SignOnly> {
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<SignOnly> {
Secp256k1::gen_new(opt)
}
}

impl Secp256k1<VerifyOnly> {
/// Creates a new Secp256k1 context that can only be used for verification
pub fn verification_only() -> Secp256k1<VerifyOnly> {
Secp256k1::gen_new()
}
}

impl Default for Secp256k1<All> {
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<VerifyOnly> {
Secp256k1::gen_new(opt)
}
}

Expand All @@ -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> {}
Expand Down
11 changes: 7 additions & 4 deletions src/lib.rs
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 10e779f

Please sign in to comment.