From 6e86a3c43cd6b16115134dfe617b091f8dbcd89d Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 17 Nov 2021 13:03:47 +0100 Subject: -sms wip --- src/util/taler-exchange-secmod-eddsa.c | 1288 +++++++++----------------------- 1 file changed, 354 insertions(+), 934 deletions(-) (limited to 'src/util/taler-exchange-secmod-eddsa.c') diff --git a/src/util/taler-exchange-secmod-eddsa.c b/src/util/taler-exchange-secmod-eddsa.c index dc2d35d13..3b7ee3ded 100644 --- a/src/util/taler-exchange-secmod-eddsa.c +++ b/src/util/taler-exchange-secmod-eddsa.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2020 Taler Systems SA + Copyright (C) 2014-2021 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -26,7 +26,7 @@ * and merged with the public keys of the helper by the exchange HTTPD! * - the main loop of the helper is SINGLE-THREADED, but there are * threads for crypto-workers which (only) do the signing in parallel, - * working of a work-queue. + * one per client. * - thread-safety: signing happens in parallel, thus when REMOVING private keys, * we must ensure that all signers are done before we fully free() the * private key. This is done by reference counting (as work is always @@ -41,6 +41,7 @@ #include "taler_error_codes.h" #include "taler_signatures.h" #include "secmod_common.h" +#include /** @@ -79,6 +80,11 @@ struct Key */ struct GNUNET_TIME_Absolute anchor; + /** + * Generation when this key was created or revoked. + */ + uint64_t key_gen; + /** * Reference counter. Counts the number of threads that are * using this key at this time. @@ -94,92 +100,6 @@ struct Key }; -/** - * Information we keep for a client connected to us. - */ -struct Client -{ - - /** - * Kept in a DLL. - */ - struct Client *next; - - /** - * Kept in a DLL. - */ - struct Client *prev; - - /** - * Client address. - */ - struct sockaddr_un addr; - - /** - * Number of bytes used in @e addr. - */ - socklen_t addr_size; - -}; - - -struct WorkItem -{ - - /** - * Kept in a DLL. - */ - struct WorkItem *next; - - /** - * Kept in a DLL. - */ - struct WorkItem *prev; - - /** - * Key to be used for this operation. - */ - struct Key *key; - - /** - * EDDSA signature over @e msg using @e key. Result of doing the work. - */ - struct TALER_ExchangeSignatureP signature; - - /** - * Message to sign. - */ - struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; - - /** - * Client address. - */ - struct sockaddr_un addr; - - /** - * Number of bytes used in @e addr. - */ - socklen_t addr_size; - - /** - * Operation status code. - */ - enum TALER_ErrorCode ec; - -}; - - -/** - * Private key of this security module. Used to sign denomination key - * announcements. - */ -static struct TALER_SecurityModulePrivateKeyP smpriv; - -/** - * Public key of this security module. - */ -static struct TALER_SecurityModulePublicKeyP smpub; - /** * Head of DLL of actual keys, sorted by anchor. */ @@ -200,13 +120,6 @@ static struct GNUNET_TIME_Relative duration; */ static int global_ret; -/** - * Number of worker threads to use. Default (0) is to use one per CPU core - * available. - * Length of the #workers array. - */ -static unsigned int num_workers; - /** * Time when the key update is executed. * Either the actual current time, or a pretended time. @@ -219,11 +132,6 @@ static struct GNUNET_TIME_Absolute now; */ static struct GNUNET_TIME_Absolute now_tmp; -/** - * Handle to the exchange's configuration - */ -static const struct GNUNET_CONFIGURATION_Handle *kcfg; - /** * Where do we store the keys? */ @@ -241,324 +149,87 @@ static struct GNUNET_TIME_Relative overlap_duration; */ static struct GNUNET_TIME_Relative lookahead_sign; -/** - * Our listen socket. - */ -static struct GNUNET_NETWORK_Handle *unix_sock; - -/** - * Path where we are listening. - */ -static char *unixpath; - -/** - * Task run to accept new inbound connections. - */ -static struct GNUNET_SCHEDULER_Task *read_task; - /** * Task run to generate new keys. */ static struct GNUNET_SCHEDULER_Task *keygen_task; /** - * Head of DLL of clients connected to us. - */ -static struct Client *clients_head; - -/** - * Tail of DLL of clients connected to us. - */ -static struct Client *clients_tail; - -/** - * Head of DLL with pending signing operations. - */ -static struct WorkItem *work_head; - -/** - * Tail of DLL with pending signing operations. - */ -static struct WorkItem *work_tail; - -/** - * Lock for the work queue. - */ -static pthread_mutex_t work_lock; - -/** - * Condition variable for the semaphore of the work queue. - */ -static pthread_cond_t work_cond = PTHREAD_COND_INITIALIZER; - -/** - * Number of items in the work queue. Also used as the semaphore counter. - */ -static unsigned long long work_counter; - -/** - * Head of DLL with completed signing operations. - */ -static struct WorkItem *done_head; - -/** - * Tail of DLL with completed signing operations. - */ -static struct WorkItem *done_tail; - -/** - * Lock for the done queue. - */ -static pthread_mutex_t done_lock; - -/** - * Task waiting for work to be done. - */ -static struct GNUNET_SCHEDULER_Task *done_task; - -/** - * Signal used by threads to notify the #done_task that they - * completed work that is now in the done queue. + * Lock for the keys queue. */ -static struct GNUNET_NETWORK_Handle *done_signal; +static pthread_mutex_t keys_lock; /** - * Set once we are in shutdown and workers should terminate. + * Current key generation. */ -static volatile bool in_shutdown; - -/** - * Array of #num_workers sign_worker() threads. - */ -static pthread_t *workers; - - -/** - * Main function of a worker thread that signs. - * - * @param cls NULL - * @return NULL - */ -static void * -sign_worker (void *cls) -{ - (void) cls; - GNUNET_assert (0 == pthread_mutex_lock (&work_lock)); - while (! in_shutdown) - { - struct WorkItem *wi; - - while (NULL != (wi = work_head)) - { - /* take work from queue */ - GNUNET_CONTAINER_DLL_remove (work_head, - work_tail, - wi); - work_counter--; - GNUNET_assert (0 == pthread_mutex_unlock (&work_lock)); - { - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_sign_ (&wi->key->exchange_priv.eddsa_priv, - wi->purpose, - &wi->signature.eddsa_signature)) - wi->ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - else - wi->ec = TALER_EC_NONE; - } - /* put completed work into done queue */ - GNUNET_assert (0 == pthread_mutex_lock (&done_lock)); - GNUNET_CONTAINER_DLL_insert (done_head, - done_tail, - wi); - GNUNET_assert (0 == pthread_mutex_unlock (&done_lock)); - { - uint64_t val = GNUNET_htonll (1); - - /* raise #done_signal */ - if (sizeof(val) != - write (GNUNET_NETWORK_get_fd (done_signal), - &val, - sizeof (val))) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "write(eventfd)"); - } - GNUNET_assert (0 == pthread_mutex_lock (&work_lock)); - } - if (in_shutdown) - break; - /* queue is empty, wait for work */ - GNUNET_assert (0 == - pthread_cond_wait (&work_cond, - &work_lock)); - } - GNUNET_assert (0 == - pthread_mutex_unlock (&work_lock)); - return NULL; -} +static uint64_t key_gen; /** - * Free @a client, releasing all (remaining) state. - * - * @param[in] client data to free - */ -static void -free_client (struct Client *client) -{ - GNUNET_CONTAINER_DLL_remove (clients_head, - clients_tail, - client); - GNUNET_free (client); -} - - -/** - * Function run to read incoming requests from a client. - * - * @param cls the `struct Client` - */ -static void -read_job (void *cls); - - -/** - * Free @a key. It must already have been removed from the DLL. - * - * @param[in] key the key to free - */ -static void -free_key (struct Key *key) -{ - GNUNET_free (key->filename); - GNUNET_free (key); -} - - -/** - * Send a message starting with @a hdr to @a client. We expect that - * the client is mostly able to handle everything at whatever speed - * we have (after all, the crypto should be the slow part). However, - * especially on startup when we send all of our keys, it is possible - * that the client cannot keep up. In that case, we throttle when - * sending fails. This does not work with poll() as we cannot specify - * the sendto() target address with poll(). So we nanosleep() instead. + * Notify @a client about @a key becoming available. * - * @param addr address where to send the message - * @param addr_size number of bytes in @a addr - * @param hdr beginning of the message, length indicated in size field + * @param[in,out] client the client to notify; possible freed if transmission fails + * @param key the key to notify @a client about * @return #GNUNET_OK on success */ -static int -transmit (const struct sockaddr_un *addr, - socklen_t addr_size, - const struct GNUNET_MessageHeader *hdr) +static enum GNUNET_GenericReturnValue +notify_client_key_add (struct TES_Client *client, + const struct Key *key) { - for (unsigned int i = 0; i<100; i++) + struct TALER_CRYPTO_EddsaKeyAvailableNotification an = { + .header.size = htons (sizeof (an)), + .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL), + .anchor_time = GNUNET_TIME_absolute_hton (key->anchor), + .duration = GNUNET_TIME_relative_hton (duration), + .exchange_pub = key->exchange_pub, + .secm_pub = TES_smpub + }; + + TALER_exchange_secmod_eddsa_sign (&key->exchange_pub, + key->anchor, + duration, + &TES_smpriv, + &an.secm_sig); + if (GNUNET_OK != + TES_transmit (client->csock, + &an.header)) { - ssize_t ret = sendto (GNUNET_NETWORK_get_fd (unix_sock), - hdr, - ntohs (hdr->size), - 0 /* no flags => blocking! */, - (const struct sockaddr *) addr, - addr_size); - if ( (-1 == ret) && - (EAGAIN == errno) ) - { - /* _Maybe_ with blocking sendto(), this should no - longer be needed; still keeping it just in case. */ - /* Wait a bit, in case client is just too slow */ - struct timespec req = { - .tv_sec = 0, - .tv_nsec = 1000 - }; - nanosleep (&req, NULL); - continue; - } - if (ret == ntohs (hdr->size)) - return GNUNET_OK; - if (ret != ntohs (hdr->size)) - break; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Client %p must have disconnected\n", + client); + return GNUNET_SYSERR; } - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "sendto"); - return GNUNET_SYSERR; + return GNUNET_OK; } /** - * Process completed tasks that are in the #done_head queue, sending - * the result back to the client (and resuming the client). + * Notify @a client about @a key being purged. * - * @param cls NULL + * @param[in,out] client the client to notify; possible freed if transmission fails + * @param key the key to notify @a client about + * @return #GNUNET_OK on success */ -static void -handle_done (void *cls) +static enum GNUNET_GenericReturnValue +notify_client_key_del (struct TES_Client *client, + const struct Key *key) { - uint64_t data; - (void) cls; + struct TALER_CRYPTO_EddsaKeyPurgeNotification pn = { + .header.type = htons (TALER_HELPER_EDDSA_MT_PURGE), + .header.size = htons (sizeof (pn)), + .exchange_pub = key->exchange_pub + }; - /* consume #done_signal */ - if (sizeof (data) != - read (GNUNET_NETWORK_get_fd (done_signal), - &data, - sizeof (data))) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "read(eventfd)"); - done_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - done_signal, - &handle_done, - NULL); - GNUNET_assert (0 == pthread_mutex_lock (&done_lock)); - while (NULL != done_head) + if (GNUNET_OK != + TES_transmit (client->csock, + &pn.header)) { - struct WorkItem *wi = done_head; - - GNUNET_CONTAINER_DLL_remove (done_head, - done_tail, - wi); - GNUNET_assert (0 == pthread_mutex_unlock (&done_lock)); - if (TALER_EC_NONE != wi->ec) - { - struct TALER_CRYPTO_EddsaSignFailure sf = { - .header.size = htons (sizeof (sf)), - .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE), - .ec = htonl (wi->ec) - }; - - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Signing request %p failed, worker failed to produce signature\n", - wi); - (void) transmit (&wi->addr, - wi->addr_size, - &sf.header); - } - else - { - struct TALER_CRYPTO_EddsaSignResponse sr = { - .header.size = htons (sizeof (sr)), - .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE), - .exchange_pub = wi->key->exchange_pub, - .exchange_sig = wi->signature - }; - - (void) transmit (&wi->addr, - wi->addr_size, - &sr.header); - } - { - struct Key *key = wi->key; - - key->rc--; - if ( (0 == key->rc) && - (key->purge) ) - free_key (key); - } - GNUNET_free (wi->purpose); - GNUNET_free (wi); - GNUNET_assert (0 == pthread_mutex_lock (&done_lock)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Client %p must have disconnected\n", + client); + return GNUNET_SYSERR; } - GNUNET_assert (0 == pthread_mutex_unlock (&done_lock)); - + return GNUNET_OK; } @@ -567,19 +238,23 @@ handle_done (void *cls) * signature using the respective key and return the result to * the client. * - * @param addr address of the client making the request - * @param addr_size number of bytes in @a addr + * @param client the client making the request * @param sr the request details + * @return #GNUNET_OK on success */ -static void -handle_sign_request (const struct sockaddr_un *addr, - socklen_t addr_size, +static enum GNUNET_GenericReturnValue +handle_sign_request (struct TES_Client *client, const struct TALER_CRYPTO_EddsaSignRequest *sr) { const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose = &sr->purpose; - struct WorkItem *wi; size_t purpose_size = ntohs (sr->header.size) - sizeof (*sr) + sizeof (*purpose); + struct Key *key; + struct TALER_CRYPTO_EddsaSignResponse sres = { + .header.size = htons (sizeof (sres)), + .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE) + }; + enum TALER_ErrorCode ec; if (purpose_size != htonl (purpose->size)) { @@ -591,122 +266,73 @@ handle_sign_request (const struct sockaddr_un *addr, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Signing request failed, request malformed\n"); - (void) transmit (addr, - addr_size, - &sf.header); - return; + return TES_transmit (client->csock, + &sf.header); } - { - struct GNUNET_TIME_Absolute now; - now = GNUNET_TIME_absolute_get (); - if ( (now.abs_value_us >= keys_head->anchor.abs_value_us) && - (now.abs_value_us < keys_head->anchor.abs_value_us - + duration.rel_value_us) ) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Signing at %llu with key valid from %llu to %llu\n", - (unsigned long long) now.abs_value_us, - (unsigned long long) keys_head->anchor.abs_value_us, - (unsigned long long) keys_head->anchor.abs_value_us - + duration.rel_value_us); - else - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Signing at %llu with key valid from %llu to %llu\n", - (unsigned long long) now.abs_value_us, - (unsigned long long) keys_head->anchor.abs_value_us, - (unsigned long long) keys_head->anchor.abs_value_us - + duration.rel_value_us); + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + key = keys_head; + while ( (NULL != key) && + (GNUNET_TIME_absolute_is_past ( + GNUNET_TIME_absolute_add (key->anchor, + duration))) ) + { + struct Key *nxt = key->next; + + if (0 != key->rc) + break; /* do later */ + GNUNET_CONTAINER_DLL_remove (keys_head, + keys_tail, + key); + if ( (! key->purge) && + (0 != unlink (key->filename)) ) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "unlink", + key->filename); + GNUNET_free (key->filename); + GNUNET_free (key); + key = nxt; } - wi = GNUNET_new (struct WorkItem); - wi->addr = *addr; - wi->addr_size = addr_size; - wi->key = keys_head; - keys_head->rc++; - wi->purpose = GNUNET_memdup (purpose, - purpose_size); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Received request to sign over %u bytes, queueing as %p\n", - (unsigned int) purpose_size, - wi); - GNUNET_assert (0 == pthread_mutex_lock (&work_lock)); - work_counter++; - GNUNET_CONTAINER_DLL_insert (work_head, - work_tail, - wi); - GNUNET_assert (0 == pthread_cond_signal (&work_cond)); - GNUNET_assert (0 == pthread_mutex_unlock (&work_lock)); -} - - -/** - * Notify @a client about @a key becoming available. - * - * @param[in,out] client the client to notify; possible freed if transmission fails - * @param key the key to notify @a client about - * @return #GNUNET_OK on success - */ -static int -notify_client_key_add (struct Client *client, - const struct Key *key) -{ - struct TALER_CRYPTO_EddsaKeyAvailableNotification an = { - .header.size = htons (sizeof (an)), - .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL), - .anchor_time = GNUNET_TIME_absolute_hton (key->anchor), - .duration = GNUNET_TIME_relative_hton (duration), - .exchange_pub = key->exchange_pub, - .secm_pub = smpub - }; - - TALER_exchange_secmod_eddsa_sign (&key->exchange_pub, - key->anchor, - duration, - &smpriv, - &an.secm_sig); - if (GNUNET_OK != - transmit (&client->addr, - client->addr_size, - &an.header)) + if (NULL == key) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %s must have disconnected\n", - client->addr.sun_path); - free_client (client); - return GNUNET_SYSERR; + GNUNET_break (0); + ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; } - return GNUNET_OK; -} - + else + { + GNUNET_assert (key->rc < UINT_MAX); + key->rc++; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); -/** - * Notify @a client about @a key being purged. - * - * @param[in,out] client the client to notify; possible freed if transmission fails - * @param key the key to notify @a client about - * @return #GNUNET_OK on success - */ -static int -notify_client_key_del (struct Client *client, - const struct Key *key) -{ - struct TALER_CRYPTO_EddsaKeyPurgeNotification pn = { - .header.type = htons (TALER_HELPER_EDDSA_MT_PURGE), - .header.size = htons (sizeof (pn)), - .exchange_pub = key->exchange_pub - }; + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_sign_ (&key->exchange_priv.eddsa_priv, + purpose, + &sres.exchange_sig.eddsa_signature)) + ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + else + ec = TALER_EC_NONE; + sres.exchange_pub = key->exchange_pub; + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + GNUNET_assert (key->rc > 0); + key->rc--; + } + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + if (TALER_EC_NONE != ec) + { + struct TALER_CRYPTO_EddsaSignFailure sf = { + .header.size = htons (sizeof (sf)), + .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE), + .ec = htonl ((uint32_t) ec) + }; - if (GNUNET_OK != - transmit (&client->addr, - client->addr_size, - &pn.header)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %s must have disconnected\n", - client->addr.sun_path); - free_client (client); - return GNUNET_SYSERR; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Signing request %p failed, worker failed to produce signature\n", + client); + return TES_transmit (client->csock, + &sf.header); } - return GNUNET_OK; + return TES_transmit (client->csock, + &sres.header); } @@ -717,7 +343,7 @@ notify_client_key_del (struct Client *client, * @param position where in the DLL will the @a key go * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue setup_key (struct Key *key, struct Key *position) { @@ -746,54 +372,58 @@ setup_key (struct Key *key, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Setup fresh private key in `%s'\n", key->filename); + key->key_gen = key_gen; key->exchange_priv.eddsa_priv = priv; key->exchange_pub.eddsa_pub = pub; GNUNET_CONTAINER_DLL_insert_after (keys_head, keys_tail, position, key); + return GNUNET_OK; +} - /* tell clients about new key */ - { - struct Client *nxt; - for (struct Client *client = clients_head; - NULL != client; - client = nxt) - { - nxt = client->next; - if (GNUNET_OK != - notify_client_key_add (client, - key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Failed to notify client about new key, client dropped\n"); - } - } - } - return GNUNET_OK; +/** + * The validity period of a key @a key has expired. Purge it. + * + * @param[in] key expired or revoked key to purge + */ +static void +purge_key (struct Key *key) +{ + if (key->purge) + return; + if (0 != unlink (key->filename)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "unlink", + key->filename); + key->purge = true; + key->key_gen = key_gen; + GNUNET_free (key->filename); } /** - * A client informs us that a key has been revoked. + * A @a client informs us that a key has been revoked. * Check if the key is still in use, and if so replace (!) * it with a fresh key. * - * @param addr address of the client making the request - * @param addr_size number of bytes in @a addr + * @param client the client making the request * @param rr the revocation request + * @return #GNUNET_OK on success */ -static void -handle_revoke_request (const struct sockaddr_un *addr, - socklen_t addr_size, +static enum GNUNET_GenericReturnValue +handle_revoke_request (struct TES_Client *client, const struct TALER_CRYPTO_EddsaRevokeRequest *rr) { struct Key *key; struct Key *nkey; key = NULL; - for (struct Key *pos = keys_head; NULL != pos; pos = pos->next) + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + for (struct Key *pos = keys_head; + NULL != pos; + pos = pos->next) if (0 == GNUNET_memcmp (&pos->exchange_pub, &rr->exchange_pub)) { @@ -802,16 +432,18 @@ handle_revoke_request (const struct sockaddr_un *addr, } if (NULL == key) { + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Revocation request ignored, key unknown\n"); - return; + return GNUNET_OK; } - /* kill existing key, done first to ensure this always happens */ - if (0 != unlink (key->filename)) - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "unlink", - key->filename); + key_gen++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Revoking key %p, bumping generation to %llu\n", + key, + (unsigned long long) key_gen); + purge_key (key); /* Setup replacement key */ nkey = GNUNET_new (struct Key); @@ -820,157 +452,146 @@ handle_revoke_request (const struct sockaddr_un *addr, setup_key (nkey, key)) { + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); GNUNET_break (0); GNUNET_SCHEDULER_shutdown (); - global_ret = 44; - return; + global_ret = EXIT_FAILURE; + return GNUNET_SYSERR; } + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + TES_wake_clients (); + return GNUNET_OK; +} - /* get rid of the old key */ - key->purge = true; - GNUNET_CONTAINER_DLL_remove (keys_head, - keys_tail, - key); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Revocation complete\n"); - /* Tell clients this key is gone */ - { - struct Client *nxt; +/** + * Handle @a hdr message received from @a client. + * + * @param client the client that received the message + * @param hdr message that was received + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +eddsa_work_dispatch (struct TES_Client *client, + const struct GNUNET_MessageHeader *hdr) +{ + uint16_t msize = ntohs (hdr->size); - for (struct Client *client = clients_head; - NULL != client; - client = nxt) + switch (ntohs (hdr->type)) + { + case TALER_HELPER_EDDSA_MT_REQ_SIGN: + if (msize < sizeof (struct TALER_CRYPTO_EddsaSignRequest)) { - nxt = client->next; - if (GNUNET_OK != - notify_client_key_del (client, - key)) - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Failed to notify client about revoked key, client dropped\n"); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return handle_sign_request ( + client, + (const struct TALER_CRYPTO_EddsaSignRequest *) hdr); + case TALER_HELPER_EDDSA_MT_REQ_REVOKE: + if (msize != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; } + return handle_revoke_request ( + client, + (const struct TALER_CRYPTO_EddsaRevokeRequest *) hdr); + default: + GNUNET_break_op (0); + return GNUNET_SYSERR; } - if (0 == key->rc) - free_key (key); } -static void -read_job (void *cls) +/** + * Send our initial key set to @a client together with the + * "sync" terminator. + * + * @param client the client to inform + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +eddsa_client_init (struct TES_Client *client) { - struct Client *client = cls; - char buf[65536]; - ssize_t buf_size; - const struct GNUNET_MessageHeader *hdr; - struct sockaddr_un addr; - socklen_t addr_size = sizeof (addr); - - read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - unix_sock, - &read_job, - NULL); - buf_size = GNUNET_NETWORK_socket_recvfrom (unix_sock, - buf, - sizeof (buf), - (struct sockaddr *) &addr, - &addr_size); - if (-1 == buf_size) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "recv"); - return; - } - if (0 == buf_size) - { - return; - } - if (buf_size < sizeof (struct GNUNET_MessageHeader)) - { - GNUNET_break_op (0); - return; - } - hdr = (const struct GNUNET_MessageHeader *) buf; - if (ntohs (hdr->size) != buf_size) + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + for (struct Key *key = keys_head; + NULL != key; + key = key->next) { - GNUNET_break_op (0); - free_client (client); - return; + if (GNUNET_OK != + notify_client_key_add (client, + key)) + { + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + GNUNET_break (0); + return GNUNET_SYSERR; + } } - switch (ntohs (hdr->type)) + client->key_gen = key_gen; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); { - case TALER_HELPER_EDDSA_MT_REQ_INIT: - if (ntohs (hdr->size) != sizeof (struct GNUNET_MessageHeader)) + struct GNUNET_MessageHeader synced = { + .type = htons (TALER_HELPER_EDDSA_SYNCED), + .size = htons (sizeof (synced)) + }; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Client %p synced\n", + client); + if (GNUNET_OK != + TES_transmit (client->csock, + &synced)) { - GNUNET_break_op (0); - return; + GNUNET_break (0); + return GNUNET_SYSERR; } + } + return GNUNET_OK; +} + + +/** + * Notify @a client about all changes to the keys since + * the last generation known to the @a client. + * + * @param client the client to notify + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +eddsa_update_client_keys (struct TES_Client *client) +{ + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + for (struct Key *key = keys_head; + NULL != key; + key = key->next) + { + if (key->key_gen <= client->key_gen) + continue; + if (key->purge) { - struct Client *client; - - client = GNUNET_new (struct Client); - client->addr = addr; - client->addr_size = addr_size; - GNUNET_CONTAINER_DLL_insert (clients_head, - clients_tail, - client); - for (struct Key *key = keys_head; - NULL != key; - key = key->next) - { - if (GNUNET_OK != - notify_client_key_add (client, - key)) - { - /* client died, skip the rest */ - client = NULL; - break; - } - } - if (NULL != client) + if (GNUNET_OK != + notify_client_key_del (client, + key)) { - struct GNUNET_MessageHeader synced = { - .type = htons (TALER_HELPER_EDDSA_SYNCED), - .size = htons (sizeof (synced)) - }; - - if (GNUNET_OK != - transmit (&client->addr, - client->addr_size, - &synced)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %s must have disconnected\n", - client->addr.sun_path); - free_client (client); - } + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + return GNUNET_SYSERR; } } - break; - case TALER_HELPER_EDDSA_MT_REQ_SIGN: - if (ntohs (hdr->size) < sizeof (struct TALER_CRYPTO_EddsaSignRequest)) - { - GNUNET_break_op (0); - return; - } - handle_sign_request (&addr, - addr_size, - (const struct TALER_CRYPTO_EddsaSignRequest *) buf); - break; - case TALER_HELPER_EDDSA_MT_REQ_REVOKE: - if (ntohs (hdr->size) != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest)) + else { - GNUNET_break_op (0); - return; + if (GNUNET_OK != + notify_client_key_add (client, + key)) + { + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + return GNUNET_SYSERR; + } } - handle_revoke_request (&addr, - addr_size, - (const struct - TALER_CRYPTO_EddsaRevokeRequest *) buf); - break; - default: - GNUNET_break_op (0); - return; } + client->key_gen = key_gen; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + return GNUNET_OK; } @@ -979,7 +600,7 @@ read_job (void *cls) * * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue create_key (void) { struct Key *key; @@ -1010,7 +631,7 @@ create_key (void) GNUNET_break (0); GNUNET_free (key); GNUNET_SCHEDULER_shutdown (); - global_ret = 42; + global_ret = EXIT_FAILURE; return GNUNET_SYSERR; } return GNUNET_OK; @@ -1039,55 +660,6 @@ key_action_time (void) } -/** - * The validity period of a key @a key has expired. Purge it. - * - * @param[in] key expired key to purge and free - */ -static void -purge_key (struct Key *key) -{ - struct Client *nxt; - - for (struct Client *client = clients_head; - NULL != client; - client = nxt) - { - nxt = client->next; - if (GNUNET_OK != - notify_client_key_del (client, - key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Failed to notify client about purged key, client dropped\n"); - } - } - GNUNET_CONTAINER_DLL_remove (keys_head, - keys_tail, - key); - if (0 != unlink (key->filename)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "unlink", - key->filename); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Purged expired private key `%s'\n", - key->filename); - } - GNUNET_free (key->filename); - if (0 != key->rc) - { - /* delay until all signing threads are done with this key */ - key->purge = true; - return; - } - GNUNET_free (key); -} - - /** * Create new keys and expire ancient keys. * @@ -1096,35 +668,52 @@ purge_key (struct Key *key) static void update_keys (void *cls) { - (void) cls; + bool wake = false; + (void) cls; keygen_task = NULL; + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); /* create new keys */ while ( (NULL == keys_tail) || - (0 == - GNUNET_TIME_absolute_get_remaining ( - GNUNET_TIME_absolute_subtract ( - GNUNET_TIME_absolute_subtract ( - GNUNET_TIME_absolute_add (keys_tail->anchor, - duration), - lookahead_sign), - overlap_duration)).rel_value_us) ) + GNUNET_TIME_absolute_is_past ( + GNUNET_TIME_absolute_subtract ( + GNUNET_TIME_absolute_subtract ( + GNUNET_TIME_absolute_add (keys_tail->anchor, + duration), + lookahead_sign), + overlap_duration)) ) { + if (! wake) + { + key_gen++; + wake = true; + } if (GNUNET_OK != create_key ()) { + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); GNUNET_break (0); + global_ret = EXIT_FAILURE; GNUNET_SCHEDULER_shutdown (); return; } } /* remove expired keys */ while ( (NULL != keys_head) && - (0 == - GNUNET_TIME_absolute_get_remaining - (GNUNET_TIME_absolute_add (keys_head->anchor, - duration)).rel_value_us) ) + GNUNET_TIME_absolute_is_past ( + GNUNET_TIME_absolute_add (keys_head->anchor, + duration))) + { + if (! wake) + { + key_gen++; + wake = true; + } purge_key (keys_head); + } + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + if (wake) + TES_wake_clients (); keygen_task = GNUNET_SCHEDULER_add_at (key_action_time (), &update_keys, NULL); @@ -1137,8 +726,9 @@ update_keys (void *cls) * @param filename name of the file we are parsing, for logging * @param buf key material * @param buf_size number of bytes in @a buf + * @return #GNUNET_OK on success */ -static void +static enum GNUNET_GenericReturnValue parse_key (const char *filename, const void *buf, size_t buf_size) @@ -1155,7 +745,7 @@ parse_key (const char *filename, { /* File in a directory without '/' in the name, this makes no sense. */ GNUNET_break (0); - return; + return GNUNET_SYSERR; } anchor_s++; if (1 != sscanf (anchor_s, @@ -1167,7 +757,7 @@ parse_key (const char *filename, GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Filename `%s' invalid for key file, skipping\n", filename); - return; + return GNUNET_SYSERR; } anchor.abs_value_us = anchor_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us; if (anchor_ll != anchor.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us) @@ -1176,7 +766,7 @@ parse_key (const char *filename, GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Filename `%s' invalid for key file, skipping\n", filename); - return; + return GNUNET_SYSERR; } if (buf_size != sizeof (priv)) { @@ -1184,7 +774,7 @@ parse_key (const char *filename, GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "File `%s' is malformed, skipping\n", filename); - return; + return GNUNET_SYSERR; } memcpy (&priv, buf, @@ -1197,11 +787,13 @@ parse_key (const char *filename, GNUNET_CRYPTO_eddsa_key_get_public (&priv, &pub); + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); key = GNUNET_new (struct Key); key->exchange_priv.eddsa_priv = priv; key->exchange_pub.eddsa_pub = pub; key->anchor = anchor; key->filename = GNUNET_strdup (filename); + key->key_gen = key_gen; before = NULL; for (struct Key *pos = keys_head; NULL != pos; @@ -1215,10 +807,12 @@ parse_key (const char *filename, keys_tail, before, key); + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Imported key from `%s'\n", filename); } + return GNUNET_OK; } @@ -1228,7 +822,7 @@ parse_key (const char *filename, * @param cls NULL * @param filename name of a file in the directory */ -static int +static enum GNUNET_GenericReturnValue import_key (void *cls, const char *filename) { @@ -1326,9 +920,9 @@ import_key (void *cls, GNUNET_DISK_file_close (fh); return GNUNET_OK; } - parse_key (filename, - ptr, - (size_t) sbuf.st_size); + (void) parse_key (filename, + ptr, + (size_t) sbuf.st_size); GNUNET_DISK_file_unmap (map); GNUNET_DISK_file_close (fh); return GNUNET_OK; @@ -1336,15 +930,16 @@ import_key (void *cls, /** - * Load the various duration values from #kcfg. + * Load the various duration values from @a kcfg. * + * @param cfg configuration to use * @return #GNUNET_OK on success */ -static int -load_durations (void) +static enum GNUNET_GenericReturnValue +load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg) { if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, + GNUNET_CONFIGURATION_get_value_time (cfg, "taler-exchange-secmod-eddsa", "OVERLAP_DURATION", &overlap_duration)) @@ -1355,7 +950,7 @@ load_durations (void) return GNUNET_SYSERR; } if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, + GNUNET_CONFIGURATION_get_value_time (cfg, "taler-exchange-secmod-eddsa", "DURATION", &duration)) @@ -1368,7 +963,7 @@ load_durations (void) GNUNET_TIME_round_rel (&overlap_duration); if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, + GNUNET_CONFIGURATION_get_value_time (cfg, "taler-exchange-secmod-eddsa", "LOOKAHEAD_SIGN", &lookahead_sign)) @@ -1392,51 +987,12 @@ static void do_shutdown (void *cls) { (void) cls; - if (NULL != read_task) - { - GNUNET_SCHEDULER_cancel (read_task); - read_task = NULL; - } - if (NULL != unix_sock) - { - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (unix_sock)); - unix_sock = NULL; - } - if (0 != unlink (unixpath)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "unlink", - unixpath); - } - GNUNET_free (unixpath); + TES_listen_stop (); if (NULL != keygen_task) { GNUNET_SCHEDULER_cancel (keygen_task); keygen_task = NULL; } - if (NULL != done_task) - { - GNUNET_SCHEDULER_cancel (done_task); - done_task = NULL; - } - /* shut down worker threads */ - if (NULL != workers) - { - GNUNET_assert (0 == pthread_mutex_lock (&work_lock)); - in_shutdown = true; - GNUNET_assert (0 == pthread_cond_broadcast (&work_cond)); - GNUNET_assert (0 == pthread_mutex_unlock (&work_lock)); - for (unsigned int i = 0; i