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

#ifndef SECP256K1_MODULE_FROST_SESSION_IMPL_H
#define SECP256K1_MODULE_FROST_SESSION_IMPL_H

#include <string.h>

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

#include "keygen.h"
#include "session.h"
#include "../../eckey.h"
#include "../../ecmult.h"
#include "../../hash.h"
#include "../../util.h"

/* Initializes SHA256 with the BIP 445 "BIP0445/aux" tag. */
static void secp256k1_frost_sha256_tagged_aux(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha) {
    secp256k1_sha256_initialize_tagged(hash_ctx, sha, (const unsigned char *)"BIP0445/aux", sizeof("BIP0445/aux") - 1);
}

/* Initializes SHA256 with the BIP 445 "BIP0445/nonce" tag. */
static void secp256k1_frost_sha256_tagged_nonce(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha) {
    secp256k1_sha256_initialize_tagged(hash_ctx, sha, (const unsigned char *)"BIP0445/nonce", sizeof("BIP0445/nonce") - 1);
}

/* Initializes SHA256 with the BIP 445 "BIP0445/noncecoef" tag. */
static void secp256k1_frost_sha256_tagged_noncecoef(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha) {
    secp256k1_sha256_initialize_tagged(hash_ctx, sha, (const unsigned char *)"BIP0445/noncecoef", sizeof("BIP0445/noncecoef") - 1);
}

/* Initializes SHA256 with the BIP 445 "BIP0445/deterministic/nonce" tag. */
static void secp256k1_frost_sha256_tagged_deterministic_nonce(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha) {
    secp256k1_sha256_initialize_tagged(hash_ctx, sha, (const unsigned char *)"BIP0445/deterministic/nonce", sizeof("BIP0445/deterministic/nonce") - 1);
}

/* Writes data into the hash, framed with a big-endian length prefix of
 * prefix_size bytes, matching the framing of optional and variable-length
 * inputs in the BIP 445 nonce_hash and det_nonce_hash algorithms. If data is
 * NULL, only the (zero) length prefix is written. Returns 0 without writing
 * anything if data_len does not fit into prefix_size bytes, which corresponds
 * to the OverflowError raised by the reference implementation. */
static int secp256k1_frost_sha256_write_prefixed(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *sha, unsigned int prefix_size, const unsigned char *data, size_t data_len) {
    unsigned char prefix[8];
    size_t len = data_len;
    unsigned int i;

    /* The spec requires length prefixes to be between 1 and 8 bytes
     * (inclusive) */
    VERIFY_CHECK(prefix_size >= 1 && prefix_size <= 8);
    VERIFY_CHECK(data != NULL || data_len == 0);
    for (i = prefix_size; i-- > 0; ) {
        prefix[i] = len & 0xFF;
        len >>= 8;
    }
    if (len != 0) {
        /* The length does not fit into the prefix. Only reachable for the
         * 4-byte extra_in prefix, and only on platforms whose size_t is wider
         * than 32 bits. */
        return 0;
    }
    secp256k1_sha256_write(hash_ctx, sha, prefix, prefix_size);
    if (data != NULL) {
        secp256k1_sha256_write(hash_ctx, sha, data, data_len);
    }
    return 1;
}

static const unsigned char secp256k1_frost_secnonce_magic[4] = { 0x5c, 0xcf, 0xb9, 0x99 };

static void secp256k1_frost_secnonce_save(secp256k1_frost_secnonce *secnonce, const secp256k1_scalar *k) {
    memcpy(&secnonce->data[0], secp256k1_frost_secnonce_magic, 4);
    secp256k1_scalar_get_b32(&secnonce->data[4], &k[0]);
    secp256k1_scalar_get_b32(&secnonce->data[36], &k[1]);
}

static int secp256k1_frost_secnonce_load(const secp256k1_context* ctx, secp256k1_scalar *k, const secp256k1_frost_secnonce *secnonce) {
    int is_zero0, is_zero1;
    ARG_CHECK(secp256k1_memcmp_var(&secnonce->data[0], secp256k1_frost_secnonce_magic, 4) == 0);
    /* We make very sure that the nonce isn't invalidated by checking the
     * values in addition to the magic. Both halves must be nonzero, matching
     * the secnonce validation of the BIP 445 Sign algorithm ("first/second
     * secnonce value is out of range"). */
    is_zero0 = secp256k1_is_zero_array(&secnonce->data[4], 32);
    is_zero1 = secp256k1_is_zero_array(&secnonce->data[36], 32);
    secp256k1_declassify(ctx, &is_zero0, sizeof(is_zero0));
    secp256k1_declassify(ctx, &is_zero1, sizeof(is_zero1));
    ARG_CHECK(!is_zero0);
    ARG_CHECK(!is_zero1);

    secp256k1_scalar_set_b32(&k[0], &secnonce->data[4], NULL);
    secp256k1_scalar_set_b32(&k[1], &secnonce->data[36], NULL);
    return 1;
}

static const unsigned char secp256k1_frost_pubnonce_magic[4] = { 0x70, 0xd5, 0x1c, 0xff };

/* Saves two group elements into a pubnonce. Requires that none of the
 * provided group elements is infinity. */
static void secp256k1_frost_pubnonce_save(secp256k1_frost_pubnonce *nonce, const secp256k1_ge *ges) {
    int i;
    memcpy(&nonce->data[0], secp256k1_frost_pubnonce_magic, 4);
    for (i = 0; i < 2; i++) {
        VERIFY_CHECK(!secp256k1_ge_is_infinity(&ges[i]));
        secp256k1_ge_to_bytes(&nonce->data[4 + 64*i], &ges[i]);
    }
}

/* Loads two group elements from a pubnonce. Returns 1 unless the nonce wasn't
 * properly initialized */
static int secp256k1_frost_pubnonce_load(const secp256k1_context* ctx, secp256k1_ge *ges, const secp256k1_frost_pubnonce *nonce) {
    int i;

    ARG_CHECK(secp256k1_memcmp_var(&nonce->data[0], secp256k1_frost_pubnonce_magic, 4) == 0);
    for (i = 0; i < 2; i++) {
        secp256k1_ge_from_bytes(&ges[i], &nonce->data[4 + 64*i]);
    }
    return 1;
}

static const unsigned char secp256k1_frost_aggnonce_magic[4] = { 0xcd, 0x45, 0x0f, 0x4a };

/* Saves two group elements into an aggnonce. In contrast to a pubnonce, the
 * group elements are allowed to be the point at infinity. */
static void secp256k1_frost_aggnonce_save(secp256k1_frost_aggnonce *nonce, const secp256k1_ge *ges) {
    int i;
    memcpy(&nonce->data[0], secp256k1_frost_aggnonce_magic, 4);
    for (i = 0; i < 2; i++) {
        secp256k1_ge_to_bytes_ext(&nonce->data[4 + 64*i], &ges[i]);
    }
}

