rtb_telemetry/machine.rs
1//! Machine-ID derivation with a per-tool salt.
2
3use sha2::{Digest, Sha256};
4
5/// Salted-SHA-256 derivation of the host's machine ID.
6///
7/// The raw ID obtained from `machine-uid` is never returned — callers
8/// only see the salted hash, hex-encoded. If the OS does not expose
9/// a machine ID (sandboxed container, WASI), a random UUID fills in
10/// so the pipeline never fails because of identity.
11pub struct MachineId;
12
13impl MachineId {
14 /// Compute `sha256(salt || machine_id)` as a 64-char hex string.
15 ///
16 /// `salt` MUST be per-tool — rotating the salt invalidates every
17 /// previously-recorded machine identity, which is the intended
18 /// path for "reset my telemetry identity" flows.
19 #[must_use]
20 pub fn derive(salt: &str) -> String {
21 let raw = machine_uid::get().unwrap_or_else(|_| uuid::Uuid::new_v4().to_string());
22
23 let mut hasher = Sha256::new();
24 hasher.update(salt.as_bytes());
25 hasher.update(raw.as_bytes());
26 let digest = hasher.finalize();
27
28 hex_encode(&digest)
29 }
30}
31
32fn hex_encode(bytes: &[u8]) -> String {
33 let mut out = String::with_capacity(bytes.len() * 2);
34 for b in bytes {
35 use std::fmt::Write;
36 let _ = write!(out, "{b:02x}");
37 }
38 out
39}