[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:
parent
0b6ebc6160
commit
0b56de6c99
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 ==
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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 !=
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user