Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

musig: add partial_sig_point function #222

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions include/secp256k1_musig.h
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,31 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verif
const secp256k1_musig_session *session
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);

/** Computes a "signature point"
*
* The signature point for a MuSig2 signer is s*G, where s is the (unique)
* partial signature of the signer. The signature point can be computed without
* knowledge of the signer's secret key.
*
* Returns: 0 if the arguments are invalid, 1 otherwise
* Args ctx: pointer to a context object, initialized for verification
* Out: sig_point: signature point
* In: pubnonce: public nonce of the signer in the signing session
* pubkey: public key of the signer in the signing session
* keyagg_cache: pointer to the keyagg_cache that was output when the
* aggregate public key for this signing session
* session: pointer to the session that was created with
* `musig_nonce_process`
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_point(
const secp256k1_context* ctx,
secp256k1_pubkey *sig_point,
const secp256k1_musig_pubnonce *pubnonce,
const secp256k1_pubkey *pubkey,
const secp256k1_musig_keyagg_cache *keyagg_cache,
const secp256k1_musig_session *session
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);

/** Aggregates partial signatures
*
* Returns: 0 if the arguments are invalid, 1 otherwise (which does NOT mean
Expand Down
64 changes: 64 additions & 0 deletions src/modules/musig/session_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,70 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2
return secp256k1_gej_is_infinity(&tmp);
}

/* TODO: abstract parts of the code that are shared with verify */
int secp256k1_musig_partial_sig_point(const secp256k1_context* ctx, secp256k1_pubkey *sig_point, const secp256k1_musig_pubnonce *pubnonce, const secp256k1_pubkey *pubkey, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_musig_session *session) {
secp256k1_keyagg_cache_internal cache_i;
secp256k1_musig_session_internal session_i;
secp256k1_scalar mu, e;
secp256k1_gej pkj;
secp256k1_ge nonce_pt[2];
secp256k1_gej rj;
secp256k1_gej tmpj;
secp256k1_ge tmp;
secp256k1_ge pkp;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubnonce != NULL);
ARG_CHECK(pubkey != NULL);
ARG_CHECK(keyagg_cache != NULL);
ARG_CHECK(session != NULL);

if (!secp256k1_musig_session_load(ctx, &session_i, session)) {
return 0;
}

/* Compute "effective" nonce rj = aggnonce[0] + b*aggnonce[1] */
/* TODO: use multiexp to compute -s*G + e*mu*pubkey + aggnonce[0] + b*aggnonce[1] */
if (!secp256k1_musig_pubnonce_load(ctx, nonce_pt, pubnonce)) {
return 0;
}
secp256k1_gej_set_ge(&rj, &nonce_pt[1]);
secp256k1_ecmult(&rj, &rj, &session_i.noncecoef, NULL);
secp256k1_gej_add_ge_var(&rj, &rj, &nonce_pt[0], NULL);

if (!secp256k1_pubkey_load(ctx, &pkp, pubkey)) {
return 0;
}
if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) {
return 0;
}
/* Multiplying the challenge by the KeyAgg coefficient is equivalent
* to multiplying the signer's public key by the coefficient, except
* much easier to do. */
secp256k1_musig_keyaggcoef(&mu, &cache_i, &pkp);
secp256k1_scalar_mul(&e, &session_i.challenge, &mu);

/* Negate e if secp256k1_fe_is_odd(&cache_i.pk.y)) XOR cache_i.parity_acc.
* This corresponds to the line "Let g' = g⋅gacc mod n" and the multiplication "g'⋅e"
* in the specification. */
if (secp256k1_fe_is_odd(&cache_i.pk.y)
!= cache_i.parity_acc) {
secp256k1_scalar_negate(&e, &e);
}

