import { createHash, generateKeyPairSync, sign, verify } from 'node:crypto' import { mkdirSync, rmSync, writeFileSync } from 'node:fs' import { dirname, resolve } from 'node:path' const args = process.argv.slice(2) const privateKeyPath = resolve(requiredOption('--private-key')) const publicKeyPath = resolve(requiredOption('--public-key')) if (privateKeyPath === publicKeyPath) fail('Private and public key paths must differ.') mkdirSync(dirname(privateKeyPath), { recursive: true }) mkdirSync(dirname(publicKeyPath), { recursive: true }) const { privateKey, publicKey } = generateKeyPairSync('ed25519') const probe = Buffer.from('d3ro-desktop-license-keypair-v1', 'utf8') const signature = sign(null, probe, privateKey) if (!verify(null, probe, publicKey, signature)) fail('Generated keypair self-check failed.') const privatePem = privateKey.export({ type: 'pkcs8', format: 'pem' }) const publicPem = publicKey.export({ type: 'spki', format: 'pem' }) let privateCreated = false try { writeFileSync(privateKeyPath, privatePem, { flag: 'wx', mode: 0o600 }) privateCreated = true writeFileSync(publicKeyPath, publicPem, { flag: 'wx', mode: 0o644 }) } catch (error) { if (privateCreated) rmSync(privateKeyPath, { force: true }) throw error } const keyId = createHash('sha256') .update(publicKey.export({ type: 'spki', format: 'der' })) .digest('hex') process.stdout.write(`${JSON.stringify({ ok: true, purpose: 'desktop-license-signing', algorithm: 'Ed25519', keyId, privateKeyPath, publicKeyPath, }, null, 2)}\n`) function requiredOption(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(`[desktop-license-keypair] ${message}\n`) process.exit(1) }