Files
9af9cb3dbc NFC: stop fourteen more Mifare Classic parsers rendering unread blocks (#1103)
Second tranche of #1098, after #1102. Keys-found and blocks-read are tracked by two
independent masks - mf_classic_set_key_found() writes a key into the sector trailer and never
touches block_read_mask - so a sector routinely has both keys known while its data blocks are
still zero-filled. Fourteen parsers presented those zeros as card data.

Each now uses mf_classic_parser_block_has_data() from #1102: the read mask when it can be
trusted, non-zero content when it cannot, because mf_classic_load() wipes the mask for any .nfc
saved without "Data format version: 2".

WashCity, Microel and MiZIP named a credit they never read, and decline outright once none of
them survives - their card number is the UID, which the card info screen already shows, so an
all-Unknown page would replace the Keys Found view with nothing. MiZIP additionally read block
10 to choose which of blocks 8 and 9 held the current credit, so an unread selector silently
picked block 8 and labelled it the balance.

Bip required its card-number block and names an unread balance or trip window, and gates each
ring-buffer slot as the loop reaches it - a slot that was never read is not the same as one a
card with fewer than three top-ups legitimately left empty, and conflating them either printed
a fabricated transaction or hid a real one.

CharlieCard picked its live balance sector by comparing two trip counters that may never have
been read; on zeros the tie-break chose sector 2 and the use count underflowed to 65535. That
decision now refuses to produce a sector it cannot justify. Its transaction history and passes
were rendered from sectors nothing verifies, and expiry names all three of its states rather
than dropping the line for two different reasons.

Saflok verified sector 1 and then decoded every field from blocks 1 and 2, which are sector 0 -
seventeen zero bytes decrypt into a Primary Programming Key. CSC has no key check at all and its
XOR checksum is satisfied by all zeros; its refill signature also selects the New Card branch,
which skips that checksum, so a used card read without sector 3 reported as new.

Banapass told the user to scan a clone at a cabinet on the strength of an unread block. H World
decoded a room from a block its key check does not cover, and called a card a standard room key
from a trailer nothing reads. The SKPPK, SevPPK and SZPPK ticket parsers paired a real departure
station with an empty ticket, and - worse - reported it through an existing message that blames
the ticket for not being issued, on a screen that has already replaced Keys Found.

SmartRider and Bambu had the opposite bug: both called mf_classic_is_block_read() directly, so
for a legacy save or a converted PM3 dump the wiped mask refused the card outright with every
byte present. Same fault #1102 repaired for Social Moscow.

Builds clean with fbt format and fbt lint. Not hardware-tested - none of these cards were
available - and the PR carries a manual QA table. The risk is asymmetric: for a block that was
never read there is no case that renders correctly today, since the parser was already printing
fabricated values.

Part of #1098

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 23:56:20 +03:00
..
2026-08-16 04:18:50 +03:00
2025-09-24 21:00:39 +04:00
2026-08-11 02:42:32 +03:00
2026-01-05 21:15:41 +03:00