Skip to content

SudoXploit7/QuantumShield

Repository files navigation

QuantumShield

Post-Quantum Secure OpenID Connect over KEMTLS

QuantumShield replaces the classical TLS handshake in an OpenID Connect authentication flow with KEMTLS — a signature-less key exchange protocol built on ML-KEM-768 (NIST FIPS 203). The result is a full OIDC provider where every credential, authorization code, and token travels over a post-quantum encrypted channel, and the server proves its identity implicitly through KEM decapsulation rather than a certificate signature.

This project implements the protocol described in Wiggers (2020), IACR ePrint 2020/534 and applies it end-to-end: from TCP socket transport, through an HTTP adapter layer, to a browser-compatible hybrid handshake and a live interactive dashboard.


Screenshot 2026-05-25 002611

Table of Contents


How It Works

Standard TLS authenticates the server with a certificate signature (RSA or ECDSA). KEMTLS eliminates that signature entirely. Instead, the server's identity is its long-term ML-KEM-768 public key. If the server can decapsulate the client's ciphertext and produce the correct Finished MAC, it has proven possession of the private key — no signature required.

Handshake message sequence (Wiggers 2020, §3, Fig. 1):

Client                                      Server
  |                                           |
  |<------ ServerHello (KEM public key) ------|
  |                                           |
  |------- ClientKEMCiphertext (encap) ------>|
  |                                           |
  |          [both derive channel key]        |
  |                                           |
  |<------- Finished (HMAC-SHA3-256 MAC) -----|
  |                                           |
  |   [OIDC flow over AES-256-GCM channel]    |

Once the channel is established, all OIDC traffic (authorization request, authorization code, token exchange, userinfo) is encrypted with AES-256-GCM keyed from the KEM shared secret via HKDF-SHA256.

OIDC ID Tokens are signed with ML-DSA-65 (NIST FIPS 204) for independent third-party verification — signatures appear only at the application layer, never in the transport handshake.


Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        Client Side                              │
│                                                                 │
│  Browser / CLI / Test Client                                    │
│  ┌──────────────────┐   ┌─────────────────────────────────┐     │
│  │  login.js        │   │  kemtls_client_tcp.py           │     │
│  │  (hybrid KEMTLS) │   │  KEMTLSTCPSession               │     │
│  │  ECDH P-256 wrap │   │  kemtls_http_adapter.py         │     │
│  └────────┬─────────┘   └──────────────┬────────────────── ┘    │
└───────────┼──────────────────────────── ┼───────────────────────┘
            │ HTTPS / TCP                 │ Raw TCP :9001
┌───────────┼─────────────────────────────┼───────────────────────┐
│           │          Server Side        │                       │
│  ┌────────▼────────────────────────┐   ┌▼────────────────────┐  │
│  │  web_demo/server.py (Flask)     │   │ kemtls_server_tcp.py│  │
│  │  :9000                          │   │ :9999 / :9001       │  │
│  │  /kemtls/browser-handshake      │   │ KEMTLSTCPServer     │  │
│  │  /kemtls/send                   │   │                     │  │
│  │  /oidc/authorize                │◄──┤ OIDC Bridge         │  │
│  │  /oidc/token                    │   │                     │  │
│  │  /oidc/userinfo                 │   └─────────────────────┘  │
│  └────────────────────────────────┘                             │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  kemtls/handshake.py     ·   kemtls/channel.py          │    │
│  │  KEMTLSHandshake             SecureChannel              │    │
│  │  ML-KEM-768 keygen/encap/decap   AES-256-GCM encrypt    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  web_demo/pq_crypto_real.py                             │    │
│  │  RealKEMTLS   ·   PQTokenService                        │    │
│  │  liboqs ML-KEM-768 + ML-DSA-65  (NIST FIPS 203/204)     │    │
│  └─────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘

There are two transport paths:

