[age restriction] progress 9/n

More worke towards support for extensions and age restriction:

- taler-exchange-httpd_management_extensions.c almost completed
  - handling of request implemented
  - stub "set_extensions" for database transaction added

- utility functions added
  - TALER_exchange_offline_extension_agemask_{sign,verify}
  - TALER_agemask_parse_json
This commit is contained in:
Özgür Kesim 2021-12-27 23:24:48 +01:00
parent 070f442a11
commit ef4238874f
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
12 changed files with 495 additions and 374 deletions

View File

@ -53,6 +53,7 @@
#include "taler-exchange-httpd_withdraw.h" #include "taler-exchange-httpd_withdraw.h"
#include "taler_exchangedb_lib.h" #include "taler_exchangedb_lib.h"
#include "taler_exchangedb_plugin.h" #include "taler_exchangedb_plugin.h"
#include "taler_extensions.h"
#include <gnunet/gnunet_mhd_compat.h> #include <gnunet/gnunet_mhd_compat.h>
/** /**
@ -146,6 +147,25 @@ int TEH_check_invariants_flag;
*/ */
bool TEH_suicide; bool TEH_suicide;
/**
* The global manifest with the list supported extensions, sorted by
* TALER_Extension_Type.
**/
const struct TALER_Extension TEH_extensions[TALER_Extension_Max] = {
[TALER_Extension_Peer2Peer] = {
.type = TALER_Extension_Peer2Peer,
.name = "peer2peer",
.critical = false,
.config = NULL, // disabled per default
},
[TALER_Extension_AgeRestriction] = {
.type = TALER_Extension_AgeRestriction,
.name = "age_restriction",
.critical = false,
.config = NULL, // disabled per default
},
};
/** /**
* Value to return from main() * Value to return from main()
*/ */
@ -184,7 +204,6 @@ struct GNUNET_CURL_Context *TEH_curl_ctx;
*/ */
static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc; static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc;
/** /**
* Signature of functions that handle operations on coins. * Signature of functions that handle operations on coins.
* *

View File

@ -26,6 +26,7 @@
#include <microhttpd.h> #include <microhttpd.h>
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_crypto_lib.h" #include "taler_crypto_lib.h"
#include "taler_extensions.h"
#include <gnunet/gnunet_mhd_compat.h> #include <gnunet/gnunet_mhd_compat.h>
@ -200,6 +201,11 @@ 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
*/
extern const struct TALER_Extension TEH_extensions[TALER_Extension_Max];
/** /**
* @brief Struct describing an URL and the handler for it. * @brief Struct describing an URL and the handler for it.
*/ */

View File

