#![forbid(unsafe_code)]
use bergshamra_core::{algorithm, Error};
use kryptering::algorithm::{AesKeySize, KeyWrapAlgorithm as KKeyWrapAlgorithm};
pub trait KeyWrapAlgorithm: Send {
fn uri(&self) -> &'static str;
fn wrap(&self, kek: &[u8], key_data: &[u8]) -> Result<Vec<u8>, Error>;
fn unwrap(&self, kek: &[u8], wrapped: &[u8]) -> Result<Vec<u8>, Error>;
fn kek_size(&self) -> usize;
}
fn uri_to_keywrap(uri: &str) -> Result<(KKeyWrapAlgorithm, &'static str), Error> {
match uri {
algorithm::KW_AES128 => Ok((
KKeyWrapAlgorithm::AesKw(AesKeySize::Aes128),
algorithm::KW_AES128,
)),
algorithm::KW_AES192 => Ok((
KKeyWrapAlgorithm::AesKw(AesKeySize::Aes192),
algorithm::KW_AES192,
)),
algorithm::KW_AES256 => Ok((
KKeyWrapAlgorithm::AesKw(AesKeySize::Aes256),
algorithm::KW_AES256,
)),
algorithm::KW_TRIPLEDES => Ok((KKeyWrapAlgorithm::TripleDesKw, algorithm::KW_TRIPLEDES)),
_ => Err(Error::UnsupportedAlgorithm(format!("key wrap: {uri}"))),
}
}
pub fn from_uri(uri: &str) -> Result<Box<dyn KeyWrapAlgorithm>, Error> {
let (algo, static_uri) = uri_to_keywrap(uri)?;
Ok(Box::new(KrypteringKeyWrap {
algo,
uri: static_uri,
}))
}
struct KrypteringKeyWrap {
algo: KKeyWrapAlgorithm,
uri: &'static str,
}
impl KeyWrapAlgorithm for KrypteringKeyWrap {
fn uri(&self) -> &'static str {
self.uri
}
fn kek_size(&self) -> usize {
self.algo.kek_size()
}
fn wrap(&self, kek: &[u8], key_data: &[u8]) -> Result<Vec<u8>, Error> {
kryptering::keywrap::wrap(self.algo, kek, key_data).map_err(crate::map_kryptering_err)
}
fn unwrap(&self, kek: &[u8], wrapped: &[u8]) -> Result<Vec<u8>, Error> {
kryptering::keywrap::unwrap(self.algo, kek, wrapped).map_err(crate::map_kryptering_err)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tdes_key_wrap_roundtrip() {
let kek = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18";
let key_data = b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8";
let kw = from_uri(algorithm::KW_TRIPLEDES).unwrap();
let wrapped = kw.wrap(kek, key_data).expect("wrap");
let unwrapped = kw.unwrap(kek, &wrapped).expect("unwrap");
assert_eq!(unwrapped, key_data);
}
fn nist_aes_kw_vector(kek: &[u8], plaintext: &[u8], expected_ct: &[u8]) {
let kw = from_uri(match kek.len() {
16 => algorithm::KW_AES128,
24 => algorithm::KW_AES192,
32 => algorithm::KW_AES256,
_ => panic!("unexpected KEK size"),
})
.unwrap();
let wrapped = kw.wrap(kek, plaintext).expect("wrap failed");
assert_eq!(wrapped, expected_ct, "wrap ciphertext mismatch");
let unwrapped = kw.unwrap(kek, expected_ct).expect("unwrap failed");
assert_eq!(unwrapped, plaintext, "unwrap plaintext mismatch");
}
#[test]
fn test_nist_aes128_kw_128bit_data() {
let kek = hex::decode("000102030405060708090A0B0C0D0E0F").unwrap();
let pt = hex::decode("00112233445566778899AABBCCDDEEFF").unwrap();
let ct = hex::decode("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5").unwrap();
nist_aes_kw_vector(&kek, &pt, &ct);
}
#[test]
fn test_nist_aes192_kw_128bit_data() {
let kek = hex::decode("000102030405060708090A0B0C0D0E0F1011121314151617").unwrap();
let pt = hex::decode("00112233445566778899AABBCCDDEEFF").unwrap();
let ct = hex::decode("96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D").unwrap();
nist_aes_kw_vector(&kek, &pt, &ct);
}
#[test]
fn test_nist_aes256_kw_128bit_data() {
let kek = hex::decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")
.unwrap();
let pt = hex::decode("00112233445566778899AABBCCDDEEFF").unwrap();
let ct = hex::decode("64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7").unwrap();
nist_aes_kw_vector(&kek, &pt, &ct);
}
#[test]
fn test_nist_aes192_kw_192bit_data() {
let kek = hex::decode("000102030405060708090A0B0C0D0E0F1011121314151617").unwrap();
let pt = hex::decode("00112233445566778899AABBCCDDEEFF0001020304050607").unwrap();
let ct = hex::decode("031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2")
.unwrap();
nist_aes_kw_vector(&kek, &pt, &ct);
}
#[test]
fn test_nist_aes256_kw_256bit_data() {
let kek = hex::decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")
.unwrap();
let pt = hex::decode("00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F")
.unwrap();
let ct = hex::decode(
"28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21",
)
.unwrap();
nist_aes_kw_vector(&kek, &pt, &ct);
}
#[test]
fn test_aes_kw_wrong_kek_size() {
let kw = from_uri(algorithm::KW_AES128).unwrap();
let result = kw.wrap(&[0u8; 15], &[0u8; 16]);
assert!(result.is_err());
}
#[test]
fn test_aes_kw_integrity_check_failure() {
let kek = hex::decode("000102030405060708090A0B0C0D0E0F").unwrap();
let pt = hex::decode("00112233445566778899AABBCCDDEEFF").unwrap();
let kw = from_uri(algorithm::KW_AES128).unwrap();
let mut wrapped = kw.wrap(&kek, &pt).unwrap();
wrapped[0] ^= 0xFF;
let result = kw.unwrap(&kek, &wrapped);
assert!(
result.is_err(),
"unwrap should fail for corrupted ciphertext"
);
}
#[test]
fn test_aes_kw_roundtrip_all_sizes() {
let kek_sizes: &[(usize, &str)] = &[
(16, algorithm::KW_AES128),
(24, algorithm::KW_AES192),
(32, algorithm::KW_AES256),
];
let data_sizes = [16, 24, 32, 40, 48, 64, 128];
for &(kek_size, uri) in kek_sizes {
let kw = from_uri(uri).unwrap();
for &data_size in &data_sizes {
let kek: Vec<u8> = (0..kek_size).map(|i| (i * 7 + 3) as u8).collect();
let data: Vec<u8> = (0..data_size).map(|i| (i * 13 + 5) as u8).collect();
let wrapped = kw.wrap(&kek, &data).unwrap();
assert_eq!(
wrapped.len(),
data.len() + 8,
"ciphertext should be 8 bytes longer"
);
let unwrapped = kw.unwrap(&kek, &wrapped).unwrap();
assert_eq!(
unwrapped, data,
"roundtrip failed for kek={kek_size}, data={data_size}"
);
}
}
}
}