|
| 1 | +#ifndef SECP256K1_ECDSA_S2C_H |
| 2 | +#define SECP256K1_ECDSA_S2C_H |
| 3 | + |
| 4 | +#include "secp256k1.h" |
| 5 | + |
| 6 | +/** This module implements the sign-to-contract scheme for ECDSA signatures, as |
| 7 | + * well as the "ECDSA Anti-Klepto Protocol" that is based on sign-to-contract |
| 8 | + * and is specified further down. The sign-to-contract scheme allows creating a |
| 9 | + * signature that also commits to some data. This works by offsetting the public |
| 10 | + * nonce point of the signature R by hash(R, data)*G where G is the secp256k1 |
| 11 | + * group generator. |
| 12 | + */ |
| 13 | + |
| 14 | +#ifdef __cplusplus |
| 15 | +extern "C" { |
| 16 | +#endif |
| 17 | + |
| 18 | +/** Data structure that holds a sign-to-contract ("s2c") opening information. |
| 19 | + * Sign-to-contract allows a signer to commit to some data as part of a signature. It |
| 20 | + * can be used as an Out-argument in certain signing functions. |
| 21 | + * |
| 22 | + * The exact representation of data inside is implementation defined and not |
| 23 | + * guaranteed to be portable between different platforms or versions. It is |
| 24 | + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. |
| 25 | + * If you need to convert to a format suitable for storage, transmission, or |
| 26 | + * comparison, use secp256k1_ecdsa_s2c_opening_serialize and secp256k1_ecdsa_s2c_opening_parse. |
| 27 | + */ |
| 28 | +typedef struct { |
| 29 | + unsigned char data[64]; |
| 30 | +} secp256k1_ecdsa_s2c_opening; |
| 31 | + |
| 32 | +/** Parse a sign-to-contract opening. |
| 33 | + * |
| 34 | + * Returns: 1 if the opening could be parsed |
| 35 | + * 0 if the opening could not be parsed |
| 36 | + * Args: ctx: a secp256k1 context object. |
| 37 | + * Out: opening: pointer to an opening object. If 1 is returned, it is set to a |
| 38 | + * parsed version of input. If not, its value is unspecified. |
| 39 | + * In: input33: pointer to 33-byte array with a serialized opening |
| 40 | + * |
| 41 | + */ |
| 42 | +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_parse( |
| 43 | + const secp256k1_context* ctx, |
| 44 | + secp256k1_ecdsa_s2c_opening* opening, |
| 45 | + const unsigned char* input33 |
| 46 | +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); |
| 47 | + |
| 48 | +/** Serialize a sign-to-contract opening into a byte sequence. |
| 49 | + * |
| 50 | + * Returns: 1 if the opening was successfully serialized. |
| 51 | + * 0 if the opening could not be serialized |
| 52 | + * Args: ctx: a secp256k1 context object |
| 53 | + * Out: output33: pointer to a 33-byte array to place the serialized opening in |
| 54 | + * In: opening: a pointer to an initialized `secp256k1_ecdsa_s2c_opening` |
| 55 | + */ |
| 56 | +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_serialize( |
| 57 | + const secp256k1_context* ctx, |
| 58 | + unsigned char* output33, |
| 59 | + const secp256k1_ecdsa_s2c_opening* opening |
| 60 | +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); |
| 61 | + |
| 62 | +/** Same as secp256k1_ecdsa_sign, but s2c_data32 is committed to inside the nonce |
| 63 | + * |
| 64 | + * Returns: 1: signature created |
| 65 | + * 0: the nonce generation function failed, or the private key was invalid. |
| 66 | + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) |
| 67 | + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) |
| 68 | + * s2c_opening: if non-NULL, pointer to an secp256k1_ecdsa_s2c_opening structure to populate |
| 69 | + * In: msg32: the 32-byte message hash being signed (cannot be NULL) |
| 70 | + * seckey: pointer to a 32-byte secret key (cannot be NULL) |
| 71 | + * s2c_data32: pointer to a 32-byte data to commit to in the nonce (cannot be NULL) |
| 72 | + */ |
| 73 | +SECP256K1_API int secp256k1_ecdsa_s2c_sign( |
| 74 | + const secp256k1_context* ctx, |
| 75 | + secp256k1_ecdsa_signature* sig, |
| 76 | + secp256k1_ecdsa_s2c_opening* s2c_opening, |
| 77 | + const unsigned char* msg32, |
| 78 | + const unsigned char* seckey, |
| 79 | + const unsigned char* s2c_data32 |
| 80 | +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); |
| 81 | + |
| 82 | +/** Verify a sign-to-contract commitment. |
| 83 | + * |
| 84 | + * Returns: 1: the signature contains a commitment to data32 (though it does |
| 85 | + * not necessarily need to be a valid siganture!) |
| 86 | + * 0: incorrect opening |
| 87 | + * Args: ctx: a secp256k1 context object, initialized for verification. |
| 88 | + * In: sig: the signature containing the sign-to-contract commitment (cannot be NULL) |
| 89 | + * data32: the 32-byte data that was committed to (cannot be NULL) |
| 90 | + * opening: pointer to the opening created during signing (cannot be NULL) |
| 91 | + */ |
| 92 | +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit( |
| 93 | + const secp256k1_context* ctx, |
| 94 | + const secp256k1_ecdsa_signature *sig, |
| 95 | + const unsigned char *data32, |
| 96 | + const secp256k1_ecdsa_s2c_opening *opening |
| 97 | +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); |
| 98 | + |
| 99 | + |
| 100 | +/** ECDSA Anti-Klepto Protocol |
| 101 | + * |
| 102 | + * The ecdsa_anti_klepto_* functions can be used to prevent a signing device from |
| 103 | + * exfiltrating the secret signing keys through biased signature nonces. The general |
| 104 | + * idea is that a host provides additional randomness to the signing device client |
| 105 | + * and the client commits to the randomness in the nonce using sign-to-contract. |
| 106 | + * |
| 107 | + * The following scheme is described by Stepan Snigirev here: |
| 108 | + * https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-February/017655.html |
| 109 | + * and by Pieter Wuille (as "Scheme 6") here: |
| 110 | + * https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-March/017667.html |
| 111 | + * |
| 112 | + * In order to ensure the host cannot trick the signing device into revealing its |
| 113 | + * keys, or the signing device to bias the nonce despite the host's contributions, |
| 114 | + * the host and client must engage in a commit-reveal protocol as follows: |
| 115 | + * 1. The host draws randomness `rho` and computes a sha256 commitment to it using |
| 116 | + * `secp256k1_ecdsa_anti_klepto_host_commit`. It sends this to the signing device. |
| 117 | + * 2. The signing device computes a public nonce `R` using the host's commitment |
| 118 | + * as auxiliary randomness, using `secp256k1_ecdsa_anti_klepto_signer_commit`. |
| 119 | + * The signing device sends the resulting `R` to the host as a s2c_opening. |
| 120 | + * |
| 121 | + * If, at any point from this step onward, the hardware device fails, it is |
| 122 | + * okay to restart the protocol using **exactly the same `rho`** and checking |
| 123 | + * that the hardware device proposes **exactly the same** `R`. Otherwise, the |
| 124 | + * hardware device may be selectively aborting and thereby biasing the set of |
| 125 | + * nonces that are used in actual signatures. |
| 126 | + * |
| 127 | + * It takes many (>100) such aborts before there is a plausible attack, given |
| 128 | + * current knowledge in 2020. However such aborts accumulate even across a total |
| 129 | + * replacement of all relevant devices (but not across replacement of the actual |
| 130 | + * signing keys with new independently random ones). |
| 131 | + * |
| 132 | + * In case the hardware device cannot be made to sign with the given `rho`, `R` |
| 133 | + * pair, wallet authors should alert the user and present a very scary message |
| 134 | + * implying that if this happens more than even a few times, say 20 or more times |
| 135 | + * EVER, they should change hardware vendors and perhaps sweep their coins. |
| 136 | + * |
| 137 | + * 3. The host replies with `rho` generated in step 1. |
| 138 | + * 4. The device signs with `secp256k1_anti_klepto_sign`, using `rho` as `host_data32`, |
| 139 | + * and sends the signature to the host. |
| 140 | + * 5. The host verifies that the signature's public nonce matches the opening from |
| 141 | + * step 2 and its original randomness `rho`, using `secp256k1_anti_klepto_host_verify`. |
| 142 | + * |
| 143 | + * Rationale: |
| 144 | + * - The reason for having a host commitment is to allow the signing device to |
| 145 | + * deterministically derive a unique nonce even if the host restarts the protocol |
| 146 | + * using the same message and keys. Otherwise the signer might reuse the original |
| 147 | + * nonce in two iterations of the protocol with different `rho`, which leaks the |
| 148 | + * the secret key. |
| 149 | + * - The signer does not need to check that the host commitment matches the host's |
| 150 | + * claimed `rho`. Instead it re-derives the commitment (and its original `R`) from |
| 151 | + * the provided `rho`. If this differs from the original commitment, the result |
| 152 | + * will be an invalid `s2c_opening`, but since `R` was unique there is no risk to |
| 153 | + * the signer's secret keys. Because of this, the signing device does not need to |
| 154 | + * maintain any state about the progress of the protocol. |
| 155 | + */ |
| 156 | + |
| 157 | +/** Create the initial host commitment to `rho`. Part of the ECDSA Anti-Klepto Protocol. |
| 158 | + * |
| 159 | + * Returns 1 on success, 0 on failure. |
| 160 | + * Args: ctx: pointer to a context object (cannot be NULL) |
| 161 | + * Out: rand_commitment32: pointer to 32-byte array to store the returned commitment (cannot be NULL) |
| 162 | + * In: rand32: the 32-byte randomness to commit to (cannot be NULL). It must come from |
| 163 | + * a cryptographically secure RNG. As per the protocol, this value must not |
| 164 | + * be revealed to the client until after the host has received the client |
| 165 | + * commitment. |
| 166 | + */ |
| 167 | +SECP256K1_API int secp256k1_ecdsa_anti_klepto_host_commit( |
| 168 | + const secp256k1_context* ctx, |
| 169 | + unsigned char* rand_commitment32, |
| 170 | + const unsigned char* rand32 |
| 171 | +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); |
| 172 | + |
| 173 | +/** Compute signer's original nonce. Part of the ECDSA Anti-Klepto Protocol. |
| 174 | + * |
| 175 | + * Returns 1 on success, 0 on failure. |
| 176 | + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) |
| 177 | + * Out: s2c_opening: pointer to an s2c_opening where the signer's public nonce will be |
| 178 | + * placed. (cannot be NULL) |
| 179 | + * In: msg32: the 32-byte message hash to be signed (cannot be NULL) |
| 180 | + * seckey32: the 32-byte secret key used for signing (cannot be NULL) |
| 181 | + * rand_commitment32: the 32-byte randomness commitment from the host (cannot be NULL) |
| 182 | + */ |
| 183 | +SECP256K1_API int secp256k1_ecdsa_anti_klepto_signer_commit( |
| 184 | + const secp256k1_context* ctx, |
| 185 | + secp256k1_ecdsa_s2c_opening* s2c_opening, |
| 186 | + const unsigned char* msg32, |
| 187 | + const unsigned char* seckey32, |
| 188 | + const unsigned char* rand_commitment32 |
| 189 | +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); |
| 190 | + |
| 191 | +/** Same as secp256k1_ecdsa_sign, but commits to host randomness in the nonce. Part of the |
| 192 | + * ECDSA Anti-Klepto Protocol. |
| 193 | + * |
| 194 | + * Returns: 1: signature created |
| 195 | + * 0: the nonce generation function failed, or the private key was invalid. |
| 196 | + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) |
| 197 | + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) |
| 198 | + * In: msg32: the 32-byte message hash being signed (cannot be NULL) |
| 199 | + * seckey: pointer to a 32-byte secret key (cannot be NULL) |
| 200 | + * host_data32: pointer to 32-byte host-provided randomness (cannot be NULL) |
| 201 | + */ |
| 202 | +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_sign( |
| 203 | + const secp256k1_context* ctx, |
| 204 | + secp256k1_ecdsa_signature* sig, |
| 205 | + const unsigned char* msg32, |
| 206 | + const unsigned char* seckey, |
| 207 | + const unsigned char* host_data32 |
| 208 | +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); |
| 209 | + |
| 210 | +/** Verify a signature was correctly constructed using the ECDSA Anti-Klepto Protocol. |
| 211 | + * |
| 212 | + * Returns: 1: the signature is valid and contains a commitment to host_data32 |
| 213 | + * 0: incorrect opening |
| 214 | + * Args: ctx: a secp256k1 context object, initialized for verification. |
| 215 | + * In: sig: the signature produced by the signer (cannot be NULL) |
| 216 | + * msghash32: the 32-byte message hash being verified (cannot be NULL) |
| 217 | + * pubkey: pointer to the signer's public key (cannot be NULL) |
| 218 | + * host_data32: the 32-byte data provided by the host (cannot be NULL) |
| 219 | + * opening: the s2c opening provided by the signer (cannot be NULL) |
| 220 | + */ |
| 221 | +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_host_verify( |
| 222 | + const secp256k1_context* ctx, |
| 223 | + const secp256k1_ecdsa_signature *sig, |
| 224 | + const unsigned char *msg32, |
| 225 | + const secp256k1_pubkey *pubkey, |
| 226 | + const unsigned char *host_data32, |
| 227 | + const secp256k1_ecdsa_s2c_opening *opening |
| 228 | +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); |
| 229 | + |
| 230 | +#ifdef __cplusplus |
| 231 | +} |
| 232 | +#endif |
| 233 | + |
| 234 | +#endif /* SECP256K1_ECDSA_S2C_H */ |
0 commit comments