static int secp256k1_frost_aggnonce_load(const secp256k1_context* ctx, secp256k1_ge *ges, const secp256k1_frost_aggnonce *nonce) {
    int i;

    ARG_CHECK(secp256k1_memcmp_var(&nonce->data[0], secp256k1_frost_aggnonce_magic, 4) == 0);
    for (i = 0; i < 2; i++) {
        secp256k1_ge_from_bytes_ext(&ges[i], &nonce->data[4 + 64*i]);
    }
    return 1;
}

static const unsigned char secp256k1_frost_session_magic[4] = { 0x34, 0xb5, 0x27, 0x3d };

/* A session consists of
 * - 4 byte session magic
 * - 32 byte x-only encoding of the final nonce R
 * - 32 byte nonce coefficient b
 * - 32 byte signature challenge hash e
 * - 32 byte scalar s_part = e*g*tacc that is added to the aggregate partial
 *   signature
 * - 4 byte number of signers u (big-endian)
 * - 1 byte with the parity of the final nonce in bit 0 and the parity of
 *   g*gacc in bit 1
 */
static void secp256k1_frost_session_save(secp256k1_frost_session *session, const secp256k1_frost_session_internal *session_i) {
    unsigned char *ptr = session->data;

    memcpy(ptr, secp256k1_frost_session_magic, 4);
    ptr += 4;
    memcpy(ptr, session_i->fin_nonce, 32);
    ptr += 32;
    secp256k1_scalar_get_b32(ptr, &session_i->noncecoef);
    ptr += 32;
    secp256k1_scalar_get_b32(ptr, &session_i->challenge);
    ptr += 32;
    secp256k1_scalar_get_b32(ptr, &session_i->s_part);
    ptr += 32;
    secp256k1_write_be32(ptr, session_i->n_signers);
    ptr += 4;
    *ptr = (session_i->fin_nonce_parity & 1) | ((session_i->g_times_gacc_parity & 1) << 1);
}

static int secp256k1_frost_session_load(const secp256k1_context* ctx, secp256k1_frost_session_internal *session_i, const secp256k1_frost_session *session) {
    const unsigned char *ptr = session->data;

    ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_frost_session_magic, 4) == 0);
    ptr += 4;
    memcpy(session_i->fin_nonce, ptr, 32);
    ptr += 32;
    secp256k1_scalar_set_b32(&session_i->noncecoef, ptr, NULL);
    ptr += 32;
    secp256k1_scalar_set_b32(&session_i->challenge, ptr, NULL);
    ptr += 32;
    secp256k1_scalar_set_b32(&session_i->s_part, ptr, NULL);
    ptr += 32;
    session_i->n_signers = secp256k1_read_be32(ptr);
    ptr += 4;
    session_i->fin_nonce_parity = *ptr & 1;
    session_i->g_times_gacc_parity = (*ptr >> 1) & 1;
    return 1;
}

static const unsigned char secp256k1_frost_partial_sig_magic[4] = { 0x58, 0x0a, 0x17, 0x4e };

static void secp256k1_frost_partial_sig_save(secp256k1_frost_partial_sig *sig, const secp256k1_scalar *s) {
    memcpy(&sig->data[0], secp256k1_frost_partial_sig_magic, 4);
    secp256k1_scalar_get_b32(&sig->data[4], s);
}

static int secp256k1_frost_partial_sig_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_partial_sig *sig) {
    int overflow;

    ARG_CHECK(secp256k1_memcmp_var(&sig->data[0], secp256k1_frost_partial_sig_magic, 4) == 0);
    secp256k1_scalar_set_b32(s, &sig->data[4], &overflow);
    /* Parsed signatures can not overflow */
    VERIFY_CHECK(!overflow);
    return 1;
}

int secp256k1_frost_pubnonce_parse(const secp256k1_context* ctx, secp256k1_frost_pubnonce *nonce, const unsigned char *in66) {
    secp256k1_ge ges[2];
    int i;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(nonce != NULL);
    memset(nonce, 0, sizeof(*nonce));
    ARG_CHECK(in66 != NULL);

    for (i = 0; i < 2; i++) {
        /* A 33-byte compressed encoding can never be the point at infinity */
        if (!secp256k1_eckey_pubkey_parse(&ges[i], &in66[33*i], 33)) {
            return 0;
        }
        if (!secp256k1_ge_is_in_correct_subgroup(&ges[i])) {
            return 0;
        }
    }
    secp256k1_frost_pubnonce_save(nonce, ges);
    return 1;
}

int secp256k1_frost_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_frost_pubnonce *nonce) {
    secp256k1_ge ges[2];
    int i;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(out66 != NULL);
    memset(out66, 0, 66);
    ARG_CHECK(nonce != NULL);

    if (!secp256k1_frost_pubnonce_load(ctx, ges, nonce)) {
        return 0;
    }
    for (i = 0; i < 2; i++) {
        /* serialize must succeed because the point was just loaded and is not
         * the point at infinity */
        secp256k1_eckey_pubkey_serialize33(&ges[i], &out66[33*i]);
    }
    return 1;
}

int secp256k1_frost_aggnonce_parse(const secp256k1_context* ctx, secp256k1_frost_aggnonce *nonce, const unsigned char *in66) {
    secp256k1_ge ges[2];
    int i;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(nonce != NULL);
    memset(nonce, 0, sizeof(*nonce));
    ARG_CHECK(in66 != NULL);

    for (i = 0; i < 2; i++) {
        /* 33 zero bytes encode the point at infinity (BIP 445 cbytes_ext) */
        if (!secp256k1_ge_parse_ext(&ges[i], &in66[33*i])) {
            return 0;
        }
        if (!secp256k1_ge_is_infinity(&ges[i])
                && !secp256k1_ge_is_in_correct_subgroup(&ges[i])) {
            return 0;
        }
    }
    secp256k1_frost_aggnonce_save(nonce, ges);
    return 1;
}

int secp256k1_frost_aggnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_frost_aggnonce *nonce) {
    secp256k1_ge ges[2];
    int i;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(out66 != NULL);
    memset(out66, 0, 66);
    ARG_CHECK(nonce != NULL);

    if (!secp256k1_frost_aggnonce_load(ctx, ges, nonce)) {
        return 0;
    }
    for (i = 0; i < 2; i++) {
        /* A point at infinity is encoded as 33 zero bytes (BIP 445
         * cbytes_ext) */
        secp256k1_ge_serialize_ext(&out66[33*i], &ges[i]);
    }
    return 1;
}

int secp256k1_frost_partial_sig_parse(const secp256k1_context* ctx, secp256k1_frost_partial_sig *sig, const unsigned char *in32) {
    secp256k1_scalar tmp;
    int overflow;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(sig != NULL);
    ARG_CHECK(in32 != NULL);

    /* Ensure that using the signature will fail if parsing fails (and the
     * user doesn't check the return value). */
    memset(sig, 0, sizeof(*sig));

    secp256k1_scalar_set_b32(&tmp, in32, &overflow);
    if (overflow) {
        return 0;
    }
    secp256k1_frost_partial_sig_save(sig, &tmp);
    return 1;
}

