import * as crypto from 'node:crypto'; /** * Manages ACME replay nonces. * Each nonce is single-use: consumed on verification, fresh one issued with every response. */ export class NonceManager { private nonces = new Set(); private nonceQueue: string[] = []; private maxSize: number; constructor(maxSize = 10000) { this.maxSize = maxSize; } generate(): string { const nonce = crypto.randomBytes(16).toString('base64url'); if (this.nonces.size >= this.maxSize) { const oldest = this.nonceQueue.shift(); if (oldest) { this.nonces.delete(oldest); } } this.nonces.add(nonce); this.nonceQueue.push(nonce); return nonce; } consume(nonce: string): boolean { if (this.nonces.has(nonce)) { this.nonces.delete(nonce); return true; } return false; } }