Logic for extensions more generic

- signature of the hash of the json will be checked
- config_verify for signature check of any extension
- FP for parsing the config as and converting to JSON
- simplify TEH_handler_management_post_extensions
- cleanups
This commit is contained in:
Özgür Kesim 2021-12-29 16:08:45 +01:00
parent 128d136885
commit 75763f2823
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
12 changed files with 337 additions and 291 deletions

View File

@ -1,18 +1,18 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2021 Taler Systems SA Copyright (C) 2014-2021 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software terms of the GNU Affero General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version. 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 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 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* @file taler-exchange-httpd.c * @file taler-exchange-httpd.c
* @brief Serve the HTTP interface of the exchange * @brief Serve the HTTP interface of the exchange
@ -150,19 +150,33 @@ bool TEH_suicide;
/** /**
* The global manifest with the list supported extensions, sorted by * The global manifest with the list supported extensions, sorted by
* TALER_Extension_Type. * TALER_Extension_Type.
*
* TODO: This needs to become a dynamic list, once we have a model for
* extensions as plugins.
**/ **/
const struct TALER_Extension TEH_extensions[TALER_Extension_Max] = { const struct TALER_Extension TEH_extensions[] = {
[TALER_Extension_AgeRestriction] = {
.type = TALER_Extension_AgeRestriction,
.name = "age_restriction",
.critical = false,
.config = NULL, // disabled per default
/* TODO:
.parse_config = &TALER_Extension_AgeRestriction_parse_config,
.config_to_json = &TALER_Extension_AgeRestriction_config_to_json,
*/
},
[TALER_Extension_Peer2Peer] = { [TALER_Extension_Peer2Peer] = {
.type = TALER_Extension_Peer2Peer, .type = TALER_Extension_Peer2Peer,
.name = "peer2peer", .name = "peer2peer",
.critical = false, .critical = false,
.config = NULL, // disabled per default .config = NULL, // disabled per default
}, },
[TALER_Extension_AgeRestriction] = { /* terminator */
.type = TALER_Extension_AgeRestriction, [TALER_Extension_Max] = {
.name = "age_restriction", .type = TALER_Extension_Max,
.name = NULL,
.critical = false, .critical = false,
.config = NULL, // disabled per default .config = NULL,
}, },
}; };
@ -485,7 +499,7 @@ proceed_with_handler (struct TEH_RequestContext *rc,
if (GNUNET_SYSERR == res) if (GNUNET_SYSERR == res)
{ {
GNUNET_assert (NULL == root); GNUNET_assert (NULL == root);
return MHD_NO; /* bad upload, could not even generate error */ return MHD_NO; /* bad upload, could not even generate error */
} }
if ( (GNUNET_NO == res) || if ( (GNUNET_NO == res) ||
(NULL == root) ) (NULL == root) )
@ -528,8 +542,8 @@ proceed_with_handler (struct TEH_RequestContext *rc,
sizeof (emsg), sizeof (emsg),
"Got %u/%u segments for %s request ('%s')", "Got %u/%u segments for %s request ('%s')",
(NULL == args[i - 1]) (NULL == args[i - 1])
? i - 1 ? i - 1
: i + ((NULL != fin) ? 1 : 0), : i + ((NULL != fin) ? 1 : 0),
rh->nargs, rh->nargs,
rh->url, rh->url,
url); url);
@ -553,7 +567,7 @@ proceed_with_handler (struct TEH_RequestContext *rc,
root, root,
args); args);
else /* We also only have "POST" or "GET" in the API for at this point else /* We also only have "POST" or "GET" in the API for at this point
(OPTIONS/HEAD are taken care of earlier) */ (OPTIONS/HEAD are taken care of earlier) */
ret = rh->handler.get (rc, ret = rh->handler.get (rc,
args); args);
} }
@ -1120,7 +1134,7 @@ handle_mhd_request (void *cls,
if (0 == strcasecmp (method, if (0 == strcasecmp (method,
MHD_HTTP_METHOD_HEAD)) MHD_HTTP_METHOD_HEAD))
method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */ method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
/* parse first part of URL */ /* parse first part of URL */
{ {
@ -1954,8 +1968,8 @@ run (void *cls,
MHD_OPTION_CONNECTION_TIMEOUT, MHD_OPTION_CONNECTION_TIMEOUT,
connection_timeout, connection_timeout,
(0 == allow_address_reuse) (0 == allow_address_reuse)
? MHD_OPTION_END ? MHD_OPTION_END
: MHD_OPTION_LISTENING_ADDRESS_REUSE, : MHD_OPTION_LISTENING_ADDRESS_REUSE,
(unsigned int) allow_address_reuse, (unsigned int) allow_address_reuse,
MHD_OPTION_END); MHD_OPTION_END);
if (NULL == mhd) if (NULL == mhd)

View File

@ -202,9 +202,9 @@ extern volatile bool MHD_terminating;
extern struct GNUNET_CURL_Context *TEH_curl_ctx; extern struct GNUNET_CURL_Context *TEH_curl_ctx;
/** /**
* The manifest of the available extensions * The manifest of the available extensions, NULL terminated
*/ */
extern const struct TALER_Extension TEH_extensions[TALER_Extension_Max]; extern const struct TALER_Extension TEH_extensions[];
/** /**
* @brief Struct describing an URL and the handler for it. * @brief Struct describing an URL and the handler for it.

View File

@ -32,19 +32,14 @@
#include "taler_dbevents.h" #include "taler_dbevents.h"
/**
* Extension carries the necessary data for a particular extension.
*
*/
struct Extension struct Extension
{ {
enum TALER_Extension_Type type; enum TALER_Extension_Type type;
json_t *config_json; json_t *config;
// This union contains the parsed configuration for each extension.
union
{
// configuration for the age restriction
struct TALER_AgeMask mask;
/* TODO oec - add peer2peer config */
};
}; };
/** /**
@ -57,6 +52,38 @@ struct SetExtensionsContext
struct TALER_MasterSignatureP *extensions_sigs; 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.
@ -88,7 +115,7 @@ set_extensions (void *cls,
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
char *config; char *config;
config = json_dumps (ext->config_json, JSON_COMPACT | JSON_SORT_KEYS); config = json_dumps (ext->config, JSON_COMPACT | JSON_SORT_KEYS);
if (NULL == config) if (NULL == config)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -143,7 +170,7 @@ TEH_handler_management_post_extensions (
struct SetExtensionsContext sec = {0}; struct SetExtensionsContext sec = {0};
json_t *extensions; json_t *extensions;
json_t *extensions_sigs; json_t *extensions_sigs;
struct GNUNET_JSON_Specification 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_json ("extensions_sigs",
@ -157,7 +184,7 @@ TEH_handler_management_post_extensions (
res = TALER_MHD_parse_json_data (connection, res = TALER_MHD_parse_json_data (connection,
root, root,
spec); top_spec);
if (GNUNET_SYSERR == res) if (GNUNET_SYSERR == res)
return MHD_NO; /* hard failure */ return MHD_NO; /* hard failure */
if (GNUNET_NO == res) if (GNUNET_NO == res)
@ -168,7 +195,7 @@ TEH_handler_management_post_extensions (
json_is_array (extensions_sigs)) ) json_is_array (extensions_sigs)) )
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
GNUNET_JSON_parse_free (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,
@ -180,7 +207,7 @@ TEH_handler_management_post_extensions (
if (json_array_size (extensions) != sec.num_extensions) if (json_array_size (extensions) != sec.num_extensions)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
GNUNET_JSON_parse_free (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,
@ -198,105 +225,53 @@ TEH_handler_management_post_extensions (
for (unsigned int i = 0; i<sec.num_extensions; i++) for (unsigned int i = 0; i<sec.num_extensions; i++)
{ {
// 1. parse the extension // 1. parse the extension out of the json
enum GNUNET_GenericReturnValue res;
const struct TALER_Extension *extension;
const char *name;
struct GNUNET_JSON_Specification ext_spec[] = {
GNUNET_JSON_spec_string ("extension",
&name),
GNUNET_JSON_spec_json ("config",
&sec.extensions[i].config),
GNUNET_JSON_spec_end ()
};
res = TALER_MHD_parse_json_array (connection,
extensions,
ext_spec,
i,
-1);
if (GNUNET_SYSERR == res)
{ {
enum GNUNET_GenericReturnValue res; ret = MHD_NO; /* hard failure */
const char *name; goto CLEANUP;
struct GNUNET_JSON_Specification ispec[] = { }
GNUNET_JSON_spec_string ("extension", if (GNUNET_NO == res)
&name), {
GNUNET_JSON_spec_json ("config", ret = MHD_YES;
&sec.extensions[i].config_json), goto CLEANUP;
GNUNET_JSON_spec_end ()
};
res = TALER_MHD_parse_json_array (connection,
extensions,
ispec,
i,
-1);
if (GNUNET_SYSERR == res)
{
ret = MHD_NO; /* hard failure */
goto CLEANUP;
}
if (GNUNET_NO == res)
{
ret = MHD_YES;
goto CLEANUP;;
}
/* Make sure name refers to a supported extension */
{
bool found = false;
for (unsigned int k = 0; k < TALER_Extension_Max; k++)
{
if (0 == strncmp (name,
TEH_extensions[k].name,
strlen (TEH_extensions[k].name)))
{
sec.extensions[i].type = TEH_extensions[k].type;
found = true;
break;
}
}
if (! found)
{
ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"invalid extension type");
goto CLEANUP;
}
}
/* We have a JSON object for the extension.
* Increment its refcount and free the parser.
* TODO: is this correct? */
json_incref (sec.extensions[i].config_json);
GNUNET_JSON_parse_free (ispec);
/* Make sure the config is sound */
{
switch (sec.extensions[i].type)
{
case TALER_Extension_AgeRestriction:
if (GNUNET_OK != TALER_agemask_parse_json (
sec.extensions[i].config_json,
&sec.extensions[i].mask))
{
ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"invalid mask for age restriction");
goto CLEANUP;
}
break;
case TALER_Extension_Peer2Peer:
/* TODO */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"peer2peer not yet supported in handler for /management/extensions\n");
ret = MHD_NO;
goto CLEANUP;
default:
/* not reachable */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"shouldn't be reached in handler for /management/extensions\n");
ret = MHD_NO;
goto CLEANUP;
}
}
} }
// 2. parse and verify the signature /* 2. Make sure name refers to a supported extension */
if (GNUNET_OK != TALER_get_extension_by_name (name,
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; enum GNUNET_GenericReturnValue res;
struct GNUNET_JSON_Specification ispec[] = { struct GNUNET_JSON_Specification sig_spec[] = {
GNUNET_JSON_spec_fixed_auto (NULL, GNUNET_JSON_spec_fixed_auto (NULL,
&sec.extensions_sigs[i]), &sec.extensions_sigs[i]),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
@ -304,7 +279,7 @@ TEH_handler_management_post_extensions (
res = TALER_MHD_parse_json_array (connection, res = TALER_MHD_parse_json_array (connection,
extensions_sigs, extensions_sigs,
ispec, sig_spec,
i, i,
-1); -1);
if (GNUNET_SYSERR == res) if (GNUNET_SYSERR == res)
@ -317,44 +292,41 @@ TEH_handler_management_post_extensions (
ret = MHD_YES; ret = MHD_YES;
goto CLEANUP; goto CLEANUP;
} }
/* verify the signature */
res = GNUNET_SYSERR;
switch (sec.extensions[i].type)
{
case TALER_Extension_AgeRestriction:
res = TALER_exchange_offline_extension_agemask_verify (
sec.extensions[i].mask,
&TEH_master_public_key,
&sec.extensions_sigs[i]);
break;
case TALER_Extension_Peer2Peer:
/* TODO */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Peer2peer not yet supported in handler for /management/extensions\n");
ret = MHD_NO;
goto CLEANUP;
default:
/* not reachable */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"shouldn't be reached in handler for /management/extensions\n");
ret = MHD_NO;
goto CLEANUP;
}
if (GNUNET_OK != res)
{
ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"invalid signature for extension");
goto CLEANUP;
}
} }
/* 4. Verify the signature of the config */
if (GNUNET_OK != config_verify (
sec.extensions[i].config,
&TEH_master_public_key,
&sec.extensions_sigs[i]))
{
ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"invalid signature for extension");
goto CLEANUP;
}
/* 5. Make sure the config is sound */
if (GNUNET_OK != extension->parse_config (NULL /* only verify */,
sec.extensions[i].config))
{
GNUNET_JSON_parse_free (ext_spec);
ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"invalid configuration for extension");
goto CLEANUP;
}
/* We have a validly signed JSON object for the extension.
* Increment its refcount and free the parser for the extension.
*/
json_incref (sec.extensions[i].config);
GNUNET_JSON_parse_free (ext_spec);
} /* for-loop */ } /* for-loop */
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -386,14 +358,14 @@ TEH_handler_management_post_extensions (
CLEANUP: CLEANUP:
for (unsigned int i = 0; i < sec.num_extensions; i++) for (unsigned int i = 0; i < sec.num_extensions; i++)
{ {
if (NULL != sec.extensions[i].config_json) if (NULL != sec.extensions[i].config)
{ {
json_decref (sec.extensions[i].config_json); json_decref (sec.extensions[i].config);
} }
} }
GNUNET_free (sec.extensions); GNUNET_free (sec.extensions);
GNUNET_free (sec.extensions_sigs); GNUNET_free (sec.extensions_sigs);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (top_spec);
return ret; return ret;
} }

