Skip to main content

bsv/primitives/
random.rs

1//! Cryptographically secure random byte generation.
2//!
3//! Uses the `getrandom` crate for OS-provided entropy, which delegates
4//! to the appropriate platform-specific CSPRNG (e.g., /dev/urandom on
5//! Linux, SecRandomCopyBytes on macOS, BCryptGenRandom on Windows).
6
7/// Generate `len` cryptographically secure random bytes.
8///
9/// Uses the operating system's CSPRNG via the `getrandom` crate.
10/// Returns an empty Vec if `len` is 0.
11///
12/// # Panics
13///
14/// Panics if the OS random number generator fails, which should
15/// only happen in extremely unusual circumstances (e.g., very early
16/// boot on a system with no entropy sources).
17pub fn random_bytes(len: usize) -> Vec<u8> {
18    if len == 0 {
19        return Vec::new();
20    }
21    let mut buf = vec![0u8; len];
22    // SAFETY: OS CSPRNG failure is unrecoverable (no entropy source available)
23    getrandom::getrandom(&mut buf).expect("OS random number generator failed");
24    buf
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30
31    #[test]
32    fn test_random_bytes_correct_length() {
33        assert_eq!(random_bytes(0).len(), 0);
34        assert_eq!(random_bytes(1).len(), 1);
35        assert_eq!(random_bytes(32).len(), 32);
36        assert_eq!(random_bytes(64).len(), 64);
37        assert_eq!(random_bytes(256).len(), 256);
38    }
39
40    #[test]
41    fn test_random_bytes_empty() {
42        let result = random_bytes(0);
43        assert!(result.is_empty());
44    }
45
46    #[test]
47    fn test_random_bytes_varying_output() {
48        // Two consecutive calls should produce different output
49        // (probability of collision for 32 bytes is negligible: 2^-256)
50        let a = random_bytes(32);
51        let b = random_bytes(32);
52        assert_ne!(
53            a, b,
54            "Two random_bytes(32) calls should produce different output"
55        );
56    }
57
58    #[test]
59    fn test_random_bytes_non_zero() {
60        // 64 random bytes should not all be zero
61        // (probability: 2^-512, effectively impossible)
62        let result = random_bytes(64);
63        assert!(
64            result.iter().any(|&b| b != 0),
65            "64 random bytes should not all be zero"
66        );
67    }
68
69    #[test]
70    fn test_random_bytes_distribution() {
71        // Generate a large sample and verify it's not degenerate
72        // (all same byte value would indicate a broken RNG)
73        let result = random_bytes(1024);
74        let first = result[0];
75        let all_same = result.iter().all(|&b| b == first);
76        assert!(
77            !all_same,
78            "1024 random bytes should not all be the same value"
79        );
80    }
81}