Commit Graph
23223 Commits
Author SHA1 Message Date
iceman1001 465deeda39 text 2026-09-14 12:24:31 +02:00
iceman1001 aae70014e0 - VIGIK service badge fields now show which ones the RSA signature actually covers 2026-09-14 12:21:47 +02:00
iceman1001 9caa6d6117 minor change we device now reports back EMULATOR memory size to the client. Had to bump capability version number to 11. 2026-09-14 12:16:51 +02:00
iceman1001andClaude Opus 5 (1M context) 6f4643a637 hf mf: add the MAD helpers parseproac needs, and register AID 0x4982
b82f60362 landed parseproac.c without them, so a clean checkout does not
build.

 - mad.c / mad.h: mad_find_aid() and mad_count_aid(); DetectHID() delegates
   to the first, it was already generic
 - mad.json: 0x4982 PROAC, sorted in after NORALSY's 0x4980

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-14 12:13:35 +02:00
iceman1001 b82f603622 urmet parsing 2026-09-14 12:11:42 +02:00
iceman1001 cac7378e95 text 2026-09-14 09:19:29 +02:00
iceman1001 9b60b1b218 missing define 2026-09-14 09:19:17 +02:00
iceman1001andClaude Opus 5 (1M context) 82783eae26 hf mf: decode the Hexact payload
Sectors 9 and 11 were printed raw and marked 'not decoded'.

 - remove the XOR keystream and print the ten eight byte records
 - check the eighteen record bytes that are XOR combinations of sector 0,
   sector 15 and the UID, plus three record to record ties
 - 'hf mf view --selftest' now covers the decoder, using a synthetic card
 - sector 15 marker compare ignores case, factory blanks were not matched
 - an all FF or all 00 payload is named, not decrypted
 - sector 15 block 2 is text on a blank, so only read a serial there when
   the first four bytes are not printable

Research. The remaining 58 payload bytes are issuer data and are printed
without interpretation.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-14 09:19:05 +02:00
iceman1001andClaude Opus 5 (1M context) 8fd9bcbebc hf mfdes: dump a whole card to json, and view it back
'hf mfdes dump' walked one application and printed it. It now walks
every application on the PICC, keeps what it reads, and saves a
'hf-mfdes-<UID>-dump.json' card image. '--aid' / '--isoid' / '--dfname'
still narrow it to one application, '--ns' skips the save.

The format is 'mfdes v1', written and read in fileutils.c and documented
in doc/mfdes_dump_format.md. Two decisions worth stating:

 - The PICC level is application 000000, so every key in the file says
   which AID it opens. Key version and key value are separate: a version
   with no key is the normal shape for a key that was found but never
   recovered, and a missing key never means the key is zero.

 - Every file carries a 'Read' flag. A file whose contents could not be
   fetched is recorded as unread with no data at all, rather than as a
   run of zeros. A simulator built on this must not confuse '8 bytes of
   00' with 'we could not read 8 bytes'.

'hf mfdes view -f <fn>' prints such a file with no device attached.

With no '--keys', the dump looks for 'hf-mfdes-<UID>-keys.json' by
itself, so a 'hf mfdes chk -j' run is picked up on the next dump without
naming the file again.

Two fixes fell out of testing against a DESFire EV2:

 - DesfireSetKey() calls DesfireClearContext(), which wipes command set,
   comm mode, KDF and UID, not just the key. Swapping in a per-application
   key that way left the context at 'Communication mode: n/a' and
   DesfireFillFileList() then returned junk file ids. Use
   DesfireSetKeyNoClear().

 - GetVersion and the originality signature are answered unauthenticated.
   Asking for them from inside the authenticated session produced a
   'Wrong communication mode' warning and a run of MAC mismatches.

