From 5740506b249c18259c34e5d34ae4b539abbafd9a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 17 Jun 2015 18:50:09 +0200 Subject: [PATCH] refactoring mint API, mostly done (compiles again, /keys might even work) --- src/include/taler_crypto_lib.h | 12 + src/include/taler_mint_service.h | 273 +++++--- src/mint-lib/Makefile.am | 4 +- src/mint-lib/mint_api.c | 1023 +----------------------------- src/mint-lib/mint_api_context.c | 370 +++++++++++ src/mint-lib/mint_api_context.h | 83 +++ src/mint-lib/mint_api_handle.c | 821 ++++++++++++++++++++++++ src/mint-lib/mint_api_handle.h | 59 ++ src/mint-lib/test_mint_api.c | 2 +- 9 files changed, 1546 insertions(+), 1101 deletions(-) create mode 100644 src/mint-lib/mint_api_context.c create mode 100644 src/mint-lib/mint_api_context.h create mode 100644 src/mint-lib/mint_api_handle.c create mode 100644 src/mint-lib/mint_api_handle.h diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index f2c73faa3..6c1ef8e8e 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -170,6 +170,18 @@ struct TALER_MasterPublicKeyP }; +/** + * @brief Type of the public key used by the auditor. + */ +struct TALER_AuditorPublicKeyP +{ + /** + * Taler uses EdDSA for the auditor's signing key. + */ + struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub; +}; + + /** * @brief Type of the offline master public keys used by the mint. */ diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h index 4fd9a281d..2f641241f 100644 --- a/src/include/taler_mint_service.h +++ b/src/include/taler_mint_service.h @@ -17,6 +17,7 @@ * @file include/taler_mint_service.h * @brief C interface of libtalermint, a C library to use mint's HTTP API * @author Sree Harsha Totakura + * @author Christian Grothoff */ #ifndef _TALER_MINT_SERVICE_H #define _TALER_MINT_SERVICE_H @@ -24,14 +25,114 @@ #include "taler_util.h" /** - * @brief Handle to this library context + * @brief Handle to this library context. This is where the + * main event loop logic lives. */ struct TALER_MINT_Context; + /** - * @brief Handle to the mint + * Initialise a context. A context should be used for each thread and should + * not be shared among multiple threads. + * + * @return the context, NULL on error (failure to initialize) */ -struct TALER_MINT_Handle; +struct TALER_MINT_Context * +TALER_MINT_init (void); + + +/** + * Obtain the information for a select() call to wait until + * #TALER_MINT_perform() is ready again. Note that calling + * any other TALER_MINT-API may also imply that the library + * is again ready for #TALER_MINT_perform(). + * + * Basically, a client should use this API to prepare for select(), + * then block on select(), then call #TALER_MINT_perform() and then + * start again until the work with the context is done. + * + * This function will NOT zero out the sets and assumes that @a max_fd + * and @a timeout are already set to minimal applicable values. It is + * safe to give this API FD-sets and @a max_fd and @a timeout that are + * already initialized to some other descriptors that need to go into + * the select() call. + * + * @param ctx context to get the event loop information for + * @param read_fd_set will be set for any pending read operations + * @param write_fd_set will be set for any pending write operations + * @param except_fd_set is here because curl_multi_fdset() has this argument + * @param max_fd set to the highest FD included in any set; + * if the existing sets have no FDs in it, the initial + * value should be "-1". (Note that `max_fd + 1` will need + * to be passed to select().) + * @param timeout set to the timeout in milliseconds (!); -1 means + * no timeout (NULL, blocking forever is OK), 0 means to + * proceed immediately with #TALER_MINT_perform(). + */ +void +TALER_MINT_get_select_info (struct TALER_MINT_Context *ctx, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *except_fd_set, + int *max_fd, + long *timeout); + + +/** + * Run the main event loop for the Taler interaction. + * + * @param ctx the library context + */ +void +TALER_MINT_perform (struct TALER_MINT_Context *ctx); + + +/** + * Cleanup library initialisation resources. This function should be called + * after using this library to cleanup the resources occupied during library's + * initialisation. + * + * @param ctx the library context + */ +void +TALER_MINT_fini (struct TALER_MINT_Context *ctx); + + +/** + * List of possible options to be passed to + * #TALER_MINT_connect(). + */ +enum TALER_MINT_Option +{ + /** + * Terminator (end of option list). + */ + TALER_MINT_OPTION_END = 0 + +}; + + +/** + * Information we get from the mint about auditors. + */ +struct TALER_MINT_AuditorInformation +{ + /** + * Public key of the auditing institution. + */ + struct TALER_AuditorPublicKeyP auditor_pub; + + /** + * URL of the auditing institution. The application must check that + * this is an acceptable auditor for its purpose and also verify + * that the @a auditor_pub matches the auditor's public key given at + * that website. We expect that in practice software is going to + * often ship with an initial list of accepted auditors, just like + * browsers ship with a CA root store. + */ + const char *auditor_url; +}; + /** * @brief Mint's signature key @@ -56,7 +157,7 @@ struct TALER_MINT_SigningPublicKey /** - * @brief Mint's denomination key + * @brief Public information about a mint's denomination key */ struct TALER_MINT_DenomPublicKey { @@ -104,45 +205,94 @@ struct TALER_MINT_DenomPublicKey /** - * Initialise a context. A context should be used for each thread and should - * not be shared among multiple threads. - * - * @return the context + * Information about keys from the mint. */ -struct TALER_MINT_Context * -TALER_MINT_init (void); +struct TALER_MINT_Keys +{ + + /** + * Long-term offline signing key of the mint. + */ + struct TALER_MasterPublicKeyP master_pub; + + /** + * Array of the mint's online signing keys. + */ + struct TALER_MINT_SigningPublicKey *sign_keys; + + /** + * Array of the mint's denomination keys. + */ + struct TALER_MINT_DenomPublicKey *denom_keys; + + /** + * Array of the keys of the auditors of the mint. + */ + struct TALER_AuditorPublicKeyP *auditors; + + /** + * Length of the @e sign_keys array. + */ + unsigned int num_sign_keys; + + /** + * Length of the @e denom_keys array. + */ + unsigned int num_denom_keys; + + /** + * Length of the @e auditors array. + */ + unsigned int num_auditors; + +}; /** - * Cleanup library initialisation resources. This function should be called - * after using this library to cleanup the resources occupied during library's - * initialisation. + * Function called with information about who is auditing + * a particular mint and what key the mint is using. * - * @param ctx the library context + * @param cls closure + * @param keys information about the various keys used + * by the mint */ -void -TALER_MINT_cleanup (struct TALER_MINT_Context *ctx); +typedef void +(*TALER_MINT_CertificationCallback) (void *cls, + const struct TALER_MINT_Keys *keys); /** - * Initialise a connection to the mint. + * @brief Handle to the mint. This is where we interact with + * a particular mint and keep the per-mint information. + */ +struct TALER_MINT_Handle; + + +/** + * Initialise a connection to the mint. Will connect to the + * mint and obtain information about the mint's master public + * key and the mint's auditor. The respective information will + * be passed to the @a cert_cb once available, and all future + * interactions with the mint will be checked to be signed + * (where appropriate) by the respective master key. * * @param ctx the context - * @param hostname the hostname of the mint - * @param port the point where the mint's HTTP service is running. If port is - * given as 0, ports 80 or 443 are chosen depending on @a url. - * @param master_key the public master key of the mint. This is used to verify the - * responses of the mint. + * @param url HTTP base URL for the mint + * @param cert_cb function to call with the mint's certification information + * @param cert_cb_cls closure for @a cert_cb + * @param ... list of additional arguments, terminated by #TALER_MINT_OPTION_END. * @return the mint handle; NULL upon error */ struct TALER_MINT_Handle * TALER_MINT_connect (struct TALER_MINT_Context *ctx, - const char *hostname, - uint16_t port, - const struct TALER_MasterPublicKeyP *master_key); + const char *url, + TALER_MINT_CertificationCallback cert_cb, + void *cert_cb_cls, + ...); + /** - * Disconnect from the mint + * Disconnect from the mint. * * @param mint the mint handle */ @@ -150,69 +300,9 @@ void TALER_MINT_disconnect (struct TALER_MINT_Handle *mint); -/** - * @brief A handle to get the keys of a mint - */ -struct TALER_MINT_KeysGetHandle; - -/** - * @brief Functions of this type are called to signal completion of an asynchronous call. - * - * @param cls closure - * @param emsg if the asynchronous call could not be completed due to an error, - * this parameter contains a human readable error message - */ -typedef void -(*TALER_MINT_ContinuationCallback) (void *cls, - const char *emsg); - -/** - * @brief Functions of this type are called to provide the retrieved signing and - * denomination keys of the mint. No TALER_MINT_*() functions should be called - * in this callback. - * - * @param cls closure passed to TALER_MINT_keys_get() - * @param sign_keys NULL-terminated array of pointers to the mint's signing - * keys. NULL if no signing keys are retrieved. - * @param denom_keys NULL-terminated array of pointers to the mint's - * denomination keys; will be NULL if no signing keys are retrieved. - */ -typedef void -(*TALER_MINT_KeysGetCallback) (void *cls, - struct TALER_MINT_SigningPublicKey **sign_keys, - struct TALER_MINT_DenomPublicKey **denom_keys); - - -/** - * Get the signing and denomination key of the mint. - * - * @param mint handle to the mint - * @param cb the callback to call with the keys - * @param cb_cls closure for the @a cb callback - * @param cont_cb the callback to call after completing this asynchronous call - * @param cont_cls the closure for the @a cont_cb callback - * @return a handle to this asynchronous call; NULL upon eror - */ -struct TALER_MINT_KeysGetHandle * -TALER_MINT_keys_get (struct TALER_MINT_Handle *mint, - TALER_MINT_KeysGetCallback cb, - void *cb_cls, - TALER_MINT_ContinuationCallback cont_cb, - void *cont_cls); - - -/** - * Cancel the asynchronous call initiated by TALER_MINT_keys_get(). This should - * not be called if either of the @a TALER_MINT_KeysGetCallback or @a - * TALER_MINT_ContinuationCallback passed to TALER_MINT_keys_get() have been - * called. - * - * @param get the handle for retrieving the keys - */ -void -TALER_MINT_keys_get_cancel (struct TALER_MINT_KeysGetHandle *get); - +#if 0 +// FIXME: API below with json-crap is too low-level... /** * @brief A Deposit Handle */ @@ -221,7 +311,7 @@ struct TALER_MINT_DepositHandle; /** * Callbacks of this type are used to serve the result of submitting a deposit - * permission object to a mint + * permission object to a mint. * * @param cls closure * @param status 1 for successful deposit, 2 for retry, 0 for failure @@ -256,7 +346,6 @@ TALER_MINT_deposit_submit_json (struct TALER_MINT_Handle *mint, json_t *deposit_obj); -#if 0 /** * Submit a deposit permission to the mint and get the mint's response. * @@ -291,7 +380,6 @@ TALER_MINT_deposit_submit_json_ (struct TALER_MINT_Handle *mint, const struct GNUNET_HashCode *h_wire, const struct TALER_CoinSignature *csig, json_t *wire_obj); -#endif /** @@ -303,4 +391,7 @@ TALER_MINT_deposit_submit_json_ (struct TALER_MINT_Handle *mint, void TALER_MINT_deposit_submit_cancel (struct TALER_MINT_DepositHandle *deposit); +#endif + + #endif /* _TALER_MINT_SERVICE_H */ diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am index e6e25b046..d5940e9c2 100644 --- a/src/mint-lib/Makefile.am +++ b/src/mint-lib/Makefile.am @@ -14,7 +14,8 @@ libtalermint_la_LDFLAGS = \ -no-undefined libtalermint_la_SOURCES = \ - mint_api.c + mint_api_context.c mint_api_context.h \ + mint_api_handle.c mint_api_handle.h libtalermint_la_LIBADD = \ -lgnunetutil \ @@ -32,4 +33,3 @@ test_mint_api_LDADD = \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetutil \ -ljansson - diff --git a/src/mint-lib/mint_api.c b/src/mint-lib/mint_api.c index 250d52ec1..23d591ff1 100644 --- a/src/mint-lib/mint_api.c +++ b/src/mint-lib/mint_api.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) 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 @@ -14,7 +14,6 @@ TALER; see the file COPYING. If not, If not, see */ - /** * @file mint-lib/mint_api.c * @brief Implementation of the client interface to mint's HTTP API @@ -28,10 +27,18 @@ #include "taler_signatures.h" -#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)); +// leftovers follow... +/** + * 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)); /** @@ -43,150 +50,10 @@ __FILE__, __LINE__, error.text, error.source) /** - * Failsafe flag + * Failsafe flag. Raised if our constructor fails to initialize + * the Curl library. */ -static int fail; - -/** - * Context - */ -struct TALER_MINT_Context -{ - /** - * CURL multi handle - */ - CURLM *multi; - - /** - * CURL share handle - */ - CURLSH *share; - - /** - * Perform task handle - */ - struct GNUNET_SCHEDULER_Task *perform_task; -}; - -/** - * Type of requests we currently have - */ -enum RequestType -{ - /** - * No request - */ - REQUEST_TYPE_NONE, - - /** - * Current request is to receive mint's keys - */ - REQUEST_TYPE_KEYSGET, - - /** - * Current request is to submit a deposit permission and get its status - */ - REQUEST_TYPE_DEPOSIT -}; - - -/** - * Handle to the mint - */ -struct TALER_MINT_Handle -{ - /** - * The context of this handle - */ - struct TALER_MINT_Context *ctx; - - /** - * The hostname of the mint - */ - char *hostname; - - /** - * The CURL handle - */ - CURL *curl; - - /** - * Error buffer for CURL - */ - char emsg[CURL_ERROR_SIZE]; - - /** - * Download buffer - */ - void *buf; - - /** - * The currently active request - */ - union { - /** - * Used to denote no request if set to NULL - */ - void *none; - - /** - * Denom keys get request if REQUEST_TYPE_KEYSGET - */ - struct TALER_MINT_KeysGetHandle *keys_get; - - /** - * Deposit request if REQUEST_TYPE_DEPOSIT - */ - struct TALER_MINT_DepositHandle *deposit; - } req; - - /** - * The size of the download buffer - */ - size_t buf_size; - - /** - * Active request type - */ - enum RequestType req_type; - - /** - * The service port of the mint - */ - uint16_t port; - - /** - * Are we connected to the mint? - */ - uint8_t connected; - -}; - - -/** - * A handle to get the keys of a mint - */ -struct TALER_MINT_KeysGetHandle -{ - /** - * The connection to mint this request handle will use - */ - struct TALER_MINT_Handle *mint; - - /** - * The url for this handle - */ - char *url; - - TALER_MINT_KeysGetCallback cb; - - void *cb_cls; - - TALER_MINT_ContinuationCallback cont_cb; - - void *cont_cls; -}; - +static int TALER_MINT_curl_fail; /** * A handle to submit a deposit permission and get its status @@ -214,27 +81,6 @@ struct TALER_MINT_DepositHandle }; -/** - * Parses the timestamp encoded as ASCII string as UNIX timstamp. - * - * @param abs successfully parsed timestamp will be returned thru this parameter - * @param tstamp_enc the ASCII encoding of the timestamp - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -static int -parse_timestamp (struct GNUNET_TIME_Absolute *abs, - const char *tstamp_enc) -{ - unsigned long tstamp; - - if (1 != sscanf (tstamp_enc, "%lu", &tstamp)) - return GNUNET_SYSERR; - *abs = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get_zero_ (), - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, tstamp)); - return GNUNET_OK; -} - #define EXITIF(cond) \ do { \ @@ -244,341 +90,6 @@ parse_timestamp (struct GNUNET_TIME_Absolute *abs, static int -parse_json_signkey (struct TALER_MINT_SigningPublicKey **_sign_key, - json_t *sign_key_obj, - struct TALER_MasterPublicKeyP *master_key) -{ - json_t *valid_from_obj; - json_t *valid_until_obj; - json_t *valid_legal_obj; - json_t *key_obj; - json_t *sig_obj; - const char *valid_from_enc; - const char *valid_until_enc; - const char *valid_legal_enc; - const char *key_enc; - const char *sig_enc; - struct TALER_MINT_SigningPublicKey *sign_key; - struct TALER_MintSigningKeyValidityPS sign_key_issue; - struct GNUNET_CRYPTO_EddsaSignature sig; - struct GNUNET_TIME_Absolute valid_from; - struct GNUNET_TIME_Absolute valid_until; - struct GNUNET_TIME_Absolute valid_legal; - - EXITIF (JSON_OBJECT != json_typeof (sign_key_obj)); - EXITIF (NULL == (valid_from_obj = json_object_get (sign_key_obj, - "stamp_start"))); - EXITIF (NULL == (valid_until_obj = json_object_get (sign_key_obj, - "stamp_expire"))); - EXITIF (NULL == (valid_legal_obj = json_object_get (sign_key_obj, - "stamp_end"))); - EXITIF (NULL == (key_obj = json_object_get (sign_key_obj, "key"))); - EXITIF (NULL == (sig_obj = json_object_get (sign_key_obj, "master_sig"))); - EXITIF (NULL == (valid_from_enc = json_string_value (valid_from_obj))); - EXITIF (NULL == (valid_until_enc = json_string_value (valid_until_obj))); - EXITIF (NULL == (valid_legal_enc = json_string_value (valid_legal_obj))); - EXITIF (NULL == (key_enc = json_string_value (key_obj))); - EXITIF (NULL == (sig_enc = json_string_value (sig_obj))); - EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from, - valid_from_enc)); - EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_until, - valid_until_enc)); - EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_legal, - valid_legal_enc)); - EXITIF (52 != strlen (key_enc)); /* strlen(base32(char[32])) = 52 */ - EXITIF (103 != strlen (sig_enc)); /* strlen(base32(char[64])) = 103 */ - EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103, - &sig, sizeof (sig))); - memset (&sign_key_issue, - 0, - sizeof (sign_key_issue)); - EXITIF (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_public_key_from_string (key_enc, - 52, - &sign_key_issue.signkey_pub.eddsa_pub)); - sign_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY); - sign_key_issue.purpose.size = - htonl (sizeof (sign_key_issue) - - offsetof (struct TALER_MintSigningKeyValidityPS, purpose)); - 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); - EXITIF (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY, - &sign_key_issue.purpose, - &sig, - &master_key->eddsa_pub)); - sign_key = GNUNET_new (struct TALER_MINT_SigningPublicKey); - sign_key->valid_from = valid_from; - sign_key->valid_until = valid_until; - sign_key->key = sign_key_issue.signkey_pub; - *_sign_key = sign_key; - return GNUNET_OK; - - EXITIF_exit: - return GNUNET_SYSERR; -} - - -static int -parse_json_amount (json_t *amount_obj, struct TALER_Amount *amt) -{ - json_t *obj; - const char *currency_str; - int value; - int fraction; - - EXITIF (NULL == (obj = json_object_get (amount_obj, "currency"))); - EXITIF (NULL == (currency_str = json_string_value (obj))); - EXITIF (NULL == (obj = json_object_get (amount_obj, "value"))); - EXITIF (JSON_INTEGER != json_typeof (obj)); - EXITIF (0 > (value = json_integer_value (obj))); - EXITIF (NULL == (obj = json_object_get (amount_obj, "fraction"))); - EXITIF (JSON_INTEGER != json_typeof (obj)); - EXITIF (0 > (fraction = json_integer_value (obj))); - (void) memset (amt->currency, 0, sizeof (amt->currency)); - (void) strncpy (amt->currency, currency_str, sizeof (amt->currency) - 1); - amt->value = (uint32_t) value; - amt->fraction = (uint32_t) fraction; - return GNUNET_OK; - - EXITIF_exit: - return GNUNET_SYSERR; -} - - -/* FIXME: avoid useless ** for _denom_key! */ -static int -parse_json_denomkey (struct TALER_MINT_DenomPublicKey **_denom_key, - json_t *denom_key_obj, - struct TALER_MasterPublicKeyP *master_key) -{ - json_t *obj; - const char *sig_enc; - const char *deposit_valid_until_enc; - const char *withdraw_valid_until_enc; - const char *valid_from_enc; - const char *key_enc; - char *buf; - size_t buf_size; - struct TALER_MINT_DenomPublicKey *denom_key; - struct GNUNET_TIME_Absolute valid_from; - struct GNUNET_TIME_Absolute withdraw_valid_until; - struct GNUNET_TIME_Absolute deposit_valid_until; - struct TALER_Amount value; - struct TALER_Amount fee_withdraw; - struct TALER_Amount fee_deposit; - struct TALER_Amount fee_refresh; - struct TALER_DenominationKeyValidityPS denom_key_issue; - struct GNUNET_CRYPTO_rsa_PublicKey *pk; - struct GNUNET_CRYPTO_EddsaSignature sig; - - EXITIF (JSON_OBJECT != json_typeof (denom_key_obj)); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "master_sig"))); - EXITIF (NULL == (sig_enc = json_string_value (obj))); - EXITIF (103 != strlen (sig_enc)); - EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103, - &sig, sizeof (sig))); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_deposit"))); - EXITIF (NULL == (deposit_valid_until_enc = json_string_value (obj))); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_withdraw"))); - EXITIF (NULL == (withdraw_valid_until_enc = json_string_value (obj))); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_start"))); - EXITIF (NULL == (valid_from_enc = json_string_value (obj))); - - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "denom_pub"))); - EXITIF (NULL == (key_enc = json_string_value (obj))); - - EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from, valid_from_enc)); - EXITIF (GNUNET_SYSERR == parse_timestamp (&withdraw_valid_until, - withdraw_valid_until_enc)); - EXITIF (GNUNET_SYSERR == parse_timestamp (&deposit_valid_until, - deposit_valid_until_enc)); - - memset (&denom_key_issue, 0, sizeof (denom_key_issue)); - - buf_size = (strlen (key_enc) * 5) / 8; - buf = GNUNET_malloc (buf_size); - - EXITIF (GNUNET_OK != - GNUNET_STRINGS_string_to_data (key_enc, strlen (key_enc), - buf, - buf_size)); - pk = GNUNET_CRYPTO_rsa_public_key_decode (buf, buf_size); - GNUNET_free (buf); - - EXITIF (NULL == pk); - GNUNET_CRYPTO_rsa_public_key_hash (pk, - &denom_key_issue.denom_hash); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "value"))); - EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &value)); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_withdraw"))); - EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_withdraw)); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_deposit"))); - EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_deposit)); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_refresh"))); - EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_refresh)); - denom_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY); - denom_key_issue.purpose.size = htonl - (sizeof (struct TALER_DenominationKeyValidityPS) - - offsetof (struct TALER_DenominationKeyValidityPS, purpose)); - 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); - denom_key_issue.expire_spend = GNUNET_TIME_absolute_hton (deposit_valid_until); - 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); - EXITIF (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY, - &denom_key_issue.purpose, - &sig, - &master_key->eddsa_pub)); - denom_key = GNUNET_new (struct TALER_MINT_DenomPublicKey); - denom_key->key.rsa_public_key = pk; - denom_key->valid_from = valid_from; - denom_key->withdraw_valid_until = withdraw_valid_until; - denom_key->deposit_valid_until = deposit_valid_until; - denom_key->value = value; - denom_key->fee_withdraw = fee_withdraw; - denom_key->fee_deposit = fee_deposit; - denom_key->fee_refresh = fee_refresh; - *_denom_key = denom_key; - return GNUNET_OK; - - EXITIF_exit: - return GNUNET_SYSERR; -} - - -static int -parse_response_keys_get (const char *in, size_t size, - struct TALER_MINT_SigningPublicKey ***_sign_keys, - unsigned int *_n_sign_keys, - struct TALER_MINT_DenomPublicKey ***_denom_keys, - unsigned int *_n_denom_keys) -{ - json_t *resp_obj; - struct TALER_MINT_DenomPublicKey **denom_keys; - struct TALER_MasterPublicKeyP master_key; - struct GNUNET_TIME_Absolute list_issue_date; - struct TALER_MINT_SigningPublicKey **sign_keys; - unsigned int n_denom_keys; - unsigned int n_sign_keys; - json_error_t error; - unsigned int index; - int OK; - - denom_keys = NULL; - n_denom_keys = 0; - sign_keys = NULL; - n_sign_keys = 0; - OK = 0; - resp_obj = json_loadb (in, size, - JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK, - &error); - if (NULL == resp_obj) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to parse received data as JSON object\n"); - return GNUNET_SYSERR; - } - - EXITIF (JSON_OBJECT != json_typeof (resp_obj)); - { - /* parse the master public key */ - json_t *master_key_obj; - const char *master_key_enc; - - EXITIF (NULL == (master_key_obj = json_object_get (resp_obj, "TMH_master_public_key"))); - EXITIF (NULL == (master_key_enc = json_string_value (master_key_obj))); - EXITIF (52 != strlen (master_key_enc)); /* strlen(base32(char[32])) = 52 */ - EXITIF (GNUNET_OK != - GNUNET_CRYPTO_eddsa_public_key_from_string (master_key_enc, - 52, - &master_key.eddsa_pub)); - } - { - /* parse the issue date of the response */ - json_t *list_issue_date_obj; - const char *tstamp_enc; - - EXITIF (NULL == (list_issue_date_obj = - json_object_get(resp_obj, "list_issue_date"))); - EXITIF (NULL == (tstamp_enc = json_string_value (list_issue_date_obj))); - EXITIF (GNUNET_SYSERR == parse_timestamp (&list_issue_date, tstamp_enc)); - } - { - /* parse the signing keys */ - json_t *sign_keys_array; - json_t *sign_key_obj; - - EXITIF (NULL == (sign_keys_array = - json_object_get (resp_obj, "signkeys"))); - EXITIF (JSON_ARRAY != json_typeof (sign_keys_array)); - EXITIF (0 == (n_sign_keys = json_array_size (sign_keys_array))); - sign_keys = GNUNET_malloc (sizeof (struct TALER_MINT_SigningPublicKey *) - * (n_sign_keys + 1)); - index = 0; - json_array_foreach (sign_keys_array, index, sign_key_obj) { - EXITIF (GNUNET_SYSERR == parse_json_signkey (&sign_keys[index], - sign_key_obj, - &master_key)); - } - } - { - /* parse the denomination keys */ - json_t *denom_keys_array; - json_t *denom_key_obj; - - EXITIF (NULL == (denom_keys_array = json_object_get (resp_obj, "denoms"))); - EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); - EXITIF (0 == (n_denom_keys = json_array_size (denom_keys_array))); - denom_keys = GNUNET_malloc (sizeof (struct TALER_MINT_DenomPublicKey *) - * (n_denom_keys + 1)); - index = 0; - json_array_foreach (denom_keys_array, index, denom_key_obj) { - EXITIF (GNUNET_SYSERR == parse_json_denomkey (&denom_keys[index], - denom_key_obj, - &master_key)); - } - } - OK = 1; - - EXITIF_exit: - json_decref (resp_obj); - if (!OK) - { - if (NULL != sign_keys) - { - for (index=0; NULL != sign_keys[index]; index++) - GNUNET_free_non_null (sign_keys[index]); - GNUNET_free (sign_keys); - } - if (NULL != denom_keys) - { - for (index=0; NULL != denom_keys[index]; index++) - GNUNET_free_non_null (denom_keys[index]); - GNUNET_free (denom_keys); - } - return GNUNET_SYSERR; - } - - *_sign_keys = sign_keys; - *_n_sign_keys = n_sign_keys; - *_denom_keys = denom_keys; - *_n_denom_keys = n_denom_keys; - return GNUNET_OK; -} - - -int parse_deposit_response (void *buf, size_t size, int *r_status, json_t **r_obj) { json_t *obj; @@ -612,417 +123,6 @@ parse_deposit_response (void *buf, size_t size, int *r_status, json_t **r_obj) #undef EXITIF -static void -mint_connect (struct TALER_MINT_Handle *mint) -{ - struct TALER_MINT_Context *ctx = mint->ctx; - - GNUNET_assert (0 == mint->connected); - GNUNET_assert (CURLM_OK == curl_multi_add_handle (ctx->multi, mint->curl)); - mint->connected = GNUNET_YES; -} - -static void -mint_disconnect (struct TALER_MINT_Handle *mint) -{ - struct TALER_MINT_Context *ctx = mint->ctx; - - GNUNET_assert (GNUNET_YES == mint->connected); - GNUNET_break (CURLM_OK == curl_multi_remove_handle (ctx->multi, - mint->curl)); - mint->connected = GNUNET_NO; - GNUNET_free_non_null (mint->buf); - mint->buf = NULL; - mint->buf_size = 0; - mint->req_type = REQUEST_TYPE_NONE; - mint->req.none = NULL; -} - -static void -cleanup_keys_get (struct TALER_MINT_KeysGetHandle *gh) -{ - GNUNET_free (gh->url); - GNUNET_free (gh); -} - -static void -cleanup_deposit (struct TALER_MINT_DepositHandle *dh) -{ - curl_slist_free_all (dh->headers); - GNUNET_free_non_null (dh->json_enc); - GNUNET_free (dh->url); - GNUNET_free (dh); -} - -static void -request_failed (struct TALER_MINT_Handle *mint, long resp_code) -{ - switch (mint->req_type) - { - case REQUEST_TYPE_NONE: - GNUNET_assert (0); - break; - case REQUEST_TYPE_KEYSGET: - { - struct TALER_MINT_KeysGetHandle *gh = mint->req.keys_get; - TALER_MINT_ContinuationCallback cont_cb; - void *cont_cls; - GNUNET_assert (NULL != gh); - cont_cb = gh->cont_cb; - cont_cls = gh->cont_cls; - cleanup_keys_get (gh); - mint_disconnect (mint); - cont_cb (cont_cls, mint->emsg); - } - break; - case REQUEST_TYPE_DEPOSIT: - { - struct TALER_MINT_DepositHandle *dh = mint->req.deposit; - TALER_MINT_DepositResultCallback cb = dh->cb; - void *cls = dh->cb_cls; - - GNUNET_assert (NULL != dh); - cleanup_deposit (dh); - mint_disconnect (mint); - cb (cls, 0, NULL, mint->emsg); - } - break; - } -} - -static void -request_succeeded (struct TALER_MINT_Handle *mint, long resp_code) -{ - char *emsg; - - emsg = NULL; - switch (mint->req_type) - { - case REQUEST_TYPE_NONE: - GNUNET_assert (0); - break; - case REQUEST_TYPE_KEYSGET: - { - struct TALER_MINT_KeysGetHandle *gh = mint->req.keys_get; - TALER_MINT_ContinuationCallback cont_cb; - void *cont_cls; - struct TALER_MINT_SigningPublicKey **sign_keys; - struct TALER_MINT_DenomPublicKey **denom_keys; - unsigned int n_sign_keys; - unsigned int n_denom_keys; - - GNUNET_assert (NULL != gh); - cont_cb = gh->cont_cb; - cont_cls = gh->cont_cls; - if (200 == resp_code) - { - /* parse JSON object from the mint->buf which is of size mint->buf_size */ - if (GNUNET_OK == - parse_response_keys_get (mint->buf, mint->buf_size, - &sign_keys, &n_sign_keys, - &denom_keys, &n_denom_keys)) - gh->cb (gh->cb_cls, sign_keys, denom_keys); - else - emsg = GNUNET_strdup ("Error parsing response"); - } - else - GNUNET_asprintf (&emsg, "Failed with response code: %ld", resp_code); - cleanup_keys_get (gh); - mint_disconnect (mint); - cont_cb (cont_cls, emsg); - } - break; - case REQUEST_TYPE_DEPOSIT: - { - struct TALER_MINT_DepositHandle *dh = mint->req.deposit; - TALER_MINT_DepositResultCallback cb; - void *cls; - int status; - json_t *obj; - - GNUNET_assert (NULL != dh); - obj = NULL; - cb = dh->cb; - cls = dh->cb_cls; - status = 0; - if (200 == resp_code) - { - /* parse JSON object from the mint->buf which is of size mint->buf_size */ - if (GNUNET_OK != - parse_deposit_response (mint->buf, mint->buf_size, - &status, &obj)) - emsg = GNUNET_strdup ("Error parsing response"); - } - else - GNUNET_asprintf (&emsg, "Failed with response code: %ld", resp_code); - cleanup_deposit (dh); - mint_disconnect (mint); - cb (cls, status, obj, emsg); - } - break; - } - GNUNET_free_non_null (emsg); -} - - -static void -do_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); - -static void -perform (struct TALER_MINT_Context *ctx) -{ - fd_set fd_rs; - fd_set fd_ws; - struct GNUNET_NETWORK_FDSet rs; - struct GNUNET_NETWORK_FDSet ws; - CURLMsg *cmsg; - struct TALER_MINT_Handle *mint; - long timeout; - long resp_code; - static unsigned int n_old; - int n_running; - int n_completed; - int max_fd; - - n_completed = 0; - curl_multi_perform (ctx->multi, &n_running); - GNUNET_assert (0 <= n_running); - if ((0 == n_running) || (n_running < n_old)) - { - /* some requests were completed -- handle them */ - while (NULL != (cmsg = curl_multi_info_read (ctx->multi, &n_completed))) - { - GNUNET_break (CURLMSG_DONE == cmsg->msg); /* curl only has CURLMSG_DONE */ - GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle, - CURLINFO_PRIVATE, - (char *) &mint)); - GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle, - CURLINFO_RESPONSE_CODE, - &resp_code)); - GNUNET_assert (ctx == mint->ctx); /* did we get the correct one? */ - if (CURLE_OK == cmsg->data.result) - request_succeeded (mint, resp_code); - else - request_failed (mint, resp_code); - } - } - n_old = n_running; - /* reschedule perform() */ - if (0 != n_old) - { - FD_ZERO (&fd_rs); - FD_ZERO (&fd_ws); - GNUNET_assert (CURLM_OK == curl_multi_fdset (ctx->multi, - &fd_rs, - &fd_ws, - NULL, - &max_fd)); - if (-1 == max_fd) - { - ctx->perform_task = GNUNET_SCHEDULER_add_delayed - (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100), - &do_perform, ctx); - return; - } - GNUNET_assert (CURLM_OK == curl_multi_timeout (ctx->multi, &timeout)); - if (-1 == timeout) - { - timeout = 1000 * 60 * 5; - } - GNUNET_NETWORK_fdset_zero (&rs); - GNUNET_NETWORK_fdset_zero (&ws); - GNUNET_NETWORK_fdset_copy_native (&rs, &fd_rs, max_fd + 1); - GNUNET_NETWORK_fdset_copy_native (&ws, &fd_ws, max_fd + 1); - ctx->perform_task = GNUNET_SCHEDULER_add_select - (GNUNET_SCHEDULER_PRIORITY_KEEP, - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, timeout), - &rs, &ws, - &do_perform, ctx); - } -} - - -static void -do_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct TALER_MINT_Context *ctx = cls; - - GNUNET_assert (NULL != ctx->perform_task); - ctx->perform_task = NULL; - perform (ctx); -} - -static void -perform_now (struct TALER_MINT_Context *ctx) -{ - if (NULL != ctx->perform_task) - { - GNUNET_SCHEDULER_cancel (ctx->perform_task); - ctx->perform_task = NULL; - } - ctx->perform_task = GNUNET_SCHEDULER_add_now (&do_perform, ctx); -} - - -/* This function gets called by libcurl as soon as there is data received that */ -/* needs to be saved. The size of the data pointed to by ptr is size */ -/* multiplied with nmemb, it will not be zero terminated. Return the number */ -/* of bytes actually taken care of. If that amount differs from the amount passed */ -/* to your function, it'll signal an error to the library. This will abort the */ -/* transfer and return CURLE_WRITE_ERROR. */ - -/* From 7.18.0, the function can return CURL_WRITEFUNC_PAUSE which then will */ -/* cause writing to this connection to become paused. See */ -/* curl_easy_pause(3) for further details. */ - -/* This function may be called with zero bytes data if the transferred file is */ -/* empty. */ - -/* Set this option to NULL to get the internal default function. The internal */ -/* default function will write the data to the FILE * given with */ -/* CURLOPT_WRITEDATA. */ - -/* Set the userdata argument with the CURLOPT_WRITEDATA option. */ - -/* The callback function will be passed as much data as possible in all invokes, */ -/* but you cannot possibly make any assumptions. It may be one byte, it may be */ -/* thousands. The maximum amount of body data that can be passed to the write */ -/* callback is defined in the curl.h header file: CURL_MAX_WRITE_SIZE (the usual */ -/* default is 16K). If you however have CURLOPT_HEADER set, which sends */ -/* header data to the write callback, you can get up to */ -/* CURL_MAX_HTTP_HEADER bytes of header data passed into it. This usually */ -/* means 100K. */ -static size_t -download (char *bufptr, size_t size, size_t nitems, void *cls) -{ - struct TALER_MINT_Handle *mint = cls; - size_t msize; - void *buf; - - if (0 == size * nitems) - { - /* file is empty */ - return 0; - } - msize = size * nitems; - mint->buf = GNUNET_realloc (mint->buf, mint->buf_size + msize); - buf = mint->buf + mint->buf_size; - memcpy (buf, bufptr, msize); - mint->buf_size += msize; - return msize; -} - - -/** - * Initialise a connection to the mint. - * - * @param ctx the context - * @param hostname the hostname of the mint - * @param port the point where the mint's HTTP service is running. - * @param master_key the offline master public key of the mint. - * This is used to verify the responses of the mint. - * @return the mint handle; NULL upon error - */ -struct TALER_MINT_Handle * -TALER_MINT_connect (struct TALER_MINT_Context *ctx, - const char *hostname, - uint16_t port, - const struct TALER_MasterPublicKeyP *master_key) -{ - struct TALER_MINT_Handle *mint; - - mint = GNUNET_new (struct TALER_MINT_Handle); - mint->ctx = ctx; - mint->hostname = GNUNET_strdup (hostname); - mint->port = (0 != port) ? port : 80; - mint->curl = curl_easy_init (); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_SHARE, ctx->share)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_ERRORBUFFER, mint->emsg)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_WRITEFUNCTION, &download)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_WRITEDATA, mint)); - GNUNET_assert (CURLE_OK == curl_easy_setopt (mint->curl, CURLOPT_PRIVATE, mint)); - return mint; -} - - -/** - * Disconnect from the mint - * - * @param mint the mint handle - */ -void -TALER_MINT_disconnect (struct TALER_MINT_Handle *mint) -{ - if (GNUNET_YES == mint->connected) - mint_disconnect (mint); - curl_easy_cleanup (mint->curl); - GNUNET_free (mint->hostname); - GNUNET_free (mint); -} - -/** - * Get the signing and denomination key of the mint. - * - * @param mint handle to the mint - * @param cb the callback to call with each retrieved denomination key - * @param cb_cls closure for the above callback - * @param cont_cb the callback to call after completing this asynchronous call - * @param cont_cls the closure for the continuation callback - * @return a handle to this asynchronous call; NULL upon eror - */ -struct TALER_MINT_KeysGetHandle * -TALER_MINT_keys_get (struct TALER_MINT_Handle *mint, - TALER_MINT_KeysGetCallback cb, - void *cb_cls, - TALER_MINT_ContinuationCallback cont_cb, - void *cont_cls) -{ - struct TALER_MINT_KeysGetHandle *gh; - - GNUNET_assert (REQUEST_TYPE_NONE == mint->req_type); - gh = GNUNET_new (struct TALER_MINT_KeysGetHandle); - gh->mint = mint; - mint->req_type = REQUEST_TYPE_KEYSGET; - mint->req.keys_get = gh; - gh->cb = cb; - gh->cb_cls = cb_cls; - gh->cont_cb = cont_cb; - gh->cont_cls = cont_cls; - GNUNET_asprintf (&gh->url, - "http://%s:%hu/keys", - mint->hostname, - mint->port); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, - CURLOPT_URL, - gh->url)); - if (GNUNET_NO == mint->connected) - mint_connect (mint); - perform_now (mint->ctx); - return gh; -} - - -/** - * Cancel the asynchronous call initiated by TALER_MINT_keys_get(). This - * should not be called if either of the @a TALER_MINT_KeysGetCallback or - * @a TALER_MINT_ContinuationCallback passed to TALER_MINT_keys_get() have - * been called. - * - * @param get the handle for retrieving the keys - */ -void -TALER_MINT_keys_get_cancel (struct TALER_MINT_KeysGetHandle *get) -{ - struct TALER_MINT_Handle *mint = get->mint; - - mint_disconnect (mint); - cleanup_keys_get (get); -} /** * Submit a deposit permission to the mint and get the mint's response @@ -1072,95 +172,4 @@ TALER_MINT_deposit_submit_json (struct TALER_MINT_Handle *mint, } -/** - * Cancel a deposit permission request. This function cannot be used on a - * request handle if a response is already served for it. - * - * @param deposit the deposit permission request handle - */ -void -TALER_MINT_deposit_submit_cancel (struct TALER_MINT_DepositHandle *deposit) -{ - struct TALER_MINT_Handle *mint = deposit->mint; - - mint_disconnect (mint); - cleanup_deposit (deposit); -} - - -/** - * Initialise this library. This function should be called before using any of - * the following functions. - * - * @return library context - */ -struct TALER_MINT_Context * -TALER_MINT_init () -{ - struct TALER_MINT_Context *ctx; - CURLM *multi; - CURLSH *share; - - if (fail) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "cURL was not initialised properly\n"); - return NULL; - } - if (NULL == (multi = curl_multi_init ())) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot create a cURL multi handle\n"); - return NULL; - } - if (NULL == (share = curl_share_init ())) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot create a cURL share handle\n"); - return NULL; - } - ctx = GNUNET_new (struct TALER_MINT_Context); - ctx->multi = multi; - ctx->share = share; - return ctx; -} - - -/** - * Cleanup library initialisation resources. This function should be called - * after using this library to cleanup the resources occupied during library's - * initialisation. - * - * @param ctx the library context - */ -void -TALER_MINT_cleanup (struct TALER_MINT_Context *ctx) -{ - curl_share_cleanup (ctx->share); - curl_multi_cleanup (ctx->multi); - if (NULL != ctx->perform_task) - { - GNUNET_break (0); /* investigate why this happens */ - GNUNET_SCHEDULER_cancel (ctx->perform_task); - } - GNUNET_free (ctx); -} - - -__attribute__ ((constructor)) -void -TALER_MINT_constructor__ (void) -{ - CURLcode ret; - if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT))) - { - CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR, "curl_global_init", ret); - fail = 1; - } -} - -__attribute__ ((destructor)) -void -TALER_MINT_destructor__ (void) -{ - if (fail) - return; - curl_global_cleanup (); -} +/* end of mint_api.c */ diff --git a/src/mint-lib/mint_api_context.c b/src/mint-lib/mint_api_context.c new file mode 100644 index 000000000..7f70d092a --- /dev/null +++ b/src/mint-lib/mint_api_context.c @@ -0,0 +1,370 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + 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 + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_context.c + * @brief Implementation of the context part of the mint's HTTP API + * @author Sree Harsha Totakura + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include "taler_mint_service.h" +#include "mint_api_context.h" + + +/** + * 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)); + + +/** + * Failsafe flag. Raised if our constructor fails to initialize + * the Curl library. + */ +static int TALER_MINT_curl_fail; + + +/** + * Jobs are CURL requests running within a `struct TALER_MINT_Context`. + */ +struct MAC_Job +{ + + /** + * We keep jobs in a DLL. + */ + struct MAC_Job *next; + + /** + * We keep jobs in a DLL. + */ + struct MAC_Job *prev; + + /** + * Easy handle of the job. + */ + CURL *easy_handle; + + /** + * Context this job runs in. + */ + struct TALER_MINT_Context *ctx; + + /** + * Function to call upon completion. + */ + MAC_JobCompletionCallback jcc; + + /** + * Closure for @e jcc. + */ + void *jcc_cls; + +}; + + +/** + * Context + */ +struct TALER_MINT_Context +{ + /** + * Curl multi handle + */ + CURLM *multi; + + /** + * Curl share handle + */ + CURLSH *share; + + /** + * We keep jobs in a DLL. + */ + struct MAC_Job *jobs_head; + + /** + * We keep jobs in a DLL. + */ + struct MAC_Job *jobs_tail; + +}; + + +/** + * Initialise this library. This function should be called before using any of + * the following functions. + * + * @return library context + */ +struct TALER_MINT_Context * +TALER_MINT_init () +{ + struct TALER_MINT_Context *ctx; + CURLM *multi; + CURLSH *share; + + if (TALER_MINT_curl_fail) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Curl was not initialised properly\n"); + return NULL; + } + if (NULL == (multi = curl_multi_init ())) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create a Curl multi handle\n"); + return NULL; + } + if (NULL == (share = curl_share_init ())) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create a Curl share handle\n"); + return NULL; + } + ctx = GNUNET_new (struct TALER_MINT_Context); + ctx->multi = multi; + ctx->share = share; + return ctx; +} + + +/** + * Schedule a CURL request to be executed and call the given @a jcc + * upon its completion. Note that the context will make use of the + * CURLOPT_PRIVATE facility of the CURL @a eh. Applications can + * instead use #MAC_easy_to_closure to extract the @a jcc_cls argument + * from a valid @a eh afterwards. + * + * @param ctx context to execute the job in + * @param eh curl easy handle for the request, will + * be executed AND cleaned up + * @param jcc callback to invoke upon completion + * @param jcc_cls closure for @a jcc + */ +struct MAC_Job * +MAC_job_add (struct TALER_MINT_Context *ctx, + CURL *eh, + MAC_JobCompletionCallback jcc, + void *jcc_cls) +{ + struct MAC_Job *job; + + job = GNUNET_new (struct MAC_Job); + job->easy_handle = eh; + job->ctx = ctx; + job->jcc = jcc; + job->jcc_cls = jcc_cls; + GNUNET_CONTAINER_DLL_insert (ctx->jobs_head, + ctx->jobs_tail, + job); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_PRIVATE, + job)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_SHARE, + ctx->share)); + GNUNET_assert (CURLM_OK == + curl_multi_add_handle (ctx->multi, + eh)); + return job; +} + + +/** + * Obtain the `jcc_cls` argument from an `eh` that was + * given to #MAC_job_add(). + * + * @param eh easy handle that was used + * @return the `jcc_cls` that was given to #MAC_job_add(). + */ +void * +MAC_easy_to_closure (CURL *eh) +{ + struct MAC_Job *job; + + GNUNET_assert (CURLE_OK == + curl_easy_getinfo (eh, + CURLINFO_PRIVATE, + (char *) &job)); + return job->jcc_cls; +} + + +/** + * Cancel a job. Must only be called before the job completion + * callback is called for the respective job. + * + * @param job job to cancel + */ +void +MAC_job_cancel (struct MAC_Job *job) +{ + struct TALER_MINT_Context *ctx = job->ctx; + + GNUNET_CONTAINER_DLL_remove (ctx->jobs_head, + ctx->jobs_tail, + job); + GNUNET_assert (CURLM_OK == + curl_multi_remove_handle (ctx->multi, + job->easy_handle)); + curl_easy_cleanup (job->easy_handle); + GNUNET_free (job); +} + + +/** + * Run the main event loop for the Taler interaction. + * + * @param ctx the library context + */ +void +TALER_MINT_perform (struct TALER_MINT_Context *ctx) +{ + CURLMsg *cmsg; + struct MAC_Job *job; + int n_running; + int n_completed; + + (void) curl_multi_perform (ctx->multi, + &n_running); + while (NULL != (cmsg = curl_multi_info_read (ctx->multi, + &n_completed))) + { + /* Only documented return value is CURLMSG_DONE */ + GNUNET_break (CURLMSG_DONE == cmsg->msg); + GNUNET_assert (CURLE_OK == + curl_easy_getinfo (cmsg->easy_handle, + CURLINFO_PRIVATE, + (char *) &job)); + GNUNET_assert (job->ctx == ctx); + job->jcc (job->jcc_cls); + MAC_job_cancel (job); + } +} + + +/** + * Obtain the information for a select() call to wait until + * #TALER_MINT_perform() is ready again. Note that calling + * any other TALER_MINT-API may also imply that the library + * is again ready for #TALER_MINT_perform(). + * + * Basically, a client should use this API to prepare for select(), + * then block on select(), then call #TALER_MINT_perform() and then + * start again until the work with the context is done. + * + * This function will NOT zero out the sets and assumes that @a max_fd + * and @a timeout are already set to minimal applicable values. It is + * safe to give this API FD-sets and @a max_fd and @a timeout that are + * already initialized to some other descriptors that need to go into + * the select() call. + * + * @param ctx context to get the event loop information for + * @param read_fd_set will be set for any pending read operations + * @param write_fd_set will be set for any pending write operations + * @param except_fd_set is here because curl_multi_fdset() has this argument + * @param max_fd set to the highest FD included in any set; + * if the existing sets have no FDs in it, the initial + * value should be "-1". (Note that `max_fd + 1` will need + * to be passed to select().) + * @param timeout set to the timeout in milliseconds (!); -1 means + * no timeout (NULL, blocking forever is OK), 0 means to + * proceed immediately with #TALER_MINT_perform(). + */ +void +TALER_MINT_get_select_info (struct TALER_MINT_Context *ctx, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *except_fd_set, + int *max_fd, + long *timeout) +{ + GNUNET_assert (CURLM_OK == + curl_multi_fdset (ctx->multi, + read_fd_set, + write_fd_set, + except_fd_set, + max_fd)); + GNUNET_assert (CURLM_OK == + curl_multi_timeout (ctx->multi, + timeout)); + if ( (-1 == (*timeout)) && + (NULL != ctx->jobs_head) ) + *timeout = 1000 * 60 * 5; /* curl is not always good about giving timeouts */ +} + + +/** + * Cleanup library initialisation resources. This function should be called + * after using this library to cleanup the resources occupied during library's + * initialisation. + * + * @param ctx the library context + */ +void +TALER_MINT_fini (struct TALER_MINT_Context *ctx) +{ + /* all jobs must have been cancelled at this time, assert this */ + GNUNET_assert (NULL == ctx->jobs_head); + curl_share_cleanup (ctx->share); + curl_multi_cleanup (ctx->multi); + GNUNET_free (ctx); +} + + +/** + * Initial global setup logic, specifically runs the Curl setup. + */ +__attribute__ ((constructor)) +void +TALER_MINT_constructor__ (void) +{ + CURLcode ret; + + if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT))) + { + CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR, + "curl_global_init", + ret); + TALER_MINT_curl_fail = 1; + } +} + + +/** + * Cleans up after us, specifically runs the Curl cleanup. + */ +__attribute__ ((destructor)) +void +TALER_MINT_destructor__ (void) +{ + if (TALER_MINT_curl_fail) + return; + curl_global_cleanup (); +} + +/* end of mint_api_context.c */ diff --git a/src/mint-lib/mint_api_context.h b/src/mint-lib/mint_api_context.h new file mode 100644 index 000000000..b64f007bc --- /dev/null +++ b/src/mint-lib/mint_api_context.h @@ -0,0 +1,83 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + 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 + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_context.h + * @brief Internal interface to the context part of the mint's HTTP API + * @author Sree Harsha Totakura + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler_mint_service.h" +#include "taler_signatures.h" + + +/** + * Entry in the context's job queue. + */ +struct MAC_Job; + +/** + * Function to call upon completion of a job. + */ +typedef void +(*MAC_JobCompletionCallback)(void *cls); + + +/** + * Schedule a CURL request to be executed and call the given @a jcc + * upon its completion. Note that the context will make use of the + * CURLOPT_PRIVATE facility of the CURL @a eh. Applications can + * instead use #MAC_easy_to_closure to extract the @a jcc_cls argument + * from a valid @a eh afterwards. + * + * @param ctx context to execute the job in + * @param eh curl easy handle for the request, will + * be executed AND cleaned up + * @param jcc callback to invoke upon completion + * @param jcc_cls closure for @a jcc + */ +struct MAC_Job * +MAC_job_add (struct TALER_MINT_Context *ctx, + CURL *eh, + MAC_JobCompletionCallback jcc, + void *jcc_cls); + + +/** + * Obtain the `jcc_cls` argument from an `eh` that was + * given to #MAC_job_add(). + * + * @param eh easy handle that was used + * @return the `jcc_cls` that was given to #MAC_job_add(). + */ +void * +MAC_easy_to_closure (CURL *eh); + + +/** + * Cancel a job. Must only be called before the job completion + * callback is called for the respective job. + * + * @param job job to cancel + */ +void +MAC_job_cancel (struct MAC_Job *job); + + +/* end of mint_api_context.h */ diff --git a/src/mint-lib/mint_api_handle.c b/src/mint-lib/mint_api_handle.c new file mode 100644 index 000000000..11c0b793a --- /dev/null +++ b/src/mint-lib/mint_api_handle.c @@ -0,0 +1,821 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + 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 + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_handle.c + * @brief Implementation of the "handle" component of the mint's HTTP API + * @author Sree Harsha Totakura + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include +#include "taler_mint_service.h" +#include "taler_signatures.h" +#include "mint_api_context.h" +#include "mint_api_handle.h" + + +/** + * 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)); + + +/** + * Print JSON parsing related error information + */ +#define JSON_WARN(error) \ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \ + "JSON parsing failed at %s:%u: %s (%s)", \ + __FILE__, __LINE__, error.text, error.source) + +/** + * Stages of initialization for the `struct TALER_MINT_Handle` + */ +enum MintHandleState +{ + /** + * Just allocated. + */ + MHS_INIT = 0, + + /** + * Obtained the mint's certification data and keys. + */ + MHS_CERT = 1, + + /** + * Failed to initialize (fatal). + */ + MHS_FAILED = 2 +}; + + +/** + * Data for the request to get the /keys of a mint. + */ +struct KeysRequest; + + +/** + * Handle to the mint + */ +struct TALER_MINT_Handle +{ + /** + * The context of this handle + */ + struct TALER_MINT_Context *ctx; + + /** + * The URL of the mint (i.e. "http://mint.taler.net/") + */ + char *url; + + /** + * Function to call with the mint's certification data, + * NULL if this has already been done. + */ + TALER_MINT_CertificationCallback cert_cb; + + /** + * Closure to pass to @e cert_cb. + */ + void *cert_cb_cls; + + /** + * Data for the request to get the /keys of a mint, + * NULL once we are past stage #MHS_INIT. + */ + struct KeysRequest *kr; + + /** + * Key data of the mint, only valid if + * @e handshake_complete is past stage #MHS_CERT. + */ + struct TALER_MINT_Keys key_data; + + /** + * Stage of the mint's initialization routines. + */ + enum MintHandleState state; + +}; + + +/* ***************** Internal /keys fetching ************* */ + +/** + * Data for the request to get the /keys of a mint. + */ +struct KeysRequest +{ + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this handle + */ + char *url; + + /** + * Entry for this request with the `struct TALER_MINT_Context`. + */ + struct MAC_Job *job; + + /** + * Error buffer for Curl. Do we need this? + */ + char emsg[CURL_ERROR_SIZE]; + + /** + * Download buffer + */ + void *buf; + + /** + * The size of the download buffer + */ + size_t buf_size; + + /** + * Error code (based on libc errno) if we failed to download + * (i.e. response too large). + */ + int eno; + +}; + + +/** + * Callback used when downloading the reply to a /keys request. + * Just appends all of the data to the `buf` in the + * `struct KeysRequest` for further processing. The size of + * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if + * the download exceeds this size, we abort with an error. + * + * @param bufptr data downloaded via HTTP + * @param size size of an item in @a bufptr + * @param nitems number of items in @a bufptr + * @param cls the `struct KeysRequest` + * @return number of bytes processed from @a bufptr + */ +static size_t +keys_download_cb (char *bufptr, + size_t size, + size_t nitems, + void *cls) +{ + struct KeysRequest *kr = cls; + size_t msize; + void *buf; + + if (0 == size * nitems) + { + /* Nothing (left) to do */ + return 0; + } + msize = size * nitems; + if ( (msize + kr->buf_size) >= GNUNET_MAX_MALLOC_CHECKED) + { + kr->eno = ENOMEM; + return 0; /* signals an error to curl */ + } + kr->buf = GNUNET_realloc (kr->buf, + kr->buf_size + msize); + buf = kr->buf + kr->buf_size; + memcpy (buf, bufptr, msize); + kr->buf_size += msize; + return msize; +} + + +/** + * 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_non_null (kr->buf); + GNUNET_free (kr->url); + GNUNET_free (kr); +} + + +/** + * Parses the timestamp encoded as ASCII string as UNIX timstamp. + * FIXME: we might want to move this function into libtalerutil. + * + * @param[out] abs successfully parsed timestamp will be returned thru this parameter + * @param tstamp_enc the ASCII encoding of the timestamp + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +static int +parse_timestamp (struct GNUNET_TIME_Absolute *abs, + const char *tstamp_enc) +{ + unsigned long tstamp; + + if (1 != sscanf (tstamp_enc, "%lu", &tstamp)) + return GNUNET_SYSERR; + *abs = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get_zero_ (), + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, tstamp)); + return GNUNET_OK; +} + + +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + + +/** + * Parse a mint's signing key encoded in JSON. + * + * @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 +parse_json_signkey (struct TALER_MINT_SigningPublicKey *sign_key, + json_t *sign_key_obj, + const struct TALER_MasterPublicKeyP *master_key) +{ + // TODO: try to simplify... + json_t *valid_from_obj; + json_t *valid_until_obj; + json_t *valid_legal_obj; + json_t *key_obj; + json_t *sig_obj; + const char *valid_from_enc; + const char *valid_until_enc; + const char *valid_legal_enc; + const char *key_enc; + const char *sig_enc; + struct TALER_MintSigningKeyValidityPS sign_key_issue; + struct GNUNET_CRYPTO_EddsaSignature sig; + struct GNUNET_TIME_Absolute valid_from; + struct GNUNET_TIME_Absolute valid_until; + struct GNUNET_TIME_Absolute valid_legal; + + EXITIF (JSON_OBJECT != json_typeof (sign_key_obj)); + EXITIF (NULL == (valid_from_obj = json_object_get (sign_key_obj, + "stamp_start"))); + EXITIF (NULL == (valid_until_obj = json_object_get (sign_key_obj, + "stamp_expire"))); + EXITIF (NULL == (valid_legal_obj = json_object_get (sign_key_obj, + "stamp_end"))); + EXITIF (NULL == (key_obj = json_object_get (sign_key_obj, "key"))); + EXITIF (NULL == (sig_obj = json_object_get (sign_key_obj, "master_sig"))); + EXITIF (NULL == (valid_from_enc = json_string_value (valid_from_obj))); + EXITIF (NULL == (valid_until_enc = json_string_value (valid_until_obj))); + EXITIF (NULL == (valid_legal_enc = json_string_value (valid_legal_obj))); + EXITIF (NULL == (key_enc = json_string_value (key_obj))); + EXITIF (NULL == (sig_enc = json_string_value (sig_obj))); + EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from, + valid_from_enc)); + EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_until, + valid_until_enc)); + EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_legal, + valid_legal_enc)); + EXITIF (52 != strlen (key_enc)); /* strlen(base32(char[32])) = 52 */ + EXITIF (103 != strlen (sig_enc)); /* strlen(base32(char[64])) = 103 */ + EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103, + &sig, sizeof (sig))); + memset (&sign_key_issue, + 0, + sizeof (sign_key_issue)); + EXITIF (GNUNET_SYSERR == + GNUNET_CRYPTO_eddsa_public_key_from_string (key_enc, + 52, + &sign_key_issue.signkey_pub.eddsa_pub)); + sign_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY); + sign_key_issue.purpose.size = + htonl (sizeof (sign_key_issue) + - offsetof (struct TALER_MintSigningKeyValidityPS, purpose)); + 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); + EXITIF (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY, + &sign_key_issue.purpose, + &sig, + &master_key->eddsa_pub)); + sign_key->valid_from = valid_from; + sign_key->valid_until = valid_until; + sign_key->key = sign_key_issue.signkey_pub; + return GNUNET_OK; + + EXITIF_exit: + return GNUNET_SYSERR; +} + + +/** + * Parse an amount given in JSON encoding. + * + * @param[in] amount_obj the amount in json + * @param[out] where to return the parsed amount + * @return #GNUNET_OK if all is well, #GNUNET_SYSERR on parse errors + */ +static int +parse_json_amount (json_t *amount_obj, + struct TALER_Amount *amt) +{ + // FIXME: check for correctness... + json_t *obj; + const char *currency_str; + int value; // FIXME: bad data type! (64 bit!) + int fraction; + + EXITIF (NULL == (obj = json_object_get (amount_obj, "currency"))); + EXITIF (NULL == (currency_str = json_string_value (obj))); + EXITIF (NULL == (obj = json_object_get (amount_obj, "value"))); + EXITIF (JSON_INTEGER != json_typeof (obj)); + EXITIF (0 > (value = json_integer_value (obj))); + EXITIF (NULL == (obj = json_object_get (amount_obj, "fraction"))); + EXITIF (JSON_INTEGER != json_typeof (obj)); + EXITIF (0 > (fraction = json_integer_value (obj))); + (void) memset (amt->currency, 0, sizeof (amt->currency)); + (void) strncpy (amt->currency, currency_str, sizeof (amt->currency) - 1); + amt->value = (uint32_t) value; + amt->fraction = (uint32_t) fraction; + return GNUNET_OK; + + EXITIF_exit: + return GNUNET_SYSERR; +} + + +/** + * Parse a mint's denomination key encoded in JSON. + * + * @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 + * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is + * invalid or the json malformed. + */ +static int +parse_json_denomkey (struct TALER_MINT_DenomPublicKey *denom_key, + json_t *denom_key_obj, + struct TALER_MasterPublicKeyP *master_key) +{ + // FIXME: check logic, try to simplify + json_t *obj; + const char *sig_enc; + const char *deposit_valid_until_enc; + const char *withdraw_valid_until_enc; + const char *valid_from_enc; + const char *key_enc; + char *buf; + size_t buf_size; + struct GNUNET_TIME_Absolute valid_from; + struct GNUNET_TIME_Absolute withdraw_valid_until; + struct GNUNET_TIME_Absolute deposit_valid_until; + struct TALER_Amount value; + struct TALER_Amount fee_withdraw; + struct TALER_Amount fee_deposit; + struct TALER_Amount fee_refresh; + struct TALER_DenominationKeyValidityPS denom_key_issue; + struct GNUNET_CRYPTO_rsa_PublicKey *pk; + struct GNUNET_CRYPTO_EddsaSignature sig; + + EXITIF (JSON_OBJECT != json_typeof (denom_key_obj)); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "master_sig"))); + EXITIF (NULL == (sig_enc = json_string_value (obj))); + EXITIF (103 != strlen (sig_enc)); + EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103, + &sig, sizeof (sig))); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_deposit"))); + EXITIF (NULL == (deposit_valid_until_enc = json_string_value (obj))); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_withdraw"))); + EXITIF (NULL == (withdraw_valid_until_enc = json_string_value (obj))); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_start"))); + EXITIF (NULL == (valid_from_enc = json_string_value (obj))); + + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "denom_pub"))); + EXITIF (NULL == (key_enc = json_string_value (obj))); + + EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from, valid_from_enc)); + EXITIF (GNUNET_SYSERR == parse_timestamp (&withdraw_valid_until, + withdraw_valid_until_enc)); + EXITIF (GNUNET_SYSERR == parse_timestamp (&deposit_valid_until, + deposit_valid_until_enc)); + + memset (&denom_key_issue, 0, sizeof (denom_key_issue)); + + buf_size = (strlen (key_enc) * 5) / 8; + buf = GNUNET_malloc (buf_size); + + EXITIF (GNUNET_OK != + GNUNET_STRINGS_string_to_data (key_enc, strlen (key_enc), + buf, + buf_size)); + pk = GNUNET_CRYPTO_rsa_public_key_decode (buf, buf_size); + GNUNET_free (buf); + + EXITIF (NULL == pk); + GNUNET_CRYPTO_rsa_public_key_hash (pk, + &denom_key_issue.denom_hash); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "value"))); + EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &value)); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_withdraw"))); + EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_withdraw)); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_deposit"))); + EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_deposit)); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_refresh"))); + EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_refresh)); + denom_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY); + denom_key_issue.purpose.size = htonl + (sizeof (struct TALER_DenominationKeyValidityPS) - + offsetof (struct TALER_DenominationKeyValidityPS, purpose)); + 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); + denom_key_issue.expire_spend = GNUNET_TIME_absolute_hton (deposit_valid_until); + 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); + EXITIF (GNUNET_SYSERR == + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY, + &denom_key_issue.purpose, + &sig, + &master_key->eddsa_pub)); + denom_key->key.rsa_public_key = pk; + denom_key->valid_from = valid_from; + denom_key->withdraw_valid_until = withdraw_valid_until; + denom_key->deposit_valid_until = deposit_valid_until; + denom_key->value = value; + denom_key->fee_withdraw = fee_withdraw; + denom_key->fee_deposit = fee_deposit; + denom_key->fee_refresh = fee_refresh; + return GNUNET_OK; + + EXITIF_exit: + return GNUNET_SYSERR; +} + + +/** + * 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 + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (malformed JSON) + */ +static int +decode_keys_json (json_t *resp_obj, + struct TALER_MINT_Keys *key_data) +{ + struct GNUNET_TIME_Absolute list_issue_date; + + if (JSON_OBJECT != json_typeof (resp_obj)) + return GNUNET_SYSERR; + + /* parse the master public key */ + { + json_t *master_key_obj; + const char *master_key_enc; + + EXITIF (NULL == (master_key_obj = + json_object_get (resp_obj, + "master_public_key"))); + EXITIF (NULL == (master_key_enc = + json_string_value (master_key_obj))); + EXITIF (GNUNET_OK != + GNUNET_CRYPTO_eddsa_public_key_from_string (master_key_enc, + strlen (master_key_enc), + &key_data->master_pub.eddsa_pub)); + } + + /* parse the issue date of the response */ + { + json_t *list_issue_date_obj; + const char *tstamp_enc; + + EXITIF (NULL == (list_issue_date_obj = + json_object_get (resp_obj, + "list_issue_date"))); + EXITIF (NULL == (tstamp_enc = json_string_value (list_issue_date_obj))); + EXITIF (GNUNET_SYSERR == parse_timestamp (&list_issue_date, + tstamp_enc)); + } + + /* 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)); + EXITIF (0 == (key_data->num_sign_keys = + json_array_size (sign_keys_array))); + key_data->sign_keys + = GNUNET_malloc (sizeof (struct TALER_MINT_SigningPublicKey) + * key_data->num_sign_keys); + index = 0; + json_array_foreach (sign_keys_array, index, sign_key_obj) { + EXITIF (GNUNET_SYSERR == + parse_json_signkey (&key_data->sign_keys[index], + sign_key_obj, + &key_data->master_pub)); + } + } + + /* parse the denomination keys */ + { + json_t *denom_keys_array; + json_t *denom_key_obj; + unsigned int index; + + EXITIF (NULL == (denom_keys_array = + json_object_get (resp_obj, "denoms"))); + EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); + EXITIF (0 == (key_data->num_denom_keys = json_array_size (denom_keys_array))); + key_data->denom_keys = GNUNET_malloc (sizeof (struct TALER_MINT_DenomPublicKey) + * key_data->num_denom_keys); + index = 0; + json_array_foreach (denom_keys_array, index, denom_key_obj) { + EXITIF (GNUNET_SYSERR == + parse_json_denomkey (&key_data->denom_keys[index], + denom_key_obj, + &key_data->master_pub)); + } + } + return GNUNET_OK; + + /* FIXME: parse the auditor keys */ + + /* FIXME: parse 'eddsa_sig' */ + + /* FIXME: validate signature... */ + + EXITIF_exit: + return GNUNET_OK; +} + + +/** + * We have successfully received the reply to the /keys + * request from the mint. We now need to parse the reply + * and, if successful, store the resulting information + * in the `key_data` structure. + * + * @param kr key request with all of the data to parse + * and references to the `struct TALER_MINT_Handle` + * where we need to store the result + * @return #GNUNET_OK on success, + * #GNUNET_SYSERR on failure + */ +static int +parse_response_keys_get (struct KeysRequest *kr) +{ + json_t *resp_obj; + json_error_t error; + int ret; + + resp_obj = json_loadb (kr->buf, + kr->buf_size, + JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK, + &error); + if (NULL == resp_obj) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to parse received /keys data as JSON object\n"); + GNUNET_free_non_null (kr->buf); + kr->buf = NULL; + kr->buf_size = 0; + return GNUNET_SYSERR; + } + GNUNET_free_non_null (kr->buf); + kr->buf = NULL; + kr->buf_size = 0; + ret = decode_keys_json (resp_obj, + &kr->mint->key_data); + json_decref (resp_obj); + return ret; +} + + +/** + * Callback used when downloading the reply to a /keys request + * is complete. + * + * @param cls the `struct KeysRequest` + */ +static void +keys_completed_cb (void *cls) +{ + struct KeysRequest *kr = cls; + struct TALER_MINT_Handle *mint = kr->mint; + + if ( (0 != kr->eno) || + (GNUNET_OK != + parse_response_keys_get (kr)) ) + { + mint->kr = NULL; + free_keys_request (kr); + mint->state = MHS_FAILED; + /* notify application that we failed */ + if (NULL != mint->cert_cb) + { + mint->cert_cb (mint->cert_cb_cls, + NULL); + mint->cert_cb = NULL; + } + return; + } + mint->kr = NULL; + free_keys_request (kr); + mint->state = MHS_CERT; + /* notify application about the key information */ + if (NULL != mint->cert_cb) + { + mint->cert_cb (mint->cert_cb_cls, + &mint->key_data); + mint->cert_cb = NULL; + } +} + + +/* ********************* library internal API ********* */ + + +/** + * Get the context of a mint. + * + * @param h the mint handle to query + * @return ctx context to execute jobs in + */ +struct TALER_MINT_Context * +MAH_handle_to_context (struct TALER_MINT_Handle *h) +{ + return h->ctx; +} + + +/** + * Check if the handle is ready to process requests. + * + * @param h the mint handle to query + * @return #GNUNET_YES if we are ready, #GNUNET_NO if not + */ +int +MAH_handle_is_ready (struct TALER_MINT_Handle *h) +{ + return (MHS_CERT == h->state) ? GNUNET_YES : GNUNET_NO; +} + + +/** + * Obtain the URL to use for an API request. + * + * @param h the mint handle to query + * @param path Taler API path (i.e. "/withdraw/sign") + * @return the full URI to use with cURL + */ +char * +MAH_path_to_url (struct TALER_MINT_Handle *h, + const char *path) +{ + char *url; + + GNUNET_asprintf (&url, + "%s%s", + h->url, + path); + return url; +} + + +/* ********************* public API ******************* */ + +/** + * Initialise a connection to the mint. Will connect to the + * mint and obtain information about the mint's master public + * key and the mint's auditor. The respective information will + * be passed to the @a cert_cb once available, and all future + * interactions with the mint will be checked to be signed + * (where appropriate) by the respective master key. + * + * @param ctx the context + * @param url HTTP base URL for the mint + * @param cert_cb function to call with the mint's certification information + * @param cert_cb_cls closure for @a cert_cb + * @param ... list of additional arguments, terminated by #TALER_MINT_OPTION_END. + * @return the mint handle; NULL upon error + */ +struct TALER_MINT_Handle * +TALER_MINT_connect (struct TALER_MINT_Context *ctx, + const char *url, + TALER_MINT_CertificationCallback cert_cb, + void *cert_cb_cls, + ...) +{ + struct TALER_MINT_Handle *mint; + struct KeysRequest *kr; + CURL *c; + + mint = GNUNET_new (struct TALER_MINT_Handle); + mint->ctx = ctx; + mint->url = GNUNET_strdup (url); + kr = GNUNET_new (struct KeysRequest); + kr->mint = mint; + kr->url = MAH_path_to_url (mint, "/keys"); + c = curl_easy_init (); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (c, + CURLOPT_URL, + kr->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (c, + CURLOPT_ERRORBUFFER, + kr->emsg)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (c, + CURLOPT_WRITEFUNCTION, + &keys_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (c, + CURLOPT_WRITEDATA, + kr)); + kr->job = MAC_job_add (mint->ctx, + c, + &keys_completed_cb, + kr); + mint->kr = kr; + return mint; +} + + +/** + * Disconnect from the mint + * + * @param mint the mint handle + */ +void +TALER_MINT_disconnect (struct TALER_MINT_Handle *mint) +{ + if (NULL != mint->kr) + { + MAC_job_cancel (mint->kr->job); + free_keys_request (mint->kr); + mint->kr = NULL; + } + GNUNET_array_grow (mint->key_data.sign_keys, + mint->key_data.num_sign_keys, + 0); + GNUNET_array_grow (mint->key_data.denom_keys, + mint->key_data.num_denom_keys, + 0); + GNUNET_array_grow (mint->key_data.auditors, + mint->key_data.num_auditors, + 0); + GNUNET_free (mint->url); + GNUNET_free (mint); +} + + +/* end of mint_api_handle.c */ diff --git a/src/mint-lib/mint_api_handle.h b/src/mint-lib/mint_api_handle.h new file mode 100644 index 000000000..aeaeeb593 --- /dev/null +++ b/src/mint-lib/mint_api_handle.h @@ -0,0 +1,59 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + 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 + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_handle.h + * @brief Internal interface to the handle part of the mint's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include "taler_mint_service.h" + + +/** + * Get the context of a mint. + * + * @param h the mint handle to query + * @return ctx context to execute jobs in + */ +struct TALER_MINT_Context * +MAH_handle_to_context (struct TALER_MINT_Handle *h); + + +/** + * Check if the handle is ready to process requests. + * + * @param h the mint handle to query + * @return #GNUNET_YES if we are ready, #GNUNET_NO if not + */ +int +MAH_handle_is_ready (struct TALER_MINT_Handle *h); + + +/** + * Obtain the URL to use for an API request. + * + * @param h the mint handle to query + * @param path Taler API path (i.e. "/withdraw/sign") + * @return the full URI to use with cURL + */ +char * +MAH_path_to_url (struct TALER_MINT_Handle *h, + const char *path); + + +/* end of mint_api_handle.h */ diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index a068dde93..0b429d889 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -49,7 +49,7 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) dh = NULL; TALER_MINT_disconnect (mint); mint = NULL; - TALER_MINT_cleanup (ctx); + TALER_MINT_fini (ctx); ctx = NULL; }