Hush Full Node software. We were censored from Github, this is where all development happens now.
https://hush.is
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
631 lines
22 KiB
631 lines
22 KiB
|
|
/**********************************************************************
|
|
* Copyright (c) 2018 Andrew Poelstra, Jonas Nick *
|
|
* Distributed under the MIT software license, see the accompanying *
|
|
* file COPYING or https://www.opensource.org/licenses/mit-license.php*
|
|
**********************************************************************/
|
|
|
|
#ifndef _SECP256K1_MODULE_MUSIG_MAIN_
|
|
#define _SECP256K1_MODULE_MUSIG_MAIN_
|
|
|
|
#include "../../../include/secp256k1.h"
|
|
#include "../../../include/secp256k1_musig.h"
|
|
#include "hash.h"
|
|
|
|
/* Computes ell = SHA256(pk[0], ..., pk[np-1]) */
|
|
static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_pubkey *pk, size_t np) {
|
|
secp256k1_sha256 sha;
|
|
size_t i;
|
|
|
|
secp256k1_sha256_initialize(&sha);
|
|
for (i = 0; i < np; i++) {
|
|
unsigned char ser[33];
|
|
size_t serlen = sizeof(ser);
|
|
if (!secp256k1_ec_pubkey_serialize(ctx, ser, &serlen, &pk[i], SECP256K1_EC_COMPRESSED)) {
|
|
return 0;
|
|
}
|
|
secp256k1_sha256_write(&sha, ser, serlen);
|
|
}
|
|
secp256k1_sha256_finalize(&sha, ell);
|
|
return 1;
|
|
}
|
|
|
|
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
|
* SHA256 to SHA256("MuSig coefficient")||SHA256("MuSig coefficient"). */
|
|
static void secp256k1_musig_sha256_init_tagged(secp256k1_sha256 *sha) {
|
|
secp256k1_sha256_initialize(sha);
|
|
|
|
sha->s[0] = 0x0fd0690cul;
|
|
sha->s[1] = 0xfefeae97ul;
|
|
sha->s[2] = 0x996eac7ful;
|
|
sha->s[3] = 0x5c30d864ul;
|
|
sha->s[4] = 0x8c4a0573ul;
|
|
sha->s[5] = 0xaca1a22ful;
|
|
sha->s[6] = 0x6f43b801ul;
|
|
sha->s[7] = 0x85ce27cdul;
|
|
sha->bytes = 64;
|
|
}
|
|
|
|
/* Compute r = SHA256(ell, idx). The four bytes of idx are serialized least significant byte first. */
|
|
static void secp256k1_musig_coefficient(secp256k1_scalar *r, const unsigned char *ell, uint32_t idx) {
|
|
secp256k1_sha256 sha;
|
|
unsigned char buf[32];
|
|
size_t i;
|
|
|
|
secp256k1_musig_sha256_init_tagged(&sha);
|
|
secp256k1_sha256_write(&sha, ell, 32);
|
|
/* We're hashing the index of the signer instead of its public key as specified
|
|
* in the MuSig paper. This reduces the total amount of data that needs to be
|
|
* hashed.
|
|
* Additionally, it prevents creating identical musig_coefficients for identical
|
|
* public keys. A participant Bob could choose his public key to be the same as
|
|
* Alice's, then replay Alice's messages (nonce and partial signature) to create
|
|
* a valid partial signature. This is not a problem for MuSig per se, but could
|
|
* result in subtle issues with protocols building on threshold signatures.
|
|
* With the assumption that public keys are unique, hashing the index is
|
|
* equivalent to hashing the public key. Because the public key can be
|
|
* identified by the index given the ordered list of public keys (included in
|
|
* ell), the index is just a different encoding of the public key.*/
|
|
for (i = 0; i < sizeof(uint32_t); i++) {
|
|
unsigned char c = idx;
|
|
secp256k1_sha256_write(&sha, &c, 1);
|
|
idx >>= 8;
|
|
}
|
|
secp256k1_sha256_finalize(&sha, buf);
|
|
secp256k1_scalar_set_b32(r, buf, NULL);
|
|
}
|
|
|
|
typedef struct {
|
|
const secp256k1_context *ctx;
|
|
unsigned char ell[32];
|
|
const secp256k1_pubkey *pks;
|
|
} secp256k1_musig_pubkey_combine_ecmult_data;
|
|
|
|
/* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */
|
|
static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
|
|
secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data;
|
|
secp256k1_musig_coefficient(sc, ctx->ell, idx);
|
|
return secp256k1_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]);
|
|
}
|
|
|
|
|
|
static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *signers, uint32_t n_signers) {
|
|
uint32_t i;
|
|
for (i = 0; i < n_signers; i++) {
|
|
memset(&signers[i], 0, sizeof(signers[i]));
|
|
signers[i].index = i;
|
|
signers[i].present = 0;
|
|
}
|
|
}
|
|
|
|
int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_pubkey *combined_pk, unsigned char *pk_hash32, const secp256k1_pubkey *pubkeys, size_t n_pubkeys) {
|
|
secp256k1_musig_pubkey_combine_ecmult_data ecmult_data;
|
|
secp256k1_gej pkj;
|
|
secp256k1_ge pkp;
|
|
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(combined_pk != NULL);
|
|
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
|
ARG_CHECK(pubkeys != NULL);
|
|
ARG_CHECK(n_pubkeys > 0);
|
|
|
|
ecmult_data.ctx = ctx;
|
|
ecmult_data.pks = pubkeys;
|
|
if (!secp256k1_musig_compute_ell(ctx, ecmult_data.ell, pubkeys, n_pubkeys)) {
|
|
return 0;
|
|
}
|
|
if (!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &pkj, NULL, secp256k1_musig_pubkey_combine_callback, (void *) &ecmult_data, n_pubkeys)) {
|
|
return 0;
|
|
}
|
|
secp256k1_ge_set_gej(&pkp, &pkj);
|
|
secp256k1_pubkey_save(combined_pk, &pkp);
|
|
|
|
if (pk_hash32 != NULL) {
|
|
memcpy(pk_hash32, ecmult_data.ell, 32);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, size_t n_signers, size_t my_index, const unsigned char *seckey) {
|
|
unsigned char combined_ser[33];
|
|
size_t combined_ser_size = sizeof(combined_ser);
|
|
int overflow;
|
|
secp256k1_scalar secret;
|
|
secp256k1_scalar mu;
|
|
secp256k1_sha256 sha;
|
|
secp256k1_gej rj;
|
|
secp256k1_ge rp;
|
|
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
|
ARG_CHECK(session != NULL);
|
|
ARG_CHECK(signers != NULL);
|
|
ARG_CHECK(nonce_commitment32 != NULL);
|
|
ARG_CHECK(session_id32 != NULL);
|
|
ARG_CHECK(combined_pk != NULL);
|
|
ARG_CHECK(pk_hash32 != NULL);
|
|
ARG_CHECK(seckey != NULL);
|
|
|
|
memset(session, 0, sizeof(*session));
|
|
|
|
if (msg32 != NULL) {
|
|
memcpy(session->msg, msg32, 32);
|
|
session->msg_is_set = 1;
|
|
} else {
|
|
session->msg_is_set = 0;
|
|
}
|
|
memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk));
|
|
memcpy(session->pk_hash, pk_hash32, 32);
|
|
session->nonce_is_set = 0;
|
|
session->has_secret_data = 1;
|
|
if (n_signers == 0 || my_index >= n_signers) {
|
|
return 0;
|
|
}
|
|
if (n_signers > UINT32_MAX) {
|
|
return 0;
|
|
}
|
|
session->n_signers = (uint32_t) n_signers;
|
|
secp256k1_musig_signers_init(signers, session->n_signers);
|
|
session->nonce_commitments_hash_is_set = 0;
|
|
|
|
/* Compute secret key */
|
|
secp256k1_scalar_set_b32(&secret, seckey, &overflow);
|
|
if (overflow) {
|
|
secp256k1_scalar_clear(&secret);
|
|
return 0;
|
|
}
|
|
secp256k1_musig_coefficient(&mu, pk_hash32, (uint32_t) my_index);
|
|
secp256k1_scalar_mul(&secret, &secret, &mu);
|
|
secp256k1_scalar_get_b32(session->seckey, &secret);
|
|
|
|
/* Compute secret nonce */
|
|
secp256k1_sha256_initialize(&sha);
|
|
secp256k1_sha256_write(&sha, session_id32, 32);
|
|
if (session->msg_is_set) {
|
|
secp256k1_sha256_write(&sha, msg32, 32);
|
|
}
|
|
secp256k1_ec_pubkey_serialize(ctx, combined_ser, &combined_ser_size, combined_pk, SECP256K1_EC_COMPRESSED);
|
|
secp256k1_sha256_write(&sha, combined_ser, combined_ser_size);
|
|
secp256k1_sha256_write(&sha, seckey, 32);
|
|
secp256k1_sha256_finalize(&sha, session->secnonce);
|
|
secp256k1_scalar_set_b32(&secret, session->secnonce, &overflow);
|
|
if (overflow) {
|
|
secp256k1_scalar_clear(&secret);
|
|
return 0;
|
|
}
|
|
|
|
/* Compute public nonce and commitment */
|
|
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &secret);
|
|
secp256k1_ge_set_gej(&rp, &rj);
|
|
secp256k1_pubkey_save(&session->nonce, &rp);
|
|
|
|
if (nonce_commitment32 != NULL) {
|
|
unsigned char commit[33];
|
|
size_t commit_size = sizeof(commit);
|
|
secp256k1_sha256_initialize(&sha);
|
|
secp256k1_ec_pubkey_serialize(ctx, commit, &commit_size, &session->nonce, SECP256K1_EC_COMPRESSED);
|
|
secp256k1_sha256_write(&sha, commit, commit_size);
|
|
secp256k1_sha256_finalize(&sha, nonce_commitment32);
|
|
}
|
|
|
|
secp256k1_scalar_clear(&secret);
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, secp256k1_pubkey *nonce, const unsigned char *const *commitments, size_t n_commitments) {
|
|
secp256k1_sha256 sha;
|
|
unsigned char nonce_commitments_hash[32];
|
|
size_t i;
|
|
(void) ctx;
|
|
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(session != NULL);
|
|
ARG_CHECK(signers != NULL);
|
|
ARG_CHECK(nonce != NULL);
|
|
ARG_CHECK(commitments != NULL);
|
|
|
|
if (!session->has_secret_data || n_commitments != session->n_signers) {
|
|
return 0;
|
|
}
|
|
for (i = 0; i < n_commitments; i++) {
|
|
ARG_CHECK(commitments[i] != NULL);
|
|
}
|
|
|
|
secp256k1_sha256_initialize(&sha);
|
|
for (i = 0; i < n_commitments; i++) {
|
|
memcpy(signers[i].nonce_commitment, commitments[i], 32);
|
|
secp256k1_sha256_write(&sha, commitments[i], 32);
|
|
}
|
|
secp256k1_sha256_finalize(&sha, nonce_commitments_hash);
|
|
if (session->nonce_commitments_hash_is_set
|
|
&& memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) {
|
|
/* Abort if get_public_nonce has been called before with a different array of
|
|
* commitments. */
|
|
return 0;
|
|
}
|
|
memcpy(session->nonce_commitments_hash, nonce_commitments_hash, 32);
|
|
session->nonce_commitments_hash_is_set = 1;
|
|
memcpy(nonce, &session->nonce, sizeof(*nonce));
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, const unsigned char *const *commitments, size_t n_signers) {
|
|
size_t i;
|
|
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(session != NULL);
|
|
ARG_CHECK(signers != NULL);
|
|
ARG_CHECK(combined_pk != NULL);
|
|
ARG_CHECK(pk_hash32 != NULL);
|
|
ARG_CHECK(commitments != NULL);
|
|
/* Check n_signers before checking commitments to allow testing the case where
|
|
* n_signers is big without allocating the space. */
|
|
if (n_signers > UINT32_MAX) {
|
|
return 0;
|
|
}
|
|
for (i = 0; i < n_signers; i++) {
|
|
ARG_CHECK(commitments[i] != NULL);
|
|
}
|
|
(void) ctx;
|
|
|
|
memset(session, 0, sizeof(*session));
|
|
|
|
memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk));
|
|
if (n_signers == 0) {
|
|
return 0;
|
|
}
|
|
session->n_signers = (uint32_t) n_signers;
|
|
secp256k1_musig_signers_init(signers, session->n_signers);
|
|
|
|
memcpy(session->pk_hash, pk_hash32, 32);
|
|
session->nonce_is_set = 0;
|
|
session->msg_is_set = 0;
|
|
if (msg32 != NULL) {
|
|
memcpy(session->msg, msg32, 32);
|
|
session->msg_is_set = 1;
|
|
}
|
|
session->has_secret_data = 0;
|
|
session->nonce_commitments_hash_is_set = 0;
|
|
|
|
for (i = 0; i < n_signers; i++) {
|
|
memcpy(signers[i].nonce_commitment, commitments[i], 32);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, const secp256k1_pubkey *nonce) {
|
|
unsigned char commit[33];
|
|
size_t commit_size = sizeof(commit);
|
|
secp256k1_sha256 sha;
|
|
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(signer != NULL);
|
|
ARG_CHECK(nonce != NULL);
|
|
|
|
secp256k1_sha256_initialize(&sha);
|
|
secp256k1_ec_pubkey_serialize(ctx, commit, &commit_size, nonce, SECP256K1_EC_COMPRESSED);
|
|
secp256k1_sha256_write(&sha, commit, commit_size);
|
|
secp256k1_sha256_finalize(&sha, commit);
|
|
|
|
if (memcmp(commit, signer->nonce_commitment, 32) != 0) {
|
|
return 0;
|
|
}
|
|
memcpy(&signer->nonce, nonce, sizeof(*nonce));
|
|
signer->present = 1;
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signers, size_t n_signers, int *nonce_is_negated, const secp256k1_pubkey *adaptor) {
|
|
secp256k1_gej combined_noncej;
|
|
secp256k1_ge combined_noncep;
|
|
secp256k1_ge noncep;
|
|
secp256k1_sha256 sha;
|
|
unsigned char nonce_commitments_hash[32];
|
|
size_t i;
|
|
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(session != NULL);
|
|
ARG_CHECK(signers != NULL);
|
|
|
|
if (n_signers != session->n_signers) {
|
|
return 0;
|
|
}
|
|
secp256k1_sha256_initialize(&sha);
|
|
secp256k1_gej_set_infinity(&combined_noncej);
|
|
for (i = 0; i < n_signers; i++) {
|
|
if (!signers[i].present) {
|
|
return 0;
|
|
}
|
|
secp256k1_sha256_write(&sha, signers[i].nonce_commitment, 32);
|
|
secp256k1_pubkey_load(ctx, &noncep, &signers[i].nonce);
|
|
secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL);
|
|
}
|
|
secp256k1_sha256_finalize(&sha, nonce_commitments_hash);
|
|
/* Either the session is a verifier session or or the nonce_commitments_hash has
|
|
* been set in `musig_session_get_public_nonce`. */
|
|
VERIFY_CHECK(!session->has_secret_data || session->nonce_commitments_hash_is_set);
|
|
if (session->has_secret_data
|
|
&& memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) {
|
|
/* If the signers' commitments changed between get_public_nonce and now we
|
|
* have to abort because in that case they may have seen our nonce before
|
|
* creating their commitment. That can happen if the signer_data given to
|
|
* this function is different to the signer_data given to get_public_nonce.
|
|
* */
|
|
return 0;
|
|
}
|
|
|
|
/* Add public adaptor to nonce */
|
|
if (adaptor != NULL) {
|
|
secp256k1_pubkey_load(ctx, &noncep, adaptor);
|
|
secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL);
|
|
}
|
|
secp256k1_ge_set_gej(&combined_noncep, &combined_noncej);
|
|
if (secp256k1_fe_is_quad_var(&combined_noncep.y)) {
|
|
session->nonce_is_negated = 0;
|
|
} else {
|
|
session->nonce_is_negated = 1;
|
|
secp256k1_ge_neg(&combined_noncep, &combined_noncep);
|
|
}
|
|
if (nonce_is_negated != NULL) {
|
|
*nonce_is_negated = session->nonce_is_negated;
|
|
}
|
|
secp256k1_pubkey_save(&session->combined_nonce, &combined_noncep);
|
|
session->nonce_is_set = 1;
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_session_set_msg(const secp256k1_context* ctx, secp256k1_musig_session *session, const unsigned char *msg32) {
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(session != NULL);
|
|
ARG_CHECK(msg32 != NULL);
|
|
|
|
if (session->msg_is_set) {
|
|
return 0;
|
|
}
|
|
memcpy(session->msg, msg32, 32);
|
|
session->msg_is_set = 1;
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_partial_signature_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_signature* sig) {
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(out32 != NULL);
|
|
ARG_CHECK(sig != NULL);
|
|
memcpy(out32, sig->data, 32);
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_partial_signature_parse(const secp256k1_context* ctx, secp256k1_musig_partial_signature* sig, const unsigned char *in32) {
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(sig != NULL);
|
|
ARG_CHECK(in32 != NULL);
|
|
memcpy(sig->data, in32, 32);
|
|
return 1;
|
|
}
|
|
|
|
/* Compute msghash = SHA256(combined_nonce, combined_pk, msg) */
|
|
static int secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) {
|
|
unsigned char buf[33];
|
|
size_t bufsize = 33;
|
|
secp256k1_ge rp;
|
|
secp256k1_sha256 sha;
|
|
|
|
secp256k1_sha256_initialize(&sha);
|
|
if (!session->nonce_is_set) {
|
|
return 0;
|
|
}
|
|
secp256k1_pubkey_load(ctx, &rp, &session->combined_nonce);
|
|
secp256k1_fe_get_b32(buf, &rp.x);
|
|
secp256k1_sha256_write(&sha, buf, 32);
|
|
secp256k1_ec_pubkey_serialize(ctx, buf, &bufsize, &session->combined_pk, SECP256K1_EC_COMPRESSED);
|
|
VERIFY_CHECK(bufsize == 33);
|
|
secp256k1_sha256_write(&sha, buf, bufsize);
|
|
if (!session->msg_is_set) {
|
|
return 0;
|
|
}
|
|
secp256k1_sha256_write(&sha, session->msg, 32);
|
|
secp256k1_sha256_finalize(&sha, msghash);
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_musig_partial_signature *partial_sig) {
|
|
unsigned char msghash[32];
|
|
int overflow;
|
|
secp256k1_scalar sk;
|
|
secp256k1_scalar e, k;
|
|
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(partial_sig != NULL);
|
|
ARG_CHECK(session != NULL);
|
|
|
|
if (!session->nonce_is_set || !session->has_secret_data) {
|
|
return 0;
|
|
}
|
|
|
|
/* build message hash */
|
|
if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) {
|
|
return 0;
|
|
}
|
|
secp256k1_scalar_set_b32(&e, msghash, NULL);
|
|
|
|
secp256k1_scalar_set_b32(&sk, session->seckey, &overflow);
|
|
if (overflow) {
|
|
secp256k1_scalar_clear(&sk);
|
|
return 0;
|
|
}
|
|
|
|
secp256k1_scalar_set_b32(&k, session->secnonce, &overflow);
|
|
if (overflow || secp256k1_scalar_is_zero(&k)) {
|
|
secp256k1_scalar_clear(&sk);
|
|
secp256k1_scalar_clear(&k);
|
|
return 0;
|
|
}
|
|
if (session->nonce_is_negated) {
|
|
secp256k1_scalar_negate(&k, &k);
|
|
}
|
|
|
|
/* Sign */
|
|
secp256k1_scalar_mul(&e, &e, &sk);
|
|
secp256k1_scalar_add(&e, &e, &k);
|
|
secp256k1_scalar_get_b32(&partial_sig->data[0], &e);
|
|
secp256k1_scalar_clear(&sk);
|
|
secp256k1_scalar_clear(&k);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs) {
|
|
size_t i;
|
|
secp256k1_scalar s;
|
|
secp256k1_ge noncep;
|
|
(void) ctx;
|
|
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(sig != NULL);
|
|
ARG_CHECK(partial_sigs != NULL);
|
|
ARG_CHECK(session != NULL);
|
|
|
|
if (!session->nonce_is_set) {
|
|
return 0;
|
|
}
|
|
if (n_sigs != session->n_signers) {
|
|
return 0;
|
|
}
|
|
secp256k1_scalar_clear(&s);
|
|
for (i = 0; i < n_sigs; i++) {
|
|
int overflow;
|
|
secp256k1_scalar term;
|
|
|
|
secp256k1_scalar_set_b32(&term, partial_sigs[i].data, &overflow);
|
|
if (overflow) {
|
|
return 0;
|
|
}
|
|
secp256k1_scalar_add(&s, &s, &term);
|
|
}
|
|
|
|
secp256k1_pubkey_load(ctx, &noncep, &session->combined_nonce);
|
|
VERIFY_CHECK(secp256k1_fe_is_quad_var(&noncep.y));
|
|
secp256k1_fe_normalize(&noncep.x);
|
|
secp256k1_fe_get_b32(&sig->data[0], &noncep.x);
|
|
secp256k1_scalar_get_b32(&sig->data[32], &s);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, const secp256k1_pubkey *pubkey) {
|
|
unsigned char msghash[32];
|
|
secp256k1_scalar s;
|
|
secp256k1_scalar e;
|
|
secp256k1_scalar mu;
|
|
secp256k1_gej rj;
|
|
secp256k1_ge rp;
|
|
int overflow;
|
|
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
|
ARG_CHECK(session != NULL);
|
|
ARG_CHECK(signer != NULL);
|
|
ARG_CHECK(partial_sig != NULL);
|
|
ARG_CHECK(pubkey != NULL);
|
|
|
|
if (!session->nonce_is_set || !signer->present) {
|
|
return 0;
|
|
}
|
|
secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow);
|
|
if (overflow) {
|
|
return 0;
|
|
}
|
|
if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) {
|
|
return 0;
|
|
}
|
|
secp256k1_scalar_set_b32(&e, msghash, NULL);
|
|
|
|
/* Multiplying the messagehash by the musig coefficient is equivalent
|
|
* to multiplying the signer's public key by the coefficient, except
|
|
* much easier to do. */
|
|
secp256k1_musig_coefficient(&mu, session->pk_hash, signer->index);
|
|
secp256k1_scalar_mul(&e, &e, &mu);
|
|
|
|
if (!secp256k1_pubkey_load(ctx, &rp, &signer->nonce)) {
|
|
return 0;
|
|
}
|
|
|
|
if (!secp256k1_schnorrsig_real_verify(ctx, &rj, &s, &e, pubkey)) {
|
|
return 0;
|
|
}
|
|
if (!session->nonce_is_negated) {
|
|
secp256k1_ge_neg(&rp, &rp);
|
|
}
|
|
secp256k1_gej_add_ge_var(&rj, &rj, &rp, NULL);
|
|
|
|
return secp256k1_gej_is_infinity(&rj);
|
|
}
|
|
|
|
int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_musig_partial_signature *adaptor_sig, const secp256k1_musig_partial_signature *partial_sig, const unsigned char *sec_adaptor32, int nonce_is_negated) {
|
|
secp256k1_scalar s;
|
|
secp256k1_scalar t;
|
|
int overflow;
|
|
|
|
(void) ctx;
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(adaptor_sig != NULL);
|
|
ARG_CHECK(partial_sig != NULL);
|
|
ARG_CHECK(sec_adaptor32 != NULL);
|
|
|
|
secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow);
|
|
if (overflow) {
|
|
return 0;
|
|
}
|
|
secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow);
|
|
if (overflow) {
|
|
secp256k1_scalar_clear(&t);
|
|
return 0;
|
|
}
|
|
|
|
if (nonce_is_negated) {
|
|
secp256k1_scalar_negate(&t, &t);
|
|
}
|
|
|
|
secp256k1_scalar_add(&s, &s, &t);
|
|
secp256k1_scalar_get_b32(adaptor_sig->data, &s);
|
|
secp256k1_scalar_clear(&t);
|
|
return 1;
|
|
}
|
|
|
|
int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_is_negated) {
|
|
secp256k1_scalar t;
|
|
secp256k1_scalar s;
|
|
int overflow;
|
|
size_t i;
|
|
|
|
(void) ctx;
|
|
VERIFY_CHECK(ctx != NULL);
|
|
ARG_CHECK(sec_adaptor32 != NULL);
|
|
ARG_CHECK(sig != NULL);
|
|
ARG_CHECK(partial_sigs != NULL);
|
|
|
|
secp256k1_scalar_set_b32(&t, &sig->data[32], &overflow);
|
|
if (overflow) {
|
|
return 0;
|
|
}
|
|
secp256k1_scalar_negate(&t, &t);
|
|
|
|
for (i = 0; i < n_partial_sigs; i++) {
|
|
secp256k1_scalar_set_b32(&s, partial_sigs[i].data, &overflow);
|
|
if (overflow) {
|
|
secp256k1_scalar_clear(&t);
|
|
return 0;
|
|
}
|
|
secp256k1_scalar_add(&t, &t, &s);
|
|
}
|
|
|
|
if (!nonce_is_negated) {
|
|
secp256k1_scalar_negate(&t, &t);
|
|
}
|
|
secp256k1_scalar_get_b32(sec_adaptor32, &t);
|
|
secp256k1_scalar_clear(&t);
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
|