int secp256k1_frost_partial_sig_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_frost_partial_sig *sig) {
    secp256k1_scalar s;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(out32 != NULL);
    ARG_CHECK(sig != NULL);

    if (!secp256k1_frost_partial_sig_load(ctx, &s, sig)) {
        return 0;
    }
    secp256k1_scalar_get_b32(out32, &s);
    return 1;
}

/* Nonce derivation function of NonceGen (BIP 445). Computes the secret nonces
 * k[0], k[1] from the session randomness and the optional inputs. Returns 0 if
 * one of the variable-length inputs is too long for its length prefix, 1
 * otherwise. All secret intermediates (the masked randomness, the hash states,
 * the hash output) are cleansed before this function returns. */
static int secp256k1_frost_nonce_function(const secp256k1_hash_ctx *hash_ctx, secp256k1_scalar *k, const unsigned char *session_secrand, const unsigned char *secshare32, const unsigned char *pubshare33, const unsigned char *thresh_pk32, const unsigned char *msg, size_t msglen, const unsigned char *extra_in, size_t extra_in_len) {
    secp256k1_sha256 sha;
    unsigned char rand[32];
    unsigned char i;
    unsigned char msg_present;
    int ret = 1;

    if (secshare32 != NULL) {
        secp256k1_frost_sha256_tagged_aux(hash_ctx, &sha);
        secp256k1_sha256_write(hash_ctx, &sha, session_secrand, 32);
        secp256k1_sha256_finalize(hash_ctx, &sha, rand);
        for (i = 0; i < 32; i++) {
            rand[i] ^= secshare32[i];
        }
    } else {
        memcpy(rand, session_secrand, sizeof(rand));
    }

    secp256k1_frost_sha256_tagged_nonce(hash_ctx, &sha);
    secp256k1_sha256_write(hash_ctx, &sha, rand, sizeof(rand));
    ret &= secp256k1_frost_sha256_write_prefixed(hash_ctx, &sha, 1, pubshare33, pubshare33 != NULL ? 33 : 0);
    ret &= secp256k1_frost_sha256_write_prefixed(hash_ctx, &sha, 1, thresh_pk32, thresh_pk32 != NULL ? 32 : 0);
    msg_present = msg != NULL;
    secp256k1_sha256_write(hash_ctx, &sha, &msg_present, 1);
    if (msg_present) {
        ret &= secp256k1_frost_sha256_write_prefixed(hash_ctx, &sha, 8, msg, msglen);
    }
    ret &= secp256k1_frost_sha256_write_prefixed(hash_ctx, &sha, 4, extra_in, extra_in_len);

    for (i = 0; i < 2; i++) {
        unsigned char buf[32];
        secp256k1_sha256 sha_tmp = sha;
        secp256k1_sha256_write(hash_ctx, &sha_tmp, &i, 1);
        secp256k1_sha256_finalize(hash_ctx, &sha_tmp, buf);
        secp256k1_scalar_set_b32(&k[i], buf, NULL);

        /* Attempt to erase secret data */
        secp256k1_memclear_explicit(buf, sizeof(buf));
        secp256k1_sha256_clear(&sha_tmp);
    }
    secp256k1_memclear_explicit(rand, sizeof(rand));
    secp256k1_sha256_clear(&sha);
    return ret;
}

int secp256k1_frost_nonce_gen(const secp256k1_context* ctx, secp256k1_frost_secnonce *secnonce, secp256k1_frost_pubnonce *pubnonce, unsigned char *session_secrand32, const unsigned char *secshare32, const secp256k1_pubkey *pubshare, const unsigned char *thresh_pk32, const unsigned char *msg, size_t msglen, const unsigned char *extra_in, size_t extra_in_len) {
    secp256k1_scalar k[2];
    secp256k1_ge nonce_pts[2];
    secp256k1_gej nonce_ptj[2];
    unsigned char pubshare33[33];
    unsigned char *pubshare33_ptr = NULL;
    int i;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(secnonce != NULL);
    memset(secnonce, 0, sizeof(*secnonce));
    ARG_CHECK(pubnonce != NULL);
    memset(pubnonce, 0, sizeof(*pubnonce));
    ARG_CHECK(session_secrand32 != NULL);
    ARG_CHECK(msg != NULL || msglen == 0);
    ARG_CHECK(extra_in != NULL || extra_in_len == 0);
    ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));

    /* Check that the secshare is valid to be able to sign with it later. */
    if (secshare32 != NULL) {
        secp256k1_scalar secshare;
        int valid = secp256k1_scalar_set_b32_seckey(&secshare, secshare32);
        secp256k1_scalar_clear(&secshare);
        /* We can declassify because branching on valid only leaks whether the
         * provided secret share is a valid secret key, which is not
         * secret. */
        secp256k1_declassify(ctx, &valid, sizeof(valid));
        if (!valid) {
            secp256k1_memzero_explicit(session_secrand32, 32);
            return 0;
        }
    }
    if (pubshare != NULL) {
        secp256k1_ge pubshare_ge;
        if (!secp256k1_pubkey_load(ctx, &pubshare_ge, pubshare)) {
            secp256k1_memzero_explicit(session_secrand32, 32);
            return 0;
        }
        secp256k1_eckey_pubkey_serialize33(&pubshare_ge, pubshare33);
        pubshare33_ptr = pubshare33;
    }

    if (!secp256k1_frost_nonce_function(secp256k1_get_hash_context(ctx), k, session_secrand32, secshare32, pubshare33_ptr, thresh_pk32, msg, msglen, extra_in, extra_in_len)) {
        /* extra_in is too long to be committed to in a 4-byte length prefix. */
        secp256k1_scalar_clear(&k[0]);
        secp256k1_scalar_clear(&k[1]);
        secp256k1_memzero_explicit(session_secrand32, 32);
        return 0;
    }
    {
        /* k_i == 0 has negligible probability, so its occurrence is not
         * secret. Fail cleanly instead of asserting like the reference
         * implementation. */
        int k_zero = secp256k1_scalar_is_zero(&k[0]) | secp256k1_scalar_is_zero(&k[1]);
        secp256k1_declassify(ctx, &k_zero, sizeof(k_zero));
        if (k_zero) {
            secp256k1_scalar_clear(&k[0]);
            secp256k1_scalar_clear(&k[1]);
            secp256k1_memzero_explicit(session_secrand32, 32);
            return 0;
        }
    }

    secp256k1_frost_secnonce_save(secnonce, k);

    /* Compute pubnonce as two gejs */
    for (i = 0; i < 2; i++) {
        secp256k1_ecmult_gen_gej(&ctx->ecmult_gen_ctx, &nonce_ptj[i], &k[i]);
        secp256k1_scalar_clear(&k[i]);
    }

    /* Batch convert to two public ges */
    secp256k1_ge_set_all_gej(nonce_pts, nonce_ptj, 2);
    for (i = 0; i < 2; i++) {
        secp256k1_gej_clear(&nonce_ptj[i]);
        secp256k1_declassify(ctx, &nonce_pts[i], sizeof(nonce_pts[i]));
    }

    /* None of the nonce_pts will be infinity because k != 0 */
    secp256k1_frost_pubnonce_save(pubnonce, nonce_pts);

    /* Wipe the session_secrand32 buffer to prevent the caller from using
     * nonce_gen multiple times with the same buffer. */
    secp256k1_memzero_explicit(session_secrand32, 32);
    return 1;
}

int secp256k1_frost_nonce_agg(const secp256k1_context* ctx, secp256k1_frost_aggnonce *aggnonce, size_t *error_index, const secp256k1_frost_pubnonce *const *pubnonces, size_t n_pubnonces) {
    secp256k1_gej aggnonce_ptsj[2];
    secp256k1_ge aggnonce_pts[2];
    size_t i;
    int j;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(aggnonce != NULL);
    memset(aggnonce, 0, sizeof(*aggnonce));
    ARG_CHECK(pubnonces != NULL);
    ARG_CHECK(n_pubnonces > 0);

    secp256k1_gej_set_infinity(&aggnonce_ptsj[0]);
    secp256k1_gej_set_infinity(&aggnonce_ptsj[1]);

    for (i = 0; i < n_pubnonces; i++) {
        secp256k1_ge nonce_pts[2];
        /* A NULL array element indicates a missing contribution. This is the
         * identifiable-abort path, not an API usage error. */
        if (pubnonces[i] == NULL) {
            if (error_index != NULL) {
                *error_index = i;
            }
            return 0;
        }
        if (!secp256k1_frost_pubnonce_load(ctx, nonce_pts, pubnonces[i])) {
            if (error_index != NULL) {
                *error_index = i;
            }
            return 0;
        }
        for (j = 0; j < 2; j++) {
            secp256k1_gej_add_ge_var(&aggnonce_ptsj[j], &aggnonce_ptsj[j], &nonce_pts[j], NULL);
        }
    }
    /* The column sums may be the point at infinity; this is not an error (BIP
     * 445 NonceAgg). */
    secp256k1_ge_set_all_gej_var(aggnonce_pts, aggnonce_ptsj, 2);
    secp256k1_frost_aggnonce_save(aggnonce, aggnonce_pts);
    return 1;
}

/* Copies ids into out and sorts them in ascending order (insertion sort).
 * Requires n_ids <= SECP256K1_FROST_MAX_PARTICIPANTS. The signers are a set;
 * sorting keeps the nonce coefficient independent of the order of ids (BIP 445
 * serialize_ids). */
static void secp256k1_frost_sort_ids(uint32_t *out, const uint32_t *ids, size_t n_ids) {
    size_t i;

    VERIFY_CHECK(n_ids <= SECP256K1_FROST_MAX_PARTICIPANTS);
    memcpy(out, ids, n_ids * sizeof(*out));
    for (i = 1; i < n_ids; i++) {
        uint32_t v = out[i];
        size_t j = i;
        while (j > 0 && out[j - 1] > v) {
            out[j] = out[j - 1];
            j--;
        }
        out[j] = v;
    }
}

/* Implements the validate_session_params algorithm of BIP 445: checks the
 * threshold parameters, the range and uniqueness of the signer ids and, if
 * pubshares is non-NULL, that all pubshares parse and that the threshold
 * public key derived from them equals the untweaked threshold public key of
 * the tweak cache. Returns 1 if the parameters are valid, 0 otherwise. */
static int secp256k1_frost_validate_session_params(const secp256k1_context *ctx, const uint32_t *ids, const secp256k1_pubkey *pubshares, size_t n_signers, size_t n_participants, uint32_t threshold, const secp256k1_frost_tweak_cache_internal *cache_i) {
    size_t i;

    if (threshold < 1 || (size_t)threshold > n_participants) {
        return 0;
    }
    if (n_participants > SECP256K1_FROST_MAX_PARTICIPANTS) {
        return 0;
    }
    if (n_signers < (size_t)threshold || n_signers > n_participants) {
        return 0;
    }
    for (i = 0; i < n_signers; i++) {
        if ((size_t)ids[i] >= n_participants) {
            return 0;
        }
    }
    /* Rejects duplicate ids; the range check above covers the rest. */
    if (!secp256k1_frost_ids_are_valid(ids, n_signers)) {
        return 0;
    }
    if (pubshares != NULL) {
        secp256k1_ge pubshares_ge[SECP256K1_FROST_MAX_PARTICIPANTS];
        secp256k1_gej derived;
        secp256k1_ge derived_ge;

        for (i = 0; i < n_signers; i++) {
            if (!secp256k1_pubkey_load(ctx, &pubshares_ge[i], &pubshares[i])) {
                return 0;
            }
        }
        /* derive_thresh_pubkey fails if the result is the point at infinity */
        if (!secp256k1_frost_derive_thresh_pubkey(&derived, ids, pubshares_ge, n_signers)) {
            return 0;
        }
        secp256k1_ge_set_gej_var(&derived_ge, &derived);
        /* The derived threshold public key must match the untweaked threshold
         * public key (Q_0) stored in the tweak cache. */
        if (!secp256k1_ge_eq_var(&derived_ge, &cache_i->thresh_pk)) {
            return 0;
        }
    }
    return 1;
}

/* Implements the GetSessionValues algorithm of BIP 445 (given already
 * validated session parameters): computes the nonce coefficient b, the final
 * nonce R, the BIP340 challenge e and s_part = e*g*tacc, where g = -1 iff the
 * (tweaked) threshold public key has odd Y. aggnonce_pts holds the two
 * aggregate nonce components, each of which may be the point at infinity.
 * Returns 0 in the (negligible probability) case that b == 0 or e == 0, 1
 * otherwise. */
