1use ic_core::traits::{Algorithm, Digest, Mac, SelfTest};
4use ic_core::{ensure, Result, Zeroize};
5
6const MAX_BLOCK_LEN: usize = 144;
11
12#[derive(Clone)]
18pub struct Hmac<D: Digest> {
19 inner: D,
20 outer: D,
21}
22
23pub trait HmacDigest: Digest {
29 const HMAC_ID: &'static str;
31 const HMAC_NAME: &'static str;
33}
34
35impl<D: HmacDigest> Algorithm for Hmac<D> {
36 const ID: &'static str = D::HMAC_ID;
37 const NAME: &'static str = D::HMAC_NAME;
38}
39
40impl<D: HmacDigest> Mac for Hmac<D> {
41 type Tag = D::Output;
42 const TAG_LEN: usize = D::OUTPUT_LEN;
43
44 fn new(key: &[u8]) -> Result<Self> {
45 ensure!(
46 D::BLOCK_LEN <= MAX_BLOCK_LEN,
47 InvalidParameter,
48 "digest block exceeds hmac buffer"
49 );
50
51 let mut pad = [0x36u8; MAX_BLOCK_LEN];
60 let block = &mut pad[..D::BLOCK_LEN];
61 if key.len() > D::BLOCK_LEN {
62 let mut hashed = D::digest(key);
63 for (p, k) in block.iter_mut().zip(hashed.as_ref()) {
64 *p ^= k;
65 }
66 hashed.as_mut().zeroize();
68 } else {
69 for (p, k) in block.iter_mut().zip(key) {
70 *p ^= k;
71 }
72 }
73
74 let mut inner = D::new();
75 inner.update(block);
76 for p in block.iter_mut() {
77 *p ^= 0x36 ^ 0x5c;
78 }
79 let mut outer = D::new();
80 outer.update(block);
81
82 block.zeroize();
83 Ok(Self { inner, outer })
84 }
85
86 fn update(&mut self, data: &[u8]) {
87 self.inner.update(data);
88 }
89
90 fn finalize(mut self) -> Self::Tag {
91 let inner_digest = self.inner.finalize();
92 self.outer.update(inner_digest.as_ref());
93 self.outer.finalize()
94 }
95}
96
97macro_rules! hmac_alias {
98 (
99 $name:ident, $digest:ty, $id:literal, $disp:literal, $taglen:literal,
100 $kat_key:expr, $kat_msg:expr, $kat_tag:literal
101 ) => {
102 #[doc = concat!($disp, ".")]
103 pub type $name = Hmac<$digest>;
104
105 impl HmacDigest for $digest {
106 const HMAC_ID: &'static str = $id;
107 const HMAC_NAME: &'static str = $disp;
108 }
109
110 impl SelfTest for Hmac<$digest> {
111 fn self_test() -> Result<()> {
112 let mut key = [0u8; 20];
113 ic_core::codec::hex_decode($kat_key.as_bytes(), &mut key)?;
114 let tag = <Self as Mac>::mac(&key, $kat_msg)?;
115 let mut want = [0u8; $taglen];
116 ic_core::codec::hex_decode($kat_tag.as_bytes(), &mut want)?;
117 ensure!(
118 ic_core::ct::verify(&want, tag.as_ref()),
119 SelfTestFailed,
120 $id
121 );
122 Ok(())
123 }
124 }
125 };
126}
127
128const KAT_KEY: &str = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b";
130
131hmac_alias!(
138 HmacSha256,
139 ic_hash::Sha256,
140 "hmac-sha2-256",
141 "HMAC-SHA-256",
142 32,
143 KAT_KEY,
144 b"Hi There",
145 "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
146);
147hmac_alias!(
148 HmacSha384,
149 ic_hash::Sha384,
150 "hmac-sha2-384",
151 "HMAC-SHA-384",
152 48,
153 KAT_KEY,
154 b"Hi There",
155 "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6"
156);
157hmac_alias!(
158 HmacSha512,
159 ic_hash::Sha512,
160 "hmac-sha2-512",
161 "HMAC-SHA-512",
162 64,
163 KAT_KEY,
164 b"Hi There",
165 "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"
166);
167hmac_alias!(
168 HmacSha512_256,
169 ic_hash::Sha512_256,
170 "hmac-sha2-512-256",
171 "HMAC-SHA-512/256",
172 32,
173 KAT_KEY,
174 b"Hi There",
175 "9f9126c3d9c3c330d760425ca8a217e31feae31bfe70196ff81642b868402eab"
176);
177hmac_alias!(
178 HmacSha3_256,
179 ic_hash::Sha3_256,
180 "hmac-sha3-256",
181 "HMAC-SHA3-256",
182 32,
183 KAT_KEY,
184 b"Hi There",
185 "ba85192310dffa96e2a3a40e69774351140bb7185e1202cdcc917589f95e16bb"
186);
187hmac_alias!(
188 HmacSha3_512,
189 ic_hash::Sha3_512,
190 "hmac-sha3-512",
191 "HMAC-SHA3-512",
192 64,
193 KAT_KEY,
194 b"Hi There",
195 "eb3fbd4b2eaab8f5c504bd3a41465aacec15770a7cabac531e482f860b5ec7ba47ccb2c6f2afce8f88d22b6dc61380f23a668fd3888bb80537c0a0b86407689e"
196);
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201 use ic_core::codec::hex;
202
203 #[test]
204 fn rfc4231_case_1() {
205 let key = [0x0bu8; 20];
206 assert_eq!(
207 hex(HmacSha256::mac(&key, b"Hi There").unwrap().as_ref()),
208 "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
209 );
210 assert_eq!(
211 hex(HmacSha512::mac(&key, b"Hi There").unwrap().as_ref()),
212 "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"
213 );
214 }
215
216 #[test]
217 fn rfc4231_case_2_short_key() {
218 assert_eq!(
219 hex(HmacSha256::mac(b"Jefe", b"what do ya want for nothing?")
220 .unwrap()
221 .as_ref()),
222 "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"
223 );
224 }
225
226 #[test]
228 fn rfc4231_case_3_long_data() {
229 let key = [0xaau8; 20];
230 let data = [0xddu8; 50];
231 assert_eq!(
232 hex(HmacSha256::mac(&key, &data).unwrap().as_ref()),
233 "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"
234 );
235 }
236
237 #[test]
240 fn rfc4231_case_6_oversized_key() {
241 let key = [0xaau8; 131];
242 assert_eq!(
243 hex(HmacSha256::mac(
244 &key,
245 b"Test Using Larger Than Block-Size Key - Hash Key First"
246 )
247 .unwrap()
248 .as_ref()),
249 "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"
250 );
251 }
252
253 #[test]
254 fn empty_key_and_message() {
255 assert_eq!(
256 hex(HmacSha256::mac(b"", b"").unwrap().as_ref()),
257 "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"
258 );
259 }
260
261 #[test]
262 fn streaming_matches_one_shot() {
263 let data: Vec<u8> = (0..200u8).collect();
264 for split in [0usize, 1, 63, 64, 128, 200] {
265 let mut m = HmacSha256::new(b"k").unwrap();
266 m.update(&data[..split]);
267 m.update(&data[split..]);
268 assert_eq!(m.finalize(), HmacSha256::mac(b"k", &data).unwrap());
269 }
270 }
271
272 #[test]
273 fn verify_rejects_wrong_tag_and_length() {
274 let tag = HmacSha256::mac(b"k", b"m").unwrap();
275 HmacSha256::verify(b"k", b"m", tag.as_ref()).unwrap();
276 let mut bad = tag;
277 bad[0] ^= 1;
278 assert!(HmacSha256::verify(b"k", b"m", bad.as_ref()).is_err());
279 assert!(HmacSha256::verify(b"k", b"m", &tag.as_ref()[..31]).is_err());
280 }
281
282 #[test]
283 fn self_tests_pass() {
284 HmacSha256::self_test().unwrap();
285 HmacSha384::self_test().unwrap();
286 HmacSha512::self_test().unwrap();
287 HmacSha512_256::self_test().unwrap();
288 HmacSha3_256::self_test().unwrap();
289 HmacSha3_512::self_test().unwrap();
290 }
291}