Skip to content

Commit bbef641

Browse files
committed
Fixed FAST_READ command and handling of dynamic and static locks for NTG213 NTG215 and NTG216 tags
1 parent 97dfe5b commit bbef641

File tree

3 files changed

+73
-14
lines changed

3 files changed

+73
-14
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file.
33
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...
44

55
## [unreleased][unreleased]
6+
- Fix for FAST_READ command for nfc - mf0 tags
7+
- Rewrite of the dynamic and static locks logic for NTAG213, NTAG215 and NTAG216; we shouldn't take into account the block lock bits
8+
- Fixed an issue where we wouldn't be able to change CFG0 and CFG1 for NTAG213, NTAG215 and NTG216 once a password was added even if the cfg bit was reset.
69
- Fix for static nested key recovery (@jekkos)
710

811
## [v2.1.0][2025-09-02]

firmware/application/src/rfid/nfctag/hf/nfc_14a.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,15 @@ void nfc_tag_14a_data_process(uint8_t *p_data) {
397397
m_tag_state_14a = NFC_TAG_STATE_14A_IDLE;
398398
}
399399
return;
400+
case NFC_TAG_14A_CMD_REQA:
401+
case NFC_TAG_14A_CMD_WUPA:
402+
// Reader is re-sending REQA/WUPA while in READY state
403+
// This can happen if reader retries or if frame was received incorrectly
404+
// Respond with ATQA again and stay in READY state
405+
if (auto_coll_res != NULL) {
406+
nfc_tag_14a_tx_bytes(auto_coll_res->atqa, 2, false);
407+
}
408+
return;
400409
default: {
401410
// After receiving the wrong level instruction, directly reset the status machine
402411
NRF_LOG_INFO("[MFEMUL_SELECT] Incorrect cascade level received: %02x", p_data[0]);

firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.c

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -675,29 +675,49 @@ static void handle_fast_read_command(uint8_t block_num, uint8_t end_block_num) {
675675

676676
int block_max = get_block_max_by_tag_type(m_tag_type, true);
677677

678-
if (block_num >= end_block_num || end_block_num >= block_max) {
678+
if (block_num > end_block_num || end_block_num >= block_max) {
679679
nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBV, 4);
680680
return;
681681
}
682682

683683
NRF_LOG_INFO("HANDLING FAST READ %02x %02x", block_num, end_block_num);
684-
685-
handle_any_read(block_num, end_block_num - block_num, block_max);
684+
// FAST_READ is inclusive: read from block_num to end_block_num (both included)
685+
handle_any_read(block_num, end_block_num - block_num + 1, block_max);
686686
}
687687

688688
static bool check_ro_lock_on_page(int block_num) {
689689
if (block_num < 3) return true;
690-
else if (block_num == 3) return (m_tag_information->memory[2][2] & 9) != 0; // bits 0 and 3
691-
else if (block_num <= MF0ICU1_PAGES) {
690+
else if (block_num == 3) {
691+
switch (m_tag_type) {
692+
case TAG_TYPE_NTAG_213:
693+
case TAG_TYPE_NTAG_215:
694+
case TAG_TYPE_NTAG_216:
695+
//page 3 can be locked or not independant of BL CC bit
696+
//the BL bit only freezes the lock bytes !
697+
return (m_tag_information->memory[2][2] & 8) != 0;
698+
default:
699+
return (m_tag_information->memory[2][2] & 9) != 0;
700+
}
701+
// bits 0 and 3
702+
} else if (block_num <= MF0ICU1_PAGES) {
692703
bool locked = false;
704+
switch (m_tag_type) {
705+
case TAG_TYPE_NTAG_213:
706+
case TAG_TYPE_NTAG_215:
707+
case TAG_TYPE_NTAG_216:
708+
// pages can be locked or not independant of BL bits
709+
//the BL bits only freezes the lock bytes !
710+
uint16_t lock_bits = *(uint16_t *)&m_tag_information->memory[2][2];
711+
return ((lock_bits >> block_num) & 0x01) == 1;
712+
default:
713+
// check block locking bits
714+
if (block_num <= 9) locked |= (m_tag_information->memory[2][2] & 2) == 2;
715+
else locked |= (m_tag_information->memory[2][2] & 4) == 4;
693716

694-
// check block locking bits
695-
if (block_num <= 9) locked |= (m_tag_information->memory[2][2] & 2) == 2;
696-
else locked |= (m_tag_information->memory[2][2] & 4) == 4;
697-
698-
locked |= (((*(uint16_t *)&m_tag_information->memory[2][2]) >> block_num) & 1) == 1;
717+
locked |= (((*(uint16_t *)&m_tag_information->memory[2][2]) >> block_num) & 1) == 1;
699718

700-
return locked;
719+
return locked;
720+
}
701721
} else {
702722
uint8_t *p_lock_bytes = NULL;
703723
int user_memory_end = 0;
@@ -776,9 +796,18 @@ static bool check_ro_lock_on_page(int block_num) {
776796

777797
bool locked_small_range = ((lock_word >> (index / dyn_lock_bit_page_cnt)) & 1) != 0;
778798
bool locked_large_range = ((p_lock_bytes[2] >> (index / dyn_lock_bit_page_cnt / 2)) & 1) != 0;
779-
780-
return locked_small_range | locked_large_range;
799+
switch (m_tag_type) {
800+
case TAG_TYPE_NTAG_213:
801+
case TAG_TYPE_NTAG_215:
802+
case TAG_TYPE_NTAG_216:
803+
// For NTAG213/215/216: byte 2 contains block-locking bits (BL) which only freeze
804+
// the lock configuration. We only check the actual lock bits (L0-L15) in bytes 0-1.
805+
return locked_small_range;
806+
default:
807+
return locked_small_range | locked_large_range;
808+
}
781809
} else {
810+
//TODO needs to check the block locking bits to see if we can touch the dynamic locks bytes for NTAG tags
782811
// check CFGLCK bit
783812
int first_cfg_page = get_first_cfg_page_by_tag_type(m_tag_type);
784813
uint8_t access = m_tag_information->memory[first_cfg_page + CONF_ACCESS_PAGE_OFFSET][CONF_ACCESS_BYTE];
@@ -793,7 +822,25 @@ static bool check_ro_lock_on_page(int block_num) {
793822
static int handle_write_command(uint8_t block_num, uint8_t *p_data) {
794823
int block_max = get_block_max_by_tag_type(m_tag_type, false);
795824

796-
if (block_num >= block_max) {
825+
bool out_of_bounds = false;
826+
switch (m_tag_type) {
827+
case TAG_TYPE_NTAG_213:
828+
case TAG_TYPE_NTAG_215:
829+
case TAG_TYPE_NTAG_216:
830+
int first_cfg_page = get_first_cfg_page_by_tag_type(m_tag_type);
831+
uint8_t cfglck = m_tag_information->memory[first_cfg_page][0] & 0x40;
832+
// For NTAG cards we need to check CFGLCK bit for config pages
833+
bool is_config_page = (block_num >= first_cfg_page) && (block_num <= first_cfg_page + 1);
834+
bool config_locked = (cfglck != 0) && (m_tag_information->config.mode_uid_magic);
835+
bool is_beyond_user_memory = (block_num >= block_max);
836+
out_of_bounds = (is_beyond_user_memory && !is_config_page) || (config_locked && is_config_page);
837+
break;
838+
default:
839+
out_of_bounds = block_num >= block_max;
840+
break;
841+
}
842+
// Reject out-of-bounds writes (except config pages)
843+
if (out_of_bounds) {
797844
NRF_LOG_ERROR("Write failed: block_num %08x >= block_max %08x", block_num, block_max);
798845
return NAK_INVALID_OPERATION_TBV;
799846
}

0 commit comments

Comments
 (0)