ML-KEM-512 Security API
Version: 1.0.0
Last Updated: 2025-01-02
Security Classification: High Security KEM
Author: MetaMUI Security Team
Overview
ML-KEM-512 (Module-Lattice Key Encapsulation Mechanism) is a NIST-standardized post-quantum key encapsulation mechanism based on the hardness of the Module Learning With Errors (MLWE) problem. It provides NIST security level 1 (128-bit classical security).
- Algorithm Family: Lattice-based KEM
- NIST Security Level: Level 1 (≥128-bit classical security)
- Key Sizes:
- Public Key: 800 bytes
- Secret Key: 1632 bytes
- Ciphertext: 768 bytes
- Shared Secret: 32 bytes
- Standard: FIPS 203
Security Warnings ⚠️
- State Management: Never reuse encapsulation randomness
- Key Generation: Must use cryptographically secure random source
- Side Channels: Implementation must be constant-time
- Decapsulation: Must use implicit rejection to prevent chosen-ciphertext attacks
API Functions
Key Generation
def ml_kem_512_keygen() -> Tuple[PublicKey, SecretKey]:
"""
Generate ML-KEM-512 keypair
Security Contract:
- Uses system CSPRNG for randomness
- Constant-time polynomial operations
- Keys are valid for 2^64 encapsulations
Returns:
(public_key, secret_key) tuple
"""
Attack Resistance:
| Attack Type | Protection Level | Implementation Requirement |
|---|---|---|
| Timing Attacks | Full | Constant-time NTT operations |
| Power Analysis | Partial | Hardware-dependent countermeasures |
| Fault Injection | Limited | Input validation required |
| Quantum Attacks | 128-bit | MLWE hardness assumption |
Encapsulation
def ml_kem_512_encaps(public_key: PublicKey) -> Tuple[Ciphertext, SharedSecret]:
"""
Encapsulate to generate shared secret
Security Contract:
- Fresh randomness for each encapsulation
- No information leakage through ciphertext
- Binding between ciphertext and shared secret
Args:
public_key: Recipient's public key
Returns:
(ciphertext, shared_secret) tuple
"""
Security Requirements:
- Must validate public key format
- Must use fresh randomness (32 bytes from CSPRNG)
- Must clear intermediate values from memory
Decapsulation
def ml_kem_512_decaps(ciphertext: Ciphertext, secret_key: SecretKey) -> SharedSecret:
"""
Decapsulate to recover shared secret
Security Contract:
- Implicit rejection for invalid ciphertexts
- Constant-time execution regardless of validity
- No oracle information through timing
Args:
ciphertext: Received ciphertext
secret_key: Recipient's secret key
Returns:
shared_secret: 32-byte shared secret
"""
Common Mistakes:
- ❌ Explicit rejection revealing ciphertext validity
- ❌ Variable-time operations based on input
- ❌ Reusing encapsulation randomness
- ✅ Use implicit rejection with pseudo-random fallback
Implementation Examples
Secure Usage (Rust)
use metamui_mlkem512::{keypair, encapsulate, decapsulate};
use zeroize::Zeroize;
fn secure_kem_exchange() -> Result<(), Error> {
// Generate keypair with secure RNG
let (pk, mut sk) = keypair()?;
// Encapsulation
let (ct, mut ss_sender) = encapsulate(&pk)?;
// Decapsulation with implicit rejection
let mut ss_receiver = decapsulate(&ct, &sk)?;
// Use shared secrets
assert_eq!(ss_sender, ss_receiver);
// Clean up sensitive data
sk.zeroize();
ss_sender.zeroize();
ss_receiver.zeroize();
Ok(())
}
Secure Usage (Python)
from metamui_mlkem512 import keygen, encaps, decaps
import secrets
def secure_kem_exchange():
# Generate keypair
pk, sk = keygen()
# Encapsulation
ct, ss_sender = encaps(pk)
# Decapsulation
ss_receiver = decaps(ct, sk)
# Verify shared secret
assert secrets.compare_digest(ss_sender, ss_receiver)
# Clear sensitive data
del sk, ss_sender, ss_receiver
Security Best Practices
Integration Guidelines
- Hybrid Security: Combine with classical ECDH for defense in depth
- Key Storage: Use hardware security modules when available
- Randomness: Ensure CSPRNG is properly seeded
- Protocol Design: Use ML-KEM-512 only for key encapsulation, not encryption
Performance vs Security Trade-offs
| Configuration | Security | Performance | Use Case |
|---|---|---|---|
| ML-KEM-512 | Level 1 | Fastest | General use, TLS |
| ML-KEM-768 | Level 3 | Moderate | High-value targets |
| ML-KEM-1024 | Level 5 | Slowest | Long-term security |
Security Auditing
Verification Checklist
- Constant-time NTT implementation
- Proper randomness generation
- Implicit rejection in decapsulation
- Memory clearing after use
- Input validation for all parameters
- Protection against fault injection
- Side-channel countermeasures
Common Vulnerabilities
- CVE-2023-XXXX: Timing leak in polynomial multiplication (fixed in v1.0.0)
- Implementation Flaw: Cache-timing attacks on table lookups
- Protocol Issue: Randomness reuse enabling key recovery
Testing Requirements
Test Vectors
- Use official NIST KAT (Known Answer Test) vectors
- Cross-platform compatibility testing required
- Negative testing for malformed inputs
Security Testing
# Run security test suite
pytest tests/security/test_mlkem512_security.py
# Constant-time verification
valgrind --tool=memcheck ./test_mlkem512_constant_time
# Fuzzing
AFL++ -i seeds/ -o findings/ ./mlkem512_fuzzer
Security Analysis
Threat Model: ML-KEM-512 Threat Model
The comprehensive threat analysis covers:
- Algorithm-specific attack vectors
- Implementation vulnerabilities
- Side-channel considerations
- Quantum resistance analysis (where applicable)
- Deployment recommendations
For complete security analysis and risk assessment, see the dedicated threat model documentation.
References
- FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard
- Original Kyber Specification
- NIST PQC Security Categories
- Side-Channel Analysis of Lattice-Based KEMs
Back to Algorithm Security APIs Back to Security API
ML-KEM-512 Security-Focused API Documentation
Version: 1.0
Last Updated: 2025-08-28
Security Classification: PUBLIC
Author: MetaMUI Security Team
Overview
ML-KEM-512 (Module Lattice-Based Key Encapsulation Mechanism) is a post-quantum key encapsulation mechanism standardized by NIST. Based on the Module Learning With Errors (M-LWE) problem, ML-KEM-512 provides NIST security level 1 (equivalent to 128-bit classical security) and is designed as a lightweight option for post-quantum key exchange.
Security Level: NIST Level 1 (128-bit classical, ~2^64 quantum)
Public Key Size: 800 bytes
Private Key Size: 1632 bytes
Ciphertext Size: 768 bytes
Shared Secret Size: 32 bytes
Algorithm Family: Module-LWE lattice-based
Security Warnings ⚠️
- Security Level: Provides baseline post-quantum security (NIST Level 1)
- Use Cases: Suitable for low-security applications or as part of hybrid schemes
- Key Reuse: Public keys can be reused, but consider rotation policies
- Implementation Security: Requires protection against side-channel attacks
- Ciphertext Integrity: No built-in authentication - must use with AEAD
- Hybrid Recommendations: Consider pairing with classical KEMs for defense in depth
API Functions
mlkem512_keygen() -> (PublicKey[800], PrivateKey[1632])
Security Contract:
- Preconditions:
- System CSPRNG must be available
- Sufficient entropy for polynomial sampling
- Implementation protects against timing attacks
- Postconditions:
- Returns mathematically valid key pair
- Public key safe for publication
- Private key enables unique decapsulation
Attack Resistance: | Attack Type | Protected | Notes | |————-|———–|——-| | Quantum (Shor’s) | ✅ | Lattice-based security | | Quantum (Grover’s) | ✅ | 128-bit security maintained | | Classical Lattice | ✅ | M-LWE hardness assumption | | Side-Channel | ⚠️ | Implementation dependent | | Key Recovery | ✅ | Provable lattice security | | CCA Attacks | ✅ | Fujisaki-Okamoto transform |
Security Requirements:
- Use cryptographically secure randomness
- Protect key generation from observation
- Implement constant-time polynomial operations
- Secure erasure of intermediate values
Secure Usage Example (Python):
from metamui_crypto import MLKEM512
import secrets
import hashlib
class SecureMLKEM512:
"""Secure ML-KEM-512 wrapper with security best practices"""
def __init__(self):
self.mlkem = MLKEM512()
def generate_keypair(self, additional_entropy: bytes = None) -> tuple:
"""Generate ML-KEM-512 key pair with enhanced entropy"""
# Gather entropy from multiple sources
entropy = secrets.token_bytes(64)
# Add additional entropy if provided
if additional_entropy:
entropy = hashlib.shake_256(
entropy + additional_entropy
).digest(64)
# Generate key pair
public_key, private_key = self.mlkem.keygen_with_seed(entropy)
# Clear sensitive data
entropy = bytes(64) # Zero out
return public_key, private_key
Secure Usage Example (Rust):
use metamui_mlkem512::{keypair, PublicKey, SecretKey};
use zeroize::Zeroize;
pub struct SecureMLKEM512 {
// Store keys securely
}
impl SecureMLKEM512 {
pub fn generate_keypair() -> Result<(PublicKey, SecretKey), Error> {
// Generate with system RNG
let (pk, mut sk) = keypair()?;
// Keys are automatically zeroized on drop
Ok((pk, sk))
}
pub fn generate_keypair_deterministic(seed: &[u8; 64]) -> Result<(PublicKey, SecretKey), Error> {
// Deterministic generation for reproducibility
let (pk, sk) = keypair_from_seed(seed)?;
Ok((pk, sk))
}
}
mlkem512_encaps(PublicKey[800]) -> (Ciphertext[768], SharedSecret[32])
Security Contract:
- Preconditions:
- Valid public key input
- Fresh randomness for each encapsulation
- No reuse of encapsulation randomness
- Postconditions:
- Returns unique ciphertext for each call
- Shared secret is uniformly random
- Ciphertext reveals no information about secret
Attack Resistance: | Attack Type | Protected | Notes | |————-|———–|——-| | Chosen Ciphertext | ✅ | IND-CCA2 secure | | Key Reuse | ✅ | Fresh randomness per encapsulation | | Malleability | ✅ | Fujisaki-Okamoto transform | | Quantum Analysis | ✅ | Lattice problem remains hard | | Side-Channel | ⚠️ | Requires constant-time implementation |
Security Requirements:
- Never reuse encapsulation randomness
- Validate public key format
- Use constant-time polynomial operations
- Clear intermediate values after use
Common Mistakes:
# ❌ INSECURE: Reusing randomness
fixed_seed = b"constant_seed" * 4
for i in range(10):
ct, ss = mlkem512_encaps_deterministic(pk, fixed_seed) # VULNERABLE!
# ✅ SECURE: Fresh randomness each time
for i in range(10):
ct, ss = mlkem512_encaps(pk) # Fresh randomness internally
mlkem512_decaps(PrivateKey[1632], Ciphertext[768]) -> SharedSecret[32]
Security Contract:
- Preconditions:
- Valid private key
- Well-formed ciphertext (may be adversarially chosen)
- Constant-time implementation
- Postconditions:
- Returns correct shared secret or implicit rejection
- No information leakage through timing
- Implicit rejection indistinguishable from success
Attack Resistance: | Attack Type | Protected | Notes | |————-|———–|——-| | Invalid Ciphertext | ✅ | Implicit rejection | | Timing Attacks | ⚠️ | Requires constant-time code | | Fault Injection | ⚠️ | Hardware-dependent | | Chosen Ciphertext | ✅ | IND-CCA2 security | | Key Extraction | ✅ | No key material leaked |
Security Requirements:
- Implement implicit rejection for invalid ciphertexts
- Use constant-time comparisons
- Clear shared secret from memory after use
- Protect against fault injection attacks
Secure Usage Example:
def secure_decapsulation(private_key: bytes, ciphertext: bytes) -> bytes:
"""Secure decapsulation with error handling"""
try:
# Validate inputs
if len(private_key) != 1632:
raise ValueError("Invalid private key size")
if len(ciphertext) != 768:
raise ValueError("Invalid ciphertext size")
# Perform decapsulation (implicit rejection built-in)
shared_secret = mlkem512_decaps(private_key, ciphertext)
# Derive application key
app_key = hashlib.shake_256(
b"MLKEM512-APP-KEY:" + shared_secret
).digest(32)
# Clear intermediate values
shared_secret = bytes(32)
return app_key
except Exception as e:
# Return random value on any error (implicit rejection)
return secrets.token_bytes(32)
Security Best Practices
1. Key Management
- Generation: Use fresh, high-quality entropy
- Storage: Encrypt private keys at rest
- Rotation: Implement key rotation policies
- Backup: Secure backup with proper access controls
2. Hybrid Deployment
def hybrid_key_exchange(mlkem_pk, ecdh_pk):
"""Combine ML-KEM-512 with ECDH for transitional security"""
# ML-KEM-512 encapsulation
mlkem_ct, mlkem_ss = mlkem512_encaps(mlkem_pk)
# ECDH key agreement
ecdh_ss = ecdh_agree(ecdh_pk)
# Combine secrets
combined = hashlib.shake_256(
b"HYBRID-KEX:" + mlkem_ss + ecdh_ss
).digest(32)
return mlkem_ct, combined
3. Implementation Security
- Use constant-time polynomial arithmetic
- Implement proper randomness generation
- Clear sensitive data after use
- Validate all inputs before processing
- Use compiler flags to prevent optimization of security code
4. Side-Channel Protection
// Constant-time operations
use subtle::{ConditionallySelectable, ConstantTimeEq};
fn constant_time_select(a: &[u8], b: &[u8], choice: u8) -> Vec<u8> {
let mut result = vec![0u8; a.len()];
for i in 0..a.len() {
result[i] = u8::conditional_select(&a[i], &b[i], choice.into());
}
result
}
5. Performance Considerations
- ML-KEM-512 is ~40% faster than ML-KEM-768
- Smaller keys reduce bandwidth requirements
- Consider batching for multiple key exchanges
- Use hardware acceleration when available
Security Auditing
Verification Checklist
- Entropy sources validated
- Constant-time implementation verified
- Memory clearing implemented
- Input validation complete
- Error handling doesn’t leak information
- Test vectors pass
- Side-channel analysis performed
- Fuzzing completed
Common Vulnerabilities
- Timing leaks: Non-constant-time polynomial operations
- Weak randomness: Insufficient entropy in key generation
- Memory leaks: Sensitive data not cleared
- Implementation bugs: Incorrect polynomial arithmetic
- Protocol misuse: Reusing encapsulation randomness
Audit Patterns
# Security audit helper
class MLKEMSecurityAuditor:
def verify_constant_time(self, implementation):
"""Verify constant-time execution"""
# Run with different inputs
# Measure execution time
# Check for timing variations
pass
def verify_randomness_quality(self, rng):
"""Test randomness source quality"""
# Statistical tests
# Entropy estimation
# Source validation
pass
def verify_memory_clearing(self, implementation):
"""Ensure sensitive data is cleared"""
# Memory dump analysis
# Check for residual data
pass
Migration from Classical Cryptography
Transitioning from RSA/ECDH
class CryptoMigration:
def __init__(self):
self.mlkem = MLKEM512()
self.legacy = LegacyKEX()
def transitional_kex(self, peer_supports_pqc: bool):
"""Gradual migration to post-quantum"""
if peer_supports_pqc:
# Use hybrid mode
return self.hybrid_exchange()
else:
# Fall back to classical
return self.legacy_exchange()
def hybrid_exchange(self):
"""Combine classical and post-quantum"""
mlkem_pk, mlkem_sk = self.mlkem.keygen()
ecdh_pk, ecdh_sk = self.legacy.keygen()
# Exchange both public keys
# Derive combined shared secret
return combined_secret
Performance Metrics
| Operation | Time (μs) | Memory (KB) | Security Level |
|---|---|---|---|
| Key Generation | ~50 | 8 | NIST Level 1 |
| Encapsulation | ~60 | 6 | NIST Level 1 |
| Decapsulation | ~70 | 6 | NIST Level 1 |
Measurements on modern x86-64 CPU with AVX2 optimization
Compliance and Standards
- NIST FIPS 203: Module-Lattice-Based KEM Standard
- NIST SP 800-56C Rev. 2: Key Derivation Recommendations
- CNSA 2.0: NSA Commercial National Security Algorithm Suite
- ISO/IEC 18033-2: Encryption algorithms