diff options
Diffstat (limited to 'src/util/taler-exchange-secmod-rsa.c')
-rw-r--r-- | src/util/taler-exchange-secmod-rsa.c | 2020 |
1 files changed, 1028 insertions, 992 deletions
diff --git a/src/util/taler-exchange-secmod-rsa.c b/src/util/taler-exchange-secmod-rsa.c index 2aabaddc0..c80e2e3c4 100644 --- a/src/util/taler-exchange-secmod-rsa.c +++ b/src/util/taler-exchange-secmod-rsa.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2020 Taler Systems SA + Copyright (C) 2014-2022 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 @@ -20,13 +20,12 @@ * * Key design points: * - EVERY thread of the exchange will have its own pair of connections to the - * crypto helpers. This way, every threat will also have its own /keys state + * crypto helpers. This way, every thread will also have its own /keys state * and avoid the need to synchronize on those. * - 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 @@ -37,10 +36,10 @@ #include "taler-exchange-secmod-rsa.h" #include <gcrypt.h> #include <pthread.h> -#include <sys/eventfd.h> #include "taler_error_codes.h" #include "taler_signatures.h" #include "secmod_common.h" +#include <poll.h> /** @@ -78,22 +77,32 @@ struct DenominationKey /** * The private key of the denomination. */ - struct TALER_DenominationPrivateKey denom_priv; + struct GNUNET_CRYPTO_RsaPrivateKey *denom_priv; /** * The public key of the denomination. */ - struct TALER_DenominationPublicKey denom_pub; + struct GNUNET_CRYPTO_RsaPublicKey *denom_pub; + + /** + * Message to transmit to clients to introduce this public key. + */ + struct TALER_CRYPTO_RsaKeyAvailableNotification *an; /** * Hash of this denomination's public key. */ - struct GNUNET_HashCode h_denom_pub; + struct TALER_RsaPubHashP h_rsa; /** * Time at which this key is supposed to become valid. */ - struct GNUNET_TIME_Absolute anchor; + struct GNUNET_TIME_Timestamp anchor; + + /** + * Generation when this key was created or revoked. + */ + uint64_t key_gen; /** * Reference counter. Counts the number of threads that are @@ -155,131 +164,155 @@ struct Denomination /** - * Actively worked on client request. + * A semaphore. */ -struct WorkItem; +struct Semaphore +{ + /** + * Mutex for the semaphore. + */ + pthread_mutex_t mutex; + + /** + * Condition variable for the semaphore. + */ + pthread_cond_t cv; + + /** + * Counter of the semaphore. + */ + unsigned int ctr; +}; /** - * Information we keep for a client connected to us. + * Job in a batch sign request. */ -struct Client -{ +struct BatchJob; +/** + * Handle for a thread that does work in batch signing. + */ +struct Worker +{ /** * Kept in a DLL. */ - struct Client *next; + struct Worker *prev; /** * Kept in a DLL. */ - struct Client *prev; + struct Worker *next; /** - * Client address. + * Job this worker should do next. */ - struct sockaddr_un addr; + struct BatchJob *job; /** - * Number of bytes used in @e addr. + * Semaphore to signal the worker that a job is available. */ - socklen_t addr_size; - -}; - - -struct WorkItem -{ + struct Semaphore sem; /** - * Kept in a DLL. + * Handle for this thread. */ - struct WorkItem *next; + pthread_t pt; /** - * Kept in a DLL. + * Set to true if the worker should terminate. */ - struct WorkItem *prev; + bool do_shutdown; +}; - /** - * Key to be used for this operation. - */ - struct DenominationKey *dk; +/** + * Job in a batch sign request. + */ +struct BatchJob +{ /** - * RSA signature over @e blinded_msg using @e dk. Result of doing the - * work. Initially NULL. + * Request we are working on. */ - struct GNUNET_CRYPTO_RsaSignature *rsa_signature; + const struct TALER_CRYPTO_SignRequest *sr; /** - * Coin_ev value to sign. + * Thread doing the work. */ - void *blinded_msg; + struct Worker *worker; /** - * Number of bytes in #blinded_msg. + * Result with the signature. */ - size_t blinded_msg_size; + struct GNUNET_CRYPTO_RsaSignature *rsa_signature; /** - * Client address. + * Semaphore to signal that the job is finished. */ - struct sockaddr_un addr; + struct Semaphore sem; /** - * Number of bytes used in @e addr. + * Computation status. */ - socklen_t addr_size; + enum TALER_ErrorCode ec; }; /** - * Return value from main(). + * Head of DLL of workers ready for more work. */ -static int global_ret; +static struct Worker *worker_head; /** - * Private key of this security module. Used to sign denomination key - * announcements. + * Tail of DLL of workers ready for more work. */ -static struct TALER_SecurityModulePrivateKeyP smpriv; +static struct Worker *worker_tail; /** - * Public key of this security module. + * Lock for manipulating the worker DLL. */ -static struct TALER_SecurityModulePublicKeyP smpub; +static pthread_mutex_t worker_lock; /** - * Number of worker threads to use. Default (0) is to use one per CPU core - * available. - * Length of the #workers array. + * Total number of workers that were started. */ -static unsigned int num_workers; +static unsigned int workers; + +/** + * Semaphore used to grab a worker. + */ +static struct Semaphore worker_sem; + +/** + * Return value from main(). + */ +static int global_ret; /** * Time when the key update is executed. * Either the actual current time, or a pretended time. */ -static struct GNUNET_TIME_Absolute now; +static struct GNUNET_TIME_Timestamp now; /** * The time for the key update, as passed by the user * on the command line. */ -static struct GNUNET_TIME_Absolute now_tmp; +static struct GNUNET_TIME_Timestamp now_tmp; /** - * Handle to the exchange's configuration + * Where do we store the keys? */ -static const struct GNUNET_CONFIGURATION_Handle *kcfg; +static char *keydir; /** - * Where do we store the keys? + * Name of the configuration section prefix to use. Usually either "taler-exchange" or + * "donau". The actual configuration section will then be + * "$SECTION-secmod-rsa". */ -static char *keydir; +static char *section; /** * How much should coin creation (@e duration_withdraw) duration overlap @@ -310,529 +343,523 @@ static struct Denomination *denom_tail; static struct GNUNET_CONTAINER_MultiHashMap *keys; /** - * Our listen socket. + * Task run to generate new keys. */ -static struct GNUNET_NETWORK_Handle *unix_sock; +static struct GNUNET_SCHEDULER_Task *keygen_task; /** - * Path where we are listening. + * Lock for the keys queue. */ -static char *unixpath; +static pthread_mutex_t keys_lock; /** - * Task run to accept new inbound connections. + * Current key generation. */ -static struct GNUNET_SCHEDULER_Task *read_task; +static uint64_t key_gen; /** - * Task run to generate new keys. + * Number of workers to launch. Note that connections to + * exchanges are NOT workers. */ -static struct GNUNET_SCHEDULER_Task *keygen_task; +static unsigned int max_workers = 16; -/** - * Head of DLL of clients connected to us. - */ -static struct Client *clients_head; /** - * Tail of DLL of clients connected to us. + * Generate the announcement message for @a dk. + * + * @param[in,out] dk denomination key to generate the announcement for */ -static struct Client *clients_tail; +static void +generate_response (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; -/** - * Head of DLL with pending signing operations. - */ -static struct WorkItem *work_head; + buf_len = GNUNET_CRYPTO_rsa_public_key_encode (dk->denom_pub, + &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_timestamp_hton (dk->anchor); + an->duration_withdraw = GNUNET_TIME_relative_hton (denom->duration_withdraw); + TALER_exchange_secmod_rsa_sign (&dk->h_rsa, + denom->section, + dk->anchor, + denom->duration_withdraw, + &TES_smpriv, + &an->secm_sig); + an->secm_pub = TES_smpub; + p = (void *) &an[1]; + GNUNET_memcpy (p, + buf, + buf_len); + GNUNET_free (buf); + GNUNET_memcpy (p + buf_len, + denom->section, + nlen); + dk->an = an; +} -/** - * Tail of DLL with pending signing operations. - */ -static struct WorkItem *work_tail; /** - * Lock for the work queue. + * Do the actual signing work. + * + * @param h_rsa key to sign with + * @param bm blinded message to sign + * @param[out] rsa_signaturep set to the RSA signature + * @return #TALER_EC_NONE on success */ -static pthread_mutex_t work_lock; +static enum TALER_ErrorCode +do_sign (const struct TALER_RsaPubHashP *h_rsa, + const struct GNUNET_CRYPTO_RsaBlindedMessage *bm, + struct GNUNET_CRYPTO_RsaSignature **rsa_signaturep) +{ + struct DenominationKey *dk; + struct GNUNET_CRYPTO_RsaSignature *rsa_signature; + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); -/** - * Condition variable for the semaphore of the work queue. - */ -static pthread_cond_t work_cond = PTHREAD_COND_INITIALIZER; + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + dk = GNUNET_CONTAINER_multihashmap_get (keys, + &h_rsa->hash); + if (NULL == dk) + { + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Signing request failed, denomination key %s unknown\n", + GNUNET_h2s (&h_rsa->hash)); + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; + } + if (GNUNET_TIME_absolute_is_future (dk->anchor.abs_time)) + { + /* it is 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 (&h_rsa->hash)); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY; + } -/** - * Number of items in the work queue. Also used as the semaphore counter. - */ -static unsigned long long work_counter; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received request to sign over %u bytes with key %s\n", + (unsigned int) bm->blinded_msg_size, + GNUNET_h2s (&h_rsa->hash)); + GNUNET_assert (dk->rc < UINT_MAX); + dk->rc++; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + rsa_signature + = GNUNET_CRYPTO_rsa_sign_blinded (dk->denom_priv, + bm); + 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) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Signing request failed, worker failed to produce signature\n"); + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } -/** - * Head of DLL with completed signing operations. - */ -static struct WorkItem *done_head; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Sending RSA signature after %s\n", + GNUNET_TIME_relative2s ( + GNUNET_TIME_absolute_get_duration (now), + GNUNET_YES)); + *rsa_signaturep = rsa_signature; + return TALER_EC_NONE; +} -/** - * Tail of DLL with completed signing operations. - */ -static struct WorkItem *done_tail; /** - * Lock for the done queue. + * Generate error response that signing failed. + * + * @param client client to send response to + * @param ec error code to include + * @return #GNUNET_OK on success */ -static pthread_mutex_t done_lock; +static enum GNUNET_GenericReturnValue +fail_sign (struct TES_Client *client, + enum TALER_ErrorCode ec) +{ + struct TALER_CRYPTO_SignFailure sf = { + .header.size = htons (sizeof (sf)), + .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE), + .ec = htonl (ec) + }; -/** - * Task waiting for work to be done. - */ -static struct GNUNET_SCHEDULER_Task *done_task; + return TES_transmit (client->csock, + &sf.header); +} -/** - * 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. + * Generate signature response. + * + * @param client client to send response to + * @param[in] rsa_signature signature to send, freed by this function + * @return #GNUNET_OK on success */ -static volatile bool in_shutdown; +static enum GNUNET_GenericReturnValue +send_signature (struct TES_Client *client, + struct GNUNET_CRYPTO_RsaSignature *rsa_signature) +{ + struct TALER_CRYPTO_SignResponse *sr; + void *buf; + size_t buf_size; + size_t tsize; + enum GNUNET_GenericReturnValue ret; -/** - * Array of #num_workers sign_worker() threads. - */ -static pthread_t *workers; + 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); + GNUNET_memcpy (&sr[1], + buf, + buf_size); + GNUNET_free (buf); + ret = TES_transmit (client->csock, + &sr->header); + GNUNET_free (sr); + return ret; +} /** - * Main function of a worker thread that signs. + * Handle @a client request @a sr to create signature. Create the + * signature using the respective key and return the result to + * the client. * - * @param cls NULL - * @return NULL + * @param client the client making the request + * @param sr the request details + * @return #GNUNET_OK on success */ -static void * -sign_worker (void *cls) +static enum GNUNET_GenericReturnValue +handle_sign_request (struct TES_Client *client, + const struct TALER_CRYPTO_SignRequest *sr) { - (void) cls; - GNUNET_assert (0 == pthread_mutex_lock (&work_lock)); - while (! in_shutdown) - { - struct WorkItem *wi; + struct GNUNET_CRYPTO_RsaBlindedMessage bm = { + .blinded_msg = (void *) &sr[1], + .blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr) + }; + struct GNUNET_CRYPTO_RsaSignature *rsa_signature; + enum TALER_ErrorCode ec; - 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)); - wi->rsa_signature - = GNUNET_CRYPTO_rsa_sign_blinded (wi->dk->denom_priv.rsa_private_key, - 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)); + ec = do_sign (&sr->h_rsa, + &bm, + &rsa_signature); + if (TALER_EC_NONE != ec) + { + return fail_sign (client, + ec); } - GNUNET_assert (0 == - pthread_mutex_unlock (&work_lock)); - return NULL; + return send_signature (client, + rsa_signature); } /** - * Free @a client, releasing all (remaining) state. + * Initialize a semaphore @a sem with a value of @a val. * - * @param[in] client data to free + * @param[out] sem semaphore to initialize + * @param val initial value of the semaphore */ static void -free_client (struct Client *client) +sem_init (struct Semaphore *sem, + unsigned int val) { - GNUNET_CONTAINER_DLL_remove (clients_head, - clients_tail, - client); - GNUNET_free (client); + GNUNET_assert (0 == + pthread_mutex_init (&sem->mutex, + NULL)); + GNUNET_assert (0 == + pthread_cond_init (&sem->cv, + NULL)); + sem->ctr = val; } /** - * Function run to read incoming requests from a client. + * Decrement semaphore, blocks until this is possible. * - * @param cls the `struct Client` + * @param[in,out] sem semaphore to decrement */ static void -read_job (void *cls); +sem_down (struct Semaphore *sem) +{ + GNUNET_assert (0 == pthread_mutex_lock (&sem->mutex)); + while (0 == sem->ctr) + { + pthread_cond_wait (&sem->cv, + &sem->mutex); + } + sem->ctr--; + GNUNET_assert (0 == pthread_mutex_unlock (&sem->mutex)); +} /** - * Free @a dk. It must already have been removed from #keys and the - * denomination's DLL. + * Increment semaphore, blocks until this is possible. * - * @param[in] dk key to free + * @param[in,out] sem semaphore to decrement */ static void -free_dk (struct DenominationKey *dk) +sem_up (struct Semaphore *sem) { - GNUNET_free (dk->filename); - 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); + GNUNET_assert (0 == pthread_mutex_lock (&sem->mutex)); + sem->ctr++; + GNUNET_assert (0 == pthread_mutex_unlock (&sem->mutex)); + pthread_cond_signal (&sem->cv); } /** - * 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. + * Release resources used by @a sem. * - * @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 + * @param[in] sem semaphore to release (except the memory itself) */ -static int -transmit (const struct sockaddr_un *addr, - socklen_t addr_size, - const struct GNUNET_MessageHeader *hdr) +static void +sem_done (struct Semaphore *sem) { - 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; + GNUNET_break (0 == pthread_cond_destroy (&sem->cv)); + GNUNET_break (0 == pthread_mutex_destroy (&sem->mutex)); } /** - * Process completed tasks that are in the #done_head queue, sending - * the result back to the client (and resuming the client). + * Main logic of a worker thread. Grabs work, does it, + * grabs more work. * - * @param cls NULL + * @param cls a `struct Worker *` + * @returns cls */ -static void -handle_done (void *cls) +static void * +worker (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 (NULL == wi->rsa_signature) + struct Worker *w = cls; + + while (true) + { + GNUNET_assert (0 == pthread_mutex_lock (&worker_lock)); + GNUNET_CONTAINER_DLL_insert (worker_head, + worker_tail, + w); + GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock)); + sem_up (&worker_sem); + sem_down (&w->sem); + if (w->do_shutdown) + break; { - 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) + struct BatchJob *bj = w->job; + const struct TALER_CRYPTO_SignRequest *sr = bj->sr; + struct GNUNET_CRYPTO_RsaBlindedMessage bm = { + .blinded_msg = (void *) &sr[1], + .blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr) }; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Signing request failed, worker failed to produce signature\n"); - (void) transmit (&wi->addr, - wi->addr_size, - &sf.header); + bj->ec = do_sign (&sr->h_rsa, + &bm, + &bj->rsa_signature); + sem_up (&bj->sem); + w->job = NULL; } - else - { - struct TALER_CRYPTO_SignResponse *sr; - void *buf; - size_t buf_size; - size_t tsize; - - buf_size = GNUNET_CRYPTO_rsa_signature_encode (wi->rsa_signature, - &buf); - GNUNET_CRYPTO_rsa_signature_free (wi->rsa_signature); - wi->rsa_signature = NULL; - 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_assert (0 == pthread_mutex_unlock (&done_lock)); - + return w; } /** - * Handle @a client request @a sr to create signature. Create the - * signature using the respective key and return the result to - * the client. + * Start batch job @a bj to sign @a sr. * - * @param addr address of the client making the request - * @param addr_size number of bytes in @a addr - * @param sr the request details + * @param sr signature request to answer + * @param[out] bj job data structure */ static void -handle_sign_request (const struct sockaddr_un *addr, - socklen_t addr_size, - const struct TALER_CRYPTO_SignRequest *sr) +start_job (const struct TALER_CRYPTO_SignRequest *sr, + struct BatchJob *bj) { - struct DenominationKey *dk; - struct WorkItem *wi; - const void *blinded_msg = &sr[1]; - size_t blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr); + sem_init (&bj->sem, + 0); + bj->sr = sr; + sem_down (&worker_sem); + GNUNET_assert (0 == pthread_mutex_lock (&worker_lock)); + bj->worker = worker_head; + GNUNET_CONTAINER_DLL_remove (worker_head, + worker_tail, + bj->worker); + GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock)); + bj->worker->job = bj; + sem_up (&bj->worker->sem); +} - dk = GNUNET_CONTAINER_multihashmap_get (keys, - &sr->h_denom_pub); - if (NULL == dk) - { - struct TALER_CRYPTO_SignFailure sf = { - .header.size = htons (sizeof (sr)), - .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE), - .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN) - }; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Signing request failed, denomination key %s unknown\n", - GNUNET_h2s (&sr->h_denom_pub)); - (void) transmit (addr, - addr_size, - &sf.header); - return; - } - if (0 != - GNUNET_TIME_absolute_get_remaining (dk->anchor).rel_value_us) +/** + * Finish a job @a bj for a @a client. + * + * @param client who made the request + * @param[in,out] bj job to finish + */ +static void +finish_job (struct TES_Client *client, + struct BatchJob *bj) +{ + sem_down (&bj->sem); + sem_done (&bj->sem); + if (TALER_EC_NONE != bj->ec) { - /* it is too early */ - struct TALER_CRYPTO_SignFailure sf = { - .header.size = htons (sizeof (sr)), - .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE), - .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY) - }; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Signing request failed, denomination key %s is not yet valid\n", - GNUNET_h2s (&sr->h_denom_pub)); - (void) transmit (addr, - addr_size, - &sf.header); + fail_sign (client, + bj->ec); return; } - - 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)); - wi = GNUNET_new (struct WorkItem); - wi->addr = *addr; - wi->addr_size = addr_size; - wi->dk = dk; - 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)); + GNUNET_assert (NULL != bj->rsa_signature); + send_signature (client, + bj->rsa_signature); + bj->rsa_signature = NULL; /* freed in send_signature */ } /** - * Notify @a client about @a dk becoming available. + * Handle @a client request @a sr to create a batch of signature. Creates the + * signatures using the respective key and return the results to the client. * - * @param[in,out] client the client to notify; possible freed if transmission fails - * @param dk the key to notify @a client about + * @param client the client making the request + * @param bsr the request details * @return #GNUNET_OK on success */ -static int -notify_client_dk_add (struct Client *client, - const struct DenominationKey *dk) +static enum GNUNET_GenericReturnValue +handle_batch_sign_request (struct TES_Client *client, + const struct TALER_CRYPTO_BatchSignRequest *bsr) { - 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; + uint32_t bs = ntohl (bsr->batch_size); + uint16_t size = ntohs (bsr->header.size) - sizeof (*bsr); + const void *off = (const void *) &bsr[1]; + unsigned int idx = 0; + struct BatchJob jobs[bs]; + bool failure = false; - 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, - &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); + if (bs > TALER_MAX_FRESH_COINS) { - int ret = GNUNET_OK; + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + while ( (bs > 0) && + (size > sizeof (struct TALER_CRYPTO_SignRequest)) ) + { + const struct TALER_CRYPTO_SignRequest *sr = off; + uint16_t s = ntohs (sr->header.size); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending RSA denomination key %s (%s)\n", - GNUNET_h2s (&dk->h_denom_pub), - denom->section); - if (GNUNET_OK != - transmit (&client->addr, - client->addr_size, - &an->header)) + if (s > size) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %s must have disconnected\n", - client->addr.sun_path); - free_client (client); - ret = GNUNET_SYSERR; + failure = true; + bs = idx; + break; } - GNUNET_free (an); - return ret; + start_job (sr, + &jobs[idx++]); + off += s; + size -= s; + } + GNUNET_break_op (0 == size); + bs = GNUNET_MIN (bs, + idx); + for (unsigned int i = 0; i<bs; i++) + finish_job (client, + &jobs[i]); + if (failure) + { + struct TALER_CRYPTO_SignFailure sf = { + .header.size = htons (sizeof (sf)), + .header.type = htons (TALER_HELPER_RSA_MT_RES_BATCH_FAILURE), + .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE) + }; + + GNUNET_break (0); + return TES_transmit (client->csock, + &sf.header); } + return GNUNET_OK; } /** - * Notify @a client about @a dk being purged. + * Start worker thread for batch processing. * - * @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) +static enum GNUNET_GenericReturnValue +start_worker (void) { - 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)); - if (GNUNET_OK != - transmit (&client->addr, - client->addr_size, - &pn.header)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %s must have disconnected\n", - client->addr.sun_path); - free_client (client); + struct Worker *w; + + w = GNUNET_new (struct Worker); + sem_init (&w->sem, + 0); + if (0 != pthread_create (&w->pt, + NULL, + &worker, + w)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "pthread_create"); + GNUNET_free (w); return GNUNET_SYSERR; } + workers++; return GNUNET_OK; } /** + * Stop all worker threads. + */ +static void +stop_workers (void) +{ + while (workers > 0) + { + struct Worker *w; + void *result; + + sem_down (&worker_sem); + GNUNET_assert (0 == pthread_mutex_lock (&worker_lock)); + w = worker_head; + GNUNET_CONTAINER_DLL_remove (worker_head, + worker_tail, + w); + GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock)); + w->do_shutdown = true; + sem_up (&w->sem); + pthread_join (w->pt, + &result); + GNUNET_assert (result == w); + sem_done (&w->sem); + GNUNET_free (w); + workers--; + } +} + + +/** * Initialize key material for denomination key @a dk (also on disk). * * @param[in,out] dk denomination key to compute key material for * @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) { @@ -847,7 +874,7 @@ setup_key (struct DenominationKey *dk, { GNUNET_break (0); GNUNET_SCHEDULER_shutdown (); - global_ret = 40; + global_ret = EXIT_FAILURE; return GNUNET_SYSERR; } pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv); @@ -860,12 +887,12 @@ setup_key (struct DenominationKey *dk, buf_size = GNUNET_CRYPTO_rsa_private_key_encode (priv, &buf); GNUNET_CRYPTO_rsa_public_key_hash (pub, - &dk->h_denom_pub); + &dk->h_rsa.hash); GNUNET_asprintf (&dk->filename, "%s/%s/%llu", keydir, denom->section, - (unsigned long long) (dk->anchor.abs_value_us + (unsigned long long) (dk->anchor.abs_time.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us)); if (GNUNET_OK != GNUNET_DISK_fn_write (dk->filename, @@ -883,25 +910,28 @@ setup_key (struct DenominationKey *dk, } 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), - GNUNET_STRINGS_absolute_time_to_string (dk->anchor), - dk->filename); - dk->denom_priv.rsa_private_key = priv; - dk->denom_pub.rsa_public_key = pub; - + "Setup fresh private key %s at %s in `%s' (generation #%llu)\n", + GNUNET_h2s (&dk->h_rsa.hash), + GNUNET_TIME_timestamp2s (dk->anchor), + dk->filename, + (unsigned long long) key_gen); + dk->denom_priv = priv; + dk->denom_pub = pub; + dk->key_gen = key_gen; + generate_response (dk); if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put ( keys, - &dk->h_denom_pub, + &dk->h_rsa.hash, dk, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Duplicate private key created! Terminating.\n"); - 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_CRYPTO_rsa_private_key_free (dk->denom_priv); + GNUNET_CRYPTO_rsa_public_key_free (dk->denom_pub); GNUNET_free (dk->filename); + GNUNET_free (dk->an); GNUNET_free (dk); return GNUNET_SYSERR; } @@ -909,62 +939,74 @@ 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; + (void) client; + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); dk = GNUNET_CONTAINER_multihashmap_get (keys, - &rr->h_denom_pub); + &rr->h_rsa.hash); 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)); - return; + GNUNET_h2s (&rr->h_rsa.hash)); + return GNUNET_OK; + } + if (dk->purge) + { + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Revocation request ignored, denomination key %s already revoked\n", + GNUNET_h2s (&rr->h_rsa.hash)); + 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 %s, bumping generation to %llu\n", + GNUNET_h2s (&rr->h_rsa.hash), + (unsigned long long) key_gen); + purge_key (dk); + /* Setup replacement key */ denom = dk->denom; ndk = GNUNET_new (struct DenominationKey); @@ -974,171 +1016,233 @@ 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, - 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)); - /* 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)) + { + 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); + case TALER_HELPER_RSA_MT_REQ_BATCH_SIGN: + if (msize <= sizeof (struct TALER_CRYPTO_BatchSignRequest)) { - 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_batch_sign_request ( + client, + (const struct TALER_CRYPTO_BatchSignRequest *) 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; + size_t obs = 0; + char *buf; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Initializing new client %p\n", + client); + 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) + { + GNUNET_assert (obs + ntohs (dk->an->header.size) + > obs); + obs += ntohs (dk->an->header.size); + } } - if (0 == buf_size) + buf = GNUNET_malloc (obs); + obs = 0; + for (struct Denomination *denom = denom_head; + NULL != denom; + denom = denom->next) { - return; + for (struct DenominationKey *dk = denom->keys_head; + NULL != dk; + dk = dk->next) + { + GNUNET_memcpy (&buf[obs], + dk->an, + ntohs (dk->an->header.size)); + GNUNET_assert (obs + ntohs (dk->an->header.size) + > obs); + obs += ntohs (dk->an->header.size); + } } - if (buf_size < sizeof (struct GNUNET_MessageHeader)) + client->key_gen = key_gen; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + if (GNUNET_OK != + TES_transmit_raw (client->csock, + obs, + buf)) { - GNUNET_break_op (0); - return; + GNUNET_free (buf); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Client %p must have disconnected\n", + client); + return GNUNET_SYSERR; } - hdr = (const struct GNUNET_MessageHeader *) buf; - if (ntohs (hdr->size) != buf_size) + GNUNET_free (buf); { - GNUNET_break_op (0); - free_client (client); - return; + 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 (0); + return GNUNET_SYSERR; + } } - switch (ntohs (hdr->type)) - { - case TALER_HELPER_RSA_MT_REQ_INIT: - if (ntohs (hdr->size) != sizeof (struct GNUNET_MessageHeader)) + 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) +{ + size_t obs = 0; + char *buf; + enum GNUNET_GenericReturnValue ret; + + 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) { - GNUNET_break_op (0); - return; + if (key->key_gen <= client->key_gen) + continue; + if (key->purge) + obs += sizeof (struct TALER_CRYPTO_RsaKeyPurgeNotification); + else + obs += ntohs (key->an->header.size); } + } + if (0 == obs) + { + /* nothing to do */ + client->key_gen = key_gen; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + return GNUNET_OK; + } + buf = GNUNET_malloc (obs); + obs = 0; + 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_add (client, - dk)) - { - /* client died, skip the rest */ - client = NULL; - break; - } - } - if (NULL == client) - break; - } - if (NULL != client) - { - struct GNUNET_MessageHeader synced = { - .type = htons (TALER_HELPER_RSA_SYNCED), - .size = htons (sizeof (synced)) + struct TALER_CRYPTO_RsaKeyPurgeNotification pn = { + .header.type = htons (TALER_HELPER_RSA_MT_PURGE), + .header.size = htons (sizeof (pn)), + .h_rsa = key->h_rsa }; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending RSA SYNCED message\n"); - 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_memcpy (&buf[obs], + &pn, + sizeof (pn)); + GNUNET_assert (obs + sizeof (pn) + > obs); + obs += sizeof (pn); + } + else + { + GNUNET_memcpy (&buf[obs], + key->an, + ntohs (key->an->header.size)); + GNUNET_assert (obs + ntohs (key->an->header.size) + > obs); + obs += ntohs (key->an->header.size); } } - 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)); + ret = TES_transmit_raw (client->csock, + obs, + buf); + GNUNET_free (buf); + return ret; } @@ -1149,25 +1253,25 @@ 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) + struct GNUNET_TIME_Timestamp now) { struct DenominationKey *dk; - struct GNUNET_TIME_Absolute anchor; + struct GNUNET_TIME_Timestamp anchor; - if (NULL == denom->keys_tail) - { - anchor = now; - } - else + anchor = now; + // FIXME: round down to multiple of 'anchor_round' value from configuration + if (NULL != denom->keys_tail) { - anchor = GNUNET_TIME_absolute_add (denom->keys_tail->anchor, - GNUNET_TIME_relative_subtract ( - denom->duration_withdraw, - overlap_duration)); - if (now.abs_value_us > anchor.abs_value_us) - anchor = now; + struct GNUNET_TIME_Absolute abs; + + abs = GNUNET_TIME_absolute_add (denom->keys_tail->anchor.abs_time, + GNUNET_TIME_relative_subtract ( + denom->duration_withdraw, + overlap_duration)); + if (GNUNET_TIME_absolute_cmp (now.abs_time, <, abs)) + anchor = GNUNET_TIME_absolute_to_timestamp (abs); } dk = GNUNET_new (struct DenominationKey); dk->denom = denom; @@ -1176,12 +1280,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; } @@ -1197,72 +1301,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.abs_time, + 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.abs_time, 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, - 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; - } - GNUNET_CRYPTO_rsa_private_key_free (dk->denom_priv.rsa_private_key); - GNUNET_free (dk); + tt); } @@ -1273,37 +1329,83 @@ 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_Timestamp now, + bool *wake) { /* create new denomination keys */ + if (NULL != denom->keys_tail) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Updating keys of denomination `%s', last key %s valid for another %s\n", + denom->section, + GNUNET_h2s (&denom->keys_tail->h_rsa.hash), + GNUNET_TIME_relative2s ( + GNUNET_TIME_absolute_get_remaining ( + GNUNET_TIME_absolute_subtract ( + GNUNET_TIME_absolute_add ( + denom->keys_tail->anchor.abs_time, + denom->duration_withdraw), + overlap_duration)), + GNUNET_YES)); 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.abs_time, + 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.abs_time, + 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_rsa.hash, + 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); + GNUNET_CRYPTO_rsa_public_key_free (key->denom_pub); + GNUNET_free (key->an); + GNUNET_free (key); + key = nxt; + } /* Update position of 'denom' in #denom_head DLL: sort by action time */ { @@ -1319,16 +1421,16 @@ update_keys (struct Denomination *denom, NULL != pos; pos = pos->next) { - if (denomination_action_time (pos).abs_value_us >= at.abs_value_us) + if (GNUNET_TIME_absolute_cmp (denomination_action_time (pos), >=, at)) break; before = pos; } - GNUNET_CONTAINER_DLL_insert_after (denom_head, denom_tail, before, denom); } + return GNUNET_OK; } @@ -1342,16 +1444,29 @@ update_denominations (void *cls) { struct Denomination *denom; struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Timestamp t; + bool wake = false; (void) cls; keygen_task = NULL; now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); + t = GNUNET_TIME_absolute_to_timestamp (now); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Updating denominations ...\n"); + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); do { denom = denom_head; - update_keys (denom, - now); + if (GNUNET_OK != + update_keys (denom, + t, + &wake)) + return; } while (denom != denom_head); + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Updating denominations finished ...\n"); + if (wake) + TES_wake_clients (); keygen_task = GNUNET_SCHEDULER_add_at (denomination_action_time (denom), &update_denominations, NULL); @@ -1376,7 +1491,7 @@ parse_key (struct Denomination *denom, char *anchor_s; char dummy; unsigned long long anchor_ll; - struct GNUNET_TIME_Absolute anchor; + struct GNUNET_TIME_Timestamp anchor; anchor_s = strrchr (filename, '/'); @@ -1398,8 +1513,10 @@ parse_key (struct Denomination *denom, filename); return; } - 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) + anchor.abs_time.abs_value_us + = anchor_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us; + if (anchor_ll != anchor.abs_time.abs_value_us + / GNUNET_TIME_UNIT_SECONDS.rel_value_us) { /* Integer overflow. Bad, invalid filename. */ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -1431,26 +1548,28 @@ parse_key (struct Denomination *denom, return; } dk = GNUNET_new (struct DenominationKey); - dk->denom_priv.rsa_private_key = priv; + dk->denom_priv = priv; dk->denom = denom; dk->anchor = anchor; dk->filename = GNUNET_strdup (filename); GNUNET_CRYPTO_rsa_public_key_hash (pub, - &dk->h_denom_pub); - dk->denom_pub.rsa_public_key = pub; + &dk->h_rsa.hash); + dk->denom_pub = pub; + generate_response (dk); if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put ( keys, - &dk->h_denom_pub, + &dk->h_rsa.hash, 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), + GNUNET_h2s (&dk->h_rsa.hash), filename); GNUNET_CRYPTO_rsa_private_key_free (priv); GNUNET_CRYPTO_rsa_public_key_free (pub); + GNUNET_free (dk->an); GNUNET_free (dk); return; } @@ -1459,7 +1578,9 @@ parse_key (struct Denomination *denom, NULL != pos; pos = pos->next) { - if (pos->anchor.abs_value_us > anchor.abs_value_us) + if (GNUNET_TIME_timestamp_cmp (pos->anchor, + >, + anchor)) break; before = pos; } @@ -1469,7 +1590,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), + GNUNET_h2s (&dk->h_rsa.hash), filename); } } @@ -1481,8 +1602,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) { @@ -1514,7 +1636,7 @@ import_key (void *cls, } fd = open (filename, - O_CLOEXEC); + O_RDONLY | O_CLOEXEC); if (-1 == fd) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, @@ -1528,6 +1650,7 @@ import_key (void *cls, GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename); + GNUNET_break (0 == close (fd)); return GNUNET_OK; } if (! S_ISREG (sbuf.st_mode)) @@ -1535,6 +1658,7 @@ import_key (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "File `%s' is not a regular file, which is not allowed for private keys!\n", filename); + GNUNET_break (0 == close (fd)); return GNUNET_OK; } if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO))) @@ -1561,10 +1685,10 @@ import_key (void *cls, GNUNET_break (0 == close (fd)); return GNUNET_OK; } - if (sbuf.st_size > 2048) + if (sbuf.st_size > 16 * 1024) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "File `%s' to big to be a private key\n", + "File `%s' too big to be a private key\n", filename); GNUNET_DISK_file_close (fh); return GNUNET_OK; @@ -1595,18 +1719,24 @@ 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; + char *secname; + GNUNET_asprintf (&secname, + "%s-secmod-rsa", + section); if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, + GNUNET_CONFIGURATION_get_value_time (cfg, ct, "DURATION_WITHDRAW", &denom->duration_withdraw)) @@ -1614,20 +1744,22 @@ parse_denomination_cfg (const char *ct, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, ct, "DURATION_WITHDRAW"); + GNUNET_free (secname); return GNUNET_SYSERR; } - GNUNET_TIME_round_rel (&denom->duration_withdraw); - if (overlap_duration.rel_value_us >= - denom->duration_withdraw.rel_value_us) + if (GNUNET_TIME_relative_cmp (overlap_duration, + >=, + denom->duration_withdraw)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", + section, "OVERLAP_DURATION", "Value given must be smaller than value for DURATION_WITHDRAW!"); + GNUNET_free (secname); return GNUNET_SYSERR; } if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (kcfg, + GNUNET_CONFIGURATION_get_value_number (cfg, ct, "RSA_KEYSIZE", &rsa_keysize)) @@ -1635,6 +1767,7 @@ parse_denomination_cfg (const char *ct, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, ct, "RSA_KEYSIZE"); + GNUNET_free (secname); return GNUNET_SYSERR; } if ( (rsa_keysize > 4 * 2048) || @@ -1644,8 +1777,10 @@ parse_denomination_cfg (const char *ct, ct, "RSA_KEYSIZE", "Given RSA keysize outside of permitted range [1024,8192]\n"); + GNUNET_free (secname); return GNUNET_SYSERR; } + GNUNET_free (secname); denom->rsa_keysize = (unsigned int) rsa_keysize; denom->section = GNUNET_strdup (ct); return GNUNET_OK; @@ -1657,15 +1792,21 @@ parse_denomination_cfg (const char *ct, */ struct LoadContext { + + /** + * Configuration to use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + /** * Current time to use. */ - struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Timestamp t; /** * Status, to be set to #GNUNET_SYSERR on failure */ - int ret; + enum GNUNET_GenericReturnValue ret; }; @@ -1682,6 +1823,8 @@ load_denominations (void *cls, { struct LoadContext *ctx = cls; struct Denomination *denom; + bool wake = true; + char *cipher; if ( (0 != strncasecmp (denomination_alias, "coin_", @@ -1690,9 +1833,27 @@ load_denominations (void *cls, "coin-", strlen ("coin-"))) ) return; /* not a denomination type definition */ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (ctx->cfg, + denomination_alias, + "CIPHER", + &cipher)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + denomination_alias, + "CIPHER"); + return; + } + if (0 != strcmp (cipher, "RSA")) + { + GNUNET_free (cipher); + return; /* Ignore denominations of other types than CS */ + } + GNUNET_free (cipher); 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; @@ -1720,43 +1881,50 @@ load_denominations (void *cls, denom_tail, denom); update_keys (denom, - ctx->now); + ctx->t, + &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) { + char *secname; + + GNUNET_asprintf (&secname, + "%s-secmod-rsa", + section); if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, - "taler-exchange-secmod-rsa", + GNUNET_CONFIGURATION_get_value_time (cfg, + secname, "OVERLAP_DURATION", &overlap_duration)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", + secname, "OVERLAP_DURATION"); + GNUNET_free (secname); return GNUNET_SYSERR; } - GNUNET_TIME_round_rel (&overlap_duration); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, - "taler-exchange-secmod-rsa", + GNUNET_CONFIGURATION_get_value_time (cfg, + secname, "LOOKAHEAD_SIGN", &lookahead_sign)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", + secname, "LOOKAHEAD_SIGN"); + GNUNET_free (secname); return GNUNET_SYSERR; } - GNUNET_TIME_round_rel (&lookahead_sign); + GNUNET_free (secname); return GNUNET_OK; } @@ -1770,51 +1938,14 @@ 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; - } + stop_workers (); + sem_done (&worker_sem); } @@ -1832,11 +1963,17 @@ 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 + }; + char *secname; + (void) cls; (void) args; (void) cfgfile; - kcfg = cfg; - if (now.abs_value_us != now_tmp.abs_value_us) + if (GNUNET_TIME_timestamp_cmp (now, !=, now_tmp)) { /* The user gave "--now", use it! */ now = now_tmp; @@ -1844,206 +1981,100 @@ run (void *cls, else { /* get current time again, we may be timetraveling! */ - 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; + now = GNUNET_TIME_timestamp_get (); } + GNUNET_asprintf (&secname, + "%s-secmod-rsa", + section); if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (kcfg, - "taler-exchange-secmod-rsa", + GNUNET_CONFIGURATION_get_value_filename (cfg, + secname, "KEY_DIR", &keydir)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", + secname, "KEY_DIR"); - global_ret = 1; + GNUNET_free (secname); + 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); - } - + GNUNET_free (secname); 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; - return; - } + char *secname; + GNUNET_asprintf (&secname, + "%s-secmod-rsa", + section); + global_ret = TES_listen_start (cfg, + secname, + &cb); + GNUNET_free (secname); + } + if (0 != global_ret) + return; + sem_init (&worker_sem, + 0); GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + if (0 == max_workers) + { + long lret; + + lret = sysconf (_SC_NPROCESSORS_CONF); + if (lret <= 0) + lret = 1; + max_workers = (unsigned int) lret; + } + for (unsigned int i = 0; i<max_workers; i++) + if (GNUNET_OK != + start_worker ()) + { + GNUNET_SCHEDULER_shutdown (); + return; + } /* Load denominations */ keys = GNUNET_CONTAINER_multihashmap_create (65536, GNUNET_YES); { struct LoadContext lc = { + .cfg = cfg, .ret = GNUNET_OK, - .now = GNUNET_TIME_absolute_get () + .t = 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; } } if (NULL == denom_head) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No denominations configured\n"); - global_ret = 5; - GNUNET_SCHEDULER_shutdown (); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No RSA denominations configured\n"); + TES_wake_clients (); 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)); } @@ -2059,30 +2090,35 @@ main (int argc, char **argv) { struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_string ('s', + "section", + "SECTION", + "name of the configuration section prefix to use, default is 'taler'", + §ion), 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", - "pretend it is a different time for the update", - &now_tmp), + GNUNET_GETOPT_option_timestamp ('t', + "time", + "TIMESTAMP", + "pretend it is a different time for the update", + &now_tmp), + GNUNET_GETOPT_option_uint ('w', + "workers", + "COUNT", + "use COUNT workers for parallel processing of batch requests", + &max_workers), 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); - + section = GNUNET_strdup ("taler-exchange"); /* force linker to link against libtalerutil; if we do not do this, the linker may "optimize" libtalerutil away and skip #TALER_OS_init(), which we do need */ TALER_OS_init (); - now = now_tmp = GNUNET_TIME_absolute_get (); + now_tmp = now = GNUNET_TIME_timestamp_get (); ret = GNUNET_PROGRAM_run (argc, argv, "taler-exchange-secmod-rsa", "Handle private RSA key operations for a Taler exchange", @@ -2090,8 +2126,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; } |