/***********************************************************************
 * Distributed under the MIT software license, see the accompanying    *
 * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
 ***********************************************************************/

#ifndef SECP256K1_MODULE_CHILLDKG_MAIN_H
#define SECP256K1_MODULE_CHILLDKG_MAIN_H

#include <string.h>

#include "../../../include/secp256k1_chilldkg.h"

#include "util_impl.h"
#include "vss_impl.h"
#include "simplpedpop_impl.h"
#include "encpedpop_impl.h"
#include "certeq_impl.h"

/* ARG_CHECK returns 0, which would read as SECP256K1_CHILLDKG_OK in the
 * enum-returning public functions of this module. Use this variant there. */
#define SECP256K1_CHILLDKG_ARG_CHECK(cond) do { \
    if (EXPECT(!(cond), 0)) { \
        secp256k1_callback_call(&ctx->illegal_callback, #cond); \
        return SECP256K1_CHILLDKG_INVALID_INPUT; \
    } \
} while(0)

/* Validate the session parameters (params_validate in chilldkg_ref): it must
 * hold that 1 <= t <= n <= SECP256K1_CHILLDKG_MAX_PARTICIPANTS (the reference
 * allows n up to 2^32 - 1), all host public keys must be valid compressed
 * public keys (the infinity encoding is not a valid public key), and there
 * must be no duplicates. */
static int secp256k1_chilldkg_params_validate(const unsigned char *hostpubkeys33, size_t n, uint32_t t) {
    size_t i, j;

    if (t < 1 || n < 1 || t > n || n > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
        return 0;
    }
    for (i = 0; i < n; i++) {
        secp256k1_ge hostpubkey;
        if (!secp256k1_chilldkg_point_load(&hostpubkey, hostpubkeys33 + 33 * i)
            || secp256k1_ge_is_infinity(&hostpubkey)) {
            /* InvalidHostPubkeyError(i) in the reference. */
            return 0;
        }
    }
    for (i = 0; i < n; i++) {
        for (j = i + 1; j < n; j++) {
            if (secp256k1_memcmp_var(hostpubkeys33 + 33 * i, hostpubkeys33 + 33 * j, 33) == 0) {
                /* DuplicateHostPubkeyError in the reference. */
                return 0;
            }
        }
    }
    return 1;
}

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 };
static const unsigned char secp256k1_chilldkg_participant_inv_data_magic[4] = { 0x62, 0x4a, 0xc5, 0x90 };

/* An inv_data object consists of
 * - 4 byte magic
 * - 4 byte participant count n, 4 byte participant_id (big-endian)
 * - 32 byte untweaked secshare
 * - 33 byte untweaked pubshare (compressed encoding with infinity)
 * - 32 byte encrypted secshare
 * - 32*SECP256K1_CHILLDKG_MAX_PARTICIPANTS byte pads (only the first n
 *   entries are meaningful)
 * The inv data contains secrets (the untweaked secshare and the pads). */
static void secp256k1_chilldkg_participant_inv_data_save(secp256k1_chilldkg_participant_inv_data *inv_data, const secp256k1_chilldkg_encpedpop_participant_inv_data *inv_data_i) {
    unsigned char *ptr = inv_data->data;
    size_t i;

    memcpy(ptr, secp256k1_chilldkg_participant_inv_data_magic, 4);
    ptr += 4;
    secp256k1_write_be32(ptr, inv_data_i->simpl.n);
    ptr += 4;
    secp256k1_write_be32(ptr, inv_data_i->simpl.participant_id);
    ptr += 4;
    secp256k1_scalar_get_b32(ptr, &inv_data_i->simpl.secshare);
    ptr += 32;
    secp256k1_chilldkg_point_save(ptr, &inv_data_i->simpl.pubshare);
    ptr += 33;
    secp256k1_scalar_get_b32(ptr, &inv_data_i->enc_secshare);
    ptr += 32;
    for (i = 0; i < SECP256K1_CHILLDKG_MAX_PARTICIPANTS; i++) {
        secp256k1_scalar_get_b32(ptr + 32 * i, &inv_data_i->pads[i]);
    }
    ptr += 32 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS;
    VERIFY_CHECK(ptr == inv_data->data + sizeof(inv_data->data));
}

static int secp256k1_chilldkg_participant_inv_data_load(const secp256k1_context *ctx, secp256k1_chilldkg_encpedpop_participant_inv_data *inv_data_i, const secp256k1_chilldkg_participant_inv_data *inv_data) {
    const unsigned char *ptr = inv_data->data;
    size_t i;

    ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_chilldkg_participant_inv_data_magic, 4) == 0);
    ptr += 4;
    inv_data_i->simpl.n = secp256k1_read_be32(ptr);
    ptr += 4;
    inv_data_i->simpl.participant_id = secp256k1_read_be32(ptr);
    ptr += 4;
    /* The remaining contents were written by inv_data_save. The bounds check
     * must not be a VERIFY_CHECK: a persisted inv_data object can be tampered
     * with, and in noverify builds the check would be compiled out and the
     * out-of-range n would index fixed-size arrays below and in callers. */
    ARG_CHECK(inv_data_i->simpl.n >= 1
              && inv_data_i->simpl.n <= SECP256K1_CHILLDKG_MAX_PARTICIPANTS
              && inv_data_i->simpl.participant_id < inv_data_i->simpl.n);
    secp256k1_scalar_set_b32(&inv_data_i->simpl.secshare, ptr, NULL);
    ptr += 32;
    /* This load always succeeds; call it unconditionally (it must not sit
     * inside VERIFY_CHECK, which is compiled out in noverify builds). */
    if (!secp256k1_chilldkg_point_load(&inv_data_i->simpl.pubshare, ptr)) {
        VERIFY_CHECK(0);
        secp256k1_ge_set_infinity(&inv_data_i->simpl.pubshare);
    }
    ptr += 33;
    secp256k1_scalar_set_b32(&inv_data_i->enc_secshare, ptr, NULL);
    ptr += 32;
    for (i = 0; i < SECP256K1_CHILLDKG_MAX_PARTICIPANTS; i++) {
        secp256k1_scalar_set_b32(&inv_data_i->pads[i], ptr + 32 * i, NULL);
    }
    return 1;
}

