summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/auditordb/plugin_auditordb_postgres.c2
-rw-r--r--src/exchange-tools/taler-exchange-offline.c539
-rw-r--r--src/exchange/taler-exchange-httpd.h4
-rw-r--r--src/exchange/taler-exchange-httpd_extensions.c10
-rw-r--r--src/exchange/taler-exchange-httpd_management_extensions.c229
-rw-r--r--src/exchangedb/exchange-0001.sql7
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c41
-rw-r--r--src/exchangedb/test_exchangedb.c108
-rw-r--r--src/include/taler_crypto_lib.h4
-rw-r--r--src/include/taler_exchange_service.h15
-rw-r--r--src/include/taler_exchangedb_plugin.h9
-rw-r--r--src/include/taler_extensions.h19
-rw-r--r--src/lib/exchange_api_handle.c2
-rw-r--r--src/lib/exchange_api_management_post_extensions.c74
-rw-r--r--src/testing/test_exchange_api.conf66
-rw-r--r--src/util/extension_age_restriction.c75
-rw-r--r--src/util/offline_signatures.c8
17 files changed, 842 insertions, 370 deletions
diff --git a/src/auditordb/plugin_auditordb_postgres.c b/src/auditordb/plugin_auditordb_postgres.c
index 7931900a5..e0355d93b 100644
--- a/src/auditordb/plugin_auditordb_postgres.c
+++ b/src/auditordb/plugin_auditordb_postgres.c
@@ -796,7 +796,7 @@ static enum GNUNET_GenericReturnValue
postgres_gc (void *cls)
{
struct PostgresClosure *pg = cls;
- struct GNUNET_TIME_Absolute now;
+ struct GNUNET_TIME_Absolute now = {0};
struct GNUNET_PQ_QueryParam params_time[] = {
GNUNET_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_end
diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c
index 9255737a9..8db1fc9fa 100644
--- a/src/exchange-tools/taler-exchange-offline.c
+++ b/src/exchange-tools/taler-exchange-offline.c
@@ -1,18 +1,18 @@
/*
- This file is part of TALER
- Copyright (C) 2020, 2021 Taler Systems SA
+ This file is part of TALER
+ Copyright (C) 2020, 2021 Taler Systems SA
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
+ 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.
+ 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, see <http://www.gnu.org/licenses/>
-*/
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
/**
* @file taler-exchange-offline.c
* @brief Support for operations involving the exchange's offline master key.
@@ -20,8 +20,10 @@
*/
#include <platform.h>
#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_util_lib.h>
#include "taler_json_lib.h"
#include "taler_exchange_service.h"
+#include "taler_extensions.h"
/**
* Name of the input for the 'sign' and 'show' operation.
@@ -93,6 +95,11 @@
*/
#define OP_SETUP "exchange-setup-0"
+/**
+ * sign the enabled and configured extensions.
+ */
+#define OP_EXTENSIONS "exchange-extensions-0"
+
/**
* Our private key, initialized in #load_offline_key().
@@ -392,6 +399,32 @@ struct UploadKeysRequest
size_t idx;
};
+/**
+ * Ongoing /management/extensions request.
+ */
+struct UploadExtensionsRequest
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct UploadExtensionsRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct UploadExtensionsRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementPostExtensionsHandle *h;
+
+ /**
+ * Operation index.
+ */
+ size_t idx;
+};
+
/**
* Next work item to perform.
@@ -483,6 +516,15 @@ static struct UploadKeysRequest *ukr_head;
*/
static struct UploadKeysRequest *ukr_tail;
+/**
+ * Active extensions upload requests.
+ */
+static struct UploadExtensionsRequest *uer_head;
+
+/**
+ * Active extensions upload requests.
+ */
+static struct UploadExtensionsRequest *uer_tail;
/**
* Shutdown task. Invoked when the application is being terminated.
@@ -615,6 +657,21 @@ do_shutdown (void *cls)
GNUNET_free (ukr);
}
}
+ {
+ struct UploadExtensionsRequest *uer;
+
+ while (NULL != (uer = uer_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete extensions signature upload #%u\n",
+ (unsigned int) uer->idx);
+ TALER_EXCHANGE_post_management_extensions_cancel (uer->h);
+ GNUNET_CONTAINER_DLL_remove (uer_head,
+ uer_tail,
+ uer);
+ GNUNET_free (uer);
+ }
+ }
if (NULL != out)
{
json_dumpf (out,
@@ -667,6 +724,7 @@ test_shutdown (void)
(NULL == wdr_head) &&
(NULL == wfr_head) &&
(NULL == ukr_head) &&
+ (NULL == uer_head) &&
(NULL == mgkh) &&
(NULL == nxt) )
GNUNET_SCHEDULER_shutdown ();
@@ -1662,6 +1720,136 @@ upload_keys (const char *exchange_url,
/**
+ * Function called with information about the post upload extensions operation result.
+ *
+ * @param cls closure with a `struct UploadExtensionsRequest`
+ * @param hr HTTP response data
+ */
+static void
+extensions_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_HttpResponse *hr)
+{
+ struct UploadExtensionsRequest *uer = cls;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) uer->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (uer_head,
+ uer_tail,
+ uer);
+ GNUNET_free (uer);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload extension configuration
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for POSTing configurations of extensions
+ */
+static void
+upload_extensions (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ json_t *extensions;
+ struct TALER_MasterSignatureP sig;
+ const char *err_name;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_json ("extensions",
+ &extensions),
+ GNUNET_JSON_spec_fixed_auto ("extensions_sig",
+ &sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ /* 1. Parse the signed extensions */
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to set extensions: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return;
+ }
+
+ /* 2. Verify the signature */
+ {
+ struct TALER_ExtensionConfigHash h_config;
+
+ if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
+ {
+ GNUNET_JSON_parse_free (spec);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "couldn't hash extensions\n");
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return;
+ }
+
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+
+ if (GNUNET_OK != TALER_exchange_offline_extension_config_hash_verify (
+ &h_config,
+ &master_pub,
+ &sig))
+ {
+ GNUNET_JSON_parse_free (spec);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "invalid signature for extensions\n");
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return;
+ }
+ }
+
+ /* 3. Upload the extensions */
+ {
+ struct TALER_EXCHANGE_ManagementPostExtensionsData ped = {
+ .extensions = extensions,
+ .extensions_sig = sig,
+ };
+ struct UploadExtensionsRequest *uer = GNUNET_new (struct
+ UploadExtensionsRequest);
+ uer->idx = idx;
+ uer->h = TALER_EXCHANGE_management_post_extensions (
+ ctx,
+ exchange_url,
+ &ped,
+ &extensions_cb,
+ uer);
+ GNUNET_CONTAINER_DLL_insert (uer_head,
+ uer_tail,
+ uer);
+ }
+ GNUNET_JSON_parse_free (spec);
+}
+
+
+/**
* Perform uploads based on the JSON in #out.
*
* @param exchange_url base URL of the exchange to use
@@ -1702,6 +1890,10 @@ trigger_upload (const char *exchange_url)
.key = OP_UPLOAD_SIGS,
.cb = &upload_keys
},
+ {
+ .key = OP_EXTENSIONS,
+ .cb = &upload_extensions
+ },
/* array termination */
{
.key = NULL
@@ -3314,6 +3506,297 @@ do_setup (char *const *args)
}
+/**
+ * struct extension carries the information about an extension together with
+ * callbacks to parse the configuration and marshal it as JSON
+ */
+struct extension
+{
+ char *name;
+ bool enabled;
+ bool critical;
+ char *version;
+ void *config;
+
+ enum GNUNET_GenericReturnValue (*parse_config)(struct extension *this,
+ const char *section);
+ json_t *(*config_json)(const struct extension *this);
+};
+
+#define EXT_PREFIX "exchange-extension-"
+
+#define DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21"
+
+static enum GNUNET_GenericReturnValue
+age_restriction_parse_config (struct extension *this, const char *section)
+{
+ char *age_groups = NULL;
+ struct TALER_AgeMask mask = {0};
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg, section, "ENABLED");
+
+ this->enabled = (GNUNET_YES == ret);
+
+ if (! this->enabled)
+ return GNUNET_OK;
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg,
+ section,
+ "AGE_GROUPS",
+ &age_groups))
+ age_groups = DEFAULT_AGE_GROUPS;
+
+ if (GNUNET_OK != TALER_parse_age_group_string (age_groups, &mask))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "AGE_GROUPS");
+ test_shutdown ();
+ global_ret = EXIT_NOTCONFIGURED;
+ return GNUNET_SYSERR;
+ }
+
+ /* Don't look here. We just store the mask in/as the pointer .*/
+ this->config = (void *) (size_t) mask.mask;
+ return GNUNET_OK;
+}
+
+
+static json_t *
+age_restriction_json (const struct extension *this)
+{
+ struct TALER_AgeMask mask;
+ json_t *conf;
+
+ if (! this->enabled)
+ return NULL;
+
+ /* Don't look here. We just restore the mask from/as the pointer .*/
+ mask.mask = (uint32_t) (size_t) this->config;
+
+ conf = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string (
+ "age_groups",
+ TALER_age_mask_to_string (&mask)));
+
+ return GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_bool ("critical",
+ this->critical),
+ GNUNET_JSON_pack_string ("version",
+ this->version),
+ GNUNET_JSON_pack_object_steal ("config", conf));
+}
+
+
+static struct extension extensions[] = {
+ {
+ .name = "age_restriction",
+ .version = "1",
+ .config = 0,
+ .parse_config = &age_restriction_parse_config,
+ .config_json = &age_restriction_json,
+ },
+ /* TODO: add p2p here */
+ {0},
+};
+
+
+static const struct extension*
+get_extension (const char *extension)
+{
+ for (const struct extension *known = extensions;
+ NULL != known->name;
+ known++)
+ {
+ if (0 == strncasecmp (extension,
+ known->name,
+ strlen (known->name)))
+ return known;
+ }
+ return NULL;
+}
+
+
+static void
+collect_extensions (void *cls, const char *section)
+{
+ json_t *obj = (json_t *) cls;
+ const char *name;
+ const struct extension *extension;
+
+ if (0 != global_ret)
+ return;
+
+ if (0 != strncasecmp (section,
+ EXT_PREFIX,
+ sizeof(EXT_PREFIX) - 1))
+ {
+ return;
+ }
+
+ name = section + sizeof(EXT_PREFIX) - 1;
+
+ if (NULL == (extension = get_extension (name)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unsupported extension `%s` (section [%s]).\n", name,
+ section);
+ test_shutdown ();
+ global_ret = EXIT_NOTCONFIGURED;
+ return;
+ }
+
+ if (GNUNET_OK != extension->parse_config ((struct extension *) extension,
+ section))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Couldn't parse configuration for extension `%s` (section [%s]).\n",
+ name,
+ section);
+ test_shutdown ();
+ global_ret = EXIT_NOTCONFIGURED;
+ return;
+ }
+
+ json_object_set (obj, name, extension->config_json (extension));
+}
+
+
+/*
+ * Print the current extensions as configured
+ */
+static void
+do_extensions_show (char *const *args)
+{
+
+ json_t *obj = json_object ();
+ json_t *exts = json_object ();
+
+ GNUNET_CONFIGURATION_iterate_sections (kcfg,
+ &collect_extensions,
+ exts);
+ json_object_set (obj, "extensions", exts);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "%s\n",
+ json_dumps (obj, JSON_INDENT (2)));
+
+ json_decref (obj);
+}
+
+
+/*
+ * Sign the configurations of the enabled extensions
+ */
+static void
+do_extensions_sign (char *const *args)
+{
+ json_t *obj = json_object ();
+ json_t *extensions = json_object ();
+ struct TALER_ExtensionConfigHash h_config;
+ struct TALER_MasterSignatureP sig;
+
+ GNUNET_CONFIGURATION_iterate_sections (kcfg,
+ &collect_extensions,
+ extensions);
+
+ // TODO: check size of extensions?
+ if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "error while hashing config for extensions\n");
+ return;
+ }
+
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+
+
+ TALER_exchange_offline_extension_config_hash_sign (&h_config,
+ &master_priv,
+ &sig);
+ json_object_set (obj, "extensions", extensions);
+ json_object_update (obj,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto (
+ "extensions_sig",
+ &sig)));
+
+ output_operation (OP_EXTENSIONS, obj);
+}
+
+
+static void
+cmd_handler (char *const *args, const struct SubCommand *cmds)
+{
+ nxt = NULL;
+ for (unsigned int i = 0; NULL != cmds[i].name; i++)
+ {
+ if (0 == strcasecmp (cmds[i].name,
+ args[0]))
+ {
+ cmds[i].cb (&args[1]);
+ return;
+ }
+ }
+
+ if (0 != strcasecmp ("help",
+ args[0]))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Unexpected command `%s'\n",
+ args[0]);
+ global_ret = EXIT_INVALIDARGUMENT;
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Supported subcommands:\n");
+ for (unsigned int i = 0; NULL != cmds[i].name; i++)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "- %s: %s\n",
+ cmds[i].name,
+ cmds[i].help);
+ }
+}
+
+
+static void
+do_work_extensions (char *const *args)
+{
+ struct SubCommand cmds[] = {
+ {
+ .name = "show",
+ .help =
+ "show the extensions in the Taler-config and their configured parameters",
+ .cb = &do_extensions_show
+ },
+ {
+ .name = "sign",
+ .help =
+ "sign the configuration of the extensions and publish it with the exchange",
+ .cb = &do_extensions_sign
+ },
+ {
+ .name = NULL,
+ }
+ };
+
+ if (NULL == args[0])
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must provide a subcommand: `show` or `sign`.\n");
+ test_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+
+ cmd_handler (args, cmds);
+ next (args + 1);
+}
+
+
static void
work (void *cls)
{
@@ -3390,6 +3873,11 @@ work (void *cls)
"upload operation result to exchange (to be performed online!)",
.cb = &do_upload
},
+ {
+ .name = "extensions",
+ .help = "subcommands for extension handling",
+ .cb = &do_work_extensions
+ },
/* list terminator */
{
.name = NULL,
@@ -3397,34 +3885,7 @@ work (void *cls)
};
(void) cls;
- nxt = NULL;
- for (unsigned int i = 0; NULL != cmds[i].name; i++)
- {
- if (0 == strcasecmp (cmds[i].name,
- args[0]))
- {
- cmds[i].cb (&args[1]);
- return;
- }
- }
-
- if (0 != strcasecmp ("help",
- args[0]))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
- "Unexpected command `%s'\n",
- args[0]);
- global_ret = EXIT_INVALIDARGUMENT;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
- "Supported subcommands:\n");
- for (unsigned int i = 0; NULL != cmds[i].name; i++)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
- "- %s: %s\n",
- cmds[i].name,
- cmds[i].help);
- }
+ cmd_handler (args, cmds);
}
diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h
index 4f04029e6..39666379e 100644
--- a/src/exchange/taler-exchange-httpd.h
+++ b/src/exchange/taler-exchange-httpd.h
@@ -206,7 +206,9 @@ extern struct GNUNET_CURL_Context *TEH_curl_ctx;
*/
extern struct TALER_Extension **TEH_extensions;
-#define TEH_extension_enabled(ext) (0 <= ext && TALER_Extension_Max > ext && \
+/* TODO: this will not work anymore, once we have plugable extensions */
+#define TEH_extension_enabled(ext) (0 <= ext && TALER_Extension_MaxPredefined > \
+ ext && \
NULL != TEH_extensions[ext]->config)
/**
diff --git a/src/exchange/taler-exchange-httpd_extensions.c b/src/exchange/taler-exchange-httpd_extensions.c
index 8723bebc8..1a2c4552d 100644
--- a/src/exchange/taler-exchange-httpd_extensions.c
+++ b/src/exchange/taler-exchange-httpd_extensions.c
@@ -116,11 +116,12 @@ static struct TALER_Extension **
get_known_extensions ()
{
- struct TALER_Extension **list = GNUNET_new_array (TALER_Extension_Max + 1,
- struct TALER_Extension *);
+ struct TALER_Extension **list = GNUNET_new_array (
+ TALER_Extension_MaxPredefined + 1,
+ struct TALER_Extension *);
list[TALER_Extension_AgeRestriction] = &extension_age_restriction;
list[TALER_Extension_Peer2Peer] = &extension_peer2peer;
- list[TALER_Extension_Max] = NULL;
+ list[TALER_Extension_MaxPredefined] = NULL;
return list;
}
@@ -160,7 +161,8 @@ extension_update_event_cb (void *cls,
}
type = *(enum TALER_Extension_Type *) extra;
- if (type <0 || type >= TALER_Extension_Max)
+ /* TODO: This check will not work once we have plugable extensions */
+ if (type <0 || type >= TALER_Extension_MaxPredefined)
{
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c
index 96b855c3c..8476e669d 100644
--- a/src/exchange/taler-exchange-httpd_management_extensions.c
+++ b/src/exchange/taler-exchange-httpd_management_extensions.c
@@ -49,41 +49,8 @@ struct SetExtensionsContext
{
uint32_t num_extensions;
struct Extension *extensions;
- struct TALER_MasterSignatureP *extensions_sigs;
};
-
-/**
- * @brief verifies the signature a configuration with the offline master key.
- *
- * @param config configuration of an extension given as JSON object
- * @param master_priv offline master public key of the exchange
- * @param[out] master_sig signature
- * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
- */
-static enum GNUNET_GenericReturnValue
-config_verify (
- const json_t *config,
- const struct TALER_MasterPublicKeyP *master_pub,
- const struct TALER_MasterSignatureP *master_sig
- )
-{
- enum GNUNET_GenericReturnValue ret;
- struct TALER_ExtensionConfigHash h_config;
-
- ret = TALER_extension_config_hash (config, &h_config);
- if (GNUNET_OK != ret)
- {
- GNUNET_break (0);
- return ret;
- }
-
- return TALER_exchange_offline_extension_config_hash_verify (h_config,
- master_pub,
- master_sig);
-}
-
-
/**
* Function implementing database transaction to set the configuration of
* extensions. It runs the transaction logic.
@@ -111,14 +78,13 @@ set_extensions (void *cls,
for (uint32_t i = 0; i<sec->num_extensions; i++)
{
struct Extension *ext = &sec->extensions[i];
- struct TALER_MasterSignatureP *sig = &sec->extensions_sigs[i];
enum GNUNET_DB_QueryStatus qs;
char *config;
/* Sanity check.
- * TODO: replace with general API to retrieve the extension-handler
+ * TODO: This will not work anymore, once we have plugable extensions
*/
- if (0 > ext->type || TALER_Extension_Max <= ext->type)
+ if (0 > ext->type || TALER_Extension_MaxPredefined <= ext->type)
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
@@ -138,8 +104,7 @@ set_extensions (void *cls,
qs = TEH_plugin->set_extension_config (
TEH_plugin->cls,
TEH_extensions[ext->type]->name,
- config,
- sig);
+ config);
if (qs < 0)
{
@@ -176,19 +141,19 @@ TEH_handler_management_post_extensions (
struct MHD_Connection *connection,
const json_t *root)
{
- struct SetExtensionsContext sec = {0};
+ MHD_RESULT ret;
json_t *extensions;
- json_t *extensions_sigs;
+ struct TALER_MasterSignatureP sig = {0};
struct GNUNET_JSON_Specification top_spec[] = {
GNUNET_JSON_spec_json ("extensions",
&extensions),
- GNUNET_JSON_spec_json ("extensions_sigs",
- &extensions_sigs),
+ GNUNET_JSON_spec_fixed_auto ("extensions_sig",
+ &sig),
GNUNET_JSON_spec_end ()
};
- MHD_RESULT ret;
+ struct SetExtensionsContext sec = {0};
- // Parse the top level json structure
+ /* Parse the top level json structure */
{
enum GNUNET_GenericReturnValue res;
@@ -201,153 +166,106 @@ TEH_handler_management_post_extensions (
return MHD_YES; /* failure */
}
- // Ensure we have two arrays of the same size
- if (! (json_is_array (extensions) &&
- json_is_array (extensions_sigs)) )
+ /* Ensure we have an object */
+ if (! json_is_object (extensions))
{
- GNUNET_break_op (0);
GNUNET_JSON_parse_free (top_spec);
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "array expected for extensions and extensions_sigs");
+ "invalid object");
}
- sec.num_extensions = json_array_size (extensions_sigs);
- if (json_array_size (extensions) != sec.num_extensions)
+ /* Verify the signature */
{
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (top_spec);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "arrays extensions and extensions_sigs are not of the same size");
+ struct TALER_ExtensionConfigHash h_config;
+ if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
+ {
+ GNUNET_JSON_parse_free (top_spec);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "invalid object, non-hashable");
+ }
+
+ if (GNUNET_OK != TALER_exchange_offline_extension_config_hash_verify (
+ &h_config,
+ &TEH_master_public_key,
+ &sig))
+ {
+ GNUNET_JSON_parse_free (top_spec);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "invalid signuture");
+ }
}
+
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received /management/extensions\n");
+ sec.num_extensions = json_object_size (extensions);
sec.extensions = GNUNET_new_array (sec.num_extensions,
struct Extension);
- sec.extensions_sigs = GNUNET_new_array (sec.num_extensions,
- struct TALER_MasterSignatureP);
- // Now parse individual extensions and signatures from those arrays.
- for (unsigned int i = 0; i<sec.num_extensions; i++)
+ /* Now parse individual extensions and signatures from those objects. */
{
- // 1. parse the extension out of the json
- enum GNUNET_GenericReturnValue res;
const struct TALER_Extension *extension;
const char *name;
- struct GNUNET_JSON_Specification ext_spec[] = {
- GNUNET_JSON_spec_string ("extension",
- &name),
- GNUNET_JSON_spec_json ("config",
- &sec.extensions[i].config),
- GNUNET_JSON_spec_end ()
- };
-
- res = TALER_MHD_parse_json_array (connection,
- extensions,
- ext_spec,
- i,
- -1);
- if (GNUNET_SYSERR == res)
- {
- ret = MHD_NO; /* hard failure */
- goto CLEANUP;
- }
- if (GNUNET_NO == res)
- {
- ret = MHD_YES;
- goto CLEANUP;
- }
+ json_t *config;
+ int idx = 0;
- /* 2. Make sure name refers to a supported extension */
- if (GNUNET_OK != TALER_extension_get_by_name (name,
- (const struct
- TALER_Extension **)
- TEH_extensions,
- &extension))
- {
- ret = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "invalid extension type");
- goto CLEANUP;
- }
-
- sec.extensions[i].type = extension->type;
+ json_object_foreach (extensions, name, config){
- /* 3. Extract the signature out of the json array */
- {
- enum GNUNET_GenericReturnValue res;
- struct GNUNET_JSON_Specification sig_spec[] = {
- GNUNET_JSON_spec_fixed_auto (NULL,
- &sec.extensions_sigs[i]),
- GNUNET_JSON_spec_end ()
- };
-
- res = TALER_MHD_parse_json_array (connection,
- extensions_sigs,
- sig_spec,
- i,
- -1);
- if (GNUNET_SYSERR == res)
+ /* 1. Make sure name refers to a supported extension */
+ if (GNUNET_OK != TALER_extension_get_by_name (name,
+ (const struct
+ TALER_Extension **)
+ TEH_extensions,
+ &extension))
{
- ret = MHD_NO; /* hard failure */
+ ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "invalid extension type");
goto CLEANUP;
}
- if (GNUNET_NO == res)
+
+ sec.extensions[idx].config = config;
+ sec.extensions[idx].type = extension->type;
+
+ /* 2. Make sure the config is sound */
+ if (GNUNET_OK != extension->test_config (sec.extensions[idx].config))
{
- ret = MHD_YES;
+ ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "invalid configuration for extension");
goto CLEANUP;
- }
- }
-
- /* 4. Verify the signature of the config */
- if (GNUNET_OK != config_verify (
- sec.extensions[i].config,
- &TEH_master_public_key,
- &sec.extensions_sigs[i]))
- {
- ret = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "invalid signature for extension");
- goto CLEANUP;
- }
- /* 5. Make sure the config is sound */
- if (GNUNET_OK != extension->test_config (sec.extensions[i].config))
- {
- GNUNET_JSON_parse_free (ext_spec);
- ret = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "invalid configuration for extension");
- goto CLEANUP;
+ }
- }
+ /* We have a validly signed JSON object for the extension. Increment its
+ * refcount.
+ */
+ json_incref (sec.extensions[idx].config);
+ idx++;
- /* We have a validly signed JSON object for the extension.
- * Increment its refcount and free the parser for the extension.
- */
- json_incref (sec.extensions[i].config);
- GNUNET_JSON_parse_free (ext_spec);
+ } /* json_object_foreach */
+ }
- } /* for-loop */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received %u extensions\n",
sec.num_extensions);
- // now run the transaction to persist the configurations
+ /* now run the transaction to persist the configurations */
{
enum GNUNET_GenericReturnValue res;
@@ -378,7 +296,6 @@ CLEANUP:
}
}
GNUNET_free (sec.extensions);
- GNUNET_free (sec.extensions_sigs);
GNUNET_JSON_parse_free (top_spec);
return ret;
}
diff --git a/src/exchangedb/exchange-0001.sql b/src/exchangedb/exchange-0001.sql
index 51fd26eca..a8e79335b 100644
--- a/src/exchangedb/exchange-0001.sql
+++ b/src/exchangedb/exchange-0001.sql
@@ -297,17 +297,14 @@ COMMENT ON TABLE signkey_revocations
CREATE TABLE IF NOT EXISTS extensions
(extension_id BIGSERIAL UNIQUE
,name VARCHAR NOT NULL UNIQUE
- ,config BYTEA NOT NULL
- ,config_sig BYTEA NOT NULL
+ ,config BYTEA
);
COMMENT ON TABLE extensions
IS 'Configurations of the activated extensions';
COMMENT ON COLUMN extensions.name
IS 'Name of the extension';
COMMENT ON COLUMN extensions.config
- IS 'Configuration of the extension as JSON-blob';
-COMMENT ON COLUMN extensions.config
- IS 'Signature of the configuration of an extension, signed with the master key of the exchange';
+ IS 'Configuration of the extension as JSON-blob, maybe NULL';
CREATE TABLE IF NOT EXISTS known_coins
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 4b0096078..918fc38ca 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -2745,15 +2745,17 @@ prepare_statements (struct PostgresClosure *pg)
/* Used in #postgres_set_extension_config */
GNUNET_PQ_make_prepare (
"set_extension_config",
- "INSERT INTO extensions (name, config, config_sig) VALUES ($1, $2, $3) "
+ "INSERT INTO extensions (name, config) VALUES ($1, $2) "
"ON CONFLICT (name) "
- "DO UPDATE SET (config, config_sig) = ($2, $3)",
- 3),
+ "DO UPDATE SET config=$2",
+ 2),
/* Used in #postgres_get_extension_config */
GNUNET_PQ_make_prepare (
"get_extension_config",
- "SELECT (config) FROM extensions"
- " WHERE name=$1;",
+ "SELECT "
+ " config "
+ "FROM extensions"
+ " WHERE name=$1;",
1),
GNUNET_PQ_PREPARED_STATEMENT_END
};
@@ -11410,20 +11412,20 @@ postgres_delete_shard_locks (void *cls)
* @param cls the @e cls of this struct with the plugin-specific state
* @param extension_name the name of the extension
* @param config JSON object of the configuration as string
- * @param config_sig signature of the configuration by the offline master key
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
postgres_set_extension_config (void *cls,
const char *extension_name,
- const char *config,
- const struct TALER_MasterSignatureP *config_sig)
+ const char *config)
{
struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam pcfg = (NULL == config || 0 == *config) ?
+ GNUNET_PQ_query_param_null () :
+ GNUNET_PQ_query_param_string (config);
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (extension_name),
- GNUNET_PQ_query_param_string (config),
- GNUNET_PQ_query_param_auto_from_type (config_sig),
+ pcfg,
GNUNET_PQ_query_param_end
};
@@ -11452,15 +11454,24 @@ postgres_get_extension_config (void *cls,
GNUNET_PQ_query_param_string (extension_name),
GNUNET_PQ_query_param_end
};
+ bool is_null;
struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_string ("config", config),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("config", config),
+ &is_null),
GNUNET_PQ_result_spec_end
};
+ enum GNUNET_DB_QueryStatus qs;
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "get_extension_config",
- params,
- rs);
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "get_extension_config",
+ params,
+ rs);
+ if (is_null)
+ {
+ *config = NULL;
+ }
+ return qs;
}
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index 6724e7b42..cca7c3f47 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -109,6 +109,63 @@ mark_prepare_cb (void *cls,
/**
+ * Simple check that config retrieval and setting for extensions work
+ */
+static enum GNUNET_GenericReturnValue
+test_extension_config (void)
+{
+ char *config;
+
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+ plugin->get_extension_config (plugin->cls,
+ "fnord",
+ &config));
+
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->set_extension_config (plugin->cls,
+ "fnord",
+ "bar"));
+
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->get_extension_config (plugin->cls,
+ "fnord",
+ &config));
+
+ FAILIF (0 != strcmp ("bar", config));
+
+ /* let's do this again! */
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->set_extension_config (plugin->cls,
+ "fnord",
+ "buzz"));
+
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->get_extension_config (plugin->cls,
+ "fnord",
+ &config));
+
+ FAILIF (0 != strcmp ("buzz", config));
+
+ /* let's do this again, with NULL */
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->set_extension_config (plugin->cls,
+ "fnord",
+ NULL));
+
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->get_extension_config (plugin->cls,
+ "fnord",
+ &config));
+
+ FAILIF (NULL != config);
+
+ return GNUNET_OK;
+drop:
+ return GNUNET_SYSERR;
+}
+
+
+/**
* Test API relating to persisting the wire plugins preparation data.
*
* @return #GNUNET_OK on success
@@ -1334,6 +1391,10 @@ run (void *cls)
0,
&recoup_cb,
NULL));
+ /* simple extension check */
+ FAILIF (GNUNET_OK !=
+ test_extension_config ());
+
RND_BLK (&reserve_pub);
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1.000010",
@@ -1406,27 +1467,36 @@ run (void *cls)
{
struct TALER_PlanchetDetail pd;
struct TALER_CoinSpendPublicKeyP coin_pub;
+ struct TALER_AgeHash age_hash;
+ struct TALER_AgeHash *p_ah[2] = {NULL, &age_hash};
- RND_BLK (&coin_pub);
- TALER_blinding_secret_create (&bks);
- GNUNET_assert (GNUNET_OK ==
- TALER_denom_blind (&dkp->pub,
- &bks,
- NULL, /* FIXME-Oec */
- &coin_pub,
- &c_hash,
- &pd.coin_ev,
- &pd.coin_ev_size));
- TALER_coin_ev_hash (pd.coin_ev,
- pd.coin_ev_size,
- &cbc.h_coin_envelope);
- GNUNET_assert (GNUNET_OK ==
- TALER_denom_sign_blinded (&cbc.sig,
- &dkp->priv,
- pd.coin_ev,
- pd.coin_ev_size));
- GNUNET_free (pd.coin_ev);
+ /* Call TALER_denom_blind()/TALER_denom_sign_blinded() twice, once without
+ * age_hash, once with age_hash */
+ RND_BLK (&age_hash);
+ for (size_t i = 0; i < sizeof(p_ah) / sizeof(p_ah[0]); i++)
+ {
+ RND_BLK (&coin_pub);
+ TALER_blinding_secret_create (&bks);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_blind (&dkp->pub,
+ &bks,
+ p_ah[i],
+ &coin_pub,
+ &c_hash,
+ &pd.coin_ev,
+ &pd.coin_ev_size));
+ TALER_coin_ev_hash (pd.coin_ev,
+ pd.coin_ev_size,
+ &cbc.h_coin_envelope);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_sign_blinded (&cbc.sig,
+ &dkp->priv,
+ pd.coin_ev,
+ pd.coin_ev_size));
+ GNUNET_free (pd.coin_ev);
+ }
}
+
cbc.reserve_pub = reserve_pub;
cbc.amount_with_fee = value;
GNUNET_assert (GNUNET_OK ==
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index f1fa0285d..6a805b645 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -2536,7 +2536,7 @@ TALER_merchant_wire_signature_make (
*/
void
TALER_exchange_offline_extension_config_hash_sign (
- const struct TALER_ExtensionConfigHash h_config,
+ const struct TALER_ExtensionConfigHash *h_config,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig);
@@ -2552,7 +2552,7 @@ TALER_exchange_offline_extension_config_hash_sign (
*/
enum GNUNET_GenericReturnValue
TALER_exchange_offline_extension_config_hash_verify (
- const struct TALER_ExtensionConfigHash h_config,
+ const struct TALER_ExtensionConfigHash *h_config,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig
);
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index 7fb5b4ec0..5bc87cf47 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -2682,12 +2682,14 @@ TALER_EXCHANGE_post_management_keys_cancel (
/**
* Information needed for a POST /management/extensions operation.
+ *
+ * It represents the interface ExchangeKeysResponse as defined in
+ * https://docs.taler.net/design-documents/006-extensions.html#exchange
*/
struct TALER_EXCHANGE_ManagementPostExtensionsData
{
- struct TALER_Extension *extensions;
- struct TALER_MasterSignatureP *extensions_sigs;
- uint32_t num_extensions;
+ json_t *extensions;
+ struct TALER_MasterSignatureP extensions_sig;
};
/**
@@ -2708,11 +2710,12 @@ struct TALER_EXCHANGE_ManagementPostExtensionsHandle;
/**
- * FIXME-oec: Provide correct explanation of this function.
+ * Uploads the configurations of enabled extensions to the exchange, signed
+ * with the master key.
*
* @param ctx the context
* @param url HTTP base URL for the exchange
- * @param pkd signature data to POST
+ * @param ped signature data to POST
* @param cb function to call with the exchange's result
* @param cb_cls closure for @a cb
* @return the request handle; NULL upon error
@@ -2721,7 +2724,7 @@ struct TALER_EXCHANGE_ManagementPostExtensionsHandle *
TALER_EXCHANGE_management_post_extensions (
struct GNUNET_CURL_Context *ctx,
const char *url,
- const struct TALER_EXCHANGE_ManagementPostExtensionsData *pkd,
+ struct TALER_EXCHANGE_ManagementPostExtensionsData *ped,
TALER_EXCHANGE_ManagementPostExtensionsCallback cb,
void *cb_cls);
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 5eb168e14..cd68e1edb 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -4026,15 +4026,13 @@ struct TALER_EXCHANGEDB_Plugin
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param extension_name the name of the extension
- * @param config JSON object of the configuration as string
- * @param config_sig signature of the configuration by the offline master key
+ * @param config JSON object of the configuration as string, maybe NULL (== disabled extension)
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
(*set_extension_config)(void *cls,
const char *extension_name,
- const char *config,
- const struct TALER_MasterSignatureP *config_sig);
+ const char *config);
/**
* Function called to retrieve the configuration of an extension
@@ -4042,8 +4040,7 @@ struct TALER_EXCHANGEDB_Plugin
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param extension_name the name of the extension
- * @param[out] config JSON object of the configuration as string
- * @param[out] config_sig signature of the configuration by the master key
+ * @param[out] config JSON object of the configuration as string, maybe NULL (== disabled extension)
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h
index 243811eb5..31e5c6738 100644
--- a/src/include/taler_extensions.h
+++ b/src/include/taler_extensions.h
@@ -28,29 +28,22 @@
#define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-"
-enum TALER_Extension_ReturnValue
-{
- TALER_Extension_OK = 0,
- TALER_Extension_ERROR_PARSING = 1,
- TALER_Extension_ERROR_INVALID = 2,
- TALER_Extension_ERROR_SYS = 3
-};
-
enum TALER_Extension_Type
{
TALER_Extension_AgeRestriction = 0,
TALER_Extension_Peer2Peer = 1,
- TALER_Extension_Max = 2 // Must be last
+ TALER_Extension_MaxPredefined = 2 // Must be last
};
/*
- * TODO oec: documentation
+ * Represents the implementation of an extension.
*/
struct TALER_Extension
{
enum TALER_Extension_Type type;
char *name;
bool critical;
+ bool enabled;
void *config;
enum GNUNET_GenericReturnValue (*test_config)(const json_t *config);
@@ -68,7 +61,7 @@ struct TALER_Extension
* Finds and returns a supported extension by a given name.
*
* @param name name of the extension to lookup
- * @param extensions list of TALER_Extensions as haystack, terminated by an entry of type TALER_Extension_Max
+ * @param extensions list of TALER_Extensions as haystack, terminated by a NULL-entry
* @param[out] ext set to the extension, if found, NULL otherwise
* @return GNUNET_OK if extension was found, GNUNET_NO otherwise
*/
@@ -109,7 +102,7 @@ TALER_extension_get_by_name (const char *name,
* @param[out] mask Mask representation for age restriction.
* @return Error, if age groups were invalid, OK otherwise.
*/
-enum TALER_Extension_ReturnValue
+enum GNUNET_GenericReturnValue
TALER_parse_age_group_string (const char *groups,
struct TALER_AgeMask *mask);
@@ -133,7 +126,7 @@ TALER_age_mask_to_string (const struct TALER_AgeMask *mask);
* @return Error if extension for age restriction was set but age groups were
* invalid, OK otherwise.
*/
-enum TALER_Extension_ReturnValue
+enum GNUNET_GenericReturnValue
TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
struct TALER_AgeMask *mask);
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index ac0e0584f..aea09a81f 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -831,7 +831,7 @@ decode_keys_json (const json_t *resp_obj,
return GNUNET_SYSERR;
}
- if (TALER_Extension_OK !=
+ if (GNUNET_OK !=
TALER_parse_age_group_string (age_groups,
&key_data->age_mask))
{
diff --git a/src/lib/exchange_api_management_post_extensions.c b/src/lib/exchange_api_management_post_extensions.c
index 862ff7117..c0ab143f6 100644
--- a/src/lib/exchange_api_management_post_extensions.c
+++ b/src/lib/exchange_api_management_post_extensions.c
@@ -1,19 +1,19 @@
/*
- This file is part of TALER
- Copyright (C) 2015-2021 Taler Systems SA
+ This file is part of TALER
+ Copyright (C) 2015-2021 Taler Systems SA
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
+ 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.
+ 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, see
- <http://www.gnu.org/licenses/>
-*/
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+ */
/**
* @file lib/exchange_api_management_post_extensions.c
* @brief functions to handle the settings for extensions (p2p and age restriction)
@@ -125,15 +125,13 @@ struct TALER_EXCHANGE_ManagementPostExtensionsHandle *
TALER_EXCHANGE_management_post_extensions (
struct GNUNET_CURL_Context *ctx,
const char *url,
- const struct TALER_EXCHANGE_ManagementPostExtensionsData *pkd,
- TALER_EXCHANGE_ManagementPostKeysCallback cb,
+ struct TALER_EXCHANGE_ManagementPostExtensionsData *ped,
+ TALER_EXCHANGE_ManagementPostExtensionsCallback cb,
void *cb_cls)
{
struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph;
CURL *eh = NULL;
json_t *body = NULL;
- json_t *extensions = NULL;
- json_t *extensions_sigs = NULL;
ph = GNUNET_new (struct TALER_EXCHANGE_ManagementPostExtensionsHandle);
ph->cb = cb;
@@ -149,45 +147,13 @@ TALER_EXCHANGE_management_post_extensions (
GNUNET_free (ph);
return NULL;
}
- extensions = json_array ();
- GNUNET_assert (NULL != extensions);
- for (unsigned int i = 0; i<pkd->num_extensions; i++)
- {
- const json_t *config;
- const struct TALER_Extension *ext = &pkd->extensions[i];
-
- config = ext->config_to_json (ext);
-
- GNUNET_assert (NULL != config);
- GNUNET_assert (0 ==
- json_array_append_new (
- extensions,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("extension",
- &ext->name),
- GNUNET_JSON_pack_data_auto ("config",
- config)
- )));
- }
- extensions_sigs = json_array ();
- GNUNET_assert (NULL != extensions_sigs);
- for (unsigned int i = 0; i<pkd->num_extensions; i++)
- {
- const struct TALER_MasterSignatureP *sks
- = &pkd->extensions_sigs[i];
-
- GNUNET_assert (0 ==
- json_array_append_new (
- extensions_sigs,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("extension_sig",
- &sks->eddsa_signature))));
- }
+
body = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_array_steal ("extensions",
- extensions),
- GNUNET_JSON_pack_array_steal ("extensions_sigs",
- extensions_sigs));
+ GNUNET_JSON_pack_object_steal ("extensions",
+ ped->extensions),
+ GNUNET_JSON_pack_data_auto ("extensions_sigs",
+ &ped->extensions_sig));
+
eh = curl_easy_init ();
GNUNET_assert (NULL != eh);
if (GNUNET_OK !=
diff --git a/src/testing/test_exchange_api.conf b/src/testing/test_exchange_api.conf
index a1b743658..48d5c2004 100644
--- a/src/testing/test_exchange_api.conf
+++ b/src/testing/test_exchange_api.conf
@@ -77,6 +77,12 @@ WIRE_GATEWAY_URL = "http://localhost:9081/2/"
[bank]
HTTP_PORT = 9081
+# Enabled extensions
+[exchange-extension-age_restriction]
+ENABLED = YES
+# default age groups:
+#AGE_GROUPS = "8:10:12:14:16:18:21"
+
# Sections starting with "coin_" specify which denominations
# the exchange should support (and their respective fee structure)
[coin_eur_ct_1]
@@ -133,3 +139,63 @@ fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
rsa_keysize = 1024
+
+[coin_eur_ct_1_age_restricted]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+age_restricted = true
+
+[coin_eur_ct_10_age_restricted]
+value = EUR:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+age_restricted = true
+
+[coin_eur_1_age_restricted]
+value = EUR:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+age_restricted = true
+
+[coin_eur_5_age_restricted]
+value = EUR:5
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+age_restricted = true
+
+[coin_eur_10_age_restricted]
+value = EUR:10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+age_restricted = true
diff --git a/src/util/extension_age_restriction.c b/src/util/extension_age_restriction.c
index b29a8ca88..0b04c7d7b 100644
--- a/src/util/extension_age_restriction.c
+++ b/src/util/extension_age_restriction.c
@@ -30,12 +30,12 @@
* @return Error if extension for age restriction was set, but age groups were
* invalid, OK otherwise.
*/
-enum TALER_Extension_ReturnValue
+enum GNUNET_GenericReturnValue
TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
struct TALER_AgeMask *mask)
{
char *groups;
- enum TALER_Extension_ReturnValue ret = TALER_Extension_ERROR_SYS;
+ enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
if ((GNUNET_YES != GNUNET_CONFIGURATION_have_value (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
@@ -46,7 +46,7 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
{
/* Age restriction is not enabled */
mask->mask = 0;
- return TALER_Extension_OK;
+ return GNUNET_OK;
}
/* Age restriction is enabled, extract age groups */
@@ -56,13 +56,13 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
&groups))
{
/* FIXME: log error? */
- return TALER_Extension_ERROR_SYS;
+ return GNUNET_SYSERR;
}
if (groups == NULL)
{
/* No groups defined in config, return default_age_mask */
mask->mask = TALER_EXTENSION_DEFAULT_AGE_MASK;
- return TALER_Extension_OK;
+ return GNUNET_OK;
}
ret = TALER_parse_age_group_string (groups, mask);
@@ -79,59 +79,46 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
* @param[out] mask Bit representation of the age groups.
* @return Error if string was invalid, OK otherwise.
*/
-enum TALER_Extension_ReturnValue
+enum GNUNET_GenericReturnValue
TALER_parse_age_group_string (const char *groups,
struct TALER_AgeMask *mask)
{
- enum TALER_Extension_ReturnValue ret = TALER_Extension_ERROR_SYS;
- char *pos;
+
+ const char *pos = groups;
unsigned int prev = 0;
- unsigned int val;
- char dummy;
+ unsigned int val = 0;
+ char c;
- while (1)
+ while (*pos)
{
- pos = strchr (groups, ':');
- if (NULL != pos)
+ c = *pos++;
+ if (':' == c)
{
- *pos = 0;
- }
+ if (prev >= val)
+ return GNUNET_SYSERR;
- if (1 != sscanf (groups,
- "%u%c",
- &val,
- &dummy))
- {
- /* Invalid input */
- mask->mask = 0;
- ret = TALER_Extension_ERROR_PARSING;
- break;
- }
- else if ((0 >= val) || (32 <= val) || (prev >= val))
- {
- /* Invalid value */
- mask->mask = 0;
- ret = TALER_Extension_ERROR_INVALID;
- break;
+ mask->mask |= 1 << val;
+ prev = val;
+ val = 0;
+ continue;
}
- /* Set the corresponding bit in the mask */
- mask->mask |= 1 << val;
+ if ('0'>c || '9'<c)
+ return GNUNET_SYSERR;
- if (NULL == pos)
- {
- /* We reached the end. Mark zeroth age-group and exit. */
- mask->mask |= 1;
- ret = TALER_Extension_OK;
- break;
- }
+ val = 10 * val + c - '0';
- prev = val;
- *pos = ':';
- groups = pos + 1;
+ if (0>=val || 32<=val)
+ return GNUNET_SYSERR;
}
- return ret;
+ if (0>val || 32<=val || prev>=val)
+ return GNUNET_SYSERR;
+
+ mask->mask |= (1 << val);
+ mask->mask |= 1; // mark zeroth group, too
+
+ return GNUNET_OK;
}
diff --git a/src/util/offline_signatures.c b/src/util/offline_signatures.c
index 1240a8bc5..ab2988349 100644
--- a/src/util/offline_signatures.c
+++ b/src/util/offline_signatures.c
@@ -492,14 +492,14 @@ TALER_exchange_offline_wire_fee_verify (
void
TALER_exchange_offline_extension_config_hash_sign (
- const struct TALER_ExtensionConfigHash h_config,
+ const struct TALER_ExtensionConfigHash *h_config,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig)
{
struct TALER_MasterExtensionConfigurationPS ec = {
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
.purpose.size = htonl (sizeof(ec)),
- .h_config = h_config
+ .h_config = *h_config
};
GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
&ec,
@@ -509,7 +509,7 @@ TALER_exchange_offline_extension_config_hash_sign (
enum GNUNET_GenericReturnValue
TALER_exchange_offline_extension_config_hash_verify (
- const struct TALER_ExtensionConfigHash h_config,
+ const struct TALER_ExtensionConfigHash *h_config,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig
)
@@ -517,7 +517,7 @@ TALER_exchange_offline_extension_config_hash_verify (
struct TALER_MasterExtensionConfigurationPS ec = {
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
.purpose.size = htonl (sizeof(ec)),
- .h_config = h_config
+ .h_config = *h_config
};
return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_EXTENSION,