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-eddsa.c
18 : * @brief Standalone process to perform private key EDDSA 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 threat 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 (only) do the signing in parallel,
29 : * one per client.
30 : * - thread-safety: signing happens in parallel, thus when REMOVING private keys,
31 : * we must ensure that all signers are done before we fully free() the
32 : * private key. This is done by reference counting (as work is always
33 : * assigned and collected by the main thread).
34 : */
35 : #include "platform.h"
36 : #include "taler_util.h"
37 : #include "taler-exchange-secmod-eddsa.h"
38 : #include <gcrypt.h>
39 : #include <pthread.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 : * One particular key.
48 : */
49 : struct Key
50 : {
51 :
52 : /**
53 : * Kept in a DLL. Sorted by anchor time.
54 : */
55 : struct Key *next;
56 :
57 : /**
58 : * Kept in a DLL. Sorted by anchor time.
59 : */
60 : struct Key *prev;
61 :
62 : /**
63 : * Name of the file this key is stored under.
64 : */
65 : char *filename;
66 :
67 : /**
68 : * The private key.
69 : */
70 : struct TALER_ExchangePrivateKeyP exchange_priv;
71 :
72 : /**
73 : * The public key.
74 : */
75 : struct TALER_ExchangePublicKeyP exchange_pub;
76 :
77 : /**
78 : * Time at which this key is supposed to become valid.
79 : */
80 : struct GNUNET_TIME_Timestamp anchor;
81 :
82 : /**
83 : * Generation when this key was created or revoked.
84 : */
85 : uint64_t key_gen;
86 :
87 : /**
88 : * Reference counter. Counts the number of threads that are
89 : * using this key at this time.
90 : */
91 : unsigned int rc;
92 :
93 : /**
94 : * Flag set to true if this key has been purged and the memory
95 : * must be freed as soon as @e rc hits zero.
96 : */
97 : bool purge;
98 :
99 : };
100 :
101 :
102 : /**
103 : * Head of DLL of actual keys, sorted by anchor.
104 : */
105 : static struct Key *keys_head;
106 :
107 : /**
108 : * Tail of DLL of actual keys.
109 : */
110 : static struct Key *keys_tail;
111 :
112 : /**
113 : * How long can a key be used?
114 : */
115 : static struct GNUNET_TIME_Relative duration;
116 :
117 : /**
118 : * Return value from main().
119 : */
120 : static int global_ret;
121 :
122 : /**
123 : * Time when the key update is executed.
124 : * Either the actual current time, or a pretended time.
125 : */
126 : static struct GNUNET_TIME_Timestamp now;
127 :
128 : /**
129 : * The time for the key update, as passed by the user
130 : * on the command line.
131 : */
132 : static struct GNUNET_TIME_Timestamp now_tmp;
133 :
134 : /**
135 : * Where do we store the keys?
136 : */
137 : static char *keydir;
138 :
139 : /**
140 : * How much should coin creation duration overlap
141 : * with the next key? Basically, the starting time of two
142 : * keys is always #duration - #overlap_duration apart.
143 : */
144 : static struct GNUNET_TIME_Relative overlap_duration;
145 :
146 : /**
147 : * How long into the future do we pre-generate keys?
148 : */
149 : static struct GNUNET_TIME_Relative lookahead_sign;
150 :
151 : /**
152 : * Task run to generate new keys.
153 : */
154 : static struct GNUNET_SCHEDULER_Task *keygen_task;
155 :
156 : /**
157 : * Lock for the keys queue.
158 : */
159 : static pthread_mutex_t keys_lock;
160 :
161 : /**
162 : * Current key generation.
163 : */
164 : static uint64_t key_gen;
165 :
166 :
167 : /**
168 : * Notify @a client about @a key becoming available.
169 : *
170 : * @param[in,out] client the client to notify; possible freed if transmission fails
171 : * @param key the key to notify @a client about
172 : * @return #GNUNET_OK on success
173 : */
174 : static enum GNUNET_GenericReturnValue
175 81 : notify_client_key_add (struct TES_Client *client,
176 : const struct Key *key)
177 : {
178 162 : struct TALER_CRYPTO_EddsaKeyAvailableNotification an = {
179 81 : .header.size = htons (sizeof (an)),
180 81 : .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL),
181 81 : .anchor_time = GNUNET_TIME_timestamp_hton (key->anchor),
182 81 : .duration = GNUNET_TIME_relative_hton (duration),
183 : .exchange_pub = key->exchange_pub,
184 : .secm_pub = TES_smpub
185 : };
186 :
187 81 : TALER_exchange_secmod_eddsa_sign (&key->exchange_pub,
188 : key->anchor,
189 : duration,
190 : &TES_smpriv,
191 : &an.secm_sig);
192 81 : if (GNUNET_OK !=
193 81 : TES_transmit (client->csock,
194 : &an.header))
195 : {
196 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
197 : "Client %p must have disconnected\n",
198 : client);
199 0 : return GNUNET_SYSERR;
200 : }
201 81 : return GNUNET_OK;
202 : }
203 :
204 :
205 : /**
206 : * Notify @a client about @a key being purged.
207 : *
208 : * @param[in,out] client the client to notify; possible freed if transmission fails
209 : * @param key the key to notify @a client about
210 : * @return #GNUNET_OK on success
211 : */
212 : static enum GNUNET_GenericReturnValue
213 3 : notify_client_key_del (struct TES_Client *client,
214 : const struct Key *key)
215 : {
216 3 : struct TALER_CRYPTO_EddsaKeyPurgeNotification pn = {
217 3 : .header.type = htons (TALER_HELPER_EDDSA_MT_PURGE),
218 3 : .header.size = htons (sizeof (pn)),
219 : .exchange_pub = key->exchange_pub
220 : };
221 :
222 3 : if (GNUNET_OK !=
223 3 : TES_transmit (client->csock,
224 : &pn.header))
225 : {
226 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
227 : "Client %p must have disconnected\n",
228 : client);
229 0 : return GNUNET_SYSERR;
230 : }
231 3 : return GNUNET_OK;
232 : }
233 :
234 :
235 : /**
236 : * Handle @a client request @a sr to create signature. Create the
237 : * signature using the respective key and return the result to
238 : * the client.
239 : *
240 : * @param client the client making the request
241 : * @param sr the request details
242 : * @return #GNUNET_OK on success
243 : */
244 : static enum GNUNET_GenericReturnValue
245 899 : handle_sign_request (struct TES_Client *client,
246 : const struct TALER_CRYPTO_EddsaSignRequest *sr)
247 : {
248 899 : const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose = &sr->purpose;
249 899 : size_t purpose_size = ntohs (sr->header.size) - sizeof (*sr)
250 : + sizeof (*purpose);
251 : struct Key *key;
252 899 : struct TALER_CRYPTO_EddsaSignResponse sres = {
253 899 : .header.size = htons (sizeof (sres)),
254 899 : .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE)
255 : };
256 : enum TALER_ErrorCode ec;
257 :
258 899 : if (purpose_size != htonl (purpose->size))
259 : {
260 0 : struct TALER_CRYPTO_EddsaSignFailure sf = {
261 0 : .header.size = htons (sizeof (sr)),
262 0 : .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
263 0 : .ec = htonl (TALER_EC_GENERIC_PARAMETER_MALFORMED)
264 : };
265 :
266 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
267 : "Signing request failed, request malformed\n");
268 0 : return TES_transmit (client->csock,
269 : &sf.header);
270 : }
271 :
272 899 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
273 903 : key = keys_head;
274 1806 : while ( (NULL != key) &&
275 903 : (GNUNET_TIME_absolute_is_past (
276 : GNUNET_TIME_absolute_add (key->anchor.abs_time,
277 : duration))) )
278 : {
279 0 : struct Key *nxt = key->next;
280 :
281 0 : if (0 != key->rc)
282 0 : break; /* do later */
283 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
284 : "Deleting past key %s (expired %s ago)\n",
285 : TALER_B2S (&nxt->exchange_pub),
286 : GNUNET_TIME_relative2s (
287 : GNUNET_TIME_absolute_get_duration (
288 : GNUNET_TIME_absolute_add (key->anchor.abs_time,
289 : duration)),
290 : GNUNET_YES));
291 0 : GNUNET_CONTAINER_DLL_remove (keys_head,
292 : keys_tail,
293 : key);
294 0 : if ( (! key->purge) &&
295 0 : (0 != unlink (key->filename)) )
296 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
297 : "unlink",
298 : key->filename);
299 0 : GNUNET_free (key->filename);
300 0 : GNUNET_free (key);
301 0 : key = nxt;
302 : }
303 903 : if (NULL == key)
304 : {
305 0 : GNUNET_break (0);
306 0 : ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
307 : }
308 : else
309 : {
310 903 : GNUNET_assert (key->rc < UINT_MAX);
311 903 : key->rc++;
312 903 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
313 :
314 900 : if (GNUNET_OK !=
315 903 : GNUNET_CRYPTO_eddsa_sign_ (&key->exchange_priv.eddsa_priv,
316 : purpose,
317 : &sres.exchange_sig.eddsa_signature))
318 0 : ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
319 : else
320 900 : ec = TALER_EC_NONE;
321 900 : sres.exchange_pub = key->exchange_pub;
322 900 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
323 903 : GNUNET_assert (key->rc > 0);
324 903 : key->rc--;
325 : }
326 903 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
327 903 : if (TALER_EC_NONE != ec)
328 : {
329 0 : struct TALER_CRYPTO_EddsaSignFailure sf = {
330 0 : .header.size = htons (sizeof (sf)),
331 0 : .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
332 0 : .ec = htonl ((uint32_t) ec)
333 : };
334 :
335 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
336 : "Signing request %p failed, worker failed to produce signature\n",
337 : client);
338 0 : return TES_transmit (client->csock,
339 : &sf.header);
340 : }
341 903 : return TES_transmit (client->csock,
342 : &sres.header);
343 : }
344 :
345 :
346 : /**
347 : * Initialize key material for key @a key (also on disk).
348 : *
349 : * @param[in,out] key to compute key material for
350 : * @param position where in the DLL will the @a key go
351 : * @return #GNUNET_OK on success
352 : */
353 : static enum GNUNET_GenericReturnValue
354 9 : setup_key (struct Key *key,
355 : struct Key *position)
356 : {
357 : struct GNUNET_CRYPTO_EddsaPrivateKey priv;
358 : struct GNUNET_CRYPTO_EddsaPublicKey pub;
359 :
360 9 : GNUNET_CRYPTO_eddsa_key_create (&priv);
361 9 : GNUNET_CRYPTO_eddsa_key_get_public (&priv,
362 : &pub);
363 9 : GNUNET_asprintf (&key->filename,
364 : "%s/%llu",
365 : keydir,
366 9 : (unsigned long long) (key->anchor.abs_time.abs_value_us
367 9 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us));
368 9 : if (GNUNET_OK !=
369 9 : GNUNET_DISK_fn_write (key->filename,
370 : &priv,
371 : sizeof (priv),
372 : GNUNET_DISK_PERM_USER_READ))
373 : {
374 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
375 : "write",
376 : key->filename);
377 0 : return GNUNET_SYSERR;
378 : }
379 9 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
380 : "Setup fresh private key in `%s'\n",
381 : key->filename);
382 9 : key->key_gen = key_gen;
383 9 : key->exchange_priv.eddsa_priv = priv;
384 9 : key->exchange_pub.eddsa_pub = pub;
385 9 : GNUNET_CONTAINER_DLL_insert_after (keys_head,
386 : keys_tail,
387 : position,
388 : key);
389 9 : return GNUNET_OK;
390 : }
391 :
392 :
393 : /**
394 : * The validity period of a key @a key has expired. Purge it.
395 : *
396 : * @param[in] key expired or revoked key to purge
397 : */
398 : static void
399 3 : purge_key (struct Key *key)
400 : {
401 3 : if (key->purge)
402 : {
403 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
404 : "Key %s already purged, skipping\n",
405 : TALER_B2S (&key->exchange_pub));
406 0 : return;
407 : }
408 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
409 : "Purging key %s\n",
410 : TALER_B2S (&key->exchange_pub));
411 3 : if (0 != unlink (key->filename))
412 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
413 : "unlink",
414 : key->filename);
415 3 : key->purge = true;
416 3 : key->key_gen = key_gen;
417 3 : GNUNET_free (key->filename);
418 : }
419 :
420 :
421 : /**
422 : * A @a client informs us that a key has been revoked.
423 : * Check if the key is still in use, and if so replace (!)
424 : * it with a fresh key.
425 : *
426 : * @param client the client making the request
427 : * @param rr the revocation request
428 : * @return #GNUNET_OK on success
429 : */
430 : static enum GNUNET_GenericReturnValue
431 3 : handle_revoke_request (struct TES_Client *client,
432 : const struct TALER_CRYPTO_EddsaRevokeRequest *rr)
433 : {
434 : struct Key *key;
435 : struct Key *nkey;
436 :
437 : (void) client;
438 3 : key = NULL;
439 3 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
440 13 : for (struct Key *pos = keys_head;
441 : NULL != pos;
442 10 : pos = pos->next)
443 13 : if (0 == GNUNET_memcmp (&pos->exchange_pub,
444 : &rr->exchange_pub))
445 : {
446 3 : key = pos;
447 3 : break;
448 : }
449 3 : if (NULL == key)
450 : {
451 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
452 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
453 : "Revocation request ignored, key unknown\n");
454 0 : return GNUNET_OK;
455 : }
456 3 : if (key->purge)
457 : {
458 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
459 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
460 : "Revocation request ignored, key %s already revoked\n",
461 : TALER_B2S (&key->exchange_pub));
462 0 : return GNUNET_OK;
463 : }
464 3 : key_gen++;
465 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
466 : "Revoking key %s, bumping generation to %llu\n",
467 : TALER_B2S (&key->exchange_pub),
468 : (unsigned long long) key_gen);
469 3 : purge_key (key);
470 :
471 : /* Setup replacement key */
472 3 : nkey = GNUNET_new (struct Key);
473 3 : nkey->anchor = key->anchor;
474 3 : if (GNUNET_OK !=
475 3 : setup_key (nkey,
476 : key))
477 : {
478 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
479 0 : GNUNET_break (0);
480 0 : GNUNET_SCHEDULER_shutdown ();
481 0 : global_ret = EXIT_FAILURE;
482 0 : return GNUNET_SYSERR;
483 : }
484 3 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
485 3 : TES_wake_clients ();
486 3 : return GNUNET_OK;
487 : }
488 :
489 :
490 : /**
491 : * Handle @a hdr message received from @a client.
492 : *
493 : * @param client the client that received the message
494 : * @param hdr message that was received
495 : * @return #GNUNET_OK on success
496 : */
497 : static enum GNUNET_GenericReturnValue
498 902 : eddsa_work_dispatch (struct TES_Client *client,
499 : const struct GNUNET_MessageHeader *hdr)
500 : {
501 902 : uint16_t msize = ntohs (hdr->size);
502 :
503 902 : switch (ntohs (hdr->type))
504 : {
505 899 : case TALER_HELPER_EDDSA_MT_REQ_SIGN:
506 899 : if (msize < sizeof (struct TALER_CRYPTO_EddsaSignRequest))
507 : {
508 0 : GNUNET_break_op (0);
509 0 : return GNUNET_SYSERR;
510 : }
511 899 : return handle_sign_request (
512 : client,
513 : (const struct TALER_CRYPTO_EddsaSignRequest *) hdr);
514 3 : case TALER_HELPER_EDDSA_MT_REQ_REVOKE:
515 3 : if (msize != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest))
516 : {
517 0 : GNUNET_break_op (0);
518 0 : return GNUNET_SYSERR;
519 : }
520 3 : return handle_revoke_request (
521 : client,
522 : (const struct TALER_CRYPTO_EddsaRevokeRequest *) hdr);
523 0 : default:
524 0 : GNUNET_break_op (0);
525 0 : return GNUNET_SYSERR;
526 : }
527 : }
528 :
529 :
530 : /**
531 : * Send our initial key set to @a client together with the
532 : * "sync" terminator.
533 : *
534 : * @param client the client to inform
535 : * @return #GNUNET_OK on success
536 : */
537 : static enum GNUNET_GenericReturnValue
538 9 : eddsa_client_init (struct TES_Client *client)
539 : {
540 9 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
541 87 : for (struct Key *key = keys_head;
542 : NULL != key;
543 78 : key = key->next)
544 : {
545 78 : if (GNUNET_OK !=
546 78 : notify_client_key_add (client,
547 : key))
548 : {
549 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
550 0 : GNUNET_break (0);
551 0 : return GNUNET_SYSERR;
552 : }
553 : }
554 9 : client->key_gen = key_gen;
555 9 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
556 : {
557 9 : struct GNUNET_MessageHeader synced = {
558 9 : .type = htons (TALER_HELPER_EDDSA_SYNCED),
559 9 : .size = htons (sizeof (synced))
560 : };
561 :
562 9 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
563 : "Client %p synced\n",
564 : client);
565 9 : if (GNUNET_OK !=
566 9 : TES_transmit (client->csock,
567 : &synced))
568 : {
569 0 : GNUNET_break (0);
570 0 : return GNUNET_SYSERR;
571 : }
572 : }
573 9 : return GNUNET_OK;
574 : }
575 :
576 :
577 : /**
578 : * Notify @a client about all changes to the keys since
579 : * the last generation known to the @a client.
580 : *
581 : * @param client the client to notify
582 : * @return #GNUNET_OK on success
583 : */
584 : static enum GNUNET_GenericReturnValue
585 3 : eddsa_update_client_keys (struct TES_Client *client)
586 : {
587 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
588 : "Updating client %p to generation %llu\n",
589 : client,
590 : (unsigned long long) key_gen);
591 3 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
592 27 : for (struct Key *key = keys_head;
593 : NULL != key;
594 24 : key = key->next)
595 : {
596 24 : if (key->key_gen <= client->key_gen)
597 : {
598 18 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
599 : "Skipping key %s, no change since generation %llu\n",
600 : TALER_B2S (&key->exchange_pub),
601 : (unsigned long long) client->key_gen);
602 18 : continue;
603 : }
604 6 : if (key->purge)
605 : {
606 3 : if (GNUNET_OK !=
607 3 : notify_client_key_del (client,
608 : key))
609 : {
610 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
611 0 : return GNUNET_SYSERR;
612 : }
613 : }
614 : else
615 : {
616 3 : if (GNUNET_OK !=
617 3 : notify_client_key_add (client,
618 : key))
619 : {
620 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
621 0 : return GNUNET_SYSERR;
622 : }
623 : }
624 : }
625 3 : client->key_gen = key_gen;
626 3 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
627 3 : return GNUNET_OK;
628 : }
629 :
630 :
631 : /**
632 : * Create a new key (we do not have enough).
633 : *
634 : * @return #GNUNET_OK on success
635 : */
636 : static enum GNUNET_GenericReturnValue
637 6 : create_key (void)
638 : {
639 : struct Key *key;
640 : struct GNUNET_TIME_Timestamp anchor;
641 :
642 6 : anchor = GNUNET_TIME_timestamp_get ();
643 6 : if (NULL != keys_tail)
644 : {
645 : struct GNUNET_TIME_Absolute abs;
646 :
647 5 : abs = GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
648 : GNUNET_TIME_relative_subtract (
649 : duration,
650 : overlap_duration));
651 5 : if (GNUNET_TIME_absolute_cmp (anchor.abs_time,
652 : <,
653 : abs))
654 5 : anchor = GNUNET_TIME_absolute_to_timestamp (abs);
655 : }
656 6 : key = GNUNET_new (struct Key);
657 6 : key->anchor = anchor;
658 6 : if (GNUNET_OK !=
659 6 : setup_key (key,
660 : keys_tail))
661 : {
662 0 : GNUNET_break (0);
663 0 : GNUNET_free (key);
664 0 : GNUNET_SCHEDULER_shutdown ();
665 0 : global_ret = EXIT_FAILURE;
666 0 : return GNUNET_SYSERR;
667 : }
668 6 : return GNUNET_OK;
669 : }
670 :
671 :
672 : /**
673 : * At what time does the current key set require its next action? Basically,
674 : * the minimum of the expiration time of the oldest key, and the expiration
675 : * time of the newest key minus the #lookahead_sign time.
676 : */
677 : static struct GNUNET_TIME_Absolute
678 1 : key_action_time (void)
679 : {
680 : struct Key *nxt;
681 :
682 1 : nxt = keys_head;
683 1 : while ( (NULL != nxt) &&
684 1 : (nxt->purge) )
685 0 : nxt = nxt->next;
686 1 : if (NULL == nxt)
687 0 : return GNUNET_TIME_UNIT_ZERO_ABS;
688 1 : return GNUNET_TIME_absolute_min (
689 : GNUNET_TIME_absolute_add (nxt->anchor.abs_time,
690 : duration),
691 : GNUNET_TIME_absolute_subtract (
692 : GNUNET_TIME_absolute_subtract (
693 1 : GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
694 : duration),
695 : lookahead_sign),
696 : overlap_duration));
697 : }
698 :
699 :
700 : /**
701 : * Create new keys and expire ancient keys.
702 : *
703 : * @param cls NULL
704 : */
705 : static void
706 1 : update_keys (void *cls)
707 : {
708 1 : bool wake = false;
709 : struct Key *nxt;
710 :
711 : (void) cls;
712 1 : keygen_task = NULL;
713 1 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
714 : /* create new keys */
715 13 : while ( (NULL == keys_tail) ||
716 6 : GNUNET_TIME_absolute_is_past (
717 : GNUNET_TIME_absolute_subtract (
718 : GNUNET_TIME_absolute_subtract (
719 6 : GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
720 : duration),
721 : lookahead_sign),
722 : overlap_duration)) )
723 : {
724 6 : if (! wake)
725 : {
726 1 : key_gen++;
727 1 : wake = true;
728 : }
729 6 : if (GNUNET_OK !=
730 6 : create_key ())
731 : {
732 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
733 0 : GNUNET_break (0);
734 0 : global_ret = EXIT_FAILURE;
735 0 : GNUNET_SCHEDULER_shutdown ();
736 0 : return;
737 : }
738 : }
739 1 : nxt = keys_head;
740 : /* purge expired keys */
741 2 : while ( (NULL != nxt) &&
742 1 : GNUNET_TIME_absolute_is_past (
743 : GNUNET_TIME_absolute_add (nxt->anchor.abs_time,
744 : duration)))
745 : {
746 0 : if (! wake)
747 : {
748 0 : key_gen++;
749 0 : wake = true;
750 : }
751 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
752 : "Purging past key %s (expired %s ago)\n",
753 : TALER_B2S (&nxt->exchange_pub),
754 : GNUNET_TIME_relative2s (
755 : GNUNET_TIME_absolute_get_duration (
756 : GNUNET_TIME_absolute_add (nxt->anchor.abs_time,
757 : duration)),
758 : GNUNET_YES));
759 0 : purge_key (nxt);
760 0 : nxt = nxt->next;
761 : }
762 1 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
763 1 : if (wake)
764 1 : TES_wake_clients ();
765 1 : keygen_task = GNUNET_SCHEDULER_add_at (key_action_time (),
766 : &update_keys,
767 : NULL);
768 : }
769 :
770 :
771 : /**
772 : * Parse private key from @a filename in @a buf.
773 : *
774 : * @param filename name of the file we are parsing, for logging
775 : * @param buf key material
776 : * @param buf_size number of bytes in @a buf
777 : * @return #GNUNET_OK on success
778 : */
779 : static enum GNUNET_GenericReturnValue
780 0 : parse_key (const char *filename,
781 : const void *buf,
782 : size_t buf_size)
783 : {
784 : struct GNUNET_CRYPTO_EddsaPrivateKey priv;
785 : char *anchor_s;
786 : char dummy;
787 : unsigned long long anchor_ll;
788 : struct GNUNET_TIME_Timestamp anchor;
789 :
790 0 : anchor_s = strrchr (filename,
791 : '/');
792 0 : if (NULL == anchor_s)
793 : {
794 : /* File in a directory without '/' in the name, this makes no sense. */
795 0 : GNUNET_break (0);
796 0 : return GNUNET_SYSERR;
797 : }
798 0 : anchor_s++;
799 0 : if (1 != sscanf (anchor_s,
800 : "%llu%c",
801 : &anchor_ll,
802 : &dummy))
803 : {
804 : /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */
805 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
806 : "Filename `%s' invalid for key file, skipping\n",
807 : filename);
808 0 : return GNUNET_SYSERR;
809 : }
810 0 : anchor.abs_time.abs_value_us = anchor_ll
811 0 : * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
812 0 : if (anchor_ll != anchor.abs_time.abs_value_us
813 0 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
814 : {
815 : /* Integer overflow. Bad, invalid filename. */
816 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
817 : "Filename `%s' invalid for key file, skipping\n",
818 : filename);
819 0 : return GNUNET_SYSERR;
820 : }
821 0 : if (buf_size != sizeof (priv))
822 : {
823 : /* Parser failure. */
824 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
825 : "File `%s' is malformed, skipping\n",
826 : filename);
827 0 : return GNUNET_SYSERR;
828 : }
829 0 : memcpy (&priv,
830 : buf,
831 : buf_size);
832 :
833 : {
834 : struct GNUNET_CRYPTO_EddsaPublicKey pub;
835 : struct Key *key;
836 : struct Key *before;
837 :
838 0 : GNUNET_CRYPTO_eddsa_key_get_public (&priv,
839 : &pub);
840 0 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
841 0 : key = GNUNET_new (struct Key);
842 0 : key->exchange_priv.eddsa_priv = priv;
843 0 : key->exchange_pub.eddsa_pub = pub;
844 0 : key->anchor = anchor;
845 0 : key->filename = GNUNET_strdup (filename);
846 0 : key->key_gen = key_gen;
847 0 : before = NULL;
848 0 : for (struct Key *pos = keys_head;
849 : NULL != pos;
850 0 : pos = pos->next)
851 : {
852 0 : if (GNUNET_TIME_timestamp_cmp (pos->anchor, >, anchor))
853 0 : break;
854 0 : before = pos;
855 : }
856 0 : GNUNET_CONTAINER_DLL_insert_after (keys_head,
857 : keys_tail,
858 : before,
859 : key);
860 0 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
861 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
862 : "Imported key from `%s'\n",
863 : filename);
864 : }
865 0 : return GNUNET_OK;
866 : }
867 :
868 :
869 : /**
870 : * Import a private key from @a filename.
871 : *
872 : * @param cls NULL
873 : * @param filename name of a file in the directory
874 : */
875 : static enum GNUNET_GenericReturnValue
876 0 : import_key (void *cls,
877 : const char *filename)
878 : {
879 : struct GNUNET_DISK_FileHandle *fh;
880 : struct GNUNET_DISK_MapHandle *map;
881 : void *ptr;
882 : int fd;
883 : struct stat sbuf;
884 :
885 : (void) cls;
886 : {
887 : struct stat lsbuf;
888 :
889 0 : if (0 != lstat (filename,
890 : &lsbuf))
891 : {
892 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
893 : "lstat",
894 : filename);
895 0 : return GNUNET_OK;
896 : }
897 0 : if (! S_ISREG (lsbuf.st_mode))
898 : {
899 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
900 : "File `%s' is not a regular file, which is not allowed for private keys!\n",
901 : filename);
902 0 : return GNUNET_OK;
903 : }
904 : }
905 :
906 0 : fd = open (filename,
907 : O_RDONLY | O_CLOEXEC);
908 0 : if (-1 == fd)
909 : {
910 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
911 : "open",
912 : filename);
913 0 : return GNUNET_OK;
914 : }
915 0 : if (0 != fstat (fd,
916 : &sbuf))
917 : {
918 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
919 : "stat",
920 : filename);
921 0 : GNUNET_break (0 == close (fd));
922 0 : return GNUNET_OK;
923 : }
924 0 : if (! S_ISREG (sbuf.st_mode))
925 : {
926 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
927 : "File `%s' is not a regular file, which is not allowed for private keys!\n",
928 : filename);
929 0 : GNUNET_break (0 == close (fd));
930 0 : return GNUNET_OK;
931 : }
932 0 : if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
933 : {
934 : /* permission are NOT tight, try to patch them up! */
935 0 : if (0 !=
936 0 : fchmod (fd,
937 : S_IRUSR))
938 : {
939 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
940 : "fchmod",
941 : filename);
942 : /* refuse to use key if file has wrong permissions */
943 0 : GNUNET_break (0 == close (fd));
944 0 : return GNUNET_OK;
945 : }
946 : }
947 0 : fh = GNUNET_DISK_get_handle_from_int_fd (fd);
948 0 : if (NULL == fh)
949 : {
950 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
951 : "open",
952 : filename);
953 0 : GNUNET_break (0 == close (fd));
954 0 : return GNUNET_OK;
955 : }
956 0 : if (sbuf.st_size > 2048)
957 : {
958 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
959 : "File `%s' to big to be a private key\n",
960 : filename);
961 0 : GNUNET_DISK_file_close (fh);
962 0 : return GNUNET_OK;
963 : }
964 0 : ptr = GNUNET_DISK_file_map (fh,
965 : &map,
966 : GNUNET_DISK_MAP_TYPE_READ,
967 0 : (size_t) sbuf.st_size);
968 0 : if (NULL == ptr)
969 : {
970 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
971 : "mmap",
972 : filename);
973 0 : GNUNET_DISK_file_close (fh);
974 0 : return GNUNET_OK;
975 : }
976 0 : (void) parse_key (filename,
977 : ptr,
978 0 : (size_t) sbuf.st_size);
979 0 : GNUNET_DISK_file_unmap (map);
980 0 : GNUNET_DISK_file_close (fh);
981 0 : return GNUNET_OK;
982 : }
983 :
984 :
985 : /**
986 : * Load the various duration values from @a kcfg.
987 : *
988 : * @param cfg configuration to use
989 : * @return #GNUNET_OK on success
990 : */
991 : static enum GNUNET_GenericReturnValue
992 1 : load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
993 : {
994 1 : if (GNUNET_OK !=
995 1 : GNUNET_CONFIGURATION_get_value_time (cfg,
996 : "taler-exchange-secmod-eddsa",
997 : "OVERLAP_DURATION",
998 : &overlap_duration))
999 : {
1000 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1001 : "taler-exchange-secmod-eddsa",
1002 : "OVERLAP_DURATION");
1003 0 : return GNUNET_SYSERR;
1004 : }
1005 1 : if (GNUNET_OK !=
1006 1 : GNUNET_CONFIGURATION_get_value_time (cfg,
1007 : "taler-exchange-secmod-eddsa",
1008 : "DURATION",
1009 : &duration))
1010 : {
1011 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1012 : "taler-exchange-secmod-eddsa",
1013 : "DURATION");
1014 0 : return GNUNET_SYSERR;
1015 : }
1016 1 : if (GNUNET_OK !=
1017 1 : GNUNET_CONFIGURATION_get_value_time (cfg,
1018 : "taler-exchange-secmod-eddsa",
1019 : "LOOKAHEAD_SIGN",
1020 : &lookahead_sign))
1021 : {
1022 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1023 : "taler-exchange-secmod-eddsa",
1024 : "LOOKAHEAD_SIGN");
1025 0 : return GNUNET_SYSERR;
1026 : }
1027 1 : return GNUNET_OK;
1028 : }
1029 :
1030 :
1031 : /**
1032 : * Function run on shutdown. Stops the various jobs (nicely).
1033 : *
1034 : * @param cls NULL
1035 : */
1036 : static void
1037 1 : do_shutdown (void *cls)
1038 : {
1039 : (void) cls;
1040 1 : TES_listen_stop ();
1041 1 : if (NULL != keygen_task)
1042 : {
1043 1 : GNUNET_SCHEDULER_cancel (keygen_task);
1044 1 : keygen_task = NULL;
1045 : }
1046 1 : }
1047 :
1048 :
1049 : /**
1050 : * Main function that will be run under the GNUnet scheduler.
1051 : *
1052 : * @param cls closure
1053 : * @param args remaining command-line arguments
1054 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1055 : * @param cfg configuration
1056 : */
1057 : static void
1058 1 : run (void *cls,
1059 : char *const *args,
1060 : const char *cfgfile,
1061 : const struct GNUNET_CONFIGURATION_Handle *cfg)
1062 : {
1063 : static struct TES_Callbacks cb = {
1064 : .dispatch = eddsa_work_dispatch,
1065 : .updater = eddsa_update_client_keys,
1066 : .init = eddsa_client_init
1067 : };
1068 :
1069 : (void) cls;
1070 : (void) args;
1071 : (void) cfgfile;
1072 1 : if (GNUNET_TIME_timestamp_cmp (now, !=, now_tmp))
1073 : {
1074 : /* The user gave "--now", use it! */
1075 0 : now = now_tmp;
1076 : }
1077 : else
1078 : {
1079 : /* get current time again, we may be timetraveling! */
1080 1 : now = GNUNET_TIME_timestamp_get ();
1081 : }
1082 1 : if (GNUNET_OK !=
1083 1 : load_durations (cfg))
1084 : {
1085 0 : global_ret = EXIT_NOTCONFIGURED;
1086 0 : return;
1087 : }
1088 1 : if (GNUNET_OK !=
1089 1 : GNUNET_CONFIGURATION_get_value_filename (cfg,
1090 : "taler-exchange-secmod-eddsa",
1091 : "KEY_DIR",
1092 : &keydir))
1093 : {
1094 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1095 : "taler-exchange-secmod-eddsa",
1096 : "KEY_DIR");
1097 0 : global_ret = EXIT_NOTCONFIGURED;
1098 0 : return;
1099 : }
1100 1 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1101 : NULL);
1102 1 : global_ret = TES_listen_start (cfg,
1103 : "taler-exchange-secmod-eddsa",
1104 : &cb);
1105 1 : if (0 != global_ret)
1106 0 : return;
1107 : /* Load keys */
1108 1 : GNUNET_break (GNUNET_OK ==
1109 : GNUNET_DISK_directory_create (keydir));
1110 1 : GNUNET_DISK_directory_scan (keydir,
1111 : &import_key,
1112 : NULL);
1113 1 : if ( (NULL != keys_head) &&
1114 0 : (GNUNET_TIME_absolute_is_future (keys_head->anchor.abs_time)) )
1115 : {
1116 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1117 : "Existing anchor is in %s the future. Refusing to start\n",
1118 : GNUNET_TIME_relative2s (
1119 : GNUNET_TIME_absolute_get_remaining (
1120 : keys_head->anchor.abs_time),
1121 : GNUNET_YES));
1122 0 : global_ret = EXIT_FAILURE;
1123 0 : GNUNET_SCHEDULER_shutdown ();
1124 0 : return;
1125 : }
1126 : /* start job to keep keys up-to-date; MUST be run before the #listen_task,
1127 : hence with priority. */
1128 1 : keygen_task = GNUNET_SCHEDULER_add_with_priority (
1129 : GNUNET_SCHEDULER_PRIORITY_URGENT,
1130 : &update_keys,
1131 : NULL);
1132 : }
1133 :
1134 :
1135 : /**
1136 : * The entry point.
1137 : *
1138 : * @param argc number of arguments in @a argv
1139 : * @param argv command-line arguments
1140 : * @return 0 on normal termination
1141 : */
1142 : int
1143 1 : main (int argc,
1144 : char **argv)
1145 : {
1146 1 : struct GNUNET_GETOPT_CommandLineOption options[] = {
1147 1 : GNUNET_GETOPT_option_timetravel ('T',
1148 : "timetravel"),
1149 1 : GNUNET_GETOPT_option_timestamp ('t',
1150 : "time",
1151 : "TIMESTAMP",
1152 : "pretend it is a different time for the update",
1153 : &now_tmp),
1154 : GNUNET_GETOPT_OPTION_END
1155 : };
1156 : enum GNUNET_GenericReturnValue ret;
1157 :
1158 : /* Restrict permissions for the key files that we create. */
1159 1 : (void) umask (S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH);
1160 :
1161 : /* force linker to link against libtalerutil; if we do
1162 : not do this, the linker may "optimize" libtalerutil
1163 : away and skip #TALER_OS_init(), which we do need */
1164 1 : TALER_OS_init ();
1165 1 : now_tmp = now = GNUNET_TIME_timestamp_get ();
1166 1 : ret = GNUNET_PROGRAM_run (argc,
1167 : argv,
1168 : "taler-exchange-secmod-eddsa",
1169 : "Handle private EDDSA key operations for a Taler exchange",
1170 : options,
1171 : &run,
1172 : NULL);
1173 1 : if (GNUNET_NO == ret)
1174 0 : return EXIT_SUCCESS;
1175 1 : if (GNUNET_SYSERR == ret)
1176 0 : return EXIT_INVALIDARGUMENT;
1177 1 : return global_ret;
1178 : }
|