// Verifies the trust-root pinning fix (Residual-1 + Residual-2 from audit doc 5).
import { HSM, hex, sha256 } from './hsm-core.mjs';

let PASS=0, FAIL=0; const fails=[];
function assert(c,n,d=''){ if(c){PASS++;console.log(`  ✓ ${n}${d?'  ['+d+']':''}`);}else{FAIL++;fails.push(n);console.log(`  ✗ ${n}${d?'  ['+d+']':''}`);} }
async function fresh(seed){ const h=new HSM(seed!==undefined?{seed}:{}); await h.provision(); return h; }

console.log('═══ TRUST-ROOT PINNING VERIFICATION ═══\n');

// ── Residual-2: attacker re-keys the anchor with their own keypair ──
console.log('── Residual-2: adversarial anchor re-key ──');
{
  const device = await fresh(0x1111);
  for (let i=0;i<3;i++) await device.audit_sign({event:'op-'+i}, null);
  const { traceJsonl, anchor } = await device.exportAnchoredTrace();
  const pinned = device.identityCertHistory[0].attestationPubSpki;

  // Genuine: pinned identity matches → adversarial-forgery-resistant + verified
  const genuine = await device.verifyTrace(traceJsonl, anchor, { pinnedAttestationPubSpki: pinned });
  assert(genuine.verified === true, 'genuine trace + correct pin verifies');
  assert(genuine.trustModel === 'adversarial-forgery-resistant',
    'genuine trace reports adversarial-forgery-resistant', genuine.trustModel);
  assert(genuine.identityPinned === true, 'genuine trace identity pinned');

  // Attacker: tamper a sign event, recompute count/sha, re-sign anchor with OWN P-384 key
  const attacker = await fresh(0x9999); // attacker's own device → own keypair
  const lines = traceJsonl.split('\n').map(l=>JSON.parse(l));
  // modify a benign field in an event to simulate tampering then "fix" the chain
  const forgedTrace = traceJsonl + '\n' + JSON.stringify({type:'note', injected:'attacker-added'});
  const forgedAnchor = await attacker._buildTraceAnchor(forgedTrace);
  // forgedAnchor.attestationPubSpki is the ATTACKER's key, signed by attacker.
  // Without a pin, the old code would import the attacker key and return verified:true.

  // (a) No pin supplied, attacker is unknown to verifier → trust downgraded, NOT resistant
  const cleanVerifier = new HSM(); // no identity history
  const noPin = await cleanVerifier.verifyTrace(forgedTrace, forgedAnchor);
  assert(noPin.trustModel === 'integrity-and-same-session',
    'unpinned verifier downgrades trust (cannot resist adversary)', noPin.trustModel);
  assert(noPin.identityPinned === false, 'unpinned: identityPinned=false');

  // (b) Pin the GENUINE device identity → attacker's anchor key mismatches → FORGERY DETECTED
  const pinnedCheck = await cleanVerifier.verifyTrace(forgedTrace, forgedAnchor,
    { pinnedAttestationPubSpki: pinned });
  assert(pinnedCheck.verified === false,
    'attacker re-keyed anchor FAILS against pinned genuine identity');
  assert(pinnedCheck.mismatches.some(m=>m.type==='ATTESTATION_IDENTITY_MISMATCH'),
    'forgery flagged as ATTESTATION_IDENTITY_MISMATCH');
}

// ── Residual-1: foreign-trace audit sigs verify off the trace's OWN key ──
console.log('\n── Residual-1: foreign-trace verification ──');
{
  // Device A produces a trace+anchor, then we re-provision (fresh keys) → instance
  // key no longer matches the trace. Old code marked all sigs invalid.
  const deviceA = await fresh(0x2222);
  for (let i=0;i<3;i++) await deviceA.audit_sign({event:'A-'+i}, null);
  const { traceJsonl, anchor } = await deviceA.exportAnchoredTrace();
  const pinnedA = deviceA.identityCertHistory[0].attestationPubSpki;

  // Re-provision deviceA → new keys; its _exportedSigningPubSpki changes
  const oldSigning = hex(deviceA._exportedSigningPubSpki);
  await deviceA.reset();  // reset() internally re-provisions with fresh keys
  const newSigning = hex(deviceA._exportedSigningPubSpki);
  assert(oldSigning !== newSigning, 'instance signing key changed after re-provision');

  // Verify the OLD trace. Audit sigs must verify off the trace-embedded key,
  // NOT deviceA's current instance key.
  const v = await deviceA.verifyTrace(traceJsonl, anchor, { pinnedAttestationPubSpki: pinnedA });
  assert(v.signaturesChecked.keySource === 'trace-embedded',
    'audit sigs verified using trace-embedded key', v.signaturesChecked.keySource);
  assert(v.signaturesChecked.invalid === 0 && v.signaturesChecked.valid >= 3,
    'all foreign-trace audit signatures verify correctly',
    `${v.signaturesChecked.valid}/${v.signaturesChecked.total}`);
  assert(v.verified === true, 'foreign trace fully verifies with correct pin');
}

// ── Same-session auto-pin (self-test path) still works ──
console.log('\n── Same-session auto-pin ──');
{
  const h = await fresh(0x3333);
  for (let i=0;i<2;i++) await h.audit_sign({event:'s-'+i}, null);
  const { traceJsonl, anchor } = await h.exportAnchoredTrace();
  // No explicit pin: verifier auto-pins from its own identityCertHistory
  const v = await h.verifyTrace(traceJsonl, anchor);
  assert(v.verified === true, 'same-session trace auto-verifies');
  assert(v.identityPinned === true && v.trustModel === 'adversarial-forgery-resistant',
    'same-session auto-pins from own identity history', v.trustModel);
}

console.log(`\n═══ PINNING: ${PASS} PASS / ${FAIL} FAIL ═══`);
if(FAIL>0){fails.forEach(f=>console.log('  - '+f));process.exit(1);}
console.log('TRUST-ROOT PINNING VERIFIED — adversarial re-key detected, foreign traces verify correctly.');
