Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1d8e0dc
descriptor: use the context and not node to determine elements-ness
jgriffiths May 10, 2025
7075dce
descriptor: use a named key for repeated key xpub
jgriffiths May 10, 2025
00cc54b
descriptor: add support for elements el-prefixed descriptor builtins
jgriffiths May 10, 2025
beaca14
descriptor: add support for parsing ct descriptors with slip77 blindi…
jgriffiths May 12, 2025
ca056f8
elements: add asset_blinding_key_to_ec_public_key
jgriffiths May 12, 2025
6b2ba68
descriptor: add support for generating elements slip77 confidential a…
jgriffiths May 12, 2025
0941e3f
build: fix job error reported on clean valgrind runs
jgriffiths May 12, 2025
f2a3fd1
txio: expose tagged hash helpers for internal use
jgriffiths May 13, 2025
3ae219c
crypto: add ec_public_key_tweak to tweak standard pubkeys
jgriffiths May 13, 2025
7c18c96
bip32: Simplify code by using ec_public_key_tweak internally
jgriffiths May 13, 2025
5df197f
descriptor: support elip150 key expressions for ct() blinding keys
jgriffiths May 14, 2025
6a5d040
descriptor: standardize generation handling of the length written
jgriffiths May 16, 2025
a170f50
descriptor: extend tests to completely cover short buffer inputs
jgriffiths May 18, 2025
6ca519c
descriptor: explicitly disallow wrappers on ct() and blinding keys
jgriffiths May 18, 2025
1c3c74b
descriptor: allow hex private keys for ct() blinding keys
jgriffiths May 18, 2025
e67740b
descriptor: add tests for the number of keys in a descriptor
jgriffiths May 18, 2025
823f21c
js: migrate ubuntu wasm build to 22.04 LTS
jgriffiths May 18, 2025
ecabd17
descriptor: expose elip-150 blinding key tweaking
jgriffiths May 19, 2025
45a7bed
descriptor: allow fetching the blinding key from ct descriptors
jgriffiths May 19, 2025
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 .github/workflows/wasm-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:

jobs:
build:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test_with_valgrind:
- for t in $(ls src/.libs/test_* | egrep -v '_clear|xml|json' | tr '\n' ' '); do valgrind-codequality --input-file $t.xml --output-file $t.json; done
- for t in $(ls src/test/test_*.py | tr '\n' ' '); do WALLY_SKIP_EXPENSIVE_TESTS=1 PYTHONMALLOC=malloc PYTHONDEVMODE=1 MALLOC_CHECK_=3 valgrind --tool=memcheck --leak-check=no --verbose --xml=yes --xml-file=$t.xml python $t; done
- for t in $(ls src/test/test_*.py | tr '\n' ' '); do valgrind-codequality --input-file $t.xml --output-file $t.json; done
- jq '[.[]|.[]]' -s ./src/.libs/test_*.json src/test/test_*.json > valgrind.json
- jq '[.[]|.[]]' -s ./src/.libs/test_*.json src/test/test_*.json > valgrind.json || true

test_asan_ubsan_gcc:
stage: test
Expand Down
30 changes: 30 additions & 0 deletions include/wally.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,12 @@ inline int ec_public_key_negate(const PUB_KEY& pub_key, BYTES_OUT& bytes_out) {
return detail::check_ret(__FUNCTION__, ret);
}

template <class PUB_KEY, class TWEAK, class BYTES_OUT>
inline int ec_public_key_tweak(const PUB_KEY& pub_key, const TWEAK& tweak, BYTES_OUT& bytes_out) {
int ret = ::wally_ec_public_key_tweak(pub_key.data(), pub_key.size(), tweak.data(), tweak.size(), bytes_out.data(), bytes_out.size());
return detail::check_ret(__FUNCTION__, ret);
}

