diff options
Diffstat (limited to 'src/util/taler-exchange-secmod-rsa.c')
-rw-r--r-- | src/util/taler-exchange-secmod-rsa.c | 876 |
1 files changed, 722 insertions, 154 deletions
diff --git a/src/util/taler-exchange-secmod-rsa.c b/src/util/taler-exchange-secmod-rsa.c index 43109b5a4..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-2021 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 @@ -36,7 +36,6 @@ #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" @@ -98,7 +97,7 @@ struct DenominationKey /** * 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. @@ -165,6 +164,128 @@ struct Denomination /** + * A semaphore. + */ +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; +}; + + +/** + * Job in a batch sign request. + */ +struct BatchJob; + +/** + * Handle for a thread that does work in batch signing. + */ +struct Worker +{ + /** + * Kept in a DLL. + */ + struct Worker *prev; + + /** + * Kept in a DLL. + */ + struct Worker *next; + + /** + * Job this worker should do next. + */ + struct BatchJob *job; + + /** + * Semaphore to signal the worker that a job is available. + */ + struct Semaphore sem; + + /** + * Handle for this thread. + */ + pthread_t pt; + + /** + * Set to true if the worker should terminate. + */ + bool do_shutdown; +}; + + +/** + * Job in a batch sign request. + */ +struct BatchJob +{ + /** + * Request we are working on. + */ + const struct TALER_CRYPTO_SignRequest *sr; + + /** + * Thread doing the work. + */ + struct Worker *worker; + + /** + * Result with the signature. + */ + struct GNUNET_CRYPTO_RsaSignature *rsa_signature; + + /** + * Semaphore to signal that the job is finished. + */ + struct Semaphore sem; + + /** + * Computation status. + */ + enum TALER_ErrorCode ec; + +}; + + +/** + * Head of DLL of workers ready for more work. + */ +static struct Worker *worker_head; + +/** + * Tail of DLL of workers ready for more work. + */ +static struct Worker *worker_tail; + +/** + * Lock for manipulating the worker DLL. + */ +static pthread_mutex_t worker_lock; + +/** + * Total number of workers that were started. + */ +static unsigned int workers; + +/** + * Semaphore used to grab a worker. + */ +static struct Semaphore worker_sem; + +/** * Return value from main(). */ static int global_ret; @@ -173,13 +294,13 @@ 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; /** * Where do we store the keys? @@ -187,6 +308,13 @@ static struct GNUNET_TIME_Absolute now_tmp; static char *keydir; /** + * 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 *section; + +/** * How much should coin creation (@e duration_withdraw) duration overlap * with the next denomination? Basically, the starting time of two * denominations is always @e duration_withdraw - #overlap_duration apart. @@ -229,11 +357,17 @@ static pthread_mutex_t keys_lock; */ static uint64_t key_gen; +/** + * Number of workers to launch. Note that connections to + * exchanges are NOT workers. + */ +static unsigned int max_workers = 16; + /** * Generate the announcement message for @a dk. * - * @param[in,out] denomination key to generate the announcement for + * @param[in,out] dk denomination key to generate the announcement for */ static void generate_response (struct DenominationKey *dk) @@ -257,7 +391,7 @@ generate_response (struct DenominationKey *dk) 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->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, @@ -267,129 +401,453 @@ generate_response (struct DenominationKey *dk) &an->secm_sig); an->secm_pub = TES_smpub; p = (void *) &an[1]; - memcpy (p, - buf, - buf_len); + GNUNET_memcpy (p, + buf, + buf_len); GNUNET_free (buf); - memcpy (p + buf_len, - denom->section, - nlen); + GNUNET_memcpy (p + buf_len, + denom->section, + nlen); dk->an = an; } /** - * Handle @a client request @a sr to create signature. Create the - * signature using the respective key and return the result to - * the client. + * Do the actual signing work. * - * @param client the client making the request - * @param sr the request details - * @return #GNUNET_OK on success + * @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 enum GNUNET_GenericReturnValue -handle_sign_request (struct TES_Client *client, - const struct TALER_CRYPTO_SignRequest *sr) +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; - const void *blinded_msg = &sr[1]; - size_t blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr); struct GNUNET_CRYPTO_RsaSignature *rsa_signature; struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); dk = GNUNET_CONTAINER_multihashmap_get (keys, - &sr->h_rsa.hash); + &h_rsa->hash); 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_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_rsa.hash)); - return TES_transmit (client->csock, - &sf.header); + GNUNET_h2s (&h_rsa->hash)); + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; } - if (0 != - GNUNET_TIME_absolute_get_remaining (dk->anchor).rel_value_us) + if (GNUNET_TIME_absolute_is_future (dk->anchor.abs_time)) { /* 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_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_rsa.hash)); - return TES_transmit (client->csock, - &sf.header); + GNUNET_h2s (&h_rsa->hash)); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY; } 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_rsa.hash)); + (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, - blinded_msg, - blinded_msg_size); + 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; + } + + 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; +} + + +/** + * 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 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) + }; + + return TES_transmit (client->csock, + &sf.header); +} + + +/** + * 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 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; + + 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; +} + + +/** + * 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_SignRequest *sr) +{ + 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; + + ec = do_sign (&sr->h_rsa, + &bm, + &rsa_signature); + if (TALER_EC_NONE != ec) + { + return fail_sign (client, + ec); + } + return send_signature (client, + rsa_signature); +} + + +/** + * Initialize a semaphore @a sem with a value of @a val. + * + * @param[out] sem semaphore to initialize + * @param val initial value of the semaphore + */ +static void +sem_init (struct Semaphore *sem, + unsigned int val) +{ + GNUNET_assert (0 == + pthread_mutex_init (&sem->mutex, + NULL)); + GNUNET_assert (0 == + pthread_cond_init (&sem->cv, + NULL)); + sem->ctr = val; +} + + +/** + * Decrement semaphore, blocks until this is possible. + * + * @param[in,out] sem semaphore to decrement + */ +static void +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)); +} + + +/** + * Increment semaphore, blocks until this is possible. + * + * @param[in,out] sem semaphore to decrement + */ +static void +sem_up (struct Semaphore *sem) +{ + GNUNET_assert (0 == pthread_mutex_lock (&sem->mutex)); + sem->ctr++; + GNUNET_assert (0 == pthread_mutex_unlock (&sem->mutex)); + pthread_cond_signal (&sem->cv); +} + + +/** + * Release resources used by @a sem. + * + * @param[in] sem semaphore to release (except the memory itself) + */ +static void +sem_done (struct Semaphore *sem) +{ + GNUNET_break (0 == pthread_cond_destroy (&sem->cv)); + GNUNET_break (0 == pthread_mutex_destroy (&sem->mutex)); +} + + +/** + * Main logic of a worker thread. Grabs work, does it, + * grabs more work. + * + * @param cls a `struct Worker *` + * @returns cls + */ +static void * +worker (void *cls) +{ + 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 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) + }; + + bj->ec = do_sign (&sr->h_rsa, + &bm, + &bj->rsa_signature); + sem_up (&bj->sem); + w->job = NULL; + } + } + return w; +} + + +/** + * Start batch job @a bj to sign @a sr. + * + * @param sr signature request to answer + * @param[out] bj job data structure + */ +static void +start_job (const struct TALER_CRYPTO_SignRequest *sr, + struct BatchJob *bj) +{ + 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); +} + + +/** + * 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) + { + fail_sign (client, + bj->ec); + return; + } + GNUNET_assert (NULL != bj->rsa_signature); + send_signature (client, + bj->rsa_signature); + bj->rsa_signature = NULL; /* freed in send_signature */ +} + + +/** + * 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 client the client making the request + * @param bsr the request details + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +handle_batch_sign_request (struct TES_Client *client, + const struct TALER_CRYPTO_BatchSignRequest *bsr) +{ + 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; + + if (bs > TALER_MAX_FRESH_COINS) + { + 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); + + if (s > size) + { + failure = true; + bs = idx; + break; + } + 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_SIGN_FAILURE), + .header.type = htons (TALER_HELPER_RSA_MT_RES_BATCH_FAILURE), .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE) }; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Signing request failed, worker failed to produce signature\n"); + GNUNET_break (0); return TES_transmit (client->csock, &sf.header); } + return GNUNET_OK; +} + +/** + * Start worker thread for batch processing. + * + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +start_worker (void) +{ + struct Worker *w; + + w = GNUNET_new (struct Worker); + sem_init (&w->sem, + 0); + if (0 != pthread_create (&w->pt, + NULL, + &worker, + w)) { - struct TALER_CRYPTO_SignResponse *sr; - void *buf; - size_t buf_size; - size_t tsize; - enum GNUNET_GenericReturnValue ret; + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "pthread_create"); + GNUNET_free (w); + return GNUNET_SYSERR; + } + workers++; + return GNUNET_OK; +} - 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, - "Sending RSA signature after %s\n", - GNUNET_STRINGS_relative_time_to_string ( - GNUNET_TIME_absolute_get_duration (now), - GNUNET_YES)); - ret = TES_transmit (client->csock, - &sr->header); - GNUNET_free (sr); - return ret; + +/** + * 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--; } } @@ -428,13 +886,13 @@ setup_key (struct DenominationKey *dk, } buf_size = GNUNET_CRYPTO_rsa_private_key_encode (priv, &buf); - TALER_rsa_pub_hash (pub, - &dk->h_rsa); + GNUNET_CRYPTO_rsa_public_key_hash (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, @@ -454,7 +912,7 @@ setup_key (struct DenominationKey *dk, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Setup fresh private key %s at %s in `%s' (generation #%llu)\n", GNUNET_h2s (&dk->h_rsa.hash), - GNUNET_STRINGS_absolute_time_to_string (dk->anchor), + GNUNET_TIME_timestamp2s (dk->anchor), dk->filename, (unsigned long long) key_gen); dk->denom_priv = priv; @@ -603,6 +1061,15 @@ rsa_work_dispatch (struct TES_Client *client, 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)) + { + 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; @@ -635,6 +1102,8 @@ rsa_client_init (struct TES_Client *client) NULL != dk; dk = dk->next) { + GNUNET_assert (obs + ntohs (dk->an->header.size) + > obs); obs += ntohs (dk->an->header.size); } } @@ -648,9 +1117,11 @@ rsa_client_init (struct TES_Client *client) NULL != dk; dk = dk->next) { - memcpy (&buf[obs], - dk->an, - ntohs (dk->an->header.size)); + 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); } } @@ -747,16 +1218,20 @@ rsa_update_client_keys (struct TES_Client *client) .h_rsa = key->h_rsa }; - memcpy (&buf[obs], - &pn, - sizeof (pn)); + GNUNET_memcpy (&buf[obs], + &pn, + sizeof (pn)); + GNUNET_assert (obs + sizeof (pn) + > obs); obs += sizeof (pn); } else { - memcpy (&buf[obs], - key->an, - ntohs (key->an->header.size)); + 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); } } @@ -780,23 +1255,23 @@ rsa_update_client_keys (struct TES_Client *client) */ 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; @@ -834,14 +1309,14 @@ denomination_action_time (const struct Denomination *denom) return GNUNET_TIME_UNIT_ZERO_ABS; tt = GNUNET_TIME_absolute_subtract ( GNUNET_TIME_absolute_subtract ( - GNUNET_TIME_absolute_add (tail->anchor, + 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 (head->anchor, + GNUNET_TIME_absolute_add (head->anchor.abs_time, denom->duration_withdraw), tt); } @@ -859,15 +1334,28 @@ denomination_action_time (const struct Denomination *denom) */ 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) || GNUNET_TIME_absolute_is_past ( GNUNET_TIME_absolute_subtract ( GNUNET_TIME_absolute_subtract ( - GNUNET_TIME_absolute_add (denom->keys_tail->anchor, + GNUNET_TIME_absolute_add (denom->keys_tail->anchor.abs_time, denom->duration_withdraw), lookahead_sign), overlap_duration)) ) @@ -890,7 +1378,7 @@ update_keys (struct Denomination *denom, /* remove expired denomination keys */ while ( (NULL != denom->keys_head) && GNUNET_TIME_absolute_is_past - (GNUNET_TIME_absolute_add (denom->keys_head->anchor, + (GNUNET_TIME_absolute_add (denom->keys_head->anchor.abs_time, denom->duration_withdraw)) ) { struct DenominationKey *key = denom->keys_head; @@ -933,7 +1421,7 @@ 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; } @@ -956,12 +1444,13 @@ 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)); @@ -969,7 +1458,7 @@ update_denominations (void *cls) denom = denom_head; if (GNUNET_OK != update_keys (denom, - now, + t, &wake)) return; } while (denom != denom_head); @@ -1002,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, '/'); @@ -1024,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, @@ -1061,8 +1552,8 @@ parse_key (struct Denomination *denom, dk->denom = denom; dk->anchor = anchor; dk->filename = GNUNET_strdup (filename); - TALER_rsa_pub_hash (pub, - &dk->h_rsa); + GNUNET_CRYPTO_rsa_public_key_hash (pub, + &dk->h_rsa.hash); dk->denom_pub = pub; generate_response (dk); if (GNUNET_OK != @@ -1087,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; } @@ -1143,13 +1636,12 @@ import_key (void *cls, } fd = open (filename, - O_CLOEXEC); + O_RDONLY | O_CLOEXEC); if (-1 == fd) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", filename); - GNUNET_break (0 == close (fd)); return GNUNET_OK; } if (0 != fstat (fd, @@ -1158,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)) @@ -1237,7 +1730,11 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, 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 (cfg, ct, @@ -1247,16 +1744,18 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, 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 != @@ -1268,6 +1767,7 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, ct, "RSA_KEYSIZE"); + GNUNET_free (secname); return GNUNET_SYSERR; } if ( (rsa_keysize > 4 * 2048) || @@ -1277,8 +1777,10 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, 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; @@ -1299,7 +1801,7 @@ struct LoadContext /** * Current time to use. */ - struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Timestamp t; /** * Status, to be set to #GNUNET_SYSERR on failure @@ -1322,6 +1824,7 @@ load_denominations (void *cls, struct LoadContext *ctx = cls; struct Denomination *denom; bool wake = true; + char *cipher; if ( (0 != strncasecmp (denomination_alias, "coin_", @@ -1330,6 +1833,23 @@ 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 (ctx->cfg, @@ -1361,7 +1881,7 @@ load_denominations (void *cls, denom_tail, denom); update_keys (denom, - ctx->now, + ctx->t, &wake); } @@ -1375,31 +1895,36 @@ load_denominations (void *cls, 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 (cfg, - "taler-exchange-secmod-rsa", + 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 (cfg, - "taler-exchange-secmod-rsa", + 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; } @@ -1419,6 +1944,8 @@ do_shutdown (void *cls) GNUNET_SCHEDULER_cancel (keygen_task); keygen_task = NULL; } + stop_workers (); + sem_done (&worker_sem); } @@ -1441,10 +1968,12 @@ run (void *cls, .updater = rsa_update_client_keys, .init = rsa_client_init }; + char *secname; + (void) cls; (void) args; (void) cfgfile; - 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; @@ -1452,34 +1981,65 @@ run (void *cls, else { /* get current time again, we may be timetraveling! */ - now = GNUNET_TIME_absolute_get (); + now = GNUNET_TIME_timestamp_get (); } - GNUNET_TIME_round_abs (&now); + GNUNET_asprintf (&secname, + "%s-secmod-rsa", + section); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, - "taler-exchange-secmod-rsa", + secname, "KEY_DIR", &keydir)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", + secname, "KEY_DIR"); + GNUNET_free (secname); global_ret = EXIT_NOTCONFIGURED; return; } + GNUNET_free (secname); if (GNUNET_OK != load_durations (cfg)) { global_ret = EXIT_NOTCONFIGURED; return; } - global_ret = TES_listen_start (cfg, - "taler-exchange-secmod-rsa", - &cb); + { + 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); @@ -1487,10 +2047,9 @@ run (void *cls, struct LoadContext lc = { .cfg = cfg, .ret = GNUNET_OK, - .now = now + .t = now }; - (void) GNUNET_TIME_round_abs (&lc.now); GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); GNUNET_CONFIGURATION_iterate_sections (cfg, &load_denominations, @@ -1505,10 +2064,9 @@ run (void *cls, } if (NULL == denom_head) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No denominations configured\n"); - global_ret = EXIT_NOTCONFIGURED; - GNUNET_SCHEDULER_shutdown (); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No RSA denominations configured\n"); + TES_wake_clients (); return; } /* start job to keep keys up-to-date; MUST be run before the #listen_task, @@ -1532,25 +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_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 }; 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", |