@@ -10,6 +10,258 @@ use crate::ffi::types::{c_uint, c_void, AlignedType};
10
10
use crate :: ffi:: { self , CPtr } ;
11
11
use crate :: { Error , Secp256k1 } ;
12
12
13
+ /// TODO: Rename to global and remove the other one.
14
+ #[ cfg( feature = "std" ) ]
15
+ pub mod _global {
16
+ use core:: convert:: TryFrom ;
17
+ use core:: sync:: atomic:: { AtomicBool , AtomicU8 , AtomicUsize , Ordering } ;
18
+ use std:: ops:: Deref ;
19
+ use std:: sync:: Once ;
20
+
21
+ use super :: alloc_only:: { SignOnly , VerifyOnly } ;
22
+ use crate :: ffi:: CPtr ;
23
+ use crate :: { ffi, Secp256k1 } ;
24
+
25
+ struct GlobalVerifyContext {
26
+ __private : ( ) ,
27
+ }
28
+
29
+ impl Deref for GlobalVerifyContext {
30
+ type Target = Secp256k1 < VerifyOnly > ;
31
+
32
+ fn deref ( & self ) -> & Self :: Target {
33
+ static ONCE : Once = Once :: new ( ) ;
34
+ static mut CONTEXT : Option < Secp256k1 < VerifyOnly > > = None ;
35
+ ONCE . call_once ( || unsafe {
36
+ let ctx = Secp256k1 :: verification_only ( ) ;
37
+ CONTEXT = Some ( ctx) ;
38
+ } ) ;
39
+ unsafe { CONTEXT . as_ref ( ) . unwrap ( ) }
40
+ }
41
+ }
42
+
43
+ struct GlobalSignContext {
44
+ __private : ( ) ,
45
+ }
46
+
47
+ impl Deref for GlobalSignContext {
48
+ type Target = Secp256k1 < SignOnly > ;
49
+
50
+ fn deref ( & self ) -> & Self :: Target {
51
+ static ONCE : Once = Once :: new ( ) ;
52
+ static mut CONTEXT : Option < Secp256k1 < SignOnly > > = None ;
53
+ ONCE . call_once ( || unsafe {
54
+ let ctx = Secp256k1 :: signing_only ( ) ;
55
+ CONTEXT = Some ( ctx) ;
56
+ } ) ;
57
+ unsafe { CONTEXT . as_ref ( ) . unwrap ( ) }
58
+ }
59
+ }
60
+
61
+ static GLOBAL_VERIFY_CONTEXT : & GlobalVerifyContext = & GlobalVerifyContext { __private : ( ) } ;
62
+
63
+ static GLOBAL_SIGN_CONTEXTS : [ & GlobalSignContext ; 2 ] =
64
+ [ & GlobalSignContext { __private : ( ) } , & GlobalSignContext { __private : ( ) } ] ;
65
+
66
+ static SIGN_CONTEXTS_DIRTY : [ AtomicBool ; 2 ] = [ AtomicBool :: new ( false ) , AtomicBool :: new ( false ) ] ;
67
+
68
+ /// The sign contexts semaphore, stores two flags in the lowest bits and the reader count
69
+ /// in the remaining bits. Thus adding or subtracting 4 increments/decrements the counter.
70
+ ///
71
+ /// The two flags are:
72
+ /// * Active context bit - least significant (0b1)
73
+ /// * Swap bit - second least significant (0b10) (see [`needs_swap`]).
74
+ static SIGN_CONTEXTS_SEM : AtomicUsize = AtomicUsize :: new ( 0 ) ;
75
+
76
+ /// Re-randomization lock, true==locked, false==unlocked.
77
+ static RERAND_LOCK : AtomicBool = AtomicBool :: new ( false ) ;
78
+
79
+ /// Stores the seed for RNG. Notably it doesn't matter that a thread may read "inconsistent"
80
+ /// content because it's all random data. If the array is being overwritten while being read it
81
+ /// cannot worsen entropy and the exact data doesn't matter.
82
+ ///
83
+ /// We still have to use atomics because multiple mutable accesses is undefined behavior in Rust.
84
+ static GLOBAL_SEED : [ AtomicU8 ; 32 ] = init_seed_buffer ( ) ;
85
+
86
+ /// Rerandomizes inactive context using first half of `seed` and stores the second half in the
87
+ /// global seed buffer used for later rerandomizations.
88
+ pub fn reseed ( seed : & [ u8 ; 64 ] ) {
89
+ if rerand_lock ( ) {
90
+ let last = sign_contexts_inc ( ) ;
91
+ let other = 1 - active_context ( last) ;
92
+
93
+ _rerandomize ( other, <& [ u8 ; 32 ] >:: try_from ( & seed[ 0 ..32 ] ) . expect ( "32 bytes" ) ) ;
94
+ clear_context_dirty ( other) ;
95
+ rerand_unlock ( ) ;
96
+
97
+ sign_contexts_dec ( ) ;
98
+
99
+ // We unlock before setting the swap bit so that soon as another
100
+ // reader sees the swap bit set they can grab the rand lock.
101
+ sign_contexts_set_swap_bit ( ) ;
102
+ }
103
+ write_global_seed ( <& [ u8 ; 32 ] >:: try_from ( & seed[ 32 ..64 ] ) . expect ( "32 bytes" ) ) ;
104
+ }
105
+
106
+ /// Perform function using the current active global verification context.
107
+ ///
108
+ /// # Safety
109
+ ///
110
+ /// TODO: Write safety docs.
111
+ pub unsafe fn with_global_verify_context < F : FnOnce ( * const ffi:: Context ) -> R , R > ( f : F ) -> R {
112
+ f ( GLOBAL_VERIFY_CONTEXT . ctx . as_ptr ( ) )
113
+ }
114
+
115
+ /// Perform function using the current active global signing context.
116
+ ///
117
+ /// # Safety
118
+ ///
119
+ /// TODO: Write safety docs.
120
+ pub unsafe fn with_global_signing_context < F : FnOnce ( * const ffi:: Context ) -> R , R > ( f : F ) -> R {
121
+ let last = sign_contexts_inc ( ) ;
122
+
123
+ // Shift 2 for the 2 flag bits.
124
+ if last >= usize:: MAX >> 2 {
125
+ // Having this many threads should be impossible so if this happens it's because of a bug.
126
+ panic ! ( "too many readers" ) ;
127
+ }
128
+
129
+ let active = active_context ( last) ;
130
+
131
+ let res = f ( GLOBAL_SIGN_CONTEXTS [ active] . ctx . as_ptr ( ) ) ;
132
+ set_context_dirty ( active) ;
133
+
134
+ let last = sign_contexts_dec ( ) ;
135
+
136
+ // No readers and needs swap.
137
+ if last & !1 == 0b10 {
138
+ if let Some ( ctx) = sign_contexts_swap ( last) {
139
+ rerandomize_with_global_seed ( ctx) ;
140
+ }
141
+ }
142
+ res
143
+ }
144
+
145
+ /// Returns the index (into GLOBAL_SIGN_CONTEXTS) of the active context.
146
+ fn active_context ( sem : usize ) -> usize { sem & 1 }
147
+
148
+ /// Attempts to lock the rerand lock.
149
+ ///
150
+ /// # Returns
151
+ ///
152
+ /// `true` if lock was acquired, false otherwise.
153
+ fn rerand_lock ( ) -> bool {
154
+ RERAND_LOCK . compare_exchange ( false , true , Ordering :: Acquire , Ordering :: Relaxed ) . is_ok ( )
155
+ }
156
+
157
+ /// Attempts to unlock the rerand lock.
158
+ ///
159
+ /// # Returns
160
+ ///
161
+ /// `true` if the lock was unlocked by this operation.
162
+ fn rerand_unlock ( ) -> bool {
163
+ RERAND_LOCK . compare_exchange ( true , false , Ordering :: Acquire , Ordering :: Relaxed ) . is_ok ( )
164
+ }
165
+
166
+ /// Increments the sign-contexts reader semaphore.
167
+ // FIXME: What happens if we have more than usize::MAX >> 2 readers i.e., overflow?
168
+ fn sign_contexts_inc ( ) -> usize { SIGN_CONTEXTS_SEM . fetch_add ( 4 , Ordering :: Acquire ) }
169
+
170
+ /// Decrements the sign-contexts reader semaphore.
171
+ fn sign_contexts_dec ( ) -> usize { SIGN_CONTEXTS_SEM . fetch_sub ( 4 , Ordering :: Acquire ) }
172
+
173
+ /// Swap the active context and clear the swap bit.
174
+ ///
175
+ /// # Panics
176
+ ///
177
+ /// If `lock` has count > 0.
178
+ ///
179
+ /// # Returns
180
+ ///
181
+ /// The now-inactive context index (ie, the index of the context swapped out).
182
+ fn sign_contexts_swap ( sem : usize ) -> Option < usize > {
183
+ assert ! ( sem & !0b11 == 0 ) ; // reader count == 0
184
+ let new = ( sem & !0b10 ) ^ 0b01 ; // turn off swap bit, toggle active bit.
185
+ match SIGN_CONTEXTS_SEM . compare_exchange ( sem, new, Ordering :: Relaxed , Ordering :: Relaxed ) {
186
+ Ok ( last) => Some ( active_context ( last) ) ,
187
+ // Another reader signaled before we had a chance to swap.
188
+ Err ( _) => None ,
189
+ }
190
+ }
191
+
192
+ /// Unconditionally turns on the "needs swap" bit.
193
+ fn sign_contexts_set_swap_bit ( ) { SIGN_CONTEXTS_SEM . fetch_or ( 0b10 , Ordering :: Relaxed ) ; }
194
+
195
+ fn set_context_dirty ( ctx : usize ) {
196
+ assert ! ( ctx < 2 ) ;
197
+ SIGN_CONTEXTS_DIRTY [ ctx] . store ( true , Ordering :: Relaxed ) ;
198
+ }
199
+
200
+ fn clear_context_dirty ( ctx : usize ) {
201
+ assert ! ( ctx < 2 ) ;
202
+ SIGN_CONTEXTS_DIRTY [ ctx] . store ( true , Ordering :: Relaxed ) ;
203
+ }
204
+
205
+ fn write_global_seed ( seed : & [ u8 ; 32 ] ) {
206
+ for ( i, b) in seed. iter ( ) . enumerate ( ) {
207
+ GLOBAL_SEED [ i] . store ( * b, Ordering :: Relaxed ) ;
208
+ }
209
+ }
210
+
211
+ /// Rerandomize the global signing context using randomness in the global seed.
212
+ fn rerandomize_with_global_seed ( ctx : usize ) {
213
+ let mut buf = [ 0_u8 ; 32 ] ;
214
+ for ( i, b) in buf. iter_mut ( ) . enumerate ( ) {
215
+ let atomic = & GLOBAL_SEED [ i] ;
216
+ * b = atomic. load ( Ordering :: Relaxed ) ;
217
+ }
218
+ rerandomize ( ctx, & buf)
219
+ }
220
+
221
+ /// Rerandomize global context index `ctx` using randomness in `seed`.
222
+ fn rerandomize ( ctx : usize , seed : & [ u8 ; 32 ] ) {
223
+ assert ! ( ctx < 2 ) ;
224
+ if rerand_lock ( ) {
225
+ _rerandomize ( ctx, seed) ;
226
+ clear_context_dirty ( ctx) ;
227
+ rerand_unlock ( ) ;
228
+
229
+ // We unlock before setting the swap bit so that soon as another
230
+ // reader sees the swap bit set they can grab the rand lock.
231
+ sign_contexts_set_swap_bit ( ) ;
232
+ }
233
+ }
234
+
235
+ /// Should be called with the RERAND_LOCK held.
236
+ fn _rerandomize ( ctx : usize , seed : & [ u8 ; 32 ] ) {
237
+ let secp = GLOBAL_SIGN_CONTEXTS [ ctx] ;
238
+ unsafe {
239
+ let err = ffi:: secp256k1_context_randomize ( secp. ctx , seed. as_c_ptr ( ) ) ;
240
+ // This function cannot fail; it has an error return for future-proofing.
241
+ // We do not expose this error since it is impossible to hit, and we have
242
+ // precedent for not exposing impossible errors (for example in
243
+ // `PublicKey::from_secret_key` where it is impossible to create an invalid
244
+ // secret key through the API.)
245
+ // However, if this DOES fail, the result is potentially weaker side-channel
246
+ // resistance, which is deadly and undetectable, so we take out the entire
247
+ // thread to be on the safe side.
248
+ assert_eq ! ( err, 1 ) ;
249
+ }
250
+ }
251
+
252
+ // TODO: Find better way to do this.
253
+ #[ rustfmt:: skip]
254
+ const fn init_seed_buffer ( ) -> [ AtomicU8 ; 32 ] {
255
+ let buf: [ AtomicU8 ; 32 ] = [
256
+ AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) ,
257
+ AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) ,
258
+ AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) ,
259
+ AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) , AtomicU8 :: new ( 0 ) ,
260
+ ] ;
261
+ buf
262
+ }
263
+ }
264
+
13
265
#[ cfg( all( feature = "global-context" , feature = "std" ) ) ]
14
266
/// Module implementing a singleton pattern for a global `Secp256k1` context.
15
267
pub mod global {
0 commit comments