5
5
#[ cfg( feature = "std" ) ]
6
6
extern crate std;
7
7
8
- use crate :: Error ;
8
+ use crate :: { util , Error } ;
9
9
use core:: mem:: MaybeUninit ;
10
10
#[ cfg( feature = "std" ) ]
11
11
use std:: thread_local;
@@ -15,7 +15,7 @@ pub use crate::util::{inner_u32, inner_u64};
15
15
#[ cfg( not( all( target_arch = "wasm32" , any( target_os = "unknown" , target_os = "none" ) ) ) ) ]
16
16
compile_error ! ( "`wasm_js` backend can be enabled only for OS-less WASM targets!" ) ;
17
17
18
- use js_sys:: Uint8Array ;
18
+ use js_sys:: { JsString , Uint8Array } ;
19
19
use wasm_bindgen:: { prelude:: wasm_bindgen, JsValue } ;
20
20
21
21
// Size of our temporary Uint8Array buffer used with WebCrypto methods
@@ -26,34 +26,81 @@ pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
26
26
CRYPTO . with ( |crypto| {
27
27
let crypto = crypto. as_ref ( ) . ok_or ( Error :: WEB_CRYPTO ) ?;
28
28
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
+ }
48
49
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
+ }
51
63
}
52
64
Ok ( ( ) )
53
65
} )
54
66
}
55
67
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
+
56
102
#[ wasm_bindgen]
103
+ #[ rustfmt:: skip]
57
104
extern "C" {
58
105
// Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
59
106
type Crypto ;
@@ -63,4 +110,12 @@ extern "C" {
63
110
// Crypto.getRandomValues()
64
111
#[ wasm_bindgen( method, js_name = getRandomValues, catch) ]
65
112
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" ;
66
121
}
0 commit comments