Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions firmware/application/src/app_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1258,19 +1258,30 @@ data_frame_tx_t *cmd_processor_get_device_capabilities(uint16_t cmd, uint16_t st

static data_frame_tx_t *cmd_processor_mf0_ntag_get_uid_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
int rc = nfc_tag_mf0_ntag_get_uid_mode();

if (rc < 0) return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
else {
uint8_t res = rc;
return data_frame_make(cmd, STATUS_SUCCESS, 1, &res);
uint8_t mode = rc;
return data_frame_make(cmd, STATUS_SUCCESS, 1, &mode);
}
}

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

static data_frame_tx_t *cmd_processor_mf0_ntag_get_write_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
uint8_t mode = nfc_tag_mf0_ntag_get_write_mode();
return data_frame_make(cmd, STATUS_SUCCESS, 1, &mode);
}

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

Expand Down Expand Up @@ -1334,7 +1345,7 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_MF1_MANIPULATE_VALUE_BLOCK, before_hf_reader_run, cmd_processor_mf1_manipulate_value_block, after_hf_reader_run },
{ DATA_CMD_MF1_CHECK_KEYS_OF_SECTORS, before_hf_reader_run, cmd_processor_mf1_check_keys_of_sectors, after_hf_reader_run },
{ DATA_CMD_MF1_HARDNESTED_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_hardnested_nonces_acquire, after_hf_reader_run },

{ 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 Expand Up @@ -1370,7 +1381,8 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_MF0_NTAG_SET_COUNTER_DATA, NULL, cmd_processor_mf0_ntag_set_counter_data, NULL },
{ DATA_CMD_MF0_NTAG_RESET_AUTH_CNT, NULL, cmd_processor_mf0_ntag_reset_auth_cnt, NULL },
{ DATA_CMD_MF0_NTAG_GET_PAGE_COUNT, NULL, cmd_processor_mf0_ntag_get_emu_page_count, NULL },

