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