sage-estimator
ML-KEM + ML-DSA security estimation API with leaky-LWE side-channel analysis
818
REST API for precise ML-KEM (FIPS 203) and ML-DSA / Dilithium (FIPS 204) security estimation using malb/lattice-estimator and leaky-LWE-Estimator.
Computes BKZ blocksize, classical/quantum security bits, and decryption failure rates for any ML-KEM parameter set, plus dual MLWE + MSIS security for ML-DSA. Supports side-channel leakage analysis via the leaky-LWE-Estimator (Ducas et al.).
docker run -d --name sage-estimator \
-p 5123:5000 \
--memory 12g \
-e CACHE_PATH=/data/cache.json \
-v sage-data:/data \
panchajanya1999/sage-estimator:latest
The service precomputes 6 standard parameter sets on startup (~10-15 min):
# Health check
curl http://localhost:5123/health
# Check prewarm progress (ready when count = 6)
curl http://localhost:5123/cache
| Method | Path | Description |
|---|---|---|
| POST | /estimate | Submit ML-KEM estimate — returns 200 if cached, 202 with job_id if computing |
| GET | /verify | Verify ML-KEM against known beta values |
| Method | Path | Description |
|---|---|---|
| POST | /estimate_dilithium | Submit ML-DSA estimate — computes MLWE + MSIS |
| GET | /verify_dilithium | Verify ML-DSA standard sets |
| Method | Path | Description |
|---|---|---|
| GET | /health | Service health check (includes active_jobs count) |
| GET | /status/<job_id> | Poll job status — 202 while computing, 200 when done |
| GET | /cache | List all cached results |
| DELETE | /cache | Clear both ML-KEM and ML-DSA caches |
# Submit estimate — cached result (200)
curl -X POST http://localhost:5123/estimate \
-H 'Content-Type: application/json' \
-d '{"k": 3, "eta1": 2, "eta2": 2, "du": 10, "dv": 4}'
# {"beta": 624, "classical": 182, ..., "source": "cached", "status": "done"}
# Submit estimate — new computation (202)
curl -X POST http://localhost:5123/estimate \
-H 'Content-Type: application/json' \
-d '{"k": 3, "eta1": 3, "eta2": 3, "du": 10, "dv": 4}'
# {"status": "computing", "job_id": "23cc08da", "elapsed": 0}
# Poll until done
curl http://localhost:5123/status/23cc08da
# {"beta": 660, "classical": 192, ..., "status": "done"}
| Field | Range | Description |
|---|---|---|
k | 2-8 | Module rank (lattice dimension = 256 * k) |
eta1 | 1-5 | Secret noise parameter (CBD) |
eta2 | 1-5 | Error noise parameter (CBD) |
du | 8-12 | Ciphertext compression bits (u) |
dv | 3-6 | Ciphertext compression bits (v) |
| Variant | k | eta1 | eta2 | du | dv | beta | Classical | Quantum |
|---|---|---|---|---|---|---|---|---|
| ML-KEM-512 | 2 | 3 | 2 | 10 | 4 | 382 | ~111 bits | ~101 bits |
| ML-KEM-768 | 3 | 2 | 2 | 10 | 4 | 624 | ~182 bits | ~165 bits |
| ML-KEM-1024 | 4 | 2 | 2 | 11 | 5 | 874 | ~255 bits | ~231 bits |
The Dilithium endpoint computes both MLWE (key recovery) and MSIS (forgery resistance) security. Overall security = min(MLWE, MSIS).
# Submit ML-DSA-44 estimate
curl -X POST http://localhost:5123/estimate_dilithium \
-H 'Content-Type: application/json' \
-d '{"k": 4, "l": 4, "eta": 2, "gamma1": 131072, "gamma2": 95232}'
# {"status": "computing", "job_id": "e9a2863a", "elapsed": 0}
# Poll until done (~60-90 seconds)
curl http://localhost:5123/status/e9a2863a
# {"mlwe_beta": 424, "mlwe_classical": 123, "mlwe_quantum": 112,
# "msis_beta": 1128, "msis_classical": 329, "msis_quantum": 298,
# "status": "done", "source": "computed"}
| Field | Range | Description |
|---|---|---|
k | 2-8 | Matrix rows (FIPS 204: {4, 6, 8}) |
l | 2-7 | Matrix cols (FIPS 204: {4, 5, 7}) |
eta | 2 or 4 | Secret key coefficient bound |
gamma1 | 131072 or 524288 | Mask coefficient range (2^17 or 2^19) |
gamma2 | 95232 or 261888 | Decomposition factor ((q-1)/88 or (q-1)/32) |
| Variant | k | l | eta | MLWE beta | MSIS beta | Classical | Quantum |
|---|---|---|---|---|---|---|---|
| ML-DSA-44 | 4 | 4 | 2 | 424 | 1128 | ~123 bits | ~112 bits |
| ML-DSA-65 | 6 | 5 | 4 | 624 | 1386 | ~182 bits | ~165 bits |
| ML-DSA-87 | 8 | 7 | 2 | 863 | 1952 | ~252 bits | ~228 bits |
The MSIS estimate uses the native SIS.estimate() from the lattice-estimator with L2 (Euclidean) norm. The infinity norm estimator has an upstream gmpy2/SageMath compatibility bug. L2 norm provides a conservative upper bound — the real forgery problem (SelfTargetMSIS) is harder still.
Both /estimate and /estimate_dilithium accept optional leakage parameters to model security degradation when an attacker has partial knowledge of the secret key via side channels (DPA, power analysis, etc.).
Uses the leaky-LWE-Estimator by Ducas et al. (DBDD_predict_diag variant for large dimensions).
| Field | Type | Description |
|---|---|---|
leak_type | "perfect" or "approx" | Leakage model (optional) |
leak_amount | int > 0 | Number of leaked coordinates/measurements |
leak_variance | float > 0 | Noise variance (approx only) |
"perfect"): Exact bits leaked (e.g., DPA revealed N coordinates of the secret key). Models the worst-case scenario."approx"): Noisy measurements (e.g., N power traces with variance sigma^2). Models realistic side-channel attacks.# ML-KEM-512 with 50 perfectly leaked coordinates
curl -X POST http://localhost:5123/estimate \
-H 'Content-Type: application/json' \
-d '{"k": 2, "eta1": 3, "eta2": 2, "du": 10, "dv": 4,
"leak_type": "perfect", "leak_amount": 50}'
# Response includes standard + leaky fields:
# {"beta": 382, "classical": 111, ...,
# "leaky_beta": 347.8, "leaky_classical": 101,
# "leaky_quantum": 92, "security_loss_bits": 13, ...}
50 leaked coordinates (~10% of ML-KEM-512's 512-dimensional secret) drops classical security from ~111 to ~101 bits — a 13-bit loss.
# ML-DSA-44 with 50 perfectly leaked coordinates
curl -X POST http://localhost:5123/estimate_dilithium \
-H 'Content-Type: application/json' \
-d '{"k": 4, "l": 4, "eta": 2, "gamma1": 131072, "gamma2": 95232,
"leak_type": "perfect", "leak_amount": 50}'
# Response: mlwe_leaky_beta=335.8, mlwe_leaky_classical=98, security_loss=6 bits
# MSIS fields unchanged — forgery resistance is unaffected by key leakage
For ML-DSA, leakage analysis applies to MLWE only — MSIS (forgery resistance) is unaffected by key leakage.
| Field | Description |
|---|---|
leaky_beta / mlwe_leaky_beta | BKZ blocksize with leakage |
leaky_classical / mlwe_leaky_classical | Classical bits with leakage |
leaky_quantum / mlwe_leaky_quantum | Quantum bits with leakage |
security_loss_bits / mlwe_security_loss_bits | Classical bits lost |
leak_type | Leakage model used |
leak_amount | Number of leaked coordinates/measurements |
The estimator runs 6 attacks against the Module-LWE instance:
| Attack | Type | Description |
|---|---|---|
| usvp | Primal lattice | Unique Shortest Vector Problem — primary security metric (beta) |
| bdd | Primal lattice | Bounded Distance Decoding — finds closest lattice point to target |
| dual | Dual lattice | Distinguishes LWE samples from uniform using short dual vectors |
| dual_hybrid | Hybrid | Combines lattice reduction with guessing secret coordinates |
| arora-gb | Algebraic | Groebner basis attack on polynomial system from LWE samples |
| bkw | Combinatorial | Blum-Kalai-Wasserman — trades memory for reduced operations |
Results are cached in-memory and persisted to Docker volumes:
| Cache | File | Contents |
|---|---|---|
| ML-KEM | /data/cache.json | (k, eta1, eta2, du, dv[, leak_*]) -> result |
| ML-DSA | /data/dilithium_cache.json | (k, l, eta, gamma1, gamma2[, leak_*]) -> result |
Each unique parameter set is computed once (~30s-2min for ML-KEM, ~60-90s for ML-DSA, leaky adds ~2-5min via SageMath), then served instantly from cache.
| Parameter Set | Recommended Memory |
|---|---|
| ML-KEM-512 / ML-DSA-44 | 4 GB |
| ML-KEM-768 / ML-DSA-65 | 6 GB |
| ML-KEM-1024 / ML-DSA-87 | 8 GB |
| All sets + leaky-LWE | 12 GB |
condaforge/miniforge3 with SageMath via conda-forgelinux/arm64, linux/amd64Content type
Image
Digest
sha256:871c62dca…
Size
1.9 GB
Last updated
4 months ago
docker pull panchajanya1999/sage-estimator