Skip to content

Commit

Permalink
Add ECDSA adaptor signature APIs
Browse files Browse the repository at this point in the history
This commit adds the ECDSA adaptor signature APIs:

- Encrypted Signing

  Creates an adaptor signature, which includes a proof to verify the adaptor
  signature.

- Encryption Verification

  Verifies that the adaptor decryption key can be extracted from the adaptor
  signature and the completed ECDSA signature.

- Signature Decryption

  Derives an ECDSA signature from an adaptor signature and an adaptor decryption
  key.

- Key Recovery

  Extracts the adaptor decryption key from the complete signature and the adaptor
  signature.
  • Loading branch information
jesseposner committed Jan 28, 2021
1 parent 4a470f0 commit f4af79c
Show file tree
Hide file tree
Showing 2 changed files with 379 additions and 0 deletions.
98 changes: 98 additions & 0 deletions include/secp256k1_ecdsa_adaptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,104 @@ typedef int (*secp256k1_nonce_function_hardened_ecdsa_adaptor)(
*/
SECP256K1_API extern const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor;

/** Encrypted Signing
*
* Creates an adaptor signature, which includes a proof to verify the adaptor
* signature.
*
* Returns: 1 on success, 0 on failure
* Args: ctx: a secp256k1 context object, initialized for signing
* (cannot be NULL)
* Out: adaptor_sig162: pointer to 162 byte to store the returned signature
* (cannot be NULL)
* In: seckey32: pointer to 32 byte secret key that will be used for
* signing (cannot be NULL)
* enckey: pointer to the encryption public key (cannot be NULL)
* msg32: pointer to the 32-byte message to sign (cannot be NULL)
* noncefp: pointer to a nonce generation function. If NULL,
* secp256k1_nonce_function_ecdsa_adaptor is used
* ndata: pointer to arbitrary data used by the nonce generation
* function (can be NULL). If it is non-NULL and
* secp256k1_nonce_function_ecdsa_adaptor is used, then
* ndata must be a pointer to 32-byte auxiliary randomness
* as per BIP-340.
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_encrypt(
const secp256k1_context* ctx,
unsigned char *adaptor_sig162,
unsigned char *seckey32,
const secp256k1_pubkey *enckey,
const unsigned char *msg32,
secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp,
void *ndata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

/** Encryption Verification
*
* Verifies that the adaptor decryption key can be extracted from the adaptor signature
* and the completed ECDSA signature.
*
* Returns: 1 on success, 0 on failure
* Args: ctx: a secp256k1 context object, initialized for verification
* (cannot be NULL)
* In: adaptor_sig162: pointer to 162-byte signature to verify (cannot be NULL)
* pubkey: pointer to the public key corresponding to the secret key
* used for signing (cannot be NULL)
* msg32: pointer to the 32-byte message (cannot be NULL)
* enckey: pointer to the adaptor encryption public key (cannot be NULL)
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_verify(
const secp256k1_context* ctx,
const unsigned char *adaptor_sig162,
const secp256k1_pubkey *pubkey,
const unsigned char *msg32,
const secp256k1_pubkey *enckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

/** Signature Decryption
*
* Derives an ECDSA signature from an adaptor signature and an adaptor decryption key.
*
* Returns: 1 on success, 0 on failure
* Args: ctx: a secp256k1 context object (cannot be NULL)
* Out: sig: pointer to the ECDSA signature to create (cannot
* be NULL)
* In: deckey32: pointer to 32-byte decryption secret key for the adaptor
* encryption public key (cannot be NULL)
* adaptor_sig162: pointer to 162-byte byte adaptor sig (cannot be NULL)
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_decrypt(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature *sig,
const unsigned char *deckey32,
const unsigned char *adaptor_sig162
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Decryption Key Recovery
*
* Extracts the adaptor decryption key from the complete signature and the adaptor
* signature.
*
* Returns: 1 on success, 0 on failure
* Args: ctx: a secp256k1 context object, initialized for signing
* (cannot be NULL)
* Out: deckey32: pointer to 32-byte adaptor decryption key for the adaptor
* encryption public key (cannot be NULL)
* In: sig: pointer to ECDSA signature to recover the adaptor decryption
* key from (cannot be NULL)
* adaptor_sig: pointer to adaptor signature to recover the adaptor
* decryption key from (cannot be NULL)
* enckey: pointer to the adaptor encryption public key
* (cannot be NULL)
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_recover(
const secp256k1_context* ctx,
unsigned char *deckey32,
const secp256k1_ecdsa_signature *sig,
const unsigned char *adaptor_sig162,
const secp256k1_pubkey *enckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

#ifdef __cplusplus
}
#endif
Expand Down
281 changes: 281 additions & 0 deletions src/modules/ecdsa_adaptor/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,62 @@
#include "include/secp256k1_ecdsa_adaptor.h"
#include "modules/ecdsa_adaptor/dleq_impl.h"

/* (R, R', s', dleq_proof) */
static void secp256k1_ecdsa_adaptor_sig_serialize(unsigned char *adaptor_sig162, secp256k1_ge *r, secp256k1_ge *rp, const secp256k1_scalar *sp, const secp256k1_scalar *dleq_proof_e, const secp256k1_scalar *dleq_proof_s) {
size_t size = 33;

secp256k1_eckey_pubkey_serialize(r, adaptor_sig162, &size, 1);
secp256k1_eckey_pubkey_serialize(rp, &adaptor_sig162[33], &size, 1);
secp256k1_scalar_get_b32(&adaptor_sig162[66], sp);
secp256k1_scalar_get_b32(&adaptor_sig162[98], dleq_proof_e);
secp256k1_scalar_get_b32(&adaptor_sig162[130], dleq_proof_s);
}

static int secp256k1_ecdsa_adaptor_sig_deserialize(secp256k1_ge *r, secp256k1_scalar *sigr, secp256k1_ge *rp, secp256k1_scalar *sp, secp256k1_scalar *dleq_proof_e, secp256k1_scalar *dleq_proof_s, const unsigned char *adaptor_sig162) {
/* If r is deserialized, require that a sigr is provided to receive
* the X-coordinate */
VERIFY_CHECK((r == NULL) || (r != NULL && sigr != NULL));
if (r != NULL) {
if (!secp256k1_eckey_pubkey_parse(r, &adaptor_sig162[0], 33)) {
return 0;
}
}
if (sigr != NULL) {
int overflow;
secp256k1_scalar_set_b32(sigr, &adaptor_sig162[1], &overflow);
if(overflow) {
return 0;
}
}
if (rp != NULL) {
if (!secp256k1_eckey_pubkey_parse(rp, &adaptor_sig162[33], 33)) {
return 0;
}
}
if (sp != NULL) {
int overflow;
secp256k1_scalar_set_b32(sp, &adaptor_sig162[66], &overflow);
if(overflow) {
return 0;
}
}
if (dleq_proof_e != NULL) {
int overflow;
secp256k1_scalar_set_b32(dleq_proof_e, &adaptor_sig162[98], &overflow);
if (overflow) {
return 0;
}
}
if (dleq_proof_s != NULL) {
int overflow;
secp256k1_scalar_set_b32(dleq_proof_s, &adaptor_sig162[130], &overflow);
if (overflow) {
return 0;
}
}
return 1;
}

/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
* SHA256 to SHA256("ECDSAadaptor/non")||SHA256("ECDSAadaptor/non"). */
static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(secp256k1_sha256 *sha) {
Expand Down Expand Up @@ -94,4 +150,229 @@ static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned c

const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor = nonce_function_ecdsa_adaptor;

/* Modified secp256k1_ecdsa_sig_sign. Unlike the original, conditional negation of s
* takes place upon decryption, not signing */
static int secp256k1_ecdsa_adaptor_sig_sign(secp256k1_scalar *sigs, secp256k1_scalar *message, secp256k1_scalar *k, secp256k1_ge *r, secp256k1_scalar *sk) {
unsigned char b[32];
secp256k1_scalar sigr;
secp256k1_scalar n;
int overflow;

secp256k1_fe_normalize(&r->x);
secp256k1_fe_get_b32(b, &r->x);
secp256k1_scalar_set_b32(&sigr, b, &overflow);

secp256k1_scalar_mul(&n, &sigr, sk);
secp256k1_scalar_add(&n, &n, message);
secp256k1_scalar_inverse(sigs, k);
secp256k1_scalar_mul(sigs, sigs, &n);
secp256k1_scalar_clear(&n);

return !secp256k1_scalar_is_zero(&sigr) & !secp256k1_scalar_is_zero(sigs);
}

int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char *adaptor_sig162, unsigned char *seckey32, const secp256k1_pubkey *enckey, const unsigned char *msg32, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) {
secp256k1_scalar k;
secp256k1_gej rj, rpj;
secp256k1_ge r, rp;
secp256k1_ge enckey_ge;
secp256k1_scalar dleq_proof_s;
secp256k1_scalar dleq_proof_e;
secp256k1_scalar sk;
secp256k1_scalar msg;
secp256k1_scalar sp;
unsigned char nonce32[32] = { 0 };
unsigned char buf33[33];
int overflow;
size_t size = 33;
int ret = 1;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(adaptor_sig162 != NULL);
ARG_CHECK(seckey32 != NULL);
ARG_CHECK(enckey != NULL);
ARG_CHECK(msg32 != NULL);

if (noncefp == NULL) {
noncefp = secp256k1_nonce_function_ecdsa_adaptor;
}

ret &= secp256k1_pubkey_load(ctx, &enckey_ge, enckey);
secp256k1_eckey_pubkey_serialize(&enckey_ge, buf33, &size, 1);
ret &= !!noncefp(nonce32, msg32, seckey32, buf33, ecdsa_adaptor_algo16, ndata);
secp256k1_scalar_set_b32(&k, nonce32, NULL);
ret &= !secp256k1_scalar_is_zero(&k);
secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret);

/* R' := k*G */
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rpj, &k);
secp256k1_ge_set_gej(&rp, &rpj);
/* R = k*Y; */
secp256k1_ecmult_const(&rj, &enckey_ge, &k, 256);
secp256k1_ge_set_gej(&r, &rj);
/* We declassify the non-secret values rp and r to allow using them
* as branch points. */

