Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bip85: support RSA entropy #463

Merged
merged 6 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "src/secp256k1"]
path = src/secp256k1
url = https://github.com/ElementsProject/secp256k1-zkp.git
url = https://github.com/BlockstreamResearch/secp256k1-zkp.git
10 changes: 5 additions & 5 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -280,18 +280,18 @@ AM_COND_IF([LINK_SYSTEM_SECP256K1], [
AC_CHECK_FUNCS([$2], [], [missing_modules="${missing_modules} $1"])
])
CHECK_MODULE([ecdh], [secp256k1_ecdh])
CHECK_MODULE([extrakeys], [secp256k1_xonly_pubkey_parse])
CHECK_MODULE([recovery], [secp256k1_ecdsa_recover])
CHECK_MODULE([extrakeys], [secp256k1_xonly_pubkey_parse])
CHECK_MODULE([schnorrsig], [secp256k1_schnorrsig_verify])
AM_COND_IF([BUILD_STANDARD_SECP], [], [
CHECK_MODULE([ecdsa-s2c], [secp256k1_ecdsa_s2c_sign])
])
AM_COND_IF([BUILD_ELEMENTS], [
CHECK_MODULE([generator], [secp256k1_generator_parse])
CHECK_MODULE([rangeproof], [secp256k1_rangeproof_verify])
CHECK_MODULE([surjectionproof], [secp256k1_surjectionproof_initialize])
CHECK_MODULE([whitelist], [secp256k1_whitelist_sign])
])
AM_COND_IF([BUILD_STANDARD_SECP], [], [
CHECK_MODULE([ecdsa-s2c], [secp256k1_ecdsa_s2c_sign])
])
AS_IF([test -n "${missing_modules}"], [
AC_MSG_ERROR([system-installed $with_system_secp256k1 does not support these required modules:${missing_modules}])
])
Expand Down Expand Up @@ -443,7 +443,7 @@ export LD
export LDFLAGS

AM_COND_IF([LINK_SYSTEM_SECP256K1], [], [
AX_SUBDIRS_CONFIGURE([src/secp256k1], [[--disable-shared], [--enable-static], [--with-pic], [--enable-experimental], [--enable-module-ecdh], [--enable-module-recovery], [--enable-module-ecdsa-s2c], [--enable-module-rangeproof], [--enable-module-surjectionproof], [--enable-module-whitelist], [--enable-module-generator], [--enable-module-extrakeys], [--enable-module-schnorrsig], [$secp256k1_test_opt], [--enable-exhaustive-tests=no], [--enable-benchmark=no], [--disable-dependency-tracking], [$secp_asm]])
AX_SUBDIRS_CONFIGURE([src/secp256k1], [[--disable-shared], [--enable-static], [--with-pic], [--enable-experimental], [--enable-module-ecdh], [--enable-module-recovery], [--enable-module-extrakeys], [--enable-module-schnorrsig], [--enable-module-generator], [--enable-module-rangeproof], [--enable-module-surjectionproof], [--enable-module-whitelist], [--enable-module-ecdsa-s2c], [$secp256k1_test_opt], [--enable-exhaustive-tests=no], [--enable-benchmark=no], [--disable-dependency-tracking], [$secp_asm]])
])

AC_OUTPUT
6 changes: 6 additions & 0 deletions include/wally.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,12 @@ inline int bip85_get_languages(char** output) {
return detail::check_ret(__FUNCTION__, ret);
}

template <class HDKEY, class BYTES_OUT>
inline int bip85_get_rsa_entropy(const HDKEY& hdkey, uint32_t key_bits, uint32_t index, BYTES_OUT& bytes_out, size_t* written) {
int ret = ::bip85_get_rsa_entropy(detail::get_p(hdkey), key_bits, index, bytes_out.data(), bytes_out.size(), written);
return detail::check_ret(__FUNCTION__, ret);
}

