ecdsa_adaptor: add support for proof of discrete logarithm equality

This commit adds proving and verification functions for discrete
logarithm equality.

From the spec (https://github.com/discreetlogcontracts/dlcspecs/pull/114):

"As part of the ECDSA adaptor signature a proof of discrete logarithm
equality must be provided. This is a proof that the discrete logarithm of
some X to the standard base G is the same as the discrete logarithm of
some Z to the base Y. This proof can be constructed by using equality
composition on two Sigma protocols proving knowledge of the discrete
logarithm between both pairs of points. In other words the prover proves
knowledge of a such that X = a * G and b such that Z = b * Y and that
a = b. We make the resulting Sigma protocol non-interactive by applying
the Fiat-Shamir transformation with SHA256 as the challenge hash."
This commit is contained in:
Jesse Posner 2021-03-05 00:04:03 -08:00
parent d8f336564f
commit b508e5dd9b
3 changed files with 163 additions and 0 deletions

View File

@ -1,2 +1,3 @@
include_HEADERS += include/secp256k1_ecdsa_adaptor.h
noinst_HEADERS += src/modules/ecdsa_adaptor/main_impl.h
noinst_HEADERS += src/modules/ecdsa_adaptor/dleq_impl.h

View File

@ -0,0 +1,158 @@
#ifndef SECP256K1_DLEQ_IMPL_H
#define SECP256K1_DLEQ_IMPL_H
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
* SHA256 to SHA256("DLEQ")||SHA256("DLEQ"). */
static void secp256k1_nonce_function_dleq_sha256_tagged(secp256k1_sha256 *sha) {
secp256k1_sha256_initialize(sha);
sha->s[0] = 0x8cc4beacul;
sha->s[1] = 0x2e011f3ful;
sha->s[2] = 0x355c75fbul;
sha->s[3] = 0x3ba6a2c5ul;
sha->s[4] = 0xe96f3aeful;
sha->s[5] = 0x180530fdul;
sha->s[6] = 0x94582499ul;
sha->s[7] = 0x577fd564ul;
sha->bytes = 64;
}
/* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */
static const unsigned char dleq_algo[4] = "DLEQ";
static int secp256k1_dleq_hash_point(secp256k1_sha256 *sha, secp256k1_ge *p) {
unsigned char buf[33];
size_t size = 33;
if (!secp256k1_eckey_pubkey_serialize(p, buf, &size, 1)) {
return 0;
}
secp256k1_sha256_write(sha, buf, size);
return 1;
}
static int secp256k1_dleq_nonce(secp256k1_scalar *k, const unsigned char *sk32, const unsigned char *gen2_33, const unsigned char *p1_33, const unsigned char *p2_33, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) {
secp256k1_sha256 sha;
unsigned char buf[32];
unsigned char nonce[32];
size_t size = 33;
if (noncefp == NULL) {
noncefp = secp256k1_nonce_function_ecdsa_adaptor;
}
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, p1_33, size);
secp256k1_sha256_write(&sha, p2_33, size);
secp256k1_sha256_finalize(&sha, buf);
if (!noncefp(nonce, buf, sk32, gen2_33, dleq_algo, sizeof(dleq_algo), ndata)) {
return 0;
}
secp256k1_scalar_set_b32(k, nonce, NULL);
if (secp256k1_scalar_is_zero(k)) {
return 0;
}
return 1;
}
/* Generates a challenge as defined in the DLC Specification at
* https://github.com/discreetlogcontracts/dlcspecs */
static void secp256k1_dleq_challenge(secp256k1_scalar *e, secp256k1_ge *gen2, secp256k1_ge *r1, secp256k1_ge *r2, secp256k1_ge *p1, secp256k1_ge *p2) {
unsigned char buf[32];
secp256k1_sha256 sha;
secp256k1_nonce_function_dleq_sha256_tagged(&sha);
secp256k1_dleq_hash_point(&sha, p1);
secp256k1_dleq_hash_point(&sha, gen2);
secp256k1_dleq_hash_point(&sha, p2);
secp256k1_dleq_hash_point(&sha, r1);
secp256k1_dleq_hash_point(&sha, r2);
secp256k1_sha256_finalize(&sha, buf);
secp256k1_scalar_set_b32(e, buf, NULL);
}
/* P1 = x*G, P2 = x*Y */
static void secp256k1_dleq_pair(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_ge *p1, secp256k1_ge *p2, const secp256k1_scalar *sk, const secp256k1_ge *gen2) {
secp256k1_gej p1j, p2j;
secp256k1_ecmult_gen(ecmult_gen_ctx, &p1j, sk);
secp256k1_ge_set_gej(p1, &p1j);
secp256k1_ecmult_const(&p2j, gen2, sk, 256);
secp256k1_ge_set_gej(p2, &p2j);
}
/* Generates a proof that the discrete logarithm of P1 to the secp256k1 base G is the
* same as the discrete logarithm of P2 to the base Y */
static int secp256k1_dleq_prove(const secp256k1_context* ctx, secp256k1_scalar *s, secp256k1_scalar *e, const secp256k1_scalar *sk, secp256k1_ge *gen2, secp256k1_ge *p1, secp256k1_ge *p2, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) {
secp256k1_ge r1, r2;
secp256k1_scalar k = { 0 };
unsigned char sk32[32];
unsigned char gen2_33[33];
unsigned char p1_33[33];
unsigned char p2_33[33];
int ret = 1;
size_t pubkey_size = 33;
secp256k1_scalar_get_b32(sk32, sk);
if (!secp256k1_eckey_pubkey_serialize(gen2, gen2_33, &pubkey_size, 1)) {
return 0;
}
if (!secp256k1_eckey_pubkey_serialize(p1, p1_33, &pubkey_size, 1)) {
return 0;
}
if (!secp256k1_eckey_pubkey_serialize(p2, p2_33, &pubkey_size, 1)) {
return 0;
}
ret &= secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, noncefp, ndata);
/* R1 = k*G, R2 = k*Y */
secp256k1_dleq_pair(&ctx->ecmult_gen_ctx, &r1, &r2, &k, gen2);
/* We declassify the non-secret values r1 and r2 to allow using them as
* branch points. */
secp256k1_declassify(ctx, &r1, sizeof(r1));
secp256k1_declassify(ctx, &r2, sizeof(r2));
/* e = tagged hash(p1, gen2, p2, r1, r2) */
/* s = k + e * sk */
secp256k1_dleq_challenge(e, gen2, &r1, &r2, p1, p2);
secp256k1_scalar_mul(s, e, sk);
secp256k1_scalar_add(s, s, &k);
secp256k1_scalar_clear(&k);
return ret;
}
static int secp256k1_dleq_verify(const secp256k1_ecmult_context *ecmult_ctx, const secp256k1_scalar *s, const secp256k1_scalar *e, secp256k1_ge *p1, secp256k1_ge *gen2, secp256k1_ge *p2) {
secp256k1_scalar e_neg;
secp256k1_scalar e_expected;
secp256k1_gej gen2j;
secp256k1_gej p1j, p2j;
secp256k1_gej r1j, r2j;
secp256k1_ge r1, r2;
secp256k1_gej tmpj;
secp256k1_gej_set_ge(&p1j, p1);
secp256k1_gej_set_ge(&p2j, p2);
secp256k1_scalar_negate(&e_neg, e);
/* R1 = s*G - e*P1 */
secp256k1_ecmult(ecmult_ctx, &r1j, &p1j, &e_neg, s);
/* R2 = s*gen2 - e*P2 */
secp256k1_ecmult(ecmult_ctx, &tmpj, &p2j, &e_neg, &secp256k1_scalar_zero);
secp256k1_gej_set_ge(&gen2j, gen2);
secp256k1_ecmult(ecmult_ctx, &r2j, &gen2j, s, &secp256k1_scalar_zero);
secp256k1_gej_add_var(&r2j, &r2j, &tmpj, NULL);
secp256k1_ge_set_gej(&r1, &r1j);
secp256k1_ge_set_gej(&r2, &r2j);
secp256k1_dleq_challenge(&e_expected, gen2, &r1, &r2, p1, p2);
secp256k1_scalar_add(&e_expected, &e_expected, &e_neg);
return secp256k1_scalar_is_zero(&e_expected);
}
#endif

View File

@ -8,6 +8,7 @@
#define SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H
#include "include/secp256k1_ecdsa_adaptor.h"
#include "modules/ecdsa_adaptor/dleq_impl.h"
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
* SHA256 to SHA256("ECDSAadaptor/non")||SHA256("ECDSAadaptor/non"). */
@ -69,6 +70,9 @@ static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned c
if (algolen == sizeof(ecdsa_adaptor_algo)
&& secp256k1_memcmp_var(algo, ecdsa_adaptor_algo, algolen) == 0) {
secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(&sha);
} else if (algolen == sizeof(dleq_algo)
&& secp256k1_memcmp_var(algo, dleq_algo, algolen) == 0) {
secp256k1_nonce_function_dleq_sha256_tagged(&sha);
} else {
secp256k1_sha256_initialize_tagged(&sha, algo, algolen);
}