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
+4 -1
View File
@@ -514,7 +514,10 @@ the counter that was sent, advanced by one when the card accepted the packet.
- with `apdu` (the server builds the packet) a missing `cntr` is seeded from
the named preset's keyset (the one the packet's KIc/KID resolve to); the
effective value is echoed back as `cntr`, so a caller that omits it learns
what was used;
what was used. The seed uses the packet's keyset whatever `SPI1.b5b4`
says — with no counter check the CNTR field is spec-ignored, nothing is
advanced or persisted, and the `OTA SEND: counter not tracked` line names
it;
- an explicit `cntr` always wins — the packet uses it verbatim (a hand send,
the counter probe or a pre-built `sp` may deliberately pick one);
- the store only moves **forward**: an explicit value (or its next counter)