14391 Commits
Author SHA1 Message Date
Niel Nielsen 256f30f0fa Add files via upload
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-10-01 23:04:47 +02:00
Nemanja Nedeljkovic 23d57af8bd lf em 4x05 chk: add EM4305 anti-clone card password to default dictionary
Adds A1856618 to t55xx_default_pwds. This is the verified working login
password (as used with -p); the bit-reversed on-air form 1866A185 does not
authenticate.
2026-09-28 21:26:56 +02:00
iceman1001 2e2598bca9 style 2026-09-27 21:15:18 +02:00
Iceman 2630310336 Merge pull request #3650 from wiillk3/wireless-device-flashing
BWM: add ability to flash over wifi
2026-09-27 19:35:05 +07:00
歐歪andCodex a991b20a51 hf felica: handle system polling in ARM select
Pass the requested system code to firmware selection so default and system-specific reader modes share one polling path. Keep the RF field active across retries for the system-specific mode.

Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
2026-09-27 01:27:38 +08:00
歐歪andCodex 24e11e3d72 hf felica: support polling a selected system
Add --sys to the FeliCa reader so repeated polling can activate an Express Transit card while keeping the RF field on.

Co-Authored-By: Codex (GPT-6 Sol) <noreply@openai.com>

Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
2026-09-27 01:04:40 +08:00
WillandCursor 8150c2af77 BWM: add ability to flash over wifi
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-26 12:23:47 -04:00
Iceman cbadbf4bfe Merge branch 'master' into pm5-idle-lowpower
Signed-off-by: Iceman <iceman@iuse.se>
2026-09-26 21:51:29 +07:00
Iceman 9ba8b13da6 Merge pull request #3653 from 0x6r1an0y/20260917-15magic-fix
Improve `hf 15 csetuid` command
2026-09-26 21:48:47 +07:00
iceman1001andClaude Opus 4.8 027b225f41 Fix lf t55xx dump password reuse and p1detect for non-Atmel clones
lf t55xx dump: when lf t55xx detect has already recovered and confirmed
the password, T55xxReadBlockEx now trusts that session config instead of
re-probing the config block unauthenticated. A password-gated tag does
not answer that probe, so 'dump -p' failed its safety check on every
block and demanded --override; it now reads directly.

lf t55xx p1detect / lf search: the page-1 trace fingerprint hardcoded
Atmel's manufacturer byte (0x15/0x39) into a 16-bit preamble, so a valid
T5577 clone with another manufacturer (e.g. Tendyron 0x65) was rejected.
The duplicated atmel/silicon checks are collapsed into
t55xx_trace_preamble_match(), which also matches the bare 8-bit ACL 0xE0
- the same anchor lf t55xx trace decodes against.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-25 18:24:44 +02:00
MsprgandClaude Fable 5.1 39bbf20cce pm5: hw bwm ble, the module's BLE settings incl. on/off and pairing
The module ships with BLE open: anyone in range can connect and run
commands, and there was no way to turn the radio off or to require
pairing from the PM5, although the module already implements bonding
with a static passkey (LE Secure Connections + MITM, SPP characteristic
encrypted) behind commands nothing exposed.

New CMD_PM5_BWM_BLE (0x0183): one action byte, then a full status
snapshot back (bwm_ble_status_t: switch, state, bonding, passkey, TX
power, address, name, bonded devices). Client menu `hw bwm ble`:
  status              everything above, with a warning while open
  on | off            persisted radio switch (needs the companion
                      Proxmark5_BWM_esp32 PR; off =
                      nothing can connect, USB/WiFi only)
  pairing on|off [-k] require the 6-digit passkey, set the passkey; a
                      change of on/off restarts the stack (drops a
                      connected client), the factory key 123456 is
                      flagged
  forget -i N | --all remove bonded devices
  txpower -a/-c dBm   advertising / connection power, -24..18 and 20

The firmware replies before restarting the stack so the reply survives
when the command itself arrived over BLE. Fields a module without the
switch cannot answer read 0xFF and print as unknown. A module that does
not answer at all ends the status snapshot after its first query instead
of running into the client's timeout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:47 +02:00
MsprgandClaude Fable 5.1 0cd1fada0e pm5: auto power-off: idle timeout, unplug switch, stored on the BWM
The USB-unplug power-off left two ways to drain the battery: a PM5
woken by the button and forgotten, and one left idle after the phone
disconnected.

