/*************************************************************************
 * To the extent possible under law, the author(s) have dedicated all    *
 * copyright and related and neighboring rights to the software in this  *
 * file to the public domain worldwide. This software is distributed     *
 * without any warranty. For the CC0 Public Domain Dedication, see       *
 * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
 *************************************************************************/

/** This file demonstrates how to use the FROST module (BIP 445) to create a
 *  2-of-3 threshold signature. Additionally, see the documentation in
 *  include/secp256k1_frost.h and src/modules/frost/frost.md.
 *
 *  The example runs all roles (trusted dealer, signers, coordinator) in a
 *  single process. In a real deployment these roles are performed by
 *  different parties communicating over secure/authenticated channels.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <secp256k1.h>
#include <secp256k1_extrakeys.h>
#include <secp256k1_frost.h>
#include <secp256k1_schnorrsig.h>

#include "examples_util.h"

/* Total number of participants n */
#define N_PARTICIPANTS 3
/* Threshold t: the minimum number of signers required to produce a
 * signature */
#define THRESHOLD 2
/* The signers of this signing session (u = 2, participants 0 and 2) */
#define N_SIGNERS 2
static const uint32_t SIGNER_IDS[N_SIGNERS] = { 0, 2 };

struct signer_secrets {
    /* The signer's secret share, received from the dealer over a secure
     * channel */
    unsigned char secshare[32];
    /* Secret nonce. It is wiped by secp256k1_frost_sign; never reuse it. */
    secp256k1_frost_secnonce secnonce;
};

struct signer {
    uint32_t id;
    secp256k1_pubkey pubshare;
    secp256k1_frost_pubnonce pubnonce;
    secp256k1_frost_partial_sig partial_sig;
};

/* Run the trusted dealer key generation: from a random threshold secret key,
 * derive the secret share of every participant, the threshold public key and
 * the public shares.
 *
 * WARNING: The trusted dealer knows the threshold secret key and all secret
 * shares, and must erase them securely after distributing the shares. A
 * distributed key generation (DKG) protocol avoids a trusted dealer entirely
 * but is out of scope for BIP 445 and for this example. */
static int trusted_dealer_keygen(const secp256k1_context* ctx, unsigned char *threshold_seckey, unsigned char *secshares, secp256k1_pubkey *thresh_pk, secp256k1_pubkey *pubshares) {
    if (!fill_random(threshold_seckey, 32)) {
        printf("Failed to generate randomness\n");
        return 0;
    }
    if (!secp256k1_frost_trusted_dealer_keygen(ctx, secshares, thresh_pk, pubshares, N_PARTICIPANTS, THRESHOLD, threshold_seckey)) {
        return 0;
    }
    /* Everyone can check that the public shares and the threshold public key
     * are consistent. Note that this does NOT validate the security of the
     * key generation that produced them. */
    if (!secp256k1_frost_threshold_info_validate(ctx, thresh_pk, pubshares, N_PARTICIPANTS, THRESHOLD)) {
        return 0;
    }
    return 1;
}

/* Apply an x-only tweak to the threshold public key, e.g. a BIP 341
 * ("Taproot") tweak committing to a script tree. All participants (and the
 * coordinator) must apply exactly the same tweaks in the same order to their
 * own tweak cache. */
static int tweak(const secp256k1_context* ctx, secp256k1_frost_tweak_cache *cache, secp256k1_xonly_pubkey *tweaked_pk) {
    /* For Taproot tweaking the tweak is set to the TapTweak hash as defined
     * in BIP 341. */
    unsigned char xonly_tweak[32] = "this could be a Taproot tweak..";

    if (!secp256k1_frost_pubkey_xonly_tweak_add(ctx, tweaked_pk, cache, xonly_tweak)) {
        return 0;
    }
    /* For BIP 32-style plain tweaking, use
     * secp256k1_frost_pubkey_ec_tweak_add instead. */
    return 1;
}

/* Sign a message with the given signers and store the resulting BIP340
 * signature in sig64. */
