Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...

## [unreleased][unreleased]
- Add per-slot fuzzing-mode setting to randomize data at each read for MF
- Added command to check keys of multiple sectors at once (@taichunmin)
- Fixed unused target key type parameter for nested (@petepriority)
- Skip already used items `hf mf elog --decrypt` (@p-l-)
Expand Down
20 changes: 18 additions & 2 deletions firmware/application/src/app_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,19 @@ static data_frame_tx_t *cmd_processor_mf1_get_detection_log(uint16_t cmd, uint16
return data_frame_make(cmd, STATUS_SUCCESS, length, resp);
}

static data_frame_tx_t *cmd_processor_mf1_set_mode_fuzzing(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if (length != 1 || data[0] > 1) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
nfc_tag_mf1_set_mode_fuzzing(data[0]);
return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
}

static data_frame_tx_t *cmd_processor_mf1_get_mode_fuzzing(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
uint8_t is_fuzzing = nfc_tag_mf1_is_mode_fuzzing();
return data_frame_make(cmd, STATUS_SUCCESS, 1, (uint8_t *)(&is_fuzzing));
}

static data_frame_tx_t *cmd_processor_mf1_write_emu_block_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if (length == 0 || (((length - 1) % NFC_TAG_MF1_DATA_SIZE) != 0)) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
Expand Down Expand Up @@ -880,13 +893,14 @@ static data_frame_tx_t *cmd_processor_delete_slot_tag_nick(uint16_t cmd, uint16_
}

static data_frame_tx_t *cmd_processor_mf1_get_emulator_config(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
uint8_t mf1_info[5] = {};
uint8_t mf1_info[6] = {};
mf1_info[0] = nfc_tag_mf1_is_detection_enable();
mf1_info[1] = nfc_tag_mf1_is_gen1a_magic_mode();
mf1_info[2] = nfc_tag_mf1_is_gen2_magic_mode();
mf1_info[3] = nfc_tag_mf1_is_use_mf1_coll_res();
mf1_info[4] = nfc_tag_mf1_get_write_mode();
return data_frame_make(cmd, STATUS_SUCCESS, 5, mf1_info);
mf1_info[5] = nfc_tag_mf1_is_mode_fuzzing();
return data_frame_make(cmd, STATUS_SUCCESS, 6, mf1_info);
}

