Indexer for SMVA save files
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.
 
smva-indexer/sign-bundle.mjs

62 lines
2.5 KiB

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