1use crypto_core::{CryptoError, MacAlgorithm, MacFailureKind, MacHash};
6use subtle::ConstantTimeEq;
7use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
8
9pub const HMAC_SHA256_TAG_LENGTH: usize = 32;
11pub const HMAC_SHA384_TAG_LENGTH: usize = 48;
13pub const HMAC_SHA512_TAG_LENGTH: usize = 64;
15pub const HMAC_MAX_KEY_LENGTH: usize = 4096;
21
22#[derive(Zeroize, ZeroizeOnDrop)]
27pub struct HmacKey {
28 bytes: Zeroizing<Vec<u8>>,
29}
30
31impl HmacKey {
32 pub fn from_slice(input: &[u8]) -> Result<Self, CryptoError> {
38 if input.is_empty() || input.len() > HMAC_MAX_KEY_LENGTH {
39 return Err(CryptoError::Mac {
40 hash: MacHash::Sha2_256,
41 kind: MacFailureKind::InvalidKeyLength,
42 });
43 }
44
45 Ok(Self {
46 bytes: Zeroizing::new(input.to_vec()),
47 })
48 }
49
50 pub fn as_bytes(&self) -> &[u8] {
52 &self.bytes
53 }
54}
55
56#[derive(Clone, Zeroize, ZeroizeOnDrop)]
58pub struct HmacTag {
59 bytes: [u8; HMAC_SHA512_TAG_LENGTH],
60 len: usize,
61}
62
63impl PartialEq for HmacTag {
64 fn eq(&self, other: &Self) -> bool {
65 bool::from(self.bytes.ct_eq(&other.bytes) & self.len.ct_eq(&other.len))
69 }
70}
71
72impl Eq for HmacTag {}
73
74impl HmacTag {
75 pub fn from_slice(algorithm: MacAlgorithm, input: &[u8]) -> Result<Self, CryptoError> {
77 let expected = tag_length(algorithm);
78 if input.len() != expected {
79 return Err(CryptoError::Mac {
80 hash: mac_hash(algorithm),
81 kind: MacFailureKind::InvalidTagLength,
82 });
83 }
84
85 let mut bytes = [0u8; HMAC_SHA512_TAG_LENGTH];
86 bytes[..expected].copy_from_slice(input);
87 Ok(Self {
88 bytes,
89 len: expected,
90 })
91 }
92
93 pub fn as_bytes(&self) -> &[u8] {
95 &self.bytes[..self.len]
96 }
97
98 pub fn len(&self) -> usize {
100 self.len
101 }
102
103 pub fn is_empty(&self) -> bool {
105 self.len == 0
106 }
107
108 pub fn into_vec(self) -> Vec<u8> {
110 self.as_bytes().to_vec()
111 }
112}
113
114impl core::fmt::Debug for HmacTag {
115 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
116 write!(f, "HmacTag(len={})", self.len)
117 }
118}
119
120pub(crate) fn tag_length(algorithm: MacAlgorithm) -> usize {
121 match algorithm {
122 MacAlgorithm::HmacSha256 => HMAC_SHA256_TAG_LENGTH,
123 MacAlgorithm::HmacSha384 => HMAC_SHA384_TAG_LENGTH,
124 MacAlgorithm::HmacSha512 => HMAC_SHA512_TAG_LENGTH,
125 }
126}
127
128pub(crate) fn mac_hash(algorithm: MacAlgorithm) -> MacHash {
129 match algorithm {
130 MacAlgorithm::HmacSha256 => MacHash::Sha2_256,
131 MacAlgorithm::HmacSha384 => MacHash::Sha2_384,
132 MacAlgorithm::HmacSha512 => MacHash::Sha2_512,
133 }
134}