crypt_io/mac/
hmac_impl.rs1use hmac::{Hmac, Mac};
11use sha2::{Sha256, Sha512};
12
13use super::{HMAC_SHA256_OUTPUT_LEN, HMAC_SHA512_OUTPUT_LEN};
14use crate::error::{Error, Result};
15
16type HmacSha256Inner = Hmac<Sha256>;
17type HmacSha512Inner = Hmac<Sha512>;
18
19pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<[u8; HMAC_SHA256_OUTPUT_LEN]> {
38 let mut mac =
39 HmacSha256Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha256 init"))?;
40 mac.update(data);
41 Ok(mac.finalize().into_bytes().into())
42}
43
44pub fn hmac_sha256_verify(key: &[u8], data: &[u8], expected_tag: &[u8]) -> Result<bool> {
73 let mut mac =
74 HmacSha256Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha256 init"))?;
75 mac.update(data);
76 Ok(mac.verify_slice(expected_tag).is_ok())
77}
78
79pub fn hmac_sha512(key: &[u8], data: &[u8]) -> Result<[u8; HMAC_SHA512_OUTPUT_LEN]> {
96 let mut mac =
97 HmacSha512Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha512 init"))?;
98 mac.update(data);
99 Ok(mac.finalize().into_bytes().into())
100}
101
102pub fn hmac_sha512_verify(key: &[u8], data: &[u8], expected_tag: &[u8]) -> Result<bool> {
108 let mut mac =
109 HmacSha512Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha512 init"))?;
110 mac.update(data);
111 Ok(mac.verify_slice(expected_tag).is_ok())
112}
113
114#[derive(Debug, Clone)]
136pub struct HmacSha256 {
137 inner: HmacSha256Inner,
138}
139
140impl HmacSha256 {
141 pub fn new(key: &[u8]) -> Result<Self> {
148 let inner =
149 HmacSha256Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha256 init"))?;
150 Ok(Self { inner })
151 }
152
153 pub fn update(&mut self, data: &[u8]) -> &mut Self {
156 self.inner.update(data);
157 self
158 }
159
160 #[must_use]
162 pub fn finalize(self) -> [u8; HMAC_SHA256_OUTPUT_LEN] {
163 self.inner.finalize().into_bytes().into()
164 }
165
166 #[must_use]
170 pub fn verify(self, expected_tag: &[u8]) -> bool {
171 self.inner.verify_slice(expected_tag).is_ok()
172 }
173}
174
175#[derive(Debug, Clone)]
192pub struct HmacSha512 {
193 inner: HmacSha512Inner,
194}
195
196impl HmacSha512 {
197 pub fn new(key: &[u8]) -> Result<Self> {
203 let inner =
204 HmacSha512Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha512 init"))?;
205 Ok(Self { inner })
206 }
207
208 pub fn update(&mut self, data: &[u8]) -> &mut Self {
211 self.inner.update(data);
212 self
213 }
214
215 #[must_use]
217 pub fn finalize(self) -> [u8; HMAC_SHA512_OUTPUT_LEN] {
218 self.inner.finalize().into_bytes().into()
219 }
220
221 #[must_use]
225 pub fn verify(self, expected_tag: &[u8]) -> bool {
226 self.inner.verify_slice(expected_tag).is_ok()
227 }
228}
229
230#[cfg(test)]
231#[allow(clippy::unwrap_used, clippy::expect_used, unused_results)]
232mod tests {
233 use super::*;
234
235 fn hex_to_bytes(s: &str) -> alloc::vec::Vec<u8> {
236 hex::decode(s).expect("valid hex")
237 }
238
239 #[test]
248 fn hmac_sha256_rfc4231_case1() {
249 let key = hex_to_bytes("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
250 let data = b"Hi There";
251 let expected =
252 hex_to_bytes("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7");
253 assert_eq!(&hmac_sha256(&key, data).unwrap()[..], &expected[..]);
254 assert!(hmac_sha256_verify(&key, data, &expected).unwrap());
255 }
256
257 #[test]
259 fn hmac_sha256_rfc4231_case2() {
260 let key = b"Jefe";
261 let data = b"what do ya want for nothing?";
262 let expected =
263 hex_to_bytes("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843");
264 assert_eq!(&hmac_sha256(key, data).unwrap()[..], &expected[..]);
265 assert!(hmac_sha256_verify(key, data, &expected).unwrap());
266 }
267
268 #[test]
272 fn hmac_sha512_rfc4231_case1() {
273 let key = hex_to_bytes("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
274 let data = b"Hi There";
275 let expected = hex_to_bytes(
276 "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\
277 daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854",
278 );
279 assert_eq!(&hmac_sha512(&key, data).unwrap()[..], &expected[..]);
280 assert!(hmac_sha512_verify(&key, data, &expected).unwrap());
281 }
282
283 #[test]
285 fn hmac_sha512_rfc4231_case2() {
286 let key = b"Jefe";
287 let data = b"what do ya want for nothing?";
288 let expected = hex_to_bytes(
289 "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\
290 9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737",
291 );
292 assert_eq!(&hmac_sha512(key, data).unwrap()[..], &expected[..]);
293 assert!(hmac_sha512_verify(key, data, &expected).unwrap());
294 }
295
296 #[test]
299 fn hmac_sha256_verify_rejects_wrong_tag() {
300 let tag = hmac_sha256(b"key", b"data").unwrap();
301 let mut tampered = tag;
302 tampered[0] ^= 0x01;
303 assert!(!hmac_sha256_verify(b"key", b"data", &tampered).unwrap());
304 }
305
306 #[test]
307 fn hmac_sha256_verify_rejects_wrong_key() {
308 let tag = hmac_sha256(b"correct", b"data").unwrap();
309 assert!(!hmac_sha256_verify(b"wrong", b"data", &tag).unwrap());
310 }
311
312 #[test]
313 fn hmac_sha256_verify_rejects_wrong_data() {
314 let tag = hmac_sha256(b"key", b"original").unwrap();
315 assert!(!hmac_sha256_verify(b"key", b"tampered", &tag).unwrap());
316 }
317
318 #[test]
319 fn hmac_sha256_verify_rejects_truncated_tag() {
320 let tag = hmac_sha256(b"key", b"data").unwrap();
321 assert!(!hmac_sha256_verify(b"key", b"data", &tag[..16]).unwrap());
324 }
325
326 #[test]
327 fn hmac_sha512_verify_rejects_wrong_tag() {
328 let tag = hmac_sha512(b"key", b"data").unwrap();
329 let mut tampered = tag;
330 tampered[0] ^= 0x01;
331 assert!(!hmac_sha512_verify(b"key", b"data", &tampered).unwrap());
332 }
333
334 #[test]
337 fn hmac_sha256_streaming_equals_one_shot() {
338 let key = b"shared secret";
339 let data = b"the quick brown fox jumps over the lazy dog";
340 let one_shot = hmac_sha256(key, data).unwrap();
341 let mut m = HmacSha256::new(key).unwrap();
342 m.update(&data[..10]);
343 m.update(&data[10..25]);
344 m.update(&data[25..]);
345 assert_eq!(m.finalize(), one_shot);
346 }
347
348 #[test]
349 fn hmac_sha512_streaming_equals_one_shot() {
350 let key = b"shared secret";
351 let data = b"the quick brown fox jumps over the lazy dog";
352 let one_shot = hmac_sha512(key, data).unwrap();
353 let mut m = HmacSha512::new(key).unwrap();
354 m.update(&data[..10]);
355 m.update(&data[10..25]);
356 m.update(&data[25..]);
357 assert_eq!(m.finalize(), one_shot);
358 }
359
360 #[test]
361 fn hmac_sha256_streaming_chain_returns_self() {
362 let mut m = HmacSha256::new(b"k").unwrap();
363 m.update(b"chain").update(b"-friendly");
364 assert_eq!(m.finalize(), hmac_sha256(b"k", b"chain-friendly").unwrap());
365 }
366
367 #[test]
368 fn hmac_sha512_streaming_chain_returns_self() {
369 let mut m = HmacSha512::new(b"k").unwrap();
370 m.update(b"chain").update(b"-friendly");
371 assert_eq!(m.finalize(), hmac_sha512(b"k", b"chain-friendly").unwrap());
372 }
373
374 #[test]
377 fn hmac_sha256_streaming_verify_accepts_correct_tag() {
378 let key = b"k";
379 let tag = hmac_sha256(key, b"message").unwrap();
380 let mut m = HmacSha256::new(key).unwrap();
381 m.update(b"message");
382 assert!(m.verify(&tag));
383 }
384
385 #[test]
386 fn hmac_sha256_streaming_verify_rejects_wrong_tag() {
387 let key = b"k";
388 let tag = hmac_sha256(key, b"message").unwrap();
389 let mut tampered = tag;
390 tampered[0] ^= 0xff;
391 let mut m = HmacSha256::new(key).unwrap();
392 m.update(b"message");
393 assert!(!m.verify(&tampered));
394 }
395
396 #[test]
397 fn hmac_sha512_streaming_verify_accepts_correct_tag() {
398 let key = b"k";
399 let tag = hmac_sha512(key, b"message").unwrap();
400 let mut m = HmacSha512::new(key).unwrap();
401 m.update(b"message");
402 assert!(m.verify(&tag));
403 }
404
405 #[test]
408 fn hmac_sha256_accepts_empty_key() {
409 let tag = hmac_sha256(&[], b"data").unwrap();
410 assert!(hmac_sha256_verify(&[], b"data", &tag).unwrap());
411 }
412
413 #[test]
414 fn hmac_sha256_accepts_long_key() {
415 let key = [0xaau8; 256];
418 let tag = hmac_sha256(&key, b"data").unwrap();
419 assert!(hmac_sha256_verify(&key, b"data", &tag).unwrap());
420 }
421}