Coverage Report

Created: 2026-01-09 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/digest-0.10.7/src/mac.rs
Line
Count
Source
1
use crate::{FixedOutput, FixedOutputReset, Update};
2
use crypto_common::{InvalidLength, Key, KeyInit, Output, OutputSizeUser, Reset};
3
4
#[cfg(feature = "rand_core")]
5
use crate::rand_core::{CryptoRng, RngCore};
6
use core::fmt;
7
use crypto_common::typenum::Unsigned;
8
use subtle::{Choice, ConstantTimeEq};
9
10
/// Marker trait for Message Authentication algorithms.
11
#[cfg_attr(docsrs, doc(cfg(feature = "mac")))]
12
pub 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")))]
19
pub 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
97
impl<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
5
    fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength>
108
5
    where
109
5
        Self: KeyInit,
110
    {
111
5
        KeyInit::new_from_slice(key)
112
5
    }
113
114
    #[inline]
115
44
    fn update(&mut self, data: &[u8]) {
116
44
        Update::update(self, data);
117
44
    }
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
0
    fn finalize_reset(&mut self) -> CtOutput<Self>
132
0
    where
133
0
        Self: FixedOutputReset,
134
    {
135
0
        CtOutput::new(self.finalize_fixed_reset())
136
0
    }
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")))]
244
pub struct CtOutput<T: OutputSizeUser> {
245
    bytes: Output<T>,
246
}
247
248
impl<T: OutputSizeUser> CtOutput<T> {
249
    /// Create a new [`CtOutput`] value.
250
    #[inline(always)]
251
0
    pub fn new(bytes: Output<T>) -> Self {
252
0
        Self { bytes }
253
0
    }
254
255
    /// Get the inner [`Output`] array this type wraps.
256
    #[inline(always)]
257
0
    pub fn into_bytes(self) -> Output<T> {
258
0
        self.bytes
259
0
    }
260
}
261
262
impl<T: OutputSizeUser> From<Output<T>> for CtOutput<T> {
263
    #[inline(always)]
264
    fn from(bytes: Output<T>) -> Self {
265
        Self { bytes }
266
    }
267
}
268
269
impl<'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
276
impl<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
283
impl<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
290
impl<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")))]
296
pub struct MacError;
297
298
impl 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")]
305
impl std::error::Error for MacError {}