14222 Commits
Author SHA1 Message Date
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