diff --git a/bip39.c b/bip39.c index 42ef82798..8d0fa1ee1 100644 --- a/bip39.c +++ b/bip39.c @@ -14,12 +14,9 @@ const char *mnemonic_generate(int strength) if (strength % 32 || strength < 128 || strength > 256) { return 0; } - int i; - static uint32_t data[16]; - for (i = 0; i < 16; i++) { - data[i] = random32(); - } - return mnemonic_from_data((const uint8_t *)data, strength / 8); + static uint8_t data[32]; + random_buffer(data, 32); + return mnemonic_from_data(data, strength / 8); } const char *mnemonic_from_data(const uint8_t *data, int len) @@ -28,31 +25,23 @@ const char *mnemonic_from_data(const uint8_t *data, int len) return 0; } - int i, j; - char bits[256 + 8]; - for (i = 0; i < len; i++) { - for (j = 0; j < 8; j++) { - bits[8 * i + j] = (data[i] & (1 << (7 - j))) > 0; - } - } + uint8_t bits[32 + 1]; + memcpy(bits, data, len); - uint8_t hash[32]; - sha256_Raw((const uint8_t *)data, len, hash); - - char hlen = len / 4; - for (i = 0; i < hlen; i++) { - char c = (hash[0] & (1 << (7 - i))) > 0; - bits[8 * len + i] = c; - } + sha256_Raw(data, len, bits); + bits[len] = bits[0]; + memcpy(bits, data, len); int mlen = len * 3 / 4; static char mnemo[24 * 10]; + int i, j, idx; char *p = mnemo; for (i = 0; i < mlen; i++) { - int idx = 0; + idx = 0; for (j = 0; j < 11; j++) { - idx += bits[i * 11 + j] << (10 - j); + idx <<= 1; + idx += (bits[(i * 11 + j) / 8] & (1 << (7 - ((i * 11 + j) % 8)))) > 0; } strcpy(p, wordlist[idx]); p += strlen(wordlist[idx]); @@ -65,9 +54,73 @@ const char *mnemonic_from_data(const uint8_t *data, int len) int mnemonic_check(const char *mnemonic) { - // TODO: add proper check - (void)mnemonic; - return 1; + if (!mnemonic) { + return 0; + } + + uint32_t i, n; + + i = 0; n = 0; + while (mnemonic[i]) { + if (mnemonic[i] == ' ') { + n++; + } + i++; + } + n++; + // check number of words + if (n != 12 && n != 18 && n != 24) { + return 0; + } + + char current_word[10]; + uint32_t j, k, ki, bi; + uint8_t bits[32 + 1]; + memset(bits, 0, sizeof(bits)); + i = 0; bi = 0; + while (mnemonic[i]) { + j = 0; + while (mnemonic[i] != ' ' && mnemonic[i] != 0) { + if (j >= sizeof(current_word)) { + return 0; + } + current_word[j] = mnemonic[i]; + i++; j++; + } + current_word[j] = 0; + if (mnemonic[i] != 0) i++; + k = 0; + for (;;) { + if (!wordlist[k]) { // word not found + return 0; + } + if (strcmp(current_word, wordlist[k]) == 0) { // word found on index k + for (ki = 0; ki < 11; ki++) { + if (k & (1 << (10 - ki))) { + bits[bi / 8] |= 1 << (7 - (bi % 8)); + } + bi++; + } + break; + } + k++; + } + } + if (bi != n * 11) { + return 0; + } + bits[32] = bits[n * 4 / 3]; + sha256_Raw(bits, n * 4 / 3, bits); + if (n == 12) { + return (bits[0] & 0xF0) == (bits[32] & 0xF0); // compare first 4 bits + } else + if (n == 18) { + return (bits[0] & 0xFC) == (bits[32] & 0xFC); // compare first 6 bits + } else + if (n == 24) { + return bits[0] == bits[32]; // compare 8 bits + } + return 0; } void mnemonic_to_seed(const char *mnemonic, const char *passphrase, uint8_t seed[512 / 8], void (*progress_callback)(uint32_t current, uint32_t total)) diff --git a/rand.c b/rand.c index fe6d724ad..20176d71e 100644 --- a/rand.c +++ b/rand.c @@ -27,12 +27,19 @@ static FILE *f; -void init_rand(void) { +void init_rand(void) +{ f = fopen("/dev/urandom", "r"); } -uint32_t random32(void) { +uint32_t random32(void) +{ uint32_t r; fread(&r, 1, sizeof(r), f); return r; } + +void random_buffer(uint8_t *buf, uint32_t len) +{ + fread(buf, 1, len, f); +} diff --git a/rand.h b/rand.h index 2b72bde5e..fff02e260 100644 --- a/rand.h +++ b/rand.h @@ -28,5 +28,6 @@ void init_rand(void); uint32_t random32(void); +void random_buffer(uint8_t *buf, uint32_t len); #endif diff --git a/tests.c b/tests.c index ba2b4271d..7742466e4 100644 --- a/tests.c +++ b/tests.c @@ -458,6 +458,104 @@ START_TEST(test_mnemonic) } END_TEST +START_TEST(test_mnemonic_check) +{ + static const char *vectors_ok[] = { + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", + "legal winner thank year wave sausage worth useful legal winner thank yellow", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage above", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", + "jelly better achieve collect unaware mountain thought cargo oxygen act hood bridge", + "renew stay biology evidence goat welcome casual join adapt armor shuffle fault little machine walk stumble urge swap", + "dignity pass list indicate nasty swamp pool script soccer toe leaf photo multiply desk host tomato cradle drill spread actor shine dismiss champion exotic", + "afford alter spike radar gate glance object seek swamp infant panel yellow", + "indicate race push merry suffer human cruise dwarf pole review arch keep canvas theme poem divorce alter left", + "clutch control vehicle tonight unusual clog visa ice plunge glimpse recipe series open hour vintage deposit universe tip job dress radar refuse motion taste", + "turtle front uncle idea crush write shrug there lottery flower risk shell", + "kiss carry display unusual confirm curtain upgrade antique rotate hello void custom frequent obey nut hole price segment", + "exile ask congress lamp submit jacket era scheme attend cousin alcohol catch course end lucky hurt sentence oven short ball bird grab wing top", + "board flee heavy tunnel powder denial science ski answer betray cargo cat", + "board blade invite damage undo sun mimic interest slam gaze truly inherit resist great inject rocket museum chief", + "beyond stage sleep clip because twist token leaf atom beauty genius food business side grid unable middle armed observe pair crouch tonight away coconut", + 0, + }; + static const char *vectors_fail[] = { + "above abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", + "above winner thank year wave sausage worth useful legal winner thank yellow", + "above advice cage absurd amount doctor acoustic avoid letter advice cage above", + "above zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "above abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", + "above winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", + "above advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", + "above zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", + "above abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", + "above winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", + "above advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", + "above zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", + "above better achieve collect unaware mountain thought cargo oxygen act hood bridge", + "above stay biology evidence goat welcome casual join adapt armor shuffle fault little machine walk stumble urge swap", + "above pass list indicate nasty swamp pool script soccer toe leaf photo multiply desk host tomato cradle drill spread actor shine dismiss champion exotic", + "above alter spike radar gate glance object seek swamp infant panel yellow", + "above race push merry suffer human cruise dwarf pole review arch keep canvas theme poem divorce alter left", + "above control vehicle tonight unusual clog visa ice plunge glimpse recipe series open hour vintage deposit universe tip job dress radar refuse motion taste", + "above front uncle idea crush write shrug there lottery flower risk shell", + "above carry display unusual confirm curtain upgrade antique rotate hello void custom frequent obey nut hole price segment", + "above ask congress lamp submit jacket era scheme attend cousin alcohol catch course end lucky hurt sentence oven short ball bird grab wing top", + "above flee heavy tunnel powder denial science ski answer betray cargo cat", + "above blade invite damage undo sun mimic interest slam gaze truly inherit resist great inject rocket museum chief", + "above stage sleep clip because twist token leaf atom beauty genius food business side grid unable middle armed observe pair crouch tonight away coconut", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", + "winner thank year wave sausage worth useful legal winner thank yellow", + "advice cage absurd amount doctor acoustic avoid letter advice cage above", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", + "winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", + "advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", + "winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", + "advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", + "better achieve collect unaware mountain thought cargo oxygen act hood bridge", + "stay biology evidence goat welcome casual join adapt armor shuffle fault little machine walk stumble urge swap", + "pass list indicate nasty swamp pool script soccer toe leaf photo multiply desk host tomato cradle drill spread actor shine dismiss champion exotic", + "alter spike radar gate glance object seek swamp infant panel yellow", + "race push merry suffer human cruise dwarf pole review arch keep canvas theme poem divorce alter left", + "control vehicle tonight unusual clog visa ice plunge glimpse recipe series open hour vintage deposit universe tip job dress radar refuse motion taste", + "front uncle idea crush write shrug there lottery flower risk shell", + "carry display unusual confirm curtain upgrade antique rotate hello void custom frequent obey nut hole price segment", + "ask congress lamp submit jacket era scheme attend cousin alcohol catch course end lucky hurt sentence oven short ball bird grab wing top", + "flee heavy tunnel powder denial science ski answer betray cargo cat", + "blade invite damage undo sun mimic interest slam gaze truly inherit resist great inject rocket museum chief", + "stage sleep clip because twist token leaf atom beauty genius food business side grid unable middle armed observe pair crouch tonight away coconut", + 0, + }; + + const char **m; + int r; + m = vectors_ok; + while (*m) { + r = mnemonic_check(*m); + ck_assert_int_eq(r, 1); + m++; + } + m = vectors_fail; + while (*m) { + r = mnemonic_check(*m); + ck_assert_int_eq(r, 0); + m++; + } +} +END_TEST + START_TEST(test_address) { char address[35]; @@ -660,6 +758,7 @@ Suite *test_suite(void) tc = tcase_create("bip39"); tcase_add_test(tc, test_mnemonic); + tcase_add_test(tc, test_mnemonic_check); suite_add_tcase(s, tc); return s;