Denial-of-service protection for post-quantum endpoints

Post-quantum key exchange gave attackers a 585 microsecond lever

Decapsulating an ML-KEM-768 ciphertext costs 585 µs of server CPU, and any client triggers it by sending 1,088 bytes. AWS enabled it by default across KMS, ACM and S3. Cloudflare ships X25519MLKEM768. The upgrade arrived; the layer keeping it from being flooded did not.

This is that layer. A 24-byte admission token — 2.2% overhead on the ciphertext it gates — checked in 1.5 µs with no server state, no session memory, no shared secret. Junk is refused 390× cheaper than answering it, and past the threshold, earning admission costs an attacker more CPU than serving them costs you.

Architecture
585 µs
Decapsulation Protected
1.5 µs
Constant Verify Cost
2.2%
Wire Overhead
390×
Cheaper To Refuse
Why it works

Admission must cost the attacker more than service costs you

Every filter faces the same problem: cheap for you, expensive for them. Filters that only reject malformed traffic fail the moment an attacker reads the spec and sends well-formed traffic instead. So the token is bound to itself.

The token proves work, statelessly

Targets derive from a hash of the token: targets[z] = SHA-256(token ‖ context)[z] mod 6. Change any element and every target moves, so a token cannot be repaired into validity — it must be found by search. The server records nothing about who is solving what. Verification is one hash and a few sums, constant regardless of difficulty.

Difficulty tracks load to within 3%

Hashcash-style puzzles step in powers of two, so each rung doubles what honest clients pay. Because ℤ/6ℤ factors as ℤ/2ℤ × ℤ/3ℤ, difficulty lands on 2a·3b190 settings below 107 against 24 for powers of two. Tracking a load-driven target that is 1.03× mean overshoot versus 1.19× for binary. Raise the price on attackers without pricing out customers.

It sits in front of any ML-KEM endpoint

The layer is independent of what it protects. It does not modify, wrap or replace your key exchange — it decides which requests reach it. Standard ML-KEM-768 behind it, unchanged, cryptanalysis intact.

What it is not

Not a cipher and not a replacement for one. The ℤ/6ℤ layer provides admission control and HKDF context binding; confidentiality is ML-KEM-768's job, done unmodified. Search hardness is measured, not proven, and the structure has had no external cryptanalysis. The challenge below exists to change that.

Core Concept

Key establishment and admission control

Key establishment is ML-KEM-768 (NIST FIPS 203), used unchanged — a lattice KEM, because that is the construction with standing cryptanalysis behind it. The ℤ₆ layer answers a different question: whether a request deserves a decapsulation at all. Each element is a phase in {0,1,2,3,4,5} — no floating point, no noise sampling, no NTT polynomial multiplication.

Z6 Arithmetic

All operations are mod-6: add, subtract, multiply in ℤ₆. Deterministic, constant-time, trivially side-channel resistant. No continuous parameters to leak.

Topological Shield

Phase states are projected onto the nearest Z6 lattice point. Noise within ±π/6 is absorbed; noise at the π/6 boundary triggers an enforced collapse — detectable, non-forgeable.

🧹

Memory Scrubbing

All ephemeral key material is zeroed immediately after use. Private keys, shared secrets, and intermediate buffers are overwritten in a finally block — guaranteed cleanup.

Z6 KEM handshake

Run a complete key encapsulation cycle in your browser. Client and server generate ℤ₆ key pairs, negotiate a shared secret, and the topological shield validates every step.

1 Client Key Generation

Public Key (64 Z6 elements)
Private Key (64 Z6 elements, secret)

2 Server Handshake

Server Public Key
// Awaiting handshake...
Topological shield: awaiting handshake

3 Shared Secret Verification

// Run a handshake to verify
Computed Shared Secret (SHA-256)

Error detection

The Z6 topological shield enforces phase quantization at every gateway hop. No active error correction — just geometry.

Phase Quantization

Every Z6 element is projected to the nearest multiple of π/3. Deviation beyond ±π/6 triggers a detectable topological collapse.

