Skip to content

Commit

Permalink
Merge pull request #12 from tevador/pr-fix-decode
Browse files Browse the repository at this point in the history
Handle seeds that can decode in multiple languages
  • Loading branch information
tevador authored Apr 29, 2024
2 parents 9d4f1a0 + 583cad4 commit dfb05d8
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 9 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ set_property(TARGET polyseed PROPERTY PUBLIC_HEADER include/polyseed.h)
include_directories(polyseed
include/)
target_compile_definitions(polyseed PRIVATE POLYSEED_SHARED)
set_target_properties(polyseed PROPERTIES VERSION 2.0.0
set_target_properties(polyseed PROPERTIES VERSION 2.1.0
SOVERSION 2
C_STANDARD 11
C_STANDARD_REQUIRED ON)
Expand Down
20 changes: 20 additions & 0 deletions include/polyseed.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ typedef enum polyseed_status {
POLYSEED_ERR_FORMAT = 5,
/* Memory allocation failure */
POLYSEED_ERR_MEMORY = 6,
/* Phrase matches more than one language */
POLYSEED_ERR_MULT_LANG = 7,
} polyseed_status;

/* Opaque struct with the seed data */
Expand Down Expand Up @@ -275,6 +277,24 @@ POLYSEED_API
polyseed_status polyseed_decode(const char* str, polyseed_coin coin,
const polyseed_lang** lang_out, polyseed_data** seed_out);

/**
* Decodes the seed from a mnemonic phrase with a specific language.
* This should be used if polyseed_decode returns POLYSEED_ERR_MULT_LANG.
*
* @param str is the mnemonic phrase as a C-style string. Must not be NULL.
* @param coin is the coin the mnemonic phrase is intended for.
* @param lang is a pointer to the language to decode the seed.
* Must not be NULL.
* @param seed_out is a pointer where the seed pointer will be stored.
* Must not be NULL.
*
* @return POLYSEED_OK if the operation was successful. Other values indicate
* an error (in that case, *seed_out is undefined).
*/
POLYSEED_API
polyseed_status polyseed_decode_explicit(const char* str, polyseed_coin coin,
const polyseed_lang* lang, polyseed_data** seed_out);

/**
* Serializes the seed data in a platform-independent way.
*
Expand Down
35 changes: 30 additions & 5 deletions src/lang.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,12 @@ int polyseed_lang_find_word(const polyseed_lang* lang, const char* word) {
return lang_search(lang, word, cmp);
}

bool polyseed_phrase_decode(const polyseed_phrase phrase,
polyseed_status polyseed_phrase_decode(const polyseed_phrase phrase,
uint_fast16_t idx_out[POLYSEED_NUM_WORDS], const polyseed_lang** lang_out) {
/* Iterate through all languages and try to find one where
/* Iterate through all languages and try to find just one where
all the words are a match. */
uint_fast16_t idx[POLYSEED_NUM_WORDS];
bool have_lang = false;
for (int li = 0; li < NUM_LANGS; ++li) {
const polyseed_lang* lang = languages[li];
polyseed_cmp* cmp = get_comparer(lang);
Expand All @@ -217,17 +219,40 @@ bool polyseed_phrase_decode(const polyseed_phrase phrase,
success = false;
break;
}
idx_out[wi] = value;
idx[wi] = value;
}
if (!success) {
continue;
}
if (have_lang) {
/* The phrase can decode in multiple languages.
Use polyseed_phrase_decode_explicit. */
return POLYSEED_ERR_MULT_LANG;
}
have_lang = true;
for (int wi = 0; wi < POLYSEED_NUM_WORDS; ++wi) {
idx_out[wi] = idx[wi];
}
if (lang_out != NULL) {
*lang_out = lang;
}
return true;
}
return false;
return have_lang ? POLYSEED_OK : POLYSEED_ERR_LANG;
}

polyseed_status polyseed_phrase_decode_explicit(const polyseed_phrase phrase,
const polyseed_lang* lang, uint_fast16_t idx_out[POLYSEED_NUM_WORDS]) {

polyseed_cmp* cmp = get_comparer(lang);
for (int wi = 0; wi < POLYSEED_NUM_WORDS; ++wi) {
const char* word = phrase[wi];
int value = lang_search(lang, word, cmp);
if (value < 0) {
return POLYSEED_ERR_LANG;
}
idx_out[wi] = value;
}
return POLYSEED_OK;
}

