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 10 : for (unsigned int i = 0; i<MAX_KEYS; i++)
161 10 : 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 313 : for (unsigned int i = 0; i<MAX_KEYS; i++)
181 313 : 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 (GNUNET_CRYPTO_QUALITY_WEAK,
217 : num_keys);
218 : /* find index of key to revoke */
219 10 : for (unsigned int j = 0; j < MAX_KEYS; j++)
220 : {
221 10 : if (! keys[j].valid)
222 0 : continue;
223 10 : if (0 != off)
224 : {
225 7 : off--;
226 7 : continue;
227 : }
228 3 : keys[j].revoked = true;
229 3 : fprintf (stderr,
230 : "Revoking key %s ...",
231 3 : GNUNET_h2s (&keys[j].h_cs.hash));
232 3 : TALER_CRYPTO_helper_cs_revoke (dh,
233 3 : &keys[j].h_cs);
234 6 : for (unsigned int k = 0; k<1000; k++)
235 : {
236 6 : TALER_CRYPTO_helper_cs_poll (dh);
237 6 : if (! keys[j].revoked)
238 3 : break;
239 3 : nanosleep (&req, NULL);
240 3 : fprintf (stderr, ".");
241 : }
242 3 : if (keys[j].revoked)
243 : {
244 0 : fprintf (stderr,
245 : "\nFAILED: timeout trying to revoke key %u\n",
246 : j);
247 0 : TALER_CRYPTO_helper_cs_disconnect (dh);
248 0 : return 2;
249 : }
250 3 : fprintf (stderr, "\n");
251 3 : break;
252 : }
253 : }
254 1 : return 0;
255 : }
256 :
257 :
258 : /**
259 : * Test R derivation logic.
260 : *
261 : * @param dh handle to the helper
262 : * @return 0 on success
263 : */
264 : static int
265 1 : test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh)
266 : {
267 : enum TALER_ErrorCode ec;
268 1 : bool success = false;
269 : struct TALER_PlanchetMasterSecretP ps;
270 : struct TALER_CoinSpendPrivateKeyP coin_priv;
271 : union GNUNET_CRYPTO_BlindingSecretP bks;
272 : struct TALER_CoinPubHashP c_hash;
273 1 : struct GNUNET_CRYPTO_BlindingInputValues bi = {
274 : .cipher = GNUNET_CRYPTO_BSA_CS
275 : };
276 1 : struct TALER_ExchangeBlindingValues alg_values = {
277 : .blinding_inputs = &bi
278 : };
279 : union GNUNET_CRYPTO_BlindSessionNonce nonce;
280 :
281 1 : TALER_planchet_master_setup_random (&ps);
282 1025 : for (unsigned int i = 0; i<MAX_KEYS; i++)
283 : {
284 : struct TALER_PlanchetDetail pd;
285 :
286 1024 : if (! keys[i].valid)
287 1019 : continue;
288 5 : GNUNET_assert (GNUNET_CRYPTO_BSA_CS ==
289 : keys[i].denom_pub.bsign_pub_key->cipher);
290 : #pragma message "phase out TALER_cs_withdraw_nonce_derive"
291 5 : TALER_cs_withdraw_nonce_derive (
292 : &ps,
293 : &nonce.cs_nonce);
294 5 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
295 : "Requesting R derivation with key %s\n",
296 : GNUNET_h2s (&keys[i].h_cs.hash));
297 : {
298 5 : struct TALER_CRYPTO_CsDeriveRequest cdr = {
299 5 : .h_cs = &keys[i].h_cs,
300 : .nonce = &nonce.cs_nonce
301 : };
302 :
303 5 : ec = TALER_CRYPTO_helper_cs_r_batch_derive (
304 : dh,
305 : 1,
306 : &cdr,
307 : false,
308 : &bi.details.cs_values);
309 : }
310 5 : switch (ec)
311 : {
312 1 : case TALER_EC_NONE:
313 1 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
314 : keys[i].start_time.abs_time),
315 : >,
316 : GNUNET_TIME_UNIT_SECONDS))
317 : {
318 : /* key worked too early */
319 0 : GNUNET_break (0);
320 0 : return 4;
321 : }
322 1 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
323 : keys[i].start_time.abs_time),
324 : >,
325 : keys[i].validity_duration))
326 : {
327 : /* key worked too later */
328 0 : GNUNET_break (0);
329 0 : return 5;
330 : }
331 :
332 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
333 : "Received valid R for key %s\n",
334 : GNUNET_h2s (&keys[i].h_cs.hash));
335 1 : TALER_planchet_setup_coin_priv (&ps,
336 : &alg_values,
337 : &coin_priv);
338 1 : TALER_planchet_blinding_secret_create (&ps,
339 : &alg_values,
340 : &bks);
341 1 : GNUNET_assert (GNUNET_OK ==
342 : TALER_planchet_prepare (&keys[i].denom_pub,
343 : &alg_values,
344 : &bks,
345 : &nonce,
346 : &coin_priv,
347 : NULL, /* no age commitment */
348 : &c_hash,
349 : &pd));
350 1 : TALER_blinded_planchet_free (&pd.blinded_planchet);
351 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
352 : "Successfully prepared planchet");
353 1 : success = true;
354 1 : break;
355 4 : case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
356 : /* This 'failure' is expected, we're testing also for the
357 : error handling! */
358 4 : if ( (GNUNET_TIME_relative_is_zero (
359 : GNUNET_TIME_absolute_get_remaining (
360 0 : keys[i].start_time.abs_time))) &&
361 0 : (GNUNET_TIME_relative_cmp (
362 : GNUNET_TIME_absolute_get_duration (
363 : keys[i].start_time.abs_time),
364 : <,
365 : keys[i].validity_duration)) )
366 : {
367 : /* key should have worked! */
368 0 : GNUNET_break (0);
369 0 : return 6;
370 : }
371 4 : break;
372 0 : default:
373 : /* unexpected error */
374 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
375 : "Unexpected error %d\n",
376 : ec);
377 0 : return 7;
378 : }
379 : }
380 1 : if (! success)
381 : {
382 : /* no valid key for signing found, also bad */
383 0 : GNUNET_break (0);
384 0 : return 16;
385 : }
386 :
387 : /* check R derivation does not work if the key is unknown */
388 : {
389 : struct TALER_CsPubHashP rnd;
390 : struct GNUNET_CRYPTO_CSPublicRPairP crp;
391 1 : struct TALER_CRYPTO_CsDeriveRequest cdr = {
392 : .h_cs = &rnd,
393 : .nonce = &nonce.cs_nonce,
394 : };
395 :
396 1 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
397 : &rnd,
398 : sizeof (rnd));
399 1 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
400 : &nonce,
401 : sizeof (nonce));
402 1 : ec = TALER_CRYPTO_helper_cs_r_batch_derive (dh,
403 : 1,
404 : &cdr,
405 : false,
406 : &crp);
407 1 : if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
408 : {
409 0 : GNUNET_break (0);
410 0 : return 17;
411 : }
412 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
413 : "R derivation with invalid key %s failed as desired\n",
414 : GNUNET_h2s (&rnd.hash));
415 : }
416 1 : return 0;
417 : }
418 :
419 :
420 : /**
421 : * Test signing logic.
422 : *
423 : * @param dh handle to the helper
424 : * @return 0 on success
425 : */
426 : static int
427 1 : test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh)
428 : {
429 : struct TALER_BlindedDenominationSignature ds;
430 : enum TALER_ErrorCode ec;
431 1 : bool success = false;
432 : struct TALER_PlanchetMasterSecretP ps;
433 : struct TALER_CoinSpendPrivateKeyP coin_priv;
434 : union GNUNET_CRYPTO_BlindingSecretP bks;
435 : struct TALER_CoinPubHashP c_hash;
436 1 : struct GNUNET_CRYPTO_BlindingInputValues bi = {
437 : .cipher = GNUNET_CRYPTO_BSA_CS
438 : };
439 1 : struct TALER_ExchangeBlindingValues alg_values = {
440 : .blinding_inputs = &bi
441 : };
442 : union GNUNET_CRYPTO_BlindSessionNonce nonce;
443 :
444 1 : TALER_planchet_master_setup_random (&ps);
445 1025 : for (unsigned int i = 0; i<MAX_KEYS; i++)
446 : {
447 1024 : if (! keys[i].valid)
448 1019 : continue;
449 : {
450 : struct TALER_PlanchetDetail pd;
451 : struct TALER_CRYPTO_CsSignRequest csr;
452 5 : struct TALER_CRYPTO_CsDeriveRequest cdr = {
453 5 : .h_cs = &keys[i].h_cs,
454 : .nonce = &nonce.cs_nonce
455 : };
456 :
457 5 : TALER_cs_withdraw_nonce_derive (&ps,
458 : &nonce.cs_nonce);
459 5 : ec = TALER_CRYPTO_helper_cs_r_batch_derive (
460 : dh,
461 : 1,
462 : &cdr,
463 : false,
464 : &bi.details.cs_values);
465 5 : if (TALER_EC_NONE != ec)
466 4 : continue;
467 1 : TALER_planchet_setup_coin_priv (&ps,
468 : &alg_values,
469 : &coin_priv);
470 1 : TALER_planchet_blinding_secret_create (&ps,
471 : &alg_values,
472 : &bks);
473 1 : GNUNET_assert (GNUNET_YES ==
474 : TALER_planchet_prepare (&keys[i].denom_pub,
475 : &alg_values,
476 : &bks,
477 : &nonce,
478 : &coin_priv,
479 : NULL, /* no age commitment */
480 : &c_hash,
481 : &pd));
482 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
483 : "Requesting signature with key %s\n",
484 : GNUNET_h2s (&keys[i].h_cs.hash));
485 1 : csr.h_cs = &keys[i].h_cs;
486 : csr.blinded_planchet
487 1 : = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
488 1 : ec = TALER_CRYPTO_helper_cs_batch_sign (
489 : dh,
490 : 1,
491 : &csr,
492 : false,
493 : &ds);
494 1 : TALER_blinded_planchet_free (&pd.blinded_planchet);
495 : }
496 1 : switch (ec)
497 : {
498 1 : case TALER_EC_NONE:
499 1 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
500 : keys[i].start_time.abs_time),
501 : >,
502 : GNUNET_TIME_UNIT_SECONDS))
503 : {
504 : /* key worked too early */
505 0 : GNUNET_break (0);
506 0 : TALER_blinded_denom_sig_free (&ds);
507 0 : return 4;
508 : }
509 1 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
510 : keys[i].start_time.abs_time),
511 : >,
512 : keys[i].validity_duration))
513 : {
514 : /* key worked too later */
515 0 : GNUNET_break (0);
516 0 : TALER_blinded_denom_sig_free (&ds);
517 0 : return 5;
518 : }
519 : {
520 : struct TALER_FreshCoin coin;
521 :
522 1 : if (GNUNET_OK !=
523 1 : TALER_planchet_to_coin (&keys[i].denom_pub,
524 : &ds,
525 : &bks,
526 : &coin_priv,
527 : NULL, /* no age commitment */
528 : &c_hash,
529 : &alg_values,
530 : &coin))
531 : {
532 0 : GNUNET_break (0);
533 0 : TALER_blinded_denom_sig_free (&ds);
534 0 : return 6;
535 : }
536 1 : TALER_blinded_denom_sig_free (&ds);
537 1 : TALER_denom_sig_free (&coin.sig);
538 : }
539 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
540 : "Received valid signature for key %s\n",
541 : GNUNET_h2s (&keys[i].h_cs.hash));
542 1 : success = true;
543 1 : break;
544 0 : case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
545 : /* This 'failure' is expected, we're testing also for the
546 : error handling! */
547 0 : if ( (GNUNET_TIME_relative_is_zero (
548 : GNUNET_TIME_absolute_get_remaining (
549 0 : keys[i].start_time.abs_time))) &&
550 0 : (GNUNET_TIME_relative_cmp (
551 : GNUNET_TIME_absolute_get_duration (
552 : keys[i].start_time.abs_time),
553 : <,
554 : keys[i].validity_duration)) )
555 : {
556 : /* key should have worked! */
557 0 : GNUNET_break (0);
558 0 : return 6;
559 : }
560 0 : break;
561 0 : default:
562 : /* unexpected error */
563 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
564 : "Unexpected error %d\n",
565 : ec);
566 0 : return 7;
567 : }
568 : }
569 1 : if (! success)
570 : {
571 : /* no valid key for signing found, also bad */
572 0 : GNUNET_break (0);
573 0 : return 16;
574 : }
575 :
576 : /* check signing does not work if the key is unknown */
577 : {
578 : struct TALER_PlanchetDetail pd;
579 : struct TALER_CsPubHashP rnd;
580 : struct TALER_CRYPTO_CsSignRequest csr;
581 :
582 1 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
583 : &rnd,
584 : sizeof (rnd));
585 1 : GNUNET_assert (GNUNET_YES ==
586 : TALER_planchet_prepare (&keys[0].denom_pub,
587 : &alg_values,
588 : &bks,
589 : &nonce,
590 : &coin_priv,
591 : NULL, /* no age commitment */
592 : &c_hash,
593 : &pd));
594 1 : csr.h_cs = &rnd;
595 : csr.blinded_planchet
596 1 : = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
597 1 : ec = TALER_CRYPTO_helper_cs_batch_sign (
598 : dh,
599 : 1,
600 : &csr,
601 : false,
602 : &ds);
603 1 : TALER_blinded_planchet_free (&pd.blinded_planchet);
604 1 : if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
605 : {
606 0 : if (TALER_EC_NONE == ec)
607 0 : TALER_blinded_denom_sig_free (&ds);
608 0 : GNUNET_break (0);
609 0 : return 17;
610 : }
611 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
612 : "Signing with invalid key %s failed as desired\n",
613 : GNUNET_h2s (&rnd.hash));
614 : }
615 1 : return 0;
616 : }
617 :
618 :
619 : /**
620 : * Test batch signing logic.
621 : *
622 : * @param dh handle to the helper
623 : * @param batch_size how large should the batch be
624 : * @param check_sigs also check unknown key and signatures
625 : * @return 0 on success
626 : */
627 : static int
628 6 : test_batch_signing (struct TALER_CRYPTO_CsDenominationHelper *dh,
629 : unsigned int batch_size,
630 : bool check_sigs)
631 6 : {
632 6 : struct TALER_BlindedDenominationSignature ds[batch_size];
633 : enum TALER_ErrorCode ec;
634 6 : bool success = false;
635 6 : struct TALER_PlanchetMasterSecretP ps[batch_size];
636 6 : struct TALER_CoinSpendPrivateKeyP coin_priv[batch_size];
637 6 : union GNUNET_CRYPTO_BlindingSecretP bks[batch_size];
638 6 : struct TALER_CoinPubHashP c_hash[batch_size];
639 6 : struct GNUNET_CRYPTO_BlindingInputValues bi[batch_size];
640 6 : struct TALER_ExchangeBlindingValues alg_values[batch_size];
641 6 : union GNUNET_CRYPTO_BlindSessionNonce nonces[batch_size];
642 :
643 157 : for (unsigned int i = 0; i<batch_size; i++)
644 151 : TALER_planchet_master_setup_random (&ps[i]);
645 6150 : for (unsigned int k = 0; k<MAX_KEYS; k++)
646 : {
647 6144 : if (! keys[k].valid)
648 6114 : continue;
649 30 : {
650 30 : struct TALER_PlanchetDetail pd[batch_size];
651 30 : struct TALER_CRYPTO_CsSignRequest csr[batch_size];
652 30 : struct TALER_CRYPTO_CsDeriveRequest cdr[batch_size];
653 30 : struct GNUNET_CRYPTO_CSPublicRPairP crps[batch_size];
654 :
655 785 : for (unsigned int i = 0; i<batch_size; i++)
656 : {
657 755 : cdr[i].h_cs = &keys[k].h_cs;
658 755 : cdr[i].nonce = &nonces[i].cs_nonce;
659 755 : TALER_cs_withdraw_nonce_derive (
660 755 : &ps[i],
661 : &nonces[i].cs_nonce);
662 755 : bi[i].cipher = GNUNET_CRYPTO_BSA_CS;
663 755 : alg_values[i].blinding_inputs = &bi[i];
664 : }
665 30 : ec = TALER_CRYPTO_helper_cs_r_batch_derive (
666 : dh,
667 : batch_size,
668 : cdr,
669 : false,
670 : crps);
671 30 : if (TALER_EC_NONE != ec)
672 24 : continue;
673 157 : for (unsigned int i = 0; i<batch_size; i++)
674 : {
675 151 : bi[i].details.cs_values = crps[i];
676 151 : TALER_planchet_setup_coin_priv (&ps[i],
677 151 : &alg_values[i],
678 : &coin_priv[i]);
679 151 : TALER_planchet_blinding_secret_create (&ps[i],
680 151 : &alg_values[i],
681 : &bks[i]);
682 151 : GNUNET_assert (GNUNET_YES ==
683 : TALER_planchet_prepare (&keys[k].denom_pub,
684 : &alg_values[i],
685 : &bks[i],
686 : &nonces[i],
687 : &coin_priv[i],
688 : NULL, /* no age commitment */
689 : &c_hash[i],
690 : &pd[i]));
691 151 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
692 : "Requesting signature with key %s\n",
693 : GNUNET_h2s (&keys[k].h_cs.hash));
694 151 : csr[i].h_cs = &keys[k].h_cs;
695 : csr[i].blinded_planchet
696 151 : = &pd[i].blinded_planchet.blinded_message->details.cs_blinded_message;
697 : }
698 6 : ec = TALER_CRYPTO_helper_cs_batch_sign (
699 : dh,
700 : batch_size,
701 : csr,
702 : false,
703 : ds);
704 157 : for (unsigned int i = 0; i<batch_size; i++)
705 : {
706 151 : TALER_blinded_planchet_free (&pd[i].blinded_planchet);
707 : }
708 : }
709 6 : switch (ec)
710 : {
711 6 : case TALER_EC_NONE:
712 6 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
713 : keys[k].start_time.abs_time),
714 : >,
715 : GNUNET_TIME_UNIT_SECONDS))
716 : {
717 : /* key worked too early */
718 0 : GNUNET_break (0);
719 0 : return 4;
720 : }
721 6 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
722 : keys[k].start_time.abs_time),
723 : >,
724 : keys[k].validity_duration))
725 : {
726 : /* key worked too later */
727 0 : GNUNET_break (0);
728 0 : return 5;
729 : }
730 6 : if (check_sigs)
731 : {
732 68 : for (unsigned int i = 0; i<batch_size; i++)
733 : {
734 : struct TALER_FreshCoin coin;
735 :
736 66 : if (GNUNET_OK !=
737 66 : TALER_planchet_to_coin (&keys[k].denom_pub,
738 66 : &ds[i],
739 66 : &bks[i],
740 66 : &coin_priv[i],
741 : NULL, /* no age commitment */
742 66 : &c_hash[i],
743 66 : &alg_values[i],
744 : &coin))
745 : {
746 0 : GNUNET_break (0);
747 0 : return 6;
748 : }
749 66 : TALER_blinded_denom_sig_free (&ds[i]);
750 66 : TALER_denom_sig_free (&coin.sig);
751 : }
752 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
753 : "Received valid signature for key %s\n",
754 : GNUNET_h2s (&keys[k].h_cs.hash));
755 : }
756 : else
757 : {
758 89 : for (unsigned int i = 0; i<batch_size; i++)
759 85 : TALER_blinded_denom_sig_free (&ds[i]);
760 : }
761 6 : success = true;
762 6 : break;
763 0 : case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
764 : /* This 'failure' is expected, we're testing also for the
765 : error handling! */
766 0 : if ( (GNUNET_TIME_relative_is_zero (
767 : GNUNET_TIME_absolute_get_remaining (
768 0 : keys[k].start_time.abs_time))) &&
769 0 : (GNUNET_TIME_relative_cmp (
770 : GNUNET_TIME_absolute_get_duration (
771 : keys[k].start_time.abs_time),
772 : <,
773 : keys[k].validity_duration)) )
774 : {
775 : /* key should have worked! */
776 0 : GNUNET_break (0);
777 0 : return 6;
778 : }
779 0 : break;
780 0 : default:
781 : /* unexpected error */
782 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
783 : "Unexpected error %d\n",
784 : ec);
785 0 : return 7;
786 : }
787 : }
788 6 : if (! success)
789 : {
790 : /* no valid key for signing found, also bad */
791 0 : GNUNET_break (0);
792 0 : return 16;
793 : }
794 :
795 : /* check signing does not work if the key is unknown */
796 6 : if (check_sigs)
797 : {
798 : struct TALER_PlanchetDetail pd;
799 : struct TALER_CsPubHashP rnd;
800 : struct TALER_CRYPTO_CsSignRequest csr;
801 :
802 2 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
803 : &rnd,
804 : sizeof (rnd));
805 2 : GNUNET_assert (GNUNET_YES ==
806 : TALER_planchet_prepare (&keys[0].denom_pub,
807 : &alg_values[0],
808 : &bks[0],
809 : &nonces[0],
810 : &coin_priv[0],
811 : NULL, /* no age commitment */
812 : &c_hash[0],
813 : &pd));
814 2 : csr.h_cs = &rnd;
815 : csr.blinded_planchet
816 2 : = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
817 2 : ec = TALER_CRYPTO_helper_cs_batch_sign (
818 : dh,
819 : 1,
820 : &csr,
821 : false,
822 : &ds[0]);
823 2 : TALER_blinded_planchet_free (&pd.blinded_planchet);
824 2 : if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
825 : {
826 0 : if (TALER_EC_NONE == ec)
827 0 : TALER_blinded_denom_sig_free (&ds[0]);
828 0 : GNUNET_break (0);
829 0 : return 17;
830 : }
831 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
832 : "Signing with invalid key %s failed as desired\n",
833 : GNUNET_h2s (&rnd.hash));
834 : }
835 6 : return 0;
836 : }
837 :
838 :
839 : /**
840 : * Benchmark signing logic.
841 : *
842 : * @param dh handle to the helper
843 : * @return 0 on success
844 : */
845 : static int
846 9 : perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh,
847 : const char *type)
848 : {
849 : struct TALER_BlindedDenominationSignature ds;
850 : enum TALER_ErrorCode ec;
851 : struct GNUNET_TIME_Relative duration;
852 : struct TALER_PlanchetMasterSecretP ps;
853 : struct TALER_CoinSpendPrivateKeyP coin_priv;
854 : union GNUNET_CRYPTO_BlindingSecretP bks;
855 9 : struct GNUNET_CRYPTO_BlindingInputValues bv = {
856 : .cipher = GNUNET_CRYPTO_BSA_CS
857 : };
858 9 : struct TALER_ExchangeBlindingValues alg_values = {
859 : .blinding_inputs = &bv
860 : };
861 :
862 9 : TALER_planchet_master_setup_random (&ps);
863 9 : duration = GNUNET_TIME_UNIT_ZERO;
864 9 : TALER_CRYPTO_helper_cs_poll (dh);
865 18 : for (unsigned int j = 0; j<NUM_SIGN_PERFS;)
866 : {
867 9225 : for (unsigned int i = 0; i<MAX_KEYS; i++)
868 : {
869 9216 : if (! keys[i].valid)
870 9147 : continue;
871 69 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
872 : keys[i].start_time.abs_time),
873 : >,
874 : GNUNET_TIME_UNIT_SECONDS))
875 60 : continue;
876 9 : if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
877 : keys[i].start_time.abs_time),
878 : >,
879 : keys[i].validity_duration))
880 0 : continue;
881 : {
882 : struct TALER_CoinPubHashP c_hash;
883 : struct TALER_PlanchetDetail pd;
884 : union GNUNET_CRYPTO_BlindSessionNonce nonce;
885 9 : struct TALER_CRYPTO_CsDeriveRequest cdr = {
886 9 : .h_cs = &keys[i].h_cs,
887 : .nonce = &nonce.cs_nonce
888 : };
889 :
890 9 : TALER_cs_withdraw_nonce_derive (
891 : &ps,
892 : &nonce.cs_nonce);
893 9 : ec = TALER_CRYPTO_helper_cs_r_batch_derive (
894 : dh,
895 : 1,
896 : &cdr,
897 : true,
898 : &bv.details.cs_values);
899 9 : if (TALER_EC_NONE != ec)
900 0 : continue;
901 9 : TALER_planchet_setup_coin_priv (&ps,
902 : &alg_values,
903 : &coin_priv);
904 9 : TALER_planchet_blinding_secret_create (&ps,
905 : &alg_values,
906 : &bks);
907 9 : GNUNET_assert (GNUNET_YES ==
908 : TALER_planchet_prepare (&keys[i].denom_pub,
909 : &alg_values,
910 : &bks,
911 : &nonce,
912 : &coin_priv,
913 : NULL, /* no age commitment */
914 : &c_hash,
915 : &pd));
916 : /* use this key as long as it works */
917 : while (1)
918 891 : {
919 900 : struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get ();
920 : struct GNUNET_TIME_Relative delay;
921 : struct TALER_CRYPTO_CsSignRequest csr;
922 :
923 900 : csr.h_cs = &keys[i].h_cs;
924 : csr.blinded_planchet
925 900 : = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
926 900 : ec = TALER_CRYPTO_helper_cs_batch_sign (
927 : dh,
928 : 1,
929 : &csr,
930 : true,
931 : &ds);
932 900 : if (TALER_EC_NONE != ec)
933 0 : break;
934 900 : delay = GNUNET_TIME_absolute_get_duration (start);
935 900 : duration = GNUNET_TIME_relative_add (duration,
936 : delay);
937 900 : TALER_blinded_denom_sig_free (&ds);
938 900 : j++;
939 900 : if (NUM_SIGN_PERFS <= j)
940 9 : break;
941 : }
942 9 : TALER_blinded_planchet_free (&pd.blinded_planchet);
943 : }
944 : } /* for i */
945 : } /* for j */
946 9 : fprintf (stderr,
947 : "%u (%s) signature operations took %s\n",
948 : (unsigned int) NUM_SIGN_PERFS,
949 : type,
950 : GNUNET_STRINGS_relative_time_to_string (duration,
951 : GNUNET_YES));
952 9 : return 0;
953 : }
954 :
955 :
956 : /**
957 : * Parallel signing logic.
958 : *
959 : * @param esh handle to the helper
960 : * @return 0 on success
961 : */
962 : static int
963 1 : par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
964 : {
965 : struct GNUNET_TIME_Absolute start;
966 : struct GNUNET_TIME_Relative duration;
967 : pid_t pids[NUM_CORES];
968 : struct TALER_CRYPTO_CsDenominationHelper *dh;
969 :
970 1 : start = GNUNET_TIME_absolute_get ();
971 9 : for (unsigned int i = 0; i<NUM_CORES; i++)
972 : {
973 8 : pids[i] = fork ();
974 16 : num_keys = 0;
975 16 : GNUNET_assert (-1 != pids[i]);
976 16 : if (0 == pids[i])
977 : {
978 : int ret;
979 :
980 8 : dh = TALER_CRYPTO_helper_cs_connect (cfg,
981 : "taler-exchange",
982 : &key_cb,
983 : NULL);
984 8 : GNUNET_assert (NULL != dh);
985 8 : ret = perf_signing (dh,
986 : "parallel");
987 8 : TALER_CRYPTO_helper_cs_disconnect (dh);
988 8 : free_keys ();
989 8 : exit (ret);
990 : }
991 : }
992 9 : for (unsigned int i = 0; i<NUM_CORES; i++)
993 : {
994 : int wstatus;
995 :
996 8 : GNUNET_assert (pids[i] ==
997 : waitpid (pids[i],
998 : &wstatus,
999 : 0));
1000 : }
1001 1 : duration = GNUNET_TIME_absolute_get_duration (start);
1002 1 : fprintf (stderr,
1003 : "%u (parallel) signature operations took %s (total real time)\n",
1004 : (unsigned int) NUM_SIGN_PERFS * NUM_CORES,
1005 : GNUNET_STRINGS_relative_time_to_string (duration,
1006 : GNUNET_YES));
1007 1 : return 0;
1008 : }
1009 :
1010 :
1011 : /**
1012 : * Main entry point into the test logic with the helper already running.
1013 : */
1014 : static int
1015 1 : run_test (void)
1016 : {
1017 : struct GNUNET_CONFIGURATION_Handle *cfg;
1018 : struct TALER_CRYPTO_CsDenominationHelper *dh;
1019 1 : struct timespec req = {
1020 : .tv_nsec = 250000000
1021 : };
1022 : int ret;
1023 :
1024 1 : cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ());
1025 1 : if (GNUNET_OK !=
1026 1 : GNUNET_CONFIGURATION_load (cfg,
1027 : "test_helper_cs.conf"))
1028 : {
1029 0 : GNUNET_break (0);
1030 0 : return 77;
1031 : }
1032 :
1033 1 : fprintf (stderr, "Waiting for helper to start ... ");
1034 1 : for (unsigned int i = 0; i<100; i++)
1035 : {
1036 1 : nanosleep (&req,
1037 : NULL);
1038 1 : dh = TALER_CRYPTO_helper_cs_connect (cfg,
1039 : "taler-exchange",
1040 : &key_cb,
1041 : NULL);
1042 1 : if (NULL != dh)
1043 1 : break;
1044 0 : fprintf (stderr, ".");
1045 : }
1046 1 : if (NULL == dh)
1047 : {
1048 0 : fprintf (stderr,
1049 : "\nFAILED: timeout trying to connect to helper\n");
1050 0 : GNUNET_CONFIGURATION_destroy (cfg);
1051 0 : return 1;
1052 : }
1053 1 : if (0 == num_keys)
1054 : {
1055 0 : fprintf (stderr,
1056 : "\nFAILED: timeout trying to connect to helper\n");
1057 0 : TALER_CRYPTO_helper_cs_disconnect (dh);
1058 0 : GNUNET_CONFIGURATION_destroy (cfg);
1059 0 : return 1;
1060 : }
1061 1 : fprintf (stderr,
1062 : " Done (%u keys)\n",
1063 : num_keys);
1064 1 : ret = 0;
1065 1 : if (0 == ret)
1066 1 : ret = test_revocation (dh);
1067 1 : if (0 == ret)
1068 1 : ret = test_r_derive (dh);
1069 1 : if (0 == ret)
1070 1 : ret = test_signing (dh);
1071 1 : if (0 == ret)
1072 1 : ret = test_batch_signing (dh,
1073 : 2,
1074 : true);
1075 1 : if (0 == ret)
1076 1 : ret = test_batch_signing (dh,
1077 : 64,
1078 : true);
1079 5 : for (unsigned int i = 0; i<4; i++)
1080 : {
1081 : static unsigned int batches[] = { 1, 4, 16, 64 };
1082 4 : unsigned int batch_size = batches[i];
1083 : struct GNUNET_TIME_Absolute start;
1084 : struct GNUNET_TIME_Relative duration;
1085 :
1086 4 : start = GNUNET_TIME_absolute_get ();
1087 4 : if (0 != ret)
1088 0 : break;
1089 4 : ret = test_batch_signing (dh,
1090 : batch_size,
1091 : false);
1092 4 : duration = GNUNET_TIME_absolute_get_duration (start);
1093 4 : fprintf (stderr,
1094 : "%4u (batch) signature operations took %s (total real time)\n",
1095 : (unsigned int) batch_size,
1096 : GNUNET_STRINGS_relative_time_to_string (duration,
1097 : GNUNET_YES));
1098 : }
1099 1 : if (0 == ret)
1100 1 : ret = perf_signing (dh,
1101 : "sequential");
1102 1 : TALER_CRYPTO_helper_cs_disconnect (dh);
1103 1 : free_keys ();
1104 1 : if (0 == ret)
1105 1 : ret = par_signing (cfg);
1106 : /* clean up our state */
1107 1 : GNUNET_CONFIGURATION_destroy (cfg);
1108 1 : return ret;
1109 : }
1110 :
1111 :
1112 : int
1113 1 : main (int argc,
1114 : const char *const argv[])
1115 : {
1116 : struct GNUNET_Process *helper;
1117 : char *libexec_dir;
1118 : char *binary_name;
1119 : int ret;
1120 : enum GNUNET_OS_ProcessStatusType type;
1121 : unsigned long code;
1122 1 : const char *loglev = "WARNING";
1123 :
1124 : (void) argc;
1125 : (void) argv;
1126 1 : unsetenv ("XDG_DATA_HOME");
1127 1 : unsetenv ("XDG_CONFIG_HOME");
1128 1 : GNUNET_log_setup ("test-helper-cs",
1129 : loglev,
1130 : NULL);
1131 1 : libexec_dir = GNUNET_OS_installation_get_path (TALER_EXCHANGE_project_data (),
1132 : GNUNET_OS_IPK_BINDIR);
1133 1 : GNUNET_asprintf (&binary_name,
1134 : "%s/%s",
1135 : libexec_dir,
1136 : "taler-exchange-secmod-cs");
1137 1 : GNUNET_free (libexec_dir);
1138 1 : helper = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
1139 1 : if (GNUNET_OK !=
1140 1 : GNUNET_process_run_command_va (helper,
1141 : binary_name,
1142 : binary_name,
1143 : "-c",
1144 : "test_helper_cs.conf",
1145 : "-L",
1146 : loglev,
1147 : NULL))
1148 : {
1149 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1150 : "exec",
1151 : binary_name);
1152 0 : GNUNET_process_destroy (helper);
1153 0 : GNUNET_free (binary_name);
1154 0 : return 77;
1155 : }
1156 1 : GNUNET_free (binary_name);
1157 1 : ret = run_test ();
1158 :
1159 1 : GNUNET_break (GNUNET_OK ==
1160 : GNUNET_process_kill (helper,
1161 : SIGTERM));
1162 1 : if (GNUNET_OK !=
1163 1 : GNUNET_process_wait (helper,
1164 : true,
1165 : &type,
1166 : &code))
1167 : {
1168 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1169 : "Helper process did not die voluntarily, killing hard\n");
1170 0 : GNUNET_break (GNUNET_OK ==
1171 : GNUNET_process_kill (helper,
1172 : SIGKILL));
1173 0 : ret = 4;
1174 : }
1175 1 : else if ( (GNUNET_OS_PROCESS_EXITED != type) ||
1176 1 : (0 != code) )
1177 : {
1178 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1179 : "Helper died with unexpected status %d/%d\n",
1180 : (int) type,
1181 : (int) code);
1182 0 : ret = 5;
1183 : }
1184 1 : GNUNET_process_destroy (helper);
1185 1 : return ret;
1186 : }
1187 :
1188 :
1189 : /* end of test_helper_cs.c */
|