55#[ cfg( feature = "std" ) ]
66extern crate std;
77
8- use crate :: Error ;
8+ use crate :: { util , Error } ;
99use core:: mem:: MaybeUninit ;
1010#[ cfg( feature = "std" ) ]
1111use std:: thread_local;
@@ -15,7 +15,7 @@ pub use crate::util::{inner_u32, inner_u64};
1515#[ cfg( not( all( target_arch = "wasm32" , any( target_os = "unknown" , target_os = "none" ) ) ) ) ]
1616compile_error ! ( "`wasm_js` backend can be enabled only for OS-less WASM targets!" ) ;
1717
18- use js_sys:: Uint8Array ;
18+ use js_sys:: { JsString , Uint8Array } ;
1919use wasm_bindgen:: { prelude:: wasm_bindgen, JsValue } ;
2020
2121// Size of our temporary Uint8Array buffer used with WebCrypto methods
@@ -26,34 +26,81 @@ pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
2626 CRYPTO . with ( |crypto| {
2727 let crypto = crypto. as_ref ( ) . ok_or ( Error :: WEB_CRYPTO ) ?;
2828
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 = if chunk_len == u32:: from ( CRYPTO_BUFFER_SIZE ) {
40- buf. clone ( )
41- } else {
42- buf. subarray ( 0 , chunk_len)
43- } ;
44-
45- if crypto. get_random_values ( & sub_buf) . is_err ( ) {
46- return Err ( Error :: WEB_GET_RANDOM_VALUES ) ;
47- }
29+ if is_sab ( ) {
30+ // getRandomValues does not work with all types of WASM memory,
31+ // so we initially write to browser memory to avoid exceptions.
32+ let buf = Uint8Array :: new_with_length ( CRYPTO_BUFFER_SIZE . into ( ) ) ;
33+ for chunk in dest. chunks_mut ( CRYPTO_BUFFER_SIZE . into ( ) ) {
34+ let chunk_len: u32 = chunk
35+ . len ( )
36+ . try_into ( )
37+ . expect ( "chunk length is bounded by CRYPTO_BUFFER_SIZE" ) ;
38+ // The chunk can be smaller than buf's length, so we call to
39+ // JS to create a smaller view of buf without allocation.
40+ let sub_buf = if chunk_len == u32:: from ( CRYPTO_BUFFER_SIZE ) {
41+ buf. clone ( )
42+ } else {
43+ buf. subarray ( 0 , chunk_len)
44+ } ;
45+
46+ if crypto. get_random_values ( & sub_buf) . is_err ( ) {
47+ return Err ( Error :: WEB_GET_RANDOM_VALUES ) ;
48+ }
4849
49- // SAFETY: `sub_buf`'s length is the same length as `chunk`
50- unsafe { sub_buf. raw_copy_to_ptr ( chunk. as_mut_ptr ( ) . cast :: < u8 > ( ) ) } ;
50+ // SAFETY: `sub_buf`'s length is the same length as `chunk`
51+ unsafe { sub_buf. raw_copy_to_ptr ( chunk. as_mut_ptr ( ) . cast :: < u8 > ( ) ) } ;
52+ }
53+ } else {
54+ for chunk in dest. chunks_mut ( CRYPTO_BUFFER_SIZE . into ( ) ) {
55+ // SAFETY: this is only safe because on Wasm the issues with unitialized data don't exist
56+ if crypto
57+ . get_random_values_ref ( unsafe { util:: slice_assume_init_mut ( chunk) } )
58+ . is_err ( )
59+ {
60+ return Err ( Error :: WEB_GET_RANDOM_VALUES ) ;
61+ }
62+ }
5163 }
5264 Ok ( ( ) )
5365 } )
5466}
5567
68+ #[ cfg( not( target_feature = "atomics" ) ) ]
69+ fn is_sab ( ) -> bool {
70+ use js_sys:: WebAssembly :: Memory ;
71+ use js_sys:: { Object , SharedArrayBuffer } ;
72+ use wasm_bindgen:: JsCast ;
73+
74+ let buffer: Object = wasm_bindgen:: memory ( )
75+ . unchecked_into :: < Memory > ( )
76+ . buffer ( )
77+ . unchecked_into ( ) ;
78+
79+ // `crossOriginIsolated` is not available on Node.js and Safari <v15.2.
80+ if let Some ( true ) = CROSS_ORIGIN_ISOLATED . with ( Option :: clone) {
81+ buffer. is_instance_of :: < SharedArrayBuffer > ( )
82+ } else {
83+ // `SharedArrayBuffer` is only available with COOP & COEP. But even
84+ // without its possible to create a shared `WebAssembly.Memory`, so we
85+ // check for that via the constructor name.
86+
87+ let constructor_name = buffer. constructor ( ) . name ( ) ;
88+
89+ if SHARED_ARRAY_BUFFER_NAME . with ( |sab_name| & constructor_name == sab_name) {
90+ true
91+ } else {
92+ false
93+ }
94+ }
95+ }
96+
97+ #[ cfg( target_feature = "atomics" ) ]
98+ fn is_sab ( ) -> bool {
99+ true
100+ }
101+
56102#[ wasm_bindgen]
103+ #[ rustfmt:: skip]
57104extern "C" {
58105 // Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
59106 type Crypto ;
@@ -63,4 +110,12 @@ extern "C" {
63110 // Crypto.getRandomValues()
64111 #[ wasm_bindgen( method, js_name = getRandomValues, catch) ]
65112 fn get_random_values ( this : & Crypto , buf : & Uint8Array ) -> Result < ( ) , JsValue > ;
113+ #[ wasm_bindgen( method, js_name = getRandomValues, catch) ]
114+ fn get_random_values_ref ( this : & Crypto , buf : & mut [ u8 ] ) -> Result < ( ) , JsValue > ;
115+ // Holds the `crossOriginIsolated` (https://developer.mozilla.org/en-US/docs/Web/API/crossOriginIsolated) global property.
116+ #[ wasm_bindgen( thread_local_v2, js_namespace = globalThis, js_name = crossOriginIsolated) ]
117+ static CROSS_ORIGIN_ISOLATED : Option < bool > ;
118+ // Holds the constructor name of the `SharedArrayBuffer` class.
119+ #[ wasm_bindgen( thread_local_v2, static_string) ]
120+ static SHARED_ARRAY_BUFFER_NAME : JsString = "SharedArrayBuffer" ;
66121}
0 commit comments