Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions main/button_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ typedef enum {
BTN_OTP_ISSUER,
BTN_OTP_TYPE,
BTN_OTP_DETAILS,
BTN_OTP_DETAILS_VIEW,
BTN_OTP_DETAILS_EXPORT,
BTN_OTP_DETAILS_SECRET,
BTN_OTP_RETAIN_CONFIRM,
BTN_OTP_DISCARD_DELETE,

Expand Down
37 changes: 34 additions & 3 deletions main/process/dashboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ gui_activity_t* make_bip39_passphrase_prefs_activity(
gui_activity_t* make_otp_activity(void);
gui_activity_t* make_new_otp_activity(void);

gui_activity_t* make_view_export_otp_activity(const char* name);

bool show_otp_details_activity(
const otpauth_ctx_t* ctx, bool initial_confirmation, bool is_valid, bool show_delete_btn);
gui_activity_t* make_show_hotp_code_activity(const char* name, const char* codestr, bool confirm_only);
Expand Down Expand Up @@ -1465,6 +1467,34 @@ static bool delete_otp_record(const char* otpname)
return true;
}


static bool show_otp_detail_options_activity(
const otpauth_ctx_t* otp_ctx,
const bool initial_confirmation,
const bool is_valid,
const bool show_delete_btn)
{
JADE_ASSERT(otp_ctx);
JADE_ASSERT(otp_ctx->name);

gui_activity_t* const act = make_view_export_otp_activity(otp_ctx->name);
int32_t ev_id;

while (true) {
gui_set_current_activity(act);

if (gui_activity_wait_event(act, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0)) {
if (ev_id == BTN_BACK) {
return true;
} else if (ev_id == BTN_OTP_DETAILS_VIEW) {
show_otp_details_activity(otp_ctx, initial_confirmation, is_valid, show_delete_btn);
} else if (ev_id == BTN_OTP_DETAILS_EXPORT) {
show_otp_uri_qr_activity(otp_ctx);
}
}
}
return true;
}
// HOTP token-code fixed
static bool display_hotp_screen(const otpauth_ctx_t* otp_ctx, const char* token, const bool confirm_only)
{
Expand Down Expand Up @@ -1492,7 +1522,7 @@ static bool display_hotp_screen(const otpauth_ctx_t* otp_ctx, const char* token,
const bool is_valid = true; // asserted above
const bool initial_confirmation = false;
const bool show_delete_btn = false;
const bool retain = show_otp_details_activity(otp_ctx, initial_confirmation, is_valid, show_delete_btn);
const bool retain = show_otp_detail_options_activity(otp_ctx, initial_confirmation, is_valid, show_delete_btn);
JADE_ASSERT(retain); // should be no 'discard' option
} else if (ev_id == BTN_OTP_DISCARD_DELETE) {
if (confirm_only || delete_otp_record(otp_ctx->name))
Expand All @@ -1504,6 +1534,7 @@ static bool display_hotp_screen(const otpauth_ctx_t* otp_ctx, const char* token,
}
}


// TOTP token-code display updates with passage of time (unless flagged not to)
static bool display_totp_screen(otpauth_ctx_t* otp_ctx, uint64_t epoch_value, char* token, const size_t token_len,
const bool confirm_only, const bool auto_update)
Expand Down Expand Up @@ -1582,7 +1613,7 @@ static bool display_totp_screen(otpauth_ctx_t* otp_ctx, uint64_t epoch_value, ch
const bool is_valid = true; // asserted above
const bool initial_confirmation = false;
const bool show_delete_btn = false;
const bool retain = show_otp_details_activity(otp_ctx, initial_confirmation, is_valid, show_delete_btn);
const bool retain = show_otp_detail_options_activity(otp_ctx, initial_confirmation, is_valid, show_delete_btn);
JADE_ASSERT(retain); // should be no 'discard' option
} else if (ev_id == BTN_OTP_DISCARD_DELETE) {
if (confirm_only || delete_otp_record(otp_ctx->name))
Expand Down Expand Up @@ -1703,7 +1734,7 @@ static void handle_view_otps(void)
JADE_LOGE("Error loading or executing otp record: %s", names[selected]);
const bool initial_confirmation = false;
const bool show_delete_btn = true;
if (!show_otp_details_activity(&otp_ctx, initial_confirmation, is_valid, show_delete_btn)) {
if (!show_otp_detail_options_activity(&otp_ctx, initial_confirmation, is_valid, show_delete_btn)) {
// Delete invalid record
delete_otp_record(otp_ctx.name);
}
Expand Down
90 changes: 90 additions & 0 deletions main/qrmode.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
#define ACCOUNT_INDEX_MAX 65536
#define ACCOUNT_INDEX_FLAGS_SHIFT 16

#define MAX_OTP_SCREENS 1
#define OTP_TEXTSPLITLEN 4
#define OTP_GRID_TOPPAD 4
#define OTP_GRID_X 4
#define OTP_GRID_Y 6
#define OTP_GRID_SIZE (OTP_GRID_X * OTP_GRID_Y)
// When we are displaying a BCUR QR code we ensure the timeout is at least this value
// as we don't want the unit to shut down because of apparent inactivity.
#define BCUR_QR_DISPLAY_MIN_TIMEOUT_SECS 300
Expand All @@ -61,6 +67,8 @@ gui_activity_t* make_show_xpub_qr_activity(
gui_activity_t* make_xpub_qr_options_activity(
gui_view_node_t** script_textbox, gui_view_node_t** wallet_textbox, gui_view_node_t** density_textbox);

gui_activity_t* make_show_otp_qr_actvity(const char* otp_name, Icon* qr_icon);

gui_activity_t* make_search_verify_address_activity(
const char* root_label, gui_view_node_t** label_text, progress_bar_t* progress_bar, gui_view_node_t** index_text);
gui_activity_t* make_search_address_options_activity(
Expand All @@ -80,6 +88,8 @@ int sign_message_file(
int get_bip85_bip39_entropy_cbor(const CborValue* params, CborEncoder* output, const char** errmsg);

bool show_confirm_address_activity(const char* address, bool default_selection);
static void split_text(const char* src, const size_t len, const size_t wordlen, char* output, const size_t output_len,
size_t* num_words, size_t* written);

bool handle_mnemonic_qr(const char* mnemonic);

Expand Down Expand Up @@ -843,6 +853,7 @@ static bool verify_address(const address_data_t* const addr_data)
return verified;
}


// Handle QR Options dialog - ie. QR size and frame-rate
static bool handle_qr_options(uint32_t* qr_flags)
{
Expand Down Expand Up @@ -1489,6 +1500,85 @@ static void add_cr_after_last_slash(const char* url, char* output, const size_t
strcpy(output + index + 2, url + index + 1);
}

bool show_otp_secret_text_activity(const otpauth_ctx_t* otp_ctx)
{
JADE_ASSERT(otp_ctx);

size_t num_words = 0;
size_t words_len = 0;
char secret_display[256];

split_text(otp_ctx->secret, otp_ctx->secret_len, OTP_TEXTSPLITLEN, secret_display, sizeof(secret_display), &num_words, &words_len);
JADE_ASSERT(num_words <= MAX_OTP_SCREENS * OTP_GRID_SIZE);
JADE_ASSERT(words_len <= sizeof(secret_display));

btn_data_t hdrbtns[] = {
{ .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_BACK },
{ .txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } };

const char* remaining_words = NULL;
gui_activity_t* const act = make_text_grid_activity("Secret Key", hdrbtns, 2, OTP_GRID_TOPPAD, OTP_GRID_X, OTP_GRID_Y, secret_display, num_words, GUI_DEFAULT_FONT, &remaining_words);
JADE_ASSERT(remaining_words == secret_display + words_len);
gui_set_current_activity(act);

int32_t ev_id;
while (gui_activity_wait_event(act, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0)) {
if (ev_id == BTN_BACK) {
break;
}
}

return true;
}

bool show_otp_uri_qr_activity(const otpauth_ctx_t* otp_ctx)
{
JADE_ASSERT(otp_ctx);
JADE_ASSERT(otp_ctx->name);

bool ok = false;
char uri[OTP_MAX_URI_LEN];
size_t written = 0;

SENSITIVE_PUSH(uri, sizeof(uri));

if (!otp_load_uri(otp_ctx->name, uri, sizeof(uri), &written) || !written) {
const char* msg[] = { "Failed to load", "OTP URI" };
await_error_activity(msg, 2);
goto cleanup;
}

if (written >= MAX_QR_V6_DATA_LEN) {
const char* msg[] = { "URI too long", "for QR" };
await_error_activity(msg, 2);
goto cleanup;
}

Icon* const qr_icon = JADE_MALLOC(sizeof(Icon));
bytes_to_qr_icon((const uint8_t*)uri, written, qr_icon);

gui_activity_t* const act = make_show_otp_qr_actvity(otp_ctx->name, qr_icon);
int32_t ev_id;

while(true){
gui_set_current_activity(act);
if (gui_activity_wait_event(act, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0)) {
if (ev_id == BTN_BACK) {
ok = true;
goto cleanup;
} else if (ev_id == BTN_OTP_DETAILS_SECRET) {
show_otp_secret_text_activity(otp_ctx);
} else if (ev_id == BTN_HELP) {
await_qr_help_activity("blkstrm.com/otp");
}
}
}
cleanup:
SENSITIVE_POP(uri);
return ok;
}


// Display screen with help url and qr code
// Handles up to v4 codes - ie. text up to 78 bytes
void await_qr_help_activity(const char* url)
Expand Down
1 change: 1 addition & 0 deletions main/qrmode.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ void await_qr_help_activity(const char* url);
bool await_qr_back_continue_activity(
const char* message[], size_t message_size, const char* url, bool default_selection);

bool show_otp_uri_qr_activity(const otpauth_ctx_t* otp_ctx);
// Start pinserver authentication via qr codes
void handle_qr_auth(bool suppress_pin_change_confirmation);

Expand Down
12 changes: 12 additions & 0 deletions main/ui/otpauth.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,18 @@ static gui_activity_t* make_otp_details_activities(const otpauth_ctx_t* ctx, con
return act;
}

gui_activity_t* make_view_export_otp_activity(const char* name) {

//TODO change BTN EVENTS
btn_data_t hdrbtns[] = { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_BACK},
{ .txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE }};

btn_data_t menubtns[] = { { .txt = "View", .font = GUI_DEFAULT_FONT, .ev_id = BTN_OTP_DETAILS_VIEW},
{ .txt = "Export", .font = GUI_DEFAULT_FONT, .ev_id = BTN_OTP_DETAILS_EXPORT}};

return make_menu_activity(name, hdrbtns, 2, menubtns, 2);
}

// otp details screen for viewing or confirmation
// returns true if we are to store/retain this record, false if we are to discard/delete the record
bool show_otp_details_activity(
Expand Down
53 changes: 53 additions & 0 deletions main/ui/qrmode.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,59 @@ gui_activity_t* make_qr_options_activity(gui_view_node_t** density_textbox, gui_
return make_menu_activity("QR Settings", hdrbtns, 2, menubtns, 2);
}

// NOTE: 'icons' passed in here must be heap-allocated as the gui element takes ownership
gui_activity_t* make_show_otp_qr_actvity(const char* otp_name, Icon* qr_icon) {

JADE_ASSERT(otp_name);
JADE_ASSERT(qr_icon);

gui_activity_t* const act = gui_make_activity();
gui_view_node_t* node;

gui_view_node_t* hsplit;
gui_make_hsplit(&hsplit, GUI_SPLIT_RELATIVE, 2, 44, 56);
gui_set_parent(hsplit, act->root_node);

// LHS
gui_view_node_t* vsplit;
gui_make_vsplit(&vsplit, GUI_SPLIT_RELATIVE, 4, 20, 30, 25, 25);
gui_set_parent(vsplit, hsplit);

// back button
btn_data_t hdrbtns[]
= { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_BACK, .borders = GUI_BORDER_ALL },
{ .txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE },
{ .txt = "?", .font = GUI_TITLE_FONT, .ev_id = BTN_HELP, .borders = GUI_BORDER_ALL } };
add_buttons(vsplit, UI_ROW, hdrbtns, 3); // 44 (hsplit) / 3 == 14 - almost 15 so ok

// second row, type label
gui_make_text(&node, otp_name, TFT_WHITE);
gui_set_parent(node, vsplit);
gui_set_align(node, GUI_ALIGN_LEFT, GUI_ALIGN_MIDDLE);

// third row, path
gui_make_text_font(&node, "Scan Secret Key", TFT_WHITE, DEFAULT_FONT); // fits path
gui_set_parent(node, vsplit);
gui_set_align(node, GUI_ALIGN_LEFT, GUI_ALIGN_TOP);

// button
btn_data_t ftrbtn
= { .txt = "View Secret", .font = GUI_DEFAULT_FONT, .ev_id = BTN_OTP_DETAILS_SECRET, .borders = GUI_BORDER_TOP };
add_buttons(vsplit, UI_COLUMN, &ftrbtn, 1);

// RHS - QR icons
gui_view_node_t* fill;
gui_make_fill(&fill, GUI_BLOCKSTREAM_QR_PALE);
gui_set_parent(fill, hsplit);

gui_make_icon(&node, qr_icon, TFT_BLACK, &GUI_BLOCKSTREAM_QR_PALE);
gui_set_align(node, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE);
gui_set_parent(node, fill);
gui_set_icon_animation(node, qr_icon, 1, 0);

return act;
}

// NOTE: 'icons' passed in here must be heap-allocated as the gui element takes ownership
gui_activity_t* make_show_qr_activity(const char* message[], const size_t message_size, Icon* icons,
const size_t num_icons, const size_t frames_per_qr_icon, const bool show_options_button, const bool show_help_btn)
Expand Down