/* ecc-eddsa.c  -  Elliptic Curve EdDSA signatures
 * Copyright (C) 2013, 2014 g10 Code GmbH
 *
 * This file is part of Libgcrypt.
 *
 * Libgcrypt is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * Libgcrypt is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

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

#include "g10lib.h"
#include "mpi.h"
#include "cipher.h"
#include "context.h"
#include "ec-context.h"
#include "ecc-common.h"



void
reverse_buffer (unsigned char *buffer, unsigned int length)
{
  unsigned int tmp, i;

  for (i=0; i < length/2; i++)
    {
      tmp = buffer[i];
      buffer[i] = buffer[length-1-i];
      buffer[length-1-i] = tmp;
    }
}


/* Helper to scan a hex string. */
static gcry_mpi_t
scanval (const char *string)
{
  gpg_err_code_t rc;
  gcry_mpi_t val;

  rc = _gcry_mpi_scan (&val, GCRYMPI_FMT_HEX, string, 0, NULL);
  if (rc)
    log_fatal ("scanning ECC parameter failed: %s\n", gpg_strerror (rc));
  return val;
}



/* Encode MPI using the EdDSA scheme.  MINLEN specifies the required
   length of the buffer in bytes.  On success 0 is returned an a
   malloced buffer with the encoded point is stored at R_BUFFER; the
   length of this buffer is stored at R_BUFLEN.  */
static gpg_err_code_t
eddsa_encodempi (gcry_mpi_t mpi, unsigned int nbits,
                 unsigned char **r_buffer, unsigned int *r_buflen)
{
  unsigned char *rawmpi;
  unsigned int rawmpilen;
  unsigned int minlen = (nbits%8) == 0 ? (nbits/8 + 1): (nbits+7)/8;

  rawmpi = _gcry_mpi_get_buffer (mpi, minlen, &rawmpilen, NULL);
  if (!rawmpi)
    return gpg_err_code_from_syserror ();

  *r_buffer = rawmpi;
  *r_buflen = rawmpilen;
  return 0;
}


/* Encode (X,Y) using the EdDSA scheme.  NBITS is the number of bits
   of the field of the curve.  If WITH_PREFIX is set the returned
   buffer is prefixed with a 0x40 byte.  On success 0 is returned and
   a malloced buffer with the encoded point is stored at R_BUFFER; the
   length of this buffer is stored at R_BUFLEN.  */
static gpg_err_code_t
eddsa_encode_x_y (gcry_mpi_t x, gcry_mpi_t y, unsigned int nbits,
                  int with_prefix,
                  unsigned char **r_buffer, unsigned int *r_buflen)
{
  unsigned char *rawmpi;
  unsigned int rawmpilen;
  int off = with_prefix? 1:0;
  unsigned int minlen = (nbits%8) == 0 ? (nbits/8 + 1): (nbits+7)/8;

  rawmpi = _gcry_mpi_get_buffer_extra (y, minlen, off?-1:0, &rawmpilen, NULL);
  if (!rawmpi)
    return gpg_err_code_from_syserror ();
  if (mpi_test_bit (x, 0) && rawmpilen)
    rawmpi[off + rawmpilen - 1] |= 0x80;  /* Set sign bit.  */
  if (off)
    rawmpi[0] = 0x40;

  *r_buffer = rawmpi;
  *r_buflen = rawmpilen + off;
  return 0;
}

/* Encode POINT using the EdDSA scheme.  X and Y are either scratch
   variables supplied by the caller or NULL.  CTX is the usual
   context.  If WITH_PREFIX is set the returned buffer is prefixed
   with a 0x40 byte.  On success 0 is returned and a malloced buffer
   with the encoded point is stored at R_BUFFER; the length of this
   buffer is stored at R_BUFLEN.  */
gpg_err_code_t
_gcry_ecc_eddsa_encodepoint (mpi_point_t point, mpi_ec_t ec,
                             gcry_mpi_t x_in, gcry_mpi_t y_in,
                             int with_prefix,
                             unsigned char **r_buffer, unsigned int *r_buflen)
{
  gpg_err_code_t rc;
  gcry_mpi_t x, y;

  x = x_in? x_in : mpi_new (0);
  y = y_in? y_in : mpi_new (0);

  if (_gcry_mpi_ec_get_affine (x, y, point, ec))
    {
      log_error ("eddsa_encodepoint: Failed to get affine coordinates\n");
      rc = GPG_ERR_INTERNAL;
    }
  else
    rc = eddsa_encode_x_y (x, y, ec->nbits, with_prefix, r_buffer, r_buflen);

  if (!x_in)
    mpi_free (x);
  if (!y_in)
    mpi_free (y);
  return rc;
}


/* Make sure that the opaque MPI VALUE is in compact EdDSA format.
   This function updates MPI if needed.  */
