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); }