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