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

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

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

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

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

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

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

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

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