Skip to content

Commit dcba49c

Browse files
committed
context: introduce global rerandomizable context (std only)
This introduces the new global context API when std is enabled, using thread locals to allow rerandomizing the context after sensitive operations. As you can see, even the simple case involves some unsafe code and is a bit tricky to implement.
1 parent f4c13b7 commit dcba49c

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed

src/context.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,108 @@ use crate::ffi::types::{c_uint, c_void, AlignedType};
1010
use crate::ffi::{self, CPtr};
1111
use crate::{Error, Secp256k1};
1212

13+
#[cfg(feature = "std")]
14+
mod internal {
15+
use std::cell::RefCell;
16+
use std::marker::PhantomData;
17+
use std::mem::ManuallyDrop;
18+
use std::ptr::NonNull;
19+
20+
use secp256k1_sys as ffi;
21+
22+
use crate::{All, Context, Secp256k1};
23+
24+
thread_local! {
25+
static SECP256K1: RefCell<Secp256k1<All>> = RefCell::new(Secp256k1::new());
26+
static RAND_SEED: RefCell<[u8; 32]> = const { RefCell::new([0; 32]) };
27+
}
28+
29+
/// Borrows the global context and do some operation on it.
30+
///
31+
/// If provided, after the operation is complete, [`rerandomize_global_context`]
32+
/// is called on the context. If you have some random data available,
33+
pub fn with_global_context<T, Ctx: Context, F: FnOnce(&Secp256k1<Ctx>) -> T>(
34+
f: F,
35+
rerandomize_seed: Option<&[u8; 32]>,
36+
) -> T {
37+
with_raw_global_context(
38+
|ctx| {
39+
let secp = ManuallyDrop::new(Secp256k1 { ctx, phantom: PhantomData });
40+
f(&*secp)
41+
},
42+
rerandomize_seed,
43+
)
44+
}
45+
46+
/// Borrows the global context as a raw pointer and do some operation on it.
47+
///
48+
/// If provided, after the operation is complete, [`rerandomize_global_context`]
49+
/// is called on the context. If you have some random data available,
50+
pub fn with_raw_global_context<T, F: FnOnce(NonNull<ffi::Context>) -> T>(
51+
f: F,
52+
rerandomize_seed: Option<&[u8; 32]>,
53+
) -> T {
54+
SECP256K1.with(|secp| {
55+
let borrow = secp.borrow();
56+
let ret = f(borrow.ctx);
57+
drop(borrow);
58+
59+
if let Some(seed) = rerandomize_seed {
60+
rerandomize_global_context(seed);
61+
}
62+
ret
63+
})
64+
}
65+
66+
/// Rerandomize the global context, using the given data as a seed.
67+
///
68+
/// The provided data will be mixed with the entropy from previous calls in a timing
69+
/// analysis resistant way. It is safe to directly pass secret data to this function.
70+
pub fn rerandomize_global_context(seed: &[u8; 32]) {
71+
SECP256K1.with(|secp| {
72+
RAND_SEED.with(|rand_seed| {
73+
let mut old_seed = rand_seed.borrow_mut();
74+
let mut new_seed = [0; 32];
75+
// We don't have direct access to sha256 except through the default nonce
76+
// functions. The ECDSA one uses RFC6979 which is absurdly slow, but the
77+
// Schnorr one is not too bad.
78+
unsafe {
79+
assert_eq!(
80+
(ffi::secp256k1_nonce_function_bip340.unwrap())(
81+
new_seed.as_mut_ptr(),
82+
seed.as_ptr(), // msg
83+
seed.len(), // msg len
84+
old_seed.as_ptr(), // key32
85+
old_seed.as_ptr(), // xonly_pk32
86+
b"rust-secp-randomize".as_ptr(),
87+
b"rust-secp-randomize".len(),
88+
core::ptr::null_mut(),
89+
),
90+
1,
91+
"calling bip340 nonce function failed",
92+
);
93+
}
94+
95+
// If we have access to the thread rng then use it as well.
96+
#[cfg(feature = "rand")]
97+
{
98+
let mask: [u8; 32] = rand::random();
99+
for (byte, mask) in new_seed.iter_mut().zip(mask.iter()) {
100+
*byte ^= *mask;
101+
}
102+
}
103+
104+
// Actual rerandomization
105+
let mut borrow = secp.borrow_mut();
106+
borrow.seeded_randomize(&new_seed);
107+
old_seed.copy_from_slice(&new_seed);
108+
});
109+
});
110+
}
111+
}
112+
#[cfg(feature = "std")]
113+
pub use internal::{rerandomize_global_context, with_global_context, with_raw_global_context};
114+
13115
#[cfg(all(feature = "global-context", feature = "std"))]
14116
/// Module implementing a singleton pattern for a global `Secp256k1` context.
15117
pub mod global {

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ pub use secp256k1_sys as ffi;
184184
#[cfg(feature = "serde")]
185185
pub use serde;
186186

187+
#[cfg(feature = "std")]
188+
pub use crate::context::{
189+
rerandomize_global_context, with_global_context, with_raw_global_context,
190+
};
187191
#[cfg(feature = "alloc")]
188192
pub use crate::context::{All, SignOnly, VerifyOnly};
189193
pub use crate::context::{

0 commit comments

Comments
 (0)