Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2020, 2021, 2023 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_cs.c
18 : * @brief Tests for CS crypto helper
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include "taler/taler_util.h"
23 :
24 : /**
25 : * Configuration has 1 minute duration and 5 minutes lookahead, but
26 : * we do not get 'revocations' for expired keys. So this must be
27 : * large enough to deal with key rotation during the runtime of
28 : * the benchmark.
29 : */
30 : #define MAX_KEYS 1024
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 : */
40 : #define NUM_SIGN_TESTS 5
41 :
42 : /**
43 : * How many iterations of the successful signing test should we run
44 : * during the benchmark phase?
45 : */
46 : #define NUM_SIGN_PERFS 100
47 :
48 : /**
49 : * How many parallel clients should we use for the parallel
50 : * benchmark? (> 500 may cause problems with the max open FD number limit).
51 : */
52 : #define NUM_CORES 8
53 :
54 : /**
55 : * Number of keys currently in #keys.
56 : */
57 : static unsigned int num_keys;
58 :
59 : /**
60 : * Keys currently managed by the helper.
61 : */
62 : struct KeyData
63 : {
64 : /**
65 : * Validity start point.
66 : */
67 : struct GNUNET_TIME_Timestamp start_time;
68 :
69 : /**
70 : * Key expires for signing at @e start_time plus this value.
71 : */
72 : struct GNUNET_TIME_Relative validity_duration;
73 :
74 : /**
75 : * Hash of the public key.
76 : */
77 : struct TALER_CsPubHashP h_cs;
78 :
79 : /**
80 : * Full public key.
81 : */
82 : struct TALER_DenominationPublicKey denom_pub;
83 :
84 : /**
85 : * Is this key currently valid?
86 : */
87 : bool valid;
88 :
89 : /**
90 : * Did the test driver revoke this key?
91 : */
92 : bool revoked;
93 : };
94 :
95 : /**
96 : * Array of all the keys we got from the helper.
97 : */
98 : static struct KeyData keys[MAX_KEYS];
99 :
100 :
101 : /**
102 : * Release memory occupied by #keys.
103 : */
104 : static void
105 9 : free_keys (void)
106 : {
107 9225 : for (unsigned int i = 0; i<MAX_KEYS; i++)
108 9216 : if (keys[i].valid)
109 : {
110 69 : TALER_denom_pub_free (&keys[i].denom_pub);
111 69 : keys[i].valid = false;
112 69 : GNUNET_assert (num_keys > 0);
113 69 : num_keys--;
114 : }
115 9 : }
116 :
117 :
118 : /**
119 : * Function called with information about available keys for signing. Usually
120 : * only called once per key upon connect. Also called again in case a key is
121 : * being revoked, in that case with an @a end_time of zero. Stores the keys
122 : * status in #keys.
123 : *
124 : * @param cls closure, NULL
125 : * @param section_name name of the denomination type in the configuration;
126 : * NULL if the key has been revoked or purged
127 : * @param start_time when does the key become available for signing;
128 : * zero if the key has been revoked or purged
129 : * @param validity_duration how long does the key remain available for signing;
130 : * zero if the key has been revoked or purged
131 : * @param h_cs hash of the @a denom_pub that is available (or was purged)
132 : * @param bs_pub the public key itself, NULL if the key was revoked or purged
133 : * @param sm_pub public key of the security module, NULL if the key was revoked or purged
134 : * @param sm_sig signature from the security module, NULL if the key was revoked or purged
135 : * The signature was already verified against @a sm_pub.
136 : */
137 : static void
138 75 : key_cb (void *cls,
139 : const char *section_name,
140 : struct GNUNET_TIME_Timestamp start_time,
141 : struct GNUNET_TIME_Relative validity_duration,
142 : const struct TALER_CsPubHashP *h_cs,
143 : struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub,
144 : const struct TALER_SecurityModulePublicKeyP *sm_pub,
145 : const struct TALER_SecurityModuleSignatureP *sm_sig)
146 : {
147 : (void) cls;
148 : (void) sm_pub;
149 : (void) sm_sig;
150 75 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
151 : "Key notification about key %s in `%s'\n",
152 : GNUNET_h2s (&h_cs->hash),
153 : section_name);
154 75 : if (0 == validity_duration.rel_value_us)
155 : {
156 3 : bool found = false;
157 :
158 3 : GNUNET_break (NULL == bs_pub);
159 3 : GNUNET_break (NULL == section_name);
160 11 : for (unsigned int i = 0; i<MAX_KEYS; i++)
161 11 : if (0 == GNUNET_memcmp (h_cs,
162 : &keys[i].h_cs))
163 : {
164 3 : keys[i].valid = false;
165 3 : keys[i].revoked = false;
166 3 : TALER_denom_pub_free (&keys[i].denom_pub);
167 3 : GNUNET_assert (num_keys > 0);
168 3 : num_keys--;
169 3 : found = true;
170 3 : break;
171 : }
172 3 : if (! found)
173 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
174 : "Error: helper announced expiration of unknown key!\n");
175 :
176 3 : return;
177 : }
178 :
179 72 : GNUNET_break (NULL != bs_pub);
180 314 : for (unsigned int i = 0; i<MAX_KEYS; i++)
181 314 : if (! keys[i].valid)
182 : {
183 72 : keys[i].valid = true;
184 72 : keys[i].h_cs = *h_cs;
185 72 : keys[i].start_time = start_time;
186 72 : keys[i].validity_duration = validity_duration;
187 : keys[i].denom_pub.bsign_pub_key
188 72 : = GNUNET_CRYPTO_bsign_pub_incref (bs_pub);
189 72 : num_keys++;
190 72 : return;
191 : }
192 : /* too many keys! */
193 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
194 : "Error: received %d live keys from the service!\n",
195 : MAX_KEYS + 1);
196 : }
197 :
198 :
199 : /**
200 : * Test key revocation logic.
201 : *
202 : * @param dh handle to the helper
203 : * @return 0 on success
204 : */
205 : static int
206 1 : test_revocation (struct TALER_CRYPTO_CsDenominationHelper *dh)
207 : {
208 1 : struct timespec req = {
209 : .tv_nsec = 250000000
210 : };
211 :
212 4 : for (unsigned int i = 0; i<NUM_REVOKES; i++)
213 : {
214 : uint32_t off;
215 :
216 3 : off = GNUNET_CRYPTO_random_u32 (num_keys);
217 : /* find index of key to revoke */
218 11 : for (unsigned int j = 0; j < MAX_KEYS; j++)
219 : {
220 11 : if (! keys[j].valid)
221 0 : continue;
222 11 : if (0 != off)
223 : {
224 8 : off--;
225 8 : continue;
226 : }
227 3 : keys[j].revoked = true;
228 3 : fprintf (stderr,
229 : "Revoking key %s ...",
230 3 : GNUNET_h2s (&keys[j].h_cs.hash));
231 3 : TALER_CRYPTO_helper_cs_revoke (dh,
232 3 : &keys[j].h_cs);
233 6 : for (unsigned int k = 0; k<1000; k++)
234 : {
235 6 : TALER_CRYPTO_helper_cs_poll (dh);
236 6 : if (! keys[j].revoked)
237 3 : break;
238 3 : nanosleep (&req, NULL);
239 3 : fprintf (stderr, ".");
240 : }
241 3 : if (keys[j].revoked)
242 : {
243 0 : fprintf (stderr,
244 : "\nFAILED: timeout trying to revoke key %u\n",
245 : j);
246 0 : TALER_CRYPTO_helper_cs_disconnect (dh);
247 0 : return 2;
248 : }
249 3 : fprintf (stderr, "\n");
250 3 : break;
251 : }
252 : }
253 1 : return 0;
254 : }
255 :
256 :
257 : /**
258 : * Set up planchet secrets and Clause-Schnorr nonces for a withdraw-like
259 : * operation.
260 : *
261 : * @param num number of planchets and nonces to derive
262 : * @param for_melt true to use the refresh nonce domain
263 : * @param[out] ps planchet secrets to initialize
264 : * @param[out] nonces Clause-Schnorr nonces to initialize
265 : */
266 : static void
267 17 : setup_withdraw_secrets (
268 : size_t num,
269 : bool for_melt,
270 : struct TALER_PlanchetMasterSecretP ps[static num],
271 : union GNUNET_CRYPTO_BlindSessionNonce nonces[static num])
272 17 : {
273 : struct TALER_WithdrawMasterSeedP seed;
274 : struct TALER_BlindingMasterSeedP blinding_seed;
275 17 : uint32_t indices[num];
276 :
277 17 : TALER_withdraw_master_seed_setup_random (&seed);
278 17 : TALER_withdraw_expand_secrets (num,
279 : &seed,
280 : ps);
281 17 : TALER_cs_withdraw_seed_to_blinding_seed (&seed,
282 : &blinding_seed);
283 179 : for (uint32_t i = 0; i<num; i++)
284 162 : indices[i] = i;
285 17 : TALER_cs_derive_only_cs_blind_nonces_from_seed (
286 : &blinding_seed,
287 : for_melt,
288 : num,
289 : indices,
290 : nonces);
291 17 : }
292 :
293 :
294 : /**
295 : * Test R derivation logic.
296 : *
297 : * @param dh handle to the helper
298 : * @return 0 on success
299 : */
300 : static int
301 1 : test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh)
302 : {
303 : enum TALER_ErrorCode ec;
304 1 : bool success = false;
305 : struct TALER_PlanchetMasterSecretP ps;
306 : struct TALER_CoinSpendPrivateKeyP coin_priv;
307 : union GNUNET_CRYPTO_BlindingSecretP bks;
308 : struct TALER_CoinPubHashP c_hash;
309 1 : struct GNUNET_CRYPTO_BlindingInputValues bi = {
310 : .cipher = GNUNET_CRYPTO_BSA_CS
311 : };
312 1 : struct TALER_ExchangeBlindingValues alg_values = {
313 : .blinding_inputs = &bi
314 : };
315 : union GNUNET_CRYPTO_BlindSessionNonce nonce;
316 :
317 1 : setup_withdraw_secrets (1,
318 : false,
319 : &ps,
320 : &nonce);
321 1025 : for (unsigned int i = 0; i<MAX_KEYS; i++)
322 : {
323 : struct TALER_PlanchetDetail pd;
324 :
325 1024 : if (! keys[i].valid)
326 1019 : continue;
327 5 : GNUNET_assert (GNUNET_CRYPTO_BSA_CS ==
328 : keys[i].denom_pub.bsign_pub_key->cipher);
329 5 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
330 : "Requesting R derivation with key %s\n",
331 : GNUNET_h2s (&keys[i].h_cs.hash));
332 : {
333 5 : struct TALER_CRYPTO_CsDeriveRequest cdr = {
334 5 : .h_cs = &keys[i].h_cs,
335 : .nonce = &nonce.cs_nonce
336 : };
337 :
338 5 : ec = TALER_CRYPTO_helper_cs_r_batch_derive (
339 : dh,
340 : 1,
341 : &cdr,
342 : false,
343 : &bi.details.cs_values);
344 : }
345 5 : switch (ec)
346 : {
347 1 : case TALER_EC_NONE:
348 1 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
349 : keys[i].start_time.abs_time),
350 : >,
351 : GNUNET_TIME_UNIT_SECONDS))
352 : {
353 : /* key worked too early */
354 0 : GNUNET_break (0);
355 0 : return 4;
356 : }
357 1 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
358 : keys[i].start_time.abs_time),
359 : >,
360 : keys[i].validity_duration))
361 : {
362 : /* key worked too later */
363 0 : GNUNET_break (0);
364 0 : return 5;
365 : }
366 :
367 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
368 : "Received valid R for key %s\n",
369 : GNUNET_h2s (&keys[i].h_cs.hash));
370 1 : TALER_planchet_setup_coin_priv (&ps,
371 : &alg_values,
372 : &coin_priv);
373 1 : TALER_planchet_blinding_secret_create (&ps,
374 : &alg_values,
375 : &bks);
376 1 : GNUNET_assert (GNUNET_OK ==
377 : TALER_planchet_prepare (&keys[i].denom_pub,
378 : &alg_values,
379 : &bks,
380 : &nonce,
381 : &coin_priv,
382 : NULL, /* no age commitment */
383 : &c_hash,
384 : &pd));
385 1 : TALER_blinded_planchet_free (&pd.blinded_planchet);
386 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
387 : "Successfully prepared planchet");
388 1 : success = true;
389 1 : break;
390 4 : case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
391 : /* This 'failure' is expected, we're testing also for the
392 : error handling! */
393 4 : if ( (GNUNET_TIME_relative_is_zero (
394 : GNUNET_TIME_absolute_get_remaining (
395 0 : keys[i].start_time.abs_time))) &&
396 0 : (GNUNET_TIME_relative_cmp (
397 : GNUNET_TIME_absolute_get_duration (
398 : keys[i].start_time.abs_time),
399 : <,
400 : keys[i].validity_duration)) )
401 : {
402 : /* key should have worked! */
403 0 : GNUNET_break (0);
404 0 : return 6;
405 : }
406 4 : break;
407 0 : default:
408 : /* unexpected error */
409 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
410 : "Unexpected error %d\n",
411 : ec);
412 0 : return 7;
413 : }
414 : }
415 1 : if (! success)
416 : {
417 : /* no valid key for signing found, also bad */
418 0 : GNUNET_break (0);
419 0 : return 16;
420 : }
421 :
422 : /* check R derivation does not work if the key is unknown */
423 : {
424 : struct TALER_CsPubHashP rnd;
425 : struct GNUNET_CRYPTO_CSPublicRPairP crp;
426 1 : struct TALER_CRYPTO_CsDeriveRequest cdr = {
427 : .h_cs = &rnd,
428 : .nonce = &nonce.cs_nonce,
429 : };
430 :
431 1 : GNUNET_CRYPTO_random_block (&rnd,
432 : sizeof (rnd));
433 1 : GNUNET_CRYPTO_random_block (&nonce,
434 : sizeof (nonce));
435 1 : ec = TALER_CRYPTO_helper_cs_r_batch_derive (dh,
436 : 1,
437 : &cdr,
438 : false,
439 : &crp);
440 1 : if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
441 : {
442 0 : GNUNET_break (0);
443 0 : return 17;
444 : }
445 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
446 : "R derivation with invalid key %s failed as desired\n",
447 : GNUNET_h2s (&rnd.hash));
448 : }
449 1 : return 0;
450 : }
451 :
452 :
453 : /**
454 : * Test signing logic.
455 : *
456 : * @param dh handle to the helper
457 : * @return 0 on success
458 : */
459 : static int
460 1 : test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh)
461 : {
462 : struct TALER_BlindedDenominationSignature ds;
463 : enum TALER_ErrorCode ec;
464 1 : bool success = false;
465 : struct TALER_PlanchetMasterSecretP ps;
466 : struct TALER_CoinSpendPrivateKeyP coin_priv;
467 : union GNUNET_CRYPTO_BlindingSecretP bks;
468 : struct TALER_CoinPubHashP c_hash;
469 1 : struct GNUNET_CRYPTO_BlindingInputValues bi = {
470 : .cipher = GNUNET_CRYPTO_BSA_CS
471 : };
472 1 : struct TALER_ExchangeBlindingValues alg_values = {
473 : .blinding_inputs = &bi
474 : };
475 : union GNUNET_CRYPTO_BlindSessionNonce nonce;
476 :
477 1 : setup_withdraw_secrets (1,
478 : false,
479 : &ps,
480 : &nonce);
481 1025 : for (unsigned int i = 0; i<MAX_KEYS; i++)
482 : {
483 1024 : if (! keys[i].valid)
484 1019 : continue;
485 : {
486 : struct TALER_PlanchetDetail pd;
487 : struct TALER_CRYPTO_CsSignRequest csr;
488 5 : struct TALER_CRYPTO_CsDeriveRequest cdr = {
489 5 : .h_cs = &keys[i].h_cs,
490 : .nonce = &nonce.cs_nonce
491 : };
492 :
493 5 : ec = TALER_CRYPTO_helper_cs_r_batch_derive (
494 : dh,
495 : 1,
496 : &cdr,
497 : false,
498 : &bi.details.cs_values);
499 5 : if (TALER_EC_NONE != ec)
500 4 : continue;
501 1 : TALER_planchet_setup_coin_priv (&ps,
502 : &alg_values,
503 : &coin_priv);
504 1 : TALER_planchet_blinding_secret_create (&ps,
505 : &alg_values,
506 : &bks);
507 1 : GNUNET_assert (GNUNET_YES ==
508 : TALER_planchet_prepare (&keys[i].denom_pub,
509 : &alg_values,
510 : &bks,
511 : &nonce,
512 : &coin_priv,
513 : NULL, /* no age commitment */
514 : &c_hash,
515 : &pd));
516 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
517 : "Requesting signature with key %s\n",
518 : GNUNET_h2s (&keys[i].h_cs.hash));
519 1 : csr.h_cs = &keys[i].h_cs;
520 : csr.blinded_planchet
521 1 : = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
522 1 : ec = TALER_CRYPTO_helper_cs_batch_sign (
523 : dh,
524 : 1,
525 : &csr,
526 : false,
527 : &ds);
528 1 : TALER_blinded_planchet_free (&pd.blinded_planchet);
529 : }
530 1 : switch (ec)
531 : {
532 1 : case TALER_EC_NONE:
533 1 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
534 : keys[i].start_time.abs_time),
535 : >,
536 : GNUNET_TIME_UNIT_SECONDS))
537 : {
538 : /* key worked too early */
539 0 : GNUNET_break (0);
540 0 : TALER_blinded_denom_sig_free (&ds);
541 0 : return 4;
542 : }
543 1 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
544 : keys[i].start_time.abs_time),
545 : >,
546 : keys[i].validity_duration))
547 : {
548 : /* key worked too later */
549 0 : GNUNET_break (0);
550 0 : TALER_blinded_denom_sig_free (&ds);
551 0 : return 5;
552 : }
553 : {
554 : struct TALER_FreshCoin coin;
555 :
556 1 : if (GNUNET_OK !=
557 1 : TALER_planchet_to_coin (&keys[i].denom_pub,
558 : &ds,
559 : &bks,
560 : &coin_priv,
561 : NULL, /* no age commitment */
562 : &c_hash,
563 : &alg_values,
564 : &coin))
565 : {
566 0 : GNUNET_break (0);
567 0 : TALER_blinded_denom_sig_free (&ds);
568 0 : return 6;
569 : }
570 1 : TALER_blinded_denom_sig_free (&ds);
571 1 : TALER_denom_sig_free (&coin.sig);
572 : }
573 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
574 : "Received valid signature for key %s\n",
575 : GNUNET_h2s (&keys[i].h_cs.hash));
576 1 : success = true;
577 1 : break;
578 0 : case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
579 : /* This 'failure' is expected, we're testing also for the
580 : error handling! */
581 0 : if ( (GNUNET_TIME_relative_is_zero (
582 : GNUNET_TIME_absolute_get_remaining (
583 0 : keys[i].start_time.abs_time))) &&
584 0 : (GNUNET_TIME_relative_cmp (
585 : GNUNET_TIME_absolute_get_duration (
586 : keys[i].start_time.abs_time),
587 : <,
588 : keys[i].validity_duration)) )
589 : {
590 : /* key should have worked! */
591 0 : GNUNET_break (0);
592 0 : return 6;
593 : }
594 0 : break;
595 0 : default:
596 : /* unexpected error */
597 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
598 : "Unexpected error %d\n",
599 : ec);
600 0 : return 7;
601 : }
602 : }
603 1 : if (! success)
604 : {
605 : /* no valid key for signing found, also bad */
606 0 : GNUNET_break (0);
607 0 : return 16;
608 : }
609 :
610 : /* check signing does not work if the key is unknown */
611 : {
612 : struct TALER_PlanchetDetail pd;
613 : struct TALER_CsPubHashP rnd;
614 : struct TALER_CRYPTO_CsSignRequest csr;
615 :
616 1 : GNUNET_CRYPTO_random_block (&rnd,
617 : sizeof (rnd));
618 1 : GNUNET_assert (GNUNET_YES ==
619 : TALER_planchet_prepare (&keys[0].denom_pub,
620 : &alg_values,
621 : &bks,
622 : &nonce,
623 : &coin_priv,
624 : NULL, /* no age commitment */
625 : &c_hash,
626 : &pd));
627 1 : csr.h_cs = &rnd;
628 : csr.blinded_planchet
629 1 : = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
630 1 : ec = TALER_CRYPTO_helper_cs_batch_sign (
631 : dh,
632 : 1,
633 : &csr,
634 : false,
635 : &ds);
636 1 : TALER_blinded_planchet_free (&pd.blinded_planchet);
637 1 : if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
638 : {
639 0 : if (TALER_EC_NONE == ec)
640 0 : TALER_blinded_denom_sig_free (&ds);
641 0 : GNUNET_break (0);
642 0 : return 17;
643 : }
644 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
645 : "Signing with invalid key %s failed as desired\n",
646 : GNUNET_h2s (&rnd.hash));
647 : }
648 1 : return 0;
649 : }
650 :
651 :
652 : /**
653 : * Test batch signing logic.
654 : *
655 : * @param dh handle to the helper
656 : * @param batch_size how large should the batch be
657 : * @param check_sigs also check unknown key and signatures
658 : * @return 0 on success
659 : */
660 : static int
661 6 : test_batch_signing (struct TALER_CRYPTO_CsDenominationHelper *dh,
662 : unsigned int batch_size,
663 : bool check_sigs)
664 6 : {
665 6 : struct TALER_BlindedDenominationSignature ds[batch_size];
666 : enum TALER_ErrorCode ec;
667 6 : bool success = false;
668 6 : struct TALER_PlanchetMasterSecretP ps[batch_size];
669 6 : struct TALER_CoinSpendPrivateKeyP coin_priv[batch_size];
670 6 : union GNUNET_CRYPTO_BlindingSecretP bks[batch_size];
671 6 : struct TALER_CoinPubHashP c_hash[batch_size];
672 6 : struct GNUNET_CRYPTO_BlindingInputValues bi[batch_size];
673 6 : struct TALER_ExchangeBlindingValues alg_values[batch_size];
674 6 : union GNUNET_CRYPTO_BlindSessionNonce nonces[batch_size];
675 :
676 6 : setup_withdraw_secrets (batch_size,
677 : false,
678 : ps,
679 : nonces);
680 6150 : for (unsigned int k = 0; k<MAX_KEYS; k++)
681 : {
682 6144 : if (! keys[k].valid)
683 6114 : continue;
684 30 : {
685 30 : struct TALER_PlanchetDetail pd[batch_size];
686 30 : struct TALER_CRYPTO_CsSignRequest csr[batch_size];
687 30 : struct TALER_CRYPTO_CsDeriveRequest cdr[batch_size];
688 30 : struct GNUNET_CRYPTO_CSPublicRPairP crps[batch_size];
689 :
690 785 : for (unsigned int i = 0; i<batch_size; i++)
691 : {
692 755 : cdr[i].h_cs = &keys[k].h_cs;
693 755 : cdr[i].nonce = &nonces[i].cs_nonce;
694 755 : bi[i].cipher = GNUNET_CRYPTO_BSA_CS;
695 755 : alg_values[i].blinding_inputs = &bi[i];
696 : }
697 30 : ec = TALER_CRYPTO_helper_cs_r_batch_derive (
698 : dh,
699 : batch_size,
700 : cdr,
701 : false,
702 : crps);
703 30 : if (TALER_EC_NONE != ec)
704 24 : continue;
705 157 : for (unsigned int i = 0; i<batch_size; i++)
706 : {
707 151 : bi[i].details.cs_values = crps[i];
708 151 : TALER_planchet_setup_coin_priv (&ps[i],
709 151 : &alg_values[i],
710 : &coin_priv[i]);
711 151 : TALER_planchet_blinding_secret_create (&ps[i],
712 151 : &alg_values[i],
713 : &bks[i]);
714 151 : GNUNET_assert (GNUNET_YES ==
715 : TALER_planchet_prepare (&keys[k].denom_pub,
716 : &alg_values[i],
717 : &bks[i],
718 : &nonces[i],
719 : &coin_priv[i],
720 : NULL, /* no age commitment */
721 : &c_hash[i],
722 : &pd[i]));
723 151 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
724 : "Requesting signature with key %s\n",
725 : GNUNET_h2s (&keys[k].h_cs.hash));
726 151 : csr[i].h_cs = &keys[k].h_cs;
727 : csr[i].blinded_planchet
728 151 : = &pd[i].blinded_planchet.blinded_message->details.cs_blinded_message;
729 : }
730 6 : ec = TALER_CRYPTO_helper_cs_batch_sign (
731 : dh,
732 : batch_size,
733 : csr,
734 : false,
735 : ds);
736 157 : for (unsigned int i = 0; i<batch_size; i++)
737 : {
738 151 : TALER_blinded_planchet_free (&pd[i].blinded_planchet);
739 : }
740 : }
741 6 : switch (ec)
742 : {
743 6 : case TALER_EC_NONE:
744 6 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
745 : keys[k].start_time.abs_time),
746 : >,
747 : GNUNET_TIME_UNIT_SECONDS))
748 : {
749 : /* key worked too early */
750 0 : GNUNET_break (0);
751 0 : return 4;
752 : }
753 6 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
754 : keys[k].start_time.abs_time),
755 : >,
756 : keys[k].validity_duration))
757 : {
758 : /* key worked too later */
759 0 : GNUNET_break (0);
760 0 : return 5;
761 : }
762 6 : if (check_sigs)
763 : {
764 68 : for (unsigned int i = 0; i<batch_size; i++)
765 : {
766 : struct TALER_FreshCoin coin;
767 :
768 66 : if (GNUNET_OK !=
769 66 : TALER_planchet_to_coin (&keys[k].denom_pub,
770 66 : &ds[i],
771 66 : &bks[i],
772 66 : &coin_priv[i],
773 : NULL, /* no age commitment */
774 66 : &c_hash[i],
775 66 : &alg_values[i],
776 : &coin))
777 : {
778 0 : GNUNET_break (0);
779 0 : return 6;
780 : }
781 66 : TALER_blinded_denom_sig_free (&ds[i]);
782 66 : TALER_denom_sig_free (&coin.sig);
783 : }
784 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
785 : "Received valid signature for key %s\n",
786 : GNUNET_h2s (&keys[k].h_cs.hash));
787 : }
788 : else
789 : {
790 89 : for (unsigned int i = 0; i<batch_size; i++)
791 85 : TALER_blinded_denom_sig_free (&ds[i]);
792 : }
793 6 : success = true;
794 6 : break;
795 0 : case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
796 : /* This 'failure' is expected, we're testing also for the
797 : error handling! */
798 0 : if ( (GNUNET_TIME_relative_is_zero (
799 : GNUNET_TIME_absolute_get_remaining (
800 0 : keys[k].start_time.abs_time))) &&
801 0 : (GNUNET_TIME_relative_cmp (
802 : GNUNET_TIME_absolute_get_duration (
803 : keys[k].start_time.abs_time),
804 : <,
805 : keys[k].validity_duration)) )
806 : {
807 : /* key should have worked! */
808 0 : GNUNET_break (0);
809 0 : return 6;
810 : }
811 0 : break;
812 0 : default:
813 : /* unexpected error */
814 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
815 : "Unexpected error %d\n",
816 : ec);
817 0 : return 7;
818 : }
819 : }
820 6 : if (! success)
821 : {
822 : /* no valid key for signing found, also bad */
823 0 : GNUNET_break (0);
824 0 : return 16;
825 : }
826 :
827 : /* check signing does not work if the key is unknown */
828 6 : if (check_sigs)
829 : {
830 : struct TALER_PlanchetDetail pd;
831 : struct TALER_CsPubHashP rnd;
832 : struct TALER_CRYPTO_CsSignRequest csr;
833 :
834 2 : GNUNET_CRYPTO_random_block (&rnd,
835 : sizeof (rnd));
836 2 : GNUNET_assert (GNUNET_YES ==
837 : TALER_planchet_prepare (&keys[0].denom_pub,
838 : &alg_values[0],
839 : &bks[0],
840 : &nonces[0],
841 : &coin_priv[0],
842 : NULL, /* no age commitment */
843 : &c_hash[0],
844 : &pd));
845 2 : csr.h_cs = &rnd;
846 : csr.blinded_planchet
847 2 : = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
848 2 : ec = TALER_CRYPTO_helper_cs_batch_sign (
849 : dh,
850 : 1,
851 : &csr,
852 : false,
853 : &ds[0]);
854 2 : TALER_blinded_planchet_free (&pd.blinded_planchet);
855 2 : if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
856 : {
857 0 : if (TALER_EC_NONE == ec)
858 0 : TALER_blinded_denom_sig_free (&ds[0]);
859 0 : GNUNET_break (0);
860 0 : return 17;
861 : }
862 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
863 : "Signing with invalid key %s failed as desired\n",
864 : GNUNET_h2s (&rnd.hash));
865 : }
866 6 : return 0;
867 : }
868 :
869 :
870 : /**
871 : * Benchmark signing logic.
872 : *
873 : * @param dh handle to the helper
874 : * @return 0 on success
875 : */
876 : static int
877 9 : perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh,
878 : const char *type)
879 : {
880 : struct TALER_BlindedDenominationSignature ds;
881 : enum TALER_ErrorCode ec;
882 : struct GNUNET_TIME_Relative duration;
883 : struct TALER_PlanchetMasterSecretP ps;
884 : struct TALER_CoinSpendPrivateKeyP coin_priv;
885 : union GNUNET_CRYPTO_BlindingSecretP bks;
886 9 : struct GNUNET_CRYPTO_BlindingInputValues bv = {
887 : .cipher = GNUNET_CRYPTO_BSA_CS
888 : };
889 9 : struct TALER_ExchangeBlindingValues alg_values = {
890 : .blinding_inputs = &bv
891 : };
892 : union GNUNET_CRYPTO_BlindSessionNonce nonce;
893 :
894 9 : setup_withdraw_secrets (1,
895 : true,
896 : &ps,
897 : &nonce);
898 9 : duration = GNUNET_TIME_UNIT_ZERO;
899 9 : TALER_CRYPTO_helper_cs_poll (dh);
900 18 : for (unsigned int j = 0; j<NUM_SIGN_PERFS;)
901 : {
902 9225 : for (unsigned int i = 0; i<MAX_KEYS; i++)
903 : {
904 9216 : if (! keys[i].valid)
905 9147 : continue;
906 69 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
907 : keys[i].start_time.abs_time),
908 : >,
909 : GNUNET_TIME_UNIT_SECONDS))
910 52 : continue;
911 17 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
912 : keys[i].start_time.abs_time),
913 : >,
914 : keys[i].validity_duration))
915 0 : continue;
916 : {
917 : struct TALER_CoinPubHashP c_hash;
918 : struct TALER_PlanchetDetail pd;
919 17 : struct TALER_CRYPTO_CsDeriveRequest cdr = {
920 17 : .h_cs = &keys[i].h_cs,
921 : .nonce = &nonce.cs_nonce
922 : };
923 :
924 17 : ec = TALER_CRYPTO_helper_cs_r_batch_derive (
925 : dh,
926 : 1,
927 : &cdr,
928 : true,
929 : &bv.details.cs_values);
930 17 : if (TALER_EC_NONE != ec)
931 8 : continue;
932 9 : TALER_planchet_setup_coin_priv (&ps,
933 : &alg_values,
934 : &coin_priv);
935 9 : TALER_planchet_blinding_secret_create (&ps,
936 : &alg_values,
937 : &bks);
938 9 : GNUNET_assert (GNUNET_YES ==
939 : TALER_planchet_prepare (&keys[i].denom_pub,
940 : &alg_values,
941 : &bks,
942 : &nonce,
943 : &coin_priv,
944 : NULL, /* no age commitment */
945 : &c_hash,
946 : &pd));
947 : /* use this key as long as it works */
948 : while (1)
949 891 : {
950 900 : struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get ();
951 : struct GNUNET_TIME_Relative delay;
952 : struct TALER_CRYPTO_CsSignRequest csr;
953 :
954 900 : csr.h_cs = &keys[i].h_cs;
955 : csr.blinded_planchet
956 900 : = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
957 900 : ec = TALER_CRYPTO_helper_cs_batch_sign (
958 : dh,
959 : 1,
960 : &csr,
961 : true,
962 : &ds);
963 900 : if (TALER_EC_NONE != ec)
964 0 : break;
965 900 : delay = GNUNET_TIME_absolute_get_duration (start);
966 900 : duration = GNUNET_TIME_relative_add (duration,
967 : delay);
968 900 : TALER_blinded_denom_sig_free (&ds);
969 900 : j++;
970 900 : if (NUM_SIGN_PERFS <= j)
971 9 : break;
972 : }
973 9 : TALER_blinded_planchet_free (&pd.blinded_planchet);
974 : }
975 : } /* for i */
976 : } /* for j */
977 9 : fprintf (stderr,
978 : "%u (%s) signature operations took %s\n",
979 : (unsigned int) NUM_SIGN_PERFS,
980 : type,
981 : GNUNET_STRINGS_relative_time_to_string (duration,
982 : GNUNET_YES));
983 9 : return 0;
984 : }
985 :
986 :
987 : /**
988 : * Parallel signing logic.
989 : *
990 : * @param esh handle to the helper
991 : * @return 0 on success
992 : */
993 : static int
994 1 : par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
995 : {
996 : struct GNUNET_TIME_Absolute start;
997 : struct GNUNET_TIME_Relative duration;
998 : pid_t pids[NUM_CORES];
999 : struct TALER_CRYPTO_CsDenominationHelper *dh;
1000 :
1001 1 : start = GNUNET_TIME_absolute_get ();
1002 9 : for (unsigned int i = 0; i<NUM_CORES; i++)
1003 : {
1004 8 : pids[i] = fork ();
1005 16 : num_keys = 0;
1006 16 : GNUNET_assert (-1 != pids[i]);
1007 16 : if (0 == pids[i])
1008 : {
1009 : int ret;
1010 :
1011 8 : dh = TALER_CRYPTO_helper_cs_connect (cfg,
1012 : "taler-exchange",
1013 : &key_cb,
1014 : NULL);
1015 8 : GNUNET_assert (NULL != dh);
1016 8 : ret = perf_signing (dh,
1017 : "parallel");
1018 8 : TALER_CRYPTO_helper_cs_disconnect (dh);
1019 8 : free_keys ();
1020 8 : exit (ret);
1021 : }
1022 : }
1023 9 : for (unsigned int i = 0; i<NUM_CORES; i++)
1024 : {
1025 : int wstatus;
1026 :
1027 8 : GNUNET_assert (pids[i] ==
1028 : waitpid (pids[i],
1029 : &wstatus,
1030 : 0));
1031 : }
1032 1 : duration = GNUNET_TIME_absolute_get_duration (start);
1033 1 : fprintf (stderr,
1034 : "%u (parallel) signature operations took %s (total real time)\n",
1035 : (unsigned int) NUM_SIGN_PERFS * NUM_CORES,
1036 : GNUNET_STRINGS_relative_time_to_string (duration,
1037 : GNUNET_YES));
1038 1 : return 0;
1039 : }
1040 :
1041 :
1042 : /**
1043 : * Main entry point into the test logic with the helper already running.
1044 : */
1045 : static int
1046 1 : run_test (void)
1047 : {
1048 : struct GNUNET_CONFIGURATION_Handle *cfg;
1049 : struct TALER_CRYPTO_CsDenominationHelper *dh;
1050 1 : struct timespec req = {
1051 : .tv_nsec = 250000000
1052 : };
1053 : int ret;
1054 :
1055 1 : cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ());
1056 1 : if (GNUNET_OK !=
1057 1 : GNUNET_CONFIGURATION_load (cfg,
1058 : "test_helper_cs.conf"))
1059 : {
1060 0 : GNUNET_break (0);
1061 0 : return 77;
1062 : }
1063 :
1064 1 : fprintf (stderr, "Waiting for helper to start ... ");
1065 1 : for (unsigned int i = 0; i<100; i++)
1066 : {
1067 1 : nanosleep (&req,
1068 : NULL);
1069 1 : dh = TALER_CRYPTO_helper_cs_connect (cfg,
1070 : "taler-exchange",
1071 : &key_cb,
1072 : NULL);
1073 1 : if (NULL != dh)
1074 1 : break;
1075 0 : fprintf (stderr, ".");
1076 : }
1077 1 : if (NULL == dh)
1078 : {
1079 0 : fprintf (stderr,
1080 : "\nFAILED: timeout trying to connect to helper\n");
1081 0 : GNUNET_CONFIGURATION_destroy (cfg);
1082 0 : return 1;
1083 : }
1084 1 : if (0 == num_keys)
1085 : {
1086 0 : fprintf (stderr,
1087 : "\nFAILED: timeout trying to connect to helper\n");
1088 0 : TALER_CRYPTO_helper_cs_disconnect (dh);
1089 0 : GNUNET_CONFIGURATION_destroy (cfg);
1090 0 : return 1;
1091 : }
1092 1 : fprintf (stderr,
1093 : " Done (%u keys)\n",
1094 : num_keys);
1095 1 : ret = 0;
1096 1 : if (0 == ret)
1097 1 : ret = test_revocation (dh);
1098 1 : if (0 == ret)
1099 1 : ret = test_r_derive (dh);
1100 1 : if (0 == ret)
1101 1 : ret = test_signing (dh);
1102 1 : if (0 == ret)
1103 1 : ret = test_batch_signing (dh,
1104 : 2,
1105 : true);
1106 1 : if (0 == ret)
1107 1 : ret = test_batch_signing (dh,
1108 : 64,
1109 : true);
1110 5 : for (unsigned int i = 0; i<4; i++)
1111 : {
1112 : static unsigned int batches[] = { 1, 4, 16, 64 };
1113 4 : unsigned int batch_size = batches[i];
1114 : struct GNUNET_TIME_Absolute start;
1115 : struct GNUNET_TIME_Relative duration;
1116 :
1117 4 : start = GNUNET_TIME_absolute_get ();
1118 4 : if (0 != ret)
1119 0 : break;
1120 4 : ret = test_batch_signing (dh,
1121 : batch_size,
1122 : false);
1123 4 : duration = GNUNET_TIME_absolute_get_duration (start);
1124 4 : fprintf (stderr,
1125 : "%4u (batch) signature operations took %s (total real time)\n",
1126 : (unsigned int) batch_size,
1127 : GNUNET_STRINGS_relative_time_to_string (duration,
1128 : GNUNET_YES));
1129 : }
1130 1 : if (0 == ret)
1131 1 : ret = perf_signing (dh,
1132 : "sequential");
1133 1 : TALER_CRYPTO_helper_cs_disconnect (dh);
1134 1 : free_keys ();
1135 1 : if (0 == ret)
1136 1 : ret = par_signing (cfg);
1137 : /* clean up our state */
1138 1 : GNUNET_CONFIGURATION_destroy (cfg);
1139 1 : return ret;
1140 : }
1141 :
1142 :
1143 : int
1144 1 : main (int argc,
1145 : const char *const argv[])
1146 : {
1147 : struct GNUNET_Process *helper;
1148 : char *libexec_dir;
1149 : char *binary_name;
1150 : int ret;
1151 : enum GNUNET_OS_ProcessStatusType type;
1152 : unsigned long code;
1153 1 : const char *loglev = "WARNING";
1154 :
1155 : (void) argc;
1156 : (void) argv;
1157 1 : unsetenv ("XDG_DATA_HOME");
1158 1 : unsetenv ("XDG_CONFIG_HOME");
1159 1 : GNUNET_log_setup ("test-helper-cs",
1160 : loglev,
1161 : NULL);
1162 1 : libexec_dir = GNUNET_OS_installation_get_path (TALER_EXCHANGE_project_data (),
1163 : GNUNET_OS_IPK_BINDIR);
1164 1 : GNUNET_asprintf (&binary_name,
1165 : "%s/%s",
1166 : libexec_dir,
1167 : "taler-exchange-secmod-cs");
1168 1 : GNUNET_free (libexec_dir);
1169 1 : helper = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
1170 1 : if (GNUNET_OK !=
1171 1 : GNUNET_process_run_command_va (helper,
1172 : binary_name,
1173 : binary_name,
1174 : "-c",
1175 : "test_helper_cs.conf",
1176 : "-L",
1177 : loglev,
1178 : NULL))
1179 : {
1180 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1181 : "exec",
1182 : binary_name);
1183 0 : GNUNET_process_destroy (helper);
1184 0 : GNUNET_free (binary_name);
1185 0 : return 77;
1186 : }
1187 1 : GNUNET_free (binary_name);
1188 1 : ret = run_test ();
1189 :
1190 1 : GNUNET_break (GNUNET_OK ==
1191 : GNUNET_process_kill (helper,
1192 : SIGTERM));
1193 1 : if (GNUNET_OK !=
1194 1 : GNUNET_process_wait (helper,
1195 : true,
1196 : &type,
1197 : &code))
1198 : {
1199 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1200 : "Helper process did not die voluntarily, killing hard\n");
1201 0 : GNUNET_break (GNUNET_OK ==
1202 : GNUNET_process_kill (helper,
1203 : SIGKILL));
1204 0 : ret = 4;
1205 : }
1206 1 : else if ( (GNUNET_OS_PROCESS_EXITED != type) ||
1207 1 : (0 != code) )
1208 : {
1209 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1210 : "Helper died with unexpected status %d/%d\n",
1211 : (int) type,
1212 : (int) code);
1213 0 : ret = 5;
1214 : }
1215 1 : GNUNET_process_destroy (helper);
1216 1 : return ret;
1217 : }
1218 :
1219 :
1220 : /* end of test_helper_cs.c */
|