frost trusted dealer: nonce aggregation and adaptor signatures
This commit adds nonce aggregation, as well as adaptor signatures.
This commit is contained in:
parent
5368c81a3c
commit
ea059393f0
@ -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
|
||||
|
@ -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
|
||||
|
168
src/modules/frost/adaptor_impl.h
Normal file
168
src/modules/frost/adaptor_impl.h
Normal file
@ -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 <string.h>
|
||||
|
||||
#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
|
@ -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
|
||||
|
@ -9,5 +9,6 @@
|
||||
|
||||
#include "keygen_impl.h"
|
||||
#include "session_impl.h"
|
||||
#include "adaptor_impl.h"
|
||||
|
||||
#endif
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user