mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-23 00:54:20 +01:00
libwally-core: import version 3b025127cbf11912f8b95e7ff3c905d74e8433ce
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
240
libwally-core/src/bip39.c
Normal file
240
libwally-core/src/bip39.c
Normal file
@@ -0,0 +1,240 @@
|
||||
#include "internal.h"
|
||||
#include "mnemonic.h"
|
||||
#include "wordlist.h"
|
||||
#include "hmac.h"
|
||||
#include "ccan/ccan/crypto/sha256/sha256.h"
|
||||
#include "ccan/ccan/crypto/sha512/sha512.h"
|
||||
#include <include/wally_bip39.h>
|
||||
#include <include/wally_crypto.h>
|
||||
|
||||
#include "data/wordlists/chinese_simplified.c"
|
||||
#include "data/wordlists/chinese_traditional.c"
|
||||
#include "data/wordlists/english.c"
|
||||
#include "data/wordlists/french.c"
|
||||
#include "data/wordlists/italian.c"
|
||||
#include "data/wordlists/spanish.c"
|
||||
#include "data/wordlists/japanese.c"
|
||||
|
||||
/* Maximum length including up to 2 bytes for checksum */
|
||||
#define BIP39_ENTROPY_LEN_MAX (BIP39_ENTROPY_LEN_320 + sizeof(unsigned char) * 2)
|
||||
|
||||
static const struct {
|
||||
const char name[4];
|
||||
const struct words *words;
|
||||
} lookup[] = {
|
||||
{ "en", &en_words}, { "es", &es_words}, { "fr", &fr_words},
|
||||
{ "it", &it_words}, { "jp", &jp_words}, { "zhs", &zhs_words},
|
||||
{ "zht", &zht_words},
|
||||
/* FIXME: Should 'zh' map to traditional or simplified? */
|
||||
};
|
||||
|
||||
int bip39_get_languages(char **output)
|
||||
{
|
||||
if (!output)
|
||||
return WALLY_EINVAL;
|
||||
*output = wally_strdup("en es fr it jp zhs zht");
|
||||
return *output ? WALLY_OK : WALLY_ENOMEM;
|
||||
}
|
||||
|
||||
int bip39_get_wordlist(const char *lang, const struct words **output)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!output)
|
||||
return WALLY_EINVAL;
|
||||
|
||||
*output = &en_words; /* Fallback to English if not found */
|
||||
|
||||
if (lang)
|
||||
for (i = 0; i < sizeof(lookup) / sizeof(lookup[0]); ++i)
|
||||
if (!strcmp(lang, lookup[i].name)) {
|
||||
*output = lookup[i].words;
|
||||
break;
|
||||
}
|
||||
return WALLY_OK;
|
||||
}
|
||||
|
||||
int bip39_get_word(const struct words *w, size_t idx,
|
||||
char **output)
|
||||
{
|
||||
const char *word;
|
||||
|
||||
if (output)
|
||||
*output = NULL;
|
||||
|
||||
w = w ? w : &en_words;
|
||||
|
||||
if (!output || !(word = wordlist_lookup_index(w, idx)))
|
||||
return WALLY_EINVAL;
|
||||
|
||||
*output = word ? wally_strdup(word) : NULL;
|
||||
return *output ? WALLY_OK : WALLY_ENOMEM;
|
||||
}
|
||||
|
||||
/* Convert an input entropy length to a mask for checksum bits. As it
|
||||
* returns 0 for bad lengths, it serves as a validation function too.
|
||||
*/
|
||||
static size_t len_to_mask(size_t len)
|
||||
{
|
||||
switch (len) {
|
||||
case BIP39_ENTROPY_LEN_128: return 0xf0;
|
||||
case BIP39_ENTROPY_LEN_160: return 0xf8;
|
||||
case BIP39_ENTROPY_LEN_192: return 0xfc;
|
||||
case BIP39_ENTROPY_LEN_224: return 0xfe;
|
||||
case BIP39_ENTROPY_LEN_256: return 0xff;
|
||||
case BIP39_ENTROPY_LEN_288: return 0x80ff;
|
||||
case BIP39_ENTROPY_LEN_320: return 0xC0ff;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t bip39_checksum(const unsigned char *bytes_in, size_t len_in, size_t mask)
|
||||
{
|
||||
struct sha256 sha;
|
||||
size_t ret;
|
||||
sha256(&sha, bytes_in, len_in);
|
||||
ret = sha.u.u8[0] | (sha.u.u8[1] << 8);
|
||||
clear(&sha, sizeof(sha));
|
||||
return ret & mask;
|
||||
}
|
||||
|
||||
int bip39_mnemonic_from_bytes(const struct words *w,
|
||||
const unsigned char *bytes_in, size_t len_in,
|
||||
char **output)
|
||||
{
|
||||
unsigned char tmp_bytes[BIP39_ENTROPY_LEN_MAX];
|
||||
size_t checksum, mask;
|
||||
|
||||
if (output)
|
||||
*output = NULL;
|
||||
|
||||
if (!bytes_in || !len_in || !output)
|
||||
return WALLY_EINVAL;
|
||||
|
||||
w = w ? w : &en_words;
|
||||
|
||||
if (w->bits != 11u || !(mask = len_to_mask(len_in)))
|
||||
return WALLY_EINVAL;
|
||||
|
||||
memcpy(tmp_bytes, bytes_in, len_in);
|
||||
checksum = bip39_checksum(bytes_in, len_in, mask);
|
||||
tmp_bytes[len_in] = checksum & 0xff;
|
||||
if (mask > 0xff)
|
||||
tmp_bytes[++len_in] = (checksum >> 8) & 0xff;
|
||||
*output = mnemonic_from_bytes(w, tmp_bytes, len_in + 1);
|
||||
clear(tmp_bytes, sizeof(tmp_bytes));
|
||||
return *output ? WALLY_OK : WALLY_ENOMEM;
|
||||
}
|
||||
|
||||
static bool checksum_ok(const unsigned char *bytes, size_t idx, size_t mask)
|
||||
{
|
||||
/* The checksum is stored after the data to sum */
|
||||
size_t calculated = bip39_checksum(bytes, idx, mask);
|
||||
size_t stored = bytes[idx];
|
||||
if (mask > 0xff)
|
||||
stored |= (bytes[idx + 1] << 8);
|
||||
return (stored & mask) == calculated;
|
||||
}
|
||||
|
||||
int bip39_mnemonic_to_bytes(const struct words *w, const char *mnemonic,
|
||||
unsigned char *bytes_out, size_t len,
|
||||
size_t *written)
|
||||
{
|
||||
unsigned char tmp_bytes[BIP39_ENTROPY_LEN_MAX];
|
||||
size_t mask, tmp_len;
|
||||
int ret;
|
||||
|
||||
/* Ideally we would infer the wordlist here. Unfortunately this cannot
|
||||
* work reliably because the default word lists overlap. In combination
|
||||
* with being sorted lexographically, this means the default lists
|
||||
* were poorly chosen. But we are stuck with them now.
|
||||
*
|
||||
* If the caller doesn't know which word list to use, they should iterate
|
||||
* over the available ones and try any resulting list that the mnemonic
|
||||
* validates against.
|
||||
*/
|
||||
w = w ? w : &en_words;
|
||||
|
||||
if (written)
|
||||
*written = 0;
|
||||
|
||||
if (w->bits != 11u || !mnemonic || !bytes_out)
|
||||
return WALLY_EINVAL;
|
||||
|
||||
ret = mnemonic_to_bytes(w, mnemonic, tmp_bytes, sizeof(tmp_bytes), &tmp_len);
|
||||
|
||||
if (!ret) {
|
||||
/* Remove checksum bytes from the output length */
|
||||
--tmp_len;
|
||||
if (tmp_len > BIP39_ENTROPY_LEN_256)
|
||||
--tmp_len; /* Second byte required */
|
||||
|
||||
if (tmp_len > sizeof(tmp_bytes))
|
||||
ret = WALLY_EINVAL; /* Too big for biggest supported entropy */
|
||||
else {
|
||||
if (tmp_len <= len) {
|
||||
if (!(mask = len_to_mask(tmp_len)) ||
|
||||
!checksum_ok(tmp_bytes, tmp_len, mask)) {
|
||||
tmp_len = 0;
|
||||
ret = WALLY_EINVAL; /* Bad checksum */
|
||||
}
|
||||
else
|
||||
memcpy(bytes_out, tmp_bytes, tmp_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clear(tmp_bytes, sizeof(tmp_bytes));
|
||||
if (!ret && written)
|
||||
*written = tmp_len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bip39_mnemonic_validate(const struct words *w, const char *mnemonic)
|
||||
{
|
||||
unsigned char buf[BIP39_ENTROPY_LEN_MAX];
|
||||
size_t len;
|
||||
int ret = bip39_mnemonic_to_bytes(w, mnemonic, buf, sizeof(buf), &len);
|
||||
clear(buf, sizeof(buf));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bip39_mnemonic_to_seed(const char *mnemonic, const char *password,
|
||||
unsigned char *bytes_out, size_t len,
|
||||
size_t *written)
|
||||
{
|
||||
const size_t bip9_cost = 2048u;
|
||||
const char *prefix = "mnemonic";
|
||||
const size_t prefix_len = strlen(prefix);
|
||||
const size_t password_len = password ? strlen(password) : 0;
|
||||
const size_t salt_len = prefix_len + password_len + PBKDF2_HMAC_EXTRA_LEN;
|
||||
unsigned char *salt;
|
||||
int ret;
|
||||
|
||||
if (written)
|
||||
*written = 0;
|
||||
|
||||
if (!mnemonic || !bytes_out || len != BIP39_SEED_LEN_512)
|
||||
return WALLY_EINVAL;
|
||||
|
||||
salt = wally_malloc(salt_len);
|
||||
if (!salt)
|
||||
return WALLY_ENOMEM;
|
||||
|
||||
memcpy(salt, prefix, prefix_len);
|
||||
if (password_len)
|
||||
memcpy(salt + prefix_len, password, password_len);
|
||||
|
||||
ret = wally_pbkdf2_hmac_sha512((unsigned char *)mnemonic, strlen(mnemonic),
|
||||
salt, salt_len,
|
||||
PBKDF2_HMAC_FLAG_BLOCK_RESERVED,
|
||||
bip9_cost, bytes_out, len);
|
||||
|
||||
if (!ret && written)
|
||||
*written = BIP39_SEED_LEN_512; /* Succeeded */
|
||||
|
||||
clear(salt, salt_len);
|
||||
wally_free(salt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user