diff --git a/applications/nfc/nfc_types.c b/applications/nfc/nfc_types.c index 1b67284c..2d11c339 100644 --- a/applications/nfc/nfc_types.c +++ b/applications/nfc/nfc_types.c @@ -35,6 +35,14 @@ const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) { return "NTAG215"; } else if(type == MfUltralightTypeNTAG216) { return "NTAG216"; + } else if(type == MfUltralightTypeNTAGI2C1K) { + return "NTAG I2C 1K"; + } else if(type == MfUltralightTypeNTAGI2C2K) { + return "NTAG I2C 2K"; + } else if(type == MfUltralightTypeNTAGI2CPlus1K) { + return "NTAG I2C Plus 1K"; + } else if(type == MfUltralightTypeNTAGI2CPlus2K) { + return "NTAG I2C Plus 2K"; } else if(type == MfUltralightTypeUL11 && full_name) { return "Mifare Ultralight 11"; } else if(type == MfUltralightTypeUL21 && full_name) { diff --git a/applications/nfc/nfc_worker.c b/applications/nfc/nfc_worker.c index 5ae99d6d..88729bf4 100644 --- a/applications/nfc/nfc_worker.c +++ b/applications/nfc/nfc_worker.c @@ -315,6 +315,11 @@ void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) { MfUltralightEmulator emulator = {}; mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); while(nfc_worker->state == NfcWorkerStateEmulateMifareUltralight) { + emulator.auth_success = false; + if(emulator.data.type >= MfUltralightTypeNTAGI2C1K) { + // Sector index needs to be reset + emulator.curr_sector = 0; + } furi_hal_nfc_emulate_nfca( nfc_data->uid, nfc_data->uid_len, diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c old mode 100755 new mode 100644 index 4391685e..d1bdb5d7 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -1,3 +1,4 @@ +#include #include "furi_hal_nfc.h" #include #include @@ -337,6 +338,8 @@ bool furi_hal_nfc_emulate_nfca( break; } if(buff_tx_len) { + if(buff_tx_len == UINT16_MAX) buff_tx_len = 0; + ReturnCode ret = rfalTransceiveBitsBlockingTx( buff_tx, buff_tx_len, diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index 860db80d..30672fb9 100755 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -36,6 +36,10 @@ extern "C" { ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \ (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_NONE) +#define FURI_HAL_NFC_TX_RAW_RX_DEFAULT \ + ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_REMV | \ + (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_NONE) + typedef enum { FuriHalNfcTxRxTypeDefault, FuriHalNfcTxRxTypeRxNoCrc, diff --git a/lib/nfc_protocols/mifare_ultralight.c b/lib/nfc_protocols/mifare_ultralight.c index 5b3d70b5..fb425ea7 100644 --- a/lib/nfc_protocols/mifare_ultralight.c +++ b/lib/nfc_protocols/mifare_ultralight.c @@ -1,5 +1,7 @@ +#include #include "mifare_ultralight.h" #include +#include #define TAG "MfUltralight" @@ -16,6 +18,7 @@ static void mf_ul_set_default_version(MfUltralightReader* reader, MfUltralightDa reader->support_fast_read = false; reader->support_tearing_flags = false; reader->support_counters = false; + reader->support_signature = false; } bool mf_ultralight_read_version( @@ -44,30 +47,71 @@ bool mf_ultralight_read_version( reader->support_fast_read = true; reader->support_tearing_flags = true; reader->support_counters = true; + reader->support_signature = true; } else if(version->storage_size == 0x0E) { data->type = MfUltralightTypeUL21; reader->pages_to_read = 41; reader->support_fast_read = true; reader->support_tearing_flags = true; reader->support_counters = true; + reader->support_signature = true; } else if(version->storage_size == 0x0F) { data->type = MfUltralightTypeNTAG213; reader->pages_to_read = 45; reader->support_fast_read = true; reader->support_tearing_flags = false; reader->support_counters = false; + reader->support_signature = true; } else if(version->storage_size == 0x11) { data->type = MfUltralightTypeNTAG215; reader->pages_to_read = 135; reader->support_fast_read = true; reader->support_tearing_flags = false; reader->support_counters = false; + reader->support_signature = true; + } else if(version->prod_subtype == 5 && version->prod_ver_major == 2) { + // NTAG I2C + bool known = false; + if(version->prod_ver_minor == 1) { + if(version->storage_size == 0x13) { + data->type = MfUltralightTypeNTAGI2C1K; + reader->pages_to_read = 231; + reader->support_signature = false; + known = true; + } else if(version->storage_size == 0x15) { + data->type = MfUltralightTypeNTAGI2C2K; + reader->pages_to_read = 485; + reader->support_signature = false; + known = true; + } + } else if(version->prod_ver_minor == 2) { + if(version->storage_size == 0x13) { + data->type = MfUltralightTypeNTAGI2CPlus1K; + reader->pages_to_read = 236; + reader->support_signature = true; + known = true; + } else if(version->storage_size == 0x15) { + data->type = MfUltralightTypeNTAGI2CPlus2K; + reader->pages_to_read = 492; + reader->support_signature = true; + known = true; + } + } + + if(known) { + reader->support_fast_read = true; + reader->support_tearing_flags = false; + reader->support_counters = false; + } else { + mf_ul_set_default_version(reader, data); + } } else if(version->storage_size == 0x13) { data->type = MfUltralightTypeNTAG216; reader->pages_to_read = 231; reader->support_fast_read = true; reader->support_tearing_flags = false; reader->support_counters = false; + reader->support_signature = true; } else { mf_ul_set_default_version(reader, data); break; @@ -78,26 +122,351 @@ bool mf_ultralight_read_version( return version_read; } +static int16_t mf_ultralight_ntag_i2c_addr_lin_to_tag_1k( + int16_t linear_address, + uint8_t* sector, + int16_t* valid_pages) { + // 0 - 226: sector 0 + // 227 - 228: config registers + // 229 - 230: session registers + + if(linear_address > 230) { + *valid_pages = 0; + return -1; + } else if(linear_address >= 229) { + *sector = 3; + *valid_pages = 2 - (linear_address - 229); + return linear_address - 229 + 248; + } else if(linear_address >= 227) { + *sector = 0; + *valid_pages = 2 - (linear_address - 227); + return linear_address - 227 + 232; + } else { + *sector = 0; + *valid_pages = 227 - linear_address; + return linear_address; + } +} + +static int16_t mf_ultralight_ntag_i2c_addr_lin_to_tag_2k( + int16_t linear_address, + uint8_t* sector, + int16_t* valid_pages) { + // 0 - 255: sector 0 + // 256 - 480: sector 1 + // 481 - 482: config registers + // 483 - 484: session registers + + if(linear_address > 484) { + *valid_pages = 0; + return -1; + } else if(linear_address >= 483) { + *sector = 3; + *valid_pages = 2 - (linear_address - 483); + return linear_address - 483 + 248; + } else if(linear_address >= 481) { + *sector = 1; + *valid_pages = 2 - (linear_address - 481); + return linear_address - 481 + 232; + } else if(linear_address >= 256) { + *sector = 1; + *valid_pages = 225 - (linear_address - 256); + return linear_address - 256; + } else { + *sector = 0; + *valid_pages = 256 - linear_address; + return linear_address; + } +} + +static int16_t mf_ultralight_ntag_i2c_addr_lin_to_tag_plus_1k( + int16_t linear_address, + uint8_t* sector, + int16_t* valid_pages) { + // 0 - 233: sector 0 + registers + // 234 - 235: session registers + + if(linear_address > 235) { + *valid_pages = 0; + return -1; + } else if(linear_address >= 234) { + *sector = 0; + *valid_pages = 2 - (linear_address - 234); + return linear_address - 234 + 236; + } else { + *sector = 0; + *valid_pages = 234 - linear_address; + return linear_address; + } +} + +static int16_t mf_ultralight_ntag_i2c_addr_lin_to_tag_plus_2k( + int16_t linear_address, + uint8_t* sector, + int16_t* valid_pages) { + // 0 - 233: sector 0 + registers + // 234 - 235: session registers + // 236 - 491: sector 1 + + if(linear_address > 491) { + *valid_pages = 0; + return -1; + } else if(linear_address >= 236) { + *sector = 1; + *valid_pages = 256 - (linear_address - 236); + return linear_address - 236; + } else if(linear_address >= 234) { + *sector = 0; + *valid_pages = 2 - (linear_address - 234); + return linear_address - 234 + 236; + } else { + *sector = 0; + *valid_pages = 234 - linear_address; + return linear_address; + } +} + +static int16_t mf_ultralight_ntag_i2c_addr_lin_to_tag( + MfUltralightData* data, + MfUltralightReader* reader, + int16_t linear_address, + uint8_t* sector, + int16_t* valid_pages) { + switch(data->type) { + case MfUltralightTypeNTAGI2C1K: + return mf_ultralight_ntag_i2c_addr_lin_to_tag_1k(linear_address, sector, valid_pages); + + case MfUltralightTypeNTAGI2C2K: + return mf_ultralight_ntag_i2c_addr_lin_to_tag_2k(linear_address, sector, valid_pages); + + case MfUltralightTypeNTAGI2CPlus1K: + return mf_ultralight_ntag_i2c_addr_lin_to_tag_plus_1k(linear_address, sector, valid_pages); + + case MfUltralightTypeNTAGI2CPlus2K: + return mf_ultralight_ntag_i2c_addr_lin_to_tag_plus_2k(linear_address, sector, valid_pages); + + default: + *sector = 0xff; + *valid_pages = reader->pages_to_read - linear_address; + return linear_address; + } +} + +static int16_t + mf_ultralight_ntag_i2c_addr_tag_to_lin_1k(uint8_t page, uint8_t sector, uint16_t* valid_pages) { + bool valid = false; + int16_t translated_page; + if(sector == 0) { + if(page <= 226) { + *valid_pages = 227 - page; + translated_page = page; + valid = true; + } else if(page >= 232 && page <= 233) { + *valid_pages = 2 - (page - 232); + translated_page = page - 232 + 227; + valid = true; + } + } else if(sector == 3) { + if(page >= 248 && page <= 249) { + *valid_pages = 2 - (page - 248); + translated_page = page - 248 + 229; + valid = true; + } + } + + if(!valid) { + *valid_pages = 0; + translated_page = -1; + } + return translated_page; +} + +static int16_t + mf_ultralight_ntag_i2c_addr_tag_to_lin_2k(uint8_t page, uint8_t sector, uint16_t* valid_pages) { + bool valid = false; + int16_t translated_page; + if(sector == 0) { + *valid_pages = 256 - page; + translated_page = page; + valid = true; + } else if(sector == 1) { + if(page <= 224) { + *valid_pages = 225 - page; + translated_page = 256 + page; + valid = true; + } else if(page >= 232 && page <= 233) { + *valid_pages = 2 - (page - 232); + translated_page = page - 232 + 481; + valid = true; + } + } else if(sector == 3) { + if(page >= 248 && page <= 249) { + *valid_pages = 2 - (page - 248); + translated_page = page - 248 + 483; + valid = true; + } + } + + if(!valid) { + *valid_pages = 0; + translated_page = -1; + } + return translated_page; +} + +static int16_t mf_ultralight_ntag_i2c_addr_tag_to_lin_plus_1k( + uint8_t page, + uint8_t sector, + uint16_t* valid_pages) { + bool valid = false; + int16_t translated_page; + if(sector == 0) { + if(page <= 233) { + *valid_pages = 234 - page; + translated_page = page; + valid = true; + } else if(page >= 236 && page <= 237) { + *valid_pages = 2 - (page - 236); + translated_page = page - 236 + 234; + valid = true; + } + } else if(sector == 3) { + if(page >= 248 && page <= 249) { + *valid_pages = 2 - (page - 248); + translated_page = page - 248 + 234; + valid = true; + } + } + + if(!valid) { + *valid_pages = 0; + translated_page = -1; + } + return translated_page; +} + +static int16_t mf_ultralight_ntag_i2c_addr_tag_to_lin_plus_2k( + uint8_t page, + uint8_t sector, + uint16_t* valid_pages) { + bool valid = false; + int16_t translated_page; + if(sector == 0) { + if(page <= 233) { + *valid_pages = 234 - page; + translated_page = page; + valid = true; + } else if(page >= 236 && page <= 237) { + *valid_pages = 2 - (page - 236); + translated_page = page - 236 + 234; + valid = true; + } + } else if(sector == 1) { + *valid_pages = 256 - page; + translated_page = page + 236; + valid = true; + } else if(sector == 3) { + if(page >= 248 && page <= 249) { + *valid_pages = 2 - (page - 248); + translated_page = page - 248 + 234; + valid = true; + } + } + + if(!valid) { + *valid_pages = 0; + translated_page = -1; + } + return translated_page; +} + +static int16_t mf_ultralight_ntag_i2c_addr_tag_to_lin( + MfUltralightData* data, + uint8_t page, + uint8_t sector, + uint16_t* valid_pages) { + switch(data->type) { + case MfUltralightTypeNTAGI2C1K: + return mf_ultralight_ntag_i2c_addr_tag_to_lin_1k(page, sector, valid_pages); + + case MfUltralightTypeNTAGI2C2K: + return mf_ultralight_ntag_i2c_addr_tag_to_lin_2k(page, sector, valid_pages); + + case MfUltralightTypeNTAGI2CPlus1K: + return mf_ultralight_ntag_i2c_addr_tag_to_lin_plus_1k(page, sector, valid_pages); + + case MfUltralightTypeNTAGI2CPlus2K: + return mf_ultralight_ntag_i2c_addr_tag_to_lin_plus_2k(page, sector, valid_pages); + + default: + *valid_pages = data->data_size / 4 - page; + return page; + } +} + +static bool mf_ultralight_sector_select(FuriHalNfcTxRxContext* tx_rx, uint8_t sector) { + FURI_LOG_D(TAG, "Selecting sector %u", sector); + tx_rx->tx_data[0] = MF_UL_SECTOR_SELECT; + tx_rx->tx_data[1] = 0xff; + tx_rx->tx_bits = 16; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { + FURI_LOG_D(TAG, "Failed to issue sector select command"); + return false; + } + + tx_rx->tx_data[0] = sector; + tx_rx->tx_data[1] = 0x00; + tx_rx->tx_data[2] = 0x00; + tx_rx->tx_data[3] = 0x00; + tx_rx->tx_bits = 32; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + // This is NOT a typo! The tag ACKs by not sending a response within 1ms. + if(furi_hal_nfc_tx_rx(tx_rx, 20)) { + // TODO: what gets returned when an actual NAK is received? + FURI_LOG_D(TAG, "Sector %u select NAK'd", sector); + return false; + } + + return true; +} + bool mf_ultralight_read_pages( FuriHalNfcTxRxContext* tx_rx, MfUltralightReader* reader, MfUltralightData* data) { uint8_t pages_read_cnt = 0; + uint8_t curr_sector_index = 0xff; + reader->pages_read = 0; + for(size_t i = 0; i < reader->pages_to_read; i += pages_read_cnt) { + uint8_t tag_sector; + int16_t valid_pages; + int16_t tag_page = mf_ultralight_ntag_i2c_addr_lin_to_tag( + data, reader, (int16_t)i, &tag_sector, &valid_pages); - for(size_t i = 0; i < reader->pages_to_read; i += 4) { - FURI_LOG_D(TAG, "Reading pages %d - %d", i, i + 3); + furi_assert(tag_page != -1); + if(curr_sector_index != tag_sector) { + if(!mf_ultralight_sector_select(tx_rx, tag_sector)) return false; + curr_sector_index = tag_sector; + } + + FURI_LOG_D(TAG, "Reading pages %d - %d", i, i + (valid_pages > 4 ? 4 : valid_pages) - 1); tx_rx->tx_data[0] = MF_UL_READ_CMD; - tx_rx->tx_data[1] = i; + tx_rx->tx_data[1] = tag_page; tx_rx->tx_bits = 16; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { - FURI_LOG_D(TAG, "Failed to read pages %d - %d", i, i + 3); + FURI_LOG_D( + TAG, + "Failed to read pages %d - %d", + i, + i + (valid_pages > 4 ? 4 : valid_pages) - 1); break; } - if(i + 4 <= reader->pages_to_read) { + if(valid_pages > 4) { pages_read_cnt = 4; } else { - pages_read_cnt = reader->pages_to_read - reader->pages_read; + pages_read_cnt = valid_pages; } reader->pages_read += pages_read_cnt; data->data_size = reader->pages_read * 4; @@ -111,18 +480,39 @@ bool mf_ultralight_fast_read_pages( FuriHalNfcTxRxContext* tx_rx, MfUltralightReader* reader, MfUltralightData* data) { - FURI_LOG_D(TAG, "Reading pages 0 - %d", reader->pages_to_read); - tx_rx->tx_data[0] = MF_UL_FAST_READ_CMD; - tx_rx->tx_data[1] = 0; - tx_rx->tx_data[2] = reader->pages_to_read - 1; - tx_rx->tx_bits = 24; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - if(furi_hal_nfc_tx_rx(tx_rx, 50)) { - reader->pages_read = reader->pages_to_read; - data->data_size = reader->pages_read * 4; - memcpy(data->data, tx_rx->rx_data, data->data_size); - } else { - FURI_LOG_D(TAG, "Failed to read pages 0 - %d", reader->pages_to_read); + uint8_t curr_sector_index = 0xff; + reader->pages_read = 0; + while(reader->pages_read < reader->pages_to_read) { + uint8_t tag_sector; + int16_t valid_pages; + int16_t tag_page = mf_ultralight_ntag_i2c_addr_lin_to_tag( + data, reader, reader->pages_read, &tag_sector, &valid_pages); + + furi_assert(tag_page != -1); + if(curr_sector_index != tag_sector) { + if(!mf_ultralight_sector_select(tx_rx, tag_sector)) return false; + curr_sector_index = tag_sector; + } + + FURI_LOG_D( + TAG, "Reading pages %d - %d", reader->pages_read, reader->pages_read + valid_pages - 1); + tx_rx->tx_data[0] = MF_UL_FAST_READ_CMD; + tx_rx->tx_data[1] = tag_page; + tx_rx->tx_data[2] = valid_pages - 1; + tx_rx->tx_bits = 24; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + memcpy(&data->data[reader->pages_read * 4], tx_rx->rx_data, valid_pages * 4); + reader->pages_read += valid_pages; + data->data_size = reader->pages_read * 4; + } else { + FURI_LOG_D( + TAG, + "Failed to read pages %d - %d", + reader->pages_read, + reader->pages_read + valid_pages - 1); + break; + } } return reader->pages_read == reader->pages_to_read; @@ -199,8 +589,10 @@ bool mf_ul_read_card( // Read Mifare Ultralight version if(mf_ultralight_read_version(tx_rx, reader, data)) { - // Read Signature - mf_ultralight_read_signature(tx_rx, data); + if(reader->support_signature) { + // Read Signature + mf_ultralight_read_signature(tx_rx, data); + } } if(reader->support_counters) { mf_ultralight_read_counters(tx_rx, data); @@ -231,11 +623,123 @@ static void mf_ul_protect_auth_data_on_read_command( } } +static void mf_ul_protect_auth_data_on_read_command_i2c( + uint8_t* tx_buff, + uint8_t start_page, + uint8_t end_page, + MfUltralightEmulator* emulator) { + if(emulator->data.type >= MfUltralightTypeNTAGI2CPlus1K) { + // Blank out PWD and PACK + if(start_page <= 229 && end_page >= 229) { + uint16_t offset = (229 - start_page) * 4; + uint8_t count = 4; + if(end_page >= 230) count += 2; + memset(&tx_buff[offset], 0, count); + } + + // Handle AUTH0 for sector 0 + if(!emulator->auth_success) { + uint8_t access = emulator->data.data[228 * 4]; + if(access & 0x80) { + uint8_t auth0 = emulator->data.data[227 * 4 + 3]; + if(auth0 < end_page) { + // start_page is always < auth0; otherwise is NAK'd already + uint8_t page_offset = auth0 - start_page; + uint8_t page_count = end_page - auth0; + memset(&tx_buff[page_offset * 4], 0, page_count * 4); + } + } + } + } +} + +static void mf_ul_ntag_i2c_fill_cross_area_read( + uint8_t* tx_buff, + uint8_t start_page, + uint8_t end_page, + MfUltralightEmulator* emulator) { + // For copying config or session registers in fast read + int16_t tx_page_offset; + int16_t data_page_offset; + uint8_t page_length; + bool apply = false; + MfUltralightType type = emulator->data.type; + if(emulator->curr_sector == 0) { + if(type == MfUltralightTypeNTAGI2C1K) { + if(start_page <= 233 && end_page >= 232) { + tx_page_offset = start_page - 232; + data_page_offset = 227; + page_length = 2; + apply = true; + } + } else if(type == MfUltralightTypeNTAGI2CPlus1K || type == MfUltralightTypeNTAGI2CPlus2K) { + if(start_page <= 237 && end_page >= 236) { + tx_page_offset = start_page - 236; + data_page_offset = 234; + page_length = 2; + apply = true; + } + } + } else if(emulator->curr_sector == 1) { + if(type == MfUltralightTypeNTAGI2C2K) { + if(start_page <= 233 && end_page >= 232) { + tx_page_offset = start_page - 232; + data_page_offset = 483; + page_length = 2; + apply = true; + } + } + } + + if(apply) { + while(tx_page_offset < 0 && page_length > 0) { + ++tx_page_offset; + ++data_page_offset; + --page_length; + } + memcpy( + &tx_buff[tx_page_offset * 4], + &emulator->data.data[data_page_offset * 4], + page_length * 4); + } +} + +static bool mf_ul_ntag_i2c_plus_check_auth( + MfUltralightEmulator* emulator, + uint8_t start_page, + bool is_write) { + if(!emulator->auth_success) { + uint8_t access = emulator->data.data[228 * 4]; + // Check NFC_PROT + if(emulator->curr_sector == 0 && ((access & 0x80) || is_write)) { + uint8_t auth0 = emulator->data.data[227 * 4 + 3]; + if(start_page >= auth0) return false; + } else if(emulator->curr_sector == 1) { + // We don't have to specifically check for type because this is done + // by address translator + uint8_t pt_i2c = emulator->data.data[231 * 4]; + // Check 2K_PROT + if(pt_i2c & 0x08) return false; + } + } + + if(emulator->curr_sector == 1) { + // Check NFC_DIS_SEC1 + uint8_t access = emulator->data.data[228 * 4]; + if(access & 0x20) return false; + } + + return true; +} + void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* data) { + FURI_LOG_D(TAG, "Prepare emulation"); emulator->data = *data; emulator->auth_data = NULL; emulator->data_changed = false; emulator->comp_write_cmd_started = false; + emulator->sector_select_cmd_started = false; + emulator->ntag_i2c_plus_sector3_lockout = false; if(data->type == MfUltralightTypeUnknown) { emulator->support_fast_read = false; } else if(data->type == MfUltralightTypeUL11) { @@ -248,11 +752,15 @@ void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* d emulator->support_fast_read = true; } else if(data->type == MfUltralightTypeNTAG216) { emulator->support_fast_read = true; + } else if(data->type >= MfUltralightTypeNTAGI2C1K) { + emulator->support_fast_read = true; } - if(data->type >= MfUltralightTypeNTAG213) { + if(data->type >= MfUltralightTypeNTAG213 && data->type < MfUltralightTypeNTAGI2C1K) { uint16_t pwd_page = (data->data_size / 4) - 2; emulator->auth_data = (MfUltralightAuth*)&data->data[pwd_page * 4]; + } else if(data->type >= MfUltralightTypeNTAGI2CPlus1K) { + emulator->auth_data = (MfUltralightAuth*)&data->data[229 * 4]; } } @@ -270,6 +778,19 @@ bool mf_ul_prepare_emulation_response( uint16_t tx_bytes = 0; uint16_t tx_bits = 0; bool command_parsed = false; + bool send_ack = false; + bool respond_nothing = false; + +#ifdef FURI_DEBUG + string_t debug_buf; + string_init(debug_buf); + for(int i = 0; i < (buff_rx_len + 7) / 8; ++i) { + string_cat_printf(debug_buf, "%02x ", buff_rx[i]); + } + string_strim(debug_buf); + FURI_LOG_T(TAG, "Emu RX (%d): %s", buff_rx_len, string_get_cstr(debug_buf)); + string_reset(debug_buf); +#endif // Check composite commands if(emulator->comp_write_cmd_started) { @@ -284,6 +805,15 @@ bool mf_ul_prepare_emulation_response( command_parsed = true; } emulator->comp_write_cmd_started = false; + } else if(emulator->sector_select_cmd_started) { + if(buff_rx[0] <= 0xFE) { + emulator->curr_sector = buff_rx[0] > 3 ? 0 : buff_rx[0]; + emulator->ntag_i2c_plus_sector3_lockout = false; + command_parsed = true; + respond_nothing = true; + FURI_LOG_D(TAG, "Changing sector to %d", emulator->curr_sector); + } + emulator->sector_select_cmd_started = false; } else if(cmd == MF_UL_GET_VERSION_CMD) { if(emulator->data.type != MfUltralightTypeUnknown) { tx_bytes = sizeof(emulator->data.version); @@ -292,85 +822,189 @@ bool mf_ul_prepare_emulation_response( command_parsed = true; } } else if(cmd == MF_UL_READ_CMD) { - uint8_t start_page = buff_rx[1]; - if(start_page < page_num) { - tx_bytes = 16; - if(start_page + 4 > page_num) { - // Handle roll-over mechanism - uint8_t end_pages_num = page_num - start_page; - memcpy(buff_tx, &emulator->data.data[start_page * 4], end_pages_num * 4); - memcpy(&buff_tx[end_pages_num * 4], emulator->data.data, (4 - end_pages_num) * 4); - } else { - memcpy(buff_tx, &emulator->data.data[start_page * 4], tx_bytes); + int16_t start_page = buff_rx[1]; + tx_bytes = 16; + if(emulator->data.type < MfUltralightTypeNTAGI2C1K) { + if(start_page < page_num) { + if(start_page + 4 > page_num) { + // Handle roll-over mechanism + uint8_t end_pages_num = page_num - start_page; + memcpy(buff_tx, &emulator->data.data[start_page * 4], end_pages_num * 4); + memcpy( + &buff_tx[end_pages_num * 4], emulator->data.data, (4 - end_pages_num) * 4); + } else { + memcpy(buff_tx, &emulator->data.data[start_page * 4], tx_bytes); + } + mf_ul_protect_auth_data_on_read_command( + buff_tx, start_page, (start_page + 4), emulator); + *data_type = FURI_HAL_NFC_TXRX_DEFAULT; + command_parsed = true; + } + } else { + uint16_t valid_pages; + start_page = mf_ultralight_ntag_i2c_addr_tag_to_lin( + &emulator->data, start_page, emulator->curr_sector, &valid_pages); + if(start_page != -1) { + if(emulator->data.type < MfUltralightTypeNTAGI2CPlus1K || + mf_ul_ntag_i2c_plus_check_auth(emulator, buff_rx[1], false)) { + if(emulator->data.type >= MfUltralightTypeNTAGI2CPlus1K && + emulator->curr_sector == 3 && valid_pages == 1) { + // Rewind back a sector to match behavior on a real tag + --start_page; + ++valid_pages; + } + + uint16_t copy_count = (valid_pages > 4 ? 4 : valid_pages) * 4; + FURI_LOG_D( + TAG, + "NTAG I2C Emu: page valid, %02x:%02x -> %d, %d", + emulator->curr_sector, + buff_rx[1], + start_page, + valid_pages); + memcpy(buff_tx, &emulator->data.data[start_page * 4], copy_count); + // For NTAG I2C, there's no roll-over; remainder is filled by null bytes + if(copy_count < tx_bytes) + memset(&buff_tx[copy_count], 0, tx_bytes - copy_count); + // Special case: NTAG I2C Plus sector 0 page 233 read crosses into page 236 + if(start_page == 233) + memcpy(&buff_tx[12], &emulator->data.data[(start_page + 1) * 4], 4); + mf_ul_protect_auth_data_on_read_command_i2c( + buff_tx, start_page, start_page + copy_count / 4 - 1, emulator); + *data_type = FURI_HAL_NFC_TXRX_DEFAULT; + command_parsed = true; + } + } else { + FURI_LOG_D( + TAG, + "NTAG I2C Emu: page invalid, %02x:%02x", + emulator->curr_sector, + buff_rx[1]); + if(emulator->data.type >= MfUltralightTypeNTAGI2CPlus1K && + emulator->curr_sector == 3 && !emulator->ntag_i2c_plus_sector3_lockout) { + // NTAG I2C Plus has a weird behavior where if you read sector 3 + // at an invalid address, it responds with zeroes then locks + // the read out, while if you read the mirrored session registers, + // it returns both session registers on either pages + memset(buff_tx, 0, tx_bytes); + *data_type = FURI_HAL_NFC_TXRX_DEFAULT; + command_parsed = true; + emulator->ntag_i2c_plus_sector3_lockout = true; + } } - mf_ul_protect_auth_data_on_read_command( - buff_tx, start_page, (start_page + 4), emulator); - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; } + if(!command_parsed) tx_bytes = 0; } else if(cmd == MF_UL_FAST_READ_CMD) { if(emulator->support_fast_read) { - uint8_t start_page = buff_rx[1]; + int16_t start_page = buff_rx[1]; uint8_t end_page = buff_rx[2]; - if((start_page < page_num) && (end_page < page_num) && (start_page < (end_page + 1))) { + if(start_page <= end_page) { tx_bytes = ((end_page + 1) - start_page) * 4; - memcpy(buff_tx, &emulator->data.data[start_page * 4], tx_bytes); - mf_ul_protect_auth_data_on_read_command(buff_tx, start_page, end_page, emulator); + if(emulator->data.type < MfUltralightTypeNTAGI2C1K) { + if((start_page < page_num) && (end_page < page_num)) { + memcpy(buff_tx, &emulator->data.data[start_page * 4], tx_bytes); + mf_ul_protect_auth_data_on_read_command( + buff_tx, start_page, end_page, emulator); + *data_type = FURI_HAL_NFC_TXRX_DEFAULT; + command_parsed = true; + } + } else { + uint16_t valid_pages; + start_page = mf_ultralight_ntag_i2c_addr_tag_to_lin( + &emulator->data, start_page, emulator->curr_sector, &valid_pages); + if(start_page != -1) { + if(emulator->data.type < MfUltralightTypeNTAGI2CPlus1K || + mf_ul_ntag_i2c_plus_check_auth(emulator, buff_rx[1], false)) { + uint16_t copy_count = (valid_pages > 4 ? 4 : valid_pages) * 4; + memcpy(buff_tx, &emulator->data.data[start_page * 4], copy_count); + if(copy_count < tx_bytes) + memset(&buff_tx[copy_count], 0, tx_bytes - copy_count); + mf_ul_ntag_i2c_fill_cross_area_read( + buff_tx, buff_rx[1], buff_rx[2], emulator); + mf_ul_protect_auth_data_on_read_command_i2c( + buff_tx, start_page, start_page + copy_count / 4 - 1, emulator); + *data_type = FURI_HAL_NFC_TXRX_DEFAULT; + command_parsed = true; + } + } + } + if(!command_parsed) tx_bytes = 0; + } + } + } else if(cmd == MF_UL_WRITE) { + int16_t write_page = buff_rx[1]; + if(write_page > 1) { + uint16_t valid_pages; + write_page = mf_ultralight_ntag_i2c_addr_tag_to_lin( + &emulator->data, write_page, emulator->curr_sector, &valid_pages); + if(write_page != -1 && + (emulator->data.type >= MfUltralightTypeNTAGI2C1K || (write_page < page_num - 2))) { + if(emulator->data.type < MfUltralightTypeNTAGI2CPlus1K || + mf_ul_ntag_i2c_plus_check_auth(emulator, buff_rx[1], true)) { + memcpy(&emulator->data.data[write_page * 4], &buff_rx[2], 4); + emulator->data_changed = true; + send_ack = true; + command_parsed = true; + } + } + } + } else if(cmd == MF_UL_FAST_WRITE) { + if(emulator->data.type >= MfUltralightTypeNTAGI2CPlus1K) { + if(buff_rx[1] == 0xF0 && buff_rx[2] == 0xFF) { + // TODO: update when SRAM emulation implemented + send_ack = true; + command_parsed = true; + } + } + } else if(cmd == MF_UL_COMP_WRITE) { + if(emulator->data.type < MfUltralightTypeNTAGI2C1K) { + uint8_t write_page = buff_rx[1]; + if((write_page > 1) && (write_page < page_num - 2)) { + emulator->comp_write_cmd_started = true; + emulator->comp_write_page_addr = write_page; + // ACK + buff_tx[0] = 0x0A; + tx_bits = 4; + *data_type = FURI_HAL_NFC_TXRX_RAW; + command_parsed = true; + } + } + } else if(cmd == MF_UL_READ_CNT) { + if(emulator->data.type < MfUltralightTypeNTAGI2C1K) { + uint8_t cnt_num = buff_rx[1]; + if(cnt_num < 3) { + buff_tx[0] = emulator->data.counter[cnt_num] >> 16; + buff_tx[1] = emulator->data.counter[cnt_num] >> 8; + buff_tx[2] = emulator->data.counter[cnt_num]; + tx_bytes = 3; *data_type = FURI_HAL_NFC_TXRX_DEFAULT; command_parsed = true; } } - } else if(cmd == MF_UL_WRITE) { - uint8_t write_page = buff_rx[1]; - if((write_page > 1) && (write_page < page_num - 2)) { - memcpy(&emulator->data.data[write_page * 4], &buff_rx[2], 4); - emulator->data_changed = true; - // ACK - buff_tx[0] = 0x0A; - tx_bits = 4; - *data_type = FURI_HAL_NFC_TXRX_RAW; - command_parsed = true; - } - } else if(cmd == MF_UL_COMP_WRITE) { - uint8_t write_page = buff_rx[1]; - if((write_page > 1) && (write_page < page_num - 2)) { - emulator->comp_write_cmd_started = true; - emulator->comp_write_page_addr = write_page; - // ACK - buff_tx[0] = 0x0A; - tx_bits = 4; - *data_type = FURI_HAL_NFC_TXRX_RAW; - command_parsed = true; - } - } else if(cmd == MF_UL_READ_CNT) { - uint8_t cnt_num = buff_rx[1]; - if(cnt_num < 3) { - buff_tx[0] = emulator->data.counter[cnt_num] >> 16; - buff_tx[1] = emulator->data.counter[cnt_num] >> 8; - buff_tx[2] = emulator->data.counter[cnt_num]; - tx_bytes = 3; - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; - } } else if(cmd == MF_UL_INC_CNT) { - uint8_t cnt_num = buff_rx[1]; - uint32_t inc = (buff_rx[2] | (buff_rx[3] << 8) | (buff_rx[4] << 16)); - if((cnt_num < 3) && (emulator->data.counter[cnt_num] + inc < 0x00FFFFFF)) { - emulator->data.counter[cnt_num] += inc; - emulator->data_changed = true; - // ACK - buff_tx[0] = 0x0A; - tx_bits = 4; - *data_type = FURI_HAL_NFC_TXRX_RAW; - command_parsed = true; + if(emulator->data.type < MfUltralightTypeNTAGI2C1K) { + uint8_t cnt_num = buff_rx[1]; + uint32_t inc = (buff_rx[2] | (buff_rx[3] << 8) | (buff_rx[4] << 16)); + if((cnt_num < 3) && (emulator->data.counter[cnt_num] + inc < 0x00FFFFFF)) { + emulator->data.counter[cnt_num] += inc; + emulator->data_changed = true; + // ACK + buff_tx[0] = 0x0A; + tx_bits = 4; + *data_type = FURI_HAL_NFC_TXRX_RAW; + command_parsed = true; + } } } else if(cmd == MF_UL_AUTH) { - if(emulator->data.type >= MfUltralightTypeNTAG213) { + if(emulator->data.type >= MfUltralightTypeNTAG213 && + emulator->data.type != MfUltralightTypeNTAGI2C1K && + emulator->data.type != MfUltralightTypeNTAGI2C2K) { if(memcmp(&buff_rx[1], emulator->auth_data->pwd, 4) == 0) { buff_tx[0] = emulator->auth_data->pack.raw[0]; buff_tx[1] = emulator->auth_data->pack.raw[1]; tx_bytes = 2; *data_type = FURI_HAL_NFC_TXRX_DEFAULT; + emulator->auth_success = true; command_parsed = true; } else if(!emulator->auth_data->pack.value) { buff_tx[0] = 0x80; @@ -381,36 +1015,81 @@ bool mf_ul_prepare_emulation_response( } } } else if(cmd == MF_UL_READ_SIG) { - // Check 2nd byte = 0x00 - RFU - if(buff_rx[1] == 0x00) { - tx_bytes = sizeof(emulator->data.signature); - memcpy(buff_tx, emulator->data.signature, tx_bytes); - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; + if(emulator->data.type != MfUltralightTypeNTAGI2C1K && + emulator->data.type != MfUltralightTypeNTAGI2C2K) { + // Check 2nd byte = 0x00 - RFU + if(buff_rx[1] == 0x00) { + tx_bytes = sizeof(emulator->data.signature); + memcpy(buff_tx, emulator->data.signature, tx_bytes); + *data_type = FURI_HAL_NFC_TXRX_DEFAULT; + command_parsed = true; + } } } else if(cmd == MF_UL_CHECK_TEARING) { - uint8_t cnt_num = buff_rx[1]; - if(cnt_num < 3) { - buff_tx[0] = emulator->data.tearing[cnt_num]; - tx_bytes = 1; - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; + if(emulator->data.type < MfUltralightTypeNTAGI2C1K) { + uint8_t cnt_num = buff_rx[1]; + if(cnt_num < 3) { + buff_tx[0] = emulator->data.tearing[cnt_num]; + tx_bytes = 1; + *data_type = FURI_HAL_NFC_TXRX_DEFAULT; + command_parsed = true; + } } } else if(cmd == MF_UL_HALT_START) { tx_bits = 0; + emulator->curr_sector = 0; + emulator->ntag_i2c_plus_sector3_lockout = false; + emulator->auth_success = false; command_parsed = true; + FURI_LOG_D(TAG, "Received HLTA"); + } else if(cmd == MF_UL_SECTOR_SELECT) { + if(emulator->data.type >= MfUltralightTypeNTAGI2C1K) { + if(buff_rx[1] == 0xFF) { + // Send ACK + emulator->sector_select_cmd_started = true; + send_ack = true; + command_parsed = true; + } + } } if(!command_parsed) { // Send NACK buff_tx[0] = 0x00; tx_bits = 4; - *data_type = FURI_HAL_NFC_TXRX_RAW; + *data_type = FURI_HAL_NFC_TX_RAW_RX_DEFAULT; + } else if(send_ack) { + buff_tx[0] = 0x0A; + tx_bits = 4; + *data_type = FURI_HAL_NFC_TX_RAW_RX_DEFAULT; } - // Return tx buffer size in bits - if(tx_bytes) { - tx_bits = tx_bytes * 8; + + if(respond_nothing) { + *buff_tx_len = UINT16_MAX; + *data_type = FURI_HAL_NFC_TX_RAW_RX_DEFAULT; + } else { + // Return tx buffer size in bits + if(tx_bytes) { + tx_bits = tx_bytes * 8; + } + *buff_tx_len = tx_bits; } - *buff_tx_len = tx_bits; + +#ifdef FURI_DEBUG + if(*buff_tx_len == UINT16_MAX) { + FURI_LOG_T(TAG, "Emu TX: no reply"); + } else if(*buff_tx_len > 0) { + int count = (*buff_tx_len + 7) / 8; + for(int i = 0; i < count; ++i) { + string_cat_printf(debug_buf, "%02x ", buff_tx[i]); + } + string_strim(debug_buf); + FURI_LOG_T(TAG, "Emu TX (%d): %s", *buff_tx_len, string_get_cstr(debug_buf)); + string_clear(debug_buf); + } else { + FURI_LOG_T(TAG, "Emu TX: HALT"); + } +#endif + return tx_bits > 0; } diff --git a/lib/nfc_protocols/mifare_ultralight.h b/lib/nfc_protocols/mifare_ultralight.h index 858fc3ec..76dc2edb 100644 --- a/lib/nfc_protocols/mifare_ultralight.h +++ b/lib/nfc_protocols/mifare_ultralight.h @@ -2,7 +2,8 @@ #include -#define MF_UL_MAX_DUMP_SIZE 1024 +// Largest tag is NTAG I2C Plus 2K, both data sectors plus SRAM +#define MF_UL_MAX_DUMP_SIZE ((238 + 256 + 16) * 4) #define MF_UL_TEARING_FLAG_DEFAULT (0xBD) @@ -11,6 +12,7 @@ #define MF_UL_READ_CMD (0x30) #define MF_UL_FAST_READ_CMD (0x3A) #define MF_UL_WRITE (0xA2) +#define MF_UL_FAST_WRITE (0xA6) #define MF_UL_COMP_WRITE (0xA0) #define MF_UL_READ_CNT (0x39) #define MF_UL_INC_CNT (0xA5) @@ -18,6 +20,7 @@ #define MF_UL_READ_SIG (0x3C) #define MF_UL_CHECK_TEARING (0x3E) #define MF_UL_READ_VCSL (0x4B) +#define MF_UL_SECTOR_SELECT (0xC2) typedef enum { MfUltralightTypeUnknown, @@ -26,6 +29,10 @@ typedef enum { MfUltralightTypeNTAG213, MfUltralightTypeNTAG215, MfUltralightTypeNTAG216, + MfUltralightTypeNTAGI2C1K, + MfUltralightTypeNTAGI2C2K, + MfUltralightTypeNTAGI2CPlus1K, + MfUltralightTypeNTAGI2CPlus2K, // Keep last for number of types calculation MfUltralightTypeNum, @@ -71,11 +78,12 @@ typedef struct { } MfUltralightAuth; typedef struct { - uint8_t pages_to_read; - uint8_t pages_read; + uint16_t pages_to_read; + int16_t pages_read; bool support_fast_read; bool support_tearing_flags; bool support_counters; + bool support_signature; } MfUltralightReader; typedef struct { @@ -85,6 +93,10 @@ typedef struct { bool comp_write_cmd_started; uint8_t comp_write_page_addr; MfUltralightAuth* auth_data; + bool auth_success; + uint8_t curr_sector; + bool sector_select_cmd_started; + bool ntag_i2c_plus_sector3_lockout; } MfUltralightEmulator; bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK);