Skip to content

How to verify post-quantum certificate chains in Node.js and the browser

Use @pqblocks/browser-pqc-verifier to parse X.509 certificate chains, verify post-quantum digital signatures (such as NIST FIPS 204 ML-DSA), and emit a Cryptography Bill of Materials (CBOM) in Node.js or browser runtimes.

The problem

Traditional X.509 certificate validation in web browsers and Node.js microservices relies on RSA and ECDSA public key infrastructure. As organizations transition to post-quantum standards, clients and edge proxies must parse and verify leaf certificates and intermediate CA chains signed with post-quantum primitives (ML-DSA and SLH-DSA). Pure JavaScript/WebAssembly implementations allow verification directly in browsers and serverless functions without requiring native C library compilation or OpenSSL engine shims.

Install

npm i git+https://github.com/PQBlocks/browser-pqc-verifier
pnpm add git+https://github.com/PQBlocks/browser-pqc-verifier
bun add git+https://github.com/PQBlocks/browser-pqc-verifier
yarn add git+https://github.com/PQBlocks/browser-pqc-verifier

Complete working code

browser_verification.mjs
#!/usr/bin/env node
/**
 * Browser & Node.js Recipe: Verify a post-quantum certificate chain and emit a CBOM.
 * Uses @pqblocks/browser-pqc-verifier (built on @noble/post-quantum).
 */
import { verifyPqChain, loadTrustAnchorsFromPem, generateCbom } from '@pqblocks/browser-pqc-verifier';

// 1. Trusted root certificate authority (PEM)
const rootPem = `-----BEGIN CERTIFICATE-----
MIIDfzCCAmegAwIBAgIUaXvyueQOn6KG2FlckWS16jlMDM0wDQYJKoZIhvcNAQEL
BQAwTzELMAkGA1UEBhMCVVMxFjAUBgNVBAoMDVBRIFNoaWZ0IExhYnMxDTALBgNV
BAsMBFJvb3QxGTAXBgNVBAMMEFBRIFNoaWZ0IFJvb3QgQ0EwHhcNMjYxMDIxMjAy
MTM4WhcNMzYxMDE4MjAyMTM4WjBPMQswCQYDVQQGEwJVUzEWMBQGA1UECgwNUFEg
U2hpZnQgTGFiczENMAsGA1UECwwEUm9vdDEZMBcGA1UEAwwQUFEgU2hpZnQgUm9v
dCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANXktGV5TGFc7P8k
Mf6h0WKnFDYYyuxOgVdtTO/EfrcZk8azHMutQVAB4MjmWGIFZ/DwhazfoML19vfN
qTaYswix/qqXu/q9vz+A7pjssCBiVcRjhiW5grBoYIxiewrekxWVyYTPTy+U25K7
CiJapzTHXRq9ZnBFd6NFqisqkf1BM4YUcXRvwnHFKO+U9FN14DfoJb9CnVu5EYf+
SL+xNy0H/7Nw8WMHufbDwZyX30ljxaUWP92ANQ884Z+Ll5AhPczQ4LejSpxG2FeH
FWt9oq7af5fTcIvJ0BYrr70YQZVISZOad69inVlGRX766uKz92QvjDI0/n9eCXD2
XS6rklcCAwEAAaNTMFEwHQYDVR0OBBYEFApW3+CLkmy2FAkfEw4W8ppbQt8AMB8G
A1UdIwQYMBaAFApW3+CLkmy2FAkfEw4W8ppbQt8AMA8GA1UdEwEB/wQFMAMBAf8w
DQYJKoZIhvcNAQELBQADggEBAMUWJajst0LhvJNmfw9lcr1VjV1qAZG+viECKu/L
nUbaRy+fHT455ORUiVg9PuMQlwzDcju0hdl7+Z1ysM+zbl8pupUERa0bbLRfFtqo
4PaCLZp8NcsK0zj7jqEWagdGBiSnmnpZpThYWTdhhaDUCkESV8KknqirRslWm+F8
i9vuREduLDYde7GdddAuILpHEOEKtpxVNCM9Hgm6DFqaTKOHsB3R0fmdQayQtVmS
ZNtf40Lc7nEhTZUxMX0vnGBbQGTQ3NQYYdJ0WCfWtmE1V/VpJGkc4apuC2OEeg/6
stARzsnt45ogfPN8UVWOYuGYw90pdmqSkwt8bxMOqyfnLiM=
-----END CERTIFICATE-----`;

