mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-07-20 17:40:56 +00:00
Jablotron uses differential biphase (inverted) at RF/64, 64-bit frames:
bits 0-15: 0xFFFF preamble
bits 16-55: 40-bit data (5 bytes), bit 16 must be 0
bits 56-63: 8-bit checksum = (sum of data bytes) XOR 0x3A
Firmware:
- rfid/nfctag/lf/protocols/jablotron.c - encoder/decoder codec
- rfid/nfctag/lf/utils/diphase.c - inverted-biphase state machine
(shared util, reusable by other diphase protocols)
- rfid/reader/lf/lf_jablotron_data.c - GPIO-interval reader path
- app_cmd.c: JABLOTRON_SCAN, JABLOTRON_WRITE_TO_T55XX,
JABLOTRON_SET_EMU_ID, JABLOTRON_GET_EMU_ID
- tag_base_type.h: TAG_TYPE_JABLOTRON enum
- t55xx.h: T5577_JABLOTRON_CONFIG (DIPHASE modulation, RF/64)
- lf_tag_em.c: load callback, factory-default data, save callback
Python CLI (software/script/):
- lf jablotron read - scan a real tag
- lf jablotron write --id - clone onto T55xx
- lf jablotron econfig -s N --id - set emulator ID on a slot
- hw slot list shows Jablotron ID and decimal card number
Python test (software/script/tests/test_jablotron_modulator.py):
Pure-Python round-trip validator that reimplements the modulator and
diphase decoder, expands PWM entries to an edge stream, and confirms
the decoded data matches the input. Regression guard for both the
firmware's double-frame encoding and the single-frame variant.
Notable PWM design choices:
- Constant-level diphase encoding uses the same PAC pattern:
CC=0 for LOW, CC=counter_top+1 for HIGH. counter_top=31 gives
exactly 32 carrier cycles per half-bit at NRF_PWM_CLK_125kHz.
- The 64-bit frame is encoded twice in the 256-entry PWM buffer with
the internal level variable persisting between the two passes.
This is required for clean PWM looping: a single 64-bit diphase
frame with an odd number of zero bits ends at a level opposite the
starting level, leaving no transition at the loop boundary where
the reader expects one. Encoding twice guarantees a continuous
diphase stream regardless of the data's zero-count parity.
Reference: Proxmark3 cmdlfjablotron.c
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
669 lines
19 KiB
Python
669 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
|
|
|
|
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
|
|
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
|
|
JABLOTRON_SCAN = 3016
|
|
JABLOTRON_WRITE_TO_T55XX = 3017
|
|
ADC_GENERIC_READ = 3009
|
|
IOPROX_SCAN = 3010
|
|
IOPROX_WRITE_TO_T55XX = 3011
|
|
IOPROX_DECODE_RAW = 3012
|
|
IOPROX_COMPOSE_ID = 3013
|
|
|
|
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
|
|
|
|
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
|
|
JABLOTRON_SET_EMU_ID = 5010
|
|
JABLOTRON_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
|
|
Jablotron = 180
|
|
|
|
# FSK Tag-Talk-First 200
|
|
HIDProx = 200
|
|
ioProx = 201
|
|
# 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
|
|
MF0ICU1 = 1103
|
|
MF0ICU2 = 1104
|
|
MF0UL11 = 1105
|
|
MF0UL21 = 1106
|
|
NTAG_210 = 1107
|
|
NTAG_212 = 1108
|
|
# 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.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.Jablotron:
|
|
return "Jablotron"
|
|
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"
|