Seader can present the attached HID SAM to a host PC as a **USB CCID contact
smart-card reader**, so any PC/SC application can drive the SAM directly (e.g.
the PM3 SAM host tools). The SAM appears as the card in slot 0.
seader_mfc_transmit() lost its caller when the HF plugin took over the
read path; MIFARE Classic frames now go through plugin_hf_mfc_transmit()
in hf_interface_fal/hf.c. Remove it along with the three statics it was
the last user of: seader_require_worker(), seader_trace_mfc_bitbuffer()
and seader_trace_mfc_packed_frame(). Both trace helpers had a real and a
stubbed variant behind SEADER_ENABLE_TRACE_LOG, so all four go.
Nothing in the file uses bit_lib any more either, so its include goes
with them.
Claude-Session: https://claude.ai/code/session_016a4SuDfC8EkHo3MGGrRMod
plugin_hf_capture_sio() passed a fixed 32 bytes to append_picopass_sio()
no matter how long the response was. A short or truncated READ4 fed the
SIO whatever else happened to be sitting in the receive buffer past the
end of the frame.
Derive the length from the frame instead, trimming the trailing CRC and
clamping to the four blocks a READ4 can return. The DESFire branch got
the same treatment: its length was computed as size - 2 with no check
that the response held at least two bytes, so an empty frame underflowed
to a huge value. Both now bail out when the response is too short to
hold a trailer.
Claude-Session: https://claude.ai/code/session_016a4SuDfC8EkHo3MGGrRMod
seader_capture_sio() copied 32 bytes into the 128 byte credential->sio
at an unchecked uint8_t offset derived from the block number. Reads
below the recorded start block underflowed the offset and wrote well
past the struct, over load_path and the loading callback pointers.
It is unreachable: its only callers were seader_iso15693_transmit() and
seader_iso14443a_transmit(), which lost their own callers when the HF
plugin took over the read path. The live equivalent lives in seader.c as
seader_hf_plugin_append_picopass_sio() and is bounds checked. Delete all
three rather than fix a copy nobody runs, along with the command
constants they were the last users of.
seader_mfc_transmit() is dead too but never touched the SIO buffer, and
removing it would orphan the MFC trace helpers, so it stays for now.
Claude-Session: https://claude.ai/code/session_016a4SuDfC8EkHo3MGGrRMod
The virtual credential state machine computed a uint8_t block offset as
buffer[1] - 10 with no range check, then read from credential->sio at
that offset. Any block below the SR base underflowed: an SE credential
starts at block 6, so the SAM's first read produced offset 252 and
copied eight bytes from ~2KB past the 128 byte SIO buffer. Blocks above
25 walked off the far end for the same reason.
Route both the READ and READ4 branches through a helper that rejects
out-of-range blocks, and reply with an empty frame instead of garbage
when the SAM asks for something the saved credential does not hold.
Claude-Session: https://claude.ai/code/session_016a4SuDfC8EkHo3MGGrRMod
picopass_poller_send_frame() received into the caller-supplied rx_buffer
but trimmed instance->rx_buffer. The SAM read path passes its own
buffers, so every CRC-bearing card response silently shrank
instance->rx_buffer by two bytes: 8 after select, then 6, 4, 2, 0. The
fifth such frame called bit_buffer_set_size_bytes() with an underflowed
size and tripped its capacity furi_check.
A normal Picopass read spends exactly four trims (READ 05, READ 06, two
READ4s), landing on zero and surviving by one frame. Anything needing a
third READ4 -- an SIO too long for 64 bytes -- crashes.
READCHECK and CHECK responses carry no CRC, so they never reached the
trim; only READ, READ4 and PAGESEL did.
Drop the trim from send_frame rather than retargeting it: the SAM
expects responses with the CRC still attached, which is why the virtual
card state machine appends one and why capture_sio copies 32 of the 34
bytes a READ4 returns. identify() and select() need the trim for their
size checks, so they now do it themselves.
Claude-Session: https://claude.ai/code/session_016a4SuDfC8EkHo3MGGrRMod
Stack-allocate the two mbedtls DES contexts in loclass optimized_elite
(128 bytes each) and the APDU runner progress text buffer (24 bytes).
All three were static/global but are used only within single call frames.