cli: move enums to new file

This commit is contained in:
Philippe Teuwen
2023-10-15 01:17:34 +02:00
parent 8ef42c705c
commit 830ba98721
3 changed files with 453 additions and 447 deletions
+84 -82
View File
@@ -19,7 +19,9 @@ import chameleon_status
from chameleon_utils import ArgumentParserNoExit, ArgsParserError, UnexpectedResponseError
from chameleon_utils import CLITree
from chameleon_utils import CR, CG, CB, CC, CY, CM, C0
from chameleon_enum import Command, SlotNumber, TagSenseType, TagSpecificType
from chameleon_enum import MifareClassicWriteMode, MifareClassicPrngType, MifareClassicDarksideStatus, MfcKeyType
from chameleon_enum import AnimationMode, ButtonType, ButtonPressFunction
# NXP IDs based on https://www.nxp.com/docs/en/application-note/AN10833.pdf
type_id_SAK_dict = {0x00: "MIFARE Ultralight Classic/C/EV1/Nano | NTAG 2xx",
@@ -177,7 +179,7 @@ class ReaderRequiredUnit(DeviceRequiredUnit):
class SlotIndexArgsUnit(DeviceRequiredUnit):
@staticmethod
def add_slot_args(parser: ArgumentParserNoExit, mandatory=False):
slot_choices = [x.value for x in chameleon_cmd.SlotNumber]
slot_choices = [x.value for x in SlotNumber]
help_str = f"Slot Index: {slot_choices} Default: active slot"
parser.add_argument('-s', "--slot", type=int, required=mandatory, help=help_str, metavar="<1-8>",
@@ -188,7 +190,7 @@ class SlotIndexArgsUnit(DeviceRequiredUnit):
class SlotIndexArgsAndGoUnit(SlotIndexArgsUnit):
def before_exec(self, args: argparse.Namespace):
if super().before_exec(args):
self.prev_slot_num = chameleon_cmd.SlotNumber.from_fw(self.cmd.get_active_slot())
self.prev_slot_num = SlotNumber.from_fw(self.cmd.get_active_slot())
if args.slot is not None:
self.slot_num = args.slot
if self.slot_num != self.prev_slot_num:
@@ -227,7 +229,7 @@ class MF1AuthArgsUnit(ReaderRequiredUnit):
class Param:
def __init__(self):
self.block = args.blk
self.type = chameleon_cmd.MfcKeyType.B if args.b else chameleon_cmd.MfcKeyType.A
self.type = MfcKeyType.B if args.b else MfcKeyType.A
key: str = args.key
if not re.match(r"^[a-fA-F0-9]{12}$", key):
raise ArgsParserError("key must include 12 HEX symbols")
@@ -350,7 +352,7 @@ class LFEMIdArgsUnit(DeviceRequiredUnit):
class TagTypeArgsUnit(DeviceRequiredUnit):
@staticmethod
def add_type_args(parser: ArgumentParserNoExit):
type_names = [t.name for t in chameleon_cmd.TagSpecificType.list()]
type_names = [t.name for t in TagSpecificType.list()]
help_str = "Tag Type: " + ", ".join(type_names)
parser.add_argument('-t', "--type", type=str, required=True, metavar="TAG_TYPE",
help=help_str, choices=type_names)
@@ -581,7 +583,7 @@ class HF14AScan(ReaderRequiredUnit):
# detect prng
print("- Mifare Classic technology")
prng_type = self.cmd.mf1_detect_prng()
print(f" # Prng: {chameleon_cmd.MifareClassicPrngType(prng_type)}")
print(f" # Prng: {MifareClassicPrngType(prng_type)}")
def sak_info(self, data_tag):
# detect the technology in use based on SAK
@@ -727,7 +729,7 @@ class HFMFNested(ReaderRequiredUnit):
def on_exec(self, args: argparse.Namespace):
block_known = args.blk
# default to A
type_known = chameleon_cmd.MfcKeyType.B if args.b else chameleon_cmd.MfcKeyType.A
type_known = MfcKeyType.B if args.b else MfcKeyType.A
key_known: str = args.key
if not re.match(r"^[a-fA-F0-9]{12}$", key_known):
@@ -736,7 +738,7 @@ class HFMFNested(ReaderRequiredUnit):
key_known: bytearray = bytearray.fromhex(key_known)
block_target = args.tblk
# default to A
type_target = chameleon_cmd.MfcKeyType.B if args.b else chameleon_cmd.MfcKeyType.A
type_target = MfcKeyType.B if args.b else MfcKeyType.A
if block_known == block_target and type_known == type_target:
print(f"{CR}Target key already known{C0}")
return
@@ -772,8 +774,8 @@ class HFMFDarkside(ReaderRequiredUnit):
while retry_count < 0xFF:
darkside_resp = self.cmd.mf1_darkside_acquire(block_target, type_target, first_recover, 30)
first_recover = False # not first run.
if darkside_resp[0] != chameleon_cmd.MifareClassicDarksideStatus.OK:
print(f"Darkside error: {chameleon_cmd.MifareClassicDarksideStatus(darkside_resp[0])}")
if darkside_resp[0] != MifareClassicDarksideStatus.OK:
print(f"Darkside error: {MifareClassicDarksideStatus(darkside_resp[0])}")
break
darkside_obj = darkside_resp[1]
@@ -815,7 +817,7 @@ class HFMFDarkside(ReaderRequiredUnit):
return None
def on_exec(self, args: argparse.Namespace):
key = self.recover_key(0x03, chameleon_cmd.MfcKeyType.A)
key = self.recover_key(0x03, MfcKeyType.A)
if key is not None:
print(f" - Key Found: {key}")
else:
@@ -1043,14 +1045,14 @@ class HFMFESave(SlotIndexArgsAndGoUnit, DeviceRequiredUnit):
selected_slot = self.cmd.get_active_slot()
slot_info = self.cmd.get_slot_info()
tag_type = chameleon_cmd.TagSpecificType(slot_info[selected_slot]['hf'])
if tag_type == chameleon_cmd.TagSpecificType.MIFARE_Mini:
tag_type = TagSpecificType(slot_info[selected_slot]['hf'])
if tag_type == TagSpecificType.MIFARE_Mini:
block_count = 20
elif tag_type == chameleon_cmd.TagSpecificType.MIFARE_1024:
elif tag_type == TagSpecificType.MIFARE_1024:
block_count = 64
elif tag_type == chameleon_cmd.TagSpecificType.MIFARE_2048:
elif tag_type == TagSpecificType.MIFARE_2048:
block_count = 128
elif tag_type == chameleon_cmd.TagSpecificType.MIFARE_4096:
elif tag_type == TagSpecificType.MIFARE_4096:
block_count = 256
else:
raise Exception("Card in current slot is not Mifare Classic/Plus in SL1 mode")
@@ -1091,7 +1093,7 @@ class HFMFEConfig(SlotIndexArgsAndGoUnit, HF14AAntiCollArgsUnit, DeviceRequiredU
block0_group.add_argument('--enable-block0', action='store_true',
help="Use anti-collision data from block 0 for 4 byte UID tags")
block0_group.add_argument('--disable-block0', action='store_true', help="Use anti-collision data from settings")
write_names = [w.name for w in chameleon_cmd.MifareClassicWriteMode.list()]
write_names = [w.name for w in MifareClassicWriteMode.list()]
help_str = "Write Mode: " + ", ".join(write_names)
parser.add_argument('--write', type=str, help=help_str, metavar="MODE", choices=write_names)
log_group = parser.add_mutually_exclusive_group()
@@ -1110,13 +1112,13 @@ class HFMFEConfig(SlotIndexArgsAndGoUnit, HF14AAntiCollArgsUnit, DeviceRequiredU
sak = anti_coll_data['sak']
ats = anti_coll_data['ats']
slotinfo = self.cmd.get_slot_info()
fwslot = chameleon_cmd.SlotNumber.to_fw(self.slot_num)
hf_tag_type = chameleon_cmd.TagSpecificType(slotinfo[fwslot]['hf'])
fwslot = SlotNumber.to_fw(self.slot_num)
hf_tag_type = TagSpecificType(slotinfo[fwslot]['hf'])
if hf_tag_type not in [
chameleon_cmd.TagSpecificType.MIFARE_Mini,
chameleon_cmd.TagSpecificType.MIFARE_1024,
chameleon_cmd.TagSpecificType.MIFARE_2048,
chameleon_cmd.TagSpecificType.MIFARE_4096,
TagSpecificType.MIFARE_Mini,
TagSpecificType.MIFARE_1024,
TagSpecificType.MIFARE_2048,
TagSpecificType.MIFARE_4096,
]:
print(f"{CR}Slot {self.slot_num} not configured as MIFARE Classic{C0}")
return
@@ -1124,7 +1126,7 @@ class HFMFEConfig(SlotIndexArgsAndGoUnit, HF14AAntiCollArgsUnit, DeviceRequiredU
gen1a_mode = mfc_config["gen1a_mode"]
gen2_mode = mfc_config["gen2_mode"]
block_anti_coll_mode = mfc_config["block_anti_coll_mode"]
write_mode = chameleon_cmd.MifareClassicWriteMode(mfc_config["write_mode"])
write_mode = MifareClassicWriteMode(mfc_config["write_mode"])
detection = mfc_config["detection"]
change_requested, change_done, uid, atqa, sak, ats = self.update_hf14a_anticoll(args, uid, atqa, sak, ats)
if args.enable_gen1a:
@@ -1177,7 +1179,7 @@ class HFMFEConfig(SlotIndexArgsAndGoUnit, HF14AAntiCollArgsUnit, DeviceRequiredU
print(f'{CY}Requested block0 anti-coll mode already disabled{C0}')
if args.write is not None:
change_requested = True
new_write_mode = chameleon_cmd.MifareClassicWriteMode[args.write]
new_write_mode = MifareClassicWriteMode[args.write]
if new_write_mode != write_mode:
write_mode = new_write_mode
self.cmd.mf1_set_write_mode(write_mode)
@@ -1219,7 +1221,7 @@ class HFMFEConfig(SlotIndexArgsAndGoUnit, HF14AAntiCollArgsUnit, DeviceRequiredU
f'- {"Use anti-collision data from block 0:":40}'
f'{f"{CG}enabled{C0}" if block_anti_coll_mode else f"{CR}disabled{C0}"}')
try:
print(f'- {"Write mode:":40}{CY}{chameleon_cmd.MifareClassicWriteMode(write_mode)}{C0}')
print(f'- {"Write mode:":40}{CY}{MifareClassicWriteMode(write_mode)}{C0}')
except ValueError:
print(f'- {"Write mode:":40}{CR}invalid value!{C0}')
print(
@@ -1333,12 +1335,12 @@ class HFMFUEConfig(SlotIndexArgsAndGoUnit, HF14AAntiCollArgsUnit, DeviceRequired
sak = anti_coll_data['sak']
ats = anti_coll_data['ats']
slotinfo = self.cmd.get_slot_info()
fwslot = chameleon_cmd.SlotNumber.to_fw(self.slot_num)
hf_tag_type = chameleon_cmd.TagSpecificType(slotinfo[fwslot]['hf'])
fwslot = SlotNumber.to_fw(self.slot_num)
hf_tag_type = TagSpecificType(slotinfo[fwslot]['hf'])
if hf_tag_type not in [
chameleon_cmd.TagSpecificType.NTAG_213,
chameleon_cmd.TagSpecificType.NTAG_215,
chameleon_cmd.TagSpecificType.NTAG_216,
TagSpecificType.NTAG_213,
TagSpecificType.NTAG_215,
TagSpecificType.NTAG_216,
]:
print(f"{CR}Slot {self.slot_num} not configured as MIFARE Ultralight / NTAG{C0}")
return
@@ -1407,21 +1409,21 @@ class HWSlotList(DeviceRequiredUnit):
def on_exec(self, args: argparse.Namespace):
slotinfo = self.cmd.get_slot_info()
selected = chameleon_cmd.SlotNumber.from_fw(self.cmd.get_active_slot())
selected = SlotNumber.from_fw(self.cmd.get_active_slot())
current = selected
enabled = self.cmd.get_enabled_slots()
maxnamelength = 0
slotnames = []
for slot in chameleon_cmd.SlotNumber:
hfn = self.get_slot_name(slot, chameleon_cmd.TagSenseType.HF)
lfn = self.get_slot_name(slot, chameleon_cmd.TagSenseType.LF)
for slot in SlotNumber:
hfn = self.get_slot_name(slot, TagSenseType.HF)
lfn = self.get_slot_name(slot, TagSenseType.LF)
m = max(hfn['baselen'], lfn['baselen'])
maxnamelength = m if m > maxnamelength else maxnamelength
slotnames.append({'hf': hfn, 'lf': lfn})
for slot in chameleon_cmd.SlotNumber:
fwslot = chameleon_cmd.SlotNumber.to_fw(slot)
hf_tag_type = chameleon_cmd.TagSpecificType(slotinfo[fwslot]['hf'])
lf_tag_type = chameleon_cmd.TagSpecificType(slotinfo[fwslot]['lf'])
for slot in SlotNumber:
fwslot = SlotNumber.to_fw(slot)
hf_tag_type = TagSpecificType(slotinfo[fwslot]['hf'])
lf_tag_type = TagSpecificType(slotinfo[fwslot]['lf'])
print(f' - {f"Slot {slot}:":{4+maxnamelength+1}}'
f'{f"({CG}active{C0})" if slot == selected else ""}')
@@ -1430,7 +1432,7 @@ class HWSlotList(DeviceRequiredUnit):
print(f' HF: '
f'{slotnames[fwslot]["hf"]["name"]:{field_length}}', end='')
print(f'{f"({CR}disabled{C0}) " if not enabled[fwslot]["hf"] else ""}', end='')
if hf_tag_type != chameleon_cmd.TagSpecificType.UNDEFINED:
if hf_tag_type != TagSpecificType.UNDEFINED:
print(f"{CY if enabled[fwslot]['hf'] else C0}{hf_tag_type}{C0}")
else:
print("undef")
@@ -1451,10 +1453,10 @@ class HWSlotList(DeviceRequiredUnit):
if len(ats) > 0:
print(f' {"ATS:":40}{CY}{ats.hex().upper()}{C0}')
if hf_tag_type in [
chameleon_cmd.TagSpecificType.MIFARE_Mini,
chameleon_cmd.TagSpecificType.MIFARE_1024,
chameleon_cmd.TagSpecificType.MIFARE_2048,
chameleon_cmd.TagSpecificType.MIFARE_4096,
TagSpecificType.MIFARE_Mini,
TagSpecificType.MIFARE_1024,
TagSpecificType.MIFARE_2048,
TagSpecificType.MIFARE_4096,
]:
config = self.cmd.mf1_get_emulator_config()
# print(' - Mifare Classic emulator settings:')
@@ -1469,7 +1471,7 @@ class HWSlotList(DeviceRequiredUnit):
f'{f"{CG}enabled{C0}" if config["block_anti_coll_mode"] else f"{CR}disabled{C0}"}')
try:
print(f' {"Write mode:":40}{CY}'
f'{chameleon_cmd.MifareClassicWriteMode(config["write_mode"])}{C0}')
f'{MifareClassicWriteMode(config["write_mode"])}{C0}')
except ValueError:
print(f' {"Write mode:":40}{CR}invalid value!{C0}')
print(
@@ -1481,7 +1483,7 @@ class HWSlotList(DeviceRequiredUnit):
print(f' LF: '
f'{slotnames[fwslot]["lf"]["name"]:{field_length}}', end='')
print(f'{f"({CR}disabled{C0}) " if not enabled[fwslot]["lf"] else ""}', end='')
if lf_tag_type != chameleon_cmd.TagSpecificType.UNDEFINED:
if lf_tag_type != TagSpecificType.UNDEFINED:
print(f"{CY if enabled[fwslot]['lf'] else C0}{lf_tag_type}{C0}")
else:
print("undef")
@@ -1519,11 +1521,11 @@ class HWSlotType(TagTypeArgsUnit, SlotIndexArgsUnit):
return parser
def on_exec(self, args: argparse.Namespace):
tag_type = chameleon_cmd.TagSpecificType[args.type]
tag_type = TagSpecificType[args.type]
if args.slot is not None:
slot_num = args.slot
else:
slot_num = chameleon_cmd.SlotNumber.from_fw(self.cmd.get_active_slot())
slot_num = SlotNumber.from_fw(self.cmd.get_active_slot())
self.cmd.set_slot_tag_type(slot_num, tag_type)
print(f' - Set slot {slot_num} tag type success.')
@@ -1541,11 +1543,11 @@ class HWDeleteSlotSense(SlotIndexArgsUnit, SenseTypeArgsUnit):
if args.slot is not None:
slot_num = args.slot
else:
slot_num = chameleon_cmd.SlotNumber.from_fw(self.cmd.get_active_slot())
slot_num = SlotNumber.from_fw(self.cmd.get_active_slot())
if args.lf:
sense_type = chameleon_cmd.TagSenseType.LF
sense_type = TagSenseType.LF
else:
sense_type = chameleon_cmd.TagSenseType.HF
sense_type = TagSenseType.HF
self.cmd.delete_slot_sense_type(slot_num, sense_type)
print(f' - Delete slot {slot_num} {sense_type.name} tag type success.')
@@ -1560,11 +1562,11 @@ class HWSlotInit(TagTypeArgsUnit, SlotIndexArgsUnit):
return parser
def on_exec(self, args: argparse.Namespace):
tag_type = chameleon_cmd.TagSpecificType[args.type]
tag_type = TagSpecificType[args.type]
if args.slot is not None:
slot_num = args.slot
else:
slot_num = chameleon_cmd.SlotNumber.from_fw(self.cmd.get_active_slot())
slot_num = SlotNumber.from_fw(self.cmd.get_active_slot())
self.cmd.set_slot_data_default(slot_num, tag_type)
print(' - Set slot tag data init success.')
@@ -1582,11 +1584,11 @@ class HWSlotEnable(SlotIndexArgsUnit, SenseTypeArgsUnit):
if args.slot is not None:
slot_num = args.slot
else:
slot_num = chameleon_cmd.SlotNumber.from_fw(self.cmd.get_active_slot())
slot_num = SlotNumber.from_fw(self.cmd.get_active_slot())
if args.lf:
sense_type = chameleon_cmd.TagSenseType.LF
sense_type = TagSenseType.LF
else:
sense_type = chameleon_cmd.TagSenseType.HF
sense_type = TagSenseType.HF
self.cmd.set_slot_enable(slot_num, sense_type, True)
print(f' - Enable slot {slot_num} {sense_type.name} success.')
@@ -1603,9 +1605,9 @@ class HWSlotDisable(SlotIndexArgsUnit, SenseTypeArgsUnit):
def on_exec(self, args: argparse.Namespace):
slot_num = args.slot
if args.lf:
sense_type = chameleon_cmd.TagSenseType.LF
sense_type = TagSenseType.LF
else:
sense_type = chameleon_cmd.TagSenseType.HF
sense_type = TagSenseType.HF
self.cmd.set_slot_enable(slot_num, sense_type, False)
print(f' - Disable slot {slot_num} {sense_type.name} success.')
@@ -1645,11 +1647,11 @@ class HWSlotNick(SlotIndexArgsUnit, SenseTypeArgsUnit):
if args.slot is not None:
slot_num = args.slot
else:
slot_num = chameleon_cmd.SlotNumber.from_fw(self.cmd.get_active_slot())
slot_num = SlotNumber.from_fw(self.cmd.get_active_slot())
if args.lf:
sense_type = chameleon_cmd.TagSenseType.LF
sense_type = TagSenseType.LF
else:
sense_type = chameleon_cmd.TagSenseType.HF
sense_type = TagSenseType.HF
if args.name is not None:
name: str = args.name
encoded_name = name.encode(encoding="utf8")
@@ -1687,11 +1689,11 @@ class HWSlotOpenAll(DeviceRequiredUnit):
def on_exec(self, args: argparse.Namespace):
# what type you need set to default?
hf_type = chameleon_cmd.TagSpecificType.MIFARE_1024
lf_type = chameleon_cmd.TagSpecificType.EM410X
hf_type = TagSpecificType.MIFARE_1024
lf_type = TagSpecificType.EM410X
# set all slot
for slot in chameleon_cmd.SlotNumber:
for slot in SlotNumber:
print(f' Slot {slot} setting...')
# first to set tag type
self.cmd.set_slot_tag_type(slot, hf_type)
@@ -1700,8 +1702,8 @@ class HWSlotOpenAll(DeviceRequiredUnit):
self.cmd.set_slot_data_default(slot, hf_type)
self.cmd.set_slot_data_default(slot, lf_type)
# finally, we can enable this slot.
self.cmd.set_slot_enable(slot, chameleon_cmd.TagSenseType.HF, True)
self.cmd.set_slot_enable(slot, chameleon_cmd.TagSenseType.LF, True)
self.cmd.set_slot_enable(slot, TagSenseType.HF, True)
self.cmd.set_slot_enable(slot, TagSenseType.LF, True)
print(f' Slot {slot} setting done.')
# update config and save to flash
@@ -1734,7 +1736,7 @@ class HWSettingsAnimation(DeviceRequiredUnit):
def args_parser(self) -> ArgumentParserNoExit:
parser = ArgumentParserNoExit()
parser.description = 'Get or change current animation mode value'
mode_names = [m.name for m in list(chameleon_cmd.AnimationMode)]
mode_names = [m.name for m in list(AnimationMode)]
help_str = "Mode: " + ", ".join(mode_names)
parser.add_argument('-m', '--mode', type=str, required=False,
help=help_str, metavar="MODE", choices=mode_names)
@@ -1742,12 +1744,12 @@ class HWSettingsAnimation(DeviceRequiredUnit):
def on_exec(self, args: argparse.Namespace):
if args.mode is not None:
mode = chameleon_cmd.AnimationMode[args.mode]
mode = AnimationMode[args.mode]
self.cmd.set_animation_mode(mode)
print("Animation mode change success.")
print(f"{CY}Do not forget to store your settings in flash!{C0}")
else:
print(chameleon_cmd.AnimationMode(self.cmd.get_animation_mode()))
print(AnimationMode(self.cmd.get_animation_mode()))
@hw_settings.command('bleclearbonds')
@@ -1852,8 +1854,8 @@ class HWButtonSettingsGet(DeviceRequiredUnit):
duration_group = parser.add_mutually_exclusive_group()
duration_group.add_argument('-s', '--short', action='store_true', help="Short-press (default)")
duration_group.add_argument('-l', '--long', action='store_true', help="Long-press")
function_names = [f.name for f in list(chameleon_cmd.ButtonPressFunction)]
function_descs = [f"{f.name} ({f})" for f in list(chameleon_cmd.ButtonPressFunction)]
function_names = [f.name for f in list(ButtonPressFunction)]
function_descs = [f"{f.name} ({f})" for f in list(ButtonPressFunction)]
help_str = "Function: " + ", ".join(function_descs)
parser.add_argument('-f', '--function', type=str, required=False,
help=help_str, metavar="FUNCTION", choices=function_names)
@@ -1861,14 +1863,14 @@ class HWButtonSettingsGet(DeviceRequiredUnit):
def on_exec(self, args: argparse.Namespace):
if args.function is not None:
function = chameleon_cmd.ButtonPressFunction[args.function]
function = ButtonPressFunction[args.function]
if not args.a and not args.b:
print(f"{CR}You must specify which button you want to change{C0}")
return
if args.a:
button = chameleon_cmd.ButtonType.A
button = ButtonType.A
else:
button = chameleon_cmd.ButtonType.B
button = ButtonType.B
if args.long:
self.cmd.set_long_button_press_config(button, function)
else:
@@ -1878,19 +1880,19 @@ class HWButtonSettingsGet(DeviceRequiredUnit):
print(f"{CY}Do not forget to store your settings in flash!{C0}")
else:
if args.a:
button_list = [chameleon_cmd.ButtonType.A]
button_list = [ButtonType.A]
elif args.b:
button_list = [chameleon_cmd.ButtonType.B]
button_list = [ButtonType.B]
else:
button_list = list(chameleon_cmd.ButtonType)
button_list = list(ButtonType)
for button in button_list:
if not args.long:
resp = self.cmd.get_button_press_config(button)
button_fn = chameleon_cmd.ButtonPressFunction(resp)
button_fn = ButtonPressFunction(resp)
print(f" - {CG}{button.name} short{C0}: {button_fn}")
if not args.short:
resp_long = self.cmd.get_long_button_press_config(button)
button_long_fn = chameleon_cmd.ButtonPressFunction(resp_long)
button_long_fn = ButtonPressFunction(resp_long)
print(f" - {CG}{button.name} long {C0}: {button_long_fn}")
print("")
@@ -1965,7 +1967,7 @@ class HWRaw(DeviceRequiredUnit):
def args_parser(self) -> ArgumentParserNoExit:
parser = ArgumentParserNoExit()
parser.description = 'Send raw command'
cmd_names = sorted([c.name for c in list(chameleon_cmd.Command)])
cmd_names = sorted([c.name for c in list(Command)])
help_str = "Command: " + ", ".join(cmd_names)
command_group = parser.add_mutually_exclusive_group(required=True)
command_group.add_argument('-c', '--command', type=str, metavar="COMMAND", help=help_str, choices=cmd_names)
@@ -1976,7 +1978,7 @@ class HWRaw(DeviceRequiredUnit):
def on_exec(self, args: argparse.Namespace):
if args.command is not None:
command = chameleon_cmd.Command[args.command]
command = Command[args.command]
else:
# We accept not-yet-known command ids as "hw raw" is meant for debugging
command = args.num_command
@@ -1984,7 +1986,7 @@ class HWRaw(DeviceRequiredUnit):
command, data=bytes.fromhex(args.data), status=0x0, timeout=args.timeout)
print(" - Received:")
try:
command = chameleon_cmd.Command(response.cmd)
command = Command(response.cmd)
print(f" Command: {response.cmd} {command.name}")
except ValueError:
print(f" Command: {response.cmd} (unknown)")
+3 -365
View File
@@ -1,377 +1,15 @@
import enum
import struct
import ctypes
import chameleon_com
import chameleon_status
from chameleon_utils import expect_response
from chameleon_enum import Command, SlotNumber, TagSenseType, TagSpecificType
from chameleon_enum import MifareClassicDarksideStatus
from chameleon_enum import ButtonType, ButtonPressFunction
CURRENT_VERSION_SETTINGS = 5
@enum.unique
class Command(enum.IntEnum):
GET_APP_VERSION = 1000
CHANGE_DEVICE_MODE = 1001
GET_DEVICE_MODE = 1002
SET_ACTIVE_SLOT = 1003
SET_SLOT_TAG_TYPE = 1004
SET_SLOT_DATA_DEFAULT = 1005
SET_SLOT_ENABLE = 1006
SET_SLOT_TAG_NICK = 1007
GET_SLOT_TAG_NICK = 1008
SLOT_DATA_CONFIG_SAVE = 1009
ENTER_BOOTLOADER = 1010
GET_DEVICE_CHIP_ID = 1011
GET_DEVICE_ADDRESS = 1012
SAVE_SETTINGS = 1013
RESET_SETTINGS = 1014
SET_ANIMATION_MODE = 1015
GET_ANIMATION_MODE = 1016
GET_GIT_VERSION = 1017
GET_ACTIVE_SLOT = 1018
GET_SLOT_INFO = 1019
WIPE_FDS = 1020
DELETE_SLOT_TAG_NICK = 1021
GET_ENABLED_SLOTS = 1023
DELETE_SLOT_SENSE_TYPE = 1024
GET_BATTERY_INFO = 1025
GET_BUTTON_PRESS_CONFIG = 1026
SET_BUTTON_PRESS_CONFIG = 1027
GET_LONG_BUTTON_PRESS_CONFIG = 1028
SET_LONG_BUTTON_PRESS_CONFIG = 1029
SET_BLE_PAIRING_KEY = 1030
GET_BLE_PAIRING_KEY = 1031
DELETE_ALL_BLE_BONDS = 1032
GET_DEVICE_MODEL = 1033
# FIXME: implemented but unused in CLI commands
GET_DEVICE_SETTINGS = 1034
GET_DEVICE_CAPABILITIES = 1035
GET_BLE_PAIRING_ENABLE = 1036
SET_BLE_PAIRING_ENABLE = 1037
HF14A_SCAN = 2000
MF1_DETECT_SUPPORT = 2001
MF1_DETECT_PRNG = 2002
MF1_STATIC_NESTED_ACQUIRE = 2003
MF1_DARKSIDE_ACQUIRE = 2004
MF1_DETECT_NT_DIST = 2005
MF1_NESTED_ACQUIRE = 2006
MF1_AUTH_ONE_KEY_BLOCK = 2007
MF1_READ_ONE_BLOCK = 2008
MF1_WRITE_ONE_BLOCK = 2009
HF14A_RAW = 2010
EM410X_SCAN = 3000
EM410X_WRITE_TO_T55XX = 3001
MF1_WRITE_EMU_BLOCK_DATA = 4000
HF14A_SET_ANTI_COLL_DATA = 4001
MF1_SET_DETECTION_ENABLE = 4004
MF1_GET_DETECTION_COUNT = 4005
MF1_GET_DETECTION_LOG = 4006
# FIXME: not implemented
MF1_GET_DETECTION_ENABLE = 4007
MF1_READ_EMU_BLOCK_DATA = 4008
MF1_GET_EMULATOR_CONFIG = 4009
# FIXME: not implemented
MF1_GET_GEN1A_MODE = 4010
MF1_SET_GEN1A_MODE = 4011
# FIXME: not implemented
MF1_GET_GEN2_MODE = 4012
MF1_SET_GEN2_MODE = 4013
# FIXME: not implemented
MF1_GET_BLOCK_ANTI_COLL_MODE = 4014
MF1_SET_BLOCK_ANTI_COLL_MODE = 4015
# FIXME: not implemented
MF1_GET_WRITE_MODE = 4016
MF1_SET_WRITE_MODE = 4017
HF14A_GET_ANTI_COLL_DATA = 4018
EM410X_SET_EMU_ID = 5000
EM410X_GET_EMU_ID = 5001
@enum.unique
class SlotNumber(enum.IntEnum):
SLOT_1 = 1
SLOT_2 = 2
SLOT_3 = 3
SLOT_4 = 4
SLOT_5 = 5
SLOT_6 = 6
SLOT_7 = 7
SLOT_8 = 8
@staticmethod
def to_fw(index: int): # can be int or SlotNumber
# SlotNumber() will raise error for us if index not in slot range
return SlotNumber(index).value - 1
@staticmethod
def from_fw(index: int):
# SlotNumber() will raise error for us if index not in fw range
return SlotNumber(index + 1)
@enum.unique
class TagSenseType(enum.IntEnum):
# Unknown
UNDEFINED = 0
# 125 kHz
LF = 1
# 13.56 MHz
HF = 2
@enum.unique
class TagSpecificType(enum.IntEnum):
UNDEFINED = 0
# old HL/LF common types, slots using these ones need to be migrated first
OLD_EM410X = 1
OLD_MIFARE_Mini = 2
OLD_MIFARE_1024 = 3
OLD_MIFARE_2048 = 4
OLD_MIFARE_4096 = 5
OLD_NTAG_213 = 6
OLD_NTAG_215 = 7
OLD_NTAG_216 = 8
OLD_TAG_TYPES_END = 9
###### LF ######
#### ASK Tag-Talk-First 100 ####
# EM410x
EM410X = 100
# FDX-B
# securakey
# gallagher
# PAC/Stanley
# Presco
# Visa2000
# Viking
# Noralsy
# Jablotron
#### FSK Tag-Talk-First 200 ####
# HID Prox
# ioProx
# AWID
# Paradox
#### PSK Tag-Talk-First 300 ####
# Indala
# Keri
# NexWatch
#### Reader-Talk-First 400 ####
# T5577
# EM4x05/4x69
# EM4x50/4x70
# Hitag series
TAG_TYPES_LF_END = 999
###### HF ######
#### MIFARE Classic series 1000 ####
MIFARE_Mini = 1000
MIFARE_1024 = 1001
MIFARE_2048 = 1002
MIFARE_4096 = 1003
#### MFUL / NTAG series 1100 ####
NTAG_213 = 1100
NTAG_215 = 1101
NTAG_216 = 1102
#### MIFARE Plus series 1200 ####
#### DESFire series 1300 ####
#### ST25TA series 2000 ####
#### HF14A-4 series 3000 ####
@staticmethod
def list(exclude_meta=True):
return [t for t in TagSpecificType
if (t > TagSpecificType.OLD_TAG_TYPES_END and
t != TagSpecificType.TAG_TYPES_LF_END)
or not exclude_meta]
@staticmethod
def list_hf():
return [t for t in TagSpecificType.list()
if (t > TagSpecificType.TAG_TYPES_LF_END)]
@staticmethod
def list_lf():
return [t for t in TagSpecificType.list()
if (TagSpecificType.UNDEFINED < t < TagSpecificType.TAG_TYPES_LF_END)]
def __str__(self):
if self == TagSpecificType.UNDEFINED:
return "Undefined"
elif self == TagSpecificType.EM410X:
return "EM410X"
elif self == TagSpecificType.MIFARE_Mini:
return "Mifare Mini"
elif self == TagSpecificType.MIFARE_1024:
return "Mifare Classic 1k"
elif self == TagSpecificType.MIFARE_2048:
return "Mifare Classic 2k"
elif self == TagSpecificType.MIFARE_4096:
return "Mifare Classic 4k"
elif self == TagSpecificType.NTAG_213:
return "NTAG 213"
elif self == TagSpecificType.NTAG_215:
return "NTAG 215"
elif self == TagSpecificType.NTAG_216:
return "NTAG 216"
elif self < TagSpecificType.OLD_TAG_TYPES_END:
return "Old tag type, must be migrated! Upgrade fw!"
return "Invalid"
@enum.unique
class MifareClassicWriteMode(enum.IntEnum):
# Normal write
NORMAL = 0
# Send NACK to write attempts
DENIED = 1
# Acknowledge writes, but don't remember contents
DECEIVE = 2
# Store data to RAM, but not to ROM
SHADOW = 3
# Shadow requested, will be changed to SHADOW and stored to ROM
SHADOW_REQ = 4
@staticmethod
def list(exclude_meta=True):
return [m for m in MifareClassicWriteMode
if m != MifareClassicWriteMode.SHADOW_REQ
or not exclude_meta]
def __str__(self):
if self == MifareClassicWriteMode.NORMAL:
return "Normal"
elif self == MifareClassicWriteMode.DENIED:
return "Denied"
elif self == MifareClassicWriteMode.DECEIVE:
return "Deceive"
elif self == MifareClassicWriteMode.SHADOW:
return "Shadow"
elif self == MifareClassicWriteMode.SHADOW_REQ:
return "Shadow requested"
return "None"
@enum.unique
class MifareClassicPrngType(enum.IntEnum):
# the random number of the card response is fixed
STATIC = 0
# the random number of the card response is weak
WEAK = 1
# the random number of the card response is unpredictable
HARD = 2
def __str__(self):
if self == MifareClassicPrngType.STATIC:
return "Static"
elif self == MifareClassicPrngType.WEAK:
return "Weak"
elif self == MifareClassicPrngType.HARD:
return "Hard"
return "None"
@enum.unique
class MifareClassicDarksideStatus(enum.IntEnum):
OK = 0
# Darkside can't fix NT (PRNG is unpredictable)
CANT_FIX_NT = 1
# Darkside try to recover a default key
LUCKY_AUTH_OK = 2
# Darkside can't get tag response enc(nak)
NO_NAK_SENT = 3
# Darkside running, can't change tag
TAG_CHANGED = 4
def __str__(self):
if self == MifareClassicDarksideStatus.OK:
return "Success"
elif self == MifareClassicDarksideStatus.CANT_FIX_NT:
return "Cannot fix NT (unpredictable PRNG)"
elif self == MifareClassicDarksideStatus.LUCKY_AUTH_OK:
return "Try to recover a default key"
elif self == MifareClassicDarksideStatus.NO_NAK_SENT:
return "Cannot get tag response enc(nak)"
elif self == MifareClassicDarksideStatus.TAG_CHANGED:
return "Tag changed during attack"
return "None"
@enum.unique
class AnimationMode(enum.IntEnum):
FULL = 0
MINIMAL = 1
NONE = 2
def __str__(self):
if self == AnimationMode.FULL:
return "Full animation"
elif self == AnimationMode.MINIMAL:
return "Minimal animation"
elif self == AnimationMode.NONE:
return "No animation"
@enum.unique
class ButtonType(enum.IntEnum):
A = ord('A')
B = ord('B')
@enum.unique
class MfcKeyType(enum.IntEnum):
A = 0x60
B = 0x61
@enum.unique
class ButtonPressFunction(enum.IntEnum):
NONE = 0
NEXTSLOT = 1
PREVSLOT = 2
CLONE = 3
BATTERY = 4
def __str__(self):
if self == ButtonPressFunction.NONE:
return "No Function"
elif self == ButtonPressFunction.NEXTSLOT:
return "Select next slot"
elif self == ButtonPressFunction.PREVSLOT:
return "Select previous slot"
elif self == ButtonPressFunction.CLONE:
return "Read then simulate the ID/UID card number"
elif self == ButtonPressFunction.BATTERY:
return "Show Battery Level"
return "None"
class ChameleonCMD:
"""
Chameleon cmd function
+366
View File
@@ -0,0 +1,366 @@
import enum
@enum.unique
class Command(enum.IntEnum):
GET_APP_VERSION = 1000
CHANGE_DEVICE_MODE = 1001
GET_DEVICE_MODE = 1002
SET_ACTIVE_SLOT = 1003
SET_SLOT_TAG_TYPE = 1004
SET_SLOT_DATA_DEFAULT = 1005
SET_SLOT_ENABLE = 1006
SET_SLOT_TAG_NICK = 1007
GET_SLOT_TAG_NICK = 1008
SLOT_DATA_CONFIG_SAVE = 1009
ENTER_BOOTLOADER = 1010
GET_DEVICE_CHIP_ID = 1011
GET_DEVICE_ADDRESS = 1012
SAVE_SETTINGS = 1013
RESET_SETTINGS = 1014
SET_ANIMATION_MODE = 1015
GET_ANIMATION_MODE = 1016
GET_GIT_VERSION = 1017
GET_ACTIVE_SLOT = 1018
GET_SLOT_INFO = 1019
WIPE_FDS = 1020
DELETE_SLOT_TAG_NICK = 1021
GET_ENABLED_SLOTS = 1023
DELETE_SLOT_SENSE_TYPE = 1024
GET_BATTERY_INFO = 1025
GET_BUTTON_PRESS_CONFIG = 1026
SET_BUTTON_PRESS_CONFIG = 1027
GET_LONG_BUTTON_PRESS_CONFIG = 1028
SET_LONG_BUTTON_PRESS_CONFIG = 1029
SET_BLE_PAIRING_KEY = 1030
GET_BLE_PAIRING_KEY = 1031
DELETE_ALL_BLE_BONDS = 1032
GET_DEVICE_MODEL = 1033
# FIXME: implemented but unused in CLI commands
GET_DEVICE_SETTINGS = 1034
GET_DEVICE_CAPABILITIES = 1035
GET_BLE_PAIRING_ENABLE = 1036
SET_BLE_PAIRING_ENABLE = 1037
HF14A_SCAN = 2000
MF1_DETECT_SUPPORT = 2001
MF1_DETECT_PRNG = 2002
MF1_STATIC_NESTED_ACQUIRE = 2003
MF1_DARKSIDE_ACQUIRE = 2004
MF1_DETECT_NT_DIST = 2005
MF1_NESTED_ACQUIRE = 2006
MF1_AUTH_ONE_KEY_BLOCK = 2007
MF1_READ_ONE_BLOCK = 2008
MF1_WRITE_ONE_BLOCK = 2009
HF14A_RAW = 2010
EM410X_SCAN = 3000
EM410X_WRITE_TO_T55XX = 3001
MF1_WRITE_EMU_BLOCK_DATA = 4000
HF14A_SET_ANTI_COLL_DATA = 4001
MF1_SET_DETECTION_ENABLE = 4004
MF1_GET_DETECTION_COUNT = 4005
MF1_GET_DETECTION_LOG = 4006
# FIXME: not implemented
MF1_GET_DETECTION_ENABLE = 4007
MF1_READ_EMU_BLOCK_DATA = 4008
MF1_GET_EMULATOR_CONFIG = 4009
# FIXME: not implemented
MF1_GET_GEN1A_MODE = 4010
MF1_SET_GEN1A_MODE = 4011
# FIXME: not implemented
MF1_GET_GEN2_MODE = 4012
MF1_SET_GEN2_MODE = 4013
# FIXME: not implemented
MF1_GET_BLOCK_ANTI_COLL_MODE = 4014
MF1_SET_BLOCK_ANTI_COLL_MODE = 4015
# FIXME: not implemented
MF1_GET_WRITE_MODE = 4016
MF1_SET_WRITE_MODE = 4017
HF14A_GET_ANTI_COLL_DATA = 4018
EM410X_SET_EMU_ID = 5000
EM410X_GET_EMU_ID = 5001
@enum.unique
class SlotNumber(enum.IntEnum):
SLOT_1 = 1
SLOT_2 = 2
SLOT_3 = 3
SLOT_4 = 4
SLOT_5 = 5
SLOT_6 = 6
SLOT_7 = 7
SLOT_8 = 8
@staticmethod
def to_fw(index: int): # can be int or SlotNumber
# SlotNumber() will raise error for us if index not in slot range
return SlotNumber(index).value - 1
@staticmethod
def from_fw(index: int):
# SlotNumber() will raise error for us if index not in fw range
return SlotNumber(index + 1)
@enum.unique
class TagSenseType(enum.IntEnum):
# Unknown
UNDEFINED = 0
# 125 kHz
LF = 1
# 13.56 MHz
HF = 2
@enum.unique
class TagSpecificType(enum.IntEnum):
UNDEFINED = 0
# old HL/LF common types, slots using these ones need to be migrated first
OLD_EM410X = 1
OLD_MIFARE_Mini = 2
OLD_MIFARE_1024 = 3
OLD_MIFARE_2048 = 4
OLD_MIFARE_4096 = 5
OLD_NTAG_213 = 6
OLD_NTAG_215 = 7
OLD_NTAG_216 = 8
OLD_TAG_TYPES_END = 9
###### LF ######
#### ASK Tag-Talk-First 100 ####
# EM410x
EM410X = 100
# FDX-B
# securakey
# gallagher
# PAC/Stanley
# Presco
# Visa2000
# Viking
# Noralsy
# Jablotron
#### FSK Tag-Talk-First 200 ####
# HID Prox
# ioProx
# AWID
# Paradox
#### PSK Tag-Talk-First 300 ####
# Indala
# Keri
# NexWatch
#### Reader-Talk-First 400 ####
# T5577
# EM4x05/4x69
# EM4x50/4x70
# Hitag series
TAG_TYPES_LF_END = 999
###### HF ######
#### MIFARE Classic series 1000 ####
MIFARE_Mini = 1000
MIFARE_1024 = 1001
MIFARE_2048 = 1002
MIFARE_4096 = 1003
#### MFUL / NTAG series 1100 ####
NTAG_213 = 1100
NTAG_215 = 1101
NTAG_216 = 1102
#### MIFARE Plus series 1200 ####
#### DESFire series 1300 ####
#### ST25TA series 2000 ####
#### HF14A-4 series 3000 ####
@staticmethod
def list(exclude_meta=True):
return [t for t in TagSpecificType
if (t > TagSpecificType.OLD_TAG_TYPES_END and
t != TagSpecificType.TAG_TYPES_LF_END)
or not exclude_meta]
@staticmethod
def list_hf():
return [t for t in TagSpecificType.list()
if (t > TagSpecificType.TAG_TYPES_LF_END)]
@staticmethod
def list_lf():
return [t for t in TagSpecificType.list()
if (TagSpecificType.UNDEFINED < t < TagSpecificType.TAG_TYPES_LF_END)]
def __str__(self):
if self == TagSpecificType.UNDEFINED:
return "Undefined"
elif self == TagSpecificType.EM410X:
return "EM410X"
elif self == TagSpecificType.MIFARE_Mini:
return "Mifare Mini"
elif self == TagSpecificType.MIFARE_1024:
return "Mifare Classic 1k"
elif self == TagSpecificType.MIFARE_2048:
return "Mifare Classic 2k"
elif self == TagSpecificType.MIFARE_4096:
return "Mifare Classic 4k"
elif self == TagSpecificType.NTAG_213:
return "NTAG 213"
elif self == TagSpecificType.NTAG_215:
return "NTAG 215"
elif self == TagSpecificType.NTAG_216:
return "NTAG 216"
elif self < TagSpecificType.OLD_TAG_TYPES_END:
return "Old tag type, must be migrated! Upgrade fw!"
return "Invalid"
@enum.unique
class MifareClassicWriteMode(enum.IntEnum):
# Normal write
NORMAL = 0
# Send NACK to write attempts
DENIED = 1
# Acknowledge writes, but don't remember contents
DECEIVE = 2
# Store data to RAM, but not to ROM
SHADOW = 3
# Shadow requested, will be changed to SHADOW and stored to ROM
SHADOW_REQ = 4
@staticmethod
def list(exclude_meta=True):
return [m for m in MifareClassicWriteMode
if m != MifareClassicWriteMode.SHADOW_REQ
or not exclude_meta]
def __str__(self):
if self == MifareClassicWriteMode.NORMAL:
return "Normal"
elif self == MifareClassicWriteMode.DENIED:
return "Denied"
elif self == MifareClassicWriteMode.DECEIVE:
return "Deceive"
elif self == MifareClassicWriteMode.SHADOW:
return "Shadow"
elif self == MifareClassicWriteMode.SHADOW_REQ:
return "Shadow requested"
return "None"
@enum.unique
class MifareClassicPrngType(enum.IntEnum):
# the random number of the card response is fixed
STATIC = 0
# the random number of the card response is weak
WEAK = 1
# the random number of the card response is unpredictable
HARD = 2
def __str__(self):
if self == MifareClassicPrngType.STATIC:
return "Static"
elif self == MifareClassicPrngType.WEAK:
return "Weak"
elif self == MifareClassicPrngType.HARD:
return "Hard"
return "None"
@enum.unique
class MifareClassicDarksideStatus(enum.IntEnum):
OK = 0
# Darkside can't fix NT (PRNG is unpredictable)
CANT_FIX_NT = 1
# Darkside try to recover a default key
LUCKY_AUTH_OK = 2
# Darkside can't get tag response enc(nak)
NO_NAK_SENT = 3
# Darkside running, can't change tag
TAG_CHANGED = 4
def __str__(self):
if self == MifareClassicDarksideStatus.OK:
return "Success"
elif self == MifareClassicDarksideStatus.CANT_FIX_NT:
return "Cannot fix NT (unpredictable PRNG)"
elif self == MifareClassicDarksideStatus.LUCKY_AUTH_OK:
return "Try to recover a default key"
elif self == MifareClassicDarksideStatus.NO_NAK_SENT:
return "Cannot get tag response enc(nak)"
elif self == MifareClassicDarksideStatus.TAG_CHANGED:
return "Tag changed during attack"
return "None"
@enum.unique
class AnimationMode(enum.IntEnum):
FULL = 0
MINIMAL = 1
NONE = 2
def __str__(self):
if self == AnimationMode.FULL:
return "Full animation"
elif self == AnimationMode.MINIMAL:
return "Minimal animation"
elif self == AnimationMode.NONE:
return "No animation"
@enum.unique
class ButtonType(enum.IntEnum):
A = ord('A')
B = ord('B')
@enum.unique
class MfcKeyType(enum.IntEnum):
A = 0x60
B = 0x61
@enum.unique
class ButtonPressFunction(enum.IntEnum):
NONE = 0
NEXTSLOT = 1
PREVSLOT = 2
CLONE = 3
BATTERY = 4
def __str__(self):
if self == ButtonPressFunction.NONE:
return "No Function"
elif self == ButtonPressFunction.NEXTSLOT:
return "Select next slot"
elif self == ButtonPressFunction.PREVSLOT:
return "Select previous slot"
elif self == ButtonPressFunction.CLONE:
return "Read then simulate the ID/UID card number"
elif self == ButtonPressFunction.BATTERY:
return "Show Battery Level"
return "None"