Merge pull request #307 from azuwis/hf-14a-config

Add `hf 14a config` to deal with badly configured cards
This commit is contained in:
GameTec-live
2026-02-06 11:19:23 +01:00
committed by GitHub
8 changed files with 207 additions and 2 deletions
+83
View File
@@ -13,6 +13,7 @@ import serial.tools.list_ports
import threading
import struct
import queue
from enum import Enum
from multiprocessing import Pool, cpu_count
from typing import Union
from pathlib import Path
@@ -756,6 +757,88 @@ class HWVersion(DeviceRequiredUnit):
print(f' - Chameleon {model}, Version: {fw_version} ({git_version})')
@hf_14a.command('config')
class HF14AConfig(DeviceRequiredUnit):
class Config(Enum):
def __new__(cls, value, desc):
obj = object.__new__(cls)
obj._value_ = value
obj.desc = desc
return obj
@classmethod
def choices(cls):
return [elem.name for elem in cls]
@classmethod
def format(cls, index):
item = cls(index)
color = CG if index == 0 else CR
return f' - {cls.__name__.upper()} override: {color_string((color, item.name))} ( {item.desc} )'
@classmethod
def help(cls):
return ' / '.join([f'{elem.desc}' for elem in cls])
class Bcc(Config):
std = (0, "follow standard")
fix = (1, "fix bad BCC")
ignore = (2, "ignore bad BCC, always use card BCC")
class Cl2(Config):
std = (0, "follow standard")
force = (1, "always do CL2")
skip = (2, "always skip CL2")
class Cl3(Config):
std = (0, "follow standard")
force = (1, "always do CL3")
skip = (2, "always skip CL3")
class Rats(Config):
std = (0, "follow standard")
force = (1, "always do RATS")
skip = (2, "always skip RATS")
def args_parser(self) -> ArgumentParserNoExit:
parser = ArgumentParserNoExit()
parser.description = 'Configure 14a settings (use with caution)'
parser.add_argument('--std', action='store_true', help='Reset default configuration (follow standard)')
parser.add_argument('--bcc', type=str, choices=self.Bcc.choices(), help=self.Bcc.help())
parser.add_argument('--cl2', type=str, choices=self.Cl2.choices(), help=self.Cl2.help())
parser.add_argument('--cl3', type=str, choices=self.Cl3.choices(), help=self.Cl3.help())
parser.add_argument('--rats', type=str, choices=self.Rats.choices(), help=self.Rats.help())
return parser
def on_exec(self, args: argparse.Namespace):
change_requested = False
if args.std:
config = {'bcc': 0, 'cl2': 0, 'cl3': 0, 'rats': 0}
change_requested = True
else:
config = self.cmd.hf14a_get_config()
if args.bcc:
config['bcc'] = self.Bcc[args.bcc].value
change_requested = True
if args.cl2:
config['cl2'] = self.Cl2[args.cl2].value
change_requested = True
if args.cl3:
config['cl3'] = self.Cl3[args.cl3].value
change_requested = True
if args.rats:
config['rats'] = self.Rats[args.rats].value
change_requested = True
if change_requested:
self.cmd.hf14a_set_config(config)
config = self.cmd.hf14a_get_config()
print('HF 14a config')
print(self.Bcc.format(config['bcc']))
print(self.Cl2.format(config['cl2']))
print(self.Cl3.format(config['cl3']))
print(self.Rats.format(config['rats']))
@hf_14a.command('scan')
class HF14AScan(ReaderRequiredUnit):
def args_parser(self) -> ArgumentParserNoExit:
+26
View File
@@ -425,6 +425,32 @@ class ChameleonCMD:
i += 14
return resp
@expect_response(Status.SUCCESS)
def hf14a_get_config(self):
"""
Get hf 14a config
:return:
"""
resp = self.device.send_cmd_sync(Command.HF14A_GET_CONFIG)
if resp.status == Status.SUCCESS:
bcc, cl2, cl3, rats = struct.unpack('!bbbb', resp.data)
resp.parsed = {'bcc': bcc,
'cl2': cl2,
'cl3': cl3,
'rats': rats}
return resp
@expect_response(Status.SUCCESS)
def hf14a_set_config(self, data):
"""
Set hf 14a config
:return:
"""
data = struct.pack('!bbbb', data['bcc'], data['cl2'], data['cl3'], data['rats'])
return self.device.send_cmd_sync(Command.HF14A_SET_CONFIG, data)
@expect_response(Status.LF_TAG_OK)
def em410x_scan(self):
"""
+2
View File
@@ -73,6 +73,8 @@ class Command(enum.IntEnum):
MF1_HARDNESTED_ACQUIRE = 2013
MF1_ENC_NESTED_ACQUIRE = 2014
MF1_CHECK_KEYS_ON_BLOCK = 2015
HF14A_GET_CONFIG = 2200
HF14A_SET_CONFIG = 2201
EM410X_SCAN = 3000
EM410X_WRITE_TO_T55XX = 3001