hex_to_buffer() treats hex_max_len as a byte count while every sprint_hex*
caller passes sizeof(buf) - 1, a character count, so it writes two or
three times the buffer size. Measured, sprint_hex_inrow overflowed at
4098 input bytes. Doubling UTIL_BUFFER_SIZE_SPRINT to 16384 moves that to
8192; the mixed semantics still need auditing across ~30 call sites.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-14 09:11:38 +02:00
iceman1001 66dbf6ac5c update 2026-09-14 09:03:57 +02:00
iceman1001 48d779261a the new dump format for desfire cards 2026-09-14 08:37:00 +02:00
Iceman 7751be208c Merge pull request #3624 from nieldk/esp32-c2
PM5 add option to connect by BLE name
2026-09-14 01:22:25 +07:00
Niel Nielsen 96634fb2bf Increase PM3_FPC_MAX_DATA from 2048 to 4096
Fixes the the disconnects when on BLE connection

Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 20:18:32 +02:00
Niel Nielsen 23d851610b Fix CMakeLists.txt to properly end foreach loop
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 18:26:44 +02:00
Niel Nielsen d8d0ecc724 Fix CMakeLists.txt by correcting foreach loop syntax
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 18:24:07 +02:00
Niel Nielsen 1b9eb458ec Merge branch 'RfidResearchGroup:master' into esp32-c2 2026-09-13 18:22:48 +02:00
iceman1001 64b5db4ddd text 2026-09-13 18:22:27 +02:00
Niel Nielsen 720ea518f7 Fix CMakeLists.txt by adding missing newline
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 18:20:51 +02:00
Niel Nielsen afeab5f6dc Fix CMakeLists.txt by adding missing newline
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 18:20:43 +02:00
Niel Nielsen 2486e74aa0 Merge branch 'RfidResearchGroup:master' into esp32-c2 2026-09-13 17:59:03 +02:00
Niel Nielsen 284e4b385c Refactor BLE error handling and time functions
Refactor BLE error handling to return standard error codes instead of PM3_* constants. Update time-related functions to use a consistent method for obtaining the current time.

Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 17:58:45 +02:00
Niel Nielsen c264251935 Fix CMakeLists.txt to properly close foreach loop
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 17:57:59 +02:00
Niel Nielsen 583f05198a Add parsehexact.c to CMakeLists.txt
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 17:57:07 +02:00
iceman1001andClaude Opus 5 (1M context) a812e6728f tools: run the vigik_recover_pk selftests in CI
Beside the recover_pk ones, in the common target.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-13 17:50:45 +02:00
Niel Nielsen 0a90b714cc Add files via upload
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 17:31:12 +02:00
Niel Nielsen bf703c24ff Merge branch 'RfidResearchGroup:master' into esp32-c2 2026-09-13 17:03:14 +02:00
Niel Nielsen 50e07859e8 Refactor error handling in select function
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 16:25:09 +02:00
iceman1001andClaude Opus 5 (1M context) e19c6755fe hf mf: stop a refused nested auth from hanging autopwn and eating the keys
MifareNested collected nonces in 'while (target_nt[i] == 0)' with no
attempt counter. When the tag NAKs the nested authentication the loop
re-sent the identical frame forever, so the client's 2s wait expired and
autopwn returned PM3_ETIMEOUT — throwing away every key recovered up to
that point. Reported on a card whose sector 16 refuses the standard MFC
EV1 keys: 32 keys cracked, nothing saved.

A NAK is a refusal, not a glitch, so the device now gives up on it and
reports PM3_EWRONGANSWER. autopwn names the sector it skipped and carries
on, and a timeout falls through to the key table and the dump whenever
anything was recovered.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-13 15:56:41 +02:00
iceman1001 c615ef8930 adapting parser 2026-09-13 15:52:39 +02:00
Niel Nielsen 510767f51e Update return values in ble_posix.h documentation
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 14:54:45 +02:00
Niel Nielsen cdc4d7ad26 Refactor BLE connection error handling and logging
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 14:54:07 +02:00
Niel Nielsen e004cc213a Update Makefile
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 14:52:41 +02:00
Niel Nielsen 52fac7ca1b Add parsehexact.c to Makefile build list
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 13:44:42 +02:00
Niel Nielsen ea585bacd6 Fix include directive for dependency files in Makefile
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 13:18:57 +02:00
Niel Nielsen 28daf3e2c4 Enhance BLE capabilities hint and connection logic
Add hints for granting BLE scanning privileges and improve error handling in connection functions.

Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 13:15:40 +02:00
Niel Nielsen 84dd953cf4 Merge branch 'RfidResearchGroup:master' into esp32-c2 2026-09-13 13:12:32 +02:00
Niel Nielsen ca29260b0d Correct preprocessor directive formatting
Fix formatting of preprocessor directive ending.

Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 13:09:46 +02:00
iceman1001andClaude Opus 5 (1M context) 66f9a6b51f hf mfdes: report a select/auth failure once, not twice
DesfireSelectAndAuthenticate*() and its callers both printed the same
error at different severities, so every failure came out as two lines.
The core helper now owns the message and names the AID and the step;
the 23 restatements in cmdhfmfdes.c are gone. Same duplicate pair fixed
in cmdhfgallagher.c.

Also: DesfireAuthErrorToStr() falls back to DesfireGetErrorString() for
the PM3_E* codes the select paths pass through, which used to print an
empty reason, and auth error 7 no longer reuses the text of error 1.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-13 12:58:46 +02:00
iceman1001 7142ddc9bd added hexact parser 2026-09-13 12:55:43 +02:00
Niel Nielsen 7efc83ca1d Implement BD address validation and update BLE logic
Added a function to check if a string is a valid Bluetooth address and updated the BLE connection logic to handle both addresses and names.

Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 12:44:15 +02:00
Niel Nielsen dcde9dd16e Add ble_resolve_name function for device name resolution
Added a function to resolve advertised device names to their LE addresses.

Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 12:43:40 +02:00
Niel Nielsen e0fbc4174c Add includes for string and time functions
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 12:43:10 +02:00
iceman1001andClaude Opus 5 (1M context) 72f48c8a02 fileutils: honor a filename extension the user supplied
'-f card.mfd' looked for card.mfd.bin and saved to card.mfd.bin,
because the caller's suffix was appended unless the name already
ended in that exact suffix.

searchFile() now tries the name as typed before falling back to the
suffixed one.  newfilenamemcopyEx() keeps an extension that denotes
the same kind of file it is about to write, and swaps any other for
its own, so 'hf mf dump -f card.mfd' gives card.mfd + card.json
rather than card.mfd + card.mfd.json.  Both classify with
get_filetype() so load and save cannot drift apart.

Also drops the size_t underflow in newfilenamemcopyEx(), where a long
path plus a configured save path made the snprintf bound wrap past
the 1000 byte buffer.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-13 12:19:53 +02:00
Iceman 196a467bf8 Merge pull request #3623 from nieldk/esp32-c2
Update pm3_cmd.h
2026-09-13 16:33:30 +07:00
Niel Nielsen 094df6102e Update pm3_cmd.h
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-13 11:22:47 +02:00
iceman1001andClaude Opus 5 (1M context) c77d6455f5 hf mf: identify VIGIK family systems by their static key sets
is_valid_vigik_card() only fired when the MAD advertised aid 0x4910. Plenty of
VIGIK based deployments ship no MAD at all - a Hexact card has HEXACT as key A
on every sector and nothing in sector 0 block 1 - so hf mf view and hf mf dump
--ns said nothing whatsoever about them.

These systems use the same keys on every card they issue, which makes them
identifiable outright. Take the schemas out of armsrc/Standalone/hf_colin.c,
where they sit commented out inside the standalone mode, and match a whole dump
against them:

  Infineon / Hexact / COGELEC / Intratone   key A HEXACT x16, 15 static key B
  Noralsy                                   ALARON / BLARON
  Urmet Captiv                              8829da9daf76 throughout
  VIGIK service badge                       MAD key, then 1KIGIV on sectors 1-4

Every slot a schema pins down has to match, VIGIK_KEY_ANY marks the ones it does
not, so there are 31 exact keys to hit for Hexact and no room for a coincidence.
A HID card carrying a MAD, and a card on default keys, both still match nothing.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-13 09:18:08 +02:00
iceman1001andClaude Opus 5 (1M context) a9105c5089 hf mf: split HID and VIGIK decoding into client/src/parsers/
The shared viewer had the VIGIK sector assembly inlined, and the HID PACS
decode sat in hf mf mad's file branch where hf mf view and hf mf dump --ns
could not reach it. Neither scheme had a home of its own.

Give each one a parser next to parsehrt.c, same shape as that one - an
is_valid_x_card() detector and an x_parser_parse() that prints:

  parsers/parsehid.c    MAD aid 0x484d, PACS sector, Wiegand decode
  parsers/parsevigik.c  MAD aid 0x4910/0x4916, sector assembly

parsevigik.c also takes vigik_get_service(), vigik_verify() and
vigik_annotate() out of mifare/mifarehost.c, 306 lines that were VIGIK only
with a single caller.

mf_view_dump() is now two detector calls, so hf mf view -f and hf mf dump --ns
both decode a HID credential off a live card for the first time, and adding a
scheme is a new file plus two lines. hf mf mad -f keeps its HID decode through
the same parser.

The sector copy in the VIGIK path gains a bounds check; a MAD entry pointing
past the end of a short dump used to read past the buffer.

All three source lists get the new files: client/Makefile,
client/CMakeLists.txt and client/experimental_lib/CMakeLists.txt. The library
one matters because vigik_annotate() moved; without it anything linking
libpm3rrg_rdv4 loses the symbol.

hf mf mad against a card still cannot decode PACS. It authenticates with the
MAD key alone and never reads the application sector, so it has no credential
bytes to work with - unchanged here.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-13 08:37:17 +02:00
iceman1001andClaude Opus 5 (1M context) f0b569b507 fpga_compress: pad a single bitstream to the interleave boundary
zlib_decompress() walks its output in whole FPGA_INTERLEAVE_SIZE chunks:

  for (long k = 0; k < *outsize / (FPGA_INTERLEAVE_SIZE * num_outfiles); k++)

so a stream that is not a whole number of chunks loses its trailing partial one.
With two or more inputs the read loop zero-pads each stream past EOF and the
total lands on a boundary, but the padding was guarded by 'num_infiles > 1', so
the single input case was left ragged. 42172 bytes of fpga_pm3_hf.bit is 146.43
chunks, and -d handed back 39788 - a clean looking prefix, short by 2384 bytes,
22 of them real bitstream data.

Gate the padding on single_block instead. It must not be 'always pad': -s is the
.data section, and start.c's uncompress_data_section() sizes the decompression
with __data_end__ - __data_start__. Rounding .data from 14944 up to 14976 makes
LZ4_decompress_safe() return an error, and that path is the LED panic loop, so
the firmware would never reach AppMain().

  1 bitstream   42172 in -> 42336 packed, -d round trip byte identical,
                archive 28730 -> 28731
  4 bitstreams  archive byte identical to 43fe6c3eb, round trip exact
  -s .data      byte identical to 43fe6c3eb on the same input, unpadded

The 164 padding bytes never reach the FPGA: DownloadFPGA() shifts out only
bitstream_length bytes, taken from the .bit 'e' section header.

Also simulated the ARM decoder over the new archive - LZ4_decompress_safe_continue()
block by block into a FPGA_RING_BUFFER_BYTES buffer - 3 blocks of 16384/16384/9568,
none over the ring buffer.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-13 08:27:53 +02:00
iceman1001andClaude Opus 5 (1M context) 36b16b943f hf mf: share the view analysis between a file and a card read
'hf mf view -f' decoded VIGIK PACS and could extract keys with '--sk'. 'hf mf dump
--ns', which reads the same 1K off the card, printed only the blocks and the
verbose key/ACL tables. Same bytes, less analysis, purely because of where they
came from so getting a PACS decode off a live card meant dumping it to a file
and viewing that.

Move everything view does once the blocks are in hand into mf_view_dump() and
call it from both. 'hf mf dump --ns' and 'hf mf view -f' now print byte identical
output for the same card, and dump gains '--sk' to match. The old view leaked its
dump buffer on both VIGIK error returns; the shared version frees it in one place.

Drops a stale commented out convert_mfc_2_arr() call referencing a pdump
variable that no longer exists.

Rounds off #1942. mfc_read_tag() and '--ns' landed long ago, this was the piece
still missing.

hf mf eview, cview and the gen4 view share the same print block but omit
mf_analyse_acl() in verbose, so folding them in changes their output and is
left for a separate decision.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-13 08:22:52 +02:00
iceman1001andClaude Opus 5 (1M context) 43fe6c3eb8 fpga_compress: pick the LZ4 block size by consumer, not by file count
The 1 MB block branch exists for the ARM .data section: start.c's
uncompress_data_section() reads one 4-byte length and does one
LZ4_decompress_safe(), so .data has to arrive as a single block. It was
selected by 'num_infiles == 1', which is not what tells the two callers apart.

A build that skips LF, FeliCa and ISO15693 leaves FPGA_BITSTREAMS holding just
fpga_pm3_hf.bit, so the bitstream took that same branch and was packed as one
42 kB block. get_from_fpga_combined_stream() decompresses into a
FPGA_RING_BUFFER_BYTES buffer, 16 kB since 83c3f81b1:

  [#] inflate returned: -13247
  [#] reset_fpga_stream failed

Before 83c3f81b1 the copy was clamped with MIN(FPGA_RING_BUFFER_BYTES, ...)
whatever buffer_size said, so the blocks came out at 30 kB and the 30 kB ring
buffer still took them. That is why the commit looks like the cause - it only
removed the clamp that was covering for the wrong branch.

Add -s for the single block case and let the FPGA path always chop at
FPGA_RING_BUFFER_BYTES, however many bitstreams went in:

  4 bitstreams   169344 in -> 106933 out, byte identical to before
  1 bitstream     42172 in ->  28718 out, 3 blocks 13265/13206/2247,
                                          was 1 block of 27627
  .data  (-s)     14944 in ->   8786 out, byte identical to the
                                          obj/fullimage.data.bin.z in tree

Also hand the ring buffer back when reset_fpga_stream() fails. The early
return left it allocated for the rest of the session, which is the reporter's

  [#]   BigBuf_size............. 48116
  [#]   Available memory........ 31732

48116 - 31732 is 16384, exactly FPGA_RING_BUFFER_BYTES.

No CAPABILITIES_VERSION bump: fpga_all.bit.z is objcopy'd into the same
fullimage as the decompressor that reads it, so nothing here is client facing.

Reported and correctly diagnosed by @ewangsoft.

Fixes #3599

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-13 08:14:43 +02:00