2 This file is part of GNUnet.
3 Copyright (C) 2012, 2016 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
17 * @file secretsharing/secretsharing_api.c
19 * @author Florian Dold
22 #include "gnunet_util_lib.h"
23 #include "gnunet_secretsharing_service.h"
24 #include "secretsharing.h"
28 #define LOG(kind,...) GNUNET_log_from (kind, "secretsharing-api",__VA_ARGS__)
31 * Session that will eventually establish a shared secred between
32 * the involved peers and allow encryption and cooperative decryption.
34 struct GNUNET_SECRETSHARING_Session
38 * Message queue for @e client.
40 struct GNUNET_MQ_Handle *mq;
43 * Called when the secret sharing is done.
45 GNUNET_SECRETSHARING_SecretReadyCallback secret_ready_cb;
48 * Closure for @e secret_ready_cb.
50 void *secret_ready_cls;
55 * Handle to cancel a cooperative decryption operation.
57 struct GNUNET_SECRETSHARING_DecryptionHandle
61 * Message queue for @e client.
63 struct GNUNET_MQ_Handle *mq;
66 * Called when the secret sharing is done.
68 GNUNET_SECRETSHARING_DecryptCallback decrypt_cb;
71 * Closure for @e decrypt_cb.
78 * The ElGamal prime field order as libgcrypt mpi.
79 * Initialized in #init_crypto_constants.
81 static gcry_mpi_t elgamal_q;
84 * Modulus of the prime field used for ElGamal.
85 * Initialized in #init_crypto_constants.
87 static gcry_mpi_t elgamal_p;
90 * Generator for prime field of order 'elgamal_q'.
91 * Initialized in #init_crypto_constants.
93 static gcry_mpi_t elgamal_g;
97 * Function to initialize #elgamal_q, #elgamal_p and #elgamal_g.
100 ensure_elgamal_initialized (void)
102 if (NULL != elgamal_q)
103 return; /* looks like crypto is already initialized */
105 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_q, GCRYMPI_FMT_HEX,
106 GNUNET_SECRETSHARING_ELGAMAL_Q_HEX, 0, NULL));
107 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_p, GCRYMPI_FMT_HEX,
108 GNUNET_SECRETSHARING_ELGAMAL_P_HEX, 0, NULL));
109 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_g, GCRYMPI_FMT_HEX,
110 GNUNET_SECRETSHARING_ELGAMAL_G_HEX, 0, NULL));
115 * Callback invoked when there is an error communicating with
116 * the service. Notifies the application about the error.
118 * @param cls the `struct GNUNET_SECRETSHARING_Session`
119 * @param error error code
122 handle_session_client_error (void *cls,
123 enum GNUNET_MQ_Error error)
125 struct GNUNET_SECRETSHARING_Session *s = cls;
127 s->secret_ready_cb (s->secret_ready_cls, NULL, NULL, 0, NULL);
128 GNUNET_SECRETSHARING_session_destroy (s);
133 * Callback invoked when there is an error communicating with
134 * the service. Notifies the application about the error.
136 * @param cls the `struct GNUNET_SECRETSHARING_DecryptionHandle`
137 * @param error error code
140 handle_decrypt_client_error (void *cls,
141 enum GNUNET_MQ_Error error)
143 struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
145 dh->decrypt_cb (dh->decrypt_cls, NULL);
146 GNUNET_SECRETSHARING_decrypt_cancel (dh);
151 * Handler invoked with the final result message from
152 * secret sharing. Decodes the message and passes the
153 * result to the application.
155 * @param cls the `struct GNUNET_SECRETSHARING_Session`
156 * @param m message with the result
159 check_secret_ready (void *cls,
160 const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
162 /* FIXME: actually check m is well-formed here! */
168 * Handler invoked with the final result message from
169 * secret sharing. Decodes the message and passes the
170 * result to the application.
172 * @param cls the `struct GNUNET_SECRETSHARING_Session`
173 * @param m message with the result
176 handle_secret_ready (void *cls,
177 const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
179 struct GNUNET_SECRETSHARING_Session *s = cls;
180 struct GNUNET_SECRETSHARING_Share *share;
183 LOG (GNUNET_ERROR_TYPE_DEBUG,
184 "Got secret ready message of size %u\n",
185 ntohs (m->header.size));
186 share_size = ntohs (m->header.size) - sizeof (struct GNUNET_SECRETSHARING_SecretReadyMessage);
188 share = GNUNET_SECRETSHARING_share_read (&m[1],
191 GNUNET_assert (NULL != share); // FIXME: this can fail!
192 // should have been checked in #check_secret_ready!
193 // FIXME: below we never check &m[1] is valid!
194 // FIXME: do we leak 'share' here?
195 s->secret_ready_cb (s->secret_ready_cls,
199 (const struct GNUNET_PeerIdentity *) &m[1]);
201 GNUNET_SECRETSHARING_session_destroy (s);
206 * Destroy a secret sharing session.
207 * The secret ready callback will not be called.
209 * @param s session to destroy
212 GNUNET_SECRETSHARING_session_destroy (struct GNUNET_SECRETSHARING_Session *s)
214 GNUNET_MQ_destroy (s->mq);
221 * Create a session that will eventually establish a shared secret
222 * with the other peers.
224 * @param cfg configuration to use
225 * @param num_peers number of peers in @a peers
226 * @param peers array of peers that we will share secrets with, can optionally contain the local peer
227 * @param session_id unique session id
228 * @param start When should all peers be available for sharing the secret?
229 * Random number generation can take place before the start time.
230 * @param deadline point in time where the session must be established; taken as hint
231 * by underlying consensus sessions
232 * @param threshold minimum number of peers that must cooperate to decrypt a value
233 * @param cb called when the secret has been established
234 * @param cls closure for @a cb
236 struct GNUNET_SECRETSHARING_Session *
237 GNUNET_SECRETSHARING_create_session (const struct GNUNET_CONFIGURATION_Handle *cfg,
238 unsigned int num_peers,
239 const struct GNUNET_PeerIdentity *peers,
240 const struct GNUNET_HashCode *session_id,
241 struct GNUNET_TIME_Absolute start,
242 struct GNUNET_TIME_Absolute deadline,
243 unsigned int threshold,
244 GNUNET_SECRETSHARING_SecretReadyCallback cb,
247 struct GNUNET_SECRETSHARING_Session *s
248 = GNUNET_new (struct GNUNET_SECRETSHARING_Session);
249 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
250 GNUNET_MQ_hd_var_size (secret_ready,
251 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY,
252 struct GNUNET_SECRETSHARING_SecretReadyMessage,
254 GNUNET_MQ_handler_end ()
256 struct GNUNET_MQ_Envelope *ev;
257 struct GNUNET_SECRETSHARING_CreateMessage *msg;
259 s->mq = GNUNET_CLIENT_connect (cfg,
262 &handle_session_client_error,
266 /* secretsharing not configured correctly */
271 s->secret_ready_cb = cb;
272 s->secret_ready_cls = cls;
273 ev = GNUNET_MQ_msg_extra (msg,
274 num_peers * sizeof (struct GNUNET_PeerIdentity),
275 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE);
277 msg->threshold = htons (threshold);
278 msg->num_peers = htons (num_peers);
279 msg->session_id = *session_id;
280 msg->start = GNUNET_TIME_absolute_hton (start);
281 msg->deadline = GNUNET_TIME_absolute_hton (deadline);
282 GNUNET_memcpy (&msg[1], peers, num_peers * sizeof (struct GNUNET_PeerIdentity));
284 GNUNET_MQ_send (s->mq, ev);
286 LOG (GNUNET_ERROR_TYPE_DEBUG,
287 "Secretsharing session created with %u peers\n",
294 handle_decrypt_done (void *cls,
295 const struct GNUNET_SECRETSHARING_DecryptResponseMessage *m)
297 struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
298 const struct GNUNET_SECRETSHARING_Plaintext *plaintext;
303 plaintext = (void *) &m->plaintext;
304 dh->decrypt_cb (dh->decrypt_cls, plaintext);
305 GNUNET_SECRETSHARING_decrypt_cancel (dh);
310 * Publish the given ciphertext for decryption. Once a sufficient (>=k) number of peers has
311 * published the same value, it will be decrypted.
313 * When the operation is canceled, the decrypt_cb is not called anymore, but the calling
314 * peer may already have irrevocably contributed his share for the decryption of the value.
316 * @param share our secret share to use for decryption
317 * @param ciphertext ciphertext to publish in order to decrypt it (if enough peers agree)
318 * @param decrypt_cb callback called once the decryption succeeded
319 * @param decrypt_cb_cls closure for @a decrypt_cb
320 * @return handle to cancel the operation
322 struct GNUNET_SECRETSHARING_DecryptionHandle *
323 GNUNET_SECRETSHARING_decrypt (const struct GNUNET_CONFIGURATION_Handle *cfg,
324 struct GNUNET_SECRETSHARING_Share *share,
325 const struct GNUNET_SECRETSHARING_Ciphertext *ciphertext,
326 struct GNUNET_TIME_Absolute start,
327 struct GNUNET_TIME_Absolute deadline,
328 GNUNET_SECRETSHARING_DecryptCallback decrypt_cb,
329 void *decrypt_cb_cls)
331 struct GNUNET_SECRETSHARING_DecryptionHandle *s
332 = GNUNET_new (struct GNUNET_SECRETSHARING_DecryptionHandle);
333 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
334 GNUNET_MQ_hd_fixed_size (decrypt_done,
335 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE,
336 struct GNUNET_SECRETSHARING_DecryptResponseMessage,
338 GNUNET_MQ_handler_end ()
340 struct GNUNET_MQ_Envelope *ev;
341 struct GNUNET_SECRETSHARING_DecryptRequestMessage *msg;
344 s->decrypt_cb = decrypt_cb;
345 s->decrypt_cls = decrypt_cb_cls;
346 s->mq = GNUNET_CLIENT_connect (cfg,
349 &handle_decrypt_client_error,
356 GNUNET_assert (GNUNET_OK ==
357 GNUNET_SECRETSHARING_share_write (share, NULL, 0,
360 ev = GNUNET_MQ_msg_extra (msg,
362 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT);
364 GNUNET_assert (GNUNET_OK ==
365 GNUNET_SECRETSHARING_share_write (share,
370 msg->start = GNUNET_TIME_absolute_hton (start);
371 msg->deadline = GNUNET_TIME_absolute_hton (deadline);
372 msg->ciphertext = *ciphertext;
374 GNUNET_MQ_send (s->mq, ev);
376 LOG (GNUNET_ERROR_TYPE_DEBUG,
377 "decrypt session created\n");
383 GNUNET_SECRETSHARING_plaintext_generate_i (struct GNUNET_SECRETSHARING_Plaintext *plaintext,
389 ensure_elgamal_initialized ();
391 GNUNET_assert (NULL != (x = gcry_mpi_new (0)));
393 negative = GNUNET_NO;
396 negative = GNUNET_YES;
397 exponent = -exponent;
400 gcry_mpi_set_ui (x, exponent);
402 gcry_mpi_powm (x, elgamal_g, x, elgamal_p);
404 if (GNUNET_YES == negative)
407 res = gcry_mpi_invm (x, x, elgamal_p);
409 return GNUNET_SYSERR;
412 GNUNET_CRYPTO_mpi_print_unsigned (plaintext,
413 sizeof (struct GNUNET_SECRETSHARING_Plaintext),
421 * Encrypt a value. This operation is executed locally, no communication is
424 * This is a helper function, encryption can be done soley with a session's public key
425 * and the crypto system parameters.
427 * @param public_key public key to use for decryption
428 * @param message message to encrypt
429 * @param message_size number of bytes in @a message
430 * @param result_ciphertext pointer to store the resulting ciphertext
431 * @return #GNUNET_YES on succes, #GNUNET_SYSERR if the message is invalid (invalid range)
434 GNUNET_SECRETSHARING_encrypt (const struct GNUNET_SECRETSHARING_PublicKey *public_key,
435 const struct GNUNET_SECRETSHARING_Plaintext *plaintext,
436 struct GNUNET_SECRETSHARING_Ciphertext *result_ciphertext)
442 /* plaintext message */
447 ensure_elgamal_initialized ();
449 GNUNET_assert (NULL != (h = gcry_mpi_new (0)));
450 GNUNET_assert (NULL != (y = gcry_mpi_new (0)));
451 GNUNET_assert (NULL != (tmp = gcry_mpi_new (0)));
453 GNUNET_CRYPTO_mpi_scan_unsigned (&h, public_key, sizeof *public_key);
454 GNUNET_CRYPTO_mpi_scan_unsigned (&m, plaintext, sizeof *plaintext);
456 // Randomize y such that 0 < y < elgamal_q.
457 // The '- 1' is necessary as bitlength(q) = bitlength(p) - 1.
460 gcry_mpi_randomize (y, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1, GCRY_WEAK_RANDOM);
461 } while ((gcry_mpi_cmp_ui (y, 0) == 0) || (gcry_mpi_cmp (y, elgamal_q) >= 0));
464 gcry_mpi_powm (tmp, elgamal_g, y, elgamal_p);
466 GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c1_bits,
467 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
470 gcry_mpi_powm (tmp, h, y, elgamal_p);
472 gcry_mpi_mulm (tmp, tmp, m, elgamal_p);
474 GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c2_bits,
475 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
482 * Cancel a decryption.
484 * The decrypt_cb is not called anymore, but the calling
485 * peer may already have irrevocably contributed his share for the decryption of the value.
487 * @param dh to cancel
490 GNUNET_SECRETSHARING_decrypt_cancel (struct GNUNET_SECRETSHARING_DecryptionHandle *dh)
492 GNUNET_MQ_destroy (dh->mq);
497 /* end of secretsharing_api.c */