uuid/
rng.rs

1#![allow(dead_code, unused_imports)] // Keeps our cfg's from becoming too convoluted in here
2
3trait Rng {
4    fn u128() -> u128;
5    fn u64() -> u64;
6    fn u16() -> u16;
7}
8
9pub(crate) fn u128() -> u128 {
10    imp::RngImp::u128()
11}
12
13pub(crate) fn u64() -> u64 {
14    imp::RngImp::u64()
15}
16
17pub(crate) fn u16() -> u16 {
18    imp::RngImp::u16()
19}
20
21#[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))]
22mod imp {
23    /*
24    Random support for non `wasm32-unknown-unknown` platforms.
25    */
26
27    use super::*;
28
29    // Using `rand`
30    #[cfg(any(feature = "rng-rand", feature = "fast-rng"))]
31    pub(super) struct RngImp;
32
33    #[cfg(any(feature = "rng-rand", feature = "fast-rng"))]
34    impl Rng for RngImp {
35        fn u128() -> u128 {
36            rand::random()
37        }
38
39        fn u64() -> u64 {
40            rand::random()
41        }
42
43        fn u16() -> u16 {
44            rand::random()
45        }
46    }
47
48    // Using `getrandom`
49    #[cfg(all(not(feature = "fast-rng"), not(feature = "rng-rand")))]
50    pub(super) struct RngImp;
51
52    #[cfg(all(not(feature = "fast-rng"), not(feature = "rng-rand")))]
53    impl Rng for RngImp {
54        fn u128() -> u128 {
55            let mut bytes = [0u8; 16];
56
57            getrandom::fill(&mut bytes).unwrap_or_else(|err| {
58                // NB: getrandom::Error has no source; this is adequate display
59                panic!("could not retrieve random bytes for uuid: {}", err)
60            });
61
62            u128::from_ne_bytes(bytes)
63        }
64
65        fn u64() -> u64 {
66            let mut bytes = [0u8; 8];
67
68            getrandom::fill(&mut bytes).unwrap_or_else(|err| {
69                // NB: getrandom::Error has no source; this is adequate display
70                panic!("could not retrieve random bytes for uuid: {}", err)
71            });
72
73            u64::from_ne_bytes(bytes)
74        }
75
76        fn u16() -> u16 {
77            let mut bytes = [0u8; 2];
78
79            getrandom::fill(&mut bytes).unwrap_or_else(|err| {
80                // NB: getrandom::Error has no source; this is adequate display
81                panic!("could not retrieve random bytes for uuid: {}", err)
82            });
83
84            u16::from_ne_bytes(bytes)
85        }
86    }
87}
88
89#[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
90mod imp {
91    /*
92    Random support for `wasm32-unknown-unknown`.
93    */
94
95    #![allow(dead_code, unused_imports)] // Keeps our cfg's from becoming too convoluted in here
96
97    use super::*;
98
99    #[cfg(all(
100        not(feature = "js"),
101        not(feature = "rng-getrandom"),
102        not(feature = "rng-rand")
103    ))]
104    compile_error!("to use `uuid` on `wasm32-unknown-unknown`, specify a source of randomness using one of the `js`, `rng-getrandom`, or `rng-rand` features");
105
106    // Using `rand`
107    #[cfg(feature = "rng-rand")]
108    pub(super) struct RngImp;
109
110    #[cfg(feature = "rng-rand")]
111    impl Rng for RngImp {
112        fn u128() -> u128 {
113            uuid_rng_internal_lib::__private::rand::random()
114        }
115
116        fn u64() -> u64 {
117            uuid_rng_internal_lib::__private::rand::random()
118        }
119
120        fn u16() -> u16 {
121            uuid_rng_internal_lib::__private::rand::random()
122        }
123    }
124
125    // Using `getrandom`
126    #[cfg(all(feature = "rng-getrandom", not(feature = "rng-rand")))]
127    pub(super) struct RngImp;
128
129    #[cfg(all(feature = "rng-getrandom", not(feature = "rng-rand")))]
130    impl Rng for RngImp {
131        fn u128() -> u128 {
132            let mut bytes = [0u8; 16];
133
134            uuid_rng_internal_lib::__private::getrandom::fill(&mut bytes).unwrap_or_else(|err| {
135                // NB: getrandom::Error has no source; this is adequate display
136                panic!("could not retrieve random bytes for uuid: {}", err)
137            });
138
139            u128::from_ne_bytes(bytes)
140        }
141
142        fn u64() -> u64 {
143            let mut bytes = [0u8; 8];
144
145            uuid_rng_internal_lib::__private::getrandom::fill(&mut bytes).unwrap_or_else(|err| {
146                // NB: getrandom::Error has no source; this is adequate display
147                panic!("could not retrieve random bytes for uuid: {}", err)
148            });
149
150            u64::from_ne_bytes(bytes)
151        }
152
153        fn u16() -> u16 {
154            let mut bytes = [0u8; 2];
155
156            uuid_rng_internal_lib::__private::getrandom::fill(&mut bytes).unwrap_or_else(|err| {
157                // NB: getrandom::Error has no source; this is adequate display
158                panic!("could not retrieve random bytes for uuid: {}", err)
159            });
160
161            u16::from_ne_bytes(bytes)
162        }
163    }
164
165    // Using WebCrypto via `wasm-bindgen`
166    #[cfg(all(
167        feature = "js",
168        not(feature = "rng-rand"),
169        not(feature = "rng-getrandom")
170    ))]
171    pub(super) struct RngImp;
172
173    #[cfg(all(
174        feature = "js",
175        not(feature = "rng-rand"),
176        not(feature = "rng-getrandom")
177    ))]
178    impl Rng for RngImp {
179        fn u128() -> u128 {
180            let mut bytes = [0u8; 16];
181
182            if !webcrypto::fill(&mut bytes) {
183                panic!("could not retrieve random bytes for uuid")
184            }
185
186            u128::from_ne_bytes(bytes)
187        }
188
189        fn u64() -> u64 {
190            let mut bytes = [0u8; 8];
191
192            if !webcrypto::fill(&mut bytes) {
193                panic!("could not retrieve random bytes for uuid")
194            }
195
196            u64::from_ne_bytes(bytes)
197        }
198
199        fn u16() -> u16 {
200            let mut bytes = [0u8; 2];
201
202            if !webcrypto::fill(&mut bytes) {
203                panic!("could not retrieve random bytes for uuid")
204            }
205
206            u16::from_ne_bytes(bytes)
207        }
208    }
209
210    #[cfg(feature = "js")]
211    mod webcrypto {
212        /*
213        This module preserves the stabilized behavior of `uuid` that requires the
214        `js` feature to enable rng on `wasm32-unknown-unknown`, which it inherited
215        from `getrandom` `0.2`.
216
217        Vendored from `getrandom`: https://github.com/rust-random/getrandom/blob/ce3b017fdee0233c6ecd61e68b96a84bf6f911bf/src/backends/wasm_js.rs
218
219        Copyright (c) 2018-2024 The rust-random Project Developers
220        Copyright (c) 2014 The Rust Project Developers
221
222        Permission is hereby granted, free of charge, to any
223        person obtaining a copy of this software and associated
224        documentation files (the "Software"), to deal in the
225        Software without restriction, including without
226        limitation the rights to use, copy, modify, merge,
227        publish, distribute, sublicense, and/or sell copies of
228        the Software, and to permit persons to whom the Software
229        is furnished to do so, subject to the following
230        conditions:
231
232        The above copyright notice and this permission notice
233        shall be included in all copies or substantial portions
234        of the Software.
235
236        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
237        ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
238        TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
239        PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
240        SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
241        CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
242        OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
243        IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
244        DEALINGS IN THE SOFTWARE.
245        */
246
247        use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
248
249        #[cfg(target_feature = "atomics")]
250        use core::convert::TryInto;
251
252        // Maximum buffer size allowed in `Crypto.getRandomValuesSize` is 65536 bytes.
253        // See https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
254        const MAX_BUFFER_SIZE: usize = 65536;
255
256        #[cfg(not(target_feature = "atomics"))]
257        #[inline]
258        pub fn fill(dest: &mut [u8]) -> bool {
259            for chunk in dest.chunks_mut(MAX_BUFFER_SIZE) {
260                if get_random_values(chunk).is_err() {
261                    return false;
262                }
263            }
264
265            true
266        }
267
268        #[cfg(target_feature = "atomics")]
269        pub fn fill(dest: &mut [u8]) -> bool {
270            // getRandomValues does not work with all types of WASM memory,
271            // so we initially write to browser memory to avoid exceptions.
272            let buf_len = usize::min(dest.len(), MAX_BUFFER_SIZE);
273            let buf_len_u32 = buf_len
274                .try_into()
275                .expect("buffer length is bounded by MAX_BUFFER_SIZE");
276            let buf = js_sys::Uint8Array::new_with_length(buf_len_u32);
277            for chunk in dest.chunks_mut(buf_len) {
278                let chunk_len = chunk
279                    .len()
280                    .try_into()
281                    .expect("chunk length is bounded by MAX_BUFFER_SIZE");
282                // The chunk can be smaller than buf's length, so we call to
283                // JS to create a smaller view of buf without allocation.
284                let sub_buf = if chunk_len == buf_len_u32 {
285                    &buf
286                } else {
287                    &buf.subarray(0, chunk_len)
288                };
289
290                if get_random_values(sub_buf).is_err() {
291                    return false;
292                }
293
294                sub_buf.copy_to(chunk);
295            }
296
297            true
298        }
299
300        #[wasm_bindgen]
301        extern "C" {
302            // Crypto.getRandomValues()
303            #[cfg(not(target_feature = "atomics"))]
304            #[wasm_bindgen(js_namespace = ["globalThis", "crypto"], js_name = getRandomValues, catch)]
305            fn get_random_values(buf: &mut [u8]) -> Result<(), JsValue>;
306            #[cfg(target_feature = "atomics")]
307            #[wasm_bindgen(js_namespace = ["globalThis", "crypto"], js_name = getRandomValues, catch)]
308            fn get_random_values(buf: &js_sys::Uint8Array) -> Result<(), JsValue>;
309        }
310    }
311}