secp256k1_declassify(ctx, &rp, sizeof(rp));
secp256k1_declassify(ctx, &r, sizeof(r));

/* proof = DLEQ_prove(k, (R', Y, R)) */
ret &= secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, dleq_algo16, &k, &enckey_ge, &rp, &r, noncefp, ndata);

secp256k1_scalar_set_b32(&sk, seckey32, &overflow);
ret &= !overflow;
ret &= !secp256k1_scalar_is_zero(&sk);
secp256k1_scalar_cmov(&sk, &secp256k1_scalar_one, !ret);
secp256k1_scalar_set_b32(&msg, msg32, NULL);
/* s' = k⁻¹(H(m) + x_coord(R)x) */
ret &= secp256k1_ecdsa_adaptor_sig_sign(&sp, &msg, &k, &r, &sk);

/* return (R, R', s', proof) */
secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig162, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s);

secp256k1_memczero(adaptor_sig162, 162, !ret);
secp256k1_scalar_clear(&k);
secp256k1_scalar_clear(&sk);

return ret;
}


/* Modified secp256k1_ecdsa_sig_verify */
static int secp256k1_ecdsa_adaptor_sig_verify(const secp256k1_context* ctx, secp256k1_gej *pr, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) {
secp256k1_scalar sn, u1, u2;
secp256k1_gej pubkeyj;

if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) {
return 0;
}

