API Keys Simplified
A secure, Rust library for generating and validating API keys with built-in security best practices.
What It Does
- Generate cryptographically secure API keys (192-bit entropy default)
- Hash keys using Argon2id (memory-hard, OWASP recommended)
- Checksum keys with BLAKE3 for fast DoS protection (2900x speedup)
- Verify keys with constant-time comparison (prevents timing attacks)
- Protect sensitive data with automatic memory zeroing
- Expire keys automatically based on embedded timestamps
- Revoke keys instantly by marking hashes as invalid
Quick Start
Basic Usage
use ;
Key Format
prefix[-version]-environment-random_data[.expiry][.checksum]
│ │ │ │ │ │
│ │ │ │ │ └─ BLAKE3 (recommended, 16 hex chars)
│ │ │ │ └─────────── Optional: 11-char base64url timestamp
│ │ │ └─────────────────── Base64URL (192 bits default)
│ │ └──────────────────────────────── dev/test/staging/live
│ └──────────────────────────────────────────── Optional: vN (v1, v2, etc.)
└────────────────────────────────────────────────── User-defined (e.g., acme_sk, stripe_pk)
Examples:
- Unversioned (default):
gpmcp_sk-live-Xf8kP2qW9zLmN4vC8aH5tJw1bQmK3rN9.a1b2c3d4e5f6g7h8 - With version:
gpmcp_sk-v1-live-Xf8kP2qW9zLmN4vC8aH5tJw1bQmK3rN9.a1b2c3d4e5f6g7h8 - With expiry:
acme_api-dev-Rt7jK3pV8wNmQ2uD4fG6hLk8nPqS2uW5.AAAAAGldxGE.9f8e7d6c5b4a3210 - Full format:
api-v2-live-Rt7jK3pV8wNmQ2uD4fG6hLk8nPqS2uW5.AAAAAGldxGE.9f8e7d6c5b4a3210
Checksum provides:
- 2900x faster rejection of invalid keys
- DoS protection against malformed requests
- Integrity verification before expensive Argon2
Expiration provides:
- Time-based access control (trial keys, temporary access)
- Stateless expiry (no database cleanup needed)
- Automatic rejection after timestamp
Versioning provides:
- Gradual migration between key formats
- Clear identification of key format version
- Backward compatibility (version 0 = unversioned)
- Future-proof format evolution
Why Use This?
Common API key security mistakes:
❌ Weak random number generators → Predictable keys
❌ Plaintext storage → Database breach = total compromise
❌ Vulnerable hashing (MD5, SHA1) → Easy to crack
❌ Timing-vulnerable comparisons → Leaks key information
❌ Keys lingering in memory → Core dumps expose secrets
This library solves all of these with secure defaults and minimal code.
Security Features
🔒 Cryptographic Strength
- RNG: OS-level CSPRNG via
getrandomcrate - Hashing: Argon2id (Password Hashing Competition winner)
- Entropy: 192 bits default (NIST compliant through 2030+)
- Memory-Hard: Prevents GPU/ASIC brute force attacks
🛡️ Side-Channel Protection
- Constant-Time Comparison: Via
subtlecrate (timing-attack resistant) - No Early Returns: Same verification time regardless of key differences
- Memory Hardness: Argon2 prevents cache-timing attacks
🔐 Memory Safety
- Auto-Zeroing:
SecureStringclears memory on drop viazeroizecrate - No Accidental Logging: Custom
Debugimpl redacts keys - Explicit Access: No
Dereftrait (prevents silent leaks)
📊 DoS Protection
- BLAKE3 Checksums: Invalid keys rejected in ~20μs (vs ~300ms Argon2)
- 2900x Speedup: Dramatically reduces DoS attack surface
- Input Validation: 512-byte max key length
- Resource Limits: Prevents hash complexity attacks
Performance Comparison (10 invalid keys):
- ✅ With checksum: 0ms (fast rejection)
- ❌ Without checksum: 2907ms (all Argon2)
🔍 Threat Model
Protected Against: ✅ Brute force • ✅ Timing attacks • ✅ Rainbow tables • ✅ Memory disclosure • ✅ Database breaches • ✅ GPU/ASIC attacks
NOT Protected Against: ❌ Compromised app server • ❌ User negligence • ❌ Network interception (use HTTPS) • ❌ Quantum computers
Best Practices
use ;
use ;
// ✅ Checksums enabled by default (DoS protection - use .disable_checksum() to turn off)
let manager = init_default_config?;
// ✅ Never log keys (auto-redacted)
let key = manager.generate?;
println!; // Prints: ApiKey { key: "[REDACTED]", ... }
// ✅ Show keys only once
display_to_user_once;
db.save; // Store hash only
// ✅ Always use HTTPS
let response = client.get
.header
.send?;
// ✅ Implement key rotation
// ✅ Use expiration for temporary access (trials, partners)
let trial_expiry = now + days;
let trial_key = manager.generate_with_expiry?;
db.save?;
// ✅ Implement key revocation for compromised keys
// ✅ Check revocation status during verification
// ✅ Rate limit verification (still important with checksums)
if rate_limiter.check.is_err
// Convert incoming string to SecureString
let incoming_key = from;
manager.verify?;
Performance
| Preset | Memory | Time | Verification |
|---|---|---|---|
| Balanced (default) | 19 MB | 2 iter | ~50ms |
| High Security | 64 MB | 3 iter | ~150ms |
Note: Slow verification is intentional—it prevents brute force attacks.
Testing
Error Handling
use ;
match init_default_config
Error messages are intentionally generic to prevent information leakage.
Comparison
| Feature | api-keys-simplified | uuid | nanoid |
|---|---|---|---|
| Cryptographic security | ✅ Argon2id | ❌ | ⚠️ Basic |
| Hashed storage | ✅ Built-in | ❌ | ❌ |
| Constant-time verify | ✅ Yes | ❌ | ❌ |
| Memory protection | ✅ Auto-zeroing | ❌ | ❌ |
| Structured format | ✅ prefix.env.data | ❌ | ❌ |
License
Licensed under the Apache License, Version 2.0.
Dependencies
All cryptographic implementations use well-audited crates:
argon2- Official Argon2 implementationsubtle- Constant-time primitiveszeroize- Secure memory zeroinggetrandom- OS-level CSPRNG
References
- OWASP Authentication Cheat Sheet
- NIST SP 800-63B - Digital Identity Guidelines
- RFC 9106 - Argon2 Specification
Reporting Vulnerabilities
Email security issues to: sandip@ssdd.dev
Progress
- Key expiration support
- Key versioning
- Key rotation
- Fix timing attack in dummy_load
- Zero all intermediate string allocations
- Switch to ZII or a hybrid (ZII + RAII) approach for easier memory management.
- Write e2e tests to ensure memory zeroization
- Write e2e tests to verify prevention of side-channel attacks
Contributions welcome!