1 // Copyright 2018 Developers of the Rand project.
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
9 //! Implementation for WASM via stdweb
15 use stdweb
::unstable
::TryInto
;
16 use stdweb
::web
::error
::Error
as WebError
;
18 use crate::error
::{STDWEB_NO_RNG, STDWEB_RNG_FAILED}
;
22 #[derive(Clone, Copy, Debug)]
28 pub fn getrandom_inner(dest
: &mut [u8]) -> Result
<(), Error
> {
29 assert_eq
!(mem
::size_of
::<usize>(), 4);
30 static ONCE
: Once
= Once
::new();
31 static mut RNG_SOURCE
: Result
<RngSource
, Error
> = Ok(RngSource
::Node
);
33 // SAFETY: RNG_SOURCE is only written once, before being read.
34 ONCE
.call_once(|| unsafe {
35 RNG_SOURCE
= getrandom_init();
37 getrandom_fill(unsafe { RNG_SOURCE }?
, dest
)
40 fn getrandom_init() -> Result
<RngSource
, Error
> {
44 typeof self === "object" &&
45 typeof self.crypto
=== "object" &&
46 typeof self.crypto
.getRandomValues
=== "function"
48 return { success: true, ty: 1 }
;
51 if (typeof require("crypto").randomBytes
=== "function") {
52 return { success: true, ty: 2 }
;
55 return { success: false, error: new Error("not supported") }
;
57 return { success: false, error: err }
;
61 if js
! { return @{ result.as_ref() }
.success
} == true {
62 let ty
= js
! { return @{ result }
.ty
};
65 Ok(RngSource
::Browser
)
72 let _err
: WebError
= js
! { return @{ result }
.error
}.try_into().unwrap();
73 error
!("getrandom unavailable: {}", _err
);
78 fn getrandom_fill(source
: RngSource
, dest
: &mut [u8]) -> Result
<(), Error
> {
79 for chunk
in dest
.chunks_mut(65536) {
80 let len
= chunk
.len() as u32;
81 let ptr
= chunk
.as_mut_ptr() as i32;
83 let result
= match source
{
84 RngSource
::Browser
=> js
! {
86 let array
= new
Uint8Array(@{ len }
);
87 self.crypto
.getRandomValues(array
);
88 HEAPU8
.set(array
, @{ ptr }
);
90 return { success: true }
;
92 return { success: false, error: err }
;
95 RngSource
::Node
=> js
! {
97 let bytes
= require("crypto").randomBytes(@{ len }
);
98 HEAPU8
.set(new
Uint8Array(bytes
), @{ ptr }
);
100 return { success: true }
;
102 return { success: false, error: err }
;
107 if js
! { return @{ result.as_ref() }
.success
} != true {
108 let _err
: WebError
= js
! { return @{ result }
.error
}.try_into().unwrap();
109 error
!("getrandom failed: {}", _err
);
110 return Err(STDWEB_RNG_FAILED
);