Using Rust Cryptography Libraries: ring, sodiumoxide, and...
Learn to implement cryptographic operations in Rust using ring, sodiumoxide, and other cryptography libraries for secure applications.
Learn to implement cryptographic operations in Rust using production-ready libraries like ring, sodiumoxide, and others. Master encryption, hashing, digital signatures, and key management for secure applications.
Key Takeaways
- Cryptography Libraries: Understand Rust crypto ecosystem
- Encryption: Implement symmetric and asymmetric encryption
- Hashing: Use cryptographic hash functions
- Digital Signatures: Create and verify signatures
- Key Management: Handle cryptographic keys securely
- Best Practices: Security considerations for crypto code
Table of Contents
- Rust Cryptography Ecosystem
- Using ring Library
- Using sodiumoxide
- Hashing and MACs
- Digital Signatures
- Key Management
- Advanced Scenarios
- Troubleshooting Guide
- Real-World Case Study
- FAQ
- Conclusion
TL;DR
Master Rust cryptography libraries for secure applications. Learn encryption, hashing, signatures, and key management using ring, sodiumoxide, and other production-ready libraries.
Prerequisites
- Rust 1.80+ installed
- Understanding of basic cryptography concepts
- Familiarity with Rust error handling
Safety and Legal
- Use cryptography responsibly
- Follow export control laws
- Choose appropriate algorithms
- Validate cryptographic implementations
- Protect keys securely
Cryptography Threat Model (Essential Context)
Understanding what cryptography protects against—and what it doesn’t.
Before implementing cryptography, you must understand your threat model. Different threats require different defenses.
Threat Classification
| Threat Type | Description | Cryptographic Defense | Non-Crypto Defense |
|---|---|---|---|
| Passive Attacker | Eavesdrops on communication | Encryption (AES-GCM, ChaCha20-Poly1305) | Secure channels (TLS) |
| Active Attacker | Modifies messages in transit | Authenticated encryption (AEAD) | Message authentication |
| Local Attacker | Has access to your system | Key encryption, secure storage | OS permissions, HSM |
| Remote Attacker | Attacks over network | All of the above | Firewalls, rate limiting |
| Memory Disclosure | Reads process memory (e.g., Heartbleed) | Zeroization, constant-time ops | Memory protection, ASLR |
| Side-Channel Attacker | Timing, cache, power analysis | Constant-time algorithms | Hardware isolation |
Passive vs Active Attackers
Passive Attacker (Eavesdropper):
- Can: Read encrypted traffic
- Cannot: Modify traffic
- Defense: Encryption alone is sufficient (AES, ChaCha20)
- Example: ISP monitoring your traffic
Active Attacker (Man-in-the-Middle):
- Can: Read AND modify traffic
- Cannot: Break encryption (without key)
- Defense: Authenticated encryption (AEAD) required
- Example: Malicious WiFi access point
Key Difference:
// ❌ BAD: Encryption without authentication (passive attacker only)
let ciphertext = aes_encrypt(plaintext, key);
// Active attacker can modify ciphertext, causing decryption to succeed
// with corrupted plaintext (bit-flipping attack)
// ✅ GOOD: Authenticated encryption (defends against active attacker)
let ciphertext = aes_gcm_encrypt(plaintext, key, nonce);
// Active attacker cannot modify ciphertext without detection
// (authentication tag will fail)
Local vs Remote Attackers
Local Attacker:
- Has access to your system (malware, insider threat)
- Can read memory, files, environment variables
- Can debug your process
- Crypto alone cannot protect you
Defense:
- Encrypt keys at rest (with OS keychain or HSM)
- Zeroize secrets after use
- Use secure enclaves (SGX, TrustZone)
- Limit attack surface (sandboxing)
Remote Attacker:
- Attacks over network
- Cannot read your memory (unless memory disclosure bug)
- Crypto can protect you
Defense:
- Use TLS for transport security
- Validate all inputs
- Rate limit to prevent brute force
Memory Disclosure Risks
The Problem:
Even with perfect cryptography, memory disclosure vulnerabilities (like Heartbleed) can leak keys.
Threats:
- Buffer over-reads (Heartbleed)
- Use-after-free bugs
- Core dumps
- Swap files
- Hibernation images
Defenses:
- Zeroize secrets immediately after use:
use zeroize::Zeroize;
fn process_key(mut key: Vec<u8>) {
// Use key...
// ✅ Zeroize before dropping
key.zeroize();
}
- Use
ZeroizeOnDrop:
use zeroize::ZeroizeOnDrop;
#[derive(ZeroizeOnDrop)]
struct SecureKey {
key: Vec<u8>,
}
// ✅ Automatically zeroized when dropped
- Disable core dumps:
#[cfg(unix)]
fn disable_core_dumps() {
use libc::{setrlimit, rlimit, RLIMIT_CORE};
unsafe {
let limit = rlimit { rlim_cur: 0, rlim_max: 0 };
setrlimit(RLIMIT_CORE, &limit);
}
}
Side-Channel Risks
The Problem:
Cryptographic implementations can leak secrets through timing, cache access patterns, or power consumption.
Timing Side-Channels:
// ❌ BAD: Timing leak (early return on mismatch)
fn compare_hmac_insecure(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false; // ✅ This is OK (length is public)
}
for (x, y) in a.iter().zip(b.iter()) {
if x != y {
return false; // ❌ TIMING LEAK! Returns early
}
}
true
}
// ✅ GOOD: Constant-time comparison
use subtle::ConstantTimeEq;
fn compare_hmac_secure(a: &[u8], b: &[u8]) -> bool {
a.ct_eq(b).into()
}
Cache Side-Channels:
- AES T-table implementations leak key bits via cache timing
- Use hardware AES-NI instructions (constant-time)
- Or use ChaCha20 (no lookup tables)
Defenses:
- Use constant-time algorithms (ring, sodiumoxide do this)
- Use
subtlecrate for constant-time comparisons - Avoid branching on secret data
- Use hardware crypto instructions when available
Threat Model Decision Matrix
| Your Scenario | Threat Model | Recommended Approach |
|---|---|---|
| Storing passwords | Local attacker (database breach) | Argon2id (memory-hard, GPU-resistant) |
| Encrypting files | Local attacker (disk access) | AES-256-GCM with key from OS keychain |
| Secure messaging | Active network attacker | TLS + end-to-end encryption (Signal protocol) |
| API authentication | Active network attacker | HMAC-SHA256 with TLS |
| Protecting secrets in memory | Memory disclosure (Heartbleed) | Zeroization + constant-time ops |
| Preventing timing attacks | Side-channel attacker | Constant-time algorithms + subtle crate |
What Cryptography CANNOT Protect Against
Cryptography is not magic. It cannot protect against:
- ❌ Bugs in your code (buffer overflows, logic errors)
- ❌ Social engineering (phishing, pretexting)
- ❌ Physical access (rubber-hose cryptanalysis)
- ❌ Compromised endpoints (keyloggers, malware)
- ❌ Weak passwords (brute force, dictionary attacks)
- ❌ Implementation bugs (nonce reuse, weak RNG)
- ❌ Side-channels (unless using constant-time implementations)
Cryptography CAN protect against:
- ✅ Eavesdropping (passive network attacker)
- ✅ Tampering (active network attacker, with AEAD)
- ✅ Unauthorized access (with proper key management)
- ✅ Data breaches (if data is encrypted at rest)
Key Takeaway
Threat Model First, Cryptography Second
- Identify your threats (passive, active, local, remote, side-channel)
- Choose appropriate defenses (encryption, authentication, constant-time)
- Implement defense-in-depth (crypto + access control + monitoring)
- Test against your threat model (not just “does it work?”)
Critical Rule: If you don’t understand your threat model, you cannot choose the right cryptography.
Rust Cryptography Ecosystem
Popular Libraries
ring:
- Modern, safe cryptography
- Based on BoringSSL
- Excellent performance
- Well-maintained
- ✅ Constant-time implementations
- ✅ Hardware acceleration (AES-NI)
sodiumoxide:
- Rust bindings to libsodium
- Easy-to-use API
- Comprehensive features
- Good for beginners
- ⚠️ Maintenance risk: Wrapper around C library (libsodium must be updated separately)
- ✅ Misuse-resistant (hard to use incorrectly)
rustls:
- TLS implementation
- No C dependencies
- Memory-safe
- Modern protocols
- ✅ Pure Rust (no FFI risks)
argon2:
- Password hashing (Argon2id)
- Memory-hard (GPU-resistant)
- Winner of Password Hashing Competition
- ✅ Best choice for password hashing in 2026
🚨 NEVER USE These Algorithms (Critical)
Explicit bans to prevent catastrophic security failures.
The following algorithms are broken and must never be used in production code. Using them is a security vulnerability.
❌ NEVER USE: AES-ECB Mode
Why it’s broken:
- Identical plaintext blocks produce identical ciphertext blocks
- Reveals patterns in data (e.g., images, repeated text)
- No authentication (vulnerable to tampering)
Example of failure:
Plaintext: "HELLO HELLO HELLO"
AES-ECB: "X7$9k X7$9k X7$9k" ← Pattern visible!
AES-GCM: "a3$kL p9#mN q2@vB" ← No pattern
What to use instead:
- ✅ AES-GCM (authenticated encryption)
- ✅ ChaCha20-Poly1305 (authenticated encryption)
- ✅ AES-CBC with HMAC (if you must use CBC)
❌ NEVER USE: SHA-1
Why it’s broken:
- Collision attacks are practical (SHAttered attack, 2017)
- Deprecated by NIST, browsers, and major software
- Attackers can create two files with same SHA-1 hash
What to use instead:
- ✅ SHA-256 (minimum)
- ✅ SHA-512 (better)
- ✅ BLAKE3 (fastest, modern)
❌ NEVER USE: MD5
Why it’s broken:
- Completely broken (collisions found in 1996)
- Can generate collisions in seconds on modern hardware
- Used in Flame malware to forge Microsoft certificates
What to use instead:
- ✅ SHA-256 (minimum)
- ✅ SHA-512 (better)
- ✅ BLAKE3 (fastest, modern)
❌ NEVER USE: Raw RSA (Textbook RSA)
Why it’s broken:
- Deterministic (same plaintext → same ciphertext)
- Vulnerable to chosen-ciphertext attacks
- No padding = malleable ciphertext
What to use instead:
- ✅ RSA-OAEP (for encryption)
- ✅ RSA-PSS (for signatures)
- ✅ Ed25519 (better: faster, smaller keys, simpler)
❌ NEVER USE: DIY Key Derivation
Why it’s broken:
- Weak key derivation (e.g.,
SHA256(password)) is fast to brute force - GPUs can try billions of passwords per second
- No salt = rainbow table attacks
Examples of broken DIY:
// ❌ NEVER DO THIS
fn derive_key_insecure(password: &str) -> Vec<u8> {
use ring::digest;
digest::digest(&digest::SHA256, password.as_bytes())
.as_ref()
.to_vec()
// ❌ Fast to brute force (billions/sec on GPU)
// ❌ No salt (vulnerable to rainbow tables)
// ❌ No memory hardness (GPU-friendly)
}
What to use instead:
- ✅ Argon2id (best for passwords, memory-hard, GPU-resistant)
- ✅ PBKDF2 with ≥600,000 iterations (acceptable, but weaker than Argon2)
- ✅ scrypt (memory-hard, but Argon2 is better)
❌ NEVER USE: Custom Crypto
Why it’s broken:
- Cryptography is extremely hard to get right
- Subtle bugs lead to catastrophic failures
- Even experts make mistakes
Examples of what NOT to do:
// ❌ NEVER implement your own encryption
fn my_custom_cipher(data: &[u8], key: &[u8]) -> Vec<u8> {
data.iter().zip(key.iter().cycle())
.map(|(d, k)| d ^ k)
.collect()
}
// ❌ This is just XOR (broken, trivial to break)
// ❌ NEVER implement your own hash function
fn my_custom_hash(data: &[u8]) -> u64 {
data.iter().fold(0u64, |acc, &b| acc.wrapping_mul(31).wrapping_add(b as u64))
}
// ❌ This is not cryptographically secure
// ❌ NEVER implement your own random number generator
fn my_custom_rng() -> u64 {
use std::time::SystemTime;
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
.unwrap().as_nanos() as u64
}
// ❌ Predictable, not cryptographically secure
What to use instead:
- ✅ Use battle-tested libraries (ring, sodiumoxide, RustCrypto)
- ✅ Use standard algorithms (AES-GCM, ChaCha20-Poly1305, SHA-256)
- ✅ Use
ring::rand::SystemRandomfor RNG
Summary: Banned Algorithms
| Algorithm | Status | Reason | Replacement |
|---|---|---|---|
| AES-ECB | ❌ BANNED | Pattern leakage | AES-GCM, ChaCha20-Poly1305 |
| SHA-1 | ❌ BANNED | Collision attacks | SHA-256, SHA-512, BLAKE3 |
| MD5 | ❌ BANNED | Completely broken | SHA-256, SHA-512, BLAKE3 |
| Raw RSA | ❌ BANNED | No padding, malleable | RSA-OAEP, RSA-PSS, Ed25519 |
| DIY KDF | ❌ BANNED | Fast to brute force | Argon2id, PBKDF2 (≥600k iter) |
| Custom crypto | ❌ BANNED | Likely broken | Use standard libraries |
| DES, 3DES | ❌ BANNED | Weak (56-bit, 112-bit) | AES-256 |
| RC4 | ❌ BANNED | Biased keystream | ChaCha20 |
Key Takeaway
Critical Rule: If an algorithm is on this list, do NOT use it. Period. No exceptions. These are not “weak” or “deprecated”—they are broken.
Using ring Library
Setup
Add to Cargo.toml:
[dependencies]
ring = "0.17"
rand = "0.8" # For secure random number generation
🚨 CRITICAL: AEAD Nonce Management (Most Important Section)
⚠️ WARNING: The following is the #1 cause of catastrophic cryptographic failures.
AEAD (Authenticated Encryption with Associated Data) nonces must NEVER repeat with the same key.
What happens if you reuse a nonce:
- AES-GCM: Complete key recovery (attacker can decrypt ALL past and future messages)
- ChaCha20-Poly1305: Keystream reuse (attacker can XOR ciphertexts to get plaintext XOR)
This is not theoretical—it’s catastrophic and practical.
❌ DANGEROUS: Random Nonces (DO NOT USE FOR PRODUCTION)
// ❌ EDUCATIONAL ONLY - NOT SAFE FOR PRODUCTION
// This example is for learning ONLY. Do NOT use in real code.
use ring::aead;
use ring::rand::{SecureRandom, SystemRandom};
fn encrypt_data_UNSAFE_EXAMPLE(data: &[u8], key: &[u8]) -> Result<Vec<u8>, ring::error::Unspecified> {
let algorithm = &aead::AES_256_GCM;
let unbound_key = aead::UnboundKey::new(algorithm, key)?;
// ❌ DANGER: Random nonce
// - 12 bytes = 96 bits
// - Birthday paradox: ~50% collision after 2^48 messages (~281 trillion)
// - Sounds safe? It's NOT. Many systems send billions of messages.
// - One collision = complete key compromise
let rng = SystemRandom::new();
let mut nonce_bytes = [0u8; 12];
rng.fill(&mut nonce_bytes)?;
let nonce = aead::Nonce::assume_unique_for_key(nonce_bytes);
let mut sealing_key = aead::LessSafeKey::new(unbound_key);
let mut in_out = data.to_vec();
sealing_key.seal_in_place_append_tag(nonce, aead::Aad::empty(), &mut in_out)?;
// ⚠️ CRITICAL: You must store nonce with ciphertext!
// Format: [nonce (12 bytes) || ciphertext || tag (16 bytes)]
let mut result = nonce_bytes.to_vec();
result.extend_from_slice(&in_out);
Ok(result)
}
// ⚠️ This is NOT safe for:
// - High-volume systems (>2^32 messages)
// - Long-lived keys
// - Any production system where key compromise is unacceptable
✅ SAFE: Counter-Based Nonces (Recommended for Production)
use ring::aead;
use std::sync::atomic::{AtomicU64, Ordering};
pub struct SafeAEAD {
key: aead::LessSafeKey,
nonce_counter: AtomicU64,
}
impl SafeAEAD {
pub fn new(key_bytes: &[u8]) -> Result<Self, ring::error::Unspecified> {
let algorithm = &aead::AES_256_GCM;
let unbound_key = aead::UnboundKey::new(algorithm, key_bytes)?;
let key = aead::LessSafeKey::new(unbound_key);
Ok(Self {
key,
nonce_counter: AtomicU64::new(0),
})
}
pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, ring::error::Unspecified> {
// ✅ SAFE: Counter-based nonce (never repeats)
let counter = self.nonce_counter.fetch_add(1, Ordering::SeqCst);
// ⚠️ CRITICAL: Check for counter wraparound
if counter >= (1u64 << 48) {
// After 2^48 messages, rotate key!
panic!("Nonce counter exhausted - rotate key immediately!");
}
// Construct nonce: [counter (8 bytes) || zeros (4 bytes)]
let mut nonce_bytes = [0u8; 12];
nonce_bytes[..8].copy_from_slice(&counter.to_le_bytes());
let nonce = aead::Nonce::assume_unique_for_key(nonce_bytes);
let mut in_out = plaintext.to_vec();
self.key.seal_in_place_append_tag(nonce, aead::Aad::empty(), &mut in_out)?;
// Store nonce with ciphertext
let mut result = nonce_bytes.to_vec();
result.extend_from_slice(&in_out);
Ok(result)
}
pub fn decrypt(&self, ciphertext_with_nonce: &[u8]) -> Result<Vec<u8>, ring::error::Unspecified> {
if ciphertext_with_nonce.len() < 12 {
return Err(ring::error::Unspecified);
}
// Extract nonce
let nonce_bytes: [u8; 12] = ciphertext_with_nonce[..12].try_into()
.map_err(|_| ring::error::Unspecified)?;
let nonce = aead::Nonce::assume_unique_for_key(nonce_bytes);
// Extract ciphertext
let mut ciphertext = ciphertext_with_nonce[12..].to_vec();
// Decrypt
let plaintext = self.key.open_in_place(nonce, aead::Aad::empty(), &mut ciphertext)?;
Ok(plaintext.to_vec())
}
}
// ✅ Safe usage
fn safe_encryption_example() -> Result<(), ring::error::Unspecified> {
let key = [0u8; 32]; // In production: use secure key generation
let aead = SafeAEAD::new(&key)?;
let plaintext = b"Secret message";
let ciphertext = aead.encrypt(plaintext)?;
let decrypted = aead.decrypt(&ciphertext)?;
assert_eq!(plaintext, &decrypted[..]);
Ok(())
}
✅ SAFEST: Use libsodium’s secretbox (Recommended for Beginners)
For beginners, use sodiumoxide::crypto::secretbox instead of ring’s AEAD:
use sodiumoxide::crypto::secretbox;
// ✅ SAFEST: sodiumoxide handles nonces for you
fn encrypt_with_sodium(message: &[u8], key: &secretbox::Key) -> Vec<u8> {
// ✅ gen_nonce() generates a random nonce
// ✅ Nonce is automatically included in output
// ✅ Hard to misuse
let nonce = secretbox::gen_nonce();
secretbox::seal(message, &nonce, key)
}
fn decrypt_with_sodium(ciphertext: &[u8], key: &secretbox::Key) -> Option<Vec<u8>> {
// ✅ Nonce is extracted from ciphertext automatically
let nonce = secretbox::Nonce::from_slice(&ciphertext[..secretbox::NONCEBYTES])?;
secretbox::open(&ciphertext[secretbox::NONCEBYTES..], &nonce, key).ok()
}
Nonce Strategy Decision Matrix
| Scenario | Nonce Strategy | Safety | Complexity |
|---|---|---|---|
| Learning / prototyping | sodiumoxide::secretbox | ✅ Safest | Low |
| Low-volume (<10M messages) | Random nonces (with storage) | ⚠️ Acceptable | Medium |
| High-volume (>10M messages) | Counter-based nonces | ✅ Safe | Medium |
| Distributed systems | Counter + node ID | ✅ Safe | High |
| Long-lived keys | Counter-based + key rotation | ✅ Safest | High |
Key Rotation Strategy
After 2^48 messages (or 1 year), rotate keys:
pub struct RotatingAEAD {
current_key: SafeAEAD,
previous_key: Option<SafeAEAD>,
key_version: u32,
}
impl RotatingAEAD {
pub fn rotate_key(&mut self, new_key: &[u8]) -> Result<(), ring::error::Unspecified> {
let new_aead = SafeAEAD::new(new_key)?;
self.previous_key = Some(std::mem::replace(&mut self.current_key, new_aead));
self.key_version += 1;
Ok(())
}
pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, ring::error::Unspecified> {
let mut ciphertext = self.current_key.encrypt(plaintext)?;
// Prepend key version
let mut result = self.key_version.to_le_bytes().to_vec();
result.extend_from_slice(&ciphertext);
Ok(result)
}
pub fn decrypt(&self, ciphertext_with_version: &[u8]) -> Result<Vec<u8>, ring::error::Unspecified> {
if ciphertext_with_version.len() < 4 {
return Err(ring::error::Unspecified);
}
let version = u32::from_le_bytes(ciphertext_with_version[..4].try_into()
.map_err(|_| ring::error::Unspecified)?);
let ciphertext = &ciphertext_with_version[4..];
if version == self.key_version {
self.current_key.decrypt(ciphertext)
} else if let Some(ref prev_key) = self.previous_key {
prev_key.decrypt(ciphertext)
} else {
Err(ring::error::Unspecified)
}
}
}
Critical Takeaways
AEAD Nonce Rules:
- NEVER reuse a nonce with the same key (catastrophic failure)
- Use counter-based nonces for high-volume systems
- Use
sodiumoxide::secretboxif you’re unsure (safest for beginners) - Store nonces with ciphertext (they’re not secret, just unique)
- Rotate keys after 2^48 messages or 1 year
- Test nonce uniqueness in your implementation
Critical Rule: If you’re not 100% confident in your nonce management, use
sodiumoxide::secretboxinstead of ring’s AEAD. Nonce reuse is catastrophic.
Hashing
Click to view Rust code
use ring::digest;
fn hash_data(data: &[u8]) -> Vec<u8> {
let hash = digest::digest(&digest::SHA256, data);
hash.as_ref().to_vec()
}
Hashing
Click to view Rust code
use ring::digest;
fn hash_data(data: &[u8]) -> Vec<u8> {
let hash = digest::digest(&digest::SHA256, data);
hash.as_ref().to_vec()
}
Using sodiumoxide
Setup
Add to Cargo.toml:
[dependencies]
sodiumoxide = "0.2"
Initialization
Click to view Rust code
use sodiumoxide;
fn init_sodium() -> Result<(), ()> {
sodiumoxide::init()
}
Encryption
Click to view Rust code
use sodiumoxide::crypto::secretbox;
fn encrypt_with_sodium(message: &[u8], key: &secretbox::Key) -> (Vec<u8>, secretbox::Nonce) {
let nonce = secretbox::gen_nonce();
let ciphertext = secretbox::seal(message, &nonce, key);
(ciphertext, nonce)
}
Hashing and MACs
HMAC with ring
Click to view Rust code
use ring::{hmac, digest};
fn compute_hmac(key: &[u8], data: &[u8]) -> Vec<u8> {
let key = hmac::Key::new(hmac::HMAC_SHA256, key);
let tag = hmac::sign(&key, data);
tag.as_ref().to_vec()
}
Digital Signatures
Signing with ring
Click to view Rust code
use ring::{signature, rand};
fn generate_key_pair() -> (signature::Ed25519KeyPair, Vec<u8>) {
let rng = rand::SystemRandom::new();
let pkcs8 = signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
let key_pair = signature::Ed25519KeyPair::from_pkcs8(pkcs8.as_ref()).unwrap();
(key_pair, pkcs8.as_ref().to_vec())
}
fn sign_message(key_pair: &signature::Ed25519KeyPair, message: &[u8]) -> Vec<u8> {
key_pair.sign(message).as_ref().to_vec()
}
Key Management
Secure Key Storage
Click to view Rust code
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(ZeroizeOnDrop)]
pub struct SecureKey {
key: Vec<u8>,
}
impl SecureKey {
pub fn new(key: Vec<u8>) -> Self {
SecureKey { key }
}
pub fn as_slice(&self) -> &[u8] {
&self.key
}
}
Advanced Scenarios
Scenario 1: Password Hashing
Click to view Rust code
use ring::{digest, pbkdf2};
use std::num::NonZeroU32;
fn hash_password(password: &str, salt: &[u8]) -> Vec<u8> {
let mut output = [0u8; digest::SHA256_OUTPUT_LEN];
pbkdf2::derive(
pbkdf2::PBKDF2_HMAC_SHA256,
NonZeroU32::new(100_000).unwrap(),
salt,
password.as_bytes(),
&mut output,
);
output.to_vec()
}
Troubleshooting Guide
Problem: Compilation Errors
Solution:
- Check library versions
- Verify feature flags
- Review documentation
- Check platform support
Problem: Runtime Errors
Solution:
- Validate inputs
- Check key sizes
- Verify algorithm compatibility
- Review error messages
Real-World Case Study
Case Study: Secure communication system using Rust crypto
Implementation:
- ring for encryption and signatures
- sodiumoxide for key exchange
- Secure key management
- Proper error handling
Results:
- Strong security guarantees
- Good performance
- Easy to maintain
- No known vulnerabilities
FAQ
Q: Which library should I use?
A: Choose based on:
- ring: Modern, high-performance
- sodiumoxide: Easy-to-use, comprehensive
- rustls: TLS needs
- Consider your specific requirements
Q: How do I handle keys securely?
A:
- Use secure key storage
- Implement key rotation
- Protect keys in memory
- Use hardware security modules when possible
Code Review Checklist for Rust Cryptography
Library Selection
- Use well-audited crypto libraries (ring, rustls, etc.)
- Avoid deprecated crypto algorithms
- Check for security advisories
- Prefer maintained libraries
Key Management
- Keys never hardcoded or in logs
- Keys stored securely (HSM, KMS, etc.)
- Key rotation implemented
- Key derivation functions used properly
Implementation
- No custom crypto implementations
- Proper initialization vectors (IVs) used
- Authenticated encryption where needed
- Constant-time operations for sensitive comparisons
Testing
- Crypto operations have tests
- Test with known test vectors
- Test error conditions
- Test key generation and management
Security
- No side-channel vulnerabilities
- Secrets properly zeroized after use
- Proper random number generation
- Algorithm parameters properly configured
Conclusion
Rust’s cryptography libraries provide secure, performant cryptographic operations. Choose appropriate libraries and follow best practices for key management and secure implementation.
Action Steps
- Choose appropriate library
- Implement encryption/decryption
- Add hashing and signatures
- Implement key management
- Test thoroughly
Next Steps
- Explore advanced crypto protocols
- Study key management systems
- Learn about post-quantum crypto
- Practice with real applications
Related Topics
Remember: Cryptography is complex. Use well-tested libraries, follow best practices, and validate your implementations thoroughly.
Cleanup
Click to view commands
# Clean up crypto artifacts
rm -rf target/
rm -f *.key *.pem *.der *.cert
# Securely remove any test keys
find . -name "*test_key*" -exec shred -u {} \; 2>/dev/null || find . -name "*test_key*" -delete
Validation: Verify no cryptographic keys or certificates remain in the project directory.