cryptography/hash/
hmac.rs1use super::Digest;
7
8pub struct Hmac<H: Digest> {
10 inner: H,
11 outer: H,
12}
13
14impl<H: Digest> Hmac<H> {
15 #[must_use]
17 pub fn new(key: &[u8]) -> Self {
18 let mut key_block = vec![0u8; H::BLOCK_LEN];
19 if key.len() > H::BLOCK_LEN {
20 let mut digest = H::digest(key);
24 key_block[..H::OUTPUT_LEN].copy_from_slice(&digest);
25 crate::ct::zeroize_slice(digest.as_mut_slice());
26 } else {
27 key_block[..key.len()].copy_from_slice(key);
28 }
29
30 let mut ipad = key_block.clone();
31 let mut opad = key_block;
32 for b in &mut ipad {
33 *b ^= 0x36;
34 }
35 for b in &mut opad {
36 *b ^= 0x5c;
37 }
38
39 let mut inner = H::new();
40 inner.update(&ipad);
41 let mut outer = H::new();
42 outer.update(&opad);
43
44 crate::ct::zeroize_slice(ipad.as_mut_slice());
45 crate::ct::zeroize_slice(opad.as_mut_slice());
46
47 Self { inner, outer }
48 }
49
50 pub fn update(&mut self, data: &[u8]) {
52 self.inner.update(data);
53 }
54
55 #[must_use]
56 pub fn finalize(mut self) -> Vec<u8> {
58 let mut inner_digest = vec![0u8; H::OUTPUT_LEN];
59 self.inner.finalize_reset(&mut inner_digest);
63 self.outer.update(&inner_digest);
64 let mut out = vec![0u8; H::OUTPUT_LEN];
65 self.outer.finalize_reset(&mut out);
66 crate::ct::zeroize_slice(inner_digest.as_mut_slice());
67 out
68 }
69
70 #[must_use]
71 pub fn compute(key: &[u8], data: &[u8]) -> Vec<u8> {
73 let mut mac = Self::new(key);
74 mac.update(data);
75 mac.finalize()
76 }
77
78 #[must_use]
79 pub fn verify(key: &[u8], data: &[u8], tag: &[u8]) -> bool {
81 crate::ct::constant_time_eq_mask(&Self::compute(key, data), tag) == u8::MAX
82 }
83}
84
85impl<H: Digest> Drop for Hmac<H> {
86 fn drop(&mut self) {
87 self.inner.zeroize();
88 self.outer.zeroize();
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use crate::{Sha256, Sha3_256, Sha3_512};
96 fn hex(bytes: &[u8]) -> String {
97 let mut out = String::with_capacity(bytes.len() * 2);
98 for b in bytes {
99 use core::fmt::Write;
100 let _ = write!(&mut out, "{b:02x}");
101 }
102 out
103 }
104
105 #[test]
106 fn hmac_sha3_256_known_vector() {
107 let tag = Hmac::<Sha3_256>::compute(b"key", b"The quick brown fox jumps over the lazy dog");
108 assert_eq!(
109 hex(&tag),
110 "8c6e0683409427f8931711b10ca92a50".to_owned() + "6eb1fafa48fadd66d76126f47ac2c333"
111 );
112 }
113
114 #[test]
115 fn hmac_sha3_512_known_vector() {
116 let tag = Hmac::<Sha3_512>::compute(b"key", b"The quick brown fox jumps over the lazy dog");
117 assert_eq!(
118 hex(&tag),
119 "237a35049c40b3ef5ddd960b3dc893d8".to_owned()
120 + "284953b9a4756611b1b61bffcf53edd9"
121 + "79f93547db714b06ef0a692062c609b7"
122 + "0208ab8d4a280ceee40ed8100f293063"
123 );
124 }
125
126 #[test]
127 fn hmac_sha3_256_streaming_matches_one_shot() {
128 let key = (0u8..32).collect::<Vec<_>>();
129 let expected = Hmac::<Sha3_256>::compute(&key, b"abc");
130
131 let mut mac = Hmac::<Sha3_256>::new(&key);
132 mac.update(b"a");
133 mac.update(b"b");
134 mac.update(b"c");
135 let got = mac.finalize();
136
137 assert_eq!(got, expected);
138 assert_eq!(
139 hex(&got),
140 "632f618ac17ba24355d9ee1fd187cf75".to_owned() + "bb5b68e6948804bf6674bf5ee7f1c345"
141 );
142 assert!(Hmac::<Sha3_256>::verify(&key, b"abc", &got));
143 }
144
145 #[test]
146 fn hmac_sha3_256_matches_openssl() {
147 let key = b"key";
148 let msg = b"The quick brown fox jumps over the lazy dog";
149 let Some(expected) = crate::test_utils::run_openssl(
150 &[
151 "dgst",
152 "-sha3-256",
153 "-mac",
154 "HMAC",
155 "-macopt",
156 "hexkey:6b6579",
157 "-binary",
158 ],
159 msg,
160 ) else {
161 return;
162 };
163
164 let tag = Hmac::<Sha3_256>::compute(key, msg);
165 assert_eq!(tag, expected);
166 }
167
168 #[test]
169 fn hmac_sha256_rfc4231_case1() {
170 let key = [0x0bu8; 20];
171 let tag = Hmac::<Sha256>::compute(&key, b"Hi There");
172 assert_eq!(
173 hex(&tag),
174 "b0344c61d8db38535ca8afceaf0bf12b".to_owned() + "881dc200c9833da726e9376c2e32cff7"
175 );
176 }
177
178 #[test]
179 fn hmac_sha256_rfc4231_case2() {
180 let tag = Hmac::<Sha256>::compute(b"Jefe", b"what do ya want for nothing?");
181 assert_eq!(
182 hex(&tag),
183 "5bdcc146bf60754e6a042426089575c7".to_owned() + "5a003f089d2739839dec58b964ec3843"
184 );
185 }
186
187 #[test]
188 fn hmac_sha256_rfc4231_case3() {
189 let key = [0xaau8; 20];
190 let data = [0xddu8; 50];
191 let tag = Hmac::<Sha256>::compute(&key, &data);
192 assert_eq!(
193 hex(&tag),
194 "773ea91e36800e46854db8ebd09181a7".to_owned() + "2959098b3ef8c122d9635514ced565fe"
195 );
196 }
197
198 #[test]
199 fn hmac_sha256_rfc4231_case4() {
200 let key = [
201 0x01u8, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
202 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
203 ];
204 let data = [0xcdu8; 50];
205 let tag = Hmac::<Sha256>::compute(&key, &data);
206 assert_eq!(
207 hex(&tag),
208 "82558a389a443c0ea4cc819899f2083a".to_owned() + "85f0faa3e578f8077a2e3ff46729665b"
209 );
210 }
211
212 #[test]
213 fn hmac_sha256_rfc4231_case5_truncated() {
214 let key = [0x0cu8; 20];
215 let tag = Hmac::<Sha256>::compute(&key, b"Test With Truncation");
216 assert_eq!(hex(&tag[..16]), "a3b6167473100ee06e0c796c2955552b");
217 }
218
219 #[test]
220 fn hmac_sha256_rfc4231_case6() {
221 let key = [0xaau8; 131];
222 let tag = Hmac::<Sha256>::compute(
223 &key,
224 b"Test Using Larger Than Block-Size Key - Hash Key First",
225 );
226 assert_eq!(
227 hex(&tag),
228 "60e431591ee0b67f0d8a26aacbf5b77f".to_owned() + "8e0bc6213728c5140546040f0ee37f54"
229 );
230 }
231
232 #[test]
233 fn hmac_sha256_rfc4231_case7() {
234 let key = [0xaau8; 131];
235 let data = b"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.";
236 let tag = Hmac::<Sha256>::compute(&key, data);
237 assert_eq!(
238 hex(&tag),
239 "9b09ffa71b942fcb27635fbcd5b0e944".to_owned() + "bfdc63644f0713938a7f51535c3a35e2"
240 );
241 }
242
243 #[test]
244 fn hmac_sha256_matches_openssl() {
245 let key = b"key";
246 let msg = b"The quick brown fox jumps over the lazy dog";
247 let Some(expected) = crate::test_utils::run_openssl(
248 &[
249 "dgst",
250 "-sha256",
251 "-mac",
252 "HMAC",
253 "-macopt",
254 "hexkey:6b6579",
255 "-binary",
256 ],
257 msg,
258 ) else {
259 return;
260 };
261
262 let tag = Hmac::<Sha256>::compute(key, msg);
263 assert_eq!(tag.as_slice(), expected.as_slice());
264 }
265}