Files
ChameleonUltra/software/script/chameleon_enum.py
T
matteoscrugli 729a7e56ea feat(cli): add lf idteck subgroup and extend lf clone with idteck type
Adds host-side CLI support for IDTECK:
- lf idteck econfig -s <slot> [--id <hex>]   set or read the emulated frame
- lf idteck write --id <hex>                 clone to a T55xx tag in reader mode
- lf clone -t idteck --id <hex>              same via the unified clone command
- hw slot list                                now renders Frame and Card ID
                                              for IDTECK slots

Input accepts 16 hex characters for the full 64-bit frame, or 8 hex
for the 32-bit payload (the fixed preamble 4944544B is auto-prepended).
A non-blocking informational note is emitted when the payload checksum
does not match the value computed from the card number, since some
readers validate this field and some do not.

Private helpers in chameleon_cli_unit.py (_idteck_compute_checksum,
_idteck_compose_frame, _idteck_frame_info) parse and compose IDTECK
frames and expose card-number-driven composition for a future
`lf idteck compose` command.
2026-05-15 10:09:51 +02:00

688 lines
19 KiB
Python

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
GET_ALL_SLOT_NICKS = 1038
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
GET_SLEEP_TIMEOUT = 1039
SET_SLEEP_TIMEOUT = 1040
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
HF14A_SCAN_KEEP = 2016
HF14A_AUTH_TRACE = 2017
MF1_MANIPULATE_VALUE_BLOCK = 2011
MF1_CHECK_KEYS_OF_SECTORS = 2012
MF1_HARDNESTED_ACQUIRE = 2013
MF1_ENC_NESTED_ACQUIRE = 2014
MF1_CHECK_KEYS_ON_BLOCK = 2015
HF14A_GET_CONFIG = 2200
HF14A_SET_CONFIG = 2201
HF14A_SNIFF = 2020
EM410X_SCAN = 3000
EM410X_WRITE_TO_T55XX = 3001
EM410X_ELECTRA_WRITE_TO_T55XX = 3006
HIDPROX_SCAN = 3002
HIDPROX_WRITE_TO_T55XX = 3003
VIKING_SCAN = 3004
VIKING_WRITE_TO_T55XX = 3005
PAC_SCAN = 3014
PAC_WRITE_TO_T55XX = 3015
ADC_GENERIC_READ = 3009
IOPROX_SCAN = 3010
IOPROX_WRITE_TO_T55XX = 3011
IOPROX_DECODE_RAW = 3012
IOPROX_COMPOSE_ID = 3013
LF_T55XX_WRITE = 3016
IDTECK_WRITE_TO_T55XX = 3017
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
MF0_NTAG_GET_UID_MAGIC_MODE = 4019
MF0_NTAG_SET_UID_MAGIC_MODE = 4020
MF0_NTAG_READ_EMU_PAGE_DATA = 4021
MF0_NTAG_WRITE_EMU_PAGE_DATA = 4022
MF0_NTAG_GET_VERSION_DATA = 4023
MF0_NTAG_SET_VERSION_DATA = 4024
MF0_NTAG_GET_SIGNATURE_DATA = 4025
MF0_NTAG_SET_SIGNATURE_DATA = 4026
MF0_NTAG_GET_COUNTER_DATA = 4027
MF0_NTAG_SET_COUNTER_DATA = 4028
MF0_NTAG_RESET_AUTH_CNT = 4029
MF0_NTAG_GET_PAGE_COUNT = 4030
MF0_NTAG_GET_WRITE_MODE = 4031
MF0_NTAG_SET_WRITE_MODE = 4032
MF0_NTAG_SET_DETECTION_ENABLE = 4033
MF0_NTAG_GET_DETECTION_COUNT = 4034
MF0_NTAG_GET_DETECTION_LOG = 4035
MF0_NTAG_GET_DETECTION_ENABLE = 4036
# FIXME: not implemented
MF0_NTAG_GET_EMULATOR_CONFIG = 4037
MF1_SET_FIELD_OFF_DO_RESET = 4038
MF1_GET_FIELD_OFF_DO_RESET = 4039
MF1_GET_PRNG_TYPE = 4040
MF1_SET_PRNG_TYPE = 4041
# ISO14443-4 T=CL emulation
HF14A_4_APDU_RECV = 6000
HF14A_4_APDU_SEND = 6001
HF14A_4_SET_ANTI_COLL = 6002
HF14A_4_STATIC_RESP = 6003
HF14A_4_READER_APDU = 6004
HF14A_4_EMV_SCAN = 6005
EM410X_SET_EMU_ID = 5000
EM410X_GET_EMU_ID = 5001
HIDPROX_SET_EMU_ID = 5002
HIDPROX_GET_EMU_ID = 5003
VIKING_SET_EMU_ID = 5004
VIKING_GET_EMU_ID = 5005
PAC_SET_EMU_ID = 5006
PAC_GET_EMU_ID = 5007
IOPROX_SET_EMU_ID = 5008
IOPROX_GET_EMU_ID = 5009
IDTECK_SET_EMU_ID = 5010
IDTECK_GET_EMU_ID = 5011
EM4X05_SCAN = 3030
EM4X05_READSNIFF = 3032
LF_SNIFF = 3031
@enum.unique
class Status(enum.IntEnum):
HF_TAG_OK = 0x00 # IC card operation is successful
HF_TAG_NO = 0x01 # IC card not found
HF_ERR_STAT = 0x02 # Abnormal IC card communication
HF_ERR_CRC = 0x03 # IC card communication verification abnormal
HF_COLLISION = 0x04 # IC card conflict
HF_ERR_BCC = 0x05 # IC card BCC error
MF_ERR_AUTH = 0x06 # MF card verification failed
HF_ERR_PARITY = 0x07 # IC card parity error
HF_ERR_ATS = 0x08 # ATS should be present but card NAKed, or ATS too large
# Some operations with low frequency cards succeeded!
LF_TAG_OK = 0x40
# Unable to search for a valid EM410X tag
LF_TAG_NO_FOUND = 0x41
# The parameters passed by the BLE instruction are wrong, or the parameters passed
# by calling some functions are wrong
PAR_ERR = 0x60
# The mode of the current device is wrong, and the corresponding API cannot be called
DEVICE_MODE_ERROR = 0x66
INVALID_CMD = 0x67
SUCCESS = 0x68
NOT_IMPLEMENTED = 0x69
FLASH_WRITE_FAIL = 0x70
FLASH_READ_FAIL = 0x71
INVALID_SLOT_TYPE = 0x72
def __str__(self):
if self == Status.HF_TAG_OK:
return "HF tag operation succeeded"
elif self == Status.HF_TAG_NO:
return "HF tag no found or lost"
elif self == Status.HF_ERR_STAT:
return "HF tag status error"
elif self == Status.HF_ERR_CRC:
return "HF tag data crc error"
elif self == Status.HF_COLLISION:
return "HF tag collision"
elif self == Status.HF_ERR_BCC:
return "HF tag uid bcc error"
elif self == Status.MF_ERR_AUTH:
return "HF tag auth fail"
elif self == Status.HF_ERR_PARITY:
return "HF tag data parity error"
elif self == Status.HF_ERR_ATS:
return "HF tag was supposed to send ATS but didn't"
elif self == Status.LF_TAG_OK:
return "LF tag operation succeeded"
elif self == Status.LF_TAG_NO_FOUND:
return "LF tag not found"
elif self == Status.PAR_ERR:
return "API request fail, param error"
elif self == Status.DEVICE_MODE_ERROR:
return "API request fail, device mode error"
elif self == Status.INVALID_CMD:
return "API request fail, cmd invalid"
elif self == Status.SUCCESS:
return "Device operation succeeded"
elif self == Status.NOT_IMPLEMENTED:
return "Some api not implemented"
elif self == Status.FLASH_WRITE_FAIL:
return "Flash write failed"
elif self == Status.FLASH_READ_FAIL:
return "Flash read failed"
elif self == Status.INVALID_SLOT_TYPE:
return "Invalid card type in slot"
return "Invalid status"
@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
EM410X_16 = 101
EM410X_32 = 102
EM410X_64 = 103
EM410X_ELECTRA = 104
# FDX-B
# securakey
# gallagher
# PAC/Stanley
PAC = 150
# Presco
# Visa2000
Viking = 170
# Noralsy
# Jablotron
# FSK Tag-Talk-First 200
HIDProx = 200
ioProx = 201
# AWID
# Paradox
# PSK Tag-Talk-First 300
# Indala
# Keri
# NexWatch
IDTECK = 310
# 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
MF0ICU1 = 1103
MF0ICU2 = 1104
MF0UL11 = 1105
MF0UL21 = 1106
NTAG_210 = 1107
NTAG_212 = 1108
# MIFARE Plus series 1200
# DESFire series 1300
# ST25TA series 2000
# ISO14443-4 T=CL emulation
HF14A_4 = 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.EM410X_16:
return "EM410X/16"
elif self == TagSpecificType.EM410X_32:
return "EM410X/32"
elif self == TagSpecificType.EM410X_64:
return "EM410X/64"
elif self == TagSpecificType.EM410X_ELECTRA:
return "EM410X Electra"
elif self == TagSpecificType.HIDProx:
return "HIDProx"
elif self == TagSpecificType.ioProx:
return "ioProx"
elif self == TagSpecificType.PAC:
return "PAC/Stanley"
elif self == TagSpecificType.Viking:
return "Viking"
elif self == TagSpecificType.IDTECK:
return "IDTECK"
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.MF0ICU1:
return "Mifare Ultralight"
elif self == TagSpecificType.MF0ICU2:
return "Mifare Ultralight C"
elif self == TagSpecificType.MF0UL11:
return "Mifare Ultralight EV1 (640 bit)"
elif self == TagSpecificType.MF0UL21:
return "Mifare Ultralight EV1 (1312 bit)"
elif self == TagSpecificType.NTAG_210:
return "NTAG 210"
elif self == TagSpecificType.NTAG_212:
return "NTAG 212"
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 MifareUltralightWriteMode(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 MifareUltralightWriteMode
if m != MifareUltralightWriteMode.SHADOW_REQ or not exclude_meta
]
def __str__(self):
if self == MifareUltralightWriteMode.NORMAL:
return "Normal"
elif self == MifareUltralightWriteMode.DENIED:
return "Denied"
elif self == MifareUltralightWriteMode.DECEIVE:
return "Deceive"
elif self == MifareUltralightWriteMode.SHADOW:
return "Shadow"
elif self == MifareUltralightWriteMode.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
SYMMETRIC = 3
NONE = 2
def __str__(self):
if self == AnimationMode.FULL:
return "Full animation"
elif self == AnimationMode.MINIMAL:
return "Minimal animation"
elif self == AnimationMode.SYMMETRIC:
return "Symmetric 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
FIELDGEN = 5
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"
elif self == ButtonPressFunction.FIELDGEN:
return "Toggle NFC Field Generator"
return "None"
@enum.unique
class MfcValueBlockOperator(enum.IntEnum):
DECREMENT = 0xC0
INCREMENT = 0xC1
RESTORE = 0xC2
@enum.unique
class HIDFormat(enum.IntEnum):
H10301 = 1
IND26 = 2
IND27 = 3
INDASC27 = 4
TECOM27 = 5
W2804 = 6
IND29 = 7
ATSW30 = 8
ADT31 = 9
HCP32 = 10
HPP32 = 11
KASTLE = 12
KANTECH = 13
WIE32 = 14
D10202 = 15
H10306 = 16
N10002 = 17
OPTUS34 = 18
SMP34 = 19
BQT34 = 20
C1K35S = 21
C15001 = 22
S12906 = 23
ACTPHID = 42
SIE36 = 24
H10320 = 25
H10302 = 26
H10304 = 27
P10004 = 28
HGEN37 = 29
MDI37 = 30
def __str__(self):
descriptions = {
HIDFormat.H10301: "HID H10301 26-bit",
HIDFormat.IND26: "Indala 26-bit",
HIDFormat.IND27: "Indala 27-bit",
HIDFormat.INDASC27: "Indala ASC 27-bit",
HIDFormat.TECOM27: "Tecom 27-bit",
HIDFormat.W2804: "2804 Wiegand 28-bit",
HIDFormat.IND29: "Indala 29-bit",
HIDFormat.ATSW30: "ATS Wiegand 30-bit",
HIDFormat.ADT31: "HID ADT 31-bit",
HIDFormat.HCP32: "HID Check Point 32-bit",
HIDFormat.HPP32: "HID Hewlett-Packard 32-bit",
HIDFormat.KASTLE: "Kastle 32-bit",
HIDFormat.KANTECH: "Indala/Kantech KFS 32-bit",
HIDFormat.WIE32: "Wiegand 32-bit",
HIDFormat.D10202: "HID D10202 33-bit",
HIDFormat.H10306: "HID H10306 34-bit",
HIDFormat.N10002: "Honeywell/Northern N10002 34-bit",
HIDFormat.OPTUS34: "Indala Optus 34-bit",
HIDFormat.SMP34: "Cardkey Smartpass 34-bit",
HIDFormat.BQT34: "BQT 34-bit",
HIDFormat.C1K35S: "HID Corporate 1000 35-bit Std",
HIDFormat.C15001: "HID KeyScan 36-bit",
HIDFormat.S12906: "HID Simplex 36-bit",
HIDFormat.ACTPHID: "HID ACTProx 36-bit",
HIDFormat.SIE36: "HID 36-bit Siemens",
HIDFormat.H10320: "HID H10320 37-bit BCD",
HIDFormat.H10302: "HID H10302 37-bit huge ID",
HIDFormat.H10304: "HID H10304 37-bit",
HIDFormat.P10004: "HID P10004 37-bit PCSC",
HIDFormat.HGEN37: "HID Generic 37-bit",
HIDFormat.MDI37: "PointGuard MDI 37-bit",
}
if self in descriptions:
return descriptions[self]
return "Invalid"