Skip to content

Commit

Permalink
Add Stax layouts for checking SSKR shares
Browse files Browse the repository at this point in the history
  • Loading branch information
aido committed Aug 18, 2024
1 parent 8db0a31 commit 72f5701
Show file tree
Hide file tree
Showing 16 changed files with 870 additions and 313 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
5 changes: 3 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
14 changes: 7 additions & 7 deletions src/nano/nanos_enter_phrase.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
14 changes: 7 additions & 7 deletions src/nano/nanox_enter_phrase.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down
25 changes: 22 additions & 3 deletions src/stax/bip39_mnemonic.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
#include <string.h>

#include "../ux_common/common_bip39.h"
#include "../ux_common/common_sskr.h"
#include "./bip39_mnemonic.h"
#include "./sskr_shares.h"

#if defined(TARGET_STAX)

Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/stax/bip39_mnemonic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
112 changes: 112 additions & 0 deletions src/stax/sskr_shares.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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) {
Expand All @@ -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;
}
Expand Down
35 changes: 34 additions & 1 deletion src/stax/sskr_shares.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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
*/
Expand Down
Loading

0 comments on commit 72f5701

Please sign in to comment.