1use crate::{FixedOutput, FixedOutputReset, Update};
2use common::{Output, OutputSizeUser, Reset};
3
4use common::typenum::Unsigned;
5use core::fmt;
6use ctutils::CtEq;
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>;
43
44 fn verify_reset(&mut self, tag: &Output<Self>) -> Result<(), MacError>
50 where
51 Self: FixedOutputReset;
52
53 fn verify_slice(self, tag: &[u8]) -> Result<(), MacError>;
59
60 fn verify_slice_reset(&mut self, tag: &[u8]) -> Result<(), MacError>
66 where
67 Self: FixedOutputReset;
68
69 fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError>;
75
76 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#[derive(Clone)]
188pub struct CtOutput<T: OutputSizeUser> {
189 bytes: Output<T>,
190}
191
192impl<T: OutputSizeUser> CtOutput<T> {
193 #[inline(always)]
195 pub fn new(bytes: Output<T>) -> Self {
196 Self { bytes }
197 }
198
199 #[inline(always)]
201 pub fn as_bytes(&self) -> &Output<T> {
202 &self.bytes
203 }
204
205 #[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#[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 {}