Files
ycode/scripts/updates/init-key.mjs
T

82 lines
2.5 KiB
JavaScript

import fsp from 'node:fs/promises'
import path from 'node:path'
import {
DEFAULT_PRIVATE_KEY,
DEFAULT_PUBLIC_KEY,
assertOutsideRepo,
generateEd25519KeyPair,
parseOptions,
publicKeyFingerprint,
resolvePath,
runProcess,
usageError,
writeFileAtomic
} from './lib.mjs'
const usage = `Usage:
node scripts/updates/init-key.mjs [--private-key <path>] [--public-key <path>] [--force]
The private key must remain outside the repository. The default private key is:
${DEFAULT_PRIVATE_KEY}
`
function secureWindowsAcl(filename, isDirectory = false) {
if (process.platform !== 'win32') return
const identity = runProcess('whoami', []).trim()
if (!identity) throw new Error('Unable to determine the current Windows identity')
const permission = isDirectory ? `${identity}:(OI)(CI)F` : `${identity}:(F)`
runProcess('icacls', [filename, '/inheritance:r', '/grant:r', permission])
}
async function pathExists(filename) {
try {
await fsp.access(filename)
return true
} catch {
return false
}
}
async function main() {
const options = parseOptions(process.argv.slice(2), {
values: ['private-key', 'public-key'],
booleans: ['force']
})
const privateKeyPath = await assertOutsideRepo(
resolvePath(options['private-key'] || DEFAULT_PRIVATE_KEY),
'Private key'
)
const publicKeyPath = resolvePath(options['public-key'] || DEFAULT_PUBLIC_KEY)
if (!options.force) {
if (await pathExists(privateKeyPath)) throw new Error(`Refusing to overwrite existing private key: ${privateKeyPath}`)
if (await pathExists(publicKeyPath)) throw new Error(`Refusing to overwrite existing public key: ${publicKeyPath}`)
}
const keyPair = generateEd25519KeyPair()
const directory = path.dirname(privateKeyPath)
await fsp.mkdir(directory, { recursive: true, mode: 0o700 })
try {
await fsp.chmod(directory, 0o700)
} catch {
// Windows ACLs are applied below.
}
secureWindowsAcl(directory, true)
await writeFileAtomic(privateKeyPath, keyPair.privatePem, 0o600)
try {
await fsp.chmod(privateKeyPath, 0o600)
} catch {
// Windows ACLs are applied below.
}
secureWindowsAcl(privateKeyPath)
await writeFileAtomic(publicKeyPath, keyPair.publicPem, 0o644)
console.log(`Private key: ${privateKeyPath}`)
console.log(`Public key: ${publicKeyPath}`)
console.log(`Public key SHA-256 fingerprint: ${publicKeyFingerprint(keyPair.publicPem)}`)
console.log('Private key material was not printed. Back up the private key separately before rotating it.')
}
main().catch((error) => usageError(usage, error))