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