Skip to content

Commit 3d33f24

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: rust-bitcoin#225
1 parent 6ebea93 commit 3d33f24

File tree

13 files changed

+251
-126
lines changed

13 files changed

+251
-126
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 {
@@ -40,7 +40,7 @@ pub mod global {
4040
static ONCE: Once = Once::new();
4141
static mut CONTEXT: Option<Secp256k1<All>> = None;
4242
ONCE.call_once(|| unsafe {
43-
let mut ctx = Secp256k1::new();
43+
let mut ctx = Secp256k1::new_no_randomize();
4444
#[cfg(feature = "global-context")]
4545
{
4646
ctx.randomize(&mut rand::thread_rng());
@@ -167,8 +167,8 @@ mod alloc_only {
167167
}
168168

169169
impl<C: Context> Secp256k1<C> {
170-
/// Lets you create a context in a generic manner(sign/verify/all)
171-
pub fn gen_new() -> Secp256k1<C> {
170+
/// Helper function only intended to be called by other gen_new_* functions.
171+
fn _gen_new() -> Secp256k1<C> {
172172
#[cfg(target_arch = "wasm32")]
173173
ffi::types::sanity_checks_for_wasm();
174174

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

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

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

200272
impl Secp256k1<VerifyOnly> {
201-
/// Creates a new Secp256k1 context that can only be used for verification
202-
pub fn verification_only() -> Secp256k1<VerifyOnly> {
203-
Secp256k1::gen_new()
273+
/// Creates a new Secp256k1 context that can only be used for verifying.
274+
///
275+
/// Context is randomized using `thread_rng` for additional defense-in-depth side channel
276+
/// protection.
277+
#[cfg(feature = "rand-std")]
278+
#[cfg_attr(docsrs, doc(cfg(feature = "rand-std")))]
279+
pub fn verification_only_randomize() -> Secp256k1<VerifyOnly> {
280+
Secp256k1::gen_new_randomize()
281+
}
282+
283+
/// Creates a new Secp256k1 context that can only be used for verifying.
284+
///
285+
/// No randomization is done, this context is perfectly safe but for additional
286+
/// defense-in-depth side channel protection consider using [`verification_only_seeded_randomize`].
287+
pub fn verification_only_no_randomize() -> Secp256k1<VerifyOnly> {
288+
Secp256k1::gen_new_no_randomize()
204289
}
205-
}
206290

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

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
/// #

src/ecdsa/recovery.rs

+13-12
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,11 @@ mod tests {
210210
use wasm_bindgen_test::wasm_bindgen_test as test;
211211

212212
#[test]
213+
#[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))]
213214
fn capabilities() {
214-
let sign = Secp256k1::signing_only();
215-
let vrfy = Secp256k1::verification_only();
216-
let full = Secp256k1::new();
215+
let sign = Secp256k1::signing_only_randomize();
216+
let vrfy = Secp256k1::verification_only_randomize();
217+
let full = Secp256k1::new_randomize();
217218

218219
let mut msg = [0u8; 32];
219220
thread_rng().fill_bytes(&mut msg);
@@ -243,9 +244,9 @@ mod tests {
243244

244245
#[test]
245246
#[cfg(not(fuzzing))] // fixed sig vectors can't work with fuzz-sigs
247+
#[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))]
246248
fn sign() {
247-
let mut s = Secp256k1::new();
248-
s.randomize(&mut thread_rng());
249+
let s = Secp256k1::new_randomize();
249250
let one: [u8; 32] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
250251
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
251252

@@ -266,9 +267,9 @@ mod tests {
266267
}
267268

268269
#[test]
270+
#[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))]
269271
fn sign_and_verify_fail() {
270-
let mut s = Secp256k1::new();
271-
s.randomize(&mut thread_rng());
272+
let s = Secp256k1::new_randomize();
272273

273274
let mut msg = [0u8; 32];
274275
thread_rng().fill_bytes(&mut msg);
@@ -289,9 +290,9 @@ mod tests {
289290
}
290291

291292
#[test]
293+
#[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))]
292294
fn sign_with_recovery() {
293-
let mut s = Secp256k1::new();
294-
s.randomize(&mut thread_rng());
295+
let s = Secp256k1::new_randomize();
295296

296297
let mut msg = [0u8; 32];
297298
thread_rng().fill_bytes(&mut msg);
@@ -305,9 +306,9 @@ mod tests {
305306
}
306307

307308
#[test]
309+
#[cfg(all(feature = "rand-std", any(features = "alloc", feature = "std")))]
308310
fn bad_recovery() {
309-
let mut s = Secp256k1::new();
310-
s.randomize(&mut thread_rng());
311+
let s = Secp256k1::new_randomize();
311312

312313
let msg = Message::from_slice(&[0x55; 32]).unwrap();
313314

@@ -379,7 +380,7 @@ mod benches {
379380

380381
#[bench]
381382
pub fn bench_recover(bh: &mut Bencher) {
382-
let s = Secp256k1::new();
383+
let s = Secp256k1::new_randomize();
383384
let mut msg = [0u8; 32];
384385
thread_rng().fill_bytes(&mut msg);
385386
let msg = Message::from_slice(&msg).unwrap();

0 commit comments

Comments
 (0)