diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c old mode 100755 new mode 100644 index 4a7b6f659..38aadc49b --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -91,11 +91,16 @@ static void bt_battery_level_changed_callback(const void* _event, void* context) furi_assert(context); Bt* bt = context; + BtMessage message = {}; const PowerEvent* event = _event; if(event->type == PowerEventTypeBatteryLevelChanged) { - BtMessage message = { - .type = BtMessageTypeUpdateBatteryLevel, - .data.battery_level = event->data.battery_level}; + message.type = BtMessageTypeUpdateBatteryLevel; + message.data.battery_level = event->data.battery_level; + furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); + } else if( + event->type == PowerEventTypeStartCharging || event->type == PowerEventTypeFullyCharged || + event->type == PowerEventTypeStopCharging) { + message.type = BtMessageTypeUpdatePowerState; furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); } } @@ -167,7 +172,11 @@ static void bt_rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t byt furi_assert(context); Bt* bt = context; - osEventFlagsClear(bt->rpc_event, BT_RPC_EVENT_ALL); + if(osEventFlagsGet(bt->rpc_event) & BT_RPC_EVENT_DISCONNECTED) { + // Early stop from sending if we're already disconnected + return; + } + osEventFlagsClear(bt->rpc_event, BT_RPC_EVENT_ALL & (~BT_RPC_EVENT_DISCONNECTED)); size_t bytes_sent = 0; while(bytes_sent < bytes_len) { size_t bytes_remain = bytes_len - bytes_sent; @@ -178,10 +187,14 @@ static void bt_rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t byt furi_hal_bt_serial_tx(&bytes[bytes_sent], bytes_remain); bytes_sent += bytes_remain; } - uint32_t event_flag = - osEventFlagsWait(bt->rpc_event, BT_RPC_EVENT_ALL, osFlagsWaitAny, osWaitForever); + // We want BT_RPC_EVENT_DISCONNECTED to stick, so don't clear + uint32_t event_flag = osEventFlagsWait( + bt->rpc_event, BT_RPC_EVENT_ALL, osFlagsWaitAny | osFlagsNoClear, osWaitForever); if(event_flag & BT_RPC_EVENT_DISCONNECTED) { break; + } else { + // If we didn't get BT_RPC_EVENT_DISCONNECTED, then clear everything else + osEventFlagsClear(bt->rpc_event, BT_RPC_EVENT_ALL & (~BT_RPC_EVENT_DISCONNECTED)); } } } @@ -197,6 +210,8 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { bt->status = BtStatusConnected; BtMessage message = {.type = BtMessageTypeUpdateStatus}; furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); + // Clear BT_RPC_EVENT_DISCONNECTED because it might be set from previous session + osEventFlagsClear(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); if(bt->profile == BtProfileSerial) { // Open RPC session bt->rpc_session = rpc_session_open(bt->rpc); @@ -368,6 +383,8 @@ int32_t bt_srv() { } else if(message.type == BtMessageTypeUpdateBatteryLevel) { // Update battery level furi_hal_bt_update_battery_level(message.data.battery_level); + } else if(message.type == BtMessageTypeUpdatePowerState) { + furi_hal_bt_update_power_state(); } else if(message.type == BtMessageTypePinCodeShow) { // Display PIN code bt_pin_code_show(bt, message.data.pin_code); diff --git a/applications/bt/bt_service/bt_i.h b/applications/bt/bt_service/bt_i.h index 610c09056..caa45360a 100644 --- a/applications/bt/bt_service/bt_i.h +++ b/applications/bt/bt_service/bt_i.h @@ -21,6 +21,7 @@ typedef enum { BtMessageTypeUpdateStatus, BtMessageTypeUpdateBatteryLevel, + BtMessageTypeUpdatePowerState, BtMessageTypePinCodeShow, BtMessageTypeKeysStorageUpdated, BtMessageTypeSetProfile, diff --git a/applications/gpio/gpio_custom_event.h b/applications/gpio/gpio_custom_event.h index cae360357..2bf3e5a8b 100644 --- a/applications/gpio/gpio_custom_event.h +++ b/applications/gpio/gpio_custom_event.h @@ -3,7 +3,7 @@ typedef enum { GpioStartEventOtgOff = 0, GpioStartEventOtgOn, - GpioStartEventManualConrol, + GpioStartEventManualControl, GpioStartEventUsbUart, GpioCustomEventErrorBack, diff --git a/applications/gpio/scenes/gpio_scene_start.c b/applications/gpio/scenes/gpio_scene_start.c index 4df74114a..41b745233 100644 --- a/applications/gpio/scenes/gpio_scene_start.c +++ b/applications/gpio/scenes/gpio_scene_start.c @@ -23,7 +23,7 @@ static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t ind furi_assert(context); GpioApp* app = context; if(index == GpioItemTest) { - view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventManualConrol); + view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventManualControl); } else if(index == GpioItemUsbUart) { view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventUsbUart); } @@ -82,7 +82,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { furi_hal_power_enable_otg(); } else if(event.event == GpioStartEventOtgOff) { furi_hal_power_disable_otg(); - } else if(event.event == GpioStartEventManualConrol) { + } else if(event.event == GpioStartEventManualControl) { scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemTest); scene_manager_next_scene(app->scene_manager, GpioSceneTest); } else if(event.event == GpioStartEventUsbUart) { diff --git a/applications/nfc/nfc.c b/applications/nfc/nfc.c index 4660b344f..c4eaf900b 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 9970cbfed..63f0c3ccb 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"); @@ -192,6 +195,10 @@ static bool nfc_device_save_mifare_df_key_settings( string_printf(key, "%s Key Changeable", prefix); if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->master_key_changeable, 1)) break; + if(ks->flags) { + string_printf(key, "%s Flags", prefix); + if(!flipper_format_write_hex(file, string_get_cstr(key), &ks->flags, 1)) break; + } string_printf(key, "%s Max Keys", prefix); if(!flipper_format_write_hex(file, string_get_cstr(key), &ks->max_keys, 1)) break; for(MifareDesfireKeyVersion* kv = ks->key_version_head; kv; kv = kv->next) { @@ -227,8 +234,14 @@ bool nfc_device_load_mifare_df_key_settings( string_printf(key, "%s Key Changeable", prefix); if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->master_key_changeable, 1)) break; + string_printf(key, "%s Flags", prefix); + if(flipper_format_key_exist(file, string_get_cstr(key))) { + if(!flipper_format_read_hex(file, string_get_cstr(key), &ks->flags, 1)) break; + } string_printf(key, "%s Max Keys", prefix); if(!flipper_format_read_hex(file, string_get_cstr(key), &ks->max_keys, 1)) break; + ks->flags |= ks->max_keys >> 4; + ks->max_keys &= 0xF; MifareDesfireKeyVersion** kv_head = &ks->key_version_head; for(int key_id = 0; key_id < ks->max_keys; key_id++) { string_printf(key, "%s Key %d Version", prefix, key_id); @@ -624,6 +637,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 +645,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 +677,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 +693,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 6b3c8f092..5ae99d6d2 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 1933a79b7..a68f42d72 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 6b5d5d10c..e6351d421 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 000000000..1286024c3 --- /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 000000000..4611d16b7 --- /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 c6ce4c2d0..c44222857 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 e0489c157..1b435ccd1 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 83a56b0b4..0f9da494a 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/assets/resources/nfc/assets/aid.nfc b/assets/resources/nfc/assets/aid.nfc index 43854f3d8..30c1030e7 100644 --- a/assets/resources/nfc/assets/aid.nfc +++ b/assets/resources/nfc/assets/aid.nfc @@ -64,6 +64,7 @@ A0000004540010: Etranzact Genesis Card A0000004540011: Etranzact Genesis Card 2 A0000004766C: GOOGLE_PAYMENT A0000005241010: RuPay +A0000006472F0001: FIDO U2F A0000006723010: TROY chip credit card A0000006723020: TROY chip debit card A0000007705850: XTRAPOWER diff --git a/core/furi/common_defines.h b/core/furi/common_defines.h index 5b0e69713..5e9a7904e 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/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h index 1406f9649..1d7474da5 100644 --- a/firmware/targets/f7/ble_glue/app_conf.h +++ b/firmware/targets/f7/ble_glue/app_conf.h @@ -127,7 +127,7 @@ * Maximum number of simultaneous connections that the device will support. * Valid values are from 1 to 8 */ -#define CFG_BLE_NUM_LINK 2 +#define CFG_BLE_NUM_LINK 1 /** * Maximum number of Services that can be stored in the GATT database. diff --git a/firmware/targets/f7/ble_glue/battery_service.c b/firmware/targets/f7/ble_glue/battery_service.c index 85f1eeea0..a95f91872 100644 --- a/firmware/targets/f7/ble_glue/battery_service.c +++ b/firmware/targets/f7/ble_glue/battery_service.c @@ -3,18 +3,50 @@ #include "ble.h" #include +#include #define TAG "BtBatterySvc" typedef struct { uint16_t svc_handle; - uint16_t char_level_handle; + uint16_t battery_level_char_handle; + uint16_t power_state_char_handle; } BatterySvc; +enum { + // Common states + BatterySvcPowerStateUnknown = 0b00, + BatterySvcPowerStateUnsupported = 0b01, + // Level states + BatterySvcPowerStateGoodLevel = 0b10, + BatterySvcPowerStateCriticallyLowLevel = 0b11, + // Charging states + BatterySvcPowerStateNotCharging = 0b10, + BatterySvcPowerStateCharging = 0b11, + // Discharging states + BatterySvcPowerStateNotDischarging = 0b10, + BatterySvcPowerStateDischarging = 0b11, + // Battery states + BatterySvcPowerStateBatteryNotPresent = 0b10, + BatterySvcPowerStateBatteryPresent = 0b11, +}; + +typedef struct { + uint8_t present : 2; + uint8_t discharging : 2; + uint8_t charging : 2; + uint8_t level : 2; +} BattrySvcPowerState; + +_Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size"); + static BatterySvc* battery_svc = NULL; +#define BATTERY_POWER_STATE (0x2A1A) + static const uint16_t service_uuid = BATTERY_SERVICE_UUID; -static const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID; +static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID; +static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE; void battery_svc_start() { battery_svc = malloc(sizeof(BatterySvc)); @@ -22,7 +54,7 @@ void battery_svc_start() { // Add Battery service status = aci_gatt_add_service( - UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc->svc_handle); + UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 8, &battery_svc->svc_handle); if(status) { FURI_LOG_E(TAG, "Failed to add Battery service: %d", status); } @@ -30,24 +62,47 @@ void battery_svc_start() { status = aci_gatt_add_char( battery_svc->svc_handle, UUID_TYPE_16, - (Char_UUID_t*)&char_battery_level_uuid, + (Char_UUID_t*)&battery_level_char_uuid, 1, CHAR_PROP_READ | CHAR_PROP_NOTIFY, ATTR_PERMISSION_AUTHEN_READ, GATT_DONT_NOTIFY_EVENTS, 10, CHAR_VALUE_LEN_CONSTANT, - &battery_svc->char_level_handle); + &battery_svc->battery_level_char_handle); if(status) { FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); } + // Add Power state characteristic + status = aci_gatt_add_char( + battery_svc->svc_handle, + UUID_TYPE_16, + (Char_UUID_t*)&power_state_char_uuid, + 1, + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &battery_svc->power_state_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); + } + // Update power state charachteristic + battery_svc_update_power_state(); } void battery_svc_stop() { tBleStatus status; if(battery_svc) { // Delete Battery level characteristic - status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->char_level_handle); + status = + aci_gatt_del_char(battery_svc->svc_handle, battery_svc->battery_level_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); + } + // Delete Power state characteristic + status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->power_state_char_handle); if(status) { FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); } @@ -73,9 +128,39 @@ bool battery_svc_update_level(uint8_t battery_charge) { // Update battery level characteristic FURI_LOG_D(TAG, "Updating battery level characteristic"); tBleStatus result = aci_gatt_update_char_value( - battery_svc->svc_handle, battery_svc->char_level_handle, 0, 1, &battery_charge); + battery_svc->svc_handle, battery_svc->battery_level_char_handle, 0, 1, &battery_charge); if(result) { FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result); } return result != BLE_STATUS_SUCCESS; } + +bool battery_svc_update_power_state() { + // Check if service was started + if(battery_svc == NULL) { + return false; + } + // Update power state characteristic + BattrySvcPowerState power_state = { + .level = BatterySvcPowerStateUnsupported, + .present = BatterySvcPowerStateBatteryPresent, + }; + if(furi_hal_power_is_charging()) { + power_state.charging = BatterySvcPowerStateCharging; + power_state.discharging = BatterySvcPowerStateNotDischarging; + } else { + power_state.charging = BatterySvcPowerStateNotCharging; + power_state.discharging = BatterySvcPowerStateDischarging; + } + FURI_LOG_D(TAG, "Updating power state characteristic"); + tBleStatus result = aci_gatt_update_char_value( + battery_svc->svc_handle, + battery_svc->power_state_char_handle, + 0, + 1, + (uint8_t*)&power_state); + if(result) { + FURI_LOG_E(TAG, "Failed updating Power state characteristic: %d", result); + } + return result != BLE_STATUS_SUCCESS; +} diff --git a/firmware/targets/f7/ble_glue/battery_service.h b/firmware/targets/f7/ble_glue/battery_service.h index 2d35e252d..f38bfc00d 100644 --- a/firmware/targets/f7/ble_glue/battery_service.h +++ b/firmware/targets/f7/ble_glue/battery_service.h @@ -15,6 +15,8 @@ bool battery_svc_is_started(); bool battery_svc_update_level(uint8_t battery_level); +bool battery_svc_update_power_state(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 48d69844d..1e8690771 100755 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -284,6 +284,12 @@ void furi_hal_bt_update_battery_level(uint8_t battery_level) { } } +void furi_hal_bt_update_power_state() { + if(battery_svc_is_started()) { + battery_svc_update_power_state(); + } +} + void furi_hal_bt_get_key_storage_buff(uint8_t** key_buff_addr, uint16_t* key_buff_size) { ble_app_get_key_storage_buff(key_buff_addr, key_buff_size); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 768a4bac7..4391685e6 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_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 73b16becb..3a05081bd 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -91,6 +91,9 @@ bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, */ void furi_hal_bt_update_battery_level(uint8_t battery_level); +/** Update battery power state */ +void furi_hal_bt_update_power_state(); + /** Checks if BLE state is active * * @return true if device is connected or advertising, false otherwise diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index 20a469002..860db80de 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/flipper_format/flipper_format.c b/lib/flipper_format/flipper_format.c index cee767017..df848ead3 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 49e3f9365..e32a52192 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 bd700849e..5c2105361 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 6ac17322f..75eaef20e 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/nfc_protocols/crypto1.c b/lib/nfc_protocols/crypto1.c index 469b0de09..f08164ba9 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 aaa2470c7..07b39c22c 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 ace37bff3..38c47127b 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 fa778b771..bbf34b2dc 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/mifare_desfire.c b/lib/nfc_protocols/mifare_desfire.c index 4f02e8396..6f28dc5da 100644 --- a/lib/nfc_protocols/mifare_desfire.c +++ b/lib/nfc_protocols/mifare_desfire.c @@ -115,6 +115,9 @@ void mf_df_cat_key_settings(MifareDesfireKeySettings* ks, string_t out) { string_cat_printf(out, "freeCreateDelete %d\n", ks->free_create_delete); string_cat_printf(out, "freeDirectoryList %d\n", ks->free_directory_list); string_cat_printf(out, "masterChangeable %d\n", ks->master_key_changeable); + if(ks->flags) { + string_cat_printf(out, "flags %d\n", ks->flags); + } string_cat_printf(out, "maxKeys %d\n", ks->max_keys); for(MifareDesfireKeyVersion* kv = ks->key_version_head; kv; kv = kv->next) { string_cat_printf(out, "key %d version %d\n", kv->id, kv->version); @@ -274,7 +277,8 @@ bool mf_df_parse_get_key_settings_response( out->free_create_delete = (buf[0] & 0x4) != 0; out->free_directory_list = (buf[0] & 0x2) != 0; out->master_key_changeable = (buf[0] & 0x1) != 0; - out->max_keys = buf[1]; + out->flags = buf[1] >> 4; + out->max_keys = buf[1] & 0xF; return true; } diff --git a/lib/nfc_protocols/mifare_desfire.h b/lib/nfc_protocols/mifare_desfire.h index 809f17827..dbe0802eb 100644 --- a/lib/nfc_protocols/mifare_desfire.h +++ b/lib/nfc_protocols/mifare_desfire.h @@ -56,6 +56,7 @@ typedef struct { bool free_create_delete; bool free_directory_list; bool master_key_changeable; + uint8_t flags; uint8_t max_keys; MifareDesfireKeyVersion* key_version_head; } MifareDesfireKeySettings; diff --git a/lib/nfc_protocols/nfca.c b/lib/nfc_protocols/nfca.c index 81a6ddfcc..e1cbdafe1 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 73e2e65e0..498ef2843 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 739eb30b6..41bb24bba 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 ca10f2bec..c6683a52c 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