2 This file is part of GNUnet.
3 (C) 2013 Christian Grothoff (and other contributing authors)
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.
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.
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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file scalarproduct/gnunet-service-scalarproduct.c
23 * @brief scalarproduct service implementation
24 * @author Christian M. Fuchs
29 #include "gnunet_util_lib.h"
30 #include "gnunet_core_service.h"
31 #include "gnunet_mesh_service.h"
32 #include "gnunet_applications.h"
33 #include "gnunet_protocols.h"
34 #include "gnunet_scalarproduct_service.h"
35 #include "scalarproduct.h"
37 #define LOG(kind,...) GNUNET_log_from (kind, "scalarproduct", __VA_ARGS__)
39 ///////////////////////////////////////////////////////////////////////////////
40 // Service Structure Definitions
41 ///////////////////////////////////////////////////////////////////////////////
45 * state a session can be in
49 CLIENT_REQUEST_RECEIVED,
50 WAITING_FOR_BOBS_CONNECT,
51 CLIENT_RESPONSE_RECEIVED,
52 WAITING_FOR_SERVICE_REQUEST,
53 WAITING_FOR_MULTIPART_TRANSMISSION,
54 WAITING_FOR_SERVICE_RESPONSE,
55 SERVICE_REQUEST_RECEIVED,
56 SERVICE_RESPONSE_RECEIVED,
62 * role a peer in a session can assume
72 * A scalarproduct session which tracks:
74 * a request form the client to our final response.
76 * a request from a service to us(service).
81 * the role this peer has
86 * session information is kept in a DLL
88 struct ServiceSession *next;
91 * session information is kept in a DLL
93 struct ServiceSession *prev;
96 * (hopefully) unique transaction ID
98 struct GNUNET_HashCode key;
101 * state of the session
103 enum SessionState state;
106 * Alice or Bob's peerID
108 struct GNUNET_PeerIdentity peer;
111 * the client this request is related to
113 struct GNUNET_SERVER_Client * client;
116 * The message to send
118 struct GNUNET_MessageHeader * msg;
121 * how many elements we were supplied with from the client
126 * how many elements actually are used after applying the mask
131 * already transferred elements (sent/received) for multipart messages, less or equal than used_element_count for
133 uint32_t transferred;
136 * index of the last transferred element for multipart messages
138 uint32_t last_processed;
141 * how many bytes the mask is long.
142 * just for convenience so we don't have to re-re-re calculate it each time
144 uint32_t mask_length;
147 * all the vector elements we received
152 * mask of which elements to check
154 unsigned char * mask;
157 * Public key of the remote service, only used by bob
159 struct GNUNET_CRYPTO_PaillierPublicKey * remote_pubkey;
162 * ai(Alice) after applying the mask
167 * E(ai)(Bob) after applying the mask
169 struct GNUNET_CRYPTO_PaillierCiphertext * e_a;
172 * Bob's permutation p of R
174 struct GNUNET_CRYPTO_PaillierCiphertext * r;
177 * Bob's permutation q of R
179 struct GNUNET_CRYPTO_PaillierCiphertext * r_prime;
184 struct GNUNET_CRYPTO_PaillierCiphertext * s;
189 struct GNUNET_CRYPTO_PaillierCiphertext * s_prime;
192 * Bobs matching response session from the client
194 struct ServiceSession * response;
197 * The computed scalar
202 * My transmit handle for the current message to a alice/bob
204 struct GNUNET_MESH_TransmitHandle * service_transmit_handle;
207 * My transmit handle for the current message to the client
209 struct GNUNET_SERVER_TransmitHandle * client_transmit_handle;
212 * channel-handle associated with our mesh handle
214 struct GNUNET_MESH_Channel * channel;
217 * Handle to a task that sends a msg to the our client
219 GNUNET_SCHEDULER_TaskIdentifier client_notification_task;
222 * Handle to a task that sends a msg to the our peer
224 GNUNET_SCHEDULER_TaskIdentifier service_request_task;
227 ///////////////////////////////////////////////////////////////////////////////
228 // Forward Delcarations
229 ///////////////////////////////////////////////////////////////////////////////
232 * Send a multi part chunk of a service request from alice to bob.
233 * This element only contains a part of the elements-vector (session->a[]),
234 * mask and public key set have to be contained within the first message
236 * This allows a ~32kbit key length while using 32000 elements or 62000 elements per request.
238 * @param cls the associated service session
241 prepare_service_request_multipart (void *cls);
244 * Send a multi part chunk of a service response from bob to alice.
245 * This element only contains the two permutations of R, R'.
247 * @param cls the associated service session
250 prepare_service_response_multipart (void *cls);
253 ///////////////////////////////////////////////////////////////////////////////
255 ///////////////////////////////////////////////////////////////////////////////
259 * Handle to the core service (NULL until we've connected to it).
261 static struct GNUNET_MESH_Handle *my_mesh;
264 * The identity of this host.
266 static struct GNUNET_PeerIdentity me;
269 * Service's own public key
271 static struct GNUNET_CRYPTO_PaillierPublicKey my_pubkey;
274 * Service's own private key
276 static struct GNUNET_CRYPTO_PaillierPrivateKey my_privkey;
279 * Service's offset for values that could possibly be negative but are plaintext for encryption.
281 static gcry_mpi_t my_offset;
284 * Head of our double linked list for client-requests sent to us.
285 * for all of these elements we calculate a scalar product with a remote peer
286 * split between service->service and client->service for simplicity
288 static struct ServiceSession * from_client_head;
290 * Tail of our double linked list for client-requests sent to us.
291 * for all of these elements we calculate a scalar product with a remote peer
292 * split between service->service and client->service for simplicity
294 static struct ServiceSession * from_client_tail;
297 * Head of our double linked list for service-requests sent to us.
298 * for all of these elements we help the requesting service in calculating a scalar product
299 * split between service->service and client->service for simplicity
301 static struct ServiceSession * from_service_head;
304 * Tail of our double linked list for service-requests sent to us.
305 * for all of these elements we help the requesting service in calculating a scalar product
306 * split between service->service and client->service for simplicity
308 static struct ServiceSession * from_service_tail;
311 * Certain events (callbacks for server & mesh operations) must not be queued after shutdown.
313 static int do_shutdown;
315 ///////////////////////////////////////////////////////////////////////////////
317 ///////////////////////////////////////////////////////////////////////////////
321 * computes the square sum over a vector of a given length.
323 * @param vector the vector to encrypt
324 * @param length the length of the vector
325 * @return an MPI value containing the calculated sum, never NULL
328 compute_square_sum (gcry_mpi_t * vector, uint32_t length)
334 GNUNET_assert (sum = gcry_mpi_new (0));
335 GNUNET_assert (elem = gcry_mpi_new (0));
337 // calculare E(sum (ai ^ 2), publickey)
338 for (i = 0; i < length; i++) {
339 gcry_mpi_mul (elem, vector[i], vector[i]);
340 gcry_mpi_add (sum, sum, elem);
342 gcry_mpi_release (elem);
349 * Primitive callback for copying over a message, as they
350 * usually are too complex to be handled in the callback itself.
351 * clears a session-callback, if a session was handed over and the transmit handle was stored
353 * @param cls the message object
354 * @param size the size of the buffer we got
355 * @param buf the buffer to copy the message to
356 * @return 0 if we couldn't copy, else the size copied over
359 do_send_message (void *cls, size_t size, void *buf)
361 struct ServiceSession * session = cls;
366 if (ntohs (session->msg->size) != size) {
371 type = ntohs (session->msg->type);
372 memcpy (buf, session->msg, size);
373 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
374 "Sent a message of type %hu.\n",
376 GNUNET_free (session->msg);
381 case GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SERVICE_TO_CLIENT:
382 session->state = FINALIZED;
383 session->client_transmit_handle = NULL;
386 case GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_TO_BOB:
387 case GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_TO_BOB_MULTIPART:
388 session->service_transmit_handle = NULL;
389 if (session->state == WAITING_FOR_MULTIPART_TRANSMISSION)
390 prepare_service_request_multipart (session);
393 case GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_TO_ALICE:
394 case GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_TO_ALICE_MULTIPART:
395 session->service_transmit_handle = NULL;
396 if (session->state == WAITING_FOR_MULTIPART_TRANSMISSION)
397 prepare_service_response_multipart (session);
409 * initializes a new vector with fresh MPI values (=0) of a given length
411 * @param length of the vector to create
412 * @return the initialized vector, never NULL
415 initialize_mpi_vector (uint32_t length)
418 gcry_mpi_t * output = GNUNET_malloc (sizeof (gcry_mpi_t) * length);
420 for (i = 0; i < length; i++)
421 GNUNET_assert (NULL != (output[i] = gcry_mpi_new (0)));
427 * Finds a not terminated client/service session in the
428 * given DLL based on session key, element count and state.
430 * @param tail - the tail of the DLL
431 * @param key - the key we want to search for
432 * @param element_count - the total element count of the dataset (session->total)
433 * @param state - a pointer to the state the session should be in, NULL to ignore
434 * @param peerid - a pointer to the peer ID of the associated peer, NULL to ignore
435 * @return a pointer to a matching session, or NULL
437 static struct ServiceSession *
438 find_matching_session (struct ServiceSession * tail,
439 const struct GNUNET_HashCode * key,
440 uint32_t element_count,
441 enum SessionState * state,
442 const struct GNUNET_PeerIdentity * peerid)
444 struct ServiceSession * curr;
446 for (curr = tail; NULL != curr; curr = curr->prev) {
447 // if the key matches, and the element_count is same
448 if ((!memcmp (&curr->key, key, sizeof (struct GNUNET_HashCode)))
449 && (curr->total == element_count)) {
450 // if incoming state is NULL OR is same as state of the queued request
451 if ((NULL == state) || (curr->state == *state)) {
452 // if peerid is NULL OR same as the peer Id in the queued request
454 || (!memcmp (&curr->peer, peerid, sizeof (struct GNUNET_PeerIdentity))))
455 // matches and is not an already terminated session
466 * Safely frees ALL memory areas referenced by a session.
468 * @param session - the session to free elements from
471 free_session_variables (struct ServiceSession * session)
476 for (i = 0; i < session->used; i++)
477 if (session->a[i]) gcry_mpi_release (session->a[i]);
478 GNUNET_free (session->a);
482 GNUNET_free (session->e_a);
486 GNUNET_free (session->mask);
487 session->mask = NULL;
490 GNUNET_free (session->r);
493 if (session->r_prime) {
494 GNUNET_free (session->r_prime);
495 session->r_prime = NULL;
498 GNUNET_free (session->s);
501 if (session->s_prime) {
502 GNUNET_free (session->s_prime);
503 session->s_prime = NULL;
505 if (session->product) {
506 gcry_mpi_release (session->product);
507 session->product = NULL;
509 if (session->remote_pubkey) {
510 GNUNET_free (session->remote_pubkey);
511 session->remote_pubkey = NULL;
513 if (session->vector) {
514 GNUNET_free_non_null (session->vector);
518 ///////////////////////////////////////////////////////////////////////////////
519 // Event and Message Handlers
520 ///////////////////////////////////////////////////////////////////////////////
524 * A client disconnected.
526 * Remove the associated session(s), release data structures
527 * and cancel pending outgoing transmissions to the client.
528 * if the session has not yet completed, we also cancel Alice's request to Bob.
530 * @param cls closure, NULL
531 * @param client identification of the client
534 handle_client_disconnect (void *cls,
535 struct GNUNET_SERVER_Client *client)
537 struct ServiceSession *session;
540 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
541 _ ("Client (%p) disconnected from us.\n"), client);
545 session = GNUNET_SERVER_client_get_user_context (client, struct ServiceSession);
548 GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session);
550 if (!(session->role == BOB && session->state == FINALIZED)) {
551 //we MUST terminate any client message underway
552 if (session->service_transmit_handle && session->channel)
553 GNUNET_MESH_notify_transmit_ready_cancel (session->service_transmit_handle);
554 if (session->channel && session->state == WAITING_FOR_SERVICE_RESPONSE)
555 GNUNET_MESH_channel_destroy (session->channel);
557 if (GNUNET_SCHEDULER_NO_TASK != session->client_notification_task) {
558 GNUNET_SCHEDULER_cancel (session->client_notification_task);
559 session->client_notification_task = GNUNET_SCHEDULER_NO_TASK;
561 if (GNUNET_SCHEDULER_NO_TASK != session->service_request_task) {
562 GNUNET_SCHEDULER_cancel (session->service_request_task);
563 session->service_request_task = GNUNET_SCHEDULER_NO_TASK;
565 if (NULL != session->client_transmit_handle) {
566 GNUNET_SERVER_notify_transmit_ready_cancel (session->client_transmit_handle);
567 session->client_transmit_handle = NULL;
569 free_session_variables (session);
570 GNUNET_free (session);
575 * Notify the client that the session has succeeded or failed completely.
576 * This message gets sent to
577 * * alice's client if bob disconnected or to
578 * * bob's client if the operation completed or alice disconnected
580 * @param cls the associated client session
581 * @param tc the task context handed to us by the scheduler, unused
584 prepare_client_end_notification (void * cls,
585 const struct GNUNET_SCHEDULER_TaskContext * tc)
587 struct ServiceSession * session = cls;
588 struct GNUNET_SCALARPRODUCT_client_response * msg;
590 session->client_notification_task = GNUNET_SCHEDULER_NO_TASK;
592 msg = GNUNET_new (struct GNUNET_SCALARPRODUCT_client_response);
593 msg->header.type = htons (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SERVICE_TO_CLIENT);
594 memcpy (&msg->key, &session->key, sizeof (struct GNUNET_HashCode));
595 memcpy (&msg->peer, &session->peer, sizeof ( struct GNUNET_PeerIdentity));
596 msg->header.size = htons (sizeof (struct GNUNET_SCALARPRODUCT_client_response));
597 // signal error if not signalized, positive result-range field but zero length.
598 msg->product_length = htonl (0);
599 msg->range = (session->state == FINALIZED) ? 0 : -1;
601 session->msg = &msg->header;
603 //transmit this message to our client
604 session->client_transmit_handle =
605 GNUNET_SERVER_notify_transmit_ready (session->client,
606 sizeof (struct GNUNET_SCALARPRODUCT_client_response),
607 GNUNET_TIME_UNIT_FOREVER_REL,
611 // if we could not even queue our request, something is wrong
612 if (NULL == session->client_transmit_handle) {
613 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not send message to client (%p)!\n"), session->client);
614 // usually gets freed by do_send_message
619 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Sending session-end notification to client (%p) for session %s\n"), &session->client, GNUNET_h2s (&session->key));
621 free_session_variables (session);
626 * prepare the response we will send to alice or bobs' clients.
627 * in Bobs case the product will be NULL.
629 * @param cls the session associated with our client.
630 * @param tc the task context handed to us by the scheduler, unused
633 prepare_client_response (void *cls,
634 const struct GNUNET_SCHEDULER_TaskContext *tc)
636 struct ServiceSession * session = cls;
637 struct GNUNET_SCALARPRODUCT_client_response * msg;
638 unsigned char * product_exported = NULL;
639 size_t product_length = 0;
640 uint32_t msg_length = 0;
645 session->client_notification_task = GNUNET_SCHEDULER_NO_TASK;
647 if (session->product) {
648 gcry_mpi_t value = gcry_mpi_new (0);
650 sign = gcry_mpi_cmp_ui (session->product, 0);
651 // libgcrypt can not handle a print of a negative number
652 // if (a->sign) return gcry_error (GPG_ERR_INTERNAL); /* Can't handle it yet. */
654 gcry_mpi_sub (value, value, session->product);
658 gcry_mpi_add (value, value, session->product);
663 gcry_mpi_release (session->product);
664 session->product = NULL;
666 // get representation as string
668 && (0 != (rc = gcry_mpi_aprint (GCRYMPI_FMT_STD,
672 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
674 range = -1; // signal error with product-length = 0 and range = -1
676 gcry_mpi_release (value);
679 msg_length = sizeof (struct GNUNET_SCALARPRODUCT_client_response) +product_length;
680 msg = GNUNET_malloc (msg_length);
681 msg->key = session->key;
682 msg->peer = session->peer;
683 if (product_exported != NULL) {
684 memcpy (&msg[1], product_exported, product_length);
685 GNUNET_free (product_exported);
687 msg->header.type = htons (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SERVICE_TO_CLIENT);
688 msg->header.size = htons (msg_length);
690 msg->product_length = htonl (product_length);
692 session->msg = (struct GNUNET_MessageHeader *) msg;
693 //transmit this message to our client
694 session->client_transmit_handle =
695 GNUNET_SERVER_notify_transmit_ready (session->client,
697 GNUNET_TIME_UNIT_FOREVER_REL,
700 if (NULL == session->client_transmit_handle) {
701 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
702 _ ("Could not send message to client (%p)!\n"),
704 session->client = NULL;
705 // callback was not called!
710 // gracefully sent message, just terminate session structure
711 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
712 _ ("Sent result to client (%p), this session (%s) has ended!\n"),
714 GNUNET_h2s (&session->key));
715 free_session_variables (session);
720 * Send a multipart chunk of a service response from bob to alice.
721 * This element only contains the two permutations of R, R'.
723 * @param cls the associated service session
726 prepare_service_response_multipart (void *cls)
728 struct ServiceSession * session = cls;
729 struct GNUNET_CRYPTO_PaillierCiphertext * payload;
730 struct GNUNET_SCALARPRODUCT_multipart_message * msg;
736 msg_length = sizeof (struct GNUNET_SCALARPRODUCT_multipart_message);
737 todo_count = session->used - session->transferred;
739 if (todo_count > MULTIPART_ELEMENT_CAPACITY / 2)
740 // send the currently possible maximum chunk, we always transfer both permutations
741 todo_count = MULTIPART_ELEMENT_CAPACITY / 2;
743 msg_length += todo_count * sizeof (struct GNUNET_CRYPTO_PaillierCiphertext) * 2;
744 msg = GNUNET_malloc (msg_length);
745 msg->header.type = htons (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_TO_BOB_MULTIPART);
746 msg->header.size = htons (msg_length);
747 msg->multipart_element_count = htonl (todo_count);
749 payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
750 for (i = session->transferred, j=0; i < session->transferred + todo_count; i++) {
751 //r[i][p] and r[i][q]
752 memcpy (&payload[j++], &session->r[i], sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
753 memcpy (&payload[j++], &session->r_prime[i], sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
755 session->transferred += todo_count;
756 session->msg = (struct GNUNET_MessageHeader *) msg;
757 session->service_transmit_handle =
758 GNUNET_MESH_notify_transmit_ready (session->channel,
760 GNUNET_TIME_UNIT_FOREVER_REL,
764 //disconnect our client
765 if (NULL == session->service_transmit_handle) {
766 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Could not send service-response message via mesh!)\n"));
767 session->state = FINALIZED;
769 session->response->client_notification_task =
770 GNUNET_SCHEDULER_add_now (&prepare_client_end_notification,
774 if (session->transferred != session->used)
776 session->state = WAITING_FOR_MULTIPART_TRANSMISSION;
779 session->state = FINALIZED;
780 GNUNET_free (session->r_prime);
781 GNUNET_free (session->r);
782 session->r_prime = NULL;
790 * generates the response message to be sent to alice after computing
791 * the values (1), (2), S and S'
792 * (1)[]: $E_A(a_{pi(i)}) times E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$
793 * (2)[]: $E_A(a_{pi'(i)}) times E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$
794 * S: $S := E_A(sum (r_i + b_i)^2)$
795 * S': $S' := E_A(sum r_i^2)$
797 * @param session the associated requesting session with alice
798 * @return #GNUNET_NO if we could not send our message
799 * #GNUNET_OK if the operation succeeded
802 prepare_service_response (struct ServiceSession * session)
804 struct GNUNET_SCALARPRODUCT_service_response * msg;
805 uint32_t msg_length = 0;
806 struct GNUNET_CRYPTO_PaillierCiphertext * payload;
809 msg_length = sizeof (struct GNUNET_SCALARPRODUCT_service_response)
810 + 2 * sizeof (struct GNUNET_CRYPTO_PaillierCiphertext); // s, stick
812 if (GNUNET_SERVER_MAX_MESSAGE_SIZE >
813 msg_length + 2 * session->used * sizeof (struct GNUNET_CRYPTO_PaillierCiphertext)) { //r, r'
814 msg_length += 2 * session->used * sizeof (struct GNUNET_CRYPTO_PaillierCiphertext);
815 session->transferred = session->used;
818 session->transferred = (GNUNET_SERVER_MAX_MESSAGE_SIZE - 1 - msg_length) /
819 (sizeof (struct GNUNET_CRYPTO_PaillierCiphertext) * 2);
821 msg = GNUNET_malloc (msg_length);
823 msg->header.type = htons (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_TO_ALICE);
824 msg->header.size = htons (msg_length);
825 msg->total_element_count = htonl (session->total);
826 msg->used_element_count = htonl (session->used);
827 msg->contained_element_count = htonl (session->transferred);
828 memcpy (&msg->key, &session->key, sizeof (struct GNUNET_HashCode));
830 payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
831 memcpy (&payload[0], session->s, sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
832 memcpy (&payload[1], session->s_prime, sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
833 GNUNET_free (session->s_prime);
834 session->s_prime = NULL;
835 GNUNET_free (session->s);
839 for (i = 0; i < session->transferred; i++) {
840 //k[i][p] and k[i][q]
841 memcpy (&payload[2 + i*2], &session->r[i], sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
842 memcpy (&payload[3 + i*2], &session->r_prime[i], sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
845 session->msg = (struct GNUNET_MessageHeader *) msg;
846 session->service_transmit_handle =
847 GNUNET_MESH_notify_transmit_ready (session->channel,
849 GNUNET_TIME_UNIT_FOREVER_REL,
853 //disconnect our client
854 if (NULL == session->service_transmit_handle) {
855 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Could not send service-response message via mesh!)\n"));
856 session->state = FINALIZED;
858 session->response->client_notification_task =
859 GNUNET_SCHEDULER_add_now (&prepare_client_end_notification,
863 if (session->transferred != session->used)
865 session->state = WAITING_FOR_MULTIPART_TRANSMISSION;
868 session->state = FINALIZED;
869 GNUNET_free (session->r);
871 GNUNET_free (session->r_prime);
872 session->r_prime = NULL;
882 * (1)[]: $E_A(a_{pi(i)}) otimes E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$
883 * (2)[]: $E_A(a_{pi'(i)}) otimes E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$
884 * S: $S := E_A(sum (r_i + b_i)^2)$
885 * S': $S' := E_A(sum r_i^2)$
887 * @param request the requesting session + bob's requesting peer
888 * @param response the responding session + bob's client handle
889 * @return GNUNET_SYSERR if the computation failed
890 * GNUNET_OK if everything went well.
893 compute_service_response (struct ServiceSession * request,
894 struct ServiceSession * response)
898 int ret = GNUNET_SYSERR;
902 gcry_mpi_t * rand = NULL;
905 struct GNUNET_CRYPTO_PaillierCiphertext * a;
906 struct GNUNET_CRYPTO_PaillierCiphertext * r;
907 struct GNUNET_CRYPTO_PaillierCiphertext * r_prime;
908 struct GNUNET_CRYPTO_PaillierCiphertext * s;
909 struct GNUNET_CRYPTO_PaillierCiphertext * s_prime;
912 count = request->used;
914 b = initialize_mpi_vector (count);
915 q = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, count);
916 p = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, count);
917 rand = initialize_mpi_vector (count);
918 r = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_PaillierCiphertext) * count);
919 r_prime = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_PaillierCiphertext) * count);
920 s = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
921 s_prime = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
923 // convert responder session to from long to mpi
924 for (i = 0, j = 0; i < response->total && j < count; i++) {
925 if (request->mask[i / 8] & (1 << (i % 8))) {
926 value = response->vector[i] >= 0 ? response->vector[i] : -response->vector[i];
927 // long to gcry_mpi_t
928 if (0 > response->vector[i])
929 gcry_mpi_sub_ui (b[j], b[j], value);
931 b[j] = gcry_mpi_set_ui (b[j], value);
935 GNUNET_free (response->vector);
936 response->vector = NULL;
938 for (i = 0; i < count; i++) {
941 svalue = (int32_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX);
943 // long to gcry_mpi_t
945 gcry_mpi_sub_ui (rand[i],
949 rand[i] = gcry_mpi_set_ui (rand[i], svalue);
952 tmp = gcry_mpi_new (0);
953 // encrypt the element
954 // for the sake of readability I decided to have dedicated permutation
955 // vectors, which get rid of all the lookups in p/q.
956 // however, ap/aq are not absolutely necessary but are just abstraction
957 // Calculate Kp = E(S + a_pi) (+) E(S - r_pi - b_pi)
958 for (i = 0; i < count; i++) {
959 // E(S - r_pi - b_pi)
960 gcry_mpi_sub (tmp, my_offset, rand[p[i]]);
961 gcry_mpi_sub (tmp, tmp, b[p[i]]);
962 GNUNET_CRYPTO_paillier_encrypt (request->remote_pubkey,
967 // E(S - r_pi - b_pi) * E(S + a_pi) == E(2*S + a - r - b)
968 GNUNET_CRYPTO_paillier_hom_add (request->remote_pubkey,
974 // Calculate Kq = E(S + a_qi) (+) E(S - r_qi)
975 for (i = 0; i < count; i++) {
977 gcry_mpi_sub (tmp, my_offset, rand[q[i]]);
978 GNUNET_assert (2 == GNUNET_CRYPTO_paillier_encrypt (request->remote_pubkey,
983 // E(S - r_qi) * E(S + a_qi) == E(2*S + a_qi - r_qi)
984 GNUNET_assert (1 == GNUNET_CRYPTO_paillier_hom_add (request->remote_pubkey,
990 // Calculate S' = E(SUM( r_i^2 ))
991 tmp = compute_square_sum (rand, count);
992 GNUNET_CRYPTO_paillier_encrypt (request->remote_pubkey,
997 // Calculate S = E(SUM( (r_i + b_i)^2 ))
998 for (i = 0; i < count; i++)
999 gcry_mpi_add (rand[i], rand[i], b[i]);
1000 tmp = compute_square_sum (rand, count);
1001 GNUNET_CRYPTO_paillier_encrypt (request->remote_pubkey,
1007 request->r_prime = r_prime;
1009 request->s_prime = s_prime;
1010 request->response = response;
1012 // release rand, b and a
1013 for (i = 0; i < count; i++) {
1014 gcry_mpi_release (rand[i]);
1015 gcry_mpi_release (b[i]);
1016 gcry_mpi_release (request->a[i]);
1018 gcry_mpi_release (tmp);
1019 GNUNET_free (request->a);
1026 // copy the r[], r_prime[], S and Stick into a new message, prepare_service_response frees these
1027 if (GNUNET_YES != prepare_service_response (request))
1028 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Failed to communicate with `%s', scalar product calculation aborted.\n"),
1029 GNUNET_i2s (&request->peer));
1038 * Send a multi part chunk of a service request from alice to bob.
1039 * This element only contains a part of the elements-vector (session->a[]),
1040 * mask and public key set have to be contained within the first message
1042 * This allows a ~32kbit key length while using 32000 elements or 62000 elements per request.
1044 * @param cls the associated service session
1047 prepare_service_request_multipart (void *cls)
1049 struct ServiceSession * session = cls;
1050 struct GNUNET_SCALARPRODUCT_multipart_message * msg;
1051 struct GNUNET_CRYPTO_PaillierCiphertext * payload;
1054 uint32_t msg_length;
1055 uint32_t todo_count;
1059 msg_length = sizeof (struct GNUNET_SCALARPRODUCT_multipart_message);
1060 todo_count = session->used - session->transferred;
1062 if (todo_count > MULTIPART_ELEMENT_CAPACITY)
1063 // send the currently possible maximum chunk
1064 todo_count = MULTIPART_ELEMENT_CAPACITY;
1066 msg_length += todo_count * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext);
1067 msg = GNUNET_malloc (msg_length);
1068 msg->header.type = htons (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_TO_BOB_MULTIPART);
1069 msg->header.size = htons (msg_length);
1070 msg->multipart_element_count = htonl (todo_count);
1072 a = gcry_mpi_new (0);
1073 payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
1074 // encrypt our vector and generate string representations
1075 for (i = session->last_processed, j = 0; i < session->total; i++) {
1076 // is this a used element?
1077 if (session->mask[i / 8] & 1 << (i % 8)) {
1078 if (todo_count <= j)
1079 break; //reached end of this message, can't include more
1081 value = session->vector[i] >= 0 ? session->vector[i] : -session->vector[i];
1083 a = gcry_mpi_set_ui (a, 0);
1084 // long to gcry_mpi_t
1085 if (session->vector[i] < 0)
1086 gcry_mpi_sub_ui (a, a, value);
1088 gcry_mpi_add_ui (a, a, value);
1090 session->a[session->transferred + j] = gcry_mpi_set (NULL, a);
1091 gcry_mpi_add (a, a, my_offset);
1092 GNUNET_CRYPTO_paillier_encrypt (&my_pubkey, a, 3, &payload[j++]);
1095 gcry_mpi_release (a);
1096 session->transferred += todo_count;
1098 session->msg = (struct GNUNET_MessageHeader *) msg;
1099 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Transmitting service request.\n"));
1101 //transmit via mesh messaging
1102 session->service_transmit_handle = GNUNET_MESH_notify_transmit_ready (session->channel, GNUNET_YES,
1103 GNUNET_TIME_UNIT_FOREVER_REL,
1107 if (!session->service_transmit_handle) {
1108 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Could not send service-request multipart message to channel!\n"));
1110 session->msg = NULL;
1111 session->client_notification_task =
1112 GNUNET_SCHEDULER_add_now (&prepare_client_end_notification,
1116 if (session->transferred != session->used) {
1117 session->last_processed = i;
1121 session->state = WAITING_FOR_SERVICE_RESPONSE;
1126 * Executed by Alice, fills in a service-request message and sends it to the given peer
1128 * @param cls the session associated with this request
1129 * @param tc task context handed over by scheduler, unsued
1132 prepare_service_request (void *cls,
1133 const struct GNUNET_SCHEDULER_TaskContext *tc)
1135 struct ServiceSession * session = cls;
1136 unsigned char * current;
1137 struct GNUNET_SCALARPRODUCT_service_request * msg;
1138 struct GNUNET_CRYPTO_PaillierCiphertext * payload;
1141 uint32_t msg_length;
1145 session->service_request_task = GNUNET_SCHEDULER_NO_TASK;
1147 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Successfully created new channel to peer (%s)!\n"), GNUNET_i2s (&session->peer));
1149 msg_length = sizeof (struct GNUNET_SCALARPRODUCT_service_request)
1150 + session->mask_length
1151 + sizeof(struct GNUNET_CRYPTO_PaillierPublicKey);
1153 if (GNUNET_SERVER_MAX_MESSAGE_SIZE > msg_length + session->used * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)) {
1154 msg_length += session->used * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext);
1155 session->transferred = session->used;
1158 //create a multipart msg, first we calculate a new msg size for the head msg
1159 session->transferred = (GNUNET_SERVER_MAX_MESSAGE_SIZE - 1 - msg_length) / sizeof(struct GNUNET_CRYPTO_PaillierCiphertext);
1162 msg = GNUNET_malloc (msg_length);
1163 msg->header.type = htons (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_TO_BOB);
1164 msg->total_element_count = htonl (session->used);
1165 msg->contained_element_count = htonl (session->transferred);
1166 memcpy (&msg->key, &session->key, sizeof (struct GNUNET_HashCode));
1167 msg->mask_length = htonl (session->mask_length);
1168 msg->element_count = htonl (session->total);
1169 msg->header.size = htons (msg_length);
1171 // fill in the payload
1172 current = (unsigned char *) &msg[1];
1173 // copy over the mask
1174 memcpy (current, session->mask, session->mask_length);
1175 // copy over our public key
1176 current += session->mask_length;
1177 memcpy (current, &my_pubkey, sizeof(struct GNUNET_CRYPTO_PaillierPublicKey));
1178 current += sizeof(struct GNUNET_CRYPTO_PaillierPublicKey);
1179 payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) current;
1181 // now copy over the element vector
1182 session->a = GNUNET_malloc (sizeof (gcry_mpi_t) * session->used);
1183 a = gcry_mpi_new (0);
1184 // encrypt our vector and generate string representations
1185 for (i = 0, j = 0; i < session->total; i++) {
1186 // if this is a used element...
1187 if (session->mask[i / 8] & 1 << (i % 8)) {
1188 if (session->transferred <= j)
1189 break; //reached end of this message, can't include more
1191 value = session->vector[i] >= 0 ? session->vector[i] : -session->vector[i];
1193 a = gcry_mpi_set_ui (a, 0);
1194 // long to gcry_mpi_t
1195 if (session->vector[i] < 0)
1196 gcry_mpi_sub_ui (a, a, value);
1198 gcry_mpi_add_ui (a, a, value);
1200 session->a[j] = gcry_mpi_set (NULL, a);
1201 gcry_mpi_add (a, a, my_offset);
1202 GNUNET_CRYPTO_paillier_encrypt (&my_pubkey, a, 3, &payload[j++]);
1205 gcry_mpi_release (a);
1207 session->msg = (struct GNUNET_MessageHeader *) msg;
1208 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Transmitting service request.\n"));
1210 //transmit via mesh messaging
1211 session->service_transmit_handle = GNUNET_MESH_notify_transmit_ready (session->channel, GNUNET_YES,
1212 GNUNET_TIME_UNIT_FOREVER_REL,
1216 if (!session->service_transmit_handle) {
1217 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Could not send message to channel!\n"));
1219 session->msg = NULL;
1220 session->client_notification_task =
1221 GNUNET_SCHEDULER_add_now (&prepare_client_end_notification,
1225 if (session->transferred != session->used) {
1226 session->state = WAITING_FOR_MULTIPART_TRANSMISSION;
1227 session->last_processed = i;
1230 //singlepart message
1231 session->state = WAITING_FOR_SERVICE_RESPONSE;
1236 * Handler for a client request message.
1237 * Can either be type A or B
1238 * A: request-initiation to compute a scalar product with a peer
1239 * B: response role, keep the values + session and wait for a matching session or process a waiting request
1241 * @param cls closure
1242 * @param client identification of the client
1243 * @param message the actual message
1246 handle_client_request (void *cls,
1247 struct GNUNET_SERVER_Client *client,
1248 const struct GNUNET_MessageHeader *message)
1250 const struct GNUNET_SCALARPRODUCT_client_request * msg = (const struct GNUNET_SCALARPRODUCT_client_request *) message;
1251 struct ServiceSession * session;
1252 uint32_t element_count;
1253 uint32_t mask_length;
1258 // only one concurrent session per client connection allowed, simplifies logics a lot...
1259 session = GNUNET_SERVER_client_get_user_context (client, struct ServiceSession);
1260 if ((NULL != session) && (session->state != FINALIZED)) {
1261 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1264 else if (NULL != session) {
1265 // old session is already completed, clean it up
1266 GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session);
1267 free_session_variables (session);
1268 GNUNET_free (session);
1271 //we need at least a peer and one message id to compare
1272 if (sizeof (struct GNUNET_SCALARPRODUCT_client_request) > ntohs (msg->header.size)) {
1273 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1274 _ ("Too short message received from client!\n"));
1275 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1279 msg_type = ntohs (msg->header.type);
1280 element_count = ntohl (msg->element_count);
1281 mask_length = ntohl (msg->mask_length);
1283 //sanity check: is the message as long as the message_count fields suggests?
1284 if ((ntohs (msg->header.size) != (sizeof (struct GNUNET_SCALARPRODUCT_client_request) +element_count * sizeof (int32_t) + mask_length))
1285 || (0 == element_count)) {
1286 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1287 _ ("Invalid message received from client, session information incorrect!\n"));
1288 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1292 // do we have a duplicate session here already?
1293 if (NULL != find_matching_session (from_client_tail,
1297 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1298 _ ("Duplicate session information received, cannot create new session with key `%s'\n"),
1299 GNUNET_h2s (&msg->key));
1300 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1304 session = GNUNET_new (struct ServiceSession);
1305 session->service_request_task = GNUNET_SCHEDULER_NO_TASK;
1306 session->client_notification_task = GNUNET_SCHEDULER_NO_TASK;
1307 session->client = client;
1308 session->total = element_count;
1309 session->mask_length = mask_length;
1310 // get our transaction key
1311 memcpy (&session->key, &msg->key, sizeof (struct GNUNET_HashCode));
1312 //allocate memory for vector and encrypted vector
1313 session->vector = GNUNET_malloc (sizeof (int32_t) * element_count);
1314 vector = (int32_t *) & msg[1];
1316 if (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE == msg_type) {
1317 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1318 _ ("Got client-request-session with key %s, preparing channel to remote service.\n"),
1319 GNUNET_h2s (&session->key));
1321 session->role = ALICE;
1323 session->mask = GNUNET_malloc (mask_length);
1324 memcpy (session->mask, &vector[element_count], mask_length);
1326 // copy over the elements
1328 for (i = 0; i < element_count; i++) {
1329 session->vector[i] = ntohl (vector[i]);
1330 if (session->vector[i] == 0)
1331 session->mask[i / 8] &= ~(1 << (i % 8));
1332 if (session->mask[i / 8] & (1 << (i % 8)))
1336 if (0 == session->used) {
1337 GNUNET_break_op (0);
1338 GNUNET_free (session->vector);
1339 GNUNET_free (session);
1340 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1343 //session with ourself makes no sense!
1344 if (!memcmp (&msg->peer, &me, sizeof (struct GNUNET_PeerIdentity))) {
1346 GNUNET_free (session->vector);
1347 GNUNET_free (session);
1348 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1352 memcpy (&session->peer, &msg->peer, sizeof (struct GNUNET_PeerIdentity));
1353 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1354 _ ("Creating new channel for session with key %s.\n"),
1355 GNUNET_h2s (&session->key));
1356 session->channel = GNUNET_MESH_channel_create (my_mesh, session,
1358 GNUNET_APPLICATION_TYPE_SCALARPRODUCT,
1359 GNUNET_MESH_OPTION_RELIABLE);
1360 //prepare_service_request, channel_peer_disconnect_handler,
1361 if (!session->channel) {
1363 GNUNET_free (session->vector);
1364 GNUNET_free (session);
1365 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1368 GNUNET_SERVER_client_set_user_context (client, session);
1369 GNUNET_CONTAINER_DLL_insert (from_client_head, from_client_tail, session);
1371 session->state = CLIENT_REQUEST_RECEIVED;
1372 session->service_request_task =
1373 GNUNET_SCHEDULER_add_now (&prepare_service_request,
1378 struct ServiceSession * requesting_session;
1379 enum SessionState needed_state = SERVICE_REQUEST_RECEIVED;
1381 session->role = BOB;
1382 session->mask = NULL;
1383 // copy over the elements
1384 session->used = element_count;
1385 for (i = 0; i < element_count; i++)
1386 session->vector[i] = ntohl (vector[i]);
1387 session->state = CLIENT_RESPONSE_RECEIVED;
1389 GNUNET_SERVER_client_set_user_context (client, session);
1390 GNUNET_CONTAINER_DLL_insert (from_client_head, from_client_tail, session);
1392 //check if service queue contains a matching request
1393 requesting_session = find_matching_session (from_service_tail,
1396 &needed_state, NULL);
1397 if (NULL != requesting_session) {
1398 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1399 _ ("Got client-responder-session with key %s and a matching service-request-session set, processing.\n"),
1400 GNUNET_h2s (&session->key));
1401 if (GNUNET_OK != compute_service_response (requesting_session, session))
1402 session->client_notification_task =
1403 GNUNET_SCHEDULER_add_now (&prepare_client_end_notification,
1408 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1409 _ ("Got client-responder-session with key %s but NO matching service-request-session set, queuing element for later use.\n"),
1410 GNUNET_h2s (&session->key));
1411 // no matching session exists yet, store the response
1412 // for later processing by handle_service_request()
1415 GNUNET_SERVER_receive_done (client, GNUNET_YES);
1420 * Function called for inbound channels.
1422 * @param cls closure
1423 * @param channel new handle to the channel
1424 * @param initiator peer that started the channel
1425 * @param port unused
1426 * @param options unused
1428 * @return session associated with the channel
1431 channel_incoming_handler (void *cls,
1432 struct GNUNET_MESH_Channel *channel,
1433 const struct GNUNET_PeerIdentity *initiator,
1434 uint32_t port, enum GNUNET_MESH_ChannelOption options)
1436 struct ServiceSession * c = GNUNET_new (struct ServiceSession);
1438 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1439 _ ("New incoming channel from peer %s.\n"),
1440 GNUNET_i2s (initiator));
1442 c->peer = *initiator;
1443 c->channel = channel;
1445 c->state = WAITING_FOR_SERVICE_REQUEST;
1451 * Function called whenever a channel is destroyed. Should clean up
1452 * any associated state.
1454 * It must NOT call GNUNET_MESH_channel_destroy on the channel.
1456 * @param cls closure (set from GNUNET_MESH_connect)
1457 * @param channel connection to the other end (henceforth invalid)
1458 * @param channel_ctx place where local state associated
1459 * with the channel is stored
1462 channel_destruction_handler (void *cls,
1463 const struct GNUNET_MESH_Channel *channel,
1466 struct ServiceSession * session = channel_ctx;
1467 struct ServiceSession * client_session;
1468 struct ServiceSession * curr;
1470 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1471 _ ("Peer disconnected, terminating session %s with peer (%s)\n"),
1472 GNUNET_h2s (&session->key),
1473 GNUNET_i2s (&session->peer));
1474 if (ALICE == session->role) {
1475 // as we have only one peer connected in each session, just remove the session
1477 if ((SERVICE_RESPONSE_RECEIVED > session->state) && (!do_shutdown)) {
1478 session->channel = NULL;
1479 // if this happened before we received the answer, we must terminate the session
1480 session->client_notification_task =
1481 GNUNET_SCHEDULER_add_now (&prepare_client_end_notification,
1485 else { //(BOB == session->role) service session
1486 // remove the session, unless it has already been dequeued, but somehow still active
1487 // this could bug without the IF in case the queue is empty and the service session was the only one know to the service
1488 // scenario: disconnect before alice can send her message to bob.
1489 for (curr = from_service_head; NULL != curr; curr = curr->next)
1490 if (curr == session) {
1491 GNUNET_CONTAINER_DLL_remove (from_service_head, from_service_tail, curr);
1494 // there is a client waiting for this service session, terminate it, too!
1495 // i assume the tupel of key and element count is unique. if it was not the rest of the code would not work either.
1496 client_session = find_matching_session (from_client_tail,
1500 free_session_variables (session);
1501 GNUNET_free (session);
1503 // the client has to check if it was waiting for a result
1504 // or if it was a responder, no point in adding more statefulness
1505 if (client_session && (!do_shutdown)) {
1506 client_session->state = FINALIZED;
1507 client_session->client_notification_task =
1508 GNUNET_SCHEDULER_add_now (&prepare_client_end_notification,
1516 * Compute our scalar product, done by Alice
1518 * @param session - the session associated with this computation
1519 * @return product as MPI, never NULL
1522 compute_scalar_product (struct ServiceSession * session)
1531 gcry_mpi_t r[session->used];
1532 gcry_mpi_t r_prime[session->used];
1537 count = session->used;
1538 // due to the introduced static offset S, we now also have to remove this
1539 // from the E(a_pi)(+)E(-b_pi-r_pi) and E(a_qi)(+)E(-r_qi) twice each,
1540 // the result is E((S + a_pi) + (S -b_pi-r_pi)) and E(S + a_qi + S - r_qi)
1541 for (i = 0; i < count; i++) {
1542 GNUNET_CRYPTO_paillier_decrypt (&my_privkey, &my_pubkey,
1543 &session->r[i], r[i]);
1544 gcry_mpi_sub (r[i], r[i], my_offset);
1545 gcry_mpi_sub (r[i], r[i], my_offset);
1546 GNUNET_CRYPTO_paillier_decrypt (&my_privkey, &my_pubkey,
1547 &session->r_prime[i], r_prime[i]);
1548 gcry_mpi_sub (r_prime[i], r_prime[i], my_offset);
1549 gcry_mpi_sub (r_prime[i], r_prime[i], my_offset);
1552 // calculate t = sum(ai)
1553 t = compute_square_sum (session->a, count);
1556 u = gcry_mpi_new (0);
1557 tmp = compute_square_sum (r, count);
1558 gcry_mpi_sub (u, u, tmp);
1559 gcry_mpi_release (tmp);
1562 u_prime = gcry_mpi_new (0);
1563 tmp = compute_square_sum (r_prime, count);
1564 gcry_mpi_sub (u_prime, u_prime, tmp);
1566 GNUNET_assert (p = gcry_mpi_new (0));
1567 GNUNET_assert (p_prime = gcry_mpi_new (0));
1568 GNUNET_assert (s = gcry_mpi_new (0));
1569 GNUNET_assert (s_prime = gcry_mpi_new (0));
1572 GNUNET_CRYPTO_paillier_decrypt (&my_privkey, &my_pubkey,
1574 GNUNET_CRYPTO_paillier_decrypt (&my_privkey, &my_pubkey,
1575 session->s_prime, s_prime);
1578 gcry_mpi_add (p, s, t);
1579 gcry_mpi_add (p, p, u);
1582 gcry_mpi_add (p_prime, s_prime, t);
1583 gcry_mpi_add (p_prime, p_prime, u_prime);
1585 gcry_mpi_release (t);
1586 gcry_mpi_release (u);
1587 gcry_mpi_release (u_prime);
1588 gcry_mpi_release (s);
1589 gcry_mpi_release (s_prime);
1592 gcry_mpi_sub (p, p, p_prime);
1593 gcry_mpi_release (p_prime);
1594 tmp = gcry_mpi_set_ui (tmp, 2);
1595 gcry_mpi_div (p, NULL, p, tmp, 0);
1597 gcry_mpi_release (tmp);
1598 for (i = 0; i < count; i++){
1599 gcry_mpi_release (session->a[i]);
1600 gcry_mpi_release (r[i]);
1601 gcry_mpi_release (r_prime[i]);
1603 GNUNET_free (session->a);
1605 GNUNET_free (session->s);
1607 GNUNET_free (session->s_prime);
1608 session->s_prime = NULL;
1609 GNUNET_free (session->r);
1611 GNUNET_free (session->r_prime);
1612 session->r_prime = NULL;
1619 * Handle a multipart-chunk of a request from another service to calculate a scalarproduct with us.
1621 * @param cls closure (set from #GNUNET_MESH_connect)
1622 * @param channel connection to the other end
1623 * @param channel_ctx place to store local state associated with the channel
1624 * @param message the actual message
1625 * @return #GNUNET_OK to keep the connection open,
1626 * #GNUNET_SYSERR to close it (signal serious error)
1629 handle_service_request_multipart (void *cls,
1630 struct GNUNET_MESH_Channel * channel,
1632 const struct GNUNET_MessageHeader * message)
1634 struct ServiceSession * session;
1635 const struct GNUNET_SCALARPRODUCT_multipart_message * msg = (const struct GNUNET_SCALARPRODUCT_multipart_message *) message;
1636 struct GNUNET_CRYPTO_PaillierCiphertext *payload;
1637 uint32_t used_elements;
1638 uint32_t contained_elements = 0;
1639 uint32_t msg_length;
1641 // are we in the correct state?
1642 session = (struct ServiceSession *) * channel_ctx;
1643 if ((BOB != session->role) || (WAITING_FOR_MULTIPART_TRANSMISSION != session->state)) {
1646 // shorter than minimum?
1647 if (ntohs (msg->header.size) <= sizeof (struct GNUNET_SCALARPRODUCT_multipart_message)) {
1650 used_elements = session->used;
1651 contained_elements = ntohl (msg->multipart_element_count);
1652 msg_length = sizeof (struct GNUNET_SCALARPRODUCT_multipart_message)
1653 + contained_elements * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext);
1655 if ((ntohs (msg->header.size) != msg_length)
1656 || (used_elements < contained_elements + session->transferred)) {
1659 payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
1660 if (contained_elements != 0) {
1661 // Convert each vector element to MPI_value
1662 memcpy(&session->e_a[session->transferred], payload,
1663 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext) * contained_elements);
1665 session->transferred += contained_elements;
1667 if (session->transferred == used_elements) {
1668 // single part finished
1669 session->state = SERVICE_REQUEST_RECEIVED;
1670 if (session->response) {
1671 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1672 _ ("Got session with key %s and a matching element set, processing.\n"),
1673 GNUNET_h2s (&session->key));
1674 if (GNUNET_OK != compute_service_response (session, session->response)) {
1675 //something went wrong, remove it again...
1680 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1681 _ ("Got session with key %s without a matching element set, queueing.\n"),
1682 GNUNET_h2s (&session->key));
1685 // multipart message
1691 // and notify our client-session that we could not complete the session
1692 GNUNET_CONTAINER_DLL_remove (from_service_head, from_service_tail, session);
1693 if (session->response)
1694 // we just found the responder session in this queue
1695 session->response->client_notification_task =
1696 GNUNET_SCHEDULER_add_now (&prepare_client_end_notification,
1698 free_session_variables (session);
1699 GNUNET_free (session);
1700 return GNUNET_SYSERR;
1705 * Handle a request from another service to calculate a scalarproduct with us.
1707 * @param cls closure (set from #GNUNET_MESH_connect)
1708 * @param channel connection to the other end
1709 * @param channel_ctx place to store local state associated with the channel
1710 * @param message the actual message
1711 * @return #GNUNET_OK to keep the connection open,
1712 * #GNUNET_SYSERR to close it (signal serious error)
1715 handle_service_request (void *cls,
1716 struct GNUNET_MESH_Channel * channel,
1718 const struct GNUNET_MessageHeader * message)
1720 struct ServiceSession * session;
1721 const struct GNUNET_SCALARPRODUCT_service_request * msg = (const struct GNUNET_SCALARPRODUCT_service_request *) message;
1722 uint32_t mask_length;
1723 struct GNUNET_CRYPTO_PaillierCiphertext *payload;
1724 uint32_t used_elements;
1725 uint32_t contained_elements = 0;
1726 uint32_t element_count;
1727 uint32_t msg_length;
1728 unsigned char * current;
1729 enum SessionState needed_state;
1731 session = (struct ServiceSession *) * channel_ctx;
1732 if (WAITING_FOR_SERVICE_REQUEST != session->state) {
1735 // Check if message was sent by me, which would be bad!
1736 if (!memcmp (&session->peer, &me, sizeof (struct GNUNET_PeerIdentity))) {
1737 GNUNET_free (session);
1739 return GNUNET_SYSERR;
1741 // shorter than expected?
1742 if (ntohs (msg->header.size) < sizeof (struct GNUNET_SCALARPRODUCT_service_request)) {
1743 GNUNET_free (session);
1744 GNUNET_break_op (0);
1745 return GNUNET_SYSERR;
1747 mask_length = ntohl (msg->mask_length);
1748 used_elements = ntohl (msg->total_element_count);
1749 contained_elements = ntohl (msg->contained_element_count);
1750 element_count = ntohl (msg->element_count);
1751 msg_length = sizeof (struct GNUNET_SCALARPRODUCT_service_request)
1752 + mask_length + sizeof(struct GNUNET_CRYPTO_PaillierPublicKey)
1753 + contained_elements * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext);
1755 //sanity check: is the message as long as the message_count fields suggests?
1756 if ((ntohs (msg->header.size) != msg_length) ||
1757 (element_count < used_elements) ||
1758 (used_elements < contained_elements) ||
1759 (0 == used_elements) ||
1760 (mask_length != (element_count / 8 + ((element_count % 8) ? 1 : 0)))) {
1761 GNUNET_free (session);
1762 GNUNET_break_op (0);
1763 return GNUNET_SYSERR;
1765 if (find_matching_session (from_service_tail,
1770 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1771 _ ("Got message with duplicate session key (`%s'), ignoring service request.\n"),
1772 (const char *) &(msg->key));
1773 GNUNET_free (session);
1774 return GNUNET_SYSERR;
1777 session->total = element_count;
1778 session->used = used_elements;
1779 session->transferred = contained_elements;
1780 session->channel = channel;
1783 memcpy (&session->key, &msg->key, sizeof (struct GNUNET_HashCode));
1784 current = (unsigned char *) &msg[1];
1785 //preserve the mask, we will need that later on
1786 session->mask = GNUNET_malloc (mask_length);
1787 memcpy (session->mask, current, mask_length);
1789 current += mask_length;
1791 //convert the publickey to sexp
1792 session->remote_pubkey = GNUNET_malloc(sizeof(struct GNUNET_CRYPTO_PaillierPublicKey));
1793 memcpy(session->remote_pubkey, current, sizeof(struct GNUNET_CRYPTO_PaillierPublicKey));
1794 current += sizeof(struct GNUNET_CRYPTO_PaillierPublicKey);
1796 payload = (struct GNUNET_CRYPTO_PaillierCiphertext*) current;
1797 //check if service queue contains a matching request
1798 needed_state = CLIENT_RESPONSE_RECEIVED;
1799 session->response = find_matching_session (from_client_tail,
1802 &needed_state, NULL);
1803 session->state = WAITING_FOR_MULTIPART_TRANSMISSION;
1804 GNUNET_CONTAINER_DLL_insert (from_service_head, from_service_tail, session);
1806 session->e_a = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_PaillierCiphertext) * used_elements);
1807 if (contained_elements != 0) {
1808 // Convert each vector element to MPI_value
1809 memcpy(session->e_a, payload, sizeof (struct GNUNET_CRYPTO_PaillierCiphertext) * used_elements);
1810 if (contained_elements == used_elements) {
1811 // single part finished
1812 session->state = SERVICE_REQUEST_RECEIVED;
1813 if (session->response) {
1814 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Got session with key %s and a matching element set, processing.\n"), GNUNET_h2s (&session->key));
1815 if (GNUNET_OK != compute_service_response (session, session->response)) {
1816 //something went wrong, remove it again...
1821 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Got session with key %s without a matching element set, queueing.\n"), GNUNET_h2s (&session->key));
1824 // multipart message
1829 GNUNET_break_op (0);
1830 if ((NULL != session->next) || (NULL != session->prev) || (from_service_head == session))
1831 GNUNET_CONTAINER_DLL_remove (from_service_head, from_service_tail, session);
1832 // and notify our client-session that we could not complete the session
1833 if (session->response)
1834 // we just found the responder session in this queue
1835 session->response->client_notification_task =
1836 GNUNET_SCHEDULER_add_now (&prepare_client_end_notification,
1838 free_session_variables (session);
1839 return GNUNET_SYSERR;
1844 * Handle a multipart chunk of a response we got from another service we wanted to calculate a scalarproduct with.
1846 * @param cls closure (set from #GNUNET_MESH_connect)
1847 * @param channel connection to the other end
1848 * @param channel_ctx place to store local state associated with the channel
1849 * @param message the actual message
1850 * @return #GNUNET_OK to keep the connection open,
1851 * #GNUNET_SYSERR to close it (signal serious error)
1854 handle_service_response_multipart (void *cls,
1855 struct GNUNET_MESH_Channel * channel,
1857 const struct GNUNET_MessageHeader * message)
1859 struct ServiceSession * session;
1860 const struct GNUNET_SCALARPRODUCT_multipart_message * msg = (const struct GNUNET_SCALARPRODUCT_multipart_message *) message;
1861 struct GNUNET_CRYPTO_PaillierCiphertext * payload;
1863 uint32_t contained = 0;
1865 size_t required_size;
1867 GNUNET_assert (NULL != message);
1868 // are we in the correct state?
1869 session = (struct ServiceSession *) * channel_ctx;
1870 if ((ALICE != session->role) || (WAITING_FOR_MULTIPART_TRANSMISSION != session->state)) {
1873 msg_size = ntohs (msg->header.size);
1874 required_size = sizeof (struct GNUNET_SCALARPRODUCT_multipart_message)
1875 + 2 * sizeof (struct GNUNET_CRYPTO_PaillierCiphertext);
1876 // shorter than minimum?
1877 if (required_size > msg_size) {
1880 contained = ntohl (msg->multipart_element_count);
1881 required_size = sizeof (struct GNUNET_SCALARPRODUCT_multipart_message)
1882 + 2 * contained * sizeof (struct GNUNET_CRYPTO_PaillierCiphertext);
1883 //sanity check: is the message as long as the message_count fields suggests?
1884 if ((required_size != msg_size) || (session->used < session->transferred + contained)) {
1887 payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
1888 // Convert each k[][perm] to its MPI_value
1889 for (i = 0; i < contained; i++) {
1890 memcpy(&session->r[session->transferred+i], &payload[2*i], sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
1891 memcpy(&session->r_prime[session->transferred+i], &payload[2*i], sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
1893 session->transferred += contained;
1894 if (session->transferred != session->used)
1896 session->state = SERVICE_RESPONSE_RECEIVED;
1897 session->product = compute_scalar_product (session); //never NULL
1900 GNUNET_break_op (NULL != session->product); //NULL if we never tried to compute it...
1902 // send message with product to client
1903 if (ALICE == session->role) {
1904 session->state = FINALIZED;
1905 session->channel = NULL;
1906 session->client_notification_task =
1907 GNUNET_SCHEDULER_add_now (&prepare_client_response,
1910 // the channel has done its job, terminate our connection and the channel
1911 // the peer will be notified that the channel was destroyed via channel_destruction_handler
1912 // just close the connection, as recommended by Christian
1913 return GNUNET_SYSERR;
1918 * Handle a response we got from another service we wanted to calculate a scalarproduct with.
1920 * @param cls closure (set from #GNUNET_MESH_connect)
1921 * @param channel connection to the other end
1922 * @param channel_ctx place to store local state associated with the channel
1923 * @param message the actual message
1924 * @return #GNUNET_OK to keep the connection open,
1925 * #GNUNET_SYSERR to close it (we are done)
1928 handle_service_response (void *cls,
1929 struct GNUNET_MESH_Channel * channel,
1931 const struct GNUNET_MessageHeader * message)
1933 struct ServiceSession * session;
1934 const struct GNUNET_SCALARPRODUCT_service_response * msg = (const struct GNUNET_SCALARPRODUCT_service_response *) message;
1935 struct GNUNET_CRYPTO_PaillierCiphertext * payload;
1937 uint32_t contained = 0;
1939 size_t required_size;
1941 GNUNET_assert (NULL != message);
1942 session = (struct ServiceSession *) * channel_ctx;
1943 // are we in the correct state?
1944 if (WAITING_FOR_SERVICE_RESPONSE != session->state) {
1947 //we need at least a full message without elements attached
1948 msg_size = ntohs (msg->header.size);
1949 required_size = sizeof (struct GNUNET_SCALARPRODUCT_service_response) + 2 * sizeof (struct GNUNET_CRYPTO_PaillierCiphertext);
1951 if (required_size > msg_size) {
1954 contained = ntohl (msg->contained_element_count);
1955 required_size = sizeof (struct GNUNET_SCALARPRODUCT_service_response)
1956 + 2 * contained * sizeof (struct GNUNET_CRYPTO_PaillierCiphertext)
1957 + 2 * sizeof (struct GNUNET_CRYPTO_PaillierCiphertext);
1958 //sanity check: is the message as long as the message_count fields suggests?
1959 if ((msg_size != required_size) || (session->used < contained)) {
1962 session->state = WAITING_FOR_MULTIPART_TRANSMISSION;
1963 session->transferred = contained;
1965 payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
1967 session->s = GNUNET_malloc(sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
1968 session->s_prime = GNUNET_malloc(sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
1969 memcpy(session->s,&payload[0],sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
1970 memcpy(session->s_prime,&payload[1],sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
1972 session->r = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_PaillierCiphertext) * session->used);
1973 session->r_prime = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_PaillierCiphertext) * session->used);
1975 // Convert each k[][perm] to its MPI_value
1976 for (i = 0; i < contained; i++) {
1977 memcpy(&session->r[i], &payload[2 + 2*i], sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
1978 memcpy(&session->r_prime[i], &payload[3 + 2*i], sizeof (struct GNUNET_CRYPTO_PaillierCiphertext));
1980 if (session->transferred != session->used)
1981 return GNUNET_OK; //wait for the other multipart chunks
1983 session->state = SERVICE_RESPONSE_RECEIVED;
1984 session->product = compute_scalar_product (session); //never NULL
1987 GNUNET_break_op (NULL != session->product);
1988 // send message with product to client
1989 if (ALICE == session->role) {
1990 session->state = FINALIZED;
1991 session->channel = NULL;
1992 session->client_notification_task =
1993 GNUNET_SCHEDULER_add_now (&prepare_client_response,
1996 // the channel has done its job, terminate our connection and the channel
1997 // the peer will be notified that the channel was destroyed via channel_destruction_handler
1998 // just close the connection, as recommended by Christian
1999 return GNUNET_SYSERR;
2004 * Task run during shutdown.
2010 shutdown_task (void *cls,
2011 const struct GNUNET_SCHEDULER_TaskContext *tc)
2013 struct ServiceSession * session;
2014 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Shutting down, initiating cleanup.\n"));
2016 do_shutdown = GNUNET_YES;
2018 // terminate all owned open channels.
2019 for (session = from_client_head; NULL != session; session = session->next) {
2020 if ((FINALIZED != session->state) && (NULL != session->channel)) {
2021 GNUNET_MESH_channel_destroy (session->channel);
2022 session->channel = NULL;
2024 if (GNUNET_SCHEDULER_NO_TASK != session->client_notification_task) {
2025 GNUNET_SCHEDULER_cancel (session->client_notification_task);
2026 session->client_notification_task = GNUNET_SCHEDULER_NO_TASK;
2028 if (GNUNET_SCHEDULER_NO_TASK != session->service_request_task) {
2029 GNUNET_SCHEDULER_cancel (session->service_request_task);
2030 session->service_request_task = GNUNET_SCHEDULER_NO_TASK;
2032 if (NULL != session->client) {
2033 GNUNET_SERVER_client_disconnect (session->client);
2034 session->client = NULL;
2037 for (session = from_service_head; NULL != session; session = session->next)
2038 if (NULL != session->channel) {
2039 GNUNET_MESH_channel_destroy (session->channel);
2040 session->channel = NULL;
2044 GNUNET_MESH_disconnect (my_mesh);
2051 * Initialization of the program and message handlers
2053 * @param cls closure
2054 * @param server the initialized server
2055 * @param c configuration to use
2059 struct GNUNET_SERVER_Handle *server,
2060 const struct GNUNET_CONFIGURATION_Handle *c)
2062 static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
2063 {&handle_client_request, NULL, GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE, 0},
2064 {&handle_client_request, NULL, GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB, 0},
2067 static const struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
2068 { &handle_service_request, GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_TO_BOB, 0},
2069 { &handle_service_request_multipart, GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_TO_BOB_MULTIPART, 0},
2070 { &handle_service_response, GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_TO_ALICE, 0},
2071 { &handle_service_response_multipart, GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_TO_ALICE_MULTIPART, 0},
2074 static const uint32_t ports[] = {
2075 GNUNET_APPLICATION_TYPE_SCALARPRODUCT,
2078 //generate private/public key set
2079 GNUNET_CRYPTO_paillier_create (&my_pubkey, &my_privkey);
2081 // offset has to be sufficiently small to allow computation of:
2082 // m1+m2 mod n == (S + a) + (S + b) mod n,
2083 // if we have more complex operations, this factor needs to be lowered
2084 my_offset = gcry_mpi_new (GNUNET_CRYPTO_PAILLIER_BITS / 3);
2085 gcry_mpi_set_bit (my_offset, GNUNET_CRYPTO_PAILLIER_BITS / 3);
2087 // register server callbacks and disconnect handler
2088 GNUNET_SERVER_add_handlers (server, server_handlers);
2089 GNUNET_SERVER_disconnect_notify (server,
2090 &handle_client_disconnect,
2092 GNUNET_break (GNUNET_OK ==
2093 GNUNET_CRYPTO_get_peer_identity (c,
2095 my_mesh = GNUNET_MESH_connect (c, NULL,
2096 &channel_incoming_handler,
2097 &channel_destruction_handler,
2098 mesh_handlers, ports);
2100 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Connect to MESH failed\n"));
2101 GNUNET_SCHEDULER_shutdown ();
2104 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Mesh initialized\n"));
2105 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
2112 * The main function for the scalarproduct service.
2114 * @param argc number of arguments from the command line
2115 * @param argv command line arguments
2116 * @return 0 ok, 1 on error
2119 main (int argc, char *const *argv)
2121 return (GNUNET_OK ==
2122 GNUNET_SERVICE_run (argc, argv,
2124 GNUNET_SERVICE_OPTION_NONE,
2125 &run, NULL)) ? 0 : 1;
2128 /* end of gnunet-service-scalarproduct.c */