#![cfg(feature = "chacha20poly1305")]
use bolero::check;
use evidence::{
codec::Identity,
encrypted::{Encrypted, EncryptedUnchecked},
encryption::chacha20poly1305::ChaCha20Poly1305,
};
fn key_from_seed(seed: [u8; 32]) -> chacha20poly1305::Key {
chacha20poly1305::Key::from(seed)
}
#[test]
#[allow(clippy::expect_used)] fn encrypted_decrypt_roundtrip() {
check!()
.with_type::<([u8; 32], Vec<u8>)>()
.for_each(|(seed, plaintext)| {
let key = key_from_seed(*seed);
let encrypted: Encrypted<Vec<u8>, ChaCha20Poly1305, Identity> =
Encrypted::encrypt(&key, plaintext);
let decrypted = encrypted
.try_decrypt(&key)
.expect("decryption should succeed for just-encrypted payload");
assert_eq!(
&decrypted, plaintext,
"decrypted payload must match original"
);
});
}
#[test]
fn encrypted_different_nonces() {
check!()
.with_type::<([u8; 32], Vec<u8>)>()
.for_each(|(seed, plaintext)| {
let key = key_from_seed(*seed);
let encrypted1: Encrypted<Vec<u8>, ChaCha20Poly1305, Identity> =
Encrypted::encrypt(&key, plaintext);
let encrypted2: Encrypted<Vec<u8>, ChaCha20Poly1305, Identity> =
Encrypted::encrypt(&key, plaintext);
assert_ne!(
encrypted1.nonce(),
encrypted2.nonce(),
"different encryptions should use different nonces"
);
});
}
#[test]
fn wrong_key_fails_decryption() {
check!()
.with_type::<([u8; 32], [u8; 32], Vec<u8>)>()
.filter(|(a, b, _)| a != b)
.for_each(|(seed_correct, seed_wrong, plaintext)| {
let correct_key = key_from_seed(*seed_correct);
let wrong_key = key_from_seed(*seed_wrong);
let encrypted: Encrypted<Vec<u8>, ChaCha20Poly1305, Identity> =
Encrypted::encrypt(&correct_key, plaintext);
assert!(
encrypted.try_decrypt(&wrong_key).is_err(),
"decryption must fail with wrong key"
);
});
}
#[test]
#[allow(clippy::indexing_slicing)] fn tampered_ciphertext_fails_decryption() {
check!()
.with_type::<([u8; 32], Vec<u8>)>()
.cloned()
.filter(|(_, plaintext)| !plaintext.is_empty())
.for_each(|(seed, plaintext)| {
let key = key_from_seed(seed);
let encrypted: Encrypted<Vec<u8>, ChaCha20Poly1305, Identity> =
Encrypted::encrypt(&key, &plaintext);
let mut tampered_ciphertext = encrypted.ciphertext().to_vec();
tampered_ciphertext[0] = tampered_ciphertext[0].wrapping_add(1);
let tampered: Encrypted<Vec<u8>, ChaCha20Poly1305, Identity> =
Encrypted::from_unchecked_parts(tampered_ciphertext, *encrypted.nonce());
assert!(
tampered.try_decrypt(&key).is_err(),
"decryption must fail with tampered ciphertext"
);
});
}
#[test]
fn deterministic_with_same_nonce() {
check!()
.with_type::<([u8; 32], [u8; 12], Vec<u8>)>()
.for_each(|(seed, nonce_bytes, plaintext)| {
let key = key_from_seed(*seed);
let nonce = hybrid_array::Array::from(*nonce_bytes);
let encrypted1: Encrypted<Vec<u8>, ChaCha20Poly1305, Identity> =
Encrypted::encrypt_with_nonce(&key, &nonce, plaintext);
let encrypted2: Encrypted<Vec<u8>, ChaCha20Poly1305, Identity> =
Encrypted::encrypt_with_nonce(&key, &nonce, plaintext);
assert_eq!(
encrypted1.ciphertext(),
encrypted2.ciphertext(),
"same key + nonce + plaintext must produce same ciphertext"
);
});
}