burble_crypto/
cmac.rs

1use std::fmt::Debug;
2
3use cmac::digest;
4use zeroize::{Zeroize, ZeroizeOnDrop};
5
6/// Provides a [`Debug`] implementation for a type containing sensitive data.
7macro_rules! debug_secret {
8    ($T:ty) => {
9        impl ::std::fmt::Debug for $T {
10            #[inline]
11            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
12                f.debug_tuple(stringify!($T)).field(&"<secret>").finish()
13            }
14        }
15    };
16}
17pub(super) use debug_secret;
18
19/// RFC-4493 AES-CMAC ([Vol 3] Part H, Section 2.2.5).
20#[derive(Debug)]
21#[repr(transparent)]
22pub struct AesCmac(cmac::Cmac<aes::Aes128>);
23
24impl AesCmac {
25    /// Creates new AES-CMAC state using key `k`.
26    #[inline(always)]
27    #[must_use]
28    pub(super) fn new(k: &Key) -> Self {
29        Self(digest::KeyInit::new(&k.0))
30    }
31
32    /// Creates new AES-CMAC state using an all-zero key for GAP database hash
33    /// calculation ([Vol 3] Part G, Section 7.3.1).
34    #[inline(always)]
35    #[must_use]
36    pub fn db_hash() -> Self {
37        Self::new(&Key::new(0))
38    }
39
40    /// Updates CMAC state.
41    #[inline(always)]
42    pub fn update(&mut self, b: impl AsRef<[u8]>) -> &mut Self {
43        digest::Update::update(&mut self.0, b.as_ref());
44        self
45    }
46
47    /// Computes the final MAC value.
48    #[inline(always)]
49    #[must_use]
50    pub fn finalize(self) -> u128 {
51        u128::from_be_bytes(*digest::FixedOutput::finalize_fixed(self.0).as_ref())
52    }
53
54    /// Computes the final MAC value for use as a future key and resets the
55    /// state.
56    #[inline(always)]
57    pub(super) fn finalize_key(&mut self) -> Key {
58        // Best effort to avoid leaving copies
59        let mut k = Key::new(0);
60        digest::FixedOutputReset::finalize_into_reset(&mut self.0, &mut k.0);
61        k
62    }
63}
64
65/// 128-bit AES-CMAC key ([Vol 3] Part H, Section 2.2.5).
66#[allow(clippy::redundant_pub_crate)]
67#[derive(Zeroize, ZeroizeOnDrop)]
68#[must_use]
69#[repr(transparent)]
70pub(super) struct Key(aes::cipher::Key<aes::Aes128>);
71
72debug_secret!(Key);
73
74impl Key {
75    /// Creates a key from a `u128` value.
76    #[inline(always)]
77    pub fn new(k: u128) -> Self {
78        Self(k.to_be_bytes().into())
79    }
80}
81
82impl From<&Key> for u128 {
83    #[inline(always)]
84    fn from(k: &Key) -> Self {
85        Self::from_be_bytes(k.0.into())
86    }
87}
88
89#[allow(clippy::unusual_byte_groupings)]
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    /// AES-CMAC RFC-4493 test vectors ([Vol 3] Part H, Section D.1).
95    #[test]
96    fn aes_cmac() {
97        const fn b(v: u128) -> [u8; 16] {
98            v.to_be_bytes()
99        }
100        fn eq(m: &mut AesCmac, v: u128) {
101            assert_eq!(u128::from(&m.finalize_key()), v);
102        }
103        let mut m = AesCmac::new(&Key::new(0x2b7e1516_28aed2a6_abf71588_09cf4f3c));
104        eq(&mut m, 0xbb1d6929_e9593728_7fa37d12_9b756746);
105
106        m.update(b(0x6bc1bee2_2e409f96_e93d7e11_7393172a));
107        eq(&mut m, 0x070a16b4_6b4d4144_f79bdd9d_d04a287c);
108
109        m.update(b(0x6bc1bee2_2e409f96_e93d7e11_7393172a));
110        m.update(b(0xae2d8a57_1e03ac9c_9eb76fac_45af8e51));
111        m.update(0x30c81c46_a35ce411_u64.to_be_bytes());
112        eq(&mut m, 0xdfa66747_de9ae630_30ca3261_1497c827);
113
114        m.update(b(0x6bc1bee2_2e409f96_e93d7e11_7393172a));
115        m.update(b(0xae2d8a57_1e03ac9c_9eb76fac_45af8e51));
116        m.update(b(0x30c81c46_a35ce411_e5fbc119_1a0a52ef));
117        m.update(b(0xf69f2445_df4f9b17_ad2b417b_e66c3710));
118        assert_eq!(m.finalize(), 0x51f0bebf_7e3b9d92_fc497417_79363cfe);
119    }
120}