Files
secp256k1-zkp/include/secp256k1_frost.h
Kgothatso Ngako 3765a82886 frost: document deterministic_sign's nonce derivation domain
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>
2026-09-01 23:37:22 +02:00

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