mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-08 06:13:14 +00:00
300 lines
10 KiB
Rust
300 lines
10 KiB
Rust
use crate::{decrypt_data, encrypt_data};
|
|
|
|
const PASSWORD: &[u8] = "test_password".as_bytes();
|
|
const LONG_PASSWORD: &[u8] = "very_long_password_with_many_characters_for_testing_purposes_123456789".as_bytes();
|
|
const EMPTY_PASSWORD: &[u8] = b"";
|
|
|
|
#[test_case::test_case("hello world".as_bytes())]
|
|
#[test_case::test_case(&[])]
|
|
#[test_case::test_case(&[1, 2, 3])]
|
|
#[test_case::test_case(&[3, 2, 1])]
|
|
fn test_basic_encrypt_decrypt_roundtrip(input: &[u8]) -> Result<(), crate::Error> {
|
|
let encrypted = encrypt_data(PASSWORD, input)?;
|
|
let decrypted = decrypt_data(PASSWORD, &encrypted)?;
|
|
assert_eq!(input, decrypted, "input is not equal output");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_decrypt_with_different_passwords() -> Result<(), crate::Error> {
|
|
let data = b"sensitive data";
|
|
let password1 = b"password1";
|
|
let password2 = b"password2";
|
|
|
|
let encrypted = encrypt_data(password1, data)?;
|
|
|
|
// Decrypting with correct password should work
|
|
let decrypted = decrypt_data(password1, &encrypted)?;
|
|
assert_eq!(data, decrypted.as_slice());
|
|
|
|
// Decrypting with wrong password should fail
|
|
let result = decrypt_data(password2, &encrypted);
|
|
assert!(result.is_err(), "Decryption with wrong password should fail");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_decrypt_empty_data() -> Result<(), crate::Error> {
|
|
let empty_data = b"";
|
|
let encrypted = encrypt_data(PASSWORD, empty_data)?;
|
|
let decrypted = decrypt_data(PASSWORD, &encrypted)?;
|
|
assert_eq!(empty_data, decrypted.as_slice());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_decrypt_large_data() -> Result<(), crate::Error> {
|
|
// Test with 1MB of data
|
|
let large_data = vec![0xAB; 1024 * 1024];
|
|
let encrypted = encrypt_data(PASSWORD, &large_data)?;
|
|
let decrypted = decrypt_data(PASSWORD, &encrypted)?;
|
|
assert_eq!(large_data, decrypted);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_decrypt_with_empty_password() -> Result<(), crate::Error> {
|
|
let data = b"test data";
|
|
let encrypted = encrypt_data(EMPTY_PASSWORD, data)?;
|
|
let decrypted = decrypt_data(EMPTY_PASSWORD, &encrypted)?;
|
|
assert_eq!(data, decrypted.as_slice());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_decrypt_with_long_password() -> Result<(), crate::Error> {
|
|
let data = b"test data with long password";
|
|
let encrypted = encrypt_data(LONG_PASSWORD, data)?;
|
|
let decrypted = decrypt_data(LONG_PASSWORD, &encrypted)?;
|
|
assert_eq!(data, decrypted.as_slice());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_decrypt_binary_data() -> Result<(), crate::Error> {
|
|
// Test with various binary patterns
|
|
let binary_patterns = [
|
|
vec![0x00; 100], // All zeros
|
|
vec![0xFF; 100], // All ones
|
|
(0..=255u8).cycle().take(1000).collect::<Vec<u8>>(), // Sequential pattern
|
|
[0xAA, 0x55].repeat(500), // Alternating pattern
|
|
];
|
|
|
|
for pattern in &binary_patterns {
|
|
let encrypted = encrypt_data(PASSWORD, pattern)?;
|
|
let decrypted = decrypt_data(PASSWORD, &encrypted)?;
|
|
assert_eq!(pattern, &decrypted, "Binary pattern mismatch");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypt_decrypt_unicode_data() -> Result<(), crate::Error> {
|
|
let unicode_strings = [
|
|
"Hello, 世界! 🌍",
|
|
"Тест на русском языке",
|
|
"العربية اختبار",
|
|
"🚀🔐💻🌟⭐",
|
|
"Mixed: ASCII + 中文 + العربية + 🎉",
|
|
];
|
|
|
|
for text in &unicode_strings {
|
|
let data = text.as_bytes();
|
|
let encrypted = encrypt_data(PASSWORD, data)?;
|
|
let decrypted = decrypt_data(PASSWORD, &encrypted)?;
|
|
assert_eq!(data, decrypted.as_slice(), "Unicode data mismatch for: {}", text);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_with_corrupted_data() {
|
|
let data = b"test data";
|
|
let encrypted = encrypt_data(PASSWORD, data).expect("Encryption should succeed");
|
|
|
|
// Test various corruption scenarios
|
|
let corruption_tests = [
|
|
(0, "Corrupt first byte"),
|
|
(encrypted.len() - 1, "Corrupt last byte"),
|
|
(encrypted.len() / 2, "Corrupt middle byte"),
|
|
];
|
|
|
|
for (corrupt_index, description) in &corruption_tests {
|
|
let mut corrupted = encrypted.clone();
|
|
corrupted[*corrupt_index] ^= 0xFF; // Flip all bits
|
|
|
|
let result = decrypt_data(PASSWORD, &corrupted);
|
|
assert!(result.is_err(), "{} should cause decryption to fail", description);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_with_truncated_data() {
|
|
let data = b"test data for truncation";
|
|
let encrypted = encrypt_data(PASSWORD, data).expect("Encryption should succeed");
|
|
|
|
// Test truncation at various lengths
|
|
let truncation_lengths = [
|
|
0, // Empty data
|
|
10, // Very short
|
|
32, // Salt length
|
|
44, // Just before nonce
|
|
encrypted.len() - 1, // Missing last byte
|
|
];
|
|
|
|
for &length in &truncation_lengths {
|
|
let truncated = &encrypted[..length.min(encrypted.len())];
|
|
let result = decrypt_data(PASSWORD, truncated);
|
|
assert!(result.is_err(), "Truncated data (length {}) should cause decryption to fail", length);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_decrypt_with_invalid_header() {
|
|
let data = b"test data";
|
|
let mut encrypted = encrypt_data(PASSWORD, data).expect("Encryption should succeed");
|
|
|
|
// Corrupt the algorithm ID (byte 32)
|
|
if encrypted.len() > 32 {
|
|
encrypted[32] = 0xFF; // Invalid algorithm ID
|
|
let result = decrypt_data(PASSWORD, &encrypted);
|
|
assert!(result.is_err(), "Invalid algorithm ID should cause decryption to fail");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_encryption_produces_different_outputs() -> Result<(), crate::Error> {
|
|
let data = b"same data";
|
|
|
|
// Encrypt the same data multiple times
|
|
let encrypted1 = encrypt_data(PASSWORD, data)?;
|
|
let encrypted2 = encrypt_data(PASSWORD, data)?;
|
|
|
|
// Encrypted outputs should be different due to random salt and nonce
|
|
assert_ne!(encrypted1, encrypted2, "Encryption should produce different outputs for same input");
|
|
|
|
// But both should decrypt to the same original data
|
|
let decrypted1 = decrypt_data(PASSWORD, &encrypted1)?;
|
|
let decrypted2 = decrypt_data(PASSWORD, &encrypted2)?;
|
|
assert_eq!(decrypted1, decrypted2);
|
|
assert_eq!(data, decrypted1.as_slice());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_encrypted_data_structure() -> Result<(), crate::Error> {
|
|
let data = b"test data";
|
|
let encrypted = encrypt_data(PASSWORD, data)?;
|
|
|
|
// Encrypted data should be longer than original (due to salt, nonce, tag)
|
|
assert!(encrypted.len() > data.len(), "Encrypted data should be longer than original");
|
|
|
|
// Should have at least: 32 bytes salt + 1 byte ID + 12 bytes nonce + data + 16 bytes tag
|
|
let min_expected_length = 32 + 1 + 12 + data.len() + 16;
|
|
assert!(encrypted.len() >= min_expected_length,
|
|
"Encrypted data length {} should be at least {}", encrypted.len(), min_expected_length);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_password_variations() -> Result<(), crate::Error> {
|
|
let data = b"test data";
|
|
|
|
let password_variations = [
|
|
b"a".as_slice(), // Single character
|
|
b"12345".as_slice(), // Numeric
|
|
b"!@#$%^&*()".as_slice(), // Special characters
|
|
b"\x00\x01\x02\x03".as_slice(), // Binary password
|
|
"密码测试".as_bytes(), // Unicode password
|
|
&[0xFF; 64], // Long binary password
|
|
];
|
|
|
|
for password in &password_variations {
|
|
let encrypted = encrypt_data(password, data)?;
|
|
let decrypted = decrypt_data(password, &encrypted)?;
|
|
assert_eq!(data, decrypted.as_slice(), "Failed with password: {:?}", password);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_deterministic_with_same_salt_and_nonce() {
|
|
// Note: This test is more for understanding the behavior
|
|
// In real implementation, salt and nonce should be random
|
|
let data = b"test data";
|
|
|
|
let encrypted1 = encrypt_data(PASSWORD, data).expect("Encryption should succeed");
|
|
let encrypted2 = encrypt_data(PASSWORD, data).expect("Encryption should succeed");
|
|
|
|
// Due to random salt and nonce, outputs should be different
|
|
assert_ne!(encrypted1, encrypted2, "Encryption should use random salt/nonce");
|
|
}
|
|
|
|
#[test]
|
|
fn test_cross_platform_compatibility() -> Result<(), crate::Error> {
|
|
// Test data that might behave differently on different platforms
|
|
let test_cases = [
|
|
vec![0x00, 0x01, 0x02, 0x03], // Low values
|
|
vec![0xFC, 0xFD, 0xFE, 0xFF], // High values
|
|
(0..256u16).map(|x| (x % 256) as u8).collect::<Vec<u8>>(), // Full byte range
|
|
];
|
|
|
|
for test_data in &test_cases {
|
|
let encrypted = encrypt_data(PASSWORD, test_data)?;
|
|
let decrypted = decrypt_data(PASSWORD, &encrypted)?;
|
|
assert_eq!(test_data, &decrypted, "Cross-platform compatibility failed");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_memory_safety_with_large_passwords() -> Result<(), crate::Error> {
|
|
let data = b"test data";
|
|
|
|
// Test with very large passwords
|
|
let large_passwords = [
|
|
vec![b'a'; 1024], // 1KB password
|
|
vec![b'x'; 10 * 1024], // 10KB password
|
|
(0..=255u8).cycle().take(5000).collect::<Vec<u8>>(), // 5KB varied password
|
|
];
|
|
|
|
for password in &large_passwords {
|
|
let encrypted = encrypt_data(password, data)?;
|
|
let decrypted = decrypt_data(password, &encrypted)?;
|
|
assert_eq!(data, decrypted.as_slice(), "Failed with large password of size {}", password.len());
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_concurrent_encryption_safety() -> Result<(), crate::Error> {
|
|
use std::sync::Arc;
|
|
use std::thread;
|
|
|
|
let data = Arc::new(b"concurrent test data".to_vec());
|
|
let password = Arc::new(b"concurrent_password".to_vec());
|
|
|
|
let handles: Vec<_> = (0..10).map(|i| {
|
|
let data = Arc::clone(&data);
|
|
let password = Arc::clone(&password);
|
|
|
|
thread::spawn(move || {
|
|
let encrypted = encrypt_data(&password, &data).expect("Encryption should succeed");
|
|
let decrypted = decrypt_data(&password, &encrypted).expect("Decryption should succeed");
|
|
assert_eq!(**data, decrypted, "Thread {} failed", i);
|
|
})
|
|
}).collect();
|
|
|
|
for handle in handles {
|
|
handle.join().expect("Thread should complete successfully");
|
|
}
|
|
|
|
Ok(())
|
|
}
|