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

#ifndef SECP256K1_MODULE_FROST_TESTS_IMPL_H
#define SECP256K1_MODULE_FROST_TESTS_IMPL_H

#include <string.h>

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

#include "keygen.h"
#include "session.h"
#include "../../group.h"
#include "../../scalar.h"
#include "../../util.h"

#include "vectors.h"

/* Fixed-value test of the trusted dealer key generation (BIP 445
 * TrustedDealerKeyGen) with threshold_seckey32 = 0x0102...20, n = 5, t = 3.
 * This is the first fixed-value anchor of the module. */
static void frost_trusted_dealer_vector_test(void) {
    static const unsigned char expected_thresh_pk[33] = {
        0x02, 0x84, 0xbf, 0x75, 0x62, 0x26, 0x2b, 0xbd, 0x69, 0x40, 0x08, 0x57,
        0x48, 0xf3, 0xbe, 0x6a, 0xfa, 0x52, 0xae, 0x31, 0x71, 0x55, 0x18, 0x1e,
        0xce, 0x31, 0xb6, 0x63, 0x51, 0xcc, 0xff, 0xa4, 0xb0
    };
    static const unsigned char expected_secshares[5][32] = {
        { 0x68, 0x4d, 0x12, 0xac, 0xd2, 0x49, 0x4c, 0x6b, 0x3b, 0x87, 0xb4, 0x57,
          0x29, 0x8b, 0xb8, 0x66, 0xd3, 0xdd, 0x2c, 0x0d, 0x11, 0x5a, 0x40, 0xaa,
          0xd8, 0x37, 0x08, 0xcc, 0x9a, 0xc0, 0x4f, 0xc0 },
        { 0x85, 0x7a, 0x31, 0x82, 0x63, 0xa1, 0xd2, 0x53, 0x26, 0x1c, 0xdb, 0x1a,
          0x36, 0x55, 0x45, 0x26, 0x78, 0xaa, 0x92, 0xb2, 0x9d, 0xcc, 0x4b, 0x23,
          0xc0, 0xa1, 0xa0, 0xfe, 0x29, 0x0b, 0xd6, 0xac },
        { 0x58, 0x89, 0x5f, 0x84, 0xb9, 0x0f, 0x98, 0xbf, 0xc8, 0xc9, 0x7f, 0x55,
          0x33, 0x6a, 0xb5, 0x4e, 0xff, 0x7a, 0x47, 0x04, 0xba, 0x6c, 0x36, 0x82,
          0xd2, 0x59, 0xe3, 0xb0, 0xc8, 0x00, 0xb3, 0xe4 },
        { 0xe1, 0x7a, 0x9c, 0xb3, 0xd2, 0x92, 0x9f, 0xb1, 0x23, 0x8d, 0xa1, 0x08,
          0x20, 0xcc, 0x08, 0xdf, 0x22, 0xfb, 0x25, 0xea, 0x16, 0x82, 0xa3, 0x03,
          0xcd, 0x32, 0x2f, 0x71, 0x47, 0xd5, 0x28, 0xa9 },
        { 0x20, 0x4d, 0xe9, 0x0f, 0xb0, 0x2a, 0xe7, 0x27, 0x36, 0x69, 0x40, 0x32,
          0xfe, 0x79, 0x3f, 0xd9, 0x6d, 0xcf, 0x75, 0x95, 0x53, 0x7e, 0x50, 0x2f,
          0x31, 0x85, 0xc7, 0x26, 0x08, 0x1c, 0xb2, 0x79 }
    };
    static const unsigned char expected_pubshares[5][33] = {
        { 0x03, 0x8f, 0x88, 0x0d, 0xa3, 0x11, 0x06, 0xe8, 0x0e, 0xc2, 0x9e, 0xf3,
          0xe8, 0x60, 0x91, 0xc7, 0x6a, 0x3e, 0x4e, 0xc9, 0x1d, 0xdc, 0x57, 0xf4,
          0x34, 0x5e, 0x79, 0xc8, 0x10, 0x47, 0xe0, 0xe7, 0x61 },
        { 0x03, 0x2f, 0x80, 0x8b, 0x2a, 0x21, 0x24, 0x3d, 0xc2, 0x28, 0x62, 0xdf,
          0x5c, 0x60, 0x79, 0x89, 0x7e, 0x77, 0xe0, 0xfb, 0x39, 0xb1, 0x8b, 0x6b,
          0x92, 0x10, 0xf3, 0xba, 0xab, 0x76, 0x5a, 0x08, 0x2d },
        { 0x02, 0xd9, 0xa1, 0xfd, 0x34, 0xd8, 0xe3, 0x9f, 0xba, 0x3f, 0xcb, 0x59,
          0x9c, 0x98, 0x78, 0x3d, 0x88, 0xfe, 0xd1, 0x1a, 0xf4, 0xb8, 0x94, 0x9e,
          0x4b, 0x4a, 0xe3, 0xd9, 0x51, 0xe9, 0xed, 0xab, 0x6f },
        { 0x03, 0x8a, 0x07, 0x53, 0xbd, 0xf9, 0x11, 0x15, 0x50, 0x44, 0x99, 0xb0,
          0x92, 0xc3, 0x7a, 0x91, 0x9d, 0x08, 0x26, 0x4b, 0x59, 0xf7, 0x80, 0x28,
          0xb2, 0x0a, 0x85, 0x19, 0x05, 0xb0, 0x53, 0x81, 0xf5 },
        { 0x03, 0x7a, 0x34, 0xc6, 0x4b, 0x81, 0x55, 0xac, 0xfe, 0x23, 0x3e, 0x8c,
          0xab, 0xa3, 0xce, 0x15, 0xe9, 0xf4, 0xfb, 0x83, 0x10, 0xb3, 0xdf, 0xee,
          0xb9, 0x7d, 0xd2, 0x50, 0xdb, 0xe3, 0x08, 0x54, 0x0d }
    };
    unsigned char thresh_sk[32];
    unsigned char secshares[5 * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[5];
    unsigned char buf[33];
    size_t outl;
    size_t i;

    for (i = 0; i < 32; i++) {
        thresh_sk[i] = (unsigned char)(i + 1);
    }
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 5, 3, thresh_sk) == 1);

    outl = sizeof(buf);
    CHECK(secp256k1_ec_pubkey_serialize(CTX, buf, &outl, &thresh_pk, SECP256K1_EC_COMPRESSED) == 1);
    CHECK(outl == 33);
    CHECK(secp256k1_memcmp_var(buf, expected_thresh_pk, 33) == 0);

    for (i = 0; i < 5; i++) {
        CHECK(secp256k1_memcmp_var(&secshares[32 * i], expected_secshares[i], 32) == 0);
        outl = sizeof(buf);
        CHECK(secp256k1_ec_pubkey_serialize(CTX, buf, &outl, &pubshares[i], SECP256K1_EC_COMPRESSED) == 1);
        CHECK(outl == 33);
        CHECK(secp256k1_memcmp_var(buf, expected_pubshares[i], 33) == 0);
    }

    CHECK(secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, pubshares, 5, 3) == 1);
}

static void frost_trusted_dealer_test_internal(void) {
    unsigned char thresh_sk[32];
    unsigned char secshares[SECP256K1_FROST_MAX_PARTICIPANTS * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[SECP256K1_FROST_MAX_PARTICIPANTS];
    size_t n = 1 + testrand_int(SECP256K1_FROST_MAX_PARTICIPANTS);
    uint32_t t = 1 + testrand_int(n);
    size_t i;

    testutil_random_scalar_order_b32(thresh_sk);
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, n, t, thresh_sk) == 1);

    /* Every pubshare matches its secshare. */
    for (i = 0; i < n; i++) {
        secp256k1_scalar share;
        secp256k1_ge expected, actual;
        int overflow;

        secp256k1_scalar_set_b32(&share, &secshares[32 * i], &overflow);
        CHECK(!overflow);
        CHECK(!secp256k1_scalar_is_zero(&share));
        secp256k1_ecmult_gen_ge(&CTX->ecmult_gen_ctx, &expected, &share);
        CHECK(secp256k1_pubkey_load(CTX, &actual, &pubshares[i]));
        CHECK(secp256k1_ge_eq_var(&expected, &actual));
        secp256k1_scalar_clear(&share);
    }

    /* The shares of a random subset of t signers combine to the threshold
     * secret. */
    {
        uint32_t ids[SECP256K1_FROST_MAX_PARTICIPANTS];
        secp256k1_scalar secret, combined, lambda, share;

        for (i = 0; i < n; i++) {
            ids[i] = (uint32_t)i;
        }
        /* Partial Fisher-Yates shuffle of the ids */
        for (i = 0; i < n; i++) {
            size_t j = i + testrand_int((uint32_t)(n - i));
            uint32_t tmp = ids[i];
            ids[i] = ids[j];
            ids[j] = tmp;
        }
        secp256k1_scalar_set_int(&combined, 0);
        for (i = 0; i < t; i++) {
            secp256k1_scalar_set_b32(&share, &secshares[32 * ids[i]], NULL);
            CHECK(secp256k1_frost_derive_interpolating_value(&lambda, ids, t, ids[i]) == 1);
            secp256k1_scalar_mul(&share, &share, &lambda);
            secp256k1_scalar_add(&combined, &combined, &share);
        }
        secp256k1_scalar_set_b32(&secret, thresh_sk, NULL);
        CHECK(secp256k1_scalar_eq(&combined, &secret));
        secp256k1_scalar_clear(&secret);
        secp256k1_scalar_clear(&combined);
        secp256k1_scalar_clear(&lambda);
        secp256k1_scalar_clear(&share);
    }

    /* The generated key material validates. */
    CHECK(secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, pubshares, n, t) == 1);

    /* Replacing a pubshare with a valid but inconsistent point invalidates the
     * key material. */
    {
        secp256k1_pubkey backup = pubshares[n - 1];
        secp256k1_pubkey other_pk;
        unsigned char other_sk[32];

        testutil_random_scalar_order_b32(other_sk);
        CHECK(secp256k1_ec_pubkey_create(CTX, &other_pk, other_sk) == 1);
        pubshares[n - 1] = other_pk;
        CHECK(secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, pubshares, n, t) == 0);
        pubshares[n - 1] = backup;
    }

    /* Swapping two pubshares invalidates the key material (unless t = 1, in
     * which case all pubshares are equal and the swap is undetectable). */
    if (t > 1) {
        secp256k1_pubkey tmp = pubshares[0];
        pubshares[0] = pubshares[1];
        pubshares[1] = tmp;
        CHECK(secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, pubshares, n, t) == 0);
        tmp = pubshares[0];
        pubshares[0] = pubshares[1];
        pubshares[1] = tmp;
    }

    /* Invalid n and t are rejected. */
    CHECK(secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, pubshares, n, 0) == 0);
    CHECK(secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, pubshares, n, (uint32_t)(n + 1)) == 0);
    CHECK(secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, pubshares, 0, t) == 0);
    CHECK(secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, pubshares, SECP256K1_FROST_MAX_PARTICIPANTS + 1, t) == 0);
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 0, 1, thresh_sk) == 0);
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, SECP256K1_FROST_MAX_PARTICIPANTS + 1, 1, thresh_sk) == 0);
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, n, 0, thresh_sk) == 0);
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, n, (uint32_t)(n + 1), thresh_sk) == 0);
}
REPEAT_TEST(frost_trusted_dealer_test)

/* Fixed-value test of the tweak cache functions (BIP 445 TweakCtxInit and
 * ApplyTweak). The expected values have been verified against the BIP 445
 * Python reference implementation. */
static void frost_tweak_vector_test(void) {
    static const unsigned char thresh_pk33[33] = {
        0x02, 0x84, 0xbf, 0x75, 0x62, 0x26, 0x2b, 0xbd, 0x69, 0x40, 0x08, 0x57,
        0x48, 0xf3, 0xbe, 0x6a, 0xfa, 0x52, 0xae, 0x31, 0x71, 0x55, 0x18, 0x1e,
        0xce, 0x31, 0xb6, 0x63, 0x51, 0xcc, 0xff, 0xa4, 0xb0
    };
    static const unsigned char thresh_pk33_odd[33] = {
        0x03, 0x84, 0xbf, 0x75, 0x62, 0x26, 0x2b, 0xbd, 0x69, 0x40, 0x08, 0x57,
        0x48, 0xf3, 0xbe, 0x6a, 0xfa, 0x52, 0xae, 0x31, 0x71, 0x55, 0x18, 0x1e,
        0xce, 0x31, 0xb6, 0x63, 0x51, 0xcc, 0xff, 0xa4, 0xb0
    };
    /* xonly(thresh_pk) */
    static const unsigned char expected0[32] = {
        0x84, 0xbf, 0x75, 0x62, 0x26, 0x2b, 0xbd, 0x69, 0x40, 0x08, 0x57, 0x48,
        0xf3, 0xbe, 0x6a, 0xfa, 0x52, 0xae, 0x31, 0x71, 0x55, 0x18, 0x1e, 0xce,
        0x31, 0xb6, 0x63, 0x51, 0xcc, 0xff, 0xa4, 0xb0
    };
    /* after xonly tweak 0x02...02 */
    static const unsigned char expected1[32] = {
        0x55, 0xd2, 0xac, 0xd9, 0x3c, 0xcd, 0x26, 0x82, 0xc7, 0x49, 0xd5, 0x97,
        0xaf, 0x74, 0x26, 0x72, 0x6d, 0x31, 0x65, 0x42, 0x62, 0x04, 0x70, 0x8b,
        0xe4, 0xaa, 0x8e, 0x81, 0xe2, 0xbb, 0x8c, 0xd4
    };
    /* after plain tweak 0x03...03 */
    static const unsigned char expected2[32] = {
        0xdb, 0x5f, 0x9c, 0x2e, 0x60, 0xeb, 0xfb, 0x9e, 0x91, 0x74, 0xaa, 0xf9,
        0x0c, 0xa2, 0xab, 0x51, 0x8b, 0xb4, 0x9a, 0x56, 0x75, 0xd6, 0x36, 0x50,
        0x1d, 0x2f, 0x05, 0x84, 0x31, 0x54, 0xb8, 0x41
    };
    /* after xonly tweak 0x04...04 */
    static const unsigned char expected3[32] = {
        0xc8, 0xf3, 0x62, 0xf9, 0x68, 0x68, 0x60, 0xba, 0x45, 0x75, 0xc8, 0xd8,
        0xb2, 0x31, 0xb9, 0xcd, 0xed, 0x9e, 0x87, 0xec, 0x3a, 0x85, 0xff, 0x53,
        0x1a, 0x98, 0x2e, 0xd1, 0xb4, 0x70, 0xae, 0xfd
    };
    secp256k1_pubkey thresh_pk;
    secp256k1_frost_tweak_cache cache;
    secp256k1_xonly_pubkey tweaked_pk;
    unsigned char tweak[32];
    unsigned char out32[32];

    /* Even-Y base point. */
    CHECK(secp256k1_ec_pubkey_parse(CTX, &thresh_pk, thresh_pk33, 33) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);
    CHECK(secp256k1_frost_tweaked_pubkey_get(CTX, &tweaked_pk, &cache) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, out32, &tweaked_pk) == 1);
    CHECK(secp256k1_memcmp_var(out32, expected0, 32) == 0);

    memset(tweak, 0x02, 32);
    CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, &tweaked_pk, &cache, tweak) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, out32, &tweaked_pk) == 1);
    CHECK(secp256k1_memcmp_var(out32, expected1, 32) == 0);

    memset(tweak, 0x03, 32);
    CHECK(secp256k1_frost_pubkey_ec_tweak_add(CTX, &tweaked_pk, &cache, tweak) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, out32, &tweaked_pk) == 1);
    CHECK(secp256k1_memcmp_var(out32, expected2, 32) == 0);

    memset(tweak, 0x04, 32);
    CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, &tweaked_pk, &cache, tweak) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, out32, &tweaked_pk) == 1);
    CHECK(secp256k1_memcmp_var(out32, expected3, 32) == 0);

    /* Odd-Y encoding of the same base point: x-only normalization makes the
     * result of an xonly tweak identical to the even-Y base point. */
    CHECK(secp256k1_ec_pubkey_parse(CTX, &thresh_pk, thresh_pk33_odd, 33) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);
    memset(tweak, 0x02, 32);
    CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, &tweaked_pk, &cache, tweak) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, out32, &tweaked_pk) == 1);
    CHECK(secp256k1_memcmp_var(out32, expected1, 32) == 0);
}

