1use core::{
4 array::TryFromSliceError,
5 fmt::{self, Debug},
6 result::Result,
7};
8
9use generic_array::{ArrayLength, GenericArray};
10use subtle::{Choice, ConstantTimeEq};
11use typenum::{IsGreaterOrEqual, IsLess, U32, U48, U64, U65536};
12
13use crate::keys::{raw_key, InvalidKey, SecretKey};
14
15#[derive(Debug, Eq, PartialEq)]
17pub enum MacError {
18 InsecureKey,
20 Verification,
22}
23
24impl fmt::Display for MacError {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 Self::InsecureKey => write!(f, "insecure key"),
28 Self::Verification => write!(f, "unable to verify MAC"),
29 }
30 }
31}
32
33impl core::error::Error for MacError {}
34
35pub trait Mac: Clone + Sized {
51 type Tag: ConstantTimeEq;
53 type TagSize: ArrayLength + IsGreaterOrEqual<U32> + IsLess<U65536> + 'static;
57
58 type Key: SecretKey<Size = Self::KeySize>;
67 type KeySize: ArrayLength
72 + IsGreaterOrEqual<Self::MinKeySize>
73 + IsGreaterOrEqual<U32>
74 + IsLess<U65536>
75 + 'static;
76
77 fn new(key: &Self::Key) -> Self;
79
80 type MinKeySize: ArrayLength + IsGreaterOrEqual<U32> + IsLess<U65536> + 'static;
85
86 fn try_new(key: &[u8]) -> Result<Self, InvalidKey>;
95
96 fn update(&mut self, data: &[u8]);
98
99 fn tag(self) -> Self::Tag;
101
102 fn verify(self, expect: &Self::Tag) -> Result<(), MacError> {
105 if bool::from(self.tag().ct_eq(expect)) {
106 Ok(())
107 } else {
108 Err(MacError::Verification)
109 }
110 }
111
112 fn mac(key: &Self::Key, data: &[u8]) -> Self::Tag {
118 let mut h = Self::new(key);
119 h.update(data);
120 h.tag()
121 }
122}
123
124raw_key! {
125 pub MacKey,
127}
128
129#[derive(Copy, Clone, Debug)]
131pub struct Tag<const N: usize>([u8; N]);
132
133impl<const N: usize> Tag<N> {
134 #[allow(clippy::len_without_is_empty)]
136 #[inline]
137 pub const fn len(&self) -> usize {
138 N
139 }
140
141 #[inline]
143 pub const fn as_bytes(&self) -> &[u8; N] {
144 &self.0
145 }
146}
147
148impl<const N: usize> ConstantTimeEq for Tag<N> {
149 #[inline]
150 fn ct_eq(&self, other: &Self) -> Choice {
151 self.0[..].ct_eq(&other.0[..])
152 }
153}
154
155impl<const N: usize> AsRef<[u8]> for Tag<N> {
156 #[inline]
157 fn as_ref(&self) -> &[u8] {
158 &self.0
159 }
160}
161
162impl<const N: usize> From<[u8; N]> for Tag<N> {
163 #[inline]
164 fn from(tag: [u8; N]) -> Self {
165 Self(tag)
166 }
167}
168
169impl From<GenericArray<u8, U32>> for Tag<32> {
170 #[inline]
171 fn from(tag: GenericArray<u8, U32>) -> Self {
172 Self(tag.into())
173 }
174}
175
176impl From<GenericArray<u8, U48>> for Tag<48> {
177 #[inline]
178 fn from(tag: GenericArray<u8, U48>) -> Self {
179 Self(tag.into())
180 }
181}
182
183impl From<GenericArray<u8, U64>> for Tag<64> {
184 #[inline]
185 fn from(tag: GenericArray<u8, U64>) -> Self {
186 Self(tag.into())
187 }
188}
189
190impl<const N: usize> From<Tag<N>> for [u8; N] {
191 #[inline]
192 fn from(tag: Tag<N>) -> Self {
193 tag.0
194 }
195}
196
197impl<const N: usize> TryFrom<&[u8]> for Tag<N> {
198 type Error = TryFromSliceError;
199
200 #[inline]
201 fn try_from(tag: &[u8]) -> Result<Self, Self::Error> {
202 Ok(Tag(tag.try_into()?))
203 }
204}