From d22ff443424f497181d3819b50aea97233b4bd88 Mon Sep 17 00:00:00 2001 From: Foxushka <135865149+Foxushka@users.noreply.github.com> Date: Mon, 28 Aug 2023 09:12:48 +0300 Subject: [PATCH 1/9] DATA_CMD_GET_DEVICE_CAPABILITIES implementation --- firmware/application/src/app_cmd.c | 36 +++ firmware/application/src/app_cmd.h | 1 + firmware/application/src/app_main.c | 1 + firmware/application/src/data_cmd.h | 6 +- software/script/chameleon_cli_unit.py | 314 ++++++++++---------------- software/script/chameleon_cmd.py | 29 ++- software/script/chameleon_com.py | 25 +- 7 files changed, 204 insertions(+), 208 deletions(-) diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index be95a688..2c0425cf 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -34,6 +34,14 @@ data_frame_tx_t *cmd_processor_get_git_version(uint16_t cmd, uint16_t status, ui return data_frame_make(cmd, status, strlen(GIT_VERSION), (uint8_t *)GIT_VERSION); } +data_frame_tx_t *cmd_processor_get_device(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { +#if defined(PROJECT_CHAMELEON_ULTRA) + return data_frame_make(cmd, status, 1, (uint8_t *)1); +#else + return data_frame_make(cmd, status, 1, (uint8_t *)0); +#endif +} + data_frame_tx_t *cmd_processor_change_device_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { if (length == 1) { @@ -860,6 +868,9 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_GET_BLE_CONNECT_KEY_CONFIG, NULL, cmd_processor_get_ble_connect_key, NULL }, { DATA_CMD_SET_BLE_CONNECT_KEY_CONFIG, NULL, cmd_processor_set_ble_connect_key, NULL }, { DATA_CMD_DELETE_ALL_BLE_BONDS, NULL, cmd_processor_del_ble_all_bonds, NULL }, + { DATA_CMD_GET_DEVICE, NULL, cmd_processor_get_device, NULL }, + // { DATA_CMD_GET_SETTINGS, NULL, NULL, NULL }, + { DATA_CMD_GET_DEVICE_CAPABILITIES, NULL, NULL, NULL }, #if defined(PROJECT_CHAMELEON_ULTRA) @@ -919,6 +930,31 @@ static cmd_data_map_t m_data_cmd_map[] = { }; +data_frame_tx_t *cmd_processor_get_capabilities(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + size_t count = sizeof(m_data_cmd_map) / sizeof(m_data_cmd_map[0]); + uint16_t commands[count]; + memset(commands, 0, count * sizeof(uint16_t)); + + for (size_t i = 0; i < count; i++) { + // beware: wrong endianness + commands[i] = m_data_cmd_map[i].cmd; + } + + return data_frame_make(cmd, status, count * sizeof(uint16_t), (uint8_t *)commands); +} + + +void cmd_map_init() { + size_t count = sizeof(m_data_cmd_map) / sizeof(m_data_cmd_map[0]); + + for (size_t i = 0; i < count; i++) { + if (m_data_cmd_map[i].cmd == DATA_CMD_GET_DEVICE_CAPABILITIES) { + m_data_cmd_map[i].cmd_processor = cmd_processor_get_capabilities; + return; + } + } +} + /** * @brief Auto select source to response * diff --git a/firmware/application/src/app_cmd.h b/firmware/application/src/app_cmd.h index 538c069d..02b963e7 100644 --- a/firmware/application/src/app_cmd.h +++ b/firmware/application/src/app_cmd.h @@ -15,5 +15,6 @@ typedef struct { } cmd_data_map_t; void on_data_frame_received(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data); +void cmd_map_init(); #endif diff --git a/firmware/application/src/app_main.c b/firmware/application/src/app_main.c index d3981491..f303d7d8 100644 --- a/firmware/application/src/app_main.c +++ b/firmware/application/src/app_main.c @@ -767,6 +767,7 @@ static void ble_passkey_init(void) { */ int main(void) { hw_connect_init(); // Remember to initialize the pins first + cmd_map_init(); // Set function in CMD map for DATA_CMD_GET_DEVICE_CAPABILITIES init_leds(); // LED initialization log_init(); // Log initialization diff --git a/firmware/application/src/data_cmd.h b/firmware/application/src/data_cmd.h index 4be3c5e6..c44723d5 100644 --- a/firmware/application/src/data_cmd.h +++ b/firmware/application/src/data_cmd.h @@ -38,6 +38,10 @@ #define DATA_CMD_SET_BLE_CONNECT_KEY_CONFIG (1030) #define DATA_CMD_GET_BLE_CONNECT_KEY_CONFIG (1031) #define DATA_CMD_DELETE_ALL_BLE_BONDS (1032) +#define DATA_CMD_GET_DEVICE (1033) +#define DATA_CMD_GET_SETTINGS (1034) +#define DATA_CMD_GET_DEVICE_CAPABILITIES (1035) + // // ****************************************************************** @@ -80,7 +84,7 @@ // #define DATA_CMD_LOAD_MF1_EMU_BLOCK_DATA (4000) #define DATA_CMD_SET_MF1_ANTI_COLLISION_RES (4001) -#define DATA_CMD_SET_MF1_ANTICOLLISION_INFO (4002) +#define DATA_CMD_SET_MF1_ANTI_COLLISION_INFO (4002) #define DATA_CMD_SET_MF1_ATS_RESOURCE (4003) #define DATA_CMD_SET_MF1_DETECTION_ENABLE (4004) #define DATA_CMD_GET_MF1_DETECTION_COUNT (4005) diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index 28059baf..cbc67924 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -160,29 +160,20 @@ def on_exec(self, args: argparse.Namespace): hw_ble = hw.subgroup('ble', 'Bluetooth low energy') hw_ble_bonds = hw_ble.subgroup('bonds', 'All devices bound by chameleons.') hw_settings = hw.subgroup('settings', 'Chameleon settings management') -hw_settings_animation = hw_settings.subgroup( - 'animation', 'Manage wake-up and sleep animation modes') -hw_settings_button_press = hw_settings.subgroup( - 'btnpress', 'Manage button press function') -hw_settings_ble_key = hw_settings.subgroup( - 'blekey', 'Manage ble connect key') +hw_settings_animation = hw_settings.subgroup('animation', 'Manage wake-up and sleep animation modes') +hw_settings_button_press = hw_settings.subgroup('btnpress', 'Manage button press function') +hw_settings_ble_key = hw_settings.subgroup('blekey', 'Manage ble connect key') hf = CLITree('hf', 'high frequency tag/reader') hf_14a = hf.subgroup('14a', 'ISO14443-a tag read/write/info...') hf_mf = hf.subgroup('mf', 'Mifare Classic mini/1/2/4, attack/read/write') -hf_mf_detection = hf.subgroup( - 'detection', 'Mifare Classic detection log') +hf_mf_detection = hf.subgroup('detection', 'Mifare Classic detection log') lf = CLITree('lf', 'low frequency tag/reader') lf_em = lf.subgroup('em', 'EM410x read/write/emulator') -lf_em_sim = lf_em.subgroup( - 'sim', 'Manage EM410x emulation data for selected slot') +lf_em_sim = lf_em.subgroup('sim', 'Manage EM410x emulation data for selected slot') -root_commands: dict[str, CLITree] = { - 'hw': hw, - 'hf': hf, - 'lf': lf, -} +root_commands: dict[str, CLITree] = {'hw': hw, 'hf': hf, 'lf': lf} @hw.command('connect', 'Connect to chameleon by serial port') @@ -201,23 +192,20 @@ def on_exec(self, args: argparse.Namespace): 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/") + path.append("/mnt/c/Windows/System32/WindowsPowerShell/v1.0/") + powershell_path = None 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 + powershell_path = 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) + if powershell_path: + process = subprocess.Popen([powershell_path, "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: @@ -229,8 +217,7 @@ def on_exec(self, args: argparse.Namespace): args.port = port.device break if args.port is None: # If no chameleon was found, exit - print( - "Chameleon not found, please connect the device or try connecting manually with the -p flag.") + print("Chameleon not found, please connect the device or try connecting manually with the -p flag.") return self.device_com.open(args.port) print(" { Chameleon connected } ") @@ -262,8 +249,7 @@ def args_parser(self) -> ArgumentParserNoExit or None: pass def on_exec(self, args: argparse.Namespace): - print( - f"- Device Mode ( Tag {'Reader' if self.cmd.is_reader_device_mode() else 'Emulator'} )") + print(f"- Device Mode ( Tag {'Reader' if self.cmd.is_reader_device_mode() else 'Emulator'} )") @hw_chipid.command('get', 'Get device chipset ID') @@ -320,7 +306,6 @@ def on_exec(self, args: argparse.Namespace): @hf_14a.command('info', 'Scan 14a tag, and print detail information') class HF14AInfo(ReaderRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: pass @@ -351,7 +336,6 @@ def on_exec(self, args: argparse.Namespace): @hf_mf.command('nested', 'Mifare Classic nested recover key') class HFMFNested(ReaderRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: type_choices = ['A', 'B', 'a', 'b'] parser = ArgumentParserNoExit() @@ -361,8 +345,7 @@ def args_parser(self) -> ArgumentParserNoExit or None: help="The block where the key of the card is known") parser.add_argument('--type-known', type=str, required=True, choices=type_choices, help="The key type of the tag") - parser.add_argument('--key-known', type=str, - required=True, metavar="hex", help="tag sector key") + parser.add_argument('--key-known', type=str, required=True, metavar="hex", help="tag sector key") parser.add_argument('--block-target', type=int, metavar="decimal", help="The key of the target block to recover") parser.add_argument('--type-target', type=str, choices=type_choices, @@ -381,13 +364,10 @@ def recover_a_key(self, block_known, type_known, key_known, block_target, type_t :return: """ # acquire - dist_resp = self.cmd.detect_nt_distance( - block_known, type_known, key_known) - nt_resp = self.cmd.acquire_nested( - block_known, type_known, key_known, block_target, type_target) + dist_resp = self.cmd.detect_nt_distance(block_known, type_known, key_known) + nt_resp = self.cmd.acquire_nested(block_known, type_known, key_known, block_target, type_target) # parse - dist_obj = chameleon_cstruct.parse_nt_distance_detect_result( - dist_resp.data) + dist_obj = chameleon_cstruct.parse_nt_distance_detect_result(dist_resp.data) nt_obj = chameleon_cstruct.parse_nested_nt_acquire_group(nt_resp.data) # create cmd cmd_param = f"{dist_obj['uid']} {dist_obj['dist']}" @@ -419,8 +399,7 @@ def recover_a_key(self, block_known, type_known, key_known, block_target, type_t print(f" - [{len(key_list)} candidate keys found ]") for key in key_list: key_bytes = bytearray.fromhex(key) - ret = self.cmd.auth_mf1_key( - block_target, type_target, key_bytes) + ret = self.cmd.auth_mf1_key(block_target, type_target, key_bytes) if ret.status == chameleon_status.Device.HF_TAG_OK: return key else: @@ -444,10 +423,8 @@ def on_exec(self, args: argparse.Namespace): type_target = args.type_target if block_target is not None and type_target is not None: type_target = 0x60 if type_target == 'A' or type_target == 'a' else 0x61 - print( - f" - {colorama.Fore.CYAN}Nested recover one key running...{colorama.Style.RESET_ALL}") - key = self.recover_a_key( - block_known, type_known, key_known, block_target, type_target) + print(f" - {colorama.Fore.CYAN}Nested recover one key running...{colorama.Style.RESET_ALL}") + key = self.recover_a_key(block_known, type_known, key_known, block_target, type_target) if key is None: print("No keys found, you can retry recover.") else: @@ -456,15 +433,13 @@ def on_exec(self, args: argparse.Namespace): print("Please input block_target and type_target") self.args_parser().print_help() else: - raise NotImplementedError( - "hf mf nested recover all key not implement.") + raise NotImplementedError("hf mf nested recover all key not implement.") return @hf_mf.command('darkside', 'Mifare Classic darkside recover key') class HFMFDarkside(ReaderRequiredUnit): - def __init__(self): super().__init__() self.darkside_list = [] @@ -482,11 +457,9 @@ def recover_key(self, block_target, type_target): first_recover = True retry_count = 0 while retry_count < 0xFF: - darkside_resp = self.cmd.acquire_darkside( - block_target, type_target, first_recover, 15) - first_recover = False # not first run. - darkside_obj = chameleon_cstruct.parse_darkside_acquire_result( - darkside_resp.data) + darkside_resp = self.cmd.acquire_darkside(block_target, type_target, first_recover, 15) + first_recover = False # not first run. + darkside_obj = chameleon_cstruct.parse_darkside_acquire_result(darkside_resp.data) self.darkside_list.append(darkside_obj) recover_params = f"{darkside_obj['uid']}" for darkside_item in self.darkside_list: @@ -517,8 +490,7 @@ def recover_key(self, block_target, type_target): # auth key for key in key_list: key_bytes = bytearray.fromhex(key) - auth_ret = self.cmd.auth_mf1_key( - block_target, type_target, key_bytes) + auth_ret = self.cmd.auth_mf1_key(block_target, type_target, key_bytes) if auth_ret.status == chameleon_status.Device.HF_TAG_OK: return key return None @@ -533,7 +505,6 @@ def on_exec(self, args: argparse.Namespace): class BaseMF1AuthOpera(ReaderRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: type_choices = ['A', 'B', 'a', 'b'] parser = ArgumentParserNoExit() @@ -541,8 +512,7 @@ def args_parser(self) -> ArgumentParserNoExit or None: help="The block where the key of the card is known") parser.add_argument('-t', '--type', type=str, required=True, choices=type_choices, help="The key type of the tag") - parser.add_argument('-k', '--key', type=str, - required=True, metavar="hex", help="tag sector key") + parser.add_argument('-k', '--key', type=str, required=True, metavar="hex", help="tag sector key") return parser def get_param(self, args): @@ -584,11 +554,9 @@ def on_exec(self, args: argparse.Namespace): if not re.match(r"^[a-fA-F0-9]{32}$", args.data): raise ArgsParserError("Data must include 32 HEX symbols") param.data = bytearray.fromhex(args.data) - resp = self.cmd.write_mf1_block( - param.block, param.type, param.key, param.data) + resp = self.cmd.write_mf1_block(param.block, param.type, param.key, param.data) if resp.status == chameleon_status.Device.HF_TAG_OK: - print( - f" - {colorama.Fore.GREEN}Write done.{colorama.Style.RESET_ALL}") + print(f" - {colorama.Fore.GREEN}Write done.{colorama.Style.RESET_ALL}") else: print(f" - {colorama.Fore.RED}Write fail.{colorama.Style.RESET_ALL}") @@ -597,15 +565,14 @@ def on_exec(self, args: argparse.Namespace): class HFMFDetectionEnable(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() - parser.add_argument('-e', '--enable', type=int, required=True, - choices=[1, 0], help="1 = enable, 0 = disable") + parser.add_argument('-e', '--enable', type=int, required=True, choices=[1, 0], help="1 = enable, 0 = disable") return parser # hf mf detection enable -e 1 def on_exec(self, args: argparse.Namespace): enable = True if args.enable == 1 else False self.cmd.set_mf1_detection_enable(enable) - print(f" - Set mf1 detection { 'enable' if enable else 'disable'}.") + print(f" - Set mf1 detection {'enable' if enable else 'disable'}.") @hf_mf_detection.command('count', 'Detection log count') @@ -653,8 +620,7 @@ def decrypt_by_list(self, rs: list): # get output output_str = process.get_output_sync() # print(output_str) - sea_obj = re.search( - r"([a-fA-F0-9]{12})", output_str, flags=re.MULTILINE) + sea_obj = re.search(r"([a-fA-F0-9]{12})", output_str, flags=re.MULTILINE) if sea_obj is not None: keys.append(sea_obj[1]) @@ -664,22 +630,19 @@ def decrypt_by_list(self, rs: list): def on_exec(self, args: argparse.Namespace): buffer = bytearray() index = 0 - count = int.from_bytes( - self.cmd.get_mf1_detection_count().data, "little", signed=False) + count = int.from_bytes(self.cmd.get_mf1_detection_count().data, "little", signed=False) if count == 0: print(" - No detection log to download") return print(f" - MF1 detection log count = {count}, start download", end="") while index < count: tmp = self.cmd.get_mf1_detection_log(index).data - recv_count = int( - len(tmp) / HFMFDetectionDecrypt.detection_log_size) + recv_count = int(len(tmp) / HFMFDetectionDecrypt.detection_log_size) index += recv_count buffer.extend(tmp) print(".", end="") print() - print( - f" - Download done ({len(buffer)}bytes), start parse and decrypt") + print(f" - Download done ({len(buffer)}bytes), start parse and decrypt") result_maps = chameleon_cstruct.parse_mf1_detection_result(buffer) for uid in result_maps.keys(): @@ -691,22 +654,18 @@ def on_exec(self, args: argparse.Namespace): # print(f" - A record: { result_maps[block]['A'] }") records = result_maps_for_uid[block]['A'] if len(records) > 1: - result_maps[uid][block]['A'] = self.decrypt_by_list( - records) + result_maps[uid][block]['A'] = self.decrypt_by_list(records) if 'B' in result_maps_for_uid[block]: # print(f" - B record: { result_maps[block]['B'] }") records = result_maps_for_uid[block]['B'] if len(records) > 1: - result_maps[uid][block]['B'] = self.decrypt_by_list( - records) + result_maps[uid][block]['B'] = self.decrypt_by_list(records) print(" > Result ---------------------------") for block in result_maps_for_uid.keys(): if 'A' in result_maps_for_uid[block]: - print( - f" > Block {block}, A key result: {result_maps_for_uid[block]['A']}") + print(f" > Block {block}, A key result: {result_maps_for_uid[block]['A']}") if 'B' in result_maps_for_uid[block]: - print( - f" > Block {block}, B key result: {result_maps_for_uid[block]['B']}") + print(f" > Block {block}, B key result: {result_maps_for_uid[block]['B']}") return @@ -714,10 +673,8 @@ def on_exec(self, args: argparse.Namespace): class HFMFELoad(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() - parser.add_argument('-f', '--file', type=str, - required=True, help="file path") - parser.add_argument('-t', '--type', type=str, required=False, - help="content type", choices=['bin', 'hex']) + parser.add_argument('-f', '--file', type=str, required=True, help="file path") + parser.add_argument('-t', '--type', type=str, required=False, help="content type", choices=['bin', 'hex']) return parser # hf mf eload -f test.bin -t bin @@ -730,8 +687,7 @@ def on_exec(self, args: argparse.Namespace): elif file.endswith('.eml'): content_type = 'hex' else: - raise Exception( - "Unknown file format, Specify content type with -t option") + raise Exception("Unknown file format, Specify content type with -t option") else: content_type = args.type buffer = bytearray() @@ -764,10 +720,8 @@ def on_exec(self, args: argparse.Namespace): class HFMFERead(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() - parser.add_argument('-f', '--file', type=str, - required=True, help="file path") - parser.add_argument('-t', '--type', type=str, required=False, - help="content type", choices=['bin', 'hex']) + parser.add_argument('-f', '--file', type=str, required=True, help="file path") + parser.add_argument('-t', '--type', type=str, required=False, help="content type", choices=['bin', 'hex']) return parser def on_exec(self, args: argparse.Namespace): @@ -778,8 +732,7 @@ def on_exec(self, args: argparse.Namespace): elif file.endswith('.eml'): content_type = 'hex' else: - raise Exception( - "Unknown file format, Specify content type with -t option") + raise Exception("Unknown file format, Specify content type with -t option") else: content_type = args.type @@ -795,8 +748,7 @@ def on_exec(self, args: argparse.Namespace): elif tag_type == chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_4096: block_count = 256 else: - raise Exception( - "Card in current slot is not Mifare Classic/Plus in SL1 mode") + raise Exception("Card in current slot is not Mifare Classic/Plus in SL1 mode") with open(file, 'wb') as fd: block = 0 @@ -831,29 +783,24 @@ def args_parser(self) -> ArgumentParserNoExit or None: parser.add_argument('--coll', type=int, required=False, help="Use anti-collision data from block 0 for 4 byte UID tags, 1 - enable, 0 - disable", default=-1, choices=[1, 0]) - parser.add_argument('--write', type=int, required=False, - help=f"Write mode: {help_str}", - default=-1, choices=chameleon_cmd.MifareClassicWriteMode.list()) + parser.add_argument('--write', type=int, required=False, help=f"Write mode: {help_str}", default=-1, + choices=chameleon_cmd.MifareClassicWriteMode.list()) return parser # hf mf settings def on_exec(self, args: argparse.Namespace): if args.gen1a != -1: self.cmd.set_mf1_gen1a_mode(args.gen1a) - print( - f' - Set gen1a mode to {"enabled" if args.gen1a else "disabled"} success') + print(f' - Set gen1a mode to {"enabled" if args.gen1a else "disabled"} success') if args.gen2 != -1: self.cmd.set_mf1_gen2_mode(args.gen2) - print( - f' - Set gen2 mode to {"enabled" if args.gen2 else "disabled"} success') + print(f' - Set gen2 mode to {"enabled" if args.gen2 else "disabled"} success') if args.coll != -1: self.cmd.set_mf1_block_anti_coll_mode(args.coll) - print( - f' - Set anti-collision mode to {"enabled" if args.coll else "disabled"} success') + print(f' - Set anti-collision mode to {"enabled" if args.coll else "disabled"} success') 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' - Set write mode to {chameleon_cmd.MifareClassicWriteMode(args.write)} success') print(' - Emulator settings updated') @@ -861,12 +808,9 @@ def on_exec(self, args: argparse.Namespace): class HFMFSim(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() - parser.add_argument('--sak', type=str, required=True, - help="Select AcKnowledge(hex)", metavar="hex") - parser.add_argument('--atqa', type=str, required=True, - help="Answer To Request(hex)", metavar="hex") - parser.add_argument('--uid', type=str, required=True, - help="Unique ID(hex)", metavar="hex") + parser.add_argument('--sak', type=str, required=True, help="Select AcKnowledge(hex)", metavar="hex") + parser.add_argument('--atqa', type=str, required=True, help="Answer To Request(hex)", metavar="hex") + parser.add_argument('--uid', type=str, required=True, help="Unique ID(hex)", metavar="hex") return parser # hf mf sim --sak 08 --atqa 0400 --uid DEADBEEF @@ -899,23 +843,19 @@ def on_exec(self, args: argparse.Namespace): @lf_em.command('read', 'Scan em410x tag and print id') class LFEMRead(ReaderRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None def on_exec(self, args: argparse.Namespace): resp = self.cmd.read_em_410x() id_hex = resp.data.hex() - print( - f" - EM410x ID(10H): {colorama.Fore.GREEN}{id_hex}{colorama.Style.RESET_ALL}") + print(f" - EM410x ID(10H): {colorama.Fore.GREEN}{id_hex}{colorama.Style.RESET_ALL}") class LFEMCardRequiredUnit(DeviceRequiredUnit): - @staticmethod def add_card_arg(parser: ArgumentParserNoExit): - parser.add_argument("--id", type=str, required=True, - help="EM410x tag id", metavar="hex") + parser.add_argument("--id", type=str, required=True, help="EM410x tag id", metavar="hex") return parser def before_exec(self, args: argparse.Namespace): @@ -934,7 +874,6 @@ def on_exec(self, args: argparse.Namespace): @lf_em.command('write', 'Write em410x id to t55xx') class LFEMWriteT55xx(LFEMCardRequiredUnit, ReaderRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() return self.add_card_arg(parser) @@ -953,7 +892,6 @@ def on_exec(self, args: argparse.Namespace): class SlotIndexRequireUnit(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: raise NotImplementedError() @@ -998,7 +936,7 @@ def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() parser.add_argument('-e', '--extend', type=int, required=False, help="Show slot nicknames and Mifare Classic emulator settings. 0 - skip, 1 - show (" - "default)", choices=[0, 1], default=1) + "default, 2 - show emulator settings for each slot)", choices=[0, 1, 2], default=1) return parser def get_slot_name(self, slot, sense): @@ -1012,39 +950,29 @@ def get_slot_name(self, slot, sense): # hw slot list def on_exec(self, args: argparse.Namespace): data = self.cmd.get_slot_info().data - selected = chameleon_cmd.SlotNumber.from_fw( - self.cmd.get_active_slot().data[0]) + selected = chameleon_cmd.SlotNumber.from_fw(self.cmd.get_active_slot().data[0]) enabled = self.cmd.get_enabled_slots().data for slot in chameleon_cmd.SlotNumber: - print( - f' - Slot {slot} data{" (active)" if slot == selected else ""}' - f'{" (disabled)" if not enabled[chameleon_cmd.SlotNumber.to_fw(slot)] else ""}:') - print( - f' HF: ' - f'{(self.get_slot_name(slot, chameleon_cmd.TagSenseType.TAG_SENSE_HF) + " - ") if args.extend else ""}' - f'{chameleon_cmd.TagSpecificType(data[chameleon_cmd.SlotNumber.to_fw(slot) * 2])}') - print( - f' LF: ' - f'{(self.get_slot_name(slot, chameleon_cmd.TagSenseType.TAG_SENSE_LF) + " - ") if args.extend else ""}' - f'{chameleon_cmd.TagSpecificType(data[chameleon_cmd.SlotNumber.to_fw(slot) * 2 + 1])}') - if args.extend: - config = self.cmd.get_mf1_emulator_settings().data - print(' - Mifare Classic emulator settings:') - print( - f' Detection (mfkey32) mode: {"enabled" if config[0] else "disabled"}') - print( - f' Gen1A magic mode: {"enabled" if config[1] else "disabled"}') - print( - f' Gen2 magic mode: {"enabled" if config[2] else "disabled"}') - print( - f' Use anti-collision data from block 0: {"enabled" if config[3] else "disabled"}') - print( - f' Write mode: {chameleon_cmd.MifareClassicWriteMode(config[4])}') + print(f' - Slot {slot} data{" (active)" if slot == selected else ""}' + f'{" (disabled)" if not enabled[chameleon_cmd.SlotNumber.to_fw(slot)] else ""}:') + print(f' HF: ' + f'{(self.get_slot_name(slot, chameleon_cmd.TagSenseType.TAG_SENSE_HF) + " - ") if args.extend else ""}' + f'{chameleon_cmd.TagSpecificType(data[chameleon_cmd.SlotNumber.to_fw(slot) * 2])}') + print(f' LF: ' + f'{(self.get_slot_name(slot, chameleon_cmd.TagSenseType.TAG_SENSE_LF) + " - ") if args.extend else ""}' + f'{chameleon_cmd.TagSpecificType(data[chameleon_cmd.SlotNumber.to_fw(slot) * 2 + 1])}') + if args.extend == 2 or args.extend == 1 and enabled[chameleon_cmd.SlotNumber.to_fw(slot)]: + config = self.cmd.get_mf1_emulator_settings().data + print(' - Mifare Classic emulator settings:') + print(f' Detection (mfkey32) mode: {"enabled" if config[0] else "disabled"}') + print(f' Gen1A magic mode: {"enabled" if config[1] else "disabled"}') + print(f' Gen2 magic mode: {"enabled" if config[2] else "disabled"}') + print(f' Use anti-collision data from block 0: {"enabled" if config[3] else "disabled"}') + print(f' Write mode: {chameleon_cmd.MifareClassicWriteMode(config[4])}') @hw_slot.command('change', 'Set emulation tag slot activated.') class HWSlotSet(SlotIndexRequireUnit): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() return self.add_slot_args(parser) @@ -1057,7 +985,6 @@ def on_exec(self, args: argparse.Namespace): class TagTypeRequiredUnit(DeviceRequiredUnit): - @staticmethod def add_type_args(parser: ArgumentParserNoExit): type_choices = chameleon_cmd.TagSpecificType.list() @@ -1080,7 +1007,6 @@ def on_exec(self, args: argparse.Namespace): @hw_slot.command('type', 'Set emulation tag type') class HWSlotTagType(TagTypeRequiredUnit, SlotIndexRequireUnit): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() self.add_type_args(parser) @@ -1112,7 +1038,6 @@ def on_exec(self, args: argparse.Namespace): @hw_slot.command('init', 'Set emulation tag data to default') class HWSlotDataDefault(TagTypeRequiredUnit, SlotIndexRequireUnit): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() self.add_type_args(parser) @@ -1133,8 +1058,7 @@ class HWSlotEnableSet(SlotIndexRequireUnit): def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() self.add_slot_args(parser) - parser.add_argument('-e', '--enable', type=int, required=True, - help="1 is Enable or 0 Disable", choices=[0, 1]) + parser.add_argument('-e', '--enable', type=int, required=True, help="1 is Enable or 0 Disable", choices=[0, 1]) return parser # hw slot enable -s 1 -e 0 @@ -1142,13 +1066,11 @@ def on_exec(self, args: argparse.Namespace): slot_num = args.slot enable = args.enable self.cmd.set_slot_enable(slot_num, enable) - print( - f' - Set slot {slot_num} {"enable" if enable else "disable"} success.') + print(f' - Set slot {slot_num} {"enable" if enable else "disable"} success.') @lf_em_sim.command('set', 'Set simulated em410x card id') class LFEMSimSet(LFEMCardRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() return self.add_card_arg(parser) @@ -1163,7 +1085,6 @@ def on_exec(self, args: argparse.Namespace): @lf_em_sim.command('get', 'Get simulated em410x card id') class LFEMSimGet(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -1180,8 +1101,7 @@ def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() self.add_slot_args(parser) self.add_sense_type_args(parser) - parser.add_argument('-n', '--name', type=str, - required=True, help="Your tag nick name for slot") + parser.add_argument('-n', '--name', type=str, required=True, help="Your tag nick name for slot") return parser # hw slot nick set -s 1 -st 1 -n Save the test name @@ -1189,10 +1109,10 @@ def on_exec(self, args: argparse.Namespace): slot_num = args.slot sense_type = args.sense_type name: str = args.name - uname = name.encode(encoding="utf8") - if len(uname) > 32: + encoded_name = name.encode(encoding="utf8") + if len(encoded_name) > 32: raise ValueError("Your tag nick name too long.") - self.cmd.set_slot_tag_nick_name(slot_num, sense_type, uname) + self.cmd.set_slot_tag_nick_name(slot_num, sense_type, encoded_name) print(f' - Set tag nick name for slot {slot_num} success.') @@ -1300,8 +1220,7 @@ def args_parser(self) -> ArgumentParserNoExit or None: def on_exec(self, args: argparse.Namespace): mode = args.mode self.cmd.set_settings_animation(mode) - print( - "Animation mode change success. Do not forget to store your settings in flash!") + print("Animation mode change success. Do not forget to store your settings in flash!") @hw_settings.command('store', 'Store current settings to flash') @@ -1334,13 +1253,12 @@ def on_exec(self, args: argparse.Namespace): @hw.command('factory_reset', 'Wipe all data and return to factory settings') class HWFactoryReset(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit: + def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() parser.description = "Permanently wipes Chameleon to factory settings. " \ "This will delete all your slot data and custom settings. " \ "There's no going back." - parser.add_argument("--i-know-what-im-doing", default=False, - action="store_true", help="Just to be sure :)") + parser.add_argument("--i-know-what-im-doing", default=False, action="store_true", help="Just to be sure :)") return parser def on_exec(self, args: argparse.Namespace): @@ -1361,7 +1279,7 @@ class HWBatteryInfo(DeviceRequiredUnit): # How much remaining battery is considered low? BATTERY_LOW_LEVEL = 30 - def args_parser(self) -> ArgumentParserNoExit: + def args_parser(self) -> ArgumentParserNoExit or None: return None def on_exec(self, args: argparse.Namespace): @@ -1372,30 +1290,24 @@ def on_exec(self, args: argparse.Namespace): print(f" voltage -> {voltage}mV") print(f" percentage -> {percentage}%") if percentage < HWBatteryInfo.BATTERY_LOW_LEVEL: - print( - f"{colorama.Fore.RED}[!] Low battery, please charge.{colorama.Style.RESET_ALL}") + print(f"{colorama.Fore.RED}[!] Low battery, please charge.{colorama.Style.RESET_ALL}") @hw_settings_button_press.command('get', 'Get button press function of Button A and Button B.') class HWButtonSettingsGet(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit: + def args_parser(self) -> ArgumentParserNoExit or None: return None def on_exec(self, args: argparse.Namespace): # all button in here. - button_list = [ - chameleon_cmd.ButtonType.ButtonA, - chameleon_cmd.ButtonType.ButtonB, - ] + button_list = [chameleon_cmd.ButtonType.ButtonA, chameleon_cmd.ButtonType.ButtonB, ] print("") for button in button_list: resp = self.cmd.get_button_press_fun(button) 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]) + 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}:" f" {button_fn}") print(f" usage: {button_fn.usage()}") @@ -1409,19 +1321,17 @@ def on_exec(self, args: argparse.Namespace): @hw_settings_button_press.command('set', 'Set button press function of Button A and Button B.') class HWButtonSettingsSet(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit: + def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() - parser.add_argument('-l', '--long', action='store_true', default=False, - help="set keybinding for long-press") - parser.add_argument('-b', type=str, required=True, - help="Change the function of the pressed button(?).", + parser.add_argument('-l', '--long', action='store_true', default=False, help="set keybinding for long-press") + parser.add_argument('-b', type=str, required=True, help="Change the function of the pressed button(?).", choices=chameleon_cmd.ButtonType.list_str()) function_usage = "" for fun in chameleon_cmd.ButtonPressFunction: function_usage += f"{int(fun)} = {fun.usage()}, " function_usage = function_usage.rstrip(' ').rstrip(',') - parser.add_argument('-f', type=int, required=True, - help=function_usage, choices=chameleon_cmd.ButtonPressFunction.list()) + parser.add_argument('-f', type=int, required=True, help=function_usage, + choices=chameleon_cmd.ButtonPressFunction.list()) return parser def on_exec(self, args: argparse.Namespace): @@ -1437,10 +1347,9 @@ def on_exec(self, args: argparse.Namespace): @hw_settings_ble_key.command('set', 'Set the ble connect key') class HWSettingsBLEKeySet(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit: + def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() - parser.add_argument('-k', '--key', required=True, - help="Ble connect key for your device") + parser.add_argument('-k', '--key', required=True, help="Ble connect key for your device") return parser def on_exec(self, args: argparse.Namespace): @@ -1457,7 +1366,7 @@ def on_exec(self, args: argparse.Namespace): @hw_settings_ble_key.command('get', 'Get the ble connect key') class HWSettingsBLEKeyGet(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit: + def args_parser(self) -> ArgumentParserNoExit or None: return None def on_exec(self, args: argparse.Namespace): @@ -1469,9 +1378,26 @@ def on_exec(self, args: argparse.Namespace): @hw_ble_bonds.command('clear', 'Clear all bindings') class HWBLEBondsClear(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit: + def args_parser(self) -> ArgumentParserNoExit or None: return None def on_exec(self, args: argparse.Namespace): self.cmd.delete_ble_all_bonds() print(" - Successfully clear all bonds") + + +@hw.command('raw', 'Send raw command') +class HWRaw(DeviceRequiredUnit): + + def args_parser(self) -> ArgumentParserNoExit or None: + parser = ArgumentParserNoExit() + parser.add_argument('-c', '--command', type=int, required=True, help="Command (Int) to send") + parser.add_argument('-d', '--data', type=str, help="Data (HEX) to send", default="") + return parser + + def on_exec(self, args: argparse.Namespace): + response = self.cmd.device.send_cmd_sync(args.command, data=bytes.fromhex(args.data), status=0x0) + print(" - Received:") + print(f" Command: {response.cmd}") + print(f" Status: {response.status}") + print(f" Data (HEX): {response.data.hex()}") diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index 9bb4b13d..fad9580e 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -50,6 +50,10 @@ DATA_CMD_DELETE_ALL_BLE_BONDS = 1032 +DATA_CMD_GET_DEVICE = 1033 +DATA_CMD_GET_SETTINGS = 1034 +DATA_CMD_GET_DEVICE_CAPABILITIES = 1035 + DATA_CMD_SCAN_14A_TAG = 2000 DATA_CMD_MF1_SUPPORT_DETECT = 2001 DATA_CMD_MF1_NT_LEVEL_DETECT = 2002 @@ -283,6 +287,8 @@ def __init__(self, chameleon: chameleon_com.ChameleonCom): :param chameleon: chameleon instance, @see chameleon_device.Chameleon """ self.device = chameleon + if not len(self.device.commands): + self.get_device_capabilities() def get_firmware_version(self) -> int: """ @@ -810,7 +816,7 @@ def set_ble_connect_key(self, key: str): data_bytes = key.encode(encoding='ascii') # check key length - if (len(data_bytes) != 6): + if len(data_bytes) != 6: raise ValueError("The ble connect key length must be 6") return self.device.send_cmd_sync( @@ -831,6 +837,27 @@ def delete_ble_all_bonds(self): """ return self.device.send_cmd_sync(DATA_CMD_DELETE_ALL_BLE_BONDS, 0x00, None) + def get_device_capabilities(self): + """ + Get (and set) commands that client understands + """ + + commands = [] + + try: + ret = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_CAPABILITIES, 0x00) + + for i in range(0, len(ret.data), 2): + if i + 1 < len(ret.data): + commands.append((ret.data[i + 1] << 8) | ret.data[i]) + + self.device.commands = commands + except: + print("Chameleon doesn't understand get capabilities command. Please update firmware") + + return commands + + if __name__ == '__main__': # connect to chameleon dev = chameleon_com.ChameleonCom() diff --git a/software/script/chameleon_com.py b/software/script/chameleon_com.py index f3a62dbf..c309fb1f 100644 --- a/software/script/chameleon_com.py +++ b/software/script/chameleon_com.py @@ -42,6 +42,7 @@ class ChameleonCom: """ data_frame_sof = 0x11 data_max_length = 512 + commands = [] def __init__(self): """ @@ -70,8 +71,7 @@ def open(self, port): error = None try: # open serial port - self.serial_instance = serial.Serial( - port=port, baudrate=115200) + self.serial_instance = serial.Serial(port=port, baudrate=115200) except Exception as e: error = e finally: @@ -99,8 +99,7 @@ def check_open(self): :return: """ if not self.isOpen(): - raise NotOpenException( - "Please call open() function to start device.") + raise NotOpenException("Please call open() function to start device.") @staticmethod def lrc_calc(array): @@ -198,8 +197,7 @@ def thread_data_receive(self): if callable(fn_call): # delete wait task from map del self.wait_response_map[data_cmd] - fn_call(data_cmd, data_status, - data_response) + fn_call(data_cmd, data_status, data_response) else: self.wait_response_map[data_cmd]['response'] = Response(data_cmd, data_status, data_response) @@ -230,8 +228,7 @@ def thread_data_transfer(self): task_close = task['close'] # register to wait map if 'callback' in task and callable(task['callback']): - self.wait_response_map[task_cmd] = { - 'callback': task['callback']} # The callback for this task + self.wait_response_map[task_cmd] = {'callback': task['callback']} # The callback for this task else: self.wait_response_map[task_cmd] = {'response': None} # set start time @@ -262,8 +259,7 @@ def thread_check_timeout(self): if time.time() > self.wait_response_map[task_cmd]['end_time']: if 'callback' in self.wait_response_map[task_cmd]: # not sync, call function to notify timeout. - self.wait_response_map[task_cmd]['callback']( - task_cmd, None, None) + self.wait_response_map[task_cmd]['callback'](task_cmd, None, None) else: # sync mode, set timeout flag self.wait_response_map[task_cmd]['is_timeout'] = True @@ -308,8 +304,7 @@ def send_cmd_auto(self, cmd: int, status: int, data: bytearray = None, callback= del self.wait_response_map[cmd] # make data frame data_frame = self.make_data_frame_bytes(cmd, status, data) - task = {'cmd': cmd, 'frame': data_frame, - 'timeout': timeout, 'close': close} + task = {'cmd': cmd, 'frame': data_frame, 'timeout': timeout, 'close': close} if callable(callback): task['callback'] = callback self.send_data_queue.put(task) @@ -327,6 +322,12 @@ def send_cmd_sync(self, cmd: int, status: int, data: bytearray or bytes or list """ if isinstance(data, int): data = [data] # warp array. + if len(self.commands): + # check if chameleon can understand this command + if cmd not in self.commands: + raise CMDInvalidException(f"This device doesn't declare that it can support this command: {cmd}.\nMake " + f"sure firmware is up to date and matches client") + # return Response(cmd=cmd, status=0, data=b"\0" * 32) # forge fake response to not break app # first to send cmd, no callback mode(sync) self.send_cmd_auto(cmd, status, data, None, timeout) # wait cmd start process From 4272d7661ac3cdb7761e695a676849a21a5bff2a Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 28 Aug 2023 09:48:05 +0200 Subject: [PATCH 2/9] Fix GET_DEVICE crash --- firmware/application/src/app_cmd.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index 2c0425cf..dac9939b 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -34,12 +34,14 @@ data_frame_tx_t *cmd_processor_get_git_version(uint16_t cmd, uint16_t status, ui return data_frame_make(cmd, status, strlen(GIT_VERSION), (uint8_t *)GIT_VERSION); } + data_frame_tx_t *cmd_processor_get_device(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { #if defined(PROJECT_CHAMELEON_ULTRA) - return data_frame_make(cmd, status, 1, (uint8_t *)1); + uint8_t device = 1; #else - return data_frame_make(cmd, status, 1, (uint8_t *)0); + uint8_t device = 0; #endif + return data_frame_make(cmd, status, 1, &device); } @@ -763,11 +765,11 @@ data_frame_tx_t *cmd_processor_get_enabled_slots(uint16_t cmd, uint16_t status, data_frame_tx_t *cmd_processor_get_ble_connect_key(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { return data_frame_make( - cmd, - STATUS_DEVICE_SUCCESS, - BLE_CONNECT_KEY_LEN_MAX, // 6 - settings_get_ble_connect_key() // Get key point from config - ); + cmd, + STATUS_DEVICE_SUCCESS, + BLE_CONNECT_KEY_LEN_MAX, // 6 + settings_get_ble_connect_key() // Get key point from config + ); } data_frame_tx_t *cmd_processor_set_ble_connect_key(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { From 58e310c0c72954bf712de689a36435df8bf88733 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 28 Aug 2023 09:49:00 +0200 Subject: [PATCH 3/9] autopep8: increase line size to 120, else satest style will be reverted again --- resource/tools/make_style.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resource/tools/make_style.sh b/resource/tools/make_style.sh index eeedc88d..42242888 100755 --- a/resource/tools/make_style.sh +++ b/resource/tools/make_style.sh @@ -26,7 +26,7 @@ find . \( -not -path "./.git/*" -and -not -path "./firmware/nrf52_sdk/*" -and -n # Apply autopep8 on *py find . \( -not -path "./.git/*" -and -not -path "./firmware/nrf52_sdk/*" -and -not -path "*/venv/*" -and -not -path "*/tmp/*" -and \ -name "*.py" \) \ - -exec autopep8 -i {} \; + -exec autopep8 --in-place --max-line-length 120 {} \; # Detecting tabs. From 55a70fc2e5ebdde6bbdfb3f12cfc481710c60526 Mon Sep 17 00:00:00 2001 From: Foxushka <135865149+Foxushka@users.noreply.github.com> Date: Wed, 6 Sep 2023 22:55:26 +0300 Subject: [PATCH 4/9] Move to PP_HTONS --- firmware/application/src/app_cmd.c | 4 +- firmware/common/lwip_def.h | 142 +++++++++++++++++++++++++++++ software/script/chameleon_cmd.py | 7 +- 3 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 firmware/common/lwip_def.h diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index ac198999..2a1e6a57 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -14,6 +14,7 @@ #include "nrf_pwr_mgmt.h" #include "settings.h" #include "delayed_reset.h" +#include "lwip_def.h" #define NRF_LOG_MODULE_NAME app_cmd @@ -921,8 +922,7 @@ data_frame_tx_t *cmd_processor_get_capabilities(uint16_t cmd, uint16_t status, u memset(commands, 0, count * sizeof(uint16_t)); for (size_t i = 0; i < count; i++) { - // beware: wrong endianness - commands[i] = m_data_cmd_map[i].cmd; + commands[i] = PP_HTONS(m_data_cmd_map[i].cmd); } return data_frame_make(cmd, status, count * sizeof(uint16_t), (uint8_t *)commands); diff --git a/firmware/common/lwip_def.h b/firmware/common/lwip_def.h new file mode 100644 index 00000000..7d7effe8 --- /dev/null +++ b/firmware/common/lwip_def.h @@ -0,0 +1,142 @@ +/** + * @file + * various utility macros + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/** + * @defgroup perf Performance measurement + * @ingroup sys_layer + * All defines related to this section must not be placed in lwipopts.h, + * but in arch/perf.h! + * Measurement calls made throughout lwip, these can be defined to nothing. + * - PERF_START: start measuring something. + * - PERF_STOP(x): stop measuring something, and record the result. + */ + +#ifndef LWIP_HDR_DEF_H +#define LWIP_HDR_DEF_H + +/* arch.h might define NULL already */ +#define PERF_START /* null definition */ +#define PERF_STOP(x) /* null definition */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define LWIP_MAX(x , y) (((x) > (y)) ? (x) : (y)) +#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y)) + +/* Get the number of entries in an array ('x' must NOT be a pointer!) */ +#define LWIP_ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0])) + +/** Create uint32_t value from bytes */ +#define LWIP_MAKEU32(a,b,c,d) (((uint32_t)((a) & 0xff) << 24) | \ + ((uint32_t)((b) & 0xff) << 16) | \ + ((uint32_t)((c) & 0xff) << 8) | \ + (uint32_t)((d) & 0xff)) + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#if BYTE_ORDER == BIG_ENDIAN +#define lwip_htons(x) ((uint16_t)(x)) +#define lwip_ntohs(x) ((uint16_t)(x)) +#define lwip_htonl(x) ((uint32_t)(x)) +#define lwip_ntohl(x) ((uint32_t)(x)) +#define PP_HTONS(x) ((uint16_t)(x)) +#define PP_NTOHS(x) ((uint16_t)(x)) +#define PP_HTONL(x) ((uint32_t)(x)) +#define PP_NTOHL(x) ((uint32_t)(x)) +#else /* BYTE_ORDER != BIG_ENDIAN */ +#ifndef lwip_htons +uint16_t lwip_htons(uint16_t x); +#endif +#define lwip_ntohs(x) lwip_htons(x) + +#ifndef lwip_htonl +uint32_t lwip_htonl(uint32_t x); +#endif +#define lwip_ntohl(x) lwip_htonl(x) + +/* These macros should be calculated by the preprocessor and are used + with compile-time constants only (so that there is no little-endian + overhead at runtime). */ +#define PP_HTONS(x) ((uint16_t)((((x) & (uint16_t)0x00ffU) << 8) | (((x) & (uint16_t)0xff00U) >> 8))) +#define PP_NTOHS(x) PP_HTONS(x) +#define PP_HTONL(x) ((((x) & (uint32_t)0x000000ffUL) << 24) | \ + (((x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((x) & (uint32_t)0xff000000UL) >> 24)) +#define PP_NTOHL(x) PP_HTONL(x) +#endif /* BYTE_ORDER == BIG_ENDIAN */ + +/* Functions that are not available as standard implementations. + * In cc.h, you can #define these to implementations available on + * your platform to save some code bytes if you use these functions + * in your application, too. + */ + +#ifndef lwip_itoa +/* This can be #defined to itoa() or snprintf(result, bufsize, "%d", number) depending on your platform */ +void lwip_itoa(char* result, size_t bufsize, int number); +#endif +#ifndef lwip_strnicmp +/* This can be #defined to strnicmp() or strncasecmp() depending on your platform */ +int lwip_strnicmp(const char* str1, const char* str2, size_t len); +#endif +#ifndef lwip_stricmp +/* This can be #defined to stricmp() or strcasecmp() depending on your platform */ +int lwip_stricmp(const char* str1, const char* str2); +#endif +#ifndef lwip_strnstr +/* This can be #defined to strnstr() depending on your platform */ +char* lwip_strnstr(const char* buffer, const char* token, size_t n); +#endif +#ifndef lwip_strnistr +/* This can be #defined to strnistr() depending on your platform */ +char* lwip_strnistr(const char* buffer, const char* token, size_t n); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_DEF_H */ diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index cc9f0215..1e5c1246 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -846,12 +846,7 @@ def get_device_capabilities(self): try: ret = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_CAPABILITIES, 0x00) - - for i in range(0, len(ret.data), 2): - if i + 1 < len(ret.data): - commands.append((ret.data[i + 1] << 8) | ret.data[i]) - - self.device.commands = commands + self.device.commands = struct.unpack(f"{len(ret.data) // 2}H", ret.data) except: print("Chameleon doesn't understand get capabilities command. Please update firmware") From 2e912285f53ad39724d10bf4a02a47e24d1b3793 Mon Sep 17 00:00:00 2001 From: Foxushka <135865149+Foxushka@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:36:43 +0300 Subject: [PATCH 5/9] Implement DATA_CMD_GET_MF1_ANTI_COLL_DATA and DATA_CMD_GET_SETTINGS --- firmware/application/src/app_cmd.c | 39 +++++++++- firmware/application/src/data_cmd.h | 1 + .../application/src/rfid/nfctag/hf/nfc_mf1.c | 11 +++ .../application/src/rfid/nfctag/hf/nfc_mf1.h | 1 + software/script/chameleon_cli_unit.py | 28 ++++++-- software/script/chameleon_cmd.py | 71 ++++++++++++------- 6 files changed, 119 insertions(+), 32 deletions(-) diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index 2a1e6a57..6243a189 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -117,6 +117,19 @@ data_frame_tx_t *cmd_processor_reset_settings(uint16_t cmd, uint16_t status, uin return data_frame_make(cmd, status, 0, NULL); } +data_frame_tx_t *cmd_processor_get_settings(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + uint8_t settings[6 + BLE_CONNECT_KEY_LEN_MAX] = {}; + settings[0] = SETTINGS_CURRENT_VERSION; // current version + settings[1] = settings_get_animation_config(); // animation mode + settings[2] = settings_get_button_press_config('A'); // short A button press mode + settings[3] = settings_get_button_press_config('B'); // short B button press mode + settings[4] = settings_get_long_button_press_config('A'); // long A button press mode + settings[5] = settings_get_long_button_press_config('B'); // long B button press mode + memcpy(settings + 6, settings_get_ble_connect_key(), BLE_CONNECT_KEY_LEN_MAX); + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 6 + BLE_CONNECT_KEY_LEN_MAX, settings); +} + + data_frame_tx_t *cmd_processor_set_animation_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { if (length == 1) { status = STATUS_DEVICE_SUCCESS; @@ -446,11 +459,30 @@ data_frame_tx_t *cmd_processor_set_em410x_emu_id(uint16_t cmd, uint16_t status, } data_frame_tx_t *cmd_processor_get_em410x_emu_id(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + tag_specific_type_t tag_type[2]; + tag_emulation_get_specific_type_by_slot(tag_emulation_get_slot(), tag_type); + if (tag_type[1] == TAG_TYPE_UNKNOWN) { + return data_frame_make(cmd, STATUS_PAR_ERR, 0, data); // no data in slot, don't send garbage + } tag_data_buffer_t *buffer = get_buffer_by_tag_type(TAG_TYPE_EM410X); uint8_t responseData[LF_EM410X_TAG_ID_SIZE]; memcpy(responseData, buffer->buffer, LF_EM410X_TAG_ID_SIZE); - status = STATUS_DEVICE_SUCCESS; - return data_frame_make(cmd, status, LF_EM410X_TAG_ID_SIZE, responseData); + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, LF_EM410X_TAG_ID_SIZE, responseData); +} + +data_frame_tx_t *cmd_processor_get_mf1_anti_coll_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + tag_specific_type_t tag_type[2]; + tag_emulation_get_specific_type_by_slot(tag_emulation_get_slot(), tag_type); + if (tag_type[0] == TAG_TYPE_UNKNOWN) { + return data_frame_make(cmd, STATUS_PAR_ERR, 0, data); // no data in slot, don't send garbage + } + uint8_t responseData[sizeof(picc_14a_tag_t)] = {}; + nfc_tag_14a_coll_res_reference_t *info = get_saved_mifare_coll_res(); + memcpy(responseData, info->uid, *info->size); + responseData[10] = *info->size; // size is 2 byte len, but... + responseData[12] = info->sak[0]; + memcpy(&responseData[13], info->atqa, 2); + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, sizeof(picc_14a_tag_t), responseData); } data_frame_tx_t *cmd_processor_set_mf1_detection_enable(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { @@ -855,7 +887,7 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_SET_BLE_CONNECT_KEY_CONFIG, NULL, cmd_processor_set_ble_connect_key, NULL }, { DATA_CMD_DELETE_ALL_BLE_BONDS, NULL, cmd_processor_del_ble_all_bonds, NULL }, { DATA_CMD_GET_DEVICE, NULL, cmd_processor_get_device, NULL }, - // { DATA_CMD_GET_SETTINGS, NULL, NULL, NULL }, + { DATA_CMD_GET_SETTINGS, NULL, cmd_processor_get_settings, NULL }, { DATA_CMD_GET_DEVICE_CAPABILITIES, NULL, NULL, NULL }, #if defined(PROJECT_CHAMELEON_ULTRA) @@ -910,6 +942,7 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_SET_MF1_USE_FIRST_BLOCK_COLL, NULL, cmd_processor_set_mf1_use_coll_res, NULL }, { DATA_CMD_GET_MF1_WRITE_MODE, NULL, cmd_processor_get_mf1_write_mode, NULL }, { DATA_CMD_SET_MF1_WRITE_MODE, NULL, cmd_processor_set_mf1_write_mode, NULL }, + { DATA_CMD_GET_MF1_ANTI_COLL_DATA, NULL, cmd_processor_get_mf1_anti_coll_data, NULL }, { DATA_CMD_SET_SLOT_TAG_NICK, NULL, cmd_processor_set_slot_tag_nick_name, NULL }, { DATA_CMD_GET_SLOT_TAG_NICK, NULL, cmd_processor_get_slot_tag_nick_name, NULL }, diff --git a/firmware/application/src/data_cmd.h b/firmware/application/src/data_cmd.h index c44723d5..3d6976cd 100644 --- a/firmware/application/src/data_cmd.h +++ b/firmware/application/src/data_cmd.h @@ -100,6 +100,7 @@ #define DATA_CMD_SET_MF1_USE_FIRST_BLOCK_COLL (4015) #define DATA_CMD_GET_MF1_WRITE_MODE (4016) #define DATA_CMD_SET_MF1_WRITE_MODE (4017) +#define DATA_CMD_GET_MF1_ANTI_COLL_DATA (4018) // // ****************************************************************** diff --git a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c index 19395312..129e7b9d 100644 --- a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c +++ b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c @@ -1070,6 +1070,17 @@ nfc_tag_14a_coll_res_reference_t *get_mifare_coll_res() { return &m_shadow_coll_res; } + +nfc_tag_14a_coll_res_reference_t *get_saved_mifare_coll_res() { + // Always give saved data, not from block 0 + m_shadow_coll_res.sak = m_tag_information->res_coll.sak; + m_shadow_coll_res.atqa = m_tag_information->res_coll.atqa; + m_shadow_coll_res.uid = m_tag_information->res_coll.uid; + m_shadow_coll_res.size = &(m_tag_information->res_coll.size); + m_shadow_coll_res.ats = &(m_tag_information->res_coll.ats); + return &m_shadow_coll_res; +} + /** * @brief Reconcile when the parameter label needs to be reset */ diff --git a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h index 97041dc1..f07d0e46 100644 --- a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h +++ b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h @@ -145,6 +145,7 @@ bool nfc_tag_mf1_is_detection_enable(void); void nfc_tag_mf1_detection_log_clear(void); uint32_t nfc_tag_mf1_detection_log_count(void); nfc_tag_14a_coll_res_reference_t *get_mifare_coll_res(void); +nfc_tag_14a_coll_res_reference_t *get_saved_mifare_coll_res(void); void nfc_tag_mf1_set_gen1a_magic_mode(bool enable); bool nfc_tag_mf1_is_gen1a_magic_mode(void); void nfc_tag_mf1_set_gen2_magic_mode(bool enable); diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index b45d626e..0a6b1905 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -841,6 +841,28 @@ def on_exec(self, args: argparse.Namespace): print(" - Set anti-collision resources success") +@hf_mf.command('info', 'Get information about current slot (UID/SAK/ATQA)') +class HFMFInfo(DeviceRequiredUnit): + def args_parser(self) -> ArgumentParserNoExit or None: + pass + + def scan(self): + resp: chameleon_com.Response = self.cmd.get_mf1_anti_coll_data() + if resp.status == chameleon_status.Device.STATUS_DEVICE_SUCCESS: + info = chameleon_cstruct.parse_14a_scan_tag_result(resp.data) + print(f"- UID Size: {info['uid_size']}") + print(f"- UID Hex : {info['uid_hex'].upper()}") + print(f"- SAK Hex : {info['sak_hex'].upper()}") + print(f"- ATQA Hex : {info['atqa_hex'].upper()}") + return True + else: + print("No data loaded in slot") + return False + + def on_exec(self, args: argparse.Namespace): + return self.scan() + + @lf_em.command('read', 'Scan em410x tag and print id') class LFEMRead(ReaderRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: @@ -1354,15 +1376,13 @@ def args_parser(self) -> ArgumentParserNoExit or None: def on_exec(self, args: argparse.Namespace): if len(args.key) != 6: - print( - f" - {colorama.Fore.RED}The ble connect key length must be 6{colorama.Style.RESET_ALL}") + print(f" - {colorama.Fore.RED}The ble connect key length must be 6{colorama.Style.RESET_ALL}") return if re.match(r'[0-9]{6}', args.key): self.cmd.set_ble_connect_key(args.key) print(" - Successfully set ble connect key to settings") else: - print( - f" - {colorama.Fore.RED}Only 6 ASCII characters from 0 to 9 are supported.{colorama.Style.RESET_ALL}") + print(f" - {colorama.Fore.RED}Only 6 ASCII characters from 0 to 9 are supported.{colorama.Style.RESET_ALL}") @hw_settings_ble_key.command('get', 'Get the ble connect key') diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index 1e5c1246..b7a5ca7a 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -5,6 +5,8 @@ import chameleon_status from chameleon_utils import expect_response +CURRENT_VERSION_SETTINGS = 4 + DATA_CMD_GET_APP_VERSION = 1000 DATA_CMD_CHANGE_MODE = 1001 DATA_CMD_GET_DEVICE_MODE = 1002 @@ -86,6 +88,7 @@ DATA_CMD_SET_MF1_USE_FIRST_BLOCK_COLL = 4015 DATA_CMD_GET_MF1_WRITE_MODE = 4016 DATA_CMD_SET_MF1_WRITE_MODE = 4017 +DATA_CMD_GET_MF1_ANTI_COLL_DATA = 4018 DATA_CMD_SET_EM410X_EMU_ID = 5000 DATA_CMD_GET_EM410X_EMU_ID = 5001 @@ -272,8 +275,8 @@ def usage(self): 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" @@ -407,10 +410,7 @@ def acquire_darkside(self, block_target, type_target, first_recover: int or bool data.append(sync_max) return self.device.send_cmd_sync(DATA_CMD_MF1_DARKSIDE_ACQUIRE, 0x00, data, timeout=sync_max + 5) - @expect_response([ - chameleon_status.Device.HF_TAG_OK, - chameleon_status.Device.MF_ERR_AUTH, - ]) + @expect_response([chameleon_status.Device.HF_TAG_OK, chameleon_status.Device.MF_ERR_AUTH]) def auth_mf1_key(self, block, type_value, key): """ Verify the mf1 key, only verify the specified type of key for a single sector @@ -533,10 +533,8 @@ def delete_slot_sense_type(self, slot_index: SlotNumber, sense_type: TagSenseTyp :param sense_type: Sense type to disable :return: """ - return self.device.send_cmd_sync(DATA_CMD_DELETE_SLOT_SENSE_TYPE, 0x00, bytearray([ - SlotNumber.to_fw(slot_index), - sense_type, - ])) + return self.device.send_cmd_sync(DATA_CMD_DELETE_SLOT_SENSE_TYPE, 0x00, + bytearray([SlotNumber.to_fw(slot_index), sense_type])) @expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS) def set_slot_data_default(self, slot_index: SlotNumber, tag_type: TagSpecificType): @@ -784,11 +782,7 @@ def set_button_press_fun(self, button: ButtonType, function: ButtonPressFunction """ Set config of button press function """ - return self.device.send_cmd_sync( - DATA_CMD_SET_BUTTON_PRESS_CONFIG, - 0x00, - bytearray([button, function]) - ) + return self.device.send_cmd_sync(DATA_CMD_SET_BUTTON_PRESS_CONFIG, 0x00, bytearray([button, function])) @expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS) def get_long_button_press_fun(self, button: ButtonType): @@ -802,11 +796,7 @@ def set_long_button_press_fun(self, button: ButtonType, function: ButtonPressFun """ Set config of button press function """ - return self.device.send_cmd_sync( - DATA_CMD_SET_LONG_BUTTON_PRESS_CONFIG, - 0x00, - bytearray([button, function]) - ) + return self.device.send_cmd_sync(DATA_CMD_SET_LONG_BUTTON_PRESS_CONFIG, 0x00, bytearray([button, function])) @expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS) def set_ble_connect_key(self, key: str): @@ -819,11 +809,7 @@ def set_ble_connect_key(self, key: str): if len(data_bytes) != 6: raise ValueError("The ble connect key length must be 6") - return self.device.send_cmd_sync( - DATA_CMD_SET_BLE_CONNECT_KEY_CONFIG, - 0x00, - data_bytes - ) + return self.device.send_cmd_sync(DATA_CMD_SET_BLE_CONNECT_KEY_CONFIG, 0x00, data_bytes) def get_ble_connect_key(self): """ @@ -852,6 +838,41 @@ def get_device_capabilities(self): return commands + def get_device_type(self): + """ + Get device type + 0 - Chameleon Ultra + 1 - Chameleon Lite + """ + + return self.device.send_cmd_sync(DATA_CMD_GET_DEVICE, 0x00).data[0] + + def get_device_settings(self): + """ + Get all possible settings + For version 4: + settings[0] = SETTINGS_CURRENT_VERSION; // current version + settings[1] = settings_get_animation_config(); // animation mode + settings[2] = settings_get_button_press_config('A'); // short A button press mode + settings[3] = settings_get_button_press_config('B'); // short B button press mode + settings[4] = settings_get_long_button_press_config('A'); // long A button press mode + settings[5] = settings_get_long_button_press_config('B'); // long B button press mode + settings[6:12] = settings_get_ble_connect_key(); // BLE connection key + """ + data = self.device.send_cmd_sync(DATA_CMD_GET_SETTINGS, 0x00).data + if data[0] != CURRENT_VERSION_SETTINGS: + raise ValueError("Settings version in app doesn't match Chameleon response. Upgrade client or Chameleon " + "firmware") + return data + + def get_mf1_anti_coll_data(self): + """ + Get data from current slot (UID/SAK/ATQA) + :return: + """ + return self.device.send_cmd_sync(DATA_CMD_GET_MF1_ANTI_COLL_DATA, 0x00) + + if __name__ == '__main__': # connect to chameleon dev = chameleon_com.ChameleonCom() From cc9a10bcc8d36711e22ef2dfab81b4069d56fe93 Mon Sep 17 00:00:00 2001 From: Foxushka <135865149+Foxushka@users.noreply.github.com> Date: Wed, 6 Sep 2023 20:42:03 +0000 Subject: [PATCH 6/9] Update CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1e9ace5..c1e5cf26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Add `hf mf info` command to get UID/SAK/ATQA from slot (@Foxushka) + - Add `hw raw` to send raw command to Chameleon (@Foxushka) + - Implement command to fetch all available commands from Chameleon and test if Chameleon supports it - ChameleonLite emulation bug fixed (@spp2000) - Fixed MFC emulation issues with OEM readers, also temporarily disabling NFC_MF1_FAST_SIM (@xianglin1998) - Fixed Chameleon crash during BLE pairing (@Foxushka) From 2867c7d56c30d143f725426876ee022f2337aff6 Mon Sep 17 00:00:00 2001 From: Foxushka <135865149+Foxushka@users.noreply.github.com> Date: Wed, 6 Sep 2023 20:42:13 +0000 Subject: [PATCH 7/9] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1e5cf26..31017257 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ## [unreleased][unreleased] - Add `hf mf info` command to get UID/SAK/ATQA from slot (@Foxushka) - Add `hw raw` to send raw command to Chameleon (@Foxushka) - - Implement command to fetch all available commands from Chameleon and test if Chameleon supports it + - Implement command to fetch all available commands from Chameleon and test if Chameleon supports it (@Foxushka) - ChameleonLite emulation bug fixed (@spp2000) - Fixed MFC emulation issues with OEM readers, also temporarily disabling NFC_MF1_FAST_SIM (@xianglin1998) - Fixed Chameleon crash during BLE pairing (@Foxushka) From 0398d7e9a8d395f5d1f08cfdfc580d43e98ef967 Mon Sep 17 00:00:00 2001 From: Foxushka <135865149+Foxushka@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:45:32 +0300 Subject: [PATCH 8/9] Lite doesn't have this exported --- firmware/application/src/app_cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index 6243a189..bf37eb7c 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -476,13 +476,13 @@ data_frame_tx_t *cmd_processor_get_mf1_anti_coll_data(uint16_t cmd, uint16_t sta if (tag_type[0] == TAG_TYPE_UNKNOWN) { return data_frame_make(cmd, STATUS_PAR_ERR, 0, data); // no data in slot, don't send garbage } - uint8_t responseData[sizeof(picc_14a_tag_t)] = {}; + uint8_t responseData[15] = {}; nfc_tag_14a_coll_res_reference_t *info = get_saved_mifare_coll_res(); memcpy(responseData, info->uid, *info->size); responseData[10] = *info->size; // size is 2 byte len, but... responseData[12] = info->sak[0]; memcpy(&responseData[13], info->atqa, 2); - return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, sizeof(picc_14a_tag_t), responseData); + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 15, responseData); } data_frame_tx_t *cmd_processor_set_mf1_detection_enable(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { From 902deed36237f5a4a2229d410c72cbfdc0a84b4e Mon Sep 17 00:00:00 2001 From: Foxushka <135865149+Foxushka@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:47:10 +0300 Subject: [PATCH 9/9] Eh --- firmware/application/src/app_cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index bf37eb7c..44f21ce0 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -476,13 +476,13 @@ data_frame_tx_t *cmd_processor_get_mf1_anti_coll_data(uint16_t cmd, uint16_t sta if (tag_type[0] == TAG_TYPE_UNKNOWN) { return data_frame_make(cmd, STATUS_PAR_ERR, 0, data); // no data in slot, don't send garbage } - uint8_t responseData[15] = {}; + uint8_t responseData[16] = {}; nfc_tag_14a_coll_res_reference_t *info = get_saved_mifare_coll_res(); memcpy(responseData, info->uid, *info->size); responseData[10] = *info->size; // size is 2 byte len, but... responseData[12] = info->sak[0]; memcpy(&responseData[13], info->atqa, 2); - return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 15, responseData); + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 16, responseData); } data_frame_tx_t *cmd_processor_set_mf1_detection_enable(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {