Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Functionality for Exporting BIP85 Mnemonic as QR Code #192

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
69 changes: 69 additions & 0 deletions main/process/mnemonic.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ gui_activity_t* make_calculate_final_word_activity(void);
gui_activity_t* make_confirm_passphrase_activity(const char* passphrase, gui_view_node_t** textbox);

gui_activity_t* make_export_qr_overview_activity(const Icon* icon, bool initial);
gui_activity_t* make_bip85_qr_overview_activity(const Icon* icon, bool initial);
gui_activity_t* make_export_qr_fragment_activity(
const Icon* icon, gui_view_node_t** icon_node, gui_view_node_t** label_node);

Expand Down Expand Up @@ -1476,6 +1477,73 @@ void get_bip85_mnemonic(const uint32_t nwords, const uint32_t index, char** new_
SENSITIVE_POP(entropy);
}


// Export a BIP85-derived mnemonic as a QR code overview
// Returns true if completed/skipped, false if backed out
static bool mnemonic_export_qr_bip85(const char* mnemonic)
{
JADE_ASSERT(mnemonic);

// Ask user if they want to view the QR
const char* question[] = { "View BIP85", "CompactSeedQR" };
if (!await_skipyes_activity(NULL, question, 2, true, "blkstrm.com/bip85")) {
return true;
}

// Convert mnemonic to entropy bytes
size_t entropy_len = 0;
uint8_t entropy[BIP32_ENTROPY_LEN_256]; // Sufficient for 12 and 24 words
JADE_WALLY_VERIFY(bip39_mnemonic_to_bytes(NULL, mnemonic, entropy, sizeof(entropy), &entropy_len));
JADE_ASSERT(entropy_len == BIP32_ENTROPY_LEN_128 || entropy_len == BIP32_ENTROPY_LEN_256);

// Create QR code from entropy
QRCode qrcode;
const uint8_t qrcode_version = entropy_len == BIP32_ENTROPY_LEN_128 ? 1 : 2;
uint8_t qrbuffer[96];
JADE_ASSERT(sizeof(qrbuffer) > qrcode_getBufferSize(qrcode_version));
const int qret = qrcode_initBytes(&qrcode, qrbuffer, qrcode_version, ECC_LOW, entropy, entropy_len);
JADE_ASSERT(qret == 0);

// Select scale based on display size
#if CONFIG_DISPLAY_WIDTH >= 480 && CONFIG_DISPLAY_HEIGHT >= 220
const uint8_t overview_scale = qrcode_version == 1 ? 9 : 8;
#elif CONFIG_DISPLAY_WIDTH >= 320 && CONFIG_DISPLAY_HEIGHT >= 170
const uint8_t overview_scale = qrcode_version == 1 ? 7 : 6;
#else
const uint8_t overview_scale = qrcode_version == 1 ? 5 : 4;
#endif

// Create overview QR icon
Icon qr_overview;
qrcode_toIcon(&qrcode, &qr_overview, overview_scale);

// Show the QR overview and wait for user interaction
bool retval = true;
gui_activity_t* const act_overview_qr = make_bip85_qr_overview_activity(&qr_overview, true);
gui_set_current_activity_ex(act_overview_qr, true);

int32_t ev_id;
while (true) {
if (gui_activity_wait_event(act_overview_qr, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0)) {
switch (ev_id) {
case BTN_MNEMONIC_EXIT: // Use the same exit button as display_confirm_mnemonic
// User wants to exit/abandon
retval = false;
goto cleanup;

default:
// Ignore other buttons
continue;
}
}
}


cleanup:
qrcode_freeIcon(&qr_overview);
return retval;
}

// Offer the user the option to generate a bip39 recovery phrase using entropy
// calculated as per bip85. User provides number of words and also path index.
// NOTE: only the English wordlist is supported.
Expand Down Expand Up @@ -1559,6 +1627,7 @@ void handle_bip85_mnemonic()
JADE_ASSERT(mnemonic_len < MNEMONIC_BUFLEN);
SENSITIVE_PUSH(new_mnemonic, mnemonic_len);

mnemonic_export_qr_bip85(new_mnemonic);
// Display and confirm mnemonic phrase
if (display_confirm_mnemonic(nwords, new_mnemonic, mnemonic_len)) {
const char* message[] = { "Recovery Phrase", "Confirmed" };
Expand Down
45 changes: 45 additions & 0 deletions main/ui/mnemonic.c
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,51 @@ gui_activity_t* make_confirm_passphrase_activity(const char* passphrase, gui_vie
return act;
}

gui_activity_t* make_bip85_qr_overview_activity(const Icon* icon, const bool initial)
{
JADE_ASSERT(icon);

gui_activity_t* const act = gui_make_activity();

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

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

// rhs - icon
gui_view_node_t* icon_bg;
gui_make_fill(&icon_bg, TFT_DARKGREY);
gui_set_parent(icon_bg, hsplit);

gui_view_node_t* node;
gui_make_icon(&node, icon, TFT_BLACK, &TFT_LIGHTGREY);
gui_set_align(node, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE);
gui_set_parent(node, icon_bg);

// First row, header with back/next buttons
btn_data_t hdrbtns[] = {
{ .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_MNEMONIC_EXIT, .borders = GUI_BORDER_ALL }
// { .txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE }, // spacer
// { .txt = ">", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_QR_EXPORT_NEXT, .borders = GUI_BORDER_ALL }
};

add_buttons(vsplit, UI_ROW, hdrbtns, 1);

// Message
gui_make_text(&node, "BIP85", TFT_WHITE);
gui_set_parent(node, vsplit);
gui_set_align(node, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE);

// Select next button by default
gui_set_activity_initial_selection(act, hdrbtns[0].btn);

return act;
}

gui_activity_t* make_export_qr_overview_activity(const Icon* icon, const bool initial)
{
JADE_ASSERT(icon);
Expand Down