Commit Graph
14223 Commits
Author SHA1 Message Date
iceman1001andClaude Opus 5 (1M context) 6341f40b4c hf mf hardnested: fix the nonce reply length, 9 bytes per pair not 4 per nonce
Thanks @TheArchitect0880 for pointing it out and suggested a first fix.

MifareAcquireEncryptedNonces packs two 4 byte encrypted nonces plus one
byte holding both their encrypted parity nibbles into every entry, but
the reply declared num_nonces * 4 bytes. The client walks that buffer 9
bytes at a time, so on RDV4 it read 612 bytes out of a 544 byte payload
and handed roughly 15 of every 136 nonces to add_nonce() from stale
packet buffer content. Those fake nonces went into the .bin nonce file
too.

Count pairs instead of bytes. num_nonces now reports whole pairs only,
so a button abort or a static nonce bailout part way through a pair
drops the dangling nonce rather than shipping a half built entry whose
parity nibble was never filled in.

MFC_NONCE_PAIR_SIZE and MFC_MAX_NONCE_PAIRS document the layout next to
mf_nonces_resp_t so the 9 vs 4 confusion cannot come back, and the
client now refuses a reply too short for the nonce count it carries.

Both acquisition functions collect straight into the reply buffer rather
than a second PM3_CMD_DATA_SIZE stack array, which halves the stack used
per call. MifareAcquireNonces also returned isOK = 2 on button press,
which is not a PM3_* status; that is PM3_EOPABORTED now.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-12 10:44:00 +02:00
Iceman 5ca15e4587 Merge pull request #3616 from mfcarroll/t55xx-psk-trim-graphbuffer
lf t55xx detect: save and restore the graph buffer through the shared helpers
2026-09-11 22:53:21 +07:00
iceman1001 7fb520ce18 text 2026-09-11 15:17:52 +02:00
iceman1001andClaude Opus 5 (1M context) 67c121f554 hf mfdes detect: sweep all key numbers, and count errors per key type
errcount was declared before the key type loop and only reset by a
non -11 result, so eleven scattered card errors during the DES pass left
the AES pass to break on its first error without trying a key.  Count
per key type instead.

With no -n, detect only ever looked at key 0.  Sweep the key numbers the
application declares (low nibble of the key settings) instead, falling
back to 0x00..0x0D when the settings are unreadable.  All keys in an
application share an algo, so the first key number that succeeds narrows
keytypes[] for the rest.  --save now stores the first key found rather
than whatever dctx was left holding, and a lost card aborts the sweep.

-n <num> behaves exactly as before.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-11 15:15:36 +02:00
iceman1001andClaude Opus 5 (1M context) e0543bd612 hf mfdes detect: probe for LRP, and use the DESFire dictionary
Two things stopped 'detect' finding keys it should have found.

An AES application and an LRP application report the same key settings
byte, so a card in LRP mode was only caught when the settings happened to
be unreadable and the auth-command fallback ran.  Probe with
AuthenticateLRPFirst whenever --schann was not given, then re-select.

With no -f, candidates came from g_mifare_plus_default_keys, which is 26
entries of 16 byte AES.  For DES that meant authenticating with the first
half of an AES key, and for 3K3DES it fabricated 24 bytes as
key[0..15] + key[0..7].  Default to the bundled mfdes_default_keys
dictionary instead: 7 DES, 47 2TDEA/AES and 4 real 3K3DES keys.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-11 12:38:16 +02:00
Matthew CarrollandClaude Opus 5 e810245caa lf t55xx detect: save and restore the graph buffer through the shared helpers
The psk antenna-settle trim open-coded the save/restore pair: save_bufferS32
plus g_GridOffset on the way in, and restore_bufferS32's return assigned to
g_GraphTraceLen on the way out. graph.c now has save_graphbuffer and
restore_graphbuffer, which hold contents, length and grid offset together, so
call those and drop the local copy of the length contract.

No behaviour change. `lf t55xx detect -1` on the in-tree psk traces leaves the
graph at 20000 samples before and after.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 02:20:10 -07:00
iceman1001andClaude Opus 5 (1M context) cec0e800ab hf mfdes chk: detect the secure channel per application
secureChannel was the constant DACEV1, and there was no way to override
it, so a card in LRP mode could never be authenticated - chk reported no
keys on a card 'hf mfdes detect' handles fine.  When the key settings
were unreadable it gave up instead of probing.

Work the channel out per AID: the key settings give the algo, and for an
AES app one AuthenticateLRPFirst probe separates EV1/EV2 from LRP, which
the settings byte cannot.  When the settings are unreadable, fall back to
DesfireCheckAuthCommands() the way detect does.  --schann d40|ev1|ev2|lrp
pins it and skips detection.  Only AES is tried on an LRP channel.