View File

@ -23,7 +23,6 @@
#define TALER_CRYPTO_LIB_H #define TALER_CRYPTO_LIB_H
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include "taler_extensions.h"
#include "taler_error_codes.h" #include "taler_error_codes.h"
#include <gcrypt.h> #include <gcrypt.h>
@ -281,6 +280,26 @@ struct TALER_MasterSignatureP
struct GNUNET_CRYPTO_EddsaSignature eddsa_signature; struct GNUNET_CRYPTO_EddsaSignature eddsa_signature;
}; };
/*
* @brief Type of a list of age groups, represented as bit mask.
*
* The bits set in the mask mark the edges at the beginning of a next age
* group. F.e. for the age groups
* 0-7, 8-9, 10-11, 12-14, 14-15, 16-17, 18-21, 21-*
* the following bits are set:
*
* 31 24 16 8 0
* | | | | |
* oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
*
* A value of 0 means that the exchange does not support the extension for
* age-restriction.
*/
struct TALER_AgeMask
{
uint32_t mask;
};
/** /**
* @brief Age restriction commitment of a coin. * @brief Age restriction commitment of a coin.
*/ */
@ -523,6 +542,19 @@ struct TALER_PickupIdentifierP
}; };
/**
* @brief Salted hash over the JSON object representing the configuration of an
* extension.
*/
struct TALER_ExtensionConfigHash
{
/**
* Actual hash value.
*/
struct GNUNET_HashCode hash;
};
GNUNET_NETWORK_STRUCT_END GNUNET_NETWORK_STRUCT_END
@ -2502,30 +2534,31 @@ TALER_merchant_wire_signature_make (
/* **************** /management/extensions offline signing **************** */ /* **************** /management/extensions offline signing **************** */
/** /**
* Create a signature for age restriction groups * Create a signature for the hash of the configuration of an extension
* *
* @param mask The bitmask representing age groups * @param h_config hash of the JSON object representing the configuration
* @param master_priv private key to sign with * @param master_priv private key to sign with
* @param[out] master_sig where to write the signature * @param[out] master_sig where to write the signature
*/ */
void void
TALER_exchange_offline_extension_agemask_sign ( TALER_exchange_offline_extension_config_hash_sign (
const struct TALER_AgeMask mask, 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);
/** /**
* Verify the signature in @a master_sig. * Verify the signature in @a master_sig of the given hash, taken over the JSON
* blob representing the configuration of an extension
* *
* @param mask bit mask representing an age group for age restriction * @param h_config hash of the JSON blob of a configuration of an extension
* @param master_pub master public key of the exchange * @param master_pub master public key of the exchange
* @param master_sig signature of the exchange * @param master_sig signature of the exchange
* @return #GNUNET_OK if signature is valid * @return #GNUNET_OK if signature is valid
*/ */
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_exchange_offline_extension_agemask_verify ( TALER_exchange_offline_extension_config_hash_verify (
const struct TALER_AgeMask mask, 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

@ -23,6 +23,7 @@
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include "taler_crypto_lib.h" #include "taler_crypto_lib.h"
#include "taler_json_lib.h"
#define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-" #define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-"
@ -39,41 +40,45 @@ enum TALER_Extension_Type
{ {
TALER_Extension_AgeRestriction = 0, TALER_Extension_AgeRestriction = 0,
TALER_Extension_Peer2Peer = 1, TALER_Extension_Peer2Peer = 1,
TALER_Extension_Max = 2 TALER_Extension_Max = 2 // Must be last
}; };
/*
* TODO oec: documentation
*/
struct TALER_Extension struct TALER_Extension
{ {
enum TALER_Extension_Type type; enum TALER_Extension_Type type;
char *name; char *name;
bool critical; bool critical;
void *config; void *config;
enum GNUNET_GenericReturnValue (*parse_config)(struct TALER_Extension *this,
const json_t *config);
json_t *(*config_to_json)(const struct TALER_Extension *this);
}; };
/**
* Generic functions for extensions
*/
/**
* 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[out] ext set to the extension, if found, NULL otherwise
* @return GNUNET_OK if extension was found, GNUNET_NO otherwise
*/
enum GNUNET_GenericReturnValue
TALER_get_extension_by_name (const char *name,
const struct TALER_Extension *extensions,
const struct TALER_Extension **ext);
/* /*
* TALER Age Restriction Extension * TALER Age Restriction Extension
*/ */
/*
* @brief Type of a list of age groups, represented as bit mask.
*
* The bits set in the mask mark the edges at the beginning of a next age
* group. F.e. for the age groups
* 0-7, 8-9, 10-11, 12-14, 14-15, 16-17, 18-21, 21-*
* the following bits are set:
*
* 31 24 16 8 0
* | | | | |
* oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
*
* A value of 0 means that the exchange does not support the extension for
* age-restriction.
*/
struct TALER_AgeMask
{
uint32_t mask;
};
#define TALER_EXTENSION_SECTION_AGE_RESTRICTION (TALER_EXTENSION_SECTION_PREFIX \ #define TALER_EXTENSION_SECTION_AGE_RESTRICTION (TALER_EXTENSION_SECTION_PREFIX \
"age_restriction") "age_restriction")
@ -86,7 +91,19 @@ struct TALER_AgeMask
<< 21) << 21)
/** /**
* @param groups String representation of age groups, like: "8:10:12:14:16:18:21" * @brief Parses a string as a list of age groups.
*
* The string must consist of a colon-separated list of increasing integers
* between 0 and 31. Each entry represents the beginning of a new age group.
* F.e. the string "8:10:12:14:16:18:21" parses into the following list of age
* groups
* 0-7, 8-9, 10-11, 12-13, 14-15, 16-17, 18-20, 21-...
* which then is represented as bit mask with the corresponding bits set:
* 31 24 16 8 0
* | | | | |
* oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
*
* @param groups String representation of age groups
* @param[out] mask Mask representation for age restriction. * @param[out] mask Mask representation for age restriction.
* @return Error, if age groups were invalid, OK otherwise. * @return Error, if age groups were invalid, OK otherwise.
*/ */