@ -1,18 +1,18 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2021 Taler Systems SA Copyright (C) 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_management_extensions.c * @file taler-exchange-httpd_management_extensions.c
* @brief Handle request to POST /management/extensions * @brief Handle request to POST /management/extensions
@ -23,260 +23,80 @@
#include <gnunet/gnunet_json_lib.h> #include <gnunet/gnunet_json_lib.h>
#include <jansson.h> #include <jansson.h>
#include <microhttpd.h> #include <microhttpd.h>
#include <pthread.h>
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_mhd_lib.h" #include "taler_mhd_lib.h"
#include "taler_signatures.h" #include "taler_signatures.h"
#include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_management.h"
#include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_responses.h"
#include "taler_extensions.h"
#if 0 struct Extension
{
enum TALER_Extension_Type type;
json_t *config_json;
// This union contains the parsed configuration for each extension.
union
{
// configuration for the age restriction
struct TALER_AgeMask mask;
/* TODO oec - peer2peer config */
};
};
/** /**
* Function implementing database transaction to add offline signing keys. * Closure for the #set_extensions transaction
* Runs the transaction logic; IF it returns a non-error code, the transaction */
* logic MUST NOT queue a MHD response. IF it returns an hard error, the struct SetExtensionsContext
* transaction logic MUST queue a MHD response and set @a mhd_ret. IF it {
* returns the soft error code, the function MAY be called again to retry and uint32_t num_extensions;
* MUST not queue a MHD response. struct Extension *extensions;
struct TALER_MasterSignatureP *extensions_sigs;
};
/**
* Function implementing database transaction to set the configuration of
* extensions. It runs the transaction logic.
* - IF it returns a non-error code, the transaction logic MUST NOT queue a
* MHD response.
* - IF it returns an hard error, the transaction logic MUST queue a MHD
* response and set @a mhd_ret.
* - IF it returns the soft error code, the function MAY be called again to
* retry and MUST not queue a MHD response.
* *
* @param cls closure with a `struct AddKeysContext` * @param cls closure with a `struct SetExtensionsContext`
* @param connection MHD request which triggered the transaction * @param connection MHD request which triggered the transaction
* @param[out] mhd_ret set to MHD response status for @a connection, * @param[out] mhd_ret set to MHD response status for @a connection,
* if transaction failed (!) * if transaction failed (!)
* @return transaction status * @return transaction status
*/ */
static enum GNUNET_DB_QueryStatus static enum GNUNET_DB_QueryStatus
add_keys (void *cls, set_extensions (void *cls,
struct MHD_Connection *connection, struct MHD_Connection *connection,
MHD_RESULT *mhd_ret) MHD_RESULT *mhd_ret)
{ {
struct AddKeysContext *akc = cls; // struct SetExtensionContext *sec = cls;
/* activate all denomination keys */ // TODO oec
for (unsigned int i = 0; i<akc->nd_sigs; i++)
{
struct DenomSig *d = &akc->d_sigs[i];
enum GNUNET_DB_QueryStatus qs;
bool is_active = false;
struct TALER_EXCHANGEDB_DenominationKeyMetaData meta;
struct TALER_DenominationPublicKey denom_pub;
/* For idempotency, check if the key is already active */
memset (&denom_pub,
0,
sizeof (denom_pub));
qs = TEH_plugin->lookup_denomination_key (
TEH_plugin->cls,
&d->h_denom_pub,
&meta);
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
return qs;
GNUNET_break (0);
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"lookup denomination key");
return qs;
}
if (0 == qs)
{
enum GNUNET_GenericReturnValue rv;
rv = TEH_keys_load_fees (&d->h_denom_pub,
&denom_pub,
&meta);
switch (rv)
{
case GNUNET_SYSERR:
*mhd_ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
GNUNET_h2s (&d->h_denom_pub.hash));
return GNUNET_DB_STATUS_HARD_ERROR;
case GNUNET_NO:
*mhd_ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
GNUNET_h2s (&d->h_denom_pub.hash));
return GNUNET_DB_STATUS_HARD_ERROR;
case GNUNET_OK:
break;
}
}
else
{
is_active = true;
}
/* check signature is valid */
if (GNUNET_OK !=
TALER_exchange_offline_denom_validity_verify (
&d->h_denom_pub,
meta.start,
meta.expire_withdraw,
meta.expire_deposit,
meta.expire_legal,
&meta.value,
&meta.fee_withdraw,
&meta.fee_deposit,
&meta.fee_refresh,
&meta.fee_refund,
&TEH_master_public_key,
&d->master_sig))
{
GNUNET_break_op (0);
*mhd_ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_MANAGEMENT_KEYS_DENOMKEY_ADD_SIGNATURE_INVALID,
GNUNET_h2s (&d->h_denom_pub.hash));
if (! is_active)
TALER_denom_pub_free (&denom_pub);
return GNUNET_DB_STATUS_HARD_ERROR;
}
if (is_active)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Denomination key %s already active, skipping\n",
GNUNET_h2s (&d->h_denom_pub.hash));
continue; /* skip, already known */
}
qs = TEH_plugin->add_denomination_key (
TEH_plugin->cls,
&d->h_denom_pub,
&denom_pub,
&meta,
&d->master_sig);
TALER_denom_pub_free (&denom_pub);
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
return qs;
GNUNET_break (0);
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_STORE_FAILED,
"activate denomination key");
return qs;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Added offline signature for denomination `%s'\n",
GNUNET_h2s (&d->h_denom_pub.hash));
GNUNET_assert (0 != qs);
}
for (unsigned int i = 0; i<akc->ns_sigs; i++)
{
struct SigningSig *s = &akc->s_sigs[i];
enum GNUNET_DB_QueryStatus qs;
bool is_active = false;
struct TALER_EXCHANGEDB_SignkeyMetaData meta;
qs = TEH_plugin->lookup_signing_key (
TEH_plugin->cls,
&s->exchange_pub,
&meta);
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
return qs;
GNUNET_break (0);
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"lookup signing key");
return qs;
}
if (0 == qs)
{
if (GNUNET_OK !=
TEH_keys_get_timing (&s->exchange_pub,
&meta))
{
/* For idempotency, check if the key is already active */
*mhd_ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_UNKNOWN,
TALER_B2S (&s->exchange_pub));
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
else
{
is_active = true; /* if we pass, it's active! */
}
/* check signature is valid */
if (GNUNET_OK !=
TALER_exchange_offline_signkey_validity_verify (
&s->exchange_pub,
meta.start,
meta.expire_sign,
meta.expire_legal,
&TEH_master_public_key,
&s->master_sig))
{
GNUNET_break_op (0);
*mhd_ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_ADD_SIGNATURE_INVALID,
TALER_B2S (&s->exchange_pub));
return GNUNET_DB_STATUS_HARD_ERROR;
}
if (is_active)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Signing key %s already active, skipping\n",
TALER_B2S (&s->exchange_pub));
continue; /* skip, already known */
}
qs = TEH_plugin->activate_signing_key (
TEH_plugin->cls,
&s->exchange_pub,
&meta,
&s->master_sig);
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
return qs;
GNUNET_break (0);
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_STORE_FAILED,
"activate signing key");
return qs;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Added offline signature for signing key `%s'\n",
TALER_B2S (&s->exchange_pub));
GNUNET_assert (0 != qs);
}
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */
} }
#endif
MHD_RESULT MHD_RESULT
TEH_handler_management_post_extensions ( TEH_handler_management_post_extensions (
struct MHD_Connection *connection, struct MHD_Connection *connection,
const json_t *root) const json_t *root)
{ {
#if 0 struct SetExtensionsContext sec = {0};
json_t *denom_sigs; json_t *extensions;
json_t *signkey_sigs; json_t *extensions_sigs;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("denom_sigs", GNUNET_JSON_spec_json ("extensions",
&denom_sigs), &extensions),
GNUNET_JSON_spec_json ("signkey_sigs", GNUNET_JSON_spec_json ("extensions_sigs",
&signkey_sigs), &extensions_sigs),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
bool ok; bool ok;
@ -293,8 +113,9 @@ TEH_handler_management_post_extensions (
if (GNUNET_NO == res) if (GNUNET_NO == res)
return MHD_YES; /* failure */ return MHD_YES; /* failure */
} }
if (! (json_is_array (denom_sigs) &&
json_is_array (signkey_sigs)) ) if (! (json_is_array (extensions) &&
json_is_array (extensions_sigs)) )
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
@ -302,113 +123,236 @@ TEH_handler_management_post_extensions (
connection, connection,
MHD_HTTP_BAD_REQUEST, MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED, TALER_EC_GENERIC_PARAMETER_MALFORMED,
"array expected for denom_sigs and signkey_sigs"); "array expected for extensions and extensions_sig");
} }
sec.num_extensions = json_array_size (extensions_sigs);
if (json_array_size (extensions) != sec.num_extensions)
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"arrays extensions and extensions_sig are not of equal size");
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received /management/keys\n"); "Received /management/extensions\n");
akc.nd_sigs = json_array_size (denom_sigs);
akc.d_sigs = GNUNET_new_array (akc.nd_sigs, sec.extensions = GNUNET_new_array (sec.num_extensions,
struct DenomSig); struct Extension);
sec.extensions_sigs = GNUNET_new_array (sec.num_extensions,
struct TALER_MasterSignatureP);
ok = true; ok = true;
for (unsigned int i = 0; i<akc.nd_sigs; i++)
{
struct DenomSig *d = &akc.d_sigs[i];
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_fixed_auto ("master_sig",
&d->master_sig),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
&d->h_denom_pub),
GNUNET_JSON_spec_end ()
};
enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_data (connection, for (unsigned int i = 0; i<sec.num_extensions; i++)
json_array_get (denom_sigs,
i),
ispec);
if (GNUNET_SYSERR == res)
{
ret = MHD_NO; /* hard failure */
ok = false;
break;
}
if (GNUNET_NO == res)
{
ret = MHD_YES;
ok = false;
break;
}
}
if (! ok)
{ {
GNUNET_free (akc.d_sigs);
GNUNET_JSON_parse_free (spec);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failure to handle /management/keys\n");
return ret;
}
akc.ns_sigs = json_array_size (signkey_sigs);
akc.s_sigs = GNUNET_new_array (akc.ns_sigs,
struct SigningSig);
for (unsigned int i = 0; i<akc.ns_sigs; i++)
{
struct SigningSig *s = &akc.s_sigs[i];
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_fixed_auto ("master_sig",
&s->master_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
&s->exchange_pub),
GNUNET_JSON_spec_end ()
};
enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_data (connection, // 1. parse the extension
json_array_get (signkey_sigs,
i),
ispec);
if (GNUNET_SYSERR == res)
{ {
ret = MHD_NO; /* hard failure */ enum GNUNET_GenericReturnValue res;
ok = false; const char *name;
break; struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_string ("extension",
&name),
GNUNET_JSON_spec_json ("config",
&sec.extensions[i].config_json),
GNUNET_JSON_spec_end ()
};
res = TALER_MHD_parse_json_array (connection,
extensions,
ispec,
i,
-1);
if (GNUNET_SYSERR == res)
{
ret = MHD_NO; /* hard failure */
ok = false;
break;
}
if (GNUNET_NO == res)
{
ret = MHD_YES;
ok = false;
break;
}
// 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)
{
GNUNET_free (sec.extensions);
GNUNET_free (sec.extensions_sigs);
GNUNET_JSON_parse_free (spec);
GNUNET_JSON_parse_free (ispec);
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"invalid extension type");
}
}
// 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))
{
GNUNET_free (sec.extensions);
GNUNET_free (sec.extensions_sigs);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"invalid mask for age restriction");
}
break;
case TALER_Extension_Peer2Peer: /* TODO */
ok = false;
ret = MHD_NO;
goto BREAK;
default:
/* not reachable */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"shouldn't be reached in handler for /management/extensions\n");
ok = false;
ret = MHD_NO;
goto BREAK;
}
}
} }
if (GNUNET_NO == res)
// 2. parse the signature
{ {
ret = MHD_YES; enum GNUNET_GenericReturnValue res;
ok = false; struct GNUNET_JSON_Specification ispec[] = {
break; GNUNET_JSON_spec_fixed_auto (NULL,
&sec.extensions_sigs[i]),
GNUNET_JSON_spec_end ()
};
res = TALER_MHD_parse_json_array (connection,
extensions_sigs,
ispec,
i,
-1);
if (GNUNET_SYSERR == res)
{
ret = MHD_NO; /* hard failure */
ok = false;
break;
}
if (GNUNET_NO == res)
{
ret = MHD_YES;
ok = false;
break;
}
}
// 3. verify the signature
{
enum GNUNET_GenericReturnValue res;
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]);
if (GNUNET_OK != res)
{
GNUNET_free (sec.extensions);
GNUNET_free (sec.extensions_sigs);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"invalid signature for age mask");
}
break;
case TALER_Extension_Peer2Peer: /* TODO */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Peer2peer not yet supported in handler for /management/extensions\n");
ok = false;
ret = MHD_NO;
goto BREAK;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"shouldn't be reached in handler for /management/extensions\n");
ok = false;
ret = MHD_NO;
/* not reachable */
goto BREAK;
}
} }
} }
BREAK:
if (! ok) if (! ok)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failure to handle /management/keys\n"); "Failure to handle /management/extensions\n");
GNUNET_free (akc.d_sigs); GNUNET_free (sec.extensions);
GNUNET_free (akc.s_sigs); GNUNET_free (sec.extensions_sigs);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return ret; return ret;
} }
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received %u denomination and %u signing key signatures\n", "Received %u extensions\n",
akc.nd_sigs, sec.num_extensions);
akc.ns_sigs);
{ {
enum GNUNET_GenericReturnValue res; enum GNUNET_GenericReturnValue res;
res = TEH_DB_run_transaction (connection, res = TEH_DB_run_transaction (connection,
"add keys", "set extensions",
TEH_MT_OTHER, TEH_MT_OTHER,
&ret, &ret,
&add_keys, &set_extensions,
&akc); &sec);
GNUNET_free (akc.d_sigs);
GNUNET_free (akc.s_sigs); GNUNET_free (sec.extensions);
GNUNET_free (sec.extensions_sigs);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
if (GNUNET_SYSERR == res) if (GNUNET_SYSERR == res)
return ret; return ret;
} }
TEH_keys_update_states ();
#endif
return TALER_MHD_reply_static ( return TALER_MHD_reply_static (
connection, connection,

View File

@ -2518,4 +2518,36 @@ TALER_merchant_wire_signature_make (
struct TALER_MerchantSignatureP *merch_sig); struct TALER_MerchantSignatureP *merch_sig);
/* **************** /management/extensions offline signing **************** */
/**
* Create a signature for age restriction groups
*
* @param mask The bitmask representing age groups
* @param master_priv private key to sign with
* @param[out] master_sig where to write the signature
*/
void
TALER_exchange_offline_extension_agemask_sign (
const struct TALER_AgeMask mask,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig);
/**
* Verify the signature in @a master_sig.
*
* @param mask bit mask representing an age group for age restriction
* @param master_pub master public key of the exchange
* @param master_sig signature of the exchange
* @return #GNUNET_OK if signature is valid
*/
enum GNUNET_GenericReturnValue
TALER_exchange_offline_extension_agemask_verify (
const struct TALER_AgeMask mask,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig
);
#endif #endif

View File

@ -2680,7 +2680,7 @@ TALER_EXCHANGE_post_management_keys_cancel (
struct TALER_EXCHANGE_ManagementPostExtensionsData struct TALER_EXCHANGE_ManagementPostExtensionsData
{ {
struct TALER_Extension *extensions; struct TALER_Extension *extensions;
struct TALER_MasterSignatureP *extension_sigs; struct TALER_MasterSignatureP *extensions_sigs;
uint32_t num_extensions; uint32_t num_extensions;
}; };

View File

@ -22,6 +22,7 @@
#define TALER_EXTENSIONS_H #define TALER_EXTENSIONS_H
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include "taler_crypto_lib.h"
#define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-" #define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-"
@ -36,8 +37,8 @@ enum TALER_Extension_ReturnValue
enum TALER_Extension_Type enum TALER_Extension_Type
{ {
TALER_Extension_Peer2Peer = 0, TALER_Extension_AgeRestriction = 0,
TALER_Extension_AgeRestriction = 1, TALER_Extension_Peer2Peer = 1,
TALER_Extension_Max = 2 TALER_Extension_Max = 2
}; };
@ -47,40 +48,11 @@ struct TALER_Extension
char *name; char *name;
bool critical; bool critical;
void *config; void *config;
size_t config_size;
}; };
struct TALER_Peer2Peer_Config
{
// FIXME
};
/**
* TEH_extensions is the global manifest with the list supported extensions,
* sorted by TALER_Extension_Type.
*
* TODO: Mutex?
*
**/
struct TALER_Extension TEH_extensions[TALER_Extension_Max] = {
[TALER_Extension_Peer2Peer] = {
.type = TALER_Extension_Peer2Peer,
.name = "peer2peer",
.critical = false,
.config_size = sizeof(struct TALER_Peer2Peer_Config),
},
[TALER_Extension_AgeRestriction] = {
.type = TALER_Extension_AgeRestriction,
.name = "age_restriction",
.critical = false,
.config_size = sizeof(struct TALER_AgeMask),
},
};
/* /*
* TALER Peer2Peer Extension * TALER Peer2Peer Extension
* FIXME * FIXME oec
*/ */

