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

descriptor: Implement wallet-policies BIP #369

Merged
merged 25 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8e4fc85
descriptor-policy: initial implementation of wallet-policies
jgriffiths Sep 5, 2023
2e78e3d
descriptor-policy: ensure policy key expressions are unique
jgriffiths Sep 5, 2023
defa31b
descriptor-policy: ensure at least one policy key is present
jgriffiths Sep 5, 2023
6cb042e
descriptor-policy: ensure key ordering is sequential
jgriffiths Sep 6, 2023
8ef414d
descriptor: store and expose the number of keys in a descriptor
jgriffiths Sep 6, 2023
4b24d1a
descriptor-policy: ensure all policy keys are substituted key express…
jgriffiths Sep 6, 2023
1874a8e
descriptor-policy: disallow mixed cardinality key expressions
jgriffiths Sep 6, 2023
41efd7c
descriptor-policy: disallow solved cardinalities other than 1 and 2
jgriffiths Sep 6, 2023
df75323
descriptor: store key nodes when parsing
jgriffiths Sep 7, 2023
625fb62
descriptor: allow iterating keys
jgriffiths Sep 7, 2023
b28398d
descriptor: allow fetching child paths from iterated keys
jgriffiths Sep 7, 2023
2656858
descriptor: expose whether x-only keys are present in features
jgriffiths Sep 7, 2023
fa9b984
descriptor: store all features in the node flags as well as the context
jgriffiths Sep 10, 2023
f298a44
descriptor: allow caller to fetch node features for keys
jgriffiths Sep 10, 2023
ef7220b
descriptor-policy: rename flag to disambiguate from miniscript policy
jgriffiths Sep 11, 2023
1e3eac7
descriptor-policy: pass correctly sized key information map for tests
jgriffiths Sep 11, 2023
d705b70
descriptor-policy: add a flag for enforcing unique keypaths in policies
jgriffiths Sep 11, 2023
ad635b5
descriptor: allow fetching key origin info from keys
jgriffiths Sep 12, 2023
5fcce12
aes: check explicitly for encryption or decryption flags
jgriffiths Sep 19, 2023
b5aed77
aes: provide aes_len and remove manual wrapper hacks for it
jgriffiths Sep 19, 2023
28c72d9
aes: provide aes_cbc_get_maximum_length and remove manual wrapper hac…
jgriffiths Sep 19, 2023
de6316d
python: remove py2 support from SHA test and clean it up.
jgriffiths Sep 20, 2023
375983c
python: make the auto-buffer assertion more helpful
jgriffiths Sep 20, 2023
06ce5e7
aes: add aes_cbc_with_ecdh_key for ephemeral request/response encryption
jgriffiths Sep 20, 2023
7446283
docs: minor improvements
jgriffiths Sep 20, 2023
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ $ brew install swig
### configure options

- `--enable-debug`. Enables debugging information and disables compiler
optimisations (default: no).
optimizations (default: no).
- `--enable-minimal`. Minimises library size and memory requirements to target
embedded or resource-constrained environments (default: no).
- `--enable-asm`. Enables fast assembly language implementations where available.
Expand Down
73 changes: 73 additions & 0 deletions include/wally.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,31 @@ inline int aes_cbc(const KEY& key, const IV& iv, const BYTES& bytes, uint32_t fl
return written || ret != WALLY_OK ? ret : n == static_cast<size_t>(bytes_out.size()) ? WALLY_OK : WALLY_EINVAL;
}

template <class KEY, class IV, class BYTES>
inline int aes_cbc_get_maximum_length(const KEY& key, const IV& iv, const BYTES& bytes, uint32_t flags, size_t* written) {
int ret = ::wally_aes_cbc_get_maximum_length(key.data(), key.size(), iv.data(), iv.size(), bytes.data(), bytes.size(), flags, written);
return ret;
}

template <class PRIV_KEY, class IV, class BYTES, class PUB_KEY, class LABEL, class BYTES_OUT>
inline int aes_cbc_with_ecdh_key(const PRIV_KEY& priv_key, const IV& iv, const BYTES& bytes, const PUB_KEY& pub_key, const LABEL& label, uint32_t flags, BYTES_OUT& bytes_out, size_t* written = 0) {
size_t n;
int ret = ::wally_aes_cbc_with_ecdh_key(priv_key.data(), priv_key.size(), iv.data(), iv.size(), bytes.data(), bytes.size(), pub_key.data(), pub_key.size(), label.data(), label.size(), flags, bytes_out.data(), bytes_out.size(), written ? written : &n);
return written || ret != WALLY_OK ? ret : n == static_cast<size_t>(bytes_out.size()) ? WALLY_OK : WALLY_EINVAL;
}

