diff --git a/applications/nfc/nfc.c b/applications/nfc/nfc.c index 4660b344..c4eaf900 100755 --- a/applications/nfc/nfc.c +++ b/applications/nfc/nfc.c @@ -173,6 +173,8 @@ int32_t nfc_app(void* p) { if(nfc_device_load(nfc->dev, p)) { if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); + } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } diff --git a/applications/nfc/nfc_device.c b/applications/nfc/nfc_device.c index 9970cbfe..0771a1ef 100644 --- a/applications/nfc/nfc_device.c +++ b/applications/nfc/nfc_device.c @@ -7,6 +7,9 @@ static const char* nfc_file_header = "Flipper NFC device"; static const uint32_t nfc_file_version = 2; +// Protocols format versions +static const uint32_t nfc_mifare_classic_data_format_version = 1; + NfcDevice* nfc_device_alloc() { NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); nfc_dev->storage = furi_record_open("storage"); @@ -624,6 +627,7 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* // Save Mifare Classic specific data do { if(!flipper_format_write_comment_cstr(file, "Mifare Classic specific data")) break; + if(data->type == MfClassicType1k) { if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; blocks = 64; @@ -631,8 +635,17 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break; blocks = 256; } - if(!flipper_format_write_comment_cstr(file, "Mifare Classic blocks")) break; + if(!flipper_format_write_uint32( + file, "Data format version", &nfc_mifare_classic_data_format_version, 1)) + break; + if(!flipper_format_write_comment_cstr( + file, "Key map is the bit mask indicating valid key in each sector")) + break; + if(!flipper_format_write_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; + if(!flipper_format_write_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; + + if(!flipper_format_write_comment_cstr(file, "Mifare Classic blocks")) break; bool block_saved = true; for(size_t i = 0; i < blocks; i++) { string_printf(temp_str, "Block %d", i); @@ -654,6 +667,7 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* bool parsed = false; MfClassicData* data = &dev->dev_data.mf_classic_data; string_t temp_str; + uint32_t data_format_version = 0; string_init(temp_str); uint16_t data_blocks = 0; @@ -669,6 +683,19 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* } else { break; } + + // Read Mifare Classic format version + if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { + // Load unread sectors with zero keys access for backward compatability + if(!flipper_format_rewind(file)) break; + data->key_a_mask = 0xffffffffffffffff; + data->key_b_mask = 0xffffffffffffffff; + } else { + if(data_format_version != nfc_mifare_classic_data_format_version) break; + if(!flipper_format_read_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; + if(!flipper_format_read_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; + } + // Read Mifare Classic blocks bool block_read = true; for(size_t i = 0; i < data_blocks; i++) { diff --git a/applications/nfc/nfc_worker.c b/applications/nfc/nfc_worker.c index 6b3c8f09..5ae99d6d 100644 --- a/applications/nfc/nfc_worker.c +++ b/applications/nfc/nfc_worker.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "helpers/nfc_mf_classic_dict.h" @@ -104,6 +105,8 @@ int32_t nfc_worker_task(void* context) { nfc_worker_emulate_mifare_ul(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { nfc_worker_mifare_classic_dict_attack(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) { + nfc_worker_emulate_mifare_classic(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { nfc_worker_read_mifare_desfire(nfc_worker); } @@ -474,6 +477,34 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) { stream_free(nfc_worker->dict_stream); } +void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + MfClassicEmulator emulator = { + .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), + .data = nfc_worker->dev_data->mf_classic_data, + .data_changed = false, + }; + NfcaSignal* nfca_signal = nfca_signal_alloc(); + tx_rx.nfca_signal = nfca_signal; + + while(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) { + if(furi_hal_nfc_listen( + nfc_data->uid, nfc_data->uid_len, nfc_data->atqa, nfc_data->sak, true, 300)) { + mf_classic_emulator(&emulator, &tx_rx); + } + } + if(emulator.data_changed) { + nfc_worker->dev_data->mf_classic_data = emulator.data; + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + } + emulator.data_changed = false; + } + + nfca_signal_free(nfca_signal); +} + void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { ReturnCode err; uint8_t tx_buff[64] = {}; diff --git a/applications/nfc/nfc_worker.h b/applications/nfc/nfc_worker.h index 1933a79b..a68f42d7 100755 --- a/applications/nfc/nfc_worker.h +++ b/applications/nfc/nfc_worker.h @@ -19,6 +19,7 @@ typedef enum { NfcWorkerStateReadMifareUltralight, NfcWorkerStateEmulateMifareUltralight, NfcWorkerStateReadMifareClassic, + NfcWorkerStateEmulateMifareClassic, NfcWorkerStateReadMifareDesfire, // Transition NfcWorkerStateStop, diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h index 6b5d5d10..e6351d42 100755 --- a/applications/nfc/scenes/nfc_scene_config.h +++ b/applications/nfc/scenes/nfc_scene_config.h @@ -34,4 +34,6 @@ ADD_SCENE(nfc, restore_original, RestoreOriginal) ADD_SCENE(nfc, debug, Debug) ADD_SCENE(nfc, field, Field) ADD_SCENE(nfc, read_mifare_classic, ReadMifareClassic) +ADD_SCENE(nfc, emulate_mifare_classic, EmulateMifareClassic) +ADD_SCENE(nfc, mifare_classic_menu, MifareClassicMenu) ADD_SCENE(nfc, dict_not_found, DictNotFound) diff --git a/applications/nfc/scenes/nfc_scene_emulate_mifare_classic.c b/applications/nfc/scenes/nfc_scene_emulate_mifare_classic.c new file mode 100644 index 00000000..1286024c --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_emulate_mifare_classic.c @@ -0,0 +1,64 @@ +#include "../nfc_i.h" +#include + +#define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL) +#define NFC_MF_CLASSIC_DATA_CHANGED (1UL) + +void nfc_emulate_mifare_classic_worker_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + Nfc* nfc = context; + + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateMifareClassic, NFC_MF_CLASSIC_DATA_CHANGED); +} + +void nfc_scene_emulate_mifare_classic_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + // Setup view + Popup* popup = nfc->popup; + if(strcmp(nfc->dev->dev_name, "")) { + nfc_text_store_set(nfc, "%s", nfc->dev->dev_name); + } + popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); + popup_set_header(popup, "Emulating\nMf Classic", 56, 31, AlignLeft, AlignTop); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + nfc_worker_start( + nfc->worker, + NfcWorkerStateEmulateMifareClassic, + &nfc->dev->dev_data, + nfc_emulate_mifare_classic_worker_callback, + nfc); +} + +bool nfc_scene_emulate_mifare_classic_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + notification_message(nfc->notifications, &sequence_blink_blue_10); + consumed = true; + } else if(event.type == SceneManagerEventTypeBack) { + // Stop worker + nfc_worker_stop(nfc->worker); + // Check if data changed and save in shadow file + if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateMifareClassic) == + NFC_MF_CLASSIC_DATA_CHANGED) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateMifareClassic, NFC_MF_CLASSIC_DATA_NOT_CHANGED); + nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); + } + consumed = false; + } + return consumed; +} + +void nfc_scene_emulate_mifare_classic_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/nfc/scenes/nfc_scene_mifare_classic_menu.c b/applications/nfc/scenes/nfc_scene_mifare_classic_menu.c new file mode 100644 index 00000000..4611d16b --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mifare_classic_menu.c @@ -0,0 +1,64 @@ +#include "../nfc_i.h" + +enum SubmenuIndex { + SubmenuIndexSave, + SubmenuIndexEmulate, +}; + +void nfc_scene_mifare_classic_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mifare_classic_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, "Save", SubmenuIndexSave, nfc_scene_mifare_classic_menu_submenu_callback, nfc); + submenu_add_item( + submenu, + "Emulate", + SubmenuIndexEmulate, + nfc_scene_mifare_classic_menu_submenu_callback, + nfc); + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareUlMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mifare_classic_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSave) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexSave); + nfc->dev->format = NfcDeviceSaveFormatMifareClassic; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate); + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); + } + + return consumed; +} + +void nfc_scene_mifare_classic_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/nfc/scenes/nfc_scene_read_mifare_classic.c b/applications/nfc/scenes/nfc_scene_read_mifare_classic.c index c6ce4c2d..c4422285 100644 --- a/applications/nfc/scenes/nfc_scene_read_mifare_classic.c +++ b/applications/nfc/scenes/nfc_scene_read_mifare_classic.c @@ -47,7 +47,7 @@ bool nfc_scene_read_mifare_classic_on_event(void* context, SceneManagerEvent eve consumed = true; } else if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventDictAttackDone) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareClassicMenu); consumed = true; } else if(event.event == NfcWorkerEventDetectedClassic1k) { dict_attack_card_detected(nfc->dict_attack, MfClassicType1k); @@ -71,7 +71,6 @@ bool nfc_scene_read_mifare_classic_on_event(void* context, SceneManagerEvent eve scene_manager_set_scene_state( nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateDone); notification_message(nfc->notifications, &sequence_success); - nfc->dev->format = NfcDeviceSaveFormatMifareClassic; dict_attack_set_result(nfc->dict_attack, true); consumed = true; } else if(event.event == NfcWorkerEventFail) { diff --git a/applications/nfc/scenes/nfc_scene_saved_menu.c b/applications/nfc/scenes/nfc_scene_saved_menu.c index e0489c15..1b435ccd 100644 --- a/applications/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/nfc/scenes/nfc_scene_saved_menu.c @@ -27,13 +27,11 @@ void nfc_scene_saved_menu_on_enter(void* context) { SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); - } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { + } else if( + nfc->dev->format == NfcDeviceSaveFormatMifareUl || + nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { submenu_add_item( - submenu, - "Emulate Ultralight", - SubmenuIndexEmulate, - nfc_scene_saved_menu_submenu_callback, - nfc); + submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); } submenu_add_item( submenu, "Edit UID and Name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc); @@ -64,6 +62,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexEmulate) { if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); + } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } diff --git a/applications/nfc/views/dict_attack.c b/applications/nfc/views/dict_attack.c index 83a56b0b..0f9da494 100644 --- a/applications/nfc/views/dict_attack.c +++ b/applications/nfc/views/dict_attack.c @@ -46,7 +46,7 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, draw_str); } else if(m->state == DictAttackStateSuccess) { canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Complete!"); - elements_button_right(canvas, "Save"); + elements_button_right(canvas, "More"); } else if(m->state == DictAttackStateFail) { canvas_draw_str_aligned( canvas, 64, 2, AlignCenter, AlignTop, "Failed to read any sector"); diff --git a/core/furi/common_defines.h b/core/furi/common_defines.h index 5b0e6971..5e9a7904 100644 --- a/core/furi/common_defines.h +++ b/core/furi/common_defines.h @@ -84,7 +84,7 @@ extern "C" { #endif #ifndef FURI_BIT -#define FURI_BIT(x, n) ((x) >> (n)&1) +#define FURI_BIT(x, n) (((x) >> (n)) & 1) #endif #ifndef FURI_IS_IRQ_MASKED diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 768a4bac..4391685e 100755 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -1,9 +1,12 @@ #include "furi_hal_nfc.h" #include +#include #include #include #include -#include + +#include +#include #define TAG "FuriHalNfc" @@ -394,6 +397,80 @@ ReturnCode furi_hal_nfc_data_exchange( return ret; } +static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { + furi_assert(tx_rx->nfca_signal); + + platformDisableIrqCallback(); + + bool ret = false; + + // Start transparent mode + st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); + // Reconfigure gpio + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + furi_hal_gpio_init(&gpio_spi_r_sck, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(&gpio_spi_r_miso, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(&gpio_nfc_cs, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(&gpio_spi_r_mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + + // Send signal + nfca_signal_encode(tx_rx->nfca_signal, tx_rx->tx_data, tx_rx->tx_bits, tx_rx->tx_parity); + digital_signal_send(tx_rx->nfca_signal->tx_signal, &gpio_spi_r_mosi); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + + // Configure gpio back to SPI and exit transparent + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); + + // Manually wait for interrupt + furi_hal_gpio_init(&gpio_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + st25r3916ClearAndEnableInterrupts(ST25R3916_IRQ_MASK_RXE); + + uint32_t irq = 0; + uint8_t rxe = 0; + uint32_t start = DWT->CYCCNT; + while(true) { + if(furi_hal_gpio_read(&gpio_rfid_pull) == true) { + st25r3916ReadRegister(ST25R3916_REG_IRQ_MAIN, &rxe); + if(rxe & (1 << 4)) { + irq = 1; + break; + } + } + uint32_t timeout = DWT->CYCCNT - start; + if(timeout / furi_hal_delay_instructions_per_microsecond() > timeout_ms * 1000) { + FURI_LOG_D(TAG, "Interrupt waiting timeout"); + break; + } + } + if(irq) { + uint8_t fifo_stat[2]; + st25r3916ReadMultipleRegisters( + ST25R3916_REG_FIFO_STATUS1, fifo_stat, ST25R3916_FIFO_STATUS_LEN); + uint16_t len = + ((((uint16_t)fifo_stat[1] & ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >> + ST25R3916_REG_FIFO_STATUS2_fifo_b_shift) + << RFAL_BITS_IN_BYTE); + len |= (((uint16_t)fifo_stat[0]) & 0x00FFU); + uint8_t rx[100]; + st25r3916ReadFifo(rx, len); + + tx_rx->rx_bits = len * 8; + memcpy(tx_rx->rx_data, rx, len); + + ret = true; + } else { + FURI_LOG_E(TAG, "Timeout error"); + ret = false; + } + + st25r3916ClearInterrupts(); + platformEnableIrqCallback(); + + return ret; +} + static uint32_t furi_hal_nfc_tx_rx_get_flag(FuriHalNfcTxRxType type) { uint32_t flags = 0; @@ -405,6 +482,9 @@ static uint32_t furi_hal_nfc_tx_rx_get_flag(FuriHalNfcTxRxType type) { } else if(type == FuriHalNfcTxRxTypeRaw) { flags = RFAL_TXRX_FLAGS_CRC_TX_MANUAL | RFAL_TXRX_FLAGS_CRC_RX_KEEP | RFAL_TXRX_FLAGS_PAR_RX_KEEP | RFAL_TXRX_FLAGS_PAR_TX_NONE; + } else if(type == FuriHalNfcTxRxTypeRxRaw) { + flags = RFAL_TXRX_FLAGS_CRC_TX_MANUAL | RFAL_TXRX_FLAGS_CRC_RX_KEEP | + RFAL_TXRX_FLAGS_PAR_RX_KEEP | RFAL_TXRX_FLAGS_PAR_TX_NONE; } return flags; @@ -470,6 +550,10 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { uint8_t* temp_rx_buff = NULL; uint16_t* temp_rx_bits = NULL; + if(tx_rx->tx_rx_type == FuriHalNfcTxRxTransparent) { + return furi_hal_nfc_transparent_tx_rx(tx_rx, timeout_ms); + } + // Prepare data for FIFO if necessary uint32_t flags = furi_hal_nfc_tx_rx_get_flag(tx_rx->tx_rx_type); if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw) { @@ -502,7 +586,8 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { osDelay(1); } - if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw) { + if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw || + tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRxRaw) { tx_rx->rx_bits = 8 * furi_hal_nfc_bitstream_to_data_and_parity( temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity); } else { diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index 20a46900..860db80d 100755 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -10,6 +10,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -39,6 +41,8 @@ typedef enum { FuriHalNfcTxRxTypeRxNoCrc, FuriHalNfcTxRxTypeRxKeepPar, FuriHalNfcTxRxTypeRaw, + FuriHalNfcTxRxTypeRxRaw, + FuriHalNfcTxRxTransparent, } FuriHalNfcTxRxType; typedef bool (*FuriHalNfcEmulateCallback)( @@ -80,6 +84,7 @@ typedef struct { uint8_t rx_parity[FURI_HAL_NFC_PARITY_BUFF_SIZE]; uint16_t rx_bits; FuriHalNfcTxRxType tx_rx_type; + NfcaSignal* nfca_signal; } FuriHalNfcTxRxContext; /** Init nfc diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c new file mode 100644 index 00000000..23ddaf90 --- /dev/null +++ b/lib/digital_signal/digital_signal.c @@ -0,0 +1,173 @@ +#include "digital_signal.h" + +#include +#include +#include +#include + +#define F_TIM (64000000.0) +#define T_TIM (1.0 / F_TIM) + +DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { + DigitalSignal* signal = malloc(sizeof(DigitalSignal)); + signal->start_level = true; + signal->edges_max_cnt = max_edges_cnt; + signal->edge_timings = malloc(max_edges_cnt * sizeof(float)); + signal->reload_reg_buff = malloc(max_edges_cnt * sizeof(uint32_t)); + signal->edge_cnt = 0; + + return signal; +} + +void digital_signal_free(DigitalSignal* signal) { + furi_assert(signal); + + free(signal->edge_timings); + free(signal->reload_reg_buff); + free(signal); +} + +bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) { + furi_assert(signal_a); + furi_assert(signal_b); + + if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) { + return false; + } + + bool end_level = signal_a->start_level; + if(signal_a->edge_cnt) { + end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2); + } + uint8_t start_copy = 0; + if(end_level == signal_b->start_level) { + if(signal_a->edge_cnt) { + signal_a->edge_timings[signal_a->edge_cnt - 1] += signal_b->edge_timings[0]; + start_copy += 1; + } else { + signal_a->edge_timings[signal_a->edge_cnt] += signal_b->edge_timings[0]; + } + } + memcpy( + &signal_a->edge_timings[signal_a->edge_cnt], + &signal_b->edge_timings[start_copy], + (signal_b->edge_cnt - start_copy) * sizeof(float)); + signal_a->edge_cnt += signal_b->edge_cnt - start_copy; + + return true; +} + +bool digital_signal_get_start_level(DigitalSignal* signal) { + furi_assert(signal); + + return signal->start_level; +} + +uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal) { + furi_assert(signal); + + return signal->edge_cnt; +} + +float digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { + furi_assert(signal); + furi_assert(edge_num < signal->edge_cnt); + + return signal->edge_timings[edge_num]; +} + +static void digital_signal_prepare_arr(DigitalSignal* signal) { + float t_signal = 0; + float t_current = 0; + float r = 0; + float r_int = 0; + float r_dec = 0; + + for(size_t i = 0; i < signal->edge_cnt - 1; i++) { + t_signal += signal->edge_timings[i]; + r = (t_signal - t_current) / T_TIM; + r_dec = modff(r, &r_int); + if(r_dec < 0.5f) { + signal->reload_reg_buff[i] = (uint32_t)r_int - 1; + } else { + signal->reload_reg_buff[i] = (uint32_t)r_int; + } + t_current += (signal->reload_reg_buff[i] + 1) * T_TIM; + } +} + +bool digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { + furi_assert(signal); + furi_assert(gpio); + + // Configure gpio as output + furi_hal_gpio_init(gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + // Init gpio buffer and DMA channel + uint16_t gpio_reg = gpio->port->ODR; + uint16_t gpio_buff[2]; + if(signal->start_level) { + gpio_buff[0] = gpio_reg | gpio->pin; + gpio_buff[1] = gpio_reg & ~(gpio->pin); + } else { + gpio_buff[0] = gpio_reg & ~(gpio->pin); + gpio_buff[1] = gpio_reg | gpio->pin; + } + LL_DMA_InitTypeDef dma_config = {}; + dma_config.MemoryOrM2MDstAddress = (uint32_t)gpio_buff; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->ODR); + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; + dma_config.NbData = 2; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + + // Init timer arr register buffer and DMA channel + digital_signal_prepare_arr(signal); + dma_config.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = signal->edge_cnt - 2; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_PRIORITY_HIGH; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->edge_cnt - 2); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + + // Set up timer + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetPrescaler(TIM2, 0); + LL_TIM_SetAutoReload(TIM2, 10); + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableUpdateEvent(TIM2); + LL_TIM_EnableDMAReq_UPDATE(TIM2); + + // Start transactions + LL_TIM_GenerateEvent_UPDATE(TIM2); // Do we really need it? + LL_TIM_EnableCounter(TIM2); + + while(!LL_DMA_IsActiveFlag_TC2(DMA1)) + ; + + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); + LL_TIM_DisableCounter(TIM2); + LL_TIM_SetCounter(TIM2, 0); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + + return true; +} diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h new file mode 100644 index 00000000..5e20e733 --- /dev/null +++ b/lib/digital_signal/digital_signal.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +#include + +typedef struct { + bool start_level; + uint32_t edge_cnt; + uint32_t edges_max_cnt; + float* edge_timings; + uint32_t* reload_reg_buff; +} DigitalSignal; + +DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); + +void digital_signal_free(DigitalSignal* signal); + +bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b); + +bool digital_signal_get_start_level(DigitalSignal* signal); + +uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal); + +float digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num); + +bool digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); diff --git a/lib/flipper_format/flipper_format.c b/lib/flipper_format/flipper_format.c index cee76701..df848ead 100644 --- a/lib/flipper_format/flipper_format.c +++ b/lib/flipper_format/flipper_format.c @@ -185,6 +185,37 @@ bool flipper_format_write_string_cstr( return result; } +bool flipper_format_read_hex_uint64( + FlipperFormat* flipper_format, + const char* key, + uint64_t* data, + const uint16_t data_size) { + furi_assert(flipper_format); + return flipper_format_stream_read_value_line( + flipper_format->stream, + key, + FlipperStreamValueHexUint64, + data, + data_size, + flipper_format->strict_mode); +} + +bool flipper_format_write_hex_uint64( + FlipperFormat* flipper_format, + const char* key, + const uint64_t* data, + const uint16_t data_size) { + furi_assert(flipper_format); + FlipperStreamWriteData write_data = { + .key = key, + .type = FlipperStreamValueHexUint64, + .data = data, + .data_size = data_size, + }; + bool result = flipper_format_stream_write_value_line(flipper_format->stream, &write_data); + return result; +} + bool flipper_format_read_uint32( FlipperFormat* flipper_format, const char* key, diff --git a/lib/flipper_format/flipper_format.h b/lib/flipper_format/flipper_format.h index 49e3f936..e32a5219 100644 --- a/lib/flipper_format/flipper_format.h +++ b/lib/flipper_format/flipper_format.h @@ -273,6 +273,34 @@ bool flipper_format_write_string_cstr( const char* key, const char* data); +/** + * Read array of uint64 in hex format by key + * @param flipper_format Pointer to a FlipperFormat instance + * @param key Key + * @param data Value + * @param data_size Values count + * @return True on success + */ +bool flipper_format_read_hex_uint64( + FlipperFormat* flipper_format, + const char* key, + uint64_t* data, + const uint16_t data_size); + +/** + * Write key and array of uint64 in hex format + * @param flipper_format Pointer to a FlipperFormat instance + * @param key Key + * @param data Value + * @param data_size Values count + * @return True on success + */ +bool flipper_format_write_hex_uint64( + FlipperFormat* flipper_format, + const char* key, + const uint64_t* data, + const uint16_t data_size); + /** * Read array of uint32 by key * @param flipper_format Pointer to a FlipperFormat instance diff --git a/lib/flipper_format/flipper_format_stream.c b/lib/flipper_format/flipper_format_stream.c index bd700849..5c210536 100644 --- a/lib/flipper_format/flipper_format_stream.c +++ b/lib/flipper_format/flipper_format_stream.c @@ -287,6 +287,11 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa const uint32_t* data = write_data->data; string_printf(value, "%" PRId32, data[i]); }; break; + case FlipperStreamValueHexUint64: { + const uint64_t* data = write_data->data; + string_printf( + value, "%08lX%08lX", (uint32_t)(data[i] >> 32), (uint32_t)data[i]); + }; break; case FlipperStreamValueBool: { const bool* data = write_data->data; string_printf(value, data[i] ? "true" : "false"); @@ -380,6 +385,14 @@ bool flipper_format_stream_read_value_line( uint32_t* data = _data; scan_values = sscanf(string_get_cstr(value), "%" PRId32, &data[i]); }; break; + case FlipperStreamValueHexUint64: { + uint64_t* data = _data; + if(string_size(value) >= 16) { + if(hex_chars_to_uint64(string_get_cstr(value), &data[i])) { + scan_values = 1; + } + } + }; break; case FlipperStreamValueBool: { bool* data = _data; data[i] = !string_cmpi_str(value, "true"); diff --git a/lib/flipper_format/flipper_format_stream.h b/lib/flipper_format/flipper_format_stream.h index 6ac17322..75eaef20 100644 --- a/lib/flipper_format/flipper_format_stream.h +++ b/lib/flipper_format/flipper_format_stream.h @@ -15,6 +15,7 @@ typedef enum { FlipperStreamValueFloat, FlipperStreamValueInt32, FlipperStreamValueUint32, + FlipperStreamValueHexUint64, FlipperStreamValueBool, } FlipperStreamValue; diff --git a/lib/lib.mk b/lib/lib.mk index cb58fade..43f4a1a2 100644 --- a/lib/lib.mk +++ b/lib/lib.mk @@ -95,6 +95,11 @@ C_SOURCES += $(wildcard $(LIB_DIR)/toolbox/*/*.c) CPP_SOURCES += $(wildcard $(LIB_DIR)/toolbox/*.cpp) CPP_SOURCES += $(wildcard $(LIB_DIR)/toolbox/*/*.cpp) +# Digital signal +CFLAGS += -I$(LIB_DIR)/digital_signal +C_SOURCES += $(wildcard $(LIB_DIR)/digital_signal/*.c) + + # USB Stack CFLAGS += -I$(LIB_DIR)/libusb_stm32/inc C_SOURCES += $(LIB_DIR)/libusb_stm32/src/usbd_stm32wb55_devfs.c diff --git a/lib/nfc_protocols/crypto1.c b/lib/nfc_protocols/crypto1.c index 469b0de0..f08164ba 100644 --- a/lib/nfc_protocols/crypto1.c +++ b/lib/nfc_protocols/crypto1.c @@ -58,7 +58,7 @@ uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) { return out; } -uint8_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) { +uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) { furi_assert(crypto1); uint32_t out = 0; for(uint8_t i = 0; i < 32; i++) { diff --git a/lib/nfc_protocols/crypto1.h b/lib/nfc_protocols/crypto1.h index aaa2470c..07b39c22 100644 --- a/lib/nfc_protocols/crypto1.h +++ b/lib/nfc_protocols/crypto1.h @@ -16,7 +16,7 @@ uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted); uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted); -uint8_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted); +uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted); uint32_t crypto1_filter(uint32_t in); diff --git a/lib/nfc_protocols/mifare_classic.c b/lib/nfc_protocols/mifare_classic.c index ace37bff..38c47127 100644 --- a/lib/nfc_protocols/mifare_classic.c +++ b/lib/nfc_protocols/mifare_classic.c @@ -1,6 +1,7 @@ #include "mifare_classic.h" #include "nfca.h" #include "nfc_util.h" +#include // Algorithm from https://github.com/RfidResearchGroup/proxmark3.git @@ -10,6 +11,20 @@ #define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U) #define MF_CLASSIC_READ_SECT_CMD (0x30) +typedef enum { + MfClassicActionDataRead, + MfClassicActionDataWrite, + MfClassicActionDataInc, + MfClassicActionDataDec, + + MfClassicActionKeyARead, + MfClassicActionKeyAWrite, + MfClassicActionKeyBRead, + MfClassicActionKeyBWrite, + MfClassicActionACRead, + MfClassicActionACWrite, +} MfClassicAction; + static uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) { furi_assert(sector < 40); if(sector < 32) { @@ -19,11 +34,31 @@ static uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) { } } +static uint8_t mf_classic_get_sector_by_block(uint8_t block) { + if(block < 128) { + return (block | 0x03) / 4; + } else { + return 32 + ((block | 0xf) - 32 * 4) / 16; + } +} + static uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) { furi_assert(sector < 40); return sector < 32 ? 4 : 16; } +static uint8_t mf_classic_get_sector_trailer(uint8_t block) { + if(block < 128) { + return block | 0x03; + } else { + return block | 0x0f; + } +} + +static bool mf_classic_is_sector_trailer(uint8_t block) { + return block == mf_classic_get_sector_trailer(block); +} + uint8_t mf_classic_get_total_sectors_num(MfClassicReader* reader) { furi_assert(reader); if(reader->type == MfClassicType1k) { @@ -35,6 +70,132 @@ uint8_t mf_classic_get_total_sectors_num(MfClassicReader* reader) { } } +static uint16_t mf_classic_get_total_block_num(MfClassicType type) { + if(type == MfClassicType1k) { + return 64; + } else if(type == MfClassicType4k) { + return 256; + } else { + return 0; + } +} + +static bool mf_classic_is_allowed_access_sector_trailer( + MfClassicEmulator* emulator, + uint8_t block_num, + MfClassicKey key, + MfClassicAction action) { + uint8_t* sector_trailer = emulator->data.block[block_num].value; + uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) | + ((sector_trailer[8] >> 7) & 0x01); + switch(action) { + case MfClassicActionKeyARead: { + return false; + } + case MfClassicActionKeyAWrite: { + return ( + (key == MfClassicKeyA && (AC == 0x00 || AC == 0x01)) || + (key == MfClassicKeyB && (AC == 0x04 || AC == 0x03))); + } + case MfClassicActionKeyBRead: { + return (key == MfClassicKeyA && (AC == 0x00 || AC == 0x02 || AC == 0x01)); + } + case MfClassicActionKeyBWrite: { + return ( + (key == MfClassicKeyA && (AC == 0x00 || AC == 0x01)) || + (key == MfClassicKeyB && (AC == 0x04 || AC == 0x03))); + } + case MfClassicActionACRead: { + return ( + (key == MfClassicKeyA) || + (key == MfClassicKeyB && !(AC == 0x00 || AC == 0x02 || AC == 0x01))); + } + case MfClassicActionACWrite: { + return ( + (key == MfClassicKeyA && (AC == 0x01)) || + (key == MfClassicKeyB && (AC == 0x03 || AC == 0x05))); + } + default: + return false; + } + return true; +} + +static bool mf_classic_is_allowed_access_data_block( + MfClassicEmulator* emulator, + uint8_t block_num, + MfClassicKey key, + MfClassicAction action) { + uint8_t* sector_trailer = emulator->data.block[mf_classic_get_sector_trailer(block_num)].value; + + uint8_t sector_block; + if(block_num <= 128) { + sector_block = block_num & 0x03; + } else { + sector_block = (block_num & 0x0f) / 5; + } + + uint8_t AC; + switch(sector_block) { + case 0x00: { + AC = ((sector_trailer[7] >> 2) & 0x04) | ((sector_trailer[8] << 1) & 0x02) | + ((sector_trailer[8] >> 4) & 0x01); + break; + } + case 0x01: { + AC = ((sector_trailer[7] >> 3) & 0x04) | ((sector_trailer[8] >> 0) & 0x02) | + ((sector_trailer[8] >> 5) & 0x01); + break; + } + case 0x02: { + AC = ((sector_trailer[7] >> 4) & 0x04) | ((sector_trailer[8] >> 1) & 0x02) | + ((sector_trailer[8] >> 6) & 0x01); + break; + } + default: + return false; + } + + switch(action) { + case MfClassicActionDataRead: { + return ( + (key == MfClassicKeyA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) || + (key == MfClassicKeyB && !(AC == 0x07))); + } + case MfClassicActionDataWrite: { + return ( + (key == MfClassicKeyA && (AC == 0x00)) || + (key == MfClassicKeyB && (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03))); + } + case MfClassicActionDataInc: { + return ( + (key == MfClassicKeyA && (AC == 0x00)) || + (key == MfClassicKeyB && (AC == 0x00 || AC == 0x06))); + } + case MfClassicActionDataDec: { + return ( + (key == MfClassicKeyA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) || + (key == MfClassicKeyB && (AC == 0x00 || AC == 0x06 || AC == 0x01))); + } + default: + return false; + } + + return false; +} + +static bool mf_classic_is_allowed_access( + MfClassicEmulator* emulator, + uint8_t block_num, + MfClassicKey key, + MfClassicAction action) { + if(mf_classic_is_sector_trailer(block_num)) { + return mf_classic_is_allowed_access_sector_trailer(emulator, block_num, key, action); + } else { + return mf_classic_is_allowed_access_data_block(emulator, block_num, key, action); + } +} + bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { UNUSED(ATQA1); if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08)) { @@ -120,7 +281,7 @@ static bool mf_classic_auth( tx_rx->tx_data[1] = block; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRxNoCrc; tx_rx->tx_bits = 2 * 8; - if(!furi_hal_nfc_tx_rx(tx_rx, 5)) break; + if(!furi_hal_nfc_tx_rx(tx_rx, 6)) break; uint32_t nt = (uint32_t)nfc_util_bytes2num(tx_rx->rx_data, 4); crypto1_init(crypto, key); @@ -142,7 +303,7 @@ static bool mf_classic_auth( } tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; tx_rx->tx_bits = 8 * 8; - if(!furi_hal_nfc_tx_rx(tx_rx, 5)) break; + if(!furi_hal_nfc_tx_rx(tx_rx, 6)) break; if(tx_rx->rx_bits == 32) { crypto1_word(crypto, 0, 0); auth_success = true; @@ -296,6 +457,8 @@ uint8_t mf_classic_read_card( uint8_t sectors_read = 0; data->type = reader->type; + data->key_a_mask = 0; + data->key_b_mask = 0; MfClassicSector temp_sector = {}; for(uint8_t i = 0; i < reader->sectors_to_read; i++) { if(mf_classic_read_sector( @@ -305,9 +468,279 @@ uint8_t mf_classic_read_card( for(uint8_t j = 0; j < temp_sector.total_blocks; j++) { data->block[first_block + j] = temp_sector.block[j]; } + if(reader->sector_reader[i].key_a != MF_CLASSIC_NO_KEY) { + data->key_a_mask |= 1 << reader->sector_reader[i].sector_num; + } + if(reader->sector_reader[i].key_b != MF_CLASSIC_NO_KEY) { + data->key_b_mask |= 1 << reader->sector_reader[i].sector_num; + } sectors_read++; } } return sectors_read; } + +void mf_crypto1_decrypt( + Crypto1* crypto, + uint8_t* encrypted_data, + uint16_t encrypted_data_bits, + uint8_t* decrypted_data) { + if(encrypted_data_bits < 8) { + uint8_t decrypted_byte = 0; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3; + decrypted_data[0] = decrypted_byte; + } else { + for(size_t i = 0; i < encrypted_data_bits / 8; i++) { + decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; + } + } +} + +void mf_crypto1_encrypt( + Crypto1* crypto, + uint8_t* keystream, + uint8_t* plain_data, + uint16_t plain_data_bits, + uint8_t* encrypted_data, + uint8_t* encrypted_parity) { + if(plain_data_bits < 8) { + encrypted_data[0] = 0; + for(size_t i = 0; i < plain_data_bits; i++) { + encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; + } + } else { + memset(encrypted_parity, 0, plain_data_bits / 8 + 1); + for(uint8_t i = 0; i < plain_data_bits / 8; i++) { + encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ + plain_data[i]; + encrypted_parity[i / 8] |= + (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01) + << (7 - (i & 0x0007))); + } + } +} + +bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) { + 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; + + // Read command + while(!command_processed) { + if(!is_encrypted) { + // Read first frame + tx_rx->tx_bits = 0; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + } + if(!furi_hal_nfc_tx_rx(tx_rx, 300)) { + FURI_LOG_D( + TAG, "Error in tx rx. Tx :%d bits, Rx: %d bits", tx_rx->tx_bits, tx_rx->rx_bits); + break; + } + if(!is_encrypted) { + memcpy(plain_data, tx_rx->rx_data, tx_rx->rx_bits / 8); + } else { + mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + } + // TODO Check crc + + if(plain_data[0] == 0x50 && plain_data[1] == 00) { + FURI_LOG_T(TAG, "Halt received"); + command_processed = true; + break; + } else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) { + uint8_t block = plain_data[1]; + uint64_t key = 0; + uint8_t sector_trailer_block = mf_classic_get_sector_trailer(block); + MfClassicSectorTrailer* sector_trailer = + (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; + if(plain_data[0] == 0x61) { + key = nfc_util_bytes2num(sector_trailer->key_b, 6); + access_key = MfClassicKeyA; + } else { + key = nfc_util_bytes2num(sector_trailer->key_a, 6); + access_key = MfClassicKeyB; + } + + uint32_t nonce = prng_successor(DWT->CYCCNT, 32); + uint8_t nt[4]; + uint8_t nt_keystream[4]; + nfc_util_num2bytes(nonce, 4, nt); + nfc_util_num2bytes(nonce ^ emulator->cuid, 4, nt_keystream); + crypto1_init(&emulator->crypto, key); + if(!is_encrypted) { + crypto1_word(&emulator->crypto, emulator->cuid ^ nonce, 0); + memcpy(tx_rx->tx_data, nt, sizeof(nt)); + tx_rx->tx_bits = sizeof(nt) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRxRaw; + } else { + mf_crypto1_encrypt( + &emulator->crypto, + nt_keystream, + nt, + sizeof(nt) * 8, + tx_rx->tx_data, + tx_rx->tx_parity); + 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; + break; + } + + if(tx_rx->rx_bits != 64) { + FURI_LOG_W(TAG, "Incorrect nr + ar"); + command_processed = true; + break; + } + + // Check if we store valid key + if(access_key == MfClassicKeyA) { + if(FURI_BIT(emulator->data.key_a_mask, mf_classic_get_sector_by_block(block)) == + 0) { + FURI_LOG_D(TAG, "Unsupported sector key A for block %d", sector_trailer_block); + break; + } + } else if(access_key == MfClassicKeyB) { + if(FURI_BIT(emulator->data.key_b_mask, mf_classic_get_sector_by_block(block)) == + 0) { + FURI_LOG_D(TAG, "Unsupported sector key B for block %d", sector_trailer_block); + break; + } + } + + uint32_t nr = nfc_util_bytes2num(tx_rx->rx_data, 4); + uint32_t ar = nfc_util_bytes2num(&tx_rx->rx_data[4], 4); + 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! %08X != %08X", cardRr, prng_successor(nonce, 64)); + // Don't send NACK, as tag don't send it + command_processed = true; + break; + } + + uint32_t ans = prng_successor(nonce, 96); + uint8_t responce[4] = {}; + nfc_util_num2bytes(ans, 4, responce); + mf_crypto1_encrypt( + &emulator->crypto, + NULL, + responce, + sizeof(responce) * 8, + tx_rx->tx_data, + tx_rx->tx_parity); + tx_rx->tx_bits = sizeof(responce) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + + is_encrypted = true; + } else if(is_encrypted && plain_data[0] == 0x30) { + uint8_t block = plain_data[1]; + uint8_t block_data[18] = {}; + memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); + if(mf_classic_is_sector_trailer(block)) { + if(!mf_classic_is_allowed_access( + emulator, block, access_key, MfClassicActionKeyARead)) { + memset(block_data, 0, 6); + } + if(!mf_classic_is_allowed_access( + emulator, block, access_key, MfClassicActionKeyBRead)) { + memset(&block_data[10], 0, 6); + } + if(!mf_classic_is_allowed_access( + emulator, block, access_key, MfClassicActionACRead)) { + memset(&block_data[6], 0, 4); + } + } else { + if(!mf_classic_is_allowed_access( + emulator, block, access_key, MfClassicActionDataRead)) { + memset(block_data, 0, 16); + } + } + nfca_append_crc16(block_data, 16); + + mf_crypto1_encrypt( + &emulator->crypto, + NULL, + block_data, + sizeof(block_data) * 8, + tx_rx->tx_data, + tx_rx->tx_parity); + tx_rx->tx_bits = 18 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + } else if(is_encrypted && plain_data[0] == 0xA0) { + uint8_t block = plain_data[1]; + if(block > mf_classic_get_total_block_num(emulator->data.type)) { + break; + } + // Send ACK + uint8_t ack = 0x0A; + mf_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 != 18 * 8) break; + + mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + uint8_t block_data[16] = {}; + memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); + if(mf_classic_is_sector_trailer(block)) { + if(mf_classic_is_allowed_access( + emulator, block, access_key, MfClassicActionKeyAWrite)) { + memcpy(block_data, plain_data, 6); + } + if(mf_classic_is_allowed_access( + emulator, block, access_key, MfClassicActionKeyBWrite)) { + memcpy(&block_data[10], &plain_data[10], 6); + } + if(mf_classic_is_allowed_access( + emulator, block, access_key, MfClassicActionACWrite)) { + memcpy(&block_data[6], &plain_data[6], 4); + } + } else { + if(mf_classic_is_allowed_access( + emulator, block, access_key, MfClassicActionDataWrite)) { + memcpy(block_data, plain_data, MF_CLASSIC_BLOCK_SIZE); + } + } + if(memcmp(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE)) { + memcpy(emulator->data.block[block].value, block_data, MF_CLASSIC_BLOCK_SIZE); + emulator->data_changed = true; + } + // Send ACK + ack = 0x0A; + mf_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 { + // Unknown command + break; + } + } + + if(!command_processed) { + // Send NACK + uint8_t nack = 0x04; + if(is_encrypted) { + mf_crypto1_encrypt( + &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); + } else { + tx_rx->tx_data[0] = nack; + } + tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_bits = 4; + furi_hal_nfc_tx_rx(tx_rx, 300); + } + + return true; +} diff --git a/lib/nfc_protocols/mifare_classic.h b/lib/nfc_protocols/mifare_classic.h index fa778b77..bbf34b2d 100644 --- a/lib/nfc_protocols/mifare_classic.h +++ b/lib/nfc_protocols/mifare_classic.h @@ -13,6 +13,7 @@ #define MF_CLASSIC_BLOCKS_IN_SECTOR_MAX (16) #define MF_CLASSIC_NO_KEY (0xFFFFFFFFFFFFFFFF) +#define MF_CLASSIC_MAX_DATA_SIZE (16) typedef enum { MfClassicType1k, @@ -41,6 +42,8 @@ typedef struct { typedef struct { MfClassicType type; + uint64_t key_a_mask; + uint64_t key_b_mask; MfClassicBlock block[MF_CLASSIC_TOTAL_BLOCKS_MAX]; } MfClassicData; @@ -65,6 +68,13 @@ typedef struct { MfClassicSectorReader sector_reader[MF_CLASSIC_SECTORS_MAX]; } MfClassicReader; +typedef struct { + uint32_t cuid; + Crypto1 crypto; + MfClassicData data; + bool data_changed; +} MfClassicEmulator; + bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); bool mf_classic_get_type( @@ -100,3 +110,5 @@ uint8_t mf_classic_read_card( FuriHalNfcTxRxContext* tx_rx, MfClassicReader* reader, MfClassicData* data); + +bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); diff --git a/lib/nfc_protocols/nfca.c b/lib/nfc_protocols/nfca.c index 81a6ddfc..e1cbdafe 100755 --- a/lib/nfc_protocols/nfca.c +++ b/lib/nfc_protocols/nfca.c @@ -1,11 +1,17 @@ #include "nfca.h" #include #include +#include #define NFCA_CMD_RATS (0xE0U) #define NFCA_CRC_INIT (0x6363) +#define NFCA_F_SIG (13560000.0) +#define NFCA_T_SIG (1.0 / NFCA_F_SIG) + +#define NFCA_SIGNAL_MAX_EDGES (1350) + typedef struct { uint8_t cmd; uint8_t param; @@ -53,3 +59,81 @@ bool nfca_emulation_handler( return sleep; } + +static void nfca_add_bit(DigitalSignal* signal, bool bit) { + if(bit) { + signal->start_level = true; + for(size_t i = 0; i < 7; i++) { + signal->edge_timings[i] = 8 * NFCA_T_SIG; + } + signal->edge_timings[7] = 9 * 8 * NFCA_T_SIG; + signal->edge_cnt = 8; + } else { + signal->start_level = false; + signal->edge_timings[0] = 8 * 8 * NFCA_T_SIG; + for(size_t i = 1; i < 9; i++) { + signal->edge_timings[i] = 8 * NFCA_T_SIG; + } + signal->edge_cnt = 9; + } +} + +static void nfca_add_byte(NfcaSignal* nfca_signal, uint8_t byte, bool parity) { + for(uint8_t i = 0; i < 8; i++) { + if(byte & (1 << i)) { + digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); + } else { + digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); + } + } + if(parity) { + digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); + } else { + digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); + } +} + +NfcaSignal* nfca_signal_alloc() { + NfcaSignal* nfca_signal = malloc(sizeof(NfcaSignal)); + nfca_signal->one = digital_signal_alloc(10); + nfca_signal->zero = digital_signal_alloc(10); + nfca_add_bit(nfca_signal->one, true); + nfca_add_bit(nfca_signal->zero, false); + nfca_signal->tx_signal = digital_signal_alloc(NFCA_SIGNAL_MAX_EDGES); + + return nfca_signal; +} + +void nfca_signal_free(NfcaSignal* nfca_signal) { + furi_assert(nfca_signal); + + digital_signal_free(nfca_signal->one); + digital_signal_free(nfca_signal->zero); + digital_signal_free(nfca_signal->tx_signal); + free(nfca_signal); +} + +void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, uint8_t* parity) { + furi_assert(nfca_signal); + furi_assert(data); + furi_assert(parity); + + nfca_signal->tx_signal->edge_cnt = 0; + nfca_signal->tx_signal->start_level = true; + // Start of frame + digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); + + if(bits < 8) { + for(size_t i = 0; i < bits; i++) { + if(FURI_BIT(data[0], i)) { + digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); + } else { + digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); + } + } + } else { + for(size_t i = 0; i < bits / 8; i++) { + nfca_add_byte(nfca_signal, data[i], parity[i / 8] & (1 << (7 - (i & 0x07)))); + } + } +} diff --git a/lib/nfc_protocols/nfca.h b/lib/nfc_protocols/nfca.h index 73e2e65e..498ef284 100644 --- a/lib/nfc_protocols/nfca.h +++ b/lib/nfc_protocols/nfca.h @@ -3,6 +3,14 @@ #include #include +#include + +typedef struct { + DigitalSignal* one; + DigitalSignal* zero; + DigitalSignal* tx_signal; +} NfcaSignal; + uint16_t nfca_get_crc16(uint8_t* buff, uint16_t len); void nfca_append_crc16(uint8_t* buff, uint16_t len); @@ -12,3 +20,9 @@ bool nfca_emulation_handler( uint16_t buff_rx_len, uint8_t* buff_tx, uint16_t* buff_tx_len); + +NfcaSignal* nfca_signal_alloc(); + +void nfca_signal_free(NfcaSignal* nfca_signal); + +void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, uint8_t* parity); diff --git a/lib/toolbox/hex.c b/lib/toolbox/hex.c index 739eb30b..41bb24bb 100644 --- a/lib/toolbox/hex.c +++ b/lib/toolbox/hex.c @@ -26,3 +26,14 @@ bool hex_chars_to_uint8(char hi, char low, uint8_t* value) { return false; } } + +bool hex_chars_to_uint64(const char* value_str, uint64_t* value) { + uint8_t* _value = (uint8_t*)value; + bool parse_success = false; + + for(uint8_t i = 0; i < 8; i++) { + parse_success = hex_chars_to_uint8(value_str[i * 2], value_str[i * 2 + 1], &_value[7 - i]); + if(!parse_success) break; + } + return parse_success; +} diff --git a/lib/toolbox/hex.h b/lib/toolbox/hex.h index ca10f2be..c6683a52 100644 --- a/lib/toolbox/hex.h +++ b/lib/toolbox/hex.h @@ -6,23 +6,31 @@ extern "C" { #endif -/** - * Convert ASCII hex value to nibble - * @param c ASCII character - * @param nibble nibble pointer, output - * @return bool conversion status +/** Convert ASCII hex value to nibble + * @param c ASCII character + * @param nibble nibble pointer, output + * + * @return bool conversion status */ bool hex_char_to_hex_nibble(char c, uint8_t* nibble); -/** - * Convert ASCII hex values to byte - * @param hi hi nibble text - * @param low low nibble text - * @param value output value - * @return bool conversion status +/** Convert ASCII hex values to byte + * @param hi hi nibble text + * @param low low nibble text + * @param value output value + * + * @return bool conversion status */ bool hex_chars_to_uint8(char hi, char low, uint8_t* value); +/** Convert ASCII hex values to uint64_t + * @param value_str ASCII 64 bi data + * @param value output value + * + * @return bool conversion status + */ +bool hex_chars_to_uint64(const char* value_str, uint64_t* value); + #ifdef __cplusplus } #endif