1use ic_core::traits::{Algorithm, BlockCipher, SelfTest};
29use ic_core::{ensure, Result, Zeroize};
30
31const KW_IV: [u8; 8] = [0xa6; 8];
38
39const KWP_IV: [u8; 4] = [0xa6, 0x59, 0x59, 0xa6];
41
42const MAX_BLOCKS: usize = 128;
47
48pub const OVERHEAD: usize = 8;
50
51#[inline(always)]
54fn lt_u32(x: u32, y: u32) -> u32 {
55 ((!x & y) | (!(x ^ y) & x.wrapping_sub(y))) >> 31
56}
57
58#[allow(clippy::needless_range_loop)]
64fn wrap_blocks<C: BlockCipher>(
65 cipher: &C,
66 a: &mut [u8; 8],
67 r: &mut [[u8; 8]],
68 n: usize,
69) -> Result<()> {
70 let mut block = [0u8; 16];
71 for j in 0..6u64 {
72 for i in 0..n {
73 block[..8].copy_from_slice(a);
74 block[8..].copy_from_slice(&r[i]);
75 cipher.encrypt_block(&mut block)?;
76
77 let t = (n as u64) * j + (i as u64) + 1;
79 a.copy_from_slice(&block[..8]);
80 for (k, byte) in t.to_be_bytes().iter().enumerate() {
81 a[k] ^= *byte;
82 }
83 r[i].copy_from_slice(&block[8..]);
84 }
85 }
86 block.zeroize();
87 Ok(())
88}
89
90#[allow(clippy::needless_range_loop)]
92fn unwrap_blocks<C: BlockCipher>(
93 cipher: &C,
94 a: &mut [u8; 8],
95 r: &mut [[u8; 8]],
96 n: usize,
97) -> Result<()> {
98 let mut block = [0u8; 16];
99 for j in (0..6u64).rev() {
100 for i in (0..n).rev() {
101 let t = (n as u64) * j + (i as u64) + 1;
102 block[..8].copy_from_slice(a);
103 for (k, byte) in t.to_be_bytes().iter().enumerate() {
104 block[k] ^= *byte;
105 }
106 block[8..].copy_from_slice(&r[i]);
107 cipher.decrypt_block(&mut block)?;
108
109 a.copy_from_slice(&block[..8]);
110 r[i].copy_from_slice(&block[8..]);
111 }
112 }
113 block.zeroize();
114 Ok(())
115}
116
117macro_rules! key_wrap {
119 ($kw:ident, $kwp:ident, $cipher:ty, $key_len:literal, $kw_id:literal, $kwp_id:literal) => {
120 #[doc = concat!("SP 800-38F KW with AES-", stringify!($key_len), "*8.")]
121 pub struct $kw;
122
123 impl Algorithm for $kw {
124 const ID: &'static str = $kw_id;
125 const NAME: &'static str = $kw_id;
126 }
127
128 impl $kw {
129 pub const KEY_LEN: usize = $key_len;
131
132 pub fn wrap(kek: &[u8], plaintext: &[u8], out: &mut [u8]) -> Result<()> {
138 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
139 ensure!(
140 plaintext.len() % 8 == 0,
141 InvalidLength,
142 "key-wrap input must be a whole number of 64-bit blocks"
143 );
144 let n = plaintext.len() / 8;
145 ensure!(
146 n >= 2,
147 InvalidLength,
148 "key-wrap input must be at least 16 bytes"
149 );
150 ensure!(n <= MAX_BLOCKS, InvalidLength, "key-wrap input too large");
151 ensure!(
152 out.len() == plaintext.len() + OVERHEAD,
153 InvalidLength,
154 "key-wrap output"
155 );
156
157 let cipher = <$cipher>::new(kek)?;
158 let mut a = KW_IV;
159 let mut r = [[0u8; 8]; MAX_BLOCKS];
160 for i in 0..n {
161 r[i].copy_from_slice(&plaintext[i * 8..(i + 1) * 8]);
162 }
163
164 wrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
165
166 out[..8].copy_from_slice(&a);
167 for i in 0..n {
168 out[8 + i * 8..16 + i * 8].copy_from_slice(&r[i]);
169 }
170 for block in r.iter_mut() {
171 block.zeroize();
172 }
173 Ok(())
174 }
175
176 pub fn unwrap(kek: &[u8], ciphertext: &[u8], out: &mut [u8]) -> Result<()> {
181 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
182 ensure!(
183 ciphertext.len() % 8 == 0 && ciphertext.len() >= 24,
184 InvalidLength,
185 "key-wrap ciphertext"
186 );
187 let n = ciphertext.len() / 8 - 1;
188 ensure!(
189 n <= MAX_BLOCKS,
190 InvalidLength,
191 "key-wrap ciphertext too large"
192 );
193 ensure!(
194 out.len() == ciphertext.len() - OVERHEAD,
195 InvalidLength,
196 "key-wrap output"
197 );
198
199 let cipher = <$cipher>::new(kek)?;
200 let mut a = [0u8; 8];
201 a.copy_from_slice(&ciphertext[..8]);
202 let mut r = [[0u8; 8]; MAX_BLOCKS];
203 for i in 0..n {
204 r[i].copy_from_slice(&ciphertext[8 + i * 8..16 + i * 8]);
205 }
206
207 unwrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
208
209 let ok = ic_core::ct::verify(&a, &KW_IV);
212 if !ok {
213 for block in r.iter_mut() {
214 block.zeroize();
215 }
216 return Err(ic_core::err!(AuthenticationFailed, $kw_id));
217 }
218 for i in 0..n {
219 out[i * 8..(i + 1) * 8].copy_from_slice(&r[i]);
220 }
221 for block in r.iter_mut() {
222 block.zeroize();
223 }
224 Ok(())
225 }
226 }
227
228 #[doc = concat!("SP 800-38F KWP with AES-", stringify!($key_len), "*8, per RFC 5649.")]
229 pub struct $kwp;
230
231 impl Algorithm for $kwp {
232 const ID: &'static str = $kwp_id;
233 const NAME: &'static str = $kwp_id;
234 }
235
236 impl $kwp {
237 pub const KEY_LEN: usize = $key_len;
239
240 pub const fn wrapped_len(plaintext_len: usize) -> usize {
243 plaintext_len.div_ceil(8) * 8 + OVERHEAD
244 }
245
246 pub fn wrap(kek: &[u8], plaintext: &[u8], out: &mut [u8]) -> Result<()> {
252 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
253 ensure!(
254 !plaintext.is_empty(),
255 InvalidLength,
256 "key-wrap input is empty"
257 );
258 ensure!(
259 plaintext.len() <= MAX_BLOCKS * 8,
260 InvalidLength,
261 "key-wrap input too large"
262 );
263 ensure!(
264 out.len() == Self::wrapped_len(plaintext.len()),
265 InvalidLength,
266 "key-wrap output"
267 );
268
269 let cipher = <$cipher>::new(kek)?;
270 let mut a = [0u8; 8];
271 a[..4].copy_from_slice(&KWP_IV);
272 a[4..].copy_from_slice(&(plaintext.len() as u32).to_be_bytes());
273
274 let n = plaintext.len().div_ceil(8);
275 let mut r = [[0u8; 8]; MAX_BLOCKS];
276 for (i, chunk) in plaintext.chunks(8).enumerate() {
277 r[i][..chunk.len()].copy_from_slice(chunk);
278 }
279
280 if n == 1 {
281 let mut block = [0u8; 16];
284 block[..8].copy_from_slice(&a);
285 block[8..].copy_from_slice(&r[0]);
286 cipher.encrypt_block(&mut block)?;
287 out.copy_from_slice(&block);
288 block.zeroize();
289 } else {
290 wrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
291 out[..8].copy_from_slice(&a);
292 for i in 0..n {
293 out[8 + i * 8..16 + i * 8].copy_from_slice(&r[i]);
294 }
295 }
296 for block in r.iter_mut() {
297 block.zeroize();
298 }
299 Ok(())
300 }
301
302 pub fn unwrap(kek: &[u8], ciphertext: &[u8], out: &mut [u8]) -> Result<usize> {
307 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
308 ensure!(
309 ciphertext.len() % 8 == 0 && ciphertext.len() >= 16,
310 InvalidLength,
311 "key-wrap ciphertext"
312 );
313 let n = ciphertext.len() / 8 - 1;
314 ensure!(
315 n <= MAX_BLOCKS,
316 InvalidLength,
317 "key-wrap ciphertext too large"
318 );
319 ensure!(out.len() >= n * 8, InvalidLength, "key-wrap output");
320
321 let cipher = <$cipher>::new(kek)?;
322 let mut a = [0u8; 8];
323 let mut r = [[0u8; 8]; MAX_BLOCKS];
324
325 if n == 1 {
326 let mut block = [0u8; 16];
327 block.copy_from_slice(ciphertext);
328 cipher.decrypt_block(&mut block)?;
329 a.copy_from_slice(&block[..8]);
330 r[0].copy_from_slice(&block[8..]);
331 block.zeroize();
332 } else {
333 a.copy_from_slice(&ciphertext[..8]);
334 for i in 0..n {
335 r[i].copy_from_slice(&ciphertext[8 + i * 8..16 + i * 8]);
336 }
337 unwrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
338 }
339
340 let mut ok = ic_core::ct::eq(&a[..4], &KWP_IV);
357 let declared = u32::from_be_bytes([a[4], a[5], a[6], a[7]]);
358 let padded = (n * 8) as u32;
360 let slack = padded.wrapping_sub(declared) >> 3;
366 let bad = ((slack | slack.wrapping_neg()) >> 31) as u8;
367 ok = ok.and(ic_core::ct::Choice::from_u8(!bad & 1));
368
369 let last = n - 1;
374 let mut zeros = 0u8;
375 for (i, &byte) in r[last].iter().enumerate() {
376 let pos = (last * 8 + i) as u32;
377 let is_pad = (lt_u32(pos, declared) as u8) ^ 1;
378 zeros |= byte & core::hint::black_box(is_pad.wrapping_neg());
379 }
380 ok = ok.and(ic_core::ct::is_zero(&[zeros]));
381
382 if !bool::from(ok) {
383 for block in r.iter_mut() {
384 block.zeroize();
385 }
386 return Err(ic_core::err!(AuthenticationFailed, $kwp_id));
387 }
388
389 for i in 0..n {
390 out[i * 8..(i + 1) * 8].copy_from_slice(&r[i]);
391 }
392 for block in r.iter_mut() {
393 block.zeroize();
394 }
395 Ok(declared as usize)
396 }
397 }
398 };
399}
400
401key_wrap!(
402 Aes128Kw,
403 Aes128Kwp,
404 crate::Aes128,
405 16,
406 "aes-128-kw",
407 "aes-128-kwp"
408);
409key_wrap!(
410 Aes192Kw,
411 Aes192Kwp,
412 crate::Aes192,
413 24,
414 "aes-192-kw",
415 "aes-192-kwp"
416);
417key_wrap!(
418 Aes256Kw,
419 Aes256Kwp,
420 crate::Aes256,
421 32,
422 "aes-256-kw",
423 "aes-256-kwp"
424);
425
426impl SelfTest for Aes128Kw {
427 fn self_test() -> Result<()> {
430 let mut kek = [0u8; 16];
431 ic_core::codec::hex_decode(b"000102030405060708090a0b0c0d0e0f", &mut kek)?;
432 let mut key = [0u8; 16];
433 ic_core::codec::hex_decode(b"00112233445566778899aabbccddeeff", &mut key)?;
434 let mut want = [0u8; 24];
435 ic_core::codec::hex_decode(
436 b"1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5",
437 &mut want,
438 )?;
439
440 let mut got = [0u8; 24];
441 Aes128Kw::wrap(&kek, &key, &mut got)?;
442 ensure!(
443 ic_core::ct::verify(&want, &got),
444 SelfTestFailed,
445 "aes-128-kw"
446 );
447
448 let mut back = [0u8; 16];
449 Aes128Kw::unwrap(&kek, &want, &mut back)?;
450 ensure!(
451 ic_core::ct::verify(&key, &back),
452 SelfTestFailed,
453 "aes-128-kw"
454 );
455
456 let mut tampered = want;
457 tampered[0] ^= 1;
458 ensure!(
459 Aes128Kw::unwrap(&kek, &tampered, &mut back).is_err(),
460 SelfTestFailed,
461 "aes-128-kw"
462 );
463 Ok(())
464 }
465}
466
467impl SelfTest for Aes256Kw {
468 fn self_test() -> Result<()> {
470 let mut kek = [0u8; 32];
471 ic_core::codec::hex_decode(
472 b"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
473 &mut kek,
474 )?;
475 let mut key = [0u8; 32];
476 ic_core::codec::hex_decode(
477 b"00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f",
478 &mut key,
479 )?;
480 let mut want = [0u8; 40];
481 ic_core::codec::hex_decode(
482 b"28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21",
483 &mut want,
484 )?;
485
486 let mut got = [0u8; 40];
487 Aes256Kw::wrap(&kek, &key, &mut got)?;
488 ensure!(
489 ic_core::ct::verify(&want, &got),
490 SelfTestFailed,
491 "aes-256-kw"
492 );
493
494 let mut back = [0u8; 32];
495 Aes256Kw::unwrap(&kek, &want, &mut back)?;
496 ensure!(
497 ic_core::ct::verify(&key, &back),
498 SelfTestFailed,
499 "aes-256-kw"
500 );
501 Ok(())
502 }
503}
504
505impl SelfTest for Aes192Kwp {
506 fn self_test() -> Result<()> {
509 let mut kek = [0u8; 24];
510 ic_core::codec::hex_decode(
511 b"5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8",
512 &mut kek,
513 )?;
514 let mut key = [0u8; 20];
515 ic_core::codec::hex_decode(b"c37b7e6492584340bed12207808941155068f738", &mut key)?;
516 let mut want = [0u8; 32];
517 ic_core::codec::hex_decode(
518 b"138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a",
519 &mut want,
520 )?;
521
522 let mut got = [0u8; 32];
523 Aes192Kwp::wrap(&kek, &key, &mut got)?;
524 ensure!(
525 ic_core::ct::verify(&want, &got),
526 SelfTestFailed,
527 "aes-192-kwp"
528 );
529
530 let mut back = [0u8; 24];
531 let len = Aes192Kwp::unwrap(&kek, &want, &mut back)?;
532 ensure!(len == key.len(), SelfTestFailed, "aes-192-kwp");
533 ensure!(
534 ic_core::ct::verify(&key, &back[..len]),
535 SelfTestFailed,
536 "aes-192-kwp"
537 );
538
539 let mut tampered = want;
540 tampered[3] ^= 1;
541 ensure!(
542 Aes192Kwp::unwrap(&kek, &tampered, &mut back).is_err(),
543 SelfTestFailed,
544 "aes-192-kwp"
545 );
546 Ok(())
547 }
548}
549
550impl SelfTest for Aes256Kwp {
551 fn self_test() -> Result<()> {
556 let kek = [0x5au8; 32];
557 let secret = b"nineteen bytes here";
558 let mut wrapped = [0u8; 32];
559 Aes256Kwp::wrap(&kek, secret, &mut wrapped)?;
560
561 let mut out = [0u8; 24];
562 let len = Aes256Kwp::unwrap(&kek, &wrapped, &mut out)?;
563 ensure!(len == secret.len(), SelfTestFailed, "aes-256-kwp");
564 ensure!(
565 ic_core::ct::verify(secret, &out[..len]),
566 SelfTestFailed,
567 "aes-256-kwp"
568 );
569
570 let mut tampered = wrapped;
571 tampered[3] ^= 1;
572 ensure!(
573 Aes256Kwp::unwrap(&kek, &tampered, &mut out).is_err(),
574 SelfTestFailed,
575 "aes-256-kwp"
576 );
577 Ok(())
578 }
579}
580
581#[cfg(test)]
582mod tests {
583 use super::*;
584
585 #[test]
586 fn lt_u32_is_less_than() {
587 let edges = [
588 0u32,
589 1,
590 2,
591 7,
592 8,
593 9,
594 1023,
595 1024,
596 1 << 31,
597 (1 << 31) - 1,
598 u32::MAX - 1,
599 u32::MAX,
600 ];
601 for &x in &edges {
602 for &y in &edges {
603 assert_eq!(lt_u32(x, y), u32::from(x < y), "{x} < {y}");
604 }
605 }
606 }
607
608 #[test]
613 fn kwp_checks_exactly_the_padding() {
614 let kek = [0x42u8; 16];
615 for len in 1..=24usize {
616 let data: std::vec::Vec<u8> = (1..=len as u8).collect();
617 let mut wrapped = std::vec![0u8; Aes128Kwp::wrapped_len(len)];
618 Aes128Kwp::wrap(&kek, &data, &mut wrapped).unwrap();
619 let mut out = [0u8; 32];
620 assert_eq!(Aes128Kwp::unwrap(&kek, &wrapped, &mut out).unwrap(), len);
621 assert_eq!(&out[..len], &data[..]);
622 }
623 }
624
625 #[test]
630 fn kwp_rejects_every_implausible_length_and_dirty_padding() {
631 let kek = [0x24u8; 16];
632 let cipher = crate::Aes128::new(&kek).unwrap();
633 let make = |declared: u32, payload: [u8; 16]| {
635 let mut a = [0u8; 8];
636 a[..4].copy_from_slice(&KWP_IV);
637 a[4..].copy_from_slice(&declared.to_be_bytes());
638 let mut r = [[0u8; 8]; MAX_BLOCKS];
639 r[0].copy_from_slice(&payload[..8]);
640 r[1].copy_from_slice(&payload[8..]);
641 wrap_blocks(&cipher, &mut a, &mut r[..2], 2).unwrap();
642 let mut ct = std::vec![0u8; 24];
643 ct[..8].copy_from_slice(&a);
644 ct[8..16].copy_from_slice(&r[0]);
645 ct[16..].copy_from_slice(&r[1]);
646 ct
647 };
648 let mut clean = [0u8; 16];
649 clean[..9].copy_from_slice(&[7u8; 9]);
650 let mut out = [0u8; 16];
651 assert_eq!(
652 Aes128Kwp::unwrap(&kek, &make(9, clean), &mut out).unwrap(),
653 9
654 );
655 for declared in [0u32, 8, 17, 24, 1 << 31, u32::MAX] {
656 assert!(
657 Aes128Kwp::unwrap(&kek, &make(declared, clean), &mut out).is_err(),
658 "declared {declared}"
659 );
660 }
661 let mut short = [0u8; 16];
664 short[..8].copy_from_slice(&[7u8; 8]);
665 for declared in [1u32, 8] {
666 assert!(
667 Aes128Kwp::unwrap(&kek, &make(declared, short), &mut out).is_err(),
668 "declared {declared} of 16 with zero padding"
669 );
670 }
671 for dirty in 9..16 {
672 let mut p = clean;
673 p[dirty] = 1;
674 assert!(
675 Aes128Kwp::unwrap(&kek, &make(9, p), &mut out).is_err(),
676 "padding byte {dirty}"
677 );
678 }
679 }
680 use ic_core::codec::{hex, unhex};
681
682 #[test]
689 fn rfc_3394_vectors() {
690 struct Case {
691 kek: &'static str,
692 key: &'static str,
693 wrapped: &'static str,
694 }
695 let cases = [
696 Case {
698 kek: "000102030405060708090a0b0c0d0e0f",
699 key: "00112233445566778899aabbccddeeff",
700 wrapped: "1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5",
701 },
702 Case {
704 kek: "000102030405060708090a0b0c0d0e0f1011121314151617",
705 key: "00112233445566778899aabbccddeeff",
706 wrapped: "96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d",
707 },
708 Case {
710 kek: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
711 key: "00112233445566778899aabbccddeeff",
712 wrapped: "64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7",
713 },
714 Case {
716 kek: "000102030405060708090a0b0c0d0e0f1011121314151617",
717 key: "00112233445566778899aabbccddeeff0001020304050607",
718 wrapped: "031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2",
719 },
720 Case {
722 kek: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
723 key: "00112233445566778899aabbccddeeff0001020304050607",
724 wrapped: "a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1",
725 },
726 Case {
728 kek: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
729 key: "00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f",
730 wrapped: "28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21",
731 },
732 ];
733
734 for (index, case) in cases.iter().enumerate() {
735 let kek = unhex(case.kek).unwrap();
736 let key = unhex(case.key).unwrap();
737 let want = unhex(case.wrapped).unwrap();
738
739 let mut got = vec![0u8; key.len() + OVERHEAD];
740 match kek.len() {
741 16 => Aes128Kw::wrap(&kek, &key, &mut got).unwrap(),
742 24 => Aes192Kw::wrap(&kek, &key, &mut got).unwrap(),
743 _ => Aes256Kw::wrap(&kek, &key, &mut got).unwrap(),
744 }
745 assert_eq!(hex(&got), case.wrapped, "RFC 3394 case 4.{}", index + 1);
746
747 let mut back = vec![0u8; key.len()];
748 match kek.len() {
749 16 => Aes128Kw::unwrap(&kek, &want, &mut back).unwrap(),
750 24 => Aes192Kw::unwrap(&kek, &want, &mut back).unwrap(),
751 _ => Aes256Kw::unwrap(&kek, &want, &mut back).unwrap(),
752 }
753 assert_eq!(hex(&back), case.key, "RFC 3394 unwrap 4.{}", index + 1);
754 }
755 }
756
757 #[test]
763 fn rfc_5649_vectors() {
764 let kek = unhex("5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8").unwrap();
765
766 let key = unhex("c37b7e6492584340bed12207808941155068f738").unwrap();
767 let mut wrapped = vec![0u8; Aes192Kwp::wrapped_len(key.len())];
768 Aes192Kwp::wrap(&kek, &key, &mut wrapped).unwrap();
769 assert_eq!(
770 hex(&wrapped),
771 "138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a",
772 "RFC 5649 twenty-byte vector"
773 );
774 let mut back = vec![0u8; wrapped.len() - 8];
775 let len = Aes192Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
776 assert_eq!(hex(&back[..len]), hex(&key));
777
778 let key = unhex("466f7250617369").unwrap();
779 let mut wrapped = vec![0u8; Aes192Kwp::wrapped_len(key.len())];
780 Aes192Kwp::wrap(&kek, &key, &mut wrapped).unwrap();
781 assert_eq!(
782 hex(&wrapped),
783 "afbeb0f07dfbf5419200f2ccb50bb24f",
784 "RFC 5649 seven-byte vector, the single-block path"
785 );
786 let mut back = vec![0u8; wrapped.len() - 8];
787 let len = Aes192Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
788 assert_eq!(hex(&back[..len]), hex(&key));
789 }
790
791 #[test]
792 fn wrapping_round_trips_at_every_supported_size() {
793 let kek = [0x11u8; 32];
794 for blocks in 2..=16usize {
795 let plaintext: Vec<u8> = (0..blocks * 8).map(|i| i as u8).collect();
796 let mut wrapped = vec![0u8; plaintext.len() + OVERHEAD];
797 Aes256Kw::wrap(&kek, &plaintext, &mut wrapped).unwrap();
798 assert_ne!(&wrapped[8..], &plaintext[..], "the data must be encrypted");
799
800 let mut back = vec![0u8; plaintext.len()];
801 Aes256Kw::unwrap(&kek, &wrapped, &mut back).unwrap();
802 assert_eq!(back, plaintext, "{blocks} blocks");
803 }
804 }
805
806 #[test]
808 fn tampering_is_rejected() {
809 let kek = [0x22u8; 32];
810 let plaintext = [0x33u8; 32];
811 let mut wrapped = [0u8; 40];
812 Aes256Kw::wrap(&kek, &plaintext, &mut wrapped).unwrap();
813
814 let mut back = [0u8; 32];
815 for byte in 0..wrapped.len() {
816 let mut bad = wrapped;
817 bad[byte] ^= 1;
818 assert!(
819 Aes256Kw::unwrap(&kek, &bad, &mut back).is_err(),
820 "a flip in byte {byte} was accepted"
821 );
822 }
823 assert!(Aes256Kw::unwrap(&[0x23u8; 32], &wrapped, &mut back).is_err());
825 }
826
827 #[test]
829 fn wrapping_is_deterministic() {
830 let kek = [0x44u8; 32];
831 let plaintext = [0x55u8; 24];
832 let mut a = [0u8; 32];
833 let mut b = [0u8; 32];
834 Aes256Kw::wrap(&kek, &plaintext, &mut a).unwrap();
835 Aes256Kw::wrap(&kek, &plaintext, &mut b).unwrap();
836 assert_eq!(a, b);
837 }
838
839 #[test]
842 fn every_output_block_depends_on_every_input_block() {
843 let kek = [0x66u8; 32];
844 let base = [0u8; 64];
845 let mut reference = [0u8; 72];
846 Aes256Kw::wrap(&kek, &base, &mut reference).unwrap();
847
848 for index in [0usize, 8, 32, 63] {
849 let mut changed = base;
850 changed[index] ^= 1;
851 let mut wrapped = [0u8; 72];
852 Aes256Kw::wrap(&kek, &changed, &mut wrapped).unwrap();
853
854 let same = reference
855 .chunks(8)
856 .zip(wrapped.chunks(8))
857 .filter(|(a, b)| a == b)
858 .count();
859 assert_eq!(
860 same, 0,
861 "changing input byte {index} left {same} output blocks unchanged"
862 );
863 }
864 }
865
866 #[test]
867 fn padded_wrapping_round_trips_at_every_length() {
868 let kek = [0x77u8; 32];
869 for len in 1..=64usize {
870 let plaintext: Vec<u8> = (0..len).map(|i| (i * 7) as u8).collect();
871 let mut wrapped = vec![0u8; Aes256Kwp::wrapped_len(len)];
872 Aes256Kwp::wrap(&kek, &plaintext, &mut wrapped).unwrap();
873 assert_eq!(wrapped.len(), len.div_ceil(8) * 8 + 8);
874
875 let mut back = vec![0u8; wrapped.len() - 8];
876 let got = Aes256Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
877 assert_eq!(got, len, "recovered length at {len}");
878 assert_eq!(&back[..got], &plaintext[..], "round trip at {len}");
879 }
880 }
881
882 #[test]
885 fn the_single_block_padded_path_works() {
886 let kek = [0x88u8; 32];
887 for len in 1..=8usize {
888 let plaintext = vec![0xabu8; len];
889 let mut wrapped = vec![0u8; 16];
890 Aes256Kwp::wrap(&kek, &plaintext, &mut wrapped).unwrap();
891 assert_eq!(wrapped.len(), 16, "one block plus the header");
892
893 let mut back = [0u8; 8];
894 let got = Aes256Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
895 assert_eq!(got, len);
896 assert_eq!(&back[..got], &plaintext[..]);
897 }
898 }
899
900 #[test]
901 fn padded_wrapping_rejects_tampering() {
902 let kek = [0x99u8; 32];
903 let plaintext = b"a secret of awkward length";
904 let mut wrapped = vec![0u8; Aes256Kwp::wrapped_len(plaintext.len())];
905 Aes256Kwp::wrap(&kek, plaintext, &mut wrapped).unwrap();
906
907 let mut back = vec![0u8; wrapped.len() - 8];
908 for byte in 0..wrapped.len() {
909 let mut bad = wrapped.clone();
910 bad[byte] ^= 1;
911 assert!(
912 Aes256Kwp::unwrap(&kek, &bad, &mut back).is_err(),
913 "a flip in byte {byte} was accepted"
914 );
915 }
916 }
917
918 #[test]
919 fn lengths_are_validated() {
920 let kek = [0u8; 32];
921 let mut out = [0u8; 64];
922
923 assert!(Aes256Kw::wrap(&kek, &[0u8; 20], &mut out[..28]).is_err());
925 assert!(Aes256Kw::wrap(&kek, &[0u8; 8], &mut out[..16]).is_err());
927 assert!(Aes256Kw::wrap(&kek, &[], &mut out[..8]).is_err());
929 assert!(Aes256Kwp::wrap(&kek, &[], &mut out[..8]).is_err());
930 assert!(Aes256Kw::wrap(&[0u8; 16], &[0u8; 16], &mut out[..24]).is_err());
932 assert!(Aes256Kw::wrap(&kek, &[0u8; 16], &mut out[..23]).is_err());
934 assert!(Aes256Kw::unwrap(&kek, &[0u8; 16], &mut out[..8]).is_err());
936 }
937
938 #[test]
939 fn all_self_tests_pass() {
940 Aes128Kw::self_test().unwrap();
941 Aes256Kw::self_test().unwrap();
942 Aes192Kwp::self_test().unwrap();
943 Aes256Kwp::self_test().unwrap();
944 }
945}