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_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 (GNUNET_CRYPTO_QUALITY_WEAK,
187 : num_keys);
188 : /* find index of key to revoke */
189 11 : for (unsigned int j = 0; j < MAX_KEYS; j++)
190 : {
191 11 : if (! keys[j].valid)
192 0 : continue;
193 11 : if (0 != off)
194 : {
195 8 : off--;
196 8 : continue;
197 : }
198 3 : keys[j].revoked = true;
199 3 : fprintf (stderr,
200 : "Revoking key %s (%u) ...",
201 3 : TALER_B2S (&keys[j].exchange_pub),
202 : j);
203 3 : TALER_CRYPTO_helper_esign_revoke (esh,
204 3 : &keys[j].exchange_pub);
205 6 : for (unsigned int k = 0; k<1000; k++)
206 : {
207 6 : TALER_CRYPTO_helper_esign_poll (esh);
208 9 : if ( (! keys[j].revoked) ||
209 3 : (GNUNET_TIME_absolute_is_past (
210 : GNUNET_TIME_absolute_add (keys[j].start_time.abs_time,
211 : keys[j].validity_duration))) )
212 : {
213 : break;
214 : }
215 3 : nanosleep (&req, NULL);
216 3 : fprintf (stderr, ".");
217 : }
218 3 : if ( (keys[j].revoked) &&
219 0 : (! GNUNET_TIME_absolute_is_past (
220 : GNUNET_TIME_absolute_add (keys[j].start_time.abs_time,
221 : keys[j].validity_duration))) )
222 : {
223 0 : fprintf (stderr,
224 : "\nFAILED: timeout trying to revoke key %u\n",
225 : j);
226 0 : TALER_CRYPTO_helper_esign_disconnect (esh);
227 0 : esh = NULL;
228 0 : return 2;
229 : }
230 3 : fprintf (stderr, "\n");
231 3 : break;
232 : }
233 : }
234 1 : return 0;
235 : }
236 :
237 :
238 : /**
239 : * Test signing logic.
240 : *
241 : * @param esh handle to the helper
242 : * @return 0 on success
243 : */
244 : static int
245 1 : test_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh)
246 : {
247 1 : struct GNUNET_CRYPTO_EccSignaturePurpose purpose = {
248 1 : .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
249 1 : .size = htonl (sizeof (purpose)),
250 : };
251 :
252 4 : for (unsigned int i = 0; i<NUM_SIGN_TESTS; i++)
253 : {
254 : struct TALER_ExchangePublicKeyP exchange_pub;
255 : struct TALER_ExchangeSignatureP exchange_sig;
256 : enum TALER_ErrorCode ec;
257 :
258 3 : ec = TALER_CRYPTO_helper_esign_sign_ (esh,
259 : &purpose,
260 : &exchange_pub,
261 : &exchange_sig);
262 3 : switch (ec)
263 : {
264 3 : case TALER_EC_NONE:
265 3 : if (GNUNET_OK !=
266 3 : GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
267 : &purpose,
268 : &exchange_sig.eddsa_signature,
269 : &exchange_pub.eddsa_pub))
270 : {
271 : /* signature invalid */
272 0 : GNUNET_break (0);
273 0 : return 17;
274 : }
275 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
276 : "Received valid signature\n");
277 3 : break;
278 0 : default:
279 : /* unexpected error */
280 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
281 : "Unexpected error %d\n",
282 : ec);
283 0 : return 7;
284 : }
285 : }
286 1 : return 0;
287 : }
288 :
289 :
290 : /**
291 : * Benchmark signing logic.
292 : *
293 : * @param esh handle to the helper
294 : * @return 0 on success
295 : */
296 : static int
297 9 : perf_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh,
298 : const char *type)
299 : {
300 9 : struct GNUNET_CRYPTO_EccSignaturePurpose purpose = {
301 9 : .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
302 9 : .size = htonl (sizeof (purpose)),
303 : };
304 : struct GNUNET_TIME_Relative duration;
305 :
306 9 : duration = GNUNET_TIME_UNIT_ZERO;
307 909 : for (unsigned int j = 0; j<NUM_SIGN_PERFS; j++)
308 : {
309 : struct GNUNET_TIME_Relative delay;
310 : struct TALER_ExchangePublicKeyP exchange_pub;
311 : struct TALER_ExchangeSignatureP exchange_sig;
312 : enum TALER_ErrorCode ec;
313 : struct GNUNET_TIME_Absolute start;
314 :
315 900 : TALER_CRYPTO_helper_esign_poll (esh);
316 900 : start = GNUNET_TIME_absolute_get ();
317 900 : ec = TALER_CRYPTO_helper_esign_sign_ (esh,
318 : &purpose,
319 : &exchange_pub,
320 : &exchange_sig);
321 900 : if (TALER_EC_NONE != ec)
322 : {
323 0 : GNUNET_break (0);
324 0 : return 42;
325 : }
326 900 : delay = GNUNET_TIME_absolute_get_duration (start);
327 900 : duration = GNUNET_TIME_relative_add (duration,
328 : delay);
329 : } /* for j */
330 9 : fprintf (stderr,
331 : "%u (%s) signature operations took %s\n",
332 : (unsigned int) NUM_SIGN_PERFS,
333 : type,
334 : GNUNET_STRINGS_relative_time_to_string (duration,
335 : GNUNET_YES));
336 9 : return 0;
337 : }
338 :
339 :
340 : /**
341 : * Parallel signing logic.
342 : *
343 : * @param esh handle to the helper
344 : * @return 0 on success
345 : */
346 : static int
347 1 : par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
348 : {
349 : struct GNUNET_TIME_Absolute start;
350 : struct GNUNET_TIME_Relative duration;
351 : pid_t pids[NUM_CORES];
352 :
353 1 : memset (keys,
354 : 0,
355 : sizeof (keys));
356 1 : num_keys = 0;
357 1 : start = GNUNET_TIME_absolute_get ();
358 9 : for (unsigned int i = 0; i<NUM_CORES; i++)
359 : {
360 8 : pids[i] = fork ();
361 16 : GNUNET_assert (-1 != pids[i]);
362 16 : if (0 == pids[i])
363 : {
364 : struct TALER_CRYPTO_ExchangeSignHelper *esh;
365 : int ret;
366 :
367 8 : esh = TALER_CRYPTO_helper_esign_connect (cfg,
368 : "taler-exchange",
369 : &key_cb,
370 : NULL);
371 8 : if (NULL == esh)
372 : {
373 0 : GNUNET_break (0);
374 0 : exit (EXIT_FAILURE);
375 : }
376 8 : ret = perf_signing (esh,
377 : "parallel");
378 8 : TALER_CRYPTO_helper_esign_disconnect (esh);
379 8 : exit (ret);
380 : }
381 : }
382 9 : for (unsigned int i = 0; i<NUM_CORES; i++)
383 : {
384 : int wstatus;
385 :
386 8 : GNUNET_assert (pids[i] ==
387 : waitpid (pids[i],
388 : &wstatus,
389 : 0));
390 : }
391 1 : duration = GNUNET_TIME_absolute_get_duration (start);
392 1 : fprintf (stderr,
393 : "%u (parallel) signature operations took %s (total real time)\n",
394 : (unsigned int) NUM_SIGN_PERFS * NUM_CORES,
395 : GNUNET_STRINGS_relative_time_to_string (duration,
396 : true));
397 1 : return 0;
398 : }
399 :
400 :
401 : /**
402 : * Main entry point into the test logic with the helper already running.
403 : */
404 : static int
405 1 : run_test (void)
406 : {
407 : struct GNUNET_CONFIGURATION_Handle *cfg;
408 : struct TALER_CRYPTO_ExchangeSignHelper *esh;
409 : int ret;
410 1 : struct timespec req = {
411 : .tv_nsec = 250000000
412 : };
413 :
414 1 : cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ());
415 1 : if (GNUNET_OK !=
416 1 : GNUNET_CONFIGURATION_load (cfg,
417 : "test_helper_eddsa.conf"))
418 : {
419 0 : GNUNET_break (0);
420 0 : return 77;
421 : }
422 :
423 : /* wait for helper to start and give us keys */
424 1 : fprintf (stderr, "Waiting for helper to start ... ");
425 1 : for (unsigned int i = 0; i<100; i++)
426 : {
427 1 : nanosleep (&req,
428 : NULL);
429 1 : esh = TALER_CRYPTO_helper_esign_connect (cfg,
430 : "taler-exchange",
431 : &key_cb,
432 : NULL);
433 1 : if (NULL != esh)
434 1 : break;
435 0 : fprintf (stderr, ".");
436 : }
437 1 : if (NULL == esh)
438 : {
439 0 : fprintf (stderr,
440 : "\nFAILED: timeout trying to connect to helper\n");
441 0 : GNUNET_CONFIGURATION_destroy (cfg);
442 0 : return 1;
443 : }
444 1 : if (0 == num_keys)
445 : {
446 0 : fprintf (stderr,
447 : "\nFAILED: no keys returned by helper\n");
448 0 : TALER_CRYPTO_helper_esign_disconnect (esh);
449 0 : esh = NULL;
450 0 : GNUNET_CONFIGURATION_destroy (cfg);
451 0 : return 1;
452 : }
453 1 : fprintf (stderr,
454 : " Done (%u keys)\n",
455 : num_keys);
456 1 : ret = 0;
457 1 : if (0 == ret)
458 1 : ret = test_revocation (esh);
459 1 : if (0 == ret)
460 1 : ret = test_signing (esh);
461 1 : if (0 == ret)
462 1 : ret = perf_signing (esh,
463 : "sequential");
464 1 : if (NULL != esh)
465 : {
466 1 : TALER_CRYPTO_helper_esign_disconnect (esh);
467 1 : esh = NULL;
468 : }
469 1 : if (0 == ret)
470 1 : ret = par_signing (cfg);
471 : /* clean up our state */
472 21 : for (unsigned int i = 0; i<MAX_KEYS; i++)
473 20 : if (keys[i].valid)
474 : {
475 0 : keys[i].valid = false;
476 0 : GNUNET_assert (num_keys > 0);
477 0 : num_keys--;
478 : }
479 1 : GNUNET_CONFIGURATION_destroy (cfg);
480 1 : return ret;
481 : }
482 :
483 :
484 : int
485 1 : main (int argc,
486 : const char *const argv[])
487 : {
488 : struct GNUNET_OS_Process *helper;
489 : char *libexec_dir;
490 : char *binary_name;
491 : int ret;
492 : enum GNUNET_OS_ProcessStatusType type;
493 : unsigned long code;
494 :
495 : (void) argc;
496 : (void) argv;
497 1 : unsetenv ("XDG_DATA_HOME");
498 1 : unsetenv ("XDG_CONFIG_HOME");
499 1 : GNUNET_log_setup ("test-helper-eddsa",
500 : "INFO",
501 : NULL);
502 1 : libexec_dir = GNUNET_OS_installation_get_path (TALER_EXCHANGE_project_data (),
503 : GNUNET_OS_IPK_BINDIR);
504 1 : GNUNET_asprintf (&binary_name,
505 : "%s/%s",
506 : libexec_dir,
507 : "taler-exchange-secmod-eddsa");
508 1 : GNUNET_free (libexec_dir);
509 1 : helper = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
510 : NULL, NULL, NULL,
511 : binary_name,
512 : binary_name,
513 : "-c",
514 : "test_helper_eddsa.conf",
515 : "-L",
516 : "INFO",
517 : NULL);
518 1 : if (NULL == helper)
519 : {
520 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
521 : "exec",
522 : binary_name);
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_OS_process_kill (helper,
530 : SIGTERM);
531 1 : if (GNUNET_OK !=
532 1 : GNUNET_OS_process_wait_status (helper,
533 : &type,
534 : &code))
535 : {
536 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
537 : "Helper process did not die voluntarily, killing hard\n");
538 0 : GNUNET_OS_process_kill (helper,
539 : SIGKILL);
540 0 : ret = 4;
541 : }
542 1 : else if ( (GNUNET_OS_PROCESS_EXITED != type) ||
543 1 : (0 != code) )
544 : {
545 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
546 : "Helper died with unexpected status %d/%d\n",
547 : (int) type,
548 : (int) code);
549 0 : ret = 5;
550 : }
551 1 : GNUNET_OS_process_destroy (helper);
552 1 : return ret;
553 : }
554 :
555 :
556 : /* end of test_helper_eddsa.c */
|