mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-06-11 19:33:30 -07:00
TOTP
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
- Last Synced/Checked [OFW](https://github.com/flipperdevices/flipperzero-firmware), changes in [commits](https://github.com/flipperdevices/flipperzero-firmware/commits/dev): `2022-09-28 17:55 GMT`
|
||||
- Settings: Rename from SD `dolphin/name.txt` [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/259)
|
||||
- New animations from stop_oxy: School Days, Whistper of the Heart and The Legend of Zelda
|
||||
- Added: [TOTP (By akopachov)](https://github.com/akopachov/flipperzero-firmware/tree/totp_plugin)
|
||||
|
||||
<details>
|
||||
<summary><B>TO DO</b></summary><br/>
|
||||
@@ -220,6 +221,7 @@ $ ./fbt plugin_dist FIRMWARE_APP_SET=ext_apps
|
||||
- [Spectrum Analyzer (By jolcese)](https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer) [Updates (for testing) Thanks to theY4Kman](https://github.com/theY4Kman/flipperzero-firmware)
|
||||
- [Sub-GHz Bruteforcer (By Ganapati & xMasterX)](https://github.com/Eng1n33r/flipperzero-firmware/pull/57)
|
||||
- [Sub-GHz Playlist (By darmiel)](https://github.com/darmiel/flipper-playlist)
|
||||
- [TOTP (By akopachov)](https://github.com/akopachov/flipperzero-firmware/tree/totp_plugin)
|
||||
- [UPC-A Generator (By McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator)
|
||||
- [USB Keyboard (By huuck)](https://github.com/huuck/FlipperZeroUSBKeyboard)
|
||||
- [WAV Player (By Zlo)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) Updated by Atmanos & RogueMaster To Work
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
App(
|
||||
appid="totp",
|
||||
name="Authenticator",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="totp_app",
|
||||
cdefines=["APP_TOTP"],
|
||||
requires=[
|
||||
"gui",
|
||||
"cli",
|
||||
"dialogs"
|
||||
],
|
||||
provides=["totp_start"],
|
||||
stack_size=2 * 1024,
|
||||
order=20,
|
||||
fap_category="Misc",
|
||||
fap_icon="totp_10px.png",
|
||||
)
|
||||
# App(
|
||||
# appid="totp_start",
|
||||
# apptype=FlipperAppType.STARTUP,
|
||||
# entry_point="totp_on_system_start",
|
||||
# requires=["totp"],
|
||||
# order=30,
|
||||
# )
|
||||
@@ -0,0 +1,95 @@
|
||||
// Base32 implementation
|
||||
//
|
||||
// Copyright 2010 Google Inc.
|
||||
// Author: Markus Gutschke
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "base32.h"
|
||||
|
||||
int base32_decode(const uint8_t *encoded, uint8_t *result, int bufSize) {
|
||||
int buffer = 0;
|
||||
int bitsLeft = 0;
|
||||
int count = 0;
|
||||
for (const uint8_t *ptr = encoded; count < bufSize && *ptr; ++ptr) {
|
||||
uint8_t ch = *ptr;
|
||||
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') {
|
||||
continue;
|
||||
}
|
||||
buffer <<= 5;
|
||||
|
||||
// Deal with commonly mistyped characters
|
||||
if (ch == '0') {
|
||||
ch = 'O';
|
||||
} else if (ch == '1') {
|
||||
ch = 'L';
|
||||
} else if (ch == '8') {
|
||||
ch = 'B';
|
||||
}
|
||||
|
||||
// Look up one base32 digit
|
||||
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
|
||||
ch = (ch & 0x1F) - 1;
|
||||
} else if (ch >= '2' && ch <= '7') {
|
||||
ch -= '2' - 26;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer |= ch;
|
||||
bitsLeft += 5;
|
||||
if (bitsLeft >= 8) {
|
||||
result[count++] = buffer >> (bitsLeft - 8);
|
||||
bitsLeft -= 8;
|
||||
}
|
||||
}
|
||||
if (count < bufSize) {
|
||||
result[count] = '\000';
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Base32 implementation
|
||||
//
|
||||
// Copyright 2010 Google Inc.
|
||||
// Author: Markus Gutschke
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Encode and decode from base32 encoding using the following alphabet:
|
||||
// ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
|
||||
// This alphabet is documented in RFC 4648/3548
|
||||
//
|
||||
// We allow white-space and hyphens, but all other characters are considered
|
||||
// invalid.
|
||||
//
|
||||
// All functions return the number of output bytes or -1 on error. If the
|
||||
// output buffer is too small, the result will silently be truncated.
|
||||
|
||||
#ifndef _BASE32_H_
|
||||
#define _BASE32_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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")));
|
||||
|
||||
#endif /* _BASE32_H_ */
|
||||
@@ -0,0 +1,189 @@
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
#include "../../types/common.h"
|
||||
#include "../../types/token_info.h"
|
||||
|
||||
#define CONFIG_FILE_PATH "/ext/apps/Misc/totp.conf"
|
||||
#define CONFIG_FILE_HEADER "Flipper TOTP plugin config file"
|
||||
#define CONFIG_FILE_VERSION 1
|
||||
|
||||
Storage* totp_open_storage() {
|
||||
return furi_record_open(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
void totp_close_storage() {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
FlipperFormat* totp_open_config_file(Storage* storage) {
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
|
||||
if (storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK) {
|
||||
if(!flipper_format_file_open_existing(fff_data_file, CONFIG_FILE_PATH)) {
|
||||
FURI_LOG_E(LOGGING_TAG, "Error opening existing file %s", CONFIG_FILE_PATH);
|
||||
totp_close_config_file(fff_data_file);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (!flipper_format_file_open_new(fff_data_file, CONFIG_FILE_PATH)) {
|
||||
totp_close_config_file(fff_data_file);
|
||||
FURI_LOG_E(LOGGING_TAG, "Error creating new file %s", CONFIG_FILE_PATH);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flipper_format_write_header_cstr(fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_VERSION);
|
||||
float tmp_tz = 0;
|
||||
flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &tmp_tz, 1);
|
||||
if(!flipper_format_rewind(fff_data_file)) {
|
||||
totp_close_config_file(fff_data_file);
|
||||
FURI_LOG_E(LOGGING_TAG, "Rewind error");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return fff_data_file;
|
||||
}
|
||||
|
||||
void totp_full_save_config_file(PluginState* const plugin_state) {
|
||||
Storage* storage = totp_open_storage();
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
|
||||
flipper_format_file_open_always(fff_data_file, CONFIG_FILE_PATH);
|
||||
flipper_format_write_header_cstr(fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_VERSION);
|
||||
flipper_format_write_hex(fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], 16);
|
||||
flipper_format_write_hex(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, plugin_state->crypto_verify_data, plugin_state->crypto_verify_data_length);
|
||||
flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1);
|
||||
ListNode* node = plugin_state->tokens_list;
|
||||
while (node != NULL) {
|
||||
TokenInfo* token_info = node->data;
|
||||
flipper_format_write_string_cstr(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name);
|
||||
flipper_format_write_hex(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length);
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
totp_close_config_file(fff_data_file);
|
||||
totp_close_storage();
|
||||
}
|
||||
|
||||
void totp_config_file_load_base(PluginState* const plugin_state) {
|
||||
Storage* storage = totp_open_storage();
|
||||
FlipperFormat* fff_data_file = totp_open_config_file(storage);
|
||||
|
||||
plugin_state->timezone_offset = 0;
|
||||
|
||||
string_t temp_str;
|
||||
uint32_t temp_data32;
|
||||
string_init(temp_str);
|
||||
|
||||
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
|
||||
FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!flipper_format_read_hex(fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], 16)) {
|
||||
FURI_LOG_D(LOGGING_TAG, "Missing base IV");
|
||||
}
|
||||
|
||||
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)) {
|
||||
plugin_state->crypto_verify_data = malloc(sizeof(uint8_t) * crypto_size);
|
||||
plugin_state->crypto_verify_data_length = crypto_size;
|
||||
if (!flipper_format_read_hex(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, plugin_state->crypto_verify_data, crypto_size)) {
|
||||
FURI_LOG_D(LOGGING_TAG, "Missing crypto verify token");
|
||||
free(plugin_state->crypto_verify_data);
|
||||
plugin_state->crypto_verify_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
flipper_format_rewind(fff_data_file);
|
||||
|
||||
if (!flipper_format_read_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) {
|
||||
plugin_state->timezone_offset = 0;
|
||||
FURI_LOG_D(LOGGING_TAG, "Missing timezone offset information, defaulting to 0");
|
||||
}
|
||||
|
||||
string_clear(temp_str);
|
||||
totp_close_config_file(fff_data_file);
|
||||
totp_close_storage();
|
||||
}
|
||||
|
||||
void totp_config_file_load_tokens(PluginState* const plugin_state) {
|
||||
Storage* storage = totp_open_storage();
|
||||
FlipperFormat* fff_data_file = totp_open_config_file(storage);
|
||||
|
||||
string_t temp_str;
|
||||
uint32_t temp_data32;
|
||||
string_init(temp_str);
|
||||
|
||||
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
|
||||
FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t index = 0;
|
||||
bool has_any_plain_secret = false;
|
||||
while (true) {
|
||||
if (!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) {
|
||||
break;
|
||||
}
|
||||
|
||||
TokenInfo* tokenInfo = malloc(sizeof(TokenInfo));
|
||||
|
||||
const char* temp_cstr = string_get_cstr(temp_str);
|
||||
tokenInfo->name = (char *)malloc(strlen(temp_cstr) + 1);
|
||||
strcpy(tokenInfo->name, temp_cstr);
|
||||
|
||||
uint32_t secret_bytes_count;
|
||||
if (!flipper_format_get_value_count(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (secret_bytes_count == 1) { // Plain secret key
|
||||
if (!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
temp_cstr = 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)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_D(LOGGING_TAG, "Found token \"%s\"", tokenInfo->name);
|
||||
|
||||
if (plugin_state->tokens_list == NULL) {
|
||||
plugin_state->tokens_list = list_init_head(tokenInfo);
|
||||
} else {
|
||||
list_add(plugin_state->tokens_list, tokenInfo);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
plugin_state->tokens_count = index;
|
||||
plugin_state->token_list_loaded = true;
|
||||
|
||||
FURI_LOG_D(LOGGING_TAG, "Found %d tokens", index);
|
||||
|
||||
string_clear(temp_str);
|
||||
totp_close_config_file(fff_data_file);
|
||||
totp_close_storage();
|
||||
|
||||
if (has_any_plain_secret) {
|
||||
totp_full_save_config_file(plugin_state);
|
||||
}
|
||||
}
|
||||
|
||||
void totp_close_config_file(FlipperFormat* file) {
|
||||
if (file == NULL) return;
|
||||
flipper_format_file_close(file);
|
||||
flipper_format_free(file);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef _TOTP_CONFIG_FILE_H_
|
||||
#define _TOTP_CONFIG_FILE_H_
|
||||
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <furi.h>
|
||||
#include "../../types/plugin_state.h"
|
||||
|
||||
#define TOTP_CONFIG_KEY_TIMEZONE "Timezone"
|
||||
#define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName"
|
||||
#define TOTP_CONFIG_KEY_TOKEN_SECRET "TokenSecret"
|
||||
#define TOTP_CONFIG_KEY_CRYPTO_VERIFY "Crypto"
|
||||
#define TOTP_CONFIG_KEY_BASE_IV "BaseIV"
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "list.h"
|
||||
|
||||
ListNode *list_init_head(void* data) {
|
||||
ListNode *new = (ListNode *) malloc(sizeof(ListNode));
|
||||
new->data = data;
|
||||
new->next = NULL;
|
||||
return new;
|
||||
}
|
||||
|
||||
ListNode *list_add(ListNode *head, void* data) {
|
||||
ListNode *new = (ListNode *) malloc(sizeof(ListNode));
|
||||
new->data = data;
|
||||
new->next = NULL;
|
||||
|
||||
if (head == NULL)
|
||||
head = new;
|
||||
else {
|
||||
ListNode *it;
|
||||
|
||||
for (it = head; it->next != NULL; it = it->next)
|
||||
;
|
||||
|
||||
it->next = new;
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
ListNode *list_find(ListNode *head, void* data) {
|
||||
ListNode *it;
|
||||
|
||||
for (it = head; it != NULL; it = it->next)
|
||||
if (it->data == data)
|
||||
break;
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
ListNode *list_element_at(ListNode *head, uint16_t index) {
|
||||
ListNode *it;
|
||||
uint16_t i;
|
||||
for (it = head, i = 0; it != NULL && i < index; it = it->next, i++);
|
||||
return it;
|
||||
}
|
||||
|
||||
ListNode *list_remove(ListNode *head, ListNode *ep) {
|
||||
if (head == ep) {
|
||||
ListNode *new_head = head->next;
|
||||
free(head);
|
||||
return new_head;
|
||||
}
|
||||
|
||||
ListNode *it;
|
||||
|
||||
for (it = head; it->next != ep; it = it->next)
|
||||
;
|
||||
|
||||
it->next = ep->next;
|
||||
free(ep);
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
void list_free(ListNode *head) {
|
||||
ListNode *it = head, *tmp;
|
||||
|
||||
while (it != NULL) {
|
||||
tmp = it;
|
||||
it = it->next;
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef _TOTP_LIST_H_
|
||||
#define _TOTP_LIST_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef struct ListNode {
|
||||
void* data;
|
||||
struct ListNode *next;
|
||||
} ListNode;
|
||||
|
||||
ListNode *list_init_head(void* data);
|
||||
ListNode *list_add(ListNode *head, void* data); /* adds element with specified data to the end of the list and returns new head node. */
|
||||
ListNode *list_find(ListNode *head, void* data); /* returns pointer of element with specified data in list. */
|
||||
ListNode *list_element_at(ListNode *head, uint16_t index); /* returns pointer of element with specified index in list. */
|
||||
ListNode *list_remove(ListNode *head, ListNode *ep); /* removes element from the list and returns new head node. */
|
||||
void list_free(ListNode *head); /* deletes all elements of the list. */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
#include <string.h>
|
||||
#include "sha1.h"
|
||||
|
||||
union _buffer {
|
||||
uint8_t b[BLOCK_LENGTH];
|
||||
uint32_t w[BLOCK_LENGTH/4];
|
||||
} buffer;
|
||||
union _state {
|
||||
uint8_t b[HASH_LENGTH];
|
||||
uint32_t w[HASH_LENGTH/4];
|
||||
} state;
|
||||
|
||||
uint8_t bufferOffset;
|
||||
uint32_t byteCount;
|
||||
uint8_t keyBuffer[BLOCK_LENGTH];
|
||||
uint8_t innerHash[HASH_LENGTH];
|
||||
|
||||
#define SHA1_K0 0x5a827999
|
||||
#define SHA1_K20 0x6ed9eba1
|
||||
#define SHA1_K40 0x8f1bbcdc
|
||||
#define SHA1_K60 0xca62c1d6
|
||||
|
||||
uint8_t sha1InitState[] = {
|
||||
0x01,0x23,0x45,0x67, // H0
|
||||
0x89,0xab,0xcd,0xef, // H1
|
||||
0xfe,0xdc,0xba,0x98, // H2
|
||||
0x76,0x54,0x32,0x10, // H3
|
||||
0xf0,0xe1,0xd2,0xc3 // H4
|
||||
};
|
||||
|
||||
void sha1_init(void) {
|
||||
memcpy(state.b,sha1InitState,HASH_LENGTH);
|
||||
byteCount = 0;
|
||||
bufferOffset = 0;
|
||||
}
|
||||
|
||||
uint32_t rol32(uint32_t number, uint8_t bits) {
|
||||
return ((number << bits) | (uint32_t)(number >> (32-bits)));
|
||||
}
|
||||
|
||||
void hashBlock() {
|
||||
uint8_t i;
|
||||
uint32_t a,b,c,d,e,t;
|
||||
|
||||
a=state.w[0];
|
||||
b=state.w[1];
|
||||
c=state.w[2];
|
||||
d=state.w[3];
|
||||
e=state.w[4];
|
||||
for (i=0; i<80; i++) {
|
||||
if (i>=16) {
|
||||
t = buffer.w[(i+13)&15] ^ buffer.w[(i+8)&15] ^ buffer.w[(i+2)&15] ^ buffer.w[i&15];
|
||||
buffer.w[i&15] = rol32(t,1);
|
||||
}
|
||||
if (i<20) {
|
||||
t = (d ^ (b & (c ^ d))) + SHA1_K0;
|
||||
} else if (i<40) {
|
||||
t = (b ^ c ^ d) + SHA1_K20;
|
||||
} else if (i<60) {
|
||||
t = ((b & c) | (d & (b | c))) + SHA1_K40;
|
||||
} else {
|
||||
t = (b ^ c ^ d) + SHA1_K60;
|
||||
}
|
||||
t+=rol32(a,5) + e + buffer.w[i&15];
|
||||
e=d;
|
||||
d=c;
|
||||
c=rol32(b,30);
|
||||
b=a;
|
||||
a=t;
|
||||
}
|
||||
state.w[0] += a;
|
||||
state.w[1] += b;
|
||||
state.w[2] += c;
|
||||
state.w[3] += d;
|
||||
state.w[4] += e;
|
||||
}
|
||||
|
||||
void addUncounted(uint8_t data) {
|
||||
buffer.b[bufferOffset ^ 3] = data;
|
||||
bufferOffset++;
|
||||
if (bufferOffset == BLOCK_LENGTH) {
|
||||
hashBlock();
|
||||
bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void sha1_write(uint8_t data) {
|
||||
++byteCount;
|
||||
addUncounted(data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void sha1_write_array(uint8_t *buffer, uint8_t size){
|
||||
while (size--) {
|
||||
sha1_write(*buffer++);
|
||||
}
|
||||
}
|
||||
|
||||
void pad() {
|
||||
// Implement SHA-1 padding (fips180-2 ��5.1.1)
|
||||
|
||||
// Pad with 0x80 followed by 0x00 until the end of the block
|
||||
addUncounted(0x80);
|
||||
while (bufferOffset != 56) addUncounted(0x00);
|
||||
|
||||
// Append length in the last 8 bytes
|
||||
addUncounted(0); // We're only using 32 bit lengths
|
||||
addUncounted(0); // But SHA-1 supports 64 bit lengths
|
||||
addUncounted(0); // So zero pad the top bits
|
||||
addUncounted(byteCount >> 29); // Shifting to multiply by 8
|
||||
addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as
|
||||
addUncounted(byteCount >> 13); // byte.
|
||||
addUncounted(byteCount >> 5);
|
||||
addUncounted(byteCount << 3);
|
||||
}
|
||||
|
||||
uint8_t* sha1_result(void) {
|
||||
// Pad to complete the last block
|
||||
pad();
|
||||
|
||||
// Swap byte order back
|
||||
uint8_t i;
|
||||
for (i=0; i<5; i++) {
|
||||
uint32_t a,b;
|
||||
a=state.w[i];
|
||||
b=a<<24;
|
||||
b|=(a<<8) & 0x00ff0000;
|
||||
b|=(a>>8) & 0x0000ff00;
|
||||
b|=a>>24;
|
||||
state.w[i]=b;
|
||||
}
|
||||
|
||||
// Return pointer to hash (20 characters)
|
||||
return state.b;
|
||||
}
|
||||
|
||||
#define HMAC_IPAD 0x36
|
||||
#define HMAC_OPAD 0x5c
|
||||
|
||||
void sha1_init_hmac(const uint8_t* key, uint8_t keyLength) {
|
||||
uint8_t i;
|
||||
memset(keyBuffer,0,BLOCK_LENGTH);
|
||||
if (keyLength > BLOCK_LENGTH) {
|
||||
// Hash long keys
|
||||
sha1_init();
|
||||
for (;keyLength--;) sha1_write(*key++);
|
||||
memcpy(keyBuffer,sha1_result(),HASH_LENGTH);
|
||||
} else {
|
||||
// Block length keys are used as is
|
||||
memcpy(keyBuffer,key,keyLength);
|
||||
}
|
||||
// Start inner hash
|
||||
sha1_init();
|
||||
for (i=0; i<BLOCK_LENGTH; i++) {
|
||||
sha1_write(keyBuffer[i] ^ HMAC_IPAD);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* sha1_result_hmac(void) {
|
||||
uint8_t i;
|
||||
// Complete inner hash
|
||||
memcpy(innerHash,sha1_result(),HASH_LENGTH);
|
||||
// Calculate outer hash
|
||||
sha1_init();
|
||||
for (i=0; i<BLOCK_LENGTH; i++) sha1_write(keyBuffer[i] ^ HMAC_OPAD);
|
||||
for (i=0; i<HASH_LENGTH; i++) sha1_write(innerHash[i]);
|
||||
return sha1_result();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef _sha1_h
|
||||
#define _sha1_h
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define HASH_LENGTH 20
|
||||
#define BLOCK_LENGTH 64
|
||||
|
||||
void sha1_init(void);
|
||||
void sha1_init_hmac(const uint8_t* secret, uint8_t secretLength);
|
||||
uint8_t* sha1_result(void);
|
||||
uint8_t* sha1_result_hmac(void);
|
||||
void sha1_write(uint8_t);
|
||||
void sha1_write_array(uint8_t *buffer, uint8_t size);
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "totp.h"
|
||||
#include "sha1.h"
|
||||
|
||||
int _timeZoneOffset;
|
||||
uint32_t _timeStep;
|
||||
|
||||
// Init the library with the private key, its length and the timeStep duration
|
||||
void totp_setup(uint32_t timeStep) {
|
||||
_timeStep = timeStep;
|
||||
}
|
||||
|
||||
void totp_set_timezone(float timezone){
|
||||
_timeZoneOffset = (int)(timezone * 3600.0f);
|
||||
}
|
||||
|
||||
// Generate a code, using the number of steps provided
|
||||
uint32_t totp_get_code_from_steps(uint8_t* hmacKey, uint8_t keyLength, uint32_t steps) {
|
||||
// STEP 0, map the number of steps in a 8-bytes array (counter value)
|
||||
uint8_t _byteArray[8];
|
||||
_byteArray[0] = 0x00;
|
||||
_byteArray[1] = 0x00;
|
||||
_byteArray[2] = 0x00;
|
||||
_byteArray[3] = 0x00;
|
||||
_byteArray[4] = (uint8_t)((steps >> 24) & 0xFF);
|
||||
_byteArray[5] = (uint8_t)((steps >> 16) & 0xFF);
|
||||
_byteArray[6] = (uint8_t)((steps >> 8) & 0XFF);
|
||||
_byteArray[7] = (uint8_t)((steps & 0XFF));
|
||||
|
||||
// STEP 1, get the HMAC-SHA1 hash from counter and key
|
||||
sha1_init_hmac(hmacKey, keyLength);
|
||||
sha1_write_array(_byteArray, 8);
|
||||
uint8_t* _hash = sha1_result_hmac();
|
||||
|
||||
// STEP 2, apply dynamic truncation to obtain a 4-bytes string
|
||||
uint32_t _truncatedHash = 0;
|
||||
uint8_t _offset = _hash[20 - 1] & 0xF;
|
||||
uint8_t j;
|
||||
for (j = 0; j < 4; ++j) {
|
||||
_truncatedHash <<= 8;
|
||||
_truncatedHash |= _hash[_offset + j];
|
||||
}
|
||||
|
||||
// STEP 3, compute the OTP value
|
||||
_truncatedHash &= 0x7FFFFFFF; //Disabled
|
||||
_truncatedHash %= 1000000;
|
||||
|
||||
return _truncatedHash;
|
||||
}
|
||||
|
||||
uint32_t time_struct_to_timestamp(struct tm time){
|
||||
//time.tm_mon -= 1;
|
||||
//time.tm_year -= 1900;
|
||||
return mktime(&(time)) - 2208988800;
|
||||
}
|
||||
|
||||
// Generate a code, using the timestamp provided
|
||||
uint32_t totp_get_code_from_timestamp(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStamp) {
|
||||
uint32_t steps;
|
||||
if (_timeZoneOffset >= 0) {
|
||||
steps = (timeStamp - _timeZoneOffset) / _timeStep;
|
||||
} else {
|
||||
steps = (timeStamp + (-_timeZoneOffset)) / _timeStep;
|
||||
}
|
||||
|
||||
return totp_get_code_from_steps(hmacKey, keyLength, steps);
|
||||
}
|
||||
|
||||
// Generate a code, using the timestamp provided
|
||||
uint32_t totp_get_code_from_time_struct(uint8_t* hmacKey, uint8_t keyLength, struct tm time) {
|
||||
return totp_get_code_from_timestamp(hmacKey, keyLength, time_struct_to_timestamp(time));
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef _token_h
|
||||
#define _token_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
void totp_setup(uint32_t timeStep);
|
||||
void totp_set_timezone(float timezone);
|
||||
uint32_t totp_get_code_from_timestamp(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStamp);
|
||||
uint32_t totp_get_code_from_time_struct(uint8_t* hmacKey, uint8_t keyLength, struct tm time);
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "canvas_extensions.h"
|
||||
|
||||
void canvas_draw_dots(Canvas* const canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height, const uint8_t *dots) {
|
||||
for (uint8_t i = 0; i < width; i++) {
|
||||
for (uint8_t j = 0; j < height; j++) {
|
||||
if (dots[i + j * width]) {
|
||||
canvas_draw_dot(canvas, x + i, y + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef _TOTPCANVAS_EXTENSIONS_H_
|
||||
#define _TOTPCANVAS_EXTENSIONS_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <gui/gui.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
void canvas_draw_dots(Canvas* const canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height, const uint8_t *dots);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef _TOTP_UI_CONSTANTS_H_
|
||||
#define _TOTP_UI_CONSTANTS_H_
|
||||
|
||||
#define SCREEN_WIDTH 128
|
||||
#define SCREEN_HEIGHT 64
|
||||
#define SCREEN_WIDTH_CENTER 64
|
||||
#define SCREEN_HEIGHT_CENTER 32
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef _TOTP_ICONS_H_
|
||||
#define _TOTP_ICONS_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
static const uint8_t ICON_ARROW_LEFT_8x9[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 0, 1, 1, 1,
|
||||
0, 0, 0, 1, 1, 1, 1, 1,
|
||||
0, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 1,
|
||||
};
|
||||
|
||||
static const uint8_t ICON_ARROW_RIGHT_8x9[] = {
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "ui_controls.h"
|
||||
#include "constants.h"
|
||||
|
||||
#define TEXT_BOX_HEIGHT 13
|
||||
|
||||
void ui_control_text_box_render(Canvas* const canvas, uint8_t y, char* text, bool is_selected) {
|
||||
const uint8_t TEXT_BOX_MARGIN = 4;
|
||||
if (is_selected) {
|
||||
canvas_draw_rframe(canvas, TEXT_BOX_MARGIN, TEXT_BOX_MARGIN + y, SCREEN_WIDTH - TEXT_BOX_MARGIN - TEXT_BOX_MARGIN, TEXT_BOX_HEIGHT, 0);
|
||||
canvas_draw_rframe(canvas, TEXT_BOX_MARGIN - 1, TEXT_BOX_MARGIN + y - 1, SCREEN_WIDTH - TEXT_BOX_MARGIN - TEXT_BOX_MARGIN + 2, TEXT_BOX_HEIGHT + 2, 1);
|
||||
} else {
|
||||
canvas_draw_rframe(canvas, TEXT_BOX_MARGIN, TEXT_BOX_MARGIN + y, SCREEN_WIDTH - TEXT_BOX_MARGIN - TEXT_BOX_MARGIN, TEXT_BOX_HEIGHT, 1);
|
||||
}
|
||||
|
||||
canvas_draw_str_aligned(canvas, TEXT_BOX_MARGIN + 2, TEXT_BOX_MARGIN + 3 + y, AlignLeft, AlignTop, text);
|
||||
}
|
||||
|
||||
void ui_control_button_render(Canvas* const canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height, char* text, bool is_selected) {
|
||||
if (is_selected) {
|
||||
canvas_draw_rbox(canvas, x, y, width, height, 1);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
} else {
|
||||
canvas_draw_rframe(canvas, x, y, width, height, 1);
|
||||
}
|
||||
|
||||
canvas_draw_str_aligned(canvas, x + (width >> 1), y + (height >> 1) + 1, AlignCenter, AlignCenter, text);
|
||||
if (is_selected) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef _TOTP_UI_CONTROLS_H_
|
||||
#define _TOTP_UI_CONTROLS_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
void ui_control_text_box_render(Canvas* const canvas, uint8_t y, char* text, bool is_selected);
|
||||
void ui_control_button_render(Canvas* const canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height, char* text, bool is_selected);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "totp_input_text.h"
|
||||
#include <gui/view_i.h>
|
||||
#include "../../types/common.h"
|
||||
|
||||
void view_draw(View* view, Canvas* canvas) {
|
||||
furi_assert(view);
|
||||
if(view->draw_callback) {
|
||||
void* data = view_get_model(view);
|
||||
view->draw_callback(canvas, data);
|
||||
view_unlock_model(view);
|
||||
}
|
||||
}
|
||||
|
||||
bool view_input(View* view, InputEvent* event) {
|
||||
furi_assert(view);
|
||||
if(view->input_callback) {
|
||||
return view->input_callback(event, view->context);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void view_unlock_model(View* view) {
|
||||
furi_assert(view);
|
||||
if(view->model_type == ViewModelTypeLocking) {
|
||||
ViewModelLocking* model = (ViewModelLocking*)(view->model);
|
||||
furi_check(furi_mutex_release(model->mutex) == FuriStatusOk);
|
||||
}
|
||||
}
|
||||
|
||||
static void commit_text_input_callback(void* context) {
|
||||
InputTextSceneState* text_input_state = (InputTextSceneState *)context;
|
||||
if (text_input_state->callback != 0) {
|
||||
InputTextSceneCallbackResult* result = malloc(sizeof(InputTextSceneCallbackResult));
|
||||
result->user_input_length = strlen(text_input_state->text_input_buffer);
|
||||
result->user_input = malloc(result->user_input_length + 1);
|
||||
result->callback_data = text_input_state->callback_data;
|
||||
strcpy(result->user_input, text_input_state->text_input_buffer);
|
||||
text_input_state->callback(result);
|
||||
}
|
||||
}
|
||||
|
||||
InputTextSceneState* totp_input_text_activate(InputTextSceneContext* context) {
|
||||
InputTextSceneState* text_input_state = malloc(sizeof(InputTextSceneState));
|
||||
text_input_state->text_input = text_input_alloc();
|
||||
text_input_state->text_input_view = text_input_get_view(text_input_state->text_input);
|
||||
text_input_state->callback = context->callback;
|
||||
text_input_state->callback_data = context->callback_data;
|
||||
text_input_set_header_text(text_input_state->text_input, context->header_text);
|
||||
text_input_set_result_callback(
|
||||
text_input_state->text_input,
|
||||
commit_text_input_callback,
|
||||
text_input_state,
|
||||
&text_input_state->text_input_buffer[0],
|
||||
INPUT_BUFFER_SIZE,
|
||||
true);
|
||||
return text_input_state;
|
||||
}
|
||||
|
||||
void totp_input_text_render(Canvas* const canvas, InputTextSceneState* text_input_state) {
|
||||
view_draw(text_input_state->text_input_view, canvas);
|
||||
}
|
||||
|
||||
bool totp_input_text_handle_event(PluginEvent* const event, InputTextSceneState* text_input_state) {
|
||||
if(event->type == EventTypeKey) {
|
||||
view_input(text_input_state->text_input_view, &event->input);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void totp_input_text_free(InputTextSceneState* state) {
|
||||
text_input_free(state->text_input);
|
||||
free(state);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef _TOTP_INPUT_TEXT_H_
|
||||
#define _TOTP_INPUT_TEXT_H_
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../../types/plugin_state.h"
|
||||
#include "../../types/plugin_event.h"
|
||||
|
||||
typedef struct {
|
||||
char* user_input;
|
||||
uint8_t user_input_length;
|
||||
void* callback_data;
|
||||
} InputTextSceneCallbackResult;
|
||||
|
||||
typedef void (*InputTextSceneCallback)(InputTextSceneCallbackResult* result);
|
||||
|
||||
typedef struct {
|
||||
InputTextSceneCallback callback;
|
||||
char* header_text;
|
||||
void* callback_data;
|
||||
} InputTextSceneContext;
|
||||
|
||||
#define INPUT_BUFFER_SIZE 255
|
||||
|
||||
typedef struct {
|
||||
TextInput* text_input;
|
||||
View* text_input_view;
|
||||
char text_input_buffer[INPUT_BUFFER_SIZE];
|
||||
InputTextSceneCallback callback;
|
||||
void* callback_data;
|
||||
} InputTextSceneState;
|
||||
|
||||
InputTextSceneState* totp_input_text_activate(InputTextSceneContext* context);
|
||||
void totp_input_text_render(Canvas* const canvas, InputTextSceneState* text_input_state);
|
||||
bool totp_input_text_handle_event(PluginEvent* const event, InputTextSceneState* text_input_state);
|
||||
void totp_input_text_free(InputTextSceneState* state);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "totp_scene_add_new_token.h"
|
||||
#include "../../types/common.h"
|
||||
#include "../../lib/ui/constants.h"
|
||||
#include "../scene_director.h"
|
||||
#include "totp_input_text.h"
|
||||
#include "../../types/token_info.h"
|
||||
#include "../../lib/list/list.h"
|
||||
#include "../../lib/base32/base32.h"
|
||||
#include "../../lib/config/config.h"
|
||||
#include "../../lib/ui/ui_controls.h"
|
||||
#include "../generate_token/totp_scene_generate_token.h"
|
||||
|
||||
typedef enum {
|
||||
TokenNameTextBox,
|
||||
TokenSecretTextBox,
|
||||
ConfirmButton,
|
||||
} Control;
|
||||
|
||||
typedef struct {
|
||||
char* token_name;
|
||||
uint8_t token_name_length;
|
||||
char* token_secret;
|
||||
uint8_t token_secret_length;
|
||||
bool saved;
|
||||
Control selected_control;
|
||||
InputTextSceneContext* token_name_input_context;
|
||||
InputTextSceneContext* token_secret_input_context;
|
||||
InputTextSceneState* input_state;
|
||||
uint32_t input_started_at;
|
||||
int current_token_index;
|
||||
} SceneState;
|
||||
|
||||
void totp_scene_add_new_token_init(PluginState* plugin_state) {
|
||||
UNUSED(plugin_state);
|
||||
}
|
||||
|
||||
static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) {
|
||||
SceneState* scene_state = result->callback_data;
|
||||
free(scene_state->token_name);
|
||||
scene_state->token_name = result->user_input;
|
||||
scene_state->token_name_length = result->user_input_length;
|
||||
scene_state->input_started_at = 0;
|
||||
free(result);
|
||||
}
|
||||
|
||||
static void on_token_secret_user_comitted(InputTextSceneCallbackResult* result) {
|
||||
SceneState* scene_state = result->callback_data;
|
||||
free(scene_state->token_secret);
|
||||
scene_state->token_secret = result->user_input;
|
||||
scene_state->token_secret_length = result->user_input_length;
|
||||
scene_state->input_started_at = 0;
|
||||
free(result);
|
||||
}
|
||||
|
||||
void totp_scene_add_new_token_activate(PluginState* plugin_state, const TokenAddEditSceneContext* context) {
|
||||
SceneState* scene_state = malloc(sizeof(SceneState));
|
||||
plugin_state->current_scene_state = scene_state;
|
||||
scene_state->token_name = "Name";
|
||||
scene_state->token_name_length = strlen(scene_state->token_name);
|
||||
scene_state->token_secret = "Secret";
|
||||
scene_state->token_secret_length = strlen(scene_state->token_secret);
|
||||
|
||||
scene_state->token_name_input_context = malloc(sizeof(InputTextSceneContext));
|
||||
scene_state->token_name_input_context->header_text = "Enter token name";
|
||||
scene_state->token_name_input_context->callback_data = scene_state;
|
||||
scene_state->token_name_input_context->callback = on_token_name_user_comitted;
|
||||
|
||||
scene_state->token_secret_input_context = malloc(sizeof(InputTextSceneContext));
|
||||
scene_state->token_secret_input_context->header_text = "Enter token secret";
|
||||
scene_state->token_secret_input_context->callback_data = scene_state;
|
||||
scene_state->token_secret_input_context->callback = on_token_secret_user_comitted;
|
||||
|
||||
scene_state->input_state = NULL;
|
||||
|
||||
if (context == NULL) {
|
||||
scene_state->current_token_index = -1;
|
||||
} else {
|
||||
scene_state->current_token_index = context->current_token_index;
|
||||
}
|
||||
}
|
||||
|
||||
void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state) {
|
||||
SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
|
||||
if (scene_state->input_started_at > 0) {
|
||||
totp_input_text_render(canvas, scene_state->input_state);
|
||||
return;
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Add new token");
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
ui_control_text_box_render(canvas, 10, scene_state->token_name, scene_state->selected_control == TokenNameTextBox);
|
||||
ui_control_text_box_render(canvas, 27, scene_state->token_secret, scene_state->selected_control == TokenSecretTextBox);
|
||||
ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 24, 48, 48, 13, "Confirm", scene_state->selected_control == ConfirmButton);
|
||||
}
|
||||
|
||||
bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state) {
|
||||
if(event->type == EventTypeKey) {
|
||||
SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
|
||||
if (scene_state->input_started_at > 0 && furi_get_tick() - scene_state->input_started_at > 300) {
|
||||
return totp_input_text_handle_event(event, scene_state->input_state);
|
||||
}
|
||||
|
||||
if (event->input.type == InputTypeLong && event->input.key == InputKeyBack) {
|
||||
return false;
|
||||
} else if(event->input.type == InputTypePress) {
|
||||
switch(event->input.key) {
|
||||
case InputKeyUp:
|
||||
if (scene_state->selected_control > TokenNameTextBox) {
|
||||
scene_state->selected_control--;
|
||||
}
|
||||
break;
|
||||
case InputKeyDown:
|
||||
if (scene_state->selected_control < ConfirmButton) {
|
||||
scene_state->selected_control++;
|
||||
}
|
||||
break;
|
||||
case InputKeyRight:
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
break;
|
||||
case InputKeyOk:
|
||||
switch (scene_state->selected_control) {
|
||||
case TokenNameTextBox:
|
||||
if (scene_state->input_state != NULL) {
|
||||
totp_input_text_free(scene_state->input_state);
|
||||
}
|
||||
scene_state->input_state = totp_input_text_activate(scene_state->token_name_input_context);
|
||||
scene_state->input_started_at = furi_get_tick();
|
||||
break;
|
||||
case TokenSecretTextBox:
|
||||
if (scene_state->input_state != NULL) {
|
||||
totp_input_text_free(scene_state->input_state);
|
||||
}
|
||||
scene_state->input_state = totp_input_text_activate(scene_state->token_secret_input_context);
|
||||
scene_state->input_started_at = furi_get_tick();
|
||||
break;
|
||||
case ConfirmButton: {
|
||||
TokenInfo* tokenInfo = malloc(sizeof(TokenInfo));
|
||||
tokenInfo->name = malloc(scene_state->token_name_length + 1);
|
||||
strcpy(tokenInfo->name, scene_state->token_name);
|
||||
|
||||
token_info_set_secret(tokenInfo, scene_state->token_secret, scene_state->token_secret_length, &plugin_state->iv[0]);
|
||||
|
||||
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++;
|
||||
|
||||
Storage* cfg_storage = totp_open_storage();
|
||||
FlipperFormat* cfg_file = totp_open_config_file(cfg_storage);
|
||||
|
||||
flipper_format_seek_to_end(cfg_file);
|
||||
flipper_format_write_string_cstr(cfg_file, TOTP_CONFIG_KEY_TOKEN_NAME, tokenInfo->name);
|
||||
flipper_format_write_hex(cfg_file, TOTP_CONFIG_KEY_TOKEN_SECRET, tokenInfo->token, tokenInfo->token_length);
|
||||
|
||||
totp_close_config_file(cfg_file);
|
||||
totp_close_storage();
|
||||
|
||||
GenerateTokenSceneContext generate_scene_context = { .current_token_index = plugin_state->tokens_count - 1 };
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, &generate_scene_context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InputKeyBack:
|
||||
if (scene_state->current_token_index >= 0) {
|
||||
GenerateTokenSceneContext generate_scene_context = { .current_token_index = scene_state->current_token_index };
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, &generate_scene_context);
|
||||
} else {
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void totp_scene_add_new_token_deactivate(PluginState* plugin_state) {
|
||||
if (plugin_state->current_scene_state == NULL) return;
|
||||
SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
|
||||
free(scene_state->token_name);
|
||||
free(scene_state->token_secret);
|
||||
|
||||
free(scene_state->token_name_input_context->header_text);
|
||||
free(scene_state->token_name_input_context);
|
||||
|
||||
free(scene_state->token_secret_input_context->header_text);
|
||||
free(scene_state->token_secret_input_context);
|
||||
|
||||
if (scene_state->input_state != NULL) {
|
||||
totp_input_text_free(scene_state->input_state);
|
||||
}
|
||||
|
||||
free(plugin_state->current_scene_state);
|
||||
plugin_state->current_scene_state = NULL;
|
||||
}
|
||||
|
||||
void totp_scene_add_new_token_free(PluginState* plugin_state) {
|
||||
UNUSED(plugin_state);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef _TOTP_SCENE_ADD_NEW_TOKEN_H_
|
||||
#define _TOTP_SCENE_ADD_NEW_TOKEN_H_
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../../types/plugin_state.h"
|
||||
#include "../../types/plugin_event.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t current_token_index;
|
||||
} TokenAddEditSceneContext;
|
||||
|
||||
void totp_scene_add_new_token_init(PluginState* plugin_state);
|
||||
void totp_scene_add_new_token_activate(PluginState* plugin_state, const TokenAddEditSceneContext* context);
|
||||
void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state);
|
||||
bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state);
|
||||
void totp_scene_add_new_token_deactivate(PluginState* plugin_state);
|
||||
void totp_scene_add_new_token_free(PluginState* plugin_state);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,159 @@
|
||||
#include "totp_scene_authenticate.h"
|
||||
#include "../../types/common.h"
|
||||
#include "../../lib/ui/icons.h"
|
||||
#include "../../lib/ui/canvas_extensions.h"
|
||||
#include "../../lib/ui/constants.h"
|
||||
#include "../../lib/config/config.h"
|
||||
#include "../scene_director.h"
|
||||
#include "../totp_scenes_enum.h"
|
||||
|
||||
#define MAX_CODE_LENGTH 16
|
||||
#define CRYPTO_VERIFY_KEY "FFF_Crypto_pass"
|
||||
#define CRYPTO_VERIFY_KEY_LENGTH 16
|
||||
#define BASE_IV_LENGTH 16
|
||||
|
||||
typedef struct {
|
||||
uint8_t code_input[MAX_CODE_LENGTH];
|
||||
uint8_t code_length;
|
||||
} SceneState;
|
||||
|
||||
void totp_scene_authenticate_init(PluginState* plugin_state) {
|
||||
for (uint8_t i = 0; i < MAX_CODE_LENGTH; i++) plugin_state->iv[i] = 0;
|
||||
}
|
||||
|
||||
void totp_scene_authenticate_activate(PluginState* plugin_state) {
|
||||
SceneState* scene_state = malloc(sizeof(SceneState));
|
||||
scene_state->code_length = 0;
|
||||
for (uint8_t i = 0; i < MAX_CODE_LENGTH; i++) scene_state->code_input[i] = 0;
|
||||
plugin_state->current_scene_state = scene_state;
|
||||
}
|
||||
|
||||
void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state) {
|
||||
SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
|
||||
if (plugin_state->crypto_verify_data == NULL) {
|
||||
canvas_draw_str_aligned(canvas, SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER - 20, AlignCenter, AlignCenter, "Use arrow keys");
|
||||
canvas_draw_str_aligned(canvas, SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER - 5, AlignCenter, AlignCenter, "to setup new PIN");
|
||||
} else {
|
||||
canvas_draw_str_aligned(canvas, SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER - 10, AlignCenter, AlignCenter, "Use arrow keys to enter PIN");
|
||||
}
|
||||
const uint8_t PIN_ASTERISK_RADIUS = 3;
|
||||
const uint8_t PIN_ASTERISK_STEP = (PIN_ASTERISK_RADIUS << 1) + 2;
|
||||
if (scene_state->code_length > 0) {
|
||||
uint8_t left_start_x = (scene_state->code_length - 1) * PIN_ASTERISK_STEP >> 1;
|
||||
for (uint8_t i = 0; i < scene_state->code_length; i++) {
|
||||
canvas_draw_disc(
|
||||
canvas,
|
||||
SCREEN_WIDTH_CENTER - left_start_x + i * PIN_ASTERISK_STEP,
|
||||
SCREEN_HEIGHT_CENTER + 10,
|
||||
PIN_ASTERISK_RADIUS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool totp_scene_authenticate_handle_event(PluginEvent* const event, PluginState* plugin_state) {
|
||||
if(event->type == EventTypeKey) {
|
||||
if (event->input.type == InputTypeLong && event->input.key == InputKeyBack) {
|
||||
return false;
|
||||
} else if(event->input.type == InputTypePress) {
|
||||
SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
|
||||
|
||||
const uint8_t ARROW_UP_CODE = 2;
|
||||
const uint8_t ARROW_RIGHT_CODE = 8;
|
||||
const uint8_t ARROW_DOWN_CODE = 11;
|
||||
const uint8_t ARROW_LEFT_CODE = 5;
|
||||
|
||||
switch(event->input.key) {
|
||||
case InputKeyUp:
|
||||
if (scene_state->code_length < MAX_CODE_LENGTH) {
|
||||
scene_state->code_input[scene_state->code_length] = ARROW_UP_CODE;
|
||||
scene_state->code_length++;
|
||||
}
|
||||
break;
|
||||
case InputKeyDown:
|
||||
if (scene_state->code_length < MAX_CODE_LENGTH) {
|
||||
scene_state->code_input[scene_state->code_length] = ARROW_DOWN_CODE;
|
||||
scene_state->code_length++;
|
||||
}
|
||||
break;
|
||||
case InputKeyRight:
|
||||
if (scene_state->code_length < MAX_CODE_LENGTH) {
|
||||
scene_state->code_input[scene_state->code_length] = ARROW_RIGHT_CODE;
|
||||
scene_state->code_length++;
|
||||
}
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
if (scene_state->code_length < MAX_CODE_LENGTH) {
|
||||
scene_state->code_input[scene_state->code_length] = ARROW_LEFT_CODE;
|
||||
scene_state->code_length++;
|
||||
}
|
||||
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], BASE_IV_LENGTH);
|
||||
}
|
||||
|
||||
memcpy(&plugin_state->iv[0], &plugin_state->base_iv[0], BASE_IV_LENGTH);
|
||||
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, BASE_IV_LENGTH);
|
||||
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) {
|
||||
FURI_LOG_D(LOGGING_TAG, "PIN is valid");
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
|
||||
} else {
|
||||
FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid");
|
||||
for (uint8_t i = 0; i < MAX_CODE_LENGTH; i++) {
|
||||
scene_state->code_input[i] = 0;
|
||||
plugin_state->iv[i] = 0;
|
||||
}
|
||||
scene_state->code_length = 0;
|
||||
}
|
||||
break;
|
||||
case InputKeyBack:
|
||||
if (scene_state->code_length > 0) {
|
||||
scene_state->code_input[scene_state->code_length - 1] = 0;
|
||||
scene_state->code_length--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void totp_scene_authenticate_deactivate(PluginState* plugin_state) {
|
||||
if (plugin_state->current_scene_state == NULL) return;
|
||||
free(plugin_state->current_scene_state);
|
||||
plugin_state->current_scene_state = NULL;
|
||||
}
|
||||
|
||||
void totp_scene_authenticate_free(PluginState* plugin_state) {
|
||||
UNUSED(plugin_state);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef _TOTP_SCENE_AUTHENTICATE_H_
|
||||
#define _TOTP_SCENE_AUTHENTICATE_H_
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../../types/plugin_state.h"
|
||||
#include "../../types/plugin_event.h"
|
||||
|
||||
void totp_scene_authenticate_init(PluginState* plugin_state);
|
||||
void totp_scene_authenticate_activate(PluginState* plugin_state);
|
||||
void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state);
|
||||
bool totp_scene_authenticate_handle_event(PluginEvent* const event, PluginState* plugin_state);
|
||||
void totp_scene_authenticate_deactivate(PluginState* plugin_state);
|
||||
void totp_scene_authenticate_free(PluginState* plugin_state);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,215 @@
|
||||
#include <gui/gui.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include "totp_scene_generate_token.h"
|
||||
#include "../../types/token_info.h"
|
||||
#include "../../types/common.h"
|
||||
#include "../../lib/ui/icons.h"
|
||||
#include "../../lib/ui/canvas_extensions.h"
|
||||
#include "../../lib/ui/constants.h"
|
||||
#include "../../lib/totp/totp.h"
|
||||
#include "../../lib/config/config.h"
|
||||
#include "../scene_director.h"
|
||||
#include "../token_menu/totp_scene_token_menu.h"
|
||||
|
||||
#define TOKEN_LIFETIME 30
|
||||
|
||||
typedef struct {
|
||||
uint8_t current_token_index;
|
||||
char last_code[7];
|
||||
char* last_code_name;
|
||||
bool need_token_update;
|
||||
uint32_t last_token_gen_time;
|
||||
} SceneState;
|
||||
|
||||
static const NotificationSequence sequence_short_vibro_and_sound = {
|
||||
&message_display_backlight_on,
|
||||
&message_green_255,
|
||||
&message_vibro_on,
|
||||
&message_note_c5,
|
||||
&message_delay_50,
|
||||
&message_vibro_off,
|
||||
&message_sound_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void i_token_to_str(uint32_t i_token_code, char* str) {
|
||||
str[6] = '\0';
|
||||
if (i_token_code == 0 || i_token_code > 999999) {
|
||||
str[5] = '-';
|
||||
str[4] = '-';
|
||||
str[3] = '-';
|
||||
str[2] = '-';
|
||||
str[1] = '-';
|
||||
str[0] = '-';
|
||||
} else {
|
||||
str[5] = i_token_code % 10 + 0x30;
|
||||
str[4] = (i_token_code = i_token_code / 10) % 10 + 0x30;
|
||||
str[3] = (i_token_code = i_token_code / 10) % 10 + 0x30;
|
||||
str[2] = (i_token_code = i_token_code / 10) % 10 + 0x30;
|
||||
str[1] = (i_token_code = i_token_code / 10) % 10 + 0x30;
|
||||
str[0] = (i_token_code = i_token_code / 10) % 10 + 0x30;
|
||||
}
|
||||
}
|
||||
|
||||
void update_totp_params(PluginState* const plugin_state) {
|
||||
SceneState* scene_state = (SceneState *)plugin_state->current_scene_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);
|
||||
|
||||
scene_state->need_token_update = true;
|
||||
scene_state->last_code_name = tokenInfo->name;
|
||||
}
|
||||
}
|
||||
|
||||
void totp_scene_generate_token_init(PluginState* plugin_state) {
|
||||
UNUSED(plugin_state);
|
||||
totp_setup(TOKEN_LIFETIME);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
SceneState* scene_state = malloc(sizeof(SceneState));
|
||||
if (context == NULL) {
|
||||
scene_state->current_token_index = 0;
|
||||
} else {
|
||||
scene_state->current_token_index = context->current_token_index;
|
||||
}
|
||||
scene_state->need_token_update = true;
|
||||
plugin_state->current_scene_state = scene_state;
|
||||
totp_set_timezone(plugin_state->timezone_offset);
|
||||
FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset);
|
||||
update_totp_params(plugin_state);
|
||||
}
|
||||
|
||||
void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) {
|
||||
if (plugin_state->tokens_count == 0) {
|
||||
canvas_draw_str_aligned(canvas, SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER - 10, AlignCenter, AlignCenter, "Token list is empty");
|
||||
canvas_draw_str_aligned(canvas, SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER + 10, AlignCenter, AlignCenter, "Press OK button to add");
|
||||
return;
|
||||
}
|
||||
|
||||
SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
|
||||
FuriHalRtcDateTime curr_dt;
|
||||
furi_hal_rtc_get_datetime(&curr_dt);
|
||||
uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
|
||||
|
||||
bool is_new_token_time = curr_ts % TOKEN_LIFETIME == 0;
|
||||
if (is_new_token_time && scene_state->last_token_gen_time != curr_ts) {
|
||||
scene_state->need_token_update = true;
|
||||
}
|
||||
|
||||
if (scene_state->need_token_update) {
|
||||
scene_state->need_token_update = false;
|
||||
scene_state->last_token_gen_time = curr_ts;
|
||||
|
||||
TokenInfo* tokenInfo = (TokenInfo*)(list_element_at(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);
|
||||
|
||||
i_token_to_str(totp_get_code_from_timestamp(key, tokenInfo->token_length, curr_ts), scene_state->last_code);
|
||||
memset(key, 0, tokenInfo->token_length);
|
||||
free(key);
|
||||
|
||||
if (is_new_token_time) {
|
||||
notification_message(plugin_state->notification, &sequence_short_vibro_and_sound);
|
||||
}
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
uint16_t token_name_width = canvas_string_width(canvas, scene_state->last_code_name);
|
||||
if (SCREEN_WIDTH - token_name_width > 18) {
|
||||
canvas_draw_str_aligned(canvas, SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER - 20, AlignCenter, AlignCenter, scene_state->last_code_name);
|
||||
} else {
|
||||
canvas_draw_str_aligned(canvas, 9, SCREEN_HEIGHT_CENTER - 20, AlignLeft, AlignCenter, scene_state->last_code_name);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 0, SCREEN_HEIGHT_CENTER - 24, 9, 9);
|
||||
canvas_draw_box(canvas, SCREEN_WIDTH - 10, SCREEN_HEIGHT_CENTER - 24, 9, 9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str_aligned(canvas, SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER, AlignCenter, AlignCenter, scene_state->last_code);
|
||||
|
||||
const uint8_t BAR_MARGIN = 3;
|
||||
const uint8_t BAR_HEIGHT = 4;
|
||||
float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME;
|
||||
uint8_t barWidth = (uint8_t)((float)(SCREEN_WIDTH - (BAR_MARGIN << 1)) * percentDone);
|
||||
uint8_t barX = ((SCREEN_WIDTH - (BAR_MARGIN << 1) - barWidth) >> 1) + BAR_MARGIN;
|
||||
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
barX,
|
||||
SCREEN_HEIGHT - BAR_MARGIN - BAR_HEIGHT,
|
||||
barWidth,
|
||||
BAR_HEIGHT);
|
||||
|
||||
if (plugin_state->tokens_count > 1) {
|
||||
canvas_draw_dots(canvas, 0, SCREEN_HEIGHT_CENTER - 24, 8, 9, ICON_ARROW_LEFT_8x9);
|
||||
canvas_draw_dots(canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, 8, 9, ICON_ARROW_RIGHT_8x9);
|
||||
}
|
||||
}
|
||||
|
||||
bool totp_scene_generate_token_handle_event(PluginEvent* const event, PluginState* plugin_state) {
|
||||
if(event->type == EventTypeKey) {
|
||||
if (event->input.type == InputTypeLong && event->input.key == InputKeyBack) {
|
||||
return false;
|
||||
} else if(event->input.type == InputTypePress) {
|
||||
SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
|
||||
switch(event->input.key) {
|
||||
case InputKeyUp:
|
||||
break;
|
||||
case InputKeyDown:
|
||||
break;
|
||||
case InputKeyRight:
|
||||
if (scene_state->current_token_index < plugin_state->tokens_count - 1) {
|
||||
scene_state->current_token_index++;
|
||||
} else {
|
||||
scene_state->current_token_index = 0;
|
||||
}
|
||||
update_totp_params(plugin_state);
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
if (scene_state->current_token_index > 0) {
|
||||
scene_state->current_token_index--;
|
||||
} else {
|
||||
scene_state->current_token_index = plugin_state->tokens_count - 1;
|
||||
}
|
||||
update_totp_params(plugin_state);
|
||||
break;
|
||||
case InputKeyOk:
|
||||
if (plugin_state->tokens_count == 0) {
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken, NULL);
|
||||
} else {
|
||||
TokenMenuSceneContext ctx = { .current_token_index = scene_state->current_token_index };
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &ctx);
|
||||
}
|
||||
break;
|
||||
case InputKeyBack:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void totp_scene_generate_token_deactivate(PluginState* plugin_state) {
|
||||
if (plugin_state->current_scene_state == NULL) return;
|
||||
SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
|
||||
|
||||
free(scene_state->last_code);
|
||||
free(scene_state);
|
||||
plugin_state->current_scene_state = NULL;
|
||||
}
|
||||
|
||||
void totp_scene_generate_token_free(PluginState* plugin_state) {
|
||||
UNUSED(plugin_state);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef _TOTP_SCENE_GENERATE_TOKEN_H_
|
||||
#define _TOTP_SCENE_GENERATE_TOKEN_H_
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../../types/plugin_state.h"
|
||||
#include "../../types/plugin_event.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t current_token_index;
|
||||
} GenerateTokenSceneContext;
|
||||
|
||||
void totp_scene_generate_token_init(PluginState* plugin_state);
|
||||
void totp_scene_generate_token_activate(PluginState* plugin_state, const GenerateTokenSceneContext* context);
|
||||
void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state);
|
||||
bool totp_scene_generate_token_handle_event(PluginEvent* const event, PluginState* plugin_state);
|
||||
void totp_scene_generate_token_deactivate(PluginState* plugin_state);
|
||||
void totp_scene_generate_token_free(PluginState* plugin_state);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "../types/common.h"
|
||||
#include "scene_director.h"
|
||||
#include "authenticate/totp_scene_authenticate.h"
|
||||
#include "generate_token/totp_scene_generate_token.h"
|
||||
#include "add_new_token/totp_scene_add_new_token.h"
|
||||
#include "token_menu/totp_scene_token_menu.h"
|
||||
|
||||
void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene, const void* context) {
|
||||
plugin_state->changing_scene = true;
|
||||
totp_scene_director_deactivate_active_scene(plugin_state);
|
||||
switch (scene) {
|
||||
case TotpSceneGenerateToken:
|
||||
totp_scene_generate_token_activate(plugin_state, context);
|
||||
break;
|
||||
case TotpSceneAuthentication:
|
||||
totp_scene_authenticate_activate(plugin_state);
|
||||
break;
|
||||
case TotpSceneAddNewToken:
|
||||
totp_scene_add_new_token_activate(plugin_state, context);
|
||||
break;
|
||||
case TotpSceneTokenMenu:
|
||||
totp_scene_token_menu_activate(plugin_state, context);
|
||||
break;
|
||||
}
|
||||
|
||||
plugin_state->current_scene = scene;
|
||||
plugin_state->changing_scene = false;
|
||||
}
|
||||
|
||||
void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state) {
|
||||
switch (plugin_state->current_scene) {
|
||||
case TotpSceneGenerateToken:
|
||||
totp_scene_generate_token_deactivate(plugin_state);
|
||||
break;
|
||||
case TotpSceneAuthentication:
|
||||
totp_scene_authenticate_deactivate(plugin_state);
|
||||
break;
|
||||
case TotpSceneAddNewToken:
|
||||
totp_scene_add_new_token_deactivate(plugin_state);
|
||||
break;
|
||||
case TotpSceneTokenMenu:
|
||||
totp_scene_token_menu_deactivate(plugin_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void totp_scene_director_init_scenes(PluginState* const plugin_state) {
|
||||
totp_scene_authenticate_init(plugin_state);
|
||||
totp_scene_generate_token_init(plugin_state);
|
||||
totp_scene_add_new_token_init(plugin_state);
|
||||
totp_scene_token_menu_init(plugin_state);
|
||||
}
|
||||
|
||||
void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state) {
|
||||
switch (plugin_state->current_scene) {
|
||||
case TotpSceneGenerateToken:
|
||||
totp_scene_generate_token_render(canvas, plugin_state);
|
||||
break;
|
||||
case TotpSceneAuthentication:
|
||||
totp_scene_authenticate_render(canvas, plugin_state);
|
||||
break;
|
||||
case TotpSceneAddNewToken:
|
||||
totp_scene_add_new_token_render(canvas, plugin_state);
|
||||
break;
|
||||
case TotpSceneTokenMenu:
|
||||
totp_scene_token_menu_render(canvas, plugin_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void totp_scene_director_dispose(PluginState* const plugin_state) {
|
||||
totp_scene_generate_token_free(plugin_state);
|
||||
totp_scene_authenticate_free(plugin_state);
|
||||
totp_scene_add_new_token_free(plugin_state);
|
||||
totp_scene_token_menu_free(plugin_state);
|
||||
}
|
||||
|
||||
bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state) {
|
||||
bool processing = true;
|
||||
switch (plugin_state->current_scene) {
|
||||
case TotpSceneGenerateToken:
|
||||
processing = totp_scene_generate_token_handle_event(event, plugin_state);
|
||||
break;
|
||||
case TotpSceneAuthentication:
|
||||
processing = totp_scene_authenticate_handle_event(event, plugin_state);
|
||||
break;
|
||||
case TotpSceneAddNewToken:
|
||||
processing = totp_scene_add_new_token_handle_event(event, plugin_state);
|
||||
break;
|
||||
case TotpSceneTokenMenu:
|
||||
processing = totp_scene_token_menu_handle_event(event, plugin_state);
|
||||
break;
|
||||
}
|
||||
|
||||
return processing;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef _SCENE_DIRECTOR_H_
|
||||
#define _SCENE_DIRECTOR_H_
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include "../types/plugin_state.h"
|
||||
#include "../types/plugin_event.h"
|
||||
#include "totp_scenes_enum.h"
|
||||
|
||||
void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene, const void* context);
|
||||
void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state);
|
||||
void totp_scene_director_init_scenes(PluginState* const plugin_state);
|
||||
void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state);
|
||||
void totp_scene_director_dispose(PluginState* const plugin_state);
|
||||
bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,115 @@
|
||||
#include "totp_scene_token_menu.h"
|
||||
#include <gui/gui.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include "../../lib/ui/ui_controls.h"
|
||||
#include "../../lib/ui/constants.h"
|
||||
#include "../scene_director.h"
|
||||
#include "../../lib/config/config.h"
|
||||
#include "../../lib/list/list.h"
|
||||
#include "../../types/token_info.h"
|
||||
#include "../generate_token/totp_scene_generate_token.h"
|
||||
#include "../add_new_token/totp_scene_add_new_token.h"
|
||||
|
||||
typedef enum {
|
||||
AddNewToken,
|
||||
DeleteToken
|
||||
} Control;
|
||||
|
||||
typedef struct {
|
||||
Control selected_control;
|
||||
uint8_t current_token_index;
|
||||
} SceneState;
|
||||
|
||||
void totp_scene_token_menu_init(PluginState* plugin_state) {
|
||||
UNUSED(plugin_state);
|
||||
}
|
||||
|
||||
void totp_scene_token_menu_activate(PluginState* plugin_state, const TokenMenuSceneContext* context) {
|
||||
SceneState* scene_state = malloc(sizeof(SceneState));
|
||||
plugin_state->current_scene_state = scene_state;
|
||||
scene_state->current_token_index = context->current_token_index;
|
||||
}
|
||||
|
||||
void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state) {
|
||||
SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
|
||||
ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 36, 5, 72, 21, "Add new token", scene_state->selected_control == AddNewToken);
|
||||
ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 36, 39, 72, 21, "Delete token", scene_state->selected_control == DeleteToken);
|
||||
}
|
||||
|
||||
bool totp_scene_token_menu_handle_event(PluginEvent* const event, PluginState* plugin_state) {
|
||||
if (event->type == EventTypeKey) {
|
||||
SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
|
||||
if(event->input.type == InputTypePress) {
|
||||
switch(event->input.key) {
|
||||
case InputKeyUp:
|
||||
if (scene_state->selected_control > AddNewToken) {
|
||||
scene_state->selected_control--;
|
||||
}
|
||||
break;
|
||||
case InputKeyDown:
|
||||
if (scene_state->selected_control < DeleteToken) {
|
||||
scene_state->selected_control++;
|
||||
}
|
||||
break;
|
||||
case InputKeyRight:
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
break;
|
||||
case InputKeyOk:
|
||||
switch (scene_state->selected_control) {
|
||||
case AddNewToken: {
|
||||
TokenAddEditSceneContext add_new_token_scene_context = { .current_token_index = scene_state->current_token_index };
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken, &add_new_token_scene_context);
|
||||
break;
|
||||
}
|
||||
case DeleteToken: {
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
dialog_message_set_buttons(message, "No", NULL, "Yes");
|
||||
dialog_message_set_header(message, "Confirmation", 0, 0, AlignLeft, AlignTop);
|
||||
dialog_message_set_text(message, "Are you sure want to delete?", SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER, AlignCenter, AlignCenter);
|
||||
DialogMessageButton dialog_result = dialog_message_show(dialogs, message);
|
||||
dialog_message_free(message);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
if (dialog_result == DialogMessageButtonRight) {
|
||||
uint8_t i = 0;
|
||||
|
||||
ListNode* list_node = plugin_state->tokens_list;
|
||||
while (i < scene_state->current_token_index && list_node->next != NULL) {
|
||||
list_node = list_node->next;
|
||||
i++;
|
||||
}
|
||||
|
||||
TokenInfo* tokenInfo = list_node->data;
|
||||
token_info_free(tokenInfo);
|
||||
plugin_state->tokens_list = list_remove(plugin_state->tokens_list, list_node);
|
||||
plugin_state->tokens_count--;
|
||||
|
||||
totp_full_save_config_file(plugin_state);
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InputKeyBack: {
|
||||
GenerateTokenSceneContext generate_scene_context = { .current_token_index = scene_state->current_token_index };
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, &generate_scene_context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void totp_scene_token_menu_deactivate(PluginState* plugin_state) {
|
||||
if (plugin_state->current_scene_state == NULL) return;
|
||||
|
||||
free(plugin_state->current_scene_state);
|
||||
plugin_state->current_scene_state = NULL;
|
||||
}
|
||||
|
||||
void totp_scene_token_menu_free(PluginState* plugin_state) {
|
||||
UNUSED(plugin_state);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef _TOTP_SCENE_TOKEN_MENU_H_
|
||||
#define _TOTP_SCENE_TOKEN_MENU_H_
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../../types/plugin_state.h"
|
||||
#include "../../types/plugin_event.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t current_token_index;
|
||||
} TokenMenuSceneContext;
|
||||
|
||||
void totp_scene_token_menu_init(PluginState* plugin_state);
|
||||
void totp_scene_token_menu_activate(PluginState* plugin_state, const TokenMenuSceneContext* context);
|
||||
void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state);
|
||||
bool totp_scene_token_menu_handle_event(PluginEvent* const event, PluginState* plugin_state);
|
||||
void totp_scene_token_menu_deactivate(PluginState* plugin_state);
|
||||
void totp_scene_token_menu_free(PluginState* plugin_state);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef _TOTP_SCENES_ENUM_H_
|
||||
#define _TOTP_SCENES_ENUM_H_
|
||||
typedef enum {
|
||||
TotpSceneAuthentication,
|
||||
TotpSceneGenerateToken,
|
||||
TotpSceneAddNewToken,
|
||||
TotpSceneTokenMenu
|
||||
} Scene;
|
||||
#endif
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 496 B |
@@ -0,0 +1,121 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdlib.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include "lib/base32/base32.h"
|
||||
#include "lib/list/list.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "types/plugin_state.h"
|
||||
#include "types/token_info.h"
|
||||
#include "types/plugin_event.h"
|
||||
#include "types/event_type.h"
|
||||
#include "types/common.h"
|
||||
#include "scenes/scene_director.h"
|
||||
|
||||
#define IDLE_TIMEOUT 60000
|
||||
|
||||
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) {
|
||||
totp_scene_director_render(canvas, plugin_state);
|
||||
}
|
||||
|
||||
release_mutex((ValueMutex*)ctx, plugin_state);
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
PluginEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void totp_state_init(PluginState* const plugin_state) {
|
||||
plugin_state->gui = furi_record_open(RECORD_GUI);
|
||||
plugin_state->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
totp_config_file_load_base(plugin_state);
|
||||
|
||||
totp_scene_director_init_scenes(plugin_state);
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
|
||||
}
|
||||
|
||||
static void dispose_plugin_state(PluginState* plugin_state) {
|
||||
totp_scene_director_deactivate_active_scene(plugin_state);
|
||||
|
||||
totp_scene_director_dispose(plugin_state);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
|
||||
ListNode* node = plugin_state->tokens_list;
|
||||
ListNode* tmp;
|
||||
while (node != NULL) {
|
||||
tmp = node->next;
|
||||
TokenInfo* tokenInfo = (TokenInfo*)node->data;
|
||||
token_info_free(tokenInfo);
|
||||
free(node);
|
||||
node = tmp;
|
||||
}
|
||||
|
||||
if (plugin_state->crypto_verify_data != NULL) {
|
||||
free(plugin_state->crypto_verify_data);
|
||||
}
|
||||
free(plugin_state);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
return 255;
|
||||
}
|
||||
|
||||
// Set system callbacks
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, render_callback, &state_mutex);
|
||||
view_port_input_callback_set(view_port, input_callback, event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
gui_add_view_port(plugin_state->gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
PluginEvent event;
|
||||
bool processing = true;
|
||||
uint32_t last_user_interaction_time = furi_get_tick();
|
||||
while(processing) {
|
||||
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);
|
||||
|
||||
if(event_status == FuriStatusOk) {
|
||||
if (event.type == EventTypeKey) {
|
||||
last_user_interaction_time = furi_get_tick();
|
||||
}
|
||||
|
||||
processing = totp_scene_director_handle_event(&event, plugin_state);
|
||||
} else if (plugin_state->current_scene != TotpSceneAuthentication && furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) {
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
release_mutex(&state_mutex, plugin_state);
|
||||
}
|
||||
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(plugin_state->gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
delete_mutex(&state_mutex);
|
||||
dispose_plugin_state(plugin_state);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
void totp_cli_print_usage() {
|
||||
printf("Usage:\r\n");
|
||||
printf("totp <cmd> <args>\r\n");
|
||||
printf("Cmd list:\r\n");
|
||||
printf("\tadd <secret:string> <name:string>\t - Add new TOTP secret\r\n");
|
||||
printf("\tremove <name:string>\t - Remove TOTP token\r\n");
|
||||
printf("\reset\t - Reset app to default (reset PIN and removes all tokens)\r\n");
|
||||
};
|
||||
|
||||
static void totp_cli(Cli* cli, string_t args, void* context) {
|
||||
UNUSED(cli);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
totp_cli_print_usage();
|
||||
// TODO: implement add\remove\reset
|
||||
}
|
||||
|
||||
void totp_on_system_start() {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
|
||||
cli_add_command(cli, "totp", CliCommandFlagDefault, totp_cli, NULL);
|
||||
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(totp_cli);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef _TOTP_COMMON_TYPES_H_
|
||||
#define _TOTP_COMMON_TYPES_H_
|
||||
|
||||
#define LOGGING_TAG "TOTP APP"
|
||||
#define CRYPTO_KEY_SLOT 2
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef _TOTP_EVENT_TYPE_H_
|
||||
#define _TOTP_EVENT_TYPE_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef _TOTP_PLUGIN_EVENT_H_
|
||||
#define _TOTP_PLUGIN_EVENT_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <input/input.h>
|
||||
#include "event_type.h"
|
||||
|
||||
typedef struct {
|
||||
EventType type;
|
||||
InputEvent input;
|
||||
} PluginEvent;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef _TOTP_PLUGIN_STATE_H_
|
||||
#define _TOTP_PLUGIN_STATE_H_
|
||||
|
||||
#include <notification/notification.h>
|
||||
#include <gui/gui.h>
|
||||
#include "../lib/list/list.h"
|
||||
#include "../scenes/totp_scenes_enum.h"
|
||||
|
||||
typedef struct {
|
||||
Scene current_scene;
|
||||
void* current_scene_state;
|
||||
bool changing_scene;
|
||||
NotificationApp* notification;
|
||||
Gui* gui;
|
||||
|
||||
float timezone_offset;
|
||||
ListNode* tokens_list;
|
||||
bool token_list_loaded;
|
||||
uint8_t tokens_count;
|
||||
|
||||
uint8_t* crypto_verify_data;
|
||||
uint8_t crypto_verify_data_length;
|
||||
uint8_t iv[16];
|
||||
uint8_t base_iv[16];
|
||||
} PluginState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#include <furi/furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "token_info.h"
|
||||
#include "stdlib.h"
|
||||
#include "common.h"
|
||||
#include "../lib/base32/base32.h"
|
||||
|
||||
void token_info_free(TokenInfo* token_info) {
|
||||
if (token_info == NULL) return;
|
||||
free(token_info->name);
|
||||
free(token_info->token);
|
||||
free(token_info);
|
||||
}
|
||||
|
||||
void token_info_set_secret(TokenInfo* token_info, const char* base32_token_secret, uint8_t token_secret_length, uint8_t* iv) {
|
||||
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 = 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);
|
||||
free(plain_secret);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef _TOTP_TOKEN_INFO_H_
|
||||
#define _TOTP_TOKEN_INFO_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t* token;
|
||||
uint8_t token_length;
|
||||
char* name;
|
||||
} TokenInfo;
|
||||
|
||||
void token_info_free(TokenInfo* token_info);
|
||||
void token_info_set_secret(TokenInfo* token_info, const char* base32_token_secret, uint8_t token_secret_length, uint8_t* iv);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user