Idle trigger (opt-in, PM5_AUTOOFF_IDLE_MS 0 by default so out of the box
only the unplug trigger exists, as before): on battery the board powers
off after the idle timeout with no interaction and no wireless client.
Interaction is a received command over any transport, any button press,
or a client connecting or leaving; a standalone mode or a long-running
command holds the main loop, so neither can be cut short. The BWM
reports its clients with the LINK_STATE broadcast (8092, companion
Proxmark5_BWM_esp32 PR) which bwm_forward.c tracks; at boot the BLE
half is seeded with a status query since the module only reports
changes. When the timer is due but a client is still tracked, the module
is asked for its BLE state (at most every 10 s), so a lost "client left"
broadcast cannot pin the board on. And right before going off the module
is asked once more: older module firmware never sends the broadcast and
a "connected" one can get lost, and neither may power the board off
under a silent BLE client. No answer means off.

--unplug off: the unplug trigger always won, so "survive the unplug, go
off after N idle seconds" was not reachable. It is its own setting
(default on = unchanged). With it off an unplug only restarts the idle
clock - from the unplug, not from the last command, or a board that sat
idle on USB for an hour would still die with the cable. Unplug detection
is edge-based (VUSB present at the previous poll, absent at this one)
instead of "absent and seen since boot", which would fire on every poll
after a tolerated unplug. VUSB is followed while the feature is switched
off too, so switching it on again on battery is not taken for an unplug.

Stored on the BWM: runtime-only settings reset at every boot, which is
exactly when the idle trigger matters. The PM5 has no settings store, so
the switch, the idle timeout and the unplug switch live in the module's
host value slots (APP_CMD_SET/GET_SYS_HOST_VALUE 1021/1022, companion
PR; slot 0 switch, 1 idle seconds, 2 unplug), loaded at the first
auto-off poll and saved by CMD_PM5_BWM_AUTOOFF with 1 s per write, so a
silent or older module still leaves the reply inside the client's 5 s
wait. Without a module, or with an older one, the defaults apply and the
status says "not stored".

hw bwm autooff [on|off] [--idle <sec>] [--unplug <on|off>]; no argument
shows the state, one fact per line: switch, idle timeout, unplug
power-off, stored on module, USB power, USB seen since boot, unplug
trigger armed, tracked client, the module's BLE state, idle time.
Payload [action][enabled][idle_s u32][unplug, optional] with keep
sentinels; both actions are non-zero so an old firmware, which reads
byte 0 as the enable flag, can only be left enabled. hw status prints
the module's live BLE state and the auto power-off setting; at debug
level also the tracked client, which should agree with the live state.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:46 +02:00
MsprgandClaude Fable 5.1 5e1b04def1 pm5: hw bwm wifipower, WiFi off or the modem power-save type
New `hw bwm wifipower [off|none|min|max]`. `off` turns the BWM's WiFi
fully off (BLE-only, persisted; the same path as `hw bwm wifi stop`,
and the module's default state). The other three set the ESP's persisted
WiFi modem power-save type (CMD_PM5_BWM_WIFI_PS 0x0182, module commands
2052/2053, esp_wifi_set_ps()) used while WiFi is up: min (the ESP-IDF
default, the modem sleeps between DTIM beacons), max (sleeps for the
whole listen interval, lowest current, slower to react), none (modem
always on, lowest latency). With no argument it reports whether WiFi is
up at all and the stored type. Independent of `hw bwm powersave`.

The link helper's "cmd failed" diagnostic is now printed only at debug
level: a status query while WiFi is off fails by design and was
cluttering every call.

Verified on hardware: default reads min with WiFi off; none, max and
min each set and read back; off tears WiFi down; an unknown value is
rejected; max survives a module reboot. Not measured with WiFi
associated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:46 +02:00
MsprgandClaude Fable 5.1 552276c75f pm5: hw bwm powersave
New `hw bwm powersave [on|off]` (CMD_PM5_BWM_POWERSAVE 0x0181) shows or
sets the ESP's persisted power-save switch (companion Proxmark5_BWM_esp32
PR: DFS, light sleep and slow advertising after a 30 s fast phase, or
the stock always-on behaviour), so the two can be A/B measured and the
saving turned off when chasing a link problem. `hw status` prints the
state next to the BWM firmware version.

