- VERSION file as authoritative version source - export.mjs reads VERSION with package.json fallback - bw-ota.ps1 DryRun mode for safe testing - auto-setup.ps1 bumped to v3.2.0 (Phase 8 OTA)
51 lines
1.6 KiB
JavaScript
51 lines
1.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* verify-settings-sig.js — 验证 settings.json HMAC 签名是否一致
|
|
*
|
|
* 用法:
|
|
* node scripts/patches/verify-settings-sig.js # 退出码 0=ok / 1=mismatch
|
|
* node scripts/patches/verify-settings-sig.js --quiet # 仅退出码
|
|
*/
|
|
|
|
'use strict';
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const crypto = require('crypto');
|
|
|
|
const ROOT = path.join(__dirname, '..', '..');
|
|
const SETTINGS = path.join(ROOT, 'settings.json');
|
|
const SIG_FILE = path.join(ROOT, 'settings.json.sig');
|
|
const HMAC_KEY = path.join(ROOT, '.hmac-key');
|
|
const QUIET = process.argv.includes('--quiet');
|
|
|
|
function log(msg) { if (!QUIET) process.stdout.write(msg + '\n'); }
|
|
function err(msg) { process.stderr.write(msg + '\n'); }
|
|
|
|
if (!fs.existsSync(SIG_FILE)) {
|
|
err('[FAIL] signature file missing: ' + SIG_FILE);
|
|
process.exit(1);
|
|
}
|
|
if (!fs.existsSync(HMAC_KEY)) {
|
|
err('[FAIL] .hmac-key missing');
|
|
process.exit(1);
|
|
}
|
|
|
|
const expected = fs.readFileSync(SIG_FILE, 'utf8').trim();
|
|
const key = fs.readFileSync(HMAC_KEY, 'utf8').trim();
|
|
const content = fs.readFileSync(SETTINGS);
|
|
const actual = crypto.createHmac('sha256', key).update(content).digest('hex');
|
|
|
|
// timing-safe 比较
|
|
const ok = expected.length === actual.length &&
|
|
crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(actual, 'hex'));
|
|
|
|
if (ok) {
|
|
log('[OK] settings.json signature valid');
|
|
process.exit(0);
|
|
} else {
|
|
err('[FAIL] settings.json TAMPERED!');
|
|
err(' expected: ' + expected.slice(0, 16) + '...' + expected.slice(-16));
|
|
err(' actual: ' + actual.slice(0, 16) + '...' + actual.slice(-16));
|
|
process.exit(1);
|
|
}
|