static int secp256k1_frost_get_session_values(const secp256k1_context *ctx, secp256k1_frost_session_internal *session_i, secp256k1_ge *aggnonce_pts, const uint32_t *ids, size_t n_signers, const secp256k1_frost_tweak_cache_internal *cache_i, const unsigned char *msg, size_t msglen) {
    const secp256k1_hash_ctx *hash_ctx = secp256k1_get_hash_context(ctx);
    secp256k1_sha256 sha;
    uint32_t sorted_ids[SECP256K1_FROST_MAX_PARTICIPANTS];
    unsigned char buf[4];
    unsigned char aggnonce66[66];
    unsigned char pk32[32];
    unsigned char hash32[32];
    secp256k1_gej rj;
    secp256k1_ge r;
    size_t i;
    int pk_odd;

    VERIFY_CHECK(n_signers <= SECP256K1_FROST_MAX_PARTICIPANTS);

    /* b = tagged_hash("BIP0445/noncecoef", ser32(u) || sorted ser32 ids ||
     * aggnonce || xbytes(Q) || msg) */
    secp256k1_frost_sha256_tagged_noncecoef(hash_ctx, &sha);
    secp256k1_write_be32(buf, (uint32_t)n_signers);
    secp256k1_sha256_write(hash_ctx, &sha, buf, sizeof(buf));
    secp256k1_frost_sort_ids(sorted_ids, ids, n_signers);
    for (i = 0; i < n_signers; i++) {
        secp256k1_write_be32(buf, sorted_ids[i]);
        secp256k1_sha256_write(hash_ctx, &sha, buf, sizeof(buf));
    }
    /* An aggregate nonce component at infinity is encoded as 33 zero bytes
     * (BIP 445 cbytes_ext) */
    secp256k1_ge_serialize_ext(&aggnonce66[0], &aggnonce_pts[0]);
    secp256k1_ge_serialize_ext(&aggnonce66[33], &aggnonce_pts[1]);
    secp256k1_sha256_write(hash_ctx, &sha, aggnonce66, sizeof(aggnonce66));
    /* The tweaked threshold public key Q is never the point at infinity and
     * its x-coordinate is normalized (loaded from storage). */
    secp256k1_fe_get_b32(pk32, &cache_i->pk.x);
    secp256k1_sha256_write(hash_ctx, &sha, pk32, sizeof(pk32));
    secp256k1_sha256_write(hash_ctx, &sha, msg, msglen);
    secp256k1_sha256_finalize(hash_ctx, &sha, hash32);
    secp256k1_scalar_set_b32(&session_i->noncecoef, hash32, NULL);
    secp256k1_memclear_explicit(hash32, sizeof(hash32));
    if (secp256k1_scalar_is_zero(&session_i->noncecoef)) {
        /* b == 0 can only occur with negligible probability */
        return 0;
    }

    /* R = R1 + b*R2; R = G if R is the point at infinity */
    if (secp256k1_ge_is_infinity(&aggnonce_pts[1])) {
        secp256k1_gej_set_infinity(&rj);
    } else {
        secp256k1_gej_set_ge(&rj, &aggnonce_pts[1]);
        secp256k1_ecmult(&rj, &rj, &session_i->noncecoef, NULL);
    }
    secp256k1_gej_add_ge_var(&rj, &rj, &aggnonce_pts[0], NULL);
    secp256k1_ge_set_gej_var(&r, &rj);
    if (secp256k1_ge_is_infinity(&r)) {
        r = secp256k1_ge_const_g;
    }
    secp256k1_fe_normalize_var(&r.x);
    secp256k1_fe_get_b32(session_i->fin_nonce, &r.x);
    secp256k1_fe_normalize_var(&r.y);
    session_i->fin_nonce_parity = secp256k1_fe_is_odd(&r.y);

    /* e = tagged_hash("BIP0340/challenge", xbytes(R) || xbytes(Q) || msg) */
    secp256k1_schnorrsig_challenge(hash_ctx, &session_i->challenge, session_i->fin_nonce, msg, msglen, pk32);
    if (secp256k1_scalar_is_zero(&session_i->challenge)) {
        /* e == 0 can only occur with negligible probability */
        return 0;
    }

    /* s_part = e*g*tacc, where g = -1 iff Q has odd Y */
    pk_odd = secp256k1_fe_is_odd(&cache_i->pk.y);
    secp256k1_scalar_mul(&session_i->s_part, &session_i->challenge, &cache_i->tacc);
    if (pk_odd) {
        secp256k1_scalar_negate(&session_i->s_part, &session_i->s_part);
    }
    /* The signers need g*gacc, where gacc is the tweak factor accumulator. */
    session_i->g_times_gacc_parity = cache_i->gacc_parity ^ pk_odd;
    session_i->n_signers = (uint32_t)n_signers;
    return 1;
}

int secp256k1_frost_session_init(const secp256k1_context *ctx, secp256k1_frost_session *session, const secp256k1_frost_aggnonce *aggnonce, const uint32_t *ids, const secp256k1_pubkey *pubshares, size_t n_signers, size_t n_participants, uint32_t threshold, const secp256k1_frost_tweak_cache *tweak_cache, const unsigned char *msg, size_t msglen) {
    secp256k1_frost_tweak_cache_internal cache_i;
    secp256k1_frost_session_internal session_i;
    secp256k1_ge aggnonce_pts[2];

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(session != NULL);
    memset(session, 0, sizeof(*session));
    ARG_CHECK(aggnonce != NULL);
    ARG_CHECK(ids != NULL);
    ARG_CHECK(tweak_cache != NULL);
    ARG_CHECK(msg != NULL || msglen == 0);

    if (!secp256k1_frost_tweak_cache_load(ctx, &cache_i, tweak_cache)) {
        return 0;
    }
    if (!secp256k1_frost_validate_session_params(ctx, ids, pubshares, n_signers, n_participants, threshold, &cache_i)) {
        return 0;
    }
    if (!secp256k1_frost_aggnonce_load(ctx, aggnonce_pts, aggnonce)) {
        return 0;
    }
    if (!secp256k1_frost_get_session_values(ctx, &session_i, aggnonce_pts, ids, n_signers, &cache_i, msg, msglen)) {
        return 0;
    }
    secp256k1_frost_session_save(session, &session_i);
    return 1;
}

/* Checks s*G == Re_s + (e*lambda*g')*P, where Re_s is the signer's effective
 * nonce R1 + b*R2 (negated iff the final nonce R has odd Y) and g' = g*gacc.
 * This is the verification equation of the BIP 445 partial_sig_verify_internal
 * algorithm. nonce_pts must not contain the point at infinity. */
static int secp256k1_frost_verify_partial_sig(const secp256k1_scalar *s, const secp256k1_ge *nonce_pts, const secp256k1_ge *pubshare, const secp256k1_scalar *lambda, const secp256k1_frost_session_internal *session_i) {
    secp256k1_scalar e, s_neg;
    secp256k1_gej rj, pkj, tmp;

    /* The signer's effective nonce rj = R1 + b*R2. The nonce components of a
     * pubnonce are never the point at infinity. */
    VERIFY_CHECK(!secp256k1_ge_is_infinity(&nonce_pts[0]));
    VERIFY_CHECK(!secp256k1_ge_is_infinity(&nonce_pts[1]));
    secp256k1_gej_set_ge(&rj, &nonce_pts[1]);
    secp256k1_ecmult(&rj, &rj, &session_i->noncecoef, NULL);
    secp256k1_gej_add_ge_var(&rj, &rj, &nonce_pts[0], NULL);
    if (session_i->fin_nonce_parity) {
        secp256k1_gej_neg(&rj, &rj);
    }

    /* e = e*lambda*g', where g' = -1 iff g*gacc == -1 */
    secp256k1_scalar_mul(&e, &session_i->challenge, lambda);
    if (session_i->g_times_gacc_parity) {
        secp256k1_scalar_negate(&e, &e);
    }

    /* Check -s*G + e*P + rj == infinity, i.e. s*G == rj + e*P */
    secp256k1_scalar_negate(&s_neg, s);
    secp256k1_gej_set_ge(&pkj, pubshare);
    secp256k1_ecmult(&tmp, &pkj, &e, &s_neg);
    secp256k1_gej_add_var(&tmp, &tmp, &rj, NULL);
    return secp256k1_gej_is_infinity(&tmp);
}

