Security Quick Reference Guide
Side-channel claim limit. Nothing on this page claims constant-time behaviour or side-channel resistance for any target: the side-channel harness covers nothing (finding A48) and no measurement exists. The Rust and C cores are designed for constant time and unmeasured; managed runtimes (JIT, GC, bounds checks) make no timing claim. Where this page names constant-time or side-channel techniques, in prose or in code, they are design intent or recommendations, not measured properties.
๐จ Emergency Contacts
- Security Incidents: incident@metamui.id (24/7)
- Vulnerability Reports: security@metamui.id
- On-Call Security: +1-XXX-XXX-XXXX
๐ Key Security Rules
NEVER
- โ Use
==for comparing secrets (use constant-time comparison) - โ Log or print secret keys
- โ Reuse nonces with the same key
- โ Use
unwrap()in production code - โ Store keys in plaintext
- โ Use non-approved cryptographic algorithms
- โ Ignore compiler warnings about unsafe code
ALWAYS
- โ Validate all inputs before processing
- โ Clear sensitive data from memory after use
- โ Use FIPS-approved RNG for key generation
- โ Handle errors gracefully without leaking information
- โ Run security tests before committing
- โ Get security review for cryptographic changes
- โ Update dependencies regularly
๐ก๏ธ Secure Coding Patterns
Input Validation
// โ
GOOD
pub fn process_data(input: &[u8]) -> Result<Vec<u8>, Error> {
// Validate input length
if input.len() > MAX_INPUT_SIZE {
return Err(Error::InputTooLarge);
}
// Validate input format
validate_format(input)?;
// Process safely
Ok(process_validated(input))
}
// โ BAD
pub fn process_data(input: &[u8]) -> Vec<u8> {
process_validated(input) // No validation!
}
Constant-Time Operations
// โ
GOOD - Constant time comparison
use subtle::ConstantTimeEq;
pub fn verify_tag(expected: &[u8; 16], actual: &[u8; 16]) -> bool {
expected.ct_eq(actual).unwrap_u8() == 1
}
// โ BAD - Variable time comparison
pub fn verify_tag(expected: &[u8; 16], actual: &[u8; 16]) -> bool {
expected == actual // Timing leak!
}
Memory Safety
// โ
GOOD - Secure memory handling
use zeroize::Zeroize;
pub fn handle_secret(secret: &[u8]) -> Result<(), Error> {
let mut temp = secret.to_vec();
// Use secret
let result = process_secret(&temp)?;
// Clear from memory
temp.zeroize();
Ok(result)
}
// โ BAD - Memory not cleared
pub fn handle_secret(secret: &[u8]) -> Result<(), Error> {
let temp = secret.to_vec();
process_secret(&temp) // Secret remains in memory!
}
Error Handling
// โ
GOOD - Safe error handling
pub fn decrypt(ciphertext: &[u8], key: &[u8]) -> Result<Vec<u8>, Error> {
if key.len() != 32 {
return Err(Error::InvalidKeySize); // Generic error
}
decrypt_internal(ciphertext, key)
.map_err(|_| Error::DecryptionFailed) // Don't leak details
}
// โ BAD - Information leakage
pub fn decrypt(ciphertext: &[u8], key: &[u8]) -> Result<Vec<u8>, Error> {
decrypt_internal(ciphertext, key) // May leak padding oracle!
}
๐ Cryptographic Guidelines
Algorithm Selection
| Use Case | Recommended | Avoid |
|---|---|---|
| Symmetric Encryption | AES-256-GCM, ChaCha20-Poly1305 | DES, 3DES, RC4 |
| Hashing | SHA-256, SHA-512, BLAKE2 | MD5, SHA-1 |
| Digital Signatures | Ed25519, ML-DSA (Dilithium) | RSA < 2048, ECDSA < 256 |
| Key Exchange | X25519, ML-KEM-768 | DH < 2048, RSA |
| Key Derivation | HKDF, Argon2 | PBKDF2 < 100k iterations |
Key Sizes
| Algorithm | Minimum Size | Recommended |
|---|---|---|
| AES | 128 bits | 256 bits |
| RSA | 2048 bits | 4096 bits |
| ECDSA | 256 bits | 384 bits |
| Ed25519 | 256 bits | 256 bits |
| ChaCha20 | 256 bits | 256 bits |
Nonce/IV Requirements
| Algorithm | Nonce Size | Requirements |
|---|---|---|
| AES-GCM | 96 bits | Never reuse with same key |
| ChaCha20-Poly1305 | 96 bits | Never reuse with same key |
| XChaCha20-Poly1305 | 192 bits | Random is safe |
| AES-CTR | 128 bits | Never reuse with same key |
๐งช Security Testing Checklist
Before Committing
- Run
cargo clippy- No warnings - Run
cargo test- All tests pass - Run
cargo audit- No vulnerabilities - Check for
unsafeblocks - All justified - Review error messages - No information leaks
- Check for hardcoded secrets - None present
Security-Specific Tests
# Run security tests
cargo test --features security-tests
# Run fuzzing (if available)
cargo fuzz run crypto_fuzz -- -max_total_time=300
# Check for unsafe code
grep -r "unsafe" src/
# Scan for hardcoded secrets
grep -r -E "(password|secret|key)\s*=\s*\"" src/
# Check dependencies
cargo audit
๐ Security Metrics
Key Rotation Schedule
- Master Keys: Annually
- Encryption Keys: Monthly
- Signing Keys: Quarterly
- Session Keys: Daily
- Nonces: Never reuse
Response Times
- Critical Security Issue: 15 minutes
- High Security Issue: 1 hour
- Medium Security Issue: 24 hours
- Low Security Issue: 1 week
๐ Quick Deployment Checklist
Pre-Deployment
- Security tests pass
- No known vulnerabilities
- Keys properly configured
- Audit logging enabled
- Monitoring configured
- Backup procedures tested
Post-Deployment
- Verify security logs
- Check performance metrics
- Monitor for anomalies
- Test incident response
- Document any issues
๐ Essential Security Commands
# Generate secure random key
openssl rand -hex 32
# Check certificate
openssl x509 -in cert.pem -text -noout
# Verify file integrity
sha256sum -c checksums.txt
# Scan for secrets
git secrets --scan
# Check TLS configuration
testssl.sh https://api.example.com
# Audit dependencies
cargo audit
# Run security linter
cargo clippy -- -D warnings
# Memory safety check
cargo miri test
๐ Common Security Issues
Issue: Timing Attack
Symptom: Operations take different time based on secret data Fix: Use constant-time operations
// Use subtle crate for constant-time operations
use subtle::{ConstantTimeEq, ConditionallySelectable};
Issue: Memory Leak of Secrets
Symptom: Secrets remain in memory after use Fix: Use Zeroize trait
use zeroize::Zeroize;
let mut secret = get_secret();
// ... use secret ...
secret.zeroize();
Issue: Weak Randomness
Symptom: Predictable random values Fix: Use cryptographic RNG
use rand::rngs::OsRng;
let mut rng = OsRng;
Issue: Nonce Reuse
Symptom: Same nonce used with same key Fix: Use nonce manager
let nonce = nonce_manager.generate_nonce(key_id)?;
๐ References
- OWASP Cryptographic Storage Cheat Sheet
- Rust Security Guidelines
- NIST Cryptographic Standards
- RustCrypto Project
Remember: When in doubt, ask the security team!