5
5
extern crate std;
6
6
7
7
use crate :: Error ;
8
- use core:: mem:: MaybeUninit ;
8
+ use core:: {
9
+ mem:: MaybeUninit ,
10
+ sync:: atomic:: { AtomicU8 , Ordering } ,
11
+ } ;
9
12
use std:: thread_local;
10
13
11
14
pub use crate :: util:: { inner_u32, inner_u64} ;
12
15
13
16
#[ cfg( not( all( target_arch = "wasm32" , any( target_os = "unknown" , target_os = "none" ) ) ) ) ]
14
17
compile_error ! ( "`wasm_js` backend can be enabled only for OS-less WASM targets!" ) ;
15
18
16
- use js_sys:: Uint8Array ;
17
- use wasm_bindgen:: { prelude:: wasm_bindgen, JsValue } ;
19
+ use js_sys:: { SharedArrayBuffer , Uint8Array , WebAssembly :: Memory } ;
20
+ use wasm_bindgen:: { prelude:: wasm_bindgen, JsCast , JsValue } ;
18
21
19
22
// Size of our temporary Uint8Array buffer used with WebCrypto methods
20
23
// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
21
24
const CRYPTO_BUFFER_SIZE : u16 = 256 ;
22
25
26
+ const MEMORY_KIND_UNINIT : u8 = 0 ;
27
+ const MEMORY_KIND_NOT_SHARED : u8 = 1 ;
28
+ const MEMORY_KIND_SHARED : u8 = 2 ;
29
+
30
+ static MEMORY_KIND : AtomicU8 = AtomicU8 :: new ( 0 ) ;
31
+
23
32
pub fn fill_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
24
33
CRYPTO . with ( |crypto| {
25
34
let crypto = crypto. as_ref ( ) . ok_or ( Error :: WEB_CRYPTO ) ?;
26
35
27
- // getRandomValues does not work with all types of WASM memory,
28
- // so we initially write to browser memory to avoid exceptions.
29
- let buf = Uint8Array :: new_with_length ( CRYPTO_BUFFER_SIZE . into ( ) ) ;
30
- for chunk in dest. chunks_mut ( CRYPTO_BUFFER_SIZE . into ( ) ) {
31
- let chunk_len: u32 = chunk
32
- . len ( )
33
- . try_into ( )
34
- . expect ( "chunk length is bounded by CRYPTO_BUFFER_SIZE" ) ;
35
- // The chunk can be smaller than buf's length, so we call to
36
- // JS to create a smaller view of buf without allocation.
37
- let sub_buf = buf. subarray ( 0 , chunk_len) ;
38
-
39
- if crypto. get_random_values ( & sub_buf) . is_err ( ) {
40
- return Err ( Error :: WEB_GET_RANDOM_VALUES ) ;
41
- }
36
+ let shared = loop {
37
+ break match MEMORY_KIND . load ( Ordering :: Relaxed ) {
38
+ MEMORY_KIND_NOT_SHARED => false ,
39
+ MEMORY_KIND_SHARED => true ,
40
+ MEMORY_KIND_UNINIT => {
41
+ let memory: Memory = wasm_bindgen:: memory ( ) . unchecked_into ( ) ;
42
+ let val = if memory. buffer ( ) . is_instance_of :: < SharedArrayBuffer > ( ) {
43
+ MEMORY_KIND_SHARED
44
+ } else {
45
+ MEMORY_KIND_NOT_SHARED
46
+ } ;
47
+ MEMORY_KIND . store ( val, Ordering :: Relaxed ) ;
48
+ continue ;
49
+ }
50
+ _ => unreachable ! ( ) ,
51
+ } ;
52
+ } ;
53
+
54
+ if shared {
55
+ // getRandomValues does not work with all types of WASM memory,
56
+ // so we initially write to browser memory to avoid exceptions.
57
+ let buf = Uint8Array :: new_with_length ( CRYPTO_BUFFER_SIZE . into ( ) ) ;
58
+ for chunk in dest. chunks_mut ( CRYPTO_BUFFER_SIZE . into ( ) ) {
59
+ let chunk_len: u32 = chunk
60
+ . len ( )
61
+ . try_into ( )
62
+ . expect ( "chunk length is bounded by CRYPTO_BUFFER_SIZE" ) ;
63
+ // The chunk can be smaller than buf's length, so we call to
64
+ // JS to create a smaller view of buf without allocation.
65
+ let sub_buf = buf. subarray ( 0 , chunk_len) ;
42
66
43
- // SAFETY: `sub_buf`'s length is the same length as `chunk`
44
- unsafe { sub_buf. raw_copy_to_ptr ( chunk. as_mut_ptr ( ) . cast :: < u8 > ( ) ) } ;
67
+ if crypto. get_random_values ( & sub_buf) . is_err ( ) {
68
+ return Err ( Error :: WEB_GET_RANDOM_VALUES ) ;
69
+ }
70
+
71
+ // SAFETY: `sub_buf`'s length is the same length as `chunk`
72
+ unsafe { sub_buf. raw_copy_to_ptr ( chunk. as_mut_ptr ( ) . cast :: < u8 > ( ) ) } ;
73
+ }
74
+ } else {
75
+ for chunk in dest. chunks_mut ( CRYPTO_BUFFER_SIZE . into ( ) ) {
76
+ // SAFETY: this is only safe because on Wasm the issues with unitialized data don't exist
77
+ let buf = unsafe {
78
+ core:: slice:: from_raw_parts_mut ( chunk. as_mut_ptr ( ) . cast :: < u8 > ( ) , chunk. len ( ) )
79
+ } ;
80
+ if crypto. get_random_values_ref ( buf) . is_err ( ) {
81
+ return Err ( Error :: WEB_GET_RANDOM_VALUES ) ;
82
+ }
83
+ }
45
84
}
46
85
Ok ( ( ) )
47
86
} )
@@ -57,4 +96,6 @@ extern "C" {
57
96
// Crypto.getRandomValues()
58
97
#[ wasm_bindgen( method, js_name = getRandomValues, catch) ]
59
98
fn get_random_values ( this : & Crypto , buf : & Uint8Array ) -> Result < ( ) , JsValue > ;
99
+ #[ wasm_bindgen( method, js_name = getRandomValues, catch) ]
100
+ fn get_random_values_ref ( this : & Crypto , buf : & mut [ u8 ] ) -> Result < ( ) , JsValue > ;
60
101
}
0 commit comments