+
+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);