/* A state1 object consists of
 * - 4 byte magic set during initialization to allow detecting an
 *   uninitialized object
 * - 4 byte threshold t, 4 byte participant count n, 4 byte participant_id
 *   (big-endian)
 * - 33 byte commitment to the secret (compressed encoding with infinity)
 * - 33 byte pubnonce
 * - 33*SECP256K1_CHILLDKG_MAX_PARTICIPANTS byte host public keys (only the
 *   first n entries are meaningful)
 * The state contains no secret key material. */
static void secp256k1_chilldkg_participant_state1_save(secp256k1_chilldkg_participant_state1 *state, const secp256k1_chilldkg_encpedpop_participant_state *state_i) {
    unsigned char *ptr = state->data;

    memcpy(ptr, secp256k1_chilldkg_participant_state1_magic, 4);
    ptr += 4;
    secp256k1_write_be32(ptr, state_i->simpl_state.t);
    ptr += 4;
    secp256k1_write_be32(ptr, state_i->simpl_state.n);
    ptr += 4;
    secp256k1_write_be32(ptr, state_i->simpl_state.participant_id);
    ptr += 4;
    secp256k1_chilldkg_point_save(ptr, &state_i->simpl_state.com_to_secret);
    ptr += 33;
    memcpy(ptr, state_i->pubnonce33, 33);
    ptr += 33;
    memcpy(ptr, &state_i->enckeys33[0][0], 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
    VERIFY_CHECK(ptr + 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS == state->data + sizeof(state->data));
}

static int secp256k1_chilldkg_participant_state1_load(const secp256k1_context *ctx, secp256k1_chilldkg_encpedpop_participant_state *state_i, const secp256k1_chilldkg_participant_state1 *state) {
    const unsigned char *ptr = state->data;

    ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_chilldkg_participant_state1_magic, 4) == 0);
    ptr += 4;
    state_i->simpl_state.t = secp256k1_read_be32(ptr);
    ptr += 4;
    state_i->simpl_state.n = secp256k1_read_be32(ptr);
    ptr += 4;
    state_i->simpl_state.participant_id = secp256k1_read_be32(ptr);
    ptr += 4;
    /* The remaining contents were written by state1_save. The bounds check
     * must not be a VERIFY_CHECK: a persisted state object can be tampered
     * with, and in noverify builds the check would be compiled out and the
     * out-of-range n would index fixed-size arrays in callers. */
    ARG_CHECK(state_i->simpl_state.t >= 1
              && state_i->simpl_state.t <= state_i->simpl_state.n
              && state_i->simpl_state.n <= SECP256K1_CHILLDKG_MAX_PARTICIPANTS
              && state_i->simpl_state.participant_id < state_i->simpl_state.n);
    /* This load always succeeds; call it unconditionally (it must not sit
     * inside VERIFY_CHECK, which is compiled out in noverify builds). */
    if (!secp256k1_chilldkg_point_load(&state_i->simpl_state.com_to_secret, ptr)) {
        VERIFY_CHECK(0);
        secp256k1_ge_set_infinity(&state_i->simpl_state.com_to_secret);
    }
    ptr += 33;
    memcpy(state_i->pubnonce33, ptr, 33);
    ptr += 33;
    memcpy(&state_i->enckeys33[0][0], ptr, 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
    return 1;
}

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];
    secp256k1_chilldkg_simplpedpop_dkg_output dkg_output;
} secp256k1_chilldkg_participant_state2_internal;

/* A state2 object consists of
 * - 4 byte magic
 * - 4 byte threshold t, 4 byte participant count n (big-endian)
 * - eq_input (fixed-capacity buffer)
 * - 32 byte (tweaked) secret share
 * - 33 byte threshold public key
 * - 33*SECP256K1_CHILLDKG_MAX_PARTICIPANTS byte public shares (only the first
 *   n entries are meaningful)
 * The state contains the secret share and must be kept secret. */