static data_frame_tx_t *cmd_processor_mf1_get_gen1a_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
Expand Down Expand Up @@ -1109,6 +1123,8 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_MF1_GET_WRITE_MODE, NULL, cmd_processor_mf1_get_write_mode, NULL },
{ DATA_CMD_MF1_SET_WRITE_MODE, NULL, cmd_processor_mf1_set_write_mode, NULL },
{ DATA_CMD_HF14A_GET_ANTI_COLL_DATA, NULL, cmd_processor_hf14a_get_anti_coll_data, NULL },
{ DATA_CMD_MF1_SET_MODE_FUZZING, NULL, cmd_processor_mf1_set_mode_fuzzing, NULL },
{ DATA_CMD_MF1_GET_MODE_FUZZING, NULL, cmd_processor_mf1_get_mode_fuzzing, 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 @@ -106,6 +106,8 @@
#define DATA_CMD_MF1_GET_WRITE_MODE (4016)
#define DATA_CMD_MF1_SET_WRITE_MODE (4017)
#define DATA_CMD_HF14A_GET_ANTI_COLL_DATA (4018)
#define DATA_CMD_MF1_SET_MODE_FUZZING (4019)
#define DATA_CMD_MF1_GET_MODE_FUZZING (4020)
//
// ******************************************************************

Expand Down
17 changes: 17 additions & 0 deletions firmware/application/src/rfid/nfctag/hf/nfc_mf1.c
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,12 @@ void nfc_tag_mf1_state_handler(uint8_t *p_data, uint16_t szDataBits) {
memcpy(respTrailerInfo->key_b, m_tag_trailer_info->key_b, 6);
}
} else {
/* If tag is marked as fuzzer: randomize data for next read */
if (m_tag_information->config.mode_fuzzing) {
for (uint8_t i = 0; i < 16; i++) {
m_tag_information->memory[CurrentAddress][i] = (uint8_t) rand();
}
}
// For data, just return to the corresponding location sector
memcpy(m_tag_tx_buffer.tx_raw_buffer, m_tag_information->memory[CurrentAddress], 16);
}
Expand Down Expand Up @@ -1157,6 +1163,7 @@ bool nfc_tag_mf1_data_factory(uint8_t slot, tag_specific_type_t tag_type) {
p_mf1_information->config.use_mf1_coll_res = false;
p_mf1_information->config.mode_block_write = NFC_TAG_MF1_WRITE_NORMAL;
p_mf1_information->config.detection_enable = false;
p_mf1_information->config.mode_fuzzing = false;

// save data to flash
tag_sense_type_t sense_type = get_sense_type_from_tag_type(tag_type);
Expand Down Expand Up @@ -1193,6 +1200,16 @@ uint32_t nfc_tag_mf1_detection_log_count(void) {
return m_auth_log.count;
}

// Settling whether mode fuzzing is on or off
void nfc_tag_mf1_set_mode_fuzzing(bool fuzzing) {
m_tag_information->config.mode_fuzzing = fuzzing;
}

// Whether mode fuzzing is on or off
bool nfc_tag_mf1_is_mode_fuzzing(void) {
return m_tag_information->config.mode_fuzzing;
}

// Set gen1a magic mode
void nfc_tag_mf1_set_gen1a_magic_mode(bool enable) {
m_tag_information->config.mode_gen1a_magic = enable;
Expand Down
6 changes: 5 additions & 1 deletion firmware/application/src/rfid/nfctag/hf/nfc_mf1.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ typedef struct {
uint8_t detection_enable: 1;
// Allow to write block 0 (CUID/gen2 mode)
uint8_t mode_gen2_magic: 1;
// Change content of tag each time it has been read
uint8_t mode_fuzzing: 1;
// reserve
uint8_t reserved1: 4;
uint8_t reserved1: 3;
uint8_t reserved2;
uint8_t reserved3;
} nfc_tag_mf1_configure_t;
Expand Down Expand Up @@ -147,6 +149,8 @@ void nfc_tag_mf1_set_detection_enable(bool enable);
bool nfc_tag_mf1_is_detection_enable(void);
void nfc_tag_mf1_detection_log_clear(void);
uint32_t nfc_tag_mf1_detection_log_count(void);
void nfc_tag_mf1_set_mode_fuzzing(bool fuzzing);
bool nfc_tag_mf1_is_mode_fuzzing(void);
nfc_tag_14a_coll_res_reference_t *get_mifare_coll_res(void);
nfc_tag_14a_coll_res_reference_t *get_saved_mifare_coll_res(void);
void nfc_tag_mf1_set_gen1a_magic_mode(bool enable);
Expand Down
58 changes: 58 additions & 0 deletions software/script/chameleon_cli_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,41 @@ def on_exec(self, args: argparse.Namespace):
return


@hf_mf.command('fuzz')
class HFMFFuzz(ReaderRequiredUnit):

key_re = re.compile(r"^(?P<idx>([0-9]|1[0-5])):(?P<ab>(A|B)):(?P<key>([0-9A-Fa-f]{12}))$")

def args_parse_key(self, arg):
m = self.key_re.match(arg)
if not m:
raise argparse.ArgumentTypeError("Expected format: <sector>:<A|B>:<key> where <sector> is between 0-15 and <key> is 6 bytes long")
return (int(m.group('idx')), m.group('ab'), bytes.fromhex(m.group('key')))

def args_parser(self) -> ArgumentParserNoExit:
parser = ArgumentParserNoExit()
parser.description = 'Mifare Classic fuzzer tag'
parser.add_argument('-k', '--key', type=self.args_parse_key, required=False, action="extend", nargs="+",
help="Key to use for a given sector")
return parser

def on_exec(self, args: argparse.Namespace):
keys = [{'a': b'\xff\xff\xff\xff\xff\xff', 'b': b'\x00\x00\x00\x00\x00\x00'} for _ in range(16)]
for keyarg in args.key:
keys[keyarg[0]][keyarg[1].lower()] = keyarg[2]
# generate and write blocks to emulate
for block_idx in range(64):
# key block
if block_idx % 4 == 3:
data = keys[block_idx // 4]['a'] + b'\xff' * 4 + keys[block_idx // 4]['b']
else:
data = b'\x00' * 16
self.cmd.mf1_write_emu_block_data(block_idx, data)
# set fuzzer mode
self.cmd.mf1_set_mode_fuzzing(True)
return


@hf_mf.command('fchk')
class HFMFFCHK(ReaderRequiredUnit):
def args_parser(self) -> ArgumentParserNoExit:
Expand Down Expand Up @@ -1444,6 +1479,8 @@ def args_parser(self) -> ArgumentParserNoExit:
log_group = parser.add_mutually_exclusive_group()
log_group.add_argument('--enable-log', action='store_true', help="Enable logging of MFC authentication data")
log_group.add_argument('--disable-log', action='store_true', help="Disable logging of MFC authentication data")
log_group.add_argument('--enable-fuzzing', action='store_true', help="Enable fuzzing mode for slot (i.e. changing data at each read)")
log_group.add_argument('--disable-fuzzing', action='store_true', help="Disable fuzzing mode for slot (i.e. changing data at each read)")
return parser

def on_exec(self, args: argparse.Namespace):
Expand Down Expand Up @@ -1473,6 +1510,7 @@ def on_exec(self, args: argparse.Namespace):
block_anti_coll_mode = mfc_config["block_anti_coll_mode"]
write_mode = MifareClassicWriteMode(mfc_config["write_mode"])
detection = mfc_config["detection"]
fuzzing = mfc_config["fuzzing"]
change_requested, change_done, uid, atqa, sak, ats = self.update_hf14a_anticoll(args, uid, atqa, sak, ats)
if args.enable_gen1a:
change_requested = True
Expand Down Expand Up @@ -1547,6 +1585,22 @@ def on_exec(self, args: argparse.Namespace):
change_done = True
else:
print(f'{CY}Requested logging of MFC authentication data already disabled{C0}')
if args.enable_fuzzing:
change_requested = True
if not fuzzing:
fuzzing = True
self.cmd.mf1_set_mode_fuzzing(fuzzing)
change_done = True
else:
print(f'{CY}Requested fuzzing mode of MFC authentication data already enabled{C0}')
elif args.disable_fuzzing:
change_requested = True
if fuzzing:
fuzzing = False
self.cmd.mf1_set_mode_fuzzing(fuzzing)
change_done = True
else:
print(f'{CY}Requested logging of MFC authentication data already disabled{C0}')

if change_done:
print(' - MF1 Emulator settings updated')
Expand All @@ -1571,6 +1625,8 @@ def on_exec(self, args: argparse.Namespace):
print(f'- {"Write mode:":40}{CR}invalid value!{C0}')
print(
f'- {"Log (mfkey32) mode:":40}{f"{CG}enabled{C0}" if detection else f"{CR}disabled{C0}"}')
print(
f'- {"Fuzzing mode:":40}{f"{CG}enabled{C0}" if fuzzing else f"{CR}disabled{C0}"}')


@hf_mfu.command('rdpg')
Expand Down Expand Up @@ -1822,6 +1878,8 @@ def on_exec(self, args: argparse.Namespace):
print(
f' {"Log (mfkey32) mode:":40}'
f'{f"{CG}enabled{C0}" if config["detection"] else f"{CR}disabled{C0}"}')
print(
f' {"Fuzzing mode:":40}{f"{CG}enabled{C0}" if config["fuzzing"] else f"{CR}disabled{C0}"}')

# LF
field_length = maxnamelength+slotnames[fwslot]["lf"]["metalen"]+1
Expand Down
17 changes: 15 additions & 2 deletions software/script/chameleon_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,17 @@ def mf1_get_detection_log(self, index: int):
resp.parsed = result_list
return resp

@expect_response(Status.SUCCESS)
def mf1_set_mode_fuzzing(self, fuzzing: bool):
"""
Set whether to enable the detection of the current card slot.

:param enable: Whether to enable
:return:
"""
data = struct.pack('!B', fuzzing)
return self.device.send_cmd_sync(Command.MF1_SET_MODE_FUZZING, data)

@expect_response(Status.SUCCESS)
def mf1_write_emu_block_data(self, block_start: int, block_data: bytes):
"""
Expand Down Expand Up @@ -641,17 +652,19 @@ def mf1_get_emulator_config(self):
[2] - mf1_is_gen2_magic_mode
[3] - mf1_is_use_mf1_coll_res (use UID/BCC/SAK/ATQA from 0 block)
[4] - mf1_get_write_mode
[5] - mf1_is_mode_fuzzing

:return:
"""
resp = self.device.send_cmd_sync(Command.MF1_GET_EMULATOR_CONFIG)
if resp.status == Status.SUCCESS:
b1, b2, b3, b4, b5 = struct.unpack('!????B', resp.data)
b1, b2, b3, b4, b5, b6 = struct.unpack('!?????B', resp.data)
resp.parsed = {'detection': b1,
'gen1a_mode': b2,
'gen2_mode': b3,
'block_anti_coll_mode': b4,
'write_mode': b5}
'write_mode': b5,
'fuzzing': b6}
return resp

@expect_response(Status.SUCCESS)
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 @@ -95,6 +95,8 @@ class Command(enum.IntEnum):
MF1_GET_WRITE_MODE = 4016
MF1_SET_WRITE_MODE = 4017
HF14A_GET_ANTI_COLL_DATA = 4018
MF1_SET_MODE_FUZZING = 4019
MF1_GET_MODE_FUZZING = 4020

EM410X_SET_EMU_ID = 5000
EM410X_GET_EMU_ID = 5001
Expand Down