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