From 72f5701367889ed195df23dc0b930cae466a57d0 Mon Sep 17 00:00:00 2001 From: Aido Date: Sun, 18 Aug 2024 23:16:57 +0100 Subject: [PATCH] Add Stax layouts for checking SSKR shares --- CHANGELOG.md | 5 +- TODO.md | 5 +- src/nano/nanos_enter_phrase.c | 14 +- src/nano/nanox_enter_phrase.c | 14 +- src/stax/bip39_mnemonic.c | 25 +- src/stax/bip39_mnemonic.h | 5 + src/stax/sskr_shares.c | 112 +++++ src/stax/sskr_shares.h | 35 +- src/stax/ui_stax.c | 449 ++++++++++-------- src/ux_common/common_sskr.h | 28 +- src/ux_common/onboarding_seed_bip39.c | 3 - src/ux_common/onboarding_seed_rom_variables.h | 7 +- src/ux_common/onboarding_seed_sskr.c | 226 +++++---- tests/deprecated/README.md | 5 + tests/deprecated/stax-sskr-128bit.test | 234 +++++++++ tests/unit/tests/roundtrip.c | 16 +- 16 files changed, 870 insertions(+), 313 deletions(-) create mode 100755 tests/deprecated/stax-sskr-128bit.test diff --git a/CHANGELOG.md b/CHANGELOG.md index f4180406..385da3b1 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,15 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.8.0] - 2024-07-29 +## [1.8.0] - 2024-08-18 ### Added - Add Stax layouts for generating SSKR shares +- Add Stax layouts for checking SSKR shares +- Add Stax layouts for recovering BIP39 phrase ## [1.7.4] - 2024-06-20 ### Fixed + - Ensure result does not overlap with operands in calls to `cx_bn_gf2_n_mul()` - Give a warning if a user chooses 1-of-m shares when m > 1 - Use CBOR tag for version 2 `sskr` diff --git a/TODO.md b/TODO.md index ed45b0fa..9df5dd55 100755 --- a/TODO.md +++ b/TODO.md @@ -6,13 +6,14 @@ - [ ] Improve the efficiency of the custom cx_bn_gf2_n_mul() function used for Nano S devices - [ ] Update automated function tests to test on nanox and nanosp - [ ] There is just enough memory available on Nano S to hold the phrases for 10 shares. Maybe just store SSKR Bytewords as shorter two letter minimal Bytewords rather than a 4 letter Byteword plus space for each share. Convert minimal ByteWords back to four letter Bytewords just prior to display. +- [ ] Add Ledger Flex to list of devices app works on ### In Progress - [ ] Add Ledger Stax to list of devices app works on - [x] Add SSKR Generate option to Stax - - [ ] Add SSKR Check option to Stax - - [ ] Write SSKR to BIP39 functionality + - [x] Add SSKR Check option to Stax + - [x] Write SSKR to BIP39 functionality - [ ] Functional Test with 29-word SSKR shares - [ ] Functional Test with 46-word SSKR shares diff --git a/src/nano/nanos_enter_phrase.c b/src/nano/nanos_enter_phrase.c index ef7316d3..b545c525 100644 --- a/src/nano/nanos_enter_phrase.c +++ b/src/nano/nanos_enter_phrase.c @@ -423,12 +423,12 @@ void compare_recovery_phrase(void) { buffer); } else if (G_bolos_ux_context.onboarding_type == ONBOARDING_TYPE_SSKR) { G_bolos_ux_context.words_buffer_length = sizeof(G_bolos_ux_context.words_buffer); - bolos_ux_sskr_hex_to_seed((unsigned char*) G_bolos_ux_context.sskr_words_buffer, - G_bolos_ux_context.sskr_words_buffer_length, - G_bolos_ux_context.sskr_share_count, - (unsigned char*) &G_bolos_ux_context.words_buffer, - &G_bolos_ux_context.words_buffer_length, - buffer); + bolos_ux_sskr_to_seed_convert((unsigned char*) G_bolos_ux_context.sskr_words_buffer, + G_bolos_ux_context.sskr_words_buffer_length, + G_bolos_ux_context.sskr_share_count, + (unsigned char*) &G_bolos_ux_context.words_buffer, + &G_bolos_ux_context.words_buffer_length, + buffer); } // get rootkey from hex-seed @@ -684,7 +684,7 @@ void screen_onboarding_restore_word_init(unsigned int action) { G_bolos_ux_context.sskr_share_index = 0; // flush the words first - memzero(G_bolos_ux_context.words_buffer, sizeof(G_bolos_ux_context.words_buffer)); + memzero(G_bolos_ux_context.words_buffer, G_bolos_ux_context.words_buffer_length); G_bolos_ux_context.words_buffer_length = 0; G_bolos_ux_context.sskr_words_buffer_length = 0; break; diff --git a/src/nano/nanox_enter_phrase.c b/src/nano/nanox_enter_phrase.c index c2ac33fd..5748c652 100644 --- a/src/nano/nanox_enter_phrase.c +++ b/src/nano/nanox_enter_phrase.c @@ -468,12 +468,12 @@ static uint8_t compare_recovery_phrase(void) { buffer); } else if (G_bolos_ux_context.onboarding_type == ONBOARDING_TYPE_SSKR) { G_bolos_ux_context.words_buffer_length = sizeof(G_bolos_ux_context.words_buffer); - bolos_ux_sskr_hex_to_seed((unsigned char*) G_bolos_ux_context.sskr_words_buffer, - G_bolos_ux_context.sskr_words_buffer_length, - G_bolos_ux_context.sskr_share_count, - (unsigned char*) &G_bolos_ux_context.words_buffer, - &G_bolos_ux_context.words_buffer_length, - buffer); + bolos_ux_sskr_to_seed_convert((unsigned char*) G_bolos_ux_context.sskr_words_buffer, + G_bolos_ux_context.sskr_words_buffer_length, + G_bolos_ux_context.sskr_share_count, + (unsigned char*) &G_bolos_ux_context.words_buffer, + &G_bolos_ux_context.words_buffer_length, + buffer); } // get rootkey from hex-seed @@ -706,7 +706,7 @@ void screen_onboarding_restore_word_init(unsigned int firstWord) { G_bolos_ux_context.sskr_share_index = 0; // flush the words first - memzero(G_bolos_ux_context.words_buffer, sizeof(G_bolos_ux_context.words_buffer)); + memzero(G_bolos_ux_context.words_buffer, G_bolos_ux_context.words_buffer_length); G_bolos_ux_context.words_buffer_length = 0; G_bolos_ux_context.sskr_words_buffer_length = 0; } diff --git a/src/stax/bip39_mnemonic.c b/src/stax/bip39_mnemonic.c index 3faec3d5..40d0fd50 100644 --- a/src/stax/bip39_mnemonic.c +++ b/src/stax/bip39_mnemonic.c @@ -2,7 +2,9 @@ #include #include "../ux_common/common_bip39.h" +#include "../ux_common/common_sskr.h" #include "./bip39_mnemonic.h" +#include "./sskr_shares.h" #if defined(TARGET_STAX) @@ -21,7 +23,7 @@ typedef struct bip39_buffer_struct { static bip39_buffer_t mnemonic = {0}; -size_t mnemonic_shrink(const size_t size) { +size_t bip39_mnemonic_shrink(const size_t size) { if (size == 0 || size > mnemonic.length) { // shrink all mnemonic.length = 0; @@ -57,7 +59,7 @@ bool bip39_mnemonic_word_remove(void) { const size_t current_length = mnemonic.word_lengths[mnemonic.current_word_index]; mnemonic.current_word_index--; // removing previous word from mnemonic buffer (+ 1 blank space) - mnemonic_shrink(current_length + 1); + bip39_mnemonic_shrink(current_length + 1); PRINTF("Number of remaining words in the mnemonic: '%d'\n", mnemonic.current_word_index + 1); return true; } @@ -92,11 +94,28 @@ bool bip39_mnemonic_check(void) { mnemonic.length); const bool result = bolos_ux_bip39_mnemonic_check((unsigned char*) &mnemonic.buffer[0], mnemonic.length); - // Don;t clear the mneminic just yet as we may need it to generate SSKR shares + // Don't clear the mnemonic just yet as we may need it to generate SSKR shares // bip39_mnemonic_reset(); return result; } +void bip39_mnemonic_from_sskr_shares(void) { + mnemonic.length = BIP39_MNEMONIC_MAX_LENGTH; + uint8_t buffer[64] = {0}; + + bolos_ux_sskr_to_seed_convert((const unsigned char*) sskr_shares_get(), + sskr_shares_length_get(), + sskr_sharecount_get(), + (const unsigned char*) bip39_mnemonic_get(), + &mnemonic.length, + buffer); + memzero(buffer, sizeof(buffer)); + + if (mnemonic.length > 0) { + PRINTF("BIP39 mnemonic: %.*s\n", mnemonic.length, bip39_mnemonic_get()); + } +} + // Used for BIP39 <-> SSKR roundtrip char* bip39_mnemonic_get(void) { return mnemonic.buffer; diff --git a/src/stax/bip39_mnemonic.h b/src/stax/bip39_mnemonic.h index b0ab6454..9a89435e 100644 --- a/src/stax/bip39_mnemonic.h +++ b/src/stax/bip39_mnemonic.h @@ -65,6 +65,11 @@ bool bip39_mnemonic_word_remove(void); */ size_t bip39_mnemonic_word_add(const char* const buffer, const size_t size); +/* + * Generate BIP39 mnemonic from SSKR shares + */ +void bip39_mnemonic_from_sskr_shares(void); + /* * Returns the mnemonic passphrase */ diff --git a/src/stax/sskr_shares.c b/src/stax/sskr_shares.c index cc253d87..3e900711 100644 --- a/src/stax/sskr_shares.c +++ b/src/stax/sskr_shares.c @@ -12,12 +12,81 @@ typedef struct sskr_buffer_struct { char buffer[SSKR_SHARES_MAX_LENGTH]; // current length of the shares buffer unsigned int length; + // index of the current word ((size_t)-1 mean there is no word currently) + size_t current_word_index; + // index of the current share ((uint8_t)-1 mean there is no share currently) + uint8_t current_share_index; unsigned int group_descriptor[1][2]; uint8_t count; + // expected number of words in the share + size_t final_size; + } sskr_buffer_t; static sskr_buffer_t shares = {0}; +size_t sskr_shares_shrink(const size_t size) { + if (size == 0 || size > shares.length) { + // shrink all + shares.length = 0; + } else { + shares.length -= size; + } + memzero(&shares.buffer[shares.length], SSKR_SHARES_MAX_LENGTH - shares.length); + return shares.length; +} + +bool sskr_shares_word_remove(void) { + PRINTF("Removing a word, currently there is '%d' of them\n", shares.current_word_index + 1); + if (shares.current_word_index == (size_t) -1) { + return false; + } + shares.current_word_index--; + // removing previous word from shares buffer (+ 1 blank space) + sskr_shares_shrink(1); + PRINTF("Number of remaining words in the shares: '%d'\n", shares.current_word_index + 1); + return true; +} + +size_t sskr_shares_current_word_number_get(void) { + return shares.current_word_index + 1; +} + +size_t sskr_shares_word_add(const char* const byteword) { + shares.buffer[shares.length] = bolos_ux_sskr_byteword_to_hex((unsigned char*) byteword); + switch (sskr_shares_current_word_number_get()) { + // 4th byte of CBOR header contains number of data bytes to follow + case 3: + // SSKR bytes = 4 bytes CBOR + n bytes share + 4 bytes CRC checksum + shares.final_size = 4 + (shares.buffer[shares.length] & 0x1F) + sizeof(uint32_t); + break; + case 4: + if ((shares.buffer[3] & 0x1F) == 24) { + shares.final_size = 4 + 1 + shares.buffer[shares.length] + sizeof(uint32_t); + } + PRINTF("SSKR final number of words in this share: %d\n", shares.final_size); + break; + // 8th byte of SSKR phrase contains member-threshold + case 7: + if ((shares.buffer[3] & 0x1F) < 24) { + shares.count = (shares.buffer[shares.length] & 0x0F) + 1; + } + break; + case 8: + if ((shares.buffer[3] & 0x1F) == 24) { + shares.count = (shares.buffer[shares.length] & 0x0F) + 1; + } + break; + } + shares.length++; + shares.current_word_index++; + + PRINTF("Current number of words in the share: '%d'\n", sskr_shares_current_word_number_get()); + PRINTF("Current shares buffer: '%.*H'\n", shares.length, &shares.buffer[0]); + + return sskr_shares_current_word_number_get(); +} + void sskr_sharenum_set(const uint8_t sharenum) { shares.group_descriptor[0][1] = sharenum; } @@ -38,8 +107,14 @@ uint8_t sskr_sharecount_get(void) { return shares.count; } +uint8_t sskr_shareindex_get(void) { + return shares.current_share_index + 1; +} + void sskr_shares_reset(void) { memzero(&shares, sizeof(shares)); + shares.current_word_index = (size_t) -1; + shares.current_share_index = (uint8_t) -1; } void sskr_shares_from_bip39_mnemonic(void) { @@ -65,6 +140,43 @@ void sskr_shares_from_bip39_mnemonic(void) { } } +bool sskr_shares_complete_check(void) { + // We won't know final size until after word 5 + if (sskr_shares_current_word_number_get() < 5 || + sskr_shares_current_word_number_get() < shares.final_size) { + return false; + } + + shares.current_share_index++; + + if (sskr_shareindex_get() < sskr_sharecount_get()) { + shares.current_word_index = (size_t) -1; + return false; + } + + return true; +} + +bool sskr_shares_check(void) { + if (!sskr_shares_complete_check()) { + return false; + } + + PRINTF("Checking the following shares: '%.*H' (size %d)\n", + shares.length, + &shares.buffer[0], + shares.length); + + const bool result = bolos_ux_sskr_hex_check((unsigned char*) sskr_shares_get(), + sskr_shares_length_get(), + sskr_sharecount_get()); + + // Don't clear the shares just yet as we may need it to generate BIP39 mnemonic + // sskr_shares_reset(); + + return result; +} + char* sskr_shares_get(void) { return shares.buffer; } diff --git a/src/stax/sskr_shares.h b/src/stax/sskr_shares.h index 47a8fc16..ca0f22de 100644 --- a/src/stax/sskr_shares.h +++ b/src/stax/sskr_shares.h @@ -23,6 +23,35 @@ // 16 shares * 229 chars per share (46 SSKR ByteWords) #define SSKR_SHARES_MAX_LENGTH 3664 +// All ByteWords have uniform length of 4 +#define SSKR_BYTEWORD_LENGTH 4 + +/* + * Remove the latest word from the shares, returns true if there was at least one to remove, + * else false (there was no word) + */ +bool sskr_shares_word_remove(void); + +/* + * Adds a word in the shares phrase, returns how many words are stored in the share + */ +size_t sskr_shares_word_add(const char* const buffer); + +/* + * Returns how many words are currently stored in the shares phrase + */ +size_t sskr_shares_current_word_number_get(void); + +/* + * Check if the current number of words in the shares fits the expected number of words + */ +bool sskr_shares_complete_check(void); + +/* + * Check if the currently stored mnemonic generates the same seed as the current device's one + */ +bool sskr_shares_check(void); + /* * Sets the number of SSKR shares */ @@ -46,9 +75,13 @@ uint8_t sskr_threshold_get(void); /* * Returns the SSKR share count */ - uint8_t sskr_sharecount_get(void); +/* + * Returns the SSKR share index + */ +uint8_t sskr_shareindex_get(void); + /* * Erase all information and reset the indexes */ diff --git a/src/stax/ui_stax.c b/src/stax/ui_stax.c index ed17e99a..9a4d9984 100644 --- a/src/stax/ui_stax.c +++ b/src/stax/ui_stax.c @@ -14,6 +14,7 @@ #include #include "../ux_common/common_bip39.h" +#include "../ux_common/common_sskr.h" #include "../ui.h" #include "./bip39_mnemonic.h" #include "./sskr_shares.h" @@ -26,19 +27,16 @@ static nbgl_page_t *pageContext; static char headerText[HEADER_SIZE] = {0}; static nbgl_layout_t *layout = 0; +unsigned int onboarding_type; + static void display_home_page(void); +static void display_check_keyboard_page(void); +static void display_check_result_page(const bool result); static void display_bip39_select_phrase_length_page(void); -static void display_bip39_keyboard_page(void); -static void display_result_page(const bool result); +static void display_bip39_mnemonic(void); static void display_sskr_select_numshares_page(void); static void display_sskr_select_threshold_page(void); -enum bip39_check { - BACK_BUTTON_TOKEN = FIRST_USER_TOKEN, - FIRST_SUGGESTION_TOKEN, - RESULT_TOKEN, -}; - /* * Utils */ @@ -55,144 +53,126 @@ static void on_quit(void) { } /* - * Select recovery type, BIP39 or SSKR + * Select tool type, BIP39 or SSKR */ -enum select_check_type { - SELECT_CHECK_TYPE_ICON_INDEX = 0, - SELECT_CHECK_TYPE_TEXT_INDEX, - SELECT_CHECK_TYPE_BIP39_INDEX, - SELECT_CHECK_TYPE_SSKR_INDEX, - SELECT_CHECK_TYPE_BACK_BUTTON_INDEX, - SELECT_CHECK_TYPE_KBD_TEXT_TOKEN, - SELECT_CHECK_TYPE_NB_CHILDREN +enum select_tool { + SELECT_TOOL_ICON_INDEX = 0, + SELECT_TOOL_TEXT_INDEX, + SELECT_TOOL_BIP39_INDEX, + SELECT_TOOL_SSKR_INDEX, + SELECT_TOOL_BIP85_INDEX, + SELECT_TOOL_BACK_BUTTON_INDEX, + SELECT_TOOL_NB_CHILDREN }; -#define SELECT_CHECK_TYPE_NB_BUTTONS 3 +#define SELECT_TOOL_NB_BUTTONS 4 -static const char *checkType[] = {"BIP39 Check", "SSKR Check"}; -static void select_check_type_callback(nbgl_obj_t *obj, nbgl_touchType_t eventType) { +static const char *toolType[] = {"BIP39 Check", "SSKR Check", "BIP85 Generate"}; +static void select_tool_callback(nbgl_obj_t *obj, nbgl_touchType_t eventType) { nbgl_obj_t **screenChildren = nbgl_screenGetElements(0); if (eventType != TOUCHED) { return; } io_seproxyhal_play_tune(TUNE_TAP_CASUAL); - if (obj == screenChildren[SELECT_CHECK_TYPE_BIP39_INDEX]) { + if (obj == screenChildren[SELECT_TOOL_BIP39_INDEX]) { nbgl_layoutRelease(layout); + onboarding_type = ONBOARDING_TYPE_BIP39; display_bip39_select_phrase_length_page(); - } else if (obj == screenChildren[SELECT_CHECK_TYPE_SSKR_INDEX]) { + } else if (obj == screenChildren[SELECT_TOOL_SSKR_INDEX]) { + nbgl_layoutRelease(layout); + onboarding_type = ONBOARDING_TYPE_SSKR; + display_check_keyboard_page(); + } else if (obj == screenChildren[SELECT_TOOL_BIP85_INDEX]) { nbgl_layoutRelease(layout); nbgl_useCaseStatus("Under Construction\nComing soon", false, display_home_page); - return; - } else if (obj == screenChildren[SELECT_CHECK_TYPE_BACK_BUTTON_INDEX]) { + } else if (obj == screenChildren[SELECT_TOOL_BACK_BUTTON_INDEX]) { nbgl_layoutRelease(layout); display_home_page(); return; } } -static void display_select_check_type_page(void) { +static void display_select_tool_page(void) { nbgl_obj_t **screenChildren; - // 2 buttons + icon + text + subText - nbgl_screenSet(&screenChildren, 5, NULL, (nbgl_touchCallback_t) &select_check_type_callback); + // 3 buttons + icon + text + subText + nbgl_screenSet(&screenChildren, + SELECT_TOOL_NB_CHILDREN, + NULL, + (nbgl_touchCallback_t) &select_tool_callback); - screenChildren[SELECT_CHECK_TYPE_ICON_INDEX] = + screenChildren[SELECT_TOOL_ICON_INDEX] = (nbgl_obj_t *) generic_screen_set_icon(&C_seed_stax_64px); - screenChildren[SELECT_CHECK_TYPE_TEXT_INDEX] = - (nbgl_obj_t *) generic_screen_set_title(screenChildren[SELECT_CHECK_TYPE_ICON_INDEX]); - ((nbgl_text_area_t *) screenChildren[SELECT_CHECK_TYPE_TEXT_INDEX])->text = - "\nSelect type of\nRecovery you wish\nto check"; + screenChildren[SELECT_TOOL_TEXT_INDEX] = + (nbgl_obj_t *) generic_screen_set_title(screenChildren[SELECT_TOOL_ICON_INDEX]); + ((nbgl_text_area_t *) screenChildren[SELECT_TOOL_TEXT_INDEX])->text = + "\n\nSelect the tool\nyou wish to use"; // create nb words buttons nbgl_objPoolGetArray(BUTTON, - SELECT_CHECK_TYPE_NB_BUTTONS, + SELECT_TOOL_NB_BUTTONS, 0, - (nbgl_obj_t **) &screenChildren[SELECT_CHECK_TYPE_BIP39_INDEX]); - generic_screen_configure_buttons( - (nbgl_button_t **) &screenChildren[SELECT_CHECK_TYPE_BIP39_INDEX], - SELECT_CHECK_TYPE_NB_BUTTONS); - ((nbgl_button_t *) screenChildren[SELECT_CHECK_TYPE_BIP39_INDEX])->text = checkType[0]; - ((nbgl_button_t *) screenChildren[SELECT_CHECK_TYPE_BIP39_INDEX])->icon = &C_bip39_stax_32px; - ((nbgl_button_t *) screenChildren[SELECT_CHECK_TYPE_SSKR_INDEX])->text = checkType[1]; - ((nbgl_button_t *) screenChildren[SELECT_CHECK_TYPE_SSKR_INDEX])->icon = &C_sskr_stax_32px; - ((nbgl_button_t *) screenChildren[SELECT_CHECK_TYPE_SSKR_INDEX])->borderColor = BLACK; - ((nbgl_button_t *) screenChildren[SELECT_CHECK_TYPE_SSKR_INDEX])->innerColor = BLACK; - ((nbgl_button_t *) screenChildren[SELECT_CHECK_TYPE_SSKR_INDEX])->foregroundColor = WHITE; + (nbgl_obj_t **) &screenChildren[SELECT_TOOL_BIP39_INDEX]); + generic_screen_configure_buttons((nbgl_button_t **) &screenChildren[SELECT_TOOL_BIP39_INDEX], + SELECT_TOOL_NB_BUTTONS); + ((nbgl_button_t *) screenChildren[SELECT_TOOL_BIP39_INDEX])->text = toolType[0]; + ((nbgl_button_t *) screenChildren[SELECT_TOOL_BIP39_INDEX])->icon = &C_bip39_stax_32px; + ((nbgl_button_t *) screenChildren[SELECT_TOOL_SSKR_INDEX])->text = toolType[1]; + ((nbgl_button_t *) screenChildren[SELECT_TOOL_SSKR_INDEX])->icon = &C_sskr_stax_32px; + ((nbgl_button_t *) screenChildren[SELECT_TOOL_SSKR_INDEX])->borderColor = BLACK; + ((nbgl_button_t *) screenChildren[SELECT_TOOL_SSKR_INDEX])->innerColor = BLACK; + ((nbgl_button_t *) screenChildren[SELECT_TOOL_SSKR_INDEX])->foregroundColor = WHITE; + ((nbgl_button_t *) screenChildren[SELECT_TOOL_BIP85_INDEX])->text = toolType[2]; + ((nbgl_button_t *) screenChildren[SELECT_TOOL_BIP85_INDEX])->icon = &C_bip85_stax_32px; // create back button - screenChildren[SELECT_CHECK_TYPE_BACK_BUTTON_INDEX] = - (nbgl_obj_t *) generic_screen_set_back_button(); + screenChildren[SELECT_TOOL_BACK_BUTTON_INDEX] = (nbgl_obj_t *) generic_screen_set_back_button(); nbgl_screenRedraw(); } /* - * Select Generate SSKR or BIP85 + * Select Recover BIP39 */ -enum select_generate_type { - SELECT_GENERATE_TYPE_ICON_INDEX = 0, - SELECT_GENERATE_TYPE_TEXT_INDEX, - SELECT_GENERATE_TYPE_BIP85_INDEX, - SELECT_GENERATE_TYPE_SSKR_INDEX, - SELECT_GENERATE_TYPE_BACK_BUTTON_INDEX, - SELECT_GENERATE_TYPE_KBD_TEXT_TOKEN, - SELECT_GENERATE_TYPE_NB_CHILDREN -}; +static void select_recover_bip39_choice(bool bip39_rec) { + if (bip39_rec) { + nbgl_layoutRelease(layout); + display_bip39_mnemonic(); + } else { + nbgl_layoutRelease(layout); + display_home_page(); + } +} -#define SELECT_GENERATE_TYPE_NB_BUTTONS 2 +void display_select_recover_bip39_page(void) { + nbgl_useCaseChoice( + &C_bip39_stax_64px, + "Recover BIP39 Phrase?", + "Choose if you wish to\nrecover the BIP39 phrase\nfrom your valid\nSSKR shares.", + "Recover BIP39", + "Done", + select_recover_bip39_choice); +} -static const char *generateType[] = {"Generate BIP85", "Generate SSKR"}; -static void select_generate_type_callback(nbgl_obj_t *obj, nbgl_touchType_t eventType) { - nbgl_obj_t **screenChildren = nbgl_screenGetElements(0); - if (eventType != TOUCHED) { - return; - } - io_seproxyhal_play_tune(TUNE_TAP_CASUAL); - if (obj == screenChildren[SELECT_GENERATE_TYPE_BIP85_INDEX]) { - nbgl_layoutRelease(layout); - nbgl_useCaseStatus("Under Construction\nComing soon", false, display_home_page); - } else if (obj == screenChildren[SELECT_GENERATE_TYPE_SSKR_INDEX]) { +/* + * Select Generate SSKR + */ +static void select_generate_sskr_choice(bool sskr_gen) { + if (sskr_gen) { nbgl_layoutRelease(layout); display_sskr_select_numshares_page(); - } else if (obj == screenChildren[SELECT_GENERATE_TYPE_BACK_BUTTON_INDEX]) { + } else { nbgl_layoutRelease(layout); display_home_page(); - return; } } -static void display_select_generate_type_page(void) { - nbgl_obj_t **screenChildren; - - // 2 buttons + icon + text + subText - nbgl_screenSet(&screenChildren, 5, NULL, (nbgl_touchCallback_t) &select_generate_type_callback); - - screenChildren[SELECT_GENERATE_TYPE_ICON_INDEX] = - (nbgl_obj_t *) generic_screen_set_icon(&C_seed_stax_64px); - screenChildren[SELECT_GENERATE_TYPE_TEXT_INDEX] = - (nbgl_obj_t *) generic_screen_set_title(screenChildren[SELECT_GENERATE_TYPE_ICON_INDEX]); - ((nbgl_text_area_t *) screenChildren[SELECT_GENERATE_TYPE_TEXT_INDEX])->text = - "\nSelect if you wish to\ngenerate SSKR shares\nor BIP85 children"; - // create nb words buttons - nbgl_objPoolGetArray(BUTTON, - SELECT_GENERATE_TYPE_NB_BUTTONS, - 0, - (nbgl_obj_t **) &screenChildren[SELECT_GENERATE_TYPE_BIP85_INDEX]); - generic_screen_configure_buttons( - (nbgl_button_t **) &screenChildren[SELECT_GENERATE_TYPE_BIP85_INDEX], - SELECT_GENERATE_TYPE_NB_BUTTONS); - ((nbgl_button_t *) screenChildren[SELECT_GENERATE_TYPE_BIP85_INDEX])->text = generateType[0]; - ((nbgl_button_t *) screenChildren[SELECT_GENERATE_TYPE_BIP85_INDEX])->icon = &C_bip85_stax_32px; - ((nbgl_button_t *) screenChildren[SELECT_GENERATE_TYPE_SSKR_INDEX])->text = generateType[1]; - ((nbgl_button_t *) screenChildren[SELECT_GENERATE_TYPE_SSKR_INDEX])->icon = &C_sskr_stax_32px; - ((nbgl_button_t *) screenChildren[SELECT_GENERATE_TYPE_SSKR_INDEX])->borderColor = BLACK; - ((nbgl_button_t *) screenChildren[SELECT_GENERATE_TYPE_SSKR_INDEX])->innerColor = BLACK; - ((nbgl_button_t *) screenChildren[SELECT_GENERATE_TYPE_SSKR_INDEX])->foregroundColor = WHITE; - - // create back button - screenChildren[SELECT_GENERATE_TYPE_BACK_BUTTON_INDEX] = - (nbgl_obj_t *) generic_screen_set_back_button(); - - nbgl_screenRedraw(); +void display_select_generate_sskr_page(void) { + nbgl_useCaseChoice(&C_sskr_stax_64px, + "Generate SSKR Phrase?", + "Choose if you wish to\ngenerate SSKR shares from\nyour valid BIP39 phrase.", + "Generate SSKR", + "Done", + select_generate_sskr_choice); } /* @@ -206,7 +186,6 @@ enum select_bip39_phrase_length { SELECT_BIP39_PHRASE_LENGTH_BUTTON_24_INDEX, SELECT_BIP39_PHRASE_LENGTH_BACK_BUTTON_INDEX, SELECT_BIP39_PHRASE_LENGTH_KBD_TEXT_TOKEN, - SELECT_BIP39_PHRASE_LENGTH_NB_CHILDREN }; #define SELECT_BIP39_PHRASE_LENGTH_NB_BUTTONS 3 @@ -226,11 +205,11 @@ static void select_bip39_phrase_length_callback(nbgl_obj_t *obj, nbgl_touchType_ bip39_mnemonic_final_size_set(BIP39_MNEMONIC_SIZE_24); } else if (obj == screenChildren[SELECT_BIP39_PHRASE_LENGTH_BACK_BUTTON_INDEX]) { nbgl_layoutRelease(layout); - display_select_check_type_page(); + display_select_tool_page(); return; } nbgl_layoutRelease(layout); - display_bip39_keyboard_page(); + display_check_keyboard_page(); } static void display_bip39_select_phrase_length_page(void) { @@ -283,6 +262,12 @@ static void display_bip39_select_phrase_length_page(void) { */ #define BUTTON_VMARGIN 32 +enum check { + CHECK_BACK_BUTTON_TOKEN = FIRST_USER_TOKEN, + CHECK_FIRST_SUGGESTION_TOKEN, + CHECK_RESULT_TOKEN, +}; + static char textToEnter[BIP39_MAX_WORD_LENGTH + 1] = {0}; static int keyboardIndex = 0; static nbgl_layoutKeyboardContent_t kbdContent; @@ -290,30 +275,6 @@ static nbgl_layoutKeyboardContent_t kbdContent; // the max number of showed suggestions is NB_MAX_SUGGESTION_BUTTONS static char wordCandidates[(BIP39_MAX_WORD_LENGTH + 1) * NB_MAX_SUGGESTION_BUTTONS] = {0}; -static void keyboard_dispatcher(const int token, uint8_t index) { - UNUSED(index); - if (token == BACK_BUTTON_TOKEN) { - nbgl_layoutRelease(layout); - if (bip39_mnemonic_word_remove()) { - display_bip39_keyboard_page(); - } else { - display_bip39_select_phrase_length_page(); - } - } else if (token >= FIRST_SUGGESTION_TOKEN) { - nbgl_layoutRelease(layout); - PRINTF("Selected word is '%s' (size '%d')\n", - buttonTexts[token - FIRST_SUGGESTION_TOKEN], - strlen(buttonTexts[token - FIRST_SUGGESTION_TOKEN])); - bip39_mnemonic_word_add(buttonTexts[token - FIRST_SUGGESTION_TOKEN], - strlen(buttonTexts[token - FIRST_SUGGESTION_TOKEN])); - if (bip39_mnemonic_complete_check()) { - display_result_page(bip39_mnemonic_check()); - } else { - display_bip39_keyboard_page(); - } - } -} - /* * Function called when a key of keyboard is touched */ @@ -338,24 +299,83 @@ static void key_press_callback(const char touchedKey) { kbdContent.suggestionButtons.nbUsedButtons = 0; } else { const size_t nbMatchingWords = - bolos_ux_bip39_fill_with_candidates((unsigned char *) &(textToEnter[0]), - strlen(textToEnter), - wordCandidates, - buttonTexts); + onboarding_type == ONBOARDING_TYPE_BIP39 + ? bolos_ux_bip39_fill_with_candidates((unsigned char *) &(textToEnter[0]), + strlen(textToEnter), + wordCandidates, + buttonTexts) + : bolos_ux_sskr_fill_with_candidates((unsigned char *) &(textToEnter[0]), + strlen(textToEnter), + wordCandidates, + buttonTexts); kbdContent.suggestionButtons.nbUsedButtons = nbMatchingWords; } if (textLen > 0) { - mask = bolos_ux_bip39_get_keyboard_mask((unsigned char *) &(textToEnter[0]), - strlen(textToEnter)); + mask = onboarding_type == ONBOARDING_TYPE_BIP39 + ? bolos_ux_bip39_get_keyboard_mask((unsigned char *) &(textToEnter[0]), + strlen(textToEnter)) + : bolos_ux_sskr_get_keyboard_mask((unsigned char *) &(textToEnter[0]), + strlen(textToEnter)); } nbgl_layoutUpdateKeyboard(layout, keyboardIndex, mask, false, LOWER_CASE); nbgl_layoutUpdateKeyboardContent(layout, &kbdContent); nbgl_refresh(); } -static void display_bip39_keyboard_page() { - nbgl_layoutDescription_t layoutDescription = {.modal = false, - .onActionCallback = &keyboard_dispatcher}; +static void bip39_keyboard_dispatcher(const int token, uint8_t index) { + UNUSED(index); + if (token == CHECK_BACK_BUTTON_TOKEN) { + nbgl_layoutRelease(layout); + if (bip39_mnemonic_word_remove()) { + display_check_keyboard_page(); + } else { + bip39_mnemonic_reset(); + display_bip39_select_phrase_length_page(); + } + } else if (token >= CHECK_FIRST_SUGGESTION_TOKEN) { + nbgl_layoutRelease(layout); + PRINTF("Selected word is '%s' (size '%d')\n", + buttonTexts[token - CHECK_FIRST_SUGGESTION_TOKEN], + strlen(buttonTexts[token - CHECK_FIRST_SUGGESTION_TOKEN])); + bip39_mnemonic_word_add(buttonTexts[token - CHECK_FIRST_SUGGESTION_TOKEN], + strlen(buttonTexts[token - CHECK_FIRST_SUGGESTION_TOKEN])); + if (bip39_mnemonic_complete_check()) { + display_check_result_page(bip39_mnemonic_check()); + } else { + display_check_keyboard_page(); + } + } +} + +static void sskr_keyboard_dispatcher(const int token, uint8_t index) { + UNUSED(index); + if (token == CHECK_BACK_BUTTON_TOKEN) { + nbgl_layoutRelease(layout); + if (sskr_shares_word_remove()) { + display_check_keyboard_page(); + } else { + sskr_shares_reset(); + display_select_tool_page(); + } + } else if (token >= CHECK_FIRST_SUGGESTION_TOKEN) { + nbgl_layoutRelease(layout); + PRINTF("Selected word is '%s' (size '%d')\n", + buttonTexts[token - CHECK_FIRST_SUGGESTION_TOKEN], + strlen(buttonTexts[token - CHECK_FIRST_SUGGESTION_TOKEN])); + sskr_shares_word_add(buttonTexts[token - CHECK_FIRST_SUGGESTION_TOKEN]); + if (sskr_shares_complete_check()) { + display_check_result_page(sskr_shares_check()); + } else { + display_check_keyboard_page(); + } + } +} + +static void display_check_keyboard_page() { + nbgl_layoutDescription_t layoutDescription = { + .modal = false, + .onActionCallback = onboarding_type == ONBOARDING_TYPE_BIP39 ? &bip39_keyboard_dispatcher + : &sskr_keyboard_dispatcher}; nbgl_layoutKbd_t kbdInfo = {.lettersOnly = true, // use only letters .mode = MODE_LETTERS, // start in letters mode .keyMask = 0, // no inactive key @@ -364,22 +384,32 @@ static void display_bip39_keyboard_page() { kbdContent.title = headerText; kbdContent.text = textToEnter; kbdContent.numbered = true; - kbdContent.number = bip39_mnemonic_current_word_number_get() + 1; + kbdContent.number = onboarding_type == ONBOARDING_TYPE_BIP39 + ? bip39_mnemonic_current_word_number_get() + 1 + : sskr_shares_current_word_number_get() + 1; kbdContent.grayedOut = false; kbdContent.textToken = SELECT_BIP39_PHRASE_LENGTH_KBD_TEXT_TOKEN; kbdContent.suggestionButtons.buttons = buttonTexts; - kbdContent.suggestionButtons.firstButtonToken = FIRST_SUGGESTION_TOKEN; + kbdContent.suggestionButtons.firstButtonToken = CHECK_FIRST_SUGGESTION_TOKEN; kbdContent.suggestionButtons.nbUsedButtons = 0; // no used buttons at start-up textToEnter[0] = '\0'; memzero(buttonTexts, sizeof(buttonTexts[0]) * NB_MAX_SUGGESTION_BUTTONS); layout = nbgl_layoutGet(&layoutDescription); - snprintf(headerText, - HEADER_SIZE, - "Enter word n. %d/%d of your\nBIP39 Recovery Phrase", - bip39_mnemonic_current_word_number_get() + 1, - bip39_mnemonic_final_size_get()); + if (onboarding_type == ONBOARDING_TYPE_BIP39) { + snprintf(headerText, + HEADER_SIZE, + "Enter word n. %d/%d of your\nBIP39 Recovery Phrase", + bip39_mnemonic_current_word_number_get() + 1, + bip39_mnemonic_final_size_get()); + } else if (onboarding_type == ONBOARDING_TYPE_SSKR) { + snprintf(headerText, + HEADER_SIZE, + "Enter Share %d Word %d\nof your Recovery Phrase", + sskr_shareindex_get() + 1, + sskr_shares_current_word_number_get() + 1); + } keyboardIndex = nbgl_layoutAddKeyboard(layout, &kbdInfo); - nbgl_layoutAddProgressIndicator(layout, 0, 0, true, BACK_BUTTON_TOKEN, TUNE_TAP_CASUAL); + nbgl_layoutAddProgressIndicator(layout, 0, 0, true, CHECK_BACK_BUTTON_TOKEN, TUNE_TAP_CASUAL); nbgl_layoutAddKeyboardContent(layout, &kbdContent); nbgl_layoutDraw(layout); } @@ -387,19 +417,16 @@ static void display_bip39_keyboard_page() { /* * Home page, infos & dispatcher */ -static const char *const infoTypes[] = {"Version", APPNAME}; -static const char *const infoContents[] = {APPVERSION, "(c) 2018-2024 Ledger"}; -static const nbgl_contentInfoList_t infoList = { - .nbInfos = 2, - .infoTypes = infoTypes, - .infoContents = infoContents, -}; - static void display_home_page() { + static const char *const infoTypes[] = {"Version", APPNAME}; + static const char *const infoContents[] = {APPVERSION, "(c) 2018-2024 Ledger"}; + static const nbgl_contentInfoList_t infoList = {.nbInfos = 2, + .infoTypes = infoTypes, + .infoContents = infoContents}; + reset_globals(); - nbgl_homeAction_t action = {.text = "Recovery Check", - .callback = PIC(display_select_check_type_page)}; + nbgl_homeAction_t action = {.text = "Select Tool", .callback = PIC(display_select_tool_page)}; nbgl_useCaseHomeAndSettings( APPNAME, @@ -415,38 +442,45 @@ static void display_home_page() { /* * Result page */ -static const char *possible_results[2][2] = { - {"Incorrect Secret\nRecovery Phrase", - "The Recovery Phrase you have\nentered doesn't match the one\npresent on this Ledger Stax."}, - {"Correct Secret\nRecovery Phrase", - "The Recovery Phrase you have\nentered matches the one\npresent on this Ledger Stax."}}; -static const nbgl_icon_details_t *icons[2] = {&C_Warning_64px, &C_Check_Circle_64px}; - -static void result_callback(int token __attribute__((unused)), - uint8_t index __attribute__((unused))) { - if (bip39_mnemonic_complete_check()) { - display_select_generate_type_page(); +static void check_result_callback(int token __attribute__((unused)), + uint8_t index __attribute__((unused))) { + if (onboarding_type == ONBOARDING_TYPE_BIP39 && bip39_mnemonic_check()) { + display_select_generate_sskr_page(); + } else if (onboarding_type == ONBOARDING_TYPE_SSKR && sskr_shares_check()) { + display_select_recover_bip39_page(); } else { reset_globals(); display_home_page(); } } -static void display_result_page(const bool result) { - nbgl_pageInfoDescription_t info = {.centeredInfo.icon = icons[result], - .centeredInfo.text1 = possible_results[result][0], - .centeredInfo.text2 = possible_results[result][1], - .centeredInfo.text3 = NULL, - .centeredInfo.style = LARGE_CASE_INFO, - .centeredInfo.offsetY = -16, - .footerText = "Tap to dismiss", - .footerToken = RESULT_TOKEN, - .bottomButtonStyle = NO_BUTTON_STYLE, - .tapActionText = NULL, - .topRightStyle = NO_BUTTON_STYLE, - .actionButtonText = NULL, - .tuneId = TUNE_TAP_CASUAL}; - pageContext = nbgl_pageDrawInfo(&result_callback, NULL, &info); +static void display_check_result_page(const bool result) { + static const char *possible_results[2][3] = { + {"Incorrect Secret\nRecovery Phrase", + "The BIP39 Recovery Phrase\nyou have entered\ndoesn't match the one present\n" + "on this Ledger Stax.", + "The SSKR Recovery Phrase\nyou have entered is not valid"}, + {"Correct Secret\nRecovery Phrase", + "The BIP39 Recovery Phrase\nyou have entered\nmatches the one present\n" + "on this Ledger Stax.", + "The SSKR Recovery Phrase\nyou have entered is valid"}}; + static const nbgl_icon_details_t *icons[2] = {&C_Warning_64px, &C_Check_Circle_64px}; + + nbgl_pageInfoDescription_t info = { + .centeredInfo.icon = icons[result], + .centeredInfo.text1 = possible_results[result][0], + .centeredInfo.text2 = possible_results[result][1 + onboarding_type], + .centeredInfo.text3 = NULL, + .centeredInfo.style = LARGE_CASE_INFO, + .centeredInfo.offsetY = -16, + .footerText = "Tap to dismiss", + .footerToken = CHECK_RESULT_TOKEN, + .bottomButtonStyle = NO_BUTTON_STYLE, + .tapActionText = NULL, + .topRightStyle = NO_BUTTON_STYLE, + .actionButtonText = NULL, + .tuneId = TUNE_TAP_CASUAL}; + pageContext = nbgl_pageDrawInfo(&check_result_callback, NULL, &info); nbgl_refresh(); } @@ -477,7 +511,7 @@ static void sskr_sharenum_validate(const uint8_t *sharenumentry, uint8_t length) } else { nbgl_useCaseStatus("Number of SSKR shares must be between 1 and 16", false, - display_select_generate_type_page); + display_select_generate_sskr_page); } } @@ -485,7 +519,7 @@ static void sskr_sharenum_entry_cb(int token, uint8_t index) { UNUSED(index); // Callback for the key navigation (back key mainly) if (token == SSKR_GEN_BACK_BUTTON_TOKEN) { - display_select_generate_type_page(); + display_select_generate_sskr_page(); } } @@ -504,11 +538,44 @@ void display_sskr_select_numshares_page() { char item_buffer[15]; char value_buffer[(SSKR_SHARES_MAX_LENGTH / 16) + 1]; -static void review_sskr_shares_cancel(void) { +static void review_done(void) { memzero(item_buffer, sizeof(item_buffer)); memzero(value_buffer, sizeof(value_buffer)); + reset_globals(); + + display_home_page(); +} + +static void display_bip39_mnemonic(void) { + bip39_mnemonic_from_sskr_shares(); + + static nbgl_layoutTagValue_t pairs[1]; + static const nbgl_content_t content[1] = { + {.type = TAG_VALUE_LIST, + .contentActionCallback = NULL, + .content.tagValueList.nbPairs = 1, + .content.tagValueList.nbMaxLinesForValue = 0, + .content.tagValueList.pairs = (nbgl_layoutTagValue_t *) pairs}}; + static const nbgl_genericContents_t genericContent = {.callbackCallNeeded = false, + .contentsList = content, + .nbContents = 1}; + + SPRINTF(item_buffer, "BIP39 Phrase"); + pairs[0].item = item_buffer; + + strncpy(value_buffer, bip39_mnemonic_get(), bip39_mnemonic_length_get()); + // Ensure null termination + value_buffer[bip39_mnemonic_length_get()] = '\0'; + // Format output + for (uint8_t i = 20; i < (uint8_t) bip39_mnemonic_length_get(); i += 21) { + while (value_buffer[i] != ' ') { + i--; + } + value_buffer[i] = '\n'; + } + pairs[0].value = value_buffer; - display_select_generate_type_page(); + nbgl_useCaseGenericReview(&genericContent, "Done", review_done); } static void review_sskr_shares_contentGetter(uint8_t index, nbgl_content_t *genericreview) { @@ -546,7 +613,7 @@ static void display_sskr_shares(void) { genericContent.contentGetterCallback = review_sskr_shares_contentGetter; genericContent.nbContents = sskr_sharecount_get(); - nbgl_useCaseGenericReview(&genericContent, "Cancel", review_sskr_shares_cancel); + nbgl_useCaseGenericReview(&genericContent, "Done", review_done); } static void sskr_threshold_validate(const uint8_t *thresholdentry, uint8_t length) { @@ -561,15 +628,15 @@ static void sskr_threshold_validate(const uint8_t *thresholdentry, uint8_t lengt PRINTF("Threshold value entered is '%d'\n", sskr_sharenum_get()); if (sskr_threshold_get() < 1) { - nbgl_useCaseStatus("Threshold value cannot be 0", false, display_select_generate_type_page); + nbgl_useCaseStatus("Threshold value cannot be 0", false, display_select_generate_sskr_page); } else if (sskr_threshold_get() > sskr_sharenum_get()) { nbgl_useCaseStatus("Threshold value cannot be greater than number of shares", false, - display_select_generate_type_page); + display_select_generate_sskr_page); } else if (sskr_sharenum_get() == 1 && sskr_sharenum_get() > 1) { nbgl_useCaseStatus("1-of-m shares where m > 1 is not supported", false, - display_select_generate_type_page); + display_select_generate_sskr_page); } else { display_sskr_shares(); } diff --git a/src/ux_common/common_sskr.h b/src/ux_common/common_sskr.h index 0ad36a1f..a455597d 100644 --- a/src/ux_common/common_sskr.h +++ b/src/ux_common/common_sskr.h @@ -4,13 +4,16 @@ // SSKR helpers #include "onboarding_seed_rom_variables.h" +// Encode SSKR ByteWord as hex +unsigned int bolos_ux_sskr_byteword_to_hex(unsigned char *byteword); + // Combine hex value SSKR shares into seed -void bolos_ux_sskr_hex_to_seed(const unsigned char *mnemonic_hex, - unsigned int mnemonic_len, - unsigned int sskr_shares_count, - const unsigned char *words_buffer, - unsigned int *words_buffer_length, - unsigned char *seed); +void bolos_ux_sskr_to_seed_convert(const unsigned char *sskr_shares_hex, + unsigned int sskr_shares_hex_length, + unsigned int sskr_shares_count, + const unsigned char *words_buffer, + unsigned int *words_buffer_length, + unsigned char *seed); // convert seed from BIP39 to SSKR unsigned int bolos_ux_bip39_to_sskr_convert(unsigned char *bip39_words_buffer, @@ -21,8 +24,8 @@ unsigned int bolos_ux_bip39_to_sskr_convert(unsigned char *bip39_words_buffer, unsigned char *sskr_words_buffer, unsigned int *sskr_words_buffer_length); -unsigned int bolos_ux_sskr_hex_check(const unsigned char *mnemonic_hex, - unsigned int mnemonic_length, +unsigned int bolos_ux_sskr_hex_check(const unsigned char *sskr_shares_hex, + unsigned int sskr_shares_hex_length, unsigned int sskr_share_count); unsigned int bolos_ux_sskr_get_word_idx_starting_with(const unsigned char *prefix, @@ -33,3 +36,12 @@ unsigned int bolos_ux_sskr_get_word_count_starting_with(const unsigned char *pre unsigned int bolos_ux_sskr_get_word_next_letters_starting_with(const unsigned char *prefix, const unsigned int prefixlength, unsigned char *next_letters_buffer); + +#if defined(HAVE_NBGL) +size_t bolos_ux_sskr_fill_with_candidates(const unsigned char *startingChars, + const size_t startingCharsLength, + char wordCandidatesBuffer[], + const char *wordIndexorBuffer[]); +uint32_t bolos_ux_sskr_get_keyboard_mask(const unsigned char *prefix, + const unsigned int prefixLength); +#endif diff --git a/src/ux_common/onboarding_seed_bip39.c b/src/ux_common/onboarding_seed_bip39.c index 3313c2a4..0cdfb9dc 100644 --- a/src/ux_common/onboarding_seed_bip39.c +++ b/src/ux_common/onboarding_seed_bip39.c @@ -6,9 +6,6 @@ #include "onboarding_seed_rom_variables.h" #include "common.h" -#define ALPHABET_LENGTH 27 -#define KBD_LETTERS "qwertyuiopasdfghjklzxcvbnm" - // separated function to lower the stack usage when jumping into pbkdf algorithm unsigned int bolos_ux_bip39_mnemonic_to_seed_hash_length128(unsigned char* mnemonic, unsigned int mnemonic_length) { diff --git a/src/ux_common/onboarding_seed_rom_variables.h b/src/ux_common/onboarding_seed_rom_variables.h index e692ffa1..c6b5e939 100644 --- a/src/ux_common/onboarding_seed_rom_variables.h +++ b/src/ux_common/onboarding_seed_rom_variables.h @@ -18,12 +18,15 @@ #define memzero(...) explicit_bzero(__VA_ARGS__) +#define ALPHABET_LENGTH 27 +#define KBD_LETTERS "qwertyuiopasdfghjklzxcvbnm" + #define BIP39_WORDLIST_LENGTH 11068 #define BIP39_WORDLIST_OFFSETS_LENGTH 2049 #define BIP39_MNEMONIC_LENGTH 8 #define SSKR_WORDLIST_LENGTH 1024 -#define SSKR_MNEMONIC_LENGTH 4 +#define SSKR_BYTEWORD_LENGTH 4 #ifdef HAVE_ELECTRUM @@ -42,7 +45,7 @@ extern unsigned char const WIDE BIP39_MNEMONIC[BIP39_MNEMONIC_LENGTH]; extern unsigned char const WIDE SSKR_WORDLIST[SSKR_WORDLIST_LENGTH]; extern unsigned short const WIDE SSKR_WORDLIST_OFFSET; -extern unsigned char const WIDE SSKR_MNEMONIC[SSKR_MNEMONIC_LENGTH]; +extern unsigned char const WIDE SSKR_BYTEWORD[SSKR_BYTEWORD_LENGTH]; #ifdef HAVE_ELECTRUM diff --git a/src/ux_common/onboarding_seed_sskr.c b/src/ux_common/onboarding_seed_sskr.c index 45d4aa0d..4e621cc2 100644 --- a/src/ux_common/onboarding_seed_sskr.c +++ b/src/ux_common/onboarding_seed_sskr.c @@ -34,19 +34,19 @@ int16_t bolos_ux_sskr_size_get(uint8_t bip39_onboarding_kind, return share_count_expected; } -unsigned int bolos_ux_sskr_hex_decode(unsigned char *mnemonic_hex, - unsigned int mnemonic_length, - unsigned int sskr_shares_count, - unsigned char *output) { +unsigned int bolos_ux_sskr_combine(unsigned char *sskr_shares_hex, + unsigned int sskr_shares_hex_length, + unsigned int sskr_shares_count, + unsigned char *output) { const uint8_t *ptr_sskr_shares[SSKR_MAX_GROUP_COUNT * SSS_MAX_SHARE_COUNT]; - uint8_t sskr_share_len = mnemonic_hex[3] & 0x1F; + uint8_t sskr_share_len = sskr_shares_hex[3] & 0x1F; if (sskr_share_len > 23) { - sskr_share_len = mnemonic_hex[4]; + sskr_share_len = sskr_shares_hex[4]; } for (uint8_t i = 0; i < (uint8_t) sskr_shares_count; i++) { - ptr_sskr_shares[i] = - mnemonic_hex + (i * mnemonic_length / sskr_shares_count) + 4 + (sskr_share_len > 23); + ptr_sskr_shares[i] = sskr_shares_hex + (i * sskr_shares_hex_length / sskr_shares_count) + + 4 + (sskr_share_len > 23); } uint16_t output_len = sskr_combine_shards(ptr_sskr_shares, @@ -56,7 +56,7 @@ unsigned int bolos_ux_sskr_hex_decode(unsigned char *mnemonic_hex, SSKR_MAX_STRENGTH_BYTES); if (output_len < 1) { - memzero(mnemonic_hex, sizeof(mnemonic_hex)); + memzero(sskr_shares_hex, sizeof(sskr_shares_hex)); return 0; } @@ -64,17 +64,19 @@ unsigned int bolos_ux_sskr_hex_decode(unsigned char *mnemonic_hex, return (unsigned int) output_len; } -void bolos_ux_sskr_hex_to_seed(unsigned char *mnemonic_hex, - unsigned int mnemonic_length, - unsigned int sskr_shares_count, - unsigned char *words_buffer, - unsigned int *words_buffer_length, - unsigned char *seed) { - PRINTF("SSKR share in hex:\n %.*H\n", mnemonic_length, mnemonic_hex); +void bolos_ux_sskr_to_seed_convert(unsigned char *sskr_shares_hex, + unsigned int sskr_shares_hex_length, + unsigned int sskr_shares_count, + unsigned char *words_buffer, + unsigned int *words_buffer_length, + unsigned char *seed) { + PRINTF("SSKR share in hex:\n %.*H\n", sskr_shares_hex_length, sskr_shares_hex); uint8_t seed_buffer[SSKR_MAX_STRENGTH_BYTES] = {0}; - uint8_t seed_buffer_len = - bolos_ux_sskr_hex_decode(mnemonic_hex, mnemonic_length, sskr_shares_count, seed_buffer); + uint8_t seed_buffer_len = bolos_ux_sskr_combine(sskr_shares_hex, + sskr_shares_hex_length, + sskr_shares_count, + seed_buffer); *words_buffer_length = bolos_ux_bip39_mnemonic_encode(seed_buffer, (uint8_t) seed_buffer_len, @@ -134,22 +136,22 @@ unsigned int bolos_ux_sskr_generate(uint8_t groups_threshold, return share_count; } -unsigned int bolos_ux_sskr_mnemonic_encode(unsigned char *input, - unsigned int input_len, - unsigned char *output, - unsigned int output_len) { +unsigned int bolos_ux_sskr_share_hex_decode(unsigned char *input, + unsigned int input_len, + unsigned char *output, + unsigned int output_len) { unsigned int position = 0; unsigned int offset = 0; for (uint8_t i = 0; i < (uint8_t) input_len; i++) { - offset = SSKR_MNEMONIC_LENGTH * input[i]; - if (position + SSKR_MNEMONIC_LENGTH <= output_len) { - memcpy(output + position, SSKR_WORDLIST + offset, SSKR_MNEMONIC_LENGTH); + offset = SSKR_BYTEWORD_LENGTH * input[i]; + if (position + SSKR_BYTEWORD_LENGTH <= output_len) { + memcpy(output + position, SSKR_WORDLIST + offset, SSKR_BYTEWORD_LENGTH); } else { memzero(output, sizeof(output)); return 0; } - position += SSKR_MNEMONIC_LENGTH; + position += SSKR_BYTEWORD_LENGTH; if (position < output_len) { output[position++] = ' '; } @@ -158,13 +160,23 @@ unsigned int bolos_ux_sskr_mnemonic_encode(unsigned char *input, return position; } +unsigned int bolos_ux_sskr_byteword_to_hex(unsigned char *byteword) { + for (unsigned int i = 0; i < SSKR_WORDLIST_LENGTH; i += SSKR_BYTEWORD_LENGTH) { + if (os_secure_memcmp((void *) (SSKR_WORDLIST + i), byteword, SSKR_BYTEWORD_LENGTH) == 0) { + return i / SSKR_BYTEWORD_LENGTH; + } + } + // no match, sry + return SSKR_WORDLIST_LENGTH / SSKR_BYTEWORD_LENGTH; +} + unsigned int bolos_ux_bip39_to_sskr_convert(unsigned char *bip39_words_buffer, unsigned int bip39_words_buffer_length, unsigned int bip39_onboarding_kind, unsigned int *group_descriptor, uint8_t *share_count, - unsigned char *mnemonics, - unsigned int *mnemonics_len) { + unsigned char *share_words_buffer, + unsigned int *share_words_buffer_length) { // get seed from bip39 mnemonic uint8_t seed_len = bip39_onboarding_kind * 4 / 3; uint8_t seed_buffer[SSKR_MAX_STRENGTH_BYTES + 1]; @@ -183,9 +195,9 @@ unsigned int bolos_ux_bip39_to_sskr_convert(unsigned char *bip39_words_buffer, groups_len, &share_len_expected); - uint16_t share_buffer_len = share_count_expected * share_len_expected; - uint8_t share_buffer[SSKR_MAX_GROUP_COUNT * SSS_MAX_SHARE_COUNT * - (SSKR_MAX_STRENGTH_BYTES + SSKR_METADATA_LENGTH_BYTES)]; + uint16_t share_hex_buffer_len = share_count_expected * share_len_expected; + uint8_t share_hex_buffer[SSKR_MAX_GROUP_COUNT * SSS_MAX_SHARE_COUNT * + (SSKR_MAX_STRENGTH_BYTES + SSKR_METADATA_LENGTH_BYTES)]; uint8_t share_len = 0; *share_count = bolos_ux_sskr_generate(groups_threshold, group_descriptor, @@ -193,8 +205,8 @@ unsigned int bolos_ux_bip39_to_sskr_convert(unsigned char *bip39_words_buffer, seed_buffer, seed_len, &share_len, - share_buffer, - share_buffer_len, + share_hex_buffer, + share_hex_buffer_len, share_len_expected, share_count_expected); memzero(seed_buffer, sizeof(seed_buffer)); @@ -219,77 +231,80 @@ unsigned int bolos_ux_bip39_to_sskr_convert(unsigned char *bip39_words_buffer, uint8_t cbor_share_crc_buffer[4 + SSKR_METADATA_LENGTH_BYTES + 1 + SSKR_MAX_STRENGTH_BYTES + 4]; - // mnemonics_len is space separated bytewords of cbor + share + checksum - *mnemonics_len = - ((cbor_len + share_len + checksum_len) * (SSKR_MNEMONIC_LENGTH + 1) - 1) * + // sskr_words_buffer is space separated bytewords of cbor + share + checksum + *share_words_buffer_length = + ((cbor_len + share_len + checksum_len) * (SSKR_BYTEWORD_LENGTH + 1) - 1) * *share_count; for (uint8_t share = 0; share < *share_count; share++) { memcpy(cbor_share_crc_buffer, cbor, cbor_len); memcpy(cbor_share_crc_buffer + cbor_len, - share_buffer + share_len * share, + share_hex_buffer + share_len * share, share_len); checksum = crc32_nbo(cbor_share_crc_buffer, cbor_len + share_len); memcpy(cbor_share_crc_buffer + cbor_len + share_len, &checksum, checksum_len); - if (bolos_ux_sskr_mnemonic_encode( + if (bolos_ux_sskr_share_hex_decode( cbor_share_crc_buffer, cbor_share_crc_buffer_len, - mnemonics + share * (*mnemonics_len / *share_count), - *mnemonics_len / *share_count) < 1) { - memzero(share_buffer, sizeof(share_buffer)); + share_words_buffer + share * (*share_words_buffer_length / *share_count), + *share_words_buffer_length / *share_count) < 1) { + memzero(share_hex_buffer, sizeof(share_hex_buffer)); memzero(cbor_share_crc_buffer, sizeof(cbor_share_crc_buffer)); - memzero(mnemonics, sizeof(mnemonics)); - mnemonics_len = 0; + memzero(share_words_buffer, sizeof(share_words_buffer)); + share_words_buffer_length = 0; memzero(bip39_words_buffer, sizeof(bip39_words_buffer)); return 0; } memzero(cbor_share_crc_buffer, sizeof(cbor_share_crc_buffer)); checksum = 0; } - memzero(share_buffer, sizeof(share_buffer)); + memzero(share_hex_buffer, sizeof(share_hex_buffer)); } } - memzero(bip39_words_buffer, sizeof(bip39_words_buffer)); + memzero(bip39_words_buffer, bip39_words_buffer_length); return 1; } -unsigned int bolos_ux_sskr_hex_check(unsigned char *mnemonic_hex, - unsigned int mnemonic_length, +unsigned int bolos_ux_sskr_hex_check(unsigned char *sskr_shares_hex, + unsigned int sskr_shares_hex_length, unsigned int sskr_shares_count) { uint8_t cbor[] = {0xD9, 0x9D, 0x75}; // CBOR Tag #6.40309 is D9 9D75 uint32_t checksum = 0; uint8_t checksum_len = sizeof(checksum); for (unsigned int i = 0; i < sskr_shares_count; i++) { - checksum = crc32_nbo(mnemonic_hex + i * (mnemonic_length / sskr_shares_count), - (mnemonic_length / sskr_shares_count) - checksum_len); + checksum = crc32_nbo(sskr_shares_hex + i * (sskr_shares_hex_length / sskr_shares_count), + (sskr_shares_hex_length / sskr_shares_count) - checksum_len); // First 8 bytes of all shares in group should be same // Test checksum - if ((os_secure_memcmp(cbor, mnemonic_hex + i * mnemonic_length / sskr_shares_count, 3) != - 0) || - (i > 0 && os_secure_memcmp(mnemonic_hex, - mnemonic_hex + i * mnemonic_length / sskr_shares_count, - 8) != 0) || - (os_secure_memcmp( - &checksum, - mnemonic_hex + ((mnemonic_length / sskr_shares_count) * (i + 1)) - checksum_len, - checksum_len) != 0)) { - memzero(mnemonic_hex, sizeof(mnemonic_hex)); + if ((os_secure_memcmp(cbor, + sskr_shares_hex + i * sskr_shares_hex_length / sskr_shares_count, + 3) != 0) || + (i > 0 && + os_secure_memcmp(sskr_shares_hex, + sskr_shares_hex + i * sskr_shares_hex_length / sskr_shares_count, + 8) != 0) || + (os_secure_memcmp(&checksum, + sskr_shares_hex + + ((sskr_shares_hex_length / sskr_shares_count) * (i + 1)) - + checksum_len, + checksum_len) != 0)) { + memzero(sskr_shares_hex, sizeof(sskr_shares_hex)); checksum = 0; return 0; }; checksum = 0; } - // alright hex decoded mnemonic is ok + // hex encoded shares are OK return 1; } unsigned int bolos_ux_sskr_idx_strcpy(unsigned int index, unsigned char *buffer) { - if (index < SSKR_WORDLIST_LENGTH / SSKR_MNEMONIC_LENGTH && buffer) { - size_t word_length = SSKR_MNEMONIC_LENGTH; - memcpy(buffer, SSKR_WORDLIST + SSKR_MNEMONIC_LENGTH * index, word_length); + if (index < SSKR_WORDLIST_LENGTH / SSKR_BYTEWORD_LENGTH && buffer) { + size_t word_length = SSKR_BYTEWORD_LENGTH; + memcpy(buffer, SSKR_WORDLIST + SSKR_BYTEWORD_LENGTH * index, word_length); buffer[word_length] = 0; // EOS return word_length; } @@ -298,13 +313,13 @@ unsigned int bolos_ux_sskr_idx_strcpy(unsigned int index, unsigned char *buffer) return 0; } -unsigned int bolos_ux_sskr_get_word_idx_starting_with(unsigned char *prefix, - unsigned int prefixlength) { +unsigned int bolos_ux_sskr_get_word_idx_starting_with(const unsigned char *prefix, + const unsigned int prefixlength) { unsigned int i; - for (i = 0; i < SSKR_WORDLIST_LENGTH / SSKR_MNEMONIC_LENGTH; i++) { + for (i = 0; i < SSKR_WORDLIST_LENGTH / SSKR_BYTEWORD_LENGTH; i++) { unsigned int j = 0; - while (j < (unsigned int) (SSKR_MNEMONIC_LENGTH) && j < prefixlength && - SSKR_WORDLIST[SSKR_MNEMONIC_LENGTH * i + j] == prefix[j]) { + while (j < (unsigned int) (SSKR_BYTEWORD_LENGTH) && j < prefixlength && + SSKR_WORDLIST[SSKR_BYTEWORD_LENGTH * i + j] == prefix[j]) { j++; } if (j == prefixlength) { @@ -312,17 +327,17 @@ unsigned int bolos_ux_sskr_get_word_idx_starting_with(unsigned char *prefix, } } // no match, sry - return SSKR_WORDLIST_LENGTH / SSKR_MNEMONIC_LENGTH; + return SSKR_WORDLIST_LENGTH / SSKR_BYTEWORD_LENGTH; } -unsigned int bolos_ux_sskr_get_word_count_starting_with(unsigned char *prefix, - unsigned int prefixlength) { +unsigned int bolos_ux_sskr_get_word_count_starting_with(const unsigned char *prefix, + const unsigned int prefixlength) { unsigned int i; unsigned int count = 0; - for (i = 0; i < SSKR_WORDLIST_LENGTH / SSKR_MNEMONIC_LENGTH; i++) { + for (i = 0; i < SSKR_WORDLIST_LENGTH / SSKR_BYTEWORD_LENGTH; i++) { unsigned int j = 0; - while (j < (unsigned int) (SSKR_MNEMONIC_LENGTH) && j < prefixlength && - SSKR_WORDLIST[SSKR_MNEMONIC_LENGTH * i + j] == prefix[j]) { + while (j < (unsigned int) (SSKR_BYTEWORD_LENGTH) && j < prefixlength && + SSKR_WORDLIST[SSKR_BYTEWORD_LENGTH * i + j] == prefix[j]) { j++; } if (j == prefixlength) { @@ -339,21 +354,21 @@ unsigned int bolos_ux_sskr_get_word_count_starting_with(unsigned char *prefix, // allocate at most 26 letters for next possibilities // algorithm considers the SSKR words are alphabetically ordered in the wordlist -unsigned int bolos_ux_sskr_get_word_next_letters_starting_with(unsigned char *prefix, +unsigned int bolos_ux_sskr_get_word_next_letters_starting_with(const unsigned char *prefix, unsigned int prefixlength, unsigned char *next_letters_buffer) { unsigned int i; unsigned int letter_count = 0; - for (i = 0; i < SSKR_WORDLIST_LENGTH / SSKR_MNEMONIC_LENGTH; i++) { + for (i = 0; i < SSKR_WORDLIST_LENGTH / SSKR_BYTEWORD_LENGTH; i++) { unsigned int j = 0; - while (j < (unsigned int) (SSKR_MNEMONIC_LENGTH) && j < prefixlength && - SSKR_WORDLIST[SSKR_MNEMONIC_LENGTH * i + j] == prefix[j]) { + while (j < (unsigned int) (SSKR_BYTEWORD_LENGTH) && j < prefixlength && + SSKR_WORDLIST[SSKR_BYTEWORD_LENGTH * i + j] == prefix[j]) { j++; } if (j == prefixlength) { - if (j < (unsigned int) (SSKR_MNEMONIC_LENGTH)) { + if (j < (unsigned int) (SSKR_BYTEWORD_LENGTH)) { // j is inc during previous loop, don't touch it - unsigned char next_letter = SSKR_WORDLIST[SSKR_MNEMONIC_LENGTH * i + j]; + unsigned char next_letter = SSKR_WORDLIST[SSKR_BYTEWORD_LENGTH * i + j]; // add the first next_letter inconditionnally if (letter_count == 0) { next_letters_buffer[0] = next_letter; @@ -375,3 +390,54 @@ unsigned int bolos_ux_sskr_get_word_next_letters_starting_with(unsigned char *pr // return number of matched word starting with the given prefix return letter_count; } + +#if defined(HAVE_NBGL) +#include + +size_t bolos_ux_sskr_fill_with_candidates(const unsigned char *startingChars, + const size_t startingCharsLength, + char wordCandidatesBuffer[], + const char *wordIndexorBuffer[]) { + PRINTF("Calculating nb of words starting with '%s' (size is '%d')\n", + startingChars, + startingCharsLength); + const size_t nbMatchingWords = + MIN(bolos_ux_sskr_get_word_count_starting_with(startingChars, startingCharsLength), + NB_MAX_SUGGESTION_BUTTONS); + PRINTF("'%d' words start with '%s'\n", nbMatchingWords, startingChars); + if (nbMatchingWords == 0) { + return 0; + } + size_t matchingWordIndex = + bolos_ux_sskr_get_word_idx_starting_with(startingChars, startingCharsLength); + size_t offset = 0; + for (size_t i = 0; i < nbMatchingWords; i++) { + unsigned char *const wordDest = (unsigned char *) (&wordCandidatesBuffer[0] + offset); + const size_t wordSize = bolos_ux_sskr_idx_strcpy(matchingWordIndex, wordDest); + matchingWordIndex++; + *(wordDest + wordSize) = '\0'; + offset += wordSize + 1; // + trailing '\0' size + wordIndexorBuffer[i] = (char *) wordDest; + } + return nbMatchingWords; +} + +uint32_t bolos_ux_sskr_get_keyboard_mask(const unsigned char *prefix, + const unsigned int prefixLength) { + uint32_t existing_mask = 0; + unsigned char next_letters[ALPHABET_LENGTH] = {0}; + PRINTF("Looking for letter candidates following '%s'\n", prefix); + const size_t nb_letters = + bolos_ux_sskr_get_word_next_letters_starting_with(prefix, prefixLength, next_letters); + next_letters[nb_letters] = '\0'; + PRINTF("Next letters are in: %s\n", next_letters); + for (int i = 0; i < ALPHABET_LENGTH; i++) { + for (size_t j = 0; j < nb_letters; j++) { + if (KBD_LETTERS[i] == next_letters[j]) { + existing_mask += 1 << i; + } + } + } + return (-1 ^ existing_mask); +} +#endif diff --git a/tests/deprecated/README.md b/tests/deprecated/README.md index 9f850fc3..67f2167b 100755 --- a/tests/deprecated/README.md +++ b/tests/deprecated/README.md @@ -62,12 +62,17 @@ Tests taken from the [SSKR Example & Test Vector]( https://github.com/Blockchain ```bash ./speculos.py ../app-seed-tool/build/nanos/bin/app.elf --model nanos --seed "fly mule excess resource treat plunge nose soda reflect adult ramp planet" ``` +##### nanos ```bash while read -a LINE do curl -d '{"action":"press-and-release"}' -X ${LINE[0]} http://127.0.0.1:5000${LINE[1]} done < ./test/speculos/nanos-sskr-128bit.test > /dev/null 2>&1 & ``` +##### stax +```bash +cut -d# -f1 ./tests/deprecated/stax-sskr-128bit.test | while read LINE ; do echo -n $n; curl -d "${LINE}" -X POST http://127.0.0.1:5000/finger; ((n++)); done > /dev/null 2>&1 & +``` #### Speculos automation ```bash ./speculos.py --automation file:../app-seed-tool/test/speculos/nanos-sskr-128bit.json ../app-seed-tool/build/nanos/bin/app.elf --model nanos --seed "fly mule excess resource treat plunge nose soda reflect adult ramp planet" diff --git a/tests/deprecated/stax-sskr-128bit.test b/tests/deprecated/stax-sskr-128bit.test new file mode 100755 index 00000000..c62e2d12 --- /dev/null +++ b/tests/deprecated/stax-sskr-128bit.test @@ -0,0 +1,234 @@ +{"action": "press-and-release", "x": 106, "y": 510} +{"action": "press-and-release", "x": 212, "y": 510} +{"action": "press-and-release", "x": 180, "y": 464} # t +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 220, "y": 576} # n +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 220, "y": 576} # n +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 60, "y": 576} # x +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 320, "y": 520} # k +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 200, "y": 520} # g +{"action": "press-and-release", "x": 220, "y": 464} # y +{"action": "press-and-release", "x": 140, "y": 464} # r +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 380, "y": 464} # p +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 180, "y": 576} # b +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 180, "y": 576} # b +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 280, "y": 520} # j +{"action": "press-and-release", "x": 340, "y": 464} # o +{"action": "press-and-release", "x": 60, "y": 464} # w +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 240, "y": 520} # h +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 120, "y": 520} # d +{"action": "press-and-release", "x": 140, "y": 464} # r +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 280, "y": 520} # j +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 120, "y": 520} # d +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 380, "y": 464} # p +{"action": "press-and-release", "x": 340, "y": 464} # o +{"action": "press-and-release", "x": 340, "y": 464} # o +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 340, "y": 464} # o +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 320, "y": 520} # k +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 120, "y": 520} # d +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 80, "y": 520} # s +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 140, "y": 464} # r +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 80, "y": 520} # s +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 160, "y": 520} # f +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 140, "y": 576} # v +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 180, "y": 464} # t +{"action": "press-and-release", "x": 60, "y": 464} # w +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 140, "y": 576} # v +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 140, "y": 464} # r +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 380, "y": 464} # p +{"action": "press-and-release", "x": 340, "y": 464} # o +{"action": "press-and-release", "x": 80, "y": 520} # s +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 380, "y": 464} # p +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 60, "y": 464} # w +{"action": "press-and-release", "x": 240, "y": 520} # h +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 280, "y": 520} # j +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 260, "y": 576} # m +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 280, "y": 520} # j +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 140, "y": 464} # r +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 180, "y": 464} # t +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 220, "y": 576} # n +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 220, "y": 576} # n +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 60, "y": 576} # x +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 320, "y": 520} # k +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 200, "y": 520} # g +{"action": "press-and-release", "x": 220, "y": 464} # y +{"action": "press-and-release", "x": 140, "y": 464} # r +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 380, "y": 464} # p +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 180, "y": 576} # b +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 200, "y": 520} # g +{"action": "press-and-release", "x": 140, "y": 464} # r +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 20, "y": 464} # q +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 320, "y": 520} # k +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 60, "y": 464} # w +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 320, "y": 520} # k +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 380, "y": 464} # p +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 120, "y": 520} # d +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 260, "y": 576} # m +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 380, "y": 464} # p +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 140, "y": 464} # r +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 160, "y": 520} # f +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 120, "y": 520} # d +{"action": "press-and-release", "x": 300, "y": 464} # i +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 180, "y": 576} # b +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 200, "y": 520} # g +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 260, "y": 576} # m +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 220, "y": 464} # y +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 220, "y": 576} # n +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 160, "y": 520} # f +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 140, "y": 464} # r +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 180, "y": 576} # b +{"action": "press-and-release", "x": 260, "y": 464} # u +{"action": "press-and-release", "x": 360, "y": 520} # l +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 200, "y": 520} # g +{"action": "press-and-release", "x": 100, "y": 464} # e +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 280, "y": 520} # j +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 120, "y": 520} # d +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 220, "y": 576} # n +{"action": "press-and-release", "x": 40, "y": 520} # a +{"action": "press-and-release", "x": 140, "y": 576} # v +{"action": "press-and-release", "x": 100, "y": 280} +{"action": "press-and-release", "x": 100, "y": 576} # c +{"action": "press-and-release", "x": 340, "y": 464} # o +{"action": "press-and-release", "x": 80, "y": 520} # s +{"action": "press-and-release", "x": 100, "y": 280} diff --git a/tests/unit/tests/roundtrip.c b/tests/unit/tests/roundtrip.c index 48661cfc..a91ab4a8 100644 --- a/tests/unit/tests/roundtrip.c +++ b/tests/unit/tests/roundtrip.c @@ -85,10 +85,10 @@ uint8_t sskr_hex[] = {0xD9, 0x9D, 0x75, 0x58, 0x25, 0x01, 0x00, 0x00, unsigned int sskr_group_descriptor[] = {2, 3}; -unsigned int bolos_ux_sskr_hex_decode(unsigned char *mnemonic_hex, - unsigned int mnemonic_length, - unsigned int sskr_shares_count, - unsigned char *output); +unsigned int bolos_ux_sskr_combine(unsigned char *sskr_shares_hex, + unsigned int sskr_shares_hex_length, + unsigned int sskr_shares_count, + unsigned char *output); static void test_bip39_to_sskr(void **state) { @@ -124,10 +124,10 @@ static void test_bip39_to_sskr(void **state) { static void test_sskr_to_bip39(void **state) { uint8_t seed_buffer[32]; - uint8_t seed_buffer_len = bolos_ux_sskr_hex_decode(sskr_hex, - sizeof(sskr_hex), - sskr_group_descriptor[0], - seed_buffer); + uint8_t seed_buffer_len = bolos_ux_sskr_combine(sskr_hex, + sizeof(sskr_hex), + sskr_group_descriptor[0], + seed_buffer); unsigned char bip39_word_buffer[sizeof(bip39_mnemonic)]; int16_t bip39_word_buffer_len = sizeof(bip39_word_buffer);