template <class BYTES, class ADDR_FAMILY>
inline int addr_segwit_from_bytes(const BYTES& bytes, const ADDR_FAMILY& addr_family, uint32_t flags, char** output) {
int ret = ::wally_addr_segwit_from_bytes(bytes.data(), bytes.size(), detail::get_p(addr_family), flags, output);
Expand Down
4 changes: 2 additions & 2 deletions include/wally_bip39.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ struct words;
#define BIP39_WORDLIST_LEN 2048

/**
* Get the list of default supported languages.
* Get the list of default supported languages for BIP39.
*
* .. note:: The string returned should be freed using `wally_free_string`.
*/
Expand Down Expand Up @@ -98,7 +98,7 @@ WALLY_CORE_API int bip39_mnemonic_from_bytes(
*
* :param w: Word list to use. Pass NULL to use the default English list.
* :param mnemonic: Mnemonic to convert.
* :param bytes_out: Where to store the resulting entropy.
* :param bytes_out: Destination for the resulting entropy.
* MAX_SIZED_OUTPUT(len, bytes_out, BIP39_ENTROPY_MAX_LEN)
* :param written: Destination for the number of bytes written to ``bytes_out``.
*/
Expand Down
34 changes: 29 additions & 5 deletions include/wally_bip85.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,24 @@ extern "C" {
struct ext_key;

/**
* Get the list of default supported languages.
* Get the list of default supported languages for BIP85.
*
* .. note:: The string returned should be freed using `wally_free_string`.
*/
WALLY_CORE_API int bip85_get_languages(
char **output);

/**
* Generate bip39 mnemonic entropy according to bip85.
* Generate BIP39 mnemonic entropy according to BIP85.
*
* :param hdkey: The parent extended key to derive entropy from.
* :param hdkey: The parent extended key to derive mnemonic entropy from.
* :param lang: The intended language. Pass NULL to use the default English value.
* :param num_words: The intended number of words. Must be 12, 18 or 24.
* :param index: The index used to create the entropy. Must be less than
*| `BIP32_INITIAL_HARDENED_CHILD`.
* :param bytes_out: Where to store the resulting entropy.
* :param bytes_out: Destination for the resulting entropy.
* MAX_SIZED_OUTPUT(len, bytes_out, HMAC_SHA512_LEN)
* :param written: Number of bytes in ``bytes_out`` to be used as entropy.
* :param written: Destination for the number of bytes written to ``bytes_out``.
*/
WALLY_CORE_API int bip85_get_bip39_entropy(
const struct ext_key *hdkey,
Expand All @@ -38,6 +38,30 @@ WALLY_CORE_API int bip85_get_bip39_entropy(
size_t len,
size_t *written);

/**
* Generate entropy for seeding RSA key generation according to BIP85.
*
* :param hdkey: The parent extended key to derive RSA entropy from.
* :param key_bits: The intended RSA key size in bits.
* :param index: The index used to create the entropy. Must be less than
*| `BIP32_INITIAL_HARDENED_CHILD`.
* :param bytes_out: Destination for the resulting entropy.
* MAX_SIZED_OUTPUT(len, bytes_out, HMAC_SHA512_LEN)
* :param written: Destination for the number of bytes written to ``bytes_out``.
*
* .. note:: This function always returns HMAC_SHA512_LEN bytes on success.
*
* .. note:: The returned entropy must be given to BIP85-DRNG in order
*| to derive the RSA key to use. It MUST NOT be used directly.
*/
WALLY_CORE_API int bip85_get_rsa_entropy(
const struct ext_key *hdkey,
uint32_t key_bits,
uint32_t index,
unsigned char *bytes_out,
size_t len,
size_t *written);

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion include/wally_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ WALLY_CORE_API int wally_hex_from_bytes(
* Convert a hexadecimal string to bytes.
*
* :param hex: String to convert.
* :param bytes_out: Where to store the resulting bytes.
* :param bytes_out: Destination for the resulting bytes.
* :param len: The length of ``bytes_out`` in bytes.
* :param written: Destination for the number of bytes written to ``bytes_out``.
*/
Expand Down
12 changes: 11 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,17 @@ def _call(args, cwd=ABS_PATH):
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
],
'keywords': 'Bitcoin wallet BIP32 BIP38 BIP39 secp256k1 Blockstream Liquid Elements',
'keywords': [
'Bitcoin',
'wallet',
'BIP32',
'BIP38',
'BIP39',
'secp256k1',
'Blockstream',
'Liquid',
'Elements',
],
'project_urls': {
'Documentation': 'https://wally.readthedocs.io/en/latest',
'Source': 'https://github.com/ElementsProject/libwally-core',
Expand Down
3 changes: 1 addition & 2 deletions src/bip39.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#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>

Expand Down Expand Up @@ -53,7 +52,7 @@ int bip39_get_wordlist(const char *lang, struct words **output)
*output = (struct words *)&en_words; /* Fallback to English if not found */

if (lang)
for (i = 0; i < sizeof(lookup) / sizeof(lookup[0]); ++i)
for (i = 0; i < NUM_ELEMS(lookup); ++i)
if (!strcmp(lang, lookup[i].name)) {
*output = (struct words *)lookup[i].words;
break;
Expand Down
56 changes: 46 additions & 10 deletions src/bip85.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#include "internal.h"
#include "hmac.h"
#include "ccan/ccan/crypto/sha512/sha512.h"
#include <include/wally_bip32.h>
#include <include/wally_bip39.h>
#include <include/wally_bip85.h>
Expand All @@ -9,14 +7,15 @@
/* Bip85 path element values */
#define BIP85_PURPOSE (BIP32_INITIAL_HARDENED_CHILD | 83696968)
#define BIP85_APPLICATION_39 (BIP32_INITIAL_HARDENED_CHILD | 39)
#define BIP85_ENTROPY_PATH_LEN 5
#define BIP85_APPLICATION_RSA (BIP32_INITIAL_HARDENED_CHILD | 828365)

#define BIP85_ENTROPY_HMAC_KEY_LEN 18
static const uint8_t BIP85_ENTROPY_HMAC_KEY[BIP85_ENTROPY_HMAC_KEY_LEN]
= { 'b', 'i', 'p', '-', 'e', 'n', 't', 'r', 'o', 'p', 'y', '-', 'f', 'r', 'o', 'm', '-', 'k' };

/* Bip85 specifies a language code from 0' to 8' - so order here is important */
static const char *bip85_langs[] = { "en", "jp", "kr", "es", "zhs", "zht", "fr", "it", "cz" };
#define BIP85_NUM_LANGS (sizeof(bip85_langs) / sizeof(bip85_langs[0]))


static size_t get_entropy_len(uint32_t num_words)
{
Expand Down Expand Up @@ -45,9 +44,10 @@ int bip85_get_bip39_entropy(const struct ext_key *hdkey,
unsigned char* bytes_out, size_t len,
size_t* written)
{
const size_t entropy_len = get_entropy_len(num_words);
uint32_t path[BIP85_ENTROPY_PATH_LEN], lang_idx = 0; /* 0=English */
struct ext_key derived;
uint32_t path[5]; /* PURPOSE_BIP85 / APP_39 / land_idx / num_words / index */
uint32_t lang_idx = 0; /* 0=English */
const size_t entropy_len = get_entropy_len(num_words);
int ret;

if (written)
Expand All @@ -60,13 +60,13 @@ int bip85_get_bip39_entropy(const struct ext_key *hdkey,
if (lang) {
/* Lookup the callers language */
size_t i;
for (i = 0; i < BIP85_NUM_LANGS; ++i) {
for (i = 0; i < NUM_ELEMS(bip85_langs); ++i) {
if (!strcmp(lang, bip85_langs[i])) {
lang_idx = i;
break;
}
}
if (i == BIP85_NUM_LANGS)
if (i == NUM_ELEMS(bip85_langs))
return WALLY_EINVAL; /* Language not found */
}

Expand All @@ -76,8 +76,8 @@ int bip85_get_bip39_entropy(const struct ext_key *hdkey,
path[2] = lang_idx | BIP32_INITIAL_HARDENED_CHILD;
path[3] = num_words | BIP32_INITIAL_HARDENED_CHILD;
path[4] = index | BIP32_INITIAL_HARDENED_CHILD;
ret = bip32_key_from_parent_path(hdkey, path, BIP85_ENTROPY_PATH_LEN,
BIP32_FLAG_KEY_PRIVATE|BIP32_FLAG_SKIP_HASH,
ret = bip32_key_from_parent_path(hdkey, path, NUM_ELEMS(path),
BIP32_FLAG_KEY_PRIVATE | BIP32_FLAG_SKIP_HASH,
&derived);

if (ret == WALLY_OK) {
Expand All @@ -93,3 +93,39 @@ int bip85_get_bip39_entropy(const struct ext_key *hdkey,
wally_clear(&derived, sizeof(derived));
return ret;
}

int bip85_get_rsa_entropy(const struct ext_key *hdkey, uint32_t key_bits, uint32_t index,
unsigned char *bytes_out, size_t len, size_t *written)
{
struct ext_key derived;
uint32_t path[4]; /* PURPOSE_BIP85 / APP_RSA / key_bits / index */
int ret;

if (written)
*written = 0;

if (!hdkey || key_bits & BIP32_INITIAL_HARDENED_CHILD || index & BIP32_INITIAL_HARDENED_CHILD || !bytes_out
|| len != HMAC_SHA512_LEN || !written)
return WALLY_EINVAL;

/* Derive a private key from the bip85 path for bip39 mnemonic entropy */
path[0] = BIP85_PURPOSE;
path[1] = BIP85_APPLICATION_RSA;
path[2] = key_bits | BIP32_INITIAL_HARDENED_CHILD;
path[3] = index | BIP32_INITIAL_HARDENED_CHILD;
ret = bip32_key_from_parent_path(hdkey, path, NUM_ELEMS(path),
BIP32_FLAG_KEY_PRIVATE | BIP32_FLAG_SKIP_HASH,
&derived);

if (ret == WALLY_OK) {
/* HMAC-SHA512 the derived private key with the fixed bip85 key
* Write result directly into output buffer - 'written' indicates
* how much should be used. */
ret = wally_hmac_sha512(BIP85_ENTROPY_HMAC_KEY, BIP85_ENTROPY_HMAC_KEY_LEN, derived.priv_key + 1,
sizeof(derived.priv_key) - 1, bytes_out, len);
if (ret == WALLY_OK)
*written = len;
}
wally_clear(&derived, sizeof(derived));
return ret;
}
1 change: 0 additions & 1 deletion src/descriptor.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include <stdbool.h>
#include <stdlib.h>

#define NUM_ELEMS(a) (sizeof(a) / sizeof(a[0]))
#define MS_FLAGS_ALL (WALLY_MINISCRIPT_TAPSCRIPT | \
WALLY_MINISCRIPT_ONLY | \
WALLY_MINISCRIPT_REQUIRE_CHECKSUM | \
Expand Down
2 changes: 1 addition & 1 deletion src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ static void sha256_midstate(struct sha256_ctx *ctx, struct sha256 *res)
/* HW: Already big endian */
memcpy(res->u.u32, ctx->SHA_CTX_STATE, sizeof(ctx->SHA_CTX_STATE));
#else
for (i = 0; i < sizeof(ctx->SHA_CTX_STATE) / sizeof(ctx->SHA_CTX_STATE[0]); i++)
for (i = 0; i < NUM_ELEMS(ctx->SHA_CTX_STATE); i++)
res->u.u32[i] = cpu_to_be32(ctx->SHA_CTX_STATE[i]);
#endif

Expand Down
2 changes: 2 additions & 0 deletions src/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ const struct wally_operations *wally_ops(void);
#endif
#define strdup(ptr) __use_wally_strdup_internally__

#define NUM_ELEMS(a) (sizeof(a) / sizeof(a[0]))

/* Validity checking for input parameters */
#define BYTES_VALID(p, len) ((p != NULL) == (len != 0))
#define BYTES_INVALID(p, len) (!BYTES_VALID(p, len))
Expand Down
6 changes: 3 additions & 3 deletions src/mnemonic.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ char *mnemonic_from_bytes(
size_t len);

/**
* Convert a mnemonic representation into a block of bytes.
* Convert a mnemonic representation into bytes.
*
* @w: List of words.
* @mnemonic: Mnemonic sentence to store.
* @bytes_out: Where to store the converted representation.
* @bytes_out: Destination for the resulting bytes.
* @len: The length of @bytes_out in bytes.
* @written: Destination for the number of bytes written.
* @written: Destination for the number of bytes written to ``bytes_out``.
*/
int mnemonic_to_bytes(
const struct words *w,
Expand Down
6 changes: 6 additions & 0 deletions src/swig_java/jni_extra.java_in
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@
return trimBuffer(buf, len);
}

public final static byte[] bip85_get_rsa_entropy(Object hdkey, long key_bits, long index) {
final byte[] buf = new byte[HMAC_SHA512_LEN];
final int len = bip85_get_rsa_entropy(hdkey, key_bits, index, buf);
return checkBuffer(buf, len);
}

public final static byte[] ecdh(byte[] jarg1, byte[] jarg2) {
return ecdh(jarg1, jarg2, null);
}
Expand Down
1 change: 1 addition & 0 deletions src/swig_java/swig.i
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) {
%returns_array_(bip39_mnemonic_to_seed512, 3, 4, BIP39_SEED_LEN_512);
%returns_string(bip85_get_languages);
%returns_size_t(bip85_get_bip39_entropy);
%returns_size_t(bip85_get_rsa_entropy);
%returns_string(wally_addr_segwit_from_bytes);
%returns_size_t(wally_addr_segwit_get_version);
%returns_size_t(wally_addr_segwit_n_get_version);
Expand Down
1 change: 1 addition & 0 deletions src/swig_python/python_extra.py_in
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ bip38_to_private_key = _wrap_bin(bip38_to_private_key, EC_PRIVATE_KEY_LEN)
bip39_mnemonic_to_bytes = _wrap_bin(bip39_mnemonic_to_bytes, BIP39_ENTROPY_MAX_LEN, resize=True)
bip39_mnemonic_to_seed512 = _wrap_bin(bip39_mnemonic_to_seed512, BIP39_SEED_LEN_512)
bip85_get_bip39_entropy = _wrap_bin(bip85_get_bip39_entropy, HMAC_SHA512_LEN, resize=True)
bip85_get_rsa_entropy = _wrap_bin(bip85_get_rsa_entropy, HMAC_SHA512_LEN, resize=True)
descriptor_get_key_origin_fingerprint = _wrap_bin(descriptor_get_key_origin_fingerprint, BIP32_KEY_FINGERPRINT_LEN)
descriptor_to_script = _wrap_bin(descriptor_to_script, descriptor_to_script_get_maximum_length, resize=True)
ec_private_key_bip341_tweak = _wrap_bin(ec_private_key_bip341_tweak, EC_PRIVATE_KEY_LEN)
Expand Down
Loading