Simplify extension verification

- simplify signature verification for extensions
- remove per-extension signatures from DB schema
- adjust prepared statements accordingly
- adjust DB event handler for extensions
This commit is contained in:
Özgür Kesim 2022-01-21 00:42:25 +01:00
parent ccc4034084
commit ba3be90ed4
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
10 changed files with 147 additions and 177 deletions

View File

@ -796,7 +796,7 @@ static enum GNUNET_GenericReturnValue
postgres_gc (void *cls) postgres_gc (void *cls)
{ {
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;
struct GNUNET_TIME_Absolute now; struct GNUNET_TIME_Absolute now = {0};
struct GNUNET_PQ_QueryParam params_time[] = { struct GNUNET_PQ_QueryParam params_time[] = {
GNUNET_PQ_query_param_absolute_time (&now), GNUNET_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end

View File

@ -1680,9 +1680,76 @@ upload_extensions (const char *exchange_url,
size_t idx, size_t idx,
const json_t *value) const json_t *value)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "uploading extensions %s\n", json_t *extensions;
json_dumps (value, JSON_INDENT (2))); 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_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_log (GNUNET_ERROR_TYPE_ERROR,
"invalid signature for extensions\n");
global_ret = EXIT_FAILURE;
test_shutdown ();
return;
}
}
/* 3. Upload */
/* TODO */ /* TODO */
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"\e[33m*TODO*\e[0m\nuploading extensions %s\n",
json_dumps (value, JSON_INDENT (2)));
} }
@ -3537,43 +3604,36 @@ static void
do_extensions_sign (char *const *args) do_extensions_sign (char *const *args)
{ {
json_t *obj = json_object (); json_t *obj = json_object ();
json_t *exts = json_object (); json_t *extensions = json_object ();
json_t *sigs = json_object (); struct TALER_ExtensionConfigHash h_config;
struct TALER_MasterSignatureP sig;
GNUNET_CONFIGURATION_iterate_sections (kcfg, GNUNET_CONFIGURATION_iterate_sections (kcfg,
&collect_extensions, &collect_extensions,
exts); extensions);
json_object_set (obj, "extensions", exts);
/* sign the extensions */ // TODO: check size of extensions?
if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
{ {
const char *name; GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
json_t *config; "error while hashing config for extensions\n");
return;
json_object_foreach (exts, name, config){
struct TALER_ExtensionConfigHash h_config;
struct TALER_MasterSignatureP sig;
if (GNUNET_OK != TALER_extension_config_hash (config, &h_config))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"error while hashing config for extension %s\n",
name);
return;
}
TALER_exchange_offline_extension_config_hash_sign (h_config, &master_priv,
&sig);
json_object_update (sigs,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto (
name,
&sig)));
}
} }
json_object_set (obj, "extensions_sigs", sigs); 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); output_operation (OP_EXTENSIONS, obj);
} }

View File

