1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use std::{any::Any, borrow::Cow, convert::Infallible};
5
6use hmac::{
7 digest::{
8 common::{Key, KeySizeUser},
9 FixedOutput, KeyInit, Output, OutputSizeUser, Update,
10 },
11 Hmac,
12};
13use sha2::Sha256;
14use vitaminc_protected::{Acceptable, Controlled, DefaultScope, Protected, ProtectedDigest};
15use zeroize::{ZeroizeOnDrop, Zeroizing};
16
17use vitaminc_prf::{
18 Context, MapPrf, Prf, PrfBuildError, PrfEncoding, PrfError, PrfKeyInit, PrfValue, PrfVisitor,
19 ReadyPrf, ResolvedPrf, ResolvedVisitor, SeqPrf,
20};
21
22type PassthroughValue = Box<dyn Any + Send + 'static>;
23
24const SHA256_BLOCK_SIZE: usize = 64;
25const SHA256_OUTPUT_SIZE: usize = 32;
26const IPAD: u8 = 0x36;
27const OPAD: u8 = 0x5C;
28
29pub const KEY_LEN: usize = 32;
31
32pub const MIN_KEY_LEN: usize = KEY_LEN;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub struct WeakKeyError {
39 len: usize,
40}
41
42impl WeakKeyError {
43 pub fn len(&self) -> usize {
45 self.len
46 }
47
48 pub fn is_empty(&self) -> bool {
50 self.len == 0
51 }
52}
53
54impl std::fmt::Display for WeakKeyError {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 write!(
57 f,
58 "HMAC-SHA256 PRF key must be at least {MIN_KEY_LEN} bytes, got {}",
59 self.len
60 )
61 }
62}
63
64impl std::error::Error for WeakKeyError {}
65
66struct ZeroizingHmacSha256 {
76 digest: Sha256,
78 opad_digest: Sha256,
80}
81
82impl KeySizeUser for ZeroizingHmacSha256 {
83 type KeySize = <Hmac<Sha256> as KeySizeUser>::KeySize;
84}
85
86impl KeyInit for ZeroizingHmacSha256 {
87 fn new(key: &Key<Self>) -> Self {
88 Self::new_from_slice(key.as_slice()).expect("HMAC-SHA256 accepts keys of any length")
89 }
90
91 fn new_from_slice(key: &[u8]) -> Result<Self, hmac::digest::InvalidLength> {
92 let mut block = Zeroizing::new([0_u8; SHA256_BLOCK_SIZE]);
93 if key.len() <= SHA256_BLOCK_SIZE {
94 block[..key.len()].copy_from_slice(key);
95 } else {
96 let mut hashed = Zeroizing::new([0_u8; SHA256_OUTPUT_SIZE]);
97 let mut hasher = Sha256::default();
98 Update::update(&mut hasher, key);
99 FixedOutput::finalize_into(hasher, (&mut *hashed).into());
100 block[..hashed.len()].copy_from_slice(hashed.as_slice());
101 }
102
103 block.iter_mut().for_each(|byte| *byte ^= IPAD);
104 let mut digest = Sha256::default();
105 Update::update(&mut digest, block.as_slice());
106
107 block.iter_mut().for_each(|byte| *byte ^= IPAD ^ OPAD);
108 let mut opad_digest = Sha256::default();
109 Update::update(&mut opad_digest, block.as_slice());
110
111 Ok(Self {
112 digest,
113 opad_digest,
114 })
115 }
116}
117
118impl OutputSizeUser for ZeroizingHmacSha256 {
119 type OutputSize = <Hmac<Sha256> as OutputSizeUser>::OutputSize;
120}
121
122impl Update for ZeroizingHmacSha256 {
123 fn update(&mut self, data: &[u8]) {
124 Update::update(&mut self.digest, data);
125 }
126}
127
128impl FixedOutput for ZeroizingHmacSha256 {
129 fn finalize_into(self, out: &mut Output<Self>) {
130 let Self {
131 digest,
132 mut opad_digest,
133 } = self;
134 let mut inner = Zeroizing::new([0_u8; SHA256_OUTPUT_SIZE]);
137 FixedOutput::finalize_into(digest, (&mut *inner).into());
138 Update::update(&mut opad_digest, inner.as_slice());
139 FixedOutput::finalize_into(opad_digest, out);
140 }
141}
142
143impl ZeroizeOnDrop for ZeroizingHmacSha256 {}
144
145#[derive(ZeroizeOnDrop)]
200pub struct HmacSha256Prf {
201 key: Protected<Vec<u8>>,
202}
203
204impl PrfKeyInit for HmacSha256Prf {
205 type Key = Protected<[u8; KEY_LEN]>;
206 type KeyError = WeakKeyError;
207
208 fn new(key: Self::Key) -> Self {
212 Self::from_vec(Protected::new(key.risky_ref().to_vec()))
217 }
218
219 fn try_from_bytes(key: Protected<Vec<u8>>) -> Result<Self, Self::KeyError> {
228 let len = key.risky_ref().len();
229 if len < MIN_KEY_LEN {
230 return Err(WeakKeyError { len });
231 }
232 Ok(Self::from_vec(key))
233 }
234}
235
236impl HmacSha256Prf {
237 fn from_vec(key: Protected<Vec<u8>>) -> Self {
238 Self { key }
239 }
240
241 fn derive<T>(&self, data: &T, encoding: PrfEncoding, context: &Context<'_>) -> [u8; 32]
242 where
243 T: Controlled + Acceptable<DefaultScope>,
244 T::Inner: AsRef<[u8]>,
245 {
246 let mut hmac: ProtectedDigest<ZeroizingHmacSha256> =
247 ProtectedDigest::new_with_key(&self.key)
248 .expect("HMAC-SHA256 accepts keys of any length");
249
250 hmac.update_public(&3_u64.to_le_bytes());
253 hmac.update_public(&(encoding.as_bytes().len() as u64).to_le_bytes());
254 hmac.update_public(encoding.as_bytes());
255 hmac.update_public(&(context.as_bytes().len() as u64).to_le_bytes());
256 hmac.update_public(context.as_bytes());
257 hmac.update_public(&(data.risky_ref().as_ref().len() as u64).to_le_bytes());
258 hmac.update(data);
259
260 let mut block = [0_u8; 32];
261 hmac.finalize_public_into(&mut block);
262 block
263 }
264
265 fn resolved<T: Send + 'static>(
266 result: Result<T, PrfError<Infallible>>,
267 ) -> ReadyPrf<T, Infallible> {
268 ReadyPrf::new(result)
269 }
270}
271
272impl Prf for HmacSha256Prf {
273 type Block = [u8; 32];
274 type BackendError = Infallible;
275 type Passthrough = PassthroughValue;
276 type SeqPrf<'a> = HmacSeqPrf<'a>;
277 type MapPrf<'a> = HmacMapPrf<'a>;
278 type Ok<T>
279 = ReadyPrf<T, Infallible>
280 where
281 T: Send + 'static;
282
283 fn prf_bytes_vec<V>(
284 &self,
285 data: Protected<Vec<u8>>,
286 encoding: PrfEncoding,
287 context: Context<'static>,
288 visitor: V,
289 ) -> Self::Ok<V::Value>
290 where
291 V: PrfVisitor<Self::Block, Self::Passthrough>,
292 {
293 let block = self.derive(&data, encoding, &context);
294 Self::resolved(visitor.visit_block(block).map_err(PrfError::Visitor))
295 }
296
297 fn prf_bytes_array<const N: usize, V>(
300 &self,
301 data: Protected<[u8; N]>,
302 encoding: PrfEncoding,
303 context: Context<'static>,
304 visitor: V,
305 ) -> Self::Ok<V::Value>
306 where
307 V: PrfVisitor<Self::Block, Self::Passthrough>,
308 {
309 let block = self.derive(&data, encoding, &context);
310 Self::resolved(visitor.visit_block(block).map_err(PrfError::Visitor))
311 }
312
313 fn prf_seq(&self, size_hint: Option<usize>) -> Self::SeqPrf<'_> {
314 HmacSeqPrf {
315 backend: self,
316 values: Vec::with_capacity(size_hint.unwrap_or(0)),
317 error: None,
318 }
319 }
320
321 fn prf_map(&self, size_hint: Option<usize>) -> Self::MapPrf<'_> {
322 HmacMapPrf {
323 backend: self,
324 entries: Vec::with_capacity(size_hint.unwrap_or(0)),
325 pending_key: None,
326 error: None,
327 }
328 }
329
330 fn prf_none<V>(&self, _context: Context<'static>, visitor: V) -> Self::Ok<V::Value>
331 where
332 V: PrfVisitor<Self::Block, Self::Passthrough>,
333 {
334 Self::resolved(visitor.visit_absent().map_err(PrfError::Visitor))
335 }
336
337 fn passthrough<V>(&self, value: Self::Passthrough, visitor: V) -> Self::Ok<V::Value>
338 where
339 V: PrfVisitor<Self::Block, Self::Passthrough>,
340 {
341 Self::resolved(visitor.visit_passthrough(value).map_err(PrfError::Visitor))
342 }
343
344 fn passthrough_boxed<V>(
345 &self,
346 value: Box<dyn Any + Send + 'static>,
347 visitor: V,
348 ) -> Self::Ok<V::Value>
349 where
350 V: PrfVisitor<Self::Block, Self::Passthrough>,
351 {
352 self.passthrough(value, visitor)
353 }
354
355 fn failure<T>(&self, error: PrfError<Self::BackendError>) -> Self::Ok<T>
356 where
357 T: Send + 'static,
358 {
359 Self::resolved(Err(error))
360 }
361}
362
363pub struct HmacSeqPrf<'a> {
365 backend: &'a HmacSha256Prf,
366 values: Vec<ResolvedPrf<[u8; 32], PassthroughValue>>,
367 error: Option<PrfError<Infallible>>,
368}
369
370impl SeqPrf for HmacSeqPrf<'_> {
371 type Prf = HmacSha256Prf;
372 type Block = [u8; 32];
373 type BackendError = Infallible;
374 type Passthrough = PassthroughValue;
375
376 fn prf_next<T>(mut self, value: T, context: Context<'static>) -> Self
377 where
378 T: PrfValue,
379 {
380 if self.error.is_none() {
381 match value
382 .prf_visit_with_context(self.backend, context, ResolvedVisitor)
383 .into_result()
384 {
385 Ok(value) => self.values.push(value),
386 Err(error) => self.error = Some(error),
387 }
388 }
389 self
390 }
391
392 fn passthrough_next(mut self, value: Self::Passthrough) -> Self {
393 if self.error.is_none() {
394 self.values.push(ResolvedPrf::Passthrough(value));
395 }
396 self
397 }
398
399 fn passthrough_next_boxed(self, value: Box<dyn Any + Send + 'static>) -> Self {
400 self.passthrough_next(value)
401 }
402
403 fn end<V>(self, visitor: V) -> <Self::Prf as Prf>::Ok<V::Value>
404 where
405 V: PrfVisitor<Self::Block, Self::Passthrough>,
406 {
407 if let Some(error) = self.error {
408 return self.backend.failure(error);
409 }
410 HmacSha256Prf::resolved(
411 ResolvedPrf::Sequence(self.values)
412 .visit(visitor)
413 .map_err(PrfError::Visitor),
414 )
415 }
416}
417
418pub struct HmacMapPrf<'a> {
420 backend: &'a HmacSha256Prf,
421 entries: Vec<(String, ResolvedPrf<[u8; 32], PassthroughValue>)>,
422 pending_key: Option<String>,
423 error: Option<PrfError<Infallible>>,
424}
425
426impl HmacMapPrf<'_> {
427 fn set_build_error(&mut self, error: PrfBuildError) {
428 if self.error.is_none() {
429 self.error = Some(PrfError::Build(error));
430 }
431 }
432
433 fn is_duplicate_key(&self, key: &str) -> bool {
434 self.entries.iter().any(|(existing, _)| existing == key)
435 }
436}
437
438impl MapPrf for HmacMapPrf<'_> {
439 type Prf = HmacSha256Prf;
440 type Block = [u8; 32];
441 type BackendError = Infallible;
442 type Passthrough = PassthroughValue;
443
444 fn prf_key<K>(mut self, key: K) -> Self
445 where
446 K: Into<Cow<'static, str>>,
447 {
448 if self.pending_key.is_some() {
449 self.set_build_error(PrfBuildError::KeyWithoutValue);
450 } else if self.error.is_none() {
451 self.pending_key = Some(key.into().into_owned());
452 }
453 self
454 }
455
456 fn prf_value<T>(mut self, value: T, context: Context<'static>) -> Self
457 where
458 T: PrfValue,
459 {
460 let Some(key) = self.pending_key.take() else {
461 self.set_build_error(PrfBuildError::ValueWithoutKey);
462 return self;
463 };
464 if self.error.is_some() {
465 return self;
466 }
467 if self.is_duplicate_key(&key) {
468 self.set_build_error(PrfBuildError::DuplicateKey);
469 return self;
470 }
471 let entry_context = context.for_map_entry(&key);
472 match value
473 .prf_visit_with_context(self.backend, entry_context, ResolvedVisitor)
474 .into_result()
475 {
476 Ok(value) => self.entries.push((key, value)),
477 Err(error) => self.error = Some(error),
478 }
479 self
480 }
481
482 fn passthrough_entry<K>(mut self, key: K, value: Self::Passthrough) -> Self
483 where
484 K: Into<Cow<'static, str>>,
485 {
486 if self.pending_key.is_some() {
487 self.set_build_error(PrfBuildError::KeyWithoutValue);
488 } else if self.error.is_none() {
489 let key = key.into().into_owned();
490 if self.is_duplicate_key(&key) {
491 self.set_build_error(PrfBuildError::DuplicateKey);
492 } else {
493 self.entries.push((key, ResolvedPrf::Passthrough(value)));
494 }
495 }
496 self
497 }
498
499 fn passthrough_entry_boxed<K>(self, key: K, value: Box<dyn Any + Send + 'static>) -> Self
500 where
501 K: Into<Cow<'static, str>>,
502 {
503 self.passthrough_entry(key, value)
504 }
505
506 fn end<V>(mut self, visitor: V) -> <Self::Prf as Prf>::Ok<V::Value>
507 where
508 V: PrfVisitor<Self::Block, Self::Passthrough>,
509 {
510 if self.pending_key.is_some() {
511 self.set_build_error(PrfBuildError::DanglingKey);
512 }
513 if let Some(error) = self.error {
514 return self.backend.failure(error);
515 }
516 HmacSha256Prf::resolved(
517 ResolvedPrf::Map(self.entries)
518 .visit(visitor)
519 .map_err(PrfError::Visitor),
520 )
521 }
522}
523
524#[cfg(test)]
525mod tests {
526 use super::ZeroizingHmacSha256;
527 use hmac::{
528 digest::{FixedOutput, KeyInit, Update},
529 Hmac, Mac,
530 };
531 use quickcheck_macros::quickcheck;
532 use sha2::Sha256;
533 use vitaminc_protected::ProtectedDigest;
534 use zeroize::ZeroizeOnDrop;
535
536 #[test]
537 fn hmac_sha256_state_zeroizes_on_drop() {
538 fn assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
539 assert_zeroize_on_drop::<ProtectedDigest<ZeroizingHmacSha256>>();
540 assert_zeroize_on_drop::<Sha256>();
543 }
544
545 fn zeroizing_hmac(key: &[u8], data: &[u8]) -> [u8; 32] {
546 let mut hmac = ZeroizingHmacSha256::new_from_slice(key).unwrap();
547 Update::update(&mut hmac, data);
548 let mut out = [0_u8; 32];
549 FixedOutput::finalize_into(hmac, (&mut out).into());
550 out
551 }
552
553 #[quickcheck]
554 fn matches_rustcrypto_hmac(key: Vec<u8>, data: Vec<u8>) -> bool {
555 let reference = {
556 let mut mac = <Hmac<Sha256> as KeyInit>::new_from_slice(&key).unwrap();
557 Mac::update(&mut mac, &data);
558 mac.finalize().into_bytes()
559 };
560 zeroizing_hmac(&key, &data).as_slice() == reference.as_slice()
561 }
562
563 #[test]
564 fn matches_rustcrypto_hmac_at_key_normalization_boundaries() {
565 for key_len in [0, 1, 63, 64, 65, 131] {
568 let key = vec![0xaa_u8; key_len];
569 let mut mac = <Hmac<Sha256> as KeyInit>::new_from_slice(&key).unwrap();
570 Mac::update(&mut mac, b"boundary");
571 assert_eq!(
572 zeroizing_hmac(&key, b"boundary").as_slice(),
573 mac.finalize().into_bytes().as_slice(),
574 "key length {key_len}"
575 );
576 }
577 }
578}