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#[allow(clippy::needless_range_loop)]
57fn wrap_blocks<C: BlockCipher>(
58 cipher: &C,
59 a: &mut [u8; 8],
60 r: &mut [[u8; 8]],
61 n: usize,
62) -> Result<()> {
63 let mut block = [0u8; 16];
64 for j in 0..6u64 {
65 for i in 0..n {
66 block[..8].copy_from_slice(a);
67 block[8..].copy_from_slice(&r[i]);
68 cipher.encrypt_block(&mut block)?;
69
70 let t = (n as u64) * j + (i as u64) + 1;
72 a.copy_from_slice(&block[..8]);
73 for (k, byte) in t.to_be_bytes().iter().enumerate() {
74 a[k] ^= *byte;
75 }
76 r[i].copy_from_slice(&block[8..]);
77 }
78 }
79 block.zeroize();
80 Ok(())
81}
82
83#[allow(clippy::needless_range_loop)]
85fn unwrap_blocks<C: BlockCipher>(
86 cipher: &C,
87 a: &mut [u8; 8],
88 r: &mut [[u8; 8]],
89 n: usize,
90) -> Result<()> {
91 let mut block = [0u8; 16];
92 for j in (0..6u64).rev() {
93 for i in (0..n).rev() {
94 let t = (n as u64) * j + (i as u64) + 1;
95 block[..8].copy_from_slice(a);
96 for (k, byte) in t.to_be_bytes().iter().enumerate() {
97 block[k] ^= *byte;
98 }
99 block[8..].copy_from_slice(&r[i]);
100 cipher.decrypt_block(&mut block)?;
101
102 a.copy_from_slice(&block[..8]);
103 r[i].copy_from_slice(&block[8..]);
104 }
105 }
106 block.zeroize();
107 Ok(())
108}
109
110macro_rules! key_wrap {
112 ($kw:ident, $kwp:ident, $cipher:ty, $key_len:literal, $kw_id:literal, $kwp_id:literal) => {
113 #[doc = concat!("SP 800-38F KW with AES-", stringify!($key_len), "*8.")]
114 pub struct $kw;
115
116 impl Algorithm for $kw {
117 const ID: &'static str = $kw_id;
118 const NAME: &'static str = $kw_id;
119 }
120
121 impl $kw {
122 pub const KEY_LEN: usize = $key_len;
124
125 pub fn wrap(kek: &[u8], plaintext: &[u8], out: &mut [u8]) -> Result<()> {
131 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
132 ensure!(
133 plaintext.len() % 8 == 0,
134 InvalidLength,
135 "key-wrap input must be a whole number of 64-bit blocks"
136 );
137 let n = plaintext.len() / 8;
138 ensure!(
139 n >= 2,
140 InvalidLength,
141 "key-wrap input must be at least 16 bytes"
142 );
143 ensure!(n <= MAX_BLOCKS, InvalidLength, "key-wrap input too large");
144 ensure!(
145 out.len() == plaintext.len() + OVERHEAD,
146 InvalidLength,
147 "key-wrap output"
148 );
149
150 let cipher = <$cipher>::new(kek)?;
151 let mut a = KW_IV;
152 let mut r = [[0u8; 8]; MAX_BLOCKS];
153 for i in 0..n {
154 r[i].copy_from_slice(&plaintext[i * 8..(i + 1) * 8]);
155 }
156
157 wrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
158
159 out[..8].copy_from_slice(&a);
160 for i in 0..n {
161 out[8 + i * 8..16 + i * 8].copy_from_slice(&r[i]);
162 }
163 for block in r.iter_mut() {
164 block.zeroize();
165 }
166 Ok(())
167 }
168
169 pub fn unwrap(kek: &[u8], ciphertext: &[u8], out: &mut [u8]) -> Result<()> {
174 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
175 ensure!(
176 ciphertext.len() % 8 == 0 && ciphertext.len() >= 24,
177 InvalidLength,
178 "key-wrap ciphertext"
179 );
180 let n = ciphertext.len() / 8 - 1;
181 ensure!(
182 n <= MAX_BLOCKS,
183 InvalidLength,
184 "key-wrap ciphertext too large"
185 );
186 ensure!(
187 out.len() == ciphertext.len() - OVERHEAD,
188 InvalidLength,
189 "key-wrap output"
190 );
191
192 let cipher = <$cipher>::new(kek)?;
193 let mut a = [0u8; 8];
194 a.copy_from_slice(&ciphertext[..8]);
195 let mut r = [[0u8; 8]; MAX_BLOCKS];
196 for i in 0..n {
197 r[i].copy_from_slice(&ciphertext[8 + i * 8..16 + i * 8]);
198 }
199
200 unwrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
201
202 let ok = ic_core::ct::verify(&a, &KW_IV);
205 if !ok {
206 for block in r.iter_mut() {
207 block.zeroize();
208 }
209 return Err(ic_core::err!(AuthenticationFailed, $kw_id));
210 }
211 for i in 0..n {
212 out[i * 8..(i + 1) * 8].copy_from_slice(&r[i]);
213 }
214 for block in r.iter_mut() {
215 block.zeroize();
216 }
217 Ok(())
218 }
219 }
220
221 #[doc = concat!("SP 800-38F KWP with AES-", stringify!($key_len), "*8, per RFC 5649.")]
222 pub struct $kwp;
223
224 impl Algorithm for $kwp {
225 const ID: &'static str = $kwp_id;
226 const NAME: &'static str = $kwp_id;
227 }
228
229 impl $kwp {
230 pub const KEY_LEN: usize = $key_len;
232
233 pub const fn wrapped_len(plaintext_len: usize) -> usize {
236 plaintext_len.div_ceil(8) * 8 + OVERHEAD
237 }
238
239 pub fn wrap(kek: &[u8], plaintext: &[u8], out: &mut [u8]) -> Result<()> {
245 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
246 ensure!(
247 !plaintext.is_empty(),
248 InvalidLength,
249 "key-wrap input is empty"
250 );
251 ensure!(
252 plaintext.len() <= MAX_BLOCKS * 8,
253 InvalidLength,
254 "key-wrap input too large"
255 );
256 ensure!(
257 out.len() == Self::wrapped_len(plaintext.len()),
258 InvalidLength,
259 "key-wrap output"
260 );
261
262 let cipher = <$cipher>::new(kek)?;
263 let mut a = [0u8; 8];
264 a[..4].copy_from_slice(&KWP_IV);
265 a[4..].copy_from_slice(&(plaintext.len() as u32).to_be_bytes());
266
267 let n = plaintext.len().div_ceil(8);
268 let mut r = [[0u8; 8]; MAX_BLOCKS];
269 for (i, chunk) in plaintext.chunks(8).enumerate() {
270 r[i][..chunk.len()].copy_from_slice(chunk);
271 }
272
273 if n == 1 {
274 let mut block = [0u8; 16];
277 block[..8].copy_from_slice(&a);
278 block[8..].copy_from_slice(&r[0]);
279 cipher.encrypt_block(&mut block)?;
280 out.copy_from_slice(&block);
281 block.zeroize();
282 } else {
283 wrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
284 out[..8].copy_from_slice(&a);
285 for i in 0..n {
286 out[8 + i * 8..16 + i * 8].copy_from_slice(&r[i]);
287 }
288 }
289 for block in r.iter_mut() {
290 block.zeroize();
291 }
292 Ok(())
293 }
294
295 pub fn unwrap(kek: &[u8], ciphertext: &[u8], out: &mut [u8]) -> Result<usize> {
300 ensure!(kek.len() == $key_len, InvalidLength, "key-wrap kek");
301 ensure!(
302 ciphertext.len() % 8 == 0 && ciphertext.len() >= 16,
303 InvalidLength,
304 "key-wrap ciphertext"
305 );
306 let n = ciphertext.len() / 8 - 1;
307 ensure!(
308 n <= MAX_BLOCKS,
309 InvalidLength,
310 "key-wrap ciphertext too large"
311 );
312 ensure!(out.len() >= n * 8, InvalidLength, "key-wrap output");
313
314 let cipher = <$cipher>::new(kek)?;
315 let mut a = [0u8; 8];
316 let mut r = [[0u8; 8]; MAX_BLOCKS];
317
318 if n == 1 {
319 let mut block = [0u8; 16];
320 block.copy_from_slice(ciphertext);
321 cipher.decrypt_block(&mut block)?;
322 a.copy_from_slice(&block[..8]);
323 r[0].copy_from_slice(&block[8..]);
324 block.zeroize();
325 } else {
326 a.copy_from_slice(&ciphertext[..8]);
327 for i in 0..n {
328 r[i].copy_from_slice(&ciphertext[8 + i * 8..16 + i * 8]);
329 }
330 unwrap_blocks(&cipher, &mut a, &mut r[..n], n)?;
331 }
332
333 let mut ok = ic_core::ct::eq(&a[..4], &KWP_IV);
337 let declared = u32::from_be_bytes([a[4], a[5], a[6], a[7]]) as usize;
338 let padded = n * 8;
339 let plausible = declared <= padded && padded - declared < 8 && declared > 0;
340 ok = ok.and(ic_core::ct::Choice::from_u8(u8::from(plausible)));
341
342 if plausible {
343 let mut zeros = 0u8;
345 for i in declared..padded {
346 zeros |= r[i / 8][i % 8];
347 }
348 ok = ok.and(ic_core::ct::is_zero(&[zeros]));
349 }
350
351 if !bool::from(ok) {
352 for block in r.iter_mut() {
353 block.zeroize();
354 }
355 return Err(ic_core::err!(AuthenticationFailed, $kwp_id));
356 }
357
358 for i in 0..n {
359 out[i * 8..(i + 1) * 8].copy_from_slice(&r[i]);
360 }
361 for block in r.iter_mut() {
362 block.zeroize();
363 }
364 Ok(declared)
365 }
366 }
367 };
368}
369
370key_wrap!(
371 Aes128Kw,
372 Aes128Kwp,
373 crate::Aes128,
374 16,
375 "aes-128-kw",
376 "aes-128-kwp"
377);
378key_wrap!(
379 Aes192Kw,
380 Aes192Kwp,
381 crate::Aes192,
382 24,
383 "aes-192-kw",
384 "aes-192-kwp"
385);
386key_wrap!(
387 Aes256Kw,
388 Aes256Kwp,
389 crate::Aes256,
390 32,
391 "aes-256-kw",
392 "aes-256-kwp"
393);
394
395impl SelfTest for Aes128Kw {
396 fn self_test() -> Result<()> {
399 let mut kek = [0u8; 16];
400 ic_core::codec::hex_decode(b"000102030405060708090a0b0c0d0e0f", &mut kek)?;
401 let mut key = [0u8; 16];
402 ic_core::codec::hex_decode(b"00112233445566778899aabbccddeeff", &mut key)?;
403 let mut want = [0u8; 24];
404 ic_core::codec::hex_decode(
405 b"1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5",
406 &mut want,
407 )?;
408
409 let mut got = [0u8; 24];
410 Aes128Kw::wrap(&kek, &key, &mut got)?;
411 ensure!(
412 ic_core::ct::verify(&want, &got),
413 SelfTestFailed,
414 "aes-128-kw"
415 );
416
417 let mut back = [0u8; 16];
418 Aes128Kw::unwrap(&kek, &want, &mut back)?;
419 ensure!(
420 ic_core::ct::verify(&key, &back),
421 SelfTestFailed,
422 "aes-128-kw"
423 );
424
425 let mut tampered = want;
426 tampered[0] ^= 1;
427 ensure!(
428 Aes128Kw::unwrap(&kek, &tampered, &mut back).is_err(),
429 SelfTestFailed,
430 "aes-128-kw"
431 );
432 Ok(())
433 }
434}
435
436impl SelfTest for Aes256Kw {
437 fn self_test() -> Result<()> {
439 let mut kek = [0u8; 32];
440 ic_core::codec::hex_decode(
441 b"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
442 &mut kek,
443 )?;
444 let mut key = [0u8; 32];
445 ic_core::codec::hex_decode(
446 b"00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f",
447 &mut key,
448 )?;
449 let mut want = [0u8; 40];
450 ic_core::codec::hex_decode(
451 b"28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21",
452 &mut want,
453 )?;
454
455 let mut got = [0u8; 40];
456 Aes256Kw::wrap(&kek, &key, &mut got)?;
457 ensure!(
458 ic_core::ct::verify(&want, &got),
459 SelfTestFailed,
460 "aes-256-kw"
461 );
462
463 let mut back = [0u8; 32];
464 Aes256Kw::unwrap(&kek, &want, &mut back)?;
465 ensure!(
466 ic_core::ct::verify(&key, &back),
467 SelfTestFailed,
468 "aes-256-kw"
469 );
470 Ok(())
471 }
472}
473
474impl SelfTest for Aes192Kwp {
475 fn self_test() -> Result<()> {
478 let mut kek = [0u8; 24];
479 ic_core::codec::hex_decode(
480 b"5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8",
481 &mut kek,
482 )?;
483 let mut key = [0u8; 20];
484 ic_core::codec::hex_decode(b"c37b7e6492584340bed12207808941155068f738", &mut key)?;
485 let mut want = [0u8; 32];
486 ic_core::codec::hex_decode(
487 b"138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a",
488 &mut want,
489 )?;
490
491 let mut got = [0u8; 32];
492 Aes192Kwp::wrap(&kek, &key, &mut got)?;
493 ensure!(
494 ic_core::ct::verify(&want, &got),
495 SelfTestFailed,
496 "aes-192-kwp"
497 );
498
499 let mut back = [0u8; 24];
500 let len = Aes192Kwp::unwrap(&kek, &want, &mut back)?;
501 ensure!(len == key.len(), SelfTestFailed, "aes-192-kwp");
502 ensure!(
503 ic_core::ct::verify(&key, &back[..len]),
504 SelfTestFailed,
505 "aes-192-kwp"
506 );
507
508 let mut tampered = want;
509 tampered[3] ^= 1;
510 ensure!(
511 Aes192Kwp::unwrap(&kek, &tampered, &mut back).is_err(),
512 SelfTestFailed,
513 "aes-192-kwp"
514 );
515 Ok(())
516 }
517}
518
519impl SelfTest for Aes256Kwp {
520 fn self_test() -> Result<()> {
525 let kek = [0x5au8; 32];
526 let secret = b"nineteen bytes here";
527 let mut wrapped = [0u8; 32];
528 Aes256Kwp::wrap(&kek, secret, &mut wrapped)?;
529
530 let mut out = [0u8; 24];
531 let len = Aes256Kwp::unwrap(&kek, &wrapped, &mut out)?;
532 ensure!(len == secret.len(), SelfTestFailed, "aes-256-kwp");
533 ensure!(
534 ic_core::ct::verify(secret, &out[..len]),
535 SelfTestFailed,
536 "aes-256-kwp"
537 );
538
539 let mut tampered = wrapped;
540 tampered[3] ^= 1;
541 ensure!(
542 Aes256Kwp::unwrap(&kek, &tampered, &mut out).is_err(),
543 SelfTestFailed,
544 "aes-256-kwp"
545 );
546 Ok(())
547 }
548}
549
550#[cfg(test)]
551mod tests {
552 use super::*;
553 use ic_core::codec::{hex, unhex};
554
555 #[test]
562 fn rfc_3394_vectors() {
563 struct Case {
564 kek: &'static str,
565 key: &'static str,
566 wrapped: &'static str,
567 }
568 let cases = [
569 Case {
571 kek: "000102030405060708090a0b0c0d0e0f",
572 key: "00112233445566778899aabbccddeeff",
573 wrapped: "1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5",
574 },
575 Case {
577 kek: "000102030405060708090a0b0c0d0e0f1011121314151617",
578 key: "00112233445566778899aabbccddeeff",
579 wrapped: "96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d",
580 },
581 Case {
583 kek: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
584 key: "00112233445566778899aabbccddeeff",
585 wrapped: "64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7",
586 },
587 Case {
589 kek: "000102030405060708090a0b0c0d0e0f1011121314151617",
590 key: "00112233445566778899aabbccddeeff0001020304050607",
591 wrapped: "031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2",
592 },
593 Case {
595 kek: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
596 key: "00112233445566778899aabbccddeeff0001020304050607",
597 wrapped: "a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1",
598 },
599 Case {
601 kek: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
602 key: "00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f",
603 wrapped: "28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21",
604 },
605 ];
606
607 for (index, case) in cases.iter().enumerate() {
608 let kek = unhex(case.kek).unwrap();
609 let key = unhex(case.key).unwrap();
610 let want = unhex(case.wrapped).unwrap();
611
612 let mut got = vec![0u8; key.len() + OVERHEAD];
613 match kek.len() {
614 16 => Aes128Kw::wrap(&kek, &key, &mut got).unwrap(),
615 24 => Aes192Kw::wrap(&kek, &key, &mut got).unwrap(),
616 _ => Aes256Kw::wrap(&kek, &key, &mut got).unwrap(),
617 }
618 assert_eq!(hex(&got), case.wrapped, "RFC 3394 case 4.{}", index + 1);
619
620 let mut back = vec![0u8; key.len()];
621 match kek.len() {
622 16 => Aes128Kw::unwrap(&kek, &want, &mut back).unwrap(),
623 24 => Aes192Kw::unwrap(&kek, &want, &mut back).unwrap(),
624 _ => Aes256Kw::unwrap(&kek, &want, &mut back).unwrap(),
625 }
626 assert_eq!(hex(&back), case.key, "RFC 3394 unwrap 4.{}", index + 1);
627 }
628 }
629
630 #[test]
636 fn rfc_5649_vectors() {
637 let kek = unhex("5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8").unwrap();
638
639 let key = unhex("c37b7e6492584340bed12207808941155068f738").unwrap();
640 let mut wrapped = vec![0u8; Aes192Kwp::wrapped_len(key.len())];
641 Aes192Kwp::wrap(&kek, &key, &mut wrapped).unwrap();
642 assert_eq!(
643 hex(&wrapped),
644 "138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a",
645 "RFC 5649 twenty-byte vector"
646 );
647 let mut back = vec![0u8; wrapped.len() - 8];
648 let len = Aes192Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
649 assert_eq!(hex(&back[..len]), hex(&key));
650
651 let key = unhex("466f7250617369").unwrap();
652 let mut wrapped = vec![0u8; Aes192Kwp::wrapped_len(key.len())];
653 Aes192Kwp::wrap(&kek, &key, &mut wrapped).unwrap();
654 assert_eq!(
655 hex(&wrapped),
656 "afbeb0f07dfbf5419200f2ccb50bb24f",
657 "RFC 5649 seven-byte vector, the single-block path"
658 );
659 let mut back = vec![0u8; wrapped.len() - 8];
660 let len = Aes192Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
661 assert_eq!(hex(&back[..len]), hex(&key));
662 }
663
664 #[test]
665 fn wrapping_round_trips_at_every_supported_size() {
666 let kek = [0x11u8; 32];
667 for blocks in 2..=16usize {
668 let plaintext: Vec<u8> = (0..blocks * 8).map(|i| i as u8).collect();
669 let mut wrapped = vec![0u8; plaintext.len() + OVERHEAD];
670 Aes256Kw::wrap(&kek, &plaintext, &mut wrapped).unwrap();
671 assert_ne!(&wrapped[8..], &plaintext[..], "the data must be encrypted");
672
673 let mut back = vec![0u8; plaintext.len()];
674 Aes256Kw::unwrap(&kek, &wrapped, &mut back).unwrap();
675 assert_eq!(back, plaintext, "{blocks} blocks");
676 }
677 }
678
679 #[test]
681 fn tampering_is_rejected() {
682 let kek = [0x22u8; 32];
683 let plaintext = [0x33u8; 32];
684 let mut wrapped = [0u8; 40];
685 Aes256Kw::wrap(&kek, &plaintext, &mut wrapped).unwrap();
686
687 let mut back = [0u8; 32];
688 for byte in 0..wrapped.len() {
689 let mut bad = wrapped;
690 bad[byte] ^= 1;
691 assert!(
692 Aes256Kw::unwrap(&kek, &bad, &mut back).is_err(),
693 "a flip in byte {byte} was accepted"
694 );
695 }
696 assert!(Aes256Kw::unwrap(&[0x23u8; 32], &wrapped, &mut back).is_err());
698 }
699
700 #[test]
702 fn wrapping_is_deterministic() {
703 let kek = [0x44u8; 32];
704 let plaintext = [0x55u8; 24];
705 let mut a = [0u8; 32];
706 let mut b = [0u8; 32];
707 Aes256Kw::wrap(&kek, &plaintext, &mut a).unwrap();
708 Aes256Kw::wrap(&kek, &plaintext, &mut b).unwrap();
709 assert_eq!(a, b);
710 }
711
712 #[test]
715 fn every_output_block_depends_on_every_input_block() {
716 let kek = [0x66u8; 32];
717 let base = [0u8; 64];
718 let mut reference = [0u8; 72];
719 Aes256Kw::wrap(&kek, &base, &mut reference).unwrap();
720
721 for index in [0usize, 8, 32, 63] {
722 let mut changed = base;
723 changed[index] ^= 1;
724 let mut wrapped = [0u8; 72];
725 Aes256Kw::wrap(&kek, &changed, &mut wrapped).unwrap();
726
727 let same = reference
728 .chunks(8)
729 .zip(wrapped.chunks(8))
730 .filter(|(a, b)| a == b)
731 .count();
732 assert_eq!(
733 same, 0,
734 "changing input byte {index} left {same} output blocks unchanged"
735 );
736 }
737 }
738
739 #[test]
740 fn padded_wrapping_round_trips_at_every_length() {
741 let kek = [0x77u8; 32];
742 for len in 1..=64usize {
743 let plaintext: Vec<u8> = (0..len).map(|i| (i * 7) as u8).collect();
744 let mut wrapped = vec![0u8; Aes256Kwp::wrapped_len(len)];
745 Aes256Kwp::wrap(&kek, &plaintext, &mut wrapped).unwrap();
746 assert_eq!(wrapped.len(), len.div_ceil(8) * 8 + 8);
747
748 let mut back = vec![0u8; wrapped.len() - 8];
749 let got = Aes256Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
750 assert_eq!(got, len, "recovered length at {len}");
751 assert_eq!(&back[..got], &plaintext[..], "round trip at {len}");
752 }
753 }
754
755 #[test]
758 fn the_single_block_padded_path_works() {
759 let kek = [0x88u8; 32];
760 for len in 1..=8usize {
761 let plaintext = vec![0xabu8; len];
762 let mut wrapped = vec![0u8; 16];
763 Aes256Kwp::wrap(&kek, &plaintext, &mut wrapped).unwrap();
764 assert_eq!(wrapped.len(), 16, "one block plus the header");
765
766 let mut back = [0u8; 8];
767 let got = Aes256Kwp::unwrap(&kek, &wrapped, &mut back).unwrap();
768 assert_eq!(got, len);
769 assert_eq!(&back[..got], &plaintext[..]);
770 }
771 }
772
773 #[test]
774 fn padded_wrapping_rejects_tampering() {
775 let kek = [0x99u8; 32];
776 let plaintext = b"a secret of awkward length";
777 let mut wrapped = vec![0u8; Aes256Kwp::wrapped_len(plaintext.len())];
778 Aes256Kwp::wrap(&kek, plaintext, &mut wrapped).unwrap();
779
780 let mut back = vec![0u8; wrapped.len() - 8];
781 for byte in 0..wrapped.len() {
782 let mut bad = wrapped.clone();
783 bad[byte] ^= 1;
784 assert!(
785 Aes256Kwp::unwrap(&kek, &bad, &mut back).is_err(),
786 "a flip in byte {byte} was accepted"
787 );
788 }
789 }
790
791 #[test]
792 fn lengths_are_validated() {
793 let kek = [0u8; 32];
794 let mut out = [0u8; 64];
795
796 assert!(Aes256Kw::wrap(&kek, &[0u8; 20], &mut out[..28]).is_err());
798 assert!(Aes256Kw::wrap(&kek, &[0u8; 8], &mut out[..16]).is_err());
800 assert!(Aes256Kw::wrap(&kek, &[], &mut out[..8]).is_err());
802 assert!(Aes256Kwp::wrap(&kek, &[], &mut out[..8]).is_err());
803 assert!(Aes256Kw::wrap(&[0u8; 16], &[0u8; 16], &mut out[..24]).is_err());
805 assert!(Aes256Kw::wrap(&kek, &[0u8; 16], &mut out[..23]).is_err());
807 assert!(Aes256Kw::unwrap(&kek, &[0u8; 16], &mut out[..8]).is_err());
809 }
810
811 #[test]
812 fn all_self_tests_pass() {
813 Aes128Kw::self_test().unwrap();
814 Aes256Kw::self_test().unwrap();
815 Aes192Kwp::self_test().unwrap();
816 Aes256Kwp::self_test().unwrap();
817 }
818}