1use hmac::{Hmac, Mac};
4use sha2::{Digest, Sha256};
5
6pub 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
17pub fn sha256(text: &str) -> String {
19 hex(&Sha256::digest(text.as_bytes()))
20}
21
22pub 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
29pub 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
36pub 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
43pub fn random_id(bytes: usize) -> String {
45 hex(&random_bytes(bytes))
46}
47
48pub fn new_id() -> String {
50 random_id(12)
51}
52
53pub 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}