mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
Add hf mfu ercnt/ewcnt commands for reading and writing emulator's counters.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 },
|
||||
|
||||
@@ -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)
|
||||
//
|
||||
// ******************************************************************
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -1670,6 +1670,43 @@ class HFMFEConfig(SlotIndexArgsAndGoUnit, HF14AAntiCollArgsUnit, DeviceRequiredU
|
||||
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:
|
||||
|
||||
@@ -608,6 +608,26 @@ class ChameleonCMD:
|
||||
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''):
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user