first batch of license fixes (boring)
[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 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
16 /**
17  * @file secretsharing/secretsharing_api.c
18  * @brief
19  * @author Florian Dold
20  */
21 #include "platform.h"
22 #include "gnunet_util_lib.h"
23 #include "gnunet_secretsharing_service.h"
24 #include "secretsharing.h"
25 #include <gcrypt.h>
26
27
28 #define LOG(kind,...) GNUNET_log_from (kind, "secretsharing-api",__VA_ARGS__)
29
30 /**
31  * Session that will eventually establish a shared secred between
32  * the involved peers and allow encryption and cooperative decryption.
33  */
34 struct GNUNET_SECRETSHARING_Session
35 {
36
37   /**
38    * Message queue for @e client.
39    */
40   struct GNUNET_MQ_Handle *mq;
41
42   /**
43    * Called when the secret sharing is done.
44    */
45   GNUNET_SECRETSHARING_SecretReadyCallback secret_ready_cb;
46
47   /**
48    * Closure for @e secret_ready_cb.
49    */
50   void *secret_ready_cls;
51 };
52
53
54 /**
55  * Handle to cancel a cooperative decryption operation.
56  */
57 struct GNUNET_SECRETSHARING_DecryptionHandle
58 {
59
60   /**
61    * Message queue for @e client.
62    */
63   struct GNUNET_MQ_Handle *mq;
64
65   /**
66    * Called when the secret sharing is done.
67    */
68   GNUNET_SECRETSHARING_DecryptCallback decrypt_cb;
69
70   /**
71    * Closure for @e decrypt_cb.
72    */
73   void *decrypt_cls;
74 };
75
76
77 /**
78  * The ElGamal prime field order as libgcrypt mpi.
79  * Initialized in #init_crypto_constants.
80  */
81 static gcry_mpi_t elgamal_q;
82
83 /**
84  * Modulus of the prime field used for ElGamal.
85  * Initialized in #init_crypto_constants.
86  */
87 static gcry_mpi_t elgamal_p;
88
89 /**
90  * Generator for prime field of order 'elgamal_q'.
91  * Initialized in #init_crypto_constants.
92  */
93 static gcry_mpi_t elgamal_g;
94
95
96 /**
97  * Function to initialize #elgamal_q, #elgamal_p and #elgamal_g.
98  */
99 static void
100 ensure_elgamal_initialized (void)
101 {
102   if (NULL != elgamal_q)
103     return; /* looks like crypto is already initialized */
104
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));
111 }
112
113
114 /**
115  * Callback invoked when there is an error communicating with
116  * the service.  Notifies the application about the error.
117  *
118  * @param cls the `struct GNUNET_SECRETSHARING_Session`
119  * @param error error code
120  */
121 static void
122 handle_session_client_error (void *cls,
123                              enum GNUNET_MQ_Error error)
124 {
125   struct GNUNET_SECRETSHARING_Session *s = cls;
126
127   s->secret_ready_cb (s->secret_ready_cls, NULL, NULL, 0, NULL);
128   GNUNET_SECRETSHARING_session_destroy (s);
129 }
130
131
132 /**
133  * Callback invoked when there is an error communicating with
134  * the service.  Notifies the application about the error.
135  *
136  * @param cls the `struct GNUNET_SECRETSHARING_DecryptionHandle`
137  * @param error error code
138  */
139 static void
140 handle_decrypt_client_error (void *cls,
141                              enum GNUNET_MQ_Error error)
142 {
143   struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
144
145   dh->decrypt_cb (dh->decrypt_cls, NULL);
146   GNUNET_SECRETSHARING_decrypt_cancel (dh);
147 }
148
149
150 /**
151  * Handler invoked with the final result message from
152  * secret sharing.  Decodes the message and passes the
153  * result to the application.
154  *
155  * @param cls the `struct GNUNET_SECRETSHARING_Session`
156  * @param m message with the result
157  */
158 static int
159 check_secret_ready (void *cls,
160                     const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
161 {
162   /* FIXME: actually check m is well-formed here! */
163   return GNUNET_OK;
164 }
165
166
167 /**
168  * Handler invoked with the final result message from
169  * secret sharing.  Decodes the message and passes the
170  * result to the application.
171  *
172  * @param cls the `struct GNUNET_SECRETSHARING_Session`
173  * @param m message with the result
174  */
175 static void
176 handle_secret_ready (void *cls,
177                      const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
178 {
179   struct GNUNET_SECRETSHARING_Session *s = cls;
180   struct GNUNET_SECRETSHARING_Share *share;
181   size_t share_size;
182
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);
187
188   share = GNUNET_SECRETSHARING_share_read (&m[1],
189                                            share_size,
190                                            NULL);
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,
196                       share, /* FIXME */
197                       &share->public_key,
198                       share->num_peers,
199                       (const struct GNUNET_PeerIdentity *) &m[1]);
200
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_free (s);
217 }
218
219
220 /**
221  * Create a session that will eventually establish a shared secret
222  * with the other peers.
223  *
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
235  */
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,
245                                      void *cls)
246 {
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,
253                            s),
254     GNUNET_MQ_handler_end ()
255   };
256   struct GNUNET_MQ_Envelope *ev;
257   struct GNUNET_SECRETSHARING_CreateMessage *msg;
258
259   s->mq = GNUNET_CLIENT_connect (cfg,
260                                  "secretsharing",
261                                  mq_handlers,
262                                  &handle_session_client_error,
263                                  s);
264   if (NULL == s->mq)
265   {
266     /* secretsharing not configured correctly */
267     GNUNET_break (0);
268     GNUNET_free (s);
269     return NULL;
270   }
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);
276
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));
283
284   GNUNET_MQ_send (s->mq, ev);
285
286   LOG (GNUNET_ERROR_TYPE_DEBUG,
287        "Secretsharing session created with %u peers\n",
288        num_peers);
289   return s;
290 }
291
292
293 static void
294 handle_decrypt_done (void *cls,
295                      const struct GNUNET_SECRETSHARING_DecryptResponseMessage *m)
296 {
297   struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
298   const struct GNUNET_SECRETSHARING_Plaintext *plaintext;
299
300   if (m->success == 0)
301     plaintext = NULL;
302   else
303     plaintext = (void *) &m->plaintext;
304   dh->decrypt_cb (dh->decrypt_cls, plaintext);
305   GNUNET_SECRETSHARING_decrypt_cancel (dh);
306 }
307
308
309 /**
310  * Publish the given ciphertext for decryption.  Once a sufficient (>=k) number of peers has
311  * published the same value, it will be decrypted.
312  *
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.
315  *
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
321  */
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)
330 {
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,
337                              s),
338     GNUNET_MQ_handler_end ()
339   };
340   struct GNUNET_MQ_Envelope *ev;
341   struct GNUNET_SECRETSHARING_DecryptRequestMessage *msg;
342   size_t share_size;
343
344   s->decrypt_cb = decrypt_cb;
345   s->decrypt_cls = decrypt_cb_cls;
346   s->mq = GNUNET_CLIENT_connect (cfg,
347                                  "secretsharing",
348                                  mq_handlers,
349                                  &handle_decrypt_client_error,
350                                  s);
351   if (NULL == s->mq)
352   {
353     GNUNET_free (s);
354     return NULL;
355   }
356   GNUNET_assert (GNUNET_OK ==
357                  GNUNET_SECRETSHARING_share_write (share, NULL, 0,
358                                                    &share_size));
359
360   ev = GNUNET_MQ_msg_extra (msg,
361                             share_size,
362                             GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT);
363
364   GNUNET_assert (GNUNET_OK ==
365                  GNUNET_SECRETSHARING_share_write (share,
366                                                    &msg[1],
367                                                    share_size,
368                                                    NULL));
369
370   msg->start = GNUNET_TIME_absolute_hton (start);
371   msg->deadline = GNUNET_TIME_absolute_hton (deadline);
372   msg->ciphertext = *ciphertext;
373
374   GNUNET_MQ_send (s->mq, ev);
375
376   LOG (GNUNET_ERROR_TYPE_DEBUG,
377        "decrypt session created\n");
378   return s;
379 }
380
381
382 int
383 GNUNET_SECRETSHARING_plaintext_generate_i (struct GNUNET_SECRETSHARING_Plaintext *plaintext,
384                                            int64_t exponent)
385 {
386   int negative;
387   gcry_mpi_t x;
388
389   ensure_elgamal_initialized ();
390
391   GNUNET_assert (NULL != (x = gcry_mpi_new (0)));
392
393   negative = GNUNET_NO;
394   if (exponent < 0)
395   {
396     negative = GNUNET_YES;
397     exponent = -exponent;
398   }
399
400   gcry_mpi_set_ui (x, exponent);
401
402   gcry_mpi_powm (x, elgamal_g, x, elgamal_p);
403
404   if (GNUNET_YES == negative)
405   {
406     int res;
407     res = gcry_mpi_invm (x, x, elgamal_p);
408     if (0 == res)
409       return GNUNET_SYSERR;
410   }
411
412   GNUNET_CRYPTO_mpi_print_unsigned (plaintext,
413                                     sizeof (struct GNUNET_SECRETSHARING_Plaintext),
414                                     x);
415
416   return GNUNET_OK;
417 }
418
419
420 /**
421  * Encrypt a value.  This operation is executed locally, no communication is
422  * necessary.
423  *
424  * This is a helper function, encryption can be done soley with a session's public key
425  * and the crypto system parameters.
426  *
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)
432  */
433 int
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)
437 {
438   /* pubkey */
439   gcry_mpi_t h;
440   /* nonce */
441   gcry_mpi_t y;
442   /* plaintext message */
443   gcry_mpi_t m;
444   /* temp value */
445   gcry_mpi_t tmp;
446
447   ensure_elgamal_initialized ();
448
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)));
452
453   GNUNET_CRYPTO_mpi_scan_unsigned (&h, public_key, sizeof *public_key);
454   GNUNET_CRYPTO_mpi_scan_unsigned (&m, plaintext, sizeof *plaintext);
455
456   // Randomize y such that 0 < y < elgamal_q.
457   // The '- 1' is necessary as bitlength(q) = bitlength(p) - 1.
458   do
459   {
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));
462
463   // tmp <- g^y
464   gcry_mpi_powm (tmp, elgamal_g, y, elgamal_p);
465   // write tmp to c1
466   GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c1_bits,
467                                     GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
468
469   // tmp <- h^y
470   gcry_mpi_powm (tmp, h, y, elgamal_p);
471   // tmp <- tmp * m
472   gcry_mpi_mulm (tmp, tmp, m, elgamal_p);
473   // write tmp to c2
474   GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c2_bits,
475                                     GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
476
477   return GNUNET_OK;
478 }
479
480
481 /**
482  * Cancel a decryption.
483  *
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.
486  *
487  * @param dh to cancel
488  */
489 void
490 GNUNET_SECRETSHARING_decrypt_cancel (struct GNUNET_SECRETSHARING_DecryptionHandle *dh)
491 {
492   GNUNET_MQ_destroy (dh->mq);
493   dh->mq = NULL;
494   GNUNET_free (dh);
495 }
496
497 /* end of secretsharing_api.c */