From 36daf7038cb77a062fac8cd4f075bf00c837e952 Mon Sep 17 00:00:00 2001 From: Niel Nielsen Date: Thu, 7 May 2026 20:07:23 +0200 Subject: [PATCH] Add files via upload --- software/script/chameleon_cli_unit.py | 11 +++++++++-- software/script/chameleon_cmd.py | 28 ++++++++++++++++++--------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index c2f8cdf..a181d61 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -7583,10 +7583,15 @@ class HF14AAuthTrace(ReaderRequiredUnit): "-k", "--key", type=str, required=True, metavar="", help="6-byte sector key (12 hex chars)" ) + parser.add_argument( + "-t", "--timeout", type=int, default=5000, metavar="", + help="Tag-presence polling timeout in ms (1-30000, default 5000)" + ) parser.epilog = """ examples: hf 14a auth-trace --blk 0 -k FFFFFFFFFFFF hf 14a auth-trace --blk 4 -b -k A0A1A2A3A4A5 + hf 14a auth-trace --blk 0 -k FFFFFFFFFFFF -t 10000 # wait up to 10s for tag """ return parser @@ -7599,13 +7604,15 @@ examples: key_bytes = bytes.fromhex(key_hex) key_type = 0x61 if args.b else 0x60 block = args.blk + timeout_ms = max(1, min(30000, int(args.timeout))) print(f" Running auth trace: block={block} keyType={'B' if args.b else 'A'} key={key_hex.upper()}") - print(" Place CU on a MIFARE Classic card now.") + print(f" Waiting up to {timeout_ms} ms for a MIFARE Classic card... " + f"({CY}place CU on a card now{C0})") print() try: - resp = self.cmd.hf14a_auth_trace(block, key_type, key_bytes) + resp = self.cmd.hf14a_auth_trace(block, key_type, key_bytes, timeout_ms=timeout_ms) except Exception as e: if 'CMDInvalid' in type(e).__name__ or '2017' in str(e): print(f"{CR}Command not supported — reflash firmware to enable hf 14a auth-trace{C0}") diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index c0d9ef4..a5c3e84 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -535,28 +535,38 @@ class ChameleonCMD: timeout_s = (timeout_ms // 1000) + 5 return self.device.send_cmd_sync(Command.HF14A_SNIFF, payload, timeout=timeout_s) - def hf14a_auth_trace(self, block: int, key_type: int, key: bytes): + def hf14a_auth_trace(self, block: int, key_type: int, key: bytes, timeout_ms: int = 5000): """ Run a full reader-side ISO14443A + MIFARE Classic Crypto1 auth flow against a real card and return every wire frame for inspection. - The firmware performs anticoll + SELECT + (optional RATS) + AUTH and - packs all frames — synthesized anticoll plus the live AUTH/NT/NR||AR/AT - — into the same buffer format used by hf14a_sniff: + The firmware polls for a tag in the field for up to `timeout_ms` + milliseconds, then performs anticoll + SELECT + (optional RATS) + + AUTH and packs all frames — synthesized anticoll plus the live + AUTH/NT/NR||AR/AT — into the same buffer format used by hf14a_sniff: [2 bytes: bit count, big-endian] [N bytes: frame data, ceil(bits/8)] ... Bit 15 of the bit-count header: 0 = reader→card, 1 = card→reader. - :param block: target block number (0-255) - :param key_type: 0x60 (Key A) or 0x61 (Key B) - :param key: 6-byte sector key + :param block: target block number (0-255) + :param key_type: 0x60 (Key A) or 0x61 (Key B) + :param key: 6-byte sector key + :param timeout_ms: tag-presence polling timeout in ms (1-30000) :return: Raw response — check .status and .data """ if key_type not in (0x60, 0x61): raise ValueError("key_type must be 0x60 (Key A) or 0x61 (Key B)") if len(key) != 6: raise ValueError("key must be exactly 6 bytes") - payload = bytes([key_type, block & 0xFF]) + bytes(key) - return self.device.send_cmd_sync(Command.HF14A_AUTH_TRACE, payload, timeout=3) + timeout_ms = max(1, min(30000, int(timeout_ms))) + payload = ( + bytes([key_type, block & 0xFF]) + + bytes(key) + + bytes([(timeout_ms >> 8) & 0xFF, timeout_ms & 0xFF]) + ) + # Add a couple of seconds of slack on top of the device-side polling + # window so the USB/BLE round-trip doesn't time out before firmware + # gives up on its own. + return self.device.send_cmd_sync(Command.HF14A_AUTH_TRACE, payload, timeout=(timeout_ms // 1000) + 3) @expect_response(Status.SUCCESS) def hf14a_get_config(self):