error handling
[oweals/gnunet.git] / src / scalarproduct / gnunet-service-scalarproduct-ecc_alice.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013-2017 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  * @file scalarproduct/gnunet-service-scalarproduct-ecc_alice.c
22  * @brief scalarproduct service implementation
23  * @author Christian M. Fuchs
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include <limits.h>
28 #include <gcrypt.h>
29 #include "gnunet_util_lib.h"
30 #include "gnunet_core_service.h"
31 #include "gnunet_cadet_service.h"
32 #include "gnunet_applications.h"
33 #include "gnunet_protocols.h"
34 #include "gnunet_scalarproduct_service.h"
35 #include "gnunet_set_service.h"
36 #include "scalarproduct.h"
37 #include "gnunet-service-scalarproduct-ecc.h"
38
39 #define LOG(kind, ...) \
40   GNUNET_log_from (kind, "scalarproduct-alice", __VA_ARGS__)
41
42 /**
43  * Maximum allowed result value for the scalarproduct computation.
44  * DLOG will fail if the result is bigger.  At 1 million, the
45  * precomputation takes about 2s on a fast machine.
46  */
47 #define MAX_RESULT (1024 * 1024)
48
49 /**
50  * How many values should DLOG store in memory (determines baseline
51  * RAM consumption, roughly 100 bytes times the value given here).
52  * Should be about SQRT (MAX_RESULT), larger values will make the
53  * online computation faster.
54  */
55 #define MAX_RAM (1024)
56
57 /**
58  * An encrypted element key-value pair.
59  */
60 struct MpiElement
61 {
62   /**
63    * Key used to identify matching pairs of values to multiply.
64    * Points into an existing data structure, to avoid copying
65    * and doubling memory use.
66    */
67   const struct GNUNET_HashCode *key;
68
69   /**
70    * a_i value, not disclosed to Bob.
71    */
72   gcry_mpi_t value;
73 };
74
75
76 /**
77  * A scalarproduct session which tracks
78  * a request form the client to our final response.
79  */
80 struct AliceServiceSession
81 {
82   /**
83    * (hopefully) unique transaction ID
84    */
85   struct GNUNET_HashCode session_id;
86
87   /**
88    * Alice or Bob's peerID
89    */
90   struct GNUNET_PeerIdentity peer;
91
92   /**
93    * The client this request is related to.
94    */
95   struct GNUNET_SERVICE_Client *client;
96
97   /**
98    * The message queue for the client.
99    */
100   struct GNUNET_MQ_Handle *client_mq;
101
102   /**
103    * The message queue for CADET.
104    */
105   struct GNUNET_MQ_Handle *cadet_mq;
106
107   /**
108    * all non-0-value'd elements transmitted to us.
109    * Values are of type `struct GNUNET_SCALARPRODUCT_Element *`
110    */
111   struct GNUNET_CONTAINER_MultiHashMap *intersected_elements;
112
113   /**
114    * Set of elements for which will conduction an intersection.
115    * the resulting elements are then used for computing the scalar product.
116    */
117   struct GNUNET_SET_Handle *intersection_set;
118
119   /**
120    * Set of elements for which will conduction an intersection.
121    * the resulting elements are then used for computing the scalar product.
122    */
123   struct GNUNET_SET_OperationHandle *intersection_op;
124
125   /**
126    * Handle to Alice's Intersection operation listening for Bob
127    */
128   struct GNUNET_SET_ListenHandle *intersection_listen;
129
130   /**
131    * channel-handle associated with our cadet handle
132    */
133   struct GNUNET_CADET_Channel *channel;
134
135   /**
136    * a(Alice), sorted array by key of length @e used_element_count.
137    */
138   struct MpiElement *sorted_elements;
139
140   /**
141    * The computed scalar
142    */
143   gcry_mpi_t product;
144
145   /**
146    * How many elements we were supplied with from the client (total
147    * count before intersection).
148    */
149   uint32_t total;
150
151   /**
152    * How many elements actually are used for the scalar product.
153    * Size of the arrays in @e r and @e r_prime.  Sometimes also
154    * reset to 0 and used as a counter!
155    */
156   uint32_t used_element_count;
157
158   /**
159    * Already transferred elements from client to us.
160    * Less or equal than @e total.
161    */
162   uint32_t client_received_element_count;
163
164   /**
165    * State of this session.   In
166    * #GNUNET_SCALARPRODUCT_STATUS_ACTIVE while operation is
167    * ongoing, afterwards in #GNUNET_SCALARPRODUCT_STATUS_SUCCESS or
168    * #GNUNET_SCALARPRODUCT_STATUS_FAILURE.
169    */
170   enum GNUNET_SCALARPRODUCT_ResponseStatus status;
171
172   /**
173    * Flag to prevent recursive calls to #destroy_service_session() from
174    * doing harm.
175    */
176   int in_destroy;
177 };
178
179
180 /**
181  * GNUnet configuration handle
182  */
183 static const struct GNUNET_CONFIGURATION_Handle *cfg;
184
185 /**
186  * Context for DLOG operations on a curve.
187  */
188 static struct GNUNET_CRYPTO_EccDlogContext *edc;
189
190 /**
191  * Alice's private key ('a').
192  */
193 static gcry_mpi_t my_privkey;
194
195 /**
196  * Inverse of Alice's private key ('a_inv').
197  */
198 static gcry_mpi_t my_privkey_inv;
199
200 /**
201  * Handle to the CADET service.
202  */
203 static struct GNUNET_CADET_Handle *my_cadet;
204
205
206 /**
207  * Iterator called to free elements.
208  *
209  * @param cls the `struct AliceServiceSession *` (unused)
210  * @param key the key (unused)
211  * @param value value to free
212  * @return #GNUNET_OK (continue to iterate)
213  */
214 static int
215 free_element_cb (void *cls, const struct GNUNET_HashCode *key, void *value)
216 {
217   struct GNUNET_SCALARPRODUCT_Element *e = value;
218
219   GNUNET_free (e);
220   return GNUNET_OK;
221 }
222
223
224 /**
225  * Destroy session state, we are done with it.
226  *
227  * @param s the session to free elements from
228  */
229 static void
230 destroy_service_session (struct AliceServiceSession *s)
231 {
232   unsigned int i;
233
234   if (GNUNET_YES == s->in_destroy)
235     return;
236   s->in_destroy = GNUNET_YES;
237   if (NULL != s->client)
238   {
239     struct GNUNET_SERVICE_Client *c = s->client;
240
241     s->client = NULL;
242     GNUNET_SERVICE_client_drop (c);
243   }
244   if (NULL != s->channel)
245   {
246     GNUNET_CADET_channel_destroy (s->channel);
247     s->channel = NULL;
248   }
249   if (NULL != s->intersected_elements)
250   {
251     GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements,
252                                            &free_element_cb,
253                                            s);
254     GNUNET_CONTAINER_multihashmap_destroy (s->intersected_elements);
255     s->intersected_elements = NULL;
256   }
257   if (NULL != s->intersection_listen)
258   {
259     GNUNET_SET_listen_cancel (s->intersection_listen);
260     s->intersection_listen = NULL;
261   }
262   if (NULL != s->intersection_op)
263   {
264     LOG (GNUNET_ERROR_TYPE_DEBUG, "Set intersection, op still ongoing!\n");
265     GNUNET_SET_operation_cancel (s->intersection_op);
266     s->intersection_op = NULL;
267   }
268   if (NULL != s->intersection_set)
269   {
270     GNUNET_SET_destroy (s->intersection_set);
271     s->intersection_set = NULL;
272   }
273   if (NULL != s->sorted_elements)
274   {
275     for (i = 0; i < s->used_element_count; i++)
276       gcry_mpi_release (s->sorted_elements[i].value);
277     GNUNET_free (s->sorted_elements);
278     s->sorted_elements = NULL;
279   }
280   if (NULL != s->product)
281   {
282     gcry_mpi_release (s->product);
283     s->product = NULL;
284   }
285   GNUNET_free (s);
286 }
287
288
289 /**
290  * Notify the client that the session has failed.  A message gets sent
291  * to Alice's client if we encountered any error.
292  *
293  * @param session the associated client session to fail or succeed
294  */
295 static void
296 prepare_client_end_notification (struct AliceServiceSession *session)
297 {
298   struct ClientResponseMessage *msg;
299   struct GNUNET_MQ_Envelope *e;
300
301   if (NULL == session->client_mq)
302     return; /* no client left to be notified */
303   GNUNET_log (
304     GNUNET_ERROR_TYPE_DEBUG,
305     "Sending session-end notification with status %d to client for session %s\n",
306     session->status,
307     GNUNET_h2s (&session->session_id));
308   e = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT);
309   msg->product_length = htonl (0);
310   msg->status = htonl (session->status);
311   GNUNET_MQ_send (session->client_mq, e);
312 }
313
314
315 /**
316  * Prepare the final (positive) response we will send to Alice's
317  * client.
318  *
319  * @param s the session associated with our client.
320  */
321 static void
322 transmit_client_response (struct AliceServiceSession *s)
323 {
324   struct ClientResponseMessage *msg;
325   struct GNUNET_MQ_Envelope *e;
326   unsigned char *product_exported = NULL;
327   size_t product_length = 0;
328   int32_t range;
329   gcry_error_t rc;
330   int sign;
331   gcry_mpi_t value;
332
333   if (NULL == s->product)
334   {
335     GNUNET_break (0);
336     prepare_client_end_notification (s);
337     return;
338   }
339   value = gcry_mpi_new (0);
340   sign = gcry_mpi_cmp_ui (s->product, 0);
341   if (0 > sign)
342   {
343     range = -1;
344     gcry_mpi_sub (value, value, s->product);
345   }
346   else if (0 < sign)
347   {
348     range = 1;
349     gcry_mpi_add (value, value, s->product);
350   }
351   else
352   {
353     /* result is exactly zero */
354     range = 0;
355   }
356   gcry_mpi_release (s->product);
357   s->product = NULL;
358
359   if ((0 != range) && (0 != (rc = gcry_mpi_aprint (GCRYMPI_FMT_STD,
360                                                    &product_exported,
361                                                    &product_length,
362                                                    value))))
363   {
364     LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
365     prepare_client_end_notification (s);
366     return;
367   }
368   gcry_mpi_release (value);
369   e = GNUNET_MQ_msg_extra (msg,
370                            product_length,
371                            GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT);
372   msg->status = htonl (GNUNET_SCALARPRODUCT_STATUS_SUCCESS);
373   msg->range = htonl (range);
374   msg->product_length = htonl (product_length);
375   if (NULL != product_exported)
376   {
377     GNUNET_memcpy (&msg[1], product_exported, product_length);
378     GNUNET_free (product_exported);
379   }
380   GNUNET_MQ_send (s->client_mq, e);
381   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
382               "Sent result to client, session %s has ended!\n",
383               GNUNET_h2s (&s->session_id));
384 }
385
386
387 /**
388  * Function called whenever a channel is destroyed.  Should clean up
389  * any associated state.
390  *
391  * It must NOT call #GNUNET_CADET_channel_destroy() on the channel.
392  *
393  * @param cls the `struct AliceServiceSession`
394  * @param channel connection to the other end (henceforth invalid)
395  */
396 static void
397 cb_channel_destruction (void *cls, const struct GNUNET_CADET_Channel *channel)
398 {
399   struct AliceServiceSession *s = cls;
400
401   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
402               "Peer disconnected, terminating session %s with peer %s\n",
403               GNUNET_h2s (&s->session_id),
404               GNUNET_i2s (&s->peer));
405   s->channel = NULL;
406   if (GNUNET_SCALARPRODUCT_STATUS_ACTIVE == s->status)
407   {
408     /* We didn't get an answer yet, fail with error */
409     s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
410     prepare_client_end_notification (s);
411   }
412 }
413
414
415 /**
416  * Compute our scalar product, done by Alice
417  *
418  * @param session the session associated with this computation
419  * @param prod_g_i_b_i value from Bob
420  * @param prod_h_i_b_i value from Bob
421  * @return product as MPI, never NULL
422  */
423 static gcry_mpi_t
424 compute_scalar_product (struct AliceServiceSession *session,
425                         gcry_mpi_point_t prod_g_i_b_i,
426                         gcry_mpi_point_t prod_h_i_b_i)
427 {
428   gcry_mpi_point_t g_i_b_i_a_inv;
429   gcry_mpi_point_t g_ai_bi;
430   int ai_bi;
431   gcry_mpi_t ret;
432
433   g_i_b_i_a_inv =
434     GNUNET_CRYPTO_ecc_pmul_mpi (edc, prod_g_i_b_i, my_privkey_inv);
435   g_ai_bi = GNUNET_CRYPTO_ecc_add (edc, g_i_b_i_a_inv, prod_h_i_b_i);
436   gcry_mpi_point_release (g_i_b_i_a_inv);
437   ai_bi = GNUNET_CRYPTO_ecc_dlog (edc, g_ai_bi);
438   gcry_mpi_point_release (g_ai_bi);
439   if (INT_MAX == ai_bi)
440   {
441     /* result too big */
442     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
443                 "Scalar product result out of range\n");
444     return NULL;
445   }
446   ret = gcry_mpi_new (0);
447   if (ai_bi > 0)
448   {
449     gcry_mpi_set_ui (ret, ai_bi);
450   }
451   else
452   {
453     gcry_mpi_set_ui (ret, -ai_bi);
454     gcry_mpi_neg (ret, ret);
455   }
456   return ret;
457 }
458
459
460 /**
461  * Handle a response we got from another service we wanted to
462  * calculate a scalarproduct with.
463  *
464  * @param cls the `struct AliceServiceSession *`
465  * @param msg the actual message
466  */
467 static void
468 handle_bobs_cryptodata_message (void *cls,
469                                 const struct EccBobCryptodataMessage *msg)
470 {
471   struct AliceServiceSession *s = cls;
472   gcry_mpi_point_t prod_g_i_b_i;
473   gcry_mpi_point_t prod_h_i_b_i;
474   uint32_t contained;
475
476   contained = ntohl (msg->contained_element_count);
477   if (2 != contained)
478   {
479     GNUNET_break_op (0);
480     destroy_service_session (s);
481     return;
482   }
483   if (NULL == s->sorted_elements)
484   {
485     /* we're not ready yet, how can Bob be? */
486     GNUNET_break_op (0);
487     destroy_service_session (s);
488     return;
489   }
490   if (s->total != s->client_received_element_count)
491   {
492     /* we're not ready yet, how can Bob be? */
493     GNUNET_break_op (0);
494     destroy_service_session (s);
495     return;
496   }
497
498   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
499               "Received %u crypto values from Bob\n",
500               (unsigned int) contained);
501   GNUNET_CADET_receive_done (s->channel);
502   prod_g_i_b_i = GNUNET_CRYPTO_ecc_bin_to_point (edc, &msg->prod_g_i_b_i);
503   prod_h_i_b_i = GNUNET_CRYPTO_ecc_bin_to_point (edc, &msg->prod_h_i_b_i);
504   s->product = compute_scalar_product (s, prod_g_i_b_i, prod_h_i_b_i);
505   gcry_mpi_point_release (prod_g_i_b_i);
506   gcry_mpi_point_release (prod_h_i_b_i);
507   transmit_client_response (s);
508 }
509
510
511 /**
512  * Iterator to copy over messages from the hash map
513  * into an array for sorting.
514  *
515  * @param cls the `struct AliceServiceSession *`
516  * @param key the key (unused)
517  * @param value the `struct GNUNET_SCALARPRODUCT_Element *`
518  */
519 static int
520 copy_element_cb (void *cls, const struct GNUNET_HashCode *key, void *value)
521 {
522   struct AliceServiceSession *s = cls;
523   struct GNUNET_SCALARPRODUCT_Element *e = value;
524   gcry_mpi_t mval;
525   int64_t val;
526
527   mval = gcry_mpi_new (0);
528   val = (int64_t) GNUNET_ntohll (e->value);
529   if (0 > val)
530     gcry_mpi_sub_ui (mval, mval, -val);
531   else
532     gcry_mpi_add_ui (mval, mval, val);
533   s->sorted_elements[s->used_element_count].value = mval;
534   s->sorted_elements[s->used_element_count].key = &e->key;
535   s->used_element_count++;
536   return GNUNET_OK;
537 }
538
539
540 /**
541  * Compare two `struct MpiValue`s by key for sorting.
542  *
543  * @param a pointer to first `struct MpiValue *`
544  * @param b pointer to first `struct MpiValue *`
545  * @return -1 for a < b, 0 for a=b, 1 for a > b.
546  */
547 static int
548 element_cmp (const void *a, const void *b)
549 {
550   const struct MpiElement *ma = a;
551   const struct MpiElement *mb = b;
552
553   return GNUNET_CRYPTO_hash_cmp (ma->key, mb->key);
554 }
555
556
557 /**
558  * Maximum number of elements we can put into a single cryptodata
559  * message
560  */
561 #define ELEMENT_CAPACITY                          \
562   ((GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE - 1   \
563     - sizeof(struct EccAliceCryptodataMessage))    \
564    / sizeof(struct GNUNET_CRYPTO_EccPoint))
565
566
567 /**
568  * Send the cryptographic data from Alice to Bob.
569  * Does nothing if we already transferred all elements.
570  *
571  * @param s the associated service session
572  */
573 static void
574 send_alices_cryptodata_message (struct AliceServiceSession *s)
575 {
576   struct EccAliceCryptodataMessage *msg;
577   struct GNUNET_MQ_Envelope *e;
578   struct GNUNET_CRYPTO_EccPoint *payload;
579   gcry_mpi_t r_ia;
580   gcry_mpi_t r_ia_ai;
581   unsigned int i;
582   unsigned int off;
583   unsigned int todo_count;
584
585   s->sorted_elements = GNUNET_new_array (GNUNET_CONTAINER_multihashmap_size (
586                                            s->intersected_elements),
587                                          struct MpiElement);
588   s->used_element_count = 0;
589   GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements,
590                                          &copy_element_cb,
591                                          s);
592   LOG (GNUNET_ERROR_TYPE_DEBUG,
593        "Finished intersection, %d items remain\n",
594        s->used_element_count);
595   qsort (s->sorted_elements,
596          s->used_element_count,
597          sizeof(struct MpiElement),
598          &element_cmp);
599   off = 0;
600   while (off < s->used_element_count)
601   {
602     todo_count = s->used_element_count - off;
603     if (todo_count > ELEMENT_CAPACITY)
604       todo_count = ELEMENT_CAPACITY;
605     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
606                 "Sending %u/%u crypto values to Bob\n",
607                 (unsigned int) todo_count,
608                 (unsigned int) s->used_element_count);
609
610     e =
611       GNUNET_MQ_msg_extra (msg,
612                            todo_count * 2
613                            * sizeof(struct GNUNET_CRYPTO_EccPoint),
614                            GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_ALICE_CRYPTODATA);
615     msg->contained_element_count = htonl (todo_count);
616     payload = (struct GNUNET_CRYPTO_EccPoint *) &msg[1];
617     r_ia = gcry_mpi_new (0);
618     r_ia_ai = gcry_mpi_new (0);
619     for (i = off; i < off + todo_count; i++)
620     {
621       gcry_mpi_t r_i;
622       gcry_mpi_point_t g_i;
623       gcry_mpi_point_t h_i;
624
625       r_i = GNUNET_CRYPTO_ecc_random_mod_n (edc);
626       g_i = GNUNET_CRYPTO_ecc_dexp_mpi (edc, r_i);
627       /* r_ia = r_i * a */
628       gcry_mpi_mul (r_ia, r_i, my_privkey);
629       gcry_mpi_release (r_i);
630       /* r_ia_ai = r_ia + a_i */
631       gcry_mpi_add (r_ia_ai, r_ia, s->sorted_elements[i].value);
632       h_i = GNUNET_CRYPTO_ecc_dexp_mpi (edc, r_ia_ai);
633       GNUNET_CRYPTO_ecc_point_to_bin (edc, g_i, &payload[(i - off) * 2]);
634       GNUNET_CRYPTO_ecc_point_to_bin (edc, h_i, &payload[(i - off) * 2 + 1]);
635       gcry_mpi_point_release (g_i);
636       gcry_mpi_point_release (h_i);
637     }
638     gcry_mpi_release (r_ia);
639     gcry_mpi_release (r_ia_ai);
640     off += todo_count;
641     GNUNET_MQ_send (s->cadet_mq, e);
642   }
643 }
644
645
646 /**
647  * Callback for set operation results. Called for each element
648  * that should be removed from the result set, and then once
649  * to indicate that the set intersection operation is done.
650  *
651  * @param cls closure with the `struct AliceServiceSession`
652  * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK
653  * @param current_size current set size
654  * @param status what has happened with the set intersection?
655  */
656 static void
657 cb_intersection_element_removed (void *cls,
658                                  const struct GNUNET_SET_Element *element,
659                                  uint64_t current_size,
660                                  enum GNUNET_SET_Status status)
661 {
662   struct AliceServiceSession *s = cls;
663   struct GNUNET_SCALARPRODUCT_Element *se;
664
665   switch (status)
666   {
667   case GNUNET_SET_STATUS_OK:
668     /* this element has been removed from the set */
669     se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements,
670                                             element->data);
671     GNUNET_assert (NULL != se);
672     LOG (GNUNET_ERROR_TYPE_DEBUG,
673          "Intersection removed element with key %s and value %lld\n",
674          GNUNET_h2s (&se->key),
675          (long long) GNUNET_ntohll (se->value));
676     GNUNET_assert (
677       GNUNET_YES ==
678       GNUNET_CONTAINER_multihashmap_remove (s->intersected_elements,
679                                             element->data,
680                                             se));
681     GNUNET_free (se);
682     return;
683
684   case GNUNET_SET_STATUS_DONE:
685     s->intersection_op = NULL;
686     if (NULL != s->intersection_set)
687     {
688       GNUNET_SET_destroy (s->intersection_set);
689       s->intersection_set = NULL;
690     }
691     send_alices_cryptodata_message (s);
692     return;
693
694   case GNUNET_SET_STATUS_HALF_DONE:
695     /* unexpected for intersection */
696     GNUNET_break (0);
697     return;
698
699   case GNUNET_SET_STATUS_FAILURE:
700     /* unhandled status code */
701     LOG (GNUNET_ERROR_TYPE_DEBUG, "Set intersection failed!\n");
702     if (NULL != s->intersection_listen)
703     {
704       GNUNET_SET_listen_cancel (s->intersection_listen);
705       s->intersection_listen = NULL;
706     }
707     s->intersection_op = NULL;
708     if (NULL != s->intersection_set)
709     {
710       GNUNET_SET_destroy (s->intersection_set);
711       s->intersection_set = NULL;
712     }
713     s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
714     prepare_client_end_notification (s);
715     return;
716
717   default:
718     GNUNET_break (0);
719     return;
720   }
721 }
722
723
724 /**
725  * Called when another peer wants to do a set operation with the
726  * local peer. If a listen error occurs, the @a request is NULL.
727  *
728  * @param cls closure with the `struct AliceServiceSession *`
729  * @param other_peer the other peer
730  * @param context_msg message with application specific information from
731  *        the other peer
732  * @param request request from the other peer (never NULL), use GNUNET_SET_accept()
733  *        to accept it, otherwise the request will be refused
734  *        Note that we can't just return value from the listen callback,
735  *        as it is also necessary to specify the set we want to do the
736  *        operation with, whith sometimes can be derived from the context
737  *        message. It's necessary to specify the timeout.
738  */
739 static void
740 cb_intersection_request_alice (void *cls,
741                                const struct GNUNET_PeerIdentity *other_peer,
742                                const struct GNUNET_MessageHeader *context_msg,
743                                struct GNUNET_SET_Request *request)
744 {
745   struct AliceServiceSession *s = cls;
746
747   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
748               "Received intersection request from %s!\n",
749               GNUNET_i2s (other_peer));
750   if (0 != GNUNET_memcmp (other_peer, &s->peer))
751   {
752     GNUNET_break_op (0);
753     return;
754   }
755   s->intersection_op = GNUNET_SET_accept (request,
756                                           GNUNET_SET_RESULT_REMOVED,
757                                           (struct GNUNET_SET_Option[]){ { 0 } },
758                                           &cb_intersection_element_removed,
759                                           s);
760   if (NULL == s->intersection_op)
761   {
762     GNUNET_break (0);
763     s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
764     prepare_client_end_notification (s);
765     return;
766   }
767   if (GNUNET_OK != GNUNET_SET_commit (s->intersection_op, s->intersection_set))
768   {
769     GNUNET_break (0);
770     s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
771     prepare_client_end_notification (s);
772     return;
773   }
774 }
775
776
777 /**
778  * Our client has finished sending us its multipart message.
779  *
780  * @param session the service session context
781  */
782 static void
783 client_request_complete_alice (struct AliceServiceSession *s)
784 {
785   struct GNUNET_MQ_MessageHandler cadet_handlers[] =
786   { GNUNET_MQ_hd_fixed_size (bobs_cryptodata_message,
787                              GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_BOB_CRYPTODATA,
788                              struct EccBobCryptodataMessage,
789                              s),
790     GNUNET_MQ_handler_end () };
791   struct EccServiceRequestMessage *msg;
792   struct GNUNET_MQ_Envelope *e;
793   struct GNUNET_HashCode set_sid;
794
795   GNUNET_CRYPTO_hash (&s->session_id,
796                       sizeof(struct GNUNET_HashCode),
797                       &set_sid);
798   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799               "Creating new channel for session with key %s.\n",
800               GNUNET_h2s (&s->session_id));
801   s->channel = GNUNET_CADET_channel_create (my_cadet,
802                                             s,
803                                             &s->peer,
804                                             &s->session_id,
805                                             NULL,
806                                             &cb_channel_destruction,
807                                             cadet_handlers);
808   if (NULL == s->channel)
809   {
810     s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
811     prepare_client_end_notification (s);
812     return;
813   }
814   s->cadet_mq = GNUNET_CADET_get_mq (s->channel);
815   s->intersection_listen = GNUNET_SET_listen (cfg,
816                                               GNUNET_SET_OPERATION_INTERSECTION,
817                                               &set_sid,
818                                               &cb_intersection_request_alice,
819                                               s);
820   if (NULL == s->intersection_listen)
821   {
822     s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
823     GNUNET_CADET_channel_destroy (s->channel);
824     s->channel = NULL;
825     prepare_client_end_notification (s);
826     return;
827   }
828
829   e =
830     GNUNET_MQ_msg (msg,
831                    GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_SESSION_INITIALIZATION);
832   GNUNET_MQ_env_set_options (e, GNUNET_MQ_PRIO_CRITICAL_CONTROL);
833   msg->session_id = s->session_id;
834   GNUNET_MQ_send (s->cadet_mq, e);
835 }
836
837
838 /**
839  * We're receiving additional set data. Check if
840  * @a msg is well-formed.
841  *
842  * @param cls client identification of the client
843  * @param msg the actual message
844  * @return #GNUNET_OK if @a msg is well-formed
845  */
846 static int
847 check_alice_client_message_multipart (
848   void *cls,
849   const struct ComputationBobCryptodataMultipartMessage *msg)
850 {
851   struct AliceServiceSession *s = cls;
852   uint32_t contained_count;
853   uint16_t msize;
854
855   msize = ntohs (msg->header.size);
856   contained_count = ntohl (msg->element_count_contained);
857   if ((msize !=
858        (sizeof(struct ComputationBobCryptodataMultipartMessage)
859         + contained_count * sizeof(struct GNUNET_SCALARPRODUCT_Element))) ||
860       (0 == contained_count) ||
861       (s->total == s->client_received_element_count) ||
862       (s->total < s->client_received_element_count + contained_count))
863   {
864     GNUNET_break_op (0);
865     return GNUNET_SYSERR;
866   }
867   return GNUNET_OK;
868 }
869
870
871 /**
872  * We're receiving additional set data. Add it to our
873  * set and if we are done, initiate the transaction.
874  *
875  * @param cls client identification of the client
876  * @param msg the actual message
877  */
878 static void
879 handle_alice_client_message_multipart (
880   void *cls,
881   const struct ComputationBobCryptodataMultipartMessage *msg)
882 {
883   struct AliceServiceSession *s = cls;
884   uint32_t contained_count;
885   const struct GNUNET_SCALARPRODUCT_Element *elements;
886   struct GNUNET_SET_Element set_elem;
887   struct GNUNET_SCALARPRODUCT_Element *elem;
888
889   contained_count = ntohl (msg->element_count_contained);
890   s->client_received_element_count += contained_count;
891   elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1];
892   for (uint32_t i = 0; i < contained_count; i++)
893   {
894     elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element);
895     GNUNET_memcpy (elem,
896                    &elements[i],
897                    sizeof(struct GNUNET_SCALARPRODUCT_Element));
898     if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put (
899           s->intersected_elements,
900           &elem->key,
901           elem,
902           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
903     {
904       GNUNET_break (0);
905       GNUNET_free (elem);
906       continue;
907     }
908     set_elem.data = &elem->key;
909     set_elem.size = sizeof(elem->key);
910     set_elem.element_type = 0;
911     GNUNET_SET_add_element (s->intersection_set, &set_elem, NULL, NULL);
912     s->used_element_count++;
913   }
914   GNUNET_SERVICE_client_continue (s->client);
915   if (s->total != s->client_received_element_count)
916   {
917     /* more to come */
918     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
919                 "Received client multipart data, waiting for more!\n");
920     return;
921   }
922   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Launching computation\n");
923   client_request_complete_alice (s);
924 }
925
926
927 /**
928  * Handler for Alice's client request message.
929  * Check that @a msg is well-formed.
930  *
931  * @param cls identification of the client
932  * @param msg the actual message
933  * @return #GNUNET_OK if @a msg is well-formed
934  */
935 static int
936 check_alice_client_message (void *cls,
937                             const struct AliceComputationMessage *msg)
938 {
939   struct AliceServiceSession *s = cls;
940   uint16_t msize;
941   uint32_t total_count;
942   uint32_t contained_count;
943
944   if (NULL != s->intersected_elements)
945   {
946     /* only one concurrent session per client connection allowed,
947        simplifies logic a lot... */
948     GNUNET_break (0);
949     return GNUNET_SYSERR;
950   }
951   msize = ntohs (msg->header.size);
952   total_count = ntohl (msg->element_count_total);
953   contained_count = ntohl (msg->element_count_contained);
954   if ((0 == total_count) || (0 == contained_count) ||
955       (msize !=
956        (sizeof(struct AliceComputationMessage)
957         + contained_count * sizeof(struct GNUNET_SCALARPRODUCT_Element))))
958   {
959     GNUNET_break_op (0);
960     return GNUNET_SYSERR;
961   }
962   return GNUNET_OK;
963 }
964
965
966 /**
967  * Handler for Alice's client request message.
968  * We are doing request-initiation to compute a scalar product with a peer.
969  *
970  * @param cls identification of the client
971  * @param msg the actual message
972  */
973 static void
974 handle_alice_client_message (void *cls,
975                              const struct AliceComputationMessage *msg)
976 {
977   struct AliceServiceSession *s = cls;
978   uint32_t contained_count;
979   uint32_t total_count;
980   const struct GNUNET_SCALARPRODUCT_Element *elements;
981   struct GNUNET_SET_Element set_elem;
982   struct GNUNET_SCALARPRODUCT_Element *elem;
983
984   total_count = ntohl (msg->element_count_total);
985   contained_count = ntohl (msg->element_count_contained);
986   s->peer = msg->peer;
987   s->status = GNUNET_SCALARPRODUCT_STATUS_ACTIVE;
988   s->total = total_count;
989   s->client_received_element_count = contained_count;
990   s->session_id = msg->session_key;
991   elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1];
992   s->intersected_elements =
993     GNUNET_CONTAINER_multihashmap_create (s->total, GNUNET_YES);
994   s->intersection_set =
995     GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_INTERSECTION);
996   for (uint32_t i = 0; i < contained_count; i++)
997   {
998     if (0 == GNUNET_ntohll (elements[i].value))
999       continue;
1000     elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element);
1001     GNUNET_memcpy (elem,
1002                    &elements[i],
1003                    sizeof(struct GNUNET_SCALARPRODUCT_Element));
1004     if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put (
1005           s->intersected_elements,
1006           &elem->key,
1007           elem,
1008           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1009     {
1010       /* element with same key encountered twice! */
1011       GNUNET_break (0);
1012       GNUNET_free (elem);
1013       continue;
1014     }
1015     set_elem.data = &elem->key;
1016     set_elem.size = sizeof(elem->key);
1017     set_elem.element_type = 0;
1018     GNUNET_SET_add_element (s->intersection_set, &set_elem, NULL, NULL);
1019     s->used_element_count++;
1020   }
1021   GNUNET_SERVICE_client_continue (s->client);
1022   if (s->total != s->client_received_element_count)
1023   {
1024     /* wait for multipart msg */
1025     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1026                 "Received partial client request, waiting for more!\n");
1027     return;
1028   }
1029   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Launching computation\n");
1030   client_request_complete_alice (s);
1031 }
1032
1033
1034 /**
1035  * Task run during shutdown.
1036  *
1037  * @param cls unused
1038  * @param tc unused
1039  */
1040 static void
1041 shutdown_task (void *cls)
1042 {
1043   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down, initiating cleanup.\n");
1044   // FIXME: we have to cut our connections to CADET first!
1045   if (NULL != my_cadet)
1046   {
1047     GNUNET_CADET_disconnect (my_cadet);
1048     my_cadet = NULL;
1049   }
1050   if (NULL != edc)
1051   {
1052     GNUNET_CRYPTO_ecc_dlog_release (edc);
1053     edc = NULL;
1054   }
1055 }
1056
1057
1058 /**
1059  * A client connected.
1060  *
1061  * Setup the associated data structure.
1062  *
1063  * @param cls closure, NULL
1064  * @param client identification of the client
1065  * @param mq message queue to communicate with @a client
1066  * @return our `struct AliceServiceSession`
1067  */
1068 static void *
1069 client_connect_cb (void *cls,
1070                    struct GNUNET_SERVICE_Client *client,
1071                    struct GNUNET_MQ_Handle *mq)
1072 {
1073   struct AliceServiceSession *s;
1074
1075   s = GNUNET_new (struct AliceServiceSession);
1076   s->client = client;
1077   s->client_mq = mq;
1078   return s;
1079 }
1080
1081
1082 /**
1083  * A client disconnected.
1084  *
1085  * Remove the associated session(s), release data structures
1086  * and cancel pending outgoing transmissions to the client.
1087  *
1088  * @param cls closure, NULL
1089  * @param client identification of the client
1090  * @param app_cls our `struct AliceServiceSession`
1091  */
1092 static void
1093 client_disconnect_cb (void *cls,
1094                       struct GNUNET_SERVICE_Client *client,
1095                       void *app_cls)
1096 {
1097   struct AliceServiceSession *s = app_cls;
1098
1099   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1100               "Client %p disconnected from us.\n",
1101               client);
1102   s->client = NULL;
1103   s->client_mq = NULL;
1104   destroy_service_session (s);
1105 }
1106
1107
1108 /**
1109  * Initialization of the program and message handlers
1110  *
1111  * @param cls closure
1112  * @param c configuration to use
1113  * @param service the initialized service
1114  */
1115 static void
1116 run (void *cls,
1117      const struct GNUNET_CONFIGURATION_Handle *c,
1118      struct GNUNET_SERVICE_Handle *service)
1119 {
1120   cfg = c;
1121   edc = GNUNET_CRYPTO_ecc_dlog_prepare (MAX_RESULT, MAX_RAM);
1122   /* Select a random 'a' value for Alice */
1123   GNUNET_CRYPTO_ecc_rnd_mpi (edc, &my_privkey, &my_privkey_inv);
1124   my_cadet = GNUNET_CADET_connect (cfg);
1125   if (NULL == my_cadet)
1126   {
1127     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Connect to CADET failed\n"));
1128     GNUNET_SCHEDULER_shutdown ();
1129     return;
1130   }
1131   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1132 }
1133
1134
1135 /**
1136  * Define "main" method using service macro.
1137  */
1138 GNUNET_SERVICE_MAIN (
1139   "scalarproduct-alice",
1140   GNUNET_SERVICE_OPTION_NONE,
1141   &run,
1142   &client_connect_cb,
1143   &client_disconnect_cb,
1144   NULL,
1145   GNUNET_MQ_hd_var_size (alice_client_message,
1146                          GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE,
1147                          struct AliceComputationMessage,
1148                          NULL),
1149   GNUNET_MQ_hd_var_size (
1150     alice_client_message_multipart,
1151     GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_ALICE,
1152     struct ComputationBobCryptodataMultipartMessage,
1153     NULL),
1154   GNUNET_MQ_handler_end ());
1155
1156
1157 /* end of gnunet-service-scalarproduct-ecc_alice.c */