feat(pm3py): project scaffolding with CRC-16/A and protocol constants
This commit is contained in:
1
pm3py/__init__.py
Normal file
1
pm3py/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""pm3py - Python wire protocol library for Proxmark3."""
|
||||
BIN
pm3py/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
pm3py/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
pm3py/__pycache__/protocol.cpython-312.pyc
Normal file
BIN
pm3py/__pycache__/protocol.cpython-312.pyc
Normal file
Binary file not shown.
261
pm3py/protocol.py
Normal file
261
pm3py/protocol.py
Normal file
@@ -0,0 +1,261 @@
|
||||
"""Proxmark3 NG wire protocol constants and CRC."""
|
||||
import struct
|
||||
from enum import IntEnum
|
||||
|
||||
# --- Magic values ---
|
||||
CMD_PREAMBLE_MAGIC = 0x61334d50 # "PM3a"
|
||||
RESP_PREAMBLE_MAGIC = 0x62334d50 # "PM3b"
|
||||
CMD_POSTAMBLE_NOCRC = 0x3361 # "a3"
|
||||
RESP_POSTAMBLE_NOCRC = 0x3362 # "b3"
|
||||
|
||||
PM3_CMD_DATA_SIZE = 512
|
||||
|
||||
# --- Frame formats (little-endian) ---
|
||||
CMD_PREAMBLE_FMT = "<IHH" # magic(4) + length_ng(2) + cmd(2) = 8 bytes
|
||||
CMD_PREAMBLE_SIZE = 8
|
||||
CMD_POSTAMBLE_FMT = "<H" # crc(2) = 2 bytes
|
||||
CMD_POSTAMBLE_SIZE = 2
|
||||
|
||||
RESP_PREAMBLE_FMT = "<IHbbH" # magic(4) + length_ng(2) + status(1) + reason(1) + cmd(2) = 10 bytes
|
||||
RESP_PREAMBLE_SIZE = 10
|
||||
RESP_POSTAMBLE_FMT = "<H"
|
||||
RESP_POSTAMBLE_SIZE = 2
|
||||
|
||||
# --- MIX frame: 3x uint64 args at start of payload ---
|
||||
MIX_ARGS_FMT = "<QQQ"
|
||||
MIX_ARGS_SIZE = 24
|
||||
|
||||
# --- CRC-16/A (ISO 14443-3) ---
|
||||
_CRC_TABLE: list[int] = []
|
||||
|
||||
def _reflect8(b: int) -> int:
|
||||
r = 0
|
||||
for i in range(8):
|
||||
if b & (1 << i):
|
||||
r |= 1 << (7 - i)
|
||||
return r
|
||||
|
||||
def _reflect16(v: int) -> int:
|
||||
r = 0
|
||||
for i in range(16):
|
||||
if v & (1 << i):
|
||||
r |= 1 << (15 - i)
|
||||
return r
|
||||
|
||||
def _init_crc_table() -> None:
|
||||
global _CRC_TABLE
|
||||
if _CRC_TABLE:
|
||||
return
|
||||
poly = 0x1021
|
||||
_CRC_TABLE = [0] * 256
|
||||
for i in range(256):
|
||||
c = _reflect8(i) << 8
|
||||
crc = 0
|
||||
for _ in range(8):
|
||||
if (crc ^ c) & 0x8000:
|
||||
crc = ((crc << 1) ^ poly) & 0xFFFF
|
||||
else:
|
||||
crc = (crc << 1) & 0xFFFF
|
||||
c = (c << 1) & 0xFFFF
|
||||
_CRC_TABLE[i] = _reflect16(crc)
|
||||
|
||||
def crc16_a(data: bytes) -> int:
|
||||
"""CRC-16/A: poly=0x1021, init=0xC6C6, refin=true, refout=true, xorout=0x0000."""
|
||||
_init_crc_table()
|
||||
crc = _reflect16(0xC6C6)
|
||||
for b in data:
|
||||
crc = ((crc >> 8) ^ _CRC_TABLE[(crc & 0xFF) ^ b]) & 0xFFFF
|
||||
return crc
|
||||
|
||||
|
||||
# --- Error codes ---
|
||||
class PM3Status(IntEnum):
|
||||
SUCCESS = 0
|
||||
EUNDEF = -1
|
||||
EINVARG = -2
|
||||
EDEVNOTSUPP = -3
|
||||
ETIMEOUT = -4
|
||||
EOPABORTED = -5
|
||||
ENOTIMPL = -6
|
||||
ERFTRANS = -7
|
||||
EIO = -8
|
||||
EOVFLOW = -9
|
||||
ESOFT = -10
|
||||
EFLASH = -11
|
||||
EMALLOC = -12
|
||||
EFILE = -13
|
||||
ENOTTY = -14
|
||||
EINIT = -15
|
||||
EWRONGANSWER = -16
|
||||
EOUTOFBOUND = -17
|
||||
ECARDEXCHANGE = -18
|
||||
EAPDU_ENCODEFAIL = -19
|
||||
EAPDU_FAIL = -20
|
||||
EFAILED = -21
|
||||
EPARTIAL = -22
|
||||
ETEAROFF = -23
|
||||
ECRC = -24
|
||||
ESTATIC_NONCE = -25
|
||||
ENOPACS = -26
|
||||
ELENGTH = -27
|
||||
ENOKEY = -28
|
||||
ECRYPTO = -29
|
||||
ENOFILE = -30
|
||||
ENODATA = -98
|
||||
EFATAL = -99
|
||||
|
||||
|
||||
# --- Command IDs ---
|
||||
class Cmd(IntEnum):
|
||||
# Bootloader
|
||||
DEVICE_INFO = 0x0000
|
||||
FINISH_WRITE = 0x0003
|
||||
HARDWARE_RESET = 0x0004
|
||||
START_FLASH = 0x0005
|
||||
CHIP_INFO = 0x0006
|
||||
BL_VERSION = 0x0007
|
||||
|
||||
# Debug
|
||||
DEBUG_PRINT_STRING = 0x0100
|
||||
DEBUG_PRINT_INTEGERS = 0x0101
|
||||
DEBUG_PRINT_BYTES = 0x0102
|
||||
|
||||
# System
|
||||
NACK = 0x00FE
|
||||
ACK = 0x00FF
|
||||
BUFF_CLEAR = 0x0105
|
||||
READ_MEM = 0x0106
|
||||
VERSION = 0x0107
|
||||
STATUS = 0x0108
|
||||
PING = 0x0109
|
||||
READ_MEM_DOWNLOAD = 0x010A
|
||||
READ_MEM_DOWNLOADED = 0x010B
|
||||
DOWNLOAD_EML_BIGBUF = 0x0110
|
||||
DOWNLOADED_EML_BIGBUF = 0x0111
|
||||
CAPABILITIES = 0x0112
|
||||
QUIT_SESSION = 0x0113
|
||||
SET_DBGMODE = 0x0114
|
||||
STANDALONE = 0x0115
|
||||
WTX = 0x0116
|
||||
TIA = 0x0117
|
||||
BREAK_LOOP = 0x0118
|
||||
SET_TEAROFF = 0x0119
|
||||
LED_CONTROL = 0x011A
|
||||
GET_DBGMODE = 0x0120
|
||||
|
||||
# FPGA
|
||||
FPGA_MAJOR_MODE_OFF = 0x0500
|
||||
|
||||
# LF
|
||||
LF_TI_READ = 0x0202
|
||||
LF_TI_WRITE = 0x0203
|
||||
LF_ACQ_RAW_ADC = 0x0205
|
||||
LF_MOD_THEN_ACQ_RAW_ADC = 0x0206
|
||||
DOWNLOAD_BIGBUF = 0x0207
|
||||
DOWNLOADED_BIGBUF = 0x0208
|
||||
LF_UPLOAD_SIM_SAMPLES = 0x0209
|
||||
LF_SIMULATE = 0x020A
|
||||
LF_HID_WATCH = 0x020B
|
||||
LF_HID_SIMULATE = 0x020C
|
||||
LF_SET_DIVISOR = 0x020D
|
||||
LF_SIMULATE_BIDIR = 0x020E
|
||||
SET_ADC_MUX = 0x020F
|
||||
LF_HID_CLONE = 0x0210
|
||||
LF_EM410X_CLONE = 0x0211
|
||||
LF_T55XX_READBL = 0x0214
|
||||
LF_T55XX_WRITEBL = 0x0215
|
||||
LF_T55XX_RESET_READ = 0x0216
|
||||
LF_PCF7931_READ = 0x0217
|
||||
LF_EM4X_READWORD = 0x0218
|
||||
LF_EM4X_WRITEWORD = 0x0219
|
||||
LF_IO_WATCH = 0x021A
|
||||
LF_EM4X_PROTECTWORD = 0x021B
|
||||
LF_EM410X_WATCH = 0x021C
|
||||
LF_SAMPLING_SET_CONFIG = 0x021D
|
||||
LF_FSK_SIMULATE = 0x021E
|
||||
LF_ASK_SIMULATE = 0x021F
|
||||
LF_PSK_SIMULATE = 0x0220
|
||||
LF_AWID_WATCH = 0x0221
|
||||
LF_VIKING_CLONE = 0x0222
|
||||
LF_PCF7931_WRITE = 0x0223
|
||||
LF_T55XX_WAKEUP = 0x0224
|
||||
LF_COTAG_READ = 0x0225
|
||||
LF_T55XX_SET_CONFIG = 0x0226
|
||||
LF_SAMPLING_PRINT_CONFIG = 0x0227
|
||||
LF_SAMPLING_GET_CONFIG = 0x0228
|
||||
LF_EM4X_LOGIN = 0x0229
|
||||
LF_EM4X_BF = 0x022A
|
||||
LF_T55XX_CHK_PWDS = 0x0230
|
||||
LF_T55XX_DANGERRAW = 0x0231
|
||||
LF_NRZ_SIMULATE = 0x0232
|
||||
LF_SNIFF_RAW_ADC = 0x0360
|
||||
LF_HITAG_SNIFF = 0x0370
|
||||
|
||||
# HF General
|
||||
HF_ACQ_RAW_ADC = 0x0301
|
||||
HF_ISO15693_ACQ_RAW_ADC = 0x0300
|
||||
HF_ISO15693_READER = 0x0310
|
||||
HF_ISO15693_SIMULATE = 0x0311
|
||||
HF_ISO15693_SNIFF = 0x0312
|
||||
HF_ISO15693_COMMAND = 0x0313
|
||||
HF_ISO15693_FINDAFI = 0x0315
|
||||
HF_ISO15693_CSETUID = 0x0316
|
||||
|
||||
HF_ISO14443A_SNIFF = 0x0383
|
||||
HF_ISO14443A_SIMULATE = 0x0384
|
||||
HF_ISO14443A_READER = 0x0385
|
||||
HF_ISO14443A_GET_CONFIG = 0x03B1
|
||||
HF_ISO14443A_SET_CONFIG = 0x03B2
|
||||
|
||||
HF_ISO14443B_COMMAND = 0x0305
|
||||
HF_ISO14443B_SIMULATE = 0x0381
|
||||
HF_ISO14443B_SNIFF = 0x0382
|
||||
|
||||
# Tuning
|
||||
MEASURE_ANTENNA_TUNING = 0x0400
|
||||
MEASURE_ANTENNA_TUNING_HF = 0x0401
|
||||
MEASURE_ANTENNA_TUNING_LF = 0x0402
|
||||
LISTEN_READER_FIELD = 0x0420
|
||||
HF_DROPFIELD = 0x0430
|
||||
HF_DECAY = 0x0440
|
||||
|
||||
# MIFARE Classic
|
||||
HF_MIFARE_EML_MEMCLR = 0x0601
|
||||
HF_MIFARE_EML_MEMSET = 0x0602
|
||||
HF_MIFARE_EML_MEMGET = 0x0603
|
||||
HF_MIFARE_SIMULATE = 0x0610
|
||||
HF_MIFARE_READER = 0x0611
|
||||
HF_MIFARE_NESTED = 0x0612
|
||||
HF_MIFARE_ACQ_ENCRYPTED_NONCES = 0x0613
|
||||
HF_MIFARE_ACQ_NONCES = 0x0614
|
||||
HF_MIFARE_STATIC_NESTED = 0x0615
|
||||
HF_MIFARE_READBL = 0x0620
|
||||
HF_MIFARE_READSC = 0x0621
|
||||
HF_MIFARE_WRITEBL = 0x0622
|
||||
HF_MIFARE_CHKKEYS = 0x0623
|
||||
HF_MIFARE_SETMOD = 0x0624
|
||||
HF_MIFARE_CHKKEYS_FAST = 0x0625
|
||||
HF_MIFARE_CHKKEYS_FILE = 0x0626
|
||||
HF_MIFARE_VALUE = 0x0627
|
||||
HF_MIFARE_READBL_EX = 0x0628
|
||||
HF_MIFARE_WRITEBL_EX = 0x0629
|
||||
HF_MIFARE_SNIFF = 0x0630
|
||||
HF_MIFARE_MFKEY = 0x0631
|
||||
HF_MIFARE_PERSONALIZE_UID = 0x0632
|
||||
HF_MIFARE_CSETBL = 0x0605
|
||||
HF_MIFARE_CGETBL = 0x0606
|
||||
HF_MIFARE_CIDENT = 0x0607
|
||||
|
||||
# MIFARE Ultralight
|
||||
HF_MIFAREU_READBL = 0x0720
|
||||
HF_MIFAREU_READCARD = 0x0721
|
||||
HF_MIFAREU_WRITEBL = 0x0722
|
||||
HF_MIFARE_NACK_DETECT = 0x0730
|
||||
|
||||
# HF Sniff/Plot
|
||||
HF_SNIFF = 0x0800
|
||||
HF_PLOT = 0x0801
|
||||
FPGAMEM_DOWNLOAD = 0x0802
|
||||
FPGAMEM_DOWNLOADED = 0x0803
|
||||
|
||||
UNKNOWN = 0xFFFF
|
||||
6
pyproject.toml
Normal file
6
pyproject.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.backends._legacy:_Backend"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
11
setup.py
Normal file
11
setup.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="pm3py",
|
||||
version="0.1.0",
|
||||
packages=find_packages(),
|
||||
install_requires=[
|
||||
"pyserial-asyncio>=0.6",
|
||||
],
|
||||
python_requires=">=3.10",
|
||||
)
|
||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
BIN
tests/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
tests/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tests/__pycache__/test_crc.cpython-312-pytest-9.0.1.pyc
Normal file
BIN
tests/__pycache__/test_crc.cpython-312-pytest-9.0.1.pyc
Normal file
Binary file not shown.
17
tests/test_crc.py
Normal file
17
tests/test_crc.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from pm3py.protocol import crc16_a
|
||||
|
||||
def test_crc16_a_empty():
|
||||
assert crc16_a(b"") == 0x6363 # init 0xC6C6 reflected = 0x6363
|
||||
|
||||
def test_crc16_a_known_vector():
|
||||
# Verified against C implementation: compute_crc(CRC_14443_A, ...)
|
||||
# "123456789" is the standard CRC check string
|
||||
data = b"123456789"
|
||||
assert crc16_a(data) == 0xBF05
|
||||
|
||||
def test_crc16_a_ping_preamble():
|
||||
# A CMD_PING preamble: magic=0x61334d50, length|ng=0x8020, cmd=0x0109
|
||||
preamble = bytes.fromhex("504d3361208001090102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
|
||||
crc = crc16_a(preamble)
|
||||
assert isinstance(crc, int)
|
||||
assert 0 <= crc <= 0xFFFF
|
||||
Reference in New Issue
Block a user