Skip to main content

digest/
mac.rs

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