From bf15d3ce7450490ad294145c318772caa0c06990 Mon Sep 17 00:00:00 2001 From: AloneLiberty <111039319+AloneLiberty@users.noreply.github.com> Date: Wed, 12 Jul 2023 09:14:11 +0000 Subject: [PATCH] NFC: Improved MFC emulation on some readers (#2825) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * NFC: Improved MFC emulation on some readers * NFC: Improved emulation on some readers (part 2): Some Android devices don't like this * NFC: Improved emulation on some readers (part 3): I knew that during the emulation timings are critical, but one log breaks all... * NFC: Improved emulation on some readers (part 4): Add fixes to Detect reader and refactor code * NFC: Improved emulation on some readers (part 5) * NFC: Improved emulation on some readers (part 6): GUI doesn't update without delay * NFC: Improved emulation on some readers (part 7): Reworked emulation flow, some bug fixes and improvements Co-authored-by: あく Co-authored-by: gornekich --- lib/nfc/nfc_worker.c | 11 +- lib/nfc/protocols/mifare_classic.c | 165 ++++++++++++++++++++--------- lib/nfc/protocols/nfca.c | 12 +-- lib/nfc/protocols/nfca.h | 3 + 4 files changed, 127 insertions(+), 64 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index d2834fa4..145007bd 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -1022,7 +1022,9 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { furi_hal_nfc_listen_start(nfc_data); while(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { //-V1044 if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { - mf_classic_emulator(&emulator, &tx_rx, false); + if(!mf_classic_emulator(&emulator, &tx_rx, false)) { + furi_hal_nfc_listen_start(nfc_data); + } } } if(emulator.data_changed) { @@ -1297,8 +1299,6 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { bool reader_no_data_notified = true; while(nfc_worker->state == NfcWorkerStateAnalyzeReader) { - furi_hal_nfc_stop_cmd(); - furi_delay_ms(5); furi_hal_nfc_listen_start(nfc_data); if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { if(reader_no_data_notified) { @@ -1309,7 +1309,9 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { NfcProtocol protocol = reader_analyzer_guess_protocol(reader_analyzer, tx_rx.rx_data, tx_rx.rx_bits / 8); if(protocol == NfcDeviceProtocolMifareClassic) { - mf_classic_emulator(&emulator, &tx_rx, true); + if(!mf_classic_emulator(&emulator, &tx_rx, true)) { + furi_hal_nfc_listen_start(nfc_data); + } } } else { reader_no_data_received_cnt++; @@ -1321,6 +1323,7 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { FURI_LOG_D(TAG, "No data from reader"); continue; } + furi_delay_ms(1); } rfal_platform_spi_release(); diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index ebe49a4a..011747d5 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -851,16 +851,20 @@ bool mf_classic_emulator( bool is_reader_analyzer) { furi_assert(emulator); furi_assert(tx_rx); - bool command_processed = false; - bool is_encrypted = false; uint8_t plain_data[MF_CLASSIC_MAX_DATA_SIZE]; MfClassicKey access_key = MfClassicKeyA; + bool need_reset = false; + bool need_nack = false; + bool is_encrypted = false; + uint8_t sector = 0; + // Used for decrement and increment - copy to block on transfer - uint8_t transfer_buf[MF_CLASSIC_BLOCK_SIZE] = {}; + uint8_t transfer_buf[MF_CLASSIC_BLOCK_SIZE]; bool transfer_buf_valid = false; - // Read command - while(!command_processed) { //-V654 + // Process commands + while(!need_reset && !need_nack) { //-V654 + memset(plain_data, 0, MF_CLASSIC_MAX_DATA_SIZE); if(!is_encrypted) { crypto1_reset(&emulator->crypto); memcpy(plain_data, tx_rx->rx_data, tx_rx->rx_bits / 8); @@ -868,9 +872,10 @@ bool mf_classic_emulator( if(!furi_hal_nfc_tx_rx(tx_rx, 300)) { FURI_LOG_D( TAG, - "Error in tx rx. Tx :%d bits, Rx: %d bits", + "Error in tx rx. Tx: %d bits, Rx: %d bits", tx_rx->tx_bits, tx_rx->rx_bits); + need_reset = true; break; } crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); @@ -879,19 +884,28 @@ bool mf_classic_emulator( // After increment, decrement or restore the only allowed command is transfer uint8_t cmd = plain_data[0]; if(transfer_buf_valid && cmd != MF_CLASSIC_TRANSFER_CMD) { + need_nack = true; break; } - if(cmd == 0x50 && plain_data[1] == 0x00) { + if(cmd == NFCA_CMD_HALT && plain_data[1] == 0x00) { FURI_LOG_T(TAG, "Halt received"); - furi_hal_nfc_listen_sleep(); - command_processed = true; + need_reset = true; break; } + + if(cmd == NFCA_CMD_RATS) { + // Mifare Classic doesn't support ATS, NACK it and start listening again + FURI_LOG_T(TAG, "RATS received"); + need_nack = true; + break; + } + if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD || cmd == MF_CLASSIC_AUTH_KEY_B_CMD) { uint8_t block = plain_data[1]; uint64_t key = 0; uint8_t sector_trailer_block = mf_classic_get_sector_trailer_num_by_block(block); + sector = mf_classic_get_sector_by_block(block); MfClassicSectorTrailer* sector_trailer = (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD) { @@ -902,7 +916,7 @@ bool mf_classic_emulator( access_key = MfClassicKeyA; } else { FURI_LOG_D(TAG, "Key not known"); - command_processed = true; + need_nack = true; break; } } else { @@ -913,7 +927,7 @@ bool mf_classic_emulator( access_key = MfClassicKeyB; } else { FURI_LOG_D(TAG, "Key not known"); - command_processed = true; + need_nack = true; break; } } @@ -942,15 +956,15 @@ bool mf_classic_emulator( tx_rx->tx_bits = sizeof(nt) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; } + if(!furi_hal_nfc_tx_rx(tx_rx, 500)) { FURI_LOG_E(TAG, "Error in NT exchange"); - command_processed = true; + need_reset = true; break; } if(tx_rx->rx_bits != 64) { - FURI_LOG_W(TAG, "Incorrect nr + ar length: %d", tx_rx->rx_bits); - command_processed = true; + need_reset = true; break; } @@ -960,9 +974,14 @@ bool mf_classic_emulator( crypto1_word(&emulator->crypto, nr, 1); uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0); if(cardRr != prng_successor(nonce, 64)) { - FURI_LOG_T(TAG, "Wrong AUTH! %08lX != %08lX", cardRr, prng_successor(nonce, 64)); + FURI_LOG_T( + TAG, + "Wrong AUTH on block %u! %08lX != %08lX", + block, + cardRr, + prng_successor(nonce, 64)); // Don't send NACK, as the tag doesn't send it - command_processed = true; + need_reset = true; break; } @@ -985,11 +1004,25 @@ bool mf_classic_emulator( if(!is_encrypted) { FURI_LOG_T(TAG, "Invalid command before auth session established: %02X", cmd); + need_nack = true; break; } - if(cmd == MF_CLASSIC_READ_BLOCK_CMD) { - uint8_t block = plain_data[1]; + // Mifare Classic commands always have block number after command + uint8_t block = plain_data[1]; + if(mf_classic_get_sector_by_block(block) != sector) { + // Don't allow access to sectors other than authorized + FURI_LOG_T( + TAG, + "Trying to access block %u from not authorized sector (command: %02X)", + block, + cmd); + need_nack = true; + break; + } + + switch(cmd) { + case MF_CLASSIC_READ_BLOCK_CMD: { uint8_t block_data[MF_CLASSIC_BLOCK_SIZE + 2] = {}; memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); if(mf_classic_is_sector_trailer(block)) { @@ -1005,17 +1038,14 @@ bool mf_classic_emulator( emulator, block, access_key, MfClassicActionACRead)) { memset(&block_data[6], 0, 4); } - } else if(!mf_classic_is_allowed_access( - emulator, block, access_key, MfClassicActionDataRead)) { - // Send NACK - uint8_t nack = 0x04; - crypto1_encrypt( - &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - tx_rx->tx_bits = 4; - furi_hal_nfc_tx_rx(tx_rx, 300); + } else if( + !mf_classic_is_allowed_access( + emulator, block, access_key, MfClassicActionDataRead) || + !mf_classic_is_block_read(&emulator->data, block)) { + need_nack = true; break; } + nfca_append_crc16(block_data, 16); crypto1_encrypt( @@ -1027,23 +1057,36 @@ bool mf_classic_emulator( tx_rx->tx_parity); tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - } else if(cmd == MF_CLASSIC_WRITE_BLOCK_CMD) { - uint8_t block = plain_data[1]; - if(block > mf_classic_get_total_block_num(emulator->data.type)) { - break; - } + break; + } + + case MF_CLASSIC_WRITE_BLOCK_CMD: { // Send ACK uint8_t ack = MF_CLASSIC_ACK_CMD; crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; - if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; - if(tx_rx->rx_bits != (MF_CLASSIC_BLOCK_SIZE + 2) * 8) break; + if(!furi_hal_nfc_tx_rx(tx_rx, 300)) { + need_reset = true; + break; + } + + if(tx_rx->rx_bits != (MF_CLASSIC_BLOCK_SIZE + 2) * 8) { + need_reset = true; + break; + } crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); uint8_t block_data[MF_CLASSIC_BLOCK_SIZE] = {}; memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); + + if(!mf_classic_is_block_read(&emulator->data, block)) { + // Don't allow writing to the block for which we haven't read data yet + need_nack = true; + break; + } + if(mf_classic_is_sector_trailer(block)) { if(mf_classic_is_allowed_access( emulator, block, access_key, MfClassicActionKeyAWrite)) { @@ -1062,38 +1105,39 @@ bool mf_classic_emulator( emulator, block, access_key, MfClassicActionDataWrite)) { memcpy(block_data, plain_data, MF_CLASSIC_BLOCK_SIZE); } else { + need_nack = true; break; } } + if(memcmp(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE) != 0) { memcpy(emulator->data.block[block].value, block_data, MF_CLASSIC_BLOCK_SIZE); emulator->data_changed = true; } + // Send ACK ack = MF_CLASSIC_ACK_CMD; crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; - } else if( - cmd == MF_CLASSIC_DECREMENT_CMD || cmd == MF_CLASSIC_INCREMENT_CMD || - cmd == MF_CLASSIC_RESTORE_CMD) { - uint8_t block = plain_data[1]; + break; + } - if(block > mf_classic_get_total_block_num(emulator->data.type)) { - break; - } + case MF_CLASSIC_DECREMENT_CMD: + case MF_CLASSIC_INCREMENT_CMD: + case MF_CLASSIC_RESTORE_CMD: { + MfClassicAction action = (cmd == MF_CLASSIC_INCREMENT_CMD) ? MfClassicActionDataInc : + MfClassicActionDataDec; - MfClassicAction action = MfClassicActionDataDec; - if(cmd == MF_CLASSIC_INCREMENT_CMD) { - action = MfClassicActionDataInc; - } if(!mf_classic_is_allowed_access(emulator, block, access_key, action)) { + need_nack = true; break; } int32_t prev_value; uint8_t addr; if(!mf_classic_block_to_value(emulator->data.block[block].value, &prev_value, &addr)) { + need_nack = true; break; } @@ -1103,8 +1147,15 @@ bool mf_classic_emulator( tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; - if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; - if(tx_rx->rx_bits != (sizeof(int32_t) + 2) * 8) break; + if(!furi_hal_nfc_tx_rx(tx_rx, 300)) { + need_reset = true; + break; + } + + if(tx_rx->rx_bits != (sizeof(int32_t) + 2) * 8) { + need_reset = true; + break; + } crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); int32_t value = *(int32_t*)&plain_data[0]; @@ -1121,9 +1172,12 @@ bool mf_classic_emulator( transfer_buf_valid = true; // Commands do not ACK tx_rx->tx_bits = 0; - } else if(cmd == MF_CLASSIC_TRANSFER_CMD) { - uint8_t block = plain_data[1]; + break; + } + + case MF_CLASSIC_TRANSFER_CMD: { if(!mf_classic_is_allowed_access(emulator, block, access_key, MfClassicActionDataDec)) { + need_nack = true; break; } if(memcmp(transfer_buf, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE) != @@ -1137,13 +1191,17 @@ bool mf_classic_emulator( crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; - } else { + break; + } + + default: FURI_LOG_T(TAG, "Unknown command: %02X", cmd); + need_nack = true; break; } } - if(!command_processed) { + if(need_nack && !need_reset) { // Send NACK uint8_t nack = transfer_buf_valid ? MF_CLASSIC_NACK_BUF_VALID_CMD : MF_CLASSIC_NACK_BUF_INVALID_CMD; @@ -1155,15 +1213,16 @@ bool mf_classic_emulator( tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; furi_hal_nfc_tx_rx(tx_rx, 300); + need_reset = true; } - return true; + return !need_reset; } void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto) { furi_assert(tx_rx); - uint8_t plain_data[4] = {0x50, 0x00, 0x00, 0x00}; + uint8_t plain_data[4] = {NFCA_CMD_HALT, 0x00, 0x00, 0x00}; nfca_append_crc16(plain_data, 2); if(crypto) { diff --git a/lib/nfc/protocols/nfca.c b/lib/nfc/protocols/nfca.c index c401f8cc..ab4f3f23 100644 --- a/lib/nfc/protocols/nfca.c +++ b/lib/nfc/protocols/nfca.c @@ -3,8 +3,6 @@ #include #include -#define NFCA_CMD_RATS (0xE0U) - #define NFCA_CRC_INIT (0x6363) #define NFCA_F_SIG (13560000.0) @@ -22,7 +20,7 @@ typedef struct { static uint8_t nfca_default_ats[] = {0x05, 0x78, 0x80, 0x80, 0x00}; -static uint8_t nfca_sleep_req[] = {0x50, 0x00}; +static uint8_t nfca_halt_req[] = {NFCA_CMD_HALT, 0x00}; uint16_t nfca_get_crc16(uint8_t* buff, uint16_t len) { uint16_t crc = NFCA_CRC_INIT; @@ -50,17 +48,17 @@ bool nfca_emulation_handler( uint16_t buff_rx_len, uint8_t* buff_tx, uint16_t* buff_tx_len) { - bool sleep = false; + bool halt = false; uint8_t rx_bytes = buff_rx_len / 8; - if(rx_bytes == sizeof(nfca_sleep_req) && !memcmp(buff_rx, nfca_sleep_req, rx_bytes)) { - sleep = true; + if(rx_bytes == sizeof(nfca_halt_req) && !memcmp(buff_rx, nfca_halt_req, rx_bytes)) { + halt = true; } else if(rx_bytes == sizeof(nfca_cmd_rats) && buff_rx[0] == NFCA_CMD_RATS) { memcpy(buff_tx, nfca_default_ats, sizeof(nfca_default_ats)); *buff_tx_len = sizeof(nfca_default_ats) * 8; } - return sleep; + return halt; } static void nfca_add_bit(DigitalSignal* signal, bool bit) { diff --git a/lib/nfc/protocols/nfca.h b/lib/nfc/protocols/nfca.h index 498ef284..e4978a3e 100644 --- a/lib/nfc/protocols/nfca.h +++ b/lib/nfc/protocols/nfca.h @@ -5,6 +5,9 @@ #include +#define NFCA_CMD_RATS (0xE0U) +#define NFCA_CMD_HALT (0x50U) + typedef struct { DigitalSignal* one; DigitalSignal* zero;