diff --git a/include/secp256k1_ecdsa_adaptor.h b/include/secp256k1_ecdsa_adaptor.h index ef8d53902..90bef4c5b 100644 --- a/include/secp256k1_ecdsa_adaptor.h +++ b/include/secp256k1_ecdsa_adaptor.h @@ -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 diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index 5e3fcec1f..e0e98c85b 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -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) { @@ -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