From 0b56de6c994d3e525aa2d0195ff4607db3f14715 Mon Sep 17 00:00:00 2001 From: Özgür Kesim Date: Tue, 11 Jan 2022 15:24:43 +0100 Subject: [age restriction] progress 12/n - taler-offline-tool now handles extensions - command "extensions" added with subcommands "show" and "sign" - parses extensions from taler config - shows and signs of extensions and their configurations - creates signed set of configurations for upload - added test for retrieval of extension config - simplified signature verification for extensions - remove per-extension signatures, also from DB schema - adjust prepared statements accordingly - adjust DB event handler for extensions - allow NULL for config for extension in DB schema - handler for /management/extensions adjusted to new datastructures - changed test for TALER_denom_blind/TALER_denom_sign_blinded with and without TALER_AgeHash - minor updates and various fixes --- src/auditordb/plugin_auditordb_postgres.c | 2 +- src/exchange-tools/taler-exchange-offline.c | 539 +++++++++++++++++++-- src/exchange/taler-exchange-httpd.h | 4 +- src/exchange/taler-exchange-httpd_extensions.c | 10 +- .../taler-exchange-httpd_management_extensions.c | 229 +++------ src/exchangedb/exchange-0001.sql | 7 +- src/exchangedb/plugin_exchangedb_postgres.c | 41 +- src/exchangedb/test_exchangedb.c | 108 ++++- src/include/taler_crypto_lib.h | 4 +- src/include/taler_exchange_service.h | 15 +- src/include/taler_exchangedb_plugin.h | 9 +- src/include/taler_extensions.h | 19 +- src/lib/exchange_api_handle.c | 2 +- src/lib/exchange_api_management_post_extensions.c | 74 +-- src/testing/test_exchange_api.conf | 66 +++ src/util/extension_age_restriction.c | 75 ++- src/util/offline_signatures.c | 8 +- 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 -*/ + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ /** * @file taler-exchange-offline.c * @brief Support for operations involving the exchange's offline master key. @@ -20,8 +20,10 @@ */ #include #include +#include #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 (); @@ -1661,6 +1719,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. * @@ -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; inum_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; itype; + 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 @@ -108,6 +108,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. * @@ -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 - -*/ + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + + */ /** * @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; inum_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; inum_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'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, -- cgit v1.2.3