Skip to main content

bitwarden_random/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::convert::Infallible;
4
5use rand::TryRng;
6
7#[cfg(feature = "uniffi")]
8uniffi::setup_scaffolding!();
9
10mod ffi;
11pub use ffi::{GenBytesError, GenRangeError, SdkRandomNumberClient};
12
13#[cfg(feature = "dangerous-seeded-rng-for-testing")]
14mod seeded;
15#[cfg(feature = "dangerous-seeded-rng-for-testing")]
16pub use seeded::{Scene, clear_seed, rng_set_scene, set_seed_raw};
17
18#[cfg(feature = "snow")]
19mod snow_resolver;
20#[cfg(feature = "snow")]
21pub use snow_resolver::SdkCryptoResolver;
22
23/// Marker trait for random number generators usable within the Bitwarden SDK.
24///
25/// Supertrait of [`rand::CryptoRng`] (hence also [`rand::Rng`] / [`rand::RngExt`]) and additionally
26/// requires `Send + Sync`, so any `SdkRng` is a drop-in wherever the SDK or third-party crates
27/// expect a cryptographically secure, thread-safe RNG.
28pub trait SdkRng: rand::CryptoRng + Send + Sync {}
29
30impl<T: rand::CryptoRng + Send + Sync + ?Sized> SdkRng for T {}
31
32/// The default Bitwarden RNG.
33#[derive(Clone, Debug)]
34pub struct SdkRngImpl(Inner);
35
36#[derive(Clone, Debug)]
37enum Inner {
38    /// OS entropy source.
39    Os,
40    /// Draws from the thread-local stream installed by [`set_seed_raw`]. Carries no data, so the
41    /// struct stays `Send + Sync`.
42    #[cfg(feature = "dangerous-seeded-rng-for-testing")]
43    Seeded,
44}
45
46impl Default for SdkRngImpl {
47    fn default() -> Self {
48        Self(Inner::Os)
49    }
50}
51
52impl SdkRngImpl {
53    /// Creates a new OS-backed RNG.
54    pub fn new() -> Self {
55        Self::default()
56    }
57}
58
59/// Returns an [`SdkRngImpl`]. Mirrors [`rand::rng`] for ergonomic drop-in replacement.
60pub fn rng() -> SdkRngImpl {
61    #[cfg(feature = "dangerous-seeded-rng-for-testing")]
62    if seeded::is_seeded() {
63        return SdkRngImpl(Inner::Seeded);
64    }
65    SdkRngImpl::default()
66}
67
68impl TryRng for SdkRngImpl {
69    type Error = Infallible;
70
71    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
72        match &self.0 {
73            Inner::Os => Ok(rand::rngs::SysRng
74                .try_next_u32()
75                .expect("system RNG must not fail")),
76            #[cfg(feature = "dangerous-seeded-rng-for-testing")]
77            Inner::Seeded => Ok(seeded::next_u32()),
78        }
79    }
80
81    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
82        match &self.0 {
83            Inner::Os => Ok(rand::rngs::SysRng
84                .try_next_u64()
85                .expect("system RNG must not fail")),
86            #[cfg(feature = "dangerous-seeded-rng-for-testing")]
87            Inner::Seeded => Ok(seeded::next_u64()),
88        }
89    }
90
91    fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
92        match &self.0 {
93            Inner::Os => rand::rngs::SysRng
94                .try_fill_bytes(dst)
95                .expect("system RNG must not fail"),
96            #[cfg(feature = "dangerous-seeded-rng-for-testing")]
97            Inner::Seeded => seeded::fill_bytes(dst),
98        }
99        Ok(())
100    }
101}
102
103impl rand::TryCryptoRng for SdkRngImpl {}
104
105#[cfg(test)]
106mod tests {
107    use rand::RngExt;
108
109    use super::*;
110
111    // Compile-time checks: SdkRngImpl satisfies SdkRng + third-party bounds, and is Send + Sync.
112    fn _assert_bounds<T: SdkRng + rand::CryptoRng + rand::Rng + Send + Sync>() {}
113    const _: fn() = || _assert_bounds::<SdkRngImpl>();
114
115    #[test]
116    fn os_rng_produces_distinct_bytes() {
117        let mut rng = rng();
118        let a: [u8; 32] = rng.random();
119        let b: [u8; 32] = rng.random();
120        assert_ne!(a, b);
121    }
122}