Skip to main content

zenith_foundation/
random.rs

1//! 统一随机源(全 workspace 唯一入口)
2//!
3//! 两类随机性严格分离:
4//! - [`random_u64`] / [`try_random_u64`]:CSPRNG(操作系统熵源),用于安全敏感场景
5//!   (TCP ISN、QUIC SCID、PATH_CHALLENGE、会话标识),禁止用伪随机替代
6//! - [`pseudo_random_u64`] / [`Splitmix64`]:线程本地 splitmix64 伪随机(种子一次性
7//!   取自 CSPRNG),用于性能敏感但无安全要求的场景(负载均衡 P2C、退避 jitter)
8//!
9//! 设计约束:
10//! - 热路径零系统调用:伪随机走线程本地状态,仅首次播种触达 OS 熵源
11//! - fail-closed:CSPRNG 失败时 [`try_random_u64`] 返回 `None`,由调用方决策;
12//!   [`random_u64`] 的应急回退仍混入纳秒时间戳与原子计数(绝不退化为固定种子)
13
14use rand::rngs::OsRng;
15use rand::RngCore;
16use std::cell::Cell;
17use std::sync::atomic::{AtomicU64, Ordering};
18
19/// 应急熵混合计数器(仅 CSPRNG 失败路径使用,保证多次调用输出互不相同)
20static EMERGENCY_COUNTER: AtomicU64 = AtomicU64::new(0);
21
22thread_local! {
23    /// 线程本地 splitmix64 状态(Cell 零同步开销;None 表示尚未播种)
24    static SPLITMIX_STATE: Cell<Option<u64>> = const { Cell::new(None) };
25}
26
27/// 从 OS 熵源读取 8 字节(CSPRNG)
28///
29/// # Returns
30/// * `Some(u64)` - 密码学安全随机数
31/// * `None` - OS 熵源失败(fail-closed,调用方必须不得退化为可预测值)
32#[inline]
33pub fn try_random_u64() -> Option<u64> {
34    let mut buf = [0u8; 8];
35    OsRng.try_fill_bytes(&mut buf).ok()?;
36    Some(u64::from_ne_bytes(buf))
37}
38
39/// 从 OS 熵源获取密码学安全随机数
40///
41/// 安全敏感场景(TCP ISN / QUIC SCID / 挑战令牌)的唯一合法入口。
42///
43/// # 应急回退
44/// OS 熵源在受支持平台上实际不会失败;万一失败,混入纳秒时间戳、
45/// 单调原子计数与栈地址熵,保证输出不可重放(仍优于任何固定种子方案)。
46#[inline]
47#[must_use]
48pub fn random_u64() -> u64 {
49    if let Some(v) = try_random_u64() {
50        return v;
51    }
52    // 应急熵:CSPRNG 不可用时的最后防线(绝不返回固定值)
53    let nanos = std::time::SystemTime::now()
54        .duration_since(std::time::UNIX_EPOCH)
55        .map(|d| d.as_nanos() as u64)
56        .unwrap_or(0);
57    let counter = EMERGENCY_COUNTER.fetch_add(1, Ordering::Relaxed);
58    let stack_addr = (&counter as *const u64) as u64;
59    splitmix64_next(nanos ^ counter.rotate_left(31) ^ stack_addr)
60}
61
62/// 填充密码学安全随机字节
63///
64/// # Returns
65/// * `true` - 填充成功
66/// * `false` - OS 熵源失败(fail-closed,缓冲区保持原样)
67#[inline]
68pub fn try_fill_random(dest: &mut [u8]) -> bool {
69    OsRng.try_fill_bytes(dest).is_ok()
70}
71
72/// splitmix64 单步推进(纯函数,供播种与应急熵复用)
73#[inline]
74const fn splitmix64_next(mut x: u64) -> u64 {
75    x = x.wrapping_add(0x9E37_79B9_7F4A_7C15);
76    let mut z = x;
77    z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
78    z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
79    z ^ (z >> 31)
80}
81
82/// Splitmix64 伪随机生成器(值语义,零堆分配)
83///
84/// 适用于无安全要求的性能场景;安全场景必须使用 [`random_u64`]。
85#[derive(Debug, Clone)]
86pub struct Splitmix64 {
87    state: u64,
88}
89
90impl Splitmix64 {
91    /// 以指定种子构造
92    #[inline]
93    #[must_use]
94    pub const fn with_seed(seed: u64) -> Self {
95        Self { state: seed }
96    }
97
98    /// 以 CSPRNG 种子构造(应急回退同 [`random_u64`])
99    #[inline]
100    #[must_use]
101    pub fn seeded() -> Self {
102        Self {
103            state: random_u64(),
104        }
105    }
106
107    /// 生成下一个伪随机数
108    #[inline]
109    pub fn next_u64(&mut self) -> u64 {
110        self.state = splitmix64_next(self.state);
111        self.state
112    }
113
114    /// 生成 `[0, bound)` 区间伪随机数(bound 为 0 时返回 0)
115    #[inline]
116    pub fn next_bounded(&mut self, bound: u64) -> u64 {
117        if bound == 0 {
118            return 0;
119        }
120        self.next_u64() % bound
121    }
122}
123
124/// 线程本地伪随机(热路径零系统调用、零同步开销)
125///
126/// 首次调用时以 CSPRNG 播种;后续调用纯用户态推进。
127/// 适用于负载均衡 P2C、退避 jitter 等性能敏感场景。
128#[inline]
129#[must_use]
130pub fn pseudo_random_u64() -> u64 {
131    SPLITMIX_STATE.with(|cell| {
132        let state = match cell.get() {
133            Some(s) => s,
134            None => {
135                let seed = random_u64();
136                cell.set(Some(seed));
137                seed
138            }
139        };
140        let next = splitmix64_next(state);
141        cell.set(Some(next));
142        next
143    })
144}
145
146/// 线程本地伪随机,输出 `[0, bound)`(bound 为 0 时返回 0)
147#[inline]
148#[must_use]
149pub fn pseudo_random_bounded(bound: u64) -> u64 {
150    if bound == 0 {
151        return 0;
152    }
153    pseudo_random_u64() % bound
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn test_random_u64_not_constant() {
162        let a = random_u64();
163        let b = random_u64();
164        assert_ne!(a, b, "CSPRNG 连续输出不得相同");
165    }
166
167    #[test]
168    fn test_try_random_u64_works() {
169        assert!(try_random_u64().is_some());
170    }
171
172    #[test]
173    fn test_try_fill_random() {
174        let mut buf = [0u8; 32];
175        assert!(try_fill_random(&mut buf));
176        assert!(buf.iter().any(|&b| b != 0), "填充后缓冲区应非全零");
177    }
178
179    #[test]
180    fn test_splitmix64_deterministic() {
181        let mut a = Splitmix64::with_seed(42);
182        let mut b = Splitmix64::with_seed(42);
183        for _ in 0..100 {
184            assert_eq!(a.next_u64(), b.next_u64());
185        }
186    }
187
188    #[test]
189    fn test_splitmix64_sequence_unique() {
190        let mut rng = Splitmix64::with_seed(1);
191        let mut seen = std::collections::HashSet::new();
192        for _ in 0..1000 {
193            assert!(seen.insert(rng.next_u64()), "splitmix64 序列不得重复");
194        }
195    }
196
197    #[test]
198    fn test_splitmix64_bounded() {
199        let mut rng = Splitmix64::with_seed(7);
200        for _ in 0..1000 {
201            assert!(rng.next_bounded(10) < 10);
202        }
203        assert_eq!(rng.next_bounded(0), 0);
204    }
205
206    #[test]
207    fn test_pseudo_random_u64_not_constant() {
208        let a = pseudo_random_u64();
209        let b = pseudo_random_u64();
210        assert_ne!(a, b);
211    }
212
213    #[test]
214    fn test_pseudo_random_bounded() {
215        for _ in 0..1000 {
216            assert!(pseudo_random_bounded(64) < 64);
217        }
218        assert_eq!(pseudo_random_bounded(0), 0);
219    }
220
221    #[test]
222    fn test_seeded_constructor() {
223        let mut rng = Splitmix64::seeded();
224        let _ = rng.next_u64();
225    }
226}