ecdsa-s2c: add anti-klepto protocol
Co-authored-by: Marko Bencun <mbencun+pgp@gmail.com> Co-authored-by: Jonas Nick <jonasd.nick@gmail.com>
This commit is contained in:
parent
290dee566e
commit
396b558273
@ -3,6 +3,14 @@
|
|||||||
|
|
||||||
#include "secp256k1.h"
|
#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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@ -88,6 +96,137 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit
|
|||||||
const secp256k1_ecdsa_s2c_opening *opening
|
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(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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -137,4 +137,62 @@ int secp256k1_ecdsa_s2c_verify_commit(const secp256k1_context* ctx, const secp25
|
|||||||
return secp256k1_scalar_eq(&sigr, &x_scalar);
|
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 */
|
#endif /* SECP256K1_ECDSA_S2C_MAIN_H */
|
||||||
|
@ -99,6 +99,8 @@ static void test_ecdsa_s2c_api(void) {
|
|||||||
const unsigned char msg[32] = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm";
|
const unsigned char msg[32] = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm";
|
||||||
const unsigned char sec[32] = "ssssssssssssssssssssssssssssssss";
|
const unsigned char sec[32] = "ssssssssssssssssssssssssssssssss";
|
||||||
const unsigned char s2c_data[32] = "dddddddddddddddddddddddddddddddd";
|
const unsigned char s2c_data[32] = "dddddddddddddddddddddddddddddddd";
|
||||||
|
const unsigned char hostrand[32] = "hrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhr";
|
||||||
|
unsigned char hostrand_commitment[32];
|
||||||
secp256k1_pubkey pk;
|
secp256k1_pubkey pk;
|
||||||
|
|
||||||
int32_t ecount;
|
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_sign(sign, &sig, NULL, msg, sec, s2c_data) == 1);
|
||||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(vrfy, &sig, s2c_data, &s2c_opening) == 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(both);
|
||||||
secp256k1_context_destroy(vrfy);
|
secp256k1_context_destroy(vrfy);
|
||||||
secp256k1_context_destroy(sign);
|
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. */
|
/* When using sign-to-contract commitments, the nonce function is fixed, so we can use fixtures to test. */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
/* Data to commit to */
|
||||||
unsigned char s2c_data[32];
|
unsigned char s2c_data[32];
|
||||||
|
/* Original nonce */
|
||||||
unsigned char expected_s2c_opening[33];
|
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;
|
} ecdsa_s2c_test;
|
||||||
|
|
||||||
static ecdsa_s2c_test ecdsa_s2c_tests[] = {
|
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",
|
"\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",
|
"\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",
|
"\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",
|
"\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_sign(ctx, &signature, &s2c_opening, message, privkey, test->s2c_data) == 1);
|
||||||
CHECK(secp256k1_ecdsa_s2c_opening_serialize(ctx, opening_ser, &s2c_opening) == 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(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) {
|
static void run_ecdsa_s2c_tests(void) {
|
||||||
run_s2c_opening_test();
|
run_s2c_opening_test();
|
||||||
test_ecdsa_s2c_tagged_hash();
|
test_ecdsa_s2c_tagged_hash();
|
||||||
test_ecdsa_s2c_api();
|
test_ecdsa_s2c_api();
|
||||||
test_ecdsa_s2c_fixed_vectors();
|
test_ecdsa_s2c_fixed_vectors();
|
||||||
test_ecdsa_s2c_sign_verify();
|
test_ecdsa_s2c_sign_verify();
|
||||||
|
|
||||||
|
test_ecdsa_anti_klepto_signer_commit();
|
||||||
|
test_ecdsa_anti_klepto();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */
|
#endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user