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

#ifndef SECP256K1_MODULE_CHILLDKG_ENCPEDPOP_H
#define SECP256K1_MODULE_CHILLDKG_ENCPEDPOP_H

#include "../../../include/secp256k1.h"

#include "util.h"
#include "simplpedpop.h"
#include "../../group.h"
#include "../../scalar.h"

/* This file contains the internal EncPedPop sub-protocol of the ChillDKG
 * module, mirroring chilldkg_ref/encpedpop.py of the bip-frost-dkg reference
 * implementation. EncPedPop wraps SimplPedPop and encrypts the partial secret
 * shares towards their recipients with an additively homomorphic "Hashed
 * ElGamal" multi-recipient KEM, so that an untrusted coordinator can aggregate
 * the encrypted shares per recipient.
 *
 * Messages are byte strings:
 *   pmsg (participant -> coordinator): simpl_pmsg (33*t + 64) || pubnonce (33)
 *        || enc_shares (32*n)
 *   cmsg (coordinator -> participants): simpl_cmsg (97*n + 33*(t-1)) ||
 *        pubnonces (33*n)
 * The EncPedPop contribution to the equality-check input appends to the
 * SimplPedPop contribution:
 *   eq_input = ... || enckeys (33*n) || pubnonces (33*n) */

/* Serialize the encryption context (encpedpop.py serialize_enc_context):
 *   out = u32be(t) || enckeys[0] || ... || enckeys[n-1]
 * enckeys33 is an array of n 33-byte compressed host public keys; out must
 * hold 4 + 33*n bytes. */
static void secp256k1_chilldkg_encpedpop_serialize_enc_context(unsigned char *out, uint32_t t, const unsigned char *enckeys33, size_t n);

/* Compute the pads for encrypting to all n recipients (encpedpop.py
 * encaps_multi): for recipient i, the context is u32be(i) || enc_context; the
 * pad for our own index is the symmetric self pad (no ECDH), the others are
 * ECDH pads between the one-time secnonce and the recipient's host public key
 * (sender-side ordering in the pad hash).
 *
 * enc_context must be at most 4 + 33*SECP256K1_CHILLDKG_MAX_PARTICIPANTS
 * bytes. Returns 1 on success and 0 if some host public key is invalid
 * (caller input error; the session parameters must be validated beforehand).
 * On failure, all pads are cleared. */
static int secp256k1_chilldkg_encpedpop_encaps_multi(const secp256k1_context *ctx, secp256k1_scalar *pads, const secp256k1_scalar *secnonce, const unsigned char *pubnonce33, const unsigned char *deckey32, const unsigned char *enckeys33, const unsigned char *enc_context, size_t enc_context_len, uint32_t participant_id, size_t n);

/* Encrypt the n plaintext shares towards the n recipients (encpedpop.py
 * encrypt_multi): ciphertexts[i] = plaintexts[i] + pad[i]. The encryption is
 * additively homomorphic modulo the group order, so the coordinator can sum
 * the ciphertexts per recipient. Returns 1 on success, 0 on invalid host
 * public keys (see encaps_multi). */
static int secp256k1_chilldkg_encpedpop_encrypt_multi(const secp256k1_context *ctx, secp256k1_scalar *ciphertexts, const secp256k1_scalar *secnonce, const unsigned char *pubnonce33, const unsigned char *deckey32, const unsigned char *enckeys33, const unsigned char *enc_context, size_t enc_context_len, uint32_t participant_id, const secp256k1_scalar *plaintexts, size_t n);

/* Compute the pads for decrypting the shares received from all n senders
 * (encpedpop.py decaps_multi): the context is u32be(participant_id) ||
 * enc_context for every sender; the pad for our own index is the symmetric
 * self pad, the others are ECDH pads between our deckey and the sender's
 * pubnonce (receiver-side ordering in the pad hash). deckey32 is our host
 * secret key and enckey33 our host public key.
 *
 * Returns SECP256K1_CHILLDKG_OK on success,
 * SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR (with *fault_index) if
 * some sender's pubnonce is invalid, and SECP256K1_CHILLDKG_INVALID_INPUT if
 * deckey32 is invalid. On failure, all pads are cleared. */
static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_decaps_multi(const secp256k1_context *ctx, secp256k1_scalar *pads, uint32_t *fault_index, const unsigned char *deckey32, const unsigned char *enckey33, const unsigned char *pubnonces33, const unsigned char *enc_context, size_t enc_context_len, uint32_t participant_id, size_t n);

/* Decrypt the sum of the encrypted shares addressed to us (encpedpop.py
 * decrypt_sum): out = sum_ciphertexts - sum of the n pads. Returns like
 * decaps_multi. */
static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_decrypt_sum(const secp256k1_context *ctx, secp256k1_scalar *out, uint32_t *fault_index, const unsigned char *deckey32, const unsigned char *enckey33, const unsigned char *pubnonces33, const unsigned char *enc_context, size_t enc_context_len, uint32_t participant_id, const secp256k1_scalar *sum_ciphertexts, size_t n);

/* Participant state (encpedpop.py ParticipantState). The enckeys are kept
 * because participant_step2 needs them to recompute the encryption context
 * and the pads. */
