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