pub mod crypt;
pub mod file;
pub mod file_ops;
pub mod header;
pub mod header_ops;
pub mod key;
pub mod key_ops;
#[cfg(test)]
mod tests {
use crate::v2::{
crypt::{decrypt_bytes, encrypt_bytes},
header::{AadPurpose, FileHeader, 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 header_round_trip_decrypts_with_header_binding() {
let key = [4u8; 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"name.txt",
&key,
&filename_nonce,
&binding.aad(AadPurpose::Filename),
)
.unwrap();
let (content_ct, _) = encrypt_bytes(
b"hello",
&key,
&content_nonce,
&binding.aad(AadPurpose::Content),
)
.unwrap();
let header =
FileHeader::new(salt, params, content_nonce, filename_nonce, filename_ct).unwrap();
let serialized = crate::v2::header_ops::serialize(&header);
let parsed = crate::v2::header_ops::try_deserialize(&serialized).unwrap();
let (name, _) = decrypt_bytes(
&parsed.filename_ciphertext,
&key,
&parsed.filename_nonce,
&parsed.binding().aad(AadPurpose::Filename),
)
.unwrap();
let (content, _) = decrypt_bytes(
&content_ct,
&key,
&parsed.content_nonce,
&parsed.binding().aad(AadPurpose::Content),
)
.unwrap();
assert_eq!(name.as_slice(), b"name.txt");
assert_eq!(content.as_slice(), b"hello");
}
}