aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2020-12-04 12:25:58 +0100
committerFlorian Dold <florian@dold.me>2020-12-04 12:25:58 +0100
commit82813b337cf02ed2306c13a53893814f629bc2f3 (patch)
treea116e1435a09ed6ae4e7d5ab39882261b8b56d7e /src
parentd3b714922f2fecfeda09a9331a48ba58ab42857a (diff)
copy (un)blinding logic from GNUnet
Diffstat (limited to 'src')
-rw-r--r--src/util/crypto.c410
1 files changed, 407 insertions, 3 deletions
diff --git a/src/util/crypto.c b/src/util/crypto.c
index 1b829c9f3..dcff5fd22 100644
--- a/src/util/crypto.c
+++ b/src/util/crypto.c
@@ -36,7 +36,7 @@
* FIXME: Can we define some macro for this in configure.ac
* to detect the version?
*/
-#define USE_GNUNET_RSA_BLINDING 1
+#define USE_GNUNET_RSA_BLINDING 0
/**
@@ -394,6 +394,281 @@ TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc,
}
+#if ! USE_GNUNET_RSA_BLINDING
+
+
+/**
+ * The private information of an RSA key pair.
+ *
+ * FIXME: This declaration is evil, as it defines
+ * an opaque struct that is "owned" by GNUnet.
+ */
+struct GNUNET_CRYPTO_RsaPrivateKey
+{
+ /**
+ * Libgcrypt S-expression for the RSA private key.
+ */
+ gcry_sexp_t sexp;
+};
+
+
+/**
+ * The public information of an RSA key pair.
+ *
+ * FIXME: This declaration is evil, as it defines
+ * an opaque struct that is "owned" by GNUnet.
+ */
+struct GNUNET_CRYPTO_RsaPublicKey
+{
+ /**
+ * Libgcrypt S-expression for the RSA public key.
+ */
+ gcry_sexp_t sexp;
+};
+
+/**
+ * @brief an RSA signature
+ *
+ * FIXME: This declaration is evil, as it defines
+ * an opaque struct that is "owned" by GNUnet.
+ */
+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;
+};
+
+
+/**
+ * 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);
+}
+
+
+/**
+ * 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;
+}
+
+
+/**
+ * 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;
+}
+
+
+/**
+ * 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;
+}
+
+
+/**
+ * Create a blinding key
+ *
+ * @param len length of the key in bits (i.e. 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)
+{
+ char *xts = "Blinding KDF extractor 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;
+ }
+
+ gcry_mpi_release (n);
+ return blind;
+}
+
+
+/**
+ * Print an MPI to a newly created buffer
+ *
+ * @param v MPI to print.
+ * @param[out] 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;
+}
+
+
+#endif /* ! USE_GNUNET_RSA_BLINDING */
+
+
/**
* Blinds the given message with the given blinding key
*
@@ -418,7 +693,67 @@ TALER_rsa_blind (const struct GNUNET_HashCode *hash,
buf,
buf_size);
#else
-# error "FIXME: implement"
+ 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;
+
+ 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 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,
+ 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);
+
+ 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;
#endif
}
@@ -443,7 +778,76 @@ TALER_rsa_unblind (const struct GNUNET_CRYPTO_RsaSignature *sig,
bks,
pkey);
#else
-# error "FIXME: implement"
+ 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;
+
+ 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);
+ return sret;
#endif
}