From 8db0a31479fe6bcd7c9e19b7d061e3c05b7dd98e Mon Sep 17 00:00:00 2001 From: Aido Date: Tue, 16 Jul 2024 16:36:23 +0100 Subject: [PATCH] Add Stax layouts for generating SSKR shares --- CHANGELOG.md | 8 +- Makefile | 4 +- README.md | 17 +- src/constants.h | 10 +- src/nano/ui_nano.c | 6 +- src/stax/{mnemonic.c => bip39_mnemonic.c} | 55 +-- src/stax/{mnemonic.h => bip39_mnemonic.h} | 34 +- src/stax/layout_generic_screen.c | 2 +- src/stax/sskr_shares.c | 75 +++++ src/stax/sskr_shares.h | 72 ++++ src/stax/ui_stax.c | 388 +++++++++++++--------- src/ux_common/onboarding_seed_sskr.c | 4 +- tests/deprecated/stax-bip39-12-word.test | 24 +- tests/unit/CMakeLists.txt | 2 +- tests/unit/tests/roundtrip.c | 2 +- 15 files changed, 468 insertions(+), 235 deletions(-) rename src/stax/{mnemonic.c => bip39_mnemonic.c} (61%) rename src/stax/{mnemonic.h => bip39_mnemonic.h} (68%) create mode 100644 src/stax/sskr_shares.c create mode 100644 src/stax/sskr_shares.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 07702abe..f4180406 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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 + +### Added + +- Add Stax layouts for generating SSKR shares + ## [1.7.4] - 2024-06-20 ### Fixed @@ -28,7 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Using Ledger SDK `cx_crc32()` function rather than buggy `cx_crc32_hw()`. - Fix build with SDK master for Nano S -## [1.7.1] - 2024-03-06 +## [1.7.1] - 2024-04-06 ### Changed diff --git a/Makefile b/Makefile index 812c4600..f610ec0d 100755 --- a/Makefile +++ b/Makefile @@ -27,8 +27,8 @@ all: default APPNAME = "Seed Tool" APPVERSION_M = 1 -APPVERSION_N = 7 -APPVERSION_P = 4 +APPVERSION_N = 8 +APPVERSION_P = 0 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" APPVERSION_RC = 0 diff --git a/README.md b/README.md index c2f03761..124a8f71 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,10 @@ [![License](https://img.shields.io/github/license/aido/app-seed-tool)](https://github.com/aido/app-seed-tool/blob/develop/LICENSE) ![nanos](https://img.shields.io/badge/nanos-working-green?logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iMTQ3IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDE0NyAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTAgOTEuNjU0OFYxMjhINTUuMjkzVjExOS45NEg4LjA1NjMxVjkxLjY1NDhIMFpNMTM4Ljk0NCA5MS42NTQ4VjExOS45NEg5MS43MDdWMTI3Ljk5OEgxNDdWOTEuNjU0OEgxMzguOTQ0Wk01NS4zNzMzIDM2LjM0NTJWOTEuNjUyOUg5MS43MDdWODQuMzg0Mkg2My40Mjk2VjM2LjM0NTJINTUuMzczM1pNMCAwVjM2LjM0NTJIOC4wNTYzMVY4LjA1ODQ0SDU1LjI5M1YwSDBaTTkxLjcwNyAwVjguMDU4NDRIMTM4Ljk0NFYzNi4zNDUySDE0N1YwSDkxLjcwN1oiIGZpbGw9IndoaXRlIi8+PC9zdmc+) -![nanox](https://img.shields.io/badge/nanox-working-green?logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iMTQ3IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDE0NyAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTAgOTEuNjU0OFYxMjhINTUuMjkzVjExOS45NEg4LjA1NjMxVjkxLjY1NDhIMFpNMTM4Ljk0NCA5MS42NTQ4VjExOS45NEg5MS43MDdWMTI3Ljk5OEgxNDdWOTEuNjU0OEgxMzguOTQ0Wk01NS4zNzMzIDM2LjM0NTJWOTEuNjUyOUg5MS43MDdWODQuMzg0Mkg2My40Mjk2VjM2LjM0NTJINTUuMzczM1pNMCAwVjM2LjM0NTJIOC4wNTYzMVY4LjA1ODQ0SDU1LjI5M1YwSDBaTTkxLjcwNyAwVjguMDU4NDRIMTM4Ljk0NFYzNi4zNDUySDE0N1YwSDkxLjcwN1oiIGZpbGw9IndoaXRlIi8+PC9zdmc+) ![nanosp](https://img.shields.io/badge/nanosp-working-green?logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iMTQ3IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDE0NyAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTAgOTEuNjU0OFYxMjhINTUuMjkzVjExOS45NEg4LjA1NjMxVjkxLjY1NDhIMFpNMTM4Ljk0NCA5MS42NTQ4VjExOS45NEg5MS43MDdWMTI3Ljk5OEgxNDdWOTEuNjU0OEgxMzguOTQ0Wk01NS4zNzMzIDM2LjM0NTJWOTEuNjUyOUg5MS43MDdWODQuMzg0Mkg2My40Mjk2VjM2LjM0NTJINTUuMzczM1pNMCAwVjM2LjM0NTJIOC4wNTYzMVY4LjA1ODQ0SDU1LjI5M1YwSDBaTTkxLjcwNyAwVjguMDU4NDRIMTM4Ljk0NFYzNi4zNDUySDE0N1YwSDkxLjcwN1oiIGZpbGw9IndoaXRlIi8+PC9zdmc+) +![nanox](https://img.shields.io/badge/nanox-working-green?logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iMTQ3IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDE0NyAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTAgOTEuNjU0OFYxMjhINTUuMjkzVjExOS45NEg4LjA1NjMxVjkxLjY1NDhIMFpNMTM4Ljk0NCA5MS42NTQ4VjExOS45NEg5MS43MDdWMTI3Ljk5OEgxNDdWOTEuNjU0OEgxMzguOTQ0Wk01NS4zNzMzIDM2LjM0NTJWOTEuNjUyOUg5MS43MDdWODQuMzg0Mkg2My40Mjk2VjM2LjM0NTJINTUuMzczM1pNMCAwVjM2LjM0NTJIOC4wNTYzMVY4LjA1ODQ0SDU1LjI5M1YwSDBaTTkxLjcwNyAwVjguMDU4NDRIMTM4Ljk0NFYzNi4zNDUySDE0N1YwSDkxLjcwN1oiIGZpbGw9IndoaXRlIi8+PC9zdmc+) ![stax](https://img.shields.io/badge/stax-in_progress-orange?logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iMTQ3IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDE0NyAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTAgOTEuNjU0OFYxMjhINTUuMjkzVjExOS45NEg4LjA1NjMxVjkxLjY1NDhIMFpNMTM4Ljk0NCA5MS42NTQ4VjExOS45NEg5MS43MDdWMTI3Ljk5OEgxNDdWOTEuNjU0OEgxMzguOTQ0Wk01NS4zNzMzIDM2LjM0NTJWOTEuNjUyOUg5MS43MDdWODQuMzg0Mkg2My40Mjk2VjM2LjM0NTJINTUuMzczM1pNMCAwVjM2LjM0NTJIOC4wNTYzMVY4LjA1ODQ0SDU1LjI5M1YwSDBaTTkxLjcwNyAwVjguMDU4NDRIMTM4Ljk0NFYzNi4zNDUySDE0N1YwSDkxLjcwN1oiIGZpbGw9IndoaXRlIi8+PC9zdmc+) +![flex](https://img.shields.io/badge/flex-in_progress-orange?logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iMTQ3IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDE0NyAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTAgOTEuNjU0OFYxMjhINTUuMjkzVjExOS45NEg4LjA1NjMxVjkxLjY1NDhIMFpNMTM4Ljk0NCA5MS42NTQ4VjExOS45NEg5MS43MDdWMTI3Ljk5OEgxNDdWOTEuNjU0OEgxMzguOTQ0Wk01NS4zNzMzIDM2LjM0NTJWOTEuNjUyOUg5MS43MDdWODQuMzg0Mkg2My40Mjk2VjM2LjM0NTJINTUuMzczM1pNMCAwVjM2LjM0NTJIOC4wNTYzMVY4LjA1ODQ0SDU1LjI5M1YwSDBaTTkxLjcwNyAwVjguMDU4NDRIMTM4Ljk0NFYzNi4zNDUySDE0N1YwSDkxLjcwN1oiIGZpbGw9IndoaXRlIi8+PC9zdmc+) [![Build app-seed-tool](https://github.com/aido/app-seed-tool/actions/workflows/ci-workflow.yml/badge.svg)](https://github.com/aido/app-seed-tool/actions/workflows/ci-workflow.yml) [![CodeQL](https://github.com/aido/app-seed-tool/actions/workflows/codeql-workflow.yml/badge.svg)](https://github.com/aido/app-seed-tool/actions/workflows/codeql-workflow.yml) @@ -23,13 +24,13 @@ Use the utilities provided by this Ledger application to check a backed up BIP-3 Not all Ledger devices are equal. The older, less capable devices do not have the capacity to provide a full range of seed utilities. The following table lists the seed utilities provided by each devices type:
-||Nano S|Nano S+|Nano X|Stax| -| :--- | :---: | :---: | :---: | :---: | -|[Check BIP39](#check-bip39)|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{green}✓}$$| -|[Check Shamir's secret shares](#check-shamirs-secret-shares)|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{orange}✓}$$| -|[Generate Shamir's secret sharing](#generate-shamirs-secret-sharing)|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{orange}✓}$$| -|[Recover BIP39](#recover-bip39)|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{orange}✓}$$| -|[Generate BIP85](#generate-bip85)|$${\color{red}✗}$$|$${\color{orange}✓}$$|$${\color{orange}✓}$$|$${\color{orange}✓}$$| +||Nano S|Nano S+|Nano X|Stax|Flex| +| :--- | :---: | :---: | :---: | :---: | :---: | +|[Check BIP39](#check-bip39)|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{orange}✓}$$| +|[Check Shamir's secret shares](#check-shamirs-secret-shares)|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{orange}✓}$$|$${\color{orange}✓}$$| +|[Generate Shamir's secret sharing](#generate-shamirs-secret-sharing)|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{orange}✓}$$| +|[Recover BIP39](#recover-bip39)|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{green}✓}$$|$${\color{orange}✓}$$|$${\color{orange}✓}$$| +|[Generate BIP85](#generate-bip85)|$${\color{red}✗}$$|$${\color{orange}✓}$$|$${\color{orange}✓}$$|$${\color{orange}✓}$$|$${\color{orange}✓}$$|
## Application menu flow diff --git a/src/constants.h b/src/constants.h index edb43252..0e65448a 100644 --- a/src/constants.h +++ b/src/constants.h @@ -19,13 +19,13 @@ #define ONBOARDING_WORD_COMPLETION_MAX_ITEMS 8 #define BOLOS_UX_HASH_LENGTH 4 // as on the blue -#define MAX_WORD_LENGTH 8 -#define MAX_NUMBER_LENGTH 2 +#define BIP39_MAX_WORD_LENGTH 8 +#define MAX_NUMBER_LENGTH 2 enum { - MNEMONIC_SIZE_12 = 12, - MNEMONIC_SIZE_18 = 18, - MNEMONIC_SIZE_24 = 24, + BIP39_MNEMONIC_SIZE_12 = 12, + BIP39_MNEMONIC_SIZE_18 = 18, + BIP39_MNEMONIC_SIZE_24 = 24, }; // Type of onboarding we are performing (BIP39 or SSKR) diff --git a/src/nano/ui_nano.c b/src/nano/ui_nano.c index 87ccbfc4..b50a370c 100644 --- a/src/nano/ui_nano.c +++ b/src/nano/ui_nano.c @@ -35,13 +35,13 @@ const char* number_of_bip39_words_getter(unsigned int idx) { void number_of_bip39_words_selector(unsigned int idx) { switch (idx) { case 0: - G_bolos_ux_context.onboarding_kind = MNEMONIC_SIZE_12; + G_bolos_ux_context.onboarding_kind = BIP39_MNEMONIC_SIZE_12; goto word_init; case 1: - G_bolos_ux_context.onboarding_kind = MNEMONIC_SIZE_18; + G_bolos_ux_context.onboarding_kind = BIP39_MNEMONIC_SIZE_18; goto word_init; case 2: - G_bolos_ux_context.onboarding_kind = MNEMONIC_SIZE_24; + G_bolos_ux_context.onboarding_kind = BIP39_MNEMONIC_SIZE_24; goto word_init; word_init: screen_onboarding_bip39_restore_init(); diff --git a/src/stax/mnemonic.c b/src/stax/bip39_mnemonic.c similarity index 61% rename from src/stax/mnemonic.c rename to src/stax/bip39_mnemonic.c index 7167e5af..3faec3d5 100644 --- a/src/stax/mnemonic.c +++ b/src/stax/bip39_mnemonic.c @@ -2,24 +2,24 @@ #include #include "../ux_common/common_bip39.h" -#include "./mnemonic.h" +#include "./bip39_mnemonic.h" #if defined(TARGET_STAX) -typedef struct buffer { +typedef struct bip39_buffer_struct { // the mnemonic passphrase, built over time - char buffer[MAX_MNEMONIC_LENGTH]; + char buffer[BIP39_MNEMONIC_MAX_LENGTH]; // current length of the mnemonic passphrase size_t length; // index of the current word ((size_t)-1 mean there is no word currently) size_t current_word_index; // array of every stored word lengths (used for removing them if needed) - size_t word_lengths[MNEMONIC_SIZE_24]; + size_t word_lengths[BIP39_MNEMONIC_SIZE_24]; // expected number of word in the final mnemonic (12 or 18 or 24) size_t final_size; -} buffer_t; +} bip39_buffer_t; -static buffer_t mnemonic = {0}; +static bip39_buffer_t mnemonic = {0}; size_t mnemonic_shrink(const size_t size) { if (size == 0 || size > mnemonic.length) { @@ -28,29 +28,29 @@ size_t mnemonic_shrink(const size_t size) { } else { mnemonic.length -= size; } - memzero(&mnemonic.buffer[mnemonic.length], MAX_MNEMONIC_LENGTH - mnemonic.length); + memzero(&mnemonic.buffer[mnemonic.length], BIP39_MNEMONIC_MAX_LENGTH - mnemonic.length); return mnemonic.length; } -void set_mnemonic_final_size(const size_t size) { +void bip39_mnemonic_final_size_set(const size_t size) { mnemonic.final_size = size; } -size_t get_mnemonic_final_size() { +size_t bip39_mnemonic_final_size_get(void) { return mnemonic.final_size; } -size_t get_current_word_number() { +size_t bip39_mnemonic_current_word_number_get(void) { return mnemonic.current_word_index + 1; } -void reset_mnemonic() { +void bip39_mnemonic_reset(void) { memzero(&mnemonic, sizeof(mnemonic)); mnemonic.current_word_index = (size_t) -1; } -bool remove_word_from_mnemonic() { - PRINTF("Removing a word, currently there is '%ld' of them\n", mnemonic.current_word_index + 1); +bool bip39_mnemonic_word_remove(void) { + PRINTF("Removing a word, currently there is '%d' of them\n", mnemonic.current_word_index + 1); if (mnemonic.current_word_index == (size_t) -1) { return false; } @@ -58,11 +58,11 @@ bool remove_word_from_mnemonic() { mnemonic.current_word_index--; // removing previous word from mnemonic buffer (+ 1 blank space) mnemonic_shrink(current_length + 1); - PRINTF("Number of remaining words in the mnemonic: '%ld'\n", mnemonic.current_word_index + 1); + PRINTF("Number of remaining words in the mnemonic: '%d'\n", mnemonic.current_word_index + 1); return true; } -size_t add_word_in_mnemonic(const char* const buffer, const size_t size) { +size_t bip39_mnemonic_word_add(const char* const buffer, const size_t size) { if (mnemonic.current_word_index != (size_t) -1) { // adding an extra white space ' ' between words mnemonic.buffer[mnemonic.length++] = ' '; @@ -72,35 +72,38 @@ size_t add_word_in_mnemonic(const char* const buffer, const size_t size) { mnemonic.length += size; mnemonic.current_word_index++; mnemonic.word_lengths[mnemonic.current_word_index] = size; - PRINTF("Number of words in the mnemonic: '%ld'\n", get_current_word_number()); + PRINTF("Number of words in the mnemonic: '%d'\n", bip39_mnemonic_current_word_number_get()); PRINTF("Current mnemonic: '%s'\n", &mnemonic.buffer[0]); - return get_current_word_number(); + return bip39_mnemonic_current_word_number_get(); } -bool is_mnemonic_complete() { +bool bip39_mnemonic_complete_check(void) { return (mnemonic.final_size == 0 ? false - : (mnemonic.current_word_index + 1) >= get_mnemonic_final_size()); + : (mnemonic.current_word_index + 1) >= bip39_mnemonic_final_size_get()); } -bool check_mnemonic() { - if (!is_mnemonic_complete()) { +bool bip39_mnemonic_check(void) { + if (!bip39_mnemonic_complete_check()) { return false; } - PRINTF("Checking the following mnemonic: '%s' (size %ld)\n", + PRINTF("Checking the following mnemonic: '%s' (size %d)\n", &mnemonic.buffer[0], 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 - // reset_mnemonic(); + // bip39_mnemonic_reset(); return result; } -#if defined(TEST) -char* get_mnemonic() { +// Used for BIP39 <-> SSKR roundtrip +char* bip39_mnemonic_get(void) { return mnemonic.buffer; } -#endif + +size_t bip39_mnemonic_length_get(void) { + return mnemonic.length; +} #endif diff --git a/src/stax/mnemonic.h b/src/stax/bip39_mnemonic.h similarity index 68% rename from src/stax/mnemonic.h rename to src/stax/bip39_mnemonic.h index 7e47c01a..b0ab6454 100644 --- a/src/stax/mnemonic.h +++ b/src/stax/bip39_mnemonic.h @@ -21,52 +21,58 @@ #if defined(TARGET_STAX) -#define memzero(...) explicit_bzero(__VA_ARGS__) -#define MAX_MNEMONIC_LENGTH (MNEMONIC_SIZE_24 * (MAX_WORD_LENGTH + 1)) +#define memzero(...) explicit_bzero(__VA_ARGS__) +#define BIP39_MNEMONIC_MAX_LENGTH (BIP39_MNEMONIC_SIZE_24 * (BIP39_MAX_WORD_LENGTH + 1)) /* * Sets how many words are expected in the mnemonic passphrase */ -void set_mnemonic_final_size(const size_t size); +void bip39_mnemonic_final_size_set(const size_t size); /* * Returns how many words are expected in the mnemonic passphrase */ -size_t get_mnemonic_final_size(void); +size_t bip39_mnemonic_final_size_get(void); /* * Returns how many words are currently stored in the mnemonic */ -size_t get_current_word_number(void); +size_t bip39_mnemonic_current_word_number_get(void); /* * Check if the current number of words in the mnemonic fits the expected number of words */ -bool is_mnemonic_complete(void); +bool bip39_mnemonic_complete_check(void); /* * Check if the currently stored mnemonic generates the same seed as the current device's one */ -bool check_mnemonic(void); +bool bip39_mnemonic_check(void); /* * Erase all information and reset the indexes */ -void reset_mnemonic(void); +void bip39_mnemonic_reset(void); /* * Remove the latest word from the passphrase, returns true if there was at least one to remove, * else false (there was no word) */ -bool remove_word_from_mnemonic(void); +bool bip39_mnemonic_word_remove(void); /* * Adds a word in the passphrase, returns how many words are stored in the mnemonic */ -size_t add_word_in_mnemonic(const char* const buffer, const size_t size); +size_t bip39_mnemonic_word_add(const char* const buffer, const size_t size); -#if defined(TEST) -char* get_mnemonic(); -#endif +/* + * Returns the mnemonic passphrase + */ +char* bip39_mnemonic_get(void); + +/* + * Returns length of the mnemonic passphrase + */ +size_t bip39_mnemonic_length_get(void); -#endif // TARGET_NANOS +#endif // TARGET_STAX diff --git a/src/stax/layout_generic_screen.c b/src/stax/layout_generic_screen.c index 99ab710a..ffca1cd1 100644 --- a/src/stax/layout_generic_screen.c +++ b/src/stax/layout_generic_screen.c @@ -68,7 +68,7 @@ nbgl_button_t *generic_screen_set_back_button() { button->obj.area.height = BUTTON_DIAMETER; button->radius = BUTTON_RADIUS; button->text = NULL; - button->icon = &C_leftArrow32px; + button->icon = &C_Back_32px; button->obj.alignmentMarginX = 0; button->obj.alignmentMarginY = UPPER_MARGIN; button->obj.alignment = TOP_LEFT; diff --git a/src/stax/sskr_shares.c b/src/stax/sskr_shares.c new file mode 100644 index 00000000..cc253d87 --- /dev/null +++ b/src/stax/sskr_shares.c @@ -0,0 +1,75 @@ +#include +#include + +#include "../ux_common/common_sskr.h" +#include "./sskr_shares.h" +#include "./bip39_mnemonic.h" + +#if defined(TARGET_STAX) + +typedef struct sskr_buffer_struct { + // The SSKR shares, built over time + char buffer[SSKR_SHARES_MAX_LENGTH]; + // current length of the shares buffer + unsigned int length; + unsigned int group_descriptor[1][2]; + uint8_t count; +} sskr_buffer_t; + +static sskr_buffer_t shares = {0}; + +void sskr_sharenum_set(const uint8_t sharenum) { + shares.group_descriptor[0][1] = sharenum; +} + +uint8_t sskr_sharenum_get(void) { + return shares.group_descriptor[0][1]; +} + +void sskr_threshold_set(const uint8_t threshold) { + shares.group_descriptor[0][0] = threshold; +} + +uint8_t sskr_threshold_get(void) { + return shares.group_descriptor[0][0]; +} + +uint8_t sskr_sharecount_get(void) { + return shares.count; +} + +void sskr_shares_reset(void) { + memzero(&shares, sizeof(shares)); +} + +void sskr_shares_from_bip39_mnemonic(void) { + shares.length = 0; + + bolos_ux_bip39_to_sskr_convert((unsigned char*) bip39_mnemonic_get(), + bip39_mnemonic_length_get(), + bip39_mnemonic_final_size_get(), + shares.group_descriptor[0], + &shares.count, + (unsigned char*) shares.buffer, + &shares.length); + + if (shares.count > 0) { + PRINTF("SSKR share count is %d\n", shares.count); + PRINTF("SSKR share buffer length is %d\n", shares.length); + for (uint8_t share = 0; share < shares.count; share++) { + PRINTF("SSKR share %d:\n", share + 1); + PRINTF("%.*s\n", + shares.length / shares.count, + shares.buffer + share * shares.length / shares.count); + } + } +} + +char* sskr_shares_get(void) { + return shares.buffer; +} + +size_t sskr_shares_length_get(void) { + return shares.length; +} +#endif diff --git a/src/stax/sskr_shares.h b/src/stax/sskr_shares.h new file mode 100644 index 00000000..47a8fc16 --- /dev/null +++ b/src/stax/sskr_shares.h @@ -0,0 +1,72 @@ +/******************************************************************************* + * (c) 2016-2022 Ledger SAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#pragma once + +#if defined(TARGET_STAX) + +#define memzero(...) explicit_bzero(__VA_ARGS__) + +// 16 shares * 229 chars per share (46 SSKR ByteWords) +#define SSKR_SHARES_MAX_LENGTH 3664 + +/* + * Sets the number of SSKR shares + */ +void sskr_sharenum_set(const uint8_t sharenum); + +/* + * Returns the SSKR shares + */ +uint8_t sskr_sharenum_get(void); + +/* + * Sets the SSKR threshold + */ +void sskr_threshold_set(const uint8_t threshold); + +/* + * Returns the SSKR threshold + */ +uint8_t sskr_threshold_get(void); + +/* + * Returns the SSKR share count + */ + +uint8_t sskr_sharecount_get(void); + +/* + * Erase all information and reset the indexes + */ +void sskr_shares_reset(void); + +/* + * Generate SSKR shares from BIP39 mnemonic + */ +void sskr_shares_from_bip39_mnemonic(void); + +/* + * Returns the generated SSKR shares + */ +char* sskr_shares_get(void); + +/* + * Returns the length of the SSKR shares buffer + */ +size_t sskr_shares_length_get(void); + +#endif // TARGET_STAX diff --git a/src/stax/ui_stax.c b/src/stax/ui_stax.c index cee37723..ed17e99a 100644 --- a/src/stax/ui_stax.c +++ b/src/stax/ui_stax.c @@ -15,7 +15,8 @@ #include "../ux_common/common_bip39.h" #include "../ui.h" -#include "./mnemonic.h" +#include "./bip39_mnemonic.h" +#include "./sskr_shares.h" #include "./layout_generic_screen.h" #define HEADER_SIZE 50 @@ -26,16 +27,15 @@ static char headerText[HEADER_SIZE] = {0}; static nbgl_layout_t *layout = 0; static void display_home_page(void); -static void display_select_bip39_passphrase_length_page(void); +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_numshares_keypad_page(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, - SELECT_MNEMONIC_SIZE_TOKEN, FIRST_SUGGESTION_TOKEN, - START_RECOVER_TOKEN, RESULT_TOKEN, }; @@ -45,7 +45,8 @@ enum bip39_check { static const char *buttonTexts[NB_MAX_SUGGESTION_BUTTONS] = {0}; static void reset_globals() { - reset_mnemonic(); + bip39_mnemonic_reset(); + sskr_shares_reset(); memzero(buttonTexts, sizeof(buttonTexts[0]) * NB_MAX_SUGGESTION_BUTTONS); } @@ -53,23 +54,6 @@ static void on_quit(void) { os_sched_exit(-1); } -/* - * About menu - */ -static const char *const infoTypes[] = {"Version", "Seed Tool"}; -static const char *const infoContents[] = {APPVERSION, "(c) 2023 Ledger"}; - -static bool on_infos(uint8_t page, nbgl_pageContent_t *content) { - if (page == 0) { - content->type = INFOS_LIST; - content->infosList.nbInfos = 2; - content->infosList.infoTypes = infoTypes; - content->infosList.infoContents = infoContents; - return true; - } - return false; -} - /* * Select recovery type, BIP39 or SSKR */ @@ -94,10 +78,10 @@ static void select_check_type_callback(nbgl_obj_t *obj, nbgl_touchType_t eventTy io_seproxyhal_play_tune(TUNE_TAP_CASUAL); if (obj == screenChildren[SELECT_CHECK_TYPE_BIP39_INDEX]) { nbgl_layoutRelease(layout); - display_select_bip39_passphrase_length_page(); + display_bip39_select_phrase_length_page(); } else if (obj == screenChildren[SELECT_CHECK_TYPE_SSKR_INDEX]) { nbgl_layoutRelease(layout); - display_home_page(); + nbgl_useCaseStatus("Under Construction\nComing soon", false, display_home_page); return; } else if (obj == screenChildren[SELECT_CHECK_TYPE_BACK_BUTTON_INDEX]) { nbgl_layoutRelease(layout); @@ -117,7 +101,7 @@ static void display_select_check_type_page(void) { 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 = - "Select type of\nRecovery you wish\nto check"; + "\nSelect type of\nRecovery you wish\nto check"; // create nb words buttons nbgl_objPoolGetArray(BUTTON, SELECT_CHECK_TYPE_NB_BUTTONS, @@ -165,10 +149,10 @@ static void select_generate_type_callback(nbgl_obj_t *obj, nbgl_touchType_t even io_seproxyhal_play_tune(TUNE_TAP_CASUAL); if (obj == screenChildren[SELECT_GENERATE_TYPE_BIP85_INDEX]) { nbgl_layoutRelease(layout); - display_home_page(); + nbgl_useCaseStatus("Under Construction\nComing soon", false, display_home_page); } else if (obj == screenChildren[SELECT_GENERATE_TYPE_SSKR_INDEX]) { nbgl_layoutRelease(layout); - display_numshares_keypad_page(); + display_sskr_select_numshares_page(); } else if (obj == screenChildren[SELECT_GENERATE_TYPE_BACK_BUTTON_INDEX]) { nbgl_layoutRelease(layout); display_home_page(); @@ -187,7 +171,7 @@ static void display_select_generate_type_page(void) { 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 = - "Select if you wish to\ngenerate SSKR shares\nor BIP85 children"; + "\nSelect if you wish to\ngenerate SSKR shares\nor BIP85 children"; // create nb words buttons nbgl_objPoolGetArray(BUTTON, SELECT_GENERATE_TYPE_NB_BUTTONS, @@ -214,33 +198,33 @@ static void display_select_generate_type_page(void) { /* * Select mnemonic size page */ -enum select_bip_passphrase_length { - SELECT_BIP39_PASSPHRASE_LENGTH_ICON_INDEX = 0, - SELECT_BIP39_PASSPHRASE_LENGTH_TEXT_INDEX, - SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_12_INDEX, - SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_18_INDEX, - SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_24_INDEX, - SELECT_BIP39_PASSPHRASE_LENGTH_BACK_BUTTON_INDEX, - SELECT_BIP39_PASSPHRASE_LENGTH_KBD_TEXT_TOKEN, - SELECT_BIP39_PASSPHRASE_LENGTH_NB_CHILDREN +enum select_bip39_phrase_length { + SELECT_BIP39_PHRASE_LENGTH_ICON_INDEX = 0, + SELECT_BIP39_PHRASE_LENGTH_TEXT_INDEX, + SELECT_BIP39_PHRASE_LENGTH_BUTTON_12_INDEX, + SELECT_BIP39_PHRASE_LENGTH_BUTTON_18_INDEX, + 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_PASSPHRASE_LENGTH_NB_BUTTONS 3 +#define SELECT_BIP39_PHRASE_LENGTH_NB_BUTTONS 3 static const char *bip39_passphraseLength[] = {"12 words", "18 words", "24 words"}; -static void select_bip39_passphrase_length_callback(nbgl_obj_t *obj, nbgl_touchType_t eventType) { +static void select_bip39_phrase_length_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_BIP39_PASSPHRASE_LENGTH_BUTTON_12_INDEX]) { - set_mnemonic_final_size(MNEMONIC_SIZE_12); - } else if (obj == screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_18_INDEX]) { - set_mnemonic_final_size(MNEMONIC_SIZE_18); - } else if (obj == screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_24_INDEX]) { - set_mnemonic_final_size(MNEMONIC_SIZE_24); - } else if (obj == screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BACK_BUTTON_INDEX]) { + if (obj == screenChildren[SELECT_BIP39_PHRASE_LENGTH_BUTTON_12_INDEX]) { + bip39_mnemonic_final_size_set(BIP39_MNEMONIC_SIZE_12); + } else if (obj == screenChildren[SELECT_BIP39_PHRASE_LENGTH_BUTTON_18_INDEX]) { + bip39_mnemonic_final_size_set(BIP39_MNEMONIC_SIZE_18); + } else if (obj == screenChildren[SELECT_BIP39_PHRASE_LENGTH_BUTTON_24_INDEX]) { + 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(); return; @@ -249,47 +233,46 @@ static void select_bip39_passphrase_length_callback(nbgl_obj_t *obj, nbgl_touchT display_bip39_keyboard_page(); } -static void display_select_bip39_passphrase_length_page(void) { +static void display_bip39_select_phrase_length_page(void) { nbgl_obj_t **screenChildren; // 3 buttons + icon + text + subText nbgl_screenSet(&screenChildren, 6, NULL, - (nbgl_touchCallback_t) &select_bip39_passphrase_length_callback); + (nbgl_touchCallback_t) &select_bip39_phrase_length_callback); - screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_ICON_INDEX] = + screenChildren[SELECT_BIP39_PHRASE_LENGTH_ICON_INDEX] = (nbgl_obj_t *) generic_screen_set_icon(&C_bip39_stax_64px); - screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_TEXT_INDEX] = - (nbgl_obj_t *) generic_screen_set_title( - screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_ICON_INDEX]); - ((nbgl_text_area_t *) screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_TEXT_INDEX])->text = - "How long is your\nBIP39 Recovery\nPhrase?"; + screenChildren[SELECT_BIP39_PHRASE_LENGTH_TEXT_INDEX] = (nbgl_obj_t *) generic_screen_set_title( + screenChildren[SELECT_BIP39_PHRASE_LENGTH_ICON_INDEX]); + ((nbgl_text_area_t *) screenChildren[SELECT_BIP39_PHRASE_LENGTH_TEXT_INDEX])->text = + "\nHow long is your\nBIP39 Recovery\nPhrase?"; // create nb words buttons nbgl_objPoolGetArray( BUTTON, - SELECT_BIP39_PASSPHRASE_LENGTH_NB_BUTTONS, + SELECT_BIP39_PHRASE_LENGTH_NB_BUTTONS, 0, - (nbgl_obj_t **) &screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_12_INDEX]); + (nbgl_obj_t **) &screenChildren[SELECT_BIP39_PHRASE_LENGTH_BUTTON_12_INDEX]); generic_screen_configure_buttons( - (nbgl_button_t **) &screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_12_INDEX], - SELECT_BIP39_PASSPHRASE_LENGTH_NB_BUTTONS); - ((nbgl_button_t *) screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_12_INDEX])->text = + (nbgl_button_t **) &screenChildren[SELECT_BIP39_PHRASE_LENGTH_BUTTON_12_INDEX], + SELECT_BIP39_PHRASE_LENGTH_NB_BUTTONS); + ((nbgl_button_t *) screenChildren[SELECT_BIP39_PHRASE_LENGTH_BUTTON_12_INDEX])->text = bip39_passphraseLength[0]; - ((nbgl_button_t *) screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_18_INDEX])->text = + ((nbgl_button_t *) screenChildren[SELECT_BIP39_PHRASE_LENGTH_BUTTON_18_INDEX])->text = bip39_passphraseLength[1]; - ((nbgl_button_t *) screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_24_INDEX])->text = + ((nbgl_button_t *) screenChildren[SELECT_BIP39_PHRASE_LENGTH_BUTTON_24_INDEX])->text = bip39_passphraseLength[2]; - ((nbgl_button_t *) screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_24_INDEX]) - ->borderColor = BLACK; - ((nbgl_button_t *) screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_24_INDEX])->innerColor = + ((nbgl_button_t *) screenChildren[SELECT_BIP39_PHRASE_LENGTH_BUTTON_24_INDEX])->borderColor = BLACK; - ((nbgl_button_t *) screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BUTTON_24_INDEX]) + ((nbgl_button_t *) screenChildren[SELECT_BIP39_PHRASE_LENGTH_BUTTON_24_INDEX])->innerColor = + BLACK; + ((nbgl_button_t *) screenChildren[SELECT_BIP39_PHRASE_LENGTH_BUTTON_24_INDEX]) ->foregroundColor = WHITE; // create back button - screenChildren[SELECT_BIP39_PASSPHRASE_LENGTH_BACK_BUTTON_INDEX] = + screenChildren[SELECT_BIP39_PHRASE_LENGTH_BACK_BUTTON_INDEX] = (nbgl_obj_t *) generic_screen_set_back_button(); nbgl_screenRedraw(); @@ -300,36 +283,40 @@ static void display_select_bip39_passphrase_length_page(void) { */ #define BUTTON_VMARGIN 32 -static char textToEnter[MAX_WORD_LENGTH + 1] = {0}; -static int textIndex, suggestionIndex, keyboardIndex = 0; +static char textToEnter[BIP39_MAX_WORD_LENGTH + 1] = {0}; +static int keyboardIndex = 0; +static nbgl_layoutKeyboardContent_t kbdContent; // the biggest word of BIP39 list is 8 char (9 with trailing '\0'), and // the max number of showed suggestions is NB_MAX_SUGGESTION_BUTTONS -static char wordCandidates[(MAX_WORD_LENGTH + 1) * NB_MAX_SUGGESTION_BUTTONS] = {0}; +static char wordCandidates[(BIP39_MAX_WORD_LENGTH + 1) * NB_MAX_SUGGESTION_BUTTONS] = {0}; -static void keyboard_dispatcher(const int token, uint8_t index __attribute__((unused))) { +static void keyboard_dispatcher(const int token, uint8_t index) { + UNUSED(index); if (token == BACK_BUTTON_TOKEN) { nbgl_layoutRelease(layout); - if (remove_word_from_mnemonic()) { + if (bip39_mnemonic_word_remove()) { display_bip39_keyboard_page(); } else { - display_select_bip39_passphrase_length_page(); + 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])); - add_word_in_mnemonic(buttonTexts[token - FIRST_SUGGESTION_TOKEN], - strlen(buttonTexts[token - FIRST_SUGGESTION_TOKEN])); - if (is_mnemonic_complete()) { - display_result_page(check_mnemonic()); + 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 +/* + * Function called when a key of keyboard is touched + */ static void key_press_callback(const char touchedKey) { size_t textLen = 0; uint32_t mask = 0; @@ -348,21 +335,21 @@ static void key_press_callback(const char touchedKey) { PRINTF("Current text is: '%s' (size '%d')\n", textToEnter, textLen); if (textLen == 0) { // no suggestion until there is at least 2 characters - nbgl_layoutUpdateSuggestionButtons(layout, suggestionIndex, 0, buttonTexts); + kbdContent.suggestionButtons.nbUsedButtons = 0; } else { const size_t nbMatchingWords = bolos_ux_bip39_fill_with_candidates((unsigned char *) &(textToEnter[0]), strlen(textToEnter), wordCandidates, buttonTexts); - nbgl_layoutUpdateSuggestionButtons(layout, suggestionIndex, nbMatchingWords, buttonTexts); + kbdContent.suggestionButtons.nbUsedButtons = nbMatchingWords; } if (textLen > 0) { mask = bolos_ux_bip39_get_keyboard_mask((unsigned char *) &(textToEnter[0]), strlen(textToEnter)); } nbgl_layoutUpdateKeyboard(layout, keyboardIndex, mask, false, LOWER_CASE); - nbgl_layoutUpdateEnteredText(layout, textIndex, false, 0, &(textToEnter[0]), false); + nbgl_layoutUpdateKeyboardContent(layout, &kbdContent); nbgl_refresh(); } @@ -373,58 +360,56 @@ static void display_bip39_keyboard_page() { .mode = MODE_LETTERS, // start in letters mode .keyMask = 0, // no inactive key .callback = &key_press_callback}; - nbgl_layoutCenteredInfo_t centeredInfo = {.text1 = NULL, - .text2 = headerText, // to use as "header" - .text3 = NULL, - .style = LARGE_CASE_INFO, - .icon = NULL, - .offsetY = 0, - .onTop = true}; + kbdContent.type = KEYBOARD_WITH_SUGGESTIONS; + kbdContent.title = headerText; + kbdContent.text = textToEnter; + kbdContent.numbered = true; + kbdContent.number = bip39_mnemonic_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.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", - get_current_word_number() + 1, - get_mnemonic_final_size()); - nbgl_layoutAddProgressIndicator(layout, 0, 0, true, BACK_BUTTON_TOKEN, TUNE_TAP_CASUAL); - nbgl_layoutAddCenteredInfo(layout, ¢eredInfo); + bip39_mnemonic_current_word_number_get() + 1, + bip39_mnemonic_final_size_get()); keyboardIndex = nbgl_layoutAddKeyboard(layout, &kbdInfo); - suggestionIndex = nbgl_layoutAddSuggestionButtons(layout, - 0, // no used buttons at start-up - buttonTexts, - FIRST_SUGGESTION_TOKEN, - TUNE_TAP_CASUAL); - textIndex = nbgl_layoutAddEnteredText(layout, - true, // numbered - get_current_word_number() + 1, // number to use - textToEnter, // text to display - false, // not grayed-out - BUTTON_VMARGIN, // vertical margin from the buttons - SELECT_BIP39_PASSPHRASE_LENGTH_KBD_TEXT_TOKEN); + nbgl_layoutAddProgressIndicator(layout, 0, 0, true, BACK_BUTTON_TOKEN, TUNE_TAP_CASUAL); + nbgl_layoutAddKeyboardContent(layout, &kbdContent); nbgl_layoutDraw(layout); } /* - * Home page & dispatcher + * Home page, infos & dispatcher */ - -static void display_settings_page() { - nbgl_useCaseSettings("Seed Tool infos", 0, 1, false, display_home_page, on_infos, NULL); -} +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() { reset_globals(); - nbgl_useCaseHomeExt("Seed Tool", - &C_seed_stax_64px, - "This app lets you enter a\nSecret Recovery Phrase and\ntest if it matches " - "the one\npresent on this Ledger Stax", - false, - "Recovery Check", - display_select_check_type_page, - display_settings_page, - on_quit); + + nbgl_homeAction_t action = {.text = "Recovery Check", + .callback = PIC(display_select_check_type_page)}; + + nbgl_useCaseHomeAndSettings( + APPNAME, + &C_seed_stax_64px, + "This Ledger application\nprovides some useful seed\nmanagement utilities.", + INIT_HOME_PAGE, + NULL, + &infoList, + &action, + on_quit); } /* @@ -435,11 +420,11 @@ static const char *possible_results[2][2] = { "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_warning64px, &C_round_check_64px}; +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 (is_mnemonic_complete()) { + if (bip39_mnemonic_complete_check()) { display_select_generate_type_page(); } else { reset_globals(); @@ -476,53 +461,138 @@ enum sskr_gen { SSKR_GEN_RESULT_TOKEN, }; -static char numToEnter[MAX_NUMBER_LENGTH + 1] = {0}; -static int keypadIndex = 0; +static void sskr_sharenum_validate(const uint8_t *sharenumentry, uint8_t length) { + // Code to validate the entered shares number -static void keypad_dispatcher(const int token, uint8_t index __attribute__((unused))) { + sskr_sharenum_set(0); + + for (uint8_t i = 0; i < length; i++) { + sskr_sharenum_set(10 * sskr_sharenum_get() + sharenumentry[i] - '0'); + } + + PRINTF("Number of shares entered is '%d'\n", sskr_sharenum_get()); + + if (sskr_sharenum_get() > 0 && sskr_sharenum_get() <= 16) { + display_sskr_select_threshold_page(); + } else { + nbgl_useCaseStatus("Number of SSKR shares must be between 1 and 16", + false, + display_select_generate_type_page); + } +} + +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) { - nbgl_layoutRelease(layout); display_select_generate_type_page(); - } else if (token >= FIRST_SUGGESTION_TOKEN) { - nbgl_layoutRelease(layout); - PRINTF("Selected number is '%d'\n", buttonTexts[token - FIRST_SUGGESTION_TOKEN]); } } -// function called when a key of keypad is touched -static void keypad_press_callback(const char touchedKey __attribute__((unused))) { - // Function currently a placeholder, remove unused attribute tag +void display_sskr_select_numshares_page() { + // Draw the keypad + nbgl_useCaseKeypadDigits("Enter number of SSKR shares\nto generate (1 - 16)", + 1, + MAX_NUMBER_LENGTH, + SSKR_GEN_BACK_BUTTON_TOKEN, + false, + TUNE_TAP_CASUAL, + sskr_sharenum_validate, + sskr_sharenum_entry_cb); } -static void display_numshares_keypad_page() { - nbgl_layoutDescription_t layoutDescription = {.modal = false, - .onActionCallback = &keypad_dispatcher}; - nbgl_layoutCenteredInfo_t centeredInfo = {.text1 = NULL, - .text2 = headerText, // to use as "header" - .text3 = NULL, - .style = LARGE_CASE_INFO, - .icon = NULL, - .offsetY = 0, - .onTop = true}; - numToEnter[0] = '\0'; - layout = nbgl_layoutGet(&layoutDescription); - snprintf(headerText, HEADER_SIZE, "Enter number of SSKR shares\nto generate (1 - 16)"); - nbgl_layoutAddProgressIndicator(layout, - 0, - 0, - true, - SSKR_GEN_BACK_BUTTON_TOKEN, - TUNE_TAP_CASUAL); - nbgl_layoutAddCenteredInfo(layout, ¢eredInfo); - keypadIndex = nbgl_layoutAddKeypad(layout, &keypad_press_callback, false); - textIndex = nbgl_layoutAddEnteredText(layout, - false, // numbered - 0, // number to use - numToEnter, // num to display - false, // not grayed-out - BUTTON_VMARGIN, // vertical margin from the buttons - SSKR_GEN_SELECT_THRESHOLD_TOKEN); - nbgl_layoutDraw(layout); +char item_buffer[15]; +char value_buffer[(SSKR_SHARES_MAX_LENGTH / 16) + 1]; + +static void review_sskr_shares_cancel(void) { + memzero(item_buffer, sizeof(item_buffer)); + memzero(value_buffer, sizeof(value_buffer)); + + display_select_generate_type_page(); +} + +static void review_sskr_shares_contentGetter(uint8_t index, nbgl_content_t *genericreview) { + static nbgl_layoutTagValue_t pairs[1]; + + genericreview->type = TAG_VALUE_LIST; + genericreview->contentActionCallback = NULL; + genericreview->content.tagValueList.nbPairs = 1; + genericreview->content.tagValueList.nbMaxLinesForValue = 0; + genericreview->content.tagValueList.pairs = (nbgl_layoutTagValue_t *) pairs; + + SPRINTF(item_buffer, "SSKR Share #%d", index + 1); + // Ensure null termination + item_buffer[sskr_sharecount_get() > 9 ? sizeof(item_buffer) - 1 : sizeof(item_buffer) - 2] = + '\0'; + pairs[0].item = item_buffer; + + strncpy(value_buffer, + sskr_shares_get() + (index * sskr_shares_length_get() / sskr_sharecount_get()), + sskr_shares_length_get() / sskr_sharecount_get()); + // Ensure null termination + value_buffer[sskr_shares_length_get() / sskr_sharecount_get()] = '\0'; + // Format output + for (uint8_t i = 19; i < (uint8_t)(sskr_shares_length_get() / sskr_sharecount_get()); i += 20) { + value_buffer[i] = '\n'; + } + pairs[0].value = value_buffer; +} + +static void display_sskr_shares(void) { + sskr_shares_from_bip39_mnemonic(); + + static nbgl_genericContents_t genericContent; + genericContent.callbackCallNeeded = true; + genericContent.contentGetterCallback = review_sskr_shares_contentGetter; + genericContent.nbContents = sskr_sharecount_get(); + + nbgl_useCaseGenericReview(&genericContent, "Cancel", review_sskr_shares_cancel); +} + +static void sskr_threshold_validate(const uint8_t *thresholdentry, uint8_t length) { + // Code to validate the entered threshold number + + sskr_threshold_set(0); + + for (uint8_t i = 0; i < length; i++) { + sskr_threshold_set(10 * sskr_threshold_get() + thresholdentry[i] - '0'); + } + + 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); + } 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); + } 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); + } else { + display_sskr_shares(); + } +} + +static void sskr_threshold_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_sskr_select_numshares_page(); + } +} + +void display_sskr_select_threshold_page() { + // Draw the keypad + nbgl_useCaseKeypadDigits("Enter threshold value", + 1, + MAX_NUMBER_LENGTH, + SSKR_GEN_BACK_BUTTON_TOKEN, + false, + TUNE_TAP_CASUAL, + sskr_threshold_validate, + sskr_threshold_entry_cb); } /* diff --git a/src/ux_common/onboarding_seed_sskr.c b/src/ux_common/onboarding_seed_sskr.c index 0c5774c9..45d4aa0d 100644 --- a/src/ux_common/onboarding_seed_sskr.c +++ b/src/ux_common/onboarding_seed_sskr.c @@ -70,7 +70,7 @@ void bolos_ux_sskr_hex_to_seed(unsigned char *mnemonic_hex, unsigned char *words_buffer, unsigned int *words_buffer_length, unsigned char *seed) { - PRINTF("SSKR mnemonic in hex:\n %.*H\n", mnemonic_length, mnemonic_hex); + PRINTF("SSKR share in hex:\n %.*H\n", mnemonic_length, mnemonic_hex); uint8_t seed_buffer[SSKR_MAX_STRENGTH_BYTES] = {0}; uint8_t seed_buffer_len = @@ -154,7 +154,7 @@ unsigned int bolos_ux_sskr_mnemonic_encode(unsigned char *input, output[position++] = ' '; } } - PRINTF("SSKR mnemonic phrase:\n %.*s\n", output_len, output); + PRINTF("SSKR share:\n %.*s\n", output_len, output); return position; } diff --git a/tests/deprecated/stax-bip39-12-word.test b/tests/deprecated/stax-bip39-12-word.test index ca0cee47..4f05894e 100755 --- a/tests/deprecated/stax-bip39-12-word.test +++ b/tests/deprecated/stax-bip39-12-word.test @@ -4,59 +4,59 @@ {"action": "press-and-release", "x": 160, "y": 520} # f {"action": "press-and-release", "x": 360, "y": 520} # l {"action": "press-and-release", "x": 220, "y": 464} # y -{"action": "press-and-release", "x": 88, "y": 320} +{"action": "press-and-release", "x": 100, "y": 280} {"action": "press-and-release", "x": 260, "y": 576} # m {"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": 464} # e -{"action": "press-and-release", "x": 88, "y": 320} +{"action": "press-and-release", "x": 100, "y": 280} {"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": 576} # c {"action": "press-and-release", "x": 100, "y": 464} # e -{"action": "press-and-release", "x": 88, "y": 320} +{"action": "press-and-release", "x": 100, "y": 280} {"action": "press-and-release", "x": 140, "y": 464} # r {"action": "press-and-release", "x": 100, "y": 464} # e {"action": "press-and-release", "x": 80, "y": 520} # s {"action": "press-and-release", "x": 340, "y": 464} # o -{"action": "press-and-release", "x": 88, "y": 320} +{"action": "press-and-release", "x": 100, "y": 280} {"action": "press-and-release", "x": 180, "y": 464} # t {"action": "press-and-release", "x": 140, "y": 464} # r {"action": "press-and-release", "x": 100, "y": 464} # e {"action": "press-and-release", "x": 40, "y": 520} # a -{"action": "press-and-release", "x": 88, "y": 320} +{"action": "press-and-release", "x": 100, "y": 280} {"action": "press-and-release", "x": 380, "y": 464} # p {"action": "press-and-release", "x": 360, "y": 520} # l {"action": "press-and-release", "x": 260, "y": 464} # u {"action": "press-and-release", "x": 220, "y": 576} # n -{"action": "press-and-release", "x": 88, "y": 320} +{"action": "press-and-release", "x": 100, "y": 280} {"action": "press-and-release", "x": 220, "y": 576} # n {"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": 464} # e -{"action": "press-and-release", "x": 88, "y": 320} +{"action": "press-and-release", "x": 100, "y": 280} {"action": "press-and-release", "x": 80, "y": 520} # s {"action": "press-and-release", "x": 340, "y": 464} # o {"action": "press-and-release", "x": 120, "y": 520} # d {"action": "press-and-release", "x": 40, "y": 520} # a -{"action": "press-and-release", "x": 88, "y": 320} +{"action": "press-and-release", "x": 100, "y": 280} {"action": "press-and-release", "x": 140, "y": 464} # r {"action": "press-and-release", "x": 100, "y": 464} # e {"action": "press-and-release", "x": 160, "y": 520} # f {"action": "press-and-release", "x": 360, "y": 520} # l -{"action": "press-and-release", "x": 88, "y": 320} +{"action": "press-and-release", "x": 100, "y": 280} {"action": "press-and-release", "x": 40, "y": 520} # a {"action": "press-and-release", "x": 120, "y": 520} # d {"action": "press-and-release", "x": 260, "y": 464} # u {"action": "press-and-release", "x": 360, "y": 520} # l -{"action": "press-and-release", "x": 88, "y": 320} +{"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": 260, "y": 576} # m {"action": "press-and-release", "x": 380, "y": 464} # p -{"action": "press-and-release", "x": 88, "y": 320} +{"action": "press-and-release", "x": 100, "y": 280} {"action": "press-and-release", "x": 380, "y": 464} # p {"action": "press-and-release", "x": 360, "y": 520} # l {"action": "press-and-release", "x": 40, "y": 520} # a {"action": "press-and-release", "x": 220, "y": 576} # n -{"action": "press-and-release", "x": 88, "y": 320} +{"action": "press-and-release", "x": 100, "y": 280} diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index fee8040a..2eafabb7 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -6,7 +6,7 @@ endif() # project information project(unit_tests - VERSION 1.7.3 + VERSION 1.8.0 DESCRIPTION "Unit tests for app-seed-tool Ledger Application" LANGUAGES C) diff --git a/tests/unit/tests/roundtrip.c b/tests/unit/tests/roundtrip.c index 9d69ed29..48661cfc 100644 --- a/tests/unit/tests/roundtrip.c +++ b/tests/unit/tests/roundtrip.c @@ -113,7 +113,7 @@ static void test_bip39_to_sskr(void **state) { assert_int_equal(bolos_ux_bip39_to_sskr_convert(bip39_word_buffer, sizeof(bip39_word_buffer) - 1, - MNEMONIC_SIZE_24, + BIP39_MNEMONIC_SIZE_24, sskr_group_descriptor, &share_count, sskr_words_buffer, &sskr_words_buffer_len), 1);