error handling
[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 it
6      under the terms of the GNU Affero 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.
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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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    * Message queue for @e client.
43    */
44   struct GNUNET_MQ_Handle *mq;
45
46   /**
47    * Called when the secret sharing is done.
48    */
49   GNUNET_SECRETSHARING_SecretReadyCallback secret_ready_cb;
50
51   /**
52    * Closure for @e secret_ready_cb.
53    */
54   void *secret_ready_cls;
55 };
56
57
58 /**
59  * Handle to cancel a cooperative decryption operation.
60  */
61 struct GNUNET_SECRETSHARING_DecryptionHandle
62 {
63   /**
64    * Message queue for @e client.
65    */
66   struct GNUNET_MQ_Handle *mq;
67
68   /**
69    * Called when the secret sharing is done.
70    */
71   GNUNET_SECRETSHARING_DecryptCallback decrypt_cb;
72
73   /**
74    * Closure for @e decrypt_cb.
75    */
76   void *decrypt_cls;
77 };
78
79
80 /**
81  * The ElGamal prime field order as libgcrypt mpi.
82  * Initialized in #init_crypto_constants.
83  */
84 static gcry_mpi_t elgamal_q;
85
86 /**
87  * Modulus of the prime field used for ElGamal.
88  * Initialized in #init_crypto_constants.
89  */
90 static gcry_mpi_t elgamal_p;
91
92 /**
93  * Generator for prime field of order 'elgamal_q'.
94  * Initialized in #init_crypto_constants.
95  */
96 static gcry_mpi_t elgamal_g;
97
98
99 /**
100  * Function to initialize #elgamal_q, #elgamal_p and #elgamal_g.
101  */
102 static void
103 ensure_elgamal_initialized (void)
104 {
105   if (NULL != elgamal_q)
106     return; /* looks like crypto is already initialized */
107
108   GNUNET_assert (0 == gcry_mpi_scan (&elgamal_q, GCRYMPI_FMT_HEX,
109                                      GNUNET_SECRETSHARING_ELGAMAL_Q_HEX, 0,
110                                      NULL));
111   GNUNET_assert (0 == gcry_mpi_scan (&elgamal_p, GCRYMPI_FMT_HEX,
112                                      GNUNET_SECRETSHARING_ELGAMAL_P_HEX, 0,
113                                      NULL));
114   GNUNET_assert (0 == gcry_mpi_scan (&elgamal_g, GCRYMPI_FMT_HEX,
115                                      GNUNET_SECRETSHARING_ELGAMAL_G_HEX, 0,
116                                      NULL));
117 }
118
119
120 /**
121  * Callback invoked when there is an error communicating with
122  * the service.  Notifies the application about the error.
123  *
124  * @param cls the `struct GNUNET_SECRETSHARING_Session`
125  * @param error error code
126  */
127 static void
128 handle_session_client_error (void *cls,
129                              enum GNUNET_MQ_Error error)
130 {
131   struct GNUNET_SECRETSHARING_Session *s = cls;
132
133   s->secret_ready_cb (s->secret_ready_cls, NULL, NULL, 0, NULL);
134   GNUNET_SECRETSHARING_session_destroy (s);
135 }
136
137
138 /**
139  * Callback invoked when there is an error communicating with
140  * the service.  Notifies the application about the error.
141  *
142  * @param cls the `struct GNUNET_SECRETSHARING_DecryptionHandle`
143  * @param error error code
144  */
145 static void
146 handle_decrypt_client_error (void *cls,
147                              enum GNUNET_MQ_Error error)
148 {
149   struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
150
151   dh->decrypt_cb (dh->decrypt_cls, NULL);
152   GNUNET_SECRETSHARING_decrypt_cancel (dh);
153 }
154
155
156 /**
157  * Handler invoked with the final result message from
158  * secret sharing.  Decodes the message and passes the
159  * result to the application.
160  *
161  * @param cls the `struct GNUNET_SECRETSHARING_Session`
162  * @param m message with the result
163  */
164 static int
165 check_secret_ready (void *cls,
166                     const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
167 {
168   /* FIXME: actually check m is well-formed here! */
169   return GNUNET_OK;
170 }
171
172
173 /**
174  * Handler invoked with the final result message from
175  * secret sharing.  Decodes the message and passes the
176  * result to the application.
177  *
178  * @param cls the `struct GNUNET_SECRETSHARING_Session`
179  * @param m message with the result
180  */
181 static void
182 handle_secret_ready (void *cls,
183                      const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
184 {
185   struct GNUNET_SECRETSHARING_Session *s = cls;
186   struct GNUNET_SECRETSHARING_Share *share;
187   size_t share_size;
188
189   LOG (GNUNET_ERROR_TYPE_DEBUG,
190        "Got secret ready message of size %u\n",
191        ntohs (m->header.size));
192   share_size = ntohs (m->header.size) - sizeof(struct
193                                                GNUNET_SECRETSHARING_SecretReadyMessage);
194
195   share = GNUNET_SECRETSHARING_share_read (&m[1],
196                                            share_size,
197                                            NULL);
198   GNUNET_assert (NULL != share);  // FIXME: this can fail!
199   // should have been checked in #check_secret_ready!
200   // FIXME: below we never check &m[1] is valid!
201   // FIXME: do we leak 'share' here?
202   s->secret_ready_cb (s->secret_ready_cls,
203                       share, /* FIXME */
204                       &share->public_key,
205                       share->num_peers,
206                       (const struct GNUNET_PeerIdentity *) &m[1]);
207
208   GNUNET_SECRETSHARING_session_destroy (s);
209 }
210
211
212 /**
213  * Destroy a secret sharing session.
214  * The secret ready callback will not be called.
215  *
216  * @param s session to destroy
217  */
218 void
219 GNUNET_SECRETSHARING_session_destroy (struct GNUNET_SECRETSHARING_Session *s)
220 {
221   GNUNET_MQ_destroy (s->mq);
222   s->mq = NULL;
223   GNUNET_free (s);
224 }
225
226
227 /**
228  * Create a session that will eventually establish a shared secret
229  * with the other peers.
230  *
231  * @param cfg configuration to use
232  * @param num_peers number of peers in @a peers
233  * @param peers array of peers that we will share secrets with, can optionally contain the local peer
234  * @param session_id unique session id
235  * @param start When should all peers be available for sharing the secret?
236  *              Random number generation can take place before the start time.
237  * @param deadline point in time where the session must be established; taken as hint
238  *                 by underlying consensus sessions
239  * @param threshold minimum number of peers that must cooperate to decrypt a value
240  * @param cb called when the secret has been established
241  * @param cls closure for @a cb
242  */
243 struct GNUNET_SECRETSHARING_Session *
244 GNUNET_SECRETSHARING_create_session (const struct
245                                      GNUNET_CONFIGURATION_Handle *cfg,
246                                      unsigned int num_peers,
247                                      const struct GNUNET_PeerIdentity *peers,
248                                      const struct GNUNET_HashCode *session_id,
249                                      struct GNUNET_TIME_Absolute start,
250                                      struct GNUNET_TIME_Absolute deadline,
251                                      unsigned int threshold,
252                                      GNUNET_SECRETSHARING_SecretReadyCallback cb,
253                                      void *cls)
254 {
255   struct GNUNET_SECRETSHARING_Session *s
256     = GNUNET_new (struct GNUNET_SECRETSHARING_Session);
257   struct GNUNET_MQ_MessageHandler mq_handlers[] = {
258     GNUNET_MQ_hd_var_size (secret_ready,
259                            GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY,
260                            struct GNUNET_SECRETSHARING_SecretReadyMessage,
261                            s),
262     GNUNET_MQ_handler_end ()
263   };
264   struct GNUNET_MQ_Envelope *ev;
265   struct GNUNET_SECRETSHARING_CreateMessage *msg;
266
267   s->mq = GNUNET_CLIENT_connect (cfg,
268                                  "secretsharing",
269                                  mq_handlers,
270                                  &handle_session_client_error,
271                                  s);
272   if (NULL == s->mq)
273   {
274     /* secretsharing not configured correctly */
275     GNUNET_break (0);
276     GNUNET_free (s);
277     return NULL;
278   }
279   s->secret_ready_cb = cb;
280   s->secret_ready_cls = cls;
281   ev = GNUNET_MQ_msg_extra (msg,
282                             num_peers * sizeof(struct GNUNET_PeerIdentity),
283                             GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE);
284
285   msg->threshold = htons (threshold);
286   msg->num_peers = htons (num_peers);
287   msg->session_id = *session_id;
288   msg->start = GNUNET_TIME_absolute_hton (start);
289   msg->deadline = GNUNET_TIME_absolute_hton (deadline);
290   GNUNET_memcpy (&msg[1], peers, num_peers * sizeof(struct
291                                                     GNUNET_PeerIdentity));
292
293   GNUNET_MQ_send (s->mq, ev);
294
295   LOG (GNUNET_ERROR_TYPE_DEBUG,
296        "Secretsharing session created with %u peers\n",
297        num_peers);
298   return s;
299 }
300
301
302 static void
303 handle_decrypt_done (void *cls,
304                      const struct
305                      GNUNET_SECRETSHARING_DecryptResponseMessage *m)
306 {
307   struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
308   const struct GNUNET_SECRETSHARING_Plaintext *plaintext;
309
310   if (m->success == 0)
311     plaintext = NULL;
312   else
313     plaintext = (void *) &m->plaintext;
314   dh->decrypt_cb (dh->decrypt_cls, plaintext);
315   GNUNET_SECRETSHARING_decrypt_cancel (dh);
316 }
317
318
319 /**
320  * Publish the given ciphertext for decryption.  Once a sufficient (>=k) number of peers has
321  * published the same value, it will be decrypted.
322  *
323  * When the operation is canceled, the decrypt_cb is not called anymore, but the calling
324  * peer may already have irrevocably contributed its share for the decryption of the value.
325  *
326  * @param share our secret share to use for decryption
327  * @param ciphertext ciphertext to publish in order to decrypt it (if enough peers agree)
328  * @param decrypt_cb callback called once the decryption succeeded
329  * @param decrypt_cb_cls closure for @a decrypt_cb
330  * @return handle to cancel the operation
331  */
332 struct GNUNET_SECRETSHARING_DecryptionHandle *
333 GNUNET_SECRETSHARING_decrypt (const struct GNUNET_CONFIGURATION_Handle *cfg,
334                               struct GNUNET_SECRETSHARING_Share *share,
335                               const struct
336                               GNUNET_SECRETSHARING_Ciphertext *ciphertext,
337                               struct GNUNET_TIME_Absolute start,
338                               struct GNUNET_TIME_Absolute deadline,
339                               GNUNET_SECRETSHARING_DecryptCallback decrypt_cb,
340                               void *decrypt_cb_cls)
341 {
342   struct GNUNET_SECRETSHARING_DecryptionHandle *s
343     = GNUNET_new (struct GNUNET_SECRETSHARING_DecryptionHandle);
344   struct GNUNET_MQ_MessageHandler mq_handlers[] = {
345     GNUNET_MQ_hd_fixed_size (decrypt_done,
346                              GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE,
347                              struct GNUNET_SECRETSHARING_DecryptResponseMessage,
348                              s),
349     GNUNET_MQ_handler_end ()
350   };
351   struct GNUNET_MQ_Envelope *ev;
352   struct GNUNET_SECRETSHARING_DecryptRequestMessage *msg;
353   size_t share_size;
354
355   s->decrypt_cb = decrypt_cb;
356   s->decrypt_cls = decrypt_cb_cls;
357   s->mq = GNUNET_CLIENT_connect (cfg,
358                                  "secretsharing",
359                                  mq_handlers,
360                                  &handle_decrypt_client_error,
361                                  s);
362   if (NULL == s->mq)
363   {
364     GNUNET_free (s);
365     return NULL;
366   }
367   GNUNET_assert (GNUNET_OK ==
368                  GNUNET_SECRETSHARING_share_write (share, NULL, 0,
369                                                    &share_size));
370
371   ev = GNUNET_MQ_msg_extra (msg,
372                             share_size,
373                             GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT);
374
375   GNUNET_assert (GNUNET_OK ==
376                  GNUNET_SECRETSHARING_share_write (share,
377                                                    &msg[1],
378                                                    share_size,
379                                                    NULL));
380
381   msg->start = GNUNET_TIME_absolute_hton (start);
382   msg->deadline = GNUNET_TIME_absolute_hton (deadline);
383   msg->ciphertext = *ciphertext;
384
385   GNUNET_MQ_send (s->mq, ev);
386
387   LOG (GNUNET_ERROR_TYPE_DEBUG,
388        "decrypt session created\n");
389   return s;
390 }
391
392
393 int
394 GNUNET_SECRETSHARING_plaintext_generate_i (struct
395                                            GNUNET_SECRETSHARING_Plaintext *
396                                            plaintext,
397                                            int64_t exponent)
398 {
399   int negative;
400   gcry_mpi_t x;
401
402   ensure_elgamal_initialized ();
403
404   GNUNET_assert (NULL != (x = gcry_mpi_new (0)));
405
406   negative = GNUNET_NO;
407   if (exponent < 0)
408   {
409     negative = GNUNET_YES;
410     exponent = -exponent;
411   }
412
413   gcry_mpi_set_ui (x, exponent);
414
415   gcry_mpi_powm (x, elgamal_g, x, elgamal_p);
416
417   if (GNUNET_YES == negative)
418   {
419     int res;
420     res = gcry_mpi_invm (x, x, elgamal_p);
421     if (0 == res)
422       return GNUNET_SYSERR;
423   }
424
425   GNUNET_CRYPTO_mpi_print_unsigned (plaintext,
426                                     sizeof(struct
427                                            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
449                               GNUNET_SECRETSHARING_PublicKey *public_key,
450                               const struct
451                               GNUNET_SECRETSHARING_Plaintext *plaintext,
452                               struct GNUNET_SECRETSHARING_Ciphertext *
453                               result_ciphertext)
454 {
455   /* pubkey */
456   gcry_mpi_t h;
457   /* nonce */
458   gcry_mpi_t y;
459   /* plaintext message */
460   gcry_mpi_t m;
461   /* temp value */
462   gcry_mpi_t tmp;
463
464   ensure_elgamal_initialized ();
465
466   GNUNET_assert (NULL != (h = gcry_mpi_new (0)));
467   GNUNET_assert (NULL != (y = gcry_mpi_new (0)));
468   GNUNET_assert (NULL != (tmp = gcry_mpi_new (0)));
469
470   GNUNET_CRYPTO_mpi_scan_unsigned (&h, public_key, sizeof *public_key);
471   GNUNET_CRYPTO_mpi_scan_unsigned (&m, plaintext, sizeof *plaintext);
472
473   // Randomize y such that 0 < y < elgamal_q.
474   // The '- 1' is necessary as bitlength(q) = bitlength(p) - 1.
475   do
476   {
477     gcry_mpi_randomize (y, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1,
478                         GCRY_WEAK_RANDOM);
479   }
480   while ((gcry_mpi_cmp_ui (y, 0) == 0) || (gcry_mpi_cmp (y, elgamal_q) >= 0));
481
482   // tmp <- g^y
483   gcry_mpi_powm (tmp, elgamal_g, y, elgamal_p);
484   // write tmp to c1
485   GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c1_bits,
486                                     GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
487
488   // tmp <- h^y
489   gcry_mpi_powm (tmp, h, y, elgamal_p);
490   // tmp <- tmp * m
491   gcry_mpi_mulm (tmp, tmp, m, elgamal_p);
492   // write tmp to c2
493   GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c2_bits,
494                                     GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
495
496   return GNUNET_OK;
497 }
498
499
500 /**
501  * Cancel a decryption.
502  *
503  * The decrypt_cb is not called anymore, but the calling
504  * peer may already have irrevocably contributed its share for the decryption of the value.
505  *
506  * @param dh to cancel
507  */
508 void
509 GNUNET_SECRETSHARING_decrypt_cancel (struct
510                                      GNUNET_SECRETSHARING_DecryptionHandle *dh)
511 {
512   GNUNET_MQ_destroy (dh->mq);
513   dh->mq = NULL;
514   GNUNET_free (dh);
515 }
516
517
518 /* end of secretsharing_api.c */