/* Shared signing routine of frost_sign and frost_deterministic_sign (the Sign
 * algorithm of BIP 445): computes the partial signature s = k1 + b*k2 +
 * e*lambda*d from the unnegated secret nonce pair k, the secret share and the
 * session values. The nonces are negated iff the final nonce R has odd Y, and
 * the secret share is multiplied by g*gacc. If pubshares is non-NULL, the
 * secret share is checked against the signer's pubshare. Fails (returns 0) if
 * the secret share is invalid, if my_id is not in the signer set, or if the
 * pubshare check fails. The secret nonce pair k and all secret intermediates
 * are cleansed before this function returns. */
static int secp256k1_frost_sign_internal(const secp256k1_context *ctx, secp256k1_frost_partial_sig *partial_sig, secp256k1_scalar *k, const unsigned char *secshare32, const secp256k1_frost_session_internal *session_i, const uint32_t *ids, const secp256k1_pubkey *pubshares, size_t n_signers, uint32_t my_id) {
    secp256k1_scalar d, lambda, s, tmp;
    secp256k1_gej nonce_ptj[2];
    secp256k1_ge nonce_pts[2];
    secp256k1_ge pk;
    size_t my_index = n_signers;
    size_t i;
    int ret = 0;

    /* Compute the pubnonce points from the unnegated nonces for the
     * self-verification below. k[0] != 0 and k[1] != 0 is guaranteed by the
     * caller, so the points are not the point at infinity. */
    secp256k1_ecmult_gen_gej(&ctx->ecmult_gen_ctx, &nonce_ptj[0], &k[0]);
    secp256k1_ecmult_gen_gej(&ctx->ecmult_gen_ctx, &nonce_ptj[1], &k[1]);
    secp256k1_ge_set_all_gej(nonce_pts, nonce_ptj, 2);
    secp256k1_declassify(ctx, &nonce_pts, sizeof(nonce_pts));

    /* The secret share must be nonzero and less than the curve order. We can
     * declassify the result of the check because branching on it only leaks
     * whether the provided secret share is a valid secret key, which is not
     * secret. */
    {
        int share_valid = secp256k1_scalar_set_b32_seckey(&d, secshare32);
        secp256k1_declassify(ctx, &share_valid, sizeof(share_valid));
        if (!share_valid) {
            goto cleanup;
        }
    }
    /* P = d*G */
    secp256k1_ecmult_gen_ge(&ctx->ecmult_gen_ctx, &pk, &d);
    secp256k1_declassify(ctx, &pk, sizeof(pk));

    /* The signer's id must be in the session's signer set. */
    for (i = 0; i < n_signers; i++) {
        if (ids[i] == my_id) {
            my_index = i;
            break;
        }
    }
    if (my_index == n_signers) {
        goto cleanup;
    }
    /* If the pubshares are known, the secret share must match the signer's
     * pubshare (recommended by BIP 445). */
    if (pubshares != NULL) {
        secp256k1_ge expected;
        if (!secp256k1_pubkey_load(ctx, &expected, &pubshares[my_index])) {
            goto cleanup;
        }
        if (!secp256k1_ge_eq_var(&pk, &expected)) {
            goto cleanup;
        }
    }
    if (!secp256k1_frost_derive_interpolating_value(&lambda, ids, n_signers, my_id)) {
        goto cleanup;
    }

    /* d = g*gacc*d' */
    if (session_i->g_times_gacc_parity) {
        secp256k1_scalar_negate(&d, &d);
    }
    /* Negate the nonces iff the final nonce R has odd Y. */
    if (session_i->fin_nonce_parity) {
        secp256k1_scalar_negate(&k[0], &k[0]);
        secp256k1_scalar_negate(&k[1], &k[1]);
    }

    /* s = k1 + b*k2 + e*lambda*d */
    secp256k1_scalar_mul(&s, &session_i->challenge, &lambda);
    secp256k1_scalar_mul(&s, &s, &d);
    secp256k1_scalar_mul(&tmp, &session_i->noncecoef, &k[1]);
    secp256k1_scalar_add(&k[0], &k[0], &tmp);
    secp256k1_scalar_add(&s, &s, &k[0]);

    /* Self-verify the partial signature, as recommended by BIP 445. This can
     * only fail in case of an implementation bug or catastrophic hardware
     * failure, so the result of the verification is not secret. The partial
     * signature itself is declassified first: it is the public output of this
     * function, and the verification below multiplies with it in variable
     * time. */
    {
        int verified;
        secp256k1_declassify(ctx, &s, sizeof(s));
        verified = secp256k1_frost_verify_partial_sig(&s, nonce_pts, &pk, &lambda, session_i);
        secp256k1_declassify(ctx, &verified, sizeof(verified));
        if (!verified) {
            goto cleanup;
        }
    }
    secp256k1_frost_partial_sig_save(partial_sig, &s);
    ret = 1;

cleanup:
    secp256k1_scalar_clear(&d);
    secp256k1_scalar_clear(&lambda);
    secp256k1_scalar_clear(&s);
    secp256k1_scalar_clear(&tmp);
    secp256k1_scalar_clear(&k[0]);
    secp256k1_scalar_clear(&k[1]);
    secp256k1_gej_clear(&nonce_ptj[0]);
    secp256k1_gej_clear(&nonce_ptj[1]);
    return ret;
}

int secp256k1_frost_sign(const secp256k1_context *ctx, secp256k1_frost_partial_sig *partial_sig, secp256k1_frost_secnonce *secnonce, const unsigned char *secshare32, const secp256k1_frost_session *session, const uint32_t *ids, const secp256k1_pubkey *pubshares, size_t n_signers, uint32_t my_id) {
    secp256k1_scalar k[2];
    secp256k1_frost_session_internal session_i;
    int ret;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(secnonce != NULL);
    /* Fails if the magic doesn't match or the nonce has been invalidated */
    ret = secp256k1_frost_secnonce_load(ctx, k, secnonce);
    /* Wipe the secnonce to prevent nonce reuse. This will cause subsequent
     * calls of this function with the same secnonce to fail. */
    secp256k1_memzero_explicit(secnonce, sizeof(*secnonce));
    if (!ret) {
        secp256k1_scalar_clear(&k[0]);
        secp256k1_scalar_clear(&k[1]);
        return 0;
    }

    ARG_CHECK(partial_sig != NULL);
    memset(partial_sig, 0, sizeof(*partial_sig));
    ARG_CHECK(secshare32 != NULL);
    ARG_CHECK(session != NULL);
    ARG_CHECK(ids != NULL);
    ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));

    if (!secp256k1_frost_session_load(ctx, &session_i, session)) {
        secp256k1_scalar_clear(&k[0]);
        secp256k1_scalar_clear(&k[1]);
        return 0;
    }
    /* The ids array must be identical to the one passed to session_init. */
    if (session_i.n_signers != n_signers) {
        secp256k1_scalar_clear(&k[0]);
        secp256k1_scalar_clear(&k[1]);
        return 0;
    }
    return secp256k1_frost_sign_internal(ctx, partial_sig, k, secshare32, &session_i, ids, pubshares, n_signers, my_id);
}

