From ea059393f0fbf3b2e05ab7fa8e924c20af38ce1e Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Thu, 23 Nov 2023 11:51:41 -0700 Subject: [PATCH] frost trusted dealer: nonce aggregation and adaptor signatures This commit adds nonce aggregation, as well as adaptor signatures. --- include/secp256k1_frost.h | 139 ++++++++++++++++ src/modules/frost/Makefile.am.include | 1 + src/modules/frost/adaptor_impl.h | 168 +++++++++++++++++++ src/modules/frost/keygen.h | 2 + src/modules/frost/main_impl.h | 1 + src/modules/frost/session.h | 15 ++ src/modules/frost/session_impl.h | 231 ++++++++++++++++++++++++++ 7 files changed, 557 insertions(+) create mode 100644 src/modules/frost/adaptor_impl.h diff --git a/include/secp256k1_frost.h b/include/secp256k1_frost.h index 4076e1a9..993b440e 100644 --- a/include/secp256k1_frost.h +++ b/include/secp256k1_frost.h @@ -78,6 +78,16 @@ typedef struct { unsigned char data[132]; } secp256k1_frost_pubnonce; +/** Opaque data structure that holds a FROST session. + * + * This structure is not required to be kept secret for the signing protocol + * to be secure. Guaranteed to be 133 bytes in size. It can be safely + * copied/moved. No serialization and parsing functions. + */ +typedef struct { + unsigned char data[133]; +} secp256k1_frost_session; + /** Parse a signer's public nonce. * * Returns: 1 when the nonce could be parsed, 0 otherwise. @@ -335,6 +345,135 @@ SECP256K1_API int secp256k1_frost_nonce_gen( const unsigned char *extra_input32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +/** Takes the public nonces of all signers and computes a session that is + * required for signing and verification of partial signatures. The participant + * IDs can be sorted before combining, but the corresponding pubnonces must be + * resorted as well. All signers must use the same sorting of pubnonces, + * otherwise signing will fail. + * + * Returns: 0 if the arguments are invalid or if some signer sent invalid + * pubnonces, 1 otherwise + * Args: ctx: pointer to a context object + * Out: session: pointer to a struct to store the session + * In: pubnonces: array of pointers to public nonces sent by the signers + * n_pubnonces: number of elements in the pubnonces array. Must be + * greater than 0. + * msg32: the 32-byte message to sign + * agg_pk: the FROST-aggregated public key + * my_id: the ID of the participant who will use the session for + * signing +* ids: array of the IDs of the signers + * tweak_cache: pointer to frost_tweak_cache struct (can be NULL) + * adaptor: optional pointer to an adaptor point encoded as a + * public key if this signing session is part of an + * adaptor signature protocol (can be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_nonce_process( + const secp256k1_context *ctx, + secp256k1_frost_session *session, + const secp256k1_frost_pubnonce * const *pubnonces, + size_t n_pubnonces, + const unsigned char *msg32, + const secp256k1_xonly_pubkey *agg_pk, + size_t my_id, + const size_t *ids, + const secp256k1_frost_tweak_cache *tweak_cache, + const secp256k1_pubkey *adaptor +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(8); + +/** Extracts the nonce_parity bit from a session + * + * This is used for adaptor signatures. + * + * Returns: 0 if the arguments are invalid, 1 otherwise + * Args: ctx: pointer to a context object + * Out: nonce_parity: pointer to an integer that indicates the parity + * of the aggregate public nonce. Used for adaptor + * signatures. + * In: session: pointer to the session that was created with + * frost_nonce_process + */ +SECP256K1_API int secp256k1_frost_nonce_parity( + const secp256k1_context *ctx, + int *nonce_parity, + const secp256k1_frost_session *session +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verifies that the adaptor can be extracted by combining the adaptor + * pre-signature and the completed signature. + * + * Returns: 0 if the arguments are invalid or the adaptor signature does not + * verify, 1 otherwise + * Args: ctx: pointer to a context object + * In: pre_sig64: 64-byte pre-signature + * msg32: the 32-byte message being verified + * pubkey: pointer to an x-only public key to verify with + * adaptor: pointer to the adaptor point being verified + * nonce_parity: the output of `frost_nonce_parity` called with the + * session used for producing the pre-signature + */ +SECP256K1_API int secp256k1_frost_verify_adaptor( + const secp256k1_context *ctx, + const unsigned char *pre_sig64, + const unsigned char *msg32, + const secp256k1_xonly_pubkey *pubkey, + const secp256k1_pubkey *adaptor, + int nonce_parity +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Creates a signature from a pre-signature and an adaptor. + * + * If the sec_adaptor32 argument is incorrect, the output signature will be + * invalid. This function does not verify the signature. + * + * Returns: 0 if the arguments are invalid, or pre_sig64 or sec_adaptor32 contain + * invalid (overflowing) values. 1 otherwise (which does NOT mean the + * signature or the adaptor are valid!) + * Args: ctx: pointer to a context object + * Out: sig64: 64-byte signature. This pointer may point to the same + * memory area as `pre_sig`. + * In: pre_sig64: 64-byte pre-signature + * sec_adaptor32: 32-byte secret adaptor to add to the pre-signature + * nonce_parity: the output of `frost_nonce_parity` called with the + * session used for producing the pre-signature + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_adapt( + const secp256k1_context *ctx, + unsigned char *sig64, + const unsigned char *pre_sig64, + const unsigned char *sec_adaptor32, + int nonce_parity +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Extracts a secret adaptor from a FROST pre-signature and corresponding + * signature + * + * This function will not fail unless given grossly invalid data; if it is + * merely given signatures that do not verify, the returned value will be + * nonsense. It is therefore important that all data be verified at earlier + * steps of any protocol that uses this function. In particular, this includes + * verifying all partial signatures that were aggregated into pre_sig64. + * + * Returns: 0 if the arguments are NULL, or sig64 or pre_sig64 contain + * grossly invalid (overflowing) values. 1 otherwise (which does NOT + * mean the signatures or the adaptor are valid!) + * Args: ctx: pointer to a context object + * Out:sec_adaptor32: 32-byte secret adaptor + * In: sig64: complete, valid 64-byte signature + * pre_sig64: the pre-signature corresponding to sig64, i.e., the + * aggregate of partial signatures without the secret + * adaptor + * nonce_parity: the output of `frost_nonce_parity` called with the + * session used for producing sig64 + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_extract_adaptor( + const secp256k1_context *ctx, + unsigned char *sec_adaptor32, + const unsigned char *sig64, + const unsigned char *pre_sig64, + int nonce_parity +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + #ifdef __cplusplus } #endif diff --git a/src/modules/frost/Makefile.am.include b/src/modules/frost/Makefile.am.include index 7e44e6ea..f19f854f 100644 --- a/src/modules/frost/Makefile.am.include +++ b/src/modules/frost/Makefile.am.include @@ -4,3 +4,4 @@ noinst_HEADERS += src/modules/frost/keygen.h noinst_HEADERS += src/modules/frost/keygen_impl.h noinst_HEADERS += src/modules/frost/session.h noinst_HEADERS += src/modules/frost/session_impl.h +noinst_HEADERS += src/modules/frost/adaptor_impl.h diff --git a/src/modules/frost/adaptor_impl.h b/src/modules/frost/adaptor_impl.h new file mode 100644 index 00000000..8860c43f --- /dev/null +++ b/src/modules/frost/adaptor_impl.h @@ -0,0 +1,168 @@ +/*********************************************************************** + * Copyright (c) 2022-2023 Jesse Posner * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_FROST_ADAPTOR_IMPL_H +#define SECP256K1_MODULE_FROST_ADAPTOR_IMPL_H + +#include + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_frost.h" + +#include "session.h" +#include "../../scalar.h" + +int secp256k1_frost_nonce_parity(const secp256k1_context* ctx, int *nonce_parity, const secp256k1_frost_session *session) { + secp256k1_frost_session_internal session_i; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(nonce_parity != NULL); + ARG_CHECK(session != NULL); + + if (!secp256k1_frost_session_load(ctx, &session_i, session)) { + return 0; + } + *nonce_parity = session_i.fin_nonce_parity; + return 1; +} + +int secp256k1_frost_verify_adaptor(const secp256k1_context* ctx, const unsigned char *pre_sig64, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey, const secp256k1_pubkey *adaptor, int nonce_parity) { + secp256k1_scalar s; + secp256k1_scalar e; + secp256k1_gej rj; + secp256k1_ge pk; + secp256k1_gej pkj; + secp256k1_ge r; + unsigned char buf[32]; + int overflow; + secp256k1_ge adaptorp; + secp256k1_xonly_pubkey noncepk; + secp256k1_gej fin_nonce_ptj; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pre_sig64 != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(adaptor != NULL); + ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); + + if (!secp256k1_xonly_pubkey_parse(ctx, &noncepk, &pre_sig64[0])) { + return 0; + } + if (!secp256k1_xonly_pubkey_load(ctx, &r, &noncepk)) { + return 0; + } + if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) { + return 0; + } + if (!nonce_parity) { + secp256k1_ge_neg(&adaptorp, &adaptorp); + } + secp256k1_gej_set_ge(&fin_nonce_ptj, &adaptorp); + secp256k1_gej_add_ge_var(&fin_nonce_ptj, &fin_nonce_ptj, &r, NULL); + if (secp256k1_gej_is_infinity(&fin_nonce_ptj)) { + /* unreachable with overwhelming probability */ + return 0; + } + + secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); + if (overflow) { + return 0; + } + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) { + return 0; + } + + /* Compute e. */ + secp256k1_fe_get_b32(buf, &pk.x); + secp256k1_schnorrsig_challenge(&e, &pre_sig64[0], msg32, 32, buf); + + /* Compute rj = s*G + (-e)*pkj */ + secp256k1_scalar_negate(&e, &e); + secp256k1_gej_set_ge(&pkj, &pk); + secp256k1_ecmult(&rj, &pkj, &e, &s); + + /* secp256k1_ge_set_gej_var(&r, &rj); */ + if (secp256k1_gej_is_infinity(&rj)) { + return 0; + } + + secp256k1_gej_neg(&rj, &rj); + secp256k1_gej_add_var(&rj, &rj, &fin_nonce_ptj, NULL); + return secp256k1_gej_is_infinity(&rj); +} + +int secp256k1_frost_adapt(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *pre_sig64, const unsigned char *sec_adaptor32, int nonce_parity) { + secp256k1_scalar s; + secp256k1_scalar t; + int overflow; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(pre_sig64 != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); + + secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow); + ret &= !overflow; + + /* Determine if the secret adaptor should be negated. + * + * The frost_session stores the X-coordinate and the parity of the "final nonce" + * (r + t)*G, where r*G is the aggregate public nonce and t is the secret adaptor. + * + * Since a BIP340 signature requires an x-only public nonce, in the case where + * (r + t)*G has odd Y-coordinate (i.e. nonce_parity == 1), the x-only public nonce + * corresponding to the signature is actually (-r - t)*G. Thus adapting a + * pre-signature requires negating t in this case. + */ + if (nonce_parity) { + secp256k1_scalar_negate(&t, &t); + } + + secp256k1_scalar_add(&s, &s, &t); + secp256k1_scalar_get_b32(&sig64[32], &s); + memmove(sig64, pre_sig64, 32); + secp256k1_scalar_clear(&t); + return ret; +} + +int secp256k1_frost_extract_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const unsigned char *pre_sig64, int nonce_parity) { + secp256k1_scalar t; + secp256k1_scalar s; + int overflow; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(pre_sig64 != NULL); + ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); + + secp256k1_scalar_set_b32(&t, &sig64[32], &overflow); + ret &= !overflow; + secp256k1_scalar_negate(&t, &t); + + secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_add(&t, &t, &s); + + if (!nonce_parity) { + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_get_b32(sec_adaptor32, &t); + secp256k1_scalar_clear(&t); + return ret; +} + +#endif diff --git a/src/modules/frost/keygen.h b/src/modules/frost/keygen.h index 285a8a86..79834bbf 100644 --- a/src/modules/frost/keygen.h +++ b/src/modules/frost/keygen.h @@ -19,6 +19,8 @@ typedef struct { int parity_acc; } secp256k1_tweak_cache_internal; +static int secp256k1_tweak_cache_load(const secp256k1_context* ctx, secp256k1_tweak_cache_internal *cache_i, const secp256k1_frost_tweak_cache *cache); + static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share); #endif diff --git a/src/modules/frost/main_impl.h b/src/modules/frost/main_impl.h index 1328bbf4..d0ca4889 100644 --- a/src/modules/frost/main_impl.h +++ b/src/modules/frost/main_impl.h @@ -9,5 +9,6 @@ #include "keygen_impl.h" #include "session_impl.h" +#include "adaptor_impl.h" #endif diff --git a/src/modules/frost/session.h b/src/modules/frost/session.h index b1e50a1a..e07f9d30 100644 --- a/src/modules/frost/session.h +++ b/src/modules/frost/session.h @@ -7,4 +7,19 @@ #ifndef SECP256K1_MODULE_FROST_SESSION_H #define SECP256K1_MODULE_FROST_SESSION_H +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_frost.h" + +#include "../../scalar.h" + +typedef struct { + int fin_nonce_parity; + unsigned char fin_nonce[32]; + secp256k1_scalar noncecoef; + secp256k1_scalar challenge; + secp256k1_scalar s_part; +} secp256k1_frost_session_internal; + +static int secp256k1_frost_session_load(const secp256k1_context* ctx, secp256k1_frost_session_internal *session_i, const secp256k1_frost_session *session); + #endif diff --git a/src/modules/frost/session_impl.h b/src/modules/frost/session_impl.h index 6c707ddd..5a2aa656 100644 --- a/src/modules/frost/session_impl.h +++ b/src/modules/frost/session_impl.h @@ -74,6 +74,49 @@ static int secp256k1_frost_pubnonce_load(const secp256k1_context* ctx, secp256k1 return 1; } +static const unsigned char secp256k1_frost_session_cache_magic[4] = { 0x5c, 0x11, 0xa8, 0x3 }; + +/* A session consists of + * - 4 byte session cache magic + * - 1 byte the parity of the final nonce + * - 32 byte serialized x-only final nonce + * - 32 byte nonce coefficient b + * - 32 byte signature challenge hash e + * - 32 byte scalar s that is added to the partial signatures of the signers + */ +static void secp256k1_frost_session_save(secp256k1_frost_session *session, const secp256k1_frost_session_internal *session_i) { + unsigned char *ptr = session->data; + + memcpy(ptr, secp256k1_frost_session_cache_magic, 4); + ptr += 4; + *ptr = session_i->fin_nonce_parity; + ptr += 1; + memcpy(ptr, session_i->fin_nonce, 32); + ptr += 32; + secp256k1_scalar_get_b32(ptr, &session_i->noncecoef); + ptr += 32; + secp256k1_scalar_get_b32(ptr, &session_i->challenge); + ptr += 32; + secp256k1_scalar_get_b32(ptr, &session_i->s_part); +} + +static int secp256k1_frost_session_load(const secp256k1_context* ctx, secp256k1_frost_session_internal *session_i, const secp256k1_frost_session *session) { + const unsigned char *ptr = session->data; + + ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_frost_session_cache_magic, 4) == 0); + ptr += 4; + session_i->fin_nonce_parity = *ptr; + ptr += 1; + memcpy(session_i->fin_nonce, ptr, 32); + ptr += 32; + secp256k1_scalar_set_b32(&session_i->noncecoef, ptr, NULL); + ptr += 32; + secp256k1_scalar_set_b32(&session_i->challenge, ptr, NULL); + ptr += 32; + secp256k1_scalar_set_b32(&session_i->s_part, ptr, NULL); + return 1; +} + int secp256k1_frost_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_frost_pubnonce* nonce) { secp256k1_ge ge[2]; int i; @@ -230,4 +273,192 @@ int secp256k1_frost_nonce_gen(const secp256k1_context* ctx, secp256k1_frost_secn return ret; } +static int secp256k1_frost_sum_nonces(const secp256k1_context* ctx, secp256k1_gej *summed_nonces, const secp256k1_frost_pubnonce * const* pubnonces, size_t n_pubnonces) { + size_t i; + int j; + + secp256k1_gej_set_infinity(&summed_nonces[0]); + secp256k1_gej_set_infinity(&summed_nonces[1]); + + for (i = 0; i < n_pubnonces; i++) { + secp256k1_ge nonce_pt[2]; + if (!secp256k1_frost_pubnonce_load(ctx, nonce_pt, pubnonces[i])) { + return 0; + } + for (j = 0; j < 2; j++) { + secp256k1_gej_add_ge_var(&summed_nonces[j], &summed_nonces[j], &nonce_pt[j], NULL); + } + } + return 1; +} + +/* TODO: consider updating to frost-08 to address maleability at the cost of performance */ +/* See https://github.com/cfrg/draft-irtf-cfrg-frost/pull/217 */ +static int secp256k1_frost_compute_noncehash(const secp256k1_context* ctx, unsigned char *noncehash, const unsigned char *msg, const secp256k1_frost_pubnonce * const* pubnonces, size_t n_pubnonces, const unsigned char *pk32, const size_t *ids) { + unsigned char buf[66]; + secp256k1_sha256 sha; + size_t i; + + secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/noncecoef", sizeof("FROST/noncecoef") - 1); + for (i = 0; i < n_pubnonces; i++) { + secp256k1_scalar idx; + + secp256k1_scalar_set_int(&idx, ids[i]); + secp256k1_scalar_get_b32(buf, &idx); + secp256k1_sha256_write(&sha, buf, 32); + if (!secp256k1_frost_pubnonce_serialize(ctx, buf, pubnonces[i])) { + return 0; + } + secp256k1_sha256_write(&sha, buf, sizeof(buf)); + } + secp256k1_sha256_write(&sha, pk32, 32); + secp256k1_sha256_write(&sha, msg, 32); + secp256k1_sha256_finalize(&sha, noncehash); + return 1; +} + +static int secp256k1_frost_nonce_process_internal(const secp256k1_context* ctx, int *fin_nonce_parity, unsigned char *fin_nonce, secp256k1_scalar *b, secp256k1_gej *aggnoncej, const unsigned char *msg, const secp256k1_frost_pubnonce * const* pubnonces, size_t n_pubnonces, const unsigned char *pk32, const size_t *ids) { + unsigned char noncehash[32]; + secp256k1_ge fin_nonce_pt; + secp256k1_gej fin_nonce_ptj; + secp256k1_ge aggnonce[2]; + + secp256k1_ge_set_gej(&aggnonce[0], &aggnoncej[0]); + secp256k1_ge_set_gej(&aggnonce[1], &aggnoncej[1]); + if (!secp256k1_frost_compute_noncehash(ctx, noncehash, msg, pubnonces, n_pubnonces, pk32, ids)) { + return 0; + } + /* fin_nonce = aggnonce[0] + b*aggnonce[1] */ + secp256k1_scalar_set_b32(b, noncehash, NULL); + secp256k1_ecmult(&fin_nonce_ptj, &aggnoncej[1], b, NULL); + secp256k1_gej_add_ge_var(&fin_nonce_ptj, &fin_nonce_ptj, &aggnonce[0], NULL); + secp256k1_ge_set_gej(&fin_nonce_pt, &fin_nonce_ptj); + + if (secp256k1_ge_is_infinity(&fin_nonce_pt)) { + /* unreachable with overwhelming probability */ + return 0; + } + secp256k1_fe_normalize_var(&fin_nonce_pt.x); + secp256k1_fe_get_b32(fin_nonce, &fin_nonce_pt.x); + + secp256k1_fe_normalize_var(&fin_nonce_pt.y); + *fin_nonce_parity = secp256k1_fe_is_odd(&fin_nonce_pt.y); + return 1; +} + +static int secp256k1_frost_lagrange_coefficient(secp256k1_scalar *r, const size_t *ids, size_t n_participants, size_t my_id) { + size_t i; + secp256k1_scalar num; + secp256k1_scalar den; + secp256k1_scalar party_idx; + + secp256k1_scalar_set_int(&num, 1); + secp256k1_scalar_set_int(&den, 1); + secp256k1_scalar_set_int(&party_idx, my_id); + for (i = 0; i < n_participants; i++) { + secp256k1_scalar mul; + + secp256k1_scalar_set_int(&mul, ids[i]); + if (secp256k1_scalar_eq(&mul, &party_idx)) { + continue; + } + + secp256k1_scalar_negate(&mul, &mul); + secp256k1_scalar_mul(&num, &num, &mul); + secp256k1_scalar_add(&mul, &mul, &party_idx); + secp256k1_scalar_mul(&den, &den, &mul); + } + + secp256k1_scalar_inverse_var(&den, &den); + secp256k1_scalar_mul(r, &num, &den); + + return 1; +} + +int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_session *session, const secp256k1_frost_pubnonce * const* pubnonces, size_t n_pubnonces, const unsigned char *msg32, const secp256k1_xonly_pubkey *pk, size_t my_id, const size_t *ids, const secp256k1_frost_tweak_cache *tweak_cache, const secp256k1_pubkey *adaptor) { + secp256k1_ge aggnonce_pt[2]; + secp256k1_gej aggnonce_ptj[2]; + unsigned char fin_nonce[32]; + secp256k1_frost_session_internal session_i = { 0 }; + unsigned char pk32[32]; + int i; + secp256k1_scalar l; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pubnonces != NULL); + ARG_CHECK(ids != NULL); + ARG_CHECK(n_pubnonces > 1); + + if (!secp256k1_xonly_pubkey_serialize(ctx, pk32, pk)) { + return 0; + } + + if (!secp256k1_frost_sum_nonces(ctx, aggnonce_ptj, pubnonces, n_pubnonces)) { + return 0; + } + for (i = 0; i < 2; i++) { + if (secp256k1_gej_is_infinity(&aggnonce_ptj[i])) { + /* There must be at least one dishonest signer. If we would return 0 + here, we will never be able to determine who it is. Therefore, we + should continue such that the culprit is revealed when collecting + and verifying partial signatures. + + However, dealing with the point at infinity (loading, + de-/serializing) would require a lot of extra code complexity. + Instead, we set the aggregate nonce to some arbitrary point (the + generator). This is secure, because it only restricts the + abilities of the attacker: an attacker that forces the sum of + nonces to be infinity by sending some maliciously generated nonce + pairs can be turned into an attacker that forces the sum to be + the generator (by simply adding the generator to one of the + malicious nonces), and this does not change the winning condition + of the EUF-CMA game. */ + aggnonce_pt[i] = secp256k1_ge_const_g; + } else { + secp256k1_ge_set_gej(&aggnonce_pt[i], &aggnonce_ptj[i]); + } + } + /* Add public adaptor to nonce */ + if (adaptor != NULL) { + secp256k1_ge adaptorp; + if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) { + return 0; + } + secp256k1_gej_add_ge_var(&aggnonce_ptj[0], &aggnonce_ptj[0], &adaptorp, NULL); + } + if (!secp256k1_frost_nonce_process_internal(ctx, &session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_ptj, msg32, pubnonces, n_pubnonces, pk32, ids)) { + return 0; + } + + secp256k1_schnorrsig_challenge(&session_i.challenge, fin_nonce, msg32, 32, pk32); + + /* If there is a tweak then set `challenge` times `tweak` to the `s`-part.*/ + secp256k1_scalar_set_int(&session_i.s_part, 0); + if (tweak_cache != NULL) { + secp256k1_tweak_cache_internal cache_i; + if (!secp256k1_tweak_cache_load(ctx, &cache_i, tweak_cache)) { + return 0; + } + if (!secp256k1_scalar_is_zero(&cache_i.tweak)) { + secp256k1_scalar e_tmp; + secp256k1_scalar_mul(&e_tmp, &session_i.challenge, &cache_i.tweak); + if (secp256k1_fe_is_odd(&cache_i.pk.y)) { + secp256k1_scalar_negate(&e_tmp, &e_tmp); + } + secp256k1_scalar_add(&session_i.s_part, &session_i.s_part, &e_tmp); + } + } + /* Update the challenge by multiplying the Lagrange coefficient to prepare + * for signing. */ + if (!secp256k1_frost_lagrange_coefficient(&l, ids, n_pubnonces, my_id)) { + return 0; + } + secp256k1_scalar_mul(&session_i.challenge, &session_i.challenge, &l); + memcpy(session_i.fin_nonce, fin_nonce, sizeof(session_i.fin_nonce)); + secp256k1_frost_session_save(session, &session_i); + return 1; +} + #endif