typedef struct {
    secp256k1_chilldkg_simplpedpop_participant_state simpl_state;
    unsigned char pubnonce33[33];
    unsigned char enckeys33[SECP256K1_CHILLDKG_MAX_PARTICIPANTS][33];
} secp256k1_chilldkg_encpedpop_participant_state;

/* Participant step 1 (encpedpop.py participant_step1). Derives
 *   simpl_seed  = TH("BIP DKG/encpedpop seed", seed32 || random32 || enc_context)
 *   aux_rand    = TH("BIP DKG/simplpedpop aux", simpl_seed)
 *   secnonce    = TH("BIP DKG/encpedpop secnonce", simpl_seed)
 *   pubnonce    = pubkey_gen_plain(secnonce)
 * (in ChillDKG, both seed32 and deckey32 are the host secret key), runs
 * SimplPedPop participant_step1 on simpl_seed, encrypts the shares, and
 * outputs the participant message pmsg (33*t + 64 + 33 + 32*n bytes).
 *
 * Returns 1 on success and 0 on invalid input (t < 1, t > n, n exceeding
 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS, participant_id >= n, invalid host
 * public keys) or on negligible-probability derivation failures. */
static int secp256k1_chilldkg_encpedpop_participant_step1(const secp256k1_context *ctx, secp256k1_chilldkg_encpedpop_participant_state *state, unsigned char *pmsg, const unsigned char *seed32, const unsigned char *deckey32, const unsigned char *enckeys33, uint32_t t, uint32_t participant_id, const unsigned char *random32, size_t n);

/* Investigation data captured when participant_step2 fails with
 * SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR
 * (encpedpop.py ParticipantInvestigationData). Contains secrets (the
 * untweaked secshare and the pads); clear after use. */
typedef struct {
    secp256k1_chilldkg_simplpedpop_participant_inv_data simpl;
    secp256k1_scalar enc_secshare;
    secp256k1_scalar pads[SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
} secp256k1_chilldkg_encpedpop_participant_inv_data;

/* Participant step 2 (encpedpop.py participant_step2): verify that the
 * coordinator echoed our pubnonce, decrypt our secshare from the summed
 * encrypted secshare, and run SimplPedPop participant_step2. Outputs the DKG
 * output and the EncPedPop eq_input (4 + 33*t + 33*n + 33*n bytes).
 *
 * cmsg must be exactly 97*n + 33*(t-1) + 33*n bytes. Returns
 * SECP256K1_CHILLDKG_OK on success; otherwise a fault code (with
 * *fault_index set when applicable): FAULTY_COORDINATOR if cmsg is malformed
 * or the pubnonce echo is wrong, FAULTY_PARTICIPANT_OR_COORDINATOR(i) if
 * sender i's pubnonce is invalid or their pop/commitment is bad, and
 * UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR if the decrypted secshare does
 * not match the pubshare; in the latter case, and only in that case, inv_data
 * (if not NULL) is filled for
 * secp256k1_chilldkg_encpedpop_participant_investigate. */
static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, uint32_t *fault_index, secp256k1_chilldkg_encpedpop_participant_inv_data *inv_data, const secp256k1_chilldkg_encpedpop_participant_state *state, const unsigned char *deckey32, const unsigned char *cmsg, size_t cmsg_len, const secp256k1_scalar *enc_secshare);

/* Determine the faulty party after an
 * UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR fault of participant_step2
 * (encpedpop.py participant_investigate). cinv is the coordinator's
 * investigation message for this participant (65*n bytes: n encrypted partial
 * secshares || n partial pubshares). Returns FAULTY_COORDINATOR if the
 * investigation message is malformed or if the coordinator is to blame,
 * FAULTY_PARTICIPANT_OR_COORDINATOR(i) (with *fault_index = i) if participant
 * i is to blame, and SECP256K1_CHILLDKG_INVALID_INPUT if all inputs are
 * consistent (i.e., the function was called even though no fault occurred;
 * the reference raises a RuntimeError in this case). */
static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_investigate(const secp256k1_context *ctx, uint32_t *fault_index, const secp256k1_chilldkg_encpedpop_participant_inv_data *inv_data, const unsigned char *cinv, size_t cinv_len);

/* Coordinator step (encpedpop.py coordinator_step): parse the n participant
 * messages (each 33*t + 64 + 33 + 32*n bytes), aggregate SimplPedPop, and sum
 * the encrypted shares per recipient (exploiting the additive homomorphism).
 * Outputs cmsg (97*n + 33*(t-1) + 33*n bytes), the coordinator's DKG output
 * (no secshare), the EncPedPop eq_input (4 + 33*t + 33*n + 33*n bytes) and
 * the n summed encrypted secshares. (In pure EncPedPop the coordinator would
 * send enc_secshares[i] to participant i only; ChillDKG broadcasts them.)
 *
 * Returns SECP256K1_CHILLDKG_OK on success and
 * SECP256K1_CHILLDKG_FAULTY_PARTICIPANT (with *fault_index) if a participant
 * message is malformed (invalid commitment, or an encrypted share that
 * overflows the group order). */
static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_coordinator_step(const secp256k1_context *ctx, unsigned char *cmsg, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, secp256k1_scalar *enc_secshares, uint32_t *fault_index, const unsigned char *const *pmsgs, uint32_t t, const unsigned char *enckeys33, size_t n);

#endif
