|
| 1 | +#include <lib/toolbox/args.h> |
| 2 | +#include <flipper_application/flipper_application.h> |
| 3 | +#include "../../../lib/polyfills/memset_s.h" |
| 4 | +#include "../../../services/config/config.h" |
| 5 | +#include "../../../services/crypto/crypto_facade.h" |
| 6 | +#include "../../../ui/scene_director.h" |
| 7 | +#include "../../cli_helpers.h" |
| 8 | +#include "../../cli_plugin_interface.h" |
| 9 | +#include "../../cli_shared_methods.h" |
| 10 | + |
| 11 | +static const char* BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; |
| 12 | + |
| 13 | +static void print_as_base32(const uint8_t* data, size_t length) { |
| 14 | + int buffer = data[0]; |
| 15 | + size_t next = 1; |
| 16 | + int bitsLeft = 8; |
| 17 | + while(bitsLeft > 0 || next < length) { |
| 18 | + if(bitsLeft < 5) { |
| 19 | + if(next < length) { |
| 20 | + buffer <<= 8; |
| 21 | + buffer |= data[next++] & 0xFF; |
| 22 | + bitsLeft += 8; |
| 23 | + } else { |
| 24 | + int pad = 5 - bitsLeft; |
| 25 | + buffer <<= pad; |
| 26 | + bitsLeft += pad; //-V1026 |
| 27 | + } |
| 28 | + } |
| 29 | + int index = 0x1F & (buffer >> (bitsLeft - 5)); |
| 30 | + bitsLeft -= 5; |
| 31 | + putchar(BASE32_ALPHABET[index]); |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +static void print_uri_component(const char* data, size_t length) { |
| 36 | + const char* c_ptr = data; |
| 37 | + const char* last_ptr = data + length; |
| 38 | + while(c_ptr < last_ptr) { |
| 39 | + const char c = *c_ptr; |
| 40 | + if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || |
| 41 | + c == '-' || c == '_') { |
| 42 | + putchar(c); |
| 43 | + } else { |
| 44 | + printf("%%%x", c); |
| 45 | + } |
| 46 | + c_ptr++; |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +static void handle(PluginState* plugin_state, FuriString* args, Cli* cli) { |
| 51 | + UNUSED(args); |
| 52 | + UNUSED(plugin_state); |
| 53 | + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { |
| 54 | + return; |
| 55 | + } |
| 56 | + |
| 57 | + TOTP_CLI_PRINTF_WARNING("WARNING!\r\n"); |
| 58 | + TOTP_CLI_PRINTF_WARNING( |
| 59 | + "ALL THE INFORMATION (INCL. UNENCRYPTED SECRET) ABOUT ALL THE TOKENS WILL BE EXPORTED AND PRINTED TO THE CONSOLE.\r\n"); |
| 60 | + TOTP_CLI_PRINTF_WARNING("Confirm? [y/n]\r\n"); |
| 61 | + fflush(stdout); |
| 62 | + char user_pick; |
| 63 | + do { |
| 64 | + user_pick = tolower(cli_getc(cli)); |
| 65 | + } while(user_pick != 'y' && user_pick != 'n' && user_pick != CliSymbolAsciiCR && |
| 66 | + user_pick != CliSymbolAsciiETX && user_pick != CliSymbolAsciiEsc); |
| 67 | + |
| 68 | + if(user_pick != 'y' && user_pick != CliSymbolAsciiCR) { |
| 69 | + TOTP_CLI_PRINTF_INFO("User has not confirmed\r\n"); |
| 70 | + return; |
| 71 | + } |
| 72 | + |
| 73 | + TokenInfoIteratorContext* iterator_context = |
| 74 | + totp_config_get_token_iterator_context(plugin_state); |
| 75 | + size_t total_count = totp_token_info_iterator_get_total_count(iterator_context); |
| 76 | + |
| 77 | + TOTP_CLI_LOCK_UI(plugin_state); |
| 78 | + |
| 79 | + size_t original_index = totp_token_info_iterator_get_current_token_index(iterator_context); |
| 80 | + |
| 81 | + cli_nl(); |
| 82 | + TOTP_CLI_PRINTF("# --- EXPORT LIST BEGIN ---\r\n"); |
| 83 | + |
| 84 | + for(size_t i = 0; i < total_count; i++) { |
| 85 | + totp_token_info_iterator_go_to(iterator_context, i); |
| 86 | + const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); |
| 87 | + TOTP_CLI_PRINTF("otpauth://%s/", token_info_get_type_as_cstr(token_info)); |
| 88 | + print_uri_component( |
| 89 | + furi_string_get_cstr(token_info->name), furi_string_size(token_info->name)); |
| 90 | + TOTP_CLI_PRINTF("?secret="); |
| 91 | + size_t key_length; |
| 92 | + uint8_t* key = totp_crypto_decrypt( |
| 93 | + token_info->token, |
| 94 | + token_info->token_length, |
| 95 | + &plugin_state->crypto_settings, |
| 96 | + &key_length); |
| 97 | + print_as_base32(key, key_length); |
| 98 | + memset_s(key, key_length, 0, key_length); |
| 99 | + free(key); |
| 100 | + TOTP_CLI_PRINTF("&algorithm=%s", token_info_get_algo_as_cstr(token_info)); |
| 101 | + TOTP_CLI_PRINTF("&digits=%" PRIu8, token_info->digits); |
| 102 | + if(token_info->type == TokenTypeHOTP) { |
| 103 | + TOTP_CLI_PRINTF("&counter=%" PRIu64, token_info->counter); |
| 104 | + } else { |
| 105 | + TOTP_CLI_PRINTF("&period=%" PRIu8, token_info->duration); |
| 106 | + } |
| 107 | + cli_nl(); |
| 108 | + } |
| 109 | + |
| 110 | + TOTP_CLI_PRINTF("# --- EXPORT LIST END ---\r\n\r\n"); |
| 111 | + |
| 112 | + totp_token_info_iterator_go_to(iterator_context, original_index); |
| 113 | + |
| 114 | + TOTP_CLI_UNLOCK_UI(plugin_state); |
| 115 | +} |
| 116 | + |
| 117 | +static const CliPlugin plugin = {.name = "TOTP CLI Plugin: Export", .handle = &handle}; |
| 118 | + |
| 119 | +static const FlipperAppPluginDescriptor plugin_descriptor = { |
| 120 | + .appid = PLUGIN_APP_ID, |
| 121 | + .ep_api_version = PLUGIN_API_VERSION, |
| 122 | + .entry_point = &plugin, |
| 123 | +}; |
| 124 | + |
| 125 | +const FlipperAppPluginDescriptor* totp_cli_export_plugin_ep() { |
| 126 | + return &plugin_descriptor; |
| 127 | +} |
0 commit comments