/**********************************************************************
 * Copyright (c) 2019-2020 Marko Bencun, Jonas Nick                   *
 * Distributed under the MIT software license, see the accompanying   *
 * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
 **********************************************************************/

#ifndef SECP256K1_MODULE_ECDSA_S2C_MAIN_H
#define SECP256K1_MODULE_ECDSA_S2C_MAIN_H

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

static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge) {
    secp256k1_pubkey_save((secp256k1_pubkey*) opening, ge);
}

static int secp256k1_ecdsa_s2c_opening_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_ecdsa_s2c_opening* opening) {
    return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey*) opening);
}

int secp256k1_ecdsa_s2c_opening_parse(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* input33) {
    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(opening != NULL);
    ARG_CHECK(input33 != NULL);
    return secp256k1_ec_pubkey_parse(ctx, (secp256k1_pubkey*) opening, input33, 33);
}

int secp256k1_ecdsa_s2c_opening_serialize(const secp256k1_context* ctx, unsigned char* output33, const secp256k1_ecdsa_s2c_opening* opening) {
    size_t out_len = 33;
    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(output33 != NULL);
    ARG_CHECK(opening != NULL);
    return secp256k1_ec_pubkey_serialize(ctx, output33, &out_len, (const secp256k1_pubkey*) opening, SECP256K1_EC_COMPRESSED);
}

/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
 * SHA256 to SHA256("s2c/ecdsa/point")||SHA256("s2c/ecdsa/point"). */
static void secp256k1_s2c_ecdsa_point_sha256_tagged(secp256k1_sha256 *sha) {
    secp256k1_sha256_initialize(sha);
    sha->s[0] = 0xa9b21c7bul;
    sha->s[1] = 0x358c3e3eul;
    sha->s[2] = 0x0b6863d1ul;
    sha->s[3] = 0xc62b2035ul;
    sha->s[4] = 0xb44b40ceul;
    sha->s[5] = 0x254a8912ul;
    sha->s[6] = 0x0f85d0d4ul;
    sha->s[7] = 0x8a5bf91cul;

    sha->bytes = 64;
}

/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
 * SHA256 to SHA256("s2c/ecdsa/data")||SHA256("s2c/ecdsa/data"). */
static void secp256k1_s2c_ecdsa_data_sha256_tagged(secp256k1_sha256 *sha) {
    secp256k1_sha256_initialize(sha);
    sha->s[0] = 0xfeefd675ul;
    sha->s[1] = 0x73166c99ul;
    sha->s[2] = 0xe2309cb8ul;
    sha->s[3] = 0x6d458113ul;
    sha->s[4] = 0x01d3a512ul;
    sha->s[5] = 0x00e18112ul;
    sha->s[6] = 0x37ee0874ul;
    sha->s[7] = 0x421fc55ful;

    sha->bytes = 64;
}

int secp256k1_ecdsa_s2c_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* signature, secp256k1_ecdsa_s2c_opening* s2c_opening, const unsigned char
 *msg32, const unsigned char *seckey, const unsigned char* s2c_data32) {
    secp256k1_scalar r, s;
    int ret;
    unsigned char ndata[32];
    secp256k1_sha256 s2c_sha;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    ARG_CHECK(msg32 != NULL);
    ARG_CHECK(signature != NULL);
    ARG_CHECK(seckey != NULL);
    ARG_CHECK(s2c_data32 != NULL);

    /* Provide `s2c_data32` to the nonce function as additional data to
     * derive the nonce. It is first hashed because it should be possible
     * to derive nonces even if only a SHA256 commitment to the data is
     * known.  This is important in the ECDSA anti-exfil protocol. */
    secp256k1_s2c_ecdsa_data_sha256_tagged(&s2c_sha);
    secp256k1_sha256_write(&s2c_sha, s2c_data32, 32);
    secp256k1_sha256_finalize(&s2c_sha, ndata);

    secp256k1_s2c_ecdsa_point_sha256_tagged(&s2c_sha);
    ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, &s2c_sha, s2c_opening, s2c_data32, msg32, seckey, NULL, ndata);
    secp256k1_scalar_cmov(&r, &secp256k1_scalar_zero, !ret);
    secp256k1_scalar_cmov(&s, &secp256k1_scalar_zero, !ret);
    secp256k1_ecdsa_signature_save(signature, &r, &s);
    return ret;
}

