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

@ -1680,9 +1680,76 @@ upload_extensions (const char *exchange_url,
size_t idx,
const json_t *value)
{
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "uploading extensions %s\n",
json_dumps (value, JSON_INDENT (2)));
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_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 */
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)
{
json_t *obj = json_object ();
json_t *exts = json_object ();
json_t *sigs = json_object ();
json_t *extensions = json_object ();
struct TALER_ExtensionConfigHash h_config;
struct TALER_MasterSignatureP sig;
GNUNET_CONFIGURATION_iterate_sections (kcfg,
&collect_extensions,
exts);
json_object_set (obj, "extensions", exts);
extensions);
/* sign the extensions */
// TODO: check size of extensions?
if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
{
const char *name;
json_t *config;
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)));
}
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"error while hashing config for extensions\n");
return;
}
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);
}

View File

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

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,7 +78,6 @@ 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;
@ -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)
{
@ -178,12 +143,12 @@ TEH_handler_management_post_extensions (
{
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 ()
};
struct SetExtensionsContext sec = {0};
@ -201,38 +166,51 @@ TEH_handler_management_post_extensions (
return MHD_YES; /* failure */
}
/* Ensure we have two objects of the same size */
if (! (json_is_object (extensions) &&
json_is_object (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,
"objects expected for extensions and extensions_sigs");
"invalid object");
}
sec.num_extensions = json_object_size (extensions);
if (json_object_size (extensions_sigs) != 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,
"objects 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 objects. */
{
@ -261,45 +239,7 @@ TEH_handler_management_post_extensions (
sec.extensions[idx].config = config;
sec.extensions[idx].type = extension->type;
/* 2. Extract the corresponding signature */
{
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 */
/* 2. Make sure the config is sound */
if (GNUNET_OK != extension->test_config (sec.extensions[idx].config))
{
ret = TALER_MHD_reply_with_error (
@ -356,7 +296,6 @@ CLEANUP:
}
}
GNUNET_free (sec.extensions);
GNUNET_free (sec.extensions_sigs);
GNUNET_JSON_parse_free (top_spec);
return ret;
}

View File

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

View File

@ -2745,16 +2745,15 @@ 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, "
" config_sig "
" config "
"FROM extensions"
" WHERE name=$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 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 params[] = {
GNUNET_PQ_query_param_string (extension_name),
GNUNET_PQ_query_param_string (config),
GNUNET_PQ_query_param_auto_from_type (config_sig),
GNUNET_PQ_query_param_end
};
@ -11448,8 +11444,7 @@ postgres_set_extension_config (void *cls,
enum GNUNET_DB_QueryStatus
postgres_get_extension_config (void *cls,
const char *extension_name,
char **config,
struct TALER_MasterSignatureP *config_sig)
char **config)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@ -11458,7 +11453,6 @@ postgres_get_extension_config (void *cls,
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_string ("config", config),
GNUNET_PQ_result_spec_auto_from_type ("config_sig", config_sig),
GNUNET_PQ_result_spec_end
};

View File

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

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

@ -4027,14 +4027,12 @@ 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
* @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
@ -4049,8 +4047,7 @@ struct TALER_EXCHANGEDB_Plugin
enum GNUNET_DB_QueryStatus
(*get_extension_config)(void *cls,
const char *extension_name,
char **config,
struct TALER_MasterSignatureP *config_sig);
char **config);
};

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,