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