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..e433ca06 100644 --- a/main/button_events.h +++ b/main/button_events.h @@ -171,7 +171,11 @@ typedef enum { BTN_SETTINGS_USBSTORAGE_BACK, BTN_SETTINGS_USBSTORAGE_HELP, BTN_SETTINGS_USBSTORAGE_SIGN, - BTN_SETTINGS_USBSTORAGE_EXPORT, + BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB, + BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS, + BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION, + BTN_SETTINGS_EXPORT_XPUB_BACK, + BTN_SETTINGS_EXPORT_XPUB_EXIT, BTN_SETTINGS_USBSTORAGE_EXIT, #endif BTN_SETTINGS_BLE, diff --git a/main/process/dashboard.c b/main/process/dashboard.c index 9f47d917..708bdce6 100644 --- a/main/process/dashboard.c +++ b/main/process/dashboard.c @@ -2382,9 +2382,11 @@ static void handle_settings(const bool startup_menu) act = make_usbstorage_settings_activity(keychain_get()); break; - case BTN_SETTINGS_USBSTORAGE_EXPORT: - // FIXME: implement - break; + case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB: + JADE_ASSERT(keychain_get()); + usbstorage_export_xpub(NULL); + act = make_usbstorage_settings_activity(keychain_get()); + break; #endif case BTN_SETTINGS_OTP_VIEW: handle_view_otps(); diff --git a/main/ui/dashboard.c b/main/ui/dashboard.c index ae4dc9c8..c66e17fd 100644 --- a/main/ui/dashboard.c +++ b/main/ui/dashboard.c @@ -307,7 +307,9 @@ gui_activity_t* make_locked_settings_activity(void) btn_data_t menubtns[] = { { .txt = "BIP39 Passphrase", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_BIP39_PASSPHRASE }, { .txt = "Device", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_DEVICE }, - { .txt = "Temporary Signer", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_TEMPORARY_WALLET_LOGIN } }; + { .txt = "Temporary Signer", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_TEMPORARY_WALLET_LOGIN }, + { .txt = "USB Storage", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE }, + }; return make_menu_activity("Options", hdrbtns, 2, menubtns, sizeof(menubtns) / sizeof(btn_data_t)); } @@ -348,11 +350,12 @@ gui_activity_t* make_usbstorage_settings_activity(const bool unlocked) btn_data_t menubtns[] = { { .txt = "Firmware Upgrade", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_FW }, { .txt = "Sign", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_SIGN }, - //{ .txt = "Export", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT }, + { .txt = "Export Xpub", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB }, }; - return make_menu_activity("USB Storage", hdrbtns, 2, menubtns, unlocked ? 2 : 1); + return make_menu_activity("USB Storage", hdrbtns, 2, menubtns, unlocked ? 3: 1); } + #endif gui_activity_t* make_device_settings_activity(void) diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index a0449500..0365997e 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -897,9 +897,156 @@ static bool sign_usb_psbt(const usbstorage_action_context_t* ctx) // After any signatures are added, the file is written in the same format. bool usbstorage_sign_psbt(const char* extra_path) { - // extra_path is optional const bool is_async = false; const usbstorage_action_context_t ctx = { .extra_path = extra_path }; return handle_usbstorage_action("Sign PSBT", sign_usb_psbt, &ctx, is_async); } + +static gui_activity_t* make_export_xpub_prompt_activity(void) { + const char* message[] = { + "Save wallet details to", + "connected storage device?" + }; + + btn_data_t hdr[] = { + {.txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_EXPORT_XPUB_BACK}, + {.txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } + }; + + btn_data_t ftrbtns[] = { + { .txt = "Options", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS, .borders = GUI_BORDER_TOPRIGHT }, + { .txt = "Export", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION, .borders = GUI_BORDER_TOPLEFT } + }; + return make_show_message_activity( + message, 2, + "Export Xpub", + hdr, 2, + ftrbtns, 2 + ); +} + +static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { + + bool ok = false; + char *fphex = NULL; + char *xpub = NULL; + uint32_t qr_flags = storage_get_qr_flags(); + + while (true) { + gui_activity_t* prompt = make_export_xpub_prompt_activity(); + gui_set_current_activity(prompt); + + int32_t ev; + gui_activity_wait_event(prompt, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev, NULL, 0); + + if (ev == BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS) { + handle_xpub_options(&qr_flags); + } + else if (ev == BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION) { + break; + } + else if (ev == BTN_SETTINGS_EXPORT_XPUB_BACK) { + return true; + } + else { + return false; + } + + } + const script_variant_t script_variant = xpub_script_variant_from_flags(qr_flags); + const uint16_t account_index = qr_flags >> ACCOUNT_INDEX_FLAGS_SHIFT; + + const char *prefix; + + switch (script_variant) { + case P2PKH: prefix = "pkh"; break; + case P2WPKH_P2SH: prefix = "sh(wpkh"; break; + case P2WPKH: prefix = "wpkh"; break; + case P2TR: prefix = "tr"; break; + case MULTI_P2SH: prefix = "sh(multi"; break; + case MULTI_P2WSH_P2SH: prefix = "sh(wsh(multi"; break; + case MULTI_P2WSH: prefix = "wsh(multi"; break; + default: prefix = "unknown"; break; + } + + uint32_t path[EXPORT_XPUB_PATH_LEN]; + size_t path_len = 0; + wallet_get_default_xpub_export_path(script_variant, + account_index, + path, + EXPORT_XPUB_PATH_LEN, + &path_len); + + network_t network_id; + if (keychain_get_network_type_restriction() == NETWORK_TYPE_TEST) { + network_id = NETWORK_BITCOIN_TESTNET; + } else { + network_id = NETWORK_BITCOIN; + } + + if (!wallet_get_xpub(network_id, path, path_len, &xpub) || xpub == NULL) { + const char* msg[] = { "unable to get", "xpub from path" }; + await_error_activity(msg, 2); + goto cleanup; + } + + char pathstr[MAX_PATH_STR_LEN(EXPORT_XPUB_PATH_LEN)]; + const bool ret = wallet_bip32_path_as_str(path, path_len, pathstr, sizeof(pathstr)); + + JADE_ASSERT(ret); + + uint8_t user_fingerprint[BIP32_KEY_FINGERPRINT_LEN]; + wallet_get_fingerprint(user_fingerprint, sizeof(user_fingerprint)); + + JADE_WALLY_VERIFY(wally_hex_from_bytes(user_fingerprint, sizeof(user_fingerprint), &fphex)); + if (!fphex) { + const char* msg[] = { "unable to get", "fingerprint from wallet" }; + await_error_activity(msg, 2); + goto cleanup; + }; + + map_string(fphex, tolower); + + char descriptor[512]; + int n = snprintf(descriptor, sizeof(descriptor), + "%s([%s/%s]%s)%s", + prefix, + fphex, + pathstr + 2, /* drop leading "m/" */ + xpub, + prefix[0]=='s' && strchr(prefix,'(') ? ")" : ""); + JADE_ASSERT(n > 0 && n < (int)sizeof(descriptor)); + char outpath[MAX_FILENAME_SIZE]; + int len = snprintf(outpath, sizeof(outpath), + "%s/jade-xpub.txt", USBSTORAGE_MOUNT_POINT); + JADE_ASSERT(len > 0 && len < sizeof(outpath)); + size_t written = write_buffer_to_file(outpath, + (const uint8_t*)descriptor, + (size_t)n); + + if (written != (size_t)n) { + const char* msg[] = { "Failed to save", "xpub file" }; + await_error_activity(msg, 2); + goto cleanup; + } + + size_t mountlen = strlen(USBSTORAGE_MOUNT_POINT); + const char* msg[] = { + "Wrote xpub to:", + outpath + mountlen + 1 + }; + await_message_activity(msg, 2); + ok = true; +cleanup: + if (xpub) wally_free_string(xpub); + if (fphex) wally_free_string(fphex); + return ok; +} + +bool usbstorage_export_xpub(const char* extra_path) { + const bool is_async = false; + usbstorage_action_context_t ctx = { .extra_path = NULL, .ctx = NULL}; + return handle_usbstorage_action("Export Xpub", export_usb_xpub_fn, &ctx, is_async); +} + #endif // AMALGAMATED_BUILD diff --git a/main/usbhmsc/usbmode.h b/main/usbhmsc/usbmode.h index 1c0bae9a..4c1f3382 100644 --- a/main/usbhmsc/usbmode.h +++ b/main/usbhmsc/usbmode.h @@ -8,5 +8,8 @@ bool usbstorage_firmware_ota(const char* extra_path); // Sign PSBT file, and write updated file bool usbstorage_sign_psbt(const char* extra_path); +// Write xpub file to usb +bool usbstorage_export_xpub(const char* extra_path); + #endif /* USBMODE_H_ */