int secp256k1_frost_partial_sig_verify(const secp256k1_context *ctx, const secp256k1_frost_partial_sig *partial_sig, const secp256k1_frost_pubnonce *pubnonce, const secp256k1_pubkey *pubshare, const secp256k1_frost_session *session, const uint32_t *ids, size_t n_signers, size_t signer_index) {
    secp256k1_frost_session_internal session_i;
    secp256k1_scalar s, lambda;
    secp256k1_ge nonce_pts[2];
    secp256k1_ge pubshare_ge;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(partial_sig != NULL);
    ARG_CHECK(pubnonce != NULL);
    ARG_CHECK(pubshare != NULL);
    ARG_CHECK(session != NULL);
    ARG_CHECK(ids != NULL);

    if (signer_index >= n_signers) {
        return 0;
    }
    if (!secp256k1_frost_session_load(ctx, &session_i, session)) {
        return 0;
    }
    /* The ids array must be identical to the one passed to session_init. */
    if (session_i.n_signers != n_signers) {
        return 0;
    }
    if (!secp256k1_frost_pubnonce_load(ctx, nonce_pts, pubnonce)) {
        return 0;
    }
    if (!secp256k1_pubkey_load(ctx, &pubshare_ge, pubshare)) {
        return 0;
    }
    if (!secp256k1_frost_partial_sig_load(ctx, &s, partial_sig)) {
        return 0;
    }
    if (!secp256k1_frost_derive_interpolating_value(&lambda, ids, n_signers, ids[signer_index])) {
        return 0;
    }
    return secp256k1_frost_verify_partial_sig(&s, nonce_pts, &pubshare_ge, &lambda, &session_i);
}

int secp256k1_frost_partial_sig_agg(const secp256k1_context *ctx, unsigned char *sig64, size_t *error_index, const secp256k1_frost_session *session, const secp256k1_frost_partial_sig *const *partial_sigs, size_t n_sigs) {
    secp256k1_frost_session_internal session_i;
    secp256k1_scalar s;
    size_t i;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(sig64 != NULL);
    memset(sig64, 0, 64);
    ARG_CHECK(session != NULL);
    ARG_CHECK(partial_sigs != NULL);

    if (!secp256k1_frost_session_load(ctx, &session_i, session)) {
        return 0;
    }
    if (n_sigs != (size_t)session_i.n_signers) {
        return 0;
    }
    /* s = e*g*tacc + sum_i s_i */
    s = session_i.s_part;
    for (i = 0; i < n_sigs; i++) {
        secp256k1_scalar term;
        /* A NULL array element indicates a missing contribution. This is the
         * identifiable-abort path, not an API usage error. */
        if (partial_sigs[i] == NULL) {
            if (error_index != NULL) {
                *error_index = i;
            }
            return 0;
        }
        if (!secp256k1_frost_partial_sig_load(ctx, &term, partial_sigs[i])) {
            if (error_index != NULL) {
                *error_index = i;
            }
            return 0;
        }
        secp256k1_scalar_add(&s, &s, &term);
    }
    memcpy(sig64, session_i.fin_nonce, 32);
    secp256k1_scalar_get_b32(&sig64[32], &s);
    return 1;
}

/* Derives the deterministic nonce pair of DeterministicSign (BIP 445):
 *   k_i = tagged_hash("BIP0445/deterministic/nonce", secshare_ || ser32(my_id)
 *         || ser32(u) || sorted ser32 ids || aggothernonce ||
 *         tweaked_thresh_pk_xonly || len8(msg) || msg || byte(i))
 * where aggothernonce is its 66-byte serialization or the empty string for a
 * sole signer. Returns 0 if the message is too long for its length prefix, or
 * if one of the nonces is zero (which can only happen with negligible
 * probability), 1 otherwise. All secret intermediates are cleansed before this
 * function returns. */
static int secp256k1_frost_det_nonce_function(const secp256k1_hash_ctx *hash_ctx, secp256k1_scalar *k, const unsigned char *secshare32, uint32_t my_id, const uint32_t *sorted_ids, size_t n_signers, const unsigned char *aggothernonce66, const unsigned char *tweaked_pk32, const unsigned char *msg, size_t msglen) {
    secp256k1_sha256 sha;
    unsigned char buf[4];
    size_t i;
    int k0_nonzero, k1_nonzero;

    secp256k1_frost_sha256_tagged_deterministic_nonce(hash_ctx, &sha);
    secp256k1_sha256_write(hash_ctx, &sha, secshare32, 32);
    secp256k1_write_be32(buf, my_id);
    secp256k1_sha256_write(hash_ctx, &sha, buf, sizeof(buf));
    secp256k1_write_be32(buf, (uint32_t)n_signers);
    secp256k1_sha256_write(hash_ctx, &sha, buf, sizeof(buf));
    for (i = 0; i < n_signers; i++) {
        secp256k1_write_be32(buf, sorted_ids[i]);
        secp256k1_sha256_write(hash_ctx, &sha, buf, sizeof(buf));
    }
    if (aggothernonce66 != NULL) {
        secp256k1_sha256_write(hash_ctx, &sha, aggothernonce66, 66);
    }
    secp256k1_sha256_write(hash_ctx, &sha, tweaked_pk32, 32);
    if (!secp256k1_frost_sha256_write_prefixed(hash_ctx, &sha, 8, msg, msglen)) {
        secp256k1_sha256_clear(&sha);
        return 0;
    }

    for (i = 0; i < 2; i++) {
        unsigned char hash32[32];
        unsigned char idx = (unsigned char)i;
        secp256k1_sha256 sha_tmp = sha;
        secp256k1_sha256_write(hash_ctx, &sha_tmp, &idx, 1);
        secp256k1_sha256_finalize(hash_ctx, &sha_tmp, hash32);
        secp256k1_scalar_set_b32(&k[i], hash32, NULL);

        /* Attempt to erase secret data */
        secp256k1_memclear_explicit(hash32, sizeof(hash32));
        secp256k1_sha256_clear(&sha_tmp);
    }
    secp256k1_sha256_clear(&sha);
    /* Bitwise (not short-circuiting) so that this does not branch on the
     * secret nonces; the caller declassifies the return value. */
    k0_nonzero = !secp256k1_scalar_is_zero(&k[0]);
    k1_nonzero = !secp256k1_scalar_is_zero(&k[1]);
    return k0_nonzero & k1_nonzero;
}

