Symmetric Cryptography (symmetric)
The dcrypt-symmetric crate provides a high-level, secure, and easy-to-use API for common symmetric encryption algorithms within the dcrypt ecosystem. It is built upon the low-level cryptographic primitives in dcrypt-algorithms and integrates with dcrypt-api for a unified and robust error-handling system.
This crate is designed for both ease of use in common scenarios and the flexibility required for more complex applications, such as streaming large files.
## Features
- Modern AEAD Ciphers: Provides implementations for industry-standard Authenticated Encryption with Associated Data (AEAD) ciphers:
- ChaCha20-Poly1305: As specified in RFC 8439.
- XChaCha20-Poly1305: An extended nonce variant, ideal for applications requiring a large number of random nonces.
- AES-128-GCM & AES-256-GCM: NIST-approved standard for high-performance, authenticated encryption.
- Secure Key Management:
- Secure random key generation.
- Key derivation from passwords using PBKDF2-HMAC-SHA256.
- Keys are wrapped in types that implement
Zeroize to securely clear them from memory on drop.
- Safe serialization format for storing and transmitting keys.
- Convenient Data Handling:
- Ciphertext Packages: An easy-to-use format that bundles the nonce and ciphertext together, simplifying storage and transmission.
- Base64-encoded string representation for packages and keys.
- Streaming API:
- Memory-efficient streaming encryption and decryption for large files or data streams.
- Handles nonce management automatically and securely across data chunks.
- Unified Error System:
- Leverages the
dcrypt-api error system for consistent and descriptive error handling across the entire dcrypt library stack.
no_std Compatibility: Core features are available in no_std environments.
## Installation
Add the crate to your Cargo.toml file:
[dependencies]
dcrypt-symmetric = "0.12.0-beta.1"
Or add it via the command line:
cargo add dcrypt-symmetric
## Usage Examples
### Basic Encryption & Decryption (AES-256-GCM)
This example shows a simple encrypt/decrypt cycle using Aes256Gcm.
use dcrypt::symmetric::{Aes256Gcm, Aes256Key, Aead, SymmetricCipher, Result};
fn main() -> Result<()> {
let key = Aes256Key::generate();
let cipher = Aes256Gcm::new(&key)?;
let plaintext = b"this is a very secret message";
let associated_data = b"metadata";
let nonce = Aes256Gcm::generate_nonce();
println!("Encrypting: '{}'", String::from_utf8_lossy(plaintext));
let ciphertext = cipher.encrypt(&nonce, plaintext, Some(associated_data))?;
println!("Ciphertext (hex): {}", hex::encode(&ciphertext));
let decrypted_plaintext = cipher.decrypt(&nonce, &ciphertext, Some(associated_data))?;
println!("Decrypted: '{}'", String::from_utf8_lossy(&decrypted_plaintext));
assert_eq!(plaintext, &decrypted_plaintext[..]);
let wrong_key = Aes256Key::generate();
let wrong_cipher = Aes256Gcm::new(&wrong_key)?;
assert!(wrong_cipher.decrypt(&nonce, &ciphertext, Some(associated_data)).is_err());
println!("Decryption with wrong key failed as expected.");
Ok(())
}
### Packaged Encryption (ChaCha20Poly1305)
The library provides a convenient package format that bundles the nonce with the ciphertext, making it easy to store or transmit.
use dcrypt::symmetric::{
ChaCha20Poly1305Cipher,
ChaCha20Poly1305Key,
ChaCha20Poly1305CiphertextPackage,
SymmetricCipher,
Result
};
fn main() -> Result<()> {
let (cipher, key) = ChaCha20Poly1305Cipher::generate()?;
let plaintext = b"data packaged for transport";
let package = cipher.encrypt_to_package(plaintext, None)?;
let serialized_package = package.to_string();
println!("Serialized Package: {}", serialized_package);
let receiving_cipher = ChaCha20Poly1305Cipher::new(&key)?;
let parsed_package = ChaCha20Poly1305CiphertextPackage::from_string(&serialized_package)?;
let decrypted_plaintext = receiving_cipher.decrypt_package(&parsed_package, None)?;
println!("Decrypted from package: '{}'", String::from_utf8_lossy(&decrypted_plaintext));
assert_eq!(plaintext, &decrypted_plaintext[..]);
Ok(())
}
### Key Derivation from a Password
Derive a strong cryptographic key from a user-provided password using PBKDF2.
use dcrypt::symmetric::{aes::derive_aes128_key, aes::generate_salt, Result};
fn main() -> Result<()> {
let password = b"a-very-secure-password-123";
let salt = generate_salt(16);
let iterations = 250_000;
let derived_key = derive_aes128_key(password, &salt, iterations)?;
println!("Successfully derived AES-128 key from password.");
Ok(())
}
### Streaming File Encryption
For large files, the streaming API encrypts and decrypts data in chunks, keeping memory usage low. This example uses std::io::Cursor to simulate file I/O.
use std::io::Cursor;
use dcrypt::symmetric::{
streaming::chacha20poly1305::{ChaCha20Poly1305EncryptStream, ChaCha20Poly1305DecryptStream},
streaming::{StreamingEncrypt, StreamingDecrypt},
ChaCha20Poly1305Key,
Result
};
fn main() -> Result<()> {
let key = ChaCha20Poly1305Key::generate();
let associated_data = b"streaming-file-example";
let source_data = b"This is the first part of a very large file. ".repeat(1000);
let mut source = Cursor::new(source_data);
let mut encrypted_dest = Vec::new();
{
let mut encrypt_stream = ChaCha20Poly1305EncryptStream::new(
&mut encrypted_dest,
&key,
Some(associated_data),
)?;
std::io::copy(&mut source, &mut encrypt_stream)?;
}
println!("Original size: {} bytes", source.get_ref().len());
println!("Encrypted size: {} bytes", encrypted_dest.len());
let mut encrypted_source = Cursor::new(encrypted_dest);
let mut decrypt_stream = ChaCha20Poly1305DecryptStream::new(
&mut encrypted_source,
&key,
Some(associated_data),
)?;
let mut decrypted_data = Vec::new();
decrypt_stream.read_to_end(&mut decrypted_data)?;
println!("Decrypted size: {} bytes", decrypted_data.len());
assert_eq!(source.get_ref(), &decrypted_data);
println!("Successfully encrypted and decrypted stream!");
Ok(())
}
## License
This crate is licensed under the Apache 2.0 License.