View File

@ -532,7 +532,7 @@ TALER_JSON_wire_to_payto (const json_t *wire_s);
/** /**
* Hash @a extensions. * Hash @a extensions in deposits.
* *
* @param extensions contract extensions to hash * @param extensions contract extensions to hash
* @param[out] ech where to write the extension hash * @param[out] ech where to write the extension hash
@ -541,6 +541,16 @@ void
TALER_deposit_extension_hash (const json_t *extensions, TALER_deposit_extension_hash (const json_t *extensions,
struct TALER_ExtensionContractHash *ech); struct TALER_ExtensionContractHash *ech);
/**
* Hash the @a config of an extension, given as JSON
*
* @param config configuration of the extension
* @param[out] eh where to write the extension hash
* @return GNUNET_OK on success, GNUNET_SYSERR on failure
*/
enum GNUNET_GenericReturnValue
TALER_extension_config_hash (const json_t *config,
struct TALER_ExtensionConfigHash *eh);
/** /**
* Parses a JSON object { "extension": "age_restriction", "mask": <uint32> }. * Parses a JSON object { "extension": "age_restriction", "mask": <uint32> }.
@ -553,7 +563,6 @@ enum GNUNET_GenericReturnValue
TALER_agemask_parse_json (const json_t *root, TALER_agemask_parse_json (const json_t *root,
struct TALER_AgeMask *mask); struct TALER_AgeMask *mask);
#endif /* TALER_JSON_LIB_H_ */ #endif /* TALER_JSON_LIB_H_ */
/* End of taler_json_lib.h */ /* End of taler_json_lib.h */

