From 3d33f24885dd304ba22c25b91ce2f7748a9e3996 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 functions that make explicit the randomization of the context during construction. 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 --- Cargo.toml | 5 +- examples/generate_keys.rs | 2 +- examples/sign_verify.rs | 2 +- examples/sign_verify_recovery.rs | 2 +- no_std_test/src/main.rs | 2 +- src/context.rs | 121 ++++++++++++++++++++++++++----- src/ecdh.rs | 24 ++++-- src/ecdsa/mod.rs | 8 +- src/ecdsa/recovery.rs | 25 ++++--- src/key.rs | 78 ++++++++++---------- src/lib.rs | 65 +++++++++-------- src/schnorr.rs | 41 ++++++++--- src/secret.rs | 2 +- 13 files changed, 251 insertions(+), 126 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e50c84392..279200d57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,14 +49,15 @@ rand = { version = "0.6", features = ["wasm-bindgen"] } [[example]] name = "sign_verify_recovery" -required-features = ["recovery"] +required-features = ["recovery", "alloc"] [[example]] name = "sign_verify" +required-features = ["alloc"] [[example]] name = "generate_keys" -required-features = ["rand"] +required-features = ["alloc", "rand-std"] [workspace] members = ["secp256k1-sys"] diff --git a/examples/generate_keys.rs b/examples/generate_keys.rs index ae1ec2b84..f605b58a7 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_randomize(); let mut rng = OsRng::new().unwrap(); // First option: let (seckey, pubkey) = secp.generate_keypair(&mut rng); diff --git a/examples/sign_verify.rs b/examples/sign_verify.rs index 20dfde303..ff186f1df 100644 --- a/examples/sign_verify.rs +++ b/examples/sign_verify.rs @@ -21,7 +21,7 @@ fn sign(secp: &Secp256k1, msg: &[u8], seckey: [u8; 32]) -> Result } fn main() { - let secp = Secp256k1::new(); + let secp = Secp256k1::new_no_randomize(); let seckey = [59, 148, 11, 85, 134, 130, 61, 253, 2, 174, 59, 70, 27, 180, 51, 107, 94, 203, 174, 253, 102, 39, 170, 146, 46, 252, 4, 143, 236, 12, 136, 28]; let pubkey = [2, 29, 21, 35, 7, 198, 183, 43, 14, 208, 65, 139, 14, 112, 205, 128, 231, 245, 41, 91, 141, 134, 245, 114, 45, 63, 82, 19, 251, 210, 57, 79, 54]; diff --git a/examples/sign_verify_recovery.rs b/examples/sign_verify_recovery.rs index 627bbdc13..5afca7845 100644 --- a/examples/sign_verify_recovery.rs +++ b/examples/sign_verify_recovery.rs @@ -22,7 +22,7 @@ fn sign_recovery(secp: &Secp256k1, msg: &[u8], seckey: [u8; 32]) } fn main() { - let secp = Secp256k1::new(); + let secp = Secp256k1::new_no_randomize(); let seckey = [ 59, 148, 11, 85, 134, 130, 61, 253, 2, 174, 59, 70, 27, 180, 51, 107, diff --git a/no_std_test/src/main.rs b/no_std_test/src/main.rs index d245ce9dd..0a7e3aa04 100644 --- a/no_std_test/src/main.rs +++ b/no_std_test/src/main.rs @@ -135,7 +135,7 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize { #[cfg(feature = "alloc")] { - let secp_alloc = Secp256k1::new(); + let secp_alloc = Secp256k1::new_no_randomize(); let public_key = PublicKey::from_secret_key(&secp_alloc, &secret_key); let message = Message::from_slice(&[0xab; 32]).expect("32 bytes"); diff --git a/src/context.rs b/src/context.rs index 755ba8a48..f11dfafee 100644 --- a/src/context.rs +++ b/src/context.rs @@ -29,7 +29,7 @@ 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 { @@ -40,7 +40,7 @@ pub mod global { static ONCE: Once = Once::new(); static mut CONTEXT: Option> = None; ONCE.call_once(|| unsafe { - let mut ctx = Secp256k1::new(); + let mut ctx = Secp256k1::new_no_randomize(); #[cfg(feature = "global-context")] { ctx.randomize(&mut rand::thread_rng()); @@ -167,8 +167,8 @@ mod alloc_only { } impl Secp256k1 { - /// Lets you create a context in a generic manner(sign/verify/all) - pub fn gen_new() -> Secp256k1 { + /// Helper function only intended to be called by other gen_new_* functions. + fn _gen_new() -> Secp256k1 { #[cfg(target_arch = "wasm32")] ffi::types::sanity_checks_for_wasm(); @@ -181,32 +181,119 @@ mod alloc_only { size, } } + + /// Lets you create a context in a generic manner(sign/verify/all). + /// + /// Context is randomized using `thread_rng` for additional defense-in-depth side channel + /// protection. + #[cfg(feature = "rand-std")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand-std")))] + pub fn gen_new_randomize() -> Secp256k1 { + use rand::thread_rng; + let mut secp = Secp256k1::_gen_new(); + secp.randomize(&mut thread_rng()); + secp + } + + /// Lets you create a context in a generic manner(sign/verify/all). + /// + /// No randomization is done, this context is perfectly safe but for additional + /// defense-in-depth side channel protection consider using [`gen_new_seeded_randomize`]. + pub fn gen_new_no_randomize() -> Secp256k1 { + Secp256k1::_gen_new() + } + + /// Lets you create a context in a generic manner(sign/verify/all). + /// + /// Context is randomized using `seed` for additional defense-in-depth side channel + /// protection. + pub fn gen_new_seeded_randomize(seed: &[u8; 32]) -> Secp256k1 { + let mut secp = Secp256k1::_gen_new(); + secp.seeded_randomize(seed); + 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. + /// + /// Context is randomized using `thread_rng` for additional defense-in-depth side channel + /// protection. + #[cfg(feature = "rand-std")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand-std")))] + pub fn new_randomize() -> Secp256k1 { + Secp256k1::gen_new_randomize() + } + /// Creates a new Secp256k1 context with all capabilities. + /// + /// No randomization is done, this context is perfectly safe but for additional + /// defense-in-depth side channel protection consider using [`new_seeded_randomize`]. + pub fn new_no_randomize() -> Secp256k1 { + Secp256k1::gen_new_no_randomize() + } + + /// Creates a new Secp256k1 context with all capabilities. + /// + /// Context is randomized using `seed` for additional defense-in-depth side channel + /// protection. + pub fn new_seeded_randomize(seed: &[u8; 32]) -> Secp256k1 { + Secp256k1::gen_new_seeded_randomize(seed) } } 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. + /// + /// Context is randomized using `thread_rng` for additional defense-in-depth side channel + /// protection. + #[cfg(feature = "rand-std")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand-std")))] + pub fn signing_only_randomize() -> Secp256k1 { + Secp256k1::gen_new_randomize() + } + + /// Creates a new Secp256k1 context that can only be used for signing. + /// + /// No randomization is done, this context is perfectly safe but for additional + /// defense-in-depth side channel protection consider using [`signing_only_seeded_randomize`]. + pub fn signing_only_no_randomize() -> Secp256k1 { + Secp256k1::gen_new_no_randomize() + } + + /// Creates a new Secp256k1 context that can only be used for signing. + /// + /// Context is randomized using `seed` for additional defense-in-depth side channel + /// protection. + pub fn signing_only_seeded_randomize(seed: &[u8; 32]) -> Secp256k1 { + Secp256k1::gen_new_seeded_randomize(seed) } } impl Secp256k1 { - /// Creates a new Secp256k1 context that can only be used for verification - pub fn verification_only() -> Secp256k1 { - Secp256k1::gen_new() + /// Creates a new Secp256k1 context that can only be used for verifying. + /// + /// Context is randomized using `thread_rng` for additional defense-in-depth side channel + /// protection. + #[cfg(feature = "rand-std")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand-std")))] + pub fn verification_only_randomize() -> Secp256k1 { + Secp256k1::gen_new_randomize() + } + + /// Creates a new Secp256k1 context that can only be used for verifying. + /// + /// No randomization is done, this context is perfectly safe but for additional + /// defense-in-depth side channel protection consider using [`verification_only_seeded_randomize`]. + pub fn verification_only_no_randomize() -> Secp256k1 { + Secp256k1::gen_new_no_randomize() } - } - impl Default for Secp256k1 { - fn default() -> Self { - Self::new() + /// Creates a new Secp256k1 context that can only be used for verifying. + /// + /// Context is randomized using `seed` for additional defense-in-depth side channel + /// protection. + pub fn verification_only_seeded_randomize(seed: &[u8; 32]) -> Secp256k1 { + Secp256k1::gen_new_seeded_randomize(seed) } } diff --git a/src/ecdh.rs b/src/ecdh.rs index 226cbe6e2..190069200 100644 --- a/src/ecdh.rs +++ b/src/ecdh.rs @@ -127,10 +127,11 @@ impl SharedSecret { /// `SharedSecret` can be easily created via the `From` impl from arrays. /// # Examples /// ``` + /// # #[cfg(any(features = "alloc", feature = "std"))] { /// # use secp256k1::ecdh::SharedSecret; /// # use secp256k1::{Secp256k1, PublicKey, SecretKey}; /// # fn sha2(_a: &[u8], _b: &[u8]) -> [u8; 32] {[0u8; 32]} - /// # let secp = Secp256k1::signing_only(); + /// # let secp = Secp256k1::signing_only_no_randomize(); /// # let secret_key = SecretKey::from_slice(&[3u8; 32]).unwrap(); /// # let secret_key2 = SecretKey::from_slice(&[7u8; 32]).unwrap(); /// # let public_key = PublicKey::from_secret_key(&secp, &secret_key2); @@ -139,7 +140,7 @@ impl SharedSecret { /// let hash: [u8; 32] = sha2(&x,&y); /// hash.into() /// }); - /// + /// # } /// ``` pub fn new_with_hash(point: &PublicKey, scalar: &SecretKey, mut hash_function: F) -> SharedSecret where F: FnMut([u8; 32], [u8; 32]) -> SharedSecret { @@ -170,15 +171,20 @@ impl SharedSecret { #[cfg(test)] mod tests { use super::*; - use rand::thread_rng; - use super::super::Secp256k1; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; + #[cfg(feature = "rand-std")] + use rand::thread_rng; + + #[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))] + use super::super::Secp256k1; + #[test] + #[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))] fn ecdh() { - let s = Secp256k1::signing_only(); + let s = Secp256k1::signing_only_randomize(); let (sk1, pk1) = s.generate_keypair(&mut thread_rng()); let (sk2, pk2) = s.generate_keypair(&mut thread_rng()); @@ -190,8 +196,9 @@ mod tests { } #[test] + #[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))] fn ecdh_with_hash() { - let s = Secp256k1::signing_only(); + let s = Secp256k1::signing_only_randomize(); let (sk1, pk1) = s.generate_keypair(&mut thread_rng()); let (sk2, pk2) = s.generate_keypair(&mut thread_rng()); @@ -203,8 +210,9 @@ mod tests { } #[test] + #[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))] fn ecdh_with_hash_callback() { - let s = Secp256k1::signing_only(); + let s = Secp256k1::signing_only_randomize(); let (sk1, pk1) = s.generate_keypair(&mut thread_rng()); let expect_result: [u8; 64] = [123; 64]; let mut x_out = [0u8; 32]; @@ -245,7 +253,7 @@ mod benches { #[bench] pub fn bench_ecdh(bh: &mut Bencher) { - let s = Secp256k1::signing_only(); + let s = Secp256k1::signing_only_randomize(); let (sk, pk) = s.generate_keypair(&mut thread_rng()); bh.iter( || { diff --git a/src/ecdsa/mod.rs b/src/ecdsa/mod.rs index 7098ab49c..565f89af5 100644 --- a/src/ecdsa/mod.rs +++ b/src/ecdsa/mod.rs @@ -431,11 +431,11 @@ impl Secp256k1 { /// verify-capable context. /// /// ```rust - /// # #[cfg(feature="rand")] { + /// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// # use secp256k1::rand::rngs::OsRng; /// # use secp256k1::{Secp256k1, Message, Error}; /// # - /// # let secp = Secp256k1::new(); + /// # let secp = Secp256k1::new_randomize(); /// # let mut rng = OsRng::new().expect("OsRng"); /// # let (secret_key, public_key) = secp.generate_keypair(&mut rng); /// # @@ -460,11 +460,11 @@ impl Secp256k1 { /// verify-capable context. /// /// ```rust - /// # #[cfg(feature="rand")] { + /// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// # use secp256k1::rand::rngs::OsRng; /// # use secp256k1::{Secp256k1, Message, Error}; /// # - /// # let secp = Secp256k1::new(); + /// # let secp = Secp256k1::new_randomize(); /// # let mut rng = OsRng::new().expect("OsRng"); /// # let (secret_key, public_key) = secp.generate_keypair(&mut rng); /// # diff --git a/src/ecdsa/recovery.rs b/src/ecdsa/recovery.rs index 9a414b82f..063618682 100644 --- a/src/ecdsa/recovery.rs +++ b/src/ecdsa/recovery.rs @@ -210,10 +210,11 @@ mod tests { use wasm_bindgen_test::wasm_bindgen_test as test; #[test] + #[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))] fn capabilities() { - let sign = Secp256k1::signing_only(); - let vrfy = Secp256k1::verification_only(); - let full = Secp256k1::new(); + let sign = Secp256k1::signing_only_randomize(); + let vrfy = Secp256k1::verification_only_randomize(); + let full = Secp256k1::new_randomize(); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); @@ -243,9 +244,9 @@ mod tests { #[test] #[cfg(not(fuzzing))] // fixed sig vectors can't work with fuzz-sigs + #[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))] fn sign() { - let mut s = Secp256k1::new(); - s.randomize(&mut thread_rng()); + let s = Secp256k1::new_randomize(); let one: [u8; 32] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; @@ -266,9 +267,9 @@ mod tests { } #[test] + #[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))] fn sign_and_verify_fail() { - let mut s = Secp256k1::new(); - s.randomize(&mut thread_rng()); + let s = Secp256k1::new_randomize(); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); @@ -289,9 +290,9 @@ mod tests { } #[test] + #[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))] fn sign_with_recovery() { - let mut s = Secp256k1::new(); - s.randomize(&mut thread_rng()); + let s = Secp256k1::new_randomize(); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); @@ -305,9 +306,9 @@ mod tests { } #[test] + #[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))] fn bad_recovery() { - let mut s = Secp256k1::new(); - s.randomize(&mut thread_rng()); + let s = Secp256k1::new_randomize(); let msg = Message::from_slice(&[0x55; 32]).unwrap(); @@ -379,7 +380,7 @@ mod benches { #[bench] pub fn bench_recover(bh: &mut Bencher) { - let s = Secp256k1::new(); + let s = Secp256k1::new_randomize(); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); let msg = Message::from_slice(&msg).unwrap(); diff --git a/src/key.rs b/src/key.rs index 358eb4698..045201425 100644 --- a/src/key.rs +++ b/src/key.rs @@ -35,10 +35,10 @@ use ffi::{self, CPtr}; /// Basic usage: /// /// ``` -/// # #[cfg(feature="rand")] { +/// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{rand, Secp256k1, SecretKey}; /// -/// let secp = Secp256k1::new(); +/// let secp = Secp256k1::new_randomize(); /// let secret_key = SecretKey::new(&mut rand::thread_rng()); /// # } /// ``` @@ -70,11 +70,13 @@ pub const ONE_KEY: SecretKey = SecretKey([0, 0, 0, 0, 0, 0, 0, 0, /// Basic usage: /// /// ``` +/// # #[cfg(any(feature = "alloc", feature = "std"))] { /// use secp256k1::{SecretKey, Secp256k1, PublicKey}; /// -/// let secp = Secp256k1::new(); +/// let secp = Secp256k1::new_no_randomize(); /// let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order"); /// let public_key = PublicKey::from_secret_key(&secp, &secret_key); +/// # } /// ``` #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] #[repr(transparent)] @@ -183,10 +185,10 @@ impl SecretKey { /// # Examples /// /// ``` - /// # #[cfg(feature="rand")] { + /// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{rand, Secp256k1, SecretKey, KeyPair}; /// - /// let secp = Secp256k1::new(); + /// let secp = Secp256k1::new_randomize(); /// let key_pair = KeyPair::new(&secp, &mut rand::thread_rng()); /// let secret_key = SecretKey::from_keypair(&key_pair); /// # } @@ -327,10 +329,10 @@ impl PublicKey { /// # Examples /// /// ``` - /// # #[cfg(feature="rand")] { + /// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{rand, Secp256k1, SecretKey, PublicKey}; /// - /// let secp = Secp256k1::new(); + /// let secp = Secp256k1::new_randomize(); /// let secret_key = SecretKey::new(&mut rand::thread_rng()); /// let public_key = PublicKey::from_secret_key(&secp, &secret_key); /// # } @@ -375,10 +377,10 @@ impl PublicKey { /// # Examples /// /// ``` - /// # #[cfg(feature="rand")] { + /// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{rand, Secp256k1, PublicKey, KeyPair}; /// - /// let secp = Secp256k1::new(); + /// let secp = Secp256k1::new_randomize(); /// let key_pair = KeyPair::new(&secp, &mut rand::thread_rng()); /// let public_key = PublicKey::from_keypair(&key_pair); /// # } @@ -506,10 +508,10 @@ impl PublicKey { /// # Examples /// /// ``` - /// # #[cfg(feature="rand")] { + /// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{rand, Secp256k1}; /// - /// let secp = Secp256k1::new(); + /// let secp = Secp256k1::new_randomize(); /// let mut rng = rand::thread_rng(); /// let (_, pk1) = secp.generate_keypair(&mut rng); /// let (_, pk2) = secp.generate_keypair(&mut rng); @@ -532,10 +534,10 @@ impl PublicKey { /// # Examples /// /// ``` - /// # #[cfg(feature="rand")] { + /// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{rand, Secp256k1, PublicKey}; /// - /// let secp = Secp256k1::new(); + /// let secp = Secp256k1::new_randomize(); /// let mut rng = rand::thread_rng(); /// let (_, pk1) = secp.generate_keypair(&mut rng); /// let (_, pk2) = secp.generate_keypair(&mut rng); @@ -648,10 +650,10 @@ impl Ord for PublicKey { /// Basic usage: /// /// ``` -/// # #[cfg(feature="rand")] { +/// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{rand, KeyPair, Secp256k1}; /// -/// let secp = Secp256k1::new(); +/// let secp = Secp256k1::new_randomize(); /// let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng()); /// let key_pair = KeyPair::from_secret_key(&secp, secret_key); /// # } @@ -742,10 +744,10 @@ impl KeyPair { /// # Examples /// /// ``` - /// # #[cfg(feature="rand")] { + /// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{rand, Secp256k1, SecretKey, KeyPair}; /// - /// let secp = Secp256k1::new(); + /// let secp = Secp256k1::new_randomize(); /// let key_pair = KeyPair::new(&secp, &mut rand::thread_rng()); /// # } /// ``` @@ -788,11 +790,11 @@ impl KeyPair { /// # Examples /// /// ``` - /// # #[cfg(feature="rand")] { + /// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{Secp256k1, KeyPair}; /// use secp256k1::rand::{RngCore, thread_rng}; /// - /// let secp = Secp256k1::new(); + /// let secp = Secp256k1::new_randomize(); /// let mut tweak = [0u8; 32]; /// thread_rng().fill_bytes(&mut tweak); /// @@ -912,10 +914,10 @@ impl<'de> ::serde::Deserialize<'de> for KeyPair { /// Basic usage: /// /// ``` -/// # #[cfg(feature="rand")] { +/// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{rand, Secp256k1, KeyPair, XOnlyPublicKey}; /// -/// let secp = Secp256k1::new(); +/// let secp = Secp256k1::new_randomize(); /// let key_pair = KeyPair::new(&secp, &mut rand::thread_rng()); /// let xonly = XOnlyPublicKey::from_keypair(&key_pair); /// # } @@ -1040,11 +1042,11 @@ impl XOnlyPublicKey { /// # Examples /// /// ``` - /// # #[cfg(feature="rand")] { + /// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{Secp256k1, KeyPair}; /// use secp256k1::rand::{RngCore, thread_rng}; /// - /// let secp = Secp256k1::new(); + /// let secp = Secp256k1::new_randomize(); /// let mut tweak = [0u8; 32]; /// thread_rng().fill_bytes(&mut tweak); /// @@ -1105,11 +1107,11 @@ impl XOnlyPublicKey { /// # Examples /// /// ``` - /// # #[cfg(feature="rand")] { + /// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] { /// use secp256k1::{Secp256k1, KeyPair}; /// use secp256k1::rand::{thread_rng, RngCore}; /// - /// let secp = Secp256k1::new(); + /// let secp = Secp256k1::new_randomize(); /// let mut tweak = [0u8; 32]; /// thread_rng().fill_bytes(&mut tweak); /// @@ -1392,8 +1394,9 @@ mod test { } #[test] + #[cfg(all(feature = "rand", any(features = "alloc", feature = "std")))] fn keypair_slice_round_trip() { - let s = Secp256k1::new(); + let s = Secp256k1::new_randomize(); let (sk1, pk1) = s.generate_keypair(&mut thread_rng()); assert_eq!(SecretKey::from_slice(&sk1[..]), Ok(sk1)); @@ -1428,6 +1431,7 @@ mod test { } #[test] + #[cfg(any(features = "alloc", feature = "std"))] fn test_out_of_range() { struct BadRng(u8); @@ -1454,7 +1458,7 @@ mod test { } } - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); s.generate_keypair(&mut BadRng(0xff)); } @@ -1539,7 +1543,7 @@ mod test { } } - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); let (sk, _) = s.generate_keypair(&mut DumbRng(0)); assert_eq!(&format!("{:?}", sk), @@ -1559,7 +1563,7 @@ mod test { 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ]; - let s = Secp256k1::signing_only(); + let s = Secp256k1::signing_only_no_randomize(); let sk = SecretKey::from_slice(&SK_BYTES).expect("sk"); // In fuzzing mode secret->public key derivation is different, so @@ -1632,7 +1636,7 @@ mod test { } } - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); let (_, pk1) = s.generate_keypair(&mut DumbRng(0)); assert_eq!(&pk1.serialize_uncompressed()[..], &[4, 124, 121, 49, 14, 253, 63, 197, 50, 39, 194, 107, 17, 193, 219, 108, 154, 126, 9, 181, 248, 2, 12, 149, 233, 198, 71, 149, 134, 250, 184, 154, 229, 185, 28, 165, 110, 27, 3, 162, 126, 238, 167, 157, 242, 221, 76, 251, 237, 34, 231, 72, 39, 245, 3, 191, 64, 111, 170, 117, 103, 82, 28, 102, 163][..]); @@ -1642,7 +1646,7 @@ mod test { #[test] fn test_addition() { - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); let (mut sk1, mut pk1) = s.generate_keypair(&mut thread_rng()); let (mut sk2, mut pk2) = s.generate_keypair(&mut thread_rng()); @@ -1660,7 +1664,7 @@ mod test { #[test] fn test_multiplication() { - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); let (mut sk1, mut pk1) = s.generate_keypair(&mut thread_rng()); let (mut sk2, mut pk2) = s.generate_keypair(&mut thread_rng()); @@ -1678,7 +1682,7 @@ mod test { #[test] fn test_negation() { - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); let (mut sk, mut pk) = s.generate_keypair(&mut thread_rng()); @@ -1709,7 +1713,7 @@ mod test { s.finish() } - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); let mut set = HashSet::new(); const COUNT : usize = 1024; for _ in 0..COUNT { @@ -1771,7 +1775,7 @@ mod test { #[test] fn create_pubkey_combine() { - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); let (mut sk1, pk1) = s.generate_keypair(&mut thread_rng()); let (sk2, pk2) = s.generate_keypair(&mut thread_rng()); @@ -1833,7 +1837,7 @@ mod test { 0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166\ "; - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); let sk = SecretKey::from_slice(&SK_BYTES).unwrap(); // In fuzzing mode secret->public key derivation is different, so @@ -1863,7 +1867,7 @@ mod test { #[test] fn test_tweak_add_assign_then_tweak_add_check() { - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); for _ in 0..10 { let mut tweak = [0u8; 32]; diff --git a/src/lib.rs b/src/lib.rs index aae975c45..44175b632 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 @@ -43,7 +46,7 @@ //! use secp256k1::{Secp256k1, Message}; //! use secp256k1::hashes::sha256; //! -//! let secp = Secp256k1::new(); +//! let secp = Secp256k1::new_randomize(); //! let mut rng = OsRng::new().expect("OsRng"); //! let (secret_key, public_key) = secp.generate_keypair(&mut rng); //! let message = Message::from_hashed_data::("Hello World!".as_bytes()); @@ -60,7 +63,7 @@ //! ```rust //! use secp256k1::{Secp256k1, Message, SecretKey, PublicKey}; //! -//! let secp = Secp256k1::new(); +//! let secp = Secp256k1::new_no_randomize(); //! let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order"); //! let public_key = PublicKey::from_secret_key(&secp, &secret_key); //! // This is unsafe unless the supplied byte slice is the output of a cryptographic hash function. @@ -74,9 +77,10 @@ //! Users who only want to verify signatures can use a cheaper context, like so: //! //! ```rust +//! # #[cfg(any(features = "alloc", feature = "std"))] //! use secp256k1::{Secp256k1, Message, ecdsa, PublicKey}; //! -//! let secp = Secp256k1::verification_only(); +//! let secp = Secp256k1::verification_only_no_randomize(); //! //! let public_key = PublicKey::from_slice(&[ //! 0x02, @@ -117,7 +121,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. @@ -551,12 +555,13 @@ mod tests { } #[test] + #[cfg(any(features = "alloc", feature = "std"))] fn test_raw_ctx() { use std::mem::ManuallyDrop; - let ctx_full = Secp256k1::new(); - let ctx_sign = Secp256k1::signing_only(); - let ctx_vrfy = Secp256k1::verification_only(); + let ctx_full = Secp256k1::new_no_randomize(); + let ctx_sign = Secp256k1::signing_only_no_randomize(); + let ctx_vrfy = Secp256k1::verification_only_no_randomize(); let mut full = unsafe {Secp256k1::from_raw_all(ctx_full.ctx)}; let mut sign = unsafe {Secp256k1::from_raw_signining_only(ctx_sign.ctx)}; @@ -586,8 +591,9 @@ mod tests { #[cfg(not(target_arch = "wasm32"))] #[test] #[ignore] // Panicking from C may trap (SIGILL) intentionally, so we test this manually. + #[cfg(any(features = "alloc", feature = "std"))] fn test_panic_raw_ctx_should_terminate_abnormally() { - let ctx_vrfy = Secp256k1::verification_only(); + let ctx_vrfy = Secp256k1::verification_only_no_randomize(); let raw_ctx_verify_as_full = unsafe {Secp256k1::from_raw_all(ctx_vrfy.ctx)}; // Generating a key pair in verify context will panic (ARG_CHECK). raw_ctx_verify_as_full.generate_keypair(&mut thread_rng()); @@ -618,10 +624,11 @@ mod tests { } #[test] + #[cfg(any(features = "alloc", feature = "std"))] fn capabilities() { - let sign = Secp256k1::signing_only(); - let vrfy = Secp256k1::verification_only(); - let full = Secp256k1::new(); + let sign = Secp256k1::signing_only_no_randomize(); + let vrfy = Secp256k1::verification_only_no_randomize(); + let full = Secp256k1::new_no_randomize(); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); @@ -647,9 +654,9 @@ mod tests { } #[test] + #[cfg(all(feature = "rand", any(features = "alloc", feature = "std")))] fn signature_serialize_roundtrip() { - let mut s = Secp256k1::new(); - s.randomize(&mut thread_rng()); + let s = Secp256k1::new_randomize(); let mut msg = [0u8; 32]; for _ in 0..100 { @@ -733,9 +740,9 @@ mod tests { } #[test] + #[cfg(all(feature = "rand", any(features = "alloc", feature = "std")))] fn sign_and_verify_ecdsa() { - let mut s = Secp256k1::new(); - s.randomize(&mut thread_rng()); + let s = Secp256k1::new_randomize(); let mut msg = [0u8; 32]; for _ in 0..100 { @@ -764,9 +771,9 @@ mod tests { } #[test] + #[cfg(any(features = "alloc", feature = "std"))] fn sign_and_verify_extreme() { - let mut s = Secp256k1::new(); - s.randomize(&mut thread_rng()); + let s = Secp256k1::new_no_randomize(); // Wild keys: 1, CURVE_ORDER - 1 // Wild msgs: 1, CURVE_ORDER - 1 @@ -797,9 +804,9 @@ mod tests { } #[test] + #[cfg(all(feature = "rand", any(features = "alloc", feature = "std")))] fn sign_and_verify_fail() { - let mut s = Secp256k1::new(); - s.randomize(&mut thread_rng()); + let s = Secp256k1::new_randomize(); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); @@ -866,7 +873,7 @@ mod tests { let pk = hex!("031ee99d2b786ab3b0991325f2de8489246a6a3fdb700f6d0511b1d80cf5f4cd43"); let msg = hex!("a4965ca63b7d8562736ceec36dfa5a11bf426eb65be8ea3f7a49ae363032da0d"); - let secp = Secp256k1::new(); + let secp = Secp256k1::new_no_randomize(); let mut sig = ecdsa::Signature::from_der(&sig[..]).unwrap(); let pk = PublicKey::from_slice(&pk[..]).unwrap(); let msg = Message::from_slice(&msg[..]).unwrap(); @@ -881,7 +888,7 @@ mod tests { #[test] #[cfg(not(fuzzing))] // fuzz-sigs have fixed size/format fn test_low_r() { - let secp = Secp256k1::new(); + let secp = Secp256k1::new_no_randomize(); let msg = hex!("887d04bb1cf1b1554f1b268dfe62d13064ca67ae45348d50d1392ce2d13418ac"); let msg = Message::from_slice(&msg).unwrap(); let sk = SecretKey::from_str("57f0148f94d13095cfda539d0da0d1541304b678d8b36e243980aab4e1b7cead").unwrap(); @@ -896,7 +903,7 @@ mod tests { #[test] #[cfg(not(fuzzing))] // fuzz-sigs have fixed size/format fn test_grind_r() { - let secp = Secp256k1::new(); + let secp = Secp256k1::new_no_randomize(); let msg = hex!("ef2d5b9a7c61865a95941d0f04285420560df7e9d76890ac1b8867b12ce43167"); let msg = Message::from_slice(&msg).unwrap(); let sk = SecretKey::from_str("848355d75fe1c354cf05539bb29b2015f1863065bcb6766b44d399ab95c3fa0b").unwrap(); @@ -913,7 +920,7 @@ mod tests { fn test_serde() { use serde_test::{Configure, Token, assert_tokens}; - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); let msg = Message::from_slice(&[1; 32]).unwrap(); let sk = SecretKey::from_slice(&[2; 32]).unwrap(); @@ -1017,7 +1024,7 @@ mod benches { } - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); let mut r = CounterRng(0); bh.iter( || { let (sk, pk) = s.generate_keypair(&mut r); @@ -1028,7 +1035,7 @@ mod benches { #[bench] pub fn bench_sign_ecdsa(bh: &mut Bencher) { - let s = Secp256k1::new(); + let s = Secp256k1::new_randomize(); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); let msg = Message::from_slice(&msg).unwrap(); @@ -1042,7 +1049,7 @@ mod benches { #[bench] pub fn bench_verify_ecdsa(bh: &mut Bencher) { - let s = Secp256k1::new(); + let s = Secp256k1::new_randomize(); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); let msg = Message::from_slice(&msg).unwrap(); diff --git a/src/schnorr.rs b/src/schnorr.rs index e71391365..d61380127 100644 --- a/src/schnorr.rs +++ b/src/schnorr.rs @@ -269,13 +269,20 @@ impl Secp256k1 { #[cfg(test)] mod tests { use super::super::Error::InvalidPublicKey; - use super::super::{constants, from_hex, All, Message, Secp256k1}; + use super::super::{constants, from_hex, Message, Secp256k1}; use super::{KeyPair, XOnlyPublicKey, Signature}; - use rand::{rngs::ThreadRng, thread_rng, Error, ErrorKind, RngCore}; - use rand_core::impls; + use std::iter; use std::str::FromStr; + #[cfg(feature = "rand")] + use rand_core::impls; + #[cfg(feature = "rand")] + use rand::{rngs::ThreadRng, thread_rng, Error, ErrorKind, RngCore}; + + #[cfg(all(feature = "rand", any(feature = "alloc", feature = "std")))] + use super::super::All; + #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; use SecretKey; @@ -289,6 +296,7 @@ mod tests { } #[test] + #[cfg(all(feature = "rand", any(feature = "alloc", feature = "std")))] fn test_schnorrsig_sign_with_aux_rand_verify() { test_schnorrsig_sign_helper(|secp, msg, seckey, rng| { let mut aux_rand = [0u8; 32]; @@ -298,6 +306,7 @@ mod tests { } #[test] + #[cfg(all(feature = "rand", any(feature = "alloc", feature = "std")))] fn test_schnorrsig_sign_with_rng_verify() { test_schnorrsig_sign_helper(|secp, msg, seckey, mut rng| { secp.sign_schnorr_with_rng(msg, seckey, &mut rng) @@ -305,23 +314,26 @@ mod tests { } #[test] - fn test_schnorrsig_sign_verify() { + #[cfg(all(feature = "rand", any(feature = "alloc", feature = "std")))] + fn test_schnorrsig_sign_verifya() { test_schnorrsig_sign_helper(|secp, msg, seckey, _| { secp.sign_schnorr(msg, seckey) }) } #[test] + #[cfg(all(feature = "rand", any(feature = "alloc", feature = "std")))] fn test_schnorrsig_sign_no_aux_rand_verify() { test_schnorrsig_sign_helper(|secp, msg, seckey, _| { secp.sign_schnorr_no_aux_rand(msg, seckey) }) } + #[cfg(all(feature = "rand", any(feature = "alloc", feature = "std")))] fn test_schnorrsig_sign_helper( sign: fn(&Secp256k1, &Message, &KeyPair, &mut ThreadRng) -> Signature, ) { - let secp = Secp256k1::new(); + let secp = Secp256k1::new_randomize(); let mut rng = thread_rng(); let kp = KeyPair::new(&secp, &mut rng); @@ -341,8 +353,9 @@ mod tests { #[test] #[cfg(not(fuzzing))] // fixed sig vectors can't work with fuzz-sigs + #[cfg(any(feature = "alloc", feature = "std"))] fn test_schnorrsig_sign() { - let secp = Secp256k1::new(); + let secp = Secp256k1::new_no_randomize(); let hex_msg = hex_32!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); let msg = Message::from_slice(&hex_msg).unwrap(); @@ -363,8 +376,9 @@ mod tests { #[test] #[cfg(not(fuzzing))] // fixed sig vectors can't work with fuzz-sigs + #[cfg(any(feature = "alloc", feature = "std"))] fn test_schnorrsig_verify() { - let secp = Secp256k1::new(); + let secp = Secp256k1::new_no_randomize(); let hex_msg = hex_32!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); let msg = Message::from_slice(&hex_msg).unwrap(); @@ -389,8 +403,9 @@ mod tests { } #[test] + #[cfg(all(feature = "rand", any(feature = "alloc", feature = "std")))] fn test_pubkey_serialize_roundtrip() { - let secp = Secp256k1::new(); + let secp = Secp256k1::new_randomize(); let kp = KeyPair::new(&secp, &mut thread_rng()); let pk = kp.public_key(); @@ -400,8 +415,9 @@ mod tests { } #[test] + #[cfg(any(feature = "alloc", feature = "std"))] fn test_xonly_key_extraction() { - let secp = Secp256k1::new(); + let secp = Secp256k1::new_no_randomize(); let sk_str = "688C77BC2D5AAFF5491CF309D4753B732135470D05B7B2CD21ADD0744FE97BEF"; let keypair = KeyPair::from_seckey_str(&secp, sk_str).unwrap(); let sk = SecretKey::from_keypair(&keypair); @@ -441,7 +457,7 @@ mod tests { #[test] fn test_pubkey_display_output() { - let secp = Secp256k1::new(); + let secp = Secp256k1::new_no_randomize(); static SK_BYTES: [u8; 32] = [ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, @@ -496,6 +512,7 @@ mod tests { // In fuzzing mode secret->public key derivation is different, so // this test will never correctly derive the static pubkey. #[cfg(not(fuzzing))] + #[cfg(all(feature = "rand", any(feature = "alloc", feature = "std")))] fn test_pubkey_serialize() { struct DumbRng(u32); impl RngCore for DumbRng { @@ -515,7 +532,7 @@ mod tests { } } - let secp = Secp256k1::new(); + let secp = Secp256k1::new_no_randomize(); let kp = KeyPair::new(&secp, &mut DumbRng(0)); let pk = kp.public_key(); assert_eq!( @@ -533,7 +550,7 @@ mod tests { fn test_serde() { use serde_test::{assert_tokens, Configure, Token}; - let s = Secp256k1::new(); + let s = Secp256k1::new_no_randomize(); let msg = Message::from_slice(&[1; 32]).unwrap(); let keypair = KeyPair::from_seckey_slice(&s, &[2; 32]).unwrap(); diff --git a/src/secret.rs b/src/secret.rs index 28bd80d36..9e4168307 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -129,7 +129,7 @@ impl KeyPair { /// use secp256k1::KeyPair; /// use secp256k1::Secp256k1; /// - /// let secp = Secp256k1::new(); + /// let secp = Secp256k1::new_no_randomize(); /// let key = ONE_KEY; /// let key = KeyPair::from_secret_key(&secp, key); ///