Collapse Detection

At the exact midpoint between two Z6 states (deviation = π/6), the shield rejects the operation with a 403 — no ambiguity, no false acceptance.

🔒

Constant-Time Validation

Shield projection uses integer rounding — no branches on secret data. The validation cost is identical for all 6 phase states.

♻️

Zero Overhead Per Hop

The shield adds no per-request latency, no memory allocation, and no additional network round-trips. It runs inline in the Worker's fetch handler.

Break the Z6 moat shield

A standing challenge, with the attack surface published. The moat is an admission filter over 64 elements of ℤ/6ℤ. Under the hash-bound scheme now running on staging, admission targets are derived from the key itself, so no local repair exists and a valid key must be found by search. Break that and the filter falls.

The claim

Produce an admissible key for the current epoch in materially fewer than 0.693·6k attempts. Measured medians across k=2..7 track that figure at ratios 0.84–1.30, consistent with search and nothing better. A method that beats it — by exploiting the ℤ/6ℤ constraint structure, the group sums, or the packing — breaks the admission layer.

Rule

targets[z] = SHA-256(packed_key ‖ context)[z] mod 6, and the sum of every element i with i mod k == z must equal targets[z] mod 6. Element i belongs to group i mod k. Keys are 64 elements packed 3 bits each into 24 bytes.

Try it

GET https://z6-pqc-staging.fogeboro.workers.dev/api/moat-params?k=4 returns the live context string and difficulty. POST /api/moat-admit with {k, key_b64u, context} answers 200 or 403. Staging only — production runs the constant-target moat, which this scheme replaces.

What is not claimed

No external cryptanalysis has been performed on the ℤ/6ℤ admission structure. Search hardness is measured, not proven. The key-establishment layer is ML-KEM-768 and is not part of this challenge.

⚔ Attack Generator

Quick-craft attack vectors to probe the shield:

✏ Custom Key

Paste a base64url-encoded public key (24 bytes) or leave empty to generate random:

📊 Results

Submit an attack to see results here.

📜 Attack Log (Anonymous Metrics)

Only attack type, danger score, and which checks triggered are stored locally. No key data is ever saved.

No attacks logged yet.

Deployment model

Every component is self-contained. No external dependencies, no hardware assumptions, no trusted setup.

Z6 Primitive Engine

Mod-6 arithmetic (add, sub, mul) and continuous-to-discrete phase mapping. Every real angle is snapped to the nearest π/3 step with a bounded deviation.

📦

3-Bit Packing

Each Z6 element fits in 3 bits (values 0-5). 64 elements pack into 24 bytes. Serialization is a single O(n) bit-shift loop — no alloc, no compression.

🤝

KEM Handshake

Client and server exchange ℤ₆ public keys. Shared secret is wrapped with HKDF-SHA256 + AES-256-GCM — context-bound to the server public key. NIST-standard, constant-time, memory-scrubbed.

🧹

Guaranteed Scrubbing

In the finally block of every request, all Uint8Array and number[] buffers are filled with zero. No ephemeral key material survives past the response.

// === Z6 KEM Handshake (Client → Gateway → Origin) === // 1. Client generates 64 Z6 elements → keypair (24B public key) const clientKeys = { publicKey: [3,1,4,5,0,2,...], privateKey: [...] }; // 2. Client sends request with X-Z6-KEM-Public header (base64url) fetch("https://sec6z.oooooooooo.se/api/kem", { headers: { "X-Z6-KEM-Public": base64url(keys.publicKey) } }); // 3. Gateway generates shared secret, wraps with HKDF-SHA256 + AES-256-GCM // 4. Gateway returns server key + KEM envelope (nonce|ciphertext|tag|salt) // 5. Response includes X-Z6-Topological-Shield: Active | Degraded // 6. Client decapsulates shared secret = HKDF-derive + AES-GCM-decrypt // 7. All ephemeral buffers zeroed in finally block
Validation

Test results

