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:
Andrew Poelstra
2020-12-06 16:31:42 +00:00
parent 290dee566e
commit 396b558273
3 changed files with 355 additions and 0 deletions

View File

@@ -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 */

View File

@@ -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 */