static void frost_tweak_test_internal(void) {
    /* The curve order n; any tweak >= n is out of range. */
    static const unsigned char order32[32] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
        0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41
    };
    unsigned char sk[32];
    secp256k1_pubkey thresh_pk, invalid_pk;
    secp256k1_frost_tweak_cache cache, invalid_cache;
    secp256k1_xonly_pubkey tweaked_pk;
    unsigned char tweak[32];
    int i;

    memset(&invalid_pk, 0, sizeof(invalid_pk));
    memset(&invalid_cache, 0, sizeof(invalid_cache));
    testutil_random_scalar_order_b32(sk);
    CHECK(secp256k1_ec_pubkey_create(CTX, &thresh_pk, sk) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);

    /* NULL and invalid argument coverage. */
    CHECK_ILLEGAL(CTX, secp256k1_frost_tweak_cache_init(CTX, NULL, &thresh_pk));
    {
        /* tweak_cache_init wipes the cache before checking arguments. */
        secp256k1_frost_tweak_cache tmp_cache = cache;
        CHECK_ILLEGAL(CTX, secp256k1_frost_tweak_cache_init(CTX, &tmp_cache, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_tweak_cache_init(CTX, &tmp_cache, &invalid_pk));
    }
    CHECK_ILLEGAL(CTX, secp256k1_frost_tweaked_pubkey_get(CTX, NULL, &cache));
    CHECK_ILLEGAL(CTX, secp256k1_frost_tweaked_pubkey_get(CTX, &tweaked_pk, NULL));
    CHECK_ILLEGAL(CTX, secp256k1_frost_tweaked_pubkey_get(CTX, &tweaked_pk, &invalid_cache));
    testrand256(tweak);
    {
        secp256k1_frost_tweak_cache tmp_cache = cache;
        CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_xonly_tweak_add(CTX, &tweaked_pk, NULL, tweak));
        CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_xonly_tweak_add(CTX, &tweaked_pk, &tmp_cache, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_xonly_tweak_add(CTX, &tweaked_pk, &invalid_cache, tweak));
        CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_ec_tweak_add(CTX, &tweaked_pk, NULL, tweak));
        CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_ec_tweak_add(CTX, &tweaked_pk, &tmp_cache, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_ec_tweak_add(CTX, &tweaked_pk, &invalid_cache, tweak));
        CHECK(secp256k1_memcmp_var(tmp_cache.data, cache.data, sizeof(cache.data)) == 0);
    }

    /* Apply a chain of random xonly/plain tweaks and cross-check each step:
     * the frost tweak must match secp256k1_xonly_pubkey_tweak_add applied to
     * xonly(current Q) or secp256k1_ec_pubkey_tweak_add applied to the full
     * current Q, respectively. The reference point ref_pk tracks the full
     * current Q. */
    {
        secp256k1_pubkey ref_pk = thresh_pk;

        for (i = 0; i < 8; i++) {
            secp256k1_xonly_pubkey ref_xonly;
            unsigned char out32[32], ref32[32];

            testrand256(tweak);
            if (testrand_bits(1)) {
                CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, &tweaked_pk, &cache, tweak) == 1);
                CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &ref_xonly, NULL, &ref_pk) == 1);
                CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &ref_pk, &ref_xonly, tweak) == 1);
            } else {
                CHECK(secp256k1_frost_pubkey_ec_tweak_add(CTX, &tweaked_pk, &cache, tweak) == 1);
                CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &ref_pk, tweak) == 1);
            }
            CHECK(secp256k1_xonly_pubkey_serialize(CTX, out32, &tweaked_pk) == 1);
            CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &ref_xonly, NULL, &ref_pk) == 1);
            CHECK(secp256k1_xonly_pubkey_serialize(CTX, ref32, &ref_xonly) == 1);
            CHECK(secp256k1_memcmp_var(out32, ref32, 32) == 0);

            /* tweaked_pubkey_get agrees with the tweak_add output. */
            CHECK(secp256k1_frost_tweaked_pubkey_get(CTX, &tweaked_pk, &cache) == 1);
            CHECK(secp256k1_xonly_pubkey_serialize(CTX, ref32, &tweaked_pk) == 1);
            CHECK(secp256k1_memcmp_var(out32, ref32, 32) == 0);
        }
    }

    /* A failing tweak (out of range) must leave the cache unchanged: applying
     * a known tweak after the failed one must give the same result as applying
     * it to a cache that never saw the failed tweak. */
    {
        secp256k1_frost_tweak_cache failed_cache = cache;
        secp256k1_frost_tweak_cache backup_cache = cache;
        secp256k1_xonly_pubkey out_pk1, out_pk2;
        unsigned char ser1[32], ser2[32];

        CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, &tweaked_pk, &failed_cache, order32) == 0);
        CHECK(secp256k1_frost_pubkey_ec_tweak_add(CTX, &tweaked_pk, &failed_cache, order32) == 0);
        CHECK(secp256k1_memcmp_var(failed_cache.data, cache.data, sizeof(cache.data)) == 0);

        testrand256(tweak);
        CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, &out_pk1, &failed_cache, tweak) == 1);
        CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, &out_pk2, &backup_cache, tweak) == 1);
        CHECK(secp256k1_xonly_pubkey_serialize(CTX, ser1, &out_pk1) == 1);
        CHECK(secp256k1_xonly_pubkey_serialize(CTX, ser2, &out_pk2) == 1);
        CHECK(secp256k1_memcmp_var(ser1, ser2, 32) == 0);
    }
}
REPEAT_TEST(frost_tweak_test)

/* Fixed-value tests of nonce generation and aggregation (BIP 445 NonceGen and
 * NonceAgg). The expected values have been verified against the BIP 445
 * Python reference implementation. */
static void frost_nonce_vector_test(void) {
    static const unsigned char pubshare33[33] = {
        0x03, 0x8f, 0x88, 0x0d, 0xa3, 0x11, 0x06, 0xe8, 0x0e, 0xc2, 0x9e, 0xf3,
        0xe8, 0x60, 0x91, 0xc7, 0x6a, 0x3e, 0x4e, 0xc9, 0x1d, 0xdc, 0x57, 0xf4,
        0x34, 0x5e, 0x79, 0xc8, 0x10, 0x47, 0xe0, 0xe7, 0x61
    };
    static const unsigned char thresh_pk32[32] = {
        0x84, 0xbf, 0x75, 0x62, 0x26, 0x2b, 0xbd, 0x69, 0x40, 0x08, 0x57, 0x48,
        0xf3, 0xbe, 0x6a, 0xfa, 0x52, 0xae, 0x31, 0x71, 0x55, 0x18, 0x1e, 0xce,
        0x31, 0xb6, 0x63, 0x51, 0xcc, 0xff, 0xa4, 0xb0
    };
    static const unsigned char msg[] = "frost test message";
    static const unsigned char expected_full_secnonce[64] = {
        0xc8, 0x5b, 0xe4, 0xd1, 0x05, 0x92, 0xcb, 0xf3, 0x94, 0xe6, 0xe2, 0x7f,
        0x7c, 0xa8, 0x78, 0x46, 0x4b, 0x1b, 0xd0, 0x3e, 0x81, 0x6a, 0x7a, 0x77,
        0xec, 0xf2, 0x93, 0x7e, 0x13, 0xdf, 0x8b, 0x16, 0x5e, 0x8c, 0xc9, 0x24,
        0x00, 0xd9, 0xa8, 0x4d, 0xa5, 0x3e, 0xfb, 0x9b, 0x25, 0x7b, 0x62, 0x37,
        0x65, 0x76, 0xb4, 0x2d, 0xe5, 0x74, 0xec, 0x9d, 0x9f, 0xcf, 0x82, 0x9c,
        0xc6, 0xbd, 0x14, 0xe8
    };
    static const unsigned char expected_full_pubnonce[66] = {
        0x03, 0x2e, 0x36, 0xc1, 0x27, 0x26, 0x27, 0x31, 0x41, 0x9a, 0xa1, 0x65,
        0xa9, 0xcc, 0xce, 0xcd, 0xb0, 0x03, 0x1c, 0x50, 0x19, 0xe1, 0x24, 0xb6,
        0x9c, 0x18, 0xd7, 0xa7, 0x5a, 0xa4, 0xa2, 0x27, 0x7c, 0x03, 0x77, 0x53,
        0xb9, 0xd7, 0xaf, 0x60, 0x16, 0x4a, 0x27, 0x18, 0x79, 0x2e, 0xa4, 0x95,
        0x60, 0xc7, 0xe1, 0xfe, 0x13, 0x7b, 0xea, 0xc1, 0x3d, 0x43, 0x85, 0xda,
        0x5e, 0xe1, 0xd6, 0x6d, 0x35, 0x75
    };
    static const unsigned char expected_min_secnonce[64] = {
        0x3e, 0x7f, 0xc5, 0xe4, 0x3a, 0xac, 0x89, 0xce, 0x22, 0x9f, 0x07, 0x9a,
        0x2b, 0x85, 0xd3, 0x10, 0xfd, 0x6d, 0x5a, 0x8f, 0x24, 0xa2, 0xc1, 0x13,
        0xb1, 0x3e, 0xdd, 0x37, 0x40, 0x3e, 0x0a, 0x21, 0xfc, 0xea, 0x76, 0x24,
        0x34, 0x5b, 0x3f, 0xab, 0xa0, 0xd0, 0xe1, 0xb6, 0x7f, 0xce, 0xb0, 0x7c,
        0xd4, 0x7b, 0x3c, 0xc3, 0x3c, 0x7c, 0x1e, 0xd0, 0x0b, 0xd4, 0x34, 0x6a,
        0xaf, 0x4e, 0x4f, 0xc6
    };
    static const unsigned char expected_min_pubnonce[66] = {
        0x03, 0x7d, 0xc8, 0xa9, 0xd6, 0x99, 0x91, 0xd5, 0x6e, 0x50, 0xf5, 0x85,
        0xbc, 0x79, 0x0a, 0xa3, 0x6b, 0x83, 0xfd, 0x16, 0x6c, 0xa2, 0x6d, 0x16,
        0x7c, 0x66, 0x84, 0xa5, 0xa6, 0xa4, 0x7d, 0x35, 0x0d, 0x03, 0xd5, 0x9d,
        0x58, 0xe9, 0xa6, 0x63, 0xaf, 0xa6, 0xfe, 0x00, 0xe8, 0xb7, 0x3c, 0x60,
        0xde, 0x79, 0x52, 0x68, 0x8b, 0x44, 0x6d, 0xe9, 0xb7, 0x2c, 0x34, 0x9f,
        0xa5, 0xb7, 0xdb, 0x5a, 0x27, 0x3a
    };
    static const unsigned char expected_aggnonce[66] = {
        0x02, 0xca, 0x8f, 0x0a, 0x06, 0x95, 0x1f, 0xed, 0x10, 0x82, 0x96, 0x00,
        0xcb, 0x15, 0x81, 0xb1, 0xf6, 0x15, 0x31, 0xa5, 0x98, 0x97, 0xa8, 0x27,
        0x11, 0x6a, 0xd0, 0x57, 0xae, 0x91, 0x68, 0x28, 0x74, 0x03, 0xa8, 0x0f,
        0xb3, 0x5e, 0x70, 0xa9, 0xc1, 0xfd, 0x00, 0xd4, 0xcb, 0x4b, 0x79, 0x6f,
        0x60, 0xf1, 0x9d, 0x33, 0x05, 0x01, 0x86, 0x45, 0x52, 0xdb, 0xa6, 0xf1,
        0x69, 0xed, 0x8b, 0xb6, 0x8c, 0xeb
    };
    static const unsigned char expected_inf_second[33] = {
        0x02, 0x2f, 0x8c, 0x4c, 0x80, 0xa4, 0x9f, 0x4e, 0x7b, 0x97, 0x35, 0x6a,
        0xed, 0x72, 0x3c, 0x3a, 0x17, 0xb8, 0x86, 0x0a, 0x58, 0x68, 0x8e, 0x83,
        0x03, 0x0d, 0x36, 0x95, 0xbe, 0x1e, 0x5f, 0x99, 0x91
    };
    secp256k1_pubkey pubshare;
    secp256k1_frost_secnonce secnonce;
    secp256k1_frost_secnonce secnonce2;
    secp256k1_frost_pubnonce pubnonce;
    secp256k1_frost_pubnonce pubnonce2;
    secp256k1_frost_aggnonce aggnonce;
    unsigned char rand[32];
    unsigned char secshare[32];
    unsigned char extra_in[16];
    unsigned char buf[66];
    secp256k1_scalar k[2];
    secp256k1_ge ges[2];

    /* Full-input case: all optional inputs present. */
    memset(rand, 0x0a, 32);
    memset(secshare, 0x0b, 32);
    memset(extra_in, 0x0c, 16);
    CHECK(secp256k1_ec_pubkey_parse(CTX, &pubshare, pubshare33, 33) == 1);
    CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce, &pubnonce, rand, secshare, &pubshare, thresh_pk32, msg, sizeof(msg) - 1, extra_in, sizeof(extra_in)) == 1);
    CHECK(secp256k1_frost_secnonce_load(CTX, k, &secnonce) == 1);
    secp256k1_scalar_get_b32(&buf[0], &k[0]);
    secp256k1_scalar_get_b32(&buf[32], &k[1]);
    CHECK(secp256k1_memcmp_var(buf, expected_full_secnonce, 64) == 0);
    CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf, &pubnonce) == 1);
    CHECK(secp256k1_memcmp_var(buf, expected_full_pubnonce, 66) == 0);
    /* nonce_gen wipes the session randomness before returning. */
    CHECK(secp256k1_is_zero_array(rand, 32));

    /* Minimal case: no optional inputs. */
    memset(rand, 0x0d, 32);
    CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce2, &pubnonce2, rand, NULL, NULL, NULL, NULL, 0, NULL, 0) == 1);
    CHECK(secp256k1_frost_secnonce_load(CTX, k, &secnonce2) == 1);
    secp256k1_scalar_get_b32(&buf[0], &k[0]);
    secp256k1_scalar_get_b32(&buf[32], &k[1]);
    CHECK(secp256k1_memcmp_var(buf, expected_min_secnonce, 64) == 0);
    CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf, &pubnonce2) == 1);
    CHECK(secp256k1_memcmp_var(buf, expected_min_pubnonce, 66) == 0);
    CHECK(secp256k1_is_zero_array(rand, 32));

    /* Aggregating the two pubnonces. */
    {
        const secp256k1_frost_pubnonce *pubnonce_ptrs[2];

        pubnonce_ptrs[0] = &pubnonce;
        pubnonce_ptrs[1] = &pubnonce2;

        CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 2) == 1);
        CHECK(secp256k1_frost_aggnonce_serialize(CTX, buf, &aggnonce) == 1);
        CHECK(secp256k1_memcmp_var(buf, expected_aggnonce, 66) == 0);
    }

    /* A column whose sum is the point at infinity is allowed in an aggnonce
     * and serializes as 33 zero bytes. */
    {
        secp256k1_frost_pubnonce pubnonce_neg;
        const secp256k1_frost_pubnonce *pubnonce_ptrs[2];

        pubnonce_ptrs[0] = &pubnonce;
        pubnonce_ptrs[1] = &pubnonce_neg;

        CHECK(secp256k1_frost_pubnonce_load(CTX, ges, &pubnonce) == 1);
        secp256k1_ge_neg(&ges[0], &ges[0]);
        secp256k1_frost_pubnonce_save(&pubnonce_neg, ges);
        CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 2) == 1);
        CHECK(secp256k1_frost_aggnonce_serialize(CTX, buf, &aggnonce) == 1);
        CHECK(secp256k1_is_zero_array(buf, 33));
        CHECK(secp256k1_memcmp_var(&buf[33], expected_inf_second, 33) == 0);

        /* An aggnonce with an infinity component can be parsed; a pubnonce
         * can not. */
        CHECK(secp256k1_frost_aggnonce_parse(CTX, &aggnonce, buf) == 1);
        CHECK(secp256k1_frost_pubnonce_parse(CTX, &pubnonce_neg, buf) == 0);
    }
}