Every property of the Z6 PQC gateway is verified — arithmetic, packing, topological shield, key agreement, and memory safety.

✓ PASS
Z6 Arithmetic
add, sub, mul mod 6
✓ PASS
Phase Mapping
continuous → discrete
✓ PASS
Bit-Packing
64×3-bit → 24 bytes
✓ PASS
Base64URL Round-trip
binary ↔ string
✓ PASS
Topological Shield
within-threshold noise
✓ PASS
Midpoint Collapse
deviation = π/6 → 403
✓ PASS
KEM Key Agreement
client⊕server = shared
✓ PASS
Memory Scrubbing
all buffers zeroed
Live API Test

Live gateway test

Make a real request to the Z6 PQC Gateway, verify the topological shield header, and perform a live KEM handshake.

Health Check

// Click to check

Live KEM Handshake

// Click to run KEM against deployed worker

Encrypt and decrypt

Use the KEM shared secret to derive an AES-256-GCM key and encrypt/decrypt messages. Your Z6 public key serves as the identity.

🔐 Encrypt

Your Public Key (base64url)
// Result will appear here

🔓 Decrypt

Your Public Key (base64url)
// Result will appear here

⚡ Bulk KEM

Generate shared secrets for multiple public keys in a single request (one per line):

// Results will appear here

Deploy at your edge

ML-KEM-768 key establishment, ℤ₆ admission control, HKDF-SHA256/AES-256-GCM sealing. Deploy to your own Cloudflare account. No third-party APIs.

Access Gateway → Contact

Measured performance

Live measurements against this production gateway running v4.0.0. Reproducible harness; no modelled or projected figures.

Admission filter economics — where the ℤ6 structure pays

A stateless public endpoint must decide whether to spend a post-quantum decapsulation on an incoming request. The ℤ6 admission check is a keyless integer test over 64 elements, so it runs before any asymmetric work.

PathCost per requestJunk rejected / second
Reject at ℤ6 admission filter0.36 µs2,777,778
Reject after ML-KEM decapsulation585.39 µs1,708
Advantage1,626.1× cheaper — 99.94% of CPU avoided per junk request

Unstructured key material is admitted at a rate of 1 in 1296. In fairness, any inexpensive pre-filter yields this shape of saving — a keyed MAC would too. What ℤ6 contributes specifically is that the check is structural and keyless: no shared secret, no server-side state, which is what makes it usable on an anonymous stateless endpoint where a MAC is not available.

Layer A · primitive cost (median, same machine)

OperationMedianRole
6 moat validation (64 elements)0.36 µsadmission screening
6 moat-valid key generation5.34 µsclient key construction
6 pack / unpack (24 B)0.42 µswire encoding
ML-KEM-768 encapsulate (JS runtime)534.25 µsclient key agreement
ML-KEM-768 decapsulate (JS runtime)585.39 µsserver key agreement
HKDF-SHA256 derive34.87 µskey derivation
X25519 keygen / ECDH34.21 / 33.89 µsclassical baseline

The JavaScript ML-KEM used in the Worker runtime is roughly 8× slower than the PQClean C reference (57.4 / 63.1 / 77.9 µs). Both are reported.

Layer B · wire footprint (measured from live envelopes)

ConfigurationBytes on wireWithin 1500 B MTU
X25519 (classical, pre-quantum)64Yes
ML-KEM-768 full handshake2,272No
ML-KEM-1024 full handshake3,136No
HQC-1286,682No
Classic McEliece-348864261,216No
Z6P v4 sealed message (server key cached)1,188Yes

Because the 1,184-byte server public key is fetched once and cached, only the ML-KEM ciphertext travels per message. That keeps a sealed message inside a single MTU where a full ML-KEM-768 handshake would fragment.

Layer C · transactional latency (single EU vantage)

ConcurrencyThroughputp50p95Success
sequential33 ms57 ms30/30
1090 req/s64 ms329 ms60/60
25236 req/s84 ms225 ms150/150
50278 req/s163 ms270 ms300/300

