v3.20.0 (review fixes): the PoR decode uses the effective counter; the PWA fallback matches the server's seed

Two boundary flaws found reviewing v3.20.0:

- /api/send-ota decoded the PoR with the request's `cntr`, not the counter
  the packet was actually built with.  With the new seeding an omitted
  counter is the normal case, so a warning ENVELOPE's verdict - notably the
  live card's cntr_low PoR - could fail its RC/CC check and be lost (the
  success and the failure path are both fixed).  Tests pin the decode counter
  on both paths; the failure path also echoes the effective `cntr`.
- the PWA's effective counter (preview / verify / the older-server fallback)
  fell back to the preset's first keyset and then 1, while the server's empty
  seed builds counter 0 (no preset / kvn 0 / an unknown keyset).  A manual
  form or a keyless packet then produced a spurious "Verify vs pySim"
  MISMATCH and a preview that differed from the delivered packet.
  spPresetCounterValue()/spCntrEffective() now mirror _preset_counter_seed()
  exactly: the packet's matched keyset, else 0.
- the `OTA SEND: counter not tracked` line reflects the effective counter (a
  seeded value is logged too); docs/api.md records that the seed uses the
  packet's keyset whatever SPI1.b5b4 says.

Tests: 823 frontend + 737 Python; sw cache simple-v394.
This commit is contained in:
2026-10-05 22:37:28 +03:00
parent 85cc4b9535
commit bf6730f028
6 changed files with 120 additions and 14 deletions
+9 -6
View File
@@ -8583,10 +8583,12 @@ function spPresetCounterValue() {
if (idx < 0) return '';
const kvn = spKeysetKvnOf((document.getElementById('sp-kic-hex') || {}).value) ||
spKeysetKvnOf((document.getElementById('sp-kid-hex') || {}).value) || 0;
// the same fallback cardsApplyFields uses: the matching keyset, else the
// preset's first one (an undefined duplicate would otherwise leave the
// placeholder empty)
const ks = (kvn && spKeysetFor(cards[idx], kvn)) || spKeysetList(cards[idx])[0] || null;
// Only a keyset the packet's KIc/KID actually resolve - exactly the value
// the server's _preset_counter_seed() uses (the body carries the same two
// bytes). No preset / kvn 0 / an unknown keyset = no seed: the packet
// then carries counter 0, and neither the preview nor the pySim reference
// may show a value the send would not use.
const ks = kvn ? spKeysetFor(cards[idx], kvn) : null;
return (ks && ks.cntr) || '';
}
@@ -8598,11 +8600,12 @@ function spCntrPlaceholder() {
}
// The counter the packet is built with (preview / verify): the typed
// override, else the preset's next value, else the historic 1 default.
// override, else the preset's next value, else 0 - the same value the
// server's empty seed puts into the packet (v3.20.0).
function spCntrEffective() {
const el = document.getElementById('sp-cntr');
const typed = ((el && el.value) || '').replace(/[^0-9a-fA-F]/g, '');
const v = typed || spPresetCounterValue() || '1';
const v = typed || spPresetCounterValue() || '0';
return v.toUpperCase().padStart(10, '0').slice(0, 10);
}