secp256k1_scalar_inverse_var(&sn, sigs);
secp256k1_scalar_mul(&u1, &sn, message);
secp256k1_scalar_mul(&u2, &sn, sigr);
secp256k1_gej_set_ge(&pubkeyj, pubkey);
secp256k1_ecmult(&ctx->ecmult_ctx, pr, &pubkeyj, &u2, &u1);
if (secp256k1_gej_is_infinity(pr)) {
return 0;
}
return 1;
}

int secp256k1_ecdsa_adaptor_verify(const secp256k1_context* ctx, const unsigned char *adaptor_sig162, const secp256k1_pubkey *pubkey, const unsigned char *msg32, const secp256k1_pubkey *enckey) {
secp256k1_scalar dleq_proof_s, dleq_proof_e;
secp256k1_scalar msg;
secp256k1_ge pubkey_ge;
secp256k1_ge r, rp;
secp256k1_scalar sp;
secp256k1_scalar sigr;
secp256k1_ge enckey_ge;
secp256k1_gej derived_rp;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(adaptor_sig162 != NULL);
ARG_CHECK(pubkey != NULL);
ARG_CHECK(msg32 != NULL);
ARG_CHECK(enckey != NULL);

if (!secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig162)) {
return 0;
}
if (!secp256k1_pubkey_load(ctx, &enckey_ge, enckey)) {
return 0;
}
/* DLEQ_verify((R', Y, R), proof) */
if(!secp256k1_dleq_verify(&ctx->ecmult_ctx, &dleq_proof_s, &dleq_proof_e, &rp, &enckey_ge, &r)) {
return 0;
}
secp256k1_scalar_set_b32(&msg, msg32, NULL);
if (!secp256k1_pubkey_load(ctx, &pubkey_ge, pubkey)) {
return 0;
}