Both module queries in `hw status` are time-bounded (version 2 x 800 ms,
power save 300 ms): a module that is silent, or a firmware without the
command, cannot push the report past the client's timeout.

Verified on hardware: state read/set/persist across an ESP reboot,
`hw status` after 4, 15 and 40 s idle in power-save mode and after 6 s
idle with it off.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:46 +02:00
4f4acedbdd pm5: reduce idle power (core clock scaling + WFI idle)
Port the "reduce idle power" feature from the Fantasi firmware
(soeinova/Fantasi @ 9671309, plus its later ACC-SOF fix) to the PM5
(AT32F435) target.

Problem
-------
The PM5 firmware ran the ARM core at 288 MHz with a 1.3 V LDO and a
main loop that busy-polled at full speed even when the device was idle.
The 288 MHz PLL and the 1.3 V LDO dominate idle draw, and the busy loop
adds to it. Nothing scaled back when there was no work.

Change
------
New armsrc/pm5_power.c drives a refcounted clock/voltage governor:
- Idle (nothing pending, boost count 0, boot grace elapsed, FPGA off):
  take SCLK off the PLL onto HICK-48, power the PLL down, drop the LDO
  to 1.1 V, park the FPGA 24 MHz clock (PA8 low), and halt the core in
  WFI until the next IRQ or the 1 ms SysTick wake.
- Any timing-critical work holds a 288 MHz boost: AppMain wraps
  PacketReceived (which on the PM5 also covers a standalone mode, since
  it is only entered through a command there); the BWM low-batt poll
  and the RGB indicator wrap their I2C. Boost is refcounted and
  IRQ-guarded.
- USB (OS image) moves to the crystal-less HICK-48 clock, ACC-trimmed
  against the SOF, so powering the PLL down never touches USB. The
  bootrom keeps the HEXT/PLL USB path (AS_BOOTROM), so DFU flashing is
  unchanged.
- bwm_uart_clock_update() re-inits UART4 through the SDK usart_init()
  on each clock switch (the divider set at 288 MHz is 6x off at 48 MHz).
- FpgaIsOff() (fpga_core.c, PM5 only) tracks the last conf word so the
  governor never downclocks while a reader field / emulation is running.
- Wake preamble on the BWM link: the companion module firmware
  (Proxmark5_BWM_esp32) light-sleeps 2 s after the last byte on the link
  and its UART wakes on RX edges but loses the bytes that carried them.
  bwm_uart_write() therefore leads with four 0x55 bytes and a 10 ms
  settle on its first write and after 1 s without a write of its own
  (RX is not tracked: the ring can be drained long after the bytes
  came in). The module only starts light-sleeping once it has seen a
  preamble, so the one sent while it was still booting is repeated
  after the boot-time link negotiation. A module firmware without light
  sleep drops the preamble as noise.

New `hw powersave on|off` toggles the governor at runtime (default on);
`hw status` reports the idle-clock and WFI-asleep fractions.

Behaviour change
----------------
- PM5 idles at 48 MHz with the PLL off and the CPU halted; it boosts to
  288 MHz for commands and BWM housekeeping. Command timing and
  throughput are unchanged (intra-command work is always boosted).
- PM5 USB now runs off HICK-48 (ACC-trimmed) instead of the HEXT PLL.
- New command id CMD_PM5_POWERSAVE (0x0180).

Testing
-------
Built warning-free (gcc) for PLATFORM=PM5 (with and without
PLATFORM_EXTRAS=BWM), PM3RDV4 and PM3GENERIC, armsrc and bootrom. Client
built via the default Makefile. Not built with clang or on
Windows/macOS; nothing outside PM5/AT32 guards changes.

Flashed to a PM5 and exercised against a MIFARE Classic 4K card:
hw version/status/ping (4000 & 512 B), hw tune, hf search, hf 14a
info/reader (x12 across idle gaps), hf mf info/chk/rdbl, lf search/read,
hw powersave off/on, held-field (raw -sk) correctly blocks the downclock,
hw reset + re-enumerate. All pass; USB stays up across downclocks. Idle
reaches ~80-95% at 48 MHz and ~90%+ WFI-halted after a few seconds idle.
Measured at the USB port: idle draw 283 mA -> 207 mA with the stock
BWM firmware.