Path Description When to use
Browser (Hybrid) Browser derives the KEMTLS session key via a two-round proxy handshake. P-256 ECDH delivers the ML-KEM-768-derived key to the browser; all subsequent OIDC traffic is encrypted with that key. Web UI, any browser
TCP Native Full socket-level KEMTLS. Client and server exchange raw framed messages; no HTTP wrapping. CLI, programmatic clients, kemtls_http_adapter.py

Screenshot 2026-05-25 002844 Screenshot 2026-05-25 003340 Screenshot 2026-05-25 003210 Screenshot 2026-05-25 003032

Algorithms

Layer Algorithm Standard Security Level
Key Encapsulation ML-KEM-768 (Kyber768) NIST FIPS 203 Level 3
Token Signing ML-DSA-65 (Dilithium3) NIST FIPS 204 Level 3
Symmetric Encryption AES-256-GCM NIST FIPS 197 256-bit
Key Derivation HKDF-SHA256 RFC 5869
Transcript Binding SHA3-256 NIST FIPS 202 256-bit

All post-quantum operations are performed by liboqs (Open Quantum Safe C library) via liboqs-python. There is no simulation or mock crypto anywhere in the codebase.

Why SHA-256 inside HKDF and SHA3-256 for transcripts? HKDF operates purely on symmetric material (the KEM shared secret); NIST's post-quantum requirements apply to public-key primitives. HKDF-SHA256 is used here to match the TLS 1.3 key schedule (RFC 8446 §7.1) and the KEMTLS paper's recommendation. SHA3-256 is used for handshake transcript binding to maintain domain separation from the KDF, consistent with Wiggers §3.


Performance

Measured on x86-64 hardware using metrics/benchmark.py with real liboqs operations over 1,000 iterations.

Primitive latencies:

Operation Mean (ms) Std (ms) N
ML-KEM-768 Keygen 0.081 0.016 100
ML-KEM-768 Encapsulation 0.059 0.009 100
ML-KEM-768 Decapsulation 0.069 0.013 100
ML-DSA-65 Signing 0.506 0.355 100
ML-DSA-65 Verification 0.152 0.050 100
Full KEMTLS Handshake 1.08 0.461 1000
OIDC Token Issuance 0.58 0.287 1000
OIDC Token Verification 0.17 0.054 1000

Protocol comparison:

Metric Classical TLS (RSA-2048) PQ-TLS (reference) KEMTLS (this project)
Handshake latency ~0.92 ms ~1.38 ms ~1.08 ms
vs. PQ-TLS baseline 21.6% faster
Handshake message size ~1.4 KB ~10.8 KB ~7.5 KB
Post-quantum secure No Yes Yes

KEMTLS is 21.6% faster than standard PQ-TLS and 30% smaller on the wire because it eliminates the ML-DSA-65 signature (~3.3 KB) from the handshake entirely.



Project Structure

QuantumShield/
│
├── kemtls/                        # Core protocol library
│   ├── handshake.py               # KEMTLSHandshake — signature-less key exchange
│   └── channel.py                 # SecureChannel — AES-256-GCM over KEM shared secret
│
├── web_demo/                      # Interactive web application
│   ├── server.py                  # Flask server: OIDC provider + KEMTLS endpoints (~2300 lines)
│   ├── pq_crypto_real.py          # RealKEMTLS + PQTokenService — liboqs wrapper
│   ├── tls_comparison_demo.py     # Classical TLS demo for side-by-side comparison
│   ├── static/
│   │   ├── login.js               # Browser hybrid KEMTLS handshake + OIDC flow
│   │   ├── dashboard.js           # Live test runner + WebSocket updates
│   │   ├── comparison.js          # Protocol benchmark visualisation
│   │   └── particles.js           # Background canvas animation
│   └── templates/
│       ├── login.html             # KEMTLS login page
│       ├── dashboard.html         # Security test dashboard
│       ├── comparison.html        # KEMTLS vs TLS side-by-side
│       └── tls_login.html         # Classical TLS login (control)
│
├── kemtls_server_tcp.py           # Hardened raw TCP KEMTLS server (port 9999)
├── kemtls_client_tcp.py           # Raw TCP KEMTLS client
├── kemtls_http_adapter.py         # requests-compatible KEMTLS session wrapper
│
├── pq_crypto/
│   └── pq_jwt.py                  # ML-DSA-65 JWT issuance + verification
│
├── metrics/
│   └── benchmark.py               # Cryptographic benchmark suite
│
├── benchmark/
│   └── benchmark_compare.py       # KEMTLS vs TLS comparison benchmark
│
├── scripts/
│   └── demo_flow.py               # End-to-end automated demonstration
│
├── tests/
│   ├── test_kemtls_protocol.py    # Protocol unit tests (real crypto)
│   ├── test_kemtls_oidc_e2e.py    # OIDC end-to-end tests
│   ├── test_kemtls_oidc_bridge_e2e.py  # TCP bridge integration tests
│   ├── test_auth.py               # Authentication tests
│   └── web_demo/                  # Web demo API + security tests
│
├── tls_simulation/                # Classical TLS simulation (control group)
├── dashboard/                     # State + event log files (runtime)
│
├── Dockerfile                     # Multi-stage build with liboqs from source
├── render.yaml                    # One-click Render.com deployment
└── web_demo/requirements.txt      # Python dependencies

