import { mkdtempSync, writeFileSync, readFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; import { spawnSync } from 'node:child_process'; import { createHash, createPublicKey, verify } from 'node:crypto'; const temp = mkdtempSync(join(tmpdir(), 'fieldlens-signing-')); try { if (!process.env.TAURI_SIGNING_PRIVATE_KEY) throw new Error('missing key'); const probe = join(temp, 'probe.txt'); writeFileSync(probe, 'FieldLens updater signing preflight\n', { mode: 0o600 }); const result = spawnSync(process.execPath, [resolve('node_modules/@tauri-apps/cli/tauri.js'), 'signer', 'sign', probe], { env: process.env, stdio: 'pipe', timeout: 30000, }); if (result.status !== 0) throw new Error('signing failed'); const config = JSON.parse(readFileSync('src-tauri/tauri.conf.json', 'utf8')); const publicLines = Buffer.from(config.plugins.updater.pubkey, 'base64').toString().trim().split(/\r?\n/); const signatureLines = Buffer.from(readFileSync(probe + '.sig', 'utf8').trim(), 'base64').toString().trim().split(/\r?\n/); const pub = Buffer.from(publicLines[1], 'base64'); const sig = Buffer.from(signatureLines[1], 'base64'); if (pub.length !== 42 || sig.length !== 74 || !pub.subarray(2, 10).equals(sig.subarray(2, 10))) throw new Error('key mismatch'); const key = createPublicKey({ key: Buffer.concat([Buffer.from('302a300506032b6570032100', 'hex'), pub.subarray(10)]), format: 'der', type: 'spki', }); const algorithm = sig.subarray(0, 2).toString(); if (!['ED', 'Ed'].includes(algorithm)) throw new Error('unsupported signature'); const message = readFileSync(probe); const signed = algorithm === 'ED' ? createHash('blake2b512').update(message).digest() : message; if (!verify(null, signed, key, sig.subarray(10))) throw new Error('invalid signature'); if (!signatureLines[2].startsWith('trusted comment: ')) throw new Error('invalid comment'); const comment = Buffer.from(signatureLines[2].slice('trusted comment: '.length)); if (!verify(null, Buffer.concat([sig.subarray(10), comment]), key, Buffer.from(signatureLines[3], 'base64'))) throw new Error('invalid trusted comment'); console.log('Updater signing preflight passed: key, password and public key match.'); } catch { console.error('Updater signing preflight failed: check the signing key/password and configured public key.'); process.exitCode = 1; } finally { rmSync(temp, { recursive: true, force: true }); }