summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2015-01-08 18:37:20 +0100
committerChristian Grothoff <christian@grothoff.org>2015-01-08 18:37:20 +0100
commit57d1f08dbca256f5fe16d57b29bfa523dec8f6c4 (patch)
tree3b3ee5f3b8c174887217e5c465048dea4e79bae2 /src
downloadexchange-57d1f08dbca256f5fe16d57b29bfa523dec8f6c4.tar.gz
exchange-57d1f08dbca256f5fe16d57b29bfa523dec8f6c4.tar.bz2
exchange-57d1f08dbca256f5fe16d57b29bfa523dec8f6c4.zip
-initial import for mint
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore9
-rw-r--r--src/Makefile.am2
-rw-r--r--src/include/Makefile.am7
-rw-r--r--src/include/platform.h56
-rw-r--r--src/include/taler_db_lib.h132
-rw-r--r--src/include/taler_json_lib.h101
-rw-r--r--src/include/taler_microhttpd_lib.h119
-rw-r--r--src/include/taler_mint_service.h303
-rw-r--r--src/include/taler_rsa.h357
-rw-r--r--src/include/taler_signatures.h106
-rw-r--r--src/include/taler_types.h120
-rw-r--r--src/include/taler_util.h255
-rw-r--r--src/mint/.gitignore6
-rw-r--r--src/mint/Makefile.am131
-rw-r--r--src/mint/mint.h198
-rw-r--r--src/mint/mint_api.c1121
-rw-r--r--src/mint/mint_common.c283
-rw-r--r--src/mint/mint_db.c1838
-rw-r--r--src/mint/mint_db.h344
-rw-r--r--src/mint/taler-mint-dbinit.c285
-rw-r--r--src/mint/taler-mint-httpd.c376
-rw-r--r--src/mint/taler-mint-httpd.h106
-rw-r--r--src/mint/taler-mint-httpd_deposit.c270
-rw-r--r--src/mint/taler-mint-httpd_deposit.h48
-rw-r--r--src/mint/taler-mint-httpd_keys.c512
-rw-r--r--src/mint/taler-mint-httpd_keys.h155
-rw-r--r--src/mint/taler-mint-httpd_mhd.c300
-rw-r--r--src/mint/taler-mint-httpd_mhd.h132
-rw-r--r--src/mint/taler-mint-httpd_refresh.c1497
-rw-r--r--src/mint/taler-mint-httpd_refresh.h103
-rw-r--r--src/mint/taler-mint-httpd_withdraw.c400
-rw-r--r--src/mint/taler-mint-httpd_withdraw.h65
-rw-r--r--src/mint/taler-mint-keycheck.c169
-rw-r--r--src/mint/taler-mint-keyup.c657
-rw-r--r--src/mint/taler-mint-reservemod.c215
-rw-r--r--src/mint/test_mint_api.c211
-rw-r--r--src/mint/test_mint_common.c83
-rw-r--r--src/mint/test_mint_deposits.c149
-rw-r--r--src/mint/test_mint_nayapaisa.eccbin0 -> 32 bytes
-rw-r--r--src/mint/test_mint_nayapaisa/README1
-rw-r--r--src/mint/test_mint_nayapaisa/config/mint-common.conf6
-rw-r--r--src/mint/test_mint_nayapaisa/config/mint-keyup.conf79
-rw-r--r--src/mint/test_mint_nyadirahim.ecc1
-rw-r--r--src/mint/test_mint_nyadirahim/README1
-rw-r--r--src/mint/test_mint_nyadirahim/config/mint-common.conf6
-rw-r--r--src/mint/test_mint_nyadirahim/config/mint-keyup.conf79
-rw-r--r--src/util/Makefile.am39
-rw-r--r--src/util/db.c196
-rw-r--r--src/util/json.c194
-rw-r--r--src/util/microhttpd.c417
-rw-r--r--src/util/misc.supp28
-rw-r--r--src/util/rsa.c925
-rw-r--r--src/util/test_hash_context.c48
-rw-r--r--src/util/test_rsa.c112
-rw-r--r--src/util/util.c528
55 files changed, 13881 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 00000000..90ea1a04
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,9 @@
+*.o
+*.deps
+*.libs
+*.lo
+*.la
+*.log
+*.trs
+*/__pycache__
+test-* \ No newline at end of file
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 00000000..485c4f9d
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,2 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+SUBDIRS = include util mint
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
new file mode 100644
index 00000000..d10d6d70
--- /dev/null
+++ b/src/include/Makefile.am
@@ -0,0 +1,7 @@
+EXTRA_DIST = \
+ platform.h \
+ taler_blind.h \
+ taler_signatures.h \
+ taler_types.h \
+ taler_util.h \
+ taler_rsa.h
diff --git a/src/include/platform.h b/src/include/platform.h
new file mode 100644
index 00000000..4cba7abf
--- /dev/null
+++ b/src/include/platform.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of TALER
+ (C) 2014 Chrisitan 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/platform.h
+ * @brief This file contains the includes and definitions which are used by the
+ * rest of the modules
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#ifndef PLATFORM_H_
+#define PLATFORM_H_
+
+/* Include our configuration header */
+#ifndef HAVE_USED_CONFIG_H
+# define HAVE_USED_CONFIG_H
+# ifdef HAVE_CONFIG_H
+# include "taler_config.h"
+# endif
+#endif
+
+
+#if (GNUNET_EXTRA_LOGGING >= 1)
+#define VERBOSE(cmd) cmd
+#else
+#define VERBOSE(cmd) do { break; }while(0)
+#endif
+
+/* Include the features available for GNU source */
+#define _GNU_SOURCE
+
+/* Include GNUnet's platform file */
+#include <gnunet/platform.h>
+
+/* Do not use shortcuts for gcrypt mpi */
+#define GCRYPT_NO_MPI_MACROS 1
+
+/* Do not use deprecated functions from gcrypt */
+#define GCRYPT_NO_DEPRECATED 1
+
+#endif /* PLATFORM_H_ */
+
+/* end of platform.h */
diff --git a/src/include/taler_db_lib.h b/src/include/taler_db_lib.h
new file mode 100644
index 00000000..41b46264
--- /dev/null
+++ b/src/include/taler_db_lib.h
@@ -0,0 +1,132 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+
+/**
+ * @file include/taler_db_lib.h
+ * @brief helper functions for DB interactions
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Florian Dold
+ */
+
+#ifndef TALER_DB_LIB_H_
+#define TALER_DB_LIB_H_
+
+#include <libpq-fe.h>
+#include "taler_util.h"
+
+#define TALER_DB_QUERY_PARAM_END { NULL, 0, 0 }
+#define TALER_DB_QUERY_PARAM_PTR(x) { (x), sizeof (*(x)), 1 }
+#define TALER_DB_QUERY_PARAM_PTR_SIZED(x, s) { (x), (s), 1 }
+
+
+#define TALER_DB_RESULT_SPEC_END { NULL, 0, NULL }
+#define TALER_DB_RESULT_SPEC(name, dst) { (void *) (dst), sizeof (*(dst)), (name) }
+#define TALER_DB_RESULT_SPEC_SIZED(name, dst, s) { (void *) (dst), (s), (name) }
+
+
+/**
+ * Description of a DB query parameter.
+ */
+struct TALER_DB_QueryParam
+{
+ /**
+ * Data or NULL
+ */
+ const void *data;
+ /**
+ * Size of 'data'
+ */
+ size_t size;
+ /**
+ * Non-null if this is not the last parameter.
+ * This allows for null as sentinal value.
+ */
+ int more;
+};
+
+
+/**
+ * Description of a DB result cell.
+ */
+struct TALER_DB_ResultSpec
+{
+ /**
+ * Destination for the data.
+ */
+ void *dst;
+
+ /**
+ * Allowed size for the data.
+ */
+ size_t dst_size;
+
+ /**
+ * Field name of the desired result.
+ */
+ char *fname;
+};
+
+
+/**
+ * Execute a prepared statement.
+ */
+PGresult *
+TALER_DB_exec_prepared (PGconn *db_conn,
+ const char *name,
+ const struct TALER_DB_QueryParam *params);
+
+
+/**
+ * Extract results from a query result according to the given specification.
+ * If colums are NULL, the destination is not modified, and GNUNET_NO
+ * is returned.
+ *
+ * @return
+ * GNUNET_YES if all results could be extracted
+ * GNUNET_NO if at least one result was NULL
+ * GNUNET_SYSERR if a result was invalid (non-existing field)
+ */
+int
+TALER_DB_extract_result (PGresult *result, struct TALER_DB_ResultSpec *rs, int row);
+
+
+int
+TALER_DB_field_isnull (PGresult *result,
+ int row,
+ const char *fname);
+
+
+int
+TALER_DB_extract_amount_nbo (PGresult *result,
+ int row,
+ const char *val_name,
+ const char *frac_name,
+ const char *curr_name,
+ struct TALER_AmountNBO *r_amount_nbo);
+
+
+int
+TALER_DB_extract_amount (PGresult *result,
+ int row,
+ const char *val_name,
+ const char *frac_name,
+ const char *curr_name,
+ struct TALER_Amount *r_amount);
+
+#endif /* TALER_DB_LIB_H_ */
+
+/* end of include/taler_db_lib.h */
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
new file mode 100644
index 00000000..b224c4b3
--- /dev/null
+++ b/src/include/taler_json_lib.h
@@ -0,0 +1,101 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/taler_json_lib.h
+ * @brief helper functions for JSON processing using libjansson
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#ifndef TALER_JSON_LIB_H_
+#define TALER_JSON_LIB_H_
+
+#include <jansson.h>
+
+
+/**
+ * Convert a TALER amount to a JSON
+ * object.
+ *
+ * @param amount the amount
+ * @return a json object describing the amount
+ */
+json_t *
+TALER_JSON_from_amount (struct TALER_Amount amount);
+
+
+/**
+ * Convert absolute timestamp to a json string.
+ *
+ * @param the time stamp
+ * @return a json string with the timestamp in @a stamp
+ */
+json_t *
+TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp);
+
+
+
+/**
+ * Convert binary data to a JSON string
+ * with the base32crockford encoding.
+ *
+ * @param data binary data
+ * @param size size of @a data in bytes
+ * @return json string that encodes @a data
+ */
+json_t *
+TALER_JSON_from_data (const void *data, size_t size);
+
+
+/**
+ * Parse given JSON object to Amount
+ *
+ * @param json the json object representing Amount
+ * @param r_amount where the amount has to be written
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_amount (json_t *json,
+ struct TALER_Amount *r_amount);
+
+/**
+ * Parse given JSON object to absolute time.
+ *
+ * @param json the json object representing absolute time in seconds
+ * @param r_abs where the time has to be written
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_abs (json_t *json,
+ struct GNUNET_TIME_Absolute *r_abs);
+
+/**
+ * Parse given JSON object to data
+ *
+ * @param json the json object representing data
+ * @param out the pointer to hold the parsed data.
+ * @param out_size the size of r_data.
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_data (json_t *json,
+ void *out,
+ size_t out_size);
+
+
+#endif /* TALER_JSON_LIB_H_ */
+
+/* End of taler_json_lib.h */
diff --git a/src/include/taler_microhttpd_lib.h b/src/include/taler_microhttpd_lib.h
new file mode 100644
index 00000000..da601401
--- /dev/null
+++ b/src/include/taler_microhttpd_lib.h
@@ -0,0 +1,119 @@
+
+
+#ifndef TALER_MICROHTTPD_LIB_H_
+#define TALER_MICROHTTPD_LIB_H_
+
+
+#include <microhttpd.h>
+#include <jansson.h>
+
+
+/**
+ * Constants for JSON navigation description.
+ */
+enum
+{
+ /**
+ * Access a field.
+ * Param: const char *
+ */
+ JNAV_FIELD,
+ /**
+ * Access an array index.
+ * Param: int
+ */
+ JNAV_INDEX,
+ /**
+ * Return base32crockford encoded data of
+ * constant size.
+ * Params: (void *, size_t)
+ */
+ JNAV_RET_DATA,
+ /**
+ * Return base32crockford encoded data of
+ * variable size.
+ * Params: (void **, size_t *)
+ */
+ JNAV_RET_DATA_VAR,
+ /**
+ * Return a json object, which must be
+ * of the given type (JSON_* type constants,
+ * or -1 for any type).
+ * Params: (int, json_t **)
+ */
+ JNAV_RET_TYPED_JSON
+};
+
+
+
+/**
+ * Send JSON object as response. Decreases
+ * the reference count of the JSON object.
+ *
+ * @param connection the MHD connection
+ * @param json the json object
+ * @param status_code the http status code
+ * @return MHD result code (MHD_YES on success)
+ */
+int
+send_response_json (struct MHD_Connection *connection,
+ json_t *json,
+ unsigned int status_code);
+
+
+/**
+ * Send a JSON object via an MHD connection,
+ * specified with the JANSSON pack syntax (see json_pack).
+ *
+ * @param connection connection to send the JSON over
+ * @param http_code HTTP status for the response
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD_YES on success or MHD_NO on error
+ */
+int
+request_send_json_pack (struct MHD_Connection *connection,
+ unsigned int http_code,
+ const char *fmt, ...);
+
+
+/**
+ * Process a POST request containing a JSON object.
+ *
+ * @param connection the MHD connection
+ * @param con_cs the closure (contains a 'struct Buffer *')
+ * @param upload_data the POST data
+ * @param upload_data_size the POST data size
+ * @param json the JSON object for a completed request
+ *
+ * @returns
+ * GNUNET_YES if json object was parsed
+ * GNUNET_NO is request incomplete or invalid
+ * GNUNET_SYSERR on internal error
+ */
+int
+process_post_json (struct MHD_Connection *connection,
+ void **con_cls,
+ const char *upload_data,
+ size_t *upload_data_size,
+ json_t **json);
+
+
+/**
+ * Navigate through a JSON tree.
+ *
+ * Sends an error response if navigation is impossible (i.e.
+ * the JSON object is invalid)
+ *
+ * @param connection the connection to send an error response to
+ * @param root the JSON node to start the navigation at.
+ * @param ... navigation specification (see JNAV_*)
+ * @return GNUNET_YES if navigation was successful
+ * GNUNET_NO if json is malformed, error response was generated
+ * GNUNET_SYSERR on internal error
+ */
+int
+request_json_require_nav (struct MHD_Connection *connection,
+ const json_t *root, ...);
+
+#endif /* TALER_MICROHTTPD_LIB_H_ */
diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h
new file mode 100644
index 00000000..ee3b30e3
--- /dev/null
+++ b/src/include/taler_mint_service.h
@@ -0,0 +1,303 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/taler_mint_service.h
+ * @brief C interface to the mint's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#ifndef _TALER_MINT_SERVICE_H
+#define _TALER_MINT_SERVICE_H
+
+#include "taler_rsa.h"
+#include "taler_util.h"
+#include <jansson.h>
+
+/**
+ * Handle to this library context
+ */
+struct TALER_MINT_Context;
+
+/**
+ * Handle to the mint
+ */
+struct TALER_MINT_Handle;
+
+/**
+ * Mint's signature key
+ */
+struct TALER_MINT_SigningPublicKey
+{
+ /**
+ * The signing public key
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey key;
+
+ /**
+ * Validity start time
+ */
+ struct GNUNET_TIME_Absolute valid_from;
+
+ /**
+ * Validity expiration time
+ */
+ struct GNUNET_TIME_Absolute valid_until;
+};
+
+
+/**
+ * Mint's denomination key
+ */
+struct TALER_MINT_DenomPublicKey
+{
+ /**
+ * The public key
+ */
+ struct TALER_RSA_PublicKeyBinaryEncoded key;
+
+ /**
+ * Timestamp indicating when the denomination key becomes valid
+ */
+ struct GNUNET_TIME_Absolute valid_from;
+
+ /**
+ * Timestamp indicating when the denomination key can’t be used anymore to
+ * withdraw new coins.
+ */
+ struct GNUNET_TIME_Absolute withdraw_valid_until;
+
+ /**
+ * Timestamp indicating when coins of this denomination become invalid.
+ */
+ struct GNUNET_TIME_Absolute deposit_valid_until;
+
+ /**
+ * The value of this denomination
+ */
+ struct TALER_Amount value;
+
+ /**
+ * The applicable fee for withdrawing a coin of this denomination
+ */
+ struct TALER_Amount fee_withdraw;
+
+ /**
+ * The applicable fee to spend a coin of this denomination
+ */
+ struct TALER_Amount fee_deposit;
+
+ /**
+ *The applicable fee to refresh a coin of this denomination
+ */
+ struct TALER_Amount fee_refresh;
+};
+
+
+/**
+ * Initialise a context. A context should be used for each thread and should
+ * not be shared among multiple threads.
+ *
+ * @return the context
+ */
+struct TALER_MINT_Context *
+TALER_MINT_init ();
+
+
+/**
+ * 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);
+
+
+/**
+ * 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. If port is
+ * given as 0, ports 80 or 443 are chosen depending on @a url.
+ * @param mint_key the 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,
+ struct GNUNET_CRYPTO_EddsaPublicKey *mint_key);
+
+/**
+ * Disconnect from the mint
+ *
+ * @param mint the mint handle
+ */
+void
+TALER_MINT_disconnect (struct TALER_MINT_Handle *mint);
+
+
+/**
+ * A handle to get the keys of a mint
+ */
+struct TALER_MINT_KeysGetHandle;
+
+/**
+ * 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);
+
+/**
+ * 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 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 *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);
+
+
+/**
+ * A Deposit Handle
+ */
+struct TALER_MINT_DepositHandle;
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a deposit
+ * permission object to a mint
+ *
+ * @param cls closure
+ * @param status 1 for successful deposit, 2 for retry, 0 for failure
+ * @param obj the received JSON object; can be NULL if it cannot be constructed
+ * from the reply
+ * @param emsg in case of unsuccessful deposit, this contains a human readable
+ * explanation.
+ */
+typedef void (*TALER_MINT_DepositResultCallback) (void *cls,
+ int status,
+ json_t *obj,
+ char *emsg);
+
+/**
+ * Submit a deposit permission to the mint and get the mint's response
+ *
+ * @param mint the mint handle
+ * @param cb the callback to call when a reply for this request is available
+ * @param cls closure for the above callback
+ * @param deposit_obj the deposit permission received from the customer along
+ * with the wireformat JSON object
+ * @return a handle for this request; NULL if the JSON object could not be
+ * parsed or is of incorrect format or any other error. In this case,
+ * the callback is not called.
+ */
+struct TALER_MINT_DepositHandle *
+TALER_MINT_deposit_submit_json (struct TALER_MINT_Handle *mint,
+ TALER_MINT_DepositResultCallback cb,
+ void *cls,
+ json_t *deposit_obj);
+
+
+#if 0
+/**
+ * Submit a deposit permission to the mint and get the mint's response.
+ *
+ * @param mint the mint handle
+ * @param cb the callback to call when a reply for this request is available
+ * @param cls closure for the above callback
+ * @param coin the public key of the coin
+ * @param denom_key denomination key of the mint which is used to blind-sign the
+ * coin
+ * @param ubsig the mint's unblinded signature
+ * @param transaction_id transaction identifier
+ * @param amount the amount to deposit
+ * @param merchant_pub the public key of the merchant
+ * @param h_contract hash of the contract
+ * @param h_wire hash of the wire format used
+ * @param csig signature of the coin over the transaction_id, amount,
+ * merchant_pub, h_contract and, h_wire
+ * @param wire_obj the wireformat object corresponding to h_wire
+ * @return a handle for this request
+ */
+struct TALER_MINT_DepositHandle *
+TALER_MINT_deposit_submit_json_ (struct TALER_MINT_Handle *mint,
+ TALER_MINT_DepositResultCallback *cb,
+ void *cls,
+ struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub,
+ struct TALER_BLIND_SigningPublicKey *denom_pub,
+ struct TALER_BLIND_Signature *ubsig,
+ uint64_t transaction_id,
+ struct TALER_Amount *amount,
+ struct GNUNET_CRYPTO_EddsaPublicKey *merchant_pub,
+ struct GNUNET_HashCode *h_contract,
+ struct GNUNET_HashCode *h_wire,
+ struct GNUNET_CRYPTO_EddsaSignature *csig,
+ json_t *wire_obj);
+#endif
+
+
+/**
+ * Cancel a deposit permission request. This function cannot be used on a
+ * request handle if a response is already served for it.
+ *
+ * @param the deposit permission request handle
+ */
+void
+TALER_MINT_deposit_submit_cancel (struct TALER_MINT_DepositHandle *deposit);
+
+#endif /* _TALER_MINT_SERVICE_H */
diff --git a/src/include/taler_rsa.h b/src/include/taler_rsa.h
new file mode 100644
index 00000000..1ed53001
--- /dev/null
+++ b/src/include/taler_rsa.h
@@ -0,0 +1,357 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/taler_rsa.h
+ * @brief RSA key management utilities. Some code is taken from gnunet-0.9.5a
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ *
+ * Authors of the gnunet code:
+ * Christian Grothoff
+ * Krista Bennett
+ * Gerd Knorr <kraxel@bytesex.org>
+ * Ioana Patrascu
+ * Tzvetan Horozov
+ */
+
+#ifndef TALER_RSA_H
+#define TALER_RSA_H
+
+#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_crypto_lib.h>
+
+/**
+ * Length of an RSA KEY (n,e,len), 2048 bit (=256 octests) key n, 2 byte e
+ */
+#define TALER_RSA_KEY_LENGTH 258
+
+/**
+ * @brief Length of RSA encrypted data (2048 bit)
+ *
+ * We currently do not handle encryption of data
+ * that can not be done in a single call to the
+ * RSA methods (read: large chunks of data).
+ * We should never need that, as we can use
+ * the GNUNET_CRYPTO_hash for larger pieces of data for signing,
+ * and for encryption, we only need to encode sessionkeys!
+ */
+#define TALER_RSA_DATA_ENCODING_LENGTH 256
+
+/**
+ * The private information of an RSA key pair.
+ */
+struct TALER_RSA_PrivateKey;
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * GNUnet mandates a certain format for the encoding
+ * of private RSA key information that is provided
+ * by the RSA implementations. This format is used
+ * to serialize a private RSA key (typically when
+ * writing it to disk).
+ */
+struct TALER_RSA_PrivateKeyBinaryEncoded
+{
+ /**
+ * Total size of the structure, in bytes, in big-endian!
+ */
+ uint16_t len GNUNET_PACKED;
+ uint16_t sizen GNUNET_PACKED; /* in big-endian! */
+ uint16_t sizee GNUNET_PACKED; /* in big-endian! */
+ uint16_t sized GNUNET_PACKED; /* in big-endian! */
+ uint16_t sizep GNUNET_PACKED; /* in big-endian! */
+ uint16_t sizeq GNUNET_PACKED; /* in big-endian! */
+ uint16_t sizedmp1 GNUNET_PACKED; /* in big-endian! */
+ uint16_t sizedmq1 GNUNET_PACKED; /* in big-endian! */
+ /* followed by the actual values */
+};
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * @brief an RSA signature
+ */
+struct TALER_RSA_Signature
+{
+ unsigned char sig[TALER_RSA_DATA_ENCODING_LENGTH];
+};
+
+GNUNET_NETWORK_STRUCT_BEGIN
+/**
+ * @brief header of what an RSA signature signs
+ * this must be followed by "size - 8" bytes of
+ * the actual signed data
+ */
+struct TALER_RSA_SignaturePurpose
+{
+ /**
+ * How many bytes does this signature sign?
+ * (including this purpose header); in network
+ * byte order (!).
+ */
+ uint32_t size GNUNET_PACKED;
+
+ /**
+ * What does this signature vouch for? This
+ * must contain a GNUNET_SIGNATURE_PURPOSE_XXX
+ * constant (from gnunet_signatures.h). In
+ * network byte order!
+ */
+ uint32_t purpose GNUNET_PACKED;
+
+};
+
+
+struct TALER_RSA_BlindedSignaturePurpose
+{
+ unsigned char data[TALER_RSA_DATA_ENCODING_LENGTH];
+};
+
+
+/**
+ * @brief A public key.
+ */
+struct TALER_RSA_PublicKeyBinaryEncoded
+{
+ /**
+ * In big-endian, must be GNUNET_CRYPTO_RSA_KEY_LENGTH+4
+ */
+ uint16_t len GNUNET_PACKED;
+
+ /**
+ * Size of n in key; in big-endian!
+ */
+ uint16_t sizen GNUNET_PACKED;
+
+ /**
+ * The key itself, contains n followed by e.
+ */
+ unsigned char key[TALER_RSA_KEY_LENGTH];
+
+ /**
+ * Padding (must be 0)
+ */
+ uint16_t padding GNUNET_PACKED;
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+/**
+ * Create a new private key. Caller must free return value.
+ *
+ * @return fresh private key
+ */
+struct TALER_RSA_PrivateKey *
+TALER_RSA_key_create ();
+
+
+/**
+ * Free memory occupied by the private key.
+ *
+ * @param key pointer to the memory to free
+ */
+void
+TALER_RSA_key_free (struct TALER_RSA_PrivateKey *key);
+
+
+/**
+ * Encode the private key in a format suitable for
+ * storing it into a file.
+ * @return encoding of the private key
+ */
+struct TALER_RSA_PrivateKeyBinaryEncoded *
+TALER_RSA_encode_key (const struct TALER_RSA_PrivateKey *hostkey);
+
+
+/**
+ * Extract the public key of the given private key.
+ *
+ * @param priv the private key
+ * @param pub where to write the public key
+ */
+void
+TALER_RSA_key_get_public (const struct TALER_RSA_PrivateKey *priv,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pub);
+
+
+/**
+ * Decode the private key from the data-format back
+ * to the "normal", internal format.
+ *
+ * @param buf the buffer where the private key data is stored
+ * @param len the length of the data in 'buffer'
+ * @return NULL on error
+ */
+struct TALER_RSA_PrivateKey *
+TALER_RSA_decode_key (const char *buf, uint16_t len);
+
+
+/**
+ * Convert a public key to a string.
+ *
+ * @param pub key to convert
+ * @return string representing 'pub'
+ */
+char *
+TALER_RSA_public_key_to_string (const struct TALER_RSA_PublicKeyBinaryEncoded *pub);
+
+
+/**
+ * Convert a string representing a public key to a public key.
+ *
+ * @param enc encoded public key
+ * @param enclen number of bytes in enc (without 0-terminator)
+ * @param pub where to store the public key
+ * @return GNUNET_OK on success
+ */
+int
+TALER_RSA_public_key_from_string (const char *enc,
+ size_t enclen,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pub);
+
+
+/**
+ * Sign a given block.h
+ *
+ * @param key private key to use for the signing
+ * @param msg the message
+ * @param size the size of the message
+ * @param sig where to write the signature
+ * @return GNUNET_SYSERR on error, GNUNET_OK on success
+ */
+int
+TALER_RSA_sign (const struct TALER_RSA_PrivateKey *key,
+ const void *msg,
+ size_t size,
+ struct TALER_RSA_Signature *sig);
+
+
+/**
+ * Verify signature with the given hash.
+ *
+ * @param hash the hash code to verify against the signature
+ * @param sig signature that is being validated
+ * @param publicKey public key of the signer
+ * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid
+ */
+int
+TALER_RSA_hash_verify (const struct GNUNET_HashCode *hash,
+ const struct TALER_RSA_Signature *sig,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey);
+
+
+/**
+ * Verify signature on the given message
+ *
+ * @param msg the message
+ * @param size the size of the message
+ * @param sig signature that is being validated
+ * @param publicKey public key of the signer
+ * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid
+ */
+int
+TALER_RSA_verify (const void *msg, size_t size,
+ const struct TALER_RSA_Signature *sig,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey);
+
+/**
+ * Key used to blind a message
+ */
+struct TALER_RSA_BlindingKey;
+
+/**
+ * Create a blinding key
+ *
+ * @return the newly created blinding key
+ */
+struct TALER_RSA_BlindingKey *
+TALER_RSA_blinding_key_create ();
+
+
+/**
+ * Destroy a blinding key
+ *
+ * @param bkey the blinding key to destroy
+ */
+void
+TALER_RSA_blinding_key_destroy (struct TALER_RSA_BlindingKey *bkey);
+
+
+/**
+ * Binary encoding for TALER_RSA_BlindingKey
+ */
+struct TALER_RSA_BlindingKeyBinaryEncoded
+{
+ unsigned char data[TALER_RSA_DATA_ENCODING_LENGTH];
+};
+
+
+/**
+ * Encode a blinding key
+ *
+ * @param bkey the blinding key to encode
+ * @param bkey_enc where to store the encoded binary key
+ * @return #GNUNET_OK upon successful encoding; #GNUNET_SYSERR upon failure
+ */
+int
+TALER_RSA_blinding_key_encode (struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_BlindingKeyBinaryEncoded *bkey_enc);
+
+
+/**
+ * Decode a blinding key from its encoded form
+ *
+ * @param bkey_enc the encoded blinding key
+ * @return the decoded blinding key; NULL upon error
+ */
+struct TALER_RSA_BlindingKey *
+TALER_RSA_blinding_key_decode (struct TALER_RSA_BlindingKeyBinaryEncoded *bkey_enc);
+
+
+/**
+ * Blinds the given message with the given blinding key
+ *
+ * @param msg the message
+ * @param size the size of the message
+ * @param bkey the blinding key
+ * @param pkey the public key of the signer
+ * @return the blinding signature purpose; NULL upon any error
+ */
+struct TALER_RSA_BlindedSignaturePurpose *
+TALER_RSA_message_blind (const void *msg, size_t size,
+ struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pkey);
+
+
+/**
+ * Unblind a signature made on blinding signature purpose. The signature
+ * purpose should have been generated with TALER_RSA_message_blind() function.
+ *
+ * @param sig the signature made on the blinded signature purpose
+ * @param bkey the blinding key used to blind the signature purpose
+ * @param pkey the public key of the signer
+ * @return GNUNET_SYSERR upon error; GNUNET_OK upon success.
+ */
+int
+TALER_RSA_unblind (struct TALER_RSA_Signature *sig,
+ struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pkey);
+
+#endif /* TALER_RSA_H */
+
+/* end of include/taler_rsa.h */
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
new file mode 100644
index 00000000..8c142f61
--- /dev/null
+++ b/src/include/taler_signatures.h
@@ -0,0 +1,106 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-keyup.c
+ * @brief Update the mint's keys for coins and signatures,
+ * using the mint's offline master key.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#ifndef TALER_SIGNATURES_H
+#define TALER_SIGNATURES_H
+
+/**
+ * Purpose for signing public keys signed
+ * by the mint master key.
+ */
+#define TALER_SIGNATURE_MASTER_SIGNKEY 1
+
+/**
+ * Purpose for denomination keys signed
+ * by the mint master key.
+ */
+#define TALER_SIGNATURE_MASTER_DENOM 2
+
+/**
+ * Purpose for the state of a reserve,
+ * signed by the mint's signing key.
+ */
+#define TALER_SIGNATURE_RESERVE_STATUS 3
+
+/**
+ * Signature where the reserve key
+ * confirms a withdraw request.
+ */
+#define TALER_SIGNATURE_WITHDRAW 4
+
+/**
+ * Signature where the refresh session confirms
+ * the list of melted coins and requested denominations.
+ */
+#define TALER_SIGNATURE_REFRESH_MELT 5
+
+/**
+ * Signature where the refresh session confirms
+ * the commits.
+ */
+#define TALER_SIGNATURE_REFRESH_COMMIT 6
+
+/**
+ * Signature where the mint (current signing key)
+ * confirms the list of blind session keys.
+ */
+#define TALER_SIGNATURE_REFRESH_MELT_RESPONSE 7
+
+/**
+ * Signature where the mint (current signing key)
+ * confirms the no-reveal index for cut-and-choose.
+ */
+#define TALER_SIGNATURE_REFRESH_COMMIT_RESPONSE 8
+
+/**
+ * Signature where coins confirm that they want
+ * to be melted into a certain session.
+ */
+#define TALER_SIGNATURE_REFRESH_MELT_CONFIRM 9
+
+/***********************/
+/* Merchant signatures */
+/***********************/
+
+/**
+ * Signature where the merchant confirms a contract
+ */
+#define TALER_SIGNATURE_MERCHANT_CONTRACT 101
+
+/*********************/
+/* Wallet signatures */
+/*********************/
+
+/**
+ * Signature made by the wallet of a user to confirm a deposit permission
+ */
+#define TALER_SIGNATURE_DEPOSIT 201
+
+/**
+ * Signature made by the wallet of a user to confirm a incremental deposit permission
+ */
+#define TALER_SIGNATURE_INCREMENTAL_DEPOSIT 202
+
+#endif
+
diff --git a/src/include/taler_types.h b/src/include/taler_types.h
new file mode 100644
index 00000000..c6c2c020
--- /dev/null
+++ b/src/include/taler_types.h
@@ -0,0 +1,120 @@
+/**
+ * @file include/types.h
+ * @brief This files defines the various data and message types in TALER.
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Florian Dold
+ */
+
+#ifndef TYPES_H_
+#define TYPES_H_
+
+#include "taler_rsa.h"
+
+
+/**
+ * Public information about a coin.
+ */
+struct TALER_CoinPublicInfo
+{
+ /**
+ * The coin's public key.
+ */
+ struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub;
+
+ /*
+ * The public key signifying the coin's denomination.
+ */
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+
+ /**
+ * Signature over coin_pub by denom_pub.
+ */
+ struct TALER_RSA_Signature denom_sig;
+};
+
+
+/**
+ * Request to withdraw coins from a reserve.
+ */
+struct TALER_WithdrawRequest
+{
+ /**
+ * Signature over the rest of the message
+ * by the withdraw public key.
+ */
+ struct GNUNET_CRYPTO_EddsaSignature sig;
+
+ /**
+ * Purpose must be TALER_SIGNATURE_WITHDRAW.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Reserve public key.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
+
+ /**
+ * Denomination public key for the coin that is withdrawn.
+ */
+ struct TALER_RSA_PublicKeyBinaryEncoded denomination_pub;
+
+ /**
+ * Purpose containing coin's blinded public key.
+ */
+ struct TALER_RSA_BlindedSignaturePurpose coin_envelope;
+};
+
+
+
+/**
+ * Data type for messages
+ */
+struct TALER_MessageHeader
+{
+ /**
+ * The type of the message in Network-byte order (NBO)
+ */
+ uint16_t type;
+
+ /**
+ * The size of the message in NBO
+ */
+ uint16_t size;
+};
+
+/*****************/
+/* Message types */
+/*****************/
+
+/**
+ * The message type of a blind signature
+ */
+#define TALER_MSG_TYPE_BLINDED_SIGNATURE 1
+
+/**
+ * The message type of a blinded message
+ */
+#define TALER_MSG_TYPE_BLINDED_MESSAGE 2
+
+/**
+ * The message type of an unblinded signature
+ * @FIXME: Not currently used
+ */
+#define TALER_MSG_TYPE_UNBLINDED_SIGNATURE 3
+
+/**
+ * The type of a blinding residue message
+ * @FIXME: Not currently used
+ */
+#define TALER_MSG_TYPE_BLINDING_RESIDUE 4
+
+/**
+ * The type of a message containing the blinding factor
+ */
+#define TALER_MSG_TYPE_BLINDING_FACTOR 5
+
+
+#endif /* TYPES_H_ */
+
+/* end of include/types.h */
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
new file mode 100644
index 00000000..a8a7c201
--- /dev/null
+++ b/src/include/taler_util.h
@@ -0,0 +1,255 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/taler_util.h
+ * @brief Interface for common utility functions
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <gnunet/gnunet_util_lib.h>
+#include <gcrypt.h>
+
+/* Define logging functions */
+#define LOG_DEBUG(...) \
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
+
+#define LOG_WARNING(...) \
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, __VA_ARGS__)
+
+#define LOG_ERROR(...) \
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, __VA_ARGS__)
+
+
+/**
+ * Tests a given as assertion and if failed prints it as a warning with the
+ * given reason
+ *
+ * @param EXP the expression to test as assertion
+ * @param reason string to print as warning
+ */
+#define TALER_assert_as(EXP, reason) \
+ do { \
+ if (EXP) break; \
+ LOG_ERROR("%s at %s:%d\n", reason, __FILE__, __LINE__); \
+ abort(); \
+ } while(0)
+
+
+
+/**
+ * Log an error message at log-level 'level' that indicates
+ * a failure of the command 'cmd' with the message given
+ * by gcry_strerror(rc).
+ */
+#define LOG_GCRY_ERROR(cmd, rc) do { LOG_ERROR("`%s' failed at %s:%d with error: %s\n", cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0)
+
+
+#define TALER_gcry_ok(cmd) \
+ do {int rc; rc = cmd; if (!rc) break; LOG_ERROR("A Gcrypt call failed at %s:%d with error: %s\n", __FILE__, __LINE__, gcry_strerror(rc)); abort(); } while (0)
+
+
+#define TALER_CURRENCY_LEN 4
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct TALER_AmountNBO
+{
+ uint32_t value;
+ uint32_t fraction;
+ char currency[TALER_CURRENCY_LEN];
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+struct TALER_HashContext
+{
+ gcry_md_hd_t hd;
+};
+
+
+
+/**
+ * Representation of monetary value in a given currency.
+ */
+struct TALER_Amount
+{
+ /**
+ * Value (numerator of fraction)
+ */
+ uint32_t value;
+ /**
+ * Fraction (denominator of fraction)
+ */
+ uint32_t fraction;
+ /**
+ * Currency string, left adjusted and padded with zeros.
+ */
+ char currency[4];
+};
+
+
+/**
+ * Initialize Gcrypt library.
+ */
+void
+TALER_gcrypt_init();
+
+
+/**
+ * Generate a ECC private key.
+ *
+ * @return the s-expression representing the generated ECC private key; NULL
+ * upon error
+ */
+gcry_sexp_t
+TALER_genkey ();
+
+
+/**
+ * Parse denomination description, in the format "T : V : F".
+ *
+ * @param str denomination description
+ * @param denom denomination to write the result to
+ * @return GNUNET_OK if the string is a valid denomination specification,
+ * GNUNET_SYSERR if it is invalid.
+ */
+int
+TALER_string_to_amount (const char *str, struct TALER_Amount *denom);
+
+
+/**
+ * FIXME
+ */
+struct TALER_AmountNBO
+TALER_amount_hton (struct TALER_Amount d);
+
+
+/**
+ * FIXME
+ */
+struct TALER_Amount
+TALER_amount_ntoh (struct TALER_AmountNBO dn);
+
+/**
+ * Compare the value/fraction of two amounts. Does not compare the currency,
+ * i.e. comparing amounts with the same value and fraction but different
+ * currency would return 0.
+ *
+ * @param a1 first amount
+ * @param a2 second amount
+ * @return result of the comparison
+ */
+int
+TALER_amount_cmp (struct TALER_Amount a1, struct TALER_Amount a2);
+
+
+/**
+ * Perform saturating subtraction of amounts.
+ *
+ * @param a1 amount to subtract from
+ * @param a2 amount to subtract
+ * @return (a1-a2) or 0 if a2>=a1
+ */
+struct TALER_Amount
+TALER_amount_subtract (struct TALER_Amount a1, struct TALER_Amount a2);
+
+
+/**
+ * Perform saturating addition of amounts
+ *
+ * @param a1 first amount to add
+ * @param a2 second amount to add
+ * @return sum of a1 and a2
+ */
+struct TALER_Amount
+TALER_amount_add (struct TALER_Amount a1, struct TALER_Amount a2);
+
+
+/**
+ * Normalize the given amount.
+ *
+ * @param amout amount to normalize
+ * @return normalized amount
+ */
+struct TALER_Amount
+TALER_amount_normalize (struct TALER_Amount amount);
+
+
+/**
+ * Convert amount to string.
+ *
+ * @param amount amount to convert to string
+ * @return freshly allocated string representation
+ */
+char *
+TALER_amount_to_string (struct TALER_Amount amount);
+
+
+/**
+ * Return the base32crockford encoding of the given buffer.
+ *
+ * The returned string will be freshly allocated, and must be free'd
+ * with GNUNET_free.
+ *
+ * @param buffer with data
+ * @param size size of the buffer
+ * @return freshly allocated, null-terminated string
+ */
+char *
+TALER_data_to_string_alloc (const void *buf, size_t size);
+
+
+/**
+ * Get encoded binary data from a configuration.
+ *
+ * @return GNUNET_OK on success
+ * GNUNET_NO is the value does not exist
+ * GNUNET_SYSERR on encoding error
+ */
+int
+TALER_configuration_get_data (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section, const char *option,
+ void *buf, size_t buf_size);
+
+
+
+
+int
+TALER_refresh_decrypt (const void *input, size_t input_size, const struct GNUNET_HashCode *secret, void *result);
+
+int
+TALER_refresh_encrypt (const void *input, size_t input_size, const struct GNUNET_HashCode *secret, void *result);
+
+
+
+void
+TALER_hash_context_start (struct TALER_HashContext *hc);
+
+
+void
+TALER_hash_context_read (struct TALER_HashContext *hc, void *buf, size_t size);
+
+
+void
+TALER_hash_context_finish (struct TALER_HashContext *hc,
+ struct GNUNET_HashCode *r_hash);
+
+#endif
diff --git a/src/mint/.gitignore b/src/mint/.gitignore
new file mode 100644
index 00000000..a2e71d5d
--- /dev/null
+++ b/src/mint/.gitignore
@@ -0,0 +1,6 @@
+taler-mint-dbinit
+taler-mint-keycheck
+taler-mint-keyup
+taler-mint-pursemod
+taler-mint-reservemod
+taler-mint-httpd \ No newline at end of file
diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am
new file mode 100644
index 00000000..2ae15348
--- /dev/null
+++ b/src/mint/Makefile.am
@@ -0,0 +1,131 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS)
+
+lib_LTLIBRARIES = \
+ libtalermint.la \
+ libtalermintapi.la
+
+libtalermint_la_SOURCES = \
+ mint_common.c \
+ mint_db.c
+
+libtalermint_la_LIBADD = \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil \
+ -lpq
+
+libtalermint_la_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS) \
+ -version-info 0:0:0 \
+ -no-undefined
+
+libtalermintapi_la_SOURCES = \
+ mint_api.c
+
+libtalermintapi_la_LIBADD = \
+ -lgnunetutil \
+ -ljansson \
+ -lcurl
+
+libtalermintapi_la_LDFLAGS = \
+ -version-info 0:0:0 \
+ -no-undefined
+
+
+bin_PROGRAMS = \
+ taler-mint-keyup \
+ taler-mint-keycheck \
+ taler-mint-reservemod \
+ taler-mint-httpd \
+ taler-mint-dbinit
+
+taler_mint_keyup_SOURCES = \
+ taler-mint-keyup.c
+
+taler_mint_keyup_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/mint/libtalermint.la \
+ -lpq \
+ -lgnunetutil
+taler_mint_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+
+
+taler_mint_keycheck_SOURCES = \
+ taler-mint-keycheck.c
+
+taler_mint_keycheck_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/mint/libtalermint.la \
+ -lgnunetutil \
+ -lpq
+taler_mint_keycheck_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+
+taler_mint_reservemod_SOURCES = \
+ taler-mint-reservemod.c
+taler_mint_reservemod_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/mint/libtalermint.la \
+ -lpq \
+ -lgnunetutil
+taler_mint_reservemod_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS)
+
+taler_mint_httpd_SOURCES = \
+ taler-mint-httpd.c \
+ taler-mint-httpd_mhd.c \
+ taler-mint-httpd_keys.c \
+ taler-mint-httpd_deposit.c \
+ taler-mint-httpd_withdraw.c \
+ taler-mint-httpd_refresh.c
+taler_mint_httpd_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/mint/libtalermint.la \
+ -lpq \
+ -lmicrohttpd \
+ -ljansson \
+ -lgnunetutil \
+ -lpthread
+taler_mint_httpd_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS)
+
+
+taler_mint_dbinit_SOURCES = \
+ taler-mint-dbinit.c
+taler_mint_dbinit_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/mint/libtalermint.la \
+ -lpq \
+ -lgnunetutil
+taler_mint_dbinit_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+
+check_PROGRAMS = \
+ test-mint-api \
+ test-mint-deposits \
+ test-mint-common
+
+test_mint_api_SOURCES = test_mint_api.c
+test_mint_api_LDADD = \
+ libtalermintapi.la \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil \
+ -ljansson
+
+test_mint_deposits_SOURCES = \
+ test_mint_deposits.c
+test_mint_deposits_LDADD = \
+ libtalermint.la \
+ $(top_srcdir)/src/util/libtalerutil.la \
+ -lgnunetutil \
+ -lpq
+
+test_mint_common_SOURCES = \
+ test_mint_common.c
+test_mint_common_LDADD = \
+ libtalermint.la \
+ $(top_srcdir)/src/util/libtalerutil.la \
+ -lgnunetutil
diff --git a/src/mint/mint.h b/src/mint/mint.h
new file mode 100644
index 00000000..5adce03c
--- /dev/null
+++ b/src/mint/mint.h
@@ -0,0 +1,198 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler_mint.h
+ * @brief Common functionality for the mint
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#ifndef _MINT_H
+#define _MINT_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_common.h>
+#include <libpq-fe.h>
+#include "taler_util.h"
+#include "taler_rsa.h"
+
+#define DIR_SIGNKEYS "signkeys"
+#define DIR_DENOMKEYS "denomkeys"
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+
+/**
+ * FIXME
+ */
+struct TALER_MINT_SignKeyIssue
+{
+ struct GNUNET_CRYPTO_EddsaPrivateKey signkey_priv;
+ struct GNUNET_CRYPTO_EddsaSignature signature;
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_CRYPTO_EddsaPublicKey master_pub;
+ struct GNUNET_TIME_AbsoluteNBO start;
+ struct GNUNET_TIME_AbsoluteNBO expire;
+ struct GNUNET_CRYPTO_EddsaPublicKey signkey_pub;
+};
+
+struct TALER_MINT_DenomKeyIssue
+{
+ /**
+ * The private key of the denomination. Will be NULL if the private key is
+ * not available.
+ */
+ struct TALER_RSA_PrivateKey *denom_priv;
+ struct GNUNET_CRYPTO_EddsaSignature signature;
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_CRYPTO_EddsaPublicKey master;
+ struct GNUNET_TIME_AbsoluteNBO start;
+ struct GNUNET_TIME_AbsoluteNBO expire_withdraw;
+ struct GNUNET_TIME_AbsoluteNBO expire_spend;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ struct TALER_AmountNBO value;
+ struct TALER_AmountNBO fee_withdraw;
+ struct TALER_AmountNBO fee_deposit;
+ struct TALER_AmountNBO fee_refresh;
+};
+
+struct RefreshMeltSignatureBody
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_HashCode melt_hash;
+};
+
+struct RefreshCommitSignatureBody
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_HashCode commit_hash;
+};
+
+struct RefreshCommitResponseSignatureBody
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ uint16_t noreveal_index;
+};
+
+struct RefreshMeltResponseSignatureBody
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_HashCode melt_response_hash;
+};
+
+
+struct RefreshMeltConfirmSignRequestBody
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_CRYPTO_EddsaPublicKey session_pub;
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+
+
+/**
+ * Iterator for sign keys.
+ *
+ * @param cls closure
+ * @param ski the sign key issue
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+typedef int (*TALER_MINT_SignkeyIterator)(void *cls,
+ const struct TALER_MINT_SignKeyIssue *ski);
+
+/**
+ * Iterator for denomination keys.
+ *
+ * @param cls closure
+ * @param dki the denomination key issue
+ * @param alias coin alias
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+typedef int (*TALER_MINT_DenomkeyIterator)(void *cls,
+ const char *alias,
+ const struct TALER_MINT_DenomKeyIssue *dki);
+
+
+
+/**
+ * FIXME
+ */
+int
+TALER_MINT_signkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_SignkeyIterator it, void *cls);
+
+
+/**
+ * FIXME
+ */
+int
+TALER_MINT_denomkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_DenomkeyIterator it, void *cls);
+
+
+/**
+ * Exports a denomination key to the given file
+ *
+ * @param filename the file where to write the denomination key
+ * @param dki the denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure.
+ */
+int
+TALER_MINT_write_denom_key (const char *filename,
+ const struct TALER_MINT_DenomKeyIssue *dki);
+
+
+/**
+ * Import a denomination key from the given file
+ *
+ * @param filename the file to import the key from
+ * @param dki pointer to return the imported denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+int
+TALER_MINT_read_denom_key (const char *filename,
+ struct TALER_MINT_DenomKeyIssue *dki);
+
+
+/**
+ * Load the configuration for the mint in the given
+ * directory.
+ *
+ * @param mint_base_dir the mint's base directory
+ * @return the mint configuratin, or NULL on error
+ */
+struct GNUNET_CONFIGURATION_Handle *
+TALER_MINT_config_load (const char *mint_base_dir);
+
+
+int
+TALER_TALER_DB_extract_amount (PGresult *result, unsigned int row,
+ int indices[3], struct TALER_Amount *denom);
+
+int
+TALER_TALER_DB_extract_amount_nbo (PGresult *result, unsigned int row,
+ int indices[3], struct TALER_AmountNBO *denom_nbo);
+
+#endif /* _MINT_H */
+
diff --git a/src/mint/mint_api.c b/src/mint/mint_api.c
new file mode 100644
index 00000000..b8d42b27
--- /dev/null
+++ b/src/mint/mint_api.c
@@ -0,0 +1,1121 @@
+/*
+ This file is part of TALER
+ (C) 2014 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
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint/mint_api.c
+ * @brief Implementation of the client interface to mint's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_mint_service.h"
+#include "taler_signatures.h"
+#include "mint.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));
+
+
+
+/**
+ * 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)
+
+/**
+ * Failsafe flag
+ */
+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 *cls;
+
+ TALER_MINT_ContinuationCallback cont_cb;
+ void *cont_cls;
+};
+
+
+/**
+ * A handle to submit a deposit permission and get its status
+ */
+struct TALER_MINT_DepositHandle
+{
+ /**
+ *The connection to mint this request handle will use
+ */
+ struct TALER_MINT_Handle *mint;
+
+ /**
+ * The url for this handle
+ */
+ char *url;
+
+ TALER_MINT_DepositResultCallback cb;
+ void *cls;
+
+ char *json_enc;
+
+ struct curl_slist *headers;
+
+};
+
+
+/**
+ * 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 { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+
+
+static int
+parse_json_signkey (struct TALER_MINT_SigningPublicKey **_sign_key,
+ json_t *sign_key_obj,
+ struct GNUNET_CRYPTO_EddsaPublicKey *master_key)
+{
+ json_t *valid_from_obj;
+ json_t *valid_until_obj;
+ json_t *key_obj;
+ json_t *sig_obj;
+ const char *valid_from_enc;
+ const char *valid_until_enc;
+ const char *key_enc;
+ const char *sig_enc;
+ struct TALER_MINT_SigningPublicKey *sign_key;
+ struct TALER_MINT_SignKeyIssue sign_key_issue;
+ struct GNUNET_CRYPTO_EddsaSignature sig;
+ struct GNUNET_TIME_Absolute valid_from;
+ struct GNUNET_TIME_Absolute valid_until;
+
+ 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 == (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 == (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 (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)));
+ (void) 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));
+ sign_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNKEY);
+ sign_key_issue.purpose.size =
+ htonl (sizeof (sign_key_issue)
+ - offsetof (struct TALER_MINT_SignKeyIssue, purpose));
+ sign_key_issue.master_pub = *master_key;
+ sign_key_issue.start = GNUNET_TIME_absolute_hton (valid_from);
+ sign_key_issue.expire = GNUNET_TIME_absolute_hton (valid_until);
+ EXITIF (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNKEY,
+ &sign_key_issue.purpose,
+ &sig,
+ master_key));
+ 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;
+}
+
+static int
+parse_json_denomkey (struct TALER_MINT_DenomPublicKey **_denom_key,
+ json_t *denom_key_obj,
+ struct GNUNET_CRYPTO_EddsaPublicKey *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;
+ 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_MINT_DenomKeyIssue denom_key_issue;
+ 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 (52 != strlen (key_enc)); /* strlen(base32(char[32])) = 52 */
+ 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));
+
+ (void) memset (&denom_key_issue, 0, sizeof (denom_key_issue));
+ EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (key_enc, 52,
+ &denom_key_issue.denom_pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)));
+ 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_DENOM);
+ denom_key_issue.purpose.size = htonl
+ (sizeof (struct TALER_MINT_DenomKeyIssue) -
+ offsetof (struct TALER_MINT_DenomKeyIssue, 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);
+ denom_key_issue.value = TALER_amount_hton (value);
+ denom_key_issue.fee_withdraw = TALER_amount_hton (fee_withdraw);
+ denom_key_issue.fee_deposit = TALER_amount_hton (fee_deposit);
+ denom_key_issue.fee_refresh = TALER_amount_hton (fee_refresh);
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOM,
+ &denom_key_issue.purpose,
+ &sig,
+ master_key));
+ denom_key = GNUNET_new (struct TALER_MINT_DenomPublicKey);
+ denom_key->key = denom_key_issue.denom_pub;
+ 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 GNUNET_CRYPTO_EddsaPublicKey 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, "master_pub")));
+ 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));
+ }
+ {
+ /* 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;
+ const char *status_str;
+ json_error_t error;
+
+ status_str = NULL;
+ obj = NULL;
+ obj = json_loadb (buf, size,
+ JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK, &error);
+ if (NULL == obj)
+ {
+ JSON_WARN (error);
+ return GNUNET_SYSERR;
+ }
+ EXITIF (-1 == json_unpack (obj, "{s:s}", "status", &status_str));
+ LOG_DEBUG ("Received deposit response: %s from mint\n", status_str);
+ if (0 == strcmp ("DEPOSIT_OK", status_str))
+ *r_status = 1;
+ else if (0 == strcmp ("DEPOSIT_QUEUED", status_str))
+ *r_status = 2;
+ else
+ *r_status = 0;
+ *r_obj = obj;
+
+ return GNUNET_OK;
+ EXITIF_exit:
+ json_decref (obj);
+ return GNUNET_SYSERR;
+}
+
+#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->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->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->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 mint_key the 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,
+ struct GNUNET_CRYPTO_EddsaPublicKey *mint_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 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 *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->cls = 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
+ *
+ * @param mint the mint handle
+ * @param cb the callback to call when a reply for this request is available
+ * @param cls closure for the above callback
+ * @param deposit_obj the deposit permission received from the customer along
+ * with the wireformat JSON object
+ * @return a handle for this request; NULL if the JSON object could not be
+ * parsed or is of incorrect format or any other error. In this case,
+ * the callback is not called.
+ */
+struct TALER_MINT_DepositHandle *
+TALER_MINT_deposit_submit_json (struct TALER_MINT_Handle *mint,
+ TALER_MINT_DepositResultCallback cb,
+ void *cls,
+ json_t *deposit_obj)
+{
+ struct TALER_MINT_DepositHandle *dh;
+
+ GNUNET_assert (REQUEST_TYPE_NONE == mint->req_type);
+ dh = GNUNET_new (struct TALER_MINT_DepositHandle);
+ dh->mint = mint;
+ mint->req_type = REQUEST_TYPE_DEPOSIT;
+ mint->req.deposit = dh;
+ dh->cb = cb;
+ dh->cls = cls;
+ GNUNET_asprintf (&dh->url, "http://%s:%hu/deposit", mint->hostname, mint->port);
+ GNUNET_assert (NULL != (dh->json_enc = json_dumps (deposit_obj, JSON_COMPACT)));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_URL, dh->url));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_POSTFIELDS,
+ dh->json_enc));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_POSTFIELDSIZE,
+ strlen (dh->json_enc)));
+ GNUNET_assert (NULL != (dh->headers =
+ curl_slist_append (dh->headers, "Content-Type: application/json")));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (mint->curl, CURLOPT_HTTPHEADER, dh->headers));
+ if (GNUNET_NO == mint->connected)
+ mint_connect (mint);
+ perform_now (mint->ctx);
+ return dh;
+}
+
+
+/**
+ * Cancel a deposit permission request. This function cannot be used on a
+ * request handle if a response is already served for it.
+ *
+ * @param 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 ();
+}
diff --git a/src/mint/mint_common.c b/src/mint/mint_common.c
new file mode 100644
index 00000000..4afbf072
--- /dev/null
+++ b/src/mint/mint_common.c
@@ -0,0 +1,283 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint_common.c
+ * @brief Common functionality for the mint
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Sree Harsha Totakura
+ */
+
+#include "platform.h"
+#include "mint.h"
+
+struct SignkeysIterateContext
+{
+ TALER_MINT_SignkeyIterator it;
+ void *it_cls;
+};
+
+
+struct DenomkeysIterateContext
+{
+ const char *alias;
+ TALER_MINT_DenomkeyIterator it;
+ void *it_cls;
+};
+
+
+static int
+signkeys_iterate_dir_iter (void *cls,
+ const char *filename)
+{
+
+ struct SignkeysIterateContext *skc = cls;
+ ssize_t nread;
+ struct TALER_MINT_SignKeyIssue issue;
+ nread = GNUNET_DISK_fn_read (filename,
+ &issue,
+ sizeof (struct TALER_MINT_SignKeyIssue));
+ if (nread != sizeof (struct TALER_MINT_SignKeyIssue))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid signkey file: '%s'\n", filename);
+ return GNUNET_OK;
+ }
+ return skc->it (skc->it_cls, &issue);
+}
+
+
+int
+TALER_MINT_signkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_SignkeyIterator it, void *cls)
+{
+ char *signkey_dir;
+ size_t len;
+ struct SignkeysIterateContext skc;
+
+ len = GNUNET_asprintf (&signkey_dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS), mint_base_dir);
+ GNUNET_assert (len > 0);
+
+ skc.it = it;
+ skc.it_cls = cls;
+
+ return GNUNET_DISK_directory_scan (signkey_dir, &signkeys_iterate_dir_iter, &skc);
+}
+
+
+/**
+ * Import a denomination key from the given file
+ *
+ * @param filename the file to import the key from
+ * @param dki pointer to return the imported denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+int
+TALER_MINT_read_denom_key (const char *filename,
+ struct TALER_MINT_DenomKeyIssue *dki)
+{
+ uint64_t size;
+ size_t offset;
+ void *data;
+ struct TALER_RSA_PrivateKey *priv;
+ int ret;
+
+ ret = GNUNET_SYSERR;
+ data = NULL;
+ offset = sizeof (struct TALER_MINT_DenomKeyIssue)
+ - offsetof (struct TALER_MINT_DenomKeyIssue, signature);
+ if (GNUNET_OK != GNUNET_DISK_file_size (filename,
+ &size,
+ GNUNET_YES,
+ GNUNET_YES))
+ goto cleanup;
+ if (size <= offset)
+ {
+ GNUNET_break (0);
+ goto cleanup;
+ }
+ data = GNUNET_malloc (size);
+ if (size != GNUNET_DISK_fn_read (filename,
+ data,
+ size))
+ goto cleanup;
+ if (NULL == (priv = TALER_RSA_decode_key (data + offset, size - offset)))
+ goto cleanup;
+ dki->denom_priv = priv;
+ (void) memcpy (&dki->signature, data, offset);
+ ret = GNUNET_OK;
+
+ cleanup:
+ GNUNET_free_non_null (data);
+ return ret;
+}
+
+
+/**
+ * Exports a denomination key to the given file
+ *
+ * @param filename the file where to write the denomination key
+ * @param dki the denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure.
+ */
+int
+TALER_MINT_write_denom_key (const char *filename,
+ const struct TALER_MINT_DenomKeyIssue *dki)
+{
+ struct TALER_RSA_PrivateKeyBinaryEncoded *priv_enc;
+ struct GNUNET_DISK_FileHandle *fh;
+ ssize_t wrote;
+ size_t wsize;
+ int ret;
+
+ fh = NULL;
+ priv_enc = NULL;
+ ret = GNUNET_SYSERR;
+ if (NULL == (fh = GNUNET_DISK_file_open
+ (filename,
+ GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_TRUNCATE,
+ GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE)))
+ goto cleanup;
+ if (NULL == (priv_enc = TALER_RSA_encode_key (dki->denom_priv)))
+ goto cleanup;
+ wsize = sizeof (struct TALER_MINT_DenomKeyIssue)
+ - offsetof (struct TALER_MINT_DenomKeyIssue, signature);
+ if (GNUNET_SYSERR == (wrote = GNUNET_DISK_file_write (fh,
+ &dki->signature,
+ wsize)))
+ goto cleanup;
+ if (wrote != wsize)
+ goto cleanup;
+ wsize = ntohs (priv_enc->len);
+ if (GNUNET_SYSERR == (wrote = GNUNET_DISK_file_write (fh,
+ priv_enc,
+ wsize)))
+ goto cleanup;
+ if (wrote != wsize)
+ goto cleanup;
+ ret = GNUNET_OK;
+ cleanup:
+ GNUNET_free_non_null (priv_enc);
+ if (NULL != fh)
+ (void) GNUNET_DISK_file_close (fh);
+ return ret;
+}
+
+
+static int
+denomkeys_iterate_keydir_iter (void *cls,
+ const char *filename)
+{
+
+ struct DenomkeysIterateContext *dic = cls;
+ struct TALER_MINT_DenomKeyIssue issue;
+
+ if (GNUNET_OK != TALER_MINT_read_denom_key (filename, &issue))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid denomkey file: '%s'\n", filename);
+ return GNUNET_OK;
+ }
+ return dic->it (dic->it_cls, dic->alias, &issue);
+}
+
+
+static int
+denomkeys_iterate_topdir_iter (void *cls,
+ const char *filename)
+{
+
+ struct DenomkeysIterateContext *dic = cls;
+ dic->alias = GNUNET_STRINGS_get_short_name (filename);
+
+ // FIXME: differentiate between error case and normal iteration abortion
+ if (0 > GNUNET_DISK_directory_scan (filename, &denomkeys_iterate_keydir_iter, dic))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_denomkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_DenomkeyIterator it, void *cls)
+{
+ char *dir;
+ size_t len;
+ struct DenomkeysIterateContext dic;
+ len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_DENOMKEYS),
+ mint_base_dir);
+ GNUNET_assert (len > 0);
+
+ dic.it = it;
+ dic.it_cls = cls;
+
+ // scan over alias dirs
+ return GNUNET_DISK_directory_scan (dir, &denomkeys_iterate_topdir_iter, &dic);
+}
+
+
+struct GNUNET_CONFIGURATION_Handle *
+TALER_MINT_config_load (const char *mint_base_dir)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ char *cfg_dir;
+ int res;
+
+ res = GNUNET_asprintf (&cfg_dir, "%s" DIR_SEPARATOR_STR "config", mint_base_dir);
+ GNUNET_assert (res > 0);
+
+ cfg = GNUNET_CONFIGURATION_create ();
+ res = GNUNET_CONFIGURATION_load_from (cfg, cfg_dir);
+ GNUNET_free (cfg_dir);
+ if (GNUNET_OK != res)
+ return NULL;
+ return cfg;
+}
+
+int
+TALER_TALER_DB_extract_amount_nbo (PGresult *result, unsigned int row,
+ int indices[3], struct TALER_AmountNBO *denom_nbo)
+{
+ if ((indices[0] < 0) || (indices[1] < 0) || (indices[2] < 0))
+ return GNUNET_NO;
+ if (sizeof (uint32_t) != PQgetlength (result, row, indices[0]))
+ return GNUNET_SYSERR;
+ if (sizeof (uint32_t) != PQgetlength (result, row, indices[1]))
+ return GNUNET_SYSERR;
+ if (PQgetlength (result, row, indices[2]) > TALER_CURRENCY_LEN)
+ return GNUNET_SYSERR;
+ denom_nbo->value = *(uint32_t *) PQgetvalue (result, row, indices[0]);
+ denom_nbo->fraction = *(uint32_t *) PQgetvalue (result, row, indices[1]);
+ memset (denom_nbo->currency, 0, TALER_CURRENCY_LEN);
+ memcpy (denom_nbo->currency, PQgetvalue (result, row, indices[2]), PQgetlength (result, row, indices[2]));
+ return GNUNET_OK;
+}
+
+
+int
+TALER_TALER_DB_extract_amount (PGresult *result, unsigned int row,
+ int indices[3], struct TALER_Amount *denom)
+{
+ struct TALER_AmountNBO denom_nbo;
+ int res;
+
+ res = TALER_TALER_DB_extract_amount_nbo (result, row, indices, &denom_nbo);
+ if (GNUNET_OK != res)
+ return res;
+ *denom = TALER_amount_ntoh (denom_nbo);
+ return GNUNET_OK;
+}
+
+/* end of mint_common.c */
diff --git a/src/mint/mint_db.c b/src/mint/mint_db.c
new file mode 100644
index 00000000..6dc02587
--- /dev/null
+++ b/src/mint/mint_db.c
@@ -0,0 +1,1838 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint_db.c
+ * @brief Database access for the mint
+ * @author Florian Dold
+ */
+#include "platform.h"
+#include "taler_db_lib.h"
+#include "taler_signatures.h"
+#include "mint_db.h"
+#include "mint.h"
+#include <pthread.h>
+
+/**
+ * Thread-local database connection.
+ * Contains a pointer to PGconn or NULL.
+ */
+static pthread_key_t db_conn_threadlocal;
+
+
+/**
+ * Database connection string, as read from
+ * the configuration.
+ */
+static char *TALER_MINT_db_connection_cfg_str;
+
+
+#define break_db_err(result) do { \
+ GNUNET_break(0); \
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \
+ } while (0)
+
+/**
+ * Shorthand for exit jumps.
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+int
+TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn,
+ struct TALER_RSA_BlindedSignaturePurpose *blind_ev,
+ struct CollectableBlindcoin *collectable)
+{
+ PGresult *result;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (blind_ev),
+ TALER_DB_QUERY_PARAM_END
+ };
+ result = TALER_DB_exec_prepared (db_conn, "get_collectable_blindcoins", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("blind_ev_sig", &collectable->ev_sig),
+ TALER_DB_RESULT_SPEC("denom_pub", &collectable->denom_pub),
+ TALER_DB_RESULT_SPEC("reserve_sig", &collectable->reserve_sig),
+ TALER_DB_RESULT_SPEC("reserve_pub", &collectable->reserve_pub),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ (void) memcpy (&collectable->ev, blind_ev, sizeof (struct TALER_RSA_BlindedSignaturePurpose));
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn,
+ const struct CollectableBlindcoin *collectable)
+{
+ PGresult *result;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (&collectable->ev),
+ TALER_DB_QUERY_PARAM_PTR (&collectable->ev_sig),
+ TALER_DB_QUERY_PARAM_PTR (&collectable->denom_pub),
+ TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_pub),
+ TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_sig),
+ TALER_DB_QUERY_PARAM_END
+ };
+ result = TALER_DB_exec_prepared (db_conn, "insert_collectable_blindcoins", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Insert failed (updated '%s' tupes instead of '1')\n",
+ PQcmdTuples (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_get_reserve (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub,
+ struct Reserve *reserve)
+{
+ PGresult *result;
+ int res;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (reserve_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ result = TALER_DB_exec_prepared (db_conn, "get_reserve", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ reserve->reserve_pub = *reserve_pub;
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("status_sig", &reserve->status_sig),
+ TALER_DB_RESULT_SPEC("status_sign_pub", &reserve->status_sign_pub),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ res = TALER_DB_extract_result (result, rs, 0);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ {
+ int fnums[] = {
+ PQfnumber (result, "balance_value"),
+ PQfnumber (result, "balance_fraction"),
+ PQfnumber (result, "balance_currency"),
+ };
+ if (GNUNET_OK != TALER_TALER_DB_extract_amount_nbo (result, 0, fnums, &reserve->balance))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ /* FIXME: Add expiration?? */
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/* If fresh is GNUNET_YES, set some fields to NULL as they are not actually valid */
+int
+TALER_MINT_DB_update_reserve (PGconn *db_conn,
+ const struct Reserve *reserve,
+ int fresh)
+{
+ PGresult *result;
+ uint64_t stamp_sec;
+
+ stamp_sec = GNUNET_ntohll (GNUNET_TIME_absolute_ntoh (reserve->expiration).abs_value_us / 1000000);
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (&reserve->reserve_pub),
+ TALER_DB_QUERY_PARAM_PTR (&reserve->balance.value),
+ TALER_DB_QUERY_PARAM_PTR (&reserve->balance.fraction),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (&reserve->balance.currency,
+ strlen (reserve->balance.currency)),
+ TALER_DB_QUERY_PARAM_PTR (&reserve->status_sig),
+ TALER_DB_QUERY_PARAM_PTR (&reserve->status_sign_pub),
+ TALER_DB_QUERY_PARAM_PTR (&stamp_sec),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ /* set some fields to NULL if they are not actually valid */
+
+ if (GNUNET_YES == fresh)
+ {
+ unsigned i;
+ for (i = 4; i <= 7; i += 1)
+ {
+ params[i].data = NULL;
+ params[i].size = 0;
+ }
+ }
+
+ result = TALER_DB_exec_prepared (db_conn, "update_reserve", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Update failed (updated '%s' tupes instead of '1')\n",
+ PQcmdTuples (result));
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+
+int
+TALER_MINT_DB_prepare (PGconn *db_conn)
+{
+ PGresult *result;
+
+ result = PQprepare (db_conn, "get_reserve",
+ "SELECT "
+ " balance_value, balance_fraction, balance_currency "
+ ",expiration_date, blind_session_pub, blind_session_priv"
+ ",status_sig, status_sign_pub "
+ "FROM reserves "
+ "WHERE reserve_pub=$1 "
+ "LIMIT 1; ",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "update_reserve",
+ "UPDATE reserves "
+ "SET"
+ " balance_value=$2 "
+ ",balance_fraction=$3 "
+ ",balance_currency=$4 "
+ ",status_sig=$5 "
+ ",status_sign_pub=$6 "
+ ",expiration_date=$7 "
+ "WHERE reserve_pub=$1 ",
+ 9, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ result = PQprepare (db_conn, "insert_collectable_blindcoins",
+ "INSERT INTO collectable_blindcoins ( "
+ " blind_ev, blind_ev_sig "
+ ",denom_pub, reserve_pub, reserve_sig) "
+ "VALUES ($1, $2, $3, $4, $5)",
+ 6, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_collectable_blindcoins",
+ "SELECT "
+ "blind_ev_sig, denom_pub, reserve_sig, reserve_pub "
+ "FROM collectable_blindcoins "
+ "WHERE blind_ev = $1",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_reserve_order",
+ "SELECT "
+ " blind_ev, blind_ev_sig, denom_pub, reserve_sig, reserve_pub "
+ "FROM collectable_blindcoins "
+ "WHERE blind_session_pub = $1",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ /* FIXME: does it make sense to store these computed values in the DB? */
+ result = PQprepare (db_conn, "get_refresh_session",
+ "SELECT "
+ " (SELECT count(*) FROM refresh_melt WHERE session_pub = $1)::INT2 as num_oldcoins "
+ ",(SELECT count(*) FROM refresh_blind_session_keys "
+ " WHERE session_pub = $1 and cnc_index = 0)::INT2 as num_newcoins "
+ ",(SELECT count(*) FROM refresh_blind_session_keys "
+ " WHERE session_pub = $1 and newcoin_index = 0)::INT2 as kappa "
+ ",noreveal_index"
+ ",session_commit_sig "
+ ",reveal_ok "
+ "FROM refresh_sessions "
+ "WHERE session_pub = $1",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_known_coin",
+ "SELECT "
+ " coin_pub, denom_pub, denom_sig "
+ ",expended_value, expended_fraction, expended_currency "
+ ",refresh_session_pub "
+ "FROM known_coins "
+ "WHERE coin_pub = $1",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "update_known_coin",
+ "UPDATE known_coins "
+ "SET "
+ " denom_pub = $2 "
+ ",denom_sig = $3 "
+ ",expended_value = $4 "
+ ",expended_fraction = $5 "
+ ",expended_currency = $6 "
+ ",refresh_session_pub = $7 "
+ "WHERE "
+ " coin_pub = $1 ",
+ 7, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_known_coin",
+ "INSERT INTO known_coins ("
+ " coin_pub"
+ ",denom_pub"
+ ",denom_sig"
+ ",expended_value"
+ ",expended_fraction"
+ ",expended_currency"
+ ",refresh_session_pub"
+ ")"
+ "VALUES ($1,$2,$3,$4,$5,$6,$7)",
+ 7, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_refresh_commit_link",
+ "SELECT "
+ " transfer_pub "
+ ",link_secret_enc "
+ "FROM refresh_commit_link "
+ "WHERE session_pub = $1 AND cnc_index = $2 AND oldcoin_index = $3",
+ 3, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_refresh_commit_coin",
+ "SELECT "
+ " link_vector_enc "
+ ",coin_ev "
+ "FROM refresh_commit_coin "
+ "WHERE session_pub = $1 AND cnc_index = $2 AND newcoin_index = $3",
+ 3, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_order",
+ "INSERT INTO refresh_order ( "
+ " newcoin_index "
+ ",session_pub "
+ ",denom_pub "
+ ") "
+ "VALUES ($1, $2, $3) ",
+ 3, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_melt",
+ "INSERT INTO refresh_melt ( "
+ " session_pub "
+ ",oldcoin_index "
+ ",coin_pub "
+ ",denom_pub "
+ ") "
+ "VALUES ($1, $2, $3, $4) ",
+ 3, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_refresh_order",
+ "SELECT denom_pub "
+ "FROM refresh_order "
+ "WHERE session_pub = $1 AND newcoin_index = $2",
+ 2, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_refresh_collectable",
+ "SELECT ev_sig "
+ "FROM refresh_collectable "
+ "WHERE session_pub = $1 AND newcoin_index = $2",
+ 2, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_refresh_melt",
+ "SELECT coin_pub "
+ "FROM refresh_melt "
+ "WHERE session_pub = $1 AND oldcoin_index = $2",
+ 2, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_session",
+ "INSERT INTO refresh_sessions ( "
+ " session_pub "
+ ",noreveal_index "
+ ") "
+ "VALUES ($1, $2) ",
+ 2, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_commit_link",
+ "INSERT INTO refresh_commit_link ( "
+ " session_pub "
+ ",transfer_pub "
+ ",cnc_index "
+ ",oldcoin_index "
+ ",link_secret_enc "
+ ") "
+ "VALUES ($1, $2, $3, $4, $5) ",
+ 5, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_commit_coin",
+ "INSERT INTO refresh_commit_coin ( "
+ " session_pub "
+ ",coin_ev "
+ ",cnc_index "
+ ",newcoin_index "
+ ",link_vector_enc "
+ ") "
+ "VALUES ($1, $2, $3, $4, $5) ",
+ 5, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "insert_refresh_collectable",
+ "INSERT INTO refresh_collectable ( "
+ " session_pub "
+ ",newcoin_index "
+ ",ev_sig "
+ ") "
+ "VALUES ($1, $2, $3) ",
+ 3, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "set_reveal_ok",
+ "UPDATE refresh_sessions "
+ "SET reveal_ok = TRUE "
+ "WHERE session_pub = $1 ",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_link",
+ "SELECT link_vector_enc, ro.denom_pub, ev_sig "
+ "FROM refresh_melt rm "
+ " JOIN refresh_order ro USING (session_pub) "
+ " JOIN refresh_commit_coin rcc USING (session_pub) "
+ " JOIN refresh_sessions rs USING (session_pub) "
+ " JOIN refresh_collectable rc USING (session_pub) "
+ "WHERE rm.coin_pub = $1 "
+ "AND ro.newcoin_index = rcc.newcoin_index "
+ "AND ro.newcoin_index = rc.newcoin_index "
+ "AND rcc.cnc_index = rs.noreveal_index % ( "
+ " SELECT count(*) FROM refresh_commit_coin rcc2 "
+ " WHERE rcc2.newcoin_index = 0 AND rcc2.session_pub = rs.session_pub "
+ " ) ",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_transfer",
+ "SELECT transfer_pub, link_secret_enc "
+ "FROM refresh_melt rm "
+ " JOIN refresh_commit_link rcl USING (session_pub) "
+ " JOIN refresh_sessions rs USING (session_pub) "
+ "WHERE rm.coin_pub = $1 "
+ "AND rm.oldcoin_index = rcl.oldcoin_index "
+ "AND rcl.cnc_index = rs.noreveal_index % ( "
+ " SELECT count(*) FROM refresh_commit_coin rcc2 "
+ " WHERE newcoin_index = 0 AND rcc2.session_pub = rm.session_pub "
+ " ) ",
+ 1, NULL);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ if (GNUNET_OK != TALER_MINT_DB_prepare_deposits (db_conn))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Roll back the current transaction of a database connection.
+ *
+ * @param db_conn the database connection
+ * @return GNUNET_OK on success
+ */
+int
+TALER_MINT_DB_rollback (PGconn *db_conn)
+{
+ PGresult *result;
+
+ result = PQexec(db_conn, "ROLLBACK");
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Roll back the current transaction of a database connection.
+ *
+ * @param db_conn the database connection
+ * @return GNUNET_OK on success
+ */
+int
+TALER_MINT_DB_commit (PGconn *db_conn)
+{
+ PGresult *result;
+
+ result = PQexec(db_conn, "COMMIT");
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Start a transaction.
+ *
+ * @param db_conn the database connection
+ * @return GNUNET_OK on success
+ */
+int
+TALER_MINT_DB_transaction (PGconn *db_conn)
+{
+ PGresult *result;
+
+ result = PQexec(db_conn, "BEGIN");
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Can't start transaction: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Insert a refresh order into the database.
+ */
+int
+TALER_MINT_DB_insert_refresh_order (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub)
+{
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(denom_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_order", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_get_refresh_session (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ struct RefreshSession *session)
+{
+ int res;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_session", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ return GNUNET_NO;
+
+ GNUNET_assert (1 == PQntuples (result));
+
+ /* We're done if the caller is only interested in
+ * whether the session exists or not */
+
+ if (NULL == session)
+ return GNUNET_YES;
+
+ memset (session, 0, sizeof (struct RefreshSession));
+
+ session->session_pub = *refresh_session_pub;
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("num_oldcoins", &session->num_oldcoins),
+ TALER_DB_RESULT_SPEC("num_newcoins", &session->num_newcoins),
+ TALER_DB_RESULT_SPEC("kappa", &session->kappa),
+ TALER_DB_RESULT_SPEC("noreveal_index", &session->noreveal_index),
+ TALER_DB_RESULT_SPEC("reveal_ok", &session->reveal_ok),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ res = TALER_DB_extract_result (result, rs, 0);
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (TALER_DB_field_isnull (result, 0, "session_commit_sig"))
+ session->has_commit_sig = GNUNET_NO;
+ else
+ session->has_commit_sig = GNUNET_YES;
+
+ session->num_oldcoins = ntohs (session->num_oldcoins);
+ session->num_newcoins = ntohs (session->num_newcoins);
+ session->kappa = ntohs (session->kappa);
+ session->noreveal_index = ntohs (session->noreveal_index);
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+int
+TALER_MINT_DB_get_known_coin (PGconn *db_conn, struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ struct KnownCoin *known_coin)
+{
+ int res;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(coin_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_known_coin", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ return GNUNET_NO;
+
+ GNUNET_assert (1 == PQntuples (result));
+
+ /* extract basic information about the known coin */
+
+ {
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("coin_pub", &known_coin->public_info.coin_pub),
+ TALER_DB_RESULT_SPEC("denom_pub", &known_coin->public_info.denom_pub),
+ TALER_DB_RESULT_SPEC("denom_sig", &known_coin->public_info.denom_sig),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != (res = TALER_DB_extract_result (result, rs, 0)))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ /* extract the expended amount of the coin */
+
+ if (GNUNET_OK != TALER_DB_extract_amount (result, 0,
+ "expended_value",
+ "expended_fraction",
+ "expended_currency",
+ &known_coin->expended_balance))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ /* extract the refresh session of the coin or mark it as missing */
+
+ {
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("refresh_session_pub", &known_coin->refresh_session_pub),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_SYSERR == (res = TALER_DB_extract_result (result, rs, 0)))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_NO == res)
+ {
+ known_coin->is_refreshed = GNUNET_NO;
+ memset (&known_coin->refresh_session_pub, 0, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ }
+ else
+ {
+ known_coin->is_refreshed = GNUNET_YES;
+ }
+ }
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+int
+TALER_MINT_DB_create_refresh_session (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub)
+{
+ uint16_t noreveal_index;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&noreveal_index),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1<<15);
+ noreveal_index = htonl (noreveal_index);
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_session", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_set_commit_signature (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct GNUNET_CRYPTO_EddsaSignature *commit_sig)
+{
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+}
+
+
+int
+TALER_MINT_DB_set_reveal_ok (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub)
+{
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "set_reveal_ok", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_update_known_coin (PGconn *db_conn,
+ const struct KnownCoin *known_coin)
+{
+ struct TALER_AmountNBO expended_nbo = TALER_amount_hton (known_coin->expended_balance);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.coin_pub),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_pub),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_sig),
+ TALER_DB_QUERY_PARAM_PTR(&expended_nbo.value),
+ TALER_DB_QUERY_PARAM_PTR(&expended_nbo.fraction),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(expended_nbo.currency, strlen (expended_nbo.currency)),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->refresh_session_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ if (GNUNET_NO == known_coin->is_refreshed)
+ {
+ // Mind the magic index!
+ params[6].data = NULL;
+ params[6].size = 0;
+ }
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "update_known_coin", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ PQclear (result);
+ // return 'no' here (don't fail) so that we can
+ // insert if update fails (=> "upsert")
+ return GNUNET_NO;
+ }
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+int
+TALER_MINT_DB_insert_known_coin (PGconn *db_conn,
+ const struct KnownCoin *known_coin)
+{
+ struct TALER_AmountNBO expended_nbo = TALER_amount_hton (known_coin->expended_balance);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.coin_pub),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_pub),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_sig),
+ TALER_DB_QUERY_PARAM_PTR(&expended_nbo.value),
+ TALER_DB_QUERY_PARAM_PTR(&expended_nbo.fraction),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(&expended_nbo.currency, strlen (expended_nbo.currency)),
+ TALER_DB_QUERY_PARAM_PTR(&known_coin->refresh_session_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ if (GNUNET_NO == known_coin->is_refreshed)
+ {
+ // Mind the magic index!
+ params[6].data = NULL;
+ params[6].size = 0;
+ }
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_known_coin", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ PQclear (result);
+ // return 'no' here (don't fail) so that we can
+ // update if insert fails (=> "upsert")
+ return GNUNET_NO;
+ }
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+int
+TALER_MINT_DB_upsert_known_coin (PGconn *db_conn, struct KnownCoin *known_coin)
+{
+ int ret;
+ ret = TALER_MINT_DB_update_known_coin (db_conn, known_coin);
+ if (GNUNET_SYSERR == ret)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_YES == ret)
+ return GNUNET_YES;
+ return TALER_MINT_DB_insert_known_coin (db_conn, known_coin);
+}
+
+
+int
+TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, struct RefreshCommitLink *commit_link)
+{
+ uint16_t cnc_index_nbo = htons (commit_link->cnc_index);
+ uint16_t oldcoin_index_nbo = htons (commit_link->oldcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(&commit_link->session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&commit_link->transfer_pub),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(&commit_link->shared_secret_enc, sizeof (struct GNUNET_HashCode)),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_commit_link", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, struct RefreshCommitCoin *commit_coin)
+{
+ uint16_t cnc_index_nbo = htons (commit_coin->cnc_index);
+ uint16_t newcoin_index_nbo = htons (commit_coin->newcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(&commit_coin->session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&commit_coin->coin_ev),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(&commit_coin->link_enc, sizeof (struct LinkData)),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_commit_coin", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ int cnc_index, int oldcoin_index,
+ struct RefreshCommitLink *cc)
+{
+ uint16_t cnc_index_nbo = htons (cnc_index);
+ uint16_t oldcoin_index_nbo = htons (oldcoin_index);
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ cc->cnc_index = cnc_index;
+ cc->oldcoin_index = oldcoin_index;
+ cc->session_pub = *refresh_session_pub;
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_commit_link", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("transfer_pub", &cc->transfer_pub),
+ TALER_DB_RESULT_SPEC_SIZED("link_secret_enc", &cc->shared_secret_enc,
+ TALER_REFRESH_SHARED_SECRET_LENGTH),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_YES != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_free (cc);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+int
+TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ int cnc_index, int newcoin_index,
+ struct RefreshCommitCoin *cc)
+{
+ uint16_t cnc_index_nbo = htons (cnc_index);
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+
+ cc->cnc_index = cnc_index;
+ cc->newcoin_index = newcoin_index;
+ cc->session_pub = *refresh_session_pub;
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_commit_coin", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("coin_ev", &cc->coin_ev),
+ TALER_DB_RESULT_SPEC_SIZED("link_vector_enc", &cc->link_enc,
+ TALER_REFRESH_LINK_LENGTH),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_YES != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+int
+TALER_MINT_DB_get_refresh_order (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub)
+{
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_order", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ GNUNET_assert (1 == PQntuples (result));
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("denom_pub", denom_pub),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct TALER_RSA_Signature *ev_sig)
+{
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(ev_sig),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_collectable", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_get_refresh_collectable (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ struct TALER_RSA_Signature *ev_sig)
+{
+
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_collectable", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ GNUNET_assert (1 == PQntuples (result));
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("ev_sig", ev_sig),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+
+int
+TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t oldcoin_index,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub)
+{
+ uint16_t oldcoin_index_nbo = htons (oldcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(coin_pub),
+ TALER_DB_QUERY_PARAM_PTR(denom_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_melt", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+
+int
+TALER_MINT_DB_get_refresh_melt (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t oldcoin_index,
+ struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub)
+{
+ uint16_t oldcoin_index_nbo = htons (oldcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_melt", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ GNUNET_assert (1 == PQntuples (result));
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("coin_pub", coin_pub),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_db_get_link (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ LinkIterator link_iter,
+ void *cls)
+{
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(coin_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_link", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+
+ int i = 0;
+ int res;
+
+ for (i = 0; i < PQntuples (result); i++)
+ {
+ struct LinkDataEnc link_data_enc;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ struct TALER_RSA_Signature ev_sig;
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("link_vector_enc", &link_data_enc),
+ TALER_DB_RESULT_SPEC("denom_pub", &denom_pub),
+ TALER_DB_RESULT_SPEC("ev_sig", &ev_sig),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, i))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != (res = link_iter (cls, &link_data_enc, &denom_pub, &ev_sig)))
+ {
+ GNUNET_assert (GNUNET_SYSERR != res);
+ PQclear (result);
+ return res;
+ }
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_db_get_transfer (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub,
+ struct SharedSecretEnc *shared_secret_enc)
+{
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(coin_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (db_conn, "get_transfer", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ if (1 != PQntuples (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "got %d tuples for get_transfer\n", PQntuples (result));
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("transfer_pub", transfer_pub),
+ TALER_DB_RESULT_SPEC("link_secret_enc", shared_secret_enc),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+int
+TALER_MINT_DB_init_deposits (PGconn *conn, int tmp)
+{
+ const char *tmp_str = (1 == tmp) ? "TEMPORARY" : "";
+ char *sql;
+ PGresult *res;
+ int ret;
+
+ res = NULL;
+ (void) GNUNET_asprintf (&sql,
+ "CREATE %1$s TABLE IF NOT EXISTS deposits ("
+ " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)"
+ ",denom_pub BYTEA NOT NULL CHECK (length(denom_pub)=32)"
+ ",transaction_id INT8 NOT NULL"
+ ",amount_value INT4 NOT NULL"
+ ",amount_fraction INT4 NOT NULL"
+ ",amount_currency VARCHAR(4) NOT NULL"
+ ",merchant_pub BYTEA NOT NULL"
+ ",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)"
+ ",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)"
+ ",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)"
+ ",wire TEXT NOT NULL"
+ ")",
+ tmp_str);
+ res = PQexec (conn, sql);
+ GNUNET_free (sql);
+ if (PGRES_COMMAND_OK != PQresultStatus (res))
+ {
+ break_db_err (res);
+ ret = GNUNET_SYSERR;
+ }
+ else
+ ret = GNUNET_OK;
+ PQclear (res);
+ return ret;
+}
+
+int
+TALER_MINT_DB_prepare_deposits (PGconn *db_conn)
+{
+ PGresult *result;
+
+ result = PQprepare (db_conn, "insert_deposit",
+ "INSERT INTO deposits ("
+ "coin_pub,"
+ "denom_pub,"
+ "transaction_id,"
+ "amount_value,"
+ "amount_fraction,"
+ "amount_currency,"
+ "merchant_pub,"
+ "h_contract,"
+ "h_wire,"
+ "coin_sig,"
+ "wire"
+ ") VALUES ("
+ "$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11"
+ ")",
+ 11, NULL);
+ EXITIF (PGRES_COMMAND_OK != PQresultStatus(result));
+ PQclear (result);
+
+ result = PQprepare (db_conn, "get_deposit",
+ "SELECT "
+ "coin_pub,"
+ "denom_pub,"
+ "transaction_id,"
+ "amount_value,"
+ "amount_fraction,"
+ "amount_currency,"
+ "merchant_pub,"
+ "h_contract,"
+ "h_wire,"
+ "coin_sig"
+ " FROM deposits WHERE ("
+ "coin_pub = $1"
+ ")",
+ 1, NULL);
+ EXITIF (PGRES_COMMAND_OK != PQresultStatus(result));
+ PQclear (result);
+
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+}
+
+
+int
+TALER_MINT_DB_insert_deposit (PGconn *db_conn,
+ const struct Deposit *deposit)
+{
+ struct TALER_DB_QueryParam params[]= {
+ TALER_DB_QUERY_PARAM_PTR (&deposit->coin_pub),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->denom_pub),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->transaction_id),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->amount.value),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->amount.fraction),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->amount.currency, strlen (deposit->amount.currency)),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->merchant_pub),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->h_contract),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->h_wire),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->coin_sig),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->wire, strlen(deposit->wire)),
+ TALER_DB_QUERY_PARAM_END
+ };
+ PGresult *result;
+
+ result = TALER_DB_exec_prepared (db_conn, "insert_deposit", params);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+int
+TALER_MINT_DB_get_deposit (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub,
+ struct Deposit **r_deposit)
+{
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (coin_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+ PGresult *result;
+ struct Deposit *deposit;
+
+ deposit = NULL;
+ result = TALER_DB_exec_prepared (db_conn, "get_deposit", params);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ break_db_err (result);
+ goto EXITIF_exit;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ if (1 != PQntuples (result))
+ {
+ GNUNET_break (0);
+ goto EXITIF_exit;
+ }
+
+ {
+ deposit = GNUNET_malloc (sizeof (struct Deposit)); /* Without wire data */
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC ("coin_pub", &deposit->coin_pub),
+ TALER_DB_RESULT_SPEC ("denom_pub", &deposit->denom_pub),
+ TALER_DB_RESULT_SPEC ("coin_sig", &deposit->coin_sig),
+ TALER_DB_RESULT_SPEC ("transaction_id", &deposit->transaction_id),
+ TALER_DB_RESULT_SPEC ("merchant_pub", &deposit->merchant_pub),
+ TALER_DB_RESULT_SPEC ("h_contract", &deposit->h_contract),
+ TALER_DB_RESULT_SPEC ("h_wire", &deposit->h_wire),
+ TALER_DB_RESULT_SPEC_END
+ };
+ EXITIF (GNUNET_OK != TALER_DB_extract_result (result, rs, 0));
+ EXITIF (GNUNET_OK != TALER_DB_extract_amount_nbo (result, 0,
+ "amount_value",
+ "amount_fraction",
+ "amount_currency",
+ &deposit->amount));
+ deposit->purpose.purpose = htonl (TALER_SIGNATURE_DEPOSIT);
+ deposit->purpose.size = htonl (sizeof (struct Deposit)
+ - offsetof (struct Deposit, purpose));
+ }
+
+ PQclear (result);
+ *r_deposit = deposit;
+ return GNUNET_OK;
+
+EXITIF_exit:
+ PQclear (result);
+ GNUNET_free_non_null (deposit);
+ deposit = NULL;
+ return GNUNET_SYSERR;
+}
+
+
+
+/**
+ * Get the thread-local database-handle.
+ * Connect to the db if the connection does not exist yet.
+ *
+ * @param the database connection, or NULL on error
+ */
+PGconn *
+TALER_MINT_DB_get_connection (void)
+{
+ PGconn *db_conn;
+
+ if (NULL != (db_conn = pthread_getspecific (db_conn_threadlocal)))
+ return db_conn;
+
+ db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str);
+
+ if (CONNECTION_OK != PQstatus (db_conn))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "db connection failed: %s\n",
+ PQerrorMessage (db_conn));
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_prepare (db_conn))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ if (0 != pthread_setspecific (db_conn_threadlocal, db_conn))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ return db_conn;
+}
+
+
+/**
+ * Close thread-local database connection when a thread is destroyed.
+ *
+ * @param closure we get from pthreads (the db handle)
+ */
+static void
+db_conn_destroy (void *cls)
+{
+ PGconn *db_conn = cls;
+ if (NULL != db_conn)
+ PQfinish (db_conn);
+}
+
+
+/**
+ * Initialize database subsystem.
+ *
+ * @param connection_cfg configuration to use to talk to DB
+ * @return GNUNET_OK on success
+ */
+int
+TALER_MINT_DB_init (const char *connection_cfg)
+{
+
+ if (0 != pthread_key_create (&db_conn_threadlocal, &db_conn_destroy))
+ {
+ fprintf (stderr,
+ "Can't create pthread key.\n");
+ return GNUNET_SYSERR;
+ }
+ TALER_MINT_db_connection_cfg_str = GNUNET_strdup (connection_cfg);
+ return GNUNET_OK;
+}
diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h
new file mode 100644
index 00000000..4f47aac1
--- /dev/null
+++ b/src/mint/mint_db.h
@@ -0,0 +1,344 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint/mint_db.h
+ * @brief Mint-specific database access
+ * @author Florian Dold
+ */
+
+#ifndef _NEURO_MINT_DB_H
+#define _NEURO_MINT_DB_H
+
+#include <libpq-fe.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
+#include "taler_types.h"
+#include "taler_rsa.h"
+
+
+/**
+ * Reserve row. Corresponds to table 'reserves' in
+ * the mint's database.
+ */
+struct Reserve
+{
+ /**
+ * Signature over the purse.
+ * Only valid if (blind_session_missing==GNUNET_YES).
+ */
+ struct GNUNET_CRYPTO_EddsaSignature status_sig;
+ /**
+ * Signature with purpose TALER_SIGNATURE_PURSE.
+ * Only valid if (blind_session_missing==GNUNET_YES).
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose status_sig_purpose;
+ /**
+ * Signing key used to sign the purse.
+ * Only valid if (blind_session_missing==GNUNET_YES).
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey status_sign_pub;
+ /**
+ * Withdraw public key, identifies the purse.
+ * Only the customer knows the corresponding private key.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
+ /**
+ * Remaining balance in the purse.
+ */
+ struct TALER_AmountNBO balance;
+
+ /**
+ * Expiration date for the purse.
+ */
+ struct GNUNET_TIME_AbsoluteNBO expiration;
+};
+
+
+struct CollectableBlindcoin
+{
+ struct TALER_RSA_BlindedSignaturePurpose ev;
+ struct TALER_RSA_Signature ev_sig;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
+ struct GNUNET_CRYPTO_EddsaSignature reserve_sig;
+};
+
+
+struct RefreshSession
+{
+ int has_commit_sig;
+ struct GNUNET_CRYPTO_EddsaSignature commit_sig;
+ struct GNUNET_CRYPTO_EddsaPublicKey session_pub;
+ uint16_t num_oldcoins;
+ uint16_t num_newcoins;
+ uint16_t kappa;
+ uint16_t noreveal_index;
+ uint8_t reveal_ok;
+};
+
+
+#define TALER_REFRESH_SHARED_SECRET_LENGTH (sizeof (struct GNUNET_HashCode))
+#define TALER_REFRESH_LINK_LENGTH (sizeof (struct LinkData))
+
+struct RefreshCommitLink
+{
+ struct GNUNET_CRYPTO_EddsaPublicKey session_pub;
+ struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub;
+ uint16_t cnc_index;
+ uint16_t oldcoin_index;
+ char shared_secret_enc[sizeof (struct GNUNET_HashCode)];
+};
+
+struct LinkData
+{
+ struct GNUNET_CRYPTO_EcdsaPrivateKey coin_priv;
+ struct TALER_RSA_BlindingKeyBinaryEncoded bkey_enc;
+};
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct SharedSecretEnc
+{
+ char data[TALER_REFRESH_SHARED_SECRET_LENGTH];
+};
+
+
+struct LinkDataEnc
+{
+ char data[sizeof (struct LinkData)];
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+struct RefreshCommitCoin
+{
+ struct GNUNET_CRYPTO_EddsaPublicKey session_pub;
+ struct TALER_RSA_BlindedSignaturePurpose coin_ev;
+ uint16_t cnc_index;
+ uint16_t newcoin_index;
+ char link_enc[sizeof (struct LinkData)];
+};
+
+
+struct KnownCoin
+{
+ struct TALER_CoinPublicInfo public_info;
+ struct TALER_Amount expended_balance;
+ int is_refreshed;
+ /**
+ * Refreshing session, only valid if
+ * is_refreshed==1.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub;
+};
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct Deposit
+{
+ /* FIXME: should be TALER_CoinPublicInfo */
+ struct GNUNET_CRYPTO_EddsaPublicKey coin_pub;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ struct TALER_RSA_Signature coin_sig;
+ struct TALER_RSA_SignaturePurpose purpose;
+ uint64_t transaction_id;
+ struct TALER_AmountNBO amount;
+ struct GNUNET_CRYPTO_EddsaPublicKey merchant_pub;
+ struct GNUNET_HashCode h_contract;
+ struct GNUNET_HashCode h_wire;
+ /* TODO: uint16_t wire_size */
+ char wire[]; /* string encoded wire JSON object */
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+int
+TALER_MINT_DB_prepare (PGconn *db_conn);
+
+int
+TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn,
+ struct TALER_RSA_BlindedSignaturePurpose *blind_ev,
+ struct CollectableBlindcoin *collectable);
+
+int
+TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn,
+ const struct CollectableBlindcoin *collectable);
+
+
+int
+TALER_MINT_DB_rollback (PGconn *db_conn);
+
+
+int
+TALER_MINT_DB_transaction (PGconn *db_conn);
+
+
+int
+TALER_MINT_DB_commit (PGconn *db_conn);
+
+
+int
+TALER_MINT_DB_get_reserve (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub,
+ struct Reserve *reserve_res);
+
+int
+TALER_MINT_DB_update_reserve (PGconn *db_conn,
+ const struct Reserve *reserve,
+ int fresh);
+
+
+int
+TALER_MINT_DB_insert_refresh_order (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub);
+
+int
+TALER_MINT_DB_get_refresh_session (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ struct RefreshSession *r_session);
+
+
+int
+TALER_MINT_DB_get_known_coin (PGconn *db_conn, struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ struct KnownCoin *known_coin);
+
+
+int
+TALER_MINT_DB_upsert_known_coin (PGconn *db_conn, struct KnownCoin *known_coin);
+
+
+int
+TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, struct RefreshCommitLink *commit_link);
+
+int
+TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, struct RefreshCommitCoin *commit_coin);
+
+
+int
+TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ int i, int j,
+ struct RefreshCommitLink *commit_link);
+
+
+int
+TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ int i, int j,
+ struct RefreshCommitCoin *commit_coin);
+
+
+int
+TALER_MINT_DB_create_refresh_session (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey
+ *session_pub);
+
+
+int
+TALER_MINT_DB_get_refresh_order (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub);
+
+
+int
+TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct TALER_RSA_Signature *ev_sig);
+int
+TALER_MINT_DB_get_refresh_collectable (PGconn *db_conn,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ struct TALER_RSA_Signature *ev_sig);
+int
+TALER_MINT_DB_set_reveal_ok (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub);
+
+int
+TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t oldcoin_index,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub);
+
+
+int
+TALER_MINT_DB_get_refresh_melt (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t oldcoin_index,
+ struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub);
+
+
+typedef
+int (*LinkIterator) (void *cls,
+ const struct LinkDataEnc *link_data_enc,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub,
+ const struct TALER_RSA_Signature *ev_sig);
+
+int
+TALER_db_get_link (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ LinkIterator link_iter,
+ void *cls);
+
+
+int
+TALER_db_get_transfer (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub,
+ struct SharedSecretEnc *shared_secret_enc);
+
+int
+TALER_MINT_DB_init_deposits (PGconn *db_conn, int temporary);
+
+int
+TALER_MINT_DB_prepare_deposits (PGconn *db_conn);
+
+int
+TALER_MINT_DB_insert_deposit (PGconn *db_conn,
+ const struct Deposit *deposit);
+
+int
+TALER_MINT_DB_get_deposit (PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub,
+ struct Deposit **r_deposit);
+int
+TALER_MINT_DB_insert_known_coin (PGconn *db_conn,
+ const struct KnownCoin *known_coin);
+
+
+
+/**
+ * Get the thread-local database-handle.
+ * Connect to the db if the connection does not exist yet.
+ *
+ * @param the database connection, or NULL on error
+ */
+PGconn *
+TALER_MINT_DB_get_connection (void);
+
+
+int
+TALER_MINT_DB_init (const char *connection_cfg);
+
+
+
+#endif /* _NEURO_MINT_DB_H */
diff --git a/src/mint/taler-mint-dbinit.c b/src/mint/taler-mint-dbinit.c
new file mode 100644
index 00000000..d877f62c
--- /dev/null
+++ b/src/mint/taler-mint-dbinit.c
@@ -0,0 +1,285 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-dbinit.c
+ * @brief Create tables for the mint database.
+ * @author Florian Dold
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <libpq-fe.h>
+#include "mint.h"
+
+
+#define break_db_err(result) do { \
+ GNUNET_break(0); \
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \
+ PQclear (result); \
+ } while (0)
+
+
+static char *mint_base_dir;
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+static PGconn *db_conn;
+static char *TALER_MINT_db_connection_cfg_str;
+
+
+int
+TALER_MINT_init_withdraw_tables (PGconn *conn)
+{
+ PGresult *result;
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS reserves"
+ "("
+ " reserve_pub BYTEA PRIMARY KEY"
+ ",balance_value INT4 NOT NULL"
+ ",balance_fraction INT4 NOT NULL"
+ ",balance_currency VARCHAR(4) NOT NULL"
+ ",status_sig BYTEA"
+ ",status_sign_pub BYTEA"
+ ",expiration_date INT8 NOT NULL"
+ ")");
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS collectable_blindcoins"
+ "("
+ "blind_ev BYTEA PRIMARY KEY"
+ ",blind_ev_sig BYTEA NOT NULL"
+ ",denom_pub BYTEA NOT NULL"
+ ",reserve_sig BYTEA NOT NULL"
+ ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub)"
+ ")");
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS known_coins "
+ "("
+ " coin_pub BYTEA NOT NULL PRIMARY KEY"
+ ",denom_pub BYTEA NOT NULL"
+ ",denom_sig BYTEA NOT NULL"
+ ",expended_value INT4 NOT NULL"
+ ",expended_fraction INT4 NOT NULL"
+ ",expended_currency VARCHAR(4) NOT NULL"
+ ",refresh_session_pub BYTEA"
+ ")");
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_sessions "
+ "("
+ " session_pub BYTEA PRIMARY KEY CHECK (length(session_pub) = 32)"
+ ",session_melt_sig BYTEA"
+ ",session_commit_sig BYTEA"
+ ",noreveal_index INT2 NOT NULL"
+ // non-zero if all reveals were ok
+ // and the new coin signatures are ready
+ ",reveal_ok BOOLEAN NOT NULL DEFAULT false"
+ ") ");
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_order "
+ "( "
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
+ ",newcoin_index INT2 NOT NULL "
+ ",denom_pub BYTEA NOT NULL "
+ ",PRIMARY KEY (session_pub, newcoin_index)"
+ ") ");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_commit_link"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
+ ",transfer_pub BYTEA NOT NULL"
+ ",link_secret_enc BYTEA NOT NULL"
+ // index of the old coin in the customer's request
+ ",oldcoin_index INT2 NOT NULL"
+ // index for cut and choose,
+ // ranges from 0 to kappa-1
+ ",cnc_index INT2 NOT NULL"
+ ")");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_commit_coin"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
+ ",link_vector_enc BYTEA NOT NULL"
+ // index of the new coin in the customer's request
+ ",newcoin_index INT2 NOT NULL"
+ // index for cut and choose,
+ ",cnc_index INT2 NOT NULL"
+ ",coin_ev BYTEA NOT NULL"
+ ")");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_melt"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
+ ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) "
+ ",denom_pub BYTEA NOT NULL "
+ ",oldcoin_index INT2 NOT NULL"
+ ")");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS refresh_collectable"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
+ ",ev_sig BYTEA NOT NULL"
+ ",newcoin_index INT2 NOT NULL"
+ ")");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ result = PQexec (conn,
+ "CREATE TABLE IF NOT EXISTS deposits "
+ "( "
+ " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)"
+ ",denom_pub BYTEA NOT NULL CHECK (length(denom_pub)=32)"
+ ",transaction_id INT8 NOT NULL"
+ ",amount_currency VARCHAR(4) NOT NULL"
+ ",amount_value INT4 NOT NULL"
+ ",amount_fraction INT4 NOT NULL"
+ ",merchant_pub BYTEA NOT NULL"
+ ",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)"
+ ",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)"
+ ",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)"
+ ",wire TEXT NOT NULL"
+ ")");
+
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ break_db_err (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * The main function of the serve tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
+ {'d', "mint-dir", "DIR",
+ "mint directory", 1,
+ &GNUNET_GETOPT_set_filename, &mint_base_dir},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ if (GNUNET_GETOPT_run ("taler-mint-serve", options, argc, argv) < 0)
+ return 1;
+
+ GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-dbinit", "INFO", NULL));
+
+ if (NULL == mint_base_dir)
+ {
+ fprintf (stderr, "Mint base directory not given.\n");
+ return 1;
+ }
+
+ cfg = TALER_MINT_config_load (mint_base_dir);
+ if (NULL == cfg)
+ {
+ fprintf (stderr, "Can't load mint configuration.\n");
+ return 1;
+ }
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "mint", "db", &TALER_MINT_db_connection_cfg_str))
+ {
+ fprintf (stderr, "Configuration 'mint.db' not found.\n");
+ return 42;
+ }
+ db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str);
+ if (CONNECTION_OK != PQstatus (db_conn))
+ {
+ fprintf (stderr, "Database connection failed: %s\n", PQerrorMessage (db_conn));
+ return 1;
+ }
+
+ if (GNUNET_OK != TALER_MINT_init_withdraw_tables (db_conn))
+ {
+ fprintf (stderr, "Failed to initialize database.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c
new file mode 100644
index 00000000..6d69813c
--- /dev/null
+++ b/src/mint/taler-mint-httpd.c
@@ -0,0 +1,376 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-httpd.c
+ * @brief Serve the HTTP interface of the mint
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "mint.h"
+#include "mint_db.h"
+#include "taler_types.h"
+#include "taler_signatures.h"
+#include "taler_rsa.h"
+#include "taler_json_lib.h"
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd_mhd.h"
+#include "taler-mint-httpd_keys.h"
+#include "taler-mint-httpd_deposit.h"
+#include "taler-mint-httpd_withdraw.h"
+#include "taler-mint-httpd_refresh.h"
+
+
+/**
+ * Base directory of the mint (global)
+ */
+char *mintdir;
+
+/**
+ * The mint's configuration (global)
+ */
+struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Master public key (according to the
+ * configuration in the mint directory).
+ */
+struct GNUNET_CRYPTO_EddsaPublicKey master_pub;
+
+/**
+ * The HTTP Daemon.
+ */
+static struct MHD_Daemon *mydaemon;
+
+/**
+ * The kappa value for refreshing.
+ */
+static unsigned int refresh_security_parameter;
+
+/**
+ * Port to run the daemon on.
+ */
+static uint16_t serve_port;
+
+
+/**
+ * Convert a string representing an EdDSA signature to an EdDSA
+ * signature.
+ *
+ * FIXME: this should be in GNUnet.
+ * FIXME: why? this code is dead, even here!
+ *
+ * @param enc encoded EdDSA signature
+ * @param enclen number of bytes in @a enc (without 0-terminator)
+ * @param pub where to store the EdDSA signature
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_eddsa_signature_from_string (const char *enc,
+ size_t enclen,
+ struct GNUNET_CRYPTO_EddsaSignature *sig)
+{
+ size_t keylen = (sizeof (struct GNUNET_CRYPTO_EddsaSignature)) * 8;
+
+ if (keylen % 5 > 0)
+ keylen += 5 - keylen % 5;
+ keylen /= 5;
+ if (enclen != keylen)
+ return GNUNET_SYSERR;
+
+ if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen,
+ sig,
+ sizeof (struct GNUNET_CRYPTO_EddsaSignature)))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle a request coming from libmicrohttpd.
+ *
+ * @param cls closure for MHD daemon (unused)
+ * @param connection the connection
+ * @param url the requested url
+ * @param method the method (POST, GET, ...)
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request (a `struct Buffer *`)
+ * @return MHD result code
+ */
+static int
+handle_mhd_request (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ static struct RequestHandler handlers[] =
+ {
+ { "/", MHD_HTTP_METHOD_GET, "text/plain",
+ "Hello, I'm the mint\n", 0,
+ &TALER_MINT_handler_static_response, MHD_HTTP_OK },
+ { "/agpl", MHD_HTTP_METHOD_GET, "text/plain",
+ NULL, 0,
+ &TALER_MINT_handler_agpl_redirect, MHD_HTTP_FOUND },
+ { "/keys", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_keys, MHD_HTTP_OK },
+ { "/keys", NULL, "text/plain",
+ "Only GET is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/withdraw/status", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_withdraw_status, MHD_HTTP_OK },
+ { "/withdraw/status", NULL, "text/plain",
+ "Only GET is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/withdraw/sign", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_withdraw_sign, MHD_HTTP_OK },
+ { "/withdraw/sign", NULL, "text/plain",
+ "Only GET is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/refresh/melt", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_refresh_melt, MHD_HTTP_OK },
+ { "/refresh/melt", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/refresh/commit", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_refresh_commit, MHD_HTTP_OK },
+ { "/refresh/commit", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/refresh/reveal", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_refresh_melt, MHD_HTTP_OK },
+ { "/refresh/reveal", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/refresh/link", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_refresh_link, MHD_HTTP_OK },
+ { "/refresh/link", NULL, "text/plain",
+ "Only GET is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/refresh/reveal", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_refresh_reveal, MHD_HTTP_OK },
+ { "/refresh/reveal", NULL, "text/plain",
+ "Only GET is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { "/deposit", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &TALER_MINT_handler_deposit, MHD_HTTP_OK },
+ { "/deposit", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TALER_MINT_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ { NULL, NULL, NULL, NULL, 0, 0 }
+ };
+ static struct RequestHandler h404 =
+ {
+ "", NULL, "text/html",
+ "<html><title>404: not found</title></html>", 0,
+ &TALER_MINT_handler_static_response, MHD_HTTP_NOT_FOUND
+ };
+ struct RequestHandler *rh;
+ unsigned int i;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling request for URL '%s'\n",
+ url);
+ for (i=0;NULL != handlers[i].url;i++)
+ {
+ rh = &handlers[i];
+ if ( (0 == strcasecmp (url,
+ rh->url)) &&
+ ( (NULL == rh->method) ||
+ (0 == strcasecmp (method,
+ rh->method)) ) )
+ return rh->handler (rh,
+ connection,
+ con_cls,
+ upload_data,
+ upload_data_size);
+ }
+ return TALER_MINT_handler_static_response (&h404,
+ connection,
+ con_cls,
+ upload_data,
+ upload_data_size);
+}
+
+
+
+/**
+ * Load configuration parameters for the mint
+ * server into the corresponding global variables.
+ *
+ * @param param mint_directory the mint's directory
+ * @return GNUNET_OK on success
+ */
+static int
+mint_serve_process_config (const char *mint_directory)
+{
+ unsigned long long port;
+ unsigned long long kappa;
+ char *master_pub_str;
+ char *db_cfg;
+
+ cfg = TALER_MINT_config_load (mint_directory);
+ if (NULL == cfg)
+ {
+ fprintf (stderr,
+ "can't load mint configuration\n");
+ return 1;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint", "master_pub",
+ &master_pub_str))
+ {
+ fprintf (stderr,
+ "No master public key given in mint configuration.");
+ return GNUNET_NO;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_public_key_from_string (master_pub_str,
+ strlen (master_pub_str),
+ &master_pub))
+ {
+ fprintf (stderr,
+ "Invalid master public key given in mint configuration.");
+ return GNUNET_NO;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint", "db",
+ &db_cfg))
+ {
+ fprintf (stderr,
+ "invalid configuration: mint.db\n");
+ return GNUNET_NO;
+ }
+ if (GNUNET_OK !=
+ TALER_MINT_DB_init (db_cfg))
+ {
+ fprintf (stderr,
+ "failed to initialize DB subsystem\n");
+ return GNUNET_NO;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "mint", "port",
+ &port))
+ {
+ fprintf (stderr,
+ "invalid configuration: mint.port\n");
+ return GNUNET_NO;
+ }
+
+ if ((port == 0) || (port > UINT16_MAX))
+ {
+ fprintf (stderr,
+ "invalid configuration (value out of range): mint.port\n");
+ return GNUNET_NO;
+ }
+ serve_port = port;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "mint", "refresh_security_parameter",
+ &kappa))
+ {
+ fprintf (stderr,
+ "invalid configuration: mint.refresh_security_parameter\n");
+ return GNUNET_NO;
+ }
+ refresh_security_parameter = kappa;
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * The main function of the serve tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
+ {'d', "mint-dir", "DIR",
+ "mint directory", 1,
+ &GNUNET_GETOPT_set_filename, &mintdir},
+ GNUNET_GETOPT_OPTION_END
+ };
+ int ret;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-mint-serve",
+ "INFO",
+ NULL));
+ if (GNUNET_GETOPT_run ("taler-mint-serve",
+ options,
+ argc, argv) < 0)
+ return 1;
+ if (NULL == mintdir)
+ {
+ fprintf (stderr,
+ "no mint dir given\n");
+ return 1;
+ }
+
+ if (GNUNET_OK != mint_serve_process_config (mintdir))
+ return 1;
+
+
+ mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+ serve_port,
+ NULL, NULL,
+ &handle_mhd_request, NULL,
+ MHD_OPTION_END);
+
+ if (NULL == mydaemon)
+ {
+ fprintf (stderr,
+ "Failed to start MHD.\n");
+ return 1;
+ }
+
+ ret = TALER_MINT_key_reload_loop ();
+ MHD_stop_daemon (mydaemon);
+ return (GNUNET_OK == ret) ? 0 : 1;
+}
+
diff --git a/src/mint/taler-mint-httpd.h b/src/mint/taler-mint-httpd.h
new file mode 100644
index 00000000..59f38aad
--- /dev/null
+++ b/src/mint/taler-mint-httpd.h
@@ -0,0 +1,106 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd.h
+ * @brief Global declarations for the mint
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_H
+#define TALER_MINT_HTTPD_H
+
+
+/**
+ * Cut-and-choose size for refreshing.
+ * FIXME: maybe make it a config option?
+ */
+#define KAPPA 3
+
+
+/**
+ * The mint's configuration.
+ */
+extern struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Main directory with mint data.
+ */
+extern char *mintdir;
+
+/**
+ * Master public key (according to the
+ * configuration in the mint directory).
+ */
+extern struct GNUNET_CRYPTO_EddsaPublicKey master_pub;
+
+
+/**
+ * Struct describing an URL and the handler for it.
+ */
+struct RequestHandler
+{
+
+ /**
+ * URL the handler is for.
+ */
+ const char *url;
+
+ /**
+ * Method the handler is for, NULL for "all".
+ */
+ const char *method;
+
+ /**
+ * Mime type to use in reply (hint, can be NULL).
+ */
+ const char *mime_type;
+
+ /**
+ * Raw data for the @e handler
+ */
+ const void *data;
+
+ /**
+ * Number of bytes in @e data, 0 for 0-terminated.
+ */
+ size_t data_size;
+
+ /**
+ * Function to call to handle the request.
+ *
+ * @param rh this struct
+ * @param mime_type the @e mime_type for the reply (hint, can be NULL)
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+ int (*handler)(struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+ /**
+ * Default response code.
+ */
+ int response_code;
+};
+
+
+#endif
diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c
new file mode 100644
index 00000000..ecbc5c13
--- /dev/null
+++ b/src/mint/taler-mint-httpd_deposit.c
@@ -0,0 +1,270 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_deposit.c
+ * @brief Handle /deposit requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "mint.h"
+#include "mint_db.h"
+#include "taler_types.h"
+#include "taler_signatures.h"
+#include "taler_rsa.h"
+#include "taler_json_lib.h"
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd_keys.h"
+#include "taler-mint-httpd_deposit.h"
+
+
+/**
+ * Send confirmation of deposit success to client.
+ *
+ * @param connection connection to the client
+ * @param deposit deposit request to confirm
+ * @return MHD result code
+ */
+static int
+helper_deposit_send_response_success (struct MHD_Connection *connection,
+ struct Deposit *deposit)
+{
+ // FIXME: return more information here
+ return request_send_json_pack (connection, MHD_HTTP_OK,
+ "{s:s}", "status", "DEPOSIT_OK");
+}
+
+
+/**
+ * Handle a "/deposit" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_deposit (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ json_t *json;
+ struct Deposit *deposit;
+ json_t *wire;
+ json_t *resp;
+ char *wire_enc = NULL;
+ const char *deposit_type;
+ struct MintKeyState *key_state;
+ struct TALER_CoinPublicInfo coin_info;
+ struct TALER_RSA_Signature ubsig;
+ size_t len;
+ int resp_code;
+ PGconn *db_conn;
+ int res;
+
+ res = process_post_json (connection,
+ connection_cls,
+ upload_data, upload_data_size,
+ &json);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return MHD_YES;
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ deposit = NULL;
+ wire = NULL;
+ resp = NULL;
+ if (-1 == json_unpack (json,
+ "{s:s s:o}",
+ "type", &deposit_type,
+ "wire", &wire))
+ {
+ GNUNET_break_op (0);
+ resp = json_pack ("{s:s}", "error", "Bad format");
+ resp_code = MHD_HTTP_BAD_REQUEST;
+ goto EXITIF_exit;
+ }
+ if (NULL == (wire_enc = json_dumps (wire, JSON_COMPACT|JSON_SORT_KEYS)))
+ {
+ GNUNET_break_op (0);
+ resp = json_pack ("{s:s}", "error", "Bad format");
+ resp_code = MHD_HTTP_BAD_REQUEST;
+ goto EXITIF_exit;
+ }
+ len = strlen (wire_enc) + 1;
+ deposit = GNUNET_malloc (sizeof (struct Deposit) + len);
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+#define PARSE_DATA(field, addr) \
+ EXITIF (GNUNET_OK != request_json_require_nav \
+ (connection, json, \
+ JNAV_FIELD, field, JNAV_RET_DATA, addr, sizeof (*addr)))
+ PARSE_DATA ("coin_pub", &deposit->coin_pub);
+ PARSE_DATA ("denom_pub", &deposit->denom_pub);
+ PARSE_DATA ("ubsig", &ubsig);
+ PARSE_DATA ("merchant_pub", &deposit->merchant_pub);
+ PARSE_DATA ("H_a", &deposit->h_contract);
+ PARSE_DATA ("H_wire", &deposit->h_wire);
+ PARSE_DATA ("csig", &deposit->coin_sig);
+ PARSE_DATA ("transaction_id", &deposit->transaction_id);
+#undef PARSE_DATA
+ if (0 == strcmp ("DIRECT_DEPOSIT", deposit_type))
+ deposit->purpose.purpose = htonl (TALER_SIGNATURE_DEPOSIT);
+ else if (0 == strcmp ("INCREMENTAL_DEPOSIT", deposit_type))
+ deposit->purpose.purpose = htonl (TALER_SIGNATURE_INCREMENTAL_DEPOSIT);
+ else
+ {
+ GNUNET_break_op (0);
+ resp = json_pack ("{s:s}", "error", "Bad format");
+ resp_code = MHD_HTTP_BAD_REQUEST;
+ goto EXITIF_exit;
+ }
+ deposit->purpose.size = htonl (sizeof (struct Deposit)
+ - offsetof (struct Deposit, purpose));
+ memcpy (&coin_info.coin_pub,
+ &deposit->coin_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+ coin_info.denom_pub = deposit->denom_pub;
+ coin_info.denom_sig = ubsig;
+ key_state = TALER_MINT_key_state_acquire ();
+ if (GNUNET_YES != TALER_MINT_test_coin_valid (key_state,
+ &coin_info))
+ {
+ TALER_MINT_key_state_release (key_state);
+ resp = json_pack ("{s:s}", "error", "Coin is not valid");
+ resp_code = MHD_HTTP_NOT_FOUND;
+ goto EXITIF_exit;
+ }
+ TALER_MINT_key_state_release (key_state);
+ /*
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_DEPOSIT,
+ &deposit->purpose,
+ &deposit->coin_sig,
+ &deposit->coin_pub))
+ {
+ resp = json_pack ("{s:s}", "error", "Signature verfication failed");
+ resp_code = MHD_HTTP_NOT_FOUND;
+ goto EXITIF_exit;
+ }
+ */
+
+ /* Check if we already received the same deposit permission,
+ * or the coin was already deposited */
+
+ {
+ struct Deposit *existing_deposit;
+ int res;
+
+ res = TALER_MINT_DB_get_deposit (db_conn,
+ &deposit->coin_pub,
+ &existing_deposit);
+ if (GNUNET_YES == res)
+ {
+ // FIXME: memory leak
+ if (0 == memcmp (existing_deposit, deposit, sizeof (struct Deposit)))
+ return helper_deposit_send_response_success (connection, deposit);
+ // FIXME: in the future, check if there's enough credits
+ // left on the coin. For now: refuse
+ // FIXME: return more information here
+ return request_send_json_pack (connection, MHD_HTTP_FORBIDDEN,
+ "{s:s}",
+ "error", "double spending");
+ }
+
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ {
+ struct KnownCoin known_coin;
+ int res;
+
+ res = TALER_MINT_DB_get_known_coin (db_conn, &coin_info.coin_pub, &known_coin);
+ if (GNUNET_YES == res)
+ {
+ // coin must have been refreshed
+ // FIXME: check
+ // FIXME: return more information here
+ return request_send_json_pack (connection, MHD_HTTP_FORBIDDEN,
+ "{s:s}",
+ "error", "coin was refreshed");
+ }
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* coin valid but not known => insert into DB */
+ known_coin.is_refreshed = GNUNET_NO;
+ known_coin.expended_balance = TALER_amount_ntoh (deposit->amount);
+ known_coin.public_info = coin_info;
+
+ if (GNUNET_OK != TALER_MINT_DB_insert_known_coin (db_conn, &known_coin))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_insert_deposit (db_conn, deposit))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return helper_deposit_send_response_success (connection, deposit);
+
+ EXITIF_exit:
+ if (NULL != resp)
+ res = send_response_json (connection, resp, resp_code);
+ else
+ res = MHD_NO;
+ if (NULL != wire)
+ json_decref (wire);
+ if (NULL != deposit)
+ GNUNET_free (deposit);
+ if (NULL != wire_enc)
+ GNUNET_free (wire_enc);
+ return res;
+#undef EXITIF
+#undef PARSE_DATA
+}
+
+/* end of taler-mint-httpd_deposit.c */
diff --git a/src/mint/taler-mint-httpd_deposit.h b/src/mint/taler-mint-httpd_deposit.h
new file mode 100644
index 00000000..dd7b8c13
--- /dev/null
+++ b/src/mint/taler-mint-httpd_deposit.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_deposit.h
+ * @brief Handle /deposit requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_DEPOSIT_H
+#define TALER_MINT_HTTPD_DEPOSIT_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Handle a "/deposit" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_deposit (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+#endif
diff --git a/src/mint/taler-mint-httpd_keys.c b/src/mint/taler-mint-httpd_keys.c
new file mode 100644
index 00000000..ba023fe6
--- /dev/null
+++ b/src/mint/taler-mint-httpd_keys.c
@@ -0,0 +1,512 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_keys.c
+ * @brief Handle /keys requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "mint.h"
+#include "mint_db.h"
+#include "taler_types.h"
+#include "taler_signatures.h"
+#include "taler_rsa.h"
+#include "taler_json_lib.h"
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd_keys.h"
+
+
+/**
+ * Mint key state. Never use directly, instead access via
+ * #TALER_MINT_key_state_acquire and #TALER_MINT_key_state_release.
+ */
+static struct MintKeyState *internal_key_state;
+
+/**
+ * Mutex protecting access to #internal_key_state.
+ */
+static pthread_mutex_t internal_key_state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Pipe used for signaling reloading of our key state.
+ */
+static int reload_pipe[2];
+
+
+/**
+ * Convert the public part of a denomination key
+ * issue to a JSON object.
+ *
+ * @param dki the denomination key issue
+ * @return a JSON object describing the denomination key isue (public part)
+ */
+static json_t *
+denom_key_issue_to_json (const struct TALER_MINT_DenomKeyIssue *dki)
+{
+ json_t *dk_json = json_object ();
+ json_object_set_new (dk_json, "master_sig",
+ TALER_JSON_from_data (&dki->signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature)));
+ json_object_set_new (dk_json, "stamp_start", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->start)));
+ json_object_set_new (dk_json, "stamp_expire_withdraw", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_withdraw)));
+ json_object_set_new (dk_json, "stamp_expire_deposit", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_spend)));
+ json_object_set_new (dk_json, "denom_pub",
+ TALER_JSON_from_data (&dki->denom_pub, sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)));
+ json_object_set_new (dk_json, "value",
+ TALER_JSON_from_amount (TALER_amount_ntoh (dki->value)));
+ json_object_set_new (dk_json,
+ "fee_withdraw",
+ TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_withdraw)));
+ json_object_set_new (dk_json,
+ "fee_deposit",
+ TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_deposit)));
+ json_object_set_new (dk_json,
+ "fee_refresh",
+ TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_refresh)));
+ return dk_json;
+}
+
+
+/**
+ * Convert the public part of a sign key
+ * issue to a JSON object.
+ *
+ * @param ski the sign key issue
+ * @return a JSON object describing the sign key isue (public part)
+ */
+static json_t *
+sign_key_issue_to_json (const struct TALER_MINT_SignKeyIssue *ski)
+{
+ json_t *sk_json = json_object ();
+ json_object_set_new (sk_json, "stamp_start", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->start)));
+ json_object_set_new (sk_json, "stamp_expire", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->expire)));
+ json_object_set_new (sk_json, "master_sig",
+ TALER_JSON_from_data (&ski->signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature)));
+ json_object_set_new (sk_json, "key",
+ TALER_JSON_from_data (&ski->signkey_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)));
+ return sk_json;
+}
+
+
+/**
+ * Get the relative time value that describes how
+ * far in the future do we want to provide coin keys.
+ *
+ * @return the provide duration
+ */
+static struct GNUNET_TIME_Relative
+TALER_MINT_conf_duration_provide ()
+{
+ struct GNUNET_TIME_Relative rel;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (cfg,
+ "mint_keys",
+ "lookahead_provide",
+ &rel))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "mint_keys.lookahead_provide not valid or not given\n");
+ GNUNET_abort ();
+ }
+ return rel;
+}
+
+
+/**
+ * Iterator for denomination keys.
+ *
+ * @param cls closure
+ * @param dki the denomination key issue
+ * @param alias coin alias
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+reload_keys_denom_iter (void *cls,
+ const char *alias,
+ const struct TALER_MINT_DenomKeyIssue *dki)
+{
+ struct MintKeyState *ctx = cls;
+ struct GNUNET_TIME_Absolute stamp_provide;
+ struct GNUNET_HashCode denom_key_hash;
+ int res;
+
+ stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time,
+ TALER_MINT_conf_duration_provide ());
+
+ if (GNUNET_TIME_absolute_ntoh (dki->expire_spend).abs_value_us < ctx->reload_time.abs_value_us)
+ {
+ // this key is expired
+ return GNUNET_OK;
+ }
+ if (GNUNET_TIME_absolute_ntoh (dki->start).abs_value_us > stamp_provide.abs_value_us)
+ {
+ // we are to early for this key
+ return GNUNET_OK;
+ }
+
+ GNUNET_CRYPTO_hash (&dki->denom_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), &denom_key_hash);
+
+ res = GNUNET_CONTAINER_multihashmap_put (ctx->denomkey_map,
+ &denom_key_hash,
+ GNUNET_memdup (dki, sizeof (struct TALER_MINT_DenomKeyIssue)),
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+ if (GNUNET_OK != res)
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Duplicate denomination key\n");
+
+ json_array_append_new (ctx->denom_keys_array,
+ denom_key_issue_to_json (dki));
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Iterator for sign keys.
+ *
+ * @param cls closure
+ * @param ski the sign key issue
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+reload_keys_sign_iter (void *cls,
+ const struct TALER_MINT_SignKeyIssue *ski)
+{
+ struct MintKeyState *ctx = cls;
+ struct GNUNET_TIME_Absolute stamp_provide;
+
+ stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time, TALER_MINT_conf_duration_provide (cfg));
+
+ if (GNUNET_TIME_absolute_ntoh (ski->expire).abs_value_us < ctx->reload_time.abs_value_us)
+ {
+ // this key is expired
+ return GNUNET_OK;
+ }
+
+ if (GNUNET_TIME_absolute_ntoh (ski->start).abs_value_us > stamp_provide.abs_value_us)
+ {
+ // we are to early for this key
+ return GNUNET_OK;
+ }
+
+ // the signkey is valid for now, check
+ // if it's more recent than the current one!
+ if (GNUNET_TIME_absolute_ntoh (ctx->current_sign_key_issue.start).abs_value_us >
+ GNUNET_TIME_absolute_ntoh (ski->start).abs_value_us)
+ ctx->current_sign_key_issue = *ski;
+
+
+ ctx->next_reload = GNUNET_TIME_absolute_min (ctx->next_reload,
+ GNUNET_TIME_absolute_ntoh (ski->expire));
+
+ json_array_append_new (ctx->sign_keys_array,
+ sign_key_issue_to_json (ski));
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Load the mint's key state from disk.
+ *
+ * @return fresh key state (with reference count 1)
+ */
+static struct MintKeyState *
+reload_keys ()
+{
+ struct MintKeyState *key_state;
+ json_t *keys;
+
+ key_state = GNUNET_new (struct MintKeyState);
+ key_state->refcnt = 1;
+
+ key_state->next_reload = GNUNET_TIME_UNIT_FOREVER_ABS;
+
+ key_state->denom_keys_array = json_array ();
+ GNUNET_assert (NULL != key_state->denom_keys_array);
+
+ key_state->sign_keys_array = json_array ();
+ GNUNET_assert (NULL != key_state->sign_keys_array);
+
+ key_state->denomkey_map = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
+ GNUNET_assert (NULL != key_state->denomkey_map);
+
+ key_state->reload_time = GNUNET_TIME_absolute_get ();
+
+ TALER_MINT_denomkeys_iterate (mintdir, &reload_keys_denom_iter, key_state);
+ TALER_MINT_signkeys_iterate (mintdir, &reload_keys_sign_iter, key_state);
+
+ keys = json_pack ("{s:o, s:o, s:o, s:o}",
+ "master_pub", TALER_JSON_from_data (&master_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)),
+ "signkeys", key_state->sign_keys_array,
+ "denoms", key_state->denom_keys_array,
+ "list_issue_date", TALER_JSON_from_abs (key_state->reload_time));
+
+ key_state->keys_json = json_dumps (keys, JSON_INDENT(2));
+
+ return key_state;
+}
+
+
+/**
+ * Release key state, free if necessary (if reference count gets to zero).
+ *
+ * @param key_state the key state to release
+ */
+void
+TALER_MINT_key_state_release (struct MintKeyState *key_state)
+{
+ GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
+ GNUNET_assert (0 != key_state->refcnt);
+ key_state->refcnt += 1;
+ if (key_state->refcnt == 0) {
+ GNUNET_free (key_state);
+ }
+ GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
+}
+
+
+/**
+ * Acquire the key state of the mint. Updates keys if necessary.
+ * For every call to #TALER_MINT_key_state_acquire, a matching call
+ * to #TALER_MINT_key_state_release must be made.
+ *
+ * @return the key state
+ */
+struct MintKeyState *
+TALER_MINT_key_state_acquire (void)
+{
+ struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+ struct MintKeyState *key_state;
+
+ // FIXME: the locking we have is very coarse-grained,
+ // using multiple locks might be nicer ...
+
+ GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
+ if (NULL == internal_key_state)
+ {
+ internal_key_state = reload_keys ();
+ }
+ else if (internal_key_state->next_reload.abs_value_us <= now.abs_value_us)
+ {
+ GNUNET_assert (0 != internal_key_state->refcnt);
+ internal_key_state->refcnt--;
+ if (0 == internal_key_state->refcnt)
+ GNUNET_free (internal_key_state);
+ internal_key_state = reload_keys ();
+ }
+ key_state = internal_key_state;
+ key_state->refcnt += 1;
+ GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
+
+ return key_state;
+}
+
+
+/**
+ * Look up the issue for a denom public key.
+ *
+ * @param key state to look in
+ * @param denom_pub denomination public key
+ * @return the denomination key issue,
+ * or NULL if denom_pub could not be found
+ */
+struct TALER_MINT_DenomKeyIssue *
+TALER_MINT_get_denom_key (const struct MintKeyState *key_state,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub)
+{
+ struct TALER_MINT_DenomKeyIssue *issue;
+ struct GNUNET_HashCode hash;
+
+ GNUNET_CRYPTO_hash (denom_pub, sizeof (struct TALER_RSA_PublicKeyBinaryEncoded), &hash);
+ issue = GNUNET_CONTAINER_multihashmap_get (key_state->denomkey_map, &hash);
+ return issue;
+}
+
+
+/**
+ * Check if a coin is valid; that is, whether the denomination key exists,
+ * is not expired, and the signature is correct.
+ *
+ * @param key_state the key state to use for checking the coin's validity
+ * @param coin_public_info the coin public info to check for validity
+ * @return GNUNET_YES if the coin is valid,
+ * GNUNET_NO if it is invalid
+ * GNUNET_SYSERROR if an internal error occured
+ */
+int
+TALER_MINT_test_coin_valid (const struct MintKeyState *key_state,
+ struct TALER_CoinPublicInfo *coin_public_info)
+{
+ struct TALER_MINT_DenomKeyIssue *dki;
+
+ dki = TALER_MINT_get_denom_key (key_state, &coin_public_info->denom_pub);
+ if (NULL == dki)
+ return GNUNET_NO;
+ if (GNUNET_OK != TALER_RSA_verify (&coin_public_info->coin_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
+ &coin_public_info->denom_sig,
+ &dki->denom_pub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "coin signature is invalid\n");
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+}
+
+
+/**
+ * Function to call to handle the request by sending
+ * back static data from the @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_keys (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct MintKeyState *key_state;
+ struct MHD_Response *response;
+ int ret;
+
+ key_state = TALER_MINT_key_state_acquire ();
+ response = MHD_create_response_from_buffer (strlen (key_state->keys_json),
+ key_state->keys_json,
+ MHD_RESPMEM_MUST_COPY);
+ TALER_MINT_key_state_release (key_state);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ (void) MHD_add_response_header (response,
+ "Content-Type",
+ rh->mime_type);
+ ret = MHD_queue_response (connection,
+ rh->response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Handle a signal, writing relevant signal numbers
+ * (currently just SIGUSR1) to a pipe.
+ *
+ * @param signal_number the signal number
+ */
+static void
+handle_signal (int signal_number)
+{
+ size_t res;
+ char c = signal_number;
+
+ if (SIGUSR1 == signal_number)
+ {
+ errno = 0;
+ res = write (reload_pipe[1], &c, 1);
+ if ((res < 0) && (EINTR != errno))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (0 == res)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ }
+}
+
+
+/**
+ * Read signals from a pipe in a loop, and reload keys from disk if
+ * SIGUSR1 is read from the pipe.
+ */
+int
+TALER_MINT_key_reload_loop (void)
+{
+ struct sigaction act;
+
+ if (0 != pipe (reload_pipe))
+ {
+ fprintf (stderr,
+ "Failed to create pipe.\n");
+ return GNUNET_SYSERR;
+ }
+ memset (&act, 0, sizeof (struct sigaction));
+ act.sa_handler = &handle_signal;
+
+ if (0 != sigaction (SIGUSR1, &act, NULL))
+ {
+ fprintf (stderr,
+ "Failed to set signal handler.\n");
+ return GNUNET_SYSERR;
+ }
+
+ while (1)
+ {
+ char c;
+ ssize_t res;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "(re-)loading keys\n");
+ GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
+ if (NULL != internal_key_state)
+ {
+ GNUNET_assert (0 != internal_key_state->refcnt);
+ internal_key_state->refcnt -= 1;
+ if (0 == internal_key_state->refcnt)
+ GNUNET_free (internal_key_state);
+ }
+ internal_key_state = reload_keys ();
+ GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
+read_again:
+ errno = 0;
+ res = read (reload_pipe[0], &c, 1);
+ if ((res < 0) && (EINTR != errno))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (EINTR == errno)
+ goto read_again;
+ }
+ return GNUNET_OK;
+}
+
+
+/* end of taler-mint-httpd_keys.c */
diff --git a/src/mint/taler-mint-httpd_keys.h b/src/mint/taler-mint-httpd_keys.h
new file mode 100644
index 00000000..640a9c91
--- /dev/null
+++ b/src/mint/taler-mint-httpd_keys.h
@@ -0,0 +1,155 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_keys.h
+ * @brief Handle /keys requests and manage key state
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_KEYS_H
+#define TALER_MINT_HTTPD_KEYS_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Snapshot of the (coin and signing)
+ * keys (including private keys) of the mint.
+ */
+struct MintKeyState
+{
+ /**
+ * When did we initiate the key reloading?
+ */
+ struct GNUNET_TIME_Absolute reload_time;
+
+ /**
+ * JSON array with denomination keys.
+ */
+ json_t *denom_keys_array;
+
+ /**
+ * JSON array with signing keys.
+ */
+ json_t *sign_keys_array;
+
+ /**
+ * Mapping from denomination keys to denomination key issue struct.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *denomkey_map;
+
+ /**
+ * When is the next key invalid and we have to reload?
+ */
+ struct GNUNET_TIME_Absolute next_reload;
+
+ /**
+ * Mint signing key that should be used currently.
+ */
+ struct TALER_MINT_SignKeyIssue current_sign_key_issue;
+
+ /**
+ * Cached JSON text that the mint will send for
+ * a /keys request.
+ */
+ char *keys_json;
+
+ /**
+ * Reference count.
+ */
+ unsigned int refcnt;
+};
+
+
+/**
+ * Release key state, free if necessary (if reference count gets to zero).
+ *
+ * @param key_state the key state to release
+ */
+void
+TALER_MINT_key_state_release (struct MintKeyState *key_state);
+
+
+/**
+ * Acquire the key state of the mint. Updates keys if necessary.
+ * For every call to #TALER_MINT_key_state_acquire, a matching call
+ * to #TALER_MINT_key_state_release must be made.
+ *
+ * @return the key state
+ */
+struct MintKeyState *
+TALER_MINT_key_state_acquire (void);
+
+
+/**
+ * Look up the issue for a denom public key.
+ *
+ * @param key state to look in
+ * @param denom_pub denomination public key
+ * @return the denomination key issue,
+ * or NULL if denom_pub could not be found
+ */
+struct TALER_MINT_DenomKeyIssue *
+TALER_MINT_get_denom_key (const struct MintKeyState *key_state,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub);
+
+
+/**
+ * Check if a coin is valid; that is, whether the denomination key exists,
+ * is not expired, and the signature is correct.
+ *
+ * @param key_state the key state to use for checking the coin's validity
+ * @param coin_public_info the coin public info to check for validity
+ * @return GNUNET_YES if the coin is valid,
+ * GNUNET_NO if it is invalid
+ * GNUNET_SYSERROR if an internal error occured
+ */
+int
+TALER_MINT_test_coin_valid (const struct MintKeyState *key_state,
+ struct TALER_CoinPublicInfo *coin_public_info);
+
+
+/**
+ * Read signals from a pipe in a loop, and reload keys from disk if
+ * SIGUSR1 is read from the pipe.
+ *
+ * @return GNUNET_OK if we terminated normally, GNUNET_SYSERR on error
+ */
+int
+TALER_MINT_key_reload_loop (void);
+
+
+/**
+ * Handle a "/keys" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_keys (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+#endif
diff --git a/src/mint/taler-mint-httpd_mhd.c b/src/mint/taler-mint-httpd_mhd.c
new file mode 100644
index 00000000..09f3025b
--- /dev/null
+++ b/src/mint/taler-mint-httpd_mhd.c
@@ -0,0 +1,300 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-httpd_mhd.c
+ * @brief helpers for MHD interaction
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd.h"
+#include "taler-mint-httpd_mhd.h"
+
+
+/**
+ * Function to call to handle the request by sending
+ * back static data from the @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_static_response (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct MHD_Response *response;
+ int ret;
+
+ if (0 == rh->data_size)
+ rh->data_size = strlen ((const char *) rh->data);
+ response = MHD_create_response_from_buffer (rh->data_size,
+ (void *) rh->data,
+ MHD_RESPMEM_PERSISTENT);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (NULL != rh->mime_type)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ rh->mime_type);
+ ret = MHD_queue_response (connection,
+ rh->response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Function to call to handle the request by sending
+ * back a redirect to the AGPL source code.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_agpl_redirect (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ const char *agpl =
+ "This server is licensed under the Affero GPL. You will now be redirected to the source code.";
+ struct MHD_Response *response;
+ int ret;
+
+ response = MHD_create_response_from_buffer (strlen (agpl),
+ (void *) agpl,
+ MHD_RESPMEM_PERSISTENT);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (NULL != rh->mime_type)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ rh->mime_type);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_LOCATION,
+ "http://www.git.taler.net/?p=mint.git");
+ ret = MHD_queue_response (connection,
+ rh->response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply from varargs.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param response_code HTTP response code to use
+ * @param do_cache can the response be cached? (0: no, 1: yes)
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD result code
+ */
+int
+TALER_MINT_helper_send_json_pack (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void *connection_cls,
+ int response_code,
+ int do_cache,
+ const char *fmt,
+ ...)
+{
+ int ret;
+ json_t *json;
+ va_list argp;
+ char *json_str;
+ struct MHD_Response *response;
+
+ va_start (argp, fmt);
+ json = json_vpack_ex (NULL, 0, fmt, argp);
+ va_end (argp);
+ if (NULL == json)
+ return MHD_NO;
+ json_str = json_dumps (json, JSON_INDENT(2));
+ json_decref (json);
+ if (NULL == json_str)
+ return MHD_NO;
+ response = MHD_create_response_from_buffer (strlen (json_str),
+ json_str,
+ MHD_RESPMEM_MUST_FREE);
+ if (NULL == response)
+ {
+ free (json_str);
+ return MHD_NO;
+ }
+ if (NULL != rh->mime_type)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ rh->mime_type);
+ ret = MHD_queue_response (connection,
+ response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply with an error message from @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_send_json_pack_error (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ return TALER_MINT_helper_send_json_pack (rh,
+ connection,
+ connection_cls,
+ 1, /* caching enabled */
+ rh->response_code,
+ "{s:s}",
+ "error",
+ rh->data);
+}
+
+
+/**
+ * Send a response for an invalid argument.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is missing
+ * @return a GNUnet result code
+ */
+static int
+request_arg_invalid (struct MHD_Connection *connection,
+ const char *param_name)
+{
+ json_t *json;
+ json = json_pack ("{ s:s, s:s }",
+ "error", "invalid parameter",
+ "parameter", param_name);
+ if (MHD_YES != send_response_json (connection, json, MHD_HTTP_BAD_REQUEST))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_NO;
+}
+
+
+/**
+ * Get a GET paramater that is a string,
+ * or send an error response if the parameter is missing.
+ *
+ * @param connection the connection to get the parameter from /
+ * send the error response to
+ * @param param_name the parameter name
+ * @param str pointer to store the parameter string,
+ * must be freed by the caller
+ * @return GNUNET_YES if the parameter is present and valid,
+ * GNUNET_NO if the parameter is missing
+ * GNUNET_SYSERR on internal error
+ */
+static int
+request_arg_require_string (struct MHD_Connection *connection,
+ const char *param_name,
+ const char **str)
+{
+ *str = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, param_name);
+ if (NULL == *str)
+ {
+ if (MHD_NO ==
+ request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ "{ s:s, s:s }",
+ "error", "missing parameter",
+ "parameter", param_name))
+ return GNUNET_SYSERR;
+ return GNUNET_NO;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Extraxt base32crockford encoded data from request.
+ *
+ * Queues an error response to the connection if the parameter is missing or
+ * invalid.
+ *
+ * @param connection the MHD connection
+ * @param param_name the name of the parameter with the key
+ * @param[out] out_data pointer to store the result
+ * @param out_size expected size of data
+ * @return
+ * GNUNET_YES if the the argument is present
+ * GNUNET_NO if the argument is absent or malformed
+ * GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+int
+TALER_MINT_mhd_request_arg_data (struct MHD_Connection *connection,
+ const char *param_name,
+ void *out_data,
+ size_t out_size)
+{
+ const char *str;
+ int ret;
+
+ if (GNUNET_OK != (ret = request_arg_require_string (connection, param_name, &str)))
+ return ret;
+ if (GNUNET_OK != GNUNET_STRINGS_string_to_data (str, strlen (str), out_data, out_size))
+ return request_arg_invalid (connection, param_name);
+ return GNUNET_OK;
+}
+
+
+
+/* end of taler-mint-httpd_mhd.c */
diff --git a/src/mint/taler-mint-httpd_mhd.h b/src/mint/taler-mint-httpd_mhd.h
new file mode 100644
index 00000000..29ab7f64
--- /dev/null
+++ b/src/mint/taler-mint-httpd_mhd.h
@@ -0,0 +1,132 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-httpd_mhd.h
+ * @brief helpers for MHD interaction
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_MHD_H
+#define TALER_MINT_HTTPD_MHD_H
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Function to call to handle the request by sending
+ * back static data from the @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_static_response (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Function to call to handle the request by sending
+ * back a redirect to the AGPL source code.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_agpl_redirect (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply from varargs.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param response_code HTTP response code to use
+ * @param do_cache can the response be cached? (0: no, 1: yes)
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD result code
+ */
+int
+TALER_MINT_helper_send_json_pack (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void *connection_cls,
+ int response_code,
+ int do_cache,
+ const char *fmt,
+ ...);
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply with an error message from @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_send_json_pack_error (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Extraxt base32crockford encoded data from request.
+ *
+ * Queues an error response to the connection if the parameter is missing or
+ * invalid.
+ *
+ * @param connection the MHD connection
+ * @param param_name the name of the parameter with the key
+ * @param[out] out_data pointer to store the result
+ * @param out_size expected size of data
+ * @return
+ * GNUNET_YES if the the argument is present
+ * GNUNET_NO if the argument is absent or malformed
+ * GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+int
+TALER_MINT_mhd_request_arg_data (struct MHD_Connection *connection,
+ const char *param_name,
+ void *out_data,
+ size_t out_size);
+
+#endif
diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c
new file mode 100644
index 00000000..8121bb23
--- /dev/null
+++ b/src/mint/taler-mint-httpd_refresh.c
@@ -0,0 +1,1497 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_refresh.c
+ * @brief Handle /refresh/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "mint.h"
+#include "mint_db.h"
+#include "taler_types.h"
+#include "taler_signatures.h"
+#include "taler_rsa.h"
+#include "taler_json_lib.h"
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd_keys.h"
+#include "taler-mint-httpd_mhd.h"
+#include "taler-mint-httpd_refresh.h"
+
+
+/**
+ * Sign the message in @a purpose with the mint's signing
+ * key and encode the signature as a JSON object.
+ *
+ * @param purpose the message to sign
+ * @return signature as JSON object
+ */
+static json_t *
+sign_as_json (struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)
+{
+ json_t *sig_json;
+ struct GNUNET_CRYPTO_EddsaSignature sig;
+ struct MintKeyState *key_state;
+
+ key_state = TALER_MINT_key_state_acquire ();
+
+ sig_json = json_object ();
+
+ GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv,
+ purpose,
+ &sig));
+
+ TALER_MINT_key_state_release (key_state);
+
+ json_object_set (sig_json, "sig", TALER_JSON_from_data (&sig, sizeof (struct GNUNET_CRYPTO_EddsaSignature)));
+ json_object_set (sig_json, "purpose", json_integer (ntohl (purpose->purpose)));
+ json_object_set (sig_json, "size", json_integer (ntohl (purpose->size)));
+
+ return sig_json;
+}
+
+
+static int
+link_iter (void *cls,
+ const struct LinkDataEnc *link_data_enc,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub,
+ const struct TALER_RSA_Signature *ev_sig)
+{
+ json_t *list = cls;
+ json_t *obj = json_object ();
+
+ json_array_append_new (list, obj);
+
+ json_object_set_new (obj, "link_enc",
+ TALER_JSON_from_data (link_data_enc,
+ sizeof (struct LinkDataEnc)));
+
+ json_object_set_new (obj, "denom_pub",
+ TALER_JSON_from_data (denom_pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)));
+
+ json_object_set_new (obj, "ev_sig",
+ TALER_JSON_from_data (ev_sig,
+ sizeof (struct TALER_RSA_Signature)));
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Insert all requested denominations into the db, and compute the
+ * required cost of the denominations, including fees.
+ *
+ * @param connection the connection to send an error response to
+ * @param db_conn the database connection
+ * @param key_state the mint's key state to use
+ * @param session_pub the refresh session public key
+ * @param root the request JSON object
+ * @param hash_context the hash context where accepted
+ * denominations will be hased into
+ * @param r_amount the sum of the cost (value+fee) for
+ * all requested coins
+ * @return FIXME!
+ */
+static int
+refresh_accept_denoms (struct MHD_Connection *connection,
+ PGconn *db_conn,
+ const struct MintKeyState *key_state,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const json_t *root,
+ struct TALER_HashContext *hash_context,
+ struct TALER_Amount *r_amount)
+{
+ unsigned i;
+ int res;
+ json_t *new_denoms;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "new_denoms",
+ JNAV_RET_TYPED_JSON,
+ JSON_ARRAY,
+ &new_denoms);
+ if (GNUNET_OK != res)
+ return res;
+
+ memset (r_amount, 0, sizeof (struct TALER_Amount));
+
+ for (i = 0; i < json_array_size (new_denoms); i++)
+ {
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ int res;
+ struct TALER_MINT_DenomKeyIssue *dki;
+ struct TALER_Amount cost;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "new_denoms",
+ JNAV_INDEX, (int) i,
+ JNAV_RET_DATA,
+ &denom_pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded));
+
+ if (GNUNET_OK != res)
+ return res;
+
+ dki = TALER_MINT_get_denom_key (key_state, &denom_pub);
+
+ TALER_hash_context_read (hash_context,
+ &denom_pub, sizeof (struct TALER_RSA_PublicKeyBinaryEncoded));
+
+ cost = TALER_amount_add (TALER_amount_ntoh (dki->value),
+ TALER_amount_ntoh (dki->fee_withdraw));
+
+ *r_amount = TALER_amount_add (cost, *r_amount);
+
+ /* Insert the requested coin into the DB, so we'll know later
+ * what denomination the request had */
+
+ if (GNUNET_OK !=
+ TALER_MINT_DB_insert_refresh_order (db_conn,
+ i,
+ session_pub,
+ &denom_pub))
+ return res; // ???
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Get an amount in the mint's currency
+ * that is zero.
+ *
+ * @return zero amount in the mint's currency
+ */
+static struct TALER_Amount
+mint_amount_native_zero ()
+{
+ struct TALER_Amount amount;
+
+ memset (&amount, 0, sizeof (amount));
+ // FIXME: load from config
+ memcpy (amount.currency, "EUR", 3);
+
+ return amount;
+}
+
+
+static int
+check_confirm_signature (struct MHD_Connection *connection,
+ json_t *coin_info,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub)
+{
+ struct RefreshMeltConfirmSignRequestBody body;
+ struct GNUNET_CRYPTO_EcdsaSignature sig;
+ int res;
+
+ body.purpose.size = htonl (sizeof (struct RefreshMeltConfirmSignRequestBody));
+ body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_CONFIRM);
+ body.session_pub = *session_pub;
+
+ res = request_json_require_nav (connection, coin_info,
+ JNAV_FIELD, "confirm_sig",
+ JNAV_RET_DATA,
+ &sig,
+ sizeof (struct GNUNET_CRYPTO_EcdsaSignature));
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return res;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_CONFIRM,
+ &body.purpose,
+ &sig,
+ coin_pub))
+ {
+ if (MHD_YES !=
+ request_send_json_pack (connection,
+ MHD_HTTP_UNAUTHORIZED,
+ "{s:s}",
+ "error", "signature invalid"))
+ return GNUNET_SYSERR;
+ return GNUNET_NO;
+ }
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Extract public coin information from a JSON object.
+ *
+ * @param connection the connection to send error responses to
+ * @param root the JSON object to extract the coin info from
+ * @return GNUNET_YES if coin public info in JSON was valid
+ * GNUNET_NO otherwise
+ * GNUNET_SYSERR on internal error
+ */
+static int
+request_json_require_coin_public_info (struct MHD_Connection *connection,
+ json_t *root,
+ struct TALER_CoinPublicInfo *r_public_info)
+{
+ int ret;
+
+ GNUNET_assert (NULL != root);
+
+ ret = request_json_require_nav (connection, root,
+ JNAV_FIELD, "coin_pub",
+ JNAV_RET_DATA,
+ &r_public_info->coin_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+ if (GNUNET_OK != ret)
+ return ret;
+
+ ret = request_json_require_nav (connection, root,
+ JNAV_FIELD, "denom_sig",
+ JNAV_RET_DATA,
+ &r_public_info->denom_sig,
+ sizeof (struct TALER_RSA_Signature));
+ if (GNUNET_OK != ret)
+ return ret;
+
+ ret = request_json_require_nav (connection, root,
+ JNAV_FIELD, "denom_pub",
+ JNAV_RET_DATA,
+ &r_public_info->denom_pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded));
+ if (GNUNET_OK != ret)
+ return ret;
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Parse coin melt requests from a JSON object and write them to
+ * the database.
+ *
+ * @param connection the connection to send errors to
+ * @param db_conn the database connection
+ * @param key_state the mint's key state
+ * @param session_pub the refresh session's public key
+ * @param root the JSON object
+ * @param hash_context the hash context that will receive
+ * the coin public keys of the melted coin
+ * @return a GNUnet result code, GNUNET_OK on success,
+ * GNUNET_NO if an error message was generated,
+ * GNUNET_SYSERR on internal errors (no response generated)
+ */
+static int
+refresh_accept_melts (struct MHD_Connection *connection,
+ PGconn *db_conn,
+ const struct MintKeyState *key_state,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ json_t *root,
+ struct TALER_HashContext *hash_context,
+ struct TALER_Amount *r_melt_balance)
+{
+ size_t i;
+ int res;
+ json_t *melt_coins;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "melt_coins",
+ JNAV_RET_TYPED_JSON,
+ JSON_ARRAY,
+ &melt_coins);
+ if (GNUNET_OK != res)
+ return res;
+
+ memset (r_melt_balance, 0, sizeof (struct TALER_Amount));
+
+ for (i = 0; i < json_array_size (melt_coins); i++)
+ {
+ struct TALER_CoinPublicInfo coin_public_info;
+ struct TALER_MINT_DenomKeyIssue *dki;
+ struct KnownCoin known_coin;
+ // money the customer gets by melting the current coin
+ struct TALER_Amount coin_gain;
+
+ res = request_json_require_coin_public_info (connection,
+ json_array_get (melt_coins, i),
+ &coin_public_info);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return res;
+ }
+
+ if (GNUNET_OK != (res = check_confirm_signature (connection,
+ json_array_get (melt_coins, i),
+ &coin_public_info.coin_pub,
+ session_pub)))
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return res;
+ }
+
+ TALER_hash_context_read (hash_context,
+ &coin_public_info.coin_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+
+ dki = TALER_MINT_get_denom_key (key_state, &coin_public_info.denom_pub);
+
+ if (NULL == dki)
+ return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "denom not found"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+
+ if (GNUNET_OK != TALER_MINT_test_coin_valid (key_state, &coin_public_info))
+ return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "coin invalid"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+
+ res = TALER_MINT_DB_get_known_coin (db_conn, &coin_public_info.coin_pub,
+ &known_coin);
+
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_YES == res)
+ {
+ if (GNUNET_YES == known_coin.is_refreshed)
+ return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "coin already refreshed")) ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ else
+ {
+ known_coin.expended_balance = mint_amount_native_zero ();
+ known_coin.public_info = coin_public_info;
+ }
+
+ known_coin.is_refreshed = GNUNET_YES;
+ known_coin.refresh_session_pub = *session_pub;
+
+ if (GNUNET_OK != TALER_MINT_DB_upsert_known_coin (db_conn, &known_coin))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_insert_refresh_melt (db_conn, session_pub, i,
+ &coin_public_info.coin_pub,
+ &coin_public_info.denom_pub))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ coin_gain = TALER_amount_ntoh (dki->value);
+ coin_gain = TALER_amount_subtract (coin_gain, known_coin.expended_balance);
+
+ /* Refuse to refresh when the coin does not have enough money left to
+ * pay the refreshing fees of the coin. */
+
+ if (TALER_amount_cmp (coin_gain, TALER_amount_ntoh (dki->fee_refresh)) < 0)
+ return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "depleted")) ? GNUNET_NO : GNUNET_SYSERR;
+
+ coin_gain = TALER_amount_subtract (coin_gain, TALER_amount_ntoh (dki->fee_refresh));
+
+ *r_melt_balance = TALER_amount_add (*r_melt_balance, coin_gain);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Send a response for "/refresh/melt".
+ *
+ * @param connection the connection to send the response to
+ * @param db_conn the database connection to fetch values from
+ * @param session_pub the refresh session public key.
+ * @return a MHD result code
+ */
+static int
+helper_refresh_send_melt_response (struct MHD_Connection *connection,
+ PGconn *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub)
+{
+ struct RefreshSession session;
+ int res;
+ json_t *root;
+ json_t *list;
+ struct TALER_HashContext hash_context;
+
+ if (GNUNET_OK !=
+ (res = TALER_MINT_DB_get_refresh_session (db_conn,
+ session_pub,
+ &session)))
+ {
+ // FIXME: send internal error
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ root = json_object ();
+ list = json_array ();
+ json_object_set_new (root, "blind_session_pubs", list);
+
+ TALER_hash_context_start (&hash_context);
+
+ {
+ struct RefreshMeltResponseSignatureBody body;
+ json_t *sig_json;
+
+ body.purpose.size = htonl (sizeof (struct RefreshMeltResponseSignatureBody));
+ body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_RESPONSE);
+ TALER_hash_context_finish (&hash_context, &body.melt_response_hash);
+ sig_json = sign_as_json (&body.purpose);
+ GNUNET_assert (NULL != sig_json);
+ json_object_set (root, "signature", sig_json);
+ }
+
+ return send_response_json (connection,
+ root,
+ MHD_HTTP_OK);
+}
+
+
+/**
+ * Verify a signature that is encoded in a JSON object
+ *
+ * @param connection the connection to send errors to
+ * @param root the JSON object with the signature
+ * @param the public key that the signature was created with
+ * @param purpose the signed message
+ * @return GNUNET_YES if the signature was valid
+ * GNUNET_NO if the signature was invalid
+ * GNUNET_SYSERR on internal error
+ */
+static int
+request_json_check_signature (struct MHD_Connection *connection,
+ json_t *root,
+ struct GNUNET_CRYPTO_EddsaPublicKey *pub,
+ struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)
+{
+ struct GNUNET_CRYPTO_EddsaSignature signature;
+ int size;
+ uint32_t purpose_num;
+ int res;
+ json_t *el;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "sig",
+ JNAV_RET_DATA,
+ &signature,
+ sizeof (struct GNUNET_CRYPTO_EddsaSignature));
+
+ if (GNUNET_OK != res)
+ return res;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "purpose",
+ JNAV_RET_TYPED_JSON,
+ JSON_INTEGER,
+ &el);
+
+ if (GNUNET_OK != res)
+ return res;
+
+ purpose_num = json_integer_value (el);
+
+ if (purpose_num != ntohl (purpose->purpose))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (purpose wrong)\n");
+ return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ "{s:s}",
+ "error", "signature invalid (purpose)");
+ }
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "size",
+ JNAV_RET_TYPED_JSON,
+ JSON_INTEGER,
+ &el);
+
+ if (GNUNET_OK != res)
+ return res;
+
+ size = json_integer_value (el);
+
+ if (size != ntohl (purpose->size))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (size wrong)\n");
+ return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ GNUNET_NO, GNUNET_SYSERR,
+ "{s:s}",
+ "error", "signature invalid (size)");
+ }
+
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (purpose_num,
+ purpose,
+ &signature,
+ pub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (did not verify)\n");
+ return request_send_json_pack (connection, MHD_HTTP_UNAUTHORIZED,
+ "{s:s}",
+ "error", "invalid signature (verification)");
+ }
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle a "/refresh/melt" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ json_t *root;
+ PGconn *db_conn;
+ struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub;
+ int res;
+ struct MintKeyState *key_state;
+ struct TALER_Amount requested_cost;
+ struct TALER_Amount melt_balance;
+ struct TALER_HashContext hash_context;
+ struct GNUNET_HashCode melt_hash;
+
+ res = process_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size, &root);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return MHD_YES;
+
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ return GNUNET_SYSERR;
+
+ /* session_pub field must always be present */
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "session_pub",
+ JNAV_RET_DATA,
+ &refresh_session_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return MHD_YES;
+
+ /* Send response immediately if we already know the session.
+ * Do _not_ care about fields other than session_pub in this case. */
+
+ res = TALER_MINT_DB_get_refresh_session (db_conn,
+ &refresh_session_pub,
+ NULL);
+ if (GNUNET_YES == res)
+ return helper_refresh_send_melt_response (connection,
+ db_conn,
+ &refresh_session_pub);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* We incrementally update the db with other parameters in a transaction.
+ * The transaction is aborted if some parameter does not validate. */
+
+ if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_create_refresh_session (db_conn,
+ &refresh_session_pub))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ TALER_MINT_DB_rollback (db_conn);
+ return MHD_NO;
+ }
+
+ /* The next two operations must see the same key state,
+ * thus we acquire it here. */
+
+ key_state = TALER_MINT_key_state_acquire ();
+
+ /* Write requested denominations to the DB,
+ * and sum the costs (value plus fees) */
+
+ TALER_hash_context_start (&hash_context);
+
+ if (GNUNET_OK != (res = refresh_accept_denoms (connection, db_conn, key_state,
+ &refresh_session_pub, root,
+ &hash_context,
+ &requested_cost)))
+ {
+ TALER_MINT_key_state_release (key_state);
+ TALER_MINT_DB_rollback (db_conn);
+ // FIXME: hash_context_end?
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ /* Write old coins to db and sum their value */
+
+ if (GNUNET_OK != (res = refresh_accept_melts (connection, db_conn, key_state,
+ &refresh_session_pub, root,
+ &hash_context,
+ &melt_balance)))
+ {
+ TALER_MINT_key_state_release (key_state);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ TALER_hash_context_finish (&hash_context, &melt_hash);
+
+ TALER_MINT_key_state_release (key_state);
+
+ /* check that signature from the session public key is ok */
+ {
+ struct RefreshMeltSignatureBody body;
+ json_t *melt_sig_json;
+
+ melt_sig_json = json_object_get (root, "melt_signature");
+ if (NULL == melt_sig_json)
+ {
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return request_send_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s}",
+ "error", "melt_signature missing");
+ }
+
+ body.melt_hash = melt_hash;
+ body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT);
+ body.purpose.size = htonl (sizeof (struct RefreshMeltSignatureBody));
+
+ if (GNUNET_OK != (res = request_json_check_signature (connection,
+ melt_sig_json,
+ &refresh_session_pub,
+ &body.purpose)))
+ {
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ }
+
+
+ /* Request is only ok if cost of requested coins
+ * does not exceed value of melted coins. */
+
+ // FIXME: also, consider fees?
+ if (TALER_amount_cmp (melt_balance, requested_cost) < 0)
+ {
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+
+ return request_send_json_pack (connection, MHD_HTTP_FORBIDDEN,
+ "{s:s}",
+ "error", "not enough coins melted");
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_commit (db_conn))
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ return helper_refresh_send_melt_response (connection,
+ db_conn,
+ &refresh_session_pub);
+}
+
+
+/**
+ * Send a response to a "/refresh/commit" request.
+ *
+ * @param connection the connection to send the response to
+ * @param db_conn the mint database
+ * @param refresh_session the refresh session
+ * @return a MHD status code
+ */
+static int
+refresh_send_commit_response (struct MHD_Connection *connection,
+ PGconn *db_conn,
+ struct RefreshSession *refresh_session)
+{
+ struct RefreshCommitResponseSignatureBody body;
+ json_t *sig_json;
+
+ body.purpose.size = htonl (sizeof (struct RefreshCommitResponseSignatureBody));
+ body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_COMMIT_RESPONSE);
+ body.noreveal_index = htons (refresh_session->noreveal_index);
+ sig_json = sign_as_json (&body.purpose);
+ GNUNET_assert (NULL != sig_json);
+ return request_send_json_pack (connection, MHD_HTTP_OK,
+ "{s:i, s:o}",
+ "noreveal_index", (int) refresh_session->noreveal_index,
+ "signature", sig_json);
+}
+
+
+/**
+ * Handle a "/refresh/commit" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_commit (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub;
+ int res;
+ PGconn *db_conn;
+ struct RefreshSession refresh_session;
+ int i;
+ struct GNUNET_HashCode commit_hash;
+ struct TALER_HashContext hash_context;
+ json_t *root;
+
+ res = process_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size, &root);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return MHD_YES;
+
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "session_pub",
+ JNAV_RET_DATA,
+ &refresh_session_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* Send response immediately if we already know the session.
+ * Do _not_ care about fields other than session_pub in this case. */
+
+ res = TALER_MINT_DB_get_refresh_session (db_conn,
+ &refresh_session_pub,
+ &refresh_session);
+ if ( (GNUNET_YES == res) &&
+ (GNUNET_YES == refresh_session.has_commit_sig) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "sending cached commit response\n");
+ res = refresh_send_commit_response (connection,
+ db_conn,
+ &refresh_session);
+ GNUNET_break (res != GNUNET_SYSERR);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* Re-fetch the session information from the database,
+ * in case a concurrent transaction modified it. */
+
+ res = TALER_MINT_DB_get_refresh_session (db_conn,
+ &refresh_session_pub,
+ &refresh_session);
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (GNUNET_SYSERR != res);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return MHD_NO;
+ }
+
+ TALER_hash_context_start (&hash_context);
+
+ for (i = 0; i < refresh_session.kappa; i++)
+ {
+ unsigned int j;
+
+ for (j = 0; j < refresh_session.num_newcoins; j++)
+ {
+ struct RefreshCommitCoin commit_coin;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "coin_evs",
+ JNAV_INDEX, (int) i,
+ JNAV_INDEX, (int) j,
+ JNAV_RET_DATA,
+ &commit_coin.coin_ev,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose));
+
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ TALER_hash_context_read (&hash_context,
+ &commit_coin.coin_ev,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose));
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "link_encs",
+ JNAV_INDEX, (int) i,
+ JNAV_INDEX, (int) j,
+ JNAV_RET_DATA,
+ commit_coin.link_enc,
+ TALER_REFRESH_LINK_LENGTH);
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ TALER_hash_context_read (&hash_context,
+ commit_coin.link_enc,
+ TALER_REFRESH_LINK_LENGTH);
+
+ commit_coin.cnc_index = i;
+ commit_coin.newcoin_index = j;
+ commit_coin.session_pub = refresh_session_pub;
+
+ if (GNUNET_OK !=
+ TALER_MINT_DB_insert_refresh_commit_coin (db_conn,
+ &commit_coin))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return MHD_NO;
+ }
+ }
+ }
+
+ for (i = 0; i < refresh_session.kappa; i++)
+ {
+ unsigned int j;
+ for (j = 0; j < refresh_session.num_oldcoins; j++)
+ {
+ struct RefreshCommitLink commit_link;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "transfer_pubs",
+ JNAV_INDEX, (int) i,
+ JNAV_INDEX, (int) j,
+ JNAV_RET_DATA,
+ &commit_link.transfer_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ TALER_hash_context_read (&hash_context,
+ &commit_link.transfer_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "secret_encs",
+ JNAV_INDEX, (int) i,
+ JNAV_INDEX, (int) j,
+ JNAV_RET_DATA,
+ commit_link.shared_secret_enc,
+ TALER_REFRESH_SHARED_SECRET_LENGTH);
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ TALER_hash_context_read (&hash_context,
+ commit_link.shared_secret_enc,
+ TALER_REFRESH_SHARED_SECRET_LENGTH);
+
+ commit_link.cnc_index = i;
+ commit_link.oldcoin_index = j;
+ commit_link.session_pub = refresh_session_pub;
+
+ if (GNUNET_OK != TALER_MINT_DB_insert_refresh_commit_link (db_conn, &commit_link))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return MHD_NO;
+ }
+ }
+ }
+
+ TALER_hash_context_finish (&hash_context, &commit_hash);
+
+ {
+ struct RefreshCommitSignatureBody body;
+ json_t *commit_sig_json;
+
+ commit_sig_json = json_object_get (root, "commit_signature");
+ if (NULL == commit_sig_json)
+ {
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ "{s:s}",
+ "error", "commit_signature missing");
+ }
+
+ body.commit_hash = commit_hash;
+
+ body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_COMMIT);
+ body.purpose.size = htonl (sizeof (struct RefreshCommitSignatureBody));
+
+ if (GNUNET_OK != (res = request_json_check_signature (connection,
+ commit_sig_json,
+ &refresh_session_pub,
+ &body.purpose)))
+ {
+ GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn));
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_commit (db_conn))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ return refresh_send_commit_response (connection, db_conn, &refresh_session);
+}
+
+
+/**
+ * Send response for "/refresh/reveal".
+ *
+ * @param connection the MHD connection
+ * @param db_conn the connection to the mint's db
+ * @param refresh_session_pub the refresh session's public key
+ * @return a MHD result code
+ */
+static int
+helper_refresh_reveal_send_response (struct MHD_Connection *connection,
+ PGconn *db_conn,
+ struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub)
+{
+ int res;
+ int newcoin_index;
+ struct RefreshSession refresh_session;
+ json_t *root;
+ json_t *list;
+
+ res = TALER_MINT_DB_get_refresh_session (db_conn,
+ refresh_session_pub,
+ &refresh_session);
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ GNUNET_assert (0 != refresh_session.reveal_ok);
+
+ root = json_object ();
+ list = json_array ();
+ json_object_set_new (root, "ev_sigs", list);
+
+ for (newcoin_index = 0; newcoin_index < refresh_session.num_newcoins; newcoin_index++)
+ {
+ struct TALER_RSA_Signature ev_sig;
+
+ res = TALER_MINT_DB_get_refresh_collectable (db_conn,
+ newcoin_index,
+ refresh_session_pub,
+ &ev_sig);
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ json_array_append_new (list,
+ TALER_JSON_from_data (&ev_sig,
+ sizeof (struct TALER_RSA_Signature)));
+ }
+ return send_response_json (connection,
+ root,
+ MHD_HTTP_OK);
+}
+
+
+/**
+ * Handle a "/refresh/reveal" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_reveal (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub;
+ int res;
+ PGconn *db_conn;
+ struct RefreshSession refresh_session;
+ struct MintKeyState *key_state;
+ int i;
+ int j;
+ json_t *root;
+
+ res = process_post_json (connection,
+ connection_cls,
+ upload_data, upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return MHD_YES;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "session_pub",
+ JNAV_RET_DATA,
+ &refresh_session_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return res;
+ }
+
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* Send response immediately if we already know the session,
+ * and the session commited already.
+ * Do _not_ care about fields other than session_pub in this case. */
+
+ res = TALER_MINT_DB_get_refresh_session (db_conn, &refresh_session_pub, &refresh_session);
+ if (GNUNET_YES == res && 0 != refresh_session.reveal_ok)
+ return helper_refresh_reveal_send_response (connection, db_conn, &refresh_session_pub);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* Check that the transfer private keys match their commitments.
+ * Then derive the shared secret for each kappa, and check that they match. */
+
+ for (i = 0; i < refresh_session.kappa; i++)
+ {
+ struct GNUNET_HashCode last_shared_secret;
+ int secret_initialized = GNUNET_NO;
+
+ if (i == (refresh_session.noreveal_index % refresh_session.kappa))
+ continue;
+
+ for (j = 0; j < refresh_session.num_oldcoins; j++)
+ {
+ struct GNUNET_CRYPTO_EcdsaPrivateKey transfer_priv;
+ struct RefreshCommitLink commit_link;
+ struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub;
+ struct GNUNET_HashCode transfer_secret;
+ struct GNUNET_HashCode shared_secret;
+
+ res = request_json_require_nav (connection, root,
+ JNAV_FIELD, "transfer_privs",
+ JNAV_INDEX, (int) i,
+ JNAV_INDEX, (int) j,
+ JNAV_RET_DATA,
+ &transfer_priv,
+ sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey));
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ return res;
+ }
+
+ res = TALER_MINT_DB_get_refresh_commit_link (db_conn,
+ &refresh_session_pub,
+ i, j,
+ &commit_link);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ res = TALER_MINT_DB_get_refresh_melt (db_conn, &refresh_session_pub, j, &coin_pub);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* We're converting key types here, which is not very nice
+ * but necessary and harmless (keys will be thrown away later). */
+ if (GNUNET_OK != GNUNET_CRYPTO_ecc_ecdh ((struct GNUNET_CRYPTO_EcdhePrivateKey *) &transfer_priv,
+ (struct GNUNET_CRYPTO_EcdhePublicKey *) &coin_pub,
+ &transfer_secret))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 >= TALER_refresh_decrypt (commit_link.shared_secret_enc, TALER_REFRESH_SHARED_SECRET_LENGTH,
+ &transfer_secret, &shared_secret))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "decryption failed\n");
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_NO == secret_initialized)
+ {
+ secret_initialized = GNUNET_YES;
+ last_shared_secret = shared_secret;
+ }
+ else if (0 != memcmp (&shared_secret, &last_shared_secret, sizeof (struct GNUNET_HashCode)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "shared secrets do not match\n");
+ return GNUNET_SYSERR;
+ }
+
+ {
+ struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub_check;
+ GNUNET_CRYPTO_ecdsa_key_get_public (&transfer_priv, &transfer_pub_check);
+ if (0 != memcmp (&transfer_pub_check, &commit_link.transfer_pub, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "transfer keys do not match\n");
+ return GNUNET_SYSERR;
+ }
+ }
+ }
+
+ /* Check that the commitments for all new coins were correct */
+
+ for (j = 0; j < refresh_session.num_newcoins; j++)
+ {
+ struct RefreshCommitCoin commit_coin;
+ struct LinkData link_data;
+ struct TALER_RSA_BlindedSignaturePurpose *coin_ev_check;
+ struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub;
+ struct TALER_RSA_BlindingKey *bkey;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+
+ bkey = NULL;
+ res = TALER_MINT_DB_get_refresh_commit_coin (db_conn,
+ &refresh_session_pub,
+ i, j,
+ &commit_coin);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+
+ if (0 >= TALER_refresh_decrypt (commit_coin.link_enc, sizeof (struct LinkData),
+ &last_shared_secret, &link_data))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "decryption failed\n");
+ return GNUNET_SYSERR;
+ }
+
+ GNUNET_CRYPTO_ecdsa_key_get_public (&link_data.coin_priv, &coin_pub);
+ if (NULL == (bkey = TALER_RSA_blinding_key_decode (&link_data.bkey_enc)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid blinding key\n");
+ return GNUNET_SYSERR;
+ }
+ res = TALER_MINT_DB_get_refresh_order (db_conn, j, &refresh_session_pub, &denom_pub);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (NULL == (coin_ev_check =
+ TALER_RSA_message_blind (&coin_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
+ bkey,
+ &denom_pub)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "blind failed\n");
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != memcmp (&coin_ev_check,
+ &commit_coin.coin_ev,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "blind envelope does not match for kappa=%d, old=%d\n",
+ (int) i, (int) j);
+ return GNUNET_SYSERR;
+ }
+ }
+ }
+
+
+ if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn))
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ for (j = 0; j < refresh_session.num_newcoins; j++)
+ {
+ struct RefreshCommitCoin commit_coin;
+ struct TALER_RSA_PublicKeyBinaryEncoded denom_pub;
+ struct TALER_MINT_DenomKeyIssue *dki;
+ struct TALER_RSA_Signature ev_sig;
+
+ res = TALER_MINT_DB_get_refresh_commit_coin (db_conn,
+ &refresh_session_pub,
+ refresh_session.noreveal_index % refresh_session.kappa,
+ j,
+ &commit_coin);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ res = TALER_MINT_DB_get_refresh_order (db_conn, j, &refresh_session_pub, &denom_pub);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+
+ key_state = TALER_MINT_key_state_acquire ();
+ dki = TALER_MINT_get_denom_key (key_state, &denom_pub);
+ TALER_MINT_key_state_release (key_state);
+ if (NULL == dki)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_RSA_sign (dki->denom_priv,
+ &commit_coin.coin_ev,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose),
+ &ev_sig))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ res = TALER_MINT_DB_insert_refresh_collectable (db_conn,
+ j,
+ &refresh_session_pub,
+ &ev_sig);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ /* mark that reveal was successful */
+
+ res = TALER_MINT_DB_set_reveal_ok (db_conn, &refresh_session_pub);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != TALER_MINT_DB_commit (db_conn))
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ return helper_refresh_reveal_send_response (connection, db_conn, &refresh_session_pub);
+}
+
+
+/**
+ * Handle a "/refresh/link" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_link (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub;
+ int res;
+ json_t *root;
+ json_t *list;
+ PGconn *db_conn;
+ struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub;
+ struct SharedSecretEnc shared_secret_enc;
+
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "coin_pub",
+ &coin_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ list = json_array ();
+ root = json_object ();
+ json_object_set_new (root, "new_coins", list);
+
+ res = TALER_db_get_transfer (db_conn,
+ &coin_pub,
+ &transfer_pub,
+ &shared_secret_enc);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ {
+ return request_send_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "link data not found (transfer)");
+ }
+ GNUNET_assert (GNUNET_OK == res);
+
+ res = TALER_db_get_link (db_conn, &coin_pub, link_iter, list);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ {
+ return request_send_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "link data not found (link)");
+ }
+ GNUNET_assert (GNUNET_OK == res);
+ json_object_set_new (root, "transfer_pub",
+ TALER_JSON_from_data (&transfer_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)));
+ json_object_set_new (root, "secret_enc",
+ TALER_JSON_from_data (&shared_secret_enc,
+ sizeof (struct SharedSecretEnc)));
+ return send_response_json (connection, root, MHD_HTTP_OK);
+}
+
+
+/* end of taler-mint-httpd_refresh.c */
diff --git a/src/mint/taler-mint-httpd_refresh.h b/src/mint/taler-mint-httpd_refresh.h
new file mode 100644
index 00000000..20e7d97c
--- /dev/null
+++ b/src/mint/taler-mint-httpd_refresh.h
@@ -0,0 +1,103 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_refresh.h
+ * @brief Handle /refresh/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_REFRESH_H
+#define TALER_MINT_HTTPD_REFRESH_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Handle a "/refresh/melt" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Handle a "/refresh/commit" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_commit (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Handle a "/refresh/link" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_link (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Handle a "/refresh/reveal" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_refresh_reveal (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+#endif
diff --git a/src/mint/taler-mint-httpd_withdraw.c b/src/mint/taler-mint-httpd_withdraw.c
new file mode 100644
index 00000000..7ffa4570
--- /dev/null
+++ b/src/mint/taler-mint-httpd_withdraw.c
@@ -0,0 +1,400 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_withdraw.c
+ * @brief Handle /withdraw/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <libpq-fe.h>
+#include <pthread.h>
+#include "mint.h"
+#include "mint_db.h"
+#include "taler_types.h"
+#include "taler_signatures.h"
+#include "taler_rsa.h"
+#include "taler_json_lib.h"
+#include "taler_microhttpd_lib.h"
+#include "taler-mint-httpd_keys.h"
+#include "taler-mint-httpd_mhd.h"
+#include "taler-mint-httpd_withdraw.h"
+
+
+/**
+ * Convert a signature (with purpose) to
+ * a JSON object representation.
+ *
+ * @param purpose purpose of the signature
+ * @param signature the signature
+ * @return the JSON reporesentation of the signature with purpose
+ */
+static json_t *
+sig_to_json (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
+ const struct GNUNET_CRYPTO_EddsaSignature *signature)
+{
+ json_t *root;
+ json_t *el;
+
+ root = json_object ();
+
+ el = json_integer ((json_int_t) ntohl (purpose->size));
+ json_object_set_new (root, "size", el);
+
+ el = json_integer ((json_int_t) ntohl (purpose->purpose));
+ json_object_set_new (root, "purpose", el);
+
+ el = TALER_JSON_from_data (signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature));
+ json_object_set_new (root, "sig", el);
+
+ return root;
+}
+
+
+/**
+ * Sign a reserve's status with the current signing key.
+ *
+ * @param reserve the reserve to sign
+ * @param key_state the key state containing the current
+ * signing private key
+ */
+static void
+sign_reserve (struct Reserve *reserve,
+ struct MintKeyState *key_state)
+{
+ reserve->status_sign_pub = key_state->current_sign_key_issue.signkey_pub;
+ reserve->status_sig_purpose.purpose = htonl (TALER_SIGNATURE_RESERVE_STATUS);
+ reserve->status_sig_purpose.size = htonl (sizeof (struct Reserve) -
+ offsetof (struct Reserve, status_sig_purpose));
+ GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv,
+ &reserve->status_sig_purpose,
+ &reserve->status_sig);
+}
+
+
+/**
+ * Handle a "/withdraw/status" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_withdraw_status (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
+ PGconn *db_conn;
+ int res;
+ struct Reserve reserve;
+ struct MintKeyState *key_state;
+ int must_update = GNUNET_NO;
+ json_t *json;
+
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "reserve_pub",
+ &reserve_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ res = TALER_MINT_DB_get_reserve (db_conn,
+ &reserve_pub,
+ &reserve);
+ if (GNUNET_SYSERR == res)
+ return TALER_MINT_helper_send_json_pack (rh,
+ connection,
+ connection_cls,
+ 0 /* no caching */,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error",
+ "Reserve not found");
+ if (GNUNET_OK != res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ key_state = TALER_MINT_key_state_acquire ();
+ if (0 != memcmp (&key_state->current_sign_key_issue.signkey_pub,
+ &reserve.status_sign_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
+ {
+ sign_reserve (&reserve, key_state);
+ must_update = GNUNET_YES;
+ }
+ if ((GNUNET_YES == must_update) &&
+ (GNUNET_OK != TALER_MINT_DB_update_reserve (db_conn, &reserve, !must_update)))
+ {
+ GNUNET_break (0);
+ return MHD_YES;
+ }
+
+ /* Convert the public information of a reserve (i.e.
+ excluding private key) to a JSON object. */
+ json = json_object ();
+ json_object_set_new (json,
+ "balance",
+ TALER_JSON_from_amount (TALER_amount_ntoh (reserve.balance)));
+ json_object_set_new (json,
+ "expiration",
+ TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (reserve.expiration)));
+ json_object_set_new (json,
+ "signature",
+ sig_to_json (&reserve.status_sig_purpose,
+ &reserve.status_sig));
+
+ return send_response_json (connection,
+ json,
+ MHD_HTTP_OK);
+}
+
+
+/**
+ * Send positive, normal response for "/withdraw/sign".
+ *
+ * @param connection the connection to send the response to
+ * @param collectable the collectable blindcoin (i.e. the blindly signed coin)
+ * @return a MHD result code
+ */
+static int
+helper_withdraw_sign_send_reply (struct MHD_Connection *connection,
+ const struct CollectableBlindcoin *collectable)
+{
+ json_t *root = json_object ();
+
+ json_object_set_new (root, "ev_sig",
+ TALER_JSON_from_data (&collectable->ev_sig,
+ sizeof (struct TALER_RSA_Signature)));
+ return send_response_json (connection,
+ root,
+ MHD_HTTP_OK);
+}
+
+
+/**
+ * Handle a "/withdraw/sign" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct TALER_WithdrawRequest wsrd;
+ int res;
+ PGconn *db_conn;
+ struct Reserve reserve;
+ struct MintKeyState *key_state;
+ struct CollectableBlindcoin collectable;
+ struct TALER_MINT_DenomKeyIssue *dki;
+ struct TALER_RSA_Signature ev_sig;
+ struct TALER_Amount amount_required;
+
+ memset (&wsrd,
+ 0,
+ sizeof (struct TALER_WithdrawRequest));
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "reserve_pub",
+ &wsrd.reserve_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "denom_pub",
+ &wsrd.denomination_pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "coin_ev",
+ &wsrd.coin_envelope,
+ sizeof (struct TALER_RSA_Signature));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+ res = TALER_MINT_mhd_request_arg_data (connection,
+ "reserve_sig",
+ &wsrd.sig,
+ sizeof (struct GNUNET_CRYPTO_EddsaSignature));
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_OK != res)
+ return MHD_YES;
+
+ if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
+ {
+ // FIXME: return 'internal error'?
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ res = TALER_MINT_DB_get_collectable_blindcoin (db_conn,
+ &wsrd.coin_envelope,
+ &collectable);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* Don't sign again if we have already signed the coin */
+ if (GNUNET_YES == res)
+ return helper_withdraw_sign_send_reply (connection,
+ &collectable);
+ GNUNET_assert (GNUNET_NO == res);
+ res = TALER_MINT_DB_get_reserve (db_conn,
+ &wsrd.reserve_pub,
+ &reserve);
+ if (GNUNET_SYSERR == res)
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (GNUNET_NO == res)
+ return request_send_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "Reserve not found");
+
+ // fill out all the missing info in the request before
+ // we can check the signature on the request
+
+ wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WITHDRAW);
+ wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequest) -
+ offsetof (struct TALER_WithdrawRequest, purpose));
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WITHDRAW,
+ &wsrd.purpose,
+ &wsrd.sig,
+ &wsrd.reserve_pub))
+ return request_send_json_pack (connection,
+ MHD_HTTP_UNAUTHORIZED,
+ "{s:s}",
+ "error", "Invalid Signature");
+
+ key_state = TALER_MINT_key_state_acquire ();
+ dki = TALER_MINT_get_denom_key (key_state,
+ &wsrd.denomination_pub);
+ TALER_MINT_key_state_release (key_state);
+ if (NULL == dki)
+ return request_send_json_pack (connection, MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "Denomination not found");
+
+ amount_required = TALER_amount_ntoh (dki->value);
+ amount_required = TALER_amount_add (amount_required,
+ TALER_amount_ntoh (dki->fee_withdraw));
+
+ if (0 < TALER_amount_cmp (amount_required,
+ TALER_amount_ntoh (reserve.balance)))
+ return request_send_json_pack (connection,
+ MHD_HTTP_PAYMENT_REQUIRED,
+ "{s:s}",
+ "error", "Insufficient funds");
+ if (GNUNET_OK != TALER_RSA_sign (dki->denom_priv,
+ &wsrd.coin_envelope,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose),
+ &ev_sig))
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ reserve.balance = TALER_amount_hton (TALER_amount_subtract (TALER_amount_ntoh (reserve.balance),
+ amount_required));
+ if (GNUNET_OK !=
+ TALER_MINT_DB_update_reserve (db_conn,
+ &reserve,
+ GNUNET_YES))
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ collectable.ev = wsrd.coin_envelope;
+ collectable.ev_sig = ev_sig;
+ collectable.reserve_pub = wsrd.reserve_pub;
+ collectable.reserve_sig = wsrd.sig;
+ if (GNUNET_OK !=
+ TALER_MINT_DB_insert_collectable_blindcoin (db_conn,
+ &collectable))
+ {
+ // FIXME: return 'internal error'
+ GNUNET_break (0);
+ return GNUNET_NO;;
+ }
+ return helper_withdraw_sign_send_reply (connection,
+ &collectable);
+}
+
+/* end of taler-mint-httpd_withdraw.c */
diff --git a/src/mint/taler-mint-httpd_withdraw.h b/src/mint/taler-mint-httpd_withdraw.h
new file mode 100644
index 00000000..1d292ebd
--- /dev/null
+++ b/src/mint/taler-mint-httpd_withdraw.h
@@ -0,0 +1,65 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd_withdraw.h
+ * @brief Handle /withdraw/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_WITHDRAW_H
+#define TALER_MINT_HTTPD_WITHDRAW_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+/**
+ * Handle a "/withdraw/status" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_withdraw_status (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Handle a "/withdraw/sign" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[IN|OUT] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+#endif
diff --git a/src/mint/taler-mint-keycheck.c b/src/mint/taler-mint-keycheck.c
new file mode 100644
index 00000000..c6186859
--- /dev/null
+++ b/src/mint/taler-mint-keycheck.c
@@ -0,0 +1,169 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-keycheck.c
+ * @brief Check mint keys for validity.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#include <platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "mint.h"
+#include "taler_signatures.h"
+
+
+static char *mintdir;
+static struct GNUNET_CONFIGURATION_Handle *kcfg;
+
+
+static int
+signkeys_iter (void *cls, const struct TALER_MINT_SignKeyIssue *ski)
+{
+ struct GNUNET_TIME_Absolute start;
+
+ printf ("iterating over key for start time %s\n",
+ GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (ski->start)));
+
+ start = GNUNET_TIME_absolute_ntoh (ski->start);
+
+ if (ntohl (ski->purpose.size) !=
+ (sizeof (struct TALER_MINT_SignKeyIssue) - offsetof (struct TALER_MINT_SignKeyIssue, purpose)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signkey with start %s has invalid purpose field (timestamp: %llu)\n",
+ GNUNET_STRINGS_absolute_time_to_string (start),
+ (long long) start.abs_value_us);
+ return GNUNET_SYSERR;
+ }
+
+
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNKEY,
+ &ski->purpose,
+ &ski->signature,
+ &ski->master_pub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signkey with start %s has invalid signature (timestamp: %llu)\n",
+ GNUNET_STRINGS_absolute_time_to_string (start),
+ (long long) start.abs_value_us);
+ return GNUNET_SYSERR;
+ }
+ printf ("key valid\n");
+ return GNUNET_OK;
+}
+
+
+static int
+mint_signkeys_check ()
+{
+ if (0 > TALER_MINT_signkeys_iterate (mintdir, signkeys_iter, NULL))
+ return GNUNET_NO;
+ return GNUNET_OK;
+}
+
+
+static int denomkeys_iter (void *cls,
+ const char *alias,
+ const struct TALER_MINT_DenomKeyIssue *dki)
+{
+ struct GNUNET_TIME_Absolute start;
+
+ start = GNUNET_TIME_absolute_ntoh (dki->start);
+
+ if (ntohl (dki->purpose.size) !=
+ (sizeof (struct TALER_MINT_DenomKeyIssue) - offsetof (struct TALER_MINT_DenomKeyIssue, purpose)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Denomkey for '%s' with start %s has invalid purpose field (timestamp: %llu)\n",
+ alias,
+ GNUNET_STRINGS_absolute_time_to_string (start),
+ (long long) start.abs_value_us);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOM,
+ &dki->purpose,
+ &dki->signature,
+ &dki->master))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Denomkey for '%s'with start %s has invalid signature (timestamp: %llu)\n",
+ alias,
+ GNUNET_STRINGS_absolute_time_to_string (start),
+ (long long) start.abs_value_us);
+ return GNUNET_SYSERR;
+ }
+ printf ("denom key valid\n");
+
+ return GNUNET_OK;
+}
+
+
+static int
+mint_denomkeys_check ()
+{
+ if (0 > TALER_MINT_denomkeys_iterate (mintdir, denomkeys_iter, NULL))
+ return GNUNET_NO;
+ return GNUNET_OK;
+}
+
+
+static int
+mint_keys_check (void)
+{
+ if (GNUNET_OK != mint_signkeys_check ())
+ return GNUNET_NO;
+ return mint_denomkeys_check ();
+}
+
+
+/**
+ * The main function of the keyup tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
+ {'d', "mint-dir", "DIR",
+ "mint directory with keys to update", 1,
+ &GNUNET_GETOPT_set_filename, &mintdir},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-keycheck", "WARNING", NULL));
+
+ if (GNUNET_GETOPT_run ("taler-mint-keyup", options, argc, argv) < 0)
+ return 1;
+ if (NULL == mintdir)
+ {
+ fprintf (stderr, "mint directory not given\n");
+ return 1;
+ }
+
+ kcfg = TALER_MINT_config_load (mintdir);
+ if (NULL == kcfg)
+ {
+ fprintf (stderr, "can't load mint configuration\n");
+ return 1;
+ }
+ if (GNUNET_OK != mint_keys_check ())
+ return 1;
+ return 0;
+}
+
diff --git a/src/mint/taler-mint-keyup.c b/src/mint/taler-mint-keyup.c
new file mode 100644
index 00000000..8a1a7788
--- /dev/null
+++ b/src/mint/taler-mint-keyup.c
@@ -0,0 +1,657 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-keyup.c
+ * @brief Update the mint's keys for coins and signatures,
+ * using the mint's offline master key.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#include <platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "mint.h"
+
+#define HASH_CUTOFF 20
+
+/**
+ * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
+ */
+#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000);
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct CoinTypeNBO
+{
+ struct GNUNET_TIME_RelativeNBO duration_spend;
+ struct GNUNET_TIME_RelativeNBO duration_withdraw;
+ struct TALER_AmountNBO value;
+ struct TALER_AmountNBO fee_withdraw;
+ struct TALER_AmountNBO fee_deposit;
+ struct TALER_AmountNBO fee_refresh;
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+struct CoinTypeParams
+{
+ struct GNUNET_TIME_Relative duration_spend;
+ struct GNUNET_TIME_Relative duration_withdraw;
+ struct GNUNET_TIME_Relative duration_overlap;
+ struct TALER_Amount value;
+ struct TALER_Amount fee_withdraw;
+ struct TALER_Amount fee_deposit;
+ struct TALER_Amount fee_refresh;
+ struct GNUNET_TIME_Absolute anchor;
+};
+
+
+/**
+ * Filename of the master private key.
+ */
+static char *masterkeyfile;
+
+/**
+ * Director of the mint, containing the keys.
+ */
+static char *mintdir;
+
+/**
+ * Time to pretend when the key update is executed.
+ */
+static char *pretend_time_str;
+
+/**
+ * Handle to the mint's configuration
+ */
+static struct GNUNET_CONFIGURATION_Handle *kcfg;
+
+/**
+ * Time when the key update is executed. Either the actual current time, or a
+ * pretended time.
+ */
+static struct GNUNET_TIME_Absolute now;
+
+/**
+ * Master private key of the mint.
+ */
+static struct GNUNET_CRYPTO_EddsaPrivateKey *master_priv;
+
+/**
+ * Master public key of the mint.
+ */
+static struct GNUNET_CRYPTO_EddsaPublicKey *master_pub;
+
+/**
+ * Until what time do we provide keys?
+ */
+static struct GNUNET_TIME_Absolute lookahead_sign_stamp;
+
+
+int
+config_get_denom (const char *section, const char *option, struct TALER_Amount *denom)
+{
+ char *str;
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, section, option, &str))
+ return GNUNET_NO;
+ if (GNUNET_OK != TALER_string_to_amount (str, denom))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+char *
+get_signkey_dir ()
+{
+ char *dir;
+ size_t len;
+ len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS), mintdir);
+ GNUNET_assert (len > 0);
+ return dir;
+}
+
+
+char *
+get_signkey_file (struct GNUNET_TIME_Absolute start)
+{
+ char *dir;
+ size_t len;
+ len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS DIR_SEPARATOR_STR "%llu"),
+ mintdir, (long long) start.abs_value_us);
+ GNUNET_assert (len > 0);
+ return dir;
+}
+
+
+
+/**
+ * Hash the data defining the coin type.
+ * Exclude information that may not be the same for all
+ * instances of the coin type (i.e. the anchor, overlap).
+ */
+void
+hash_coin_type (const struct CoinTypeParams *p, struct GNUNET_HashCode *hash)
+{
+ struct CoinTypeNBO p_nbo;
+
+ memset (&p_nbo, 0, sizeof (struct CoinTypeNBO));
+
+ p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend);
+ p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw);
+ p_nbo.value = TALER_amount_hton (p->value);
+ p_nbo.fee_withdraw = TALER_amount_hton (p->fee_withdraw);
+ p_nbo.fee_deposit = TALER_amount_hton (p->fee_deposit);
+ p_nbo.fee_refresh = TALER_amount_hton (p->fee_refresh);
+
+ GNUNET_CRYPTO_hash (&p_nbo, sizeof (struct CoinTypeNBO), hash);
+}
+
+
+static const char *
+get_cointype_dir (const struct CoinTypeParams *p)
+{
+ static char dir[4096];
+ size_t len;
+ struct GNUNET_HashCode hash;
+ char *hash_str;
+ char *val_str;
+ unsigned int i;
+
+ hash_coin_type (p, &hash);
+ hash_str = TALER_data_to_string_alloc (&hash, sizeof (struct GNUNET_HashCode));
+ GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1);
+ GNUNET_assert (NULL != hash_str);
+ hash_str[HASH_CUTOFF] = 0;
+
+ val_str = TALER_amount_to_string (p->value);
+ for (i = 0; i < strlen (val_str); i++)
+ if (':' == val_str[i] || '.' == val_str[i])
+ val_str[i] = '_';
+
+ len = GNUNET_snprintf (dir, sizeof (dir),
+ ("%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s"),
+ mintdir, val_str, hash_str);
+ GNUNET_assert (len > 0);
+ GNUNET_free (hash_str);
+ return dir;
+}
+
+
+static const char *
+get_cointype_file (struct CoinTypeParams *p,
+ struct GNUNET_TIME_Absolute start)
+{
+ const char *dir;
+ static char filename[4096];
+ size_t len;
+ dir = get_cointype_dir (p);
+ len = GNUNET_snprintf (filename, sizeof (filename), ("%s" DIR_SEPARATOR_STR "%llu"),
+ dir, (unsigned long long) start.abs_value_us);
+ GNUNET_assert (len > 0);
+ return filename;
+}
+
+
+/**
+ * Get the latest key file from the past.
+ *
+ * @param cls closure
+ * @param filename complete filename (absolute path)
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+get_anchor_iter (void *cls,
+ const char *filename)
+{
+ struct GNUNET_TIME_Absolute stamp;
+ struct GNUNET_TIME_Absolute *anchor = cls;
+ const char *base;
+ char *end = NULL;
+
+ base = GNUNET_STRINGS_get_short_name (filename);
+ stamp.abs_value_us = strtol (base, &end, 10);
+
+ if ((NULL == end) || (0 != *end))
+ {
+ fprintf(stderr, "Ignoring unexpected file '%s'.\n", filename);
+ return GNUNET_OK;
+ }
+
+ // TODO: check if it's actually a valid key file
+
+ if ((stamp.abs_value_us <= now.abs_value_us) && (stamp.abs_value_us > anchor->abs_value_us))
+ *anchor = stamp;
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Get the timestamp where the first new key should be generated.
+ * Relies on correctly named key files.
+ *
+ * @param dir directory with the signed stuff
+ * @param duration how long is one key valid?
+ * @param overlap what's the overlap between the keys validity period?
+ * @param[out] anchor the timestamp where the first new key should be generated
+ */
+void
+get_anchor (const char *dir,
+ struct GNUNET_TIME_Relative duration,
+ struct GNUNET_TIME_Relative overlap,
+ struct GNUNET_TIME_Absolute *anchor)
+{
+ GNUNET_assert (0 == duration.rel_value_us % 1000000);
+ GNUNET_assert (0 == overlap.rel_value_us % 1000000);
+ if (GNUNET_YES != GNUNET_DISK_directory_test (dir, GNUNET_YES))
+ {
+ *anchor = now;
+ printf ("Can't look for anchor (%s)\n", dir);
+ return;
+ }
+
+ *anchor = GNUNET_TIME_UNIT_ZERO_ABS;
+ if (-1 == GNUNET_DISK_directory_scan (dir, &get_anchor_iter, anchor))
+ {
+ *anchor = now;
+ return;
+ }
+
+ if ((GNUNET_TIME_absolute_add (*anchor, duration)).abs_value_us < now.abs_value_us)
+ {
+ // there's no good anchor, start from now
+ // (existing keys are too old)
+ *anchor = now;
+ }
+ else if (anchor->abs_value_us != now.abs_value_us)
+ {
+ // we have a good anchor
+ *anchor = GNUNET_TIME_absolute_add (*anchor, duration);
+ *anchor = GNUNET_TIME_absolute_subtract (*anchor, overlap);
+ }
+ // anchor is now the stamp where we need to create a new key
+}
+
+static void
+create_signkey_issue (struct GNUNET_TIME_Absolute start,
+ struct GNUNET_TIME_Relative duration,
+ struct TALER_MINT_SignKeyIssue *issue)
+{
+ struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+
+ priv = GNUNET_CRYPTO_eddsa_key_create ();
+ GNUNET_assert (NULL != priv);
+ issue->signkey_priv = *priv;
+ GNUNET_free (priv);
+ issue->master_pub = *master_pub;
+ issue->start = GNUNET_TIME_absolute_hton (start);
+ issue->expire = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (start, duration));
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&issue->signkey_priv, &issue->signkey_pub);
+
+ issue->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNKEY);
+ issue->purpose.size = htonl (sizeof (struct TALER_MINT_SignKeyIssue) - offsetof (struct TALER_MINT_SignKeyIssue, purpose));
+
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (master_priv, &issue->purpose, &issue->signature))
+ {
+ GNUNET_abort ();
+ }
+}
+
+
+static int
+check_signkey_valid (const char *signkey_filename)
+{
+ // FIXME: do real checks
+ return GNUNET_OK;
+}
+
+
+int
+mint_keys_update_signkeys ()
+{
+ struct GNUNET_TIME_Relative signkey_duration;
+ struct GNUNET_TIME_Absolute anchor;
+ char *signkey_dir;
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_keys", "signkey_duration", &signkey_duration))
+ {
+ fprintf (stderr, "Can't read config value mint_keys.signkey_duration\n");
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (signkey_duration, rel_value_us);
+ signkey_dir = get_signkey_dir ();
+ // make sure the directory exists
+ if (GNUNET_OK != GNUNET_DISK_directory_create (signkey_dir))
+ {
+ fprintf (stderr, "Cant create signkey dir\n");
+ return GNUNET_SYSERR;
+ }
+
+ get_anchor (signkey_dir, signkey_duration, GNUNET_TIME_UNIT_ZERO, &anchor);
+
+ while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) {
+ char *skf;
+ skf = get_signkey_file (anchor);
+ if (GNUNET_YES != GNUNET_DISK_file_test (skf))
+ {
+ struct TALER_MINT_SignKeyIssue signkey_issue;
+ ssize_t nwrite;
+ printf ("Generating signing key for %s.\n", GNUNET_STRINGS_absolute_time_to_string (anchor));
+ create_signkey_issue (anchor, signkey_duration, &signkey_issue);
+ nwrite = GNUNET_DISK_fn_write (skf, &signkey_issue, sizeof (struct TALER_MINT_SignKeyIssue),
+ (GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ));
+ if (nwrite != sizeof (struct TALER_MINT_SignKeyIssue))
+ {
+ fprintf (stderr, "Can't write to file '%s'\n", skf);
+ return GNUNET_SYSERR;
+ }
+ }
+ else if (GNUNET_OK != check_signkey_valid (skf))
+ {
+ return GNUNET_SYSERR;
+ }
+ anchor = GNUNET_TIME_absolute_add (anchor, signkey_duration);
+ }
+ return GNUNET_OK;
+}
+
+
+int
+get_cointype_params (const char *ct, struct CoinTypeParams *params)
+{
+ const char *dir;
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_withdraw", ct, &params->duration_withdraw))
+ {
+ fprintf (stderr, "Withdraw duration not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (params->duration_withdraw, rel_value_us);
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_spend", ct, &params->duration_spend))
+ {
+ fprintf (stderr, "Spend duration not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (params->duration_spend, rel_value_us);
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_overlap", ct, &params->duration_overlap))
+ {
+ fprintf (stderr, "Overlap duration not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (params->duration_overlap, rel_value_us);
+
+ if (GNUNET_OK != config_get_denom ("mint_denom_value", ct, &params->value))
+ {
+ fprintf (stderr, "Value not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != config_get_denom ("mint_denom_fee_withdraw", ct, &params->fee_withdraw))
+ {
+ fprintf (stderr, "Withdraw fee not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != config_get_denom ("mint_denom_fee_deposit", ct, &params->fee_deposit))
+ {
+ fprintf (stderr, "Deposit fee not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != config_get_denom ("mint_denom_fee_refresh", ct, &params->fee_refresh))
+ {
+ fprintf (stderr, "Deposit fee not given for coin type '%s'\n", ct);
+ return GNUNET_SYSERR;
+ }
+
+ dir = get_cointype_dir (params);
+ get_anchor (dir, params->duration_spend, params->duration_overlap, &params->anchor);
+ return GNUNET_OK;
+}
+
+
+static void
+create_denomkey_issue (struct CoinTypeParams *params, struct TALER_MINT_DenomKeyIssue *dki)
+{
+ GNUNET_assert (NULL != (dki->denom_priv = TALER_RSA_key_create ()));
+ TALER_RSA_key_get_public (dki->denom_priv, &dki->denom_pub);
+ dki->master = *master_pub;
+ dki->start = GNUNET_TIME_absolute_hton (params->anchor);
+ dki->expire_withdraw =
+ GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
+ params->duration_withdraw));
+ dki->expire_spend =
+ GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
+ params->duration_spend));
+ dki->value = TALER_amount_hton (params->value);
+ dki->fee_withdraw = TALER_amount_hton (params->fee_withdraw);
+ dki->fee_deposit = TALER_amount_hton (params->fee_deposit);
+ dki->fee_refresh = TALER_amount_hton (params->fee_refresh);
+
+ dki->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOM);
+ dki->purpose.size = htonl (sizeof (struct TALER_MINT_DenomKeyIssue) - offsetof (struct TALER_MINT_DenomKeyIssue, purpose));
+
+ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (master_priv, &dki->purpose, &dki->signature))
+ {
+ GNUNET_abort ();
+ }
+}
+
+
+static int
+check_cointype_valid (const char *filename, struct CoinTypeParams *params)
+{
+ // FIXME: add real checks
+ return GNUNET_OK;
+}
+
+
+int
+mint_keys_update_cointype (const char *coin_alias)
+{
+ struct CoinTypeParams p;
+ const char *cointype_dir;
+
+ if (GNUNET_OK != get_cointype_params (coin_alias, &p))
+ return GNUNET_SYSERR;
+
+ cointype_dir = get_cointype_dir (&p);
+ if (GNUNET_OK != GNUNET_DISK_directory_create (cointype_dir))
+ return GNUNET_SYSERR;
+
+ while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) {
+ const char *dkf;
+ dkf = get_cointype_file (&p, p.anchor);
+
+ if (GNUNET_YES != GNUNET_DISK_file_test (dkf))
+ {
+ struct TALER_MINT_DenomKeyIssue denomkey_issue;
+ int ret;
+ printf ("Generating denomination key for type '%s', start %s.\n",
+ coin_alias, GNUNET_STRINGS_absolute_time_to_string (p.anchor));
+ printf ("Target path: %s\n", dkf);
+ create_denomkey_issue (&p, &denomkey_issue);
+ ret = TALER_MINT_write_denom_key (dkf, &denomkey_issue);
+ TALER_RSA_key_free (denomkey_issue.denom_priv);
+ if (GNUNET_OK != ret)
+ {
+ fprintf (stderr, "Can't write to file '%s'\n", dkf);
+ return GNUNET_SYSERR;
+ }
+ }
+ else if (GNUNET_OK != check_cointype_valid (dkf, &p))
+ {
+ return GNUNET_SYSERR;
+ }
+ p.anchor = GNUNET_TIME_absolute_add (p.anchor, p.duration_spend);
+ p.anchor = GNUNET_TIME_absolute_subtract (p.anchor, p.duration_overlap);
+ }
+ return GNUNET_OK;
+}
+
+
+int
+mint_keys_update_denomkeys ()
+{
+ char *coin_types;
+ char *ct;
+ char *tok_ctx;
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, "mint_keys", "coin_types", &coin_types))
+ {
+ fprintf (stderr, "mint_keys.coin_types not in configuration\n");
+ return GNUNET_SYSERR;
+ }
+
+ for (ct = strtok_r (coin_types, " ", &tok_ctx);
+ ct != NULL;
+ ct = strtok_r (NULL, " ", &tok_ctx))
+ {
+ if (GNUNET_OK != mint_keys_update_cointype (ct))
+ {
+ GNUNET_free (coin_types);
+ return GNUNET_SYSERR;
+ }
+ }
+ GNUNET_free (coin_types);
+ return GNUNET_OK;
+}
+
+
+static int
+mint_keys_update ()
+{
+ int ret;
+ struct GNUNET_TIME_Relative lookahead_sign;
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_keys", "lookahead_sign", &lookahead_sign))
+ {
+ fprintf (stderr, "mint_keys.lookahead_sign not found\n");
+ return GNUNET_SYSERR;
+ }
+ if (lookahead_sign.rel_value_us == 0)
+ {
+ fprintf (stderr, "mint_keys.lookahead_sign must not be zero\n");
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (lookahead_sign, rel_value_us);
+ lookahead_sign_stamp = GNUNET_TIME_absolute_add (now, lookahead_sign);
+
+ ret = mint_keys_update_signkeys ();
+ if (GNUNET_OK != ret)
+ return GNUNET_SYSERR;
+
+ return mint_keys_update_denomkeys ();
+}
+
+
+/**
+ * The main function of the keyup tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
+ {'m', "master-key", "FILE",
+ "master key file (private key)", 1,
+ &GNUNET_GETOPT_set_filename, &masterkeyfile},
+ {'d', "mint-dir", "DIR",
+ "mint directory with keys to update", 1,
+ &GNUNET_GETOPT_set_filename, &mintdir},
+ {'t', "time", "TIMESTAMP",
+ "pretend it is a different time for the update", 0,
+ &GNUNET_GETOPT_set_string, &pretend_time_str},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-keyup", "WARNING", NULL));
+
+ if (GNUNET_GETOPT_run ("taler-mint-keyup", options, argc, argv) < 0)
+ return 1;
+ if (NULL == mintdir)
+ {
+ fprintf (stderr, "mint directory not given\n");
+ return 1;
+ }
+
+ if (NULL != pretend_time_str)
+ {
+ if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str, &now))
+ {
+ fprintf (stderr, "timestamp invalid\n");
+ return 1;
+ }
+ }
+ else
+ {
+ now = GNUNET_TIME_absolute_get ();
+ }
+ ROUND_TO_SECS (now, abs_value_us);
+
+ kcfg = TALER_MINT_config_load (mintdir);
+ if (NULL == kcfg)
+ {
+ fprintf (stderr, "can't load mint configuration\n");
+ return 1;
+ }
+
+ if (NULL == masterkeyfile)
+ {
+ fprintf (stderr, "master key file not given\n");
+ return 1;
+ }
+ master_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile);
+ if (NULL == master_priv)
+ {
+ fprintf (stderr, "master key invalid\n");
+ return 1;
+ }
+
+ master_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey);
+ GNUNET_CRYPTO_eddsa_key_get_public (master_priv, master_pub);
+
+ // check if key from file matches the one from the configuration
+ {
+ struct GNUNET_CRYPTO_EddsaPublicKey master_pub_from_cfg;
+ if (GNUNET_OK != TALER_configuration_get_data (kcfg, "mint", "master_pub",
+ &master_pub_from_cfg,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
+ {
+ fprintf (stderr, "master key missing in configuration (mint.master_pub)\n");
+ return 1;
+ }
+ if (0 != memcmp (master_pub, &master_pub_from_cfg, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
+ {
+ fprintf (stderr, "Mismatch between key from mint configuration and master private key file from command line.\n");
+ return 1;
+ }
+ }
+
+ if (GNUNET_OK != mint_keys_update ())
+ return 1;
+ return 0;
+}
+
diff --git a/src/mint/taler-mint-reservemod.c b/src/mint/taler-mint-reservemod.c
new file mode 100644
index 00000000..3dd94f84
--- /dev/null
+++ b/src/mint/taler-mint-reservemod.c
@@ -0,0 +1,215 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-reservemod.c
+ * @brief Modify reserves.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <libpq-fe.h>
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "mint.h"
+
+static char *mintdir;
+static struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub;
+static struct GNUNET_CONFIGURATION_Handle *kcfg;
+static PGconn *db_conn;
+
+
+
+/**
+ * Create a new or add to existing reserve.
+ * Fails if currencies do not match.
+ *
+ * @param denom denomination to add
+ *
+ * @return ...
+ */
+int
+reservemod_add (struct TALER_Amount denom)
+{
+ PGresult *result;
+ {
+ const void *param_values[] = { reserve_pub };
+ int param_lengths[] = {sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)};
+ int param_formats[] = {1};
+ result = PQexecParams (db_conn,
+ "select balance_value, balance_fraction, balance_currency from reserves where reserve_pub=$1 limit 1;",
+ 1, NULL, (const char * const *) param_values, param_lengths, param_formats, 1);
+ }
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ fprintf (stderr, "Select failed: %s\n", PQresultErrorMessage (result));
+ return GNUNET_SYSERR;
+ }
+ if (0 == PQntuples (result))
+ {
+ struct GNUNET_TIME_AbsoluteNBO exnbo;
+ exnbo = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add ( GNUNET_TIME_absolute_get (), GNUNET_TIME_UNIT_YEARS));
+
+ uint32_t value = htonl (denom.value);
+ uint32_t fraction = htonl (denom.fraction);
+ const void *param_values[] = {
+ reserve_pub,
+ &value,
+ &fraction,
+ denom.currency,
+ &exnbo};
+ int param_lengths[] = {32, 4, 4, strlen(denom.currency), 8};
+ int param_formats[] = {1, 1, 1, 1, 1};
+ result = PQexecParams (db_conn,
+ "insert into reserves (reserve_pub, balance_value, balance_fraction, balance_currency, "
+ " expiration_date )"
+ "values ($1,$2,$3,$4,$5);",
+ 5, NULL, (const char **) param_values, param_lengths, param_formats, 1);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ fprintf (stderr, "Insert failed: %s\n", PQresultErrorMessage (result));
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+ struct TALER_Amount old_denom;
+ struct TALER_Amount new_denom;
+ struct TALER_AmountNBO new_denom_nbo;
+ int denom_indices[] = {0, 1, 2};
+ int param_lengths[] = {4, 4, 32};
+ int param_formats[] = {1, 1, 1};
+ const void *param_values[] = {
+ &new_denom_nbo.value,
+ &new_denom_nbo.fraction,
+ reserve_pub
+ };
+
+ GNUNET_assert (GNUNET_OK == TALER_TALER_DB_extract_amount (result, 0, denom_indices, &old_denom));
+ new_denom = TALER_amount_add (old_denom, denom);
+ new_denom_nbo = TALER_amount_hton (new_denom);
+ result = PQexecParams (db_conn,
+ "UPDATE reserves "
+ "SET balance_value = $1, balance_fraction = $2, "
+ " status_sig = NULL, status_sign_pub = NULL "
+ "WHERE reserve_pub = $3 ",
+ 3, NULL, (const char **) param_values, param_lengths, param_formats, 1);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ fprintf (stderr, "Update failed: %s\n", PQresultErrorMessage (result));
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ fprintf (stderr, "Update failed (updated '%s' tupes instead of '1')\n",
+ PQcmdTuples (result));
+ return GNUNET_SYSERR;
+ }
+
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * The main function of the reservemod tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static char *reserve_pub_str;
+ static char *add_str;
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
+ {'d', "mint-dir", "DIR",
+ "mint directory with keys to update", 1,
+ &GNUNET_GETOPT_set_filename, &mintdir},
+ {'R', "reserve", "KEY",
+ "reserve (public key) to modify", 1,
+ &GNUNET_GETOPT_set_string, &reserve_pub_str},
+ {'a', "add", "DENOM",
+ "value to add", 1,
+ &GNUNET_GETOPT_set_string, &add_str},
+ GNUNET_GETOPT_OPTION_END
+ };
+ char *TALER_MINT_db_connection_cfg_str;
+
+ GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-keycheck", "WARNING", NULL));
+
+ if (GNUNET_GETOPT_run ("taler-mint-keyup", options, argc, argv) < 0)
+ return 1;
+ if (NULL == mintdir)
+ {
+ fprintf (stderr, "mint directory not given\n");
+ return 1;
+ }
+
+ reserve_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey);
+ if ((NULL == reserve_pub_str) ||
+ (GNUNET_OK != GNUNET_STRINGS_string_to_data (reserve_pub_str,
+ strlen (reserve_pub_str),
+ reserve_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))))
+ {
+ fprintf (stderr, "reserve key invalid\n");
+ return 1;
+ }
+
+ kcfg = TALER_MINT_config_load (mintdir);
+ if (NULL == kcfg)
+ {
+ fprintf (stderr, "can't load mint configuration\n");
+ return 1;
+ }
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, "mint", "db", &TALER_MINT_db_connection_cfg_str))
+ {
+ fprintf (stderr, "db configuration string not found\n");
+ return 42;
+ }
+ db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str);
+ if (CONNECTION_OK != PQstatus (db_conn))
+ {
+ fprintf (stderr, "db connection failed: %s\n", PQerrorMessage (db_conn));
+ return 1;
+ }
+
+ if (NULL != add_str)
+ {
+ struct TALER_Amount add_value;
+ if (GNUNET_OK != TALER_string_to_amount (add_str, &add_value))
+ {
+ fprintf (stderr, "could not read value\n");
+ return 1;
+ }
+ if (GNUNET_OK != reservemod_add (add_value))
+ {
+ fprintf (stderr, "adding value failed\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
diff --git a/src/mint/test_mint_api.c b/src/mint/test_mint_api.c
new file mode 100644
index 00000000..965d607f
--- /dev/null
+++ b/src/mint/test_mint_api.c
@@ -0,0 +1,211 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint/test_mint_api.c
+ * @brief testcase to test mint's HTTP API interface
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_mint_service.h"
+
+struct TALER_MINT_Context *ctx;
+
+struct TALER_MINT_Handle *mint;
+
+struct TALER_MINT_KeysGetHandle *dkey_get;
+
+struct TALER_MINT_DepositHandle *dh;
+
+static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
+
+static int result;
+
+
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ shutdown_task = GNUNET_SCHEDULER_NO_TASK;
+ if (NULL != dkey_get)
+ TALER_MINT_keys_get_cancel (dkey_get);
+ dkey_get = NULL;
+ if (NULL != dh)
+ TALER_MINT_deposit_submit_cancel (dh);
+ dh = NULL;
+ TALER_MINT_disconnect (mint);
+ mint = NULL;
+ TALER_MINT_cleanup (ctx);
+ ctx = NULL;
+}
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a deposit
+ * permission object to a mint
+ *
+ * @param cls closure
+ * @param status 1 for successful deposit, 2 for retry, 0 for failure
+ * @param obj the received JSON object; can be NULL if it cannot be constructed
+ * from the reply
+ * @param emsg in case of unsuccessful deposit, this contains a human readable
+ * explanation.
+ */
+static void
+deposit_status (void *cls,
+ int status,
+ json_t *obj,
+ char *emsg)
+{
+ char *json_enc;
+
+ dh = NULL;
+ json_enc = NULL;
+ if (NULL != obj)
+ {
+ json_enc = json_dumps (obj, JSON_INDENT(2));
+ fprintf (stderr, "%s", json_enc);
+ }
+ if (1 == status)
+ result = GNUNET_OK;
+ else
+ GNUNET_break (0);
+ if (NULL != emsg)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Deposit failed: %s\n", emsg);
+ GNUNET_SCHEDULER_shutdown ();
+}
+/**
+ * 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
+ */
+static void
+cont (void *cls, const char *emsg)
+{
+ json_t *dp;
+ char rnd_32[32];
+ char rnd_64[64];
+ char *enc_32;
+ char *enc_64;
+
+ GNUNET_assert (NULL == cls);
+ dkey_get = NULL;
+ if (NULL != emsg)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
+
+ enc_32 = TALER_data_to_string_alloc (rnd_32, sizeof (rnd_32));
+ enc_64 = TALER_data_to_string_alloc (rnd_64, sizeof (rnd_64));
+ dp = json_pack ("{s:s s:o s:s s:s s:s s:s s:s s:s s:s s:s}",
+ "type", "DIRECT_DEPOSIT",
+ "wire", json_pack ("{s:s}", "type", "SEPA"),
+ "C", enc_32,
+ "K", enc_32,
+ "ubsig", enc_64,
+ "M", enc_32,
+ "H_a", enc_64,
+ "H_wire", enc_64,
+ "csig", enc_64,
+ "m", "B1C5GP2RB1C5G");
+ GNUNET_free (enc_32);
+ GNUNET_free (enc_64);
+ dh = TALER_MINT_deposit_submit_json (mint,
+ deposit_status,
+ NULL,
+ dp);
+ json_decref (dp);
+}
+
+
+/**
+ * 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.
+ */
+static void
+read_denom_key (void *cls,
+ struct TALER_MINT_SigningPublicKey **sign_keys,
+ struct TALER_MINT_DenomPublicKey **denom_keys)
+{
+ unsigned int cnt;
+ GNUNET_assert (NULL == cls);
+#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); return; } while (0)
+ ERR (NULL == sign_keys);
+ ERR (NULL == denom_keys);
+ for (cnt = 0; NULL != sign_keys[cnt]; cnt++)
+ GNUNET_free (sign_keys[cnt]);
+ ERR (0 == cnt);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %u signing keys\n", cnt);
+ GNUNET_free (sign_keys);
+ for (cnt = 0; NULL != denom_keys[cnt]; cnt++)
+ GNUNET_free (denom_keys[cnt]);
+ ERR (0 == cnt);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %u denomination keys\n", cnt);
+ GNUNET_free (denom_keys);
+#undef ERR
+ return;
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param config configuration
+ */
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *config)
+{
+ ctx = TALER_MINT_init ();
+ mint = TALER_MINT_connect (ctx, "localhost", 4241, NULL);
+ GNUNET_assert (NULL != mint);
+ dkey_get = TALER_MINT_keys_get (mint,
+ &read_denom_key, NULL,
+ &cont, NULL);
+ GNUNET_assert (NULL != dkey_get);
+ shutdown_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 5),
+ &do_shutdown, NULL);
+}
+
+int
+main (int argc, char * const *argv)
+{
+ static struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ result = GNUNET_SYSERR;
+ if (GNUNET_OK !=
+ GNUNET_PROGRAM_run (argc, argv, "test-mint-api",
+ gettext_noop
+ ("Testcase to test mint's HTTP API interface"),
+ options, &run, NULL))
+ return 3;
+ return (GNUNET_OK == result) ? 0 : 1;
+}
diff --git a/src/mint/test_mint_common.c b/src/mint/test_mint_common.c
new file mode 100644
index 00000000..b7cad3ea
--- /dev/null
+++ b/src/mint/test_mint_common.c
@@ -0,0 +1,83 @@
+/*
+ This file is part of TALER
+ (C) 2014 GNUnet e. V. (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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint/test_mint_common.c
+ * @brief test cases for some functions in mint/mint_common.c
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include "gnunet/gnunet_util_lib.h"
+#include "taler_rsa.h"
+#include "mint.h"
+
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+int
+main (int argc, const char *const argv[])
+{
+ struct TALER_MINT_DenomKeyIssue dki;
+ struct TALER_RSA_PrivateKeyBinaryEncoded *enc;
+ struct TALER_MINT_DenomKeyIssue dki_read;
+ struct TALER_RSA_PrivateKeyBinaryEncoded *enc_read;
+ char *tmpfile;
+
+ int ret;
+
+ ret = 1;
+ enc = NULL;
+ enc_read = NULL;
+ tmpfile = NULL;
+ dki.denom_priv = NULL;
+ dki_read.denom_priv = NULL;
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ &dki.signature,
+ sizeof (dki) - offsetof (struct TALER_MINT_DenomKeyIssue,
+ signature));
+ dki.denom_priv = TALER_RSA_key_create ();
+ EXITIF (NULL == (enc = TALER_RSA_encode_key (dki.denom_priv)));
+ EXITIF (NULL == (tmpfile = GNUNET_DISK_mktemp ("test_mint_common")));
+ EXITIF (GNUNET_OK != TALER_MINT_write_denom_key (tmpfile, &dki));
+ EXITIF (GNUNET_OK != TALER_MINT_read_denom_key (tmpfile, &dki_read));
+ EXITIF (NULL == (enc_read = TALER_RSA_encode_key (dki_read.denom_priv)));
+ EXITIF (enc->len != enc_read->len);
+ EXITIF (0 != memcmp (enc,
+ enc_read,
+ ntohs(enc->len)));
+ EXITIF (0 != memcmp (&dki.signature,
+ &dki_read.signature,
+ sizeof (dki) - offsetof (struct TALER_MINT_DenomKeyIssue,
+ signature)));
+ ret = 0;
+
+ EXITIF_exit:
+ GNUNET_free_non_null (enc);
+ if (NULL != tmpfile)
+ {
+ (void) unlink (tmpfile);
+ GNUNET_free (tmpfile);
+ }
+ GNUNET_free_non_null (enc_read);
+ if (NULL != dki.denom_priv)
+ TALER_RSA_key_free (dki.denom_priv);
+ if (NULL != dki_read.denom_priv)
+ TALER_RSA_key_free (dki_read.denom_priv);
+ return ret;
+}
diff --git a/src/mint/test_mint_deposits.c b/src/mint/test_mint_deposits.c
new file mode 100644
index 00000000..776bc15d
--- /dev/null
+++ b/src/mint/test_mint_deposits.c
@@ -0,0 +1,149 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file mint/test_mint_deposits.c
+ * @brief testcase for mint deposits
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include <libpq-fe.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "mint_db.h"
+
+#define DB_URI "postgres:///taler"
+
+/**
+ * Shorthand for exit jumps.
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+
+/**
+ * DB connection handle
+ */
+static PGconn *conn;
+
+/**
+ * Should we not interact with a temporary table?
+ */
+static int persistent;
+
+/**
+ * Testcase result
+ */
+static int result;
+
+
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ if (NULL != conn)
+ PQfinish (conn);
+ conn = NULL;
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param config configuration
+ */
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *config)
+{
+ static const char wire[] = "{"
+ "\"type\":\"SEPA\","
+ "\"IBAN\":\"DE67830654080004822650\","
+ "\"NAME\":\"GNUNET E.V\","
+ "\"BIC\":\"GENODEF1SRL\""
+ "}";
+ struct Deposit *deposit;
+ struct Deposit *q_deposit;
+ uint64_t transaction_id;
+
+ deposit = NULL;
+ q_deposit = NULL;
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_shutdown, NULL);
+ EXITIF (NULL == (conn = PQconnectdb(DB_URI)));
+ EXITIF (GNUNET_OK != TALER_MINT_DB_init_deposits (conn, !persistent));
+ EXITIF (GNUNET_OK != TALER_MINT_DB_prepare_deposits (conn));
+ deposit = GNUNET_malloc (sizeof (struct Deposit) + sizeof (wire));
+ /* Makeup a random coin public key */
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ deposit,
+ sizeof (struct Deposit));
+ /* Makeup a random 64bit transaction ID */
+ transaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
+ UINT64_MAX);
+ deposit->transaction_id = GNUNET_htonll (transaction_id);
+ /* Random amount */
+ deposit->amount.value =
+ htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX));
+ deposit->amount.fraction =
+ htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX));
+ strcpy (deposit->amount.currency, "EUR");
+ /* Copy wireformat */
+ (void) memcpy (deposit->wire, wire, sizeof (wire));
+ EXITIF (GNUNET_OK != TALER_MINT_DB_insert_deposit (conn,
+ deposit));
+ EXITIF (GNUNET_OK != TALER_MINT_DB_get_deposit (conn,
+ &deposit->coin_pub,
+ &q_deposit));
+ EXITIF (0 != memcmp (deposit,
+ q_deposit,
+ sizeof (struct Deposit) - offsetof(struct Deposit,
+ wire)));
+ EXITIF (transaction_id != GNUNET_ntohll (q_deposit->transaction_id));
+ result = GNUNET_OK;
+
+ EXITIF_exit:
+ GNUNET_free_non_null (deposit);
+ GNUNET_free_non_null (q_deposit);
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+}
+
+
+int main(int argc, char *const argv[])
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'T', "persist", NULL,
+ gettext_noop ("Use a persistent database table instead of a temporary one"),
+ GNUNET_NO, &GNUNET_GETOPT_set_one, &persistent},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+
+ persistent = GNUNET_NO;
+ result = GNUNET_SYSERR;
+ if (GNUNET_OK !=
+ GNUNET_PROGRAM_run (argc, argv,
+ "test-mint-deposits",
+ "testcase for mint deposits",
+ options, &run, NULL))
+ return 3;
+ return (GNUNET_OK == result) ? 0 : 1;
+}
diff --git a/src/mint/test_mint_nayapaisa.ecc b/src/mint/test_mint_nayapaisa.ecc
new file mode 100644
index 00000000..942110b5
--- /dev/null
+++ b/src/mint/test_mint_nayapaisa.ecc
Binary files differ
diff --git a/src/mint/test_mint_nayapaisa/README b/src/mint/test_mint_nayapaisa/README
new file mode 100644
index 00000000..fce5e018
--- /dev/null
+++ b/src/mint/test_mint_nayapaisa/README
@@ -0,0 +1 @@
+This directory is a template for the mint directory.
diff --git a/src/mint/test_mint_nayapaisa/config/mint-common.conf b/src/mint/test_mint_nayapaisa/config/mint-common.conf
new file mode 100644
index 00000000..c1fede7a
--- /dev/null
+++ b/src/mint/test_mint_nayapaisa/config/mint-common.conf
@@ -0,0 +1,6 @@
+[mint]
+db = postgres:///taler
+port = 4241
+master_pub = 6ZE0HEY2M0FWP61M0470HYBF4K6RRD5DP54372PD2TN9N9VX2VJG
+refresh_security_parameter = 3
+
diff --git a/src/mint/test_mint_nayapaisa/config/mint-keyup.conf b/src/mint/test_mint_nayapaisa/config/mint-keyup.conf
new file mode 100644
index 00000000..1542d1a6
--- /dev/null
+++ b/src/mint/test_mint_nayapaisa/config/mint-keyup.conf
@@ -0,0 +1,79 @@
+[mint_keys]
+
+# how long is one signkey valid?
+signkey_duration = 4 weeks
+
+# how long do we generate denomination and signing keys
+# ahead of time?
+lookahead_sign = 32 weeks 1 day
+
+# how long do we provide to clients denomination and signing keys
+# ahead of time?
+lookahead_provide = 4 weeks 1 day
+
+# what coin types do we have available?
+coin_types = default_eur_ct_10 default_eur_5 default_eur_10 default_eur_1000
+
+
+
+[mint_denom_duration_overlap]
+default_eur_ct_10 = 5 minutes
+default_eur_5 = 5 minutes
+default_eur_10 = 5 minutes
+default_eur_1000 = 5 minutes
+
+
+
+[mint_denom_value]
+default_eur_ct_10 = EUR:0.10
+default_eur_5 = EUR:5
+default_eur_10 = EUR:10
+default_eur_1000 = EUR:1000
+
+
+
+[mint_denom_duration_withdraw]
+default_eur_ct_10 = 7 days
+default_eur_5 = 7 days
+default_eur_10 = 7 days
+default_eur_1000 = 1 day
+
+
+
+[mint_denom_duration_spend]
+default_eur_ct_10 = 30 days
+default_eur_5 = 30 days
+default_eur_10 = 30 days
+default_eur_1000 = 30 day
+
+
+
+[mint_denom_fee_withdraw]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+[mint_denom_fee_deposit]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+
+[mint_denom_fee_refresh]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+
+[mint_denom_kappa]
+default_eur_ct_10 = 3
+default_eur_5 = 3
+default_eur_10 = 3
+default_eur_1000 = 5
+
diff --git a/src/mint/test_mint_nyadirahim.ecc b/src/mint/test_mint_nyadirahim.ecc
new file mode 100644
index 00000000..9db92089
--- /dev/null
+++ b/src/mint/test_mint_nyadirahim.ecc
@@ -0,0 +1 @@
+ĂWÜBřĐfŘ ŽµrŘń·•ŠĘĐŚ„Ú:ß˝V»j \ No newline at end of file
diff --git a/src/mint/test_mint_nyadirahim/README b/src/mint/test_mint_nyadirahim/README
new file mode 100644
index 00000000..fce5e018
--- /dev/null
+++ b/src/mint/test_mint_nyadirahim/README
@@ -0,0 +1 @@
+This directory is a template for the mint directory.
diff --git a/src/mint/test_mint_nyadirahim/config/mint-common.conf b/src/mint/test_mint_nyadirahim/config/mint-common.conf
new file mode 100644
index 00000000..c4d52863
--- /dev/null
+++ b/src/mint/test_mint_nyadirahim/config/mint-common.conf
@@ -0,0 +1,6 @@
+[mint]
+db = postgres:///taler
+port = 4241
+master_pub = 7995WKK71KPKTBBMA5BHNBSZFGNRZPYNXDJMQ8EK86V9598H03TG
+refresh_security_parameter = 3
+
diff --git a/src/mint/test_mint_nyadirahim/config/mint-keyup.conf b/src/mint/test_mint_nyadirahim/config/mint-keyup.conf
new file mode 100644
index 00000000..1542d1a6
--- /dev/null
+++ b/src/mint/test_mint_nyadirahim/config/mint-keyup.conf
@@ -0,0 +1,79 @@
+[mint_keys]
+
+# how long is one signkey valid?
+signkey_duration = 4 weeks
+
+# how long do we generate denomination and signing keys
+# ahead of time?
+lookahead_sign = 32 weeks 1 day
+
+# how long do we provide to clients denomination and signing keys
+# ahead of time?
+lookahead_provide = 4 weeks 1 day
+
+# what coin types do we have available?
+coin_types = default_eur_ct_10 default_eur_5 default_eur_10 default_eur_1000
+
+
+
+[mint_denom_duration_overlap]
+default_eur_ct_10 = 5 minutes
+default_eur_5 = 5 minutes
+default_eur_10 = 5 minutes
+default_eur_1000 = 5 minutes
+
+
+
+[mint_denom_value]
+default_eur_ct_10 = EUR:0.10
+default_eur_5 = EUR:5
+default_eur_10 = EUR:10
+default_eur_1000 = EUR:1000
+
+
+
+[mint_denom_duration_withdraw]
+default_eur_ct_10 = 7 days
+default_eur_5 = 7 days
+default_eur_10 = 7 days
+default_eur_1000 = 1 day
+
+
+
+[mint_denom_duration_spend]
+default_eur_ct_10 = 30 days
+default_eur_5 = 30 days
+default_eur_10 = 30 days
+default_eur_1000 = 30 day
+
+
+
+[mint_denom_fee_withdraw]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+[mint_denom_fee_deposit]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+
+[mint_denom_fee_refresh]
+default_eur_ct_10 = EUR:0.01
+default_eur_5 = EUR:0.01
+default_eur_10 = EUR:0.01
+default_eur_1000 = EUR:0.01
+
+
+
+[mint_denom_kappa]
+default_eur_ct_10 = 3
+default_eur_5 = 3
+default_eur_10 = 3
+default_eur_1000 = 5
+
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
new file mode 100644
index 00000000..f935802a
--- /dev/null
+++ b/src/util/Makefile.am
@@ -0,0 +1,39 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include $(LIBGCRYPT_CFLAGS) $(POSTGRESQL_CPPFLAGS)
+
+lib_LTLIBRARIES = \
+ libtalerutil.la
+
+libtalerutil_la_SOURCES = \
+ util.c \
+ json.c \
+ db.c \
+ microhttpd.c \
+ rsa.c
+
+libtalerutil_la_LIBADD = \
+ -lgnunetutil \
+ $(LIBGCRYPT_LIBS) \
+ -ljansson \
+ -lmicrohttpd \
+ -lpq
+
+libtalerutil_la_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS) \
+ -version-info 0:0:0 \
+ -export-dynamic -no-undefined
+
+check_PROGRAMS = \
+ test-hash-context \
+ test-rsa
+
+TESTS = \
+ $(check_PROGRAMS)
+
+test_hash_context_SOURCES = test_hash_context.c
+test_hash_context_CPPFLAGS = $(AM_CPPFLAGS) $(LIBGCRYPT_CFLAGS)
+test_hash_context_LDADD = libtalerutil.la \
+ -lgnunetutil $(LIBGCRYPT_LIBS)
+
+test_rsa_SOURCES = test_rsa.c
+test_rsa_LDADD = libtalerutil.la \
+ -lgnunetutil $(LIBGCRYPT_LIBS)
diff --git a/src/util/db.c b/src/util/db.c
new file mode 100644
index 00000000..a0b234a0
--- /dev/null
+++ b/src/util/db.c
@@ -0,0 +1,196 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+
+/**
+ * @file util/db.c
+ * @brief helper functions for DB interactions
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Florian Dold
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_db_lib.h"
+
+
+/**
+ * Execute a prepared statement.
+ */
+PGresult *
+TALER_DB_exec_prepared (PGconn *db_conn,
+ const char *name,
+ const struct TALER_DB_QueryParam *params)
+{
+ unsigned len;
+ unsigned i;
+
+ /* count the number of parameters */
+
+ {
+ const struct TALER_DB_QueryParam *x;
+ for (len = 0, x = params;
+ x->more;
+ len +=1, x += 1);
+ }
+
+ /* new scope to allow stack allocation without alloca */
+
+ {
+ void *param_values[len];
+ int param_lengths[len];
+ int param_formats[len];
+
+ for (i = 0; i < len; i += 1)
+ {
+ param_values[i] = (void *) params[i].data;
+ param_lengths[i] = params[i].size;
+ param_formats[i] = 1;
+ }
+ return PQexecPrepared (db_conn, name, len,
+ (const char **) param_values, param_lengths, param_formats, 1);
+ }
+}
+
+
+/**
+ * Extract results from a query result according to the given specification.
+ * If colums are NULL, the destination is not modified, and GNUNET_NO
+ * is returned.
+ *
+ * @return
+ * GNUNET_YES if all results could be extracted
+ * GNUNET_NO if at least one result was NULL
+ * GNUNET_SYSERR if a result was invalid (non-existing field)
+ */
+int
+TALER_DB_extract_result (PGresult *result,
+ struct TALER_DB_ResultSpec *rs,
+ int row)
+{
+ int had_null = GNUNET_NO;
+
+ for (; NULL != rs->fname; rs += 1)
+ {
+ int fnum;
+ fnum = PQfnumber (result, rs->fname);
+ if (fnum < 0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "field '%s' does not exist in result\n", rs->fname);
+ return GNUNET_SYSERR;
+ }
+
+ /* if a field is null, continue but
+ * remember that we now return a different result */
+
+ if (PQgetisnull (result, row, fnum))
+ {
+ had_null = GNUNET_YES;
+ continue;
+ }
+ const char *res;
+ if (rs->dst_size != PQgetlength (result, row, fnum))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "field '%s' has wrong size (got %u, expected %u)\n",
+ rs->fname, (int) PQgetlength (result, row, fnum), (int) rs->dst_size);
+ return GNUNET_SYSERR;
+ }
+ res = PQgetvalue (result, row, fnum);
+ GNUNET_assert (NULL != res);
+ memcpy (rs->dst, res, rs->dst_size);
+ }
+ if (GNUNET_YES == had_null)
+ return GNUNET_NO;
+ return GNUNET_YES;
+}
+
+
+int
+TALER_DB_field_isnull (PGresult *result,
+ int row,
+ const char *fname)
+{
+ int fnum;
+ fnum = PQfnumber (result, fname);
+ GNUNET_assert (fnum >= 0);
+ if (PQgetisnull (result, row, fnum))
+ return GNUNET_YES;
+ return GNUNET_NO;
+}
+
+
+int
+TALER_DB_extract_amount_nbo (PGresult *result,
+ int row,
+ const char *val_name,
+ const char *frac_name,
+ const char *curr_name,
+ struct TALER_AmountNBO *r_amount_nbo)
+{
+ int val_num;
+ int frac_num;
+ int curr_num;
+ int len;
+
+ GNUNET_assert (NULL != strstr (val_name, "_val"));
+ GNUNET_assert (NULL != strstr (frac_name, "_frac"));
+ GNUNET_assert (NULL != strstr (curr_name, "_curr"));
+
+ val_num = PQfnumber (result, val_name);
+ GNUNET_assert (val_num >= 0);
+ frac_num = PQfnumber (result, frac_name);
+ GNUNET_assert (frac_num >= 0);
+ curr_num = PQfnumber (result, curr_name);
+ GNUNET_assert (curr_num >= 0);
+
+ r_amount_nbo->value = *(uint32_t *) PQgetvalue (result, row, val_num);
+ r_amount_nbo->fraction = *(uint32_t *) PQgetvalue (result, row, frac_num);
+ memset (r_amount_nbo->currency, 0, TALER_CURRENCY_LEN);
+ // FIXME: overflow?
+ len = PQgetlength (result, row, curr_num);
+ len = GNUNET_MIN (TALER_CURRENCY_LEN, len);
+ memcpy (r_amount_nbo->currency, PQgetvalue (result, row, curr_num), len);
+ r_amount_nbo->currency[TALER_CURRENCY_LEN - 1] = '\0';
+
+ return GNUNET_OK;
+}
+
+
+int
+TALER_DB_extract_amount (PGresult *result,
+ int row,
+ const char *val_name,
+ const char *frac_name,
+ const char *curr_name,
+ struct TALER_Amount *r_amount)
+{
+ struct TALER_AmountNBO amount_nbo;
+
+ (void)
+ TALER_DB_extract_amount_nbo (result,
+ row,
+ val_name,
+ frac_name,
+ curr_name,
+ &amount_nbo);
+ r_amount->value = ntohl (amount_nbo.value);
+ r_amount->fraction = ntohl (amount_nbo.fraction);
+ (void) strncpy (r_amount->currency, amount_nbo.currency, TALER_CURRENCY_LEN);
+
+ return GNUNET_OK;
+}
+
+/* end of util/db.c */
diff --git a/src/util/json.c b/src/util/json.c
new file mode 100644
index 00000000..269e6cf2
--- /dev/null
+++ b/src/util/json.c
@@ -0,0 +1,194 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file util/json.c
+ * @brief helper functions for JSON processing using libjansson
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
+#include "taler_json_lib.h"
+
+/**
+ * Shorthand for exit jumps.
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+/**
+ * Print JSON parsing related error information
+ */
+#define WARN_JSON(error) \
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
+ "JSON parsing failed at %s:%u: %s (%s)", \
+ __FILE__, __LINE__, error.text, error.source)
+
+/**
+ * Shorthand for JSON parsing related exit jumps.
+ */
+#define UNPACK_EXITIF(cond) \
+ do { \
+ if (cond) { WARN_JSON(error); goto EXITIF_exit; } \
+ } while (0)
+
+/**
+ * Convert a TALER amount to a JSON
+ * object.
+ *
+ * @param amount the amount
+ * @return a json object describing the amount
+ */
+json_t *
+TALER_JSON_from_amount (struct TALER_Amount amount)
+{
+ json_t *j;
+ j = json_pack ("{s: s, s:I, s:I}",
+ "currency", amount.currency,
+ "value", (json_int_t) amount.value,
+ "fraction", (json_int_t) amount.fraction);
+ GNUNET_assert (NULL != j);
+ return j;
+}
+
+
+/**
+ * Convert absolute timestamp to a json string.
+ *
+ * @param the time stamp
+ * @return a json string with the timestamp in @a stamp
+ */
+json_t *
+TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp)
+{
+ json_t *j;
+ char *mystr;
+ int ret;
+ ret = GNUNET_asprintf (&mystr, "%llu",
+ (long long) (stamp.abs_value_us / (1000 * 1000)));
+ GNUNET_assert (ret > 0);
+ j = json_string (mystr);
+ GNUNET_free (mystr);
+ return j;
+}
+
+
+
+/**
+ * Convert binary data to a JSON string
+ * with the base32crockford encoding.
+ *
+ * @param data binary data
+ * @param size size of @a data in bytes
+ * @return json string that encodes @a data
+ */
+json_t *
+TALER_JSON_from_data (const void *data, size_t size)
+{
+ char *buf;
+ json_t *json;
+ buf = TALER_data_to_string_alloc (data, size);
+ json = json_string (buf);
+ GNUNET_free (buf);
+ return json;
+}
+
+
+/**
+ * Parse given JSON object to Amount
+ *
+ * @param json the json object representing Amount
+ * @param r_amount where the amount has to be written
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_amount (json_t *json,
+ struct TALER_Amount *r_amount)
+{
+ char *currency;
+ json_int_t value;
+ json_int_t fraction;
+ json_error_t error;
+
+ UNPACK_EXITIF (0 != json_unpack_ex (json, &error, JSON_STRICT,
+ "{s:s, s:I, s:I}",
+ "curreny", &currency,
+ "value", &value,
+ "fraction", &fraction));
+ EXITIF (3 < strlen (currency));
+ r_amount->value = (uint32_t) value;
+ r_amount->fraction = (uint32_t) fraction;
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Parse given JSON object to Amount
+ *
+ * @param json the json object representing Amount
+ * @param r_amount where the amount has to be written
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_abs (json_t *json,
+ struct GNUNET_TIME_Absolute *abs)
+{
+ const char *str;
+ unsigned long long abs_value_s;
+
+ GNUNET_assert (NULL != abs);
+ EXITIF (NULL == (str = json_string_value (json)));
+ EXITIF (1 > sscanf (str, "%llu", &abs_value_s));
+ abs->abs_value_us = abs_value_s * 1000 * 1000;
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+/**
+ * Parse given JSON object to data
+ *
+ * @param json the json object representing data
+ * @param out the pointer to hold the parsed data.
+ * @param out_size the size of r_data.
+ * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error
+ */
+int
+TALER_JSON_to_data (json_t *json,
+ void *out,
+ size_t out_size)
+{
+ const char *enc;
+ unsigned int len;
+
+ EXITIF (NULL == (enc = json_string_value (json)));
+ len = strlen (enc);
+ EXITIF ((((len * 5) / 8) + ((((len * 5) % 8) == 0) ? 0 : 1)) == out_size);
+ EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, len, out, out_size));
+ return GNUNET_OK;
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+/* End of util/json.c */
diff --git a/src/util/microhttpd.c b/src/util/microhttpd.c
new file mode 100644
index 00000000..f3bea74f
--- /dev/null
+++ b/src/util/microhttpd.c
@@ -0,0 +1,417 @@
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_microhttpd_lib.h"
+
+
+
+/**
+ * Initial size for POST
+ * request buffer.
+ */
+#define REQUEST_BUFFER_INITIAL 1024
+
+/**
+ * Maximum POST request size
+ */
+#define REQUEST_BUFFER_MAX (1024*1024)
+
+
+/**
+ * Buffer for POST requests.
+ */
+struct Buffer
+{
+ /**
+ * Allocated memory
+ */
+ char *data;
+
+ /**
+ * Number of valid bytes in buffer.
+ */
+ size_t fill;
+
+ /**
+ * Number of allocated bytes in buffer.
+ */
+ size_t alloc;
+};
+
+
+/**
+ * Initialize a buffer.
+ *
+ * @param buf the buffer to initialize
+ * @param data the initial data
+ * @param data_size size of the initial data
+ * @param alloc_size size of the buffer
+ * @param max_size maximum size that the buffer can grow to
+ * @return a GNUnet result code
+ */
+static int
+buffer_init (struct Buffer *buf, const void *data, size_t data_size, size_t alloc_size, size_t max_size)
+{
+ if (data_size > max_size || alloc_size > max_size)
+ return GNUNET_SYSERR;
+ if (data_size > alloc_size)
+ alloc_size = data_size;
+ buf->data = GNUNET_malloc (alloc_size);
+ memcpy (buf->data, data, data_size);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Free the data in a buffer. Does *not* free
+ * the buffer object itself.
+ *
+ * @param buf buffer to de-initialize
+ */
+static void
+buffer_deinit (struct Buffer *buf)
+{
+ GNUNET_free (buf->data);
+ buf->data = NULL;
+}
+
+
+/**
+ * Append data to a buffer, growing the buffer if necessary.
+ *
+ * @param buf the buffer to append to
+ * @param data the data to append
+ * @param size the size of @a data
+ * @param max_size maximum size that the buffer can grow to
+ * @return GNUNET_OK on success,
+ * GNUNET_NO if the buffer can't accomodate for the new data
+ * GNUNET_SYSERR on fatal error (out of memory?)
+ */
+static int
+buffer_append (struct Buffer *buf, const void *data, size_t data_size, size_t max_size)
+{
+ if (buf->fill + data_size > max_size)
+ return GNUNET_NO;
+ if (data_size + buf->fill > buf->alloc)
+ {
+ char *new_buf;
+ size_t new_size = buf->alloc;
+ while (new_size < buf->fill + data_size)
+ new_size += 2;
+ if (new_size > max_size)
+ return GNUNET_NO;
+ new_buf = GNUNET_malloc (new_size);
+ memcpy (new_buf, buf->data, buf->fill);
+ buf->data = new_buf;
+ buf->alloc = new_size;
+ }
+ memcpy (buf->data + buf->fill, data, data_size);
+ buf->fill += data_size;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Send JSON object as response. Decreases the reference count of the
+ * JSON object.
+ *
+ * @param connection the MHD connection
+ * @param json the json object
+ * @param status_code the http status code
+ * @return MHD result code
+ */
+int
+send_response_json (struct MHD_Connection *connection,
+ json_t *json,
+ unsigned int status_code)
+{
+ struct MHD_Response *resp;
+ char *json_str;
+
+ json_str = json_dumps (json, JSON_INDENT(2));
+ json_decref (json);
+ resp = MHD_create_response_from_buffer (strlen (json_str), json_str,
+ MHD_RESPMEM_MUST_FREE);
+ if (NULL == resp)
+ return MHD_NO;
+ return MHD_queue_response (connection, status_code, resp);
+}
+
+
+/**
+ * Send a JSON object via an MHD connection,
+ * specified with the JANSSON pack syntax (see json_pack).
+ *
+ * @param connection connection to send the JSON over
+ * @param http_code HTTP status for the response
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD_YES on success or MHD_NO on error
+ */
+int
+request_send_json_pack (struct MHD_Connection *connection,
+ unsigned int http_code,
+ const char *fmt, ...)
+{
+ json_t *msg;
+ va_list argp;
+ int ret;
+
+ va_start(argp, fmt);
+ msg = json_vpack_ex (NULL, 0, fmt, argp);
+ va_end(argp);
+ if (NULL == msg)
+ return MHD_NO;
+ ret = send_response_json (connection, msg, http_code);
+ json_decref (msg);
+ return ret;
+}
+
+
+/**
+ * Process a POST request containing a JSON object.
+ *
+ * @param connection the MHD connection
+ * @param con_cs the closure (contains a 'struct Buffer *')
+ * @param upload_data the POST data
+ * @param upload_data_size the POST data size
+ * @param json the JSON object for a completed request
+ *
+ * @returns
+ * GNUNET_YES if json object was parsed
+ * GNUNET_NO is request incomplete or invalid
+ * GNUNET_SYSERR on internal error
+ */
+int
+process_post_json (struct MHD_Connection *connection,
+ void **con_cls,
+ const char *upload_data,
+ size_t *upload_data_size,
+ json_t **json)
+{
+ struct Buffer *r = *con_cls;
+
+ if (NULL == *con_cls)
+ {
+ /* We are seeing a fresh POST request. */
+
+ r = GNUNET_new (struct Buffer);
+ if (GNUNET_OK != buffer_init (r, upload_data, *upload_data_size,
+ REQUEST_BUFFER_INITIAL, REQUEST_BUFFER_MAX))
+ {
+ *con_cls = NULL;
+ buffer_deinit (r);
+ GNUNET_free (r);
+ return GNUNET_SYSERR;
+ }
+ *upload_data_size = 0;
+ *con_cls = r;
+ return GNUNET_NO;
+ }
+ if (0 != *upload_data_size)
+ {
+ /* We are seeing an old request with more data available. */
+
+ if (GNUNET_OK != buffer_append (r, upload_data, *upload_data_size,
+ REQUEST_BUFFER_MAX))
+ {
+ /* Request too long or we're out of memory. */
+
+ *con_cls = NULL;
+ buffer_deinit (r);
+ GNUNET_free (r);
+ return GNUNET_SYSERR;
+ }
+ *upload_data_size = 0;
+ return GNUNET_NO;
+ }
+
+ /* We have seen the whole request. */
+
+ *json = json_loadb (r->data, r->fill, 0, NULL);
+ buffer_deinit (r);
+ GNUNET_free (r);
+ if (NULL == *json)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Can't parse JSON request body\n");
+ return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ GNUNET_NO, GNUNET_SYSERR,
+ "{s:s}",
+ "error", "invalid json");
+ }
+ *con_cls = NULL;
+
+ return GNUNET_YES;
+}
+
+
+/**
+ * Navigate through a JSON tree.
+ *
+ * Sends an error response if navigation is impossible (i.e.
+ * the JSON object is invalid)
+ *
+ * @param connection the connection to send an error response to
+ * @param root the JSON node to start the navigation at.
+ * @param ... navigation specification (see JNAV_*)
+ * @return GNUNET_YES if navigation was successful
+ * GNUNET_NO if json is malformed, error response was generated
+ * GNUNET_SYSERR on internal error
+ */
+int
+request_json_require_nav (struct MHD_Connection *connection,
+ const json_t *root, ...)
+{
+ va_list argp;
+ int ignore = GNUNET_NO;
+ // what's our current path from 'root'?
+ json_t *path;
+
+ path = json_array ();
+
+ va_start(argp, root);
+
+ while (1)
+ {
+ int command = va_arg(argp, int);
+ switch (command)
+ {
+ case JNAV_FIELD:
+ {
+ const char *fname = va_arg(argp, const char *);
+ if (GNUNET_YES == ignore)
+ break;
+ json_array_append_new (path, json_string (fname));
+ root = json_object_get (root, fname);
+ if (NULL == root)
+ {
+
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s,s:o}",
+ "error", "missing field in JSON",
+ "path", path);
+ ignore = GNUNET_YES;
+ break;
+ }
+ }
+ break;
+ case JNAV_INDEX:
+ {
+ int fnum = va_arg(argp, int);
+ if (GNUNET_YES == ignore)
+ break;
+ json_array_append_new (path, json_integer (fnum));
+ root = json_array_get (root, fnum);
+ if (NULL == root)
+ {
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s, s:o}",
+ "error", "missing index in JSON",
+ "path", path);
+ ignore = GNUNET_YES;
+ break;
+ }
+ }
+ break;
+ case JNAV_RET_DATA:
+ {
+ void *where = va_arg (argp, void *);
+ size_t len = va_arg (argp, size_t);
+ const char *str;
+ int res;
+
+ va_end(argp);
+ if (GNUNET_YES == ignore)
+ return GNUNET_NO;
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s, s:o}",
+ "error", "string expected",
+ "path", path);
+ return GNUNET_NO;
+ }
+ res = GNUNET_STRINGS_string_to_data (str, strlen (str),
+ where, len);
+ if (GNUNET_OK != res)
+ {
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s,s:o}",
+ "error", "malformed binary data in JSON",
+ "path", path);
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+ }
+ break;
+ case JNAV_RET_DATA_VAR:
+ {
+ void **where = va_arg (argp, void **);
+ size_t *len = va_arg (argp, size_t *);
+ const char *str;
+
+ va_end(argp);
+ if (GNUNET_YES == ignore)
+ return GNUNET_NO;
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ *len = (strlen (str) * 5) / 8;
+ if (where != NULL)
+ {
+ int res;
+ *where = GNUNET_malloc (*len);
+ res = GNUNET_STRINGS_string_to_data (str, strlen (str),
+ *where, *len);
+ if (GNUNET_OK != res)
+ {
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s, s:o}",
+ "error", "malformed binary data in JSON",
+ "path", path);
+ return GNUNET_NO;
+ }
+ }
+ return GNUNET_OK;
+ }
+ break;
+ case JNAV_RET_TYPED_JSON:
+ {
+ int typ = va_arg (argp, int);
+ const json_t **r_json = va_arg (argp, const json_t **);
+
+ va_end(argp);
+ if (GNUNET_YES == ignore)
+ return GNUNET_NO;
+ if (typ != -1 && json_typeof (root) != typ)
+ {
+ (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST,
+ 0, 0,
+ "{s:s, s:i, s:i s:o}",
+ "error", "wrong JSON field type",
+ "type_expected", typ,
+ "type_actual", json_typeof (root),
+ "path", path);
+ return GNUNET_NO;
+ }
+ *r_json = root;
+ return GNUNET_OK;
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ }
+ GNUNET_assert (0);
+}
+
+
+
diff --git a/src/util/misc.supp b/src/util/misc.supp
new file mode 100644
index 00000000..afcac612
--- /dev/null
+++ b/src/util/misc.supp
@@ -0,0 +1,28 @@
+{
+ <gnunet_gcrypt_init>
+ Memcheck:Leak
+ match-leak-kinds:reachable
+ ...
+ fun:GNUNET_CRYPTO_random_init
+ fun:call_init.part.0
+ ...
+}
+
+{
+ <mpi_ec_new>
+ Memcheck:Leak
+ match-leak-kinds:reachable
+ ...
+ fun:point_from_keyparam
+ fun:_gcry_mpi_ec_new
+ ...
+}
+
+{
+ <gnunet_log_setup>
+ Memcheck:Leak
+ match-leak-kinds:reachable
+ ...
+ fun:GNUNET_log_setup
+ ...
+} \ No newline at end of file
diff --git a/src/util/rsa.c b/src/util/rsa.c
new file mode 100644
index 00000000..cde56be9
--- /dev/null
+++ b/src/util/rsa.c
@@ -0,0 +1,925 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file util/rsa.c
+ * @brief RSA key management utilities. Most of the code here is taken from
+ * gnunet-0.9.5a
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ *
+ * Authors of the gnunet code:
+ * Christian Grothoff
+ * Krista Bennett
+ * Gerd Knorr <kraxel@bytesex.org>
+ * Ioana Patrascu
+ * Tzvetan Horozov
+ */
+
+#include "platform.h"
+#include "gcrypt.h"
+#include "gnunet/gnunet_util_lib.h"
+#include "taler_rsa.h"
+
+
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+/**
+ * Log an error message at log-level 'level' that indicates
+ * a failure of the command 'cmd' with the message given
+ * by gcry_strerror(rc).
+ */
+#define LOG_GCRY(level, cmd, rc) do { LOG(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0)
+
+/**
+ * Shorthand to cleanup non null mpi data types
+ */
+#define mpi_release_non_null(mpi) \
+ if (NULL != mpi) gcry_mpi_release (mpi);
+
+/**
+ * The private information of an RSA key pair.
+ * NOTE: this must match the definition in crypto_ksk.c and gnunet-rsa.c!
+ */
+struct TALER_RSA_PrivateKey
+{
+ /**
+ * Libgcrypt S-expression for the ECC key.
+ */
+ gcry_sexp_t sexp;
+};
+
+
+/**
+ * Extract values from an S-expression.
+ *
+ * @param array where to store the result(s)
+ * @param sexp S-expression to parse
+ * @param topname top-level name in the S-expression that is of interest
+ * @param elems names of the elements to extract
+ * @return 0 on success
+ */
+static int
+key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname,
+ const char *elems)
+{
+ gcry_sexp_t list;
+ gcry_sexp_t l2;
+ const char *s;
+ unsigned int i;
+ unsigned int idx;
+
+ if (! (list = gcry_sexp_find_token (sexp, topname, 0)))
+ return 1;
+ l2 = gcry_sexp_cadr (list);
+ gcry_sexp_release (list);
+ list = l2;
+ if (! list)
+ return 2;
+ idx = 0;
+ for (s = elems; *s; s++, idx++)
+ {
+ if (! (l2 = gcry_sexp_find_token (list, s, 1)))
+ {
+ for (i = 0; i < idx; i++)
+ {
+ gcry_free (array[i]);
+ array[i] = NULL;
+ }
+ gcry_sexp_release (list);
+ return 3; /* required parameter not found */
+ }
+ array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release (l2);
+ if (! array[idx])
+ {
+ for (i = 0; i < idx; i++)
+ {
+ gcry_free (array[i]);
+ array[i] = NULL;
+ }
+ gcry_sexp_release (list);
+ return 4; /* required parameter is invalid */
+ }
+ }
+ gcry_sexp_release (list);
+ return 0;
+}
+
+/**
+ * If target != size, move target bytes to the
+ * end of the size-sized buffer and zero out the
+ * first target-size bytes.
+ *
+ * @param buf original buffer
+ * @param size number of bytes in the buffer
+ * @param target target size of the buffer
+ */
+static void
+adjust (unsigned char *buf, size_t size, size_t target)
+{
+ if (size < target)
+ {
+ memmove (&buf[target - size], buf, size);
+ memset (buf, 0, target - size);
+ }
+}
+
+
+/**
+ * Create a new private key. Caller must free return value.
+ *
+ * @return fresh private key
+ */
+struct TALER_RSA_PrivateKey *
+TALER_RSA_key_create ()
+{
+ struct TALER_RSA_PrivateKey *ret;
+ gcry_sexp_t s_key;
+ gcry_sexp_t s_keyparam;
+
+ GNUNET_assert (0 ==
+ gcry_sexp_build (&s_keyparam, NULL,
+ "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))",
+ 2048));
+ GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam));
+ gcry_sexp_release (s_keyparam);
+#if EXTRA_CHECKS
+ GNUNET_assert (0 == gcry_pk_testkey (s_key));
+#endif
+ ret = GNUNET_malloc (sizeof (struct TALER_RSA_PrivateKey));
+ ret->sexp = s_key;
+ return ret;
+}
+
+
+/**
+ * Free memory occupied by the private key.
+ *
+ * @param key pointer to the memory to free
+ */
+void
+TALER_RSA_key_free (struct TALER_RSA_PrivateKey *key)
+{
+ gcry_sexp_release (key->sexp);
+ GNUNET_free (key);
+}
+
+
+/**
+ * Encode the private key in a format suitable for
+ * storing it into a file.
+ * @return encoding of the private key
+ */
+struct TALER_RSA_PrivateKeyBinaryEncoded *
+TALER_RSA_encode_key (const struct TALER_RSA_PrivateKey *hostkey)
+{
+ struct TALER_RSA_PrivateKeyBinaryEncoded *retval;
+ gcry_mpi_t pkv[6];
+ void *pbu[6];
+ size_t sizes[6];
+ int rc;
+ int i;
+ int size;
+
+#if EXTRA_CHECKS
+ if (gcry_pk_testkey (hostkey->sexp))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+#endif
+
+ memset (pkv, 0, sizeof (gcry_mpi_t) * 6);
+ rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpqu");
+ if (rc)
+ rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpqu");
+ if (rc)
+ rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpq");
+ if (rc)
+ rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpq");
+ if (rc)
+ rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "ned");
+ if (rc)
+ rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "ned");
+ GNUNET_assert (0 == rc);
+ size = sizeof (struct TALER_RSA_PrivateKeyBinaryEncoded);
+ for (i = 0; i < 6; i++)
+ {
+ if (NULL != pkv[i])
+ {
+ GNUNET_assert (0 ==
+ gcry_mpi_aprint (GCRYMPI_FMT_USG,
+ (unsigned char **) &pbu[i], &sizes[i],
+ pkv[i]));
+ size += sizes[i];
+ }
+ else
+ {
+ pbu[i] = NULL;
+ sizes[i] = 0;
+ }
+ }
+ GNUNET_assert (size < 65536);
+ retval = GNUNET_malloc (size);
+ retval->len = htons (size);
+ i = 0;
+ retval->sizen = htons (sizes[0]);
+ memcpy (&((char *) (&retval[1]))[i], pbu[0], sizes[0]);
+ i += sizes[0];
+ retval->sizee = htons (sizes[1]);
+ memcpy (&((char *) (&retval[1]))[i], pbu[1], sizes[1]);
+ i += sizes[1];
+ retval->sized = htons (sizes[2]);
+ memcpy (&((char *) (&retval[1]))[i], pbu[2], sizes[2]);
+ i += sizes[2];
+ /* swap p and q! */
+ retval->sizep = htons (sizes[4]);
+ memcpy (&((char *) (&retval[1]))[i], pbu[4], sizes[4]);
+ i += sizes[4];
+ retval->sizeq = htons (sizes[3]);
+ memcpy (&((char *) (&retval[1]))[i], pbu[3], sizes[3]);
+ i += sizes[3];
+ retval->sizedmp1 = htons (0);
+ retval->sizedmq1 = htons (0);
+ memcpy (&((char *) (&retval[1]))[i], pbu[5], sizes[5]);
+ for (i = 0; i < 6; i++)
+ {
+ if (pkv[i] != NULL)
+ gcry_mpi_release (pkv[i]);
+ if (pbu[i] != NULL)
+ free (pbu[i]);
+ }
+ return retval;
+}
+
+
+/**
+ * Extract the public key of the given private key.
+ *
+ * @param priv the private key
+ * @param pub where to write the public key
+ */
+void
+TALER_RSA_key_get_public (const struct TALER_RSA_PrivateKey *priv,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pub)
+{
+ gcry_mpi_t skey[2];
+ size_t size;
+ int rc;
+
+ rc = key_from_sexp (skey, priv->sexp, "public-key", "ne");
+ if (0 != rc)
+ rc = key_from_sexp (skey, priv->sexp, "private-key", "ne");
+ if (0 != rc)
+ rc = key_from_sexp (skey, priv->sexp, "rsa", "ne");
+ GNUNET_assert (0 == rc);
+ pub->len =
+ htons (sizeof (struct TALER_RSA_PublicKeyBinaryEncoded) -
+ sizeof (pub->padding));
+ pub->sizen = htons (TALER_RSA_DATA_ENCODING_LENGTH);
+ pub->padding = 0;
+ size = TALER_RSA_DATA_ENCODING_LENGTH;
+ GNUNET_assert (0 ==
+ gcry_mpi_print (GCRYMPI_FMT_USG, &pub->key[0], size, &size,
+ skey[0]));
+ adjust (&pub->key[0], size, TALER_RSA_DATA_ENCODING_LENGTH);
+ size = TALER_RSA_KEY_LENGTH - TALER_RSA_DATA_ENCODING_LENGTH;
+ GNUNET_assert (0 ==
+ gcry_mpi_print (GCRYMPI_FMT_USG,
+ &pub->key
+ [TALER_RSA_DATA_ENCODING_LENGTH], size,
+ &size, skey[1]));
+ adjust (&pub->key[TALER_RSA_DATA_ENCODING_LENGTH], size,
+ TALER_RSA_KEY_LENGTH -
+ TALER_RSA_DATA_ENCODING_LENGTH);
+ gcry_mpi_release (skey[0]);
+ gcry_mpi_release (skey[1]);
+}
+
+
+/**
+ * Decode the private key from the data-format back
+ * to the "normal", internal format.
+ *
+ * @param buf the buffer where the private key data is stored
+ * @param len the length of the data in 'buffer'
+ * @return NULL on error
+ */
+struct TALER_RSA_PrivateKey *
+TALER_RSA_decode_key (const char *buf, uint16_t len)
+{
+ struct TALER_RSA_PrivateKey *ret;
+ const struct TALER_RSA_PrivateKeyBinaryEncoded *encoding =
+ (const struct TALER_RSA_PrivateKeyBinaryEncoded *) buf;
+ gcry_sexp_t res;
+ gcry_mpi_t n;
+ gcry_mpi_t e;
+ gcry_mpi_t d;
+ gcry_mpi_t p;
+ gcry_mpi_t q;
+ gcry_mpi_t u;
+ int rc;
+ size_t size;
+ size_t pos;
+ uint16_t enc_len;
+ size_t erroff;
+
+ enc_len = ntohs (encoding->len);
+ if (len != enc_len)
+ return NULL;
+
+ pos = 0;
+ size = ntohs (encoding->sizen);
+ rc = gcry_mpi_scan (&n, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ pos += ntohs (encoding->sizen);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ return NULL;
+ }
+ size = ntohs (encoding->sizee);
+ rc = gcry_mpi_scan (&e, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ pos += ntohs (encoding->sizee);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ return NULL;
+ }
+ size = ntohs (encoding->sized);
+ rc = gcry_mpi_scan (&d, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ pos += ntohs (encoding->sized);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ return NULL;
+ }
+ /* swap p and q! */
+ size = ntohs (encoding->sizep);
+ if (size > 0)
+ {
+ rc = gcry_mpi_scan (&q, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ pos += ntohs (encoding->sizep);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ gcry_mpi_release (d);
+ return NULL;
+ }
+ }
+ else
+ q = NULL;
+ size = ntohs (encoding->sizeq);
+ if (size > 0)
+ {
+ rc = gcry_mpi_scan (&p, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ pos += ntohs (encoding->sizeq);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ gcry_mpi_release (d);
+ if (NULL != q)
+ gcry_mpi_release (q);
+ return NULL;
+ }
+ }
+ else
+ p = NULL;
+ pos += ntohs (encoding->sizedmp1);
+ pos += ntohs (encoding->sizedmq1);
+ size =
+ ntohs (encoding->len) - sizeof (struct TALER_RSA_PrivateKeyBinaryEncoded) - pos;
+ if (size > 0)
+ {
+ rc = gcry_mpi_scan (&u, GCRYMPI_FMT_USG,
+ &((const unsigned char *) (&encoding[1]))[pos], size,
+ &size);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ gcry_mpi_release (d);
+ if (NULL != p)
+ gcry_mpi_release (p);
+ if (NULL != q)
+ gcry_mpi_release (q);
+ return NULL;
+ }
+ }
+ else
+ u = NULL;
+
+ if ((NULL != p) && (NULL != q) && (NULL != u))
+ {
+ rc = gcry_sexp_build (&res, &erroff,
+ "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)(u %m)))",
+ n, e, d, p, q, u);
+ }
+ else
+ {
+ if ((NULL != p) && (NULL != q))
+ {
+ rc = gcry_sexp_build (&res, &erroff,
+ "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)))",
+ n, e, d, p, q);
+ }
+ else
+ {
+ rc = gcry_sexp_build (&res, &erroff,
+ "(private-key(rsa(n %m)(e %m)(d %m)))", n, e, d);
+ }
+ }
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ gcry_mpi_release (d);
+ if (NULL != p)
+ gcry_mpi_release (p);
+ if (NULL != q)
+ gcry_mpi_release (q);
+ if (NULL != u)
+ gcry_mpi_release (u);
+
+ if (0 != rc)
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
+ if (0 != (rc = gcry_pk_testkey (res)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc);
+ return NULL;
+ }
+ ret = GNUNET_malloc (sizeof (struct TALER_RSA_PrivateKey));
+ ret->sexp = res;
+ return ret;
+}
+
+
+/**
+ * Convert a public key to a string.
+ *
+ * @param pub key to convert
+ * @return string representing 'pub'
+ */
+char *
+TALER_RSA_public_key_to_string (const struct TALER_RSA_PublicKeyBinaryEncoded *pub)
+{
+ char *pubkeybuf;
+ size_t keylen = (sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)) * 8;
+ char *end;
+
+ if (keylen % 5 > 0)
+ keylen += 5 - keylen % 5;
+ keylen /= 5;
+ pubkeybuf = GNUNET_malloc (keylen + 1);
+ end = GNUNET_STRINGS_data_to_string ((unsigned char *) pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded),
+ pubkeybuf,
+ keylen);
+ if (NULL == end)
+ {
+ GNUNET_free (pubkeybuf);
+ return NULL;
+ }
+ *end = '\0';
+ return pubkeybuf;
+}
+
+
+/**
+ * Convert a string representing a public key to a public key.
+ *
+ * @param enc encoded public key
+ * @param enclen number of bytes in enc (without 0-terminator)
+ * @param pub where to store the public key
+ * @return GNUNET_OK on success
+ */
+int
+TALER_RSA_public_key_from_string (const char *enc,
+ size_t enclen,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pub)
+{
+ size_t keylen = (sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)) * 8;
+
+ if (keylen % 5 > 0)
+ keylen += 5 - keylen % 5;
+ keylen /= 5;
+ if (enclen != keylen)
+ return GNUNET_SYSERR;
+
+ if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen,
+ (unsigned char*) pub,
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)))
+ return GNUNET_SYSERR;
+ if ( (ntohs (pub->len) != sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)) ||
+ (ntohs (pub->padding) != 0) ||
+ (ntohs (pub->sizen) != TALER_RSA_DATA_ENCODING_LENGTH) )
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Convert the data specified in the given purpose argument to an
+ * S-expression suitable for signature operations.
+ *
+ * @param ptr pointer to the data to convert
+ * @param size the size of the data
+ * @return converted s-expression
+ */
+static gcry_sexp_t
+data_to_sexp (const void *ptr, size_t size)
+{
+ gcry_mpi_t value;
+ gcry_sexp_t data;
+
+ value = NULL;
+ data = NULL;
+ GNUNET_assert (0 == gcry_mpi_scan (&value, GCRYMPI_FMT_USG, ptr, size, NULL));
+ GNUNET_assert (0 == gcry_sexp_build (&data, NULL, "(data (flags raw) (value %M))", value));
+ gcry_mpi_release (value);
+ return data;
+}
+
+
+/**
+ * Sign the given hash block.
+ *
+ * @param key private key to use for the signing
+ * @param hash the block containing the hash of the message to sign
+ * @param hash_size the size of the hash block
+ * @param sig where to write the signature
+ * @return GNUNET_SYSERR on error, GNUNET_OK on success
+ */
+int
+TALER_RSA_sign (const struct TALER_RSA_PrivateKey *key,
+ const void *hash,
+ size_t hash_size,
+ struct TALER_RSA_Signature *sig)
+{
+ gcry_sexp_t result;
+ gcry_sexp_t data;
+ size_t ssize;
+ gcry_mpi_t rval;
+
+ data = data_to_sexp (hash, hash_size);
+ GNUNET_assert (0 == gcry_pk_sign (&result, data, key->sexp));
+ gcry_sexp_release (data);
+ GNUNET_assert (0 == key_from_sexp (&rval, result, "rsa", "s"));
+ gcry_sexp_release (result);
+ ssize = sizeof (struct TALER_RSA_Signature);
+ GNUNET_assert (0 ==
+ gcry_mpi_print (GCRYMPI_FMT_USG, (unsigned char *) sig, ssize,
+ &ssize, rval));
+ gcry_mpi_release (rval);
+ adjust (sig->sig, ssize, sizeof (struct TALER_RSA_Signature));
+ return GNUNET_OK;
+}
+
+
+/**
+ * Convert the given public key from the network format to the
+ * S-expression that can be used by libgcrypt.
+ *
+ * @param publicKey public key to decode
+ * @return NULL on error
+ */
+static gcry_sexp_t
+decode_public_key (const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey)
+{
+ gcry_sexp_t result;
+ gcry_mpi_t n;
+ gcry_mpi_t e;
+ size_t size;
+ size_t erroff;
+ int rc;
+
+ if ((ntohs (publicKey->sizen) != TALER_RSA_DATA_ENCODING_LENGTH) ||
+ (ntohs (publicKey->len) !=
+ sizeof (struct TALER_RSA_PublicKeyBinaryEncoded) -
+ sizeof (publicKey->padding)))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ size = TALER_RSA_DATA_ENCODING_LENGTH;
+ if (0 != (rc = gcry_mpi_scan (&n, GCRYMPI_FMT_USG, &publicKey->key[0], size, &size)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ return NULL;
+ }
+ size = TALER_RSA_KEY_LENGTH - TALER_RSA_DATA_ENCODING_LENGTH;
+ if (0 != (rc = gcry_mpi_scan (&e, GCRYMPI_FMT_USG,
+ &publicKey->key[TALER_RSA_DATA_ENCODING_LENGTH],
+ size, &size)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ gcry_mpi_release (n);
+ return NULL;
+ }
+ rc = gcry_sexp_build (&result, &erroff, "(public-key(rsa(n %m)(e %m)))", n,
+ e);
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+ if (0 != rc)
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); /* erroff gives more info */
+ return NULL;
+ }
+ return result;
+}
+
+
+/**
+ * Verify signature with the given hash.
+ *
+ * @param hash the hash code to verify against the signature
+ * @param sig signature that is being validated
+ * @param publicKey public key of the signer
+ * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid
+ */
+int
+TALER_RSA_hash_verify (const struct GNUNET_HashCode *hash,
+ const struct TALER_RSA_Signature *sig,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey)
+{
+ gcry_sexp_t data;
+ gcry_sexp_t sigdata;
+ size_t size;
+ gcry_mpi_t val;
+ gcry_sexp_t psexp;
+ size_t erroff;
+ int rc;
+
+ size = sizeof (struct TALER_RSA_Signature);
+ GNUNET_assert (0 ==
+ gcry_mpi_scan (&val, GCRYMPI_FMT_USG,
+ (const unsigned char *) sig, size, &size));
+ GNUNET_assert (0 ==
+ gcry_sexp_build (&sigdata, &erroff, "(sig-val(rsa(s %m)))",
+ val));
+ gcry_mpi_release (val);
+ data = data_to_sexp (hash, sizeof (struct GNUNET_HashCode));
+ if (! (psexp = decode_public_key (publicKey)))
+ {
+ gcry_sexp_release (data);
+ gcry_sexp_release (sigdata);
+ return GNUNET_SYSERR;
+ }
+ rc = gcry_pk_verify (sigdata, data, psexp);
+ gcry_sexp_release (psexp);
+ gcry_sexp_release (data);
+ gcry_sexp_release (sigdata);
+ if (rc)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("RSA signature verification failed at %s:%d: %s\n"), __FILE__,
+ __LINE__, gcry_strerror (rc));
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Verify signature on the given message
+ *
+ * @param msg the message
+ * @param size the size of the message
+ * @param sig signature that is being validated
+ * @param publicKey public key of the signer
+ * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid
+ */
+int
+TALER_RSA_verify (const void *msg, size_t size,
+ const struct TALER_RSA_Signature *sig,
+ const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey)
+{
+ struct GNUNET_HashCode hash;
+
+ GNUNET_CRYPTO_hash (msg, size, &hash);
+ return TALER_RSA_hash_verify (&hash, sig, publicKey);
+}
+
+/**
+ * The blinding key is equal in length to the RSA modulus
+ */
+#define TALER_RSA_BLINDING_KEY_LEN TALER_RSA_DATA_ENCODING_LENGTH
+
+struct TALER_RSA_BlindingKey
+{
+ /**
+ * The blinding factor
+ */
+ gcry_mpi_t r;
+};
+
+struct TALER_RSA_BlindingKey *
+TALER_RSA_blinding_key_create ()
+{
+ struct TALER_RSA_BlindingKey *blind;
+
+ blind = GNUNET_new (struct TALER_RSA_BlindingKey);
+ blind->r = gcry_mpi_new (TALER_RSA_BLINDING_KEY_LEN * 8);
+ gcry_mpi_randomize (blind->r, TALER_RSA_BLINDING_KEY_LEN * 8, GCRY_STRONG_RANDOM);
+ return blind;
+}
+
+
+void
+TALER_RSA_blinding_key_destroy (struct TALER_RSA_BlindingKey *bkey)
+{
+ gcry_mpi_release (bkey->r);
+ GNUNET_free (bkey);
+}
+
+
+struct TALER_RSA_BlindedSignaturePurpose *
+TALER_RSA_message_blind (const void *msg, size_t size,
+ struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pkey)
+{
+ struct TALER_RSA_BlindedSignaturePurpose *bsp;
+ struct GNUNET_HashCode hash;
+ gcry_sexp_t psexp;
+ gcry_mpi_t data;
+ gcry_mpi_t skey[2];
+ gcry_mpi_t r_e;
+ gcry_mpi_t data_r_e;
+ size_t rsize;
+ gcry_error_t rc;
+ int ret;
+
+ bsp = NULL;
+ psexp = NULL;
+ data = NULL;
+ skey[0] = skey[1] = NULL;
+ r_e = NULL;
+ data_r_e = NULL;
+ rsize = 0;
+ rc = 0;
+ ret = 0;
+ if (! (psexp = decode_public_key (pkey)))
+ return NULL;
+ ret = key_from_sexp (skey, psexp, "public-key", "ne");
+ if (0 != ret)
+ ret = key_from_sexp (skey, psexp, "rsa", "ne");
+ gcry_sexp_release (psexp);
+ psexp = NULL;
+ GNUNET_assert (0 == ret);
+ GNUNET_CRYPTO_hash (msg, size, &hash);
+ if (0 != (rc=gcry_mpi_scan (&data, GCRYMPI_FMT_USG,
+ (const unsigned char *) msg, size, &rsize)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_WARNING, "gcry_mpi_scan", rc);
+ goto cleanup;
+ }
+ r_e = gcry_mpi_new (0);
+ gcry_mpi_powm (r_e, bkey->r,
+ skey[1], /* e */
+ skey[0]); /* n */
+
+ data_r_e = gcry_mpi_new (0);
+ gcry_mpi_mulm (data_r_e, data, r_e, skey[0]);
+
+ bsp = GNUNET_new (struct TALER_RSA_BlindedSignaturePurpose);
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG,
+ (unsigned char *) bsp,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose),
+ &rsize,
+ data_r_e);
+ GNUNET_assert (0 == rc);
+ adjust ((unsigned char *) bsp, rsize,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose));
+
+ cleanup:
+ if (NULL != psexp) gcry_sexp_release (psexp);
+ mpi_release_non_null (skey[0]);
+ mpi_release_non_null (skey[1]);
+ mpi_release_non_null (data);
+ mpi_release_non_null (r_e);
+ mpi_release_non_null (data_r_e);
+ return bsp;
+}
+
+
+int
+TALER_RSA_unblind (struct TALER_RSA_Signature *sig,
+ struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_PublicKeyBinaryEncoded *pkey)
+{
+ gcry_sexp_t psexp;
+ gcry_mpi_t skey;
+ gcry_mpi_t sigval;
+ gcry_mpi_t r_inv;
+ gcry_mpi_t ubsig;
+ size_t rsize;
+ gcry_error_t rc;
+ int ret;
+
+ psexp = NULL;
+ skey = NULL;
+ sigval = NULL;
+ r_inv = NULL;
+ ubsig = NULL;
+ rsize = 0;
+ rc = 0;
+ ret = GNUNET_SYSERR;
+ if (! (psexp = decode_public_key (pkey)))
+ return GNUNET_SYSERR;
+ ret = key_from_sexp (&skey, psexp, "public-key", "n");
+ if (0 != ret)
+ ret = key_from_sexp (&skey, psexp, "rsa", "n");
+ gcry_sexp_release (psexp);
+ psexp = NULL;
+ if (0 != (rc = gcry_mpi_scan (&sigval, GCRYMPI_FMT_USG,
+ (const unsigned char *) sig,
+ sizeof (struct TALER_RSA_Signature),
+ &rsize)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
+ goto cleanup;
+ }
+ r_inv = gcry_mpi_new (0);
+ GNUNET_assert (1 == gcry_mpi_invm (r_inv, bkey->r, skey)); /* n: skey */
+ ubsig = gcry_mpi_new (0);
+ gcry_mpi_mulm (ubsig, sigval, r_inv, skey);
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG,
+ (unsigned char *) sig,
+ sizeof (struct TALER_RSA_Signature),
+ &rsize,
+ ubsig);
+ GNUNET_assert (0 == rc);
+ adjust ((unsigned char *) sig, rsize, sizeof (struct TALER_RSA_Signature));
+ ret = GNUNET_OK;
+
+ cleanup:
+ if (NULL != psexp) gcry_sexp_release (psexp);
+ mpi_release_non_null (skey);
+ mpi_release_non_null (sigval);
+ mpi_release_non_null (r_inv);
+ mpi_release_non_null (ubsig);
+ return ret;
+}
+
+
+/**
+ * Encode a blinding key
+ *
+ * @param bkey the blinding key to encode
+ * @param bkey_enc where to store the encoded binary key
+ * @return #GNUNET_OK upon successful encoding; #GNUNET_SYSERR upon failure
+ */
+int
+TALER_RSA_blinding_key_encode (struct TALER_RSA_BlindingKey *bkey,
+ struct TALER_RSA_BlindingKeyBinaryEncoded *bkey_enc)
+{
+ GNUNET_abort (); /* FIXME: not implemented */
+}
+
+
+/**
+ * Decode a blinding key from its encoded form
+ *
+ * @param bkey_enc the encoded blinding key
+ * @return the decoded blinding key; NULL upon error
+ */
+struct TALER_RSA_BlindingKey *
+TALER_RSA_blinding_key_decode (struct TALER_RSA_BlindingKeyBinaryEncoded *bkey_enc)
+{
+ GNUNET_abort (); /* FIXME: not implemented */
+}
+
+/* end of util/rsa.c */
diff --git a/src/util/test_hash_context.c b/src/util/test_hash_context.c
new file mode 100644
index 00000000..e5110f21
--- /dev/null
+++ b/src/util/test_hash_context.c
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file util/test_hash_context.c
+ * @brief test case for incremental hashing
+ * @author Florian Dold
+ */
+
+#include "platform.h"
+#include "taler_util.h"
+#include <gcrypt.h>
+
+#define LEN 1234
+
+int main()
+{
+ char data[1234];
+ struct GNUNET_HashCode hc1;
+ struct GNUNET_HashCode hc2;
+ struct TALER_HashContext hctx;
+
+ memset (data, 42, LEN);
+
+ TALER_hash_context_start (&hctx);
+ TALER_hash_context_read (&hctx, data, LEN);
+ TALER_hash_context_finish (&hctx, &hc1);
+
+ GNUNET_CRYPTO_hash (data, LEN, &hc2);
+
+ if (0 == memcmp (&hc1, &hc2, sizeof (struct GNUNET_HashCode)))
+ return 0;
+ return 1;
+}
+
diff --git a/src/util/test_rsa.c b/src/util/test_rsa.c
new file mode 100644
index 00000000..ac3ae2cd
--- /dev/null
+++ b/src/util/test_rsa.c
@@ -0,0 +1,112 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file util/test_rsa.c
+ * @brief testcase for utility functions for RSA cryptography
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+#include "platform.h"
+#include "taler_rsa.h"
+#include <gnunet/gnunet_util_lib.h>
+
+#define TEST_PURPOSE UINT32_MAX
+
+
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+int
+main (int argc, char *argv[])
+{
+#define RND_BLK_SIZE 4096
+ unsigned char rnd_blk[RND_BLK_SIZE];
+ struct TALER_RSA_PrivateKey *priv;
+ struct TALER_RSA_PrivateKeyBinaryEncoded *priv_enc;
+ struct TALER_RSA_PublicKeyBinaryEncoded pubkey;
+ struct TALER_RSA_BlindingKey *bkey;
+ struct TALER_RSA_BlindedSignaturePurpose *bsp;
+ struct TALER_RSA_Signature sig;
+ struct GNUNET_HashCode hash;
+ int ret;
+
+ priv = NULL;
+ bsp = NULL;
+ bkey = NULL;
+ ret = 1;
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, rnd_blk,
+ RND_BLK_SIZE);
+ GNUNET_CRYPTO_hash (rnd_blk, RND_BLK_SIZE, &hash);
+ priv = TALER_RSA_key_create ();
+ GNUNET_assert (NULL != priv);
+ EXITIF (GNUNET_OK != TALER_RSA_sign (priv,
+ &hash, sizeof (hash),
+ &sig));
+ TALER_RSA_key_get_public (priv, &pubkey);
+ EXITIF (NULL == (priv_enc = TALER_RSA_encode_key (priv)));
+ TALER_RSA_key_free (priv);
+ priv = NULL;
+ EXITIF (NULL == (priv = TALER_RSA_decode_key ((const char *) priv_enc,
+ ntohs (priv_enc->len))));
+ GNUNET_free (priv_enc);
+ priv_enc = NULL;
+ EXITIF (GNUNET_OK != TALER_RSA_hash_verify (&hash,
+ &sig,
+ &pubkey));
+ EXITIF (GNUNET_OK != TALER_RSA_verify (rnd_blk,
+ RND_BLK_SIZE,
+ &sig,
+ &pubkey));
+
+ /* test blind signing */
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, rnd_blk,
+ RND_BLK_SIZE);
+ GNUNET_CRYPTO_hash (rnd_blk, RND_BLK_SIZE, &hash);
+ (void) memset (&sig, 0, sizeof (struct TALER_RSA_Signature));
+ EXITIF (NULL == (bkey = TALER_RSA_blinding_key_create ()));
+ EXITIF (NULL == (bsp =
+ TALER_RSA_message_blind (&hash, sizeof (hash),
+ bkey, &pubkey)));
+ EXITIF (GNUNET_OK != TALER_RSA_sign (priv,
+ bsp,
+ sizeof (struct TALER_RSA_BlindedSignaturePurpose),
+ &sig));
+ EXITIF (GNUNET_OK != TALER_RSA_unblind (&sig,
+ bkey,
+ &pubkey));
+ EXITIF (GNUNET_OK != TALER_RSA_hash_verify (&hash,
+ &sig,
+ &pubkey));
+ ret = 0; /* all OK */
+
+ EXITIF_exit:
+ if (NULL != priv)
+ {
+ TALER_RSA_key_free (priv);
+ priv = NULL;
+ }
+ if (NULL != priv_enc)
+ {
+ GNUNET_free (priv_enc);
+ priv_enc = NULL;
+ }
+ if (NULL != bkey)
+ TALER_RSA_blinding_key_destroy (bkey);
+ GNUNET_free_non_null (bsp);
+ return ret;
+}
diff --git a/src/util/util.c b/src/util/util.c
new file mode 100644
index 00000000..3677bcbd
--- /dev/null
+++ b/src/util/util.c
@@ -0,0 +1,528 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file util.c
+ * @brief Common utility functions
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+
+#include "platform.h"
+#include "taler_util.h"
+#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <gcrypt.h>
+
+#define CURVE "Ed25519"
+
+#define AMOUNT_FRAC_BASE 1000000
+#define AMOUNT_FRAC_LEN 6
+
+
+
+static void
+fatal_error_handler (void *cls, int wtf, const char *msg)
+{
+ LOG_ERROR("Fatal error in Gcrypt: %s\n", msg);
+ abort();
+}
+
+
+/**
+ * Initialize Gcrypt library.
+ */
+void
+TALER_gcrypt_init()
+{
+ gcry_set_fatalerror_handler (&fatal_error_handler, NULL);
+ TALER_assert_as(gcry_check_version(NEED_LIBGCRYPT_VERSION),
+ "libgcrypt version mismatch");
+ /* Disable secure memory. */
+ gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+}
+
+
+/**
+ * Generate a ECC private key.
+ *
+ * @return the s-expression representing the generated ECC private key; NULL
+ * upon error
+ */
+gcry_sexp_t
+TALER_genkey ()
+{
+ gcry_sexp_t priv_sexp;
+ gcry_sexp_t s_keyparam;
+ int rc;
+
+ if (0 != (rc = gcry_sexp_build (&s_keyparam, NULL,
+ "(genkey(ecc(curve \"" CURVE "\")"
+ "(flags eddsa)))")))
+ {
+ LOG_GCRY_ERROR ("gcry_sexp_build", rc);
+ return NULL;
+ }
+ if (0 != (rc = gcry_pk_genkey (&priv_sexp, s_keyparam)))
+ {
+ LOG_GCRY_ERROR ("gcry_pk_genkey", rc);
+ gcry_sexp_release (s_keyparam);
+ return NULL;
+ }
+ gcry_sexp_release (s_keyparam);
+ if (0 != (rc = gcry_pk_testkey (priv_sexp)))
+ {
+ LOG_GCRY_ERROR("gcry_pk_testkey", rc);
+ gcry_sexp_release (priv_sexp);
+ return NULL;
+ }
+ return priv_sexp;
+}
+
+
+/**
+ * Parse money amount description, in the format "A:B.C".
+ *
+ * @param str amount description
+ * @param denom amount to write the result to
+ * @return GNUNET_OK if the string is a valid amount specification,
+ * GNUNET_SYSERR if it is invalid.
+ */
+int
+TALER_string_to_amount (const char *str, struct TALER_Amount *denom)
+{
+ unsigned int i; // pos in str
+ int n; // number tmp
+ unsigned int c; // currency pos
+ uint32_t b; // base for suffix
+
+ memset (denom, 0, sizeof (struct TALER_Amount));
+
+ i = n = c = 0;
+
+ while (isspace(str[i]))
+ i++;
+
+ if (0 == str[i])
+ {
+ printf("null before currency\n");
+ return GNUNET_SYSERR;
+ }
+
+ while (str[i] != ':')
+ {
+ if (0 == str[i])
+ {
+ printf("null before colon");
+ return GNUNET_SYSERR;
+ }
+ if (c > 3)
+ {
+ printf("currency too long\n");
+ return GNUNET_SYSERR;
+ }
+ denom->currency[c] = str[i];
+ c++;
+ i++;
+ }
+
+ // skip colon
+ i++;
+
+ if (0 == str[i])
+ {
+ printf("null before value\n");
+ return GNUNET_SYSERR;
+ }
+
+ while (str[i] != '.')
+ {
+ if (0 == str[i])
+ {
+ return GNUNET_OK;
+ }
+ n = str[i] - '0';
+ if (n < 0 || n > 9)
+ {
+ printf("invalid character '%c' before comma at %u\n", (char) n, i);
+ return GNUNET_SYSERR;
+ }
+ denom->value = (denom->value * 10) + n;
+ i++;
+ }
+
+ // skip the dot
+ i++;
+
+ if (0 == str[i])
+ {
+ printf("null after dot");
+ return GNUNET_SYSERR;
+ }
+
+ b = 100000;
+
+ while (0 != str[i])
+ {
+ n = str[i] - '0';
+ if (b == 0 || n < 0 || n > 9)
+ {
+ printf("error after comma");
+ return GNUNET_SYSERR;
+ }
+ denom->fraction += n * b;
+ b /= 10;
+ i++;
+ }
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * FIXME
+ */
+struct TALER_AmountNBO
+TALER_amount_hton (struct TALER_Amount d)
+{
+ struct TALER_AmountNBO dn;
+ dn.value = htonl (d.value);
+ dn.fraction = htonl (d.fraction);
+ memcpy (dn.currency, d.currency, TALER_CURRENCY_LEN);
+
+ return dn;
+}
+
+
+/**
+ * FIXME
+ */
+struct TALER_Amount
+TALER_amount_ntoh (struct TALER_AmountNBO dn)
+{
+ struct TALER_Amount d;
+ d.value = ntohl (dn.value);
+ d.fraction = ntohl (dn.fraction);
+ memcpy (d.currency, dn.currency, sizeof(dn.currency));
+
+ return d;
+}
+
+
+/**
+ * Compare the value/fraction of two amounts. Does not compare the currency,
+ * i.e. comparing amounts with the same value and fraction but different
+ * currency would return 0.
+ *
+ * @param a1 first amount
+ * @param a2 second amount
+ * @return result of the comparison
+ */
+int
+TALER_amount_cmp (struct TALER_Amount a1, struct TALER_Amount a2)
+{
+ a1 = TALER_amount_normalize (a1);
+ a2 = TALER_amount_normalize (a2);
+ if (a1.value == a2.value)
+ {
+ if (a1.fraction < a2.fraction)
+ return -1;
+ if (a1.fraction > a2.fraction)
+ return 1;
+ return 0;
+ }
+ if (a1.value < a2.value)
+ return -1;
+ return 1;
+}
+
+
+/**
+ * Perform saturating subtraction of amounts.
+ *
+ * @param a1 amount to subtract from
+ * @param a2 amount to subtract
+ * @return (a1-a2) or 0 if a2>=a1
+ */
+struct TALER_Amount
+TALER_amount_subtract (struct TALER_Amount a1, struct TALER_Amount a2)
+{
+ a1 = TALER_amount_normalize (a1);
+ a2 = TALER_amount_normalize (a2);
+
+ if (a1.value < a2.value)
+ {
+ a1.value = 0;
+ a1.fraction = 0;
+ return a1;
+ }
+
+ if (a1.fraction < a2.fraction)
+ {
+ if (0 == a1.value)
+ {
+ a1.fraction = 0;
+ return a1;
+ }
+ a1.fraction += AMOUNT_FRAC_BASE;
+ a1.value -= 1;
+ }
+
+ a1.fraction -= a2.fraction;
+ a1.value -= a2.value;
+
+ return a1;
+}
+
+
+/**
+ * Perform saturating addition of amounts.
+ *
+ * @param a1 first amount to add
+ * @param a2 second amount to add
+ * @return sum of a1 and a2
+ */
+struct TALER_Amount
+TALER_amount_add (struct TALER_Amount a1, struct TALER_Amount a2)
+{
+ a1 = TALER_amount_normalize (a1);
+ a2 = TALER_amount_normalize (a2);
+
+ a1.value += a2.value;
+ a1.fraction += a2.fraction;
+
+ if (0 == a1.currency[0])
+ {
+ memcpy (a2.currency, a1.currency, TALER_CURRENCY_LEN);
+ }
+
+ if (0 == a2.currency[0])
+ {
+ memcpy (a1.currency, a2.currency, TALER_CURRENCY_LEN);
+ }
+
+ if (0 != a1.currency[0] && 0 != memcmp (a1.currency, a2.currency, TALER_CURRENCY_LEN))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "adding mismatching currencies\n");
+ }
+
+ if (a1.value < a2.value)
+ {
+ a1.value = UINT32_MAX;
+ a2.value = UINT32_MAX;
+ return a1;
+ }
+
+ return TALER_amount_normalize (a1);
+}
+
+
+/**
+ * Normalize the given amount.
+ *
+ * @param amout amount to normalize
+ * @return normalized amount
+ */
+struct TALER_Amount
+TALER_amount_normalize (struct TALER_Amount amount)
+{
+ while (amount.value != UINT32_MAX && amount.fraction >= AMOUNT_FRAC_BASE)
+ {
+ amount.fraction -= AMOUNT_FRAC_BASE;
+ amount.value += 1;
+ }
+ return amount;
+}
+
+
+/**
+ * Convert amount to string.
+ *
+ * @param amount amount to convert to string
+ * @return freshly allocated string representation
+ */
+char *
+TALER_amount_to_string (struct TALER_Amount amount)
+{
+ char tail[AMOUNT_FRAC_LEN + 1] = { 0 };
+ char curr[TALER_CURRENCY_LEN + 1] = { 0 };
+ char *result = NULL;
+ int len;
+
+ memcpy (curr, amount.currency, TALER_CURRENCY_LEN);
+
+ amount = TALER_amount_normalize (amount);
+ if (0 != amount.fraction)
+ {
+ unsigned int i;
+ uint32_t n = amount.fraction;
+ for (i = 0; (i < AMOUNT_FRAC_LEN) && (n != 0); i++)
+ {
+ tail[i] = '0' + (n / (AMOUNT_FRAC_BASE / 10));
+ n = (n * 10) % (AMOUNT_FRAC_BASE);
+ }
+ tail[i] = 0;
+ len = GNUNET_asprintf (&result, "%s:%lu.%s", curr, (unsigned long) amount.value, tail);
+ }
+ else
+ {
+ len = GNUNET_asprintf (&result, "%s:%lu", curr, (unsigned long) amount.value);
+ }
+ GNUNET_assert (len > 0);
+ return result;
+}
+
+
+
+/**
+ * Return the base32crockford encoding of the given buffer.
+ *
+ * The returned string will be freshly allocated, and must be free'd
+ * with GNUNET_free.
+ *
+ * @param buffer with data
+ * @param size size of the buffer
+ * @return freshly allocated, null-terminated string
+ */
+char *
+TALER_data_to_string_alloc (const void *buf, size_t size)
+{
+ char *str_buf;
+ size_t len = size * 8;
+ char *end;
+
+ if (len % 5 > 0)
+ len += 5 - len % 5;
+ len /= 5;
+ str_buf = GNUNET_malloc (len + 1);
+ end = GNUNET_STRINGS_data_to_string (buf, size, str_buf, len);
+ if (NULL == end)
+ {
+ GNUNET_free (str_buf);
+ return NULL;
+ }
+ *end = '\0';
+ return str_buf;
+}
+
+
+/**
+ * Get encoded binary data from a configuration.
+ *
+ * @return GNUNET_OK on success
+ * GNUNET_NO is the value does not exist
+ * GNUNET_SYSERR on encoding error
+ */
+int
+TALER_configuration_get_data (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section, const char *option,
+ void *buf, size_t buf_size)
+{
+ char *enc;
+ int res;
+ size_t data_size;
+ if (GNUNET_OK != (res = GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &enc)))
+ return res;
+ data_size = (strlen (enc) * 5) / 8;
+ if (data_size != buf_size)
+ {
+ GNUNET_free (enc);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, strlen (enc),
+ buf, buf_size))
+ {
+ GNUNET_free (enc);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+static void
+derive_refresh_key (const struct GNUNET_HashCode *secret,
+ struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
+ struct GNUNET_CRYPTO_SymmetricSessionKey *skey)
+{
+ static const char ctx_key[] = "taler-key-skey";
+ static const char ctx_iv[] = "taler-key-iv";
+
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey),
+ ctx_key, strlen (ctx_key),
+ secret, sizeof (struct GNUNET_HashCode),
+ NULL, 0));
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CRYPTO_kdf (iv, sizeof (struct GNUNET_CRYPTO_SymmetricInitializationVector),
+ ctx_iv, strlen (ctx_iv),
+ secret, sizeof (struct GNUNET_HashCode),
+ NULL, 0));
+}
+
+
+int
+TALER_refresh_decrypt (const void *input, size_t input_size, const struct GNUNET_HashCode *secret, void *result)
+{
+ struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+ struct GNUNET_CRYPTO_SymmetricSessionKey skey;
+
+ derive_refresh_key (secret, &iv, &skey);
+
+ return GNUNET_CRYPTO_symmetric_decrypt (input, input_size, &skey, &iv, result);
+}
+
+
+int
+TALER_refresh_encrypt (const void *input, size_t input_size, const struct GNUNET_HashCode *secret, void *result)
+{
+ struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+ struct GNUNET_CRYPTO_SymmetricSessionKey skey;
+
+ derive_refresh_key (secret, &iv, &skey);
+
+ return GNUNET_CRYPTO_symmetric_encrypt (input, input_size, &skey, &iv, result);
+}
+
+
+void
+TALER_hash_context_start (struct TALER_HashContext *hc)
+{
+ GNUNET_assert (0 == gcry_md_open (&hc->hd, GCRY_MD_SHA512, 0));
+}
+
+
+void
+TALER_hash_context_read (struct TALER_HashContext *hc, void *buf, size_t size)
+{
+ gcry_md_write (hc->hd, buf, size);
+}
+
+
+void
+TALER_hash_context_finish (struct TALER_HashContext *hc,
+ struct GNUNET_HashCode *r_hash)
+{
+ void *res = gcry_md_read (hc->hd, 0);
+ GNUNET_assert (NULL != res);
+ if (NULL != r_hash)
+ memcpy (r_hash, res, sizeof (struct GNUNET_HashCode));
+ gcry_md_close (hc->hd);
+}
+
+
+/* end of util.c */