You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
62 lines
2.5 KiB
62 lines
2.5 KiB
3 years ago
|
import {readFile, open} from "fs/promises"
|
||
|
import enquirer from "enquirer"
|
||
|
import {createPrivateKey, createSign} from "crypto"
|
||
|
import chalk from "chalk"
|
||
|
import {algorithm, signatureGuardEnd, signatureGuardStart} from "./signing-common.mjs"
|
||
|
const {Invisible} = enquirer
|
||
|
|
||
|
async function getPassword() {
|
||
|
return await new Invisible({
|
||
|
message: "Enter the passphrase for smva-indexer-release.key.pem:"
|
||
|
}).run()
|
||
|
}
|
||
|
|
||
|
async function decryptSecretKey(keyPath) {
|
||
|
return createPrivateKey({ key: await readFile(keyPath), passphrase: await getPassword() })
|
||
|
}
|
||
|
|
||
|
async function signBundle(bundlePath, destinationPath, keyPath) {
|
||
|
const bundle = await readFile(bundlePath)
|
||
|
if (!bundle.subarray(0, signatureGuardStart.length).equals(signatureGuardStart)) {
|
||
|
throw new Error("No placeholder for the signature guard start found")
|
||
|
}
|
||
|
const signatureLength = bundle.subarray(signatureGuardStart.length).indexOf(signatureGuardEnd)
|
||
|
if (signatureLength === -1) {
|
||
|
throw new Error("No placeholder for the signature guard end found")
|
||
|
}
|
||
|
const sign = createSign(algorithm)
|
||
|
const signedContent = bundle.subarray(signatureGuardStart.length + signatureLength + signatureGuardEnd.length)
|
||
|
const secretKey = await decryptSecretKey(keyPath)
|
||
|
sign.update(signedContent)
|
||
|
const signature = sign.sign(secretKey, 'base64')
|
||
|
|
||
|
let destination
|
||
|
try {
|
||
|
destination = await open(destinationPath, "w")
|
||
|
await destination.write(signatureGuardStart)
|
||
|
await destination.write(signature, null, "utf-8")
|
||
|
await destination.write(signatureGuardEnd)
|
||
|
await destination.write(signedContent)
|
||
|
} finally {
|
||
|
await destination?.close()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async function main() {
|
||
|
const [bundlePath, destinationPath, keyPath] = process.argv.slice(2)
|
||
|
if (!bundlePath || !destinationPath || !keyPath) {
|
||
|
process.stderr.write(chalk.red("Required arguments: bundlePath destinationPath keyPath\n"))
|
||
|
process.exit(1)
|
||
|
}
|
||
|
try {
|
||
|
await signBundle(bundlePath, destinationPath, keyPath)
|
||
|
process.stderr.write(chalk.cyan(`Successfully signed ${chalk.cyanBright.bold(bundlePath)} as ${chalk.cyanBright.bold(destinationPath)} with ${chalk.cyanBright.bold(keyPath)}\n`))
|
||
|
} catch (ex) {
|
||
|
process.stderr.write(chalk.red(`Failed to sign ${chalk.redBright.bold(bundlePath)} as ${chalk.redBright.bold(destinationPath)} with ${chalk.redBright.bold(keyPath)}: ${chalk.redBright.bold(ex)}\n`))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!global.main) {
|
||
|
global.main = true
|
||
|
main()
|
||
|
}
|