Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2020, 2021 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file util/test_helper_eddsa.c
18 : * @brief Tests for EDDSA crypto helper
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include "taler/taler_util.h"
23 : #include <gnunet/gnunet_signatures.h>
24 :
25 : /**
26 : * Configuration has 1 minute duration and 5 minutes lookahead, so
27 : * we should never have more than 6 active keys, plus for during
28 : * key expiration / revocation.
29 : */
30 : #define MAX_KEYS 20
31 :
32 : /**
33 : * How many random key revocations should we test?
34 : */
35 : #define NUM_REVOKES 3
36 :
37 : /**
38 : * How many iterations of the successful signing test should we run
39 : * during the test phase?
40 : */
41 : #define NUM_SIGN_TESTS 3
42 :
43 : /**
44 : * How many iterations of the successful signing test should we run
45 : * during the benchmark phase?
46 : */
47 : #define NUM_SIGN_PERFS 100
48 :
49 : /**
50 : * How many parallel clients should we use for the parallel
51 : * benchmark? (> 500 may cause problems with the max open FD number limit).
52 : */
53 : #define NUM_CORES 8
54 :
55 : /**
56 : * Number of keys currently in #keys.
57 : */
58 : static unsigned int num_keys;
59 :
60 : /**
61 : * Keys currently managed by the helper.
62 : */
63 : struct KeyData
64 : {
65 : /**
66 : * Validity start point.
67 : */
68 : struct GNUNET_TIME_Timestamp start_time;
69 :
70 : /**
71 : * Key expires for signing at @e start_time plus this value.
72 : */
73 : struct GNUNET_TIME_Relative validity_duration;
74 :
75 : /**
76 : * Full public key.
77 : */
78 : struct TALER_ExchangePublicKeyP exchange_pub;
79 :
80 : /**
81 : * Is this key currently valid?
82 : */
83 : bool valid;
84 :
85 : /**
86 : * Did the test driver revoke this key?
87 : */
88 : bool revoked;
89 : };
90 :
91 : /**
92 : * Array of all the keys we got from the helper.
93 : */
94 : static struct KeyData keys[MAX_KEYS];
95 :
96 :
97 : /**
98 : * Function called with information about available keys for signing. Usually
99 : * only called once per key upon connect. Also called again in case a key is
100 : * being revoked, in that case with an @a end_time of zero. Stores the keys
101 : * status in #keys.
102 : *
103 : * @param cls closure, NULL
104 : * @param start_time when does the key become available for signing;
105 : * zero if the key has been revoked or purged
106 : * @param validity_duration how long does the key remain available for signing;
107 : * zero if the key has been revoked or purged
108 : * @param exchange_pub the public key itself
109 : * @param sm_pub public key of the security module, NULL if the key was revoked or purged
110 : * @param sm_sig signature from the security module, NULL if the key was revoked or purged
111 : * The signature was already verified against @a sm_pub.
112 : */
113 : static void
114 84 : key_cb (void *cls,
115 : struct GNUNET_TIME_Timestamp start_time,
116 : struct GNUNET_TIME_Relative validity_duration,
117 : const struct TALER_ExchangePublicKeyP *exchange_pub,
118 : const struct TALER_SecurityModulePublicKeyP *sm_pub,
119 : const struct TALER_SecurityModuleSignatureP *sm_sig)
120 : {
121 : (void) cls;
122 : (void) sm_pub;
123 : (void) sm_sig;
124 :
125 84 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
126 : "Update on key %s (%s)...",
127 : TALER_B2S (exchange_pub),
128 : GNUNET_STRINGS_relative_time_to_string (validity_duration,
129 : GNUNET_YES));
130 :
131 84 : if (GNUNET_TIME_relative_is_zero (validity_duration))
132 : {
133 3 : bool found = false;
134 :
135 11 : for (unsigned int i = 0; i<MAX_KEYS; i++)
136 11 : if (0 == GNUNET_memcmp (exchange_pub,
137 : &keys[i].exchange_pub))
138 : {
139 3 : keys[i].valid = false;
140 3 : keys[i].revoked = false;
141 3 : GNUNET_assert (num_keys > 0);
142 3 : num_keys--;
143 3 : found = true;
144 3 : break;
145 : }
146 3 : if (! found)
147 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
148 : "Error: helper announced expiration of unknown key!\n");
149 :
150 3 : return;
151 : }
152 392 : for (unsigned int i = 0; i<MAX_KEYS; i++)
153 392 : if (! keys[i].valid)
154 : {
155 81 : keys[i].valid = true;
156 81 : keys[i].exchange_pub = *exchange_pub;
157 81 : keys[i].start_time = start_time;
158 81 : keys[i].validity_duration = validity_duration;
159 81 : num_keys++;
160 81 : return;
161 : }
162 : /* too many keys! */
163 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
164 : "Error: received %d live keys from the service!\n",
165 : MAX_KEYS + 1);
166 : }
167 :
168 :
169 : /**
170 : * Test key revocation logic.
171 : *
172 : * @param esh handle to the helper
173 : * @return 0 on success
174 : */
175 : static int
176 1 : test_revocation (struct TALER_CRYPTO_ExchangeSignHelper *esh)
177 : {
178 1 : struct timespec req = {
179 : .tv_nsec = 250000000
180 : };
181 :
182 4 : for (unsigned int i = 0; i<NUM_REVOKES; i++)
183 : {
184 : uint32_t off;
185 :
186 3 : off = GNUNET_CRYPTO_random_u32 (num_keys);
187 : /* find index of key to revoke */
188 11 : for (unsigned int j = 0; j < MAX_KEYS; j++)
189 : {
190 11 : if (! keys[j].valid)
191 0 : continue;
192 11 : if (0 != off)
193 : {
194 8 : off--;
195 8 : continue;
196 : }
197 3 : keys[j].revoked = true;
198 3 : fprintf (stderr,
199 : "Revoking key %s (%u) ...",
200 3 : TALER_B2S (&keys[j].exchange_pub),
201 : j);
202 3 : TALER_CRYPTO_helper_esign_revoke (esh,
203 3 : &keys[j].exchange_pub);
204 6 : for (unsigned int k = 0; k<1000; k++)
205 : {
206 6 : TALER_CRYPTO_helper_esign_poll (esh);
207 9 : if ( (! keys[j].revoked) ||
208 3 : (GNUNET_TIME_absolute_is_past (
209 : GNUNET_TIME_absolute_add (keys[j].start_time.abs_time,
210 : keys[j].validity_duration))) )
211 : {
212 : break;
213 : }
214 3 : nanosleep (&req, NULL);
215 3 : fprintf (stderr, ".");
216 : }
217 3 : if ( (keys[j].revoked) &&
218 0 : (! GNUNET_TIME_absolute_is_past (
219 : GNUNET_TIME_absolute_add (keys[j].start_time.abs_time,
220 : keys[j].validity_duration))) )
221 : {
222 0 : fprintf (stderr,
223 : "\nFAILED: timeout trying to revoke key %u\n",
224 : j);
225 0 : TALER_CRYPTO_helper_esign_disconnect (esh);
226 0 : esh = NULL;
227 0 : return 2;
228 : }
229 3 : fprintf (stderr, "\n");
230 3 : break;
231 : }
232 : }
233 1 : return 0;
234 : }
235 :
236 :
237 : /**
238 : * Test signing logic.
239 : *
240 : * @param esh handle to the helper
241 : * @return 0 on success
242 : */
243 : static int
244 1 : test_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh)
245 : {
246 1 : struct GNUNET_CRYPTO_SignaturePurpose purpose = {
247 1 : .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
248 1 : .size = htonl (sizeof (purpose)),
249 : };
250 :
251 4 : for (unsigned int i = 0; i<NUM_SIGN_TESTS; i++)
252 : {
253 : struct TALER_ExchangePublicKeyP exchange_pub;
254 : struct TALER_ExchangeSignatureP exchange_sig;
255 : enum TALER_ErrorCode ec;
256 :
257 3 : ec = TALER_CRYPTO_helper_esign_sign_ (esh,
258 : &purpose,
259 : &exchange_pub,
260 : &exchange_sig);
261 3 : switch (ec)
262 : {
263 3 : case TALER_EC_NONE:
264 3 : if (GNUNET_OK !=
265 3 : GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
266 : &purpose,
267 : &exchange_sig.eddsa_signature,
268 : &exchange_pub.eddsa_pub))
269 : {
270 : /* signature invalid */
271 0 : GNUNET_break (0);
272 0 : return 17;
273 : }
274 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
275 : "Received valid signature\n");
276 3 : break;
277 0 : default:
278 : /* unexpected error */
279 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
280 : "Unexpected error %d\n",
281 : ec);
282 0 : return 7;
283 : }
284 : }
285 1 : return 0;
286 : }
287 :
288 :
289 : /**
290 : * Benchmark signing logic.
291 : *
292 : * @param esh handle to the helper
293 : * @return 0 on success
294 : */
295 : static int
296 9 : perf_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh,
297 : const char *type)
298 : {
299 9 : struct GNUNET_CRYPTO_SignaturePurpose purpose = {
300 9 : .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
301 9 : .size = htonl (sizeof (purpose)),
302 : };
303 : struct GNUNET_TIME_Relative duration;
304 :
305 9 : duration = GNUNET_TIME_UNIT_ZERO;
306 909 : for (unsigned int j = 0; j<NUM_SIGN_PERFS; j++)
307 : {
308 : struct GNUNET_TIME_Relative delay;
309 : struct TALER_ExchangePublicKeyP exchange_pub;
310 : struct TALER_ExchangeSignatureP exchange_sig;
311 : enum TALER_ErrorCode ec;
312 : struct GNUNET_TIME_Absolute start;
313 :
314 900 : TALER_CRYPTO_helper_esign_poll (esh);
315 900 : start = GNUNET_TIME_absolute_get ();
316 900 : ec = TALER_CRYPTO_helper_esign_sign_ (esh,
317 : &purpose,
318 : &exchange_pub,
319 : &exchange_sig);
320 900 : if (TALER_EC_NONE != ec)
321 : {
322 0 : GNUNET_break (0);
323 0 : return 42;
324 : }
325 900 : delay = GNUNET_TIME_absolute_get_duration (start);
326 900 : duration = GNUNET_TIME_relative_add (duration,
327 : delay);
328 : } /* for j */
329 9 : fprintf (stderr,
330 : "%u (%s) signature operations took %s\n",
331 : (unsigned int) NUM_SIGN_PERFS,
332 : type,
333 : GNUNET_STRINGS_relative_time_to_string (duration,
334 : GNUNET_YES));
335 9 : return 0;
336 : }
337 :
338 :
339 : /**
340 : * Parallel signing logic.
341 : *
342 : * @param esh handle to the helper
343 : * @return 0 on success
344 : */
345 : static int
346 1 : par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
347 : {
348 : struct GNUNET_TIME_Absolute start;
349 : struct GNUNET_TIME_Relative duration;
350 : pid_t pids[NUM_CORES];
351 :
352 1 : memset (keys,
353 : 0,
354 : sizeof (keys));
355 1 : num_keys = 0;
356 1 : start = GNUNET_TIME_absolute_get ();
357 9 : for (unsigned int i = 0; i<NUM_CORES; i++)
358 : {
359 8 : pids[i] = fork ();
360 16 : GNUNET_assert (-1 != pids[i]);
361 16 : if (0 == pids[i])
362 : {
363 : struct TALER_CRYPTO_ExchangeSignHelper *esh;
364 : int ret;
365 :
366 8 : esh = TALER_CRYPTO_helper_esign_connect (cfg,
367 : "taler-exchange",
368 : &key_cb,
369 : NULL);
370 8 : if (NULL == esh)
371 : {
372 0 : GNUNET_break (0);
373 0 : exit (EXIT_FAILURE);
374 : }
375 8 : ret = perf_signing (esh,
376 : "parallel");
377 8 : TALER_CRYPTO_helper_esign_disconnect (esh);
378 8 : exit (ret);
379 : }
380 : }
381 9 : for (unsigned int i = 0; i<NUM_CORES; i++)
382 : {
383 : int wstatus;
384 :
385 8 : GNUNET_assert (pids[i] ==
386 : waitpid (pids[i],
387 : &wstatus,
388 : 0));
389 : }
390 1 : duration = GNUNET_TIME_absolute_get_duration (start);
391 1 : fprintf (stderr,
392 : "%u (parallel) signature operations took %s (total real time)\n",
393 : (unsigned int) NUM_SIGN_PERFS * NUM_CORES,
394 : GNUNET_STRINGS_relative_time_to_string (duration,
395 : true));
396 1 : return 0;
397 : }
398 :
399 :
400 : /**
401 : * Main entry point into the test logic with the helper already running.
402 : */
403 : static int
404 1 : run_test (void)
405 : {
406 : struct GNUNET_CONFIGURATION_Handle *cfg;
407 : struct TALER_CRYPTO_ExchangeSignHelper *esh;
408 : int ret;
409 1 : struct timespec req = {
410 : .tv_nsec = 250000000
411 : };
412 :
413 1 : cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ());
414 1 : if (GNUNET_OK !=
415 1 : GNUNET_CONFIGURATION_load (cfg,
416 : "test_helper_eddsa.conf"))
417 : {
418 0 : GNUNET_break (0);
419 0 : return 77;
420 : }
421 :
422 : /* wait for helper to start and give us keys */
423 1 : fprintf (stderr, "Waiting for helper to start ... ");
424 1 : for (unsigned int i = 0; i<100; i++)
425 : {
426 1 : nanosleep (&req,
427 : NULL);
428 1 : esh = TALER_CRYPTO_helper_esign_connect (cfg,
429 : "taler-exchange",
430 : &key_cb,
431 : NULL);
432 1 : if (NULL != esh)
433 1 : break;
434 0 : fprintf (stderr, ".");
435 : }
436 1 : if (NULL == esh)
437 : {
438 0 : fprintf (stderr,
439 : "\nFAILED: timeout trying to connect to helper\n");
440 0 : GNUNET_CONFIGURATION_destroy (cfg);
441 0 : return 1;
442 : }
443 1 : if (0 == num_keys)
444 : {
445 0 : fprintf (stderr,
446 : "\nFAILED: no keys returned by helper\n");
447 0 : TALER_CRYPTO_helper_esign_disconnect (esh);
448 0 : esh = NULL;
449 0 : GNUNET_CONFIGURATION_destroy (cfg);
450 0 : return 1;
451 : }
452 1 : fprintf (stderr,
453 : " Done (%u keys)\n",
454 : num_keys);
455 1 : ret = 0;
456 1 : if (0 == ret)
457 1 : ret = test_revocation (esh);
458 1 : if (0 == ret)
459 1 : ret = test_signing (esh);
460 1 : if (0 == ret)
461 1 : ret = perf_signing (esh,
462 : "sequential");
463 1 : if (NULL != esh)
464 : {
465 1 : TALER_CRYPTO_helper_esign_disconnect (esh);
466 1 : esh = NULL;
467 : }
468 1 : if (0 == ret)
469 1 : ret = par_signing (cfg);
470 : /* clean up our state */
471 21 : for (unsigned int i = 0; i<MAX_KEYS; i++)
472 20 : if (keys[i].valid)
473 : {
474 0 : keys[i].valid = false;
475 0 : GNUNET_assert (num_keys > 0);
476 0 : num_keys--;
477 : }
478 1 : GNUNET_CONFIGURATION_destroy (cfg);
479 1 : return ret;
480 : }
481 :
482 :
483 : int
484 1 : main (int argc,
485 : const char *const argv[])
486 : {
487 : struct GNUNET_Process *helper;
488 : char *libexec_dir;
489 : char *binary_name;
490 : int ret;
491 : enum GNUNET_OS_ProcessStatusType type;
492 : unsigned long code;
493 :
494 : (void) argc;
495 : (void) argv;
496 1 : unsetenv ("XDG_DATA_HOME");
497 1 : unsetenv ("XDG_CONFIG_HOME");
498 1 : GNUNET_log_setup ("test-helper-eddsa",
499 : "INFO",
500 : NULL);
501 1 : libexec_dir = GNUNET_OS_installation_get_path (TALER_EXCHANGE_project_data (),
502 : GNUNET_OS_IPK_BINDIR);
503 1 : GNUNET_asprintf (&binary_name,
504 : "%s/%s",
505 : libexec_dir,
506 : "taler-exchange-secmod-eddsa");
507 1 : GNUNET_free (libexec_dir);
508 1 : helper = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
509 1 : if (GNUNET_OK !=
510 1 : GNUNET_process_run_command_va (helper,
511 : binary_name,
512 : binary_name,
513 : "-c",
514 : "test_helper_eddsa.conf",
515 : "-L",
516 : "INFO",
517 : NULL))
518 : {
519 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
520 : "exec",
521 : binary_name);
522 0 : GNUNET_process_destroy (helper);
523 0 : GNUNET_free (binary_name);
524 0 : return 77;
525 : }
526 1 : GNUNET_free (binary_name);
527 1 : ret = run_test ();
528 :
529 1 : GNUNET_break (GNUNET_OK ==
530 : GNUNET_process_kill (helper,
531 : SIGTERM));
532 1 : if (GNUNET_OK !=
533 1 : GNUNET_process_wait (helper,
534 : true,
535 : &type,
536 : &code))
537 : {
538 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
539 : "Helper process did not die voluntarily, killing hard\n");
540 0 : GNUNET_break (GNUNET_OK ==
541 : GNUNET_process_kill (helper,
542 : SIGKILL));
543 0 : ret = 4;
544 : }
545 1 : else if ( (GNUNET_OS_PROCESS_EXITED != type) ||
546 1 : (0 != code) )
547 : {
548 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
549 : "Helper died with unexpected status %d/%d\n",
550 : (int) type,
551 : (int) code);
552 0 : ret = 5;
553 : }
554 1 : GNUNET_process_destroy (helper);
555 1 : return ret;
556 : }
557 :
558 :
559 : /* end of test_helper_eddsa.c */
|