static void frost_nonce_test_internal(void) {
    secp256k1_frost_secnonce secnonce[3];
    secp256k1_frost_pubnonce pubnonce[3];
    secp256k1_frost_aggnonce aggnonce;
    unsigned char rand[32];
    unsigned char msg[32];
    unsigned char extra_in[16];
    unsigned char secshare[32];
    unsigned char thresh_pk32[32];
    secp256k1_pubkey pubshare;
    unsigned char buf66[66];
    size_t msglen, extra_in_len;
    size_t i;
    int j;

    /* Generate nonces with randomly chosen optional inputs. */
    for (i = 0; i < 3; i++) {
        const unsigned char *secshare_ptr = NULL;
        const secp256k1_pubkey *pubshare_ptr = NULL;
        const unsigned char *thresh_pk32_ptr = NULL;
        const unsigned char *msg_ptr = NULL;
        const unsigned char *extra_in_ptr = NULL;

        testrand256(rand);
        testutil_random_scalar_order_b32(secshare);
        testrand256(thresh_pk32);
        msglen = testrand_int(sizeof(msg) + 1);
        testrand256(msg);
        extra_in_len = testrand_int(sizeof(extra_in) + 1);
        testrand256(extra_in);
        if (testrand_bits(1)) {
            secp256k1_pubkey pubshare_tmp;
            CHECK(secp256k1_ec_pubkey_create(CTX, &pubshare_tmp, secshare) == 1);
            pubshare = pubshare_tmp;
            secshare_ptr = secshare;
            pubshare_ptr = &pubshare;
        }
        if (testrand_bits(1)) {
            thresh_pk32_ptr = thresh_pk32;
        }
        if (testrand_bits(1)) {
            msg_ptr = msg;
        } else {
            msglen = 0;
        }
        if (testrand_bits(1)) {
            extra_in_ptr = extra_in;
        } else {
            extra_in_len = 0;
        }
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], rand, secshare_ptr, pubshare_ptr, thresh_pk32_ptr, msg_ptr, msglen, extra_in_ptr, extra_in_len) == 1);
        CHECK(secp256k1_is_zero_array(rand, 32));
    }

    /* Serialize/parse round-trip preserves the internal representation. */
    for (i = 0; i < 3; i++) {
        secp256k1_frost_pubnonce parsed;
        secp256k1_ge ges[2], ges_parsed[2];

        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf66, &pubnonce[i]) == 1);
        CHECK(secp256k1_frost_pubnonce_parse(CTX, &parsed, buf66) == 1);
        CHECK(secp256k1_frost_pubnonce_load(CTX, ges, &pubnonce[i]) == 1);
        CHECK(secp256k1_frost_pubnonce_load(CTX, ges_parsed, &parsed) == 1);
        for (j = 0; j < 2; j++) {
            CHECK(secp256k1_ge_eq_var(&ges[j], &ges_parsed[j]));
        }
    }

    /* Aggregating a single pubnonce (u = 1) yields the same points. */
    {
        const secp256k1_frost_pubnonce *pubnonce_ptrs[1];
        secp256k1_ge ges[2], ges_agg[2];

        pubnonce_ptrs[0] = &pubnonce[0];

        CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 1) == 1);
        CHECK(secp256k1_frost_pubnonce_load(CTX, ges, &pubnonce[0]) == 1);
        CHECK(secp256k1_frost_aggnonce_load(CTX, ges_agg, &aggnonce) == 1);
        for (j = 0; j < 2; j++) {
            CHECK(secp256k1_ge_eq_var(&ges[j], &ges_agg[j]));
        }
    }

    /* Aggregating u nonces matches the manual column sums. */
    {
        const secp256k1_frost_pubnonce *pubnonce_ptrs[3];
        secp256k1_gej sumj[2];
        secp256k1_ge sums[2], ges_agg[2];

        pubnonce_ptrs[0] = &pubnonce[0];
        pubnonce_ptrs[1] = &pubnonce[1];
        pubnonce_ptrs[2] = &pubnonce[2];

        CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 3) == 1);
        secp256k1_gej_set_infinity(&sumj[0]);
        secp256k1_gej_set_infinity(&sumj[1]);
        for (i = 0; i < 3; i++) {
            secp256k1_ge ges[2];
            CHECK(secp256k1_frost_pubnonce_load(CTX, ges, &pubnonce[i]) == 1);
            for (j = 0; j < 2; j++) {
                secp256k1_gej_add_ge_var(&sumj[j], &sumj[j], &ges[j], NULL);
            }
        }
        secp256k1_ge_set_all_gej_var(sums, sumj, 2);
        CHECK(secp256k1_frost_aggnonce_load(CTX, ges_agg, &aggnonce) == 1);
        for (j = 0; j < 2; j++) {
            CHECK(secp256k1_ge_eq_var(&sums[j], &ges_agg[j]));
        }
    }

    /* A NULL array element is a missing contribution: nonce_agg fails and
     * reports its index. */
    {
        const secp256k1_frost_pubnonce *pubnonce_ptrs[3];
        size_t null_index = testrand_int(3);
        size_t error_index = 0xFF;

        pubnonce_ptrs[0] = &pubnonce[0];
        pubnonce_ptrs[1] = &pubnonce[1];
        pubnonce_ptrs[2] = &pubnonce[2];
        pubnonce_ptrs[null_index] = NULL;
        CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, &error_index, pubnonce_ptrs, 3) == 0);
        CHECK(error_index == null_index);
        CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 3) == 0);
    }

    /* Domain separation: changing any optional input changes the pubnonce. */
    {
        unsigned char buf_base[66], buf_variant[66];
        unsigned char rand_copy[32];

        testrand256(rand);
        memcpy(rand_copy, rand, 32);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], rand_copy, NULL, NULL, NULL, NULL, 0, NULL, 0) == 1);
        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf_base, &pubnonce[0]) == 1);

        /* msg = NULL differs from msg present (even when empty). */
        memcpy(rand_copy, rand, 32);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[1], rand_copy, NULL, NULL, NULL, msg, 0, NULL, 0) == 1);
        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf_variant, &pubnonce[1]) == 1);
        CHECK(secp256k1_memcmp_var(buf_base, buf_variant, 66) != 0);

        /* A different msg. */
        memcpy(rand_copy, rand, 32);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[1], rand_copy, NULL, NULL, NULL, msg, 1, NULL, 0) == 1);
        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf_variant, &pubnonce[1]) == 1);
        CHECK(secp256k1_memcmp_var(buf_base, buf_variant, 66) != 0);

        /* extra_in present. */
        memcpy(rand_copy, rand, 32);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[1], rand_copy, NULL, NULL, NULL, NULL, 0, extra_in, 1) == 1);
        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf_variant, &pubnonce[1]) == 1);
        CHECK(secp256k1_memcmp_var(buf_base, buf_variant, 66) != 0);

        /* secshare and pubshare present. */
        testutil_random_scalar_order_b32(secshare);
        CHECK(secp256k1_ec_pubkey_create(CTX, &pubshare, secshare) == 1);
        memcpy(rand_copy, rand, 32);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[1], rand_copy, secshare, &pubshare, NULL, NULL, 0, NULL, 0) == 1);
        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf_variant, &pubnonce[1]) == 1);
        CHECK(secp256k1_memcmp_var(buf_base, buf_variant, 66) != 0);

        /* thresh_pk32 present. */
        testrand256(thresh_pk32);
        memcpy(rand_copy, rand, 32);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[1], rand_copy, NULL, NULL, thresh_pk32, NULL, 0, NULL, 0) == 1);
        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf_variant, &pubnonce[1]) == 1);
        CHECK(secp256k1_memcmp_var(buf_base, buf_variant, 66) != 0);
    }

    /* Invalid arguments. */
    testrand256(rand);
    testutil_random_scalar_order_b32(secshare);
    CHECK_ILLEGAL(STATIC_CTX, secp256k1_frost_nonce_gen(STATIC_CTX, &secnonce[0], &pubnonce[0], rand, NULL, NULL, NULL, NULL, 0, NULL, 0));
    CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, NULL, &pubnonce[0], rand, NULL, NULL, NULL, NULL, 0, NULL, 0));
    CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], NULL, rand, NULL, NULL, NULL, NULL, 0, NULL, 0));
    CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], NULL, NULL, NULL, NULL, NULL, 0, NULL, 0));
    CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], rand, NULL, NULL, NULL, NULL, 1, NULL, 0));
    CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], rand, NULL, NULL, NULL, NULL, 0, NULL, 1));

    /* An invalid secshare (zero) makes nonce_gen fail and still wipes the
     * session randomness. */
    {
        unsigned char zero_seckey[32] = { 0 };
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], rand, zero_seckey, NULL, NULL, NULL, 0, NULL, 0) == 0);
        CHECK(secp256k1_is_zero_array(rand, 32));
    }

    /* nonce_agg argument validation. */
    {
        const secp256k1_frost_pubnonce *pubnonce_ptrs[3];

        pubnonce_ptrs[0] = &pubnonce[0];
        pubnonce_ptrs[1] = &pubnonce[1];
        pubnonce_ptrs[2] = &pubnonce[2];

        CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_agg(CTX, NULL, NULL, pubnonce_ptrs, 3));
        CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, NULL, 3));
        CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 0));
    }
}
REPEAT_TEST(frost_nonce_test)

/* Fixed-value tests of a full signing session (BIP 445 GetSessionValues,
 * Sign, PartialSigVerify and PartialSigAgg) with threshold_seckey32 =
 * 0x0102...20, n = 5, t = 3, signers with ids {0, 2, 4} and msg =
 * "BIP 445 phase 6 anchor". The expected values have been verified against
 * the BIP 445 Python reference implementation. */
static void frost_sign_vector_test(void) {
    static const unsigned char msg[] = "BIP 445 phase 6 anchor";
    static const unsigned char expected_pubnonce0[66] = {
        0x03, 0x74, 0x8a, 0x28, 0x3e, 0x69, 0x78, 0x74, 0x88, 0x70, 0xff, 0x3e,
        0x30, 0x30, 0xe8, 0x1c, 0x69, 0x9c, 0x2b, 0xad, 0x22, 0x1c, 0xec, 0x8c,
        0xd8, 0xb4, 0xaf, 0x87, 0xfb, 0xae, 0x20, 0x1e, 0xf1, 0x03, 0x3b, 0x3e,
        0xa4, 0x7d, 0xf6, 0xca, 0x58, 0x6c, 0x78, 0x8c, 0x79, 0x4f, 0x86, 0x3d,
        0x7c, 0xbb, 0x11, 0x27, 0x74, 0x95, 0xc6, 0x9a, 0x8d, 0x85, 0x81, 0x44,
        0x53, 0xf9, 0xb3, 0x1d, 0x99, 0x3a
    };
    static const unsigned char expected_pubnonce2[66] = {
        0x02, 0xbc, 0xa1, 0x24, 0x83, 0x98, 0x22, 0x6f, 0xef, 0xcb, 0x5e, 0x3f,
        0x28, 0xd6, 0x9b, 0x4b, 0x69, 0xf7, 0x12, 0xf3, 0xde, 0xe4, 0xca, 0x62,
        0xb0, 0xcb, 0xe9, 0xc8, 0xd0, 0x9c, 0x62, 0x33, 0x7a, 0x02, 0x31, 0xc1,
        0x8e, 0xd3, 0xc6, 0x57, 0x91, 0xf7, 0x01, 0x10, 0xde, 0x63, 0x50, 0x43,
        0xfb, 0xc6, 0x98, 0x1f, 0x71, 0xe3, 0x52, 0x90, 0x61, 0x98, 0x7f, 0x75,
        0x3a, 0x10, 0x48, 0x7f, 0x45, 0x20
    };
    static const unsigned char expected_pubnonce4[66] = {
        0x02, 0xf1, 0x7c, 0xe6, 0xbd, 0x2d, 0xe9, 0x97, 0x81, 0x5d, 0xdd, 0xb6,
        0x5d, 0x44, 0xea, 0x8c, 0x24, 0xfc, 0x6d, 0xd6, 0x41, 0xef, 0x0c, 0xc6,
        0x53, 0xa5, 0x1b, 0x82, 0x2d, 0xab, 0x86, 0x23, 0x61, 0x03, 0xb9, 0x56,
        0x14, 0xc7, 0xb2, 0xea, 0x79, 0x58, 0x9b, 0x3c, 0xb7, 0xc7, 0x47, 0x0f,
        0x9f, 0xf7, 0x3e, 0xe3, 0x57, 0x83, 0xcb, 0x9b, 0x7f, 0x0d, 0xf4, 0x8f,
        0xb0, 0x64, 0x3f, 0xdd, 0x74, 0xb1
    };
    static const unsigned char expected_aggnonce[66] = {
        0x03, 0xd8, 0x6c, 0xa2, 0xd4, 0xd8, 0x0b, 0x3e, 0xe6, 0xdf, 0xf7, 0xfc,
        0xab, 0x19, 0xb1, 0xf8, 0x60, 0x61, 0x8b, 0xd3, 0x0a, 0xbc, 0x2a, 0xe9,
        0x8d, 0x51, 0xb3, 0x7a, 0x2c, 0x89, 0x2f, 0x38, 0xec, 0x02, 0x37, 0x03,
        0x1d, 0x23, 0x71, 0x1b, 0xd0, 0x3b, 0x32, 0xb0, 0xab, 0x1f, 0xd2, 0x5e,
        0xed, 0x30, 0xf1, 0xcf, 0xb8, 0x7f, 0x11, 0xb6, 0xa0, 0xc7, 0xb6, 0x01,
        0x56, 0x93, 0x80, 0x2e, 0xda, 0x3e
    };
    static const unsigned char expected_psig0[32] = {
        0xce, 0xa4, 0x74, 0xd1, 0xb8, 0xe3, 0x1b, 0xa3, 0xcd, 0x7a, 0xde, 0xe2,
        0xe5, 0x61, 0x12, 0x9c, 0xb0, 0x97, 0x88, 0x9f, 0x41, 0xcc, 0xf3, 0xc6,
        0x2b, 0x6e, 0x4f, 0x41, 0x63, 0x10, 0x00, 0x29
    };
    static const unsigned char expected_psig2[32] = {
        0xaa, 0x89, 0xef, 0x0c, 0x22, 0xb6, 0xae, 0xa2, 0x6a, 0x18, 0x40, 0x3a,
        0xc1, 0x2a, 0x5c, 0x4a, 0xfd, 0x84, 0x87, 0x89, 0x4e, 0xcd, 0x6b, 0x3e,
        0x69, 0x6f, 0xb0, 0x85, 0xaf, 0x91, 0x0e, 0x11
    };
    static const unsigned char expected_psig4[32] = {
        0x17, 0x73, 0xf9, 0x60, 0x68, 0x41, 0xd5, 0x3e, 0xe8, 0x49, 0x21, 0x39,
        0xf6, 0xda, 0xdd, 0xf2, 0xe7, 0xfb, 0x0c, 0xa0, 0xe9, 0xe2, 0x00, 0xd7,
        0x34, 0xea, 0x17, 0xb2, 0xfa, 0x6b, 0x77, 0x87
    };
    static const unsigned char expected_sig_a[64] = {
        0x5f, 0x4f, 0x69, 0xac, 0xf5, 0x19, 0xbc, 0x24, 0x02, 0xeb, 0xe9, 0x1f,
        0x9d, 0x54, 0xf9, 0x4c, 0xbc, 0xee, 0xd2, 0xc0, 0x62, 0x0b, 0x62, 0xa6,
        0x9b, 0x2f, 0x18, 0x64, 0xea, 0xd1, 0xdb, 0x41, 0x90, 0xa2, 0x5d, 0x3e,
        0x43, 0xdb, 0x9f, 0x85, 0x1f, 0xdc, 0x40, 0x57, 0x9d, 0x66, 0x4c, 0xdb,
        0xdb, 0x68, 0x3f, 0xe2, 0xcb, 0x33, 0xbf, 0xa0, 0x09, 0xf5, 0xb8, 0xed,
        0x3c, 0xd6, 0x44, 0x80
    };
    static const unsigned char expected_sig_b[64] = {
        0x9e, 0x1f, 0x53, 0xfa, 0xdd, 0x44, 0x5d, 0x70, 0x34, 0x8b, 0xae, 0xa1,
        0x98, 0xa3, 0x25, 0x9e, 0xd3, 0xef, 0xa9, 0x33, 0xcc, 0x0c, 0xe3, 0x25,
        0xa5, 0xc0, 0x4d, 0xef, 0x1c, 0x75, 0xf8, 0x80, 0x8e, 0x7c, 0x0d, 0xa7,
        0x3c, 0x29, 0xa2, 0x08, 0x68, 0xcf, 0x0f, 0x8b, 0x85, 0xbf, 0x17, 0xbf,
        0xa4, 0x44, 0xda, 0xa5, 0x23, 0x38, 0x88, 0x6d, 0x7f, 0xe5, 0x7e, 0x8b,
        0x92, 0x7e, 0xa4, 0xfe
    };
    static const unsigned char expected_tweaked_pk_b[32] = {
        0x55, 0xd2, 0xac, 0xd9, 0x3c, 0xcd, 0x26, 0x82, 0xc7, 0x49, 0xd5, 0x97,
        0xaf, 0x74, 0x26, 0x72, 0x6d, 0x31, 0x65, 0x42, 0x62, 0x04, 0x70, 0x8b,
        0xe4, 0xaa, 0x8e, 0x81, 0xe2, 0xbb, 0x8c, 0xd4
    };
    const unsigned char * const expected_pubnonces[3] = {
        expected_pubnonce0, expected_pubnonce2, expected_pubnonce4
    };
    const unsigned char * const expected_psigs[3] = {
        expected_psig0, expected_psig2, expected_psig4
    };
    static const uint32_t ids[3] = { 0, 2, 4 };
    unsigned char thresh_sk[32];
    unsigned char secshares[5 * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[5];
    secp256k1_pubkey signer_pubshares[3];
    secp256k1_frost_secnonce secnonce[3];
    secp256k1_frost_pubnonce pubnonce[3];
    const secp256k1_frost_pubnonce *pubnonce_ptrs[3];
    secp256k1_frost_partial_sig psig[3];
    const secp256k1_frost_partial_sig *psig_ptrs[3];
    secp256k1_frost_aggnonce aggnonce;
    secp256k1_frost_tweak_cache cache;
    secp256k1_frost_session session;
    secp256k1_xonly_pubkey xonly_pk, tweaked_pk;
    unsigned char rand[32];
    unsigned char thresh_pk32[32];
    unsigned char pk32[32];
    unsigned char buf[66];
    unsigned char sig64[64];
    unsigned char tweak[32];
    size_t i;

    for (i = 0; i < 32; i++) {
        thresh_sk[i] = (unsigned char)(i + 1);
    }
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 5, 3, thresh_sk) == 1);
    CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &thresh_pk) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, thresh_pk32, &xonly_pk) == 1);
    for (i = 0; i < 3; i++) {
        signer_pubshares[i] = pubshares[ids[i]];
        pubnonce_ptrs[i] = &pubnonce[i];
        psig_ptrs[i] = &psig[i];
    }

    /* Case A: no tweaks. */
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);
    for (i = 0; i < 3; i++) {
        memset(rand, 0xa0 + i, 32);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], rand, &secshares[32 * ids[i]], &signer_pubshares[i], thresh_pk32, msg, sizeof(msg) - 1, NULL, 0) == 1);
        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf, &pubnonce[i]) == 1);
        CHECK(secp256k1_memcmp_var(buf, expected_pubnonces[i], 66) == 0);
    }
    CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 3) == 1);
    CHECK(secp256k1_frost_aggnonce_serialize(CTX, buf, &aggnonce) == 1);
    CHECK(secp256k1_memcmp_var(buf, expected_aggnonce, 66) == 0);

    CHECK(secp256k1_frost_session_init(CTX, &session, &aggnonce, ids, signer_pubshares, 3, 5, 3, &cache, msg, sizeof(msg) - 1) == 1);
    for (i = 0; i < 3; i++) {
        CHECK(secp256k1_frost_sign(CTX, &psig[i], &secnonce[i], &secshares[32 * ids[i]], &session, ids, signer_pubshares, 3, ids[i]) == 1);
        CHECK(secp256k1_frost_partial_sig_serialize(CTX, buf, &psig[i]) == 1);
        CHECK(secp256k1_memcmp_var(buf, expected_psigs[i], 32) == 0);
        CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[i], &pubnonce[i], &signer_pubshares[i], &session, ids, 3, i) == 1);
    }
    CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, psig_ptrs, 3) == 1);
    CHECK(secp256k1_memcmp_var(sig64, expected_sig_a, 64) == 0);
    CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg, sizeof(msg) - 1, &xonly_pk) == 1);

    /* Case B: one xonly tweak 0x02...02 applied to the tweak cache. The
     * session inputs (and thus the nonces) are identical to case A. */
    memset(tweak, 0x02, 32);
    CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, &tweaked_pk, &cache, tweak) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, pk32, &tweaked_pk) == 1);
    CHECK(secp256k1_memcmp_var(pk32, expected_tweaked_pk_b, 32) == 0);
    for (i = 0; i < 3; i++) {
        memset(rand, 0xa0 + i, 32);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], rand, &secshares[32 * ids[i]], &signer_pubshares[i], thresh_pk32, msg, sizeof(msg) - 1, NULL, 0) == 1);
    }
    CHECK(secp256k1_frost_session_init(CTX, &session, &aggnonce, ids, signer_pubshares, 3, 5, 3, &cache, msg, sizeof(msg) - 1) == 1);
    for (i = 0; i < 3; i++) {
        CHECK(secp256k1_frost_sign(CTX, &psig[i], &secnonce[i], &secshares[32 * ids[i]], &session, ids, signer_pubshares, 3, ids[i]) == 1);
        CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[i], &pubnonce[i], &signer_pubshares[i], &session, ids, 3, i) == 1);
    }
    CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, psig_ptrs, 3) == 1);
    CHECK(secp256k1_memcmp_var(sig64, expected_sig_b, 64) == 0);
    CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg, sizeof(msg) - 1, &tweaked_pk) == 1);
}