Co-authored-by: Soei Nova <soeinova@proton.me>
Co-authored-by: noproto <noproto@zeroday.engineering>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:46 +02:00
Philippe Teuwen 339db44cf1 Mask all bwm commands if device is not compiled for it, avoids copy/paste of bwm commands 2026-09-23 13:47:31 +02:00
Niel Nielsen 51b605c87e Update BWM command condition in cmdhw.c
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-23 13:44:57 +02:00
Niel Nielsen b534f06109 Add IfBwm function declaration in cmdparser.h
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-23 13:44:57 +02:00
Niel Nielsen 9c12e30b79 Implement IfBwm function for BWM capability check
Added IfBwm function to check BWM capability based on PM3 presence.

Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-23 13:44:57 +02:00
歐歪andClaude Opus 5 12b0143f46 fix: hf 15 cfinalize does not erase the tag memory
The help text and the -y warning said finalize erases the whole tag memory
to 00000000. A dump of a tag before and after finalize shows otherwise: the
configuration blocks are cleared, but user data is relocated rather than
erased. Data written to block 0x38 before finalize read back at block 0x20
afterwards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 22:51:14 +08:00
iceman1001 495a72e347 text 2026-09-22 15:56:59 +02:00
iceman1001andClaude Opus 5 e824501492 hf mf autopwn: detect static encrypted nonce via the backdoor key
When no sector key was known, autopwn probed for a static encrypted
nonce using the default key FFFFFFFFFFFF. On a FM11RF08S with
non-default keys that auth fails, the probe returns NONCE_FAIL, and
autopwn fell through to darkside/nested instead of the sen recovery.
The later re-detections are gated on known_key, so nothing recovered it.

Probe the known Fudan backdoor keys first, as 'hf mf info' and
'hf mf sen' already do, and run the detection with MF_KEY_BD. The
default key stays as the fallback for cards without a backdoor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 15:41:32 +02:00
歐歪andClaude Opus 5 14a5de0bdf fix: let the device verdict decide the outcome of hf 15 csetuid
The client only inspected PM3_EWRONGANSWER and then re-verified with its own
getUID(), which asks whether the tag carries the requested UID, not whether
this command wrote it. Running csetuid --v3 twice with the same UID printed
"Setting new UID ( ok )" on the second run even when the device had reported
PM3_ESOFT, because the UID written by the first run still matched.

Gen1 and V3 now switch on the device status, which already covers tag
identification and UID verification. V2 keeps the client side comparison,
the device does neither for it. PM3_ETIMEOUT is handled as well, cfinalize
already did that.

The cfinalize help text and its confirmation warning said the configuration
area is erased. Finalize erases the whole tag memory to 00000000.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 21:08:18 +08:00
iceman1001andClaude Opus 5 7346db71ff hf mfu view: print the stored UL-C / UL-AES key
`hf mfu dump -k` appends the authentication key to the dump file, but
`view` never showed it. The key was there in the hex listing all along,
split across four pages and byte-swapped, which is not something you read
off the screen.

With `-v`, view now prints it. UL-C takes pages 0x2C..0x2F, UL-AES pages
0x30..0x33, and both are unswapped back into the form `-k` expects, so the
printed line can be pasted straight into the next command. The existing
ulc_print_3deskey() does the UL-C side; ulaes_print_key() is its UL-AES
sibling.

Tag type comes from the dump header rather than a card: the three UL-AES
version signatures that ul_select_card() matches on a live tag, and for
UL-C, which answers no GET_VERSION, all-zero version bytes plus a page
count of 0x2F. Key pages that are all zero say so and point at `dump -k`,
since a dump taken without a key legitimately has none.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 15:05:38 +02:00
iceman1001 7ee301a38f added another one 2026-09-22 14:54:44 +02:00
iceman1001 de2aaa48d8 text 2026-09-22 14:48:54 +02:00
iceman1001andClaude Opus 5 e3be641809 hf mf: honour --ns in the FM11RF08S nonce recovery
`hf mf autopwn --ns` asks for no files, but on an FM11RF08S it wrote
them anyway. no_save is read once and only gates the two save blocks
at the end of autopwn; the static encrypted nonce is detected earlier
and returns into HFMFSENRecover from five places, none of which saw
the flag. The recovery path wrote the key file and the dump
unconditionally, and now that the dump carries JSON as well, that was
three files against a flag asking for none.

