/*
This file is part of GNUnet.
- (C) 2012 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2012, 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
You should have received a copy of the GNU General Public License
along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
*/
/**
#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__)
*/
struct GNUNET_SECRETSHARING_Session
{
- /**
- * Client connected to the secretsharing service.
- */
- struct GNUNET_CLIENT_Connection *client;
/**
- * Message queue for 'client'.
+ * Message queue for @e client.
*/
struct GNUNET_MQ_Handle *mq;
GNUNET_SECRETSHARING_SecretReadyCallback secret_ready_cb;
/**
- * Closure for 'secret_ready_cb'.
+ * Closure for @e secret_ready_cb.
*/
void *secret_ready_cls;
};
+/**
+ * Handle to cancel a cooperative decryption operation.
+ */
struct GNUNET_SECRETSHARING_DecryptionHandle
{
- /**
- * Client connected to the secretsharing service.
- */
- struct GNUNET_CLIENT_Connection *client;
/**
- * Message queue for 'client'.
+ * Message queue for @e client.
*/
struct GNUNET_MQ_Handle *mq;
GNUNET_SECRETSHARING_DecryptCallback decrypt_cb;
/**
- * Closure for 'decrypt_cb'.
+ * Closure for @e 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;
+
+
+/**
+ * Function to initialize #elgamal_q, #elgamal_p and #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));
+}
+
+
+/**
+ * Callback invoked when there is an error communicating with
+ * the service. Notifies the application about the error.
+ *
+ * @param cls the `struct GNUNET_SECRETSHARING_Session`
+ * @param error error code
+ */
static void
-handle_session_client_error (void *cls, enum GNUNET_MQ_Error error)
+handle_session_client_error (void *cls,
+ enum GNUNET_MQ_Error error)
{
struct GNUNET_SECRETSHARING_Session *s = cls;
s->secret_ready_cb (s->secret_ready_cls, NULL, NULL, 0, NULL);
+ GNUNET_SECRETSHARING_session_destroy (s);
}
+/**
+ * Callback invoked when there is an error communicating with
+ * the service. Notifies the application about the error.
+ *
+ * @param cls the `struct GNUNET_SECRETSHARING_DecryptionHandle`
+ * @param error error code
+ */
static void
-handle_decrypt_client_error (void *cls, enum GNUNET_MQ_Error error)
+handle_decrypt_client_error (void *cls,
+ enum GNUNET_MQ_Error error)
{
- GNUNET_assert (0);
+ struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
+
+ dh->decrypt_cb (dh->decrypt_cls, NULL);
+ GNUNET_SECRETSHARING_decrypt_cancel (dh);
}
+
+/**
+ * Handler invoked with the final result message from
+ * secret sharing. Decodes the message and passes the
+ * result to the application.
+ *
+ * @param cls the `struct GNUNET_SECRETSHARING_Session`
+ * @param m message with the result
+ */
+static int
+check_secret_ready (void *cls,
+ const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
+{
+ /* FIXME: actually check m is well-formed here! */
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handler invoked with the final result message from
+ * secret sharing. Decodes the message and passes the
+ * result to the application.
+ *
+ * @param cls the `struct GNUNET_SECRETSHARING_Session`
+ * @param m message with the result
+ */
static void
-handle_secret_ready (void *cls, const struct GNUNET_MessageHeader *msg)
+handle_secret_ready (void *cls,
+ const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
{
- struct GNUNET_SECRETSHARING_Session *session = cls;
+ struct GNUNET_SECRETSHARING_Session *s = cls;
struct GNUNET_SECRETSHARING_Share *share;
- const struct GNUNET_SECRETSHARING_SecretReadyMessage *m = (const void *) msg;
size_t share_size;
- share_size = ntohs (m->header.size) - sizeof *m;
-
- share = GNUNET_SECRETSHARING_share_read (&m[1], share_size, NULL);
-
- session->secret_ready_cb (session->secret_ready_cls,
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Got secret ready message of size %u\n",
+ ntohs (m->header.size));
+ share_size = ntohs (m->header.size) - sizeof (struct GNUNET_SECRETSHARING_SecretReadyMessage);
+
+ share = GNUNET_SECRETSHARING_share_read (&m[1],
+ share_size,
+ NULL);
+ GNUNET_assert (NULL != share); // FIXME: this can fail!
+ // should have been checked in #check_secret_ready!
+ // FIXME: below we never check &m[1] is valid!
+ // FIXME: do we leak 'share' here?
+ s->secret_ready_cb (s->secret_ready_cls,
share, /* FIXME */
&share->public_key,
share->num_peers,
- (struct GNUNET_PeerIdentity *) &m[1]);
+ (const struct GNUNET_PeerIdentity *) &m[1]);
+
+ GNUNET_SECRETSHARING_session_destroy (s);
+}
+
+/**
+ * Destroy a secret sharing session.
+ * The secret ready callback will not be called.
+ *
+ * @param s session to destroy
+ */
+void
+GNUNET_SECRETSHARING_session_destroy (struct GNUNET_SECRETSHARING_Session *s)
+{
+ GNUNET_MQ_destroy (s->mq);
+ s->mq = NULL;
+ GNUNET_free (s);
}
+/**
+ * Create a session that will eventually establish a shared secret
+ * with the other peers.
+ *
+ * @param cfg configuration to use
+ * @param num_peers number of peers in @a peers
+ * @param peers array of peers that we will share secrets with, can optionally contain the local peer
+ * @param session_id unique session id
+ * @param start When should all peers be available for sharing the secret?
+ * Random number generation can take place before the start time.
+ * @param deadline point in time where the session must be established; taken as hint
+ * by underlying consensus sessions
+ * @param threshold minimum number of peers that must cooperate to decrypt a value
+ * @param cb called when the secret has been established
+ * @param cls closure for @a cb
+ */
struct GNUNET_SECRETSHARING_Session *
GNUNET_SECRETSHARING_create_session (const struct GNUNET_CONFIGURATION_Handle *cfg,
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,
void *cls)
{
- struct GNUNET_SECRETSHARING_Session *s;
+ GNUNET_MQ_hd_var_size (secret_ready,
+ GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY,
+ struct GNUNET_SECRETSHARING_SecretReadyMessage);
+ struct GNUNET_SECRETSHARING_Session *s
+ = GNUNET_new (struct GNUNET_SECRETSHARING_Session);
+ struct GNUNET_MQ_MessageHandler mq_handlers[] = {
+ make_secret_ready_handler (s),
+ GNUNET_MQ_handler_end ()
+ };
struct GNUNET_MQ_Envelope *ev;
struct GNUNET_SECRETSHARING_CreateMessage *msg;
- static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
- {handle_secret_ready, GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY, 0},
- GNUNET_MQ_HANDLERS_END
- };
-
- s = GNUNET_new (struct GNUNET_SECRETSHARING_Session);
- s->client = GNUNET_CLIENT_connect ("secretsharing", cfg);
+ s->mq = GNUNET_CLIENT_connecT (cfg,
+ "secretsharing",
+ mq_handlers,
+ &handle_session_client_error,
+ s);
+ if (NULL == s->mq)
+ {
+ /* secretsharing not configured correctly */
+ GNUNET_break (0);
+ GNUNET_free (s);
+ return NULL;
+ }
s->secret_ready_cb = cb;
s->secret_ready_cls = cls;
- GNUNET_assert (NULL != s->client);
-
- s->mq = GNUNET_MQ_queue_for_connection_client (s->client, mq_handlers,
- handle_session_client_error, s);
- GNUNET_assert (NULL != s->mq);
-
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 with %u peers\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)
+handle_decrypt_done (void *cls,
+ const struct GNUNET_SECRETSHARING_DecryptResponseMessage *m)
{
- GNUNET_assert (0);
+ struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
+ 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);
}
* @return handle to cancel the operation
*/
struct GNUNET_SECRETSHARING_DecryptionHandle *
-GNUNET_SECRETSHARING_decrypt (struct GNUNET_CONFIGURATION_Handle *cfg,
+GNUNET_SECRETSHARING_decrypt (const struct GNUNET_CONFIGURATION_Handle *cfg,
struct GNUNET_SECRETSHARING_Share *share,
- struct GNUNET_SECRETSHARING_Ciphertext *ciphertext,
+ 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;
+ GNUNET_MQ_hd_fixed_size (decrypt_done,
+ GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE,
+ struct GNUNET_SECRETSHARING_DecryptResponseMessage);
+ struct GNUNET_SECRETSHARING_DecryptionHandle *s
+ = GNUNET_new (struct GNUNET_SECRETSHARING_DecryptionHandle);
+ struct GNUNET_MQ_MessageHandler mq_handlers[] = {
+ make_decrypt_done_handler (s),
+ GNUNET_MQ_handler_end ()
+ };
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));
+ s->mq = GNUNET_CLIENT_connecT (cfg,
+ "secretsharing",
+ mq_handlers,
+ &handle_decrypt_client_error,
+ s);
+ if (NULL == s->mq)
+ {
+ GNUNET_free (s);
+ return NULL;
+ }
+ 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));
+ 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");
+ 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;
+}
+
+
+/**
+ * Cancel a decryption.
+ *
+ * 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 dh to cancel
+ */
+void
+GNUNET_SECRETSHARING_decrypt_cancel (struct GNUNET_SECRETSHARING_DecryptionHandle *dh)
+{
+ GNUNET_MQ_destroy (dh->mq);
+ dh->mq = NULL;
+ GNUNET_free (dh);
+}
+
+/* end of secretsharing_api.c */