gnunet-svn
[Top][All Lists]

## [GNUnet-SVN] r37214 - in gnunet/src: include util

 From: gnunet Subject: [GNUnet-SVN] r37214 - in gnunet/src: include util Date: Mon, 30 May 2016 17:54:56 +0200

```Author: burdges
Date: 2016-05-30 17:54:56 +0200 (Mon, 30 May 2016)
New Revision: 37214

Modified:
gnunet/src/include/gnunet_crypto_lib.h
gnunet/src/util/crypto_kdf.c
gnunet/src/util/crypto_rsa.c
gnunet/src/util/test_crypto_rsa.c
Log:
Use a uniform random number mod an RSA composites for both
the blinding factor and the full domain hash.

This resolves an attack against the blinding factor in Taler:

There was  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 fear 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.

I suspect we need a more uniform deterministic pseudo-random number
generator for blinding factors.  Just fyi, our old call to
gcry_mpi_randomize had this same problem.

I do not believe this caused a problem for the full domain hash, but
we can fix it easily enough anyways.

Modified: gnunet/src/include/gnunet_crypto_lib.h
===================================================================
--- gnunet/src/include/gnunet_crypto_lib.h      2016-05-30 15:54:38 UTC (rev
37213)
+++ gnunet/src/include/gnunet_crypto_lib.h      2016-05-30 15:54:56 UTC (rev
37214)
@@ -27,6 +27,7 @@
* @author Gerd Knorr <address@hidden>
* @author Ioana Patrascu
* @author Tzvetan Horozov
+ * @author Jeffrey Burdges <address@hidden>
*
* @defgroup crypto  Crypto library: cryptographic operations
* Provides cryptographic primitives.
@@ -1015,6 +1016,26 @@

/**
+ * Deterministically generate a pseudo-random number uniformly from the
+ * integers modulo a libgcrypt mpi.
+ *
+ * @param[out] r MPI value set to the FDH
+ * @param n MPI to work modulo
+ * @param xts salt
+ * @param xts_len length of @a xts
+ * @param skm source key material
+ * @param skm_len length of @a skm
+ * @param ctx context string
+ */
+void
+GNUNET_CRYPTO_kdf_mod_mpi (gcry_mpi_t *r,
+                           gcry_mpi_t n,
+                           const void *xts,  size_t xts_len,
+                           const void *skm,  size_t skm_len,
+                           const char *ctx);
+
+
+/**
* @ingroup hash
* @brief Derive key
* @param result buffer for the derived key, allocated by caller

Modified: gnunet/src/util/crypto_kdf.c
===================================================================
--- gnunet/src/util/crypto_kdf.c        2016-05-30 15:54:38 UTC (rev 37213)
+++ gnunet/src/util/crypto_kdf.c        2016-05-30 15:54:56 UTC (rev 37214)
@@ -22,6 +22,7 @@
* @file src/util/crypto_kdf.c
* @brief Key derivation
* @author Nils Durner
+ * @author Jeffrey Burdges <address@hidden>
*/

#include <gcrypt.h>
@@ -43,8 +44,9 @@
* @return #GNUNET_YES on success
*/
int
-GNUNET_CRYPTO_kdf_v (void *result, size_t out_len, const void *xts,
-                     size_t xts_len, const void *skm, size_t skm_len,
+GNUNET_CRYPTO_kdf_v (void *result, size_t out_len,
+                     const void *xts, size_t xts_len,
+                     const void *skm, size_t skm_len,
va_list argp)
{
/*
@@ -76,8 +78,9 @@
* @return #GNUNET_YES on success
*/
int
-GNUNET_CRYPTO_kdf (void *result, size_t out_len, const void *xts,
-                   size_t xts_len, const void *skm, size_t skm_len, ...)
+GNUNET_CRYPTO_kdf (void *result, size_t out_len,
+                   const void *xts, size_t xts_len,
+                   const void *skm, size_t skm_len, ...)
{
va_list argp;
int ret;
@@ -88,3 +91,60 @@

return ret;
}
+
+
+/**
+ * Deterministically generate a pseudo-random number uniformly from the
+ * integers modulo a libgcrypt mpi.
+ *
+ * @param[out] r MPI value set to the FDH
+ * @param n MPI to work modulo
+ * @param xts salt
+ * @param xts_len length of @a xts
+ * @param skm source key material
+ * @param skm_len length of @a skm
+ * @param ctx context string
+ */
+void
+GNUNET_CRYPTO_kdf_mod_mpi (gcry_mpi_t *r,
+                           gcry_mpi_t n,
+                           const void *xts,  size_t xts_len,
+                           const void *skm,  size_t skm_len,
+                           const char *ctx)
+{
+  gcry_error_t rc;
+  unsigned int nbits;
+  size_t rsize;
+  unsigned int ctr;
+
+  nbits = gcry_mpi_get_nbits (n);
+  /* GNUNET_assert (nbits > 512); */
+
+  ctr = 0;
+  do {
+    /* Ain't clear if n is always divisible by 8 */
+    uint8_t buf[ (nbits-1)/8 + 1 ];
+
+    rc = GNUNET_CRYPTO_kdf (buf,
+                            sizeof (buf),
+                            xts, xts_len,
+                            skm, skm_len,
+                            ctx, strlen(ctx),
+                            &ctr, sizeof(ctr),
+                            NULL, 0);
+    GNUNET_assert (GNUNET_YES == rc);
+
+    rc = gcry_mpi_scan (r,
+                        GCRYMPI_FMT_USG,
+                        (const unsigned char *) buf,
+                        sizeof (buf),
+                        &rsize);
+    GNUNET_assert (0 == rc);  /* Allocation erro? */
+
+    gcry_mpi_clear_highbit (*r, nbits);
+    GNUNET_assert( 0 == gcry_mpi_test_bit (*r, nbits) );
+    ++ctr;
+  } while ( 0 <= gcry_mpi_cmp(*r,n) );
+}
+
+

Modified: gnunet/src/util/crypto_rsa.c
===================================================================
--- gnunet/src/util/crypto_rsa.c        2016-05-30 15:54:38 UTC (rev 37213)
+++ gnunet/src/util/crypto_rsa.c        2016-05-30 15:54:56 UTC (rev 37214)
@@ -400,31 +400,24 @@
* @return the newly created blinding key
*/
static struct RsaBlindingKey *
-rsa_blinding_key_derive (unsigned int len,
+rsa_blinding_key_derive (const struct GNUNET_CRYPTO_RsaPublicKey *pkey,
const struct GNUNET_CRYPTO_RsaBlindingKeySecret *bks)
{
+  char *xts = "Blinding KDF extrator HMAC key";  /* Trusts bks' randomness
more */
struct RsaBlindingKey *blind;
-  uint8_t buf[len / 8];
-  int rc;
-  size_t rsize;
+  gcry_mpi_t n;

blind = GNUNET_new (struct RsaBlindingKey);
-  /* FIXME: #4483: actually derive key from bks! - Jeff,
-     check that you're happy with this!*/
-  GNUNET_assert (GNUNET_YES ==
-                GNUNET_CRYPTO_kdf (buf,
-                                   sizeof (buf),
-                                   "blinding-kdf",
-                                   strlen ("blinding-kdf"),
-                                   bks,
-                                   sizeof (*bks),
-                                   NULL, 0));
-  rc = gcry_mpi_scan (&blind->r,
-                     GCRYMPI_FMT_USG,
-                     (const unsigned char *) buf,
-                     sizeof (buf),
-                     &rsize);
-  GNUNET_assert (0 == rc);
+
+  /* Extract the composite n from the RSA public key */
+  GNUNET_assert( 0 == key_from_sexp (&n, pkey->sexp, "rsa", "n") );
+  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");
return blind;
}

@@ -538,13 +531,10 @@
GNUNET_CRYPTO_rsa_public_key_len (const struct GNUNET_CRYPTO_RsaPublicKey *key)
{
gcry_mpi_t n;
-  int ret;
unsigned int rval;

-  ret = key_from_sexp (&n, key->sexp, "rsa", "n");
-  if (0 != ret)
-  {
-    /* this is no public RSA key */
+  if (0 != key_from_sexp (&n, key->sexp, "rsa", "n"))
+  { /* Not an RSA public key */
GNUNET_break (0);
return 0;
}
@@ -610,91 +600,33 @@
* @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 libgcrypt error that to represent an allocation failure
*/
-/* FIXME: exported symbol without proper prefix... */
-gcry_error_t
+static void
rsa_full_domain_hash (gcry_mpi_t *r,
-                      const struct GNUNET_HashCode *hash,
-                      const struct GNUNET_CRYPTO_RsaPublicKey *pkey,
-                      size_t *rsize)
+                                    const struct GNUNET_HashCode *hash,
+                                    const struct GNUNET_CRYPTO_RsaPublicKey
*pkey)
{
-  unsigned int i;
-  unsigned int nbits;
-  unsigned int nhashes;
-  gcry_error_t rc;
-  char *buf;
-  size_t buf_len;
-  gcry_md_hd_t h;
-  gcry_md_hd_t h0;
-  struct GNUNET_HashCode *hs;
+  gcry_mpi_t n;
+  char *xts;
+  size_t xts_len;

-  /* Uncomment the following to debug without using the full domain hash */
-  /*
-  rc = gcry_mpi_scan (r,
-                      GCRYMPI_FMT_USG,
-                      (const unsigned char *)hash,
-                      sizeof(struct GNUNET_HashCode),
-                      rsize);
-  return rc;
-  */
+  /* Extract the composite n from the RSA public key */
+  GNUNET_assert( 0 == key_from_sexp (&n, pkey->sexp, "rsa", "n") );
+  GNUNET_assert( 0 == gcry_mpi_get_flag(n, GCRYMPI_FLAG_OPAQUE) );

-  nbits = GNUNET_CRYPTO_rsa_public_key_len (pkey);
-  if (nbits < 512)
-    nbits = 512;
+  /* 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);

-  /* Already almost an HMAC since we consume a hash, so no GCRY_MD_FLAG_HMAC.
*/
-  rc = gcry_md_open (&h, GCRY_MD_SHA512, 0);
-  if (0 != rc)
-    return rc;
+  GNUNET_CRYPTO_kdf_mod_mpi (r,
+                             n,
+                             xts,  xts_len,
+                             hash,  sizeof(*hash),
+                             "RSA-FDA FTpsW!");

-  /* We seed 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!              */
-  buf_len = GNUNET_CRYPTO_rsa_public_key_encode (pkey, &buf);
-  gcry_md_write (h, buf, buf_len);
-  GNUNET_free (buf);
-
-  nhashes = (nbits-1) / (8 * sizeof(struct GNUNET_HashCode)) + 1;
-  hs = GNUNET_new_array (nhashes,
-                         struct GNUNET_HashCode);
-  for (i=0; i<nhashes; i++)
-  {
-    gcry_md_write (h, hash, sizeof(struct GNUNET_HashCode));
-    rc = gcry_md_copy (&h0, h);
-    if (0 != rc)
-    {
-      gcry_md_close (h0);
-      break;
-    }
-    gcry_md_putc (h0, i % 256);
-    memcpy (&hs[i],
-            gcry_md_read (h0, GCRY_MD_SHA512),
-            sizeof(struct GNUNET_HashCode));
-    gcry_md_close (h0);
-  }
-  gcry_md_close (h);
-  if (0 != rc)
-  {
-    GNUNET_free (hs);
-    return rc;
-  }
-
-  rc = gcry_mpi_scan (r,
-                      GCRYMPI_FMT_USG,
-                      (const unsigned char *) hs,
-                      nhashes * sizeof(struct GNUNET_HashCode),
-                      rsize);
-  GNUNET_free (hs);
-  if (0 != rc)
-    return rc;
-
-  /* Do not allow *r to exceed n or signatures fail to verify unpredictably. *
-   * This happening with  gcry_mpi_clear_highbit (*r, nbits-1) so maybe      *
-   * gcry_mpi_clear_highbit is broken, but setting the highbit sounds good.  */
-  gcry_mpi_set_highbit (*r, nbits-2);
-  return rc;
+  GNUNET_free (xts);
}

@@ -718,11 +650,8 @@
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;
int ret;
-  unsigned int len;

ret = key_from_sexp (ne, pkey->sexp, "public-key", "ne");
if (0 != ret)
@@ -734,17 +663,8 @@
return 0;
}

-  rc = rsa_full_domain_hash (&data, hash, pkey, &rsize);
-  if (0 != rc)  /* Allocation error in libgcrypt */
-  {
-    GNUNET_break (0);
-    gcry_mpi_release (ne[0]);
-    gcry_mpi_release (ne[1]);
-    *buffer = NULL;
-    return 0;
-  }
-  len = GNUNET_CRYPTO_rsa_public_key_len (pkey);
-  bkey = rsa_blinding_key_derive (len,
+  rsa_full_domain_hash (&data, hash, pkey);
+  bkey = rsa_blinding_key_derive (pkey,
bks);
r_e = gcry_mpi_new (0);
gcry_mpi_powm (r_e,
@@ -886,13 +806,11 @@
{
struct GNUNET_CRYPTO_RsaPublicKey *pkey;
gcry_mpi_t v = NULL;
-  gcry_error_t rc;
struct GNUNET_CRYPTO_RsaSignature *sig;

pkey = GNUNET_CRYPTO_rsa_private_key_get_public (key);
-  rc = rsa_full_domain_hash (&v, hash, pkey, NULL);
+  rsa_full_domain_hash (&v, hash, pkey);
GNUNET_CRYPTO_rsa_public_key_free (pkey);
-  GNUNET_assert (0 == rc);

sig = rsa_sign_mpi (key, v);
gcry_mpi_release (v);
@@ -1034,7 +952,6 @@
gcry_mpi_t ubsig;
int ret;
struct GNUNET_CRYPTO_RsaSignature *sret;
-  unsigned int len;

ret = key_from_sexp (&n, pkey->sexp, "public-key", "n");
if (0 != ret)
@@ -1053,8 +970,7 @@
GNUNET_break_op (0);
return NULL;
}
-  len = GNUNET_CRYPTO_rsa_public_key_len (pkey);
-  bkey = rsa_blinding_key_derive (len,
+  bkey = rsa_blinding_key_derive (pkey,
bks);

r_inv = gcry_mpi_new (0);
@@ -1106,8 +1022,7 @@
gcry_mpi_t r;
int rc;

-  rc = rsa_full_domain_hash (&r, hash, pkey, NULL);
-  GNUNET_assert (0 == rc);  /* Allocation error in libgcrypt */
+  rsa_full_domain_hash (&r, hash, pkey);
data = mpi_to_sexp(r);
gcry_mpi_release (r);

Modified: gnunet/src/util/test_crypto_rsa.c
===================================================================
--- gnunet/src/util/test_crypto_rsa.c   2016-05-30 15:54:38 UTC (rev 37213)
+++ gnunet/src/util/test_crypto_rsa.c   2016-05-30 15:54:56 UTC (rev 37214)
@@ -18,6 +18,7 @@
* @file util/test_crypto_rsa.c
* @brief testcase for utility functions for RSA cryptography
* @author Sree Harsha Totakura <address@hidden>
+ * @author Jeffrey Burdges <address@hidden>
*/
#include "platform.h"
#include <gcrypt.h>
@@ -26,13 +27,6 @@
#define KEY_SIZE 1024

-gcry_error_t
-rsa_full_domain_hash (gcry_mpi_t *r,
-                      const struct GNUNET_HashCode *hash,
-                      const struct GNUNET_CRYPTO_RsaPublicKey *pkey,
-                      size_t *rsize);
-
-
int
main (int argc,
char *argv[])
@@ -50,7 +44,6 @@
struct GNUNET_HashCode hash;
char *blind_buf;
size_t bsize;
-  gcry_mpi_t v;

GNUNET_log_setup ("test-rsa", "WARNING", NULL);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
@@ -82,14 +75,6 @@
(void) fprintf (stderr, "The above warning is expected.\n");
GNUNET_free (enc);

-  /* test full domain hash size */
-  GNUNET_assert (0 == rsa_full_domain_hash (&v, &hash, pub, NULL));
-  GNUNET_assert (gcry_mpi_get_nbits(v) < KEY_SIZE);
-  gcry_mpi_clear_highbit (v, gcry_mpi_get_nbits(v)-1); /* clear the set high
bit */
-  GNUNET_assert (gcry_mpi_get_nbits(v) > 3*KEY_SIZE/4);
-  /* This test necessarily randomly fails with probability 2^(3 - KEY_SIZE/4)
*/
-  gcry_mpi_release(v);
-
/* try ordinary sig first */
sig = GNUNET_CRYPTO_rsa_sign_fdh (priv,
&hash);

```

reply via email to