// kit 01

PQC Toolkit

Production-ready post-quantum crypto. Pick stack, pick primitive, copy the code.

$ app/api/sign/route.ts
L3
TS
1import { NextResponse } from 'next/server';
2import { ml_dsa65 } from '@noble/post-quantum/ml-dsa';
3import { ed25519 } from '@noble/curves/ed25519';
4 
5const PQ_PRIV = Buffer.from(process.env.PQ_DSA_PRIV!, 'base64');
6const PQ_PUB = Buffer.from(process.env.PQ_DSA_PUB!, 'base64');
7const ED_PRIV = Buffer.from(process.env.ED_PRIV!, 'hex');
8const ED_PUB = ed25519.getPublicKey(ED_PRIV);
9 
10function b64u(b: Uint8Array | Buffer) {
11 return Buffer.from(b).toString('base64url');
12}
13 
14export 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.