/***********************************************************************
 * Copyright (c) 2014-2015 Pieter Wuille                               *
 * Distributed under the MIT software license, see the accompanying    *
 * file COPYING or http://www.opensource.org/licenses/mit-license.php. *
 ***********************************************************************/

#ifndef _SECP256K1_SCHNORR_IMPL_H_
#define _SECP256K1_SCHNORR_IMPL_H_

#include <string.h>

#include "schnorr.h"
#include "num.h"
#include "field.h"
#include "group.h"
#include "ecmult.h"
#include "ecmult_gen.h"

/**
 * Custom Schnorr-based signature scheme. They support multiparty signing, public key
 * recovery and batch validation.
 *
 * Rationale for verifying R's y coordinate:
 * In order to support batch validation and public key recovery, the full R point must
 * be known to verifiers, rather than just its x coordinate. In order to not risk
 * being more strict in batch validation than normal validation, validators must be
 * required to reject signatures with incorrect y coordinate. This is only possible
 * by including a (relatively slow) field inverse, or a field square root. However,
 * batch validation offers potentially much higher benefits than this cost.
 *
 * Rationale for having an implicit y coordinate oddness:
 * If we commit to having the full R point known to verifiers, there are two mechanism.
 * Either include its oddness in the signature, or give it an implicit fixed value.
 * As the R y coordinate can be flipped by a simple negation of the nonce, we choose the
 * latter, as it comes with nearly zero impact on signing or validation performance, and
 * saves a byte in the signature.
 *
 * Signing:
 *   Inputs: 32-byte message m, 32-byte scalar key x (!=0), 32-byte scalar nonce k (!=0)
 *
 *   Compute point R = k * G. Reject nonce if R's y coordinate is odd (or negate nonce).
 *   Compute 32-byte r, the serialization of R's x coordinate.
 *   Compute scalar h = Hash(r || m). Reject nonce if h == 0 or h >= order.
 *   Compute scalar s = k - h * x.
 *   The signature is (r, s).
 *
 *
 * Verification:
 *   Inputs: 32-byte message m, public key point Q, signature: (32-byte r, scalar s)
 *
 *   Signature is invalid if s >= order.
 *   Signature is invalid if r >= p.
 *   Compute scalar h = Hash(r || m). Signature is invalid if h == 0 or h >= order.
 *   Option 1 (faster for single verification):
 *     Compute point R = h * Q + s * G. Signature is invalid if R is infinity or R's y coordinate is odd.
 *     Signature is valid if the serialization of R's x coordinate equals r.
 *   Option 2 (allows batch validation and pubkey recovery):
 *     Decompress x coordinate r into point R, with odd y coordinate. Fail if R is not on the curve.
 *     Signature is valid if R + h * Q + s * G == 0.
 */

static int secp256k1_schnorr_sig_sign(const secp256k1_ecmult_gen_context_t* ctx, unsigned char *sig64, const secp256k1_scalar_t *key, const secp256k1_scalar_t *nonce, const secp256k1_ge_t *pubnonce, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32) {
    secp256k1_gej_t Rj;
    secp256k1_ge_t Ra;
    unsigned char h32[32];
    secp256k1_scalar_t h, s;
    int overflow;
    secp256k1_scalar_t n;

    if (secp256k1_scalar_is_zero(key) || secp256k1_scalar_is_zero(nonce)) {
        return 0;
    }
    n = *nonce;

    secp256k1_ecmult_gen(ctx, &Rj, &n);
    if (pubnonce) {
        secp256k1_gej_add_ge(&Rj, &Rj, pubnonce);
    }
    secp256k1_ge_set_gej(&Ra, &Rj);
    secp256k1_fe_normalize(&Ra.y);
    if (secp256k1_fe_is_odd(&Ra.y)) {
        /* R's y coordinate is odd, which is not allowed (see rationale above).
           Force it to be even by negating the nonce. Note that this even works
           for multiparty signing, as the R point is known to all participants,
           which can all decide to flip the sign in unison, resulting in the
           overall R point to be negated too. */
        secp256k1_scalar_negate(&n, &n);
    }
    secp256k1_fe_normalize(&Ra.x);
    secp256k1_fe_get_b32(sig64, &Ra.x);
    hash(h32, sig64, msg32);
    overflow = 0;
    secp256k1_scalar_set_b32(&h, h32, &overflow);
    if (overflow || secp256k1_scalar_is_zero(&h)) {
        secp256k1_scalar_clear(&n);
        return 0;
    }
    secp256k1_scalar_mul(&s, &h, key);
    secp256k1_scalar_negate(&s, &s);
    secp256k1_scalar_add(&s, &s, &n);
    secp256k1_scalar_clear(&n);
    secp256k1_scalar_get_b32(sig64 + 32, &s);
    return 1;
}

