MX
2022-10-21 22:56:58 +03:00
parent a44739cde9
commit 1019025b13
13 changed files with 175 additions and 169 deletions
@@ -34,7 +34,7 @@ typedef struct {
InputTextSceneContext* token_secret_input_context;
InputTextSceneState* input_state;
uint32_t input_started_at;
int current_token_index;
int16_t current_token_index;
int32_t screen_y_offset;
TokenHashAlgo algo;
TokenDigitsCount digits_count;
@@ -227,31 +227,47 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
break;
case ConfirmButton: {
TokenInfo* tokenInfo = token_info_alloc();
tokenInfo->name = malloc(scene_state->token_name_length + 1);
strcpy(tokenInfo->name, scene_state->token_name);
token_info_set_secret(
bool token_secret_set = token_info_set_secret(
tokenInfo,
scene_state->token_secret,
scene_state->token_secret_length,
&plugin_state->iv[0]);
tokenInfo->algo = scene_state->algo;
tokenInfo->digits = scene_state->digits_count;
if(token_secret_set) {
tokenInfo->name = malloc(scene_state->token_name_length + 1);
strcpy(tokenInfo->name, scene_state->token_name);
tokenInfo->algo = scene_state->algo;
tokenInfo->digits = scene_state->digits_count;
if(plugin_state->tokens_list == NULL) {
plugin_state->tokens_list = list_init_head(tokenInfo);
if(plugin_state->tokens_list == NULL) {
plugin_state->tokens_list = list_init_head(tokenInfo);
} else {
list_add(plugin_state->tokens_list, tokenInfo);
}
plugin_state->tokens_count++;
totp_config_file_save_new_token(tokenInfo);
GenerateTokenSceneContext generate_scene_context = {
.current_token_index = plugin_state->tokens_count - 1};
totp_scene_director_activate_scene(
plugin_state, TotpSceneGenerateToken, &generate_scene_context);
} else {
list_add(plugin_state->tokens_list, tokenInfo);
token_info_free(tokenInfo);
DialogMessage* message = dialog_message_alloc();
dialog_message_set_buttons(message, "Back", NULL, NULL);
dialog_message_set_text(
message,
"Token secret is invalid",
SCREEN_WIDTH_CENTER,
SCREEN_HEIGHT_CENTER,
AlignCenter,
AlignCenter);
dialog_message_show(plugin_state->dialogs, message);
dialog_message_free(message);
scene_state->selected_control = TokenSecretTextBox;
update_screen_y_offset(scene_state);
}
plugin_state->tokens_count++;
totp_config_file_save_new_token(tokenInfo);
GenerateTokenSceneContext generate_scene_context = {
.current_token_index = plugin_state->tokens_count - 1};
totp_scene_director_activate_scene(
plugin_state, TotpSceneGenerateToken, &generate_scene_context);
break;
}
}
@@ -88,9 +88,7 @@ void update_totp_params(PluginState* const plugin_state) {
if(scene_state->current_token_index < plugin_state->tokens_count) {
TokenInfo* tokenInfo =
(TokenInfo*)(list_element_at(
plugin_state->tokens_list, scene_state->current_token_index)
->data);
list_element_at(plugin_state->tokens_list, scene_state->current_token_index)->data;
scene_state->need_token_update = true;
scene_state->last_code_name = tokenInfo->name;
@@ -105,7 +103,31 @@ void totp_scene_generate_token_activate(
PluginState* plugin_state,
const GenerateTokenSceneContext* context) {
if(!plugin_state->token_list_loaded) {
totp_config_file_load_tokens(plugin_state);
TokenLoadingResult token_load_result = totp_config_file_load_tokens(plugin_state);
if(token_load_result != TokenLoadingResultSuccess) {
DialogMessage* message = dialog_message_alloc();
dialog_message_set_buttons(message, NULL, "Okay", NULL);
if(token_load_result == TokenLoadingResultWarning) {
dialog_message_set_text(
message,
"Unable to load some tokens\nPlease review conf file",
SCREEN_WIDTH_CENTER,
SCREEN_HEIGHT_CENTER,
AlignCenter,
AlignCenter);
} else if(token_load_result == TokenLoadingResultError) {
dialog_message_set_text(
message,
"Unable to load tokens\nPlease review conf file",
SCREEN_WIDTH_CENTER,
SCREEN_HEIGHT_CENTER,
AlignCenter,
AlignCenter);
}
dialog_message_show(plugin_state->dialogs, message);
dialog_message_free(message);
}
}
SceneState* scene_state = malloc(sizeof(SceneState));
if(context == NULL) {
@@ -157,23 +179,27 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
plugin_state->tokens_list, scene_state->current_token_index)
->data);
uint8_t key_length;
uint8_t* key = totp_crypto_decrypt(
tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length);
if(tokenInfo->token != NULL && tokenInfo->token_length > 0) {
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,
key_length,
curr_ts,
plugin_state->timezone_offset,
TOKEN_LIFETIME),
scene_state->last_code,
tokenInfo->digits);
memset(key, 0, key_length);
free(key);
i_token_to_str(
totp_at(
get_totp_algo_impl(tokenInfo->algo),
token_info_get_digits_count(tokenInfo),
key,
key_length,
curr_ts,
plugin_state->timezone_offset,
TOKEN_LIFETIME),
scene_state->last_code,
tokenInfo->digits);
memset(key, 0, key_length);
free(key);
} else {
i_token_to_str(0, scene_state->last_code, tokenInfo->digits);
}
if(is_new_token_time) {
notification_message(plugin_state->notification, &sequence_short_vibro_and_sound);
@@ -60,35 +60,3 @@ int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) {
}
return count;
}
int base32_encode(const uint8_t* data, int length, uint8_t* result, int bufSize) {
if(length < 0 || length > (1 << 28)) {
return -1;
}
int count = 0;
if(length > 0) {
int buffer = data[0];
int next = 1;
int bitsLeft = 8;
while(count < bufSize && (bitsLeft > 0 || next < length)) {
if(bitsLeft < 5) {
if(next < length) {
buffer <<= 8;
buffer |= data[next++] & 0xFF;
bitsLeft += 8;
} else {
int pad = 5 - bitsLeft;
buffer <<= pad;
bitsLeft += pad;
}
}
int index = 0x1F & (buffer >> (bitsLeft - 5));
bitsLeft -= 5;
result[count++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[index];
}
}
if(count < bufSize) {
result[count] = '\000';
}
return count;
}
@@ -31,5 +31,3 @@
int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize)
__attribute__((visibility("hidden")));
int base32_encode(const uint8_t* data, int length, uint8_t* result, int bufSize)
__attribute__((visibility("hidden")));
@@ -155,8 +155,15 @@ FlipperFormat* totp_open_config_file(Storage* storage) {
void totp_config_file_save_new_token_i(FlipperFormat* file, 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;
if(!token_is_valid) {
flipper_format_write_comment_cstr(file, "!!! WARNING BEGIN: INVALID TOKEN !!!");
}
flipper_format_write_hex(
file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length);
if(!token_is_valid) {
flipper_format_write_comment_cstr(file, "!!! WARNING END !!!");
}
flipper_format_write_string_cstr(
file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info));
uint32_t digits_count_as_uint32 = token_info_get_digits_as_int(token_info);
@@ -312,7 +319,7 @@ void totp_config_file_load_base(PluginState* const plugin_state) {
totp_close_storage();
}
void totp_config_file_load_tokens(PluginState* const plugin_state) {
TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) {
Storage* storage = totp_open_storage();
FlipperFormat* fff_data_file = totp_open_config_file(storage);
@@ -322,9 +329,10 @@ void totp_config_file_load_tokens(PluginState* const plugin_state) {
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header");
furi_string_free(temp_str);
return;
return TokenLoadingResultError;
}
TokenLoadingResult result = TokenLoadingResultSuccess;
uint8_t index = 0;
bool has_any_plain_secret = false;
@@ -342,48 +350,61 @@ void totp_config_file_load_tokens(PluginState* const plugin_state) {
uint32_t secret_bytes_count;
if(!flipper_format_get_value_count(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) {
token_info_free(tokenInfo);
continue;
secret_bytes_count = 0;
}
if(secret_bytes_count == 1) { // Plain secret key
if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) {
token_info_free(tokenInfo);
continue;
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])) {
FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name);
} else {
tokenInfo->token = NULL;
tokenInfo->token_length = 0;
FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has invalid secret", tokenInfo->name);
result = TokenLoadingResultWarning;
}
} else {
tokenInfo->token = NULL;
tokenInfo->token_length = 0;
result = TokenLoadingResultWarning;
}
temp_cstr = furi_string_get_cstr(temp_str);
token_info_set_secret(tokenInfo, temp_cstr, strlen(temp_cstr), &plugin_state->iv[0]);
has_any_plain_secret = true;
FURI_LOG_W(LOGGING_TAG, "Found token with plain secret");
} else { // encrypted
tokenInfo->token_length = secret_bytes_count;
tokenInfo->token = malloc(tokenInfo->token_length);
if(!flipper_format_read_hex(
fff_data_file,
TOTP_CONFIG_KEY_TOKEN_SECRET,
tokenInfo->token,
tokenInfo->token_length)) {
token_info_free(tokenInfo);
continue;
if(secret_bytes_count > 0) {
tokenInfo->token = malloc(tokenInfo->token_length);
if(!flipper_format_read_hex(
fff_data_file,
TOTP_CONFIG_KEY_TOKEN_SECRET,
tokenInfo->token,
tokenInfo->token_length)) {
free(tokenInfo->token);
tokenInfo->token = NULL;
tokenInfo->token_length = 0;
result = TokenLoadingResultWarning;
}
} else {
tokenInfo->token = NULL;
result = TokenLoadingResultWarning;
}
}
if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str)) {
token_info_free(tokenInfo);
continue;
if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str)) {
token_info_set_algo_from_str(tokenInfo, temp_str);
} else {
tokenInfo->algo = SHA1;
}
token_info_set_algo_from_str(tokenInfo, temp_str);
if(!flipper_format_read_uint32(
if(flipper_format_read_uint32(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1)) {
token_info_free(tokenInfo);
continue;
token_info_set_digits_from_int(tokenInfo, temp_data32);
} else {
tokenInfo->digits = TOTP_6_DIGITS;
}
token_info_set_digits_from_int(tokenInfo, temp_data32);
FURI_LOG_D(LOGGING_TAG, "Found token \"%s\"", tokenInfo->name);
if(plugin_state->tokens_list == NULL) {
@@ -407,6 +428,8 @@ void totp_config_file_load_tokens(PluginState* const plugin_state) {
if(has_any_plain_secret) {
totp_full_save_config_file(plugin_state);
}
return result;
}
void totp_close_config_file(FlipperFormat* file) {
@@ -6,12 +6,18 @@
#include "../../types/token_info.h"
#include "constants.h"
typedef enum {
TokenLoadingResultSuccess,
TokenLoadingResultWarning,
TokenLoadingResultError
} TokenLoadingResult;
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_config_file_load_base(PluginState* const plugin_state);
void totp_config_file_load_tokens(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_update_timezone_offset(float new_timezone_offset);
@@ -1,4 +1,4 @@
/* hmac_sha1.c -- hashed message authentication codes
/* hmac-sha1.c -- hashed message authentication codes
Copyright (C) 2018-2022 Free Software Foundation, Inc.
This file is free software: you can redistribute it and/or modify
@@ -1,4 +1,4 @@
/* hmac_sha256.c -- hashed message authentication codes
/* hmac-sha256.c -- hashed message authentication codes
Copyright (C) 2018-2022 Free Software Foundation, Inc.
This file is free software: you can redistribute it and/or modify
@@ -1,4 +1,4 @@
/* hmac_sha512.c -- hashed message authentication codes
/* hmac-sha512.c -- hashed message authentication codes
Copyright (C) 2018-2022 Free Software Foundation, Inc.
This file is free software: you can redistribute it and/or modify
+3 -24
View File
@@ -8,10 +8,9 @@
#include "../hmac/hmac_sha1.h"
#include "../hmac/hmac_sha256.h"
#include "../hmac/hmac_sha512.h"
#include "../hmac/byteswap.h"
#include "../timezone_utils/timezone_utils.h"
#define UINT64_GET_BYTE(integer, index) ((integer >> (8 * index)) & 0xFF)
/*
Generates the timeblock for a time in seconds.
@@ -29,22 +28,6 @@ uint64_t totp_timecode(uint8_t interval, uint64_t for_time) {
return for_time / interval;
}
/*
Converts an integer into an 8 byte array.
out_bytes is the null-terminated output string already allocated
*/
void otp_num_to_bytes(uint64_t integer, uint8_t* out_bytes) {
out_bytes[7] = UINT64_GET_BYTE(integer, 0);
out_bytes[6] = UINT64_GET_BYTE(integer, 1);
out_bytes[5] = UINT64_GET_BYTE(integer, 2);
out_bytes[4] = UINT64_GET_BYTE(integer, 3);
out_bytes[3] = UINT64_GET_BYTE(integer, 4);
out_bytes[2] = UINT64_GET_BYTE(integer, 5);
out_bytes[1] = UINT64_GET_BYTE(integer, 6);
out_bytes[0] = UINT64_GET_BYTE(integer, 7);
}
/*
Generates an OTP (One Time Password).
@@ -61,17 +44,14 @@ uint32_t otp_generate(
const uint8_t* plain_secret,
uint8_t plain_secret_length,
uint64_t input) {
uint8_t* bytes = malloc(8);
memset(bytes, 0, 8);
uint8_t* hmac = malloc(64);
memset(hmac, 0, 64);
otp_num_to_bytes(input, bytes);
uint64_t input_swapped = swap_uint64(input);
int hmac_len = (*(algo))(plain_secret, plain_secret_length, bytes, 8, hmac);
int hmac_len = (*(algo))(plain_secret, plain_secret_length, (uint8_t*)&input_swapped, 8, hmac);
if(hmac_len == 0) {
free(hmac);
free(bytes);
return OTP_ERROR;
}
@@ -82,7 +62,6 @@ uint32_t otp_generate(
i_code %= (uint64_t)pow(10, digits);
free(hmac);
free(bytes);
return i_code;
}
+20 -36
View File
@@ -23,7 +23,7 @@
static void render_callback(Canvas* const canvas, void* ctx) {
PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
if(plugin_state != NULL && !plugin_state->changing_scene) {
if (plugin_state != NULL && !plugin_state->changing_scene) {
totp_scene_director_render(canvas, plugin_state);
}
@@ -45,43 +45,29 @@ static bool totp_state_init(PluginState* const plugin_state) {
totp_scene_director_init_scenes(plugin_state);
if(plugin_state->crypto_verify_data == 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);
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) {
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) {
} 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)) {
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");
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_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;
@@ -91,7 +77,7 @@ static bool totp_state_init(PluginState* const plugin_state) {
return true;
}
static void dispose_plugin_state(PluginState* plugin_state) {
static void plugin_state_free(PluginState* plugin_state) {
totp_scene_director_deactivate_active_scene(plugin_state);
totp_scene_director_dispose(plugin_state);
@@ -102,15 +88,15 @@ static void dispose_plugin_state(PluginState* plugin_state) {
ListNode* node = plugin_state->tokens_list;
ListNode* tmp;
while(node != NULL) {
while (node != NULL) {
tmp = node->next;
TokenInfo* tokenInfo = (TokenInfo*)node->data;
TokenInfo* tokenInfo = node->data;
token_info_free(tokenInfo);
free(node);
node = tmp;
}
if(plugin_state->crypto_verify_data != NULL) {
if (plugin_state->crypto_verify_data != NULL) {
free(plugin_state->crypto_verify_data);
}
free(plugin_state);
@@ -120,16 +106,16 @@ int32_t totp_app() {
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
PluginState* plugin_state = malloc(sizeof(PluginState));
if(!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);
plugin_state_free(plugin_state);
return 254;
}
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
FURI_LOG_E(LOGGING_TAG, "Cannot create mutex\r\n");
dispose_plugin_state(plugin_state);
plugin_state_free(plugin_state);
return 255;
}
@@ -145,20 +131,18 @@ int32_t totp_app() {
bool processing = true;
uint32_t last_user_interaction_time = furi_get_tick();
while(processing) {
if(plugin_state->changing_scene) continue;
if (plugin_state->changing_scene) continue;
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
PluginState* plugin_state = acquire_mutex_block(&state_mutex);
if(event_status == FuriStatusOk) {
if(event.type == EventTypeKey) {
if (event.type == EventTypeKey) {
last_user_interaction_time = furi_get_tick();
}
processing = totp_scene_director_handle_event(&event, plugin_state);
} else if(
plugin_state->pin_set && plugin_state->current_scene != TotpSceneAuthentication &&
furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) {
} else if (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);
}
@@ -171,6 +155,6 @@ int32_t totp_app() {
view_port_free(view_port);
furi_message_queue_free(event_queue);
delete_mutex(&state_mutex);
dispose_plugin_state(plugin_state);
plugin_state_free(plugin_state);
return 0;
}
+10 -4
View File
@@ -20,7 +20,7 @@ void token_info_free(TokenInfo* token_info) {
free(token_info);
}
void token_info_set_secret(
bool token_info_set_secret(
TokenInfo* token_info,
const char* base32_token_secret,
uint8_t token_secret_length,
@@ -28,12 +28,18 @@ 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 =
totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length);
bool result;
if(plain_secret_length >= 0) {
token_info->token =
totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length);
result = true;
} else {
result = false;
}
memset(plain_secret, 0, token_secret_length);
free(plain_secret);
return result;
}
uint8_t token_info_get_digits_count(TokenInfo* token_info) {
+1 -1
View File
@@ -16,7 +16,7 @@ typedef struct {
TokenInfo* token_info_alloc();
void token_info_free(TokenInfo* token_info);
void token_info_set_secret(
bool token_info_set_secret(
TokenInfo* token_info,
const char* base32_token_secret,
uint8_t token_secret_length,