#include <string.h>
#include "crypto_additions.h"
#include "gen_x.h"
#include "gen_constants.h"
#include "gen_eddsa.h"
#include "gen_veddsa.h"
#include "gen_crypto_additions.h"
#include "zeroize.h"

static int convert_25519_pubkey(unsigned char* ed_pubkey_bytes, const unsigned char* x25519_pubkey_bytes) {
  fe u;
  fe y;

  /* Convert the X25519 public key into an Ed25519 public key.

     y = (u - 1) / (u + 1)

     NOTE: u=-1 is converted to y=0 since fe_invert is mod-exp
  */
  if (!fe_isreduced(x25519_pubkey_bytes))
      return -1;
  fe_frombytes(u, x25519_pubkey_bytes);
  fe_montx_to_edy(y, u);
  fe_tobytes(ed_pubkey_bytes, y);
  return 0;
}

static int calculate_25519_keypair(unsigned char* K_bytes, unsigned char* k_scalar, 
                            const unsigned char* x25519_privkey_scalar)
{
  unsigned char kneg[SCALARLEN];
  ge_p3 ed_pubkey_point;
  unsigned char sign_bit = 0;

  if (SCALARLEN != 32)
    return -1;

  /* Convert the Curve25519 privkey to an Ed25519 public key */
  ge_scalarmult_base(&ed_pubkey_point, x25519_privkey_scalar);
  ge_p3_tobytes(K_bytes, &ed_pubkey_point);

  /* Force Edwards sign bit to zero */
  sign_bit = (K_bytes[31] & 0x80) >> 7;
  memcpy(k_scalar, x25519_privkey_scalar, 32);
  sc_neg(kneg, k_scalar);
  sc_cmov(k_scalar, kneg, sign_bit); 
  K_bytes[31] &= 0x7F;

  zeroize(kneg, SCALARLEN);
  return 0;
}

int generalized_xeddsa_25519_sign(unsigned char* signature_out,
                              const unsigned char* x25519_privkey_scalar,
                              const unsigned char* msg, const unsigned long msg_len,
                              const unsigned char* random,
                              const unsigned char* customization_label,
                              const unsigned long customization_label_len)
{
  unsigned char K_bytes[POINTLEN];
  unsigned char k_scalar[SCALARLEN];
  int retval = -1;

  if (calculate_25519_keypair(K_bytes, k_scalar, x25519_privkey_scalar) != 0)
    return -1;

  retval = generalized_eddsa_25519_sign(signature_out, 
                                        K_bytes, k_scalar,
                                        msg, msg_len, random, 
                                        customization_label, customization_label_len);
  zeroize(k_scalar, SCALARLEN);
  return retval;
}

int generalized_xveddsa_25519_sign(
                  unsigned char* signature_out,
                  const unsigned char* x25519_privkey_scalar,
                  const unsigned char* msg, 
                  const unsigned long msg_len,
                  const unsigned char* random,
                  const unsigned char* customization_label,
                  const unsigned long customization_label_len)
{
  unsigned char K_bytes[POINTLEN];
  unsigned char k_scalar[SCALARLEN];
  int retval = -1;

  if (calculate_25519_keypair(K_bytes, k_scalar, x25519_privkey_scalar) != 0)
    return -1;

  retval = generalized_veddsa_25519_sign(signature_out, K_bytes, k_scalar, 
                                         msg, msg_len, random, 
                                         customization_label, customization_label_len);
  zeroize(k_scalar, SCALARLEN);
  return retval;
}

int generalized_xeddsa_25519_verify(
                  const unsigned char* signature,
                  const unsigned char* x25519_pubkey_bytes,
                  const unsigned char* msg, 
                  const unsigned long msg_len,
                  const unsigned char* customization_label,
                  const unsigned long customization_label_len)
{
  unsigned char K_bytes[POINTLEN];

  if (convert_25519_pubkey(K_bytes, x25519_pubkey_bytes) != 0)
      return -1;

  return generalized_eddsa_25519_verify(signature, K_bytes, msg, msg_len, 
                                        customization_label, customization_label_len);
}

int generalized_xveddsa_25519_verify(
                  unsigned char* vrf_out,
                  const unsigned char* signature,
                  const unsigned char* x25519_pubkey_bytes,
                  const unsigned char* msg, 
                  const unsigned long msg_len,
                  const unsigned char* customization_label,
                  const unsigned long customization_label_len)
{
  unsigned char K_bytes[POINTLEN];

  if (convert_25519_pubkey(K_bytes, x25519_pubkey_bytes) != 0)
      return -1;

  return generalized_veddsa_25519_verify(vrf_out, signature, K_bytes, msg, msg_len, 
                                         customization_label, customization_label_len);
}