bitwarden_random/
seeded.rs1use rand::SeedableRng;
4
5thread_local! {
6 static SEEDED_STREAM: core::cell::RefCell<Option<rand_chacha::ChaCha8Rng>> =
9 const { core::cell::RefCell::new(None) };
10}
11
12pub fn set_seed_raw(seed: [u8; 32]) {
15 SEEDED_STREAM.with(|c| *c.borrow_mut() = Some(rand_chacha::ChaCha8Rng::from_seed(seed)));
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum Scene {
21 SceneA,
23 SceneB,
25 SceneC,
27}
28
29pub fn rng_set_scene(scene: Scene) {
33 set_seed_raw([scene as u8; 32]);
34}
35
36pub fn clear_seed() {
38 SEEDED_STREAM.with(|c| *c.borrow_mut() = None);
39}
40
41pub(crate) fn is_seeded() -> bool {
42 SEEDED_STREAM.with(|c| c.borrow().is_some())
43}
44
45pub(crate) fn next_u32() -> u32 {
46 SEEDED_STREAM
47 .with(|c| rand::Rng::next_u32(c.borrow_mut().as_mut().expect("seed set for this thread")))
48}
49
50pub(crate) fn next_u64() -> u64 {
51 SEEDED_STREAM
52 .with(|c| rand::Rng::next_u64(c.borrow_mut().as_mut().expect("seed set for this thread")))
53}
54
55pub(crate) fn fill_bytes(dst: &mut [u8]) {
56 SEEDED_STREAM.with(|c| {
57 rand::Rng::fill_bytes(
58 c.borrow_mut().as_mut().expect("seed set for this thread"),
59 dst,
60 )
61 });
62}
63
64#[cfg(test)]
65mod tests {
66 use rand::RngExt;
67
68 use super::*;
69 use crate::rng;
70
71 #[test]
72 fn set_seed_makes_rng_calls_deterministic_and_advancing() {
73 set_seed_raw([7u8; 32]);
75 let a: [u8; 16] = rng().random();
76 let b: [u8; 16] = rng().random();
77 assert_ne!(a, b, "successive rng() must not repeat (no IV/nonce reuse)");
78
79 set_seed_raw([7u8; 32]);
81 let a2: [u8; 16] = rng().random();
82 let b2: [u8; 16] = rng().random();
83 assert_eq!((a, b), (a2, b2));
84
85 clear_seed();
86 }
87
88 #[test]
89 fn rng_set_scene_is_deterministic_and_distinct_per_scene() {
90 rng_set_scene(Scene::SceneA);
91 let a1: [u8; 16] = rng().random();
92 rng_set_scene(Scene::SceneA);
93 let a2: [u8; 16] = rng().random();
94 assert_eq!(a1, a2, "same scene must reproduce the same stream");
95
96 rng_set_scene(Scene::SceneB);
97 let b: [u8; 16] = rng().random();
98 assert_ne!(a1, b, "different scenes must use different seeds");
99
100 clear_seed();
101 }
102}