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 ic_core::module::operational()?;
139 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
140 ensure!(
141 plaintext.len() % 8 == 0,
142 InvalidLength,
143 "key-wrap input must be a whole number of 64-bit blocks"
144 );
145 let n = plaintext.len() / 8;
146 ensure!(
147 n >= 2,
148 InvalidLength,
149 "key-wrap input must be at least 16 bytes"
150 );
151 ensure!(n <= MAX_BLOCKS, InvalidLength, "key-wrap input too large");
152 ensure!(
153 out.len() == plaintext.len() + OVERHEAD,
154 InvalidLength,
155 "key-wrap output"
156 );
157
158 let cipher = <$cipher>::new(kek)?;
159 let mut a = KW_IV;
160 let mut r = [[0u8; 8]; MAX_BLOCKS];
161 for i in 0..n {
162 r[i].copy_from_slice(&plaintext[i * 8..(i + 1) * 8]);
163 }
164
165 wrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
166
167 out[..8].copy_from_slice(&a);
168 for i in 0..n {
169 out[8 + i * 8..16 + i * 8].copy_from_slice(&r[i]);
170 }
171 for block in r.iter_mut() {
172 block.zeroize();
173 }
174 Ok(())
175 }
176
177 pub fn unwrap(kek: &[u8], ciphertext: &[u8], out: &mut [u8]) -> Result<()> {
182 ic_core::module::operational()?;
183 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
184 ensure!(
185 ciphertext.len() % 8 == 0 && ciphertext.len() >= 24,
186 InvalidLength,
187 "key-wrap ciphertext"
188 );
189 let n = ciphertext.len() / 8 - 1;
190 ensure!(
191 n <= MAX_BLOCKS,
192 InvalidLength,
193 "key-wrap ciphertext too large"
194 );
195 ensure!(
196 out.len() == ciphertext.len() - OVERHEAD,
197 InvalidLength,
198 "key-wrap output"
199 );
200
201 let cipher = <$cipher>::new(kek)?;
202 let mut a = [0u8; 8];
203 a.copy_from_slice(&ciphertext[..8]);
204 let mut r = [[0u8; 8]; MAX_BLOCKS];
205 for i in 0..n {
206 r[i].copy_from_slice(&ciphertext[8 + i * 8..16 + i * 8]);
207 }
208
209 unwrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
210
211 let ok = ic_core::ct::verify(&a, &KW_IV);
214 if !ok {
215 for block in r.iter_mut() {
216 block.zeroize();
217 }
218 return Err(ic_core::err!(AuthenticationFailed, $kw_id));
219 }
220 for i in 0..n {
221 out[i * 8..(i + 1) * 8].copy_from_slice(&r[i]);
222 }
223 for block in r.iter_mut() {
224 block.zeroize();
225 }
226 Ok(())
227 }
228 }
229
230 #[doc = concat!("SP 800-38F KWP with AES-", stringify!($key_len), "*8, per RFC 5649.")]
231 pub struct $kwp;
232
233 impl Algorithm for $kwp {
234 const ID: &'static str = $kwp_id;
235 const NAME: &'static str = $kwp_id;
236 }
237
238 impl $kwp {
239 pub const KEY_LEN: usize = $key_len;
241
242 pub const fn wrapped_len(plaintext_len: usize) -> usize {
245 plaintext_len.div_ceil(8) * 8 + OVERHEAD
246 }
247
248 pub fn wrap(kek: &[u8], plaintext: &[u8], out: &mut [u8]) -> Result<()> {
254 ic_core::module::operational()?;
255 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
256 ensure!(
257 !plaintext.is_empty(),
258 InvalidLength,
259 "key-wrap input is empty"
260 );
261 ensure!(
262 plaintext.len() <= MAX_BLOCKS * 8,
263 InvalidLength,
264 "key-wrap input too large"
265 );
266 ensure!(
267 out.len() == Self::wrapped_len(plaintext.len()),
268 InvalidLength,
269 "key-wrap output"
270 );
271
272 let cipher = <$cipher>::new(kek)?;
273 let mut a = [0u8; 8];
274 a[..4].copy_from_slice(&KWP_IV);
275 a[4..].copy_from_slice(&(plaintext.len() as u32).to_be_bytes());
276
277 let n = plaintext.len().div_ceil(8);
278 let mut r = [[0u8; 8]; MAX_BLOCKS];
279 for (i, chunk) in plaintext.chunks(8).enumerate() {
280 r[i][..chunk.len()].copy_from_slice(chunk);
281 }
282
283 if n == 1 {
284 let mut block = [0u8; 16];
287 block[..8].copy_from_slice(&a);
288 block[8..].copy_from_slice(&r[0]);
289 cipher.encrypt_block(&mut block)?;
290 out.copy_from_slice(&block);
291 block.zeroize();
292 } else {
293 wrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
294 out[..8].copy_from_slice(&a);
295 for i in 0..n {
296 out[8 + i * 8..16 + i * 8].copy_from_slice(&r[i]);
297 }
298 }
299 for block in r.iter_mut() {
300 block.zeroize();
301 }
302 Ok(())
303 }
304
305 pub fn unwrap(kek: &[u8], ciphertext: &[u8], out: &mut [u8]) -> Result<usize> {
310 ic_core::module::operational()?;
311 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
312 ensure!(
313 ciphertext.len() % 8 == 0 && ciphertext.len() >= 16,
314 InvalidLength,
315 "key-wrap ciphertext"
316 );
317 let n = ciphertext.len() / 8 - 1;
318 ensure!(
319 n <= MAX_BLOCKS,
320 InvalidLength,
321 "key-wrap ciphertext too large"
322 );
323 ensure!(out.len() >= n * 8, InvalidLength, "key-wrap output");
324
325 let cipher = <$cipher>::new(kek)?;
326 let mut a = [0u8; 8];
327 let mut r = [[0u8; 8]; MAX_BLOCKS];
328
329 if n == 1 {
330 let mut block = [0u8; 16];
331 block.copy_from_slice(ciphertext);
332 cipher.decrypt_block(&mut block)?;
333 a.copy_from_slice(&block[..8]);
334 r[0].copy_from_slice(&block[8..]);
335 block.zeroize();
336 } else {
337 a.copy_from_slice(&ciphertext[..8]);
338 for i in 0..n {
339 r[i].copy_from_slice(&ciphertext[8 + i * 8..16 + i * 8]);
340 }
341 unwrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
342 }
343
344 let mut ok = ic_core::ct::eq(&a[..4], &KWP_IV);
361 let declared = u32::from_be_bytes([a[4], a[5], a[6], a[7]]);
362 let padded = (n * 8) as u32;
364 let slack = padded.wrapping_sub(declared) >> 3;
370 let bad = ((slack | slack.wrapping_neg()) >> 31) as u8;
371 ok = ok.and(ic_core::ct::Choice::from_u8(!bad & 1));
372
373 let last = n - 1;
378 let mut zeros = 0u8;
379 for (i, &byte) in r[last].iter().enumerate() {
380 let pos = (last * 8 + i) as u32;
381 let is_pad = (lt_u32(pos, declared) as u8) ^ 1;
382 zeros |= byte & core::hint::black_box(is_pad.wrapping_neg());
383 }
384 ok = ok.and(ic_core::ct::is_zero(&[zeros]));
385
386 if !bool::from(ok) {
387 for block in r.iter_mut() {
388 block.zeroize();
389 }
390 return Err(ic_core::err!(AuthenticationFailed, $kwp_id));
391 }
392
393 for i in 0..n {
394 out[i * 8..(i + 1) * 8].copy_from_slice(&r[i]);
395 }
396 for block in r.iter_mut() {
397 block.zeroize();
398 }
399 Ok(declared as usize)
400 }
401 }
402 };
403}
404
405key_wrap!(
406 Aes128Kw,
407 Aes128Kwp,
408 crate::Aes128,
409 16,
410 "aes-128-kw",
411 "aes-128-kwp"
412);
413key_wrap!(
414 Aes192Kw,
415 Aes192Kwp,
416 crate::Aes192,
417 24,
418 "aes-192-kw",
419 "aes-192-kwp"
420);
421key_wrap!(
422 Aes256Kw,
423 Aes256Kwp,
424 crate::Aes256,
425 32,
426 "aes-256-kw",
427 "aes-256-kwp"
428);
429
430impl SelfTest for Aes128Kw {
431 fn self_test() -> Result<()> {
434 let mut kek = [0u8; 16];
435 ic_core::codec::hex_decode(b"000102030405060708090a0b0c0d0e0f", &mut kek)?;
436 let mut key = [0u8; 16];
437 ic_core::codec::hex_decode(b"00112233445566778899aabbccddeeff", &mut key)?;
438 let mut want = [0u8; 24];
439 ic_core::codec::hex_decode(
440 b"1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5",
441 &mut want,
442 )?;
443
444 let mut got = [0u8; 24];
445 Aes128Kw::wrap(&kek, &key, &mut got)?;
446 ensure!(
447 ic_core::ct::verify(&want, &got),
448 SelfTestFailed,
449 "aes-128-kw"
450 );
451
452 let mut back = [0u8; 16];
453 Aes128Kw::unwrap(&kek, &want, &mut back)?;
454 ensure!(
455 ic_core::ct::verify(&key, &back),
456 SelfTestFailed,
457 "aes-128-kw"
458 );
459
460 let mut tampered = want;
461 tampered[0] ^= 1;
462 ensure!(
463 Aes128Kw::unwrap(&kek, &tampered, &mut back).is_err(),
464 SelfTestFailed,
465 "aes-128-kw"
466 );
467 Ok(())
468 }
469}
470
471impl SelfTest for Aes256Kw {
472 fn self_test() -> Result<()> {
474 let mut kek = [0u8; 32];
475 ic_core::codec::hex_decode(
476 b"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
477 &mut kek,
478 )?;
479 let mut key = [0u8; 32];
480 ic_core::codec::hex_decode(
481 b"00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f",
482 &mut key,
483 )?;
484 let mut want = [0u8; 40];
485 ic_core::codec::hex_decode(
486 b"28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21",
487 &mut want,
488 )?;
489
490 let mut got = [0u8; 40];
491 Aes256Kw::wrap(&kek, &key, &mut got)?;
492 ensure!(
493 ic_core::ct::verify(&want, &got),
494 SelfTestFailed,
495 "aes-256-kw"
496 );
497
498 let mut back = [0u8; 32];
499 Aes256Kw::unwrap(&kek, &want, &mut back)?;
500 ensure!(
501 ic_core::ct::verify(&key, &back),
502 SelfTestFailed,
503 "aes-256-kw"
504 );
505 Ok(())
506 }
507}
508
509impl SelfTest for Aes192Kwp {
510 fn self_test() -> Result<()> {
513 let mut kek = [0u8; 24];
514 ic_core::codec::hex_decode(
515 b"5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8",
516 &mut kek,
517 )?;
518 let mut key = [0u8; 20];
519 ic_core::codec::hex_decode(b"c37b7e6492584340bed12207808941155068f738", &mut key)?;
520 let mut want = [0u8; 32];
521 ic_core::codec::hex_decode(
522 b"138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a",
523 &mut want,
524 )?;
525
526 let mut got = [0u8; 32];
527 Aes192Kwp::wrap(&kek, &key, &mut got)?;
528 ensure!(
529 ic_core::ct::verify(&want, &got),
530 SelfTestFailed,
531 "aes-192-kwp"
532 );
533
534 let mut back = [0u8; 24];
535 let len = Aes192Kwp::unwrap(&kek, &want, &mut back)?;
536 ensure!(len == key.len(), SelfTestFailed, "aes-192-kwp");
537 ensure!(
538 ic_core::ct::verify(&key, &back[..len]),
539 SelfTestFailed,
540 "aes-192-kwp"
541 );
542
543 let mut tampered = want;
544 tampered[3] ^= 1;
545 ensure!(
546 Aes192Kwp::unwrap(&kek, &tampered, &mut back).is_err(),
547 SelfTestFailed,
548 "aes-192-kwp"
549 );
550 Ok(())
551 }
552}
553
554impl SelfTest for Aes256Kwp {
555 fn self_test() -> Result<()> {
560 let kek = [0x5au8; 32];
561 let secret = b"nineteen bytes here";
562 let mut wrapped = [0u8; 32];
563 Aes256Kwp::wrap(&kek, secret, &mut wrapped)?;
564
565 let mut out = [0u8; 24];
566 let len = Aes256Kwp::unwrap(&kek, &wrapped, &mut out)?;
567 ensure!(len == secret.len(), SelfTestFailed, "aes-256-kwp");
568 ensure!(
569 ic_core::ct::verify(secret, &out[..len]),
570 SelfTestFailed,
571 "aes-256-kwp"
572 );
573
574 let mut tampered = wrapped;
575 tampered[3] ^= 1;
576 ensure!(
577 Aes256Kwp::unwrap(&kek, &tampered, &mut out).is_err(),
578 SelfTestFailed,
579 "aes-256-kwp"
580 );
581 Ok(())
582 }
583}
584
585#[cfg(test)]
586mod tests {
587 use super::*;
588
589 #[test]
590 fn lt_u32_is_less_than() {
591 let edges = [
592 0u32,
593 1,
594 2,
595 7,
596 8,
597 9,
598 1023,
599 1024,
600 1 << 31,
601 (1 << 31) - 1,
602 u32::MAX - 1,
603 u32::MAX,
604 ];
605 for &x in &edges {
606 for &y in &edges {
607 assert_eq!(lt_u32(x, y), u32::from(x < y), "{x} < {y}");
608 }
609 }
610 }
611
612 #[test]
617 fn kwp_checks_exactly_the_padding() {
618 let kek = [0x42u8; 16];
619 for len in 1..=24usize {
620 let data: std::vec::Vec<u8> = (1..=len as u8).collect();
621 let mut wrapped = std::vec![0u8; Aes128Kwp::wrapped_len(len)];
622 Aes128Kwp::wrap(&kek, &data, &mut wrapped).unwrap();
623 let mut out = [0u8; 32];
624 assert_eq!(Aes128Kwp::unwrap(&kek, &wrapped, &mut out).unwrap(), len);
625 assert_eq!(&out[..len], &data[..]);
626 }
627 }
628
629 #[test]
634 fn kwp_rejects_every_implausible_length_and_dirty_padding() {
635 let kek = [0x24u8; 16];
636 let cipher = crate::Aes128::new(&kek).unwrap();
637 let make = |declared: u32, payload: [u8; 16]| {
639 let mut a = [0u8; 8];
640 a[..4].copy_from_slice(&KWP_IV);
641 a[4..].copy_from_slice(&declared.to_be_bytes());
642 let mut r = [[0u8; 8]; MAX_BLOCKS];
643 r[0].copy_from_slice(&payload[..8]);
644 r[1].copy_from_slice(&payload[8..]);
645 wrap_blocks(&cipher, &mut a, &mut r[..2], 2).unwrap();
646 let mut ct = std::vec![0u8; 24];
647 ct[..8].copy_from_slice(&a);
648 ct[8..16].copy_from_slice(&r[0]);
649 ct[16..].copy_from_slice(&r[1]);
650 ct
651 };
652 let mut clean = [0u8; 16];
653 clean[..9].copy_from_slice(&[7u8; 9]);
654 let mut out = [0u8; 16];
655 assert_eq!(
656 Aes128Kwp::unwrap(&kek, &make(9, clean), &mut out).unwrap(),
657 9
658 );
659 for declared in [0u32, 8, 17, 24, 1 << 31, u32::MAX] {
660 assert!(
661 Aes128Kwp::unwrap(&kek, &make(declared, clean), &mut out).is_err(),
662 "declared {declared}"
663 );
664 }
665 let mut short = [0u8; 16];
668 short[..8].copy_from_slice(&[7u8; 8]);
669 for declared in [1u32, 8] {
670 assert!(
671 Aes128Kwp::unwrap(&kek, &make(declared, short), &mut out).is_err(),
672 "declared {declared} of 16 with zero padding"
673 );
674 }
675 for dirty in 9..16 {
676 let mut p = clean;
677 p[dirty] = 1;
678 assert!(
679 Aes128Kwp::unwrap(&kek, &make(9, p), &mut out).is_err(),
680 "padding byte {dirty}"
681 );
682 }
683 }
684 use ic_core::codec::{hex, unhex};
685
686 #[test]
693 fn rfc_3394_vectors() {
694 struct Case {
695 kek: &'static str,
696 key: &'static str,
697 wrapped: &'static str,
698 }
699 let cases = [
700 Case {
702 kek: "000102030405060708090a0b0c0d0e0f",
703 key: "00112233445566778899aabbccddeeff",
704 wrapped: "1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5",
705 },
706 Case {
708 kek: "000102030405060708090a0b0c0d0e0f1011121314151617",
709 key: "00112233445566778899aabbccddeeff",
710 wrapped: "96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d",
711 },
712 Case {
714 kek: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
715 key: "00112233445566778899aabbccddeeff",
716 wrapped: "64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7",
717 },
718 Case {
720 kek: "000102030405060708090a0b0c0d0e0f1011121314151617",
721 key: "00112233445566778899aabbccddeeff0001020304050607",
722 wrapped: "031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2",
723 },
724 Case {
726 kek: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
727 key: "00112233445566778899aabbccddeeff0001020304050607",
728 wrapped: "a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1",
729 },
730 Case {
732 kek: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
733 key: "00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f",
734 wrapped: "28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21",
735 },
736 ];
737
738 for (index, case) in cases.iter().enumerate() {
739 let kek = unhex(case.kek).unwrap();
740 let key = unhex(case.key).unwrap();
741 let want = unhex(case.wrapped).unwrap();
742
743 let mut got = vec![0u8; key.len() + OVERHEAD];
744 match kek.len() {
745 16 => Aes128Kw::wrap(&kek, &key, &mut got).unwrap(),
746 24 => Aes192Kw::wrap(&kek, &key, &mut got).unwrap(),
747 _ => Aes256Kw::wrap(&kek, &key, &mut got).unwrap(),
748 }
749 assert_eq!(hex(&got), case.wrapped, "RFC 3394 case 4.{}", index + 1);
750
751 let mut back = vec![0u8; key.len()];
752 match kek.len() {
753 16 => Aes128Kw::unwrap(&kek, &want, &mut back).unwrap(),
754 24 => Aes192Kw::unwrap(&kek, &want, &mut back).unwrap(),
755 _ => Aes256Kw::unwrap(&kek, &want, &mut back).unwrap(),
756 }
757 assert_eq!(hex(&back), case.key, "RFC 3394 unwrap 4.{}", index + 1);
758 }
759 }
760
761 #[test]
767 fn rfc_5649_vectors() {
768 let kek = unhex("5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8").unwrap();
769
770 let key = unhex("c37b7e6492584340bed12207808941155068f738").unwrap();
771 let mut wrapped = vec![0u8; Aes192Kwp::wrapped_len(key.len())];
772 Aes192Kwp::wrap(&kek, &key, &mut wrapped).unwrap();
773 assert_eq!(
774 hex(&wrapped),
775 "138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a",
776 "RFC 5649 twenty-byte vector"
777 );
778 let mut back = vec![0u8; wrapped.len() - 8];
779 let len = Aes192Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
780 assert_eq!(hex(&back[..len]), hex(&key));
781
782 let key = unhex("466f7250617369").unwrap();
783 let mut wrapped = vec![0u8; Aes192Kwp::wrapped_len(key.len())];
784 Aes192Kwp::wrap(&kek, &key, &mut wrapped).unwrap();
785 assert_eq!(
786 hex(&wrapped),
787 "afbeb0f07dfbf5419200f2ccb50bb24f",
788 "RFC 5649 seven-byte vector, the single-block path"
789 );
790 let mut back = vec![0u8; wrapped.len() - 8];
791 let len = Aes192Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
792 assert_eq!(hex(&back[..len]), hex(&key));
793 }
794
795 #[test]
796 fn wrapping_round_trips_at_every_supported_size() {
797 let kek = [0x11u8; 32];
798 for blocks in 2..=16usize {
799 let plaintext: Vec<u8> = (0..blocks * 8).map(|i| i as u8).collect();
800 let mut wrapped = vec![0u8; plaintext.len() + OVERHEAD];
801 Aes256Kw::wrap(&kek, &plaintext, &mut wrapped).unwrap();
802 assert_ne!(&wrapped[8..], &plaintext[..], "the data must be encrypted");
803
804 let mut back = vec![0u8; plaintext.len()];
805 Aes256Kw::unwrap(&kek, &wrapped, &mut back).unwrap();
806 assert_eq!(back, plaintext, "{blocks} blocks");
807 }
808 }
809
810 #[test]
812 fn tampering_is_rejected() {
813 let kek = [0x22u8; 32];
814 let plaintext = [0x33u8; 32];
815 let mut wrapped = [0u8; 40];
816 Aes256Kw::wrap(&kek, &plaintext, &mut wrapped).unwrap();
817
818 let mut back = [0u8; 32];
819 for byte in 0..wrapped.len() {
820 let mut bad = wrapped;
821 bad[byte] ^= 1;
822 assert!(
823 Aes256Kw::unwrap(&kek, &bad, &mut back).is_err(),
824 "a flip in byte {byte} was accepted"
825 );
826 }
827 assert!(Aes256Kw::unwrap(&[0x23u8; 32], &wrapped, &mut back).is_err());
829 }
830
831 #[test]
833 fn wrapping_is_deterministic() {
834 let kek = [0x44u8; 32];
835 let plaintext = [0x55u8; 24];
836 let mut a = [0u8; 32];
837 let mut b = [0u8; 32];
838 Aes256Kw::wrap(&kek, &plaintext, &mut a).unwrap();
839 Aes256Kw::wrap(&kek, &plaintext, &mut b).unwrap();
840 assert_eq!(a, b);
841 }
842
843 #[test]
846 fn every_output_block_depends_on_every_input_block() {
847 let kek = [0x66u8; 32];
848 let base = [0u8; 64];
849 let mut reference = [0u8; 72];
850 Aes256Kw::wrap(&kek, &base, &mut reference).unwrap();
851
852 for index in [0usize, 8, 32, 63] {
853 let mut changed = base;
854 changed[index] ^= 1;
855 let mut wrapped = [0u8; 72];
856 Aes256Kw::wrap(&kek, &changed, &mut wrapped).unwrap();
857
858 let same = reference
859 .chunks(8)
860 .zip(wrapped.chunks(8))
861 .filter(|(a, b)| a == b)
862 .count();
863 assert_eq!(
864 same, 0,
865 "changing input byte {index} left {same} output blocks unchanged"
866 );
867 }
868 }
869
870 #[test]
871 fn padded_wrapping_round_trips_at_every_length() {
872 let kek = [0x77u8; 32];
873 for len in 1..=64usize {
874 let plaintext: Vec<u8> = (0..len).map(|i| (i * 7) as u8).collect();
875 let mut wrapped = vec![0u8; Aes256Kwp::wrapped_len(len)];
876 Aes256Kwp::wrap(&kek, &plaintext, &mut wrapped).unwrap();
877 assert_eq!(wrapped.len(), len.div_ceil(8) * 8 + 8);
878
879 let mut back = vec![0u8; wrapped.len() - 8];
880 let got = Aes256Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
881 assert_eq!(got, len, "recovered length at {len}");
882 assert_eq!(&back[..got], &plaintext[..], "round trip at {len}");
883 }
884 }
885
886 #[test]
889 fn the_single_block_padded_path_works() {
890 let kek = [0x88u8; 32];
891 for len in 1..=8usize {
892 let plaintext = vec![0xabu8; len];
893 let mut wrapped = vec![0u8; 16];
894 Aes256Kwp::wrap(&kek, &plaintext, &mut wrapped).unwrap();
895 assert_eq!(wrapped.len(), 16, "one block plus the header");
896
897 let mut back = [0u8; 8];
898 let got = Aes256Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
899 assert_eq!(got, len);
900 assert_eq!(&back[..got], &plaintext[..]);
901 }
902 }
903
904 #[test]
905 fn padded_wrapping_rejects_tampering() {
906 let kek = [0x99u8; 32];
907 let plaintext = b"a secret of awkward length";
908 let mut wrapped = vec![0u8; Aes256Kwp::wrapped_len(plaintext.len())];
909 Aes256Kwp::wrap(&kek, plaintext, &mut wrapped).unwrap();
910
911 let mut back = vec![0u8; wrapped.len() - 8];
912 for byte in 0..wrapped.len() {
913 let mut bad = wrapped.clone();
914 bad[byte] ^= 1;
915 assert!(
916 Aes256Kwp::unwrap(&kek, &bad, &mut back).is_err(),
917 "a flip in byte {byte} was accepted"
918 );
919 }
920 }
921
922 #[test]
923 fn lengths_are_validated() {
924 let kek = [0u8; 32];
925 let mut out = [0u8; 64];
926
927 assert!(Aes256Kw::wrap(&kek, &[0u8; 20], &mut out[..28]).is_err());
929 assert!(Aes256Kw::wrap(&kek, &[0u8; 8], &mut out[..16]).is_err());
931 assert!(Aes256Kw::wrap(&kek, &[], &mut out[..8]).is_err());
933 assert!(Aes256Kwp::wrap(&kek, &[], &mut out[..8]).is_err());
934 assert!(Aes256Kw::wrap(&[0u8; 16], &[0u8; 16], &mut out[..24]).is_err());
936 assert!(Aes256Kw::wrap(&kek, &[0u8; 16], &mut out[..23]).is_err());
938 assert!(Aes256Kw::unwrap(&kek, &[0u8; 16], &mut out[..8]).is_err());
940 }
941
942 #[test]
943 fn all_self_tests_pass() {
944 Aes128Kw::self_test().unwrap();
945 Aes256Kw::self_test().unwrap();
946 Aes192Kwp::self_test().unwrap();
947 Aes256Kwp::self_test().unwrap();
948 }
949}