diff --git a/.gitignore b/.gitignore index 87f8c62ea..b221ba174 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ wally*.sha256 wally*.asc wally*.whl docs/source/address.rst +docs/source/anti_klepto.rst docs/source/bip32.rst docs/source/bip38.rst docs/source/bip39.rst diff --git a/configure.ac b/configure.ac index 31202eedd..38aae9a45 100644 --- a/configure.ac +++ b/configure.ac @@ -424,7 +424,7 @@ export ARFLAGS export AR_FLAGS export LD export LDFLAGS -ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-experimental --enable-module-ecdh --enable-module-recovery --enable-module-rangeproof --enable-module-surjectionproof --enable-module-whitelist --enable-module-generator --enable-openssl-tests=no --enable-tests=no --enable-exhaustive-tests=no --enable-benchmark=no --disable-dependency-tracking ${secp_asm}" +ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --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-openssl-tests=no --enable-tests=no --enable-exhaustive-tests=no --enable-benchmark=no --disable-dependency-tracking ${secp_asm}" AC_CONFIG_SUBDIRS([src/secp256k1]) diff --git a/docs/source/anti_klepto_protocol.rst b/docs/source/anti_klepto_protocol.rst new file mode 100644 index 000000000..179654011 --- /dev/null +++ b/docs/source/anti_klepto_protocol.rst @@ -0,0 +1,76 @@ +Anti-Klepto Protocol +==================== + +.. _anti-klepto-protocol: + +The following walkthrough demonstrates how to use libwally to implement the +ECDSA Anti-Klepto Protocol to prevent a signing device from exfiltrating the +secret signing keys through biased signature nonces. +For the full details, see +`here `_. + +The example code here is written in python using the generated python swig +wrappers. + +Step 1 +------ + +The host draws randomness ``rho`` and computes a commitment to it: + +.. literalinclude:: ../../src/pyexample/anti-klepto.py + :start-after: start-step-1 + :end-before: end-step-1 + +Host sends ``host_commitment`` to the signer. + +Step 2 +------ + +The signing device computes the original nonce ``R``, i.e. ``signer commitment``: + +.. literalinclude:: ../../src/pyexample/anti-klepto.py + :start-after: start-step-2 + :end-before: end-step-2 + +Signing device sends ``signer_commitment`` to the host. + +.. warning:: + If, at any point from this step onward, the hardware device fails, it is + okay to restart the protocol using **exactly the same** ``rho`` and checking + that the hardware device proposes **exactly the same** ``R``. Otherwise, the + hardware device may be selectively aborting and thereby biasing the set of + nonces that are used in actual signatures. + + It takes many (>100) such aborts before there is a plausible attack, given + current knowledge in 2020. However such aborts accumulate even across a total + replacement of all relevant devices (but not across replacement of the actual + signing keys with new independently random ones). + + In case the hardware device cannot be made to sign with the given ``rho``, ``R`` + pair, wallet authors should alert the user and present a very scary message + implying that if this happens more than even a few times, say 20 or more times + EVER, they should change hardware vendors and perhaps sweep their coins. + +Step 3 +------ + +The host replies with ``rho`` generated at `Step 1`_. + +Step 4 +------ + +The signing device signs and provide the signature to the host: + +.. literalinclude:: ../../src/pyexample/anti-klepto.py + :start-after: start-step-4 + :end-before: end-step-4 + +Step 5 +------ + +The host verifies that the signature's public nonce matches the signer +commitment ``R`` from `Step 2`_ and its original randomness ``rho``: + +.. literalinclude:: ../../src/pyexample/anti-klepto.py + :start-after: start-step-5 + :end-before: end-step-5 diff --git a/docs/source/conf.py b/docs/source/conf.py index 5c45bf922..9580bf36b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -34,7 +34,7 @@ def output_func(docs, func): def extract_docs(infile, outfile): lines = [l.strip() for l in open(infile).readlines()] - title = infile.split('_')[1][:-2].capitalize() + ' Functions' + title = infile.split('wally_')[1][:-2].title().replace('_', '-') + ' Functions' title_markup = '=' * len(title) output, current, func, state = [title, title_markup, ''], [], '', SCANNING @@ -62,7 +62,7 @@ def extract_docs(infile, outfile): # Generate the documentation source files for m in [ 'core', 'crypto', 'address', 'bip32', 'bip38', 'bip39', 'script', 'psbt', - 'symmetric', 'transaction', 'elements' + 'symmetric', 'transaction', 'elements', 'anti_klepto' ]: extract_docs('../../include/wally_%s.h' % m, '%s.rst' % m) diff --git a/docs/source/index.rst b/docs/source/index.rst index 37590e050..e69b4c381 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -16,6 +16,7 @@ libwally-core documentation symmetric transaction elements + anti_klepto .. toctree:: :maxdepth: 2 @@ -23,6 +24,7 @@ libwally-core documentation Library Conventions Liquid + Anti Klepto protocol Indices and tables ================== diff --git a/include/wally.hpp b/include/wally.hpp index 61cc73ee0..f2630f6da 100644 --- a/include/wally.hpp +++ b/include/wally.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -255,6 +256,30 @@ 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(bytes_out.size()) ? WALLY_OK : WALLY_EINVAL; } +template +inline int ak_host_commit_from_bytes(const ENTROPY& entropy, uint32_t flags, BYTES_OUT& bytes_out) { + int ret = ::wally_ak_host_commit_from_bytes(entropy.data(), entropy.size(), flags, bytes_out.data(), bytes_out.size()); + return ret; +} + +template +inline int ak_sig_from_bytes(const PRIV_KEY& priv_key, const BYTES& bytes, const ENTROPY& entropy, uint32_t flags, BYTES_OUT& bytes_out) { + int ret = ::wally_ak_sig_from_bytes(priv_key.data(), priv_key.size(), bytes.data(), bytes.size(), entropy.data(), entropy.size(), flags, bytes_out.data(), bytes_out.size()); + return ret; +} + +template +inline int ak_signer_commit_from_bytes(const PRIV_KEY& priv_key, const BYTES& bytes, const COMMITMENT& commitment, uint32_t flags, S2C_OPENING_OUT& s2c_opening_out) { + int ret = ::wally_ak_signer_commit_from_bytes(priv_key.data(), priv_key.size(), bytes.data(), bytes.size(), commitment.data(), commitment.size(), flags, s2c_opening_out.data(), s2c_opening_out.size()); + return ret; +} + +template +inline int ak_verify(const PUB_KEY& pub_key, const BYTES& bytes, const ENTROPY& entropy, const S2C_OPENING& s2c_opening, uint32_t flags, const SIG& sig) { + int ret = ::wally_ak_verify(pub_key.data(), pub_key.size(), bytes.data(), bytes.size(), entropy.data(), entropy.size(), s2c_opening.data(), s2c_opening.size(), flags, sig.data(), sig.size()); + return ret; +} + template 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); @@ -730,6 +755,18 @@ inline int psbt_to_bytes(const PSBT& psbt, uint32_t flags, BYTES_OUT& bytes_out, return written || ret != WALLY_OK ? ret : n == static_cast(bytes_out.size()) ? WALLY_OK : WALLY_EINVAL; } +template +inline int s2c_commitment_verify(const SIG& sig, const S2C_DATA& s2c_data, const S2C_OPENING& s2c_opening, uint32_t flags) { + int ret = ::wally_s2c_commitment_verify(sig.data(), sig.size(), s2c_data.data(), s2c_data.size(), s2c_opening.data(), s2c_opening.size(), flags); + return ret; +} + +template +inline int s2c_sig_from_bytes(const PRIV_KEY& priv_key, const BYTES& bytes, const S2C_DATA& s2c_data, uint32_t flags, S2C_OPENING_OUT& s2c_opening_out, BYTES_OUT& bytes_out) { + int ret = ::wally_s2c_sig_from_bytes(priv_key.data(), priv_key.size(), bytes.data(), bytes.size(), s2c_data.data(), s2c_data.size(), flags, s2c_opening_out.data(), s2c_opening_out.size(), bytes_out.data(), bytes_out.size()); + return ret; +} + template inline int script_push_from_bytes(const BYTES& bytes, uint32_t flags, BYTES_OUT& bytes_out, size_t* written = 0) { size_t n; diff --git a/include/wally_anti_klepto.h b/include/wally_anti_klepto.h new file mode 100644 index 000000000..e6a8327ff --- /dev/null +++ b/include/wally_anti_klepto.h @@ -0,0 +1,124 @@ +#ifndef LIBWALLY_CORE_ANTI_KLEPTO_H +#define LIBWALLY_CORE_ANTI_KLEPTO_H + +#include "wally_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** The length of the commitment to the host provided randomness */ +#define WALLY_HOST_COMMITMENT_LEN 32 + +/** + * Create the initial commitment to host randomness. + * + * :param entropy: Randomness to commit to. It must come from a + *| cryptographically secure RNG. As per the protocol, this value must not + *| be revealed to the client until after the host has received the client + *| commitment. + * :param entropy_len: The length of ``entropy`` in bytes. Must be + *| ``WALLY_S2C_DATA_LEN``. + * :param flags: Must be ``EC_FLAG_ECDSA``. + * :param bytes_out: Destination for the resulting compact signature. + * :param len: The length of ``bytes_out`` in bytes. Must be ``WALLY_HOST_COMMITMENT_LEN``. + * + * .. note:: This function requires external locking if called from multiple threads. + */ +WALLY_CORE_API int wally_ak_host_commit_from_bytes( + const unsigned char *entropy, + size_t entropy_len, + uint32_t flags, + unsigned char *bytes_out, + size_t len); + +/** + * Compute signer's original nonce. + * + * :param priv_key: The private key used for signing. + * :param priv_key_len: The length of ``priv_key`` in bytes. Must be ``EC_PRIVATE_KEY_LEN``. + * :param bytes: The message hash to be signed. + * :param bytes_len: The length of ``bytes`` in bytes. Must be ``EC_MESSAGE_HASH_LEN``. + * :param commitment: Randomness commitment from the host. + * :param commitment_len: The length of ``commitment`` in bytes. Must be + *| ``WALLY_HOST_COMMITMENT_LEN``. + * :param flags: Must be ``EC_FLAG_ECDSA``. + * :param s2c_opening_out: Destination for the resulting opening information. + * :param s2c_opening_out_len: The length of ``s2c_opening_out`` in bytes. Must be + *| ``WALLY_S2C_OPENING_LEN``. + * + * .. note:: This function requires external locking if called from multiple threads. + */ +WALLY_CORE_API int wally_ak_signer_commit_from_bytes( + const unsigned char *priv_key, + size_t priv_key_len, + const unsigned char *bytes, + size_t bytes_len, + const unsigned char *commitment, + size_t commitment_len, + uint32_t flags, + unsigned char *s2c_opening_out, + size_t s2c_opening_out_len); + +/** + * Same as ``wally_ec_sig_from_bytes``, but commits to the host randomness. + * + * :param priv_key: The private key to sign with. + * :param priv_key_len: The length of ``priv_key`` in bytes. Must be ``EC_PRIVATE_KEY_LEN``. + * :param bytes: The message hash to sign. + * :param bytes_len: The length of ``bytes`` in bytes. Must be ``EC_MESSAGE_HASH_LEN``. + * :param entropy: Host provided randomness. + * :param entropy_len: The length of ``entropy`` in bytes. Must be ``WALLY_S2C_DATA_LEN``. + * :param flags: Must be ``EC_FLAG_ECDSA``. + * :param bytes_out: Destination for the resulting compact signature. + * :param len: The length of ``bytes_out`` in bytes. Must be ``EC_SIGNATURE_LEN``. + * + * .. note:: This function requires external locking if called from multiple threads. + */ +WALLY_CORE_API int wally_ak_sig_from_bytes( + const unsigned char *priv_key, + size_t priv_key_len, + const unsigned char *bytes, + size_t bytes_len, + const unsigned char *entropy, + size_t entropy_len, + uint32_t flags, + unsigned char *bytes_out, + size_t len); + +/** + * Verify a signature was correctly constructed using the Anti-Klepto Protocol. + * + * :param pub_key: The public key to verify with. + * :param pub_key_len: The length of ``pub_key`` in bytes. Must be ``EC_PUBLIC_KEY_LEN``. + * :param bytes: The message hash to verify. + * :param bytes_len: The length of ``bytes`` in bytes. Must be ``EC_MESSAGE_HASH_LEN``. + * :param entropy: Randomness provided by the host. + * :param entropy_len: The length of ``entropy`` in bytes. Must be ``WALLY_S2C_DATA_LEN``. + * :param s2c_opening: Opening information provided by the signer. + * :param s2c_opening_len: The length of ``s2c_opening`` in bytes. Must be + *| ``WALLY_S2C_OPENING_LEN``. + * :param flags: Must be ``EC_FLAG_ECDSA``. + * :param sig: The compact signature of the message in ``bytes``. + * :param sig_len: The length of ``sig`` in bytes. Must be ``EC_SIGNATURE_LEN``. + * + * .. note:: This function requires external locking if called from multiple threads. + */ +WALLY_CORE_API int wally_ak_verify( + const unsigned char *pub_key, + size_t pub_key_len, + const unsigned char *bytes, + size_t bytes_len, + const unsigned char *entropy, + size_t entropy_len, + const unsigned char *s2c_opening, + size_t s2c_opening_len, + uint32_t flags, + const unsigned char *sig, + size_t sig_len); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBWALLY_CORE_ANTI_KLEPTO_H */ diff --git a/include/wally_crypto.h b/include/wally_crypto.h index e955e97b4..7c1aef8fb 100644 --- a/include/wally_crypto.h +++ b/include/wally_crypto.h @@ -523,6 +523,66 @@ WALLY_CORE_API int wally_ecdh( unsigned char *bytes_out, size_t len); +/** The length of a data committed using sign-to-contract (s2c) */ +#define WALLY_S2C_DATA_LEN 32 +/** The length of a sign-to-contract (s2c) opening */ +#define WALLY_S2C_OPENING_LEN 33 + +/** + * Sign a message hash with a private key, producing a compact signature which + * commits to additional data using sign-to-contract (s2c). + * + * :param priv_key: The private key to sign with. + * :param priv_key_len: The length of ``priv_key`` in bytes. Must be ``EC_PRIVATE_KEY_LEN``. + * :param bytes: The message hash to sign. + * :param bytes_len: The length of ``bytes`` in bytes. Must be ``EC_MESSAGE_HASH_LEN``. + * :param s2c_data: The data to commit to. + * :param s2c_data_len: The length of ``s2c_data`` in bytes. Must be ``WALLY_S2C_DATA_LEN``. + * :param flags: Must be ``EC_FLAG_ECDSA``. + * :param s2c_opening_out: Destination for the resulting opening information. + * :param s2c_opening_out_len: The length of ``s2c_opening_out`` in bytes. Must be + *| ``WALLY_S2C_OPENING_LEN``. + * :param bytes_out: Destination for the resulting compact signature. + * :param len: The length of ``bytes_out`` in bytes. Must be ``EC_SIGNATURE_LEN``. + * + * .. note:: This function requires external locking if called from multiple threads. + */ +WALLY_CORE_API int wally_s2c_sig_from_bytes( + const unsigned char *priv_key, + size_t priv_key_len, + const unsigned char *bytes, + size_t bytes_len, + const unsigned char *s2c_data, + size_t s2c_data_len, + uint32_t flags, + unsigned char *s2c_opening_out, + size_t s2c_opening_out_len, + unsigned char *bytes_out, + size_t len); + +/** + * Verify a sign-to-contract (s2c) commitment. + * + * :param sig: The compact signature. + * :param sig_len: The length of ``sig`` in bytes. Must be ``EC_SIGNATURE_LEN``. + * :param s2c_data: The data that was committed to. + * :param s2c_data_len: The length of ``s2c_data`` in bytes. Must be ``WALLY_S2C_DATA_LEN``. + * :param s2c_opening: The opening information produced during signing. + * :param s2c_opening_len: The length of ``s2c_opening`` in bytes. Must be + *| ``WALLY_S2C_OPENING_LEN``. + * :param flags: Must be ``EC_FLAG_ECDSA``. + * + * .. note:: This function requires external locking if called from multiple threads. + */ +WALLY_CORE_API int wally_s2c_commitment_verify( + const unsigned char *sig, + size_t sig_len, + const unsigned char *s2c_data, + size_t s2c_data_len, + const unsigned char *s2c_opening, + size_t s2c_opening_len, + uint32_t flags); + #ifdef __cplusplus } #endif diff --git a/src/Makefile.am b/src/Makefile.am index 8d77ab459..67aa9204e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,6 +8,7 @@ noinst_LTLIBRARIES = include_HEADERS = include_HEADERS += $(top_srcdir)/include/wally.hpp include_HEADERS += $(top_srcdir)/include/wally_address.h +include_HEADERS += $(top_srcdir)/include/wally_anti_klepto.h include_HEADERS += $(top_srcdir)/include/wally_bip32.h include_HEADERS += $(top_srcdir)/include/wally_bip38.h include_HEADERS += $(top_srcdir)/include/wally_bip39.h @@ -174,6 +175,7 @@ lib_LTLIBRARIES = libwallycore.la libwallycore_la_SOURCES = \ address.c \ + anti_klepto.c \ aes.c \ base58.c \ bip32.c \ @@ -292,6 +294,7 @@ if SHARED_BUILD_ENABLED if RUN_PYTHON_TESTS $(AM_V_at)$(PYTHON_TEST) test/test_address.py $(AM_V_at)$(PYTHON_TEST) test/test_aes.py + $(AM_V_at)$(PYTHON_TEST) test/test_anti_klepto.py $(AM_V_at)$(PYTHON_TEST) test/test_base58.py $(AM_V_at)$(PYTHON_TEST) test/test_bech32.py $(AM_V_at)$(PYTHON_TEST) test/test_bip32.py @@ -325,6 +328,7 @@ if USE_SWIG_PYTHON $(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/sha.py $(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/signmessage.py $(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/tx.py + $(AM_V_at)$(PYTHON_SWIGTEST) pyexample/anti-klepto.py if BUILD_ELEMENTS $(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/elements_tx.py $(AM_V_at)$(PYTHON_SWIGTEST) pyexample/liquid/receive-send.py diff --git a/src/anti_klepto.c b/src/anti_klepto.c new file mode 100644 index 000000000..be23bc072 --- /dev/null +++ b/src/anti_klepto.c @@ -0,0 +1,129 @@ +#include "internal.h" +#include +#include +#include + +WALLY_CORE_API int wally_ak_host_commit_from_bytes( + const unsigned char *entropy, + size_t entropy_len, + uint32_t flags, + unsigned char *bytes_out, + size_t len) +{ + const secp256k1_context *ctx = secp_ctx(); + + if (!entropy || entropy_len != WALLY_S2C_DATA_LEN || + flags != EC_FLAG_ECDSA || + !bytes_out || len != WALLY_HOST_COMMITMENT_LEN) + return WALLY_EINVAL; + + if (!ctx) + return WALLY_ENOMEM; + + if (!secp256k1_ecdsa_anti_klepto_host_commit(ctx, bytes_out, entropy)) + return WALLY_ERROR; /* Should not happen! */ + return WALLY_OK; +} + +WALLY_CORE_API int wally_ak_signer_commit_from_bytes( + const unsigned char *priv_key, + size_t priv_key_len, + const unsigned char *bytes, + size_t bytes_len, + const unsigned char *commitment, + size_t commitment_len, + uint32_t flags, + unsigned char *s2c_opening_out, + size_t s2c_opening_out_len) +{ + secp256k1_ecdsa_s2c_opening opening_secp; + const secp256k1_context *ctx = secp_ctx(); + bool ok; + + if (!priv_key || priv_key_len != EC_PRIVATE_KEY_LEN || + !bytes || bytes_len != EC_MESSAGE_HASH_LEN || + !commitment || commitment_len != WALLY_HOST_COMMITMENT_LEN || + flags != EC_FLAG_ECDSA || + !s2c_opening_out || s2c_opening_out_len != WALLY_S2C_OPENING_LEN) + return WALLY_EINVAL; + + if (!ctx) + return WALLY_ENOMEM; + + ok = secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &opening_secp, bytes, priv_key, commitment) && + secp256k1_ecdsa_s2c_opening_serialize(ctx, s2c_opening_out, &opening_secp); + + wally_clear(&opening_secp, sizeof(opening_secp)); + return ok ? WALLY_OK : WALLY_EINVAL; +} + +WALLY_CORE_API int wally_ak_sig_from_bytes( + const unsigned char *priv_key, + size_t priv_key_len, + const unsigned char *bytes, + size_t bytes_len, + const unsigned char *entropy, + size_t entropy_len, + uint32_t flags, + unsigned char *bytes_out, + size_t len) +{ + secp256k1_ecdsa_signature sig_secp; + const secp256k1_context *ctx = secp_ctx(); + bool ok; + + if (!priv_key || priv_key_len != EC_PRIVATE_KEY_LEN || + !bytes || bytes_len != EC_MESSAGE_HASH_LEN || + !entropy || entropy_len != WALLY_S2C_DATA_LEN || + flags != EC_FLAG_ECDSA || + !bytes_out || len != EC_SIGNATURE_LEN) + return WALLY_EINVAL; + + if (!ctx) + return WALLY_ENOMEM; + + ok = secp256k1_anti_klepto_sign(ctx, &sig_secp, bytes, priv_key, entropy) && + secp256k1_ecdsa_signature_serialize_compact(ctx, bytes_out, &sig_secp); + + wally_clear(&sig_secp, sizeof(sig_secp)); + return ok ? WALLY_OK : WALLY_EINVAL; +} + +WALLY_CORE_API int wally_ak_verify( + const unsigned char *pub_key, + size_t pub_key_len, + const unsigned char *bytes, + size_t bytes_len, + const unsigned char *entropy, + size_t entropy_len, + const unsigned char *s2c_opening, + size_t s2c_opening_len, + uint32_t flags, + const unsigned char *sig, + size_t sig_len) +{ + secp256k1_pubkey pub_secp; + secp256k1_ecdsa_signature sig_secp; + secp256k1_ecdsa_s2c_opening opening_secp; + const secp256k1_context *ctx = secp_ctx(); + bool ok; + + if (!pub_key || pub_key_len != EC_PUBLIC_KEY_LEN || + !bytes || bytes_len != EC_MESSAGE_HASH_LEN || + !entropy || entropy_len != WALLY_S2C_DATA_LEN || + !s2c_opening || s2c_opening_len != WALLY_S2C_OPENING_LEN || + flags != EC_FLAG_ECDSA || + !sig || sig_len != EC_SIGNATURE_LEN) + return WALLY_EINVAL; + + if (!ctx) + return WALLY_ENOMEM; + + ok = pubkey_parse(&pub_secp, pub_key, pub_key_len) && + secp256k1_ecdsa_signature_parse_compact(ctx, &sig_secp, sig) && + secp256k1_ecdsa_s2c_opening_parse(ctx, &opening_secp, s2c_opening) && + secp256k1_anti_klepto_host_verify(ctx, &sig_secp, bytes, &pub_secp, entropy, &opening_secp); + + wally_clear_3(&pub_secp, sizeof(pub_secp), &sig_secp, sizeof(sig_secp), &opening_secp, sizeof(opening_secp)); + return ok ? WALLY_OK : WALLY_EINVAL; +} diff --git a/src/internal.h b/src/internal.h index 15e8dcd9d..5b7c4069c 100644 --- a/src/internal.h +++ b/src/internal.h @@ -4,6 +4,7 @@ #include #include "secp256k1/include/secp256k1.h" #include "secp256k1/include/secp256k1_recovery.h" +#include "secp256k1/include/secp256k1_ecdsa_s2c.h" #include #if defined(HAVE_MEMSET_S) #define __STDC_WANT_LIB_EXT1__ 1 diff --git a/src/pyexample/anti-klepto.py b/src/pyexample/anti-klepto.py new file mode 100644 index 000000000..73b9d4c03 --- /dev/null +++ b/src/pyexample/anti-klepto.py @@ -0,0 +1,23 @@ +import wallycore as wally +import os + +rho = os.urandom(32) +priv_key = os.urandom(32) +message_hash = os.urandom(32) +pub_key = wally.ec_public_key_from_private_key(priv_key) + +# start-step-1 +host_commitment = wally.ak_host_commit_from_bytes(rho, wally.EC_FLAG_ECDSA) +# end-step-1 + +# start-step-2 +signer_commitment = wally.ak_signer_commit_from_bytes(priv_key, message_hash, host_commitment, wally.EC_FLAG_ECDSA) +# end-step-2 + +# start-step-4 +signature = wally.ak_sig_from_bytes(priv_key, message_hash, rho, wally.EC_FLAG_ECDSA) +# end-step-4 + +# start-step-5 +wally.ak_verify(pub_key, message_hash, rho, signer_commitment, wally.EC_FLAG_ECDSA, signature) +# end-step-5 diff --git a/src/secp256k1 b/src/secp256k1 index 6370bdd53..673e551f4 160000 --- a/src/secp256k1 +++ b/src/secp256k1 @@ -1 +1 @@ -Subproject commit 6370bdd537492638e5ae0f7235cc73daab13ff12 +Subproject commit 673e551f4d1092e2816f497e5abe2c34ece7235e diff --git a/src/sign.c b/src/sign.c index 0ec1888b0..62bd4aa58 100644 --- a/src/sign.c +++ b/src/sign.c @@ -374,3 +374,67 @@ int wally_format_bitcoin_message(const unsigned char *bytes, size_t bytes_len, } return WALLY_OK; } + +int wally_s2c_sig_from_bytes(const unsigned char *priv_key, size_t priv_key_len, + const unsigned char *bytes, size_t bytes_len, + const unsigned char *s2c_data, size_t s2c_data_len, + uint32_t flags, + unsigned char *s2c_opening_out, size_t s2c_opening_out_len, + unsigned char *bytes_out, size_t len) +{ + secp256k1_ecdsa_signature sig_secp; + secp256k1_ecdsa_s2c_opening opening_secp; + const secp256k1_context *ctx = secp_ctx(); + bool ok; + + if (!priv_key || priv_key_len != EC_PRIVATE_KEY_LEN || + !bytes || bytes_len != EC_MESSAGE_HASH_LEN || + !s2c_data || s2c_data_len != WALLY_S2C_DATA_LEN || + flags != EC_FLAG_ECDSA || + !bytes_out || len != EC_SIGNATURE_LEN || + !s2c_opening_out || s2c_opening_out_len != WALLY_S2C_OPENING_LEN) + return WALLY_EINVAL; + + if (!ctx) + return WALLY_ENOMEM; + + if (!secp256k1_ecdsa_s2c_sign(ctx, &sig_secp, &opening_secp, bytes, priv_key, s2c_data)) { + wally_clear_2(&sig_secp, sizeof(sig_secp), &opening_secp, sizeof(opening_secp)); + if (!secp256k1_ec_seckey_verify(ctx, priv_key)) + return WALLY_EINVAL; /* invalid priv_key */ + return WALLY_ERROR; /* Nonce function failed */ + } + + ok = secp256k1_ecdsa_signature_serialize_compact(ctx, bytes_out, &sig_secp) && + secp256k1_ecdsa_s2c_opening_serialize(ctx, s2c_opening_out, &opening_secp); + + wally_clear_2(&sig_secp, sizeof(sig_secp), &opening_secp, sizeof(opening_secp)); + return ok ? WALLY_OK : WALLY_EINVAL; +} + +int wally_s2c_commitment_verify(const unsigned char *sig, size_t sig_len, + const unsigned char *s2c_data, size_t s2c_data_len, + const unsigned char *s2c_opening, size_t s2c_opening_len, + uint32_t flags) +{ + secp256k1_ecdsa_signature sig_secp; + secp256k1_ecdsa_s2c_opening opening_secp; + const secp256k1_context *ctx = secp_ctx(); + bool ok; + + if (!sig || sig_len != EC_SIGNATURE_LEN || + !s2c_data || s2c_data_len != WALLY_S2C_DATA_LEN || + !s2c_opening || s2c_opening_len != WALLY_S2C_OPENING_LEN || + flags != EC_FLAG_ECDSA) + return WALLY_EINVAL; + + if (!ctx) + return WALLY_ENOMEM; + + ok = secp256k1_ecdsa_signature_parse_compact(ctx, &sig_secp, sig) && + secp256k1_ecdsa_s2c_opening_parse(ctx, &opening_secp, s2c_opening) && + secp256k1_ecdsa_s2c_verify_commit(ctx, &sig_secp, s2c_data, &opening_secp); + + wally_clear_2(&sig_secp, sizeof(sig_secp), &opening_secp, sizeof(opening_secp)); + return ok ? WALLY_OK : WALLY_EINVAL; +} diff --git a/src/swig_java/swig.i b/src/swig_java/swig.i index a45ca8977..ca4af3e02 100644 --- a/src/swig_java/swig.i +++ b/src/swig_java/swig.i @@ -2,6 +2,7 @@ %{ #include "../include/wally_core.h" #include "../include/wally_address.h" +#include "../include/wally_anti_klepto.h" #include "../include/wally_bip32.h" #include "bip32_int.h" #include "../include/wally_bip38.h" @@ -244,6 +245,8 @@ static jbyteArray create_array(JNIEnv *jenv, const unsigned char* p, size_t len) %apply(char *STRING, size_t LENGTH) { (const unsigned char* pub_key, size_t pub_key_len) }; %apply(char *STRING, size_t LENGTH) { (const unsigned char* rangeproof, size_t rangeproof_len) }; %apply(char *STRING, size_t LENGTH) { (const unsigned char* redeem_script, size_t redeem_script_len) }; +%apply(char *STRING, size_t LENGTH) { (const unsigned char* s2c_data, size_t s2c_data_len) }; +%apply(char *STRING, size_t LENGTH) { (const unsigned char* s2c_opening, size_t s2c_opening_len) }; %apply(char *STRING, size_t LENGTH) { (const unsigned char* salt, size_t salt_len) }; %apply(char *STRING, size_t LENGTH) { (const unsigned char* script, size_t script_len) }; %apply(char *STRING, size_t LENGTH) { (const unsigned char* scriptpubkey, size_t scriptpubkey_len) }; @@ -259,6 +262,7 @@ static jbyteArray create_array(JNIEnv *jenv, const unsigned char* p, size_t len) %apply(char *STRING, size_t LENGTH) { (unsigned char* abf_out, size_t abf_out_len) }; %apply(char *STRING, size_t LENGTH) { (unsigned char* asset_out, size_t asset_out_len) }; %apply(char *STRING, size_t LENGTH) { (unsigned char* bytes_out, size_t len) }; +%apply(char *STRING, size_t LENGTH) { (unsigned char* s2c_opening_out, size_t s2c_opening_out_len) }; %apply(char *STRING, size_t LENGTH) { (unsigned char* vbf_out, size_t vbf_out_len) }; /* END AUTOGENERATED */ @@ -825,12 +829,19 @@ static jbyteArray create_array(JNIEnv *jenv, const unsigned char* p, size_t len) %returns_array_(wally_symmetric_key_from_parent, 6, 7, HMAC_SHA512_LEN); %returns_size_t(wally_asset_pak_whitelistproof_size); %returns_size_t(wally_asset_pak_whitelistproof); +%returns_array_(wally_s2c_sig_from_bytes, 10, 11, EC_SIGNATURE_LEN); +%returns_void__(wally_s2c_commitment_verify); +%returns_array_(wally_ak_host_commit_from_bytes, 4, 5, WALLY_HOST_COMMITMENT_LEN); +%returns_array_(wally_ak_signer_commit_from_bytes, 8, 9, WALLY_S2C_OPENING_LEN); +%returns_array_(wally_ak_sig_from_bytes, 8, 9, EC_SIGNATURE_LEN); +%returns_void__(wally_ak_verify); %rename("_cleanup") wally_cleanup; %returns_void__(_cleanup) %include "../include/wally_core.h" %include "../include/wally_address.h" +%include "../include/wally_anti_klepto.h" %include "../include/wally_bip32.h" %include "bip32_int.h" %include "../include/wally_bip38.h" diff --git a/src/swig_python/python_extra.py_in b/src/swig_python/python_extra.py_in index b91e6f7b2..05a03530e 100644 --- a/src/swig_python/python_extra.py_in +++ b/src/swig_python/python_extra.py_in @@ -180,6 +180,20 @@ psbt_get_output_unknown = _wrap_bin(psbt_get_output_unknown, psbt_get_output_unk psbt_to_bytes = _wrap_bin(psbt_to_bytes, psbt_get_length, resize=True) +def _s2c_sig_from_bytes_fn(fn): + def wrapped(*args): + buffers = [bytearray(WALLY_S2C_OPENING_LEN), bytearray(EC_SIGNATURE_LEN)] + ret = fn(*list(args) + buffers) + if ret is None: + return buffers[0], buffers[1] + return ret, buffers[0], buffers[1] + return wrapped +s2c_sig_from_bytes = _s2c_sig_from_bytes_fn(s2c_sig_from_bytes) + +ak_host_commit_from_bytes = _wrap_bin(ak_host_commit_from_bytes, WALLY_HOST_COMMITMENT_LEN) +ak_signer_commit_from_bytes = _wrap_bin(ak_signer_commit_from_bytes, WALLY_S2C_OPENING_LEN) +ak_sig_from_bytes = _wrap_bin(ak_sig_from_bytes, EC_SIGNATURE_LEN) + if is_elements_build(): asset_generator_from_bytes = _wrap_bin(asset_generator_from_bytes, ASSET_GENERATOR_LEN) asset_final_vbf = _wrap_bin(asset_final_vbf, ASSET_TAG_LEN) diff --git a/src/swig_python/swig.i b/src/swig_python/swig.i index 4ef8f55d9..b96d36da5 100644 --- a/src/swig_python/swig.i +++ b/src/swig_python/swig.i @@ -23,6 +23,7 @@ del swig_import_helper #include #include "../include/wally_core.h" #include "../include/wally_address.h" +#include "../include/wally_anti_klepto.h" #include "../include/wally_bip32.h" #include "bip32_int.h" #include "../include/wally_bip38.h" @@ -293,6 +294,8 @@ static void destroy_words(PyObject *obj) { (void)obj; } %pybuffer_nullable_binary(const unsigned char* pub_key, size_t pub_key_len); %pybuffer_nullable_binary(const unsigned char* rangeproof, size_t rangeproof_len); %pybuffer_nullable_binary(const unsigned char* redeem_script, size_t redeem_script_len); +%pybuffer_nullable_binary(const unsigned char* s2c_data, size_t s2c_data_len); +%pybuffer_nullable_binary(const unsigned char* s2c_opening, size_t s2c_opening_len); %pybuffer_nullable_binary(const unsigned char* salt, size_t salt_len); %pybuffer_nullable_binary(const unsigned char* script, size_t script_len); %pybuffer_nullable_binary(const unsigned char* scriptpubkey, size_t scriptpubkey_len); @@ -308,6 +311,7 @@ static void destroy_words(PyObject *obj) { (void)obj; } %pybuffer_output_binary(unsigned char* abf_out, size_t abf_out_len); %pybuffer_output_binary(unsigned char* asset_out, size_t asset_out_len); %pybuffer_output_binary(unsigned char* bytes_out, size_t len); +%pybuffer_output_binary(unsigned char* s2c_opening_out, size_t s2c_opening_out_len); %pybuffer_output_binary(unsigned char* vbf_out, size_t vbf_out_len); /* END AUTOGENERATED */ @@ -352,6 +356,7 @@ static void destroy_words(PyObject *obj) { (void)obj; } %include "../include/wally_core.h" %include "../include/wally_address.h" +%include "../include/wally_anti_klepto.h" %include "../include/wally_bip32.h" %include "bip32_int.h" %include "../include/wally_bip38.h" diff --git a/src/test/test_anti_klepto.py b/src/test/test_anti_klepto.py new file mode 100755 index 000000000..1f3bde22a --- /dev/null +++ b/src/test/test_anti_klepto.py @@ -0,0 +1,94 @@ +import unittest +from util import * + +FLAG_ECDSA = 1 + +class AntiKleptoTests(unittest.TestCase): + + def cbufferize(self, values): + conv = lambda v: make_cbuffer(v)[0] if type(v) is str else v + return [conv(v) for v in values] + + def test_anti_klepto(self): + entropy, host_commitment, signer_commitment, priv_key, pub_key, msg, sig = self.cbufferize( + ['11' * 32, '00' * 32, '00' * 33, '22' * 32, '00' * 33, '33' * 32, '00' * 64]) + + flags = FLAG_ECDSA + + ret = wally_ec_public_key_from_private_key(priv_key, 32, pub_key, 33); + self.assertEqual(WALLY_OK, ret) + + ret = wally_ak_host_commit_from_bytes(entropy, 32, flags, host_commitment, 32) + self.assertEqual(WALLY_OK, ret) + + ret = wally_ak_signer_commit_from_bytes(priv_key, 32, msg, 32, host_commitment, 32, flags, signer_commitment, 33) + self.assertEqual(WALLY_OK, ret) + + ret = wally_ak_sig_from_bytes(priv_key, 32, msg, 32, entropy, 32, flags, sig, 64) + self.assertEqual(WALLY_OK, ret) + + ret = wally_ak_verify(pub_key, 33, msg, 32, entropy, 32, signer_commitment, 33, flags, sig, 64) + self.assertEqual(WALLY_OK, ret) + + # Invalid cases + for args in [ + (None, 32, flags, host_commitment, 32), # Missing host randomness + (entropy, 31, flags, host_commitment, 32), # Incorrect host randomness length + (entropy, 31, 0, host_commitment, 32), # Unsupported flag + (entropy, 31, flags, None, 32), # Missing host commitment + (entropy, 31, flags, host_commitment, 31), # Incorrect host commitment length + ]: + self.assertEqual(WALLY_EINVAL, wally_ak_host_commit_from_bytes(*args)) + + for args in [ + (None, 32, msg, 32, host_commitment, 32, flags, signer_commitment, 33), # Missing privkey + (priv_key, 31, msg, 32, host_commitment, 32, flags, signer_commitment, 33), # Incorrect privkey length + (priv_key, 32, None, 32, host_commitment, 32, flags, signer_commitment, 33), # Missing message + (priv_key, 32, msg, 31, host_commitment, 32, flags, signer_commitment, 33), # Incorrect message length + (priv_key, 32, msg, 32, None, 32, flags, signer_commitment, 33), # Missing host commitment + (priv_key, 32, msg, 32, host_commitment, 31, flags, signer_commitment, 33), # Incorrect host commitment length + (priv_key, 32, msg, 32, host_commitment, 32, 0, signer_commitment, 33), # Unsupported flag + (priv_key, 32, msg, 32, host_commitment, 32, flags, None, 33), # Missing signer commitment + (priv_key, 32, msg, 32, host_commitment, 32, flags, signer_commitment, 32), # Incorrect signer commitment length + ]: + self.assertEqual(WALLY_EINVAL, wally_ak_signer_commit_from_bytes(*args)) + + for args in [ + (None, 32, msg, 32, entropy, 32, flags, sig, 64), # Missing privkey + (priv_key, 31, msg, 32, entropy, 32, flags, sig, 64), # Incorrect privkey length + (priv_key, 32, None, 32, entropy, 32, flags, sig, 64), # Missing message + (priv_key, 32, msg, 31, entropy, 32, flags, sig, 64), # Incorrect message length + (priv_key, 32, msg, 32, None, 32, flags, sig, 64), # Missing host randomness + (priv_key, 32, msg, 32, entropy, 31, flags, sig, 64), # Incorrect host randomness length + (priv_key, 32, msg, 32, entropy, 32, 0, sig, 64), # Unsupported flags + (priv_key, 32, msg, 32, entropy, 32, flags, None, 64), # Missing sig + (priv_key, 32, msg, 32, entropy, 32, flags, sig, 63), # Incorrect sig length + ]: + self.assertEqual(WALLY_EINVAL, wally_ak_sig_from_bytes(*args)) + + inv_pub, inv_msg, inv_rand, inv_opening, inv_sig = self.cbufferize( + ['02' * 33, 'ff' * 32, 'ff' * 32, 'ff' * 33, 'ff' * 64]) + + for args in [ + (None, 32, msg, 32, entropy, 32, signer_commitment, 33, flags, sig, 64), # Missing pubkey + (pub_key, 31, msg, 32, entropy, 32, signer_commitment, 33, flags, sig, 64), # Incorrect pubkey length + (inv_pub, 32, msg, 32, entropy, 32, signer_commitment, 33, flags, sig, 64), # Invalud pubkey + (pub_key, 32, None, 32, entropy, 32, signer_commitment, 33, flags, sig, 64), # Missing message + (pub_key, 32, msg, 31, entropy, 32, signer_commitment, 33, flags, sig, 64), # Incorrect message length + (pub_key, 32, inv_msg, 32, entropy, 32, signer_commitment, 33, flags, sig, 64), # Invalid message + (pub_key, 32, msg, 32, None, 32, signer_commitment, 33, flags, sig, 64), # Missing host randomness + (pub_key, 32, msg, 32, entropy, 31, signer_commitment, 33, flags, sig, 64), # Incorrect host randomness length + (pub_key, 32, msg, 32, inv_rand, 32, signer_commitment, 33, flags, sig, 64), # Invalid host randomness + (pub_key, 32, msg, 32, entropy, 32, None, 33, flags, sig, 64), # Missing opening + (pub_key, 32, msg, 32, entropy, 32, signer_commitment, 32, flags, sig, 64), # Incorrect opening length + (pub_key, 32, msg, 32, entropy, 32, inv_opening, 33, flags, sig, 64), # Invalid opening + (pub_key, 32, msg, 32, entropy, 32, signer_commitment, 33, 0, sig, 64), # Unsupported flags + (pub_key, 32, msg, 32, entropy, 32, signer_commitment, 33, flags, None, 64), # Missing sig + (pub_key, 32, msg, 32, entropy, 32, signer_commitment, 33, flags, sig, 64), # Incorrect sig length + (pub_key, 32, msg, 32, entropy, 32, signer_commitment, 33, flags, inv_sig, 64), # Invalid sig + ]: + self.assertEqual(WALLY_EINVAL, wally_ak_verify(*args)) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/test/test_sign.py b/src/test/test_sign.py index f01d20fd9..1a2293839 100755 --- a/src/test/test_sign.py +++ b/src/test/test_sign.py @@ -244,6 +244,52 @@ def test_recoverable_sig(self): ]: self.assertEqual(WALLY_EINVAL, wally_ec_sig_to_public_key(*args)) + def test_s2c(self): + priv_key, pub_key, msg, s2c_data, sig_out, s2c_opening_out = self.cbufferize( + ['11' * 32, '00' * 32, '22' * 32, '33' * 32, '00' * 64, '00' * 33]) + + flags = FLAG_ECDSA + + self.assertEqual(WALLY_OK, wally_s2c_sig_from_bytes(priv_key, 32, msg, 32, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 64)) + + self.assertEqual(WALLY_OK, wally_ec_public_key_from_private_key(priv_key, 32, pub_key, 33)) + self.assertEqual(WALLY_OK, wally_ec_sig_verify(pub_key, 33, msg, 32, flags, sig_out, 64)) + + self.assertEqual(WALLY_OK, wally_s2c_commitment_verify(sig_out, 64, s2c_data, 32, s2c_opening_out, 33, flags)) + + # Invalid cases + for args in [ + (None, 32, msg, 32, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 64), # Missing privkey + (priv_key, 31, msg, 32, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 64), # Incorrect privkey length + (priv_key, 32, None, 32, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 64), # Missing message + (priv_key, 32, msg, 31, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 64), # Incorrect message length + (priv_key, 32, msg, 32, None, 32, flags, s2c_opening_out, 33, sig_out, 64), # Missing s2c data + (priv_key, 32, msg, 32, s2c_data, 31, flags, s2c_opening_out, 33, sig_out, 64), # Incorrect s2c data length + (priv_key, 32, msg, 32, s2c_data, 32, 0, s2c_opening_out, 33, sig_out, 64), # Unsupported flags + (priv_key, 32, msg, 32, s2c_data, 32, flags, None, 33, sig_out, 64), # Missing s2c opening + (priv_key, 32, msg, 32, s2c_data, 32, flags, s2c_opening_out, 32, sig_out, 64), # Incorrect s2c opening length + (priv_key, 32, msg, 32, s2c_data, 32, flags, s2c_opening_out, 33, None, 64), # Missing sig + (priv_key, 32, msg, 32, s2c_data, 32, flags, s2c_opening_out, 33, sig_out, 63), # Incorrect sig length + ]: + self.assertEqual(WALLY_EINVAL, wally_s2c_sig_from_bytes(*args)) + + inv_sig, inv_s2c_data, inv_s2c_opening = self.cbufferize( + ['ff' * 64, 'ff' * 32, 'ff' * 33]) + + for args in [ + (None, 64, s2c_data, 32, s2c_opening_out, 33, flags), # Missing signature + (sig_out, 63, s2c_data, 32, s2c_opening_out, 33, flags), # Incorrect signature length + (inv_sig, 64, s2c_data, 32, s2c_opening_out, 33, flags), # Invalid signature + (sig_out, 64, None, 32, s2c_opening_out, 33, flags), # Missing s2c data + (sig_out, 64, s2c_data, 31, s2c_opening_out, 33, flags), # Incorrect s2c data length + (sig_out, 64, inv_s2c_data, 32, s2c_opening_out, 33, flags), # Invalid s2c data + (sig_out, 64, s2c_data, 32, None, 33, flags), # Missing s2c opening + (sig_out, 64, s2c_data, 32, s2c_opening_out, 32, flags), # Incorrect s2c opening length + (sig_out, 64, s2c_data, 32, inv_s2c_opening, 33, flags), # Invalid s2c opening + (sig_out, 64, s2c_data, 32, s2c_opening_out, 33, 0), # Unsupported flags + ]: + self.assertEqual(WALLY_EINVAL, wally_s2c_commitment_verify(*args)) + if __name__ == '__main__': unittest.main() diff --git a/src/test/util.py b/src/test/util.py index 774d20e6a..3e79ac690 100755 --- a/src/test/util.py +++ b/src/test/util.py @@ -239,6 +239,10 @@ class wally_psbt(Structure): ('wally_address_to_scriptpubkey', c_int, [c_char_p, c_uint, c_void_p, c_ulong, c_ulong_p]), ('wally_aes', c_int, [c_void_p, c_ulong, c_void_p, c_ulong, c_uint, c_void_p, c_ulong]), ('wally_aes_cbc', c_int, [c_void_p, c_ulong, c_void_p, c_ulong, c_void_p, c_ulong, c_uint, c_void_p, c_ulong, c_ulong_p]), + ('wally_ak_host_commit_from_bytes', c_int, [c_void_p, c_ulong, c_uint, c_void_p, c_ulong]), + ('wally_ak_sig_from_bytes', c_int, [c_void_p, c_ulong, c_void_p, c_ulong, c_void_p, c_ulong, c_uint, c_void_p, c_ulong]), + ('wally_ak_signer_commit_from_bytes', c_int, [c_void_p, c_ulong, c_void_p, c_ulong, c_void_p, c_ulong, c_uint, c_void_p, c_ulong]), + ('wally_ak_verify', c_int, [c_void_p, c_ulong, c_void_p, c_ulong, c_void_p, c_ulong, c_void_p, c_ulong, c_uint, c_void_p, c_ulong]), ('wally_asset_blinding_key_from_seed', c_int, [c_void_p, c_ulong, c_void_p, c_ulong]), ('wally_asset_blinding_key_to_ec_private_key', c_int, [c_void_p, c_ulong, c_void_p, c_ulong, c_void_p, c_ulong]), ('wally_asset_final_vbf', c_int, [POINTER(c_uint64), c_ulong, c_ulong, c_void_p, c_ulong, c_void_p, c_ulong, c_void_p, c_ulong]), @@ -357,6 +361,8 @@ class wally_psbt(Structure): ('wally_psbt_sign', c_int, [POINTER(wally_psbt), c_void_p, c_ulong, c_uint]), ('wally_psbt_to_base64', c_int, [POINTER(wally_psbt), c_uint, c_char_p_p]), ('wally_psbt_to_bytes', c_int, [POINTER(wally_psbt), c_uint, c_void_p, c_ulong, c_ulong_p]), + ('wally_s2c_commitment_verify', c_int, [c_void_p, c_ulong, c_void_p, c_ulong, c_void_p, c_ulong, c_uint]), + ('wally_s2c_sig_from_bytes', c_int, [c_void_p, c_ulong, c_void_p, c_ulong, c_void_p, c_ulong, c_uint, c_void_p, c_ulong, c_void_p, c_ulong]), ('wally_script_push_from_bytes', c_int, [c_void_p, c_ulong, c_uint, c_void_p, c_ulong, c_ulong_p]), ('wally_scriptpubkey_csv_2of2_then_1_from_bytes', c_int, [c_void_p, c_ulong, c_uint, c_uint, c_void_p, c_ulong, c_ulong_p]), ('wally_scriptpubkey_csv_2of2_then_1_from_bytes_opt', c_int, [c_void_p, c_ulong, c_uint, c_uint, c_void_p, c_ulong, c_ulong_p]), diff --git a/src/wrap_js/src/combined.c b/src/wrap_js/src/combined.c index 6a92a22d9..832f9c517 100644 --- a/src/wrap_js/src/combined.c +++ b/src/wrap_js/src/combined.c @@ -2,6 +2,7 @@ #include "internal.c" #include "address.c" #include "aes.c" +#include "anti_klepto.c" #include "base58.c" #include "bech32.c" #include "blech32.c" diff --git a/src/wrap_js/windows_config/libsecp256k1-config.h b/src/wrap_js/windows_config/libsecp256k1-config.h index 2761a5f00..262768be0 100644 --- a/src/wrap_js/windows_config/libsecp256k1-config.h +++ b/src/wrap_js/windows_config/libsecp256k1-config.h @@ -3,6 +3,7 @@ #define ECMULT_GEN_PREC_BITS 4 #define ENABLE_MODULE_ECDH 1 +#define ENABLE_MODULE_ECDSA_S2C 1 #define ENABLE_MODULE_GENERATOR 1 #define ENABLE_MODULE_RANGEPROOF 1 #define ENABLE_MODULE_RECOVERY 1 diff --git a/tools/cleanup.sh b/tools/cleanup.sh index 5c5b6323f..93142d228 100755 --- a/tools/cleanup.sh +++ b/tools/cleanup.sh @@ -58,6 +58,6 @@ rm -f tools/build-aux/missing rm -f tools/build-aux/m4/l*.m4 rm -f tools/build-aux/test-driver rm -rf autom4te.cache/ src/secp256k1/autom4te.cache -rm -rf docs/build docs/source/address.rst docs/source/bip32.rst docs/source/bip38.rst docs/source/bip39.rst docs/source/core.rst docs/source/crypto.rst docs/source/elements.rst docs/source/psbt.rst docs/source/script.rst docs/source/symmetric.rst docs/source/transaction.rst +rm -rf docs/build docs/source/address.rst docs/source/anti_klepto.rst docs/source/bip32.rst docs/source/bip38.rst docs/source/bip39.rst docs/source/core.rst docs/source/crypto.rst docs/source/elements.rst docs/source/psbt.rst docs/source/script.rst docs/source/symmetric.rst docs/source/transaction.rst rm -rf .venv exit 0 diff --git a/tools/msvc/build.bat b/tools/msvc/build.bat index 1ca3bb3cf..62389a2a2 100644 --- a/tools/msvc/build.bat +++ b/tools/msvc/build.bat @@ -20,4 +20,4 @@ if %ELEMENTS_BUILD% == "elements" ( REM Compile everything (wally, ccan, libsecp256k) in one lump. REM Define USE_ECMULT_STATIC_PRECOMPUTATION to pick up the REM ecmult_static_context.h file generated previously -cl /utf-8 /DUSE_ECMULT_STATIC_PRECOMPUTATION /DECMULT_WINDOW_SIZE=16 /DWALLY_CORE_BUILD %ELEMENTS_OPT% /DHAVE_CONFIG_H /DSECP256K1_BUILD /I%LIBWALLY_DIR%\src\wrap_js\windows_config /I%LIBWALLY_DIR% /I%LIBWALLY_DIR%\src /I%LIBWALLY_DIR%\include /I%LIBWALLY_DIR%\src\ccan /I%LIBWALLY_DIR%\src\ccan\base64 /I%LIBWALLY_DIR%\src\secp256k1 /Zi /LD src/aes.c src/base58.c src/bech32.c src/bip32.c src/bip38.c src/bip39.c src/blech32.c src/ecdh.c src/elements.c src/hex.c src/hmac.c src/internal.c src/mnemonic.c src/pbkdf2.c src/psbt.c src/script.c src/scrypt.c src/sign.c src/symmetric.c src/transaction.c src/wif.c src/wordlist.c src/ccan/ccan/crypto/ripemd160/ripemd160.c src/ccan/ccan/crypto/sha256/sha256.c src/ccan/ccan/crypto/sha512/sha512.c src/ccan/ccan/base64/base64.c src\ccan\ccan\str\hex\hex_.c src/secp256k1/src/secp256k1.c /Fewally.dll +cl /utf-8 /DUSE_ECMULT_STATIC_PRECOMPUTATION /DECMULT_WINDOW_SIZE=16 /DWALLY_CORE_BUILD %ELEMENTS_OPT% /DHAVE_CONFIG_H /DSECP256K1_BUILD /I%LIBWALLY_DIR%\src\wrap_js\windows_config /I%LIBWALLY_DIR% /I%LIBWALLY_DIR%\src /I%LIBWALLY_DIR%\include /I%LIBWALLY_DIR%\src\ccan /I%LIBWALLY_DIR%\src\ccan\base64 /I%LIBWALLY_DIR%\src\secp256k1 /Zi /LD src/aes.c src/anti_klepto.c src/base58.c src/bech32.c src/bip32.c src/bip38.c src/bip39.c src/blech32.c src/ecdh.c src/elements.c src/hex.c src/hmac.c src/internal.c src/mnemonic.c src/pbkdf2.c src/psbt.c src/script.c src/scrypt.c src/sign.c src/symmetric.c src/transaction.c src/wif.c src/wordlist.c src/ccan/ccan/crypto/ripemd160/ripemd160.c src/ccan/ccan/crypto/sha256/sha256.c src/ccan/ccan/crypto/sha512/sha512.c src/ccan/ccan/base64/base64.c src\ccan\ccan\str\hex\hex_.c src/secp256k1/src/secp256k1.c /Fewally.dll diff --git a/tools/wasm_exports.sh b/tools/wasm_exports.sh index 680db98da..077a20b2f 100644 --- a/tools/wasm_exports.sh +++ b/tools/wasm_exports.sh @@ -3,7 +3,7 @@ if [ -z "$EXPORTED_FUNCTIONS" ]; then # BEGIN AUTOGENERATED -EXPORTED_FUNCTIONS="['_malloc','_free','_wally_init','_wally_cleanup','_wally_bzero','_wally_free_string','_wally_secp_randomize','_wally_hex_from_bytes','_wally_hex_to_bytes','_wally_base58_from_bytes','_wally_base58_to_bytes','_wally_base58_get_length','_wally_get_operations','_wally_set_operations','_wally_is_elements_build','_wally_scrypt','_wally_aes','_wally_aes_cbc','_wally_sha256','_wally_sha256_midstate','_wally_sha256d','_wally_sha512','_wally_hash160','_wally_hmac_sha256','_wally_hmac_sha512','_wally_pbkdf2_hmac_sha256','_wally_pbkdf2_hmac_sha512','_wally_ec_private_key_verify','_wally_ec_public_key_verify','_wally_ec_public_key_from_private_key','_wally_ec_public_key_decompress','_wally_ec_public_key_negate','_wally_ec_sig_from_bytes','_wally_ec_sig_normalize','_wally_ec_sig_to_der','_wally_ec_sig_from_der','_wally_ec_sig_verify','_wally_ec_sig_to_public_key','_wally_format_bitcoin_message','_wally_ecdh','_wally_addr_segwit_from_bytes','_wally_addr_segwit_to_bytes','_wally_address_to_scriptpubkey','_wally_scriptpubkey_to_address','_wally_wif_from_bytes','_wally_wif_to_bytes','_wally_wif_is_uncompressed','_wally_wif_to_public_key','_wally_bip32_key_to_address','_wally_bip32_key_to_addr_segwit','_wally_wif_to_address','_bip32_key_free','_bip32_key_init','_bip32_key_init_alloc','_bip32_key_from_seed','_bip32_key_from_seed_alloc','_bip32_key_serialize','_bip32_key_unserialize','_bip32_key_unserialize_alloc','_bip32_key_from_parent','_bip32_key_from_parent_alloc','_bip32_key_from_parent_path','_bip32_key_from_parent_path_alloc','_bip32_key_to_base58','_bip32_key_from_base58','_bip32_key_from_base58_alloc','_bip32_key_strip_private_key','_bip32_key_get_fingerprint','_bip38_raw_from_private_key','_bip38_from_private_key','_bip38_raw_to_private_key','_bip38_to_private_key','_bip38_raw_get_flags','_bip38_get_flags','_bip39_get_languages','_bip39_get_wordlist','_bip39_get_word','_bip39_mnemonic_from_bytes','_bip39_mnemonic_to_bytes','_bip39_mnemonic_validate','_bip39_mnemonic_to_seed','_wally_scriptpubkey_get_type','_wally_scriptpubkey_p2pkh_from_bytes','_wally_scriptsig_p2pkh_from_sig','_wally_witness_p2wpkh_from_sig','_wally_scriptsig_p2pkh_from_der','_wally_witness_p2wpkh_from_der','_wally_scriptpubkey_op_return_from_bytes','_wally_scriptpubkey_p2sh_from_bytes','_wally_scriptpubkey_multisig_from_bytes','_wally_scriptsig_multisig_from_bytes','_wally_witness_multisig_from_bytes','_wally_scriptpubkey_csv_2of2_then_1_from_bytes','_wally_scriptpubkey_csv_2of2_then_1_from_bytes_opt','_wally_scriptpubkey_csv_2of3_then_2_from_bytes','_wally_script_push_from_bytes','_wally_varint_get_length','_wally_varint_to_bytes','_wally_varbuff_get_length','_wally_varbuff_to_bytes','_wally_witness_program_from_bytes','_wally_map_init_alloc','_wally_map_free','_wally_map_find','_wally_map_add','_wally_map_add_keypath_item','_wally_map_sort','_wally_psbt_input_is_finalized','_wally_psbt_input_set_utxo','_wally_psbt_input_set_witness_utxo','_wally_psbt_input_set_redeem_script','_wally_psbt_input_set_witness_script','_wally_psbt_input_set_final_scriptsig','_wally_psbt_input_set_final_witness','_wally_psbt_input_set_keypaths','_wally_psbt_input_find_keypath','_wally_psbt_input_add_keypath_item','_wally_psbt_input_set_signatures','_wally_psbt_input_find_signature','_wally_psbt_input_add_signature','_wally_psbt_input_set_unknowns','_wally_psbt_input_find_unknown','_wally_psbt_input_set_sighash','_wally_psbt_output_set_redeem_script','_wally_psbt_output_set_witness_script','_wally_psbt_output_set_keypaths','_wally_psbt_output_find_keypath','_wally_psbt_output_add_keypath_item','_wally_psbt_output_set_unknowns','_wally_psbt_output_find_unknown','_wally_psbt_init_alloc','_wally_psbt_free','_wally_psbt_is_finalized','_wally_psbt_set_global_tx','_wally_psbt_add_input_at','_wally_psbt_remove_input','_wally_psbt_add_output_at','_wally_psbt_remove_output','_wally_psbt_from_bytes','_wally_psbt_get_length','_wally_psbt_to_bytes','_wally_psbt_from_base64','_wally_psbt_to_base64','_wally_psbt_combine','_wally_psbt_clone_alloc','_wally_psbt_sign','_wally_psbt_finalize','_wally_psbt_extract','_wally_psbt_is_elements','_wally_symmetric_key_from_seed','_wally_symmetric_key_from_parent','_wally_tx_witness_stack_init_alloc','_wally_tx_witness_stack_clone_alloc','_wally_tx_witness_stack_add','_wally_tx_witness_stack_add_dummy','_wally_tx_witness_stack_set','_wally_tx_witness_stack_set_dummy','_wally_tx_witness_stack_free','_wally_tx_input_init_alloc','_wally_tx_input_free','_wally_tx_output_init','_wally_tx_output_init_alloc','_wally_tx_output_clone_alloc','_wally_tx_output_clone','_wally_tx_output_free','_wally_tx_init_alloc','_wally_tx_clone_alloc','_wally_tx_add_input','_wally_tx_add_input_at','_wally_tx_add_raw_input','_wally_tx_add_raw_input_at','_wally_tx_remove_input','_wally_tx_set_input_script','_wally_tx_set_input_witness','_wally_tx_add_output','_wally_tx_add_output_at','_wally_tx_add_raw_output','_wally_tx_add_raw_output_at','_wally_tx_remove_output','_wally_tx_get_witness_count','_wally_tx_free','_wally_tx_get_txid','_wally_tx_get_length','_wally_tx_from_bytes','_wally_tx_from_hex','_wally_tx_to_bytes','_wally_tx_to_hex','_wally_tx_get_weight','_wally_tx_get_vsize','_wally_tx_vsize_from_weight','_wally_tx_get_total_output_satoshi','_wally_tx_get_btc_signature_hash','_wally_tx_get_signature_hash','_wally_tx_is_coinbase'" +EXPORTED_FUNCTIONS="['_malloc','_free','_wally_init','_wally_cleanup','_wally_bzero','_wally_free_string','_wally_secp_randomize','_wally_hex_from_bytes','_wally_hex_to_bytes','_wally_base58_from_bytes','_wally_base58_to_bytes','_wally_base58_get_length','_wally_get_operations','_wally_set_operations','_wally_is_elements_build','_wally_scrypt','_wally_aes','_wally_aes_cbc','_wally_sha256','_wally_sha256_midstate','_wally_sha256d','_wally_sha512','_wally_hash160','_wally_hmac_sha256','_wally_hmac_sha512','_wally_pbkdf2_hmac_sha256','_wally_pbkdf2_hmac_sha512','_wally_ec_private_key_verify','_wally_ec_public_key_verify','_wally_ec_public_key_from_private_key','_wally_ec_public_key_decompress','_wally_ec_public_key_negate','_wally_ec_sig_from_bytes','_wally_ec_sig_normalize','_wally_ec_sig_to_der','_wally_ec_sig_from_der','_wally_ec_sig_verify','_wally_ec_sig_to_public_key','_wally_format_bitcoin_message','_wally_ecdh','_wally_s2c_sig_from_bytes','_wally_s2c_commitment_verify','_wally_addr_segwit_from_bytes','_wally_addr_segwit_to_bytes','_wally_address_to_scriptpubkey','_wally_scriptpubkey_to_address','_wally_wif_from_bytes','_wally_wif_to_bytes','_wally_wif_is_uncompressed','_wally_wif_to_public_key','_wally_bip32_key_to_address','_wally_bip32_key_to_addr_segwit','_wally_wif_to_address','_bip32_key_free','_bip32_key_init','_bip32_key_init_alloc','_bip32_key_from_seed','_bip32_key_from_seed_alloc','_bip32_key_serialize','_bip32_key_unserialize','_bip32_key_unserialize_alloc','_bip32_key_from_parent','_bip32_key_from_parent_alloc','_bip32_key_from_parent_path','_bip32_key_from_parent_path_alloc','_bip32_key_to_base58','_bip32_key_from_base58','_bip32_key_from_base58_alloc','_bip32_key_strip_private_key','_bip32_key_get_fingerprint','_bip38_raw_from_private_key','_bip38_from_private_key','_bip38_raw_to_private_key','_bip38_to_private_key','_bip38_raw_get_flags','_bip38_get_flags','_bip39_get_languages','_bip39_get_wordlist','_bip39_get_word','_bip39_mnemonic_from_bytes','_bip39_mnemonic_to_bytes','_bip39_mnemonic_validate','_bip39_mnemonic_to_seed','_wally_scriptpubkey_get_type','_wally_scriptpubkey_p2pkh_from_bytes','_wally_scriptsig_p2pkh_from_sig','_wally_witness_p2wpkh_from_sig','_wally_scriptsig_p2pkh_from_der','_wally_witness_p2wpkh_from_der','_wally_scriptpubkey_op_return_from_bytes','_wally_scriptpubkey_p2sh_from_bytes','_wally_scriptpubkey_multisig_from_bytes','_wally_scriptsig_multisig_from_bytes','_wally_witness_multisig_from_bytes','_wally_scriptpubkey_csv_2of2_then_1_from_bytes','_wally_scriptpubkey_csv_2of2_then_1_from_bytes_opt','_wally_scriptpubkey_csv_2of3_then_2_from_bytes','_wally_script_push_from_bytes','_wally_varint_get_length','_wally_varint_to_bytes','_wally_varbuff_get_length','_wally_varbuff_to_bytes','_wally_witness_program_from_bytes','_wally_map_init_alloc','_wally_map_free','_wally_map_find','_wally_map_add','_wally_map_add_keypath_item','_wally_map_sort','_wally_psbt_input_is_finalized','_wally_psbt_input_set_utxo','_wally_psbt_input_set_witness_utxo','_wally_psbt_input_set_redeem_script','_wally_psbt_input_set_witness_script','_wally_psbt_input_set_final_scriptsig','_wally_psbt_input_set_final_witness','_wally_psbt_input_set_keypaths','_wally_psbt_input_find_keypath','_wally_psbt_input_add_keypath_item','_wally_psbt_input_set_signatures','_wally_psbt_input_find_signature','_wally_psbt_input_add_signature','_wally_psbt_input_set_unknowns','_wally_psbt_input_find_unknown','_wally_psbt_input_set_sighash','_wally_psbt_output_set_redeem_script','_wally_psbt_output_set_witness_script','_wally_psbt_output_set_keypaths','_wally_psbt_output_find_keypath','_wally_psbt_output_add_keypath_item','_wally_psbt_output_set_unknowns','_wally_psbt_output_find_unknown','_wally_psbt_init_alloc','_wally_psbt_free','_wally_psbt_is_finalized','_wally_psbt_set_global_tx','_wally_psbt_add_input_at','_wally_psbt_remove_input','_wally_psbt_add_output_at','_wally_psbt_remove_output','_wally_psbt_from_bytes','_wally_psbt_get_length','_wally_psbt_to_bytes','_wally_psbt_from_base64','_wally_psbt_to_base64','_wally_psbt_combine','_wally_psbt_clone_alloc','_wally_psbt_sign','_wally_psbt_finalize','_wally_psbt_extract','_wally_psbt_is_elements','_wally_symmetric_key_from_seed','_wally_symmetric_key_from_parent','_wally_tx_witness_stack_init_alloc','_wally_tx_witness_stack_clone_alloc','_wally_tx_witness_stack_add','_wally_tx_witness_stack_add_dummy','_wally_tx_witness_stack_set','_wally_tx_witness_stack_set_dummy','_wally_tx_witness_stack_free','_wally_tx_input_init_alloc','_wally_tx_input_free','_wally_tx_output_init','_wally_tx_output_init_alloc','_wally_tx_output_clone_alloc','_wally_tx_output_clone','_wally_tx_output_free','_wally_tx_init_alloc','_wally_tx_clone_alloc','_wally_tx_add_input','_wally_tx_add_input_at','_wally_tx_add_raw_input','_wally_tx_add_raw_input_at','_wally_tx_remove_input','_wally_tx_set_input_script','_wally_tx_set_input_witness','_wally_tx_add_output','_wally_tx_add_output_at','_wally_tx_add_raw_output','_wally_tx_add_raw_output_at','_wally_tx_remove_output','_wally_tx_get_witness_count','_wally_tx_free','_wally_tx_get_txid','_wally_tx_get_length','_wally_tx_from_bytes','_wally_tx_from_hex','_wally_tx_to_bytes','_wally_tx_to_hex','_wally_tx_get_weight','_wally_tx_get_vsize','_wally_tx_vsize_from_weight','_wally_tx_get_total_output_satoshi','_wally_tx_get_btc_signature_hash','_wally_tx_get_signature_hash','_wally_tx_is_coinbase','_wally_ak_host_commit_from_bytes','_wally_ak_signer_commit_from_bytes','_wally_ak_sig_from_bytes','_wally_ak_verify'" if [ -n "$ENABLE_ELEMENTS" ]; then EXPORTED_FUNCTIONS="$EXPORTED_FUNCTIONS"",'_wally_confidential_addr_to_addr','_wally_confidential_addr_to_ec_public_key','_wally_confidential_addr_from_addr','_wally_confidential_addr_to_addr_segwit','_wally_confidential_addr_segwit_to_ec_public_key','_wally_confidential_addr_from_addr_segwit','_bip32_key_with_tweak_from_parent_path','_bip32_key_with_tweak_from_parent_path_alloc','_wally_elements_pegout_script_size','_wally_elements_pegout_script_from_bytes','_wally_elements_pegin_contract_script_from_bytes','_wally_psbt_elements_init_alloc','_wally_psbt_input_set_value','_wally_psbt_input_clear_value','_wally_psbt_input_set_vbf','_wally_psbt_input_set_asset','_wally_psbt_input_set_abf','_wally_psbt_input_set_pegin_tx','_wally_psbt_input_set_txoutproof','_wally_psbt_input_set_genesis_blockhash','_wally_psbt_input_set_claim_script','_wally_psbt_output_set_blinding_pubkey','_wally_psbt_output_set_value_commitment','_wally_psbt_output_set_vbf','_wally_psbt_output_set_asset_commitment','_wally_psbt_output_set_abf','_wally_psbt_output_set_nonce','_wally_psbt_output_set_rangeproof','_wally_psbt_output_set_surjectionproof','_wally_tx_elements_input_issuance_set','_wally_tx_elements_input_issuance_free','_wally_tx_elements_input_init_alloc','_wally_tx_elements_input_is_pegin','_wally_tx_elements_output_commitment_set','_wally_tx_elements_output_commitment_free','_wally_tx_elements_output_init','_wally_tx_elements_output_init_alloc','_wally_tx_add_elements_raw_input','_wally_tx_add_elements_raw_input_at','_wally_tx_add_elements_raw_output','_wally_tx_add_elements_raw_output_at','_wally_tx_is_elements','_wally_tx_confidential_value_from_satoshi','_wally_tx_confidential_value_to_satoshi','_wally_tx_get_elements_signature_hash','_wally_tx_elements_issuance_generate_entropy','_wally_tx_elements_issuance_calculate_asset','_wally_tx_elements_issuance_calculate_reissuance_token','_wally_asset_generator_from_bytes','_wally_asset_final_vbf','_wally_asset_value_commitment','_wally_asset_rangeproof','_wally_asset_surjectionproof_size','_wally_asset_surjectionproof','_wally_asset_unblind_with_nonce','_wally_asset_unblind','_wally_asset_blinding_key_from_seed','_wally_asset_blinding_key_to_ec_private_key','_wally_asset_pak_whitelistproof_size','_wally_asset_pak_whitelistproof'" fi