complete new implementation of GET /keys

This commit is contained in:
Christian Grothoff 2020-12-09 23:39:11 +01:00
parent 788f84f695
commit 5a24334e83
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
5 changed files with 643 additions and 167 deletions

View File

@ -103,6 +103,11 @@ struct GNUNET_CONFIGURATION_Handle *TEH_cfg;
*/ */
struct GNUNET_TIME_Relative TEH_max_keys_caching; struct GNUNET_TIME_Relative TEH_max_keys_caching;
/**
* How long is the delay before we close reserves?
*/
struct GNUNET_TIME_Relative TEH_reserve_closing_delay;
/** /**
* Master public key (according to the * Master public key (according to the
* configuration in the exchange directory). (global) * configuration in the exchange directory). (global)
@ -1065,6 +1070,21 @@ exchange_serve_process_config (void)
{ {
req_max = ULONG_LONG_MAX; req_max = ULONG_LONG_MAX;
} }
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
"exchangedb",
"IDLE_RESERVE_EXPIRATION_TIME",
&TEH_reserve_closing_delay))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchangedb",
"IDLE_RESERVE_EXPIRATION_TIME");
/* use default */
TEH_reserve_closing_delay
= GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS,
4);
}
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (TEH_cfg, GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
"exchange", "exchange",

View File

@ -34,6 +34,11 @@
*/ */
extern struct GNUNET_TIME_Relative TEH_max_keys_caching; extern struct GNUNET_TIME_Relative TEH_max_keys_caching;
/**
* How long is the delay before we close reserves?
*/
extern struct GNUNET_TIME_Relative TEH_reserve_closing_delay;
/** /**
* The exchange's configuration. * The exchange's configuration.
*/ */

View File

@ -22,6 +22,7 @@
#include <pthread.h> #include <pthread.h>
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_mhd_lib.h" #include "taler_mhd_lib.h"
#include "taler-exchange-httpd.h"
#include "taler-exchange-httpd_keys.h" #include "taler-exchange-httpd_keys.h"
#include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_responses.h"
#include "taler_exchangedb_plugin.h" #include "taler_exchangedb_plugin.h"
@ -241,7 +242,7 @@ struct SigningKey
* reference counter) should be considered READ-ONLY until it is * reference counter) should be considered READ-ONLY until it is
* ultimately destroyed (as there can be many concurrent users). * ultimately destroyed (as there can be many concurrent users).
*/ */
struct TEH_KeyStateHandle struct KeyStateHandle
{ {
/** /**
@ -292,11 +293,21 @@ struct TEH_KeyStateHandle
*/ */
uint64_t key_generation; uint64_t key_generation;
/**
* When did we initiate the key reloading?
*/
struct GNUNET_TIME_Absolute reload_time;
/**
* When is the next key invalid and we expect to have a different reply?
*/
struct GNUNET_TIME_Absolute next_reload;
}; };
/** /**
* Thread-local. Contains a pointer to `struct TEH_KeyStateHandle` or NULL. * Thread-local. Contains a pointer to `struct KeyStateHandle` or NULL.
* Stores the per-thread latest generation of our key state. * Stores the per-thread latest generation of our key state.
*/ */
static pthread_key_t key_state; static pthread_key_t key_state;
@ -339,7 +350,7 @@ static pthread_mutex_t sm_pub_mutex = PTHREAD_MUTEX_INITIALIZER;
* @param[in,out] ksh key state to update * @param[in,out] ksh key state to update
*/ */
static void static void
clear_response_cache (struct TEH_KeyStateHandle *ksh) clear_response_cache (struct KeyStateHandle *ksh)
{ {
for (unsigned int i = 0; i<ksh->krd_array_length; i++) for (unsigned int i = 0; i<ksh->krd_array_length; i++)
{ {
@ -664,7 +675,7 @@ sync_key_helpers (struct HelperState *hs)
/** /**
* Free denomination key data. * Free denomination key data.
* *
* @param cls a `struct TEH_KeyStateHandle`, unused * @param cls a `struct KeyStateHandle`, unused
* @param h_denom_pub hash of the denomination public key, unused * @param h_denom_pub hash of the denomination public key, unused
* @param value a `struct TEH_DenominationKey` to free * @param value a `struct TEH_DenominationKey` to free
* @return #GNUNET_OK (continue to iterate) * @return #GNUNET_OK (continue to iterate)
@ -695,7 +706,7 @@ clear_denomination_cb (void *cls,
/** /**
* Free denomination key data. * Free denomination key data.
* *
* @param cls a `struct TEH_KeyStateHandle`, unused * @param cls a `struct KeyStateHandle`, unused
* @param h_denom_pub hash of the denomination public key, unused * @param h_denom_pub hash of the denomination public key, unused
* @param value a `struct SigningKey` to free * @param value a `struct SigningKey` to free
* @return #GNUNET_OK (continue to iterate) * @return #GNUNET_OK (continue to iterate)
@ -722,7 +733,7 @@ clear_signkey_cb (void *cls,
* @param free_helper true to also release the helper state * @param free_helper true to also release the helper state
*/ */
static void static void
destroy_key_state (struct TEH_KeyStateHandle *ksh, destroy_key_state (struct KeyStateHandle *ksh,
bool free_helper) bool free_helper)
{ {
clear_response_cache (ksh); clear_response_cache (ksh);
@ -751,12 +762,12 @@ destroy_key_state (struct TEH_KeyStateHandle *ksh,
* Free all resources associated with @a cls. Called when * Free all resources associated with @a cls. Called when
* the respective pthread is destroyed. * the respective pthread is destroyed.
* *
* @param[in] cls a `struct TEH_KeyStateHandle`. * @param[in] cls a `struct KeyStateHandle`.
*/ */
static void static void
destroy_key_state_cb (void *cls) destroy_key_state_cb (void *cls)
{ {
struct TEH_KeyStateHandle *ksh = cls; struct KeyStateHandle *ksh = cls;
destroy_key_state (ksh, destroy_key_state (ksh,
true); true);
@ -804,7 +815,7 @@ TEH_keys_done ()
/** /**
* Function called with information about the exchange's denomination keys. * Function called with information about the exchange's denomination keys.
* *
* @param cls closure with a `struct TEH_KeyStateHandle *` * @param cls closure with a `struct KeyStateHandle *`
* @param denom_pub public key of the denomination * @param denom_pub public key of the denomination
* @param h_denom_pub hash of @a denom_pub * @param h_denom_pub hash of @a denom_pub
* @param meta meta data information about the denomination type (value, expirations, fees) * @param meta meta data information about the denomination type (value, expirations, fees)
@ -821,7 +832,7 @@ denomination_info_cb (
const struct TALER_MasterSignatureP *master_sig, const struct TALER_MasterSignatureP *master_sig,
bool recoup_possible) bool recoup_possible)
{ {
struct TEH_KeyStateHandle *ksh = cls; struct KeyStateHandle *ksh = cls;
struct TEH_DenominationKey *dk; struct TEH_DenominationKey *dk;
dk = GNUNET_new (struct TEH_DenominationKey); dk = GNUNET_new (struct TEH_DenominationKey);
@ -843,7 +854,7 @@ denomination_info_cb (
/** /**
* Function called with information about the exchange's online signing keys. * Function called with information about the exchange's online signing keys.
* *
* @param cls closure with a `struct TEH_KeyStateHandle *` * @param cls closure with a `struct KeyStateHandle *`
* @param exchange_pub the public key * @param exchange_pub the public key
* @param meta meta data information about the denomination type (expirations) * @param meta meta data information about the denomination type (expirations)
* @param master_sig master signature affirming the validity of this denomination * @param master_sig master signature affirming the validity of this denomination
@ -855,7 +866,7 @@ signkey_info_cb (
const struct TALER_EXCHANGEDB_SignkeyMetaData *meta, const struct TALER_EXCHANGEDB_SignkeyMetaData *meta,
const struct TALER_MasterSignatureP *master_sig) const struct TALER_MasterSignatureP *master_sig)
{ {
struct TEH_KeyStateHandle *ksh = cls; struct KeyStateHandle *ksh = cls;
struct SigningKey *sk; struct SigningKey *sk;
struct GNUNET_PeerIdentity pid; struct GNUNET_PeerIdentity pid;
@ -876,7 +887,7 @@ signkey_info_cb (
/** /**
* Function called with information about the exchange's auditors. * Function called with information about the exchange's auditors.
* *
* @param cls closure with a `struct TEH_KeyStateHandle *` * @param cls closure with a `struct KeyStateHandle *`
* @param auditor_pub the public key of the auditor * @param auditor_pub the public key of the auditor
* @param auditor_url URL of the REST API of the auditor * @param auditor_url URL of the REST API of the auditor
* @param auditor_name human readable official name of the auditor * @param auditor_name human readable official name of the auditor
@ -888,7 +899,7 @@ auditor_info_cb (
const char *auditor_url, const char *auditor_url,
const char *auditor_name) const char *auditor_name)
{ {
struct TEH_KeyStateHandle *ksh = cls; struct KeyStateHandle *ksh = cls;
GNUNET_break (0 == GNUNET_break (0 ==
json_array_append_new ( json_array_append_new (
@ -907,7 +918,7 @@ auditor_info_cb (
* Function called with information about the denominations * Function called with information about the denominations
* audited by the exchange's auditors. * audited by the exchange's auditors.
* *
* @param cls closure with a `struct TEH_KeyStateHandle *` * @param cls closure with a `struct KeyStateHandle *`
* @param auditor_pub the public key of an auditor * @param auditor_pub the public key of an auditor
* @param h_denom_pub hash of a denomination key audited by this auditor * @param h_denom_pub hash of a denomination key audited by this auditor
* @param auditor_sig signature from the auditor affirming this * @param auditor_sig signature from the auditor affirming this
@ -919,7 +930,7 @@ auditor_denom_cb (
const struct GNUNET_HashCode *h_denom_pub, const struct GNUNET_HashCode *h_denom_pub,
const struct TALER_AuditorSignatureP *auditor_sig) const struct TALER_AuditorSignatureP *auditor_sig)
{ {
struct TEH_KeyStateHandle *ksh = cls; struct KeyStateHandle *ksh = cls;
struct TEH_DenominationKey *dk; struct TEH_DenominationKey *dk;
struct TEH_AuditorSignature *as; struct TEH_AuditorSignature *as;
@ -948,13 +959,15 @@ auditor_denom_cb (
* @param[in] hs helper state to (re)use, NULL if not available * @param[in] hs helper state to (re)use, NULL if not available
* @return NULL on error (i.e. failed to access database) * @return NULL on error (i.e. failed to access database)
*/ */
static struct TEH_KeyStateHandle * static struct KeyStateHandle *
build_key_state (struct HelperState *hs) build_key_state (struct HelperState *hs)
{ {
struct TEH_KeyStateHandle *ksh; struct KeyStateHandle *ksh;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
ksh = GNUNET_new (struct TEH_KeyStateHandle); ksh = GNUNET_new (struct KeyStateHandle);
ksh->reload_time = GNUNET_TIME_absolute_get ();
GNUNET_TIME_round_abs (&ksh->reload_time);
/* We must use the key_generation from when we STARTED the process! */ /* We must use the key_generation from when we STARTED the process! */
ksh->key_generation = key_generation; ksh->key_generation = key_generation;
if (NULL == hs) if (NULL == hs)
@ -1021,6 +1034,330 @@ build_key_state (struct HelperState *hs)
} }
/**
* Closure for #add_sign_key_cb.
*/
struct SignKeyCtx
{
/**
* When does the next signing key expire. Updated.
*/
struct GNUNET_TIME_Absolute next_sk_expire;
/**
* JSON array of signing keys (being created).
*/
json_t *signkeys;
};
/**
* Function called for all signing keys, used to build up the
* respective JSON response.
*
* @param cls a `struct SignKeyCtx *` with the array to append keys to
* @param pid the exchange public key (in type disguise)
* @param value a `struct SigningKey`
* @return #GNUNET_OK (continue to iterate)
*/
static int
add_sign_key_cb (void *cls,
const struct GNUNET_PeerIdentity *pid,
void *value)
{
struct SignKeyCtx *ctx = cls;
struct SigningKey *sk = value;
ctx->next_sk_expire =
GNUNET_TIME_absolute_min (ctx->next_sk_expire,
sk->meta.expire_sign);
GNUNET_assert (
0 ==
json_array_append_new (
ctx->signkeys,
json_pack ("{s:o, s:o, s:o, s:o, s:o}",
"stamp_start",
GNUNET_JSON_from_time_abs (sk->meta.start),
"stamp_expire",
GNUNET_JSON_from_time_abs (sk->meta.expire_sign),
"stamp_end",
GNUNET_JSON_from_time_abs (sk->meta.expire_legal),
"master_sig",
GNUNET_JSON_from_data_auto (&sk->master_sig),
"key",
GNUNET_JSON_from_data_auto (&sk->exchange_pub))));
return GNUNET_OK;
}
/**
* Closure for #add_denom_key_cb.
*/
struct DenomKeyCtx
{
/**
* Heap for sorting active denomination keys by start time.
*/
struct GNUNET_CONTAINER_Heap *heap;
/**
* JSON array of revoked denomination keys.
*/
json_t *recoup;
/**
* When does the next denomination key expire. Updated.
*/
struct GNUNET_TIME_Absolute next_dk_expire;
};
/**
* Function called for all denomination keys, used to build up the
* JSON list of *revoked* denomination keys and the
* heap of non-revoked denomination keys by timeout.
*
* @param cls a `struct DenomKeyCtx`
* @param h_denom_pub hash of the denomination key
* @param value a `struct TEH_DenominationKey`
* @return #GNUNET_OK (continue to iterate)
*/
static int
add_denom_key_cb (void *cls,
const struct GNUNET_HashCode *h_denom_pub,
void *value)
{
struct DenomKeyCtx *dkc = cls;
struct TEH_DenominationKey *dk = value;
if (dk->recoup_possible)
{
GNUNET_assert (
0 ==
json_array_append_new (
dkc->recoup,
json_pack ("{s:o}",
"h_denom_pub",
GNUNET_JSON_from_data_auto (h_denom_pub))));
}
else
{
dkc->next_dk_expire =
GNUNET_TIME_absolute_min (dkc->next_dk_expire,
dk->meta.expire_withdraw);
(void) GNUNET_CONTAINER_heap_insert (dkc->heap,
dk,
dk->meta.start.abs_value_us);
}
return GNUNET_OK;
}
/**
* Produce HTTP "Date:" header.
*
* @param at time to write to @a date
* @param[out] date where to write the header, with
* at least 128 bytes available space.
*/
static void
get_date_string (struct GNUNET_TIME_Absolute at,
char date[128])
{
static const char *const days[] =
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char *const mons[] =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
"Nov", "Dec"};
struct tm now;
time_t t;
#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
! defined(HAVE_GMTIME_R)
struct tm*pNow;
#endif
date[0] = 0;
t = (time_t) (at.abs_value_us / 1000LL / 1000LL);
#if defined(HAVE_C11_GMTIME_S)
if (NULL == gmtime_s (&t, &now))
return;
#elif defined(HAVE_W32_GMTIME_S)
if (0 != gmtime_s (&now, &t))
return;
#elif defined(HAVE_GMTIME_R)
if (NULL == gmtime_r (&t, &now))
return;
#else
pNow = gmtime (&t);
if (NULL == pNow)
return;
now = *pNow;
#endif
sprintf (date,
"%3s, %02u %3s %04u %02u:%02u:%02u GMT",
days[now.tm_wday % 7],
(unsigned int) now.tm_mday,
mons[now.tm_mon % 12],
(unsigned int) (1900 + now.tm_year),
(unsigned int) now.tm_hour,
(unsigned int) now.tm_min,
(unsigned int) now.tm_sec);
}
/**
* Add the headers we want to set for every /keys response.
*
* @param ksh the key state to use
* @param[in,out] response the response to modify
* @return #GNUNET_OK on success
*/
static int
setup_general_response_headers (const struct KeyStateHandle *ksh,
struct MHD_Response *response)
{
char dat[128];
TALER_MHD_add_global_headers (response);
GNUNET_break (MHD_YES ==
MHD_add_response_header (response,
MHD_HTTP_HEADER_CONTENT_TYPE,
"application/json"));
get_date_string (ksh->reload_time,
dat);
GNUNET_break (MHD_YES ==
MHD_add_response_header (response,
MHD_HTTP_HEADER_LAST_MODIFIED,
dat));
if (0 != ksh->next_reload.abs_value_us)
{
struct GNUNET_TIME_Absolute m;
m = GNUNET_TIME_relative_to_absolute (TEH_max_keys_caching);
m = GNUNET_TIME_absolute_min (m,
ksh->next_reload);
get_date_string (m,
dat);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Setting /keys 'Expires' header to '%s'\n",
dat);
GNUNET_break (MHD_YES ==
MHD_add_response_header (response,
MHD_HTTP_HEADER_EXPIRES,
dat));
}
return GNUNET_OK;
}
/**
* Initialize @a krd using the given values for @a signkeys,
* @a recoup and @a denoms.
*
* @param[in,out] ksh key state handle we build @a krd for
* @param[in] denom_keys_hash hash over all the denominatoin keys in @a denoms
* @param last_cpd timestamp to use
* @param signkeys list of sign keys to return
* @param recoup list of revoked keys to return
* @param denoms list of denominations to return
*/
static void
create_krd (struct KeyStateHandle *ksh,
const struct GNUNET_HashCode *denom_keys_hash,
struct GNUNET_TIME_Absolute last_cpd,
json_t *signkeys,
json_t *recoup,
json_t *denoms)
{
struct KeysResponseData krd;
struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP exchange_sig;
json_t *keys;
/* Sign hash over denomination keys */
{
struct TALER_ExchangeKeySetPS ks = {
.purpose.size = htonl (sizeof (ks)),
.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET),
.list_issue_date = GNUNET_TIME_absolute_hton (last_cpd),
.hc = *denom_keys_hash
};
TEH_keys_exchange_sign (&ks,
&exchange_pub,
&exchange_sig);
}
keys = json_pack (
"{s:s, s:o, s:o, s:O, s:O,"
" s:O, s:o, s:o, s:o, s:o}",
/* 1-5 */
"version", EXCHANGE_PROTOCOL_VERSION,
"master_public_key", GNUNET_JSON_from_data_auto (&TEH_master_public_key),
"reserve_closing_delay", GNUNET_JSON_from_time_rel (
TEH_reserve_closing_delay),
"signkeys", signkeys,
"recoup", recoup,
/* 6-10 */
"denoms", denoms,
"auditors", ksh->auditors,
"list_issue_date", GNUNET_JSON_from_time_abs (last_cpd),
"eddsa_pub", GNUNET_JSON_from_data_auto (&exchange_pub),
"eddsa_sig", GNUNET_JSON_from_data_auto (&exchange_sig));
GNUNET_assert (NULL != keys);
{
char *keys_json;
void *keys_jsonz;
size_t keys_jsonz_size;
int comp;
/* Convert /keys response to UTF8-String */
keys_json = json_dumps (keys,
JSON_INDENT (2));
json_decref (keys);
GNUNET_assert (NULL != keys_json);
/* Keep copy for later compression... */
keys_jsonz = GNUNET_strdup (keys_json);
keys_jsonz_size = strlen (keys_json);
/* Create uncompressed response */
krd.response_uncompressed
= MHD_create_response_from_buffer (keys_jsonz_size,
keys_json,
MHD_RESPMEM_MUST_FREE);
GNUNET_assert (NULL != krd.response_uncompressed);
GNUNET_assert (GNUNET_OK ==
setup_general_response_headers (ksh,
krd.response_uncompressed));
/* Also compute compressed version of /keys response */
comp = TALER_MHD_body_compress (&keys_jsonz,
&keys_jsonz_size);
krd.response_compressed
= MHD_create_response_from_buffer (keys_jsonz_size,
keys_jsonz,
MHD_RESPMEM_MUST_FREE);
GNUNET_assert (NULL != krd.response_compressed);
/* If the response is actually compressed, set the
respective header. */
GNUNET_assert ( (MHD_YES != comp) ||
(MHD_YES ==
MHD_add_response_header (krd.response_compressed,
MHD_HTTP_HEADER_CONTENT_ENCODING,
"deflate")) );
GNUNET_assert (GNUNET_OK ==
setup_general_response_headers (ksh,
krd.response_compressed));
}
GNUNET_array_append (ksh->krd_array,
ksh->krd_array_length,
krd);
}
/** /**
* Update the "/keys" responses in @a ksh up to @a now into the future. * Update the "/keys" responses in @a ksh up to @a now into the future.
* *
@ -1029,27 +1366,121 @@ build_key_state (struct HelperState *hs)
* *
* *
* @param[in,out] ksh state handle to update * @param[in,out] ksh state handle to update
* @param now timestamp for when to compute the replies.
*/ */
static void static void
update_keys_response (struct TEH_KeyStateHandle *ksh, update_keys_response (struct KeyStateHandle *ksh)
struct GNUNET_TIME_Absolute now)
{ {
// FIXME: update 'krd_array' here! json_t *recoup;
// FIXME: this relates to a good design for cherry-picking, struct SignKeyCtx sctx;
// which we currently don't have for new /keys! json_t *denoms;
struct GNUNET_TIME_Absolute last_cpd;
struct GNUNET_CONTAINER_Heap *heap;
struct GNUNET_HashContext *hash_context;
sctx.signkeys = json_array ();
sctx.next_sk_expire = GNUNET_TIME_UNIT_FOREVER_ABS;
GNUNET_assert (NULL != sctx.signkeys);
GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map,
&add_sign_key_cb,
&sctx);
recoup = json_array ();
GNUNET_assert (NULL != recoup);
heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
{
struct DenomKeyCtx dkc = {
.recoup = recoup,
.heap = heap,
.next_dk_expire = GNUNET_TIME_UNIT_FOREVER_ABS,
};
GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
&add_denom_key_cb,
&dkc);
ksh->next_reload
= GNUNET_TIME_absolute_min (dkc.next_dk_expire,
sctx.next_sk_expire);
}
denoms = json_array ();
GNUNET_assert (NULL != denoms);
last_cpd = GNUNET_TIME_UNIT_ZERO_ABS;
hash_context = GNUNET_CRYPTO_hash_context_start ();
{
struct TEH_DenominationKey *dk;
while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap)))
{
if ( (last_cpd.abs_value_us != dk->meta.start.abs_value_us) &&
(0 != last_cpd.abs_value_us) )
{
struct GNUNET_HashCode hc;
GNUNET_CRYPTO_hash_context_finish (
GNUNET_CRYPTO_hash_context_copy (hash_context),
&hc);
create_krd (ksh,
&hc,
last_cpd,
sctx.signkeys,
recoup,
denoms);
last_cpd = dk->meta.start;
}
GNUNET_CRYPTO_hash_context_read (hash_context,
&dk->h_denom_pub,
sizeof (struct GNUNET_HashCode));
GNUNET_assert (
0 ==
json_array_append_new (
denoms,
json_pack ("{s:o, s:o, s:o, s:o, s:o,"
" s:o, s:o, s:o, s:o, s:o,"
" s:o}",
"master_sig",
GNUNET_JSON_from_data_auto (&dk->master_sig),
"stamp_start",
GNUNET_JSON_from_time_abs (dk->meta.start),
"stamp_expire_withdraw",
GNUNET_JSON_from_time_abs (dk->meta.expire_withdraw),
"stamp_expire_deposit",
GNUNET_JSON_from_time_abs (dk->meta.expire_deposit),
"stamp_expire_legal",
GNUNET_JSON_from_time_abs (dk->meta.expire_legal),
/* 5 entries until here */
"denom_pub",
GNUNET_JSON_from_rsa_public_key (
dk->denom_pub.rsa_public_key),
"value",
TALER_JSON_from_amount (&dk->meta.value),
"fee_withdraw",
TALER_JSON_from_amount (&dk->meta.fee_withdraw),
"fee_deposit",
TALER_JSON_from_amount (&dk->meta.fee_deposit),
"fee_refresh",
TALER_JSON_from_amount (&dk->meta.fee_refresh),
/* 10 entries until here */
"fee_refund",
TALER_JSON_from_amount (&dk->meta.fee_refund))));
}
}
GNUNET_CONTAINER_heap_destroy (heap);
{
struct GNUNET_HashCode hc;
GNUNET_CRYPTO_hash_context_finish (hash_context,
&hc);
create_krd (ksh,
&hc,
last_cpd,
sctx.signkeys,
recoup,
denoms);
}
json_decref (sctx.signkeys);
json_decref (recoup);
json_decref (denoms);
} }
/**
* Something changed in the database. Rebuild all key states. This function
* should be called if the exchange learns about a new signature from an
* auditor or our master key.
*
* (We do not do so immediately, but merely signal to all threads that they
* need to rebuild their key state upon the next call to
* #TEH_get_key_state()).
*/
void void
TEH_keys_update_states () TEH_keys_update_states ()
{ {
@ -1059,17 +1490,16 @@ TEH_keys_update_states ()
/** /**
* Return the current key state for this thread. Possibly * Return the current key state for this thread. Possibly re-builds the key
* re-builds the key state if we have reason to believe * state if we have reason to believe that something changed.
* that something changed.
* *
* @return NULL on error * @return NULL on error
*/ */
struct TEH_KeyStateHandle * static struct KeyStateHandle *
TEH_keys_get_state (void) get_key_state (void)
{ {
struct TEH_KeyStateHandle *old_ksh; struct KeyStateHandle *old_ksh;
struct TEH_KeyStateHandle *ksh; struct KeyStateHandle *ksh;
old_ksh = pthread_getspecific (key_state); old_ksh = pthread_getspecific (key_state);
if (NULL == old_ksh) if (NULL == old_ksh)
@ -1115,10 +1545,10 @@ TEH_keys_denomination_by_hash (
enum TALER_ErrorCode *ec, enum TALER_ErrorCode *ec,
unsigned int *hc) unsigned int *hc)
{ {
struct TEH_KeyStateHandle *ksh; struct KeyStateHandle *ksh;
struct TEH_DenominationKey *dk; struct TEH_DenominationKey *dk;
ksh = TEH_keys_get_state (); ksh = get_key_state ();
if (NULL == ksh) if (NULL == ksh)
{ {
*hc = MHD_HTTP_INTERNAL_SERVER_ERROR; *hc = MHD_HTTP_INTERNAL_SERVER_ERROR;
@ -1144,10 +1574,10 @@ TEH_keys_denomination_sign (
size_t msg_size, size_t msg_size,
enum TALER_ErrorCode *ec) enum TALER_ErrorCode *ec)
{ {
struct TEH_KeyStateHandle *ksh; struct KeyStateHandle *ksh;
struct TALER_DenominationSignature none = { NULL }; struct TALER_DenominationSignature none = { NULL };
ksh = TEH_keys_get_state (); ksh = get_key_state ();
if (NULL == ksh) if (NULL == ksh)
{ {
*ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; *ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
@ -1165,9 +1595,9 @@ void
TEH_keys_denomination_revoke ( TEH_keys_denomination_revoke (
const struct GNUNET_HashCode *h_denom_pub) const struct GNUNET_HashCode *h_denom_pub)
{ {
struct TEH_KeyStateHandle *ksh; struct KeyStateHandle *ksh;
ksh = TEH_keys_get_state (); ksh = get_key_state ();
if (NULL == ksh) if (NULL == ksh)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1185,10 +1615,10 @@ TEH_keys_exchange_sign_ (const struct
struct TALER_ExchangePublicKeyP *pub, struct TALER_ExchangePublicKeyP *pub,
struct TALER_ExchangeSignatureP *sig) struct TALER_ExchangeSignatureP *sig)
{ {
struct TEH_KeyStateHandle *ksh; struct KeyStateHandle *ksh;
enum TALER_ErrorCode ec; enum TALER_ErrorCode ec;
ksh = TEH_keys_get_state (); ksh = get_key_state ();
if (NULL == ksh) if (NULL == ksh)
{ {
/* This *can* happen if the exchange's crypto helper is not running /* This *can* happen if the exchange's crypto helper is not running
@ -1232,9 +1662,9 @@ TEH_keys_exchange_sign_ (const struct
void void
TEH_keys_exchange_revoke (const struct TALER_ExchangePublicKeyP *exchange_pub) TEH_keys_exchange_revoke (const struct TALER_ExchangePublicKeyP *exchange_pub)
{ {
struct TEH_KeyStateHandle *ksh; struct KeyStateHandle *ksh;
ksh = TEH_keys_get_state (); ksh = get_key_state ();
if (NULL == ksh) if (NULL == ksh)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1275,7 +1705,6 @@ TEH_handler_keys_NEW (const struct TEH_RequestHandler *rh,
const char *const args[]) const char *const args[])
{ {
struct GNUNET_TIME_Absolute last_issue_date; struct GNUNET_TIME_Absolute last_issue_date;
struct GNUNET_TIME_Absolute now;
(void) rh; (void) rh;
(void) args; (void) args;
@ -1312,52 +1741,11 @@ TEH_handler_keys_NEW (const struct TEH_RequestHandler *rh,
} }
} }
now = GNUNET_TIME_absolute_get ();
{ {
const char *have_fakenow; struct KeyStateHandle *ksh;
have_fakenow = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"now");
if (NULL != have_fakenow)
{
unsigned long long fakenown;
if (1 !=
sscanf (have_fakenow,
"%llu",
&fakenown))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
have_fakenow);
}
if (TEH_allow_keys_timetravel)
{
/* The following multiplication may overflow; but this should not really
be a problem, as giving back 'older' data than what the client asks for
(given that the client asks for data in the distant future) is not
problematic */
now.abs_value_us = (uint64_t) fakenown * 1000000LLU;
}
else
{
/* Option not allowed by configuration */
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_KEYS_TIMETRAVEL_FORBIDDEN,
NULL);
}
}
}
{
struct TEH_KeyStateHandle *ksh;
const struct KeysResponseData *krd; const struct KeysResponseData *krd;
ksh = TEH_keys_get_state (); ksh = get_key_state ();
if (NULL == ksh) if (NULL == ksh)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1366,8 +1754,7 @@ TEH_handler_keys_NEW (const struct TEH_RequestHandler *rh,
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
"no key state"); "no key state");
} }
update_keys_response (ksh, update_keys_response (ksh);
now);
krd = bsearch (&last_issue_date, krd = bsearch (&last_issue_date,
ksh->krd_array, ksh->krd_array,
ksh->krd_array_length, ksh->krd_array_length,
@ -1416,8 +1803,8 @@ TEH_handler_keys_NEW (const struct TEH_RequestHandler *rh,
* @param[in,out] meta denomination type data to complete * @param[in,out] meta denomination type data to complete
* @return #GNUNET_OK on success * @return #GNUNET_OK on success
*/ */
int static int
TEH_keys_load_fees (const char *section_name, load_fees (const char *section_name,
struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta) struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta)
{ {
struct GNUNET_TIME_Relative deposit_duration; struct GNUNET_TIME_Relative deposit_duration;
@ -1535,6 +1922,59 @@ TEH_keys_load_fees (const char *section_name,
} }
int
TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub,
struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta)
{
struct KeyStateHandle *ksh;
struct HelperDenomination *hd;
ksh = get_key_state ();
if (NULL == ksh)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers.denom_keys,
h_denom_pub);
meta->start = hd->start_time;
meta->expire_withdraw = GNUNET_TIME_absolute_add (meta->start,
hd->validity_duration);
return load_fees (hd->section_name,
meta);
}
int
TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub,
struct GNUNET_TIME_Absolute *start_sign,
struct GNUNET_TIME_Absolute *end_sign,
struct GNUNET_TIME_Absolute *end_legal)
{
struct KeyStateHandle *ksh;
struct HelperSignkey *hsk;
struct GNUNET_PeerIdentity pid;
ksh = get_key_state ();
if (NULL == ksh)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
pid.public_key = exchange_pub->eddsa_pub;
hsk = GNUNET_CONTAINER_multipeermap_get (ksh->helpers.esign_keys,
&pid);
*start_sign = hsk->start_time;
*end_sign = GNUNET_TIME_absolute_add (*start_sign,
hsk->validity_duration);
*end_legal = GNUNET_TIME_absolute_add (*end_sign,
signkey_legal_duration);
return GNUNET_OK;
}
/** /**
* Closure for #add_future_denomkey_cb and #add_future_signkey_cb. * Closure for #add_future_denomkey_cb and #add_future_signkey_cb.
*/ */
@ -1543,7 +1983,7 @@ struct FutureBuilderContext
/** /**
* Our key state. * Our key state.
*/ */
struct TEH_KeyStateHandle *ksh; struct KeyStateHandle *ksh;
/** /**
* Array of denomination keys. * Array of denomination keys.
@ -1587,7 +2027,7 @@ add_future_denomkey_cb (void *cls,
meta.expire_withdraw = GNUNET_TIME_absolute_add (meta.start, meta.expire_withdraw = GNUNET_TIME_absolute_add (meta.start,
hd->validity_duration); hd->validity_duration);
if (GNUNET_OK != if (GNUNET_OK !=
TEH_keys_load_fees (hd->section_name, load_fees (hd->section_name,
&meta)) &meta))
{ {
/* Woops, couldn't determine fee structure!? */ /* Woops, couldn't determine fee structure!? */
@ -1672,24 +2112,15 @@ add_future_signkey_cb (void *cls,
} }
/**
* Function to call to handle requests to "/management/keys" by sending
* back our future key material.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param args array of additional options (must be empty for this function)
* @return MHD result code
*/
MHD_RESULT MHD_RESULT
TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh, TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh,
struct MHD_Connection *connection, struct MHD_Connection *connection,
const char *const args[]) const char *const args[])
{ {
struct TEH_KeyStateHandle *ksh; struct KeyStateHandle *ksh;
json_t *reply; json_t *reply;
ksh = TEH_keys_get_state (); ksh = get_key_state ();
if (NULL == ksh) if (NULL == ksh)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1741,4 +2172,4 @@ TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh,
} }
/* end of taler-exchange-httpd_keystate.c */ /* end of taler-exchange-httpd_keys.c */

View File

@ -191,7 +191,7 @@ TEH_keys_exchange_sign_ (const struct
/* check 'ps' begins with the purpose */ \ /* check 'ps' begins with the purpose */ \
GNUNET_static_assert (((void*) (ps)) == \ GNUNET_static_assert (((void*) (ps)) == \
((void*) &(ps)->purpose)); \ ((void*) &(ps)->purpose)); \
TEH_exchange_sign_ (&(ps)->purpose, \ TEH_keys_exchange_sign_ (&(ps)->purpose, \
pub, \ pub, \
sig); \ sig); \
}) })
@ -243,19 +243,35 @@ TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh,
/** /**
* Load fees and expiration times (!) for the denomination type configured * Load fees and expiration times (!) for the denomination type configured for
* in section @a section_name. Before calling this function, the * the denomination matching @a h_denom_pub.
* `start` and `validity_duration` times must already be initialized in @a meta.
* *
* @param section_name section in the configuration to use * @param h_denom_pub hash of the denomination public key
* @param[in,out] meta denomination type data to complete * to use to derive the section name of the configuration to use
* @param[out] meta denomination type data to complete
* @return #GNUNET_OK on success * @return #GNUNET_OK on success
*/ */
int int
TEH_keys_load_fees (const char *section_name, TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub,
struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta); struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta);
/**
* Load expiration times for the given onling signing key.
*
* @param exchange_pub the online signing key
* @param[out] start_sign starting signing time
* @param[out] end_sign send signing time
* @param[out] end_legal legal expiration time
* @return #GNUNET_OK on success
*/
int
TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub,
struct GNUNET_TIME_Absolute *start_sign,
struct GNUNET_TIME_Absolute *end_sign,
struct GNUNET_TIME_Absolute *end_legal);
/** /**
* Initialize keys submodule. * Initialize keys submodule.
* *

View File

@ -126,21 +126,12 @@ add_keys (void *cls,
bool is_active = false; bool is_active = false;
struct TALER_EXCHANGEDB_DenominationKeyMetaData meta; struct TALER_EXCHANGEDB_DenominationKeyMetaData meta;
qs = TEH_plugin->lookup_future_denomination_key (
TEH_plugin->cls,
session,
&akc->d_sigs[i].h_denom_pub,
&meta);
if (0 == qs)
{
/* For idempotency, check if the key is already active */ /* For idempotency, check if the key is already active */
qs = TEH_plugin->lookup_denomination_key ( qs = TEH_plugin->lookup_denomination_key (
TEH_plugin->cls, TEH_plugin->cls,
session, session,
&akc->d_sigs[i].h_denom_pub, &akc->d_sigs[i].h_denom_pub,
&meta); &meta);
is_active = true; /* if we pass, it's active! */
}
if (qs < 0) if (qs < 0)
{ {
if (GNUNET_DB_STATUS_SOFT_ERROR == qs) if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@ -153,6 +144,10 @@ add_keys (void *cls,
return qs; return qs;
} }
if (0 == qs) if (0 == qs)
{
if (GNUNET_OK !=
TEH_keys_load_fees (&akc->d_sigs[i].h_denom_pub,
&meta))
{ {
*mhd_ret = TALER_MHD_reply_with_error ( *mhd_ret = TALER_MHD_reply_with_error (
connection, connection,
@ -161,6 +156,11 @@ add_keys (void *cls,
GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); GNUNET_h2s (&akc->d_sigs[i].h_denom_pub));
return qs; return qs;
} }
}
else
{
active = true;
}
/* check signature is valid */ /* check signature is valid */
{ {
@ -214,24 +214,17 @@ add_keys (void *cls,
{ {
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
bool is_active = false; bool is_active = false;
struct GNUNET_TIME_Absolute start_sign;
struct GNUNET_TIME_Absolute end_sign;
struct GNUNET_TIME_Absolute end_legal;
// FIXME: future signing keys are currently not in DB,
// may want to get them from in-memory instead.
qs = TEH_plugin->lookup_future_signing_key (
TEH_plugin->cls,
session,
&akc->s_sigs[i].exchange_pub,
&META);
if (0 == qs)
{
/* For idempotency, check if the key is already active */
qs = TEH_plugin->lookup_signing_key ( qs = TEH_plugin->lookup_signing_key (
TEH_plugin->cls, TEH_plugin->cls,
session, session,
&akc->s_sigs[i].exchange_pub, &akc->s_sigs[i].exchange_pub,
&META); &start_sign,
is_active = true; /* if we pass, it's active! */ &end_sign,
} &end_legal);
if (qs < 0) if (qs < 0)
{ {
if (GNUNET_DB_STATUS_SOFT_ERROR == qs) if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@ -245,6 +238,13 @@ add_keys (void *cls,
} }
if (0 == qs) if (0 == qs)
{ {
if (GNUNET_OK !=
TEH_keys_get_timing (&akc->s_sigs[i].exchange_pub,
&start_sign,
&end_sign,
&end_legal))
{
/* For idempotency, check if the key is already active */
*mhd_ret = TALER_MHD_reply_with_error ( *mhd_ret = TALER_MHD_reply_with_error (
connection, connection,
MHD_HTTP_NOT_FOUND, MHD_HTTP_NOT_FOUND,
@ -252,18 +252,22 @@ add_keys (void *cls,
TALER_B2S (&akc->s_sigs[i].exchange_pub)); TALER_B2S (&akc->s_sigs[i].exchange_pub));
return qs; return qs;
} }
}
else
{
is_active = true; /* if we pass, it's active! */
}
/* check signature is valid */ /* check signature is valid */
{ {
if (GNUNET_OK != if (GNUNET_OK !=
TALER_exchange_offline_signkey_validity_verify ( TALER_exchange_offline_signkey_validity_verify (
&akc->s_sigs[i].exchange_pub, &akc->s_sigs[i].exchange_pub,
x, start_sign,
y, end_sign,
z, end_legal,
&TEH_master_public_key, &TEH_master_public_key,
& &akc->s_sigs[i].master_sig))
& akc->s_sigs[i].master_sig))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
return TALER_MHD_reply_with_error ( return TALER_MHD_reply_with_error (
@ -278,7 +282,7 @@ add_keys (void *cls,
qs = TEH_plugin->activate_signing_key ( qs = TEH_plugin->activate_signing_key (
TEH_plugin->cls, TEH_plugin->cls,
session, session,
&akc->s_sigs[i].exchange_pub, &akc->s_sigs[i].exchange_pub, // FIXME: provision meta data!?
&akc->s_sigs[i].master_sig); &akc->s_sigs[i].master_sig);
if (qs < 0) if (qs < 0)
{ {