#[cfg(any(target_arch = "wasm32", feature = "_test-rust-crypto-backend"))]
mod rust_crypto;
#[cfg(any(target_arch = "wasm32", feature = "_test-rust-crypto-backend"))]
pub(crate) use rust_crypto::CipherKey;
#[cfg(not(any(target_arch = "wasm32", feature = "_test-rust-crypto-backend")))]
mod aws_lc;
#[cfg(not(any(target_arch = "wasm32", feature = "_test-rust-crypto-backend")))]
pub(crate) use aws_lc::CipherKey;
pub(crate) const NONCE_LEN: usize = 12;
#[cfg(any(target_arch = "wasm32", feature = "_test-rust-crypto-backend"))]
pub(crate) const TAG_LEN: usize = 16;
#[cfg(test)]
mod tests {
use super::CipherKey;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test;
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn cross_backend_byte_parity() {
let key: [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,
];
let nonce: [u8; 12] = [
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab,
];
let plaintext = b"vitaminc cross-backend parity vector";
let aad = b"vitaminc-encrypt KAT v1";
const EXPECTED: [u8; 52] = [
0x90, 0x71, 0x08, 0x4c, 0x28, 0xa2, 0x6c, 0xdc, 0x42, 0x06, 0xf5, 0xbc, 0x74, 0x09,
0xed, 0xbc, 0x11, 0xcf, 0x32, 0x75, 0xfc, 0xd3, 0x62, 0x1c, 0xfd, 0x7c, 0x4f, 0xf2,
0x06, 0x8b, 0x03, 0x64, 0xb1, 0x02, 0x28, 0x8d, 0xed, 0x9a, 0xa3, 0xcf, 0x46, 0x3d,
0xa6, 0xb2, 0x0e, 0x7f, 0x86, 0x23, 0x76, 0x7a, 0xfb, 0x0a,
];
let cipher = CipherKey::new(&key).expect("key construction failed");
let mut sealed = plaintext.to_vec();
cipher.seal(&nonce, aad, &mut sealed).expect("seal failed");
assert_eq!(
sealed,
EXPECTED.as_slice(),
"ciphertext+tag diverged from cross-backend KAT"
);
let mut to_open = sealed.clone();
let pt_len = cipher.open(&nonce, aad, &mut to_open).expect("open failed");
assert_eq!(
&to_open[..pt_len],
plaintext,
"round-trip plaintext mismatch"
);
}
fn fixed_key() -> [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,
]
}
fn fixed_nonce() -> [u8; 12] {
[
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab,
]
}
fn roundtrip(plaintext: &[u8], aad: &[u8]) {
let key = fixed_key();
let nonce = fixed_nonce();
let cipher = CipherKey::new(&key).expect("key");
let mut buf = plaintext.to_vec();
cipher.seal(&nonce, aad, &mut buf).expect("seal");
let pt_len = cipher.open(&nonce, aad, &mut buf).expect("open");
assert_eq!(&buf[..pt_len], plaintext);
}
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn roundtrip_no_aad() {
roundtrip(b"hello world", b"");
}
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn roundtrip_with_aad() {
roundtrip(b"hello world", b"associated data");
}
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn roundtrip_empty_plaintext() {
roundtrip(b"", b"some aad");
}
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn roundtrip_multi_block_plaintext() {
let pt: Vec<u8> = (0..200u8).collect();
roundtrip(&pt, b"multi-block aad");
}
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn open_fails_with_wrong_aad() {
let key = fixed_key();
let nonce = fixed_nonce();
let cipher = CipherKey::new(&key).expect("key");
let mut buf = b"payload".to_vec();
cipher.seal(&nonce, b"correct-aad", &mut buf).expect("seal");
let mut tampered = buf.clone();
assert!(
cipher.open(&nonce, b"wrong-aad", &mut tampered).is_err(),
"open must reject mismatched AAD"
);
}
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn open_fails_with_wrong_key() {
let nonce = fixed_nonce();
let key_a = fixed_key();
let mut key_b = fixed_key();
key_b[0] ^= 0xff;
let cipher_a = CipherKey::new(&key_a).expect("key a");
let cipher_b = CipherKey::new(&key_b).expect("key b");
let mut buf = b"payload".to_vec();
cipher_a.seal(&nonce, b"", &mut buf).expect("seal");
assert!(
cipher_b.open(&nonce, b"", &mut buf).is_err(),
"open must reject ciphertext sealed under a different key"
);
}
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn open_fails_with_tampered_ciphertext() {
let key = fixed_key();
let nonce = fixed_nonce();
let cipher = CipherKey::new(&key).expect("key");
let mut buf = b"payload".to_vec();
cipher.seal(&nonce, b"", &mut buf).expect("seal");
buf[0] ^= 0x01;
assert!(
cipher.open(&nonce, b"", &mut buf).is_err(),
"open must reject ciphertext whose bytes have been modified"
);
}
}