View File

@ -542,6 +542,18 @@ TALER_deposit_extension_hash (const json_t *extensions,
struct TALER_ExtensionContractHash *ech); struct TALER_ExtensionContractHash *ech);
/**
* Parses a JSON object { "extension": "age_restriction", "mask": <uint32> }.
*
* @param root is the json object
* @param[out] mask on succes, will contain the age mask
* @return GNUNET_OK on success and GNUNET_SYSERR on failure.
*/
enum GNUNET_GenericReturnValue
TALER_agemask_parse_json (const json_t *root,
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

@ -97,6 +97,10 @@
*/ */
#define TALER_SIGNATURE_MASTER_WIRE_DETAILS 1030 #define TALER_SIGNATURE_MASTER_WIRE_DETAILS 1030
/**
* Set the configuration of an extension (age-restriction or peer2peer)
*/
#define TALER_SIGNATURE_MASTER_EXTENSION 1031
/*********************************************/ /*********************************************/
/* Exchange online signatures (with signing key) */ /* Exchange online signatures (with signing key) */
@ -961,6 +965,42 @@ struct TALER_MasterDelWirePS
}; };
/*
* @brief Signature made by the exchange offline key over the
* configuration of the age restriction extension.
*/
struct TALER_MasterExtensionAgeRestrictionPS
{
/**
* Purpose is #TALER_SIGNATURE_MASTER_EXTENSION. Signed
* by a `struct TALER_MasterPublicKeyP` using EdDSA.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Bit mask representing the lits of age groups, see TALER_AgeMask for a
* description.
*/
struct TALER_AgeMask mask;
};
#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

