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