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

#ifndef SECP256K1_MODULE_CHILLDKG_UTIL_H
#define SECP256K1_MODULE_CHILLDKG_UTIL_H

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

#include "../../group.h"
#include "../../hash.h"
#include "../../scalar.h"

/* The public header defines SECP256K1_CHILLDKG_MAX_PARTICIPANTS and the fault
 * enum secp256k1_chilldkg_fault (SECP256K1_CHILLDKG_OK, ...), which the
 * internal SimplPedPop/EncPedPop functions use directly as their return
 * codes. */

/* This file contains the internal primitives of the ChillDKG module that
 * mirror chilldkg_ref/util.py, secp256k1lab/bip340.py and secp256k1lab/ecdh.py
 * of the bip-frost-dkg reference implementation. Byte-exactness with the
 * reference is the goal; the tagged hash strings below are copied verbatim
 * from the Python sources. */

/* Tagged hash initializers (BIP 340 tagged hashes). All ChillDKG tags share
 * the "BIP DKG/" prefix (BIP_TAG in chilldkg_ref/util.py); "TapTweak" is the
 * BIP 341 tag. */
static void secp256k1_chilldkg_sha256_tagged_params_hash(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha);
static void secp256k1_chilldkg_sha256_tagged_encpedpop_seed(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha);
static void secp256k1_chilldkg_sha256_tagged_simplpedpop_aux(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha);
static void secp256k1_chilldkg_sha256_tagged_encpedpop_secnonce(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha);
static void secp256k1_chilldkg_sha256_tagged_encpedpop_ecdh(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha);
static void secp256k1_chilldkg_sha256_tagged_self_pad(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha);
static void secp256k1_chilldkg_sha256_tagged_vss_coeffs(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha);
static void secp256k1_chilldkg_sha256_tagged_taptweak(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha);

/* Serialize a group element to 33 bytes: the point at infinity maps to 33
 * zero bytes, otherwise the standard SEC compressed encoding (0x02/0x03
 * prefix) is used. Mirrors GE.to_bytes_compressed_with_infinity. */
static void secp256k1_chilldkg_point_save(unsigned char *out33, const secp256k1_ge *p);

/* Parse a 33-byte group element encoding: 33 zero bytes map to the point at
 * infinity, otherwise standard SEC compressed parsing applies. Returns 1 on
 * success and 0 if the encoding is invalid (wrong prefix, x >= p, or x not on
 * the curve). On failure, *p is set to the point at infinity. Mirrors
 * GE.from_bytes_compressed_with_infinity. */
static int secp256k1_chilldkg_point_load(secp256k1_ge *p, const unsigned char *in33);

/* Serialize the x coordinate of a non-infinity group element (32 bytes).
 * Mirrors GE.to_bytes_xonly, which asserts non-infinity. */
static void secp256k1_chilldkg_xonly_save(unsigned char *out32, const secp256k1_ge *p);

/* Parse a 32-byte x-only group element encoding (even y). Returns 1 on
 * success, 0 if x >= p or x is not on the curve. Mirrors
 * GE.from_bytes_xonly. */
static int secp256k1_chilldkg_xonly_load(secp256k1_ge *p, const unsigned char *in32);

/* Write str (at most 33 bytes) zero-padded to 33 bytes. Used to build the
 * domain separation prefixes of the CertEq and recovery acknowledgment
 * messages (see certeq_message/recovery_ack_message in chilldkg_ref). */
static void secp256k1_chilldkg_pad33(unsigned char *out33, const char *str);

/* BIP 340 Schnorr signing/verification with a parameterized tag prefix and
 * arbitrary-length messages, mirroring secp256k1lab/bip340.py's schnorr_sign
 * and schnorr_verify. The tags used for aux, nonce and challenge hashes are
 * tag_prefix || "/aux", tag_prefix || "/nonce" and tag_prefix ||
 * "/challenge". ChillDKG uses tag_prefix "BIP DKG/pop message" for proofs of
 * possession (POP_MSG_TAG in simplpedpop.py) and the standard "BIP0340"
 * prefix for CertEq and recovery acknowledgments.
 *
 * sign returns 1 on success and 0 if seckey32 is not in range 1..n-1 or the
 * derived nonce is zero (negligible probability). On failure, sig64 is set to
 * zero. verify returns 1 if the signature is valid and 0 otherwise. */
static int secp256k1_chilldkg_schnorrsig_sign(const secp256k1_context *ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const unsigned char *seckey32, const unsigned char *aux_rand32, const char *tag_prefix);
static int secp256k1_chilldkg_schnorrsig_verify(const secp256k1_context *ctx, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const unsigned char *pubkey32, const char *tag_prefix);

/* Compute the EncPedPop ECDH pad (encpedpop.py `ecdh`):
 *   shared = SHA256(compressed(seckey * their_point))   [libsecp256k1 ECDH]
 *   out = Scalar.from_bytes_wrapping(TH("BIP DKG/encpedpop ecdh",
 *         shared || my_pubkey33 || their_pubkey33 || context))  if sending
 *   out = Scalar.from_bytes_wrapping(TH("BIP DKG/encpedpop ecdh",
 *         shared || their_pubkey33 || my_pubkey33 || context))  otherwise
 * In the protocol, my/their_pubkey33 are the sender's pubnonce and the
 * receiver's host pubkey (in this order in the hash input, regardless of
 * sending), and context is u32be(receiver_index) || enc_context.
 * their_point must be the parsed their_pubkey33 and must not be infinity
 * (enforced by VERIFY_CHECK). seckey must be nonzero. */
static void secp256k1_chilldkg_encpedpop_ecdh(const secp256k1_context *ctx, secp256k1_scalar *out, const secp256k1_scalar *seckey, const secp256k1_ge *their_point, const unsigned char *my_pubkey33, const unsigned char *their_pubkey33, const unsigned char *context, size_t context_len, int sending);

/* Compute the EncPedPop symmetric pad for encrypting to ourselves
 * (encpedpop.py `self_pad`):
 *   out = Scalar.from_bytes_wrapping(TH("BIP DKG/encaps_multi self_pad",
 *         symkey32 || nonce33 || context))
 * where symkey32 is the host seckey, nonce33 is the own pubnonce and context
 * is u32be(own_index) || enc_context. */
static void secp256k1_chilldkg_encpedpop_self_pad(const secp256k1_hash_ctx *hash_ctx, secp256k1_scalar *out, const unsigned char *symkey32, const unsigned char *nonce33, const unsigned char *context, size_t context_len);

/* Compute the session parameters hash (chilldkg.py `params_hash`):
 *   out32 = TH("BIP DKG/params_hash", u32be(t) || hostpubkeys[0] || ... || hostpubkeys[n-1])
 * where hostpubkeys33 is an array of n 33-byte compressed host public keys. */
static void secp256k1_chilldkg_params_hash_internal(const secp256k1_hash_ctx *hash_ctx, unsigned char *out32, const unsigned char *hostpubkeys33, size_t n, uint32_t t);

#endif
