<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: sachin ruhil</title>
    <description>The latest articles on DEV Community by sachin ruhil (@sachin_ruhil_4139768c2981).</description>
    <link>https://dev.to/sachin_ruhil_4139768c2981</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3935199%2Fa74a67c6-8c1b-4509-8747-cd5edd388932.png</url>
      <title>DEV Community: sachin ruhil</title>
      <link>https://dev.to/sachin_ruhil_4139768c2981</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sachin_ruhil_4139768c2981"/>
    <language>en</language>
    <item>
      <title>JWT Is Not Quantum-Safe — So I Built a Library That Is</title>
      <dc:creator>sachin ruhil</dc:creator>
      <pubDate>Sat, 16 May 2026 17:17:09 +0000</pubDate>
      <link>https://dev.to/sachin_ruhil_4139768c2981/jwt-is-not-quantum-safe-so-i-built-a-library-that-is-19p</link>
      <guid>https://dev.to/sachin_ruhil_4139768c2981/jwt-is-not-quantum-safe-so-i-built-a-library-that-is-19p</guid>
      <description>&lt;p&gt;I built @pq-jwt/core — a post-quantum JWT library using &lt;strong&gt;NIST FIPS 204 (ML-DSA) and FIPS 205 (SLH-DSA)&lt;/strong&gt;. Drop-in successor to RS256/ES256. Here's why it matters and how to use it.&lt;/p&gt;

&lt;p&gt;Every Node.js app using jsonwebtoken with RS256 or ES256 has the same problem.&lt;br&gt;
A sufficiently powerful quantum computer running Shor's algorithm can derive your private key from your public key — in polynomial time. That means every token your server has ever signed becomes forgeable. Not "hard to forge." Forgeable.&lt;br&gt;
I spent the last few weeks building a fix: &lt;strong&gt;@pq-jwt/core&lt;/strong&gt; — a post-quantum JWT library built on NIST-standardized algorithms.&lt;br&gt;
bashnpm install &lt;strong&gt;@pq-jwt/core&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Problem With RS256 and ES256&lt;br&gt;
Standard JWTs use one of these signing algorithms:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RS256 — RSA + SHA-256&lt;/strong&gt;. Security basis: integer factorization.&lt;br&gt;
&lt;strong&gt;ES256 — ECDSA + SHA-256&lt;/strong&gt;. Security basis: elliptic curve discrete logarithm.&lt;/p&gt;

&lt;p&gt;Both of these problems are efficiently solvable by a quantum computer running Shor's algorithm (1994). The math has been known for 30 years. The hardware is catching up.&lt;br&gt;
&lt;strong&gt;In August 2024, NIST published three post-quantum cryptography standards&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FIPS 203 — ML-KEM&lt;/strong&gt; (key encapsulation)&lt;br&gt;
&lt;strong&gt;FIPS 204 — ML-DSA&lt;/strong&gt; (digital signatures) ← what we need for JWTs&lt;br&gt;
&lt;strong&gt;FIPS 205 — SLH-DSA&lt;/strong&gt; (hash-based signatures)&lt;/p&gt;

&lt;p&gt;These algorithms are designed to be secure against both classical and quantum computers. They are now official US federal standards. &lt;strong&gt;The NSA's CNSA 2.0 suite mandates them for all national security systems by 2030&lt;/strong&gt;.&lt;br&gt;
Nobody had built a developer-friendly JWT library around them. So I did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What @pq-jwt/core Does&lt;/strong&gt;&lt;br&gt;
It replaces the signing algorithm in your JWT workflow. Everything else stays the same — same three-part token structure (header.payload.signature), same claims (iss, sub, aud, exp), same API shape.&lt;br&gt;
import { generateKeyPair, sign, verify } from '@pq-jwt/core';&lt;/p&gt;

&lt;p&gt;// Generate once, store securely&lt;br&gt;
const { publicKey, secretKey } = generateKeyPair('ML-DSA-65');&lt;/p&gt;

