obfuscate/
lib.rs

1use std::{fmt::Formatter, sync::LazyLock};
2
3use derive_more::From;
4use log::kv::{ToValue, Value};
5use sha2::{Digest, Sha256};
6
7pub mod impls;
8
9pub trait Obfuscate {
10    fn obfuscate(&self, f: &mut Formatter<'_>) -> core::fmt::Result;
11}
12
13pub fn compute_hash(bytes: &[u8]) -> [u8; 10] {
14    /// Store a per-instantiation salt, so that obfuscated values cannot turn into pseudo-ids.
15    ///
16    /// This will be regenerated each time the library is instantiated. This will be approximately
17    /// once per client instantiation.
18    static SALT: LazyLock<[u8; 32]> = LazyLock::new(|| {
19        use rand::Rng as _;
20        let mut salt = [0; _];
21        rand::thread_rng().fill(&mut salt);
22        salt
23    });
24
25    let mut hasher = Sha256::new();
26    hasher.update(*SALT);
27    hasher.update(bytes);
28
29    let mut output = [0; 10];
30    output.copy_from_slice(&hasher.finalize().as_slice()[0..10]);
31    output
32}
33
34/// We often want logging for some values that we shouldn't know the real value of, for privacy reasons.
35///
36/// `ConversationId` is a canonical example of such an item.
37///
38/// This wrapper lets us log a partial hash of the sensitive item, so we have deterministic loggable non-sensitive
39/// aliases for all our sensitive values.
40#[derive(From)]
41pub struct Obfuscated<'a, T: Obfuscate>(&'a T);
42impl<'a, T: Obfuscate> core::fmt::Debug for Obfuscated<'a, T> {
43    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44        self.0.obfuscate(f)
45    }
46}
47
48impl<'a, T: Obfuscate> ToValue for Obfuscated<'a, T> {
49    fn to_value(&self) -> Value<'_> {
50        Value::from_debug(self)
51    }
52}