-improve UDP logging
[oweals/gnunet.git] / src / secretsharing / secretsharing_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file secretsharing/secretsharing_api.c
23  * @brief
24  * @author Florian Dold
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_secretsharing_service.h"
29 #include "secretsharing.h"
30 #include <gcrypt.h>
31
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "secretsharing-api",__VA_ARGS__)
34
35 /**
36  * Session that will eventually establish a shared secred between
37  * the involved peers and allow encryption and cooperative decryption.
38  */
39 struct GNUNET_SECRETSHARING_Session
40 {
41   /**
42    * Client connected to the secretsharing service.
43    */
44   struct GNUNET_CLIENT_Connection *client;
45
46   /**
47    * Message queue for @e client.
48    */
49   struct GNUNET_MQ_Handle *mq;
50
51   /**
52    * Called when the secret sharing is done.
53    */
54   GNUNET_SECRETSHARING_SecretReadyCallback secret_ready_cb;
55
56   /**
57    * Closure for @e secret_ready_cb.
58    */
59   void *secret_ready_cls;
60 };
61
62
63 /**
64  * Handle to cancel a cooperative decryption operation.
65  */
66 struct GNUNET_SECRETSHARING_DecryptionHandle
67 {
68   /**
69    * Client connected to the secretsharing service.
70    */
71   struct GNUNET_CLIENT_Connection *client;
72
73   /**
74    * Message queue for @e client.
75    */
76   struct GNUNET_MQ_Handle *mq;
77
78   /**
79    * Called when the secret sharing is done.
80    */
81   GNUNET_SECRETSHARING_DecryptCallback decrypt_cb;
82
83   /**
84    * Closure for @e decrypt_cb.
85    */
86   void *decrypt_cls;
87 };
88
89
90 /**
91  * The ElGamal prime field order as libgcrypt mpi.
92  * Initialized in #init_crypto_constants.
93  */
94 static gcry_mpi_t elgamal_q;
95
96 /**
97  * Modulus of the prime field used for ElGamal.
98  * Initialized in #init_crypto_constants.
99  */
100 static gcry_mpi_t elgamal_p;
101
102 /**
103  * Generator for prime field of order 'elgamal_q'.
104  * Initialized in #init_crypto_constants.
105  */
106 static gcry_mpi_t elgamal_g;
107
108
109 /**
110  * Function to initialize #elgamal_q, #elgamal_p and #elgamal_g.
111  */
112 static void
113 ensure_elgamal_initialized (void)
114 {
115   if (NULL != elgamal_q)
116     return; /* looks like crypto is already initialized */
117
118   GNUNET_assert (0 == gcry_mpi_scan (&elgamal_q, GCRYMPI_FMT_HEX,
119                                      GNUNET_SECRETSHARING_ELGAMAL_Q_HEX, 0, NULL));
120   GNUNET_assert (0 == gcry_mpi_scan (&elgamal_p, GCRYMPI_FMT_HEX,
121                                      GNUNET_SECRETSHARING_ELGAMAL_P_HEX, 0, NULL));
122   GNUNET_assert (0 == gcry_mpi_scan (&elgamal_g, GCRYMPI_FMT_HEX,
123                                      GNUNET_SECRETSHARING_ELGAMAL_G_HEX, 0, NULL));
124 }
125
126
127 /**
128  * Callback invoked when there is an error communicating with
129  * the service.  Notifies the application about the error.
130  *
131  * @param cls the `struct GNUNET_SECRETSHARING_Session`
132  * @param error error code
133  */
134 static void
135 handle_session_client_error (void *cls,
136                              enum GNUNET_MQ_Error error)
137 {
138   struct GNUNET_SECRETSHARING_Session *s = cls;
139
140   s->secret_ready_cb (s->secret_ready_cls, NULL, NULL, 0, NULL);
141   GNUNET_SECRETSHARING_session_destroy (s);
142 }
143
144
145 /**
146  * Callback invoked when there is an error communicating with
147  * the service.  Notifies the application about the error.
148  *
149  * @param cls the `struct GNUNET_SECRETSHARING_DecryptionHandle`
150  * @param error error code
151  */
152 static void
153 handle_decrypt_client_error (void *cls,
154                              enum GNUNET_MQ_Error error)
155 {
156   struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
157
158   dh->decrypt_cb (dh->decrypt_cls, NULL);
159   GNUNET_SECRETSHARING_decrypt_cancel (dh);
160 }
161
162
163 /**
164  * Handler invoked with the final result message from
165  * secret sharing.  Decodes the message and passes the
166  * result to the application.
167  *
168  * @param cls the `struct GNUNET_SECRETSHARING_Session`
169  * @param msg message with the result
170  */
171 static void
172 handle_secret_ready (void *cls,
173                      const struct GNUNET_MessageHeader *msg)
174 {
175   struct GNUNET_SECRETSHARING_Session *s = cls;
176   const struct GNUNET_SECRETSHARING_SecretReadyMessage *m;
177   struct GNUNET_SECRETSHARING_Share *share;
178   size_t share_size;
179
180   LOG (GNUNET_ERROR_TYPE_DEBUG,
181        "Got secret ready message of size %u\n",
182        ntohs (msg->size));
183   if (ntohs (msg->size) < sizeof (struct GNUNET_SECRETSHARING_SecretReadyMessage))
184   {
185     GNUNET_break (0);
186     s->secret_ready_cb (s->secret_ready_cls, NULL, NULL, 0, NULL);
187     GNUNET_SECRETSHARING_session_destroy (s);
188     return;
189   }
190   m = (const struct GNUNET_SECRETSHARING_SecretReadyMessage *) msg;
191   share_size = ntohs (m->header.size) - sizeof (struct GNUNET_SECRETSHARING_SecretReadyMessage);
192
193   share = GNUNET_SECRETSHARING_share_read (&m[1],
194                                            share_size,
195                                            NULL);
196   s->secret_ready_cb (s->secret_ready_cls,
197                       share, /* FIXME */
198                       &share->public_key,
199                       share->num_peers,
200                       (struct GNUNET_PeerIdentity *) &m[1]);
201   GNUNET_SECRETSHARING_session_destroy (s);
202 }
203
204
205 /**
206  * Destroy a secret sharing session.
207  * The secret ready callback will not be called.
208  *
209  * @param s session to destroy
210  */
211 void
212 GNUNET_SECRETSHARING_session_destroy (struct GNUNET_SECRETSHARING_Session *s)
213 {
214   GNUNET_MQ_destroy (s->mq);
215   s->mq = NULL;
216   GNUNET_CLIENT_disconnect (s->client);
217   s->client = NULL;
218   GNUNET_free (s);
219 }
220
221
222 /**
223  * Create a session that will eventually establish a shared secret
224  * with the other peers.
225  *
226  * @param cfg configuration to use
227  * @param num_peers number of peers in @a peers
228  * @param peers array of peers that we will share secrets with, can optionally contain the local peer
229  * @param session_id unique session id
230  * @param start When should all peers be available for sharing the secret?
231  *              Random number generation can take place before the start time.
232  * @param deadline point in time where the session must be established; taken as hint
233  *                 by underlying consensus sessions
234  * @param threshold minimum number of peers that must cooperate to decrypt a value
235  * @param cb called when the secret has been established
236  * @param cls closure for @a cb
237  */
238 struct GNUNET_SECRETSHARING_Session *
239 GNUNET_SECRETSHARING_create_session (const struct GNUNET_CONFIGURATION_Handle *cfg,
240                                      unsigned int num_peers,
241                                      const struct GNUNET_PeerIdentity *peers,
242                                      const struct GNUNET_HashCode *session_id,
243                                      struct GNUNET_TIME_Absolute start,
244                                      struct GNUNET_TIME_Absolute deadline,
245                                      unsigned int threshold,
246                                      GNUNET_SECRETSHARING_SecretReadyCallback cb,
247                                      void *cls)
248 {
249   struct GNUNET_SECRETSHARING_Session *s;
250   struct GNUNET_MQ_Envelope *ev;
251   struct GNUNET_SECRETSHARING_CreateMessage *msg;
252   static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
253     { &handle_secret_ready,
254       GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY, 0},
255     GNUNET_MQ_HANDLERS_END
256   };
257
258   s = GNUNET_new (struct GNUNET_SECRETSHARING_Session);
259   s->client = GNUNET_CLIENT_connect ("secretsharing", cfg);
260   if (NULL == s->client)
261   {
262     /* secretsharing not configured correctly */
263     GNUNET_break (0);
264     GNUNET_free (s);
265     return NULL;
266   }
267   s->secret_ready_cb = cb;
268   s->secret_ready_cls = cls;
269   s->mq = GNUNET_MQ_queue_for_connection_client (s->client, mq_handlers,
270                                                  &handle_session_client_error,
271                                                  s);
272   GNUNET_assert (NULL != s->mq);
273
274   ev = GNUNET_MQ_msg_extra (msg,
275                             num_peers * sizeof (struct GNUNET_PeerIdentity),
276                             GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE);
277
278   msg->threshold = htons (threshold);
279   msg->num_peers = htons (num_peers);
280   msg->session_id = *session_id;
281   msg->start = GNUNET_TIME_absolute_hton (start);
282   msg->deadline = GNUNET_TIME_absolute_hton (deadline);
283   memcpy (&msg[1], peers, num_peers * sizeof (struct GNUNET_PeerIdentity));
284
285   GNUNET_MQ_send (s->mq, ev);
286
287   LOG (GNUNET_ERROR_TYPE_DEBUG,
288        "Secretsharing session created with %u peers\n",
289        num_peers);
290   return s;
291 }
292
293
294 static void
295 handle_decrypt_done (void *cls,
296                      const struct GNUNET_MessageHeader *msg)
297 {
298   struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
299   const struct GNUNET_SECRETSHARING_DecryptResponseMessage *m =
300     (const void *) msg; // FIXME: size check!?
301   const struct GNUNET_SECRETSHARING_Plaintext *plaintext;
302
303   if (m->success == 0)
304     plaintext = NULL;
305   else
306     plaintext = (void *) &m->plaintext;
307
308   dh->decrypt_cb (dh->decrypt_cls, plaintext);
309
310   GNUNET_SECRETSHARING_decrypt_cancel (dh);
311 }
312
313
314 /**
315  * Publish the given ciphertext for decryption.  Once a sufficient (>=k) number of peers has
316  * published the same value, it will be decrypted.
317  *
318  * When the operation is canceled, the decrypt_cb is not called anymore, but the calling
319  * peer may already have irrevocably contributed his share for the decryption of the value.
320  *
321  * @param share our secret share to use for decryption
322  * @param ciphertext ciphertext to publish in order to decrypt it (if enough peers agree)
323  * @param decrypt_cb callback called once the decryption succeeded
324  * @param decrypt_cb_cls closure for @a decrypt_cb
325  * @return handle to cancel the operation
326  */
327 struct GNUNET_SECRETSHARING_DecryptionHandle *
328 GNUNET_SECRETSHARING_decrypt (const struct GNUNET_CONFIGURATION_Handle *cfg,
329                               struct GNUNET_SECRETSHARING_Share *share,
330                               const struct GNUNET_SECRETSHARING_Ciphertext *ciphertext,
331                               struct GNUNET_TIME_Absolute start,
332                               struct GNUNET_TIME_Absolute deadline,
333                               GNUNET_SECRETSHARING_DecryptCallback decrypt_cb,
334                               void *decrypt_cb_cls)
335 {
336   struct GNUNET_SECRETSHARING_DecryptionHandle *s;
337   struct GNUNET_MQ_Envelope *ev;
338   struct GNUNET_SECRETSHARING_DecryptRequestMessage *msg;
339   static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
340     {handle_decrypt_done, GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE, 0},
341     GNUNET_MQ_HANDLERS_END
342   };
343   size_t share_size;
344
345
346   s = GNUNET_new (struct GNUNET_SECRETSHARING_DecryptionHandle);
347   s->client = GNUNET_CLIENT_connect ("secretsharing", cfg);
348   s->decrypt_cb = decrypt_cb;
349   s->decrypt_cls = decrypt_cb_cls;
350   GNUNET_assert (NULL != s->client);
351
352   s->mq = GNUNET_MQ_queue_for_connection_client (s->client, mq_handlers,
353                                                  &handle_decrypt_client_error,
354                                                  s);
355   GNUNET_assert (NULL != s->mq);
356
357   GNUNET_assert (GNUNET_OK == GNUNET_SECRETSHARING_share_write (share, NULL, 0, &share_size));
358
359   ev = GNUNET_MQ_msg_extra (msg,
360                             share_size,
361                             GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT);
362
363   GNUNET_assert (GNUNET_OK == GNUNET_SECRETSHARING_share_write (share, &msg[1], share_size, NULL));
364
365   msg->start = GNUNET_TIME_absolute_hton (start);
366   msg->deadline = GNUNET_TIME_absolute_hton (deadline);
367   msg->ciphertext = *ciphertext;
368
369   GNUNET_MQ_send (s->mq, ev);
370
371   LOG (GNUNET_ERROR_TYPE_DEBUG, "decrypt session created\n");
372   return s;
373 }
374
375
376 int
377 GNUNET_SECRETSHARING_plaintext_generate_i (struct GNUNET_SECRETSHARING_Plaintext *plaintext,
378                                            int64_t exponent)
379 {
380   int negative;
381   gcry_mpi_t x;
382
383   ensure_elgamal_initialized ();
384
385   GNUNET_assert (NULL != (x = gcry_mpi_new (0)));
386
387   negative = GNUNET_NO;
388   if (exponent < 0)
389   {
390     negative = GNUNET_YES;
391     exponent = -exponent;
392   }
393
394   gcry_mpi_set_ui (x, exponent);
395
396   gcry_mpi_powm (x, elgamal_g, x, elgamal_p);
397
398   if (GNUNET_YES == negative)
399   {
400     int res;
401     res = gcry_mpi_invm (x, x, elgamal_p);
402     if (0 == res)
403       return GNUNET_SYSERR;
404   }
405
406   GNUNET_CRYPTO_mpi_print_unsigned (plaintext, sizeof (struct GNUNET_SECRETSHARING_Plaintext), x);
407
408   return GNUNET_OK;
409 }
410
411
412 /**
413  * Encrypt a value.  This operation is executed locally, no communication is
414  * necessary.
415  *
416  * This is a helper function, encryption can be done soley with a session's public key
417  * and the crypto system parameters.
418  *
419  * @param public_key public key to use for decryption
420  * @param message message to encrypt
421  * @param message_size number of bytes in @a message
422  * @param result_ciphertext pointer to store the resulting ciphertext
423  * @return #GNUNET_YES on succes, #GNUNET_SYSERR if the message is invalid (invalid range)
424  */
425 int
426 GNUNET_SECRETSHARING_encrypt (const struct GNUNET_SECRETSHARING_PublicKey *public_key,
427                               const struct GNUNET_SECRETSHARING_Plaintext *plaintext,
428                               struct GNUNET_SECRETSHARING_Ciphertext *result_ciphertext)
429 {
430   /* pubkey */
431   gcry_mpi_t h;
432   /* nonce */
433   gcry_mpi_t y;
434   /* plaintext message */
435   gcry_mpi_t m;
436   /* temp value */
437   gcry_mpi_t tmp;
438
439   ensure_elgamal_initialized ();
440
441   GNUNET_assert (NULL != (h = gcry_mpi_new (0)));
442   GNUNET_assert (NULL != (y = gcry_mpi_new (0)));
443   GNUNET_assert (NULL != (tmp = gcry_mpi_new (0)));
444
445   GNUNET_CRYPTO_mpi_scan_unsigned (&h, public_key, sizeof *public_key);
446   GNUNET_CRYPTO_mpi_scan_unsigned (&m, plaintext, sizeof *plaintext);
447
448   // Randomize y such that 0 < y < elgamal_q.
449   // The '- 1' is necessary as bitlength(q) = bitlength(p) - 1.
450   do
451   {
452     gcry_mpi_randomize (y, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1, GCRY_WEAK_RANDOM);
453   } while ((gcry_mpi_cmp_ui (y, 0) == 0) || (gcry_mpi_cmp (y, elgamal_q) >= 0));
454
455   // tmp <- g^y
456   gcry_mpi_powm (tmp, elgamal_g, y, elgamal_p);
457   // write tmp to c1
458   GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c1_bits,
459                                     GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
460
461   // tmp <- h^y
462   gcry_mpi_powm (tmp, h, y, elgamal_p);
463   // tmp <- tmp * m
464   gcry_mpi_mulm (tmp, tmp, m, elgamal_p);
465   // write tmp to c2
466   GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c2_bits,
467                                     GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
468
469   return GNUNET_OK;
470 }
471
472
473 /**
474  * Cancel a decryption.
475  *
476  * The decrypt_cb is not called anymore, but the calling
477  * peer may already have irrevocably contributed his share for the decryption of the value.
478  *
479  * @param dh to cancel
480  */
481 void
482 GNUNET_SECRETSHARING_decrypt_cancel (struct GNUNET_SECRETSHARING_DecryptionHandle *dh)
483 {
484   GNUNET_MQ_destroy (dh->mq);
485   dh->mq = NULL;
486   GNUNET_CLIENT_disconnect (dh->client);
487   dh->client = NULL;
488   GNUNET_free (dh);
489 }
490
491 /* end of secretsharing_api.c */