&lt;p&gt;// Sign (at login)&lt;br&gt;
const token = sign(&lt;br&gt;
  { sub: 'user_42', role: 'admin' },&lt;br&gt;
  secretKey,&lt;br&gt;
  {&lt;br&gt;
    algorithm:  'ML-DSA-65',&lt;br&gt;
    expiresIn:  '1h',&lt;br&gt;
    issuer:     'auth.myapp.com',&lt;br&gt;
    audience:   'api.myapp.com',&lt;br&gt;
  }&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;// Verify (in middleware)&lt;br&gt;
const { header, payload } = verify(token, publicKey, {&lt;br&gt;
  issuer:   'auth.myapp.com',&lt;br&gt;
  audience: 'api.myapp.com',&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;console.log(header.alg);    // 'ML-DSA-65'&lt;br&gt;
console.log(payload.sub);   // 'user_42'&lt;br&gt;
That's it. If you've used jsonwebtoken before, you already know how to use this.&lt;/p&gt;

&lt;p&gt;Supported Algorithms&lt;br&gt;
All four are NIST-standardized:&lt;br&gt;
AlgorithmStandardQuantum SecurityBest For&lt;br&gt;
&lt;strong&gt;ML-DSA-44 FIPS 20464-bit&lt;/strong&gt; QIoT / constrained&lt;br&gt;
&lt;strong&gt;ML-DSA-65 FIPS 20496-bit&lt;/strong&gt; QGeneral use (recommended)&lt;br&gt;
&lt;strong&gt;ML-DSA-87 FIPS 204128-bit&lt;/strong&gt; QHigh security / government&lt;br&gt;
&lt;strong&gt;SLH-DSA-SHA2-128s FIPS 20564-bit&lt;/strong&gt; QConservative / hash-based&lt;br&gt;
ML-DSA is CRYSTALS-Dilithium under its standardized name. SLH-DSA is SPHINCS+.&lt;/p&gt;

&lt;p&gt;TypeScript Support&lt;br&gt;
Full TypeScript types are included — no @types/ package needed:&lt;br&gt;
typescript&lt;br&gt;
import {&lt;br&gt;
  generateKeyPair,&lt;br&gt;
  sign,&lt;br&gt;
  verify,&lt;br&gt;
  type Algorithm,&lt;br&gt;
  type KeyPair,&lt;br&gt;
  type SignOptions,&lt;br&gt;
  TokenExpiredError,&lt;br&gt;
  SignatureError,&lt;br&gt;
} from '@pq-jwt/core';&lt;/p&gt;

&lt;p&gt;const alg: Algorithm = 'ML-DSA-65';&lt;br&gt;
const kp: KeyPair = generateKeyPair(alg);&lt;/p&gt;

&lt;p&gt;const opts: SignOptions = {&lt;br&gt;
  algorithm:  'ML-DSA-65',&lt;br&gt;
  expiresIn:  '8h',&lt;br&gt;
  issuer:     'auth.myapp.com',&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;const token: string = sign({ userId: 'u1' }, kp.secretKey, opts);&lt;/p&gt;

&lt;p&gt;try {&lt;br&gt;
  const { payload } = verify(token, kp.publicKey, { issuer: 'auth.myapp.com' });&lt;br&gt;
  console.log(payload.userId); // 'u1'&lt;br&gt;
} catch (e) {&lt;br&gt;
  if (e instanceof TokenExpiredError) { /* 401 &lt;em&gt;/ }&lt;br&gt;
  if (e instanceof SignatureError)    { /&lt;/em&gt; 403 */ }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Express.js Middleware Example&lt;br&gt;
javascriptimport { verify, importKey, TokenExpiredError, SignatureError } from '@pq-jwt/core';&lt;/p&gt;

&lt;p&gt;const PUBLIC_KEY = importKey(process.env.PQ_PUBLIC_KEY);&lt;/p&gt;

&lt;p&gt;function pqAuth(req, res, next) {&lt;br&gt;
  const auth = req.headers.authorization;&lt;br&gt;
  if (!auth?.startsWith('Bearer '))&lt;br&gt;
    return res.status(401).json({ error: 'Missing token' });&lt;/p&gt;

&lt;p&gt;try {&lt;br&gt;
    const { payload } = verify(auth.slice(7), PUBLIC_KEY, {&lt;br&gt;
      issuer:   'auth.myapp.com',&lt;br&gt;
      audience: 'api.myapp.com',&lt;br&gt;
    });&lt;br&gt;
    req.user = payload;&lt;br&gt;
    next();&lt;br&gt;
  } catch (e) {&lt;br&gt;
    if (e instanceof TokenExpiredError) return res.status(401).json({ error: 'Token expired' });&lt;br&gt;
    if (e instanceof SignatureError)    return res.status(403).json({ error: 'Invalid signature' });&lt;br&gt;
    res.status(400).json({ error: 'Bad token' });&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;app.get('/profile', pqAuth, (req, res) =&amp;gt; res.json(req.user));&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Shor's Algorithm Doesn't Break This&lt;/strong&gt;&lt;br&gt;
This is the key technical point.&lt;br&gt;
&lt;strong&gt;Shor's algorithm&lt;/strong&gt; exploits the periodic structure of cyclic group problems — integer rings (RSA) and elliptic curve groups (ECDSA). It finds the period of a function defined over those groups, which lets it factor numbers or solve discrete logs efficiently.&lt;br&gt;
ML-DSA is based on Module-LWE (Learning With Errors) — a lattice problem. Lattice problems have no group-periodic structure. Shor's algorithm provides zero speedup against them. This was acknowledged in Shor's original 1994 paper.&lt;br&gt;
Grover's algorithm does apply — it gives a square-root speedup against signature verification. That's why ML-DSA-65 is parameterized for 192-bit classical security: under Grover it reduces to 96-bit quantum security, which is still computationally infeasible.&lt;/p&gt;

&lt;p&gt;Performance Numbers&lt;br&gt;
Benchmarked on Node.js v22, single thread:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8h8bn4b5pskzwj08bbnk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8h8bn4b5pskzwj08bbnk.png" alt=" " width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The trade-off is real. Tokens are larger (~4.5 KB vs ~0.5 KB), and signing is slower (~11 ms vs &amp;lt; 1 ms). For login flows and session tokens, this is imperceptible to users. For high-frequency per-request signing, you'd want to benchmark your specific workload.&lt;br&gt;
SLH-DSA signing at 5+ seconds is only suitable for low-frequency operations like initial authentication — not per-request refresh.&lt;/p&gt;

&lt;p&gt;What It's Built On&lt;br&gt;
The cryptographic implementation is &lt;a class="mentioned-user" href="https://dev.to/noble"&gt;@noble&lt;/a&gt;/post-quantum by Paul Miller — zero native dependencies, independently audited, TypeScript-native. I built the JWT layer on top of it.&lt;br&gt;
The cryptographic primitives themselves are not mine to invent. My job was to make them as easy to use as jsonwebtoken. That's the whole value of the library.&lt;/p&gt;

&lt;p&gt;Security Properties (Tested)&lt;br&gt;
From the test suite:&lt;/p&gt;

&lt;p&gt;✓ 50/50 single-bit signature flips detected&lt;br&gt;
✓ 20/20 payload substitution attempts caught&lt;br&gt;
✓ Algorithm confusion (alg: none) rejected&lt;br&gt;
✓ Cross-key attacks rejected&lt;br&gt;
✓ 10,000 random forgery attempts — 0 succeeded&lt;br&gt;
✓ 61/61 tests passing (TypeScript strict + runtime)&lt;/p&gt;

&lt;p&gt;The Honest Limitations&lt;br&gt;
I want to be direct about what this is and isn't.&lt;br&gt;
&lt;strong&gt;What it is&lt;/strong&gt;: A production-usable library for post-quantum JWT signing, built on &lt;strong&gt;NIST-standardized algorithms, with full TypeScript support, typed errors, and a clean **API.&lt;br&gt;
**What it isn't&lt;/strong&gt;: A mathematically proven unbreakable system. No such thing exists in practical cryptography. The &lt;strong&gt;security of ML-DSA reduces to the hardness of Module-LWE, which has survived 15+ years of public cryptanalysis.&lt;/strong&gt; The same category of hardness assumption underpins every cryptographic system deployed in production today.&lt;br&gt;
&lt;strong&gt;What to never put in the payload&lt;/strong&gt;: Passwords, private keys, sensitive data. The JWT payload is base64url-encoded, not encrypted. Anyone can decode it. Put user IDs, roles, and session metadata — not secrets. This is true of all JWTs, not just this library.&lt;/p&gt;

&lt;p&gt;Getting Started&lt;br&gt;
bash npm install @pq-jwt/core&lt;br&gt;
Full working demo (Express + MongoDB + TypeScript):&lt;br&gt;
→ &lt;a href="https://github.com/pq-jwt/PQ-JWT-Demo" rel="noopener noreferrer"&gt;https://github.com/pq-jwt/PQ-JWT-Demo&lt;/a&gt;&lt;br&gt;
Source code:&lt;br&gt;
→ &lt;a href="https://github.com/pq-jwt/PQ-JWT" rel="noopener noreferrer"&gt;https://github.com/pq-jwt/PQ-JWT&lt;/a&gt;&lt;br&gt;
-- &lt;a href="https://github.com/pq-jwt/PQ-JWT/blob/main/README.md" rel="noopener noreferrer"&gt;https://github.com/pq-jwt/PQ-JWT/blob/main/README.md&lt;/a&gt;&lt;br&gt;
npm:&lt;br&gt;
→ &lt;a href="https://www.npmjs.com/package/@pq-jwt/core" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@pq-jwt/core&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're working on authentication infrastructure and want to contribute, issues and PRs are open at github.com/pq-jwt/PQ-JWT&lt;/p&gt;

&lt;p&gt;Built by Sachin Ruhil. Published May 2026.&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>security</category>
      <category>cryptography</category>
    </item>
  </channel>
</rss>
