progress: TALER_exchange_offline_extension* added, TEH_Extension moved, et. all

This commit is contained in:
Özgür Kesim 2021-12-27 16:33:09 +01:00
parent 6a16449d15
commit a18de0c657
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
6 changed files with 257 additions and 291 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>
/** /**
@ -184,6 +185,25 @@ struct GNUNET_CURL_Context *TEH_curl_ctx;
*/ */
static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc; static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc;
/**
* TEH_extensions is the global manifest with the list supported extensions,
* sorted by TALER_Extension_Type.
**/
static struct TALER_Extension TEH_extensions[TALER_Extension_Max] = {
[TALER_Extension_Peer2Peer] = {
.type = TALER_Extension_Peer2Peer,
.name = "peer2peer",
.critical = false,
.config = NULL, // == disabled
},
[TALER_Extension_AgeRestriction] = {
.type = TALER_Extension_AgeRestriction,
.name = "age_restriction",
.critical = false,
.config = NULL, // == disabled
},
};
/** /**
* Signature of functions that handle operations on coins. * Signature of functions that handle operations on coins.

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
@ -28,16 +28,37 @@
#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
/** /**
* Function implementing database transaction to add offline signing keys. * TODO oec
* 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 Extension
* 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 char *name;
* MUST not queue a MHD response. json_t *config;
};
/**
* Closure for the #set_extention transaction
*/
struct SetExtensionContext
{
uint32_t num_extensions;
struct Extension *extensions;
struct TALER_MasterSignatureP *extensions_sigs;
};
/**
* Function implementing database transaction to set the configuration of an
* extension. It tuns 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 AddKeysContext`
* @param connection MHD request which triggered the transaction * @param connection MHD request which triggered the transaction
@ -46,228 +67,23 @@
* @return transaction status * @return transaction status
*/ */
static enum GNUNET_DB_QueryStatus static enum GNUNET_DB_QueryStatus
add_keys (void *cls, set_extension (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)
{ {
struct SetExtensionContext sec;
json_t *extensions; json_t *extensions;
json_t *extensions_sigs; json_t *extensions_sigs;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
@ -291,6 +107,7 @@ 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 (extensions) && if (! (json_is_array (extensions) &&
json_is_array (extensions_sigs)) ) json_is_array (extensions_sigs)) )
{ {
@ -302,42 +119,97 @@ TEH_handler_management_post_extensions (
TALER_EC_GENERIC_PARAMETER_MALFORMED, TALER_EC_GENERIC_PARAMETER_MALFORMED,
"array expected for extensions and extensions_sig"); "array expected for extensions and extensions_sig");
} }
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received /management/keys\n");
#if 0
akc.nd_sigs = json_array_size (denom_sigs);
akc.d_sigs = GNUNET_new_array (akc.nd_sigs,
struct DenomSig);
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, sec.num_extensions = json_array_size (extensions_sigs);
json_array_get (denom_sigs, if (json_array_size (extensions) != sec.num_extensions)
i), {
ispec); GNUNET_break_op (0);
if (GNUNET_SYSERR == res) 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,
"Received /management/extensions\n");
sec.extensions_sigs = GNUNET_new_array (sec.num_extensions,
struct TALER_MasterSignatureP);
ok = true;
for (unsigned int i = 0; i<sec.num_extensions; i++)
{
// 1. parse the extensions
{ {
ret = MHD_NO; /* hard failure */ enum GNUNET_GenericReturnValue res;
ok = false; struct GNUNET_JSON_Specification ispec[] = {
break; GNUNET_JSON_spec_string ("name",
(const char **) &sec.extensions[i].name),
GNUNET_JSON_spec_json ("config",
&sec.extensions[i].config),
GNUNET_JSON_spec_end ()
};
res = TALER_MHD_parse_json_array (connection,
extensions,
ispec,
i,
-1);
GNUNET_JSON_parse_free (ispec); // FIXME: needed?
if (GNUNET_SYSERR == res)
{
ret = MHD_NO; /* hard failure */
ok = false;
break;
}
if (GNUNET_NO == res)
{
ret = MHD_YES;
ok = false;
break;
}
} }
if (GNUNET_NO == res)
// 2. parse the signatures
{
enum GNUNET_GenericReturnValue res;
struct GNUNET_JSON_Specification ispec[] = {
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);
GNUNET_JSON_parse_free (ispec); // FIXME: needed?
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 signatures
// TODO oec
{ {
ret = MHD_YES;
ok = false;
break;
} }
} }
#if 0
if (! ok) if (! ok)
{ {
GNUNET_free (akc.d_sigs); GNUNET_free (akc.d_sigs);

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-"
@ -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

@ -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

@ -132,7 +132,7 @@ TALER_EXCHANGE_management_post_extensions (
struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph; struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph;
// FIXME-oec: TODO! // 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 *extensions_sigs = NULL; json_t *extensions_sigs = NULL;
@ -177,7 +177,7 @@ 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)

View File

@ -490,4 +490,66 @@ TALER_exchange_offline_wire_fee_verify (
} }
void
TALER_exchange_offline_extension_age_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_age_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
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 */