/* Fixed-value tests of deterministic signing (BIP 445 DeterministicSign) with
 * threshold_seckey32 = 0x0102...20, n = 3, t = 1 and msg =
 * "BIP 445 phase 6 anchor". With t = 1, every secret share equals the
 * threshold secret and every pubshare equals the threshold public key. The
 * expected values have been verified against the BIP 445 Python reference
 * implementation. */
static void frost_det_sign_vector_test(void) {
    static const unsigned char msg[] = "BIP 445 phase 6 anchor";
    static const unsigned char expected_det_pubnonce_c[66] = {
        0x03, 0x9d, 0x34, 0xba, 0xb5, 0x4f, 0xf0, 0x63, 0xeb, 0xa8, 0x34, 0xa8,
        0xbf, 0x4f, 0xbf, 0x51, 0x72, 0xda, 0xe8, 0xdc, 0x5c, 0x4f, 0x3d, 0xf6,
        0xe3, 0x9f, 0xd9, 0xd5, 0x1d, 0xc9, 0x25, 0x93, 0x2e, 0x02, 0xc5, 0x62,
        0x90, 0x0e, 0x2f, 0x3b, 0x59, 0xa3, 0x5d, 0x70, 0x4a, 0xdd, 0xf1, 0xea,
        0xed, 0xfc, 0x40, 0x00, 0x59, 0xae, 0xd3, 0x36, 0x6b, 0x20, 0x7b, 0xea,
        0x96, 0xf9, 0xce, 0xd9, 0x3b, 0xeb
    };
    static const unsigned char expected_det_psig_c[32] = {
        0x80, 0x67, 0x74, 0x87, 0xff, 0x6c, 0x1a, 0xdd, 0xee, 0x7a, 0x29, 0xbf,
        0x69, 0x3c, 0x8c, 0xee, 0x89, 0x7e, 0xed, 0x9c, 0x60, 0x3c, 0x05, 0x86,
        0x2d, 0x95, 0x98, 0x1e, 0x74, 0x44, 0x4e, 0xb4
    };
    static const unsigned char expected_pubnonce_d0[66] = {
        0x02, 0x8b, 0xde, 0xef, 0x70, 0x39, 0x48, 0x77, 0x04, 0xa3, 0xd6, 0xdd,
        0xe6, 0x46, 0xd0, 0x88, 0xe0, 0x29, 0x80, 0x66, 0x96, 0xd4, 0x4a, 0xa0,
        0xd9, 0x75, 0x4a, 0x41, 0xbd, 0x4d, 0x91, 0x6c, 0xd9, 0x03, 0x75, 0xa8,
        0x5e, 0x0b, 0x0a, 0xfe, 0x4a, 0xf7, 0x53, 0xdf, 0x09, 0x5c, 0x0e, 0xc8,
        0x7b, 0xa9, 0x75, 0xfa, 0x74, 0x4e, 0x00, 0x3c, 0x6d, 0x7a, 0xd7, 0x49,
        0xc2, 0x13, 0xb3, 0xc3, 0xc6, 0x1e
    };
    static const unsigned char expected_pubnonce_d1[66] = {
        0x02, 0x1e, 0x05, 0xfb, 0xf1, 0xa2, 0x33, 0x08, 0xf8, 0xc5, 0x67, 0xc4,
        0x87, 0x3a, 0x8a, 0xcf, 0x1c, 0x9b, 0xc9, 0x88, 0xcb, 0x18, 0x10, 0x18,
        0x5f, 0xd8, 0x45, 0x06, 0xcd, 0xe2, 0x6d, 0x6c, 0x1c, 0x02, 0x00, 0x4e,
        0x9e, 0x9c, 0xea, 0x6c, 0x1e, 0x11, 0x56, 0x6a, 0x25, 0x2f, 0x12, 0x7d,
        0x52, 0xd7, 0xe4, 0x30, 0xb8, 0xe5, 0xe2, 0x21, 0x24, 0x52, 0x4c, 0x5f,
        0xe7, 0x2f, 0xc1, 0xa0, 0x8e, 0x02
    };
    static const unsigned char expected_psig_d1[32] = {
        0x3d, 0xdd, 0xe1, 0x1e, 0x87, 0xab, 0x4b, 0xd7, 0x3e, 0x65, 0xf3, 0x71,
        0x79, 0x62, 0x23, 0xac, 0x06, 0x1a, 0x13, 0x4f, 0x2a, 0x08, 0xb5, 0xce,
        0x1e, 0x7b, 0x93, 0x8b, 0x14, 0x82, 0x2e, 0x0c
    };
    static const unsigned char expected_sig_d[64] = {
        0xaa, 0xd7, 0x30, 0xc9, 0x2e, 0xb6, 0x51, 0x17, 0x59, 0xd7, 0x35, 0x84,
        0x31, 0x15, 0x0d, 0x4a, 0x08, 0x26, 0x47, 0x29, 0xe1, 0xb3, 0x3e, 0x96,
        0x93, 0xbe, 0x5b, 0xf7, 0x8b, 0x06, 0x0a, 0xea, 0xa8, 0xd7, 0xfa, 0xfc,
        0x09, 0x26, 0x3d, 0x80, 0x4b, 0xd3, 0x67, 0x74, 0xe5, 0xcb, 0x33, 0xdd,
        0xaf, 0xd1, 0x90, 0x1b, 0x54, 0x53, 0x3d, 0xb2, 0x43, 0x3b, 0x0f, 0xf7,
        0xcf, 0x0a, 0xcc, 0xda
    };
    unsigned char thresh_sk[32];
    unsigned char secshares[3 * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[3];
    secp256k1_frost_tweak_cache cache;
    secp256k1_frost_session session;
    secp256k1_frost_aggnonce aggnonce;
    secp256k1_xonly_pubkey xonly_pk;
    unsigned char pk32[32];
    unsigned char buf[66];
    unsigned char sig64[64];
    size_t i;

    for (i = 0; i < 32; i++) {
        thresh_sk[i] = (unsigned char)(i + 1);
    }
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 3, 1, thresh_sk) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);
    CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &thresh_pk) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, pk32, &xonly_pk) == 1);

    /* Case C: sole signer with id = 2, aggothernonce = NULL, aux_rand =
     * 0x77...77. */
    {
        static const uint32_t ids[1] = { 2 };
        secp256k1_frost_partial_sig psig;
        const secp256k1_frost_partial_sig *psig_ptrs[1];
        secp256k1_frost_pubnonce pubnonce;
        const secp256k1_frost_pubnonce *pubnonce_ptrs[1];
        unsigned char aux_rand[32];

        pubnonce_ptrs[0] = &pubnonce;

        psig_ptrs[0] = &psig;

        memset(aux_rand, 0x77, 32);
        CHECK(secp256k1_frost_deterministic_sign(CTX, &psig, &pubnonce, &secshares[2 * 32], 2, NULL, ids, &pubshares[2], 1, 3, 1, &cache, msg, sizeof(msg) - 1, aux_rand) == 1);
        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf, &pubnonce) == 1);
        CHECK(secp256k1_memcmp_var(buf, expected_det_pubnonce_c, 66) == 0);
        CHECK(secp256k1_frost_partial_sig_serialize(CTX, buf, &psig) == 1);
        CHECK(secp256k1_memcmp_var(buf, expected_det_psig_c, 32) == 0);

        /* The coordinator aggregates the single pubnonce and verifies and
         * aggregates the partial signature. */
        CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 1) == 1);
        CHECK(secp256k1_frost_session_init(CTX, &session, &aggnonce, ids, &pubshares[2], 1, 3, 1, &cache, msg, sizeof(msg) - 1) == 1);
        CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig, &pubnonce, &pubshares[2], &session, ids, 1, 0) == 1);
        CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, psig_ptrs, 1) == 1);
        CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg, sizeof(msg) - 1, &xonly_pk) == 1);
    }

    /* Case D: u = 2, ids = {0, 1}, no aux_rand. Signer 0 uses frost_nonce_gen
     * with rand = 0xd0...d0; signer 1 uses frost_deterministic_sign with
     * aggothernonce = nonce_agg([pubnonce of signer 0]). */
    {
        static const uint32_t ids[2] = { 0, 1 };
        secp256k1_pubkey signer_pubshares[2];
        secp256k1_frost_secnonce secnonce;
        secp256k1_frost_pubnonce pubnonce[2];
        const secp256k1_frost_pubnonce *pubnonce_ptrs[2];
        secp256k1_frost_partial_sig psig[2];
        const secp256k1_frost_partial_sig *psig_ptrs[2];
        secp256k1_frost_aggnonce aggothernonce;
        unsigned char rand[32];

        psig_ptrs[0] = &psig[0];
        psig_ptrs[1] = &psig[1];

        pubnonce_ptrs[0] = &pubnonce[0];
        pubnonce_ptrs[1] = &pubnonce[1];

        signer_pubshares[0] = pubshares[0];
        signer_pubshares[1] = pubshares[1];

        memset(rand, 0xd0, 32);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce, &pubnonce[0], rand, &secshares[0], &pubshares[0], pk32, msg, sizeof(msg) - 1, NULL, 0) == 1);
        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf, &pubnonce[0]) == 1);
        CHECK(secp256k1_memcmp_var(buf, expected_pubnonce_d0, 66) == 0);
        CHECK(secp256k1_frost_nonce_agg(CTX, &aggothernonce, NULL, pubnonce_ptrs, 1) == 1);
        CHECK(secp256k1_frost_deterministic_sign(CTX, &psig[1], &pubnonce[1], &secshares[32], 1, &aggothernonce, ids, signer_pubshares, 2, 3, 1, &cache, msg, sizeof(msg) - 1, NULL) == 1);
        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf, &pubnonce[1]) == 1);
        CHECK(secp256k1_memcmp_var(buf, expected_pubnonce_d1, 66) == 0);
        CHECK(secp256k1_frost_partial_sig_serialize(CTX, buf, &psig[1]) == 1);
        CHECK(secp256k1_memcmp_var(buf, expected_psig_d1, 32) == 0);

        CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 2) == 1);
        CHECK(secp256k1_frost_session_init(CTX, &session, &aggnonce, ids, signer_pubshares, 2, 3, 1, &cache, msg, sizeof(msg) - 1) == 1);
        CHECK(secp256k1_frost_sign(CTX, &psig[0], &secnonce, &secshares[0], &session, ids, signer_pubshares, 2, 0) == 1);
        CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[0], &pubnonce[0], &signer_pubshares[0], &session, ids, 2, 0) == 1);
        CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[1], &pubnonce[1], &signer_pubshares[1], &session, ids, 2, 1) == 1);
        CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, psig_ptrs, 2) == 1);
        CHECK(secp256k1_memcmp_var(sig64, expected_sig_d, 64) == 0);
        CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg, sizeof(msg) - 1, &xonly_pk) == 1);
    }
}

#define FROST_SIGN_TEST_MAX_N 8

/* Runs a randomized full signing round: trusted-dealer keygen, random signer
 * subset, random tweak chain (xonly and plain), random message length
 * (including 0 and > 32), nonce gen/agg, session init, sign, partial sig
 * verify and aggregation with a final BIP340 verification against the tweaked
 * threshold public key. Also covers the failure paths of the signing API. */
