From 6694e08c22c0bfd628aeb15b8c5fb1ea057f3de8 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Sun, 17 Aug 2025 15:57:21 +0000 Subject: [PATCH 1/8] added intermediate options screen for otp details --- components/libwally-core/upstream | 2 +- main/button_events.h | 2 ++ main/process/dashboard.c | 36 ++++++++++++++++++++++++++++--- main/ui/otpauth.c | 12 +++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/components/libwally-core/upstream b/components/libwally-core/upstream index 8fc41b9d..12ab3fa2 160000 --- a/components/libwally-core/upstream +++ b/components/libwally-core/upstream @@ -1 +1 @@ -Subproject commit 8fc41b9d2b152868953176176cff9890aa268c8d +Subproject commit 12ab3fa22cbdc26040a9900d02cdb03da5164c85 diff --git a/main/button_events.h b/main/button_events.h index 7a24058d..cf41f03c 100644 --- a/main/button_events.h +++ b/main/button_events.h @@ -232,6 +232,8 @@ typedef enum { BTN_OTP_ISSUER, BTN_OTP_TYPE, BTN_OTP_DETAILS, + BTN_OTP_DETAILS_VIEW, + BTN_OTP_DETAILS_EXPORT, BTN_OTP_RETAIN_CONFIRM, BTN_OTP_DISCARD_DELETE, diff --git a/main/process/dashboard.c b/main/process/dashboard.c index 9f47d917..3890e402 100644 --- a/main/process/dashboard.c +++ b/main/process/dashboard.c @@ -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); @@ -1465,6 +1467,33 @@ 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) { + return show_otp_details_activity(otp_ctx, initial_confirmation, is_valid, show_delete_btn); + } else if (ev_id == BTN_OTP_DETAILS_EXPORT) { + const char* msg[] = { "Export is not", "implemented yet" }; + await_message_activity(msg, 2); + } + } + } +} // HOTP token-code fixed static bool display_hotp_screen(const otpauth_ctx_t* otp_ctx, const char* token, const bool confirm_only) { @@ -1492,7 +1521,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)) @@ -1504,6 +1533,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) @@ -1582,7 +1612,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)) @@ -1703,7 +1733,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); } diff --git a/main/ui/otpauth.c b/main/ui/otpauth.c index 104c912b..4f4dc0bd 100644 --- a/main/ui/otpauth.c +++ b/main/ui/otpauth.c @@ -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_XPUB_OPTIONS_EXIT }, + { .txt = "?", .font = GUI_TITLE_FONT, .ev_id = BTN_XPUB_OPTIONS_HELP }}; + + 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( From 9dca167380f5507d8d27c902212f91341e45d6d1 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Mon, 18 Aug 2025 23:32:15 +0000 Subject: [PATCH 2/8] added qr for otp --- main/process/dashboard.c | 4 ++-- main/qrmode.c | 42 ++++++++++++++++++++++++++++++++ main/qrmode.h | 1 + main/ui/qrmode.c | 52 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 2 deletions(-) diff --git a/main/process/dashboard.c b/main/process/dashboard.c index 3890e402..9675f835 100644 --- a/main/process/dashboard.c +++ b/main/process/dashboard.c @@ -1467,6 +1467,7 @@ 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, @@ -1488,8 +1489,7 @@ static bool show_otp_detail_options_activity( } else if (ev_id == BTN_OTP_DETAILS_VIEW) { return show_otp_details_activity(otp_ctx, initial_confirmation, is_valid, show_delete_btn); } else if (ev_id == BTN_OTP_DETAILS_EXPORT) { - const char* msg[] = { "Export is not", "implemented yet" }; - await_message_activity(msg, 2); + return show_otp_uri_qr_activity(otp_ctx->name); } } } diff --git a/main/qrmode.c b/main/qrmode.c index 917d97ca..a61d6e8b 100644 --- a/main/qrmode.c +++ b/main/qrmode.c @@ -61,6 +61,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( @@ -843,6 +845,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) { @@ -1489,6 +1492,45 @@ 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_uri_qr_activity(const char* otp_name) { + JADE_ASSERT(otp_name); + + bool ok = false; + char uri[OTP_MAX_URI_LEN]; + size_t written = 0; + + SENSITIVE_PUSH(uri, sizeof(uri)); + + if (!otp_load_uri(otp_name, uri, sizeof(uri), &written) || !written) { + const char* msg[] = { "Failed to load", "OTP URI" }; + await_error_activity(msg, 2); + goto cleanup; + } + + Icon* const qr_icon = JADE_MALLOC(sizeof(Icon)); + + if (written >= MAX_QR_V6_DATA_LEN) { + const char* msg[] = { "URI too long", "for QR" }; + await_error_activity(msg, 2); + goto cleanup; + } + + bytes_to_qr_icon((const uint8_t*)uri, written, qr_icon); + + gui_activity_t* const act = make_show_otp_qr_actvity(otp_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)) { + continue; + } + } +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) diff --git a/main/qrmode.h b/main/qrmode.h index 936993b5..b6fc2374 100644 --- a/main/qrmode.h +++ b/main/qrmode.h @@ -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 char* otp_name); // Start pinserver authentication via qr codes void handle_qr_auth(bool suppress_pin_change_confirmation); diff --git a/main/ui/qrmode.c b/main/ui/qrmode.c index 04add7b6..620da42a 100644 --- a/main/ui/qrmode.c +++ b/main/ui/qrmode.c @@ -201,6 +201,58 @@ gui_activity_t* make_qr_options_activity(gui_view_node_t** density_textbox, gui_ return make_menu_activity("QR Settings", hdrbtns, 2, menubtns, 2); } +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_XPUB_EXIT, .borders = GUI_BORDER_ALL }, + { .txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE }, + { .txt = "?", .font = GUI_TITLE_FONT, .ev_id = BTN_XPUB_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 otp", 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 = "show secret", .font = GUI_DEFAULT_FONT, .ev_id = BTN_XPUB_OPTIONS, .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) From 66a819ce29f43a846f31ec8a4d834797818e8df3 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Thu, 28 Aug 2025 01:41:20 +0000 Subject: [PATCH 3/8] created secret display for otp --- main/button_events.h | 1 + main/process/dashboard.c | 2 +- main/qrmode.c | 48 +++++++++++++++++++++++++++++++++++----- main/qrmode.h | 2 +- main/ui/qrmode.c | 40 ++++++++++++++++++++++++++++++--- 5 files changed, 82 insertions(+), 11 deletions(-) diff --git a/main/button_events.h b/main/button_events.h index cf41f03c..c477d8ad 100644 --- a/main/button_events.h +++ b/main/button_events.h @@ -234,6 +234,7 @@ typedef enum { BTN_OTP_DETAILS, BTN_OTP_DETAILS_VIEW, BTN_OTP_DETAILS_EXPORT, + BTN_OTP_DETAILS_SECRET, BTN_OTP_RETAIN_CONFIRM, BTN_OTP_DISCARD_DELETE, diff --git a/main/process/dashboard.c b/main/process/dashboard.c index 9675f835..17f9fc5b 100644 --- a/main/process/dashboard.c +++ b/main/process/dashboard.c @@ -1489,7 +1489,7 @@ static bool show_otp_detail_options_activity( } else if (ev_id == BTN_OTP_DETAILS_VIEW) { return show_otp_details_activity(otp_ctx, initial_confirmation, is_valid, show_delete_btn); } else if (ev_id == BTN_OTP_DETAILS_EXPORT) { - return show_otp_uri_qr_activity(otp_ctx->name); + return show_otp_uri_qr_activity(otp_ctx); } } } diff --git a/main/qrmode.c b/main/qrmode.c index a61d6e8b..74323cfd 100644 --- a/main/qrmode.c +++ b/main/qrmode.c @@ -62,6 +62,7 @@ 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_show_otp_secret_text_activity(const char* otp_name, const char* secret_text); 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); @@ -1492,8 +1493,37 @@ 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_uri_qr_activity(const char* otp_name) { - JADE_ASSERT(otp_name); +bool show_otp_secret_text_activity(const otpauth_ctx_t* otp_ctx) +{ + JADE_ASSERT(otp_ctx); + + // Convert secret from base32 to readable format (or just show as-is) + char secret_display[256]; + size_t secret_len = otp_ctx->secret_len; + if (secret_len >= sizeof(secret_display)) { + secret_len = sizeof(secret_display) - 1; + } + + memcpy(secret_display, otp_ctx->secret, secret_len); + secret_display[secret_len] = '\0'; + + gui_activity_t* const act = make_show_otp_secret_text_activity(otp_ctx->name, secret_display); + 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]; @@ -1501,7 +1531,7 @@ bool show_otp_uri_qr_activity(const char* otp_name) { SENSITIVE_PUSH(uri, sizeof(uri)); - if (!otp_load_uri(otp_name, uri, sizeof(uri), &written) || !written) { + 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; @@ -1517,13 +1547,18 @@ bool show_otp_uri_qr_activity(const char* otp_name) { bytes_to_qr_icon((const uint8_t*)uri, written, qr_icon); - gui_activity_t* const act = make_show_otp_qr_actvity(otp_name, 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)) { - continue; + 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); + } } } cleanup: @@ -1531,6 +1566,7 @@ bool show_otp_uri_qr_activity(const char* otp_name) { 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) diff --git a/main/qrmode.h b/main/qrmode.h index b6fc2374..26633edb 100644 --- a/main/qrmode.h +++ b/main/qrmode.h @@ -28,7 +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 char* otp_name); +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); diff --git a/main/ui/qrmode.c b/main/ui/qrmode.c index 620da42a..ca5b2505 100644 --- a/main/ui/qrmode.c +++ b/main/ui/qrmode.c @@ -201,6 +201,7 @@ 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); @@ -220,9 +221,9 @@ gui_activity_t* make_show_otp_qr_actvity(const char* otp_name, Icon* qr_icon) { // back button btn_data_t hdrbtns[] - = { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_XPUB_EXIT, .borders = GUI_BORDER_ALL }, + = { { .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_XPUB_HELP, .borders = GUI_BORDER_ALL } }; + { .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 @@ -237,7 +238,7 @@ gui_activity_t* make_show_otp_qr_actvity(const char* otp_name, Icon* qr_icon) { // button btn_data_t ftrbtn - = { .txt = "show secret", .font = GUI_DEFAULT_FONT, .ev_id = BTN_XPUB_OPTIONS, .borders = GUI_BORDER_TOP }; + = { .txt = "show 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 @@ -253,6 +254,39 @@ gui_activity_t* make_show_otp_qr_actvity(const char* otp_name, Icon* qr_icon) { return act; } +gui_activity_t* make_show_otp_secret_text_activity(const char* otp_name, const char* secret_text) +{ + JADE_ASSERT(otp_name); + JADE_ASSERT(secret_text); + + gui_activity_t* const act = gui_make_activity(); + gui_view_node_t* node; + + 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 } }; + gui_view_node_t* const parent = add_title_bar(act, "Secret Key", hdrbtns, 2, NULL); + + gui_view_node_t* vsplit; + gui_make_vsplit(&vsplit, GUI_SPLIT_RELATIVE, 3, 25, 50, 25); + gui_set_parent(vsplit, parent); + + gui_make_text(&node, otp_name, TFT_WHITE); + gui_set_parent(node, vsplit); + gui_set_align(node, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); + + gui_make_text_font(&node, secret_text, TFT_WHITE, DEFAULT_FONT); + gui_set_parent(node, vsplit); + gui_set_align(node, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); + + btn_data_t ftrbtn = { + .txt = "Back", .font = GUI_DEFAULT_FONT, .ev_id = BTN_BACK + }; + add_buttons(vsplit, UI_ROW, &ftrbtn, 1); + + 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) From 2390bb9c9cdfcf96fe27089a07f92ba0b4387975 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Mon, 1 Sep 2025 14:40:14 +0000 Subject: [PATCH 4/8] Cleaned up ui --- main/process/dashboard.c | 5 +++-- main/ui/otpauth.c | 4 ++-- main/ui/qrmode.c | 13 ++----------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/main/process/dashboard.c b/main/process/dashboard.c index 17f9fc5b..83a0d70d 100644 --- a/main/process/dashboard.c +++ b/main/process/dashboard.c @@ -1487,12 +1487,13 @@ static bool show_otp_detail_options_activity( if (ev_id == BTN_BACK) { return true; } else if (ev_id == BTN_OTP_DETAILS_VIEW) { - return show_otp_details_activity(otp_ctx, initial_confirmation, is_valid, show_delete_btn); + show_otp_details_activity(otp_ctx, initial_confirmation, is_valid, show_delete_btn); } else if (ev_id == BTN_OTP_DETAILS_EXPORT) { - return show_otp_uri_qr_activity(otp_ctx); + 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) diff --git a/main/ui/otpauth.c b/main/ui/otpauth.c index 4f4dc0bd..c7d0fe85 100644 --- a/main/ui/otpauth.c +++ b/main/ui/otpauth.c @@ -167,8 +167,8 @@ static gui_activity_t* make_otp_details_activities(const otpauth_ctx_t* ctx, con 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_XPUB_OPTIONS_EXIT }, - { .txt = "?", .font = GUI_TITLE_FONT, .ev_id = BTN_XPUB_OPTIONS_HELP }}; + 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}}; diff --git a/main/ui/qrmode.c b/main/ui/qrmode.c index ca5b2505..a893b250 100644 --- a/main/ui/qrmode.c +++ b/main/ui/qrmode.c @@ -232,13 +232,13 @@ gui_activity_t* make_show_otp_qr_actvity(const char* otp_name, Icon* qr_icon) { gui_set_align(node, GUI_ALIGN_LEFT, GUI_ALIGN_MIDDLE); // third row, path - gui_make_text_font(&node, "scan otp", TFT_WHITE, DEFAULT_FONT); // fits 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 = "show secret", .font = GUI_DEFAULT_FONT, .ev_id = BTN_OTP_DETAILS_SECRET, .borders = GUI_BORDER_TOP }; + = { .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 @@ -271,19 +271,10 @@ gui_activity_t* make_show_otp_secret_text_activity(const char* otp_name, const c gui_make_vsplit(&vsplit, GUI_SPLIT_RELATIVE, 3, 25, 50, 25); gui_set_parent(vsplit, parent); - gui_make_text(&node, otp_name, TFT_WHITE); - gui_set_parent(node, vsplit); - gui_set_align(node, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); - gui_make_text_font(&node, secret_text, TFT_WHITE, DEFAULT_FONT); gui_set_parent(node, vsplit); gui_set_align(node, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); - btn_data_t ftrbtn = { - .txt = "Back", .font = GUI_DEFAULT_FONT, .ev_id = BTN_BACK - }; - add_buttons(vsplit, UI_ROW, &ftrbtn, 1); - return act; } From 65b96bb9b0a6aa05ec17638ba1ef9653eff45339 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Mon, 1 Sep 2025 18:14:19 +0000 Subject: [PATCH 5/8] added secret key viewer similar to address confirm --- main/qrmode.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/main/qrmode.c b/main/qrmode.c index 74323cfd..c579ec39 100644 --- a/main/qrmode.c +++ b/main/qrmode.c @@ -83,6 +83,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); @@ -1497,17 +1499,18 @@ bool show_otp_secret_text_activity(const otpauth_ctx_t* otp_ctx) { JADE_ASSERT(otp_ctx); - // Convert secret from base32 to readable format (or just show as-is) + size_t num_words = 0; + size_t words_len = 0; char secret_display[256]; - size_t secret_len = otp_ctx->secret_len; - if (secret_len >= sizeof(secret_display)) { - secret_len = sizeof(secret_display) - 1; - } - - memcpy(secret_display, otp_ctx->secret, secret_len); - secret_display[secret_len] = '\0'; - gui_activity_t* const act = make_show_otp_secret_text_activity(otp_ctx->name, secret_display); + split_text(otp_ctx->secret, otp_ctx->secret_len, 4, secret_display, sizeof(secret_display), &num_words, &words_len); + + 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, 4, 4, 6, secret_display, num_words, GUI_DEFAULT_FONT, &remaining_words); gui_set_current_activity(act); int32_t ev_id; From fb9dc485032b3db33bafe0315e378efec315ff3f Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Mon, 1 Sep 2025 18:24:22 +0000 Subject: [PATCH 6/8] cleaned up qrmode ui added otp defines --- main/qrmode.c | 9 ++++++--- main/ui/qrmode.c | 24 ------------------------ 2 files changed, 6 insertions(+), 27 deletions(-) diff --git a/main/qrmode.c b/main/qrmode.c index c579ec39..417a8257 100644 --- a/main/qrmode.c +++ b/main/qrmode.c @@ -36,6 +36,10 @@ #define ACCOUNT_INDEX_MAX 65536 #define ACCOUNT_INDEX_FLAGS_SHIFT 16 +#define OTP_TEXTSPLITLEN 4 +#define OTP_GRID_TOPPAD 4 +#define OTP_GRID_X 4 +#define OTP_GRID_Y 6 // 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 @@ -62,7 +66,6 @@ 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_show_otp_secret_text_activity(const char* otp_name, const char* secret_text); 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); @@ -1503,14 +1506,14 @@ bool show_otp_secret_text_activity(const otpauth_ctx_t* otp_ctx) size_t words_len = 0; char secret_display[256]; - split_text(otp_ctx->secret, otp_ctx->secret_len, 4, secret_display, sizeof(secret_display), &num_words, &words_len); + split_text(otp_ctx->secret, otp_ctx->secret_len, OTP_TEXTSPLITLEN, secret_display, sizeof(secret_display), &num_words, &words_len); 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, 4, 4, 6, secret_display, num_words, GUI_DEFAULT_FONT, &remaining_words); + 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); gui_set_current_activity(act); int32_t ev_id; diff --git a/main/ui/qrmode.c b/main/ui/qrmode.c index a893b250..236aa251 100644 --- a/main/ui/qrmode.c +++ b/main/ui/qrmode.c @@ -254,30 +254,6 @@ gui_activity_t* make_show_otp_qr_actvity(const char* otp_name, Icon* qr_icon) { return act; } -gui_activity_t* make_show_otp_secret_text_activity(const char* otp_name, const char* secret_text) -{ - JADE_ASSERT(otp_name); - JADE_ASSERT(secret_text); - - gui_activity_t* const act = gui_make_activity(); - gui_view_node_t* node; - - 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 } }; - gui_view_node_t* const parent = add_title_bar(act, "Secret Key", hdrbtns, 2, NULL); - - gui_view_node_t* vsplit; - gui_make_vsplit(&vsplit, GUI_SPLIT_RELATIVE, 3, 25, 50, 25); - gui_set_parent(vsplit, parent); - - gui_make_text_font(&node, secret_text, TFT_WHITE, DEFAULT_FONT); - gui_set_parent(node, vsplit); - gui_set_align(node, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); - - 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) From 18636e5bbb347463b48facbb2363fa9f12de4194 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Mon, 1 Sep 2025 18:37:41 +0000 Subject: [PATCH 7/8] added assertion checks --- main/qrmode.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main/qrmode.c b/main/qrmode.c index 417a8257..5d03fb08 100644 --- a/main/qrmode.c +++ b/main/qrmode.c @@ -36,10 +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 @@ -1507,6 +1509,8 @@ bool show_otp_secret_text_activity(const otpauth_ctx_t* otp_ctx) 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 }, @@ -1514,6 +1518,7 @@ bool show_otp_secret_text_activity(const otpauth_ctx_t* otp_ctx) 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; From ea1d501a62de16eee3ec6f1e2c2239792c7c6cd7 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Thu, 4 Sep 2025 16:17:54 +0000 Subject: [PATCH 8/8] fixed possible memory leak --- main/qrmode.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main/qrmode.c b/main/qrmode.c index 5d03fb08..f67c3b22 100644 --- a/main/qrmode.c +++ b/main/qrmode.c @@ -1548,14 +1548,13 @@ bool show_otp_uri_qr_activity(const otpauth_ctx_t* otp_ctx) goto cleanup; } - Icon* const qr_icon = JADE_MALLOC(sizeof(Icon)); - 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); @@ -1569,6 +1568,8 @@ bool show_otp_uri_qr_activity(const otpauth_ctx_t* otp_ctx) 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"); } } }