void polyseed_lang_check(const polyseed_lang* lang) {
Expand Down
5 changes: 4 additions & 1 deletion src/lang.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ typedef const char* polyseed_phrase[POLYSEED_NUM_WORDS];
POLYSEED_PRIVATE int polyseed_lang_find_word(const polyseed_lang* lang,
const char* word);

POLYSEED_PRIVATE bool polyseed_phrase_decode(const polyseed_phrase phrase,
POLYSEED_PRIVATE polyseed_status polyseed_phrase_decode(const polyseed_phrase phrase,
uint_fast16_t idx_out[POLYSEED_NUM_WORDS], const polyseed_lang** lang_out);

POLYSEED_PRIVATE polyseed_status polyseed_phrase_decode_explicit(const polyseed_phrase phrase,
const polyseed_lang* lang, uint_fast16_t idx_out[POLYSEED_NUM_WORDS]);

POLYSEED_PRIVATE void polyseed_lang_check(const polyseed_lang* lang);

#endif
74 changes: 72 additions & 2 deletions src/polyseed.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,78 @@ polyseed_status polyseed_decode(const char* str, polyseed_coin coin,
}

/* decode words into polynomial coefficients */
if (!polyseed_phrase_decode(words, poly.coeff, lang_out)) {
res = POLYSEED_ERR_LANG;
res = polyseed_phrase_decode(words, poly.coeff, lang_out);

if (res != POLYSEED_OK) {
goto cleanup;
}

/* finalize polynomial */
poly.coeff[POLY_NUM_CHECK_DIGITS] ^= coin;

/* checksum */
if (!gf_poly_check(&poly)) {
res = POLYSEED_ERR_CHECKSUM;
goto cleanup;
}

/* alocate memory */
seed = ALLOC(sizeof(polyseed_data));

if (seed == NULL) {
res = POLYSEED_ERR_MEMORY;
goto cleanup;
}

/* decode polynomial into seed data */
polyseed_poly_to_data(&poly, seed);

/* check features */
if (!polyseed_features_supported(seed->features)) {
polyseed_free(seed);
res = POLYSEED_ERR_UNSUPPORTED;
goto cleanup;
}

*seed_out = seed;
res = POLYSEED_OK;

cleanup:
MEMZERO_LOC(str_tmp);
MEMZERO_LOC(words);
MEMZERO_LOC(poly);
return res;
}

polyseed_status polyseed_decode_explicit(const char* str, polyseed_coin coin,
const polyseed_lang* lang, polyseed_data** seed_out) {

assert(str != NULL);
assert((gf_elem)coin < GF_SIZE);
assert(lang != NULL);
assert(seed_out != NULL);
CHECK_DEPS();

polyseed_str str_tmp;
polyseed_phrase words;
gf_poly poly = { 0 };
polyseed_status res;
polyseed_data* seed;

/* canonical decomposition */
size_t str_size = UTF8_DECOMPOSE(str, str_tmp);
assert(str_size < POLYSEED_STR_SIZE);

/* split into words */
if (str_split(str_tmp, words) != POLYSEED_NUM_WORDS) {
res = POLYSEED_ERR_NUM_WORDS;
goto cleanup;
}

/* decode words into polynomial coefficients */
res = polyseed_phrase_decode_explicit(words, lang, poly.coeff);

if (res != POLYSEED_OK) {
goto cleanup;
}

Expand Down
25 changes: 25 additions & 0 deletions tests/tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ static const char* g_phrase_es5 =
"eje fin part cele tab pest lien puma "
"pris hora rega leng exis lapi lote sono";

static const char* g_phrase_es_mult =
"impo sort usua cabi venu nobl oliv clim "
"cont barr marc auto prod vaca torn fati";

static const char* g_phrase_garbage1 = "xxx xxx";

static const char* g_phrase_garbage2 =
Expand Down Expand Up @@ -662,6 +666,25 @@ static bool test_decode_es_prefix2(void) {
return true;
}

static bool test_decode_es_mult1(void) {
const polyseed_lang* lang;
polyseed_data* seed;
polyseed_status res = polyseed_decode(g_phrase_es_mult, POLYSEED_MONERO, &lang, &seed);
assert(res == POLYSEED_ERR_MULT_LANG);
return true;
}

static bool test_decode_es_mult2(void) {
if (g_lang_es == NULL) {
return false;
}
polyseed_data* seed;
polyseed_status res = polyseed_decode_explicit(g_phrase_es_mult, POLYSEED_MONERO, g_lang_es, &seed);
assert(res == POLYSEED_OK);
polyseed_free(seed);
return true;
}

static bool test_free2(void) {
polyseed_free(g_seed2);
return true;
Expand Down Expand Up @@ -863,6 +886,8 @@ int main() {
RUN_TEST(test_decode_es_prefix1);
RUN_TEST(test_decode_es_suffix);
RUN_TEST(test_decode_es_prefix2);
RUN_TEST(test_decode_es_mult1);
RUN_TEST(test_decode_es_mult2);
RUN_TEST(test_free2);
RUN_TEST(test_inject3);
RUN_TEST(test_features3a);
Expand Down

0 comments on commit dfb05d8

Please sign in to comment.