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 "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 1133 : 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 1133 : 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 1147 : GNUNET_CRYPTO_cs_r_derive (&planchet->nonce,
479 : for_melt ? "rm" : "rw",
480 1147 : &dk->denom_priv,
481 : r);
482 1148 : GNUNET_CRYPTO_cs_sign_derive (&dk->denom_priv,
483 : r,
484 : planchet,
485 : cs_sigp);
486 1147 : 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 1145 : 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 1142 : send_signature (struct TES_Client *client,
547 : const struct GNUNET_CRYPTO_CsBlindSignature *cs_answer)
548 : {
549 : struct TALER_CRYPTO_SignResponse sres;
550 :
551 1142 : sres.header.size = htons (sizeof (sres));
552 1142 : sres.header.type = htons (TALER_HELPER_CS_MT_RES_SIGNATURE);
553 1142 : sres.b = htonl (cs_answer->b);
554 1142 : sres.cs_answer = cs_answer->s_scalar;
555 1142 : 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 0 : handle_sign_request (struct TES_Client *client,
571 : const struct TALER_CRYPTO_CsSignRequestMessage *sr)
572 : {
573 : struct GNUNET_CRYPTO_CsBlindSignature cs_answer;
574 0 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
575 : enum TALER_ErrorCode ec;
576 : enum GNUNET_GenericReturnValue ret;
577 :
578 0 : ec = do_sign (&sr->h_cs,
579 : &sr->message,
580 0 : (0 != ntohl (sr->for_melt)),
581 : &cs_answer);
582 0 : if (TALER_EC_NONE != ec)
583 : {
584 0 : return fail_sign (client,
585 : ec);
586 : }
587 0 : ret = send_signature (client,
588 : &cs_answer);
589 0 : 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 0 : 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 948 : 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 948 : 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 610 : 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 608 : 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 356 : GNUNET_CRYPTO_cs_r_derive (nonce,
654 : for_melt ? "rm" : "rw",
655 356 : &dk->denom_priv,
656 : r_priv.r);
657 357 : 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 354 : GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[1],
664 : &rpairp->r_pub[1]);
665 352 : 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 2423 : sem_init (struct Semaphore *sem,
699 : unsigned int val)
700 : {
701 2423 : GNUNET_assert (0 ==
702 : pthread_mutex_init (&sem->mutex,
703 : NULL));
704 2424 : GNUNET_assert (0 ==
705 : pthread_cond_init (&sem->cv,
706 : NULL));
707 2423 : sem->ctr = val;
708 2423 : }
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 6878 : sem_down (struct Semaphore *sem)
718 : {
719 6878 : GNUNET_assert (0 == pthread_mutex_lock (&sem->mutex));
720 10376 : while (0 == sem->ctr)
721 : {
722 3507 : pthread_cond_wait (&sem->cv,
723 : &sem->mutex);
724 : }
725 6869 : sem->ctr--;
726 6869 : GNUNET_assert (0 == pthread_mutex_unlock (&sem->mutex));
727 6833 : }
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 6861 : sem_up (struct Semaphore *sem)
737 : {
738 6861 : GNUNET_assert (0 == pthread_mutex_lock (&sem->mutex));
739 6909 : sem->ctr++;
740 6909 : GNUNET_assert (0 == pthread_mutex_unlock (&sem->mutex));
741 6896 : pthread_cond_signal (&sem->cv);
742 6888 : }
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 2422 : sem_done (struct Semaphore *sem)
752 : {
753 2422 : GNUNET_break (0 == pthread_cond_destroy (&sem->cv));
754 2428 : GNUNET_break (0 == pthread_mutex_destroy (&sem->mutex));
755 2426 : }
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 2391 : GNUNET_assert (0 == pthread_mutex_lock (&worker_lock));
773 2410 : GNUNET_CONTAINER_DLL_insert (worker_head,
774 : worker_tail,
775 : w);
776 2410 : GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock));
777 2404 : sem_up (&worker_sem);
778 2403 : sem_down (&w->sem);
779 2391 : if (w->do_shutdown)
780 288 : break;
781 : {
782 2103 : struct BatchJob *bj = w->job;
783 :
784 2103 : switch (bj->type)
785 : {
786 1142 : case TYPE_SIGN:
787 : {
788 1142 : const struct TALER_CRYPTO_CsSignRequestMessage *sr
789 : = bj->details.sign.sr;
790 :
791 2288 : bj->ec = do_sign (&sr->h_cs,
792 : &sr->message,
793 1142 : (0 != ntohl (sr->for_melt)),
794 : &bj->details.sign.cs_answer);
795 1146 : break;
796 : }
797 959 : case TYPE_RDERIVE:
798 : {
799 959 : const struct TALER_CRYPTO_CsRDeriveRequest *rdr
800 : = bj->details.rderive.rdr;
801 1924 : bj->ec = do_derive (&rdr->h_cs,
802 : &rdr->nonce,
803 959 : (0 != ntohl (rdr->for_melt)),
804 : &bj->details.rderive.rpairp);
805 965 : break;
806 : }
807 : }
808 2113 : sem_up (&bj->sem);
809 2103 : 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 1148 : start_sign_job (const struct TALER_CRYPTO_CsSignRequestMessage *sr,
824 : struct BatchJob *bj)
825 : {
826 1148 : sem_init (&bj->sem,
827 : 0);
828 1145 : bj->type = TYPE_SIGN;
829 1145 : bj->details.sign.sr = sr;
830 1145 : sem_down (&worker_sem);
831 1152 : GNUNET_assert (0 == pthread_mutex_lock (&worker_lock));
832 1152 : bj->worker = worker_head;
833 1152 : GNUNET_CONTAINER_DLL_remove (worker_head,
834 : worker_tail,
835 : bj->worker);
836 1152 : GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock));
837 1152 : bj->worker->job = bj;
838 1152 : sem_up (&bj->worker->sem);
839 1151 : }
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 970 : start_derive_job (const struct TALER_CRYPTO_CsRDeriveRequest *rdr,
850 : struct BatchJob *bj)
851 : {
852 970 : sem_init (&bj->sem,
853 : 0);
854 970 : bj->type = TYPE_RDERIVE;
855 970 : bj->details.rderive.rdr = rdr;
856 970 : sem_down (&worker_sem);
857 970 : GNUNET_assert (0 == pthread_mutex_lock (&worker_lock));
858 970 : bj->worker = worker_head;
859 970 : GNUNET_CONTAINER_DLL_remove (worker_head,
860 : worker_tail,
861 : bj->worker);
862 970 : GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock));
863 970 : bj->worker->job = bj;
864 970 : sem_up (&bj->worker->sem);
865 970 : }
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 2116 : finish_job (struct TES_Client *client,
876 : struct BatchJob *bj)
877 : {
878 2116 : sem_down (&bj->sem);
879 2120 : sem_done (&bj->sem);
880 2119 : switch (bj->type)
881 : {
882 1149 : case TYPE_SIGN:
883 1149 : if (TALER_EC_NONE != bj->ec)
884 : {
885 3 : fail_sign (client,
886 : bj->ec);
887 3 : return;
888 : }
889 1146 : send_signature (client,
890 1146 : &bj->details.sign.cs_answer);
891 1144 : break;
892 970 : case TYPE_RDERIVE:
893 970 : if (TALER_EC_NONE != bj->ec)
894 : {
895 613 : fail_derive (client,
896 : bj->ec);
897 613 : return;
898 : }
899 357 : send_derivation (client,
900 357 : &bj->details.rderive.rpairp);
901 357 : 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 953 : handle_batch_sign_request (struct TES_Client *client,
916 : const struct TALER_CRYPTO_BatchSignRequest *bsr)
917 953 : {
918 953 : uint32_t bs = ntohl (bsr->batch_size);
919 953 : uint16_t size = ntohs (bsr->header.size) - sizeof (*bsr);
920 953 : const void *off = (const void *) &bsr[1];
921 953 : unsigned int idx = 0;
922 953 : struct BatchJob jobs[GNUNET_NZL (bs)];
923 953 : bool failure = false;
924 :
925 953 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
926 : "Handling batch sign request of size %u\n",
927 : (unsigned int) bs);
928 953 : if (bs > TALER_MAX_COINS)
929 : {
930 0 : GNUNET_break_op (0);
931 0 : return GNUNET_SYSERR;
932 : }
933 2100 : while ( (bs > 0) &&
934 : (size >= sizeof (struct TALER_CRYPTO_CsSignRequestMessage)) )
935 : {
936 1147 : const struct TALER_CRYPTO_CsSignRequestMessage *sr = off;
937 1147 : uint16_t s = ntohs (sr->header.size);
938 :
939 1147 : if (s > size)
940 : {
941 0 : failure = true;
942 0 : bs = idx;
943 0 : break;
944 : }
945 1147 : start_sign_job (sr,
946 1147 : &jobs[idx++]);
947 1147 : off += s;
948 1147 : size -= s;
949 : }
950 953 : GNUNET_break_op (0 == size);
951 953 : bs = GNUNET_MIN (bs,
952 : idx);
953 2098 : for (unsigned int i = 0; i<bs; i++)
954 1146 : finish_job (client,
955 : &jobs[i]);
956 952 : 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 952 : 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 149 : handle_batch_derive_request (struct TES_Client *client,
982 : const struct TALER_CRYPTO_BatchDeriveRequest *bdr)
983 149 : {
984 149 : uint32_t bs = ntohl (bdr->batch_size);
985 149 : uint16_t size = ntohs (bdr->header.size) - sizeof (*bdr);
986 149 : const void *off = (const void *) &bdr[1];
987 149 : unsigned int idx = 0;
988 149 : struct BatchJob jobs[bs];
989 149 : bool failure = false;
990 :
991 149 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
992 : "Handling batch derivation request of size %u\n",
993 : (unsigned int) bs);
994 149 : if (bs > TALER_MAX_COINS)
995 : {
996 0 : GNUNET_break_op (0);
997 0 : return GNUNET_SYSERR;
998 : }
999 1119 : while ( (bs > 0) &&
1000 : (size >= sizeof (struct TALER_CRYPTO_CsRDeriveRequest)) )
1001 : {
1002 970 : const struct TALER_CRYPTO_CsRDeriveRequest *rdr = off;
1003 970 : uint16_t s = ntohs (rdr->header.size);
1004 :
1005 970 : if ( (s > size) ||
1006 : (s != sizeof (*rdr)) )
1007 : {
1008 0 : failure = true;
1009 0 : bs = idx;
1010 0 : break;
1011 : }
1012 970 : start_derive_job (rdr,
1013 970 : &jobs[idx++]);
1014 970 : off += s;
1015 970 : size -= s;
1016 : }
1017 149 : GNUNET_break_op (0 == size);
1018 149 : bs = GNUNET_MIN (bs,
1019 : idx);
1020 1119 : for (unsigned int i = 0; i<bs; i++)
1021 970 : finish_job (client,
1022 : &jobs[i]);
1023 149 : if (failure)
1024 : {
1025 0 : GNUNET_break (0);
1026 0 : return fail_derive (client,
1027 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE);
1028 : }
1029 149 : 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 0 : handle_r_derive_request (struct TES_Client *client,
1263 : const struct TALER_CRYPTO_CsRDeriveRequest *rdr)
1264 : {
1265 : struct GNUNET_CRYPTO_CSPublicRPairP r_pub;
1266 0 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1267 : enum TALER_ErrorCode ec;
1268 : enum GNUNET_GenericReturnValue ret;
1269 :
1270 0 : ec = do_derive (&rdr->h_cs,
1271 : &rdr->nonce,
1272 0 : (0 != ntohl (rdr->for_melt)),
1273 : &r_pub);
1274 0 : if (TALER_EC_NONE != ec)
1275 : {
1276 0 : return fail_derive (client,
1277 : ec);
1278 : }
1279 :
1280 0 : ret = send_derivation (client,
1281 : &r_pub);
1282 0 : 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 0 : 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 1110 : cs_work_dispatch (struct TES_Client *client,
1300 : const struct GNUNET_MessageHeader *hdr)
1301 : {
1302 1110 : uint16_t msize = ntohs (hdr->size);
1303 :
1304 1110 : switch (ntohs (hdr->type))
1305 : {
1306 0 : case TALER_HELPER_CS_MT_REQ_SIGN:
1307 0 : if (msize < sizeof (struct TALER_CRYPTO_CsSignRequestMessage))
1308 : {
1309 0 : GNUNET_break_op (0);
1310 0 : return GNUNET_SYSERR;
1311 : }
1312 0 : 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 958 : case TALER_HELPER_CS_MT_REQ_BATCH_SIGN:
1325 958 : if (msize <= sizeof (struct TALER_CRYPTO_BatchSignRequest))
1326 : {
1327 0 : GNUNET_break_op (0);
1328 0 : return GNUNET_SYSERR;
1329 : }
1330 958 : return handle_batch_sign_request (
1331 : client,
1332 : (const struct TALER_CRYPTO_BatchSignRequest *) hdr);
1333 149 : case TALER_HELPER_CS_MT_REQ_BATCH_RDERIVE:
1334 149 : if (msize <= sizeof (struct TALER_CRYPTO_BatchDeriveRequest))
1335 : {
1336 0 : GNUNET_break_op (0);
1337 0 : return GNUNET_SYSERR;
1338 : }
1339 149 : return handle_batch_derive_request (
1340 : client,
1341 : (const struct TALER_CRYPTO_BatchDeriveRequest *) hdr);
1342 0 : case TALER_HELPER_CS_MT_REQ_RDERIVE:
1343 0 : if (msize != sizeof (struct TALER_CRYPTO_CsRDeriveRequest))
1344 : {
1345 0 : GNUNET_break_op (0);
1346 0 : return GNUNET_SYSERR;
1347 : }
1348 0 : 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 339 : NULL != dk;
1381 278 : dk = dk->next)
1382 : {
1383 278 : 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 339 : NULL != dk;
1394 278 : dk = dk->next)
1395 : {
1396 278 : GNUNET_memcpy (&buf[obs],
1397 : dk->an,
1398 : ntohs (dk->an->header.size));
1399 278 : 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 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1754 0 : globals->global_ret = EXIT_FAILURE;
1755 0 : GNUNET_SCHEDULER_shutdown ();
1756 0 : return;
1757 : }
1758 54 : anchor_start = anchor_end;
1759 : }
1760 108 : remove_expired_denomination_keys (denom);
1761 : }
1762 28 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1763 28 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1764 : "Updating denominations finished ...\n");
1765 : }
1766 :
1767 :
1768 : /**
1769 : * Task run periodically to expire keys and/or generate fresh ones.
1770 : *
1771 : * @param cls the `struct TALER_SECMOD_Options *`
1772 : */
1773 : static void
1774 10 : update_denominations (void *cls)
1775 : {
1776 10 : struct TALER_SECMOD_Options *opt = cls;
1777 : struct GNUNET_TIME_Absolute at;
1778 10 : bool wake = false;
1779 :
1780 : (void) cls;
1781 10 : keygen_task = NULL;
1782 10 : opt->global_now = GNUNET_TIME_timestamp_get ();
1783 10 : create_missing_keys (opt,
1784 : &wake);
1785 10 : if (wake)
1786 2 : TES_wake_clients ();
1787 10 : at = action_time ();
1788 10 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1789 : "Next key generation due at %s\n",
1790 : GNUNET_TIME_absolute2s (at));
1791 10 : keygen_task = GNUNET_SCHEDULER_add_at (at,
1792 : &update_denominations,
1793 : opt);
1794 10 : }
1795 :
1796 :
1797 : /**
1798 : * Parse private key of denomination @a denom in @a buf.
1799 : *
1800 : * @param[out] denom denomination of the key
1801 : * @param filename name of the file we are parsing, for logging
1802 : * @param priv key material
1803 : */
1804 : static void
1805 165 : parse_key (struct Denomination *denom,
1806 : const char *filename,
1807 : const struct GNUNET_CRYPTO_CsPrivateKey *priv)
1808 : {
1809 : char *anchor_s;
1810 : char dummy;
1811 : unsigned long long anchor_start_ll;
1812 : unsigned long long anchor_end_ll;
1813 : struct GNUNET_TIME_Timestamp anchor_start;
1814 : struct GNUNET_TIME_Timestamp anchor_end;
1815 165 : char *nf = NULL;
1816 :
1817 165 : anchor_s = strrchr (filename,
1818 : '/');
1819 165 : if (NULL == anchor_s)
1820 : {
1821 : /* File in a directory without '/' in the name, this makes no sense. */
1822 0 : GNUNET_break (0);
1823 0 : return;
1824 : }
1825 165 : anchor_s++;
1826 165 : if (2 != sscanf (anchor_s,
1827 : "%llu-%llu%c",
1828 : &anchor_start_ll,
1829 : &anchor_end_ll,
1830 : &dummy))
1831 : {
1832 : /* try legacy mode */
1833 0 : if (1 != sscanf (anchor_s,
1834 : "%llu%c",
1835 : &anchor_start_ll,
1836 : &dummy))
1837 : {
1838 : /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */
1839 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1840 : "Filename `%s' invalid for key file, skipping\n",
1841 : anchor_s);
1842 0 : return;
1843 : }
1844 : anchor_start.abs_time.abs_value_us
1845 0 : = anchor_start_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
1846 0 : if (anchor_start_ll != anchor_start.abs_time.abs_value_us
1847 0 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
1848 : {
1849 : /* Integer overflow. Bad, invalid filename. */
1850 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1851 : "Integer overflow. Filename `%s' invalid for key file, skipping\n",
1852 : anchor_s);
1853 0 : return;
1854 : }
1855 : anchor_end
1856 0 : = GNUNET_TIME_absolute_to_timestamp (
1857 : GNUNET_TIME_absolute_add (anchor_start.abs_time,
1858 : denom->duration_withdraw));
1859 0 : GNUNET_asprintf (
1860 : &nf,
1861 : "%s/%s/%llu-%llu",
1862 : keydir,
1863 : denom->section,
1864 : anchor_start_ll,
1865 0 : (unsigned long long) (anchor_end.abs_time.abs_value_us
1866 0 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us));
1867 : /* Try to fix the legacy filename */
1868 0 : if (0 !=
1869 0 : rename (filename,
1870 : nf))
1871 : {
1872 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1873 : "rename",
1874 : filename);
1875 0 : GNUNET_free (nf);
1876 : }
1877 : }
1878 : else
1879 : {
1880 : anchor_start.abs_time.abs_value_us
1881 165 : = anchor_start_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
1882 : anchor_end.abs_time.abs_value_us
1883 165 : = anchor_end_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
1884 330 : if ( (anchor_start_ll != anchor_start.abs_time.abs_value_us
1885 165 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us) ||
1886 330 : (anchor_end_ll != anchor_end.abs_time.abs_value_us
1887 165 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us) )
1888 : {
1889 : /* Integer overflow. Bad, invalid filename. */
1890 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1891 : "Integer overflow. Filename `%s' invalid for key file, skipping\n",
1892 : anchor_s);
1893 0 : return;
1894 : }
1895 : }
1896 :
1897 : {
1898 : struct DenominationKey *dk;
1899 : struct DenominationKey *before;
1900 :
1901 165 : dk = GNUNET_new (struct DenominationKey);
1902 165 : dk->denom_priv = *priv;
1903 165 : dk->denom = denom;
1904 165 : dk->anchor_start = anchor_start;
1905 165 : dk->anchor_end = anchor_end;
1906 165 : dk->filename = (NULL == nf) ? GNUNET_strdup (filename) : nf;
1907 165 : GNUNET_CRYPTO_cs_private_key_get_public (priv,
1908 : &dk->denom_pub);
1909 165 : GNUNET_CRYPTO_hash (&dk->denom_pub,
1910 : sizeof (dk->denom_pub),
1911 : &dk->h_cs.hash);
1912 165 : generate_response (dk);
1913 165 : if (GNUNET_OK !=
1914 165 : GNUNET_CONTAINER_multihashmap_put (
1915 : keys,
1916 165 : &dk->h_cs.hash,
1917 : dk,
1918 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1919 : {
1920 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1921 : "Duplicate private key %s detected in file `%s'. Skipping.\n",
1922 : GNUNET_h2s (&dk->h_cs.hash),
1923 : filename);
1924 0 : GNUNET_free (dk->an);
1925 0 : GNUNET_free (dk);
1926 0 : return;
1927 : }
1928 165 : before = NULL;
1929 165 : for (struct DenominationKey *pos = denom->keys_head;
1930 165 : NULL != pos;
1931 0 : pos = pos->next)
1932 : {
1933 124 : if (GNUNET_TIME_timestamp_cmp (pos->anchor_start,
1934 : >,
1935 : anchor_start))
1936 124 : break;
1937 0 : before = pos;
1938 : }
1939 165 : GNUNET_CONTAINER_DLL_insert_after (denom->keys_head,
1940 : denom->keys_tail,
1941 : before,
1942 : dk);
1943 165 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1944 : "Imported key %s from `%s'\n",
1945 : GNUNET_h2s (&dk->h_cs.hash),
1946 : filename);
1947 : }
1948 : }
1949 :
1950 :
1951 : /**
1952 : * Import a private key from @a filename for the denomination
1953 : * given in @a cls.
1954 : *
1955 : * @param[in,out] cls a `struct Denomiantion`
1956 : * @param filename name of a file in the directory
1957 : * @return #GNUNET_OK (always, continue to iterate)
1958 : */
1959 : static enum GNUNET_GenericReturnValue
1960 165 : import_key (void *cls,
1961 : const char *filename)
1962 : {
1963 165 : struct Denomination *denom = cls;
1964 : struct GNUNET_DISK_FileHandle *fh;
1965 : struct GNUNET_DISK_MapHandle *map;
1966 : void *ptr;
1967 : int fd;
1968 : struct stat sbuf;
1969 :
1970 : {
1971 : struct stat lsbuf;
1972 :
1973 165 : if (0 != lstat (filename,
1974 : &lsbuf))
1975 : {
1976 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1977 : "lstat",
1978 : filename);
1979 0 : return GNUNET_OK;
1980 : }
1981 165 : if (! S_ISREG (lsbuf.st_mode))
1982 : {
1983 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1984 : "File `%s' is not a regular file, which is not allowed for private keys!\n",
1985 : filename);
1986 0 : return GNUNET_OK;
1987 : }
1988 : }
1989 :
1990 165 : fd = open (filename,
1991 : O_RDONLY | O_CLOEXEC);
1992 165 : if (-1 == fd)
1993 : {
1994 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1995 : "open",
1996 : filename);
1997 0 : return GNUNET_OK;
1998 : }
1999 165 : if (0 != fstat (fd,
2000 : &sbuf))
2001 : {
2002 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2003 : "stat",
2004 : filename);
2005 0 : GNUNET_break (0 == close (fd));
2006 0 : return GNUNET_OK;
2007 : }
2008 165 : if (! S_ISREG (sbuf.st_mode))
2009 : {
2010 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2011 : "File `%s' is not a regular file, which is not allowed for private keys!\n",
2012 : filename);
2013 0 : GNUNET_break (0 == close (fd));
2014 0 : return GNUNET_OK;
2015 : }
2016 165 : if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
2017 : {
2018 : /* permission are NOT tight, try to patch them up! */
2019 0 : if (0 !=
2020 0 : fchmod (fd,
2021 : S_IRUSR))
2022 : {
2023 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2024 : "fchmod",
2025 : filename);
2026 : /* refuse to use key if file has wrong permissions */
2027 0 : GNUNET_break (0 == close (fd));
2028 0 : return GNUNET_OK;
2029 : }
2030 : }
2031 165 : fh = GNUNET_DISK_get_handle_from_int_fd (fd);
2032 165 : if (NULL == fh)
2033 : {
2034 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2035 : "open",
2036 : filename);
2037 0 : GNUNET_break (0 == close (fd));
2038 0 : return GNUNET_OK;
2039 : }
2040 165 : if (sbuf.st_size != sizeof(struct GNUNET_CRYPTO_CsPrivateKey))
2041 : {
2042 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2043 : "File `%s' too big to be a private key\n",
2044 : filename);
2045 0 : GNUNET_DISK_file_close (fh);
2046 0 : return GNUNET_OK;
2047 : }
2048 165 : ptr = GNUNET_DISK_file_map (fh,
2049 : &map,
2050 : GNUNET_DISK_MAP_TYPE_READ,
2051 165 : (size_t) sbuf.st_size);
2052 165 : if (NULL == ptr)
2053 : {
2054 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2055 : "mmap",
2056 : filename);
2057 0 : GNUNET_DISK_file_close (fh);
2058 0 : return GNUNET_OK;
2059 : }
2060 165 : parse_key (denom,
2061 : filename,
2062 : (const struct GNUNET_CRYPTO_CsPrivateKey *) ptr);
2063 165 : GNUNET_DISK_file_unmap (map);
2064 165 : GNUNET_DISK_file_close (fh);
2065 165 : return GNUNET_OK;
2066 : }
2067 :
2068 :
2069 : /**
2070 : * Parse configuration for denomination type parameters. Also determines
2071 : * our anchor by looking at the existing denominations of the same type.
2072 : *
2073 : * @param cfg configuration to use
2074 : * @param ct section in the configuration file giving the denomination type parameters
2075 : * @param[out] denom set to the denomination parameters from the configuration
2076 : * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid
2077 : */
2078 : static enum GNUNET_GenericReturnValue
2079 53 : parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
2080 : const char *ct,
2081 : struct Denomination *denom)
2082 : {
2083 : char *secname;
2084 :
2085 53 : GNUNET_asprintf (&secname,
2086 : "%s-secmod-cs",
2087 53 : globals->section);
2088 53 : if (GNUNET_OK !=
2089 53 : GNUNET_CONFIGURATION_get_value_time (cfg,
2090 : ct,
2091 : "DURATION_WITHDRAW",
2092 : &denom->duration_withdraw))
2093 : {
2094 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2095 : ct,
2096 : "DURATION_WITHDRAW");
2097 0 : GNUNET_free (secname);
2098 0 : return GNUNET_SYSERR;
2099 : }
2100 53 : if (GNUNET_TIME_relative_cmp (denom->duration_withdraw,
2101 : <,
2102 : GNUNET_TIME_UNIT_SECONDS))
2103 : {
2104 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
2105 : ct,
2106 : "DURATION_WITHDRAW",
2107 : "less than one second is not supported");
2108 0 : GNUNET_free (secname);
2109 0 : return GNUNET_SYSERR;
2110 : }
2111 53 : if (GNUNET_TIME_relative_cmp (overlap_duration,
2112 : >=,
2113 : denom->duration_withdraw))
2114 : {
2115 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
2116 : secname,
2117 : "OVERLAP_DURATION",
2118 : "Value given must be smaller than value for DURATION_WITHDRAW!");
2119 0 : GNUNET_free (secname);
2120 0 : return GNUNET_SYSERR;
2121 : }
2122 53 : GNUNET_free (secname);
2123 53 : denom->section = GNUNET_strdup (ct);
2124 53 : return GNUNET_OK;
2125 : }
2126 :
2127 :
2128 : /**
2129 : * Closure for #load_denominations.
2130 : */
2131 : struct LoadContext
2132 : {
2133 :
2134 : /**
2135 : * Configuration to use.
2136 : */
2137 : const struct GNUNET_CONFIGURATION_Handle *cfg;
2138 :
2139 : /**
2140 : * Current time to use.
2141 : */
2142 : struct GNUNET_TIME_Timestamp t;
2143 :
2144 : /**
2145 : * Status, to be set to #GNUNET_SYSERR on failure
2146 : */
2147 : enum GNUNET_GenericReturnValue ret;
2148 : };
2149 :
2150 :
2151 : /**
2152 : * Generate new denomination signing keys for the denomination type of the given @a
2153 : * denomination_alias.
2154 : *
2155 : * @param cls a `struct LoadContext`, with 'ret' to be set to #GNUNET_SYSERR on failure
2156 : * @param denomination_alias name of the denomination's section in the configuration
2157 : */
2158 : static void
2159 894 : load_denominations (void *cls,
2160 : const char *denomination_alias)
2161 : {
2162 894 : struct LoadContext *ctx = cls;
2163 : struct Denomination *denom;
2164 : char *cipher;
2165 :
2166 894 : if ( (0 != strncasecmp (denomination_alias,
2167 : "coin_",
2168 777 : strlen ("coin_"))) &&
2169 777 : (0 != strncasecmp (denomination_alias,
2170 : "coin-",
2171 : strlen ("coin-"))) )
2172 841 : return; /* not a denomination type definition */
2173 117 : if (GNUNET_OK !=
2174 117 : GNUNET_CONFIGURATION_get_value_string (ctx->cfg,
2175 : denomination_alias,
2176 : "CIPHER",
2177 : &cipher))
2178 : {
2179 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2180 : denomination_alias,
2181 : "CIPHER");
2182 0 : return;
2183 : }
2184 117 : if (0 != strcmp (cipher, "CS"))
2185 : {
2186 64 : GNUNET_free (cipher);
2187 64 : return; /* Ignore denominations of other types than CS*/
2188 : }
2189 53 : GNUNET_free (cipher);
2190 :
2191 53 : denom = GNUNET_new (struct Denomination);
2192 53 : if (GNUNET_OK !=
2193 53 : parse_denomination_cfg (ctx->cfg,
2194 : denomination_alias,
2195 : denom))
2196 : {
2197 0 : ctx->ret = GNUNET_SYSERR;
2198 0 : GNUNET_free (denom);
2199 0 : return;
2200 : }
2201 53 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2202 : "Loading keys for denomination %s\n",
2203 : denom->section);
2204 : {
2205 : char *dname;
2206 :
2207 53 : GNUNET_asprintf (&dname,
2208 : "%s/%s",
2209 : keydir,
2210 : denom->section);
2211 53 : GNUNET_break (GNUNET_OK ==
2212 : GNUNET_DISK_directory_create (dname));
2213 53 : GNUNET_DISK_directory_scan (dname,
2214 : &import_key,
2215 : denom);
2216 53 : GNUNET_free (dname);
2217 : }
2218 53 : GNUNET_CONTAINER_DLL_insert (denom_head,
2219 : denom_tail,
2220 : denom);
2221 : }
2222 :
2223 :
2224 : /**
2225 : * Load the various duration values from @a cfg
2226 : *
2227 : * @param cfg configuration to use
2228 : * @return #GNUNET_OK on success
2229 : */
2230 : static enum GNUNET_GenericReturnValue
2231 18 : load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
2232 : {
2233 : char *secname;
2234 :
2235 18 : GNUNET_asprintf (&secname,
2236 : "%s-secmod-cs",
2237 18 : globals->section);
2238 18 : if (GNUNET_OK !=
2239 18 : GNUNET_CONFIGURATION_get_value_time (cfg,
2240 : secname,
2241 : "OVERLAP_DURATION",
2242 : &overlap_duration))
2243 : {
2244 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2245 : secname,
2246 : "OVERLAP_DURATION");
2247 0 : GNUNET_free (secname);
2248 0 : return GNUNET_SYSERR;
2249 : }
2250 18 : if (GNUNET_OK !=
2251 18 : GNUNET_CONFIGURATION_get_value_time (cfg,
2252 : secname,
2253 : "LOOKAHEAD_SIGN",
2254 : &lookahead_sign))
2255 : {
2256 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2257 : secname,
2258 : "LOOKAHEAD_SIGN");
2259 0 : GNUNET_free (secname);
2260 0 : return GNUNET_SYSERR;
2261 : }
2262 18 : GNUNET_free (secname);
2263 18 : return GNUNET_OK;
2264 : }
2265 :
2266 :
2267 : /**
2268 : * Function run on shutdown. Stops the various jobs (nicely).
2269 : *
2270 : * @param cls a `struct TALER_SECMOD_Options`
2271 : */
2272 : static void
2273 18 : do_shutdown (void *cls)
2274 : {
2275 : (void) cls;
2276 18 : TES_listen_stop ();
2277 18 : if (NULL != keygen_task)
2278 : {
2279 8 : GNUNET_SCHEDULER_cancel (keygen_task);
2280 8 : keygen_task = NULL;
2281 : }
2282 18 : stop_workers ();
2283 18 : sem_done (&worker_sem);
2284 18 : }
2285 :
2286 :
2287 : void
2288 18 : TALER_SECMOD_cs_run (void *cls,
2289 : char *const *args,
2290 : const char *cfgfile,
2291 : const struct GNUNET_CONFIGURATION_Handle *cfg)
2292 : {
2293 : static struct TES_Callbacks cb = {
2294 : .dispatch = &cs_work_dispatch,
2295 : .updater = &cs_update_client_keys,
2296 : .init = &cs_client_init
2297 : };
2298 18 : struct TALER_SECMOD_Options *opt = cls;
2299 : char *secname;
2300 :
2301 : (void) args;
2302 : (void) cfgfile;
2303 18 : globals = opt;
2304 18 : if (GNUNET_TIME_timestamp_cmp (opt->global_now,
2305 : !=,
2306 : opt->global_now_tmp))
2307 : {
2308 : /* The user gave "--now", use it! */
2309 0 : opt->global_now = opt->global_now_tmp;
2310 : }
2311 : else
2312 : {
2313 : /* get current time again, we may be timetraveling! */
2314 18 : opt->global_now = GNUNET_TIME_timestamp_get ();
2315 : }
2316 18 : GNUNET_asprintf (&secname,
2317 : "%s-secmod-cs",
2318 : opt->section);
2319 18 : if (GNUNET_OK !=
2320 18 : GNUNET_CONFIGURATION_get_value_filename (cfg,
2321 : secname,
2322 : "KEY_DIR",
2323 : &keydir))
2324 : {
2325 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2326 : secname,
2327 : "KEY_DIR");
2328 0 : GNUNET_free (secname);
2329 0 : opt->global_ret = EXIT_NOTCONFIGURED;
2330 10 : return;
2331 : }
2332 18 : if (GNUNET_OK !=
2333 18 : load_durations (cfg))
2334 : {
2335 0 : opt->global_ret = EXIT_NOTCONFIGURED;
2336 0 : GNUNET_free (secname);
2337 0 : return;
2338 : }
2339 18 : opt->global_ret = TES_listen_start (cfg,
2340 : secname,
2341 : &cb);
2342 18 : GNUNET_free (secname);
2343 18 : if (0 != opt->global_ret)
2344 0 : return;
2345 18 : sem_init (&worker_sem,
2346 : 0);
2347 18 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
2348 : opt);
2349 18 : if (0 == opt->max_workers)
2350 : {
2351 : long lret;
2352 :
2353 0 : lret = sysconf (_SC_NPROCESSORS_CONF);
2354 0 : if (lret <= 0)
2355 0 : lret = 1;
2356 0 : opt->max_workers = (unsigned int) lret;
2357 : }
2358 306 : for (unsigned int i = 0; i<opt->max_workers; i++)
2359 288 : if (GNUNET_OK !=
2360 288 : start_worker ())
2361 : {
2362 0 : GNUNET_SCHEDULER_shutdown ();
2363 0 : return;
2364 : }
2365 : /* Load denominations */
2366 18 : keys = GNUNET_CONTAINER_multihashmap_create (65536,
2367 : true);
2368 : {
2369 18 : struct LoadContext lc = {
2370 : .cfg = cfg,
2371 : .ret = GNUNET_OK,
2372 : .t = opt->global_now
2373 : };
2374 18 : bool wake = true;
2375 :
2376 18 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
2377 18 : GNUNET_CONFIGURATION_iterate_sections (cfg,
2378 : &load_denominations,
2379 : &lc);
2380 18 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
2381 18 : if (GNUNET_OK != lc.ret)
2382 : {
2383 0 : opt->global_ret = EXIT_FAILURE;
2384 0 : GNUNET_SCHEDULER_shutdown ();
2385 0 : return;
2386 : }
2387 18 : create_missing_keys (opt,
2388 : &wake);
2389 : }
2390 18 : if (NULL == denom_head)
2391 : {
2392 10 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2393 : "No CS denominations configured. Make sure section names start with `%s' if you are using CS!\n",
2394 : opt->section);
2395 10 : TES_wake_clients ();
2396 10 : return;
2397 : }
2398 : /* start job to keep keys up-to-date; MUST be run before the #listen_task,
2399 : hence with priority. */
2400 8 : keygen_task = GNUNET_SCHEDULER_add_with_priority (
2401 : GNUNET_SCHEDULER_PRIORITY_URGENT,
2402 : &update_denominations,
2403 : opt);
2404 : }
|