1
use serde::{Deserialize, Serialize};
2
#[cfg(feature = "native_crypto")]
3
use subtle::ConstantTimeEq;
4
use zbus::zvariant::Type;
5

            
6
// There is no constructor to avoid performing sanity checks, e.g. length.
7
/// A message authentication code. It provides constant-time comparison when
8
/// compared against another mac or against a slice of bytes.
9
#[derive(Deserialize, Serialize, Type, Clone)]
10
pub struct Mac(Vec<u8>);
11

            
12
impl std::fmt::Debug for Mac {
13
2
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14
2
        write!(f, "Mac([REDACTED])")
15
    }
16
}
17

            
18
impl Mac {
19
4
    pub(crate) const fn new(inner: Vec<u8>) -> Self {
20
4
        Self(inner)
21
    }
22

            
23
    /// Constant-time comparison against a slice of bytes.
24
4
    fn verify_slice(&self, other: &[u8]) -> bool {
25
        #[cfg(feature = "native_crypto")]
26
        {
27
2
            self.0.ct_eq(other).into()
28
        }
29
        #[cfg(feature = "openssl_crypto")]
30
        {
31
2
            openssl::memcmp::eq(&self.0, other)
32
        }
33
    }
34

            
35
    // This is made private to prevent non-constant-time comparisons.
36
4
    pub(crate) fn as_slice(&self) -> &[u8] {
37
4
        self.0.as_slice()
38
    }
39
}
40

            
41
impl PartialEq for Mac {
42
4
    fn eq(&self, other: &Self) -> bool {
43
4
        self.verify_slice(&other.0)
44
    }
45
}
46

            
47
#[cfg(test)]
48
mod tests {
49
    use super::*;
50

            
51
    #[test]
52
    fn mac_debug_is_redacted() {
53
        let mac = Mac::new(vec![1, 2, 3, 4]);
54
        assert_eq!(format!("{:?}", mac), "Mac([REDACTED])");
55
    }
56
}