1use ic_core::traits::{Algorithm, SelfTest};
32use ic_core::{ensure, Result};
33use ic_hash::sp800_185::{right_encode, MAX_ENCODE};
34
35const MAX_TAG: usize = 64;
37
38macro_rules! kmac {
40 ($name:ident, $cshake:ty, $id:literal, $disp:literal, $bits:literal) => {
41 #[doc = concat!("SP 800-185 ", $disp, ", offering ", $bits, "-bit security.")]
42 #[derive(Clone)]
43 pub struct $name {
44 inner: $cshake,
45 }
46
47 impl Algorithm for $name {
48 const ID: &'static str = $id;
49 const NAME: &'static str = $disp;
50 }
51
52 impl $name {
53 pub fn new(key: &[u8], custom: &[u8]) -> Self {
57 let mut inner = <$cshake>::new(b"KMAC", custom);
58 inner.absorb_bytepadded_string(key);
59 Self { inner }
60 }
61
62 pub fn update(&mut self, data: &[u8]) {
64 self.inner.update(data);
65 }
66
67 pub fn finalize(mut self, out: &mut [u8]) -> Result<()> {
73 ic_core::module::operational()?;
74 let mut buf = [0u8; MAX_ENCODE];
75 let used = right_encode((out.len() as u64) * 8, &mut buf);
76 self.inner.update(&buf[..used]);
77 self.inner.finalize_xof_unchecked(out);
78 Ok(())
79 }
80
81 pub fn finalize_xof(mut self, out: &mut [u8]) -> Result<()> {
86 ic_core::module::operational()?;
87 let mut buf = [0u8; MAX_ENCODE];
88 let used = right_encode(0, &mut buf);
89 self.inner.update(&buf[..used]);
90 self.inner.finalize_xof_unchecked(out);
91 Ok(())
92 }
93
94 pub fn mac(key: &[u8], custom: &[u8], data: &[u8], out: &mut [u8]) -> Result<()> {
96 let mut k = Self::new(key, custom);
97 k.update(data);
98 k.finalize(out)
99 }
100
101 pub fn mac_xof(key: &[u8], custom: &[u8], data: &[u8], out: &mut [u8]) -> Result<()> {
103 let mut k = Self::new(key, custom);
104 k.update(data);
105 k.finalize_xof(out)
106 }
107
108 pub fn verify(key: &[u8], custom: &[u8], data: &[u8], tag: &[u8]) -> Result<()> {
114 ic_core::module::operational()?;
115 ensure!(
116 !tag.is_empty() && tag.len() <= MAX_TAG,
117 InvalidLength,
118 "kmac tag length"
119 );
120 let mut expected = [0u8; MAX_TAG];
121 Self::mac(key, custom, data, &mut expected[..tag.len()])?;
122 ensure!(
123 ic_core::ct::verify(&expected[..tag.len()], tag),
124 AuthenticationFailed,
125 $id
126 );
127 Ok(())
128 }
129 }
130
131 impl SelfTest for $name {
132 fn self_test() -> Result<()> {
142 let key = [0x40u8; 32];
143 let mut short = [0u8; 32];
144 let mut long = [0u8; 64];
145 Self::mac(&key, b"self-test", b"message", &mut short)?;
146 Self::mac(&key, b"self-test", b"message", &mut long)?;
147
148 ensure!(short[..] != long[..32], SelfTestFailed, $id);
151
152 let mut again = [0u8; 32];
153 Self::mac(&key, b"self-test", b"message", &mut again)?;
154 ensure!(ic_core::ct::verify(&short, &again), SelfTestFailed, $id);
155 Self::verify(&key, b"self-test", b"message", &short)?;
156
157 let mut tampered = short;
158 tampered[0] ^= 1;
159 ensure!(
160 Self::verify(&key, b"self-test", b"message", &tampered).is_err(),
161 SelfTestFailed,
162 $id
163 );
164 ensure!(
166 Self::verify(&key, b"other", b"message", &short).is_err(),
167 SelfTestFailed,
168 $id
169 );
170 Ok(())
171 }
172 }
173 };
174}
175
176kmac!(Kmac128, ic_hash::CShake128, "kmac128", "KMAC128", "128");
177kmac!(Kmac256, ic_hash::CShake256, "kmac256", "KMAC256", "256");
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use ic_hash::sp800_185::{left_encode, CShake128, CShake256};
183
184 fn reference_kmac(
194 rate: usize,
195 key: &[u8],
196 custom: &[u8],
197 data: &[u8],
198 out: &mut [u8],
199 xof: bool,
200 ) {
201 fn enc(x: u64) -> Vec<u8> {
202 let mut bytes = x.to_be_bytes().to_vec();
203 while bytes.len() > 1 && bytes[0] == 0 {
204 bytes.remove(0);
205 }
206 let mut v = vec![bytes.len() as u8];
207 v.extend_from_slice(&bytes);
208 v
209 }
210 fn renc(x: u64) -> Vec<u8> {
211 let mut bytes = x.to_be_bytes().to_vec();
212 while bytes.len() > 1 && bytes[0] == 0 {
213 bytes.remove(0);
214 }
215 let n = bytes.len() as u8;
216 bytes.push(n);
217 bytes
218 }
219
220 let mut message = enc(rate as u64);
222 message.extend_from_slice(&enc((key.len() as u64) * 8));
223 message.extend_from_slice(key);
224 while !message.len().is_multiple_of(rate) {
225 message.push(0);
226 }
227 message.extend_from_slice(data);
228 message.extend_from_slice(&renc(if xof { 0 } else { (out.len() as u64) * 8 }));
229
230 if rate == 168 {
231 CShake128::xof(b"KMAC", custom, &message, out).unwrap();
232 } else {
233 CShake256::xof(b"KMAC", custom, &message, out).unwrap();
234 }
235 }
236
237 #[test]
238 fn kmac_matches_an_independent_construction() {
239 let cases: &[(&[u8], &[u8], &[u8])] = &[
240 (&[0x40u8; 32], b"", b""),
241 (&[0x40u8; 32], b"My Tagged Application", b"\x00\x01\x02\x03"),
242 (b"short key", b"S", &[0xa5u8; 500]),
243 (&[0x11u8; 200], b"", b"key longer than the rate"),
244 ];
245
246 for (key, custom, data) in cases {
247 for len in [16usize, 32, 64] {
248 let mut want = vec![0u8; len];
249 let mut got = vec![0u8; len];
250
251 reference_kmac(168, key, custom, data, &mut want, false);
252 Kmac128::mac(key, custom, data, &mut got).unwrap();
253 assert_eq!(got, want, "KMAC128 fixed, {len} bytes");
254
255 reference_kmac(136, key, custom, data, &mut want, false);
256 Kmac256::mac(key, custom, data, &mut got).unwrap();
257 assert_eq!(got, want, "KMAC256 fixed, {len} bytes");
258
259 reference_kmac(168, key, custom, data, &mut want, true);
260 Kmac128::mac_xof(key, custom, data, &mut got).unwrap();
261 assert_eq!(got, want, "KMAC128 xof, {len} bytes");
262
263 reference_kmac(136, key, custom, data, &mut want, true);
264 Kmac256::mac_xof(key, custom, data, &mut got).unwrap();
265 assert_eq!(got, want, "KMAC256 xof, {len} bytes");
266 }
267 }
268 }
269
270 #[test]
273 fn tag_length_is_bound_into_the_tag() {
274 let key = [0x7fu8; 32];
275 let mut short = [0u8; 32];
276 let mut long = [0u8; 64];
277 Kmac128::mac(&key, b"", b"message", &mut short).unwrap();
278 Kmac128::mac(&key, b"", b"message", &mut long).unwrap();
279 assert_ne!(short[..], long[..32], "truncation must not forge");
280 }
281
282 #[test]
285 fn the_xof_variant_extends_rather_than_changes() {
286 let key = [0x7fu8; 32];
287 let mut short = [0u8; 32];
288 let mut long = [0u8; 64];
289 Kmac128::mac_xof(&key, b"", b"message", &mut short).unwrap();
290 Kmac128::mac_xof(&key, b"", b"message", &mut long).unwrap();
291 assert_eq!(short[..], long[..32], "the xof output is a prefix");
292 }
293
294 #[test]
295 fn streaming_matches_the_one_shot() {
296 let key = [0x31u8; 32];
297 let data = [0x62u8; 777];
298 let mut one = [0u8; 32];
299 Kmac256::mac(&key, b"S", &data, &mut one).unwrap();
300
301 let mut k = Kmac256::new(&key, b"S");
302 for chunk in data.chunks(13) {
303 k.update(chunk);
304 }
305 let mut streamed = [0u8; 32];
306 k.finalize(&mut streamed).unwrap();
307 assert_eq!(one, streamed);
308 }
309
310 #[test]
311 fn keys_and_customization_both_change_the_tag() {
312 let mut a = [0u8; 32];
313 let mut b = [0u8; 32];
314 Kmac128::mac(&[1u8; 32], b"S", b"m", &mut a).unwrap();
315 Kmac128::mac(&[2u8; 32], b"S", b"m", &mut b).unwrap();
316 assert_ne!(a, b, "the key matters");
317 Kmac128::mac(&[1u8; 32], b"T", b"m", &mut b).unwrap();
318 assert_ne!(a, b, "the customization matters");
319 }
320
321 #[test]
322 fn verification_rejects_tampering_and_bad_lengths() {
323 let key = [0x55u8; 32];
324 let mut tag = [0u8; 32];
325 Kmac128::mac(&key, b"S", b"message", &mut tag).unwrap();
326 Kmac128::verify(&key, b"S", b"message", &tag).unwrap();
327
328 for bit in [0usize, 7, 128, 255] {
329 let mut bad = tag;
330 bad[bit / 8] ^= 1 << (bit % 8);
331 assert!(Kmac128::verify(&key, b"S", b"message", &bad).is_err());
332 }
333 assert!(Kmac128::verify(&key, b"S", b"messagf", &tag).is_err());
334 assert!(Kmac128::verify(&[0u8; 32], b"S", b"message", &tag).is_err());
335 assert!(
336 Kmac128::verify(&key, b"S", b"message", &[]).is_err(),
337 "empty tag"
338 );
339 assert!(
340 Kmac128::verify(&key, b"S", b"message", &[0u8; 65]).is_err(),
341 "over-long tag"
342 );
343 }
344
345 #[test]
346 fn both_self_tests_pass() {
347 Kmac128::self_test().unwrap();
348 Kmac256::self_test().unwrap();
349 }
350
351 #[test]
354 fn the_encoding_helpers_are_reachable() {
355 let mut buf = [0u8; MAX_ENCODE];
356 assert_eq!(left_encode(168, &mut buf), 2);
357 assert_eq!(&buf[..2], &[0x01, 0xa8]);
358 }
359}