import { generateKeyPairSync, randomUUID } from 'node:crypto'; import { NODE_KEYSTORE_RECORD_SCHEMA, nodePublicKeyFingerprint, parseNodeKeyRecord, type NodeKeyRecord, } from './node-keystore-bridge.js'; const BASE64_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; export interface NodeKeyEnrollmentAdapter { encryptPrivateKeyPkcs8(privateKeyPkcs8: Buffer): Promise; isAvailable(): boolean; readRecord(): Promise; writeRecord(record: Readonly): Promise; } export interface PublicNodeKeyEnrollment { created: boolean; nodeId: string; nodeKeyFingerprint: string; nodeType: 'local-terminal'; publicKeyPem: string; registrationState: 'LOCAL_KEY_READY'; } function publicProjection(record: Readonly, created: boolean): Readonly { if (record.nodeType !== 'local-terminal') throw new Error('node_keystore_wrong_node_type'); return Object.freeze({ created, nodeId: record.nodeId, nodeKeyFingerprint: record.nodeKeyFingerprint, nodeType: record.nodeType, publicKeyPem: record.publicKeyPem, registrationState: 'LOCAL_KEY_READY', }); } export class EncryptedLocalNodeEnrollmentStore { constructor( private readonly adapter: NodeKeyEnrollmentAdapter, private readonly nodeIdFactory: () => string = () => `HL-LOCAL-${randomUUID().toUpperCase()}`, ) {} async status(): Promise | null> { const raw = await this.adapter.readRecord(); if (raw === null) return null; const record = parseNodeKeyRecord(raw); if (!record) throw new Error('node_keystore_record_invalid'); return publicProjection(record, false); } async ensureLocalTerminal(): Promise> { if (!this.adapter.isAvailable()) throw new Error('node_keystore_encryption_unavailable'); const existing = await this.status(); if (existing) return existing; let privateKeyPkcs8: Buffer | null = null; try { const { privateKey, publicKey } = generateKeyPairSync('ed25519'); privateKeyPkcs8 = privateKey.export({ format: 'der', type: 'pkcs8' }); const publicKeyPem = publicKey.export({ format: 'pem', type: 'spki' }).toString(); const nodeKeyFingerprint = nodePublicKeyFingerprint(publicKeyPem); const encryptedPrivateKeyPkcs8 = await this.adapter.encryptPrivateKeyPkcs8(privateKeyPkcs8); if (!nodeKeyFingerprint || !encryptedPrivateKeyPkcs8 || encryptedPrivateKeyPkcs8.length > 65_536 || !BASE64_PATTERN.test(encryptedPrivateKeyPkcs8)) { throw new Error('node_keystore_enrollment_failed'); } const record: Readonly = Object.freeze({ encryptedPrivateKeyPkcs8, nodeId: this.nodeIdFactory(), nodeKeyFingerprint, nodeType: 'local-terminal', publicKeyPem, schema: NODE_KEYSTORE_RECORD_SCHEMA, }); if (!parseNodeKeyRecord(record)) throw new Error('node_keystore_enrollment_failed'); await this.adapter.writeRecord(record); return publicProjection(record, true); } finally { privateKeyPkcs8?.fill(0); } } }