gpg_err_code_t
_gcry_ecc_eddsa_ensure_compact (gcry_mpi_t value, unsigned int nbits)
{
  gpg_err_code_t rc;
  const unsigned char *buf;
  unsigned int rawmpilen;
  gcry_mpi_t x, y;
  unsigned char *enc;
  unsigned int enclen;

  if (!mpi_is_opaque (value))
    return GPG_ERR_INV_OBJ;
  buf = mpi_get_opaque (value, &rawmpilen);
  if (!buf)
    return GPG_ERR_INV_OBJ;
  rawmpilen = (rawmpilen + 7)/8;

  if (rawmpilen > 1 && (rawmpilen%2))
    {
      if (buf[0] == 0x04)
        {
          /* Buffer is in SEC1 uncompressed format.  Extract y and
             compress.  */
          rc = _gcry_mpi_scan (&x, GCRYMPI_FMT_USG,
                               buf+1, (rawmpilen-1)/2, NULL);
          if (rc)
            return rc;
          rc = _gcry_mpi_scan (&y, GCRYMPI_FMT_USG,
                               buf+1+(rawmpilen-1)/2, (rawmpilen-1)/2, NULL);
          if (rc)
            {
              mpi_free (x);
              return rc;
            }

          rc = eddsa_encode_x_y (x, y, nbits, 0, &enc, &enclen);
          mpi_free (x);
          mpi_free (y);
          if (rc)
            return rc;

          mpi_set_opaque (value, enc, 8*enclen);
        }
      else if (buf[0] == 0x40)
        {
          /* Buffer is compressed but with our SEC1 alike compression
             indicator.  Remove that byte.  FIXME: We should write and
             use a function to manipulate an opaque MPI in place. */
          if (!_gcry_mpi_set_opaque_copy (value, buf + 1, (rawmpilen - 1)*8))
            return gpg_err_code_from_syserror ();
        }
    }

  return 0;
}


static gpg_err_code_t
ecc_ed448_recover_x (gcry_mpi_t x, gcry_mpi_t y, int x_0, mpi_ec_t ec)
{
  gpg_err_code_t rc = 0;
  gcry_mpi_t u, v, u3, v3, t;
  static gcry_mpi_t p34; /* Hard coded (P-3)/4 */

  if (mpi_cmp (y, ec->p) >= 0)
    rc = GPG_ERR_INV_OBJ;

  if (!p34)
    p34 = scanval ("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
                   "BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");

  u   = mpi_new (0);
  v   = mpi_new (0);
  u3  = mpi_new (0);
  v3  = mpi_new (0);
  t   = mpi_new (0);

  /* Compute u and v */
  /* u = y^2    */
  mpi_mulm (u, y, y, ec->p);
  /* v = b*y^2   */
  mpi_mulm (v, ec->b, u, ec->p);
  /* u = y^2-1  */
  mpi_sub_ui (u, u, 1);
  /* v = b*y^2-1 */
  mpi_sub_ui (v, v, 1);

  /* Compute sqrt(u/v) */
  /* u3 = u^3 */
  mpi_powm (u3, u, mpi_const (MPI_C_THREE), ec->p);
  mpi_powm (v3, v, mpi_const (MPI_C_THREE), ec->p);
  /* t = u^4 * u * v3 = u^5 * v^3 */
  mpi_powm (t, u, mpi_const (MPI_C_FOUR), ec->p);
  mpi_mulm (t, t, u, ec->p);
  mpi_mulm (t, t, v3, ec->p);
  /* t = t^((p-3)/4) = (u^5 * v^3)^((p-3)/4)  */
  mpi_powm (t, t, p34, ec->p);
  /* x = t * u^3 * v = (u^3 * v) * (u^5 * v^3)^((p-3)/4) */
  mpi_mulm (t, t, u3, ec->p);
  mpi_mulm (x, t, v, ec->p);

  /* t = v * x^2  */
  mpi_mulm (t, x, x, ec->p);
  mpi_mulm (t, t, v, ec->p);

  if (mpi_cmp (t, u) != 0)
    rc = GPG_ERR_INV_OBJ;
  else
    {
      if (!mpi_cmp_ui (x, 0) && x_0)
        rc = GPG_ERR_INV_OBJ;

      /* Choose the desired square root according to parity */
      if (mpi_test_bit (x, 0) != !!x_0)
        mpi_sub (x, ec->p, x);
    }

  mpi_free (t);
  mpi_free (u3);
  mpi_free (v3);
  mpi_free (v);
  mpi_free (u);

  return rc;
}


