mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-23 13:03:13 +00:00
nfc: NTAG203 support (#1383)
* nfc: Fix original MFUL feature flags * nfc: Add NTAG203 read support * nfc: Update emulation lock byte handling for NTAG203 * nfc: Add NTAG203 counter emulation support * nfc: Add NTAG203 tag generator * nfc: Fix NTAG203 emulating GET_VERSION * nfc: Fix MFUL version reading * nfc: Complete NTAG203 counter emulation behavior * nfc: Complete NTAG203 emulation * nfc: Remove unnecessary init in MFUL emulation * nfc: Add notes about MFUL type enum Co-authored-by: gornekich <n.gorbadey@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
parent
30820b83b5
commit
f8e0ec42c5
4 changed files with 237 additions and 57 deletions
|
@ -6,6 +6,8 @@
|
||||||
static const uint8_t version_bytes_mf0ulx1[] = {0x00, 0x04, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03};
|
static const uint8_t version_bytes_mf0ulx1[] = {0x00, 0x04, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03};
|
||||||
static const uint8_t version_bytes_ntag21x[] = {0x00, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x03};
|
static const uint8_t version_bytes_ntag21x[] = {0x00, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x03};
|
||||||
static const uint8_t version_bytes_ntag_i2c[] = {0x00, 0x04, 0x04, 0x05, 0x02, 0x00, 0x00, 0x03};
|
static const uint8_t version_bytes_ntag_i2c[] = {0x00, 0x04, 0x04, 0x05, 0x02, 0x00, 0x00, 0x03};
|
||||||
|
static const uint8_t default_data_ntag203[] =
|
||||||
|
{0xE1, 0x10, 0x12, 0x00, 0x01, 0x03, 0xA0, 0x10, 0x44, 0x03, 0x00, 0xFE};
|
||||||
static const uint8_t default_data_ntag213[] = {0x01, 0x03, 0xA0, 0x0C, 0x34, 0x03, 0x00, 0xFE};
|
static const uint8_t default_data_ntag213[] = {0x01, 0x03, 0xA0, 0x0C, 0x34, 0x03, 0x00, 0xFE};
|
||||||
static const uint8_t default_data_ntag215_216[] = {0x03, 0x00, 0xFE};
|
static const uint8_t default_data_ntag215_216[] = {0x03, 0x00, 0xFE};
|
||||||
static const uint8_t default_data_ntag_i2c[] = {0xE1, 0x10, 0x00, 0x00, 0x03, 0x00, 0xFE};
|
static const uint8_t default_data_ntag_i2c[] = {0xE1, 0x10, 0x00, 0x00, 0x03, 0x00, 0xFE};
|
||||||
|
@ -58,6 +60,18 @@ static void nfc_generate_mf_ul_orig(NfcDeviceData* data) {
|
||||||
memset(&mful->data[4 * 4], 0xFF, 4);
|
memset(&mful->data[4 * 4], 0xFF, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nfc_generate_mf_ul_ntag203(NfcDeviceData* data) {
|
||||||
|
nfc_generate_common_start(data);
|
||||||
|
nfc_generate_mf_ul_common(data);
|
||||||
|
|
||||||
|
MfUltralightData* mful = &data->mf_ul_data;
|
||||||
|
mful->type = MfUltralightTypeNTAG203;
|
||||||
|
mful->data_size = 42 * 4;
|
||||||
|
nfc_generate_mf_ul_copy_uid_with_bcc(data);
|
||||||
|
mful->data[9] = 0x48; // Internal byte
|
||||||
|
memcpy(&mful->data[3 * 4], default_data_ntag203, sizeof(default_data_ntag203));
|
||||||
|
}
|
||||||
|
|
||||||
static void nfc_generate_mf_ul_with_config_common(NfcDeviceData* data, uint8_t num_pages) {
|
static void nfc_generate_mf_ul_with_config_common(NfcDeviceData* data, uint8_t num_pages) {
|
||||||
nfc_generate_common_start(data);
|
nfc_generate_common_start(data);
|
||||||
nfc_generate_mf_ul_common(data);
|
nfc_generate_mf_ul_common(data);
|
||||||
|
@ -275,6 +289,11 @@ static const NfcGenerator mf_ul_h21_generator = {
|
||||||
.generator_func = nfc_generate_mf_ul_h21,
|
.generator_func = nfc_generate_mf_ul_h21,
|
||||||
.next_scene = NfcSceneMifareUlMenu};
|
.next_scene = NfcSceneMifareUlMenu};
|
||||||
|
|
||||||
|
static const NfcGenerator ntag203_generator = {
|
||||||
|
.name = "NTAG203",
|
||||||
|
.generator_func = nfc_generate_mf_ul_ntag203,
|
||||||
|
.next_scene = NfcSceneMifareUlMenu};
|
||||||
|
|
||||||
static const NfcGenerator ntag213_generator = {
|
static const NfcGenerator ntag213_generator = {
|
||||||
.name = "NTAG213",
|
.name = "NTAG213",
|
||||||
.generator_func = nfc_generate_ntag213,
|
.generator_func = nfc_generate_ntag213,
|
||||||
|
@ -316,6 +335,7 @@ const NfcGenerator* const nfc_generators[] = {
|
||||||
&mf_ul_h11_generator,
|
&mf_ul_h11_generator,
|
||||||
&mf_ul_21_generator,
|
&mf_ul_21_generator,
|
||||||
&mf_ul_h21_generator,
|
&mf_ul_h21_generator,
|
||||||
|
&ntag203_generator,
|
||||||
&ntag213_generator,
|
&ntag213_generator,
|
||||||
&ntag215_generator,
|
&ntag215_generator,
|
||||||
&ntag216_generator,
|
&ntag216_generator,
|
||||||
|
|
|
@ -43,6 +43,8 @@ const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) {
|
||||||
return "NTAG I2C Plus 1K";
|
return "NTAG I2C Plus 1K";
|
||||||
} else if(type == MfUltralightTypeNTAGI2CPlus2K) {
|
} else if(type == MfUltralightTypeNTAGI2CPlus2K) {
|
||||||
return "NTAG I2C Plus 2K";
|
return "NTAG I2C Plus 2K";
|
||||||
|
} else if(type == MfUltralightTypeNTAG203) {
|
||||||
|
return "NTAG203";
|
||||||
} else if(type == MfUltralightTypeUL11 && full_name) {
|
} else if(type == MfUltralightTypeUL11 && full_name) {
|
||||||
return "Mifare Ultralight 11";
|
return "Mifare Ultralight 11";
|
||||||
} else if(type == MfUltralightTypeUL21 && full_name) {
|
} else if(type == MfUltralightTypeUL21 && full_name) {
|
||||||
|
|
|
@ -35,9 +35,11 @@ static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) {
|
||||||
return MfUltralightSupportFastRead | MfUltralightSupportAuth |
|
return MfUltralightSupportFastRead | MfUltralightSupportAuth |
|
||||||
MfUltralightSupportFastWrite | MfUltralightSupportSignature |
|
MfUltralightSupportFastWrite | MfUltralightSupportSignature |
|
||||||
MfUltralightSupportSectorSelect;
|
MfUltralightSupportSectorSelect;
|
||||||
|
case MfUltralightTypeNTAG203:
|
||||||
|
return MfUltralightSupportCompatWrite | MfUltralightSupportCounterInMemory;
|
||||||
default:
|
default:
|
||||||
// Assumed original MFUL 512-bit
|
// Assumed original MFUL 512-bit
|
||||||
return MfUltralightSupportNone;
|
return MfUltralightSupportCompatWrite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +48,11 @@ static void mf_ul_set_default_version(MfUltralightReader* reader, MfUltralightDa
|
||||||
reader->pages_to_read = 16;
|
reader->pages_to_read = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mf_ul_set_version_ntag203(MfUltralightReader* reader, MfUltralightData* data) {
|
||||||
|
data->type = MfUltralightTypeNTAG203;
|
||||||
|
reader->pages_to_read = 42;
|
||||||
|
}
|
||||||
|
|
||||||
bool mf_ultralight_read_version(
|
bool mf_ultralight_read_version(
|
||||||
FuriHalNfcTxRxContext* tx_rx,
|
FuriHalNfcTxRxContext* tx_rx,
|
||||||
MfUltralightReader* reader,
|
MfUltralightReader* reader,
|
||||||
|
@ -57,7 +64,7 @@ bool mf_ultralight_read_version(
|
||||||
tx_rx->tx_data[0] = MF_UL_GET_VERSION_CMD;
|
tx_rx->tx_data[0] = MF_UL_GET_VERSION_CMD;
|
||||||
tx_rx->tx_bits = 8;
|
tx_rx->tx_bits = 8;
|
||||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||||
if(!furi_hal_nfc_tx_rx(tx_rx, 50)) {
|
if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits != 64) {
|
||||||
FURI_LOG_D(TAG, "Failed reading version");
|
FURI_LOG_D(TAG, "Failed reading version");
|
||||||
mf_ul_set_default_version(reader, data);
|
mf_ul_set_default_version(reader, data);
|
||||||
furi_hal_nfc_sleep();
|
furi_hal_nfc_sleep();
|
||||||
|
@ -468,6 +475,23 @@ static bool mf_ultralight_sector_select(FuriHalNfcTxRxContext* tx_rx, uint8_t se
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mf_ultralight_read_pages_direct(
|
||||||
|
FuriHalNfcTxRxContext* tx_rx,
|
||||||
|
uint8_t start_index,
|
||||||
|
uint8_t* data) {
|
||||||
|
FURI_LOG_D(TAG, "Reading pages %d - %d", start_index, start_index + 3);
|
||||||
|
tx_rx->tx_data[0] = MF_UL_READ_CMD;
|
||||||
|
tx_rx->tx_data[1] = start_index;
|
||||||
|
tx_rx->tx_bits = 16;
|
||||||
|
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||||
|
if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits < 16 * 8) {
|
||||||
|
FURI_LOG_D(TAG, "Failed to read pages %d - %d", start_index, start_index + 3);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(data, tx_rx->rx_data, 16);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool mf_ultralight_read_pages(
|
bool mf_ultralight_read_pages(
|
||||||
FuriHalNfcTxRxContext* tx_rx,
|
FuriHalNfcTxRxContext* tx_rx,
|
||||||
MfUltralightReader* reader,
|
MfUltralightReader* reader,
|
||||||
|
@ -632,6 +656,17 @@ bool mf_ul_read_card(
|
||||||
// Read Signature
|
// Read Signature
|
||||||
mf_ultralight_read_signature(tx_rx, data);
|
mf_ultralight_read_signature(tx_rx, data);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// No GET_VERSION command, check for NTAG203 by reading last page (41)
|
||||||
|
uint8_t dummy[16];
|
||||||
|
if(mf_ultralight_read_pages_direct(tx_rx, 41, dummy)) {
|
||||||
|
mf_ul_set_version_ntag203(reader, data);
|
||||||
|
reader->supported_features = mf_ul_get_features(data->type);
|
||||||
|
} else {
|
||||||
|
// We're really an original Mifare Ultralight, reset tag for safety
|
||||||
|
furi_hal_nfc_sleep();
|
||||||
|
furi_hal_nfc_activate_nfca(300, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
card_read = mf_ultralight_read_pages(tx_rx, reader, data);
|
card_read = mf_ultralight_read_pages(tx_rx, reader, data);
|
||||||
|
@ -772,6 +807,8 @@ static bool mf_ul_ntag_i2c_plus_check_auth(
|
||||||
|
|
||||||
static int16_t mf_ul_get_dynamic_lock_page_addr(MfUltralightData* data) {
|
static int16_t mf_ul_get_dynamic_lock_page_addr(MfUltralightData* data) {
|
||||||
switch(data->type) {
|
switch(data->type) {
|
||||||
|
case MfUltralightTypeNTAG203:
|
||||||
|
return 0x28;
|
||||||
case MfUltralightTypeUL21:
|
case MfUltralightTypeUL21:
|
||||||
case MfUltralightTypeNTAG213:
|
case MfUltralightTypeNTAG213:
|
||||||
case MfUltralightTypeNTAG215:
|
case MfUltralightTypeNTAG215:
|
||||||
|
@ -804,6 +841,10 @@ static bool mf_ul_check_lock(MfUltralightEmulator* emulator, int16_t write_page)
|
||||||
|
|
||||||
// Check max page
|
// Check max page
|
||||||
switch(emulator->data.type) {
|
switch(emulator->data.type) {
|
||||||
|
case MfUltralightTypeNTAG203:
|
||||||
|
// Counter page can be locked and is after dynamic locks
|
||||||
|
if(write_page == 40) return true;
|
||||||
|
break;
|
||||||
case MfUltralightTypeUL21:
|
case MfUltralightTypeUL21:
|
||||||
case MfUltralightTypeNTAG213:
|
case MfUltralightTypeNTAG213:
|
||||||
case MfUltralightTypeNTAG215:
|
case MfUltralightTypeNTAG215:
|
||||||
|
@ -841,6 +882,19 @@ static bool mf_ul_check_lock(MfUltralightEmulator* emulator, int16_t write_page)
|
||||||
|
|
||||||
switch(emulator->data.type) {
|
switch(emulator->data.type) {
|
||||||
// low byte LSB range, MSB range
|
// low byte LSB range, MSB range
|
||||||
|
case MfUltralightTypeNTAG203:
|
||||||
|
if(write_page >= 16 && write_page <= 27)
|
||||||
|
shift = (write_page - 16) / 4 + 1;
|
||||||
|
else if(write_page >= 28 && write_page <= 39)
|
||||||
|
shift = (write_page - 28) / 4 + 5;
|
||||||
|
else if(write_page == 41)
|
||||||
|
shift = 12;
|
||||||
|
else {
|
||||||
|
furi_assert(false);
|
||||||
|
shift = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
case MfUltralightTypeUL21:
|
case MfUltralightTypeUL21:
|
||||||
case MfUltralightTypeNTAG213:
|
case MfUltralightTypeNTAG213:
|
||||||
// 16-17, 30-31
|
// 16-17, 30-31
|
||||||
|
@ -937,6 +991,42 @@ static void mf_ul_increment_single_counter(MfUltralightEmulator* emulator) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
mf_ul_emulate_ntag203_counter_write(MfUltralightEmulator* emulator, uint8_t* page_buff) {
|
||||||
|
// We'll reuse the existing counters for other NTAGs as staging
|
||||||
|
// Counter 0 stores original value, data is new value
|
||||||
|
uint32_t counter_value;
|
||||||
|
if(emulator->data.tearing[0] == MF_UL_TEARING_FLAG_DEFAULT) {
|
||||||
|
counter_value = emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] |
|
||||||
|
(emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] << 8);
|
||||||
|
} else {
|
||||||
|
// We've had a reset here, so load from original value
|
||||||
|
counter_value = emulator->data.counter[0];
|
||||||
|
}
|
||||||
|
// Although the datasheet says increment by 0 is always possible, this is not the case on
|
||||||
|
// an actual tag. If the counter is at 0xFFFF, any writes are locked out.
|
||||||
|
if(counter_value == 0xFFFF) return false;
|
||||||
|
uint32_t increment = page_buff[0] | (page_buff[1] << 8);
|
||||||
|
if(counter_value == 0) {
|
||||||
|
counter_value = increment;
|
||||||
|
} else {
|
||||||
|
// Per datasheet specifying > 0x000F is supposed to NAK, but actual tag doesn't
|
||||||
|
increment &= 0x000F;
|
||||||
|
if(counter_value + increment > 0xFFFF) return false;
|
||||||
|
counter_value += increment;
|
||||||
|
}
|
||||||
|
// Commit to new value counter
|
||||||
|
emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] = (uint8_t)counter_value;
|
||||||
|
emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] = (uint8_t)(counter_value >> 8);
|
||||||
|
emulator->data.tearing[0] = MF_UL_TEARING_FLAG_DEFAULT;
|
||||||
|
if(counter_value == 0xFFFF) {
|
||||||
|
// Tag will lock out counter if final number is 0xFFFF, even if you try to roll it back
|
||||||
|
emulator->data.counter[1] = 0xFFFF;
|
||||||
|
}
|
||||||
|
emulator->data_changed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void mf_ul_emulate_write(
|
static void mf_ul_emulate_write(
|
||||||
MfUltralightEmulator* emulator,
|
MfUltralightEmulator* emulator,
|
||||||
int16_t tag_addr,
|
int16_t tag_addr,
|
||||||
|
@ -962,53 +1052,75 @@ static void mf_ul_emulate_write(
|
||||||
*(uint32_t*)page_buff |= *(uint32_t*)&emulator->data.data[write_page * 4];
|
*(uint32_t*)page_buff |= *(uint32_t*)&emulator->data.data[write_page * 4];
|
||||||
} else if(tag_addr == mf_ul_get_dynamic_lock_page_addr(&emulator->data)) {
|
} else if(tag_addr == mf_ul_get_dynamic_lock_page_addr(&emulator->data)) {
|
||||||
// Handle dynamic locks
|
// Handle dynamic locks
|
||||||
uint16_t orig_locks = emulator->data.data[write_page * 4] |
|
if(emulator->data.type == MfUltralightTypeNTAG203) {
|
||||||
(emulator->data.data[write_page * 4 + 1] << 8);
|
// NTAG203 lock bytes are a bit different from the others
|
||||||
uint8_t orig_block_locks = emulator->data.data[write_page * 4 + 2];
|
uint8_t orig_page_lock_byte = emulator->data.data[write_page * 4];
|
||||||
uint16_t new_locks = page_buff[0] | (page_buff[1] << 8);
|
uint8_t orig_cnt_lock_byte = emulator->data.data[write_page * 4 + 1];
|
||||||
uint8_t new_block_locks = page_buff[2];
|
uint8_t new_page_lock_byte = page_buff[0];
|
||||||
|
uint8_t new_cnt_lock_byte = page_buff[1];
|
||||||
|
|
||||||
int block_lock_count;
|
if(orig_page_lock_byte & 0x01) // Block lock bits 1-3
|
||||||
switch(emulator->data.type) {
|
new_page_lock_byte &= ~0x0E;
|
||||||
case MfUltralightTypeUL21:
|
if(orig_page_lock_byte & 0x10) // Block lock bits 5-7
|
||||||
block_lock_count = 5;
|
new_page_lock_byte &= ~0xE0;
|
||||||
break;
|
for(uint8_t i = 0; i < 4; ++i) {
|
||||||
case MfUltralightTypeNTAG213:
|
if(orig_cnt_lock_byte & (1 << i)) // Block lock counter bit
|
||||||
block_lock_count = 6;
|
new_cnt_lock_byte &= ~(1 << (4 + i));
|
||||||
break;
|
}
|
||||||
case MfUltralightTypeNTAG215:
|
|
||||||
block_lock_count = 4;
|
new_page_lock_byte |= orig_page_lock_byte;
|
||||||
break;
|
new_cnt_lock_byte |= orig_cnt_lock_byte;
|
||||||
case MfUltralightTypeNTAG216:
|
page_buff[0] = new_page_lock_byte;
|
||||||
case MfUltralightTypeNTAGI2C1K:
|
page_buff[1] = new_cnt_lock_byte;
|
||||||
case MfUltralightTypeNTAGI2CPlus1K:
|
} else {
|
||||||
block_lock_count = 7;
|
uint16_t orig_locks = emulator->data.data[write_page * 4] |
|
||||||
break;
|
(emulator->data.data[write_page * 4 + 1] << 8);
|
||||||
case MfUltralightTypeNTAGI2C2K:
|
uint8_t orig_block_locks = emulator->data.data[write_page * 4 + 2];
|
||||||
case MfUltralightTypeNTAGI2CPlus2K:
|
uint16_t new_locks = page_buff[0] | (page_buff[1] << 8);
|
||||||
block_lock_count = 8;
|
uint8_t new_block_locks = page_buff[2];
|
||||||
break;
|
|
||||||
default:
|
int block_lock_count;
|
||||||
furi_assert(false);
|
switch(emulator->data.type) {
|
||||||
block_lock_count = 0;
|
case MfUltralightTypeUL21:
|
||||||
break;
|
block_lock_count = 5;
|
||||||
|
break;
|
||||||
|
case MfUltralightTypeNTAG213:
|
||||||
|
block_lock_count = 6;
|
||||||
|
break;
|
||||||
|
case MfUltralightTypeNTAG215:
|
||||||
|
block_lock_count = 4;
|
||||||
|
break;
|
||||||
|
case MfUltralightTypeNTAG216:
|
||||||
|
case MfUltralightTypeNTAGI2C1K:
|
||||||
|
case MfUltralightTypeNTAGI2CPlus1K:
|
||||||
|
block_lock_count = 7;
|
||||||
|
break;
|
||||||
|
case MfUltralightTypeNTAGI2C2K:
|
||||||
|
case MfUltralightTypeNTAGI2CPlus2K:
|
||||||
|
block_lock_count = 8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
furi_assert(false);
|
||||||
|
block_lock_count = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < block_lock_count; ++i) {
|
||||||
|
if(orig_block_locks & (1 << i)) new_locks &= ~(3 << (2 * i));
|
||||||
|
}
|
||||||
|
|
||||||
|
new_locks |= orig_locks;
|
||||||
|
new_block_locks |= orig_block_locks;
|
||||||
|
|
||||||
|
page_buff[0] = new_locks & 0xff;
|
||||||
|
page_buff[1] = new_locks >> 8;
|
||||||
|
page_buff[2] = new_block_locks;
|
||||||
|
if(emulator->data.type >= MfUltralightTypeUL21 &&
|
||||||
|
emulator->data.type <= MfUltralightTypeNTAG216)
|
||||||
|
page_buff[3] = MF_UL_TEARING_FLAG_DEFAULT;
|
||||||
|
else
|
||||||
|
page_buff[3] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < block_lock_count; ++i) {
|
|
||||||
if(orig_block_locks & (1 << i)) new_locks &= ~(3 << (2 * i));
|
|
||||||
}
|
|
||||||
|
|
||||||
new_locks |= orig_locks;
|
|
||||||
new_block_locks |= orig_block_locks;
|
|
||||||
|
|
||||||
page_buff[0] = new_locks & 0xff;
|
|
||||||
page_buff[1] = new_locks >> 8;
|
|
||||||
page_buff[2] = new_block_locks;
|
|
||||||
if(emulator->data.type >= MfUltralightTypeUL21 &&
|
|
||||||
emulator->data.type <= MfUltralightTypeNTAG216)
|
|
||||||
page_buff[3] = MF_UL_TEARING_FLAG_DEFAULT;
|
|
||||||
else
|
|
||||||
page_buff[3] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&emulator->data.data[write_page * 4], page_buff, 4);
|
memcpy(&emulator->data.data[write_page * 4], page_buff, 4);
|
||||||
|
@ -1025,6 +1137,18 @@ void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle)
|
||||||
if(emulator->supported_features & MfUltralightSupportSingleCounter) {
|
if(emulator->supported_features & MfUltralightSupportSingleCounter) {
|
||||||
emulator->read_counter_incremented = false;
|
emulator->read_counter_incremented = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(emulator->data.type == MfUltralightTypeNTAG203) {
|
||||||
|
// Apply lockout if counter ever reached 0xFFFF
|
||||||
|
if(emulator->data.counter[1] == 0xFFFF) {
|
||||||
|
emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] = 0xFF;
|
||||||
|
emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] = 0xFF;
|
||||||
|
}
|
||||||
|
// Copy original counter value from data
|
||||||
|
emulator->data.counter[0] =
|
||||||
|
emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] |
|
||||||
|
(emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] << 8);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if(emulator->config != NULL) {
|
if(emulator->config != NULL) {
|
||||||
// ACCESS (less CFGLCK) and AUTH0 are updated when reactivated
|
// ACCESS (less CFGLCK) and AUTH0 are updated when reactivated
|
||||||
|
@ -1034,6 +1158,10 @@ void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle)
|
||||||
emulator->config_cache.auth0 = emulator->config->auth0;
|
emulator->config_cache.auth0 = emulator->config->auth0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(emulator->data.type == MfUltralightTypeNTAG203) {
|
||||||
|
// Mark counter as dirty
|
||||||
|
emulator->data.tearing[0] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* data) {
|
void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* data) {
|
||||||
|
@ -1077,12 +1205,20 @@ bool mf_ul_prepare_emulation_response(
|
||||||
|
|
||||||
// Check composite commands
|
// Check composite commands
|
||||||
if(emulator->comp_write_cmd_started) {
|
if(emulator->comp_write_cmd_started) {
|
||||||
// Compatibility write is the only one composit command
|
|
||||||
if(buff_rx_len == 16 * 8) {
|
if(buff_rx_len == 16 * 8) {
|
||||||
mf_ul_emulate_write(
|
if(emulator->data.type == MfUltralightTypeNTAG203 &&
|
||||||
emulator, emulator->comp_write_page_addr, emulator->comp_write_page_addr, buff_rx);
|
emulator->comp_write_page_addr == MF_UL_NTAG203_COUNTER_PAGE) {
|
||||||
send_ack = true;
|
send_ack = mf_ul_emulate_ntag203_counter_write(emulator, buff_rx);
|
||||||
command_parsed = true;
|
command_parsed = send_ack;
|
||||||
|
} else {
|
||||||
|
mf_ul_emulate_write(
|
||||||
|
emulator,
|
||||||
|
emulator->comp_write_page_addr,
|
||||||
|
emulator->comp_write_page_addr,
|
||||||
|
buff_rx);
|
||||||
|
send_ack = true;
|
||||||
|
command_parsed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
emulator->comp_write_cmd_started = false;
|
emulator->comp_write_cmd_started = false;
|
||||||
} else if(emulator->sector_select_cmd_started) {
|
} else if(emulator->sector_select_cmd_started) {
|
||||||
|
@ -1099,7 +1235,7 @@ bool mf_ul_prepare_emulation_response(
|
||||||
} else if(buff_rx_len >= 8) {
|
} else if(buff_rx_len >= 8) {
|
||||||
uint8_t cmd = buff_rx[0];
|
uint8_t cmd = buff_rx[0];
|
||||||
if(cmd == MF_UL_GET_VERSION_CMD) {
|
if(cmd == MF_UL_GET_VERSION_CMD) {
|
||||||
if(emulator->data.type != MfUltralightTypeUnknown) {
|
if(emulator->data.type >= MfUltralightTypeUL11) {
|
||||||
if(buff_rx_len == 1 * 8) {
|
if(buff_rx_len == 1 * 8) {
|
||||||
tx_bytes = sizeof(emulator->data.version);
|
tx_bytes = sizeof(emulator->data.version);
|
||||||
memcpy(buff_tx, &emulator->data.version, tx_bytes);
|
memcpy(buff_tx, &emulator->data.version, tx_bytes);
|
||||||
|
@ -1409,9 +1545,15 @@ bool mf_ul_prepare_emulation_response(
|
||||||
int16_t tag_addr = mf_ultralight_page_addr_to_tag_addr(
|
int16_t tag_addr = mf_ultralight_page_addr_to_tag_addr(
|
||||||
emulator->curr_sector, orig_write_page);
|
emulator->curr_sector, orig_write_page);
|
||||||
if(!mf_ul_check_lock(emulator, tag_addr)) break;
|
if(!mf_ul_check_lock(emulator, tag_addr)) break;
|
||||||
mf_ul_emulate_write(emulator, tag_addr, write_page, &buff_rx[2]);
|
if(emulator->data.type == MfUltralightTypeNTAG203 &&
|
||||||
send_ack = true;
|
orig_write_page == MF_UL_NTAG203_COUNTER_PAGE) {
|
||||||
command_parsed = true;
|
send_ack = mf_ul_emulate_ntag203_counter_write(emulator, &buff_rx[2]);
|
||||||
|
command_parsed = send_ack;
|
||||||
|
} else {
|
||||||
|
mf_ul_emulate_write(emulator, tag_addr, write_page, &buff_rx[2]);
|
||||||
|
send_ack = true;
|
||||||
|
command_parsed = true;
|
||||||
|
}
|
||||||
} while(false);
|
} while(false);
|
||||||
}
|
}
|
||||||
} else if(cmd == MF_UL_FAST_WRITE) {
|
} else if(cmd == MF_UL_FAST_WRITE) {
|
||||||
|
@ -1590,7 +1732,8 @@ bool mf_ul_prepare_emulation_response(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reset_idle = true;
|
// NTAG203 appears to NAK instead of just falling off on invalid commands
|
||||||
|
if(emulator->data.type != MfUltralightTypeNTAG203) reset_idle = true;
|
||||||
FURI_LOG_D(TAG, "Received invalid command");
|
FURI_LOG_D(TAG, "Received invalid command");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -26,15 +26,23 @@
|
||||||
#define MF_UL_NAK_INVALID_ARGUMENT (0x0)
|
#define MF_UL_NAK_INVALID_ARGUMENT (0x0)
|
||||||
#define MF_UL_NAK_AUTHLIM_REACHED (0x4)
|
#define MF_UL_NAK_AUTHLIM_REACHED (0x4)
|
||||||
|
|
||||||
|
#define MF_UL_NTAG203_COUNTER_PAGE (41)
|
||||||
|
|
||||||
|
// Important: order matters; some features are based on positioning in this enum
|
||||||
typedef enum {
|
typedef enum {
|
||||||
MfUltralightTypeUnknown,
|
MfUltralightTypeUnknown,
|
||||||
|
MfUltralightTypeNTAG203,
|
||||||
|
// Below have config pages and GET_VERSION support
|
||||||
MfUltralightTypeUL11,
|
MfUltralightTypeUL11,
|
||||||
MfUltralightTypeUL21,
|
MfUltralightTypeUL21,
|
||||||
MfUltralightTypeNTAG213,
|
MfUltralightTypeNTAG213,
|
||||||
MfUltralightTypeNTAG215,
|
MfUltralightTypeNTAG215,
|
||||||
MfUltralightTypeNTAG216,
|
MfUltralightTypeNTAG216,
|
||||||
|
// Below also have sector select
|
||||||
|
// NTAG I2C's *does not* have regular config pages, so it's a bit of an odd duck
|
||||||
MfUltralightTypeNTAGI2C1K,
|
MfUltralightTypeNTAGI2C1K,
|
||||||
MfUltralightTypeNTAGI2C2K,
|
MfUltralightTypeNTAGI2C2K,
|
||||||
|
// NTAG I2C Plus has stucture expected from NTAG21x
|
||||||
MfUltralightTypeNTAGI2CPlus1K,
|
MfUltralightTypeNTAGI2CPlus1K,
|
||||||
MfUltralightTypeNTAGI2CPlus2K,
|
MfUltralightTypeNTAGI2CPlus2K,
|
||||||
|
|
||||||
|
@ -58,6 +66,8 @@ typedef enum {
|
||||||
MfUltralightSupportSingleCounter = 1 << 10,
|
MfUltralightSupportSingleCounter = 1 << 10,
|
||||||
// ASCII mirror is not a command, but handy to have as a flag
|
// ASCII mirror is not a command, but handy to have as a flag
|
||||||
MfUltralightSupportAsciiMirror = 1 << 11,
|
MfUltralightSupportAsciiMirror = 1 << 11,
|
||||||
|
// NTAG203 counter that's in memory rather than through a command
|
||||||
|
MfUltralightSupportCounterInMemory = 1 << 12,
|
||||||
} MfUltralightFeatures;
|
} MfUltralightFeatures;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -173,6 +183,11 @@ bool mf_ultralight_read_version(
|
||||||
MfUltralightReader* reader,
|
MfUltralightReader* reader,
|
||||||
MfUltralightData* data);
|
MfUltralightData* data);
|
||||||
|
|
||||||
|
bool mf_ultralight_read_pages_direct(
|
||||||
|
FuriHalNfcTxRxContext* tx_rx,
|
||||||
|
uint8_t start_index,
|
||||||
|
uint8_t* data);
|
||||||
|
|
||||||
bool mf_ultralight_read_pages(
|
bool mf_ultralight_read_pages(
|
||||||
FuriHalNfcTxRxContext* tx_rx,
|
FuriHalNfcTxRxContext* tx_rx,
|
||||||
MfUltralightReader* reader,
|
MfUltralightReader* reader,
|
||||||
|
|
Loading…
Reference in a new issue