chilldkg: Phase 4 - public coordinator API

Add the coordinator side of the ChillDKG protocol
(bip-frost-dkg v0.3.0-dev, reference commit
a91896883f85b159415ecf298d5e844879af112d), as thin wrappers over the
Phase 2 encpedpop coordinator internals (whose cmsg1 output was
already verified byte-identical to the reference coordinator_step1).

Public API:
- secp256k1_chilldkg_coordinator_step1: takes an array of pointers to
  the n participant pmsg1 messages (musig/frost-style convention),
  parses each with checked scalar parse, aggregates SimplPedPop and
  EncPedPop, builds eq_input (including the enc_secshares suffix,
  mirroring the reference) and emits cmsg1 (162n + 33(t-1) bytes).
  PoPs are not verified coordinator-side, exactly as the reference.
- secp256k1_chilldkg_coordinator_finalize: concatenates the n CertEq
  pmsg2 signatures into the 64n-byte certificate, verifies all of them
  via certeq_verify (hostpubkeys recovered from eq_input at offset
  4+33t), and outputs the coordinator-side DKG result: threshold
  pubkey, pubshares and recovery data -- no secshare.
- secp256k1_chilldkg_coordinator_state: opaque, 21041 bytes,
  magic-validated, holds only t, n, eq_input, thresh_pk and pubshares
  -- no secret material, documented as freely copyable/persistable so
  a stateless coordinator is possible.

Blame mapping (verified against chilldkg.py):
- malformed pmsg1 (bad commitment encoding, overflowing encrypted
  share) -> FAULTY_PARTICIPANT(sender index),
- invalid CertEq signature -> FAULTY_PARTICIPANT(failing index)
  (deliberately different from participant_finalize, which maps the
  same failure to FAULTY_COORDINATOR -- matching the reference),
- invalid session params -> INVALID_INPUT; all outputs zeroed on
  failure.

tests_impl.h: chilldkg_coordinator_api_test runs a full n=3,t=2
session through only the public APIs on both sides, byte-exact
against the Python reference vectors and cross-checked against every
participant's finalize outputs; blame cases (malformed pmsg1 and
overflowing share -> FAULTY_PARTICIPANT with the right index, bad
CertEq sig -> FAULTY_PARTICIPANT(2), zeroed outputs); misuse coverage
(NULL args, corrupted state magic).

Verified: make check 3/3 (incl. noverify); CMake ctest 365/365;
./tests --target=chilldkg green.
This commit is contained in:
Kgothatso Ngako
2026-08-31 05:33:42 +02:00
parent 2a0e14d076
commit 5409aae813
3 changed files with 418 additions and 4 deletions

View File

@@ -32,12 +32,13 @@ extern "C" {
* not exceed SECP256K1_CHILLDKG_MAX_PARTICIPANTS. The message flow is:
* 1. Every participant runs secp256k1_chilldkg_participant_step1 and sends
* the resulting pmsg1 to the coordinator.
* 2. The coordinator aggregates the pmsg1s into a single cmsg1 broadcast
* to all participants (coordinator API is not available yet).
* 2. The coordinator runs secp256k1_chilldkg_coordinator_step1 on all
* pmsg1s and broadcasts the resulting cmsg1 to all participants.
* 3. Every participant runs secp256k1_chilldkg_participant_step2 and sends
* the resulting signature (pmsg2) to the coordinator.
* 4. The coordinator collects the n signatures into a certificate (cmsg2)
* broadcast to all participants.
* 4. The coordinator runs secp256k1_chilldkg_coordinator_finalize on all
* pmsg2s and broadcasts the resulting certificate (cmsg2) to all
* participants.
* 5. Every participant runs secp256k1_chilldkg_participant_finalize to
* obtain the DKG output and the recovery data.
*
@@ -111,6 +112,19 @@ typedef struct secp256k1_chilldkg_participant_state2 {
unsigned char data[12 + 4 + 131 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS + 32 + 33 + 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
} secp256k1_chilldkg_participant_state2;
/** Opaque data structure that holds the coordinator's session state after
* secp256k1_chilldkg_coordinator_step1, to be passed to
* secp256k1_chilldkg_coordinator_finalize (it must not be reused).
*
* This structure contains no secret key material; it can be copied freely
* (e.g., to persist it between the two coordinator steps).
*
* Guaranteed to be 21041 bytes in size.
*/
typedef struct secp256k1_chilldkg_coordinator_state {
unsigned char data[12 + 4 + 131 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS + 33 + 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
} secp256k1_chilldkg_coordinator_state;
/** Compute the participant's host public key from the host secret key.
*
* The host public key is the long-term cryptographic identity of the
@@ -307,6 +321,85 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_chilldkg_fault secp256k1_ch
const unsigned char *cmsg2
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);
/** Perform the coordinator's first step of a ChillDKG session.
*
* Parses all n participant messages and aggregates them into the message to
* broadcast to all participants. The proofs of possession contained in the
* pmsg1s are NOT verified here; the participants verify them in step 2 (this
* mirrors the reference implementation).
*
* Returns: SECP256K1_CHILLDKG_OK on success,
* SECP256K1_CHILLDKG_INVALID_INPUT on invalid session parameters,
* or SECP256K1_CHILLDKG_FAULTY_PARTICIPANT (with fault_index set to
* the sender) if a participant message is malformed (invalid
* commitment encoding, or an encrypted share that overflows the
* group order). On failure, cmsg1 and the state are set to zero.
* Args: ctx: pointer to a context object
* Out: state: pointer to a coordinator_state object to be passed
* to secp256k1_chilldkg_coordinator_finalize (must not
* be reused)
* cmsg1: pointer to a 162*n + 33*(t-1) byte array (see
* secp256k1_chilldkg_coordinator_msg1_len) to store
* the message to be broadcast to all participants
* fault_index: pointer to a uint32 that receives the identifier of
* the faulty participant where applicable, and
* UINT32_MAX otherwise
* In: pmsgs1: array of n pointers to the participants' first
* messages (33*t + 32*n + 97 bytes each)
* hostpubkeys33: pointer to an array of n host public keys (33 bytes
* each); must be identical (in content and order) to
* the arrays used by the participants
* n_participants: total number of participants n
* threshold: threshold t
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_chilldkg_fault secp256k1_chilldkg_coordinator_step1(
const secp256k1_context *ctx,
secp256k1_chilldkg_coordinator_state *state,
unsigned char *cmsg1,
uint32_t *fault_index,
const unsigned char *const *pmsgs1,
const unsigned char *hostpubkeys33,
size_t n_participants,
uint32_t threshold
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
/** Perform the coordinator's final step of a ChillDKG session.
*
* Collects the n CertEq signatures into the certificate and verifies all of
* them. If this function returns SECP256K1_CHILLDKG_OK, the coordinator
* deems the DKG session successful.
*
* Returns: SECP256K1_CHILLDKG_OK on success, or
* SECP256K1_CHILLDKG_FAULTY_PARTICIPANT (with fault_index set to
* the signer) if a CertEq signature is invalid. On failure, all
* outputs are set to zero.
* Args: ctx: pointer to a context object
* Out: cmsg2: pointer to a 64*n byte array to store the
* certificate, to be broadcast to all participants
* thresh_pk33: pointer to a 33-byte array to store the threshold
* public key (compressed serialization)
* pubshares33: pointer to an array of n 33-byte elements to store
* the public shares of all participants
* recovery: pointer to a 4 + 33*t + 162*n byte array (see
* secp256k1_chilldkg_recovery_data_len) to store the
* recovery data
* fault_index: pointer to a uint32 (see above)
* In: state: pointer to the coordinator_state object output by
* secp256k1_chilldkg_coordinator_step1
* pmsgs2: array of n pointers to the participants' second
* messages (64 bytes each)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_chilldkg_fault secp256k1_chilldkg_coordinator_finalize(
const secp256k1_context *ctx,
unsigned char *cmsg2,
unsigned char *thresh_pk33,
unsigned char *pubshares33,
unsigned char *recovery,
uint32_t *fault_index,
const secp256k1_chilldkg_coordinator_state *state,
const unsigned char *const *pmsgs2
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);
#ifdef __cplusplus
}
#endif

View File

@@ -57,6 +57,7 @@ static int secp256k1_chilldkg_params_validate(const unsigned char *hostpubkeys33
static const unsigned char secp256k1_chilldkg_participant_state1_magic[4] = { 0x3f, 0x2c, 0x9e, 0x51 };
static const unsigned char secp256k1_chilldkg_participant_state2_magic[4] = { 0x7a, 0xd1, 0x44, 0x0b };
static const unsigned char secp256k1_chilldkg_coordinator_state_magic[4] = { 0x1b, 0x8e, 0x63, 0xa7 };
/* A state1 object consists of
* - 4 byte magic set during initialization to allow detecting an
@@ -442,4 +443,175 @@ secp256k1_chilldkg_fault secp256k1_chilldkg_participant_finalize(const secp256k1
return SECP256K1_CHILLDKG_OK;
}
typedef struct {
uint32_t t;
uint32_t n;
/* Only the first 4 + 33*t + 98*n bytes are meaningful. */
unsigned char eq_input[SECP256K1_CHILLDKG_MAX_EQ_INPUT_LEN];
unsigned char thresh_pk33[33];
unsigned char pubshares33[SECP256K1_CHILLDKG_MAX_PARTICIPANTS][33];
} secp256k1_chilldkg_coordinator_state_internal;
/* A coordinator state object consists of
* - 4 byte magic
* - 4 byte threshold t, 4 byte participant count n (big-endian)
* - eq_input (fixed-capacity buffer)
* - 33 byte threshold public key
* - 33*SECP256K1_CHILLDKG_MAX_PARTICIPANTS byte public shares (only the first
* n entries are meaningful)
* The coordinator state contains no secret key material (the coordinator has
* no secret share). */
static void secp256k1_chilldkg_coordinator_state_save(secp256k1_chilldkg_coordinator_state *state, const secp256k1_chilldkg_coordinator_state_internal *state_i) {
unsigned char *ptr = state->data;
memcpy(ptr, secp256k1_chilldkg_coordinator_state_magic, 4);
ptr += 4;
secp256k1_write_be32(ptr, state_i->t);
ptr += 4;
secp256k1_write_be32(ptr, state_i->n);
ptr += 4;
memcpy(ptr, state_i->eq_input, sizeof(state_i->eq_input));
ptr += sizeof(state_i->eq_input);
memcpy(ptr, state_i->thresh_pk33, 33);
ptr += 33;
memcpy(ptr, &state_i->pubshares33[0][0], 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
VERIFY_CHECK(ptr + 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS == state->data + sizeof(state->data));
}
static int secp256k1_chilldkg_coordinator_state_load(const secp256k1_context *ctx, secp256k1_chilldkg_coordinator_state_internal *state_i, const secp256k1_chilldkg_coordinator_state *state) {
const unsigned char *ptr = state->data;
ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_chilldkg_coordinator_state_magic, 4) == 0);
ptr += 4;
state_i->t = secp256k1_read_be32(ptr);
ptr += 4;
state_i->n = secp256k1_read_be32(ptr);
ptr += 4;
/* The remaining contents were written by coordinator_state_save. */
VERIFY_CHECK(state_i->t >= 1
&& state_i->t <= state_i->n
&& state_i->n <= SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
memcpy(state_i->eq_input, ptr, sizeof(state_i->eq_input));
ptr += sizeof(state_i->eq_input);
memcpy(state_i->thresh_pk33, ptr, 33);
ptr += 33;
memcpy(&state_i->pubshares33[0][0], ptr, 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
return 1;
}
secp256k1_chilldkg_fault secp256k1_chilldkg_coordinator_step1(const secp256k1_context *ctx, secp256k1_chilldkg_coordinator_state *state, unsigned char *cmsg1, uint32_t *fault_index, const unsigned char *const *pmsgs1, const unsigned char *hostpubkeys33, size_t n_participants, uint32_t threshold) {
secp256k1_chilldkg_coordinator_state_internal state_i;
secp256k1_chilldkg_simplpedpop_dkg_output dkg_output;
secp256k1_scalar enc_secshares[SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
secp256k1_chilldkg_fault fault = SECP256K1_CHILLDKG_INVALID_INPUT;
size_t cmsg1_len, enc_cmsg_len, eq_input_len;
size_t i;
VERIFY_CHECK(ctx != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
SECP256K1_CHILLDKG_ARG_CHECK(state != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(cmsg1 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(pmsgs1 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(hostpubkeys33 != NULL);
memset(&state_i, 0, sizeof(state_i));
memset(&dkg_output, 0, sizeof(dkg_output));
memset(state->data, 0, sizeof(state->data));
*fault_index = UINT32_MAX;
cmsg1_len = secp256k1_chilldkg_coordinator_msg1_len(n_participants, threshold);
if (!secp256k1_chilldkg_params_validate(hostpubkeys33, n_participants, threshold)) {
if (cmsg1_len > 0) {
memset(cmsg1, 0, cmsg1_len);
}
return SECP256K1_CHILLDKG_INVALID_INPUT;
}
memset(cmsg1, 0, cmsg1_len);
/* Aggregate SimplPedPop + EncPedPop. This parses every pmsg1 (invalid
* commitment encodings and overflowing encrypted shares blame the
* sender); the proofs of possession are not verified here (the
* participants verify them in step 2). */
fault = secp256k1_chilldkg_encpedpop_coordinator_step(ctx, cmsg1, &dkg_output, state_i.eq_input, enc_secshares, fault_index, pmsgs1, threshold, hostpubkeys33, n_participants);
if (fault != SECP256K1_CHILLDKG_OK) {
memset(cmsg1, 0, cmsg1_len);
goto cleanup;
}
/* cmsg1 = enc_cmsg || enc_secshares; the enc_secshares are also appended
* to eq_input (the participants do the same in participant_step2). */
enc_cmsg_len = 97 * n_participants + 33 * (size_t)(threshold - 1) + 33 * n_participants;
eq_input_len = 4 + 33 * (size_t)threshold + 66 * n_participants;
for (i = 0; i < n_participants; i++) {
secp256k1_scalar_get_b32(cmsg1 + enc_cmsg_len + 32 * i, &enc_secshares[i]);
secp256k1_scalar_get_b32(state_i.eq_input + eq_input_len + 32 * i, &enc_secshares[i]);
}
state_i.t = threshold;
state_i.n = (uint32_t)n_participants;
/* The coordinator's DKG output has no secret share. */
memcpy(state_i.thresh_pk33, dkg_output.thresh_pk33, 33);
memcpy(&state_i.pubshares33[0][0], &dkg_output.pubshares33[0][0], 33 * n_participants);
secp256k1_chilldkg_coordinator_state_save(state, &state_i);
cleanup:
for (i = 0; i < n_participants; i++) {
secp256k1_scalar_clear(&enc_secshares[i]);
}
return fault;
}
secp256k1_chilldkg_fault secp256k1_chilldkg_coordinator_finalize(const secp256k1_context *ctx, unsigned char *cmsg2, unsigned char *thresh_pk33, unsigned char *pubshares33, unsigned char *recovery, uint32_t *fault_index, const secp256k1_chilldkg_coordinator_state *state, const unsigned char *const *pmsgs2) {
secp256k1_chilldkg_coordinator_state_internal state_i;
size_t n, t, eq_input_len;
size_t i;
VERIFY_CHECK(ctx != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
SECP256K1_CHILLDKG_ARG_CHECK(cmsg2 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(thresh_pk33 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(pubshares33 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(recovery != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(state != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(pmsgs2 != NULL);
memset(thresh_pk33, 0, 33);
*fault_index = UINT32_MAX;
/* On a state load failure (uninitialized state), only thresh_pk33 is
* zeroed: the sizes of the other outputs depend on the state contents. */
if (!secp256k1_chilldkg_coordinator_state_load(ctx, &state_i, state)) {
return SECP256K1_CHILLDKG_INVALID_INPUT;
}
t = state_i.t;
n = state_i.n;
eq_input_len = 4 + 33 * t + 98 * n;
memset(cmsg2, 0, 64 * n);
memset(pubshares33, 0, 33 * n);
memset(recovery, 0, 4 + 33 * t + 162 * n);
/* certeq_coordinator_step: the certificate is the concatenation of the
* pmsg2 signatures. */
for (i = 0; i < n; i++) {
memcpy(cmsg2 + 64 * i, pmsgs2[i], 64);
}
/* certeq_verify: the host public keys are part of eq_input (at offset
* 4 + 33*t). An invalid signature in the certificate blames the signer
* (FaultyParticipantError in the reference, unlike on the participant
* side, where it blames the coordinator). */
if (!secp256k1_chilldkg_certeq_verify(ctx, state_i.eq_input + 4 + 33 * t, n, state_i.eq_input, eq_input_len, cmsg2, fault_index)) {
memset(cmsg2, 0, 64 * n);
return SECP256K1_CHILLDKG_FAULTY_PARTICIPANT;
}
memcpy(thresh_pk33, state_i.thresh_pk33, 33);
memcpy(pubshares33, &state_i.pubshares33[0][0], 33 * n);
/* recovery data = eq_input || cert */
memcpy(recovery, state_i.eq_input, eq_input_len);
memcpy(recovery + eq_input_len, cmsg2, 64 * n);
return SECP256K1_CHILLDKG_OK;
}
#endif

View File

@@ -1262,6 +1262,154 @@ static void chilldkg_participant_api_test(void) {
}
}
static void chilldkg_coordinator_api_test(void) {
const size_t n = 3;
const uint32_t t = 2;
const size_t pmsg1_len = 33 * 2 + 32 * 3 + 97; /* 259 */
const size_t cmsg1_len = 162 * 3 + 33 * 1; /* 519 */
const size_t recovery_len = 4 + 33 * 2 + 162 * 3; /* 556 */
secp256k1_chilldkg_participant_state1 p_state1[3];
secp256k1_chilldkg_participant_state2 p_state2[3];
secp256k1_chilldkg_coordinator_state coord_state;
unsigned char pmsg1[3][259];
unsigned char pmsg2[3][64];
const unsigned char *pmsg1_ptrs[3];
const unsigned char *pmsg2_ptrs[3];
unsigned char cmsg1[519];
unsigned char cmsg2[192];
unsigned char hostpubkeys33[3 * 33];
unsigned char pubshares33[3 * 33];
unsigned char recovery[556];
unsigned char buf33[33];
unsigned char secshare32[32];
secp256k1_chilldkg_fault fault;
uint32_t fault_index = 0;
size_t i, j;
for (i = 0; i < n; i++) {
pmsg1_ptrs[i] = pmsg1[i];
pmsg2_ptrs[i] = pmsg2[i];
memcpy(hostpubkeys33 + 33 * i, vec3_hostpubkeys[i], 33);
}
/* A full session using only the public API on both sides. */
for (i = 0; i < n; i++) {
CHECK(secp256k1_chilldkg_participant_step1(CTX, &p_state1[i], pmsg1[i], vec3_hostseckeys[i], hostpubkeys33, n, t, vec3_randoms[i]) == 1);
CHECK(secp256k1_memcmp_var(pmsg1[i], vec3_pmsgs1[i], pmsg1_len) == 0);
}
fault = secp256k1_chilldkg_coordinator_step1(CTX, &coord_state, cmsg1, &fault_index, pmsg1_ptrs, hostpubkeys33, n, t);
CHECK(fault == SECP256K1_CHILLDKG_OK);
CHECK(secp256k1_memcmp_var(cmsg1, vec3_cmsg1, cmsg1_len) == 0);
for (i = 0; i < n; i++) {
fault = secp256k1_chilldkg_participant_step2(CTX, &p_state2[i], pmsg2[i], &fault_index, &p_state1[i], vec3_hostseckeys[i], cmsg1, vec3_aux_rands[i]);
CHECK(fault == SECP256K1_CHILLDKG_OK);
CHECK(secp256k1_memcmp_var(pmsg2[i], vec3_pmsgs2[i], 64) == 0);
}
fault = secp256k1_chilldkg_coordinator_finalize(CTX, cmsg2, buf33, pubshares33, recovery, &fault_index, &coord_state, pmsg2_ptrs);
CHECK(fault == SECP256K1_CHILLDKG_OK);
CHECK(secp256k1_memcmp_var(cmsg2, vec3_cmsg2, sizeof(cmsg2)) == 0);
CHECK(secp256k1_memcmp_var(buf33, vec3_thresh_pk, 33) == 0);
for (j = 0; j < n; j++) {
CHECK(secp256k1_memcmp_var(pubshares33 + 33 * j, vec3_pubshares[j], 33) == 0);
}
CHECK(secp256k1_memcmp_var(recovery, vec3_recovery, recovery_len) == 0);
/* The participants agree with the coordinator's outputs. */
for (i = 0; i < n; i++) {
unsigned char p_recovery[556];
unsigned char p_pubshares33[3 * 33];
fault = secp256k1_chilldkg_participant_finalize(CTX, secshare32, buf33, p_pubshares33, p_recovery, &fault_index, &p_state2[i], cmsg2);
CHECK(fault == SECP256K1_CHILLDKG_OK);
CHECK(secp256k1_memcmp_var(secshare32, vec3_secshares[i], 32) == 0);
CHECK(secp256k1_memcmp_var(buf33, vec3_thresh_pk, 33) == 0);
CHECK(secp256k1_memcmp_var(p_pubshares33, pubshares33, 33 * n) == 0);
CHECK(secp256k1_memcmp_var(p_recovery, recovery, recovery_len) == 0);
}
/* A malformed pmsg1 blames its sender and zeroes the outputs. */
{
secp256k1_chilldkg_coordinator_state tmp_state;
unsigned char pmsg1_bad[259];
const unsigned char *bad_ptrs[3];
bad_ptrs[0] = pmsg1[0];
bad_ptrs[1] = pmsg1_bad;
bad_ptrs[2] = pmsg1[2];
/* Invalid commitment encoding. */
memcpy(pmsg1_bad, pmsg1[1], pmsg1_len);
pmsg1_bad[0] = 0x04;
fault_index = 0;
fault = secp256k1_chilldkg_coordinator_step1(CTX, &tmp_state, cmsg1, &fault_index, bad_ptrs, hostpubkeys33, n, t);
CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT);
CHECK(fault_index == 1);
CHECK(secp256k1_is_zero_array(cmsg1, cmsg1_len));
CHECK(secp256k1_is_zero_array(tmp_state.data, sizeof(tmp_state.data)));
/* An encrypted share that overflows the group order. */
memcpy(pmsg1_bad, pmsg1[1], pmsg1_len);
memset(pmsg1_bad + 33 * 2 + 64 + 33, 0xff, 32);
fault_index = 0;
fault = secp256k1_chilldkg_coordinator_step1(CTX, &tmp_state, cmsg1, &fault_index, bad_ptrs, hostpubkeys33, n, t);
CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT);
CHECK(fault_index == 1);
/* Invalid session parameters are input errors. */
fault = secp256k1_chilldkg_coordinator_step1(CTX, &tmp_state, cmsg1, &fault_index, pmsg1_ptrs, hostpubkeys33, n, 0);
CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT);
fault = secp256k1_chilldkg_coordinator_step1(CTX, &tmp_state, cmsg1, &fault_index, pmsg1_ptrs, hostpubkeys33, n, (uint32_t)(n + 1));
CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT);
}
/* An invalid CertEq signature in a pmsg2 blames the signer (unlike on
* the participant side, where it blames the coordinator). */
{
secp256k1_chilldkg_coordinator_state fresh_state;
unsigned char pmsg2_bad[64];
const unsigned char *bad_ptrs[3];
bad_ptrs[0] = pmsg2[0];
bad_ptrs[1] = pmsg2[1];
bad_ptrs[2] = pmsg2_bad;
fault = secp256k1_chilldkg_coordinator_step1(CTX, &fresh_state, cmsg1, &fault_index, pmsg1_ptrs, hostpubkeys33, n, t);
CHECK(fault == SECP256K1_CHILLDKG_OK);
memcpy(pmsg2_bad, pmsg2[2], 64);
pmsg2_bad[10] ^= 1;
fault_index = 0;
fault = secp256k1_chilldkg_coordinator_finalize(CTX, cmsg2, buf33, pubshares33, recovery, &fault_index, &fresh_state, bad_ptrs);
CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT);
CHECK(fault_index == 2);
CHECK(secp256k1_is_zero_array(cmsg2, sizeof(cmsg2)));
CHECK(secp256k1_is_zero_array(buf33, 33));
CHECK(secp256k1_is_zero_array(pubshares33, 33 * n));
CHECK(secp256k1_is_zero_array(recovery, recovery_len));
}
/* API misuse of the enum-returning functions: the illegal-argument
* callback fires exactly once and the function returns INVALID_INPUT. */
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_step1(CTX, NULL, cmsg1, &fault_index, pmsg1_ptrs, hostpubkeys33, n, t) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_step1(CTX, &coord_state, NULL, &fault_index, pmsg1_ptrs, hostpubkeys33, n, t) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_step1(CTX, &coord_state, cmsg1, NULL, pmsg1_ptrs, hostpubkeys33, n, t) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_step1(CTX, &coord_state, cmsg1, &fault_index, NULL, hostpubkeys33, n, t) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_step1(CTX, &coord_state, cmsg1, &fault_index, pmsg1_ptrs, NULL, n, t) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_finalize(CTX, NULL, buf33, pubshares33, recovery, &fault_index, &coord_state, pmsg2_ptrs) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_finalize(CTX, cmsg2, NULL, pubshares33, recovery, &fault_index, &coord_state, pmsg2_ptrs) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_finalize(CTX, cmsg2, buf33, NULL, recovery, &fault_index, &coord_state, pmsg2_ptrs) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_finalize(CTX, cmsg2, buf33, pubshares33, NULL, &fault_index, &coord_state, pmsg2_ptrs) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_finalize(CTX, cmsg2, buf33, pubshares33, recovery, NULL, &coord_state, pmsg2_ptrs) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_finalize(CTX, cmsg2, buf33, pubshares33, recovery, &fault_index, NULL, pmsg2_ptrs) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_finalize(CTX, cmsg2, buf33, pubshares33, recovery, &fault_index, &coord_state, NULL) == SECP256K1_CHILLDKG_INVALID_INPUT));
/* A state with a bad magic (uninitialized or corrupted) is rejected. */
{
secp256k1_chilldkg_coordinator_state bad_state;
memset(&bad_state, 0, sizeof(bad_state));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_finalize(CTX, cmsg2, buf33, pubshares33, recovery, &fault_index, &bad_state, pmsg2_ptrs) == SECP256K1_CHILLDKG_INVALID_INPUT));
fault = secp256k1_chilldkg_coordinator_step1(CTX, &bad_state, cmsg1, &fault_index, pmsg1_ptrs, hostpubkeys33, n, t);
CHECK(fault == SECP256K1_CHILLDKG_OK);
bad_state.data[0] ^= 1;
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_finalize(CTX, cmsg2, buf33, pubshares33, recovery, &fault_index, &bad_state, pmsg2_ptrs) == SECP256K1_CHILLDKG_INVALID_INPUT));
}
}
static const struct tf_test_entry tests_chilldkg[] = {
CASE1(chilldkg_tagged_hashes_test),
CASE1(chilldkg_params_hash_test),
@@ -1273,6 +1421,7 @@ static const struct tf_test_entry tests_chilldkg[] = {
CASE1(chilldkg_simplpedpop_test),
CASE1(chilldkg_encpedpop_test),
CASE1(chilldkg_participant_api_test),
CASE1(chilldkg_coordinator_api_test),
};
#endif