update secretsharing to new MQ API
[oweals/gnunet.git] / src / secretsharing / secretsharing_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012, 2016 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, 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 m message with the result
170  */
171 static int
172 check_secret_ready (void *cls,
173                     const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
174 {
175   /* FIXME: actually check m is well-formed here! */
176   return GNUNET_OK;
177 }
178
179
180 /**
181  * Handler invoked with the final result message from
182  * secret sharing.  Decodes the message and passes the
183  * result to the application.
184  *
185  * @param cls the `struct GNUNET_SECRETSHARING_Session`
186  * @param m message with the result
187  */
188 static void
189 handle_secret_ready (void *cls,
190                      const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
191 {
192   struct GNUNET_SECRETSHARING_Session *s = cls;
193   struct GNUNET_SECRETSHARING_Share *share;
194   size_t share_size;
195
196   LOG (GNUNET_ERROR_TYPE_DEBUG,
197        "Got secret ready message of size %u\n",
198        ntohs (m->header.size));
199   share_size = ntohs (m->header.size) - sizeof (struct GNUNET_SECRETSHARING_SecretReadyMessage);
200
201   share = GNUNET_SECRETSHARING_share_read (&m[1],
202                                            share_size,
203                                            NULL);
204   GNUNET_assert (NULL != share); // FIXME: this can fail!
205   // should have been checked in #check_secret_ready!
206   // FIXME: below we never check &m[1] is valid!
207   // FIXME: do we leak 'share' here?
208   s->secret_ready_cb (s->secret_ready_cls,
209                       share, /* FIXME */
210                       &share->public_key,
211                       share->num_peers,
212                       (const struct GNUNET_PeerIdentity *) &m[1]);
213
214   GNUNET_SECRETSHARING_session_destroy (s);
215 }
216
217
218 /**
219  * Destroy a secret sharing session.
220  * The secret ready callback will not be called.
221  *
222  * @param s session to destroy
223  */
224 void
225 GNUNET_SECRETSHARING_session_destroy (struct GNUNET_SECRETSHARING_Session *s)
226 {
227   GNUNET_MQ_destroy (s->mq);
228   s->mq = NULL;
229   GNUNET_CLIENT_disconnect (s->client);
230   s->client = NULL;
231   GNUNET_free (s);
232 }
233
234
235 /**
236  * Create a session that will eventually establish a shared secret
237  * with the other peers.
238  *
239  * @param cfg configuration to use
240  * @param num_peers number of peers in @a peers
241  * @param peers array of peers that we will share secrets with, can optionally contain the local peer
242  * @param session_id unique session id
243  * @param start When should all peers be available for sharing the secret?
244  *              Random number generation can take place before the start time.
245  * @param deadline point in time where the session must be established; taken as hint
246  *                 by underlying consensus sessions
247  * @param threshold minimum number of peers that must cooperate to decrypt a value
248  * @param cb called when the secret has been established
249  * @param cls closure for @a cb
250  */
251 struct GNUNET_SECRETSHARING_Session *
252 GNUNET_SECRETSHARING_create_session (const struct GNUNET_CONFIGURATION_Handle *cfg,
253                                      unsigned int num_peers,
254                                      const struct GNUNET_PeerIdentity *peers,
255                                      const struct GNUNET_HashCode *session_id,
256                                      struct GNUNET_TIME_Absolute start,
257                                      struct GNUNET_TIME_Absolute deadline,
258                                      unsigned int threshold,
259                                      GNUNET_SECRETSHARING_SecretReadyCallback cb,
260                                      void *cls)
261 {
262   GNUNET_MQ_hd_var_size (secret_ready,
263                          GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY,
264                          struct GNUNET_SECRETSHARING_SecretReadyMessage);
265   struct GNUNET_SECRETSHARING_Session *s
266     = GNUNET_new (struct GNUNET_SECRETSHARING_Session);
267   struct GNUNET_MQ_MessageHandler mq_handlers[] = {
268     make_secret_ready_handler (s),
269     GNUNET_MQ_handler_end ()
270   };
271   struct GNUNET_MQ_Envelope *ev;
272   struct GNUNET_SECRETSHARING_CreateMessage *msg;
273
274   s->client = GNUNET_CLIENT_connect ("secretsharing", cfg);
275   if (NULL == s->client)
276   {
277     /* secretsharing not configured correctly */
278     GNUNET_break (0);
279     GNUNET_free (s);
280     return NULL;
281   }
282   s->secret_ready_cb = cb;
283   s->secret_ready_cls = cls;
284   s->mq = GNUNET_MQ_queue_for_connection_client (s->client, mq_handlers,
285                                                  &handle_session_client_error,
286                                                  s);
287   GNUNET_assert (NULL != s->mq);
288
289   ev = GNUNET_MQ_msg_extra (msg,
290                             num_peers * sizeof (struct GNUNET_PeerIdentity),
291                             GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE);
292
293   msg->threshold = htons (threshold);
294   msg->num_peers = htons (num_peers);
295   msg->session_id = *session_id;
296   msg->start = GNUNET_TIME_absolute_hton (start);
297   msg->deadline = GNUNET_TIME_absolute_hton (deadline);
298   memcpy (&msg[1], peers, num_peers * sizeof (struct GNUNET_PeerIdentity));
299
300   GNUNET_MQ_send (s->mq, ev);
301
302   LOG (GNUNET_ERROR_TYPE_DEBUG,
303        "Secretsharing session created with %u peers\n",
304        num_peers);
305   return s;
306 }
307
308
309 static void
310 handle_decrypt_done (void *cls,
311                      const struct GNUNET_SECRETSHARING_DecryptResponseMessage *m)
312 {
313   struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
314   const struct GNUNET_SECRETSHARING_Plaintext *plaintext;
315
316   if (m->success == 0)
317     plaintext = NULL;
318   else
319     plaintext = (void *) &m->plaintext;
320   dh->decrypt_cb (dh->decrypt_cls, plaintext);
321   GNUNET_SECRETSHARING_decrypt_cancel (dh);
322 }
323
324
325 /**
326  * Publish the given ciphertext for decryption.  Once a sufficient (>=k) number of peers has
327  * published the same value, it will be decrypted.
328  *
329  * When the operation is canceled, the decrypt_cb is not called anymore, but the calling
330  * peer may already have irrevocably contributed his share for the decryption of the value.
331  *
332  * @param share our secret share to use for decryption
333  * @param ciphertext ciphertext to publish in order to decrypt it (if enough peers agree)
334  * @param decrypt_cb callback called once the decryption succeeded
335  * @param decrypt_cb_cls closure for @a decrypt_cb
336  * @return handle to cancel the operation
337  */
338 struct GNUNET_SECRETSHARING_DecryptionHandle *
339 GNUNET_SECRETSHARING_decrypt (const struct GNUNET_CONFIGURATION_Handle *cfg,
340                               struct GNUNET_SECRETSHARING_Share *share,
341                               const struct GNUNET_SECRETSHARING_Ciphertext *ciphertext,
342                               struct GNUNET_TIME_Absolute start,
343                               struct GNUNET_TIME_Absolute deadline,
344                               GNUNET_SECRETSHARING_DecryptCallback decrypt_cb,
345                               void *decrypt_cb_cls)
346 {
347   GNUNET_MQ_hd_fixed_size (decrypt_done,
348                            GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE,
349                            struct GNUNET_SECRETSHARING_DecryptResponseMessage);
350   struct GNUNET_SECRETSHARING_DecryptionHandle *s
351     = GNUNET_new (struct GNUNET_SECRETSHARING_DecryptionHandle);
352   struct GNUNET_MQ_MessageHandler mq_handlers[] = {
353     make_decrypt_done_handler (s),
354     GNUNET_MQ_handler_end ()
355   };
356   struct GNUNET_MQ_Envelope *ev;
357   struct GNUNET_SECRETSHARING_DecryptRequestMessage *msg;
358   size_t share_size;
359
360   s->client = GNUNET_CLIENT_connect ("secretsharing", cfg);
361   s->decrypt_cb = decrypt_cb;
362   s->decrypt_cls = decrypt_cb_cls;
363   GNUNET_assert (NULL != s->client);
364
365   s->mq = GNUNET_MQ_queue_for_connection_client (s->client, mq_handlers,
366                                                  &handle_decrypt_client_error,
367                                                  s);
368   GNUNET_assert (NULL != s->mq);
369
370   GNUNET_assert (GNUNET_OK ==
371                  GNUNET_SECRETSHARING_share_write (share, NULL, 0,
372                                                    &share_size));
373
374   ev = GNUNET_MQ_msg_extra (msg,
375                             share_size,
376                             GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT);
377
378   GNUNET_assert (GNUNET_OK ==
379                  GNUNET_SECRETSHARING_share_write (share,
380                                                    &msg[1],
381                                                    share_size,
382                                                    NULL));
383
384   msg->start = GNUNET_TIME_absolute_hton (start);
385   msg->deadline = GNUNET_TIME_absolute_hton (deadline);
386   msg->ciphertext = *ciphertext;
387
388   GNUNET_MQ_send (s->mq, ev);
389
390   LOG (GNUNET_ERROR_TYPE_DEBUG,
391        "decrypt session created\n");
392   return s;
393 }
394
395
396 int
397 GNUNET_SECRETSHARING_plaintext_generate_i (struct GNUNET_SECRETSHARING_Plaintext *plaintext,
398                                            int64_t exponent)
399 {
400   int negative;
401   gcry_mpi_t x;
402
403   ensure_elgamal_initialized ();
404
405   GNUNET_assert (NULL != (x = gcry_mpi_new (0)));
406
407   negative = GNUNET_NO;
408   if (exponent < 0)
409   {
410     negative = GNUNET_YES;
411     exponent = -exponent;
412   }
413
414   gcry_mpi_set_ui (x, exponent);
415
416   gcry_mpi_powm (x, elgamal_g, x, elgamal_p);
417
418   if (GNUNET_YES == negative)
419   {
420     int res;
421     res = gcry_mpi_invm (x, x, elgamal_p);
422     if (0 == res)
423       return GNUNET_SYSERR;
424   }
425
426   GNUNET_CRYPTO_mpi_print_unsigned (plaintext,
427                                     sizeof (struct GNUNET_SECRETSHARING_Plaintext),
428                                     x);
429
430   return GNUNET_OK;
431 }
432
433
434 /**
435  * Encrypt a value.  This operation is executed locally, no communication is
436  * necessary.
437  *
438  * This is a helper function, encryption can be done soley with a session's public key
439  * and the crypto system parameters.
440  *
441  * @param public_key public key to use for decryption
442  * @param message message to encrypt
443  * @param message_size number of bytes in @a message
444  * @param result_ciphertext pointer to store the resulting ciphertext
445  * @return #GNUNET_YES on succes, #GNUNET_SYSERR if the message is invalid (invalid range)
446  */
447 int
448 GNUNET_SECRETSHARING_encrypt (const struct GNUNET_SECRETSHARING_PublicKey *public_key,
449                               const struct GNUNET_SECRETSHARING_Plaintext *plaintext,
450                               struct GNUNET_SECRETSHARING_Ciphertext *result_ciphertext)
451 {
452   /* pubkey */
453   gcry_mpi_t h;
454   /* nonce */
455   gcry_mpi_t y;
456   /* plaintext message */
457   gcry_mpi_t m;
458   /* temp value */
459   gcry_mpi_t tmp;
460
461   ensure_elgamal_initialized ();
462
463   GNUNET_assert (NULL != (h = gcry_mpi_new (0)));
464   GNUNET_assert (NULL != (y = gcry_mpi_new (0)));
465   GNUNET_assert (NULL != (tmp = gcry_mpi_new (0)));
466
467   GNUNET_CRYPTO_mpi_scan_unsigned (&h, public_key, sizeof *public_key);
468   GNUNET_CRYPTO_mpi_scan_unsigned (&m, plaintext, sizeof *plaintext);
469
470   // Randomize y such that 0 < y < elgamal_q.
471   // The '- 1' is necessary as bitlength(q) = bitlength(p) - 1.
472   do
473   {
474     gcry_mpi_randomize (y, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1, GCRY_WEAK_RANDOM);
475   } while ((gcry_mpi_cmp_ui (y, 0) == 0) || (gcry_mpi_cmp (y, elgamal_q) >= 0));
476
477   // tmp <- g^y
478   gcry_mpi_powm (tmp, elgamal_g, y, elgamal_p);
479   // write tmp to c1
480   GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c1_bits,
481                                     GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
482
483   // tmp <- h^y
484   gcry_mpi_powm (tmp, h, y, elgamal_p);
485   // tmp <- tmp * m
486   gcry_mpi_mulm (tmp, tmp, m, elgamal_p);
487   // write tmp to c2
488   GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c2_bits,
489                                     GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
490
491   return GNUNET_OK;
492 }
493
494
495 /**
496  * Cancel a decryption.
497  *
498  * The decrypt_cb is not called anymore, but the calling
499  * peer may already have irrevocably contributed his share for the decryption of the value.
500  *
501  * @param dh to cancel
502  */
503 void
504 GNUNET_SECRETSHARING_decrypt_cancel (struct GNUNET_SECRETSHARING_DecryptionHandle *dh)
505 {
506   GNUNET_MQ_destroy (dh->mq);
507   dh->mq = NULL;
508   GNUNET_CLIENT_disconnect (dh->client);
509   dh->client = NULL;
510   GNUNET_free (dh);
511 }
512
513 /* end of secretsharing_api.c */