/*
   This file is part of GNUnet
   Copyright (C) 2014,2016,2019 GNUnet e.V.

   GNUnet is free software: you can redistribute it and/or modify it
   under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License,
   or (at your option) any later version.

   GNUnet 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
   Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

     SPDX-License-Identifier: AGPL3.0-or-later
 */

/**
 * @file util/crypto_rsa.c
 * @brief Chaum-style Blind signatures based on RSA
 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
 * @author Christian Grothoff
 * @author Jeffrey Burdges <burdges@gnunet.org>
 */
#include "platform.h"
#include <gcrypt.h>
#include "gnunet_crypto_lib.h"
#include "benchmark.h"

#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-rsa", __VA_ARGS__)


/**
 * The private information of an RSA key pair.
 */
struct GNUNET_CRYPTO_RsaPrivateKey
{
  /**
   * Libgcrypt S-expression for the RSA private key.
   */
  gcry_sexp_t sexp;
};


/**
 * The public information of an RSA key pair.
 */
struct GNUNET_CRYPTO_RsaPublicKey
{
  /**
   * Libgcrypt S-expression for the RSA public key.
   */
  gcry_sexp_t sexp;
};


/**
 * @brief an RSA signature
 */
struct GNUNET_CRYPTO_RsaSignature
{
  /**
   * Libgcrypt S-expression for the RSA signature.
   */
  gcry_sexp_t sexp;
};


/**
 * @brief RSA blinding key
 */
struct RsaBlindingKey
{
  /**
   * Random value used for blinding.
   */
  gcry_mpi_t r;
};


/**
 * Extract values from an S-expression.
 *
 * @param array where to store the result(s)
 * @param sexp S-expression to parse
 * @param topname top-level name in the S-expression that is of interest
 * @param elems names of the elements to extract
 * @return 0 on success
 */
static int
key_from_sexp (gcry_mpi_t *array,
               gcry_sexp_t sexp,
               const char *topname,
               const char *elems)
{
  gcry_sexp_t list;
  gcry_sexp_t l2;
  const char *s;
  unsigned int idx;

  if (! (list = gcry_sexp_find_token (sexp, topname, 0)))
    return 1;
  l2 = gcry_sexp_cadr (list);
  gcry_sexp_release (list);
  list = l2;
  if (! list)
    return 2;
  idx = 0;
  for (s = elems; *s; s++, idx++)
  {
    if (! (l2 = gcry_sexp_find_token (list, s, 1)))
    {
      for (unsigned int i = 0; i < idx; i++)
      {
        gcry_free (array[i]);
        array[i] = NULL;
      }
      gcry_sexp_release (list);
      return 3;                 /* required parameter not found */
    }
    array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
    gcry_sexp_release (l2);
    if (! array[idx])
    {
      for (unsigned int i = 0; i < idx; i++)
      {
        gcry_free (array[i]);
        array[i] = NULL;
      }
      gcry_sexp_release (list);
      return 4;                 /* required parameter is invalid */
    }
  }
  gcry_sexp_release (list);
  return 0;
}


struct GNUNET_CRYPTO_RsaPrivateKey *
GNUNET_CRYPTO_rsa_private_key_create (unsigned int len)
{
  struct GNUNET_CRYPTO_RsaPrivateKey *ret;
  gcry_sexp_t s_key;
  gcry_sexp_t s_keyparam;

  BENCHMARK_START (rsa_private_key_create);

  GNUNET_assert (0 ==
                 gcry_sexp_build (&s_keyparam,
                                  NULL,
                                  "(genkey(rsa(nbits %d)))",
                                  len));
  GNUNET_assert (0 ==
                 gcry_pk_genkey (&s_key,
                                 s_keyparam));
  gcry_sexp_release (s_keyparam);
#if EXTRA_CHECKS
  GNUNET_assert (0 ==
                 gcry_pk_testkey (s_key));
#endif
  ret = GNUNET_new (struct GNUNET_CRYPTO_RsaPrivateKey);
  ret->sexp = s_key;
  BENCHMARK_END (rsa_private_key_create);
  return ret;
}


void
GNUNET_CRYPTO_rsa_private_key_free (struct GNUNET_CRYPTO_RsaPrivateKey *key)
{
  gcry_sexp_release (key->sexp);
  GNUNET_free (key);
}


size_t
GNUNET_CRYPTO_rsa_private_key_encode (const struct
                                      GNUNET_CRYPTO_RsaPrivateKey *key,
                                      void **buffer)
{
  size_t n;
  char *b;

  n = gcry_sexp_sprint (key->sexp,
                        GCRYSEXP_FMT_DEFAULT,
                        NULL,
                        0);
  b = GNUNET_malloc (n);
  GNUNET_assert ((n - 1) ==      /* since the last byte is \0 */
                 gcry_sexp_sprint (key->sexp,
                                   GCRYSEXP_FMT_DEFAULT,
                                   b,
                                   n));
  *buffer = b;
  return n;
}


struct GNUNET_CRYPTO_RsaPrivateKey *
GNUNET_CRYPTO_rsa_private_key_decode (const void *buf,
                                      size_t buf_size)
{
  struct GNUNET_CRYPTO_RsaPrivateKey *key;

  key = GNUNET_new (struct GNUNET_CRYPTO_RsaPrivateKey);
  if (0 !=
      gcry_sexp_new (&key->sexp,
                     buf,
                     buf_size,
                     0))
  {
    LOG (GNUNET_ERROR_TYPE_WARNING,
         "Decoded private key is not valid\n");
    GNUNET_free (key);
    return NULL;
  }
  if (0 != gcry_pk_testkey (key->sexp))
  {
    LOG (GNUNET_ERROR_TYPE_WARNING,
         "Decoded private key is not valid\n");
    GNUNET_CRYPTO_rsa_private_key_free (key);
    return NULL;
  }
  return key;
}


struct GNUNET_CRYPTO_RsaPublicKey *
GNUNET_CRYPTO_rsa_private_key_get_public (
  const struct GNUNET_CRYPTO_RsaPrivateKey *priv)
{
  struct GNUNET_CRYPTO_RsaPublicKey *pub;
  gcry_mpi_t ne[2];
  int rc;
  gcry_sexp_t result;

  BENCHMARK_START (rsa_private_key_get_public);

  rc = key_from_sexp (ne, priv->sexp, "public-key", "ne");
  if (0 != rc)
    rc = key_from_sexp (ne, priv->sexp, "private-key", "ne");
  if (0 != rc)
    rc = key_from_sexp (ne, priv->sexp, "rsa", "ne");
  if (0 != rc)
  {
    GNUNET_break_op (0);
    return NULL;
  }
  rc = gcry_sexp_build (&result,
                        NULL,
                        "(public-key(rsa(n %m)(e %m)))",
                        ne[0],
                        ne[1]);
  gcry_mpi_release (ne[0]);
  gcry_mpi_release (ne[1]);
  pub = GNUNET_new (struct GNUNET_CRYPTO_RsaPublicKey);
  pub->sexp = result;
  BENCHMARK_END (rsa_private_key_get_public);
  return pub;
}


void
GNUNET_CRYPTO_rsa_public_key_free (struct GNUNET_CRYPTO_RsaPublicKey *key)
{
  gcry_sexp_release (key->sexp);
  GNUNET_free (key);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Format of the header of a serialized RSA public key.
 */
struct GNUNET_CRYPTO_RsaPublicKeyHeaderP
{
  /**
   * length of modulus 'n' in bytes, in NBO
   */
  uint16_t modulus_length GNUNET_PACKED;

  /**
   * length of exponent in bytes, in NBO
   */
  uint16_t public_exponent_length GNUNET_PACKED;

  /* followed by variable-size modulus and
     public exponent follows as big-endian encoded
     integers */
};

GNUNET_NETWORK_STRUCT_END


bool
GNUNET_CRYPTO_rsa_public_key_check (
  const struct GNUNET_CRYPTO_RsaPublicKey *key)
{
  gcry_mpi_t ne[2];
  int ret;

  ret = key_from_sexp (ne,
                       key->sexp,
                       "public-key",
                       "ne");
  if (0 != ret)
    ret = key_from_sexp (ne,
                         key->sexp,
                         "rsa",
                         "ne");
  if (0 != ret)
    return false;
  gcry_mpi_release (ne[0]);
  gcry_mpi_release (ne[1]);
  return true;
}


size_t
GNUNET_CRYPTO_rsa_public_key_encode (
  const struct GNUNET_CRYPTO_RsaPublicKey *key,
  void **buffer)
{
  gcry_mpi_t ne[2];
  size_t n_size;
  size_t e_size;
  size_t rsize;
  size_t buf_size;
  char *buf;
  struct GNUNET_CRYPTO_RsaPublicKeyHeaderP hdr;
  int ret;

  ret = key_from_sexp (ne,
                       key->sexp,
                       "public-key",
                       "ne");
  if (0 != ret)
    ret = key_from_sexp (ne,
                         key->sexp,
                         "rsa",
                         "ne");
  if (0 != ret)
  {
    GNUNET_break (0);
    *buffer = NULL;
    return 0;
  }
  gcry_mpi_print (GCRYMPI_FMT_USG,
                  NULL,
                  0,
                  &n_size,
                  ne[0]);
  gcry_mpi_print (GCRYMPI_FMT_USG,
                  NULL,
                  0,
                  &e_size,
                  ne[1]);
  if ( (e_size > UINT16_MAX) ||
       (n_size > UINT16_MAX) )
  {
    GNUNET_break (0);
    if (NULL != buffer)
      *buffer = NULL;
    gcry_mpi_release (ne[0]);
    gcry_mpi_release (ne[1]);
    return 0;
  }
  buf_size = n_size + e_size + sizeof (hdr);
  if (NULL == buffer)
  {
    gcry_mpi_release (ne[0]);
    gcry_mpi_release (ne[1]);
    return buf_size;
  }
  buf = GNUNET_malloc (buf_size);
  hdr.modulus_length = htons ((uint16_t) n_size);
  hdr.public_exponent_length = htons ((uint16_t) e_size);
  memcpy (buf,
          &hdr,
          sizeof (hdr));
  GNUNET_assert (0 ==
                 gcry_mpi_print (GCRYMPI_FMT_USG,
                                 (unsigned char *) &buf[sizeof (hdr)],
                                 n_size,
                                 &rsize,
                                 ne[0]));

  GNUNET_assert (0 ==
                 gcry_mpi_print (GCRYMPI_FMT_USG,
                                 (unsigned char *) &buf[sizeof (hdr) + n_size],
                                 e_size,
                                 &rsize,
                                 ne[1]));
  *buffer = buf;
  gcry_mpi_release (ne[0]);
  gcry_mpi_release (ne[1]);
  return buf_size;
}


void
GNUNET_CRYPTO_rsa_public_key_hash (const struct GNUNET_CRYPTO_RsaPublicKey *key,
                                   struct GNUNET_HashCode *hc)
{
  void *buf;
  size_t buf_size;

  buf_size = GNUNET_CRYPTO_rsa_public_key_encode (key,
                                                  &buf);
  GNUNET_CRYPTO_hash (buf,
                      buf_size,
                      hc);
  GNUNET_free (buf);
}


struct GNUNET_CRYPTO_RsaPublicKey *
GNUNET_CRYPTO_rsa_public_key_decode (const char *buf,
                                     size_t len)
{
  struct GNUNET_CRYPTO_RsaPublicKey *key;
  struct GNUNET_CRYPTO_RsaPublicKeyHeaderP hdr;
  size_t e_size;
  size_t n_size;
  gcry_mpi_t n;
  gcry_mpi_t e;
  gcry_sexp_t data;

  if (len < sizeof (hdr))
  {
    GNUNET_break_op (0);
    return NULL;
  }
  memcpy (&hdr, buf, sizeof (hdr));
  n_size = ntohs (hdr.modulus_length);
  e_size = ntohs (hdr.public_exponent_length);
  if (len != sizeof (hdr) + e_size + n_size)
  {
    GNUNET_break_op (0);
    return NULL;
  }
  if (0 !=
      gcry_mpi_scan (&n,
                     GCRYMPI_FMT_USG,
                     &buf[sizeof (hdr)],
                     n_size,
                     NULL))
  {
    GNUNET_break_op (0);
    return NULL;
  }
  if (0 !=
      gcry_mpi_scan (&e,
                     GCRYMPI_FMT_USG,
                     &buf[sizeof (hdr) + n_size],
                     e_size,
                     NULL))
  {
    GNUNET_break_op (0);
    gcry_mpi_release (n);
    return NULL;
  }

  if (0 !=
      gcry_sexp_build (&data,
                       NULL,
                       "(public-key(rsa(n %m)(e %m)))",
                       n,
                       e))
  {
    GNUNET_break (0);
    gcry_mpi_release (n);
    gcry_mpi_release (e);
    return NULL;
  }
  gcry_mpi_release (n);
  gcry_mpi_release (e);
  key = GNUNET_new (struct GNUNET_CRYPTO_RsaPublicKey);
  key->sexp = data;
  return key;
}


/**
 * Test for malicious RSA key.
 *
 * Assuming n is an RSA modulous and r is generated using a call to
 * GNUNET_CRYPTO_kdf_mod_mpi, if gcd(r,n) != 1 then n must be a
 * malicious RSA key designed to deanomize the user.
 *
 * @param r KDF result
 * @param n RSA modulus
 * @return True if gcd(r,n) = 1, False means RSA key is malicious
 */
static int
rsa_gcd_validate (gcry_mpi_t r,
                  gcry_mpi_t n)
{
  gcry_mpi_t g;
  int t;

  g = gcry_mpi_new (0);
  t = gcry_mpi_gcd (g, r, n);
  gcry_mpi_release (g);
  return t;
}


/**
 * Create a blinding key
 *
 * @param len length of the key in bits (e.g. 2048)
 * @param bks pre-secret to use to derive the blinding key
 * @return the newly created blinding key, NULL if RSA key is malicious
 */
static struct RsaBlindingKey *
rsa_blinding_key_derive (const struct GNUNET_CRYPTO_RsaPublicKey *pkey,
                         const struct GNUNET_CRYPTO_RsaBlindingKeySecret *bks)
{
  const char *xts = "Blinding KDF extractor HMAC key";  /* Trusts bks' randomness more */
  struct RsaBlindingKey *blind;
  gcry_mpi_t n;

  blind = GNUNET_new (struct RsaBlindingKey);

  /* Extract the composite n from the RSA public key */
  GNUNET_assert (0 ==
                 key_from_sexp (&n,
                                pkey->sexp,
                                "rsa",
                                "n"));
  /* Assert that it at least looks like an RSA key */
  GNUNET_assert (0 ==
                 gcry_mpi_get_flag (n,
                                    GCRYMPI_FLAG_OPAQUE));
  GNUNET_CRYPTO_kdf_mod_mpi (&blind->r,
                             n,
                             xts, strlen (xts),
                             bks, sizeof(*bks),
                             "Blinding KDF");
  if (0 == rsa_gcd_validate (blind->r,
                             n))
  {
    gcry_mpi_release (blind->r);
    GNUNET_free (blind);
    blind = NULL;
  }
  gcry_mpi_release (n);
  return blind;
}


/*
   We originally added GNUNET_CRYPTO_kdf_mod_mpi for the benefit of the
   previous routine.

   There was previously a call to GNUNET_CRYPTO_kdf in
   bkey = rsa_blinding_key_derive (len, bks);
   that gives exactly len bits where
   len = GNUNET_CRYPTO_rsa_public_key_len (pkey);

   Now r = 2^(len-1)/pkey.n is the probability that a set high bit being
   okay, meaning bkey < pkey.n.  It follows that (1-r)/2 of the time bkey >
   pkey.n making the effective bkey be
   bkey mod pkey.n = bkey - pkey.n
   so the effective bkey has its high bit set with probability r/2.

   We expect r to be close to 1/2 if the exchange is honest, but the
   exchange can choose r otherwise.

   In blind signing, the exchange sees
   B = bkey * S mod pkey.n
   On deposit, the exchange sees S so they can compute bkey' = B/S mod
   pkey.n for all B they recorded to see if bkey' has it's high bit set.
   Also, note the exchange can compute 1/S efficiently since they know the
   factors of pkey.n.

   I suppose that happens with probability r/(1+r) if its the wrong B, not
   completely sure.  If otoh we've the right B, then we've the probability
   r/2 of a set high bit in the effective bkey.

   Interestingly, r^2-r has a maximum at the default r=1/2 anyways, giving
   the wrong and right probabilities 1/3 and 1/4, respectively.

   I feared this gives the exchange a meaningful fraction of a bit of
   information per coin involved in the transaction.  It sounds damaging if
   numerous coins were involved.  And it could run across transactions in
   some scenarios.

   We fixed this by using a more uniform deterministic pseudo-random number
   generator for blinding factors.  I do not believe this to be a problem
   for the rsa_full_domain_hash routine, but better safe than sorry.
 */


int
GNUNET_CRYPTO_rsa_signature_cmp (const struct GNUNET_CRYPTO_RsaSignature *s1,
                                 const struct GNUNET_CRYPTO_RsaSignature *s2)
{
  void *b1;
  void *b2;
  size_t z1;
  size_t z2;
  int ret;

  z1 = GNUNET_CRYPTO_rsa_signature_encode (s1,
                                           &b1);
  z2 = GNUNET_CRYPTO_rsa_signature_encode (s2,
                                           &b2);
  if (z1 != z2)
    ret = 1;
  else
    ret = memcmp (b1,
                  b2,
                  z1);
  GNUNET_free (b1);
  GNUNET_free (b2);
  return ret;
}


int
GNUNET_CRYPTO_rsa_public_key_cmp (const struct GNUNET_CRYPTO_RsaPublicKey *p1,
                                  const struct GNUNET_CRYPTO_RsaPublicKey *p2)
{
  void *b1;
  void *b2;
  size_t z1;
  size_t z2;
  int ret;

  z1 = GNUNET_CRYPTO_rsa_public_key_encode (p1,
                                            &b1);
  z2 = GNUNET_CRYPTO_rsa_public_key_encode (p2,
                                            &b2);
  if (z1 != z2)
    ret = 1;
  else
    ret = memcmp (b1,
                  b2,
                  z1);
  GNUNET_free (b1);
  GNUNET_free (b2);
  return ret;
}


int
GNUNET_CRYPTO_rsa_private_key_cmp (const struct GNUNET_CRYPTO_RsaPrivateKey *p1,
                                   const struct GNUNET_CRYPTO_RsaPrivateKey *p2)
{
  void *b1;
  void *b2;
  size_t z1;
  size_t z2;
  int ret;

  z1 = GNUNET_CRYPTO_rsa_private_key_encode (p1,
                                             &b1);
  z2 = GNUNET_CRYPTO_rsa_private_key_encode (p2,
                                             &b2);
  if (z1 != z2)
    ret = 1;
  else
    ret = memcmp (b1,
                  b2,
                  z1);
  GNUNET_free (b1);
  GNUNET_free (b2);
  return ret;
}


unsigned int
GNUNET_CRYPTO_rsa_public_key_len (const struct GNUNET_CRYPTO_RsaPublicKey *key)
{
  gcry_mpi_t n;
  unsigned int rval;

  if (0 != key_from_sexp (&n, key->sexp, "rsa", "n"))
  {   /* Not an RSA public key */
    GNUNET_break (0);
    return 0;
  }
  rval = gcry_mpi_get_nbits (n);
  gcry_mpi_release (n);
  return rval;
}


/**
 * Destroy a blinding key
 *
 * @param bkey the blinding key to destroy
 */
static void
rsa_blinding_key_free (struct RsaBlindingKey *bkey)
{
  gcry_mpi_release (bkey->r);
  GNUNET_free (bkey);
}


/**
 * Print an MPI to a newly created buffer
 *
 * @param v MPI to print.
 * @param[out] buffer newly allocated buffer containing the result
 * @return number of bytes stored in @a buffer
 */
static size_t
numeric_mpi_alloc_n_print (gcry_mpi_t v,
                           char **buffer)
{
  size_t n;
  char *b;
  size_t rsize;

  gcry_mpi_print (GCRYMPI_FMT_USG,
                  NULL,
                  0,
                  &n,
                  v);
  b = GNUNET_malloc (n);
  GNUNET_assert (0 ==
                 gcry_mpi_print (GCRYMPI_FMT_USG,
                                 (unsigned char *) b,
                                 n,
                                 &rsize,
                                 v));
  *buffer = b;
  return n;
}


/**
 * Computes a full domain hash seeded by the given public key.
 * This gives a measure of provable security to the Taler exchange
 * against one-more forgery attacks.  See:
 *   https://eprint.iacr.org/2001/002.pdf
 *   http://www.di.ens.fr/~pointche/Documents/Papers/2001_fcA.pdf
 *
 * @param hash initial hash of the message to sign
 * @param pkey the public key of the signer
 * @param rsize If not NULL, the number of bytes actually stored in buffer
 * @return MPI value set to the FDH, NULL if RSA key is malicious
 */
static gcry_mpi_t
rsa_full_domain_hash (const struct GNUNET_CRYPTO_RsaPublicKey *pkey,
                      const struct GNUNET_HashCode *hash)
{
  gcry_mpi_t r, n;
  void *xts;
  size_t xts_len;
  int ok;

  /* Extract the composite n from the RSA public key */
  GNUNET_assert (0 == key_from_sexp (&n, pkey->sexp, "rsa", "n"));
  /* Assert that it at least looks like an RSA key */
  GNUNET_assert (0 == gcry_mpi_get_flag (n, GCRYMPI_FLAG_OPAQUE));

  /* We key with the public denomination key as a homage to RSA-PSS by  *
  * Mihir Bellare and Phillip Rogaway.  Doing this lowers the degree   *
  * of the hypothetical polyomial-time attack on RSA-KTI created by a  *
  * polynomial-time one-more forgary attack.  Yey seeding!       */
  xts_len = GNUNET_CRYPTO_rsa_public_key_encode (pkey,
                                                 &xts);

  GNUNET_CRYPTO_kdf_mod_mpi (&r,
                             n,
                             xts, xts_len,
                             hash, sizeof(*hash),
                             "RSA-FDA FTpsW!");
  GNUNET_free (xts);
  ok = rsa_gcd_validate (r, n);
  gcry_mpi_release (n);
  if (ok)
    return r;
  gcry_mpi_release (r);
  return NULL;
}


enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_rsa_blind (const struct GNUNET_HashCode *hash,
                         const struct GNUNET_CRYPTO_RsaBlindingKeySecret *bks,
                         struct GNUNET_CRYPTO_RsaPublicKey *pkey,
                         void **buf,
                         size_t *buf_size)
{
  struct RsaBlindingKey *bkey;
  gcry_mpi_t data;
  gcry_mpi_t ne[2];
  gcry_mpi_t r_e;
  gcry_mpi_t data_r_e;
  int ret;

  BENCHMARK_START (rsa_blind);

  GNUNET_assert (buf != NULL);
  GNUNET_assert (buf_size != NULL);
  ret = key_from_sexp (ne, pkey->sexp, "public-key", "ne");
  if (0 != ret)
    ret = key_from_sexp (ne, pkey->sexp, "rsa", "ne");
  if (0 != ret)
  {
    GNUNET_break (0);
    *buf = NULL;
    *buf_size = 0;
    return GNUNET_NO;
  }

  data = rsa_full_domain_hash (pkey, hash);
  if (NULL == data)
    goto rsa_gcd_validate_failure;

  bkey = rsa_blinding_key_derive (pkey, bks);
  if (NULL == bkey)
  {
    gcry_mpi_release (data);
    goto rsa_gcd_validate_failure;
  }

  r_e = gcry_mpi_new (0);
  gcry_mpi_powm (r_e,
                 bkey->r,
                 ne[1],
                 ne[0]);
  data_r_e = gcry_mpi_new (0);
  gcry_mpi_mulm (data_r_e,
                 data,
                 r_e,
                 ne[0]);
  gcry_mpi_release (data);
  gcry_mpi_release (ne[0]);
  gcry_mpi_release (ne[1]);
  gcry_mpi_release (r_e);
  rsa_blinding_key_free (bkey);

  *buf_size = numeric_mpi_alloc_n_print (data_r_e,
                                         (char **) buf);
  gcry_mpi_release (data_r_e);

  BENCHMARK_END (rsa_blind);

  return GNUNET_YES;

rsa_gcd_validate_failure:
  /* We know the RSA key is malicious here, so warn the wallet. */
  /* GNUNET_break_op (0); */
  gcry_mpi_release (ne[0]);
  gcry_mpi_release (ne[1]);
  *buf = NULL;
  *buf_size = 0;
  return GNUNET_NO;
}


/**
 * Convert an MPI to an S-expression suitable for signature operations.
 *
 * @param value pointer to the data to convert
 * @return converted s-expression
 */
static gcry_sexp_t
mpi_to_sexp (gcry_mpi_t value)
{
  gcry_sexp_t data = NULL;

  GNUNET_assert (0 ==
                 gcry_sexp_build (&data,
                                  NULL,
                                  "(data (flags raw) (value %M))",
                                  value));
  return data;
}


/**
 * Sign the given MPI.
 *
 * @param key private key to use for the signing
 * @param value the MPI to sign
 * @return NULL on error, signature on success
 */
static struct GNUNET_CRYPTO_RsaSignature *
rsa_sign_mpi (const struct GNUNET_CRYPTO_RsaPrivateKey *key,
              gcry_mpi_t value)
{
  struct GNUNET_CRYPTO_RsaSignature *sig;
  gcry_sexp_t data;
  gcry_sexp_t result;
  int rc;

  data = mpi_to_sexp (value);

  if (0 !=
      (rc = gcry_pk_sign (&result,
                          data,
                          key->sexp)))
  {
    LOG (GNUNET_ERROR_TYPE_WARNING,
         _ ("RSA signing failed at %s:%d: %s\n"),
         __FILE__,
         __LINE__,
         gcry_strerror (rc));
    gcry_sexp_release (data);
    GNUNET_break (0);
    return NULL;
  }

  /* Lenstra protection was first added to libgcrypt 1.6.4
   * with commit c17f84bd02d7ee93845e92e20f6ddba814961588.
   */
#if GCRYPT_VERSION_NUMBER < 0x010604
  /* verify signature (guards against Lenstra's attack with fault injection...) */
  struct GNUNET_CRYPTO_RsaPublicKey *public_key =
    GNUNET_CRYPTO_rsa_private_key_get_public (key);
  if (0 !=
      gcry_pk_verify (result,
                      data,
                      public_key->sexp))
  {
    GNUNET_break (0);
    GNUNET_CRYPTO_rsa_public_key_free (public_key);
    gcry_sexp_release (data);
    gcry_sexp_release (result);
    return NULL;
  }
  GNUNET_CRYPTO_rsa_public_key_free (public_key);
#endif

  /* return signature */
  gcry_sexp_release (data);
  sig = GNUNET_new (struct GNUNET_CRYPTO_RsaSignature);
  sig->sexp = result;
  return sig;
}


struct GNUNET_CRYPTO_RsaSignature *
GNUNET_CRYPTO_rsa_sign_blinded (const struct GNUNET_CRYPTO_RsaPrivateKey *key,
                                const void *msg,
                                size_t msg_len)
{
  gcry_mpi_t v = NULL;
  struct GNUNET_CRYPTO_RsaSignature *sig;

  BENCHMARK_START (rsa_sign_blinded);

  GNUNET_assert (0 ==
                 gcry_mpi_scan (&v,
                                GCRYMPI_FMT_USG,
                                msg,
                                msg_len,
                                NULL));

  sig = rsa_sign_mpi (key, v);
  gcry_mpi_release (v);
  BENCHMARK_END (rsa_sign_blinded);
  return sig;
}


struct GNUNET_CRYPTO_RsaSignature *
GNUNET_CRYPTO_rsa_sign_fdh (const struct GNUNET_CRYPTO_RsaPrivateKey *key,
                            const struct GNUNET_HashCode *hash)
{
  struct GNUNET_CRYPTO_RsaPublicKey *pkey;
  gcry_mpi_t v = NULL;
  struct GNUNET_CRYPTO_RsaSignature *sig;

  pkey = GNUNET_CRYPTO_rsa_private_key_get_public (key);
  v = rsa_full_domain_hash (pkey, hash);
  GNUNET_CRYPTO_rsa_public_key_free (pkey);
  if (NULL == v)   /* rsa_gcd_validate failed meaning */
    return NULL;   /* our *own* RSA key is malicious. */

  sig = rsa_sign_mpi (key, v);
  gcry_mpi_release (v);
  return sig;
}


void
GNUNET_CRYPTO_rsa_signature_free (struct GNUNET_CRYPTO_RsaSignature *sig)
{
  gcry_sexp_release (sig->sexp);
  GNUNET_free (sig);
}


size_t
GNUNET_CRYPTO_rsa_signature_encode (
  const struct GNUNET_CRYPTO_RsaSignature *sig,
  void **buffer)
{
  gcry_mpi_t s;
  size_t buf_size;
  size_t rsize;
  unsigned char *buf;
  int ret;

  ret = key_from_sexp (&s,
                       sig->sexp,
                       "sig-val",
                       "s");
  if (0 != ret)
    ret = key_from_sexp (&s,
                         sig->sexp,
                         "rsa",
                         "s");
  GNUNET_assert (0 == ret);
  gcry_mpi_print (GCRYMPI_FMT_USG,
                  NULL,
                  0,
                  &buf_size,
                  s);
  buf = GNUNET_malloc (buf_size);
  GNUNET_assert (0 ==
                 gcry_mpi_print (GCRYMPI_FMT_USG,
                                 buf,
                                 buf_size,
                                 &rsize,
                                 s));
  GNUNET_assert (rsize == buf_size);
  *buffer = (void *) buf;
  gcry_mpi_release (s);
  return buf_size;
}


struct GNUNET_CRYPTO_RsaSignature *
GNUNET_CRYPTO_rsa_signature_decode (const void *buf,
                                    size_t buf_size)
{
  struct GNUNET_CRYPTO_RsaSignature *sig;
  gcry_mpi_t s;
  gcry_sexp_t data;

  if (0 !=
      gcry_mpi_scan (&s,
                     GCRYMPI_FMT_USG,
                     buf,
                     buf_size,
                     NULL))
  {
    GNUNET_break_op (0);
    return NULL;
  }

  if (0 !=
      gcry_sexp_build (&data,
                       NULL,
                       "(sig-val(rsa(s %M)))",
                       s))
  {
    GNUNET_break (0);
    gcry_mpi_release (s);
    return NULL;
  }
  gcry_mpi_release (s);
  sig = GNUNET_new (struct GNUNET_CRYPTO_RsaSignature);
  sig->sexp = data;
  return sig;
}


struct GNUNET_CRYPTO_RsaPublicKey *
GNUNET_CRYPTO_rsa_public_key_dup (const struct GNUNET_CRYPTO_RsaPublicKey *key)
{
  struct GNUNET_CRYPTO_RsaPublicKey *dup;
  gcry_sexp_t dup_sexp;
  size_t erroff;

  /* check if we really are exporting a public key */
  dup_sexp = gcry_sexp_find_token (key->sexp, "public-key", 0);
  GNUNET_assert (NULL != dup_sexp);
  gcry_sexp_release (dup_sexp);
  /* copy the sexp */
  GNUNET_assert (0 == gcry_sexp_build (&dup_sexp, &erroff, "%S", key->sexp));
  dup = GNUNET_new (struct GNUNET_CRYPTO_RsaPublicKey);
  dup->sexp = dup_sexp;
  return dup;
}


struct GNUNET_CRYPTO_RsaSignature *
GNUNET_CRYPTO_rsa_unblind (const struct GNUNET_CRYPTO_RsaSignature *sig,
                           const struct GNUNET_CRYPTO_RsaBlindingKeySecret *bks,
                           struct GNUNET_CRYPTO_RsaPublicKey *pkey)
{
  struct RsaBlindingKey *bkey;
  gcry_mpi_t n;
  gcry_mpi_t s;
  gcry_mpi_t r_inv;
  gcry_mpi_t ubsig;
  int ret;
  struct GNUNET_CRYPTO_RsaSignature *sret;

  BENCHMARK_START (rsa_unblind);

  ret = key_from_sexp (&n, pkey->sexp, "public-key", "n");
  if (0 != ret)
    ret = key_from_sexp (&n, pkey->sexp, "rsa", "n");
  if (0 != ret)
  {
    GNUNET_break_op (0);
    return NULL;
  }
  ret = key_from_sexp (&s, sig->sexp, "sig-val", "s");
  if (0 != ret)
    ret = key_from_sexp (&s, sig->sexp, "rsa", "s");
  if (0 != ret)
  {
    gcry_mpi_release (n);
    GNUNET_break_op (0);
    return NULL;
  }

  bkey = rsa_blinding_key_derive (pkey, bks);
  if (NULL == bkey)
  {
    /* RSA key is malicious since rsa_gcd_validate failed here.
     * It should have failed during GNUNET_CRYPTO_rsa_blind too though,
     * so the exchange is being malicious in an unfamilair way, maybe
     * just trying to crash us.  */
    GNUNET_break_op (0);
    gcry_mpi_release (n);
    gcry_mpi_release (s);
    return NULL;
  }

  r_inv = gcry_mpi_new (0);
  if (1 !=
      gcry_mpi_invm (r_inv,
                     bkey->r,
                     n))
  {
    /* We cannot find r mod n, so gcd(r,n) != 1, which should get *
    * caught above, but we handle it the same here.              */
    GNUNET_break_op (0);
    gcry_mpi_release (r_inv);
    rsa_blinding_key_free (bkey);
    gcry_mpi_release (n);
    gcry_mpi_release (s);
    return NULL;
  }

  ubsig = gcry_mpi_new (0);
  gcry_mpi_mulm (ubsig, s, r_inv, n);
  gcry_mpi_release (n);
  gcry_mpi_release (r_inv);
  gcry_mpi_release (s);
  rsa_blinding_key_free (bkey);

  sret = GNUNET_new (struct GNUNET_CRYPTO_RsaSignature);
  GNUNET_assert (0 ==
                 gcry_sexp_build (&sret->sexp,
                                  NULL,
                                  "(sig-val (rsa (s %M)))",
                                  ubsig));
  gcry_mpi_release (ubsig);
  BENCHMARK_END (rsa_unblind);
  return sret;
}


enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_rsa_verify (const struct GNUNET_HashCode *hash,
                          const struct GNUNET_CRYPTO_RsaSignature *sig,
                          const struct GNUNET_CRYPTO_RsaPublicKey *pkey)
{
  gcry_sexp_t data;
  gcry_mpi_t r;
  int rc;

  BENCHMARK_START (rsa_verify);

  r = rsa_full_domain_hash (pkey, hash);
  if (NULL == r)
  {
    GNUNET_break_op (0);
    /* RSA key is malicious since rsa_gcd_validate failed here.
     * It should have failed during GNUNET_CRYPTO_rsa_blind too though,
     * so the exchange is being malicious in an unfamilair way, maybe
     * just trying to crash us.  Arguably, we've only an internal error
     * though because we should've detected this in our previous call
     * to GNUNET_CRYPTO_rsa_unblind. *///
    return GNUNET_NO;
  }

  data = mpi_to_sexp (r);
  gcry_mpi_release (r);

  rc = gcry_pk_verify (sig->sexp,
                       data,
                       pkey->sexp);
  gcry_sexp_release (data);
  if (0 != rc)
  {
    LOG (GNUNET_ERROR_TYPE_WARNING,
         _ ("RSA signature verification failed at %s:%d: %s\n"),
         __FILE__,
         __LINE__,
         gcry_strerror (rc));
    BENCHMARK_END (rsa_verify);
    return GNUNET_SYSERR;
  }
  BENCHMARK_END (rsa_verify);
  return GNUNET_OK;
}


struct GNUNET_CRYPTO_RsaPrivateKey *
GNUNET_CRYPTO_rsa_private_key_dup (
  const struct GNUNET_CRYPTO_RsaPrivateKey *key)
{
  struct GNUNET_CRYPTO_RsaPrivateKey *dup;
  gcry_sexp_t dup_sexp;
  size_t erroff;

  /* check if we really are exporting a private key */
  dup_sexp = gcry_sexp_find_token (key->sexp, "private-key", 0);
  GNUNET_assert (NULL != dup_sexp);
  gcry_sexp_release (dup_sexp);
  /* copy the sexp */
  GNUNET_assert (0 == gcry_sexp_build (&dup_sexp, &erroff, "%S", key->sexp));
  dup = GNUNET_new (struct GNUNET_CRYPTO_RsaPrivateKey);
  dup->sexp = dup_sexp;
  return dup;
}


struct GNUNET_CRYPTO_RsaSignature *
GNUNET_CRYPTO_rsa_signature_dup (const struct GNUNET_CRYPTO_RsaSignature *sig)
{
  struct GNUNET_CRYPTO_RsaSignature *dup;
  gcry_sexp_t dup_sexp;
  size_t erroff;
  gcry_mpi_t s;
  int ret;

  /* verify that this is an RSA signature */
  ret = key_from_sexp (&s, sig->sexp, "sig-val", "s");
  if (0 != ret)
    ret = key_from_sexp (&s, sig->sexp, "rsa", "s");
  GNUNET_assert (0 == ret);
  gcry_mpi_release (s);
  /* copy the sexp */
  GNUNET_assert (0 == gcry_sexp_build (&dup_sexp, &erroff, "%S", sig->sexp));
  dup = GNUNET_new (struct GNUNET_CRYPTO_RsaSignature);
  dup->sexp = dup_sexp;
  return dup;
}


/* end of util/rsa.c */
