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
8pub trait MacMarker {}
10
11pub trait Mac: OutputSizeUser + Sized {
16 fn update(&mut self, data: &[u8]);
18
19 #[must_use]
21 fn chain_update(self, data: impl AsRef<[u8]>) -> Self;
22
23 fn finalize(self) -> CtOutput<Self>;
26
27 fn finalize_reset(&mut self) -> CtOutput<Self>
30 where
31 Self: FixedOutputReset;
32
33 fn reset(&mut self)
35 where
36 Self: Reset;
37
38 fn verify(self, tag: &Output<Self>) -> Result<(), MacError>;
40
41 fn verify_reset(&mut self, tag: &Output<Self>) -> Result<(), MacError>
44 where
45 Self: FixedOutputReset;
46
47 fn verify_slice(self, tag: &[u8]) -> Result<(), MacError>;
53
54 fn verify_slice_reset(&mut self, tag: &[u8]) -> Result<(), MacError>
60 where
61 Self: FixedOutputReset;
62
63 fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError>;
68
69 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#[derive(Clone)]
180pub struct CtOutput<T: OutputSizeUser> {
181 bytes: Output<T>,
182}
183
184impl<T: OutputSizeUser> CtOutput<T> {
185 #[inline(always)]
187 pub fn new(bytes: Output<T>) -> Self {
188 Self { bytes }
189 }
190
191 #[inline(always)]
193 pub fn as_bytes(&self) -> &Output<T> {
194 &self.bytes
195 }
196
197 #[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#[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 {}