Skip to main content

oo7/
mac.rs

1use serde::{Deserialize, Serialize};
2#[cfg(feature = "native_crypto")]
3use subtle::ConstantTimeEq;
4use zbus::zvariant::Type;
5use 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)]
11pub struct Mac(#[serde(with = "serde_bytes")] Vec<u8>);
12
13impl std::fmt::Debug for Mac {
14    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15        write!(f, "Mac([REDACTED])")
16    }
17}
18
19impl Mac {
20    pub(crate) const fn new(inner: Vec<u8>) -> Self {
21        Self(inner)
22    }
23
24    /// Constant-time comparison against a slice of bytes.
25    fn verify_slice(&self, other: &[u8]) -> bool {
26        #[cfg(feature = "native_crypto")]
27        {
28            self.0.ct_eq(other).into()
29        }
30        #[cfg(feature = "openssl_crypto")]
31        {
32            openssl::memcmp::eq(&self.0, other)
33        }
34    }
35
36    // This is made private to prevent non-constant-time comparisons.
37    pub(crate) fn as_slice(&self) -> &[u8] {
38        self.0.as_slice()
39    }
40}
41
42impl PartialEq for Mac {
43    fn eq(&self, other: &Self) -> bool {
44        self.verify_slice(&other.0)
45    }
46}
47
48#[cfg(test)]
49mod 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}