import { createHash, createPrivateKey, createPublicKey, sign, timingSafeEqual, verify, } from 'node:crypto' import { readFileSync } from 'node:fs' const args = process.argv.slice(2) const privateKey = createPrivateKey(readFileSync(option('--private-key'))) const publicKey = createPublicKey(readFileSync(option('--public-key'))) if (privateKey.asymmetricKeyType !== 'ed25519' || publicKey.asymmetricKeyType !== 'ed25519') { fail('Both release-evidence keys must be Ed25519.') } const derivedPublic = createPublicKey(privateKey).export({ type: 'spki', format: 'der' }) const suppliedPublic = publicKey.export({ type: 'spki', format: 'der' }) if (derivedPublic.length !== suppliedPublic.length || !timingSafeEqual(derivedPublic, suppliedPublic)) { fail('Release-evidence private and public keys do not match.') } const message = Buffer.from('d3ro-release-evidence-key-pair-check-v1') const signature = sign(null, message, privateKey) if (!verify(null, message, publicKey, signature)) fail('Release-evidence signature round trip failed.') const keyId = createHash('sha256').update(suppliedPublic).digest('hex') const expectedKeyId = option('--expected-key-id') if (keyId !== expectedKeyId) fail(`Release-evidence key ID mismatch: ${keyId}`) process.stdout.write(`${JSON.stringify({ ok: true, algorithm: 'Ed25519', keyId, signatureRoundTrip: true, }, null, 2)}\n`) function option(name) { const index = args.indexOf(name) const value = index === -1 ? undefined : args[index + 1] if (!value || value.startsWith('--')) fail(`${name} is required.`) return value } function fail(message) { process.stderr.write(`[release-evidence-key] ${message}\n`) process.exit(1) }