use crabgraph::{
aead::stream::{
Aes256GcmStreamDecryptor, Aes256GcmStreamEncryptor, ChaCha20Poly1305StreamDecryptor,
ChaCha20Poly1305StreamEncryptor,
},
rand::secure_bytes,
CrabResult,
};
fn basic_aes_gcm_streaming() -> CrabResult<()> {
println!("=== Example 1: Basic AES-256-GCM Streaming ===\n");
let key = secure_bytes(32)?;
println!("Generated 32-byte key");
let mut encryptor = Aes256GcmStreamEncryptor::new(&key)?;
let nonce = encryptor.nonce().to_vec(); println!("Created encryptor with nonce: {} bytes", nonce.len());
let chunks = vec![
b"First chunk of data...".to_vec(),
b"Second chunk of data...".to_vec(),
b"Third chunk of data...".to_vec(),
b"Final chunk!".to_vec(),
];
println!("\nEncrypting {} chunks:", chunks.len());
let mut encrypted_chunks = Vec::new();
for (i, chunk) in chunks[..chunks.len() - 1].iter().enumerate() {
let encrypted = encryptor.encrypt_next(chunk)?;
println!(" Chunk {}: {} bytes -> {} bytes", i + 1, chunk.len(), encrypted.len());
encrypted_chunks.push(encrypted);
}
let last_chunk = &chunks[chunks.len() - 1];
let encrypted_last = encryptor.encrypt_last(last_chunk)?;
println!(
" Chunk {} (final): {} bytes -> {} bytes",
chunks.len(),
last_chunk.len(),
encrypted_last.len()
);
encrypted_chunks.push(encrypted_last);
println!("\nDecrypting {} chunks:", encrypted_chunks.len());
let mut decryptor = Aes256GcmStreamDecryptor::from_nonce(&key, &nonce)?;
let mut decrypted_chunks = Vec::new();
for (i, encrypted) in encrypted_chunks[..encrypted_chunks.len() - 1].iter().enumerate() {
let decrypted = decryptor.decrypt_next(encrypted)?;
println!(" Chunk {}: {} bytes -> {} bytes", i + 1, encrypted.len(), decrypted.len());
decrypted_chunks.push(decrypted);
}
let last_encrypted = &encrypted_chunks[encrypted_chunks.len() - 1];
let decrypted_last = decryptor.decrypt_last(last_encrypted)?;
println!(
" Chunk {} (final): {} bytes -> {} bytes",
encrypted_chunks.len(),
last_encrypted.len(),
decrypted_last.len()
);
decrypted_chunks.push(decrypted_last);
assert_eq!(decrypted_chunks, chunks);
println!("\n✓ Roundtrip successful! All chunks match.");
Ok(())
}
fn chacha20poly1305_streaming() -> CrabResult<()> {
println!("\n=== Example 2: ChaCha20-Poly1305 Streaming ===\n");
let key = secure_bytes(32)?;
println!("Generated 32-byte key");
let mut encryptor = ChaCha20Poly1305StreamEncryptor::new(&key)?;
let nonce = encryptor.nonce().to_vec();
println!("Created ChaCha20-Poly1305 encryptor");
let data = b"This is a simulated large file that we're processing chunk by chunk.";
let chunk_size = 20;
println!("\nEncrypting data in {}-byte chunks:", chunk_size);
let mut encrypted_chunks = Vec::new();
let chunks: Vec<&[u8]> = data.chunks(chunk_size).collect();
for (i, chunk) in chunks[..chunks.len() - 1].iter().enumerate() {
let encrypted = encryptor.encrypt_next(chunk)?;
println!(" Chunk {}: {} bytes", i + 1, chunk.len());
encrypted_chunks.push(encrypted);
}
let last_chunk = chunks[chunks.len() - 1];
let encrypted_last = encryptor.encrypt_last(last_chunk)?;
println!(" Chunk {} (final): {} bytes", chunks.len(), last_chunk.len());
encrypted_chunks.push(encrypted_last);
println!("\nDecrypting {} chunks:", encrypted_chunks.len());
let mut decryptor = ChaCha20Poly1305StreamDecryptor::from_nonce(&key, &nonce)?;
let mut decrypted_data = Vec::new();
for (i, encrypted) in encrypted_chunks[..encrypted_chunks.len() - 1].iter().enumerate() {
let decrypted = decryptor.decrypt_next(encrypted)?;
println!(" Chunk {}: {} bytes", i + 1, encrypted.len());
decrypted_data.extend_from_slice(&decrypted);
}
let last_encrypted = &encrypted_chunks[encrypted_chunks.len() - 1];
let decrypted_last = decryptor.decrypt_last(last_encrypted)?;
println!(" Chunk {} (final): {} bytes", encrypted_chunks.len(), last_encrypted.len());
decrypted_data.extend_from_slice(&decrypted_last);
assert_eq!(decrypted_data, data);
println!("\n✓ Roundtrip successful! Original data restored.");
Ok(())
}
fn file_encryption_simulation() -> CrabResult<()> {
println!("\n=== Example 3: File Encryption Simulation ===\n");
let key = secure_bytes(32)?;
let mut encryptor = Aes256GcmStreamEncryptor::new(&key)?;
let nonce = encryptor.nonce().to_vec();
let filename = "secret_document.txt";
let file_data = b"This is the content of the secret document.\n\
It contains multiple lines of sensitive information.\n\
We're encrypting it chunk by chunk for efficiency.";
println!("Encrypting file: {}", filename);
println!("File size: {} bytes", file_data.len());
println!("Nonce: {} bytes", nonce.len());
const CHUNK_SIZE: usize = 64 * 1024;
println!("\nProcessing in {}-byte chunks:", CHUNK_SIZE);
let mut encrypted_chunks = Vec::new();
let chunks: Vec<&[u8]> = file_data.chunks(CHUNK_SIZE).collect();
for (i, chunk) in chunks[..chunks.len() - 1].iter().enumerate() {
let encrypted = encryptor.encrypt_next(chunk)?;
println!(" Chunk {}: {} bytes", i + 1, chunk.len());
encrypted_chunks.push(encrypted);
}
let last_chunk = chunks[chunks.len() - 1];
let encrypted_last = encryptor.encrypt_last(last_chunk)?;
println!(" Final chunk: {} bytes", last_chunk.len());
encrypted_chunks.push(encrypted_last);
let total_encrypted_size: usize = encrypted_chunks.iter().map(|c| c.len()).sum();
println!("\nTotal encrypted size: {} bytes", total_encrypted_size);
println!(
"Overhead: {} bytes ({}%)",
total_encrypted_size as i32 - file_data.len() as i32,
((total_encrypted_size as f64 / file_data.len() as f64 - 1.0) * 100.0) as i32
);
println!("\nDecrypting file...");
let mut decryptor = Aes256GcmStreamDecryptor::from_nonce(&key, &nonce)?;
let mut decrypted_data = Vec::new();
for encrypted in encrypted_chunks[..encrypted_chunks.len() - 1].iter() {
let decrypted = decryptor.decrypt_next(encrypted)?;
decrypted_data.extend_from_slice(&decrypted);
}
let last_encrypted = &encrypted_chunks[encrypted_chunks.len() - 1];
let decrypted_last = decryptor.decrypt_last(last_encrypted)?;
decrypted_data.extend_from_slice(&decrypted_last);
assert_eq!(decrypted_data, file_data);
println!("✓ File decrypted successfully! Content matches original.");
Ok(())
}
fn tampering_detection() -> CrabResult<()> {
println!("\n=== Example 4: Tampering Detection ===\n");
let key = secure_bytes(32)?;
let mut encryptor = Aes256GcmStreamEncryptor::new(&key)?;
let nonce = encryptor.nonce().to_vec();
let chunk1 = b"Original data chunk 1";
let chunk2 = b"Original data chunk 2";
let encrypted1 = encryptor.encrypt_next(chunk1)?;
let mut encrypted2 = encryptor.encrypt_last(chunk2)?;
println!("Encrypted 2 chunks");
println!("\nTampering with encrypted data...");
if !encrypted2.is_empty() {
encrypted2[0] ^= 0xFF; }
println!("Attempting to decrypt tampered data:");
let mut decryptor = Aes256GcmStreamDecryptor::from_nonce(&key, &nonce)?;
let decrypted1 = decryptor.decrypt_next(&encrypted1)?;
assert_eq!(decrypted1, chunk1);
println!(" Chunk 1: ✓ Decrypted successfully");
match decryptor.decrypt_last(&encrypted2) {
Ok(_) => {
println!(" Chunk 2: ✗ ERROR - Tampered data was accepted!");
panic!("Security failure: tampered data accepted");
}
Err(e) => {
println!(" Chunk 2: ✓ Tampering detected!");
println!(" Error: {}", e);
}
}
println!("\n✓ Authentication working correctly!");
Ok(())
}
fn main() -> CrabResult<()> {
println!("CrabGraph Streaming Encryption Examples\n");
println!("========================================\n");
basic_aes_gcm_streaming()?;
chacha20poly1305_streaming()?;
file_encryption_simulation()?;
tampering_detection()?;
println!("\n========================================");
println!("All examples completed successfully! ✓");
Ok(())
}