Getting Started

Prerequisites

  • Python 3.10+
  • CMake 3.15+ and a C compiler (for building liboqs)
  • Git

Or just use Docker — it handles the liboqs build automatically.

Docker (recommended)

git clone https://github.com/<your-username>/QuantumShield.git
cd QuantumShield

docker build -t quantumshield .
docker run -p 9000:9000 quantumshield

Open http://localhost:9000.

Note: crypto.subtle (used in the browser handshake) requires a secure context. Access the app via http://localhost — not via an IP address or a plain HTTP hostname. On Render, HTTPS is provided automatically.

Local Setup

1. Install liboqs

git clone -b 0.10.0 https://github.com/open-quantum-safe/liboqs.git
cd liboqs && mkdir build && cd build
cmake -DOQS_USE_OPENSSL=OFF -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr/local ..
make -j$(nproc) && sudo make install
sudo ldconfig
cd ../..

2. Install Python dependencies

python -m venv .venv && source .venv/bin/activate
pip install -r web_demo/requirements.txt
pip install gunicorn gevent gevent-websocket

3. Start the server

# Development
python web_demo/server.py

# Production (matches Docker CMD)
gunicorn -k gevent -w 1 --timeout 300 --bind 0.0.0.0:9000 --chdir web_demo "server:app"

Open http://localhost:9000. Default credentials: admin / quantum123.


Running the Demo

Web interface

Navigate to http://localhost:9000. The landing page offers two paths:

  • KEMTLS Login — post-quantum handshake, step-by-step visualisation, ML-DSA-65 signed ID Token display
  • Compare — side-by-side latency chart of KEMTLS vs classical TLS

Programmatic (HTTP adapter)

from kemtls_http_adapter import KEMTLSSession

session = KEMTLSSession("http://localhost:9000")
session.establish()                              # ML-KEM-768 handshake

result = session.authorize("admin", "quantum123")
print(result["authorization"]["code"])           # OIDC authorization code

tokens = session.exchange_token(result["authorization"]["code"])
print(tokens["id_token"])                        # ML-DSA-65 signed JWT

session.close()

Raw TCP (native KEMTLS)

from kemtls_http_adapter import KEMTLSTCPSession

session = KEMTLSTCPSession(host="127.0.0.1", port=9001)
result  = session.authorize("admin", "quantum123")
tokens  = session.exchange_token(result["authorization"]["code"])
session.close()

End-to-end demo script

# Start the server first, then in a second terminal:
python scripts/demo_flow.py

This runs the full pipeline: KEMTLS handshake test, OIDC flow, benchmark comparison, and a summary report.

Benchmark suite

python metrics/benchmark.py

API Reference

All OIDC endpoints conform to OpenID Connect Core 1.0. The KEMTLS-specific endpoints handle channel establishment and encrypted message relay.

OIDC Endpoints

