mirror of
https://github.com/sbrow/envr.git
synced 2026-06-27 18:48:33 -04:00
808 lines
23 KiB
Rust
808 lines
23 KiB
Rust
//! Tests for file-based encryption and decryption functions.
|
|
|
|
use crate::file::*;
|
|
use crate::keys::*;
|
|
use crate::memory::*;
|
|
use crate::passphrase::*;
|
|
use crate::types::*;
|
|
use std::ffi::CString;
|
|
use std::fs;
|
|
use std::io::Write;
|
|
|
|
fn create_temp_file(suffix: &str) -> String {
|
|
let temp_dir = std::env::temp_dir();
|
|
let unique_id = std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap()
|
|
.as_nanos();
|
|
format!("{}/age_test_{}_{}", temp_dir.display(), unique_id, suffix)
|
|
}
|
|
|
|
// ============= age_encrypt_to_file tests =============
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_basic() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
let plaintext = b"Hello, file encryption!";
|
|
let output_path = create_temp_file("encrypted.age");
|
|
let output_path_c = CString::new(output_path.as_str()).unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
output_path_c.as_ptr(),
|
|
keypair.public_key,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::Success);
|
|
assert!(std::path::Path::new(&output_path).exists());
|
|
|
|
// Clean up
|
|
fs::remove_file(&output_path).ok();
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_null_plaintext() {
|
|
let output_path = create_temp_file("test.age");
|
|
let output_path_c = CString::new(output_path.as_str()).unwrap();
|
|
let recipient = CString::new("age1test").unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
std::ptr::null(),
|
|
0,
|
|
output_path_c.as_ptr(),
|
|
recipient.as_ptr(),
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidInput);
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_null_output_path() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
let plaintext = b"test";
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
std::ptr::null(),
|
|
keypair.public_key,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidInput);
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_null_recipient() {
|
|
let plaintext = b"test";
|
|
let output_path = create_temp_file("test.age");
|
|
let output_path_c = CString::new(output_path.as_str()).unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
output_path_c.as_ptr(),
|
|
std::ptr::null(),
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidInput);
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_invalid_recipient() {
|
|
let plaintext = b"test";
|
|
let output_path = create_temp_file("test.age");
|
|
let output_path_c = CString::new(output_path.as_str()).unwrap();
|
|
let invalid_recipient = CString::new("age1invalid_not_a_real_key").unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
output_path_c.as_ptr(),
|
|
invalid_recipient.as_ptr(),
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidRecipient);
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_and_decrypt_with_identity() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
let plaintext = b"Round trip file encryption test!";
|
|
let output_path = create_temp_file("roundtrip.age");
|
|
let output_path_c = CString::new(output_path.as_str()).unwrap();
|
|
|
|
// Encrypt to file
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
output_path_c.as_ptr(),
|
|
keypair.public_key,
|
|
);
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
// Decrypt with identity string
|
|
let mut output = AgeBuffer::null();
|
|
let result = age_decrypt_file_with_identity(
|
|
output_path_c.as_ptr(),
|
|
keypair.private_key,
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
let decrypted = unsafe { std::slice::from_raw_parts(output.data, output.len) };
|
|
assert_eq!(decrypted, plaintext);
|
|
|
|
// Clean up
|
|
fs::remove_file(&output_path).ok();
|
|
age_free_buffer(&mut output);
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
// ============= age_encrypt_to_file_armor tests =============
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_armor_basic() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
let plaintext = b"Armored file test";
|
|
let output_path = create_temp_file("armored.age");
|
|
let output_path_c = CString::new(output_path.as_str()).unwrap();
|
|
|
|
let result = age_encrypt_to_file_armor(
|
|
plaintext.as_ptr(),
|
|
plaintext.len(),
|
|
output_path_c.as_ptr(),
|
|
keypair.public_key,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
// Verify the file is armored
|
|
let contents = fs::read_to_string(&output_path).unwrap();
|
|
assert!(contents.contains("-----BEGIN AGE ENCRYPTED FILE-----"));
|
|
assert!(contents.contains("-----END AGE ENCRYPTED FILE-----"));
|
|
|
|
// Clean up
|
|
fs::remove_file(&output_path).ok();
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_armor_null_plaintext() {
|
|
let output_path = create_temp_file("test.age");
|
|
let output_path_c = CString::new(output_path.as_str()).unwrap();
|
|
let recipient = CString::new("age1test").unwrap();
|
|
|
|
let result = age_encrypt_to_file_armor(
|
|
std::ptr::null(),
|
|
0,
|
|
output_path_c.as_ptr(),
|
|
recipient.as_ptr(),
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidInput);
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_armor_null_output_path() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
let plaintext = b"test";
|
|
|
|
let result = age_encrypt_to_file_armor(
|
|
plaintext.as_ptr(),
|
|
plaintext.len(),
|
|
std::ptr::null(),
|
|
keypair.public_key,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidInput);
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_armor_invalid_recipient() {
|
|
let plaintext = b"test";
|
|
let output_path = create_temp_file("test.age");
|
|
let output_path_c = CString::new(output_path.as_str()).unwrap();
|
|
let invalid_recipient = CString::new("not-a-recipient").unwrap();
|
|
|
|
let result = age_encrypt_to_file_armor(
|
|
plaintext.as_ptr(),
|
|
plaintext.len(),
|
|
output_path_c.as_ptr(),
|
|
invalid_recipient.as_ptr(),
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidRecipient);
|
|
}
|
|
|
|
// ============= age_decrypt_file tests =============
|
|
|
|
#[test]
|
|
fn test_decrypt_file_basic() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
let plaintext = b"Decrypt from identity file test";
|
|
|
|
// Create encrypted file
|
|
let encrypted_path = create_temp_file("encrypted.age");
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
encrypted_path_c.as_ptr(),
|
|
keypair.public_key,
|
|
);
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
// Create identity file
|
|
let identity_path = create_temp_file("identity.txt");
|
|
let private_key = unsafe { std::ffi::CStr::from_ptr(keypair.private_key).to_str().unwrap() };
|
|
fs::write(&identity_path, private_key).unwrap();
|
|
let identity_path_c = CString::new(identity_path.as_str()).unwrap();
|
|
|
|
// Decrypt
|
|
let mut output = AgeBuffer::null();
|
|
let result = age_decrypt_file(
|
|
encrypted_path_c.as_ptr(),
|
|
identity_path_c.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
let decrypted = unsafe { std::slice::from_raw_parts(output.data, output.len) };
|
|
assert_eq!(decrypted, plaintext);
|
|
|
|
// Clean up
|
|
fs::remove_file(&encrypted_path).ok();
|
|
fs::remove_file(&identity_path).ok();
|
|
age_free_buffer(&mut output);
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_null_output() {
|
|
let encrypted_path = CString::new("/tmp/test.age").unwrap();
|
|
let identity_path = CString::new("/tmp/identity.txt").unwrap();
|
|
|
|
let result = age_decrypt_file(
|
|
encrypted_path.as_ptr(),
|
|
identity_path.as_ptr(),
|
|
std::ptr::null_mut(),
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidInput);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_null_encrypted_path() {
|
|
let identity_path = CString::new("/tmp/identity.txt").unwrap();
|
|
let mut output = AgeBuffer::null();
|
|
|
|
let result = age_decrypt_file(
|
|
std::ptr::null(),
|
|
identity_path.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidInput);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_nonexistent_identity_file() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
// Create a real encrypted file
|
|
let plaintext = b"test";
|
|
let encrypted_path = create_temp_file("test_enc.age");
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
encrypted_path_c.as_ptr(),
|
|
keypair.public_key,
|
|
);
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
// Try to decrypt with nonexistent identity file
|
|
let identity_path = CString::new("/nonexistent/identity.txt").unwrap();
|
|
let mut output = AgeBuffer::null();
|
|
|
|
let result = age_decrypt_file(
|
|
encrypted_path_c.as_ptr(),
|
|
identity_path.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::IoError);
|
|
|
|
fs::remove_file(&encrypted_path).ok();
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_nonexistent_encrypted_file() {
|
|
// Create a valid identity file
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
let identity_path = create_temp_file("identity.txt");
|
|
let private_key = unsafe { std::ffi::CStr::from_ptr(keypair.private_key).to_str().unwrap() };
|
|
fs::write(&identity_path, private_key).unwrap();
|
|
let identity_path_c = CString::new(identity_path.as_str()).unwrap();
|
|
|
|
let encrypted_path = CString::new("/nonexistent/encrypted.age").unwrap();
|
|
let mut output = AgeBuffer::null();
|
|
|
|
let result = age_decrypt_file(
|
|
encrypted_path.as_ptr(),
|
|
identity_path_c.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::IoError);
|
|
|
|
fs::remove_file(&identity_path).ok();
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_empty_identity_file() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
// Create encrypted file
|
|
let plaintext = b"test";
|
|
let encrypted_path = create_temp_file("enc.age");
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
encrypted_path_c.as_ptr(),
|
|
keypair.public_key,
|
|
);
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
// Create empty identity file
|
|
let identity_path = create_temp_file("empty_identity.txt");
|
|
fs::write(&identity_path, "").unwrap();
|
|
let identity_path_c = CString::new(identity_path.as_str()).unwrap();
|
|
|
|
let mut output = AgeBuffer::null();
|
|
let result = age_decrypt_file(
|
|
encrypted_path_c.as_ptr(),
|
|
identity_path_c.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidIdentity);
|
|
|
|
fs::remove_file(&encrypted_path).ok();
|
|
fs::remove_file(&identity_path).ok();
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_with_comments_in_identity() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
// Create encrypted file
|
|
let plaintext = b"test with comments";
|
|
let encrypted_path = create_temp_file("enc_comments.age");
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
encrypted_path_c.as_ptr(),
|
|
keypair.public_key,
|
|
);
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
// Create identity file with comments
|
|
let identity_path = create_temp_file("identity_with_comments.txt");
|
|
let private_key = unsafe { std::ffi::CStr::from_ptr(keypair.private_key).to_str().unwrap() };
|
|
let content = format!("# This is a comment\n\n{}\n# Another comment", private_key);
|
|
fs::write(&identity_path, content).unwrap();
|
|
let identity_path_c = CString::new(identity_path.as_str()).unwrap();
|
|
|
|
let mut output = AgeBuffer::null();
|
|
let result = age_decrypt_file(
|
|
encrypted_path_c.as_ptr(),
|
|
identity_path_c.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
let decrypted = unsafe { std::slice::from_raw_parts(output.data, output.len) };
|
|
assert_eq!(decrypted, plaintext);
|
|
|
|
fs::remove_file(&encrypted_path).ok();
|
|
fs::remove_file(&identity_path).ok();
|
|
age_free_buffer(&mut output);
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
// ============= age_decrypt_file_with_identity tests =============
|
|
|
|
#[test]
|
|
fn test_decrypt_file_with_identity_null_output() {
|
|
let encrypted_path = CString::new("/tmp/test.age").unwrap();
|
|
let identity = CString::new("AGE-SECRET-KEY-1TEST").unwrap();
|
|
|
|
let result = age_decrypt_file_with_identity(
|
|
encrypted_path.as_ptr(),
|
|
identity.as_ptr(),
|
|
std::ptr::null_mut(),
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidInput);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_with_identity_null_path() {
|
|
let identity = CString::new("AGE-SECRET-KEY-1TEST").unwrap();
|
|
let mut output = AgeBuffer::null();
|
|
|
|
let result = age_decrypt_file_with_identity(
|
|
std::ptr::null(),
|
|
identity.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidInput);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_with_identity_invalid_identity() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
// Create encrypted file
|
|
let plaintext = b"test";
|
|
let encrypted_path = create_temp_file("enc_invalid_id.age");
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
encrypted_path_c.as_ptr(),
|
|
keypair.public_key,
|
|
);
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
let invalid_identity = CString::new("not-a-valid-identity").unwrap();
|
|
let mut output = AgeBuffer::null();
|
|
|
|
let result = age_decrypt_file_with_identity(
|
|
encrypted_path_c.as_ptr(),
|
|
invalid_identity.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidIdentity);
|
|
|
|
fs::remove_file(&encrypted_path).ok();
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_with_identity_wrong_key() {
|
|
let mut keypair1 = AgeKeypair::null();
|
|
let mut keypair2 = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair1);
|
|
age_generate_x25519(&mut keypair2);
|
|
|
|
// Encrypt with keypair1
|
|
let plaintext = b"secret message";
|
|
let encrypted_path = create_temp_file("wrong_key.age");
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
encrypted_path_c.as_ptr(),
|
|
keypair1.public_key,
|
|
);
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
// Try to decrypt with keypair2
|
|
let mut output = AgeBuffer::null();
|
|
let result = age_decrypt_file_with_identity(
|
|
encrypted_path_c.as_ptr(),
|
|
keypair2.private_key,
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::DecryptionFailed);
|
|
|
|
fs::remove_file(&encrypted_path).ok();
|
|
age_free_keypair(&mut keypair1);
|
|
age_free_keypair(&mut keypair2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_with_identity_nonexistent_file() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
let encrypted_path = CString::new("/nonexistent/file.age").unwrap();
|
|
let mut output = AgeBuffer::null();
|
|
|
|
let result = age_decrypt_file_with_identity(
|
|
encrypted_path.as_ptr(),
|
|
keypair.private_key,
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::IoError);
|
|
|
|
age_free_keypair(&mut keypair);
|
|
}
|
|
|
|
// ============= age_decrypt_file_passphrase tests =============
|
|
|
|
#[test]
|
|
fn test_decrypt_file_passphrase_basic() {
|
|
let passphrase = CString::new("mysecretpassword").unwrap();
|
|
let plaintext = b"Passphrase protected content";
|
|
|
|
// Encrypt with passphrase first (using in-memory function)
|
|
let mut encrypted = AgeBuffer::null();
|
|
let result = age_encrypt_passphrase(
|
|
plaintext.as_ptr(),
|
|
plaintext.len(),
|
|
passphrase.as_ptr(),
|
|
false,
|
|
&mut encrypted,
|
|
);
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
// Write encrypted content to file
|
|
let encrypted_path = create_temp_file("passphrase.age");
|
|
let encrypted_slice = unsafe { std::slice::from_raw_parts(encrypted.data, encrypted.len) };
|
|
fs::write(&encrypted_path, encrypted_slice).unwrap();
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
|
|
// Decrypt file with passphrase
|
|
let mut output = AgeBuffer::null();
|
|
let result = age_decrypt_file_passphrase(
|
|
encrypted_path_c.as_ptr(),
|
|
passphrase.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
let decrypted = unsafe { std::slice::from_raw_parts(output.data, output.len) };
|
|
assert_eq!(decrypted, plaintext);
|
|
|
|
// Clean up
|
|
fs::remove_file(&encrypted_path).ok();
|
|
age_free_buffer(&mut encrypted);
|
|
age_free_buffer(&mut output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_passphrase_null_output() {
|
|
let encrypted_path = CString::new("/tmp/test.age").unwrap();
|
|
let passphrase = CString::new("password").unwrap();
|
|
|
|
let result = age_decrypt_file_passphrase(
|
|
encrypted_path.as_ptr(),
|
|
passphrase.as_ptr(),
|
|
std::ptr::null_mut(),
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidInput);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_passphrase_null_path() {
|
|
let passphrase = CString::new("password").unwrap();
|
|
let mut output = AgeBuffer::null();
|
|
|
|
let result = age_decrypt_file_passphrase(
|
|
std::ptr::null(),
|
|
passphrase.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidInput);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_passphrase_wrong_passphrase() {
|
|
let passphrase = CString::new("correctpassword").unwrap();
|
|
let wrong_passphrase = CString::new("wrongpassword").unwrap();
|
|
let plaintext = b"Secret content";
|
|
|
|
// Encrypt with passphrase
|
|
let mut encrypted = AgeBuffer::null();
|
|
let result = age_encrypt_passphrase(
|
|
plaintext.as_ptr(),
|
|
plaintext.len(),
|
|
passphrase.as_ptr(),
|
|
false,
|
|
&mut encrypted,
|
|
);
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
// Write to file
|
|
let encrypted_path = create_temp_file("wrong_pass.age");
|
|
let encrypted_slice = unsafe { std::slice::from_raw_parts(encrypted.data, encrypted.len) };
|
|
fs::write(&encrypted_path, encrypted_slice).unwrap();
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
|
|
// Try to decrypt with wrong passphrase
|
|
let mut output = AgeBuffer::null();
|
|
let result = age_decrypt_file_passphrase(
|
|
encrypted_path_c.as_ptr(),
|
|
wrong_passphrase.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::DecryptionFailed);
|
|
|
|
// Clean up
|
|
fs::remove_file(&encrypted_path).ok();
|
|
age_free_buffer(&mut encrypted);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_passphrase_nonexistent_file() {
|
|
let passphrase = CString::new("password").unwrap();
|
|
let encrypted_path = CString::new("/nonexistent/passphrase.age").unwrap();
|
|
let mut output = AgeBuffer::null();
|
|
|
|
let result = age_decrypt_file_passphrase(
|
|
encrypted_path.as_ptr(),
|
|
passphrase.as_ptr(),
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::IoError);
|
|
}
|
|
|
|
// ============= Recipient file tests =============
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_with_recipients_file() {
|
|
let mut keypair1 = AgeKeypair::null();
|
|
let mut keypair2 = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair1);
|
|
age_generate_x25519(&mut keypair2);
|
|
|
|
// Create recipients file
|
|
let recipients_path = create_temp_file("recipients.txt");
|
|
let pub_key1 = unsafe { std::ffi::CStr::from_ptr(keypair1.public_key).to_str().unwrap() };
|
|
let pub_key2 = unsafe { std::ffi::CStr::from_ptr(keypair2.public_key).to_str().unwrap() };
|
|
let content = format!("# Comment line\n{}\n{}\n", pub_key1, pub_key2);
|
|
fs::write(&recipients_path, content).unwrap();
|
|
let recipients_path_c = CString::new(recipients_path.as_str()).unwrap();
|
|
|
|
// Encrypt to file
|
|
let plaintext = b"Multi-recipient from file test";
|
|
let encrypted_path = create_temp_file("multi_recip.age");
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
encrypted_path_c.as_ptr(),
|
|
recipients_path_c.as_ptr(),
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
// Both recipients should be able to decrypt
|
|
let mut output1 = AgeBuffer::null();
|
|
let result = age_decrypt_file_with_identity(
|
|
encrypted_path_c.as_ptr(),
|
|
keypair1.private_key,
|
|
&mut output1,
|
|
);
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
let mut output2 = AgeBuffer::null();
|
|
let result = age_decrypt_file_with_identity(
|
|
encrypted_path_c.as_ptr(),
|
|
keypair2.private_key,
|
|
&mut output2,
|
|
);
|
|
assert_eq!(result, AgeResult::Success);
|
|
|
|
// Clean up
|
|
fs::remove_file(&recipients_path).ok();
|
|
fs::remove_file(&encrypted_path).ok();
|
|
age_free_buffer(&mut output1);
|
|
age_free_buffer(&mut output2);
|
|
age_free_keypair(&mut keypair1);
|
|
age_free_keypair(&mut keypair2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_empty_recipients_file() {
|
|
let plaintext = b"test";
|
|
let encrypted_path = create_temp_file("empty_recip.age");
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
|
|
// Create empty recipients file
|
|
let recipients_path = create_temp_file("empty_recipients.txt");
|
|
fs::write(&recipients_path, "# Only comments\n\n").unwrap();
|
|
let recipients_path_c = CString::new(recipients_path.as_str()).unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
encrypted_path_c.as_ptr(),
|
|
recipients_path_c.as_ptr(),
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::InvalidRecipient);
|
|
|
|
// Clean up
|
|
fs::remove_file(&recipients_path).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_to_file_nonexistent_recipients_file() {
|
|
let plaintext = b"test";
|
|
let encrypted_path = create_temp_file("test.age");
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
let recipients_path = CString::new("/nonexistent/recipients.txt").unwrap();
|
|
|
|
let result = age_encrypt_to_file(
|
|
plaintext.as_ptr() as *const i8,
|
|
plaintext.len(),
|
|
encrypted_path_c.as_ptr(),
|
|
recipients_path.as_ptr(),
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::IoError);
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_file_corrupted_file() {
|
|
let mut keypair = AgeKeypair::null();
|
|
age_generate_x25519(&mut keypair);
|
|
|
|
// Create corrupted encrypted file
|
|
let encrypted_path = create_temp_file("corrupted.age");
|
|
fs::write(&encrypted_path, "not valid age encrypted content").unwrap();
|
|
let encrypted_path_c = CString::new(encrypted_path.as_str()).unwrap();
|
|
|
|
let mut output = AgeBuffer::null();
|
|
let result = age_decrypt_file_with_identity(
|
|
encrypted_path_c.as_ptr(),
|
|
keypair.private_key,
|
|
&mut output,
|
|
);
|
|
|
|
assert_eq!(result, AgeResult::DecryptionFailed);
|
|
|
|
// Clean up
|
|
fs::remove_file(&encrypted_path).ok();
|
|
age_free_keypair(&mut keypair);
|
|
} |