int secp256k1_ecdsa_s2c_verify_commit(const secp256k1_context* ctx, const secp256k1_ecdsa_signature* sig, const unsigned char* data32, const secp256k1_ecdsa_s2c_opening* opening) {
    secp256k1_ge commitment_ge;
    secp256k1_ge original_pubnonce_ge;
    unsigned char x_bytes[32];
    secp256k1_scalar sigr, sigs, x_scalar;
    secp256k1_sha256 s2c_sha;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(sig != NULL);
    ARG_CHECK(data32 != NULL);
    ARG_CHECK(opening != NULL);

    if (!secp256k1_ecdsa_s2c_opening_load(ctx, &original_pubnonce_ge, opening)) {
        return 0;
    }
    secp256k1_s2c_ecdsa_point_sha256_tagged(&s2c_sha);
    if (!secp256k1_ec_commit(&commitment_ge, &original_pubnonce_ge, &s2c_sha, data32, 32)) {
        return 0;
    }

    /* Check that sig_r == commitment_x (mod n)
     * sig_r is the x coordinate of R represented by a scalar.
     * commitment_x is the x coordinate of the commitment (field element).
     *
     * Note that we are only checking the x-coordinate -- this is because the y-coordinate
     * is not part of the ECDSA signature (and therefore not part of the commitment!)
     */
    secp256k1_ecdsa_signature_load(ctx, &sigr, &sigs, sig);

    secp256k1_fe_normalize(&commitment_ge.x);
    secp256k1_fe_get_b32(x_bytes, &commitment_ge.x);
    /* Do not check overflow; overflowing a scalar does not affect whether
     * or not the R value is a cryptographic commitment, only whether it
     * is a valid R value for an ECDSA signature. If users care about that
     * they should use `ecdsa_verify` or `anti_exfil_host_verify`. In other
     * words, this check would be (at best) unnecessary, and (at worst)
     * insufficient. */
    secp256k1_scalar_set_b32(&x_scalar, x_bytes, NULL);
    return secp256k1_scalar_eq(&sigr, &x_scalar);
}

/*** anti-exfil ***/
int secp256k1_ecdsa_anti_exfil_host_commit(const secp256k1_context* ctx, unsigned char* rand_commitment32, const unsigned char* rand32) {
    secp256k1_sha256 sha;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(rand_commitment32 != NULL);
    ARG_CHECK(rand32 != NULL);

    secp256k1_s2c_ecdsa_data_sha256_tagged(&sha);
    secp256k1_sha256_write(&sha, rand32, 32);
    secp256k1_sha256_finalize(&sha, rand_commitment32);
    return 1;
}

int secp256k1_ecdsa_anti_exfil_signer_commit(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* msg32, const unsigned char* seckey32, const unsigned char* rand_commitment32) {
    unsigned char nonce32[32];
    secp256k1_scalar k;
    secp256k1_gej rj;
    secp256k1_ge r;
    unsigned int count = 0;
    int is_nonce_valid = 0;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
    ARG_CHECK(opening != NULL);
    ARG_CHECK(msg32 != NULL);
    ARG_CHECK(seckey32 != NULL);
    ARG_CHECK(rand_commitment32 != NULL);

    memset(nonce32, 0, 32);
    while (!is_nonce_valid) {
        /* cast to void* removes const qualifier, but secp256k1_nonce_function_default does not modify it */
        if (!secp256k1_nonce_function_default(nonce32, msg32, seckey32, NULL, (void*)rand_commitment32, count)) {
            secp256k1_callback_call(&ctx->error_callback, "(cryptographically unreachable) generated bad nonce");
        }
        is_nonce_valid = secp256k1_scalar_set_b32_seckey(&k, nonce32);
        /* The nonce is still secret here, but it being invalid is is less likely than 1:2^255. */
        secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid));
        count++;
    }

    secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
    secp256k1_ge_set_gej(&r, &rj);
    secp256k1_ecdsa_s2c_opening_save(opening, &r);
    memset(nonce32, 0, 32);
    secp256k1_scalar_clear(&k);
    return 1;
}

int secp256k1_anti_exfil_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char* msg32, const unsigned char* seckey, const unsigned char* host_data32) {
    return secp256k1_ecdsa_s2c_sign(ctx, sig, NULL, msg32, seckey, host_data32);
}

int secp256k1_anti_exfil_host_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey, const unsigned char *host_data32, const secp256k1_ecdsa_s2c_opening *opening) {
    return secp256k1_ecdsa_s2c_verify_commit(ctx, sig, host_data32, opening) &&
        secp256k1_ecdsa_verify(ctx, sig, msg32, pubkey);
}

#endif /* SECP256K1_ECDSA_S2C_MAIN_H */
