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