From 396b558273ce88969d4b0abc86e003f7557224f7 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 6 Dec 2020 16:31:42 +0000 Subject: [PATCH] ecdsa-s2c: add anti-klepto protocol Co-authored-by: Marko Bencun Co-authored-by: Jonas Nick --- include/secp256k1_ecdsa_s2c.h | 139 +++++++++++++++++++++++++ src/modules/ecdsa_s2c/main_impl.h | 58 +++++++++++ src/modules/ecdsa_s2c/tests_impl.h | 158 +++++++++++++++++++++++++++++ 3 files changed, 355 insertions(+) diff --git a/include/secp256k1_ecdsa_s2c.h b/include/secp256k1_ecdsa_s2c.h index b28003f0..482b0c13 100644 --- a/include/secp256k1_ecdsa_s2c.h +++ b/include/secp256k1_ecdsa_s2c.h @@ -3,6 +3,14 @@ #include "secp256k1.h" +/** This module implements the sign-to-contract scheme for ECDSA signatures, as + * well as the "ECDSA Anti-Klepto Protocol" that is based on sign-to-contract + * and is specified further down. The sign-to-contract scheme allows creating a + * signature that also commits to some data. This works by offsetting the public + * nonce point of the signature R by hash(R, data)*G where G is the secp256k1 + * group generator. + */ + #ifdef __cplusplus extern "C" { #endif @@ -88,6 +96,137 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit const secp256k1_ecdsa_s2c_opening *opening ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** ECDSA Anti-Klepto Protocol + * + * The ecdsa_anti_klepto_* functions can be used to prevent a signing device from + * exfiltrating the secret signing keys through biased signature nonces. The general + * idea is that a host provides additional randomness to the signing device client + * and the client commits to the randomness in the nonce using sign-to-contract. + * + * The following scheme is described by Stepan Snigirev here: + * https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-February/017655.html + * and by Pieter Wuille (as "Scheme 6") here: + * https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-March/017667.html + * + * In order to ensure the host cannot trick the signing device into revealing its + * keys, or the signing device to bias the nonce despite the host's contributions, + * the host and client must engage in a commit-reveal protocol as follows: + * 1. The host draws randomness `rho` and computes a sha256 commitment to it using + * `secp256k1_ecdsa_anti_klepto_host_commit`. It sends this to the signing device. + * 2. The signing device computes a public nonce `R` using the host's commitment + * as auxiliary randomness, using `secp256k1_ecdsa_anti_klepto_signer_commit`. + * The signing device sends the resulting `R` to the host as a s2c_opening. + * + * 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. + * + * 3. The host replies with `rho` generated in step 1. + * 4. The device signs with `secp256k1_anti_klepto_sign`, using `rho` as `host_data32`, + * and sends the signature to the host. + * 5. The host verifies that the signature's public nonce matches the opening from + * step 2 and its original randomness `rho`, using `secp256k1_anti_klepto_host_verify`. + * + * Rationale: + * - The reason for having a host commitment is to allow the signing device to + * deterministically derive a unique nonce even if the host restarts the protocol + * using the same message and keys. Otherwise the signer might reuse the original + * nonce in two iterations of the protocol with different `rho`, which leaks the + * the secret key. + * - The signer does not need to check that the host commitment matches the host's + * claimed `rho`. Instead it re-derives the commitment (and its original `R`) from + * the provided `rho`. If this differs from the original commitment, the result + * will be an invalid `s2c_opening`, but since `R` was unique there is no risk to + * the signer's secret keys. Because of this, the signing device does not need to + * maintain any state about the progress of the protocol. + */ + +/** Create the initial host commitment to `rho`. Part of the ECDSA Anti-Klepto Protocol. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: rand_commitment32: pointer to 32-byte array to store the returned commitment (cannot be NULL) + * In: rand32: the 32-byte randomness to commit to (cannot be NULL). 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. + */ +SECP256K1_API int secp256k1_ecdsa_anti_klepto_host_commit( + const secp256k1_context* ctx, + unsigned char* rand_commitment32, + const unsigned char* rand32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Compute signer's original nonce. Part of the ECDSA Anti-Klepto Protocol. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: s2c_opening: pointer to an s2c_opening where the signer's public nonce will be + * placed. (cannot be NULL) + * In: msg32: the 32-byte message hash to be signed (cannot be NULL) + * seckey32: the 32-byte secret key used for signing (cannot be NULL) + * rand_commitment32: the 32-byte randomness commitment from the host (cannot be NULL) + */ +SECP256K1_API int secp256k1_ecdsa_anti_klepto_signer_commit( + const secp256k1_context* ctx, + secp256k1_ecdsa_s2c_opening* s2c_opening, + const unsigned char* msg32, + const unsigned char* seckey32, + const unsigned char* rand_commitment32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Same as secp256k1_ecdsa_sign, but commits to host randomness in the nonce. Part of the + * ECDSA Anti-Klepto Protocol. + * + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * host_data32: pointer to 32-byte host-provided randomness (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_sign( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const unsigned char* msg32, + const unsigned char* seckey, + const unsigned char* host_data32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Verify a signature was correctly constructed using the ECDSA Anti-Klepto Protocol. + * + * Returns: 1: the signature is valid and contains a commitment to host_data32 + * 0: incorrect opening + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig: the signature produced by the signer (cannot be NULL) + * msghash32: the 32-byte message hash being verified (cannot be NULL) + * pubkey: pointer to the signer's public key (cannot be NULL) + * host_data32: the 32-byte data provided by the host (cannot be NULL) + * opening: the s2c opening provided by the signer (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_host_verify( + const secp256k1_context* ctx, + const secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + const secp256k1_pubkey *pubkey, + const unsigned char *host_data32, + const secp256k1_ecdsa_s2c_opening *opening +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + #ifdef __cplusplus } #endif diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h index 7246ddc7..093bab85 100755 --- a/src/modules/ecdsa_s2c/main_impl.h +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -137,4 +137,62 @@ int secp256k1_ecdsa_s2c_verify_commit(const secp256k1_context* ctx, const secp25 return secp256k1_scalar_eq(&sigr, &x_scalar); } +/*** anti-klepto ***/ +int secp256k1_ecdsa_anti_klepto_host_commit(const secp256k1_context* ctx, unsigned char* rand_commitment32, const unsigned char* rand32) { + secp256k1_sha256 sha; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(rand_commitment32 != NULL); + ARG_CHECK(rand32 != NULL); + + secp256k1_s2c_ecdsa_data_sha256_tagged(&sha); + secp256k1_sha256_write(&sha, rand32, 32); + secp256k1_sha256_finalize(&sha, rand_commitment32); + return 1; +} + +int secp256k1_ecdsa_anti_klepto_signer_commit(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* msg32, const unsigned char* seckey32, const unsigned char* rand_commitment32) { + unsigned char nonce32[32]; + secp256k1_scalar k; + secp256k1_gej rj; + secp256k1_ge r; + unsigned int count = 0; + int is_nonce_valid = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(opening != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(seckey32 != NULL); + ARG_CHECK(rand_commitment32 != NULL); + + memset(nonce32, 0, 32); + while (!is_nonce_valid) { + /* cast to void* removes const qualifier, but secp256k1_nonce_function_default does not modify it */ + if (!secp256k1_nonce_function_default(nonce32, msg32, seckey32, NULL, (void*)rand_commitment32, count)) { + secp256k1_callback_call(&ctx->error_callback, "(cryptographically unreachable) generated bad nonce"); + } + is_nonce_valid = secp256k1_scalar_set_b32_seckey(&k, nonce32); + /* The nonce is still secret here, but it being invalid is is less likely than 1:2^255. */ + secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid)); + count++; + } + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); + secp256k1_ge_set_gej(&r, &rj); + secp256k1_ecdsa_s2c_opening_save(opening, &r); + memset(nonce32, 0, 32); + secp256k1_scalar_clear(&k); + return 1; +} + +int secp256k1_anti_klepto_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char* msg32, const unsigned char* seckey, const unsigned char* host_data32) { + return secp256k1_ecdsa_s2c_sign(ctx, sig, NULL, msg32, seckey, host_data32); +} + +int secp256k1_anti_klepto_host_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey, const unsigned char *host_data32, const secp256k1_ecdsa_s2c_opening *opening) { + return secp256k1_ecdsa_s2c_verify_commit(ctx, sig, host_data32, opening) && + secp256k1_ecdsa_verify(ctx, sig, msg32, pubkey); +} + #endif /* SECP256K1_ECDSA_S2C_MAIN_H */ diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h index 6e8dae8f..c76e6b11 100644 --- a/src/modules/ecdsa_s2c/tests_impl.h +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -99,6 +99,8 @@ static void test_ecdsa_s2c_api(void) { const unsigned char msg[32] = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"; const unsigned char sec[32] = "ssssssssssssssssssssssssssssssss"; const unsigned char s2c_data[32] = "dddddddddddddddddddddddddddddddd"; + const unsigned char hostrand[32] = "hrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhr"; + unsigned char hostrand_commitment[32]; secp256k1_pubkey pk; int32_t ecount; @@ -148,6 +150,65 @@ static void test_ecdsa_s2c_api(void) { CHECK(secp256k1_ecdsa_s2c_sign(sign, &sig, NULL, msg, sec, s2c_data) == 1); CHECK(secp256k1_ecdsa_s2c_verify_commit(vrfy, &sig, s2c_data, &s2c_opening) == 1); + /* anti-klepto */ + ecount = 0; + CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, NULL, hostrand) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, hostrand_commitment, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, hostrand_commitment, hostrand) == 1); + CHECK(ecount == 2); + + ecount = 0; + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, NULL, msg, sec, hostrand_commitment) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, NULL, sec, hostrand_commitment) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, msg, NULL, hostrand_commitment) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, msg, sec, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(none, &s2c_opening, msg, sec, hostrand_commitment) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(vrfy, &s2c_opening, msg, sec, hostrand_commitment) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(sign, &s2c_opening, msg, sec, hostrand_commitment) == 1); + CHECK(ecount == 6); + + ecount = 0; + CHECK(secp256k1_anti_klepto_sign(both, NULL, msg, sec, hostrand) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_anti_klepto_sign(both, &sig, NULL, sec, hostrand) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, NULL, hostrand) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, sec, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_anti_klepto_sign(none, &sig, msg, sec, hostrand) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_anti_klepto_sign(vrfy, &sig, msg, sec, hostrand) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, sec, hostrand) == 1); + CHECK(ecount == 6); + + ecount = 0; + CHECK(secp256k1_anti_klepto_host_verify(both, NULL, msg, &pk, hostrand, &s2c_opening) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_anti_klepto_host_verify(both, &sig, NULL, &pk, hostrand, &s2c_opening) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, NULL, hostrand, &s2c_opening) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, &pk, NULL, &s2c_opening) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, &pk, hostrand, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_anti_klepto_host_verify(none, &sig, msg, &pk, hostrand, &s2c_opening) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_anti_klepto_host_verify(sign, &sig, msg, &pk, hostrand, &s2c_opening) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_anti_klepto_host_verify(vrfy, &sig, msg, &pk, hostrand, &s2c_opening) == 1); + CHECK(ecount == 7); + secp256k1_context_destroy(both); secp256k1_context_destroy(vrfy); secp256k1_context_destroy(sign); @@ -156,18 +217,24 @@ static void test_ecdsa_s2c_api(void) { /* When using sign-to-contract commitments, the nonce function is fixed, so we can use fixtures to test. */ typedef struct { + /* Data to commit to */ unsigned char s2c_data[32]; + /* Original nonce */ unsigned char expected_s2c_opening[33]; + /* Original nonce (anti-klepto protocol, which mixes in host randomness) */ + unsigned char expected_s2c_klepto_opening[33]; } ecdsa_s2c_test; static ecdsa_s2c_test ecdsa_s2c_tests[] = { { "\x1b\xf6\xfb\x42\xf4\x1e\xb8\x76\xc4\xd7\xaa\x0d\x67\x24\x2b\x00\xba\xab\x99\xdc\x20\x84\x49\x3e\x4e\x63\x27\x7f\xa1\xf7\x7f\x22", "\x03\xf0\x30\xde\xf3\x18\x8c\x0f\x56\xfc\xea\x87\x43\x5b\x30\x76\x43\xf4\x5d\xaf\xe2\x2c\xbc\x82\xfd\x56\x03\x4f\xae\x97\x41\x7d\x3a", + "\x02\xdf\x63\x75\x5d\x1f\x32\x92\xbf\xfe\xd8\x29\x86\xb1\x06\x49\x7c\x93\xb1\xf8\xbd\xc0\x45\x4b\x6b\x0b\x0a\x47\x79\xc0\xef\x71\x88", }, { "\x35\x19\x9a\x8f\xbf\x84\xad\x6e\xf6\x9a\x18\x4c\x1b\x19\x28\x5b\xef\xbe\x06\xe6\x0b\x62\x64\xe6\xd3\x73\x89\x3f\x68\x55\xe2\x4a", "\x03\x90\x17\x17\xce\x7c\x74\x84\xa2\xce\x1b\x7d\xc7\x40\x3b\x14\xe0\x35\x49\x71\x39\x3e\xc0\x92\xa7\xf3\xe0\xc8\xe4\xe2\xd2\x63\x9d", + "\x02\xc0\x4a\xc7\xf7\x71\xe8\xeb\xdb\xf3\x15\xff\x5e\x58\xb7\xfe\x95\x16\x10\x21\x03\x50\x00\x66\x17\x2c\x4f\xac\x5b\x20\xf9\xe0\xea", }, }; @@ -190,6 +257,7 @@ static void test_ecdsa_s2c_fixed_vectors(void) { CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, message, privkey, test->s2c_data) == 1); CHECK(secp256k1_ecdsa_s2c_opening_serialize(ctx, opening_ser, &s2c_opening) == 1); CHECK(memcmp(test->expected_s2c_opening, opening_ser, sizeof(opening_ser)) == 0); + CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, test->s2c_data, &s2c_opening) == 1); } } @@ -247,12 +315,102 @@ static void test_ecdsa_s2c_sign_verify(void) { } } +static void test_ecdsa_anti_klepto_signer_commit(void) { + size_t i; + unsigned char privkey[32] = { + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + }; + unsigned char message[32] = { + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + }; + /* Check that original pubnonce is derived from s2c_data */ + for (i = 0; i < sizeof(ecdsa_s2c_tests) / sizeof(ecdsa_s2c_tests[0]); i++) { + secp256k1_ecdsa_s2c_opening s2c_opening; + unsigned char buf[33]; + const ecdsa_s2c_test *test = &ecdsa_s2c_tests[i]; + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &s2c_opening, message, privkey, test->s2c_data) == 1); + CHECK(secp256k1_ecdsa_s2c_opening_serialize(ctx, buf, &s2c_opening) == 1); + CHECK(memcmp(test->expected_s2c_klepto_opening, buf, sizeof(buf)) == 0); + } +} + +/* This tests the full ECDSA Anti-Klepto Protocol */ +static void test_ecdsa_anti_klepto(void) { + unsigned char signer_privkey[32]; + unsigned char host_msg[32]; + unsigned char host_commitment[32]; + unsigned char host_nonce_contribution[32]; + secp256k1_pubkey signer_pubkey; + secp256k1_ecdsa_signature signature; + secp256k1_ecdsa_s2c_opening s2c_opening; + + /* Generate a random key, message. */ + { + secp256k1_scalar key; + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(signer_privkey, &key); + CHECK(secp256k1_ec_pubkey_create(ctx, &signer_pubkey, signer_privkey) == 1); + secp256k1_testrand256_test(host_msg); + secp256k1_testrand256_test(host_nonce_contribution); + } + + /* Protocol step 1. */ + CHECK(secp256k1_ecdsa_anti_klepto_host_commit(ctx, host_commitment, host_nonce_contribution) == 1); + /* Protocol step 2. */ + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &s2c_opening, host_msg, signer_privkey, host_commitment) == 1); + /* Protocol step 3: host_nonce_contribution send to signer to be used in step 4. */ + /* Protocol step 4. */ + CHECK(secp256k1_anti_klepto_sign(ctx, &signature, host_msg, signer_privkey, host_nonce_contribution) == 1); + /* Protocol step 5. */ + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1); + /* Protocol step 5 (explicitly) */ + CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, host_nonce_contribution, &s2c_opening) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature, host_msg, &signer_pubkey) == 1); + + { /* host_verify: commitment does not match */ + unsigned char sigbytes[64]; + size_t i; + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, sigbytes, &signature) == 1); + for(i = 0; i < 32; i++) { + /* change one byte */ + sigbytes[i] += 1; + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &signature, sigbytes) == 1); + CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, host_nonce_contribution, &s2c_opening) == 0); + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + /* revert */ + sigbytes[i] -= 1; + } + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &signature, sigbytes) == 1); + } + { /* host_verify: message does not match */ + unsigned char bad_msg[32]; + secp256k1_testrand256_test(bad_msg); + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1); + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, bad_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + } + { /* s2c_sign: host provided data that didn't match commitment */ + secp256k1_ecdsa_s2c_opening orig_opening = s2c_opening; + unsigned char bad_nonce_contribution[32] = { 1, 2, 3, 4 }; + CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, host_msg, signer_privkey, bad_nonce_contribution) == 1); + /* good signature but the opening (original public nonce does not match the original */ + CHECK(secp256k1_ecdsa_verify(ctx, &signature, host_msg, &signer_pubkey) == 1); + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, bad_nonce_contribution, &s2c_opening) == 1); + CHECK(memcmp(&s2c_opening, &orig_opening, sizeof(s2c_opening)) != 0); + } +} + static void run_ecdsa_s2c_tests(void) { run_s2c_opening_test(); test_ecdsa_s2c_tagged_hash(); test_ecdsa_s2c_api(); test_ecdsa_s2c_fixed_vectors(); test_ecdsa_s2c_sign_verify(); + + test_ecdsa_anti_klepto_signer_commit(); + test_ecdsa_anti_klepto(); } #endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */