WIP commit of scalar product 2.0. It is unfinished and does not yet pass tests. This...
[oweals/gnunet.git] / src / secretsharing / secretsharing_api.c
index a44fa2a2fd09e0627bbbc7b5c16fd71479d5daab..6e347f6da9e44dfef058ccdc55ece3d67d4d213c 100644 (file)
 #include "gnunet_util_lib.h"
 #include "gnunet_secretsharing_service.h"
 #include "secretsharing.h"
+#include <gcrypt.h>
 
 
 #define LOG(kind,...) GNUNET_log_from (kind, "secretsharing-api",__VA_ARGS__)
 
-
-
 /**
  * Session that will eventually establish a shared secred between
  * the involved peers and allow encryption and cooperative decryption.
@@ -61,6 +60,64 @@ struct GNUNET_SECRETSHARING_Session
 };
 
 
+struct GNUNET_SECRETSHARING_DecryptionHandle
+{
+  /**
+   * Client connected to the secretsharing service.
+   */
+  struct GNUNET_CLIENT_Connection *client;
+
+  /**
+   * Message queue for 'client'.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * Called when the secret sharing is done.
+   */
+  GNUNET_SECRETSHARING_DecryptCallback decrypt_cb;
+
+  /**
+   * Closure for 'decrypt_cb'.
+   */
+  void *decrypt_cls;
+};
+
+
+/**
+ * The ElGamal prime field order as libgcrypt mpi.
+ * Initialized in #init_crypto_constants.
+ */
+static gcry_mpi_t elgamal_q;
+
+/**
+ * Modulus of the prime field used for ElGamal.
+ * Initialized in #init_crypto_constants.
+ */
+static gcry_mpi_t elgamal_p;
+
+/**
+ * Generator for prime field of order 'elgamal_q'.
+ * Initialized in #init_crypto_constants.
+ */
+static gcry_mpi_t elgamal_g;
+
+
+static void
+ensure_elgamal_initialized (void)
+{
+  if (NULL != elgamal_q)
+    return; /* looks like crypto is already initialized */
+
+  GNUNET_assert (0 == gcry_mpi_scan (&elgamal_q, GCRYMPI_FMT_HEX,
+                                     GNUNET_SECRETSHARING_ELGAMAL_Q_HEX, 0, NULL));
+  GNUNET_assert (0 == gcry_mpi_scan (&elgamal_p, GCRYMPI_FMT_HEX,
+                                     GNUNET_SECRETSHARING_ELGAMAL_P_HEX, 0, NULL));
+  GNUNET_assert (0 == gcry_mpi_scan (&elgamal_g, GCRYMPI_FMT_HEX,
+                                     GNUNET_SECRETSHARING_ELGAMAL_G_HEX, 0, NULL));
+}
+
+
 static void
 handle_session_client_error (void *cls, enum GNUNET_MQ_Error error)
 {
@@ -69,18 +126,49 @@ handle_session_client_error (void *cls, enum GNUNET_MQ_Error error)
   s->secret_ready_cb (s->secret_ready_cls, NULL, NULL, 0, NULL);
 }
 
+
+static void
+handle_decrypt_client_error (void *cls, enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
+  
+  dh->decrypt_cb (dh->decrypt_cls, NULL);
+}
+
+
 static void
 handle_secret_ready (void *cls, const struct GNUNET_MessageHeader *msg)
 {
-  struct GNUNET_SECRETSHARING_Session *s = cls;
+  struct GNUNET_SECRETSHARING_Session *session = cls;
+  struct GNUNET_SECRETSHARING_Share *share;
   const struct GNUNET_SECRETSHARING_SecretReadyMessage *m = (const void *) msg;
+  size_t share_size;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "got secret ready message of size %u\n",
+       ntohs (m->header.size));
+
+  share_size = ntohs (m->header.size) - sizeof *m;
+
+  share = GNUNET_SECRETSHARING_share_read (&m[1], share_size, NULL);
 
-  s->secret_ready_cb (s->secret_ready_cls,
-                      NULL,
-                      &m->public_key,
-                      ntohs (m->num_secret_peers),
+  session->secret_ready_cb (session->secret_ready_cls,
+                      share, /* FIXME */
+                      &share->public_key,
+                      share->num_peers,
                       (struct GNUNET_PeerIdentity *) &m[1]);
 
+  GNUNET_SECRETSHARING_session_destroy (session);
+}
+
+
+void
+GNUNET_SECRETSHARING_session_destroy (struct GNUNET_SECRETSHARING_Session *session)
+{
+  GNUNET_MQ_destroy (session->mq);
+  session->mq = NULL;
+  GNUNET_CLIENT_disconnect (session->client);
+  session->client = NULL;
+  GNUNET_free (session);
 }
 
 