template <class PUB_KEY>
inline bool ec_public_key_verify(const PUB_KEY& pub_key) {
int ret = ::wally_ec_public_key_verify(pub_key.data(), pub_key.size());
Expand Down Expand Up @@ -2268,6 +2274,12 @@ inline int asset_blinding_key_to_ec_private_key(const BYTES& bytes, const SCRIPT
return detail::check_ret(__FUNCTION__, ret);
}

template <class BYTES, class SCRIPT, class BYTES_OUT>
inline int asset_blinding_key_to_ec_public_key(const BYTES& bytes, const SCRIPT& script, BYTES_OUT& bytes_out) {
int ret = ::wally_asset_blinding_key_to_ec_public_key(bytes.data(), bytes.size(), script.data(), script.size(), bytes_out.data(), bytes_out.size());
return detail::check_ret(__FUNCTION__, ret);
}

template <class BYTES, class HASH_PREVOUTS, class BYTES_OUT>
inline int asset_blinding_key_to_vbf(const BYTES& bytes, const HASH_PREVOUTS& hash_prevouts, uint32_t output_index, BYTES_OUT& bytes_out) {
int ret = ::wally_asset_blinding_key_to_vbf(bytes.data(), bytes.size(), hash_prevouts.data(), hash_prevouts.size(), output_index, bytes_out.data(), bytes_out.size());
Expand Down Expand Up @@ -2426,6 +2438,24 @@ inline int elements_pegout_script_size(size_t genesis_blockhash_len, size_t main
return detail::check_ret(__FUNCTION__, ret);
}

template <class BYTES, class SCRIPT, class BYTES_OUT>
inline int elip150_private_key_to_ec_private_key(const BYTES& bytes, const SCRIPT& script, BYTES_OUT& bytes_out) {
int ret = ::wally_elip150_private_key_to_ec_private_key(bytes.data(), bytes.size(), script.data(), script.size(), bytes_out.data(), bytes_out.size());
return detail::check_ret(__FUNCTION__, ret);
}

template <class BYTES, class SCRIPT, class BYTES_OUT>
inline int elip150_private_key_to_ec_public_key(const BYTES& bytes, const SCRIPT& script, BYTES_OUT& bytes_out) {
int ret = ::wally_elip150_private_key_to_ec_public_key(bytes.data(), bytes.size(), script.data(), script.size(), bytes_out.data(), bytes_out.size());
return detail::check_ret(__FUNCTION__, ret);
}

template <class BYTES, class SCRIPT, class BYTES_OUT>
inline int elip150_public_key_to_ec_public_key(const BYTES& bytes, const SCRIPT& script, BYTES_OUT& bytes_out) {
int ret = ::wally_elip150_public_key_to_ec_public_key(bytes.data(), bytes.size(), script.data(), script.size(), bytes_out.data(), bytes_out.size());
return detail::check_ret(__FUNCTION__, ret);
}

template <class NONCE, class VBF, class COMMITMENT, class GENERATOR, class BYTES_OUT>
inline int explicit_rangeproof(uint64_t value, const NONCE& nonce, const VBF& vbf, const COMMITMENT& commitment, const GENERATOR& generator, BYTES_OUT& bytes_out, size_t* written) {
int ret = ::wally_explicit_rangeproof(value, nonce.data(), nonce.size(), vbf.data(), vbf.size(), commitment.data(), commitment.size(), generator.data(), generator.size(), bytes_out.data(), bytes_out.size(), written);
Expand Down
18 changes: 18 additions & 0 deletions include/wally_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,24 @@ WALLY_CORE_API int wally_ec_public_key_negate(
unsigned char *bytes_out,
size_t len);

/**
* Tweak a public key.
*
* :param pub_key: The public key to tweak.
* :param pub_key_len: The length of ``pub_key`` in bytes. Must be `EC_PUBLIC_KEY_LEN`.
* :param tweak: The scalar/private key to tweak by.
* :param tweak_len: The length of ``tweak``. Must be `EC_PRIVATE_KEY_LEN`.
* :param bytes_out: Destination for the tweaked public key.
* FIXED_SIZED_OUTPUT(len, bytes_out, EC_PUBLIC_KEY_LEN)
*/
WALLY_CORE_API int wally_ec_public_key_tweak(
const unsigned char *pub_key,
size_t pub_key_len,
const unsigned char *tweak,
size_t tweak_len,
unsigned char *bytes_out,
size_t len);

/**
* Tweak a compressed or x-only public key for taproot.
*
Expand Down
35 changes: 23 additions & 12 deletions include/wally_descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,25 @@ struct wally_descriptor;
#define WALLY_MINISCRIPT_DEPTH_SHIFT 16 /** Shift to convert maximum depth to flags */

/*** miniscript-features Miniscript/Descriptor feature flags */
#define WALLY_MS_IS_RANGED 0x001 /** Allows key ranges via ``*`` */
#define WALLY_MS_IS_MULTIPATH 0x002 /** Allows multiple paths via ``<a;b;c>`` */
#define WALLY_MS_IS_PRIVATE 0x004 /** Contains at least one private key */
#define WALLY_MS_IS_UNCOMPRESSED 0x008 /** Contains at least one uncompressed key */
#define WALLY_MS_IS_RAW 0x010 /** Contains at least one raw key */
#define WALLY_MS_IS_DESCRIPTOR 0x020 /** Contains only descriptor expressions (no miniscript) */
#define WALLY_MS_IS_X_ONLY 0x040 /** Contains at least one x-only key */
#define WALLY_MS_IS_PARENTED 0x080 /** Contains at least one key key with a parent key origin */
#define WALLY_MS_IS_ELEMENTS 0x100 /** Contains Elements expressions or was parsed as Elements */
#define WALLY_MS_IS_RANGED 0x001 /** Allows key ranges via ``*`` */
#define WALLY_MS_IS_MULTIPATH 0x002 /** Allows multiple paths via ``<a;b;c>`` */
#define WALLY_MS_IS_PRIVATE 0x004 /** Contains at least one private key */
#define WALLY_MS_IS_UNCOMPRESSED 0x008 /** Contains at least one uncompressed key */
#define WALLY_MS_IS_RAW 0x010 /** Contains at least one raw key */
#define WALLY_MS_IS_DESCRIPTOR 0x020 /** Contains only descriptor expressions (no miniscript) */
#define WALLY_MS_IS_X_ONLY 0x040 /** Contains at least one x-only key */
#define WALLY_MS_IS_PARENTED 0x080 /** Contains at least one key key with a parent key origin */
#define WALLY_MS_IS_ELEMENTS 0x100 /** Contains Elements expressions or was parsed as Elements */
#define WALLY_MS_IS_SLIP77 0x200 /** A confidential ct() descriptor with SLIP-77 blinding */
#define WALLY_MS_IS_ELIP150 0x400 /** A confidential ct() descriptor with ELIP-150 blinding */
#define WALLY_MS_IS_ELIP151 0x800 /** A confidential ct() descriptor with ELIP-151 blinding */
#define WALLY_MS_ANY_BLINDING_KEY 0xE00 /** SLIP-77, ELIP-150 or ELIP-151 blinding key present */

/*** ms-canonicalization-flags Miniscript/Descriptor canonicalization flags */
#define WALLY_MS_CANONICAL_NO_CHECKSUM 0x01 /** Do not include a checksum */

#define WALLY_MS_BLINDING_KEY_INDEX 0xffffffff /* Key index for confidential blinding key */

/**
* Parse an output descriptor or miniscript expression.
*
Expand Down Expand Up @@ -192,7 +198,8 @@ WALLY_CORE_API int wally_descriptor_get_depth(
* :param descriptor: Parsed output descriptor or miniscript expression.
* :param value_out: Destination for the number of keys.
*
* .. note:: Repeated keys are counted once for each time they appear.
* .. note:: Repeated keys are counted once for each time they appear, and any
*| blinding key within the descriptor is not included in the count.
*/
WALLY_CORE_API int wally_descriptor_get_num_keys(
const struct wally_descriptor *descriptor,
Expand All @@ -202,13 +209,16 @@ WALLY_CORE_API int wally_descriptor_get_num_keys(
* Get the string representation of a key in a parsed output descriptor or miniscript expression.
*
* :param descriptor: Parsed output descriptor or miniscript expression.
* :param index: The zero-based index of the key to get.
* :param index: The zero-based index of the key to get, or `WALLY_MS_BLINDING_KEY_INDEX`
*| to fetch the descriptors blinding key representaton (if any).
* :param output: Destination for the resulting string representation.
*| The string returned should be freed using `wally_free_string`.
*
* .. note:: Keys may be BIP32 xpub/xpriv, WIF or hex pubkeys, and may be
*| x-only. The caller can use `wally_descriptor_get_key_features` to
*| determine the type of a given key.
*
* .. note:: Raw private blinding keys are returned as hex, not WIF.
*/
WALLY_CORE_API int wally_descriptor_get_key(
const struct wally_descriptor *descriptor,
Expand All @@ -219,7 +229,8 @@ WALLY_CORE_API int wally_descriptor_get_key(
* Get the features of a key in a parsed output descriptor or miniscript expression.
*
* :param descriptor: Parsed output descriptor or miniscript expression.
* :param index: The zero-based index of the key to get.
* :param index: The zero-based index of the key to get, or `WALLY_MS_BLINDING_KEY_INDEX`
*| to fetch the descriptors blinding key features (if any).
* :param value_out: Destination for the resulting :ref:`miniscript-features`.
*/
WALLY_CORE_API int wally_descriptor_get_key_features(
Expand Down
66 changes: 65 additions & 1 deletion include/wally_elements.h
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ WALLY_CORE_API int wally_asset_blinding_key_from_seed(
size_t len);

/**
* Generate a blinding private key for a scriptPubkey.
* Generate a blinding private key for a scriptPubkey from a SLIP-0077 master blinding key.
*
* :param bytes: A full master blinding key, e.g. from `wally_asset_blinding_key_from_seed`,
*| or a partial key of length `SHA256_LEN`, typically from the last half of the full key.
Expand All @@ -513,6 +513,70 @@ WALLY_CORE_API int wally_asset_blinding_key_to_ec_private_key(
unsigned char *bytes_out,
size_t len);

/**
* Generate a blinding public key for a scriptPubkey from a SLIP-0077 master blinding key.
*
* :param bytes: A full master blinding key, e.g. from `wally_asset_blinding_key_from_seed`,
*| or a partial key of length `SHA256_LEN`, typically from the last half of the full key.
* :param bytes_len: Length of ``bytes``. Must be `HMAC_SHA512_LEN` or `SHA256_LEN`.
* :param script: The scriptPubkey for the confidential output address.
* :param script_len: Length of ``script``.
* :param bytes_out: Destination for the resulting blinding public key.
* FIXED_SIZED_OUTPUT(len, bytes_out, EC_PUBLIC_KEY_LEN)
*/
WALLY_CORE_API int wally_asset_blinding_key_to_ec_public_key(
const unsigned char *bytes,
size_t bytes_len,
const unsigned char *script,
size_t script_len,
unsigned char *bytes_out,
size_t len);

/**
* Generate a blinding private key for a scriptPubkey from an ELIP-0150 blinding private key.
*
* :param bytes: An ELIP-0150 blinding private key ("View Key"), e.g. from a ct() descriptor.
* :param bytes_len: Length of ``bytes``. Must be `EC_PRIVATE_KEY_LEN`.
* :param script: The scriptPubkey for the confidential output address.
* :param script_len: Length of ``script``.
* :param bytes_out: Destination for the resulting blinding private key.
* FIXED_SIZED_OUTPUT(len, bytes_out, EC_PRIVATE_KEY_LEN)
*/
WALLY_CORE_API int wally_elip150_private_key_to_ec_private_key(
const unsigned char *bytes, size_t bytes_len,
const unsigned char *script, size_t script_len,
unsigned char *bytes_out, size_t len);

/**
* Generate a blinding public key for a scriptPubkey from an ELIP-0150 blinding private key.
*
* :param bytes: An ELIP-0150 blinding private key ("View Key"), e.g. from a ct() descriptor.
* :param bytes_len: Length of ``bytes``. Must be `EC_PRIVATE_KEY_LEN`.
* :param script: The scriptPubkey for the confidential output address.
* :param script_len: Length of ``script``.
* :param bytes_out: Destination for the resulting blinding public key.
* FIXED_SIZED_OUTPUT(len, bytes_out, EC_PUBLIC_KEY_LEN)
*/
WALLY_CORE_API int wally_elip150_private_key_to_ec_public_key(
const unsigned char *bytes, size_t bytes_len,
const unsigned char *script, size_t script_len,
unsigned char *bytes_out, size_t len);

/**
* Generate a blinding public key for a scriptPubkey from an ELIP-0150 blinding public key.
*
* :param bytes: An ELIP-0150 blinding public key, e.g. from a ct() descriptor.
* :param bytes_len: Length of ``bytes``. Must be `EC_PUBLIC_KEY_LEN`.
* :param script: The scriptPubkey for the confidential output address.
* :param script_len: Length of ``script``.
* :param bytes_out: Destination for the resulting blinding public key.
* FIXED_SIZED_OUTPUT(len, bytes_out, EC_PUBLIC_KEY_LEN)
*/
WALLY_CORE_API int wally_elip150_public_key_to_ec_public_key(
const unsigned char *bytes, size_t bytes_len,
const unsigned char *script, size_t script_len,
unsigned char *bytes_out, size_t len);

#define WALLY_ABF_VBF_LEN 64

/**
Expand Down
49 changes: 17 additions & 32 deletions src/bip32.c
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,6 @@ int bip32_key_from_parent(const struct ext_key *hdkey, uint32_t child_num,
uint32_t flags, struct ext_key *key_out)
{
struct sha512 sha;
const secp256k1_context *ctx;
const bool we_are_private = hdkey && key_is_private(hdkey);
const bool derive_private = !(flags & BIP32_FLAG_KEY_PUBLIC);
const bool hardened = child_is_hardened(child_num);
Expand All @@ -650,9 +649,6 @@ int bip32_key_from_parent(const struct ext_key *hdkey, uint32_t child_num,
if (!hdkey || !key_out)
return WALLY_EINVAL;

if (!(ctx = secp_ctx()))
return WALLY_ENOMEM;

if (!we_are_private && (derive_private || hardened))
return wipe_key_fail(key_out); /* Unsupported derivation */

Expand Down Expand Up @@ -710,20 +706,14 @@ int bip32_key_from_parent(const struct ext_key *hdkey, uint32_t child_num,
} else {
/* The returned child key ki is point(parse256(IL) + kpar)
* In case parse256(IL) ≥ n or Ki is the point at infinity, the
* resulting key is invalid (NOTE: pubkey_tweak_add checks both
* conditions)
* resulting key is invalid (NOTE: wally_ec_public_key_tweak checks
* both conditions)
*/
secp256k1_pubkey pub_key;
size_t len = sizeof(key_out->pub_key);

/* FIXME: Out of bounds on pubkey_tweak_add */
if (!pubkey_parse(&pub_key, hdkey->pub_key, sizeof(hdkey->pub_key)) ||
!pubkey_tweak_add(ctx, &pub_key, sha.u.u8) ||
!pubkey_serialize(key_out->pub_key, &len, &pub_key,
PUBKEY_COMPRESSED) ||
len != sizeof(key_out->pub_key)) {
if (wally_ec_public_key_tweak(hdkey->pub_key, sizeof(hdkey->pub_key),
sha.u.u8, EC_PRIVATE_KEY_LEN,
key_out->pub_key, sizeof(key_out->pub_key))
!= WALLY_OK)
goto fail;
}
}
#ifndef WALLY_ABI_NO_ELEMENTS
memset(key_out->pub_key_tweak_sum, 0,
Expand Down Expand Up @@ -862,27 +852,22 @@ int bip32_key_with_tweak_from_parent_path(const struct ext_key *hdkey,
#ifndef BUILD_ELEMENTS
return WALLY_ERROR;
#else
const secp256k1_context *ctx;
secp256k1_pubkey pub_key;
size_t len = EC_PUBLIC_KEY_LEN;
int ret;

if (!(ctx = secp_ctx()))
return WALLY_ENOMEM;

if (!(flags & (BIP32_FLAG_KEY_TWEAK_SUM | BIP32_FLAG_KEY_PUBLIC)))
return WALLY_EINVAL;

if ((ret = bip32_key_from_parent_path(hdkey, child_path,
child_path_len, flags, output)) != WALLY_OK)
return ret;

if (!pubkey_parse(&pub_key, hdkey->pub_key, sizeof(hdkey->pub_key)) ||
!pubkey_tweak_add(ctx, &pub_key, output->pub_key_tweak_sum) ||
!pubkey_serialize(output->pub_key, &len, &pub_key, PUBKEY_COMPRESSED))
return wipe_key_fail(output);

return WALLY_OK;
ret = bip32_key_from_parent_path(hdkey, child_path,
child_path_len, flags, output);
if (ret == WALLY_OK) {
ret = wally_ec_public_key_tweak(hdkey->pub_key, sizeof(hdkey->pub_key),
output->pub_key_tweak_sum,
sizeof(output->pub_key_tweak_sum),
output->pub_key, sizeof(output->pub_key));
if (ret != WALLY_OK)
wipe_key_fail(output);
}
return ret;
#endif /* BUILD_ELEMENTS */
}

Expand Down
14 changes: 7 additions & 7 deletions src/blech32.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ int wally_confidential_addr_to_addr_segwit(
return WALLY_ERROR;
#else
unsigned char buf[WALLY_BLECH32_MAXLEN];
unsigned char *hash_bytes_p = &buf[EC_PUBLIC_KEY_LEN - 2];
unsigned char *p = &buf[EC_PUBLIC_KEY_LEN - 2];
size_t written = 0;
int ret;
uint8_t witver;
Expand All @@ -237,9 +237,9 @@ int wally_confidential_addr_to_addr_segwit(
ret = WALLY_EINVAL;
else {
written = written - EC_PUBLIC_KEY_LEN + 2;
hash_bytes_p[0] = value_to_op_n(witver);
hash_bytes_p[1] = (unsigned char) (written - 2);
ret = wally_addr_segwit_from_bytes(hash_bytes_p, written,
p[0] = value_to_op_n(witver);
p[1] = (unsigned char) (written - 2);
ret = wally_addr_segwit_from_bytes(p, written,
addr_family, 0, output);
}

Expand Down Expand Up @@ -290,7 +290,7 @@ int wally_confidential_addr_from_addr_segwit(
#else
char result[WALLY_BLECH32_MAXLEN + 1];
unsigned char buf[EC_PUBLIC_KEY_LEN + SHA256_LEN];
unsigned char *hash_bytes_p = &buf[EC_PUBLIC_KEY_LEN - 2];
unsigned char *p = &buf[EC_PUBLIC_KEY_LEN - 2];
size_t written = SHA256_LEN + 2;
int ret;
size_t witver;
Expand All @@ -305,14 +305,14 @@ int wally_confidential_addr_from_addr_segwit(

/* get witness program's script */
ret = wally_addr_segwit_to_bytes(address, addr_family, 0,
hash_bytes_p, written, &written);
p, written, &written);
if (ret == WALLY_OK) {
if ((written != (HASH160_LEN + 2)) && (written != (SHA256_LEN + 2))) {
ret = WALLY_EINVAL;
goto done;
}

if (!script_is_op_n(hash_bytes_p[0], true, &witver)) {
if (!script_is_op_n(p[0], true, &witver)) {
ret = WALLY_EINVAL;
goto done;
}
Expand Down
Loading