Also clear the session after a found key, so the next key number starts a
first auth rather than an EV2/LRP non-first one.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-11 11:08:45 +02:00
iceman1001andClaude Opus 5 (1M context) 2cd5285cb2 hf mfdes chk: track found keys per application
foundKeys was indexed [keytype][keyno] with no AID dimension and was
never reset between applications, so a key number recovered on one AID
was skipped without a single auth attempt on every later AID.  On a card
with AES key 00 set on two apps, only the first one was ever reported.

Give every application its own desfire_app_keys_t and hand that to the
checker.  Saving to json now uses a new 'mfdes v2' format keyed by AID,
with a loader that round-trips it; v1 is kept for existing files.

Also in the same path:
 - one auth error below 7 broke out of the key number loop, abandoning
   every remaining key number for the AID.  Only an algo mismatch (4, 50,
   51) skips the key type now; an invalid key number (3) skips just that
   key number, and a transmit error retries after a reselect
 - 3TDEA keys were stored 16 bytes wide and written out as 24
 - -k with a 24 byte key was rejected by the parser, making the 24 byte
   branch unreachable
 - DesfireGetAIDList() wrote unbounded into a 78 byte app_ids buffer

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-11 10:38:57 +02:00
Iceman f5d50bb9d7 Merge pull request #3615 from mfcarroll/t55xx-nrz-startidx
lf: fix silently rotated and inverted blocks in the t55xx read path
2026-09-11 15:12:00 +07:00
Iceman b3794d821f Merge branch 'master' into cmddata-trim-frame
Signed-off-by: Iceman <iceman@iuse.se>
2026-09-11 15:08:04 +07:00
Matthew CarrollandClaude Opus 5 b0d5178c92 lf t55xx detect: finish undoing the psk antenna-settle trim
test_scan and t55xx_fallback_try drop 160 samples off the head of the capture
before demodulating psk, then restore it. restore_bufferS32 copies the samples
back and returns the length it put back, but no caller assigned that return, so
g_GraphTraceLen stayed at the trimmed length for the rest of detect.

That left the sample-space anchoring unable to engage. t55xx_demod_offset only
resolves when config.anchor_tracelen equals g_GraphTraceLen; detect recorded
11840 while every block read acquires 12000, so the guard never matched and a
dump fell back to config.offset - a bit index into a demod buffer that no longer
exists, which is the case its own comment warns about. Over six instrumented
dumps of a PSK1 RF/128 tag, one resolved and five did not; the one that resolved
read block 0 correctly on the first try.

A candidate found inside the trim was also measured 160 samples left of where a
block read measures, so those anchors were out of frame even when the length
happened to match. 160 samples is round(160 / clk) bits - 5 at RF/32, 1 at
RF/128 - and a rotation of that size is what read-back was showing.

Pair the trim with its undo so a third call site cannot pick up half of it, hold
a bias while the capture is trimmed so a recorded anchor still names an
untrimmed sample, and give the 160 one home.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 15:12:43 -07:00
Matthew CarrollandClaude Opus 5 0586b8a1c3 lf t55xx detect: settle the block 0 rotation with the broadcast period
A block read repeats one 32 bit word, so every offset yields a rotation and more
than one can pass the structural checks. A T5577 in direct/nrz carried both
00080040, the word on the tag, and 00080001, its ror19 - both master key 0,
reserved 0, RF/32, direct. test() answers with the first hit from a fixed floor,
so which one detect reported was scan order rather than evidence.

No structural check separates them: every bit of block 0 is a real field, and
the three the scan skips are otp, fast write and inverse data, all settable. The
tag can, though - maxblock has to account for what regular read mode cycles
through. So measure the broadcast period and keep the rotation that agrees,
reusing the constraint printConfiguration already applies to the psk2/psk3
pre-images. Costs one acquisition, and only when the reading is ambiguous.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 14:57:15 -07:00
Matthew CarrollandClaude Opus 5 bdc4828e0f lf t55xx writetest: read each block three times and name how it differs
One read cannot see the fault this suite is most useful for. A block that comes
back silently wrong on some reads and right on others scores the same as one
that is simply wrong, and the same as one that is fine. Read each block three
times and mark disagreement between them as `unstable`.

Where a block is wrong the same way every time, name it: `rol1`, `ror5`,
`inverted`, `inv+ror2`, `shr1`. The kind matters. A rotation says the word
boundary moved, an inversion says psk picked the opposite phase, and a shift
says a demodulation opened a bit early and padded with zero - three different
faults that want three different fixes, and all of them previously reported as
"block 3,4 bad".