510 of 510 concurrent requests completed with no failures. Throughput is bounded by the measuring client and its network path, not by a datacentre capacity ceiling.

v3 → v4 · what changed

Propertyv3v4 (live)
Shared secret derivable from transcriptYes — demonstratedNo
Key establishmentcustom; public key equalled private keyML-KEM-768 (FIPS 203)
Admission filteradvisory headers onlyenforced, HTTP 403
Admission-valid key space216 keys (7.75 bits)~660 (155.1 bits)
Context bindingNoYes — verified
Median latency36 ms33 ms

Both paths are live on this host and were timed under identical conditions. The legacy route is marginally faster because it performs no real key establishment; its transcript remains derivable offline, which is why it is deprecated rather than recommended. The 3 ms difference is the entire latency cost of adding genuine post-quantum key establishment to this service.

How it compares

Every KEM below was benchmarked on the same machine with the same method, using the reference C implementations (PQClean) of the NIST standard and alternate schemes. Nobody builds exactly this, so the comparison is against what the industry actually deploys for post-quantum key agreement.

SchemeStatusPublic keyTransit KeygenEncapsDecapsFits MTU
X25519 (classical baseline)RFC 774832 B64 B31.1 µs32.9 µs32.9 µsYes
ML-KEM-512NIST FIPS 203800 B1,568 B34.7 µs39.9 µs50.5 µsNo
ML-KEM-768 (the deployed choice)NIST FIPS 2031,184 B2,272 B57.4 µs63.1 µs77.9 µsNo
ML-KEM-1024NIST FIPS 2031,568 B3,136 B87.2 µs93.3 µs114.1 µsNo
HQC-128NIST alternate2,249 B6,682 B1,437 µs2,931 µs4,778 µsNo
Classic McEliece-348864NIST alternate261,120 B261,216 B83,947 µs105 µs18,881 µsNo
Z6 lattice KEMResearch — unvetted24 B160 B10.2 µs18.3 µs7.3 µsYes

Reading these results honestly

Z6 is the smallest and fastest row in the table. That result is real and reproducible — and on its own it does not make Z6 a better choice than ML-KEM. Three reasons:

AxisML-KEM-768Z6 lattice KEM
Security analysisMulti-year NIST process, public cryptanalysis, security proofsNone. No external review, no proof, no published attack surface
Stated security levelNIST Category 3165-bit key space — a keyspace size, not an assessed security level
InteroperabilityX25519MLKEM768: one hybrid the whole industry converged onProprietary. Interoperates with nothing
Regulatory standingFIPS-validated path for the 2030 federal deadlinesNot FIPS, not eligible
DeploymentMajority of browser traffic to major CDNsThis gateway

Why smaller and faster is not automatically better. ML-KEM's 1,184-byte key and its keygen cost buy something specific: module-lattice operations whose hardness has been studied publicly for years. Z6 keygen samples 64 elements and bit-packs them, so it is cheap precisely because it does less mathematical work. A fair reading is that Z6 occupies a different point on the curve — extremely compact and MTU-resident, with an unquantified security margin — not that it dominates ML-KEM.

The interoperability lesson. The TLS ecosystem converged on a single hybrid (X25519MLKEM768) and deployment followed quickly; by mid-2026 post-quantum key agreement covers the majority of browser traffic to Cloudflare, and Executive Order 14412 set federal deadlines of 2030 for post-quantum encryption. IPsec went the other way — vendors shipped proprietary post-quantum key agreements that could not interoperate, and the migration was delayed by years. Z6 is, by construction, in that second category. It is a research protocol exploring what a compact ℤ₆ construction can do, not a drop-in replacement for a standardised KEM.

Method: PQClean reference implementations via pqcrypto, median of 3–300 iterations per operation depending on scheme cost, single machine, same process. Z6 timings are a local reimplementation of the deployed construction (sample → pack, HKDF-SHA256, AES-256-GCM); Z6 transit is measured from a live gateway envelope. Sizes for the NIST schemes are the implementations' own, matching the published parameters. Harness is reproducible.