Compare NIST-standardized post-quantum algorithms against classical cryptography
These free benchmarking tools measure the real-world performance of ML-KEM, ML-DSA, and SLH-DSA compared to RSA and ECC. Use these results to plan your infrastructure capacity for PQC migration.
Educational Purpose Only: All code samples and scripts provided on this page are for educational and illustrative purposes. They are NOT production-ready and have NOT been thoroughly tested in all environments.
Recommendation: Treat these examples as starting points for learning and development. Adapt, test, and validate according to your specific requirements and security standards.
liboqs is the reference implementation library for NIST PQC algorithms.
# Install build dependencies (Ubuntu/Debian) sudo apt-get update sudo apt-get install cmake gcc ninja-build libssl-dev python3-pytest python3-pytest-xdist unzip xsltproc doxygen graphviz # Clone and build liboqs git clone https://github.com/open-quantum-safe/liboqs.git cd liboqs mkdir build && cd build cmake -GNinja -DCMAKE_INSTALL_PREFIX=/usr/local .. ninja sudo ninja install # Install Python bindings pip install liboqs-python
Compare Kyber512/768/1024 against RSA-2048/3072/4096
#!/usr/bin/env python3
"""ML-KEM (Kyber) Performance Benchmark"""
import oqs
import time
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
def benchmark_kem(algorithm, iterations=1000):
"""Benchmark a KEM algorithm"""
with oqs.KeyEncapsulation(algorithm) as kem:
# Key generation
start = time.perf_counter()
for _ in range(iterations):
public_key = kem.generate_keypair()
keygen_time = (time.perf_counter() - start) / iterations * 1000
# Encapsulation
start = time.perf_counter()
for _ in range(iterations):
ciphertext, shared_secret = kem.encap_secret(public_key)
encap_time = (time.perf_counter() - start) / iterations * 1000
# Decapsulation
start = time.perf_counter()
for _ in range(iterations):
shared_secret_dec = kem.decap_secret(ciphertext)
decap_time = (time.perf_counter() - start) / iterations * 1000
# Sizes
pk_size = len(public_key)
ct_size = len(ciphertext)
ss_size = len(shared_secret)
return {
"keygen_ms": keygen_time,
"encap_ms": encap_time,
"decap_ms": decap_time,
"pk_bytes": pk_size,
"ct_bytes": ct_size,
"ss_bytes": ss_size
}
def benchmark_rsa(key_size, iterations=100):
"""Benchmark RSA"""
# Key generation
start = time.perf_counter()
for _ in range(iterations):
key = RSA.generate(key_size)
keygen_time = (time.perf_counter() - start) / iterations * 1000
# Encryption
key = RSA.generate(key_size)
cipher = PKCS1_OAEP.new(key)
message = b"0" * 32 # 32 bytes like a symmetric key
start = time.perf_counter()
for _ in range(iterations * 10): # More iterations since RSA encrypt is fast
ciphertext = cipher.encrypt(message)
encrypt_time = (time.perf_counter() - start) / (iterations * 10) * 1000
# Decryption
start = time.perf_counter()
for _ in range(iterations):
plaintext = cipher.decrypt(ciphertext)
decrypt_time = (time.perf_counter() - start) / iterations * 1000
return {
"keygen_ms": keygen_time,
"encap_ms": encrypt_time,
"decap_ms": decrypt_time,
"pk_bytes": len(key.export_key()),
"ct_bytes": len(ciphertext),
"ss_bytes": 32
}
if __name__ == "__main__":
print("="*70)
print("ML-KEM (KYBER) vs RSA PERFORMANCE BENCHMARK")
print("="*70)
# ML-KEM variants
kyber_algs = ["Kyber512", "Kyber768", "Kyber1024"]
print("\\n--- ML-KEM (Kyber) Performance ---")
for alg in kyber_algs:
result = benchmark_kem(alg)
print(f"\\n{alg}:")
print(f" Key Generation: {result['keygen_ms']:.3f} ms")
print(f" Encapsulation: {result['encap_ms']:.3f} ms")
print(f" Decapsulation: {result['decap_ms']:.3f} ms")
print(f" Public Key Size: {result['pk_bytes']} bytes")
print(f" Ciphertext Size: {result['ct_bytes']} bytes")
# RSA comparison
rsa_sizes = [2048, 3072, 4096]
print("\\n--- RSA Performance (for comparison) ---")
for size in rsa_sizes:
result = benchmark_rsa(size, iterations=50)
print(f"\\nRSA-{size}:")
print(f" Key Generation: {result['keygen_ms']:.3f} ms")
print(f" Encryption: {result['encap_ms']:.3f} ms")
print(f" Decryption: {result['decap_ms']:.3f} ms")
print(f" Public Key Size: {result['pk_bytes']} bytes")
print(f" Ciphertext Size: {result['ct_bytes']} bytes")
print("\\n" + "="*70)
print("SUMMARY: Kyber is typically 10-40x faster than RSA for key generation")
print("and 50x faster for decryption, with smaller ciphertexts.")
print("="*70)
Run: python benchmark_kem.py
Compare Dilithium2/3/5 against RSA and ECDSA signatures
#!/usr/bin/env python3
"""ML-DSA (Dilithium) Performance Benchmark"""
import oqs
import time
from Crypto.PublicKey import RSA, ECC
from Crypto.Signature import pkcs1_15, DSS
from Crypto.Hash import SHA256
def benchmark_signature(algorithm, iterations=1000):
"""Benchmark a signature algorithm"""
with oqs.Signature(algorithm) as sig:
# Key generation
start = time.perf_counter()
for _ in range(iterations):
public_key = sig.generate_keypair()
keygen_time = (time.perf_counter() - start) / iterations * 1000
# Signing
message = b"Benchmark message for signing" * 10
start = time.perf_counter()
for _ in range(iterations):
signature = sig.sign(message)
sign_time = (time.perf_counter() - start) / iterations * 1000
# Verification
start = time.perf_counter()
for _ in range(iterations):
is_valid = sig.verify(message, signature, public_key)
verify_time = (time.perf_counter() - start) / iterations * 1000
return {
"keygen_ms": keygen_time,
"sign_ms": sign_time,
"verify_ms": verify_time,
"pk_bytes": len(public_key),
"sig_bytes": len(signature)
}
def benchmark_rsa_signature(key_size, iterations=100):
"""Benchmark RSA signatures"""
# Key generation
start = time.perf_counter()
for _ in range(iterations):
key = RSA.generate(key_size)
keygen_time = (time.perf_counter() - start) / iterations * 1000
# Signing
key = RSA.generate(key_size)
message = b"Benchmark message for signing" * 10
h = SHA256.new(message)
start = time.perf_counter()
for _ in range(iterations):
signature = pkcs1_15.new(key).sign(h)
sign_time = (time.perf_counter() - start) / iterations * 1000
# Verification
start = time.perf_counter()
for _ in range(iterations * 10): # RSA verify is fast
pkcs1_15.new(key).verify(h, signature)
verify_time = (time.perf_counter() - start) / (iterations * 10) * 1000
return {
"keygen_ms": keygen_time,
"sign_ms": sign_time,
"verify_ms": verify_time,
"pk_bytes": len(key.export_key()),
"sig_bytes": len(signature)
}
def benchmark_ecdsa(curve, iterations=1000):
"""Benchmark ECDSA"""
# Key generation
start = time.perf_counter()
for _ in range(iterations):
key = ECC.generate(curve=curve)
keygen_time = (time.perf_counter() - start) / iterations * 1000
# Signing
key = ECC.generate(curve=curve)
message = b"Benchmark message for signing" * 10
h = SHA256.new(message)
start = time.perf_counter()
for _ in range(iterations):
signature = DSS.new(key, 'fips-186-3').sign(h)
sign_time = (time.perf_counter() - start) / iterations * 1000
# Verification
verifier = DSS.new(key, 'fips-186-3')
start = time.perf_counter()
for _ in range(iterations):
verifier.verify(h, signature)
verify_time = (time.perf_counter() - start) / iterations * 1000
return {
"keygen_ms": keygen_time,
"sign_ms": sign_time,
"verify_ms": verify_time,
"pk_bytes": len(key.export_key(format='DER')),
"sig_bytes": len(signature)
}
if __name__ == "__main__":
print("="*70)
print("ML-DSA (DILITHIUM) vs RSA/ECDSA SIGNATURE BENCHMARK")
print("="*70)
# ML-DSA variants
dilithium_algs = ["Dilithium2", "Dilithium3", "Dilithium5"]
print("\\n--- ML-DSA (Dilithium) Performance ---")
for alg in dilithium_algs:
result = benchmark_signature(alg)
print(f"\\n{alg}:")
print(f" Key Generation: {result['keygen_ms']:.3f} ms")
print(f" Signing: {result['sign_ms']:.3f} ms")
print(f" Verification: {result['verify_ms']:.3f} ms")
print(f" Public Key Size: {result['pk_bytes']} bytes")
print(f" Signature Size: {result['sig_bytes']} bytes")
# RSA comparison
print("\\n--- RSA Signatures ---")
for size in [2048, 3072]:
result = benchmark_rsa_signature(size, iterations=50)
print(f"\\nRSA-{size}:")
print(f" Key Generation: {result['keygen_ms']:.3f} ms")
print(f" Signing: {result['sign_ms']:.3f} ms")
print(f" Verification: {result['verify_ms']:.3f} ms")
print(f" Public Key Size: {result['pk_bytes']} bytes")
print(f" Signature Size: {result['sig_bytes']} bytes")
# ECDSA comparison
print("\\n--- ECDSA Signatures ---")
for curve in ['P-256', 'P-384']:
result = benchmark_ecdsa(curve)
print(f"\\nECDSA-{curve}:")
print(f" Key Generation: {result['keygen_ms']:.3f} ms")
print(f" Signing: {result['sign_ms']:.3f} ms")
print(f" Verification: {result['verify_ms']:.3f} ms")
print(f" Public Key Size: {result['pk_bytes']} bytes")
print(f" Signature Size: {result['sig_bytes']} bytes")
print("\\n" + "="*70)
#!/bin/bash # comprehensive-benchmark.sh python3 benchmark_kem.py > results/kem-benchmark.txt python3 benchmark_signatures.py > results/sig-benchmark.txt # Generate HTML report python3 generate_benchmark_report.py \ --input results/ \ --output reports/pqc-performance-report.html
| Algorithm | Security Level | KeyGen (ms) | Encap (ms) | Decap (ms) | PK Size | CT Size |
|---|---|---|---|---|---|---|
| Kyber512 | NIST L1 (AES-128) | 0.02 | 0.03 | 0.03 | 800 B | 768 B |
| Kyber768 | NIST L3 (AES-192) | 0.03 | 0.04 | 0.05 | 1,184 B | 1,088 B |
| Kyber1024 | NIST L5 (AES-256) | 0.05 | 0.06 | 0.06 | 1,568 B | 1,568 B |
| RSA-2048 | ~NIST L1 | 45 | 0.10 | 2.5 | 294 B | 256 B |
| RSA-3072 | ~NIST L3 | 150 | 0.15 | 7.0 | 422 B | 384 B |
| Algorithm | Security Level | KeyGen (ms) | Sign (ms) | Verify (ms) | PK Size | Sig Size |
|---|---|---|---|---|---|---|
| Dilithium2 | NIST L2 | 0.08 | 0.30 | 0.10 | 1,312 B | 2,420 B |
| Dilithium3 | NIST L3 | 0.12 | 0.50 | 0.15 | 1,952 B | 3,293 B |
| Dilithium5 | NIST L5 | 0.18 | 0.70 | 0.22 | 2,592 B | 4,595 B |
| RSA-2048 | ~NIST L1 | 45 | 3.0 | 0.08 | 294 B | 256 B |
| ECDSA P-256 | NIST L1 | 0.50 | 0.60 | 0.70 | 91 B | 64 B |
100% Free & Open Source Educational Resource
SpinDynamics.io - Making Quantum Security Accessible to All
← Return to Homepage