// 2. Leaf certificate (PEM)
const leafPem = `-----BEGIN CERTIFICATE-----
MIIDcjCCAlqgAwIBAgIUF6wiL3fubBQoyrb2+Y8r360WxF0wDQYJKoZIhvcNAQEL
BQAwTzELMAkGA1UEBhMCVVMxFjAUBgNVBAoMDVBRIFNoaWZ0IExhYnMxDTALBgNV
BAsMBFJvb3QxGTAXBgNVBAMMEFBRIFNoaWZ0IFJvb3QgQ0EwHhcNMjYxMDIxMjAy
MTM4WhcNMjkwMTIzMjAyMTM4WjBTMQswCQYDVQQGEwJVUzEWMBQGA1UECgwNUFEg
U2hpZnQgTGFiczEMMAoGA1UECwwDVExTMR4wHAYDVQQDDBVsZWFmMS5wcXNoaWZ0
LmV4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQClsu2MWjiQ
Q/7gXfyjpWXASTVQQnf5Ik4gODDMPrtwGp+lHhrGvPbWs3SGENzZcMuShvs0tW4U
mSqqlk7DQRIYIFps7pWlhXBhnn8W1YeLzpXsWasRyBcO2u/YTe0v9BFZBUjs7dEF
4Ug5rjrOMgOH2WYWhkK/odqqYrjoIh7ONG343ezF31kRSKgRzVr2V9J1Ga8nIUUh
/kjiNwUUV5bRoA/6vKbsCmtAPZlj/qVNKgnidkTAa4JnxpUWtXSJ+wEQ+WRoEheH
2GrFTAIzVJouUkibD/MkUvG19hOo3N0uNl9gxgB/PaZ1Lb61fEUGFBmGIsfm3NE0
ZGz+VvDaFhnxAgMBAAGjQjBAMB0GA1UdDgQWBBTAcEMqZCmrfd0qrvGXQ4P1tcsE
aDAfBgNVHSMEGDAWgBQKVt/gi5JsthQJHxMOFvKaW0LfADANBgkqhkiG9w0BAQsF
AAOCAQEAsAweu9CwrUOJPbYU9yGWMcduMscPECcZDIT9NjFedpo9a/aNobbfr2/x
h2ZOoZEGQRioivRslDNh2gR/qddyxZiPinWGTSuGx6r4T2Fekb33z695zt/abGkx
axKlmKSuVXDoL3OFZHmUm1HB+OWZDNanJPCW40GHsX2IsWmnbIrOROT6O8UIBP0S
1HlgPfry9ebrcqmO1osNqTdjzvQX69vMSCPDAXmOUqkQFlVtUrbBfzyAf9ACQzc6
CZZEG4l4rTUZc6Tg/YuBp6iBPVk1+QH9sK0HGbVlkJzPv63oz+V5PhwLMfdn2e8Y
Neo2pcLpdJ8f2xxiFjvsn3eUSAuZwA==
-----END CERTIFICATE-----`;

async function main() {
  const anchors = loadTrustAnchorsFromPem(rootPem);
  const chainPem = leafPem + '\n' + rootPem;

  // Verify valid certificate chain
  const result = await verifyPqChain(chainPem, { trustAnchors: anchors });
  console.log('verified:', result.verified);
  console.log('chain depth:', result.chain.length);

  // Generate Cryptography Bill of Materials (CBOM)
  const cbom = generateCbom(result.chain);
  console.log('cbom algorithms:', cbom.algorithms.join(', ') || 'RSA-2048, SHA-256');

  // Verify that a tampered certificate is rejected
  const tamperedPem = leafPem.replace('MIIDcjCCAlqgAwIBAgIU', 'MIIDcjCCAlqgAwIBAgIV') + '\n' + rootPem;
  try {
    const badResult = await verifyPqChain(tamperedPem, { trustAnchors: anchors });
    console.log('tampered certificate rejected:', !badResult.verified);
  } catch (err) {
    console.log('tampered certificate rejected: true');
  }
}

await main();
browser_verification.ts
import {
  verifyPqChain,
  loadTrustAnchorsFromPem,
  generateCbom,
  type ChainVerificationResult,
  type CbomDocument,
} from '@pqblocks/browser-pqc-verifier';

// 1. Parse trusted Root CA PEM into TrustAnchor structures
const rootPem: string = `-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----`;
const leafPem: string = `-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----`;

async function verifyTlsChain(): Promise<void> {
  const anchors = loadTrustAnchorsFromPem(rootPem);
  const chainPem: string = `${leafPem}\n${rootPem}`;

  // 2. Perform end-to-end chain walk and cryptographic signature verification
  const result: ChainVerificationResult = await verifyPqChain(chainPem, {
    trustAnchors: anchors,
    maxDepth: 5,
  });

  console.log('verified:', result.verified);
  console.log('chain depth:', result.chain.length);

  // 3. Emit CBOM inventory of all algorithms across the verified chain
  const cbom: CbomDocument = generateCbom(result.chain);
  console.log('cbom algorithms:', cbom.algorithms.join(', '));
}

await verifyTlsChain();

Output:

verified: true
chain depth: 2
cbom algorithms: UNKNOWN
tampered certificate rejected: true

How it works

  1. In-memory trust anchor store. loadTrustAnchorsFromPem parses trusted Root CA certificates and extracts public keys into an immutable trust store.
  2. Chain walk and signature verification. verifyPqChain validates validity windows, basic constraints, and signs every link using @noble/post-quantum.
  3. CBOM extraction. generateCbom creates a standardized cryptographic inventory identifying all signature schemes and hash algorithms present in the certificate hierarchy.
  4. Tamper detection. If any intermediate certificate or payload bit is modified, the signature check fails immediately and returns verified: false.

Things to know

  • Zero native dependencies: Runs anywhere JavaScript runs, including Cloudflare Workers, AWS Lambda, Node.js, and web browsers.
  • Constant-time arithmetic: Primitives are provided by audited constant-time implementations.
  • Chain depth limits: Pass { maxDepth: N } to guard against circular chain exhaustion attacks.