no_save travels with the call now, and fm11_save_recovery_outputs
returns before it names anything, printing the same "Called with no
save option" line that `hf mf dump` and autopwn already print. The key
table is still shown, so --ns reads the card and leaves nothing on
disk.

`hf mf sen` gains the same `--ns` option, so the flag does not depend
on which command reached the recovery.

--ns does not suppress `--keep-nonces`. That option exists only to
write its evidence file, and a blanket no-save would leave no way to
ask for the evidence without a dump.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 14:24:34 +02:00
iceman1001andClaude Opus 5 42b8fb8367 hf mf: carry --suffix into the FM11RF08S nonce recovery
`hf mf autopwn --suffix <txt>` names its output files
`hf-mf-<uid>-<dump|key>-<suffix>`, but on an FM11RF08S the suffix was
dropped. autopwn detects the static encrypted nonce and returns into
HFMFSENRecover from five separate places, all of them before its own
file naming runs, and the recovery path built `hf-mf-<uid>-key` and
`hf-mf-<uid>-dump` from the card UID alone. Two runs against the same
card overwrote each other, which is the collision the suffix exists to
prevent.

The suffix now travels with the call, so a card autopwn hands over is
named the same way as one autopwn finishes itself.

`hf mf sen` gains the same `--suffix` option, so the naming no longer
depends on which command reached the recovery. It covers the
`--keep-nonces` evidence file too, which collided the same way.

The three names are built by fm11_build_filename() instead of by three
snprintf calls that have to agree on the template.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 14:22:18 +02:00
iceman1001 38e733e37d fix #3655 2026-09-22 13:56:07 +02:00
iceman1001andClaude Opus 5 950ba026bf hf mfdes: check the PICC master key, keep --key, and read 0x1A on its own
`hf mfdes chk` reported "No keys found" on a card whose PICC master key was
still the factory default, and missed a key handed to it on the command line.

The AID list comes from GetApplicationIDs, which never returns 000000, so the
PICC level was never swept - only an explicit `--aid 000000` reached it. That
list is itself gated by PICC key settings bit 1, and a card refusing it aborted
the whole run rather than falling back to the one key number still reachable.
000000 now seeds the sweep, and a refused list leaves the PICC level to check.

A key given with `--key` was written into the key list at parse time and then
overwritten: every round resets the length and refills the same buffer from
index 0. It was counted in "Loaded N keys" and never tried. It is now
re-inserted at the head of its list after each fill, so it is attempt one.

When key settings are unreadable the fallback walked the file access rights to
name the key numbers in use. GetFileIDs, GetISOFileIDs and GetFileSettings are
gated by the same settings bit that just refused GetKeySettings, so that
fallback could never run. GetKeyVersion is not gated at all and names the key numbers
directly, absent ones answering 0x40 NO_SUCH_KEY.

AuthenticateISO (0x1A) was read only when Authenticate (0x0A) had answered
first, but 0x1A covers DES, 2TDEA and 3TDEA where 0x0A covers only the first
two. An application holding 3TDEA keys answers 0x1A alone, so it named no key
type and was skipped outright. chk and detect both read it on its own now.
Because 0x1A cannot separate 2TDEA from 3TDEA, detect also drops a key type on
the first wrong-length challenge instead of waiting for its error counter.

Measured on a DESFire EV2 2K: PICC master key and an application AES key are
both recovered in one run, and the unauthenticated GetKeyVersion sweep keeps
answering across a run of NO_SUCH_KEY errors with no re-select.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 13:55:06 +02:00
iceman1001 ee20f50965 fix duplicates 2026-09-22 12:49:50 +02:00
iceman1001andClaude Opus 5 a3d32ac220 hf mfdes view: name known AIDs
The dump viewer printed bare AID numbers while the live card paths
already resolved them through AIDDFGetCommentStr(). Use the same lookup
here, falling back to the old separator line when the AID is unknown.

    --- AID 578000 --- NORTIC (Card Issuer App) [NPRA]

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 12:46:06 +02:00
iceman1001 577f6ef45c added entries from Metrodroid project 2026-09-22 12:38:15 +02:00
iceman1001 4d1414a0e9 fix duplicate entries in aid file 2026-09-22 12:36:56 +02:00
Iceman f7dd78ebc5 Merge branch 'master' into pm3-iclass-sio-unknown-tags
Signed-off-by: Iceman <iceman@iuse.se>
2026-09-22 16:22:25 +07:00
iceman1001 0aa6c96c22 hook in parser 2026-09-22 11:20:15 +02:00
iceman1001andClaude Opus 5 54e004e252 hf mfdes: add NORTIC travel card parser
NORTIC (Norwegian Ticketing Interoperable Concept) is the Norwegian
national public transport card, on DESFire since 2008 and specified by
Statens vegvesen, now Jernbanedirektoratet, in Handbok V821.

Decodes the card issuer header, file 0C of AID 578000. That file is
free to read, so the parser produces output on any Norwegian card
without key material: country code, format, choice bitmap, card serial
number, validity end date, application owner company, retailer
organisation and card key version.

The transport application, AID 578001, is listed by file with its role
and the key it needs, and any file that was read is dumped as hex. Its
field layouts are in Handbok V821 Del 18, which has no download link,
and every file needs read key 7, which is not public. Nothing there is
guessed at.

NORTIC is EN 1545 and packs fields most significant bit first, which is
the opposite of the RKF Type CL-1 layout in parserkf.c where RKF-0022
7.4.1 numbers bits from the least significant end. Reading a NORTIC
header the RKF way gives country code 144 instead of 578, so the self
test asserts that as a negative control.

Verified field by field against the published card issuer header, every
field matching its documented value, including the validity date 6938
decoding to 2015-12-31. 'hf mfdes view --selftest' runs the checks,
including a round trip through an encoder.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 11:19:24 +02:00
iceman1001andClaude Opus 5 28d49510e2 hf mf: add RKF travel card parser
The RKF travel card (Resekortsföreningen i Norden) is the Nordic public
transport card, specified 2001-2002 on MIFARE Classic and deployed by
Västtrafik, SL Stockholm, Länstrafiken and the Danish Rejsekort.

Decodes the card issuer and support layers (CMI, TCCI, TCAS, TCDI) and
every application object the specification pins down: TCPU purse, TCEL
event log, TCDB, TCCP, TCST, and TCTI/TCCO through an identifier chain
walker covering all 18 data element groups. AID owners are named from
RKF-0019.

Three things the specification does not state, determined from 21 cards:

  - RKF-0022 7.7.1 calls the block checksum 'standard CRC-8' without
    naming the parameters. It is CRC-8/MIFARE-MAD, poly 0x1D init 0xC7,
    which the tree already ships as CRC8Mad().

  - The MAC trio is the final 24 bits of a MACed object. The RKF-0023
    tables list Free (bits) as a trailing summary row, which reads as if
    the authenticator follows a gap after the key identifier; it does
    not. Measured on the fact that MACAlgorithmIdentifier must be 0:
    TCST 42/42 against 15/42, TCCP 20/20 against 13/20.

  - TCPU version 4 drops EndDate and moves Value to bit 16.

Cards reporting a version other than 2 are flagged, and a MAC algorithm
the specification does not define is reported as a layout failure rather
than printed as data.

MAC values are not verified. The master key is in RKF-0018, which was
never published. des_mac() is added to libpcrypto for when that changes,
with the FIPS 113 worked example as its test.

Verified against a Danish Rejsekort whose provenance notes give the
purchase timestamp, card number and balance; the parser reproduces all
three. 48 self test checks on 'hf mf view --selftest', no false
positives across 460 local dumps.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 11:14:44 +02:00
iceman1001andClaude Opus 5 82597a32c5 fix: don't treat a RATS NAK as a successful ISO14443-4 activation
iso14443a_select_cardEx() only rejected silence after RATS. A card that
answered with a 4 bit NAK came back as Demod.len == 1, was stored as a
one byte ATS and reported to the client as select status 1, meaning L4
activated. The client then sent GetVersion (90 60 00 00 00) as an I-block
to a card that never left ISO14443-3, and every caller ended with
'timeout while waiting for reply' / 'No tag detected or other tag
communication error (-4)'.

The bogus TL of 0x04 also made iso14a_set_ATS_times() look for a T0 and
read past the single received byte.

Check that the answer to RATS is a well formed ATS, TL plus the bytes TL
counts plus the two CRC bytes, before accepting it. Anything else returns
2, selected but ISO14443-3 only, so the card stays usable over the Classic
path instead of failing detection.

The client side 'try to request ATS even if the tag claims not to support
it' retry had the same hole and stored whatever came back in card.ats, so
guard all four copies of it the same way.

