Skip to content

Commit

Permalink
Add hf mfu ercnt/ewcnt commands for reading and writing emulator's …
Browse files Browse the repository at this point in the history
…counters.
  • Loading branch information
turbocool3r committed Jul 9, 2024
1 parent ff58d97 commit 0ce920c
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 20 deletions.
8 changes: 8 additions & 0 deletions docs/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,14 @@ Notes:
* Command: 32 signature data bytes.
* Response: no data
* CLI: cf `hf mfu econfig --set-signature <hex>`
### 4027: MF0_NTAG_GET_COUNTER_DATA
* Command: 1 byte for the counter index
* Response: 3 bytes for the counter value (big-endian) + 1 byte for tearing where `0xBD` means tearing flag is not set.
* CLI: cf `hf mfu ercnt`
### 4028: MF0_NTAG_SET_COUNTER_DATA
* Command: 1 byte where the lower 7 bits are the counter index and the top bit indicates whether tearing event flag should be reset + 3 bytes of the counter value (big-endian).
* Response: no data
* CLI: cf `hf mfu ewcnt`
### 5000: EM410X_SET_EMU_ID
* Command: 5 bytes. `id[5]`. ID as 5 bytes.
* Response: no data
Expand Down
36 changes: 36 additions & 0 deletions firmware/application/src/app_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,40 @@ static data_frame_tx_t *cmd_processor_mf0_ntag_set_signature_data(uint16_t cmd,
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
}

static data_frame_tx_t *cmd_processor_mf0_ntag_get_counter_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if (length != 1) return data_frame_make(cmd, STATUS_INVALID_PARAMS, 0, NULL);

uint8_t index = data[0] & 0x7F;
uint8_t *counter_data = nfc_tag_mf0_ntag_get_counter_data_by_index(index);
if (counter_data == NULL) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, NULL);

bool tearing = (counter_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] & MF0_NTAG_AUTHLIM_MASK_IN_CTR) != 0;

uint8_t response[4];
memcpy(response, counter_data, 3);
response[3] = tearing ? 0x00 : 0xBD;

return data_frame_make(cmd, STATUS_SUCCESS, 4, response);
}

static data_frame_tx_t *cmd_processor_mf0_ntag_set_counter_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if (length != 4) return data_frame_make(cmd, STATUS_INVALID_PARAMS, 0, NULL);

uint8_t index = data[0] & 0x7F;
uint8_t *counter_data = nfc_tag_mf0_ntag_get_counter_data_by_index(index);
if (counter_data == NULL) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, NULL);

// clear tearing event flag
if ((data[0] & 0x80) == 0x80) {
counter_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] &= ~MF0_NTAG_TEARING_MASK_IN_AUTHLIM;
}

// copy the actual counter value
memcpy(counter_data, &data[1], 3);

return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
}

static data_frame_tx_t *cmd_processor_hf14a_set_anti_coll_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
// uidlen[1]|uid[uidlen]|atqa[2]|sak[1]|atslen[1]|ats[atslen]
// dynamic length, so no struct
Expand Down Expand Up @@ -1238,6 +1272,8 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_MF0_NTAG_SET_VERSION_DATA, NULL, cmd_processor_mf0_ntag_set_version_data, NULL },
{ DATA_CMD_MF0_NTAG_GET_SIGNATURE_DATA, NULL, cmd_processor_mf0_ntag_get_signature_data, NULL },
{ DATA_CMD_MF0_NTAG_SET_SIGNATURE_DATA, NULL, cmd_processor_mf0_ntag_set_signature_data, NULL },
{ DATA_CMD_MF0_NTAG_GET_COUNTER_DATA, NULL, cmd_processor_mf0_ntag_get_counter_data, NULL },
{ DATA_CMD_MF0_NTAG_SET_COUNTER_DATA, NULL, cmd_processor_mf0_ntag_set_counter_data, NULL },

