Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2021 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/taler-exchange-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_util.h"
36 : #include "taler-exchange-secmod-cs.h"
37 : #include <gcrypt.h>
38 : #include <pthread.h>
39 : #include <sys/eventfd.h>
40 : #include "taler_error_codes.h"
41 : #include "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;
102 :
103 : /**
104 : * Generation when this key was created or revoked.
105 : */
106 : uint64_t key_gen;
107 :
108 : /**
109 : * Reference counter. Counts the number of threads that are
110 : * using this key at this time.
111 : */
112 : unsigned int rc;
113 :
114 : /**
115 : * Flag set to true if this key has been purged and the memory
116 : * must be freed as soon as @e rc hits zero.
117 : */
118 : bool purge;
119 :
120 : };
121 :
122 :
123 : struct Denomination
124 : {
125 :
126 : /**
127 : * Kept in a DLL. Sorted by #denomination_action_time().
128 : */
129 : struct Denomination *next;
130 :
131 : /**
132 : * Kept in a DLL. Sorted by #denomination_action_time().
133 : */
134 : struct Denomination *prev;
135 :
136 : /**
137 : * Head of DLL of actual keys of this denomination.
138 : */
139 : struct DenominationKey *keys_head;
140 :
141 : /**
142 : * Tail of DLL of actual keys of this denomination.
143 : */
144 : struct DenominationKey *keys_tail;
145 :
146 : /**
147 : * How long can coins be withdrawn (generated)? Should be small
148 : * enough to limit how many coins will be signed into existence with
149 : * the same key, but large enough to still provide a reasonable
150 : * anonymity set.
151 : */
152 : struct GNUNET_TIME_Relative duration_withdraw;
153 :
154 : /**
155 : * What is the configuration section of this denomination type? Also used
156 : * for the directory name where the denomination keys are stored.
157 : */
158 : char *section;
159 :
160 : };
161 :
162 :
163 : /**
164 : * Return value from main().
165 : */
166 : static int global_ret;
167 :
168 : /**
169 : * Time when the key update is executed.
170 : * Either the actual current time, or a pretended time.
171 : */
172 : static struct GNUNET_TIME_Timestamp now;
173 :
174 : /**
175 : * The time for the key update, as passed by the user
176 : * on the command line.
177 : */
178 : static struct GNUNET_TIME_Timestamp now_tmp;
179 :
180 : /**
181 : * Where do we store the keys?
182 : */
183 : static char *keydir;
184 :
185 : /**
186 : * How much should coin creation (@e duration_withdraw) duration overlap
187 : * with the next denomination? Basically, the starting time of two
188 : * denominations is always @e duration_withdraw - #overlap_duration apart.
189 : */
190 : static struct GNUNET_TIME_Relative overlap_duration;
191 :
192 : /**
193 : * How long into the future do we pre-generate keys?
194 : */
195 : static struct GNUNET_TIME_Relative lookahead_sign;
196 :
197 : /**
198 : * All of our denominations, in a DLL. Sorted?
199 : */
200 : static struct Denomination *denom_head;
201 :
202 : /**
203 : * All of our denominations, in a DLL. Sorted?
204 : */
205 : static struct Denomination *denom_tail;
206 :
207 : /**
208 : * Map of hashes of public (CS) keys to `struct DenominationKey *`
209 : * with the respective private keys.
210 : */
211 : static struct GNUNET_CONTAINER_MultiHashMap *keys;
212 :
213 : /**
214 : * Task run to generate new keys.
215 : */
216 : static struct GNUNET_SCHEDULER_Task *keygen_task;
217 :
218 : /**
219 : * Lock for the keys queue.
220 : */
221 : static pthread_mutex_t keys_lock;
222 :
223 : /**
224 : * Current key generation.
225 : */
226 : static uint64_t key_gen;
227 :
228 :
229 : /**
230 : * Generate the announcement message for @a dk.
231 : *
232 : * @param[in,out] dk denomination key to generate the announcement for
233 : */
234 : static void
235 9 : generate_response (struct DenominationKey *dk)
236 : {
237 9 : struct Denomination *denom = dk->denom;
238 9 : size_t nlen = strlen (denom->section) + 1;
239 : struct TALER_CRYPTO_CsKeyAvailableNotification *an;
240 : void *p;
241 : size_t tlen;
242 :
243 : GNUNET_assert (sizeof(dk->denom_pub) < UINT16_MAX);
244 9 : GNUNET_assert (nlen < UINT16_MAX);
245 9 : tlen = nlen + sizeof (*an);
246 9 : GNUNET_assert (tlen < UINT16_MAX);
247 9 : an = GNUNET_malloc (tlen);
248 9 : an->header.size = htons ((uint16_t) tlen);
249 9 : an->header.type = htons (TALER_HELPER_CS_MT_AVAIL);
250 9 : an->section_name_len = htons ((uint16_t) nlen);
251 9 : an->anchor_time = GNUNET_TIME_timestamp_hton (dk->anchor);
252 9 : an->duration_withdraw = GNUNET_TIME_relative_hton (denom->duration_withdraw);
253 9 : an->denom_pub = dk->denom_pub;
254 9 : TALER_exchange_secmod_cs_sign (&dk->h_cs,
255 9 : denom->section,
256 : dk->anchor,
257 : denom->duration_withdraw,
258 : &TES_smpriv,
259 : &an->secm_sig);
260 9 : an->secm_pub = TES_smpub;
261 9 : p = (void *) &an[1];
262 9 : memcpy (p,
263 9 : denom->section,
264 : nlen);
265 9 : dk->an = an;
266 9 : }
267 :
268 :
269 : /**
270 : * Handle @a client request @a sr to create signature. Create the
271 : * signature using the respective key and return the result to
272 : * the client.
273 : *
274 : * @param client the client making the request
275 : * @param sr the request details
276 : * @return #GNUNET_OK on success
277 : */
278 : static enum GNUNET_GenericReturnValue
279 902 : handle_sign_request (struct TES_Client *client,
280 : const struct TALER_CRYPTO_CsSignRequest *sr)
281 : {
282 : struct DenominationKey *dk;
283 : struct GNUNET_CRYPTO_CsRSecret r[2];
284 : struct TALER_BlindedDenominationCsSignAnswer cs_answer;
285 902 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
286 : bool for_melt;
287 :
288 902 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
289 902 : dk = GNUNET_CONTAINER_multihashmap_get (keys,
290 : &sr->h_cs.hash);
291 902 : if (NULL == dk)
292 : {
293 1 : struct TALER_CRYPTO_SignFailure sf = {
294 1 : .header.size = htons (sizeof (sr)),
295 1 : .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE),
296 1 : .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN)
297 : };
298 :
299 1 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
300 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
301 : "Signing request failed, denomination key %s unknown\n",
302 : GNUNET_h2s (&sr->h_cs.hash));
303 1 : return TES_transmit (client->csock,
304 : &sf.header);
305 : }
306 901 : if (GNUNET_TIME_absolute_is_future (dk->anchor.abs_time))
307 : {
308 : /* it is too early */
309 0 : struct TALER_CRYPTO_SignFailure sf = {
310 0 : .header.size = htons (sizeof (sr)),
311 0 : .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE),
312 0 : .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY)
313 : };
314 :
315 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
316 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
317 : "Signing request failed, denomination key %s is not yet valid\n",
318 : GNUNET_h2s (&sr->h_cs.hash));
319 0 : return TES_transmit (client->csock,
320 : &sf.header);
321 : }
322 901 : for_melt = (0 != ntohl (sr->for_melt));
323 901 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
324 : "Received request to sign over bytes with key %s\n",
325 : GNUNET_h2s (&sr->h_cs.hash));
326 901 : GNUNET_assert (dk->rc < UINT_MAX);
327 901 : dk->rc++;
328 901 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
329 901 : GNUNET_CRYPTO_cs_r_derive (&sr->planchet.nonce.nonce,
330 : for_melt ? "rm" : "rw",
331 901 : &dk->denom_priv,
332 : r);
333 1802 : cs_answer.b = GNUNET_CRYPTO_cs_sign_derive (&dk->denom_priv,
334 : r,
335 901 : sr->planchet.c,
336 : &sr->planchet.nonce.nonce,
337 : &cs_answer.s_scalar);
338 :
339 901 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
340 901 : GNUNET_assert (dk->rc > 0);
341 901 : dk->rc--;
342 901 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
343 : // if (NULL == cs_answer)
344 : // {
345 : // struct TALER_CRYPTO_SignFailure sf = {
346 : // .header.size = htons (sizeof (sf)),
347 : // .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE),
348 : // .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)
349 : // };
350 :
351 : // GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
352 : // "Signing request failed, worker failed to produce signature\n");
353 : // return TES_transmit (client->csock,
354 : // &sf.header);
355 : // }
356 :
357 : {
358 : struct TALER_CRYPTO_SignResponse *sr;
359 : size_t tsize;
360 : enum GNUNET_GenericReturnValue ret;
361 :
362 901 : tsize = sizeof (*sr) + sizeof(cs_answer);
363 901 : GNUNET_assert (tsize < UINT16_MAX);
364 901 : sr = GNUNET_malloc (tsize);
365 901 : sr->header.size = htons (tsize);
366 901 : sr->header.type = htons (TALER_HELPER_CS_MT_RES_SIGNATURE);
367 901 : sr->cs_answer = cs_answer;
368 901 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
369 : "Sending CS signature after %s\n",
370 : GNUNET_TIME_relative2s (
371 : GNUNET_TIME_absolute_get_duration (now),
372 : GNUNET_YES));
373 901 : ret = TES_transmit (client->csock,
374 901 : &sr->header);
375 901 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
376 : "Sent CS signature after %s\n",
377 : GNUNET_TIME_relative2s (
378 : GNUNET_TIME_absolute_get_duration (now),
379 : GNUNET_YES));
380 900 : GNUNET_free (sr);
381 900 : return ret;
382 : }
383 : }
384 :
385 :
386 : /**
387 : * Initialize key material for denomination key @a dk (also on disk).
388 : *
389 : * @param[in,out] dk denomination key to compute key material for
390 : * @param position where in the DLL will the @a dk go
391 : * @return #GNUNET_OK on success
392 : */
393 : static enum GNUNET_GenericReturnValue
394 9 : setup_key (struct DenominationKey *dk,
395 : struct DenominationKey *position)
396 : {
397 9 : struct Denomination *denom = dk->denom;
398 : struct GNUNET_CRYPTO_CsPrivateKey priv;
399 : struct GNUNET_CRYPTO_CsPublicKey pub;
400 :
401 9 : GNUNET_CRYPTO_cs_private_key_generate (&priv);
402 9 : GNUNET_CRYPTO_cs_private_key_get_public (&priv,
403 : &pub);
404 9 : TALER_cs_pub_hash (&pub,
405 : &dk->h_cs);
406 9 : GNUNET_asprintf (&dk->filename,
407 : "%s/%s/%llu",
408 : keydir,
409 : denom->section,
410 9 : (unsigned long long) (dk->anchor.abs_time.abs_value_us
411 9 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us));
412 9 : if (GNUNET_OK !=
413 9 : GNUNET_DISK_fn_write (dk->filename,
414 : &priv,
415 : sizeof(priv),
416 : GNUNET_DISK_PERM_USER_READ))
417 : {
418 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
419 : "write",
420 : dk->filename);
421 0 : return GNUNET_SYSERR;
422 : }
423 9 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
424 : "Setup fresh private key %s at %s in `%s' (generation #%llu)\n",
425 : GNUNET_h2s (&dk->h_cs.hash),
426 : GNUNET_TIME_timestamp2s (dk->anchor),
427 : dk->filename,
428 : (unsigned long long) key_gen);
429 9 : dk->denom_priv = priv;
430 9 : dk->denom_pub = pub;
431 9 : dk->key_gen = key_gen;
432 9 : generate_response (dk);
433 9 : if (GNUNET_OK !=
434 9 : GNUNET_CONTAINER_multihashmap_put (
435 : keys,
436 9 : &dk->h_cs.hash,
437 : dk,
438 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
439 : {
440 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
441 : "Duplicate private key created! Terminating.\n");
442 0 : GNUNET_free (dk->filename);
443 0 : GNUNET_free (dk->an);
444 0 : GNUNET_free (dk);
445 0 : return GNUNET_SYSERR;
446 : }
447 9 : GNUNET_CONTAINER_DLL_insert_after (denom->keys_head,
448 : denom->keys_tail,
449 : position,
450 : dk);
451 9 : return GNUNET_OK;
452 : }
453 :
454 :
455 : /**
456 : * The withdraw period of a key @a dk has expired. Purge it.
457 : *
458 : * @param[in] dk expired denomination key to purge
459 : */
460 : static void
461 3 : purge_key (struct DenominationKey *dk)
462 : {
463 3 : if (dk->purge)
464 0 : return;
465 3 : if (0 != unlink (dk->filename))
466 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
467 : "unlink",
468 : dk->filename);
469 3 : GNUNET_free (dk->filename);
470 3 : dk->purge = true;
471 3 : dk->key_gen = key_gen;
472 : }
473 :
474 :
475 : /**
476 : * A @a client informs us that a key has been revoked.
477 : * Check if the key is still in use, and if so replace (!)
478 : * it with a fresh key.
479 : *
480 : * @param client the client making the request
481 : * @param rr the revocation request
482 : */
483 : static enum GNUNET_GenericReturnValue
484 3 : handle_revoke_request (struct TES_Client *client,
485 : const struct TALER_CRYPTO_CsRevokeRequest *rr)
486 : {
487 : struct DenominationKey *dk;
488 : struct DenominationKey *ndk;
489 : struct Denomination *denom;
490 :
491 : (void) client;
492 3 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
493 3 : dk = GNUNET_CONTAINER_multihashmap_get (keys,
494 : &rr->h_cs.hash);
495 3 : if (NULL == dk)
496 : {
497 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
498 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
499 : "Revocation request ignored, denomination key %s unknown\n",
500 : GNUNET_h2s (&rr->h_cs.hash));
501 0 : return GNUNET_OK;
502 : }
503 3 : if (dk->purge)
504 : {
505 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
506 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
507 : "Revocation request ignored, denomination key %s already revoked\n",
508 : GNUNET_h2s (&rr->h_cs.hash));
509 0 : return GNUNET_OK;
510 : }
511 :
512 3 : key_gen++;
513 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
514 : "Revoking key %s, bumping generation to %llu\n",
515 : GNUNET_h2s (&rr->h_cs.hash),
516 : (unsigned long long) key_gen);
517 3 : purge_key (dk);
518 :
519 : /* Setup replacement key */
520 3 : denom = dk->denom;
521 3 : ndk = GNUNET_new (struct DenominationKey);
522 3 : ndk->denom = denom;
523 3 : ndk->anchor = dk->anchor;
524 3 : if (GNUNET_OK !=
525 3 : setup_key (ndk,
526 : dk))
527 : {
528 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
529 0 : GNUNET_break (0);
530 0 : GNUNET_SCHEDULER_shutdown ();
531 0 : global_ret = EXIT_FAILURE;
532 0 : return GNUNET_SYSERR;
533 : }
534 3 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
535 3 : TES_wake_clients ();
536 3 : return GNUNET_OK;
537 : }
538 :
539 :
540 : /**
541 : * Handle @a client request @a rdr to create signature. Create the
542 : * signature using the respective key and return the result to
543 : * the client.
544 : *
545 : * @param client the client making the request
546 : * @param rdr the request details
547 : * @return #GNUNET_OK on success
548 : */
549 : static enum GNUNET_GenericReturnValue
550 22 : handle_r_derive_request (struct TES_Client *client,
551 : const struct TALER_CRYPTO_CsRDeriveRequest *rdr)
552 : {
553 : struct DenominationKey *dk;
554 : struct TALER_DenominationCSPrivateRPairP r_priv;
555 : struct TALER_DenominationCSPublicRPairP r_pub;
556 22 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
557 : bool for_melt;
558 :
559 22 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
560 22 : dk = GNUNET_CONTAINER_multihashmap_get (keys,
561 : &rdr->h_cs.hash);
562 22 : if (NULL == dk)
563 : {
564 1 : struct TALER_CRYPTO_RDeriveFailure rdf = {
565 1 : .header.size = htons (sizeof (rdr)),
566 1 : .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE),
567 1 : .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN)
568 : };
569 :
570 1 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
571 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
572 : "R Derive request failed, denomination key %s unknown\n",
573 : GNUNET_h2s (&rdr->h_cs.hash));
574 1 : return TES_transmit (client->csock,
575 : &rdf.header);
576 : }
577 21 : if (GNUNET_TIME_absolute_is_future (dk->anchor.abs_time))
578 : {
579 : /* it is too early */
580 10 : struct TALER_CRYPTO_RDeriveFailure rdf = {
581 10 : .header.size = htons (sizeof (rdr)),
582 10 : .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE),
583 10 : .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY)
584 : };
585 :
586 10 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
587 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
588 : "R Derive request failed, denomination key %s is not yet valid\n",
589 : GNUNET_h2s (&rdr->h_cs.hash));
590 10 : return TES_transmit (client->csock,
591 : &rdf.header);
592 : }
593 11 : for_melt = (0 != ntohl (rdr->for_melt));
594 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
595 : "Received request to derive R with key %s\n",
596 : GNUNET_h2s (&rdr->h_cs.hash));
597 11 : GNUNET_assert (dk->rc < UINT_MAX);
598 11 : dk->rc++;
599 11 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
600 11 : GNUNET_CRYPTO_cs_r_derive (&rdr->nonce.nonce,
601 : for_melt ? "rm" : "rw",
602 11 : &dk->denom_priv,
603 : r_priv.r);
604 11 : GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[0],
605 : &r_pub.r_pub[0]);
606 11 : GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[1],
607 : &r_pub.r_pub[1]);
608 11 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
609 11 : GNUNET_assert (dk->rc > 0);
610 11 : dk->rc--;
611 11 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
612 :
613 : {
614 11 : struct TALER_CRYPTO_RDeriveResponse rdr = {
615 11 : .header.size = htons (sizeof (struct TALER_CRYPTO_RDeriveResponse)),
616 11 : .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE),
617 : .r_pub = r_pub
618 : };
619 : enum GNUNET_GenericReturnValue ret;
620 :
621 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
622 : "Sending CS Derived R after %s\n",
623 : GNUNET_TIME_relative2s (
624 : GNUNET_TIME_absolute_get_duration (now),
625 : GNUNET_YES));
626 11 : ret = TES_transmit (client->csock,
627 : &rdr.header);
628 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
629 : "Sent CS Derived R after %s\n",
630 : GNUNET_TIME_relative2s (
631 : GNUNET_TIME_absolute_get_duration (now),
632 : GNUNET_YES));
633 11 : return ret;
634 : }
635 : }
636 :
637 :
638 : /**
639 : * Handle @a hdr message received from @a client.
640 : *
641 : * @param client the client that received the message
642 : * @param hdr message that was received
643 : * @return #GNUNET_OK on success
644 : */
645 : static enum GNUNET_GenericReturnValue
646 927 : cs_work_dispatch (struct TES_Client *client,
647 : const struct GNUNET_MessageHeader *hdr)
648 : {
649 927 : uint16_t msize = ntohs (hdr->size);
650 :
651 927 : switch (ntohs (hdr->type))
652 : {
653 902 : case TALER_HELPER_CS_MT_REQ_SIGN:
654 902 : if (msize < sizeof (struct TALER_CRYPTO_CsSignRequest))
655 : {
656 0 : GNUNET_break_op (0);
657 0 : return GNUNET_SYSERR;
658 : }
659 902 : return handle_sign_request (
660 : client,
661 : (const struct TALER_CRYPTO_CsSignRequest *) hdr);
662 3 : case TALER_HELPER_CS_MT_REQ_REVOKE:
663 3 : if (msize != sizeof (struct TALER_CRYPTO_CsRevokeRequest))
664 : {
665 0 : GNUNET_break_op (0);
666 0 : return GNUNET_SYSERR;
667 : }
668 3 : return handle_revoke_request (
669 : client,
670 : (const struct TALER_CRYPTO_CsRevokeRequest *) hdr);
671 22 : case TALER_HELPER_CS_MT_REQ_RDERIVE:
672 22 : if (msize != sizeof (struct TALER_CRYPTO_CsRDeriveRequest))
673 : {
674 0 : GNUNET_break_op (0);
675 0 : return GNUNET_SYSERR;
676 : }
677 22 : return handle_r_derive_request (client,
678 : (const struct
679 : TALER_CRYPTO_CsRDeriveRequest *) hdr);
680 0 : default:
681 0 : GNUNET_break_op (0);
682 0 : return GNUNET_SYSERR;
683 : }
684 : }
685 :
686 :
687 : /**
688 : * Send our initial key set to @a client together with the
689 : * "sync" terminator.
690 : *
691 : * @param client the client to inform
692 : * @return #GNUNET_OK on success
693 : */
694 : static enum GNUNET_GenericReturnValue
695 9 : cs_client_init (struct TES_Client *client)
696 : {
697 9 : size_t obs = 0;
698 : char *buf;
699 :
700 9 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
701 : "Initializing new client %p\n",
702 : client);
703 9 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
704 18 : for (struct Denomination *denom = denom_head;
705 : NULL != denom;
706 9 : denom = denom->next)
707 : {
708 87 : for (struct DenominationKey *dk = denom->keys_head;
709 : NULL != dk;
710 78 : dk = dk->next)
711 : {
712 78 : obs += ntohs (dk->an->header.size);
713 : }
714 : }
715 9 : buf = GNUNET_malloc (obs);
716 9 : obs = 0;
717 18 : for (struct Denomination *denom = denom_head;
718 : NULL != denom;
719 9 : denom = denom->next)
720 : {
721 87 : for (struct DenominationKey *dk = denom->keys_head;
722 : NULL != dk;
723 78 : dk = dk->next)
724 : {
725 78 : memcpy (&buf[obs],
726 78 : dk->an,
727 78 : ntohs (dk->an->header.size));
728 78 : obs += ntohs (dk->an->header.size);
729 : }
730 : }
731 9 : client->key_gen = key_gen;
732 9 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
733 9 : if (GNUNET_OK !=
734 9 : TES_transmit_raw (client->csock,
735 : obs,
736 : buf))
737 : {
738 0 : GNUNET_free (buf);
739 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
740 : "Client %p must have disconnected\n",
741 : client);
742 0 : return GNUNET_SYSERR;
743 : }
744 9 : GNUNET_free (buf);
745 : {
746 9 : struct GNUNET_MessageHeader synced = {
747 9 : .type = htons (TALER_HELPER_CS_SYNCED),
748 9 : .size = htons (sizeof (synced))
749 : };
750 :
751 9 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
752 : "Sending CS SYNCED message to %p\n",
753 : client);
754 9 : if (GNUNET_OK !=
755 9 : TES_transmit (client->csock,
756 : &synced))
757 : {
758 0 : GNUNET_break (0);
759 0 : return GNUNET_SYSERR;
760 : }
761 : }
762 9 : return GNUNET_OK;
763 : }
764 :
765 :
766 : /**
767 : * Notify @a client about all changes to the keys since
768 : * the last generation known to the @a client.
769 : *
770 : * @param client the client to notify
771 : * @return #GNUNET_OK on success
772 : */
773 : static enum GNUNET_GenericReturnValue
774 3 : cs_update_client_keys (struct TES_Client *client)
775 : {
776 3 : size_t obs = 0;
777 : char *buf;
778 : enum GNUNET_GenericReturnValue ret;
779 :
780 3 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
781 6 : for (struct Denomination *denom = denom_head;
782 : NULL != denom;
783 3 : denom = denom->next)
784 : {
785 27 : for (struct DenominationKey *key = denom->keys_head;
786 : NULL != key;
787 24 : key = key->next)
788 : {
789 24 : if (key->key_gen <= client->key_gen)
790 18 : continue;
791 6 : if (key->purge)
792 3 : obs += sizeof (struct TALER_CRYPTO_CsKeyPurgeNotification);
793 : else
794 3 : obs += ntohs (key->an->header.size);
795 : }
796 : }
797 3 : if (0 == obs)
798 : {
799 : /* nothing to do */
800 0 : client->key_gen = key_gen;
801 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
802 0 : return GNUNET_OK;
803 : }
804 3 : buf = GNUNET_malloc (obs);
805 3 : obs = 0;
806 6 : for (struct Denomination *denom = denom_head;
807 : NULL != denom;
808 3 : denom = denom->next)
809 : {
810 27 : for (struct DenominationKey *key = denom->keys_head;
811 : NULL != key;
812 24 : key = key->next)
813 : {
814 24 : if (key->key_gen <= client->key_gen)
815 18 : continue;
816 6 : if (key->purge)
817 : {
818 3 : struct TALER_CRYPTO_CsKeyPurgeNotification pn = {
819 3 : .header.type = htons (TALER_HELPER_CS_MT_PURGE),
820 3 : .header.size = htons (sizeof (pn)),
821 : .h_cs = key->h_cs
822 : };
823 :
824 3 : memcpy (&buf[obs],
825 : &pn,
826 : sizeof (pn));
827 3 : GNUNET_assert (obs + sizeof (pn)
828 : > obs);
829 3 : obs += sizeof (pn);
830 : }
831 : else
832 : {
833 3 : memcpy (&buf[obs],
834 3 : key->an,
835 3 : ntohs (key->an->header.size));
836 3 : GNUNET_assert (obs + ntohs (key->an->header.size)
837 : > obs);
838 3 : obs += ntohs (key->an->header.size);
839 : }
840 : }
841 : }
842 3 : client->key_gen = key_gen;
843 3 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
844 3 : ret = TES_transmit_raw (client->csock,
845 : obs,
846 : buf);
847 3 : GNUNET_free (buf);
848 3 : return ret;
849 : }
850 :
851 :
852 : /**
853 : * Create a new denomination key (we do not have enough).
854 : *
855 : * @param denom denomination key to create
856 : * @param now current time to use (to get many keys to use the exact same time)
857 : * @return #GNUNET_OK on success
858 : */
859 : static enum GNUNET_GenericReturnValue
860 6 : create_key (struct Denomination *denom,
861 : struct GNUNET_TIME_Timestamp now)
862 : {
863 : struct DenominationKey *dk;
864 : struct GNUNET_TIME_Timestamp anchor;
865 :
866 6 : anchor = now;
867 6 : if (NULL != denom->keys_tail)
868 : {
869 : struct GNUNET_TIME_Absolute abs;
870 :
871 5 : abs = GNUNET_TIME_absolute_add (denom->keys_tail->anchor.abs_time,
872 : GNUNET_TIME_relative_subtract (
873 : denom->duration_withdraw,
874 : overlap_duration));
875 5 : if (GNUNET_TIME_absolute_cmp (now.abs_time, <, abs))
876 5 : anchor = GNUNET_TIME_absolute_to_timestamp (abs);
877 : }
878 6 : dk = GNUNET_new (struct DenominationKey);
879 6 : dk->denom = denom;
880 6 : dk->anchor = anchor;
881 6 : if (GNUNET_OK !=
882 6 : setup_key (dk,
883 : denom->keys_tail))
884 : {
885 0 : GNUNET_break (0);
886 0 : GNUNET_free (dk);
887 0 : GNUNET_SCHEDULER_shutdown ();
888 0 : global_ret = EXIT_FAILURE;
889 0 : return GNUNET_SYSERR;
890 : }
891 6 : return GNUNET_OK;
892 : }
893 :
894 :
895 : /**
896 : * At what time does this denomination require its next action?
897 : * Basically, the minimum of the withdraw expiration time of the
898 : * oldest denomination key, and the withdraw expiration time of
899 : * the newest denomination key minus the #lookahead_sign time.
900 : *
901 : * @param denom denomination to compute action time for
902 : */
903 : static struct GNUNET_TIME_Absolute
904 3 : denomination_action_time (const struct Denomination *denom)
905 : {
906 3 : struct DenominationKey *head = denom->keys_head;
907 3 : struct DenominationKey *tail = denom->keys_tail;
908 : struct GNUNET_TIME_Absolute tt;
909 :
910 3 : if (NULL == head)
911 0 : return GNUNET_TIME_UNIT_ZERO_ABS;
912 3 : tt = GNUNET_TIME_absolute_subtract (
913 : GNUNET_TIME_absolute_subtract (
914 : GNUNET_TIME_absolute_add (tail->anchor.abs_time,
915 : denom->duration_withdraw),
916 : lookahead_sign),
917 : overlap_duration);
918 3 : if (head->rc > 0)
919 0 : return tt; /* head expiration does not count due to rc > 0 */
920 3 : return GNUNET_TIME_absolute_min (
921 : GNUNET_TIME_absolute_add (head->anchor.abs_time,
922 : denom->duration_withdraw),
923 : tt);
924 : }
925 :
926 :
927 : /**
928 : * Create new keys and expire ancient keys of the given denomination @a denom.
929 : * Removes the @a denom from the #denom_head DLL and re-insert its at the
930 : * correct location sorted by next maintenance activity.
931 : *
932 : * @param[in,out] denom denomination to update material for
933 : * @param now current time to use (to get many keys to use the exact same time)
934 : * @param[in,out] wake set to true if we should wake the clients
935 : * @return #GNUNET_OK on success
936 : */
937 : static enum GNUNET_GenericReturnValue
938 2 : update_keys (struct Denomination *denom,
939 : struct GNUNET_TIME_Timestamp now,
940 : bool *wake)
941 : {
942 : /* create new denomination keys */
943 2 : if (NULL != denom->keys_tail)
944 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
945 : "Updating keys of denomination `%s', last key %s valid for another %s\n",
946 : denom->section,
947 : GNUNET_h2s (&denom->keys_tail->h_cs.hash),
948 : GNUNET_TIME_relative2s (
949 : GNUNET_TIME_absolute_get_remaining (
950 : GNUNET_TIME_absolute_subtract (
951 : GNUNET_TIME_absolute_add (
952 : denom->keys_tail->anchor.abs_time,
953 : denom->duration_withdraw),
954 : overlap_duration)),
955 : GNUNET_YES));
956 15 : while ( (NULL == denom->keys_tail) ||
957 7 : GNUNET_TIME_absolute_is_past (
958 : GNUNET_TIME_absolute_subtract (
959 : GNUNET_TIME_absolute_subtract (
960 7 : GNUNET_TIME_absolute_add (denom->keys_tail->anchor.abs_time,
961 : denom->duration_withdraw),
962 : lookahead_sign),
963 : overlap_duration)) )
964 : {
965 6 : if (! *wake)
966 : {
967 0 : key_gen++;
968 0 : *wake = true;
969 : }
970 6 : if (GNUNET_OK !=
971 6 : create_key (denom,
972 : now))
973 : {
974 0 : GNUNET_break (0);
975 0 : global_ret = EXIT_FAILURE;
976 0 : GNUNET_SCHEDULER_shutdown ();
977 0 : return GNUNET_SYSERR;
978 : }
979 : }
980 : /* remove expired denomination keys */
981 4 : while ( (NULL != denom->keys_head) &&
982 2 : GNUNET_TIME_absolute_is_past
983 2 : (GNUNET_TIME_absolute_add (denom->keys_head->anchor.abs_time,
984 : denom->duration_withdraw)) )
985 : {
986 0 : struct DenominationKey *key = denom->keys_head;
987 0 : struct DenominationKey *nxt = key->next;
988 :
989 0 : if (0 != key->rc)
990 0 : break; /* later */
991 0 : GNUNET_CONTAINER_DLL_remove (denom->keys_head,
992 : denom->keys_tail,
993 : key);
994 0 : GNUNET_assert (GNUNET_OK ==
995 : GNUNET_CONTAINER_multihashmap_remove (
996 : keys,
997 : &key->h_cs.hash,
998 : key));
999 0 : if ( (! key->purge) &&
1000 0 : (0 != unlink (key->filename)) )
1001 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1002 : "unlink",
1003 : key->filename);
1004 0 : GNUNET_free (key->filename);
1005 0 : GNUNET_free (key->an);
1006 0 : GNUNET_free (key);
1007 0 : key = nxt;
1008 : }
1009 :
1010 : /* Update position of 'denom' in #denom_head DLL: sort by action time */
1011 : {
1012 : struct Denomination *before;
1013 : struct GNUNET_TIME_Absolute at;
1014 :
1015 2 : at = denomination_action_time (denom);
1016 2 : GNUNET_CONTAINER_DLL_remove (denom_head,
1017 : denom_tail,
1018 : denom);
1019 2 : before = NULL;
1020 2 : for (struct Denomination *pos = denom_head;
1021 : NULL != pos;
1022 0 : pos = pos->next)
1023 : {
1024 0 : if (GNUNET_TIME_absolute_cmp (denomination_action_time (pos), >=, at))
1025 0 : break;
1026 0 : before = pos;
1027 : }
1028 2 : GNUNET_CONTAINER_DLL_insert_after (denom_head,
1029 : denom_tail,
1030 : before,
1031 : denom);
1032 : }
1033 2 : return GNUNET_OK;
1034 : }
1035 :
1036 :
1037 : /**
1038 : * Task run periodically to expire keys and/or generate fresh ones.
1039 : *
1040 : * @param cls NULL
1041 : */
1042 : static void
1043 1 : update_denominations (void *cls)
1044 : {
1045 : struct Denomination *denom;
1046 : struct GNUNET_TIME_Absolute now;
1047 : struct GNUNET_TIME_Timestamp t;
1048 1 : bool wake = false;
1049 :
1050 : (void) cls;
1051 1 : keygen_task = NULL;
1052 1 : now = GNUNET_TIME_absolute_get ();
1053 1 : t = GNUNET_TIME_absolute_to_timestamp (now);
1054 1 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1055 : "Updating denominations ...\n");
1056 1 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
1057 : do {
1058 1 : denom = denom_head;
1059 1 : if (GNUNET_OK !=
1060 1 : update_keys (denom,
1061 : t,
1062 : &wake))
1063 0 : return;
1064 1 : } while (denom != denom_head);
1065 1 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1066 1 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1067 : "Updating denominations finished ...\n");
1068 1 : if (wake)
1069 0 : TES_wake_clients ();
1070 1 : keygen_task = GNUNET_SCHEDULER_add_at (denomination_action_time (denom),
1071 : &update_denominations,
1072 : NULL);
1073 : }
1074 :
1075 :
1076 : /**
1077 : * Parse private key of denomination @a denom in @a buf.
1078 : *
1079 : * @param[out] denom denomination of the key
1080 : * @param filename name of the file we are parsing, for logging
1081 : * @param priv key material
1082 : */
1083 : static void
1084 0 : parse_key (struct Denomination *denom,
1085 : const char *filename,
1086 : const struct GNUNET_CRYPTO_CsPrivateKey *priv)
1087 : {
1088 : char *anchor_s;
1089 : char dummy;
1090 : unsigned long long anchor_ll;
1091 : struct GNUNET_TIME_Timestamp anchor;
1092 :
1093 0 : anchor_s = strrchr (filename,
1094 : '/');
1095 0 : if (NULL == anchor_s)
1096 : {
1097 : /* File in a directory without '/' in the name, this makes no sense. */
1098 0 : GNUNET_break (0);
1099 0 : return;
1100 : }
1101 0 : anchor_s++;
1102 0 : if (1 != sscanf (anchor_s,
1103 : "%llu%c",
1104 : &anchor_ll,
1105 : &dummy))
1106 : {
1107 : /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */
1108 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1109 : "Filename `%s' invalid for key file, skipping\n",
1110 : filename);
1111 0 : return;
1112 : }
1113 : anchor.abs_time.abs_value_us
1114 0 : = anchor_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
1115 0 : if (anchor_ll != anchor.abs_time.abs_value_us
1116 0 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
1117 : {
1118 : /* Integer overflow. Bad, invalid filename. */
1119 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1120 : "Filename `%s' invalid for key file, skipping\n",
1121 : filename);
1122 0 : return;
1123 : }
1124 : {
1125 : struct GNUNET_CRYPTO_CsPublicKey pub;
1126 : struct DenominationKey *dk;
1127 : struct DenominationKey *before;
1128 :
1129 0 : GNUNET_CRYPTO_cs_private_key_get_public (priv,
1130 : &pub);
1131 0 : dk = GNUNET_new (struct DenominationKey);
1132 0 : dk->denom_priv = *priv;
1133 0 : dk->denom = denom;
1134 0 : dk->anchor = anchor;
1135 0 : dk->filename = GNUNET_strdup (filename);
1136 0 : TALER_cs_pub_hash (&pub,
1137 : &dk->h_cs);
1138 0 : dk->denom_pub = pub;
1139 0 : generate_response (dk);
1140 0 : if (GNUNET_OK !=
1141 0 : GNUNET_CONTAINER_multihashmap_put (
1142 : keys,
1143 0 : &dk->h_cs.hash,
1144 : dk,
1145 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1146 : {
1147 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1148 : "Duplicate private key %s detected in file `%s'. Skipping.\n",
1149 : GNUNET_h2s (&dk->h_cs.hash),
1150 : filename);
1151 0 : GNUNET_free (dk->an);
1152 0 : GNUNET_free (dk);
1153 0 : return;
1154 : }
1155 0 : before = NULL;
1156 0 : for (struct DenominationKey *pos = denom->keys_head;
1157 : NULL != pos;
1158 0 : pos = pos->next)
1159 : {
1160 0 : if (GNUNET_TIME_timestamp_cmp (pos->anchor, >, anchor))
1161 0 : break;
1162 0 : before = pos;
1163 : }
1164 0 : GNUNET_CONTAINER_DLL_insert_after (denom->keys_head,
1165 : denom->keys_tail,
1166 : before,
1167 : dk);
1168 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1169 : "Imported key %s from `%s'\n",
1170 : GNUNET_h2s (&dk->h_cs.hash),
1171 : filename);
1172 : }
1173 : }
1174 :
1175 :
1176 : /**
1177 : * Import a private key from @a filename for the denomination
1178 : * given in @a cls.
1179 : *
1180 : * @param[in,out] cls a `struct Denomiantion`
1181 : * @param filename name of a file in the directory
1182 : * @return #GNUNET_OK (always, continue to iterate)
1183 : */
1184 : static enum GNUNET_GenericReturnValue
1185 0 : import_key (void *cls,
1186 : const char *filename)
1187 : {
1188 0 : struct Denomination *denom = cls;
1189 : struct GNUNET_DISK_FileHandle *fh;
1190 : struct GNUNET_DISK_MapHandle *map;
1191 : void *ptr;
1192 : int fd;
1193 : struct stat sbuf;
1194 :
1195 : {
1196 : struct stat lsbuf;
1197 :
1198 0 : if (0 != lstat (filename,
1199 : &lsbuf))
1200 : {
1201 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1202 : "lstat",
1203 : filename);
1204 0 : return GNUNET_OK;
1205 : }
1206 0 : if (! S_ISREG (lsbuf.st_mode))
1207 : {
1208 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1209 : "File `%s' is not a regular file, which is not allowed for private keys!\n",
1210 : filename);
1211 0 : return GNUNET_OK;
1212 : }
1213 : }
1214 :
1215 0 : fd = open (filename,
1216 : O_RDONLY | O_CLOEXEC);
1217 0 : if (-1 == fd)
1218 : {
1219 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1220 : "open",
1221 : filename);
1222 0 : return GNUNET_OK;
1223 : }
1224 0 : if (0 != fstat (fd,
1225 : &sbuf))
1226 : {
1227 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1228 : "stat",
1229 : filename);
1230 0 : GNUNET_break (0 == close (fd));
1231 0 : return GNUNET_OK;
1232 : }
1233 0 : if (! S_ISREG (sbuf.st_mode))
1234 : {
1235 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1236 : "File `%s' is not a regular file, which is not allowed for private keys!\n",
1237 : filename);
1238 0 : GNUNET_break (0 == close (fd));
1239 0 : return GNUNET_OK;
1240 : }
1241 0 : if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
1242 : {
1243 : /* permission are NOT tight, try to patch them up! */
1244 0 : if (0 !=
1245 0 : fchmod (fd,
1246 : S_IRUSR))
1247 : {
1248 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1249 : "fchmod",
1250 : filename);
1251 : /* refuse to use key if file has wrong permissions */
1252 0 : GNUNET_break (0 == close (fd));
1253 0 : return GNUNET_OK;
1254 : }
1255 : }
1256 0 : fh = GNUNET_DISK_get_handle_from_int_fd (fd);
1257 0 : if (NULL == fh)
1258 : {
1259 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1260 : "open",
1261 : filename);
1262 0 : GNUNET_break (0 == close (fd));
1263 0 : return GNUNET_OK;
1264 : }
1265 0 : if (sbuf.st_size != sizeof(struct GNUNET_CRYPTO_CsPrivateKey))
1266 : {
1267 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1268 : "File `%s' too big to be a private key\n",
1269 : filename);
1270 0 : GNUNET_DISK_file_close (fh);
1271 0 : return GNUNET_OK;
1272 : }
1273 0 : ptr = GNUNET_DISK_file_map (fh,
1274 : &map,
1275 : GNUNET_DISK_MAP_TYPE_READ,
1276 0 : (size_t) sbuf.st_size);
1277 0 : if (NULL == ptr)
1278 : {
1279 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1280 : "mmap",
1281 : filename);
1282 0 : GNUNET_DISK_file_close (fh);
1283 0 : return GNUNET_OK;
1284 : }
1285 0 : parse_key (denom,
1286 : filename,
1287 : (const struct GNUNET_CRYPTO_CsPrivateKey *) ptr);
1288 0 : GNUNET_DISK_file_unmap (map);
1289 0 : GNUNET_DISK_file_close (fh);
1290 0 : return GNUNET_OK;
1291 : }
1292 :
1293 :
1294 : /**
1295 : * Parse configuration for denomination type parameters. Also determines
1296 : * our anchor by looking at the existing denominations of the same type.
1297 : *
1298 : * @param cfg configuration to use
1299 : * @param ct section in the configuration file giving the denomination type parameters
1300 : * @param[out] denom set to the denomination parameters from the configuration
1301 : * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid
1302 : */
1303 : static enum GNUNET_GenericReturnValue
1304 1 : parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
1305 : const char *ct,
1306 : struct Denomination *denom)
1307 : {
1308 1 : if (GNUNET_OK !=
1309 1 : GNUNET_CONFIGURATION_get_value_time (cfg,
1310 : ct,
1311 : "DURATION_WITHDRAW",
1312 : &denom->duration_withdraw))
1313 : {
1314 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1315 : ct,
1316 : "DURATION_WITHDRAW");
1317 0 : return GNUNET_SYSERR;
1318 : }
1319 1 : if (GNUNET_TIME_relative_cmp (overlap_duration,
1320 : >=,
1321 : denom->duration_withdraw))
1322 : {
1323 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1324 : "taler-exchange-secmod-cs",
1325 : "OVERLAP_DURATION",
1326 : "Value given must be smaller than value for DURATION_WITHDRAW!");
1327 0 : return GNUNET_SYSERR;
1328 : }
1329 1 : denom->section = GNUNET_strdup (ct);
1330 1 : return GNUNET_OK;
1331 : }
1332 :
1333 :
1334 : /**
1335 : * Closure for #load_denominations.
1336 : */
1337 : struct LoadContext
1338 : {
1339 :
1340 : /**
1341 : * Configuration to use.
1342 : */
1343 : const struct GNUNET_CONFIGURATION_Handle *cfg;
1344 :
1345 : /**
1346 : * Current time to use.
1347 : */
1348 : struct GNUNET_TIME_Timestamp t;
1349 :
1350 : /**
1351 : * Status, to be set to #GNUNET_SYSERR on failure
1352 : */
1353 : enum GNUNET_GenericReturnValue ret;
1354 : };
1355 :
1356 :
1357 : /**
1358 : * Generate new denomination signing keys for the denomination type of the given @a
1359 : * denomination_alias.
1360 : *
1361 : * @param cls a `struct LoadContext`, with 'ret' to be set to #GNUNET_SYSERR on failure
1362 : * @param denomination_alias name of the denomination's section in the configuration
1363 : */
1364 : static void
1365 22 : load_denominations (void *cls,
1366 : const char *denomination_alias)
1367 : {
1368 22 : struct LoadContext *ctx = cls;
1369 : struct Denomination *denom;
1370 22 : bool wake = true;
1371 : char *cipher;
1372 :
1373 22 : if ( (0 != strncasecmp (denomination_alias,
1374 : "coin_",
1375 21 : strlen ("coin_"))) &&
1376 21 : (0 != strncasecmp (denomination_alias,
1377 : "coin-",
1378 : strlen ("coin-"))) )
1379 21 : return; /* not a denomination type definition */
1380 1 : if (GNUNET_OK !=
1381 1 : GNUNET_CONFIGURATION_get_value_string (ctx->cfg,
1382 : denomination_alias,
1383 : "CIPHER",
1384 : &cipher))
1385 : {
1386 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1387 : denomination_alias,
1388 : "CIPHER");
1389 0 : return;
1390 : }
1391 1 : if (0 != strcmp (cipher, "CS"))
1392 : {
1393 0 : GNUNET_free (cipher);
1394 0 : return; /* Ignore denominations of other types than CS*/
1395 : }
1396 1 : GNUNET_free (cipher);
1397 :
1398 1 : denom = GNUNET_new (struct Denomination);
1399 1 : if (GNUNET_OK !=
1400 1 : parse_denomination_cfg (ctx->cfg,
1401 : denomination_alias,
1402 : denom))
1403 : {
1404 0 : ctx->ret = GNUNET_SYSERR;
1405 0 : GNUNET_free (denom);
1406 0 : return;
1407 : }
1408 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1409 : "Loading keys for denomination %s\n",
1410 : denom->section);
1411 : {
1412 : char *dname;
1413 :
1414 1 : GNUNET_asprintf (&dname,
1415 : "%s/%s",
1416 : keydir,
1417 : denom->section);
1418 1 : GNUNET_break (GNUNET_OK ==
1419 : GNUNET_DISK_directory_create (dname));
1420 1 : GNUNET_DISK_directory_scan (dname,
1421 : &import_key,
1422 : denom);
1423 1 : GNUNET_free (dname);
1424 : }
1425 1 : GNUNET_CONTAINER_DLL_insert (denom_head,
1426 : denom_tail,
1427 : denom);
1428 1 : update_keys (denom,
1429 : ctx->t,
1430 : &wake);
1431 : }
1432 :
1433 :
1434 : /**
1435 : * Load the various duration values from @a cfg
1436 : *
1437 : * @param cfg configuration to use
1438 : * @return #GNUNET_OK on success
1439 : */
1440 : static enum GNUNET_GenericReturnValue
1441 1 : load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
1442 : {
1443 1 : if (GNUNET_OK !=
1444 1 : GNUNET_CONFIGURATION_get_value_time (cfg,
1445 : "taler-exchange-secmod-cs",
1446 : "OVERLAP_DURATION",
1447 : &overlap_duration))
1448 : {
1449 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1450 : "taler-exchange-secmod-cs",
1451 : "OVERLAP_DURATION");
1452 0 : return GNUNET_SYSERR;
1453 : }
1454 1 : if (GNUNET_OK !=
1455 1 : GNUNET_CONFIGURATION_get_value_time (cfg,
1456 : "taler-exchange-secmod-cs",
1457 : "LOOKAHEAD_SIGN",
1458 : &lookahead_sign))
1459 : {
1460 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1461 : "taler-exchange-secmod-cs",
1462 : "LOOKAHEAD_SIGN");
1463 0 : return GNUNET_SYSERR;
1464 : }
1465 1 : return GNUNET_OK;
1466 : }
1467 :
1468 :
1469 : /**
1470 : * Function run on shutdown. Stops the various jobs (nicely).
1471 : *
1472 : * @param cls NULL
1473 : */
1474 : static void
1475 1 : do_shutdown (void *cls)
1476 : {
1477 : (void) cls;
1478 1 : TES_listen_stop ();
1479 1 : if (NULL != keygen_task)
1480 : {
1481 1 : GNUNET_SCHEDULER_cancel (keygen_task);
1482 1 : keygen_task = NULL;
1483 : }
1484 1 : }
1485 :
1486 :
1487 : /**
1488 : * Main function that will be run under the GNUnet scheduler.
1489 : *
1490 : * @param cls closure
1491 : * @param args remaining command-line arguments
1492 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1493 : * @param cfg configuration
1494 : */
1495 : static void
1496 1 : run (void *cls,
1497 : char *const *args,
1498 : const char *cfgfile,
1499 : const struct GNUNET_CONFIGURATION_Handle *cfg)
1500 : {
1501 : static struct TES_Callbacks cb = {
1502 : .dispatch = &cs_work_dispatch,
1503 : .updater = &cs_update_client_keys,
1504 : .init = &cs_client_init
1505 : };
1506 :
1507 : (void) cls;
1508 : (void) args;
1509 : (void) cfgfile;
1510 1 : if (GNUNET_TIME_timestamp_cmp (now, !=, now_tmp))
1511 : {
1512 : /* The user gave "--now", use it! */
1513 0 : now = now_tmp;
1514 : }
1515 : else
1516 : {
1517 : /* get current time again, we may be timetraveling! */
1518 1 : now = GNUNET_TIME_timestamp_get ();
1519 : }
1520 1 : if (GNUNET_OK !=
1521 1 : GNUNET_CONFIGURATION_get_value_filename (cfg,
1522 : "taler-exchange-secmod-cs",
1523 : "KEY_DIR",
1524 : &keydir))
1525 : {
1526 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1527 : "taler-exchange-secmod-cs",
1528 : "KEY_DIR");
1529 0 : global_ret = EXIT_NOTCONFIGURED;
1530 0 : return;
1531 : }
1532 1 : if (GNUNET_OK !=
1533 1 : load_durations (cfg))
1534 : {
1535 0 : global_ret = EXIT_NOTCONFIGURED;
1536 0 : return;
1537 : }
1538 1 : global_ret = TES_listen_start (cfg,
1539 : "taler-exchange-secmod-cs",
1540 : &cb);
1541 1 : if (0 != global_ret)
1542 0 : return;
1543 1 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1544 : NULL);
1545 : /* Load denominations */
1546 1 : keys = GNUNET_CONTAINER_multihashmap_create (65536,
1547 : GNUNET_YES);
1548 : {
1549 1 : struct LoadContext lc = {
1550 : .cfg = cfg,
1551 : .ret = GNUNET_OK,
1552 : .t = now
1553 : };
1554 :
1555 1 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
1556 1 : GNUNET_CONFIGURATION_iterate_sections (cfg,
1557 : &load_denominations,
1558 : &lc);
1559 1 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
1560 1 : if (GNUNET_OK != lc.ret)
1561 : {
1562 0 : global_ret = EXIT_FAILURE;
1563 0 : GNUNET_SCHEDULER_shutdown ();
1564 0 : return;
1565 : }
1566 : }
1567 1 : if (NULL == denom_head)
1568 : {
1569 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1570 : "No CS denominations configured\n");
1571 0 : TES_wake_clients ();
1572 0 : return;
1573 : }
1574 : /* start job to keep keys up-to-date; MUST be run before the #listen_task,
1575 : hence with priority. */
1576 1 : keygen_task = GNUNET_SCHEDULER_add_with_priority (
1577 : GNUNET_SCHEDULER_PRIORITY_URGENT,
1578 : &update_denominations,
1579 : NULL);
1580 : }
1581 :
1582 :
1583 : /**
1584 : * The entry point.
1585 : *
1586 : * @param argc number of arguments in @a argv
1587 : * @param argv command-line arguments
1588 : * @return 0 on normal termination
1589 : */
1590 : int
1591 1 : main (int argc,
1592 : char **argv)
1593 : {
1594 1 : struct GNUNET_GETOPT_CommandLineOption options[] = {
1595 1 : GNUNET_GETOPT_option_timetravel ('T',
1596 : "timetravel"),
1597 1 : GNUNET_GETOPT_option_timestamp ('t',
1598 : "time",
1599 : "TIMESTAMP",
1600 : "pretend it is a different time for the update",
1601 : &now_tmp),
1602 : GNUNET_GETOPT_OPTION_END
1603 : };
1604 : enum GNUNET_GenericReturnValue ret;
1605 :
1606 : /* Restrict permissions for the key files that we create. */
1607 1 : (void) umask (S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH);
1608 :
1609 : /* force linker to link against libtalerutil; if we do
1610 : not do this, the linker may "optimize" libtalerutil
1611 : away and skip #TALER_OS_init(), which we do need */
1612 1 : TALER_OS_init ();
1613 1 : now_tmp = now = GNUNET_TIME_timestamp_get ();
1614 1 : ret = GNUNET_PROGRAM_run (argc, argv,
1615 : "taler-exchange-secmod-cs",
1616 : "Handle private CS key operations for a Taler exchange",
1617 : options,
1618 : &run,
1619 : NULL);
1620 1 : if (GNUNET_NO == ret)
1621 0 : return EXIT_SUCCESS;
1622 1 : if (GNUNET_SYSERR == ret)
1623 0 : return EXIT_INVALIDARGUMENT;
1624 1 : return global_ret;
1625 : }
|