/* Recover X from Y and SIGN (which actually is a parity bit).  */
gpg_err_code_t
_gcry_ecc_eddsa_recover_x (gcry_mpi_t x, gcry_mpi_t y, int sign, mpi_ec_t ec)
{
  gpg_err_code_t rc = 0;
  gcry_mpi_t u, v, v3, t;
  static gcry_mpi_t p58, seven;

  /*
   * This routine is actually curve specific.  Now, only supports
   * Ed25519 and Ed448.
   */

  if (ec->dialect != ECC_DIALECT_ED25519)
    /* For now, it's only Ed448.  */
    return ecc_ed448_recover_x (x, y, sign, ec);

  /* It's Ed25519.  */

  if (!p58)
    p58 = scanval ("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
                   "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD");
  if (!seven)
    seven = mpi_set_ui (NULL, 7);

  u   = mpi_new (0);
  v   = mpi_new (0);
  v3  = mpi_new (0);
  t   = mpi_new (0);

  /* Compute u and v */
  /* u = y^2    */
  mpi_mulm (u, y, y, ec->p);
  /* v = b*y^2   */
  mpi_mulm (v, ec->b, u, ec->p);
  /* u = y^2-1  */
  mpi_sub_ui (u, u, 1);
  /* v = b*y^2+1 */
  mpi_add_ui (v, v, 1);

  /* Compute sqrt(u/v) */
  /* v3 = v^3 */
  mpi_powm (v3, v, mpi_const (MPI_C_THREE), ec->p);
  /* t = v3 * v3 * u * v = u * v^7 */
  mpi_powm (t, v, seven, ec->p);
  mpi_mulm (t, t, u, ec->p);
  /* t = t^((p-5)/8) = (u * v^7)^((p-5)/8)  */
  mpi_powm (t, t, p58, ec->p);
  /* x = t * u * v^3 = (u * v^3) * (u * v^7)^((p-5)/8) */
  mpi_mulm (t, t, u, ec->p);
  mpi_mulm (x, t, v3, ec->p);

  /* Adjust if needed.  */
  /* t = v * x^2  */
  mpi_mulm (t, x, x, ec->p);
  mpi_mulm (t, t, v, ec->p);
  /* -t == u ? x = x * sqrt(-1) */
  mpi_sub (t, ec->p, t);
  if (!mpi_cmp (t, u))
    {
      static gcry_mpi_t m1;  /* Fixme: this is not thread-safe.  */
      if (!m1)
        m1 = scanval ("2B8324804FC1DF0B2B4D00993DFBD7A7"
                      "2F431806AD2FE478C4EE1B274A0EA0B0");
      mpi_mulm (x, x, m1, ec->p);
      /* t = v * x^2  */
      mpi_mulm (t, x, x, ec->p);
      mpi_mulm (t, t, v, ec->p);
      /* -t == u ? x = x * sqrt(-1) */
      mpi_sub (t, ec->p, t);
      if (!mpi_cmp (t, u))
        rc = GPG_ERR_INV_OBJ;
    }

  /* Choose the desired square root according to parity */
  if (mpi_test_bit (x, 0) != !!sign)
    mpi_sub (x, ec->p, x);

  mpi_free (t);
  mpi_free (v3);
  mpi_free (v);
  mpi_free (u);

  return rc;
}


/* Decode the EdDSA style encoded PK and set it into RESULT.  CTX is
   the usual curve context.  If R_ENCPK is not NULL, the encoded PK is
   stored at that address; this is a new copy to be released by the
   caller.  In contrast to the supplied PK, this is not an MPI and
   thus guaranteed to be properly padded.  R_ENCPKLEN receives the
   length of that encoded key.  */