static void frost_sign_test_internal(void) {
    unsigned char thresh_sk[32];
    unsigned char secshares[FROST_SIGN_TEST_MAX_N * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[FROST_SIGN_TEST_MAX_N];
    secp256k1_pubkey signer_pubshares[FROST_SIGN_TEST_MAX_N];
    uint32_t ids_all[FROST_SIGN_TEST_MAX_N];
    uint32_t ids[FROST_SIGN_TEST_MAX_N];
    secp256k1_frost_secnonce secnonce[FROST_SIGN_TEST_MAX_N];
    secp256k1_frost_pubnonce pubnonce[FROST_SIGN_TEST_MAX_N];
    const secp256k1_frost_pubnonce *pubnonce_ptrs[FROST_SIGN_TEST_MAX_N];
    secp256k1_frost_partial_sig psig[FROST_SIGN_TEST_MAX_N];
    const secp256k1_frost_partial_sig *psig_ptrs[FROST_SIGN_TEST_MAX_N];
    secp256k1_frost_aggnonce aggnonce;
    secp256k1_frost_tweak_cache cache;
    secp256k1_frost_session session;
    secp256k1_xonly_pubkey tweaked_pk;
    unsigned char msg[64];
    unsigned char rand[32];
    unsigned char pk32[32];
    unsigned char sig64[64];
    size_t n = 1 + testrand_int(FROST_SIGN_TEST_MAX_N);
    uint32_t t = 1 + testrand_int((uint32_t)n);
    size_t u = (size_t)t + testrand_int((uint32_t)(n - t + 1));
    size_t msglen = testrand_int(sizeof(msg) + 1);
    int with_pubshares = testrand_bits(1);
    size_t i;
    int j;

    testutil_random_scalar_order_b32(thresh_sk);
    testrand256(msg);
    testrand256(&msg[32]);
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, n, t, thresh_sk) == 1);

    /* Random signer subset via Fisher-Yates shuffle. */
    for (i = 0; i < n; i++) {
        ids_all[i] = (uint32_t)i;
    }
    for (i = 0; i < n; i++) {
        size_t sj = i + testrand_int((uint32_t)(n - i));
        uint32_t tmp = ids_all[i];
        ids_all[i] = ids_all[sj];
        ids_all[sj] = tmp;
    }
    for (i = 0; i < u; i++) {
        ids[i] = ids_all[i];
        signer_pubshares[i] = pubshares[ids[i]];
        pubnonce_ptrs[i] = &pubnonce[i];
        psig_ptrs[i] = &psig[i];
    }

    /* Random tweak chain of up to 3 xonly/plain tweaks. */
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);
    for (j = 0; j < 3; j++) {
        unsigned char tweak[32];
        if (testrand_bits(2) == 0) {
            continue;
        }
        testrand256(tweak);
        if (testrand_bits(1)) {
            CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, NULL, &cache, tweak) == 1);
        } else {
            CHECK(secp256k1_frost_pubkey_ec_tweak_add(CTX, NULL, &cache, tweak) == 1);
        }
    }
    CHECK(secp256k1_frost_tweaked_pubkey_get(CTX, &tweaked_pk, &cache) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, pk32, &tweaked_pk) == 1);

    /* Full signing round. */
    for (i = 0; i < u; i++) {
        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], rand, &secshares[32 * ids[i]], &signer_pubshares[i], pk32, msg, msglen, NULL, 0) == 1);
    }
    CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, u) == 1);
    CHECK(secp256k1_frost_session_init(CTX, &session, &aggnonce, ids, with_pubshares ? signer_pubshares : NULL, u, n, t, &cache, msg, msglen) == 1);
    for (i = 0; i < u; i++) {
        CHECK(secp256k1_frost_sign(CTX, &psig[i], &secnonce[i], &secshares[32 * ids[i]], &session, ids, with_pubshares ? signer_pubshares : NULL, u, ids[i]) == 1);
        CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[i], &pubnonce[i], &signer_pubshares[i], &session, ids, u, i) == 1);
    }
    CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, psig_ptrs, u) == 1);
    CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg, msglen, &tweaked_pk) == 1);

    /* A corrupted partial signature does not verify and invalidates the
     * aggregate signature. */
    {
        secp256k1_frost_partial_sig psig_corrupt;
        const secp256k1_frost_partial_sig *corrupt_ptrs[FROST_SIGN_TEST_MAX_N];

        memcpy(&psig_corrupt, &psig[0], sizeof(psig_corrupt));
        psig_corrupt.data[sizeof(psig_corrupt.data) - 1] ^= 1;
        for (i = 0; i < u; i++) {
            corrupt_ptrs[i] = psig_ptrs[i];
        }
        corrupt_ptrs[0] = &psig_corrupt;
        CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig_corrupt, &pubnonce[0], &signer_pubshares[0], &session, ids, u, 0) == 0);
        CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, corrupt_ptrs, u) == 1);
        CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg, msglen, &tweaked_pk) == 0);
    }

    /* A missing contribution (a partial signature replaced by a duplicate of
     * another one) invalidates the aggregate signature. */
    if (u > 1) {
        const secp256k1_frost_partial_sig *dup_ptrs[FROST_SIGN_TEST_MAX_N];

        for (i = 0; i < u; i++) {
            dup_ptrs[i] = psig_ptrs[i];
        }
        dup_ptrs[1] = &psig[0];
        CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, dup_ptrs, u) == 1);
        CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg, msglen, &tweaked_pk) == 0);
    }

    /* Signing with a wrong secret share fails when the pubshares are
     * given. */
    {
        unsigned char wrong_share[32];
        secp256k1_frost_secnonce secnonce_tmp;
        secp256k1_frost_pubnonce pubnonce_tmp;

        testutil_random_scalar_order_b32(wrong_share);
        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce_tmp, &pubnonce_tmp, rand, &secshares[32 * ids[0]], &signer_pubshares[0], pk32, msg, msglen, NULL, 0) == 1);
        /* wrong_share equals the actual share with negligible probability */
        CHECK(secp256k1_frost_sign(CTX, &psig[0], &secnonce_tmp, wrong_share, &session, ids, signer_pubshares, u, ids[0]) == 0);
    }

    /* A signer whose id is not in the signer set cannot sign. */
    if (u < n) {
        secp256k1_frost_secnonce secnonce_tmp;
        secp256k1_frost_pubnonce pubnonce_tmp;

        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce_tmp, &pubnonce_tmp, rand, NULL, NULL, pk32, msg, msglen, NULL, 0) == 1);
        CHECK(secp256k1_frost_sign(CTX, &psig[0], &secnonce_tmp, &secshares[32 * ids_all[u]], &session, ids, NULL, u, ids_all[u]) == 0);
    }

    /* Signing wipes the secnonce; reusing it fails. */
    {
        secp256k1_frost_secnonce secnonce_tmp;
        secp256k1_frost_pubnonce pubnonce_tmp;

        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce_tmp, &pubnonce_tmp, rand, &secshares[32 * ids[0]], &signer_pubshares[0], pk32, msg, msglen, NULL, 0) == 1);
        CHECK(secp256k1_frost_sign(CTX, &psig[0], &secnonce_tmp, &secshares[32 * ids[0]], &session, ids, signer_pubshares, u, ids[0]) == 1);
        CHECK(secp256k1_is_zero_array(secnonce_tmp.data, sizeof(secnonce_tmp.data)));
        CHECK_ILLEGAL(CTX, secp256k1_frost_sign(CTX, &psig[0], &secnonce_tmp, &secshares[32 * ids[0]], &session, ids, signer_pubshares, u, ids[0]));
    }

    /* session_init rejects invalid session parameters. */
    {
        secp256k1_frost_session session_tmp;
        uint32_t ids_bad[FROST_SIGN_TEST_MAX_N];

        /* Duplicate signer ids. */
        if (u > 1) {
            memcpy(ids_bad, ids, u * sizeof(uint32_t));
            ids_bad[1] = ids_bad[0];
            CHECK(secp256k1_frost_session_init(CTX, &session_tmp, &aggnonce, ids_bad, NULL, u, n, t, &cache, msg, msglen) == 0);
        }
        /* A signer id out of range. */
        memcpy(ids_bad, ids, u * sizeof(uint32_t));
        ids_bad[0] = (uint32_t)n;
        CHECK(secp256k1_frost_session_init(CTX, &session_tmp, &aggnonce, ids_bad, NULL, u, n, t, &cache, msg, msglen) == 0);
        /* Fewer than t signers. */
        if (t > 1) {
            CHECK(secp256k1_frost_session_init(CTX, &session_tmp, &aggnonce, ids, NULL, t - 1, n, t, &cache, msg, msglen) == 0);
        }
        /* n > SECP256K1_FROST_MAX_PARTICIPANTS. */
        {
            uint32_t one_id = 0;
            CHECK(secp256k1_frost_session_init(CTX, &session_tmp, &aggnonce, &one_id, NULL, 1, SECP256K1_FROST_MAX_PARTICIPANTS + 1, 1, &cache, msg, msglen) == 0);
        }
        /* Pubshares that do not match the threshold public key. */
        {
            secp256k1_pubkey bad_pubshares[FROST_SIGN_TEST_MAX_N];
            secp256k1_pubkey random_pk;
            unsigned char random_sk[32];

            for (i = 0; i < u; i++) {
                bad_pubshares[i] = signer_pubshares[i];
            }
            testutil_random_scalar_order_b32(random_sk);
            CHECK(secp256k1_ec_pubkey_create(CTX, &random_pk, random_sk) == 1);
            bad_pubshares[0] = random_pk;
            CHECK(secp256k1_frost_session_init(CTX, &session_tmp, &aggnonce, ids, bad_pubshares, u, n, t, &cache, msg, msglen) == 0);
        }
    }

    /* Deterministic signing produces a pubnonce and partial signature that
     * are consistent with a session built from them. */
    {
        secp256k1_frost_partial_sig det_psig;
        const secp256k1_frost_partial_sig *det_psig_ptrs[1];
        secp256k1_frost_pubnonce det_pubnonce;
        const secp256k1_frost_pubnonce *det_pubnonce_ptrs[FROST_SIGN_TEST_MAX_N];
        secp256k1_frost_aggnonce det_aggnonce;
        secp256k1_frost_session det_session;
        unsigned char aux_rand[32];

        det_psig_ptrs[0] = &det_psig;

        det_pubnonce_ptrs[0] = &det_pubnonce;
        for (i = 1; i < u; i++) {
            det_pubnonce_ptrs[i] = &pubnonce[i];
        }
        testrand256(aux_rand);
        if (u == 1) {
            CHECK(secp256k1_frost_deterministic_sign(CTX, &det_psig, &det_pubnonce, &secshares[32 * ids[0]], ids[0], NULL, ids, signer_pubshares, u, n, t, &cache, msg, msglen, aux_rand) == 1);
        } else {
            secp256k1_frost_aggnonce aggothernonce;
            CHECK(secp256k1_frost_nonce_agg(CTX, &aggothernonce, NULL, &pubnonce_ptrs[1], u - 1) == 1);
            CHECK(secp256k1_frost_deterministic_sign(CTX, &det_psig, &det_pubnonce, &secshares[32 * ids[0]], ids[0], &aggothernonce, ids, signer_pubshares, u, n, t, &cache, msg, msglen, aux_rand) == 1);
        }
        CHECK(secp256k1_frost_nonce_agg(CTX, &det_aggnonce, NULL, det_pubnonce_ptrs, u) == 1);
        CHECK(secp256k1_frost_session_init(CTX, &det_session, &det_aggnonce, ids, signer_pubshares, u, n, t, &cache, msg, msglen) == 1);
        CHECK(secp256k1_frost_partial_sig_verify(CTX, &det_psig, &det_pubnonce, &signer_pubshares[0], &det_session, ids, u, 0) == 1);
        if (u == 1) {
            /* A sole deterministic signer produces a full valid signature. */
            CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &det_session, det_psig_ptrs, 1) == 1);
            CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg, msglen, &tweaked_pk) == 1);
        }
    }

    /* partial_sig_verify rejects an out-of-range signer index, and
     * partial_sig_agg rejects a wrong number of partial signatures and a
     * NULL element (reporting its index). */
    CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[0], &pubnonce[0], &signer_pubshares[0], &session, ids, u, u) == 0);
    CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, psig_ptrs, u + 1) == 0);
    if (u > 1) {
        CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, psig_ptrs, u - 1) == 0);
    }
    {
        const secp256k1_frost_partial_sig *null_ptrs[FROST_SIGN_TEST_MAX_N];
        size_t null_index = testrand_int((uint32_t)u);
        size_t error_index = 0xFF;

        for (i = 0; i < u; i++) {
            null_ptrs[i] = psig_ptrs[i];
        }
        null_ptrs[null_index] = NULL;
        CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, &error_index, &session, null_ptrs, u) == 0);
        CHECK(error_index == null_index);
        CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, null_ptrs, u) == 0);
    }

    /* NULL and invalid argument coverage. */
    {
        secp256k1_frost_secnonce secnonce_tmp;
        secp256k1_frost_pubnonce pubnonce_tmp;
        secp256k1_frost_partial_sig psig_tmp;
        secp256k1_frost_session session_tmp, invalid_session;
        secp256k1_frost_aggnonce invalid_aggnonce;
        secp256k1_frost_tweak_cache invalid_cache;

        memset(&invalid_session, 0, sizeof(invalid_session));
        memset(&invalid_aggnonce, 0, sizeof(invalid_aggnonce));
        memset(&invalid_cache, 0, sizeof(invalid_cache));

        /* session_init */
        CHECK_ILLEGAL(CTX, secp256k1_frost_session_init(CTX, NULL, &aggnonce, ids, NULL, u, n, t, &cache, msg, msglen));
        CHECK_ILLEGAL(CTX, secp256k1_frost_session_init(CTX, &session_tmp, NULL, ids, NULL, u, n, t, &cache, msg, msglen));
        CHECK_ILLEGAL(CTX, secp256k1_frost_session_init(CTX, &session_tmp, &invalid_aggnonce, ids, NULL, u, n, t, &cache, msg, msglen));
        CHECK_ILLEGAL(CTX, secp256k1_frost_session_init(CTX, &session_tmp, &aggnonce, NULL, NULL, u, n, t, &cache, msg, msglen));
        CHECK_ILLEGAL(CTX, secp256k1_frost_session_init(CTX, &session_tmp, &aggnonce, ids, NULL, u, n, t, NULL, msg, msglen));
        CHECK_ILLEGAL(CTX, secp256k1_frost_session_init(CTX, &session_tmp, &aggnonce, ids, NULL, u, n, t, &invalid_cache, msg, msglen));
        /* A NULL message is only legal with msglen == 0. */
        CHECK_ILLEGAL(CTX, secp256k1_frost_session_init(CTX, &session_tmp, &aggnonce, ids, NULL, u, n, t, &cache, NULL, 1));
        CHECK(secp256k1_frost_session_init(CTX, &session_tmp, &aggnonce, ids, NULL, u, n, t, &cache, NULL, 0) == 1);

        /* sign: every call that reaches the secnonce load wipes the secnonce,
         * so a fresh one is generated before each call. */
        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce_tmp, &pubnonce_tmp, rand, NULL, NULL, NULL, NULL, 0, NULL, 0) == 1);
        CHECK_ILLEGAL(CTX, secp256k1_frost_sign(CTX, NULL, &secnonce_tmp, &secshares[32 * ids[0]], &session, ids, NULL, u, ids[0]));
        CHECK_ILLEGAL(CTX, secp256k1_frost_sign(CTX, &psig_tmp, NULL, &secshares[32 * ids[0]], &session, ids, NULL, u, ids[0]));
        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce_tmp, &pubnonce_tmp, rand, NULL, NULL, NULL, NULL, 0, NULL, 0) == 1);
        CHECK_ILLEGAL(CTX, secp256k1_frost_sign(CTX, &psig_tmp, &secnonce_tmp, NULL, &session, ids, NULL, u, ids[0]));
        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce_tmp, &pubnonce_tmp, rand, NULL, NULL, NULL, NULL, 0, NULL, 0) == 1);
        CHECK_ILLEGAL(CTX, secp256k1_frost_sign(CTX, &psig_tmp, &secnonce_tmp, &secshares[32 * ids[0]], NULL, ids, NULL, u, ids[0]));
        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce_tmp, &pubnonce_tmp, rand, NULL, NULL, NULL, NULL, 0, NULL, 0) == 1);
        CHECK_ILLEGAL(CTX, secp256k1_frost_sign(CTX, &psig_tmp, &secnonce_tmp, &secshares[32 * ids[0]], &invalid_session, ids, NULL, u, ids[0]));
        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce_tmp, &pubnonce_tmp, rand, NULL, NULL, NULL, NULL, 0, NULL, 0) == 1);
        CHECK_ILLEGAL(CTX, secp256k1_frost_sign(CTX, &psig_tmp, &secnonce_tmp, &secshares[32 * ids[0]], &session, NULL, NULL, u, ids[0]));
        /* A zeroed (already used) secnonce is detected. */
        memset(&secnonce_tmp, 0, sizeof(secnonce_tmp));
        CHECK_ILLEGAL(CTX, secp256k1_frost_sign(CTX, &psig_tmp, &secnonce_tmp, &secshares[32 * ids[0]], &session, ids, NULL, u, ids[0]));

        /* partial_sig_verify */
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, NULL, &pubnonce[0], &signer_pubshares[0], &session, ids, u, 0));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &psig[0], NULL, &signer_pubshares[0], &session, ids, u, 0));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &psig[0], &pubnonce[0], NULL, &session, ids, u, 0));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &psig[0], &pubnonce[0], &signer_pubshares[0], NULL, ids, u, 0));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &psig[0], &pubnonce[0], &signer_pubshares[0], &invalid_session, ids, u, 0));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &psig[0], &pubnonce[0], &signer_pubshares[0], &session, NULL, u, 0));

        /* partial_sig_agg */
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_agg(CTX, NULL, NULL, &session, psig_ptrs, u));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, NULL, psig_ptrs, u));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &invalid_session, psig_ptrs, u));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, NULL, u));

        /* deterministic_sign */
        CHECK_ILLEGAL(CTX, secp256k1_frost_deterministic_sign(CTX, NULL, &pubnonce_tmp, &secshares[32 * ids[0]], ids[0], NULL, ids, NULL, u, n, t, &cache, msg, msglen, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_deterministic_sign(CTX, &psig_tmp, NULL, &secshares[32 * ids[0]], ids[0], NULL, ids, NULL, u, n, t, &cache, msg, msglen, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_deterministic_sign(CTX, &psig_tmp, &pubnonce_tmp, NULL, ids[0], NULL, ids, NULL, u, n, t, &cache, msg, msglen, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_deterministic_sign(CTX, &psig_tmp, &pubnonce_tmp, &secshares[32 * ids[0]], ids[0], NULL, NULL, NULL, u, n, t, &cache, msg, msglen, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_deterministic_sign(CTX, &psig_tmp, &pubnonce_tmp, &secshares[32 * ids[0]], ids[0], NULL, ids, NULL, u, n, t, NULL, msg, msglen, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_deterministic_sign(CTX, &psig_tmp, &pubnonce_tmp, &secshares[32 * ids[0]], ids[0], NULL, ids, NULL, u, n, t, &invalid_cache, msg, msglen, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_deterministic_sign(CTX, &psig_tmp, &pubnonce_tmp, &secshares[32 * ids[0]], ids[0], NULL, ids, NULL, u, n, t, &cache, NULL, 1, NULL));
    }
}
REPEAT_TEST(frost_sign_test)

/* Runs a full signing round (nonce_gen, nonce_agg, session_init, sign,
 * partial_sig_verify, partial_sig_agg) with the signers 0..n_signers-1 and
 * BIP340-verifies the final signature against the tweaked threshold public
 * key of the given tweak cache. */
static void frost_run_signing_round(const unsigned char *secshares, const secp256k1_pubkey *pubshares, const secp256k1_frost_tweak_cache *cache, size_t n_participants, uint32_t threshold, size_t n_signers, const unsigned char *msg, size_t msglen) {
    secp256k1_frost_secnonce secnonce[SECP256K1_FROST_MAX_PARTICIPANTS];
    secp256k1_frost_pubnonce pubnonce[SECP256K1_FROST_MAX_PARTICIPANTS];
    const secp256k1_frost_pubnonce *pubnonce_ptrs[SECP256K1_FROST_MAX_PARTICIPANTS];
    secp256k1_frost_partial_sig psig[SECP256K1_FROST_MAX_PARTICIPANTS];
    const secp256k1_frost_partial_sig *psig_ptrs[SECP256K1_FROST_MAX_PARTICIPANTS];
    secp256k1_pubkey signer_pubshares[SECP256K1_FROST_MAX_PARTICIPANTS];
    uint32_t ids[SECP256K1_FROST_MAX_PARTICIPANTS];
    secp256k1_frost_aggnonce aggnonce;
    secp256k1_frost_session session;
    secp256k1_xonly_pubkey tweaked_pk;
    unsigned char rand[32];
    unsigned char pk32[32];
    unsigned char sig64[64];
    size_t i;

    CHECK(n_signers >= (size_t)threshold && (size_t)threshold <= n_participants);
    CHECK(n_signers <= n_participants && n_participants <= SECP256K1_FROST_MAX_PARTICIPANTS);
    CHECK(secp256k1_frost_tweaked_pubkey_get(CTX, &tweaked_pk, cache) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, pk32, &tweaked_pk) == 1);
    for (i = 0; i < n_signers; i++) {
        ids[i] = (uint32_t)i;
        signer_pubshares[i] = pubshares[i];
        pubnonce_ptrs[i] = &pubnonce[i];
        psig_ptrs[i] = &psig[i];
        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], rand, &secshares[32 * i], &signer_pubshares[i], pk32, msg, msglen, NULL, 0) == 1);
    }
    CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, n_signers) == 1);
    CHECK(secp256k1_frost_session_init(CTX, &session, &aggnonce, ids, signer_pubshares, n_signers, n_participants, threshold, cache, msg, msglen) == 1);
    for (i = 0; i < n_signers; i++) {
        CHECK(secp256k1_frost_sign(CTX, &psig[i], &secnonce[i], &secshares[32 * i], &session, ids, signer_pubshares, n_signers, ids[i]) == 1);
        CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[i], &pubnonce[i], &signer_pubshares[i], &session, ids, n_signers, i) == 1);
    }
    CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, psig_ptrs, n_signers) == 1);
    CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg, msglen, &tweaked_pk) == 1);
}