@ -171,7 +171,6 @@ extension_update_event_cb (void *cls,
// Get the config from the database as string // Get the config from the database as string
{ {
char *config_str; char *config_str;
struct TALER_MasterSignatureP config_sig; // ignored
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
struct TALER_Extension *extension; struct TALER_Extension *extension;
json_error_t err; json_error_t err;
@ -183,8 +182,7 @@ extension_update_event_cb (void *cls,
qs = TEH_plugin->get_extension_config (TEH_plugin->cls, qs = TEH_plugin->get_extension_config (TEH_plugin->cls,
extension->name, extension->name,
&config_str, &config_str);
&config_sig);
if (qs < 0) if (qs < 0)
{ {

View File

@ -49,41 +49,8 @@ struct SetExtensionsContext
{ {
uint32_t num_extensions; uint32_t num_extensions;
struct Extension *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 * Function implementing database transaction to set the configuration of
* extensions. It runs the transaction logic. * extensions. It runs the transaction logic.
@ -111,7 +78,6 @@ set_extensions (void *cls,
for (uint32_t i = 0; i<sec->num_extensions; i++) for (uint32_t i = 0; i<sec->num_extensions; i++)
{ {
struct Extension *ext = &sec->extensions[i]; struct Extension *ext = &sec->extensions[i];
struct TALER_MasterSignatureP *sig = &sec->extensions_sigs[i];
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
char *config; char *config;
@ -138,8 +104,7 @@ set_extensions (void *cls,
qs = TEH_plugin->set_extension_config ( qs = TEH_plugin->set_extension_config (
TEH_plugin->cls, TEH_plugin->cls,
TEH_extensions[ext->type]->name, TEH_extensions[ext->type]->name,
config, config);
sig);
if (qs < 0) if (qs < 0)
{ {
@ -178,12 +143,12 @@ TEH_handler_management_post_extensions (
{ {
MHD_RESULT ret; MHD_RESULT ret;
json_t *extensions; json_t *extensions;
json_t *extensions_sigs; struct TALER_MasterSignatureP sig = {0};
struct GNUNET_JSON_Specification top_spec[] = { struct GNUNET_JSON_Specification top_spec[] = {
GNUNET_JSON_spec_json ("extensions", GNUNET_JSON_spec_json ("extensions",
&extensions), &extensions),
GNUNET_JSON_spec_json ("extensions_sigs", GNUNET_JSON_spec_fixed_auto ("extensions_sig",
&extensions_sigs), &sig),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
struct SetExtensionsContext sec = {0}; struct SetExtensionsContext sec = {0};
@ -201,38 +166,51 @@ TEH_handler_management_post_extensions (
return MHD_YES; /* failure */ return MHD_YES; /* failure */
} }
/* Ensure we have two objects of the same size */ /* Ensure we have an object */
if (! (json_is_object (extensions) && if (! json_is_object (extensions))
json_is_object (extensions_sigs)) )
{ {
GNUNET_break_op (0);
GNUNET_JSON_parse_free (top_spec); GNUNET_JSON_parse_free (top_spec);
return TALER_MHD_reply_with_error ( return TALER_MHD_reply_with_error (
connection, connection,
MHD_HTTP_BAD_REQUEST, MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED, TALER_EC_GENERIC_PARAMETER_MALFORMED,
"objects expected for extensions and extensions_sigs"); "invalid object");
} }
sec.num_extensions = json_object_size (extensions); /* Verify the signature */
if (json_object_size (extensions_sigs) != sec.num_extensions)
{ {
GNUNET_break_op (0); struct TALER_ExtensionConfigHash h_config;
GNUNET_JSON_parse_free (top_spec); if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
return TALER_MHD_reply_with_error ( {
connection, GNUNET_JSON_parse_free (top_spec);
MHD_HTTP_BAD_REQUEST, return TALER_MHD_reply_with_error (
TALER_EC_GENERIC_PARAMETER_MALFORMED, connection,
"objects extensions and extensions_sigs are not of the same size"); 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, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received /management/extensions\n"); "Received /management/extensions\n");
sec.num_extensions = json_object_size (extensions);
sec.extensions = GNUNET_new_array (sec.num_extensions, sec.extensions = GNUNET_new_array (sec.num_extensions,
struct Extension); struct Extension);
sec.extensions_sigs = GNUNET_new_array (sec.num_extensions,
struct TALER_MasterSignatureP);
/* Now parse individual extensions and signatures from those objects. */ /* Now parse individual extensions and signatures from those objects. */
{ {
@ -261,45 +239,7 @@ TEH_handler_management_post_extensions (
sec.extensions[idx].config = config; sec.extensions[idx].config = config;
sec.extensions[idx].type = extension->type; sec.extensions[idx].type = extension->type;
/* 2. Extract the corresponding signature */ /* 2. Make sure the config is sound */
{
struct GNUNET_JSON_Specification sig_spec[] = {
GNUNET_JSON_spec_fixed_auto (name,
&sec.extensions_sigs[idx]),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK != TALER_MHD_parse_json_data (connection,
extensions_sigs,
sig_spec))
{
GNUNET_JSON_parse_free (sig_spec);
ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"extension signature missing");
goto CLEANUP;
}
GNUNET_JSON_parse_free (sig_spec);
}
/* 3. Verify the signature of the config */
if (GNUNET_OK != config_verify (
sec.extensions[idx].config,
&TEH_master_public_key,
&sec.extensions_sigs[idx]))
{
ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"invalid signature for extension");
goto CLEANUP;
}
/* 4. Make sure the config is sound */
if (GNUNET_OK != extension->test_config (sec.extensions[idx].config)) if (GNUNET_OK != extension->test_config (sec.extensions[idx].config))
{ {
ret = TALER_MHD_reply_with_error ( ret = TALER_MHD_reply_with_error (
@ -356,7 +296,6 @@ CLEANUP:
} }
} }
GNUNET_free (sec.extensions); GNUNET_free (sec.extensions);
GNUNET_free (sec.extensions_sigs);
GNUNET_JSON_parse_free (top_spec); GNUNET_JSON_parse_free (top_spec);
return ret; return ret;
} }

View File

@ -298,7 +298,6 @@ CREATE TABLE IF NOT EXISTS extensions
(extension_id BIGSERIAL UNIQUE (extension_id BIGSERIAL UNIQUE
,name VARCHAR NOT NULL UNIQUE ,name VARCHAR NOT NULL UNIQUE
,config BYTEA NOT NULL ,config BYTEA NOT NULL
,config_sig BYTEA NOT NULL
); );
COMMENT ON TABLE extensions COMMENT ON TABLE extensions
IS 'Configurations of the activated extensions'; IS 'Configurations of the activated extensions';
@ -306,8 +305,6 @@ COMMENT ON COLUMN extensions.name
IS 'Name of the extension'; IS 'Name of the extension';
COMMENT ON COLUMN extensions.config COMMENT ON COLUMN extensions.config
IS 'Configuration of the extension as JSON-blob'; 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';
CREATE TABLE IF NOT EXISTS known_coins CREATE TABLE IF NOT EXISTS known_coins

View File

@ -2745,16 +2745,15 @@ prepare_statements (struct PostgresClosure *pg)
/* Used in #postgres_set_extension_config */ /* Used in #postgres_set_extension_config */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"set_extension_config", "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) " "ON CONFLICT (name) "
"DO UPDATE SET (config, config_sig) = ($2, $3)", "DO UPDATE SET config=$2",
3), 2),
/* Used in #postgres_get_extension_config */ /* Used in #postgres_get_extension_config */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"get_extension_config", "get_extension_config",
"SELECT " "SELECT "
" config, " " config "
" config_sig "
"FROM extensions" "FROM extensions"
" WHERE name=$1;", " WHERE name=$1;",
1), 1),
@ -11413,20 +11412,17 @@ postgres_delete_shard_locks (void *cls)
* @param cls the @e cls of this struct with the plugin-specific state * @param cls the @e cls of this struct with the plugin-specific state
* @param extension_name the name of the extension * @param extension_name the name of the extension
* @param config JSON object of the configuration as string * @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 * @return transaction status code
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
postgres_set_extension_config (void *cls, postgres_set_extension_config (void *cls,
const char *extension_name, const char *extension_name,
const char *config, const char *config)
const struct TALER_MasterSignatureP *config_sig)
{ {
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (extension_name), GNUNET_PQ_query_param_string (extension_name),
GNUNET_PQ_query_param_string (config), GNUNET_PQ_query_param_string (config),
GNUNET_PQ_query_param_auto_from_type (config_sig),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
@ -11448,8 +11444,7 @@ postgres_set_extension_config (void *cls,
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
postgres_get_extension_config (void *cls, postgres_get_extension_config (void *cls,
const char *extension_name, const char *extension_name,
char **config, char **config)
struct TALER_MasterSignatureP *config_sig)
{ {
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
@ -11458,7 +11453,6 @@ postgres_get_extension_config (void *cls,
}; };
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_string ("config", config), GNUNET_PQ_result_spec_string ("config", config),
GNUNET_PQ_result_spec_auto_from_type ("config_sig", config_sig),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };

View File

@ -115,51 +115,36 @@ static enum GNUNET_GenericReturnValue
test_extension_config (void) test_extension_config (void)
{ {
char *config; char *config;
struct TALER_MasterSignatureP sig;
memset (&sig, 0x42, sizeof(sig));
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->get_extension_config (plugin->cls, plugin->get_extension_config (plugin->cls,
"fnord", "fnord",
&config, &config));
&sig));
sig.eddsa_signature.r[23] = 0x23;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->set_extension_config (plugin->cls, plugin->set_extension_config (plugin->cls,
"fnord", "fnord",
"bar", "bar"));
&sig));
memset (&sig, 0, sizeof(sig));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_extension_config (plugin->cls, plugin->get_extension_config (plugin->cls,
"fnord", "fnord",
&config, &config));
&sig));
FAILIF (0 != strcmp ("bar", config)); FAILIF (0 != strcmp ("bar", config));
FAILIF (0x23 != sig.eddsa_signature.r[23]);
/* let's do this again! */ /* let's do this again! */
memset (&sig, 0x42, sizeof(sig));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->set_extension_config (plugin->cls, plugin->set_extension_config (plugin->cls,
"fnord", "fnord",
"buzz", "buzz"));
&sig));
memset (&sig, 0, sizeof(sig));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_extension_config (plugin->cls, plugin->get_extension_config (plugin->cls,
"fnord", "fnord",
&config, &config));
&sig));
FAILIF (0 != strcmp ("buzz", config)); FAILIF (0 != strcmp ("buzz", config));
FAILIF (0x42 != sig.eddsa_signature.r[23]);
return GNUNET_OK; return GNUNET_OK;
drop: drop:

View File

@ -2536,7 +2536,7 @@ TALER_merchant_wire_signature_make (
*/ */
void void
TALER_exchange_offline_extension_config_hash_sign ( 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, const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig); struct TALER_MasterSignatureP *master_sig);
@ -2552,7 +2552,7 @@ TALER_exchange_offline_extension_config_hash_sign (
*/ */
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_exchange_offline_extension_config_hash_verify ( 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_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig const struct TALER_MasterSignatureP *master_sig
); );

View File

@ -4027,14 +4027,12 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state * @param cls the @e cls of this struct with the plugin-specific state
* @param extension_name the name of the extension * @param extension_name the name of the extension
* @param config JSON object of the configuration as string * @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 * @return transaction status code
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
(*set_extension_config)(void *cls, (*set_extension_config)(void *cls,
const char *extension_name, const char *extension_name,
const char *config, const char *config);
const struct TALER_MasterSignatureP *config_sig);
/** /**
* Function called to retrieve the configuration of an extension * Function called to retrieve the configuration of an extension
@ -4049,8 +4047,7 @@ struct TALER_EXCHANGEDB_Plugin
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
(*get_extension_config)(void *cls, (*get_extension_config)(void *cls,
const char *extension_name, const char *extension_name,
char **config, char **config);
struct TALER_MasterSignatureP *config_sig);
}; };

View File

@ -492,14 +492,14 @@ TALER_exchange_offline_wire_fee_verify (
void void
TALER_exchange_offline_extension_config_hash_sign ( 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, const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig) struct TALER_MasterSignatureP *master_sig)
{ {
struct TALER_MasterExtensionConfigurationPS ec = { struct TALER_MasterExtensionConfigurationPS ec = {
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION), .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
.purpose.size = htonl (sizeof(ec)), .purpose.size = htonl (sizeof(ec)),
.h_config = h_config .h_config = *h_config
}; };
GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv, GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
&ec, &ec,
@ -509,7 +509,7 @@ TALER_exchange_offline_extension_config_hash_sign (
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_exchange_offline_extension_config_hash_verify ( 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_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig const struct TALER_MasterSignatureP *master_sig
) )
@ -517,7 +517,7 @@ TALER_exchange_offline_extension_config_hash_verify (
struct TALER_MasterExtensionConfigurationPS ec = { struct TALER_MasterExtensionConfigurationPS ec = {
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION), .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
.purpose.size = htonl (sizeof(ec)), .purpose.size = htonl (sizeof(ec)),
.h_config = h_config .h_config = *h_config
}; };
return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_EXTENSION, return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_EXTENSION,