Method Path Description
GET /.well-known/openid-configuration Discovery document
GET /oidc/jwks JSON Web Key Set (ML-DSA-65 public key)
POST /oidc/authorize Authorization endpoint (requires active KEMTLS channel)
POST /oidc/token Token endpoint — returns ML-DSA-65 signed ID Token
GET /oidc/userinfo Userinfo endpoint
POST /oidc/logout Session logout

KEMTLS Channel Endpoints

Method Path Description
POST /kemtls/handshake Full KEMTLS handshake (native clients)
POST /kemtls/send Send encrypted OIDC payload over established channel
POST /kemtls/browser-handshake Hybrid browser handshake (Round 1 — P-256 key delivery)
POST /kemtls/browser-encap Browser encapsulation relay (Round 2)

OIDC Discovery document excerpt

{
  "issuer": "http://localhost:9000",
  "authorization_endpoint": "http://localhost:9000/oidc/authorize",
  "token_endpoint": "http://localhost:9000/oidc/token",
  "jwks_uri": "http://localhost:9000/oidc/jwks",
  "id_token_signing_alg_values_supported": ["ML-DSA-65"],
  "kem_algorithms_supported": ["ML-KEM-768"]
}

Browser Compatibility

Browsers cannot load native shared libraries, so ML-KEM-768 cannot run client-side. QuantumShield solves this with a two-round proxy handshake:

  1. Round 1 — Browser generates an ephemeral P-256 keypair and sends the public key to the server. The server generates a real ML-KEM-768 keypair, derives the session key via HKDF-SHA256, wraps it under the browser's P-256 key using ECDH + AES-256-GCM, and returns the wrapped key.

  2. Round 2 — Browser decrypts the wrapped key using its P-256 private key via crypto.subtle. The ML-KEM-768-derived session key is now shared. All subsequent OIDC payloads are encrypted with that key.

P-256 is used solely as a key-delivery envelope, never for OIDC data. The channel key is ML-KEM-768-derived throughout.

Requirement: crypto.subtle is only available in secure contexts (https:// or localhost). The app will fail with a Cannot read properties of undefined (reading 'generateKey') error if accessed over plain HTTP on a non-localhost hostname.


Tests

# Unit + integration tests
pytest tests/ -v

# KEMTLS protocol tests (real crypto, no server needed)
pytest tests/test_kemtls_protocol.py -v

# End-to-end OIDC flow (server must be running)
pytest tests/test_kemtls_oidc_e2e.py -v

# TCP bridge tests
pytest tests/test_kemtls_oidc_bridge_e2e.py -v

Benchmarks

Full methodology, raw numbers, and interpretation are in BENCHMARKS.md.

To reproduce:

python metrics/benchmark.py          # primitive + handshake latencies
python benchmark/benchmark_compare.py  # KEMTLS vs classical TLS comparison

Deployment

The repository includes a render.yaml for one-click deployment on Render. Push to GitHub, connect the repo on Render, and it builds the Docker image and deploys automatically with HTTPS.

services:
  - type: web
    name: quantumshield
    env: docker
    plan: free
    healthCheckPath: /

References

  1. P. Schwabe, D. Stebila, T. Wiggers — Post-Quantum TLS without Handshake Signatures, CCS 2021. IACR ePrint 2020/534
  2. F. Schardong et al. — Post-Quantum OpenID Connect, IEEE/ACM S&P 2023.
  3. NIST FIPS 203 — Module-Lattice-Based Key-Encapsulation Mechanism Standard (ML-KEM)
  4. NIST FIPS 204 — Module-Lattice-Based Digital Signature Standard (ML-DSA)
  5. Open Quantum Safe — liboqs
  6. RFC 5869 — HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
  7. RFC 8446 — The Transport Layer Security (TLS) Protocol Version 1.3

About

Post-quantum secure OpenID Connect using KEMTLS — ML-KEM-768 key exchange, ML-DSA-65 signed tokens, AES-256-GCM channel. HackIITK 2026 finalist.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors