diff --git a/include/secp256k1_chilldkg.h b/include/secp256k1_chilldkg.h index bb0c6bed..57ac76a4 100644 --- a/include/secp256k1_chilldkg.h +++ b/include/secp256k1_chilldkg.h @@ -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 diff --git a/src/modules/chilldkg/main_impl.h b/src/modules/chilldkg/main_impl.h index e88a31d7..c2b4a44a 100644 --- a/src/modules/chilldkg/main_impl.h +++ b/src/modules/chilldkg/main_impl.h @@ -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 diff --git a/src/modules/chilldkg/tests_impl.h b/src/modules/chilldkg/tests_impl.h index e2af3351..2c4b0394 100644 --- a/src/modules/chilldkg/tests_impl.h +++ b/src/modules/chilldkg/tests_impl.h @@ -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