1#![forbid(unsafe_code)]
6
7use core::{cmp, fmt};
8
9use generic_array::{ArrayLength, GenericArray, LengthError};
10use subtle::{Choice, ConstantTimeEq};
11
12use crate::{
13 block::{Block, BlockSize},
14 csprng::{Csprng, Random},
15 hash::{Digest, Hash},
16 import::{ExportError, Import, ImportError},
17 keys::{SecretKey, SecretKeyBytes},
18 zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing},
19};
20
21#[derive(Clone, Debug)]
25pub struct Hmac<H> {
26 ipad: H,
28 opad: H,
30}
31
32impl<H: Hash + BlockSize> Hmac<H> {
33 pub fn new(key: &HmacKey<H>) -> Self {
35 let mut key = Zeroizing::new(key.0.clone());
36
37 for v in key.iter_mut() {
39 *v ^= 0x36;
40 }
41 let mut ipad = H::new();
42 ipad.update(key.as_slice());
43
44 for v in key.iter_mut() {
46 *v ^= 0x36 ^ 0x5c;
47 }
48 let mut opad = H::new();
49 opad.update(key.as_slice());
50
51 Self { ipad, opad }
52 }
53
54 #[inline]
56 pub fn update(&mut self, data: &[u8]) {
57 self.ipad.update(data)
59 }
60
61 #[inline]
63 pub fn tag(mut self) -> Tag<H::DigestSize> {
64 let d = self.ipad.digest();
65 self.opad.update(&d);
67 Tag(self.opad.digest())
69 }
70
71 pub fn mac_multi<I>(key: &HmacKey<H>, data: I) -> Tag<H::DigestSize>
73 where
74 I: IntoIterator<Item: AsRef<[u8]>>,
75 {
76 let mut h = Self::new(key);
77 data.into_iter().for_each(|s| {
78 h.update(s.as_ref());
79 });
80 h.tag()
81 }
82}
83
84#[derive(Clone, Debug)]
86#[repr(transparent)]
87pub struct Tag<N: ArrayLength>(Digest<N>);
88
89impl<N: ArrayLength> Tag<N> {
90 #[cfg(feature = "committing-aead")]
92 #[cfg_attr(docsrs, doc(cfg(feature = "committing-aead")))]
93 #[allow(clippy::len_without_is_empty)]
94 pub const fn len(&self) -> usize {
95 self.0.len()
96 }
97
98 #[doc(hidden)]
103 pub fn into_array(self) -> GenericArray<u8, N> {
104 self.0.into_array()
105 }
106}
107
108cfg_if::cfg_if! {
112 if #[cfg(feature = "hazmat")] {
113 impl<N: ArrayLength> Tag<N> {
114 #[cfg_attr(docsrs, doc(cfg(feature = "hazmat")))]
123 pub const fn as_bytes(&self) -> &[u8] {
124 self.0.as_bytes()
125 }
126 }
127 } else {
128 impl<N: ArrayLength> Tag<N> {
129 pub(crate) const fn as_bytes(&self) -> &[u8] {
130 self.0.as_bytes()
131 }
132 }
133 }
134}
135
136impl<N: ArrayLength> ConstantTimeEq for Tag<N> {
137 #[inline]
138 fn ct_eq(&self, other: &Self) -> Choice {
139 self.0.ct_eq(&other.0)
140 }
141}
142
143impl<'a, N: ArrayLength> TryFrom<&'a [u8]> for Tag<N> {
145 type Error = LengthError;
146
147 fn try_from(tag: &'a [u8]) -> Result<Self, Self::Error> {
148 let digest = GenericArray::try_from_slice(tag)?;
149 Ok(Self(Digest::new(digest.clone())))
150 }
151}
152
153#[repr(transparent)]
155pub struct HmacKey<H: Hash + BlockSize>(Block<H>);
156
157impl<H: Hash + BlockSize> HmacKey<H> {
158 pub fn new(key: &[u8]) -> Self {
160 let mut out = Block::<H>::default();
161 if key.len() <= out.len() {
162 out[..key.len()].copy_from_slice(key);
164 } else {
165 let d = H::hash(key);
167 let n = cmp::min(d.len(), out.len());
168 out[..n].copy_from_slice(&d[..n]);
169 };
170 Self(out)
171 }
172}
173
174impl<H: Hash + BlockSize> Clone for HmacKey<H> {
175 #[inline]
176 fn clone(&self) -> Self {
177 Self(self.0.clone())
178 }
179}
180
181impl<H: Hash + BlockSize> SecretKey for HmacKey<H> {
182 type Size = H::BlockSize;
183
184 #[inline]
189 fn try_export_secret(&self) -> Result<SecretKeyBytes<Self::Size>, ExportError> {
190 Ok(SecretKeyBytes::new(self.0.clone()))
191 }
192}
193
194impl<H: Hash + BlockSize> Random for HmacKey<H> {
195 fn random<R: Csprng>(rng: R) -> Self {
196 Self(Block::<H>::random(rng))
197 }
198}
199
200impl<H: Hash + BlockSize> Import<&[u8]> for HmacKey<H> {
201 #[inline]
202 fn import(data: &[u8]) -> Result<Self, ImportError> {
203 Ok(Self::new(data))
204 }
205}
206
207impl<H: Hash + BlockSize> ConstantTimeEq for HmacKey<H> {
208 #[inline]
209 fn ct_eq(&self, other: &Self) -> Choice {
210 self.0.ct_eq(&other.0)
211 }
212}
213
214impl<H: Hash + BlockSize> fmt::Debug for HmacKey<H> {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 f.debug_struct("HmacKey").finish_non_exhaustive()
217 }
218}
219
220impl<H: Hash + BlockSize> ZeroizeOnDrop for HmacKey<H> {}
221impl<H: Hash + BlockSize> Drop for HmacKey<H> {
222 #[inline]
223 fn drop(&mut self) {
224 self.0.zeroize();
225 }
226}
227
228#[macro_export]
264macro_rules! hmac_impl {
265 ($name:ident, $doc:expr, $hash:ident $(, $oid:ident)? $(,)?) => {
266 #[doc = concat!($doc, ".")]
267 #[derive(Clone, Debug)]
268 pub struct $name($crate::hmac::Hmac<$hash>);
269
270 impl $crate::mac::Mac for $name {
271 type Tag = $crate::hmac::Tag<Self::TagSize>;
272 type TagSize = <$hash as $crate::hash::Hash>::DigestSize;
273
274 type Key = $crate::hmac::HmacKey<$hash>;
275 type KeySize = <$hash as $crate::block::BlockSize>::BlockSize;
276 type MinKeySize = <$hash as $crate::hash::Hash>::DigestSize;
277
278 #[inline]
279 fn new(key: &Self::Key) -> Self {
280 Self($crate::hmac::Hmac::new(key))
281 }
282
283 #[inline]
284 fn try_new(key: &[u8]) -> ::core::result::Result<Self, $crate::keys::InvalidKey> {
285 use $crate::typenum::Unsigned;
286
287 if key.len() < Self::MinKeySize::USIZE {
288 ::core::result::Result::Err($crate::keys::InvalidKey)
289 } else {
290 let key = $crate::hmac::HmacKey::<$hash>::new(key);
291 ::core::result::Result::Ok(Self::new(&key))
292 }
293 }
294
295 #[inline]
296 fn update(&mut self, data: &[u8]) {
297 self.0.update(data)
298 }
299
300 #[inline]
301 fn tag(self) -> Self::Tag {
302 self.0.tag()
303 }
304 }
305
306 $(impl $crate::oid::Identified for $name {
307 const OID: &'static $crate::oid::Oid = $oid;
308 })?
309 };
310}
311pub(crate) use hmac_impl;
312
313#[cfg(test)]
314#[allow(clippy::wildcard_imports)]
315mod tests {
316 macro_rules! hmac_tests {
317 () => {
318 use crate::{
319 oid::consts::{HMAC_WITH_SHA2_256, HMAC_WITH_SHA2_384, HMAC_WITH_SHA2_512},
320 test_util::test_mac,
321 };
322
323 hmac_impl!(HmacSha2_256, "HMAC-SHA256", Sha256, HMAC_WITH_SHA2_256);
324 hmac_impl!(HmacSha2_384, "HMAC-SHA384", Sha384, HMAC_WITH_SHA2_384);
325 hmac_impl!(HmacSha2_512, "HMAC-SHA512", Sha512, HMAC_WITH_SHA2_512);
326
327 test_mac!(hmac_sha256, HmacSha2_256, MacTest::HmacSha256);
328 test_mac!(hmac_sha384, HmacSha2_384, MacTest::HmacSha384);
329 test_mac!(hmac_sha512, HmacSha2_512, MacTest::HmacSha512);
330 };
331 }
332
333 #[cfg(feature = "bearssl")]
334 mod bearssl {
335 use crate::bearssl::{Sha256, Sha384, Sha512};
336 hmac_tests!();
337 }
338
339 mod rust {
340 use crate::rust::{Sha256, Sha384, Sha512};
341 hmac_tests!();
342 }
343}