/* NULL/invalid argument and parse/serialize coverage for the module API
 * functions not covered by the other tests: trusted_dealer_keygen,
 * threshold_info_validate, the pubnonce, aggnonce and partial_sig
 * serialize/parse functions, and nonce_gen with an invalid pubshare. */
static void frost_api_test(void) {
    /* The curve order n; a threshold secret key or partial signature >= n is
     * out of range. */
    static const unsigned char order32[32] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
        0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41
    };
    unsigned char thresh_sk[32];
    unsigned char zero32[32] = { 0 };
    unsigned char secshares[3 * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[3];
    secp256k1_pubkey invalid_pk;
    secp256k1_frost_secnonce secnonce;
    secp256k1_frost_pubnonce pubnonce, parsed_pubnonce, invalid_pubnonce;
    secp256k1_frost_aggnonce aggnonce, parsed_aggnonce, invalid_aggnonce;
    secp256k1_frost_partial_sig psig, parsed_psig, invalid_psig;
    unsigned char buf66[66];
    unsigned char buf32[32];
    unsigned char rand[32];

    memset(&invalid_pk, 0, sizeof(invalid_pk));
    memset(&invalid_pubnonce, 0, sizeof(invalid_pubnonce));
    memset(&invalid_aggnonce, 0, sizeof(invalid_aggnonce));
    memset(&invalid_psig, 0, sizeof(invalid_psig));

    testutil_random_scalar_order_b32(thresh_sk);
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 3, 2, thresh_sk) == 1);

    /* trusted_dealer_keygen: NULL arguments, and threshold secret keys that
     * are zero or out of range (the outputs are zeroed on failure). */
    CHECK_ILLEGAL(CTX, secp256k1_frost_trusted_dealer_keygen(CTX, NULL, &thresh_pk, pubshares, 3, 2, thresh_sk));
    CHECK_ILLEGAL(CTX, secp256k1_frost_trusted_dealer_keygen(CTX, secshares, NULL, pubshares, 3, 2, thresh_sk));
    CHECK_ILLEGAL(CTX, secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, NULL, 3, 2, thresh_sk));
    CHECK_ILLEGAL(CTX, secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 3, 2, NULL));
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 3, 2, zero32) == 0);
    CHECK(secp256k1_is_zero_array(secshares, sizeof(secshares)));
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 3, 2, order32) == 0);
    /* Re-run keygen for the tests below (the failed calls zeroed the outputs). */
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 3, 2, thresh_sk) == 1);

    /* threshold_info_validate: NULL arguments and corrupted pubkeys (a zeroed
     * pubkey fails in pubkey_load, triggering the illegal-argument
     * callback). */
    CHECK_ILLEGAL(CTX, secp256k1_frost_threshold_info_validate(CTX, NULL, pubshares, 3, 2));
    CHECK_ILLEGAL(CTX, secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, NULL, 3, 2));
    CHECK_ILLEGAL(CTX, secp256k1_frost_threshold_info_validate(CTX, &invalid_pk, pubshares, 3, 2));
    {
        secp256k1_pubkey bad_pubshares[3];
        memcpy(bad_pubshares, pubshares, sizeof(bad_pubshares));
        bad_pubshares[1] = invalid_pk;
        CHECK_ILLEGAL(CTX, secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, bad_pubshares, 3, 2));
    }

    /* nonce_gen rejects an invalid pubshare (and wipes the session
     * randomness). */
    testrand256(rand);
    CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce, &pubnonce, rand, NULL, &invalid_pk, NULL, NULL, 0, NULL, 0));
    CHECK(secp256k1_is_zero_array(rand, 32));

    /* pubnonce parse/serialize. */
    testrand256(rand);
    CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce, &pubnonce, rand, NULL, NULL, NULL, NULL, 0, NULL, 0) == 1);
    CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_parse(CTX, NULL, buf66));
    CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_parse(CTX, &parsed_pubnonce, NULL));
    CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_serialize(CTX, NULL, &pubnonce));
    CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_serialize(CTX, buf66, NULL));
    CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_serialize(CTX, buf66, &invalid_pubnonce));
    /* A component with an invalid encoding is rejected. */
    memset(buf66, 0xff, 66);
    CHECK(secp256k1_frost_pubnonce_parse(CTX, &parsed_pubnonce, buf66) == 0);

    /* aggnonce parse/serialize, including a round-trip. */
    {
        const secp256k1_frost_pubnonce *pubnonce_ptrs[1];
        secp256k1_ge ges[2], ges_parsed[2];
        int j;

        pubnonce_ptrs[0] = &pubnonce;
        CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 1) == 1);
        CHECK_ILLEGAL(CTX, secp256k1_frost_aggnonce_parse(CTX, NULL, buf66));
        CHECK_ILLEGAL(CTX, secp256k1_frost_aggnonce_parse(CTX, &parsed_aggnonce, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_aggnonce_serialize(CTX, NULL, &aggnonce));
        CHECK_ILLEGAL(CTX, secp256k1_frost_aggnonce_serialize(CTX, buf66, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_aggnonce_serialize(CTX, buf66, &invalid_aggnonce));
        memset(buf66, 0xff, 66);
        CHECK(secp256k1_frost_aggnonce_parse(CTX, &parsed_aggnonce, buf66) == 0);
        /* A serialize/parse round-trip preserves the aggregate nonce. */
        CHECK(secp256k1_frost_aggnonce_serialize(CTX, buf66, &aggnonce) == 1);
        CHECK(secp256k1_frost_aggnonce_parse(CTX, &parsed_aggnonce, buf66) == 1);
        CHECK(secp256k1_frost_aggnonce_load(CTX, ges, &aggnonce) == 1);
        CHECK(secp256k1_frost_aggnonce_load(CTX, ges_parsed, &parsed_aggnonce) == 1);
        for (j = 0; j < 2; j++) {
            CHECK(secp256k1_ge_eq_var(&ges[j], &ges_parsed[j]));
        }
    }

    /* partial_sig parse/serialize, including a round-trip. */
    {
        secp256k1_scalar s, s_parsed;

        testutil_random_scalar_order(&s);
        secp256k1_frost_partial_sig_save(&psig, &s);
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_parse(CTX, NULL, buf32));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_parse(CTX, &parsed_psig, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_serialize(CTX, NULL, &psig));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_serialize(CTX, buf32, NULL));
        CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_serialize(CTX, buf32, &invalid_psig));
        /* Malformed/overflowing partial signature bytes fail to parse. */
        CHECK(secp256k1_frost_partial_sig_parse(CTX, &parsed_psig, order32) == 0);
        memset(buf32, 0xff, 32);
        CHECK(secp256k1_frost_partial_sig_parse(CTX, &parsed_psig, buf32) == 0);
        /* A serialize/parse round-trip preserves the scalar. */
        CHECK(secp256k1_frost_partial_sig_serialize(CTX, buf32, &psig) == 1);
        CHECK(secp256k1_frost_partial_sig_parse(CTX, &parsed_psig, buf32) == 1);
        CHECK(secp256k1_frost_partial_sig_load(CTX, &s_parsed, &parsed_psig) == 1);
        CHECK(secp256k1_scalar_eq(&s, &s_parsed));
        secp256k1_scalar_clear(&s);
        secp256k1_scalar_clear(&s_parsed);
    }
}

/* The BIP 445 derive_interpolating_value algorithm accepts every identifier in
 * 0 <= id < 2^32, so the +1 that maps an identifier to its polynomial
 * x-coordinate must not be computed in uint32_t arithmetic. Reconstructs the
 * constant term of a random degree-(t-1) polynomial from t shares whose
 * identifiers include UINT32_MAX. */
static void frost_large_id_test(void) {
    static const uint32_t ids[3] = { 0, UINT32_MAX - 1, UINT32_MAX };
    secp256k1_scalar coeffs[3];
    secp256k1_scalar acc, one;
    size_t i, j;

    for (i = 0; i < 3; i++) {
        testutil_random_scalar_order(&coeffs[i]);
    }
    secp256k1_scalar_set_int(&one, 1);
    secp256k1_scalar_set_int(&acc, 0);
    for (i = 0; i < 3; i++) {
        secp256k1_scalar x, share, lambda;

        /* x = id + 1, computed in scalar arithmetic so that it does not
         * overflow for id == UINT32_MAX. */
        secp256k1_scalar_set_int(&x, ids[i]);
        secp256k1_scalar_add(&x, &x, &one);
        /* share = p(x) with p(0) = coeffs[2], evaluated with Horner's method. */
        secp256k1_scalar_set_int(&share, 0);
        for (j = 0; j < 3; j++) {
            secp256k1_scalar_mul(&share, &share, &x);
            secp256k1_scalar_add(&share, &share, &coeffs[j]);
        }
        CHECK(secp256k1_frost_derive_interpolating_value(&lambda, ids, 3, ids[i]) == 1);
        secp256k1_scalar_mul(&share, &share, &lambda);
        secp256k1_scalar_add(&acc, &acc, &share);
    }
    /* sum_i lambda_i * p(x_i) == p(0) == coeffs[2] */
    CHECK(secp256k1_scalar_eq(&acc, &coeffs[2]));

    /* Duplicate identifiers are still rejected, and an identifier that is not
     * in the set has no interpolating value. */
    {
        static const uint32_t dup[2] = { UINT32_MAX, UINT32_MAX };
        secp256k1_scalar lambda;
        CHECK(secp256k1_frost_derive_interpolating_value(&lambda, dup, 2, UINT32_MAX) == 0);
        CHECK(secp256k1_frost_derive_interpolating_value(&lambda, ids, 3, 7) == 0);
    }
}

/* A zero-length message is signed like any other message, and may be passed as
 * a NULL pointer: the reference's msg is a byte string, so an empty message is
 * not distinguishable from a NULL pointer of length 0. (NonceGen is the sole
 * exception: there the BIP distinguishes msg = None from msg = b"", which the
 * API expresses as a NULL versus a non-NULL msg.) */
static void frost_empty_msg_test(void) {
    static const uint32_t ids[2] = { 0, 1 };
    unsigned char thresh_sk[32];
    unsigned char secshares[3 * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[3];
    secp256k1_frost_tweak_cache cache;
    secp256k1_frost_secnonce secnonce[2];
    secp256k1_frost_pubnonce pubnonce[2];
    const secp256k1_frost_pubnonce *pubnonce_ptrs[2];
    secp256k1_frost_aggnonce aggnonce;
    secp256k1_frost_session session_null, session_ptr;
    unsigned char msg[1];
    unsigned char rand[32];
    size_t i;

    testutil_random_scalar_order_b32(thresh_sk);
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 3, 2, thresh_sk) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);

    /* A full signing round over a zero-length message, passed both ways. */
    frost_run_signing_round(secshares, pubshares, &cache, 3, 2, 2, NULL, 0);
    frost_run_signing_round(secshares, pubshares, &cache, 3, 2, 2, msg, 0);

    /* A NULL message of length 0 and a non-NULL pointer of length 0 yield the
     * same session. */
    for (i = 0; i < 2; i++) {
        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], rand, &secshares[32 * ids[i]], &pubshares[ids[i]], NULL, NULL, 0, NULL, 0) == 1);
        pubnonce_ptrs[i] = &pubnonce[i];
    }
    CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 2) == 1);
    CHECK(secp256k1_frost_session_init(CTX, &session_null, &aggnonce, ids, pubshares, 2, 3, 2, &cache, NULL, 0) == 1);
    CHECK(secp256k1_frost_session_init(CTX, &session_ptr, &aggnonce, ids, pubshares, 2, 3, 2, &cache, msg, 0) == 1);
    CHECK(secp256k1_memcmp_var(&session_null, &session_ptr, sizeof(session_null)) == 0);
}

/* Boundary test: keygen and a full signing round with n =
 * SECP256K1_FROST_MAX_PARTICIPANTS (128), the rejection of n = 129, and the
 * threshold extremes t = 1 and t = n. */
static void frost_boundary_test(void) {
    unsigned char thresh_sk[32];
    unsigned char secshares[SECP256K1_FROST_MAX_PARTICIPANTS * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[SECP256K1_FROST_MAX_PARTICIPANTS];
    secp256k1_frost_tweak_cache cache;
    unsigned char msg[32];

    testrand256(msg);
    testutil_random_scalar_order_b32(thresh_sk);

    /* n = SECP256K1_FROST_MAX_PARTICIPANTS (128): keygen, validation and a
     * signing round with u = t = 64. */
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, SECP256K1_FROST_MAX_PARTICIPANTS, 64, thresh_sk) == 1);
    CHECK(secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, pubshares, SECP256K1_FROST_MAX_PARTICIPANTS, 64) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);
    frost_run_signing_round(secshares, pubshares, &cache, SECP256K1_FROST_MAX_PARTICIPANTS, 64, 64, msg, sizeof(msg));

    /* n = 129 exceeds the maximum and is rejected (both functions return
     * before touching the arrays). */
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, SECP256K1_FROST_MAX_PARTICIPANTS + 1, 1, thresh_sk) == 0);
    CHECK(secp256k1_frost_threshold_info_validate(CTX, &thresh_pk, pubshares, SECP256K1_FROST_MAX_PARTICIPANTS + 1, 1) == 0);

    /* t = 1, including a solo signer (u = 1) going through the full
     * nonce_gen/nonce_agg/session_init/sign/agg pipeline (not just
     * deterministic_sign). */
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 4, 1, thresh_sk) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);
    frost_run_signing_round(secshares, pubshares, &cache, 4, 1, 1, msg, sizeof(msg));
    frost_run_signing_round(secshares, pubshares, &cache, 4, 1, 2, msg, sizeof(msg));

    /* t = n. */
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 3, 3, thresh_sk) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);
    frost_run_signing_round(secshares, pubshares, &cache, 3, 3, 3, msg, sizeof(msg));
}

/* Edge cases of tweaking: a zero tweak (valid, leaves the x-only threshold
 * public key unchanged), multiple consecutive plain tweaks and a mixed
 * xonly/plain tweak chain, exercised in a full signing round with BIP340
 * verification of the final signature. */
static void frost_tweak_edge_test(void) {
    unsigned char thresh_sk[32];
    unsigned char secshares[3 * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[3];
    secp256k1_frost_tweak_cache cache;
    secp256k1_xonly_pubkey tweaked_pk;
    unsigned char zero_tweak[32] = { 0 };
    unsigned char tweak[32];
    unsigned char msg[32];
    unsigned char ser_before[32], ser_after[32];

    testrand256(msg);
    testutil_random_scalar_order_b32(thresh_sk);
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 3, 2, thresh_sk) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);

    /* tweak = 0 is valid. An xonly zero tweak normalizes Q to even Y and a
     * plain zero tweak leaves Q untouched; both leave the x-only encoding of
     * Q unchanged. */
    CHECK(secp256k1_frost_tweaked_pubkey_get(CTX, &tweaked_pk, &cache) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, ser_before, &tweaked_pk) == 1);
    CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, &tweaked_pk, &cache, zero_tweak) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, ser_after, &tweaked_pk) == 1);
    CHECK(secp256k1_memcmp_var(ser_before, ser_after, 32) == 0);
    CHECK(secp256k1_frost_pubkey_ec_tweak_add(CTX, &tweaked_pk, &cache, zero_tweak) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, ser_after, &tweaked_pk) == 1);
    CHECK(secp256k1_memcmp_var(ser_before, ser_after, 32) == 0);

    /* Multiple consecutive plain tweaks, followed by an xonly tweak (giving
     * an xonly/plain(0)/plain/plain/xonly chain in total). */
    testrand256(tweak);
    CHECK(secp256k1_frost_pubkey_ec_tweak_add(CTX, NULL, &cache, tweak) == 1);
    testrand256(tweak);
    CHECK(secp256k1_frost_pubkey_ec_tweak_add(CTX, NULL, &cache, tweak) == 1);
    testrand256(tweak);
    CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, &tweaked_pk, &cache, tweak) == 1);

    /* A full signing round under the tweaked threshold public key. */
    frost_run_signing_round(secshares, pubshares, &cache, 3, 2, 2, msg, sizeof(msg));
}

