core_crypto/
obfuscate.rs

1use crate::prelude::{ClientId, ConversationId};
2use derive_more::{Constructor, From};
3use hex;
4use log::kv::{ToValue, Value};
5use openmls::framing::Sender;
6use openmls::group::QueuedProposal;
7use openmls::prelude::Proposal;
8use sha2::{Digest, Sha256};
9use std::fmt::{Debug, Formatter};
10
11pub(crate) trait Obfuscate {
12    fn obfuscate(&self, f: &mut Formatter<'_>) -> core::fmt::Result;
13}
14
15impl Obfuscate for &ConversationId {
16    fn obfuscate(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
17        f.write_str(hex::encode(compute_hash(self)).as_str())
18    }
19}
20
21impl Obfuscate for &ClientId {
22    fn obfuscate(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
23        f.write_str(hex::encode(compute_hash(self)).as_str())
24    }
25}
26
27impl Obfuscate for &Proposal {
28    fn obfuscate(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
29        f.write_str(match self {
30            Proposal::Add(_) => "Add",
31            Proposal::Update(_) => "Update",
32            Proposal::Remove(_) => "Remove",
33            Proposal::PreSharedKey(_) => "PreSharedKey",
34            Proposal::ReInit(_) => "ReInit",
35            Proposal::ExternalInit(_) => "ExternalInit",
36            Proposal::AppAck(_) => "AppAck",
37            Proposal::GroupContextExtensions(_) => "GroupContextExtensions",
38        })
39    }
40}
41
42impl Obfuscate for &QueuedProposal {
43    fn obfuscate(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
44        (&self.proposal).obfuscate(f)
45    }
46}
47
48impl Obfuscate for &Sender {
49    fn obfuscate(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
50        match self {
51            Sender::Member(leaf_node_index) => write!(f, "Member{leaf_node_index}"),
52            Sender::External(external_sender_index) => write!(f, "External{external_sender_index:?}"),
53            Sender::NewMemberProposal => write!(f, "NewMemberProposal"),
54            Sender::NewMemberCommit => write!(f, "NewMemberCommit"),
55        }
56    }
57}
58
59/// We often want logging for some values that we shouldn't know the real value of, for privacy reasons.
60///
61/// `ConversationId` is a canonical example of such an item.
62///
63/// This wrapper lets us log a partial hash of the sensitive item, so we have deterministic loggable non-sensitive
64/// aliases for all our sensitive values.
65#[derive(From, Constructor)]
66pub struct Obfuscated<T>(T);
67
68impl<T> Debug for Obfuscated<T>
69where
70    T: Obfuscate,
71{
72    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
73        self.0.obfuscate(f)
74    }
75}
76
77impl<T> ToValue for Obfuscated<T>
78where
79    T: Obfuscate,
80{
81    fn to_value(&self) -> Value {
82        Value::from_debug(self)
83    }
84}
85
86fn compute_hash(bytes: &[u8]) -> [u8; 10] {
87    let mut hasher = Sha256::new();
88    let mut output = [0; 10];
89    hasher.update(bytes);
90    output.copy_from_slice(&hasher.finalize().as_slice()[0..10]);
91    output
92}