diff options
author | Martin Schanzenbach <schanzen@gnunet.org> | 2023-10-19 10:00:01 +0200 |
---|---|---|
committer | Martin Schanzenbach <schanzen@gnunet.org> | 2023-10-19 10:00:01 +0200 |
commit | a93ccc1ebb57f50ce6e90c7f0e57a3e58020b05e (patch) | |
tree | 8d1db5360aeb85b2c41e507869d1d3383c6999c5 /src/contrib | |
parent | 98c00bad3d29a80c514818615c224a29307400cc (diff) | |
download | gnunet-a93ccc1ebb57f50ce6e90c7f0e57a3e58020b05e.tar.gz gnunet-a93ccc1ebb57f50ce6e90c7f0e57a3e58020b05e.zip |
BUILD: Move scalarproduct to contrib/service
Diffstat (limited to 'src/contrib')
20 files changed, 6991 insertions, 0 deletions
diff --git a/src/contrib/service/Makefile.am b/src/contrib/service/Makefile.am index cfab56206..4092cc46c 100644 --- a/src/contrib/service/Makefile.am +++ b/src/contrib/service/Makefile.am | |||
@@ -10,4 +10,5 @@ SUBDIRS = \ | |||
10 | set \ | 10 | set \ |
11 | consensus \ | 11 | consensus \ |
12 | secretsharing \ | 12 | secretsharing \ |
13 | scalarproduct \ | ||
13 | $(EXP_DIR) | 14 | $(EXP_DIR) |
diff --git a/src/contrib/service/scalarproduct/.gitignore b/src/contrib/service/scalarproduct/.gitignore new file mode 100644 index 000000000..19909a3f9 --- /dev/null +++ b/src/contrib/service/scalarproduct/.gitignore | |||
@@ -0,0 +1,6 @@ | |||
1 | gnunet-service-scalarproduct-ecc-bob | ||
2 | gnunet-scalarproduct | ||
3 | gnunet-service-scalarproduct-alice | ||
4 | gnunet-service-scalarproduct-bob | ||
5 | gnunet-service-scalarproduct-ecc-alice | ||
6 | test_ecc_scalarproduct | ||
diff --git a/src/contrib/service/scalarproduct/Makefile.am b/src/contrib/service/scalarproduct/Makefile.am new file mode 100644 index 000000000..77a17ec20 --- /dev/null +++ b/src/contrib/service/scalarproduct/Makefile.am | |||
@@ -0,0 +1,116 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
5 | |||
6 | libexecdir= $(pkglibdir)/libexec/ | ||
7 | |||
8 | pkgcfg_DATA = \ | ||
9 | scalarproduct.conf | ||
10 | |||
11 | if USE_COVERAGE | ||
12 | AM_CFLAGS = -fprofile-arcs -ftest-coverage | ||
13 | endif | ||
14 | |||
15 | bin_PROGRAMS = \ | ||
16 | gnunet-scalarproduct | ||
17 | |||
18 | libexec_PROGRAMS = \ | ||
19 | gnunet-service-scalarproduct-alice \ | ||
20 | gnunet-service-scalarproduct-bob \ | ||
21 | gnunet-service-scalarproduct-ecc-alice \ | ||
22 | gnunet-service-scalarproduct-ecc-bob | ||
23 | |||
24 | lib_LTLIBRARIES = \ | ||
25 | libgnunetscalarproduct.la | ||
26 | |||
27 | gnunet_scalarproduct_SOURCES = \ | ||
28 | gnunet-scalarproduct.c | ||
29 | gnunet_scalarproduct_LDADD = \ | ||
30 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
31 | libgnunetscalarproduct.la \ | ||
32 | $(LIBGCRYPT_LIBS) \ | ||
33 | -lgcrypt \ | ||
34 | $(GN_LIBINTL) | ||
35 | |||
36 | gnunet_service_scalarproduct_alice_SOURCES = \ | ||
37 | gnunet-service-scalarproduct.h \ | ||
38 | gnunet-service-scalarproduct_alice.c | ||
39 | gnunet_service_scalarproduct_alice_LDADD = \ | ||
40 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
41 | $(top_builddir)/src/service/cadet/libgnunetcadet.la \ | ||
42 | $(top_builddir)/src/seti/libgnunetseti.la \ | ||
43 | $(LIBGCRYPT_LIBS) \ | ||
44 | -lgcrypt \ | ||
45 | $(GN_LIBINTL) | ||
46 | |||
47 | gnunet_service_scalarproduct_bob_SOURCES = \ | ||
48 | gnunet-service-scalarproduct.h \ | ||
49 | gnunet-service-scalarproduct_bob.c | ||
50 | gnunet_service_scalarproduct_bob_LDADD = \ | ||
51 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
52 | $(top_builddir)/src/service/cadet/libgnunetcadet.la \ | ||
53 | $(top_builddir)/src/seti/libgnunetseti.la \ | ||
54 | $(LIBGCRYPT_LIBS) \ | ||
55 | -lgcrypt \ | ||
56 | $(GN_LIBINTL) | ||
57 | |||
58 | gnunet_service_scalarproduct_ecc_alice_SOURCES = \ | ||
59 | gnunet-service-scalarproduct-ecc.h \ | ||
60 | gnunet-service-scalarproduct-ecc_alice.c | ||
61 | gnunet_service_scalarproduct_ecc_alice_LDADD = \ | ||
62 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
63 | $(top_builddir)/src/service/cadet/libgnunetcadet.la \ | ||
64 | $(top_builddir)/src/seti/libgnunetseti.la \ | ||
65 | $(LIBGCRYPT_LIBS) \ | ||
66 | -lsodium \ | ||
67 | -lgcrypt \ | ||
68 | $(GN_LIBINTL) | ||
69 | |||
70 | gnunet_service_scalarproduct_ecc_bob_SOURCES = \ | ||
71 | gnunet-service-scalarproduct-ecc.h \ | ||
72 | gnunet-service-scalarproduct-ecc_bob.c | ||
73 | gnunet_service_scalarproduct_ecc_bob_LDADD = \ | ||
74 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
75 | $(top_builddir)/src/service/cadet/libgnunetcadet.la \ | ||
76 | $(top_builddir)/src/seti/libgnunetseti.la \ | ||
77 | $(LIBGCRYPT_LIBS) \ | ||
78 | -lsodium \ | ||
79 | -lgcrypt \ | ||
80 | $(GN_LIBINTL) | ||
81 | |||
82 | libgnunetscalarproduct_la_SOURCES = \ | ||
83 | scalarproduct_api.c \ | ||
84 | scalarproduct.h | ||
85 | libgnunetscalarproduct_la_LIBADD = \ | ||
86 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
87 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
88 | $(LIBGCRYPT_LIBS) \ | ||
89 | -lgcrypt \ | ||
90 | $(LTLIBINTL) | ||
91 | libgnunetscalarproduct_la_LDFLAGS = \ | ||
92 | $(GN_LIB_LDFLAGS) | ||
93 | |||
94 | EXTRA_DIST = \ | ||
95 | test_scalarproduct.conf \ | ||
96 | $(check_SCRIPTS) | ||
97 | |||
98 | check_SCRIPTS = \ | ||
99 | test_scalarproduct.sh \ | ||
100 | test_scalarproduct_negative.sh \ | ||
101 | test_scalarproduct_negativezero.sh | ||
102 | |||
103 | check_PROGRAMS = \ | ||
104 | # test_ecc_scalarproduct | ||
105 | |||
106 | if ENABLE_TEST_RUN | ||
107 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
108 | TESTS = $(check_SCRIPTS) $(check_PROGRAMS) | ||
109 | endif | ||
110 | |||
111 | |||
112 | test_ecc_scalarproduct_SOURCES = \ | ||
113 | test_ecc_scalarproduct.c | ||
114 | test_ecc_scalarproduct_LDADD = \ | ||
115 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
116 | -lsodium | ||
diff --git a/src/contrib/service/scalarproduct/gnunet-scalarproduct.c b/src/contrib/service/scalarproduct/gnunet-scalarproduct.c new file mode 100644 index 000000000..5ebab5baf --- /dev/null +++ b/src/contrib/service/scalarproduct/gnunet-scalarproduct.c | |||
@@ -0,0 +1,401 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2013, 2014 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 | /** | ||
22 | * @file scalarproduct/gnunet-scalarproduct.c | ||
23 | * @brief scalarproduct client | ||
24 | * @author Christian M. Fuchs | ||
25 | */ | ||
26 | #define GCRYPT_NO_DEPRECATED | ||
27 | #include "platform.h" | ||
28 | #include <gcrypt.h> | ||
29 | #include <inttypes.h> | ||
30 | |||
31 | #include "gnunet_util_lib.h" | ||
32 | #include "gnunet_scalarproduct_service.h" | ||
33 | #include "gnunet_protocols.h" | ||
34 | #include "scalarproduct.h" | ||
35 | |||
36 | #define LOG(kind, ...) GNUNET_log_from (kind, "gnunet-scalarproduct", \ | ||
37 | __VA_ARGS__) | ||
38 | |||
39 | |||
40 | /** | ||
41 | * the session key identifying this computation | ||
42 | */ | ||
43 | static struct GNUNET_HashCode session_key; | ||
44 | |||
45 | /** | ||
46 | * PeerID we want to compute a scalar product with | ||
47 | */ | ||
48 | static struct GNUNET_PeerIdentity peer_id; | ||
49 | |||
50 | /** | ||
51 | * Option -p: destination peer identity for checking message-ids with | ||
52 | */ | ||
53 | static char *input_peer_id; | ||
54 | |||
55 | /** | ||
56 | * Option -p: destination peer identity for checking message-ids with | ||
57 | */ | ||
58 | static char *input_session_key; | ||
59 | |||
60 | /** | ||
61 | * Option -e: vector to calculate a scalarproduct with | ||
62 | */ | ||
63 | static char *input_elements; | ||
64 | |||
65 | /** | ||
66 | * Global return value | ||
67 | */ | ||
68 | static int ret = -1; | ||
69 | |||
70 | /** | ||
71 | * our Scalarproduct Computation handle | ||
72 | */ | ||
73 | static struct GNUNET_SCALARPRODUCT_ComputationHandle *computation; | ||
74 | |||
75 | |||
76 | /** | ||
77 | * Callback called if we are initiating a new computation session | ||
78 | * | ||
79 | * @param cls unused | ||
80 | * @param status if our job was successfully processed | ||
81 | */ | ||
82 | static void | ||
83 | responder_callback (void *cls, | ||
84 | enum GNUNET_SCALARPRODUCT_ResponseStatus status) | ||
85 | { | ||
86 | switch (status) | ||
87 | { | ||
88 | case GNUNET_SCALARPRODUCT_STATUS_SUCCESS: | ||
89 | ret = 0; | ||
90 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
91 | "Session %s concluded.\n", | ||
92 | GNUNET_h2s (&session_key)); | ||
93 | break; | ||
94 | |||
95 | case GNUNET_SCALARPRODUCT_STATUS_INVALID_RESPONSE: | ||
96 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
97 | "Session %s failed: invalid response\n", | ||
98 | GNUNET_h2s (&session_key)); | ||
99 | break; | ||
100 | |||
101 | case GNUNET_SCALARPRODUCT_STATUS_FAILURE: | ||
102 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
103 | "Session %s failed: service failure\n", | ||
104 | GNUNET_h2s (&session_key)); | ||
105 | break; | ||
106 | |||
107 | case GNUNET_SCALARPRODUCT_STATUS_DISCONNECTED: | ||
108 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
109 | "Session %s failed: service disconnect!\n", | ||
110 | GNUNET_h2s (&session_key)); | ||
111 | break; | ||
112 | |||
113 | default: | ||
114 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
115 | "Session %s failed: return code %d\n", | ||
116 | GNUNET_h2s (&session_key), | ||
117 | status); | ||
118 | } | ||
119 | computation = NULL; | ||
120 | GNUNET_SCHEDULER_shutdown (); | ||
121 | } | ||
122 | |||
123 | |||
124 | /** | ||
125 | * Callback called if we are initiating a new computation session | ||
126 | * | ||
127 | * @param cls unused | ||
128 | * @param status if our job was successfully processed | ||
129 | * @param result the result in gnu/gcry MPI format | ||
130 | */ | ||
131 | static void | ||
132 | requester_callback (void *cls, | ||
133 | enum GNUNET_SCALARPRODUCT_ResponseStatus status, | ||
134 | gcry_mpi_t result) | ||
135 | { | ||
136 | unsigned char *buf; | ||
137 | gcry_error_t rc; | ||
138 | |||
139 | switch (status) | ||
140 | { | ||
141 | case GNUNET_SCALARPRODUCT_STATUS_SUCCESS: | ||
142 | if (0 == (rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, result))) | ||
143 | { | ||
144 | ret = 0; | ||
145 | fprintf (stdout, | ||
146 | "%s\n", | ||
147 | buf); | ||
148 | fflush (stdout); | ||
149 | } | ||
150 | else | ||
151 | LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, | ||
152 | "gcry_mpi_aprint", | ||
153 | rc); | ||
154 | break; | ||
155 | |||
156 | case GNUNET_SCALARPRODUCT_STATUS_INVALID_RESPONSE: | ||
157 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
158 | "Session %s with peer %s failed: invalid response received\n", | ||
159 | GNUNET_h2s (&session_key), | ||
160 | GNUNET_i2s (&peer_id)); | ||
161 | break; | ||
162 | |||
163 | case GNUNET_SCALARPRODUCT_STATUS_FAILURE: | ||
164 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
165 | "Session %s with peer %s failed: API failure\n", | ||
166 | GNUNET_h2s (&session_key), | ||
167 | GNUNET_i2s (&peer_id)); | ||
168 | break; | ||
169 | |||
170 | case GNUNET_SCALARPRODUCT_STATUS_DISCONNECTED: | ||
171 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
172 | "Session %s with peer %s was disconnected from service.\n", | ||
173 | GNUNET_h2s (&session_key), | ||
174 | GNUNET_i2s (&peer_id)); | ||
175 | break; | ||
176 | |||
177 | default: | ||
178 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
179 | "Session %s with peer %s failed: return code %d\n", | ||
180 | GNUNET_h2s (&session_key), | ||
181 | GNUNET_i2s (&peer_id), | ||
182 | status); | ||
183 | } | ||
184 | computation = NULL; | ||
185 | GNUNET_SCHEDULER_shutdown (); | ||
186 | } | ||
187 | |||
188 | |||
189 | /** | ||
190 | * Task run during shutdown. | ||
191 | * | ||
192 | * @param cls unused | ||
193 | */ | ||
194 | static void | ||
195 | shutdown_task (void *cls) | ||
196 | { | ||
197 | if (NULL != computation) | ||
198 | { | ||
199 | GNUNET_SCALARPRODUCT_cancel (computation); | ||
200 | ret = 1; /* aborted */ | ||
201 | } | ||
202 | } | ||
203 | |||
204 | |||
205 | /** | ||
206 | * Main function that will be run by the scheduler. | ||
207 | * | ||
208 | * @param cls closure | ||
209 | * @param args remaining command-line arguments | ||
210 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
211 | * @param cfg configuration | ||
212 | */ | ||
213 | static void | ||
214 | run (void *cls, | ||
215 | char *const *args, | ||
216 | const char *cfgfile, | ||
217 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
218 | { | ||
219 | char *begin = input_elements; | ||
220 | char *end; | ||
221 | unsigned int i; | ||
222 | struct GNUNET_SCALARPRODUCT_Element *elements; | ||
223 | uint32_t element_count = 0; | ||
224 | |||
225 | if (NULL == input_elements) | ||
226 | { | ||
227 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
228 | _ ("You must specify at least one message ID to check!\n")); | ||
229 | return; | ||
230 | } | ||
231 | if ((NULL == input_session_key) || | ||
232 | (0 == strlen (input_session_key))) | ||
233 | { | ||
234 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
235 | _ ( | ||
236 | "This program needs a session identifier for comparing vectors.\n")); | ||
237 | return; | ||
238 | } | ||
239 | GNUNET_CRYPTO_hash (input_session_key, | ||
240 | strlen (input_session_key), | ||
241 | &session_key); | ||
242 | if ((NULL != input_peer_id) && | ||
243 | (GNUNET_OK != | ||
244 | GNUNET_CRYPTO_eddsa_public_key_from_string (input_peer_id, | ||
245 | strlen (input_peer_id), | ||
246 | &peer_id.public_key))) | ||
247 | { | ||
248 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
249 | _ ("Tried to set initiator mode, as peer ID was given. " | ||
250 | "However, `%s' is not a valid peer identifier.\n"), | ||
251 | input_peer_id); | ||
252 | return; | ||
253 | } | ||
254 | if (('\'' == *begin) && | ||
255 | ('\'' == begin[strlen (begin) - 1])) | ||
256 | { | ||
257 | begin[strlen (begin) - 1] = '\0'; | ||
258 | if (strlen (begin) > 0) | ||
259 | begin++; | ||
260 | } | ||
261 | for (end = begin; 0 != *end; end++) | ||
262 | if (*end == ';') | ||
263 | element_count++; | ||
264 | if (0 == element_count) | ||
265 | { | ||
266 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
267 | _ ("Need elements to compute the scalarproduct, got none.\n")); | ||
268 | return; | ||
269 | } | ||
270 | |||
271 | elements = GNUNET_malloc (sizeof(struct GNUNET_SCALARPRODUCT_Element) | ||
272 | * element_count); | ||
273 | |||
274 | for (i = 0; i < element_count; i++) | ||
275 | { | ||
276 | struct GNUNET_SCALARPRODUCT_Element element; | ||
277 | char*separator = NULL; | ||
278 | |||
279 | /* get the length of the current key,value; tuple */ | ||
280 | for (end = begin; *end != ';'; end++) | ||
281 | if (*end == ',') | ||
282 | separator = end; | ||
283 | |||
284 | /* final element */ | ||
285 | if ((NULL == separator) || | ||
286 | (begin == separator) || | ||
287 | (separator == end - 1)) | ||
288 | { | ||
289 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
290 | _ ("Malformed input, could not parse `%s'\n"), | ||
291 | begin); | ||
292 | GNUNET_free (elements); | ||
293 | return; | ||
294 | } | ||
295 | *separator = 0; | ||
296 | /* read the element's key */ | ||
297 | GNUNET_CRYPTO_hash (begin, | ||
298 | strlen (begin), | ||
299 | &element.key); | ||
300 | |||
301 | /* read the element's value */ | ||
302 | if (1 != | ||
303 | sscanf (separator + 1, | ||
304 | "%" SCNd64 ";", | ||
305 | &element.value)) | ||
306 | { | ||
307 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
308 | _ ("Could not convert `%s' to int64_t.\n"), | ||
309 | begin); | ||
310 | GNUNET_free (elements); | ||
311 | return; | ||
312 | } | ||
313 | element.value = GNUNET_htonll (element.value); | ||
314 | elements[i] = element; | ||
315 | begin = end + 1; | ||
316 | } | ||
317 | |||
318 | if (((NULL != input_peer_id) && | ||
319 | (NULL == (computation | ||
320 | = GNUNET_SCALARPRODUCT_start_computation (cfg, | ||
321 | &session_key, | ||
322 | &peer_id, | ||
323 | elements, | ||
324 | element_count, | ||
325 | &requester_callback, | ||
326 | NULL)))) || | ||
327 | ((NULL == input_peer_id) && | ||
328 | (NULL == (computation | ||
329 | = GNUNET_SCALARPRODUCT_accept_computation (cfg, | ||
330 | &session_key, | ||
331 | elements, | ||
332 | element_count, | ||
333 | & | ||
334 | responder_callback, | ||
335 | NULL))))) | ||
336 | { | ||
337 | fprintf (stderr, | ||
338 | _ ("Failed to initiate computation, were all keys unique?\n")); | ||
339 | GNUNET_free (elements); | ||
340 | return; | ||
341 | } | ||
342 | GNUNET_free (elements); | ||
343 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
344 | NULL); | ||
345 | ret = 0; | ||
346 | } | ||
347 | |||
348 | |||
349 | /** | ||
350 | * The main function to the scalarproduct client. | ||
351 | * | ||
352 | * @param argc number of arguments from the command line | ||
353 | * @param argv command line arguments | ||
354 | * @return 0 ok, 1 on error | ||
355 | */ | ||
356 | int | ||
357 | main (int argc, char *const *argv) | ||
358 | { | ||
359 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
360 | GNUNET_GETOPT_option_string ('e', | ||
361 | "elements", | ||
362 | "\"key1,val1;key2,val2;...,keyn,valn;\"", | ||
363 | gettext_noop ( | ||
364 | "A comma separated list of elements to compare as vector with our remote peer."), | ||
365 | &input_elements), | ||
366 | |||
367 | GNUNET_GETOPT_option_string ('e', | ||
368 | "elements", | ||
369 | "\"key1,val1;key2,val2;...,keyn,valn;\"", | ||
370 | gettext_noop ( | ||
371 | "A comma separated list of elements to compare as vector with our remote peer."), | ||
372 | &input_elements), | ||
373 | |||
374 | GNUNET_GETOPT_option_string ('p', | ||
375 | "peer", | ||
376 | "PEERID", | ||
377 | gettext_noop ( | ||
378 | "[Optional] peer to calculate our scalarproduct with. If this parameter is not given, the service will wait for a remote peer to compute the request."), | ||
379 | &input_peer_id), | ||
380 | |||
381 | GNUNET_GETOPT_option_string ('k', | ||
382 | "key", | ||
383 | "TRANSACTION_ID", | ||
384 | gettext_noop ( | ||
385 | "Transaction ID shared with peer."), | ||
386 | &input_session_key), | ||
387 | |||
388 | GNUNET_GETOPT_OPTION_END | ||
389 | }; | ||
390 | |||
391 | return (GNUNET_OK == | ||
392 | GNUNET_PROGRAM_run (argc, | ||
393 | argv, | ||
394 | "gnunet-scalarproduct", | ||
395 | gettext_noop ( | ||
396 | "Calculate the Vectorproduct with a GNUnet peer."), | ||
397 | options, &run, NULL)) ? ret : 1; | ||
398 | } | ||
399 | |||
400 | |||
401 | /* end of gnunet-scalarproduct.c */ | ||
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc.h b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc.h new file mode 100644 index 000000000..5c7a8d8f1 --- /dev/null +++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc.h | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2015 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.h | ||
22 | * @brief scalarproduct service P2P messages | ||
23 | * @author Christian M. Fuchs | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #ifndef GNUNET_SERVICE_SCALARPRODUCT_ECC_H | ||
27 | #define GNUNET_SERVICE_SCALARPRODUCT_ECC_H | ||
28 | |||
29 | |||
30 | GNUNET_NETWORK_STRUCT_BEGIN | ||
31 | |||
32 | /** | ||
33 | * Message type passed from requesting service Alice to responding | ||
34 | * service Bob to initiate a request and make Bob participate in our | ||
35 | * protocol. Afterwards, Bob is expected to perform the set | ||
36 | * intersection with Alice. Once that has succeeded, Alice will | ||
37 | * send a `struct AliceCryptodataMessage *`. Bob is not expected | ||
38 | * to respond via CADET in the meantime. | ||
39 | */ | ||
40 | struct EccServiceRequestMessage | ||
41 | { | ||
42 | /** | ||
43 | * Type is #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_SESSION_INITIALIZATION | ||
44 | */ | ||
45 | struct GNUNET_MessageHeader header; | ||
46 | |||
47 | /** | ||
48 | * For alignment. Always zero. | ||
49 | */ | ||
50 | uint32_t reserved; | ||
51 | |||
52 | /** | ||
53 | * The transaction/session key used to identify a session | ||
54 | */ | ||
55 | struct GNUNET_HashCode session_id; | ||
56 | }; | ||
57 | |||
58 | |||
59 | /** | ||
60 | * Vector of ECC-encrypted values sent by Alice to Bob | ||
61 | * (after set intersection). Alice may send messages of this | ||
62 | * type repeatedly to transmit all values. | ||
63 | */ | ||
64 | struct EccAliceCryptodataMessage | ||
65 | { | ||
66 | /** | ||
67 | * Type is #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_ALICE_CRYPTODATA | ||
68 | */ | ||
69 | struct GNUNET_MessageHeader header; | ||
70 | |||
71 | /** | ||
72 | * How many elements we appended to this message? In NBO. | ||
73 | */ | ||
74 | uint32_t contained_element_count GNUNET_PACKED; | ||
75 | |||
76 | /** | ||
77 | * struct GNUNET_CRYPTO_EccPoint[contained_element_count] | ||
78 | */ | ||
79 | }; | ||
80 | |||
81 | |||
82 | /** | ||
83 | * Message type passed from responding service Bob to responding | ||
84 | * service Alice to complete a request and allow Alice to compute the | ||
85 | * result. If Bob's reply does not fit into this one message, the | ||
86 | * conversation may be continued with `struct BobCryptodataMultipartMessage` | ||
87 | * messages afterwards. | ||
88 | */ | ||
89 | struct EccBobCryptodataMessage | ||
90 | { | ||
91 | /** | ||
92 | * GNUNET message header with type | ||
93 | * #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_BOB_CRYPTODATA. | ||
94 | */ | ||
95 | struct GNUNET_MessageHeader header; | ||
96 | |||
97 | /** | ||
98 | * How many elements this individual message delivers (in NBO), | ||
99 | * always TWO. | ||
100 | */ | ||
101 | uint32_t contained_element_count GNUNET_PACKED; | ||
102 | |||
103 | /** | ||
104 | * The product of the g_i^{b_i} values. | ||
105 | */ | ||
106 | struct GNUNET_CRYPTO_EccPoint prod_g_i_b_i; | ||
107 | |||
108 | /** | ||
109 | * The product of the h_i^{b_i} values. | ||
110 | */ | ||
111 | struct GNUNET_CRYPTO_EccPoint prod_h_i_b_i; | ||
112 | }; | ||
113 | |||
114 | |||
115 | GNUNET_NETWORK_STRUCT_END | ||
116 | |||
117 | |||
118 | #endif | ||
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c new file mode 100644 index 000000000..b8bac0803 --- /dev/null +++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c | |||
@@ -0,0 +1,1150 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2013-2017, 2021 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_seti_service.h" | ||
36 | #include "scalarproduct.h" | ||
37 | #include "gnunet-service-scalarproduct-ecc.h" | ||
38 | #include "gnunet_constants.h" | ||
39 | |||
40 | #define LOG(kind, ...) \ | ||
41 | GNUNET_log_from (kind, "scalarproduct-alice", __VA_ARGS__) | ||
42 | |||
43 | /** | ||
44 | * Maximum allowed result value for the scalarproduct computation. | ||
45 | * DLOG will fail if the result is bigger. At 1 million, the | ||
46 | * precomputation takes about 2s on a fast machine. | ||
47 | */ | ||
48 | #define MAX_RESULT (1024 * 1024) | ||
49 | |||
50 | /** | ||
51 | * How many values should DLOG store in memory (determines baseline | ||
52 | * RAM consumption, roughly 100 bytes times the value given here). | ||
53 | * Should be about SQRT (MAX_RESULT), larger values will make the | ||
54 | * online computation faster. | ||
55 | */ | ||
56 | #define MAX_RAM (1024) | ||
57 | |||
58 | /** | ||
59 | * An encrypted element key-value pair. | ||
60 | */ | ||
61 | struct MpiElement | ||
62 | { | ||
63 | /** | ||
64 | * Key used to identify matching pairs of values to multiply. | ||
65 | * Points into an existing data structure, to avoid copying | ||
66 | * and doubling memory use. | ||
67 | */ | ||
68 | const struct GNUNET_HashCode *key; | ||
69 | |||
70 | /** | ||
71 | * a_i value, not disclosed to Bob. | ||
72 | */ | ||
73 | int64_t value; | ||
74 | }; | ||
75 | |||
76 | |||
77 | /** | ||
78 | * A scalarproduct session which tracks | ||
79 | * a request form the client to our final response. | ||
80 | */ | ||
81 | struct AliceServiceSession | ||
82 | { | ||
83 | /** | ||
84 | * (hopefully) unique transaction ID | ||
85 | */ | ||
86 | struct GNUNET_HashCode session_id; | ||
87 | |||
88 | /** | ||
89 | * Alice or Bob's peerID | ||
90 | */ | ||
91 | struct GNUNET_PeerIdentity peer; | ||
92 | |||
93 | /** | ||
94 | * The client this request is related to. | ||
95 | */ | ||
96 | struct GNUNET_SERVICE_Client *client; | ||
97 | |||
98 | /** | ||
99 | * The message queue for the client. | ||
100 | */ | ||
101 | struct GNUNET_MQ_Handle *client_mq; | ||
102 | |||
103 | /** | ||
104 | * The message queue for CADET. | ||
105 | */ | ||
106 | struct GNUNET_MQ_Handle *cadet_mq; | ||
107 | |||
108 | /** | ||
109 | * all non-0-value'd elements transmitted to us. | ||
110 | * Values are of type `struct GNUNET_SCALARPRODUCT_Element *` | ||
111 | */ | ||
112 | struct GNUNET_CONTAINER_MultiHashMap *intersected_elements; | ||
113 | |||
114 | /** | ||
115 | * Set of elements for which will conduction an intersection. | ||
116 | * the resulting elements are then used for computing the scalar product. | ||
117 | */ | ||
118 | struct GNUNET_SETI_Handle *intersection_set; | ||
119 | |||
120 | /** | ||
121 | * Set of elements for which will conduction an intersection. | ||
122 | * the resulting elements are then used for computing the scalar product. | ||
123 | */ | ||
124 | struct GNUNET_SETI_OperationHandle *intersection_op; | ||
125 | |||
126 | /** | ||
127 | * Handle to Alice's Intersection operation listening for Bob | ||
128 | */ | ||
129 | struct GNUNET_SETI_ListenHandle *intersection_listen; | ||
130 | |||
131 | /** | ||
132 | * channel-handle associated with our cadet handle | ||
133 | */ | ||
134 | struct GNUNET_CADET_Channel *channel; | ||
135 | |||
136 | /** | ||
137 | * a(Alice), sorted array by key of length @e used_element_count. | ||
138 | */ | ||
139 | struct MpiElement *sorted_elements; | ||
140 | |||
141 | /** | ||
142 | * The computed scalar product. INT_MAX if the computation failed. | ||
143 | */ | ||
144 | int product; | ||
145 | |||
146 | /** | ||
147 | * How many elements we were supplied with from the client (total | ||
148 | * count before intersection). | ||
149 | */ | ||
150 | uint32_t total; | ||
151 | |||
152 | /** | ||
153 | * How many elements actually are used for the scalar product. | ||
154 | * Size of the arrays in @e r and @e r_prime. Sometimes also | ||
155 | * reset to 0 and used as a counter! | ||
156 | */ | ||
157 | uint32_t used_element_count; | ||
158 | |||
159 | /** | ||
160 | * Already transferred elements from client to us. | ||
161 | * Less or equal than @e total. | ||
162 | */ | ||
163 | uint32_t client_received_element_count; | ||
164 | |||
165 | /** | ||
166 | * State of this session. In | ||
167 | * #GNUNET_SCALARPRODUCT_STATUS_ACTIVE while operation is | ||
168 | * ongoing, afterwards in #GNUNET_SCALARPRODUCT_STATUS_SUCCESS or | ||
169 | * #GNUNET_SCALARPRODUCT_STATUS_FAILURE. | ||
170 | */ | ||
171 | enum GNUNET_SCALARPRODUCT_ResponseStatus status; | ||
172 | |||
173 | /** | ||
174 | * Flag to prevent recursive calls to #destroy_service_session() from | ||
175 | * doing harm. | ||
176 | */ | ||
177 | int in_destroy; | ||
178 | }; | ||
179 | |||
180 | |||
181 | /** | ||
182 | * GNUnet configuration handle | ||
183 | */ | ||
184 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
185 | |||
186 | /** | ||
187 | * Context for DLOG operations on a curve. | ||
188 | */ | ||
189 | static struct GNUNET_CRYPTO_EccDlogContext *edc; | ||
190 | |||
191 | /** | ||
192 | * Alice's private key ('a'). | ||
193 | */ | ||
194 | static struct GNUNET_CRYPTO_EccScalar my_privkey; | ||
195 | |||
196 | /** | ||
197 | * Inverse of Alice's private key ('a_inv'). | ||
198 | */ | ||
199 | static struct GNUNET_CRYPTO_EccScalar my_privkey_inv; | ||
200 | |||
201 | /** | ||
202 | * Handle to the CADET service. | ||
203 | */ | ||
204 | static struct GNUNET_CADET_Handle *my_cadet; | ||
205 | |||
206 | |||
207 | /** | ||
208 | * Iterator called to free elements. | ||
209 | * | ||
210 | * @param cls the `struct AliceServiceSession *` (unused) | ||
211 | * @param key the key (unused) | ||
212 | * @param value value to free | ||
213 | * @return #GNUNET_OK (continue to iterate) | ||
214 | */ | ||
215 | static int | ||
216 | free_element_cb (void *cls, | ||
217 | const struct GNUNET_HashCode *key, | ||
218 | void *value) | ||
219 | { | ||
220 | struct GNUNET_SCALARPRODUCT_Element *e = value; | ||
221 | |||
222 | GNUNET_free (e); | ||
223 | return GNUNET_OK; | ||
224 | } | ||
225 | |||
226 | |||
227 | /** | ||
228 | * Destroy session state, we are done with it. | ||
229 | * | ||
230 | * @param s the session to free elements from | ||
231 | */ | ||
232 | static void | ||
233 | destroy_service_session (struct AliceServiceSession *s) | ||
234 | { | ||
235 | if (GNUNET_YES == s->in_destroy) | ||
236 | return; | ||
237 | s->in_destroy = GNUNET_YES; | ||
238 | if (NULL != s->client) | ||
239 | { | ||
240 | struct GNUNET_SERVICE_Client *c = s->client; | ||
241 | |||
242 | s->client = NULL; | ||
243 | GNUNET_SERVICE_client_drop (c); | ||
244 | } | ||
245 | if (NULL != s->channel) | ||
246 | { | ||
247 | GNUNET_CADET_channel_destroy (s->channel); | ||
248 | s->channel = NULL; | ||
249 | } | ||
250 | if (NULL != s->intersected_elements) | ||
251 | { | ||
252 | GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements, | ||
253 | &free_element_cb, | ||
254 | s); | ||
255 | GNUNET_CONTAINER_multihashmap_destroy (s->intersected_elements); | ||
256 | s->intersected_elements = NULL; | ||
257 | } | ||
258 | if (NULL != s->intersection_listen) | ||
259 | { | ||
260 | GNUNET_SETI_listen_cancel (s->intersection_listen); | ||
261 | s->intersection_listen = NULL; | ||
262 | } | ||
263 | if (NULL != s->intersection_op) | ||
264 | { | ||
265 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
266 | "Set intersection, op still ongoing!\n"); | ||
267 | GNUNET_SETI_operation_cancel (s->intersection_op); | ||
268 | s->intersection_op = NULL; | ||
269 | } | ||
270 | if (NULL != s->intersection_set) | ||
271 | { | ||
272 | GNUNET_SETI_destroy (s->intersection_set); | ||
273 | s->intersection_set = NULL; | ||
274 | } | ||
275 | if (NULL != s->sorted_elements) | ||
276 | { | ||
277 | GNUNET_free (s->sorted_elements); | ||
278 | s->sorted_elements = NULL; | ||
279 | } | ||
280 | GNUNET_free (s); | ||
281 | } | ||
282 | |||
283 | |||
284 | /** | ||
285 | * Notify the client that the session has failed. A message gets sent | ||
286 | * to Alice's client if we encountered any error. | ||
287 | * | ||
288 | * @param session the associated client session to fail or succeed | ||
289 | */ | ||
290 | static void | ||
291 | prepare_client_end_notification (struct AliceServiceSession *session) | ||
292 | { | ||
293 | struct ClientResponseMessage *msg; | ||
294 | struct GNUNET_MQ_Envelope *e; | ||
295 | |||
296 | if (NULL == session->client_mq) | ||
297 | return; /* no client left to be notified */ | ||
298 | GNUNET_log ( | ||
299 | GNUNET_ERROR_TYPE_DEBUG, | ||
300 | "Sending session-end notification with status %d to client for session %s\n", | ||
301 | session->status, | ||
302 | GNUNET_h2s (&session->session_id)); | ||
303 | e = GNUNET_MQ_msg (msg, | ||
304 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT); | ||
305 | msg->product_length = htonl (0); | ||
306 | msg->status = htonl (session->status); | ||
307 | GNUNET_MQ_send (session->client_mq, | ||
308 | e); | ||
309 | } | ||
310 | |||
311 | |||
312 | /** | ||
313 | * Prepare the final (positive) response we will send to Alice's | ||
314 | * client. | ||
315 | * | ||
316 | * @param s the session associated with our client. | ||
317 | */ | ||
318 | static void | ||
319 | transmit_client_response (struct AliceServiceSession *s) | ||
320 | { | ||
321 | struct ClientResponseMessage *msg; | ||
322 | struct GNUNET_MQ_Envelope *e; | ||
323 | unsigned char *product_exported = NULL; | ||
324 | size_t product_length = 0; | ||
325 | int32_t range; | ||
326 | gcry_error_t rc; | ||
327 | gcry_mpi_t value; | ||
328 | |||
329 | if (INT_MAX == s->product) | ||
330 | { | ||
331 | GNUNET_break (0); | ||
332 | prepare_client_end_notification (s); | ||
333 | return; | ||
334 | } | ||
335 | value = gcry_mpi_new (32); | ||
336 | if (0 > s->product) | ||
337 | { | ||
338 | range = -1; | ||
339 | gcry_mpi_set_ui (value, | ||
340 | -s->product); | ||
341 | } | ||
342 | else if (0 < s->product) | ||
343 | { | ||
344 | range = 1; | ||
345 | gcry_mpi_set_ui (value, | ||
346 | s->product); | ||
347 | } | ||
348 | else | ||
349 | { | ||
350 | /* result is exactly zero */ | ||
351 | range = 0; | ||
352 | } | ||
353 | if ( (0 != range) && | ||
354 | (0 != (rc = gcry_mpi_aprint (GCRYMPI_FMT_STD, | ||
355 | &product_exported, | ||
356 | &product_length, | ||
357 | value)))) | ||
358 | { | ||
359 | LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, | ||
360 | "gcry_mpi_scan", | ||
361 | rc); | ||
362 | prepare_client_end_notification (s); | ||
363 | return; | ||
364 | } | ||
365 | gcry_mpi_release (value); | ||
366 | e = GNUNET_MQ_msg_extra (msg, | ||
367 | product_length, | ||
368 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT); | ||
369 | msg->status = htonl (GNUNET_SCALARPRODUCT_STATUS_SUCCESS); | ||
370 | msg->range = htonl (range); | ||
371 | msg->product_length = htonl (product_length); | ||
372 | if (NULL != product_exported) | ||
373 | { | ||
374 | GNUNET_memcpy (&msg[1], | ||
375 | product_exported, | ||
376 | product_length); | ||
377 | GNUNET_free (product_exported); | ||
378 | } | ||
379 | GNUNET_MQ_send (s->client_mq, | ||
380 | e); | ||
381 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
382 | "Sent result to client, session %s has ended!\n", | ||
383 | GNUNET_h2s (&s->session_id)); | ||
384 | } | ||
385 | |||
386 | |||
387 | /** | ||
388 | * Function called whenever a channel is destroyed. Should clean up | ||
389 | * any associated state. | ||
390 | * | ||
391 | * It must NOT call #GNUNET_CADET_channel_destroy() on the channel. | ||
392 | * | ||
393 | * @param cls the `struct AliceServiceSession` | ||
394 | * @param channel connection to the other end (henceforth invalid) | ||
395 | */ | ||
396 | static void | ||
397 | cb_channel_destruction (void *cls, | ||
398 | const struct GNUNET_CADET_Channel *channel) | ||
399 | { | ||
400 | struct AliceServiceSession *s = cls; | ||
401 | |||
402 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
403 | "Peer disconnected, terminating session %s with peer %s\n", | ||
404 | GNUNET_h2s (&s->session_id), | ||
405 | GNUNET_i2s (&s->peer)); | ||
406 | s->channel = NULL; | ||
407 | if (GNUNET_SCALARPRODUCT_STATUS_ACTIVE == s->status) | ||
408 | { | ||
409 | /* We didn't get an answer yet, fail with error */ | ||
410 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
411 | prepare_client_end_notification (s); | ||
412 | } | ||
413 | } | ||
414 | |||
415 | |||
416 | /** | ||
417 | * Handle a response we got from another service we wanted to | ||
418 | * calculate a scalarproduct with. | ||
419 | * | ||
420 | * @param cls the `struct AliceServiceSession *` | ||
421 | * @param msg the actual message | ||
422 | */ | ||
423 | static void | ||
424 | handle_bobs_cryptodata_message (void *cls, | ||
425 | const struct EccBobCryptodataMessage *msg) | ||
426 | { | ||
427 | struct AliceServiceSession *s = cls; | ||
428 | uint32_t contained; | ||
429 | |||
430 | contained = ntohl (msg->contained_element_count); | ||
431 | if (2 != contained) | ||
432 | { | ||
433 | GNUNET_break_op (0); | ||
434 | destroy_service_session (s); | ||
435 | return; | ||
436 | } | ||
437 | if (NULL == s->sorted_elements) | ||
438 | { | ||
439 | /* we're not ready yet, how can Bob be? */ | ||
440 | GNUNET_break_op (0); | ||
441 | destroy_service_session (s); | ||
442 | return; | ||
443 | } | ||
444 | if (s->total != s->client_received_element_count) | ||
445 | { | ||
446 | /* we're not ready yet, how can Bob be? */ | ||
447 | GNUNET_break_op (0); | ||
448 | destroy_service_session (s); | ||
449 | return; | ||
450 | } | ||
451 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
452 | "Received %u crypto values from Bob\n", | ||
453 | (unsigned int) contained); | ||
454 | GNUNET_CADET_receive_done (s->channel); | ||
455 | { | ||
456 | struct GNUNET_CRYPTO_EccPoint g_i_b_i_a_inv; | ||
457 | struct GNUNET_CRYPTO_EccPoint g_ai_bi; | ||
458 | |||
459 | GNUNET_assert ( | ||
460 | GNUNET_OK == | ||
461 | GNUNET_CRYPTO_ecc_pmul_mpi (&msg->prod_g_i_b_i, | ||
462 | &my_privkey_inv, | ||
463 | &g_i_b_i_a_inv)); | ||
464 | GNUNET_assert ( | ||
465 | GNUNET_OK == | ||
466 | GNUNET_CRYPTO_ecc_add (&g_i_b_i_a_inv, | ||
467 | &msg->prod_h_i_b_i, | ||
468 | &g_ai_bi)); | ||
469 | s->product = GNUNET_CRYPTO_ecc_dlog (edc, | ||
470 | &g_ai_bi); | ||
471 | if (INT_MAX == s->product) | ||
472 | { | ||
473 | /* result too big */ | ||
474 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
475 | "Scalar product result out of range\n"); | ||
476 | } | ||
477 | } | ||
478 | transmit_client_response (s); | ||
479 | } | ||
480 | |||
481 | |||
482 | /** | ||
483 | * Iterator to copy over messages from the hash map | ||
484 | * into an array for sorting. | ||
485 | * | ||
486 | * @param cls the `struct AliceServiceSession *` | ||
487 | * @param key the key (unused) | ||
488 | * @param value the `struct GNUNET_SCALARPRODUCT_Element *` | ||
489 | */ | ||
490 | static int | ||
491 | copy_element_cb (void *cls, | ||
492 | const struct GNUNET_HashCode *key, | ||
493 | void *value) | ||
494 | { | ||
495 | struct AliceServiceSession *s = cls; | ||
496 | struct GNUNET_SCALARPRODUCT_Element *e = value; | ||
497 | |||
498 | s->sorted_elements[s->used_element_count].value = (int64_t) GNUNET_ntohll ( | ||
499 | e->value); | ||
500 | s->sorted_elements[s->used_element_count].key = &e->key; | ||
501 | s->used_element_count++; | ||
502 | return GNUNET_OK; | ||
503 | } | ||
504 | |||
505 | |||
506 | /** | ||
507 | * Compare two `struct MpiValue`s by key for sorting. | ||
508 | * | ||
509 | * @param a pointer to first `struct MpiValue *` | ||
510 | * @param b pointer to first `struct MpiValue *` | ||
511 | * @return -1 for a < b, 0 for a=b, 1 for a > b. | ||
512 | */ | ||
513 | static int | ||
514 | element_cmp (const void *a, | ||
515 | const void *b) | ||
516 | { | ||
517 | const struct MpiElement *ma = a; | ||
518 | const struct MpiElement *mb = b; | ||
519 | |||
520 | return GNUNET_CRYPTO_hash_cmp (ma->key, | ||
521 | mb->key); | ||
522 | } | ||
523 | |||
524 | |||
525 | /** | ||
526 | * Maximum number of elements we can put into a single cryptodata | ||
527 | * message | ||
528 | */ | ||
529 | #define ELEMENT_CAPACITY \ | ||
530 | ((GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE - 1 \ | ||
531 | - sizeof(struct EccAliceCryptodataMessage)) \ | ||
532 | / sizeof(struct GNUNET_CRYPTO_EccPoint)) | ||
533 | |||
534 | |||
535 | /** | ||
536 | * Send the cryptographic data from Alice to Bob. | ||
537 | * Does nothing if we already transferred all elements. | ||
538 | * | ||
539 | * @param s the associated service session | ||
540 | */ | ||
541 | static void | ||
542 | send_alices_cryptodata_message (struct AliceServiceSession *s) | ||
543 | { | ||
544 | struct EccAliceCryptodataMessage *msg; | ||
545 | struct GNUNET_MQ_Envelope *e; | ||
546 | struct GNUNET_CRYPTO_EccPoint *payload; | ||
547 | struct GNUNET_CRYPTO_EccScalar r_ia; | ||
548 | struct GNUNET_CRYPTO_EccScalar r_ia_ai; | ||
549 | unsigned int off; | ||
550 | unsigned int todo_count; | ||
551 | |||
552 | s->sorted_elements = GNUNET_new_array (GNUNET_CONTAINER_multihashmap_size ( | ||
553 | s->intersected_elements), | ||
554 | struct MpiElement); | ||
555 | s->used_element_count = 0; | ||
556 | GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements, | ||
557 | ©_element_cb, | ||
558 | s); | ||
559 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
560 | "Finished intersection, %d items remain\n", | ||
561 | s->used_element_count); | ||
562 | qsort (s->sorted_elements, | ||
563 | s->used_element_count, | ||
564 | sizeof(struct MpiElement), | ||
565 | &element_cmp); | ||
566 | off = 0; | ||
567 | while (off < s->used_element_count) | ||
568 | { | ||
569 | todo_count = s->used_element_count - off; | ||
570 | if (todo_count > ELEMENT_CAPACITY) | ||
571 | todo_count = ELEMENT_CAPACITY; | ||
572 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
573 | "Sending %u/%u crypto values to Bob\n", | ||
574 | (unsigned int) todo_count, | ||
575 | (unsigned int) s->used_element_count); | ||
576 | |||
577 | e = | ||
578 | GNUNET_MQ_msg_extra (msg, | ||
579 | todo_count * 2 | ||
580 | * sizeof(struct GNUNET_CRYPTO_EccPoint), | ||
581 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_ALICE_CRYPTODATA); | ||
582 | msg->contained_element_count = htonl (todo_count); | ||
583 | payload = (struct GNUNET_CRYPTO_EccPoint *) &msg[1]; | ||
584 | for (unsigned int i = off; i < off + todo_count; i++) | ||
585 | { | ||
586 | struct GNUNET_CRYPTO_EccScalar r_i; | ||
587 | struct GNUNET_CRYPTO_EccPoint g_i; | ||
588 | struct GNUNET_CRYPTO_EccPoint h_i; | ||
589 | |||
590 | /* r_i = random() mod n */ | ||
591 | GNUNET_CRYPTO_ecc_random_mod_n (&r_i); | ||
592 | /* g_i = g^{r_i} */ | ||
593 | GNUNET_assert (GNUNET_OK == | ||
594 | GNUNET_CRYPTO_ecc_dexp_mpi (&r_i, | ||
595 | &g_i)); | ||
596 | /* r_ia = r_i * a */ | ||
597 | crypto_core_ed25519_scalar_mul (r_ia.v, | ||
598 | r_i.v, | ||
599 | my_privkey.v); | ||
600 | /* r_ia_ai = r_ia + a_i */ | ||
601 | { | ||
602 | int64_t val = s->sorted_elements[i].value; | ||
603 | struct GNUNET_CRYPTO_EccScalar vali; | ||
604 | |||
605 | GNUNET_assert (INT64_MIN != val); | ||
606 | GNUNET_CRYPTO_ecc_scalar_from_int (val > 0 ? val : -val, | ||
607 | &vali); | ||
608 | if (val > 0) | ||
609 | crypto_core_ed25519_scalar_add (r_ia_ai.v, | ||
610 | r_ia.v, | ||
611 | vali.v); | ||
612 | else | ||
613 | crypto_core_ed25519_scalar_sub (r_ia_ai.v, | ||
614 | r_ia.v, | ||
615 | vali.v); | ||
616 | } | ||
617 | /* h_i = g^{r_ia_ai} */ | ||
618 | GNUNET_assert (GNUNET_OK == | ||
619 | GNUNET_CRYPTO_ecc_dexp_mpi (&r_ia_ai, | ||
620 | &h_i)); | ||
621 | memcpy (&payload[(i - off) * 2], | ||
622 | &g_i, | ||
623 | sizeof (g_i)); | ||
624 | memcpy (&payload[(i - off) * 2 + 1], | ||
625 | &h_i, | ||
626 | sizeof (h_i)); | ||
627 | } | ||
628 | off += todo_count; | ||
629 | GNUNET_MQ_send (s->cadet_mq, | ||
630 | e); | ||
631 | } | ||
632 | } | ||
633 | |||
634 | |||
635 | /** | ||
636 | * Callback for set operation results. Called for each element | ||
637 | * that should be removed from the result set, and then once | ||
638 | * to indicate that the set intersection operation is done. | ||
639 | * | ||
640 | * @param cls closure with the `struct AliceServiceSession` | ||
641 | * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK | ||
642 | * @param current_size current set size | ||
643 | * @param status what has happened with the set intersection? | ||
644 | */ | ||
645 | static void | ||
646 | cb_intersection_element_removed (void *cls, | ||
647 | const struct GNUNET_SETI_Element *element, | ||
648 | uint64_t current_size, | ||
649 | enum GNUNET_SETI_Status status) | ||
650 | { | ||
651 | struct AliceServiceSession *s = cls; | ||
652 | struct GNUNET_SCALARPRODUCT_Element *se; | ||
653 | |||
654 | switch (status) | ||
655 | { | ||
656 | case GNUNET_SETI_STATUS_DEL_LOCAL: | ||
657 | /* this element has been removed from the set */ | ||
658 | se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements, | ||
659 | element->data); | ||
660 | GNUNET_assert (NULL != se); | ||
661 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
662 | "Intersection removed element with key %s and value %lld\n", | ||
663 | GNUNET_h2s (&se->key), | ||
664 | (long long) GNUNET_ntohll (se->value)); | ||
665 | GNUNET_assert ( | ||
666 | GNUNET_YES == | ||
667 | GNUNET_CONTAINER_multihashmap_remove (s->intersected_elements, | ||
668 | element->data, | ||
669 | se)); | ||
670 | GNUNET_free (se); | ||
671 | return; | ||
672 | case GNUNET_SETI_STATUS_DONE: | ||
673 | s->intersection_op = NULL; | ||
674 | if (NULL != s->intersection_set) | ||
675 | { | ||
676 | GNUNET_SETI_destroy (s->intersection_set); | ||
677 | s->intersection_set = NULL; | ||
678 | } | ||
679 | send_alices_cryptodata_message (s); | ||
680 | return; | ||
681 | case GNUNET_SETI_STATUS_FAILURE: | ||
682 | /* unhandled status code */ | ||
683 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Set intersection failed!\n"); | ||
684 | if (NULL != s->intersection_listen) | ||
685 | { | ||
686 | GNUNET_SETI_listen_cancel (s->intersection_listen); | ||
687 | s->intersection_listen = NULL; | ||
688 | } | ||
689 | s->intersection_op = NULL; | ||
690 | if (NULL != s->intersection_set) | ||
691 | { | ||
692 | GNUNET_SETI_destroy (s->intersection_set); | ||
693 | s->intersection_set = NULL; | ||
694 | } | ||
695 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
696 | prepare_client_end_notification (s); | ||
697 | return; | ||
698 | |||
699 | default: | ||
700 | GNUNET_break (0); | ||
701 | return; | ||
702 | } | ||
703 | } | ||
704 | |||
705 | |||
706 | /** | ||
707 | * Called when another peer wants to do a set operation with the | ||
708 | * local peer. If a listen error occurs, the @a request is NULL. | ||
709 | * | ||
710 | * @param cls closure with the `struct AliceServiceSession *` | ||
711 | * @param other_peer the other peer | ||
712 | * @param context_msg message with application specific information from | ||
713 | * the other peer | ||
714 | * @param request request from the other peer (never NULL), use GNUNET_SETI_accept() | ||
715 | * to accept it, otherwise the request will be refused | ||
716 | * Note that we can't just return value from the listen callback, | ||
717 | * as it is also necessary to specify the set we want to do the | ||
718 | * operation with, which sometimes can be derived from the context | ||
719 | * message. It's necessary to specify the timeout. | ||
720 | */ | ||
721 | static void | ||
722 | cb_intersection_request_alice (void *cls, | ||
723 | const struct GNUNET_PeerIdentity *other_peer, | ||
724 | const struct GNUNET_MessageHeader *context_msg, | ||
725 | struct GNUNET_SETI_Request *request) | ||
726 | { | ||
727 | struct AliceServiceSession *s = cls; | ||
728 | |||
729 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
730 | "Received intersection request from %s!\n", | ||
731 | GNUNET_i2s (other_peer)); | ||
732 | if (0 != GNUNET_memcmp (other_peer, | ||
733 | &s->peer)) | ||
734 | { | ||
735 | GNUNET_break_op (0); | ||
736 | return; | ||
737 | } | ||
738 | s->intersection_op | ||
739 | = GNUNET_SETI_accept (request, | ||
740 | (struct GNUNET_SETI_Option[]){ { 0 } }, | ||
741 | &cb_intersection_element_removed, | ||
742 | s); | ||
743 | if (NULL == s->intersection_op) | ||
744 | { | ||
745 | GNUNET_break (0); | ||
746 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
747 | prepare_client_end_notification (s); | ||
748 | return; | ||
749 | } | ||
750 | if (GNUNET_OK != | ||
751 | GNUNET_SETI_commit (s->intersection_op, | ||
752 | s->intersection_set)) | ||
753 | { | ||
754 | GNUNET_break (0); | ||
755 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
756 | prepare_client_end_notification (s); | ||
757 | return; | ||
758 | } | ||
759 | } | ||
760 | |||
761 | |||
762 | /** | ||
763 | * Our client has finished sending us its multipart message. | ||
764 | * | ||
765 | * @param s the service session context | ||
766 | */ | ||
767 | static void | ||
768 | client_request_complete_alice (struct AliceServiceSession *s) | ||
769 | { | ||
770 | struct GNUNET_MQ_MessageHandler cadet_handlers[] = { | ||
771 | GNUNET_MQ_hd_fixed_size (bobs_cryptodata_message, | ||
772 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_BOB_CRYPTODATA, | ||
773 | struct EccBobCryptodataMessage, | ||
774 | s), | ||
775 | GNUNET_MQ_handler_end () | ||
776 | }; | ||
777 | struct EccServiceRequestMessage *msg; | ||
778 | struct GNUNET_MQ_Envelope *e; | ||
779 | struct GNUNET_HashCode set_sid; | ||
780 | |||
781 | GNUNET_CRYPTO_hash (&s->session_id, | ||
782 | sizeof(struct GNUNET_HashCode), | ||
783 | &set_sid); | ||
784 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
785 | "Creating new channel for session with key %s.\n", | ||
786 | GNUNET_h2s (&s->session_id)); | ||
787 | s->channel = GNUNET_CADET_channel_create (my_cadet, | ||
788 | s, | ||
789 | &s->peer, | ||
790 | &s->session_id, | ||
791 | NULL, | ||
792 | &cb_channel_destruction, | ||
793 | cadet_handlers); | ||
794 | if (NULL == s->channel) | ||
795 | { | ||
796 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
797 | prepare_client_end_notification (s); | ||
798 | return; | ||
799 | } | ||
800 | s->cadet_mq = GNUNET_CADET_get_mq (s->channel); | ||
801 | s->intersection_listen = GNUNET_SETI_listen (cfg, | ||
802 | &set_sid, | ||
803 | &cb_intersection_request_alice, | ||
804 | s); | ||
805 | if (NULL == s->intersection_listen) | ||
806 | { | ||
807 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
808 | GNUNET_CADET_channel_destroy (s->channel); | ||
809 | s->channel = NULL; | ||
810 | prepare_client_end_notification (s); | ||
811 | return; | ||
812 | } | ||
813 | |||
814 | e = | ||
815 | GNUNET_MQ_msg (msg, | ||
816 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_SESSION_INITIALIZATION); | ||
817 | GNUNET_MQ_env_set_options (e, GNUNET_MQ_PRIO_CRITICAL_CONTROL); | ||
818 | msg->session_id = s->session_id; | ||
819 | GNUNET_MQ_send (s->cadet_mq, e); | ||
820 | } | ||
821 | |||
822 | |||
823 | /** | ||
824 | * We're receiving additional set data. Check if | ||
825 | * @a msg is well-formed. | ||
826 | * | ||
827 | * @param cls client identification of the client | ||
828 | * @param msg the actual message | ||
829 | * @return #GNUNET_OK if @a msg is well-formed | ||
830 | */ | ||
831 | static int | ||
832 | check_alice_client_message_multipart ( | ||
833 | void *cls, | ||
834 | const struct ComputationBobCryptodataMultipartMessage *msg) | ||
835 | { | ||
836 | struct AliceServiceSession *s = cls; | ||
837 | uint32_t contained_count; | ||
838 | uint16_t msize; | ||
839 | |||
840 | msize = ntohs (msg->header.size); | ||
841 | contained_count = ntohl (msg->element_count_contained); | ||
842 | if ((msize != | ||
843 | (sizeof(struct ComputationBobCryptodataMultipartMessage) | ||
844 | + contained_count * sizeof(struct GNUNET_SCALARPRODUCT_Element))) || | ||
845 | (0 == contained_count) || | ||
846 | (s->total == s->client_received_element_count) || | ||
847 | (s->total < s->client_received_element_count + contained_count)) | ||
848 | { | ||
849 | GNUNET_break_op (0); | ||
850 | return GNUNET_SYSERR; | ||
851 | } | ||
852 | return GNUNET_OK; | ||
853 | } | ||
854 | |||
855 | |||
856 | /** | ||
857 | * We're receiving additional set data. Add it to our | ||
858 | * set and if we are done, initiate the transaction. | ||
859 | * | ||
860 | * @param cls client identification of the client | ||
861 | * @param msg the actual message | ||
862 | */ | ||
863 | static void | ||
864 | handle_alice_client_message_multipart ( | ||
865 | void *cls, | ||
866 | const struct ComputationBobCryptodataMultipartMessage *msg) | ||
867 | { | ||
868 | struct AliceServiceSession *s = cls; | ||
869 | uint32_t contained_count; | ||
870 | const struct GNUNET_SCALARPRODUCT_Element *elements; | ||
871 | struct GNUNET_SETI_Element set_elem; | ||
872 | struct GNUNET_SCALARPRODUCT_Element *elem; | ||
873 | |||
874 | contained_count = ntohl (msg->element_count_contained); | ||
875 | s->client_received_element_count += contained_count; | ||
876 | elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1]; | ||
877 | for (uint32_t i = 0; i < contained_count; i++) | ||
878 | { | ||
879 | elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element); | ||
880 | GNUNET_memcpy (elem, | ||
881 | &elements[i], | ||
882 | sizeof(struct GNUNET_SCALARPRODUCT_Element)); | ||
883 | if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put ( | ||
884 | s->intersected_elements, | ||
885 | &elem->key, | ||
886 | elem, | ||
887 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) | ||
888 | { | ||
889 | GNUNET_break (0); | ||
890 | GNUNET_free (elem); | ||
891 | continue; | ||
892 | } | ||
893 | set_elem.data = &elem->key; | ||
894 | set_elem.size = sizeof(elem->key); | ||
895 | set_elem.element_type = 0; | ||
896 | GNUNET_SETI_add_element (s->intersection_set, &set_elem, NULL, NULL); | ||
897 | s->used_element_count++; | ||
898 | } | ||
899 | GNUNET_SERVICE_client_continue (s->client); | ||
900 | if (s->total != s->client_received_element_count) | ||
901 | { | ||
902 | /* more to come */ | ||
903 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
904 | "Received client multipart data, waiting for more!\n"); | ||
905 | return; | ||
906 | } | ||
907 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Launching computation\n"); | ||
908 | client_request_complete_alice (s); | ||
909 | } | ||
910 | |||
911 | |||
912 | /** | ||
913 | * Handler for Alice's client request message. | ||
914 | * Check that @a msg is well-formed. | ||
915 | * | ||
916 | * @param cls identification of the client | ||
917 | * @param msg the actual message | ||
918 | * @return #GNUNET_OK if @a msg is well-formed | ||
919 | */ | ||
920 | static int | ||
921 | check_alice_client_message (void *cls, | ||
922 | const struct AliceComputationMessage *msg) | ||
923 | { | ||
924 | struct AliceServiceSession *s = cls; | ||
925 | uint16_t msize; | ||
926 | uint32_t total_count; | ||
927 | uint32_t contained_count; | ||
928 | |||
929 | if (NULL != s->intersected_elements) | ||
930 | { | ||
931 | /* only one concurrent session per client connection allowed, | ||
932 | simplifies logic a lot... */ | ||
933 | GNUNET_break (0); | ||
934 | return GNUNET_SYSERR; | ||
935 | } | ||
936 | msize = ntohs (msg->header.size); | ||
937 | total_count = ntohl (msg->element_count_total); | ||
938 | contained_count = ntohl (msg->element_count_contained); | ||
939 | if ((0 == total_count) || (0 == contained_count) || | ||
940 | (msize != | ||
941 | (sizeof(struct AliceComputationMessage) | ||
942 | + contained_count * sizeof(struct GNUNET_SCALARPRODUCT_Element)))) | ||
943 | { | ||
944 | GNUNET_break_op (0); | ||
945 | return GNUNET_SYSERR; | ||
946 | } | ||
947 | return GNUNET_OK; | ||
948 | } | ||
949 | |||
950 | |||
951 | /** | ||
952 | * Handler for Alice's client request message. | ||
953 | * We are doing request-initiation to compute a scalar product with a peer. | ||
954 | * | ||
955 | * @param cls identification of the client | ||
956 | * @param msg the actual message | ||
957 | */ | ||
958 | static void | ||
959 | handle_alice_client_message (void *cls, | ||
960 | const struct AliceComputationMessage *msg) | ||
961 | { | ||
962 | struct AliceServiceSession *s = cls; | ||
963 | uint32_t contained_count; | ||
964 | uint32_t total_count; | ||
965 | const struct GNUNET_SCALARPRODUCT_Element *elements; | ||
966 | struct GNUNET_SETI_Element set_elem; | ||
967 | struct GNUNET_SCALARPRODUCT_Element *elem; | ||
968 | |||
969 | total_count = ntohl (msg->element_count_total); | ||
970 | contained_count = ntohl (msg->element_count_contained); | ||
971 | s->peer = msg->peer; | ||
972 | s->status = GNUNET_SCALARPRODUCT_STATUS_ACTIVE; | ||
973 | s->total = total_count; | ||
974 | s->client_received_element_count = contained_count; | ||
975 | s->session_id = msg->session_key; | ||
976 | elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1]; | ||
977 | s->intersected_elements = | ||
978 | GNUNET_CONTAINER_multihashmap_create (s->total, | ||
979 | GNUNET_YES); | ||
980 | s->intersection_set = GNUNET_SETI_create (cfg); | ||
981 | for (uint32_t i = 0; i < contained_count; i++) | ||
982 | { | ||
983 | if (0 == GNUNET_ntohll (elements[i].value)) | ||
984 | continue; | ||
985 | elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element); | ||
986 | *elem = elements[i]; | ||
987 | if (GNUNET_SYSERR == | ||
988 | GNUNET_CONTAINER_multihashmap_put ( | ||
989 | s->intersected_elements, | ||
990 | &elem->key, | ||
991 | elem, | ||
992 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) | ||
993 | { | ||
994 | /* element with same key encountered twice! */ | ||
995 | GNUNET_break (0); | ||
996 | GNUNET_free (elem); | ||
997 | continue; | ||
998 | } | ||
999 | set_elem.data = &elem->key; | ||
1000 | set_elem.size = sizeof(elem->key); | ||
1001 | set_elem.element_type = 0; | ||
1002 | GNUNET_SETI_add_element (s->intersection_set, | ||
1003 | &set_elem, | ||
1004 | NULL, | ||
1005 | NULL); | ||
1006 | s->used_element_count++; | ||
1007 | } | ||
1008 | GNUNET_SERVICE_client_continue (s->client); | ||
1009 | if (s->total != s->client_received_element_count) | ||
1010 | { | ||
1011 | /* wait for multipart msg */ | ||
1012 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1013 | "Received partial client request, waiting for more!\n"); | ||
1014 | return; | ||
1015 | } | ||
1016 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1017 | "Launching computation\n"); | ||
1018 | client_request_complete_alice (s); | ||
1019 | } | ||
1020 | |||
1021 | |||
1022 | /** | ||
1023 | * Task run during shutdown. | ||
1024 | * | ||
1025 | * @param cls unused | ||
1026 | * @param tc unused | ||
1027 | */ | ||
1028 | static void | ||
1029 | shutdown_task (void *cls) | ||
1030 | { | ||
1031 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1032 | "Shutting down, initiating cleanup.\n"); | ||
1033 | // FIXME: we have to cut our connections to CADET first! | ||
1034 | if (NULL != my_cadet) | ||
1035 | { | ||
1036 | GNUNET_CADET_disconnect (my_cadet); | ||
1037 | my_cadet = NULL; | ||
1038 | } | ||
1039 | if (NULL != edc) | ||
1040 | { | ||
1041 | GNUNET_CRYPTO_ecc_dlog_release (edc); | ||
1042 | edc = NULL; | ||
1043 | } | ||
1044 | } | ||
1045 | |||
1046 | |||
1047 | /** | ||
1048 | * A client connected. | ||
1049 | * | ||
1050 | * Setup the associated data structure. | ||
1051 | * | ||
1052 | * @param cls closure, NULL | ||
1053 | * @param client identification of the client | ||
1054 | * @param mq message queue to communicate with @a client | ||
1055 | * @return our `struct AliceServiceSession` | ||
1056 | */ | ||
1057 | static void * | ||
1058 | client_connect_cb (void *cls, | ||
1059 | struct GNUNET_SERVICE_Client *client, | ||
1060 | struct GNUNET_MQ_Handle *mq) | ||
1061 | { | ||
1062 | struct AliceServiceSession *s; | ||
1063 | |||
1064 | s = GNUNET_new (struct AliceServiceSession); | ||
1065 | s->client = client; | ||
1066 | s->client_mq = mq; | ||
1067 | return s; | ||
1068 | } | ||
1069 | |||
1070 | |||
1071 | /** | ||
1072 | * A client disconnected. | ||
1073 | * | ||
1074 | * Remove the associated session(s), release data structures | ||
1075 | * and cancel pending outgoing transmissions to the client. | ||
1076 | * | ||
1077 | * @param cls closure, NULL | ||
1078 | * @param client identification of the client | ||
1079 | * @param app_cls our `struct AliceServiceSession` | ||
1080 | */ | ||
1081 | static void | ||
1082 | client_disconnect_cb (void *cls, | ||
1083 | struct GNUNET_SERVICE_Client *client, | ||
1084 | void *app_cls) | ||
1085 | { | ||
1086 | struct AliceServiceSession *s = app_cls; | ||
1087 | |||
1088 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1089 | "Client %p disconnected from us.\n", | ||
1090 | client); | ||
1091 | s->client = NULL; | ||
1092 | s->client_mq = NULL; | ||
1093 | destroy_service_session (s); | ||
1094 | } | ||
1095 | |||
1096 | |||
1097 | /** | ||
1098 | * Initialization of the program and message handlers | ||
1099 | * | ||
1100 | * @param cls closure | ||
1101 | * @param c configuration to use | ||
1102 | * @param service the initialized service | ||
1103 | */ | ||
1104 | static void | ||
1105 | run (void *cls, | ||
1106 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
1107 | struct GNUNET_SERVICE_Handle *service) | ||
1108 | { | ||
1109 | cfg = c; | ||
1110 | edc = GNUNET_CRYPTO_ecc_dlog_prepare (MAX_RESULT, | ||
1111 | MAX_RAM); | ||
1112 | /* Select a random 'a' value for Alice */ | ||
1113 | GNUNET_CRYPTO_ecc_rnd_mpi (&my_privkey, | ||
1114 | &my_privkey_inv); | ||
1115 | my_cadet = GNUNET_CADET_connect (cfg); | ||
1116 | if (NULL == my_cadet) | ||
1117 | { | ||
1118 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1119 | _ ("Connect to CADET failed\n")); | ||
1120 | GNUNET_SCHEDULER_shutdown (); | ||
1121 | return; | ||
1122 | } | ||
1123 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
1124 | NULL); | ||
1125 | } | ||
1126 | |||
1127 | |||
1128 | /** | ||
1129 | * Define "main" method using service macro. | ||
1130 | */ | ||
1131 | GNUNET_SERVICE_MAIN ( | ||
1132 | "scalarproduct-alice", | ||
1133 | GNUNET_SERVICE_OPTION_NONE, | ||
1134 | &run, | ||
1135 | &client_connect_cb, | ||
1136 | &client_disconnect_cb, | ||
1137 | NULL, | ||
1138 | GNUNET_MQ_hd_var_size (alice_client_message, | ||
1139 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE, | ||
1140 | struct AliceComputationMessage, | ||
1141 | NULL), | ||
1142 | GNUNET_MQ_hd_var_size ( | ||
1143 | alice_client_message_multipart, | ||
1144 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_ALICE, | ||
1145 | struct ComputationBobCryptodataMultipartMessage, | ||
1146 | NULL), | ||
1147 | GNUNET_MQ_handler_end ()); | ||
1148 | |||
1149 | |||
1150 | /* end of gnunet-service-scalarproduct-ecc_alice.c */ | ||
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c new file mode 100644 index 000000000..1945f1937 --- /dev/null +++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c | |||
@@ -0,0 +1,1060 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2013-2017, 2021 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_bob.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_seti_service.h" | ||
36 | #include "scalarproduct.h" | ||
37 | #include "gnunet-service-scalarproduct-ecc.h" | ||
38 | |||
39 | #define LOG(kind, ...) GNUNET_log_from (kind, "scalarproduct-bob", __VA_ARGS__) | ||
40 | |||
41 | |||
42 | /** | ||
43 | * An encrypted element key-value pair. | ||
44 | */ | ||
45 | struct MpiElement | ||
46 | { | ||
47 | /** | ||
48 | * Key used to identify matching pairs of values to multiply. | ||
49 | * Points into an existing data structure, to avoid copying | ||
50 | * and doubling memory use. | ||
51 | */ | ||
52 | const struct GNUNET_HashCode *key; | ||
53 | |||
54 | /** | ||
55 | * Value represented (a). | ||
56 | */ | ||
57 | int64_t value; | ||
58 | }; | ||
59 | |||
60 | |||
61 | /** | ||
62 | * A scalarproduct session which tracks an offer for a | ||
63 | * multiplication service by a local client. | ||
64 | */ | ||
65 | struct BobServiceSession | ||
66 | { | ||
67 | /** | ||
68 | * The client this request is related to. | ||
69 | */ | ||
70 | struct GNUNET_SERVICE_Client *client; | ||
71 | |||
72 | /** | ||
73 | * Client message queue. | ||
74 | */ | ||
75 | struct GNUNET_MQ_Handle *client_mq; | ||
76 | |||
77 | /** | ||
78 | * All non-0-value'd elements transmitted to us. | ||
79 | */ | ||
80 | struct GNUNET_CONTAINER_MultiHashMap *intersected_elements; | ||
81 | |||
82 | /** | ||
83 | * Set of elements for which we will be conducting an intersection. | ||
84 | * The resulting elements are then used for computing the scalar product. | ||
85 | */ | ||
86 | struct GNUNET_SETI_Handle *intersection_set; | ||
87 | |||
88 | /** | ||
89 | * Set of elements for which will conduction an intersection. | ||
90 | * the resulting elements are then used for computing the scalar product. | ||
91 | */ | ||
92 | struct GNUNET_SETI_OperationHandle *intersection_op; | ||
93 | |||
94 | /** | ||
95 | * Our open port. | ||
96 | */ | ||
97 | struct GNUNET_CADET_Port *port; | ||
98 | |||
99 | /** | ||
100 | * b(Bob) | ||
101 | */ | ||
102 | struct MpiElement *sorted_elements; | ||
103 | |||
104 | /** | ||
105 | * Product of the g_i^{b_i} | ||
106 | */ | ||
107 | struct GNUNET_CRYPTO_EccPoint prod_g_i_b_i; | ||
108 | |||
109 | /** | ||
110 | * Product of the h_i^{b_i} | ||
111 | */ | ||
112 | struct GNUNET_CRYPTO_EccPoint prod_h_i_b_i; | ||
113 | |||
114 | /** | ||
115 | * How many elements will be supplied in total from the client. | ||
116 | */ | ||
117 | uint32_t total; | ||
118 | |||
119 | /** | ||
120 | * Already transferred elements (received) for multipart | ||
121 | * messages from client. Always less than @e total. | ||
122 | */ | ||
123 | uint32_t client_received_element_count; | ||
124 | |||
125 | /** | ||
126 | * How many elements actually are used for the scalar product. | ||
127 | * Size of the arrays in @e r and @e r_prime. Also sometimes | ||
128 | * used as an index into the arrays during construction. | ||
129 | */ | ||
130 | uint32_t used_element_count; | ||
131 | |||
132 | /** | ||
133 | * Counts the number of values received from Alice by us. | ||
134 | * Always less than @e used_element_count. | ||
135 | */ | ||
136 | uint32_t cadet_received_element_count; | ||
137 | |||
138 | /** | ||
139 | * State of this session. In | ||
140 | * #GNUNET_SCALARPRODUCT_STATUS_ACTIVE while operation is | ||
141 | * ongoing, afterwards in #GNUNET_SCALARPRODUCT_STATUS_SUCCESS or | ||
142 | * #GNUNET_SCALARPRODUCT_STATUS_FAILURE. | ||
143 | */ | ||
144 | enum GNUNET_SCALARPRODUCT_ResponseStatus status; | ||
145 | |||
146 | /** | ||
147 | * Are we already in #destroy_service_session()? | ||
148 | */ | ||
149 | int in_destroy; | ||
150 | |||
151 | /** | ||
152 | * The CADET channel. | ||
153 | */ | ||
154 | struct GNUNET_CADET_Channel *channel; | ||
155 | |||
156 | /** | ||
157 | * Originator's peer identity. (Only for diagnostics.) | ||
158 | */ | ||
159 | struct GNUNET_PeerIdentity peer; | ||
160 | |||
161 | /** | ||
162 | * (hopefully) unique transaction ID | ||
163 | */ | ||
164 | struct GNUNET_HashCode session_id; | ||
165 | |||
166 | /** | ||
167 | * The message queue for this channel. | ||
168 | */ | ||
169 | struct GNUNET_MQ_Handle *cadet_mq; | ||
170 | }; | ||
171 | |||
172 | |||
173 | /** | ||
174 | * GNUnet configuration handle | ||
175 | */ | ||
176 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
177 | |||
178 | /** | ||
179 | * Handle to the CADET service. | ||
180 | */ | ||
181 | static struct GNUNET_CADET_Handle *my_cadet; | ||
182 | |||
183 | /** | ||
184 | * Context for DLOG operations on a curve. | ||
185 | */ | ||
186 | static struct GNUNET_CRYPTO_EccDlogContext *edc; | ||
187 | |||
188 | |||
189 | /** | ||
190 | * Callback used to free the elements in the map. | ||
191 | * | ||
192 | * @param cls NULL | ||
193 | * @param key key of the element | ||
194 | * @param value the value to free | ||
195 | */ | ||
196 | static int | ||
197 | free_element_cb (void *cls, | ||
198 | const struct GNUNET_HashCode *key, | ||
199 | void *value) | ||
200 | { | ||
201 | struct GNUNET_SCALARPRODUCT_Element *element = value; | ||
202 | |||
203 | GNUNET_free (element); | ||
204 | return GNUNET_OK; | ||
205 | } | ||
206 | |||
207 | |||
208 | /** | ||
209 | * Destroy session state, we are done with it. | ||
210 | * | ||
211 | * @param s the session to free elements from | ||
212 | */ | ||
213 | static void | ||
214 | destroy_service_session (struct BobServiceSession *s) | ||
215 | { | ||
216 | if (GNUNET_YES == s->in_destroy) | ||
217 | return; | ||
218 | s->in_destroy = GNUNET_YES; | ||
219 | if (NULL != s->client) | ||
220 | { | ||
221 | struct GNUNET_SERVICE_Client *c = s->client; | ||
222 | |||
223 | s->client = NULL; | ||
224 | GNUNET_SERVICE_client_drop (c); | ||
225 | } | ||
226 | if (NULL != s->intersected_elements) | ||
227 | { | ||
228 | GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements, | ||
229 | &free_element_cb, | ||
230 | NULL); | ||
231 | GNUNET_CONTAINER_multihashmap_destroy (s->intersected_elements); | ||
232 | s->intersected_elements = NULL; | ||
233 | } | ||
234 | if (NULL != s->intersection_op) | ||
235 | { | ||
236 | GNUNET_SETI_operation_cancel (s->intersection_op); | ||
237 | s->intersection_op = NULL; | ||
238 | } | ||
239 | if (NULL != s->intersection_set) | ||
240 | { | ||
241 | GNUNET_SETI_destroy (s->intersection_set); | ||
242 | s->intersection_set = NULL; | ||
243 | } | ||
244 | if (NULL != s->sorted_elements) | ||
245 | { | ||
246 | GNUNET_free (s->sorted_elements); | ||
247 | s->sorted_elements = NULL; | ||
248 | } | ||
249 | if (NULL != s->port) | ||
250 | { | ||
251 | GNUNET_CADET_close_port (s->port); | ||
252 | s->port = NULL; | ||
253 | } | ||
254 | if (NULL != s->channel) | ||
255 | { | ||
256 | GNUNET_CADET_channel_destroy (s->channel); | ||
257 | s->channel = NULL; | ||
258 | } | ||
259 | GNUNET_free (s); | ||
260 | } | ||
261 | |||
262 | |||
263 | /** | ||
264 | * Notify the client that the session has succeeded or failed. This | ||
265 | * message gets sent to Bob's client if the operation completed or | ||
266 | * Alice disconnected. | ||
267 | * | ||
268 | * @param session the associated client session to fail or succeed | ||
269 | */ | ||
270 | static void | ||
271 | prepare_client_end_notification (struct BobServiceSession *session) | ||
272 | { | ||
273 | struct ClientResponseMessage *msg; | ||
274 | struct GNUNET_MQ_Envelope *e; | ||
275 | |||
276 | if (NULL == session->client_mq) | ||
277 | return; /* no client left to be notified */ | ||
278 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
279 | "Sending session-end notification with status %d to client for session %s\n", | ||
280 | session->status, | ||
281 | GNUNET_h2s (&session->session_id)); | ||
282 | e = GNUNET_MQ_msg (msg, | ||
283 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT); | ||
284 | msg->range = 0; | ||
285 | msg->product_length = htonl (0); | ||
286 | msg->status = htonl (session->status); | ||
287 | GNUNET_MQ_send (session->client_mq, | ||
288 | e); | ||
289 | } | ||
290 | |||
291 | |||
292 | /** | ||
293 | * Function called whenever a channel is destroyed. Should clean up | ||
294 | * any associated state. | ||
295 | * | ||
296 | * It must NOT call #GNUNET_CADET_channel_destroy() on the channel. | ||
297 | * | ||
298 | * @param cls the `struct BobServiceSession` | ||
299 | * @param channel connection to the other end (henceforth invalid) | ||
300 | */ | ||
301 | static void | ||
302 | cb_channel_destruction (void *cls, | ||
303 | const struct GNUNET_CADET_Channel *channel) | ||
304 | { | ||
305 | struct BobServiceSession *s = cls; | ||
306 | |||
307 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
308 | "Peer disconnected, terminating session %s with peer %s\n", | ||
309 | GNUNET_h2s (&s->session_id), | ||
310 | GNUNET_i2s (&s->peer)); | ||
311 | s->channel = NULL; | ||
312 | if (GNUNET_SCALARPRODUCT_STATUS_ACTIVE == s->status) | ||
313 | { | ||
314 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
315 | prepare_client_end_notification (s); | ||
316 | } | ||
317 | destroy_service_session (s); | ||
318 | } | ||
319 | |||
320 | |||
321 | /** | ||
322 | * MQ finished giving our last message to CADET, now notify | ||
323 | * the client that we are finished. | ||
324 | */ | ||
325 | static void | ||
326 | bob_cadet_done_cb (void *cls) | ||
327 | { | ||
328 | struct BobServiceSession *session = cls; | ||
329 | |||
330 | session->status = GNUNET_SCALARPRODUCT_STATUS_SUCCESS; | ||
331 | prepare_client_end_notification (session); | ||
332 | } | ||
333 | |||
334 | |||
335 | /** | ||
336 | * Bob generates the response message to be sent to Alice. | ||
337 | * | ||
338 | * @param s the associated requesting session with Alice | ||
339 | */ | ||
340 | static void | ||
341 | transmit_bobs_cryptodata_message (struct BobServiceSession *s) | ||
342 | { | ||
343 | struct EccBobCryptodataMessage *msg; | ||
344 | struct GNUNET_MQ_Envelope *e; | ||
345 | |||
346 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
347 | "Sending response to Alice\n"); | ||
348 | e = GNUNET_MQ_msg (msg, | ||
349 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_BOB_CRYPTODATA); | ||
350 | msg->contained_element_count = htonl (2); | ||
351 | msg->prod_g_i_b_i = s->prod_g_i_b_i; | ||
352 | msg->prod_h_i_b_i = s->prod_h_i_b_i; | ||
353 | GNUNET_MQ_notify_sent (e, | ||
354 | &bob_cadet_done_cb, | ||
355 | s); | ||
356 | GNUNET_MQ_send (s->cadet_mq, | ||
357 | e); | ||
358 | } | ||
359 | |||
360 | |||
361 | /** | ||
362 | * Iterator to copy over messages from the hash map | ||
363 | * into an array for sorting. | ||
364 | * | ||
365 | * @param cls the `struct AliceServiceSession *` | ||
366 | * @param key the key (unused) | ||
367 | * @param value the `struct GNUNET_SCALARPRODUCT_Element *` | ||
368 | */ | ||
369 | static int | ||
370 | copy_element_cb (void *cls, | ||
371 | const struct GNUNET_HashCode *key, | ||
372 | void *value) | ||
373 | { | ||
374 | struct BobServiceSession *s = cls; | ||
375 | struct GNUNET_SCALARPRODUCT_Element *e = value; | ||
376 | |||
377 | s->sorted_elements[s->used_element_count].value = (int64_t) GNUNET_ntohll ( | ||
378 | e->value); | ||
379 | s->sorted_elements[s->used_element_count].key = &e->key; | ||
380 | s->used_element_count++; | ||
381 | return GNUNET_OK; | ||
382 | } | ||
383 | |||
384 | |||
385 | /** | ||
386 | * Compare two `struct MpiValue`s by key for sorting. | ||
387 | * | ||
388 | * @param a pointer to first `struct MpiValue *` | ||
389 | * @param b pointer to first `struct MpiValue *` | ||
390 | * @return -1 for a < b, 0 for a=b, 1 for a > b. | ||
391 | * TODO: code duplication with Alice! | ||
392 | */ | ||
393 | static int | ||
394 | element_cmp (const void *a, | ||
395 | const void *b) | ||
396 | { | ||
397 | const struct MpiElement *ma = a; | ||
398 | const struct MpiElement *mb = b; | ||
399 | |||
400 | return GNUNET_CRYPTO_hash_cmp (ma->key, | ||
401 | mb->key); | ||
402 | } | ||
403 | |||
404 | |||
405 | /** | ||
406 | * Check a multipart-chunk of a request from another service to | ||
407 | * calculate a scalarproduct with us. | ||
408 | * | ||
409 | * @param cls closure (set from #GNUNET_CADET_connect) | ||
410 | * @param msg the actual message | ||
411 | * @return #GNUNET_OK to keep the connection open, | ||
412 | * #GNUNET_SYSERR to close it (signal serious error) | ||
413 | */ | ||
414 | static int | ||
415 | check_alices_cryptodata_message (void *cls, | ||
416 | const struct EccAliceCryptodataMessage *msg) | ||
417 | { | ||
418 | struct BobServiceSession *s = cls; | ||
419 | uint32_t contained_elements; | ||
420 | size_t msg_length; | ||
421 | uint16_t msize; | ||
422 | unsigned int max; | ||
423 | |||
424 | msize = ntohs (msg->header.size); | ||
425 | if (msize <= sizeof(struct EccAliceCryptodataMessage)) | ||
426 | { | ||
427 | GNUNET_break_op (0); | ||
428 | return GNUNET_SYSERR; | ||
429 | } | ||
430 | contained_elements = ntohl (msg->contained_element_count); | ||
431 | /* Our intersection may still be ongoing, but this is nevertheless | ||
432 | an upper bound on the required array size */ | ||
433 | max = GNUNET_CONTAINER_multihashmap_size (s->intersected_elements); | ||
434 | msg_length = sizeof(struct EccAliceCryptodataMessage) | ||
435 | + contained_elements * sizeof(struct GNUNET_CRYPTO_EccPoint) * 2; | ||
436 | if ((msize != msg_length) || | ||
437 | (0 == contained_elements) || | ||
438 | (contained_elements > UINT16_MAX) || | ||
439 | (max < contained_elements + s->cadet_received_element_count)) | ||
440 | { | ||
441 | GNUNET_break_op (0); | ||
442 | return GNUNET_SYSERR; | ||
443 | } | ||
444 | return GNUNET_OK; | ||
445 | } | ||
446 | |||
447 | |||
448 | /** | ||
449 | * Handle a multipart-chunk of a request from another service to | ||
450 | * calculate a scalarproduct with us. | ||
451 | * | ||
452 | * @param cls closure (set from #GNUNET_CADET_connect) | ||
453 | * @param msg the actual message | ||
454 | */ | ||
455 | static void | ||
456 | handle_alices_cryptodata_message (void *cls, | ||
457 | const struct EccAliceCryptodataMessage *msg) | ||
458 | { | ||
459 | struct BobServiceSession *s = cls; | ||
460 | const struct GNUNET_CRYPTO_EccPoint *payload; | ||
461 | uint32_t contained_elements; | ||
462 | unsigned int max; | ||
463 | const struct GNUNET_CRYPTO_EccPoint *g_i; | ||
464 | const struct GNUNET_CRYPTO_EccPoint *h_i; | ||
465 | struct GNUNET_CRYPTO_EccPoint g_i_b_i; | ||
466 | struct GNUNET_CRYPTO_EccPoint h_i_b_i; | ||
467 | |||
468 | contained_elements = ntohl (msg->contained_element_count); | ||
469 | max = GNUNET_CONTAINER_multihashmap_size (s->intersected_elements); | ||
470 | /* sort our vector for the computation */ | ||
471 | if (NULL == s->sorted_elements) | ||
472 | { | ||
473 | s->sorted_elements | ||
474 | = GNUNET_new_array (GNUNET_CONTAINER_multihashmap_size ( | ||
475 | s->intersected_elements), | ||
476 | struct MpiElement); | ||
477 | s->used_element_count = 0; | ||
478 | GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements, | ||
479 | ©_element_cb, | ||
480 | s); | ||
481 | qsort (s->sorted_elements, | ||
482 | s->used_element_count, | ||
483 | sizeof(struct MpiElement), | ||
484 | &element_cmp); | ||
485 | } | ||
486 | |||
487 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
488 | "Received %u crypto values from Alice\n", | ||
489 | (unsigned int) contained_elements); | ||
490 | payload = (const struct GNUNET_CRYPTO_EccPoint *) &msg[1]; | ||
491 | |||
492 | for (unsigned int i = 0; i < contained_elements; i++) | ||
493 | { | ||
494 | int64_t val = s->sorted_elements[i + s->cadet_received_element_count].value; | ||
495 | struct GNUNET_CRYPTO_EccScalar vali; | ||
496 | |||
497 | GNUNET_assert (INT64_MIN != val); | ||
498 | GNUNET_CRYPTO_ecc_scalar_from_int (val > 0 ? val : -val, | ||
499 | &vali); | ||
500 | if (val < 0) | ||
501 | crypto_core_ed25519_scalar_negate (vali.v, | ||
502 | vali.v); | ||
503 | g_i = &payload[i * 2]; | ||
504 | /* g_i_b_i = g_i^vali */ | ||
505 | GNUNET_assert (GNUNET_OK == | ||
506 | GNUNET_CRYPTO_ecc_pmul_mpi (g_i, | ||
507 | &vali, | ||
508 | &g_i_b_i)); | ||
509 | h_i = &payload[i * 2 + 1]; | ||
510 | /* h_i_b_i = h_i^vali */ | ||
511 | GNUNET_assert (GNUNET_OK == | ||
512 | GNUNET_CRYPTO_ecc_pmul_mpi (h_i, | ||
513 | &vali, | ||
514 | &h_i_b_i)); | ||
515 | if (0 == i + s->cadet_received_element_count) | ||
516 | { | ||
517 | /* first iteration, nothing to add */ | ||
518 | s->prod_g_i_b_i = g_i_b_i; | ||
519 | s->prod_h_i_b_i = h_i_b_i; | ||
520 | } | ||
521 | else | ||
522 | { | ||
523 | /* further iterations, cummulate resulting value */ | ||
524 | GNUNET_assert (GNUNET_OK == | ||
525 | GNUNET_CRYPTO_ecc_add (&s->prod_g_i_b_i, | ||
526 | &g_i_b_i, | ||
527 | &s->prod_g_i_b_i)); | ||
528 | GNUNET_assert (GNUNET_OK == | ||
529 | GNUNET_CRYPTO_ecc_add (&s->prod_h_i_b_i, | ||
530 | &h_i_b_i, | ||
531 | &s->prod_h_i_b_i)); | ||
532 | } | ||
533 | } | ||
534 | s->cadet_received_element_count += contained_elements; | ||
535 | if ((s->cadet_received_element_count == max) && | ||
536 | (NULL == s->intersection_op)) | ||
537 | { | ||
538 | /* intersection has finished also on our side, and | ||
539 | we got the full set, so we can proceed with the | ||
540 | CADET response(s) */ | ||
541 | transmit_bobs_cryptodata_message (s); | ||
542 | } | ||
543 | GNUNET_CADET_receive_done (s->channel); | ||
544 | } | ||
545 | |||
546 | |||
547 | /** | ||
548 | * Callback for set operation results. Called for each element | ||
549 | * that needs to be removed from the result set. | ||
550 | * | ||
551 | * @param cls closure with the `struct BobServiceSession` | ||
552 | * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK | ||
553 | * @param current_size current set size | ||
554 | * @param status what has happened with the set intersection? | ||
555 | */ | ||
556 | static void | ||
557 | cb_intersection_element_removed (void *cls, | ||
558 | const struct GNUNET_SETI_Element *element, | ||
559 | uint64_t current_size, | ||
560 | enum GNUNET_SETI_Status status) | ||
561 | { | ||
562 | struct BobServiceSession *s = cls; | ||
563 | struct GNUNET_SCALARPRODUCT_Element *se; | ||
564 | |||
565 | switch (status) | ||
566 | { | ||
567 | case GNUNET_SETI_STATUS_DEL_LOCAL: | ||
568 | /* this element has been removed from the set */ | ||
569 | se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements, | ||
570 | element->data); | ||
571 | GNUNET_assert (NULL != se); | ||
572 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
573 | "Removed element with key %s and value %lld\n", | ||
574 | GNUNET_h2s (&se->key), | ||
575 | (long long) GNUNET_ntohll (se->value)); | ||
576 | GNUNET_assert (GNUNET_YES == | ||
577 | GNUNET_CONTAINER_multihashmap_remove ( | ||
578 | s->intersected_elements, | ||
579 | element->data, | ||
580 | se)); | ||
581 | GNUNET_free (se); | ||
582 | return; | ||
583 | case GNUNET_SETI_STATUS_DONE: | ||
584 | s->intersection_op = NULL; | ||
585 | GNUNET_break (NULL == s->intersection_set); | ||
586 | GNUNET_CADET_receive_done (s->channel); | ||
587 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
588 | "Finished intersection, %d items remain\n", | ||
589 | GNUNET_CONTAINER_multihashmap_size (s->intersected_elements)); | ||
590 | if (s->client_received_element_count == | ||
591 | GNUNET_CONTAINER_multihashmap_size (s->intersected_elements)) | ||
592 | { | ||
593 | /* CADET transmission from Alice is also already done, | ||
594 | start with our own reply */ | ||
595 | transmit_bobs_cryptodata_message (s); | ||
596 | } | ||
597 | return; | ||
598 | case GNUNET_SETI_STATUS_FAILURE: | ||
599 | /* unhandled status code */ | ||
600 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
601 | "Set intersection failed!\n"); | ||
602 | s->intersection_op = NULL; | ||
603 | if (NULL != s->intersection_set) | ||
604 | { | ||
605 | GNUNET_SETI_destroy (s->intersection_set); | ||
606 | s->intersection_set = NULL; | ||
607 | } | ||
608 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
609 | prepare_client_end_notification (s); | ||
610 | return; | ||
611 | |||
612 | default: | ||
613 | GNUNET_break (0); | ||
614 | return; | ||
615 | } | ||
616 | } | ||
617 | |||
618 | |||
619 | /** | ||
620 | * We've paired up a client session with an incoming CADET request. | ||
621 | * Initiate set intersection work. | ||
622 | * | ||
623 | * @param s client session to start intersection for | ||
624 | */ | ||
625 | static void | ||
626 | start_intersection (struct BobServiceSession *s) | ||
627 | { | ||
628 | struct GNUNET_HashCode set_sid; | ||
629 | |||
630 | GNUNET_CRYPTO_hash (&s->session_id, | ||
631 | sizeof(struct GNUNET_HashCode), | ||
632 | &set_sid); | ||
633 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
634 | "Got session with key %s and %u elements, starting intersection.\n", | ||
635 | GNUNET_h2s (&s->session_id), | ||
636 | (unsigned int) s->total); | ||
637 | |||
638 | s->intersection_op | ||
639 | = GNUNET_SETI_prepare (&s->peer, | ||
640 | &set_sid, | ||
641 | NULL, | ||
642 | (struct GNUNET_SETI_Option[]) { { 0 } }, | ||
643 | &cb_intersection_element_removed, | ||
644 | s); | ||
645 | if (GNUNET_OK != | ||
646 | GNUNET_SETI_commit (s->intersection_op, | ||
647 | s->intersection_set)) | ||
648 | { | ||
649 | GNUNET_break (0); | ||
650 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
651 | prepare_client_end_notification (s); | ||
652 | return; | ||
653 | } | ||
654 | GNUNET_SETI_destroy (s->intersection_set); | ||
655 | s->intersection_set = NULL; | ||
656 | } | ||
657 | |||
658 | |||
659 | /** | ||
660 | * Handle a request from Alice to calculate a scalarproduct with us (Bob). | ||
661 | * | ||
662 | * @param cls closure (set from #GNUNET_CADET_connect) | ||
663 | * @param msg the actual message | ||
664 | */ | ||
665 | static void | ||
666 | handle_alices_computation_request (void *cls, | ||
667 | const struct EccServiceRequestMessage *msg) | ||
668 | { | ||
669 | struct BobServiceSession *s = cls; | ||
670 | |||
671 | s->session_id = msg->session_id; // ?? | ||
672 | if (s->client_received_element_count < s->total) | ||
673 | { | ||
674 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
675 | "Alice ready, still waiting for Bob client data!\n"); | ||
676 | return; | ||
677 | } | ||
678 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
679 | "Both ready, launching intersection!\n"); | ||
680 | start_intersection (s); | ||
681 | } | ||
682 | |||
683 | |||
684 | /** | ||
685 | * Function called for inbound channels on Bob's end. Does some | ||
686 | * preliminary initialization, more happens after we get Alice's first | ||
687 | * message. | ||
688 | * | ||
689 | * @param cls our `struct BobServiceSession` | ||
690 | * @param channel new handle to the channel | ||
691 | * @param initiator peer that started the channel | ||
692 | * @return session associated with the channel | ||
693 | */ | ||
694 | static void * | ||
695 | cb_channel_incoming (void *cls, | ||
696 | struct GNUNET_CADET_Channel *channel, | ||
697 | const struct GNUNET_PeerIdentity *initiator) | ||
698 | { | ||
699 | struct BobServiceSession *s = cls; | ||
700 | |||
701 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
702 | "New incoming channel from peer %s.\n", | ||
703 | GNUNET_i2s (initiator)); | ||
704 | GNUNET_CADET_close_port (s->port); | ||
705 | s->port = NULL; | ||
706 | s->peer = *initiator; | ||
707 | s->channel = channel; | ||
708 | s->cadet_mq = GNUNET_CADET_get_mq (s->channel); | ||
709 | return s; | ||
710 | } | ||
711 | |||
712 | |||
713 | /** | ||
714 | * We're receiving additional set data. Check it is well-formed. | ||
715 | * | ||
716 | * @param cls identification of the client | ||
717 | * @param msg the actual message | ||
718 | * @return #GNUNET_OK if @a msg is well-formed | ||
719 | */ | ||
720 | static int | ||
721 | check_bob_client_message_multipart ( | ||
722 | void *cls, | ||
723 | const struct ComputationBobCryptodataMultipartMessage *msg) | ||
724 | { | ||
725 | struct BobServiceSession *s = cls; | ||
726 | uint32_t contained_count; | ||
727 | uint16_t msize; | ||
728 | |||
729 | msize = ntohs (msg->header.size); | ||
730 | contained_count = ntohl (msg->element_count_contained); | ||
731 | if ((msize != (sizeof(struct ComputationBobCryptodataMultipartMessage) | ||
732 | + contained_count * sizeof(struct | ||
733 | GNUNET_SCALARPRODUCT_Element))) || | ||
734 | (0 == contained_count) || | ||
735 | (UINT16_MAX < contained_count) || | ||
736 | (s->total == s->client_received_element_count) || | ||
737 | (s->total < s->client_received_element_count + contained_count)) | ||
738 | { | ||
739 | GNUNET_break (0); | ||
740 | return GNUNET_SYSERR; | ||
741 | } | ||
742 | return GNUNET_OK; | ||
743 | } | ||
744 | |||
745 | |||
746 | /** | ||
747 | * We're receiving additional set data. Add it to our | ||
748 | * set and if we are done, initiate the transaction. | ||
749 | * | ||
750 | * @param cls identification of the client | ||
751 | * @param msg the actual message | ||
752 | */ | ||
753 | static void | ||
754 | handle_bob_client_message_multipart ( | ||
755 | void *cls, | ||
756 | const struct ComputationBobCryptodataMultipartMessage *msg) | ||
757 | { | ||
758 | struct BobServiceSession *s = cls; | ||
759 | uint32_t contained_count; | ||
760 | const struct GNUNET_SCALARPRODUCT_Element *elements; | ||
761 | struct GNUNET_SETI_Element set_elem; | ||
762 | struct GNUNET_SCALARPRODUCT_Element *elem; | ||
763 | |||
764 | contained_count = ntohl (msg->element_count_contained); | ||
765 | elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1]; | ||
766 | for (uint32_t i = 0; i < contained_count; i++) | ||
767 | { | ||
768 | elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element); | ||
769 | GNUNET_memcpy (elem, | ||
770 | &elements[i], | ||
771 | sizeof(struct GNUNET_SCALARPRODUCT_Element)); | ||
772 | if (GNUNET_SYSERR == | ||
773 | GNUNET_CONTAINER_multihashmap_put (s->intersected_elements, | ||
774 | &elem->key, | ||
775 | elem, | ||
776 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) | ||
777 | { | ||
778 | GNUNET_break (0); | ||
779 | GNUNET_free (elem); | ||
780 | continue; | ||
781 | } | ||
782 | set_elem.data = &elem->key; | ||
783 | set_elem.size = sizeof(elem->key); | ||
784 | set_elem.element_type = 0; | ||
785 | GNUNET_SETI_add_element (s->intersection_set, | ||
786 | &set_elem, | ||
787 | NULL, NULL); | ||
788 | } | ||
789 | s->client_received_element_count += contained_count; | ||
790 | GNUNET_SERVICE_client_continue (s->client); | ||
791 | if (s->total != s->client_received_element_count) | ||
792 | { | ||
793 | /* more to come */ | ||
794 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
795 | "Request still partial, waiting for more client data!\n"); | ||
796 | return; | ||
797 | } | ||
798 | if (NULL == s->channel) | ||
799 | { | ||
800 | /* no Alice waiting for this request, wait for Alice */ | ||
801 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
802 | "Client ready, still waiting for Alice!\n"); | ||
803 | return; | ||
804 | } | ||
805 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
806 | "Both ready, launching intersection!\n"); | ||
807 | start_intersection (s); | ||
808 | } | ||
809 | |||
810 | |||
811 | /** | ||
812 | * Handler for Bob's a client request message. Check @a msg is | ||
813 | * well-formed. | ||
814 | * | ||
815 | * @param cls identification of the client | ||
816 | * @param msg the actual message | ||
817 | * @return #GNUNET_OK if @a msg is well-formed | ||
818 | */ | ||
819 | static int | ||
820 | check_bob_client_message (void *cls, | ||
821 | const struct BobComputationMessage *msg) | ||
822 | { | ||
823 | struct BobServiceSession *s = cls; | ||
824 | uint32_t contained_count; | ||
825 | uint32_t total_count; | ||
826 | uint16_t msize; | ||
827 | |||
828 | if (GNUNET_SCALARPRODUCT_STATUS_INIT != s->status) | ||
829 | { | ||
830 | GNUNET_break (0); | ||
831 | return GNUNET_SYSERR; | ||
832 | } | ||
833 | msize = ntohs (msg->header.size); | ||
834 | total_count = ntohl (msg->element_count_total); | ||
835 | contained_count = ntohl (msg->element_count_contained); | ||
836 | if ((0 == total_count) || | ||
837 | (0 == contained_count) || | ||
838 | (UINT16_MAX < contained_count) || | ||
839 | (msize != (sizeof(struct BobComputationMessage) | ||
840 | + contained_count * sizeof(struct | ||
841 | GNUNET_SCALARPRODUCT_Element)))) | ||
842 | { | ||
843 | GNUNET_break_op (0); | ||
844 | return GNUNET_SYSERR; | ||
845 | } | ||
846 | return GNUNET_OK; | ||
847 | } | ||
848 | |||
849 | |||
850 | /** | ||
851 | * Handler for Bob's a client request message. Bob is in the response | ||
852 | * role, keep the values + session and waiting for a matching session | ||
853 | * or process a waiting request from Alice. | ||
854 | * | ||
855 | * @param cls identification of the client | ||
856 | * @param msg the actual message | ||
857 | */ | ||
858 | static void | ||
859 | handle_bob_client_message (void *cls, | ||
860 | const struct BobComputationMessage *msg) | ||
861 | { | ||
862 | struct BobServiceSession *s = cls; | ||
863 | struct GNUNET_MQ_MessageHandler cadet_handlers[] = { | ||
864 | GNUNET_MQ_hd_fixed_size (alices_computation_request, | ||
865 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_SESSION_INITIALIZATION, | ||
866 | struct EccServiceRequestMessage, | ||
867 | s), | ||
868 | GNUNET_MQ_hd_var_size (alices_cryptodata_message, | ||
869 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_ALICE_CRYPTODATA, | ||
870 | struct EccAliceCryptodataMessage, | ||
871 | s), | ||
872 | GNUNET_MQ_handler_end () | ||
873 | }; | ||
874 | uint32_t contained_count; | ||
875 | uint32_t total_count; | ||
876 | const struct GNUNET_SCALARPRODUCT_Element *elements; | ||
877 | struct GNUNET_SETI_Element set_elem; | ||
878 | struct GNUNET_SCALARPRODUCT_Element *elem; | ||
879 | |||
880 | total_count = ntohl (msg->element_count_total); | ||
881 | contained_count = ntohl (msg->element_count_contained); | ||
882 | |||
883 | s->status = GNUNET_SCALARPRODUCT_STATUS_ACTIVE; | ||
884 | s->total = total_count; | ||
885 | s->client_received_element_count = contained_count; | ||
886 | s->session_id = msg->session_key; | ||
887 | elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1]; | ||
888 | s->intersected_elements | ||
889 | = GNUNET_CONTAINER_multihashmap_create (s->total, | ||
890 | GNUNET_YES); | ||
891 | s->intersection_set = GNUNET_SETI_create (cfg); | ||
892 | for (uint32_t i = 0; i < contained_count; i++) | ||
893 | { | ||
894 | if (0 == GNUNET_ntohll (elements[i].value)) | ||
895 | continue; | ||
896 | elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element); | ||
897 | GNUNET_memcpy (elem, | ||
898 | &elements[i], | ||
899 | sizeof(struct GNUNET_SCALARPRODUCT_Element)); | ||
900 | if (GNUNET_SYSERR == | ||
901 | GNUNET_CONTAINER_multihashmap_put (s->intersected_elements, | ||
902 | &elem->key, | ||
903 | elem, | ||
904 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) | ||
905 | { | ||
906 | GNUNET_break (0); | ||
907 | GNUNET_free (elem); | ||
908 | continue; | ||
909 | } | ||
910 | set_elem.data = &elem->key; | ||
911 | set_elem.size = sizeof(elem->key); | ||
912 | set_elem.element_type = 0; | ||
913 | GNUNET_SETI_add_element (s->intersection_set, | ||
914 | &set_elem, | ||
915 | NULL, NULL); | ||
916 | s->used_element_count++; | ||
917 | } | ||
918 | GNUNET_SERVICE_client_continue (s->client); | ||
919 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
920 | "Received client request, opening port %s!\n", | ||
921 | GNUNET_h2s (&msg->session_key)); | ||
922 | s->port = GNUNET_CADET_open_port (my_cadet, | ||
923 | &msg->session_key, | ||
924 | &cb_channel_incoming, | ||
925 | s, | ||
926 | NULL, | ||
927 | &cb_channel_destruction, | ||
928 | cadet_handlers); | ||
929 | if (NULL == s->port) | ||
930 | { | ||
931 | GNUNET_break (0); | ||
932 | GNUNET_SERVICE_client_drop (s->client); | ||
933 | return; | ||
934 | } | ||
935 | } | ||
936 | |||
937 | |||
938 | /** | ||
939 | * Task run during shutdown. | ||
940 | * | ||
941 | * @param cls unused | ||
942 | */ | ||
943 | static void | ||
944 | shutdown_task (void *cls) | ||
945 | { | ||
946 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
947 | "Shutting down, initiating cleanup.\n"); | ||
948 | // FIXME: we have to cut our connections to CADET first! | ||
949 | if (NULL != my_cadet) | ||
950 | { | ||
951 | GNUNET_CADET_disconnect (my_cadet); | ||
952 | my_cadet = NULL; | ||
953 | } | ||
954 | if (NULL != edc) | ||
955 | { | ||
956 | GNUNET_CRYPTO_ecc_dlog_release (edc); | ||
957 | edc = NULL; | ||
958 | } | ||
959 | } | ||
960 | |||
961 | |||
962 | /** | ||
963 | * A client connected. | ||
964 | * | ||
965 | * Setup the associated data structure. | ||
966 | * | ||
967 | * @param cls closure, NULL | ||
968 | * @param client identification of the client | ||
969 | * @param mq message queue to communicate with @a client | ||
970 | * @return our `struct BobServiceSession` | ||
971 | */ | ||
972 | static void * | ||
973 | client_connect_cb (void *cls, | ||
974 | struct GNUNET_SERVICE_Client *client, | ||
975 | struct GNUNET_MQ_Handle *mq) | ||
976 | { | ||
977 | struct BobServiceSession *s; | ||
978 | |||
979 | s = GNUNET_new (struct BobServiceSession); | ||
980 | s->client = client; | ||
981 | s->client_mq = mq; | ||
982 | return s; | ||
983 | } | ||
984 | |||
985 | |||
986 | /** | ||
987 | * A client disconnected. | ||
988 | * | ||
989 | * Remove the associated session(s), release data structures | ||
990 | * and cancel pending outgoing transmissions to the client. | ||
991 | * | ||
992 | * @param cls closure, NULL | ||
993 | * @param client identification of the client | ||
994 | * @param app_cls our `struct BobServiceSession` | ||
995 | */ | ||
996 | static void | ||
997 | client_disconnect_cb (void *cls, | ||
998 | struct GNUNET_SERVICE_Client *client, | ||
999 | void *app_cls) | ||
1000 | { | ||
1001 | struct BobServiceSession *s = app_cls; | ||
1002 | |||
1003 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1004 | "Client disconnected from us.\n"); | ||
1005 | s->client = NULL; | ||
1006 | destroy_service_session (s); | ||
1007 | } | ||
1008 | |||
1009 | |||
1010 | /** | ||
1011 | * Initialization of the program and message handlers | ||
1012 | * | ||
1013 | * @param cls closure | ||
1014 | * @param c configuration to use | ||
1015 | * @param service the initialized service | ||
1016 | */ | ||
1017 | static void | ||
1018 | run (void *cls, | ||
1019 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
1020 | struct GNUNET_SERVICE_Handle *service) | ||
1021 | { | ||
1022 | cfg = c; | ||
1023 | /* We don't really do DLOG, so we can setup with very minimal resources */ | ||
1024 | edc = GNUNET_CRYPTO_ecc_dlog_prepare (4 /* max value */, | ||
1025 | 2 /* RAM */); | ||
1026 | my_cadet = GNUNET_CADET_connect (cfg); | ||
1027 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
1028 | NULL); | ||
1029 | if (NULL == my_cadet) | ||
1030 | { | ||
1031 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1032 | _ ("Connect to CADET failed\n")); | ||
1033 | GNUNET_SCHEDULER_shutdown (); | ||
1034 | return; | ||
1035 | } | ||
1036 | } | ||
1037 | |||
1038 | |||
1039 | /** | ||
1040 | * Define "main" method using service macro. | ||
1041 | */ | ||
1042 | GNUNET_SERVICE_MAIN | ||
1043 | ("scalarproduct-bob", | ||
1044 | GNUNET_SERVICE_OPTION_NONE, | ||
1045 | &run, | ||
1046 | &client_connect_cb, | ||
1047 | &client_disconnect_cb, | ||
1048 | NULL, | ||
1049 | GNUNET_MQ_hd_var_size (bob_client_message, | ||
1050 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB, | ||
1051 | struct BobComputationMessage, | ||
1052 | NULL), | ||
1053 | GNUNET_MQ_hd_var_size (bob_client_message_multipart, | ||
1054 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_BOB, | ||
1055 | struct ComputationBobCryptodataMultipartMessage, | ||
1056 | NULL), | ||
1057 | GNUNET_MQ_handler_end ()); | ||
1058 | |||
1059 | |||
1060 | /* end of gnunet-service-scalarproduct-ecc_bob.c */ | ||
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct.h b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct.h new file mode 100644 index 000000000..4e79afa2f --- /dev/null +++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct.h | |||
@@ -0,0 +1,142 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2013, 2014 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.h | ||
22 | * @brief scalarproduct service P2P messages | ||
23 | * @author Christian M. Fuchs | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #ifndef GNUNET_SERVICE_SCALARPRODUCT_H | ||
27 | #define GNUNET_SERVICE_SCALARPRODUCT_H | ||
28 | |||
29 | |||
30 | GNUNET_NETWORK_STRUCT_BEGIN | ||
31 | |||
32 | /** | ||
33 | * Message type passed from requesting service Alice to responding | ||
34 | * service Bob to initiate a request and make Bob participate in our | ||
35 | * protocol. Afterwards, Bob is expected to perform the set | ||
36 | * intersection with Alice. Once that has succeeded, Alice will | ||
37 | * send a `struct AliceCryptodataMessage *`. Bob is not expected | ||
38 | * to respond via CADET in the meantime. | ||
39 | */ | ||
40 | struct ServiceRequestMessage | ||
41 | { | ||
42 | /** | ||
43 | * Type is #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SESSION_INITIALIZATION | ||
44 | */ | ||
45 | struct GNUNET_MessageHeader header; | ||
46 | |||
47 | /** | ||
48 | * For alignment. Always zero. | ||
49 | */ | ||
50 | uint32_t reserved; | ||
51 | |||
52 | /** | ||
53 | * The transaction/session key used to identify a session | ||
54 | */ | ||
55 | struct GNUNET_HashCode session_id; | ||
56 | |||
57 | /** | ||
58 | * Alice's public key | ||
59 | */ | ||
60 | struct GNUNET_CRYPTO_PaillierPublicKey public_key; | ||
61 | }; | ||
62 | |||
63 | |||
64 | /** | ||
65 | * Vector of Pallier-encrypted values sent by Alice to Bob | ||
66 | * (after set intersection). Alice may send messages of this | ||
67 | * type repeatedly to transmit all values. | ||
68 | */ | ||
69 | struct AliceCryptodataMessage | ||
70 | { | ||
71 | /** | ||
72 | * Type is #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_CRYPTODATA | ||
73 | */ | ||
74 | struct GNUNET_MessageHeader header; | ||
75 | |||
76 | /** | ||
77 | * How many elements we appended to this message? In NBO. | ||
78 | */ | ||
79 | uint32_t contained_element_count GNUNET_PACKED; | ||
80 | |||
81 | /** | ||
82 | * struct GNUNET_CRYPTO_PaillierCiphertext[contained_element_count] | ||
83 | */ | ||
84 | }; | ||
85 | |||
86 | |||
87 | /** | ||
88 | * Message type passed from responding service Bob to responding | ||
89 | * service Alice to complete a request and allow Alice to compute the | ||
90 | * result. If Bob's reply does not fit into this one message, the | ||
91 | * conversation may be continued with `struct BobCryptodataMultipartMessage` | ||
92 | * messages afterwards. | ||
93 | */ | ||
94 | struct BobCryptodataMessage | ||
95 | { | ||
96 | /** | ||
97 | * GNUNET message header with type | ||
98 | * #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_CRYPTODATA. | ||
99 | */ | ||
100 | struct GNUNET_MessageHeader header; | ||
101 | |||
102 | /** | ||
103 | * How many elements this individual message delivers (in NBO). | ||
104 | */ | ||
105 | uint32_t contained_element_count GNUNET_PACKED; | ||
106 | |||
107 | /** | ||
108 | * followed by s | s' | k[i][perm] | ||
109 | */ | ||
110 | }; | ||
111 | |||
112 | |||
113 | /** | ||
114 | * Multipart Message type passed between to supply additional elements | ||
115 | * for the peer. Send from Bob to Alice with additional elements | ||
116 | * of k[i][perm] after his `struct BobCryptodataMessage *`. | ||
117 | * Once all k-values have been transmitted, Bob is finished and | ||
118 | * Alice can transmit the final result to the client. | ||
119 | */ | ||
120 | struct BobCryptodataMultipartMessage | ||
121 | { | ||
122 | /** | ||
123 | * GNUNET message header | ||
124 | */ | ||
125 | struct GNUNET_MessageHeader header; | ||
126 | |||
127 | /** | ||
128 | * How many elements we supply within this message? In NBO. | ||
129 | */ | ||
130 | uint32_t contained_element_count GNUNET_PACKED; | ||
131 | |||
132 | /** | ||
133 | * Followed by `struct | ||
134 | * GNUNET_CRYPTO_PaillierCiphertext[contained_element_count]` | ||
135 | */ | ||
136 | }; | ||
137 | |||
138 | |||
139 | GNUNET_NETWORK_STRUCT_END | ||
140 | |||
141 | |||
142 | #endif | ||
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_alice.c b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_alice.c new file mode 100644 index 000000000..0149f45ba --- /dev/null +++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_alice.c | |||
@@ -0,0 +1,1388 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2013, 2014, 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_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_seti_service.h" | ||
36 | #include "scalarproduct.h" | ||
37 | #include "gnunet-service-scalarproduct.h" | ||
38 | #include "gnunet_constants.h" | ||
39 | |||
40 | #define LOG(kind, ...) \ | ||
41 | GNUNET_log_from (kind, "scalarproduct-alice", __VA_ARGS__) | ||
42 | |||
43 | /** | ||
44 | * An encrypted element key-value pair. | ||
45 | */ | ||
46 | struct MpiElement | ||
47 | { | ||
48 | /** | ||
49 | * Key used to identify matching pairs of values to multiply. | ||
50 | * Points into an existing data structure, to avoid copying | ||
51 | * and doubling memory use. | ||
52 | */ | ||
53 | const struct GNUNET_HashCode *key; | ||
54 | |||
55 | /** | ||
56 | * Value represented (a). | ||
57 | */ | ||
58 | gcry_mpi_t value; | ||
59 | }; | ||
60 | |||
61 | |||
62 | /** | ||
63 | * A scalarproduct session which tracks | ||
64 | * a request form the client to our final response. | ||
65 | */ | ||
66 | struct AliceServiceSession | ||
67 | { | ||
68 | /** | ||
69 | * (hopefully) unique transaction ID | ||
70 | */ | ||
71 | struct GNUNET_HashCode session_id; | ||
72 | |||
73 | /** | ||
74 | * Alice or Bob's peerID | ||
75 | */ | ||
76 | struct GNUNET_PeerIdentity peer; | ||
77 | |||
78 | /** | ||
79 | * The client this request is related to. | ||
80 | */ | ||
81 | struct GNUNET_SERVICE_Client *client; | ||
82 | |||
83 | /** | ||
84 | * The message queue for the client. | ||
85 | */ | ||
86 | struct GNUNET_MQ_Handle *client_mq; | ||
87 | |||
88 | /** | ||
89 | * The message queue for CADET. | ||
90 | */ | ||
91 | struct GNUNET_MQ_Handle *cadet_mq; | ||
92 | |||
93 | /** | ||
94 | * all non-0-value'd elements transmitted to us. | ||
95 | * Values are of type `struct GNUNET_SCALARPRODUCT_Element *` | ||
96 | */ | ||
97 | struct GNUNET_CONTAINER_MultiHashMap *intersected_elements; | ||
98 | |||
99 | /** | ||
100 | * Set of elements for which will conduction an intersection. | ||
101 | * the resulting elements are then used for computing the scalar product. | ||
102 | */ | ||
103 | struct GNUNET_SETI_Handle *intersection_set; | ||
104 | |||
105 | /** | ||
106 | * Set of elements for which will conduction an intersection. | ||
107 | * the resulting elements are then used for computing the scalar product. | ||
108 | */ | ||
109 | struct GNUNET_SETI_OperationHandle *intersection_op; | ||
110 | |||
111 | /** | ||
112 | * Handle to Alice's Intersection operation listening for Bob | ||
113 | */ | ||
114 | struct GNUNET_SETI_ListenHandle *intersection_listen; | ||
115 | |||
116 | /** | ||
117 | * channel-handle associated with our cadet handle | ||
118 | */ | ||
119 | struct GNUNET_CADET_Channel *channel; | ||
120 | |||
121 | /** | ||
122 | * a(Alice), sorted array by key of length @e used_element_count. | ||
123 | */ | ||
124 | struct MpiElement *sorted_elements; | ||
125 | |||
126 | /** | ||
127 | * Bob's permutation p of R | ||
128 | */ | ||
129 | struct GNUNET_CRYPTO_PaillierCiphertext *r; | ||
130 | |||
131 | /** | ||
132 | * Bob's permutation q of R | ||
133 | */ | ||
134 | struct GNUNET_CRYPTO_PaillierCiphertext *r_prime; | ||
135 | |||
136 | /** | ||
137 | * Bob's "s" | ||
138 | */ | ||
139 | struct GNUNET_CRYPTO_PaillierCiphertext s; | ||
140 | |||
141 | /** | ||
142 | * Bob's "s'" | ||
143 | */ | ||
144 | struct GNUNET_CRYPTO_PaillierCiphertext s_prime; | ||
145 | |||
146 | /** | ||
147 | * The computed scalar | ||
148 | */ | ||
149 | gcry_mpi_t product; | ||
150 | |||
151 | /** | ||
152 | * How many elements we were supplied with from the client (total | ||
153 | * count before intersection). | ||
154 | */ | ||
155 | uint32_t total; | ||
156 | |||
157 | /** | ||
158 | * How many elements actually are used for the scalar product. | ||
159 | * Size of the arrays in @e r and @e r_prime. Sometimes also | ||
160 | * reset to 0 and used as a counter! | ||
161 | */ | ||
162 | uint32_t used_element_count; | ||
163 | |||
164 | /** | ||
165 | * Already transferred elements from client to us. | ||
166 | * Less or equal than @e total. | ||
167 | */ | ||
168 | uint32_t client_received_element_count; | ||
169 | |||
170 | /** | ||
171 | * Already transferred elements from Bob to us. | ||
172 | * Less or equal than @e total. | ||
173 | */ | ||
174 | uint32_t cadet_received_element_count; | ||
175 | |||
176 | /** | ||
177 | * State of this session. In | ||
178 | * #GNUNET_SCALARPRODUCT_STATUS_ACTIVE while operation is | ||
179 | * ongoing, afterwards in #GNUNET_SCALARPRODUCT_STATUS_SUCCESS or | ||
180 | * #GNUNET_SCALARPRODUCT_STATUS_FAILURE. | ||
181 | */ | ||
182 | enum GNUNET_SCALARPRODUCT_ResponseStatus status; | ||
183 | |||
184 | /** | ||
185 | * Flag to prevent recursive calls to #destroy_service_session() from | ||
186 | * doing harm. | ||
187 | */ | ||
188 | int in_destroy; | ||
189 | }; | ||
190 | |||
191 | |||
192 | /** | ||
193 | * GNUnet configuration handle | ||
194 | */ | ||
195 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
196 | |||
197 | /** | ||
198 | * Service's own public key | ||
199 | */ | ||
200 | static struct GNUNET_CRYPTO_PaillierPublicKey my_pubkey; | ||
201 | |||
202 | /** | ||
203 | * Service's own private key | ||
204 | */ | ||
205 | static struct GNUNET_CRYPTO_PaillierPrivateKey my_privkey; | ||
206 | |||
207 | /** | ||
208 | * Service's offset for values that could possibly be negative but are plaintext for encryption. | ||
209 | */ | ||
210 | static gcry_mpi_t my_offset; | ||
211 | |||
212 | /** | ||
213 | * Handle to the CADET service. | ||
214 | */ | ||
215 | static struct GNUNET_CADET_Handle *my_cadet; | ||
216 | |||
217 | |||
218 | /** | ||
219 | * Iterator called to free elements. | ||
220 | * | ||
221 | * @param cls the `struct AliceServiceSession *` (unused) | ||
222 | * @param key the key (unused) | ||
223 | * @param value value to free | ||
224 | * @return #GNUNET_OK (continue to iterate) | ||
225 | */ | ||
226 | static int | ||
227 | free_element_cb (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
228 | { | ||
229 | struct GNUNET_SCALARPRODUCT_Element *e = value; | ||
230 | |||
231 | GNUNET_free (e); | ||
232 | return GNUNET_OK; | ||
233 | } | ||
234 | |||
235 | |||
236 | /** | ||
237 | * Destroy session state, we are done with it. | ||
238 | * | ||
239 | * @param s the session to free elements from | ||
240 | */ | ||
241 | static void | ||
242 | destroy_service_session (struct AliceServiceSession *s) | ||
243 | { | ||
244 | if (GNUNET_YES == s->in_destroy) | ||
245 | return; | ||
246 | s->in_destroy = GNUNET_YES; | ||
247 | if (NULL != s->client) | ||
248 | { | ||
249 | struct GNUNET_SERVICE_Client *c = s->client; | ||
250 | |||
251 | s->client = NULL; | ||
252 | GNUNET_SERVICE_client_drop (c); | ||
253 | } | ||
254 | if (NULL != s->channel) | ||
255 | { | ||
256 | GNUNET_CADET_channel_destroy (s->channel); | ||
257 | s->channel = NULL; | ||
258 | } | ||
259 | if (NULL != s->intersected_elements) | ||
260 | { | ||
261 | GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements, | ||
262 | &free_element_cb, | ||
263 | s); | ||
264 | GNUNET_CONTAINER_multihashmap_destroy (s->intersected_elements); | ||
265 | s->intersected_elements = NULL; | ||
266 | } | ||
267 | if (NULL != s->intersection_listen) | ||
268 | { | ||
269 | GNUNET_SETI_listen_cancel (s->intersection_listen); | ||
270 | s->intersection_listen = NULL; | ||
271 | } | ||
272 | if (NULL != s->intersection_op) | ||
273 | { | ||
274 | GNUNET_SETI_operation_cancel (s->intersection_op); | ||
275 | s->intersection_op = NULL; | ||
276 | } | ||
277 | if (NULL != s->intersection_set) | ||
278 | { | ||
279 | GNUNET_SETI_destroy (s->intersection_set); | ||
280 | s->intersection_set = NULL; | ||
281 | } | ||
282 | if (NULL != s->sorted_elements) | ||
283 | { | ||
284 | for (unsigned int i = 0; i < s->used_element_count; i++) | ||
285 | gcry_mpi_release (s->sorted_elements[i].value); | ||
286 | GNUNET_free (s->sorted_elements); | ||
287 | s->sorted_elements = NULL; | ||
288 | } | ||
289 | if (NULL != s->r) | ||
290 | { | ||
291 | GNUNET_free (s->r); | ||
292 | s->r = NULL; | ||
293 | } | ||
294 | if (NULL != s->r_prime) | ||
295 | { | ||
296 | GNUNET_free (s->r_prime); | ||
297 | s->r_prime = NULL; | ||
298 | } | ||
299 | if (NULL != s->product) | ||
300 | { | ||
301 | gcry_mpi_release (s->product); | ||
302 | s->product = NULL; | ||
303 | } | ||
304 | GNUNET_free (s); | ||
305 | } | ||
306 | |||
307 | |||
308 | /** | ||
309 | * Notify the client that the session has failed. A message gets sent | ||
310 | * to Alice's client if we encountered any error. | ||
311 | * | ||
312 | * @param session the associated client session to fail or succeed | ||
313 | */ | ||
314 | static void | ||
315 | prepare_client_end_notification (struct AliceServiceSession *session) | ||
316 | { | ||
317 | struct ClientResponseMessage *msg; | ||
318 | struct GNUNET_MQ_Envelope *e; | ||
319 | |||
320 | if (NULL == session->client_mq) | ||
321 | return; /* no client left to be notified */ | ||
322 | GNUNET_log ( | ||
323 | GNUNET_ERROR_TYPE_DEBUG, | ||
324 | "Sending session-end notification with status %d to client for session %s\n", | ||
325 | session->status, | ||
326 | GNUNET_h2s (&session->session_id)); | ||
327 | e = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT); | ||
328 | msg->product_length = htonl (0); | ||
329 | msg->status = htonl (session->status); | ||
330 | GNUNET_MQ_send (session->client_mq, e); | ||
331 | } | ||
332 | |||
333 | |||
334 | /** | ||
335 | * Prepare the final (positive) response we will send to Alice's | ||
336 | * client. | ||
337 | * | ||
338 | * @param s the session associated with our client. | ||
339 | */ | ||
340 | static void | ||
341 | transmit_client_response (struct AliceServiceSession *s) | ||
342 | { | ||
343 | struct ClientResponseMessage *msg; | ||
344 | struct GNUNET_MQ_Envelope *e; | ||
345 | unsigned char *product_exported = NULL; | ||
346 | size_t product_length = 0; | ||
347 | int32_t range; | ||
348 | gcry_error_t rc; | ||
349 | int sign; | ||
350 | gcry_mpi_t value; | ||
351 | |||
352 | if (NULL == s->product) | ||
353 | { | ||
354 | GNUNET_break (0); | ||
355 | prepare_client_end_notification (s); | ||
356 | return; | ||
357 | } | ||
358 | value = gcry_mpi_new (0); | ||
359 | sign = gcry_mpi_cmp_ui (s->product, 0); | ||
360 | if (0 > sign) | ||
361 | { | ||
362 | range = -1; | ||
363 | gcry_mpi_sub (value, value, s->product); | ||
364 | } | ||
365 | else if (0 < sign) | ||
366 | { | ||
367 | range = 1; | ||
368 | gcry_mpi_add (value, value, s->product); | ||
369 | } | ||
370 | else | ||
371 | { | ||
372 | /* result is exactly zero */ | ||
373 | range = 0; | ||
374 | } | ||
375 | gcry_mpi_release (s->product); | ||
376 | s->product = NULL; | ||
377 | |||
378 | if ((0 != range) && (0 != (rc = gcry_mpi_aprint (GCRYMPI_FMT_STD, | ||
379 | &product_exported, | ||
380 | &product_length, | ||
381 | value)))) | ||
382 | { | ||
383 | LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); | ||
384 | prepare_client_end_notification (s); | ||
385 | return; | ||
386 | } | ||
387 | gcry_mpi_release (value); | ||
388 | e = GNUNET_MQ_msg_extra (msg, | ||
389 | product_length, | ||
390 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT); | ||
391 | msg->status = htonl (GNUNET_SCALARPRODUCT_STATUS_SUCCESS); | ||
392 | msg->range = htonl (range); | ||
393 | msg->product_length = htonl (product_length); | ||
394 | if (NULL != product_exported) | ||
395 | { | ||
396 | GNUNET_memcpy (&msg[1], product_exported, product_length); | ||
397 | GNUNET_free (product_exported); | ||
398 | } | ||
399 | GNUNET_MQ_send (s->client_mq, e); | ||
400 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
401 | "Sent result to client, session %s has ended!\n", | ||
402 | GNUNET_h2s (&s->session_id)); | ||
403 | } | ||
404 | |||
405 | |||
406 | /** | ||
407 | * Function called whenever a channel is destroyed. Should clean up | ||
408 | * any associated state. | ||
409 | * | ||
410 | * It must NOT call #GNUNET_CADET_channel_destroy() on the channel. | ||
411 | * | ||
412 | * @param cls our `struct AliceServiceSession` | ||
413 | * @param channel connection to the other end (henceforth invalid) | ||
414 | */ | ||
415 | static void | ||
416 | cb_channel_destruction (void *cls, const struct GNUNET_CADET_Channel *channel) | ||
417 | { | ||
418 | struct AliceServiceSession *s = cls; | ||
419 | |||
420 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
421 | "Peer disconnected, terminating session %s with peer %s\n", | ||
422 | GNUNET_h2s (&s->session_id), | ||
423 | GNUNET_i2s (&s->peer)); | ||
424 | if (GNUNET_SCALARPRODUCT_STATUS_ACTIVE == s->status) | ||
425 | { | ||
426 | /* We didn't get an answer yet, fail with error */ | ||
427 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
428 | prepare_client_end_notification (s); | ||
429 | } | ||
430 | s->channel = NULL; | ||
431 | } | ||
432 | |||
433 | |||
434 | /** | ||
435 | * Computes the square sum over a vector of a given length. | ||
436 | * | ||
437 | * @param vector the vector to compute over | ||
438 | * @param length the length of the vector | ||
439 | * @return an MPI value containing the calculated sum, never NULL | ||
440 | */ | ||
441 | static gcry_mpi_t | ||
442 | compute_square_sum_mpi_elements (const struct MpiElement *vector, | ||
443 | uint32_t length) | ||
444 | { | ||
445 | gcry_mpi_t elem; | ||
446 | gcry_mpi_t sum; | ||
447 | uint32_t i; | ||
448 | |||
449 | GNUNET_assert (NULL != (sum = gcry_mpi_new (0))); | ||
450 | GNUNET_assert (NULL != (elem = gcry_mpi_new (0))); | ||
451 | for (i = 0; i < length; i++) | ||
452 | { | ||
453 | gcry_mpi_mul (elem, vector[i].value, vector[i].value); | ||
454 | gcry_mpi_add (sum, sum, elem); | ||
455 | } | ||
456 | gcry_mpi_release (elem); | ||
457 | return sum; | ||
458 | } | ||
459 | |||
460 | |||
461 | /** | ||
462 | * Computes the square sum over a vector of a given length. | ||
463 | * | ||
464 | * @param vector the vector to compute over | ||
465 | * @param length the length of the vector | ||
466 | * @return an MPI value containing the calculated sum, never NULL | ||
467 | */ | ||
468 | static gcry_mpi_t | ||
469 | compute_square_sum (const gcry_mpi_t *vector, uint32_t length) | ||
470 | { | ||
471 | gcry_mpi_t elem; | ||
472 | gcry_mpi_t sum; | ||
473 | uint32_t i; | ||
474 | |||
475 | GNUNET_assert (NULL != (sum = gcry_mpi_new (0))); | ||
476 | GNUNET_assert (NULL != (elem = gcry_mpi_new (0))); | ||
477 | for (i = 0; i < length; i++) | ||
478 | { | ||
479 | gcry_mpi_mul (elem, vector[i], vector[i]); | ||
480 | gcry_mpi_add (sum, sum, elem); | ||
481 | } | ||
482 | gcry_mpi_release (elem); | ||
483 | return sum; | ||
484 | } | ||
485 | |||
486 | |||
487 | /** | ||
488 | * Compute our scalar product, done by Alice | ||
489 | * | ||
490 | * @param session the session associated with this computation | ||
491 | * @return product as MPI, never NULL | ||
492 | */ | ||
493 | static gcry_mpi_t | ||
494 | compute_scalar_product (struct AliceServiceSession *session) | ||
495 | { | ||
496 | uint32_t count; | ||
497 | gcry_mpi_t t; | ||
498 | gcry_mpi_t u; | ||
499 | gcry_mpi_t u_prime; | ||
500 | gcry_mpi_t p; | ||
501 | gcry_mpi_t p_prime; | ||
502 | gcry_mpi_t tmp; | ||
503 | gcry_mpi_t r[session->used_element_count]; | ||
504 | gcry_mpi_t r_prime[session->used_element_count]; | ||
505 | gcry_mpi_t s; | ||
506 | gcry_mpi_t s_prime; | ||
507 | unsigned int i; | ||
508 | |||
509 | count = session->used_element_count; | ||
510 | // due to the introduced static offset S, we now also have to remove this | ||
511 | // from the E(a_pi)(+)E(-b_pi-r_pi) and E(a_qi)(+)E(-r_qi) twice each, | ||
512 | // the result is E((S + a_pi) + (S -b_pi-r_pi)) and E(S + a_qi + S - r_qi) | ||
513 | for (i = 0; i < count; i++) | ||
514 | { | ||
515 | r[i] = gcry_mpi_new (0); | ||
516 | GNUNET_CRYPTO_paillier_decrypt (&my_privkey, | ||
517 | &my_pubkey, | ||
518 | &session->r[i], | ||
519 | r[i]); | ||
520 | gcry_mpi_sub (r[i], r[i], my_offset); | ||
521 | gcry_mpi_sub (r[i], r[i], my_offset); | ||
522 | r_prime[i] = gcry_mpi_new (0); | ||
523 | GNUNET_CRYPTO_paillier_decrypt (&my_privkey, | ||
524 | &my_pubkey, | ||
525 | &session->r_prime[i], | ||
526 | r_prime[i]); | ||
527 | gcry_mpi_sub (r_prime[i], r_prime[i], my_offset); | ||
528 | gcry_mpi_sub (r_prime[i], r_prime[i], my_offset); | ||
529 | } | ||
530 | |||
531 | // calculate t = sum(ai) | ||
532 | t = compute_square_sum_mpi_elements (session->sorted_elements, count); | ||
533 | // calculate U | ||
534 | u = gcry_mpi_new (0); | ||
535 | tmp = compute_square_sum (r, count); | ||
536 | gcry_mpi_sub (u, u, tmp); | ||
537 | gcry_mpi_release (tmp); | ||
538 | |||
539 | // calculate U' | ||
540 | u_prime = gcry_mpi_new (0); | ||
541 | tmp = compute_square_sum (r_prime, count); | ||
542 | gcry_mpi_sub (u_prime, u_prime, tmp); | ||
543 | |||
544 | GNUNET_assert (p = gcry_mpi_new (0)); | ||
545 | GNUNET_assert (p_prime = gcry_mpi_new (0)); | ||
546 | GNUNET_assert (s = gcry_mpi_new (0)); | ||
547 | GNUNET_assert (s_prime = gcry_mpi_new (0)); | ||
548 | |||
549 | // compute P | ||
550 | GNUNET_CRYPTO_paillier_decrypt (&my_privkey, &my_pubkey, &session->s, s); | ||
551 | GNUNET_CRYPTO_paillier_decrypt (&my_privkey, | ||
552 | &my_pubkey, | ||
553 | &session->s_prime, | ||
554 | s_prime); | ||
555 | |||
556 | // compute P | ||
557 | gcry_mpi_add (p, s, t); | ||
558 | gcry_mpi_add (p, p, u); | ||
559 | |||
560 | // compute P' | ||
561 | gcry_mpi_add (p_prime, s_prime, t); | ||
562 | gcry_mpi_add (p_prime, p_prime, u_prime); | ||
563 | |||
564 | gcry_mpi_release (t); | ||
565 | gcry_mpi_release (u); | ||
566 | gcry_mpi_release (u_prime); | ||
567 | gcry_mpi_release (s); | ||
568 | gcry_mpi_release (s_prime); | ||
569 | |||
570 | // compute product | ||
571 | gcry_mpi_sub (p, p, p_prime); | ||
572 | gcry_mpi_release (p_prime); | ||
573 | tmp = gcry_mpi_set_ui (tmp, 2); | ||
574 | gcry_mpi_div (p, NULL, p, tmp, 0); | ||
575 | |||
576 | gcry_mpi_release (tmp); | ||
577 | for (i = 0; i < count; i++) | ||
578 | { | ||
579 | gcry_mpi_release (session->sorted_elements[i].value); | ||
580 | gcry_mpi_release (r[i]); | ||
581 | gcry_mpi_release (r_prime[i]); | ||
582 | } | ||
583 | GNUNET_free (session->sorted_elements); | ||
584 | session->sorted_elements = NULL; | ||
585 | GNUNET_free (session->r); | ||
586 | session->r = NULL; | ||
587 | GNUNET_free (session->r_prime); | ||
588 | session->r_prime = NULL; | ||
589 | |||
590 | return p; | ||
591 | } | ||
592 | |||
593 | |||
594 | /** | ||
595 | * Check a multipart chunk of a response we got from another service | ||
596 | * we wanted to calculate a scalarproduct with. | ||
597 | * | ||
598 | * @param cls the `struct AliceServiceSession` | ||
599 | * @param msg the actual message | ||
600 | * @return #GNUNET_OK to keep the connection open, | ||
601 | * #GNUNET_SYSERR to close it (signal serious error) | ||
602 | */ | ||
603 | static int | ||
604 | check_bobs_cryptodata_multipart ( | ||
605 | void *cls, | ||
606 | const struct BobCryptodataMultipartMessage *msg) | ||
607 | { | ||
608 | struct AliceServiceSession *s = cls; | ||
609 | uint32_t contained; | ||
610 | size_t msg_size; | ||
611 | size_t required_size; | ||
612 | |||
613 | msg_size = ntohs (msg->header.size); | ||
614 | contained = ntohl (msg->contained_element_count); | ||
615 | required_size = | ||
616 | sizeof(struct BobCryptodataMultipartMessage) | ||
617 | + 2 * contained * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext); | ||
618 | if ((required_size != msg_size) || | ||
619 | (s->cadet_received_element_count + contained > s->used_element_count)) | ||
620 | { | ||
621 | GNUNET_break (0); | ||
622 | return GNUNET_SYSERR; | ||
623 | } | ||
624 | return GNUNET_OK; | ||
625 | } | ||
626 | |||
627 | |||
628 | /** | ||
629 | * Handle a multipart chunk of a response we got from another service | ||
630 | * we wanted to calculate a scalarproduct with. | ||
631 | * | ||
632 | * @param cls the `struct AliceServiceSession` | ||
633 | * @param msg the actual message | ||
634 | */ | ||
635 | static void | ||
636 | handle_bobs_cryptodata_multipart ( | ||
637 | void *cls, | ||
638 | const struct BobCryptodataMultipartMessage *msg) | ||
639 | { | ||
640 | struct AliceServiceSession *s = cls; | ||
641 | const struct GNUNET_CRYPTO_PaillierCiphertext *payload; | ||
642 | size_t i; | ||
643 | uint32_t contained; | ||
644 | |||
645 | contained = ntohl (msg->contained_element_count); | ||
646 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
647 | "Received %u additional crypto values from Bob\n", | ||
648 | (unsigned int) contained); | ||
649 | |||
650 | payload = (const struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1]; | ||
651 | /* Convert each k[][perm] to its MPI_value */ | ||
652 | for (i = 0; i < contained; i++) | ||
653 | { | ||
654 | GNUNET_memcpy (&s->r[s->cadet_received_element_count + i], | ||
655 | &payload[2 * i], | ||
656 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
657 | GNUNET_memcpy (&s->r_prime[s->cadet_received_element_count + i], | ||
658 | &payload[2 * i], | ||
659 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
660 | } | ||
661 | s->cadet_received_element_count += contained; | ||
662 | GNUNET_CADET_receive_done (s->channel); | ||
663 | if (s->cadet_received_element_count != s->used_element_count) | ||
664 | return; /* more to come */ | ||
665 | |||
666 | s->product = compute_scalar_product (s); | ||
667 | transmit_client_response (s); | ||
668 | } | ||
669 | |||
670 | |||
671 | /** | ||
672 | * Check a response we got from another service we wanted to | ||
673 | * calculate a scalarproduct with. | ||
674 | * | ||
675 | * @param cls our `struct AliceServiceSession` | ||
676 | * @param msg the actual message | ||
677 | * @return #GNUNET_OK to keep the connection open, | ||
678 | * #GNUNET_SYSERR to close it (we are done) | ||
679 | */ | ||
680 | static int | ||
681 | check_bobs_cryptodata_message (void *cls, | ||
682 | const struct BobCryptodataMessage *msg) | ||
683 | { | ||
684 | struct AliceServiceSession *s = cls; | ||
685 | uint32_t contained; | ||
686 | uint16_t msg_size; | ||
687 | size_t required_size; | ||
688 | |||
689 | msg_size = ntohs (msg->header.size); | ||
690 | contained = ntohl (msg->contained_element_count); | ||
691 | required_size = | ||
692 | sizeof(struct BobCryptodataMessage) | ||
693 | + 2 * contained * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext) | ||
694 | + 2 * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext); | ||
695 | if ((msg_size != required_size) || (contained > UINT16_MAX) || | ||
696 | (s->used_element_count < contained)) | ||
697 | { | ||
698 | GNUNET_break_op (0); | ||
699 | return GNUNET_SYSERR; | ||
700 | } | ||
701 | if (NULL == s->sorted_elements) | ||
702 | { | ||
703 | /* we're not ready yet, how can Bob be? */ | ||
704 | GNUNET_break_op (0); | ||
705 | return GNUNET_SYSERR; | ||
706 | } | ||
707 | if (s->total != s->client_received_element_count) | ||
708 | { | ||
709 | /* we're not ready yet, how can Bob be? */ | ||
710 | GNUNET_break_op (0); | ||
711 | return GNUNET_SYSERR; | ||
712 | } | ||
713 | return GNUNET_OK; | ||
714 | } | ||
715 | |||
716 | |||
717 | /** | ||
718 | * Handle a response we got from another service we wanted to | ||
719 | * calculate a scalarproduct with. | ||
720 | * | ||
721 | * @param cls our `struct AliceServiceSession` | ||
722 | * @param msg the actual message | ||
723 | */ | ||
724 | static void | ||
725 | handle_bobs_cryptodata_message (void *cls, | ||
726 | const struct BobCryptodataMessage *msg) | ||
727 | { | ||
728 | struct AliceServiceSession *s = cls; | ||
729 | const struct GNUNET_CRYPTO_PaillierCiphertext *payload; | ||
730 | uint32_t i; | ||
731 | uint32_t contained; | ||
732 | |||
733 | contained = ntohl (msg->contained_element_count); | ||
734 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
735 | "Received %u crypto values from Bob\n", | ||
736 | (unsigned int) contained); | ||
737 | payload = (const struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1]; | ||
738 | GNUNET_memcpy (&s->s, | ||
739 | &payload[0], | ||
740 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
741 | GNUNET_memcpy (&s->s_prime, | ||
742 | &payload[1], | ||
743 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
744 | payload = &payload[2]; | ||
745 | |||
746 | s->r = GNUNET_new_array (s->used_element_count, | ||
747 | struct GNUNET_CRYPTO_PaillierCiphertext); | ||
748 | s->r_prime = GNUNET_new_array (s->used_element_count, | ||
749 | struct GNUNET_CRYPTO_PaillierCiphertext); | ||
750 | for (i = 0; i < contained; i++) | ||
751 | { | ||
752 | GNUNET_memcpy (&s->r[i], | ||
753 | &payload[2 * i], | ||
754 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
755 | GNUNET_memcpy (&s->r_prime[i], | ||
756 | &payload[2 * i + 1], | ||
757 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
758 | } | ||
759 | s->cadet_received_element_count = contained; | ||
760 | GNUNET_CADET_receive_done (s->channel); | ||
761 | |||
762 | if (s->cadet_received_element_count != s->used_element_count) | ||
763 | { | ||
764 | /* More to come */ | ||
765 | return; | ||
766 | } | ||
767 | s->product = compute_scalar_product (s); | ||
768 | transmit_client_response (s); | ||
769 | } | ||
770 | |||
771 | |||
772 | /** | ||
773 | * Iterator to copy over messages from the hash map | ||
774 | * into an array for sorting. | ||
775 | * | ||
776 | * @param cls the `struct AliceServiceSession *` | ||
777 | * @param key the key (unused) | ||
778 | * @param value the `struct GNUNET_SCALARPRODUCT_Element *` | ||
779 | */ | ||
780 | static int | ||
781 | copy_element_cb (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
782 | { | ||
783 | struct AliceServiceSession *s = cls; | ||
784 | struct GNUNET_SCALARPRODUCT_Element *e = value; | ||
785 | gcry_mpi_t mval; | ||
786 | int64_t val; | ||
787 | |||
788 | mval = gcry_mpi_new (0); | ||
789 | val = (int64_t) GNUNET_ntohll (e->value); | ||
790 | if (0 > val) | ||
791 | gcry_mpi_sub_ui (mval, mval, -val); | ||
792 | else | ||
793 | gcry_mpi_add_ui (mval, mval, val); | ||
794 | s->sorted_elements[s->used_element_count].value = mval; | ||
795 | s->sorted_elements[s->used_element_count].key = &e->key; | ||
796 | s->used_element_count++; | ||
797 | return GNUNET_OK; | ||
798 | } | ||
799 | |||
800 | |||
801 | /** | ||
802 | * Compare two `struct MpiValue`s by key for sorting. | ||
803 | * | ||
804 | * @param a pointer to first `struct MpiValue *` | ||
805 | * @param b pointer to first `struct MpiValue *` | ||
806 | * @return -1 for a < b, 0 for a=b, 1 for a > b. | ||
807 | */ | ||
808 | static int | ||
809 | element_cmp (const void *a, const void *b) | ||
810 | { | ||
811 | const struct MpiElement *ma = a; | ||
812 | const struct MpiElement *mb = b; | ||
813 | |||
814 | return GNUNET_CRYPTO_hash_cmp (ma->key, mb->key); | ||
815 | } | ||
816 | |||
817 | |||
818 | /** | ||
819 | * Maximum number of elements we can put into a single cryptodata | ||
820 | * message | ||
821 | */ | ||
822 | #define ELEMENT_CAPACITY \ | ||
823 | ((GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE - 1 \ | ||
824 | - sizeof(struct AliceCryptodataMessage)) \ | ||
825 | / sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)) | ||
826 | |||
827 | |||
828 | /** | ||
829 | * Send the cryptographic data from Alice to Bob. | ||
830 | * Does nothing if we already transferred all elements. | ||
831 | * | ||
832 | * @param s the associated service session | ||
833 | */ | ||
834 | static void | ||
835 | send_alices_cryptodata_message (struct AliceServiceSession *s) | ||
836 | { | ||
837 | struct AliceCryptodataMessage *msg; | ||
838 | struct GNUNET_MQ_Envelope *e; | ||
839 | struct GNUNET_CRYPTO_PaillierCiphertext *payload; | ||
840 | unsigned int i; | ||
841 | uint32_t todo_count; | ||
842 | gcry_mpi_t a; | ||
843 | uint32_t off; | ||
844 | |||
845 | s->sorted_elements = GNUNET_malloc ( | ||
846 | GNUNET_CONTAINER_multihashmap_size (s->intersected_elements) | ||
847 | * sizeof(struct MpiElement)); | ||
848 | s->used_element_count = 0; | ||
849 | GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements, | ||
850 | ©_element_cb, | ||
851 | s); | ||
852 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
853 | "Finished intersection, %d items remain\n", | ||
854 | s->used_element_count); | ||
855 | qsort (s->sorted_elements, | ||
856 | s->used_element_count, | ||
857 | sizeof(struct MpiElement), | ||
858 | &element_cmp); | ||
859 | off = 0; | ||
860 | while (off < s->used_element_count) | ||
861 | { | ||
862 | todo_count = s->used_element_count - off; | ||
863 | if (todo_count > ELEMENT_CAPACITY) | ||
864 | todo_count = ELEMENT_CAPACITY; | ||
865 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
866 | "Sending %u/%u crypto values to Bob\n", | ||
867 | (unsigned int) todo_count, | ||
868 | (unsigned int) s->used_element_count); | ||
869 | |||
870 | e = | ||
871 | GNUNET_MQ_msg_extra (msg, | ||
872 | todo_count | ||
873 | * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext), | ||
874 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_CRYPTODATA); | ||
875 | msg->contained_element_count = htonl (todo_count); | ||
876 | payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1]; | ||
877 | a = gcry_mpi_new (0); | ||
878 | for (i = off; i < off + todo_count; i++) | ||
879 | { | ||
880 | gcry_mpi_add (a, s->sorted_elements[i].value, my_offset); | ||
881 | GNUNET_assert ( | ||
882 | 3 == | ||
883 | GNUNET_CRYPTO_paillier_encrypt (&my_pubkey, a, 3, &payload[i - off])); | ||
884 | } | ||
885 | gcry_mpi_release (a); | ||
886 | off += todo_count; | ||
887 | GNUNET_MQ_send (s->cadet_mq, e); | ||
888 | } | ||
889 | } | ||
890 | |||
891 | |||
892 | /** | ||
893 | * Callback for set operation results. Called for each element | ||
894 | * that should be removed from the result set, and then once | ||
895 | * to indicate that the set intersection operation is done. | ||
896 | * | ||
897 | * @param cls closure with the `struct AliceServiceSession` | ||
898 | * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK | ||
899 | * @param current_size current set size | ||
900 | * @param status what has happened with the set intersection? | ||
901 | */ | ||
902 | static void | ||
903 | cb_intersection_element_removed (void *cls, | ||
904 | const struct GNUNET_SETI_Element *element, | ||
905 | uint64_t current_size, | ||
906 | enum GNUNET_SETI_Status status) | ||
907 | { | ||
908 | struct AliceServiceSession *s = cls; | ||
909 | struct GNUNET_SCALARPRODUCT_Element *se; | ||
910 | |||
911 | switch (status) | ||
912 | { | ||
913 | case GNUNET_SETI_STATUS_DEL_LOCAL: | ||
914 | /* this element has been removed from the set */ | ||
915 | se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements, | ||
916 | element->data); | ||
917 | GNUNET_assert (NULL != se); | ||
918 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
919 | "Intersection removed element with key %s and value %lld\n", | ||
920 | GNUNET_h2s (&se->key), | ||
921 | (long long) GNUNET_ntohll (se->value)); | ||
922 | GNUNET_assert ( | ||
923 | GNUNET_YES == | ||
924 | GNUNET_CONTAINER_multihashmap_remove (s->intersected_elements, | ||
925 | element->data, | ||
926 | se)); | ||
927 | GNUNET_free (se); | ||
928 | return; | ||
929 | |||
930 | case GNUNET_SETI_STATUS_DONE: | ||
931 | s->intersection_op = NULL; | ||
932 | if (NULL != s->intersection_set) | ||
933 | { | ||
934 | GNUNET_SETI_destroy (s->intersection_set); | ||
935 | s->intersection_set = NULL; | ||
936 | } | ||
937 | send_alices_cryptodata_message (s); | ||
938 | return; | ||
939 | case GNUNET_SETI_STATUS_FAILURE: | ||
940 | /* unhandled status code */ | ||
941 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Set intersection failed!\n"); | ||
942 | if (NULL != s->intersection_listen) | ||
943 | { | ||
944 | GNUNET_SETI_listen_cancel (s->intersection_listen); | ||
945 | s->intersection_listen = NULL; | ||
946 | } | ||
947 | s->intersection_op = NULL; | ||
948 | if (NULL != s->intersection_set) | ||
949 | { | ||
950 | GNUNET_SETI_destroy (s->intersection_set); | ||
951 | s->intersection_set = NULL; | ||
952 | } | ||
953 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
954 | prepare_client_end_notification (s); | ||
955 | return; | ||
956 | |||
957 | default: | ||
958 | GNUNET_break (0); | ||
959 | return; | ||
960 | } | ||
961 | } | ||
962 | |||
963 | |||
964 | /** | ||
965 | * Called when another peer wants to do a set operation with the | ||
966 | * local peer. If a listen error occurs, the @a request is NULL. | ||
967 | * | ||
968 | * @param cls closure with the `struct AliceServiceSession *` | ||
969 | * @param other_peer the other peer | ||
970 | * @param context_msg message with application specific information from | ||
971 | * the other peer | ||
972 | * @param request request from the other peer (never NULL), use GNUNET_SETI_accept() | ||
973 | * to accept it, otherwise the request will be refused | ||
974 | * Note that we can't just return value from the listen callback, | ||
975 | * as it is also necessary to specify the set we want to do the | ||
976 | * operation with, which sometimes can be derived from the context | ||
977 | * message. It's necessary to specify the timeout. | ||
978 | */ | ||
979 | static void | ||
980 | cb_intersection_request_alice (void *cls, | ||
981 | const struct GNUNET_PeerIdentity *other_peer, | ||
982 | const struct GNUNET_MessageHeader *context_msg, | ||
983 | struct GNUNET_SETI_Request *request) | ||
984 | { | ||
985 | struct AliceServiceSession *s = cls; | ||
986 | |||
987 | if (0 != GNUNET_memcmp (other_peer, &s->peer)) | ||
988 | { | ||
989 | GNUNET_break_op (0); | ||
990 | return; | ||
991 | } | ||
992 | s->intersection_op = GNUNET_SETI_accept (request, | ||
993 | (struct | ||
994 | GNUNET_SETI_Option[]){ { 0 } }, | ||
995 | &cb_intersection_element_removed, | ||
996 | s); | ||
997 | if (NULL == s->intersection_op) | ||
998 | { | ||
999 | GNUNET_break (0); | ||
1000 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
1001 | prepare_client_end_notification (s); | ||
1002 | return; | ||
1003 | } | ||
1004 | if (GNUNET_OK != GNUNET_SETI_commit (s->intersection_op, s->intersection_set)) | ||
1005 | { | ||
1006 | GNUNET_break (0); | ||
1007 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
1008 | prepare_client_end_notification (s); | ||
1009 | return; | ||
1010 | } | ||
1011 | } | ||
1012 | |||
1013 | |||
1014 | /** | ||
1015 | * Our client has finished sending us its multipart message. | ||
1016 | * | ||
1017 | * @param session the service session context | ||
1018 | */ | ||
1019 | static void | ||
1020 | client_request_complete_alice (struct AliceServiceSession *s) | ||
1021 | { | ||
1022 | struct GNUNET_MQ_MessageHandler cadet_handlers[] = | ||
1023 | { GNUNET_MQ_hd_var_size (bobs_cryptodata_message, | ||
1024 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_CRYPTODATA, | ||
1025 | struct BobCryptodataMessage, | ||
1026 | s), | ||
1027 | GNUNET_MQ_hd_var_size ( | ||
1028 | bobs_cryptodata_multipart, | ||
1029 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_CRYPTODATA_MULTIPART, | ||
1030 | struct BobCryptodataMultipartMessage, | ||
1031 | s), | ||
1032 | GNUNET_MQ_handler_end () }; | ||
1033 | struct ServiceRequestMessage *msg; | ||
1034 | struct GNUNET_MQ_Envelope *e; | ||
1035 | |||
1036 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1037 | "Creating new channel for session with key %s.\n", | ||
1038 | GNUNET_h2s (&s->session_id)); | ||
1039 | s->channel = GNUNET_CADET_channel_create (my_cadet, | ||
1040 | s, | ||
1041 | &s->peer, | ||
1042 | &s->session_id, | ||
1043 | NULL, | ||
1044 | &cb_channel_destruction, | ||
1045 | cadet_handlers); | ||
1046 | if (NULL == s->channel) | ||
1047 | { | ||
1048 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
1049 | prepare_client_end_notification (s); | ||
1050 | return; | ||
1051 | } | ||
1052 | s->cadet_mq = GNUNET_CADET_get_mq (s->channel); | ||
1053 | s->intersection_listen = GNUNET_SETI_listen (cfg, | ||
1054 | &s->session_id, | ||
1055 | &cb_intersection_request_alice, | ||
1056 | s); | ||
1057 | if (NULL == s->intersection_listen) | ||
1058 | { | ||
1059 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
1060 | GNUNET_CADET_channel_destroy (s->channel); | ||
1061 | s->channel = NULL; | ||
1062 | prepare_client_end_notification (s); | ||
1063 | return; | ||
1064 | } | ||
1065 | |||
1066 | e = GNUNET_MQ_msg (msg, | ||
1067 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SESSION_INITIALIZATION); | ||
1068 | msg->session_id = s->session_id; | ||
1069 | msg->public_key = my_pubkey; | ||
1070 | GNUNET_MQ_send (s->cadet_mq, e); | ||
1071 | } | ||
1072 | |||
1073 | |||
1074 | /** | ||
1075 | * We're receiving additional set data. Check if | ||
1076 | * @a msg is well-formed. | ||
1077 | * | ||
1078 | * @param cls client identification of the client | ||
1079 | * @param msg the actual message | ||
1080 | * @return #GNUNET_OK if @a msg is well-formed | ||
1081 | */ | ||
1082 | static int | ||
1083 | check_alice_client_message_multipart ( | ||
1084 | void *cls, | ||
1085 | const struct ComputationBobCryptodataMultipartMessage *msg) | ||
1086 | { | ||
1087 | struct AliceServiceSession *s = cls; | ||
1088 | uint32_t contained_count; | ||
1089 | uint16_t msize; | ||
1090 | |||
1091 | msize = ntohs (msg->header.size); | ||
1092 | contained_count = ntohl (msg->element_count_contained); | ||
1093 | if ((msize != | ||
1094 | (sizeof(struct ComputationBobCryptodataMultipartMessage) | ||
1095 | + contained_count * sizeof(struct GNUNET_SCALARPRODUCT_Element))) || | ||
1096 | (0 == contained_count) || | ||
1097 | (s->total == s->client_received_element_count) || | ||
1098 | (s->total < s->client_received_element_count + contained_count)) | ||
1099 | { | ||
1100 | GNUNET_break_op (0); | ||
1101 | return GNUNET_SYSERR; | ||
1102 | } | ||
1103 | return GNUNET_OK; | ||
1104 | } | ||
1105 | |||
1106 | |||
1107 | /** | ||
1108 | * We're receiving additional set data. Add it to our | ||
1109 | * set and if we are done, initiate the transaction. | ||
1110 | * | ||
1111 | * @param cls client identification of the client | ||
1112 | * @param msg the actual message | ||
1113 | */ | ||
1114 | static void | ||
1115 | handle_alice_client_message_multipart ( | ||
1116 | void *cls, | ||
1117 | const struct ComputationBobCryptodataMultipartMessage *msg) | ||
1118 | { | ||
1119 | struct AliceServiceSession *s = cls; | ||
1120 | uint32_t contained_count; | ||
1121 | const struct GNUNET_SCALARPRODUCT_Element *elements; | ||
1122 | struct GNUNET_SETI_Element set_elem; | ||
1123 | struct GNUNET_SCALARPRODUCT_Element *elem; | ||
1124 | |||
1125 | contained_count = ntohl (msg->element_count_contained); | ||
1126 | s->client_received_element_count += contained_count; | ||
1127 | elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1]; | ||
1128 | for (uint32_t i = 0; i < contained_count; i++) | ||
1129 | { | ||
1130 | elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element); | ||
1131 | GNUNET_memcpy (elem, | ||
1132 | &elements[i], | ||
1133 | sizeof(struct GNUNET_SCALARPRODUCT_Element)); | ||
1134 | if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put ( | ||
1135 | s->intersected_elements, | ||
1136 | &elem->key, | ||
1137 | elem, | ||
1138 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) | ||
1139 | { | ||
1140 | GNUNET_break (0); | ||
1141 | GNUNET_free (elem); | ||
1142 | continue; | ||
1143 | } | ||
1144 | set_elem.data = &elem->key; | ||
1145 | set_elem.size = sizeof(elem->key); | ||
1146 | set_elem.element_type = 0; | ||
1147 | GNUNET_SETI_add_element (s->intersection_set, &set_elem, NULL, NULL); | ||
1148 | s->used_element_count++; | ||
1149 | } | ||
1150 | GNUNET_SERVICE_client_continue (s->client); | ||
1151 | if (s->total != s->client_received_element_count) | ||
1152 | { | ||
1153 | /* more to come */ | ||
1154 | return; | ||
1155 | } | ||
1156 | client_request_complete_alice (s); | ||
1157 | } | ||
1158 | |||
1159 | |||
1160 | /** | ||
1161 | * Handler for Alice's client request message. | ||
1162 | * Check that @a msg is well-formed. | ||
1163 | * | ||
1164 | * @param cls identification of the client | ||
1165 | * @param msg the actual message | ||
1166 | * @return #GNUNET_OK if @a msg is well-formed | ||
1167 | */ | ||
1168 | static int | ||
1169 | check_alice_client_message (void *cls, | ||
1170 | const struct AliceComputationMessage *msg) | ||
1171 | { | ||
1172 | struct AliceServiceSession *s = cls; | ||
1173 | uint16_t msize; | ||
1174 | uint32_t total_count; | ||
1175 | uint32_t contained_count; | ||
1176 | |||
1177 | if (NULL != s->intersected_elements) | ||
1178 | { | ||
1179 | /* only one concurrent session per client connection allowed, | ||
1180 | simplifies logic a lot... */ | ||
1181 | GNUNET_break (0); | ||
1182 | return GNUNET_SYSERR; | ||
1183 | } | ||
1184 | msize = ntohs (msg->header.size); | ||
1185 | total_count = ntohl (msg->element_count_total); | ||
1186 | contained_count = ntohl (msg->element_count_contained); | ||
1187 | if ((0 == total_count) || (0 == contained_count) || | ||
1188 | (msize != | ||
1189 | (sizeof(struct AliceComputationMessage) | ||
1190 | + contained_count * sizeof(struct GNUNET_SCALARPRODUCT_Element)))) | ||
1191 | { | ||
1192 | GNUNET_break_op (0); | ||
1193 | return GNUNET_SYSERR; | ||
1194 | } | ||
1195 | return GNUNET_OK; | ||
1196 | } | ||
1197 | |||
1198 | |||
1199 | /** | ||
1200 | * Handler for Alice's client request message. | ||
1201 | * We are doing request-initiation to compute a scalar product with a peer. | ||
1202 | * | ||
1203 | * @param cls identification of the client | ||
1204 | * @param msg the actual message | ||
1205 | */ | ||
1206 | static void | ||
1207 | handle_alice_client_message (void *cls, | ||
1208 | const struct AliceComputationMessage *msg) | ||
1209 | { | ||
1210 | struct AliceServiceSession *s = cls; | ||
1211 | uint32_t contained_count; | ||
1212 | uint32_t total_count; | ||
1213 | const struct GNUNET_SCALARPRODUCT_Element *elements; | ||
1214 | struct GNUNET_SETI_Element set_elem; | ||
1215 | struct GNUNET_SCALARPRODUCT_Element *elem; | ||
1216 | |||
1217 | total_count = ntohl (msg->element_count_total); | ||
1218 | contained_count = ntohl (msg->element_count_contained); | ||
1219 | s->peer = msg->peer; | ||
1220 | s->status = GNUNET_SCALARPRODUCT_STATUS_ACTIVE; | ||
1221 | s->total = total_count; | ||
1222 | s->client_received_element_count = contained_count; | ||
1223 | s->session_id = msg->session_key; | ||
1224 | elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1]; | ||
1225 | s->intersected_elements = | ||
1226 | GNUNET_CONTAINER_multihashmap_create (s->total, GNUNET_YES); | ||
1227 | s->intersection_set = GNUNET_SETI_create (cfg); | ||
1228 | |||
1229 | for (uint32_t i = 0; i < contained_count; i++) | ||
1230 | { | ||
1231 | if (0 == GNUNET_ntohll (elements[i].value)) | ||
1232 | continue; | ||
1233 | elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element); | ||
1234 | GNUNET_memcpy (elem, | ||
1235 | &elements[i], | ||
1236 | sizeof(struct GNUNET_SCALARPRODUCT_Element)); | ||
1237 | if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put ( | ||
1238 | s->intersected_elements, | ||
1239 | &elem->key, | ||
1240 | elem, | ||
1241 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) | ||
1242 | { | ||
1243 | /* element with same key encountered twice! */ | ||
1244 | GNUNET_break (0); | ||
1245 | GNUNET_free (elem); | ||
1246 | continue; | ||
1247 | } | ||
1248 | set_elem.data = &elem->key; | ||
1249 | set_elem.size = sizeof(elem->key); | ||
1250 | set_elem.element_type = 0; | ||
1251 | GNUNET_SETI_add_element (s->intersection_set, | ||
1252 | &set_elem, | ||
1253 | NULL, | ||
1254 | NULL); | ||
1255 | s->used_element_count++; | ||
1256 | } | ||
1257 | GNUNET_SERVICE_client_continue (s->client); | ||
1258 | if (s->total != s->client_received_element_count) | ||
1259 | { | ||
1260 | /* wait for multipart msg */ | ||
1261 | return; | ||
1262 | } | ||
1263 | client_request_complete_alice (s); | ||
1264 | } | ||
1265 | |||
1266 | |||
1267 | /** | ||
1268 | * Task run during shutdown. | ||
1269 | * | ||
1270 | * @param cls unused | ||
1271 | */ | ||
1272 | static void | ||
1273 | shutdown_task (void *cls) | ||
1274 | { | ||
1275 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down, initiating cleanup.\n"); | ||
1276 | // FIXME: we have to cut our connections to CADET first! | ||
1277 | if (NULL != my_cadet) | ||
1278 | { | ||
1279 | GNUNET_CADET_disconnect (my_cadet); | ||
1280 | my_cadet = NULL; | ||
1281 | } | ||
1282 | } | ||
1283 | |||
1284 | |||
1285 | /** | ||
1286 | * A client connected. | ||
1287 | * | ||
1288 | * Setup the associated data structure. | ||
1289 | * | ||
1290 | * @param cls closure, NULL | ||
1291 | * @param client identification of the client | ||
1292 | * @param mq message queue to communicate with @a client | ||
1293 | * @return our `struct AliceServiceSession` | ||
1294 | */ | ||
1295 | static void * | ||
1296 | client_connect_cb (void *cls, | ||
1297 | struct GNUNET_SERVICE_Client *client, | ||
1298 | struct GNUNET_MQ_Handle *mq) | ||
1299 | { | ||
1300 | struct AliceServiceSession *s; | ||
1301 | |||
1302 | s = GNUNET_new (struct AliceServiceSession); | ||
1303 | s->client = client; | ||
1304 | s->client_mq = mq; | ||
1305 | return s; | ||
1306 | } | ||
1307 | |||
1308 | |||
1309 | /** | ||
1310 | * A client disconnected. | ||
1311 | * | ||
1312 | * Remove the associated session(s), release data structures | ||
1313 | * and cancel pending outgoing transmissions to the client. | ||
1314 | * | ||
1315 | * @param cls closure, NULL | ||
1316 | * @param client identification of the client | ||
1317 | * @param app_cls our `struct AliceServiceSession` | ||
1318 | */ | ||
1319 | static void | ||
1320 | client_disconnect_cb (void *cls, | ||
1321 | struct GNUNET_SERVICE_Client *client, | ||
1322 | void *app_cls) | ||
1323 | { | ||
1324 | struct AliceServiceSession *s = app_cls; | ||
1325 | |||
1326 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1327 | "Client %p disconnected from us.\n", | ||
1328 | client); | ||
1329 | s->client = NULL; | ||
1330 | s->client_mq = NULL; | ||
1331 | destroy_service_session (s); | ||
1332 | } | ||
1333 | |||
1334 | |||
1335 | /** | ||
1336 | * Initialization of the program and message handlers | ||
1337 | * | ||
1338 | * @param cls closure | ||
1339 | * @param c configuration to use | ||
1340 | * @param service the initialized service | ||
1341 | */ | ||
1342 | static void | ||
1343 | run (void *cls, | ||
1344 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
1345 | struct GNUNET_SERVICE_Handle *service) | ||
1346 | { | ||
1347 | cfg = c; | ||
1348 | /* | ||
1349 | offset has to be sufficiently small to allow computation of: | ||
1350 | m1+m2 mod n == (S + a) + (S + b) mod n, | ||
1351 | if we have more complex operations, this factor needs to be lowered */ | ||
1352 | my_offset = gcry_mpi_new (GNUNET_CRYPTO_PAILLIER_BITS / 3); | ||
1353 | gcry_mpi_set_bit (my_offset, GNUNET_CRYPTO_PAILLIER_BITS / 3); | ||
1354 | GNUNET_CRYPTO_paillier_create (&my_pubkey, &my_privkey); | ||
1355 | my_cadet = GNUNET_CADET_connect (cfg); | ||
1356 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
1357 | if (NULL == my_cadet) | ||
1358 | { | ||
1359 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Connect to CADET failed\n")); | ||
1360 | GNUNET_SCHEDULER_shutdown (); | ||
1361 | return; | ||
1362 | } | ||
1363 | } | ||
1364 | |||
1365 | |||
1366 | /** | ||
1367 | * Define "main" method using service macro. | ||
1368 | */ | ||
1369 | GNUNET_SERVICE_MAIN ( | ||
1370 | "scalarproduct-alice", | ||
1371 | GNUNET_SERVICE_OPTION_NONE, | ||
1372 | &run, | ||
1373 | &client_connect_cb, | ||
1374 | &client_disconnect_cb, | ||
1375 | NULL, | ||
1376 | GNUNET_MQ_hd_var_size (alice_client_message, | ||
1377 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE, | ||
1378 | struct AliceComputationMessage, | ||
1379 | NULL), | ||
1380 | GNUNET_MQ_hd_var_size ( | ||
1381 | alice_client_message_multipart, | ||
1382 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_ALICE, | ||
1383 | struct ComputationBobCryptodataMultipartMessage, | ||
1384 | NULL), | ||
1385 | GNUNET_MQ_handler_end ()); | ||
1386 | |||
1387 | |||
1388 | /* end of gnunet-service-scalarproduct_alice.c */ | ||
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_bob.c b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_bob.c new file mode 100644 index 000000000..65e732675 --- /dev/null +++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_bob.c | |||
@@ -0,0 +1,1384 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2013, 2014, 2016 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_bob.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_seti_service.h" | ||
36 | #include "scalarproduct.h" | ||
37 | #include "gnunet-service-scalarproduct.h" | ||
38 | #include "gnunet_constants.h" | ||
39 | |||
40 | #define LOG(kind, ...) GNUNET_log_from (kind, "scalarproduct-bob", __VA_ARGS__) | ||
41 | |||
42 | |||
43 | /** | ||
44 | * An encrypted element key-value pair. | ||
45 | */ | ||
46 | struct MpiElement | ||
47 | { | ||
48 | /** | ||
49 | * Key used to identify matching pairs of values to multiply. | ||
50 | * Points into an existing data structure, to avoid copying | ||
51 | * and doubling memory use. | ||
52 | */ | ||
53 | const struct GNUNET_HashCode *key; | ||
54 | |||
55 | /** | ||
56 | * Value represented (a). | ||
57 | */ | ||
58 | gcry_mpi_t value; | ||
59 | }; | ||
60 | |||
61 | |||
62 | /** | ||
63 | * A scalarproduct session which tracks an offer for a | ||
64 | * multiplication service by a local client. | ||
65 | */ | ||
66 | struct BobServiceSession | ||
67 | { | ||
68 | /** | ||
69 | * (hopefully) unique transaction ID | ||
70 | */ | ||
71 | struct GNUNET_HashCode session_id; | ||
72 | |||
73 | /** | ||
74 | * The client this request is related to. | ||
75 | */ | ||
76 | struct GNUNET_SERVICE_Client *client; | ||
77 | |||
78 | /** | ||
79 | * Client message queue. | ||
80 | */ | ||
81 | struct GNUNET_MQ_Handle *client_mq; | ||
82 | |||
83 | /** | ||
84 | * All non-0-value'd elements transmitted to us. | ||
85 | */ | ||
86 | struct GNUNET_CONTAINER_MultiHashMap *intersected_elements; | ||
87 | |||
88 | /** | ||
89 | * Set of elements for which we will be conducting an intersection. | ||
90 | * The resulting elements are then used for computing the scalar product. | ||
91 | */ | ||
92 | struct GNUNET_SETI_Handle *intersection_set; | ||
93 | |||
94 | /** | ||
95 | * Set of elements for which will conduction an intersection. | ||
96 | * the resulting elements are then used for computing the scalar product. | ||
97 | */ | ||
98 | struct GNUNET_SETI_OperationHandle *intersection_op; | ||
99 | |||
100 | /** | ||
101 | * CADET port we are listening on. | ||
102 | */ | ||
103 | struct GNUNET_CADET_Port *port; | ||
104 | |||
105 | /** | ||
106 | * a(Alice) | ||
107 | */ | ||
108 | struct MpiElement *sorted_elements; | ||
109 | |||
110 | /** | ||
111 | * E(ai)(Bob) after applying the mask | ||
112 | */ | ||
113 | struct GNUNET_CRYPTO_PaillierCiphertext *e_a; | ||
114 | |||
115 | /** | ||
116 | * Bob's permutation p of R | ||
117 | */ | ||
118 | struct GNUNET_CRYPTO_PaillierCiphertext *r; | ||
119 | |||
120 | /** | ||
121 | * Bob's permutation q of R | ||
122 | */ | ||
123 | struct GNUNET_CRYPTO_PaillierCiphertext *r_prime; | ||
124 | |||
125 | /** | ||
126 | * Bob's "s" | ||
127 | */ | ||
128 | struct GNUNET_CRYPTO_PaillierCiphertext s; | ||
129 | |||
130 | /** | ||
131 | * Bob's "s'" | ||
132 | */ | ||
133 | struct GNUNET_CRYPTO_PaillierCiphertext s_prime; | ||
134 | |||
135 | /** | ||
136 | * Handle for our associated incoming CADET session, or NULL | ||
137 | * if we have not gotten one yet. | ||
138 | */ | ||
139 | struct CadetIncomingSession *cadet; | ||
140 | |||
141 | /** | ||
142 | * How many elements will be supplied in total from the client. | ||
143 | */ | ||
144 | uint32_t total; | ||
145 | |||
146 | /** | ||
147 | * Already transferred elements (received) for multipart | ||
148 | * messages from client. Always less than @e total. | ||
149 | */ | ||
150 | uint32_t client_received_element_count; | ||
151 | |||
152 | /** | ||
153 | * How many elements actually are used for the scalar product. | ||
154 | * Size of the arrays in @e r and @e r_prime. Also sometimes | ||
155 | * used as an index into the arrays during construction. | ||
156 | */ | ||
157 | uint32_t used_element_count; | ||
158 | |||
159 | /** | ||
160 | * Counts the number of values received from Alice by us. | ||
161 | * Always less than @e used_element_count. | ||
162 | */ | ||
163 | uint32_t cadet_received_element_count; | ||
164 | |||
165 | /** | ||
166 | * Counts the number of values transmitted from us to Alice. | ||
167 | * Always less than @e used_element_count. | ||
168 | */ | ||
169 | uint32_t cadet_transmitted_element_count; | ||
170 | |||
171 | /** | ||
172 | * State of this session. In | ||
173 | * #GNUNET_SCALARPRODUCT_STATUS_ACTIVE while operation is | ||
174 | * ongoing, afterwards in #GNUNET_SCALARPRODUCT_STATUS_SUCCESS or | ||
175 | * #GNUNET_SCALARPRODUCT_STATUS_FAILURE. | ||
176 | */ | ||
177 | enum GNUNET_SCALARPRODUCT_ResponseStatus status; | ||
178 | |||
179 | /** | ||
180 | * Are we already in #destroy_service_session()? | ||
181 | */ | ||
182 | int in_destroy; | ||
183 | |||
184 | /** | ||
185 | * The CADET channel. | ||
186 | */ | ||
187 | struct GNUNET_CADET_Channel *channel; | ||
188 | |||
189 | /** | ||
190 | * Originator's peer identity. (Only for diagnostics.) | ||
191 | */ | ||
192 | struct GNUNET_PeerIdentity peer; | ||
193 | |||
194 | /** | ||
195 | * Public key of the remote service. | ||
196 | */ | ||
197 | struct GNUNET_CRYPTO_PaillierPublicKey remote_pubkey; | ||
198 | |||
199 | /** | ||
200 | * The message queue for this channel. | ||
201 | */ | ||
202 | struct GNUNET_MQ_Handle *cadet_mq; | ||
203 | }; | ||
204 | |||
205 | |||
206 | /** | ||
207 | * GNUnet configuration handle | ||
208 | */ | ||
209 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
210 | |||
211 | /** | ||
212 | * Service's own public key | ||
213 | */ | ||
214 | static struct GNUNET_CRYPTO_PaillierPublicKey my_pubkey; | ||
215 | |||
216 | /** | ||
217 | * Service's own private key | ||
218 | */ | ||
219 | static struct GNUNET_CRYPTO_PaillierPrivateKey my_privkey; | ||
220 | |||
221 | /** | ||
222 | * Service's offset for values that could possibly be negative but are plaintext for encryption. | ||
223 | */ | ||
224 | static gcry_mpi_t my_offset; | ||
225 | |||
226 | /** | ||
227 | * Handle to the CADET service. | ||
228 | */ | ||
229 | static struct GNUNET_CADET_Handle *my_cadet; | ||
230 | |||
231 | |||
232 | /** | ||
233 | * Callback used to free the elements in the map. | ||
234 | * | ||
235 | * @param cls NULL | ||
236 | * @param key key of the element | ||
237 | * @param value the value to free | ||
238 | */ | ||
239 | static int | ||
240 | free_element_cb (void *cls, | ||
241 | const struct GNUNET_HashCode *key, | ||
242 | void *value) | ||
243 | { | ||
244 | struct GNUNET_SCALARPRODUCT_Element *element = value; | ||
245 | |||
246 | GNUNET_free (element); | ||
247 | return GNUNET_OK; | ||
248 | } | ||
249 | |||
250 | |||
251 | /** | ||
252 | * Destroy session state, we are done with it. | ||
253 | * | ||
254 | * @param session the session to free elements from | ||
255 | */ | ||
256 | static void | ||
257 | destroy_service_session (struct BobServiceSession *s) | ||
258 | { | ||
259 | unsigned int i; | ||
260 | |||
261 | if (GNUNET_YES == s->in_destroy) | ||
262 | return; | ||
263 | s->in_destroy = GNUNET_YES; | ||
264 | if (NULL != s->client) | ||
265 | { | ||
266 | struct GNUNET_SERVICE_Client *c = s->client; | ||
267 | |||
268 | s->client = NULL; | ||
269 | GNUNET_SERVICE_client_drop (c); | ||
270 | } | ||
271 | if (NULL != s->intersected_elements) | ||
272 | { | ||
273 | GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements, | ||
274 | &free_element_cb, | ||
275 | NULL); | ||
276 | GNUNET_CONTAINER_multihashmap_destroy (s->intersected_elements); | ||
277 | s->intersected_elements = NULL; | ||
278 | } | ||
279 | if (NULL != s->intersection_op) | ||
280 | { | ||
281 | GNUNET_SETI_operation_cancel (s->intersection_op); | ||
282 | s->intersection_op = NULL; | ||
283 | } | ||
284 | if (NULL != s->intersection_set) | ||
285 | { | ||
286 | GNUNET_SETI_destroy (s->intersection_set); | ||
287 | s->intersection_set = NULL; | ||
288 | } | ||
289 | if (NULL != s->e_a) | ||
290 | { | ||
291 | GNUNET_free (s->e_a); | ||
292 | s->e_a = NULL; | ||
293 | } | ||
294 | if (NULL != s->sorted_elements) | ||
295 | { | ||
296 | for (i = 0; i < s->used_element_count; i++) | ||
297 | gcry_mpi_release (s->sorted_elements[i].value); | ||
298 | GNUNET_free (s->sorted_elements); | ||
299 | s->sorted_elements = NULL; | ||
300 | } | ||
301 | if (NULL != s->r) | ||
302 | { | ||
303 | GNUNET_free (s->r); | ||
304 | s->r = NULL; | ||
305 | } | ||
306 | if (NULL != s->r_prime) | ||
307 | { | ||
308 | GNUNET_free (s->r_prime); | ||
309 | s->r_prime = NULL; | ||
310 | } | ||
311 | if (NULL != s->port) | ||
312 | { | ||
313 | GNUNET_CADET_close_port (s->port); | ||
314 | s->port = NULL; | ||
315 | } | ||
316 | if (NULL != s->channel) | ||
317 | { | ||
318 | GNUNET_CADET_channel_destroy (s->channel); | ||
319 | s->channel = NULL; | ||
320 | } | ||
321 | GNUNET_free (s); | ||
322 | } | ||
323 | |||
324 | |||
325 | /** | ||
326 | * Notify the client that the session has succeeded or failed. This | ||
327 | * message gets sent to Bob's client if the operation completed or | ||
328 | * Alice disconnected. | ||
329 | * | ||
330 | * @param session the associated client session to fail or succeed | ||
331 | */ | ||
332 | static void | ||
333 | prepare_client_end_notification (struct BobServiceSession *session) | ||
334 | { | ||
335 | struct ClientResponseMessage *msg; | ||
336 | struct GNUNET_MQ_Envelope *e; | ||
337 | |||
338 | if (NULL == session->client_mq) | ||
339 | return; /* no client left to be notified */ | ||
340 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
341 | "Sending session-end notification with status %d to client for session %s\n", | ||
342 | session->status, | ||
343 | GNUNET_h2s (&session->session_id)); | ||
344 | e = GNUNET_MQ_msg (msg, | ||
345 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT); | ||
346 | msg->range = 0; | ||
347 | msg->product_length = htonl (0); | ||
348 | msg->status = htonl (session->status); | ||
349 | GNUNET_MQ_send (session->client_mq, | ||
350 | e); | ||
351 | } | ||
352 | |||
353 | |||
354 | /** | ||
355 | * Function called whenever a channel is destroyed. Should clean up | ||
356 | * any associated state. | ||
357 | * | ||
358 | * It must NOT call #GNUNET_CADET_channel_destroy() on the channel. | ||
359 | * | ||
360 | * @param cls the `struct BobServiceSession` | ||
361 | * @param channel connection to the other end (henceforth invalid) | ||
362 | */ | ||
363 | static void | ||
364 | cb_channel_destruction (void *cls, | ||
365 | const struct GNUNET_CADET_Channel *channel) | ||
366 | { | ||
367 | struct BobServiceSession *s = cls; | ||
368 | |||
369 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
370 | "Peer disconnected, terminating session %s with peer %s\n", | ||
371 | GNUNET_h2s (&s->session_id), | ||
372 | GNUNET_i2s (&s->peer)); | ||
373 | if (GNUNET_SCALARPRODUCT_STATUS_ACTIVE == s->status) | ||
374 | { | ||
375 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
376 | prepare_client_end_notification (s); | ||
377 | } | ||
378 | s->channel = NULL; | ||
379 | destroy_service_session (s); | ||
380 | } | ||
381 | |||
382 | |||
383 | /** | ||
384 | * MQ finished giving our last message to CADET, now notify | ||
385 | * the client that we are finished. | ||
386 | */ | ||
387 | static void | ||
388 | bob_cadet_done_cb (void *cls) | ||
389 | { | ||
390 | struct BobServiceSession *session = cls; | ||
391 | |||
392 | session->status = GNUNET_SCALARPRODUCT_STATUS_SUCCESS; | ||
393 | prepare_client_end_notification (session); | ||
394 | } | ||
395 | |||
396 | |||
397 | /** | ||
398 | * Maximum count of elements we can put into a multipart message | ||
399 | */ | ||
400 | #define ELEMENT_CAPACITY ((GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE - 1 \ | ||
401 | - sizeof(struct BobCryptodataMultipartMessage)) \ | ||
402 | / sizeof(struct \ | ||
403 | GNUNET_CRYPTO_PaillierCiphertext)) | ||
404 | |||
405 | |||
406 | /** | ||
407 | * Send a multipart chunk of a service response from Bob to Alice. | ||
408 | * This element only contains the two permutations of R, R'. | ||
409 | * | ||
410 | * @param s the associated service session | ||
411 | */ | ||
412 | static void | ||
413 | transmit_bobs_cryptodata_message_multipart (struct BobServiceSession *s) | ||
414 | { | ||
415 | struct GNUNET_CRYPTO_PaillierCiphertext *payload; | ||
416 | struct BobCryptodataMultipartMessage *msg; | ||
417 | struct GNUNET_MQ_Envelope *e; | ||
418 | unsigned int i; | ||
419 | unsigned int j; | ||
420 | uint32_t todo_count; | ||
421 | |||
422 | while (s->cadet_transmitted_element_count != s->used_element_count) | ||
423 | { | ||
424 | todo_count = s->used_element_count - s->cadet_transmitted_element_count; | ||
425 | if (todo_count > ELEMENT_CAPACITY / 2) | ||
426 | todo_count = ELEMENT_CAPACITY / 2; | ||
427 | |||
428 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
429 | "Sending %u additional crypto values to Alice\n", | ||
430 | (unsigned int) todo_count); | ||
431 | e = GNUNET_MQ_msg_extra (msg, | ||
432 | todo_count * sizeof(struct | ||
433 | GNUNET_CRYPTO_PaillierCiphertext) | ||
434 | * 2, | ||
435 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_CRYPTODATA_MULTIPART); | ||
436 | msg->contained_element_count = htonl (todo_count); | ||
437 | payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1]; | ||
438 | for (i = s->cadet_transmitted_element_count, j = 0; i < | ||
439 | s->cadet_transmitted_element_count + todo_count; i++) | ||
440 | { | ||
441 | // r[i][p] and r[i][q] | ||
442 | GNUNET_memcpy (&payload[j++], | ||
443 | &s->r[i], | ||
444 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
445 | GNUNET_memcpy (&payload[j++], | ||
446 | &s->r_prime[i], | ||
447 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
448 | } | ||
449 | s->cadet_transmitted_element_count += todo_count; | ||
450 | if (s->cadet_transmitted_element_count == s->used_element_count) | ||
451 | GNUNET_MQ_notify_sent (e, | ||
452 | &bob_cadet_done_cb, | ||
453 | s); | ||
454 | GNUNET_MQ_send (s->cadet_mq, | ||
455 | e); | ||
456 | } | ||
457 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
458 | "All values queued for Alice, Bob is done\n"); | ||
459 | } | ||
460 | |||
461 | |||
462 | /** | ||
463 | * Bob generates the response message to be sent to Alice after | ||
464 | * computing the values (1), (2), S and S'. | ||
465 | * | ||
466 | * (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)})$ | ||
467 | * (2)[]: $E_A(a_{pi'(i)}) times E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$ | ||
468 | * S: $S := E_A(sum (r_i + b_i)^2)$ | ||
469 | * S': $S' := E_A(sum r_i^2)$ | ||
470 | * | ||
471 | * @param s the associated requesting session with Alice | ||
472 | */ | ||
473 | static void | ||
474 | transmit_bobs_cryptodata_message (struct BobServiceSession *s) | ||
475 | { | ||
476 | struct BobCryptodataMessage *msg; | ||
477 | struct GNUNET_MQ_Envelope *e; | ||
478 | struct GNUNET_CRYPTO_PaillierCiphertext *payload; | ||
479 | unsigned int i; | ||
480 | |||
481 | s->cadet_transmitted_element_count | ||
482 | = ((GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE - 1 - sizeof(struct | ||
483 | BobCryptodataMessage)) | ||
484 | / sizeof(struct GNUNET_CRYPTO_PaillierCiphertext) / 2) - 1; | ||
485 | if (s->cadet_transmitted_element_count > s->used_element_count) | ||
486 | s->cadet_transmitted_element_count = s->used_element_count; | ||
487 | |||
488 | e = GNUNET_MQ_msg_extra (msg, | ||
489 | (2 + s->cadet_transmitted_element_count * 2) | ||
490 | * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext), | ||
491 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_CRYPTODATA); | ||
492 | msg->contained_element_count = htonl (s->cadet_transmitted_element_count); | ||
493 | |||
494 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
495 | "Sending %u/%u crypto values to Alice\n", | ||
496 | (unsigned int) s->cadet_transmitted_element_count, | ||
497 | (unsigned int) s->used_element_count); | ||
498 | |||
499 | payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1]; | ||
500 | GNUNET_memcpy (&payload[0], | ||
501 | &s->s, | ||
502 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
503 | GNUNET_memcpy (&payload[1], | ||
504 | &s->s_prime, | ||
505 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
506 | |||
507 | payload = &payload[2]; | ||
508 | // convert k[][] | ||
509 | for (i = 0; i < s->cadet_transmitted_element_count; i++) | ||
510 | { | ||
511 | // k[i][p] and k[i][q] | ||
512 | GNUNET_memcpy (&payload[i * 2], | ||
513 | &s->r[i], | ||
514 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
515 | GNUNET_memcpy (&payload[i * 2 + 1], | ||
516 | &s->r_prime[i], | ||
517 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)); | ||
518 | } | ||
519 | if (s->cadet_transmitted_element_count == s->used_element_count) | ||
520 | GNUNET_MQ_notify_sent (e, | ||
521 | &bob_cadet_done_cb, | ||
522 | s); | ||
523 | GNUNET_MQ_send (s->cadet_mq, | ||
524 | e); | ||
525 | transmit_bobs_cryptodata_message_multipart (s); | ||
526 | } | ||
527 | |||
528 | |||
529 | #undef ELEMENT_CAPACITY | ||
530 | |||
531 | |||
532 | /** | ||
533 | * Computes the square sum over a vector of a given length. | ||
534 | * | ||
535 | * @param vector the vector to compute over | ||
536 | * @param length the length of the vector | ||
537 | * @return an MPI value containing the calculated sum, never NULL | ||
538 | * TODO: code duplication with Alice! | ||
539 | */ | ||
540 | static gcry_mpi_t | ||
541 | compute_square_sum (const gcry_mpi_t *vector, | ||
542 | uint32_t length) | ||
543 | { | ||
544 | gcry_mpi_t elem; | ||
545 | gcry_mpi_t sum; | ||
546 | uint32_t i; | ||
547 | |||
548 | GNUNET_assert (NULL != (sum = gcry_mpi_new (0))); | ||
549 | GNUNET_assert (NULL != (elem = gcry_mpi_new (0))); | ||
550 | for (i = 0; i < length; i++) | ||
551 | { | ||
552 | gcry_mpi_mul (elem, vector[i], vector[i]); | ||
553 | gcry_mpi_add (sum, sum, elem); | ||
554 | } | ||
555 | gcry_mpi_release (elem); | ||
556 | return sum; | ||
557 | } | ||
558 | |||
559 | |||
560 | /** | ||
561 | * Compute the values | ||
562 | * (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)})$ | ||
563 | * (2)[]: $E_A(a_{pi'(i)}) otimes E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$ | ||
564 | * S: $S := E_A(sum (r_i + b_i)^2)$ | ||
565 | * S': $S' := E_A(sum r_i^2)$ | ||
566 | * | ||
567 | * @param session the requesting session + bob's requesting peer | ||
568 | * @return #GNUNET_OK on success | ||
569 | */ | ||
570 | static int | ||
571 | compute_service_response (struct BobServiceSession *session) | ||
572 | { | ||
573 | uint32_t i; | ||
574 | unsigned int *p; | ||
575 | unsigned int *q; | ||
576 | uint32_t count; | ||
577 | gcry_mpi_t *rand; | ||
578 | gcry_mpi_t tmp; | ||
579 | const struct MpiElement *b; | ||
580 | struct GNUNET_CRYPTO_PaillierCiphertext *a; | ||
581 | struct GNUNET_CRYPTO_PaillierCiphertext *r; | ||
582 | struct GNUNET_CRYPTO_PaillierCiphertext *r_prime; | ||
583 | |||
584 | count = session->used_element_count; | ||
585 | a = session->e_a; | ||
586 | b = session->sorted_elements; | ||
587 | q = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, | ||
588 | count); | ||
589 | p = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, | ||
590 | count); | ||
591 | rand = GNUNET_malloc (sizeof(gcry_mpi_t) * count); | ||
592 | for (i = 0; i < count; i++) | ||
593 | GNUNET_assert (NULL != (rand[i] = gcry_mpi_new (0))); | ||
594 | r = GNUNET_malloc (sizeof(struct GNUNET_CRYPTO_PaillierCiphertext) * count); | ||
595 | r_prime = GNUNET_malloc (sizeof(struct GNUNET_CRYPTO_PaillierCiphertext) | ||
596 | * count); | ||
597 | |||
598 | for (i = 0; i < count; i++) | ||
599 | { | ||
600 | int32_t svalue; | ||
601 | |||
602 | svalue = (int32_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
603 | UINT32_MAX); | ||
604 | // long to gcry_mpi_t | ||
605 | if (svalue < 0) | ||
606 | gcry_mpi_sub_ui (rand[i], | ||
607 | rand[i], | ||
608 | -svalue); | ||
609 | else | ||
610 | rand[i] = gcry_mpi_set_ui (rand[i], svalue); | ||
611 | } | ||
612 | |||
613 | tmp = gcry_mpi_new (0); | ||
614 | // encrypt the element | ||
615 | // for the sake of readability I decided to have dedicated permutation | ||
616 | // vectors, which get rid of all the lookups in p/q. | ||
617 | // however, ap/aq are not absolutely necessary but are just abstraction | ||
618 | // Calculate Kp = E(S + a_pi) (+) E(S - r_pi - b_pi) | ||
619 | for (i = 0; i < count; i++) | ||
620 | { | ||
621 | // E(S - r_pi - b_pi) | ||
622 | gcry_mpi_sub (tmp, my_offset, rand[p[i]]); | ||
623 | gcry_mpi_sub (tmp, tmp, b[p[i]].value); | ||
624 | GNUNET_assert (2 == | ||
625 | GNUNET_CRYPTO_paillier_encrypt (&session->remote_pubkey, | ||
626 | tmp, | ||
627 | 2, | ||
628 | &r[i])); | ||
629 | |||
630 | // E(S - r_pi - b_pi) * E(S + a_pi) == E(2*S + a - r - b) | ||
631 | if (GNUNET_OK != | ||
632 | GNUNET_CRYPTO_paillier_hom_add (&session->remote_pubkey, | ||
633 | &r[i], | ||
634 | &a[p[i]], | ||
635 | &r[i])) | ||
636 | { | ||
637 | GNUNET_break_op (0); | ||
638 | goto error_cleanup; | ||
639 | } | ||
640 | } | ||
641 | |||
642 | // Calculate Kq = E(S + a_qi) (+) E(S - r_qi) | ||
643 | for (i = 0; i < count; i++) | ||
644 | { | ||
645 | // E(S - r_qi) | ||
646 | gcry_mpi_sub (tmp, my_offset, rand[q[i]]); | ||
647 | GNUNET_assert (2 == | ||
648 | GNUNET_CRYPTO_paillier_encrypt (&session->remote_pubkey, | ||
649 | tmp, | ||
650 | 2, | ||
651 | &r_prime[i])); | ||
652 | |||
653 | // E(S - r_qi) * E(S + a_qi) == E(2*S + a_qi - r_qi) | ||
654 | if (GNUNET_OK != | ||
655 | GNUNET_CRYPTO_paillier_hom_add (&session->remote_pubkey, | ||
656 | &r_prime[i], | ||
657 | &a[q[i]], | ||
658 | &r_prime[i])) | ||
659 | { | ||
660 | GNUNET_break_op (0); | ||
661 | goto error_cleanup; | ||
662 | } | ||
663 | } | ||
664 | gcry_mpi_release (tmp); | ||
665 | |||
666 | // Calculate S' = E(SUM( r_i^2 )) | ||
667 | tmp = compute_square_sum (rand, count); | ||
668 | GNUNET_assert (1 == | ||
669 | GNUNET_CRYPTO_paillier_encrypt (&session->remote_pubkey, | ||
670 | tmp, | ||
671 | 1, | ||
672 | &session->s_prime)); | ||
673 | gcry_mpi_release (tmp); | ||
674 | |||
675 | // Calculate S = E(SUM( (r_i + b_i)^2 )) | ||
676 | for (i = 0; i < count; i++) | ||
677 | gcry_mpi_add (rand[i], rand[i], b[i].value); | ||
678 | tmp = compute_square_sum (rand, count); | ||
679 | GNUNET_assert (1 == | ||
680 | GNUNET_CRYPTO_paillier_encrypt (&session->remote_pubkey, | ||
681 | tmp, | ||
682 | 1, | ||
683 | &session->s)); | ||
684 | gcry_mpi_release (tmp); | ||
685 | |||
686 | session->r = r; | ||
687 | session->r_prime = r_prime; | ||
688 | |||
689 | for (i = 0; i < count; i++) | ||
690 | gcry_mpi_release (rand[i]); | ||
691 | GNUNET_free (session->e_a); | ||
692 | session->e_a = NULL; | ||
693 | GNUNET_free (p); | ||
694 | GNUNET_free (q); | ||
695 | GNUNET_free (rand); | ||
696 | return GNUNET_OK; | ||
697 | |||
698 | error_cleanup: | ||
699 | GNUNET_free (r); | ||
700 | GNUNET_free (r_prime); | ||
701 | gcry_mpi_release (tmp); | ||
702 | GNUNET_free (p); | ||
703 | GNUNET_free (q); | ||
704 | for (i = 0; i < count; i++) | ||
705 | gcry_mpi_release (rand[i]); | ||
706 | GNUNET_free (rand); | ||
707 | return GNUNET_SYSERR; | ||
708 | } | ||
709 | |||
710 | |||
711 | /** | ||
712 | * Iterator to copy over messages from the hash map | ||
713 | * into an array for sorting. | ||
714 | * | ||
715 | * @param cls the `struct BobServiceSession *` | ||
716 | * @param key the key (unused) | ||
717 | * @param value the `struct GNUNET_SCALARPRODUCT_Element *` | ||
718 | * TODO: code duplication with Alice! | ||
719 | */ | ||
720 | static int | ||
721 | copy_element_cb (void *cls, | ||
722 | const struct GNUNET_HashCode *key, | ||
723 | void *value) | ||
724 | { | ||
725 | struct BobServiceSession *s = cls; | ||
726 | struct GNUNET_SCALARPRODUCT_Element *e = value; | ||
727 | gcry_mpi_t mval; | ||
728 | int64_t val; | ||
729 | |||
730 | mval = gcry_mpi_new (0); | ||
731 | val = (int64_t) GNUNET_ntohll (e->value); | ||
732 | if (0 > val) | ||
733 | gcry_mpi_sub_ui (mval, mval, -val); | ||
734 | else | ||
735 | gcry_mpi_add_ui (mval, mval, val); | ||
736 | s->sorted_elements [s->used_element_count].value = mval; | ||
737 | s->sorted_elements [s->used_element_count].key = &e->key; | ||
738 | s->used_element_count++; | ||
739 | return GNUNET_OK; | ||
740 | } | ||
741 | |||
742 | |||
743 | /** | ||
744 | * Compare two `struct MpiValue`s by key for sorting. | ||
745 | * | ||
746 | * @param a pointer to first `struct MpiValue *` | ||
747 | * @param b pointer to first `struct MpiValue *` | ||
748 | * @return -1 for a < b, 0 for a=b, 1 for a > b. | ||
749 | * TODO: code duplication with Alice! | ||
750 | */ | ||
751 | static int | ||
752 | element_cmp (const void *a, | ||
753 | const void *b) | ||
754 | { | ||
755 | const struct MpiElement *ma = a; | ||
756 | const struct MpiElement *mb = b; | ||
757 | |||
758 | return GNUNET_CRYPTO_hash_cmp (ma->key, | ||
759 | mb->key); | ||
760 | } | ||
761 | |||
762 | |||
763 | /** | ||
764 | * Intersection operation and receiving data via CADET from | ||
765 | * Alice are both done, compute and transmit our reply via | ||
766 | * CADET. | ||
767 | * | ||
768 | * @param s session to transmit reply for. | ||
769 | */ | ||
770 | static void | ||
771 | transmit_cryptographic_reply (struct BobServiceSession *s) | ||
772 | { | ||
773 | struct GNUNET_CADET_Channel *channel; | ||
774 | |||
775 | /* TODO: code duplication with Alice! */ | ||
776 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
777 | "Received everything, building reply for Alice\n"); | ||
778 | s->sorted_elements | ||
779 | = GNUNET_malloc (GNUNET_CONTAINER_multihashmap_size ( | ||
780 | s->intersected_elements) | ||
781 | * sizeof(struct MpiElement)); | ||
782 | s->used_element_count = 0; | ||
783 | GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements, | ||
784 | ©_element_cb, | ||
785 | s); | ||
786 | qsort (s->sorted_elements, | ||
787 | s->used_element_count, | ||
788 | sizeof(struct MpiElement), | ||
789 | &element_cmp); | ||
790 | if (GNUNET_OK != | ||
791 | compute_service_response (s)) | ||
792 | { | ||
793 | channel = s->channel; | ||
794 | s->channel = NULL; | ||
795 | GNUNET_CADET_channel_destroy (channel); | ||
796 | return; | ||
797 | } | ||
798 | transmit_bobs_cryptodata_message (s); | ||
799 | } | ||
800 | |||
801 | |||
802 | /** | ||
803 | * Check a multipart-chunk of a request from another service to | ||
804 | * calculate a scalarproduct with us. | ||
805 | * | ||
806 | * @param cls the `struct BobServiceSession *` | ||
807 | * @param msg the actual message | ||
808 | * @return #GNUNET_OK to keep the connection open, | ||
809 | * #GNUNET_SYSERR to close it (signal serious error) | ||
810 | */ | ||
811 | static int | ||
812 | check_alices_cryptodata_message (void *cls, | ||
813 | const struct AliceCryptodataMessage *msg) | ||
814 | { | ||
815 | struct BobServiceSession *s = cls; | ||
816 | uint32_t contained_elements; | ||
817 | size_t msg_length; | ||
818 | uint16_t msize; | ||
819 | unsigned int max; | ||
820 | |||
821 | msize = ntohs (msg->header.size); | ||
822 | contained_elements = ntohl (msg->contained_element_count); | ||
823 | /* Our intersection may still be ongoing, but this is nevertheless | ||
824 | an upper bound on the required array size */ | ||
825 | max = GNUNET_CONTAINER_multihashmap_size (s->intersected_elements); | ||
826 | msg_length = sizeof(struct AliceCryptodataMessage) | ||
827 | + contained_elements * sizeof(struct | ||
828 | GNUNET_CRYPTO_PaillierCiphertext); | ||
829 | if ((msize != msg_length) || | ||
830 | (0 == contained_elements) || | ||
831 | (contained_elements > UINT16_MAX) || | ||
832 | (max < contained_elements + s->cadet_received_element_count)) | ||
833 | { | ||
834 | GNUNET_break_op (0); | ||
835 | return GNUNET_SYSERR; | ||
836 | } | ||
837 | return GNUNET_OK; | ||
838 | } | ||
839 | |||
840 | |||
841 | /** | ||
842 | * Handle a multipart-chunk of a request from another service to | ||
843 | * calculate a scalarproduct with us. | ||
844 | * | ||
845 | * @param cls the `struct BobServiceSession *` | ||
846 | * @param msg the actual message | ||
847 | */ | ||
848 | static void | ||
849 | handle_alices_cryptodata_message (void *cls, | ||
850 | const struct AliceCryptodataMessage *msg) | ||
851 | { | ||
852 | struct BobServiceSession *s = cls; | ||
853 | const struct GNUNET_CRYPTO_PaillierCiphertext *payload; | ||
854 | uint32_t contained_elements; | ||
855 | unsigned int max; | ||
856 | |||
857 | contained_elements = ntohl (msg->contained_element_count); | ||
858 | /* Our intersection may still be ongoing, but this is nevertheless | ||
859 | an upper bound on the required array size */ | ||
860 | max = GNUNET_CONTAINER_multihashmap_size (s->intersected_elements); | ||
861 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
862 | "Received %u crypto values from Alice\n", | ||
863 | (unsigned int) contained_elements); | ||
864 | |||
865 | payload = (const struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1]; | ||
866 | if (NULL == s->e_a) | ||
867 | s->e_a = GNUNET_new_array (max, | ||
868 | struct GNUNET_CRYPTO_PaillierCiphertext); | ||
869 | GNUNET_memcpy (&s->e_a[s->cadet_received_element_count], | ||
870 | payload, | ||
871 | sizeof(struct GNUNET_CRYPTO_PaillierCiphertext) | ||
872 | * contained_elements); | ||
873 | s->cadet_received_element_count += contained_elements; | ||
874 | |||
875 | if ((s->cadet_received_element_count == max) && | ||
876 | (NULL == s->intersection_op)) | ||
877 | { | ||
878 | /* intersection has finished also on our side, and | ||
879 | we got the full set, so we can proceed with the | ||
880 | CADET response(s) */ | ||
881 | transmit_cryptographic_reply (s); | ||
882 | } | ||
883 | GNUNET_CADET_receive_done (s->channel); | ||
884 | } | ||
885 | |||
886 | |||
887 | /** | ||
888 | * Callback for set operation results. Called for each element | ||
889 | * that needs to be removed from the result set. | ||
890 | * | ||
891 | * @param cls closure with the `struct BobServiceSession` | ||
892 | * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK | ||
893 | * @param current_size current set size | ||
894 | * @param status what has happened with the set intersection? | ||
895 | */ | ||
896 | static void | ||
897 | cb_intersection_element_removed (void *cls, | ||
898 | const struct GNUNET_SETI_Element *element, | ||
899 | uint64_t current_size, | ||
900 | enum GNUNET_SETI_Status status) | ||
901 | { | ||
902 | struct BobServiceSession *s = cls; | ||
903 | struct GNUNET_SCALARPRODUCT_Element *se; | ||
904 | |||
905 | switch (status) | ||
906 | { | ||
907 | case GNUNET_SETI_STATUS_DEL_LOCAL: | ||
908 | /* this element has been removed from the set */ | ||
909 | se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements, | ||
910 | element->data); | ||
911 | GNUNET_assert (NULL != se); | ||
912 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
913 | "Removed element with key %s and value %lld\n", | ||
914 | GNUNET_h2s (&se->key), | ||
915 | (long long) GNUNET_ntohll (se->value)); | ||
916 | GNUNET_assert (GNUNET_YES == | ||
917 | GNUNET_CONTAINER_multihashmap_remove ( | ||
918 | s->intersected_elements, | ||
919 | element->data, | ||
920 | se)); | ||
921 | GNUNET_free (se); | ||
922 | return; | ||
923 | case GNUNET_SETI_STATUS_DONE: | ||
924 | s->intersection_op = NULL; | ||
925 | GNUNET_break (NULL == s->intersection_set); | ||
926 | GNUNET_CADET_receive_done (s->channel); | ||
927 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
928 | "Finished intersection, %d items remain\n", | ||
929 | GNUNET_CONTAINER_multihashmap_size (s->intersected_elements)); | ||
930 | if (s->client_received_element_count == | ||
931 | GNUNET_CONTAINER_multihashmap_size (s->intersected_elements)) | ||
932 | { | ||
933 | /* CADET transmission from Alice is also already done, | ||
934 | start with our own reply */ | ||
935 | transmit_cryptographic_reply (s); | ||
936 | } | ||
937 | return; | ||
938 | case GNUNET_SETI_STATUS_FAILURE: | ||
939 | /* unhandled status code */ | ||
940 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
941 | "Set intersection failed!\n"); | ||
942 | s->intersection_op = NULL; | ||
943 | if (NULL != s->intersection_set) | ||
944 | { | ||
945 | GNUNET_SETI_destroy (s->intersection_set); | ||
946 | s->intersection_set = NULL; | ||
947 | } | ||
948 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
949 | prepare_client_end_notification (s); | ||
950 | return; | ||
951 | default: | ||
952 | GNUNET_break (0); | ||
953 | return; | ||
954 | } | ||
955 | } | ||
956 | |||
957 | |||
958 | /** | ||
959 | * We've paired up a client session with an incoming CADET request. | ||
960 | * Initiate set intersection work. | ||
961 | * | ||
962 | * @param s client session to start intersection for | ||
963 | */ | ||
964 | static void | ||
965 | start_intersection (struct BobServiceSession *s) | ||
966 | { | ||
967 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
968 | "Got session with key %s and %u elements, starting intersection.\n", | ||
969 | GNUNET_h2s (&s->session_id), | ||
970 | (unsigned int) s->total); | ||
971 | |||
972 | s->intersection_op | ||
973 | = GNUNET_SETI_prepare (&s->peer, | ||
974 | &s->session_id, | ||
975 | NULL, | ||
976 | (struct GNUNET_SETI_Option[]) { { 0 } }, | ||
977 | &cb_intersection_element_removed, | ||
978 | s); | ||
979 | if (GNUNET_OK != | ||
980 | GNUNET_SETI_commit (s->intersection_op, | ||
981 | s->intersection_set)) | ||
982 | { | ||
983 | GNUNET_break (0); | ||
984 | s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; | ||
985 | prepare_client_end_notification (s); | ||
986 | return; | ||
987 | } | ||
988 | GNUNET_SETI_destroy (s->intersection_set); | ||
989 | s->intersection_set = NULL; | ||
990 | } | ||
991 | |||
992 | |||
993 | /** | ||
994 | * Handle a request from Alice to calculate a scalarproduct with us (Bob). | ||
995 | * | ||
996 | * @param cls the `struct BobServiceSession *` | ||
997 | * @param msg the actual message | ||
998 | */ | ||
999 | static void | ||
1000 | handle_alices_computation_request (void *cls, | ||
1001 | const struct ServiceRequestMessage *msg) | ||
1002 | { | ||
1003 | struct BobServiceSession *s = cls; | ||
1004 | |||
1005 | s->session_id = msg->session_id; // ?? | ||
1006 | s->remote_pubkey = msg->public_key; | ||
1007 | if (s->client_received_element_count == s->total) | ||
1008 | start_intersection (s); | ||
1009 | } | ||
1010 | |||
1011 | |||
1012 | /** | ||
1013 | * Function called for inbound channels on Bob's end. Does some | ||
1014 | * preliminary initialization, more happens after we get Alice's first | ||
1015 | * message. | ||
1016 | * | ||
1017 | * @param cls closure with the `struct BobServiceSession` | ||
1018 | * @param channel new handle to the channel | ||
1019 | * @param initiator peer that started the channel | ||
1020 | * @return session associated with the channel | ||
1021 | */ | ||
1022 | static void * | ||
1023 | cb_channel_incoming (void *cls, | ||
1024 | struct GNUNET_CADET_Channel *channel, | ||
1025 | const struct GNUNET_PeerIdentity *initiator) | ||
1026 | { | ||
1027 | struct BobServiceSession *s = cls; | ||
1028 | |||
1029 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1030 | "New incoming channel from peer %s.\n", | ||
1031 | GNUNET_i2s (initiator)); | ||
1032 | GNUNET_CADET_close_port (s->port); | ||
1033 | s->port = NULL; | ||
1034 | s->channel = channel; | ||
1035 | s->peer = *initiator; | ||
1036 | s->cadet_mq = GNUNET_CADET_get_mq (s->channel); | ||
1037 | return s; | ||
1038 | } | ||
1039 | |||
1040 | |||
1041 | /** | ||
1042 | * We're receiving additional set data. Check it is well-formed. | ||
1043 | * | ||
1044 | * @param cls identification of the client | ||
1045 | * @param msg the actual message | ||
1046 | * @return #GNUNET_OK if @a msg is well-formed | ||
1047 | */ | ||
1048 | static int | ||
1049 | check_bob_client_message_multipart (void *cls, | ||
1050 | const struct | ||
1051 | ComputationBobCryptodataMultipartMessage * | ||
1052 | msg) | ||
1053 | { | ||
1054 | struct BobServiceSession *s = cls; | ||
1055 | uint32_t contained_count; | ||
1056 | uint16_t msize; | ||
1057 | |||
1058 | msize = ntohs (msg->header.size); | ||
1059 | contained_count = ntohl (msg->element_count_contained); | ||
1060 | if ((msize != (sizeof(struct ComputationBobCryptodataMultipartMessage) | ||
1061 | + contained_count * sizeof(struct | ||
1062 | GNUNET_SCALARPRODUCT_Element))) || | ||
1063 | (0 == contained_count) || | ||
1064 | (UINT16_MAX < contained_count) || | ||
1065 | (s->total == s->client_received_element_count) || | ||
1066 | (s->total < s->client_received_element_count + contained_count)) | ||
1067 | { | ||
1068 | GNUNET_break (0); | ||
1069 | return GNUNET_SYSERR; | ||
1070 | } | ||
1071 | return GNUNET_OK; | ||
1072 | } | ||
1073 | |||
1074 | |||
1075 | /** | ||
1076 | * We're receiving additional set data. Add it to our | ||
1077 | * set and if we are done, initiate the transaction. | ||
1078 | * | ||
1079 | * @param cls identification of the client | ||
1080 | * @param msg the actual message | ||
1081 | */ | ||
1082 | static void | ||
1083 | handle_bob_client_message_multipart (void *cls, | ||
1084 | const struct | ||
1085 | ComputationBobCryptodataMultipartMessage * | ||
1086 | msg) | ||
1087 | { | ||
1088 | struct BobServiceSession *s = cls; | ||
1089 | uint32_t contained_count; | ||
1090 | const struct GNUNET_SCALARPRODUCT_Element *elements; | ||
1091 | struct GNUNET_SETI_Element set_elem; | ||
1092 | struct GNUNET_SCALARPRODUCT_Element *elem; | ||
1093 | |||
1094 | contained_count = ntohl (msg->element_count_contained); | ||
1095 | elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1]; | ||
1096 | for (uint32_t i = 0; i < contained_count; i++) | ||
1097 | { | ||
1098 | elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element); | ||
1099 | GNUNET_memcpy (elem, | ||
1100 | &elements[i], | ||
1101 | sizeof(struct GNUNET_SCALARPRODUCT_Element)); | ||
1102 | if (GNUNET_SYSERR == | ||
1103 | GNUNET_CONTAINER_multihashmap_put (s->intersected_elements, | ||
1104 | &elem->key, | ||
1105 | elem, | ||
1106 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) | ||
1107 | { | ||
1108 | GNUNET_break (0); | ||
1109 | GNUNET_free (elem); | ||
1110 | continue; | ||
1111 | } | ||
1112 | set_elem.data = &elem->key; | ||
1113 | set_elem.size = sizeof(elem->key); | ||
1114 | set_elem.element_type = 0; | ||
1115 | GNUNET_SETI_add_element (s->intersection_set, | ||
1116 | &set_elem, | ||
1117 | NULL, NULL); | ||
1118 | } | ||
1119 | s->client_received_element_count += contained_count; | ||
1120 | GNUNET_SERVICE_client_continue (s->client); | ||
1121 | if (s->total != s->client_received_element_count) | ||
1122 | { | ||
1123 | /* more to come */ | ||
1124 | return; | ||
1125 | } | ||
1126 | if (NULL == s->channel) | ||
1127 | { | ||
1128 | /* no Alice waiting for this request, wait for Alice */ | ||
1129 | return; | ||
1130 | } | ||
1131 | start_intersection (s); | ||
1132 | } | ||
1133 | |||
1134 | |||
1135 | /** | ||
1136 | * Handler for Bob's a client request message. Check @a msg is | ||
1137 | * well-formed. | ||
1138 | * | ||
1139 | * @param cls identification of the client | ||
1140 | * @param msg the actual message | ||
1141 | * @return #GNUNET_OK if @a msg is well-formed | ||
1142 | */ | ||
1143 | static int | ||
1144 | check_bob_client_message (void *cls, | ||
1145 | const struct BobComputationMessage *msg) | ||
1146 | { | ||
1147 | struct BobServiceSession *s = cls; | ||
1148 | uint32_t contained_count; | ||
1149 | uint32_t total_count; | ||
1150 | uint16_t msize; | ||
1151 | |||
1152 | if (GNUNET_SCALARPRODUCT_STATUS_INIT != s->status) | ||
1153 | { | ||
1154 | GNUNET_break (0); | ||
1155 | return GNUNET_SYSERR; | ||
1156 | } | ||
1157 | msize = ntohs (msg->header.size); | ||
1158 | total_count = ntohl (msg->element_count_total); | ||
1159 | contained_count = ntohl (msg->element_count_contained); | ||
1160 | if ((0 == total_count) || | ||
1161 | (0 == contained_count) || | ||
1162 | (UINT16_MAX < contained_count) || | ||
1163 | (msize != (sizeof(struct BobComputationMessage) | ||
1164 | + contained_count * sizeof(struct | ||
1165 | GNUNET_SCALARPRODUCT_Element)))) | ||
1166 | { | ||
1167 | GNUNET_break_op (0); | ||
1168 | return GNUNET_SYSERR; | ||
1169 | } | ||
1170 | return GNUNET_OK; | ||
1171 | } | ||
1172 | |||
1173 | |||
1174 | /** | ||
1175 | * Handler for Bob's a client request message. Bob is in the response | ||
1176 | * role, keep the values + session and waiting for a matching session | ||
1177 | * or process a waiting request from Alice. | ||
1178 | * | ||
1179 | * @param cls identification of the client | ||
1180 | * @param msg the actual message | ||
1181 | */ | ||
1182 | static void | ||
1183 | handle_bob_client_message (void *cls, | ||
1184 | const struct BobComputationMessage *msg) | ||
1185 | { | ||
1186 | struct BobServiceSession *s = cls; | ||
1187 | struct GNUNET_MQ_MessageHandler cadet_handlers[] = { | ||
1188 | GNUNET_MQ_hd_fixed_size (alices_computation_request, | ||
1189 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SESSION_INITIALIZATION, | ||
1190 | struct ServiceRequestMessage, | ||
1191 | NULL), | ||
1192 | GNUNET_MQ_hd_var_size (alices_cryptodata_message, | ||
1193 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_CRYPTODATA, | ||
1194 | struct AliceCryptodataMessage, | ||
1195 | NULL), | ||
1196 | GNUNET_MQ_handler_end () | ||
1197 | }; | ||
1198 | uint32_t contained_count; | ||
1199 | uint32_t total_count; | ||
1200 | const struct GNUNET_SCALARPRODUCT_Element *elements; | ||
1201 | struct GNUNET_SETI_Element set_elem; | ||
1202 | struct GNUNET_SCALARPRODUCT_Element *elem; | ||
1203 | |||
1204 | total_count = ntohl (msg->element_count_total); | ||
1205 | contained_count = ntohl (msg->element_count_contained); | ||
1206 | |||
1207 | s->status = GNUNET_SCALARPRODUCT_STATUS_ACTIVE; | ||
1208 | s->total = total_count; | ||
1209 | s->client_received_element_count = contained_count; | ||
1210 | s->session_id = msg->session_key; | ||
1211 | elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1]; | ||
1212 | s->intersected_elements | ||
1213 | = GNUNET_CONTAINER_multihashmap_create (s->total, | ||
1214 | GNUNET_YES); | ||
1215 | s->intersection_set = GNUNET_SETI_create (cfg); | ||
1216 | for (uint32_t i = 0; i < contained_count; i++) | ||
1217 | { | ||
1218 | if (0 == GNUNET_ntohll (elements[i].value)) | ||
1219 | continue; | ||
1220 | elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element); | ||
1221 | GNUNET_memcpy (elem, | ||
1222 | &elements[i], | ||
1223 | sizeof(struct GNUNET_SCALARPRODUCT_Element)); | ||
1224 | if (GNUNET_SYSERR == | ||
1225 | GNUNET_CONTAINER_multihashmap_put (s->intersected_elements, | ||
1226 | &elem->key, | ||
1227 | elem, | ||
1228 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) | ||
1229 | { | ||
1230 | GNUNET_break (0); | ||
1231 | GNUNET_free (elem); | ||
1232 | continue; | ||
1233 | } | ||
1234 | set_elem.data = &elem->key; | ||
1235 | set_elem.size = sizeof(elem->key); | ||
1236 | set_elem.element_type = 0; | ||
1237 | GNUNET_SETI_add_element (s->intersection_set, | ||
1238 | &set_elem, | ||
1239 | NULL, NULL); | ||
1240 | s->used_element_count++; | ||
1241 | } | ||
1242 | GNUNET_SERVICE_client_continue (s->client); | ||
1243 | /* We're ready, open the port */ | ||
1244 | s->port = GNUNET_CADET_open_port (my_cadet, | ||
1245 | &msg->session_key, | ||
1246 | &cb_channel_incoming, | ||
1247 | s, | ||
1248 | NULL, | ||
1249 | &cb_channel_destruction, | ||
1250 | cadet_handlers); | ||
1251 | if (NULL == s->port) | ||
1252 | { | ||
1253 | GNUNET_break (0); | ||
1254 | GNUNET_SERVICE_client_drop (s->client); | ||
1255 | return; | ||
1256 | } | ||
1257 | } | ||
1258 | |||
1259 | |||
1260 | /** | ||
1261 | * Task run during shutdown. | ||
1262 | * | ||
1263 | * @param cls unused | ||
1264 | */ | ||
1265 | static void | ||
1266 | shutdown_task (void *cls) | ||
1267 | { | ||
1268 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1269 | "Shutting down, initiating cleanup.\n"); | ||
1270 | // FIXME: we have to cut our connections to CADET first! | ||
1271 | if (NULL != my_cadet) | ||
1272 | { | ||
1273 | GNUNET_CADET_disconnect (my_cadet); | ||
1274 | my_cadet = NULL; | ||
1275 | } | ||
1276 | } | ||
1277 | |||
1278 | |||
1279 | /** | ||
1280 | * A client connected. | ||
1281 | * | ||
1282 | * Setup the associated data structure. | ||
1283 | * | ||
1284 | * @param cls closure, NULL | ||
1285 | * @param client identification of the client | ||
1286 | * @param mq message queue to communicate with @a client | ||
1287 | * @return our `struct BobServiceSession` | ||
1288 | */ | ||
1289 | static void * | ||
1290 | client_connect_cb (void *cls, | ||
1291 | struct GNUNET_SERVICE_Client *client, | ||
1292 | struct GNUNET_MQ_Handle *mq) | ||
1293 | { | ||
1294 | struct BobServiceSession *s; | ||
1295 | |||
1296 | s = GNUNET_new (struct BobServiceSession); | ||
1297 | s->client = client; | ||
1298 | s->client_mq = mq; | ||
1299 | return s; | ||
1300 | } | ||
1301 | |||
1302 | |||
1303 | /** | ||
1304 | * A client disconnected. | ||
1305 | * | ||
1306 | * Remove the associated session(s), release data structures | ||
1307 | * and cancel pending outgoing transmissions to the client. | ||
1308 | * | ||
1309 | * @param cls closure, NULL | ||
1310 | * @param client identification of the client | ||
1311 | * @param app_cls our `struct BobServiceSession` | ||
1312 | */ | ||
1313 | static void | ||
1314 | client_disconnect_cb (void *cls, | ||
1315 | struct GNUNET_SERVICE_Client *client, | ||
1316 | void *app_cls) | ||
1317 | { | ||
1318 | struct BobServiceSession *s = app_cls; | ||
1319 | |||
1320 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1321 | "Client disconnected from us.\n"); | ||
1322 | s->client = NULL; | ||
1323 | destroy_service_session (s); | ||
1324 | } | ||
1325 | |||
1326 | |||
1327 | /** | ||
1328 | * Initialization of the program and message handlers | ||
1329 | * | ||
1330 | * @param cls closure | ||
1331 | * @param c configuration to use | ||
1332 | * @param service the initialized service | ||
1333 | */ | ||
1334 | static void | ||
1335 | run (void *cls, | ||
1336 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
1337 | struct GNUNET_SERVICE_Handle *service) | ||
1338 | { | ||
1339 | cfg = c; | ||
1340 | /* | ||
1341 | offset has to be sufficiently small to allow computation of: | ||
1342 | m1+m2 mod n == (S + a) + (S + b) mod n, | ||
1343 | if we have more complex operations, this factor needs to be lowered */ | ||
1344 | my_offset = gcry_mpi_new (GNUNET_CRYPTO_PAILLIER_BITS / 3); | ||
1345 | gcry_mpi_set_bit (my_offset, | ||
1346 | GNUNET_CRYPTO_PAILLIER_BITS / 3); | ||
1347 | |||
1348 | GNUNET_CRYPTO_paillier_create (&my_pubkey, | ||
1349 | &my_privkey); | ||
1350 | my_cadet = GNUNET_CADET_connect (cfg); | ||
1351 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
1352 | NULL); | ||
1353 | if (NULL == my_cadet) | ||
1354 | { | ||
1355 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1356 | _ ("Connect to CADET failed\n")); | ||
1357 | GNUNET_SCHEDULER_shutdown (); | ||
1358 | return; | ||
1359 | } | ||
1360 | } | ||
1361 | |||
1362 | |||
1363 | /** | ||
1364 | * Define "main" method using service macro. | ||
1365 | */ | ||
1366 | GNUNET_SERVICE_MAIN | ||
1367 | ("scalarproduct-bob", | ||
1368 | GNUNET_SERVICE_OPTION_NONE, | ||
1369 | &run, | ||
1370 | &client_connect_cb, | ||
1371 | &client_disconnect_cb, | ||
1372 | NULL, | ||
1373 | GNUNET_MQ_hd_var_size (bob_client_message, | ||
1374 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB, | ||
1375 | struct BobComputationMessage, | ||
1376 | NULL), | ||
1377 | GNUNET_MQ_hd_var_size (bob_client_message_multipart, | ||
1378 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_BOB, | ||
1379 | struct ComputationBobCryptodataMultipartMessage, | ||
1380 | NULL), | ||
1381 | GNUNET_MQ_handler_end ()); | ||
1382 | |||
1383 | |||
1384 | /* end of gnunet-service-scalarproduct_bob.c */ | ||
diff --git a/src/contrib/service/scalarproduct/meson.build b/src/contrib/service/scalarproduct/meson.build new file mode 100644 index 000000000..296734c2c --- /dev/null +++ b/src/contrib/service/scalarproduct/meson.build | |||
@@ -0,0 +1,102 @@ | |||
1 | libgnunetscalarproduct_src = ['scalarproduct_api.c'] | ||
2 | |||
3 | gnunetservicescalarproducta_src = ['gnunet-service-scalarproduct_alice.c'] | ||
4 | gnunetservicescalarproductb_src = ['gnunet-service-scalarproduct_bob.c'] | ||
5 | gnunetservicescalarproductecca_src = ['gnunet-service-scalarproduct-ecc_alice.c'] | ||
6 | gnunetservicescalarproducteccb_src = ['gnunet-service-scalarproduct-ecc_bob.c'] | ||
7 | |||
8 | configure_file(input : 'scalarproduct.conf.in', | ||
9 | output : 'scalarproduct.conf', | ||
10 | configuration : cdata, | ||
11 | install: true, | ||
12 | install_dir: pkgcfgdir) | ||
13 | |||
14 | |||
15 | if get_option('monolith') | ||
16 | foreach p : libgnunetscalarproduct_src + gnunetservicescalarproducta_src + gnunetservicescalarproductb_src + gnunetservicescalarproductecca_src + gnunetservicescalarproducteccb_src | ||
17 | gnunet_src += 'scalarproduct/' + p | ||
18 | endforeach | ||
19 | subdir_done() | ||
20 | endif | ||
21 | |||
22 | libgnunetscalarproduct = library('gnunetscalarproduct', | ||
23 | libgnunetscalarproduct_src, | ||
24 | soversion: '0', | ||
25 | version: '0.0.0', | ||
26 | dependencies: [libgnunetutil_dep, | ||
27 | gcrypt_dep], | ||
28 | include_directories: [incdir, configuration_inc], | ||
29 | install: true, | ||
30 | install_dir: get_option('libdir')) | ||
31 | pkg.generate(libgnunetscalarproduct, url: 'https://www.gnunet.org', | ||
32 | description : 'Provides API for accessing the scalarproduct service') | ||
33 | libgnunetscalarproduct_dep = declare_dependency(link_with : libgnunetscalarproduct) | ||
34 | |||
35 | executable ('gnunet-scalarproduct', | ||
36 | ['gnunet-scalarproduct.c'], | ||
37 | dependencies: [libgnunetscalarproduct_dep, | ||
38 | libgnunetutil_dep, | ||
39 | gcrypt_dep, | ||
40 | libgnunetstatistics_dep, | ||
41 | libgnunetcore_dep, | ||
42 | libgnunetcadet_dep, | ||
43 | libgnunetblock_dep], | ||
44 | include_directories: [incdir, configuration_inc], | ||
45 | install: true, | ||
46 | install_dir: get_option('bindir')) | ||
47 | |||
48 | executable ('gnunet-service-scalarproduct-alice', | ||
49 | gnunetservicescalarproducta_src, | ||
50 | dependencies: [libgnunetscalarproduct_dep, | ||
51 | libgnunetutil_dep, | ||
52 | gcrypt_dep, | ||
53 | libgnunetseti_dep, | ||
54 | libgnunetstatistics_dep, | ||
55 | libgnunetcore_dep, | ||
56 | libgnunetcadet_dep, | ||
57 | libgnunetblock_dep], | ||
58 | include_directories: [incdir, configuration_inc], | ||
59 | install: true, | ||
60 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
61 | executable ('gnunet-service-scalarproduct-bob', | ||
62 | gnunetservicescalarproductb_src, | ||
63 | dependencies: [libgnunetscalarproduct_dep, | ||
64 | libgnunetutil_dep, | ||
65 | gcrypt_dep, | ||
66 | libgnunetseti_dep, | ||
67 | libgnunetstatistics_dep, | ||
68 | libgnunetcore_dep, | ||
69 | libgnunetcadet_dep, | ||
70 | libgnunetblock_dep], | ||
71 | include_directories: [incdir, configuration_inc], | ||
72 | install: true, | ||
73 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
74 | executable ('gnunet-service-scalarproduct-ecc-alice', | ||
75 | gnunetservicescalarproductecca_src, | ||
76 | dependencies: [libgnunetscalarproduct_dep, | ||
77 | libgnunetutil_dep, | ||
78 | libgnunetstatistics_dep, | ||
79 | libgnunetcore_dep, | ||
80 | gcrypt_dep, | ||
81 | sodium_dep, | ||
82 | libgnunetseti_dep, | ||
83 | libgnunetcadet_dep, | ||
84 | libgnunetblock_dep], | ||
85 | include_directories: [incdir, configuration_inc], | ||
86 | install: true, | ||
87 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
88 | executable ('gnunet-service-scalarproduct-ecc-bob', | ||
89 | gnunetservicescalarproducteccb_src, | ||
90 | dependencies: [libgnunetscalarproduct_dep, | ||
91 | libgnunetutil_dep, | ||
92 | libgnunetstatistics_dep, | ||
93 | libgnunetcore_dep, | ||
94 | gcrypt_dep, | ||
95 | sodium_dep, | ||
96 | libgnunetseti_dep, | ||
97 | libgnunetcadet_dep, | ||
98 | libgnunetblock_dep], | ||
99 | include_directories: [incdir, configuration_inc], | ||
100 | install: true, | ||
101 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
102 | |||
diff --git a/src/contrib/service/scalarproduct/perf_scalarproduct.sh b/src/contrib/service/scalarproduct/perf_scalarproduct.sh new file mode 100755 index 000000000..b15465c9a --- /dev/null +++ b/src/contrib/service/scalarproduct/perf_scalarproduct.sh | |||
@@ -0,0 +1,73 @@ | |||
1 | #!/bin/bash | ||
2 | # Computes a simple scalar product, with configurable vector size. | ||
3 | # | ||
4 | # Some results (wall-clock for Alice+Bob, single-core, i7, libgcrypt): | ||
5 | # SIZE 2048-H(s) 2048-O(s) 1024-O(s) ECC-2^20-H(s) ECC-2^28-H(s) | ||
6 | # 25 10 14 3 2 29 | ||
7 | # 50 17 21 5 2 29 | ||
8 | # 100 32 39 7 2 29 | ||
9 | # 200 77 13 3 30 | ||
10 | # 400 149 23 OOR 31 | ||
11 | # 800 304 32 OOR 33 | ||
12 | |||
13 | # Bandwidth (including set intersection): | ||
14 | # RSA-1024 RSA-2048 ECC | ||
15 | # 800: 629 kb 1234 kb 65 kb | ||
16 | # | ||
17 | # LIBSODIUM, AMD Threadripper 1950: | ||
18 | # | ||
19 | # SIZE 2048-O(s) 1024-O(s) ECC-2^20-H(s) ECC-2^28-H(s) | ||
20 | # 25 4.3 0.7 0.129 4.233 | ||
21 | # 50 7.7 1.2 0.143 4.267 | ||
22 | # 100 10.3 2.4 0.163 4.282 | ||
23 | # 200 19.8 3.0 0.192 4.326 | ||
24 | # 400 35.9 6.0 0.253 4.358 | ||
25 | # 800 73.7 12.6 0.379 4.533 | ||
26 | |||
27 | # | ||
28 | # | ||
29 | # Configure benchmark size: | ||
30 | SIZE=800 | ||
31 | # | ||
32 | # Construct input vectors: | ||
33 | INPUTALICE="-k CCC -e '" | ||
34 | INPUTBOB="-k CCC -e '" | ||
35 | for X in `seq 1 $SIZE` | ||
36 | do | ||
37 | INPUTALICE="${INPUTALICE}A${X},$X;" | ||
38 | INPUTBOB="${INPUTBOB}A${X},$X;" | ||
39 | done | ||
40 | INPUTALICE="${INPUTALICE}BC,-20000;RO,1000;FL,100;LOL,24;'" | ||
41 | INPUTBOB="${INPUTBOB}AB,10;RO,3;FL,3;LOL,-1;'" | ||
42 | |||
43 | # necessary to make the testing prefix deterministic, so we can access the config files | ||
44 | PREFIX=/tmp/test-scalarproduct`date +%H%M%S` | ||
45 | |||
46 | # where can we find the peers config files? | ||
47 | CFGALICE="-c $PREFIX/0/config" | ||
48 | CFGBOB="-c $PREFIX/1/config" | ||
49 | |||
50 | # launch two peers in line topology non-interactively | ||
51 | # | ||
52 | # interactive mode would terminate the test immediately | ||
53 | # because the rest of the script is already in stdin, | ||
54 | # thus redirecting stdin does not suffice) | ||
55 | #GNUNET_FORCE_LOG=';;;;ERROR' | ||
56 | #GNUNET_FORCE_LOG='scalarproduct*;;;;DEBUG/cadet-api*;;;;DEBUG' | ||
57 | GNUNET_TESTING_PREFIX=$PREFIX ../testbed/gnunet-testbed-profiler -n -c test_scalarproduct.conf -p 2 & | ||
58 | PID=$! | ||
59 | # sleep 1 is too short on most systems, 2 works on most, 5 seems to be safe | ||
60 | echo "Waiting for peers to start..." | ||
61 | sleep 5 | ||
62 | # get Bob's peer ID, necessary for Alice | ||
63 | PEERIDBOB=`gnunet-peerinfo -qs $CFGBOB` | ||
64 | |||
65 | echo "Running problem of size $SIZE" | ||
66 | gnunet-scalarproduct $CFGBOB $INPUTBOB & | ||
67 | time RESULT=`gnunet-scalarproduct $CFGALICE $INPUTALICE -p $PEERIDBOB` | ||
68 | gnunet-statistics $CFGALICE -s core | grep "bytes encrypted" | ||
69 | gnunet-statistics $CFGBOB -s core | grep "bytes encrypted" | ||
70 | |||
71 | echo "Terminating testbed..." | ||
72 | # terminate the testbed | ||
73 | kill $PID | ||
diff --git a/src/contrib/service/scalarproduct/scalarproduct.conf.in b/src/contrib/service/scalarproduct/scalarproduct.conf.in new file mode 100644 index 000000000..e2286b076 --- /dev/null +++ b/src/contrib/service/scalarproduct/scalarproduct.conf.in | |||
@@ -0,0 +1,27 @@ | |||
1 | [scalarproduct-alice] | ||
2 | START_ON_DEMAND = @START_ON_DEMAND@ | ||
3 | BINARY = gnunet-service-scalarproduct-ecc-alice | ||
4 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-scalarproduct-alice.sock | ||
5 | @UNIXONLY@ PORT = 2117 | ||
6 | #ACCEPT_FROM = 127.0.0.1; | ||
7 | #ACCEPT_FROM6 = ::1; | ||
8 | UNIX_MATCH_UID = NO | ||
9 | UNIX_MATCH_GID = YES | ||
10 | #OPTIONS = -L DEBUG | ||
11 | #PREFIX = valgrind | ||
12 | |||
13 | |||
14 | [scalarproduct-bob] | ||
15 | START_ON_DEMAND = @START_ON_DEMAND@ | ||
16 | HOSTNAME = localhost | ||
17 | BINARY = gnunet-service-scalarproduct-ecc-bob | ||
18 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-scalarproduct-bob.sock | ||
19 | @UNIXONLY@ PORT = 2118 | ||
20 | |||
21 | #ACCEPT_FROM = 127.0.0.1; | ||
22 | #ACCEPT_FROM6 = ::1; | ||
23 | UNIX_MATCH_UID = NO | ||
24 | UNIX_MATCH_GID = YES | ||
25 | #OPTIONS = -L DEBUG | ||
26 | |||
27 | #PREFIX = valgrind | ||
diff --git a/src/contrib/service/scalarproduct/scalarproduct.h b/src/contrib/service/scalarproduct/scalarproduct.h new file mode 100644 index 000000000..f2311cda0 --- /dev/null +++ b/src/contrib/service/scalarproduct/scalarproduct.h | |||
@@ -0,0 +1,177 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2010, 2011, 2012, 2013 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.h | ||
22 | * @brief Scalar Product API Message Types | ||
23 | * @author Christian M. Fuchs | ||
24 | */ | ||
25 | #ifndef SCALARPRODUCT_H | ||
26 | #define SCALARPRODUCT_H | ||
27 | |||
28 | GNUNET_NETWORK_STRUCT_BEGIN | ||
29 | |||
30 | /** | ||
31 | * Log an error message at log-level 'level' that indicates | ||
32 | * a failure of the command 'cmd' with the message given | ||
33 | * by gcry_strerror(rc). | ||
34 | */ | ||
35 | #define LOG_GCRY(level, cmd, rc) do { LOG (level, _ ( \ | ||
36 | "`%s' failed at %s:%d with error: %s\n"), \ | ||
37 | cmd, __FILE__, __LINE__, \ | ||
38 | gcry_strerror (rc)); } while (0) | ||
39 | |||
40 | |||
41 | /** | ||
42 | * Message type passed from client to service | ||
43 | * to initiate a request or responder role | ||
44 | */ | ||
45 | struct AliceComputationMessage | ||
46 | { | ||
47 | /** | ||
48 | * GNUNET message header with type | ||
49 | * #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE | ||
50 | */ | ||
51 | struct GNUNET_MessageHeader header; | ||
52 | |||
53 | /** | ||
54 | * how many elements the vector in payload contains | ||
55 | */ | ||
56 | uint32_t element_count_total GNUNET_PACKED; | ||
57 | |||
58 | /** | ||
59 | * contained elements the vector in payload contains | ||
60 | */ | ||
61 | uint32_t element_count_contained GNUNET_PACKED; | ||
62 | |||
63 | /** | ||
64 | * Always zero. | ||
65 | */ | ||
66 | uint32_t reserved GNUNET_PACKED; | ||
67 | |||
68 | /** | ||
69 | * the transaction/session key used to identify a session | ||
70 | */ | ||
71 | struct GNUNET_HashCode session_key; | ||
72 | |||
73 | /** | ||
74 | * the identity of a remote peer we want to communicate with | ||
75 | */ | ||
76 | struct GNUNET_PeerIdentity peer; | ||
77 | |||
78 | /** | ||
79 | * followed by struct GNUNET_SCALARPRODUCT_Element[] | ||
80 | */ | ||
81 | }; | ||
82 | |||
83 | |||
84 | /** | ||
85 | * Message type passed from client to service | ||
86 | * to initiate a request or responder role | ||
87 | */ | ||
88 | struct BobComputationMessage | ||
89 | { | ||
90 | /** | ||
91 | * GNUNET message header with type | ||
92 | * #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB | ||
93 | */ | ||
94 | struct GNUNET_MessageHeader header; | ||
95 | |||
96 | /** | ||
97 | * how many elements the vector in payload contains | ||
98 | */ | ||
99 | uint32_t element_count_total GNUNET_PACKED; | ||
100 | |||
101 | /** | ||
102 | * contained elements the vector in payload contains | ||
103 | */ | ||
104 | uint32_t element_count_contained GNUNET_PACKED; | ||
105 | |||
106 | /** | ||
107 | * Always zero. | ||
108 | */ | ||
109 | uint32_t reserved GNUNET_PACKED; | ||
110 | |||
111 | /** | ||
112 | * the transaction/session key used to identify a session | ||
113 | */ | ||
114 | struct GNUNET_HashCode session_key; | ||
115 | |||
116 | /** | ||
117 | * followed by struct GNUNET_SCALARPRODUCT_Element[] | ||
118 | */ | ||
119 | }; | ||
120 | |||
121 | |||
122 | /** | ||
123 | * multipart messages following `struct ComputationMessage` | ||
124 | */ | ||
125 | struct ComputationBobCryptodataMultipartMessage | ||
126 | { | ||
127 | /** | ||
128 | * GNUNET message header | ||
129 | */ | ||
130 | struct GNUNET_MessageHeader header; | ||
131 | |||
132 | /** | ||
133 | * contained elements the vector in payload contains | ||
134 | */ | ||
135 | uint32_t element_count_contained GNUNET_PACKED; | ||
136 | |||
137 | /** | ||
138 | * followed by struct GNUNET_SCALARPRODUCT_Element[] | ||
139 | */ | ||
140 | }; | ||
141 | |||
142 | |||
143 | /** | ||
144 | * Message type passed from service client | ||
145 | * to finalize a session as requester or responder | ||
146 | */ | ||
147 | struct ClientResponseMessage | ||
148 | { | ||
149 | /** | ||
150 | * GNUNET message header | ||
151 | */ | ||
152 | struct GNUNET_MessageHeader header; | ||
153 | |||
154 | /** | ||
155 | * 0 if no product attached | ||
156 | */ | ||
157 | uint32_t product_length GNUNET_PACKED; | ||
158 | |||
159 | /** | ||
160 | * Status information about the outcome of this session, | ||
161 | * An `enum GNUNET_SCALARPRODUCT_ResponseStatus` (in NBO). | ||
162 | */ | ||
163 | uint32_t status GNUNET_PACKED; | ||
164 | |||
165 | /** | ||
166 | * Workaround for libgcrypt: -1 if negative, 0 if zero, else 1 | ||
167 | */ | ||
168 | int32_t range GNUNET_PACKED; | ||
169 | |||
170 | /** | ||
171 | * followed by product of length product_length (or nothing) | ||
172 | */ | ||
173 | }; | ||
174 | |||
175 | GNUNET_NETWORK_STRUCT_END | ||
176 | |||
177 | #endif /* SCALARPRODUCT_H */ | ||
diff --git a/src/contrib/service/scalarproduct/scalarproduct_api.c b/src/contrib/service/scalarproduct/scalarproduct_api.c new file mode 100644 index 000000000..8c667a72e --- /dev/null +++ b/src/contrib/service/scalarproduct/scalarproduct_api.c | |||
@@ -0,0 +1,468 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2013, 2014, 2016 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/scalarproduct_api.c | ||
22 | * @brief API for the scalarproduct | ||
23 | * @author Christian Fuchs | ||
24 | * @author Gaurav Kukreja | ||
25 | * @author Christian Grothoff | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_statistics_service.h" | ||
30 | #include "gnunet_scalarproduct_service.h" | ||
31 | #include "gnunet_protocols.h" | ||
32 | #include "scalarproduct.h" | ||
33 | |||
34 | #define LOG(kind, ...) GNUNET_log_from (kind, "scalarproduct-api", __VA_ARGS__) | ||
35 | |||
36 | |||
37 | /** | ||
38 | * The abstraction function for our internal callback | ||
39 | * | ||
40 | * @param h computation handle | ||
41 | * @param msg response we got, NULL on errors | ||
42 | * @param status processing status code | ||
43 | */ | ||
44 | typedef void | ||
45 | (*GNUNET_SCALARPRODUCT_ResponseMessageHandler) ( | ||
46 | struct GNUNET_SCALARPRODUCT_ComputationHandle *h, | ||
47 | const struct ClientResponseMessage *msg, | ||
48 | enum GNUNET_SCALARPRODUCT_ResponseStatus status); | ||
49 | |||
50 | |||
51 | /** | ||
52 | * A handle returned for each computation | ||
53 | */ | ||
54 | struct GNUNET_SCALARPRODUCT_ComputationHandle | ||
55 | { | ||
56 | /** | ||
57 | * Our configuration. | ||
58 | */ | ||
59 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
60 | |||
61 | /** | ||
62 | * Current connection to the scalarproduct service. | ||
63 | */ | ||
64 | struct GNUNET_MQ_Handle *mq; | ||
65 | |||
66 | /** | ||
67 | * Function to call after transmission of the request (Bob). | ||
68 | */ | ||
69 | GNUNET_SCALARPRODUCT_ContinuationWithStatus cont_status; | ||
70 | |||
71 | /** | ||
72 | * Function to call after transmission of the request (Alice). | ||
73 | */ | ||
74 | GNUNET_SCALARPRODUCT_DatumProcessor cont_datum; | ||
75 | |||
76 | /** | ||
77 | * Closure for @e cont_status or @e cont_datum. | ||
78 | */ | ||
79 | void *cont_cls; | ||
80 | |||
81 | /** | ||
82 | * API internal callback for results and failures to be forwarded to | ||
83 | * the client. | ||
84 | */ | ||
85 | GNUNET_SCALARPRODUCT_ResponseMessageHandler response_proc; | ||
86 | |||
87 | /** | ||
88 | * The shared session key identifying this computation | ||
89 | */ | ||
90 | struct GNUNET_HashCode key; | ||
91 | }; | ||
92 | |||
93 | |||
94 | /** | ||
95 | * Called when a response is received from the service. Perform basic | ||
96 | * check that the message is well-formed. | ||
97 | * | ||
98 | * @param cls Pointer to the Master Context | ||
99 | * @param message Pointer to the data received in response | ||
100 | * @return #GNUNET_OK if @a message is well-formed | ||
101 | */ | ||
102 | static int | ||
103 | check_response (void *cls, | ||
104 | const struct ClientResponseMessage *message) | ||
105 | { | ||
106 | if (ntohs (message->header.size) != | ||
107 | ntohl (message->product_length) + sizeof(struct ClientResponseMessage)) | ||
108 | { | ||
109 | GNUNET_break (0); | ||
110 | return GNUNET_SYSERR; | ||
111 | } | ||
112 | return GNUNET_OK; | ||
113 | } | ||
114 | |||
115 | |||
116 | /** | ||
117 | * Handles the STATUS received from the service for a response, does | ||
118 | * not contain a payload. Called when we participate as "Bob" via | ||
119 | * #GNUNET_SCALARPRODUCT_accept_computation(). | ||
120 | * | ||
121 | * @param h our Handle | ||
122 | * @param msg the response received | ||
123 | * @param status the condition the request was terminated with (eg: disconnect) | ||
124 | */ | ||
125 | static void | ||
126 | process_status_message (struct GNUNET_SCALARPRODUCT_ComputationHandle *h, | ||
127 | const struct ClientResponseMessage *msg, | ||
128 | enum GNUNET_SCALARPRODUCT_ResponseStatus status) | ||
129 | { | ||
130 | if (NULL != h->cont_status) | ||
131 | h->cont_status (h->cont_cls, | ||
132 | status); | ||
133 | GNUNET_SCALARPRODUCT_cancel (h); | ||
134 | } | ||
135 | |||
136 | |||
137 | /** | ||
138 | * Called when a response is received from the service. After basic | ||
139 | * check, the handler in `h->response_proc` is called. This functions | ||
140 | * handles the response to the client which used the API. | ||
141 | * | ||
142 | * @param cls Pointer to the Master Context | ||
143 | * @param message Pointer to the data received in response | ||
144 | */ | ||
145 | static void | ||
146 | handle_response (void *cls, | ||
147 | const struct ClientResponseMessage *message) | ||
148 | { | ||
149 | struct GNUNET_SCALARPRODUCT_ComputationHandle *h = cls; | ||
150 | enum GNUNET_SCALARPRODUCT_ResponseStatus status; | ||
151 | |||
152 | status = (enum GNUNET_SCALARPRODUCT_ResponseStatus) ntohl (message->status); | ||
153 | h->response_proc (h, | ||
154 | message, | ||
155 | status); | ||
156 | } | ||
157 | |||
158 | |||
159 | /** | ||
160 | * Check if the keys for all given elements are unique. | ||
161 | * | ||
162 | * @param elements elements to check | ||
163 | * @param element_count size of the @a elements array | ||
164 | * @return #GNUNET_OK if all keys are unique | ||
165 | */ | ||
166 | static int | ||
167 | check_unique (const struct GNUNET_SCALARPRODUCT_Element *elements, | ||
168 | uint32_t element_count) | ||
169 | { | ||
170 | struct GNUNET_CONTAINER_MultiHashMap *map; | ||
171 | int ok; | ||
172 | |||
173 | ok = GNUNET_OK; | ||
174 | map = GNUNET_CONTAINER_multihashmap_create (2 * element_count, | ||
175 | GNUNET_YES); | ||
176 | for (uint32_t i = 0; i < element_count; i++) | ||
177 | if (GNUNET_OK != | ||
178 | GNUNET_CONTAINER_multihashmap_put (map, | ||
179 | &elements[i].key, | ||
180 | map, | ||
181 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) | ||
182 | { | ||
183 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
184 | _ ("Keys given to SCALARPRODUCT not unique!\n")); | ||
185 | ok = GNUNET_SYSERR; | ||
186 | } | ||
187 | GNUNET_CONTAINER_multihashmap_destroy (map); | ||
188 | return ok; | ||
189 | } | ||
190 | |||
191 | |||
192 | /** | ||
193 | * We encountered an error communicating with the set service while | ||
194 | * performing a set operation. Report to the application. | ||
195 | * | ||
196 | * @param cls the `struct GNUNET_SCALARPRODUCT_ComputationHandle` | ||
197 | * @param error error code | ||
198 | */ | ||
199 | static void | ||
200 | mq_error_handler (void *cls, | ||
201 | enum GNUNET_MQ_Error error) | ||
202 | { | ||
203 | struct GNUNET_SCALARPRODUCT_ComputationHandle *h = cls; | ||
204 | |||
205 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
206 | "Disconnected from SCALARPRODUCT service.\n"); | ||
207 | h->response_proc (h, | ||
208 | NULL, | ||
209 | GNUNET_SCALARPRODUCT_STATUS_DISCONNECTED); | ||
210 | } | ||
211 | |||
212 | |||
213 | struct GNUNET_SCALARPRODUCT_ComputationHandle * | ||
214 | GNUNET_SCALARPRODUCT_accept_computation ( | ||
215 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
216 | const struct GNUNET_HashCode *session_key, | ||
217 | const struct GNUNET_SCALARPRODUCT_Element *elements, | ||
218 | uint32_t element_count, | ||
219 | GNUNET_SCALARPRODUCT_ContinuationWithStatus cont, | ||
220 | void *cont_cls) | ||
221 | { | ||
222 | struct GNUNET_SCALARPRODUCT_ComputationHandle *h | ||
223 | = GNUNET_new (struct GNUNET_SCALARPRODUCT_ComputationHandle); | ||
224 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
225 | GNUNET_MQ_hd_var_size (response, | ||
226 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT, | ||
227 | struct ClientResponseMessage, | ||
228 | h), | ||
229 | GNUNET_MQ_handler_end () | ||
230 | }; | ||
231 | struct GNUNET_MQ_Envelope *env; | ||
232 | struct BobComputationMessage *msg; | ||
233 | struct ComputationBobCryptodataMultipartMessage *mmsg; | ||
234 | uint32_t size; | ||
235 | uint16_t possible; | ||
236 | uint16_t todo; | ||
237 | uint32_t element_count_transfered; | ||
238 | |||
239 | |||
240 | if (GNUNET_SYSERR == check_unique (elements, | ||
241 | element_count)) | ||
242 | return NULL; | ||
243 | h->cont_status = cont; | ||
244 | h->cont_cls = cont_cls; | ||
245 | h->response_proc = &process_status_message; | ||
246 | h->cfg = cfg; | ||
247 | h->key = *session_key; | ||
248 | h->mq = GNUNET_CLIENT_connect (cfg, | ||
249 | "scalarproduct-bob", | ||
250 | handlers, | ||
251 | &mq_error_handler, | ||
252 | h); | ||
253 | if (NULL == h->mq) | ||
254 | { | ||
255 | /* scalarproduct configuration error */ | ||
256 | GNUNET_break (0); | ||
257 | GNUNET_free (h); | ||
258 | return NULL; | ||
259 | } | ||
260 | possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(struct | ||
261 | BobComputationMessage)) | ||
262 | / sizeof(struct GNUNET_SCALARPRODUCT_Element); | ||
263 | todo = GNUNET_MIN (possible, | ||
264 | element_count); | ||
265 | size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element); | ||
266 | env = GNUNET_MQ_msg_extra (msg, | ||
267 | size, | ||
268 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB); | ||
269 | msg->element_count_total = htonl (element_count); | ||
270 | msg->element_count_contained = htonl (todo); | ||
271 | msg->session_key = *session_key; | ||
272 | GNUNET_memcpy (&msg[1], | ||
273 | elements, | ||
274 | size); | ||
275 | element_count_transfered = todo; | ||
276 | GNUNET_MQ_send (h->mq, | ||
277 | env); | ||
278 | possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(*mmsg)) | ||
279 | / sizeof(struct GNUNET_SCALARPRODUCT_Element); | ||
280 | while (element_count_transfered < element_count) | ||
281 | { | ||
282 | todo = GNUNET_MIN (possible, | ||
283 | element_count - element_count_transfered); | ||
284 | size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element); | ||
285 | env = GNUNET_MQ_msg_extra (mmsg, | ||
286 | size, | ||
287 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_BOB); | ||
288 | mmsg->element_count_contained = htonl (todo); | ||
289 | GNUNET_memcpy (&mmsg[1], | ||
290 | &elements[element_count_transfered], | ||
291 | size); | ||
292 | element_count_transfered += todo; | ||
293 | GNUNET_MQ_send (h->mq, | ||
294 | env); | ||
295 | } | ||
296 | return h; | ||
297 | } | ||
298 | |||
299 | |||
300 | /** | ||
301 | * Handles the RESULT received from the service for a request, should | ||
302 | * contain a result MPI value. Called when we participate as "Alice" via | ||
303 | * #GNUNET_SCALARPRODUCT_start_computation(). | ||
304 | * | ||
305 | * @param h our Handle | ||
306 | * @param msg Pointer to the response received | ||
307 | * @param status the condition the request was terminated with (eg: disconnect) | ||
308 | */ | ||
309 | static void | ||
310 | process_result_message (struct GNUNET_SCALARPRODUCT_ComputationHandle *h, | ||
311 | const struct ClientResponseMessage *msg, | ||
312 | enum GNUNET_SCALARPRODUCT_ResponseStatus status) | ||
313 | { | ||
314 | uint32_t product_len; | ||
315 | gcry_mpi_t result = NULL; | ||
316 | gcry_error_t rc; | ||
317 | gcry_mpi_t num; | ||
318 | size_t rsize; | ||
319 | |||
320 | if (GNUNET_SCALARPRODUCT_STATUS_SUCCESS == status) | ||
321 | { | ||
322 | result = gcry_mpi_new (0); | ||
323 | |||
324 | product_len = ntohl (msg->product_length); | ||
325 | if (0 < product_len) | ||
326 | { | ||
327 | rsize = 0; | ||
328 | if (0 != (rc = gcry_mpi_scan (&num, GCRYMPI_FMT_STD, | ||
329 | &msg[1], | ||
330 | product_len, | ||
331 | &rsize))) | ||
332 | { | ||
333 | LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, | ||
334 | "gcry_mpi_scan", | ||
335 | rc); | ||
336 | gcry_mpi_release (result); | ||
337 | result = NULL; | ||
338 | status = GNUNET_SCALARPRODUCT_STATUS_INVALID_RESPONSE; | ||
339 | } | ||
340 | else | ||
341 | { | ||
342 | if (0 < (int32_t) ntohl (msg->range)) | ||
343 | gcry_mpi_add (result, result, num); | ||
344 | else | ||
345 | gcry_mpi_sub (result, result, num); | ||
346 | gcry_mpi_release (num); | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | if (NULL != h->cont_datum) | ||
351 | h->cont_datum (h->cont_cls, | ||
352 | status, | ||
353 | result); | ||
354 | if (NULL != result) | ||
355 | gcry_mpi_release (result); | ||
356 | GNUNET_SCALARPRODUCT_cancel (h); | ||
357 | } | ||
358 | |||
359 | |||
360 | struct GNUNET_SCALARPRODUCT_ComputationHandle * | ||
361 | GNUNET_SCALARPRODUCT_start_computation ( | ||
362 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
363 | const struct GNUNET_HashCode *session_key, | ||
364 | const struct GNUNET_PeerIdentity *peer, | ||
365 | const struct GNUNET_SCALARPRODUCT_Element *elements, | ||
366 | uint32_t element_count, | ||
367 | GNUNET_SCALARPRODUCT_DatumProcessor cont, | ||
368 | void *cont_cls) | ||
369 | { | ||
370 | struct GNUNET_SCALARPRODUCT_ComputationHandle *h | ||
371 | = GNUNET_new (struct GNUNET_SCALARPRODUCT_ComputationHandle); | ||
372 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
373 | GNUNET_MQ_hd_var_size (response, | ||
374 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT, | ||
375 | struct ClientResponseMessage, | ||
376 | h), | ||
377 | GNUNET_MQ_handler_end () | ||
378 | }; | ||
379 | struct GNUNET_MQ_Envelope *env; | ||
380 | struct AliceComputationMessage *msg; | ||
381 | struct ComputationBobCryptodataMultipartMessage *mmsg; | ||
382 | uint32_t size; | ||
383 | uint16_t possible; | ||
384 | uint16_t todo; | ||
385 | uint32_t element_count_transfered; | ||
386 | |||
387 | if (GNUNET_SYSERR == check_unique (elements, | ||
388 | element_count)) | ||
389 | return NULL; | ||
390 | h->mq = GNUNET_CLIENT_connect (cfg, | ||
391 | "scalarproduct-alice", | ||
392 | handlers, | ||
393 | &mq_error_handler, | ||
394 | h); | ||
395 | if (NULL == h->mq) | ||
396 | { | ||
397 | /* misconfigured scalarproduct service */ | ||
398 | GNUNET_break (0); | ||
399 | GNUNET_free (h); | ||
400 | return NULL; | ||
401 | } | ||
402 | h->cont_datum = cont; | ||
403 | h->cont_cls = cont_cls; | ||
404 | h->response_proc = &process_result_message; | ||
405 | h->cfg = cfg; | ||
406 | h->key = *session_key; | ||
407 | |||
408 | possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(struct | ||
409 | AliceComputationMessage)) | ||
410 | / sizeof(struct GNUNET_SCALARPRODUCT_Element); | ||
411 | todo = GNUNET_MIN (possible, | ||
412 | element_count); | ||
413 | size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element); | ||
414 | env = GNUNET_MQ_msg_extra (msg, | ||
415 | size, | ||
416 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE); | ||
417 | msg->element_count_total = htonl (element_count); | ||
418 | msg->element_count_contained = htonl (todo); | ||
419 | msg->reserved = htonl (0); | ||
420 | msg->peer = *peer; | ||
421 | msg->session_key = *session_key; | ||
422 | GNUNET_memcpy (&msg[1], | ||
423 | elements, | ||
424 | size); | ||
425 | GNUNET_MQ_send (h->mq, | ||
426 | env); | ||
427 | element_count_transfered = todo; | ||
428 | possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(*mmsg)) | ||
429 | / sizeof(struct GNUNET_SCALARPRODUCT_Element); | ||
430 | while (element_count_transfered < element_count) | ||
431 | { | ||
432 | todo = GNUNET_MIN (possible, | ||
433 | element_count - element_count_transfered); | ||
434 | size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element); | ||
435 | env = GNUNET_MQ_msg_extra (mmsg, | ||
436 | size, | ||
437 | GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_ALICE); | ||
438 | mmsg->element_count_contained = htonl (todo); | ||
439 | GNUNET_memcpy (&mmsg[1], | ||
440 | &elements[element_count_transfered], | ||
441 | size); | ||
442 | element_count_transfered += todo; | ||
443 | GNUNET_MQ_send (h->mq, | ||
444 | env); | ||
445 | } | ||
446 | return h; | ||
447 | } | ||
448 | |||
449 | |||
450 | /** | ||
451 | * Cancel an ongoing computation or revoke our collaboration offer. | ||
452 | * Closes the connection to the service | ||
453 | * | ||
454 | * @param h computation handle to terminate | ||
455 | */ | ||
456 | void | ||
457 | GNUNET_SCALARPRODUCT_cancel (struct GNUNET_SCALARPRODUCT_ComputationHandle *h) | ||
458 | { | ||
459 | if (NULL != h->mq) | ||
460 | { | ||
461 | GNUNET_MQ_destroy (h->mq); | ||
462 | h->mq = NULL; | ||
463 | } | ||
464 | GNUNET_free (h); | ||
465 | } | ||
466 | |||
467 | |||
468 | /* end of scalarproduct_api.c */ | ||
diff --git a/src/contrib/service/scalarproduct/test_ecc_scalarproduct.c b/src/contrib/service/scalarproduct/test_ecc_scalarproduct.c new file mode 100644 index 000000000..85460cb05 --- /dev/null +++ b/src/contrib/service/scalarproduct/test_ecc_scalarproduct.c | |||
@@ -0,0 +1,212 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2015 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 | /** | ||
22 | * @file util/test_ecc_scalarproduct.c | ||
23 | * @brief testcase for math behind ECC SP calculation | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include <gcrypt.h> | ||
29 | |||
30 | /** | ||
31 | * Global context. | ||
32 | */ | ||
33 | static struct GNUNET_CRYPTO_EccDlogContext *edc; | ||
34 | |||
35 | |||
36 | /** | ||
37 | * Perform SP calculation. | ||
38 | * | ||
39 | * @param avec 0-terminated vector of Alice's values | ||
40 | * @param bvec 0-terminated vector of Bob's values | ||
41 | * @return avec * bvec | ||
42 | */ | ||
43 | static int | ||
44 | test_sp (const unsigned int *avec, | ||
45 | const unsigned int *bvec) | ||
46 | { | ||
47 | unsigned int len; | ||
48 | struct GNUNET_CRYPTO_EccScalar a; | ||
49 | struct GNUNET_CRYPTO_EccScalar a_neg; | ||
50 | struct GNUNET_CRYPTO_EccPoint *g; | ||
51 | struct GNUNET_CRYPTO_EccPoint *h; | ||
52 | struct GNUNET_CRYPTO_EccPoint pg; | ||
53 | struct GNUNET_CRYPTO_EccPoint ph; | ||
54 | |||
55 | /* determine length */ | ||
56 | for (len = 0; 0 != avec[len]; len++) | ||
57 | ; | ||
58 | if (0 == len) | ||
59 | return 0; | ||
60 | |||
61 | /* Alice */ | ||
62 | GNUNET_CRYPTO_ecc_rnd_mpi (&a, | ||
63 | &a_neg); | ||
64 | g = GNUNET_new_array (len, | ||
65 | struct GNUNET_CRYPTO_EccPoint); | ||
66 | h = GNUNET_new_array (len, | ||
67 | struct GNUNET_CRYPTO_EccPoint); | ||
68 | for (unsigned int i = 0; i < len; i++) | ||
69 | { | ||
70 | struct GNUNET_CRYPTO_EccScalar tmp; | ||
71 | struct GNUNET_CRYPTO_EccScalar ri; | ||
72 | struct GNUNET_CRYPTO_EccScalar ria; | ||
73 | |||
74 | GNUNET_CRYPTO_ecc_random_mod_n (&ri); | ||
75 | GNUNET_assert (GNUNET_OK == | ||
76 | GNUNET_CRYPTO_ecc_dexp_mpi (&ri, | ||
77 | &g[i])); | ||
78 | /* ria = ri * a mod L, where L is the order of the main subgroup */ | ||
79 | crypto_core_ed25519_scalar_mul (ria.v, | ||
80 | ri.v, | ||
81 | a.v); | ||
82 | /* tmp = ria + avec[i] */ | ||
83 | { | ||
84 | int64_t val = avec[i]; | ||
85 | struct GNUNET_CRYPTO_EccScalar vali; | ||
86 | |||
87 | GNUNET_assert (INT64_MIN != val); | ||
88 | GNUNET_CRYPTO_ecc_scalar_from_int (val > 0 ? val : -val, | ||
89 | &vali); | ||
90 | if (val > 0) | ||
91 | crypto_core_ed25519_scalar_add (tmp.v, | ||
92 | ria.v, | ||
93 | vali.v); | ||
94 | else | ||
95 | crypto_core_ed25519_scalar_sub (tmp.v, | ||
96 | ria.v, | ||
97 | vali.v); | ||
98 | } | ||
99 | /* h[i] = g^tmp = g^{ria + avec[i]} */ | ||
100 | GNUNET_assert (GNUNET_OK == | ||
101 | GNUNET_CRYPTO_ecc_dexp_mpi (&tmp, | ||
102 | &h[i])); | ||
103 | } | ||
104 | |||
105 | /* Bob */ | ||
106 | for (unsigned int i = 0; i < len; i++) | ||
107 | { | ||
108 | struct GNUNET_CRYPTO_EccPoint gm; | ||
109 | struct GNUNET_CRYPTO_EccPoint hm; | ||
110 | |||
111 | { | ||
112 | int64_t val = bvec[i]; | ||
113 | struct GNUNET_CRYPTO_EccScalar vali; | ||
114 | |||
115 | GNUNET_assert (INT64_MIN != val); | ||
116 | GNUNET_CRYPTO_ecc_scalar_from_int (val > 0 ? val : -val, | ||
117 | &vali); | ||
118 | if (val < 0) | ||
119 | crypto_core_ed25519_scalar_negate (vali.v, | ||
120 | vali.v); | ||
121 | /* gm = g[i]^vali */ | ||
122 | GNUNET_assert (GNUNET_OK == | ||
123 | GNUNET_CRYPTO_ecc_pmul_mpi (&g[i], | ||
124 | &vali, | ||
125 | &gm)); | ||
126 | /* hm = h[i]^vali */ | ||
127 | GNUNET_assert (GNUNET_OK == | ||
128 | GNUNET_CRYPTO_ecc_pmul_mpi (&h[i], | ||
129 | &vali, | ||
130 | &hm)); | ||
131 | } | ||
132 | if (0 != i) | ||
133 | { | ||
134 | /* pg += gm */ | ||
135 | GNUNET_assert (GNUNET_OK == | ||
136 | GNUNET_CRYPTO_ecc_add (&gm, | ||
137 | &pg, | ||
138 | &pg)); | ||
139 | /* ph += hm */ | ||
140 | GNUNET_assert (GNUNET_OK == | ||
141 | GNUNET_CRYPTO_ecc_add (&hm, | ||
142 | &ph, | ||
143 | &ph)); | ||
144 | } | ||
145 | else | ||
146 | { | ||
147 | pg = gm; | ||
148 | ph = hm; | ||
149 | } | ||
150 | } | ||
151 | GNUNET_free (g); | ||
152 | GNUNET_free (h); | ||
153 | |||
154 | /* Alice */ | ||
155 | { | ||
156 | struct GNUNET_CRYPTO_EccPoint pgi; | ||
157 | struct GNUNET_CRYPTO_EccPoint gsp; | ||
158 | |||
159 | /* pgi = pg^inv */ | ||
160 | GNUNET_assert (GNUNET_OK == | ||
161 | GNUNET_CRYPTO_ecc_pmul_mpi (&pg, | ||
162 | &a_neg, | ||
163 | &pgi)); | ||
164 | /* gsp = pgi + ph */ | ||
165 | GNUNET_assert (GNUNET_OK == | ||
166 | GNUNET_CRYPTO_ecc_add (&pgi, | ||
167 | &ph, | ||
168 | &gsp)); | ||
169 | return GNUNET_CRYPTO_ecc_dlog (edc, | ||
170 | &gsp); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | |||
175 | /** | ||
176 | * Macro that checks that @a want is equal to @a have and | ||
177 | * if not returns with a failure code. | ||
178 | */ | ||
179 | #define CHECK(want,have) do { \ | ||
180 | if (want != have) { \ | ||
181 | GNUNET_break (0); \ | ||
182 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \ | ||
183 | "Wanted %d, got %d\n", want, have); \ | ||
184 | GNUNET_CRYPTO_ecc_dlog_release (edc); \ | ||
185 | return 1; \ | ||
186 | } } while (0) | ||
187 | |||
188 | |||
189 | int | ||
190 | main (int argc, char *argv[]) | ||
191 | { | ||
192 | static unsigned int v11[] = { 1, 1, 0 }; | ||
193 | static unsigned int v22[] = { 2, 2, 0 }; | ||
194 | static unsigned int v35[] = { 3, 5, 0 }; | ||
195 | static unsigned int v24[] = { 2, 4, 0 }; | ||
196 | |||
197 | GNUNET_log_setup ("test-ecc-scalarproduct", | ||
198 | "WARNING", | ||
199 | NULL); | ||
200 | edc = GNUNET_CRYPTO_ecc_dlog_prepare (128, 128); | ||
201 | CHECK (2, test_sp (v11, v11)); | ||
202 | CHECK (4, test_sp (v22, v11)); | ||
203 | CHECK (8, test_sp (v35, v11)); | ||
204 | CHECK (26, test_sp (v35, v24)); | ||
205 | CHECK (26, test_sp (v24, v35)); | ||
206 | CHECK (16, test_sp (v22, v35)); | ||
207 | GNUNET_CRYPTO_ecc_dlog_release (edc); | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | |||
212 | /* end of test_ecc_scalarproduct.c */ | ||
diff --git a/src/contrib/service/scalarproduct/test_scalarproduct.conf b/src/contrib/service/scalarproduct/test_scalarproduct.conf new file mode 100644 index 000000000..c7e2b55f5 --- /dev/null +++ b/src/contrib/service/scalarproduct/test_scalarproduct.conf | |||
@@ -0,0 +1,23 @@ | |||
1 | @INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf | ||
2 | |||
3 | [PATHS] | ||
4 | GNUNET_TEST_HOME = $GNUNET_TMP/test-scalarproduct/ | ||
5 | |||
6 | [testbed] | ||
7 | OVERLAY_TOPOLOGY = CLIQUE | ||
8 | |||
9 | [nse] | ||
10 | WORKBITS=0 | ||
11 | |||
12 | [scalarproduct-bob] | ||
13 | #PREFIX = valgrind --leak-check=yes | ||
14 | |||
15 | [scalarproduct-alice] | ||
16 | #PREFIX = valgrind --leak-check=yes | ||
17 | |||
18 | [ats] | ||
19 | WAN_QUOTA_IN = unlimited | ||
20 | WAN_QUOTA_OUT = unlimited | ||
21 | |||
22 | [peerinfo] | ||
23 | USE_INCLUDED_HELLOS = NO | ||
diff --git a/src/contrib/service/scalarproduct/test_scalarproduct.sh b/src/contrib/service/scalarproduct/test_scalarproduct.sh new file mode 100755 index 000000000..ed91cd5c4 --- /dev/null +++ b/src/contrib/service/scalarproduct/test_scalarproduct.sh | |||
@@ -0,0 +1,48 @@ | |||
1 | #!/bin/bash | ||
2 | # compute a simple scalar product | ||
3 | # payload for this test: | ||
4 | INPUTALICE="-k CCC -e 'AB,10;RO,3;FL,3;LOL,-1;'" | ||
5 | INPUTBOB="-k CCC -e 'BC,-20000;RO,1000;FL,100;LOL,24;'" | ||
6 | EXPECTED="0CCC" | ||
7 | |||
8 | # necessary to make the testing prefix deterministic, so we can access the config files | ||
9 | PREFIX=/tmp/test-scalarproduct`date +%H%M%S` | ||
10 | |||
11 | # where can we find the peers config files? | ||
12 | CFGALICE="-c $PREFIX/0/config" | ||
13 | CFGBOB="-c $PREFIX/1/config" | ||
14 | |||
15 | # launch two peers in line topology non-interactively | ||
16 | # | ||
17 | # interactive mode would terminate the test immediately | ||
18 | # because the rest of the script is already in stdin, | ||
19 | # thus redirecting stdin does not suffice) | ||
20 | # GNUNET_FORCE_LOG='scalarproduct*;;;;DEBUG' | ||
21 | GNUNET_TESTING_PREFIX=$PREFIX ../testbed/gnunet-testbed-profiler -n -c test_scalarproduct.conf -p 2 & | ||
22 | PID=$! | ||
23 | # sleep 1 is too short on most systems, 2 works on most, 5 seems to be safe | ||
24 | echo "Waiting for peers to start..." | ||
25 | sleep 5 | ||
26 | echo "Running test..." | ||
27 | |||
28 | which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 15" | ||
29 | |||
30 | # get bob's peer ID, necessary for alice | ||
31 | PEERIDBOB=`${DO_TIMEOUT} gnunet-peerinfo -qs $CFGBOB` | ||
32 | |||
33 | #GNUNET_LOG=';;;;DEBUG' | ||
34 | ${DO_TIMEOUT} gnunet-scalarproduct $CFGBOB $INPUTBOB & | ||
35 | #GNUNET_LOG=';;;;DEBUG' | ||
36 | RESULT=`${DO_TIMEOUT} gnunet-scalarproduct $CFGALICE $INPUTALICE -p $PEERIDBOB` | ||
37 | |||
38 | # terminate the testbed | ||
39 | kill $PID | ||
40 | |||
41 | if [ "$RESULT" = "$EXPECTED" ] | ||
42 | then | ||
43 | echo "OK" | ||
44 | exit 0 | ||
45 | else | ||
46 | echo "Result $RESULT, expected $EXPECTED - NOTOK" | ||
47 | exit 1 | ||
48 | fi | ||
diff --git a/src/contrib/service/scalarproduct/test_scalarproduct_negative.sh b/src/contrib/service/scalarproduct/test_scalarproduct_negative.sh new file mode 100755 index 000000000..459406836 --- /dev/null +++ b/src/contrib/service/scalarproduct/test_scalarproduct_negative.sh | |||
@@ -0,0 +1,49 @@ | |||
1 | #!/bin/bash | ||
2 | # compute a simple scalar product | ||
3 | # payload for this test: | ||
4 | INPUTALICE="-k CCC -e 'AB,10;RO,-3;FL,-3;LOL,1;'" | ||
5 | INPUTBOB="-k CCC -e 'BC,-20000;RO,1000;FL,100;LOL,24;'" | ||
6 | |||
7 | # necessary to make the testing prefix deterministic, so we can access the config files | ||
8 | unset XDG_DATA_HOME | ||
9 | unset XDG_CONFIG_HOME | ||
10 | |||
11 | PREFIX=/tmp/test-scalarproduct`date +%H%M%S` | ||
12 | |||
13 | # where can we find the peers config files? | ||
14 | CFGALICE="-c $PREFIX/0/config" | ||
15 | CFGBOB="-c $PREFIX/1/config" | ||
16 | |||
17 | which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 15" | ||
18 | |||
19 | # launch two peers in line topology non-interactively | ||
20 | # | ||
21 | # interactive mode would terminate the test immediately | ||
22 | # because the rest of the script is already in stdin, | ||
23 | # thus redirecting stdin does not suffice) | ||
24 | #GNUNET_LOG='scalarproduct;;;;DEBUG' | ||
25 | GNUNET_TESTING_PREFIX=$PREFIX ../testbed/gnunet-testbed-profiler -n -c test_scalarproduct.conf -p 2 & | ||
26 | PID=$! | ||
27 | # sleep 1 is too short on most systems, 2 works on most, 5 seems to be safe | ||
28 | sleep 5 | ||
29 | |||
30 | # get bob's peer ID, necessary for alice | ||
31 | PEERIDBOB=`${DO_TIMEOUT} gnunet-peerinfo -qs $CFGBOB` | ||
32 | |||
33 | #GNUNET_LOG=';;;;DEBUG' | ||
34 | ${DO_TIMEOUT} gnunet-scalarproduct $CFGBOB $INPUTBOB & | ||
35 | #RESULT=`GNUNET_LOG=';;;;DEBUG' | ||
36 | RESULT=`${DO_TIMEOUT} gnunet-scalarproduct $CFGALICE $INPUTALICE -p $PEERIDBOB` | ||
37 | |||
38 | # terminate the testbed | ||
39 | kill $PID | ||
40 | |||
41 | EXPECTED="-0CCC" | ||
42 | if [ "$RESULT" = "$EXPECTED" ] | ||
43 | then | ||
44 | echo "OK" | ||
45 | exit 0 | ||
46 | else | ||
47 | echo "Result $RESULT, expected $EXPECTED NOTOK" | ||
48 | exit 1 | ||
49 | fi | ||
diff --git a/src/contrib/service/scalarproduct/test_scalarproduct_negativezero.sh b/src/contrib/service/scalarproduct/test_scalarproduct_negativezero.sh new file mode 100755 index 000000000..6f80b2ea2 --- /dev/null +++ b/src/contrib/service/scalarproduct/test_scalarproduct_negativezero.sh | |||
@@ -0,0 +1,46 @@ | |||
1 | #!/bin/bash | ||
2 | # compute a simple scalar product | ||
3 | # payload for this test: | ||
4 | INPUTALICE="-k CCC -e 'AB,10;RO,-1;FL,1;LOL,1;'" | ||
5 | INPUTBOB="-k CCC -e 'BC,20;RO,1;FL,1;LOL,0;'" | ||
6 | |||
7 | # necessary to make the testing prefix deterministic, so we can access the config files | ||
8 | PREFIX=/tmp/test-scalarproduct`date +%H%M%S` | ||
9 | |||
10 | # where can we find the peers config files? | ||
11 | CFGALICE="-c $PREFIX/0/config" | ||
12 | CFGBOB="-c $PREFIX/1/config" | ||
13 | |||
14 | # launch two peers in line topology non-interactively | ||
15 | # | ||
16 | # interactive mode would terminate the test immediately | ||
17 | # because the rest of the script is already in stdin, | ||
18 | # thus redirecting stdin does not suffice) | ||
19 | # GNUNET_LOG='scalarproduct;;;;DEBUG' | ||
20 | GNUNET_TESTING_PREFIX=$PREFIX ../testbed/gnunet-testbed-profiler -n -c test_scalarproduct.conf -p 2 & | ||
21 | PID=$! | ||
22 | # sleep 1 is too short on most systems, 2 works on most, 5 seems to be safe | ||
23 | sleep 5 | ||
24 | |||
25 | which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 15" | ||
26 | |||
27 | # get bob's peer ID, necessary for alice | ||
28 | PEERIDBOB=`${DO_TIMEOUT} gnunet-peerinfo -qs $CFGBOB` | ||
29 | |||
30 | #GNUNET_LOG=';;;;DEBUG' | ||
31 | ${DO_TIMEOUT} gnunet-scalarproduct $CFGBOB $INPUTBOB & | ||
32 | #GNUNET_LOG=';;;;DEBUG' | ||
33 | RESULT=`${DO_TIMEOUT} gnunet-scalarproduct $CFGALICE $INPUTALICE -p $PEERIDBOB` | ||
34 | |||
35 | # terminate the testbed | ||
36 | kill $PID | ||
37 | |||
38 | EXPECTED="00" | ||
39 | if [ "$RESULT" = "$EXPECTED" ] | ||
40 | then | ||
41 | echo "OK" | ||
42 | exit 0 | ||
43 | else | ||
44 | echo "Result $RESULT NOTOK" | ||
45 | exit 1 | ||
46 | fi | ||