1use std::fmt::Debug;
2
3use cmac::digest;
4use zeroize::{Zeroize, ZeroizeOnDrop};
5
6macro_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#[derive(Debug)]
21#[repr(transparent)]
22pub struct AesCmac(cmac::Cmac<aes::Aes128>);
23
24impl AesCmac {
25 #[inline(always)]
27 #[must_use]
28 pub(super) fn new(k: &Key) -> Self {
29 Self(digest::KeyInit::new(&k.0))
30 }
31
32 #[inline(always)]
35 #[must_use]
36 pub fn db_hash() -> Self {
37 Self::new(&Key::new(0))
38 }
39
40 #[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 #[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 #[inline(always)]
57 pub(super) fn finalize_key(&mut self) -> Key {
58 let mut k = Key::new(0);
60 digest::FixedOutputReset::finalize_into_reset(&mut self.0, &mut k.0);
61 k
62 }
63}
64
65#[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 #[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 #[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}