From 8ec5527ae4a486608e4003b51e21b41045daf7fd Mon Sep 17 00:00:00 2001 From: hedger Date: Sun, 16 Oct 2022 21:11:27 +0400 Subject: [PATCH 1/6] fbt: fix for cincludes in app's private library definition (#1882) --- scripts/fbt_tools/fbt_extapps.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 34fb942ab..9f81d4145 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -80,10 +80,7 @@ def BuildAppElf(env, app): *lib_def.cflags, ], CPPDEFINES=lib_def.cdefines, - CPPPATH=list( - os.path.join(app._appdir.path, cinclude) - for cinclude in lib_def.cincludes - ), + CPPPATH=list(map(app._appdir.Dir, lib_def.cincludes)), ) lib = private_lib_env.StaticLibrary( From e7aaf3dbb2f86ec6cb51b0be34095411274182ab Mon Sep 17 00:00:00 2001 From: Ivan Podogov Date: Mon, 17 Oct 2022 16:17:04 +0100 Subject: [PATCH 2/6] Enable all `view_` methods in SDK (#1884) --- firmware/targets/f7/api_symbols.csv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index c9e2371f4..33e289948 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,+,2.2,, +Version,+,2.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2511,17 +2511,17 @@ Function,+,view_port_alloc,ViewPort*, Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" Function,+,view_port_free,void,ViewPort* -Function,-,view_port_get_height,uint8_t,ViewPort* +Function,+,view_port_get_height,uint8_t,ViewPort* Function,+,view_port_get_orientation,ViewPortOrientation,const ViewPort* Function,+,view_port_get_width,uint8_t,ViewPort* Function,+,view_port_input_callback_set,void,"ViewPort*, ViewPortInputCallback, void*" Function,+,view_port_is_enabled,_Bool,ViewPort* -Function,-,view_port_set_height,void,"ViewPort*, uint8_t" +Function,+,view_port_set_height,void,"ViewPort*, uint8_t" Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" Function,+,view_port_set_width,void,"ViewPort*, uint8_t" Function,+,view_port_update,void,ViewPort* Function,+,view_set_context,void,"View*, void*" -Function,-,view_set_custom_callback,void,"View*, ViewCustomCallback" +Function,+,view_set_custom_callback,void,"View*, ViewCustomCallback" Function,+,view_set_draw_callback,void,"View*, ViewDrawCallback" Function,+,view_set_enter_callback,void,"View*, ViewCallback" Function,+,view_set_exit_callback,void,"View*, ViewCallback" From dfbe21e7203688d33a7c002a4a19fc4714469aa6 Mon Sep 17 00:00:00 2001 From: gornekich Date: Mon, 17 Oct 2022 21:10:41 +0400 Subject: [PATCH 3/6] NFC fixes part 3 (#1885) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc: fix read next key * nfc: verify new line ending in user dictionary file * nfc: fix cache save * nfc: add unit test for dict load * nfc: fix total key count in dictionary Co-authored-by: あく --- applications/debug/unit_tests/nfc/nfc_test.c | 68 ++++++++++++++++++++ lib/nfc/helpers/mf_classic_dict.c | 22 +++++-- lib/nfc/nfc_device.c | 2 +- 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index f149508b0..8009f6a17 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -16,6 +16,7 @@ #define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/") #define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc" #define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc" +#define NFC_TEST_DICT_PATH EXT_PATH("unit_tests/mf_classic_dict.nfc") static const char* nfc_test_file_type = "Flipper NFC test"; static const uint32_t nfc_test_file_version = 1; @@ -220,11 +221,78 @@ MU_TEST(mf_classic_dict_test) { furi_string_free(temp_str); } +MU_TEST(mf_classic_dict_load_test) { + Storage* storage = furi_record_open(RECORD_STORAGE); + mu_assert(storage != NULL, "storage != NULL assert failed\r\n"); + + // Delete unit test dict file if exists + if(storage_file_exists(storage, NFC_TEST_DICT_PATH)) { + mu_assert( + storage_simply_remove(storage, NFC_TEST_DICT_PATH), + "remove == true assert failed\r\n"); + } + + // Create unit test dict file + Stream* file_stream = file_stream_alloc(storage); + mu_assert(file_stream != NULL, "file_stream != NULL assert failed\r\n"); + mu_assert( + file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS), + "file_stream_open == true assert failed\r\n"); + + // Write unit test dict file + char key_str[] = "a0a1a2a3a4a5"; + mu_assert( + stream_write_cstring(file_stream, key_str) == strlen(key_str), + "write == true assert failed\r\n"); + // Close unit test dict file + mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n"); + + // Load unit test dict file + MfClassicDict* instance = NULL; + instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest); + mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n"); + uint32_t total_keys = mf_classic_dict_get_total_keys(instance); + mu_assert(total_keys == 1, "total_keys == 1 assert failed\r\n"); + + // Read key + uint64_t key_ref = 0xa0a1a2a3a4a5; + uint64_t key_dut = 0; + FuriString* temp_str = furi_string_alloc(); + mu_assert( + mf_classic_dict_get_next_key_str(instance, temp_str), + "get_next_key_str == true assert failed\r\n"); + mu_assert(furi_string_cmp_str(temp_str, key_str) == 0, "invalid key loaded\r\n"); + mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n"); + mu_assert( + mf_classic_dict_get_next_key(instance, &key_dut), + "get_next_key == true assert failed\r\n"); + mu_assert(key_dut == key_ref, "invalid key loaded\r\n"); + furi_string_free(temp_str); + mf_classic_dict_free(instance); + + // Check that MfClassicDict added new line to the end of the file + mu_assert( + file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING), + "file_stream_open == true assert failed\r\n"); + mu_assert(stream_seek(file_stream, -1, StreamOffsetFromEnd), "seek == true assert failed\r\n"); + uint8_t last_char = 0; + mu_assert(stream_read(file_stream, &last_char, 1) == 1, "read == true assert failed\r\n"); + mu_assert(last_char == '\n', "last_char == '\\n' assert failed\r\n"); + mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n"); + + // Delete unit test dict file + mu_assert( + storage_simply_remove(storage, NFC_TEST_DICT_PATH), "remove == true assert failed\r\n"); + stream_free(file_stream); + furi_record_close(RECORD_STORAGE); +} + MU_TEST_SUITE(nfc) { nfc_test_alloc(); MU_RUN_TEST(nfc_digital_signal_test); MU_RUN_TEST(mf_classic_dict_test); + MU_RUN_TEST(mf_classic_dict_load_test); nfc_test_free(); } diff --git a/lib/nfc/helpers/mf_classic_dict.c b/lib/nfc/helpers/mf_classic_dict.c index a842ed921..690bba61b 100644 --- a/lib/nfc/helpers/mf_classic_dict.c +++ b/lib/nfc/helpers/mf_classic_dict.c @@ -44,7 +44,10 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { do { if(dict_type == MfClassicDictTypeFlipper) { if(!buffered_file_stream_open( - dict->stream, MF_CLASSIC_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + dict->stream, + MF_CLASSIC_DICT_FLIPPER_PATH, + FSAM_READ_WRITE, + FSOM_OPEN_EXISTING)) { buffered_file_stream_close(dict->stream); break; } @@ -59,12 +62,24 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { dict->stream, MF_CLASSIC_DICT_UNIT_TEST_PATH, FSAM_READ_WRITE, - FSOM_CREATE_ALWAYS)) { + FSOM_OPEN_ALWAYS)) { buffered_file_stream_close(dict->stream); break; } } + // Check for new line ending + if(!stream_eof(dict->stream)) { + if(!stream_seek(dict->stream, -1, StreamOffsetFromEnd)) break; + uint8_t last_char = 0; + if(stream_read(dict->stream, &last_char, 1) != 1) break; + if(last_char != '\n') { + FURI_LOG_D(TAG, "Adding new line ending"); + if(stream_write_char(dict->stream, '\n') != 1) break; + } + if(!stream_rewind(dict->stream)) break; + } + // Read total amount of keys FuriString* next_line; next_line = furi_string_alloc(); @@ -73,14 +88,13 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { FURI_LOG_T(TAG, "No keys left in dict"); break; } - furi_string_trim(next_line); FURI_LOG_T( TAG, "Read line: %s, len: %d", furi_string_get_cstr(next_line), furi_string_size(next_line)); if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN - 1) continue; + if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; dict->total_keys++; } furi_string_free(next_line); diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 06d57a0c3..740cfae5e 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -921,7 +921,7 @@ static bool nfc_device_save_mifare_classic_keys(NfcDevice* dev) { file, furi_string_get_cstr(temp_str), sec_tr->key_a, 6); } if(!key_save_success) break; - if(FURI_BIT(data->key_a_mask, i)) { + if(FURI_BIT(data->key_b_mask, i)) { furi_string_printf(temp_str, "Key B sector %d", i); key_save_success = flipper_format_write_hex( file, furi_string_get_cstr(temp_str), sec_tr->key_b, 6); From 5e35e51c578d938e2b3fb3dee476d88c405b0105 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Mon, 17 Oct 2022 20:49:00 +0300 Subject: [PATCH 4/6] [FL-2907] Remove the back button from MFC keys list #1878 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index a2e6ae745..54cc18d32 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -34,8 +34,6 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); widget_add_button_element( nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); - widget_add_button_element( - nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_mf_classic_keys_widget_callback, nfc); widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36); if(user_dict_keys_total > 0) { widget_add_button_element( @@ -57,9 +55,6 @@ bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) if(event.event == GuiButtonTypeCenter) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); consumed = true; - } else if(event.event == GuiButtonTypeLeft) { - scene_manager_previous_scene(nfc->scene_manager); - consumed = true; } else if(event.event == GuiButtonTypeRight) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList); consumed = true; From f61a8fda536656ee4900e24ed1b5a029b0de533f Mon Sep 17 00:00:00 2001 From: Travis Montoya <99630881+sqlsquirreltm@users.noreply.github.com> Date: Mon, 17 Oct 2022 12:07:05 -0600 Subject: [PATCH 5/6] Feature/infrared add remote to cli (#1856) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial testing of remote using cli * More fixes for cli ir remote * Fixes. Turns off power now * Finished adding other tv remote commands * Changed if-else formatting * Cleaned up unused variables * Updating cli unviersal remote to accept tv, ac and more modular. Listing signals still does not work properly * Using mlib dictionary to get unique signals from files for ir universal list * Fixing progress bar * Added error checking for invalid signal to stop freezing cli * Added error checking for arg length * Api symbols was changed somehow.. changed back and updated the argument check to account for newline * Fixing string compares and argument length issue * Freeing InfraredBruteForce in cli brute force signals Co-authored-by: sqlsquirreltm Co-authored-by: あく --- applications/main/infrared/infrared_cli.c | 178 ++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 5ec57c757..54e3e2515 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,12 +7,24 @@ #include #include "infrared_signal.h" +#include "infrared_brute_force.h" + +#include "m-dict.h" +#include "m-string.h" #define INFRARED_CLI_BUF_SIZE 10 +DICT_DEF2(dict_signals, string_t, 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; @@ -20,6 +33,7 @@ static const struct { {.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, {.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, {.cmd = "decode", .process_function = infrared_cli_process_decode}, + {.cmd = "universal", .process_function = infrared_cli_process_universal}, }; static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { @@ -90,6 +104,8 @@ 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"); } static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { @@ -328,6 +344,168 @@ 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; + string_t 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; + } + + dict_signals_init(signals_dict); + string_init(key); + + 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"); + while(flipper_format_read_string(ff, "name", signal_name)) { + string_set_str(key, furi_string_get_cstr(signal_name)); + int* v = dict_signals_get(signals_dict, key); + if(v != NULL) { + (*v)++; + max = M_MAX(*v, max); + } else { + 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", string_get_cstr(pair->key)); + } + furi_string_free(signal_name); + } + + string_clear(key); + dict_signals_clear(signals_dict); + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); +} + +static void + infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal) { + InfraredBruteForce* brute_force = infrared_brute_force_alloc(); + const char* remote_file = NULL; + uint32_t i = 0; + bool success = false; + + 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, 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_reset(brute_force); + return; + } + + printf("Sending %ld codes to the tv.\r\n", record_count); + printf("Press Ctrl-C to stop.\r\n"); + while(running) { + running = infrared_brute_force_send_next(brute_force); + + if(cli_cmd_interrupt_received(cli)) break; + + printf("\r%d%% complete.", (int)((float)records_sent++ / (float)record_count * 100)); + fflush(stdout); + } + + infrared_brute_force_stop(brute_force); + } else { + printf("Invalid signal.\r\n"); + } + + infrared_brute_force_reset(brute_force); + infrared_brute_force_free(brute_force); +} + static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) { UNUSED(context); if(furi_hal_infrared_is_busy()) { From 04d67ca8ae68914d281da588aafe99fad5bcc96f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 17 Oct 2022 21:22:34 +0300 Subject: [PATCH 6/6] update totp https://github.com/akopachov/flipper-zero_authenticator --- .../authenticate/totp_scene_authenticate.c | 51 +------ .../totp_scene_generate_token.c | 13 +- .../plugins/totp/services/config/config.c | 15 +- .../plugins/totp/services/config/constants.h | 1 + .../plugins/totp/services/crypto/crypto.c | 134 ++++++++++++++++++ .../plugins/totp/services/crypto/crypto.h | 16 +++ applications/plugins/totp/totp_app.c | 58 +++++++- applications/plugins/totp/types/common.h | 1 - .../plugins/totp/types/plugin_state.h | 1 + applications/plugins/totp/types/token_info.c | 21 +-- 10 files changed, 234 insertions(+), 77 deletions(-) create mode 100644 applications/plugins/totp/services/crypto/crypto.c create mode 100644 applications/plugins/totp/services/crypto/crypto.h diff --git a/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.c b/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.c index 180d7b9a1..73cbb4aaa 100644 --- a/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.c @@ -6,10 +6,9 @@ #include "../../services/config/config.h" #include "../scene_director.h" #include "../totp_scenes_enum.h" +#include "../../services/crypto/crypto.h" #define MAX_CODE_LENGTH TOTP_IV_SIZE -#define CRYPTO_VERIFY_KEY "FFF_Crypto_pass" -#define CRYPTO_VERIFY_KEY_LENGTH 16 typedef struct { uint8_t code_input[MAX_CODE_LENGTH]; @@ -111,52 +110,10 @@ bool totp_scene_authenticate_handle_event(PluginEvent* const event, PluginState* } break; case InputKeyOk: - 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); - } + totp_crypto_seed_iv( + plugin_state, &scene_state->code_input[0], scene_state->code_length); - memcpy(&plugin_state->iv[0], &plugin_state->base_iv[0], TOTP_IV_SIZE); - for(uint8_t i = 0; i < scene_state->code_length; i++) { - plugin_state->iv[i] = plugin_state->iv[i] ^ - (uint8_t)(scene_state->code_input[i] * (i + 1)); - } - - if(plugin_state->crypto_verify_data == NULL) { - FURI_LOG_D(LOGGING_TAG, "Generating crypto verify data"); - plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH); - plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; - Storage* storage = totp_open_storage(); - FlipperFormat* config_file = totp_open_config_file(storage); - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, &plugin_state->iv[0]); - furi_hal_crypto_encrypt( - (uint8_t*)CRYPTO_VERIFY_KEY, - plugin_state->crypto_verify_data, - CRYPTO_VERIFY_KEY_LENGTH); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); - flipper_format_insert_or_update_hex( - config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE); - flipper_format_insert_or_update_hex( - config_file, - TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, - CRYPTO_VERIFY_KEY_LENGTH); - totp_close_config_file(config_file); - totp_close_storage(); - } - - uint8_t decrypted_key[CRYPTO_VERIFY_KEY_LENGTH]; - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, &plugin_state->iv[0]); - furi_hal_crypto_decrypt( - plugin_state->crypto_verify_data, &decrypted_key[0], CRYPTO_VERIFY_KEY_LENGTH); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); - - bool key_valid = true; - for(uint8_t i = 0; i < CRYPTO_VERIFY_KEY_LENGTH && key_valid; i++) { - if(decrypted_key[i] != CRYPTO_VERIFY_KEY[i]) key_valid = false; - } - - if(key_valid) { + if(totp_crypto_verify_key(plugin_state)) { FURI_LOG_D(LOGGING_TAG, "PIN is valid"); totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); } else { 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 a2ee63d45..a4e289ccd 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 @@ -8,6 +8,7 @@ #include "../../services/ui/constants.h" #include "../../services/totp/totp.h" #include "../../services/config/config.h" +#include "../../services/crypto/crypto.h" #include "../scene_director.h" #include "../token_menu/totp_scene_token_menu.h" @@ -156,24 +157,22 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ plugin_state->tokens_list, scene_state->current_token_index) ->data); - uint8_t* key = malloc(tokenInfo->token_length); - - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, &plugin_state->iv[0]); - furi_hal_crypto_decrypt(tokenInfo->token, key, tokenInfo->token_length); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + uint8_t key_length; + uint8_t* key = totp_crypto_decrypt( + tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length); i_token_to_str( totp_at( get_totp_algo_impl(tokenInfo->algo), token_info_get_digits_count(tokenInfo), key, - tokenInfo->token_length, + key_length, curr_ts, plugin_state->timezone_offset, TOKEN_LIFETIME), scene_state->last_code, tokenInfo->digits); - memset(key, 0, tokenInfo->token_length); + memset(key, 0, key_length); free(key); if(is_new_token_time) { diff --git a/applications/plugins/totp/services/config/config.c b/applications/plugins/totp/services/config/config.c index 4b9583250..b7eb5fdd1 100644 --- a/applications/plugins/totp/services/config/config.c +++ b/applications/plugins/totp/services/config/config.c @@ -199,6 +199,7 @@ void totp_full_save_config_file(PluginState* const plugin_state) { plugin_state->crypto_verify_data_length); flipper_format_write_float( fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1); + 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; @@ -272,7 +273,8 @@ void totp_config_file_load_base(PluginState* const plugin_state) { flipper_format_rewind(fff_data_file); uint32_t crypto_size; - if(flipper_format_get_value_count(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, &crypto_size)) { + if(flipper_format_get_value_count(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, &crypto_size) && + crypto_size > 0) { plugin_state->crypto_verify_data = malloc(sizeof(uint8_t) * crypto_size); plugin_state->crypto_verify_data_length = crypto_size; if(!flipper_format_read_hex( @@ -283,7 +285,11 @@ void totp_config_file_load_base(PluginState* const plugin_state) { FURI_LOG_D(LOGGING_TAG, "Missing crypto verify token"); free(plugin_state->crypto_verify_data); plugin_state->crypto_verify_data = NULL; + plugin_state->crypto_verify_data_length = 0; } + } else { + plugin_state->crypto_verify_data = NULL; + plugin_state->crypto_verify_data_length = 0; } flipper_format_rewind(fff_data_file); @@ -294,6 +300,13 @@ void totp_config_file_load_base(PluginState* const plugin_state) { FURI_LOG_D(LOGGING_TAG, "Missing timezone offset information, defaulting to 0"); } + flipper_format_rewind(fff_data_file); + + if(!flipper_format_read_bool( + fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { + plugin_state->pin_set = true; + } + furi_string_free(temp_str); totp_close_config_file(fff_data_file); totp_close_storage(); diff --git a/applications/plugins/totp/services/config/constants.h b/applications/plugins/totp/services/config/constants.h index 931db4e02..b094f653f 100644 --- a/applications/plugins/totp/services/config/constants.h +++ b/applications/plugins/totp/services/config/constants.h @@ -10,6 +10,7 @@ #define TOTP_CONFIG_KEY_TOKEN_DIGITS "TokenDigits" #define TOTP_CONFIG_KEY_CRYPTO_VERIFY "Crypto" #define TOTP_CONFIG_KEY_BASE_IV "BaseIV" +#define TOTP_CONFIG_KEY_PINSET "PinIsSet" #define TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME "sha1" #define TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME "sha256" diff --git a/applications/plugins/totp/services/crypto/crypto.c b/applications/plugins/totp/services/crypto/crypto.c new file mode 100644 index 000000000..ade2f9f49 --- /dev/null +++ b/applications/plugins/totp/services/crypto/crypto.c @@ -0,0 +1,134 @@ +#include "crypto.h" +#include +#include +#include "../config/config.h" +#include "../../types/common.h" + +#define CRYPTO_KEY_SLOT 2 +#define CRYPTO_VERIFY_KEY "FFF_Crypto_pass" +#define CRYPTO_VERIFY_KEY_LENGTH 16 +#define CRYPTO_ALIGNMENT_FACTOR 16 + +uint8_t* totp_crypto_encrypt( + const uint8_t* plain_data, + const uint8_t plain_data_length, + const uint8_t* iv, + uint8_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; + 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); + + encrypted_data = malloc(plain_data_aligned_length); + *encrypted_data_length = plain_data_aligned_length; + + furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); + 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); + free(plain_data_aligned); + } else { + encrypted_data = malloc(plain_data_length); + *encrypted_data_length = plain_data_length; + + furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); + furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length); + furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + } + + return encrypted_data; +} + +uint8_t* totp_crypto_decrypt( + const uint8_t* encrypted_data, + const uint8_t encrypted_data_length, + const uint8_t* iv, + uint8_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); + furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length); + furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + return decrypted_data; +} + +void totp_crypto_seed_iv(PluginState* plugin_state, 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); + } + + memcpy(&plugin_state->iv[0], &plugin_state->base_iv[0], TOTP_IV_SIZE); + if(pin != NULL && pin_length > 0) { + uint8_t max_i; + if(pin_length > TOTP_IV_SIZE) { + max_i = TOTP_IV_SIZE; + } else { + max_i = pin_length; + } + + for(uint8_t i = 0; i < max_i; i++) { + plugin_state->iv[i] = plugin_state->iv[i] ^ (uint8_t)(pin[i] * (i + 1)); + } + } else { + uint8_t max_i; + size_t uid_size = furi_hal_version_uid_size(); + if(uid_size > TOTP_IV_SIZE) { + max_i = TOTP_IV_SIZE; + } else { + max_i = uid_size; + } + + const uint8_t* uid = furi_hal_version_uid(); + for(uint8_t i = 0; i < max_i; i++) { + plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; + } + } + + if(plugin_state->crypto_verify_data == NULL) { + FURI_LOG_D(LOGGING_TAG, "Generating crypto verify data"); + plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH); + plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; + Storage* storage = totp_open_storage(); + FlipperFormat* config_file = totp_open_config_file(storage); + + plugin_state->crypto_verify_data = totp_crypto_encrypt( + (uint8_t*)CRYPTO_VERIFY_KEY, + CRYPTO_VERIFY_KEY_LENGTH, + &plugin_state->iv[0], + &plugin_state->crypto_verify_data_length); + + flipper_format_insert_or_update_hex( + config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE); + flipper_format_insert_or_update_hex( + config_file, + TOTP_CONFIG_KEY_CRYPTO_VERIFY, + plugin_state->crypto_verify_data, + CRYPTO_VERIFY_KEY_LENGTH); + plugin_state->pin_set = pin != NULL && pin_length > 0; + flipper_format_insert_or_update_bool( + config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1); + totp_close_config_file(config_file); + totp_close_storage(); + } +} + +bool totp_crypto_verify_key(const PluginState* plugin_state) { + uint8_t decrypted_key_length; + uint8_t* decrypted_key = totp_crypto_decrypt( + plugin_state->crypto_verify_data, + plugin_state->crypto_verify_data_length, + &plugin_state->iv[0], + &decrypted_key_length); + + bool key_valid = true; + for(uint8_t i = 0; i < CRYPTO_VERIFY_KEY_LENGTH && key_valid; i++) { + if(decrypted_key[i] != CRYPTO_VERIFY_KEY[i]) key_valid = false; + } + + return key_valid; +} \ No newline at end of file diff --git a/applications/plugins/totp/services/crypto/crypto.h b/applications/plugins/totp/services/crypto/crypto.h new file mode 100644 index 000000000..9fc319659 --- /dev/null +++ b/applications/plugins/totp/services/crypto/crypto.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../../types/plugin_state.h" + +uint8_t* totp_crypto_encrypt( + const uint8_t* plain_data, + const uint8_t plain_data_length, + const uint8_t* iv, + uint8_t* encrypted_data_length); +uint8_t* totp_crypto_decrypt( + const uint8_t* encrypted_data, + const uint8_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); +bool totp_crypto_verify_key(const PluginState* plugin_state); \ No newline at end of file diff --git a/applications/plugins/totp/totp_app.c b/applications/plugins/totp/totp_app.c index 7438ac810..e55215488 100644 --- a/applications/plugins/totp/totp_app.c +++ b/applications/plugins/totp/totp_app.c @@ -16,6 +16,8 @@ #include "types/event_type.h" #include "types/common.h" #include "scenes/scene_director.h" +#include "services/ui/constants.h" +#include "services/crypto/crypto.h" #define IDLE_TIMEOUT 60000 @@ -35,14 +37,58 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu furi_message_queue_put(event_queue, &event, FuriWaitForever); } -static void totp_state_init(PluginState* const plugin_state) { +static bool totp_state_init(PluginState* const plugin_state) { plugin_state->gui = furi_record_open(RECORD_GUI); plugin_state->notification = furi_record_open(RECORD_NOTIFICATION); plugin_state->dialogs = furi_record_open(RECORD_DIALOGS); totp_config_file_load_base(plugin_state); totp_scene_director_init_scenes(plugin_state); - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + + if(plugin_state->crypto_verify_data == NULL) { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "No", NULL, "Yes"); + dialog_message_set_text( + message, + "Would you like to setup PIN?", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + DialogMessageButton dialog_result = dialog_message_show(plugin_state->dialogs, message); + dialog_message_free(message); + if(dialog_result == DialogMessageButtonRight) { + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + } else { + totp_crypto_seed_iv(plugin_state, NULL, 0); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + } else if(plugin_state->pin_set) { + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + } else { + totp_crypto_seed_iv(plugin_state, NULL, 0); + if(totp_crypto_verify_key(plugin_state)) { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } else { + FURI_LOG_E( + LOGGING_TAG, + "Digital signature verification failed. Looks like conf file was created on another flipper and can't be used on any other"); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "Exit", NULL, NULL); + dialog_message_set_text( + message, + "Digital signature verification failed", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + dialog_message_show(plugin_state->dialogs, message); + dialog_message_free(message); + return false; + } + } + + return true; } static void dispose_plugin_state(PluginState* plugin_state) { @@ -74,7 +120,11 @@ int32_t totp_app() { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); PluginState* plugin_state = malloc(sizeof(PluginState)); - totp_state_init(plugin_state); + if(!totp_state_init(plugin_state)) { + FURI_LOG_E(LOGGING_TAG, "App state initialization failed\r\n"); + dispose_plugin_state(plugin_state); + return 254; + } ValueMutex state_mutex; if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { @@ -107,7 +157,7 @@ int32_t totp_app() { processing = totp_scene_director_handle_event(&event, plugin_state); } else if( - plugin_state->current_scene != TotpSceneAuthentication && + plugin_state->pin_set && plugin_state->current_scene != TotpSceneAuthentication && furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) { totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); } diff --git a/applications/plugins/totp/types/common.h b/applications/plugins/totp/types/common.h index df8f6cc2d..2c6d6b293 100644 --- a/applications/plugins/totp/types/common.h +++ b/applications/plugins/totp/types/common.h @@ -1,4 +1,3 @@ #pragma once #define LOGGING_TAG "TOTP APP" -#define CRYPTO_KEY_SLOT 2 diff --git a/applications/plugins/totp/types/plugin_state.h b/applications/plugins/totp/types/plugin_state.h index 3f2b30ed6..98afe53cf 100644 --- a/applications/plugins/totp/types/plugin_state.h +++ b/applications/plugins/totp/types/plugin_state.h @@ -23,6 +23,7 @@ typedef struct { uint8_t* crypto_verify_data; uint8_t crypto_verify_data_length; + bool pin_set; uint8_t iv[TOTP_IV_SIZE]; uint8_t base_iv[TOTP_IV_SIZE]; } PluginState; diff --git a/applications/plugins/totp/types/token_info.c b/applications/plugins/totp/types/token_info.c index 6596cd510..8e1077ac6 100644 --- a/applications/plugins/totp/types/token_info.c +++ b/applications/plugins/totp/types/token_info.c @@ -4,6 +4,7 @@ #include "stdlib.h" #include "common.h" #include "../services/base32/base32.h" +#include "../services/crypto/crypto.h" TokenInfo* token_info_alloc() { TokenInfo* tokenInfo = malloc(sizeof(TokenInfo)); @@ -27,25 +28,11 @@ void token_info_set_secret( uint8_t* plain_secret = malloc(token_secret_length); int plain_secret_length = base32_decode((uint8_t*)base32_token_secret, plain_secret, token_secret_length); - token_info->token_length = plain_secret_length; - size_t remain = token_info->token_length % 16; - if(remain) { - token_info->token_length = token_info->token_length - remain + 16; - uint8_t* plain_secret_aligned = malloc(token_info->token_length); - memcpy(plain_secret_aligned, plain_secret, plain_secret_length); - memset(plain_secret, 0, plain_secret_length); - free(plain_secret); - plain_secret = plain_secret_aligned; - } + token_info->token = + totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length); - token_info->token = malloc(token_info->token_length); - - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); - furi_hal_crypto_encrypt(plain_secret, token_info->token, token_info->token_length); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); - - memset(plain_secret, 0, token_info->token_length); + memset(plain_secret, 0, token_secret_length); free(plain_secret); }