[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
This commit is contained in:
Özgür Kesim 2022-01-11 15:24:43 +01:00
parent 0b6ebc6160
commit 0b56de6c99
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
17 changed files with 842 additions and 370 deletions

View File

@ -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

View File

@ -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 ();
@ -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);
}

View File

@ -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)
/**

View File

@ -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,

View File

@ -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 ()
};
json_t *config;
int idx = 0;
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_object_foreach (extensions, name, config){
/* 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;
/* 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;
}
/* We have a validly signed JSON object for the extension. Increment its
* refcount.
*/
json_incref (sec.extensions[idx].config);
idx++;
/* 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;
} /* json_object_foreach */
}
}
/* 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);
} /* 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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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 ==

View File

@ -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
);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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))
{

View File

@ -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 !=

View File

@ -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

View File

@ -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;
mask->mask |= 1 << val;
prev = val;
val = 0;
continue;
}
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;
}
if ('0'>c || '9'<c)
return GNUNET_SYSERR;
/* Set the corresponding bit in the mask */
mask->mask |= 1 << val;
val = 10 * val + c - '0';
if (NULL == pos)
{
/* We reached the end. Mark zeroth age-group and exit. */
mask->mask |= 1;
ret = TALER_Extension_OK;
break;
}
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;
}

View File

@ -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,