Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2026 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/secmod_cs.c
18 : * @brief Standalone process to perform private key CS operations
19 : * @author Christian Grothoff
20 : *
21 : * Key design points:
22 : * - EVERY thread of the exchange will have its own pair of connections to the
23 : * crypto helpers. This way, every thread will also have its own /keys state
24 : * and avoid the need to synchronize on those.
25 : * - auditor signatures and master signatures are to be kept in the exchange DB,
26 : * and merged with the public keys of the helper by the exchange HTTPD!
27 : * - the main loop of the helper is SINGLE-THREADED, but there are
28 : * threads for crypto-workers which do the signing in parallel, one per client.
29 : * - thread-safety: signing happens in parallel, thus when REMOVING private keys,
30 : * we must ensure that all signers are done before we fully free() the
31 : * private key. This is done by reference counting (as work is always
32 : * assigned and collected by the main thread).
33 : */
34 : #include "taler/platform.h"
35 : #include "taler/taler_util.h"
36 : #include "secmod_cs.h"
37 : #include <gcrypt.h>
38 : #include <pthread.h>
39 : #include <sys/eventfd.h>
40 : #include "taler/taler_error_codes.h"
41 : #include "taler/taler_signatures.h"
42 : #include "secmod_common.h"
43 : #include <poll.h>
44 :
45 :
46 : /**
47 : * Information we keep per denomination.
48 : */
49 : struct Denomination;
50 :
51 :
52 : /**
53 : * One particular denomination key.
54 : */
55 : struct DenominationKey
56 : {
57 :
58 : /**
59 : * Kept in a DLL of the respective denomination. Sorted by anchor time.
60 : */
61 : struct DenominationKey *next;
62 :
63 : /**
64 : * Kept in a DLL of the respective denomination. Sorted by anchor time.
65 : */
66 : struct DenominationKey *prev;
67 :
68 : /**
69 : * Denomination this key belongs to.
70 : */
71 : struct Denomination *denom;
72 :
73 : /**
74 : * Name of the file this key is stored under.
75 : */
76 : char *filename;
77 :
78 : /**
79 : * The private key of the denomination.
80 : */
81 : struct GNUNET_CRYPTO_CsPrivateKey denom_priv;
82 :
83 : /**
84 : * The public key of the denomination.
85 : */
86 : struct GNUNET_CRYPTO_CsPublicKey denom_pub;
87 :
88 : /**
89 : * Message to transmit to clients to introduce this public key.
90 : */
91 : struct TALER_CRYPTO_CsKeyAvailableNotification *an;
92 :
93 : /**
94 : * Hash of this denomination's public key.
95 : */
96 : struct TALER_CsPubHashP h_cs;
97 :
98 : /**
99 : * Time at which this key is supposed to become valid.
100 : */
101 : struct GNUNET_TIME_Timestamp anchor_start;
102 :
103 : /**
104 : * Time at which this key is supposed to expire (exclusive).
105 : */
106 : struct GNUNET_TIME_Timestamp anchor_end;
107 :
108 : /**
109 : * Generation when this key was created or revoked.
110 : */
111 : uint64_t key_gen;
112 :
113 : /**
114 : * Reference counter. Counts the number of threads that are
115 : * using this key at this time.
116 : */
117 : unsigned int rc;
118 :
119 : /**
120 : * Flag set to true if this key has been purged and the memory
121 : * must be freed as soon as @e rc hits zero.
122 : */
123 : bool purge;
124 :
125 : };
126 :
127 :
128 : struct Denomination
129 : {
130 :
131 : /**
132 : * Kept in a DLL. Sorted by #denomination_action_time().
133 : */
134 : struct Denomination *next;
135 :
136 : /**
137 : * Kept in a DLL. Sorted by #denomination_action_time().
138 : */
139 : struct Denomination *prev;
140 :
141 : /**
142 : * Head of DLL of actual keys of this denomination.
143 : */
144 : struct DenominationKey *keys_head;
145 :
146 : /**
147 : * Tail of DLL of actual keys of this denomination.
148 : */
149 : struct DenominationKey *keys_tail;
150 :
151 : /**
152 : * How long can coins be withdrawn (generated)? Should be small
153 : * enough to limit how many coins will be signed into existence with
154 : * the same key, but large enough to still provide a reasonable
155 : * anonymity set.
156 : */
157 : struct GNUNET_TIME_Relative duration_withdraw;
158 :
159 : /**
160 : * What is the configuration section of this denomination type? Also used
161 : * for the directory name where the denomination keys are stored.
162 : */
163 : char *section;
164 :
165 : };
166 :
167 :
168 : /**
169 : * A semaphore.
170 : */
171 : struct Semaphore
172 : {
173 : /**
174 : * Mutex for the semaphore.
175 : */
176 : pthread_mutex_t mutex;
177 :
178 : /**
179 : * Condition variable for the semaphore.
180 : */
181 : pthread_cond_t cv;
182 :
183 : /**
184 : * Counter of the semaphore.
185 : */
186 : unsigned int ctr;
187 : };
188 :
189 :
190 : /**
191 : * Job in a batch sign request.
192 : */
193 : struct BatchJob;
194 :
195 : /**
196 : * Handle for a thread that does work in batch signing.
197 : */
198 : struct Worker
199 : {
200 : /**
201 : * Kept in a DLL.
202 : */
203 : struct Worker *prev;
204 :
205 : /**
206 : * Kept in a DLL.
207 : */
208 : struct Worker *next;
209 :
210 : /**
211 : * Job this worker should do next.
212 : */
213 : struct BatchJob *job;
214 :
215 : /**
216 : * Semaphore to signal the worker that a job is available.
217 : */
218 : struct Semaphore sem;
219 :
220 : /**
221 : * Handle for this thread.
222 : */
223 : pthread_t pt;
224 :
225 : /**
226 : * Set to true if the worker should terminate.
227 : */
228 : bool do_shutdown;
229 : };
230 :
231 :
232 : /**
233 : * Job in a batch sign request.
234 : */
235 : struct BatchJob
236 : {
237 :
238 : /**
239 : * Thread doing the work.
240 : */
241 : struct Worker *worker;
242 :
243 : /**
244 : * Semaphore to signal that the job is finished.
245 : */
246 : struct Semaphore sem;
247 :
248 : /**
249 : * Computation status.
250 : */
251 : enum TALER_ErrorCode ec;
252 :
253 : /**
254 : * Which type of request is this?
255 : */
256 : enum { TYPE_SIGN, TYPE_RDERIVE } type;
257 :
258 : /**
259 : * Details depending on @e type.
260 : */
261 : union
262 : {
263 :
264 : /**
265 : * Details if @e type is TYPE_SIGN.
266 : */
267 : struct
268 : {
269 : /**
270 : * Request we are working on.
271 : */
272 : const struct TALER_CRYPTO_CsSignRequestMessage *sr;
273 :
274 : /**
275 : * Result with the signature.
276 : */
277 : struct GNUNET_CRYPTO_CsBlindSignature cs_answer;
278 : } sign;
279 :
280 : /**
281 : * Details if type is TYPE_RDERIVE.
282 : */
283 : struct
284 : {
285 : /**
286 : * Request we are answering.
287 : */
288 : const struct TALER_CRYPTO_CsRDeriveRequest *rdr;
289 :
290 : /**
291 : * Pair of points to return.
292 : */
293 : struct GNUNET_CRYPTO_CSPublicRPairP rpairp;
294 :
295 : } rderive;
296 :
297 : } details;
298 :
299 : };
300 :
301 : /**
302 : * Head of DLL of workers ready for more work.
303 : */
304 : static struct Worker *worker_head;
305 :
306 : /**
307 : * Tail of DLL of workers ready for more work.
308 : */
309 : static struct Worker *worker_tail;
310 :
311 : /**
312 : * Lock for manipulating the worker DLL.
313 : */
314 : static pthread_mutex_t worker_lock;
315 :
316 : /**
317 : * Total number of workers that were started.
318 : */
319 : static unsigned int workers;
320 :
321 : /**
322 : * Semaphore used to grab a worker.
323 : */
324 : static struct Semaphore worker_sem;
325 :
326 : /**
327 : * Command-line options for various TALER_SECMOD_XXX_run() functions.
328 : */
329 : static struct TALER_SECMOD_Options *globals;
330 :
331 : /**
332 : * Where do we store the keys?
333 : */
334 : static char *keydir;
335 :
336 : /**
337 : * How much should coin creation (@e duration_withdraw) duration overlap
338 : * with the next denomination? Basically, the starting time of two
339 : * denominations is always @e duration_withdraw - #overlap_duration apart.
340 : */
341 : static struct GNUNET_TIME_Relative overlap_duration;
342 :
343 : /**
344 : * How long into the future do we pre-generate keys?
345 : */
346 : static struct GNUNET_TIME_Relative lookahead_sign;
347 :
348 : /**
349 : * All of our denominations, in a DLL. Sorted?
350 : */
351 : static struct Denomination *denom_head;
352 :
353 : /**
354 : * All of our denominations, in a DLL. Sorted?
355 : */
356 : static struct Denomination *denom_tail;
357 :
358 : /**
359 : * Map of hashes of public (CS) keys to `struct DenominationKey *`
360 : * with the respective private keys.
361 : */
362 : static struct GNUNET_CONTAINER_MultiHashMap *keys;
363 :
364 : /**
365 : * Task run to generate new keys.
366 : */
367 : static struct GNUNET_SCHEDULER_Task *keygen_task;
368 :
369 : /**
370 : * Lock for the keys queue.
371 : */
372 : static pthread_mutex_t keys_lock;
373 :
374 : /**
375 : * Current key generation.
376 : */
377 : static uint64_t key_gen;
378 :
379 : /**
380 : * Generate the announcement message for @a dk.
381 : *
382 : * @param[in,out] dk denomination key to generate the announcement for
383 : */
384 : static void
385 222 : generate_response (struct DenominationKey *dk)
386 : {
387 222 : struct Denomination *denom = dk->denom;
388 222 : size_t nlen = strlen (denom->section) + 1;
389 : struct TALER_CRYPTO_CsKeyAvailableNotification *an;
390 : void *p;
391 : size_t tlen;
392 : struct GNUNET_TIME_Relative effective_duration;
393 :
394 : GNUNET_assert (sizeof(dk->denom_pub) < UINT16_MAX);
395 222 : GNUNET_assert (nlen < UINT16_MAX);
396 222 : tlen = nlen + sizeof (*an);
397 222 : GNUNET_assert (tlen < UINT16_MAX);
398 222 : an = GNUNET_malloc (tlen);
399 222 : an->header.size = htons ((uint16_t) tlen);
400 222 : an->header.type = htons (TALER_HELPER_CS_MT_AVAIL);
401 222 : an->section_name_len = htons ((uint16_t) nlen);
402 222 : an->anchor_time = GNUNET_TIME_timestamp_hton (dk->anchor_start);
403 222 : effective_duration = GNUNET_TIME_absolute_get_difference (
404 : dk->anchor_start.abs_time,
405 : dk->anchor_end.abs_time);
406 222 : an->duration_withdraw = GNUNET_TIME_relative_hton (effective_duration);
407 222 : an->denom_pub = dk->denom_pub;
408 222 : TALER_exchange_secmod_cs_sign (&dk->h_cs,
409 222 : denom->section,
410 : dk->anchor_start,
411 : effective_duration,
412 : &TES_smpriv,
413 : &an->secm_sig);
414 222 : an->secm_pub = TES_smpub;
415 222 : p = (void *) &an[1];
416 222 : GNUNET_memcpy (p,
417 : denom->section,
418 : nlen);
419 222 : dk->an = an;
420 222 : }
421 :
422 :
423 : /**
424 : * Do the actual signing work.
425 : *
426 : * @param h_cs hash of key to sign with
427 : * @param planchet message to sign
428 : * @param for_melt true if for melting
429 : * @param[out] cs_sigp set to the CS signature
430 : * @return #TALER_EC_NONE on success
431 : */
432 : static enum TALER_ErrorCode
433 1142 : do_sign (const struct TALER_CsPubHashP *h_cs,
434 : const struct GNUNET_CRYPTO_CsBlindedMessage *planchet,
435 : bool for_melt,
436 : struct GNUNET_CRYPTO_CsBlindSignature *cs_sigp)
437 : {
438 : struct GNUNET_CRYPTO_CsRSecret r[2];
439 : struct DenominationKey *dk;
440 :
441 1142 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
442 1152 : dk = GNUNET_CONTAINER_multihashmap_get (keys,
443 : &h_cs->hash);
444 1152 : if (NULL == dk)
445 : {
446 3 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
447 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
448 : "Signing request failed, denomination key %s unknown\n",
449 : GNUNET_h2s (&h_cs->hash));
450 3 : return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
451 : }
452 1149 : if (GNUNET_TIME_absolute_is_future (dk->anchor_start.abs_time))
453 : {
454 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
455 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
456 : "Signing request failed, denomination key %s is not yet valid\n",
457 : GNUNET_h2s (&h_cs->hash));
458 0 : return TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY;
459 : }
460 1149 : if (GNUNET_TIME_absolute_is_past (dk->anchor_end.abs_time))
461 : {
462 : /* it is too late; now, usually we should never get here
463 : as we delete upon expiration, so this is just conservative */
464 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
465 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
466 : "Signing request failed, denomination key %s is expired (%llu)\n",
467 : GNUNET_h2s (&h_cs->hash),
468 : (unsigned long long) dk->anchor_end.abs_time.abs_value_us);
469 : /* usually we delete upon expiratoin, hence same EC */
470 0 : return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
471 : }
472 1149 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
473 : "Received request to sign over bytes with key %s\n",
474 : GNUNET_h2s (&h_cs->hash));
475 1149 : GNUNET_assert (dk->rc < UINT_MAX);
476 1149 : dk->rc++;
477 1149 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
478 1146 : GNUNET_CRYPTO_cs_r_derive (&planchet->nonce,
479 : for_melt ? "rm" : "rw",
480 1146 : &dk->denom_priv,
481 : r);
482 1144 : GNUNET_CRYPTO_cs_sign_derive (&dk->denom_priv,
483 : r,
484 : planchet,
485 : cs_sigp);
486 1139 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
487 1149 : GNUNET_assert (dk->rc > 0);
488 1149 : dk->rc--;
489 1149 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
490 1142 : return TALER_EC_NONE;
491 : }
492 :
493 :
494 : /**
495 : * Generate error response that signing failed.
496 : *
497 : * @param client client to send response to
498 : * @param ec error code to include
499 : * @return #GNUNET_OK on success
500 : */
501 : static enum GNUNET_GenericReturnValue
502 3 : fail_sign (struct TES_Client *client,
503 : enum TALER_ErrorCode ec)
504 : {
505 3 : struct TALER_CRYPTO_SignFailure sf = {
506 3 : .header.size = htons (sizeof (sf)),
507 3 : .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE),
508 3 : .ec = htonl (ec)
509 : };
510 :
511 3 : return TES_transmit (client->csock,
512 : &sf.header);
513 : }
514 :
515 :
516 : /**
517 : * Generate error response that deriving failed.
518 : *
519 : * @param client client to send response to
520 : * @param ec error code to include
521 : * @return #GNUNET_OK on success
522 : */
523 : static enum GNUNET_GenericReturnValue
524 613 : fail_derive (struct TES_Client *client,
525 : enum TALER_ErrorCode ec)
526 : {
527 613 : struct TALER_CRYPTO_RDeriveFailure sf = {
528 613 : .header.size = htons (sizeof (sf)),
529 613 : .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE),
530 613 : .ec = htonl (ec)
531 : };
532 :
533 613 : return TES_transmit (client->csock,
534 : &sf.header);
535 : }
536 :
537 :
538 : /**
539 : * Generate signature response.
540 : *
541 : * @param client client to send response to
542 : * @param cs_answer signature to send
543 : * @return #GNUNET_OK on success
544 : */
545 : static enum GNUNET_GenericReturnValue
546 1145 : send_signature (struct TES_Client *client,
547 : const struct GNUNET_CRYPTO_CsBlindSignature *cs_answer)
548 : {
549 : struct TALER_CRYPTO_SignResponse sres;
550 :
551 1145 : sres.header.size = htons (sizeof (sres));
552 1145 : sres.header.type = htons (TALER_HELPER_CS_MT_RES_SIGNATURE);
553 1145 : sres.b = htonl (cs_answer->b);
554 1145 : sres.cs_answer = cs_answer->s_scalar;
555 1145 : return TES_transmit (client->csock,
556 : &sres.header);
557 : }
558 :
559 :
560 : /**
561 : * Handle @a client request @a sr to create signature. Create the
562 : * signature using the respective key and return the result to
563 : * the client.
564 : *
565 : * @param client the client making the request
566 : * @param sr the request details
567 : * @return #GNUNET_OK on success
568 : */
569 : static enum GNUNET_GenericReturnValue
570 898 : handle_sign_request (struct TES_Client *client,
571 : const struct TALER_CRYPTO_CsSignRequestMessage *sr)
572 : {
573 : struct GNUNET_CRYPTO_CsBlindSignature cs_answer;
574 898 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
575 : enum TALER_ErrorCode ec;
576 : enum GNUNET_GenericReturnValue ret;
577 :
578 898 : ec = do_sign (&sr->h_cs,
579 : &sr->message,
580 898 : (0 != ntohl (sr->for_melt)),
581 : &cs_answer);
582 898 : if (TALER_EC_NONE != ec)
583 : {
584 1 : return fail_sign (client,
585 : ec);
586 : }
587 897 : ret = send_signature (client,
588 : &cs_answer);
589 892 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
590 : "Sent CS signature after %s\n",
591 : GNUNET_TIME_relative2s (
592 : GNUNET_TIME_absolute_get_duration (now),
593 : GNUNET_YES));
594 891 : return ret;
595 : }
596 :
597 :
598 : /**
599 : * Do the actual deriving work.
600 : *
601 : * @param h_cs key to sign with
602 : * @param nonce nonce to derive from
603 : * @param for_melt true if for melting
604 : * @param[out] rpairp set to the derived values
605 : * @return #TALER_EC_NONE on success
606 : */
607 : static enum TALER_ErrorCode
608 958 : do_derive (const struct TALER_CsPubHashP *h_cs,
609 : const struct GNUNET_CRYPTO_CsSessionNonce *nonce,
610 : bool for_melt,
611 : struct GNUNET_CRYPTO_CSPublicRPairP *rpairp)
612 : {
613 : struct DenominationKey *dk;
614 : struct GNUNET_CRYPTO_CSPrivateRPairP r_priv;
615 :
616 958 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
617 970 : dk = GNUNET_CONTAINER_multihashmap_get (keys,
618 : &h_cs->hash);
619 970 : if (NULL == dk)
620 : {
621 1 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
622 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
623 : "R Derive request failed, denomination key %s unknown\n",
624 : GNUNET_h2s (&h_cs->hash));
625 1 : return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
626 : }
627 969 : if (GNUNET_TIME_absolute_is_future (dk->anchor_start.abs_time))
628 : {
629 612 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
630 611 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
631 : "R Derive request failed, denomination key %s is not yet valid\n",
632 : GNUNET_h2s (&h_cs->hash));
633 610 : return TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY;
634 : }
635 357 : if (GNUNET_TIME_absolute_is_past (dk->anchor_end.abs_time))
636 : {
637 : /* it is too late; now, usually we should never get here
638 : as we delete upon expiration, so this is just conservative */
639 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
640 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
641 : "Signing request failed, denomination key %s is expired (%llu)\n",
642 : GNUNET_h2s (&h_cs->hash),
643 : (unsigned long long) dk->anchor_end.abs_time.abs_value_us);
644 : /* usually we delete upon expiratoin, hence same EC */
645 0 : return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
646 : }
647 357 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
648 : "Received request to derive R with key %s\n",
649 : GNUNET_h2s (&h_cs->hash));
650 357 : GNUNET_assert (dk->rc < UINT_MAX);
651 357 : dk->rc++;
652 357 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
653 357 : GNUNET_CRYPTO_cs_r_derive (nonce,
654 : for_melt ? "rm" : "rw",
655 357 : &dk->denom_priv,
656 : r_priv.r);
657 356 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
658 357 : GNUNET_assert (dk->rc > 0);
659 357 : dk->rc--;
660 357 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
661 357 : GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[0],
662 : &rpairp->r_pub[0]);
663 357 : GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[1],
664 : &rpairp->r_pub[1]);
665 354 : return TALER_EC_NONE;
666 : }
667 :
668 :
669 : /**
670 : * Generate derivation response.
671 : *
672 : * @param client client to send response to
673 : * @param r_pub public point value pair to send
674 : * @return #GNUNET_OK on success
675 : */
676 : static enum GNUNET_GenericReturnValue
677 357 : send_derivation (struct TES_Client *client,
678 : const struct GNUNET_CRYPTO_CSPublicRPairP *r_pub)
679 : {
680 357 : struct TALER_CRYPTO_RDeriveResponse rdr = {
681 357 : .header.size = htons (sizeof (rdr)),
682 357 : .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE),
683 : .r_pub = *r_pub
684 : };
685 :
686 357 : return TES_transmit (client->csock,
687 : &rdr.header);
688 : }
689 :
690 :
691 : /**
692 : * Initialize a semaphore @a sem with a value of @a val.
693 : *
694 : * @param[out] sem semaphore to initialize
695 : * @param val initial value of the semaphore
696 : */
697 : static void
698 1506 : sem_init (struct Semaphore *sem,
699 : unsigned int val)
700 : {
701 1506 : GNUNET_assert (0 ==
702 : pthread_mutex_init (&sem->mutex,
703 : NULL));
704 1506 : GNUNET_assert (0 ==
705 : pthread_cond_init (&sem->cv,
706 : NULL));
707 1506 : sem->ctr = val;
708 1506 : }
709 :
710 :
711 : /**
712 : * Decrement semaphore, blocks until this is possible.
713 : *
714 : * @param[in,out] sem semaphore to decrement
715 : */
716 : static void
717 4149 : sem_down (struct Semaphore *sem)
718 : {
719 4149 : GNUNET_assert (0 == pthread_mutex_lock (&sem->mutex));
720 5804 : while (0 == sem->ctr)
721 : {
722 1651 : pthread_cond_wait (&sem->cv,
723 : &sem->mutex);
724 : }
725 4153 : sem->ctr--;
726 4153 : GNUNET_assert (0 == pthread_mutex_unlock (&sem->mutex));
727 4160 : }
728 :
729 :
730 : /**
731 : * Increment semaphore, blocks until this is possible.
732 : *
733 : * @param[in,out] sem semaphore to decrement
734 : */
735 : static void
736 4104 : sem_up (struct Semaphore *sem)
737 : {
738 4104 : GNUNET_assert (0 == pthread_mutex_lock (&sem->mutex));
739 4158 : sem->ctr++;
740 4158 : GNUNET_assert (0 == pthread_mutex_unlock (&sem->mutex));
741 4136 : pthread_cond_signal (&sem->cv);
742 4142 : }
743 :
744 :
745 : /**
746 : * Release resources used by @a sem.
747 : *
748 : * @param[in] sem semaphore to release (except the memory itself)
749 : */
750 : static void
751 1506 : sem_done (struct Semaphore *sem)
752 : {
753 1506 : GNUNET_break (0 == pthread_cond_destroy (&sem->cv));
754 1506 : GNUNET_break (0 == pthread_mutex_destroy (&sem->mutex));
755 1506 : }
756 :
757 :
758 : /**
759 : * Main logic of a worker thread. Grabs work, does it,
760 : * grabs more work.
761 : *
762 : * @param cls a `struct Worker *`
763 : * @returns cls
764 : */
765 : static void *
766 288 : worker (void *cls)
767 : {
768 288 : struct Worker *w = cls;
769 :
770 : while (true)
771 : {
772 1478 : GNUNET_assert (0 == pthread_mutex_lock (&worker_lock));
773 1488 : GNUNET_CONTAINER_DLL_insert (worker_head,
774 : worker_tail,
775 : w);
776 1488 : GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock));
777 1483 : sem_up (&worker_sem);
778 1484 : sem_down (&w->sem);
779 1481 : if (w->do_shutdown)
780 288 : break;
781 : {
782 1193 : struct BatchJob *bj = w->job;
783 :
784 1193 : switch (bj->type)
785 : {
786 246 : case TYPE_SIGN:
787 : {
788 246 : const struct TALER_CRYPTO_CsSignRequestMessage *sr
789 : = bj->details.sign.sr;
790 :
791 494 : bj->ec = do_sign (&sr->h_cs,
792 : &sr->message,
793 246 : (0 != ntohl (sr->for_melt)),
794 : &bj->details.sign.cs_answer);
795 248 : break;
796 : }
797 942 : case TYPE_RDERIVE:
798 : {
799 942 : const struct TALER_CRYPTO_CsRDeriveRequest *rdr
800 : = bj->details.rderive.rdr;
801 1886 : bj->ec = do_derive (&rdr->h_cs,
802 : &rdr->nonce,
803 942 : (0 != ntohl (rdr->for_melt)),
804 : &bj->details.rderive.rpairp);
805 944 : break;
806 : }
807 : }
808 1197 : sem_up (&bj->sem);
809 1190 : w->job = NULL;
810 : }
811 : }
812 288 : return w;
813 : }
814 :
815 :
816 : /**
817 : * Start batch job @a bj to sign @a sr.
818 : *
819 : * @param sr signature request to answer
820 : * @param[out] bj job data structure
821 : */
822 : static void
823 250 : start_sign_job (const struct TALER_CRYPTO_CsSignRequestMessage *sr,
824 : struct BatchJob *bj)
825 : {
826 250 : sem_init (&bj->sem,
827 : 0);
828 250 : bj->type = TYPE_SIGN;
829 250 : bj->details.sign.sr = sr;
830 250 : sem_down (&worker_sem);
831 250 : GNUNET_assert (0 == pthread_mutex_lock (&worker_lock));
832 250 : bj->worker = worker_head;
833 250 : GNUNET_CONTAINER_DLL_remove (worker_head,
834 : worker_tail,
835 : bj->worker);
836 250 : GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock));
837 250 : bj->worker->job = bj;
838 250 : sem_up (&bj->worker->sem);
839 250 : }
840 :
841 :
842 : /**
843 : * Start batch job @a bj to derive @a rdr.
844 : *
845 : * @param rdr derivation request to answer
846 : * @param[out] bj job data structure
847 : */
848 : static void
849 950 : start_derive_job (const struct TALER_CRYPTO_CsRDeriveRequest *rdr,
850 : struct BatchJob *bj)
851 : {
852 950 : sem_init (&bj->sem,
853 : 0);
854 950 : bj->type = TYPE_RDERIVE;
855 950 : bj->details.rderive.rdr = rdr;
856 950 : sem_down (&worker_sem);
857 950 : GNUNET_assert (0 == pthread_mutex_lock (&worker_lock));
858 950 : bj->worker = worker_head;
859 950 : GNUNET_CONTAINER_DLL_remove (worker_head,
860 : worker_tail,
861 : bj->worker);
862 950 : GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock));
863 950 : bj->worker->job = bj;
864 950 : sem_up (&bj->worker->sem);
865 950 : }
866 :
867 :
868 : /**
869 : * Finish a job @a bj for a @a client.
870 : *
871 : * @param client who made the request
872 : * @param[in,out] bj job to finish
873 : */
874 : static void
875 1200 : finish_job (struct TES_Client *client,
876 : struct BatchJob *bj)
877 : {
878 1200 : sem_down (&bj->sem);
879 1200 : sem_done (&bj->sem);
880 1200 : switch (bj->type)
881 : {
882 250 : case TYPE_SIGN:
883 250 : if (TALER_EC_NONE != bj->ec)
884 : {
885 2 : fail_sign (client,
886 : bj->ec);
887 2 : return;
888 : }
889 248 : send_signature (client,
890 248 : &bj->details.sign.cs_answer);
891 248 : break;
892 950 : case TYPE_RDERIVE:
893 950 : if (TALER_EC_NONE != bj->ec)
894 : {
895 604 : fail_derive (client,
896 : bj->ec);
897 604 : return;
898 : }
899 346 : send_derivation (client,
900 346 : &bj->details.rderive.rpairp);
901 346 : break;
902 : }
903 : }
904 :
905 :
906 : /**
907 : * Handle @a client request @a sr to create a batch of signature. Creates the
908 : * signatures using the respective key and return the results to the client.
909 : *
910 : * @param client the client making the request
911 : * @param bsr the request details
912 : * @return #GNUNET_OK on success
913 : */
914 : static enum GNUNET_GenericReturnValue
915 57 : handle_batch_sign_request (struct TES_Client *client,
916 : const struct TALER_CRYPTO_BatchSignRequest *bsr)
917 57 : {
918 57 : uint32_t bs = ntohl (bsr->batch_size);
919 57 : uint16_t size = ntohs (bsr->header.size) - sizeof (*bsr);
920 57 : const void *off = (const void *) &bsr[1];
921 57 : unsigned int idx = 0;
922 57 : struct BatchJob jobs[GNUNET_NZL (bs)];
923 57 : bool failure = false;
924 :
925 57 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
926 : "Handling batch sign request of size %u\n",
927 : (unsigned int) bs);
928 57 : if (bs > TALER_MAX_COINS)
929 : {
930 0 : GNUNET_break_op (0);
931 0 : return GNUNET_SYSERR;
932 : }
933 307 : while ( (bs > 0) &&
934 : (size >= sizeof (struct TALER_CRYPTO_CsSignRequestMessage)) )
935 : {
936 250 : const struct TALER_CRYPTO_CsSignRequestMessage *sr = off;
937 250 : uint16_t s = ntohs (sr->header.size);
938 :
939 250 : if (s > size)
940 : {
941 0 : failure = true;
942 0 : bs = idx;
943 0 : break;
944 : }
945 250 : start_sign_job (sr,
946 250 : &jobs[idx++]);
947 250 : off += s;
948 250 : size -= s;
949 : }
950 57 : GNUNET_break_op (0 == size);
951 57 : bs = GNUNET_MIN (bs,
952 : idx);
953 307 : for (unsigned int i = 0; i<bs; i++)
954 250 : finish_job (client,
955 : &jobs[i]);
956 57 : if (failure)
957 : {
958 0 : struct TALER_CRYPTO_SignFailure sf = {
959 0 : .header.size = htons (sizeof (sf)),
960 0 : .header.type = htons (TALER_HELPER_CS_MT_RES_BATCH_SIGN_FAILURE),
961 0 : .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)
962 : };
963 :
964 0 : GNUNET_break (0);
965 0 : return TES_transmit (client->csock,
966 : &sf.header);
967 : }
968 57 : return GNUNET_OK;
969 : }
970 :
971 :
972 : /**
973 : * Handle @a client request @a sr to create a batch of derivations. Creates the
974 : * derivations using the respective key and return the results to the client.
975 : *
976 : * @param client the client making the request
977 : * @param bdr the request details
978 : * @return #GNUNET_OK on success
979 : */
980 : static enum GNUNET_GenericReturnValue
981 129 : handle_batch_derive_request (struct TES_Client *client,
982 : const struct TALER_CRYPTO_BatchDeriveRequest *bdr)
983 129 : {
984 129 : uint32_t bs = ntohl (bdr->batch_size);
985 129 : uint16_t size = ntohs (bdr->header.size) - sizeof (*bdr);
986 129 : const void *off = (const void *) &bdr[1];
987 129 : unsigned int idx = 0;
988 129 : struct BatchJob jobs[bs];
989 129 : bool failure = false;
990 :
991 129 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
992 : "Handling batch derivation request of size %u\n",
993 : (unsigned int) bs);
994 129 : if (bs > TALER_MAX_COINS)
995 : {
996 0 : GNUNET_break_op (0);
997 0 : return GNUNET_SYSERR;
998 : }
999 1079 : while ( (bs > 0) &&
1000 : (size >= sizeof (struct TALER_CRYPTO_CsRDeriveRequest)) )
1001 : {
1002 950 : const struct TALER_CRYPTO_CsRDeriveRequest *rdr = off;
1003 950 : uint16_t s = ntohs (rdr->header.size);
1004 :
1005 950 : if ( (s > size) ||
1006 : (s != sizeof (*rdr)) )
1007 : {
1008 0 : failure = true;
1009 0 : bs = idx;
1010 0 : break;
1011 : }
1012 950 : start_derive_job (rdr,
1013 950 : &jobs[idx++]);
1014 950 : off += s;
1015 950 : size -= s;
1016 : }
1017 129 : GNUNET_break_op (0 == size);
1018 129 : bs = GNUNET_MIN (bs,
1019 : idx);
1020 1079 : for (unsigned int i = 0; i<bs; i++)
1021 950 : finish_job (client,
1022 : &jobs[i]);
1023 129 : if (failure)
1024 : {
1025 0 : GNUNET_break (0);
1026 0 : return fail_derive (client,
1027 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE);
1028 : }
1029 129 : return GNUNET_OK;
1030 : }
1031 :
1032 :
1033 : /**
1034 : * Start worker thread for batch processing.
1035 : *
1036 : * @return #GNUNET_OK on success
1037 : */
1038 : static enum GNUNET_GenericReturnValue
1039 288 : start_worker (void)
1040 : {
1041 : struct Worker *w;
1042 :
1043 288 : w = GNUNET_new (struct Worker);
1044 288 : sem_init (&w->sem,
1045 : 0);
1046 288 : if (0 != pthread_create (&w->pt,
1047 : NULL,
1048 : &worker,
1049 : w))
1050 : {
1051 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1052 : "pthread_create");
1053 0 : GNUNET_free (w);
1054 0 : return GNUNET_SYSERR;
1055 : }
1056 288 : workers++;
1057 288 : return GNUNET_OK;
1058 : }
1059 :
1060 :
1061 : /**
1062 : * Stop all worker threads.
1063 : */
1064 : static void
1065 18 : stop_workers (void)
1066 : {
1067 306 : while (workers > 0)
1068 : {
1069 : struct Worker *w;
1070 : void *result;
1071 :
1072 288 : sem_down (&worker_sem);
1073 288 : GNUNET_assert (0 == pthread_mutex_lock (&worker_lock));
1074 288 : w = worker_head;
1075 288 : GNUNET_CONTAINER_DLL_remove (worker_head,
1076 : worker_tail,
1077 : w);
1078 288 : GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock));
1079 288 : w->do_shutdown = true;
1080 288 : sem_up (&w->sem);
1081 288 : pthread_join (w->pt,
1082 : &result);
1083 288 : GNUNET_assert (result == w);
1084 288 : sem_done (&w->sem);
1085 288 : GNUNET_free (w);
1086 288 : workers--;
1087 : }
1088 18 : }
1089 :
1090 :
1091 : /**
1092 : * Initialize key material for denomination key @a dk (also on disk).
1093 : *
1094 : * @param[in,out] dk denomination key to compute key material for
1095 : * @param position where in the DLL will the @a dk go
1096 : * @return #GNUNET_OK on success
1097 : */
1098 : static enum GNUNET_GenericReturnValue
1099 57 : setup_key (struct DenominationKey *dk,
1100 : struct DenominationKey *position)
1101 : {
1102 57 : struct Denomination *denom = dk->denom;
1103 : struct GNUNET_CRYPTO_CsPrivateKey priv;
1104 : struct GNUNET_CRYPTO_CsPublicKey pub;
1105 :
1106 57 : GNUNET_CRYPTO_cs_private_key_generate (&priv);
1107 57 : GNUNET_CRYPTO_cs_private_key_get_public (&priv,
1108 : &pub);
1109 57 : GNUNET_CRYPTO_hash (&pub,
1110 : sizeof (pub),
1111 : &dk->h_cs.hash);
1112 57 : GNUNET_asprintf (
1113 : &dk->filename,
1114 : "%s/%s/%llu-%llu",
1115 : keydir,
1116 : denom->section,
1117 57 : (unsigned long long) (dk->anchor_start.abs_time.abs_value_us
1118 57 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us
1119 : ),
1120 57 : (unsigned long long) (dk->anchor_end.abs_time.abs_value_us
1121 57 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us
1122 : ));
1123 57 : if (GNUNET_OK !=
1124 57 : GNUNET_DISK_fn_write (dk->filename,
1125 : &priv,
1126 : sizeof(priv),
1127 : GNUNET_DISK_PERM_USER_READ))
1128 : {
1129 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1130 : "write",
1131 : dk->filename);
1132 0 : return GNUNET_SYSERR;
1133 : }
1134 57 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1135 : "Setup fresh private key %s at %s in `%s' (generation #%llu)\n",
1136 : GNUNET_h2s (&dk->h_cs.hash),
1137 : GNUNET_TIME_timestamp2s (dk->anchor_start),
1138 : dk->filename,
1139 : (unsigned long long) key_gen);
1140 57 : dk->denom_priv = priv;
1141 57 : dk->denom_pub = pub;
1142 57 : dk->key_gen = key_gen;
1143 57 : generate_response (dk);
1144 57 : if (GNUNET_OK !=
1145 57 : GNUNET_CONTAINER_multihashmap_put (
1146 : keys,
1147 57 : &dk->h_cs.hash,
1148 : dk,
1149 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1150 : {
1151 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1152 : "Duplicate private key created! Terminating.\n");
1153 0 : GNUNET_free (dk->filename);
1154 0 : GNUNET_free (dk->an);
1155 0 : GNUNET_free (dk);
1156 0 : return GNUNET_SYSERR;
1157 : }
1158 57 : GNUNET_CONTAINER_DLL_insert_after (denom->keys_head,
1159 : denom->keys_tail,
1160 : position,
1161 : dk);
1162 57 : return GNUNET_OK;
1163 : }
1164 :
1165 :
1166 : /**
1167 : * The withdraw period of a key @a dk has expired. Purge it.
1168 : *
1169 : * @param[in] dk expired denomination key to purge
1170 : */
1171 : static void
1172 3 : purge_key (struct DenominationKey *dk)
1173 : {
1174 3 : if (dk->purge)
1175 0 : return;
1176 3 : if (0 != unlink (dk->filename))
1177 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1178 : "unlink",
1179 : dk->filename);
1180 3 : GNUNET_free (dk->filename);
1181 3 : dk->purge = true;
1182 3 : dk->key_gen = key_gen;
1183 : }
1184 :
1185 :
1186 : /**
1187 : * A @a client informs us that a key has been revoked.
1188 : * Check if the key is still in use, and if so replace (!)
1189 : * it with a fresh key.
1190 : *
1191 : * @param client the client making the request
1192 : * @param rr the revocation request
1193 : */
1194 : static enum GNUNET_GenericReturnValue
1195 3 : handle_revoke_request (struct TES_Client *client,
1196 : const struct TALER_CRYPTO_CsRevokeRequest *rr)
1197 : {
1198 : struct DenominationKey *dk;
1199 : struct DenominationKey *ndk;
1200 : struct Denomination *denom;
1201 :
1202 : (void) client;
1203 3 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
1204 3 : dk = GNUNET_CONTAINER_multihashmap_get (keys,
1205 : &rr->h_cs.hash);
1206 3 : if (NULL == dk)
1207 : {
1208 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1209 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1210 : "Revocation request ignored, denomination key %s unknown\n",
1211 : GNUNET_h2s (&rr->h_cs.hash));
1212 0 : return GNUNET_OK;
1213 : }
1214 3 : if (dk->purge)
1215 : {
1216 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1217 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1218 : "Revocation request ignored, denomination key %s already revoked\n",
1219 : GNUNET_h2s (&rr->h_cs.hash));
1220 0 : return GNUNET_OK;
1221 : }
1222 :
1223 3 : key_gen++;
1224 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1225 : "Revoking key %s, bumping generation to %llu\n",
1226 : GNUNET_h2s (&rr->h_cs.hash),
1227 : (unsigned long long) key_gen);
1228 3 : purge_key (dk);
1229 :
1230 : /* Setup replacement key */
1231 3 : denom = dk->denom;
1232 3 : ndk = GNUNET_new (struct DenominationKey);
1233 3 : ndk->denom = denom;
1234 3 : ndk->anchor_start = dk->anchor_start;
1235 3 : ndk->anchor_end = dk->anchor_end;
1236 3 : if (GNUNET_OK !=
1237 3 : setup_key (ndk,
1238 : dk))
1239 : {
1240 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1241 0 : GNUNET_break (0);
1242 0 : GNUNET_SCHEDULER_shutdown ();
1243 0 : globals->global_ret = EXIT_FAILURE;
1244 0 : return GNUNET_SYSERR;
1245 : }
1246 3 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1247 3 : TES_wake_clients ();
1248 3 : return GNUNET_OK;
1249 : }
1250 :
1251 :
1252 : /**
1253 : * Handle @a client request @a rdr to create signature. Create the
1254 : * signature using the respective key and return the result to
1255 : * the client.
1256 : *
1257 : * @param client the client making the request
1258 : * @param rdr the request details
1259 : * @return #GNUNET_OK on success
1260 : */
1261 : static enum GNUNET_GenericReturnValue
1262 20 : handle_r_derive_request (struct TES_Client *client,
1263 : const struct TALER_CRYPTO_CsRDeriveRequest *rdr)
1264 : {
1265 : struct GNUNET_CRYPTO_CSPublicRPairP r_pub;
1266 20 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1267 : enum TALER_ErrorCode ec;
1268 : enum GNUNET_GenericReturnValue ret;
1269 :
1270 20 : ec = do_derive (&rdr->h_cs,
1271 : &rdr->nonce,
1272 20 : (0 != ntohl (rdr->for_melt)),
1273 : &r_pub);
1274 20 : if (TALER_EC_NONE != ec)
1275 : {
1276 9 : return fail_derive (client,
1277 : ec);
1278 : }
1279 :
1280 11 : ret = send_derivation (client,
1281 : &r_pub);
1282 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1283 : "Sent CS Derived R after %s\n",
1284 : GNUNET_TIME_relative2s (
1285 : GNUNET_TIME_absolute_get_duration (now),
1286 : GNUNET_YES));
1287 11 : return ret;
1288 : }
1289 :
1290 :
1291 : /**
1292 : * Handle @a hdr message received from @a client.
1293 : *
1294 : * @param client the client that received the message
1295 : * @param hdr message that was received
1296 : * @return #GNUNET_OK on success
1297 : */
1298 : static enum GNUNET_GenericReturnValue
1299 1109 : cs_work_dispatch (struct TES_Client *client,
1300 : const struct GNUNET_MessageHeader *hdr)
1301 : {
1302 1109 : uint16_t msize = ntohs (hdr->size);
1303 :
1304 1109 : switch (ntohs (hdr->type))
1305 : {
1306 900 : case TALER_HELPER_CS_MT_REQ_SIGN:
1307 900 : if (msize < sizeof (struct TALER_CRYPTO_CsSignRequestMessage))
1308 : {
1309 0 : GNUNET_break_op (0);
1310 0 : return GNUNET_SYSERR;
1311 : }
1312 900 : return handle_sign_request (
1313 : client,
1314 : (const struct TALER_CRYPTO_CsSignRequestMessage *) hdr);
1315 3 : case TALER_HELPER_CS_MT_REQ_REVOKE:
1316 3 : if (msize != sizeof (struct TALER_CRYPTO_CsRevokeRequest))
1317 : {
1318 0 : GNUNET_break_op (0);
1319 0 : return GNUNET_SYSERR;
1320 : }
1321 3 : return handle_revoke_request (
1322 : client,
1323 : (const struct TALER_CRYPTO_CsRevokeRequest *) hdr);
1324 57 : case TALER_HELPER_CS_MT_REQ_BATCH_SIGN:
1325 57 : if (msize <= sizeof (struct TALER_CRYPTO_BatchSignRequest))
1326 : {
1327 0 : GNUNET_break_op (0);
1328 0 : return GNUNET_SYSERR;
1329 : }
1330 57 : return handle_batch_sign_request (
1331 : client,
1332 : (const struct TALER_CRYPTO_BatchSignRequest *) hdr);
1333 129 : case TALER_HELPER_CS_MT_REQ_BATCH_RDERIVE:
1334 129 : if (msize <= sizeof (struct TALER_CRYPTO_BatchDeriveRequest))
1335 : {
1336 0 : GNUNET_break_op (0);
1337 0 : return GNUNET_SYSERR;
1338 : }
1339 129 : return handle_batch_derive_request (
1340 : client,
1341 : (const struct TALER_CRYPTO_BatchDeriveRequest *) hdr);
1342 20 : case TALER_HELPER_CS_MT_REQ_RDERIVE:
1343 20 : if (msize != sizeof (struct TALER_CRYPTO_CsRDeriveRequest))
1344 : {
1345 0 : GNUNET_break_op (0);
1346 0 : return GNUNET_SYSERR;
1347 : }
1348 20 : return handle_r_derive_request (client,
1349 : (const struct
1350 : TALER_CRYPTO_CsRDeriveRequest *) hdr);
1351 0 : default:
1352 0 : GNUNET_break_op (0);
1353 0 : return GNUNET_SYSERR;
1354 : }
1355 : }
1356 :
1357 :
1358 : /**
1359 : * Send our initial key set to @a client together with the
1360 : * "sync" terminator.
1361 : *
1362 : * @param client the client to inform
1363 : * @return #GNUNET_OK on success
1364 : */
1365 : static enum GNUNET_GenericReturnValue
1366 26 : cs_client_init (struct TES_Client *client)
1367 : {
1368 26 : size_t obs = 0;
1369 : char *buf;
1370 :
1371 26 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1372 : "Initializing new client %p\n",
1373 : client);
1374 26 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
1375 26 : for (struct Denomination *denom = denom_head;
1376 87 : NULL != denom;
1377 61 : denom = denom->next)
1378 : {
1379 61 : for (struct DenominationKey *dk = denom->keys_head;
1380 340 : NULL != dk;
1381 279 : dk = dk->next)
1382 : {
1383 279 : obs += ntohs (dk->an->header.size);
1384 : }
1385 : }
1386 26 : buf = GNUNET_malloc (obs);
1387 26 : obs = 0;
1388 26 : for (struct Denomination *denom = denom_head;
1389 87 : NULL != denom;
1390 61 : denom = denom->next)
1391 : {
1392 61 : for (struct DenominationKey *dk = denom->keys_head;
1393 340 : NULL != dk;
1394 279 : dk = dk->next)
1395 : {
1396 279 : GNUNET_memcpy (&buf[obs],
1397 : dk->an,
1398 : ntohs (dk->an->header.size));
1399 279 : obs += ntohs (dk->an->header.size);
1400 : }
1401 : }
1402 26 : client->key_gen = key_gen;
1403 26 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1404 26 : if (GNUNET_OK !=
1405 26 : TES_transmit_raw (client->csock,
1406 : obs,
1407 : buf))
1408 : {
1409 0 : GNUNET_free (buf);
1410 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1411 : "Client %p must have disconnected\n",
1412 : client);
1413 0 : return GNUNET_SYSERR;
1414 : }
1415 26 : GNUNET_free (buf);
1416 : {
1417 26 : struct GNUNET_MessageHeader synced = {
1418 26 : .type = htons (TALER_HELPER_CS_SYNCED),
1419 26 : .size = htons (sizeof (synced))
1420 : };
1421 :
1422 26 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1423 : "Sending CS SYNCED message to %p\n",
1424 : client);
1425 26 : if (GNUNET_OK !=
1426 26 : TES_transmit (client->csock,
1427 : &synced))
1428 : {
1429 0 : GNUNET_break (0);
1430 0 : return GNUNET_SYSERR;
1431 : }
1432 : }
1433 26 : return GNUNET_OK;
1434 : }
1435 :
1436 :
1437 : /**
1438 : * Notify @a client about all changes to the keys since
1439 : * the last generation known to the @a client.
1440 : *
1441 : * @param client the client to notify
1442 : * @return #GNUNET_OK on success
1443 : */
1444 : static enum GNUNET_GenericReturnValue
1445 22 : cs_update_client_keys (struct TES_Client *client)
1446 : {
1447 22 : size_t obs = 0;
1448 : char *buf;
1449 : enum GNUNET_GenericReturnValue ret;
1450 :
1451 22 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
1452 22 : for (struct Denomination *denom = denom_head;
1453 79 : NULL != denom;
1454 57 : denom = denom->next)
1455 : {
1456 57 : for (struct DenominationKey *key = denom->keys_head;
1457 298 : NULL != key;
1458 241 : key = key->next)
1459 : {
1460 241 : if (key->key_gen <= client->key_gen)
1461 233 : continue;
1462 8 : if (key->purge)
1463 3 : obs += sizeof (struct TALER_CRYPTO_CsKeyPurgeNotification);
1464 : else
1465 5 : obs += ntohs (key->an->header.size);
1466 : }
1467 : }
1468 22 : if (0 == obs)
1469 : {
1470 : /* nothing to do */
1471 17 : client->key_gen = key_gen;
1472 17 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1473 17 : return GNUNET_OK;
1474 : }
1475 5 : buf = GNUNET_malloc (obs);
1476 5 : obs = 0;
1477 5 : for (struct Denomination *denom = denom_head;
1478 10 : NULL != denom;
1479 5 : denom = denom->next)
1480 : {
1481 5 : for (struct DenominationKey *key = denom->keys_head;
1482 36 : NULL != key;
1483 31 : key = key->next)
1484 : {
1485 31 : if (key->key_gen <= client->key_gen)
1486 23 : continue;
1487 8 : if (key->purge)
1488 : {
1489 3 : struct TALER_CRYPTO_CsKeyPurgeNotification pn = {
1490 3 : .header.type = htons (TALER_HELPER_CS_MT_PURGE),
1491 3 : .header.size = htons (sizeof (pn)),
1492 : .h_cs = key->h_cs
1493 : };
1494 :
1495 3 : GNUNET_memcpy (&buf[obs],
1496 : &pn,
1497 : sizeof (pn));
1498 3 : GNUNET_assert (obs + sizeof (pn)
1499 : > obs);
1500 3 : obs += sizeof (pn);
1501 : }
1502 : else
1503 : {
1504 5 : GNUNET_memcpy (&buf[obs],
1505 : key->an,
1506 : ntohs (key->an->header.size));
1507 5 : GNUNET_assert (obs + ntohs (key->an->header.size)
1508 : > obs);
1509 5 : obs += ntohs (key->an->header.size);
1510 : }
1511 : }
1512 : }
1513 5 : client->key_gen = key_gen;
1514 5 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1515 5 : ret = TES_transmit_raw (client->csock,
1516 : obs,
1517 : buf);
1518 5 : GNUNET_free (buf);
1519 5 : return ret;
1520 : }
1521 :
1522 :
1523 : /**
1524 : * Create a new denomination key (we do not have enough).
1525 : *
1526 : * @param[in,out] denom denomination key to create
1527 : * @param anchor_start when to start key signing validity
1528 : * @param anchor_end when to end key signing validity
1529 : * @return #GNUNET_OK on success
1530 : */
1531 : static enum GNUNET_GenericReturnValue
1532 54 : create_key (struct Denomination *denom,
1533 : struct GNUNET_TIME_Timestamp anchor_start,
1534 : struct GNUNET_TIME_Timestamp anchor_end)
1535 : {
1536 : struct DenominationKey *dk;
1537 :
1538 54 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1539 : "Creating new key for `%s' with start date %s\n",
1540 : denom->section,
1541 : GNUNET_TIME_timestamp2s (anchor_start));
1542 54 : dk = GNUNET_new (struct DenominationKey);
1543 54 : dk->denom = denom;
1544 54 : dk->anchor_start = anchor_start;
1545 54 : dk->anchor_end = anchor_end;
1546 54 : if (GNUNET_OK !=
1547 54 : setup_key (dk,
1548 : denom->keys_tail))
1549 : {
1550 0 : GNUNET_break (0);
1551 0 : GNUNET_free (dk);
1552 0 : GNUNET_SCHEDULER_shutdown ();
1553 0 : globals->global_ret = EXIT_FAILURE;
1554 0 : return GNUNET_SYSERR;
1555 : }
1556 54 : return GNUNET_OK;
1557 : }
1558 :
1559 :
1560 : /**
1561 : * Obtain the maximum withdraw duration of all denominations.
1562 : *
1563 : * Must only be called while the #keys_lock is held.
1564 : *
1565 : * @return maximum withdraw duration, zero if there are no denominations
1566 : */
1567 : static struct GNUNET_TIME_Relative
1568 38 : get_maximum_duration (void)
1569 : {
1570 38 : struct GNUNET_TIME_Relative ret
1571 : = GNUNET_TIME_UNIT_ZERO;
1572 :
1573 38 : for (struct Denomination *denom = denom_head;
1574 201 : NULL != denom;
1575 163 : denom = denom->next)
1576 : {
1577 163 : ret = GNUNET_TIME_relative_max (ret,
1578 : denom->duration_withdraw);
1579 : }
1580 38 : return ret;
1581 : }
1582 :
1583 :
1584 : /**
1585 : * At what time do we need to next create keys if we just did?
1586 : *
1587 : * @return time when to next create keys if we just finished key generation
1588 : */
1589 : static struct GNUNET_TIME_Absolute
1590 10 : action_time (void)
1591 : {
1592 10 : struct GNUNET_TIME_Relative md = get_maximum_duration ();
1593 10 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1594 : uint64_t mod;
1595 :
1596 10 : if (GNUNET_TIME_relative_is_zero (md))
1597 0 : return GNUNET_TIME_UNIT_FOREVER_ABS;
1598 10 : mod = now.abs_value_us % md.rel_value_us;
1599 10 : now.abs_value_us -= mod;
1600 10 : return GNUNET_TIME_absolute_add (now,
1601 : md);
1602 : }
1603 :
1604 :
1605 : /**
1606 : * Remove all denomination keys of @a denom that have expired.
1607 : *
1608 : * @param[in,out] denom denomination family to remove keys for
1609 : */
1610 : static void
1611 216 : remove_expired_denomination_keys (struct Denomination *denom)
1612 : {
1613 428 : while ( (NULL != denom->keys_head) &&
1614 208 : GNUNET_TIME_absolute_is_past (
1615 208 : denom->keys_head->anchor_end.abs_time) )
1616 : {
1617 4 : struct DenominationKey *key = denom->keys_head;
1618 4 : struct DenominationKey *nxt = key->next;
1619 :
1620 4 : if (0 != key->rc)
1621 0 : break; /* later */
1622 4 : GNUNET_CONTAINER_DLL_remove (denom->keys_head,
1623 : denom->keys_tail,
1624 : key);
1625 4 : GNUNET_assert (GNUNET_OK ==
1626 : GNUNET_CONTAINER_multihashmap_remove (
1627 : keys,
1628 : &key->h_cs.hash,
1629 : key));
1630 8 : if ( (! key->purge) &&
1631 4 : (0 != unlink (key->filename)) )
1632 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1633 : "unlink",
1634 : key->filename);
1635 4 : GNUNET_free (key->filename);
1636 4 : GNUNET_free (key->an);
1637 4 : GNUNET_free (key);
1638 4 : key = nxt;
1639 : }
1640 216 : }
1641 :
1642 :
1643 : /**
1644 : * Obtain the end anchor to use at this point. Uses the
1645 : * #lookahead_sign and then rounds it up by the maximum
1646 : * duration of any denomination to arrive at a globally
1647 : * valid end-date.
1648 : *
1649 : * Must only be called while the #keys_lock is held.
1650 : *
1651 : * @return end anchor
1652 : */
1653 : static struct GNUNET_TIME_Timestamp
1654 28 : get_anchor_end (void)
1655 : {
1656 28 : struct GNUNET_TIME_Relative md = get_maximum_duration ();
1657 : struct GNUNET_TIME_Absolute end
1658 28 : = GNUNET_TIME_relative_to_absolute (lookahead_sign);
1659 : uint64_t mod;
1660 :
1661 28 : if (GNUNET_TIME_relative_is_zero (md))
1662 10 : return GNUNET_TIME_UNIT_ZERO_TS;
1663 : /* Round up 'end' to a multiple of 'md' */
1664 18 : mod = end.abs_value_us % md.rel_value_us;
1665 18 : end.abs_value_us -= mod;
1666 18 : return GNUNET_TIME_absolute_to_timestamp (
1667 : GNUNET_TIME_absolute_add (end,
1668 : md));
1669 : }
1670 :
1671 :
1672 : /**
1673 : * Create all denomination keys that are required for our
1674 : * desired lookahead and that we do not yet have.
1675 : *
1676 : * @param[in,out] opt our options
1677 : * @param[in,out] wake set to true if we should wake the clients
1678 : */
1679 : static void
1680 28 : create_missing_keys (struct TALER_SECMOD_Options *opt,
1681 : bool *wake)
1682 : {
1683 : struct GNUNET_TIME_Timestamp start;
1684 : struct GNUNET_TIME_Timestamp end;
1685 :
1686 28 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1687 : "Updating denominations ...\n");
1688 28 : start = opt->global_now;
1689 28 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
1690 28 : end = get_anchor_end ();
1691 28 : for (struct Denomination *denom = denom_head;
1692 136 : NULL != denom;
1693 108 : denom = denom->next)
1694 : {
1695 : struct GNUNET_TIME_Timestamp anchor_start;
1696 : struct GNUNET_TIME_Timestamp anchor_end;
1697 : struct GNUNET_TIME_Timestamp next_end;
1698 108 : bool finished = false;
1699 :
1700 108 : remove_expired_denomination_keys (denom);
1701 108 : if (NULL != denom->keys_tail)
1702 : {
1703 96 : anchor_start = denom->keys_tail->anchor_end;
1704 96 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1705 : "Expanding keys of denomination `%s', last key %s valid for another %s\n",
1706 : denom->section,
1707 : GNUNET_h2s (&denom->keys_tail->h_cs.hash),
1708 : GNUNET_TIME_relative2s (
1709 : GNUNET_TIME_absolute_get_remaining (
1710 : anchor_start.abs_time),
1711 : true));
1712 : }
1713 : else
1714 : {
1715 12 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1716 : "Starting keys of denomination `%s'\n",
1717 : denom->section);
1718 12 : anchor_start = start;
1719 : }
1720 108 : finished = GNUNET_TIME_timestamp_cmp (anchor_start,
1721 : >=,
1722 : end);
1723 162 : while (! finished)
1724 : {
1725 54 : anchor_end = GNUNET_TIME_absolute_to_timestamp (
1726 : GNUNET_TIME_absolute_add (anchor_start.abs_time,
1727 : denom->duration_withdraw));
1728 54 : next_end = GNUNET_TIME_absolute_to_timestamp (
1729 : GNUNET_TIME_absolute_add (anchor_end.abs_time,
1730 : denom->duration_withdraw));
1731 54 : if (GNUNET_TIME_timestamp_cmp (next_end,
1732 : >,
1733 : end))
1734 : {
1735 15 : anchor_end = end; /* extend period to align end periods */
1736 15 : finished = true;
1737 : }
1738 : /* adjust start time down to ensure overlap */
1739 54 : anchor_start = GNUNET_TIME_absolute_to_timestamp (
1740 : GNUNET_TIME_absolute_subtract (anchor_start.abs_time,
1741 : overlap_duration));
1742 54 : if (! *wake)
1743 : {
1744 2 : key_gen++;
1745 2 : *wake = true;
1746 : }
1747 54 : if (GNUNET_OK !=
1748 54 : create_key (denom,
1749 : anchor_start,
1750 : anchor_end))
1751 : {
1752 0 : GNUNET_break (0);
1753 0 : return;
1754 : }
1755 54 : anchor_start = anchor_end;
1756 : }
1757 108 : remove_expired_denomination_keys (denom);
1758 : }
1759 28 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1760 28 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1761 : "Updating denominations finished ...\n");
1762 : }
1763 :
1764 :
1765 : /**
1766 : * Task run periodically to expire keys and/or generate fresh ones.
1767 : *
1768 : * @param cls the `struct TALER_SECMOD_Options *`
1769 : */
1770 : static void
1771 10 : update_denominations (void *cls)
1772 : {
1773 10 : struct TALER_SECMOD_Options *opt = cls;
1774 : struct GNUNET_TIME_Absolute at;
1775 10 : bool wake = false;
1776 :
1777 : (void) cls;
1778 10 : keygen_task = NULL;
1779 10 : opt->global_now = GNUNET_TIME_timestamp_get ();
1780 10 : create_missing_keys (opt,
1781 : &wake);
1782 10 : if (wake)
1783 2 : TES_wake_clients ();
1784 10 : at = action_time ();
1785 10 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1786 : "Next key generation due at %s\n",
1787 : GNUNET_TIME_absolute2s (at));
1788 10 : keygen_task = GNUNET_SCHEDULER_add_at (at,
1789 : &update_denominations,
1790 : opt);
1791 10 : }
1792 :
1793 :
1794 : /**
1795 : * Parse private key of denomination @a denom in @a buf.
1796 : *
1797 : * @param[out] denom denomination of the key
1798 : * @param filename name of the file we are parsing, for logging
1799 : * @param priv key material
1800 : */
1801 : static void
1802 165 : parse_key (struct Denomination *denom,
1803 : const char *filename,
1804 : const struct GNUNET_CRYPTO_CsPrivateKey *priv)
1805 : {
1806 : char *anchor_s;
1807 : char dummy;
1808 : unsigned long long anchor_start_ll;
1809 : unsigned long long anchor_end_ll;
1810 : struct GNUNET_TIME_Timestamp anchor_start;
1811 : struct GNUNET_TIME_Timestamp anchor_end;
1812 165 : char *nf = NULL;
1813 :
1814 165 : anchor_s = strrchr (filename,
1815 : '/');
1816 165 : if (NULL == anchor_s)
1817 : {
1818 : /* File in a directory without '/' in the name, this makes no sense. */
1819 0 : GNUNET_break (0);
1820 0 : return;
1821 : }
1822 165 : anchor_s++;
1823 165 : if (2 != sscanf (anchor_s,
1824 : "%llu-%llu%c",
1825 : &anchor_start_ll,
1826 : &anchor_end_ll,
1827 : &dummy))
1828 : {
1829 : /* try legacy mode */
1830 0 : if (1 != sscanf (anchor_s,
1831 : "%llu%c",
1832 : &anchor_start_ll,
1833 : &dummy))
1834 : {
1835 : /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */
1836 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1837 : "Filename `%s' invalid for key file, skipping\n",
1838 : anchor_s);
1839 0 : return;
1840 : }
1841 : anchor_start.abs_time.abs_value_us
1842 0 : = anchor_start_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
1843 0 : if (anchor_start_ll != anchor_start.abs_time.abs_value_us
1844 0 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
1845 : {
1846 : /* Integer overflow. Bad, invalid filename. */
1847 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1848 : "Integer overflow. Filename `%s' invalid for key file, skipping\n",
1849 : anchor_s);
1850 0 : return;
1851 : }
1852 : anchor_end
1853 0 : = GNUNET_TIME_absolute_to_timestamp (
1854 : GNUNET_TIME_absolute_add (anchor_start.abs_time,
1855 : denom->duration_withdraw));
1856 0 : GNUNET_asprintf (
1857 : &nf,
1858 : "%s/%s/%llu-%llu",
1859 : keydir,
1860 : denom->section,
1861 : anchor_start_ll,
1862 0 : (unsigned long long) (anchor_end.abs_time.abs_value_us
1863 0 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us));
1864 : /* Try to fix the legacy filename */
1865 0 : if (0 !=
1866 0 : rename (filename,
1867 : nf))
1868 : {
1869 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1870 : "rename",
1871 : filename);
1872 0 : GNUNET_free (nf);
1873 : }
1874 : }
1875 : else
1876 : {
1877 : anchor_start.abs_time.abs_value_us
1878 165 : = anchor_start_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
1879 : anchor_end.abs_time.abs_value_us
1880 165 : = anchor_end_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
1881 330 : if ( (anchor_start_ll != anchor_start.abs_time.abs_value_us
1882 165 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us) ||
1883 330 : (anchor_end_ll != anchor_end.abs_time.abs_value_us
1884 165 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us) )
1885 : {
1886 : /* Integer overflow. Bad, invalid filename. */
1887 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1888 : "Integer overflow. Filename `%s' invalid for key file, skipping\n",
1889 : anchor_s);
1890 0 : return;
1891 : }
1892 : }
1893 :
1894 : {
1895 : struct DenominationKey *dk;
1896 : struct DenominationKey *before;
1897 :
1898 165 : dk = GNUNET_new (struct DenominationKey);
1899 165 : dk->denom_priv = *priv;
1900 165 : dk->denom = denom;
1901 165 : dk->anchor_start = anchor_start;
1902 165 : dk->anchor_end = anchor_end;
1903 165 : dk->filename = (NULL == nf) ? GNUNET_strdup (filename) : nf;
1904 165 : GNUNET_CRYPTO_cs_private_key_get_public (priv,
1905 : &dk->denom_pub);
1906 165 : GNUNET_CRYPTO_hash (&dk->denom_pub,
1907 : sizeof (dk->denom_pub),
1908 : &dk->h_cs.hash);
1909 165 : generate_response (dk);
1910 165 : if (GNUNET_OK !=
1911 165 : GNUNET_CONTAINER_multihashmap_put (
1912 : keys,
1913 165 : &dk->h_cs.hash,
1914 : dk,
1915 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1916 : {
1917 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1918 : "Duplicate private key %s detected in file `%s'. Skipping.\n",
1919 : GNUNET_h2s (&dk->h_cs.hash),
1920 : filename);
1921 0 : GNUNET_free (dk->an);
1922 0 : GNUNET_free (dk);
1923 0 : return;
1924 : }
1925 165 : before = NULL;
1926 165 : for (struct DenominationKey *pos = denom->keys_head;
1927 165 : NULL != pos;
1928 0 : pos = pos->next)
1929 : {
1930 124 : if (GNUNET_TIME_timestamp_cmp (pos->anchor_start,
1931 : >,
1932 : anchor_start))
1933 124 : break;
1934 0 : before = pos;
1935 : }
1936 165 : GNUNET_CONTAINER_DLL_insert_after (denom->keys_head,
1937 : denom->keys_tail,
1938 : before,
1939 : dk);
1940 165 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1941 : "Imported key %s from `%s'\n",
1942 : GNUNET_h2s (&dk->h_cs.hash),
1943 : filename);
1944 : }
1945 : }
1946 :
1947 :
1948 : /**
1949 : * Import a private key from @a filename for the denomination
1950 : * given in @a cls.
1951 : *
1952 : * @param[in,out] cls a `struct Denomiantion`
1953 : * @param filename name of a file in the directory
1954 : * @return #GNUNET_OK (always, continue to iterate)
1955 : */
1956 : static enum GNUNET_GenericReturnValue
1957 165 : import_key (void *cls,
1958 : const char *filename)
1959 : {
1960 165 : struct Denomination *denom = cls;
1961 : struct GNUNET_DISK_FileHandle *fh;
1962 : struct GNUNET_DISK_MapHandle *map;
1963 : void *ptr;
1964 : int fd;
1965 : struct stat sbuf;
1966 :
1967 : {
1968 : struct stat lsbuf;
1969 :
1970 165 : if (0 != lstat (filename,
1971 : &lsbuf))
1972 : {
1973 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1974 : "lstat",
1975 : filename);
1976 0 : return GNUNET_OK;
1977 : }
1978 165 : if (! S_ISREG (lsbuf.st_mode))
1979 : {
1980 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1981 : "File `%s' is not a regular file, which is not allowed for private keys!\n",
1982 : filename);
1983 0 : return GNUNET_OK;
1984 : }
1985 : }
1986 :
1987 165 : fd = open (filename,
1988 : O_RDONLY | O_CLOEXEC);
1989 165 : if (-1 == fd)
1990 : {
1991 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1992 : "open",
1993 : filename);
1994 0 : return GNUNET_OK;
1995 : }
1996 165 : if (0 != fstat (fd,
1997 : &sbuf))
1998 : {
1999 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2000 : "stat",
2001 : filename);
2002 0 : GNUNET_break (0 == close (fd));
2003 0 : return GNUNET_OK;
2004 : }
2005 165 : if (! S_ISREG (sbuf.st_mode))
2006 : {
2007 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2008 : "File `%s' is not a regular file, which is not allowed for private keys!\n",
2009 : filename);
2010 0 : GNUNET_break (0 == close (fd));
2011 0 : return GNUNET_OK;
2012 : }
2013 165 : if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
2014 : {
2015 : /* permission are NOT tight, try to patch them up! */
2016 0 : if (0 !=
2017 0 : fchmod (fd,
2018 : S_IRUSR))
2019 : {
2020 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2021 : "fchmod",
2022 : filename);
2023 : /* refuse to use key if file has wrong permissions */
2024 0 : GNUNET_break (0 == close (fd));
2025 0 : return GNUNET_OK;
2026 : }
2027 : }
2028 165 : fh = GNUNET_DISK_get_handle_from_int_fd (fd);
2029 165 : if (NULL == fh)
2030 : {
2031 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2032 : "open",
2033 : filename);
2034 0 : GNUNET_break (0 == close (fd));
2035 0 : return GNUNET_OK;
2036 : }
2037 165 : if (sbuf.st_size != sizeof(struct GNUNET_CRYPTO_CsPrivateKey))
2038 : {
2039 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2040 : "File `%s' too big to be a private key\n",
2041 : filename);
2042 0 : GNUNET_DISK_file_close (fh);
2043 0 : return GNUNET_OK;
2044 : }
2045 165 : ptr = GNUNET_DISK_file_map (fh,
2046 : &map,
2047 : GNUNET_DISK_MAP_TYPE_READ,
2048 165 : (size_t) sbuf.st_size);
2049 165 : if (NULL == ptr)
2050 : {
2051 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2052 : "mmap",
2053 : filename);
2054 0 : GNUNET_DISK_file_close (fh);
2055 0 : return GNUNET_OK;
2056 : }
2057 165 : parse_key (denom,
2058 : filename,
2059 : (const struct GNUNET_CRYPTO_CsPrivateKey *) ptr);
2060 165 : GNUNET_DISK_file_unmap (map);
2061 165 : GNUNET_DISK_file_close (fh);
2062 165 : return GNUNET_OK;
2063 : }
2064 :
2065 :
2066 : /**
2067 : * Parse configuration for denomination type parameters. Also determines
2068 : * our anchor by looking at the existing denominations of the same type.
2069 : *
2070 : * @param cfg configuration to use
2071 : * @param ct section in the configuration file giving the denomination type parameters
2072 : * @param[out] denom set to the denomination parameters from the configuration
2073 : * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid
2074 : */
2075 : static enum GNUNET_GenericReturnValue
2076 53 : parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
2077 : const char *ct,
2078 : struct Denomination *denom)
2079 : {
2080 : char *secname;
2081 :
2082 53 : GNUNET_asprintf (&secname,
2083 : "%s-secmod-cs",
2084 53 : globals->section);
2085 53 : if (GNUNET_OK !=
2086 53 : GNUNET_CONFIGURATION_get_value_time (cfg,
2087 : ct,
2088 : "DURATION_WITHDRAW",
2089 : &denom->duration_withdraw))
2090 : {
2091 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2092 : ct,
2093 : "DURATION_WITHDRAW");
2094 0 : GNUNET_free (secname);
2095 0 : return GNUNET_SYSERR;
2096 : }
2097 53 : if (GNUNET_TIME_relative_cmp (denom->duration_withdraw,
2098 : <,
2099 : GNUNET_TIME_UNIT_SECONDS))
2100 : {
2101 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
2102 : ct,
2103 : "DURATION_WITHDRAW",
2104 : "less than one second is not supported");
2105 0 : GNUNET_free (secname);
2106 0 : return GNUNET_SYSERR;
2107 : }
2108 53 : if (GNUNET_TIME_relative_cmp (overlap_duration,
2109 : >=,
2110 : denom->duration_withdraw))
2111 : {
2112 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
2113 : secname,
2114 : "OVERLAP_DURATION",
2115 : "Value given must be smaller than value for DURATION_WITHDRAW!");
2116 0 : GNUNET_free (secname);
2117 0 : return GNUNET_SYSERR;
2118 : }
2119 53 : GNUNET_free (secname);
2120 53 : denom->section = GNUNET_strdup (ct);
2121 53 : return GNUNET_OK;
2122 : }
2123 :
2124 :
2125 : /**
2126 : * Closure for #load_denominations.
2127 : */
2128 : struct LoadContext
2129 : {
2130 :
2131 : /**
2132 : * Configuration to use.
2133 : */
2134 : const struct GNUNET_CONFIGURATION_Handle *cfg;
2135 :
2136 : /**
2137 : * Current time to use.
2138 : */
2139 : struct GNUNET_TIME_Timestamp t;
2140 :
2141 : /**
2142 : * Status, to be set to #GNUNET_SYSERR on failure
2143 : */
2144 : enum GNUNET_GenericReturnValue ret;
2145 : };
2146 :
2147 :
2148 : /**
2149 : * Generate new denomination signing keys for the denomination type of the given @a
2150 : * denomination_alias.
2151 : *
2152 : * @param cls a `struct LoadContext`, with 'ret' to be set to #GNUNET_SYSERR on failure
2153 : * @param denomination_alias name of the denomination's section in the configuration
2154 : */
2155 : static void
2156 894 : load_denominations (void *cls,
2157 : const char *denomination_alias)
2158 : {
2159 894 : struct LoadContext *ctx = cls;
2160 : struct Denomination *denom;
2161 : char *cipher;
2162 :
2163 894 : if ( (0 != strncasecmp (denomination_alias,
2164 : "coin_",
2165 777 : strlen ("coin_"))) &&
2166 777 : (0 != strncasecmp (denomination_alias,
2167 : "coin-",
2168 : strlen ("coin-"))) )
2169 841 : return; /* not a denomination type definition */
2170 117 : if (GNUNET_OK !=
2171 117 : GNUNET_CONFIGURATION_get_value_string (ctx->cfg,
2172 : denomination_alias,
2173 : "CIPHER",
2174 : &cipher))
2175 : {
2176 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2177 : denomination_alias,
2178 : "CIPHER");
2179 0 : return;
2180 : }
2181 117 : if (0 != strcmp (cipher, "CS"))
2182 : {
2183 64 : GNUNET_free (cipher);
2184 64 : return; /* Ignore denominations of other types than CS*/
2185 : }
2186 53 : GNUNET_free (cipher);
2187 :
2188 53 : denom = GNUNET_new (struct Denomination);
2189 53 : if (GNUNET_OK !=
2190 53 : parse_denomination_cfg (ctx->cfg,
2191 : denomination_alias,
2192 : denom))
2193 : {
2194 0 : ctx->ret = GNUNET_SYSERR;
2195 0 : GNUNET_free (denom);
2196 0 : return;
2197 : }
2198 53 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2199 : "Loading keys for denomination %s\n",
2200 : denom->section);
2201 : {
2202 : char *dname;
2203 :
2204 53 : GNUNET_asprintf (&dname,
2205 : "%s/%s",
2206 : keydir,
2207 : denom->section);
2208 53 : GNUNET_break (GNUNET_OK ==
2209 : GNUNET_DISK_directory_create (dname));
2210 53 : GNUNET_DISK_directory_scan (dname,
2211 : &import_key,
2212 : denom);
2213 53 : GNUNET_free (dname);
2214 : }
2215 53 : GNUNET_CONTAINER_DLL_insert (denom_head,
2216 : denom_tail,
2217 : denom);
2218 : }
2219 :
2220 :
2221 : /**
2222 : * Load the various duration values from @a cfg
2223 : *
2224 : * @param cfg configuration to use
2225 : * @return #GNUNET_OK on success
2226 : */
2227 : static enum GNUNET_GenericReturnValue
2228 18 : load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
2229 : {
2230 : char *secname;
2231 :
2232 18 : GNUNET_asprintf (&secname,
2233 : "%s-secmod-cs",
2234 18 : globals->section);
2235 18 : if (GNUNET_OK !=
2236 18 : GNUNET_CONFIGURATION_get_value_time (cfg,
2237 : secname,
2238 : "OVERLAP_DURATION",
2239 : &overlap_duration))
2240 : {
2241 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2242 : secname,
2243 : "OVERLAP_DURATION");
2244 0 : GNUNET_free (secname);
2245 0 : return GNUNET_SYSERR;
2246 : }
2247 18 : if (GNUNET_OK !=
2248 18 : GNUNET_CONFIGURATION_get_value_time (cfg,
2249 : secname,
2250 : "LOOKAHEAD_SIGN",
2251 : &lookahead_sign))
2252 : {
2253 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2254 : secname,
2255 : "LOOKAHEAD_SIGN");
2256 0 : GNUNET_free (secname);
2257 0 : return GNUNET_SYSERR;
2258 : }
2259 18 : GNUNET_free (secname);
2260 18 : return GNUNET_OK;
2261 : }
2262 :
2263 :
2264 : /**
2265 : * Function run on shutdown. Stops the various jobs (nicely).
2266 : *
2267 : * @param cls a `struct TALER_SECMOD_Options`
2268 : */
2269 : static void
2270 18 : do_shutdown (void *cls)
2271 : {
2272 : (void) cls;
2273 18 : TES_listen_stop ();
2274 18 : if (NULL != keygen_task)
2275 : {
2276 8 : GNUNET_SCHEDULER_cancel (keygen_task);
2277 8 : keygen_task = NULL;
2278 : }
2279 18 : stop_workers ();
2280 18 : sem_done (&worker_sem);
2281 18 : }
2282 :
2283 :
2284 : void
2285 18 : TALER_SECMOD_cs_run (void *cls,
2286 : char *const *args,
2287 : const char *cfgfile,
2288 : const struct GNUNET_CONFIGURATION_Handle *cfg)
2289 : {
2290 : static struct TES_Callbacks cb = {
2291 : .dispatch = &cs_work_dispatch,
2292 : .updater = &cs_update_client_keys,
2293 : .init = &cs_client_init
2294 : };
2295 18 : struct TALER_SECMOD_Options *opt = cls;
2296 : char *secname;
2297 :
2298 : (void) args;
2299 : (void) cfgfile;
2300 18 : globals = opt;
2301 18 : if (GNUNET_TIME_timestamp_cmp (opt->global_now,
2302 : !=,
2303 : opt->global_now_tmp))
2304 : {
2305 : /* The user gave "--now", use it! */
2306 0 : opt->global_now = opt->global_now_tmp;
2307 : }
2308 : else
2309 : {
2310 : /* get current time again, we may be timetraveling! */
2311 18 : opt->global_now = GNUNET_TIME_timestamp_get ();
2312 : }
2313 18 : GNUNET_asprintf (&secname,
2314 : "%s-secmod-cs",
2315 : opt->section);
2316 18 : if (GNUNET_OK !=
2317 18 : GNUNET_CONFIGURATION_get_value_filename (cfg,
2318 : secname,
2319 : "KEY_DIR",
2320 : &keydir))
2321 : {
2322 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2323 : secname,
2324 : "KEY_DIR");
2325 0 : GNUNET_free (secname);
2326 0 : opt->global_ret = EXIT_NOTCONFIGURED;
2327 10 : return;
2328 : }
2329 18 : if (GNUNET_OK !=
2330 18 : load_durations (cfg))
2331 : {
2332 0 : opt->global_ret = EXIT_NOTCONFIGURED;
2333 0 : GNUNET_free (secname);
2334 0 : return;
2335 : }
2336 18 : opt->global_ret = TES_listen_start (cfg,
2337 : secname,
2338 : &cb);
2339 18 : GNUNET_free (secname);
2340 18 : if (0 != opt->global_ret)
2341 0 : return;
2342 18 : sem_init (&worker_sem,
2343 : 0);
2344 18 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
2345 : opt);
2346 18 : if (0 == opt->max_workers)
2347 : {
2348 : long lret;
2349 :
2350 0 : lret = sysconf (_SC_NPROCESSORS_CONF);
2351 0 : if (lret <= 0)
2352 0 : lret = 1;
2353 0 : opt->max_workers = (unsigned int) lret;
2354 : }
2355 306 : for (unsigned int i = 0; i<opt->max_workers; i++)
2356 288 : if (GNUNET_OK !=
2357 288 : start_worker ())
2358 : {
2359 0 : GNUNET_SCHEDULER_shutdown ();
2360 0 : return;
2361 : }
2362 : /* Load denominations */
2363 18 : keys = GNUNET_CONTAINER_multihashmap_create (65536,
2364 : true);
2365 : {
2366 18 : struct LoadContext lc = {
2367 : .cfg = cfg,
2368 : .ret = GNUNET_OK,
2369 : .t = opt->global_now
2370 : };
2371 18 : bool wake = true;
2372 :
2373 18 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
2374 18 : GNUNET_CONFIGURATION_iterate_sections (cfg,
2375 : &load_denominations,
2376 : &lc);
2377 18 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
2378 18 : if (GNUNET_OK != lc.ret)
2379 : {
2380 0 : opt->global_ret = EXIT_FAILURE;
2381 0 : GNUNET_SCHEDULER_shutdown ();
2382 0 : return;
2383 : }
2384 18 : create_missing_keys (opt,
2385 : &wake);
2386 : }
2387 18 : if (NULL == denom_head)
2388 : {
2389 10 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2390 : "No CS denominations configured. Make sure section names start with `%s' if you are using CS!\n",
2391 : opt->section);
2392 10 : TES_wake_clients ();
2393 10 : return;
2394 : }
2395 : /* start job to keep keys up-to-date; MUST be run before the #listen_task,
2396 : hence with priority. */
2397 8 : keygen_task = GNUNET_SCHEDULER_add_with_priority (
2398 : GNUNET_SCHEDULER_PRIORITY_URGENT,
2399 : &update_denominations,
2400 : opt);
2401 : }
|