2015-06-17 18:50:09 +02:00
|
|
|
/*
|
|
|
|
This file is part of TALER
|
2018-08-19 12:22:42 +02:00
|
|
|
Copyright (C) 2014-2018 GNUnet e.V.
|
2015-06-17 18:50:09 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
Foundation; either version 3, or (at your option) any later version.
|
|
|
|
|
|
|
|
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
2016-07-07 17:55:25 +02:00
|
|
|
TALER; see the file COPYING. If not, see
|
2015-06-17 18:50:09 +02:00
|
|
|
<http://www.gnu.org/licenses/>
|
|
|
|
*/
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* @file exchange-lib/exchange_api_handle.c
|
|
|
|
* @brief Implementation of the "handle" component of the exchange's HTTP API
|
2015-06-17 18:50:09 +02:00
|
|
|
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
|
|
|
|
* @author Christian Grothoff
|
|
|
|
*/
|
|
|
|
#include "platform.h"
|
2015-06-30 21:26:16 +02:00
|
|
|
#include <microhttpd.h>
|
2016-04-17 17:45:15 +02:00
|
|
|
#include <gnunet/gnunet_curl_lib.h>
|
2016-03-19 15:54:21 +01:00
|
|
|
#include "taler_json_lib.h"
|
2016-03-01 15:35:04 +01:00
|
|
|
#include "taler_exchange_service.h"
|
2015-06-17 18:50:09 +02:00
|
|
|
#include "taler_signatures.h"
|
2016-03-01 15:35:04 +01:00
|
|
|
#include "exchange_api_handle.h"
|
2018-09-22 01:21:55 +02:00
|
|
|
#include "curl_defaults.h"
|
2018-09-26 23:24:24 +02:00
|
|
|
#include "backoff.h"
|
2015-06-17 18:50:09 +02:00
|
|
|
|
2017-07-01 14:15:26 +02:00
|
|
|
/**
|
|
|
|
* Which revision of the Taler protocol is implemented
|
|
|
|
* by this library? Used to determine compatibility.
|
|
|
|
*/
|
2017-12-10 19:03:11 +01:00
|
|
|
#define TALER_PROTOCOL_CURRENT 2
|
2017-07-01 14:15:26 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* How many revisions back are we compatible to?
|
|
|
|
*/
|
2017-12-10 19:03:11 +01:00
|
|
|
#define TALER_PROTOCOL_AGE 0
|
2017-07-01 14:15:26 +02:00
|
|
|
|
2015-06-17 18:50:09 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
#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));
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Stages of initialization for the `struct TALER_EXCHANGE_Handle`
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
2016-03-01 15:35:04 +01:00
|
|
|
enum ExchangeHandleState
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Just allocated.
|
|
|
|
*/
|
|
|
|
MHS_INIT = 0,
|
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Obtained the exchange's certification data and keys.
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
|
|
|
MHS_CERT = 1,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Failed to initialize (fatal).
|
|
|
|
*/
|
|
|
|
MHS_FAILED = 2
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Data for the request to get the /keys of a exchange.
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
|
|
|
struct KeysRequest;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Handle to the exchange
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
2016-03-01 15:35:04 +01:00
|
|
|
struct TALER_EXCHANGE_Handle
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* The context of this handle
|
|
|
|
*/
|
2016-04-17 17:45:15 +02:00
|
|
|
struct GNUNET_CURL_Context *ctx;
|
2015-06-17 18:50:09 +02:00
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* The URL of the exchange (i.e. "http://exchange.taler.net/")
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
|
|
|
char *url;
|
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Function to call with the exchange's certification data,
|
2015-06-17 18:50:09 +02:00
|
|
|
* NULL if this has already been done.
|
|
|
|
*/
|
2016-03-01 15:35:04 +01:00
|
|
|
TALER_EXCHANGE_CertificationCallback cert_cb;
|
2015-06-17 18:50:09 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Closure to pass to @e cert_cb.
|
|
|
|
*/
|
|
|
|
void *cert_cb_cls;
|
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Data for the request to get the /keys of a exchange,
|
2015-06-17 18:50:09 +02:00
|
|
|
* NULL once we are past stage #MHS_INIT.
|
|
|
|
*/
|
|
|
|
struct KeysRequest *kr;
|
|
|
|
|
2018-08-19 12:12:00 +02:00
|
|
|
/**
|
|
|
|
* Task for retrying /keys request.
|
|
|
|
*/
|
|
|
|
struct GNUNET_SCHEDULER_Task *retry_task;
|
|
|
|
|
2018-08-19 12:22:42 +02:00
|
|
|
/**
|
|
|
|
* Raw key data of the exchange, only valid if
|
|
|
|
* @e handshake_complete is past stage #MHS_CERT.
|
|
|
|
*/
|
|
|
|
json_t *key_data_raw;
|
|
|
|
|
2015-06-17 18:50:09 +02:00
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Key data of the exchange, only valid if
|
2015-06-17 18:50:09 +02:00
|
|
|
* @e handshake_complete is past stage #MHS_CERT.
|
|
|
|
*/
|
2016-03-01 15:35:04 +01:00
|
|
|
struct TALER_EXCHANGE_Keys key_data;
|
2015-06-17 18:50:09 +02:00
|
|
|
|
2018-08-19 12:12:00 +02:00
|
|
|
/**
|
|
|
|
* Retry /keys frequency.
|
|
|
|
*/
|
|
|
|
struct GNUNET_TIME_Relative retry_delay;
|
|
|
|
|
2016-05-31 14:32:06 +02:00
|
|
|
/**
|
|
|
|
* When does @e key_data expire?
|
|
|
|
*/
|
|
|
|
struct GNUNET_TIME_Absolute key_data_expiration;
|
|
|
|
|
2015-06-17 18:50:09 +02:00
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Stage of the exchange's initialization routines.
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
2016-03-01 15:35:04 +01:00
|
|
|
enum ExchangeHandleState state;
|
2015-06-17 18:50:09 +02:00
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* ***************** Internal /keys fetching ************* */
|
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Data for the request to get the /keys of a exchange.
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
|
|
|
struct KeysRequest
|
|
|
|
{
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* The connection to exchange this request handle will use
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
2016-03-01 15:35:04 +01:00
|
|
|
struct TALER_EXCHANGE_Handle *exchange;
|
2015-06-17 18:50:09 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The url for this handle
|
|
|
|
*/
|
|
|
|
char *url;
|
|
|
|
|
|
|
|
/**
|
2016-04-17 17:45:15 +02:00
|
|
|
* Entry for this request with the `struct GNUNET_CURL_Context`.
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
2016-04-17 17:45:15 +02:00
|
|
|
struct GNUNET_CURL_Job *job;
|
2015-06-17 18:50:09 +02:00
|
|
|
|
2016-05-31 14:32:06 +02:00
|
|
|
/**
|
|
|
|
* Expiration time according to "Expire:" header.
|
|
|
|
* 0 if not provided by the server.
|
|
|
|
*/
|
|
|
|
struct GNUNET_TIME_Absolute expire;
|
|
|
|
|
2015-06-17 18:50:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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; } \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Parse a exchange's signing key encoded in JSON.
|
2015-06-17 18:50:09 +02:00
|
|
|
*
|
|
|
|
* @param[out] sign_key where to return the result
|
|
|
|
* @param[in] 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.
|
|
|
|
*/
|
|
|
|
static int
|
2016-03-01 15:35:04 +01:00
|
|
|
parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key,
|
2015-06-17 18:50:09 +02:00
|
|
|
json_t *sign_key_obj,
|
|
|
|
const struct TALER_MasterPublicKeyP *master_key)
|
|
|
|
{
|
2016-03-01 15:35:04 +01:00
|
|
|
struct TALER_ExchangeSigningKeyValidityPS sign_key_issue;
|
2015-06-17 18:50:09 +02:00
|
|
|
struct GNUNET_CRYPTO_EddsaSignature sig;
|
|
|
|
struct GNUNET_TIME_Absolute valid_from;
|
|
|
|
struct GNUNET_TIME_Absolute valid_until;
|
|
|
|
struct GNUNET_TIME_Absolute valid_legal;
|
2016-03-19 15:54:21 +01:00
|
|
|
struct GNUNET_JSON_Specification spec[] = {
|
|
|
|
GNUNET_JSON_spec_fixed_auto ("master_sig",
|
2016-05-18 17:58:32 +02:00
|
|
|
&sig),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_fixed_auto ("key",
|
2016-05-18 17:58:32 +02:00
|
|
|
&sign_key_issue.signkey_pub),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_absolute_time ("stamp_start",
|
2016-05-18 17:58:32 +02:00
|
|
|
&valid_from),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_absolute_time ("stamp_expire",
|
2016-05-18 17:58:32 +02:00
|
|
|
&valid_until),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_absolute_time ("stamp_end",
|
2016-05-18 17:58:32 +02:00
|
|
|
&valid_legal),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_end()
|
2015-06-20 22:40:06 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_parse (sign_key_obj,
|
|
|
|
spec,
|
|
|
|
NULL, NULL))
|
2015-06-20 22:40:06 +02:00
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2015-06-17 18:50:09 +02:00
|
|
|
|
|
|
|
sign_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY);
|
|
|
|
sign_key_issue.purpose.size =
|
2016-03-01 15:35:04 +01:00
|
|
|
htonl (sizeof (struct TALER_ExchangeSigningKeyValidityPS)
|
|
|
|
- offsetof (struct TALER_ExchangeSigningKeyValidityPS,
|
2015-07-08 09:59:02 +02:00
|
|
|
purpose));
|
2015-06-17 18:50:09 +02:00
|
|
|
sign_key_issue.master_public_key = *master_key;
|
|
|
|
sign_key_issue.start = GNUNET_TIME_absolute_hton (valid_from);
|
|
|
|
sign_key_issue.expire = GNUNET_TIME_absolute_hton (valid_until);
|
|
|
|
sign_key_issue.end = GNUNET_TIME_absolute_hton (valid_legal);
|
2015-06-20 22:40:06 +02:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY,
|
|
|
|
&sign_key_issue.purpose,
|
|
|
|
&sig,
|
|
|
|
&master_key->eddsa_pub))
|
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2015-06-17 18:50:09 +02:00
|
|
|
sign_key->valid_from = valid_from;
|
|
|
|
sign_key->valid_until = valid_until;
|
|
|
|
sign_key->key = sign_key_issue.signkey_pub;
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Parse a exchange's denomination key encoded in JSON.
|
2015-06-17 18:50:09 +02:00
|
|
|
*
|
|
|
|
* @param[out] denom_key where to return the result
|
|
|
|
* @param[in] denom_key_obj json to parse
|
|
|
|
* @param master_key master key to use to verify signature
|
2015-06-20 23:19:21 +02:00
|
|
|
* @param hash_context where to accumulate data for signature verification
|
2015-06-17 18:50:09 +02:00
|
|
|
* @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
|
|
|
|
* invalid or the json malformed.
|
|
|
|
*/
|
|
|
|
static int
|
2016-03-01 15:35:04 +01:00
|
|
|
parse_json_denomkey (struct TALER_EXCHANGE_DenomPublicKey *denom_key,
|
2015-06-17 18:50:09 +02:00
|
|
|
json_t *denom_key_obj,
|
2015-06-20 23:19:21 +02:00
|
|
|
struct TALER_MasterPublicKeyP *master_key,
|
|
|
|
struct GNUNET_HashContext *hash_context)
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
|
|
|
struct GNUNET_TIME_Absolute valid_from;
|
|
|
|
struct GNUNET_TIME_Absolute withdraw_valid_until;
|
2016-05-02 05:10:40 +02:00
|
|
|
struct GNUNET_TIME_Absolute expire_deposit;
|
2015-06-19 22:32:58 +02:00
|
|
|
struct GNUNET_TIME_Absolute expire_legal;
|
2015-06-17 18:50:09 +02:00
|
|
|
struct TALER_Amount value;
|
|
|
|
struct TALER_Amount fee_withdraw;
|
|
|
|
struct TALER_Amount fee_deposit;
|
|
|
|
struct TALER_Amount fee_refresh;
|
2016-04-20 01:50:26 +02:00
|
|
|
struct TALER_Amount fee_refund;
|
2015-06-17 18:50:09 +02:00
|
|
|
struct TALER_DenominationKeyValidityPS denom_key_issue;
|
2016-03-21 14:40:57 +01:00
|
|
|
struct GNUNET_CRYPTO_RsaPublicKey *pk;
|
2015-06-17 18:50:09 +02:00
|
|
|
struct GNUNET_CRYPTO_EddsaSignature sig;
|
2016-03-19 15:54:21 +01:00
|
|
|
struct GNUNET_JSON_Specification spec[] = {
|
|
|
|
GNUNET_JSON_spec_fixed_auto ("master_sig",
|
2016-04-20 01:50:26 +02:00
|
|
|
&sig),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_absolute_time ("stamp_expire_deposit",
|
2016-05-02 05:10:40 +02:00
|
|
|
&expire_deposit),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_absolute_time ("stamp_expire_withdraw",
|
2016-04-20 01:50:26 +02:00
|
|
|
&withdraw_valid_until),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_absolute_time ("stamp_start",
|
2016-04-20 01:50:26 +02:00
|
|
|
&valid_from),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_absolute_time ("stamp_expire_legal",
|
2016-04-20 01:50:26 +02:00
|
|
|
&expire_legal),
|
2016-03-19 15:54:21 +01:00
|
|
|
TALER_JSON_spec_amount ("value",
|
2016-04-20 01:50:26 +02:00
|
|
|
&value),
|
2016-03-19 15:54:21 +01:00
|
|
|
TALER_JSON_spec_amount ("fee_withdraw",
|
2016-04-20 01:50:26 +02:00
|
|
|
&fee_withdraw),
|
2016-03-19 15:54:21 +01:00
|
|
|
TALER_JSON_spec_amount ("fee_deposit",
|
2016-04-20 01:50:26 +02:00
|
|
|
&fee_deposit),
|
2016-03-19 15:54:21 +01:00
|
|
|
TALER_JSON_spec_amount ("fee_refresh",
|
2016-04-20 01:50:26 +02:00
|
|
|
&fee_refresh),
|
|
|
|
TALER_JSON_spec_amount ("fee_refund",
|
|
|
|
&fee_refund),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_rsa_public_key ("denom_pub",
|
2015-06-20 22:40:06 +02:00
|
|
|
&pk),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_end()
|
2015-06-20 22:40:06 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_parse (denom_key_obj,
|
|
|
|
spec, NULL, NULL))
|
2015-06-20 22:53:01 +02:00
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
2015-06-20 22:40:06 +02:00
|
|
|
return GNUNET_SYSERR;
|
2015-06-20 22:53:01 +02:00
|
|
|
}
|
2015-06-17 18:50:09 +02:00
|
|
|
|
2017-09-13 21:43:10 +02:00
|
|
|
memset (&denom_key_issue,
|
|
|
|
0,
|
|
|
|
sizeof (denom_key_issue));
|
2015-06-17 18:50:09 +02:00
|
|
|
GNUNET_CRYPTO_rsa_public_key_hash (pk,
|
|
|
|
&denom_key_issue.denom_hash);
|
2015-06-19 22:24:02 +02:00
|
|
|
denom_key_issue.purpose.purpose
|
|
|
|
= htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
|
|
|
|
denom_key_issue.purpose.size
|
2015-07-06 10:16:49 +02:00
|
|
|
= htonl (sizeof (struct TALER_DenominationKeyValidityPS));
|
2015-06-17 18:50:09 +02:00
|
|
|
denom_key_issue.master = *master_key;
|
|
|
|
denom_key_issue.start = GNUNET_TIME_absolute_hton (valid_from);
|
|
|
|
denom_key_issue.expire_withdraw = GNUNET_TIME_absolute_hton (withdraw_valid_until);
|
2016-05-02 05:10:40 +02:00
|
|
|
denom_key_issue.expire_deposit = GNUNET_TIME_absolute_hton (expire_deposit);
|
2015-06-19 22:32:58 +02:00
|
|
|
denom_key_issue.expire_legal = GNUNET_TIME_absolute_hton (expire_legal);
|
2015-06-17 18:50:09 +02:00
|
|
|
TALER_amount_hton (&denom_key_issue.value,
|
|
|
|
&value);
|
|
|
|
TALER_amount_hton (&denom_key_issue.fee_withdraw,
|
|
|
|
&fee_withdraw);
|
|
|
|
TALER_amount_hton (&denom_key_issue.fee_deposit,
|
|
|
|
&fee_deposit);
|
|
|
|
TALER_amount_hton (&denom_key_issue.fee_refresh,
|
|
|
|
&fee_refresh);
|
2016-04-20 01:50:26 +02:00
|
|
|
TALER_amount_hton (&denom_key_issue.fee_refund,
|
|
|
|
&fee_refund);
|
2015-06-17 18:50:09 +02:00
|
|
|
EXITIF (GNUNET_SYSERR ==
|
|
|
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY,
|
|
|
|
&denom_key_issue.purpose,
|
|
|
|
&sig,
|
|
|
|
&master_key->eddsa_pub));
|
2015-06-20 23:19:21 +02:00
|
|
|
GNUNET_CRYPTO_hash_context_read (hash_context,
|
|
|
|
&denom_key_issue.denom_hash,
|
|
|
|
sizeof (struct GNUNET_HashCode));
|
2015-06-17 18:50:09 +02:00
|
|
|
denom_key->key.rsa_public_key = pk;
|
2015-09-19 16:11:31 +02:00
|
|
|
denom_key->h_key = denom_key_issue.denom_hash;
|
2015-06-17 18:50:09 +02:00
|
|
|
denom_key->valid_from = valid_from;
|
|
|
|
denom_key->withdraw_valid_until = withdraw_valid_until;
|
2016-05-02 05:10:40 +02:00
|
|
|
denom_key->expire_deposit = expire_deposit;
|
2015-09-19 16:11:31 +02:00
|
|
|
denom_key->expire_legal = expire_legal;
|
2015-06-17 18:50:09 +02:00
|
|
|
denom_key->value = value;
|
|
|
|
denom_key->fee_withdraw = fee_withdraw;
|
|
|
|
denom_key->fee_deposit = fee_deposit;
|
|
|
|
denom_key->fee_refresh = fee_refresh;
|
2016-04-20 01:50:26 +02:00
|
|
|
denom_key->fee_refund = fee_refund;
|
2015-06-17 18:50:09 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
|
|
|
|
EXITIF_exit:
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_parse_free (spec);
|
2015-06-17 18:50:09 +02:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-19 16:11:31 +02:00
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Parse a exchange's auditor information encoded in JSON.
|
2015-09-19 16:11:31 +02:00
|
|
|
*
|
|
|
|
* @param[out] auditor where to return the result
|
|
|
|
* @param[in] 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.
|
|
|
|
*/
|
|
|
|
static int
|
2016-03-01 15:35:04 +01:00
|
|
|
parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor,
|
2015-09-19 16:11:31 +02:00
|
|
|
json_t *auditor_obj,
|
2016-03-01 15:35:04 +01:00
|
|
|
const struct TALER_EXCHANGE_Keys *key_data)
|
2015-09-19 16:11:31 +02:00
|
|
|
{
|
|
|
|
json_t *keys;
|
|
|
|
json_t *key;
|
|
|
|
unsigned int len;
|
|
|
|
unsigned int off;
|
|
|
|
unsigned int i;
|
2016-03-21 01:45:53 +01:00
|
|
|
const char *auditor_url;
|
2016-03-01 15:35:04 +01:00
|
|
|
struct TALER_ExchangeKeyValidityPS kv;
|
2016-03-19 15:54:21 +01:00
|
|
|
struct GNUNET_JSON_Specification spec[] = {
|
|
|
|
GNUNET_JSON_spec_fixed_auto ("auditor_pub",
|
2016-03-21 01:45:53 +01:00
|
|
|
&auditor->auditor_pub),
|
|
|
|
GNUNET_JSON_spec_string ("auditor_url",
|
|
|
|
&auditor_url),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_json ("denomination_keys",
|
2016-03-21 01:45:53 +01:00
|
|
|
&keys),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_end()
|
2015-09-19 16:11:31 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_parse (auditor_obj,
|
|
|
|
spec,
|
|
|
|
NULL, NULL))
|
2015-09-19 16:11:31 +02:00
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2016-03-21 01:45:53 +01:00
|
|
|
auditor->auditor_url = GNUNET_strdup (auditor_url);
|
2016-03-01 15:35:04 +01:00
|
|
|
kv.purpose.purpose = htonl (TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS);
|
|
|
|
kv.purpose.size = htonl (sizeof (struct TALER_ExchangeKeyValidityPS));
|
2016-03-21 01:45:53 +01:00
|
|
|
GNUNET_CRYPTO_hash (auditor_url,
|
|
|
|
strlen (auditor_url) + 1,
|
|
|
|
&kv.auditor_url_hash);
|
2015-09-19 16:11:31 +02:00
|
|
|
kv.master = key_data->master_pub;
|
|
|
|
len = json_array_size (keys);
|
2018-09-15 22:20:07 +02:00
|
|
|
auditor->denom_key_offsets = GNUNET_new_array (len,
|
|
|
|
unsigned int);
|
2015-09-19 16:11:31 +02:00
|
|
|
i = 0;
|
|
|
|
off = 0;
|
|
|
|
json_array_foreach (keys, i, key) {
|
|
|
|
struct TALER_AuditorSignatureP auditor_sig;
|
|
|
|
struct GNUNET_HashCode denom_h;
|
2016-03-01 15:35:04 +01:00
|
|
|
const struct TALER_EXCHANGE_DenomPublicKey *dk;
|
2018-09-15 22:20:07 +02:00
|
|
|
unsigned int dk_off;
|
2017-06-04 12:24:51 +02:00
|
|
|
struct GNUNET_JSON_Specification kspec[] = {
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_fixed_auto ("denom_pub_h",
|
2017-05-11 11:05:48 +02:00
|
|
|
&denom_h),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_fixed_auto ("auditor_sig",
|
2017-05-11 11:05:48 +02:00
|
|
|
&auditor_sig),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_end()
|
2015-09-19 16:11:31 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_parse (key,
|
2017-06-04 12:24:51 +02:00
|
|
|
kspec,
|
2016-03-19 15:54:21 +01:00
|
|
|
NULL, NULL))
|
2017-09-12 15:34:38 +02:00
|
|
|
{
|
2015-09-19 16:11:31 +02:00
|
|
|
GNUNET_break_op (0);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
dk = NULL;
|
2018-09-15 22:20:07 +02:00
|
|
|
dk_off = UINT_MAX;
|
2017-06-04 12:24:51 +02:00
|
|
|
for (unsigned int j=0;j<key_data->num_denom_keys;j++)
|
2015-09-19 16:11:31 +02:00
|
|
|
{
|
|
|
|
if (0 == memcmp (&denom_h,
|
|
|
|
&key_data->denom_keys[j].h_key,
|
|
|
|
sizeof (struct GNUNET_HashCode)))
|
|
|
|
{
|
|
|
|
dk = &key_data->denom_keys[j];
|
2018-09-15 22:20:07 +02:00
|
|
|
dk_off = j;
|
2015-09-19 16:11:31 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (NULL == dk)
|
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
kv.start = GNUNET_TIME_absolute_hton (dk->valid_from);
|
|
|
|
kv.expire_withdraw = GNUNET_TIME_absolute_hton (dk->withdraw_valid_until);
|
2016-05-02 05:10:40 +02:00
|
|
|
kv.expire_deposit = GNUNET_TIME_absolute_hton (dk->expire_deposit);
|
2015-09-19 16:11:31 +02:00
|
|
|
kv.expire_legal = GNUNET_TIME_absolute_hton (dk->expire_legal);
|
|
|
|
TALER_amount_hton (&kv.value,
|
|
|
|
&dk->value);
|
|
|
|
TALER_amount_hton (&kv.fee_withdraw,
|
|
|
|
&dk->fee_withdraw);
|
|
|
|
TALER_amount_hton (&kv.fee_deposit,
|
|
|
|
&dk->fee_deposit);
|
|
|
|
TALER_amount_hton (&kv.fee_refresh,
|
|
|
|
&dk->fee_refresh);
|
2016-04-20 01:50:26 +02:00
|
|
|
TALER_amount_hton (&kv.fee_refund,
|
|
|
|
&dk->fee_refund);
|
2015-09-19 16:11:31 +02:00
|
|
|
kv.denom_hash = dk->h_key;
|
2017-09-26 15:05:27 +02:00
|
|
|
|
2015-09-19 16:11:31 +02:00
|
|
|
if (GNUNET_OK !=
|
2016-03-01 15:35:04 +01:00
|
|
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS,
|
2015-09-19 16:11:31 +02:00
|
|
|
&kv.purpose,
|
|
|
|
&auditor_sig.eddsa_sig,
|
|
|
|
&auditor->auditor_pub.eddsa_pub))
|
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
2017-06-04 12:24:51 +02:00
|
|
|
GNUNET_JSON_parse_free (spec);
|
2017-05-11 11:05:48 +02:00
|
|
|
return GNUNET_SYSERR;
|
2015-09-19 16:11:31 +02:00
|
|
|
}
|
2018-09-15 22:20:07 +02:00
|
|
|
auditor->denom_key_offsets[off] = dk_off;
|
2015-09-19 16:11:31 +02:00
|
|
|
off++;
|
|
|
|
}
|
|
|
|
auditor->num_denom_keys = off;
|
2017-06-04 12:24:51 +02:00
|
|
|
GNUNET_JSON_parse_free (spec);
|
2015-09-19 16:11:31 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-17 18:50:09 +02:00
|
|
|
/**
|
|
|
|
* Decode the JSON in @a resp_obj from the /keys response and store the data
|
|
|
|
* in the @a key_data.
|
|
|
|
*
|
|
|
|
* @param[in] resp_obj JSON object to parse
|
|
|
|
* @param[out] key_data where to store the results we decoded
|
2017-07-01 14:15:26 +02:00
|
|
|
* @param[out] where to store version compatibility data
|
2015-06-17 18:50:09 +02:00
|
|
|
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error (malformed JSON)
|
|
|
|
*/
|
|
|
|
static int
|
2016-04-17 17:45:15 +02:00
|
|
|
decode_keys_json (const json_t *resp_obj,
|
2017-07-01 14:15:26 +02:00
|
|
|
struct TALER_EXCHANGE_Keys *key_data,
|
|
|
|
enum TALER_EXCHANGE_VersionCompatibility *vc)
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
|
|
|
struct GNUNET_TIME_Absolute list_issue_date;
|
2017-09-13 01:14:31 +02:00
|
|
|
struct GNUNET_TIME_Absolute last_denom_issue_date;
|
2016-03-01 15:35:04 +01:00
|
|
|
struct TALER_ExchangeSignatureP sig;
|
|
|
|
struct TALER_ExchangeKeySetPS ks;
|
2015-06-20 23:19:21 +02:00
|
|
|
struct GNUNET_HashContext *hash_context;
|
2016-03-01 15:35:04 +01:00
|
|
|
struct TALER_ExchangePublicKeyP pub;
|
2017-09-12 15:34:38 +02:00
|
|
|
unsigned int age;
|
|
|
|
unsigned int revision;
|
|
|
|
unsigned int current;
|
2015-06-17 18:50:09 +02:00
|
|
|
|
|
|
|
if (JSON_OBJECT != json_typeof (resp_obj))
|
2017-09-26 12:30:24 +02:00
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
2015-06-17 18:50:09 +02:00
|
|
|
return GNUNET_SYSERR;
|
2017-09-26 12:30:24 +02:00
|
|
|
}
|
2017-09-12 15:34:38 +02:00
|
|
|
/* check the version */
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
2017-06-01 23:00:06 +02:00
|
|
|
const char *ver;
|
2016-03-19 15:54:21 +01:00
|
|
|
struct GNUNET_JSON_Specification spec[] = {
|
2017-06-01 23:00:06 +02:00
|
|
|
GNUNET_JSON_spec_string ("version",
|
|
|
|
&ver),
|
2016-03-19 15:54:21 +01:00
|
|
|
GNUNET_JSON_spec_end()
|
2015-06-20 22:40:06 +02:00
|
|
|
};
|
2015-06-17 18:50:09 +02:00
|
|
|
|
2017-09-26 12:30:24 +02:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_JSON_parse (resp_obj,
|
|
|
|
spec,
|
|
|
|
NULL, NULL))
|
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-07-01 14:15:26 +02:00
|
|
|
if (3 != sscanf (ver,
|
|
|
|
"%u:%u:%u",
|
|
|
|
¤t,
|
|
|
|
&revision,
|
|
|
|
&age))
|
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
*vc = TALER_EXCHANGE_VC_MATCH;
|
|
|
|
if (TALER_PROTOCOL_CURRENT < current)
|
|
|
|
{
|
|
|
|
*vc |= TALER_EXCHANGE_VC_NEWER;
|
|
|
|
if (TALER_PROTOCOL_CURRENT < current - age)
|
|
|
|
*vc |= TALER_EXCHANGE_VC_INCOMPATIBLE;
|
|
|
|
}
|
|
|
|
if (TALER_PROTOCOL_CURRENT > current)
|
|
|
|
{
|
|
|
|
*vc |= TALER_EXCHANGE_VC_OLDER;
|
|
|
|
if (TALER_PROTOCOL_CURRENT - TALER_PROTOCOL_AGE > current)
|
|
|
|
*vc |= TALER_EXCHANGE_VC_INCOMPATIBLE;
|
|
|
|
}
|
2017-06-01 23:00:06 +02:00
|
|
|
key_data->version = GNUNET_strdup (ver);
|
2015-06-17 18:50:09 +02:00
|
|
|
}
|
|
|
|
|
2017-09-12 15:34:38 +02:00
|
|
|
/* parse the master public key and issue date of the response */
|
|
|
|
hash_context = GNUNET_CRYPTO_hash_context_start ();
|
|
|
|
{
|
|
|
|
struct GNUNET_JSON_Specification spec[] = {
|
|
|
|
GNUNET_JSON_spec_fixed_auto ("master_public_key",
|
|
|
|
&key_data->master_pub),
|
|
|
|
GNUNET_JSON_spec_fixed_auto ("eddsa_sig",
|
|
|
|
&sig),
|
|
|
|
GNUNET_JSON_spec_fixed_auto ("eddsa_pub",
|
|
|
|
&pub),
|
|
|
|
GNUNET_JSON_spec_absolute_time ("list_issue_date",
|
|
|
|
&list_issue_date),
|
2018-01-18 16:55:16 +01:00
|
|
|
GNUNET_JSON_spec_relative_time ("reserve_closing_delay",
|
|
|
|
&key_data->reserve_closing_delay),
|
2017-09-12 15:34:38 +02:00
|
|
|
GNUNET_JSON_spec_end()
|
|
|
|
};
|
|
|
|
|
|
|
|
EXITIF (GNUNET_OK !=
|
|
|
|
GNUNET_JSON_parse (resp_obj,
|
|
|
|
spec,
|
|
|
|
NULL, NULL));
|
|
|
|
}
|
|
|
|
|
2015-06-17 18:50:09 +02:00
|
|
|
/* parse the signing 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_ARRAY != json_typeof (sign_keys_array));
|
2017-09-13 01:14:31 +02:00
|
|
|
EXITIF (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);
|
2015-06-17 18:50:09 +02:00
|
|
|
index = 0;
|
|
|
|
json_array_foreach (sign_keys_array, index, sign_key_obj) {
|
|
|
|
EXITIF (GNUNET_SYSERR ==
|
2017-09-13 01:14:31 +02:00
|
|
|
parse_json_signkey (&key_data->sign_keys[index],
|
2015-06-17 18:50:09 +02:00
|
|
|
sign_key_obj,
|
|
|
|
&key_data->master_pub));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-13 01:14:31 +02:00
|
|
|
/* parse the denomination keys, merging with the
|
|
|
|
possibly EXISTING array as required (/keys cherry picking) */
|
|
|
|
last_denom_issue_date.abs_value_us = 0LLU;
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
|
|
|
json_t *denom_keys_array;
|
|
|
|
json_t *denom_key_obj;
|
|
|
|
unsigned int index;
|
|
|
|
|
|
|
|
EXITIF (NULL == (denom_keys_array =
|
2018-09-15 22:20:07 +02:00
|
|
|
json_object_get (resp_obj,
|
|
|
|
"denoms")));
|
2015-06-17 18:50:09 +02:00
|
|
|
EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
|
2017-09-12 15:34:38 +02:00
|
|
|
|
2015-06-17 18:50:09 +02:00
|
|
|
index = 0;
|
|
|
|
json_array_foreach (denom_keys_array, index, denom_key_obj) {
|
2017-09-12 15:34:38 +02:00
|
|
|
struct TALER_EXCHANGE_DenomPublicKey dk;
|
|
|
|
bool found = false;
|
2017-09-13 01:14:31 +02:00
|
|
|
|
2015-06-17 18:50:09 +02:00
|
|
|
EXITIF (GNUNET_SYSERR ==
|
2017-09-12 15:34:38 +02:00
|
|
|
parse_json_denomkey (&dk,
|
2015-06-17 18:50:09 +02:00
|
|
|
denom_key_obj,
|
2015-06-20 23:19:21 +02:00
|
|
|
&key_data->master_pub,
|
|
|
|
hash_context));
|
2017-09-12 15:34:38 +02:00
|
|
|
for (unsigned int j=0;j<key_data->num_denom_keys;j++)
|
|
|
|
{
|
|
|
|
if (0 == memcmp (&dk,
|
|
|
|
&key_data->denom_keys[j],
|
|
|
|
sizeof (dk)))
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found)
|
|
|
|
{
|
|
|
|
/* 0:0:0 did not support /keys cherry picking */
|
2017-09-13 01:14:31 +02:00
|
|
|
GNUNET_break_op (0 == current);
|
2017-09-12 15:34:38 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (key_data->denom_keys_size == key_data->num_denom_keys)
|
|
|
|
GNUNET_array_grow (key_data->denom_keys,
|
|
|
|
key_data->denom_keys_size,
|
|
|
|
key_data->denom_keys_size * 2 + 2);
|
2017-09-13 01:14:31 +02:00
|
|
|
key_data->denom_keys[key_data->num_denom_keys++] = dk;
|
|
|
|
|
|
|
|
/* Update "last_denom_issue_date" */
|
|
|
|
last_denom_issue_date
|
|
|
|
= GNUNET_TIME_absolute_max (last_denom_issue_date,
|
|
|
|
dk.valid_from);
|
|
|
|
};
|
2015-06-17 18:50:09 +02:00
|
|
|
}
|
2017-09-13 01:14:31 +02:00
|
|
|
key_data->last_denom_issue_date = last_denom_issue_date;
|
2015-06-17 18:50:09 +02:00
|
|
|
|
2015-09-19 16:11:31 +02:00
|
|
|
/* 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));
|
2017-09-13 01:14:31 +02:00
|
|
|
|
|
|
|
/* Merge with the existing auditor information we have (/keys cherry picking) */
|
|
|
|
index = 0;
|
|
|
|
json_array_foreach (auditors_array, index, auditor_info) {
|
|
|
|
struct TALER_EXCHANGE_AuditorInformation ai;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
EXITIF (GNUNET_SYSERR ==
|
|
|
|
parse_json_auditor (&ai,
|
|
|
|
auditor_info,
|
|
|
|
key_data));
|
|
|
|
for (unsigned int j=0;j<key_data->num_auditors;j++)
|
|
|
|
{
|
|
|
|
struct TALER_EXCHANGE_AuditorInformation *aix = &key_data->auditors[j];
|
2018-09-15 22:20:07 +02:00
|
|
|
|
2017-09-13 01:14:31 +02:00
|
|
|
if (0 == memcmp (&ai.auditor_pub,
|
|
|
|
&aix->auditor_pub,
|
|
|
|
sizeof (struct TALER_AuditorPublicKeyP)))
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
/* Merge denomination key signatures of downloaded /keys into existing
|
|
|
|
auditor information 'aix'. */
|
2018-09-15 22:20:07 +02:00
|
|
|
GNUNET_array_grow (aix->denom_key_offsets,
|
2017-09-13 01:14:31 +02:00
|
|
|
aix->num_denom_keys,
|
|
|
|
aix->num_denom_keys + ai.num_denom_keys);
|
2018-09-15 22:20:07 +02:00
|
|
|
memcpy (&aix->denom_key_offsets[aix->num_denom_keys - ai.num_denom_keys],
|
|
|
|
ai.denom_key_offsets,
|
|
|
|
ai.num_denom_keys * sizeof (unsigned int));
|
2017-09-13 01:14:31 +02:00
|
|
|
break;
|
|
|
|
}
|
2015-09-19 16:11:31 +02:00
|
|
|
}
|
2017-09-13 01:14:31 +02:00
|
|
|
if (found)
|
|
|
|
continue; /* we are done */
|
|
|
|
if (key_data->auditors_size == key_data->num_auditors)
|
|
|
|
GNUNET_array_grow (key_data->auditors,
|
|
|
|
key_data->auditors_size,
|
|
|
|
key_data->auditors_size * 2 + 2);
|
|
|
|
key_data->auditors[key_data->num_auditors++] = ai;
|
|
|
|
};
|
2015-09-19 16:11:31 +02:00
|
|
|
}
|
2017-09-13 01:14:31 +02:00
|
|
|
|
2015-06-20 23:19:21 +02:00
|
|
|
/* Validate signature... */
|
|
|
|
ks.purpose.size = htonl (sizeof (ks));
|
2016-03-01 15:35:04 +01:00
|
|
|
ks.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET);
|
2015-06-20 23:19:21 +02:00
|
|
|
ks.list_issue_date = GNUNET_TIME_absolute_hton (list_issue_date);
|
|
|
|
GNUNET_CRYPTO_hash_context_finish (hash_context,
|
|
|
|
&ks.hc);
|
|
|
|
hash_context = NULL;
|
2015-07-05 17:15:37 +02:00
|
|
|
EXITIF (GNUNET_OK !=
|
2016-03-01 15:35:04 +01:00
|
|
|
TALER_EXCHANGE_test_signing_key (key_data,
|
2017-09-12 15:34:38 +02:00
|
|
|
&pub));
|
2015-06-20 23:19:21 +02:00
|
|
|
EXITIF (GNUNET_OK !=
|
2016-03-01 15:35:04 +01:00
|
|
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_KEY_SET,
|
2015-06-20 23:19:21 +02:00
|
|
|
&ks.purpose,
|
|
|
|
&sig.eddsa_signature,
|
2015-07-05 17:15:37 +02:00
|
|
|
&pub.eddsa_pub));
|
2015-06-17 18:50:09 +02:00
|
|
|
return GNUNET_OK;
|
2015-06-20 23:19:21 +02:00
|
|
|
EXITIF_exit:
|
|
|
|
|
|
|
|
if (NULL != hash_context)
|
|
|
|
GNUNET_CRYPTO_hash_context_abort (hash_context);
|
|
|
|
return GNUNET_SYSERR;
|
2015-06-17 18:50:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-31 14:32:06 +02:00
|
|
|
/**
|
|
|
|
* 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,
|
2017-09-13 01:14:31 +02:00
|
|
|
key_data->num_sign_keys,
|
2016-05-31 14:32:06 +02:00
|
|
|
0);
|
2017-06-04 12:13:23 +02:00
|
|
|
for (unsigned int i=0;i<key_data->num_denom_keys;i++)
|
2016-05-31 14:32:06 +02:00
|
|
|
GNUNET_CRYPTO_rsa_public_key_free (key_data->denom_keys[i].key.rsa_public_key);
|
|
|
|
GNUNET_array_grow (key_data->denom_keys,
|
2017-09-12 15:34:38 +02:00
|
|
|
key_data->denom_keys_size,
|
2016-05-31 14:32:06 +02:00
|
|
|
0);
|
2017-06-04 12:13:23 +02:00
|
|
|
for (unsigned int i=0;i<key_data->num_auditors;i++)
|
|
|
|
{
|
2018-09-15 22:20:07 +02:00
|
|
|
GNUNET_array_grow (key_data->auditors[i].denom_key_offsets,
|
2017-06-04 12:13:23 +02:00
|
|
|
key_data->auditors[i].num_denom_keys,
|
|
|
|
0);
|
|
|
|
GNUNET_free (key_data->auditors[i].auditor_url);
|
|
|
|
}
|
2016-05-31 14:32:06 +02:00
|
|
|
GNUNET_array_grow (key_data->auditors,
|
2017-09-13 01:14:31 +02:00
|
|
|
key_data->auditors_size,
|
2016-05-31 14:32:06 +02:00
|
|
|
0);
|
2017-06-01 23:00:06 +02:00
|
|
|
GNUNET_free_non_null (key_data->version);
|
|
|
|
key_data->version = NULL;
|
2016-05-31 14:32:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initiate download of /keys from the exchange.
|
|
|
|
*
|
2018-08-19 12:12:00 +02:00
|
|
|
* @param cls exchange where to download /keys from
|
2016-05-31 14:32:06 +02:00
|
|
|
*/
|
|
|
|
static void
|
2018-08-19 12:12:00 +02:00
|
|
|
request_keys (void *cls);
|
2016-05-31 14:32:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if our current response for /keys is valid, and if
|
|
|
|
* not trigger download.
|
|
|
|
*
|
|
|
|
* @param exchange exchange to check keys for
|
2017-09-13 14:08:11 +02:00
|
|
|
* @param force_download #GNUNET_YES to force download even if /keys is still valid
|
2016-05-31 14:32:06 +02:00
|
|
|
* @return until when the response is current, 0 if we are re-downloading
|
|
|
|
*/
|
|
|
|
struct GNUNET_TIME_Absolute
|
2017-09-13 14:08:11 +02:00
|
|
|
TALER_EXCHANGE_check_keys_current (struct TALER_EXCHANGE_Handle *exchange,
|
|
|
|
int force_download)
|
2016-05-31 14:32:06 +02:00
|
|
|
{
|
|
|
|
if (NULL != exchange->kr)
|
|
|
|
return GNUNET_TIME_UNIT_ZERO_ABS;
|
2017-09-13 14:08:11 +02:00
|
|
|
if ( (GNUNET_NO == force_download) &&
|
|
|
|
(0 < GNUNET_TIME_absolute_get_remaining (exchange->key_data_expiration).rel_value_us) )
|
2016-05-31 14:32:06 +02:00
|
|
|
return exchange->key_data_expiration;
|
2018-08-19 12:22:42 +02:00
|
|
|
if (NULL == exchange->retry_task)
|
2018-08-19 12:12:00 +02:00
|
|
|
exchange->retry_task = GNUNET_SCHEDULER_add_now (&request_keys,
|
|
|
|
exchange);
|
2016-05-31 14:32:06 +02:00
|
|
|
return GNUNET_TIME_UNIT_ZERO_ABS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-17 18:50:09 +02:00
|
|
|
/**
|
|
|
|
* Callback used when downloading the reply to a /keys request
|
|
|
|
* is complete.
|
|
|
|
*
|
|
|
|
* @param cls the `struct KeysRequest`
|
2016-04-17 17:45:15 +02:00
|
|
|
* @param response_code HTTP response code, 0 on error
|
|
|
|
* @param resp_obj parsed JSON result, NULL on error
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
|
|
|
static void
|
2015-06-21 00:00:33 +02:00
|
|
|
keys_completed_cb (void *cls,
|
2016-04-17 17:45:15 +02:00
|
|
|
long response_code,
|
|
|
|
const json_t *resp_obj)
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
|
|
|
struct KeysRequest *kr = cls;
|
2016-03-01 15:35:04 +01:00
|
|
|
struct TALER_EXCHANGE_Handle *exchange = kr->exchange;
|
2016-05-31 14:32:06 +02:00
|
|
|
struct TALER_EXCHANGE_Keys kd;
|
|
|
|
struct TALER_EXCHANGE_Keys kd_old;
|
2017-07-01 14:15:26 +02:00
|
|
|
enum TALER_EXCHANGE_VersionCompatibility vc;
|
2015-06-17 18:50:09 +02:00
|
|
|
|
2018-09-25 12:00:25 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2015-12-23 23:50:54 +01:00
|
|
|
"Received keys from URL `%s' with status %ld.\n",
|
|
|
|
kr->url,
|
|
|
|
response_code);
|
2016-05-31 14:32:06 +02:00
|
|
|
kd_old = exchange->key_data;
|
2017-09-14 20:52:39 +02:00
|
|
|
memset (&kd,
|
|
|
|
0,
|
|
|
|
sizeof (struct TALER_EXCHANGE_Keys));
|
2017-07-01 14:15:26 +02:00
|
|
|
vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR;
|
2016-05-31 14:32:06 +02:00
|
|
|
switch (response_code)
|
|
|
|
{
|
2015-06-26 09:03:18 +02:00
|
|
|
case 0:
|
2018-08-19 12:12:00 +02:00
|
|
|
free_keys_request (kr);
|
|
|
|
exchange->kr = NULL;
|
|
|
|
GNUNET_assert (NULL == exchange->retry_task);
|
2018-09-26 23:24:24 +02:00
|
|
|
exchange->retry_delay = EXCHANGE_LIB_BACKOFF (exchange->retry_delay);
|
2018-08-19 12:12:00 +02:00
|
|
|
exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay,
|
|
|
|
&request_keys,
|
|
|
|
exchange);
|
2018-08-19 17:37:30 +02:00
|
|
|
return;
|
2015-06-26 09:03:18 +02:00
|
|
|
case MHD_HTTP_OK:
|
2016-05-24 02:28:03 +02:00
|
|
|
if (NULL == resp_obj)
|
|
|
|
{
|
2015-07-11 23:07:36 +02:00
|
|
|
response_code = 0;
|
2016-05-24 02:28:03 +02:00
|
|
|
break;
|
|
|
|
}
|
2017-09-13 01:14:31 +02:00
|
|
|
/* We keep the denomination keys and auditor signatures from the
|
|
|
|
previous iteration (/keys cherry picking) */
|
|
|
|
kd.num_denom_keys = kd_old.num_denom_keys;
|
2017-09-14 20:52:39 +02:00
|
|
|
GNUNET_array_grow (kd.denom_keys,
|
|
|
|
kd.denom_keys_size,
|
|
|
|
kd.num_denom_keys);
|
2017-09-13 01:14:31 +02:00
|
|
|
/* First make a shallow copy, we then need another pass for the RSA key... */
|
|
|
|
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++)
|
|
|
|
kd.denom_keys[i].key.rsa_public_key
|
|
|
|
= GNUNET_CRYPTO_rsa_public_key_dup (kd_old.denom_keys[i].key.rsa_public_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];
|
|
|
|
|
|
|
|
anew->auditor_pub = aold->auditor_pub;
|
|
|
|
anew->auditor_url = GNUNET_strdup (aold->auditor_url);
|
2018-09-15 22:20:07 +02:00
|
|
|
GNUNET_array_grow (anew->denom_key_offsets,
|
2017-09-13 01:14:31 +02:00
|
|
|
anew->num_denom_keys,
|
|
|
|
aold->num_denom_keys);
|
2018-09-15 22:20:07 +02:00
|
|
|
memcpy (anew->denom_key_offsets,
|
|
|
|
aold->denom_key_offsets,
|
|
|
|
aold->num_denom_keys * sizeof (unsigned int));
|
2017-09-13 01:14:31 +02:00
|
|
|
}
|
|
|
|
|
2016-05-24 02:28:03 +02:00
|
|
|
if (GNUNET_OK !=
|
2016-05-31 14:32:06 +02:00
|
|
|
decode_keys_json (resp_obj,
|
2017-07-01 14:15:26 +02:00
|
|
|
&kd,
|
|
|
|
&vc))
|
2016-05-24 02:28:03 +02:00
|
|
|
{
|
|
|
|
response_code = 0;
|
|
|
|
break;
|
|
|
|
}
|
2016-05-31 14:32:06 +02:00
|
|
|
json_decref (exchange->key_data_raw);
|
2016-05-24 02:28:03 +02:00
|
|
|
exchange->key_data_raw = json_deep_copy (resp_obj);
|
2018-08-19 12:12:00 +02:00
|
|
|
exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
|
2015-06-26 09:03:18 +02:00
|
|
|
break;
|
|
|
|
default:
|
2015-07-11 23:07:36 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Unexpected response code %u\n",
|
2016-05-04 13:21:22 +02:00
|
|
|
(unsigned int) response_code);
|
2015-06-26 09:03:18 +02:00
|
|
|
break;
|
|
|
|
}
|
2017-09-14 20:52:39 +02:00
|
|
|
exchange->key_data = kd;
|
2015-06-26 09:03:18 +02:00
|
|
|
|
2015-07-11 23:07:36 +02:00
|
|
|
if (MHD_HTTP_OK != response_code)
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
2016-03-01 15:35:04 +01:00
|
|
|
exchange->kr = NULL;
|
2015-06-17 18:50:09 +02:00
|
|
|
free_keys_request (kr);
|
2016-03-01 15:35:04 +01:00
|
|
|
exchange->state = MHS_FAILED;
|
2015-06-17 18:50:09 +02:00
|
|
|
/* notify application that we failed */
|
2016-05-31 14:32:06 +02:00
|
|
|
exchange->cert_cb (exchange->cert_cb_cls,
|
2017-07-01 14:15:26 +02:00
|
|
|
NULL,
|
|
|
|
vc);
|
2016-05-31 14:32:06 +02:00
|
|
|
if (NULL != exchange->key_data_raw)
|
2018-09-15 22:20:07 +02:00
|
|
|
{
|
|
|
|
json_decref (exchange->key_data_raw);
|
|
|
|
exchange->key_data_raw = NULL;
|
|
|
|
}
|
2016-05-31 14:32:06 +02:00
|
|
|
free_key_data (&kd_old);
|
2015-06-17 18:50:09 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-05-31 14:32:06 +02:00
|
|
|
|
2016-03-01 15:35:04 +01:00
|
|
|
exchange->kr = NULL;
|
2016-05-31 14:32:06 +02:00
|
|
|
exchange->key_data_expiration = kr->expire;
|
2015-06-17 18:50:09 +02:00
|
|
|
free_keys_request (kr);
|
2016-03-01 15:35:04 +01:00
|
|
|
exchange->state = MHS_CERT;
|
2015-06-17 18:50:09 +02:00
|
|
|
/* notify application about the key information */
|
2016-05-31 14:32:06 +02:00
|
|
|
exchange->cert_cb (exchange->cert_cb_cls,
|
2017-07-01 14:15:26 +02:00
|
|
|
&exchange->key_data,
|
|
|
|
vc);
|
2016-05-31 14:32:06 +02:00
|
|
|
free_key_data (&kd_old);
|
2015-06-17 18:50:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ********************* library internal API ********* */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Get the context of a exchange.
|
2015-06-17 18:50:09 +02:00
|
|
|
*
|
2016-03-01 15:35:04 +01:00
|
|
|
* @param h the exchange handle to query
|
2015-06-17 18:50:09 +02:00
|
|
|
* @return ctx context to execute jobs in
|
|
|
|
*/
|
2016-04-17 17:45:15 +02:00
|
|
|
struct GNUNET_CURL_Context *
|
2016-03-01 15:35:04 +01:00
|
|
|
MAH_handle_to_context (struct TALER_EXCHANGE_Handle *h)
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
|
|
|
return h->ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the handle is ready to process requests.
|
|
|
|
*
|
2016-03-01 15:35:04 +01:00
|
|
|
* @param h the exchange handle to query
|
2015-06-17 18:50:09 +02:00
|
|
|
* @return #GNUNET_YES if we are ready, #GNUNET_NO if not
|
|
|
|
*/
|
|
|
|
int
|
2016-03-01 15:35:04 +01:00
|
|
|
MAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h)
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
|
|
|
return (MHS_CERT == h->state) ? GNUNET_YES : GNUNET_NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Obtain the URL to use for an API request.
|
|
|
|
*
|
2017-04-20 07:49:56 +02:00
|
|
|
* @param h handle for the exchange
|
2015-09-19 22:08:49 +02:00
|
|
|
* @param path Taler API path (i.e. "/reserve/withdraw")
|
2018-01-30 01:38:04 +01:00
|
|
|
* @return the full URL to use with cURL
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
|
|
|
char *
|
2017-04-20 07:49:56 +02:00
|
|
|
MAH_path_to_url (struct TALER_EXCHANGE_Handle *h,
|
2015-06-17 18:50:09 +02:00
|
|
|
const char *path)
|
2016-06-13 16:36:10 +02:00
|
|
|
{
|
2017-04-20 07:49:56 +02:00
|
|
|
return MAH_path_to_url2 (h->url,
|
2016-06-13 16:36:10 +02:00
|
|
|
path);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Obtain the URL to use for an API request.
|
|
|
|
*
|
|
|
|
* @param base_url base URL of the exchange (i.e. "http://exchange/")
|
|
|
|
* @param path Taler API path (i.e. "/reserve/withdraw")
|
2018-01-30 01:38:04 +01:00
|
|
|
* @return the full URL to use with cURL
|
2016-06-13 16:36:10 +02:00
|
|
|
*/
|
|
|
|
char *
|
|
|
|
MAH_path_to_url2 (const char *base_url,
|
|
|
|
const char *path)
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
|
|
|
char *url;
|
|
|
|
|
2015-12-19 20:43:41 +01:00
|
|
|
if ( ('/' == path[0]) &&
|
2016-06-13 16:36:10 +02:00
|
|
|
(0 < strlen (base_url)) &&
|
|
|
|
('/' == base_url[strlen (base_url) - 1]) )
|
2015-12-19 20:43:41 +01:00
|
|
|
path++; /* avoid generating URL with "//" from concat */
|
2015-06-17 18:50:09 +02:00
|
|
|
GNUNET_asprintf (&url,
|
|
|
|
"%s%s",
|
2016-06-13 16:36:10 +02:00
|
|
|
base_url,
|
2015-06-17 18:50:09 +02:00
|
|
|
path);
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-31 14:32:06 +02:00
|
|
|
/**
|
|
|
|
* Parse HTTP timestamp.
|
|
|
|
*
|
|
|
|
* @param date header to parse header
|
|
|
|
* @param at where to write the result
|
|
|
|
* @return #GNUNET_OK on success
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parse_date_string (const char *date,
|
|
|
|
struct GNUNET_TIME_Absolute *at)
|
|
|
|
{
|
|
|
|
struct tm now;
|
|
|
|
time_t t;
|
2017-09-26 13:46:06 +02:00
|
|
|
const char *end;
|
|
|
|
|
|
|
|
memset (&now,
|
|
|
|
0,
|
|
|
|
sizeof (now));
|
|
|
|
end = strptime (date,
|
|
|
|
"%a, %d %b %Y %H:%M:%S %Z", /* RFC-1123 standard spec */
|
|
|
|
&now);
|
|
|
|
if ( (NULL == end) ||
|
|
|
|
( (*end != '\n') &&
|
|
|
|
(*end != '\r') ) )
|
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
2016-05-31 14:32:06 +02:00
|
|
|
return GNUNET_SYSERR;
|
2017-09-26 13:46:06 +02:00
|
|
|
}
|
2016-05-31 14:32:06 +02:00
|
|
|
t = mktime (&now);
|
2017-09-26 13:46:06 +02:00
|
|
|
if (((time_t) -1) == t)
|
|
|
|
{
|
|
|
|
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"mktime");
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
if (t < 0)
|
|
|
|
t = 0; /* can happen due to timezone issues if date was 1.1.1970 */
|
2016-05-31 14:32:06 +02:00
|
|
|
at->abs_value_us = 1000LL * 1000LL * t;
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function called for each header in the HTTP /keys response.
|
|
|
|
* Finds the "Expire:" header and parses it, storing the result
|
|
|
|
* in the "expire" field fo the keys request.
|
|
|
|
*
|
|
|
|
* @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`
|
|
|
|
* @return `size * nitems` on success (everything else aborts)
|
|
|
|
*/
|
|
|
|
static size_t
|
|
|
|
header_cb (char *buffer,
|
|
|
|
size_t size,
|
|
|
|
size_t nitems,
|
|
|
|
void *userdata)
|
|
|
|
{
|
|
|
|
struct KeysRequest *kr = userdata;
|
|
|
|
size_t total = size * nitems;
|
|
|
|
char *val;
|
|
|
|
|
|
|
|
if (total < strlen (MHD_HTTP_HEADER_EXPIRES ": "))
|
|
|
|
return total;
|
|
|
|
if (0 != strncasecmp (MHD_HTTP_HEADER_EXPIRES ": ",
|
|
|
|
buffer,
|
|
|
|
strlen (MHD_HTTP_HEADER_EXPIRES ": ")))
|
|
|
|
return total;
|
|
|
|
val = GNUNET_strndup (&buffer[strlen (MHD_HTTP_HEADER_EXPIRES ": ")],
|
|
|
|
total - strlen (MHD_HTTP_HEADER_EXPIRES ": "));
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
parse_date_string (val,
|
|
|
|
&kr->expire))
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
|
|
|
"Failed to parse %s-header `%s'\n",
|
|
|
|
MHD_HTTP_HEADER_EXPIRES,
|
|
|
|
val);
|
2017-09-26 13:46:06 +02:00
|
|
|
kr->expire = GNUNET_TIME_UNIT_ZERO_ABS;
|
2016-05-31 14:32:06 +02:00
|
|
|
}
|
|
|
|
GNUNET_free (val);
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2018-08-19 12:22:42 +02:00
|
|
|
|
2015-06-17 18:50:09 +02:00
|
|
|
/* ********************* public API ******************* */
|
|
|
|
|
2018-08-19 12:22:42 +02:00
|
|
|
|
2018-10-13 08:15:02 +02:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
* @return data the data to deserialize
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
|
|
|
|
const json_t *data)
|
|
|
|
{
|
|
|
|
enum TALER_EXCHANGE_VersionCompatibility vc;
|
|
|
|
json_t *keys;
|
|
|
|
const char *url;
|
|
|
|
struct GNUNET_TIME_Absolute expire;
|
|
|
|
struct GNUNET_JSON_Specification spec[] = {
|
|
|
|
GNUNET_JSON_spec_json ("keys",
|
|
|
|
&keys),
|
|
|
|
GNUNET_JSON_spec_string ("url",
|
|
|
|
&url),
|
|
|
|
GNUNET_JSON_spec_absolute_time ("expire",
|
|
|
|
&expire),
|
|
|
|
GNUNET_JSON_spec_end()
|
|
|
|
};
|
|
|
|
struct TALER_EXCHANGE_Keys key_data;
|
|
|
|
|
|
|
|
if (NULL == data)
|
|
|
|
return;
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_JSON_parse (data,
|
|
|
|
spec,
|
|
|
|
NULL, NULL))
|
|
|
|
{
|
|
|
|
GNUNET_break_op (0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (0 != strcmp (url,
|
|
|
|
exchange->url))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memset (&key_data,
|
|
|
|
0,
|
|
|
|
sizeof (struct TALER_EXCHANGE_Keys));
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
decode_keys_json (keys,
|
|
|
|
&key_data,
|
|
|
|
&vc))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* 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;
|
|
|
|
/* notify application about the key information */
|
|
|
|
exchange->cert_cb (exchange->cert_cb_cls,
|
|
|
|
&exchange->key_data,
|
|
|
|
vc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Serialize the latest key data from @a exchange to be persisted 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
|
|
|
|
* @return NULL on error (i.e. no current data available); otherwise
|
|
|
|
* json object owned by the caller
|
|
|
|
*/
|
|
|
|
json_t *
|
|
|
|
TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-17 18:50:09 +02:00
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Initialise a connection to the exchange. Will connect to the
|
|
|
|
* exchange and obtain information about the exchange's master public
|
|
|
|
* key and the exchange's auditor. The respective information will
|
2015-06-17 18:50:09 +02:00
|
|
|
* be passed to the @a cert_cb once available, and all future
|
2016-03-01 15:35:04 +01:00
|
|
|
* interactions with the exchange will be checked to be signed
|
2015-06-17 18:50:09 +02:00
|
|
|
* (where appropriate) by the respective master key.
|
|
|
|
*
|
|
|
|
* @param ctx the context
|
2016-03-01 15:35:04 +01:00
|
|
|
* @param url HTTP base URL for the exchange
|
|
|
|
* @param cert_cb function to call with the exchange's certification information
|
2015-06-17 18:50:09 +02:00
|
|
|
* @param cert_cb_cls closure for @a cert_cb
|
2016-03-01 15:35:04 +01:00
|
|
|
* @param ... list of additional arguments, terminated by #TALER_EXCHANGE_OPTION_END.
|
|
|
|
* @return the exchange handle; NULL upon error
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
2016-03-01 15:35:04 +01:00
|
|
|
struct TALER_EXCHANGE_Handle *
|
2016-04-17 17:45:15 +02:00
|
|
|
TALER_EXCHANGE_connect (struct GNUNET_CURL_Context *ctx,
|
|
|
|
const char *url,
|
|
|
|
TALER_EXCHANGE_CertificationCallback cert_cb,
|
|
|
|
void *cert_cb_cls,
|
|
|
|
...)
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
2016-03-01 15:35:04 +01:00
|
|
|
struct TALER_EXCHANGE_Handle *exchange;
|
2018-08-19 12:22:42 +02:00
|
|
|
va_list ap;
|
2018-10-13 08:15:02 +02:00
|
|
|
enum TALER_EXCHANGE_Option opt;
|
2015-06-17 18:50:09 +02:00
|
|
|
|
2016-03-01 15:35:04 +01:00
|
|
|
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;
|
2018-08-19 12:12:00 +02:00
|
|
|
exchange->retry_task = GNUNET_SCHEDULER_add_now (&request_keys,
|
|
|
|
exchange);
|
2018-10-13 08:15:02 +02:00
|
|
|
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);
|
2016-05-31 14:32:06 +02:00
|
|
|
return exchange;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initiate download of /keys from the exchange.
|
|
|
|
*
|
2018-08-19 12:12:00 +02:00
|
|
|
* @param cls exchange where to download /keys from
|
2016-05-31 14:32:06 +02:00
|
|
|
*/
|
|
|
|
static void
|
2018-08-19 12:12:00 +02:00
|
|
|
request_keys (void *cls)
|
2016-05-31 14:32:06 +02:00
|
|
|
{
|
2018-08-19 12:12:00 +02:00
|
|
|
struct TALER_EXCHANGE_Handle *exchange = cls;
|
2016-05-31 14:32:06 +02:00
|
|
|
struct KeysRequest *kr;
|
|
|
|
CURL *eh;
|
|
|
|
|
2018-08-19 12:12:00 +02:00
|
|
|
exchange->retry_task = NULL;
|
2016-05-31 14:32:06 +02:00
|
|
|
GNUNET_assert (NULL == exchange->kr);
|
2015-06-17 18:50:09 +02:00
|
|
|
kr = GNUNET_new (struct KeysRequest);
|
2016-03-01 15:35:04 +01:00
|
|
|
kr->exchange = exchange;
|
2018-08-19 12:12:00 +02:00
|
|
|
if (GNUNET_YES ==
|
2018-09-15 22:20:07 +02:00
|
|
|
MAH_handle_is_ready (exchange))
|
2017-06-29 20:46:52 +02:00
|
|
|
{
|
|
|
|
char *arg;
|
|
|
|
|
|
|
|
GNUNET_asprintf (&arg,
|
|
|
|
"/keys?last_issue_date=%llu",
|
2017-09-17 16:53:14 +02:00
|
|
|
(unsigned long long) exchange->key_data.last_denom_issue_date.abs_value_us / 1000000LLU);
|
2017-06-29 20:46:52 +02:00
|
|
|
kr->url = MAH_path_to_url (exchange,
|
|
|
|
arg);
|
|
|
|
GNUNET_free (arg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
kr->url = MAH_path_to_url (exchange,
|
|
|
|
"/keys");
|
|
|
|
}
|
2015-12-23 22:46:18 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Requesting keys with URL `%s'.\n",
|
|
|
|
kr->url);
|
2018-09-22 01:21:55 +02:00
|
|
|
eh = TEL_curl_easy_get (kr->url);
|
2015-12-23 23:24:10 +01:00
|
|
|
GNUNET_assert (CURLE_OK ==
|
2016-05-31 14:32:06 +02:00
|
|
|
curl_easy_setopt (eh,
|
2015-12-23 23:24:10 +01:00
|
|
|
CURLOPT_VERBOSE,
|
2016-01-22 16:52:52 +01:00
|
|
|
0));
|
2015-12-23 23:24:10 +01:00
|
|
|
GNUNET_assert (CURLE_OK ==
|
2016-05-31 14:32:06 +02:00
|
|
|
curl_easy_setopt (eh,
|
|
|
|
CURLOPT_TIMEOUT,
|
|
|
|
(long) 300));
|
2015-06-17 18:50:09 +02:00
|
|
|
GNUNET_assert (CURLE_OK ==
|
2016-05-31 14:32:06 +02:00
|
|
|
curl_easy_setopt (eh,
|
|
|
|
CURLOPT_HEADERFUNCTION,
|
|
|
|
&header_cb));
|
|
|
|
GNUNET_assert (CURLE_OK ==
|
|
|
|
curl_easy_setopt (eh,
|
|
|
|
CURLOPT_HEADERDATA,
|
|
|
|
kr));
|
2016-04-17 17:45:15 +02:00
|
|
|
kr->job = GNUNET_CURL_job_add (exchange->ctx,
|
2016-05-31 14:32:06 +02:00
|
|
|
eh,
|
2016-04-17 17:45:15 +02:00
|
|
|
GNUNET_NO,
|
|
|
|
&keys_completed_cb,
|
|
|
|
kr);
|
2016-03-01 15:35:04 +01:00
|
|
|
exchange->kr = kr;
|
2015-06-17 18:50:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Disconnect from the exchange
|
2015-06-17 18:50:09 +02:00
|
|
|
*
|
2016-03-01 15:35:04 +01:00
|
|
|
* @param exchange the exchange handle
|
2015-06-17 18:50:09 +02:00
|
|
|
*/
|
|
|
|
void
|
2016-03-01 15:35:04 +01:00
|
|
|
TALER_EXCHANGE_disconnect (struct TALER_EXCHANGE_Handle *exchange)
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
2016-03-01 15:35:04 +01:00
|
|
|
if (NULL != exchange->kr)
|
2015-06-17 18:50:09 +02:00
|
|
|
{
|
2016-04-17 17:45:15 +02:00
|
|
|
GNUNET_CURL_job_cancel (exchange->kr->job);
|
2016-03-01 15:35:04 +01:00
|
|
|
free_keys_request (exchange->kr);
|
|
|
|
exchange->kr = NULL;
|
2015-06-17 18:50:09 +02:00
|
|
|
}
|
2016-05-31 14:32:06 +02:00
|
|
|
free_key_data (&exchange->key_data);
|
|
|
|
if (NULL != exchange->key_data_raw)
|
|
|
|
{
|
|
|
|
json_decref (exchange->key_data_raw);
|
|
|
|
exchange->key_data_raw = NULL;
|
|
|
|
}
|
2018-08-19 12:12:00 +02:00
|
|
|
if (NULL != exchange->retry_task)
|
|
|
|
{
|
|
|
|
GNUNET_SCHEDULER_cancel (exchange->retry_task);
|
|
|
|
exchange->retry_task = NULL;
|
|
|
|
}
|
2016-03-01 15:35:04 +01:00
|
|
|
GNUNET_free (exchange->url);
|
|
|
|
GNUNET_free (exchange);
|
2015-06-17 18:50:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-20 23:19:21 +02:00
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Test if the given @a pub is a the current signing key from the exchange
|
2015-07-05 17:15:37 +02:00
|
|
|
* according to @a keys.
|
2015-06-20 23:19:21 +02:00
|
|
|
*
|
2016-03-01 15:35:04 +01:00
|
|
|
* @param keys the exchange's key set
|
|
|
|
* @param pub claimed current online signing key for the exchange
|
2015-07-05 17:15:37 +02:00
|
|
|
* @return #GNUNET_OK if @a pub is (according to /keys) a current signing key
|
2015-06-20 23:19:21 +02:00
|
|
|
*/
|
2015-07-05 17:15:37 +02:00
|
|
|
int
|
2016-03-01 15:35:04 +01:00
|
|
|
TALER_EXCHANGE_test_signing_key (const struct TALER_EXCHANGE_Keys *keys,
|
2016-04-17 17:45:15 +02:00
|
|
|
const struct TALER_ExchangePublicKeyP *pub)
|
2015-06-20 23:19:21 +02:00
|
|
|
{
|
|
|
|
struct GNUNET_TIME_Absolute now;
|
|
|
|
|
2015-07-05 17:15:37 +02:00
|
|
|
/* we will check using a tolerance of 1h for the time */
|
2015-06-20 23:19:21 +02:00
|
|
|
now = GNUNET_TIME_absolute_get ();
|
2017-09-13 01:14:31 +02:00
|
|
|
for (unsigned int i=0;i<keys->num_sign_keys;i++)
|
2015-07-05 17:15:37 +02:00
|
|
|
if ( (keys->sign_keys[i].valid_from.abs_value_us <= now.abs_value_us + 60 * 60 * 1000LL * 1000LL) &&
|
|
|
|
(keys->sign_keys[i].valid_until.abs_value_us > now.abs_value_us - 60 * 60 * 1000LL * 1000LL) &&
|
|
|
|
(0 == memcmp (pub,
|
|
|
|
&keys->sign_keys[i].key,
|
2016-03-01 15:35:04 +01:00
|
|
|
sizeof (struct TALER_ExchangePublicKeyP))) )
|
2015-07-05 17:15:37 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
return GNUNET_SYSERR;
|
2015-06-20 23:19:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-21 20:43:54 +02:00
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Obtain the denomination key details from the exchange.
|
2015-06-21 20:43:54 +02:00
|
|
|
*
|
2016-03-01 15:35:04 +01:00
|
|
|
* @param keys the exchange's key set
|
2015-06-21 20:43:54 +02:00
|
|
|
* @param pk public key of the denomination to lookup
|
2015-11-03 16:49:14 +01:00
|
|
|
* @return details about the given denomination key, NULL if the key is
|
|
|
|
* not found
|
2015-06-21 20:43:54 +02:00
|
|
|
*/
|
2016-03-01 15:35:04 +01:00
|
|
|
const struct TALER_EXCHANGE_DenomPublicKey *
|
|
|
|
TALER_EXCHANGE_get_denomination_key (const struct TALER_EXCHANGE_Keys *keys,
|
2016-05-31 14:32:06 +02:00
|
|
|
const struct TALER_DenominationPublicKey *pk)
|
2015-06-21 20:43:54 +02:00
|
|
|
{
|
2017-09-13 01:14:31 +02:00
|
|
|
for (unsigned int i=0;i<keys->num_denom_keys;i++)
|
2015-06-21 20:43:54 +02:00
|
|
|
if (0 == GNUNET_CRYPTO_rsa_public_key_cmp (pk->rsa_public_key,
|
|
|
|
keys->denom_keys[i].key.rsa_public_key))
|
|
|
|
return &keys->denom_keys[i];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-19 16:11:31 +02:00
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Obtain the denomination key details from the exchange.
|
2015-09-19 16:11:31 +02:00
|
|
|
*
|
2016-03-01 15:35:04 +01:00
|
|
|
* @param keys the exchange's key set
|
2015-09-19 16:11:31 +02:00
|
|
|
* @param hc hash of the public key of the denomination to lookup
|
|
|
|
* @return details about the given denomination key
|
|
|
|
*/
|
2016-03-01 15:35:04 +01:00
|
|
|
const struct TALER_EXCHANGE_DenomPublicKey *
|
|
|
|
TALER_EXCHANGE_get_denomination_key_by_hash (const struct TALER_EXCHANGE_Keys *keys,
|
2016-05-31 14:32:06 +02:00
|
|
|
const struct GNUNET_HashCode *hc)
|
2015-09-19 16:11:31 +02:00
|
|
|
{
|
2017-09-13 01:14:31 +02:00
|
|
|
for (unsigned int i=0;i<keys->num_denom_keys;i++)
|
2015-09-19 16:11:31 +02:00
|
|
|
if (0 == memcmp (hc,
|
|
|
|
&keys->denom_keys[i].h_key,
|
|
|
|
sizeof (struct GNUNET_HashCode)))
|
|
|
|
return &keys->denom_keys[i];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-21 20:43:54 +02:00
|
|
|
/**
|
2016-03-01 15:35:04 +01:00
|
|
|
* Obtain the keys from the exchange.
|
2015-06-21 20:43:54 +02:00
|
|
|
*
|
2016-03-01 15:35:04 +01:00
|
|
|
* @param exchange the exchange handle
|
|
|
|
* @return the exchange's key set
|
2015-06-21 20:43:54 +02:00
|
|
|
*/
|
2016-03-01 15:35:04 +01:00
|
|
|
const struct TALER_EXCHANGE_Keys *
|
2016-05-31 14:32:06 +02:00
|
|
|
TALER_EXCHANGE_get_keys (struct TALER_EXCHANGE_Handle *exchange)
|
2015-06-21 20:43:54 +02:00
|
|
|
{
|
2017-09-13 14:08:11 +02:00
|
|
|
(void) TALER_EXCHANGE_check_keys_current (exchange,
|
|
|
|
GNUNET_NO);
|
2016-03-01 15:35:04 +01:00
|
|
|
return &exchange->key_data;
|
2015-06-21 20:43:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-24 02:28:03 +02:00
|
|
|
/**
|
|
|
|
* Obtain the keys from the exchange in the
|
|
|
|
* raw JSON format
|
|
|
|
*
|
|
|
|
* @param exchange the exchange handle
|
|
|
|
* @return the exchange's keys in raw JSON
|
|
|
|
*/
|
|
|
|
json_t *
|
2016-05-31 14:32:06 +02:00
|
|
|
TALER_EXCHANGE_get_keys_raw (struct TALER_EXCHANGE_Handle *exchange)
|
2016-05-24 02:28:03 +02:00
|
|
|
{
|
2017-09-13 14:08:11 +02:00
|
|
|
(void) TALER_EXCHANGE_check_keys_current (exchange,
|
|
|
|
GNUNET_NO);
|
2016-05-24 02:28:03 +02:00
|
|
|
return json_deep_copy (exchange->key_data_raw);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-01 15:35:04 +01:00
|
|
|
/* end of exchange_api_handle.c */
|