digest/
mac.rs

1use crate::{FixedOutput, FixedOutputReset, Update};
2use crypto_common::{InvalidLength, Key, KeyInit, Output, OutputSizeUser, Reset};
3
4#[cfg(feature = "rand_core")]
5use crate::rand_core::{CryptoRng, RngCore};
6use core::fmt;
7use crypto_common::typenum::Unsigned;
8use subtle::{Choice, ConstantTimeEq};
9
10/// Marker trait for Message Authentication algorithms.
11#[cfg_attr(docsrs, doc(cfg(feature = "mac")))]
12pub trait MacMarker {}
13
14/// Convenience wrapper trait covering functionality of Message Authentication algorithms.
15///
16/// This trait wraps [`KeyInit`], [`Update`], [`FixedOutput`], and [`MacMarker`]
17/// traits and provides additional convenience methods.
18#[cfg_attr(docsrs, doc(cfg(feature = "mac")))]
19pub trait Mac: OutputSizeUser + Sized {
20    /// Create new value from fixed size key.
21    fn new(key: &Key<Self>) -> Self
22    where
23        Self: KeyInit;
24
25    /// Generate random key using the provided [`CryptoRng`].
26    #[cfg(feature = "rand_core")]
27    #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))]
28    fn generate_key(rng: impl CryptoRng + RngCore) -> Key<Self>
29    where
30        Self: KeyInit;
31
32    /// Create new value from variable size key.
33    fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength>
34    where
35        Self: KeyInit;
36
37    /// Update state using the provided data.
38    fn update(&mut self, data: &[u8]);
39
40    /// Process input data in a chained manner.
41    #[must_use]
42    fn chain_update(self, data: impl AsRef<[u8]>) -> Self;
43
44    /// Obtain the result of a [`Mac`] computation as a [`CtOutput`] and consume
45    /// [`Mac`] instance.
46    fn finalize(self) -> CtOutput<Self>;
47
48    /// Obtain the result of a [`Mac`] computation as a [`CtOutput`] and reset
49    /// [`Mac`] instance.
50    fn finalize_reset(&mut self) -> CtOutput<Self>
51    where
52        Self: FixedOutputReset;
53
54    /// Reset MAC instance to its initial state.
55    fn reset(&mut self)
56    where
57        Self: Reset;
58
59    /// Check if tag/code value is correct for the processed input.
60    fn verify(self, tag: &Output<Self>) -> Result<(), MacError>;
61
62    /// Check if tag/code value is correct for the processed input and reset
63    /// [`Mac`] instance.
64    fn verify_reset(&mut self, tag: &Output<Self>) -> Result<(), MacError>
65    where
66        Self: FixedOutputReset;
67
68    /// Check truncated tag correctness using all bytes
69    /// of calculated tag.
70    ///
71    /// Returns `Error` if `tag` is not valid or not equal in length
72    /// to MAC's output.
73    fn verify_slice(self, tag: &[u8]) -> Result<(), MacError>;
74
75    /// Check truncated tag correctness using all bytes
76    /// of calculated tag and reset [`Mac`] instance.
77    ///
78    /// Returns `Error` if `tag` is not valid or not equal in length
79    /// to MAC's output.
80    fn verify_slice_reset(&mut self, tag: &[u8]) -> Result<(), MacError>
81    where
82        Self: FixedOutputReset;
83
84    /// Check truncated tag correctness using left side bytes
85    /// (i.e. `tag[..n]`) of calculated tag.
86    ///
87    /// Returns `Error` if `tag` is not valid or empty.
88    fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError>;
89
90    /// Check truncated tag correctness using right side bytes
91    /// (i.e. `tag[n..]`) of calculated tag.
92    ///
93    /// Returns `Error` if `tag` is not valid or empty.
94    fn verify_truncated_right(self, tag: &[u8]) -> Result<(), MacError>;
95}
96
97impl<T: Update + FixedOutput + MacMarker> Mac for T {
98    #[inline(always)]
99    fn new(key: &Key<Self>) -> Self
100    where
101        Self: KeyInit,
102    {
103        KeyInit::new(key)
104    }
105
106    #[inline(always)]
107    fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength>
108    where
109        Self: KeyInit,
110    {
111        KeyInit::new_from_slice(key)
112    }
113
114    #[inline]
115    fn update(&mut self, data: &[u8]) {
116        Update::update(self, data);
117    }
118
119    #[inline]
120    fn chain_update(mut self, data: impl AsRef<[u8]>) -> Self {
121        Update::update(&mut self, data.as_ref());
122        self
123    }
124
125    #[inline]
126    fn finalize(self) -> CtOutput<Self> {
127        CtOutput::new(self.finalize_fixed())
128    }
129
130    #[inline(always)]
131    fn finalize_reset(&mut self) -> CtOutput<Self>
132    where
133        Self: FixedOutputReset,
134    {
135        CtOutput::new(self.finalize_fixed_reset())
136    }
137
138    #[inline]
139    fn reset(&mut self)
140    where
141        Self: Reset,
142    {
143        Reset::reset(self)
144    }
145
146    #[inline]
147    fn verify(self, tag: &Output<Self>) -> Result<(), MacError> {
148        if self.finalize() == tag.into() {
149            Ok(())
150        } else {
151            Err(MacError)
152        }
153    }
154
155    #[inline]
156    fn verify_reset(&mut self, tag: &Output<Self>) -> Result<(), MacError>
157    where
158        Self: FixedOutputReset,
159    {
160        if self.finalize_reset() == tag.into() {
161            Ok(())
162        } else {
163            Err(MacError)
164        }
165    }
166
167    #[inline]
168    fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> {
169        let n = tag.len();
170        if n != Self::OutputSize::USIZE {
171            return Err(MacError);
172        }
173        let choice = self.finalize_fixed().ct_eq(tag);
174        if choice.into() {
175            Ok(())
176        } else {
177            Err(MacError)
178        }
179    }
180
181    #[inline]
182    fn verify_slice_reset(&mut self, tag: &[u8]) -> Result<(), MacError>
183    where
184        Self: FixedOutputReset,
185    {
186        let n = tag.len();
187        if n != Self::OutputSize::USIZE {
188            return Err(MacError);
189        }
190        let choice = self.finalize_fixed_reset().ct_eq(tag);
191        if choice.into() {
192            Ok(())
193        } else {
194            Err(MacError)
195        }
196    }
197
198    fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> {
199        let n = tag.len();
200        if n == 0 || n > Self::OutputSize::USIZE {
201            return Err(MacError);
202        }
203        let choice = self.finalize_fixed()[..n].ct_eq(tag);
204
205        if choice.into() {
206            Ok(())
207        } else {
208            Err(MacError)
209        }
210    }
211
212    fn verify_truncated_right(self, tag: &[u8]) -> Result<(), MacError> {
213        let n = tag.len();
214        if n == 0 || n > Self::OutputSize::USIZE {
215            return Err(MacError);
216        }
217        let m = Self::OutputSize::USIZE - n;
218        let choice = self.finalize_fixed()[m..].ct_eq(tag);
219
220        if choice.into() {
221            Ok(())
222        } else {
223            Err(MacError)
224        }
225    }
226
227    #[cfg(feature = "rand_core")]
228    #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))]
229    #[inline]
230    fn generate_key(rng: impl CryptoRng + RngCore) -> Key<Self>
231    where
232        Self: KeyInit,
233    {
234        <T as KeyInit>::generate_key(rng)
235    }
236}
237
238/// Fixed size output value which provides a safe [`Eq`] implementation that
239/// runs in constant time.
240///
241/// It is useful for implementing Message Authentication Codes (MACs).
242#[derive(Clone)]
243#[cfg_attr(docsrs, doc(cfg(feature = "mac")))]
244pub struct CtOutput<T: OutputSizeUser> {
245    bytes: Output<T>,
246}
247
248impl<T: OutputSizeUser> CtOutput<T> {
249    /// Create a new [`CtOutput`] value.
250    #[inline(always)]
251    pub fn new(bytes: Output<T>) -> Self {
252        Self { bytes }
253    }
254
255    /// Get the inner [`Output`] array this type wraps.
256    #[inline(always)]
257    pub fn into_bytes(self) -> Output<T> {
258        self.bytes
259    }
260}
261
262impl<T: OutputSizeUser> From<Output<T>> for CtOutput<T> {
263    #[inline(always)]
264    fn from(bytes: Output<T>) -> Self {
265        Self { bytes }
266    }
267}
268
269impl<'a, T: OutputSizeUser> From<&'a Output<T>> for CtOutput<T> {
270    #[inline(always)]
271    fn from(bytes: &'a Output<T>) -> Self {
272        bytes.clone().into()
273    }
274}
275
276impl<T: OutputSizeUser> ConstantTimeEq for CtOutput<T> {
277    #[inline(always)]
278    fn ct_eq(&self, other: &Self) -> Choice {
279        self.bytes.ct_eq(&other.bytes)
280    }
281}
282
283impl<T: OutputSizeUser> PartialEq for CtOutput<T> {
284    #[inline(always)]
285    fn eq(&self, x: &CtOutput<T>) -> bool {
286        self.ct_eq(x).into()
287    }
288}
289
290impl<T: OutputSizeUser> Eq for CtOutput<T> {}
291
292/// Error type for when the [`Output`] of a [`Mac`]
293/// is not equal to the expected value.
294#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
295#[cfg_attr(docsrs, doc(cfg(feature = "mac")))]
296pub struct MacError;
297
298impl fmt::Display for MacError {
299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300        f.write_str("MAC tag mismatch")
301    }
302}
303
304#[cfg(feature = "std")]
305impl std::error::Error for MacError {}