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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user