Skip to main content

hennery_kernel/
secret.rs

1//! Randomness and hashing for credentials (kernel spec §3, §4).
2
3use sha2::{Digest, Sha256};
4
5/// `N` bytes from the operating system's CSPRNG.
6pub fn random_bytes<const N: usize>() -> [u8; N] {
7    let mut out = [0u8; N];
8    getrandom::fill(&mut out).expect("the OS random number generator is available");
9    out
10}
11
12/// Lowercase hex SHA-256: how pairing codes (and later session ids) are
13/// stored at rest.
14pub fn sha256_hex(data: &[u8]) -> String {
15    hex::encode(Sha256::digest(data))
16}
17
18/// Seconds since the Unix epoch, the kernel tables' time unit.
19pub fn unix_now() -> i64 {
20    std::time::SystemTime::now()
21        .duration_since(std::time::UNIX_EPOCH)
22        .expect("the clock is after 1970")
23        .as_secs() as i64
24}
25
26/// RFC 3339, in UTC, for a kernel timestamp (seconds since the epoch), as
27/// the REST API shows it; empty for one out of range.
28pub fn rfc3339(unix: i64) -> String {
29    time::OffsetDateTime::from_unix_timestamp(unix)
30        .ok()
31        .and_then(|t| t.format(&time::format_description::well_known::Rfc3339).ok())
32        .unwrap_or_default()
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn random_bytes_differ_between_calls() {
41        assert_ne!(random_bytes::<32>(), random_bytes::<32>());
42    }
43
44    #[test]
45    fn sha256_hex_matches_the_known_digest_of_abc() {
46        assert_eq!(
47            sha256_hex(b"abc"),
48            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
49        );
50    }
51
52    #[test]
53    fn rfc3339_formats_kernel_timestamps_in_utc_and_out_of_range_as_empty() {
54        assert_eq!(rfc3339(0), "1970-01-01T00:00:00Z");
55        assert_eq!(rfc3339(1_700_000_000), "2023-11-14T22:13:20Z");
56        assert_eq!(rfc3339(i64::MAX), "");
57    }
58}