The new generation chameleon based on NRF52840 makes the performance of card emulation more stable. And gave the chameleon the ability to read, write, and decrypt cards.
This huge commit tries to enhance several things related to the fw/cli protocol.
Generally, the idea is to be verbose, explicit and reuse conventions, in order to enhance code maintainability and understandability for the other contributors.
docs/protocol.md got heavily updated
Many commands have been renamed for consistency. you are invited to adapt your client for easier maintenance
Guidelines, also written in docs/protocol.md "New data payloads: guidelines for developers":
Now protocol data exchanged over USB or BLE are defined in netdata.h as packed structs and values are stored in Network byte order (=Big Endian)
Command-specific payloads are defined in their respective cmd_processor handler in app_cmd.c and chameleon_cmd.py
Define C struct for cmd/resp data greater than a single byte, use and abuse of struct.pack/struct.unpack in Python. So one can understand the payload format at a simple glimpse.
If single byte of data to return, still use a 1-byte data, not status.
Use unambiguous types such as uint16_t, not int or enum. Cast explicitly int and enum to uint_t of proper size
Use Network byte order for 16b and 32b integers
Macros U16NTOHS, U32NTOHL must be used on reception of a command payload.
Macros U16HTONS, U32HTONL must be used on creation of a response payload.
In Python, use the modifier ! with all struct.pack/struct.unpack
Concentrate payload parsing in the handlers, avoid further parsing in their callers. This is true for the firmware and the client.
In cmd_processor handlers: don't reuse input length/data parameters for creating the response content
Avoid hardcoding offsets, use sizeof(), offsetof(struct, field) in C and struct.calcsize() in Python
Use the exact same command and fields names in firmware and in client, use function names matching the command names for their handlers unless there is a very good reason not to do so. This helps grepping around. Names must start with a letter, not a number, because some languages require it (e.g. 14a_scan not possible in Python)
Respect commands order in m_data_cmd_map, data_cmd.h and chameleon_cmd.py definitions
Even if a command is not yet implemented in firmware or in client but a command number is allocated, add it to data_cmd.h and chameleon_cmd.py with some FIXME: to be implemented comment
Validate data before using it, both when receiving command data in the firmware and when receiving response data in the client.
Validate response status in client.
Disruptive changes:
GET_DEVICE_CAPABILITIES: list of cmds in data are now really Big Endian
Note: the initial attempt to use macros PP_HTONS were actually considering wrongly that the platform was Big Endian (BYTE_ORDER was actually undefined) while it is actually Little Endian.
GET_APP_VERSION: response is now a tuple of bytes: major|minor (previously it was in reversed order as a single uint16_t in Little Endian)
SET_SLOT_TAG_TYPE: tag_type now on 2 bytes, to use updated tag_specific_type_t enum
SET_SLOT_DATA_DEFAULT: tag_type now on 2 bytes, to use updated tag_specific_type_t enum
GET_SLOT_INFO: tag_type now on 2 bytes, to use updated tag_specific_type_t enum
GET_DEVICE_CHIP_ID: now returns its 64b ID following Network byte order (previously, bytes were in the reverse order)
GET_DEVICE_ADDRESS: now returns its 56b address following Network byte order (previously, bytes were in the reverse order). CLI does not reverse the response anymore so it displays the same value as before.
MF1_GET_DETECTION_COUNT: now returns its 32b value following Network byte order (previously Little Endian)
GET_GIT_VERSION response status is now STATUS_DEVICE_SUCCESS
GET_DEVICE_MODEL response status is now STATUS_DEVICE_SUCCESS
MF1_READ_EMU_BLOCK_DATA response status is now STATUS_DEVICE_SUCCESS
GET_DEVICE_CAPABILITIES response status is now STATUS_DEVICE_SUCCESS
HF14A_SCAN: entirely new response format, room for ATS and multiple tags
MF1_DETECT_SUPPORT response status is HF_TAG_OK if MF1 else an error (HF_TAG_NO if the tag didn't reply to auth)
MF1_DETECT_PRNG response status is now HF_TAG_OK and prng_type is returned in 1 byte of data with a new enum mf1_prng_type_t == MifareClassicPrngType
MF1_STATIC_NESTED_ACQUIRE was added
MF1_DETECT_DARKSIDE was removed
MF1_DARKSIDE_ACQUIRE response status is now HF_TAG_OK and darkside_status is returned in 1 byte of data. If OK, followed by 32 bytes as previously
MF1_GET_ANTI_COLL_DATA: in case slot does not contain anticoll data, instead of STATUS_PAR_ERR, now it returns STATUS_DEVICE_SUCCESS with empty data
MF1_SET_ANTI_COLL_DATA and MF1_GET_ANTI_COLL_DATA now use the same data format as HF14A_SCAN
GET_ENABLED_SLOTS: returns twice more bytes as now there is a enabled_hf and a enabled_lf
SET_SLOT_ENABLE: requires sense_type to tell which of HL or HF should be enabled
HF14A_RAW: new command
DATA_CMD_GET_DEVICE_MODEL now matches chameleon_device_type_t
MF1_GET_WRITE_MODE must handle new SHADOW_REQ indicating a move to SHADOW on the news data save
For clients to detect Ultra/Lite with older firmwares, one can issue the GET_APP_VERSION and urge the user to flash his device if needed.
On older firmwares, it will return a status=b'\x00' and data=b'\x00\x01' while up-to-date firmwares will return status=STATUS_DEVICE_SUCCESS and data greater or equal to b'\x01\x00' (v1.0).
Other changes: cf CHANGELOG, and probably a few small changes I forgot about..
This huge commit tries to enhance several things related to the fw/cli protocol. Generally, the idea is to be verbose, explicit and reuse conventions, in order to enhance code maintainability and understandability for the other contributors.
docs/protocol.md got heavily updated
Many commands have been renamed for consistency. you are invited to adapt your client for easier maintenance
Guidelines, also written in docs/protocol.md "New data payloads: guidelines for developers":
struct
for cmd/resp data greater than a single byte, use and abuse ofstruct.pack
/struct.unpack
in Python. So one can understand the payload format at a simple glimpse.data
, notstatus
.uint16_t
, notint
orenum
. Cast explicitlyint
andenum
touint_t
of proper sizeU16NTOHS
,U32NTOHL
must be used on reception of a command payload.U16HTONS
,U32HTONL
must be used on creation of a response payload.!
with allstruct.pack
/struct.unpack
length
/data
parameters for creating the response contentsizeof()
,offsetof(struct, field)
in C andstruct.calcsize()
in Python14a_scan
not possible in Python)m_data_cmd_map
,data_cmd.h
andchameleon_cmd.py
definitionsdata_cmd.h
andchameleon_cmd.py
with someFIXME: to be implemented
commentDisruptive changes:
For clients to detect Ultra/Lite with older firmwares, one can issue the GET_APP_VERSION and urge the user to flash his device if needed. On older firmwares, it will return a status=b'\x00' and data=b'\x00\x01' while up-to-date firmwares will return status=STATUS_DEVICE_SUCCESS and data greater or equal to b'\x01\x00' (v1.0).
Other changes: cf CHANGELOG, and probably a few small changes I forgot about..