Measured on a T5577 over the 21 PSK1 configurations, this turns 56 fields of
"bad" into 56 marked unstable, and on a build that has since fixed part of it,
into 24 rotations whose size tracks the bit rate - ror5 at RF/32, ror4 at RF/40,
ror2 at RF/100 - which is a fixed sample offset rather than noise, and says
where to look.

Block 1 is 00000000, which equals all of its own rotations, so a rotated read of
it cannot be told from a correct one. Noted in the header rather than changed,
since the payload is the suite's own choice.

Also corrects the header, which described block 2 as ffffffff where the script
writes aa5500ff.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 12:30:20 -07:00
Matthew CarrollandClaude Opus 5 782d28932c lf: restore the graph buffer length along with its contents
restore_bufferS32() returns the length it put back and every graph-buffer
caller dropped it, so g_GraphTraceLen kept whatever value the code between
save and restore had left.

In try_detect_modulation() that is the 160 sample psk antenna-settle trim:
`data modulation` gave the buffer's samples back but left it 160 short, and
a following demod lost round(160 / clk) bits off the end. check_chiptype()
loses the same way through the acquisitions its detections make, on the
`lf search` path, and for g_DemodBuffer too. demodTI() loses convLen + 16
on its error path.

save_graphbuffer() / restore_graphbuffer() pair the length and grid offset
with the contents so no call site has to remember them. The visa2000 and
zx8211 sites do not change the length, so converting them is a no-op today
and keeps the next one from being a bug.

Regression test in tools/pm3_tests.sh: `data modulation` on a saved psk1
trace must leave all 20000 samples.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 10:33:17 -07:00
iceman1001 82c726dd9b fix t55xx_config.lua script with scoring of psk2/3 to write a proper text 2026-09-10 10:05:10 +02:00
iceman1001 78b4d4ef00 fix psk1 detection. now scores 24/24 tests 2026-09-10 05:45:13 +02:00
iceman1001 e121e3f0a8 fixing FSK / t5577 2026-09-10 05:28:06 +02:00
iceman1001 d54ae662ce fix lua scripts grabbing return text. mimics the python binding way now 2026-09-10 04:31:47 +02:00
iceman1001 bef7f3752a another entry 2026-09-10 04:03:57 +02:00
iceman1001andClaude Opus 5 (1M context) ea2909702f lf t55xx: anchor the block read offset in samples, not demod bits
GetT55xxBlockData extracted every block at config.offset, a bit index into a
demod buffer cached by the last detect - a different capture. Demodulators do
not all start on the same bit, so a later read could return a rotation of the
block with nothing marking it as wrong: psk1 by four or five bits, fsk2a and
nrz by one, manchester and biphase never.

Record the boundary where it is stable instead. Every demodulator reports the
graph sample its first bit sits on, and turn_read_lf_on(137 * 8) makes the
firmware open each read acquisition at the same point, so the word boundary
lands on the same sample every time. Convert back per demodulation.

Block 0 re-anchors from its known value, and a write is now verified by the
value being anywhere in the repeating stream rather than at one cached offset -
which also fixes correct writes reporting as validation failures. A block 0
verify re-detects on a mismatch, not only on a decode failure: the pre-write
config can decode the post-write signal into garbage rather than failing.

Also fixes a -1 password sentinel truncated to 32 bits by a ternary, which made
that detect report a password that was never supplied.

Measured on a T5577 across psk1, fsk2a and direct/nrz: three consecutive dumps
agree field for field, against a baseline where psk1 corrupted 22 of 24.

Co-Authored-By: Claude Opus 5 (1M context)
2026-09-10 03:58:14 +02:00
iceman1001 253e0d148a hf emrtd info - did not identify and mapped OID for algorithms in PACE. Now we keep a list and pretty print it 2026-09-10 02:55:16 +02:00
ry4000 ef23d014eb R&Y: Updated MEGACASH AID in aid_desfire.json
# Updates
1. Updated `MEGACASH` vendor name from `Bank Mega` to `PT Bank Mega Tbk` *which is the vendor's full name*.

-r&y.

