mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-11-24 11:14:26 +03:00
Merge branch 'dev' into flappy_patches
This commit is contained in:
commit
89d49fdba7
@ -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();
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_i.h>
|
||||
#include <infrared.h>
|
||||
#include <infrared_worker.h>
|
||||
#include <furi_hal_infrared.h>
|
||||
@ -6,12 +7,24 @@
|
||||
#include <toolbox/args.h>
|
||||
|
||||
#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 <input_file> [<output_file>]\r\n");
|
||||
printf("\tir universal <tv, ac> <signal name>\r\n");
|
||||
printf("\tir universal list <tv, ac>\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()) {
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
134
applications/plugins/totp/services/crypto/crypto.c
Normal file
134
applications/plugins/totp/services/crypto/crypto.c
Normal file
@ -0,0 +1,134 @@
|
||||
#include "crypto.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#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;
|
||||
}
|
16
applications/plugins/totp/services/crypto/crypto.h
Normal file
16
applications/plugins/totp/services/crypto/crypto.h
Normal file
@ -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);
|
@ -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);
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define LOGGING_TAG "TOTP APP"
|
||||
#define CRYPTO_KEY_SLOT 2
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -4044,17 +4044,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"
|
||||
|
|
@ -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);
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user