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