Skip to content

Commit

Permalink
Implemented hf 14a raw
Browse files Browse the repository at this point in the history
  • Loading branch information
xianglin1998 committed Sep 21, 2023
1 parent 499fa35 commit 879b8e3
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 20 deletions.
99 changes: 80 additions & 19 deletions firmware/application/src/app_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,30 @@ static data_frame_tx_t *cmd_processor_mf1_detect_prng(uint16_t cmd, uint16_t sta
return data_frame_make(cmd, HF_TAG_OK, sizeof(type), &type);
}

// We have a reusable payload structure.
typedef struct {
uint8_t type_known;
uint8_t block_known;
uint8_t key_known[6];
uint8_t type_target;
uint8_t block_target;
} PACKED nested_common_payload_t;

static data_frame_tx_t *cmd_processor_mf1_static_nested_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
mf1_static_nested_core_t sncs;
if (length != sizeof(nested_common_payload_t)) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}

nested_common_payload_t *payload = (nested_common_payload_t *)data;
status = static_nested_recover_key(bytes_to_num(payload->key_known, 6), payload->block_known, payload->type_known, payload->block_target, payload->type_target, &sncs);
if (status != HF_TAG_OK) {
return data_frame_make(cmd, status, 0, NULL);
}
// mf1_static_nested_core_t is PACKED and comprises only bytes so we can use it directly
return data_frame_make(cmd, HF_TAG_OK, sizeof(sncs), (uint8_t *)(&sncs));
}

static data_frame_tx_t *cmd_processor_mf1_darkside_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if (length != 4) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
Expand Down Expand Up @@ -300,15 +324,6 @@ static data_frame_tx_t *cmd_processor_mf1_detect_nt_dist(uint16_t cmd, uint16_t
return data_frame_make(cmd, HF_TAG_OK, sizeof(payload_resp), (uint8_t *)&payload_resp);
}

// We have a reusable payload structure.
typedef struct {
uint8_t type_known;
uint8_t block_known;
uint8_t key_known[6];
uint8_t type_target;
uint8_t block_target;
} PACKED nested_common_payload_t;

static data_frame_tx_t *cmd_processor_mf1_nested_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
mf1_nested_core_t ncs[SETS_NR];
if (length != sizeof(nested_common_payload_t)) {
Expand Down Expand Up @@ -383,19 +398,64 @@ static data_frame_tx_t *cmd_processor_mf1_write_one_block(uint16_t cmd, uint16_t
return data_frame_make(cmd, status, 0, NULL);
}

static data_frame_tx_t *cmd_processor_mf1_static_nested_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
mf1_static_nested_core_t sncs;
if (length != sizeof(nested_common_payload_t)) {
static data_frame_tx_t *cmd_processor_hf14a_raw(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
// Response Buffer
uint8_t resp[DEF_FIFO_LENGTH] = { 0x00 };
uint16_t resp_length = 0;

typedef struct {
struct { // MSB -> LSB
uint8_t reserved : 1;

uint8_t check_response_crc : 1;
uint8_t keep_rf_field : 1;
uint8_t auto_select : 1;
uint8_t bit_frame : 1;
uint8_t append_crc : 1;
uint8_t wait_response : 1;
uint8_t open_rf_field : 1;
} options;

// U16NTOHS
uint16_t resp_timeout;
uint16_t data_length;

uint8_t data_buffer[0]; // We can have a lot of data or no data. struct just to compute offsets with min options.
} PACKED payload_t;
payload_t *payload = (payload_t *)data;
if (length < sizeof(payload_t)) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}

nested_common_payload_t *payload = (nested_common_payload_t *)data;
status = static_nested_recover_key(bytes_to_num(payload->key_known, 6), payload->block_known, payload->type_known, payload->block_target, payload->type_target, &sncs);
if (status != HF_TAG_OK) {
return data_frame_make(cmd, status, 0, NULL);
}
// mf1_static_nested_core_t is PACKED and comprises only bytes so we can use it directly
return data_frame_make(cmd, HF_TAG_OK, sizeof(sncs), (uint8_t *)(&sncs));
NRF_LOG_INFO("open_rf_field = %d", payload->options.open_rf_field);
NRF_LOG_INFO("wait_response = %d", payload->options.wait_response);
NRF_LOG_INFO("append_crc = %d", payload->options.append_crc);
NRF_LOG_INFO("bit_frame = %d", payload->options.bit_frame);
NRF_LOG_INFO("auto_select = %d", payload->options.auto_select);
NRF_LOG_INFO("keep_rf_field = %d", payload->options.keep_rf_field);
NRF_LOG_INFO("check_response_crc = %d", payload->options.check_response_crc);
NRF_LOG_INFO("reserved = %d", payload->options.reserved);

status = pcd_14a_reader_raw_cmd(
payload->options.open_rf_field,
payload->options.wait_response,
payload->options.append_crc,
payload->options.bit_frame,
payload->options.auto_select,
payload->options.keep_rf_field,
payload->options.check_response_crc,

U16NTOHS(payload->resp_timeout),

U16NTOHS(payload->data_length),
payload->data_buffer,

resp,
&resp_length,
U8ARR_BIT_LEN(resp)
);

return data_frame_make(cmd, status, resp_length, resp);
}

static data_frame_tx_t *cmd_processor_em410x_scan(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
Expand Down Expand Up @@ -937,6 +997,7 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_MF1_AUTH_ONE_KEY_BLOCK, before_hf_reader_run, cmd_processor_mf1_auth_one_key_block, after_hf_reader_run },
{ DATA_CMD_MF1_READ_ONE_BLOCK, before_hf_reader_run, cmd_processor_mf1_read_one_block, after_hf_reader_run },
{ DATA_CMD_MF1_WRITE_ONE_BLOCK, before_hf_reader_run, cmd_processor_mf1_write_one_block, after_hf_reader_run },
{ DATA_CMD_HF14A_RAW, before_reader_run, cmd_processor_hf14a_raw, NULL },

{ DATA_CMD_EM410X_SCAN, before_reader_run, cmd_processor_em410x_scan, NULL },
{ DATA_CMD_EM410X_WRITE_TO_T55XX, before_reader_run, cmd_processor_em410x_write_to_t55XX, NULL },
Expand Down
2 changes: 2 additions & 0 deletions firmware/application/src/data_cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
#define DATA_CMD_MF1_AUTH_ONE_KEY_BLOCK (2007)
#define DATA_CMD_MF1_READ_ONE_BLOCK (2008)
#define DATA_CMD_MF1_WRITE_ONE_BLOCK (2009)
#define DATA_CMD_HF14A_RAW (2010)

//
// ******************************************************************

Expand Down
59 changes: 59 additions & 0 deletions software/script/chameleon_cli_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1573,3 +1573,62 @@ def on_exec(self, args: argparse.Namespace):
else:
print(f" Status: {response.status:#02x}")
print(f" Data (HEX): {response.data.hex()}")


@hf_14a.command('raw', 'Send raw command')
class HF14ARaw(ReaderRequiredUnit):

def bool_to_bit(self, value):
return 1 if value else 0

def args_parser(self) -> ArgumentParserNoExit or None:
parser = ArgumentParserNoExit()
parser.add_argument('-r', '--response', help="do not read response", action='store_true', default=False,)
parser.add_argument('-c', '--crc', help="calculate and append CRC", action='store_true', default=False,)
parser.add_argument('-cc', '--crc-clear', help="Verify and clear CRC of received data", action='store_true', default=False,)
parser.add_argument('-k', '--keep-rf', help="keep signal field ON after receive", action='store_true', default=False,)
parser.add_argument('-o', '--open-rf', help="active signal field ON", action='store_true', default=False,)
parser.add_argument('-s', '--select-tag', help="Select the tag before executing the command", action='store_true', default=False,)
parser.add_argument('-b', '--bits', type=int, help="number of bits to send. Useful for send partial byte")
parser.add_argument('-t', '--timeout', type=int, help="timeout in ms", default=100)
parser.add_argument('-d', '--data', type=str, help="Data to be sent")
return parser

def on_exec(self, args: argparse.Namespace):
options = {
'open_rf_field': self.bool_to_bit(args.open_rf),
'wait_response': self.bool_to_bit(args.response == False),
'append_crc': self.bool_to_bit(args.crc),
'bit_frame': self.bool_to_bit(args.bits is not None),
'auto_select': self.bool_to_bit(args.select_tag),
'keep_rf_field': self.bool_to_bit(args.keep_rf),
'check_response_crc': self.bool_to_bit(args.crc_clear),
}
data: str = args.data
if data is not None:
data = data.replace(' ', '')
if re.match(r'^[0-9a-fA-F]+$', data):
if len(data) % 2 != 0:
print(f" [!] {CR}The length of the data must be an integer multiple of 2.{C0}")
return
else:
data_bytes = bytes.fromhex(data)
else:
print(f" [!] {CR}The data must be a HEX string{C0}")
return
else:
data_bytes = []
# Exec 14a raw cmd.
resp = self.cmd.hf14a_raw(options, args.timeout, data_bytes, args.bits)
if resp.status == chameleon_status.Device.HF_TAG_OK:
if len(resp.data) > 0:
print(
# print head
" - " +
# print data
' '.join([ hex(byte).replace('0x', '').rjust(2, '0') for byte in resp.data ])
)
else:
print(F" [*] {CY}No data response{C0}")
else:
print(f" [!] {CR}{chameleon_status.message[resp.status]}{C0} ")
99 changes: 98 additions & 1 deletion software/script/chameleon_cmd.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import enum
import struct
import ctypes

import chameleon_com
import chameleon_status
Expand Down Expand Up @@ -68,6 +69,7 @@
DATA_CMD_MF1_AUTH_ONE_KEY_BLOCK = 2007
DATA_CMD_MF1_READ_ONE_BLOCK = 2008
DATA_CMD_MF1_WRITE_ONE_BLOCK = 2009
DATA_CMD_HF14A_RAW = 2010

DATA_CMD_EM410X_SCAN = 3000
DATA_CMD_EM410X_WRITE_TO_T55XX = 3001
Expand Down Expand Up @@ -550,6 +552,52 @@ def mf1_write_one_block(self, block, type_value, key, block_data):
resp.data = resp.status == chameleon_status.Device.HF_TAG_OK
return resp

def hf14a_raw(self, options, resp_timeout_ms=100, data=[], bit_owned_by_the_last_byte=None):
"""
Send raw cmd to 14a tag
:param options:
:param resp_timeout_ms:
:param data:
:param bit_owned_by_the_last_byte:
:return:
"""

class CStruct(ctypes.BigEndianStructure):
_fields_ = [
("open_rf_field", ctypes.c_uint8, 1),
("wait_response", ctypes.c_uint8, 1),
("append_crc", ctypes.c_uint8, 1),
("bit_frame", ctypes.c_uint8, 1),
("auto_select", ctypes.c_uint8, 1),
("keep_rf_field", ctypes.c_uint8, 1),
("check_response_crc", ctypes.c_uint8, 1),
("reserved", ctypes.c_uint8, 1),
]

cs = CStruct()
cs.open_rf_field = options['open_rf_field']
cs.wait_response = options['wait_response']
cs.append_crc = options['append_crc']
cs.bit_frame = options['bit_frame']
cs.auto_select = options['auto_select']
cs.keep_rf_field = options['keep_rf_field']
cs.check_response_crc = options['check_response_crc']

if options['bit_frame'] == 1:
bits_or_bytes = len(data) * 8 # bits = bytes * 8(bit)
if bit_owned_by_the_last_byte is not None and bit_owned_by_the_last_byte != 8:
bits_or_bytes = bits_or_bytes - (8 - bit_owned_by_the_last_byte)
else:
bits_or_bytes = len(data) # bytes length

if len(data) > 0:
data = struct.pack(f'!BHH{len(data)}s', bytes(cs)[0], resp_timeout_ms, bits_or_bytes, bytearray(data))
else:
data = struct.pack(f'!BHH', bytes(cs)[0], resp_timeout_ms, 0)

return self.device.send_cmd_sync(DATA_CMD_HF14A_RAW, data, timeout=(resp_timeout_ms / 1000) + 1)


@expect_response(chameleon_status.Device.HF_TAG_OK)
def mf1_static_nested_acquire(self, block_known, type_known, key_known, block_target, type_target):
"""
Expand Down Expand Up @@ -1098,7 +1146,7 @@ def set_ble_pairing_enable(self, enabled: bool):
return self.device.send_cmd_sync(DATA_CMD_SET_BLE_PAIRING_ENABLE, data)


if __name__ == '__main__':
def test_fn():
# connect to chameleon
dev = chameleon_com.ChameleonCom()
dev.open("com19")
Expand All @@ -1108,9 +1156,58 @@ def set_ble_pairing_enable(self, enabled: bool):
chip = cml.get_device_chip_id()
print(f"Device chip id: {chip}")

# change to reader mode
cml.set_device_reader_mode()

options = {
'open_rf_field': 1,
'wait_response': 1,
'append_crc': 0,
'bit_frame': 1,
'auto_select': 0,
'keep_rf_field': 1,
'check_response_crc': 0,
}

# unlock 1
resp = cml.hf14a_raw(options=options, resp_timeout_ms=1000, data=[0x40], bit_owned_by_the_last_byte=7)

if resp.status == 0x00 and resp.data[0] == 0x0a:
print("Gen1A unlock 1 success")
# unlock 2
options['bit_frame'] = 0
resp = cml.hf14a_raw(options=options, resp_timeout_ms=1000, data=[0x43])
if resp.status == 0x00 and resp.data[0] == 0x0a:
print("Gen1A unlock 2 success")
print("Start dump gen1a memeory...")
block = 0
while block < 64:
# Tag read block cmd
cmd_read_gen1a_block = [0x30, block]

# Transfer with crc
options['append_crc'] = 1
options['check_response_crc'] = 1
resp = cml.hf14a_raw(options=options, resp_timeout_ms=100, data=cmd_read_gen1a_block)

print(f"Block {block} : {resp.data.hex()}")
block += 1

# Close rf field
options['keep_rf_field'] = 0
resp = cml.hf14a_raw(options=options)
else:
print("Gen1A unlock 2 fail")
else:
print("Gen1A unlock 1 fail")

# disconnect
dev.close()

# never exit
while True:
pass


if __name__ == '__main__':
test_fn()

0 comments on commit 879b8e3

Please sign in to comment.