Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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])


Expand Down
76 changes: 76 additions & 0 deletions docs/source/anti_klepto_protocol.rst
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/include/secp256k1_ecdsa_s2c.h#L100-L155>`_.

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
4 changes: 2 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand Down
2 changes: 2 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ libwally-core documentation
symmetric
transaction
elements
anti_klepto

.. toctree::
:maxdepth: 2
:caption: Developer Guides

Library Conventions <conventions.rst>
Liquid <Liquid.rst>
Anti Klepto protocol <anti_klepto_protocol.rst>

Indices and tables
==================
Expand Down
37 changes: 37 additions & 0 deletions include/wally.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <type_traits>
#include <string>
#include <wally_address.h>
#include <wally_anti_klepto.h>
#include <wally_bip32.h>
#include <wally_bip38.h>
#include <wally_bip39.h>
Expand Down Expand Up @@ -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<size_t>(bytes_out.size()) ? WALLY_OK : WALLY_EINVAL;
}

template <class ENTROPY, class BYTES_OUT>
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 <class PRIV_KEY, class BYTES, class ENTROPY, class BYTES_OUT>
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 <class PRIV_KEY, class BYTES, class COMMITMENT, class S2C_OPENING_OUT>
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 <class PUB_KEY, class BYTES, class ENTROPY, class S2C_OPENING, class SIG>
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 <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 @@ -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<size_t>(bytes_out.size()) ? WALLY_OK : WALLY_EINVAL;
}

template <class SIG, class S2C_DATA, class S2C_OPENING>
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 <class PRIV_KEY, class BYTES, class S2C_DATA, class S2C_OPENING_OUT, class BYTES_OUT>
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 <class BYTES, class BYTES_OUT>
inline int script_push_from_bytes(const BYTES& bytes, uint32_t flags, BYTES_OUT& bytes_out, size_t* written = 0) {
size_t n;
Expand Down
124 changes: 124 additions & 0 deletions include/wally_anti_klepto.h
Original file line number Diff line number Diff line change
@@ -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 */
60 changes: 60 additions & 0 deletions include/wally_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading