Merge branch 'master' of ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / util / crypto_rsa.c
index 9aad9c910a077ec9bbf099ca1b2de931e19dce35..7a108c21b179c33cf6cdb6ca1007babdd8721f41 100644 (file)
@@ -1,6 +1,6 @@
 /*
   This file is part of GNUnet
-  (C) 2014 Christian Grothoff (and other contributing authors)
+  Copyright (C) 2014,2016 GNUnet e.V.
 
   GNUnet is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
  * @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_util_lib.h"
+#include "gnunet_crypto_lib.h"
 
-#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+#define LOG(kind,...) GNUNET_log_from (kind, "util-crypto-rsa", __VA_ARGS__)
 
 
 /**
  * The private information of an RSA key pair.
  */
-struct GNUNET_CRYPTO_rsa_PrivateKey
+struct GNUNET_CRYPTO_RsaPrivateKey
 {
   /**
    * Libgcrypt S-expression for the RSA private key.
@@ -42,7 +43,7 @@ struct GNUNET_CRYPTO_rsa_PrivateKey
 /**
  * The public information of an RSA key pair.
  */
-struct GNUNET_CRYPTO_rsa_PublicKey
+struct GNUNET_CRYPTO_RsaPublicKey
 {
   /**
    * Libgcrypt S-expression for the RSA public key.
@@ -54,7 +55,7 @@ struct GNUNET_CRYPTO_rsa_PublicKey
 /**
  * @brief an RSA signature
  */
-struct GNUNET_CRYPTO_rsa_Signature
+struct GNUNET_CRYPTO_RsaSignature
 {
   /**
    * Libgcrypt S-expression for the RSA signature.
@@ -66,7 +67,7 @@ struct GNUNET_CRYPTO_rsa_Signature
 /**
  * @brief RSA blinding key
  */
-struct GNUNET_CRYPTO_rsa_BlindingKey
+struct RsaBlindingKey
 {
   /**
    * Random value used for blinding.
@@ -140,10 +141,10 @@ key_from_sexp (gcry_mpi_t *array,
  * @param len length of the key in bits (i.e. 2048)
  * @return fresh private key
  */
-struct GNUNET_CRYPTO_rsa_PrivateKey *
+struct GNUNET_CRYPTO_RsaPrivateKey *
 GNUNET_CRYPTO_rsa_private_key_create (unsigned int len)
 {
-  struct GNUNET_CRYPTO_rsa_PrivateKey *ret;
+  struct GNUNET_CRYPTO_RsaPrivateKey *ret;
   gcry_sexp_t s_key;
   gcry_sexp_t s_keyparam;
 
@@ -160,7 +161,7 @@ GNUNET_CRYPTO_rsa_private_key_create (unsigned int len)
   GNUNET_assert (0 ==
                  gcry_pk_testkey (s_key));
 #endif
-  ret = GNUNET_new (struct GNUNET_CRYPTO_rsa_PrivateKey);
+  ret = GNUNET_new (struct GNUNET_CRYPTO_RsaPrivateKey);
   ret->sexp = s_key;
   return ret;
 }
@@ -172,7 +173,7 @@ GNUNET_CRYPTO_rsa_private_key_create (unsigned int len)
  * @param key pointer to the memory to free
  */
 void
-GNUNET_CRYPTO_rsa_private_key_free (struct GNUNET_CRYPTO_rsa_PrivateKey *key)
+GNUNET_CRYPTO_rsa_private_key_free (struct GNUNET_CRYPTO_RsaPrivateKey *key)
 {
   gcry_sexp_release (key->sexp);
   GNUNET_free (key);
@@ -188,8 +189,8 @@ GNUNET_CRYPTO_rsa_private_key_free (struct GNUNET_CRYPTO_rsa_PrivateKey *key)
  * @return size of memory allocated in @a buffer
  */
 size_t
-GNUNET_CRYPTO_rsa_private_key_encode (const struct GNUNET_CRYPTO_rsa_PrivateKey *key,
-                              char **buffer)
+GNUNET_CRYPTO_rsa_private_key_encode (const struct GNUNET_CRYPTO_RsaPrivateKey *key,
+                                      char **buffer)
 {
   size_t n;
   char *b;
@@ -217,24 +218,30 @@ GNUNET_CRYPTO_rsa_private_key_encode (const struct GNUNET_CRYPTO_rsa_PrivateKey
  * @param len the length of the data in @a buf
  * @return NULL on error
  */
-struct GNUNET_CRYPTO_rsa_PrivateKey *
+struct GNUNET_CRYPTO_RsaPrivateKey *
 GNUNET_CRYPTO_rsa_private_key_decode (const char *buf,
-                              size_t len)
+                                      size_t len)
 {
-  struct GNUNET_CRYPTO_rsa_PrivateKey *key;
-
-  key = GNUNET_new (struct GNUNET_CRYPTO_rsa_PrivateKey);
+  struct GNUNET_CRYPTO_RsaPrivateKey *key;
+  key = GNUNET_new (struct GNUNET_CRYPTO_RsaPrivateKey);
   if (0 !=
       gcry_sexp_new (&key->sexp,
                      buf,
                      len,
                      0))
   {
-    GNUNET_break_op (0);
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "Decoded private key is not valid\n");
     GNUNET_free (key);
     return NULL;
   }
-  /* FIXME: verify that this is an RSA private key */
+  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;
 }
 
@@ -245,10 +252,10 @@ GNUNET_CRYPTO_rsa_private_key_decode (const char *buf,
  * @param priv the private key
  * @retur NULL on error, otherwise the public key
  */
-struct GNUNET_CRYPTO_rsa_PublicKey *
-GNUNET_CRYPTO_rsa_private_key_get_public (const struct GNUNET_CRYPTO_rsa_PrivateKey *priv)
+struct GNUNET_CRYPTO_RsaPublicKey *
+GNUNET_CRYPTO_rsa_private_key_get_public (const struct GNUNET_CRYPTO_RsaPrivateKey *priv)
 {
-  struct GNUNET_CRYPTO_rsa_PublicKey *pub;
+  struct GNUNET_CRYPTO_RsaPublicKey *pub;
   gcry_mpi_t ne[2];
   int rc;
   gcry_sexp_t result;
@@ -270,7 +277,7 @@ GNUNET_CRYPTO_rsa_private_key_get_public (const struct GNUNET_CRYPTO_rsa_Private
                         ne[1]);
   gcry_mpi_release (ne[0]);
   gcry_mpi_release (ne[1]);
-  pub = GNUNET_new (struct GNUNET_CRYPTO_rsa_PublicKey);
+  pub = GNUNET_new (struct GNUNET_CRYPTO_RsaPublicKey);
   pub->sexp = result;
   return pub;
 }
@@ -282,7 +289,7 @@ GNUNET_CRYPTO_rsa_private_key_get_public (const struct GNUNET_CRYPTO_rsa_Private
  * @param key pointer to the memory to free
  */
 void
-GNUNET_CRYPTO_rsa_public_key_free (struct GNUNET_CRYPTO_rsa_PublicKey *key)
+GNUNET_CRYPTO_rsa_public_key_free (struct GNUNET_CRYPTO_RsaPublicKey *key)
 {
   gcry_sexp_release (key->sexp);
   GNUNET_free (key);
@@ -298,8 +305,8 @@ GNUNET_CRYPTO_rsa_public_key_free (struct GNUNET_CRYPTO_rsa_PublicKey *key)
  * @return size of memory allocated in @a buffer
  */
 size_t
-GNUNET_CRYPTO_rsa_public_key_encode (const struct GNUNET_CRYPTO_rsa_PublicKey *key,
-                             char **buffer)
+GNUNET_CRYPTO_rsa_public_key_encode (const struct GNUNET_CRYPTO_RsaPublicKey *key,
+                                     char **buffer)
 {
   size_t n;
   char *b;
@@ -319,6 +326,28 @@ GNUNET_CRYPTO_rsa_public_key_encode (const struct GNUNET_CRYPTO_rsa_PublicKey *k
 }
 
 
+/**
+ * Compute hash over the public key.
+ *
+ * @param key public key to hash
+ * @param hc where to store the hash code
+ */
+void
+GNUNET_CRYPTO_rsa_public_key_hash (const struct GNUNET_CRYPTO_RsaPublicKey *key,
+                                   struct GNUNET_HashCode *hc)
+{
+  char *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);
+}
+
+
 /**
  * Decode the public key from the data-format back
  * to the "normal", internal format.
@@ -327,15 +356,15 @@ GNUNET_CRYPTO_rsa_public_key_encode (const struct GNUNET_CRYPTO_rsa_PublicKey *k
  * @param len the length of the data in @a buf
  * @return NULL on error
  */
-struct GNUNET_CRYPTO_rsa_PublicKey *
+struct GNUNET_CRYPTO_RsaPublicKey *
 GNUNET_CRYPTO_rsa_public_key_decode (const char *buf,
-                             size_t len)
+                                     size_t len)
 {
-  struct GNUNET_CRYPTO_rsa_PublicKey *key;
+  struct GNUNET_CRYPTO_RsaPublicKey *key;
   gcry_mpi_t n;
   int ret;
 
-  key = GNUNET_new (struct GNUNET_CRYPTO_rsa_PublicKey);
+  key = GNUNET_new (struct GNUNET_CRYPTO_RsaPublicKey);
   if (0 !=
       gcry_sexp_new (&key->sexp,
                      buf,
@@ -363,33 +392,240 @@ GNUNET_CRYPTO_rsa_public_key_decode (const char *buf,
 }
 
 
+/**
+ * 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 (i.e. 2048)
- * @return the newly created blinding key
+ * @param bks pre-secret to use to derive the blinding key
+ * @return the newly created blinding key, NULL if RSA key is malicious
  */
-struct GNUNET_CRYPTO_rsa_BlindingKey *
-GNUNET_CRYPTO_rsa_blinding_key_create (unsigned int len)
+static struct RsaBlindingKey *
+rsa_blinding_key_derive (const struct GNUNET_CRYPTO_RsaPublicKey *pkey,
+                        const struct GNUNET_CRYPTO_RsaBlindingKeySecret *bks)
 {
-  struct GNUNET_CRYPTO_rsa_BlindingKey *blind;
+  char *xts = "Blinding KDF extrator HMAC key";  /* Trusts bks' randomness more */
+  struct RsaBlindingKey *blind;
+  gcry_mpi_t n;
+
+  blind = GNUNET_new (struct RsaBlindingKey);
+  GNUNET_assert( NULL != blind );
+
+  /* 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))  {
+    GNUNET_free (blind);
+    blind = NULL;
+  }
 
-  blind = GNUNET_new (struct GNUNET_CRYPTO_rsa_BlindingKey);
-  blind->r = gcry_mpi_new (len);
-  gcry_mpi_randomize (blind->r,
-                      len,
-                      GCRY_STRONG_RANDOM);
+  gcry_mpi_release (n);
   return blind;
 }
 
 
+/*
+We originally added GNUNET_CRYPTO_kdf_mod_mpi for the benifit 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.
+*/
+
+
+/**
+ * Compare the values of two signatures.
+ *
+ * @param s1 one signature
+ * @param s2 the other signature
+ * @return 0 if the two are equal
+ */
+int
+GNUNET_CRYPTO_rsa_signature_cmp (struct GNUNET_CRYPTO_RsaSignature *s1,
+                                struct GNUNET_CRYPTO_RsaSignature *s2)
+{
+  char *b1;
+  char *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;
+}
+
+
+/**
+ * Compare the values of two public keys.
+ *
+ * @param p1 one public key
+ * @param p2 the other public key
+ * @return 0 if the two are equal
+ */
+int
+GNUNET_CRYPTO_rsa_public_key_cmp (struct GNUNET_CRYPTO_RsaPublicKey *p1,
+                                 struct GNUNET_CRYPTO_RsaPublicKey *p2)
+{
+  char *b1;
+  char *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;
+}
+
+
+/**
+ * Compare the values of two private keys.
+ *
+ * @param p1 one private key
+ * @param p2 the other private key
+ * @return 0 if the two are equal
+ */
+int
+GNUNET_CRYPTO_rsa_private_key_cmp (struct GNUNET_CRYPTO_RsaPrivateKey *p1,
+                                   struct GNUNET_CRYPTO_RsaPrivateKey *p2)
+{
+  char *b1;
+  char *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;
+}
+
+
+/**
+ * Obtain the length of the RSA key in bits.
+ *
+ * @param key the public key to introspect
+ * @return length of the key in bits
+ */
+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
  */
-void
-GNUNET_CRYPTO_rsa_blinding_key_free (struct GNUNET_CRYPTO_rsa_BlindingKey *bkey)
+static void
+rsa_blinding_key_free (struct RsaBlindingKey *bkey)
 {
   gcry_mpi_release (bkey->r);
   GNUNET_free (bkey);
@@ -397,16 +633,15 @@ GNUNET_CRYPTO_rsa_blinding_key_free (struct GNUNET_CRYPTO_rsa_BlindingKey *bkey)
 
 
 /**
- * Encode the blinding key in a format suitable for
- * storing it into a file.
+ * Print an MPI to a newly created buffer
  *
- * @param bkey the blinding key
- * @param[out] buffer set to a buffer with the encoded key
- * @return size of memory allocated in @a buffer
+ * @param v MPI to print.
+ * @param[out] newly allocated buffer containing the result
+ * @return number of bytes stored in @a buffer
  */
-size_t
-GNUNET_CRYPTO_rsa_blinding_key_encode (const struct GNUNET_CRYPTO_rsa_BlindingKey *bkey,
-                               char **buffer)
+static size_t
+numeric_mpi_alloc_n_print (gcry_mpi_t v,
+                          char **buffer)
 {
   size_t n;
   char *b;
@@ -416,47 +651,64 @@ GNUNET_CRYPTO_rsa_blinding_key_encode (const struct GNUNET_CRYPTO_rsa_BlindingKe
                   NULL,
                   0,
                   &n,
-                  bkey->r);
+                  v);
   b = GNUNET_malloc (n);
   GNUNET_assert (0 ==
                  gcry_mpi_print (GCRYMPI_FMT_USG,
                                  (unsigned char *) b,
                                  n,
                                  &rsize,
-                                 bkey->r));
+                                 v));
   *buffer = b;
   return n;
 }
 
 
 /**
- * Decode the blinding key from the data-format back
- * to the "normal", internal format.
+ * 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 buf the buffer where the public key data is stored
- * @param len the length of the data in @a buf
- * @return NULL on error
+ * @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
  */
-struct GNUNET_CRYPTO_rsa_BlindingKey *
-GNUNET_CRYPTO_rsa_blinding_key_decode (const char *buf,
-                               size_t len)
+static gcry_mpi_t
+rsa_full_domain_hash (const struct GNUNET_CRYPTO_RsaPublicKey *pkey,
+                      const struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_CRYPTO_rsa_BlindingKey *bkey;
-  size_t rsize;
-
-  bkey = GNUNET_new (struct GNUNET_CRYPTO_rsa_BlindingKey);
-  if (0 !=
-      gcry_mpi_scan (&bkey->r,
-                     GCRYMPI_FMT_USG,
-                     (const unsigned char *) buf,
-                     len,
-                     &rsize))
-  {
-    GNUNET_break_op (0);
-    GNUNET_free (bkey);
-    return NULL;
-  }
-  return bkey;
+  gcry_mpi_t r,n;
+  char *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;
 }
 
 
@@ -466,46 +718,45 @@ GNUNET_CRYPTO_rsa_blinding_key_decode (const char *buf,
  * @param hash hash of the message to sign
  * @param bkey the blinding key
  * @param pkey the public key of the signer
- * @param[out] buffer set to a buffer with the blinded message to be signed
- * @return number of bytes stored in @a buffer
+ * @param[out] buf set to a buffer with the blinded message to be signed
+ * @param[out] buf_size number of bytes stored in @a buf
+ * @return GNUNET_YES if successful, GNUNET_NO if RSA key is malicious
  */
-size_t
+int
 GNUNET_CRYPTO_rsa_blind (const struct GNUNET_HashCode *hash,
-                 struct GNUNET_CRYPTO_rsa_BlindingKey *bkey,
-                 struct GNUNET_CRYPTO_rsa_PublicKey *pkey,
-                 char **buffer)
+                         const struct GNUNET_CRYPTO_RsaBlindingKeySecret *bks,
+                         struct GNUNET_CRYPTO_RsaPublicKey *pkey,
+                         char **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;
-  size_t rsize;
-  size_t n;
-  gcry_error_t rc;
-  char *b;
   int ret;
 
+  GNUNET_assert (buf != NULL && 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);
-    *buffer = NULL;
+    *buf = NULL;
+    *buf_size = 0;
     return 0;
   }
-  if (0 != (rc = gcry_mpi_scan (&data,
-                                GCRYMPI_FMT_USG,
-                                (const unsigned char *) hash,
-                                sizeof (struct GNUNET_HashCode),
-                                &rsize)))
-  {
-    GNUNET_break (0);
-    gcry_mpi_release (ne[0]);
-    gcry_mpi_release (ne[1]);
-    *buffer = NULL;
-    return 0;
+
+  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,
@@ -516,100 +767,170 @@ GNUNET_CRYPTO_rsa_blind (const struct GNUNET_HashCode *hash,
                  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);
 
-  gcry_mpi_print (GCRYMPI_FMT_USG,
-                  NULL,
-                  0,
-                  &n,
-                  data_r_e);
-  b = GNUNET_malloc (n);
-  rc = gcry_mpi_print (GCRYMPI_FMT_USG,
-                       (unsigned char *) b,
-                       n,
-                       &rsize,
-                       data_r_e);
+  *buf_size = numeric_mpi_alloc_n_print (data_r_e, buf);
   gcry_mpi_release (data_r_e);
-  *buffer = b;
-  return n;
+  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 the data specified in the given purpose argument to an
- * S-expression suitable for signature operations.
+ * Convert an MPI to an S-expression suitable for signature operations.
  *
- * @param ptr pointer to the data to convert
- * @param size the size of the data
+ * @param value pointer to the data to convert
  * @return converted s-expression
  */
 static gcry_sexp_t
-data_to_sexp (const void *ptr, size_t size)
+mpi_to_sexp (gcry_mpi_t value)
 {
-  gcry_mpi_t value;
-  gcry_sexp_t data;
+  gcry_sexp_t data = NULL;
 
-  value = NULL;
-  data = NULL;
-  GNUNET_assert (0 ==
-                 gcry_mpi_scan (&value,
-                                GCRYMPI_FMT_USG,
-                                ptr,
-                                size,
-                                NULL));
   GNUNET_assert (0 ==
                  gcry_sexp_build (&data,
                                   NULL,
                                   "(data (flags raw) (value %M))",
                                   value));
-  gcry_mpi_release (value);
   return data;
 }
 
 
 /**
- * Sign the given message.
+ * Sign the given MPI.
  *
  * @param key private key to use for the signing
- * @param msg the message to sign
- * @param msg_len number of bytes in @a msg to sign
+ * @param value the MPI to sign
  * @return NULL on error, signature on success
  */
-struct GNUNET_CRYPTO_rsa_Signature *
-GNUNET_CRYPTO_rsa_sign (const struct GNUNET_CRYPTO_rsa_PrivateKey *key,
-                const void *msg,
-                size_t msg_len)
+static struct GNUNET_CRYPTO_RsaSignature *
+rsa_sign_mpi (const struct GNUNET_CRYPTO_RsaPrivateKey *key,
+              gcry_mpi_t value)
 {
-  struct GNUNET_CRYPTO_rsa_Signature *sig;
-  gcry_sexp_t result;
+  struct GNUNET_CRYPTO_RsaSignature *sig;
   gcry_sexp_t data;
+  gcry_sexp_t result;
+  int rc;
+
+  data = mpi_to_sexp (value);
 
-  data = data_to_sexp (msg,
-                       msg_len);
   if (0 !=
-      gcry_pk_sign (&result,
-                    data,
-                    key->sexp))
+      (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));
     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_rsa_Signature);
+  sig = GNUNET_new (struct GNUNET_CRYPTO_RsaSignature);
   sig->sexp = result;
   return sig;
 }
 
 
+/**
+ * Sign a blinded value, which must be a full domain hash of a message.
+ *
+ * @param key private key to use for the signing
+ * @param msg the message to sign
+ * @param msg_len number of bytes in @a msg to sign
+ * @return NULL on error, signature on success
+ */
+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;
+
+  GNUNET_assert (0 ==
+                 gcry_mpi_scan (&v,
+                                GCRYMPI_FMT_USG,
+                                msg,
+                                msg_len,
+                                NULL));
+
+  sig = rsa_sign_mpi (key, v);
+  gcry_mpi_release (v);
+  return sig;
+}
+
+
+/**
+ * Create and sign a full domain hash of a message.
+ *
+ * @param key private key to use for the signing
+ * @param hash the hash of the message to sign
+ * @return NULL on error, including a malicious RSA key, signature on success
+ */
+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;
+}
+
+
 /**
  * Free memory occupied by signature.
  *
  * @param sig memory to freee
  */
 void
-GNUNET_CRYPTO_rsa_signature_free (struct GNUNET_CRYPTO_rsa_Signature *sig)
+GNUNET_CRYPTO_rsa_signature_free (struct GNUNET_CRYPTO_RsaSignature *sig)
 {
   gcry_sexp_release (sig->sexp);
   GNUNET_free (sig);
@@ -617,16 +938,15 @@ GNUNET_CRYPTO_rsa_signature_free (struct GNUNET_CRYPTO_rsa_Signature *sig)
 
 
 /**
- * Encode the signature key in a format suitable for
- * storing it into a file.
+ * Encode the given signature in a format suitable for storing it into a file.
  *
  * @param sig the signature
  * @param[out] buffer set to a buffer with the encoded key
  * @return size of memory allocated in @a buffer
  */
 size_t
-GNUNET_CRYPTO_rsa_signature_encode (const struct GNUNET_CRYPTO_rsa_Signature *sig,
-                            char **buffer)
+GNUNET_CRYPTO_rsa_signature_encode (const struct GNUNET_CRYPTO_RsaSignature *sig,
+                                   char **buffer)
 {
   size_t n;
   char *b;
@@ -647,22 +967,22 @@ GNUNET_CRYPTO_rsa_signature_encode (const struct GNUNET_CRYPTO_rsa_Signature *si
 
 
 /**
- * Decode the public key from the data-format back
- * to the "normal", internal format.
+ * Decode the signature from the data-format back to the "normal", internal
+ * format.
  *
  * @param buf the buffer where the public key data is stored
  * @param len the length of the data in @a buf
  * @return NULL on error
  */
-struct GNUNET_CRYPTO_rsa_Signature *
+struct GNUNET_CRYPTO_RsaSignature *
 GNUNET_CRYPTO_rsa_signature_decode (const char *buf,
-                            size_t len)
+                                    size_t len)
 {
-  struct GNUNET_CRYPTO_rsa_Signature *sig;
+  struct GNUNET_CRYPTO_RsaSignature *sig;
   int ret;
   gcry_mpi_t s;
 
-  sig = GNUNET_new (struct GNUNET_CRYPTO_rsa_Signature);
+  sig = GNUNET_new (struct GNUNET_CRYPTO_RsaSignature);
   if (0 !=
       gcry_sexp_new (&sig->sexp,
                      buf,
@@ -691,26 +1011,52 @@ GNUNET_CRYPTO_rsa_signature_decode (const char *buf,
 
 
 /**
- * Unblind a signature made on blinding signature purpose.  The signature
- * purpose should have been generated with #GNUNET_CRYPTO_rsa_sign() using
- * a message that was generated with #GNUNET_CRYPTO_rsa_blind().
+ * Duplicate the given public key
+ *
+ * @param key the public key to duplicate
+ * @return the duplicate key; NULL upon error
+ */
+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;
+}
+
+
+/**
+ * Unblind a blind-signed signature.  The signature should have been generated
+ * with #GNUNET_CRYPTO_rsa_sign() using a hash that was blinded with
+ * #GNUNET_CRYPTO_rsa_blind().
  *
  * @param sig the signature made on the blinded signature purpose
- * @param bkey the blinding key used to blind the signature purpose
+ * @param bks the blinding key secret used to blind the signature purpose
  * @param pkey the public key of the signer
- * @return unblinded signature on success, NULL on error
+ * @return unblinded signature on success, NULL if RSA key is bad or malicious.
  */
-struct GNUNET_CRYPTO_rsa_Signature *
-GNUNET_CRYPTO_rsa_unblind (struct GNUNET_CRYPTO_rsa_Signature *sig,
-                   struct GNUNET_CRYPTO_rsa_BlindingKey *bkey,
-                   struct GNUNET_CRYPTO_rsa_PublicKey *pkey)
+struct GNUNET_CRYPTO_RsaSignature *
+GNUNET_CRYPTO_rsa_unblind (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_rsa_Signature *sret;
+  struct GNUNET_CRYPTO_RsaSignature *sret;
 
   ret = key_from_sexp (&n, pkey->sexp, "public-key", "n");
   if (0 != ret)
@@ -729,25 +1075,44 @@ GNUNET_CRYPTO_rsa_unblind (struct GNUNET_CRYPTO_rsa_Signature *sig,
     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 (n);
     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_rsa_Signature);
+  sret = GNUNET_new (struct GNUNET_CRYPTO_RsaSignature);
   GNUNET_assert (0 ==
                  gcry_sexp_build (&sret->sexp,
                                   NULL,
@@ -759,26 +1124,41 @@ GNUNET_CRYPTO_rsa_unblind (struct GNUNET_CRYPTO_rsa_Signature *sig,
 
 
 /**
- * Verify signature with the given hash.
+ * Verify whether the given hash corresponds to the given signature and
+ * the signature is valid with respect to the given public key.
  *
  * @param hash hash of the message to verify to match the @a sig
  * @param sig signature that is being validated
- * @param public_key public key of the signer
- * @returns #GNUNET_OK if ok, #GNUNET_SYSERR if invalid
+ * @param pkey public key of the signer
+ * @returns #GNUNET_YES if ok, #GNUNET_NO if RSA key is malicious, #GNUNET_SYSERR if signature is invalid
  */
 int
 GNUNET_CRYPTO_rsa_verify (const struct GNUNET_HashCode *hash,
-                  const struct GNUNET_CRYPTO_rsa_Signature *sig,
-                  const struct GNUNET_CRYPTO_rsa_PublicKey *public_key)
+                          const struct GNUNET_CRYPTO_RsaSignature *sig,
+                          const struct GNUNET_CRYPTO_RsaPublicKey *pkey)
 {
   gcry_sexp_t data;
+  gcry_mpi_t r;
   int rc;
 
-  data = data_to_sexp (hash,
-                       sizeof (struct GNUNET_HashCode));
+  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,
-                       public_key->sexp);
+                       pkey->sexp);
   gcry_sexp_release (data);
   if (0 != rc)
   {
@@ -793,4 +1173,58 @@ GNUNET_CRYPTO_rsa_verify (const struct GNUNET_HashCode *hash,
 }
 
 
+/**
+ * Duplicate the given private key
+ *
+ * @param key the private key to duplicate
+ * @return the duplicate key; NULL upon error
+ */
+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;
+}
+
+
+/**
+ * Duplicate the given private key
+ *
+ * @param key the private key to duplicate
+ * @return the duplicate key; NULL upon error
+ */
+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 */