diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/exchange_api_handle.c | 1588 | ||||
-rw-r--r-- | src/lib/exchange_api_handle.h | 174 |
2 files changed, 546 insertions, 1216 deletions
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index 0bb3c2085..7fcb590a3 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -48,6 +48,11 @@ #define EXCHANGE_PROTOCOL_AGE 0 /** + * Set to 1 for extra debug logging. + */ +#define DEBUG 0 + +/** * Current version for (local) JSON serialization of persisted * /keys data. */ @@ -64,103 +69,58 @@ */ #define DEFAULT_EXPIRATION GNUNET_TIME_UNIT_HOURS -/** - * Set to 1 for extra debug logging. - */ -#define DEBUG 0 /** - * Log error related to CURL operations. - * - * @param type log level - * @param function which function failed to run - * @param code what was the curl error code + * Handle for a GET /keys request. */ -#define CURL_STRERROR(type, function, code) \ - GNUNET_log (type, "Curl function `%s' has failed at `%s:%d' with error: %s", \ - function, __FILE__, __LINE__, curl_easy_strerror (code)); - - -/** - * Data for the request to get the /keys of a exchange. - */ -struct KeysRequest; - - -/** - * Entry in DLL of auditors used by an exchange. - */ -struct TEAH_AuditorListEntry +struct TALER_EXCHANGE_GetKeysHandle { - /** - * Next pointer of DLL. - */ - struct TEAH_AuditorListEntry *next; - - /** - * Prev pointer of DLL. - */ - struct TEAH_AuditorListEntry *prev; /** - * Base URL of the auditor. + * The exchange base URL (i.e. "http://exchange.taler.net/") */ - char *auditor_url; + char *exchange_url; /** - * Handle to the auditor. + * The url for the /keys request. */ - struct TALER_AUDITOR_GetConfigHandle *ah; + char *url; /** - * Public key of the auditor. + * Previous /keys response, NULL for none. */ - struct TALER_AuditorPublicKeyP auditor_pub; + struct TALER_EXCHANGE_Keys *prev_keys; /** - * Flag indicating that the auditor is available and that protocol - * version compatibility is given. + * Entry for this request with the `struct GNUNET_CURL_Context`. */ - bool is_up; - -}; - - -/* ***************** Internal /keys fetching ************* */ + struct GNUNET_CURL_Job *job; -/** - * Data for the request to get the /keys of a exchange. - */ -struct KeysRequest -{ /** - * The connection to exchange this request handle will use + * Expiration time according to "Expire:" header. + * 0 if not provided by the server. */ - struct TALER_EXCHANGE_Handle *exchange; + struct GNUNET_TIME_Timestamp expire; /** - * The url for this handle + * Function to call with the exchange's certification data, + * NULL if this has already been done. */ - char *url; + TALER_EXCHANGE_GetKeysCallback cert_cb; /** - * Entry for this request with the `struct GNUNET_CURL_Context`. + * Closure to pass to @e cert_cb. */ - struct GNUNET_CURL_Job *job; - - /** - * Expiration time according to "Expire:" header. - * 0 if not provided by the server. - */ - struct GNUNET_TIME_Timestamp expire; + void *cert_cb_cls; }; void -TEAH_get_auditors_for_dc (struct TALER_EXCHANGE_Keys *keys, - TEAH_AuditorCallback ac, - void *ac_cls) +TEAH_get_auditors_for_dc ( + struct TALER_EXCHANGE_Keys *keys, + TEAH_AuditorCallback ac, + void *ac_cls) { if (0 == keys->num_auditors) { @@ -180,20 +140,6 @@ TEAH_get_auditors_for_dc (struct TALER_EXCHANGE_Keys *keys, } -/** - * Release memory occupied by a keys request. Note that this does not - * cancel the request itself. - * - * @param kr request to free - */ -static void -free_keys_request (struct KeysRequest *kr) -{ - GNUNET_free (kr->url); - GNUNET_free (kr); -} - - #define EXITIF(cond) \ do { \ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ @@ -205,21 +151,20 @@ free_keys_request (struct KeysRequest *kr) * * @param[out] sign_key where to return the result * @param check_sigs should we check signatures? - * @param[in] sign_key_obj json to parse + * @param sign_key_obj json to parse * @param master_key master key to use to verify signature * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is - * invalid or the json malformed. + * invalid or the @a sign_key_obj is malformed. */ static enum GNUNET_GenericReturnValue parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key, bool check_sigs, - json_t *sign_key_obj, + const json_t *sign_key_obj, const struct TALER_MasterPublicKeyP *master_key) { - struct TALER_MasterSignatureP sign_key_issue_sig; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("master_sig", - &sign_key_issue_sig), + &sign_key->master_sig), GNUNET_JSON_spec_fixed_auto ("key", &sign_key->key), GNUNET_JSON_spec_timestamp ("stamp_start", @@ -239,7 +184,6 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key, GNUNET_break_op (0); return GNUNET_SYSERR; } - if (! check_sigs) return GNUNET_OK; if (GNUNET_OK != @@ -249,12 +193,11 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key, sign_key->valid_until, sign_key->valid_legal, master_key, - &sign_key_issue_sig)) + &sign_key->master_sig)) { GNUNET_break_op (0); return GNUNET_SYSERR; } - sign_key->master_sig = sign_key_issue_sig; return GNUNET_OK; } @@ -270,9 +213,9 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key, * @param[out] denom_key where to return the result * @param cipher cipher type to parse * @param check_sigs should we check signatures? - * @param[in] denom_key_obj json to parse + * @param denom_key_obj json to parse * @param master_key master key to use to verify signature - * @param hash_xor where to accumulate data for signature verification via XOR + * @param[in,out] hash_xor where to accumulate data for signature verification via XOR * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is * invalid or the json malformed. */ @@ -281,7 +224,7 @@ parse_json_denomkey_partially ( struct TALER_EXCHANGE_DenomPublicKey *denom_key, enum TALER_DenominationCipher cipher, bool check_sigs, - json_t *denom_key_obj, + const json_t *denom_key_obj, struct TALER_MasterPublicKeyP *master_key, struct GNUNET_HashCode *hash_xor) { @@ -316,7 +259,6 @@ parse_json_denomkey_partially ( GNUNET_CRYPTO_hash_xor (&denom_key->h_key.hash, hash_xor, hash_xor); - if (! check_sigs) return GNUNET_OK; EXITIF (GNUNET_SYSERR == @@ -346,7 +288,7 @@ EXITIF_exit: * * @param[out] auditor where to return the result * @param check_sigs should we check signatures - * @param[in] auditor_obj json to parse + * @param auditor_obj json to parse * @param key_data information about denomination keys * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is * invalid or the json malformed. @@ -354,14 +296,12 @@ EXITIF_exit: static enum GNUNET_GenericReturnValue parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor, bool check_sigs, - json_t *auditor_obj, + const json_t *auditor_obj, const struct TALER_EXCHANGE_Keys *key_data) { const json_t *keys; json_t *key; - unsigned int len; unsigned int off; - unsigned int i; const char *auditor_url; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("auditor_pub", @@ -387,16 +327,15 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor, return GNUNET_SYSERR; } auditor->auditor_url = GNUNET_strdup (auditor_url); - len = json_array_size (keys); - auditor->denom_keys = GNUNET_new_array (len, - struct - TALER_EXCHANGE_AuditorDenominationInfo); - off = 0; - json_array_foreach (keys, i, key) { + auditor->denom_keys + = GNUNET_new_array (json_array_size (keys), + struct TALER_EXCHANGE_AuditorDenominationInfo); + + json_array_foreach (keys, off, key) { struct TALER_AuditorSignatureP auditor_sig; struct TALER_DenominationHashP denom_h; - const struct TALER_EXCHANGE_DenomPublicKey *dk; - unsigned int dk_off; + const struct TALER_EXCHANGE_DenomPublicKey *dk = NULL; + unsigned int dk_off = UINT_MAX; struct GNUNET_JSON_Specification kspec[] = { GNUNET_JSON_spec_fixed_auto ("auditor_sig", &auditor_sig), @@ -413,8 +352,6 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor, GNUNET_break_op (0); continue; } - dk = NULL; - dk_off = UINT_MAX; for (unsigned int j = 0; j<key_data->num_denom_keys; j++) { if (0 == GNUNET_memcmp (&denom_h, @@ -454,7 +391,6 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor, } auditor->denom_keys[off].denom_key_offset = dk_off; auditor->denom_keys[off].auditor_sig = auditor_sig; - off++; } auditor->num_denom_keys = off; return GNUNET_OK; @@ -466,7 +402,7 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor, * * @param[out] gf where to return the result * @param check_sigs should we check signatures - * @param[in] fee_obj json to parse + * @param fee_obj json to parse * @param key_data already parsed information about the exchange * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is * invalid or the json malformed. @@ -474,7 +410,7 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor, static enum GNUNET_GenericReturnValue parse_global_fee (struct TALER_EXCHANGE_GlobalFee *gf, bool check_sigs, - json_t *fee_obj, + const json_t *fee_obj, const struct TALER_EXCHANGE_Keys *key_data) { struct GNUNET_JSON_Specification spec[] = { @@ -532,104 +468,6 @@ parse_global_fee (struct TALER_EXCHANGE_GlobalFee *gf, /** - * Function called with information about the auditor. Marks an - * auditor as 'up'. - * - * @param cls closure, a `struct TEAH_AuditorListEntry *` - * @param vr response from the auditor - */ -static void -auditor_config_cb ( - void *cls, - const struct TALER_AUDITOR_ConfigResponse *vr) -{ - struct TEAH_AuditorListEntry *ale = cls; - enum TALER_AUDITOR_VersionCompatibility compat; - - ale->ah = NULL; - if (MHD_HTTP_OK != vr->hr.http_status) - { - /* In this case, we don't mark the auditor as 'up' */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Auditor `%s' gave unexpected version response.\n", - ale->auditor_url); - return; - } - compat = vr->details.ok.compat; - 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 = true; -} - - -/** - * 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; - - TALER_LOG_DEBUG ("Updating auditors\n"); - for (unsigned int i = 0; i<kd->num_auditors; i++) - { - /* Compare auditor data from /keys with auditor data - * from owned exchange structures. */ - 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 == GNUNET_memcmp (&auditor->auditor_pub, - &a->auditor_pub)) - { - ale = a; - break; - } - } - if (NULL != ale) - continue; /* found, no need to add */ - - /* new auditor, add */ - TALER_LOG_DEBUG ("Found new auditor %s!\n", - auditor->auditor_url); - 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_get_config (exchange->ctx, - ale->auditor_url, - &auditor_config_cb, - ale); - } -} - - -/** * Compare two denomination keys. Ignores revocation data. * * @param denom1 first denomination key @@ -683,35 +521,16 @@ decode_keys_json (const json_t *resp_obj, struct TALER_ExchangeSignatureP denominations_sig; struct GNUNET_HashCode hash_xor = {0}; struct TALER_ExchangePublicKeyP pub; - const char *currency; - const char *asset_type; - bool tipping_allowed = true; const json_t *wblwk = NULL; - struct GNUNET_JSON_Specification mspec[] = { - GNUNET_JSON_spec_fixed_auto ("denominations_sig", - &denominations_sig), - GNUNET_JSON_spec_fixed_auto ("eddsa_pub", - &pub), - GNUNET_JSON_spec_fixed_auto ("master_public_key", - &key_data->master_pub), - GNUNET_JSON_spec_timestamp ("list_issue_date", - &key_data->list_issue_date), - GNUNET_JSON_spec_relative_time ("reserve_closing_delay", - &key_data->reserve_closing_delay), - GNUNET_JSON_spec_string ("currency", - ¤cy), - GNUNET_JSON_spec_string ("asset_type", - &asset_type), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_bool ("tipping_allowed", - &tipping_allowed), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("wallet_balance_limit_without_kyc", - &wblwk), - NULL), - GNUNET_JSON_spec_end () - }; + const json_t *global_fees; + const json_t *sign_keys_array; + const json_t *denominations_by_group; + const json_t *auditors_array; + const json_t *recoup_array = NULL; + struct TALER_MasterSignatureP extensions_sig = {0}; + const json_t *manifests = NULL; + bool no_extensions = false; + bool no_signature = false; if (JSON_OBJECT != json_typeof (resp_obj)) { @@ -770,63 +589,116 @@ decode_keys_json (const json_t *resp_obj, key_data->version = GNUNET_strdup (ver); } - EXITIF (GNUNET_OK != - GNUNET_JSON_parse (resp_obj, - (check_sig) ? mspec : &mspec[2], - NULL, NULL)); - key_data->currency = GNUNET_strdup (currency); - key_data->asset_type = GNUNET_strdup (asset_type); - key_data->tipping_allowed = tipping_allowed; + { + const char *currency; + const char *asset_type; + struct GNUNET_JSON_Specification mspec[] = { + GNUNET_JSON_spec_fixed_auto ( + "denominations_sig", + &denominations_sig), + GNUNET_JSON_spec_fixed_auto ( + "eddsa_pub", + &pub), + GNUNET_JSON_spec_fixed_auto ( + "master_public_key", + &key_data->master_pub), + GNUNET_JSON_spec_timestamp ( + "list_issue_date", + &key_data->list_issue_date), + GNUNET_JSON_spec_relative_time ( + "reserve_closing_delay", + &key_data->reserve_closing_delay), + GNUNET_JSON_spec_string ( + "currency", + ¤cy), + GNUNET_JSON_spec_string ( + "asset_type", + &asset_type), + GNUNET_JSON_spec_array_const ( + "global_fees", + &global_fees), + GNUNET_JSON_spec_array_const ( + "signkeys", + &sign_keys_array), + GNUNET_JSON_spec_array_const ( + "denominations", + &denominations_by_group), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ( + "recoup", + &recoup_array), + NULL), + GNUNET_JSON_spec_array_const ( + "auditors", + &auditors_array), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_bool ( + "tipping_allowed", + &key_data->tipping_allowed), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("extensions", + &manifests), + &no_extensions), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ( + "extensions_sig", + &extensions_sig), + &no_signature), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ( + "wallet_balance_limit_without_kyc", + &wblwk), + NULL), + GNUNET_JSON_spec_end () + }; + + EXITIF (GNUNET_OK != + GNUNET_JSON_parse (resp_obj, + (check_sig) ? mspec : &mspec[2], + NULL, NULL)); + key_data->currency = GNUNET_strdup (currency); + key_data->asset_type = GNUNET_strdup (asset_type); + } /* parse the global fees */ + key_data->num_global_fees + = json_array_size (global_fees); + if (0 != key_data->num_global_fees) { - json_t *global_fees; json_t *global_fee; unsigned int index; - EXITIF (NULL == (global_fees = - json_object_get (resp_obj, - "global_fees"))); - EXITIF (! json_is_array (global_fees)); - if (0 != (key_data->num_global_fees = - json_array_size (global_fees))) + key_data->global_fees + = GNUNET_new_array (key_data->num_global_fees, + struct TALER_EXCHANGE_GlobalFee); + json_array_foreach (global_fees, index, global_fee) { - key_data->global_fees - = GNUNET_new_array (key_data->num_global_fees, - struct TALER_EXCHANGE_GlobalFee); - json_array_foreach (global_fees, index, global_fee) { - EXITIF (GNUNET_SYSERR == - parse_global_fee (&key_data->global_fees[index], - check_sig, - global_fee, - key_data)); - } + EXITIF (GNUNET_SYSERR == + parse_global_fee (&key_data->global_fees[index], + check_sig, + global_fee, + key_data)); } } /* parse the signing keys */ + key_data->num_sign_keys + = json_array_size (sign_keys_array); + if (0 != key_data->num_sign_keys) { - json_t *sign_keys_array; json_t *sign_key_obj; unsigned int index; - EXITIF (NULL == (sign_keys_array = - json_object_get (resp_obj, - "signkeys"))); - EXITIF (! json_is_array (sign_keys_array)); - if (0 != (key_data->num_sign_keys = - json_array_size (sign_keys_array))) - { - key_data->sign_keys - = GNUNET_new_array (key_data->num_sign_keys, - struct TALER_EXCHANGE_SigningPublicKey); - json_array_foreach (sign_keys_array, index, sign_key_obj) { - EXITIF (GNUNET_SYSERR == - parse_json_signkey (&key_data->sign_keys[index], - check_sig, - sign_key_obj, - &key_data->master_pub)); - } + key_data->sign_keys + = GNUNET_new_array (key_data->num_sign_keys, + struct TALER_EXCHANGE_SigningPublicKey); + json_array_foreach (sign_keys_array, index, sign_key_obj) { + EXITIF (GNUNET_SYSERR == + parse_json_signkey (&key_data->sign_keys[index], + check_sig, + sign_key_obj, + &key_data->master_pub)); } } @@ -844,7 +716,7 @@ decode_keys_json (const json_t *resp_obj, i); struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_amount (NULL, - currency, + key_data->currency, a), GNUNET_JSON_spec_end () }; @@ -858,51 +730,28 @@ decode_keys_json (const json_t *resp_obj, /* Parse the supported extension(s): age-restriction. */ /* TODO: maybe lift all this into a FP in TALER_Extension ? */ + if (! no_extensions) { - struct TALER_MasterSignatureP extensions_sig = {0}; - const json_t *manifests = NULL; - bool no_extensions = false; - bool no_signature = false; - - struct GNUNET_JSON_Specification ext_spec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("extensions", - &manifests), - &no_extensions), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_fixed_auto ( - "extensions_sig", - &extensions_sig), - &no_signature), - GNUNET_JSON_spec_end () - }; - - /* 1. Search for extensions in the response to /keys */ - EXITIF (GNUNET_OK != - GNUNET_JSON_parse (resp_obj, - ext_spec, - NULL, NULL)); - - - if (! no_extensions && no_signature) + if (no_signature) + { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "found extensions without signature\n"); - - if (! no_extensions && ! no_signature) + } + else { - /* 2. We have an extensions object. Verify its signature. */ + /* We have an extensions object. Verify its signature. */ EXITIF (GNUNET_OK != TALER_extensions_verify_manifests_signature ( manifests, &extensions_sig, &key_data->master_pub)); - /* 3. Parse and set the the configuration of the extensions accordingly */ + /* Parse and set the the configuration of the extensions accordingly */ EXITIF (GNUNET_OK != TALER_extensions_load_manifests (manifests)); } - /* 4. assuming we might have now a new value for age_mask, set it in key_data */ + /* Assuming we might have now a new value for age_mask, set it in key_data */ key_data->age_mask = TALER_extensions_get_age_restriction_mask (); } @@ -912,21 +761,13 @@ decode_keys_json (const json_t *resp_obj, * * The denominations are grouped by common values of * {cipher, value, fee, age_mask}. - **/ + */ { - json_t *denominations_by_group; json_t *group_obj; unsigned int group_idx; - denominations_by_group = - json_object_get ( - resp_obj, - "denominations"); - - EXITIF (JSON_ARRAY != - json_typeof (denominations_by_group)); - - json_array_foreach (denominations_by_group, group_idx, group_obj) { + json_array_foreach (denominations_by_group, group_idx, group_obj) + { /* Running XOR of each SHA512 hash of the denominations' public key in this group. Used to compare against group.hash after all keys have been parsed. */ @@ -935,9 +776,13 @@ decode_keys_json (const json_t *resp_obj, /* First, parse { cipher, fees, value, age_mask, hash } of the current group. */ struct TALER_DenominationGroup group = {0}; + const json_t *denom_keys_array; struct GNUNET_JSON_Specification group_spec[] = { TALER_JSON_spec_denomination_group (NULL, - currency, &group), + key_data->currency, + &group), + GNUNET_JSON_spec_array_const ("denoms", + &denom_keys_array), GNUNET_JSON_spec_end () }; EXITIF (GNUNET_SYSERR == @@ -948,25 +793,21 @@ decode_keys_json (const json_t *resp_obj, /* Now, parse the individual denominations */ { - json_t *denom_keys_array; json_t *denom_key_obj; unsigned int index; - denom_keys_array = json_object_get (group_obj, "denoms"); - EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); - - json_array_foreach (denom_keys_array, index, denom_key_obj) { - struct TALER_EXCHANGE_DenomPublicKey dk = {0}; - bool found = false; - - memset (&dk, 0, sizeof (dk)); + json_array_foreach (denom_keys_array, index, denom_key_obj) + { /* Set the common fields from the group for this particular denomination. Required to make the validity check inside parse_json_denomkey_partially pass */ - dk.key.cipher = group.cipher; - dk.value = group.value; - dk.fees = group.fees; - dk.key.age_mask = group.age_mask; + struct TALER_EXCHANGE_DenomPublicKey dk = { + .key.cipher = group.cipher, + .value = group.value, + .fees = group.fees, + .key.age_mask = group.age_mask + }; + bool found = false; EXITIF (GNUNET_SYSERR == parse_json_denomkey_partially (&dk, @@ -979,7 +820,9 @@ decode_keys_json (const json_t *resp_obj, /* Build the running xor of the SHA512-hash of the public keys */ { struct TALER_DenominationHashP hc = {0}; - TALER_denom_pub_hash (&dk.key, &hc); + + TALER_denom_pub_hash (&dk.key, + &hc); GNUNET_CRYPTO_hash_xor (&hc.hash, &group_hash_xor, &group_hash_xor); @@ -1017,30 +860,26 @@ decode_keys_json (const json_t *resp_obj, key_data->last_denom_issue_date = GNUNET_TIME_timestamp_max (key_data->last_denom_issue_date, dk.valid_from); - }; // json_array_foreach over denominations + }; /* end of json_array_foreach over denominations */ - // The calculated group_hash_xor must be the same as group.hash from - // the json. + /* The calculated group_hash_xor must be the same as group.hash from + the JSON. */ EXITIF (0 != - GNUNET_CRYPTO_hash_cmp (&group_hash_xor, &group.hash)); + GNUNET_CRYPTO_hash_cmp (&group_hash_xor, + &group.hash)); - } // block for parsing individual denominations - }; // json_array_foreach over groups of denominations + } /* end of block for parsing individual denominations */ + } /* end of json_array_foreach over groups of denominations */ } /* parse the auditor information */ { - json_t *auditors_array; json_t *auditor_info; unsigned int index; - EXITIF (NULL == (auditors_array = - json_object_get (resp_obj, - "auditors"))); - EXITIF (JSON_ARRAY != json_typeof (auditors_array)); - /* Merge with the existing auditor information we have (/keys cherry picking) */ - json_array_foreach (auditors_array, index, auditor_info) { + json_array_foreach (auditors_array, index, auditor_info) + { struct TALER_EXCHANGE_AuditorInformation ai; bool found = false; @@ -1105,41 +944,35 @@ decode_keys_json (const json_t *resp_obj, } /* parse the revocation/recoup information */ + if (NULL != recoup_array) { - json_t *recoup_array; json_t *recoup_info; unsigned int index; - if (NULL != (recoup_array = - json_object_get (resp_obj, - "recoup"))) + json_array_foreach (recoup_array, index, recoup_info) { - EXITIF (JSON_ARRAY != json_typeof (recoup_array)); - - json_array_foreach (recoup_array, index, recoup_info) { - struct TALER_DenominationHashP h_denom_pub; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("h_denom_pub", - &h_denom_pub), - GNUNET_JSON_spec_end () - }; - - EXITIF (GNUNET_OK != - GNUNET_JSON_parse (recoup_info, - spec, - NULL, NULL)); - for (unsigned int j = 0; - j<key_data->num_denom_keys; - j++) + struct TALER_DenominationHashP h_denom_pub; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_denom_pub", + &h_denom_pub), + GNUNET_JSON_spec_end () + }; + + EXITIF (GNUNET_OK != + GNUNET_JSON_parse (recoup_info, + spec, + NULL, NULL)); + for (unsigned int j = 0; + j<key_data->num_denom_keys; + j++) + { + if (0 == GNUNET_memcmp (&h_denom_pub, + &key_data->denom_keys[j].h_key)) { - if (0 == GNUNET_memcmp (&h_denom_pub, - &key_data->denom_keys[j].h_key)) - { - key_data->denom_keys[j].revoked = GNUNET_YES; - break; - } + key_data->denom_keys[j].revoked = true; + break; } - }; + } } } @@ -1148,7 +981,6 @@ decode_keys_json (const json_t *resp_obj, EXITIF (GNUNET_OK != TALER_EXCHANGE_test_signing_key (key_data, &pub)); - EXITIF (GNUNET_OK != TALER_exchange_online_key_set_verify ( key_data->list_issue_date, @@ -1156,7 +988,6 @@ decode_keys_json (const json_t *resp_obj, &pub, &denominations_sig)); } - return GNUNET_OK; EXITIF_exit: @@ -1166,101 +997,6 @@ EXITIF_exit: /** - * Free key data object. - * - * @param key_data data to free (pointer itself excluded) - */ -static void -free_key_data (struct TALER_EXCHANGE_Keys *key_data) -{ - GNUNET_array_grow (key_data->sign_keys, - key_data->num_sign_keys, - 0); - for (unsigned int i = 0; i<key_data->num_denom_keys; i++) - TALER_denom_pub_free (&key_data->denom_keys[i].key); - - GNUNET_array_grow (key_data->denom_keys, - key_data->denom_keys_size, - 0); - for (unsigned int i = 0; i<key_data->num_auditors; i++) - { - GNUNET_array_grow (key_data->auditors[i].denom_keys, - key_data->auditors[i].num_denom_keys, - 0); - GNUNET_free (key_data->auditors[i].auditor_url); - } - GNUNET_array_grow (key_data->auditors, - key_data->auditors_size, - 0); - GNUNET_free (key_data->wallet_balance_limit_without_kyc); - GNUNET_free (key_data->version); - GNUNET_free (key_data->currency); - GNUNET_free (key_data->asset_type); - GNUNET_free (key_data->global_fees); -} - - -/** - * Initiate download of /keys from the exchange. - * - * @param cls exchange where to download /keys from - */ -static void -request_keys (void *cls); - - -void -TALER_EXCHANGE_set_last_denom (struct TALER_EXCHANGE_Handle *exchange, - struct GNUNET_TIME_Timestamp last_denom_new) -{ - TALER_LOG_DEBUG ( - "Application explicitly set last denomination validity to %s\n", - GNUNET_TIME_timestamp2s (last_denom_new)); - exchange->key_data.last_denom_issue_date = last_denom_new; -} - - -struct GNUNET_TIME_Timestamp -TALER_EXCHANGE_check_keys_current (struct TALER_EXCHANGE_Handle *exchange, - enum TALER_EXCHANGE_CheckKeysFlags flags, - TALER_EXCHANGE_CertificationCallback cb, - void *cb_cls) -{ - bool force_download = 0 != (flags & TALER_EXCHANGE_CKF_FORCE_DOWNLOAD); - bool pull_all_keys = 0 != (flags & TALER_EXCHANGE_CKF_PULL_ALL_KEYS); - - GNUNET_assert (NULL != exchange); - - if ( (NULL != cb) && - ( (exchange->cert_cb != cb) || - (exchange->cert_cb_cls != cb_cls) ) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Changing target of exchange certification callback\n"); - exchange->cert_cb = cb; - exchange->cert_cb_cls = cb_cls; - } - if (NULL != exchange->kr) - return GNUNET_TIME_UNIT_ZERO_TS; - if (pull_all_keys) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Forcing re-download of all exchange keys\n"); - GNUNET_break (force_download); - exchange->state = MHS_INIT; - } - if ( (! force_download) && - (GNUNET_TIME_absolute_is_future ( - exchange->key_data_expiration.abs_time)) ) - return exchange->key_data_expiration; - if (NULL == exchange->retry_task) - exchange->retry_task = GNUNET_SCHEDULER_add_now (&request_keys, - exchange); - return GNUNET_TIME_UNIT_ZERO_TS; -} - - -/** * Callback used when downloading the reply to a /keys request * is complete. * @@ -1273,133 +1009,107 @@ keys_completed_cb (void *cls, long response_code, const void *resp_obj) { - struct KeysRequest *kr = cls; - struct TALER_EXCHANGE_Handle *exchange = kr->exchange; - struct TALER_EXCHANGE_Keys kd_old; + struct TALER_EXCHANGE_GetKeysHandle *gkh = cls; const json_t *j = resp_obj; - struct TALER_EXCHANGE_Keys kd; + struct TALER_EXCHANGE_Keys *kd = NULL; struct TALER_EXCHANGE_KeysResponse kresp = { .hr.reply = j, .hr.http_status = (unsigned int) response_code, .details.ok.compat = TALER_EXCHANGE_VC_PROTOCOL_ERROR, }; - memset (&kd, - 0, - sizeof (kd)); + gkh->job = NULL; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received keys from URL `%s' with status %ld and expiration %s.\n", - kr->url, + gkh->url, response_code, - GNUNET_TIME_timestamp2s (kr->expire)); - if (GNUNET_TIME_absolute_is_past (kr->expire.abs_time)) + GNUNET_TIME_timestamp2s (gkh->expire)); + if (GNUNET_TIME_absolute_is_past (gkh->expire.abs_time)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Exchange failed to give expiration time, assuming in %s\n", GNUNET_TIME_relative2s (DEFAULT_EXPIRATION, true)); - kr->expire + gkh->expire = GNUNET_TIME_absolute_to_timestamp ( GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION)); } - kd_old = exchange->key_data; switch (response_code) { case 0: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to receive /keys response from exchange %s\n", - exchange->url); - free_keys_request (kr); - exchange->keys_error_count++; - exchange->kr = NULL; - GNUNET_assert (NULL == exchange->retry_task); - exchange->retry_delay = EXCHANGE_LIB_BACKOFF (exchange->retry_delay); - exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay, - &request_keys, - exchange); - return; + gkh->exchange_url); + break; case MHD_HTTP_OK: - exchange->keys_error_count = 0; if (NULL == j) { + GNUNET_break (0); response_code = 0; break; } - /* We keep the denomination keys and auditor signatures from the - previous iteration (/keys cherry picking) */ - kd.num_denom_keys - = kd_old.num_denom_keys; - kd.last_denom_issue_date - = kd_old.last_denom_issue_date; - GNUNET_array_grow (kd.denom_keys, - kd.denom_keys_size, - kd.num_denom_keys); - - /* First make a shallow copy, we then need another pass for the RSA key... */ - GNUNET_memcpy (kd.denom_keys, - kd_old.denom_keys, - kd_old.num_denom_keys * sizeof (struct - TALER_EXCHANGE_DenomPublicKey)); - - for (unsigned int i = 0; i<kd_old.num_denom_keys; i++) - TALER_denom_pub_deep_copy (&kd.denom_keys[i].key, - &kd_old.denom_keys[i].key); - - kd.num_auditors = kd_old.num_auditors; - kd.auditors = GNUNET_new_array (kd.num_auditors, - struct TALER_EXCHANGE_AuditorInformation); - /* Now the necessary deep copy... */ - for (unsigned int i = 0; i<kd_old.num_auditors; i++) + kd = GNUNET_new (struct TALER_EXCHANGE_Keys); + kd->exchange_url = GNUNET_strdup (gkh->exchange_url); + if (NULL != gkh->prev_keys) { - const struct TALER_EXCHANGE_AuditorInformation *aold = - &kd_old.auditors[i]; - 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, - aold->num_denom_keys); - GNUNET_memcpy (anew->denom_keys, - aold->denom_keys, - aold->num_denom_keys - * sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo)); - } + const struct TALER_EXCHANGE_Keys *kd_old = gkh->prev_keys; + + /* We keep the denomination keys and auditor signatures from the + previous iteration (/keys cherry picking) */ + kd->num_denom_keys + = kd_old->num_denom_keys; + kd->last_denom_issue_date + = kd_old->last_denom_issue_date; + GNUNET_array_grow (kd->denom_keys, + kd->denom_keys_size, + kd->num_denom_keys); + /* First make a shallow copy, we then need another pass for the RSA key... */ + GNUNET_memcpy (kd->denom_keys, + kd_old->denom_keys, + kd_old->num_denom_keys + * sizeof (struct TALER_EXCHANGE_DenomPublicKey)); + for (unsigned int i = 0; i<kd_old->num_denom_keys; i++) + TALER_denom_pub_deep_copy (&kd->denom_keys[i].key, + &kd_old->denom_keys[i].key); + kd->num_auditors = kd_old->num_auditors; + kd->auditors = GNUNET_new_array (kd->num_auditors, + struct TALER_EXCHANGE_AuditorInformation); + /* Now the necessary deep copy... */ + for (unsigned int i = 0; i<kd_old->num_auditors; i++) + { + const struct TALER_EXCHANGE_AuditorInformation *aold = + &kd_old->auditors[i]; + struct TALER_EXCHANGE_AuditorInformation *anew = &kd->auditors[i]; - /* Old auditors got just copied into new ones. */ + anew->auditor_pub = aold->auditor_pub; + anew->auditor_url = GNUNET_strdup (aold->auditor_url); + GNUNET_array_grow (anew->denom_keys, + anew->num_denom_keys, + aold->num_denom_keys); + GNUNET_memcpy ( + anew->denom_keys, + aold->denom_keys, + aold->num_denom_keys + * sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo)); + } + } + /* Now decode fresh /keys response */ if (GNUNET_OK != decode_keys_json (j, true, - &kd, + kd, &kresp.details.ok.compat)) { TALER_LOG_ERROR ("Could not decode /keys response\n"); + kd->rc = 1; + TALER_EXCHANGE_keys_decref (kd); + kd = NULL; kresp.hr.http_status = 0; kresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - for (unsigned int i = 0; i<kd.num_auditors; i++) - { - struct TALER_EXCHANGE_AuditorInformation *anew = &kd.auditors[i]; - - GNUNET_array_grow (anew->denom_keys, - anew->num_denom_keys, - 0); - GNUNET_free (anew->auditor_url); - } - GNUNET_free (kd.auditors); - kd.auditors = NULL; - kd.num_auditors = 0; - for (unsigned int i = 0; i<kd_old.num_denom_keys; i++) - TALER_denom_pub_free (&kd.denom_keys[i].key); - GNUNET_array_grow (kd.denom_keys, - kd.denom_keys_size, - 0); - kd.num_denom_keys = 0; break; } - json_decref (exchange->key_data_raw); - exchange->key_data_raw = json_deep_copy (j); - exchange->retry_delay = GNUNET_TIME_UNIT_ZERO; + kd->rc = 1; + kresp.details.ok.keys = kd; break; case MHD_HTTP_BAD_REQUEST: case MHD_HTTP_UNAUTHORIZED: @@ -1417,8 +1127,6 @@ keys_completed_cb (void *cls, } break; default: - if (MHD_HTTP_GATEWAY_TIMEOUT == response_code) - exchange->keys_error_count++; if (NULL == j) { kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; @@ -1435,78 +1143,10 @@ keys_completed_cb (void *cls, (int) kresp.hr.ec); break; } - exchange->key_data = kd; - if (GNUNET_TIME_absolute_is_past ( - exchange->key_data.last_denom_issue_date.abs_time)) - TALER_LOG_WARNING ("Last DK issue date from exchange is in the past: %s\n", - GNUNET_TIME_timestamp2s ( - exchange->key_data.last_denom_issue_date)); - else - TALER_LOG_DEBUG ("Last DK issue date updated to: %s\n", - GNUNET_TIME_timestamp2s ( - exchange->key_data.last_denom_issue_date)); - - - if (MHD_HTTP_OK != response_code) - { - exchange->kr = NULL; - free_keys_request (kr); - exchange->state = MHS_FAILED; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Exchange keys download failed\n"); - if (NULL != exchange->key_data_raw) - { - json_decref (exchange->key_data_raw); - exchange->key_data_raw = NULL; - } - free_key_data (&kd_old); - /* notify application that we failed */ - exchange->cert_cb (exchange->cert_cb_cls, - &kresp); - return; - } - - exchange->kr = NULL; - exchange->key_data_expiration = kr->expire; - free_keys_request (kr); - exchange->state = MHS_CERT; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Successfully downloaded exchange's keys\n"); - update_auditors (exchange); - kresp.details.ok.keys = &exchange->key_data; - - /* notify application about the key information */ - exchange->cert_cb (exchange->cert_cb_cls, - &kresp); - free_key_data (&kd_old); -} - - -/* ********************* library internal API ********* */ - - -struct GNUNET_CURL_Context * -TEAH_handle_to_context (struct TALER_EXCHANGE_Handle *h) -{ - return h->ctx; -} - - -enum GNUNET_GenericReturnValue -TEAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h) -{ - return (MHS_CERT == h->state) ? GNUNET_YES : GNUNET_NO; -} - - -char * -TEAH_path_to_url (struct TALER_EXCHANGE_Handle *h, - const char *path) -{ - GNUNET_assert ('/' == path[0]); - return TALER_url_join (h->url, - path + 1, - NULL); + gkh->cert_cb (gkh->cert_cb_cls, + &kresp, + kd); + TALER_EXCHANGE_get_keys_cancel (gkh); } @@ -1520,7 +1160,7 @@ TEAH_path_to_url (struct TALER_EXCHANGE_Handle *h, * Parse HTTP timestamp. * * @param dateline header to parse header - * @param at where to write the result + * @param[out] at where to write the result * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue @@ -1620,7 +1260,7 @@ parse_date_string (const char *dateline, * @param buffer header data received * @param size size of an item in @a buffer * @param nitems number of items in @a buffer - * @param userdata the `struct KeysRequest` + * @param userdata the `struct TALER_EXCHANGE_GetKeysHandle` * @return `size * nitems` on success (everything else aborts) */ static size_t @@ -1629,7 +1269,7 @@ header_cb (char *buffer, size_t nitems, void *userdata) { - struct KeysRequest *kr = userdata; + struct TALER_EXCHANGE_GetKeysHandle *kr = userdata; size_t total = size * nitems; char *val; @@ -1656,22 +1296,249 @@ header_cb (char *buffer, } -/* ********************* public API ******************* */ +struct TALER_EXCHANGE_GetKeysHandle * +TALER_EXCHANGE_get_keys ( + struct GNUNET_CURL_Context *ctx, + const char *url, + struct TALER_EXCHANGE_Keys *last_keys, + TALER_EXCHANGE_GetKeysCallback cert_cb, + void *cert_cb_cls) +{ + struct TALER_EXCHANGE_GetKeysHandle *gkh; + CURL *eh; + char last_date[80] = { 0 }; + TALER_LOG_DEBUG ("Connecting to the exchange (%s)\n", + url); + gkh = GNUNET_new (struct TALER_EXCHANGE_GetKeysHandle); + gkh->exchange_url = GNUNET_strdup (url); + gkh->cert_cb = cert_cb; + gkh->cert_cb_cls = cert_cb_cls; + if (NULL != last_keys) + { + TALER_LOG_DEBUG ("Last DK issue date (before GETting /keys): %s\n", + GNUNET_TIME_timestamp2s ( + last_keys->last_denom_issue_date)); + GNUNET_snprintf (last_date, + sizeof (last_date), + "%llu", + (unsigned long long) + last_keys->last_denom_issue_date.abs_time.abs_value_us + / 1000000LLU); + } + gkh->url = TALER_url_join (url, + "keys", + (NULL != last_keys) + ? "last_issue_date" + : NULL, + (NULL != last_keys) + ? last_date + : NULL, + NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting keys with URL `%s'.\n", + gkh->url); + eh = TALER_EXCHANGE_curl_easy_get_ (gkh->url); + if (NULL == eh) + { + GNUNET_break (0); + GNUNET_free (gkh->exchange_url); + GNUNET_free (gkh->url); + GNUNET_free (gkh); + return NULL; + } + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_VERBOSE, + 0)); + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT, + 120 /* seconds */)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_HEADERFUNCTION, + &header_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_HEADERDATA, + gkh)); + gkh->job = GNUNET_CURL_job_add_with_ct_json (ctx, + eh, + &keys_completed_cb, + gkh); + return gkh; +} -/** - * Deserialize the key data and use it to bootstrap @a exchange to - * more efficiently recover the state. Errors in @a data must be - * tolerated (i.e. by re-downloading instead). - * - * @param exchange which exchange's key and wire data should be deserialized - * @param data the data to deserialize - */ -static void -deserialize_data (struct TALER_EXCHANGE_Handle *exchange, - const json_t *data) + +void +TALER_EXCHANGE_get_keys_cancel ( + struct TALER_EXCHANGE_GetKeysHandle *gkh) { - const json_t *keys; + if (NULL != gkh->job) + { + GNUNET_CURL_job_cancel (gkh->job); + gkh->job = NULL; + } + TALER_EXCHANGE_keys_decref (gkh->prev_keys); + GNUNET_free (gkh->exchange_url); + GNUNET_free (gkh->url); + GNUNET_free (gkh); +} + + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_test_signing_key ( + const struct TALER_EXCHANGE_Keys *keys, + const struct TALER_ExchangePublicKeyP *pub) +{ + struct GNUNET_TIME_Absolute now; + + /* we will check using a tolerance of 1h for the time */ + now = GNUNET_TIME_absolute_get (); + for (unsigned int i = 0; i<keys->num_sign_keys; i++) + if ( (GNUNET_TIME_absolute_cmp ( + keys->sign_keys[i].valid_from.abs_time, + <=, + GNUNET_TIME_absolute_add (now, + LIFETIME_TOLERANCE))) && + (GNUNET_TIME_absolute_cmp ( + keys->sign_keys[i].valid_until.abs_time, + >, + GNUNET_TIME_absolute_subtract (now, + LIFETIME_TOLERANCE))) && + (0 == GNUNET_memcmp (pub, + &keys->sign_keys[i].key)) ) + return GNUNET_OK; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Signing key not valid at time %s\n", + GNUNET_TIME_absolute2s (now)); + return GNUNET_SYSERR; +} + + +const struct TALER_EXCHANGE_DenomPublicKey * +TALER_EXCHANGE_get_denomination_key ( + const struct TALER_EXCHANGE_Keys *keys, + const struct TALER_DenominationPublicKey *pk) +{ + for (unsigned int i = 0; i<keys->num_denom_keys; i++) + if (0 == + TALER_denom_pub_cmp (pk, + &keys->denom_keys[i].key)) + return &keys->denom_keys[i]; + return NULL; +} + + +const struct TALER_EXCHANGE_GlobalFee * +TALER_EXCHANGE_get_global_fee ( + const struct TALER_EXCHANGE_Keys *keys, + struct GNUNET_TIME_Timestamp ts) +{ + for (unsigned int i = 0; i<keys->num_global_fees; i++) + { + const struct TALER_EXCHANGE_GlobalFee *gf = &keys->global_fees[i]; + + if (GNUNET_TIME_timestamp_cmp (ts, + >=, + gf->start_date) && + GNUNET_TIME_timestamp_cmp (ts, + <, + gf->end_date)) + return gf; + } + return NULL; +} + + +struct TALER_EXCHANGE_DenomPublicKey * +TALER_EXCHANGE_copy_denomination_key ( + const struct TALER_EXCHANGE_DenomPublicKey *key) +{ + struct TALER_EXCHANGE_DenomPublicKey *copy; + + copy = GNUNET_new (struct TALER_EXCHANGE_DenomPublicKey); + *copy = *key; + TALER_denom_pub_deep_copy (©->key, + &key->key); + return copy; +} + + +void +TALER_EXCHANGE_destroy_denomination_key ( + struct TALER_EXCHANGE_DenomPublicKey *key) +{ + TALER_denom_pub_free (&key->key); + GNUNET_free (key); +} + + +const struct TALER_EXCHANGE_DenomPublicKey * +TALER_EXCHANGE_get_denomination_key_by_hash ( + const struct TALER_EXCHANGE_Keys *keys, + const struct TALER_DenominationHashP *hc) +{ + for (unsigned int i = 0; i<keys->num_denom_keys; i++) + if (0 == GNUNET_memcmp (hc, + &keys->denom_keys[i].h_key)) + return &keys->denom_keys[i]; + return NULL; +} + + +struct TALER_EXCHANGE_Keys * +TALER_EXCHANGE_keys_incref (struct TALER_EXCHANGE_Keys *keys) +{ + GNUNET_assert (keys->rc < UINT_MAX); + keys->rc++; + return keys; +} + + +void +TALER_EXCHANGE_keys_decref (struct TALER_EXCHANGE_Keys *keys) +{ + if (NULL == keys) + return; + GNUNET_assert (0 < keys->rc); + keys->rc--; + if (0 != keys->rc) + return; + GNUNET_array_grow (keys->sign_keys, + keys->num_sign_keys, + 0); + for (unsigned int i = 0; i<keys->num_denom_keys; i++) + TALER_denom_pub_free (&keys->denom_keys[i].key); + + GNUNET_array_grow (keys->denom_keys, + keys->denom_keys_size, + 0); + for (unsigned int i = 0; i<keys->num_auditors; i++) + { + GNUNET_array_grow (keys->auditors[i].denom_keys, + keys->auditors[i].num_denom_keys, + 0); + GNUNET_free (keys->auditors[i].auditor_url); + } + GNUNET_array_grow (keys->auditors, + keys->auditors_size, + 0); + GNUNET_free (keys->wallet_balance_limit_without_kyc); + GNUNET_free (keys->version); + GNUNET_free (keys->currency); + GNUNET_free (keys->asset_type); + GNUNET_free (keys->global_fees); + GNUNET_free (keys->exchange_url); + GNUNET_free (keys); +} + + +struct TALER_EXCHANGE_Keys * +TALER_EXCHANGE_keys_from_json (const json_t *j) +{ + const json_t *jkeys; const char *url; uint32_t version; struct GNUNET_TIME_Timestamp expire; @@ -1679,72 +1546,50 @@ deserialize_data (struct TALER_EXCHANGE_Handle *exchange, GNUNET_JSON_spec_uint32 ("version", &version), GNUNET_JSON_spec_array_const ("keys", - &keys), + &jkeys), GNUNET_JSON_spec_string ("exchange_url", &url), GNUNET_JSON_spec_timestamp ("expire", &expire), GNUNET_JSON_spec_end () }; - struct TALER_EXCHANGE_Keys key_data; - struct TALER_EXCHANGE_KeysResponse kresp = { - .hr.ec = TALER_EC_NONE, - .hr.http_status = MHD_HTTP_OK, - .hr.reply = data, - .details.ok.keys = &exchange->key_data - }; + struct TALER_EXCHANGE_Keys *keys; + enum TALER_EXCHANGE_VersionCompatibility compat; - if (NULL == data) - return; + if (NULL == j) + return NULL; if (GNUNET_OK != - GNUNET_JSON_parse (data, + GNUNET_JSON_parse (j, spec, NULL, NULL)) { GNUNET_break_op (0); - return; + return NULL; } if (0 != version) { - return; /* unsupported version */ + return NULL; /* unsupported version */ } - if (0 != strcmp (url, - exchange->url)) - { - GNUNET_break (0); - return; - } - memset (&key_data, - 0, - sizeof (struct TALER_EXCHANGE_Keys)); + keys = GNUNET_new (struct TALER_EXCHANGE_Keys); if (GNUNET_OK != - decode_keys_json (keys, + decode_keys_json (jkeys, false, - &key_data, - &kresp.details.ok.compat)) + keys, + &compat)) { GNUNET_break (0); - return; + return NULL; } - /* decode successful, initialize with the result */ - GNUNET_assert (NULL == exchange->key_data_raw); - exchange->key_data_raw = json_deep_copy (keys); - exchange->key_data = key_data; - exchange->key_data_expiration = expire; - exchange->state = MHS_CERT; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Successfully loaded exchange's keys via deserialization\n"); - update_auditors (exchange); - /* notify application about the key information */ - exchange->cert_cb (exchange->cert_cb_cls, - &kresp); + keys->rc = 1; + keys->key_data_expiration = expire; + keys->exchange_url = GNUNET_strdup (url); + return keys; } json_t * -TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange) +TALER_EXCHANGE_keys_to_json (const struct TALER_EXCHANGE_Keys *kd) { - const struct TALER_EXCHANGE_Keys *kd = &exchange->key_data; struct GNUNET_TIME_Timestamp now; json_t *keys; json_t *signkeys; @@ -1903,371 +1748,12 @@ TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange) GNUNET_JSON_pack_uint64 ("version", EXCHANGE_SERIALIZATION_FORMAT_VERSION), GNUNET_JSON_pack_timestamp ("expire", - exchange->key_data_expiration), + kd->key_data_expiration), GNUNET_JSON_pack_string ("exchange_url", - exchange->url), + kd->exchange_url), GNUNET_JSON_pack_object_steal ("keys", keys)); } -struct TALER_EXCHANGE_Handle * -TALER_EXCHANGE_connect ( - struct GNUNET_CURL_Context *ctx, - const char *url, - TALER_EXCHANGE_CertificationCallback cert_cb, - void *cert_cb_cls, - ...) -{ - struct TALER_EXCHANGE_Handle *exchange; - va_list ap; - enum TALER_EXCHANGE_Option opt; - - TALER_LOG_DEBUG ("Connecting to the exchange (%s)\n", - url); - /* Disable 100 continue processing */ - GNUNET_break (GNUNET_OK == - GNUNET_CURL_append_header (ctx, - MHD_HTTP_HEADER_EXPECT ":")); - exchange = GNUNET_new (struct TALER_EXCHANGE_Handle); - exchange->ctx = ctx; - exchange->url = GNUNET_strdup (url); - exchange->cert_cb = cert_cb; - exchange->cert_cb_cls = cert_cb_cls; - exchange->retry_task = GNUNET_SCHEDULER_add_now (&request_keys, - exchange); - va_start (ap, cert_cb_cls); - while (TALER_EXCHANGE_OPTION_END != - (opt = va_arg (ap, int))) - { - switch (opt) - { - case TALER_EXCHANGE_OPTION_END: - GNUNET_assert (0); - break; - case TALER_EXCHANGE_OPTION_DATA: - { - const json_t *data = va_arg (ap, const json_t *); - - deserialize_data (exchange, - data); - break; - } - default: - GNUNET_assert (0); - break; - } - } - va_end (ap); - return exchange; -} - - -/** - * Compute the network timeout for the next request to /keys. - * - * @param exchange the exchange handle - * @returns the timeout in seconds (for use by CURL) - */ -static long -get_keys_timeout_seconds (struct TALER_EXCHANGE_Handle *exchange) -{ - unsigned int kec; - - /* if retry counter >= 8, do not bother to go further, we - stop the exponential back-off at 128 anyway. */ - kec = GNUNET_MIN (7, - exchange->keys_error_count); - return GNUNET_MIN (120, - 5 + (1L << kec)); -} - - -/** - * Initiate download of /keys from the exchange. - * - * @param cls exchange where to download /keys from - */ -static void -request_keys (void *cls) -{ - struct TALER_EXCHANGE_Handle *exchange = cls; - struct KeysRequest *kr; - CURL *eh; - char url[200] = "/keys?"; - - exchange->retry_task = NULL; - GNUNET_assert (NULL == exchange->kr); - kr = GNUNET_new (struct KeysRequest); - kr->exchange = exchange; - - if (GNUNET_YES == TEAH_handle_is_ready (exchange)) - { - TALER_LOG_DEBUG ("Last DK issue date (before GETting /keys): %s\n", - GNUNET_TIME_timestamp2s ( - exchange->key_data.last_denom_issue_date)); - sprintf (&url[strlen (url)], - "last_issue_date=%llu&", - (unsigned long long) - exchange->key_data.last_denom_issue_date.abs_time.abs_value_us - / 1000000LLU); - } - - /* Clean the last '&'/'?' sign that we optimistically put. */ - url[strlen (url) - 1] = '\0'; - kr->url = TEAH_path_to_url (exchange, - url); - if (NULL == kr->url) - { - struct TALER_EXCHANGE_KeysResponse kresp = { - .hr.ec = TALER_EC_GENERIC_CONFIGURATION_INVALID, - /* Next line is technically unnecessary, as the - http status we set is 0 */ - .details.ok.compat = TALER_EXCHANGE_VC_PROTOCOL_ERROR - }; - - GNUNET_free (kr); - exchange->keys_error_count++; - exchange->state = MHS_FAILED; - exchange->cert_cb (exchange->cert_cb_cls, - &kresp); - return; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting keys with URL `%s'.\n", - kr->url); - eh = TALER_EXCHANGE_curl_easy_get_ (kr->url); - if (NULL == eh) - { - GNUNET_free (kr->url); - GNUNET_free (kr); - exchange->retry_delay = EXCHANGE_LIB_BACKOFF (exchange->retry_delay); - exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay, - &request_keys, - exchange); - return; - } - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_VERBOSE, - 0)); - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_TIMEOUT, - get_keys_timeout_seconds (exchange))); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_HEADERFUNCTION, - &header_cb)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_HEADERDATA, - kr)); - kr->job = GNUNET_CURL_job_add_with_ct_json (exchange->ctx, - eh, - &keys_completed_cb, - kr); - exchange->kr = kr; -} - - -void -TALER_EXCHANGE_disconnect (struct TALER_EXCHANGE_Handle *exchange) -{ - struct TEAH_AuditorListEntry *ale; - - while (NULL != (ale = exchange->auditors_head)) - { - GNUNET_CONTAINER_DLL_remove (exchange->auditors_head, - exchange->auditors_tail, - ale); - if (NULL != ale->ah) - { - TALER_AUDITOR_get_config_cancel (ale->ah); - ale->ah = NULL; - } - GNUNET_free (ale->auditor_url); - GNUNET_free (ale); - } - if (NULL != exchange->kr) - { - GNUNET_CURL_job_cancel (exchange->kr->job); - free_keys_request (exchange->kr); - exchange->kr = NULL; - } - free_key_data (&exchange->key_data); - if (NULL != exchange->key_data_raw) - { - json_decref (exchange->key_data_raw); - exchange->key_data_raw = NULL; - } - if (NULL != exchange->retry_task) - { - GNUNET_SCHEDULER_cancel (exchange->retry_task); - exchange->retry_task = NULL; - } - GNUNET_free (exchange->url); - GNUNET_free (exchange); -} - - -enum GNUNET_GenericReturnValue -TALER_EXCHANGE_test_signing_key (const struct TALER_EXCHANGE_Keys *keys, - const struct TALER_ExchangePublicKeyP *pub) -{ - struct GNUNET_TIME_Absolute now; - - /* we will check using a tolerance of 1h for the time */ - now = GNUNET_TIME_absolute_get (); - for (unsigned int i = 0; i<keys->num_sign_keys; i++) - if ( (GNUNET_TIME_absolute_cmp ( - keys->sign_keys[i].valid_from.abs_time, - <=, - GNUNET_TIME_absolute_add (now, - LIFETIME_TOLERANCE))) && - (GNUNET_TIME_absolute_cmp ( - keys->sign_keys[i].valid_until.abs_time, - >, - GNUNET_TIME_absolute_subtract (now, - LIFETIME_TOLERANCE))) && - (0 == GNUNET_memcmp (pub, - &keys->sign_keys[i].key)) ) - return GNUNET_OK; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Signing key not valid at time %s\n", - GNUNET_TIME_absolute2s (now)); - return GNUNET_SYSERR; -} - - -const char * -TALER_EXCHANGE_get_base_url (const struct TALER_EXCHANGE_Handle *exchange) -{ - return exchange->url; -} - - -const struct TALER_EXCHANGE_DenomPublicKey * -TALER_EXCHANGE_get_denomination_key ( - const struct TALER_EXCHANGE_Keys *keys, - const struct TALER_DenominationPublicKey *pk) -{ - for (unsigned int i = 0; i<keys->num_denom_keys; i++) - if (0 == - TALER_denom_pub_cmp (pk, - &keys->denom_keys[i].key)) - return &keys->denom_keys[i]; - return NULL; -} - - -const struct TALER_EXCHANGE_GlobalFee * -TALER_EXCHANGE_get_global_fee ( - const struct TALER_EXCHANGE_Keys *keys, - struct GNUNET_TIME_Timestamp ts) -{ - for (unsigned int i = 0; i<keys->num_global_fees; i++) - { - const struct TALER_EXCHANGE_GlobalFee *gf = &keys->global_fees[i]; - - if (GNUNET_TIME_timestamp_cmp (ts, - >=, - gf->start_date) && - GNUNET_TIME_timestamp_cmp (ts, - <, - gf->end_date)) - return gf; - } - return NULL; -} - - -struct TALER_EXCHANGE_DenomPublicKey * -TALER_EXCHANGE_copy_denomination_key ( - const struct TALER_EXCHANGE_DenomPublicKey *key) -{ - struct TALER_EXCHANGE_DenomPublicKey *copy; - - copy = GNUNET_new (struct TALER_EXCHANGE_DenomPublicKey); - *copy = *key; - TALER_denom_pub_deep_copy (©->key, - &key->key); - return copy; -} - - -void -TALER_EXCHANGE_destroy_denomination_key ( - struct TALER_EXCHANGE_DenomPublicKey *key) -{ - TALER_denom_pub_free (&key->key); - GNUNET_free (key); -} - - -const struct TALER_EXCHANGE_DenomPublicKey * -TALER_EXCHANGE_get_denomination_key_by_hash ( - const struct TALER_EXCHANGE_Keys *keys, - const struct TALER_DenominationHashP *hc) -{ - for (unsigned int i = 0; i<keys->num_denom_keys; i++) - if (0 == GNUNET_memcmp (hc, - &keys->denom_keys[i].h_key)) - return &keys->denom_keys[i]; - return NULL; -} - - -struct TALER_EXCHANGE_Keys * -TALER_EXCHANGE_get_keys (struct TALER_EXCHANGE_Handle *exchange) -{ - (void) TALER_EXCHANGE_check_keys_current (exchange, - TALER_EXCHANGE_CKF_NONE, - NULL, - NULL); - return &exchange->key_data; -} - - -json_t * -TALER_EXCHANGE_get_keys_raw (struct TALER_EXCHANGE_Handle *exchange) -{ - (void) TALER_EXCHANGE_check_keys_current (exchange, - TALER_EXCHANGE_CKF_NONE, - NULL, - NULL); - return json_deep_copy (exchange->key_data_raw); -} - - -/** - * Obtain the keys from the exchange in the raw JSON format. - * - * @param keys the keys structure - * @return the keys in raw JSON - */ -json_t * -TALER_EXCHANGE_keys_to_json (struct TALER_EXCHANGE_Keys *keys) -{ - // FIXME! - return NULL; -} - - -struct TALER_EXCHANGE_Keys * -TALER_EXCHANGE_keys_incref (struct TALER_EXCHANGE_Keys *keys) -{ - // FIXME - return keys; -} - - -void -TALER_EXCHANGE_keys_decref (struct TALER_EXCHANGE_Keys *keys) -{ - // FIXME -} - - /* end of exchange_api_handle.c */ diff --git a/src/lib/exchange_api_handle.h b/src/lib/exchange_api_handle.h index 6b96e21eb..7c01b9a9f 100644 --- a/src/lib/exchange_api_handle.h +++ b/src/lib/exchange_api_handle.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 Taler Systems SA + Copyright (C) 2014, 2015, 2023 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 @@ -28,124 +28,6 @@ #include "taler_util.h" #include "taler_curl_lib.h" -/** - * Entry in DLL of auditors used by an exchange. - */ -struct TEAH_AuditorListEntry; - - -/** - * Stages of initialization for the `struct TALER_EXCHANGE_Handle` - */ -enum ExchangeHandleState -{ - /** - * Just allocated. - */ - MHS_INIT = 0, - - /** - * Obtained the exchange's certification data and keys. - */ - MHS_CERT = 1, - - /** - * Failed to initialize (fatal). - */ - MHS_FAILED = 2 -}; - - -/** - * Handle to the exchange - */ -struct TALER_EXCHANGE_Handle -{ - /** - * The context of this handle - */ - struct GNUNET_CURL_Context *ctx; - - /** - * The URL of the exchange (i.e. "http://exchange.taler.net/") - */ - char *url; - - /** - * Function to call with the exchange's certification data, - * NULL if this has already been done. - */ - TALER_EXCHANGE_CertificationCallback cert_cb; - - /** - * Closure to pass to @e cert_cb. - */ - void *cert_cb_cls; - - /** - * Data for the request to get the /keys of a exchange, - * NULL once we are past stage #MHS_INIT. - */ - struct KeysRequest *kr; - - /** - * Task for retrying /keys request. - */ - struct GNUNET_SCHEDULER_Task *retry_task; - - /** - * Raw key data of the exchange, only valid if - * @e handshake_complete is past stage #MHS_CERT. - */ - json_t *key_data_raw; - - /** - * Head of DLL of auditors of this exchange. - */ - struct TEAH_AuditorListEntry *auditors_head; - - /** - * Tail of DLL of auditors of this exchange. - */ - struct TEAH_AuditorListEntry *auditors_tail; - - /** - * Key data of the exchange, only valid if - * @e handshake_complete is past stage #MHS_CERT. - */ - struct TALER_EXCHANGE_Keys key_data; - - /** - * Retry /keys frequency. - */ - struct GNUNET_TIME_Relative retry_delay; - - /** - * When does @e key_data expire? - */ - struct GNUNET_TIME_Timestamp key_data_expiration; - - /** - * Number of subsequent failed requests to /keys. - * - * Used to compute the CURL timeout for the request. - */ - unsigned int keys_error_count; - - /** - * Number of subsequent failed requests to /wire. - * - * Used to compute the CURL timeout for the request. - */ - unsigned int wire_error_count; - - /** - * Stage of the exchange's initialization routines. - */ - enum ExchangeHandleState state; - -}; - /** * Function called for each auditor to give us a chance to possibly @@ -156,9 +38,10 @@ struct TALER_EXCHANGE_Handle * @param auditor_pub public key of the auditor */ typedef void -(*TEAH_AuditorCallback)(void *cls, - const char *auditor_url, - const struct TALER_AuditorPublicKeyP *auditor_pub); +(*TEAH_AuditorCallback)( + void *cls, + const char *auditor_url, + const struct TALER_AuditorPublicKeyP *auditor_pub); /** @@ -171,50 +54,11 @@ typedef void * @param ac_cls closure for @a ac */ void -TEAH_get_auditors_for_dc (struct TALER_EXCHANGE_Keys *keys, - TEAH_AuditorCallback ac, - void *ac_cls); - +TEAH_get_auditors_for_dc ( + struct TALER_EXCHANGE_Keys *keys, + TEAH_AuditorCallback ac, + void *ac_cls); -/** - * Get the context of a exchange. - * - * @param h the exchange handle to query - * @return ctx context to execute jobs in - */ -struct GNUNET_CURL_Context * -TEAH_handle_to_context (struct TALER_EXCHANGE_Handle *h); - - -/** - * Check if the handle is ready to process requests. - * - * @param h the exchange handle to query - * @return #GNUNET_YES if we are ready, #GNUNET_NO if not - */ -enum GNUNET_GenericReturnValue -TEAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h); - -/** - * Check if the handle is ready to process requests. - * - * @param h the exchange handle to query - * @return #GNUNET_YES if we are ready, #GNUNET_NO if not - */ -enum GNUNET_GenericReturnValue -TEAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h); - - -/** - * Obtain the URL to use for an API request. - * - * @param h the exchange handle to query - * @param path Taler API path (i.e. "/reserve/withdraw") - * @return the full URL to use with cURL - */ -char * -TEAH_path_to_url (struct TALER_EXCHANGE_Handle *h, - const char *path); /* end of exchange_api_handle.h */ #endif |