diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml deleted file mode 100644 index 8c5bac2a2..000000000 --- a/.github/workflows/unit_tests.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: 'Unit tests' - -on: - pull_request: - -env: - TARGETS: f7 - DEFAULT_TARGET: f7 - -jobs: - main: - runs-on: [self-hosted, FlipperZeroTest] - steps: - - name: Checkout code - uses: actions/checkout@v2 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Get flipper from device manager (mock)' - id: device - run: | - echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT - - - name: 'Compile unit tests firmware' - id: compile - continue-on-error: true - run: | - FBT_TOOLCHAIN_PATH=/opt ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 - - - name: 'Wait for flipper to finish updating' - id: connect - if: steps.compile.outcome == 'success' - continue-on-error: true - run: | - python3 ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - - - name: 'Format flipper SD card' - id: format - if: steps.connect.outcome == 'success' - continue-on-error: true - run: | - ./scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext - - - name: 'Copy unit tests to flipper' - id: copy - if: steps.format.outcome == 'success' - continue-on-error: true - run: | - ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/unit_tests /ext/unit_tests - - - name: 'Run units and validate results' - if: steps.copy.outcome == 'success' - continue-on-error: true - run: | - python3 ./scripts/testing/units.py ${{steps.device.outputs.flipper}} diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index b781db476..7d4c150ef 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -5,25 +5,21 @@ #include #include #include +#include #include "infrared_signal.h" #include "infrared_brute_force.h" -#include - #define INFRARED_CLI_BUF_SIZE 10 +#define INFRARED_ASSETS_FOLDER "infrared/assets" +#define INFRARED_BRUTE_FORCE_DUMMY_INDEX 0 DICT_DEF2(dict_signals, FuriString*, FURI_STRING_OPLIST, int, M_DEFAULT_OPLIST) -enum RemoteTypes { TV = 0, AC = 1 }; - static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args); static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args); static void infrared_cli_process_decode(Cli* cli, FuriString* args); static void infrared_cli_process_universal(Cli* cli, FuriString* args); -static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type); -static void - infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal); static const struct { const char* cmd; @@ -87,8 +83,10 @@ static void infrared_cli_print_usage(void) { INFRARED_MIN_FREQUENCY, INFRARED_MAX_FREQUENCY); printf("\tir decode []\r\n"); - printf("\tir universal \r\n"); - printf("\tir universal list \r\n"); + printf("\tir universal \r\n"); + printf("\tir universal list \r\n"); + // TODO: Do not hardcode universal remote names + printf("\tAvailable universal remotes: tv audio ac\r\n"); } static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { @@ -356,89 +354,31 @@ static void infrared_cli_process_decode(Cli* cli, FuriString* args) { furi_record_close(RECORD_STORAGE); } -static void infrared_cli_process_universal(Cli* cli, FuriString* args) { - enum RemoteTypes Remote; - - FuriString* command; - FuriString* remote; - FuriString* signal; - command = furi_string_alloc(); - remote = furi_string_alloc(); - signal = furi_string_alloc(); - - do { - if(!args_read_string_and_trim(args, command)) { - infrared_cli_print_usage(); - break; - } - - if(furi_string_cmp_str(command, "list") == 0) { - args_read_string_and_trim(args, remote); - if(furi_string_cmp_str(remote, "tv") == 0) { - Remote = TV; - } else if(furi_string_cmp_str(remote, "ac") == 0) { - Remote = AC; - } else { - printf("Invalid remote type.\r\n"); - break; - } - infrared_cli_list_remote_signals(Remote); - break; - } - - if(furi_string_cmp_str(command, "tv") == 0) { - Remote = TV; - } else if(furi_string_cmp_str(command, "ac") == 0) { - Remote = AC; - } else { - printf("Invalid remote type.\r\n"); - break; - } - - args_read_string_and_trim(args, signal); - if(furi_string_empty(signal)) { - printf("Must supply a valid signal for type of remote selected.\r\n"); - break; - } - - infrared_cli_brute_force_signals(cli, Remote, signal); - break; - - } while(false); - - furi_string_free(command); - furi_string_free(remote); - furi_string_free(signal); -} - -static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); - dict_signals_t signals_dict; - FuriString* key; - const char* remote_file = NULL; - bool success = false; - int max = 1; - - switch(remote_type) { - case TV: - remote_file = EXT_PATH("infrared/assets/tv.ir"); - break; - case AC: - remote_file = EXT_PATH("infrared/assets/ac.ir"); - break; - default: - break; +static void infrared_cli_list_remote_signals(FuriString* remote_name) { + if(furi_string_empty(remote_name)) { + printf("Missing remote name.\r\n"); + return; } - dict_signals_init(signals_dict); - key = furi_string_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + FuriString* remote_path = furi_string_alloc_printf( + "%s/%s.ir", EXT_PATH(INFRARED_ASSETS_FOLDER), furi_string_get_cstr(remote_name)); + + do { + if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(remote_path))) { + printf("Invalid remote name.\r\n"); + break; + } + + dict_signals_t signals_dict; + dict_signals_init(signals_dict); + + FuriString* key = furi_string_alloc(); + FuriString* signal_name = furi_string_alloc(); - success = flipper_format_buffered_file_open_existing(ff, remote_file); - if(success) { - FuriString* signal_name; - signal_name = furi_string_alloc(); printf("Valid signals:\r\n"); + int max = 1; while(flipper_format_read_string(ff, "name", signal_name)) { furi_string_set_str(key, furi_string_get_cstr(signal_name)); int* v = dict_signals_get(signals_dict, key); @@ -449,57 +389,57 @@ static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { dict_signals_set_at(signals_dict, key, 1); } } + dict_signals_it_t it; for(dict_signals_it(it, signals_dict); !dict_signals_end_p(it); dict_signals_next(it)) { const struct dict_signals_pair_s* pair = dict_signals_cref(it); printf("\t%s\r\n", furi_string_get_cstr(pair->key)); } - furi_string_free(signal_name); - } - furi_string_free(key); - dict_signals_clear(signals_dict); + furi_string_free(key); + furi_string_free(signal_name); + dict_signals_clear(signals_dict); + + } while(false); + flipper_format_free(ff); + furi_string_free(remote_path); furi_record_close(RECORD_STORAGE); } static void - infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal) { + infrared_cli_brute_force_signals(Cli* cli, FuriString* remote_name, FuriString* signal_name) { InfraredBruteForce* brute_force = infrared_brute_force_alloc(); - const char* remote_file = NULL; - uint32_t i = 0; - bool success = false; + FuriString* remote_path = furi_string_alloc_printf( + "%s/%s.ir", EXT_PATH(INFRARED_ASSETS_FOLDER), furi_string_get_cstr(remote_name)); - switch(remote_type) { - case TV: - remote_file = EXT_PATH("infrared/assets/tv.ir"); - break; - case AC: - remote_file = EXT_PATH("infrared/assets/ac.ir"); - break; - default: - break; - } + infrared_brute_force_set_db_filename(brute_force, furi_string_get_cstr(remote_path)); + infrared_brute_force_add_record( + brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, furi_string_get_cstr(signal_name)); - infrared_brute_force_set_db_filename(brute_force, remote_file); - infrared_brute_force_add_record(brute_force, i++, furi_string_get_cstr(signal)); - - success = infrared_brute_force_calculate_messages(brute_force); - if(success) { - uint32_t record_count; - uint32_t index = 0; - int records_sent = 0; - bool running = false; - - running = infrared_brute_force_start(brute_force, index, &record_count); - if(record_count <= 0) { - printf("Invalid signal.\n"); - infrared_brute_force_clear_records(brute_force); - return; + do { + if(furi_string_empty(signal_name)) { + printf("Missing signal name.\r\n"); + break; + } + if(!infrared_brute_force_calculate_messages(brute_force)) { + printf("Invalid remote name.\r\n"); + break; } - printf("Sending %ld codes to the tv.\r\n", record_count); + uint32_t record_count; + bool running = infrared_brute_force_start( + brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, &record_count); + + if(record_count <= 0) { + printf("Invalid signal name.\r\n"); + break; + } + + printf("Sending %ld signal(s)...\r\n", record_count); printf("Press Ctrl-C to stop.\r\n"); + + int records_sent = 0; while(running) { running = infrared_brute_force_send_next(brute_force); @@ -510,14 +450,35 @@ static void } infrared_brute_force_stop(brute_force); - } else { - printf("Invalid signal.\r\n"); - } + } while(false); + furi_string_free(remote_path); infrared_brute_force_clear_records(brute_force); infrared_brute_force_free(brute_force); } +static void infrared_cli_process_universal(Cli* cli, FuriString* args) { + FuriString* arg1 = furi_string_alloc(); + FuriString* arg2 = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, arg1)) break; + if(!args_read_string_and_trim(args, arg2)) break; + } while(false); + + if(furi_string_empty(arg1)) { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + } else if(furi_string_equal_str(arg1, "list")) { + infrared_cli_list_remote_signals(arg2); + } else { + infrared_cli_brute_force_signals(cli, arg1, arg2); + } + + furi_string_free(arg1); + furi_string_free(arg2); +} + static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) { UNUSED(context); if(furi_hal_infrared_is_busy()) { diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 206bab0cf..afd5b284f 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -37,6 +37,12 @@ ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete) ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate) ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) +ADD_SCENE(nfc, mf_classic_write, MfClassicWrite) +ADD_SCENE(nfc, mf_classic_write_success, MfClassicWriteSuccess) +ADD_SCENE(nfc, mf_classic_write_fail, MfClassicWriteFail) +ADD_SCENE(nfc, mf_classic_update, MfClassicUpdate) +ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess) +ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) ADD_SCENE(nfc, emv_menu, EmvMenu) ADD_SCENE(nfc, passport_read, PassportReadSuccess) diff --git a/applications/main/nfc/scenes/nfc_scene_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_detect_reader.c index abf1437d2..745946157 100644 --- a/applications/main/nfc/scenes/nfc_scene_detect_reader.c +++ b/applications/main/nfc/scenes/nfc_scene_detect_reader.c @@ -28,6 +28,11 @@ void nfc_scene_detect_reader_on_enter(void* context) { detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc); detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX); + NfcDeviceData* dev_data = &nfc->dev->dev_data; + if(dev_data->nfc_data.uid_len) { + detect_reader_set_uid( + nfc->detect_reader, dev_data->nfc_data.uid, dev_data->nfc_data.uid_len); + } // Store number of collected nonces in scene state scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDetectReader, 0); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c new file mode 100644 index 000000000..dd3a6f7d5 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c @@ -0,0 +1,98 @@ +#include "../nfc_i.h" +#include + +enum { + NfcSceneMfClassicUpdateStateCardSearch, + NfcSceneMfClassicUpdateStateCardFound, +}; + +bool nfc_mf_classic_update_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + + return true; +} + +static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) { + Popup* popup = nfc->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicUpdate); + + if(state == NfcSceneMfClassicUpdateStateCardSearch) { + popup_set_text( + nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Updating\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_update_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); + nfc_scene_mf_classic_update_setup_view(nfc); + + // Setup and start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfClassicUpdate, + &nfc->dev->dev_data, + nfc_mf_classic_update_worker_callback, + nfc); + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_mf_classic_update_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventSuccess) { + nfc_worker_stop(nfc->worker); + if(nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdateSuccess); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + } + consumed = true; + } else if(event.event == NfcWorkerEventWrongCard) { + nfc_worker_stop(nfc->worker); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneMfClassicUpdate, + NfcSceneMfClassicUpdateStateCardFound); + nfc_scene_mf_classic_update_setup_view(nfc); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneMfClassicUpdate, + NfcSceneMfClassicUpdateStateCardSearch); + nfc_scene_mf_classic_update_setup_view(nfc); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_classic_update_on_exit(void* context) { + Nfc* nfc = context; + nfc_worker_stop(nfc->worker); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); + // Clear view + popup_reset(nfc->popup); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c new file mode 100644 index 000000000..fef8fd5e9 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_classic_update_success_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_update_success_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcSave); + + notification_message(nfc->notifications, &sequence_success); + + Popup* popup = nfc->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_mf_classic_update_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_update_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } + return consumed; +} + +void nfc_scene_mf_classic_update_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c new file mode 100644 index 000000000..3543cbc58 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c @@ -0,0 +1,92 @@ +#include "../nfc_i.h" +#include + +enum { + NfcSceneMfClassicWriteStateCardSearch, + NfcSceneMfClassicWriteStateCardFound, +}; + +bool nfc_mf_classic_write_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + + return true; +} + +static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) { + Popup* popup = nfc->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicWrite); + + if(state == NfcSceneMfClassicWriteStateCardSearch) { + popup_set_text( + nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_write_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + nfc_scene_mf_classic_write_setup_view(nfc); + + // Setup and start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfClassicWrite, + &nfc->dev->dev_data, + nfc_mf_classic_write_worker_callback, + nfc); + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_mf_classic_write_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventSuccess) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventFail) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteFail); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCard) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardFound); + nfc_scene_mf_classic_write_setup_view(nfc); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + nfc_scene_mf_classic_write_setup_view(nfc); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_classic_write_on_exit(void* context) { + Nfc* nfc = context; + + nfc_worker_stop(nfc->worker); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + // Clear view + popup_reset(nfc->popup); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c new file mode 100644 index 000000000..aeea6eef0 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c @@ -0,0 +1,58 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_write_fail_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_write_fail_on_enter(void* context) { + Nfc* nfc = context; + Widget* widget = nfc->widget; + + notification_message(nfc->notifications, &sequence_error); + + widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Not all sectors\nwere written\ncorrectly."); + + widget_add_button_element( + widget, GuiButtonTypeLeft, "Finish", nfc_scene_mf_classic_write_fail_widget_callback, nfc); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_write_fail_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneSavedMenu); + } + return consumed; +} + +void nfc_scene_mf_classic_write_fail_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c new file mode 100644 index 000000000..2f2a3beb1 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_classic_write_success_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_write_success_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcSave); + + notification_message(nfc->notifications, &sequence_success); + + Popup* popup = nfc->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_mf_classic_write_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_write_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } + return consumed; +} + +void nfc_scene_mf_classic_write_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c new file mode 100644 index 000000000..2c56270e3 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c @@ -0,0 +1,53 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_wrong_card_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_wrong_card_on_enter(void* context) { + Nfc* nfc = context; + Widget* widget = nfc->widget; + + notification_message(nfc->notifications, &sequence_error); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); + widget_add_string_multiline_element( + widget, + 4, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Data management\nis only possible\nwith initial card"); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_wrong_card_widget_callback, nfc); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_wrong_card_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } + return consumed; +} + +void nfc_scene_mf_classic_wrong_card_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index b2c2b87e8..a8a850ece 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -4,6 +4,9 @@ enum SubmenuIndex { SubmenuIndexEmulate, SubmenuIndexEditUid, + SubmenuIndexDetectReader, + SubmenuIndexWrite, + SubmenuIndexUpdate, SubmenuIndexRename, SubmenuIndexDelete, SubmenuIndexInfo, @@ -45,6 +48,28 @@ void nfc_scene_saved_menu_on_enter(void* context) { submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); } + if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) { + submenu_add_item( + submenu, + "Detect reader", + SubmenuIndexDetectReader, + nfc_scene_saved_menu_submenu_callback, + nfc); + } + submenu_add_item( + submenu, + "Write To Initial Card", + SubmenuIndexWrite, + nfc_scene_saved_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Update From Initial Card", + SubmenuIndexUpdate, + nfc_scene_saved_menu_submenu_callback, + nfc); + } submenu_add_item( submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); if(nfc->dev->format == NfcDeviceSaveFormatMifareUl && @@ -97,6 +122,15 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } DOLPHIN_DEED(DolphinDeedNfcEmulate); consumed = true; + } else if(event.event == SubmenuIndexDetectReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + consumed = true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); + consumed = true; + } else if(event.event == SubmenuIndexUpdate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdate); + consumed = true; } else if(event.event == SubmenuIndexRename) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 0c4ec1cf9..028f85ae0 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -53,6 +53,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexDetectReader) { bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; if(sd_exist) { + nfc_device_data_clear(&nfc->dev->dev_data); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); DOLPHIN_DEED(DolphinDeedNfcDetectReader); } else { diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index 91537868b..e5951beb2 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -2,6 +2,8 @@ #include #include +#define DETECT_READER_UID_MAX_LEN (10) + struct DetectReader { View* view; DetectReaderDoneCallback callback; @@ -12,6 +14,7 @@ typedef struct { uint16_t nonces; uint16_t nonces_max; DetectReaderState state; + FuriString* uid_str; } DetectReaderViewModel; static void detect_reader_draw_callback(Canvas* canvas, void* model) { @@ -23,6 +26,10 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) { if(m->state == DetectReaderStateStart) { snprintf(text, sizeof(text), "Touch the reader"); canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39); + if(furi_string_size(m->uid_str)) { + elements_multiline_text_aligned( + canvas, 64, 64, AlignCenter, AlignBottom, furi_string_get_cstr(m->uid_str)); + } } else if(m->state == DetectReaderStateReaderDetected) { snprintf(text, sizeof(text), "Move the Flipper away"); canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15); @@ -86,12 +93,24 @@ DetectReader* detect_reader_alloc() { view_set_input_callback(detect_reader->view, detect_reader_input_callback); view_set_context(detect_reader->view, detect_reader); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { model->uid_str = furi_string_alloc(); }, + false); + return detect_reader; } void detect_reader_free(DetectReader* detect_reader) { furi_assert(detect_reader); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { furi_string_free(model->uid_str); }, + false); + view_free(detect_reader->view); free(detect_reader); } @@ -106,6 +125,7 @@ void detect_reader_reset(DetectReader* detect_reader) { model->nonces = 0; model->nonces_max = 0; model->state = DetectReaderStateStart; + furi_string_reset(model->uid_str); }, false); } @@ -152,3 +172,19 @@ void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState stat with_view_model( detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true); } + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len) { + furi_assert(detect_reader); + furi_assert(uid); + furi_assert(uid_len < DETECT_READER_UID_MAX_LEN); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { + furi_string_set_str(model->uid_str, "UID:"); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(model->uid_str, " %02X", uid[i]); + } + }, + true); +} diff --git a/applications/main/nfc/views/detect_reader.h b/applications/main/nfc/views/detect_reader.h index aabdd7c87..6481216b4 100644 --- a/applications/main/nfc/views/detect_reader.h +++ b/applications/main/nfc/views/detect_reader.h @@ -32,3 +32,5 @@ void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_m void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected); void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state); + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len); diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 547b3c3fd..ebbcc2453 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -30,9 +30,9 @@ typedef struct SubGhzReceiverHistory SubGhzReceiverHistory; static const Icon* ReceiverItemIcons[] = { [SubGhzProtocolTypeUnknown] = &I_Quest_7x8, - [SubGhzProtocolTypeStatic] = &I_Unlock_7x8, - [SubGhzProtocolTypeDynamic] = &I_Lock_7x8, - [SubGhzProtocolTypeRAW] = &I_Unlock_7x8, + [SubGhzProtocolTypeStatic] = &I_Static_9x7, + [SubGhzProtocolTypeDynamic] = &I_Dynamic_9x7, + [SubGhzProtocolTypeRAW] = &I_Raw_9x7, }; typedef enum { diff --git a/applications/plugins/flipfrid/application.fam b/applications/plugins/flipfrid/application.fam index 44ae894d4..fc089de48 100644 --- a/applications/plugins/flipfrid/application.fam +++ b/applications/plugins/flipfrid/application.fam @@ -9,5 +9,5 @@ App( order=180, fap_icon="rfid_10px.png", fap_category="Tools", - fap_icon_assets="icons", + fap_icon_assets="images", ) diff --git a/applications/plugins/flipfrid/flipfrid.h b/applications/plugins/flipfrid/flipfrid.h index 248e2322e..4e3e7a37b 100644 --- a/applications/plugins/flipfrid/flipfrid.h +++ b/applications/plugins/flipfrid/flipfrid.h @@ -15,6 +15,8 @@ #include #include +#include + #include #include diff --git a/applications/plugins/flipfrid/images/125_10px.png b/applications/plugins/flipfrid/images/125_10px.png new file mode 100644 index 000000000..ce01284a2 Binary files /dev/null and b/applications/plugins/flipfrid/images/125_10px.png differ diff --git a/applications/plugins/flipper_i2ctools/application.fam b/applications/plugins/flipper_i2ctools/application.fam index d4b4a975f..6fafeef58 100644 --- a/applications/plugins/flipper_i2ctools/application.fam +++ b/applications/plugins/flipper_i2ctools/application.fam @@ -9,5 +9,5 @@ App( order=175, fap_icon="i2ctools.png", fap_category="GPIO", - fap_icon_assets="icons", + fap_icon_assets="images", ) \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/images/ButtonDown_7x4.png b/applications/plugins/flipper_i2ctools/images/ButtonDown_7x4.png new file mode 100644 index 000000000..2954bb6a6 Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/ButtonDown_7x4.png differ diff --git a/applications/plugins/flipper_i2ctools/images/ButtonLeft_4x7.png b/applications/plugins/flipper_i2ctools/images/ButtonLeft_4x7.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/ButtonLeft_4x7.png differ diff --git a/applications/plugins/flipper_i2ctools/images/ButtonRight_4x7.png b/applications/plugins/flipper_i2ctools/images/ButtonRight_4x7.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/ButtonRight_4x7.png differ diff --git a/applications/plugins/flipper_i2ctools/images/ButtonUp_7x4.png b/applications/plugins/flipper_i2ctools/images/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/ButtonUp_7x4.png differ diff --git a/applications/plugins/flipper_i2ctools/images/Ok_btn_9x9.png b/applications/plugins/flipper_i2ctools/images/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/Ok_btn_9x9.png differ diff --git a/applications/plugins/flipper_i2ctools/images/passport_bad3_46x49.png b/applications/plugins/flipper_i2ctools/images/passport_bad3_46x49.png new file mode 100644 index 000000000..e39e6629d Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/passport_bad3_46x49.png differ diff --git a/applications/plugins/flipper_i2ctools/images/passport_happy2_46x49.png b/applications/plugins/flipper_i2ctools/images/passport_happy2_46x49.png new file mode 100644 index 000000000..f64e770e5 Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/passport_happy2_46x49.png differ diff --git a/applications/plugins/flipper_i2ctools/images/passport_happy3_46x49.png b/applications/plugins/flipper_i2ctools/images/passport_happy3_46x49.png new file mode 100644 index 000000000..7aef17674 Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/passport_happy3_46x49.png differ diff --git a/applications/plugins/flipper_i2ctools/views/main_view.c b/applications/plugins/flipper_i2ctools/views/main_view.c index cc175904e..dfbc24e9b 100644 --- a/applications/plugins/flipper_i2ctools/views/main_view.c +++ b/applications/plugins/flipper_i2ctools/views/main_view.c @@ -1,5 +1,4 @@ #include "main_view.h" -#include "i2cTools_icons.h" void draw_main_view(Canvas* canvas, i2cMainView* main_view) { canvas_clear(canvas); diff --git a/applications/plugins/flipper_i2ctools/views/main_view.h b/applications/plugins/flipper_i2ctools/views/main_view.h index 7a604fc60..ee7be7f9f 100644 --- a/applications/plugins/flipper_i2ctools/views/main_view.h +++ b/applications/plugins/flipper_i2ctools/views/main_view.h @@ -1,6 +1,7 @@ #include #include #include +#include #define APP_NAME "I2C Tools" diff --git a/applications/plugins/flipper_i2ctools/views/scanner_view.c b/applications/plugins/flipper_i2ctools/views/scanner_view.c index 17bbf81c1..346f82590 100644 --- a/applications/plugins/flipper_i2ctools/views/scanner_view.c +++ b/applications/plugins/flipper_i2ctools/views/scanner_view.c @@ -1,5 +1,4 @@ #include "scanner_view.h" -#include "i2cTools_icons.h" void draw_scanner_view(Canvas* canvas, i2cScanner* i2c_scanner) { canvas_clear(canvas); diff --git a/applications/plugins/flipper_i2ctools/views/scanner_view.h b/applications/plugins/flipper_i2ctools/views/scanner_view.h index 249a12942..99ba3c29f 100644 --- a/applications/plugins/flipper_i2ctools/views/scanner_view.h +++ b/applications/plugins/flipper_i2ctools/views/scanner_view.h @@ -2,6 +2,8 @@ #include #include +#include + #include "../i2cscanner.h" #define SCAN_MENU_TEXT "Scan" diff --git a/applications/plugins/flipper_i2ctools/views/sender_view.h b/applications/plugins/flipper_i2ctools/views/sender_view.h index d0ab28ba4..866e483aa 100644 --- a/applications/plugins/flipper_i2ctools/views/sender_view.h +++ b/applications/plugins/flipper_i2ctools/views/sender_view.h @@ -2,6 +2,8 @@ #include #include +#include + #include "../i2csender.h" #define SEND_MENU_TEXT "Send" diff --git a/applications/plugins/flipper_i2ctools/views/sniffer_view.c b/applications/plugins/flipper_i2ctools/views/sniffer_view.c index 0761d4ca4..8f289066c 100644 --- a/applications/plugins/flipper_i2ctools/views/sniffer_view.c +++ b/applications/plugins/flipper_i2ctools/views/sniffer_view.c @@ -1,5 +1,4 @@ #include "sniffer_view.h" -#include "i2cTools_icons.h" void draw_sniffer_view(Canvas* canvas, i2cSniffer* i2c_sniffer) { canvas_clear(canvas); diff --git a/applications/plugins/flipper_i2ctools/views/sniffer_view.h b/applications/plugins/flipper_i2ctools/views/sniffer_view.h index da46aaef1..56a213895 100644 --- a/applications/plugins/flipper_i2ctools/views/sniffer_view.h +++ b/applications/plugins/flipper_i2ctools/views/sniffer_view.h @@ -2,6 +2,8 @@ #include #include +#include + #include "../i2csniffer.h" #define SNIFF_MENU_TEXT "Sniff" diff --git a/applications/plugins/gps_nmea_uart/.gitignore b/applications/plugins/gps_nmea_uart/.gitignore deleted file mode 100644 index c6127b38c..000000000 --- a/applications/plugins/gps_nmea_uart/.gitignore +++ /dev/null @@ -1,52 +0,0 @@ -# Prerequisites -*.d - -# Object files -*.o -*.ko -*.obj -*.elf - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf diff --git a/applications/plugins/gps_nmea_uart/README.md b/applications/plugins/gps_nmea_uart/README.md index dd3e7f3ae..ffeb6ee99 100644 --- a/applications/plugins/gps_nmea_uart/README.md +++ b/applications/plugins/gps_nmea_uart/README.md @@ -3,6 +3,8 @@ A simple Flipper Zero application for NMEA 0183 serial GPS modules, such as the [Adafruit Ultimate GPS Breakout]. +[Original link](https://github.com/ezod/flipperzero-gps) + ![ui](ui.png) Heavy lifting (NMEA parsing) provided by [minmea], which is included in this @@ -15,14 +17,6 @@ Connect the GPS module to power and the USART using GPIO pins 9 (3.3V), 11 ![wiring](wiring.png) -## Building the FAP - -1. Clone the [flipperzero-firmware] repository. -2. Create a symbolic link in `applications_user` named `gps`, pointing to this - repository. -3. Compile with `./fbt fap_gps`. -4. Copy `build/f7-firmware-D/.extapps/gps.fap` to `apps/Tools` on the SD card - (directly or using [qFlipper]). ## Contributing diff --git a/applications/plugins/metronome/application.fam b/applications/plugins/metronome/application.fam index 0b79f2ff2..c1564e7f4 100644 --- a/applications/plugins/metronome/application.fam +++ b/applications/plugins/metronome/application.fam @@ -9,6 +9,7 @@ App( ], fap_icon="metronome_icon.png", fap_category="Music", + fap_icon_assets="images", stack_size=2 * 1024, fap_icon_assets="icons", order=20, diff --git a/applications/plugins/metronome/images/ButtonUp_7x4.png b/applications/plugins/metronome/images/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/applications/plugins/metronome/images/ButtonUp_7x4.png differ diff --git a/applications/plugins/mousejacker/application.fam b/applications/plugins/mousejacker/application.fam index 5791ac416..49d234916 100644 --- a/applications/plugins/mousejacker/application.fam +++ b/applications/plugins/mousejacker/application.fam @@ -12,5 +12,5 @@ App( order=50, fap_icon="mouse_10px.png", fap_category="GPIO", - fap_icon_assets="icons", + fap_icon_assets="images", ) diff --git a/applications/plugins/mousejacker/images/badusb_10px.png b/applications/plugins/mousejacker/images/badusb_10px.png new file mode 100644 index 000000000..037474aa3 Binary files /dev/null and b/applications/plugins/mousejacker/images/badusb_10px.png differ diff --git a/applications/plugins/mousejacker/images/sub1_10px.png b/applications/plugins/mousejacker/images/sub1_10px.png new file mode 100644 index 000000000..5a25fdf4e Binary files /dev/null and b/applications/plugins/mousejacker/images/sub1_10px.png differ diff --git a/applications/plugins/music_player/application.fam b/applications/plugins/music_player/application.fam index 2578d0aeb..1072f1524 100644 --- a/applications/plugins/music_player/application.fam +++ b/applications/plugins/music_player/application.fam @@ -12,8 +12,8 @@ App( stack_size=2 * 1024, order=45, fap_icon="icons/music_10px.png", - fap_category="Music", fap_icon_assets="icons", + fap_category="Music", ) App( diff --git a/applications/plugins/playlist/application.fam b/applications/plugins/playlist/application.fam index 5c7fcc10b..31acd8ea2 100644 --- a/applications/plugins/playlist/application.fam +++ b/applications/plugins/playlist/application.fam @@ -9,4 +9,5 @@ App( order=200, fap_icon="playlist_10px.png", fap_category="Tools", + fap_icon_assets="images", ) diff --git a/applications/plugins/playlist/images/ButtonRight_4x7.png b/applications/plugins/playlist/images/ButtonRight_4x7.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/applications/plugins/playlist/images/ButtonRight_4x7.png differ diff --git a/applications/plugins/playlist/images/sub1_10px.png b/applications/plugins/playlist/images/sub1_10px.png new file mode 100644 index 000000000..5a25fdf4e Binary files /dev/null and b/applications/plugins/playlist/images/sub1_10px.png differ diff --git a/applications/plugins/playlist/playlist.c b/applications/plugins/playlist/playlist.c index 103a439cb..c2c77382c 100644 --- a/applications/plugins/playlist/playlist.c +++ b/applications/plugins/playlist/playlist.c @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include diff --git a/applications/plugins/totp/application.fam b/applications/plugins/totp/application.fam index 1481b94c6..2765a4665 100644 --- a/applications/plugins/totp/application.fam +++ b/applications/plugins/totp/application.fam @@ -15,6 +15,6 @@ App( stack_size=2 * 1024, order=20, fap_category="Misc", - fap_icon="totp_10px.png", - fap_icon_assets="icons", + fap_icon_assets="images", + fap_icon="totp_10px.png" ) diff --git a/applications/plugins/totp/images/DolphinCommon_56x48.png b/applications/plugins/totp/images/DolphinCommon_56x48.png new file mode 100644 index 000000000..089aaed83 Binary files /dev/null and b/applications/plugins/totp/images/DolphinCommon_56x48.png differ diff --git a/applications/plugins/totp/scenes/add_new_token/totp_input_text.c b/applications/plugins/totp/scenes/add_new_token/totp_input_text.c index e3a7f68a6..811cd2976 100644 --- a/applications/plugins/totp/scenes/add_new_token/totp_input_text.c +++ b/applications/plugins/totp/scenes/add_new_token/totp_input_text.c @@ -2,6 +2,16 @@ #include #include "../../types/common.h" +size_t strnlen(const char* s, size_t maxlen) { + size_t len; + + for(len = 0; len < maxlen; len++, s++) { + if(!*s) break; + } + + return len; +} + void view_draw(View* view, Canvas* canvas) { furi_assert(view); if(view->draw_callback) { @@ -32,10 +42,14 @@ static void commit_text_input_callback(void* context) { InputTextSceneState* text_input_state = (InputTextSceneState*)context; if(text_input_state->callback != 0) { InputTextSceneCallbackResult* result = malloc(sizeof(InputTextSceneCallbackResult)); - result->user_input_length = strlen(text_input_state->text_input_buffer); + result->user_input_length = + strnlen(text_input_state->text_input_buffer, INPUT_BUFFER_SIZE); result->user_input = malloc(result->user_input_length + 1); result->callback_data = text_input_state->callback_data; - strcpy(result->user_input, text_input_state->text_input_buffer); + strlcpy( + result->user_input, + text_input_state->text_input_buffer, + result->user_input_length + 1); text_input_state->callback(result); } } diff --git a/applications/plugins/totp/scenes/add_new_token/totp_input_text.h b/applications/plugins/totp/scenes/add_new_token/totp_input_text.h index a73a227b5..dda0dc301 100644 --- a/applications/plugins/totp/scenes/add_new_token/totp_input_text.h +++ b/applications/plugins/totp/scenes/add_new_token/totp_input_text.h @@ -10,7 +10,7 @@ typedef struct { char* user_input; - uint8_t user_input_length; + size_t user_input_length; void* callback_data; } InputTextSceneCallbackResult; diff --git a/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.c b/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.c index fc39b66cd..cc4e6a69d 100644 --- a/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.c +++ b/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.c @@ -25,9 +25,9 @@ typedef enum { typedef struct { char* token_name; - uint8_t token_name_length; + size_t token_name_length; char* token_secret; - uint8_t token_secret_length; + size_t token_secret_length; bool saved; Control selected_control; InputTextSceneContext* token_name_input_context; @@ -35,12 +35,12 @@ typedef struct { InputTextSceneState* input_state; uint32_t input_started_at; int16_t current_token_index; - int32_t screen_y_offset; + int16_t screen_y_offset; TokenHashAlgo algo; TokenDigitsCount digits_count; } SceneState; -void totp_scene_add_new_token_init(PluginState* plugin_state) { +void totp_scene_add_new_token_init(const PluginState* plugin_state) { UNUSED(plugin_state); } @@ -235,7 +235,10 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState if(token_secret_set) { tokenInfo->name = malloc(scene_state->token_name_length + 1); - strcpy(tokenInfo->name, scene_state->token_name); + strlcpy( + tokenInfo->name, + scene_state->token_name, + scene_state->token_name_length + 1); tokenInfo->algo = scene_state->algo; tokenInfo->digits = scene_state->digits_count; @@ -308,6 +311,6 @@ void totp_scene_add_new_token_deactivate(PluginState* plugin_state) { plugin_state->current_scene_state = NULL; } -void totp_scene_add_new_token_free(PluginState* plugin_state) { +void totp_scene_add_new_token_free(const PluginState* plugin_state) { UNUSED(plugin_state); } diff --git a/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.h b/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.h index b65c567a2..7a0b0e68a 100644 --- a/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.h +++ b/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.h @@ -10,11 +10,11 @@ typedef struct { uint8_t current_token_index; } TokenAddEditSceneContext; -void totp_scene_add_new_token_init(PluginState* plugin_state); +void totp_scene_add_new_token_init(const PluginState* plugin_state); void totp_scene_add_new_token_activate( PluginState* plugin_state, const TokenAddEditSceneContext* context); void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state); void totp_scene_add_new_token_deactivate(PluginState* plugin_state); -void totp_scene_add_new_token_free(PluginState* plugin_state); +void totp_scene_add_new_token_free(const PluginState* plugin_state); diff --git a/applications/plugins/totp/scenes/app_settings/totp_app_settings.c b/applications/plugins/totp/scenes/app_settings/totp_app_settings.c index cdb775a8d..ea9a11e9b 100644 --- a/applications/plugins/totp/scenes/app_settings/totp_app_settings.c +++ b/applications/plugins/totp/scenes/app_settings/totp_app_settings.c @@ -16,7 +16,7 @@ typedef struct { Control selected_control; } SceneState; -void totp_scene_app_settings_init(PluginState* plugin_state) { +void totp_scene_app_settings_init(const PluginState* plugin_state) { UNUSED(plugin_state); } @@ -53,7 +53,7 @@ static void two_digit_to_str(int8_t num, char* str) { } void totp_scene_app_settings_render(Canvas* const canvas, PluginState* plugin_state) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Timezone offset"); @@ -90,7 +90,9 @@ void totp_scene_app_settings_render(Canvas* const canvas, PluginState* plugin_st scene_state->selected_control == ConfirmButton); } -bool totp_scene_app_settings_handle_event(PluginEvent* const event, PluginState* plugin_state) { +bool totp_scene_app_settings_handle_event( + const PluginEvent* const event, + PluginState* plugin_state) { if(event->type == EventTypeKey) { SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; if(event->input.type == InputTypePress) { @@ -171,6 +173,6 @@ void totp_scene_app_settings_deactivate(PluginState* plugin_state) { plugin_state->current_scene_state = NULL; } -void totp_scene_app_settings_free(PluginState* plugin_state) { +void totp_scene_app_settings_free(const PluginState* plugin_state) { UNUSED(plugin_state); } \ No newline at end of file diff --git a/applications/plugins/totp/scenes/app_settings/totp_app_settings.h b/applications/plugins/totp/scenes/app_settings/totp_app_settings.h index b97de3390..e965d79b7 100644 --- a/applications/plugins/totp/scenes/app_settings/totp_app_settings.h +++ b/applications/plugins/totp/scenes/app_settings/totp_app_settings.h @@ -10,11 +10,13 @@ typedef struct { uint8_t current_token_index; } AppSettingsSceneContext; -void totp_scene_app_settings_init(PluginState* plugin_state); +void totp_scene_app_settings_init(const PluginState* plugin_state); void totp_scene_app_settings_activate( PluginState* plugin_state, const AppSettingsSceneContext* context); void totp_scene_app_settings_render(Canvas* const canvas, PluginState* plugin_state); -bool totp_scene_app_settings_handle_event(PluginEvent* const event, PluginState* plugin_state); +bool totp_scene_app_settings_handle_event( + const PluginEvent* const event, + PluginState* plugin_state); void totp_scene_app_settings_deactivate(PluginState* plugin_state); -void totp_scene_app_settings_free(PluginState* plugin_state); \ No newline at end of file +void totp_scene_app_settings_free(const PluginState* plugin_state); \ No newline at end of file diff --git a/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.c b/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.c index b192e2829..e0ab40086 100644 --- a/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.c @@ -29,7 +29,7 @@ void totp_scene_authenticate_activate(PluginState* plugin_state) { } void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; int v_shift = 0; if(scene_state->code_length > 0) { @@ -74,7 +74,9 @@ void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_st } } -bool totp_scene_authenticate_handle_event(PluginEvent* const event, PluginState* plugin_state) { +bool totp_scene_authenticate_handle_event( + const PluginEvent* const event, + PluginState* plugin_state) { if(event->type == EventTypeKey) { if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { return false; @@ -157,6 +159,6 @@ void totp_scene_authenticate_deactivate(PluginState* plugin_state) { plugin_state->current_scene_state = NULL; } -void totp_scene_authenticate_free(PluginState* plugin_state) { +void totp_scene_authenticate_free(const PluginState* plugin_state) { UNUSED(plugin_state); } diff --git a/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.h b/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.h index f1199a425..c54152f62 100644 --- a/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.h +++ b/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.h @@ -9,6 +9,8 @@ void totp_scene_authenticate_init(PluginState* plugin_state); void totp_scene_authenticate_activate(PluginState* plugin_state); void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state); -bool totp_scene_authenticate_handle_event(PluginEvent* const event, PluginState* plugin_state); +bool totp_scene_authenticate_handle_event( + const PluginEvent* const event, + PluginState* plugin_state); void totp_scene_authenticate_deactivate(PluginState* plugin_state); -void totp_scene_authenticate_free(PluginState* plugin_state); +void totp_scene_authenticate_free(const PluginState* plugin_state); diff --git a/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.c b/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.c index 674bcd41e..7b5f7391b 100644 --- a/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.c @@ -9,6 +9,7 @@ #include "../../services/totp/totp.h" #include "../../services/config/config.h" #include "../../services/crypto/crypto.h" +#include "../../services/crypto/memset_s.h" #include "../scene_director.h" #include "../token_menu/totp_scene_token_menu.h" @@ -95,7 +96,7 @@ void update_totp_params(PluginState* const plugin_state) { } } -void totp_scene_generate_token_init(PluginState* plugin_state) { +void totp_scene_generate_token_init(const PluginState* plugin_state) { UNUSED(plugin_state); } @@ -180,7 +181,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ ->data); if(tokenInfo->token != NULL && tokenInfo->token_length > 0) { - uint8_t key_length; + size_t key_length; uint8_t* key = totp_crypto_decrypt( tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length); @@ -195,7 +196,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ TOKEN_LIFETIME), scene_state->last_code, tokenInfo->digits); - memset(key, 0, key_length); + memset_s(key, sizeof(key), 0, key_length); free(key); } else { i_token_to_str(0, scene_state->last_code, tokenInfo->digits); @@ -265,7 +266,9 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ } } -bool totp_scene_generate_token_handle_event(PluginEvent* const event, PluginState* plugin_state) { +bool totp_scene_generate_token_handle_event( + const PluginEvent* const event, + PluginState* plugin_state) { if(event->type == EventTypeKey) { if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { return false; @@ -314,11 +317,10 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { if(plugin_state->current_scene_state == NULL) return; SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - free(scene_state->last_code); free(scene_state); plugin_state->current_scene_state = NULL; } -void totp_scene_generate_token_free(PluginState* plugin_state) { +void totp_scene_generate_token_free(const PluginState* plugin_state) { UNUSED(plugin_state); } diff --git a/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.h b/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.h index 1284c7b41..e4ca818b6 100644 --- a/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.h +++ b/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.h @@ -10,11 +10,13 @@ typedef struct { uint8_t current_token_index; } GenerateTokenSceneContext; -void totp_scene_generate_token_init(PluginState* plugin_state); +void totp_scene_generate_token_init(const PluginState* plugin_state); void totp_scene_generate_token_activate( PluginState* plugin_state, const GenerateTokenSceneContext* context); void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state); -bool totp_scene_generate_token_handle_event(PluginEvent* const event, PluginState* plugin_state); +bool totp_scene_generate_token_handle_event( + const PluginEvent* const event, + PluginState* plugin_state); void totp_scene_generate_token_deactivate(PluginState* plugin_state); -void totp_scene_generate_token_free(PluginState* plugin_state); +void totp_scene_generate_token_free(const PluginState* plugin_state); diff --git a/applications/plugins/totp/scenes/scene_director.c b/applications/plugins/totp/scenes/scene_director.c index d4ddd1768..5265123f5 100644 --- a/applications/plugins/totp/scenes/scene_director.c +++ b/applications/plugins/totp/scenes/scene_director.c @@ -88,7 +88,7 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_ } } -void totp_scene_director_dispose(PluginState* const plugin_state) { +void totp_scene_director_dispose(const PluginState* const plugin_state) { totp_scene_generate_token_free(plugin_state); totp_scene_authenticate_free(plugin_state); totp_scene_add_new_token_free(plugin_state); diff --git a/applications/plugins/totp/scenes/scene_director.h b/applications/plugins/totp/scenes/scene_director.h index 3c25afff6..cc06029d3 100644 --- a/applications/plugins/totp/scenes/scene_director.h +++ b/applications/plugins/totp/scenes/scene_director.h @@ -12,5 +12,5 @@ void totp_scene_director_activate_scene( void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state); void totp_scene_director_init_scenes(PluginState* const plugin_state); void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state); -void totp_scene_director_dispose(PluginState* const plugin_state); +void totp_scene_director_dispose(const PluginState* const plugin_state); bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state); diff --git a/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.c b/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.c index 22c37dc2c..c4286ded9 100644 --- a/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.c +++ b/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.c @@ -21,7 +21,7 @@ typedef struct { int16_t current_token_index; } SceneState; -void totp_scene_token_menu_init(PluginState* plugin_state) { +void totp_scene_token_menu_init(const PluginState* plugin_state) { UNUSED(plugin_state); } @@ -38,7 +38,7 @@ void totp_scene_token_menu_activate( } void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; if(scene_state->current_token_index < 0) { ui_control_button_render( canvas, @@ -84,7 +84,7 @@ void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_stat } } -bool totp_scene_token_menu_handle_event(PluginEvent* const event, PluginState* plugin_state) { +bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state) { if(event->type == EventTypeKey) { SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; if(event->input.type == InputTypePress) { @@ -192,6 +192,6 @@ void totp_scene_token_menu_deactivate(PluginState* plugin_state) { plugin_state->current_scene_state = NULL; } -void totp_scene_token_menu_free(PluginState* plugin_state) { +void totp_scene_token_menu_free(const PluginState* plugin_state) { UNUSED(plugin_state); } diff --git a/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.h b/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.h index 0b117cb25..52e33e728 100644 --- a/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.h +++ b/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.h @@ -10,11 +10,11 @@ typedef struct { uint8_t current_token_index; } TokenMenuSceneContext; -void totp_scene_token_menu_init(PluginState* plugin_state); +void totp_scene_token_menu_init(const PluginState* plugin_state); void totp_scene_token_menu_activate( PluginState* plugin_state, const TokenMenuSceneContext* context); void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state); -bool totp_scene_token_menu_handle_event(PluginEvent* const event, PluginState* plugin_state); +bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state); void totp_scene_token_menu_deactivate(PluginState* plugin_state); -void totp_scene_token_menu_free(PluginState* plugin_state); +void totp_scene_token_menu_free(const PluginState* plugin_state); diff --git a/applications/plugins/totp/services/cli/cli.c b/applications/plugins/totp/services/cli/cli.c index 3a7afdd96..76e58a02d 100644 --- a/applications/plugins/totp/services/cli/cli.c +++ b/applications/plugins/totp/services/cli/cli.c @@ -9,7 +9,7 @@ #include "commands/timezone/timezone.h" #include "commands/help/help.h" -static void totp_cli_print_unknown_command(FuriString* unknown_command) { +static void totp_cli_print_unknown_command(const FuriString* unknown_command) { TOTP_CLI_PRINTF( "Command \"%s\" is unknown. Use \"" TOTP_CLI_COMMAND_HELP "\" command to get list of available commands.", diff --git a/applications/plugins/totp/services/cli/cli_helpers.c b/applications/plugins/totp/services/cli/cli_helpers.c index 81a1e4b77..4a0b8b352 100644 --- a/applications/plugins/totp/services/cli/cli_helpers.c +++ b/applications/plugins/totp/services/cli/cli_helpers.c @@ -1,7 +1,7 @@ #include "cli_helpers.h" #include -bool totp_cli_ensure_authenticated(PluginState* plugin_state, Cli* cli) { +bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) { if(plugin_state->current_scene == TotpSceneAuthentication) { TOTP_CLI_PRINTF("Pleases enter PIN on your flipper device\r\n"); @@ -11,7 +11,6 @@ bool totp_cli_ensure_authenticated(PluginState* plugin_state, Cli* cli) { } TOTP_CLI_DELETE_LAST_LINE(); - fflush(stdout); if(plugin_state->current_scene == TotpSceneAuthentication) { return false; diff --git a/applications/plugins/totp/services/cli/cli_helpers.h b/applications/plugins/totp/services/cli/cli_helpers.h index 216925e50..9b19f926b 100644 --- a/applications/plugins/totp/services/cli/cli_helpers.h +++ b/applications/plugins/totp/services/cli/cli_helpers.h @@ -13,16 +13,28 @@ #define DOCOPT_OPTIONS "[options]" #define DOCOPT_DEFAULT(val) "[default: " val "]" -#define TOTP_CLI_PRINTF(format, ...) \ - _Pragma(STRINGIFY(GCC diagnostic push)); \ - _Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")); \ - printf(format, ##__VA_ARGS__); \ - _Pragma(STRINGIFY(GCC diagnostic pop)); +#define TOTP_CLI_PRINTF(format, ...) \ + do { \ + _Pragma(STRINGIFY(GCC diagnostic push)) \ + _Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")) \ + printf(format, ##__VA_ARGS__); \ + _Pragma(STRINGIFY(GCC diagnostic pop)) \ + } while(false) + +#define TOTP_CLI_DELETE_LAST_LINE() \ + TOTP_CLI_PRINTF("\033[A\33[2K\r"); \ + fflush(stdout) + +#define TOTP_CLI_DELETE_CURRENT_LINE() \ + TOTP_CLI_PRINTF("\33[2K\r"); \ + fflush(stdout) + +#define TOTP_CLI_DELETE_LAST_CHAR() \ + TOTP_CLI_PRINTF("\b \b"); \ + fflush(stdout) -#define TOTP_CLI_DELETE_LAST_LINE() TOTP_CLI_PRINTF("\033[A\33[2K\r") -#define TOTP_CLI_DELETE_CURRENT_LINE() TOTP_CLI_PRINTF("\33[2K\r") #define TOTP_CLI_PRINT_INVALID_ARGUMENTS() \ TOTP_CLI_PRINTF( \ "Invalid command arguments. use \"help\" command to get list of available commands") -bool totp_cli_ensure_authenticated(PluginState* plugin_state, Cli* cli); +bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli); diff --git a/applications/plugins/totp/services/cli/commands/add/add.c b/applications/plugins/totp/services/cli/commands/add/add.c index 41ff5b860..c1ce42617 100644 --- a/applications/plugins/totp/services/cli/commands/add/add.c +++ b/applications/plugins/totp/services/cli/commands/add/add.c @@ -14,7 +14,7 @@ #define TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX "-d" #define TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX "-u" -static bool token_info_set_digits_from_str(TokenInfo* token_info, FuriString* str) { +static bool token_info_set_digits_from_str(TokenInfo* token_info, const FuriString* str) { switch(furi_string_get_char(str, 0)) { case '6': token_info->digits = TOTP_6_DIGITS; @@ -27,7 +27,7 @@ static bool token_info_set_digits_from_str(TokenInfo* token_info, FuriString* st return false; } -static bool token_info_set_algo_from_str(TokenInfo* token_info, FuriString* str) { +static bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) { if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) { token_info->algo = SHA1; return true; @@ -79,10 +79,16 @@ void totp_cli_command_add_docopt_options() { TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX) " Show console user input as-is without masking\r\n"); } +static void furi_string_secure_free(FuriString* str) { + for(long i = furi_string_size(str) - 1; i >= 0; i--) { + furi_string_set_char(str, i, '\0'); + } + + furi_string_free(str); +} + void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { FuriString* temp_str = furi_string_alloc(); - const char* temp_cstr; - TokenInfo* token_info = token_info_alloc(); // Reading token name @@ -93,9 +99,9 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl return; } - temp_cstr = furi_string_get_cstr(temp_str); - token_info->name = malloc(strlen(temp_cstr) + 1); - strcpy(token_info->name, temp_cstr); + size_t temp_cstr_len = furi_string_size(temp_str); + token_info->name = malloc(temp_cstr_len + 1); + strlcpy(token_info->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1); // Read optional arguments bool mask_user_input = true; @@ -146,13 +152,15 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl uint8_t c; while(cli_read(cli, &c, 1) == 1) { if(c == CliSymbolAsciiEsc) { + // Some keys generating escape-sequences + // We need to ignore them as we case about alpha-numerics only uint8_t c2; cli_read_timeout(cli, &c2, 1, 0); cli_read_timeout(cli, &c2, 1, 0); } else if(c == CliSymbolAsciiETX) { TOTP_CLI_DELETE_CURRENT_LINE(); - TOTP_CLI_PRINTF("Cancelled by user"); - furi_string_free(temp_str); + TOTP_CLI_PRINTF("Cancelled by user\r\n"); + furi_string_secure_free(temp_str); token_info_free(token_info); return; } else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { @@ -166,8 +174,7 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { size_t temp_str_size = furi_string_size(temp_str); if(temp_str_size > 0) { - TOTP_CLI_PRINTF("\b \b"); - fflush(stdout); + TOTP_CLI_DELETE_LAST_CHAR(); furi_string_left(temp_str, temp_str_size - 1); } } else if(c == CliSymbolAsciiCR) { @@ -176,25 +183,26 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl } } - temp_cstr = furi_string_get_cstr(temp_str); - TOTP_CLI_DELETE_LAST_LINE(); if(!totp_cli_ensure_authenticated(plugin_state, cli)) { - furi_string_free(temp_str); + furi_string_secure_free(temp_str); token_info_free(token_info); return; } - if(!token_info_set_secret(token_info, temp_cstr, strlen(temp_cstr), plugin_state->iv)) { + if(!token_info_set_secret( + token_info, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + plugin_state->iv)) { TOTP_CLI_PRINTF("Token secret seems to be invalid and can not be parsed\r\n"); - furi_string_free(temp_str); + furi_string_secure_free(temp_str); token_info_free(token_info); return; } - furi_string_reset(temp_str); - furi_string_free(temp_str); + furi_string_secure_free(temp_str); bool load_generate_token_scene = false; if(plugin_state->current_scene == TotpSceneGenerateToken) { diff --git a/applications/plugins/totp/services/config/config.c b/applications/plugins/totp/services/config/config.c index 0854d79ad..bb6cffde0 100644 --- a/applications/plugins/totp/services/config/config.c +++ b/applications/plugins/totp/services/config/config.c @@ -10,7 +10,7 @@ #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf" #define CONFIG_FILE_BACKUP_PATH CONFIG_FILE_PATH ".backup" -static uint8_t token_info_get_digits_as_int(TokenInfo* token_info) { +static uint8_t token_info_get_digits_as_int(const TokenInfo* token_info) { switch(token_info->digits) { case TOTP_6_DIGITS: return 6; @@ -32,7 +32,7 @@ static void token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits } } -static char* token_info_get_algo_as_cstr(TokenInfo* token_info) { +static char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { switch(token_info->algo) { case SHA1: return TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME; @@ -45,7 +45,7 @@ static char* token_info_get_algo_as_cstr(TokenInfo* token_info) { return NULL; } -static void token_info_set_algo_from_str(TokenInfo* token_info, FuriString* str) { +static void token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) { if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) { token_info->algo = SHA1; } else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME) == 0) { @@ -152,7 +152,7 @@ FlipperFormat* totp_open_config_file(Storage* storage) { return fff_data_file; } -void totp_config_file_save_new_token_i(FlipperFormat* file, TokenInfo* token_info) { +void totp_config_file_save_new_token_i(FlipperFormat* file, const TokenInfo* token_info) { flipper_format_seek_to_end(file); flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name); bool token_is_valid = token_info->token != NULL && token_info->token_length > 0; @@ -170,7 +170,7 @@ void totp_config_file_save_new_token_i(FlipperFormat* file, TokenInfo* token_inf flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &digits_count_as_uint32, 1); } -void totp_config_file_save_new_token(TokenInfo* token_info) { +void totp_config_file_save_new_token(const TokenInfo* token_info) { Storage* cfg_storage = totp_open_storage(); FlipperFormat* file = totp_open_config_file(cfg_storage); @@ -190,7 +190,7 @@ void totp_config_file_update_timezone_offset(float new_timezone_offset) { totp_close_storage(); } -void totp_full_save_config_file(PluginState* const plugin_state) { +void totp_full_save_config_file(const PluginState* const plugin_state) { Storage* storage = totp_open_storage(); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); @@ -209,7 +209,7 @@ void totp_full_save_config_file(PluginState* const plugin_state) { flipper_format_write_bool(fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1); ListNode* node = plugin_state->tokens_list; while(node != NULL) { - TokenInfo* token_info = node->data; + const TokenInfo* token_info = node->data; totp_config_file_save_new_token_i(fff_data_file, token_info); node = node->next; } @@ -343,9 +343,9 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) TokenInfo* tokenInfo = token_info_alloc(); - const char* temp_cstr = furi_string_get_cstr(temp_str); - tokenInfo->name = (char*)malloc(strlen(temp_cstr) + 1); - strcpy(tokenInfo->name, temp_cstr); + size_t temp_cstr_len = furi_string_size(temp_str); + tokenInfo->name = (char*)malloc(temp_cstr_len + 1); + strlcpy(tokenInfo->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1); uint32_t secret_bytes_count; if(!flipper_format_get_value_count( @@ -355,9 +355,11 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) if(secret_bytes_count == 1) { // Plain secret key if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) { - temp_cstr = furi_string_get_cstr(temp_str); if(token_info_set_secret( - tokenInfo, temp_cstr, strlen(temp_cstr), &plugin_state->iv[0])) { + tokenInfo, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + &plugin_state->iv[0])) { FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name); } else { tokenInfo->token = NULL; diff --git a/applications/plugins/totp/services/config/config.h b/applications/plugins/totp/services/config/config.h index 76cb40b2c..d452ad4b3 100644 --- a/applications/plugins/totp/services/config/config.h +++ b/applications/plugins/totp/services/config/config.h @@ -16,8 +16,8 @@ Storage* totp_open_storage(); void totp_close_storage(); FlipperFormat* totp_open_config_file(Storage* storage); void totp_close_config_file(FlipperFormat* file); -void totp_full_save_config_file(PluginState* const plugin_state); +void totp_full_save_config_file(const PluginState* const plugin_state); void totp_config_file_load_base(PluginState* const plugin_state); TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state); -void totp_config_file_save_new_token(TokenInfo* token_info); +void totp_config_file_save_new_token(const TokenInfo* token_info); void totp_config_file_update_timezone_offset(float new_timezone_offset); diff --git a/applications/plugins/totp/services/crypto/crypto.c b/applications/plugins/totp/services/crypto/crypto.c index ade2f9f49..79e41e6fd 100644 --- a/applications/plugins/totp/services/crypto/crypto.c +++ b/applications/plugins/totp/services/crypto/crypto.c @@ -3,6 +3,7 @@ #include #include "../config/config.h" #include "../../types/common.h" +#include "memset_s.h" #define CRYPTO_KEY_SLOT 2 #define CRYPTO_VERIFY_KEY "FFF_Crypto_pass" @@ -11,13 +12,13 @@ uint8_t* totp_crypto_encrypt( const uint8_t* plain_data, - const uint8_t plain_data_length, + const size_t plain_data_length, const uint8_t* iv, - uint8_t* encrypted_data_length) { + size_t* encrypted_data_length) { uint8_t* encrypted_data; size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR; if(remain) { - uint8_t plain_data_aligned_length = plain_data_length - remain + CRYPTO_ALIGNMENT_FACTOR; + size_t plain_data_aligned_length = plain_data_length - remain + CRYPTO_ALIGNMENT_FACTOR; uint8_t* plain_data_aligned = malloc(plain_data_aligned_length); memset(plain_data_aligned, 0, plain_data_aligned_length); memcpy(plain_data_aligned, plain_data, plain_data_length); @@ -29,7 +30,7 @@ uint8_t* totp_crypto_encrypt( furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length); furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); - memset(plain_data_aligned, 0, plain_data_aligned_length); + memset_s(plain_data_aligned, sizeof(plain_data_aligned), 0, plain_data_aligned_length); free(plain_data_aligned); } else { encrypted_data = malloc(plain_data_length); @@ -45,9 +46,9 @@ uint8_t* totp_crypto_encrypt( uint8_t* totp_crypto_decrypt( const uint8_t* encrypted_data, - const uint8_t encrypted_data_length, + const size_t encrypted_data_length, const uint8_t* iv, - uint8_t* decrypted_data_length) { + size_t* decrypted_data_length) { *decrypted_data_length = encrypted_data_length; uint8_t* decrypted_data = malloc(*decrypted_data_length); furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); @@ -56,7 +57,7 @@ uint8_t* totp_crypto_decrypt( return decrypted_data; } -void totp_crypto_seed_iv(PluginState* plugin_state, uint8_t* pin, uint8_t pin_length) { +void totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) { if(plugin_state->crypto_verify_data == NULL) { FURI_LOG_D(LOGGING_TAG, "Generating new IV"); furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE); @@ -118,8 +119,8 @@ void totp_crypto_seed_iv(PluginState* plugin_state, uint8_t* pin, uint8_t pin_le } bool totp_crypto_verify_key(const PluginState* plugin_state) { - uint8_t decrypted_key_length; - uint8_t* decrypted_key = totp_crypto_decrypt( + size_t decrypted_key_length; + const uint8_t* decrypted_key = totp_crypto_decrypt( plugin_state->crypto_verify_data, plugin_state->crypto_verify_data_length, &plugin_state->iv[0], diff --git a/applications/plugins/totp/services/crypto/crypto.h b/applications/plugins/totp/services/crypto/crypto.h index 9fc319659..f0a28f798 100644 --- a/applications/plugins/totp/services/crypto/crypto.h +++ b/applications/plugins/totp/services/crypto/crypto.h @@ -4,13 +4,13 @@ uint8_t* totp_crypto_encrypt( const uint8_t* plain_data, - const uint8_t plain_data_length, + const size_t plain_data_length, const uint8_t* iv, - uint8_t* encrypted_data_length); + size_t* encrypted_data_length); uint8_t* totp_crypto_decrypt( const uint8_t* encrypted_data, - const uint8_t encrypted_data_length, + const size_t encrypted_data_length, const uint8_t* iv, - uint8_t* decrypted_data_length); -void totp_crypto_seed_iv(PluginState* plugin_state, uint8_t* pin, uint8_t pin_length); + size_t* decrypted_data_length); +void totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length); bool totp_crypto_verify_key(const PluginState* plugin_state); \ No newline at end of file diff --git a/applications/plugins/totp/services/crypto/memset_s.c b/applications/plugins/totp/services/crypto/memset_s.c new file mode 100644 index 000000000..81c285c0d --- /dev/null +++ b/applications/plugins/totp/services/crypto/memset_s.c @@ -0,0 +1,22 @@ +#include "memset_s.h" + +#define RSIZE_MAX 0x7fffffffffffffffUL + +errno_t memset_s(void* s, rsize_t smax, int c, rsize_t n) { + if(!s || smax > RSIZE_MAX) { + return EINVAL; + } + + errno_t violation_present = 0; + if(n > smax) { + n = smax; + violation_present = EINVAL; + } + + volatile unsigned char* v = s; + for(rsize_t i = 0u; i < n; ++i) { + *v++ = (unsigned char)c; + } + + return violation_present; +} \ No newline at end of file diff --git a/applications/plugins/totp/services/crypto/memset_s.h b/applications/plugins/totp/services/crypto/memset_s.h new file mode 100644 index 000000000..2889e23b2 --- /dev/null +++ b/applications/plugins/totp/services/crypto/memset_s.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +#ifndef _RSIZE_T_DECLARED +typedef uint64_t rsize_t; +#define _RSIZE_T_DECLARED +#endif +#ifndef _ERRNOT_DECLARED +typedef int16_t errno_t; +#define _ERRNOT_DECLARED +#endif + +errno_t memset_s(void* s, rsize_t smax, int c, rsize_t n); \ No newline at end of file diff --git a/applications/plugins/totp/services/totp/totp.c b/applications/plugins/totp/services/totp/totp.c index 90e49367d..68531e171 100644 --- a/applications/plugins/totp/services/totp/totp.c +++ b/applications/plugins/totp/services/totp/totp.c @@ -42,14 +42,14 @@ uint32_t otp_generate( TOTP_ALGO algo, uint8_t digits, const uint8_t* plain_secret, - uint8_t plain_secret_length, + size_t plain_secret_length, uint64_t input) { uint8_t* hmac = malloc(64); memset(hmac, 0, 64); uint64_t input_swapped = swap_uint64(input); - int hmac_len = (*(algo))(plain_secret, plain_secret_length, (uint8_t*)&input_swapped, 8, hmac); + int hmac_len = (*algo)(plain_secret, plain_secret_length, (uint8_t*)&input_swapped, 8, hmac); if(hmac_len == 0) { free(hmac); return OTP_ERROR; @@ -80,7 +80,7 @@ uint32_t totp_at( TOTP_ALGO algo, uint8_t digits, const uint8_t* plain_secret, - uint8_t plain_secret_length, + size_t plain_secret_length, uint64_t for_time, float timezone, uint8_t interval) { @@ -96,9 +96,9 @@ uint32_t totp_at( static int totp_algo_sha1( const uint8_t* key, - uint8_t key_length, + size_t key_length, const uint8_t* input, - uint8_t input_length, + size_t input_length, uint8_t* output) { hmac_sha1(key, key_length, input, input_length, output); return HMAC_SHA1_RESULT_SIZE; @@ -106,9 +106,9 @@ static int totp_algo_sha1( static int totp_algo_sha256( const uint8_t* key, - uint8_t key_length, + size_t key_length, const uint8_t* input, - uint8_t input_length, + size_t input_length, uint8_t* output) { hmac_sha256(key, key_length, input, input_length, output); return HMAC_SHA256_RESULT_SIZE; @@ -116,9 +116,9 @@ static int totp_algo_sha256( static int totp_algo_sha512( const uint8_t* key, - uint8_t key_length, + size_t key_length, const uint8_t* input, - uint8_t input_length, + size_t input_length, uint8_t* output) { hmac_sha512(key, key_length, input, input_length, output); return HMAC_SHA512_RESULT_SIZE; diff --git a/applications/plugins/totp/services/totp/totp.h b/applications/plugins/totp/services/totp/totp.h index 31e70e01b..431ca11aa 100644 --- a/applications/plugins/totp/services/totp/totp.h +++ b/applications/plugins/totp/services/totp/totp.h @@ -17,9 +17,9 @@ */ typedef int (*TOTP_ALGO)( const uint8_t* key, - uint8_t key_length, + size_t key_length, const uint8_t* input, - uint8_t input_length, + size_t input_length, uint8_t* output); /* @@ -47,7 +47,7 @@ uint32_t totp_at( TOTP_ALGO algo, uint8_t digits, const uint8_t* plain_secret, - uint8_t plain_secret_length, + size_t plain_secret_length, uint64_t for_time, float timezone, uint8_t interval); diff --git a/applications/plugins/totp/services/ui/icons.h b/applications/plugins/totp/services/ui/icons.h index a9139403f..2ce25a898 100644 --- a/applications/plugins/totp/services/ui/icons.h +++ b/applications/plugins/totp/services/ui/icons.h @@ -1,5 +1,6 @@ #pragma once +#include #include #define ICON_ARROW_LEFT_8x9_WIDTH 8 diff --git a/applications/plugins/totp/services/ui/ui_controls.c b/applications/plugins/totp/services/ui/ui_controls.c index 7f6a4dd4d..b01036f2e 100644 --- a/applications/plugins/totp/services/ui/ui_controls.c +++ b/applications/plugins/totp/services/ui/ui_controls.c @@ -5,7 +5,11 @@ #define TEXT_BOX_HEIGHT 13 #define TEXT_BOX_MARGIN 4 -void ui_control_text_box_render(Canvas* const canvas, int8_t y, char* text, bool is_selected) { +void ui_control_text_box_render( + Canvas* const canvas, + int16_t y, + const char* text, + bool is_selected) { if(y < -TEXT_BOX_HEIGHT) { return; } @@ -44,7 +48,7 @@ void ui_control_select_render( int16_t x, int16_t y, uint8_t width, - char* text, + const char* text, bool is_selected) { if(y < -TEXT_BOX_HEIGHT) { return; @@ -99,7 +103,7 @@ void ui_control_button_render( int16_t y, uint8_t width, uint8_t height, - char* text, + const char* text, bool is_selected) { if(y < -height) { return; diff --git a/applications/plugins/totp/services/ui/ui_controls.h b/applications/plugins/totp/services/ui/ui_controls.h index e86b3e5d9..ef3af5f55 100644 --- a/applications/plugins/totp/services/ui/ui_controls.h +++ b/applications/plugins/totp/services/ui/ui_controls.h @@ -3,19 +3,23 @@ #include #include -void ui_control_text_box_render(Canvas* const canvas, int8_t y, char* text, bool is_selected); +void ui_control_text_box_render( + Canvas* const canvas, + int16_t y, + const char* text, + bool is_selected); void ui_control_button_render( Canvas* const canvas, int16_t x, int16_t y, uint8_t width, uint8_t height, - char* text, + const char* text, bool is_selected); void ui_control_select_render( Canvas* const canvas, int16_t x, int16_t y, uint8_t width, - char* text, + const char* text, bool is_selected); diff --git a/applications/plugins/totp/totp_app.c b/applications/plugins/totp/totp_app.c index ede8416b6..f296a734b 100644 --- a/applications/plugins/totp/totp_app.c +++ b/applications/plugins/totp/totp_app.c @@ -154,22 +154,22 @@ int32_t totp_app() { if(plugin_state->changing_scene) continue; FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = acquire_mutex_block(&state_mutex); + PluginState* plugin_state_m = acquire_mutex_block(&state_mutex); if(event_status == FuriStatusOk) { if(event.type == EventTypeKey) { last_user_interaction_time = furi_get_tick(); } - processing = totp_scene_director_handle_event(&event, plugin_state); + processing = totp_scene_director_handle_event(&event, plugin_state_m); } else if( - plugin_state->pin_set && plugin_state->current_scene != TotpSceneAuthentication && + plugin_state_m->pin_set && plugin_state_m->current_scene != TotpSceneAuthentication && furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + totp_scene_director_activate_scene(plugin_state_m, TotpSceneAuthentication, NULL); } view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + release_mutex(&state_mutex, plugin_state_m); } view_port_enabled_set(view_port, false); diff --git a/applications/plugins/totp/types/plugin_state.h b/applications/plugins/totp/types/plugin_state.h index 98afe53cf..26e7e244b 100644 --- a/applications/plugins/totp/types/plugin_state.h +++ b/applications/plugins/totp/types/plugin_state.h @@ -22,7 +22,7 @@ typedef struct { uint8_t tokens_count; uint8_t* crypto_verify_data; - uint8_t crypto_verify_data_length; + size_t crypto_verify_data_length; bool pin_set; uint8_t iv[TOTP_IV_SIZE]; uint8_t base_iv[TOTP_IV_SIZE]; diff --git a/applications/plugins/totp/types/token_info.c b/applications/plugins/totp/types/token_info.c index 09ad1230a..2a37c4d44 100644 --- a/applications/plugins/totp/types/token_info.c +++ b/applications/plugins/totp/types/token_info.c @@ -5,6 +5,7 @@ #include "common.h" #include "../services/base32/base32.h" #include "../services/crypto/crypto.h" +#include "../services/crypto/memset_s.h" TokenInfo* token_info_alloc() { TokenInfo* tokenInfo = malloc(sizeof(TokenInfo)); @@ -23,7 +24,7 @@ void token_info_free(TokenInfo* token_info) { bool token_info_set_secret( TokenInfo* token_info, const char* base32_token_secret, - uint8_t token_secret_length, + size_t token_secret_length, uint8_t* iv) { uint8_t* plain_secret = malloc(token_secret_length); int plain_secret_length = @@ -37,7 +38,7 @@ bool token_info_set_secret( result = false; } - memset(plain_secret, 0, token_secret_length); + memset_s(plain_secret, sizeof(plain_secret), 0, token_secret_length); free(plain_secret); return result; } diff --git a/applications/plugins/totp/types/token_info.h b/applications/plugins/totp/types/token_info.h index e40c6e9bf..3d0f0ec61 100644 --- a/applications/plugins/totp/types/token_info.h +++ b/applications/plugins/totp/types/token_info.h @@ -8,7 +8,7 @@ typedef enum { TOTP_6_DIGITS, TOTP_8_DIGITS } TokenDigitsCount; typedef struct { uint8_t* token; - uint8_t token_length; + size_t token_length; char* name; TokenHashAlgo algo; TokenDigitsCount digits; @@ -19,6 +19,6 @@ void token_info_free(TokenInfo* token_info); bool token_info_set_secret( TokenInfo* token_info, const char* base32_token_secret, - uint8_t token_secret_length, + size_t token_secret_length, uint8_t* iv); uint8_t token_info_get_digits_count(TokenInfo* token_info); diff --git a/applications/plugins/usbkeyboard/application.fam b/applications/plugins/usbkeyboard/application.fam index 6bef0d9f2..1e419f3fe 100644 --- a/applications/plugins/usbkeyboard/application.fam +++ b/applications/plugins/usbkeyboard/application.fam @@ -11,5 +11,5 @@ App( order=60, fap_icon="usb_keyboard_10px.png", fap_category="Misc", - fap_icon_assets="icons", + fap_icon_assets="assets", ) diff --git a/applications/plugins/usbkeyboard/assets/Arr_dwn_7x9.png b/applications/plugins/usbkeyboard/assets/Arr_dwn_7x9.png new file mode 100644 index 000000000..d4034efc4 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Arr_dwn_7x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Arr_up_7x9.png b/applications/plugins/usbkeyboard/assets/Arr_up_7x9.png new file mode 100644 index 000000000..28b4236a2 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Arr_up_7x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonDown_7x4.png b/applications/plugins/usbkeyboard/assets/ButtonDown_7x4.png new file mode 100644 index 000000000..2954bb6a6 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/ButtonDown_7x4.png differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonLeft_4x7.png b/applications/plugins/usbkeyboard/assets/ButtonLeft_4x7.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/ButtonLeft_4x7.png differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonRight_4x7.png b/applications/plugins/usbkeyboard/assets/ButtonRight_4x7.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/ButtonRight_4x7.png differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonUp_7x4.png b/applications/plugins/usbkeyboard/assets/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/ButtonUp_7x4.png differ diff --git a/applications/plugins/usbkeyboard/assets/Button_18x18.png b/applications/plugins/usbkeyboard/assets/Button_18x18.png new file mode 100644 index 000000000..30a5b4fab Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Button_18x18.png differ diff --git a/applications/plugins/usbkeyboard/assets/Circles_47x47.png b/applications/plugins/usbkeyboard/assets/Circles_47x47.png new file mode 100644 index 000000000..6a16ebf7b Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Circles_47x47.png differ diff --git a/applications/plugins/usbkeyboard/assets/Left_mouse_icon_9x9.png b/applications/plugins/usbkeyboard/assets/Left_mouse_icon_9x9.png new file mode 100644 index 000000000..c533d8572 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Left_mouse_icon_9x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Like_def_11x9.png b/applications/plugins/usbkeyboard/assets/Like_def_11x9.png new file mode 100644 index 000000000..555bea3d4 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Like_def_11x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Like_pressed_17x17.png b/applications/plugins/usbkeyboard/assets/Like_pressed_17x17.png new file mode 100644 index 000000000..f5bf276f3 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Like_pressed_17x17.png differ diff --git a/applications/plugins/usbkeyboard/assets/Ok_btn_9x9.png b/applications/plugins/usbkeyboard/assets/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Ok_btn_9x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Ok_btn_pressed_13x13.png b/applications/plugins/usbkeyboard/assets/Ok_btn_pressed_13x13.png new file mode 100644 index 000000000..6b46ba3a8 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Ok_btn_pressed_13x13.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_down_7x9.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_down_7x9.png new file mode 100644 index 000000000..9687397af Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pin_arrow_down_7x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_left_9x7.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_left_9x7.png new file mode 100644 index 000000000..fb4ded78f Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pin_arrow_left_9x7.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_right_9x7.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_right_9x7.png new file mode 100644 index 000000000..97648d176 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pin_arrow_right_9x7.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_up_7x9.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_up_7x9.png new file mode 100644 index 000000000..a91a6fd5e Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pin_arrow_up_7x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_back_arrow_10x8.png b/applications/plugins/usbkeyboard/assets/Pin_back_arrow_10x8.png new file mode 100644 index 000000000..3bafabd14 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pin_back_arrow_10x8.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pressed_Button_13x13.png b/applications/plugins/usbkeyboard/assets/Pressed_Button_13x13.png new file mode 100644 index 000000000..823926b84 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pressed_Button_13x13.png differ diff --git a/applications/plugins/usbkeyboard/assets/Right_mouse_icon_9x9.png b/applications/plugins/usbkeyboard/assets/Right_mouse_icon_9x9.png new file mode 100644 index 000000000..446d7176c Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Right_mouse_icon_9x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Space_65x18.png b/applications/plugins/usbkeyboard/assets/Space_65x18.png new file mode 100644 index 000000000..b60ae5097 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Space_65x18.png differ diff --git a/applications/plugins/usbkeyboard/assets/Voldwn_6x6.png b/applications/plugins/usbkeyboard/assets/Voldwn_6x6.png new file mode 100644 index 000000000..d7a82a2df Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Voldwn_6x6.png differ diff --git a/applications/plugins/usbkeyboard/assets/Volup_8x6.png b/applications/plugins/usbkeyboard/assets/Volup_8x6.png new file mode 100644 index 000000000..4b7ec66d6 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Volup_8x6.png differ diff --git a/applications/plugins/wav_player/application.fam b/applications/plugins/wav_player/application.fam index 04b558315..ec0c76291 100644 --- a/applications/plugins/wav_player/application.fam +++ b/applications/plugins/wav_player/application.fam @@ -8,5 +8,5 @@ App( order=60, fap_icon="wav_10px.png", fap_category="Music", - fap_icon_assets="icons", + fap_icon_assets="images", ) diff --git a/applications/plugins/wav_player/images/music_10px.png b/applications/plugins/wav_player/images/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/applications/plugins/wav_player/images/music_10px.png differ diff --git a/applications/plugins/wav_player/wav_player.c b/applications/plugins/wav_player/wav_player.c index 89659bff7..f8fce4549 100644 --- a/applications/plugins/wav_player/wav_player.c +++ b/applications/plugins/wav_player/wav_player.c @@ -13,6 +13,8 @@ #include #include +#include + #define TAG "WavPlayer" #define WAVPLAYER_FOLDER "/ext/wav_player" diff --git a/assets/icons/Infrared/Rotate_25x27.png b/assets/icons/Infrared/Rotate_25x27.png index 0c62e9dc1..648634a09 100644 Binary files a/assets/icons/Infrared/Rotate_25x27.png and b/assets/icons/Infrared/Rotate_25x27.png differ diff --git a/assets/icons/Infrared/Rotate_hvr_25x27.png b/assets/icons/Infrared/Rotate_hvr_25x27.png index d2ab0f3fc..a2b5cf93d 100644 Binary files a/assets/icons/Infrared/Rotate_hvr_25x27.png and b/assets/icons/Infrared/Rotate_hvr_25x27.png differ diff --git a/assets/icons/Infrared/Timer_25x27.png b/assets/icons/Infrared/Timer_25x27.png index 5ce468198..2f1853a34 100644 Binary files a/assets/icons/Infrared/Timer_25x27.png and b/assets/icons/Infrared/Timer_25x27.png differ diff --git a/assets/icons/Infrared/Timer_hvr_25x27.png b/assets/icons/Infrared/Timer_hvr_25x27.png index f6959c244..d4dffa544 100644 Binary files a/assets/icons/Infrared/Timer_hvr_25x27.png and b/assets/icons/Infrared/Timer_hvr_25x27.png differ diff --git a/assets/icons/SubGhz/Dynamic_9x7.png b/assets/icons/SubGhz/Dynamic_9x7.png new file mode 100644 index 000000000..50922d4fb Binary files /dev/null and b/assets/icons/SubGhz/Dynamic_9x7.png differ diff --git a/assets/icons/SubGhz/Raw_9x7.png b/assets/icons/SubGhz/Raw_9x7.png new file mode 100644 index 000000000..39a6d0386 Binary files /dev/null and b/assets/icons/SubGhz/Raw_9x7.png differ diff --git a/assets/icons/SubGhz/Static_9x7.png b/assets/icons/SubGhz/Static_9x7.png new file mode 100644 index 000000000..dad4833e3 Binary files /dev/null and b/assets/icons/SubGhz/Static_9x7.png differ diff --git a/firmware.scons b/firmware.scons index d501996b3..63a1aa3f7 100644 --- a/firmware.scons +++ b/firmware.scons @@ -178,23 +178,6 @@ sources.extend( ) ) - -fwenv.AppendUnique( - LINKFLAGS=[ - "-specs=nano.specs", - "-specs=nosys.specs", - "-Wl,--gc-sections", - "-Wl,--undefined=uxTopUsedPriority", - "-Wl,--wrap,_malloc_r", - "-Wl,--wrap,_free_r", - "-Wl,--wrap,_calloc_r", - "-Wl,--wrap,_realloc_r", - "-n", - "-Xlinker", - "-Map=${TARGET}.map", - ], -) - # Debug # print(fwenv.Dump()) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 4df8b4d8e..9f1968b22 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.1,, +Version,+,8.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 069ac4ea4..3ebf4f82b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -620,6 +620,10 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity( uint16_t in_buff_bits, uint8_t* out_data, uint8_t* out_parity) { + if(in_buff_bits < 8) { + out_data[0] = in_buff[0]; + return in_buff_bits; + } if(in_buff_bits % 9 != 0) { return 0; } @@ -635,7 +639,7 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity( bit_processed += 9; curr_byte++; } - return curr_byte; + return curr_byte * 8; } bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { @@ -692,8 +696,8 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { 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); + tx_rx->rx_bits = furi_hal_nfc_bitstream_to_data_and_parity( + temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity); } else { memcpy(tx_rx->rx_data, temp_rx_buff, MIN(*temp_rx_bits / 8, FURI_HAL_NFC_DATA_BUFF_SIZE)); tx_rx->rx_bits = *temp_rx_bits; diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c index eeacbfe6e..1629a06d5 100644 --- a/lib/nfc/helpers/reader_analyzer.c +++ b/lib/nfc/helpers/reader_analyzer.c @@ -205,10 +205,17 @@ NfcProtocol FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) { furi_assert(instance); - + instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic]; return &instance->nfc_data; } +void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data) { + furi_assert(instance); + furi_assert(nfc_data); + + memcpy(&instance->nfc_data, nfc_data, sizeof(FuriHalNfcDevData)); +} + static void reader_analyzer_write( ReaderAnalyzer* instance, uint8_t* data, diff --git a/lib/nfc/helpers/reader_analyzer.h b/lib/nfc/helpers/reader_analyzer.h index cc501f5a6..13bf4d77c 100644 --- a/lib/nfc/helpers/reader_analyzer.h +++ b/lib/nfc/helpers/reader_analyzer.h @@ -35,6 +35,8 @@ NfcProtocol FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance); +void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data); + void reader_analyzer_prepare_tx_rx( ReaderAnalyzer* instance, FuriHalNfcTxRxContext* tx_rx, diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 15f424fa3..bb43c7021 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -1155,6 +1155,13 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; + // Load CUID + uint8_t* cuid_start = data->uid; + if(data->uid_len == 7) { + cuid_start = &data->uid[3]; + } + data->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | + (cuid_start[3]); // Parse other data if(dev->format == NfcDeviceSaveFormatMifareUl) { if(!nfc_device_load_mifare_ul_data(file, dev)) break; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 71e10408e..ec199be82 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -104,6 +104,10 @@ int32_t nfc_worker_task(void* context) { nfc_worker_emulate_mf_ultralight(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { nfc_worker_emulate_mf_classic(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateMfClassicWrite) { + nfc_worker_write_mf_classic(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateMfClassicUpdate) { + nfc_worker_update_mf_classic(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { nfc_worker_mf_ultralight_read_auth(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { @@ -790,6 +794,144 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { rfal_platform_spi_release(); } +void nfc_worker_write_mf_classic(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + bool card_found_notified = false; + FuriHalNfcDevData nfc_data = {}; + MfClassicData* src_data = &nfc_worker->dev_data->mf_classic_data; + MfClassicData dest_data = *src_data; + + while(nfc_worker->state == NfcWorkerStateMfClassicWrite) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(!card_found_notified) { + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_found_notified = true; + } + furi_hal_nfc_sleep(); + + FURI_LOG_I(TAG, "Check low level nfc data"); + if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) { + FURI_LOG_E(TAG, "Wrong card"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + FURI_LOG_I(TAG, "Check mf classic type"); + MfClassicType type = + mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); + if(type != nfc_worker->dev_data->mf_classic_data.type) { + FURI_LOG_E(TAG, "Wrong mf classic type"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + // Set blocks not read + mf_classic_set_sector_data_not_read(&dest_data); + FURI_LOG_I(TAG, "Updating card sectors"); + uint8_t total_sectors = mf_classic_get_total_sectors_num(type); + bool write_success = true; + for(uint8_t i = 0; i < total_sectors; i++) { + FURI_LOG_I(TAG, "Reading sector %d", i); + mf_classic_read_sector(&tx_rx, &dest_data, i); + bool old_data_read = mf_classic_is_sector_data_read(src_data, i); + bool new_data_read = mf_classic_is_sector_data_read(&dest_data, i); + if(old_data_read != new_data_read) { + FURI_LOG_E(TAG, "Failed to update sector %d", i); + write_success = false; + break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break; + if(!mf_classic_write_sector(&tx_rx, &dest_data, src_data, i)) { + FURI_LOG_E(TAG, "Failed to write %d sector", i); + write_success = false; + break; + } + } + if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break; + if(write_success) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + break; + } else { + nfc_worker->callback(NfcWorkerEventFail, nfc_worker->context); + break; + } + + } else { + if(card_found_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_found_notified = false; + } + } + furi_delay_ms(300); + } +} + +void nfc_worker_update_mf_classic(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + bool card_found_notified = false; + FuriHalNfcDevData nfc_data = {}; + MfClassicData* old_data = &nfc_worker->dev_data->mf_classic_data; + MfClassicData new_data = *old_data; + + while(nfc_worker->state == NfcWorkerStateMfClassicUpdate) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(!card_found_notified) { + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_found_notified = true; + } + furi_hal_nfc_sleep(); + + FURI_LOG_I(TAG, "Check low level nfc data"); + if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) { + FURI_LOG_E(TAG, "Low level nfc data mismatch"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + FURI_LOG_I(TAG, "Check MF classic type"); + MfClassicType type = + mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); + if(type != nfc_worker->dev_data->mf_classic_data.type) { + FURI_LOG_E(TAG, "MF classic type mismatch"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + // Set blocks not read + mf_classic_set_sector_data_not_read(&new_data); + FURI_LOG_I(TAG, "Updating card sectors"); + uint8_t total_sectors = mf_classic_get_total_sectors_num(type); + bool update_success = true; + for(uint8_t i = 0; i < total_sectors; i++) { + FURI_LOG_I(TAG, "Reading sector %d", i); + mf_classic_read_sector(&tx_rx, &new_data, i); + bool old_data_read = mf_classic_is_sector_data_read(old_data, i); + bool new_data_read = mf_classic_is_sector_data_read(&new_data, i); + if(old_data_read != new_data_read) { + FURI_LOG_E(TAG, "Failed to update sector %d", i); + update_success = false; + break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break; + + // Check updated data + if(update_success) { + *old_data = new_data; + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + break; + } + } else { + if(card_found_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_found_notified = false; + } + } + furi_delay_ms(300); + } +} + void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); @@ -881,7 +1023,13 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer; - FuriHalNfcDevData* nfc_data = reader_analyzer_get_nfc_data(reader_analyzer); + FuriHalNfcDevData* nfc_data = NULL; + if(nfc_worker->dev_data->protocol == NfcDeviceProtocolMifareClassic) { + nfc_data = &nfc_worker->dev_data->nfc_data; + reader_analyzer_set_nfc_data(reader_analyzer, nfc_data); + } else { + nfc_data = reader_analyzer_get_nfc_data(reader_analyzer); + } MfClassicEmulator emulator = { .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), .data = nfc_worker->dev_data->mf_classic_data, diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 3c987b06e..d2c8add87 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -14,6 +14,8 @@ typedef enum { NfcWorkerStateUidEmulate, NfcWorkerStateMfUltralightEmulate, NfcWorkerStateMfClassicEmulate, + NfcWorkerStateMfClassicWrite, + NfcWorkerStateMfClassicUpdate, NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, @@ -49,13 +51,16 @@ typedef enum { NfcWorkerEventNoCardDetected, NfcWorkerEventWrongCardDetected, - // Mifare Classic events + // Read Mifare Classic events NfcWorkerEventNoDictFound, NfcWorkerEventNewSector, NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, - + + // Write Mifare Classic events + NfcWorkerEventWrongCard, + // Detect Reader events NfcWorkerEventDetectReaderDetected, NfcWorkerEventDetectReaderLost, diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index adc80e7f9..5e09962fe 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -43,6 +43,10 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker); +void nfc_worker_write_mf_classic(NfcWorker* nfc_worker); + +void nfc_worker_update_mf_classic(NfcWorker* nfc_worker); + void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker); void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker); diff --git a/lib/nfc/protocols/crypto1.c b/lib/nfc/protocols/crypto1.c index f08164ba9..2ac0ff081 100644 --- a/lib/nfc/protocols/crypto1.c +++ b/lib/nfc/protocols/crypto1.c @@ -73,3 +73,55 @@ uint32_t prng_successor(uint32_t x, uint32_t n) { return SWAPENDIAN(x); } + +void crypto1_decrypt( + Crypto1* crypto, + uint8_t* encrypted_data, + uint16_t encrypted_data_bits, + uint8_t* decrypted_data) { + furi_assert(crypto); + furi_assert(encrypted_data); + furi_assert(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 crypto1_encrypt( + Crypto1* crypto, + uint8_t* keystream, + uint8_t* plain_data, + uint16_t plain_data_bits, + uint8_t* encrypted_data, + uint8_t* encrypted_parity) { + furi_assert(crypto); + furi_assert(plain_data); + furi_assert(encrypted_data); + furi_assert(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))); + } + } +} diff --git a/lib/nfc/protocols/crypto1.h b/lib/nfc/protocols/crypto1.h index 07b39c22c..450d1534e 100644 --- a/lib/nfc/protocols/crypto1.h +++ b/lib/nfc/protocols/crypto1.h @@ -21,3 +21,17 @@ uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted); uint32_t crypto1_filter(uint32_t in); uint32_t prng_successor(uint32_t x, uint32_t n); + +void crypto1_decrypt( + Crypto1* crypto, + uint8_t* encrypted_data, + uint16_t encrypted_data_bits, + uint8_t* decrypted_data); + +void crypto1_encrypt( + Crypto1* crypto, + uint8_t* keystream, + uint8_t* plain_data, + uint16_t plain_data_bits, + uint8_t* encrypted_data, + uint8_t* encrypted_parity); diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index e879ff4ef..7b0e17975 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -9,21 +9,8 @@ #define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U) #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; +#define MF_CLASSIC_READ_BLOCK_CMD (0x30) +#define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0) const char* mf_classic_get_type_str(MfClassicType type) { if(type == MfClassicType1k) { @@ -122,6 +109,24 @@ void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassic FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32); } +bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num) { + furi_assert(data); + + uint8_t first_block = mf_classic_get_first_block_num_of_sector(sector_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num); + bool data_read = true; + for(size_t i = first_block; i < first_block + total_blocks; i++) { + data_read &= mf_classic_is_block_read(data, i); + } + + return data_read; +} + +void mf_classic_set_sector_data_not_read(MfClassicData* data) { + furi_assert(data); + memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); +} + bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) { furi_assert(data); @@ -190,6 +195,9 @@ void mf_classic_get_read_sectors_and_keys( uint8_t* sectors_read, uint8_t* keys_found) { furi_assert(data); + furi_assert(sectors_read); + furi_assert(keys_found); + *sectors_read = 0; *keys_found = 0; uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); @@ -225,12 +233,12 @@ bool mf_classic_is_card_read(MfClassicData* data) { return card_read; } -static bool mf_classic_is_allowed_access_sector_trailer( - MfClassicEmulator* emulator, +bool mf_classic_is_allowed_access_sector_trailer( + MfClassicData* data, uint8_t block_num, MfClassicKey key, MfClassicAction action) { - uint8_t* sector_trailer = emulator->data.block[block_num].value; + uint8_t* sector_trailer = 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) { @@ -266,13 +274,13 @@ static bool mf_classic_is_allowed_access_sector_trailer( return true; } -static bool mf_classic_is_allowed_access_data_block( - MfClassicEmulator* emulator, +bool mf_classic_is_allowed_access_data_block( + MfClassicData* data, uint8_t block_num, MfClassicKey key, MfClassicAction action) { uint8_t* sector_trailer = - emulator->data.block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; + data->block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; uint8_t sector_block; if(block_num <= 128) { @@ -336,9 +344,10 @@ static bool mf_classic_is_allowed_access( 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); + return mf_classic_is_allowed_access_sector_trailer( + &emulator->data, block_num, key, action); } else { - return mf_classic_is_allowed_access_data_block(emulator, block_num, key, action); + return mf_classic_is_allowed_access_data_block(&emulator->data, block_num, key, action); } } @@ -514,25 +523,17 @@ bool mf_classic_read_block( furi_assert(block); bool read_block_success = false; - uint8_t plain_cmd[4] = {MF_CLASSIC_READ_SECT_CMD, block_num, 0x00, 0x00}; + uint8_t plain_cmd[4] = {MF_CLASSIC_READ_BLOCK_CMD, block_num, 0x00, 0x00}; nfca_append_crc16(plain_cmd, 2); - memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data)); - memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity)); - for(uint8_t i = 0; i < 4; i++) { - tx_rx->tx_data[i] = crypto1_byte(crypto, 0x00, 0) ^ plain_cmd[i]; - tx_rx->tx_parity[0] |= - ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_cmd[i])) & 0x01) << (7 - i); - } + crypto1_encrypt(crypto, NULL, plain_cmd, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = 4 * 9; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 8 * (MF_CLASSIC_BLOCK_SIZE + 2)) { uint8_t block_received[MF_CLASSIC_BLOCK_SIZE + 2]; - for(uint8_t i = 0; i < MF_CLASSIC_BLOCK_SIZE + 2; i++) { - block_received[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i]; - } + crypto1_decrypt(crypto, tx_rx->rx_data, tx_rx->rx_bits, block_received); uint16_t crc_calc = nfca_get_crc16(block_received, MF_CLASSIC_BLOCK_SIZE); uint16_t crc_received = (block_received[MF_CLASSIC_BLOCK_SIZE + 1] << 8) | block_received[MF_CLASSIC_BLOCK_SIZE]; @@ -754,49 +755,6 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data 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); @@ -819,7 +777,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->rx_bits); break; } - mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); } if(plain_data[0] == 0x50 && plain_data[1] == 0x00) { @@ -857,7 +815,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->tx_bits = sizeof(nt) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; } else { - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, nt_keystream, nt, @@ -904,7 +862,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint32_t ans = prng_successor(nonce, 96); uint8_t responce[4] = {}; nfc_util_num2bytes(ans, 4, responce); - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, responce, @@ -938,7 +896,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Send NACK uint8_t nack = 0x04; if(is_encrypted) { - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); } else { tx_rx->tx_data[0] = nack; @@ -951,7 +909,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } nfca_append_crc16(block_data, 16); - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, block_data, @@ -967,14 +925,14 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } // Send ACK uint8_t ack = 0x0A; - mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + 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); + 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)) { @@ -1002,7 +960,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } // Send ACK ack = 0x0A; - mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + 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 { @@ -1015,8 +973,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // 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); + crypto1_encrypt(&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); } else { tx_rx->tx_data[0] = nack; } @@ -1027,3 +984,143 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ return true; } + +bool mf_classic_write_block( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key) { + furi_assert(tx_rx); + furi_assert(src_block); + + Crypto1 crypto = {}; + uint8_t plain_data[18] = {}; + uint8_t resp = 0; + bool write_success = false; + + do { + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto)) { + FURI_LOG_D(TAG, "Auth fail"); + break; + } + // Send write command + plain_data[0] = MF_CLASSIC_WRITE_BLOCK_CMD; + plain_data[1] = block_num; + nfca_append_crc16(plain_data, 2); + crypto1_encrypt(&crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = 4 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send write cmd"); + break; + } + + // Send data + memcpy(plain_data, src_block->value, MF_CLASSIC_BLOCK_SIZE); + nfca_append_crc16(plain_data, MF_CLASSIC_BLOCK_SIZE); + crypto1_encrypt( + &crypto, + NULL, + plain_data, + (MF_CLASSIC_BLOCK_SIZE + 2) * 8, + tx_rx->tx_data, + tx_rx->tx_parity); + tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on sending data"); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send data"); + break; + } + write_success = true; + + // Send Halt + plain_data[0] = 0x50; + plain_data[1] = 0x00; + nfca_append_crc16(plain_data, 2); + crypto1_encrypt(&crypto, NULL, plain_data, 2 * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = 2 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + // No response is expected + furi_hal_nfc_tx_rx(tx_rx, 50); + } while(false); + + return write_success; +} + +bool mf_classic_write_sector( + FuriHalNfcTxRxContext* tx_rx, + MfClassicData* dest_data, + MfClassicData* src_data, + uint8_t sec_num) { + furi_assert(tx_rx); + furi_assert(dest_data); + furi_assert(src_data); + + uint8_t first_block = mf_classic_get_first_block_num_of_sector(sec_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(dest_data, sec_num); + bool key_a_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyA); + bool key_b_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyB); + + bool write_success = true; + for(size_t i = first_block; i < first_block + total_blocks; i++) { + // Compare blocks + if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE)) { + bool key_a_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyA, MfClassicActionDataWrite); + bool key_b_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyB, MfClassicActionDataWrite); + + if(key_a_found && key_a_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else if(key_b_found && key_b_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); + if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else { + FURI_LOG_E(TAG, "Failed to find key with write access"); + write_success = false; + break; + } + } else { + FURI_LOG_D(TAG, "Blocks %d are equal", i); + } + } + + return write_success; +} diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index ead846e42..d5467b100 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -27,6 +27,20 @@ typedef enum { MfClassicKeyB, } MfClassicKey; +typedef enum { + MfClassicActionDataRead, + MfClassicActionDataWrite, + MfClassicActionDataInc, + MfClassicActionDataDec, + + MfClassicActionKeyARead, + MfClassicActionKeyAWrite, + MfClassicActionKeyBRead, + MfClassicActionKeyBWrite, + MfClassicActionACRead, + MfClassicActionACWrite, +} MfClassicAction; + typedef struct { uint8_t value[MF_CLASSIC_BLOCK_SIZE]; } MfClassicBlock; @@ -90,6 +104,18 @@ bool mf_classic_is_sector_trailer(uint8_t block); uint8_t mf_classic_get_sector_by_block(uint8_t block); +bool mf_classic_is_allowed_access_sector_trailer( + MfClassicData* data, + uint8_t block_num, + MfClassicKey key, + MfClassicAction action); + +bool mf_classic_is_allowed_access_data_block( + MfClassicData* data, + uint8_t block_num, + MfClassicKey key, + MfClassicAction action); + bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); void mf_classic_set_key_found( @@ -104,6 +130,10 @@ bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num); void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data); +bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num); + +void mf_classic_set_sector_data_not_read(MfClassicData* data); + bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num); bool mf_classic_is_card_read(MfClassicData* data); @@ -145,3 +175,16 @@ uint8_t mf_classic_read_card( uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data); bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); + +bool mf_classic_write_block( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key); + +bool mf_classic_write_sector( + FuriHalNfcTxRxContext* tx_rx, + MfClassicData* dest_data, + MfClassicData* src_data, + uint8_t sec_num); diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index ac2653381..6887218d7 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -660,6 +660,26 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( return 1; } break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: + man = subghz_protocol_keeloq_common_magic_serial_type2_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: + man = subghz_protocol_keeloq_common_magic_serial_type3_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; case KEELOQ_LEARNING_UNKNOWN: // Simple Learning decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); diff --git a/lib/subghz/protocols/keeloq_common.h b/lib/subghz/protocols/keeloq_common.h index 258639e05..a6c0d346e 100644 --- a/lib/subghz/protocols/keeloq_common.h +++ b/lib/subghz/protocols/keeloq_common.h @@ -23,6 +23,8 @@ #define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1 4u #define KEELOQ_LEARNING_FAAC 5u #define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 6u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 7u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 8u /** * Simple Learning Encrypt diff --git a/scripts/fbt/sdk/__init__.py b/scripts/fbt/sdk/__init__.py new file mode 100644 index 000000000..27da5f7c8 --- /dev/null +++ b/scripts/fbt/sdk/__init__.py @@ -0,0 +1,44 @@ +from typing import Set, ClassVar +from dataclasses import dataclass, field + + +@dataclass(frozen=True) +class ApiEntryFunction: + name: str + returns: str + params: str + + csv_type: ClassVar[str] = "Function" + + def dictify(self): + return dict(name=self.name, type=self.returns, params=self.params) + + +@dataclass(frozen=True) +class ApiEntryVariable: + name: str + var_type: str + + csv_type: ClassVar[str] = "Variable" + + def dictify(self): + return dict(name=self.name, type=self.var_type, params=None) + + +@dataclass(frozen=True) +class ApiHeader: + name: str + + csv_type: ClassVar[str] = "Header" + + def dictify(self): + return dict(name=self.name, type=None, params=None) + + +@dataclass +class ApiEntries: + # These are sets, to avoid creating duplicates when we have multiple + # declarations with same signature + functions: Set[ApiEntryFunction] = field(default_factory=set) + variables: Set[ApiEntryVariable] = field(default_factory=set) + headers: Set[ApiHeader] = field(default_factory=set) diff --git a/scripts/fbt/sdk.py b/scripts/fbt/sdk/cache.py similarity index 52% rename from scripts/fbt/sdk.py rename to scripts/fbt/sdk/cache.py index 48f935de3..62d42798c 100644 --- a/scripts/fbt/sdk.py +++ b/scripts/fbt/sdk/cache.py @@ -4,284 +4,18 @@ import csv import operator from enum import Enum, auto -from typing import List, Set, ClassVar, Any -from dataclasses import dataclass, field +from typing import Set, ClassVar, Any +from dataclasses import dataclass from ansi.color import fg -from cxxheaderparser.parser import CxxParser - - -# 'Fixing' complaints about typedefs -CxxParser._fundamentals.discard("wchar_t") - -from cxxheaderparser.types import ( - EnumDecl, - Field, - ForwardDecl, - FriendDecl, - Function, - Method, - Typedef, - UsingAlias, - UsingDecl, - Variable, - Pointer, - Type, - PQName, - NameSpecifier, - FundamentalSpecifier, - Parameter, - Array, - Value, - Token, - FunctionType, +from . import ( + ApiEntries, + ApiEntryFunction, + ApiEntryVariable, + ApiHeader, ) -from cxxheaderparser.parserstate import ( - State, - EmptyBlockState, - ClassBlockState, - ExternBlockState, - NamespaceBlockState, -) - - -@dataclass(frozen=True) -class ApiEntryFunction: - name: str - returns: str - params: str - - csv_type: ClassVar[str] = "Function" - - def dictify(self): - return dict(name=self.name, type=self.returns, params=self.params) - - -@dataclass(frozen=True) -class ApiEntryVariable: - name: str - var_type: str - - csv_type: ClassVar[str] = "Variable" - - def dictify(self): - return dict(name=self.name, type=self.var_type, params=None) - - -@dataclass(frozen=True) -class ApiHeader: - name: str - - csv_type: ClassVar[str] = "Header" - - def dictify(self): - return dict(name=self.name, type=None, params=None) - - -@dataclass -class ApiEntries: - # These are sets, to avoid creating duplicates when we have multiple - # declarations with same signature - functions: Set[ApiEntryFunction] = field(default_factory=set) - variables: Set[ApiEntryVariable] = field(default_factory=set) - headers: Set[ApiHeader] = field(default_factory=set) - - -class SymbolManager: - def __init__(self): - self.api = ApiEntries() - self.name_hashes = set() - - # Calculate hash of name and raise exception if it already is in the set - def _name_check(self, name: str): - name_hash = gnu_sym_hash(name) - if name_hash in self.name_hashes: - raise Exception(f"Hash collision on {name}") - self.name_hashes.add(name_hash) - - def add_function(self, function_def: ApiEntryFunction): - if function_def in self.api.functions: - return - self._name_check(function_def.name) - self.api.functions.add(function_def) - - def add_variable(self, variable_def: ApiEntryVariable): - if variable_def in self.api.variables: - return - self._name_check(variable_def.name) - self.api.variables.add(variable_def) - - def add_header(self, header: str): - self.api.headers.add(ApiHeader(header)) - - -def gnu_sym_hash(name: str): - h = 0x1505 - for c in name: - h = (h << 5) + h + ord(c) - return str(hex(h))[-8:] - - -class SdkCollector: - def __init__(self): - self.symbol_manager = SymbolManager() - - def add_header_to_sdk(self, header: str): - self.symbol_manager.add_header(header) - - def process_source_file_for_sdk(self, file_path: str): - visitor = SdkCxxVisitor(self.symbol_manager) - with open(file_path, "rt") as f: - content = f.read() - parser = CxxParser(file_path, content, visitor, None) - parser.parse() - - def get_api(self): - return self.symbol_manager.api - - -def stringify_array_dimension(size_descr): - if not size_descr: - return "" - return stringify_descr(size_descr) - - -def stringify_array_descr(type_descr): - assert isinstance(type_descr, Array) - return ( - stringify_descr(type_descr.array_of), - stringify_array_dimension(type_descr.size), - ) - - -def stringify_descr(type_descr): - if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)): - return type_descr.name - elif isinstance(type_descr, PQName): - return "::".join(map(stringify_descr, type_descr.segments)) - elif isinstance(type_descr, Pointer): - # Hack - if isinstance(type_descr.ptr_to, FunctionType): - return stringify_descr(type_descr.ptr_to) - return f"{stringify_descr(type_descr.ptr_to)}*" - elif isinstance(type_descr, Type): - return ( - f"{'const ' if type_descr.const else ''}" - f"{'volatile ' if type_descr.volatile else ''}" - f"{stringify_descr(type_descr.typename)}" - ) - elif isinstance(type_descr, Parameter): - return stringify_descr(type_descr.type) - elif isinstance(type_descr, Array): - # Hack for 2d arrays - if isinstance(type_descr.array_of, Array): - argtype, dimension = stringify_array_descr(type_descr.array_of) - return ( - f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]" - ) - return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]" - elif isinstance(type_descr, Value): - return " ".join(map(stringify_descr, type_descr.tokens)) - elif isinstance(type_descr, FunctionType): - return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})" - elif isinstance(type_descr, Token): - return type_descr.value - elif type_descr is None: - return "" - else: - raise Exception("unsupported type_descr: %s" % type_descr) - - -class SdkCxxVisitor: - def __init__(self, symbol_manager: SymbolManager): - self.api = symbol_manager - - def on_variable(self, state: State, v: Variable) -> None: - if not v.extern: - return - - self.api.add_variable( - ApiEntryVariable( - stringify_descr(v.name), - stringify_descr(v.type), - ) - ) - - def on_function(self, state: State, fn: Function) -> None: - if fn.inline or fn.has_body: - return - - self.api.add_function( - ApiEntryFunction( - stringify_descr(fn.name), - stringify_descr(fn.return_type), - ", ".join(map(stringify_descr, fn.parameters)) - + (", ..." if fn.vararg else ""), - ) - ) - - def on_define(self, state: State, content: str) -> None: - pass - - def on_pragma(self, state: State, content: str) -> None: - pass - - def on_include(self, state: State, filename: str) -> None: - pass - - def on_empty_block_start(self, state: EmptyBlockState) -> None: - pass - - def on_empty_block_end(self, state: EmptyBlockState) -> None: - pass - - def on_extern_block_start(self, state: ExternBlockState) -> None: - pass - - def on_extern_block_end(self, state: ExternBlockState) -> None: - pass - - def on_namespace_start(self, state: NamespaceBlockState) -> None: - pass - - def on_namespace_end(self, state: NamespaceBlockState) -> None: - pass - - def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None: - pass - - def on_typedef(self, state: State, typedef: Typedef) -> None: - pass - - def on_using_namespace(self, state: State, namespace: List[str]) -> None: - pass - - def on_using_alias(self, state: State, using: UsingAlias) -> None: - pass - - def on_using_declaration(self, state: State, using: UsingDecl) -> None: - pass - - def on_enum(self, state: State, enum: EnumDecl) -> None: - pass - - def on_class_start(self, state: ClassBlockState) -> None: - pass - - def on_class_field(self, state: State, f: Field) -> None: - pass - - def on_class_method(self, state: ClassBlockState, method: Method) -> None: - pass - - def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None: - pass - - def on_class_end(self, state: ClassBlockState) -> None: - pass - @dataclass(frozen=True) class SdkVersion: diff --git a/scripts/fbt/sdk/collector.py b/scripts/fbt/sdk/collector.py new file mode 100644 index 000000000..578a8c7a6 --- /dev/null +++ b/scripts/fbt/sdk/collector.py @@ -0,0 +1,238 @@ +from typing import List + +from cxxheaderparser.parser import CxxParser +from . import ( + ApiEntries, + ApiEntryFunction, + ApiEntryVariable, + ApiHeader, +) + + +# 'Fixing' complaints about typedefs +CxxParser._fundamentals.discard("wchar_t") + +from cxxheaderparser.types import ( + EnumDecl, + Field, + ForwardDecl, + FriendDecl, + Function, + Method, + Typedef, + UsingAlias, + UsingDecl, + Variable, + Pointer, + Type, + PQName, + NameSpecifier, + FundamentalSpecifier, + Parameter, + Array, + Value, + Token, + FunctionType, +) + +from cxxheaderparser.parserstate import ( + State, + EmptyBlockState, + ClassBlockState, + ExternBlockState, + NamespaceBlockState, +) + + +class SymbolManager: + def __init__(self): + self.api = ApiEntries() + self.name_hashes = set() + + # Calculate hash of name and raise exception if it already is in the set + def _name_check(self, name: str): + name_hash = gnu_sym_hash(name) + if name_hash in self.name_hashes: + raise Exception(f"Hash collision on {name}") + self.name_hashes.add(name_hash) + + def add_function(self, function_def: ApiEntryFunction): + if function_def in self.api.functions: + return + self._name_check(function_def.name) + self.api.functions.add(function_def) + + def add_variable(self, variable_def: ApiEntryVariable): + if variable_def in self.api.variables: + return + self._name_check(variable_def.name) + self.api.variables.add(variable_def) + + def add_header(self, header: str): + self.api.headers.add(ApiHeader(header)) + + +def gnu_sym_hash(name: str): + h = 0x1505 + for c in name: + h = (h << 5) + h + ord(c) + return str(hex(h))[-8:] + + +class SdkCollector: + def __init__(self): + self.symbol_manager = SymbolManager() + + def add_header_to_sdk(self, header: str): + self.symbol_manager.add_header(header) + + def process_source_file_for_sdk(self, file_path: str): + visitor = SdkCxxVisitor(self.symbol_manager) + with open(file_path, "rt") as f: + content = f.read() + parser = CxxParser(file_path, content, visitor, None) + parser.parse() + + def get_api(self): + return self.symbol_manager.api + + +def stringify_array_dimension(size_descr): + if not size_descr: + return "" + return stringify_descr(size_descr) + + +def stringify_array_descr(type_descr): + assert isinstance(type_descr, Array) + return ( + stringify_descr(type_descr.array_of), + stringify_array_dimension(type_descr.size), + ) + + +def stringify_descr(type_descr): + if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)): + return type_descr.name + elif isinstance(type_descr, PQName): + return "::".join(map(stringify_descr, type_descr.segments)) + elif isinstance(type_descr, Pointer): + # Hack + if isinstance(type_descr.ptr_to, FunctionType): + return stringify_descr(type_descr.ptr_to) + return f"{stringify_descr(type_descr.ptr_to)}*" + elif isinstance(type_descr, Type): + return ( + f"{'const ' if type_descr.const else ''}" + f"{'volatile ' if type_descr.volatile else ''}" + f"{stringify_descr(type_descr.typename)}" + ) + elif isinstance(type_descr, Parameter): + return stringify_descr(type_descr.type) + elif isinstance(type_descr, Array): + # Hack for 2d arrays + if isinstance(type_descr.array_of, Array): + argtype, dimension = stringify_array_descr(type_descr.array_of) + return ( + f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]" + ) + return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]" + elif isinstance(type_descr, Value): + return " ".join(map(stringify_descr, type_descr.tokens)) + elif isinstance(type_descr, FunctionType): + return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})" + elif isinstance(type_descr, Token): + return type_descr.value + elif type_descr is None: + return "" + else: + raise Exception("unsupported type_descr: %s" % type_descr) + + +class SdkCxxVisitor: + def __init__(self, symbol_manager: SymbolManager): + self.api = symbol_manager + + def on_variable(self, state: State, v: Variable) -> None: + if not v.extern: + return + + self.api.add_variable( + ApiEntryVariable( + stringify_descr(v.name), + stringify_descr(v.type), + ) + ) + + def on_function(self, state: State, fn: Function) -> None: + if fn.inline or fn.has_body: + return + + self.api.add_function( + ApiEntryFunction( + stringify_descr(fn.name), + stringify_descr(fn.return_type), + ", ".join(map(stringify_descr, fn.parameters)) + + (", ..." if fn.vararg else ""), + ) + ) + + def on_define(self, state: State, content: str) -> None: + pass + + def on_pragma(self, state: State, content: str) -> None: + pass + + def on_include(self, state: State, filename: str) -> None: + pass + + def on_empty_block_start(self, state: EmptyBlockState) -> None: + pass + + def on_empty_block_end(self, state: EmptyBlockState) -> None: + pass + + def on_extern_block_start(self, state: ExternBlockState) -> None: + pass + + def on_extern_block_end(self, state: ExternBlockState) -> None: + pass + + def on_namespace_start(self, state: NamespaceBlockState) -> None: + pass + + def on_namespace_end(self, state: NamespaceBlockState) -> None: + pass + + def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None: + pass + + def on_typedef(self, state: State, typedef: Typedef) -> None: + pass + + def on_using_namespace(self, state: State, namespace: List[str]) -> None: + pass + + def on_using_alias(self, state: State, using: UsingAlias) -> None: + pass + + def on_using_declaration(self, state: State, using: UsingDecl) -> None: + pass + + def on_enum(self, state: State, enum: EnumDecl) -> None: + pass + + def on_class_start(self, state: ClassBlockState) -> None: + pass + + def on_class_field(self, state: State, f: Field) -> None: + pass + + def on_class_method(self, state: ClassBlockState, method: Method) -> None: + pass + + def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None: + pass + + def on_class_end(self, state: ClassBlockState) -> None: + pass diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 5a5dab572..38c943cc5 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -8,7 +8,7 @@ import os import pathlib from fbt.elfmanifest import assemble_manifest_data from fbt.appmanifest import FlipperApplication, FlipperManifestException -from fbt.sdk import SdkCache +from fbt.sdk.cache import SdkCache import itertools from ansi.color import fg diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index 0b6e22de5..f1f55bdb8 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -4,7 +4,7 @@ from SCons.Action import Action from SCons.Errors import UserError # from SCons.Scanner import C -from SCons.Script import Mkdir, Copy, Delete, Entry +from SCons.Script import Entry from SCons.Util import LogicalLines import os.path @@ -12,7 +12,8 @@ import posixpath import pathlib import json -from fbt.sdk import SdkCollector, SdkCache +from fbt.sdk.collector import SdkCollector +from fbt.sdk.cache import SdkCache def ProcessSdkDepends(env, filename): @@ -49,15 +50,19 @@ def prebuild_sdk_create_origin_file(target, source, env): class SdkMeta: - def __init__(self, env): + def __init__(self, env, tree_builder: "SdkTreeBuilder"): self.env = env + self.treebuilder = tree_builder def save_to(self, json_manifest_path: str): meta_contents = { - "sdk_symbols": self.env["SDK_DEFINITION"].name, + "sdk_symbols": self.treebuilder.build_sdk_file_path( + self.env["SDK_DEFINITION"].path + ), "cc_args": self._wrap_scons_vars("$CCFLAGS $_CCCOMCOM"), "cpp_args": self._wrap_scons_vars("$CXXFLAGS $CCFLAGS $_CCCOMCOM"), "linker_args": self._wrap_scons_vars("$LINKFLAGS"), + "linker_script": self.env.subst("${LINKER_SCRIPT_PATH}"), } with open(json_manifest_path, "wt") as f: json.dump(meta_contents, f, indent=4) @@ -68,6 +73,8 @@ class SdkMeta: class SdkTreeBuilder: + SDK_DIR_SUBST = "SDK_ROOT_DIR" + def __init__(self, env, target, source) -> None: self.env = env self.target = target @@ -88,6 +95,8 @@ class SdkTreeBuilder: self.header_depends = list( filter(lambda fname: fname.endswith(".h"), depends.split()), ) + self.header_depends.append(self.env.subst("${LINKER_SCRIPT_PATH}")) + self.header_depends.append(self.env.subst("${SDK_DEFINITION}")) self.header_dirs = sorted( set(map(os.path.normpath, map(os.path.dirname, self.header_depends))) ) @@ -102,17 +111,33 @@ class SdkTreeBuilder: ) sdk_dirs = ", ".join(f"'{dir}'" for dir in self.header_dirs) - for dir in full_fw_paths: - if dir in sdk_dirs: - filtered_paths.append( - posixpath.normpath(posixpath.join(self.target_sdk_dir_name, dir)) - ) + filtered_paths.extend( + map( + self.build_sdk_file_path, + filter(lambda path: path in sdk_dirs, full_fw_paths), + ) + ) sdk_env = self.env.Clone() - sdk_env.Replace(CPPPATH=filtered_paths) - meta = SdkMeta(sdk_env) + sdk_env.Replace( + CPPPATH=filtered_paths, + LINKER_SCRIPT=self.env.subst("${APP_LINKER_SCRIPT}"), + ORIG_LINKER_SCRIPT_PATH=self.env["LINKER_SCRIPT_PATH"], + LINKER_SCRIPT_PATH=self.build_sdk_file_path("${ORIG_LINKER_SCRIPT_PATH}"), + ) + + meta = SdkMeta(sdk_env, self) meta.save_to(self.target[0].path) + def build_sdk_file_path(self, orig_path: str) -> str: + return posixpath.normpath( + posixpath.join( + self.SDK_DIR_SUBST, + self.target_sdk_dir_name, + orig_path, + ) + ).replace("\\", "/") + def emitter(self, target, source, env): target_folder = target[0] target = [target_folder.File("sdk.opts")] @@ -128,8 +153,6 @@ class SdkTreeBuilder: for sdkdir in dirs_to_create: os.makedirs(sdkdir, exist_ok=True) - shutil.copy2(self.env["SDK_DEFINITION"].path, self.sdk_root_dir.path) - for header in self.header_depends: shutil.copy2(header, self.sdk_deploy_dir.File(header).path) diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index ce9f65bc6..4cbea7bd9 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -48,48 +48,52 @@ class Main(App): ) self.parser_copy.set_defaults(func=self.copy) - def get_project_filename(self, project, filetype): + def get_project_file_name(self, project: ProjectDir, filetype: str) -> str: # Temporary fix project_name = project.project - if project_name == "firmware": - if filetype == "zip": - project_name = "sdk" - elif filetype != "elf": - project_name = "full" + if project_name == "firmware" and filetype != "elf": + project_name = "full" - return f"{self.DIST_FILE_PREFIX}{self.target}-{project_name}-{self.args.suffix}.{filetype}" + return self.get_dist_file_name(project_name, filetype) - def get_dist_filepath(self, filename): + def get_dist_file_name(self, dist_artifact_type: str, filetype: str) -> str: + return f"{self.DIST_FILE_PREFIX}{self.target}-{dist_artifact_type}-{self.args.suffix}.{filetype}" + + def get_dist_file_path(self, filename: str) -> str: return join(self.output_dir_path, filename) - def copy_single_project(self, project): + def copy_single_project(self, project: ProjectDir) -> None: obj_directory = join("build", project.dir) for filetype in ("elf", "bin", "dfu", "json"): if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")): shutil.copyfile( src_file, - self.get_dist_filepath( - self.get_project_filename(project, filetype) + self.get_dist_file_path( + self.get_project_file_name(project, filetype) ), ) - if exists(sdk_folder := join(obj_directory, "sdk")): - with zipfile.ZipFile( - self.get_dist_filepath(self.get_project_filename(project, "zip")), - "w", - zipfile.ZIP_DEFLATED, - ) as zf: - for root, dirs, files in walk(sdk_folder): - for file in files: - zf.write( - join(root, file), - relpath( - join(root, file), - sdk_folder, - ), - ) + for foldertype in ("sdk", "lib"): + if exists(sdk_folder := join(obj_directory, foldertype)): + self.package_zip(foldertype, sdk_folder) - def copy(self): + def package_zip(self, foldertype, sdk_folder): + with zipfile.ZipFile( + self.get_dist_file_path(self.get_dist_file_name(foldertype, "zip")), + "w", + zipfile.ZIP_DEFLATED, + ) as zf: + for root, _, files in walk(sdk_folder): + for file in files: + zf.write( + join(root, file), + relpath( + join(root, file), + sdk_folder, + ), + ) + + def copy(self) -> int: self.projects = dict( map( lambda pd: (pd.project, pd), @@ -144,12 +148,12 @@ class Main(App): "-t", self.target, "--dfu", - self.get_dist_filepath( - self.get_project_filename(self.projects["firmware"], "dfu") + self.get_dist_file_path( + self.get_project_file_name(self.projects["firmware"], "dfu") ), "--stage", - self.get_dist_filepath( - self.get_project_filename(self.projects["updater"], "bin") + self.get_dist_file_path( + self.get_project_file_name(self.projects["updater"], "bin") ), ] if self.args.resources: diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index 370f1a14a..aaed89856 100644 --- a/scripts/toolchain/windows-toolchain-download.ps1 +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -23,12 +23,12 @@ if (!(Test-Path -LiteralPath "$repo_root\toolchain")) { New-Item "$repo_root\toolchain" -ItemType Directory } -Write-Host -NoNewline "Unziping Windows toolchain.." +Write-Host -NoNewline "Extracting Windows toolchain.." Add-Type -Assembly "System.IO.Compression.Filesystem" -[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip", "$repo_root\") +[System.IO.Compression.ZipFile]::ExtractToDirectory("$repo_root\$toolchain_zip", "$repo_root\") Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\x86_64-windows" Write-Host "done!" -Write-Host -NoNewline "Clearing temporary files.." +Write-Host -NoNewline "Cleaning up temporary files.." Remove-Item -LiteralPath "$repo_root\$toolchain_zip" -Force Write-Host "done!" diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index ee317be3b..90d228e58 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -21,7 +21,7 @@ appenv = ENV.Clone( ) appenv.Replace( - LINKER_SCRIPT="application_ext", + LINKER_SCRIPT=appenv.subst("$APP_LINKER_SCRIPT"), ) appenv.AppendUnique( diff --git a/site_scons/firmwareopts.scons b/site_scons/firmwareopts.scons index f04b55cdd..9f707b4d8 100644 --- a/site_scons/firmwareopts.scons +++ b/site_scons/firmwareopts.scons @@ -32,12 +32,27 @@ else: ], ) -ENV.Append( +ENV.AppendUnique( LINKFLAGS=[ - "-Tfirmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld", + "-specs=nano.specs", + "-specs=nosys.specs", + "-Wl,--gc-sections", + "-Wl,--undefined=uxTopUsedPriority", + "-Wl,--wrap,_malloc_r", + "-Wl,--wrap,_free_r", + "-Wl,--wrap,_calloc_r", + "-Wl,--wrap,_realloc_r", + "-n", + "-Xlinker", + "-Map=${TARGET}.map", + "-T${LINKER_SCRIPT_PATH}", ], ) +ENV.SetDefault( + LINKER_SCRIPT_PATH="firmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld", +) + if ENV["FIRMWARE_BUILD_CFG"] == "updater": ENV.Append( IMAGE_BASE_ADDRESS="0x20000000", @@ -47,4 +62,5 @@ else: ENV.Append( IMAGE_BASE_ADDRESS="0x8000000", LINKER_SCRIPT="stm32wb55xx_flash", + APP_LINKER_SCRIPT="application_ext", )