static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signers, const secp256k1_frost_tweak_cache *cache, const unsigned char *msg, size_t msglen, unsigned char *sig64) {
    int i;
    const secp256k1_frost_pubnonce *pubnonces[N_SIGNERS];
    const secp256k1_frost_partial_sig *partial_sigs[N_SIGNERS];
    secp256k1_pubkey signer_pubshares[N_SIGNERS];
    uint32_t ids[N_SIGNERS];
    /* The same for all signers and the coordinator */
    secp256k1_frost_aggnonce aggnonce;
    secp256k1_frost_session session;
    /* The x-only encoding of the tweaked threshold public key, bound into the
     * nonce derivation */
    secp256k1_xonly_pubkey tweaked_pk;
    unsigned char tweaked_pk32[32];

    if (!secp256k1_frost_tweaked_pubkey_get(ctx, &tweaked_pk, cache)) {
        return 0;
    }
    if (!secp256k1_xonly_pubkey_serialize(ctx, tweaked_pk32, &tweaked_pk)) {
        return 0;
    }

    /* Every signer creates a nonce pair and sends the pubnonce to the
     * coordinator. */
    for (i = 0; i < N_SIGNERS; i++) {
        unsigned char session_secrand[32];

        ids[i] = signers[i].id;
        signer_pubshares[i] = signers[i].pubshare;
        pubnonces[i] = &signers[i].pubnonce;
        partial_sigs[i] = &signers[i].partial_sig;

        /* Create random session randomness. It is absolutely necessary that
         * this is unique for every call of secp256k1_frost_nonce_gen.
         * Otherwise it's trivial for an attacker to extract the secret share!
         * nonce_gen wipes session_secrand before returning. */
        if (!fill_random(session_secrand, sizeof(session_secrand))) {
            printf("Failed to generate randomness\n");
            return 0;
        }
        /* Bind the secret share (defense-in-depth against bad randomness),
         * the public share, the tweaked threshold public key and the message
         * into the nonce derivation. */
        if (!secp256k1_frost_nonce_gen(ctx, &signer_secrets[i].secnonce, &signers[i].pubnonce, session_secrand, signer_secrets[i].secshare, &signers[i].pubshare, tweaked_pk32, msg, msglen, NULL, 0)) {
            return 0;
        }
        secure_erase(session_secrand, sizeof(session_secrand));
    }

    /* Communication round 1: the coordinator aggregates the pubnonces and
     * sends the aggregate nonce to the signers. */
    if (!secp256k1_frost_nonce_agg(ctx, &aggnonce, NULL, pubnonces, N_SIGNERS)) {
        return 0;
    }

    /* Every signer and the coordinator run session_init locally with
     * identical arguments (aside from the session object), which yields
     * identical sessions. */
    if (!secp256k1_frost_session_init(ctx, &session, &aggnonce, ids, signer_pubshares, N_SIGNERS, N_PARTICIPANTS, THRESHOLD, cache, msg, msglen)) {
        return 0;
    }

    /* Every signer creates a partial signature and sends it to the
     * coordinator. frost_sign wipes the secnonce. That's because you must
     * _never_ reuse the secnonce (or use the same session_secrand to create a
     * secnonce). If you do, you effectively reuse the nonce and leak the
     * secret share. */
    for (i = 0; i < N_SIGNERS; i++) {
        if (!secp256k1_frost_sign(ctx, &signers[i].partial_sig, &signer_secrets[i].secnonce, signer_secrets[i].secshare, &session, ids, signer_pubshares, N_SIGNERS, signers[i].id)) {
            return 0;
        }
    }

    /* Communication round 2: the coordinator verifies the partial signatures
     * and aggregates them. Verifying the individual partial signatures (as
     * opposed to only verifying the final signature) allows the coordinator
     * to identify which signer misbehaved if the protocol run fails. */
    for (i = 0; i < N_SIGNERS; i++) {
        if (!secp256k1_frost_partial_sig_verify(ctx, &signers[i].partial_sig, &signers[i].pubnonce, &signers[i].pubshare, &session, ids, N_SIGNERS, i)) {
            return 0;
        }
    }
    return secp256k1_frost_partial_sig_agg(ctx, sig64, NULL, &session, partial_sigs, N_SIGNERS);
}