Signed-off-by: ry4000 <154689120+ry4000@users.noreply.github.com>
2026-09-09 16:15:03 +10:00
Iceman 23d6f67dc8 Merge pull request #3603 from kormax/new-aid-desfire-entries
Add new ISO7816 & DESFire AID values
2026-09-08 22:57:50 +07:00
kormax 6c8cf5bdcf Add new ISO7816 AID values 2026-09-08 18:24:09 +03:00
kormax a4b1dd0ee7 Add new DESFire AID entries 2026-09-08 18:23:15 +03:00
Niel Nielsen 6e84d4ca8b Refactor chip ID and app signature checks
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-08 13:35:48 +02:00
Niel Nielsen b0aa484eba Fix comment for app descriptor in cmdhw.c
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-08 12:17:34 +02:00
Niel Nielsen c9fe1dd891 Merge branch 'RfidResearchGroup:master' into esp32-c2 2026-09-08 12:11:52 +02:00
Niel Nielsen 14ab313ca7 Implement firmware image validation for ESP32-C2
Add checks for firmware image validity before flashing.

Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-08 12:11:32 +02:00
Sophiel Zhou 7cfc6fdea2 fix hf mf sen wrong payload frame size 2026-09-08 16:47:07 +08:00
Iceman 5fc30b1a6e Merge pull request #3597 from kormax/iso1443_3a_timeslot_support
Add ISO14443-3 Type A timeslot support to 'hf 14a info' and 'hf 14a reader'
2026-09-08 15:28:30 +07:00
iceman1001 764783cbfc annotation for topaz had an issue where it used 9 bytes for reader frames when it could be up to 16 bytes. 2026-09-08 08:04:09 +02:00
ry4000 26bacfa258 R&Y: Updated Fermax AID in aid_desfire.json
# Updates
1. Updated `Fermax` AID to reflect the parent company's name/country and DESFire PACS product.

Signed-off-by: ry4000 <154689120+ry4000@users.noreply.github.com>
2026-09-08 10:21:54 +10:00
kormaxandmxcdoam 1ecbcb74be Add ISO14443-3 Type A timeslot support to 'hf 14a info' and 'hf 14a reader'
Co-authored-by: mxcdoam <72457810+mxcdoam@users.noreply.github.com>
2026-09-07 22:55:24 +03:00
Iceman 9f641c3ced Merge pull request #3427 from Sanduuz/feature/st25ta_ndef_sim
Added support for emulating ST25TA tag (IKEA Rothult) with custom NDEF response
2026-09-07 15:38:46 +07:00
Iceman 07209fc358 Merge pull request #3493 from 0x6r1an0y/20260819-gdmfix
Improve `hf mf gdm*` commands
2026-09-07 15:28:56 +07:00
ry4000 6b1305cbc9 R&Y: Nomeclature updates to felica_system_code_list.json
# Updates
1. Changed `type` from `e-amusement` to `arcade` for:
    - Bandai Namco Passport
    - Konami e-amusement pass
    - Sega Aime
    - Taito NESiCA
2. Updated `description` to include the full name and `(AIC)` annotation to note that this information only applies to the Amusement IC Card variants as opposed to MFC/ICODE SLI/MFC/MFU, respectively.

-r&y.

Signed-off-by: ry4000 <154689120+ry4000@users.noreply.github.com>
2026-09-07 10:09:18 +02:00
xilni 342cc37cca docs(bwm): fix stale hw bwm command references 2026-09-07 00:07:07 -04:00
Iceman 2da575ab36 Merge branch 'master' into master
Signed-off-by: Iceman <iceman@iuse.se>
2026-09-06 21:25:31 +07:00
Iceman 23f8bfbc46 Merge pull request #3473 from YoungJules/feature/add_kgh_tools
Feature/add kgh tools
2026-09-06 21:01:40 +07:00
Iceman 45c1d79282 Merge branch 'master' into 20260823-mfuformat
Signed-off-by: Iceman <iceman@iuse.se>
2026-09-06 21:00:13 +07:00
dxl fc355df050 Added IO test capabilities to the factory QC for PM5. 2026-09-05 18:09:46 +02:00
dxl ea5485b4b8 Rename CMD_PM5_QC_TEST to CMD_PM5_QC_TEST_HW
and delete repeated def: CMD_PM5_BWM_SET_CAP
2026-09-05 18:09:46 +02:00
Antiklesys 999f64ddf9 Merge branch 'RfidResearchGroup:master' into master 2026-09-05 17:17:25 +08:00
Antiklesys 264d9bf284 TA1=96 support for SIM module 2026-09-05 17:16:38 +08:00
Niel Nielsen ea4d90ebe4 Refactor BWM command handling for clarity and structure
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-05 10:09:23 +02:00
歐歪 6960a6e788 fix: oops 2026-09-05 05:40:45 +08:00
歐歪 7e2ecc0ba6 rename: hf mfu format to ndefformat 2026-09-05 05:18:36 +08:00
Julian Isaac 71ca59a8ad Moved the new tool to tools/kgh and renamed it legic_kgh_tool. Added some validation so we don't return random bytes from non-kgh cards. Applied some styling to the new code. Added GPL header to new source file. Cleaned up the static build. 2026-09-04 23:14:22 +02:00
Julian Isaac 1d5d09b073 Merge branch 'master' into feature/add_kgh_tools 2026-09-04 22:23:13 +02:00