use super::*;
use crate::encryption::aad::{BlockType, EncryptionContext, build};
use crate::table::block::BlockIdentity as TableBlockIdentity;
const TEST_KEY: [u8; 32] = [0x42; 32];
const TEST_KEY_OTHER: [u8; 32] = [0x55; 32];
fn test_aad(suite: SuiteId) -> [u8; AAD_LEN] {
let ctx = EncryptionContext::v1(
7, suite, 0, 0, );
let identity = TableBlockIdentity::for_test(0xAB, BlockType::Data);
build(&ctx, &identity)
}
fn test_nonce() -> [u8; 12] {
[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
]
}
fn roundtrip_for(suite: SuiteId) {
let plaintext = b"hello AAD-bound world";
let aad = test_aad(suite);
let nonce = test_nonce();
let mut buf = plaintext.to_vec();
let tag =
encrypt_in_place(suite, &TEST_KEY, &nonce, &aad, &mut buf).expect("encrypt should succeed");
assert_ne!(&buf[..], &plaintext[..]);
decrypt_in_place(suite, &TEST_KEY, &nonce, &aad, &tag, &mut buf)
.expect("decrypt should succeed with matching key + aad");
assert_eq!(&buf[..], &plaintext[..]);
}
#[test]
fn aes256gcm_roundtrip_recovers_plaintext() {
roundtrip_for(SuiteId::Aes256Gcm);
}
#[test]
fn chacha20poly1305_roundtrip_recovers_plaintext() {
roundtrip_for(SuiteId::ChaCha20Poly1305);
}
fn wrong_key_fails_for(suite: SuiteId) {
let aad = test_aad(suite);
let nonce = test_nonce();
let mut buf = b"abc".to_vec();
let tag = encrypt_in_place(suite, &TEST_KEY, &nonce, &aad, &mut buf).unwrap();
let err = decrypt_in_place(suite, &TEST_KEY_OTHER, &nonce, &aad, &tag, &mut buf)
.expect_err("wrong key must fail");
assert!(matches!(err, DecryptError::AeadVerificationFailed));
}
#[test]
fn aes256gcm_wrong_key_fails_with_aead_verification_failed() {
wrong_key_fails_for(SuiteId::Aes256Gcm);
}
#[test]
fn chacha20poly1305_wrong_key_fails_with_aead_verification_failed() {
wrong_key_fails_for(SuiteId::ChaCha20Poly1305);
}
fn tampered_aad_fails_for(suite: SuiteId) {
let aad = test_aad(suite);
let nonce = test_nonce();
let mut buf = b"abc".to_vec();
let tag = encrypt_in_place(suite, &TEST_KEY, &nonce, &aad, &mut buf).unwrap();
let mut bad_aad = aad;
bad_aad[5] ^= 0x01;
let err = decrypt_in_place(suite, &TEST_KEY, &nonce, &bad_aad, &tag, &mut buf)
.expect_err("AAD tamper must fail");
assert!(matches!(err, DecryptError::AeadVerificationFailed));
}
#[test]
fn aes256gcm_tampered_aad_byte_fails_verification() {
tampered_aad_fails_for(SuiteId::Aes256Gcm);
}
#[test]
fn chacha20poly1305_tampered_aad_byte_fails_verification() {
tampered_aad_fails_for(SuiteId::ChaCha20Poly1305);
}
fn tampered_ciphertext_fails_for(suite: SuiteId) {
let aad = test_aad(suite);
let nonce = test_nonce();
let mut buf = b"abcdef".to_vec();
let tag = encrypt_in_place(suite, &TEST_KEY, &nonce, &aad, &mut buf).unwrap();
buf[0] ^= 0x01;
let err = decrypt_in_place(suite, &TEST_KEY, &nonce, &aad, &tag, &mut buf)
.expect_err("ciphertext tamper must fail");
assert!(matches!(err, DecryptError::AeadVerificationFailed));
}
#[test]
fn aes256gcm_tampered_ciphertext_fails_verification() {
tampered_ciphertext_fails_for(SuiteId::Aes256Gcm);
}
#[test]
fn chacha20poly1305_tampered_ciphertext_fails_verification() {
tampered_ciphertext_fails_for(SuiteId::ChaCha20Poly1305);
}
#[test]
fn wrong_nonce_length_returns_malformed_body_frame_error() {
let aad = test_aad(SuiteId::Aes256Gcm);
let mut buf = b"abc".to_vec();
let short_nonce = [0u8; 11];
let bogus_tag = [0u8; TAG_LEN];
let err = decrypt_in_place(
SuiteId::Aes256Gcm,
&TEST_KEY,
&short_nonce,
&aad,
&bogus_tag,
&mut buf,
)
.expect_err("wrong nonce length must fail");
assert!(matches!(err, DecryptError::MalformedBodyFrame(_)));
}
#[test]
fn cross_suite_decrypt_fails_verification() {
let aes_aad = test_aad(SuiteId::Aes256Gcm);
let nonce = test_nonce();
let mut buf = b"abc".to_vec();
let tag = encrypt_in_place(SuiteId::Aes256Gcm, &TEST_KEY, &nonce, &aes_aad, &mut buf).unwrap();
let err = decrypt_in_place(
SuiteId::ChaCha20Poly1305,
&TEST_KEY,
&nonce,
&aes_aad,
&tag,
&mut buf,
)
.expect_err("cross-suite decrypt must fail");
assert!(matches!(err, DecryptError::AeadVerificationFailed));
}