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