static int secp256k1_schnorr_sig_verify(const secp256k1_ecmult_context_t* ctx, const unsigned char *sig64, const secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32) {
    secp256k1_gej_t Qj, Rj;
    secp256k1_ge_t Ra;
    secp256k1_fe_t Rx;
    secp256k1_scalar_t h, s;
    unsigned char hh[32];
    int overflow;

    if (secp256k1_ge_is_infinity(pubkey)) {
        return 0;
    }
    hash(hh, sig64, msg32);
    overflow = 0;
    secp256k1_scalar_set_b32(&h, hh, &overflow);
    if (overflow || secp256k1_scalar_is_zero(&h)) {
        return 0;
    }
    overflow = 0;
    secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
    if (overflow) {
        return 0;
    }
    if (!secp256k1_fe_set_b32(&Rx, sig64)) {
        return 0;
    }
    secp256k1_gej_set_ge(&Qj, pubkey);
    secp256k1_ecmult(ctx, &Rj, &Qj, &h, &s);
    if (secp256k1_gej_is_infinity(&Rj)) {
        return 0;
    }
    secp256k1_ge_set_gej_var(&Ra, &Rj);
    secp256k1_fe_normalize_var(&Ra.y);
    if (secp256k1_fe_is_odd(&Ra.y)) {
        return 0;
    }
    return secp256k1_fe_equal_var(&Rx, &Ra.x);
}

static int secp256k1_schnorr_sig_recover(const secp256k1_ecmult_context_t* ctx, const unsigned char *sig64, secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32) {
    secp256k1_gej_t Qj, Rj;
    secp256k1_ge_t Ra;
    secp256k1_fe_t Rx;
    secp256k1_scalar_t h, s;
    unsigned char hh[32];
    int overflow;

    hash(hh, sig64, msg32);
    overflow = 0;
    secp256k1_scalar_set_b32(&h, hh, &overflow);
    if (overflow || secp256k1_scalar_is_zero(&h)) {
        return 0;
    }
    overflow = 0;
    secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
    if (overflow) {
        return 0;
    }
    if (!secp256k1_fe_set_b32(&Rx, sig64)) {
        return 0;
    }
    if (!secp256k1_ge_set_xo_var(&Ra, &Rx, 0)) {
        return 0;
    }
    secp256k1_gej_set_ge(&Rj, &Ra);
    secp256k1_scalar_inverse_var(&h, &h);
    secp256k1_scalar_negate(&s, &s);
    secp256k1_scalar_mul(&s, &s, &h);
    secp256k1_ecmult(ctx, &Qj, &Rj, &h, &s);
    if (secp256k1_gej_is_infinity(&Qj)) {
        return 0;
    }
    secp256k1_ge_set_gej(pubkey, &Qj);
    return 1;
}

static int secp256k1_schnorr_sig_combine(unsigned char *sig64, int n, const unsigned char * const *sig64ins) {
    secp256k1_scalar_t s = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
    int i;
    for (i = 0; i < n; i++) {
        secp256k1_scalar_t si;
        int overflow;
        secp256k1_scalar_set_b32(&si, sig64ins[i] + 32, &overflow);
        if (overflow) {
            return -1;
        }
        if (i) {
            if (memcmp(sig64ins[i - 1], sig64ins[i], 32) != 0) {
                return -1;
            }
        }
        secp256k1_scalar_add(&s, &s, &si);
    }
    if (secp256k1_scalar_is_zero(&s)) {
        return 0;
    }
    memcpy(sig64, sig64ins[0], 32);
    secp256k1_scalar_get_b32(sig64 + 32, &s);
    secp256k1_scalar_clear(&s);
    return 1;
}

#endif