/* Compute -s*G + e*pkj + rj (e already includes the keyagg coefficient mu) */
secp256k1_gej_set_ge(&pkj, &pkp);
secp256k1_ecmult(&tmpj, &pkj, &e, NULL);
if (session_i.fin_nonce_parity) {
secp256k1_gej_neg(&rj, &rj);
}
secp256k1_gej_add_var(&tmpj, &tmpj, &rj, NULL);
secp256k1_ge_set_gej(&tmp, &tmpj);
secp256k1_pubkey_save(sig_point, &tmp);

return 1;
}

int secp256k1_musig_partial_sig_agg(const secp256k1_context* ctx, unsigned char *sig64, const secp256k1_musig_session *session, const secp256k1_musig_partial_sig * const* partial_sigs, size_t n_sigs) {
size_t i;
secp256k1_musig_session_internal session_i;
Expand Down
55 changes: 55 additions & 0 deletions src/modules/musig/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1295,6 +1295,60 @@ void musig_test_vectors_sigagg(void) {
}
}

void musig_sig_point_test(secp256k1_scratch_space *scratch) {
unsigned char sk[2][32];
secp256k1_keypair keypair[2];
secp256k1_musig_pubnonce pubnonce[2];
const secp256k1_musig_pubnonce *pubnonce_ptr[2];
secp256k1_musig_aggnonce aggnonce;
unsigned char msg[32];
secp256k1_xonly_pubkey agg_pk;
secp256k1_musig_keyagg_cache keyagg_cache;
unsigned char session_id[2][32];
secp256k1_musig_secnonce secnonce[2];
secp256k1_pubkey pk[2];
const secp256k1_pubkey *pk_ptr[2];
secp256k1_musig_partial_sig partial_sig[2];
const secp256k1_musig_partial_sig *partial_sig_ptr[2];
unsigned char final_sig[64];
secp256k1_musig_session session;
int i;

secp256k1_testrand256(msg);
for (i = 0; i < 2; i++) {
secp256k1_testrand256(session_id[i]);
secp256k1_testrand256(sk[i]);
pk_ptr[i] = &pk[i];
pubnonce_ptr[i] = &pubnonce[i];
partial_sig_ptr[i] = &partial_sig[i];

CHECK(create_keypair_and_pk(&keypair[i], &pk[i], sk[i]));
CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce[i], &pubnonce[i], session_id[i], sk[i], &pk[i], NULL, NULL, NULL) == 1);
}

CHECK(secp256k1_musig_pubkey_agg(ctx, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1);
CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 2) == 1);
CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, &keyagg_cache, NULL) == 1);

for (i = 0; i < 2; i++) {
secp256k1_pubkey sigp;
secp256k1_ge sigp_ge;
secp256k1_gej sigp_expected_gej;
secp256k1_scalar s;
CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig[i], &secnonce[i], &keypair[i], &keyagg_cache, &session) == 1);
CHECK(secp256k1_musig_partial_sig_verify(ctx, &partial_sig[i], &pubnonce[i], &pk[i], &keyagg_cache, &session) == 1);

CHECK(secp256k1_musig_partial_sig_load(ctx, &s, &partial_sig[i]));
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &sigp_expected_gej, &s);
CHECK(secp256k1_musig_partial_sig_point(ctx, &sigp, &pubnonce[i], &pk[i], &keyagg_cache, &session));
CHECK(secp256k1_pubkey_load(ctx, &sigp_ge, &sigp));
ge_equals_gej(&sigp_ge, &sigp_expected_gej);
}

CHECK(secp256k1_musig_partial_sig_agg(ctx, final_sig, &session, partial_sig_ptr, 2) == 1);
CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, sizeof(msg), &agg_pk) == 1);
}

void run_musig_tests(void) {
int i;
secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024);
Expand All @@ -1309,6 +1363,7 @@ void run_musig_tests(void) {
* parities */
scriptless_atomic_swap(scratch);
musig_tweak_test(scratch);
musig_sig_point_test(scratch);
}
sha256_tag_test();
musig_test_vectors_keyagg();
Expand Down