// kit 01
PQC Toolkit
Production-ready post-quantum crypto. Pick stack, pick primitive, copy the code.
$ app/api/sign/route.ts
L3
1 import { NextResponse } from 'next/server'; 2 import { ml_dsa65 } from '@noble/post-quantum/ml-dsa'; 3 import { ed25519 } from '@noble/curves/ed25519'; 4 5 const PQ_PRIV = Buffer.from(process.env.PQ_DSA_PRIV!, 'base64'); 6 const PQ_PUB = Buffer.from(process.env.PQ_DSA_PUB!, 'base64'); 7 const ED_PRIV = Buffer.from(process.env.ED_PRIV!, 'hex'); 8 const ED_PUB = ed25519.getPublicKey(ED_PRIV); 9 10 function b64u(b: Uint8Array | Buffer) { 11 return Buffer.from(b).toString('base64url'); 12 } 13 14 export async function POST(req: Request) { 15 const claims = await req.json(); 16 const header = { alg: 'EdDSA+ML-DSA-65', typ: 'JWT-HYBRID' }; 17 const payload = { ...claims, iat: Math.floor(Date.now() / 1000) }; 18 19 const hb = b64u(Buffer.from(JSON.stringify(header))); 20 const pb = b64u(Buffer.from(JSON.stringify(payload))); 21 const msg = Buffer.from(`${hb}.${pb}`); 22 23 const sigEd = ed25519.sign(msg, ED_PRIV); 24 const sigPq = ml_dsa65.sign(msg, PQ_PRIV); 25 26 return NextResponse.json({ 27 token: `${hb}.${pb}.${b64u(sigEd)}.${b64u(sigPq)}`, 28 pub: { ed: b64u(ED_PUB), pq: b64u(PQ_PUB) }, 29 }); 30 }
KEY SIZE
1952 B pub
SIG / CT
3293 B sig
NIST LEVEL
L3
NOTES
Hybrid JWT with EdDSA + ML-DSA-65. Tokens get fat (~6 KB encoded). For browser clients, hold the token server-side and hand out a small session reference cookie.
- Bigger than fastify or express default header limits — bump your proxy.
- Verify both signatures; reject if either fails.
- Rotate keys quarterly. The PQ key sits in a KMS too.