Merge pull request #448 from DidierA/parity-check

hf 14a sniff: add parity checks
This commit is contained in:
GameTec-live
2026-09-07 17:24:42 +02:00
committed by GitHub
2 changed files with 20 additions and 7 deletions
+15 -7
View File
@@ -31,6 +31,7 @@ from chameleon_utils import (
execute_tool,
tqdm_if_exists,
print_key_table,
odd_parity_byte,
default_cwd
)
@@ -7797,7 +7798,7 @@ class HF14ASniff(BaseCLIUnit):
# Bit 15 of szBits: 0 = reader→card, 1 = card→reader (new firmware).
# Old firmware always sends bit15=0; parser is backward compatible.
buf = bytes(resp.data)
frames = [] # (szBits, data, is_tx)
frames = [] # (szBits, data, is_tx, parity)
i = 0
while i + 2 <= len(buf):
hdr = (buf[i] << 8) | buf[i+1]
@@ -7812,6 +7813,9 @@ class HF14ASniff(BaseCLIUnit):
raw = buf[i:i+szBytes]
i += szBytes
# parity array: parity for each byte of data array
parity_bits = []
# ISO14443-A frames include one parity bit per byte.
# Short frames (< 8 bits, e.g. REQA=7 bits) have no parity.
# All other frames: szBits = data_bytes * 9, strip every 9th bit.
@@ -7827,19 +7831,20 @@ class HF14ASniff(BaseCLIUnit):
for b in range(8):
val |= all_bits[nb * 9 + b] << b
stripped.append(val)
parity_bits.append(all_bits[nb * 9 + 8])
data = bytes(stripped)
szBits = n_bytes * 8
else:
data = raw
frames.append((szBits, data, is_tx))
frames.append((szBits, data, is_tx, parity_bits))
if not frames:
print(f"{CR}No frames decoded{C0}")
return
rx_count = sum(1 for _, _, tx in frames if not tx)
tx_count = sum(1 for _, _, tx in frames if tx)
rx_count = sum(1 for _, _, tx, _ in frames if not tx)
tx_count = sum(1 for _, _, tx, _ in frames if tx)
if tx_count > 0:
print(f" Captured : {CG}{len(frames)}{C0} frame(s) "
f"({CY}{rx_count}{C0} reader→card {CG}{tx_count}{C0} card→reader)")
@@ -7855,8 +7860,11 @@ class HF14ASniff(BaseCLIUnit):
last_auth_keytype = None
last_auth_block = None
for n, (szBits, data, is_tx) in enumerate(frames):
hex_str = ' '.join(f'{b:02x}' for b in data)
for n, (szBits, data, is_tx, parity_bits) in enumerate(frames):
if (len(data) == len(parity_bits)):
hex_str = ' '.join(f"{b:02x}{'!' if odd_parity_byte(b)!= p else ' '}" for (b,p) in zip(data,parity_bits))
else:
hex_str = ' '.join(f"{b:02x}" for b in data)
# is_tx==True means CU transmitted (card -> reader).
# is_tx==False means reader -> card.
@@ -7898,7 +7906,7 @@ class HF14ASniff(BaseCLIUnit):
# Summary block (pass only reader→card frames for protocol decode)
print()
_print_14a_sniff_summary(frames) # full frames needed for nonce extraction
_print_14a_sniff_summary([(szBits, data, is_tx) for (szBits, data, is_tx,parity) in frames]) # full frames needed for nonce extraction
@hf_14a.command("auth-trace")
+5
View File
@@ -209,6 +209,11 @@ def parity_to_str(nt_par_err):
]
)
def odd_parity_byte(value: int) -> int:
assert(value >=0 and value <= 255)
for i in [4, 2, 1]:
value ^= value >> i
return (value & 1) ^ 1
def execute_tool(tool_name, args):
if sys.platform == "win32":