BIP 445's det_nonce_hash commits to the secret share, my_id, u, the
sorted ids, the aggothernonce, the x-only tweaked threshold public key
and the message. It does not commit to the pubshares, to the untweaked
threshold public key, or to the accumulated tweaks. Because Q and -Q
share an x-coordinate, two tweak caches can agree on everything the
derivation hashes and still disagree on the sign g*gacc that multiplies
the secret share -- a cache initialized from thresh_pk and one
initialized from its negation being the smallest example.
Two calls differing only in that emit the same pubnonce and partial
signatures s = k + e*lambda*d and s' = k - e*lambda*d over the identical
k = k1 + b*k2, so subtracting them yields the secret share. Demonstrated
on a sole signer (u = 1, ids = {0}, pubshares = NULL) with thresh_sk =
0x11.. and msg = 0x42..:
tweaked pk (cache A) 4f355bdc...075871aa
tweaked pk (cache B) 4f355bdc...075871aa same x-only key
pubnonce A == pubnonce B nonce reused
sA - sB 0d7d9c4e...aa748ffa
-2*e*d 0d7d9c4e...aa748ffa d recovered
Nothing inside a single call can catch this. The self-verification that
Sign performs passes in both cases, because each partial signature is
individually valid under the cache it was produced with;
validate_session_params likewise only ties the pubshares to the cache's
own Q0, which both caches satisfy by construction. Note also that
pubshares is optional, so there need not be a second value to disagree
with.
No code change: this is the specified derivation, and committing to Q0
or to gacc here would diverge from BIP 445 and invalidate the
det_sign test vectors. The obligation is the caller's, so state it where
the caller will meet it -- in the function's own documentation and
alongside the existing secnonce and session_secrand32 rules in frost.md.
The rule is that the tweak cache and the pubshares are fixed key
material settled at key generation, never per-session parameters taken
from a coordinator or a peer; under that discipline a repeated call is
byte-identical and harmless, which is the point of a deterministic
nonce.
Worth raising against the BIP: the spec could close this by hashing the
untweaked threshold public key, at the cost of new test vectors.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
645 lines
29 KiB
C
645 lines
29 KiB
C
#ifndef SECP256K1_FROST_H
|
|
#define SECP256K1_FROST_H
|
|
|
|
#include "secp256k1.h"
|
|
#include "secp256k1_extrakeys.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
/** This module implements BIP 445 "FROST Signing Protocol for BIP340
|
|
* Signatures" (https://github.com/siv2r/bip-frost-signing), a FROST3-based
|
|
* threshold Schnorr signature scheme.
|
|
*
|
|
* This code is currently a work in progress. It's not secure nor stable.
|
|
* IT IS EXTREMELY DANGEROUS AND RECKLESS TO USE THIS MODULE IN PRODUCTION!
|
|
*
|
|
* Since distributed key generation is out of scope for BIP 445, this module
|
|
* provides a trusted dealer for key generation
|
|
* (secp256k1_frost_trusted_dealer_keygen). Participants are identified by
|
|
* uint32 identifiers 0..n-1 (participant id i sits at polynomial x-coordinate
|
|
* i+1). The total number of participants n must not exceed
|
|
* SECP256K1_FROST_MAX_PARTICIPANTS.
|
|
*
|
|
* A signing session involves u signers (threshold <= u <= n) and,
|
|
* optionally, a coordinator:
|
|
* 1. Every signer runs secp256k1_frost_nonce_gen and sends the pubnonce to
|
|
* the coordinator.
|
|
* 2. The coordinator aggregates the pubnonces with
|
|
* secp256k1_frost_nonce_agg and sends the aggnonce to the signers.
|
|
* 3. Everyone runs secp256k1_frost_session_init on the same session
|
|
* parameters (aggnonce, signer ids, tweak cache, message).
|
|
* 4. Every signer runs secp256k1_frost_sign and sends the partial signature
|
|
* to the coordinator.
|
|
* 5. The coordinator verifies partial signatures with
|
|
* secp256k1_frost_partial_sig_verify and aggregates them with
|
|
* secp256k1_frost_partial_sig_agg.
|
|
*
|
|
* It is recommended to read the documentation in this include file carefully.
|
|
* Further notes on API usage can be found in src/modules/frost/frost.md.
|
|
*/
|
|
|
|
/** The maximum number of participants n in a FROST setup. See BIP 445 for the
|
|
* security rationale behind this bound. */
|
|
#define SECP256K1_FROST_MAX_PARTICIPANTS 128
|
|
|
|
/** Opaque data structures
|
|
*
|
|
* The exact representation of data inside the opaque data structures is
|
|
* implementation defined and not guaranteed to be portable between different
|
|
* platforms or versions. With the exception of `secp256k1_frost_secnonce`,
|
|
* the data structures can be safely copied/moved. If you need to convert to
|
|
* a format suitable for storage, transmission, or comparison, use the
|
|
* corresponding serialization and parsing functions.
|
|
*/
|
|
|
|
/** 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.
|
|
*
|
|
* Copying this data structure can result in nonce reuse which will leak the
|
|
* signer's secret share.
|
|
*/
|
|
typedef struct secp256k1_frost_secnonce {
|
|
unsigned char data[68];
|
|
} secp256k1_frost_secnonce;
|
|
|
|
/** Opaque data structure that holds a signer's public nonce.
|
|
*
|
|
* Guaranteed to be 132 bytes in size. Serialized and parsed with
|
|
* `frost_pubnonce_serialize` and `frost_pubnonce_parse`.
|
|
*/
|
|
typedef struct secp256k1_frost_pubnonce {
|
|
unsigned char data[132];
|
|
} secp256k1_frost_pubnonce;
|
|
|
|
/** Opaque data structure that holds an aggregate public nonce.
|
|
*
|
|
* Guaranteed to be 132 bytes in size. Serialized and parsed with
|
|
* `frost_aggnonce_serialize` and `frost_aggnonce_parse`.
|
|
*/
|
|
typedef struct secp256k1_frost_aggnonce {
|
|
unsigned char data[132];
|
|
} secp256k1_frost_aggnonce;
|
|
|
|
/** Opaque data structure that caches the threshold public key and the state
|
|
* of public key tweaking.
|
|
*
|
|
* Initialized with `frost_tweak_cache_init` from the (untweaked) threshold
|
|
* public key and required for `frost_session_init`. Tweaks are applied to
|
|
* the cache with `frost_pubkey_xonly_tweak_add` and
|
|
* `frost_pubkey_ec_tweak_add`.
|
|
*
|
|
* Guaranteed to be 165 bytes in size. No serialization and parsing functions
|
|
* (yet).
|
|
*/
|
|
typedef struct secp256k1_frost_tweak_cache {
|
|
unsigned char data[165];
|
|
} secp256k1_frost_tweak_cache;
|
|
|
|
/** Opaque data structure that holds a FROST signing session.
|
|
*
|
|
* The session is signer-agnostic: the same session object can be used by a
|
|
* coordinator to verify the partial signatures of all signers. This
|
|
* structure is not required to be kept secret for the signing protocol to
|
|
* be secure. Guaranteed to be 137 bytes in size. No serialization and
|
|
* parsing functions (yet).
|
|
*/
|
|
typedef struct secp256k1_frost_session {
|
|
unsigned char data[137];
|
|
} secp256k1_frost_session;
|
|
|
|
/** Opaque data structure that holds a partial FROST signature.
|
|
*
|
|
* Guaranteed to be 36 bytes in size. Serialized and parsed with
|
|
* `frost_partial_sig_serialize` and `frost_partial_sig_parse`.
|
|
*/
|
|
typedef struct secp256k1_frost_partial_sig {
|
|
unsigned char data[36];
|
|
} secp256k1_frost_partial_sig;
|
|
|
|
/** 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 SECP256K1_WARN_UNUSED_RESULT 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 always
|
|
* 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);
|
|
|
|
/** Parse an aggregate public nonce.
|
|
*
|
|
* In contrast to `frost_pubnonce_parse`, this function accepts the point at
|
|
* infinity (encoded as 33 zero bytes) for either nonce component, as
|
|
* specified by BIP 445 NonceAgg.
|
|
*
|
|
* 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 SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_aggnonce_parse(
|
|
const secp256k1_context *ctx,
|
|
secp256k1_frost_aggnonce *nonce,
|
|
const unsigned char *in66
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
|
|
|
/** Serialize an aggregate public nonce
|
|
*
|
|
* A nonce component that is the point at infinity is encoded as 33 zero
|
|
* bytes, as specified by BIP 445.
|
|
*
|
|
* Returns: 1 always
|
|
* 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_aggnonce_serialize(
|
|
const secp256k1_context *ctx,
|
|
unsigned char *out66,
|
|
const secp256k1_frost_aggnonce *nonce
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
|
|
|
/** Parse a FROST partial signature.
|
|
*
|
|
* Returns: 1 when the signature could be parsed, 0 otherwise.
|
|
* Args: ctx: pointer to a context object
|
|
* Out: sig: pointer to a signature object
|
|
* In: in32: pointer to the 32-byte signature to be parsed
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_partial_sig_parse(
|
|
const secp256k1_context *ctx,
|
|
secp256k1_frost_partial_sig *sig,
|
|
const unsigned char *in32
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
|
|
|
/** Serialize a FROST partial signature
|
|
*
|
|
* Returns: 1 always
|
|
* Args: ctx: pointer to a context object
|
|
* Out: out32: pointer to a 32-byte array to store the serialized signature
|
|
* In: sig: pointer to the signature
|
|
*/
|
|
SECP256K1_API int secp256k1_frost_partial_sig_serialize(
|
|
const secp256k1_context *ctx,
|
|
unsigned char *out32,
|
|
const secp256k1_frost_partial_sig *sig
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
|
|
|
/** Generate threshold key material with a trusted dealer.
|
|
*
|
|
* Implements the trusted dealer key generation of BIP 445: from the given
|
|
* threshold secret key, the dealer derives the secret share of every
|
|
* participant and the corresponding public shares. The dealer must transmit
|
|
* each secret share to its participant over a secure channel and erase all
|
|
* secret key material afterwards.
|
|
*
|
|
* Returns: 0 if the arguments are invalid, 1 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* Out: secshares32: pointer to an n_participants*32-byte array to store
|
|
* the secret shares; participant with id i receives
|
|
* secshares32[i*32..(i+1)*32]
|
|
* thresh_pk: pointer to a pubkey object to store the threshold
|
|
* public key (full point, parity is meaningful)
|
|
* pubshares: pointer to an array of n_participants pubkey
|
|
* objects to store the public shares; entry i belongs
|
|
* to the participant with id i
|
|
* In: n_participants: total number of participants n. Must be between 1
|
|
* and SECP256K1_FROST_MAX_PARTICIPANTS.
|
|
* threshold: threshold t. Must be between 1 and n_participants.
|
|
* threshold_seckey32: pointer to the 32-byte threshold secret key
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_trusted_dealer_keygen(
|
|
const secp256k1_context *ctx,
|
|
unsigned char *secshares32,
|
|
secp256k1_pubkey *thresh_pk,
|
|
secp256k1_pubkey *pubshares,
|
|
size_t n_participants,
|
|
uint32_t threshold,
|
|
const unsigned char *threshold_seckey32
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7);
|
|
|
|
/** Validate threshold key material.
|
|
*
|
|
* Implements ValidateThresholdInfo of BIP 445: checks that the public shares
|
|
* lie on a single polynomial and that they are consistent with the threshold
|
|
* public key. Note that this validates functional compatibility of the key
|
|
* material; it does NOT validate the security of the key generation that
|
|
* produced it.
|
|
*
|
|
* Returns: 1 if the key material is valid and consistent, 0 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* In: thresh_pk: pointer to the threshold public key
|
|
* pubshares: array of n_participants pubkeys; entry i is the
|
|
* public share of the participant with id i
|
|
* n_participants: total number of participants n
|
|
* threshold: threshold t
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_threshold_info_validate(
|
|
const secp256k1_context *ctx,
|
|
const secp256k1_pubkey *thresh_pk,
|
|
const secp256k1_pubkey *pubshares,
|
|
size_t n_participants,
|
|
uint32_t threshold
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
|
|
|
/** Initialize a tweak cache from the threshold public key.
|
|
*
|
|
* The tweak cache is required for creating a signing session with
|
|
* `frost_session_init`, even if no tweaks are applied.
|
|
*
|
|
* Returns: 0 if the arguments are invalid, 1 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* Out: cache: pointer to the tweak cache to initialize
|
|
* In: thresh_pk: pointer to the (untweaked) threshold public key
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_tweak_cache_init(
|
|
const secp256k1_context *ctx,
|
|
secp256k1_frost_tweak_cache *cache,
|
|
const secp256k1_pubkey *thresh_pk
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
|
|
|
/** Get the current (tweaked) threshold public key from a tweak cache.
|
|
*
|
|
* This is the BIP340 x-only public key that final signatures of sessions
|
|
* created with this cache verify against.
|
|
*
|
|
* Returns: 0 if the arguments are invalid, 1 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* Out: tweaked_pk: pointer to an xonly_pubkey object to store the tweaked
|
|
* threshold public key
|
|
* In: cache: pointer to the tweak cache
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_tweaked_pubkey_get(
|
|
const secp256k1_context *ctx,
|
|
secp256k1_xonly_pubkey *tweaked_pk,
|
|
const secp256k1_frost_tweak_cache *cache
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
|
|
|
/** Apply an x-only tweak to the tweak cache.
|
|
*
|
|
* Implements ApplyTweak of BIP 445 with is_xonly = true, i.e. BIP 341
|
|
* ("Taproot") tweaking: the current public key is negated if it has odd Y
|
|
* before the tweak is applied.
|
|
*
|
|
* Returns: 0 if the arguments are invalid or the tweaked key would be the
|
|
* point at infinity, 1 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* Out: tweaked_pk: pointer to an xonly_pubkey object to store the tweaked
|
|
* threshold public key. If you do not need it, this arg can
|
|
* be NULL.
|
|
* In: cache: pointer to the tweak cache
|
|
* tweak32: pointer to the 32-byte tweak
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_xonly_tweak_add(
|
|
const secp256k1_context *ctx,
|
|
secp256k1_xonly_pubkey *tweaked_pk,
|
|
secp256k1_frost_tweak_cache *cache,
|
|
const unsigned char *tweak32
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
|
|
|
/** Apply a plain tweak to the tweak cache.
|
|
*
|
|
* Implements ApplyTweak of BIP 445 with is_xonly = false, i.e. ordinary
|
|
* (BIP 32-style) tweaking: the current public key is not negated before the
|
|
* tweak is applied.
|
|
*
|
|
* Returns: 0 if the arguments are invalid or the tweaked key would be the
|
|
* point at infinity, 1 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* Out: tweaked_pk: pointer to an xonly_pubkey object to store the tweaked
|
|
* threshold public key. If you do not need it, this arg can
|
|
* be NULL.
|
|
* In: cache: pointer to the tweak cache
|
|
* tweak32: pointer to the 32-byte tweak
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_ec_tweak_add(
|
|
const secp256k1_context *ctx,
|
|
secp256k1_xonly_pubkey *tweaked_pk,
|
|
secp256k1_frost_tweak_cache *cache,
|
|
const unsigned char *tweak32
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
|
|
|
/** Create a FROST nonce (pair) and its public counterpart.
|
|
*
|
|
* Implements NonceGen of BIP 445. This function must only be called once per
|
|
* signing session; never reuse a secnonce for a second signature, as this
|
|
* leaks the secret share. To prevent reuse, this function wipes
|
|
* session_secrand32 before returning, and `frost_sign` wipes the secnonce.
|
|
*
|
|
* Returns: 0 if the arguments are invalid, 1 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* Out: secnonce: pointer to a secnonce object that will be
|
|
* required for `frost_sign`
|
|
* pubnonce: pointer to a pubnonce object to be sent to the
|
|
* coordinator
|
|
* In/Out: session_secrand32: pointer to 32 bytes of fresh randomness. Must
|
|
* be unique for every call. It is wiped before
|
|
* this function returns.
|
|
* In: secshare: pointer to the signer's 32-byte secret share,
|
|
* or NULL. Providing the secret share adds
|
|
* defense-in-depth against bad randomness (the
|
|
* randomness is masked with the share, see
|
|
* BIP 445 "Modifications to Nonce Generation").
|
|
* pubshare: pointer to the signer's public share, or NULL
|
|
* thresh_pk32: pointer to the 32-byte x-only encoding of the
|
|
* threshold public key the signature will verify
|
|
* against (i.e. after applying tweaks, if any),
|
|
* or NULL
|
|
* msg: pointer to the message to be signed, or NULL if
|
|
* the message is not known yet
|
|
* msglen: length of msg. Must be 0 if msg is NULL.
|
|
* extra_in: pointer to additional data to bind into the
|
|
* nonce derivation, or NULL
|
|
* extra_in_len: length of extra_in. Must be 0 if extra_in is
|
|
* NULL and at most 2^32 - 1 (the nonce hash
|
|
* commits to it in a 4-byte length prefix).
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_nonce_gen(
|
|
const secp256k1_context *ctx,
|
|
secp256k1_frost_secnonce *secnonce,
|
|
secp256k1_frost_pubnonce *pubnonce,
|
|
unsigned char *session_secrand32,
|
|
const unsigned char *secshare32,
|
|
const secp256k1_pubkey *pubshare,
|
|
const unsigned char *thresh_pk32,
|
|
const unsigned char *msg,
|
|
size_t msglen,
|
|
const unsigned char *extra_in,
|
|
size_t extra_in_len
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
|
|
|
/** Aggregate the public nonces of the signers.
|
|
*
|
|
* Implements NonceAgg of BIP 445. Note that the aggregate nonce may contain
|
|
* the point at infinity (if the corresponding column sums to it); this is
|
|
* not an error.
|
|
*
|
|
* Returns: 0 if the arguments are invalid or one of the pubnonces is
|
|
* malformed, 1 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* Out: aggnonce: pointer to an aggnonce object
|
|
* error_index: if non-NULL, set to the index of the offending
|
|
* pubnonce on failure
|
|
* In: pubnonces: input array of pointers to pubnonces. The pubnonce at
|
|
* index i must belong to the signer with ids[i] passed
|
|
* to `frost_session_init`.
|
|
* n_pubnonces: length of the pubnonces array. Must be greater than
|
|
* 0.
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_nonce_agg(
|
|
const secp256k1_context *ctx,
|
|
secp256k1_frost_aggnonce *aggnonce,
|
|
size_t *error_index,
|
|
const secp256k1_frost_pubnonce *const *pubnonces,
|
|
size_t n_pubnonces
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
|
|
|
|
/** Initialize a FROST signing session.
|
|
*
|
|
* Implements GetSessionValues of BIP 445: computes the nonce coefficient,
|
|
* the final nonce, and the BIP340 challenge. All signers and the coordinator
|
|
* must call this function with identical arguments (aside from ctx and
|
|
* session). The session is signer-agnostic; the coordinator can use it to
|
|
* verify the partial signatures of all signers.
|
|
*
|
|
* Returns: 0 if the arguments are invalid (including inconsistent key
|
|
* material or duplicate signer ids), 1 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* Out: session: pointer to a session object
|
|
* In: aggnonce: pointer to the aggregate nonce from
|
|
* `frost_nonce_agg`
|
|
* ids: array of the u signer identifiers. Every id must
|
|
* be unique and smaller than n_participants.
|
|
* pubshares: array of u pubkeys with the public shares of the
|
|
* signers (entry i belongs to ids[i]), or NULL if the
|
|
* pubshares are unknown. If provided, they are
|
|
* validated against the threshold public key.
|
|
* n_signers: number of signers u. Must be between threshold and
|
|
* n_participants.
|
|
* n_participants: total number of participants n. Must be at most
|
|
* SECP256K1_FROST_MAX_PARTICIPANTS.
|
|
* threshold: threshold t. Must be between 1 and n_participants.
|
|
* tweak_cache: pointer to the tweak cache holding the threshold
|
|
* public key and all tweaks applied to it
|
|
* msg: pointer to the message to sign, or NULL if
|
|
* msglen is 0
|
|
* msglen: length of msg
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_session_init(
|
|
const secp256k1_context *ctx,
|
|
secp256k1_frost_session *session,
|
|
const secp256k1_frost_aggnonce *aggnonce,
|
|
const uint32_t *ids,
|
|
const secp256k1_pubkey *pubshares,
|
|
size_t n_signers,
|
|
size_t n_participants,
|
|
uint32_t threshold,
|
|
const secp256k1_frost_tweak_cache *tweak_cache,
|
|
const unsigned char *msg,
|
|
size_t msglen
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10);
|
|
|
|
/** Produce a partial signature.
|
|
*
|
|
* Implements Sign of BIP 445. Fails if my_id is not in the session's
|
|
* signer set. If pubshares is non-NULL, it must be the same array that was
|
|
* passed to `frost_session_init`, and the secret share is checked against
|
|
* the signer's public share (recommended; pass NULL only if the pubshares
|
|
* are unavailable). The secnonce is wiped by this function; calling it
|
|
* again with the same secnonce fails.
|
|
*
|
|
* The same ids array that was passed to `frost_session_init` must be passed
|
|
* here.
|
|
*
|
|
* Returns: 0 if the arguments are invalid or signing fails, 1 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* Out: partial_sig: pointer to a partial_sig object
|
|
* In: secnonce: pointer to the signer's secnonce from
|
|
* `frost_nonce_gen`
|
|
* secshare32: pointer to the signer's 32-byte secret share
|
|
* session: pointer to the session
|
|
* ids: array of the u signer identifiers (identical to
|
|
* session_init)
|
|
* pubshares: array of u pubkeys with the signers' public shares
|
|
* (identical to session_init), or NULL
|
|
* n_signers: number of signers u
|
|
* my_id: this signer's identifier
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_sign(
|
|
const secp256k1_context *ctx,
|
|
secp256k1_frost_partial_sig *partial_sig,
|
|
secp256k1_frost_secnonce *secnonce,
|
|
const unsigned char *secshare32,
|
|
const secp256k1_frost_session *session,
|
|
const uint32_t *ids,
|
|
const secp256k1_pubkey *pubshares,
|
|
size_t n_signers,
|
|
uint32_t my_id
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
|
|
|
|
/** Produce a partial signature with a deterministically derived nonce.
|
|
*
|
|
* Implements DeterministicSign of BIP 445. This function combines nonce
|
|
* generation and signing into one step for a signer that is online
|
|
* throughout the whole session. The nonce is derived deterministically from
|
|
* the secret share, the signer set, the other signers' aggregate nonce, the
|
|
* tweaked threshold public key, and the message; no secnonce object is
|
|
* involved.
|
|
*
|
|
* A sole signer (u = 1) passes aggothernonce = NULL. Otherwise aggothernonce
|
|
* is the aggregate of all _other_ signers' pubnonces (obtainable via
|
|
* `frost_nonce_agg`). Unlike an aggnonce passed to `frost_session_init`, the
|
|
* aggothernonce must not contain the point at infinity (BIP 445 feeds it
|
|
* through NonceAgg as a pubnonce contribution, and a pubnonce's components
|
|
* are never the point at infinity); if it does, this function fails.
|
|
*
|
|
* WARNING: the derivation above is the whole of what the nonce depends on. It
|
|
* does NOT commit to the pubshares, to the untweaked threshold public key, or
|
|
* to which tweaks the cache accumulated -- only to the x-only encoding of the
|
|
* _tweaked_ threshold public key (this is BIP 445's det_nonce_hash, not a
|
|
* deviation). Two tweak caches can therefore agree on that x-only key and
|
|
* still disagree on the sign g*gacc that multiplies the secret share, because
|
|
* Q and -Q have the same x-coordinate: a cache initialized from the threshold
|
|
* public key and one initialized from its negation are the simplest example.
|
|
* Two calls that differ only in that way emit the SAME pubnonce and two
|
|
* partial signatures that differ only in the sign of the secret-share term,
|
|
* which is two equations in the nonce and the secret share -- the secret
|
|
* share falls out of the pair.
|
|
*
|
|
* The caller must therefore treat the tweak cache and the pubshares as fixed
|
|
* key material belonging to the group, established once at key generation,
|
|
* and never as per-session parameters accepted from a coordinator or any
|
|
* other peer. Given that, repeating a call reproduces a byte-identical result
|
|
* and is harmless, which is the point of a deterministic nonce.
|
|
*
|
|
* Returns: 0 if the arguments are invalid or signing fails, 1 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* Out: partial_sig: pointer to a partial_sig object
|
|
* pubnonce: pointer to a pubnonce object holding this signer's
|
|
* public nonce, to be sent to the coordinator
|
|
* In: secshare32: pointer to the signer's 32-byte secret share
|
|
* my_id: this signer's identifier
|
|
* aggothernonce: pointer to the aggregate of the other signers'
|
|
* public nonces, or NULL for a sole signer
|
|
* ids: array of the u signer identifiers
|
|
* pubshares: array of u pubkeys with the signers' public
|
|
* shares, or NULL
|
|
* n_signers: number of signers u
|
|
* n_participants: total number of participants n
|
|
* threshold: threshold t
|
|
* tweak_cache: pointer to the tweak cache
|
|
* msg: pointer to the message to sign, or NULL if
|
|
* msglen is 0
|
|
* msglen: length of msg
|
|
* aux_rand32: pointer to 32 bytes of auxiliary randomness mixed
|
|
* into the nonce derivation, or NULL
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_deterministic_sign(
|
|
const secp256k1_context *ctx,
|
|
secp256k1_frost_partial_sig *partial_sig,
|
|
secp256k1_frost_pubnonce *pubnonce,
|
|
const unsigned char *secshare32,
|
|
uint32_t my_id,
|
|
const secp256k1_frost_aggnonce *aggothernonce,
|
|
const uint32_t *ids,
|
|
const secp256k1_pubkey *pubshares,
|
|
size_t n_signers,
|
|
size_t n_participants,
|
|
uint32_t threshold,
|
|
const secp256k1_frost_tweak_cache *tweak_cache,
|
|
const unsigned char *msg,
|
|
size_t msglen,
|
|
const unsigned char *aux_rand32
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(12) SECP256K1_ARG_NONNULL(13);
|
|
|
|
/** Verify a partial signature.
|
|
*
|
|
* Implements PartialSigVerify of BIP 445. The same ids array that was passed
|
|
* to `frost_session_init` must be passed here; signer_index is the index
|
|
* into that array identifying the signer whose partial signature is
|
|
* verified.
|
|
*
|
|
* Returns: 1 if the partial signature is valid, 0 otherwise
|
|
* Args: ctx: pointer to a context object
|
|
* In: partial_sig: pointer to the partial signature
|
|
* pubnonce: pointer to the signer's public nonce
|
|
* pubshare: pointer to the signer's public share
|
|
* session: pointer to the session
|
|
* ids: array of the u signer identifiers (identical to
|
|
* session_init)
|
|
* n_signers: number of signers u
|
|
* signer_index: index of the signer in the ids array
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_partial_sig_verify(
|
|
const secp256k1_context *ctx,
|
|
const secp256k1_frost_partial_sig *partial_sig,
|
|
const secp256k1_frost_pubnonce *pubnonce,
|
|
const secp256k1_pubkey *pubshare,
|
|
const secp256k1_frost_session *session,
|
|
const uint32_t *ids,
|
|
size_t n_signers,
|
|
size_t signer_index
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
|
|
|
|
/** Aggregate partial signatures into a BIP340 signature.
|
|
*
|
|
* Implements PartialSigAgg of BIP 445. The number of partial signatures must
|
|
* equal the number of signers u given to `frost_session_init`. The partial
|
|
* signature at index i must belong to the signer with ids[i].
|
|
*
|
|
* Returns: 0 if the arguments are invalid or a partial signature cannot be
|
|
* parsed, 1 otherwise. Note that 1 does NOT mean that the resulting
|
|
* signature verifies; invalid partial signatures are only detected
|
|
* by `frost_partial_sig_verify`.
|
|
* Args: ctx: pointer to a context object
|
|
* Out: sig64: pointer to a 64-byte array to store the final
|
|
* BIP340 signature
|
|
* error_index: if non-NULL, set to the index of the offending
|
|
* partial signature on failure
|
|
* In: session: pointer to the session
|
|
* partial_sigs: input array of pointers to partial signatures
|
|
* n_sigs: length of the partial_sigs array. Must equal
|
|
* n_signers from `frost_session_init`.
|
|
*/
|
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_partial_sig_agg(
|
|
const secp256k1_context *ctx,
|
|
unsigned char *sig64,
|
|
size_t *error_index,
|
|
const secp256k1_frost_session *session,
|
|
const secp256k1_frost_partial_sig *const *partial_sigs,
|
|
size_t n_sigs
|
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|