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