@ -659,4 +659,36 @@ TALER_JSON_spec_i18n_str (const char *name,
} }
enum GNUNET_GenericReturnValue
TALER_agemask_parse_json (const json_t *root,
struct TALER_AgeMask *mask)
{
const char *name;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("extension",
&name),
GNUNET_JSON_spec_uint32 ("mask",
&mask->mask),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK != GNUNET_JSON_parse (root,
spec,
NULL,
NULL))
{
return GNUNET_SYSERR;
}
if (! strncmp (name,
"age_restriction",
sizeof("age_restriction")))
{
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/* end of json/json_helper.c */ /* end of json/json_helper.c */

View File

@ -130,11 +130,10 @@ TALER_EXCHANGE_management_post_extensions (
void *cb_cls) void *cb_cls)
{ {
struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph; struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph;
// FIXME-oec: TODO!
CURL *eh = NULL; CURL *eh = NULL;
json_t *body; json_t *body = NULL;
json_t *extensions = NULL; json_t *extensions = NULL;
json_t *extension_sigs = NULL; json_t *extensions_sigs = NULL;
ph = GNUNET_new (struct TALER_EXCHANGE_ManagementPostExtensionsHandle); ph = GNUNET_new (struct TALER_EXCHANGE_ManagementPostExtensionsHandle);
ph->cb = cb; ph->cb = cb;
@ -156,16 +155,18 @@ TALER_EXCHANGE_management_post_extensions (
{ {
json_t *config; json_t *config;
const struct TALER_AgeMask *mask; const struct TALER_AgeMask *mask;
const struct TALER_Extension *ext const struct TALER_Extension *ext = &pkd->extensions[i];
= &pkd->extensions[i];
switch (ext->type) switch (ext->type)
{ {
// TODO: case TALER_Extension_Peer2Peer // TODO: case TALER_Extension_Peer2Peer
case TALER_Extension_AgeRestriction: case TALER_Extension_AgeRestriction:
mask = (struct TALER_AgeMask *) (&ext->config); mask = (const struct TALER_AgeMask *) (&ext->config);
config = GNUNET_JSON_PACK (GNUNET_JSON_pack_data_auto ("mask", config = GNUNET_JSON_PACK (
&mask->mask)); GNUNET_JSON_pack_string ("extension",
ext->name),
GNUNET_JSON_pack_data_auto ("mask",
&mask->mask));
GNUNET_assert (NULL != config); GNUNET_assert (NULL != config);
break; break;
default: default:
@ -177,22 +178,22 @@ TALER_EXCHANGE_management_post_extensions (
json_array_append_new ( json_array_append_new (
extensions, extensions,
GNUNET_JSON_PACK ( GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("extension", GNUNET_JSON_pack_data_auto ("name",
&ext->name), &ext->name),
GNUNET_JSON_pack_data_auto ("config", GNUNET_JSON_pack_data_auto ("config",
config) config)
))); )));
} }
extension_sigs = json_array (); extensions_sigs = json_array ();
GNUNET_assert (NULL != extension_sigs); GNUNET_assert (NULL != extensions_sigs);
for (unsigned int i = 0; i<pkd->num_extensions; i++) for (unsigned int i = 0; i<pkd->num_extensions; i++)
{ {
const struct TALER_MasterSignatureP *sks const struct TALER_MasterSignatureP *sks
= &pkd->extension_sigs[i]; = &pkd->extensions_sigs[i];
GNUNET_assert (0 == GNUNET_assert (0 ==
json_array_append_new ( json_array_append_new (
extension_sigs, extensions_sigs,
GNUNET_JSON_PACK ( GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("extension_sig", GNUNET_JSON_pack_data_auto ("extension_sig",
&sks->eddsa_signature)))); &sks->eddsa_signature))));
@ -200,8 +201,8 @@ TALER_EXCHANGE_management_post_extensions (
body = GNUNET_JSON_PACK ( body = GNUNET_JSON_PACK (
GNUNET_JSON_pack_array_steal ("extensions", GNUNET_JSON_pack_array_steal ("extensions",
extensions), extensions),
GNUNET_JSON_pack_array_steal ("extension_sigs", GNUNET_JSON_pack_array_steal ("extensions_sigs",
extension_sigs)); extensions_sigs));
eh = curl_easy_init (); eh = curl_easy_init ();
GNUNET_assert (NULL != eh); GNUNET_assert (NULL != eh);
if (GNUNET_OK != if (GNUNET_OK !=

View File

@ -38,12 +38,12 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
char *groups; char *groups;
enum TALER_Extension_ReturnValue ret = TALER_Extension_ERROR_SYS; enum TALER_Extension_ReturnValue ret = TALER_Extension_ERROR_SYS;
if ((GNUNET_NO == GNUNET_CONFIGURATION_have_value (cfg, if ((GNUNET_YES != GNUNET_CONFIGURATION_have_value (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION, TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"ENABLED")) || "ENABLED")) ||
(GNUNET_NO == GNUNET_CONFIGURATION_get_value_yesno (cfg, (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION, TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"ENABLED"))) "ENABLED")))
{ {
/* Age restriction is not enabled */ /* Age restriction is not enabled */
mask->mask = 0; mask->mask = 0;

View File

@ -490,4 +490,67 @@ TALER_exchange_offline_wire_fee_verify (
} }
void
TALER_exchange_offline_extension_agemask_sign (
const struct TALER_AgeMask mask,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig)
{
struct TALER_MasterExtensionAgeRestrictionPS ar = {
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
.purpose.size = htonl (sizeof(ar)),
.mask = mask
};
GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
&ar,
&master_sig->eddsa_signature);
}
enum GNUNET_GenericReturnValue
TALER_exchange_offline_extension_agemask_verify (
const struct TALER_AgeMask mask,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig
)
{
struct TALER_MasterExtensionAgeRestrictionPS ar = {
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
.purpose.size = htonl (sizeof(ar)),
.mask = mask
};
return
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_EXTENSION,
&ar,
&master_sig->eddsa_signature,
&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 */