@@ -89,6 +177,7 @@ GNUNET_SECRETSHARING_create_session (const struct GNUNET_CONFIGURATION_Handle *c
                                      unsigned int num_peers,
                                      const struct GNUNET_PeerIdentity *peers,
                                      const struct GNUNET_HashCode *session_id,
+                                     struct GNUNET_TIME_Absolute start,
                                      struct GNUNET_TIME_Absolute deadline,
                                      unsigned int threshold,
                                      GNUNET_SECRETSHARING_SecretReadyCallback cb,
@@ -113,13 +202,211 @@ GNUNET_SECRETSHARING_create_session (const struct GNUNET_CONFIGURATION_Handle *c
                                                    handle_session_client_error, s);
   GNUNET_assert (NULL != s->mq);
 
-  ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE);
+  ev = GNUNET_MQ_msg_extra (msg,
+                            num_peers * sizeof (struct GNUNET_PeerIdentity),
+                            GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE);
+
   msg->threshold = htons (threshold);
+  msg->num_peers = htons (num_peers);
+  msg->session_id = *session_id;
+  msg->start = GNUNET_TIME_absolute_hton (start);
+  msg->deadline = GNUNET_TIME_absolute_hton (deadline);
+  memcpy (&msg[1], peers, num_peers * sizeof (struct GNUNET_PeerIdentity));
+
   GNUNET_MQ_send (s->mq, ev);
 
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "secretsharing session created\n");
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "secretsharing session created with %u peers\n",
+       num_peers);
   return s;
+}
 
