use crate::algorithm::Algorithm;
pub mod crypt;
pub mod file;
pub mod header;
pub mod key;
pub const ALGORITHM: Algorithm = Algorithm::XChaCha20Poly1305;
#[cfg(test)]
mod tests {
use crate::{
file::PlaintextFile,
memory::{SecureBytes, SecureString},
v2::{
crypt::{decrypt_bytes, encrypt_bytes},
file::EncryptedFile,
header::{AadPurpose, HeaderBinding},
key::KeyDerivationParams,
},
};
#[test]
fn swapped_filename_and_content_ciphertexts_fail_authentication() {
let key = [9u8; 32];
let salt = [1u8; 16];
let params = KeyDerivationParams::test_defaults();
let content_nonce = [2u8; 24];
let filename_nonce = [3u8; 24];
let binding = HeaderBinding::new(&salt, ¶ms, &content_nonce, &filename_nonce);
let (filename_ct, _) = encrypt_bytes(
b"secret-name.txt",
&key,
&filename_nonce,
&binding.aad(AadPurpose::Filename),
)
.unwrap();
let (content_ct, _) = encrypt_bytes(
b"file content",
&key,
&content_nonce,
&binding.aad(AadPurpose::Content),
)
.unwrap();
let swapped_content = decrypt_bytes(
&filename_ct,
&key,
&filename_nonce,
&binding.aad(AadPurpose::Content),
);
let swapped_filename = decrypt_bytes(
&content_ct,
&key,
&content_nonce,
&binding.aad(AadPurpose::Filename),
);
assert!(swapped_content.is_err());
assert!(swapped_filename.is_err());
}
#[test]
fn tampered_header_fields_fail_authentication() {
let key = [9u8; 32];
let salt = [1u8; 16];
let params = KeyDerivationParams::test_defaults();
let content_nonce = [2u8; 24];
let filename_nonce = [3u8; 24];
let binding = HeaderBinding::new(&salt, ¶ms, &content_nonce, &filename_nonce);
let (content_ct, _) = encrypt_bytes(
b"file content",
&key,
&content_nonce,
&binding.aad(AadPurpose::Content),
)
.unwrap();
let weak_params = KeyDerivationParams::new(8, 1, 1, 32);
let tampered = HeaderBinding::new(&salt, &weak_params, &content_nonce, &filename_nonce);
assert!(
decrypt_bytes(
&content_ct,
&key,
&content_nonce,
&tampered.aad(AadPurpose::Content),
)
.is_err()
);
let other_salt = [7u8; 16];
let tampered = HeaderBinding::new(&other_salt, ¶ms, &content_nonce, &filename_nonce);
assert!(
decrypt_bytes(
&content_ct,
&key,
&content_nonce,
&tampered.aad(AadPurpose::Content),
)
.is_err()
);
}
#[test]
fn seal_decrypt_round_trip_via_bytes() {
let salt = [1u8; 16];
let params = KeyDerivationParams::test_defaults();
let (key, _) = params.derive_key(b"password", &salt).unwrap();
let plaintext_file = PlaintextFile::new(
SecureString::new("name.txt".to_string()),
SecureBytes::new(b"hello".to_vec()),
);
let sealed =
EncryptedFile::seal(&plaintext_file, &key, params, salt, [2u8; 24], [3u8; 24]).unwrap();
let parsed = EncryptedFile::from_bytes(&sealed.to_bytes()).unwrap();
let decrypted = parsed.decrypt(&key).unwrap();
assert_eq!(decrypted.filename().as_str(), "name.txt");
assert_eq!(decrypted.content().as_slice(), b"hello");
}
#[test]
fn seal_decrypt_with_wrong_key_fails() {
let salt = [1u8; 16];
let params = KeyDerivationParams::test_defaults();
let (key, _) = params.derive_key(b"password", &salt).unwrap();
let (wrong_key, _) = params.derive_key(b"wrong", &salt).unwrap();
let plaintext_file = PlaintextFile::new(
SecureString::new("name.txt".to_string()),
SecureBytes::new(b"hello".to_vec()),
);
let sealed =
EncryptedFile::seal(&plaintext_file, &key, params, salt, [2u8; 24], [3u8; 24]).unwrap();
assert!(sealed.decrypt(&wrong_key).is_err());
}
}