chore(crypto): Rework SLIP-39 word lookup.

poc/andrew-t1-keyboard
Andrew Kozlik 3 years ago committed by Andrew Kozlik
parent e3afd984ae
commit 55f3df6b0c

@ -27,8 +27,6 @@
#include <string.h>
#include "slip39_wordlist.h"
static uint16_t find(uint16_t prefix, bool find_index);
/**
* Returns word at position `index`.
*/
@ -65,34 +63,67 @@ bool word_index(uint16_t* index, const char* word, uint8_t word_length) {
}
/**
* Calculates which buttons on the T9 keyboard can still be pressed after the
* prefix was entered. Returns a 9-bit bitmask, where each bit specifies which
* buttons can be pressed (there are still words in this combination). The least
* significant bit corresponds to the first button.
*
* Example: 110000110 - second, third, eighth and ninth button still can be
* pressed.
* Returns the index of the first sequence in words_button_seq[] which is not
* less than the given sequence. Returns WORDS_COUNT if there is no such
* sequence.
*/
uint16_t slip39_word_completion_mask(uint16_t prefix) {
return find(prefix, false);
static uint16_t find_sequence(uint16_t sequence) {
if (sequence <= words_button_seq[0].sequence) {
return 0;
}
uint16_t lo = 0;
uint16_t hi = WORDS_COUNT;
while (hi - lo > 1) {
uint16_t mid = (hi + lo) / 2;
if (words_button_seq[mid].sequence >= sequence) {
hi = mid;
} else {
lo = mid;
}
}
return hi;
}
/**
* Returns the first word matching the button sequence prefix or NULL if no
* match is found.
* Returns a word matching the button sequence prefix or NULL if no match is
* found.
*/
const char* button_sequence_to_word(uint16_t prefix) {
return get_word(find(prefix, true));
const char* button_sequence_to_word(uint16_t sequence) {
if (sequence == 0) {
return slip39_wordlist[words_button_seq[0].index];
}
uint16_t multiplier = 1;
while (sequence < 1000) {
sequence *= 10;
multiplier *= 10;
}
uint16_t i = find_sequence(sequence);
if (i >= WORDS_COUNT ||
words_button_seq[i].sequence - sequence >= multiplier) {
return NULL;
}
return slip39_wordlist[words_button_seq[i].index];
}
/**
* Computes mask if find_index is false.
* Otherwise finds the first word index that matches the prefix. Returns
* WORDS_COUNT if no match is found.
* Calculates which buttons on the T9 keyboard can still be pressed after the
* prefix was entered. Returns a 9-bit bitmask, where each bit specifies which
* buttons can be pressed (there are still words in this combination). The least
* significant bit corresponds to the first button.
*
* Example: 110000110 - second, third, eighth and ninth button still can be
* pressed.
*/
static uint16_t find(uint16_t prefix, bool find_index) {
if (prefix == 0) {
return find_index ? 0 : 0x1ff;
uint16_t slip39_word_completion_mask(uint16_t prefix) {
if (prefix >= 1000) {
// Four char prefix -> the mask is zero.
return 0;
}
// Determine the range of sequences [min, max), which have the given prefix.
@ -106,36 +137,15 @@ static uint16_t find(uint16_t prefix, bool find_index) {
}
divider /= 10;
// Four char prefix -> the mask is zero
if (!divider && !find_index) {
return 0;
}
// Determine the range we will be searching in words_button_seq[].
min = find_sequence(min);
max = find_sequence(max);
// We can't use binary search because the numbers are not sorted.
// They are sorted using the words' alphabet (so we can use the index).
// Example: axle (1953), beam (1315)
// The first digit is sorted so we only need to search up to `max_search`.
uint16_t max_search = min - (min % 1000) + 1000;
uint16_t bitmap = 0;
for (uint16_t i = 0; i < WORDS_COUNT; i++) {
if (words_button_seq[i] >= max_search) {
break;
}
if (words_button_seq[i] >= min && words_button_seq[i] < max) {
if (find_index) {
return i;
}
uint8_t digit = (words_button_seq[i] / divider) % 10;
bitmap |= 1 << (digit - 1);
}
for (uint16_t i = min; i < max; ++i) {
uint8_t digit = (words_button_seq[i].sequence / divider) % 10;
bitmap |= 1 << (digit - 1);
}
if (find_index) {
// Index not found.
return WORDS_COUNT;
} else {
return bitmap;
}
return bitmap;
}

File diff suppressed because it is too large Load Diff

@ -5318,8 +5318,8 @@ START_TEST(test_slip39_sequence_to_word) {
const uint16_t prefix;
const char *expected_word;
} vectors[] = {
{7945, "swimming"}, {646, "photo"}, {5, "kernel"},
{34, "either"}, {62, "ocean"}, {0, "academic"},
{7945, "swimming"}, {646, "pipeline"}, {5, "laden"},
{34, "fiber"}, {62, "ocean"}, {0, "academic"},
};
for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); i++) {
const char *word = button_sequence_to_word(vectors[i].prefix);

Loading…
Cancel
Save