'use strict'; const fs = require('fs'); const crypto = require('crypto'); let _privateKey = null; let _publicKey = null; let _kid = null; /** * Lazy-loads or generates the EC P-256 key pair used for signing VHL CWTs. * * Key resolution order: * 1. PEM file at VHL_PRIVATE_KEY_FILE (if set and exists). * 2. Ephemeral key pair generated at startup (logged as warning). * * The kid (Key ID) is derived as the first 8 bytes of SHA-256 over the * DER-encoded public key, encoded as base64url. This matches the GDHCN * convention for referencing public keys in the trust registry. */ function loadKeys() { if (_privateKey) return; const keyFile = process.env.VHL_PRIVATE_KEY_FILE; if (keyFile) { if (fs.existsSync(keyFile)) { const pem = fs.readFileSync(keyFile); _privateKey = crypto.createPrivateKey({ key: pem, format: 'pem' }); _publicKey = crypto.createPublicKey(_privateKey); } else { console.warn(`[VHL] VHL_PRIVATE_KEY_FILE not found: ${keyFile}. Generating ephemeral key pair.`); } } else { console.warn('[VHL] VHL_PRIVATE_KEY_FILE not set. Generating ephemeral key pair (lost on restart).'); } if (!_privateKey) { const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }); _privateKey = privateKey; _publicKey = publicKey; } // kid = base64url(SHA-256(DER public key)[0..7]) const pubDer = _publicKey.export({ type: 'spki', format: 'der' }); _kid = crypto.createHash('sha256').update(pubDer).digest().slice(0, 8).toString('base64url'); } /** Returns the EC P-256 private KeyObject for signing. */ function getPrivateKey() { loadKeys(); return _privateKey; } /** Returns the EC P-256 public KeyObject for verification. */ function getPublicKey() { loadKeys(); return _publicKey; } /** * Returns the Key ID (kid) for use in CWT protected headers. * Derived from the public key fingerprint so verifiers can look up the key * in the GDHCN trust registry. */ function getKid() { loadKeys(); return _kid; } /** * Returns the public key in JWK format for publishing to a trust registry. * @returns {object} JWK representation of the public key. */ function getPublicJWK() { loadKeys(); return _publicKey.export({ format: 'jwk' }); } module.exports = { getPrivateKey, getPublicKey, getKid, getPublicJWK };