use aes_kw::{InnerInit, KeyInit, KwAes128, KwAes256};
use oxicrypto_core::CryptoError;
const MIN_DATA_LEN: usize = 16;
pub fn aes128_key_wrap(kek: &[u8], data: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
validate_data_len(data)?;
let cipher = aes_kw::aes::Aes128::new_from_slice(kek).map_err(|_| CryptoError::InvalidKey)?;
let kw = KwAes128::inner_init(cipher);
kw.wrap_key(data, out).map_err(map_error)?;
Ok(())
}
pub fn aes128_key_unwrap(kek: &[u8], wrapped: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
validate_wrapped_len(wrapped)?;
let cipher = aes_kw::aes::Aes128::new_from_slice(kek).map_err(|_| CryptoError::InvalidKey)?;
let kw = KwAes128::inner_init(cipher);
kw.unwrap_key(wrapped, out).map_err(map_error)?;
Ok(())
}
pub fn aes256_key_wrap(kek: &[u8], data: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
validate_data_len(data)?;
let cipher = aes_kw::aes::Aes256::new_from_slice(kek).map_err(|_| CryptoError::InvalidKey)?;
let kw = KwAes256::inner_init(cipher);
kw.wrap_key(data, out).map_err(map_error)?;
Ok(())
}
pub fn aes256_key_unwrap(kek: &[u8], wrapped: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
validate_wrapped_len(wrapped)?;
let cipher = aes_kw::aes::Aes256::new_from_slice(kek).map_err(|_| CryptoError::InvalidKey)?;
let kw = KwAes256::inner_init(cipher);
kw.unwrap_key(wrapped, out).map_err(map_error)?;
Ok(())
}
fn validate_data_len(data: &[u8]) -> Result<(), CryptoError> {
if data.len() < MIN_DATA_LEN || !data.len().is_multiple_of(8) {
return Err(CryptoError::BadInput);
}
Ok(())
}
fn validate_wrapped_len(wrapped: &[u8]) -> Result<(), CryptoError> {
if wrapped.len() < 24 || !wrapped.len().is_multiple_of(8) {
return Err(CryptoError::BadInput);
}
Ok(())
}
fn map_error(e: aes_kw::Error) -> CryptoError {
match e {
aes_kw::Error::InvalidDataSize => CryptoError::BadInput,
aes_kw::Error::InvalidOutputSize { .. } => CryptoError::BufferTooSmall,
aes_kw::Error::IntegrityCheckFailed => CryptoError::InvalidTag,
}
}
#[cfg(test)]
mod tests {
use super::*;
const KEK_128: [u8; 16] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F,
];
const KEY_128: [u8; 16] = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE,
0xFF,
];
const WRAPPED_128: [u8; 24] = [
0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B,
0x82, 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5,
];
const KEK_256: [u8; 32] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
0x1E, 0x1F,
];
const KEY_256: [u8; 32] = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE,
0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F,
];
const WRAPPED_256: [u8; 40] = [
0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4, 0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8,
0x26, 0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26, 0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99,
0xF4, 0x3B, 0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21,
];
#[test]
fn test_aes128_key_wrap_rfc3394_vec1() {
let mut out = [0u8; 24];
aes128_key_wrap(&KEK_128, &KEY_128, &mut out).expect("aes128_key_wrap failed");
assert_eq!(
out, WRAPPED_128,
"AES-128-KW wrap mismatch against RFC 3394 §4.1"
);
}
#[test]
fn test_aes128_key_unwrap_rfc3394_vec1() {
let mut out = [0u8; 16];
aes128_key_unwrap(&KEK_128, &WRAPPED_128, &mut out).expect("aes128_key_unwrap failed");
assert_eq!(
out, KEY_128,
"AES-128-KW unwrap mismatch against RFC 3394 §4.1"
);
}
#[test]
fn test_aes256_key_wrap_rfc3394() {
let mut out = [0u8; 40];
aes256_key_wrap(&KEK_256, &KEY_256, &mut out).expect("aes256_key_wrap failed");
assert_eq!(
out, WRAPPED_256,
"AES-256-KW wrap mismatch against RFC 3394 §4.6"
);
}
#[test]
fn test_aes256_key_unwrap_rfc3394() {
let mut out = [0u8; 32];
aes256_key_unwrap(&KEK_256, &WRAPPED_256, &mut out).expect("aes256_key_unwrap failed");
assert_eq!(
out, KEY_256,
"AES-256-KW unwrap mismatch against RFC 3394 §4.6"
);
}
#[test]
fn test_unwrap_tampered_fails() {
let mut tampered = WRAPPED_128;
tampered[0] ^= 0xFF; let mut out = [0u8; 16];
let result = aes128_key_unwrap(&KEK_128, &tampered, &mut out);
assert_eq!(
result,
Err(CryptoError::InvalidTag),
"tampered data must fail integrity check"
);
}
#[test]
fn test_invalid_data_length_rejected() {
let short_data = [0u8; 15];
let mut out = [0u8; 23];
let result = aes128_key_wrap(&KEK_128, &short_data, &mut out);
assert_eq!(
result,
Err(CryptoError::BadInput),
"data < 16 bytes must be rejected"
);
}
#[test]
fn test_data_not_multiple_of_8_rejected() {
let odd_data = [0u8; 17];
let mut out = [0u8; 25];
let result = aes128_key_wrap(&KEK_128, &odd_data, &mut out);
assert_eq!(
result,
Err(CryptoError::BadInput),
"data length not multiple of 8 must be rejected"
);
}
#[test]
fn test_wrong_kek_length_rejected() {
let mut out = [0u8; 24];
let result = aes128_key_wrap(&[0u8; 12], &KEY_128, &mut out);
assert_eq!(
result,
Err(CryptoError::InvalidKey),
"wrong KEK length must be rejected"
);
}
#[test]
fn test_output_buffer_too_small() {
let mut out = [0u8; 20]; let result = aes128_key_wrap(&KEK_128, &KEY_128, &mut out);
assert_eq!(
result,
Err(CryptoError::BufferTooSmall),
"short output buffer must fail"
);
}
#[test]
fn test_aes128_round_trip() {
let key = [0xABu8; 32]; let mut wrapped = [0u8; 40];
aes128_key_wrap(&KEK_128, &key, &mut wrapped).expect("wrap failed");
let mut recovered = [0u8; 32];
aes128_key_unwrap(&KEK_128, &wrapped, &mut recovered).expect("unwrap failed");
assert_eq!(recovered, key, "round-trip must recover original key");
}
#[test]
fn test_aes256_round_trip() {
let key = [0xCDu8; 16];
let mut wrapped = [0u8; 24];
aes256_key_wrap(&KEK_256, &key, &mut wrapped).expect("wrap failed");
let mut recovered = [0u8; 16];
aes256_key_unwrap(&KEK_256, &wrapped, &mut recovered).expect("unwrap failed");
assert_eq!(recovered, key, "round-trip must recover original key");
}
}