int secp256k1_frost_deterministic_sign(const secp256k1_context *ctx, secp256k1_frost_partial_sig *partial_sig, secp256k1_frost_pubnonce *pubnonce, const unsigned char *secshare32, uint32_t my_id, const secp256k1_frost_aggnonce *aggothernonce, const uint32_t *ids, const secp256k1_pubkey *pubshares, size_t n_signers, size_t n_participants, uint32_t threshold, const secp256k1_frost_tweak_cache *tweak_cache, const unsigned char *msg, size_t msglen, const unsigned char *aux_rand32) {
    const secp256k1_hash_ctx *hash_ctx;
    secp256k1_frost_tweak_cache_internal cache_i;
    secp256k1_frost_session_internal session_i;
    secp256k1_scalar k[2];
    secp256k1_gej nonce_ptj[2];
    secp256k1_ge nonce_pts[2];
    secp256k1_ge aggnonce_pts[2];
    secp256k1_ge aggother_pts[2];
    unsigned char secshare_[32];
    unsigned char pk32[32];
    unsigned char aggother66[66];
    const unsigned char *aggother66_ptr = NULL;
    uint32_t sorted_ids[SECP256K1_FROST_MAX_PARTICIPANTS];
    int i;
    int ret = 0;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(partial_sig != NULL);
    memset(partial_sig, 0, sizeof(*partial_sig));
    ARG_CHECK(pubnonce != NULL);
    memset(pubnonce, 0, sizeof(*pubnonce));
    ARG_CHECK(secshare32 != NULL);
    ARG_CHECK(ids != NULL);
    ARG_CHECK(tweak_cache != NULL);
    ARG_CHECK(msg != NULL || msglen == 0);
    ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    hash_ctx = secp256k1_get_hash_context(ctx);

    if (!secp256k1_frost_tweak_cache_load(ctx, &cache_i, tweak_cache)) {
        return 0;
    }
    if (!secp256k1_frost_validate_session_params(ctx, ids, pubshares, n_signers, n_participants, threshold, &cache_i)) {
        return 0;
    }
    /* Check that the secshare is valid to be able to sign with it below. */
    {
        secp256k1_scalar secshare;
        int valid = secp256k1_scalar_set_b32_seckey(&secshare, secshare32);
        secp256k1_scalar_clear(&secshare);
        /* We can declassify because branching on valid only leaks whether the
         * provided secret share is a valid secret key, which is not secret. */
        secp256k1_declassify(ctx, &valid, sizeof(valid));
        if (!valid) {
            return 0;
        }
    }
    if (aggothernonce != NULL) {
        if (!secp256k1_frost_aggnonce_load(ctx, aggother_pts, aggothernonce)) {
            return 0;
        }
        /* BIP 445 DeterministicSign feeds aggothernonce through NonceAgg as a
         * pubnonce contribution, and a pubnonce's components cannot be the
         * point at infinity (a pubnonce is serialized with plain compressed
         * encoding). Reject an aggothernonce with an infinity component. */
        if (secp256k1_ge_is_infinity(&aggother_pts[0]) || secp256k1_ge_is_infinity(&aggother_pts[1])) {
            return 0;
        }
        secp256k1_ge_serialize_ext(&aggother66[0], &aggother_pts[0]);
        secp256k1_ge_serialize_ext(&aggother66[33], &aggother_pts[1]);
        aggother66_ptr = aggother66;
    }

    /* secshare' = secshare XOR tagged_hash("BIP0445/aux", aux_rand32) */
    if (aux_rand32 != NULL) {
        secp256k1_sha256 sha;
        secp256k1_frost_sha256_tagged_aux(hash_ctx, &sha);
        secp256k1_sha256_write(hash_ctx, &sha, aux_rand32, 32);
        secp256k1_sha256_finalize(hash_ctx, &sha, secshare_);
        secp256k1_sha256_clear(&sha);
        for (i = 0; i < 32; i++) {
            secshare_[i] ^= secshare32[i];
        }
    } else {
        memcpy(secshare_, secshare32, sizeof(secshare_));
    }

    /* The x-only encoding of the tweaked threshold public key. Q is never the
     * point at infinity and its x-coordinate is normalized. */
    secp256k1_fe_get_b32(pk32, &cache_i.pk.x);
    secp256k1_frost_sort_ids(sorted_ids, ids, n_signers);

    {
        int k_ok = secp256k1_frost_det_nonce_function(hash_ctx, k, secshare_, my_id, sorted_ids, n_signers, aggother66_ptr, pk32, msg, msglen);
        /* k_i == 0 can only occur with negligible probability, so its
         * occurrence is not secret. */
        secp256k1_declassify(ctx, &k_ok, sizeof(k_ok));
        if (!k_ok) {
            goto cleanup;
        }
    }

    /* The signer's pubnonce. k != 0, so the points are not infinity. */
    for (i = 0; i < 2; i++) {
        secp256k1_ecmult_gen_gej(&ctx->ecmult_gen_ctx, &nonce_ptj[i], &k[i]);
    }
    secp256k1_ge_set_all_gej(nonce_pts, nonce_ptj, 2);
    secp256k1_declassify(ctx, &nonce_pts, sizeof(nonce_pts));
    secp256k1_frost_pubnonce_save(pubnonce, nonce_pts);

    /* The aggregate nonce is the signer's own pubnonce for a sole signer and
     * the column-wise sum with aggothernonce otherwise. The sums may be the
     * point at infinity (BIP 445 NonceAgg). */
    if (aggothernonce == NULL) {
        aggnonce_pts[0] = nonce_pts[0];
        aggnonce_pts[1] = nonce_pts[1];
    } else {
        secp256k1_gej sumj[2];
        for (i = 0; i < 2; i++) {
            secp256k1_gej_set_ge(&sumj[i], &nonce_pts[i]);
            secp256k1_gej_add_ge_var(&sumj[i], &sumj[i], &aggother_pts[i], NULL);
        }
        secp256k1_ge_set_all_gej_var(aggnonce_pts, sumj, 2);
    }

    if (!secp256k1_frost_get_session_values(ctx, &session_i, aggnonce_pts, ids, n_signers, &cache_i, msg, msglen)) {
        goto cleanup;
    }
    ret = secp256k1_frost_sign_internal(ctx, partial_sig, k, secshare32, &session_i, ids, pubshares, n_signers, my_id);

cleanup:
    secp256k1_scalar_clear(&k[0]);
    secp256k1_scalar_clear(&k[1]);
    secp256k1_gej_clear(&nonce_ptj[0]);
    secp256k1_gej_clear(&nonce_ptj[1]);
    secp256k1_memzero_explicit(secshare_, sizeof(secshare_));
    return ret;
}

#endif