int main(void) {
    secp256k1_context* ctx;
    int i;
    /* Secret key material known to the trusted dealer. The dealer must erase
     * it after the participants received their shares (done at the end of
     * this function). */
    unsigned char threshold_seckey[32];
    unsigned char secshares[N_PARTICIPANTS * 32];
    secp256k1_pubkey thresh_pk;
    secp256k1_pubkey pubshares[N_PARTICIPANTS];
    secp256k1_frost_tweak_cache cache;
    secp256k1_xonly_pubkey tweaked_pk;
    struct signer_secrets signer_secrets[N_SIGNERS];
    struct signer signers[N_SIGNERS];
    unsigned char msg[32] = "this_could_be_the_hash_of_a_msg";
    unsigned char sig[64];
    unsigned char buf[33];
    size_t outputlen;

    /* Create a secp256k1 context */
    ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);

    printf("Generating threshold key material...");
    fflush(stdout);
    if (!trusted_dealer_keygen(ctx, threshold_seckey, secshares, &thresh_pk, pubshares)) {
        printf("FAILED\n");
        return EXIT_FAILURE;
    }
    printf("ok\n");

    outputlen = sizeof(buf);
    if (!secp256k1_ec_pubkey_serialize(ctx, buf, &outputlen, &thresh_pk, SECP256K1_EC_COMPRESSED)) {
        printf("FAILED\n");
        return EXIT_FAILURE;
    }
    printf("Threshold public key:   ");
    print_hex(buf, outputlen);
    fflush(stdout);

    /* The dealer hands each participant their secret share (over a secure
     * channel). This example signs with the participants 0 and 2. */
    for (i = 0; i < N_SIGNERS; i++) {
        signers[i].id = SIGNER_IDS[i];
        signers[i].pubshare = pubshares[SIGNER_IDS[i]];
        memcpy(signer_secrets[i].secshare, &secshares[32 * SIGNER_IDS[i]], 32);
    }

    printf("Applying x-only tweak (Taproot-style)...");
    fflush(stdout);
    /* Every participant and the coordinator initializes a tweak cache from
     * the threshold public key and applies the same tweaks. */
    if (!secp256k1_frost_tweak_cache_init(ctx, &cache, &thresh_pk)) {
        printf("FAILED\n");
        return EXIT_FAILURE;
    }
    if (!tweak(ctx, &cache, &tweaked_pk)) {
        printf("FAILED\n");
        return EXIT_FAILURE;
    }
    printf("ok\n");
    if (!secp256k1_xonly_pubkey_serialize(ctx, buf, &tweaked_pk)) {
        printf("FAILED\n");
        return EXIT_FAILURE;
    }
    printf("Tweaked threshold public key: ");
    print_hex(buf, 32);
    fflush(stdout);

    printf("Signing message with %d-of-%d signers...", N_SIGNERS, N_PARTICIPANTS);
    fflush(stdout);
    if (!sign(ctx, signer_secrets, signers, &cache, msg, sizeof(msg), sig)) {
        printf("FAILED\n");
        return EXIT_FAILURE;
    }
    printf("ok\n");

    printf("Verifying signature.....");
    fflush(stdout);
    if (!secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &tweaked_pk)) {
        printf("FAILED\n");
        return EXIT_FAILURE;
    }
    printf("ok\n");

    /* It's best practice to try to clear secrets from memory after using them.
     * This is done because some bugs can allow an attacker to leak memory, for
     * example through "out of bounds" array access (see Heartbleed), or the OS
     * swapping them to disk. Hence, we overwrite secret key material with zeros.
     *
     * Here we are preventing these writes from being optimized out, as any good compiler
     * will remove any writes that aren't used.
     *
     * The secnonces have already been wiped by secp256k1_frost_sign. */
    secure_erase(threshold_seckey, sizeof(threshold_seckey));
    secure_erase(secshares, sizeof(secshares));
    for (i = 0; i < N_SIGNERS; i++) {
        secure_erase(&signer_secrets[i], sizeof(signer_secrets[i]));
    }
    secp256k1_context_destroy(ctx);
    return EXIT_SUCCESS;
}
