#![cfg(feature = "encryption")]
mod common;
use cachekit_core::encryption::core::ZeroKnowledgeEncryptor;
use std::collections::HashSet;
use std::sync::{Arc, Barrier};
use std::thread;
const NUM_THREADS: usize = 1000; const ENCRYPTIONS_PER_THREAD: usize = 100;
#[test]
fn test_concurrent_nonce_uniqueness_basic() {
let encryptor = Arc::new(ZeroKnowledgeEncryptor::new().unwrap());
let key = [0x42u8; 32];
let plaintext = b"test data";
let aad = b"domain";
let ciphertexts = Arc::new(std::sync::Mutex::new(Vec::new()));
let mut handles = vec![];
for _ in 0..100 {
let encryptor = Arc::clone(&encryptor);
let ciphertexts = Arc::clone(&ciphertexts);
let handle = thread::spawn(move || {
for _ in 0..10 {
let ciphertext = encryptor
.encrypt_aes_gcm(plaintext, &key, aad)
.expect("Encryption should succeed");
let nonce = &ciphertext[..12];
ciphertexts.lock().unwrap().push(nonce.to_vec());
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("Thread should complete");
}
let ciphertexts = ciphertexts.lock().unwrap();
let unique_nonces: HashSet<Vec<u8>> = ciphertexts.iter().cloned().collect();
assert_eq!(
unique_nonces.len(),
ciphertexts.len(),
"All nonces must be unique (found {} duplicates out of {} total)",
ciphertexts.len() - unique_nonces.len(),
ciphertexts.len()
);
println!(
"✓ Basic concurrency: {} unique nonces from 100 threads × 10 encryptions",
unique_nonces.len()
);
}
#[test]
fn test_concurrent_nonce_stress_1000_threads() {
let encryptor = Arc::new(ZeroKnowledgeEncryptor::new().unwrap());
let key = [0x7fu8; 32];
let plaintext = b"stress test data";
let aad = b"stress_domain";
let barrier = Arc::new(Barrier::new(NUM_THREADS));
let nonces = Arc::new(std::sync::Mutex::new(Vec::with_capacity(
NUM_THREADS * ENCRYPTIONS_PER_THREAD,
)));
let mut handles = vec![];
println!(
"Starting stress test: {} threads × {} encryptions",
NUM_THREADS, ENCRYPTIONS_PER_THREAD
);
for _ in 0..NUM_THREADS {
let encryptor = Arc::clone(&encryptor);
let barrier = Arc::clone(&barrier);
let nonces = Arc::clone(&nonces);
let handle = thread::spawn(move || {
barrier.wait();
let mut local_nonces = Vec::with_capacity(ENCRYPTIONS_PER_THREAD);
for _ in 0..ENCRYPTIONS_PER_THREAD {
let ciphertext = encryptor
.encrypt_aes_gcm(plaintext, &key, aad)
.expect("Encryption should succeed");
let nonce = &ciphertext[..12];
local_nonces.push(nonce.to_vec());
}
nonces.lock().unwrap().extend(local_nonces);
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("Thread should complete");
}
let nonces = nonces.lock().unwrap();
let unique_nonces: HashSet<Vec<u8>> = nonces.iter().cloned().collect();
let expected_count = NUM_THREADS * ENCRYPTIONS_PER_THREAD;
assert_eq!(
unique_nonces.len(),
expected_count,
"All {} nonces must be unique (found {} duplicates)",
expected_count,
expected_count - unique_nonces.len()
);
println!(
"✓ Stress test passed: {} unique nonces from {} threads × {} encryptions",
unique_nonces.len(),
NUM_THREADS,
ENCRYPTIONS_PER_THREAD
);
}
#[test]
fn test_concurrent_multi_domain_nonce_uniqueness() {
let encryptor = Arc::new(ZeroKnowledgeEncryptor::new().unwrap());
let key = [0x9cu8; 32];
let plaintext = b"multi-domain data";
let domains: Vec<&[u8]> = vec![b"cache", b"session", b"authentication", b"api_tokens"];
let nonces = Arc::new(std::sync::Mutex::new(Vec::new()));
let mut handles = vec![];
for domain in domains {
for _ in 0..50 {
let encryptor = Arc::clone(&encryptor);
let nonces = Arc::clone(&nonces);
let handle = thread::spawn(move || {
let mut local_nonces = Vec::new();
for _ in 0..20 {
let ciphertext = encryptor
.encrypt_aes_gcm(plaintext, &key, domain)
.expect("Encryption should succeed");
let nonce = &ciphertext[..12];
local_nonces.push(nonce.to_vec());
}
nonces.lock().unwrap().extend(local_nonces);
});
handles.push(handle);
}
}
for handle in handles {
handle.join().expect("Thread should complete");
}
let nonces = nonces.lock().unwrap();
let unique_nonces: HashSet<Vec<u8>> = nonces.iter().cloned().collect();
let expected_count = 4 * 50 * 20;
assert_eq!(
unique_nonces.len(),
expected_count,
"Nonces must be unique across domains (found {} duplicates)",
expected_count - unique_nonces.len()
);
println!(
"✓ Multi-domain: {} unique nonces across 4 domains × 50 threads × 20 encryptions",
unique_nonces.len()
);
}
#[test]
fn test_concurrent_multi_tenant_nonce_uniqueness() {
let encryptor = Arc::new(ZeroKnowledgeEncryptor::new().unwrap());
let tenant_keys = vec![
[0xa1u8; 32],
[0xb2u8; 32],
[0xc3u8; 32],
[0xd4u8; 32],
[0xe5u8; 32],
];
let nonces = Arc::new(std::sync::Mutex::new(Vec::new()));
let mut handles = vec![];
for tenant_key in tenant_keys {
for _ in 0..40 {
let encryptor = Arc::clone(&encryptor);
let nonces = Arc::clone(&nonces);
let handle = thread::spawn(move || {
let plaintext = b"tenant data";
let aad = b"tenant_domain";
let mut local_nonces = Vec::new();
for _ in 0..25 {
let ciphertext = encryptor
.encrypt_aes_gcm(plaintext, &tenant_key, aad)
.expect("Encryption should succeed");
let nonce = &ciphertext[..12];
local_nonces.push(nonce.to_vec());
}
nonces.lock().unwrap().extend(local_nonces);
});
handles.push(handle);
}
}
for handle in handles {
handle.join().expect("Thread should complete");
}
let nonces = nonces.lock().unwrap();
let unique_nonces: HashSet<Vec<u8>> = nonces.iter().cloned().collect();
let expected_count = 5 * 40 * 25;
assert_eq!(
unique_nonces.len(),
expected_count,
"Nonces must be unique across tenants (found {} duplicates)",
expected_count - unique_nonces.len()
);
println!(
"✓ Multi-tenant: {} unique nonces across 5 tenants × 40 threads × 25 encryptions",
unique_nonces.len()
);
}
#[test]
fn test_nonce_counter_atomicity() {
let encryptor = Arc::new(ZeroKnowledgeEncryptor::new().unwrap());
let key = [0xffu8; 32];
let plaintext = b"counter test";
let aad = b"atomicity";
let num_threads = 100;
let increments_per_thread = 1000;
let expected_final_count = num_threads * increments_per_thread;
let mut handles = vec![];
for _ in 0..num_threads {
let encryptor = Arc::clone(&encryptor);
let handle = thread::spawn(move || {
for _ in 0..increments_per_thread {
let _ = encryptor
.encrypt_aes_gcm(plaintext, &key, aad)
.expect("Encryption should succeed");
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("Thread should complete");
}
let final_counter = encryptor.get_nonce_counter();
assert_eq!(
final_counter,
expected_final_count as u64,
"Counter atomicity failed: expected {} but got {} (lost {} updates)",
expected_final_count,
final_counter,
expected_final_count as i64 - final_counter as i64
);
println!(
"✓ Counter atomicity: {} threads × {} increments = {} final count",
num_threads, increments_per_thread, final_counter
);
}
#[test]
fn test_nonce_format_consistency() {
let encryptor = Arc::new(ZeroKnowledgeEncryptor::new().unwrap());
let key = [0x33u8; 32];
let plaintext = b"format test";
let aad = b"format_check";
let nonces = Arc::new(std::sync::Mutex::new(Vec::new()));
let mut handles = vec![];
for _ in 0..50 {
let encryptor = Arc::clone(&encryptor);
let nonces = Arc::clone(&nonces);
let handle = thread::spawn(move || {
let mut local_nonces = Vec::new();
for _ in 0..100 {
let ciphertext = encryptor
.encrypt_aes_gcm(plaintext, &key, aad)
.expect("Encryption should succeed");
let nonce = &ciphertext[..12];
local_nonces.push(nonce.to_vec());
}
nonces.lock().unwrap().extend(local_nonces);
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("Thread should complete");
}
let nonces = nonces.lock().unwrap();
assert!(!nonces.is_empty(), "Should have generated nonces");
let first_nonce = &nonces[0];
let instance_id_bytes = &first_nonce[..8];
for nonce in nonces.iter() {
assert_eq!(nonce.len(), 12, "Nonce must be exactly 12 bytes");
assert_eq!(
&nonce[..8],
instance_id_bytes,
"Instance ID must be consistent across all nonces from same encryptor"
);
}
let mut counters: Vec<u32> = nonces
.iter()
.map(|nonce| u32::from_be_bytes([nonce[8], nonce[9], nonce[10], nonce[11]]))
.collect();
counters.sort_unstable();
for (i, &counter) in counters.iter().enumerate() {
assert_eq!(
counter, i as u32,
"Counter sequence broken at index {} (expected {}, got {})",
i, i, counter
);
}
println!(
"✓ Format consistency: All {} nonces have correct [iv(8)][counter(4)] format",
nonces.len()
);
}