This commit is contained in:
RogueMaster
2022-09-28 16:43:02 -04:00
parent 289dc5f94c
commit 43b62bd2a5
40 changed files with 2104 additions and 0 deletions
+2
View File
@@ -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
+24
View File
@@ -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
+72
View File
@@ -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);
}
}
+19
View File
@@ -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
+169
View File
@@ -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();
}
+15
View File
@@ -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
+71
View File
@@ -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));
}
+11
View File
@@ -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
+30
View File
@@ -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

+121
View File
@@ -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;
}
+32
View File
@@ -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
}
+7
View File
@@ -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