gpg_err_code_t
_gcry_ecc_eddsa_decodepoint (gcry_mpi_t pk, mpi_ec_t ctx, mpi_point_t result,
                             unsigned char **r_encpk, unsigned int *r_encpklen)
{
  gpg_err_code_t rc;
  unsigned char *rawmpi;
  unsigned int rawmpilen;
  int sign;

  if (mpi_is_opaque (pk))
    {
      const unsigned char *buf;
      unsigned int len;

      len = (ctx->nbits%8) == 0 ? (ctx->nbits/8 + 1): (ctx->nbits+7)/8;

      buf = mpi_get_opaque (pk, &rawmpilen);
      if (!buf)
        return GPG_ERR_INV_OBJ;
      rawmpilen = (rawmpilen + 7)/8;

      if (!(rawmpilen == len
            || rawmpilen == len + 1
            || rawmpilen == len * 2 + 1))
        return GPG_ERR_INV_OBJ;

      /* Handle compression prefixes.  The size of the buffer will be
         odd in this case.  */
      if (rawmpilen > 1 && (rawmpilen == len + 1 || rawmpilen == len * 2 + 1))
        {
          /* First check whether the public key has been given in
             standard uncompressed format (SEC1).  No need to recover
             x in this case.  */
          if (buf[0] == 0x04)
            {
              gcry_mpi_t x, y;

              rc = _gcry_mpi_scan (&x, GCRYMPI_FMT_USG,
                                   buf+1, (rawmpilen-1)/2, NULL);
              if (rc)
                return rc;
              rc = _gcry_mpi_scan (&y, GCRYMPI_FMT_USG,
                                   buf+1+(rawmpilen-1)/2, (rawmpilen-1)/2,NULL);
              if (rc)
                {
                  mpi_free (x);
                  return rc;
                }

              if (r_encpk)
                {
                  rc = eddsa_encode_x_y (x, y, ctx->nbits, 0,
                                         r_encpk, r_encpklen);
                  if (rc)
                    {
                      mpi_free (x);
                      mpi_free (y);
                      return rc;
                    }
                }
              mpi_snatch (result->x, x);
              mpi_snatch (result->y, y);
              mpi_set_ui (result->z, 1);
              return 0;
            }

          /* Check whether the public key has been prefixed with a 0x40
             byte to explicitly indicate compressed format using a SEC1
             alike prefix byte.  This is a Libgcrypt extension.  */
          if (buf[0] == 0x40)
            {
              rawmpilen--;
              buf++;
            }
        }

      /* EdDSA compressed point.  */
      rawmpi = xtrymalloc (rawmpilen);
      if (!rawmpi)
        return gpg_err_code_from_syserror ();
      memcpy (rawmpi, buf, rawmpilen);
      reverse_buffer (rawmpi, rawmpilen);
    }
  else
    {
      /* Note: Without using an opaque MPI it is not reliable possible
         to find out whether the public key has been given in
         uncompressed format.  Thus we expect native EdDSA format.  */
      rawmpi = _gcry_mpi_get_buffer (pk, (ctx->nbits+7)/8, &rawmpilen, NULL);
      if (!rawmpi)
        return gpg_err_code_from_syserror ();
    }

  if (rawmpilen)
    {
      sign = !!(rawmpi[0] & 0x80);
      rawmpi[0] &= 0x7f;
    }
  else
    sign = 0;
  _gcry_mpi_set_buffer (result->y, rawmpi, rawmpilen, 0);
  if (r_encpk)
    {
      /* Revert to little endian.  */
      if (sign && rawmpilen)
        rawmpi[0] |= 0x80;
      reverse_buffer (rawmpi, rawmpilen);
      *r_encpk = rawmpi;
      if (r_encpklen)
        *r_encpklen = rawmpilen;
    }
  else
    xfree (rawmpi);

  rc = _gcry_ecc_eddsa_recover_x (result->x, result->y, sign, ctx);
  mpi_set_ui (result->z, 1);

  return rc;
}


/* Compute the A value as used by EdDSA.  The caller needs to provide
   the context EC and the actual secret D as an MPI.  The function
   returns a newly allocated 64 byte buffer at r_digest; the first 32
   bytes represent the A value.  NULL is returned on error and NULL
   stored at R_DIGEST.  */
gpg_err_code_t
_gcry_ecc_eddsa_compute_h_d (unsigned char **r_digest, mpi_ec_t ec)
{
  gpg_err_code_t rc;
  unsigned char *rawmpi = NULL;
  unsigned int rawmpilen;
  unsigned char *digest;
  int hashalgo, b;

  *r_digest = NULL;

  b = (ec->nbits+7)/8;

  /*
   * Choice of hashalgo is curve specific.
   * For now, it's determine by the bit size of the field.
   */
  if (ec->nbits == 255)
    hashalgo = GCRY_MD_SHA512;
  else if (ec->nbits == 448)
    {
      b++;
      hashalgo = GCRY_MD_SHAKE256;
    }
  else
    return GPG_ERR_NOT_IMPLEMENTED;

  /* Note that we clear DIGEST so we can use it as input to left pad
     the key with zeroes for hashing.  */
  digest = xtrycalloc_secure (2, b);
  if (!digest)
    return gpg_err_code_from_syserror ();

  rawmpi = _gcry_mpi_get_buffer (ec->d, 0, &rawmpilen, NULL);
  if (!rawmpi)
    {
      xfree (digest);
      return gpg_err_code_from_syserror ();
    }

  if (hashalgo == GCRY_MD_SHAKE256)
    {
      gcry_error_t err;
      gcry_md_hd_t hd;

      err = _gcry_md_open (&hd, hashalgo, 0);
      if (err)
        rc = gcry_err_code (err);
      else
        {
          _gcry_md_write (hd, rawmpi, rawmpilen);
          _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0);
          _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b);
          _gcry_md_close (hd);
          rc = 0;
        }
    }
  else
    {
      gcry_buffer_t hvec[2];

      memset (hvec, 0, sizeof hvec);

      hvec[0].data = digest;
      hvec[0].len = b > rawmpilen? b - rawmpilen : 0;
      hvec[1].data = rawmpi;
      hvec[1].len = rawmpilen;
      rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 2);
    }

  xfree (rawmpi);
  if (rc)
    {
      xfree (digest);
      return rc;
    }

  /* Compute the A value.  */
  reverse_buffer (digest, b);  /* Only the first half of the hash.  */

  /* Field specific handling of clearing/setting bits. */
  if (ec->nbits == 255)
    {
      digest[0]   = (digest[0] & 0x7f) | 0x40;
      digest[31] &= 0xf8;
    }
  else
    {
      digest[0]   = 0;
      digest[1]  |= 0x80;
      digest[56] &= 0xfc;
    }

  *r_digest = digest;
  return 0;
}


