55extern crate std;
66
77use crate :: Error ;
8- use core:: mem:: MaybeUninit ;
8+ use core:: {
9+ mem:: MaybeUninit ,
10+ sync:: atomic:: { AtomicU8 , Ordering } ,
11+ } ;
912use std:: thread_local;
1013
1114pub use crate :: util:: { inner_u32, inner_u64} ;
1215
1316#[ cfg( not( all( target_arch = "wasm32" , any( target_os = "unknown" , target_os = "none" ) ) ) ) ]
1417compile_error ! ( "`wasm_js` backend can be enabled only for OS-less WASM targets!" ) ;
1518
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 } ;
1821
1922// Size of our temporary Uint8Array buffer used with WebCrypto methods
2023// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
2124const CRYPTO_BUFFER_SIZE : u16 = 256 ;
2225
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+
2332pub fn fill_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
2433 CRYPTO . with ( |crypto| {
2534 let crypto = crypto. as_ref ( ) . ok_or ( Error :: WEB_CRYPTO ) ?;
2635
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) ;
4266
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+ }
4584 }
4685 Ok ( ( ) )
4786 } )
@@ -57,4 +96,6 @@ extern "C" {
5796 // Crypto.getRandomValues()
5897 #[ wasm_bindgen( method, js_name = getRandomValues, catch) ]
5998 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 > ;
60101}
0 commit comments