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