/**
 * _gcry_ecc_eddsa_genkey - EdDSA version of the key generation.
 *
 * @ec: Elliptic curve computation context.
 * @flags: Flags controlling aspects of the creation.
 *
 * Return: An error code.
 *
 * The only @flags bit used by this function is %PUBKEY_FLAG_TRANSIENT
 * to use a faster RNG.
 */
gpg_err_code_t
_gcry_ecc_eddsa_genkey (mpi_ec_t ec, int flags)
{
  gpg_err_code_t rc;
  int b;
  gcry_mpi_t a, x, y;
  mpi_point_struct Q;
  gcry_random_level_t random_level;
  char *dbuf;
  size_t dlen;
  unsigned char *hash_d = NULL;

  point_init (&Q);

  if ((flags & PUBKEY_FLAG_TRANSIENT_KEY))
    random_level = GCRY_STRONG_RANDOM;
  else
    random_level = GCRY_VERY_STRONG_RANDOM;

  b = (ec->nbits+7)/8;

  if (ec->nbits == 255)
    ;
  else if (ec->nbits == 448)
    b++;
  else
    return GPG_ERR_NOT_IMPLEMENTED;

  dlen = b;

  a = mpi_snew (0);
  x = mpi_new (0);
  y = mpi_new (0);

  /* Generate a secret.  */
  dbuf = _gcry_random_bytes_secure (dlen, random_level);
  ec->d = _gcry_mpi_set_opaque (NULL, dbuf, dlen*8);
  rc = _gcry_ecc_eddsa_compute_h_d (&hash_d, ec);
  if (rc)
    goto leave;

  _gcry_mpi_set_buffer (a, hash_d, b, 0);
  xfree (hash_d);
  /* log_printmpi ("ecgen         a", a); */

  /* Compute Q.  */
  _gcry_mpi_ec_mul_point (&Q, a, ec->G, ec);
  if (DBG_CIPHER)
    log_printpnt ("ecgen      pk", &Q, ec);

  ec->Q = mpi_point_snatch_set (NULL, Q.x, Q.y, Q.z);
  Q.x = NULL;
  Q.y = NULL;
  Q.x = NULL;

 leave:
  _gcry_mpi_release (a);
  _gcry_mpi_release (x);
  _gcry_mpi_release (y);
  return rc;
}


/* Compute an EdDSA signature. See:
 *   [ed25519] 23pp. (PDF) Daniel J. Bernstein, Niels Duif, Tanja
 *   Lange, Peter Schwabe, Bo-Yin Yang. High-speed high-security
 *   signatures.  Journal of Cryptographic Engineering 2 (2012), 77-89.
 *   Document ID: a1a62a2f76d23f65d622484ddd09caf8.
 *   URL: http://cr.yp.to/papers.html#ed25519. Date: 2011.09.26.
 *
 * Despite that this function requires the specification of a hash
 * algorithm, we only support what has been specified by the paper.
 * This may change in the future.
 *
 * Return the signature struct (r,s) from the message hash.  The caller
 * must have allocated R_R and S.
 */

/* String to be used with Ed448 */
#define DOM25519     "SigEd25519 no Ed25519 collisions"
#define DOM25519_LEN 32
#define DOM448       "SigEd448"
#define DOM448_LEN   8

gpg_err_code_t
_gcry_ecc_eddsa_sign (gcry_mpi_t input, mpi_ec_t ec,
                      gcry_mpi_t r_r, gcry_mpi_t s,
                      struct pk_encoding_ctx *ctx)
{
  int rc;
  unsigned int tmp;
  unsigned char *digest = NULL;
  const void *mbuf;
  size_t mlen;
  unsigned char *rawmpi = NULL;
  unsigned int rawmpilen;
  unsigned char *encpk = NULL; /* Encoded public key.  */
  unsigned int encpklen;
  mpi_point_struct I;          /* Intermediate value.  */
  gcry_mpi_t a, x, y, r;
  int b;
  unsigned char x_olen[2];
  unsigned char prehashed_msg[64];

  b = (ec->nbits+7)/8;

  if (ec->nbits == 255)
    ;
  else if (ec->nbits == 448)
    b++;
  else
    return GPG_ERR_NOT_IMPLEMENTED;

  if (!mpi_is_opaque (input))
    return GPG_ERR_INV_DATA;

  /* Initialize some helpers.  */
  point_init (&I);
  a = mpi_snew (0);
  x = mpi_new (0);
  y = mpi_new (0);
  r = mpi_snew (0);

  rc = _gcry_ecc_eddsa_compute_h_d (&digest, ec);
  if (rc)
    goto leave;
  _gcry_mpi_set_buffer (a, digest, b, 0);

  /* Compute the public key if it's not available (only secret part).  */
  if (ec->Q == NULL)
    {
      mpi_point_struct Q;

      point_init (&Q);
      _gcry_mpi_ec_mul_point (&Q, a, ec->G, ec);
      ec->Q = mpi_point_snatch_set (NULL, Q.x, Q.y, Q.z);
    }
  rc = _gcry_ecc_eddsa_encodepoint (ec->Q, ec, x, y, 0, &encpk, &encpklen);
  if (rc)
    goto leave;
  if (DBG_CIPHER)
    log_printhex ("  e_pk", encpk, encpklen);