View File

@ -967,9 +967,9 @@ struct TALER_MasterDelWirePS
/* /*
* @brief Signature made by the exchange offline key over the * @brief Signature made by the exchange offline key over the
* configuration of the age restriction extension. * configuration of an extension.
*/ */
struct TALER_MasterExtensionAgeRestrictionPS struct TALER_MasterExtensionConfigurationPS
{ {
/** /**
* Purpose is #TALER_SIGNATURE_MASTER_EXTENSION. Signed * Purpose is #TALER_SIGNATURE_MASTER_EXTENSION. Signed
@ -978,29 +978,11 @@ struct TALER_MasterExtensionAgeRestrictionPS
struct GNUNET_CRYPTO_EccSignaturePurpose purpose; struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/** /**
* Bit mask representing the lits of age groups, see TALER_AgeMask for a * Hash of the JSON object that represents the configuration of an extension.
* description.
*/ */
struct TALER_AgeMask mask; struct TALER_ExtensionConfigHash h_config GNUNET_PACKED;
}; };
#if 0
/*
* @brief Signature made by the exchange offline key over the
* configuration of the peer2peer extension.
*/
struct TALER_MasterExtensionPeer2PeerPS
{
/**
* Purpose is #TALER_SIGNATURE_MASTER_EXTENSION. Signed
* by a `struct TALER_MasterPublicKeyP` using EdDSA.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
// TODO oec
};
#endif
/** /**
* @brief Information about a denomination key. Denomination keys * @brief Information about a denomination key. Denomination keys
* are used to sign coins of a certain value into existence. * are used to sign coins of a certain value into existence.

View File

@ -1009,4 +1009,14 @@ TALER_deposit_extension_hash (const json_t *extensions,
} }
enum GNUNET_GenericReturnValue
TALER_extension_config_hash (const json_t *config,
struct TALER_ExtensionConfigHash *ech)
{
return dump_and_hash (config,
"taler-extension-configuration",
&ech->hash);
}
/* End of json/json.c */ /* End of json/json.c */

View File

@ -153,32 +153,17 @@ TALER_EXCHANGE_management_post_extensions (
GNUNET_assert (NULL != extensions); GNUNET_assert (NULL != extensions);
for (unsigned int i = 0; i<pkd->num_extensions; i++) for (unsigned int i = 0; i<pkd->num_extensions; i++)
{ {
json_t *config; const json_t *config;
const struct TALER_AgeMask *mask;
const struct TALER_Extension *ext = &pkd->extensions[i]; const struct TALER_Extension *ext = &pkd->extensions[i];
switch (ext->type) config = ext->config_to_json (ext);
{
// TODO: case TALER_Extension_Peer2Peer
case TALER_Extension_AgeRestriction:
mask = (const struct TALER_AgeMask *) (&ext->config);
config = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("extension",
ext->name),
GNUNET_JSON_pack_data_auto ("mask",
&mask->mask));
GNUNET_assert (NULL != config);
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Extension not supported.\n");
}
GNUNET_assert (NULL != config);
GNUNET_assert (0 == GNUNET_assert (0 ==
json_array_append_new ( json_array_append_new (
extensions, extensions,
GNUNET_JSON_PACK ( GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("name", GNUNET_JSON_pack_data_auto ("extension",
&ext->name), &ext->name),
GNUNET_JSON_pack_data_auto ("config", GNUNET_JSON_pack_data_auto ("config",
config) config)

View File

@ -72,6 +72,7 @@ libtalerutil_la_SOURCES = \
crypto_wire.c \ crypto_wire.c \
denom.c \ denom.c \
exchange_signatures.c \ exchange_signatures.c \
extensions.c \
extension_age_restriction.c \ extension_age_restriction.c \
getopt.c \ getopt.c \
lang.c \ lang.c \

49
src/util/extensions.c Normal file
View File

@ -0,0 +1,49 @@
/*
This file is part of TALER
Copyright (C) 2014-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 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/>
*/
/**
* @file extensions.c
* @brief Utility functions for extensions
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_extensions.h"
#include "stdint.h"
enum GNUNET_GenericReturnValue
TALER_get_extension_by_name (const char *name,
const struct TALER_Extension *extensions,
const struct TALER_Extension **ext)
{
const struct TALER_Extension *it = extensions;
for (; it->type != TALER_Extension_Max; it++)
{
if (0 == strncmp (name,
it->name,
strlen (it->name)))
{
*ext = it;
return GNUNET_OK;
}
}
return GNUNET_NO;
}
/* end of extensions.c */

View File

@ -491,66 +491,40 @@ TALER_exchange_offline_wire_fee_verify (
void void
TALER_exchange_offline_extension_agemask_sign ( TALER_exchange_offline_extension_config_hash_sign (
const struct TALER_AgeMask mask, 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_MasterExtensionAgeRestrictionPS ar = { struct TALER_MasterExtensionConfigurationPS ec = {
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION), .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
.purpose.size = htonl (sizeof(ar)), .purpose.size = htonl (sizeof(ec)),
.mask = mask .h_config = h_config
}; };
GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv, GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
&ar, &ec,
&master_sig->eddsa_signature); &master_sig->eddsa_signature);
} }
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_exchange_offline_extension_agemask_verify ( TALER_exchange_offline_extension_config_hash_verify (
const struct TALER_AgeMask mask, 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
) )
{ {
struct TALER_MasterExtensionAgeRestrictionPS ar = { struct TALER_MasterExtensionConfigurationPS ec = {
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION), .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
.purpose.size = htonl (sizeof(ar)), .purpose.size = htonl (sizeof(ec)),
.mask = mask .h_config = h_config
}; };
return
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_EXTENSION, return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_EXTENSION,
&ar, &ec,
&master_sig->eddsa_signature, &master_sig->eddsa_signature,
&master_pub->eddsa_pub); &master_pub->eddsa_pub);
} }
#if 0
/* TODO peer2peer */
void
TALER_exchange_offline_extension_p2p_sign (
// TODO
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig)
{
// TODO
}
enum GNUNET_GenericReturnValue
TALER_exchange_offline_extension_p2p_verify (
// TODO
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig,
)
{
// TODO
return GNUNET_FALSE;
}
#endif
/* end of offline_signatures.c */ /* end of offline_signatures.c */