From cd18544e638ddb61563b770b757a75df519f41d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 11 Nov 2024 17:50:07 +0300 Subject: [PATCH] aes-kw: add fixed size methods --- aes-kw/README.md | 12 ++- aes-kw/src/ctx.rs | 6 +- aes-kw/src/error.rs | 18 +++- aes-kw/src/kw.rs | 172 ++++++++++++++++++++++++--------- aes-kw/src/kwp.rs | 194 ++++++++++++++++++++++++++++---------- aes-kw/src/lib.rs | 15 +-- aes-kw/tests/kw_tests.rs | 34 +++++-- aes-kw/tests/kwp_tests.rs | 45 ++++++--- 8 files changed, 366 insertions(+), 130 deletions(-) diff --git a/aes-kw/README.md b/aes-kw/README.md index 842e7b6..5e54fcc 100644 --- a/aes-kw/README.md +++ b/aes-kw/README.md @@ -71,12 +71,20 @@ let wkey: [u8; 24] = hex!("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5"); let kw = KwAes128::new(&kw_key.into()); let mut buf = [0u8; 24]; -kw.wrap(&key, &mut buf).unwrap(); +kw.wrap_key(&key, &mut buf).unwrap(); assert_eq!(buf, wkey); let mut buf = [0u8; 16]; -kw.unwrap(&wkey, &mut buf).unwrap(); +kw.unwrap_key(&wkey, &mut buf).unwrap(); assert_eq!(buf, key); + +// If key size is known at compile time, you can use the fixed methods: +use aes_kw::cipher::consts::U16; + +let wrapped_key = kw.wrap_fixed_key::(&key.into()); +assert_eq!(wrapped_key, wkey); +let unwrapped_key = kw.unwrap_fixed_key::(&wrapped_key).unwrap(); +assert_eq!(unwrapped_key, key); ``` ## Minimum Supported Rust Version diff --git a/aes-kw/src/ctx.rs b/aes-kw/src/ctx.rs index cc00a9b..0356c64 100644 --- a/aes-kw/src/ctx.rs +++ b/aes-kw/src/ctx.rs @@ -1,4 +1,4 @@ -use crate::{IV_LEN, SEMIBLOCK_SIZE}; +use crate::IV_LEN; use aes::cipher::{ typenum::U16, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherEncBackend, BlockCipherEncClosure, BlockSizeUser, @@ -19,7 +19,7 @@ impl BlockCipherEncClosure for Ctx<'_> { #[inline(always)] fn call>(self, backend: &B) { for j in 0..=5 { - for (i, chunk) in self.buf.chunks_mut(SEMIBLOCK_SIZE).skip(1).enumerate() { + for (i, chunk) in self.buf.chunks_mut(IV_LEN).skip(1).enumerate() { // A | R[i] self.block[IV_LEN..].copy_from_slice(chunk); // B = AES(K, ..) @@ -43,7 +43,7 @@ impl BlockCipherDecClosure for Ctx<'_> { #[inline(always)] fn call>(self, backend: &B) { for j in (0..=5).rev() { - for (i, chunk) in self.buf.chunks_mut(SEMIBLOCK_SIZE).enumerate().rev() { + for (i, chunk) in self.buf.chunks_mut(IV_LEN).enumerate().rev() { // A ^ t let t = (self.blocks_len * j + (i + 1)) as u64; for (ai, ti) in self.block[..IV_LEN].iter_mut().zip(&t.to_be_bytes()) { diff --git a/aes-kw/src/error.rs b/aes-kw/src/error.rs index f5e2008..0a3a0b6 100644 --- a/aes-kw/src/error.rs +++ b/aes-kw/src/error.rs @@ -19,15 +19,25 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Error::InvalidDataSize => write!(f, "data must be a multiple of 64 bits for AES-KW and less than 2^32 bytes for AES-KWP"), + Error::InvalidDataSize => f.write_str("data must be a multiple of 64 bits for AES-KW and less than 2^32 bytes for AES-KWP"), Error::InvalidOutputSize { expected_len: expected } => { write!(f, "invalid output buffer size: expected {}", expected) } - Error::IntegrityCheckFailed => { - write!(f, "integrity check failed") - } + Error::IntegrityCheckFailed => f.write_str("integrity check failed"), } } } impl core::error::Error for Error {} + +/// Error that indicates integrity check failure. +#[derive(Clone, Copy, Debug)] +pub struct IntegrityCheckFailed; + +impl fmt::Display for IntegrityCheckFailed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("integrity check failed") + } +} + +impl core::error::Error for IntegrityCheckFailed {} diff --git a/aes-kw/src/kw.rs b/aes-kw/src/kw.rs index 38d7266..e1320fb 100644 --- a/aes-kw/src/kw.rs +++ b/aes-kw/src/kw.rs @@ -1,8 +1,11 @@ -use crate::{ctx::Ctx, Error, IV_LEN, SEMIBLOCK_SIZE}; +use core::ops::{Add, Rem}; + +use crate::{ctx::Ctx, error::IntegrityCheckFailed, Error, IvLen, IV_LEN}; use aes::cipher::{ + array::ArraySize, crypto_common::{InnerInit, InnerUser}, - typenum::U16, - Block, BlockCipherDecrypt, BlockCipherEncrypt, + typenum::{Mod, NonZero, Sum, Zero, U16}, + Array, Block, BlockCipherDecrypt, BlockCipherEncrypt, }; /// Default Initial Value for AES-KW as defined in RFC3394 § 2.2.3.1. @@ -23,6 +26,9 @@ use aes::cipher::{ /// ``` const IV: [u8; IV_LEN] = [0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6]; +/// Type alias representing wrapped key roughly equivalent to `[u8; N + IV_LEN]`. +pub type KwWrappedKey = Array>; + /// AES Key Wrapper (KW), as defined in [RFC 3394]. /// /// [RFC 3394]: https://www.rfc-editor.org/rfc/rfc3394.txt @@ -43,24 +49,9 @@ impl InnerInit for AesKw { } impl> AesKw { - /// Wrap `data` and write result to `buf`. - /// - /// Returns slice which points to `buf` and contains wrapped data. - /// - /// Length of `data` must be multiple of [`SEMIBLOCK_SIZE`] and bigger than zero. - /// Length of `buf` must be bigger or equal to `data.len() + IV_LEN`. - #[inline] - pub fn wrap<'a>(&self, data: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> { - let blocks_len = data.len() / SEMIBLOCK_SIZE; - let blocks_rem = data.len() % SEMIBLOCK_SIZE; - if blocks_rem != 0 { - return Err(Error::InvalidDataSize); - } - - let expected_len = data.len() + IV_LEN; - let buf = buf - .get_mut(..expected_len) - .ok_or(Error::InvalidOutputSize { expected_len })?; + /// Wrap key into `buf` assuming that it has correct length. + fn wrap_key_trusted(&self, key: &[u8], buf: &mut [u8]) { + let blocks_len = key.len() / IV_LEN; // 1) Initialize variables @@ -69,7 +60,7 @@ impl> AesKw { block[..IV_LEN].copy_from_slice(&IV); // 2) Calculate intermediate values - buf[IV_LEN..].copy_from_slice(data); + buf[IV_LEN..].copy_from_slice(key); self.cipher.encrypt_with_backend(Ctx { blocks_len, @@ -79,42 +70,79 @@ impl> AesKw { // 3) Output the results buf[..IV_LEN].copy_from_slice(&block[..IV_LEN]); - - Ok(buf) } -} -impl> AesKw { - /// Unwrap `data` and write result to `buf`. + /// Wrap `key` and write result to `buf`. /// - /// Returns slice which points to `buf` and contains unwrapped data. + /// Returns slice which points to `buf` and contains wrapped data. /// - /// Length of `data` must be multiple of [`SEMIBLOCK_SIZE`] and bigger than zero. - /// Length of `buf` must be bigger or equal to `data.len()`. + /// Length of `data` must be multiple of [`IV_LEN`] and bigger than zero. + /// Length of `buf` must be bigger or equal to `data.len() + IV_LEN`. #[inline] - pub fn unwrap<'a>(&self, data: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> { - let blocks_len = data.len() / SEMIBLOCK_SIZE; - let blocks_rem = data.len() % SEMIBLOCK_SIZE; - if blocks_rem != 0 || blocks_len < 1 { + pub fn wrap_key<'a>(&self, key: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> { + let blocks_rem = key.len() % IV_LEN; + if blocks_rem != 0 { return Err(Error::InvalidDataSize); } - // 0) Prepare inputs - - let blocks_len = blocks_len - 1; - - let expected_len = blocks_len * SEMIBLOCK_SIZE; + let expected_len = key.len() + IV_LEN; let buf = buf .get_mut(..expected_len) .ok_or(Error::InvalidOutputSize { expected_len })?; + self.wrap_key_trusted(key, buf); + + Ok(buf) + } + + /// Wrap fixed-size key `key` and return wrapped key. + /// + /// This method is roughly equivalent to: + /// ```ignore + /// const fn check_key_size(n: usize) -> usize { + /// assert!(n != 0 && n % IV_LEN == 0); + /// 0 + /// } + /// + /// pub fn wrap_fixed_key( + /// &self, + /// key: &[u8; N], + /// ) -> [u8; N + IV_LEN] + /// where + /// [(); check_key_size(N)]: Sized, + /// { ... } + /// ``` + /// but uses [`hybrid_array::Array`][Array] instead of built-in arrays + /// to work around current limitations of the const generics system. + #[inline] + pub fn wrap_fixed_key(&self, key: &Array) -> KwWrappedKey + where + N: ArraySize + NonZero + Add + Rem, + Sum: ArraySize, + Mod: Zero, + { + let mut buf = KwWrappedKey::::default(); + self.wrap_key_trusted(key, &mut buf); + buf + } +} + +impl> AesKw { + /// Unwrap key into `buf` assuming that it has correct length. + fn unwrap_key_trusted<'a>( + &self, + wkey: &[u8], + buf: &'a mut [u8], + ) -> Result<&'a [u8], IntegrityCheckFailed> { + let blocks_len = buf.len() / IV_LEN; + // 1) Initialize variables let block = &mut Block::::default(); - block[..IV_LEN].copy_from_slice(&data[..IV_LEN]); + block[..IV_LEN].copy_from_slice(&wkey[..IV_LEN]); // for i = 1 to n: R[i] = C[i] - buf.copy_from_slice(&data[IV_LEN..]); + buf.copy_from_slice(&wkey[IV_LEN..]); // 2) Calculate intermediate values @@ -132,7 +160,67 @@ impl> AesKw { Ok(buf) } else { buf.fill(0); - Err(Error::IntegrityCheckFailed) + Err(IntegrityCheckFailed) + } + } + + /// Unwrap `data` and write result to `buf`. + /// + /// Returns slice which points to `buf` and contains unwrapped data. + /// + /// Length of `data` must be multiple of [`IV_LEN`] and bigger than zero. + /// Length of `buf` must be bigger or equal to `data.len()`. + #[inline] + pub fn unwrap_key<'a>(&self, wkey: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> { + let blocks_len = wkey.len() / IV_LEN; + let blocks_rem = wkey.len() % IV_LEN; + if blocks_rem != 0 || blocks_len < 1 { + return Err(Error::InvalidDataSize); } + + let blocks_len = blocks_len - 1; + let expected_len = blocks_len * IV_LEN; + let buf = buf + .get_mut(..expected_len) + .ok_or(Error::InvalidOutputSize { expected_len })?; + + self.unwrap_key_trusted(wkey, buf) + .map_err(|_| Error::IntegrityCheckFailed)?; + + Ok(buf) + } + + /// Unwrap key in `data` and return unwrapped key. + /// + /// This method is roughly equivalent to: + /// ```ignore + /// const fn check_key_size(n: usize) -> usize { + /// assert!(n != 0 && n % IV_LEN == 0); + /// 0 + /// } + /// + /// fn unwrap_fixed_key( + /// &self, + /// data: &[u8; N + IV_LEN], + /// ) -> [u8; N] + /// where + /// [(); check_key_size(N)]: Sized, + /// { ... } + /// ``` + /// but uses [`hybrid_array::Array`][Array] instead of built-in arrays + /// to work around current limitations of the const generics system. + #[inline] + pub fn unwrap_fixed_key( + &self, + wkey: &KwWrappedKey, + ) -> Result, IntegrityCheckFailed> + where + N: ArraySize + NonZero + Add + Rem, + Sum: ArraySize, + Mod: Zero, + { + let mut buf = Array::::default(); + self.unwrap_key_trusted(wkey, &mut buf)?; + Ok(buf) } } diff --git a/aes-kw/src/kwp.rs b/aes-kw/src/kwp.rs index 1e02a48..40e158c 100644 --- a/aes-kw/src/kwp.rs +++ b/aes-kw/src/kwp.rs @@ -1,11 +1,17 @@ -use crate::{ctx::Ctx, Error, IV_LEN, SEMIBLOCK_SIZE}; +use core::ops::{Add, Div, Mul}; + +use crate::{ctx::Ctx, Error, IntegrityCheckFailed, IvLen, IV_LEN}; use aes::cipher::{ + array::ArraySize, + consts::{B1, U4294967296, U7}, crypto_common::{InnerInit, InnerUser}, - typenum::U16, - Block, BlockCipherDecrypt, BlockCipherEncrypt, + typenum::{Add1, IsLess, Le, NonZero, Prod, Quot, Sum, U16}, + Array, Block, BlockCipherDecrypt, BlockCipherEncrypt, }; -/// Maximum length of the AES-KWP input data (2^32 bytes). +/// Maximum length of the AES-KWP input data (2^32 bytes) represented as a `typenum` type. +type KwpMaxLen = U4294967296; +/// Maximum length of the AES-KWP input data (2^32 - 1 bytes). const KWP_MAX_LEN: usize = u32::MAX as usize; /// Alternative Initial Value constant prefix for AES-KWP as defined in RFC 3394 § 3. @@ -19,6 +25,13 @@ const KWP_MAX_LEN: usize = u32::MAX as usize; /// ``` const KWP_IV_PREFIX: [u8; IV_LEN / 2] = [0xA6, 0x59, 0x59, 0xA6]; +/// [`IvLen`] (`U8`) minus one +type IvLenM1 = U7; + +/// Type alias representing wrapped key roughly equivalent to +/// `[u8; IV_LEN * (N.div_ceil(IV_LEN) + 1)]`. +pub type KwpWrappedKey = Array, IvLen>>, IvLen>>; + /// AES Key Wrapper with Padding (KWP), as defined in [RFC 5649]. /// /// [RFC 5649]: https://www.rfc-editor.org/rfc/rfc5649.txt @@ -39,26 +52,9 @@ impl InnerInit for AesKwp { } impl> AesKwp { - /// AES Key Wrap with Padding, as defined in RFC 5649. - /// - /// The `out` buffer will be overwritten, and must be the smallest - /// multiple of [`SEMIBLOCK_SIZE`] (i.e. 8) which is at least [`IV_LEN`] - /// bytes (i.e. 8 bytes) longer than the length of `data`. - #[inline] - pub fn wrap<'a>(&self, data: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> { - if data.len() > KWP_MAX_LEN { - return Err(Error::InvalidDataSize); - } - - // 0) Prepare inputs - - // number of 64 bit blocks in the input data (padded) - let semiblocks_len = data.len().div_ceil(SEMIBLOCK_SIZE); - - let expected_len = semiblocks_len * SEMIBLOCK_SIZE + IV_LEN; - let buf = buf - .get_mut(..expected_len) - .ok_or(Error::InvalidOutputSize { expected_len })?; + /// Wrap key into `buf` assuming that it has correct length. + fn wrap_key_trusted(&self, key: &[u8], buf: &mut [u8]) { + let semiblocks_len = key.len().div_ceil(IV_LEN); // 2) Wrapping @@ -69,20 +65,20 @@ impl> AesKwp { let (prefix, mli) = block[..IV_LEN].split_at_mut(IV_LEN / 2); prefix.copy_from_slice(&KWP_IV_PREFIX); // 32-bit MLI equal to the number of bytes in the input data, big endian - mli.copy_from_slice(&(data.len() as u32).to_be_bytes()); + mli.copy_from_slice(&(key.len() as u32).to_be_bytes()); // If semiblocks_len is 1, the plaintext is encrypted as a single AES block if semiblocks_len == 1 { // 1) Append padding - block[IV_LEN..][..data.len()].copy_from_slice(data); + block[IV_LEN..][..key.len()].copy_from_slice(key); self.cipher .encrypt_block_b2b(block, buf.try_into().unwrap()); } else { // 1) Append padding // 2.2) Calculate intermediate values - buf[IV_LEN..][..data.len()].copy_from_slice(data); + buf[IV_LEN..][..key.len()].copy_from_slice(key); self.cipher.encrypt_with_backend(Ctx { blocks_len: semiblocks_len, @@ -93,34 +89,79 @@ impl> AesKwp { // 2.3) Output the results buf[..IV_LEN].copy_from_slice(&block[..IV_LEN]); } - - Ok(buf) } -} -impl> AesKwp { /// AES Key Wrap with Padding, as defined in RFC 5649. /// - /// The `out` buffer will be overwritten, and must be exactly [`IV_LEN`] - /// bytes (i.e. 8 bytes) shorter than the length of `data`. - /// This method returns a slice of `out`, truncated to the appropriate - /// length by removing the padding. + /// The `buf` buffer will be overwritten, and must be the smallest + /// multiple of [`IV_LEN`] (i.e. 8) which is at least [`IV_LEN`] + /// bytes (i.e. 8 bytes) longer than the length of `data`. #[inline] - pub fn unwrap<'a>(&self, data: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> { - let blocks_len = data.len() / SEMIBLOCK_SIZE; - let blocks_rem = data.len() % SEMIBLOCK_SIZE; - if blocks_rem != 0 || blocks_len < 1 || data.len() > KWP_MAX_LEN { + pub fn wrap_key<'a>(&self, key: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> { + if key.len() > KWP_MAX_LEN { return Err(Error::InvalidDataSize); } // 0) Prepare inputs - let blocks_len = blocks_len - 1; - let expected_len = blocks_len * SEMIBLOCK_SIZE; + // number of 64 bit blocks in the input data (padded) + let semiblocks_len = key.len().div_ceil(IV_LEN); + + let expected_len = semiblocks_len * IV_LEN + IV_LEN; let buf = buf .get_mut(..expected_len) .ok_or(Error::InvalidOutputSize { expected_len })?; + self.wrap_key_trusted(key, buf); + + Ok(buf) + } + + /// Wrap fixed-size key `key` and return wrapped key. + /// + /// This method is roughly equivalent to: + /// ```ignore + /// pub fn wrap_fixed_key( + /// &self, + /// key: &[u8; N], + /// ) -> [u8; IV_LEN * (N.div_ceil(IV_LEN) + 1)] + /// { ... } + /// ``` + /// but uses [`hybrid_array::Array`][Array] instead of built-in arrays + /// to work around current limitations of the const generics system. + #[inline] + pub fn wrap_fixed_key(&self, key: &Array) -> KwpWrappedKey + where + N: ArraySize + Add + IsLess, + Le: NonZero, + Sum: Div, + Quot, IvLen>: Add, + Add1, IvLen>>: Mul, + Prod, IvLen>>, IvLen>: ArraySize, + { + // 0) Prepare inputs + + // number of 64 bit blocks in the input data (padded) + + let semiblocks_len = key.len().div_ceil(IV_LEN); + let mut buf = KwpWrappedKey::::default(); + assert_eq!(semiblocks_len * IV_LEN + IV_LEN, buf.len()); + + self.wrap_key_trusted(key, &mut buf); + + buf + } +} + +impl> AesKwp { + /// Unwrap key into `buf` assuming that it has correct length. + fn unwrap_key_trusted<'a>( + &self, + wkey: &[u8], + buf: &'a mut [u8], + ) -> Result<&'a [u8], IntegrityCheckFailed> { + let blocks_len = buf.len() / IV_LEN; + // 1) Key unwrapping // 1.1) Initialize variables @@ -129,14 +170,14 @@ impl> AesKwp { // If n is 1, the plaintext is encrypted as a single AES block if blocks_len == 1 { - block.copy_from_slice(data); + block.copy_from_slice(wkey); self.cipher.decrypt_block(block); buf.copy_from_slice(&block[IV_LEN..]); } else { - block[..IV_LEN].copy_from_slice(&data[..IV_LEN]); + block[..IV_LEN].copy_from_slice(&wkey[..IV_LEN]); // for i = 1 to n: R[i] = C[i] - buf.copy_from_slice(&data[IV_LEN..]); + buf.copy_from_slice(&wkey[IV_LEN..]); // 1.2) Calculate intermediate values @@ -155,26 +196,81 @@ impl> AesKwp { let prefix_exp = u32::from_ne_bytes(KWP_IV_PREFIX); if prefix_calc != prefix_exp { buf.fill(0); - return Err(Error::IntegrityCheckFailed); + return Err(IntegrityCheckFailed); } let mli_bytes = block[IV_LEN / 2..IV_LEN].try_into().unwrap(); let mli: usize = usize::try_from(u32::from_be_bytes(mli_bytes)).map_err(|_| { buf.fill(0); - Error::IntegrityCheckFailed + IntegrityCheckFailed })?; - if mli.div_ceil(SEMIBLOCK_SIZE) != blocks_len { + if mli.div_ceil(IV_LEN) != blocks_len { buf.fill(0); - return Err(Error::IntegrityCheckFailed); + return Err(IntegrityCheckFailed); } let (res, pad) = buf.split_at_mut(mli); if !pad.iter().all(|&b| b == 0) { res.fill(0); pad.fill(0); - return Err(Error::IntegrityCheckFailed); + return Err(IntegrityCheckFailed); } Ok(res) } + + /// AES Key Wrap with Padding, as defined in RFC 5649. + /// + /// The `buf` buffer will be overwritten, and must be exactly [`IV_LEN`] + /// bytes (i.e. 8 bytes) shorter than the length of `data`. + /// This method returns a slice of `out`, truncated to the appropriate + /// length by removing the padding. + #[inline] + pub fn unwrap_key<'a>(&self, data: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> { + let blocks_len = data.len() / IV_LEN; + let blocks_rem = data.len() % IV_LEN; + if blocks_rem != 0 || blocks_len < 1 || data.len() > KWP_MAX_LEN { + return Err(Error::InvalidDataSize); + } + + let blocks_len = blocks_len - 1; + let expected_len = blocks_len * IV_LEN; + let buf = buf + .get_mut(..expected_len) + .ok_or(Error::InvalidOutputSize { expected_len })?; + + self.unwrap_key_trusted(data, buf) + .map_err(|_| Error::IntegrityCheckFailed) + } + + /// Unwrap fixed-size wrapped key `wkey` and return resulting key. + /// + /// This method is roughly equivalent to: + /// ```ignore + /// pub fn unwrap_fixed_key( + /// &self, + /// wkey: &[u8; IV_LEN * (N.div_ceil(IV_LEN) + 1)], + /// ) -> [u8; N] + /// { ... } + /// ``` + /// but uses [`hybrid_array::Array`][Array] instead of built-in arrays + /// to work around current limitations of the const generics system. + #[inline] + pub fn unwrap_fixed_key( + &self, + wkey: &KwpWrappedKey, + ) -> Result, IntegrityCheckFailed> + where + N: ArraySize + Add + IsLess, + Le: NonZero, + Sum: Div, + Quot, IvLen>: Add + Mul, + Add1, IvLen>>: Mul, + Prod, IvLen>>, IvLen>: ArraySize, + Prod, IvLen>, IvLen>: ArraySize, + { + let mut buf = Array::, IvLen>, IvLen>>::default(); + self.unwrap_key_trusted(wkey, &mut buf) + .map(|res| res.try_into().unwrap()) + } } diff --git a/aes-kw/src/lib.rs b/aes-kw/src/lib.rs index 4d69d20..bca7d92 100644 --- a/aes-kw/src/lib.rs +++ b/aes-kw/src/lib.rs @@ -16,7 +16,9 @@ mod error; mod kw; mod kwp; -pub use error::Error; +use aes::cipher::consts::U8; +use aes::cipher::typenum::Unsigned; +pub use error::{Error, IntegrityCheckFailed}; pub use kw::AesKw; pub use kwp::AesKwp; @@ -38,12 +40,11 @@ pub type KwpAes192 = AesKwp; /// AES-256 key wrapping pub type KwpAes256 = AesKwp; -/// Size of an AES "semiblock" in bytes. +/// Size of an AES-KW and AES-KWP initialization vector in bytes represented as a `typenum` type. +pub type IvLen = U8; +/// Size of an AES-KW and AES-KWP initialization vector in bytes. /// -/// From NIST SP 800-38F § 4.1: +/// This value is equal to "semiblock" size. From NIST SP 800-38F § 4.1: /// /// > semiblock: given a block cipher, a bit string whose length is half of the block size. -pub const SEMIBLOCK_SIZE: usize = 8; - -/// Size of an AES-KW and AES-KWP initialization vector in bytes. -pub const IV_LEN: usize = SEMIBLOCK_SIZE; +pub const IV_LEN: usize = IvLen::USIZE; diff --git a/aes-kw/tests/kw_tests.rs b/aes-kw/tests/kw_tests.rs index 03de73a..559cc62 100644 --- a/aes-kw/tests/kw_tests.rs +++ b/aes-kw/tests/kw_tests.rs @@ -1,17 +1,25 @@ -use aes_kw::{Error, KeyInit, KwAes128, KwAes192, KwAes256}; +use aes_kw::{ + cipher::consts::{U16, U24, U32}, + Error, KeyInit, KwAes128, KwAes192, KwAes256, +}; use hex_literal::hex; use std::assert_eq; macro_rules! test_aes_kw { - ($name:ident, $kw_ty:ty, $key:expr, $pt:expr, $ct:expr) => { + ($name:ident, $kw_ty:ty, $n:ty, $key:expr, $pt:expr, $ct:expr) => { #[test] fn $name() { let kw = <$kw_ty>::new(&$key.into()); let mut buf = [0u8; 64]; - let ct = kw.wrap(&$pt, &mut buf).unwrap(); + let ct = kw.wrap_key(&$pt, &mut buf).unwrap(); assert_eq!($ct, ct); - let pt = kw.unwrap(&$ct, &mut buf).unwrap(); + let pt = kw.unwrap_key(&$ct, &mut buf).unwrap(); assert_eq!($pt, pt); + + let ct = kw.wrap_fixed_key::<$n>((&$pt).try_into().unwrap()); + assert_eq!($ct, ct.0); + let pt = kw.unwrap_fixed_key::<$n>(&ct).unwrap(); + assert_eq!($pt, pt.0); } }; } @@ -19,6 +27,7 @@ macro_rules! test_aes_kw { test_aes_kw!( wrap_unwrap_128_key_128_kek, KwAes128, + U16, hex!("000102030405060708090A0B0C0D0E0F"), hex!("00112233445566778899AABBCCDDEEFF"), hex!("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5") @@ -26,6 +35,7 @@ test_aes_kw!( test_aes_kw!( wrap_unwrap_128_key_192_kek, KwAes192, + U16, hex!("000102030405060708090A0B0C0D0E0F1011121314151617"), hex!("00112233445566778899AABBCCDDEEFF"), hex!("96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D") @@ -33,6 +43,7 @@ test_aes_kw!( test_aes_kw!( wrap_unwrap_128_key_256_kek, KwAes256, + U16, hex!("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"), hex!("00112233445566778899AABBCCDDEEFF"), hex!("64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7") @@ -40,6 +51,7 @@ test_aes_kw!( test_aes_kw!( wrap_unwrap_192_key_192_kek, KwAes192, + U24, hex!("000102030405060708090A0B0C0D0E0F1011121314151617"), hex!("00112233445566778899AABBCCDDEEFF0001020304050607"), hex!("031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2") @@ -47,6 +59,7 @@ test_aes_kw!( test_aes_kw!( wrap_unwrap_192_key_256_kek, KwAes256, + U24, hex!("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"), hex!("00112233445566778899AABBCCDDEEFF0001020304050607"), hex!("A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1") @@ -54,6 +67,7 @@ test_aes_kw!( test_aes_kw!( wrap_unwrap_256_key_256_kek, KwAes256, + U32, hex!("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"), hex!("00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F"), hex!("28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21") @@ -68,13 +82,13 @@ fn error_invalid_data_size() { let kek = KwAes128::new(&key.into()); let mut buf = [0u8; 24]; - let res = kek.wrap(&input, &mut buf); + let res = kek.wrap_key(&input, &mut buf); assert_eq!(res, Err(Error::InvalidDataSize)); - let res = kek.unwrap(&output, &mut buf); + let res = kek.unwrap_key(&output, &mut buf); assert_eq!(res, Err(Error::InvalidDataSize)); - let res = kek.unwrap(&[], &mut buf); + let res = kek.unwrap_key(&[], &mut buf); assert_eq!(res, Err(Error::InvalidDataSize)); } @@ -87,11 +101,11 @@ fn error_invalid_output_size() { let kek = KwAes128::new(&key.into()); let mut buf = [0u8; 23]; - let res = kek.wrap(&input, &mut buf); + let res = kek.wrap_key(&input, &mut buf); assert_eq!(res, Err(Error::InvalidOutputSize { expected_len: 24 })); let mut buf = [0u8; 15]; - let res = kek.unwrap(&output, &mut buf); + let res = kek.unwrap_key(&output, &mut buf); assert_eq!(res, Err(Error::InvalidOutputSize { expected_len: 16 })); } @@ -103,7 +117,7 @@ fn error_integrity_check_failed() { let kek = KwAes128::new(&key.into()); let mut buf = [0u8; 16]; - let res = kek.unwrap(&output, &mut buf); + let res = kek.unwrap_key(&output, &mut buf); assert_eq!(res, Err(Error::IntegrityCheckFailed)); } diff --git a/aes-kw/tests/kwp_tests.rs b/aes-kw/tests/kwp_tests.rs index 52e50b6..d9532d9 100644 --- a/aes-kw/tests/kwp_tests.rs +++ b/aes-kw/tests/kwp_tests.rs @@ -1,17 +1,22 @@ -use aes_kw::{Error, KeyInit, KwpAes128, KwpAes192, KwpAes256}; +use aes_kw::{cipher::consts, Error, KeyInit, KwpAes128, KwpAes192, KwpAes256}; use hex_literal::hex; use std::assert_eq; macro_rules! test_aes_kwp { - ($name:ident, $kwp_ty:ty, $key:expr, $pt:expr, $ct:expr) => { + ($name:ident, $kwp_ty:ty, $n:ty, $key:expr, $pt:expr, $ct:expr) => { #[test] fn $name() { let kwp = <$kwp_ty>::new(&$key.into()); let mut buf = [0u8; 64]; - let ct = kwp.wrap(&$pt, &mut buf).unwrap(); + let ct = kwp.wrap_key(&$pt, &mut buf).unwrap(); assert_eq!($ct, ct); - let pt = kwp.unwrap(&$ct, &mut buf).unwrap(); + let pt = kwp.unwrap_key(&$ct, &mut buf).unwrap(); assert_eq!($pt, pt); + + let ct = kwp.wrap_fixed_key::<$n>((&$pt).try_into().unwrap()); + assert_eq!($ct, ct.0); + let pt = kwp.unwrap_fixed_key::<$n>(&ct).unwrap(); + assert_eq!($pt, pt.0); } }; } @@ -19,6 +24,7 @@ macro_rules! test_aes_kwp { test_aes_kwp!( wrap_unwrap_160_key_192_kek, KwpAes192, + consts::U20, hex!("5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8"), hex!("c37b7e6492584340bed12207808941155068f738"), hex!("138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a") @@ -26,6 +32,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_56_key_192_kek, KwpAes192, + consts::U7, hex!("5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8"), hex!("466f7250617369"), hex!("afbeb0f07dfbf5419200f2ccb50bb24f") @@ -36,6 +43,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_24_key_128_kek, KwpAes128, + consts::U3, hex!("AF83AE6624FC006DA13B3C37B8A5933B"), hex!("13126A"), hex!("A661F530339C9F344FA4755AD4CC3558") @@ -43,6 +51,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_24_key_192_kek, KwpAes192, + consts::U3, hex!("BA0CFC260103DDD629FA8826982F5547D245F5AB0711F10F"), hex!("C01990"), hex!("91E3B5E73A25EC91E91D337D0485B960") @@ -50,6 +59,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_24_key_256_kek, KwpAes256, + consts::U3, hex!("6D60C0D0941CF3750B864C6F1FA580AE074C00EDEB386F9FC299178A70FCCCD1"), hex!("6B54A0"), hex!("24255140B4A9F8A9E35B9DA2BFA0E0C3") @@ -57,6 +67,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_64_key_128_kek, KwpAes128, + consts::U8, hex!("D19C43011C2A0242A38BD58B8D76456D"), hex!("4202C90D7298CB4B"), hex!("65BEFAEAACBB4620D1A5D64E7B57A760") @@ -64,6 +75,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_64_key_192_kek, KwpAes192, + consts::U8, hex!("D65980B811B696A44AFB3DE6DDCA07910FAB2A4C898B51AF"), hex!("E63D206E6321CBCA"), hex!("7F3B9764D9B28AA7D2E4EDA430AFBA21") @@ -71,6 +83,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_64_key_256_kek, KwpAes256, + consts::U8, hex!("EB950B844B97145A594B7F91AA81844045874AAA46DB522CF91144F63A6FED37"), hex!("A4CE3F7D7C49B11A"), hex!("F5939D472407E28EE6D7269FA75DAC88") @@ -78,6 +91,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_128_key_128_kek, KwpAes128, + consts::U16, hex!("EBEE1B9211AADEFD06D258605F7134FB"), hex!("4029F7DA4F8C29E4BB951A6F9D7F5305"), hex!("634194EACA80D77A21D11DD3E739DC5AA3FECA2CE0990507") @@ -85,6 +99,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_128_key_192_kek, KwpAes192, + consts::U16, hex!("029194F464DCF06C0E7CA8F05927874A3AC4AA93262459FC"), hex!("D45E4B35D47F2F559EE2B78D71E73C23"), hex!("2519D224F9CAB21C69ED5758F41BEB4D145FC68A3387BADF") @@ -92,6 +107,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_128_key_256_kek, KwpAes256, + consts::U16, hex!("314A549913256A71C6348EAAB9B85EFC755FE736568F0DBC9F6F8BC3CA3D12EE"), hex!("3B700E9682275D8DBE61CA7C1EC900E8"), hex!("70C684C49112AD8B8C3E13B99992127B58DCB9B59CE5C3FD") @@ -99,6 +115,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_144_key_128_kek, KwpAes128, + consts::U18, hex!("83696B21D199C224415370F2C9857E67"), hex!("8D6220459626A496036389DF998B45029CE7"), hex!("C255C96564C96F0A381A8A8091389D654357AB826C9F1ACF16EA8E1DB2F820E9") @@ -106,6 +123,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_144_key_192_kek, KwpAes192, + consts::U18, hex!("2F65E32F3BC3F0F3EA7E74E86ED66162A7447E723D30E72F"), hex!("CB4BE52BAB46B64322FFFFF30D1A39D17359"), hex!("4C27BAE9E7A7814B78946A6F06902A14C51DA65344524EAA645BE30F14C400D5") @@ -113,6 +131,7 @@ test_aes_kwp!( test_aes_kwp!( wrap_unwrap_144_key_256_kek, KwpAes256, + consts::U18, hex!("F2882A99E67FD1F0E024D2E973EE55BF2AE94D6798BC3B3A7EF94BFC9197A7F6"), hex!("13CDD6837C4C40FDE0B9EC150093713771AC"), hex!("D096D3702EA4252DA0D36666D01F1F450BCD26C87814A8041F8EEFD229EC4828") @@ -132,7 +151,7 @@ fn padding_cleared() { buf[21] = 0xFF; buf[22] = 0xFF; buf[23] = 0xFF; - let res = kwp.wrap(&input, &mut buf).unwrap(); + let res = kwp.wrap_key(&input, &mut buf).unwrap(); assert_eq!(output, res); } @@ -145,9 +164,9 @@ fn error_invalid_data_size() { let kwp = KwpAes128::new(&key.into()); let mut buf = [0u8; 16]; - let res = kwp.unwrap(&output, &mut buf); + let res = kwp.unwrap_key(&output, &mut buf); assert_eq!(res, Err(Error::InvalidDataSize)); - let res = kwp.unwrap(&[], &mut buf); + let res = kwp.unwrap_key(&[], &mut buf); assert_eq!(res, Err(Error::InvalidDataSize)); } @@ -160,10 +179,10 @@ fn error_invalid_output_size() { let kwp = KwpAes128::new(&key.into()); let mut buf = [0u8; 32]; - let res = kwp.wrap(&input, &mut buf[..23]); + let res = kwp.wrap_key(&input, &mut buf[..23]); assert_eq!(res, Err(Error::InvalidOutputSize { expected_len: 24 })); - let res = kwp.unwrap(&output, &mut buf[..15]); + let res = kwp.unwrap_key(&output, &mut buf[..15]); assert_eq!(res, Err(Error::InvalidOutputSize { expected_len: 16 })); // Make sure we also test the padded case @@ -174,10 +193,10 @@ fn error_invalid_output_size() { let kek = KwpAes128::new(&key.into()); - let res = kek.wrap(&input, &mut buf[..11]); + let res = kek.wrap_key(&input, &mut buf[..11]); assert_eq!(res, Err(Error::InvalidOutputSize { expected_len: 16 })); - let res = kek.unwrap(&output, &mut buf[..3]); + let res = kek.unwrap_key(&output, &mut buf[..3]); assert_eq!(res, Err(Error::InvalidOutputSize { expected_len: 8 })); } @@ -188,7 +207,7 @@ fn error_integrity_check_failed() { let kwp = KwpAes128::new(&key.into()); let mut buf = [0u8; 16]; - let res = kwp.unwrap(&output, &mut buf); + let res = kwp.unwrap_key(&output, &mut buf); assert_eq!(res, Err(Error::IntegrityCheckFailed)); // Make sure we also test the padded case @@ -198,6 +217,6 @@ fn error_integrity_check_failed() { let kek = KwpAes128::new(&key.into()); let mut buf = [0u8; 8]; - let res = kek.unwrap(&output, &mut buf); + let res = kek.unwrap_key(&output, &mut buf); assert_eq!(res, Err(Error::IntegrityCheckFailed)); }