/* Sessions whose aggregate nonce contains the point at infinity (BIP 445
 * NonceAgg explicitly allows infinity column sums). */
static void frost_infinity_test(void) {
    /* The x-coordinate of the generator G, which substitutes the final nonce
     * R if R would be the point at infinity (BIP 445 GetSessionValues). */
    static const unsigned char generator_x[32] = {
        0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95,
        0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9,
        0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98
    };
    static const uint32_t ids[2] = { 0, 1 };
    unsigned char thresh_sk[32];
    unsigned char secshares[2 * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[2];
    secp256k1_frost_tweak_cache cache;
    secp256k1_xonly_pubkey xonly_pk;
    unsigned char msg[32];
    unsigned char pk32[32];
    unsigned char sig64[64];
    int case_i;

    testrand256(msg);
    testutil_random_scalar_order_b32(thresh_sk);
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 2, 2, thresh_sk) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);
    CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &thresh_pk) == 1);
    CHECK(secp256k1_xonly_pubkey_serialize(CTX, pk32, &xonly_pk) == 1);

    /* The signers' secret nonces are injected directly (via the internal save
     * functions) because frost_nonce_gen cannot produce nonce contributions
     * that cancel out.
     *
     * case_i == 0: the second column of the aggregate nonce sums to the point
     * at infinity. The final nonce R = R1 + b*R2 = R1 is a valid point and
     * the session behaves like an ordinary session.
     * case_i == 1: both columns sum to the point at infinity, so the final
     * nonce R would be the point at infinity and is substituted with the
     * generator G. session_init succeeds and produces a consistent session,
     * but the resulting signature cannot verify (the nonce contributions
     * cancel out instead of summing to G).
     *
     * A crafted case in which R is the point at infinity but the aggnonce is
     * not cannot be constructed directly: it requires solving the fixed point
     * R1 = -b(R1 || R2 || ...)*R2 for the hash-based nonce coefficient b. The
     * R = G substitution is therefore tested via the all-infinity aggnonce. */
    for (case_i = 0; case_i < 2; case_i++) {
        secp256k1_scalar k[2][2];
        secp256k1_ge ges[2];
        secp256k1_frost_secnonce secnonce[2];
        secp256k1_frost_pubnonce pubnonce[2];
        const secp256k1_frost_pubnonce *pubnonce_ptrs[2];
        secp256k1_frost_aggnonce aggnonce;
        secp256k1_frost_session session;
        secp256k1_frost_partial_sig psig[2];
        const secp256k1_frost_partial_sig *psig_ptrs[2];
        secp256k1_ge agg_ges[2];
        size_t i;
        int j;

        pubnonce_ptrs[0] = &pubnonce[0];
        pubnonce_ptrs[1] = &pubnonce[1];
        psig_ptrs[0] = &psig[0];
        psig_ptrs[1] = &psig[1];

        /* Signer 0 gets the random nonce pair (k0, k1); signer 1 gets
         * (k0', -k1) with k0' random in case 0 and -k0 in case 1. */
        testutil_random_scalar_order(&k[0][0]);
        testutil_random_scalar_order(&k[0][1]);
        if (case_i == 0) {
            testutil_random_scalar_order(&k[1][0]);
        } else {
            secp256k1_scalar_negate(&k[1][0], &k[0][0]);
        }
        secp256k1_scalar_negate(&k[1][1], &k[0][1]);
        for (i = 0; i < 2; i++) {
            for (j = 0; j < 2; j++) {
                secp256k1_ecmult_gen_ge(&CTX->ecmult_gen_ctx, &ges[j], &k[i][j]);
            }
            secp256k1_frost_secnonce_save(&secnonce[i], k[i]);
            secp256k1_frost_pubnonce_save(&pubnonce[i], ges);
        }

        CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 2) == 1);
        CHECK(secp256k1_frost_aggnonce_load(CTX, agg_ges, &aggnonce) == 1);
        CHECK(secp256k1_ge_is_infinity(&agg_ges[1]));
        CHECK(secp256k1_ge_is_infinity(&agg_ges[0]) == case_i);

        /* session_init succeeds with an infinity component in the aggnonce. */
        CHECK(secp256k1_frost_session_init(CTX, &session, &aggnonce, ids, pubshares, 2, 2, 2, &cache, msg, sizeof(msg)) == 1);
        if (case_i == 1) {
            /* The final nonce R is the point at infinity and has been
             * substituted with the generator G. */
            secp256k1_frost_session_internal session_i;
            CHECK(secp256k1_frost_session_load(CTX, &session_i, &session) == 1);
            CHECK(secp256k1_memcmp_var(session_i.fin_nonce, generator_x, 32) == 0);
            CHECK(session_i.fin_nonce_parity == 0);
        }

        for (i = 0; i < 2; i++) {
            CHECK(secp256k1_frost_sign(CTX, &psig[i], &secnonce[i], &secshares[32 * i], &session, ids, pubshares, 2, ids[i]) == 1);
            CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[i], &pubnonce[i], &pubshares[i], &session, ids, 2, i) == 1);
        }
        CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, psig_ptrs, 2) == 1);
        /* In case 0 the final nonce is R1 (a valid point) and the signature
         * verifies. In case 1 the nonce contributions cancel out (their sum
         * is the point at infinity, not the substituted G), so the signature
         * must not verify. */
        CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg, sizeof(msg), &xonly_pk) == 1 - case_i);

        for (i = 0; i < 2; i++) {
            secp256k1_scalar_clear(&k[i][0]);
            secp256k1_scalar_clear(&k[i][1]);
        }
    }
}

/* A partial signature is only valid for the session and the signer it was
 * created for. */
static void frost_mismatch_test(void) {
    static const uint32_t ids[2] = { 0, 1 };
    unsigned char thresh_sk[32];
    unsigned char secshares[3 * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[3];
    secp256k1_pubkey invalid_pk;
    secp256k1_frost_tweak_cache cache;
    secp256k1_frost_secnonce secnonce[2];
    secp256k1_frost_pubnonce pubnonce[2];
    const secp256k1_frost_pubnonce *pubnonce_ptrs[2];
    secp256k1_frost_aggnonce aggnonce;
    secp256k1_frost_session session_a, session_b;
    secp256k1_frost_partial_sig psig[2];
    const secp256k1_frost_partial_sig *psig_ptrs[2];
    secp256k1_xonly_pubkey xonly_pk;
    unsigned char msg_a[32], msg_b[32];
    unsigned char rand[32];
    unsigned char sig64[64];
    size_t i;

    memset(&invalid_pk, 0, sizeof(invalid_pk));
    testrand256(msg_a);
    testrand256(msg_b);
    testutil_random_scalar_order_b32(thresh_sk);
    CHECK(secp256k1_frost_trusted_dealer_keygen(CTX, secshares, &thresh_pk, pubshares, 3, 2, thresh_sk) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);
    CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &thresh_pk) == 1);

    for (i = 0; i < 2; i++) {
        pubnonce_ptrs[i] = &pubnonce[i];
        psig_ptrs[i] = &psig[i];
        testrand256(rand);
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], rand, &secshares[32 * i], &pubshares[i], NULL, msg_a, sizeof(msg_a), NULL, 0) == 1);
    }
    CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, 2) == 1);

    /* Two sessions over the same aggnonce but different messages. */
    CHECK(secp256k1_frost_session_init(CTX, &session_a, &aggnonce, ids, pubshares, 2, 3, 2, &cache, msg_a, sizeof(msg_a)) == 1);
    CHECK(secp256k1_frost_session_init(CTX, &session_b, &aggnonce, ids, pubshares, 2, 3, 2, &cache, msg_b, sizeof(msg_b)) == 1);

    /* Both signers sign session A. */
    for (i = 0; i < 2; i++) {
        CHECK(secp256k1_frost_sign(CTX, &psig[i], &secnonce[i], &secshares[32 * i], &session_a, ids, pubshares, 2, ids[i]) == 1);
        CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[i], &pubnonce[i], &pubshares[i], &session_a, ids, 2, i) == 1);
    }

    /* Verifying a partial signature with the wrong signer index fails (the
     * interpolating value of the other signer is used). */
    CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[0], &pubnonce[0], &pubshares[0], &session_a, ids, 2, 1) == 0);

    /* A partial signature created for session A does not verify in session B,
     * and aggregating the partial signatures under session B yields an
     * invalid signature. */
    CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[0], &pubnonce[0], &pubshares[0], &session_b, ids, 2, 0) == 0);
    CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig[1], &pubnonce[1], &pubshares[1], &session_b, ids, 2, 1) == 0);
    CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session_b, psig_ptrs, 2) == 1);
    CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg_b, sizeof(msg_b), &xonly_pk) == 0);

    /* Under the correct session the aggregate signature verifies. */
    CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session_a, psig_ptrs, 2) == 1);
    CHECK(secp256k1_schnorrsig_verify(CTX, sig64, msg_a, sizeof(msg_a), &xonly_pk) == 1);

    /* partial_sig_verify rejects a corrupted pubshare object. */
    CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &psig[0], &pubnonce[0], &invalid_pk, &session_a, ids, 2, 0));
}

/* BIP 445 test vectors (generated by tools/test_vectors_frost_generate.py
 * from the reference repository, see vectors.h). */

/* Injects a raw 64-byte secnonce (k1 || k2) from the test vectors into a
 * secnonce object using the internal save function (magic + k1 || k2). */
static void frost_vectors_inject_secnonce(secp256k1_frost_secnonce *secnonce, const unsigned char *secnonce64) {
    secp256k1_scalar k[2];

    secp256k1_scalar_set_b32(&k[0], &secnonce64[0], NULL);
    secp256k1_scalar_set_b32(&k[1], &secnonce64[32], NULL);
    secp256k1_frost_secnonce_save(secnonce, k);
    secp256k1_scalar_clear(&k[0]);
    secp256k1_scalar_clear(&k[1]);
}

/* Parses the pubshares selected by the given indices. Requires that all of
 * them parse. */
static void frost_vectors_parse_pubshares(secp256k1_pubkey *pubshares, const struct frost_vec_group *group, const size_t *indices, size_t n) {
    size_t i;

    for (i = 0; i < n; i++) {
        CHECK(secp256k1_ec_pubkey_parse(CTX, &pubshares[i], group->pubshares[indices[i]], 33) == 1);
    }
}

/* Initializes the tweak cache from the group's threshold public key and
 * applies the given tweak chain (indices into the group's tweak pool) in
 * order. Returns 1 if all tweaks were applied, 0 otherwise. */
static int frost_vectors_init_cache(secp256k1_frost_tweak_cache *cache, const struct frost_vec_group *group, size_t n_tweaks, const size_t *tweak_indices, const int *is_xonly) {
    secp256k1_pubkey thresh_pk;
    size_t i;

    CHECK(secp256k1_ec_pubkey_parse(CTX, &thresh_pk, group->thresh_pk, 33) == 1);
    CHECK(secp256k1_frost_tweak_cache_init(CTX, cache, &thresh_pk) == 1);
    for (i = 0; i < n_tweaks; i++) {
        int ok = is_xonly[i]
            ? secp256k1_frost_pubkey_xonly_tweak_add(CTX, NULL, cache, group->tweaks[tweak_indices[i]])
            : secp256k1_frost_pubkey_ec_tweak_add(CTX, NULL, cache, group->tweaks[tweak_indices[i]]);
        if (!ok) {
            return 0;
        }
    }
    return 1;
}

/* Runs one sign test case (from sign_verify_vectors.json or
 * tweak_vectors.json). */
static void frost_vectors_run_sign_case(const struct frost_vec_group *group, const struct frost_vec_sign_case *c) {
    secp256k1_frost_tweak_cache cache;
    secp256k1_frost_aggnonce aggnonce;
    secp256k1_frost_session session;
    secp256k1_frost_secnonce secnonce;
    secp256k1_frost_partial_sig psig;
    secp256k1_pubkey pubshares[FROST_VEC_MAX_SIGNERS];
    const secp256k1_pubkey *pubshares_ptr = NULL;
    unsigned char buf[66];

    /* The C API parses the pubshares individually (via
     * secp256k1_ec_pubkey_parse) before session setup, so the reference's
     * "Invalid pubshare at index i" ValueError maps to the parse of the blamed
     * pubshare failing. */
    if (c->error == FROST_VEC_ERR_PUBSHARE_PARSE) {
        secp256k1_pubkey pubkey;
        CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, group->pubshares[c->pubshare_indices[c->error_index]], 33) == 0);
        return;
    }

    /* Build the tweak cache and apply the tweak chain (if any). An invalid
     * tweak maps to the tweak_add call returning 0. */
    if (c->error == FROST_VEC_ERR_TWEAK_ADD) {
        CHECK(!frost_vectors_init_cache(&cache, group, c->n_tweaks, c->tweak_indices, c->is_xonly));
        return;
    }
    CHECK(frost_vectors_init_cache(&cache, group, c->n_tweaks, c->tweak_indices, c->is_xonly));

    /* An invalid aggnonce (the coordinator's contribution) maps to
     * frost_aggnonce_parse failing. */
    if (c->error == FROST_VEC_ERR_AGGNONCE_PARSE) {
        CHECK(secp256k1_frost_aggnonce_parse(CTX, &aggnonce, c->aggnonce) == 0);
        return;
    }
    CHECK(secp256k1_frost_aggnonce_parse(CTX, &aggnonce, c->aggnonce) == 1);

    if (c->has_pubshares) {
        frost_vectors_parse_pubshares(pubshares, group, c->pubshare_indices, c->n_ids);
        pubshares_ptr = pubshares;
    }

    /* Invalid session parameters (duplicate ids, out-of-range ids, too few
     * signers, pubshares inconsistent with the threshold public key) are
     * rejected by frost_session_init. */
    if (c->error == FROST_VEC_ERR_SESSION_INIT) {
        CHECK(secp256k1_frost_session_init(CTX, &session, &aggnonce, c->ids, pubshares_ptr, c->n_ids, group->n_participants, group->threshold, &cache, c->msg, c->msg_len) == 0);
        return;
    }
    CHECK(secp256k1_frost_session_init(CTX, &session, &aggnonce, c->ids, pubshares_ptr, c->n_ids, group->n_participants, group->threshold, &cache, c->msg, c->msg_len) == 1);

    frost_vectors_inject_secnonce(&secnonce, group->secnonces[c->secnonce_index]);
    if (c->error == FROST_VEC_ERR_SECNONCE) {
        /* A secnonce with a zero half (e.g. from nonce reuse) is rejected when
         * frost_sign loads it, which triggers the illegal-argument callback. */
        CHECK_ILLEGAL(CTX, secp256k1_frost_sign(CTX, &psig, &secnonce, group->secshares[c->secshare_index], &session, c->ids, pubshares_ptr, c->n_ids, c->my_id));
        return;
    }
    if (c->error == FROST_VEC_ERR_SIGN) {
        CHECK(secp256k1_frost_sign(CTX, &psig, &secnonce, group->secshares[c->secshare_index], &session, c->ids, pubshares_ptr, c->n_ids, c->my_id) == 0);
        return;
    }
    CHECK(c->error == FROST_VEC_ERR_NONE);
    CHECK(secp256k1_frost_sign(CTX, &psig, &secnonce, group->secshares[c->secshare_index], &session, c->ids, pubshares_ptr, c->n_ids, c->my_id) == 1);
    CHECK(secp256k1_frost_partial_sig_serialize(CTX, buf, &psig) == 1);
    CHECK(secp256k1_memcmp_var(buf, c->expected, 32) == 0);
}