static void secp256k1_chilldkg_participant_state2_save(secp256k1_chilldkg_participant_state2 *state, const secp256k1_chilldkg_participant_state2_internal *state_i) {
    unsigned char *ptr = state->data;

    memcpy(ptr, secp256k1_chilldkg_participant_state2_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->dkg_output.secshare32, 32);
    ptr += 32;
    memcpy(ptr, state_i->dkg_output.thresh_pk33, 33);
    ptr += 33;
    memcpy(ptr, &state_i->dkg_output.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_participant_state2_load(const secp256k1_context *ctx, secp256k1_chilldkg_participant_state2_internal *state_i, const secp256k1_chilldkg_participant_state2 *state) {
    const unsigned char *ptr = state->data;

    ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_chilldkg_participant_state2_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 state2_save. The bounds check
     * must not be a VERIFY_CHECK: a persisted state object can be tampered
     * with, and in noverify builds the check would be compiled out and the
     * out-of-range t/n would index fixed-size arrays in callers. */
    ARG_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->dkg_output.secshare32, ptr, 32);
    ptr += 32;
    memcpy(state_i->dkg_output.thresh_pk33, ptr, 33);
    ptr += 33;
    memcpy(&state_i->dkg_output.pubshares33[0][0], ptr, 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
    return 1;
}

int secp256k1_chilldkg_hostpubkey_gen(const secp256k1_context *ctx, unsigned char *hostpubkey33, const unsigned char *hostseckey32) {
    secp256k1_scalar seckey;
    secp256k1_gej pubkeyj;
    secp256k1_ge pubkey;
    int overflow;
    int ret;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    ARG_CHECK(hostpubkey33 != NULL);
    ARG_CHECK(hostseckey32 != NULL);

    memset(hostpubkey33, 0, 33);
    secp256k1_scalar_set_b32(&seckey, hostseckey32, &overflow);
    ret = !overflow & !secp256k1_scalar_is_zero(&seckey);
    /* Branching on the validity of the host secret key is fine: whether the
     * caller's key is in range 1..n-1 is not secret. */
    secp256k1_declassify(ctx, &ret, sizeof(ret));
    if (ret) {
        /* pubkey_gen_plain in the reference (IndividualPubkey of BIP 327). */
        secp256k1_ecmult_gen_gej(&ctx->ecmult_gen_ctx, &pubkeyj, &seckey);
        secp256k1_ge_set_gej(&pubkey, &pubkeyj);
        /* The host public key is not secret; declassify it so that the
         * variable-time serialization is allowed to branch on it. */
        secp256k1_declassify(ctx, &pubkey, sizeof(pubkey));
        secp256k1_chilldkg_point_save(hostpubkey33, &pubkey);
        secp256k1_gej_clear(&pubkeyj);
    }
    secp256k1_scalar_clear(&seckey);
    return ret;
}

int secp256k1_chilldkg_params_hash(const secp256k1_context *ctx, unsigned char *hash32, const unsigned char *hostpubkeys33, size_t n_participants, uint32_t threshold) {
    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(hash32 != NULL);
    ARG_CHECK(hostpubkeys33 != NULL);

    memset(hash32, 0, 32);
    if (!secp256k1_chilldkg_params_validate(hostpubkeys33, n_participants, threshold)) {
        return 0;
    }
    secp256k1_chilldkg_params_hash_internal(secp256k1_get_hash_context(ctx), hash32, hostpubkeys33, n_participants, threshold);
    return 1;
}

size_t secp256k1_chilldkg_participant_msg1_len(size_t n_participants, uint32_t threshold) {
    if (threshold < 1 || n_participants < 1 || threshold > n_participants || n_participants > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
        return 0;
    }
    return 33 * (size_t)threshold + 32 * n_participants + 97;
}

size_t secp256k1_chilldkg_coordinator_msg1_len(size_t n_participants, uint32_t threshold) {
    if (threshold < 1 || n_participants < 1 || threshold > n_participants || n_participants > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
        return 0;
    }
    return 162 * n_participants + 33 * (size_t)(threshold - 1);
}

size_t secp256k1_chilldkg_participant_msg2_len(void) {
    return 64;
}

size_t secp256k1_chilldkg_coordinator_msg2_len(size_t n_participants) {
    if (n_participants < 1 || n_participants > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
        return 0;
    }
    return 64 * n_participants;
}

size_t secp256k1_chilldkg_recovery_data_len(size_t n_participants, uint32_t threshold) {
    if (threshold < 1 || n_participants < 1 || threshold > n_participants || n_participants > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
        return 0;
    }
    return 4 + 33 * (size_t)threshold + 162 * n_participants;
}

int secp256k1_chilldkg_participant_step1(const secp256k1_context *ctx, secp256k1_chilldkg_participant_state1 *state1, unsigned char *pmsg1, const unsigned char *hostseckey32, const unsigned char *hostpubkeys33, size_t n_participants, uint32_t threshold, const unsigned char *random32) {
    secp256k1_chilldkg_encpedpop_participant_state enc_state;
    unsigned char hostpubkey33[33];
    uint32_t participant_id = 0;
    size_t pmsg1_len;
    size_t i;
    int found = 0;
    int ret = 0;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    ARG_CHECK(state1 != NULL);
    ARG_CHECK(pmsg1 != NULL);
    ARG_CHECK(hostseckey32 != NULL);
    ARG_CHECK(hostpubkeys33 != NULL);
    ARG_CHECK(random32 != NULL);

    memset(state1->data, 0, sizeof(state1->data));
    pmsg1_len = secp256k1_chilldkg_participant_msg1_len(n_participants, threshold);

    /* params_validate, then hostpubkey_gen (HostSeckeyError if invalid). */
    if (secp256k1_chilldkg_params_validate(hostpubkeys33, n_participants, threshold)
        && secp256k1_chilldkg_hostpubkey_gen(ctx, hostpubkey33, hostseckey32)) {
        /* hostpubkeys.index(hostpubkey); the params validation rules out
         * duplicates, so there is at most one match. */
        for (i = 0; i < n_participants; i++) {
            if (secp256k1_memcmp_var(hostpubkeys33 + 33 * i, hostpubkey33, 33) == 0) {
                participant_id = (uint32_t)i;
                found = 1;
                break;
            }
        }
        /* HostSeckeyError if there is no match; RandomnessError if the
         * randomness is all zeroes (guards against a malfunctioning random
         * number generator). Whether the randomness is all zeroes does not
         * leak anything about its value otherwise (if it is, the session
         * fails), so the check can be declassified. */
        {
            int random_is_zero = secp256k1_is_zero_array(random32, 32);
            secp256k1_declassify(ctx, &random_is_zero, sizeof(random_is_zero));
            if (found && !random_is_zero) {
                /* In EncPedPop, both the seed and the deckey are the host
                 * secret key, and the enckeys are the host public keys (see
                 * chilldkg_ref/chilldkg.py participant_step1). */
                ret = secp256k1_chilldkg_encpedpop_participant_step1(ctx, &enc_state, pmsg1, hostseckey32, hostseckey32, hostpubkeys33, threshold, participant_id, random32, n_participants);
            }
        }
    }

    if (ret) {
        secp256k1_chilldkg_participant_state1_save(state1, &enc_state);
    } else {
        memset(state1->data, 0, sizeof(state1->data));
        if (pmsg1_len > 0) {
            memset(pmsg1, 0, pmsg1_len);
        }
    }
    return ret;
}

secp256k1_chilldkg_fault secp256k1_chilldkg_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_participant_state2 *state2, unsigned char *sig64, uint32_t *fault_index, secp256k1_chilldkg_participant_inv_data *inv_data, const secp256k1_chilldkg_participant_state1 *state1, const unsigned char *hostseckey32, const unsigned char *cmsg1, const unsigned char *aux_rand32) {
    secp256k1_chilldkg_encpedpop_participant_state enc_state;
    secp256k1_chilldkg_encpedpop_participant_inv_data inv_data_i;
    secp256k1_chilldkg_participant_state2_internal state2i;
    unsigned char hostpubkey33[33];
    secp256k1_scalar enc_secshares[SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
    secp256k1_chilldkg_fault fault = SECP256K1_CHILLDKG_INVALID_INPUT;
    size_t n, t, enc_cmsg_len, eq_input_len;
    uint32_t participant_id;
    size_t i;
    int overflow;

    VERIFY_CHECK(ctx != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    SECP256K1_CHILLDKG_ARG_CHECK(state2 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(sig64 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(state1 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(hostseckey32 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(cmsg1 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(aux_rand32 != NULL);

    memset(&state2i, 0, sizeof(state2i));
    memset(&inv_data_i, 0, sizeof(inv_data_i));
    memset(state2->data, 0, sizeof(state2->data));
    memset(sig64, 0, 64);
    *fault_index = UINT32_MAX;
    if (inv_data != NULL) {
        memset(inv_data->data, 0, sizeof(inv_data->data));
    }

    if (!secp256k1_chilldkg_participant_state1_load(ctx, &enc_state, state1)) {
        return SECP256K1_CHILLDKG_INVALID_INPUT;
    }
    t = enc_state.simpl_state.t;
    n = enc_state.simpl_state.n;
    participant_id = enc_state.simpl_state.participant_id;

    /* HostSeckeyError if the host secret key is invalid or does not match the
     * one used in participant_step1. */
    if (!secp256k1_chilldkg_hostpubkey_gen(ctx, hostpubkey33, hostseckey32)
        || secp256k1_memcmp_var(hostpubkey33, enc_state.enckeys33[participant_id], 33) != 0) {
        return SECP256K1_CHILLDKG_INVALID_INPUT;
    }

    /* CoordinatorMsg1.from_bytes: cmsg1 = enc_cmsg || enc_secshares, where
     * enc_cmsg has 97*n + 33*(t-1) + 33*n bytes. The encrypted shares are
     * parsed checked; an overflow blames the coordinator (MsgParseError ->
     * FaultyCoordinatorError in the reference). */
    enc_cmsg_len = 97 * n + 33 * (t - 1) + 33 * n;
    for (i = 0; i < n; i++) {
        secp256k1_scalar_set_b32(&enc_secshares[i], cmsg1 + enc_cmsg_len + 32 * i, &overflow);
        if (overflow) {
            fault = SECP256K1_CHILLDKG_FAULTY_COORDINATOR;
            goto cleanup;
        }
    }

    eq_input_len = 4 + 33 * t + 66 * n;
    fault = secp256k1_chilldkg_encpedpop_participant_step2(ctx, &state2i.dkg_output, state2i.eq_input, fault_index, inv_data != NULL ? &inv_data_i : NULL, &enc_state, hostseckey32, cmsg1, enc_cmsg_len, &enc_secshares[participant_id]);
    if (fault != SECP256K1_CHILLDKG_OK) {
        if (fault == SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR && inv_data != NULL) {
            secp256k1_chilldkg_participant_inv_data_save(inv_data, &inv_data_i);
        }
        goto cleanup;
    }

    /* Include the enc_secshares in eq_input to ensure that participants agree
     * on all shares, which in turn ensures that they have the right recovery
     * data. */
    for (i = 0; i < n; i++) {
        secp256k1_scalar_get_b32(state2i.eq_input + eq_input_len + 32 * i, &enc_secshares[i]);
    }
    eq_input_len += 32 * n;
    state2i.t = (uint32_t)t;
    state2i.n = (uint32_t)n;

    /* pmsg2 = certeq_participant_step(hostseckey, participant_id, eq_input, aux_rand) */
    if (!secp256k1_chilldkg_certeq_participant_step(ctx, sig64, hostseckey32, participant_id, state2i.eq_input, eq_input_len, aux_rand32)) {
        /* Unreachable: the host secret key was validated above. */
        fault = SECP256K1_CHILLDKG_INVALID_INPUT;
        memset(sig64, 0, 64);
        goto cleanup;
    }
    secp256k1_chilldkg_participant_state2_save(state2, &state2i);

cleanup:
    for (i = 0; i < n; i++) {
        secp256k1_scalar_clear(&enc_secshares[i]);
    }
    /* state2i and inv_data_i contain the secret share / decryption pads. */
    secp256k1_memclear_explicit(&state2i, sizeof(state2i));
    secp256k1_memclear_explicit(&inv_data_i, sizeof(inv_data_i));
    return fault;
}

secp256k1_chilldkg_fault secp256k1_chilldkg_participant_finalize(const secp256k1_context *ctx, unsigned char *secshare32, unsigned char *thresh_pk33, unsigned char *pubshares33, unsigned char *recovery, uint32_t *fault_index, const secp256k1_chilldkg_participant_state2 *state2, const unsigned char *cmsg2) {
    secp256k1_chilldkg_participant_state2_internal state2i;
    size_t n, t, eq_input_len;

    VERIFY_CHECK(ctx != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    SECP256K1_CHILLDKG_ARG_CHECK(secshare32 != 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(state2 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(cmsg2 != NULL);

    memset(secshare32, 0, 32);
    memset(thresh_pk33, 0, 33);
    *fault_index = UINT32_MAX;

    /* On a state load failure (uninitialized state), only secshare32 and
     * thresh_pk33 are zeroed: the sizes of pubshares33 and recovery depend on
     * the state contents. */
    if (!secp256k1_chilldkg_participant_state2_load(ctx, &state2i, state2)) {
        return SECP256K1_CHILLDKG_INVALID_INPUT;
    }
    t = state2i.t;
    n = state2i.n;
    eq_input_len = 4 + 33 * t + 98 * n;

    memset(pubshares33, 0, 33 * n);
    memset(recovery, 0, 4 + 33 * t + 162 * n);

    /* certeq_verify: the host public keys are part of eq_input (at offset
     * 4 + 33*t). An invalid signature in the certificate blames the
     * coordinator; fault_index receives the index of the first invalid
     * signature as diagnostic information. */
    if (!secp256k1_chilldkg_certeq_verify(ctx, state2i.eq_input + 4 + 33 * t, n, state2i.eq_input, eq_input_len, cmsg2, fault_index)) {
        secp256k1_memclear_explicit(&state2i, sizeof(state2i));
        return SECP256K1_CHILLDKG_FAULTY_COORDINATOR;
    }

    memcpy(secshare32, state2i.dkg_output.secshare32, 32);
    memcpy(thresh_pk33, state2i.dkg_output.thresh_pk33, 33);
    memcpy(pubshares33, &state2i.dkg_output.pubshares33[0][0], 33 * n);
    /* recovery data = eq_input || cert */
    memcpy(recovery, state2i.eq_input, eq_input_len);
    memcpy(recovery + eq_input_len, cmsg2, 64 * n);

    /* state2i contains the secret share. */
    secp256k1_memclear_explicit(&state2i, sizeof(state2i));
    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. The
     * bounds check must not be a VERIFY_CHECK: a persisted state object can
     * be tampered with, and in noverify builds the check would be compiled
     * out and the out-of-range t/n would index fixed-size arrays in
     * callers. */
    ARG_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;
}

size_t secp256k1_chilldkg_investigation_msg_len(size_t n_participants) {
    if (n_participants < 1 || n_participants > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
        return 0;
    }
    return 65 * n_participants;
}

/* Maximum length of the recovery data: 4 + 33*t + 162*n bytes with
 * t, n <= SECP256K1_CHILLDKG_MAX_PARTICIPANTS. */
#define SECP256K1_CHILLDKG_MAX_RECOVERY_LEN (4 + 195 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS)

/* Parse the self-delimiting recovery data (deserialize_recovery_data in
 * chilldkg_ref/chilldkg.py):
 *   u32be(t) || sum_coms (33*t) || hostpubkeys (33*n) || pubnonces (33*n) ||
 *   enc_secshares (32*n) || cert (64*n)
 * where n = (recovery_len - 4 - 33*t) / 162. sum_coms may contain the point
 * at infinity; the enc_secshares are parsed checked. The hostpubkeys33,
 * pubnonces33 and cert outputs point into the recovery buffer. Returns 1 on
 * success and 0 on invalid recovery data (RecoveryDataError in the
 * reference). */
static int secp256k1_chilldkg_deserialize_recovery_data(uint32_t *t_out, size_t *n_out, secp256k1_ge *sum_coms, secp256k1_scalar *enc_secshares, const unsigned char **hostpubkeys33, const unsigned char **pubnonces33, const unsigned char **cert, const unsigned char *recovery, size_t recovery_len) {
    size_t t, n, rest_len, i;
    const unsigned char *ptr;
    int overflow;

    if (recovery_len < 4 || recovery_len > SECP256K1_CHILLDKG_MAX_RECOVERY_LEN) {
        return 0;
    }
    t = secp256k1_read_be32(recovery);
    /* Bound t before using it as a loop bound over the fixed-size sum_coms
     * array: the recovery data is untrusted input. (The reference gets this
     * implicitly from Python's dynamically-sized lists.) */
    if (t < 1 || t > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
        return 0;
    }
    rest_len = recovery_len - 4;
    if (rest_len < 33 * t || (rest_len - 33 * t) % 162 != 0) {
        return 0;
    }
    n = (rest_len - 33 * t) / 162;
    if (n > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
        return 0;
    }
    ptr = recovery + 4;
    for (i = 0; i < t; i++) {
        if (!secp256k1_chilldkg_point_load(&sum_coms[i], ptr + 33 * i)) {
            return 0;
        }
    }
    ptr += 33 * t;
    *hostpubkeys33 = ptr;
    ptr += 33 * n;
    *pubnonces33 = ptr;
    ptr += 33 * n;
    for (i = 0; i < n; i++) {
        secp256k1_scalar_set_b32(&enc_secshares[i], ptr + 32 * i, &overflow);
        if (overflow) {
            return 0;
        }
    }
    ptr += 32 * n;
    *cert = ptr;
    *t_out = (uint32_t)t;
    *n_out = n;
    return 1;
}

/* Shared implementation of participant_recover and coordinator_recover
 * (recover in chilldkg_ref/chilldkg.py). hostseckey32 is NULL for the
 * coordinator. secshare32 is NULL for the coordinator. */
static secp256k1_chilldkg_fault secp256k1_chilldkg_recover(const secp256k1_context *ctx, unsigned char *secshare32, unsigned char *thresh_pk33, unsigned char *pubshares33, unsigned char *hostpubkeys33_out, size_t *n_participants_out, uint32_t *threshold_out, uint32_t *fault_index, const unsigned char *hostseckey32, const unsigned char *recovery, size_t recovery_len) {
    secp256k1_ge sum_coms[SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
    secp256k1_ge sum_coms_tweaked[SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
    secp256k1_ge pubtweak;
    secp256k1_ge pubshare;
    secp256k1_gej pubsharej;
    secp256k1_scalar enc_secshares[SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
    secp256k1_scalar tweak;
    secp256k1_scalar secshare;
    unsigned char enc_context[4 + 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
    unsigned char hostpubkey33[33];
    const unsigned char *hostpubkeys33 = NULL;
    const unsigned char *pubnonces33 = NULL;
    const unsigned char *cert = NULL;
    secp256k1_chilldkg_fault fault = SECP256K1_CHILLDKG_INVALID_INPUT;
    uint32_t participant_id = 0;
    uint32_t t = 0;
    size_t n = 0;
    size_t i;
    int found = 0;

    if (secshare32 != NULL) {
        memset(secshare32, 0, 32);
    }
    memset(thresh_pk33, 0, 33);
    *n_participants_out = 0;
    *threshold_out = 0;
    if (fault_index != NULL) {
        *fault_index = UINT32_MAX;
    }

    /* Deserialize; invalid recovery data is a RecoveryDataError in the
     * reference. */
    if (!secp256k1_chilldkg_deserialize_recovery_data(&t, &n, sum_coms, enc_secshares, &hostpubkeys33, &pubnonces33, &cert, recovery, recovery_len)) {
        goto cleanup;
    }
    /* params_validate; invalid session parameters in the recovery data are a
     * RecoveryDataError in the reference. */
    if (!secp256k1_chilldkg_params_validate(hostpubkeys33, n, t)) {
        goto cleanup;
    }
    memset(pubshares33, 0, 33 * n);
    memset(hostpubkeys33_out, 0, 33 * n);

    /* Verify the certificate over eq_input (the recovery data without the
     * cert). An invalid certificate is a RecoveryDataError in the reference
     * (the index of the invalid signature is not reported there). */
    {
        uint32_t cert_fault_index = UINT32_MAX;
        if (!secp256k1_chilldkg_certeq_verify(ctx, hostpubkeys33, n, recovery, recovery_len - 64 * n, cert, &cert_fault_index)) {
            if (fault_index != NULL) {
                *fault_index = UINT32_MAX;
            }
            goto cleanup;
        }
    }

    /* HostSeckeyError if the host secret key is invalid or does not match any
     * host public key in the recovery data. */
    if (hostseckey32 != NULL) {
        if (!secp256k1_chilldkg_hostpubkey_gen(ctx, hostpubkey33, hostseckey32)) {
            goto cleanup;
        }
        for (i = 0; i < n; i++) {
            if (secp256k1_memcmp_var(hostpubkeys33 + 33 * i, hostpubkey33, 33) == 0) {
                participant_id = (uint32_t)i;
                found = 1;
                break;
            }
        }
        if (!found) {
            goto cleanup;
        }
    }

    /* Recompute the tweaked threshold public key and pubshares. A failure of
     * invalid_taproot_commit (infinity commitment to the secret, or tweak
     * hash overflow) is not caught by the reference (it would raise a
     * non-protocol error); report it as invalid recovery data. */
    if (!secp256k1_chilldkg_vss_invalid_taproot_commit(ctx, sum_coms_tweaked, &tweak, &pubtweak, sum_coms, t)) {
        goto cleanup;
    }
    secp256k1_chilldkg_point_save(thresh_pk33, &sum_coms_tweaked[0]);
    for (i = 0; i < n; i++) {
        secp256k1_chilldkg_vss_pubshare(&pubsharej, sum_coms_tweaked, t, (uint32_t)i);
        secp256k1_ge_set_gej_var(&pubshare, &pubsharej);
        secp256k1_chilldkg_point_save(pubshares33 + 33 * i, &pubshare);
    }
    memcpy(hostpubkeys33_out, hostpubkeys33, 33 * n);
    *n_participants_out = n;
    *threshold_out = t;

    if (hostseckey32 != NULL) {
        /* Decrypt our secshare and apply the tweak. */
        secp256k1_chilldkg_encpedpop_serialize_enc_context(enc_context, t, hostpubkeys33, n);
        fault = secp256k1_chilldkg_encpedpop_decrypt_sum(ctx, &secshare, fault_index, hostseckey32, hostpubkeys33 + 33 * participant_id, pubnonces33, enc_context, 4 + 33 * n, participant_id, &enc_secshares[participant_id], n);
        if (fault != SECP256K1_CHILLDKG_OK) {
            memset(thresh_pk33, 0, 33);
            memset(pubshares33, 0, 33 * n);
            memset(hostpubkeys33_out, 0, 33 * n);
            *n_participants_out = 0;
            *threshold_out = 0;
            goto cleanup;
        }
        secp256k1_scalar_add(&secshare, &secshare, &tweak);
        /* This is just a sanity check (assert in the reference): the cert is
         * valid, so an equivalent check was done during the actual session. */
        if (!secp256k1_chilldkg_point_load(&pubshare, pubshares33 + 33 * participant_id)) {
            VERIFY_CHECK(0);
        }
        if (!secp256k1_chilldkg_vss_verify_secshare(ctx, &secshare, &pubshare)) {
            VERIFY_CHECK(0);
        }
        secp256k1_scalar_get_b32(secshare32, &secshare);
    }
    fault = SECP256K1_CHILLDKG_OK;

cleanup:
    secp256k1_scalar_clear(&tweak);
    secp256k1_scalar_clear(&secshare);
    for (i = 0; i < SECP256K1_CHILLDKG_MAX_PARTICIPANTS; i++) {
        secp256k1_scalar_clear(&enc_secshares[i]);
    }
    return fault;
}

secp256k1_chilldkg_fault secp256k1_chilldkg_participant_recover(const secp256k1_context *ctx, unsigned char *secshare32, unsigned char *thresh_pk33, unsigned char *pubshares33, unsigned char *hostpubkeys33_out, size_t *n_participants_out, uint32_t *threshold_out, uint32_t *fault_index, const unsigned char *hostseckey32, const unsigned char *recovery, size_t recovery_len) {
    VERIFY_CHECK(ctx != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    SECP256K1_CHILLDKG_ARG_CHECK(secshare32 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(thresh_pk33 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(pubshares33 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(hostpubkeys33_out != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(n_participants_out != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(threshold_out != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(hostseckey32 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(recovery != NULL);

    return secp256k1_chilldkg_recover(ctx, secshare32, thresh_pk33, pubshares33, hostpubkeys33_out, n_participants_out, threshold_out, fault_index, hostseckey32, recovery, recovery_len);
}

secp256k1_chilldkg_fault secp256k1_chilldkg_coordinator_recover(const secp256k1_context *ctx, unsigned char *thresh_pk33, unsigned char *pubshares33, unsigned char *hostpubkeys33_out, size_t *n_participants_out, uint32_t *threshold_out, const unsigned char *recovery, size_t recovery_len) {
    VERIFY_CHECK(ctx != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    SECP256K1_CHILLDKG_ARG_CHECK(thresh_pk33 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(pubshares33 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(hostpubkeys33_out != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(n_participants_out != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(threshold_out != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(recovery != NULL);

    return secp256k1_chilldkg_recover(ctx, NULL, thresh_pk33, pubshares33, hostpubkeys33_out, n_participants_out, threshold_out, NULL, NULL, recovery, recovery_len);
}

/* recovery_ack_message: pad33("BIP DKG/recovery acknowledgment") ||
 * u32be(participant_id) || recovery_data. msg must hold 37 + recovery_len
 * bytes. */
static void secp256k1_chilldkg_recovery_ack_message(unsigned char *msg, const unsigned char *recovery, size_t recovery_len, uint32_t participant_id) {
    secp256k1_chilldkg_pad33(msg, "BIP DKG/recovery acknowledgment");
    secp256k1_write_be32(msg + 33, participant_id);
    memcpy(msg + 37, recovery, recovery_len);
}

/* Check that the recovery data parses and matches the given session
 * parameters (part of participant_recovery_ack_sign and
 * participant_recovery_acks_verify in the reference). Returns 1 on match, 0
 * otherwise. */
static int secp256k1_chilldkg_recovery_matches_params(const unsigned char *recovery, size_t recovery_len, const unsigned char *hostpubkeys33, size_t n, uint32_t t) {
    secp256k1_ge sum_coms[SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
    secp256k1_scalar enc_secshares[SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
    const unsigned char *hostpubkeys33_rec = NULL;
    const unsigned char *pubnonces33 = NULL;
    const unsigned char *cert = NULL;
    uint32_t t_rec = 0;
    size_t n_rec = 0;
    size_t i;
    int ret;

    ret = secp256k1_chilldkg_deserialize_recovery_data(&t_rec, &n_rec, sum_coms, enc_secshares, &hostpubkeys33_rec, &pubnonces33, &cert, recovery, recovery_len)
        && t_rec == t
        && n_rec == n
        && secp256k1_memcmp_var(hostpubkeys33_rec, hostpubkeys33, 33 * n) == 0;
    for (i = 0; i < SECP256K1_CHILLDKG_MAX_PARTICIPANTS; i++) {
        secp256k1_scalar_clear(&enc_secshares[i]);
    }
    return ret;
}

int secp256k1_chilldkg_recovery_ack_sign(const secp256k1_context *ctx, unsigned char *sig64, const unsigned char *hostseckey32, const unsigned char *hostpubkeys33, size_t n_participants, uint32_t threshold, const unsigned char *recovery, size_t recovery_len, const unsigned char *aux_rand32) {
    unsigned char msg[37 + SECP256K1_CHILLDKG_MAX_RECOVERY_LEN];
    unsigned char hostpubkey33[33];
    uint32_t participant_id = 0;
    size_t i;
    int found = 0;
    int ret = 0;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    ARG_CHECK(sig64 != NULL);
    ARG_CHECK(hostseckey32 != NULL);
    ARG_CHECK(hostpubkeys33 != NULL);
    ARG_CHECK(recovery != NULL);
    ARG_CHECK(aux_rand32 != NULL);

    memset(sig64, 0, 64);
    if (recovery_len > SECP256K1_CHILLDKG_MAX_RECOVERY_LEN) {
        return 0;
    }
    if (secp256k1_chilldkg_params_validate(hostpubkeys33, n_participants, threshold)
        && secp256k1_chilldkg_hostpubkey_gen(ctx, hostpubkey33, hostseckey32)) {
        for (i = 0; i < n_participants; i++) {
            if (secp256k1_memcmp_var(hostpubkeys33 + 33 * i, hostpubkey33, 33) == 0) {
                participant_id = (uint32_t)i;
                found = 1;
                break;
            }
        }
        /* RecoveryDataError if the recovery data is invalid or does not match
         * the provided session parameters. */
        if (found && secp256k1_chilldkg_recovery_matches_params(recovery, recovery_len, hostpubkeys33, n_participants, threshold)) {
            secp256k1_chilldkg_recovery_ack_message(msg, recovery, recovery_len, participant_id);
            ret = secp256k1_chilldkg_schnorrsig_sign(ctx, sig64, msg, 37 + recovery_len, hostseckey32, aux_rand32, "BIP0340");
        }
    }
    secp256k1_memclear_explicit(msg, sizeof(msg));
    return ret;
}

secp256k1_chilldkg_fault secp256k1_chilldkg_recovery_acks_verify(const secp256k1_context *ctx, uint32_t *fault_index, const unsigned char *hostpubkeys33, size_t n_participants, uint32_t threshold, const unsigned char *recovery, size_t recovery_len, const unsigned char *const *ack_sigs64) {
    unsigned char msg[37 + SECP256K1_CHILLDKG_MAX_RECOVERY_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(fault_index != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(hostpubkeys33 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(recovery != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(ack_sigs64 != NULL);

    *fault_index = UINT32_MAX;
    if (recovery_len > SECP256K1_CHILLDKG_MAX_RECOVERY_LEN
        || !secp256k1_chilldkg_params_validate(hostpubkeys33, n_participants, threshold)
        || !secp256k1_chilldkg_recovery_matches_params(recovery, recovery_len, hostpubkeys33, n_participants, threshold)) {
        return SECP256K1_CHILLDKG_INVALID_INPUT;
    }

    for (i = 0; i < n_participants; i++) {
        secp256k1_chilldkg_recovery_ack_message(msg, recovery, recovery_len, (uint32_t)i);
        /* Dropping the sign byte from hostpubkeys33[i] is okay because the
         * message commits to the full host public key. */
        if (!secp256k1_chilldkg_schnorrsig_verify(ctx, ack_sigs64[i], msg, 37 + recovery_len, hostpubkeys33 + 33 * i + 1, "BIP0340")) {
            /* InvalidRecoveryAckError(i) in the reference (a
             * FaultyParticipantError). */
            *fault_index = (uint32_t)i;
            return SECP256K1_CHILLDKG_FAULTY_PARTICIPANT;
        }
    }
    return SECP256K1_CHILLDKG_OK;
}

secp256k1_chilldkg_fault secp256k1_chilldkg_coordinator_investigate(const secp256k1_context *ctx, unsigned char *cinv, uint32_t *fault_index, const unsigned char *const *pmsgs1, const unsigned char *hostpubkeys33, size_t n_participants, uint32_t threshold, uint32_t participant_id) {
    secp256k1_ge com[SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
    secp256k1_ge pubshare;
    secp256k1_gej pubsharej;
    secp256k1_scalar share;
    size_t cinv_len;
    size_t i;
    size_t j;
    int overflow;

    VERIFY_CHECK(ctx != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    SECP256K1_CHILLDKG_ARG_CHECK(cinv != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(pmsgs1 != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(hostpubkeys33 != NULL);

    *fault_index = UINT32_MAX;
    cinv_len = secp256k1_chilldkg_investigation_msg_len(n_participants);
    if (!secp256k1_chilldkg_params_validate(hostpubkeys33, n_participants, threshold)
        || participant_id >= n_participants) {
        if (cinv_len > 0) {
            memset(cinv, 0, cinv_len);
        }
        return SECP256K1_CHILLDKG_INVALID_INPUT;
    }
    memset(cinv, 0, cinv_len);

    /* For the given participant, collect the encrypted partial secshare and
     * the partial pubshare of every dealer. Every participant message is
     * parsed fully (ParticipantMsg1.from_bytes in the reference); a malformed
     * message blames its sender. The pubnonces are not parsed (they are plain
     * byte strings in the reference). */
    for (j = 0; j < n_participants; j++) {
        const unsigned char *pmsg1 = pmsgs1[j];
        for (i = 0; i < threshold; i++) {
            if (!secp256k1_chilldkg_point_load(&com[i], pmsg1 + 33 * i)) {
                *fault_index = (uint32_t)j;
                return SECP256K1_CHILLDKG_FAULTY_PARTICIPANT;
            }
        }
        for (i = 0; i < n_participants; i++) {
            secp256k1_scalar_set_b32(&share, pmsg1 + 33 * threshold + 97 + 32 * i, &overflow);
            if (overflow) {
                secp256k1_scalar_clear(&share);
                *fault_index = (uint32_t)j;
                return SECP256K1_CHILLDKG_FAULTY_PARTICIPANT;
            }
            if (i == participant_id) {
                secp256k1_scalar_get_b32(cinv + 32 * j, &share);
            }
        }
        secp256k1_scalar_clear(&share);
        secp256k1_chilldkg_vss_pubshare(&pubsharej, com, threshold, participant_id);
        secp256k1_ge_set_gej_var(&pubshare, &pubsharej);
        secp256k1_chilldkg_point_save(cinv + 32 * n_participants + 33 * j, &pubshare);
    }
    return SECP256K1_CHILLDKG_OK;
}

secp256k1_chilldkg_fault secp256k1_chilldkg_participant_investigate(const secp256k1_context *ctx, uint32_t *fault_index, const secp256k1_chilldkg_participant_inv_data *inv_data, const unsigned char *cinv) {
    secp256k1_chilldkg_encpedpop_participant_inv_data inv_data_i;
    secp256k1_chilldkg_fault fault;

    VERIFY_CHECK(ctx != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(inv_data != NULL);
    SECP256K1_CHILLDKG_ARG_CHECK(cinv != NULL);

    *fault_index = UINT32_MAX;
    if (!secp256k1_chilldkg_participant_inv_data_load(ctx, &inv_data_i, inv_data)) {
        return SECP256K1_CHILLDKG_INVALID_INPUT;
    }
    fault = secp256k1_chilldkg_encpedpop_participant_investigate(ctx, fault_index, &inv_data_i, cinv, 65 * (size_t)inv_data_i.simpl.n);
    /* inv_data_i contains the untweaked secshare and the pads. */
    secp256k1_memclear_explicit(&inv_data_i, sizeof(inv_data_i));
    return fault;
}

#endif
