Merge branch 'dev' into flappy_patches

This commit is contained in:
MX 2022-10-17 21:39:09 +03:00
commit 89d49fdba7
No known key found for this signature in database
GPG Key ID: 6C4C311DFD4B4AB5
16 changed files with 502 additions and 93 deletions

View File

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

View File

@ -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()) {

View File

@ -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;

View File

@ -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 {

View File

@ -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) {

View File

@ -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();

View File

@ -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"

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

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

View File

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

View File

@ -1,4 +1,3 @@
#pragma once
#define LOGGING_TAG "TOTP APP"
#define CRYPTO_KEY_SLOT 2

View File

@ -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;

View File

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

View File

@ -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"

1 entry status name type params
4044 Function + view_port_draw_callback_set void ViewPort*, ViewPortDrawCallback, void*
4045 Function + view_port_enabled_set void ViewPort*, _Bool
4046 Function + view_port_free void ViewPort*
4047 Function - + view_port_get_height uint8_t ViewPort*
4048 Function + view_port_get_orientation ViewPortOrientation const ViewPort*
4049 Function + view_port_get_width uint8_t ViewPort*
4050 Function + view_port_input_callback_set void ViewPort*, ViewPortInputCallback, void*
4051 Function + view_port_is_enabled _Bool ViewPort*
4052 Function - + view_port_set_height void ViewPort*, uint8_t
4053 Function + view_port_set_orientation void ViewPort*, ViewPortOrientation
4054 Function + view_port_set_width void ViewPort*, uint8_t
4055 Function + view_port_update void ViewPort*
4056 Function + view_set_context void View*, void*
4057 Function - + view_set_custom_callback void View*, ViewCustomCallback
4058 Function + view_set_draw_callback void View*, ViewDrawCallback
4059 Function + view_set_enter_callback void View*, ViewCallback
4060 Function + view_set_exit_callback void View*, ViewCallback

View File

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

View File

@ -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(