template <class PRIV_KEY, class IV, class BYTES, class PUB_KEY, class LABEL>
inline int aes_cbc_with_ecdh_key_get_maximum_length(const PRIV_KEY& priv_key, const IV& iv, const BYTES& bytes, const PUB_KEY& pub_key, const LABEL& label, uint32_t flags, size_t* written) {
int ret = ::wally_aes_cbc_with_ecdh_key_get_maximum_length(priv_key.data(), priv_key.size(), iv.data(), iv.size(), bytes.data(), bytes.size(), pub_key.data(), pub_key.size(), label.data(), label.size(), flags, written);
return ret;
}

template <class KEY, class BYTES>
inline int aes_len(const KEY& key, const BYTES& bytes, uint32_t flags, size_t* written) {
int ret = ::wally_aes_len(key.data(), key.size(), bytes.data(), bytes.size(), flags, written);
return ret;
}

template <class BYTES>
inline int base58_from_bytes(const BYTES& bytes, uint32_t flags, char** output) {
int ret = ::wally_base58_from_bytes(bytes.data(), bytes.size(), flags, output);
Expand Down Expand Up @@ -504,12 +529,60 @@ inline int descriptor_get_features(const DESCRIPTOR& descriptor, uint32_t* value
return ret;
}

template <class DESCRIPTOR>
inline int descriptor_get_key(const DESCRIPTOR& descriptor, size_t index, char** output) {
int ret = ::wally_descriptor_get_key(detail::get_p(descriptor), index, output);
return ret;
}

template <class DESCRIPTOR>
inline int descriptor_get_key_child_path_str(const DESCRIPTOR& descriptor, size_t index, char** output) {
int ret = ::wally_descriptor_get_key_child_path_str(detail::get_p(descriptor), index, output);
return ret;
}

template <class DESCRIPTOR>
inline int descriptor_get_key_child_path_str_len(const DESCRIPTOR& descriptor, size_t index, size_t* written) {
int ret = ::wally_descriptor_get_key_child_path_str_len(detail::get_p(descriptor), index, written);
return ret;
}

template <class DESCRIPTOR>
inline int descriptor_get_key_features(const DESCRIPTOR& descriptor, size_t index, uint32_t* value_out) {
int ret = ::wally_descriptor_get_key_features(detail::get_p(descriptor), index, value_out);
return ret;
}

template <class DESCRIPTOR, class BYTES_OUT>
inline int descriptor_get_key_origin_fingerprint(const DESCRIPTOR& descriptor, size_t index, BYTES_OUT& bytes_out) {
int ret = ::wally_descriptor_get_key_origin_fingerprint(detail::get_p(descriptor), index, bytes_out.data(), bytes_out.size());
return ret;
}

template <class DESCRIPTOR>
inline int descriptor_get_key_origin_path_str(const DESCRIPTOR& descriptor, size_t index, char** output) {
int ret = ::wally_descriptor_get_key_origin_path_str(detail::get_p(descriptor), index, output);
return ret;
}

template <class DESCRIPTOR>
inline int descriptor_get_key_origin_path_str_len(const DESCRIPTOR& descriptor, size_t index, size_t* written) {
int ret = ::wally_descriptor_get_key_origin_path_str_len(detail::get_p(descriptor), index, written);
return ret;
}

template <class DESCRIPTOR>
inline int descriptor_get_network(const DESCRIPTOR& descriptor, uint32_t* value_out) {
int ret = ::wally_descriptor_get_network(detail::get_p(descriptor), value_out);
return ret;
}

template <class DESCRIPTOR>
inline int descriptor_get_num_keys(const DESCRIPTOR& descriptor, uint32_t* value_out) {
int ret = ::wally_descriptor_get_num_keys(detail::get_p(descriptor), value_out);
return ret;
}

template <class DESCRIPTOR>
inline int descriptor_get_num_paths(const DESCRIPTOR& descriptor, uint32_t* value_out) {
int ret = ::wally_descriptor_get_num_paths(detail::get_p(descriptor), value_out);
Expand Down
125 changes: 122 additions & 3 deletions include/wally_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,30 @@ WALLY_CORE_API int wally_scrypt(
#define AES_FLAG_ENCRYPT 1 /** Encrypt */
#define AES_FLAG_DECRYPT 2 /** Decrypt */

/**
* Get the length of encrypted/decrypted data using AES (ECB mode, no padding).
*
* :param key: Encryption/decryption key.
* :param key_len: Length of ``key`` in bytes. Must be one of the :ref:`aes-key-length`.
* :param bytes: Bytes to encrypt/decrypt.
* :param bytes_len: Length of ``bytes`` in bytes. Must be a multiple of `AES_BLOCK_LEN`.
* :param flags: :ref:`aes-operation-flag` indicating the desired behavior.
* :param written: Destination for the length of the encrypted/decrypted data.
*
* This function returns ``bytes_len`` assuming its arguments are valid.
*/
WALLY_CORE_API int wally_aes_len(
const unsigned char *key,
size_t key_len,
const unsigned char *bytes,
size_t bytes_len,
uint32_t flags,
size_t *written);

/**
* Encrypt/decrypt data using AES (ECB mode, no padding).
*
* :param key: Key material for initialisation.
* :param key: Encryption/decryption key.
* :param key_len: Length of ``key`` in bytes. Must be one of the :ref:`aes-key-length`.
* :param bytes: Bytes to encrypt/decrypt.
* :param bytes_len: Length of ``bytes`` in bytes. Must be a multiple of `AES_BLOCK_LEN`.
Expand All @@ -66,12 +86,36 @@ WALLY_CORE_API int wally_aes(
unsigned char *bytes_out,
size_t len);

/**
* Get the maximum length of encrypted/decrypted data using AES (CBC mode, PKCS#7 padding).
*
* :param key: Encryption/decryption key.
* :param key_len: Length of ``key`` in bytes. Must be one of the :ref:`aes-key-length`.
* :param iv: Initialization vector. For encryption this should be secure entropy. For
*| decryption the bytes used when encrypting must be given.
* :param iv_len: Length of ``iv`` in bytes. Must be `AES_BLOCK_LEN`.
* :param bytes: Bytes to encrypt/decrypt.
* :param bytes_len: Length of ``bytes`` in bytes. Can be of any length for encryption, must be a multiple of `AES_BLOCK_LEN` for decryption.
* :param flags: :ref:`aes-operation-flag` indicating the desired behavior.
* :param written: Destination for the maximum length of the encrypted/decrypted data.
*/
WALLY_CORE_API int wally_aes_cbc_get_maximum_length(
const unsigned char *key,
size_t key_len,
const unsigned char *iv,
size_t iv_len,
const unsigned char *bytes,
size_t bytes_len,
uint32_t flags,
size_t *written);

/**
* Encrypt/decrypt data using AES (CBC mode, PKCS#7 padding).
*
* :param key: Key material for initialisation.
* :param key: Encryption/decryption key.
* :param key_len: Length of ``key`` in bytes. Must be one of the :ref:`aes-key-length`.
* :param iv: Initialisation vector.
* :param iv: Initialization vector. For encryption this should be secure entropy. For
*| decryption the bytes used when encrypting must be given.
* :param iv_len: Length of ``iv`` in bytes. Must be `AES_BLOCK_LEN`.
* :param bytes: Bytes to encrypt/decrypt.
* :param bytes_len: Length of ``bytes`` in bytes. Can be of any length for encryption, must be a multiple of `AES_BLOCK_LEN` for decryption.
Expand Down Expand Up @@ -849,6 +893,81 @@ WALLY_CORE_API int wally_s2c_commitment_verify(
size_t s2c_opening_len,
uint32_t flags);

/**
* Get the maximum length of data encrypted/decrypted using `wally_aes_cbc_with_ecdh_key`.
*
* :param priv_key: The callers private key used for Diffie-Helman exchange.
* :param priv_key_len: The length of ``priv_key`` in bytes. Must be `EC_PRIVATE_KEY_LEN`.
* :param iv: Initialization vector. Only required when encrypting, otherwise pass NULL.
* :param iv_len: Length of ``iv`` in bytes. Must be `AES_BLOCK_LEN`.
* :param bytes: Bytes to encrypt/decrypt.
* :param bytes_len: Length of ``bytes`` in bytes.
* :param pub_key: The other parties public key used for Diffie-Helman exchange.
* :param pub_key_len: Length of ``pub_key`` in bytes. Must be `EC_PUBLIC_KEY_LEN`.
* :param label: A non-empty array of bytes for internal key generation. Must
*| be the same (fixed) value when encrypting and decrypting.
* :param label_len: Length of ``label`` in bytes.
* :param flags: :ref:`aes-operation-flag` indicating the desired behavior.
* :param written: Destination for the maximum length of the encrypted/decrypted data.
*/
WALLY_CORE_API int wally_aes_cbc_with_ecdh_key_get_maximum_length(
const unsigned char *priv_key,
size_t priv_key_len,
const unsigned char *iv,
size_t iv_len,
const unsigned char *bytes,
size_t bytes_len,
const unsigned char *pub_key,
size_t pub_key_len,
const unsigned char *label,
size_t label_len,
uint32_t flags,
size_t *written);

/**
* Encrypt/decrypt data using AES-256 (CBC mode, PKCS#7 padding) and a shared Diffie-Helman secret.
*
* :param priv_key: The callers private key used for Diffie-Helman exchange.
* :param priv_key_len: The length of ``priv_key`` in bytes. Must be `EC_PRIVATE_KEY_LEN`.
* :param iv: Initialization vector. Only required when encrypting, otherwise pass NULL.
* :param iv_len: Length of ``iv`` in bytes. Must be `AES_BLOCK_LEN` if encrypting otherwise 0.
* :param bytes: Bytes to encrypt/decrypt.
* :param bytes_len: Length of ``bytes`` in bytes.
* :param pub_key: The other parties public key used for Diffie-Helman exchange.
* :param pub_key_len: Length of ``pub_key`` in bytes. Must be `EC_PUBLIC_KEY_LEN`.
* :param label: A non-empty array of bytes for internal key generation. Must
*| be the same (fixed) value when encrypting and decrypting.
* :param label_len: Length of ``label`` in bytes.
* :param flags: :ref:`aes-operation-flag` indicating the desired behavior.
* :param bytes_out: Destination for the encrypted/decrypted data.
* :param len: The length of ``bytes_out`` in bytes.
* :param written: Destination for the number of bytes written to ``bytes_out``.
*
* This function implements a scheme for sharing data using a derived secret.
* Alice creates an ephemeral key pair and sends her public key to Bob along
* with any request details. Bob creates an ephemeral key pair and calls this
* function with his private key and Alices public key to encrypt ``bytes``
* (the request payload). Bob returns his public key and the encrypted data to
* Alice, who calls this function with her private key and Bobs public key
* to decrypt and authenticate the payload. The ``label`` parameter must be
* be the same for both Alice and Bob for a given request/response.
*/
WALLY_CORE_API int wally_aes_cbc_with_ecdh_key(
const unsigned char *priv_key,
size_t priv_key_len,
const unsigned char *iv,
size_t iv_len,
const unsigned char *bytes,
size_t bytes_len,
const unsigned char *pub_key,
size_t pub_key_len,
const unsigned char *label,
size_t label_len,
uint32_t flags,
unsigned char *bytes_out,
size_t len,
size_t *written);

#ifdef __cplusplus
}
#endif
Expand Down
113 changes: 113 additions & 0 deletions include/wally_descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ struct wally_descriptor;
#define WALLY_MINISCRIPT_TAPSCRIPT 0x01 /** Tapscript, use x-only pubkeys */
#define WALLY_MINISCRIPT_ONLY 0x02 /** Only allow miniscript (not descriptor) expressions */
#define WALLY_MINISCRIPT_REQUIRE_CHECKSUM 0x04 /** Require a checksum to be present */
#define WALLY_MINISCRIPT_POLICY_TEMPLATE 0x08 /** Only allow policy templates with @n BIP32 keys */
#define WALLY_MINISCRIPT_UNIQUE_KEYPATHS 0x10 /** For policy templates, ensure BIP32 derivation paths differ for identical keys */
#define WALLY_MINISCRIPT_DEPTH_MASK 0xffff0000 /** Mask for limiting maximum depth */
#define WALLY_MINISCRIPT_DEPTH_SHIFT 16 /** Shift to convert maximum depth to flags */

Expand All @@ -25,6 +27,8 @@ struct wally_descriptor;
#define WALLY_MS_IS_UNCOMPRESSED 0x08 /** Contains at least one uncompressed key */
#define WALLY_MS_IS_RAW 0x10 /** Contains at least one raw key */
#define WALLY_MS_IS_DESCRIPTOR 0x20 /** Contains only descriptor expressions (no miniscript) */
#define WALLY_MS_IS_X_ONLY 0x40 /** Contains at least one x-only key */
#define WALLY_MS_IS_PARENTED 0x80 /** Contains at least one key key with a parent key origin */

/*** ms-canonicalization-flags Miniscript/Descriptor canonicalization flags */
#define WALLY_MS_CANONICAL_NO_CHECKSUM 0x01 /** Do not include a checksum */
Expand Down Expand Up @@ -180,6 +184,115 @@ WALLY_CORE_API int wally_descriptor_get_depth(
const struct wally_descriptor *descriptor,
uint32_t *value_out);

/**
* Get the number of keys in a parsed output descriptor or miniscript expression.
*
* :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.
*/
WALLY_CORE_API int wally_descriptor_get_num_keys(
const struct wally_descriptor *descriptor,
uint32_t *value_out);

/**
* 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 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.
*/
WALLY_CORE_API int wally_descriptor_get_key(
const struct wally_descriptor *descriptor,
size_t index,
char **output);

/**
* 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 value_out: Destination for the resulting :ref:`miniscript-features`.
*/
WALLY_CORE_API int wally_descriptor_get_key_features(
const struct wally_descriptor *descriptor,
size_t index,
uint32_t *value_out);

/**
* Get the length of a keys child path string 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 whose child path to get.
* :param written: Destination for the length of the keys child path string,
*| excluding the NUL terminator (zero if not present).
*/
WALLY_CORE_API int wally_descriptor_get_key_child_path_str_len(
const struct wally_descriptor *descriptor,
size_t index,
size_t *written);

/**
* Get the keys child path string 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 whose child path to get.
* :param output: Destination for the resulting path string (empty if not present).
*| The string returned should be freed using `wally_free_string`.
*/
WALLY_CORE_API int wally_descriptor_get_key_child_path_str(
const struct wally_descriptor *descriptor,
size_t index,
char **output);

/**
* Get the keys parent BIP32 fingerprint 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 whose parent fingerprint to get.
* :param bytes_out: Destination for the fingerprint.
* FIXED_SIZED_OUTPUT(len, bytes_out, BIP32_KEY_FINGERPRINT_LEN)
*
* If the key does not contain key origin information then `WALLY_EINVAL` is returned.
*/
WALLY_CORE_API int wally_descriptor_get_key_origin_fingerprint(
const struct wally_descriptor *descriptor,
size_t index,
unsigned char *bytes_out,
size_t len);

/**
* Get the length of a keys parent path string 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 whose parent path to get.
* :param written: Destination for the length of the keys parent path string,
*| excluding the NUL terminator (zero if not present).
*/
WALLY_CORE_API int wally_descriptor_get_key_origin_path_str_len(
const struct wally_descriptor *descriptor,
size_t index,
size_t *written);

/**
* Get the keys parent path string 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 whose parent path to get.
* :param output: Destination for the resulting path string (empty if not present).
*| The string returned should be freed using `wally_free_string`.
*/
WALLY_CORE_API int wally_descriptor_get_key_origin_path_str(
const struct wally_descriptor *descriptor,
size_t index,
char **output);

/**
* Get the maximum length of a script corresponding to an output descriptor.
*
Expand Down
2 changes: 1 addition & 1 deletion include/wally_transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extern "C" {
#define WALLY_TX_FLAG_ALLOW_PARTIAL 0x4 /* Allow partially complete transactions */
/* Note: This flag encodes/parses transactions that are ambiguous to decode.
Unless you have a good reason to do so you will most likely not need it */
#define WALLY_TX_FLAG_PRE_BIP144 0x8 /* Encode/Decode using pre-BIP 144 serialisation */
#define WALLY_TX_FLAG_PRE_BIP144 0x8 /* Encode/Decode using pre-BIP 144 serialization */

#define WALLY_TX_FLAG_BLINDED_INITIAL_ISSUANCE 0x1

Expand Down
Loading