Architecture
Nen's architecture is designed to be stateless, edge-compatible, and post-quantum secure. It relies on modern cryptographic primitives to establish ephemeral session keys and encrypt data end-to-end without requiring persistent TCP tunnels (like WebSockets) or complex identity infrastructure.
Cryptographic Primitives
The protocol leverages four primary cryptographic algorithms:
- ML-KEM (Kyber): Implements FIPS 203 for Key Encapsulation. Used to securely establish a shared secret over an insecure channel.
- ML-DSA (Dilithium): Implements FIPS 204 for Digital Signatures. Used strictly for identity verification during the handshake phase to prevent active man-in-the-middle attacks.
- ChaCha20Poly1305: A fast, symmetric authenticated encryption algorithm with associated data (AEAD). The ML-KEM shared secret is used directly as the AEAD key to encrypt all data payloads after the handshake.
- HMAC-SHA256: Used for hot-path per-request authentication with a separate random key issued at handshake, preventing forgery and replay via strict timestamp windowing and nonce tracking.
The Handshake Protocol
Nen avoids the overhead of managing long-lived stateful connections (like WebSockets or TLS tunnels) by performing a stateless cryptographic handshake.
Client App
Middleware
Session Store
1. Generate ML-KEM Keypair
1. Generate ML-KEM Keypair
2. Send Public Key (+ opt. ML-DSA signature)
2. Send Public Key (+ opt. ML-DSA signature)
3. Verify signature (if ML-DSA opt-in)
4. Encapsulate → sharedSecret
5. Generate 32-byte HMAC key
3. Verify signature (if ML-DSA opt-in)
4. Encapsulate → sharedSecret
5. Generate 32-byte HMAC key
6. Store Session Keys
6. Store Session Keys
7. Return Ciphertext, sid, HMAC Key
7. Return Ciphertext, sid, HMAC Key
8. Decapsulate to Shared Secret
8. Decapsulate to Shared Secret
- Client Initiation: The client generates an ML-KEM key pair. Optionally, it also generates an ML-DSA signing key and signs the public key.
- Key Transmission: The client sends its public key (and optionally its ML-DSA signing public key and signature) to the
/api/nen/handshakeroute. - Server Encapsulation: The server receives the Public Key, verifies the signature (if present), and generates a 32-byte shared secret using ML-KEM encapsulation. It also generates a separate random 32-byte HMAC key.
- Session Storage: The server stores the shared secret (ChaCha20 key) and the HMAC key in a pluggable session store — in-memory, Redis, or Upstash (REST, for Edge runtimes) — keyed by a unique
sid. - Ciphertext Return: The server returns the ML-KEM ciphertext, the
sid, and the base64 HMAC key to the client. - Client Decapsulation: The client uses its private key to decapsulate the ciphertext, arriving at the exact same shared secret.
Data Transport
Once the handshake is complete, all API requests flow as follows:
Client App
Middleware
Session Store
API Route
Encrypt Payload (ChaCha20)
Compute HMAC
Encrypt Payload (ChaCha20)
Compute HMAC
Encrypted Payload + Headers
Encrypted Payload + Headers
Fetch Session Keys
Fetch Session Keys
Return Keys
Return Keys
Verify HMAC → Timestamp → Nonce
1. HMAC-SHA256 over canonical string
2. Timestamp within 30s window
3. Nonce not replayed
4. AEAD decrypt → plaintext
Verify HMAC → Timestamp → Nonce
1. HMAC-SHA256 over canonical string
2. Timestamp within 30s window
3. Nonce not replayed
4. AEAD decrypt → plaintext
Pass Plaintext JSON
Pass Plaintext JSON
- The client encrypts the JSON payload with ChaCha20Poly1305 using the ML-KEM shared secret and a fresh nonce.
- The client computes an HMAC over the canonical string
METHOD \n PATH \n TIMESTAMP \n NONCE. - The request is sent with the encrypted body
{ ct, n }and the headersX-Nen-Session,X-Nen-Timestamp, andX-Nen-Signature. - The server's middleware fetches the session keys from the session store using the
sidfromX-Nen-Session. - It verifies the HMAC-SHA256 signature over the canonical string (
METHOD\nPATH\nTIMESTAMP\nNONCE). A missing signature is rejected withISO-3001; a bad signature withISO-3002. - It checks the timestamp is within a 30-second window (
ISO-3003) and that the nonce has not been replayed (ISO-5001). - Finally it AEAD-decrypts the body (
ISO-4001on tag failure) and passes the plaintext to the handler.