Skip to content

Commit 2cd66fa

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 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
1 parent f7d637e commit 2cd66fa

13 files changed

+262
-128
lines changed

Cargo.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,15 @@ rand = { version = "0.6", features = ["wasm-bindgen"] }
4949

5050
[[example]]
5151
name = "sign_verify_recovery"
52-
required-features = ["recovery"]
52+
required-features = ["recovery", "alloc"]
5353

5454
[[example]]
5555
name = "sign_verify"
56+
required-features = ["alloc"]
5657

5758
[[example]]
5859
name = "generate_keys"
59-
required-features = ["rand"]
60+
required-features = ["alloc", "rand-std"]
6061

6162
[workspace]
6263
members = ["secp256k1-sys"]

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_randomize();
88
let mut rng = OsRng::new().unwrap();
99
// First option:
1010
let (seckey, pubkey) = secp.generate_keypair(&mut rng);

examples/sign_verify.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fn sign<C: Signing>(secp: &Secp256k1<C>, msg: &[u8], seckey: [u8; 32]) -> Result
2121
}
2222

2323
fn main() {
24-
let secp = Secp256k1::new();
24+
let secp = Secp256k1::new_no_randomize();
2525

2626
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];
2727
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];

examples/sign_verify_recovery.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fn sign_recovery<C: Signing>(secp: &Secp256k1<C>, msg: &[u8], seckey: [u8; 32])
2222
}
2323

2424
fn main() {
25-
let secp = Secp256k1::new();
25+
let secp = Secp256k1::new_no_randomize();
2626

2727
let seckey = [
2828
59, 148, 11, 85, 134, 130, 61, 253, 2, 174, 59, 70, 27, 180, 51, 107,

no_std_test/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize {
135135

136136
#[cfg(feature = "alloc")]
137137
{
138-
let secp_alloc = Secp256k1::new();
138+
let secp_alloc = Secp256k1::new_no_randomize();
139139
let public_key = PublicKey::from_secret_key(&secp_alloc, &secret_key);
140140
let message = Message::from_slice(&[0xab; 32]).expect("32 bytes");
141141

src/context.rs

+104-17
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ 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 {
@@ -39,7 +39,7 @@ pub mod global {
3939
static ONCE: Once = Once::new();
4040
static mut CONTEXT: Option<Secp256k1<All>> = None;
4141
ONCE.call_once(|| unsafe {
42-
let mut ctx = Secp256k1::new();
42+
let mut ctx = Secp256k1::new_no_randomize();
4343
#[cfg(feature = "global-context")]
4444
{
4545
ctx.randomize(&mut rand::thread_rng());
@@ -166,8 +166,8 @@ mod alloc_only {
166166
}
167167

168168
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> {
169+
/// Helper function only intended to be called by other gen_new_* functions.
170+
fn _gen_new() -> Secp256k1<C> {
171171
#[cfg(target_arch = "wasm32")]
172172
ffi::types::sanity_checks_for_wasm();
173173

@@ -180,32 +180,119 @@ mod alloc_only {
180180
size,
181181
}
182182
}
183+
184+
/// Lets you create a context in a generic manner(sign/verify/all).
185+
///
186+
/// Context is randomized using `thread_rng` for additional defense-in-depth side channel
187+
/// protection.
188+
#[cfg(feature = "rand-std")]
189+
#[cfg_attr(docsrs, doc(cfg(feature = "rand-std")))]
190+
pub fn gen_new_randomize() -> Secp256k1<C> {
191+
use rand::thread_rng;
192+
let mut secp = Secp256k1::_gen_new();
193+
secp.randomize(&mut thread_rng());
194+
secp
195+
}
196+
197+
/// Lets you create a context in a generic manner(sign/verify/all).
198+
///
199+
/// No randomization is done, this context is perfectly safe but for additional
200+
/// defense-in-depth side channel protection consider using [`gen_new_seeded_randomize`].
201+
pub fn gen_new_no_randomize() -> Secp256k1<C> {
202+
Secp256k1::_gen_new()
203+
}
204+
205+
/// Lets you create a context in a generic manner(sign/verify/all).
206+
///
207+
/// Context is randomized using `seed` for additional defense-in-depth side channel
208+
/// protection.
209+
pub fn gen_new_seeded_randomize(seed: &[u8; 32]) -> Secp256k1<C> {
210+
let mut secp = Secp256k1::_gen_new();
211+
secp.seeded_randomize(seed);
212+
secp
213+
}
183214
}
184215

185216
impl Secp256k1<All> {
186-
/// Creates a new Secp256k1 context with all capabilities
187-
pub fn new() -> Secp256k1<All> {
188-
Secp256k1::gen_new()
217+
/// Creates a new Secp256k1 context with all capabilities.
218+
///
219+
/// Context is randomized using `thread_rng` for additional defense-in-depth side channel
220+
/// protection.
221+
#[cfg(feature = "rand-std")]
222+
#[cfg_attr(docsrs, doc(cfg(feature = "rand-std")))]
223+
pub fn new_randomize() -> Secp256k1<All> {
224+
Secp256k1::gen_new_randomize()
225+
}
226+
/// Creates a new Secp256k1 context with all capabilities.
227+
///
228+
/// No randomization is done, this context is perfectly safe but for additional
229+
/// defense-in-depth side channel protection consider using [`new_seeded_randomize`].
230+
pub fn new_no_randomize() -> Secp256k1<All> {
231+
Secp256k1::gen_new_no_randomize()
232+
}
233+
234+
/// Creates a new Secp256k1 context with all capabilities.
235+
///
236+
/// Context is randomized using `seed` for additional defense-in-depth side channel
237+
/// protection.
238+
pub fn new_seeded_randomize(seed: &[u8; 32]) -> Secp256k1<All> {
239+
Secp256k1::gen_new_seeded_randomize(seed)
189240
}
190241
}
191242

192243
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()
244+
/// Creates a new Secp256k1 context that can only be used for signing.
245+
///
246+
/// Context is randomized using `thread_rng` for additional defense-in-depth side channel
247+
/// protection.
248+
#[cfg(feature = "rand-std")]
249+
#[cfg_attr(docsrs, doc(cfg(feature = "rand-std")))]
250+
pub fn signing_only_randomize() -> Secp256k1<SignOnly> {
251+
Secp256k1::gen_new_randomize()
252+
}
253+
254+
/// Creates a new Secp256k1 context that can only be used for signing.
255+
///
256+
/// No randomization is done, this context is perfectly safe but for additional
257+
/// defense-in-depth side channel protection consider using [`signing_only_seeded_randomize`].
258+
pub fn signing_only_no_randomize() -> Secp256k1<SignOnly> {
259+
Secp256k1::gen_new_no_randomize()
260+
}
261+
262+
/// Creates a new Secp256k1 context that can only be used for signing.
263+
///
264+
/// Context is randomized using `seed` for additional defense-in-depth side channel
265+
/// protection.
266+
pub fn signing_only_seeded_randomize(seed: &[u8; 32]) -> Secp256k1<SignOnly> {
267+
Secp256k1::gen_new_seeded_randomize(seed)
196268
}
197269
}
198270

199271
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()
272+
/// Creates a new Secp256k1 context that can only be used for verifying.
273+
///
274+
/// Context is randomized using `thread_rng` for additional defense-in-depth side channel
275+
/// protection.
276+
#[cfg(feature = "rand-std")]
277+
#[cfg_attr(docsrs, doc(cfg(feature = "rand-std")))]
278+
pub fn verification_only_randomize() -> Secp256k1<VerifyOnly> {
279+
Secp256k1::gen_new_randomize()
280+
}
281+
282+
/// Creates a new Secp256k1 context that can only be used for verifying.
283+
///
284+
/// No randomization is done, this context is perfectly safe but for additional
285+
/// defense-in-depth side channel protection consider using [`verification_only_seeded_randomize`].
286+
pub fn verification_only_no_randomize() -> Secp256k1<VerifyOnly> {
287+
Secp256k1::gen_new_no_randomize()
203288
}
204-
}
205289

206-
impl Default for Secp256k1<All> {
207-
fn default() -> Self {
208-
Self::new()
290+
/// Creates a new Secp256k1 context that can only be used for verifying.
291+
///
292+
/// Context is randomized using `seed` for additional defense-in-depth side channel
293+
/// protection.
294+
pub fn verification_only_seeded_randomize(seed: &[u8; 32]) -> Secp256k1<VerifyOnly> {
295+
Secp256k1::gen_new_seeded_randomize(seed)
209296
}
210297
}
211298

src/ecdh.rs

+16-8
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,11 @@ impl SharedSecret {
127127
/// `SharedSecret` can be easily created via the `From` impl from arrays.
128128
/// # Examples
129129
/// ```
130+
/// # #[cfg(any(features = "alloc", feature = "std"))] {
130131
/// # use secp256k1::ecdh::SharedSecret;
131132
/// # use secp256k1::{Secp256k1, PublicKey, SecretKey};
132133
/// # fn sha2(_a: &[u8], _b: &[u8]) -> [u8; 32] {[0u8; 32]}
133-
/// # let secp = Secp256k1::signing_only();
134+
/// # let secp = Secp256k1::signing_only_no_randomize();
134135
/// # let secret_key = SecretKey::from_slice(&[3u8; 32]).unwrap();
135136
/// # let secret_key2 = SecretKey::from_slice(&[7u8; 32]).unwrap();
136137
/// # let public_key = PublicKey::from_secret_key(&secp, &secret_key2);
@@ -139,7 +140,7 @@ impl SharedSecret {
139140
/// let hash: [u8; 32] = sha2(&x,&y);
140141
/// hash.into()
141142
/// });
142-
///
143+
/// # }
143144
/// ```
144145
pub fn new_with_hash<F>(point: &PublicKey, scalar: &SecretKey, mut hash_function: F) -> SharedSecret
145146
where F: FnMut([u8; 32], [u8; 32]) -> SharedSecret {
@@ -170,15 +171,20 @@ impl SharedSecret {
170171
#[cfg(test)]
171172
mod tests {
172173
use super::*;
173-
use rand::thread_rng;
174-
use super::super::Secp256k1;
175174

176175
#[cfg(target_arch = "wasm32")]
177176
use wasm_bindgen_test::wasm_bindgen_test as test;
178177

178+
#[cfg(feature = "rand-std")]
179+
use rand::thread_rng;
180+
181+
#[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))]
182+
use super::super::Secp256k1;
183+
179184
#[test]
185+
#[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))]
180186
fn ecdh() {
181-
let s = Secp256k1::signing_only();
187+
let s = Secp256k1::signing_only_randomize();
182188
let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
183189
let (sk2, pk2) = s.generate_keypair(&mut thread_rng());
184190

@@ -190,8 +196,9 @@ mod tests {
190196
}
191197

192198
#[test]
199+
#[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))]
193200
fn ecdh_with_hash() {
194-
let s = Secp256k1::signing_only();
201+
let s = Secp256k1::signing_only_randomize();
195202
let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
196203
let (sk2, pk2) = s.generate_keypair(&mut thread_rng());
197204

@@ -203,8 +210,9 @@ mod tests {
203210
}
204211

205212
#[test]
213+
#[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))]
206214
fn ecdh_with_hash_callback() {
207-
let s = Secp256k1::signing_only();
215+
let s = Secp256k1::signing_only_randomize();
208216
let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
209217
let expect_result: [u8; 64] = [123; 64];
210218
let mut x_out = [0u8; 32];
@@ -245,7 +253,7 @@ mod benches {
245253

246254
#[bench]
247255
pub fn bench_ecdh(bh: &mut Bencher) {
248-
let s = Secp256k1::signing_only();
256+
let s = Secp256k1::signing_only_randomize();
249257
let (sk, pk) = s.generate_keypair(&mut thread_rng());
250258

251259
bh.iter( || {

src/ecdsa/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -431,11 +431,11 @@ impl<C: Verification> Secp256k1<C> {
431431
/// verify-capable context.
432432
///
433433
/// ```rust
434-
/// # #[cfg(feature="rand")] {
434+
/// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] {
435435
/// # use secp256k1::rand::rngs::OsRng;
436436
/// # use secp256k1::{Secp256k1, Message, Error};
437437
/// #
438-
/// # let secp = Secp256k1::new();
438+
/// # let secp = Secp256k1::new_randomize();
439439
/// # let mut rng = OsRng::new().expect("OsRng");
440440
/// # let (secret_key, public_key) = secp.generate_keypair(&mut rng);
441441
/// #
@@ -460,11 +460,11 @@ impl<C: Verification> Secp256k1<C> {
460460
/// verify-capable context.
461461
///
462462
/// ```rust
463-
/// # #[cfg(feature="rand")] {
463+
/// # #[cfg(all(feature="rand", any(feature = "alloc", feature = "std")))] {
464464
/// # use secp256k1::rand::rngs::OsRng;
465465
/// # use secp256k1::{Secp256k1, Message, Error};
466466
/// #
467-
/// # let secp = Secp256k1::new();
467+
/// # let secp = Secp256k1::new_randomize();
468468
/// # let mut rng = OsRng::new().expect("OsRng");
469469
/// # let (secret_key, public_key) = secp.generate_keypair(&mut rng);
470470
/// #

0 commit comments

Comments
 (0)