Simulation now completes the full exchange with a genuine Paxton reader in
password mode, and crypto mode read/write passes Proxmark-to-Proxmark.
Firmware:
- SOF was one bit period short. The lead-in that compensated for the lost
head half bit was removed and nothing replaced it, so readers rejected
every answer with a second START_AUTH. Default is now 6.
- The edge-detect threshold was latched before being measured, so the value
chosen depended on whether the Proxmark was in a field when sim started.
It is now measured on field entry and re-armed when the reader leaves.
- The percentile walk latched on run-scoped variables, so one attempt made
outside a field poisoned every later one.
- Field loss was detected from TIMESTAMP, which is free-running MCU time and
never stalls. Detect it from receive silence instead.
- Frames of a length the protocol does not have no longer reach the state
machine; our own modulation tail was resetting the session and breaking
every write.
- A dropped edge merges two or three reader bit periods into one gap. Those
bits were discarded; they are now recovered by decomposition, which is what
made crypto mode work (AUTH decode 15% -> 100%).
- Threshold selection is limited to 20 and 32 and settles in under 25 ms.
Client:
- lf hitag info printed a hardcoded 0x06 and reported 'Password mode' for
every tag. It now reads page 3, takes -k (4 bytes password, 6 bytes
crypto), and says so when the config cannot be read.
- lf hitag restore: writes a dump back in dependency order - user pages,
then key material, then config last - validates the config byte, and
prints the credential the tag will require afterwards.
- lf hitag crack2 now reports why it failed instead of a bare 'fail'.
- trace list: bit count moved to its own column, relative mode shows a
Frame Delay Time row rather than renaming Start/End, --frame and -r
rejected together.
PM3_CMD_DATA_SIZE went 512 -> 624 without a capabilities bump, so a new
client connects to old firmware and every oversized command dies at the
device's length check with no message.
Append max_cmd_data_size, bump to v9. The client now accepts an older
capabilities struct - it only ever grows by appending, so an older layout
is a prefix - and defaults the frame size for pre-v9 firmware.
SendCommandNG bounds by the device value instead of the compile time one.
Also zero init capabilities_t on the device, it leaked stack bytes.
nkeys was a 6 bit field but the client chunked by what fits in a frame -
123 keys in segment mode. nkeys wrapped to 59 while memcpy copied all 123
and the loop advanced by 123, so 64 of every 123 keys were never tested
and never reported. Full key mode was unaffected, it chunks 30.
Give nkeys its own byte. MIFAREU3P_CHKKEY_HEADER goes 18 -> 19, costing
one byte of payload, and segment mode chunks 123 again
Payload layout changed: client and firmware must be updated together.
Thanks Claude!
The OLD frame size was tied to the NG one, but the bootloader only speaks
OLD - growing PM3_CMD_DATA_SIZE would silently change sizeof(PacketCommandOLD)
and break flashing against every deployed bootrom in both directions.
Pin the OLD structs to their own constant and use it on every OLD path:
reply_old and the OLD receive branch on both sides, the bootrom, and the
flasher's write_block/send_finish_write_cmd, which memcpy into a
PacketCommandOLD using the NG size.
No behaviour change - both constants are 512 and armsrc .text is
byte-identical before and after.