+
+static void
+handle_decrypt_done (void *cls, const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
+  const struct GNUNET_SECRETSHARING_DecryptResponseMessage *m =
+      (const void *) msg;
+
+  const struct GNUNET_SECRETSHARING_Plaintext *plaintext;
+
+  if (m->success == 0)
+    plaintext = NULL;
+  else
+    plaintext = (void *) &m->plaintext;
+
+  dh->decrypt_cb (dh->decrypt_cls, plaintext);
+
+  GNUNET_SECRETSHARING_decrypt_cancel (dh);
+}
+
+
+/**
+ * Publish the given ciphertext for decryption.  Once a sufficient (>=k) number of peers has
+ * published the same value, it will be decrypted.
+ *
+ * When the operation is canceled, the decrypt_cb is not called anymore, but the calling
+ * peer may already have irrevocably contributed his share for the decryption of the value.
+ *
+ * @param share our secret share to use for decryption
+ * @param ciphertext ciphertext to publish in order to decrypt it (if enough peers agree)
+ * @param decrypt_cb callback called once the decryption succeeded
+ * @param decrypt_cb_cls closure for @a decrypt_cb
+ * @return handle to cancel the operation
+ */
+struct GNUNET_SECRETSHARING_DecryptionHandle *
+GNUNET_SECRETSHARING_decrypt (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                              struct GNUNET_SECRETSHARING_Share *share,
+                              const struct GNUNET_SECRETSHARING_Ciphertext *ciphertext,
+                              struct GNUNET_TIME_Absolute start,
+                              struct GNUNET_TIME_Absolute deadline,
+                              GNUNET_SECRETSHARING_DecryptCallback decrypt_cb,
+                              void *decrypt_cb_cls)
+{
+  struct GNUNET_SECRETSHARING_DecryptionHandle *s;
+  struct GNUNET_MQ_Envelope *ev;
+  struct GNUNET_SECRETSHARING_DecryptRequestMessage *msg;
+  static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
+    {handle_decrypt_done, GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE, 0},
+    GNUNET_MQ_HANDLERS_END
+  };
+  size_t share_size;
+
+
+  s = GNUNET_new (struct GNUNET_SECRETSHARING_DecryptionHandle);
+  s->client = GNUNET_CLIENT_connect ("secretsharing", cfg);
+  s->decrypt_cb = decrypt_cb;
+  s->decrypt_cls = decrypt_cb_cls;
+  GNUNET_assert (NULL != s->client);
+
+  s->mq = GNUNET_MQ_queue_for_connection_client (s->client, mq_handlers,
+                                                 handle_decrypt_client_error, s);
+  GNUNET_assert (NULL != s->mq);
+
+  GNUNET_assert (GNUNET_OK == GNUNET_SECRETSHARING_share_write (share, NULL, 0, &share_size));
+
+  ev = GNUNET_MQ_msg_extra (msg,
+                            share_size,
+                            GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT);
+
+  GNUNET_assert (GNUNET_OK == GNUNET_SECRETSHARING_share_write (share, &msg[1], share_size, NULL));
+
+  msg->start = GNUNET_TIME_absolute_hton (start);
+  msg->deadline = GNUNET_TIME_absolute_hton (deadline);
+  msg->ciphertext = *ciphertext;
+
+  GNUNET_MQ_send (s->mq, ev);
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "decrypt session created\n");
+  return s;
+}
+
+
+int
+GNUNET_SECRETSHARING_plaintext_generate_i (struct GNUNET_SECRETSHARING_Plaintext *plaintext,
+                                           int64_t exponent)
+{
+  int negative;
+  gcry_mpi_t x;
+
+  ensure_elgamal_initialized ();
+
+  GNUNET_assert (NULL != (x = gcry_mpi_new (0)));
+
+  negative = GNUNET_NO;
+  if (exponent < 0)
+  {
+    negative = GNUNET_YES;
+    exponent = -exponent;
+  }
+
+  gcry_mpi_set_ui (x, exponent);
+
+  gcry_mpi_powm (x, elgamal_g, x, elgamal_p);
+
+  if (GNUNET_YES == negative)
+  {
+    int res;
+    res = gcry_mpi_invm (x, x, elgamal_p);
+    if (0 == res)
+      return GNUNET_SYSERR;
+  }
+
+  GNUNET_CRYPTO_mpi_print_unsigned (plaintext, sizeof (struct GNUNET_SECRETSHARING_Plaintext), x);
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Encrypt a value.  This operation is executed locally, no communication is
+ * necessary.
+ *
+ * This is a helper function, encryption can be done soley with a session's public key
+ * and the crypto system parameters.
+ *
+ * @param public_key public key to use for decryption
+ * @param message message to encrypt
+ * @param message_size number of bytes in @a message
+ * @param result_ciphertext pointer to store the resulting ciphertext
+ * @return #GNUNET_YES on succes, #GNUNET_SYSERR if the message is invalid (invalid range)
+ */
+int
+GNUNET_SECRETSHARING_encrypt (const struct GNUNET_SECRETSHARING_PublicKey *public_key,
+                              const struct GNUNET_SECRETSHARING_Plaintext *plaintext,
+                              struct GNUNET_SECRETSHARING_Ciphertext *result_ciphertext)
+{
+  /* pubkey */
+  gcry_mpi_t h;
+  /* nonce */
+  gcry_mpi_t y;
+  /* plaintext message */
+  gcry_mpi_t m;
+  /* temp value */
+  gcry_mpi_t tmp;
+
+  ensure_elgamal_initialized ();
+
+  GNUNET_assert (NULL != (h = gcry_mpi_new (0)));
+  GNUNET_assert (NULL != (y = gcry_mpi_new (0)));
+  GNUNET_assert (NULL != (tmp = gcry_mpi_new (0)));
+
+  GNUNET_CRYPTO_mpi_scan_unsigned (&h, public_key, sizeof *public_key);
+  GNUNET_CRYPTO_mpi_scan_unsigned (&m, plaintext, sizeof *plaintext);
+
+  // Randomize y such that 0 < y < elgamal_q.
+  // The '- 1' is necessary as bitlength(q) = bitlength(p) - 1.
+  do 
+  {
+    gcry_mpi_randomize (y, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1, GCRY_WEAK_RANDOM);
+  } while ((gcry_mpi_cmp_ui (y, 0) == 0) || (gcry_mpi_cmp (y, elgamal_q) >= 0));
+
+  // tmp <- g^y
+  gcry_mpi_powm (tmp, elgamal_g, y, elgamal_p);
+  // write tmp to c1
+  GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c1_bits,
+                                    GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
+  
+  // tmp <- h^y
+  gcry_mpi_powm (tmp, h, y, elgamal_p);
+  // tmp <- tmp * m 
+  gcry_mpi_mulm (tmp, tmp, m, elgamal_p);
+  // write tmp to c2
+  GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c2_bits,
+                                    GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
+
+  return GNUNET_OK;
+}
+
+
+void
+GNUNET_SECRETSHARING_decrypt_cancel (struct GNUNET_SECRETSHARING_DecryptionHandle *h)
+{
+  GNUNET_MQ_destroy (h->mq);
+  h->mq = NULL;
+  GNUNET_CLIENT_disconnect (h->client);
+  h->client = NULL;
+  GNUNET_free (h);
 }