d3ro-voice/scripts/ci/verify-release-evidence-key-pair.mjs
2026-08-29 18:33:45 +09:00

50 lines
1.7 KiB
JavaScript

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)
}