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 102 : notify_client_key_add (struct TES_Client *client,
164 : const struct Key *key)
165 : {
166 204 : struct TALER_CRYPTO_EddsaKeyAvailableNotification an = {
167 102 : .header.size = htons (sizeof (an)),
168 102 : .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL),
169 102 : .anchor_time = GNUNET_TIME_timestamp_hton (key->anchor),
170 102 : .duration = GNUNET_TIME_relative_hton (duration),
171 : .exchange_pub = key->exchange_pub,
172 : .secm_pub = TES_smpub
173 : };
174 :
175 102 : TALER_exchange_secmod_eddsa_sign (&key->exchange_pub,
176 : key->anchor,
177 : duration,
178 : &TES_smpriv,
179 : &an.secm_sig);
180 102 : if (GNUNET_OK !=
181 102 : 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 102 : 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 1148 : handle_sign_request (struct TES_Client *client,
234 : const struct TALER_CRYPTO_EddsaSignRequest *sr)
235 : {
236 1148 : const struct GNUNET_CRYPTO_SignaturePurpose *purpose = &sr->purpose;
237 1148 : size_t purpose_size = ntohs (sr->header.size) - sizeof (*sr)
238 : + sizeof (*purpose);
239 : struct Key *key;
240 1148 : struct TALER_CRYPTO_EddsaSignResponse sres = {
241 1148 : .header.size = htons (sizeof (sres)),
242 1148 : .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE)
243 : };
244 : enum TALER_ErrorCode ec;
245 :
246 1148 : 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 1148 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
261 1148 : key = keys_head;
262 2296 : while ( (NULL != key) &&
263 1148 : (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 2051 : while ( (NULL != key) &&
294 2051 : (key->purge) )
295 903 : key = key->next;
296 1148 : if (NULL == key)
297 : {
298 0 : GNUNET_break (0);
299 0 : ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
300 : }
301 : else
302 : {
303 1148 : GNUNET_assert (key->rc < UINT_MAX);
304 1148 : key->rc++;
305 1148 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
306 :
307 1147 : if (GNUNET_OK !=
308 1148 : 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 1147 : ec = TALER_EC_NONE;
314 1147 : sres.exchange_pub = key->exchange_pub;
315 1147 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
316 1148 : GNUNET_assert (key->rc > 0);
317 1148 : key->rc--;
318 : }
319 1148 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
320 1146 : 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 1146 : 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 30 : setup_key (struct Key *key,
348 : struct Key *position)
349 : {
350 : struct GNUNET_CRYPTO_EddsaPrivateKey priv;
351 : struct GNUNET_CRYPTO_EddsaPublicKey pub;
352 :
353 30 : GNUNET_CRYPTO_eddsa_key_create (&priv);
354 30 : GNUNET_CRYPTO_eddsa_key_get_public (&priv,
355 : &pub);
356 30 : GNUNET_asprintf (&key->filename,
357 : "%s/%llu",
358 : keydir,
359 30 : (unsigned long long) (key->anchor.abs_time.abs_value_us
360 30 : / GNUNET_TIME_UNIT_SECONDS.rel_value_us
361 : ));
362 30 : if (GNUNET_OK !=
363 30 : 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 30 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
374 : "Setup fresh private key in `%s'\n",
375 : key->filename);
376 30 : key->key_gen = key_gen;
377 30 : key->exchange_priv.eddsa_priv = priv;
378 30 : key->exchange_pub.eddsa_pub = pub;
379 30 : GNUNET_CONTAINER_DLL_insert_after (keys_head,
380 : keys_tail,
381 : position,
382 : key);
383 30 : 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 3 : purge_key (struct Key *key)
394 : {
395 3 : 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 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
403 : "Purging key %s\n",
404 : TALER_B2S (&key->exchange_pub));
405 3 : if (0 != unlink (key->filename))
406 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
407 : "unlink",
408 : key->filename);
409 3 : key->purge = true;
410 3 : key->key_gen = key_gen;
411 3 : 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 3 : 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 3 : key = NULL;
433 3 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
434 3 : for (struct Key *pos = keys_head;
435 11 : NULL != pos;
436 8 : pos = pos->next)
437 11 : if (0 == GNUNET_memcmp (&pos->exchange_pub,
438 : &rr->exchange_pub))
439 : {
440 3 : key = pos;
441 3 : break;
442 : }
443 3 : 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 3 : 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 3 : key_gen++;
459 3 : 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 3 : purge_key (key);
464 :
465 : /* Setup replacement key */
466 3 : nkey = GNUNET_new (struct Key);
467 3 : nkey->anchor = key->anchor;
468 3 : if (GNUNET_OK !=
469 3 : 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 3 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
479 3 : TES_wake_clients ();
480 3 : 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 1151 : eddsa_work_dispatch (struct TES_Client *client,
493 : const struct GNUNET_MessageHeader *hdr)
494 : {
495 1151 : uint16_t msize = ntohs (hdr->size);
496 :
497 1151 : switch (ntohs (hdr->type))
498 : {
499 1148 : case TALER_HELPER_EDDSA_MT_REQ_SIGN:
500 1148 : if (msize < sizeof (struct TALER_CRYPTO_EddsaSignRequest))
501 : {
502 0 : GNUNET_break_op (0);
503 0 : return GNUNET_SYSERR;
504 : }
505 1148 : return handle_sign_request (
506 : client,
507 : (const struct TALER_CRYPTO_EddsaSignRequest *) hdr);
508 3 : case TALER_HELPER_EDDSA_MT_REQ_REVOKE:
509 3 : if (msize != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest))
510 : {
511 0 : GNUNET_break_op (0);
512 0 : return GNUNET_SYSERR;
513 : }
514 3 : 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 26 : eddsa_client_init (struct TES_Client *client)
533 : {
534 26 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
535 26 : for (struct Key *key = keys_head;
536 125 : NULL != key;
537 99 : key = key->next)
538 : {
539 99 : if (GNUNET_OK !=
540 99 : 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 26 : client->key_gen = key_gen;
549 26 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
550 : {
551 26 : struct GNUNET_MessageHeader synced = {
552 26 : .type = htons (TALER_HELPER_EDDSA_SYNCED),
553 26 : .size = htons (sizeof (synced))
554 : };
555 :
556 26 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
557 : "Client %p synced\n",
558 : client);
559 26 : if (GNUNET_OK !=
560 26 : TES_transmit (client->csock,
561 : &synced))
562 : {
563 0 : GNUNET_break (0);
564 0 : return GNUNET_SYSERR;
565 : }
566 : }
567 26 : 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 20 : eddsa_update_client_keys (struct TES_Client *client)
580 : {
581 20 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
582 20 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
583 : "Updating client %p to generation %llu\n",
584 : client,
585 : (unsigned long long) key_gen);
586 20 : for (struct Key *key = keys_head;
587 65 : NULL != key;
588 45 : key = key->next)
589 : {
590 45 : if (key->key_gen <= client->key_gen)
591 : {
592 39 : 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 39 : continue;
597 : }
598 6 : if (key->purge)
599 : {
600 3 : if (GNUNET_OK !=
601 3 : 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 3 : if (GNUNET_OK !=
611 3 : 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 20 : client->key_gen = key_gen;
620 20 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
621 20 : 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 27 : create_key (void)
632 : {
633 : struct Key *key;
634 : struct GNUNET_TIME_Timestamp anchor;
635 :
636 27 : anchor = GNUNET_TIME_timestamp_get ();
637 27 : if (NULL != keys_tail)
638 : {
639 : struct GNUNET_TIME_Absolute abs;
640 :
641 9 : abs = GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
642 : GNUNET_TIME_relative_subtract (
643 : duration,
644 : overlap_duration));
645 9 : if (GNUNET_TIME_absolute_cmp (anchor.abs_time,
646 : <,
647 : abs))
648 9 : anchor = GNUNET_TIME_absolute_to_timestamp (abs);
649 : }
650 27 : key = GNUNET_new (struct Key);
651 27 : key->anchor = anchor;
652 27 : if (GNUNET_OK !=
653 27 : 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 27 : 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 18 : key_action_time (void)
675 : {
676 : struct Key *nxt;
677 :
678 18 : nxt = keys_head;
679 18 : while ( (NULL != nxt) &&
680 18 : (nxt->purge) )
681 0 : nxt = nxt->next;
682 18 : if (NULL == nxt)
683 0 : return GNUNET_TIME_UNIT_ZERO_ABS;
684 18 : 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 18 : 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 18 : update_keys (void *cls)
703 : {
704 18 : bool wake = false;
705 : struct Key *nxt;
706 : struct GNUNET_TIME_Absolute at;
707 :
708 : (void) cls;
709 18 : keygen_task = NULL;
710 18 : GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
711 : /* create new keys */
712 72 : while ( (NULL == keys_tail) ||
713 27 : GNUNET_TIME_absolute_is_past (
714 : GNUNET_TIME_absolute_subtract (
715 : GNUNET_TIME_absolute_subtract (
716 27 : GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
717 : duration),
718 : lookahead_sign),
719 : overlap_duration)) )
720 : {
721 27 : if (! wake)
722 : {
723 18 : key_gen++;
724 18 : wake = true;
725 : }
726 27 : if (GNUNET_OK !=
727 27 : 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 18 : nxt = keys_head;
737 : /* purge expired keys */
738 36 : while ( (NULL != nxt) &&
739 18 : 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 18 : at = key_action_time ();
760 18 : GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
761 18 : if (wake)
762 18 : TES_wake_clients ();
763 18 : 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 : {
885 : struct stat lsbuf;
886 :
887 0 : if (0 != lstat (filename,
888 : &lsbuf))
889 : {
890 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
891 : "lstat",
892 : filename);
893 0 : return GNUNET_OK;
894 : }
895 0 : if (! S_ISREG (lsbuf.st_mode))
896 : {
897 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
898 : "File `%s' is not a regular file, which is not allowed for private keys!\n",
899 : filename);
900 0 : return GNUNET_OK;
901 : }
902 : }
903 :
904 0 : fd = open (filename,
905 : O_RDONLY | O_CLOEXEC);
906 0 : if (-1 == fd)
907 : {
908 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
909 : "open",
910 : filename);
911 0 : return GNUNET_OK;
912 : }
913 0 : if (0 != fstat (fd,
914 : &sbuf))
915 : {
916 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
917 : "stat",
918 : filename);
919 0 : GNUNET_break (0 == close (fd));
920 0 : return GNUNET_OK;
921 : }
922 0 : if (! S_ISREG (sbuf.st_mode))
923 : {
924 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
925 : "File `%s' is not a regular file, which is not allowed for private keys!\n",
926 : filename);
927 0 : GNUNET_break (0 == close (fd));
928 0 : return GNUNET_OK;
929 : }
930 0 : if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
931 : {
932 : /* permission are NOT tight, try to patch them up! */
933 0 : if (0 !=
934 0 : fchmod (fd,
935 : S_IRUSR))
936 : {
937 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
938 : "fchmod",
939 : filename);
940 : /* refuse to use key if file has wrong permissions */
941 0 : GNUNET_break (0 == close (fd));
942 0 : return GNUNET_OK;
943 : }
944 : }
945 0 : fh = GNUNET_DISK_get_handle_from_int_fd (fd);
946 0 : if (NULL == fh)
947 : {
948 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
949 : "open",
950 : filename);
951 0 : GNUNET_break (0 == close (fd));
952 0 : return GNUNET_OK;
953 : }
954 0 : if (sbuf.st_size > 2048)
955 : {
956 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
957 : "File `%s' to big to be a private key\n",
958 : filename);
959 0 : GNUNET_DISK_file_close (fh);
960 0 : return GNUNET_OK;
961 : }
962 0 : ptr = GNUNET_DISK_file_map (fh,
963 : &map,
964 : GNUNET_DISK_MAP_TYPE_READ,
965 0 : (size_t) sbuf.st_size);
966 0 : if (NULL == ptr)
967 : {
968 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
969 : "mmap",
970 : filename);
971 0 : GNUNET_DISK_file_close (fh);
972 0 : return GNUNET_OK;
973 : }
974 0 : (void) parse_key (filename,
975 : ptr,
976 0 : (size_t) sbuf.st_size);
977 0 : GNUNET_DISK_file_unmap (map);
978 0 : GNUNET_DISK_file_close (fh);
979 0 : return GNUNET_OK;
980 : }
981 :
982 :
983 : /**
984 : * Load the various duration values from @a kcfg.
985 : *
986 : * @param cfg configuration to use
987 : * @return #GNUNET_OK on success
988 : */
989 : static enum GNUNET_GenericReturnValue
990 18 : load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
991 : {
992 : char *secname;
993 :
994 18 : GNUNET_asprintf (&secname,
995 : "%s-secmod-eddsa",
996 18 : globals->section);
997 18 : if (GNUNET_OK !=
998 18 : GNUNET_CONFIGURATION_get_value_time (cfg,
999 : secname,
1000 : "OVERLAP_DURATION",
1001 : &overlap_duration))
1002 : {
1003 0 : GNUNET_free (secname);
1004 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1005 : secname,
1006 : "OVERLAP_DURATION");
1007 0 : return GNUNET_SYSERR;
1008 : }
1009 18 : if (GNUNET_OK !=
1010 18 : GNUNET_CONFIGURATION_get_value_time (cfg,
1011 : secname,
1012 : "DURATION",
1013 : &duration))
1014 : {
1015 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1016 : secname,
1017 : "DURATION");
1018 0 : GNUNET_free (secname);
1019 0 : return GNUNET_SYSERR;
1020 : }
1021 18 : if (GNUNET_OK !=
1022 18 : GNUNET_CONFIGURATION_get_value_time (cfg,
1023 : secname,
1024 : "LOOKAHEAD_SIGN",
1025 : &lookahead_sign))
1026 : {
1027 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1028 : secname,
1029 : "LOOKAHEAD_SIGN");
1030 0 : GNUNET_free (secname);
1031 0 : return GNUNET_SYSERR;
1032 : }
1033 18 : GNUNET_free (secname);
1034 18 : return GNUNET_OK;
1035 : }
1036 :
1037 :
1038 : /**
1039 : * Function run on shutdown. Stops the various jobs (nicely).
1040 : *
1041 : * @param cls NULL
1042 : */
1043 : static void
1044 18 : do_shutdown (void *cls)
1045 : {
1046 : (void) cls;
1047 18 : TES_listen_stop ();
1048 18 : if (NULL != keygen_task)
1049 : {
1050 18 : GNUNET_SCHEDULER_cancel (keygen_task);
1051 18 : keygen_task = NULL;
1052 : }
1053 18 : }
1054 :
1055 :
1056 : void
1057 18 : TALER_SECMOD_eddsa_run (void *cls,
1058 : char *const *args,
1059 : const char *cfgfile,
1060 : const struct GNUNET_CONFIGURATION_Handle *cfg)
1061 : {
1062 : static struct TES_Callbacks cb = {
1063 : .dispatch = eddsa_work_dispatch,
1064 : .updater = eddsa_update_client_keys,
1065 : .init = eddsa_client_init
1066 : };
1067 18 : struct TALER_SECMOD_Options *opt = cls;
1068 : char *secname;
1069 :
1070 : (void) args;
1071 : (void) cfgfile;
1072 18 : globals = opt;
1073 18 : if (GNUNET_TIME_timestamp_cmp (opt->global_now,
1074 : !=,
1075 : opt->global_now_tmp))
1076 : {
1077 : /* The user gave "--now", use it! */
1078 0 : opt->global_now = opt->global_now_tmp;
1079 : }
1080 : else
1081 : {
1082 : /* get current time again, we may be timetraveling! */
1083 18 : opt->global_now = GNUNET_TIME_timestamp_get ();
1084 : }
1085 18 : if (GNUNET_OK !=
1086 18 : load_durations (cfg))
1087 : {
1088 0 : opt->global_ret = EXIT_NOTCONFIGURED;
1089 0 : return;
1090 : }
1091 18 : GNUNET_asprintf (&secname,
1092 : "%s-secmod-eddsa",
1093 : opt->section);
1094 18 : if (GNUNET_OK !=
1095 18 : GNUNET_CONFIGURATION_get_value_filename (cfg,
1096 : secname,
1097 : "KEY_DIR",
1098 : &keydir))
1099 : {
1100 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1101 : secname,
1102 : "KEY_DIR");
1103 0 : GNUNET_free (secname);
1104 0 : opt->global_ret = EXIT_NOTCONFIGURED;
1105 0 : return;
1106 : }
1107 18 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1108 : NULL);
1109 18 : opt->global_ret = TES_listen_start (cfg,
1110 : secname,
1111 : &cb);
1112 18 : GNUNET_free (secname);
1113 18 : if (0 != opt->global_ret)
1114 0 : return;
1115 : /* Load keys */
1116 18 : GNUNET_break (GNUNET_OK ==
1117 : GNUNET_DISK_directory_create (keydir));
1118 18 : GNUNET_DISK_directory_scan (keydir,
1119 : &import_key,
1120 : NULL);
1121 18 : if ( (NULL != keys_head) &&
1122 0 : (GNUNET_TIME_absolute_is_future (keys_head->anchor.abs_time)) )
1123 : {
1124 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1125 : "Existing anchor is in %s the future. Refusing to start\n",
1126 : GNUNET_TIME_relative2s (
1127 : GNUNET_TIME_absolute_get_remaining (
1128 : keys_head->anchor.abs_time),
1129 : true));
1130 0 : opt->global_ret = EXIT_FAILURE;
1131 0 : GNUNET_SCHEDULER_shutdown ();
1132 0 : return;
1133 : }
1134 : /* start job to keep keys up-to-date; MUST be run before the #listen_task,
1135 : hence with priority. */
1136 18 : keygen_task = GNUNET_SCHEDULER_add_with_priority (
1137 : GNUNET_SCHEDULER_PRIORITY_URGENT,
1138 : &update_keys,
1139 : NULL);
1140 : }
|