[go: up one dir, main page]

digest/
mac.rs

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