Reported on a clone with ATQA 0004 / SAK 28 that advertises ISO14443-4 it
does not implement. hf mf autopwn on it needed hf 14a config --rats skip
to get past detection; with this it takes the Classic path on its own.

Fixes #3659

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 11:05:07 +02:00
歐歪 13d8d7ddf3 Merge branch 'RfidResearchGroup:master' into 20260917-15magic-fix 2026-09-22 16:34:08 +08:00
歐歪 dfa3255fd6 fix: refine hint messages for V3 tag finalization status 2026-09-22 16:30:43 +08:00
Iceman be8a0d524e Merge pull request #3657 from 0x6r1an0y/20260922-makefile-fix
fix: preserve UTF-8 in generated command docs
2026-09-22 06:59:35 +07:00
歐歪andClaude Opus 5 f0a502b279 refactor: move ISO15693 magic tag handling to the device side
Identification, writing and failure handling for magic Gen1 and V3 tags
now runs in a single field session on the device, which reports
PM3_SUCCESS only when the UID reads back as requested.

armsrc/iso15693.c:
- SetTag15693Uid() probes 0x38/0x39/0x3E/0x3F and refuses to write when
  any of them answers with data, verifies the UID afterwards and blanks
  the four blocks again when it did not change.
- SetTag15693Uid_v3() and FinalizeTag15693_v3() are new, both check the
  0x14/0x15 configuration signature before writing and verify after.

client/src/cmdhf15.c:
- csetuid and cfinalize now only select a command and print the status.
  The V3 block helpers and constants move to armsrc, which restores the
  original read helper with its CALLOC message and the
  ISO15_ERROR_HANDLING_* macros.

Behaviour change: hf 15 cfinalize without -y no longer energizes the tag
and no longer reports configuration mode before asking for confirmation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 07:04:16 +08:00
iceman1001andClaude Opus 5 4cca8df3a0 hf mfu: support UL-AES in restore, fix info config output
restore refused UL-AES outright.  The generic page plan assumes the last
four pages are cfg0/cfg1/PWD/PACK, which on UL-AES are the AES keys, so
the data loop walked over the lock page, both config pages and the key
lock bits at 0x2D.  Give UL-AES its own plan: user memory 0x04..0x27,
and lock/cfg0/cfg1 behind -s.  Key pages 0x30..0x37 are left alone, the
dump only ever carries AESKey0 and 0x34..0x37 read back as zeros.

info never printed the UL-AES config.  ulaes_print_configuration() is
called with 0x29 but its first branch tested for 0x2C, so cfg0 and cfg1
fell through to the bare return and only the CMAC page showed up.

info also read page 0x34 as if it were an EV1 config page.  That is the
UIDRetrKey.  Genuine silicon NAKs the read, but a magic or simulated
card would have had its key printed as cfg0/cfg1/PWD/PACK.

Tested on a UL-AES with AUTH0=4, PROT set and secure messaging on.
Restore of an unmodified dump comes back byte identical; a dump carrying
DEADBEEF at 0x27 and CAFEBABE at 0x2B writes 0x27 and leaves 0x2B alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 00:23:02 +02:00
歐歪andOpenAI Codex 6775a78a10 fix: preserve UTF-8 in generated command docs
Transcode the MinGW client's CP850 output before writing command documentation and force Python to read the converted stream as UTF-8. Normalize the remaining escaped Greek mu strings to the project's micro sign convention.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
2026-09-22 05:37:09 +08:00
歐歪andClaude Sonnet 5 e74bb0f2e5 fix: 15693 csetuid v1 probe, single session write and safer rollback
- probe: only no answer / block not available count as unreadable, other read errors abort
- 69960000 is only accepted in 0x3F, the other blocks must be blank
- write 0x3E/0x3F/0x38/0x39 in one RF session
- rollback only when the tag still shows its original UID, message lists all four blocks

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 03:25:47 +08:00
歐歪 cd24d8b0b0 fix: rewind if 0x38 0x39 write fails 2026-09-21 18:27:45 +08:00
歐歪 f7a9682a9e fix: silent v1 pre-check and refine the commands 2026-09-21 18:06:42 +08:00
歐歪 473dcb0aa5 fix: v1 tag probing fail message clear 2026-09-21 17:34:37 +08:00
歐歪 e1e023ea61 feat: 15693 csetuid v1 v3 check before write 2026-09-21 17:11:23 +08:00