diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/testing/testing_api_cmd_batch.c | 84 | ||||
-rw-r--r-- | src/testing/testing_api_loop.c | 175 | ||||
-rw-r--r-- | src/util/taler-exchange-secmod-eddsa.c | 1288 | ||||
-rw-r--r-- | src/util/taler-exchange-secmod-rsa.c | 1540 | ||||
-rw-r--r-- | src/util/taler-exchange-secmod-rsa.h | 6 | ||||
-rw-r--r-- | src/util/test_helper_eddsa.c | 119 | ||||
-rw-r--r-- | src/util/test_helper_rsa.c | 259 |
7 files changed, 1324 insertions, 2147 deletions
diff --git a/src/testing/testing_api_cmd_batch.c b/src/testing/testing_api_cmd_batch.c index e8f76ca37..2e880e8df 100644 --- a/src/testing/testing_api_cmd_batch.c +++ b/src/testing/testing_api_cmd_batch.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2014-2018 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 @@ -113,15 +113,22 @@ batch_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static enum GNUNET_GenericReturnValue +static int batch_traits (void *cls, const void **ret, const char *trait, unsigned int index) { +#define CURRENT_CMD_INDEX 0 +#define BATCH_INDEX 1 + struct BatchState *bs = cls; + struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_batch_cmds (&bs->batch), + TALER_TESTING_make_trait_cmd + (CURRENT_CMD_INDEX, &bs->batch[bs->batch_ip]), + TALER_TESTING_make_trait_cmd + (BATCH_INDEX, bs->batch), TALER_TESTING_trait_end () }; @@ -133,6 +140,18 @@ batch_traits (void *cls, } +/** + * Create a "batch" command. Such command takes a + * end_CMD-terminated array of CMDs and executed them. + * Once it hits the end CMD, it passes the control + * to the next top-level CMD, regardless of it being + * another batch or ordinary CMD. + * + * @param label the command label. + * @param batch array of CMDs to execute. + * + * @return the command. + */ struct TALER_TESTING_Command TALER_TESTING_cmd_batch (const char *label, struct TALER_TESTING_Command *batch) @@ -166,29 +185,68 @@ TALER_TESTING_cmd_batch (const char *label, } +/** + * Advance internal pointer to next command. + * + * @param is interpreter state. + * @param cmd batch to advance + */ void -TALER_TESTING_cmd_batch_next (struct TALER_TESTING_Interpreter *is) +TALER_TESTING_cmd_batch_next (struct TALER_TESTING_Interpreter *is, + struct TALER_TESTING_Command *par, + struct TALER_TESTING_Command *cmd) { - struct BatchState *bs = is->commands[is->ip].cls; + struct BatchState *bs = cmd->cls; + struct TALER_TESTING_Command *chld; if (NULL == bs->batch[bs->batch_ip].label) { - is->commands[is->ip].finish_time = GNUNET_TIME_absolute_get (); - is->ip++; + if (NULL == par) + { + is->commands[is->ip].finish_time = GNUNET_TIME_absolute_get (); + is->ip++; + } + else + { + struct BatchState *ps = par->cls; + + cmd->finish_time = GNUNET_TIME_absolute_get (); + ps->batch_ip++; + } return; } - bs->batch[bs->batch_ip].finish_time = GNUNET_TIME_absolute_get (); - bs->batch_ip++; + chld = &bs->batch[bs->batch_ip]; + if (TALER_TESTING_cmd_is_batch (chld)) + { + TALER_TESTING_cmd_batch_next (is, + cmd, + chld); + } + else + { + bs->batch[bs->batch_ip].finish_time = GNUNET_TIME_absolute_get (); + bs->batch_ip++; + } } -bool +/** + * Test if this command is a batch command. + * + * @return false if not, true if it is a batch command + */ +int TALER_TESTING_cmd_is_batch (const struct TALER_TESTING_Command *cmd) { return cmd->run == &batch_run; } +/** + * Obtain what command the batch is at. + * + * @return cmd current batch command + */ struct TALER_TESTING_Command * TALER_TESTING_cmd_batch_get_current (const struct TALER_TESTING_Command *cmd) { @@ -199,6 +257,12 @@ TALER_TESTING_cmd_batch_get_current (const struct TALER_TESTING_Command *cmd) } +/** + * Set what command the batch should be at. + * + * @param cmd current batch command + * @param new_ip where to move the IP + */ void TALER_TESTING_cmd_batch_set_current (const struct TALER_TESTING_Command *cmd, unsigned int new_ip) diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c index f86c7765b..868a2d750 100644 --- a/src/testing/testing_api_loop.c +++ b/src/testing/testing_api_loop.c @@ -36,6 +36,49 @@ */ static struct GNUNET_DISK_PipeHandle *sigpipe; + +const struct TALER_TESTING_Command * +lookup_helper (const struct TALER_TESTING_Command *cmd, + const char *label) +{ +#define BATCH_INDEX 1 + struct TALER_TESTING_Command *batch; + struct TALER_TESTING_Command *current; + struct TALER_TESTING_Command *icmd; + const struct TALER_TESTING_Command *match; + + current = TALER_TESTING_cmd_batch_get_current (cmd); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_cmd (cmd, + BATCH_INDEX, + &batch)); + /* We must do the loop forward, but we can find the last match */ + match = NULL; + for (unsigned int j = 0; + NULL != (icmd = &batch[j])->label; + j++) + { + if (TALER_TESTING_cmd_is_batch (icmd)) + { + const struct TALER_TESTING_Command *imatch; + + imatch = lookup_helper (icmd, + label); + if (NULL != imatch) + match = imatch; + } + if ( (current != icmd) && + (NULL != icmd->label) && + (0 == strcmp (icmd->label, + label)) ) + match = icmd; + if (current == icmd) + break; + } + return match; +} + + /** * Lookup command by label. * @@ -66,30 +109,12 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is, if (TALER_TESTING_cmd_is_batch (cmd)) { - struct TALER_TESTING_Command **batch; - struct TALER_TESTING_Command *current; - struct TALER_TESTING_Command *icmd; - const struct TALER_TESTING_Command *match; - - current = TALER_TESTING_cmd_batch_get_current (cmd); - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_batch_cmds (cmd, - &batch)); - /* We must do the loop forward, but we can find the last match */ - match = NULL; - for (unsigned int j = 0; - NULL != (icmd = &(*batch)[j])->label; - j++) - { - if (current == icmd) - break; /* do not go past current command */ - if ( (NULL != icmd->label) && - (0 == strcmp (icmd->label, - label)) ) - match = icmd; - } - if (NULL != match) - return match; + const struct TALER_TESTING_Command *ret; + + ret = lookup_helper (cmd, + label); + if (NULL != ret) + return ret; } } GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -118,6 +143,15 @@ TALER_TESTING_interpreter_get_fakebank (struct TALER_TESTING_Interpreter *is) } +/** + * Run tests starting the "fakebank" first. The "fakebank" + * is a C minimalist version of the human-oriented Python bank, + * which is also part of the Taler project. + * + * @param is pointer to the interpreter state + * @param commands the list of commands to execute + * @param bank_url the url the fakebank is supposed to run on + */ void TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is, struct TALER_TESTING_Command *commands, @@ -155,6 +189,9 @@ static void interpreter_run (void *cls); +/** + * Current command is done, run the next one. + */ void TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is) { @@ -166,7 +203,9 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is) return; /* ignore, we already failed! */ if (TALER_TESTING_cmd_is_batch (cmd)) { - TALER_TESTING_cmd_batch_next (is); + TALER_TESTING_cmd_batch_next (is, + NULL, + cmd); } else { @@ -189,6 +228,11 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is) } +/** + * Current command failed, clean up and fail the test case. + * + * @param is interpreter of the test + */ void TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is) { @@ -209,6 +253,11 @@ TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is) } +/** + * Create command array terminator. + * + * @return a end-command. + */ struct TALER_TESTING_Command TALER_TESTING_cmd_end (void) { @@ -219,6 +268,9 @@ TALER_TESTING_cmd_end (void) } +/** + * Obtain current label. + */ const char * TALER_TESTING_interpreter_get_current_label (struct TALER_TESTING_Interpreter *is) @@ -289,9 +341,8 @@ do_shutdown (void *cls) for (unsigned int j = 0; NULL != (cmd = &is->commands[j])->label; j++) - if (NULL != cmd->cleanup) - cmd->cleanup (cmd->cls, - cmd); + cmd->cleanup (cmd->cls, + cmd); if (NULL != is->exchange) { @@ -367,8 +418,17 @@ maint_child_death (void *cls) enum GNUNET_OS_ProcessStatusType type; unsigned long code; - while (TALER_TESTING_cmd_is_batch (cmd)) - cmd = TALER_TESTING_cmd_batch_get_current (cmd); + if (TALER_TESTING_cmd_is_batch (cmd)) + { + struct TALER_TESTING_Command *batch_cmd; + + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_cmd (cmd, + 0, + &batch_cmd)); + cmd = batch_cmd; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got SIGCHLD for `%s'.\n", cmd->label); @@ -381,6 +441,7 @@ maint_child_death (void *cls) sizeof (c))); if (GNUNET_OK != TALER_TESTING_get_trait_process (cmd, + 0, &processp)) { GNUNET_break (0); @@ -433,6 +494,12 @@ maint_child_death (void *cls) } +/** + * Wait until we receive SIGCHLD signal. + * Then obtain the process trait of the current + * command, wait on the the zombie and continue + * with the next command. + */ void TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is) { @@ -449,6 +516,16 @@ TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is) } +/** + * Run the testsuite. Note, CMDs are copied into + * the interpreter state because they are _usually_ + * defined into the "run" method that returns after + * having scheduled the test interpreter. + * + * @param is the interpreter state + * @param commands the list of command to execute + * @param timeout how long to wait + */ void TALER_TESTING_run2 (struct TALER_TESTING_Interpreter *is, struct TALER_TESTING_Command *commands, @@ -478,6 +555,15 @@ TALER_TESTING_run2 (struct TALER_TESTING_Interpreter *is, } +/** + * Run the testsuite. Note, CMDs are copied into + * the interpreter state because they are _usually_ + * defined into the "run" method that returns after + * having scheduled the test interpreter. + * + * @param is the interpreter state + * @param commands the list of command to execute + */ void TALER_TESTING_run (struct TALER_TESTING_Interpreter *is, struct TALER_TESTING_Command *commands) @@ -536,6 +622,16 @@ sighandler_child_death (void) } +/** + * "Canonical" cert_cb used when we are connecting to the + * Exchange. + * + * @param cls closure, typically, the "run" method containing + * all the commands to be run, and a closure for it. + * @param hr HTTP response details + * @param keys the exchange's keys. + * @param compat protocol compatibility information. + */ void TALER_TESTING_cert_cb (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, @@ -804,6 +900,25 @@ load_urls (struct TALER_TESTING_Interpreter *is) } +/** + * Install signal handlers plus schedules the main wrapper + * around the "run" method. + * + * @param main_cb the "run" method which contains all the + * commands. + * @param main_cb_cls a closure for "run", typically NULL. + * @param cfg configuration to use + * @param exchanged exchange process handle: will be put in the + * state as some commands - e.g. revoke - need to send + * signal to it, for example to let it know to reload the + * key state.. if NULL, the interpreter will run without + * trying to connect to the exchange first. + * @param exchange_connect #GNUNET_YES if the test should connect + * to the exchange, #GNUNET_NO otherwise + * @return #GNUNET_OK if all is okay, != #GNUNET_OK otherwise. + * non-GNUNET_OK codes are #GNUNET_SYSERR most of the + * times. + */ int TALER_TESTING_setup (TALER_TESTING_Main main_cb, void *main_cb_cls, 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 <poll.h> /** @@ -80,6 +81,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. */ @@ -95,92 +101,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. */ static struct Key *keys_head; @@ -201,13 +121,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. */ @@ -220,11 +133,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? */ static char *keydir; @@ -242,400 +150,19 @@ 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. - */ -static struct GNUNET_NETWORK_Handle *done_signal; - -/** - * Set once we are in shutdown and workers should terminate. - */ -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; -} - - -/** - * 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. - * - * @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 - * @return #GNUNET_OK on success - */ -static int -transmit (const struct sockaddr_un *addr, - socklen_t addr_size, - const struct GNUNET_MessageHeader *hdr) -{ - for (unsigned int i = 0; i<100; i++) - { - 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_strerror (GNUNET_ERROR_TYPE_WARNING, - "sendto"); - return GNUNET_SYSERR; -} - - -/** - * Process completed tasks that are in the #done_head queue, sending - * the result back to the client (and resuming the client). - * - * @param cls NULL + * Lock for the keys queue. */ -static void -handle_done (void *cls) -{ - uint64_t data; - (void) cls; - - /* 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) - { - 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_assert (0 == pthread_mutex_unlock (&done_lock)); - -} - +static pthread_mutex_t keys_lock; /** - * Handle @a client request @a sr to create signature. Create the - * 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 sr the request details + * Current key generation. */ -static void -handle_sign_request (const struct sockaddr_un *addr, - socklen_t addr_size, - 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); - - if (purpose_size != htonl (purpose->size)) - { - struct TALER_CRYPTO_EddsaSignFailure sf = { - .header.size = htons (sizeof (sr)), - .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE), - .ec = htonl (TALER_EC_GENERIC_PARAMETER_MALFORMED) - }; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Signing request failed, request malformed\n"); - (void) transmit (addr, - addr_size, - &sf.header); - return; - } - { - 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); - } - 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)); -} +static uint64_t key_gen; /** @@ -645,8 +172,8 @@ handle_sign_request (const struct sockaddr_un *addr, * @param key the key to notify @a client about * @return #GNUNET_OK on success */ -static int -notify_client_key_add (struct Client *client, +static enum GNUNET_GenericReturnValue +notify_client_key_add (struct TES_Client *client, const struct Key *key) { struct TALER_CRYPTO_EddsaKeyAvailableNotification an = { @@ -655,23 +182,21 @@ notify_client_key_add (struct Client *client, .anchor_time = GNUNET_TIME_absolute_hton (key->anchor), .duration = GNUNET_TIME_relative_hton (duration), .exchange_pub = key->exchange_pub, - .secm_pub = smpub + .secm_pub = TES_smpub }; TALER_exchange_secmod_eddsa_sign (&key->exchange_pub, key->anchor, duration, - &smpriv, + &TES_smpriv, &an.secm_sig); if (GNUNET_OK != - transmit (&client->addr, - client->addr_size, - &an.header)) + TES_transmit (client->csock, + &an.header)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %s must have disconnected\n", - client->addr.sun_path); - free_client (client); + "Client %p must have disconnected\n", + client); return GNUNET_SYSERR; } return GNUNET_OK; @@ -685,8 +210,8 @@ notify_client_key_add (struct Client *client, * @param key the key to notify @a client about * @return #GNUNET_OK on success */ -static int -notify_client_key_del (struct Client *client, +static enum GNUNET_GenericReturnValue +notify_client_key_del (struct TES_Client *client, const struct Key *key) { struct TALER_CRYPTO_EddsaKeyPurgeNotification pn = { @@ -696,14 +221,12 @@ notify_client_key_del (struct Client *client, }; if (GNUNET_OK != - transmit (&client->addr, - client->addr_size, - &pn.header)) + TES_transmit (client->csock, + &pn.header)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %s must have disconnected\n", - client->addr.sun_path); - free_client (client); + "Client %p must have disconnected\n", + client); return GNUNET_SYSERR; } return GNUNET_OK; @@ -711,13 +234,116 @@ notify_client_key_del (struct Client *client, /** + * Handle @a client request @a sr to create signature. Create the + * signature using the respective key and return the result to + * the client. + * + * @param client the client making the request + * @param sr the request details + * @return #GNUNET_OK on success + */ +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; + 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)) + { + struct TALER_CRYPTO_EddsaSignFailure sf = { + .header.size = htons (sizeof (sr)), + .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE), + .ec = htonl (TALER_EC_GENERIC_PARAMETER_MALFORMED) + }; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Signing request failed, request malformed\n"); + return TES_transmit (client->csock, + &sf.header); + } + + 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; + } + if (NULL == key) + { + GNUNET_break (0); + ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; + } + else + { + GNUNET_assert (key->rc < UINT_MAX); + key->rc++; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + + 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) + }; + + 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 TES_transmit (client->csock, + &sres.header); +} + + +/** * Initialize key material for key @a key (also on disk). * * @param[in,out] key to compute key material for * @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) + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + for (struct Key *key = keys_head; + NULL != key; + key = key->next) { - 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_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; @@ -1040,55 +661,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. * * @param cls NULL @@ -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<num_workers; i++) - GNUNET_assert (0 == pthread_join (workers[i], - NULL)); - } - if (NULL != done_signal) - { - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (done_signal)); - done_signal = NULL; - } } @@ -1454,10 +1010,15 @@ run (void *cls, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { + static struct TES_Callbacks cb = { + .dispatch = eddsa_work_dispatch, + .updater = eddsa_update_client_keys, + .init = eddsa_client_init + }; + (void) cls; (void) args; (void) cfgfile; - kcfg = cfg; if (now.abs_value_us != now_tmp.abs_value_us) { /* The user gave "--now", use it! */ @@ -1469,48 +1030,14 @@ run (void *cls, now = GNUNET_TIME_absolute_get (); } GNUNET_TIME_round_abs (&now); - - { - char *pfn; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (kcfg, - "taler-exchange-secmod-eddsa", - "SM_PRIV_KEY", - &pfn)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-eddsa", - "SM_PRIV_KEY"); - global_ret = 1; - return; - } - if (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_key_from_file (pfn, - GNUNET_YES, - &smpriv.eddsa_priv)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", - "SM_PRIV_KEY", - "Could not use file to persist private key"); - GNUNET_free (pfn); - global_ret = 1; - return; - } - GNUNET_free (pfn); - GNUNET_CRYPTO_eddsa_key_get_public (&smpriv.eddsa_priv, - &smpub.eddsa_pub); - } - if (GNUNET_OK != - load_durations ()) + load_durations (cfg)) { - global_ret = 1; + global_ret = EXIT_NOTCONFIGURED; return; } if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (kcfg, + GNUNET_CONFIGURATION_get_value_filename (cfg, "taler-exchange-secmod-eddsa", "KEY_DIR", &keydir)) @@ -1518,131 +1045,29 @@ run (void *cls, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "taler-exchange-secmod-eddsa", "KEY_DIR"); - global_ret = 1; + global_ret = EXIT_NOTCONFIGURED; return; } - - /* Create client directory and set permissions. */ - { - char *client_dir; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (kcfg, - "taler-exchange-secmod-eddsa", - "CLIENT_DIR", - &client_dir)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + global_ret = TES_listen_start (cfg, "taler-exchange-secmod-eddsa", - "CLIENT_DIR"); - global_ret = 3; - return; - } - - if (GNUNET_OK != GNUNET_DISK_directory_create (client_dir)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Can't create client directory (%s)\n", - client_dir); - global_ret = 3; - return; - } - /* Set sticky group bit, so that clients will be writeable by the current service. */ - if (0 != chmod (client_dir, - S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP - | S_ISGID)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Can't set permissions for client directory (%s)\n", - client_dir); - global_ret = 3; - return; - } - - GNUNET_free (client_dir); - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (kcfg, - "taler-exchange-secmod-eddsa", - "UNIXPATH", - &unixpath)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-eddsa", - "UNIXPATH"); - global_ret = 3; - return; - } - - GNUNET_assert (NULL != unixpath); - unix_sock = TES_open_socket (unixpath); - - if (NULL == unix_sock) - { - GNUNET_free (unixpath); - global_ret = 2; + &cb); + if (0 != global_ret) return; - } - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - /* Load keys */ GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_create (keydir)); GNUNET_DISK_directory_scan (keydir, &import_key, NULL); - /* start job to accept incoming requests on 'sock' */ - read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - unix_sock, - &read_job, - NULL); - /* start job to keep keys up-to-date; MUST be run before the #read_task, + + /* start job to keep keys up-to-date; MUST be run before the #listen_task, hence with priority. */ keygen_task = GNUNET_SCHEDULER_add_with_priority ( GNUNET_SCHEDULER_PRIORITY_URGENT, &update_keys, NULL); - - /* start job to handle completed work */ - { - int fd; - - fd = eventfd (0, - EFD_NONBLOCK | EFD_CLOEXEC); - if (-1 == fd) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "eventfd"); - global_ret = 6; - GNUNET_SCHEDULER_shutdown (); - return; - } - done_signal = GNUNET_NETWORK_socket_box_native (fd); - } - done_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - done_signal, - &handle_done, - NULL); - - /* start crypto workers */ - if (0 == num_workers) - num_workers = sysconf (_SC_NPROCESSORS_CONF); - if (0 == num_workers) - num_workers = 1; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Starting %u crypto workers\n", - num_workers); - workers = GNUNET_new_array (num_workers, - pthread_t); - for (unsigned int i = 0; i<num_workers; i++) - GNUNET_assert (0 == - pthread_create (&workers[i], - NULL, - &sign_worker, - NULL)); } @@ -1660,11 +1085,6 @@ main (int argc, struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_option_timetravel ('T', "timetravel"), - GNUNET_GETOPT_option_uint ('p', - "parallelism", - "NUM_WORKERS", - "number of worker threads to use", - &num_workers), GNUNET_GETOPT_option_absolute_time ('t', "time", "TIMESTAMP", @@ -1672,7 +1092,7 @@ main (int argc, &now_tmp), GNUNET_GETOPT_OPTION_END }; - int ret; + enum GNUNET_GenericReturnValue ret; /* Restrict permissions for the key files that we create. */ (void) umask (S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH); @@ -1689,8 +1109,8 @@ main (int argc, &run, NULL); if (GNUNET_NO == ret) - return 0; + return EXIT_SUCCESS; if (GNUNET_SYSERR == ret) - return 1; + return EXIT_INVALIDARGUMENT; return global_ret; } diff --git a/src/util/taler-exchange-secmod-rsa.c b/src/util/taler-exchange-secmod-rsa.c index 6bca58f88..1884ca98c 100644 --- a/src/util/taler-exchange-secmod-rsa.c +++ b/src/util/taler-exchange-secmod-rsa.c @@ -25,8 +25,7 @@ * - auditor signatures and master signatures are to be kept in the exchange DB, * 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. + * threads for crypto-workers which do the signing in parallel, 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 +40,7 @@ #include "taler_error_codes.h" #include "taler_signatures.h" #include "secmod_common.h" +#include <poll.h> /** @@ -88,7 +88,7 @@ struct DenominationKey /** * Hash of this denomination's public key. */ - struct TALER_DenominationHash h_denom_pub; + struct GNUNET_HashCode h_denom_pub; /** * Time at which this key is supposed to become valid. @@ -96,6 +96,11 @@ struct DenominationKey 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. */ @@ -155,111 +160,11 @@ struct Denomination /** - * Actively worked on client request. - */ -struct WorkItem; - - -/** - * 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 DenominationKey *dk; - - /** - * Signature over @e blinded_msg using @e dk. Result of doing the - * work. Initially zero. - */ - struct TALER_BlindedDenominationSignature denom_sig; - - /** - * Coin_ev value to sign. - */ - void *blinded_msg; - - /** - * Number of bytes in #blinded_msg. - */ - size_t blinded_msg_size; - - /** - * Client address. - */ - struct sockaddr_un addr; - - /** - * Number of bytes used in @e addr. - */ - socklen_t addr_size; - -}; - - -/** * Return value from main(). */ static int global_ret; /** - * 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; - -/** - * 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. */ @@ -272,11 +177,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? */ static char *keydir; @@ -310,335 +210,118 @@ static struct Denomination *denom_tail; static struct GNUNET_CONTAINER_MultiHashMap *keys; /** - * 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. - */ -static struct GNUNET_NETWORK_Handle *done_signal; - -/** - * Set once we are in shutdown and workers should terminate. - */ -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)); - GNUNET_break (GNUNET_OK == - TALER_denom_sign_blinded (&wi->denom_sig, - &wi->dk->denom_priv, - wi->blinded_msg, - wi->blinded_msg_size)); - /* 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; -} - /** - * 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` + * Lock for the keys queue. */ -static void -read_job (void *cls); - +static pthread_mutex_t keys_lock; /** - * Free @a dk. It must already have been removed from #keys and the - * denomination's DLL. - * - * @param[in] dk key to free + * Current key generation. */ -static void -free_dk (struct DenominationKey *dk) -{ - GNUNET_free (dk->filename); - TALER_denom_priv_free (&dk->denom_priv); - TALER_denom_pub_free (&dk->denom_pub); - GNUNET_free (dk); -} +static uint64_t key_gen; /** - * 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 dk 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 dk 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_dk_add (struct TES_Client *client, + const struct DenominationKey *dk) { - for (unsigned int i = 0; i<100; i++) + struct Denomination *denom = dk->denom; + size_t nlen = strlen (denom->section) + 1; + struct TALER_CRYPTO_RsaKeyAvailableNotification *an; + size_t buf_len; + void *buf; + void *p; + size_t tlen; + + buf_len = GNUNET_CRYPTO_rsa_public_key_encode (dk->denom_pub.rsa_public_key, + &buf); + GNUNET_assert (buf_len < UINT16_MAX); + GNUNET_assert (nlen < UINT16_MAX); + tlen = buf_len + nlen + sizeof (*an); + GNUNET_assert (tlen < UINT16_MAX); + an = GNUNET_malloc (tlen); + an->header.size = htons ((uint16_t) tlen); + an->header.type = htons (TALER_HELPER_RSA_MT_AVAIL); + an->pub_size = htons ((uint16_t) buf_len); + an->section_name_len = htons ((uint16_t) nlen); + an->anchor_time = GNUNET_TIME_absolute_hton (dk->anchor); + an->duration_withdraw = GNUNET_TIME_relative_hton (denom->duration_withdraw); + TALER_exchange_secmod_rsa_sign (&dk->h_denom_pub, + denom->section, + dk->anchor, + denom->duration_withdraw, + &TES_smpriv, + &an->secm_sig); + an->secm_pub = TES_smpub; + p = (void *) &an[1]; + memcpy (p, + buf, + buf_len); + GNUNET_free (buf); + memcpy (p + buf_len, + denom->section, + nlen); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Sending RSA denomination key %s (%s)\n", + GNUNET_h2s (&dk->h_denom_pub), + denom->section); + 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); + GNUNET_free (an); + return GNUNET_SYSERR; } - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "sendto"); - return GNUNET_SYSERR; + GNUNET_free (an); + 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 dk being purged. * - * @param cls NULL + * @param[in,out] client the client to notify; possible freed if transmission fails + * @param dk the key to notify @a client about + * @return #GNUNET_OK on success */ -static void -handle_done (void *cls) +static enum GNUNET_GenericReturnValue +notify_client_dk_del (struct TES_Client *client, + const struct DenominationKey *dk) { - uint64_t data; - (void) cls; + struct TALER_CRYPTO_RsaKeyPurgeNotification pn = { + .header.type = htons (TALER_HELPER_RSA_MT_PURGE), + .header.size = htons (sizeof (pn)), + .h_denom_pub = dk->h_denom_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) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Sending RSA denomination expiration %s\n", + GNUNET_h2s (&dk->h_denom_pub)); + 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_DENOMINATION_INVALID == wi->denom_sig.cipher) - { - struct TALER_CRYPTO_SignFailure sf = { - .header.size = htons (sizeof (sf)), - .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE), - .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE) - }; - - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Signing request failed, worker failed to produce signature\n"); - (void) transmit (&wi->addr, - wi->addr_size, - &sf.header); - } - else - { - struct TALER_CRYPTO_SignResponse *sr; - void *buf; - size_t buf_size; - size_t tsize; - - buf_size = GNUNET_CRYPTO_rsa_signature_encode ( - wi->denom_sig.details.blinded_rsa_signature, - &buf); - TALER_blinded_denom_sig_free (&wi->denom_sig); - tsize = sizeof (*sr) + buf_size; - GNUNET_assert (tsize < UINT16_MAX); - sr = GNUNET_malloc (tsize); - sr->header.size = htons (tsize); - sr->header.type = htons (TALER_HELPER_RSA_MT_RES_SIGNATURE); - memcpy (&sr[1], - buf, - buf_size); - GNUNET_free (buf); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending RSA signature\n"); - (void) transmit (&wi->addr, - wi->addr_size, - &sr->header); - GNUNET_free (sr); - } - { - struct DenominationKey *dk = wi->dk; - - dk->rc--; - if ( (0 == dk->rc) && - (dk->purge) ) - free_dk (dk); - } - GNUNET_free (wi->blinded_msg); - 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; } @@ -647,22 +330,22 @@ 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_SignRequest *sr) { struct DenominationKey *dk; - struct WorkItem *wi; const void *blinded_msg = &sr[1]; size_t blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr); + struct GNUNET_CRYPTO_RsaSignature *rsa_signature; + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); dk = GNUNET_CONTAINER_multihashmap_get (keys, - &sr->h_denom_pub.hash); + &sr->h_denom_pub); if (NULL == dk) { struct TALER_CRYPTO_SignFailure sf = { @@ -671,13 +354,12 @@ handle_sign_request (const struct sockaddr_un *addr, .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN) }; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Signing request failed, denomination key %s unknown\n", - GNUNET_h2s (&sr->h_denom_pub.hash)); - (void) transmit (addr, - addr_size, - &sf.header); - return; + GNUNET_h2s (&sr->h_denom_pub)); + return TES_transmit (client->csock, + &sf.header); } if (0 != GNUNET_TIME_absolute_get_remaining (dk->anchor).rel_value_us) @@ -689,141 +371,68 @@ handle_sign_request (const struct sockaddr_un *addr, .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY) }; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Signing request failed, denomination key %s is not yet valid\n", - GNUNET_h2s (&sr->h_denom_pub.hash)); - (void) transmit (addr, - addr_size, - &sf.header); - return; + GNUNET_h2s (&sr->h_denom_pub)); + return TES_transmit (client->csock, + &sf.header); } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received request to sign over %u bytes with key %s\n", (unsigned int) blinded_msg_size, - GNUNET_h2s (&sr->h_denom_pub.hash)); - wi = GNUNET_new (struct WorkItem); - wi->addr = *addr; - wi->addr_size = addr_size; - wi->dk = dk; + GNUNET_h2s (&sr->h_denom_pub)); + GNUNET_assert (dk->rc < UINT_MAX); dk->rc++; - wi->blinded_msg = GNUNET_memdup (blinded_msg, - blinded_msg_size); - wi->blinded_msg_size = blinded_msg_size; - 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 dk becoming available. - * - * @param[in,out] client the client to notify; possible freed if transmission fails - * @param dk the key to notify @a client about - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -notify_client_dk_add (struct Client *client, - const struct DenominationKey *dk) -{ - struct Denomination *denom = dk->denom; - size_t nlen = strlen (denom->section) + 1; - struct TALER_CRYPTO_RsaKeyAvailableNotification *an; - size_t buf_len; - void *buf; - void *p; - size_t tlen; - - buf_len = GNUNET_CRYPTO_rsa_public_key_encode ( - dk->denom_pub.details.rsa_public_key, - &buf); - GNUNET_assert (buf_len < UINT16_MAX); - GNUNET_assert (nlen < UINT16_MAX); - tlen = buf_len + nlen + sizeof (*an); - GNUNET_assert (tlen < UINT16_MAX); - an = GNUNET_malloc (tlen); - an->header.size = htons ((uint16_t) tlen); - an->header.type = htons (TALER_HELPER_RSA_MT_AVAIL); - an->pub_size = htons ((uint16_t) buf_len); - an->section_name_len = htons ((uint16_t) nlen); - an->anchor_time = GNUNET_TIME_absolute_hton (dk->anchor); - an->duration_withdraw = GNUNET_TIME_relative_hton (denom->duration_withdraw); - TALER_exchange_secmod_denom_sign (&dk->h_denom_pub, - denom->section, - dk->anchor, - denom->duration_withdraw, - &smpriv, - &an->secm_sig); - an->secm_pub = smpub; - p = (void *) &an[1]; - memcpy (p, - buf, - buf_len); - GNUNET_free (buf); - memcpy (p + buf_len, - denom->section, - nlen); + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + rsa_signature + = GNUNET_CRYPTO_rsa_sign_blinded (dk->denom_priv.rsa_private_key, + blinded_msg, + blinded_msg_size); + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + GNUNET_assert (dk->rc > 0); + dk->rc--; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + if (NULL == rsa_signature) { - enum GNUNET_GenericReturnValue ret = GNUNET_OK; + struct TALER_CRYPTO_SignFailure sf = { + .header.size = htons (sizeof (sf)), + .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE), + .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE) + }; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending RSA denomination key %s (%s)\n", - GNUNET_h2s (&dk->h_denom_pub.hash), - denom->section); - if (GNUNET_OK != - transmit (&client->addr, - client->addr_size, - &an->header)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %s must have disconnected\n", - client->addr.sun_path); - free_client (client); - ret = GNUNET_SYSERR; - } - GNUNET_free (an); - return ret; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Signing request failed, worker failed to produce signature\n"); + return TES_transmit (client->csock, + &sf.header); } -} - - -/** - * Notify @a client about @a dk being purged. - * - * @param[in,out] client the client to notify; possible freed if transmission fails - * @param dk the key to notify @a client about - * @return #GNUNET_OK on success - */ -static int -notify_client_dk_del (struct Client *client, - const struct DenominationKey *dk) -{ - struct TALER_CRYPTO_RsaKeyPurgeNotification pn = { - .header.type = htons (TALER_HELPER_RSA_MT_PURGE), - .header.size = htons (sizeof (pn)), - .h_denom_pub = dk->h_denom_pub - }; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending RSA denomination expiration %s\n", - GNUNET_h2s (&dk->h_denom_pub.hash)); - if (GNUNET_OK != - transmit (&client->addr, - client->addr_size, - &pn.header)) { + struct TALER_CRYPTO_SignResponse *sr; + void *buf; + size_t buf_size; + size_t tsize; + enum GNUNET_GenericReturnValue ret; + + buf_size = GNUNET_CRYPTO_rsa_signature_encode (rsa_signature, + &buf); + GNUNET_CRYPTO_rsa_signature_free (rsa_signature); + tsize = sizeof (*sr) + buf_size; + GNUNET_assert (tsize < UINT16_MAX); + sr = GNUNET_malloc (tsize); + sr->header.size = htons (tsize); + sr->header.type = htons (TALER_HELPER_RSA_MT_RES_SIGNATURE); + memcpy (&sr[1], + buf, + buf_size); + GNUNET_free (buf); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %s must have disconnected\n", - client->addr.sun_path); - free_client (client); - return GNUNET_SYSERR; + "Sending RSA signature\n"); + ret = TES_transmit (client->csock, + &sr->header); + GNUNET_free (sr); + return ret; } - return GNUNET_OK; } @@ -834,31 +443,35 @@ notify_client_dk_del (struct Client *client, * @param position where in the DLL will the @a dk go * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue setup_key (struct DenominationKey *dk, struct DenominationKey *position) { struct Denomination *denom = dk->denom; - struct TALER_DenominationPrivateKey priv; - struct TALER_DenominationPublicKey pub; + struct GNUNET_CRYPTO_RsaPrivateKey *priv; + struct GNUNET_CRYPTO_RsaPublicKey *pub; size_t buf_size; void *buf; - if (GNUNET_OK != - TALER_denom_priv_create (&priv, - &pub, - TALER_DENOMINATION_RSA, - (unsigned int) denom->rsa_keysize)) + priv = GNUNET_CRYPTO_rsa_private_key_create (denom->rsa_keysize); + if (NULL == priv) { GNUNET_break (0); GNUNET_SCHEDULER_shutdown (); - global_ret = 40; + global_ret = EXIT_FAILURE; return GNUNET_SYSERR; } - buf_size = GNUNET_CRYPTO_rsa_private_key_encode (priv.details.rsa_private_key, + pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv); + if (NULL == pub) + { + GNUNET_break (0); + GNUNET_CRYPTO_rsa_private_key_free (priv); + return GNUNET_SYSERR; + } + buf_size = GNUNET_CRYPTO_rsa_private_key_encode (priv, &buf); - TALER_denom_pub_hash (&pub, - &dk->h_denom_pub); + GNUNET_CRYPTO_rsa_public_key_hash (pub, + &dk->h_denom_pub); GNUNET_asprintf (&dk->filename, "%s/%s/%llu", keydir, @@ -875,30 +488,31 @@ setup_key (struct DenominationKey *dk, "write", dk->filename); GNUNET_free (buf); - TALER_denom_priv_free (&priv); - TALER_denom_pub_free (&pub); + GNUNET_CRYPTO_rsa_private_key_free (priv); + GNUNET_CRYPTO_rsa_public_key_free (pub); return GNUNET_SYSERR; } GNUNET_free (buf); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Setup fresh private key %s at %s in `%s'\n", - GNUNET_h2s (&dk->h_denom_pub.hash), + "Setup fresh private key %s at %s in `%s' (generation #%llu)\n", + GNUNET_h2s (&dk->h_denom_pub), GNUNET_STRINGS_absolute_time_to_string (dk->anchor), - dk->filename); - dk->denom_priv = priv; - dk->denom_pub = pub; - + dk->filename, + (unsigned long long) key_gen); + dk->denom_priv.rsa_private_key = priv; + dk->denom_pub.rsa_public_key = pub; + dk->key_gen = key_gen; if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put ( keys, - &dk->h_denom_pub.hash, + &dk->h_denom_pub, dk, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Duplicate private key created! Terminating.\n"); - TALER_denom_priv_free (&dk->denom_priv); - TALER_denom_pub_free (&dk->denom_pub); + GNUNET_CRYPTO_rsa_private_key_free (dk->denom_priv.rsa_private_key); + GNUNET_CRYPTO_rsa_public_key_free (dk->denom_pub.rsa_public_key); GNUNET_free (dk->filename); GNUNET_free (dk); return GNUNET_SYSERR; @@ -907,62 +521,65 @@ setup_key (struct DenominationKey *dk, denom->keys_tail, position, dk); + 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_dk_add (client, - dk)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Failed to notify client about new key, client dropped\n"); - } - } - } - return GNUNET_OK; +/** + * The withdraw period of a key @a dk has expired. Purge it. + * + * @param[in] dk expired denomination key to purge + */ +static void +purge_key (struct DenominationKey *dk) +{ + if (dk->purge) + return; + if (0 != unlink (dk->filename)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "unlink", + dk->filename); + GNUNET_free (dk->filename); + dk->purge = true; + dk->key_gen = key_gen; } /** - * 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 */ -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_RevokeRequest *rr) { struct DenominationKey *dk; struct DenominationKey *ndk; struct Denomination *denom; + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); dk = GNUNET_CONTAINER_multihashmap_get (keys, - &rr->h_denom_pub.hash); + &rr->h_denom_pub); if (NULL == dk) { + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Revocation request ignored, denomination key %s unknown\n", - GNUNET_h2s (&rr->h_denom_pub.hash)); - return; + GNUNET_h2s (&rr->h_denom_pub)); + return GNUNET_OK; } - /* kill existing key, done first to ensure this always happens */ - if (0 != unlink (dk->filename)) - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "unlink", - dk->filename); + key_gen++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Revoking key %p, bumping generation to %llu\n", + dk, + (unsigned long long) key_gen); + purge_key (dk); + /* Setup replacement key */ denom = dk->denom; ndk = GNUNET_new (struct DenominationKey); @@ -972,171 +589,158 @@ handle_revoke_request (const struct sockaddr_un *addr, setup_key (ndk, dk)) { + 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 */ - dk->purge = true; - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multihashmap_remove ( - keys, - &dk->h_denom_pub.hash, - dk)); - GNUNET_CONTAINER_DLL_remove (denom->keys_head, - denom->keys_tail, - dk); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Revocation of denomination key %s complete\n", - GNUNET_h2s (&rr->h_denom_pub.hash)); - /* 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 +rsa_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_RSA_MT_REQ_SIGN: + if (msize <= sizeof (struct TALER_CRYPTO_SignRequest)) { - nxt = client->next; - if (GNUNET_OK != - notify_client_dk_del (client, - dk)) - 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_SignRequest *) hdr); + case TALER_HELPER_RSA_MT_REQ_REVOKE: + if (msize != sizeof (struct TALER_CRYPTO_RevokeRequest)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; } + return handle_revoke_request ( + client, + (const struct TALER_CRYPTO_RevokeRequest *) hdr); + default: + GNUNET_break_op (0); + return GNUNET_SYSERR; } - if (0 == dk->rc) - free_dk (dk); } -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 +rsa_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_break_op (0); - free_client (client); - return; + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + for (struct Denomination *denom = denom_head; + NULL != denom; + denom = denom->next) + { + for (struct DenominationKey *dk = denom->keys_head; + NULL != dk; + dk = dk->next) + { + if (GNUNET_OK != + notify_client_dk_add (client, + dk)) + { + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Client %p must have disconnected\n", + client); + return GNUNET_SYSERR; + } + } } - switch (ntohs (hdr->type)) + client->key_gen = key_gen; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); { - case TALER_HELPER_RSA_MT_REQ_INIT: - if (ntohs (hdr->size) != sizeof (struct GNUNET_MessageHeader)) + struct GNUNET_MessageHeader synced = { + .type = htons (TALER_HELPER_RSA_SYNCED), + .size = htons (sizeof (synced)) + }; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Sending RSA SYNCED message to %p\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 +rsa_update_client_keys (struct TES_Client *client) +{ + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + for (struct Denomination *denom = denom_head; + NULL != denom; + denom = denom->next) + { + for (struct DenominationKey *key = denom->keys_head; + NULL != key; + key = key->next) { - 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 Denomination *denom = denom_head; - NULL != denom; - denom = denom->next) + if (key->key_gen <= client->key_gen) + continue; + if (key->purge) { - for (struct DenominationKey *dk = denom->keys_head; - NULL != dk; - dk = dk->next) + if (GNUNET_OK != + notify_client_dk_del (client, + key)) { - if (GNUNET_OK != - notify_client_dk_add (client, - dk)) - { - /* client died, skip the rest */ - client = NULL; - break; - } + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + return GNUNET_SYSERR; } - if (NULL == client) - break; } - if (NULL != client) + else { - struct GNUNET_MessageHeader synced = { - .type = htons (TALER_HELPER_RSA_SYNCED), - .size = htons (sizeof (synced)) - }; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending RSA SYNCED message\n"); if (GNUNET_OK != - transmit (&client->addr, - client->addr_size, - &synced)) + notify_client_dk_add (client, + key)) { - 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_RSA_MT_REQ_SIGN: - if (ntohs (hdr->size) <= sizeof (struct TALER_CRYPTO_SignRequest)) - { - GNUNET_break_op (0); - return; - } - handle_sign_request (&addr, - addr_size, - (const struct TALER_CRYPTO_SignRequest *) buf); - break; - case TALER_HELPER_RSA_MT_REQ_REVOKE: - if (ntohs (hdr->size) != sizeof (struct TALER_CRYPTO_RevokeRequest)) - { - GNUNET_break_op (0); - return; - } - handle_revoke_request (&addr, - addr_size, - (const struct TALER_CRYPTO_RevokeRequest *) buf); - break; - default: - GNUNET_break_op (0); - return; } + client->key_gen = key_gen; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + return GNUNET_OK; } @@ -1147,7 +751,7 @@ read_job (void *cls) * @param now current time to use (to get many keys to use the exact same time) * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue create_key (struct Denomination *denom, struct GNUNET_TIME_Absolute now) { @@ -1174,12 +778,12 @@ create_key (struct Denomination *denom, setup_key (dk, denom->keys_tail)) { + GNUNET_break (0); GNUNET_free (dk); GNUNET_SCHEDULER_shutdown (); - global_ret = 42; + global_ret = EXIT_FAILURE; return GNUNET_SYSERR; } - return GNUNET_OK; } @@ -1195,72 +799,24 @@ create_key (struct Denomination *denom, static struct GNUNET_TIME_Absolute denomination_action_time (const struct Denomination *denom) { - if (NULL == denom->keys_head) + struct DenominationKey *head = denom->keys_head; + struct DenominationKey *tail = denom->keys_tail; + struct GNUNET_TIME_Absolute tt; + + if (NULL == head) return GNUNET_TIME_UNIT_ZERO_ABS; + tt = GNUNET_TIME_absolute_subtract ( + GNUNET_TIME_absolute_subtract ( + GNUNET_TIME_absolute_add (tail->anchor, + denom->duration_withdraw), + lookahead_sign), + overlap_duration); + if (head->rc > 0) + return tt; /* head expiration does not count due to rc > 0 */ return GNUNET_TIME_absolute_min ( - GNUNET_TIME_absolute_add (denom->keys_head->anchor, + GNUNET_TIME_absolute_add (head->anchor, denom->duration_withdraw), - GNUNET_TIME_absolute_subtract ( - GNUNET_TIME_absolute_subtract ( - GNUNET_TIME_absolute_add (denom->keys_tail->anchor, - denom->duration_withdraw), - lookahead_sign), - overlap_duration)); -} - - -/** - * The withdraw period of a key @a dk has expired. Purge it. - * - * @param[in] dk expired denomination key to purge and free - */ -static void -purge_key (struct DenominationKey *dk) -{ - struct Denomination *denom = dk->denom; - struct Client *nxt; - - for (struct Client *client = clients_head; - NULL != client; - client = nxt) - { - nxt = client->next; - if (GNUNET_OK != - notify_client_dk_del (client, - dk)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Failed to notify client about purged key, client dropped\n"); - } - } - GNUNET_CONTAINER_DLL_remove (denom->keys_head, - denom->keys_tail, - dk); - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multihashmap_remove (keys, - &dk->h_denom_pub.hash, - dk)); - if (0 != unlink (dk->filename)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "unlink", - dk->filename); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Purged expired private key `%s'\n", - dk->filename); - } - GNUNET_free (dk->filename); - if (0 != dk->rc) - { - /* delay until all signing threads are done with this key */ - dk->purge = true; - return; - } - TALER_denom_priv_free (&dk->denom_priv); - GNUNET_free (dk); + tt); } @@ -1271,37 +827,69 @@ purge_key (struct DenominationKey *dk) * * @param[in,out] denom denomination to update material for * @param now current time to use (to get many keys to use the exact same time) + * @param[in,out] wake set to true if we should wake the clients + * @return #GNUNET_OK on success */ -static void +static enum GNUNET_GenericReturnValue update_keys (struct Denomination *denom, - struct GNUNET_TIME_Absolute now) + struct GNUNET_TIME_Absolute now, + bool *wake) { /* create new denomination keys */ while ( (NULL == denom->keys_tail) || - (0 == - GNUNET_TIME_absolute_get_remaining ( - GNUNET_TIME_absolute_subtract ( - GNUNET_TIME_absolute_subtract ( - GNUNET_TIME_absolute_add (denom->keys_tail->anchor, - denom->duration_withdraw), - lookahead_sign), - overlap_duration)).rel_value_us) ) + GNUNET_TIME_absolute_is_past ( + GNUNET_TIME_absolute_subtract ( + GNUNET_TIME_absolute_subtract ( + GNUNET_TIME_absolute_add (denom->keys_tail->anchor, + denom->duration_withdraw), + lookahead_sign), + overlap_duration)) ) + { + if (! *wake) + { + key_gen++; + *wake = true; + } if (GNUNET_OK != create_key (denom, now)) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create keys for `%s'\n", - denom->section); - return; + GNUNET_break (0); + global_ret = EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return GNUNET_SYSERR; } + } /* remove expired denomination keys */ while ( (NULL != denom->keys_head) && - (0 == - GNUNET_TIME_absolute_get_remaining - (GNUNET_TIME_absolute_add (denom->keys_head->anchor, - denom->duration_withdraw)).rel_value_us) ) - purge_key (denom->keys_head); + GNUNET_TIME_absolute_is_past + (GNUNET_TIME_absolute_add (denom->keys_head->anchor, + denom->duration_withdraw)) ) + { + struct DenominationKey *key = denom->keys_head; + struct DenominationKey *nxt = key->next; + + if (0 != key->rc) + break; /* later */ + GNUNET_CONTAINER_DLL_remove (denom->keys_head, + denom->keys_tail, + key); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_remove ( + keys, + &key->h_denom_pub, + key)); + if ( (! key->purge) && + (0 != unlink (key->filename)) ) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "unlink", + key->filename); + GNUNET_free (key->filename); + GNUNET_CRYPTO_rsa_private_key_free (key->denom_priv.rsa_private_key); + GNUNET_CRYPTO_rsa_public_key_free (key->denom_pub.rsa_public_key); + GNUNET_free (key); + key = nxt; + } /* Update position of 'denom' in #denom_head DLL: sort by action time */ { @@ -1321,12 +909,12 @@ update_keys (struct Denomination *denom, break; before = pos; } - GNUNET_CONTAINER_DLL_insert_after (denom_head, denom_tail, before, denom); } + return GNUNET_OK; } @@ -1340,16 +928,24 @@ update_denominations (void *cls) { struct Denomination *denom; struct GNUNET_TIME_Absolute now; + bool wake = false; (void) cls; keygen_task = NULL; now = GNUNET_TIME_absolute_get (); (void) GNUNET_TIME_round_abs (&now); + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); do { denom = denom_head; - update_keys (denom, - now); + if (GNUNET_OK != + update_keys (denom, + now, + &wake)) + return; } while (denom != denom_head); + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + if (wake) + TES_wake_clients (); keygen_task = GNUNET_SCHEDULER_add_at (denomination_action_time (denom), &update_denominations, NULL); @@ -1370,7 +966,7 @@ parse_key (struct Denomination *denom, const void *buf, size_t buf_size) { - struct TALER_DenominationPrivateKey priv; + struct GNUNET_CRYPTO_RsaPrivateKey *priv; char *anchor_s; char dummy; unsigned long long anchor_ll; @@ -1405,11 +1001,9 @@ parse_key (struct Denomination *denom, filename); return; } - priv.cipher = TALER_DENOMINATION_RSA; - priv.details.rsa_private_key - = GNUNET_CRYPTO_rsa_private_key_decode (buf, - buf_size); - if (NULL == priv.details.rsa_private_key) + priv = GNUNET_CRYPTO_rsa_private_key_decode (buf, + buf_size); + if (NULL == priv) { /* Parser failure. */ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -1419,34 +1013,38 @@ parse_key (struct Denomination *denom, } { - struct TALER_DenominationPublicKey pub; + struct GNUNET_CRYPTO_RsaPublicKey *pub; struct DenominationKey *dk; struct DenominationKey *before; - TALER_denom_priv_to_pub (&priv, - (struct TALER_AgeMask) { .mask = 0 }, /* FIXME-Oec */ - &pub); + pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv); + if (NULL == pub) + { + GNUNET_break (0); + GNUNET_CRYPTO_rsa_private_key_free (priv); + return; + } dk = GNUNET_new (struct DenominationKey); - dk->denom_priv = priv; + dk->denom_priv.rsa_private_key = priv; dk->denom = denom; dk->anchor = anchor; dk->filename = GNUNET_strdup (filename); - TALER_denom_pub_hash (&pub, - &dk->h_denom_pub); - dk->denom_pub = pub; + GNUNET_CRYPTO_rsa_public_key_hash (pub, + &dk->h_denom_pub); + dk->denom_pub.rsa_public_key = pub; if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put ( keys, - &dk->h_denom_pub.hash, + &dk->h_denom_pub, dk, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Duplicate private key %s detected in file `%s'. Skipping.\n", - GNUNET_h2s (&dk->h_denom_pub.hash), + GNUNET_h2s (&dk->h_denom_pub), filename); - TALER_denom_priv_free (&priv); - TALER_denom_pub_free (&pub); + GNUNET_CRYPTO_rsa_private_key_free (priv); + GNUNET_CRYPTO_rsa_public_key_free (pub); GNUNET_free (dk); return; } @@ -1465,7 +1063,7 @@ parse_key (struct Denomination *denom, dk); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Imported key %s from `%s'\n", - GNUNET_h2s (&dk->h_denom_pub.hash), + GNUNET_h2s (&dk->h_denom_pub), filename); } } @@ -1477,8 +1075,9 @@ parse_key (struct Denomination *denom, * * @param[in,out] cls a `struct Denomiantion` * @param filename name of a file in the directory + * @return #GNUNET_OK (always, continue to iterate) */ -static int +static enum GNUNET_GenericReturnValue import_key (void *cls, const char *filename) { @@ -1591,18 +1190,20 @@ import_key (void *cls, * Parse configuration for denomination type parameters. Also determines * our anchor by looking at the existing denominations of the same type. * + * @param cfg configuration to use * @param ct section in the configuration file giving the denomination type parameters * @param[out] denom set to the denomination parameters from the configuration * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid */ -static int -parse_denomination_cfg (const char *ct, +static enum GNUNET_GenericReturnValue +parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *ct, struct Denomination *denom) { unsigned long long rsa_keysize; if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, + GNUNET_CONFIGURATION_get_value_time (cfg, ct, "DURATION_WITHDRAW", &denom->duration_withdraw)) @@ -1623,7 +1224,7 @@ parse_denomination_cfg (const char *ct, return GNUNET_SYSERR; } if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (kcfg, + GNUNET_CONFIGURATION_get_value_number (cfg, ct, "RSA_KEYSIZE", &rsa_keysize)) @@ -1653,6 +1254,12 @@ parse_denomination_cfg (const char *ct, */ struct LoadContext { + + /** + * Configuration to use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + /** * Current time to use. */ @@ -1661,7 +1268,7 @@ struct LoadContext /** * Status, to be set to #GNUNET_SYSERR on failure */ - int ret; + enum GNUNET_GenericReturnValue ret; }; @@ -1678,6 +1285,7 @@ load_denominations (void *cls, { struct LoadContext *ctx = cls; struct Denomination *denom; + bool wake; if ( (0 != strncasecmp (denomination_alias, "coin_", @@ -1688,7 +1296,8 @@ load_denominations (void *cls, return; /* not a denomination type definition */ denom = GNUNET_new (struct Denomination); if (GNUNET_OK != - parse_denomination_cfg (denomination_alias, + parse_denomination_cfg (ctx->cfg, + denomination_alias, denom)) { ctx->ret = GNUNET_SYSERR; @@ -1716,20 +1325,22 @@ load_denominations (void *cls, denom_tail, denom); update_keys (denom, - ctx->now); + ctx->now, + &wake); } /** - * Load the various duration values from #kcfg. + * Load the various duration values from @a cfg * + * @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-rsa", "OVERLAP_DURATION", &overlap_duration)) @@ -1742,7 +1353,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-rsa", "LOOKAHEAD_SIGN", &lookahead_sign)) @@ -1766,51 +1377,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<num_workers; i++) - GNUNET_assert (0 == pthread_join (workers[i], - NULL)); - } - if (NULL != done_signal) - { - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (done_signal)); - done_signal = NULL; - } } @@ -1828,10 +1400,14 @@ run (void *cls, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { + static struct TES_Callbacks cb = { + .dispatch = rsa_work_dispatch, + .updater = rsa_update_client_keys, + .init = rsa_client_init + }; (void) cls; (void) args; (void) cfgfile; - kcfg = cfg; if (now.abs_value_us != now_tmp.abs_value_us) { /* The user gave "--now", use it! */ @@ -1843,48 +1419,8 @@ run (void *cls, now = GNUNET_TIME_absolute_get (); } GNUNET_TIME_round_abs (&now); - - { - char *pfn; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (kcfg, - "taler-exchange-secmod-rsa", - "SM_PRIV_KEY", - &pfn)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", - "SM_PRIV_KEY"); - global_ret = 1; - return; - } - if (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_key_from_file (pfn, - GNUNET_YES, - &smpriv.eddsa_priv)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", - "SM_PRIV_KEY", - "Could not use file to persist private key"); - GNUNET_free (pfn); - global_ret = 1; - return; - } - GNUNET_free (pfn); - GNUNET_CRYPTO_eddsa_key_get_public (&smpriv.eddsa_priv, - &smpub.eddsa_pub); - } - if (GNUNET_OK != - load_durations ()) - { - global_ret = 1; - return; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (kcfg, + GNUNET_CONFIGURATION_get_value_filename (cfg, "taler-exchange-secmod-rsa", "KEY_DIR", &keydir)) @@ -1892,92 +1428,41 @@ run (void *cls, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "taler-exchange-secmod-rsa", "KEY_DIR"); - global_ret = 1; + global_ret = EXIT_NOTCONFIGURED; return; } - - /* Create client directory and set permissions. */ - { - char *client_dir; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (kcfg, - "taler-exchange-secmod-rsa", - "CLIENT_DIR", - &client_dir)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", - "CLIENT_DIR"); - global_ret = 3; - return; - } - - if (GNUNET_OK != GNUNET_DISK_directory_create (client_dir)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Can't create client directory (%s)\n", - client_dir); - global_ret = 3; - return; - } - /* Set sticky group bit, so that clients will be writeable by the current service. */ - if (0 != chmod (client_dir, - S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP - | S_ISGID)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Can't set permissions for client directory (%s)\n", - client_dir); - global_ret = 3; - return; - } - - GNUNET_free (client_dir); - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (kcfg, - "taler-exchange-secmod-rsa", - "UNIXPATH", - &unixpath)) + load_durations (cfg)) { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", - "UNIXPATH"); - global_ret = 3; + global_ret = EXIT_NOTCONFIGURED; return; } - - GNUNET_assert (NULL != unixpath); - unix_sock = TES_open_socket (unixpath); - - if (NULL == unix_sock) - { - GNUNET_free (unixpath); - global_ret = 2; + global_ret = TES_listen_start (cfg, + "taler-exchange-secmod-rsa", + &cb); + if (0 != global_ret) return; - } - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - /* Load denominations */ keys = GNUNET_CONTAINER_multihashmap_create (65536, GNUNET_YES); { struct LoadContext lc = { + .cfg = cfg, .ret = GNUNET_OK, - .now = GNUNET_TIME_absolute_get () + .now = now }; (void) GNUNET_TIME_round_abs (&lc.now); - GNUNET_CONFIGURATION_iterate_sections (kcfg, + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + GNUNET_CONFIGURATION_iterate_sections (cfg, &load_denominations, &lc); + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); if (GNUNET_OK != lc.ret) { - global_ret = 4; + global_ret = EXIT_FAILURE; GNUNET_SCHEDULER_shutdown (); return; } @@ -1986,60 +1471,16 @@ run (void *cls, { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No denominations configured\n"); - global_ret = 5; + global_ret = EXIT_NOTCONFIGURED; GNUNET_SCHEDULER_shutdown (); return; } - - /* start job to accept incoming requests on 'sock' */ - read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - unix_sock, - &read_job, - NULL); - /* start job to keep keys up-to-date; MUST be run before the #read_task, + /* start job to keep keys up-to-date; MUST be run before the #listen_task, hence with priority. */ keygen_task = GNUNET_SCHEDULER_add_with_priority ( GNUNET_SCHEDULER_PRIORITY_URGENT, &update_denominations, NULL); - - /* start job to handle completed work */ - { - int fd; - - fd = eventfd (0, - EFD_NONBLOCK | EFD_CLOEXEC); - if (-1 == fd) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "eventfd"); - global_ret = 6; - GNUNET_SCHEDULER_shutdown (); - return; - } - done_signal = GNUNET_NETWORK_socket_box_native (fd); - } - done_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - done_signal, - &handle_done, - NULL); - - /* start crypto workers */ - if (0 == num_workers) - num_workers = sysconf (_SC_NPROCESSORS_CONF); - if (0 == num_workers) - num_workers = 1; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Starting %u crypto workers\n", - num_workers); - workers = GNUNET_new_array (num_workers, - pthread_t); - for (unsigned int i = 0; i<num_workers; i++) - GNUNET_assert (0 == - pthread_create (&workers[i], - NULL, - &sign_worker, - NULL)); } @@ -2057,11 +1498,6 @@ main (int argc, struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_option_timetravel ('T', "timetravel"), - GNUNET_GETOPT_option_uint ('p', - "parallelism", - "NUM_WORKERS", - "number of worker threads to use", - &num_workers), GNUNET_GETOPT_option_absolute_time ('t', "time", "TIMESTAMP", @@ -2069,7 +1505,7 @@ main (int argc, &now_tmp), GNUNET_GETOPT_OPTION_END }; - int ret; + enum GNUNET_GenericReturnValue ret; /* Restrict permissions for the key files that we create. */ (void) umask (S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH); @@ -2086,8 +1522,8 @@ main (int argc, &run, NULL); if (GNUNET_NO == ret) - return 0; + return EXIT_SUCCESS; if (GNUNET_SYSERR == ret) - return 1; + return EXIT_INVALIDARGUMENT; return global_ret; } diff --git a/src/util/taler-exchange-secmod-rsa.h b/src/util/taler-exchange-secmod-rsa.h index cf439e261..146b6948f 100644 --- a/src/util/taler-exchange-secmod-rsa.h +++ b/src/util/taler-exchange-secmod-rsa.h @@ -102,7 +102,7 @@ struct TALER_CRYPTO_RsaKeyPurgeNotification /** * Hash of the public key of the purged RSA key. */ - struct TALER_DenominationHash h_denom_pub; + struct GNUNET_HashCode h_denom_pub; }; @@ -125,7 +125,7 @@ struct TALER_CRYPTO_SignRequest /** * Hash of the public key of the RSA key to use for the signature. */ - struct TALER_DenominationHash h_denom_pub; + struct GNUNET_HashCode h_denom_pub; /* followed by message to sign */ }; @@ -149,7 +149,7 @@ struct TALER_CRYPTO_RevokeRequest /** * Hash of the public key of the revoked RSA key. */ - struct TALER_DenominationHash h_denom_pub; + struct GNUNET_HashCode h_denom_pub; }; diff --git a/src/util/test_helper_eddsa.c b/src/util/test_helper_eddsa.c index 4b44f6041..48c14491a 100644 --- a/src/util/test_helper_eddsa.c +++ b/src/util/test_helper_eddsa.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2020 Taler Systems SA + (C) 2020, 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 @@ -36,7 +36,7 @@ /** * How many iterations of the successful signing test should we run - * during the benchmark phase? + * during the test phase? */ #define NUM_SIGN_TESTS 3 @@ -46,6 +46,11 @@ */ #define NUM_SIGN_PERFS 100 +/** + * How many parallel clients should we use for the parallel + * benchmark? (> 500 may cause problems with the max open FD number limit). + */ +#define NUM_CORES 8 /** * Number of keys currently in #keys. @@ -270,7 +275,8 @@ test_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh) * @return 0 on success */ static int -perf_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh) +perf_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh, + const char *type) { struct GNUNET_CRYPTO_EccSignaturePurpose purpose = { .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST), @@ -303,8 +309,69 @@ perf_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh) delay); } /* for j */ fprintf (stderr, - "%u (sequential) signature operations took %s\n", - (unsigned int) NUM_SIGN_TESTS, + "%u (%s) signature operations took %s\n", + (unsigned int) NUM_SIGN_PERFS, + type, + GNUNET_STRINGS_relative_time_to_string (duration, + GNUNET_YES)); + return 0; +} + + +/** + * Parallel signing logic. + * + * @param esh handle to the helper + * @return 0 on success + */ +static int +par_signing (struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_TIME_Absolute start; + struct GNUNET_TIME_Relative duration; + pid_t pids[NUM_CORES]; + struct TALER_CRYPTO_ExchangeSignHelper *esh; + + memset (keys, + 0, + sizeof (keys)); + num_keys = 0; + start = GNUNET_TIME_absolute_get (); + for (unsigned int i = 0; i<NUM_CORES; i++) + { + pids[i] = fork (); + GNUNET_assert (-1 != pids[i]); + if (0 == pids[i]) + { + int ret; + + esh = TALER_CRYPTO_helper_esign_connect (cfg, + &key_cb, + NULL); + if (NULL == esh) + { + GNUNET_break (0); + exit (EXIT_FAILURE); + } + ret = perf_signing (esh, + "parallel"); + TALER_CRYPTO_helper_esign_disconnect (esh); + exit (ret); + } + } + for (unsigned int i = 0; i<NUM_CORES; i++) + { + int wstatus; + + GNUNET_assert (pids[i] == + waitpid (pids[i], + &wstatus, + 0)); + } + duration = GNUNET_TIME_absolute_get_duration (start); + fprintf (stderr, + "%u (parallel) signature operations took %s (total real time)\n", + (unsigned int) NUM_SIGN_PERFS * NUM_CORES, GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES)); return 0; @@ -319,10 +386,10 @@ run_test (void) { struct GNUNET_CONFIGURATION_Handle *cfg; struct TALER_CRYPTO_ExchangeSignHelper *esh; + int ret; struct timespec req = { .tv_nsec = 250000000 }; - int ret; cfg = GNUNET_CONFIGURATION_create (); if (GNUNET_OK != @@ -334,54 +401,47 @@ run_test (void) } /* wait for helper to start and give us keys */ - fprintf (stderr, "Waiting for helper client directory to become available "); - for (unsigned int i = 0; i<1000; i++) + fprintf (stderr, "Waiting for helper to start ... "); + for (unsigned int i = 0; i<100; i++) { + nanosleep (&req, + NULL); esh = TALER_CRYPTO_helper_esign_connect (cfg, &key_cb, NULL); if (NULL != esh) break; - nanosleep (&req, NULL); fprintf (stderr, "."); } - GNUNET_CONFIGURATION_destroy (cfg); if (NULL == esh) { - GNUNET_break (0); + fprintf (stderr, + "\nFAILED: timeout trying to connect to helper\n"); + GNUNET_CONFIGURATION_destroy (cfg); return 1; } - fprintf (stderr, " done.\n"); - - /* wait for helper to start and give us keys */ - fprintf (stderr, "Waiting for helper to start "); - for (unsigned int i = 0; i<1000; i++) - { - TALER_CRYPTO_helper_esign_poll (esh); - if (0 != num_keys) - break; - nanosleep (&req, NULL); - fprintf (stderr, "."); - } if (0 == num_keys) { fprintf (stderr, - "\nFAILED: timeout trying to connect to helper\n"); + "\nFAILED: no keys returend by helper\n"); TALER_CRYPTO_helper_esign_disconnect (esh); + GNUNET_CONFIGURATION_destroy (cfg); return 1; } fprintf (stderr, - "\nOK: Helper ready (%u keys)\n", + " Done (%u keys)\n", num_keys); - ret = 0; if (0 == ret) ret = test_revocation (esh); if (0 == ret) ret = test_signing (esh); if (0 == ret) - ret = perf_signing (esh); + ret = perf_signing (esh, + "sequential"); TALER_CRYPTO_helper_esign_disconnect (esh); + if (0 == ret) + ret = par_signing (cfg); /* clean up our state */ for (unsigned int i = 0; i<MAX_KEYS; i++) if (keys[i].valid) @@ -390,6 +450,7 @@ run_test (void) GNUNET_assert (num_keys > 0); num_keys--; } + GNUNET_CONFIGURATION_destroy (cfg); return ret; } @@ -408,7 +469,7 @@ main (int argc, (void) argc; (void) argv; GNUNET_log_setup ("test-helper-eddsa", - "INFO", + "WARNING", NULL); GNUNET_OS_init (TALER_project_data_default ()); libexec_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR); @@ -424,7 +485,7 @@ main (int argc, "-c", "test_helper_eddsa.conf", "-L", - "INFO", + "WARNING", NULL); if (NULL == helper) { diff --git a/src/util/test_helper_rsa.c b/src/util/test_helper_rsa.c index e4c0bf6fd..97844001d 100644 --- a/src/util/test_helper_rsa.c +++ b/src/util/test_helper_rsa.c @@ -22,11 +22,12 @@ #include "taler_util.h" /** - * Configuration has 1 minute duration and 5 minutes lookahead, so - * we should never have more than 6 active keys, plus for during - * key expiration / revocation. + * Configuration has 1 minute duration and 5 minutes lookahead, but + * we do not get 'revocations' for expired keys. So this must be + * large enough to deal with key rotation during the runtime of + * the benchmark. */ -#define MAX_KEYS 7 +#define MAX_KEYS 1024 /** * How many random key revocations should we test? @@ -38,6 +39,17 @@ */ #define NUM_SIGN_TESTS 5 +/** + * How many iterations of the successful signing test should we run + * during the benchmark phase? + */ +#define NUM_SIGN_PERFS 100 + +/** + * How many parallel clients should we use for the parallel + * benchmark? (> 500 may cause problems with the max open FD number limit). + */ +#define NUM_CORES 8 /** * Number of keys currently in #keys. @@ -62,7 +74,7 @@ struct KeyData /** * Hash of the public key. */ - struct TALER_DenominationHash h_denom_pub; + struct GNUNET_HashCode h_denom_pub; /** * Full public key. @@ -110,7 +122,7 @@ key_cb (void *cls, const char *section_name, struct GNUNET_TIME_Absolute start_time, struct GNUNET_TIME_Relative validity_duration, - const struct TALER_DenominationHash *h_denom_pub, + const struct GNUNET_HashCode *h_denom_pub, const struct TALER_DenominationPublicKey *denom_pub, const struct TALER_SecurityModulePublicKeyP *sm_pub, const struct TALER_SecurityModuleSignatureP *sm_sig) @@ -119,7 +131,7 @@ key_cb (void *cls, (void) sm_sig; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Key notification about key %s in `%s'\n", - GNUNET_h2s (&h_denom_pub->hash), + GNUNET_h2s (h_denom_pub), section_name); if (0 == validity_duration.rel_value_us) { @@ -133,7 +145,8 @@ key_cb (void *cls, { keys[i].valid = false; keys[i].revoked = false; - TALER_denom_pub_free (&keys[i].denom_pub); + GNUNET_CRYPTO_rsa_public_key_free (keys[i].denom_pub.rsa_public_key); + keys[i].denom_pub.rsa_public_key = NULL; GNUNET_assert (num_keys > 0); num_keys--; found = true; @@ -154,9 +167,8 @@ key_cb (void *cls, keys[i].h_denom_pub = *h_denom_pub; keys[i].start_time = start_time; keys[i].validity_duration = validity_duration; - keys[i].denom_pub = *denom_pub; - TALER_denom_pub_deep_copy (&keys[i].denom_pub, - denom_pub); + keys[i].denom_pub.rsa_public_key + = GNUNET_CRYPTO_rsa_public_key_dup (denom_pub->rsa_public_key); num_keys++; return; } @@ -199,7 +211,7 @@ test_revocation (struct TALER_CRYPTO_DenominationHelper *dh) keys[j].revoked = true; fprintf (stderr, "Revoking key %s ...", - GNUNET_h2s (&keys[j].h_denom_pub.hash)); + GNUNET_h2s (&keys[j].h_denom_pub)); TALER_CRYPTO_helper_denom_revoke (dh, &keys[j].h_denom_pub); for (unsigned int k = 0; k<1000; k++) @@ -235,35 +247,42 @@ test_revocation (struct TALER_CRYPTO_DenominationHelper *dh) static int test_signing (struct TALER_CRYPTO_DenominationHelper *dh) { - struct TALER_BlindedDenominationSignature ds; + struct TALER_DenominationSignature ds; enum TALER_ErrorCode ec; bool success = false; - struct TALER_PlanchetSecretsP ps; - struct TALER_CoinPubHash c_hash; - - TALER_planchet_setup_random (&ps); + struct GNUNET_HashCode m_hash; + struct GNUNET_CRYPTO_RsaBlindingKeySecret bks; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &bks, + sizeof (bks)); + GNUNET_CRYPTO_hash ("Hello", + strlen ("Hello"), + &m_hash); for (unsigned int i = 0; i<MAX_KEYS; i++) { if (! keys[i].valid) continue; { - struct TALER_PlanchetDetail pd; + void *buf; + size_t buf_size; GNUNET_assert (GNUNET_YES == - TALER_planchet_prepare (&keys[i].denom_pub, - &ps, - &c_hash, - &pd)); + TALER_rsa_blind (&m_hash, + &bks, + keys[i].denom_pub.rsa_public_key, + &buf, + &buf_size)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Requesting signature over %u bytes with key %s\n", - (unsigned int) pd.coin_ev_size, - GNUNET_h2s (&keys[i].h_denom_pub.hash)); + (unsigned int) buf_size, + GNUNET_h2s (&keys[i].h_denom_pub)); ds = TALER_CRYPTO_helper_denom_sign (dh, &keys[i].h_denom_pub, - pd.coin_ev, - pd.coin_ev_size, + buf, + buf_size, &ec); - GNUNET_free (pd.coin_ev); + GNUNET_free (buf); } switch (ec) { @@ -283,33 +302,32 @@ test_signing (struct TALER_CRYPTO_DenominationHelper *dh) return 5; } { - struct TALER_DenominationSignature rs; + struct GNUNET_CRYPTO_RsaSignature *rs; - if (GNUNET_OK != - TALER_denom_sig_unblind (&rs, - &ds, - &ps.blinding_key, - &keys[i].denom_pub)) + rs = TALER_rsa_unblind (ds.rsa_signature, + &bks, + keys[i].denom_pub.rsa_public_key); + if (NULL == rs) { GNUNET_break (0); return 6; } - TALER_blinded_denom_sig_free (&ds); + GNUNET_CRYPTO_rsa_signature_free (ds.rsa_signature); if (GNUNET_OK != - TALER_denom_pub_verify (&keys[i].denom_pub, - &rs, - &c_hash)) + GNUNET_CRYPTO_rsa_verify (&m_hash, + rs, + keys[i].denom_pub.rsa_public_key)) { /* signature invalid */ GNUNET_break (0); - TALER_denom_sig_free (&rs); + GNUNET_CRYPTO_rsa_signature_free (rs); return 7; } - TALER_denom_sig_free (&rs); + GNUNET_CRYPTO_rsa_signature_free (rs); } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received valid signature for key %s\n", - GNUNET_h2s (&keys[i].h_denom_pub.hash)); + GNUNET_h2s (&keys[i].h_denom_pub)); success = true; break; case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY: @@ -344,24 +362,27 @@ test_signing (struct TALER_CRYPTO_DenominationHelper *dh) /* check signing does not work if the key is unknown */ { - struct TALER_DenominationHash rnd; + struct GNUNET_HashCode rnd; + struct TALER_DenominationSignature ds; GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &rnd, sizeof (rnd)); - (void) TALER_CRYPTO_helper_denom_sign (dh, - &rnd, - "Hello", - strlen ("Hello"), - &ec); + ds = TALER_CRYPTO_helper_denom_sign (dh, + &rnd, + "Hello", + strlen ("Hello"), + &ec); if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) { + if (TALER_EC_NONE == ec) + GNUNET_CRYPTO_rsa_signature_free (ds.rsa_signature); GNUNET_break (0); return 17; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Signing with invalid key %s failed as desired\n", - GNUNET_h2s (&rnd.hash)); + GNUNET_h2s (&rnd)); } return 0; } @@ -374,18 +395,25 @@ test_signing (struct TALER_CRYPTO_DenominationHelper *dh) * @return 0 on success */ static int -perf_signing (struct TALER_CRYPTO_DenominationHelper *dh) +perf_signing (struct TALER_CRYPTO_DenominationHelper *dh, + const char *type) { - struct TALER_BlindedDenominationSignature ds; + struct TALER_DenominationSignature ds; enum TALER_ErrorCode ec; + struct GNUNET_HashCode m_hash; + struct GNUNET_CRYPTO_RsaBlindingKeySecret bks; struct GNUNET_TIME_Relative duration; - struct TALER_PlanchetSecretsP ps; - TALER_planchet_setup_random (&ps); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &bks, + sizeof (bks)); + GNUNET_CRYPTO_hash ("Hello", + strlen ("Hello"), + &m_hash); duration = GNUNET_TIME_UNIT_ZERO; - for (unsigned int j = 0; j<NUM_SIGN_TESTS;) + TALER_CRYPTO_helper_denom_poll (dh); + for (unsigned int j = 0; j<NUM_SIGN_PERFS;) { - TALER_CRYPTO_helper_denom_poll (dh); for (unsigned int i = 0; i<MAX_KEYS; i++) { if (! keys[i].valid) @@ -397,14 +425,15 @@ perf_signing (struct TALER_CRYPTO_DenominationHelper *dh) keys[i].validity_duration.rel_value_us) continue; { - struct TALER_CoinPubHash c_hash; - struct TALER_PlanchetDetail pd; + void *buf; + size_t buf_size; GNUNET_assert (GNUNET_YES == - TALER_planchet_prepare (&keys[i].denom_pub, - &ps, - &c_hash, - &pd)); + TALER_rsa_blind (&m_hash, + &bks, + keys[i].denom_pub.rsa_public_key, + &buf, + &buf_size)); /* use this key as long as it works */ while (1) { @@ -413,26 +442,83 @@ perf_signing (struct TALER_CRYPTO_DenominationHelper *dh) ds = TALER_CRYPTO_helper_denom_sign (dh, &keys[i].h_denom_pub, - pd.coin_ev, - pd.coin_ev_size, + buf, + buf_size, &ec); if (TALER_EC_NONE != ec) break; delay = GNUNET_TIME_absolute_get_duration (start); duration = GNUNET_TIME_relative_add (duration, delay); - TALER_blinded_denom_sig_free (&ds); + GNUNET_CRYPTO_rsa_signature_free (ds.rsa_signature); j++; - if (NUM_SIGN_TESTS == j) + if (NUM_SIGN_PERFS <= j) break; } - GNUNET_free (pd.coin_ev); + GNUNET_free (buf); } } /* for i */ } /* for j */ fprintf (stderr, - "%u (sequential) signature operations took %s\n", - (unsigned int) NUM_SIGN_TESTS, + "%u (%s) signature operations took %s\n", + (unsigned int) NUM_SIGN_PERFS, + type, + GNUNET_STRINGS_relative_time_to_string (duration, + GNUNET_YES)); + return 0; +} + + +/** + * Parallel signing logic. + * + * @param esh handle to the helper + * @return 0 on success + */ +static int +par_signing (struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_TIME_Absolute start; + struct GNUNET_TIME_Relative duration; + pid_t pids[NUM_CORES]; + struct TALER_CRYPTO_DenominationHelper *dh; + + start = GNUNET_TIME_absolute_get (); + for (unsigned int i = 0; i<NUM_CORES; i++) + { + pids[i] = fork (); + memset (keys, + 0, + sizeof (keys)); + num_keys = 0; + GNUNET_assert (-1 != pids[i]); + if (0 == pids[i]) + { + int ret; + + dh = TALER_CRYPTO_helper_denom_connect (cfg, + &key_cb, + NULL); + GNUNET_assert (NULL != dh); + ret = perf_signing (dh, + "parallel"); + TALER_CRYPTO_helper_denom_disconnect (dh); + exit (ret); + } + } + for (unsigned int i = 0; i<NUM_CORES; i++) + { + int wstatus; + + GNUNET_assert (pids[i] == + waitpid (pids[i], + &wstatus, + 0)); + } + duration = GNUNET_TIME_absolute_get_duration (start); + fprintf (stderr, + "%u (parallel) signature operations took %s (total real time)\n", + (unsigned int) NUM_SIGN_PERFS * NUM_CORES, GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES)); return 0; @@ -461,62 +547,57 @@ run_test (void) return 77; } - fprintf (stderr, "Waiting for helper client directory to become available "); - for (unsigned int i = 0; i<1000; i++) + fprintf (stderr, "Waiting for helper to start ... "); + for (unsigned int i = 0; i<100; i++) { + nanosleep (&req, + NULL); dh = TALER_CRYPTO_helper_denom_connect (cfg, &key_cb, NULL); if (NULL != dh) break; - nanosleep (&req, NULL); fprintf (stderr, "."); } - GNUNET_CONFIGURATION_destroy (cfg); if (NULL == dh) { - GNUNET_break (0); + fprintf (stderr, + "\nFAILED: timeout trying to connect to helper\n"); + GNUNET_CONFIGURATION_destroy (cfg); return 1; } - fprintf (stderr, " done.\n"); - - /* wait for helper to start and give us keys */ - fprintf (stderr, "Waiting for helper to start "); - for (unsigned int i = 0; i<1000; i++) - { - TALER_CRYPTO_helper_denom_poll (dh); - if (0 != num_keys) - break; - nanosleep (&req, NULL); - fprintf (stderr, "."); - } if (0 == num_keys) { fprintf (stderr, "\nFAILED: timeout trying to connect to helper\n"); TALER_CRYPTO_helper_denom_disconnect (dh); + GNUNET_CONFIGURATION_destroy (cfg); return 1; } fprintf (stderr, - "\nOK: Helper ready (%u keys)\n", + " Done (%u keys)\n", num_keys); - ret = 0; if (0 == ret) ret = test_revocation (dh); if (0 == ret) ret = test_signing (dh); if (0 == ret) - ret = perf_signing (dh); + ret = perf_signing (dh, + "sequential"); TALER_CRYPTO_helper_denom_disconnect (dh); + if (0 == ret) + ret = par_signing (cfg); /* clean up our state */ for (unsigned int i = 0; i<MAX_KEYS; i++) if (keys[i].valid) { - TALER_denom_pub_free (&keys[i].denom_pub); + GNUNET_CRYPTO_rsa_public_key_free (keys[i].denom_pub.rsa_public_key); + keys[i].denom_pub.rsa_public_key = NULL; GNUNET_assert (num_keys > 0); num_keys--; } + GNUNET_CONFIGURATION_destroy (cfg); return ret; } @@ -535,7 +616,7 @@ main (int argc, (void) argc; (void) argv; GNUNET_log_setup ("test-helper-rsa", - "INFO", + "WARNING", NULL); GNUNET_OS_init (TALER_project_data_default ()); libexec_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR); @@ -551,7 +632,7 @@ main (int argc, "-c", "test_helper_rsa.conf", "-L", - "INFO", + "WARNING", NULL); if (NULL == helper) { |