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

FROST Trusted Dealer #278

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
113 changes: 113 additions & 0 deletions include/secp256k1_frost.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ extern "C" {
*
* The module also supports BIP-341 ("Taproot") and BIP-32 ("ordinary") public
* key tweaking.
*
* Following the convention used in the MuSig module, the API uses the singular
* term "nonce" to refer to the two "nonces" used by the FROST scheme.
*/

/** Opaque data structures
Expand Down Expand Up @@ -46,6 +49,61 @@ typedef struct {
unsigned char data[36];
} secp256k1_frost_share;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming nit: maybe call it secshare (both for the type and its instances and (de)ser function names in the API), in order to underline that this should be kept in secret? also since the public counterpart is called pubshare.


/** Opaque data structure that holds a signer's _secret_ nonce.
*
* Guaranteed to be 68 bytes in size.
*
* WARNING: This structure MUST NOT be copied or read or written to directly.
* A signer who is online throughout the whole process and can keep this
* structure in memory can use the provided API functions for a safe standard
* workflow. See
* https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ for
* more details about the risks associated with serializing or deserializing
* this structure.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering that this blog post link has been removed in the MuSig2 PR in the secp256k1 main repo (see bitcoin-core/secp256k1#1479 (comment)), I guess the same should be done here.

*
* We repeat, copying this data structure can result in nonce reuse which will
* leak the secret signing key.
*/
typedef struct {
unsigned char data[68];
} secp256k1_frost_secnonce;

/** Opaque data structure that holds a signer's public nonce.
*
* Guaranteed to be 132 bytes in size. It can be safely copied/moved.
* Serialized and parsed with `frost_pubnonce_serialize` and
* `frost_pubnonce_parse`.
*/
typedef struct {
unsigned char data[132];
} secp256k1_frost_pubnonce;

/** Parse a signer's public nonce.
*
* Returns: 1 when the nonce could be parsed, 0 otherwise.
* Args: ctx: pointer to a context object
* Out: nonce: pointer to a nonce object
* In: in66: pointer to the 66-byte nonce to be parsed
*/
SECP256K1_API int secp256k1_frost_pubnonce_parse(
const secp256k1_context *ctx,
secp256k1_frost_pubnonce *nonce,
const unsigned char *in66
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Serialize a signer's public nonce
*
* Returns: 1 when the nonce could be serialized, 0 otherwise
* Args: ctx: pointer to a context object
* Out: out66: pointer to a 66-byte array to store the serialized nonce
* In: nonce: pointer to the nonce
*/
SECP256K1_API int secp256k1_frost_pubnonce_serialize(
const secp256k1_context *ctx,
unsigned char *out66,
const secp256k1_frost_pubnonce *nonce
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Serialize a FROST share
*
* Returns: 1 when the share could be serialized, 0 otherwise
Expand Down Expand Up @@ -271,6 +329,61 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_xonly_twea
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Starts a signing session by generating a nonce
*
* This function outputs a secret nonce that will be required for signing and a
* corresponding public nonce that is intended to be sent to other signers.
*
* FROST, like MuSig, differs from regular Schnorr signing in that
* implementers _must_ take special care to not reuse a nonce. This can be
* ensured by following these rules:
*
* 1. Each call to this function must have a UNIQUE session_id32 that must NOT BE
* REUSED in subsequent calls to this function.
* If you do not provide a seckey, session_id32 _must_ be UNIFORMLY RANDOM
* AND KEPT SECRET (even from other signers). If you do provide a seckey,
* session_id32 can instead be a counter (that must never repeat!). However,
* it is recommended to always choose session_id32 uniformly at random.
* 2. If you already know the seckey, message or group public key, they
* can be optionally provided to derive the nonce and increase
* misuse-resistance. The extra_input32 argument can be used to provide
* additional data that does not repeat in normal scenarios, such as the
* current time.
* 3. Avoid copying (or serializing) the secnonce. This reduces the possibility
* that it is used more than once for signing.
*
* Remember that nonce reuse will leak the secret share!
* Note that using the same agg_share for multiple FROST sessions is fine.
*
* Returns: 0 if the arguments are invalid and 1 otherwise
* Args: ctx: pointer to a context object (not secp256k1_context_static)
* Out: secnonce: pointer to a structure to store the secret nonce
* pubnonce: pointer to a structure to store the public nonce
* In: session_id32: a 32-byte session_id32 as explained above. Must be
* unique to this call to secp256k1_frost_nonce_gen and
* must be uniformly random unless you really know what you
* are doing.
* agg_share: the aggregated share that will later be used for
* signing, if already known (can be NULL)
* msg32: the 32-byte message that will later be signed, if
* already known (can be NULL)
* keygen_cache: pointer to the keygen_cache that was used to create the group
* (and potentially tweaked) public key if already known
* (can be NULL)
* extra_input32: an optional 32-byte array that is input to the nonce
* derivation function (can be NULL)
*/
SECP256K1_API int secp256k1_frost_nonce_gen(
const secp256k1_context *ctx,
secp256k1_frost_secnonce *secnonce,
secp256k1_frost_pubnonce *pubnonce,
const unsigned char *session_id32,
const secp256k1_frost_share *agg_share,
const unsigned char *msg32,
const secp256k1_frost_keygen_cache *keygen_cache,
const unsigned char *extra_input32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also add pubshare as an input argument. MuSig2 hashes the individual pubkey when generating the nonces, but we don't need to make pubshare a mandatory argument like MuSig2 does (more info).

How do we decide the args supplied to the hash function for generating a nonce? Should we throw in all available signer information into the hash function? For instance, can we include the participant identifier too?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related issue: siv2r/bip-frost-signing#15, which suggests using tweak_cache instead of agg_pk as the input arg.


#ifdef __cplusplus
}
#endif
Expand Down
10 changes: 9 additions & 1 deletion src/group.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,20 @@ static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b);

/** Convert a group element that is not infinity to a 64-byte array. The output
* array is platform-dependent. */
static void secp256k1_ge_to_bytes(unsigned char *buf, secp256k1_ge *a);
static void secp256k1_ge_to_bytes(unsigned char *buf, const secp256k1_ge *a);

/** Convert a 64-byte array into group element. This function assumes that the
* provided buffer correctly encodes a group element. */
static void secp256k1_ge_from_bytes(secp256k1_ge *r, const unsigned char *buf);

/** Convert a group element (that is allowed to be infinity) to a 64-byte
* array. The output array is platform-dependent. */
static void secp256k1_ge_to_bytes_ext(unsigned char *data, const secp256k1_ge *ge);

/** Convert a 64-byte array into a group element. This function assumes that the
* provided buffer is the output of secp256k1_ge_to_bytes_ext. */
static void secp256k1_ge_from_bytes_ext(secp256k1_ge *ge, const unsigned char *data);

/** Determine if a point (which is assumed to be on the curve) is in the correct (sub)group of the curve.
*
* In normal mode, the used group is secp256k1, which has cofactor=1 meaning that every point on the curve is in the
Expand Down
19 changes: 18 additions & 1 deletion src/group_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,7 @@ static int secp256k1_ge_x_frac_on_curve_var(const secp256k1_fe *xn, const secp25
return secp256k1_fe_is_square_var(&r);
}

static void secp256k1_ge_to_bytes(unsigned char *buf, secp256k1_ge *a) {
static void secp256k1_ge_to_bytes(unsigned char *buf, const secp256k1_ge *a) {
secp256k1_ge_storage s;

/* We require that the secp256k1_ge_storage type is exactly 64 bytes.
Expand All @@ -985,4 +985,21 @@ static void secp256k1_ge_from_bytes(secp256k1_ge *r, const unsigned char *buf) {
secp256k1_ge_from_storage(r, &s);
}

static void secp256k1_ge_to_bytes_ext(unsigned char *data, const secp256k1_ge *ge) {
if (secp256k1_ge_is_infinity(ge)) {
memset(data, 0, 64);
} else {
secp256k1_ge_to_bytes(data, ge);
}
}

static void secp256k1_ge_from_bytes_ext(secp256k1_ge *ge, const unsigned char *data) {
unsigned char zeros[64] = { 0 };
if (secp256k1_memcmp_var(data, zeros, sizeof(zeros)) == 0) {
secp256k1_ge_set_infinity(ge);
} else {
secp256k1_ge_from_bytes(ge, data);
}
}

#endif /* SECP256K1_GROUP_IMPL_H */
2 changes: 2 additions & 0 deletions src/modules/frost/Makefile.am.include
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ include_HEADERS += include/secp256k1_frost.h
noinst_HEADERS += src/modules/frost/main_impl.h
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
4 changes: 4 additions & 0 deletions src/modules/frost/keygen.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ typedef struct {
int parity_acc;
} secp256k1_keygen_cache_internal;

static int secp256k1_keygen_cache_load(const secp256k1_context* ctx, secp256k1_keygen_cache_internal *cache_i, const secp256k1_frost_keygen_cache *cache);

static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share);

#endif
1 change: 1 addition & 0 deletions src/modules/frost/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
#define SECP256K1_MODULE_FROST_MAIN

#include "keygen_impl.h"
#include "session_impl.h"

#endif
13 changes: 13 additions & 0 deletions src/modules/frost/session.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**********************************************************************
* Copyright (c) 2021-2024 Jesse Posner *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/

#ifndef SECP256K1_MODULE_FROST_SESSION_H
#define SECP256K1_MODULE_FROST_SESSION_H

#include "../../../include/secp256k1.h"
#include "../../../include/secp256k1_frost.h"

#endif
Loading