  /* Compute R.  */
  mbuf = mpi_get_opaque (input, &tmp);
  mlen = (tmp +7)/8;
  if (DBG_CIPHER)
    log_printhex ("     m", mbuf, mlen);

  if (ctx->hash_algo == GCRY_MD_SHAKE256)
    {
      gcry_error_t err;
      gcry_md_hd_t hd;

      err = _gcry_md_open (&hd, ctx->hash_algo, 0);
      if (err)
        rc = gcry_err_code (err);
      else
        {
          _gcry_md_write (hd, DOM448, DOM448_LEN);
          x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH);
          x_olen[1] = ctx->labellen;
          _gcry_md_write (hd, x_olen, 2);
          if (ctx->labellen)
            _gcry_md_write (hd, ctx->label, ctx->labellen);
          _gcry_md_write (hd, digest+b, b);
          if ((ctx->flags & PUBKEY_FLAG_PREHASH))
            {
              gcry_md_hd_t hd2;

              err = _gcry_md_open (&hd2, ctx->hash_algo, 0);
              if (err)
                {
                  rc = gcry_err_code (err);
                  _gcry_md_close (hd);
                  goto leave;
                }
              _gcry_md_write (hd2, mbuf, mlen);
              _gcry_md_ctl (hd2, GCRYCTL_FINALIZE, NULL, 0);
              _gcry_md_extract (hd2, GCRY_MD_SHAKE256, prehashed_msg, 64);
              _gcry_md_close (hd2);
              _gcry_md_write (hd, prehashed_msg, 64);
            }
          else
            _gcry_md_write (hd, mbuf, mlen);
          _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0);
          _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b);
          _gcry_md_close (hd);
          rc = 0;
        }
    }
  else
    {
      gcry_buffer_t hvec[6];
      int i = 0;

      memset (hvec, 0, sizeof hvec);

      if ((ctx->flags & PUBKEY_FLAG_PREHASH) || ctx->labellen)
        {
          hvec[i].data = (void *)DOM25519;
          hvec[i].len  = DOM25519_LEN;
          i++;
          x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH);
          x_olen[1] = ctx->labellen;
          hvec[i].data = x_olen;
          hvec[i].len  = 2;
          i++;
          if (ctx->labellen)
            {
              hvec[i].data = ctx->label;
              hvec[i].len  = ctx->labellen;
              i++;
            }
        }

      hvec[i].data = digest;
      hvec[i].off  = b;
      hvec[i].len  = b;
      i++;
      if ((ctx->flags & PUBKEY_FLAG_PREHASH))
        {
          _gcry_md_hash_buffer (ctx->hash_algo, prehashed_msg, mbuf, mlen);
          hvec[i].data = (char*)prehashed_msg;
          hvec[i].len  = 64;
        }
      else
        {
          hvec[i].data = (char*)mbuf;
          hvec[i].len  = mlen;
        }
      i++;
      rc = _gcry_md_hash_buffers (ctx->hash_algo, 0, digest, hvec, i);
    }

  if (rc)
    goto leave;
  reverse_buffer (digest, 2*b);
  if (DBG_CIPHER)
    log_printhex ("     r", digest, 2*b);
  _gcry_mpi_set_buffer (r, digest, 2*b, 0);
  mpi_mod (r, r, ec->n);
  _gcry_mpi_ec_mul_point (&I, r, ec->G, ec);
  if (DBG_CIPHER)
    log_printpnt ("   r", &I, ec);

  /* Convert R into affine coordinates and apply encoding.  */
  rc = _gcry_ecc_eddsa_encodepoint (&I, ec, x, y, 0, &rawmpi, &rawmpilen);
  if (rc)
    goto leave;
  if (DBG_CIPHER)
    log_printhex ("   e_r", rawmpi, rawmpilen);

  if (ctx->hash_algo == GCRY_MD_SHAKE256)
    {
      gcry_error_t err;
      gcry_md_hd_t hd;

      err = _gcry_md_open (&hd, ctx->hash_algo, 0);
      if (err)
        rc = gcry_err_code (err);
      else
        {
          _gcry_md_write (hd, DOM448, DOM448_LEN);
          x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH);
          x_olen[1] = ctx->labellen;
          _gcry_md_write (hd, x_olen, 2);
          if (ctx->labellen)
            _gcry_md_write (hd, ctx->label, ctx->labellen);
          _gcry_md_write (hd, rawmpi, rawmpilen);
          _gcry_md_write (hd, encpk, encpklen);
          if ((ctx->flags & PUBKEY_FLAG_PREHASH))
            _gcry_md_write (hd, prehashed_msg, 64);
          else
            _gcry_md_write (hd, mbuf, mlen);
          _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0);
          _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b);
          _gcry_md_close (hd);
          rc = 0;
        }
    }
  else
    {
      gcry_buffer_t hvec[6];
      int i = 0;

      memset (hvec, 0, sizeof hvec);

      if ((ctx->flags & PUBKEY_FLAG_PREHASH) || ctx->labellen)
        {
          hvec[i].data = (void *)DOM25519;
          hvec[i].len  = DOM25519_LEN;
          i++;
          x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH);
          x_olen[1] = ctx->labellen;
          hvec[i].data = x_olen;
          hvec[i].len  = 2;
          i++;
          if (ctx->labellen)
            {
              hvec[i].data = ctx->label;
              hvec[i].len  = ctx->labellen;
              i++;
            }
        }

      /* S = r + a * H(dom2(F,C)+encodepoint(R)+encodepoint(pk)+m) mod n  */
      hvec[i].data = rawmpi;  /* (this is R) */
      hvec[i].len  = rawmpilen;
      i++;
      hvec[i].data = encpk;
      hvec[i].len  = encpklen;
      i++;
      if ((ctx->flags & PUBKEY_FLAG_PREHASH))
        {
          hvec[i].data = (char*)prehashed_msg;
          hvec[i].len  = 64;
        }
      else
        {
          hvec[i].data = (char*)mbuf;
          hvec[i].len  = mlen;
        }
      i++;
      rc = _gcry_md_hash_buffers (ctx->hash_algo, 0, digest, hvec, i);
    }

  if (rc)
    goto leave;

  /* No more need for RAWMPI thus we now transfer it to R_R.  */
  mpi_set_opaque (r_r, rawmpi, rawmpilen*8);
  rawmpi = NULL;

  reverse_buffer (digest, 2*b);
  if (DBG_CIPHER)
    log_printhex (" H(R+)", digest, 2*b);
  _gcry_mpi_set_buffer (s, digest, 2*b, 0);
  mpi_mulm (s, s, a, ec->n);
  mpi_addm (s, s, r, ec->n);
  rc = eddsa_encodempi (s, ec->nbits, &rawmpi, &rawmpilen);
  if (rc)
    goto leave;
  if (DBG_CIPHER)
    log_printhex ("   e_s", rawmpi, rawmpilen);
  mpi_set_opaque (s, rawmpi, rawmpilen*8);
  rawmpi = NULL;

  rc = 0;

 leave:
  _gcry_mpi_release (a);
  _gcry_mpi_release (x);
  _gcry_mpi_release (y);
  _gcry_mpi_release (r);
  xfree (digest);
  point_free (&I);
  xfree (encpk);
  xfree (rawmpi);
  return rc;
}


