Skip to main content

runlight/
hash.rs

1//! Hashes and random ids.
2
3use hmac::{Hmac, Mac};
4use sha2::{Digest, Sha256};
5
6/// Lower case hex of bytes.
7pub fn hex(bytes: &[u8]) -> String {
8    const HEX: &[u8; 16] = b"0123456789abcdef";
9    let mut out = String::with_capacity(bytes.len() * 2);
10    for b in bytes {
11        out.push(HEX[(b >> 4) as usize] as char);
12        out.push(HEX[(b & 0xf) as usize] as char);
13    }
14    out
15}
16
17/// SHA-256 of the text, as hex.
18pub fn sha256(text: &str) -> String {
19    hex(&Sha256::digest(text.as_bytes()))
20}
21
22/// HMAC-SHA-256 of text under key, as hex.
23pub fn hmac(key: &str, text: &str) -> String {
24    let mut mac = Hmac::<Sha256>::new_from_slice(key.as_bytes()).expect("HMAC takes any key");
25    mac.update(text.as_bytes());
26    hex(&mac.finalize().into_bytes())
27}
28
29/// The day's visitor hash: SHA-256 of salt, site, IP, and user agent, cut to
30/// 64 bits. The salt changes every day and old salts are deleted, so the
31/// hash cannot be recomputed and does not follow anyone across days.
32pub fn visitor_hash(salt: &str, site: &str, ip: &str, ua: &str) -> String {
33    sha256(&format!("{salt}\n{site}\n{ip}\n{ua}"))[..16].to_string()
34}
35
36/// Random bytes from the system.
37pub fn random_bytes(n: usize) -> Vec<u8> {
38    let mut out = vec![0u8; n];
39    getrandom::fill(&mut out).expect("the system's random numbers");
40    out
41}
42
43/// A random id: `bytes` random bytes as hex.
44pub fn random_id(bytes: usize) -> String {
45    hex(&random_bytes(bytes))
46}
47
48/// A random id of twelve bytes, the SDK's default.
49pub fn new_id() -> String {
50    random_id(12)
51}
52
53/// A new day's salt.
54pub fn random_salt() -> String {
55    random_id(32)
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn hashes_match_the_sdk() {
64        assert_eq!(sha256("abc"), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
65        assert_eq!(visitor_hash("s", "default", "1.2.3.4", "ua").len(), 16);
66        assert_eq!(new_id().len(), 24);
67        assert_eq!(
68            hmac("key", "The quick brown fox jumps over the lazy dog"),
69            "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"
70        );
71    }
72}