mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-09-11 18:30:14 -07:00
Merge pull request #448 from DidierA/parity-check
hf 14a sniff: add parity checks
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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":
|
||||
|
||||
Reference in New Issue
Block a user