{ DATA_CMD_EM410X_SET_EMU_ID, NULL, cmd_processor_em410x_set_emu_id, NULL },
{ DATA_CMD_EM410X_GET_EMU_ID, NULL, cmd_processor_em410x_get_emu_id, 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 @@ -114,6 +114,8 @@
#define DATA_CMD_MF0_NTAG_SET_VERSION_DATA (4024)
#define DATA_CMD_MF0_NTAG_GET_SIGNATURE_DATA (4025)
#define DATA_CMD_MF0_NTAG_SET_SIGNATURE_DATA (4026)
#define DATA_CMD_MF0_NTAG_GET_COUNTER_DATA (4027)
#define DATA_CMD_MF0_NTAG_SET_COUNTER_DATA (4028)
//
// ******************************************************************

Expand Down
32 changes: 12 additions & 20 deletions firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,6 @@ NRF_LOG_MODULE_REGISTER();
#define SIGNATURE_LENGTH 32
#define PAGES_PER_VERSION (NFC_TAG_MF0_NTAG_VER_SIZE / NFC_TAG_MF0_NTAG_DATA_SIZE)

// Since all counters are 24-bit and each currently supported tag that supports counters
// has password authentication we store the auth attempts counter in the last bit of the
// first counter. AUTHLIM is only 3 bits though so we reserve 4 bits just to be sure and
// use the top bit for tearing event flag.
#define AUTHLIM_OFF_IN_CTR 3
#define AUTHLIM_MASK_IN_CTR 0xF
#define TEARING_MASK_IN_AUTHLIM 0x80

// Values for MIRROR_CONF
#define MIRROR_CONF_DISABLED 0
#define MIRROR_CONF_UID 1
Expand Down Expand Up @@ -403,7 +395,7 @@ static int get_user_data_end_by_tag_type(tag_specific_type_t type) {
return nr_pages;
}

static uint8_t *get_counter_data_by_index(uint8_t index) {
uint8_t *nfc_tag_mf0_ntag_get_counter_data_by_index(uint8_t index) {
uint8_t ctr_page_off;
uint8_t ctr_page_end;
switch (m_tag_type) {
Expand Down Expand Up @@ -489,12 +481,12 @@ static void handle_any_read(uint8_t block_num, uint8_t block_cnt, uint8_t block_
bytes2hex(m_tag_information->res_coll.uid, (char *)mirror_buf, 7);
break;
case MIRROR_CONF_CNT:
bytes2hex(get_counter_data_by_index(0), (char *)mirror_buf, 3);
bytes2hex(nfc_tag_mf0_ntag_get_counter_data_by_index(0), (char *)mirror_buf, 3);
break;
case MIRROR_CONF_UID_CNT:
bytes2hex(m_tag_information->res_coll.uid, (char *)mirror_buf, 7);
mirror_buf[7] = 'x';
bytes2hex(get_counter_data_by_index(0), (char *)&mirror_buf[8], 3);
bytes2hex(nfc_tag_mf0_ntag_get_counter_data_by_index(0), (char *)&mirror_buf[8], 3);
break;
}
}
Expand Down Expand Up @@ -546,7 +538,7 @@ static void handle_any_read(uint8_t block_num, uint8_t block_cnt, uint8_t block_
int access = m_tag_information->memory[first_cfg_page + CONF_ACCESS_PAGE_OFFSET][CONF_ACCESS_BYTE];

if ((access & CONF_ACCESS_NFC_CNT_EN) != 0) {
uint8_t *ctr = get_counter_data_by_index(0);
uint8_t *ctr = nfc_tag_mf0_ntag_get_counter_data_by_index(0);
uint32_t counter = (((uint32_t)ctr[0]) << 16) | (((uint32_t)ctr[1]) << 8) | ((uint32_t)ctr[2]);
if (counter < 0xFFFFFF) counter += 1;
ctr[0] = (uint8_t)(counter >> 16);
Expand Down Expand Up @@ -755,7 +747,7 @@ static void handle_read_cnt_command(uint8_t index) {
}
}

uint8_t *cnt_data = get_counter_data_by_index(index);
uint8_t *cnt_data = nfc_tag_mf0_ntag_get_counter_data_by_index(index);
if (cnt_data == NULL) {
nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4);
return;
Expand Down Expand Up @@ -794,7 +786,7 @@ static void handle_incr_cnt_command(uint8_t block_num, uint8_t *p_data) {

if ((0xFFFFFF - cnt) < incr_value) {
// set tearing event flag
cnt_data[AUTHLIM_OFF_IN_CTR] |= TEARING_MASK_IN_AUTHLIM;
cnt_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] |= MF0_NTAG_TEARING_MASK_IN_AUTHLIM;

nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4);
} else {
Expand All @@ -810,14 +802,14 @@ static void handle_incr_cnt_command(uint8_t block_num, uint8_t *p_data) {

static void handle_pwd_auth_command(uint8_t *p_data) {
int first_cfg_page = get_first_cfg_page_by_tag_type(m_tag_type);
uint8_t *cnt_data = get_counter_data_by_index(0);
uint8_t *cnt_data = nfc_tag_mf0_ntag_get_counter_data_by_index(0);
if (first_cfg_page == 0 || cnt_data == NULL) {
nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4);
return;
}

// check AUTHLIM counter
uint8_t auth_cnt = cnt_data[AUTHLIM_OFF_IN_CTR] & AUTHLIM_MASK_IN_CTR;
uint8_t auth_cnt = cnt_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] & MF0_NTAG_AUTHLIM_MASK_IN_CTR;
uint8_t auth_lim = m_tag_information->memory[first_cfg_page + 1][0] & CONF_ACCESS_AUTHLIM_MASK;
if ((auth_lim > 0) && (auth_lim <= auth_cnt)) {
nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4);
Expand All @@ -827,13 +819,13 @@ static void handle_pwd_auth_command(uint8_t *p_data) {
uint32_t pwd = *(uint32_t *)m_tag_information->memory[first_cfg_page + CONF_PWD_PAGE_OFFSET];
uint32_t supplied_pwd = *(uint32_t *)&p_data[1];
if (pwd != supplied_pwd) {
if (auth_lim) cnt_data[AUTHLIM_OFF_IN_CTR] |= (auth_cnt + 1) & AUTHLIM_MASK_IN_CTR;
if (auth_lim) cnt_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] |= (auth_cnt + 1) & MF0_NTAG_AUTHLIM_MASK_IN_CTR;
nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4);
return;
}

// reset authentication attempts counter and authenticate user
cnt_data[AUTHLIM_OFF_IN_CTR] = 0;
cnt_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] = 0;
m_tag_authenticated = true; // TODO: this should be possible to reset somehow

// Send the PACK value back
Expand All @@ -844,10 +836,10 @@ static void handle_check_tearing_event(int index) {
switch (m_tag_type) {
case TAG_TYPE_MF0UL11:
case TAG_TYPE_MF0UL21: {
uint8_t *ctr_data = get_counter_data_by_index(index);
uint8_t *ctr_data = nfc_tag_mf0_ntag_get_counter_data_by_index(index);

if (ctr_data) {
m_tag_tx_buffer.tx_buffer[0] = (ctr_data[AUTHLIM_OFF_IN_CTR] & TEARING_MASK_IN_AUTHLIM) == 0 ? 0xBD : 0x00;
m_tag_tx_buffer.tx_buffer[0] = (ctr_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] & MF0_NTAG_TEARING_MASK_IN_AUTHLIM) == 0 ? 0xBD : 0x00;
nfc_tag_14a_tx_bytes(m_tag_tx_buffer.tx_buffer, 1, true);
} else {
nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4);
Expand Down
9 changes: 9 additions & 0 deletions firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@
#define NFC_TAG_NTAG_FRAME_SIZE 64
#define NFC_TAG_NTAG_BLOCK_MAX NTAG216_TOTAL_PAGES

// Since all counters are 24-bit and each currently supported tag that supports counters
// has password authentication we store the auth attempts counter in the last bit of the
// first counter. AUTHLIM is only 3 bits though so we reserve 4 bits just to be sure and
// use the top bit for tearing event flag.
#define MF0_NTAG_AUTHLIM_OFF_IN_CTR 3
#define MF0_NTAG_AUTHLIM_MASK_IN_CTR 0xF
#define MF0_NTAG_TEARING_MASK_IN_AUTHLIM 0x80

typedef struct {
uint8_t mode_uid_magic: 1;
// reserve
Expand All @@ -60,6 +68,7 @@ int nfc_tag_mf0_ntag_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *bu
int nfc_tag_mf0_ntag_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer);
bool nfc_tag_mf0_ntag_data_factory(uint8_t slot, tag_specific_type_t tag_type);
int nfc_tag_mf0_ntag_get_nr_pages_by_tag_type(tag_specific_type_t tag_type);
uint8_t *nfc_tag_mf0_ntag_get_counter_data_by_index(uint8_t index);
uint8_t *nfc_tag_mf0_ntag_get_version_data(void);
uint8_t *nfc_tag_mf0_ntag_get_signature_data(void);

Expand Down
37 changes: 37 additions & 0 deletions software/script/chameleon_cli_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,43 @@ def on_exec(self, args: argparse.Namespace):
f'- {"Log (mfkey32) mode:":40}{f"{CG}enabled{C0}" if detection else f"{CR}disabled{C0}"}')


@hf_mfu.command('ercnt')
class HFMFUVERSION(ReaderRequiredUnit):
def args_parser(self) -> ArgumentParserNoExit:
parser = ArgumentParserNoExit()
parser.description = 'Read MIFARE Ultralight / NTAG counter value.'
parser.add_argument('-c', '--counter', type=int, required=True, help="Counter index.")
return parser

def on_exec(self, args: argparse.Namespace):
value, no_tearing = self.cmd.mfu_read_emu_counter_data(args.counter)
print(f" - Value: {value:06x}")
if no_tearing:
print(f" - Tearing: {CG}not set{C0}")
else:
print(f" - Tearing: {CR}set{C0}")


@hf_mfu.command('ewcnt')
class HFMFUVERSION(ReaderRequiredUnit):
def args_parser(self) -> ArgumentParserNoExit:
parser = ArgumentParserNoExit()
parser.description = 'Read MIFARE Ultralight / NTAG counter value.'
parser.add_argument('-c', '--counter', type=int, required=True, help="Counter index.")
parser.add_argument('-v', '--value', type=int, required=True, help="Counter value (24-bit).")
parser.add_argument('-t', '--reset-tearing', action='store_true', help="Reset tearing event flag.")
return parser

def on_exec(self, args: argparse.Namespace):
if args.value > 0xFFFFFF:
print(f"{CR}Counter value {args.value:#x} is too large.{C0}")
return

self.cmd.mfu_write_emu_counter_data(args.counter, args.value, args.reset_tearing)

print('- Ok')


@hf_mfu.command('rdpg')
class HFMFURDPG(MFUAuthArgsUnit):
def args_parser(self) -> ArgumentParserNoExit:
Expand Down
20 changes: 20 additions & 0 deletions software/script/chameleon_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,26 @@ def mfu_write_emu_page_data(self, page_start: int, data: bytes):
resp = self.device.send_cmd_sync(Command.MF0_NTAG_WRITE_EMU_PAGE_DATA, data)
return resp

@expect_response(Status.SUCCESS)
def mfu_read_emu_counter_data(self, index: int) -> (int, bool):
"""
Gets data for selected counter
"""
data = struct.pack('!B', index)
resp = self.device.send_cmd_sync(Command.MF0_NTAG_GET_COUNTER_DATA, data)
if resp.status == Status.SUCCESS:
resp.parsed = (((resp.data[0] << 16) | (resp.data[1] << 8) | resp.data[2]), resp.data[3] == 0xBD)
return resp

@expect_response(Status.SUCCESS)
def mfu_write_emu_counter_data(self, index: int, value: int, reset_tearing: bool):
"""
Sets data for selected counter
"""
data = struct.pack('!BBBB', index | (int(reset_tearing) << 7), (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF)
resp = self.device.send_cmd_sync(Command.MF0_NTAG_SET_COUNTER_DATA, data)
return resp

@expect_response(Status.SUCCESS)
def hf14a_set_anti_coll_data(self, uid: bytes, atqa: bytes, sak: bytes, ats: bytes = b''):
"""
Expand Down
2 changes: 2 additions & 0 deletions software/script/chameleon_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ class Command(enum.IntEnum):
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

EM410X_SET_EMU_ID = 5000
EM410X_GET_EMU_ID = 5001
Expand Down

0 comments on commit 0ce920c

Please sign in to comment.