diff options
| author | Özgür Kesim <oec-taler@kesim.org> | 2022-01-11 15:24:43 +0100 | 
|---|---|---|
| committer | Özgür Kesim <oec-taler@kesim.org> | 2022-01-21 15:41:02 +0100 | 
| commit | 0b56de6c994d3e525aa2d0195ff4607db3f14715 (patch) | |
| tree | 9f34c40155dd5538841497c9f6a151deb2305a8d /src | |
| parent | 0b6ebc6160f1fd1f6db7c433f0912b5d2845a59c (diff) | |
[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
Diffstat (limited to 'src')
| -rw-r--r-- | src/auditordb/plugin_auditordb_postgres.c | 2 | ||||
| -rw-r--r-- | src/exchange-tools/taler-exchange-offline.c | 539 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd.h | 4 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_extensions.c | 10 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_management_extensions.c | 229 | ||||
| -rw-r--r-- | src/exchangedb/exchange-0001.sql | 7 | ||||
| -rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 41 | ||||
| -rw-r--r-- | src/exchangedb/test_exchangedb.c | 108 | ||||
| -rw-r--r-- | src/include/taler_crypto_lib.h | 4 | ||||
| -rw-r--r-- | src/include/taler_exchange_service.h | 15 | ||||
| -rw-r--r-- | src/include/taler_exchangedb_plugin.h | 9 | ||||
| -rw-r--r-- | src/include/taler_extensions.h | 19 | ||||
| -rw-r--r-- | src/lib/exchange_api_handle.c | 2 | ||||
| -rw-r--r-- | src/lib/exchange_api_management_post_extensions.c | 74 | ||||
| -rw-r--r-- | src/testing/test_exchange_api.conf | 66 | ||||
| -rw-r--r-- | src/util/extension_age_restriction.c | 75 | ||||
| -rw-r--r-- | 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 7931900a..e0355d93 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 9255737a..8db1fc9f 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 4f04029e..39666379 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 8723bebc..1a2c4552 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 96b855c3..8476e669 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 51fd26ec..a8e79335 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 4b009607..918fc38c 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 6724e7b4..cca7c3f4 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 f1fa0285..6a805b64 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 7fb5b4ec..5bc87cf4 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 5eb168e1..cd68e1ed 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 243811eb..31e5c673 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 ac0e0584..aea09a81 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 862ff711..c0ab143f 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 a1b74365..48d5c200 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 b29a8ca8..0b04c7d7 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 1240a8bc..ab298834 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, | 