static void frost_test_vectors_nonce_gen(void) {
    size_t i;

    for (i = 0; i < sizeof(frost_vec_nonce_gen_cases) / sizeof(frost_vec_nonce_gen_cases[0]); i++) {
        const struct frost_vec_nonce_gen_case *c = &frost_vec_nonce_gen_cases[i];
        secp256k1_frost_secnonce secnonce;
        secp256k1_frost_pubnonce pubnonce;
        secp256k1_pubkey pubshare;
        secp256k1_scalar k[2];
        unsigned char rand[32];
        unsigned char buf[66];

        /* nonce_gen wipes session_secrand32 before returning, so work on a
         * copy. */
        memcpy(rand, c->rand, 32);
        if (c->has_pubshare) {
            CHECK(secp256k1_ec_pubkey_parse(CTX, &pubshare, c->pubshare, 33) == 1);
        }
        CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce, &pubnonce, rand, c->has_secshare ? c->secshare : NULL, c->has_pubshare ? &pubshare : NULL, c->has_thresh_pk ? c->thresh_pk_xonly : NULL, c->has_msg ? c->msg : NULL, c->msg_len, c->has_extra_in ? c->extra_in : NULL, c->extra_in_len) == 1);
        CHECK(secp256k1_is_zero_array(rand, 32));

        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf, &pubnonce) == 1);
        CHECK(secp256k1_memcmp_var(buf, c->expected_pubnonce, 66) == 0);

        /* Compare the secnonce via the internal load function (tests are
         * compiled in the same translation unit). */
        CHECK(secp256k1_frost_secnonce_load(CTX, k, &secnonce) == 1);
        secp256k1_scalar_get_b32(&buf[0], &k[0]);
        secp256k1_scalar_get_b32(&buf[32], &k[1]);
        CHECK(secp256k1_memcmp_var(buf, c->expected_secnonce, 64) == 0);
        secp256k1_scalar_clear(&k[0]);
        secp256k1_scalar_clear(&k[1]);
    }
}

static void frost_test_vectors_nonce_agg(void) {
    size_t i;

    for (i = 0; i < sizeof(frost_vec_nonce_agg_valid_cases) / sizeof(frost_vec_nonce_agg_valid_cases[0]); i++) {
        const struct frost_vec_nonce_agg_case *c = &frost_vec_nonce_agg_valid_cases[i];
        secp256k1_frost_pubnonce pubnonces[FROST_VEC_MAX_SIGNERS];
        const secp256k1_frost_pubnonce *pubnonce_ptrs[FROST_VEC_MAX_SIGNERS];
        secp256k1_frost_aggnonce aggnonce;
        unsigned char buf[66];
        size_t j;

        for (j = 0; j < c->n_pubnonces; j++) {
            CHECK(secp256k1_frost_pubnonce_parse(CTX, &pubnonces[j], frost_vec_nonce_agg_pubnonces[c->pubnonce_indices[j]]) == 1);
            pubnonce_ptrs[j] = &pubnonces[j];
        }
        /* The aggregate nonce may contain the point at infinity (serialized
         * as 33 zero bytes); this is not an error. */
        CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, c->n_pubnonces) == 1);
        CHECK(secp256k1_frost_aggnonce_serialize(CTX, buf, &aggnonce) == 1);
        CHECK(secp256k1_memcmp_var(buf, c->expected, 66) == 0);
    }

    for (i = 0; i < sizeof(frost_vec_nonce_agg_error_cases) / sizeof(frost_vec_nonce_agg_error_cases[0]); i++) {
        const struct frost_vec_nonce_agg_case *c = &frost_vec_nonce_agg_error_cases[i];
        secp256k1_frost_pubnonce pubnonce;

        /* The C API parses the pubnonces individually (via
         * frost_pubnonce_parse) before they are aggregated, so the
         * reference's InvalidContributionError blaming the pubnonce at
         * position i maps to the parse of that pubnonce failing. */
        CHECK(secp256k1_frost_pubnonce_parse(CTX, &pubnonce, frost_vec_nonce_agg_pubnonces[c->pubnonce_indices[c->error_index]]) == 0);
    }
}

static void frost_test_vectors_sign(void) {
    size_t i;

    for (i = 0; i < sizeof(frost_vec_sign_valid_cases) / sizeof(frost_vec_sign_valid_cases[0]); i++) {
        frost_vectors_run_sign_case(&frost_vec_sign_groups[frost_vec_sign_valid_cases[i].group], &frost_vec_sign_valid_cases[i]);
    }
    for (i = 0; i < sizeof(frost_vec_sign_error_cases) / sizeof(frost_vec_sign_error_cases[0]); i++) {
        frost_vectors_run_sign_case(&frost_vec_sign_groups[frost_vec_sign_error_cases[i].group], &frost_vec_sign_error_cases[i]);
    }
}

static void frost_test_vectors_verify(void) {
    size_t i, j;

    for (i = 0; i < sizeof(frost_vec_verify_fail_cases) / sizeof(frost_vec_verify_fail_cases[0]) + sizeof(frost_vec_verify_error_cases) / sizeof(frost_vec_verify_error_cases[0]); i++) {
        const struct frost_vec_verify_case *c = i < sizeof(frost_vec_verify_fail_cases) / sizeof(frost_vec_verify_fail_cases[0])
            ? &frost_vec_verify_fail_cases[i]
            : &frost_vec_verify_error_cases[i - sizeof(frost_vec_verify_fail_cases) / sizeof(frost_vec_verify_fail_cases[0])];
        const struct frost_vec_group *group = &frost_vec_sign_groups[c->group];
        secp256k1_frost_partial_sig psig;

        /* verify_error cases: the blamed contribution fails to parse. The C
         * API parses the pubnonce and pubshare individually before
         * verification, so the blame index maps to the parse step. */
        if (c->error == FROST_VEC_ERR_PUBNONCE_PARSE) {
            secp256k1_frost_pubnonce pubnonce;
            CHECK(secp256k1_frost_pubnonce_parse(CTX, &pubnonce, group->pubnonces[c->pubnonce_indices[c->error_index]]) == 0);
            continue;
        }
        if (c->error == FROST_VEC_ERR_PUBSHARE_PARSE) {
            secp256k1_pubkey pubkey;
            CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, group->pubshares[c->pubshare_indices[c->error_index]], 33) == 0);
            continue;
        }
        CHECK(c->error == FROST_VEC_ERR_NONE);

        /* A psig that is not a valid scalar maps to frost_partial_sig_parse
         * failing (the reference's partial_sig_verify returns False). */
        if (!c->psig_parses) {
            CHECK(secp256k1_frost_partial_sig_parse(CTX, &psig, c->psig) == 0);
            continue;
        }

        /* verify_fail cases: the partial signature parses but does not
         * verify. */
        {
            secp256k1_frost_tweak_cache cache;
            secp256k1_frost_session session;
            secp256k1_frost_pubnonce pubnonces[FROST_VEC_MAX_SIGNERS];
            const secp256k1_frost_pubnonce *pubnonce_ptrs[FROST_VEC_MAX_SIGNERS];
            secp256k1_pubkey pubshares[FROST_VEC_MAX_SIGNERS];
            secp256k1_frost_aggnonce aggnonce;

            CHECK(secp256k1_frost_partial_sig_parse(CTX, &psig, c->psig) == 1);
            /* The reference aggregates the pubnonces inside
             * partial_sig_verify; the C API takes the session (built from the
             * aggnonce) instead. */
            for (j = 0; j < c->n_ids; j++) {
                CHECK(secp256k1_frost_pubnonce_parse(CTX, &pubnonces[j], group->pubnonces[c->pubnonce_indices[j]]) == 1);
                pubnonce_ptrs[j] = &pubnonces[j];
            }
            CHECK(secp256k1_frost_nonce_agg(CTX, &aggnonce, NULL, pubnonce_ptrs, c->n_ids) == 1);
            frost_vectors_parse_pubshares(pubshares, group, c->pubshare_indices, c->n_ids);
            CHECK(frost_vectors_init_cache(&cache, group, 0, NULL, NULL));
            CHECK(secp256k1_frost_session_init(CTX, &session, &aggnonce, c->ids, pubshares, c->n_ids, group->n_participants, group->threshold, &cache, c->msg, c->msg_len) == 1);
            CHECK(secp256k1_frost_partial_sig_verify(CTX, &psig, &pubnonces[c->signer_index], &pubshares[c->signer_index], &session, c->ids, c->n_ids, c->signer_index) == 0);
        }
    }
}

static void frost_test_vectors_tweak(void) {
    size_t i;

    for (i = 0; i < sizeof(frost_vec_tweak_valid_cases) / sizeof(frost_vec_tweak_valid_cases[0]); i++) {
        frost_vectors_run_sign_case(&frost_vec_tweak_groups[frost_vec_tweak_valid_cases[i].group], &frost_vec_tweak_valid_cases[i]);
    }
    for (i = 0; i < sizeof(frost_vec_tweak_error_cases) / sizeof(frost_vec_tweak_error_cases[0]); i++) {
        frost_vectors_run_sign_case(&frost_vec_tweak_groups[frost_vec_tweak_error_cases[i].group], &frost_vec_tweak_error_cases[i]);
    }
}

static void frost_test_vectors_sig_agg(void) {
    size_t i, j;

    for (i = 0; i < sizeof(frost_vec_sig_agg_valid_cases) / sizeof(frost_vec_sig_agg_valid_cases[0]) + sizeof(frost_vec_sig_agg_error_cases) / sizeof(frost_vec_sig_agg_error_cases[0]); i++) {
        const struct frost_vec_sig_agg_case *c = i < sizeof(frost_vec_sig_agg_valid_cases) / sizeof(frost_vec_sig_agg_valid_cases[0])
            ? &frost_vec_sig_agg_valid_cases[i]
            : &frost_vec_sig_agg_error_cases[i - sizeof(frost_vec_sig_agg_valid_cases) / sizeof(frost_vec_sig_agg_valid_cases[0])];
        const struct frost_vec_group *group = &frost_vec_sig_agg_groups[c->group];
        secp256k1_frost_tweak_cache cache;
        secp256k1_frost_session session;
        secp256k1_frost_aggnonce aggnonce;
        secp256k1_frost_partial_sig psigs[FROST_VEC_MAX_PSIGS];
        const secp256k1_frost_partial_sig *psig_ptrs[FROST_VEC_MAX_PSIGS];
        secp256k1_pubkey pubshares[FROST_VEC_MAX_SIGNERS];
        const secp256k1_pubkey *pubshares_ptr = NULL;
        unsigned char sig64[64];
        size_t error_index;

        /* An invalid partial signature (out of range) maps to
         * frost_partial_sig_parse of the blamed psig failing: the C API
         * parses the partial signatures individually before aggregation. */
        if (c->error == FROST_VEC_ERR_PSIG_PARSE) {
            CHECK(secp256k1_frost_partial_sig_parse(CTX, &psigs[0], c->psigs[c->error_index]) == 0);
            continue;
        }

        CHECK(frost_vectors_init_cache(&cache, group, c->n_tweaks, c->tweak_indices, c->is_xonly));
        CHECK(secp256k1_frost_aggnonce_parse(CTX, &aggnonce, c->aggnonce) == 1);
        if (c->has_pubshares) {
            frost_vectors_parse_pubshares(pubshares, group, c->pubshare_indices, c->n_ids);
            pubshares_ptr = pubshares;
        }
        CHECK(secp256k1_frost_session_init(CTX, &session, &aggnonce, c->ids, pubshares_ptr, c->n_ids, group->n_participants, group->threshold, &cache, c->msg, c->msg_len) == 1);
        for (j = 0; j < c->n_psigs; j++) {
            CHECK(secp256k1_frost_partial_sig_parse(CTX, &psigs[j], c->psigs[j]) == 1);
            psig_ptrs[j] = &psigs[j];
        }
        if (c->error == FROST_VEC_ERR_AGG_LEN) {
            /* The number of partial signatures does not match the number of
             * signers of the session. */
            CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, NULL, &session, psig_ptrs, c->n_psigs) == 0);
            continue;
        }
        CHECK(c->error == FROST_VEC_ERR_NONE);
        error_index = 0xFF;
        CHECK(secp256k1_frost_partial_sig_agg(CTX, sig64, &error_index, &session, psig_ptrs, c->n_psigs) == 1);
        CHECK(secp256k1_memcmp_var(sig64, c->expected, 64) == 0);
    }
}

static void frost_test_vectors_det_sign(void) {
    size_t i, j;

    for (i = 0; i < sizeof(frost_vec_det_sign_valid_cases) / sizeof(frost_vec_det_sign_valid_cases[0]) + sizeof(frost_vec_det_sign_error_cases) / sizeof(frost_vec_det_sign_error_cases[0]); i++) {
        const struct frost_vec_det_sign_case *c = i < sizeof(frost_vec_det_sign_valid_cases) / sizeof(frost_vec_det_sign_valid_cases[0])
            ? &frost_vec_det_sign_valid_cases[i]
            : &frost_vec_det_sign_error_cases[i - sizeof(frost_vec_det_sign_valid_cases) / sizeof(frost_vec_det_sign_valid_cases[0])];
        const struct frost_vec_group *group = &frost_vec_det_sign_groups[c->group];
        secp256k1_frost_tweak_cache cache;
        secp256k1_frost_partial_sig psig;
        secp256k1_frost_pubnonce pubnonce;
        secp256k1_frost_aggnonce aggothernonce;
        const secp256k1_frost_aggnonce *aggothernonce_ptr = NULL;
        secp256k1_pubkey pubshares[FROST_VEC_MAX_SIGNERS];
        const secp256k1_pubkey *pubshares_ptr = NULL;
        secp256k1_pubkey thresh_pk;
        unsigned char buf[66];
        int tweaks_ok = 1;

        /* Same mapping as in frost_vectors_run_sign_case: an invalid pubshare
         * is detected when parsing it. */
        if (c->error == FROST_VEC_ERR_PUBSHARE_PARSE) {
            secp256k1_pubkey pubkey;
            CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, group->pubshares[c->pubshare_indices[c->error_index]], 33) == 0);
            continue;
        }

        /* The tweaks are inlined in det_sign cases (not indexed into the
         * group's pool). */
        CHECK(secp256k1_ec_pubkey_parse(CTX, &thresh_pk, group->thresh_pk, 33) == 1);
        CHECK(secp256k1_frost_tweak_cache_init(CTX, &cache, &thresh_pk) == 1);
        for (j = 0; j < c->n_tweaks; j++) {
            int ok = c->is_xonly[j]
                ? secp256k1_frost_pubkey_xonly_tweak_add(CTX, NULL, &cache, c->tweaks[j])
                : secp256k1_frost_pubkey_ec_tweak_add(CTX, NULL, &cache, c->tweaks[j]);
            if (!ok) {
                tweaks_ok = 0;
                break;
            }
        }
        if (c->error == FROST_VEC_ERR_TWEAK_ADD) {
            CHECK(!tweaks_ok);
            continue;
        }
        CHECK(tweaks_ok);

        /* An invalid aggothernonce (the coordinator's contribution) maps to
         * frost_aggnonce_parse failing, or (for an infinity component, which
         * parses as an aggnonce but is not a valid NonceAgg input) to
         * frost_deterministic_sign failing. */
        if (c->has_aggothernonce) {
            if (secp256k1_frost_aggnonce_parse(CTX, &aggothernonce, c->aggothernonce) == 0) {
                CHECK(c->error == FROST_VEC_ERR_AGGOTHERNONCE);
                continue;
            }
            aggothernonce_ptr = &aggothernonce;
        }
        if (c->has_pubshares) {
            frost_vectors_parse_pubshares(pubshares, group, c->pubshare_indices, c->n_ids);
            pubshares_ptr = pubshares;
        }
        if (c->error != FROST_VEC_ERR_NONE) {
            CHECK(secp256k1_frost_deterministic_sign(CTX, &psig, &pubnonce, group->secshares[c->secshare_index], c->my_id, aggothernonce_ptr, c->ids, pubshares_ptr, c->n_ids, group->n_participants, group->threshold, &cache, c->msg, c->msg_len, c->has_aux_rand ? c->aux_rand : NULL) == 0);
            continue;
        }
        CHECK(secp256k1_frost_deterministic_sign(CTX, &psig, &pubnonce, group->secshares[c->secshare_index], c->my_id, aggothernonce_ptr, c->ids, pubshares_ptr, c->n_ids, group->n_participants, group->threshold, &cache, c->msg, c->msg_len, c->has_aux_rand ? c->aux_rand : NULL) == 1);
        CHECK(secp256k1_frost_pubnonce_serialize(CTX, buf, &pubnonce) == 1);
        CHECK(secp256k1_memcmp_var(buf, c->expected_pubnonce, 66) == 0);
        CHECK(secp256k1_frost_partial_sig_serialize(CTX, buf, &psig) == 1);
        CHECK(secp256k1_memcmp_var(buf, c->expected_psig, 32) == 0);
    }
}

static const struct tf_test_entry tests_frost[] = {
    CASE1(frost_trusted_dealer_vector_test),
    CASE1(frost_trusted_dealer_test),
    CASE1(frost_tweak_vector_test),
    CASE1(frost_tweak_test),
    CASE1(frost_nonce_vector_test),
    CASE1(frost_nonce_test),
    CASE1(frost_sign_vector_test),
    CASE1(frost_det_sign_vector_test),
    CASE1(frost_sign_test),
    CASE1(frost_api_test),
    CASE1(frost_boundary_test),
    CASE1(frost_large_id_test),
    CASE1(frost_empty_msg_test),
    CASE1(frost_tweak_edge_test),
    CASE1(frost_infinity_test),
    CASE1(frost_mismatch_test),
    CASE1(frost_test_vectors_nonce_gen),
    CASE1(frost_test_vectors_nonce_agg),
    CASE1(frost_test_vectors_sign),
    CASE1(frost_test_vectors_verify),
    CASE1(frost_test_vectors_tweak),
    CASE1(frost_test_vectors_sig_agg),
    CASE1(frost_test_vectors_det_sign),
};

#endif
