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