python style

This commit is contained in:
Philippe Teuwen
2023-08-26 02:24:08 +02:00
parent 65368dffff
commit a596e98bf7
5 changed files with 130 additions and 103 deletions
+6 -7
View File
@@ -1,11 +1,8 @@
#!/usr/bin/env python3
import argparse
import platform
import sys
import traceback
import types
import chameleon_com
import chameleon_cmd
import colorama
import chameleon_cli_unit
import chameleon_utils
@@ -29,7 +26,7 @@ LITE = r"""
"""
# create by http://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=Chameleon%20Ultra
BANNER = f"""
BANNER = """
██████╗██╗ ██╗ █████╗ ██╗ ██╗███████╗██╗ ███████╗ █████╗ ██╗ ██╗
██╔════╝██║ ██║██╔══██╗███╗ ███║██╔════╝██║ ██╔════╝██╔══██╗███╗ ██║
██║ ███████║███████║████████║█████╗ ██║ █████╗ ██║ ██║████╗██║
@@ -46,12 +43,14 @@ class ChameleonCLI:
def __init__(self):
self.completer = chameleon_utils.CustomNestedCompleter.from_nested_dict(chameleon_cli_unit.root_commands)
self.session = prompt_toolkit.PromptSession(completer=self.completer, history=FileHistory(pathlib.Path.home() / ".chameleon_history"))
self.session = prompt_toolkit.PromptSession(completer=self.completer,
history=FileHistory(pathlib.Path.home() / ".chameleon_history"))
# new a device communication instance(only communication)
self.device_com = chameleon_com.ChameleonCom()
def get_cmd_node(self, node: chameleon_utils.CLITree, cmdline: list[str]) -> tuple[chameleon_utils.CLITree, list[str]]:
def get_cmd_node(self, node: chameleon_utils.CLITree,
cmdline: list[str]) -> tuple[chameleon_utils.CLITree, list[str]]:
"""
Recursively traverse the command line tree to get to the matching node
@@ -92,7 +91,7 @@ class ChameleonCLI:
start listen input.
:return:
"""
if sys.version_info < (3,9):
if sys.version_info < (3, 9):
raise Exception("This script requires at least Python 3.9")
self.print_banner()
+35 -23
View File
@@ -14,7 +14,8 @@ import chameleon_com
import chameleon_cmd
import chameleon_cstruct
import chameleon_status
from chameleon_utils import *
from chameleon_utils import ArgumentParserNoExit, ArgsParserError, UnexpectedResponseError
from chameleon_utils import CLITree
class BaseCLIUnit:
@@ -176,6 +177,7 @@ root_commands: dict[str, CLITree] = {
'lf': lf,
}
@hw.command('connect', 'Connect to chameleon by serial port')
class HWConnect(BaseCLIUnit):
def args_parser(self) -> ArgumentParserNoExit or None:
@@ -189,18 +191,25 @@ class HWConnect(BaseCLIUnit):
def on_exec(self, args: argparse.Namespace):
try:
if args.port is None: # Chameleon auto-detect if no port is supplied
platformname = uname().release
if 'Microsoft' in platformname:
platform_name = uname().release
if 'Microsoft' in platform_name:
path = os.environ["PATH"].split(os.pathsep)
path.append("/mnt/c/Windows/System32/WindowsPowerShell/v1.0/")
for prefix in path:
fn = os.path.join(prefix, "powershell.exe")
if not os.path.isdir(fn) and os.access(fn, os.X_OK):
PSHEXE=fn
PSHEXE = fn
break
if PSHEXE:
#process = subprocess.Popen([PSHEXE,"Get-CimInstance -ClassName Win32_serialport | Where-Object {$_.PNPDeviceID -like '*VID_6868&PID_8686*'} | Select -expandproperty DeviceID"],stdout=subprocess.PIPE);
process = subprocess.Popen([PSHEXE,"Get-PnPDevice -Class Ports -PresentOnly | where {$_.DeviceID -like '*VID_6868&PID_8686*'} | Select-Object -First 1 FriendlyName | % FriendlyName | select-string COM\d+ |% { $_.matches.value }"],stdout=subprocess.PIPE);
# process = subprocess.Popen([PSHEXE,"Get-CimInstance -ClassName Win32_serialport |"
# " Where-Object {$_.PNPDeviceID -like '*VID_6868&PID_8686*'} |"
# " Select -expandproperty DeviceID"],stdout=subprocess.PIPE);
process = subprocess.Popen([PSHEXE, "Get-PnPDevice -Class Ports -PresentOnly |"
" where {$_.DeviceID -like '*VID_6868&PID_8686*'} |"
" Select-Object -First 1 FriendlyName |"
" % FriendlyName |"
" select-string COM\d+ |"
"% { $_.matches.value }"], stdout=subprocess.PIPE)
res = process.communicate()[0]
_comport = res.decode('utf-8').strip()
if _comport:
@@ -253,7 +262,7 @@ class HWChipIdGet(DeviceRequiredUnit):
return None
def on_exec(self, args: argparse.Namespace):
print(f' - Device chip ID: ' + self.cmd.get_device_chip_id())
print(' - Device chip ID: ' + self.cmd.get_device_chip_id())
@hw_address.command('get', 'Get device address (used with Bluetooth)')
@@ -262,7 +271,7 @@ class HWAddressGet(DeviceRequiredUnit):
return None
def on_exec(self, args: argparse.Namespace):
print(f' - Device address: ' + self.cmd.get_device_address())
print(' - Device address: ' + self.cmd.get_device_address())
@hw.command('version', 'Get current device firmware version')
@@ -639,7 +648,7 @@ class HFMFDetectionDecrypt(DeviceRequiredUnit):
recv_count = int(len(tmp) / HFMFDetectionDecrypt.detection_log_size)
index += recv_count
buffer.extend(tmp)
print(f".", end="")
print(".", end="")
print()
print(f" - Download done ({len(buffer)}bytes), start parse and decrypt")
@@ -801,7 +810,7 @@ class HFMFSettings(DeviceRequiredUnit):
if args.write != -1:
self.cmd.set_mf1_write_mode(args.write)
print(f' - Set write mode to {chameleon_cmd.MifareClassicWriteMode(args.write)} success')
print(f' - Emulator settings updated')
print(' - Emulator settings updated')
@hf_mf.command('sim', 'Simulate a Mifare Classic card')
@@ -1028,7 +1037,7 @@ class HWSlotTagType(TagTypeRequiredUnit, SlotIndexRequireUnit):
tag_type = args.type
slot_index = args.slot
self.cmd.set_slot_tag_type(slot_index, tag_type)
print(f' - Set slot tag type success.')
print(' - Set slot tag type success.')
@hw_slot.command('delete', 'Delete sense type data for slot')
@@ -1061,7 +1070,7 @@ class HWSlotDataDefault(TagTypeRequiredUnit, SlotIndexRequireUnit):
tag_type = args.type
slot_num = args.slot
self.cmd.set_slot_data_default(slot_num, tag_type)
print(f' - Set slot tag data init success.')
print(' - Set slot tag data init success.')
@hw_slot.command('enable', 'Set emulation tag slot enable or disable')
@@ -1092,7 +1101,7 @@ class LFEMSimSet(LFEMCardRequiredUnit):
id_hex = args.id
id_bytes = bytearray.fromhex(id_hex)
self.cmd.set_em410x_sim_id(id_bytes)
print(f' - Set em410x tag id success.')
print(' - Set em410x tag id success.')
@lf_em_sim.command('get', 'Get simulated em410x card id')
@@ -1104,7 +1113,7 @@ class LFEMSimGet(DeviceRequiredUnit):
# lf em sim get
def on_exec(self, args: argparse.Namespace):
response = self.cmd.get_em410x_sim_id()
print(f' - Get em410x tag id success.')
print(' - Get em410x tag id success.')
print(f'ID: {response.data.hex()}')
@@ -1153,7 +1162,7 @@ class HWSlotUpdate(DeviceRequiredUnit):
# hw slot update
def on_exec(self, args: argparse.Namespace):
self.cmd.update_slot_data_config()
print(f' - Update config and data from device memory to flash success.')
print(' - Update config and data from device memory to flash success.')
@hw_slot.command('openall', 'Open all slot and set to default data')
@@ -1182,7 +1191,7 @@ class HWSlotOpenAll(DeviceRequiredUnit):
# update config and save to flash
self.cmd.update_slot_data_config()
print(f' - Succeeded opening all slots and setting data to default.')
print(' - Succeeded opening all slots and setting data to default.')
@hw.command('dfu', 'Restart application to bootloader mode(Not yet implement dfu).')
@@ -1195,8 +1204,10 @@ class HWDFU(DeviceRequiredUnit):
print("Application restarting...")
self.cmd.enter_dfu_mode()
# In theory, after the above command is executed, the dfu mode will enter, and then the USB will restart,
# To judge whether to enter the USB successfully, we only need to judge whether the USB becomes the VID and PID of the DFU device.
# At the same time, we remember to confirm the information of the device, it is the same device when it is consistent.
# To judge whether to enter the USB successfully, we only need to judge whether the USB becomes the VID and PID
# of the DFU device.
# At the same time, we remember to confirm the information of the device,
# it is the same device when it is consistent.
print(" - Enter success @.@~")
# let time for comm thread to send dfu cmd and close port
time.sleep(0.1)
@@ -1295,7 +1306,7 @@ class HWBatteryInfo(DeviceRequiredUnit):
def on_exec(self, args: argparse.Namespace):
resp = self.cmd.battery_information()
voltage = int.from_bytes(resp.data[:2], 'big')
voltage = int.from_bytes(resp.data[:2], 'big')
percentage = resp.data[2]
print(" - Battery information:")
print(f" voltage -> {voltage}mV")
@@ -1322,9 +1333,11 @@ class HWButtonSettingsGet(DeviceRequiredUnit):
resp_long = self.cmd.get_long_button_press_fun(button)
button_fn = chameleon_cmd.ButtonPressFunction.from_int(resp.data[0])
button_long_fn = chameleon_cmd.ButtonPressFunction.from_int(resp_long.data[0])
print(f" - {colorama.Fore.GREEN}{button} {colorama.Fore.YELLOW}short{colorama.Style.RESET_ALL}: {button_fn}")
print(f" - {colorama.Fore.GREEN}{button} {colorama.Fore.YELLOW}short{colorama.Style.RESET_ALL}:"
" {button_fn}")
print(f" usage: {button_fn.usage()}")
print(f" - {colorama.Fore.GREEN}{button} {colorama.Fore.YELLOW}long {colorama.Style.RESET_ALL}: {button_long_fn}")
print(f" - {colorama.Fore.GREEN}{button} {colorama.Fore.YELLOW}long {colorama.Style.RESET_ALL}:"
" {button_long_fn}")
print(f" usage: {button_long_fn.usage()}")
print("")
print(" - Successfully get button function from settings")
@@ -1351,8 +1364,7 @@ class HWButtonSettingsSet(DeviceRequiredUnit):
def on_exec(self, args: argparse.Namespace):
button = chameleon_cmd.ButtonType.from_str(args.b)
function = chameleon_cmd.ButtonPressFunction.from_int(args.f)
long = args.long == True
if long:
if args.long:
self.cmd.set_long_button_press_fun(button, function)
else:
self.cmd.set_button_press_fun(button, function)
+53 -49
View File
@@ -3,7 +3,7 @@ import struct
import chameleon_com
import chameleon_status
from chameleon_utils import UnexpectedResponseError, expect_response
from chameleon_utils import expect_response
DATA_CMD_GET_APP_VERSION = 1000
DATA_CMD_CHANGE_MODE = 1001
@@ -263,7 +263,8 @@ class ButtonPressFunction(enum.IntEnum):
elif self == ButtonPressFunction.SettingsButtonCycleSlotDec:
return "Card slot number sequence decreases after pressing"
elif self == ButtonPressFunction.SettingsButtonCloneIcUid:
return "Read the UID card number immediately after pressing, continue searching, and simulate immediately after reading the card"
return "Read the UID card number immediately after pressing, continue searching," + \
"and simulate immediately after reading the card"
return "Unknown"
@@ -322,28 +323,28 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.HF_TAG_OK)
def scan_tag_14a(self):
"""
( ) 14a tags in the scanning field
14a tags in the scanning field
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_SCAN_14A_TAG, 0x00)
def detect_mf1_support(self):
"""
( ) Detect whether it is mi (ar) classic label
Detect whether it is mifare classic label
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_MF1_SUPPORT_DETECT, 0x00)
def detect_mf1_nt_level(self):
"""
( ) detect mi (ar) Class of classic nt vulnerabilities
detect mifare Class of classic nt vulnerabilities
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_MF1_NT_LEVEL_DETECT, 0x00)
def detect_darkside_support(self):
"""
( ) Check if the card is vulnerable to mifare cla (si) darkside attack
Check if the card is vulnerable to mifare classic darkside attack
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_MF1_DARKSIDE_DETECT, 0x00, None, timeout=20)
@@ -351,7 +352,7 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.HF_TAG_OK)
def detect_nt_distance(self, block_known, type_known, key_known):
"""
( ) Detect the random number distance of the card
Detect the random number distance of the card
:return:
"""
data = bytearray()
@@ -363,7 +364,7 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.HF_TAG_OK)
def acquire_nested(self, block_known, type_known, key_known, block_target, type_target):
"""
( ) Collect the key NT parameters needed for Nested decryption
Collect the key NT parameters needed for Nested decryption
:return:
"""
data = bytearray()
@@ -377,7 +378,7 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.HF_TAG_OK)
def acquire_darkside(self, block_target, type_target, first_recover: int or bool, sync_max):
"""
( ) Collect the key parameters needed for Darkside decryption
Collect the key parameters needed for Darkside decryption
:param block_target:
:param type_target:
:param first_recover:
@@ -399,7 +400,7 @@ class ChameleonCMD:
])
def auth_mf1_key(self, block, type_value, key):
"""
( ) Verify the mf1 key, only verify the specified type of key for a single sector
Verify the mf1 key, only verify the specified type of key for a single sector
:param block:
:param type_value:
:param key:
@@ -414,7 +415,7 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.HF_TAG_OK)
def read_mf1_block(self, block, type_value, key):
"""
( ) read mf1 monoblock
read mf1 monoblock
:param block:
:param type_value:
:param key:
@@ -429,7 +430,7 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.HF_TAG_OK)
def write_mf1_block(self, block, type_value, key, block_data):
"""
( ) Write mf1 single block
Write mf1 single block
:param block:
:param type_value:
:param key:
@@ -446,7 +447,7 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.LF_TAG_OK)
def read_em_410x(self):
"""
( ) Read the card number of EM410X
Read the card number of EM410X
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_SCAN_EM410X_TAG, 0x00)
@@ -454,8 +455,8 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.LF_TAG_OK)
def write_em_410x_to_t55xx(self, id_bytes: bytearray):
"""
( ) Write EM410X card number into T55XX
:param id_by (es) ID card number
Write EM410X card number into T55XX
:param id_bytes: ID card number
:return:
"""
new_key = [0x20, 0x20, 0x66, 0x66]
@@ -498,10 +499,11 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
def set_slot_tag_type(self, slot_index: SlotNumber, tag_type: TagSpecificType):
"""
( ) Set the label type of the simulated card of the current card slot
( ) Note: This operation will not change the data in the flash, and the change of the data in the flash will only be updated at the next save
:param slot_in (ex) Card slot number
:param tag_t (pe) label type
Set the label type of the simulated card of the current card slot
Note: This operation will not change the data in the flash,
and the change of the data in the flash will only be updated at the next save
:param slot_index: Card slot number
:param tag_type: label type
:return:
"""
# SlotNumber() will raise error for us if slot_index not in slot range
@@ -526,10 +528,10 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
def set_slot_data_default(self, slot_index: SlotNumber, tag_type: TagSpecificType):
"""
( ) Set the data of the simulated card in the specified card slot as the default data
( ) Note: This API will set the data in the flash together
:param slot_in (ex) Card slot number
:param tag_t (pe) The default label type to set
Set the data of the simulated card in the specified card slot as the default data
Note: This API will set the data in the flash together
:param slot_index: Card slot number
:param tag_type: The default label type to set
:return:
"""
# SlotNumber() will raise error for us if slot_index not in slot range
@@ -541,9 +543,9 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
def set_slot_enable(self, slot_index: SlotNumber, enable: bool):
"""
( ) Set whether the specified card slot is enabled
:param slot_in (ex) Card slot number
:param ena (le) Whether to enable
Set whether the specified card slot is enabled
:param slot_index: Card slot number
:param enable: Whether to enable
:return:
"""
# SlotNumber() will raise error for us if slot_index not in slot range
@@ -555,8 +557,8 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
def set_em410x_sim_id(self, id_bytes: bytearray):
"""
( ) Set the card number simulated by EM410x
:param id_by (es) byte of the card number
Set the card number simulated by EM410x
:param id_bytes: byte of the card number
:return:
"""
if len(id_bytes) != 5:
@@ -572,8 +574,8 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
def set_mf1_detection_enable(self, enable: bool):
"""
( ) Set whether to enable the detection of the current card slot
:param ena (le) Whether to enable
Set whether to enable the detection of the current card slot
:param enable: Whether to enable
:return:
"""
data = bytearray()
@@ -582,7 +584,7 @@ class ChameleonCMD:
def get_mf1_detection_count(self):
"""
( ) Get the statistics of the current detection records
Get the statistics of the current detection records
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_GET_MF1_DETECTION_COUNT, 0x00)
@@ -590,8 +592,8 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
def get_mf1_detection_log(self, index: int):
"""
( ) Get detection logs from the specified index position
:param in (ex) start index
Get detection logs from the specified index position
:param index: start index
:return:
"""
data = bytearray()
@@ -601,9 +603,10 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
def set_mf1_block_data(self, block_start: int, block_data: bytearray):
"""
( ) Set the block data of the analog card of MF1
:param block_st (rt) Start setting the location of block data, including this location
:param block_d (ta) The byte buffer of the block data to be set can contain multiple block data, automatically from block_s (ar) increment
Set the block data of the analog card of MF1
:param block_start: Start setting the location of block data, including this location
:param block_data: The byte buffer of the block data to be set
can contain multiple block data, automatically from block_start increment
:return:
"""
data = bytearray()
@@ -621,10 +624,10 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
def set_mf1_anti_collision_res(self, sak: bytearray, atqa: bytearray, uid: bytearray):
"""
( ) Set the anti-collision resource information of the MF1 analog card
:param (ak) sak bytes
:param a (qa) atqa array
:param (id) card number array
Set the anti-collision resource information of the MF1 analog card
:param sak: sak bytes
:param atqa: atqa array
:param uid: card number array
:return:
"""
data = bytearray()
@@ -636,10 +639,10 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
def set_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType, name: bytes):
"""
( ) Set the anti-collision resource information of the MF1 analog card
:param s (ot) Card slot number
:param sense_t (pe) field type
:param n (me) Card slot nickname
Set the anti-collision resource information of the MF1 analog card
:param slot: Card slot number
:param sense_type: field type
:param name: Card slot nickname
:return:
"""
# SlotNumber() will raise error for us if slot not in slot range
@@ -651,9 +654,9 @@ class ChameleonCMD:
@expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS)
def get_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType):
"""
( ) Set the anti-collision resource information of the MF1 analog card
:param s (ot) Card slot number
:param sense_t (pe) field type
Set the anti-collision resource information of the MF1 analog card
:param slot: Card slot number
:param sense_type: field type
:return:
"""
# SlotNumber() will raise error for us if slot not in slot range
@@ -699,14 +702,14 @@ class ChameleonCMD:
def update_slot_data_config(self):
"""
( ) Update the configuration and data of the card slot to flash.
Update the configuration and data of the card slot to flash.
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_SLOT_DATA_CONFIG_SAVE, 0x00)
def enter_dfu_mode(self):
"""
( ) Reboot into DFU mode (bootloader)
Reboot into DFU mode (bootloader)
:return:
"""
return self.device.send_cmd_auto(DATA_CMD_ENTER_BOOTLOADER, 0x00, close=True)
@@ -792,6 +795,7 @@ class ChameleonCMD:
bytearray([button, function])
)
if __name__ == '__main__':
# connect to chameleon
dev = chameleon_com.ChameleonCom()
+3 -3
View File
@@ -78,7 +78,7 @@ class ChameleonCom:
raise OpenFailException(error)
try:
self.serial_instance.dtr = 1 # must make dtr enable
except:
except Exception:
# not all serial support dtr, e.g. virtual serial over BLE
pass
self.serial_instance.timeout = 0 # noblock
@@ -122,7 +122,7 @@ class ChameleonCom:
self.event_closing.set()
try:
self.serial_instance.close()
except:
except Exception:
pass
finally:
self.serial_instance = None
@@ -163,7 +163,7 @@ class ChameleonCom:
if data_buffer[data_position] != self.lrc_calc(data_buffer[0:1]):
data_position = 0
data_buffer.clear()
print(f"Data frame sof lrc error.")
print("Data frame sof lrc error.")
continue
elif data_position == 8: # frame head lrc
if data_buffer[data_position] != self.lrc_calc(data_buffer[0:8]):
+33 -21
View File
@@ -14,32 +14,44 @@ class MetaDevice(type):
class Device(metaclass=MetaDevice):
HF_TAG_OK = 0x00 # IC card operation is successful
HF_TAG_NO = 0x01 # IC card not found
HF_ERRSTAT = 0x02 # Abnormal IC card communication
HF_ERRCRC = 0x03 # IC card communication verification abnormal
HF_TAG_OK = 0x00 # IC card operation is successful
HF_TAG_NO = 0x01 # IC card not found
HF_ERRSTAT = 0x02 # Abnormal IC card communication
HF_ERRCRC = 0x03 # IC card communication verification abnormal
HF_COLLISION = 0x04 # IC card conflict
HF_ERRBCC = 0x05 # IC card BCC error
MF_ERRAUTH = 0x06 # MF card verification failed
HF_ERRBCC = 0x05 # IC card BCC error
MF_ERRAUTH = 0x06 # MF card verification failed
HF_ERRPARITY = 0x07 # IC card parity error
DARKSIDE_CANT_FIXED_NT = 0x20 # Darkside, the random number cannot be fixed, this situation may appear on the UID card
DARKSIDE_LUCK_AUTH_OK = 0x21 # Darkside, the direct verification is successful, maybe the key is empty
DARKSIDE_NACK_NO_SEND = 0x22 # Darkside, the card doesn't respond to nack, probably a card that fixes the nack logic bug
DARKSIDE_TAG_CHANGED = 0x23 # Darkside, there is a card switching during the running of darkside, maybe there is a signal problem, or the two cards really switched quickly
NESTED_TAG_IS_STATIC = 0x24 # Nested, it is detected that the random number of the card response is fixed
NESTED_TAG_IS_HARD = 0x25 # Nested, detected nonce for card response is unpredictable
# Darkside, the random number cannot be fixed, this situation may appear on the UID card
DARKSIDE_CANT_FIXED_NT = 0x20
# Darkside, the direct verification is successful, maybe the key is empty
DARKSIDE_LUCK_AUTH_OK = 0x21
# Darkside, the card doesn't respond to nack, probably a card that fixes the nack logic bug
DARKSIDE_NACK_NO_SEND = 0x22
# Darkside, there is a card switching during the running of darkside,
# maybe there is a signal problem, or the two cards really switched quickly
DARKSIDE_TAG_CHANGED = 0x23
# Nested, it is detected that the random number of the card response is fixed
NESTED_TAG_IS_STATIC = 0x24
# Nested, detected nonce for card response is unpredictable
NESTED_TAG_IS_HARD = 0x25
LF_TAG_OK = 0x40 # Some operations with low frequency cards succeeded!
EM410X_TAG_NO_FOUND = 0x41 # Unable to search for a valid EM410X label
# Some operations with low frequency cards succeeded!
LF_TAG_OK = 0x40
# Unable to search for a valid EM410X label
EM410X_TAG_NO_FOUND = 0x41
STATUS_PAR_ERR = 0x60 # The parameters passed by the BLE instruction are wrong, or the parameters passed by calling some functions are wrong
STATUS_DEVICE_MODE_ERROR = 0x66 # The mode of the current device is wrong, and the corresponding API cannot be called
STATUS_INVALID_CMD = 0x67 # invalid command
STATUS_DEVICE_SUCCESS = 0x68 # Device-related operations performed successfully
STATUS_NOT_IMPLEMENTED = 0x69 # Some unimplemented operations were called, an error missed by the developer
STATUS_FLASH_WRITE_FAIL = 0x70 # flash write failed
STATUS_FLASH_READ_FAIL = 0x71 # flash read failed
# The parameters passed by the BLE instruction are wrong, or the parameters passed
# by calling some functions are wrong
STATUS_PAR_ERR = 0x60
# The mode of the current device is wrong, and the corresponding API cannot be called
STATUS_DEVICE_MODE_ERROR = 0x66
STATUS_INVALID_CMD = 0x67
STATUS_DEVICE_SUCCESS = 0x68
STATUS_NOT_IMPLEMENTED = 0x69
STATUS_FLASH_WRITE_FAIL = 0x70
STATUS_FLASH_READ_FAIL = 0x71
message = {