/* Verify an EdDSA signature.  See sign_eddsa for the reference.
 * Check if R_IN and S_IN verifies INPUT.
 */
gpg_err_code_t
_gcry_ecc_eddsa_verify (gcry_mpi_t input, mpi_ec_t ec,
                        gcry_mpi_t r_in, gcry_mpi_t s_in,
                        struct pk_encoding_ctx *ctx)
{
  int rc;
  int b;
  unsigned int tmp;
  unsigned char *encpk = NULL; /* Encoded public key.  */
  unsigned int encpklen;
  const void *mbuf, *rbuf;
  unsigned char *tbuf = NULL;
  size_t mlen, rlen;
  unsigned int tlen;
  unsigned char digest[114];
  gcry_mpi_t h, s;
  mpi_point_struct Ia, Ib;
  unsigned char x_olen[2];
  unsigned char prehashed_msg[64];

  if (!mpi_is_opaque (input) || !mpi_is_opaque (r_in) || !mpi_is_opaque (s_in))
    return GPG_ERR_INV_DATA;

  point_init (&Ia);
  point_init (&Ib);
  h = mpi_new (0);
  s = mpi_new (0);

  b = (ec->nbits+7)/8;

  if (ec->nbits == 255)
    ;
  else if (ec->nbits == 448)
    b++;
  else
    return GPG_ERR_NOT_IMPLEMENTED;

  /* Encode and check the public key.  */
  rc = _gcry_ecc_eddsa_encodepoint (ec->Q, ec, NULL, NULL, 0,
                                    &encpk, &encpklen);
  if (rc)
    goto leave;
  if (!_gcry_mpi_ec_curve_point (ec->Q, ec))
    {
      rc = GPG_ERR_BROKEN_PUBKEY;
      goto leave;
    }
  if (DBG_CIPHER)
    log_printhex ("  e_pk", encpk, encpklen);
  if (encpklen != b)
    {
      rc = GPG_ERR_INV_LENGTH;
      goto leave;
    }

  /* Convert the other input parameters.  */
  mbuf = mpi_get_opaque (input, &tmp);
  mlen = (tmp +7)/8;
  if (DBG_CIPHER)
    log_printhex ("     m", mbuf, mlen);
  rbuf = mpi_get_opaque (r_in, &tmp);
  rlen = (tmp +7)/8;
  if (DBG_CIPHER)
    log_printhex ("     r", rbuf, rlen);
  if (rlen != b)
    {
      rc = GPG_ERR_INV_LENGTH;
      goto leave;
    }