/* return R' == s'⁻¹(m * G + R.x * X) */
if (!secp256k1_ecdsa_adaptor_sig_verify(ctx, &derived_rp, &sigr, &sp, &pubkey_ge, &msg)) {
return 0;
}
secp256k1_gej_neg(&derived_rp, &derived_rp);
secp256k1_gej_add_ge_var(&derived_rp, &derived_rp, &rp, NULL);
return secp256k1_gej_is_infinity(&derived_rp);
}

int secp256k1_ecdsa_adaptor_decrypt(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sig, const unsigned char *deckey32, const unsigned char *adaptor_sig162) {
secp256k1_scalar deckey;
secp256k1_scalar sp;
secp256k1_scalar s;
secp256k1_scalar sigr;
int overflow;
int high;
int ret = 1;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(sig != NULL);
ARG_CHECK(deckey32 != NULL);
ARG_CHECK(adaptor_sig162 != NULL);

secp256k1_scalar_set_b32(&deckey, deckey32, &overflow);
ret &= !overflow;
ret &= secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, &sp, NULL, NULL, adaptor_sig162);
secp256k1_scalar_inverse(&s, &deckey);
secp256k1_scalar_mul(&s, &s, &sp);
high = secp256k1_scalar_is_high(&s);
secp256k1_scalar_cond_negate(&s, high);
secp256k1_ecdsa_signature_save(sig, &sigr, &s);

secp256k1_memczero(&sig->data[0], 64, !ret);
secp256k1_scalar_clear(&deckey);
secp256k1_scalar_clear(&sp);
secp256k1_scalar_clear(&s);

return ret;
}

int secp256k1_ecdsa_adaptor_recover(const secp256k1_context* ctx, unsigned char *deckey32, const secp256k1_ecdsa_signature *sig, const unsigned char *adaptor_sig162, const secp256k1_pubkey *enckey) {
secp256k1_scalar sp, adaptor_sigr;
secp256k1_scalar s, r;
secp256k1_scalar deckey;
secp256k1_ge enckey_expected_ge;
secp256k1_gej enckey_expected_gej;
secp256k1_pubkey enckey_expected;
int ret = 1;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(deckey32 != NULL);
ARG_CHECK(sig != NULL);
ARG_CHECK(adaptor_sig162 != NULL);
ARG_CHECK(enckey != NULL);

if (!secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &adaptor_sigr, NULL, &sp, NULL, NULL, adaptor_sig162)) {
return 0;
}
secp256k1_ecdsa_signature_load(ctx, &r, &s, sig);
/* Check that we're not looking at some unrelated signature */
ret &= secp256k1_scalar_eq(&adaptor_sigr, &r);
secp256k1_scalar_inverse(&deckey, &s);
secp256k1_scalar_mul(&deckey, &deckey, &sp);

/* Deal with ECDSA malleability */
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &enckey_expected_gej, &deckey);
secp256k1_ge_set_gej(&enckey_expected_ge, &enckey_expected_gej);
/* We declassify enckey_expected_ge to allow using it as a branch point.
* This is fine because enckey_expected_ge is not a secret. */
secp256k1_declassify(ctx, &enckey_expected_ge, sizeof(enckey_expected_ge));
secp256k1_pubkey_save(&enckey_expected, &enckey_expected_ge);
if (memcmp(&enckey_expected, enckey, sizeof(enckey_expected)) != 0) {
secp256k1_scalar_negate(&deckey, &deckey);
}
secp256k1_scalar_get_b32(deckey32, &deckey);

secp256k1_scalar_clear(&deckey);
secp256k1_scalar_clear(&sp);
secp256k1_scalar_clear(&s);

return ret;
}

#endif

0 comments on commit f4af79c

Please sign in to comment.