{ DATA_CMD_MF0_NTAG_GET_WRITE_MODE, NULL, cmd_processor_mf0_ntag_get_write_mode, NULL },
{ DATA_CMD_MF0_NTAG_SET_WRITE_MODE, NULL, cmd_processor_mf0_ntag_set_write_mode, 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
4 changes: 4 additions & 0 deletions firmware/application/src/data_cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@
#define DATA_CMD_MF0_NTAG_SET_COUNTER_DATA (4028)
#define DATA_CMD_MF0_NTAG_RESET_AUTH_CNT (4029)
#define DATA_CMD_MF0_NTAG_GET_PAGE_COUNT (4030)
#define DATA_CMD_MF0_NTAG_RESET_AUTH_CNT (4029)
#define DATA_CMD_MF0_NTAG_GET_PAGE_COUNT (4030)
#define DATA_CMD_MF0_NTAG_GET_WRITE_MODE (4031)
#define DATA_CMD_MF0_NTAG_SET_WRITE_MODE (4032)
//
// ******************************************************************

Expand Down
38 changes: 38 additions & 0 deletions firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.c
Original file line number Diff line number Diff line change
Expand Up @@ -789,9 +789,21 @@ static int handle_write_command(uint8_t block_num, uint8_t *p_data) {

if (block_num >= block_max) {
NRF_LOG_ERROR("Write failed: block_num %08x >= block_max %08x", block_num, block_max);
return NAK_INVALID_OPERATION_TBV;
}

// Handle writing based on the current write mode
if (m_tag_information->config.mode_block_write == NFC_TAG_MF0_NTAG_WRITE_DENIED) {
// In this mode, reject all write operations
NRF_LOG_INFO("Write denied due to WRITE_DENIED mode");
return NAK_INVALID_OPERATION_TBV;
}
else if (m_tag_information->config.mode_block_write == NFC_TAG_MF0_NTAG_WRITE_DECEIVE) {
// In this mode, pretend to accept the write but don't actually write anything
NRF_LOG_INFO("Write deceived in WRITE_DECEIVE mode");
return ACK_VALUE;
}
// For NORMAL, SHADOW, and SHADOW_REQ modes, proceed with the write operation

if (m_tag_information->config.mode_uid_magic) {
// anything can be written in this mode
Expand Down Expand Up @@ -1053,6 +1065,15 @@ static int get_information_size_by_tag_type(tag_specific_type_t type) {
*/
int nfc_tag_mf0_ntag_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer) {
if (m_tag_type != TAG_TYPE_UNDEFINED && m_tag_information != NULL) {
// Add shadow mode handling
if (m_tag_information->config.mode_block_write == NFC_TAG_MF0_NTAG_WRITE_SHADOW) {
NRF_LOG_INFO("The mf0/ntag is in shadow write mode.");
return 0;
}
if (m_tag_information->config.mode_block_write == NFC_TAG_MF0_NTAG_WRITE_SHADOW_REQ) {
NRF_LOG_INFO("The mf0/ntag will be set to shadow write mode.");
m_tag_information->config.mode_block_write = NFC_TAG_MF0_NTAG_WRITE_SHADOW;
}
// Save the corresponding size data according to the current label type
return get_information_size_by_tag_type(type);
} else {
Expand Down Expand Up @@ -1217,6 +1238,7 @@ bool nfc_tag_mf0_ntag_data_factory(uint8_t slot, tag_specific_type_t tag_type) {

// default ntag config
p_ntag_information->config.mode_uid_magic = false;
p_ntag_information->config.mode_block_write = NFC_TAG_MF0_NTAG_WRITE_NORMAL;

// save data to flash
tag_sense_type_t sense_type = get_sense_type_from_tag_type(tag_type);
Expand Down Expand Up @@ -1244,4 +1266,20 @@ bool nfc_tag_mf0_ntag_set_uid_mode(bool enabled) {

m_tag_information->config.mode_uid_magic = enabled;
return true;
}

void nfc_tag_mf0_ntag_set_write_mode(nfc_tag_mf0_ntag_write_mode_t write_mode) {
if (m_tag_type == TAG_TYPE_UNDEFINED || m_tag_information == NULL) return;

if (write_mode == NFC_TAG_MF0_NTAG_WRITE_SHADOW) {
write_mode = NFC_TAG_MF0_NTAG_WRITE_SHADOW_REQ;
}
m_tag_information->config.mode_block_write = write_mode;
}

nfc_tag_mf0_ntag_write_mode_t nfc_tag_mf0_ntag_get_write_mode(void) {
if (m_tag_type == TAG_TYPE_UNDEFINED || m_tag_information == NULL)
return NFC_TAG_MF0_NTAG_WRITE_NORMAL;

return m_tag_information->config.mode_block_write;
}
17 changes: 15 additions & 2 deletions firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,21 @@
#define MF0_NTAG_AUTHLIM_MASK_IN_CTR 0xF
#define MF0_NTAG_TEARING_MASK_IN_AUTHLIM 0x80

//MF0/NTAG label writing mode
typedef enum {
NFC_TAG_MF0_NTAG_WRITE_NORMAL = 0u,
NFC_TAG_MF0_NTAG_WRITE_DENIED = 1u,
NFC_TAG_MF0_NTAG_WRITE_DECEIVE = 2u,
NFC_TAG_MF0_NTAG_WRITE_SHADOW = 3u,
NFC_TAG_MF0_NTAG_WRITE_SHADOW_REQ = 4u,
} nfc_tag_mf0_ntag_write_mode_t;

typedef struct {
uint8_t mode_uid_magic: 1;
// reserve
uint8_t reserved1: 7;
// New field for write mode
nfc_tag_mf0_ntag_write_mode_t mode_block_write: 3;
// reserve remaining bits
uint8_t reserved1: 4;
uint8_t reserved2;
uint8_t reserved3;
} nfc_tag_mf0_ntag_configure_t;
Expand Down Expand Up @@ -79,5 +90,7 @@ nfc_tag_14a_coll_res_reference_t *nfc_tag_mf0_ntag_get_coll_res(void);

int nfc_tag_mf0_ntag_get_uid_mode(void);
bool nfc_tag_mf0_ntag_set_uid_mode(bool enabled);
void nfc_tag_mf0_ntag_set_write_mode(nfc_tag_mf0_ntag_write_mode_t write_mode);
nfc_tag_mf0_ntag_write_mode_t nfc_tag_mf0_ntag_get_write_mode(void);

#endif
40 changes: 37 additions & 3 deletions software/script/chameleon_cli_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from chameleon_utils import print_mem_dump
from chameleon_enum import Command, Status, SlotNumber, TagSenseType, TagSpecificType
from chameleon_enum import MifareClassicWriteMode, MifareClassicPrngType, MifareClassicDarksideStatus, MfcKeyType
from chameleon_enum import MifareUltralightWriteMode
from chameleon_enum import AnimationMode, ButtonPressFunction, ButtonType, MfcValueBlockOperator

# NXP IDs based on https://www.nxp.com/docs/en/application-note/AN10833.pdf
Expand Down Expand Up @@ -1557,7 +1558,7 @@ def args_parser(self) -> ArgumentParserNoExit:
def on_exec(self, args: argparse.Namespace):
# collect current settings
anti_coll_data = self.cmd.hf14a_get_anti_coll_data()
if len(anti_coll_data) == 0:
if anti_coll_data is None or len(anti_coll_data) == 0:
print(f"{CR}Slot {self.slot_num} does not contain any HF 14A config{C0}")
return
uid = anti_coll_data['uid']
Expand Down Expand Up @@ -2306,6 +2307,12 @@ def args_parser(self) -> ArgumentParserNoExit:
uid_magic_group = parser.add_mutually_exclusive_group()
uid_magic_group.add_argument('--enable-uid-magic', action='store_true', help="Enable UID magic mode")
uid_magic_group.add_argument('--disable-uid-magic', action='store_true', help="Disable UID magic mode")

# Add this new write mode parameter
write_names = [w.name for w in MifareUltralightWriteMode.list()]
help_str = "Write Mode: " + ", ".join(write_names)
parser.add_argument('--write', type=str, help=help_str, metavar="MODE", choices=write_names)

parser.add_argument('--set-version', type=bytes.fromhex, help="Set data to be returned by the GET_VERSION command.")
parser.add_argument('--set-signature', type=bytes.fromhex, help="Set data to be returned by the READ_SIG command.")
parser.add_argument('--reset-auth-cnt', action='store_true', help="Resets the counter of unsuccessful authentication attempts.")
Expand Down Expand Up @@ -2387,6 +2394,22 @@ def on_exec(self, args: argparse.Namespace):
magic_mode = False
else:
magic_mode = self.cmd.mf0_ntag_get_uid_magic_mode()

# Add this new write mode handling
write_mode = None
if args.write is not None:
change_requested = True
new_write_mode = MifareUltralightWriteMode[args.write]
try:
current_write_mode = self.cmd.mf0_ntag_get_write_mode()
if new_write_mode != current_write_mode:
self.cmd.mf0_ntag_set_write_mode(new_write_mode)
change_done = True
write_mode = new_write_mode
else:
print(f'{CY}Requested write mode already set{C0}')
except:
print(f"{CR}Failed to set write mode. Check if device firmware supports this feature.{C0}")

if change_done or aux_data_changed:
print(' - MFU/NTAG Emulator settings updated')
Expand All @@ -2398,11 +2421,22 @@ def on_exec(self, args: argparse.Namespace):
print(f'- {"SAK:":40}{CY}{sak.hex().upper()}{C0}')
if len(ats) > 0:
print(f'- {"ATS:":40}{CY}{ats.hex().upper()}{C0}')

# Display UID Magic status
if magic_mode:
print(f'- {"UID Magic:":40}{CY}enabled{C0}')
else:
print(f'- {"UID Magic:":40}{CY}disabled{C0}')

# Add this to display write mode if available
try:
write_mode = self.cmd.mf0_ntag_get_write_mode()
print(f'- {"Write mode:":40}{CY}{MifareUltralightWriteMode(write_mode)}{C0}')
except:
# Write mode not supported in current firmware
pass

# Existing version/signature display code
try:
version = self.cmd.mf0_ntag_get_version_data()
print(f'- {"Version:":40}{CY}{version.hex().upper()}{C0}')
Expand Down Expand Up @@ -2495,7 +2529,7 @@ def on_exec(self, args: argparse.Namespace):
print(f"{CY if enabled[fwslot]['hf'] else C0}{hf_tag_type}{C0}")
else:
print("undef")
if (not args.short) and enabled[fwslot]['hf']:
if (not args.short) and enabled[fwslot]['hf'] and hf_tag_type != TagSpecificType.UNDEFINED:
if current != slot:
self.cmd.set_active_slot(slot)
current = slot
Expand Down Expand Up @@ -2546,7 +2580,7 @@ def on_exec(self, args: argparse.Namespace):
print(f"{CY if enabled[fwslot]['lf'] else C0}{lf_tag_type}{C0}")
else:
print("undef")
if (not args.short) and enabled[fwslot]['lf']:
if (not args.short) and enabled[fwslot]['lf'] and lf_tag_type != TagSpecificType.UNDEFINED:
if current != slot:
self.cmd.set_active_slot(slot)
current = slot
Expand Down
18 changes: 18 additions & 0 deletions software/script/chameleon_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,24 @@ def mf0_ntag_set_signature_data(self, data: bytes):
assert len(data) == 32
return self.device.send_cmd_sync(Command.MF0_NTAG_SET_SIGNATURE_DATA, data)

@expect_response(Status.SUCCESS)
def mf0_ntag_get_write_mode(self):
"""
Get write mode for MF0/NTAG
"""
resp = self.device.send_cmd_sync(Command.MF0_NTAG_GET_WRITE_MODE)
if resp.status == Status.SUCCESS:
resp.parsed = resp.data[0]
return resp

@expect_response(Status.SUCCESS)
def mf0_ntag_set_write_mode(self, mode: int):
"""
Set write mode for MF0/NTAG
"""
data = struct.pack('!B', mode)
return self.device.send_cmd_sync(Command.MF0_NTAG_SET_WRITE_MODE, data)

@expect_response(Status.SUCCESS)
def get_ble_pairing_enable(self):
"""
Expand Down
33 changes: 33 additions & 0 deletions software/script/chameleon_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ class Command(enum.IntEnum):
MF0_NTAG_SET_COUNTER_DATA = 4028
MF0_NTAG_RESET_AUTH_CNT = 4029
MF0_NTAG_GET_PAGE_COUNT = 4030
MF0_NTAG_GET_WRITE_MODE = 4031
MF0_NTAG_SET_WRITE_MODE = 4032

EM410X_SET_EMU_ID = 5000
EM410X_GET_EMU_ID = 5001
Expand Down Expand Up @@ -374,6 +376,37 @@ def __str__(self):
return "Shadow requested"
return "None"

@enum.unique
class MifareUltralightWriteMode(enum.IntEnum):
# Normal write
NORMAL = 0
# Send NACK to write attempts
DENIED = 1
# Acknowledge writes, but don't remember contents
DECEIVE = 2
# Store data to RAM, but not to ROM
SHADOW = 3
# Shadow requested, will be changed to SHADOW and stored to ROM
SHADOW_REQ = 4

@staticmethod
def list(exclude_meta=True):
return [m for m in MifareUltralightWriteMode
if m != MifareUltralightWriteMode.SHADOW_REQ
or not exclude_meta]

def __str__(self):
if self == MifareUltralightWriteMode.NORMAL:
return "Normal"
elif self == MifareUltralightWriteMode.DENIED:
return "Denied"
elif self == MifareUltralightWriteMode.DECEIVE:
return "Deceive"
elif self == MifareUltralightWriteMode.SHADOW:
return "Shadow"
elif self == MifareUltralightWriteMode.SHADOW_REQ:
return "Shadow requested"
return "None"

@enum.unique
class MifareClassicPrngType(enum.IntEnum):
Expand Down