more code towards fixing #5447

This commit is contained in:
Christian Grothoff 2019-01-13 16:22:16 +01:00
parent 54fc83ee6b
commit 91566ddee2
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
5 changed files with 301 additions and 87 deletions

2
.gitignore vendored
View File

@ -107,3 +107,5 @@ contrib/auditor-report.pdf
src/bank-lib/taler-bank-transfer
src/bank-lib/test_bank_api_twisted
src/lib/test_exchange_api_new
src/lib/test_auditor_api
src/lib/test_exchange_api_overlapping_keys_bug

View File

@ -48,7 +48,7 @@ enum TALER_EXCHANGE_Option
* /keys data (or at least only download the deltas).
*/
TALER_EXCHANGE_OPTION_DATA
};
@ -66,7 +66,7 @@ struct TALER_EXCHANGE_SigningPublicKey
* Signature over this signing key by the exchange's master signature.
*/
struct TALER_MasterSignatureP master_sig;
/**
* Validity start time
*/
@ -103,7 +103,7 @@ struct TALER_EXCHANGE_DenomPublicKey
* Exchange's master signature over this denomination record.
*/
struct TALER_MasterSignatureP master_sig;
/**
* Timestamp indicating when the denomination key becomes valid
*/
@ -167,7 +167,7 @@ struct TALER_EXCHANGE_AuditorDenominationInfo
* denomination.
*/
struct TALER_AuditorSignatureP auditor_sig;
/**
* Offsets into the key's main `denom_keys` array identifying the
* denomination being audited by this auditor.
@ -201,7 +201,7 @@ struct TALER_EXCHANGE_AuditorInformation
/**
* Array of length @a num_denom_keys with the denomination
* keys audited by this auditor.
* keys audited by this auditor.
*/
struct TALER_EXCHANGE_AuditorDenominationInfo *denom_keys;
@ -393,7 +393,7 @@ TALER_EXCHANGE_connect (struct GNUNET_CURL_Context *ctx,
/**
* Serialize the latest key data from @a exchange to be persisted
* on disk (to be used with #TALER_EXCHANGE_OPTION_DATA to more
* on disk (to be used with #TALER_EXCHANGE_OPTION_DATA to more
* efficiently recover the state).
*
* @param exchange which exchange's key and wire data should be serialized

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015, 2018 GNUnet e.V.
Copyright (C) 2014, 2015, 2018, 2019 GNUnet e.V.
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
@ -75,6 +75,16 @@ struct TALER_EXCHANGE_DepositHandle
*/
struct TALER_DepositConfirmationPS depconf;
/**
* Exchange signature, set for #auditor_cb.
*/
struct TALER_ExchangeSignatureP exchange_sig;
/**
* Exchange signing public key, set for #auditor_cb.
*/
struct TALER_ExchangePublicKeyP exchange_pub;
/**
* Value of the /deposit transaction, including fee.
*/
@ -89,21 +99,52 @@ struct TALER_EXCHANGE_DepositHandle
/**
* Signature of functions called with the result from our call to the
* auditor's /deposit-confirmation handler.
* Function called for each auditor to give us a chance to possibly
* launch a deposit confirmation interaction.
*
* @param cls closure
* @param http_status HTTP status code, 200 on success
* @param ec taler protocol error status code, 0 on success
* @param json raw json response
* @param ah handle to the auditor
* @param auditor_pub public key of the auditor
* @return NULL if no deposit confirmation interaction was launched
*/
static void
acc_confirmation_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const json_t *json)
static struct TEAH_AuditorInteractionEntry *
auditor_cb (void *cls,
struct TALER_AUDITOR_Handle *ah,
const struct TALER_AuditorPublicKeyP *auditor_pub)
{
/* FIXME: clean up state, some logging on errors! */
struct TALER_EXCHANGE_DepositHandle *dh = cls;
const struct TALER_EXCHANGE_Keys *key_state;
const struct TALER_EXCHANGE_SigningPublicKey *spk;
struct TALER_Amount amount_without_fee;
struct TEAH_AuditorInteractionEntry *aie;
if (1 /* #5447: replace with "for all auditors, if auditor selected for DC notification... */)
return NULL;
key_state = TALER_EXCHANGE_get_keys (dh->exchange);
spk = TALER_EXCHANGE_get_signing_key_details (key_state,
&dh->exchange_pub);
GNUNET_assert (NULL != spk);
TALER_amount_ntoh (&amount_without_fee,
&dh->depconf.amount_without_fee);
aie = GNUNET_new (struct TEAH_AuditorInteractionEntry);
aie->dch = TALER_AUDITOR_deposit_confirmation (ah,
&dh->depconf.h_wire,
&dh->depconf.h_contract_terms,
GNUNET_TIME_absolute_ntoh (dh->depconf.timestamp),
GNUNET_TIME_absolute_ntoh (dh->depconf.refund_deadline),
&amount_without_fee,
&dh->depconf.coin_pub,
&dh->depconf.merchant,
&dh->exchange_pub,
&dh->exchange_sig,
&key_state->master_pub,
spk->valid_from,
spk->valid_until,
spk->valid_legal,
&spk->master_sig,
&TEAH_acc_confirmation_cb,
aie);
return aie;
}
@ -118,7 +159,7 @@ acc_confirmation_cb (void *cls,
* @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
*/
static int
verify_deposit_signature_ok (const struct TALER_EXCHANGE_DepositHandle *dh,
verify_deposit_signature_ok (struct TALER_EXCHANGE_DepositHandle *dh,
const json_t *json,
struct TALER_ExchangeSignatureP *exchange_sig,
struct TALER_ExchangePublicKeyP *exchange_pub)
@ -155,37 +196,11 @@ verify_deposit_signature_ok (const struct TALER_EXCHANGE_DepositHandle *dh,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (0 /* #5447: replace with "for all auditors, if auditor selected for DC notification... */)
{
struct TALER_AUDITOR_DepositConfirmationHandle *dch;
const struct TALER_EXCHANGE_SigningPublicKey *spk;
struct TALER_Amount amount_without_fee;
spk = TALER_EXCHANGE_get_signing_key_details (key_state,
exchange_pub);
GNUNET_assert (NULL != spk);
TALER_amount_ntoh (&amount_without_fee,
&dh->depconf.amount_without_fee);
dch = TALER_AUDITOR_deposit_confirmation (NULL /* FIXME: auditor */,
&dh->depconf.h_wire,
&dh->depconf.h_contract_terms,
GNUNET_TIME_absolute_ntoh (dh->depconf.timestamp),
GNUNET_TIME_absolute_ntoh (dh->depconf.refund_deadline),
&amount_without_fee,
&dh->depconf.coin_pub,
&dh->depconf.merchant,
exchange_pub,
exchange_sig,
&key_state->master_pub,
spk->valid_from,
spk->valid_until,
spk->valid_legal,
&spk->master_sig,
&acc_confirmation_cb,
NULL /* FIXME: context! */);
}
dh->exchange_sig = *exchange_sig;
dh->exchange_pub = *exchange_pub;
TEAH_get_auditors_for_dc (dh->exchange,
&auditor_cb,
dh);
return GNUNET_OK;
}

View File

@ -91,47 +91,25 @@ enum ExchangeHandleState
struct KeysRequest;
/**
* Entry in list of ongoing interactions with an auditor.
*/
struct AuditorInteractionEntry
{
/**
* DLL entry.
*/
struct AuditorInteractionEntry *next;
/**
* DLL entry.
*/
struct AuditorInteractionEntry *prev;
/**
* Interaction state.
*/
struct TALER_AUDITOR_DepositConfirmationHandle *dch;
};
/**
* Entry in DLL of auditors used by an exchange.
*/
struct AuditorListEntry
struct TEAH_AuditorListEntry
{
/**
* Next pointer of DLL.
*/
struct AuditorListEntry *next;
struct TEAH_AuditorListEntry *next;
/**
* Prev pointer of DLL.
*/
struct AuditorListEntry *prev;
struct TEAH_AuditorListEntry *prev;
/**
* Base URL of the auditor.
*/
const char *auditor_url;
char *auditor_url;
/**
* Handle to the auditor.
@ -141,12 +119,12 @@ struct AuditorListEntry
/**
* Head of DLL of interactions with this auditor.
*/
struct AuditorInteractionEntry *ai_head;
struct TEAH_AuditorInteractionEntry *ai_head;
/**
* Tail of DLL of interactions with this auditor.
*/
struct AuditorInteractionEntry *ai_tail;
struct TEAH_AuditorInteractionEntry *ai_tail;
/**
* Public key of the auditor.
@ -208,12 +186,12 @@ struct TALER_EXCHANGE_Handle
/**
* Head of DLL of auditors of this exchange.
*/
struct AuditorListEntry *auditors_head;
struct TEAH_AuditorListEntry *auditors_head;
/**
* Tail of DLL of auditors of this exchange.
*/
struct AuditorListEntry *auditors_tail;
struct TEAH_AuditorListEntry *auditors_tail;
/**
* Key data of the exchange, only valid if
@ -270,6 +248,39 @@ struct KeysRequest
};
/**
* Signature of functions called with the result from our call to the
* auditor's /deposit-confirmation handler.
*
* @param cls closure of type `struct TEAH_AuditorInteractionEntry *`
* @param http_status HTTP status code, 200 on success
* @param ec taler protocol error status code, 0 on success
* @param json raw json response
*/
void
TEAH_acc_confirmation_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const json_t *json)
{
struct TEAH_AuditorInteractionEntry *aie = cls;
struct TEAH_AuditorListEntry *ale = aie->ale;
if (MHD_HTTP_OK != http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_("Failed to submit deposit confirmation to auditor `%s' with HTTP status %d (EC: %d). This is acceptable if it does not happen often.\n"),
ale->auditor_url,
http_status,
(int) ec);
}
GNUNET_CONTAINER_DLL_remove (ale->ai_head,
ale->ai_tail,
aie);
GNUNET_free (aie);
}
/**
* Iterate over all available auditors for @a h, calling
* @param ah and giving it a chance to start a deposit
@ -284,14 +295,38 @@ TEAH_get_auditors_for_dc (struct TALER_EXCHANGE_Handle *h,
TEAH_AuditorCallback ac,
void *ac_cls)
{
// FIXME!
if (NULL == h->auditors_head)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("No auditor available for exchange `%s'. Not submitting deposit confirmations.\n"),
h->url);
return;
}
for (struct TEAH_AuditorListEntry *ale = h->auditors_head;
NULL != ale;
ale = ale->next)
{
struct TEAH_AuditorInteractionEntry *aie;
if (GNUNET_NO == ale->is_up)
continue;
aie = ac (ac_cls,
ale->ah,
&ale->auditor_pub);
if (NULL != aie)
{
aie->ale = ale;
GNUNET_CONTAINER_DLL_insert (ale->ai_head,
ale->ai_tail,
aie);
}
}
}
/**
* Release memory occupied by a keys request.
* Note that this does not cancel the request
* itself.
* Release memory occupied by a keys request. Note that this does not
* cancel the request itself.
*
* @param kr request to free
*/
@ -601,6 +636,90 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor,
}
/**
* Function called with information about the auditor. Marks an
* auditor as 'up'.
*
* @param cls closure, a `struct TEAH_AuditorListEntry *`
* @param vi basic information about the auditor
* @param compat protocol compatibility information
*/
static void
auditor_version_cb (void *cls,
const struct TALER_AUDITOR_VersionInformation *vi,
enum TALER_AUDITOR_VersionCompatibility compat)
{
struct TEAH_AuditorListEntry *ale = cls;
if (0 != (TALER_AUDITOR_VC_INCOMPATIBLE & compat))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_("Auditor `%s' runs incompatible protocol version!\n"),
ale->auditor_url);
if (0 != (TALER_AUDITOR_VC_OLDER & compat))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Auditor `%s' runs outdated protocol version!\n"),
ale->auditor_url);
}
if (0 != (TALER_AUDITOR_VC_NEWER & compat))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_("Auditor `%s' runs more recent incompatible version. We should upgrade!\n"),
ale->auditor_url);
}
return;
}
ale->is_up = GNUNET_YES;
}
/**
* Recalculate our auditor list, we got /keys and it may have
* changed.
*
* @param exchange exchange for which to update the list.
*/
static void
update_auditors (struct TALER_EXCHANGE_Handle *exchange)
{
struct TALER_EXCHANGE_Keys *kd = &exchange->key_data;
for (unsigned int i=0;i<kd->num_auditors;i++)
{
struct TALER_EXCHANGE_AuditorInformation *auditor = &kd->auditors[i];
struct TEAH_AuditorListEntry *ale = NULL;
for (struct TEAH_AuditorListEntry *a = exchange->auditors_head;
NULL != a;
a = a->next)
{
if (0 == memcmp (&auditor->auditor_pub,
&a->auditor_pub,
sizeof (struct TALER_AuditorPublicKeyP)))
{
ale = a;
break;
}
}
if (NULL != ale)
continue; /* found, no need to add */
/* new auditor, add */
ale = GNUNET_new (struct TEAH_AuditorListEntry);
ale->auditor_pub = auditor->auditor_pub;
ale->auditor_url = GNUNET_strdup (auditor->auditor_url);
GNUNET_CONTAINER_DLL_insert (exchange->auditors_head,
exchange->auditors_tail,
ale);
ale->ah = TALER_AUDITOR_connect (exchange->ctx,
ale->auditor_url,
&auditor_version_cb,
ale);
}
}
/**
* Decode the JSON in @a resp_obj from the /keys response
* and store the data in the @a key_data.
@ -827,6 +946,7 @@ decode_keys_json (const json_t *resp_obj,
GNUNET_array_grow (key_data->auditors,
key_data->auditors_size,
key_data->auditors_size * 2 + 2);
GNUNET_assert (NULL != ai.auditor_url);
key_data->auditors[key_data->num_auditors++] = ai;
};
}
@ -997,6 +1117,7 @@ keys_completed_cb (void *cls,
struct TALER_EXCHANGE_AuditorInformation *anew = &kd.auditors[i];
anew->auditor_pub = aold->auditor_pub;
GNUNET_assert (NULL != aold->auditor_url);
anew->auditor_url = GNUNET_strdup (aold->auditor_url);
GNUNET_array_grow (anew->denom_keys,
anew->num_denom_keys,
@ -1072,6 +1193,7 @@ keys_completed_cb (void *cls,
exchange->key_data_expiration = kr->expire;
free_keys_request (kr);
exchange->state = MHS_CERT;
update_auditors (exchange);
/* notify application about the key information */
exchange->cert_cb (exchange->cert_cb_cls,
&exchange->key_data,
@ -1305,6 +1427,7 @@ deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
exchange->key_data = key_data;
exchange->key_data_expiration = expire;
exchange->state = MHS_CERT;
update_auditors (exchange);
/* notify application about the key information */
exchange->cert_cb (exchange->cert_cb_cls,
&exchange->key_data,
@ -1619,6 +1742,31 @@ request_keys (void *cls)
void
TALER_EXCHANGE_disconnect (struct TALER_EXCHANGE_Handle *exchange)
{
struct TEAH_AuditorListEntry *ale;
while (NULL != (ale = exchange->auditors_head))
{
struct TEAH_AuditorInteractionEntry *aie;
while (NULL != (aie = ale->ai_head))
{
GNUNET_assert (aie->ale == ale);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Not sending deposit confirmation to auditor `%s' due to exchange disconnect\n"),
ale->auditor_url);
TALER_AUDITOR_deposit_confirmation_cancel (aie->dch);
GNUNET_CONTAINER_DLL_remove (ale->ai_head,
ale->ai_tail,
aie);
GNUNET_free (aie);
}
GNUNET_CONTAINER_DLL_remove (exchange->auditors_head,
exchange->auditors_tail,
ale);
TALER_AUDITOR_disconnect (ale->ah);
GNUNET_free (ale->auditor_url);
GNUNET_free (ale);
}
if (NULL != exchange->kr)
{
GNUNET_CURL_job_cancel (exchange->kr->job);

View File

@ -26,21 +26,70 @@
#include "taler_crypto_lib.h"
/**
* Entry in DLL of auditors used by an exchange.
*/
struct TEAH_AuditorListEntry;
/**
* Entry in list of ongoing interactions with an auditor.
*/
struct TEAH_AuditorInteractionEntry
{
/**
* DLL entry.
*/
struct TEAH_AuditorInteractionEntry *next;
/**
* DLL entry.
*/
struct TEAH_AuditorInteractionEntry *prev;
/**
* Which auditor is this action associated with?
*/
struct TEAH_AuditorListEntry *ale;
/**
* Interaction state.
*/
struct TALER_AUDITOR_DepositConfirmationHandle *dch;
};
/**
* Function called for each auditor to give us a chance to possibly
* launch a deposit confirmation interaction.
*
* launch a deposit confirmation interaction.
*
* @param cls closure
* @param ah handle to the auditor
* @param auditor_pub public key of the auditor
* @return NULL if no deposit confirmation interaction was launched
*/
typedef struct TALER_AUDITOR_DepositConfirmationHandle *
typedef struct TEAH_AuditorInteractionEntry *
(*TEAH_AuditorCallback)(void *cls,
struct TALER_AUDITOR_Handle *ah,
const struct TALER_AuditorPublicKeyP *auditor_pub);
/**
* Signature of functions called with the result from our call to the
* auditor's /deposit-confirmation handler.
*
* @param cls closure of type `struct TEAH_AuditorInteractionEntry *`
* @param http_status HTTP status code, 200 on success
* @param ec taler protocol error status code, 0 on success
* @param json raw json response
*/
void
TEAH_acc_confirmation_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const json_t *json);
/**
* Iterate over all available auditors for @a h, calling
* @param ah and giving it a chance to start a deposit