1 // Copyright 2018 Developers of the Rand project.
2 //
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.
8 
9 //! Implementation for WASM via stdweb
10 
11 use std::mem;
12 use stdweb::unstable::TryInto;
13 use stdweb::web::error::Error as WebError;
14 use rand_core::{Error, ErrorKind};
15 use super::OsRngImpl;
16 
17 #[derive(Clone, Debug)]
18 enum OsRngMethod {
19     Browser,
20     Node
21 }
22 
23 #[derive(Clone, Debug)]
24 pub struct OsRng(OsRngMethod);
25 
26 impl OsRngImpl for OsRng {
new() -> Result<OsRng, Error>27     fn new() -> Result<OsRng, Error> {
28         let result = js! {
29             try {
30                 if (
31                     typeof self === "object" &&
32                     typeof self.crypto === "object" &&
33                     typeof self.crypto.getRandomValues === "function"
34                 ) {
35                     return { success: true, ty: 1 };
36                 }
37 
38                 if (typeof require("crypto").randomBytes === "function") {
39                     return { success: true, ty: 2 };
40                 }
41 
42                 return { success: false, error: new Error("not supported") };
43             } catch(err) {
44                 return { success: false, error: err };
45             }
46         };
47 
48         if js!{ return @{ result.as_ref() }.success } == true {
49             let ty = js!{ return @{ result }.ty };
50 
51             if ty == 1 { Ok(OsRng(OsRngMethod::Browser)) }
52             else if ty == 2 { Ok(OsRng(OsRngMethod::Node)) }
53             else { unreachable!() }
54         } else {
55             let err: WebError = js!{ return @{ result }.error }.try_into().unwrap();
56             Err(Error::with_cause(ErrorKind::Unavailable, "WASM Error", err))
57         }
58     }
59 
60 
fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error>61     fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> {
62         assert_eq!(mem::size_of::<usize>(), 4);
63 
64         let len = dest.len() as u32;
65         let ptr = dest.as_mut_ptr() as i32;
66 
67         let result = match self.0 {
68             OsRngMethod::Browser => js! {
69                 try {
70                     let array = new Uint8Array(@{ len });
71                     self.crypto.getRandomValues(array);
72                     HEAPU8.set(array, @{ ptr });
73 
74                     return { success: true };
75                 } catch(err) {
76                     return { success: false, error: err };
77                 }
78             },
79             OsRngMethod::Node => js! {
80                 try {
81                     let bytes = require("crypto").randomBytes(@{ len });
82                     HEAPU8.set(new Uint8Array(bytes), @{ ptr });
83 
84                     return { success: true };
85                 } catch(err) {
86                     return { success: false, error: err };
87                 }
88             }
89         };
90 
91         if js!{ return @{ result.as_ref() }.success } == true {
92             Ok(())
93         } else {
94             let err: WebError = js!{ return @{ result }.error }.try_into().unwrap();
95             Err(Error::with_cause(ErrorKind::Unexpected, "WASM Error", err))
96         }
97     }
98 
max_chunk_size(&self) -> usize99     fn max_chunk_size(&self) -> usize { 65536 }
100 
method_str(&self) -> &'static str101     fn method_str(&self) -> &'static str {
102         match self.0 {
103             OsRngMethod::Browser => "Crypto.getRandomValues",
104             OsRngMethod::Node => "crypto.randomBytes",
105         }
106     }
107 }
108