1use sha2::{Digest, Sha256};
10
11pub fn rand_bytes<const N: usize>() -> [u8; N] {
20 let mut buf = [0u8; N];
21 getrandom::fill(&mut buf).expect("OS entropy source (getrandom) unavailable");
22 buf
23}
24
25pub fn new_secret_hex() -> String {
27 let bytes: [u8; 32] = rand_bytes::<32>();
28 bytes.iter().map(|b| format!("{b:02x}")).collect()
29}
30
31pub fn new_uuid() -> String {
34 let bytes: [u8; 16] = rand_bytes::<16>();
35 let hex: String = bytes.iter().map(|b| format!("{b:02x}")).collect();
36 format!(
37 "{}-{}-{}-{}-{}",
38 &hex[0..8],
39 &hex[8..12],
40 &hex[12..16],
41 &hex[16..20],
42 &hex[20..32]
43 )
44}
45
46pub fn sha256_hex(input: &str) -> String {
49 let mut hasher = Sha256::new();
50 hasher.update(input.as_bytes());
51 let digest = hasher.finalize();
52 digest.iter().map(|b| format!("{b:02x}")).collect()
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn new_uuid_has_expected_shape() {
61 let id = new_uuid();
62 let parts: Vec<&str> = id.split('-').collect();
63 assert_eq!(parts.len(), 5);
64 assert_eq!(parts[0].len(), 8);
65 assert_eq!(parts[1].len(), 4);
66 assert_eq!(parts[2].len(), 4);
67 assert_eq!(parts[3].len(), 4);
68 assert_eq!(parts[4].len(), 12);
69 assert!(id.chars().all(|c| c.is_ascii_hexdigit() || c == '-'));
70 }
71
72 #[test]
73 fn new_uuid_is_unique() {
74 assert_ne!(new_uuid(), new_uuid());
75 }
76
77 #[test]
78 fn new_secret_hex_is_64_chars() {
79 let s = new_secret_hex();
80 assert_eq!(s.len(), 64);
81 assert!(s.chars().all(|c| c.is_ascii_hexdigit()));
82 }
83
84 #[test]
85 fn sha256_hex_is_deterministic() {
86 assert_eq!(sha256_hex("abc"), sha256_hex("abc"));
87 assert_ne!(sha256_hex("abc"), sha256_hex("abd"));
88 assert_eq!(sha256_hex("").len(), 64);
89 }
90
91 #[test]
99 fn rand_bytes_are_distinct_across_many_calls() {
100 use std::collections::HashSet;
101 let mut seen = HashSet::new();
102 for _ in 0..2_000 {
103 assert!(
104 seen.insert(rand_bytes::<32>()),
105 "rand_bytes produced a duplicate 32-byte value"
106 );
107 }
108 }
109
110 #[test]
111 fn rand_bytes_cover_every_bit_position() {
112 let mut ever_set = [0u8; 32];
117 let mut ever_clear = [0xffu8; 32];
118 for _ in 0..256 {
119 let b = rand_bytes::<32>();
120 for i in 0..32 {
121 ever_set[i] |= b[i];
122 ever_clear[i] &= b[i];
123 }
124 }
125 assert_eq!(ever_set, [0xffu8; 32], "a bit position was never set");
126 assert_eq!(ever_clear, [0u8; 32], "a bit position was never cleared");
127 }
128}