2026-04-27 04:25:52 +00:00

80 lines
2.4 KiB
JavaScript

'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 };