  if (ctx->hash_algo == GCRY_MD_SHAKE256)
    {
      gcry_error_t err;
      gcry_md_hd_t hd;

      err = _gcry_md_open (&hd, ctx->hash_algo, 0);
      if (err)
        rc = gcry_err_code (err);
      else
        {
          _gcry_md_write (hd, DOM448, DOM448_LEN);
          x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH);
          x_olen[1] = ctx->labellen;
          _gcry_md_write (hd, x_olen, 2);
          if (ctx->labellen)
            _gcry_md_write (hd, ctx->label, ctx->labellen);
          _gcry_md_write (hd, rbuf, rlen);
          _gcry_md_write (hd, encpk, encpklen);
          if ((ctx->flags & PUBKEY_FLAG_PREHASH))
            {
              gcry_md_hd_t hd2;

              err = _gcry_md_open (&hd2, ctx->hash_algo, 0);
              if (err)
                {
                  rc = gcry_err_code (err);
                  _gcry_md_close (hd);
                  goto leave;
                }
              _gcry_md_write (hd2, mbuf, mlen);
              _gcry_md_ctl (hd2, GCRYCTL_FINALIZE, NULL, 0);
              _gcry_md_extract (hd2, GCRY_MD_SHAKE256, prehashed_msg, 64);
              _gcry_md_close (hd2);
              _gcry_md_write (hd, prehashed_msg, 64);
            }
          else
            _gcry_md_write (hd, mbuf, mlen);
          _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0);
          _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b);
          _gcry_md_close (hd);
          rc = 0;
        }
    }
  else
    {
      gcry_buffer_t hvec[6];
      int i = 0;

      memset (hvec, 0, sizeof hvec);

      /* h = H(dom2(F,C)+encodepoint(R)+encodepoint(pk)+m)  */
      if ((ctx->flags & PUBKEY_FLAG_PREHASH) || ctx->labellen)
        {
          hvec[i].data = (void *)DOM25519;
          hvec[i].len  = DOM25519_LEN;
          i++;
          x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH);
          x_olen[1] = ctx->labellen;
          hvec[i].data = x_olen;
          hvec[i].len  = 2;
          i++;
          if (ctx->labellen)
            {
              hvec[i].data = ctx->label;
              hvec[i].len  = ctx->labellen;
              i++;
            }
        }

      hvec[i].data = (char*)rbuf;
      hvec[i].len  = rlen;
      i++;
      hvec[i].data = encpk;
      hvec[i].len  = encpklen;
      i++;
      if ((ctx->flags & PUBKEY_FLAG_PREHASH))
        {
          _gcry_md_hash_buffer (ctx->hash_algo, prehashed_msg, mbuf, mlen);
          hvec[i].data = (char*)prehashed_msg;
          hvec[i].len  = 64;
        }
      else
        {
          hvec[i].data = (char*)mbuf;
          hvec[i].len  = mlen;
        }
      i++;
      rc = _gcry_md_hash_buffers (ctx->hash_algo, 0, digest, hvec, i);
    }

  if (rc)
    goto leave;
  reverse_buffer (digest, 2*b);
  if (DBG_CIPHER)
    log_printhex (" H(R+)", digest, 2*b);
  _gcry_mpi_set_buffer (h, digest, 2*b, 0);

  /* According to the paper the best way for verification is:
         encodepoint(sG - h·Q) = encodepoint(r)
     because we don't need to decode R. */
  {
    void *sbuf;
    unsigned int slen;

    sbuf = _gcry_mpi_get_opaque_copy (s_in, &tmp);
    slen = (tmp +7)/8;
    reverse_buffer (sbuf, slen);
    if (DBG_CIPHER)
      log_printhex ("     s", sbuf, slen);
    _gcry_mpi_set_buffer (s, sbuf, slen, 0);
    xfree (sbuf);
    if (slen != b)
      {
        rc = GPG_ERR_INV_LENGTH;
        goto leave;
      }
  }

  _gcry_mpi_ec_mul_point (&Ia, s, ec->G, ec);
  _gcry_mpi_ec_mul_point (&Ib, h, ec->Q, ec);
  _gcry_mpi_sub (Ib.x, ec->p, Ib.x);
  _gcry_mpi_ec_add_points (&Ia, &Ia, &Ib, ec);
  rc = _gcry_ecc_eddsa_encodepoint (&Ia, ec, s, h, 0, &tbuf, &tlen);
  if (rc)
    goto leave;
  if (tlen != rlen || memcmp (tbuf, rbuf, tlen))
    {
      rc = GPG_ERR_BAD_SIGNATURE;
      goto leave;
    }

  rc = 0;

 leave:
  xfree (encpk);
  xfree (tbuf);
  _gcry_mpi_release (s);
  _gcry_mpi_release (h);
  point_free (&Ia);
  point_free (&Ib);
  return rc;
}
