diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index de4ffc7ca..2e0058ba9 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -53,6 +53,7 @@ #include "taler-exchange-httpd_withdraw.h" #include "taler_exchangedb_lib.h" #include "taler_exchangedb_plugin.h" +#include "taler_extensions.h" #include /** @@ -184,6 +185,25 @@ struct GNUNET_CURL_Context *TEH_curl_ctx; */ 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. diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c index 33bbf3525..dd3ac6f81 100644 --- a/src/exchange/taler-exchange-httpd_management_extensions.c +++ b/src/exchange/taler-exchange-httpd_management_extensions.c @@ -1,18 +1,18 @@ /* - This file is part of TALER - Copyright (C) 2021 Taler Systems SA + This file is part of TALER + Copyright (C) 2021 Taler Systems SA - 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 - Foundation; either version 3, or (at your option) any later version. + 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 + 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 Affero General Public License for more details. + 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 Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see -*/ + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see + */ /** * @file taler-exchange-httpd_management_extensions.c * @brief Handle request to POST /management/extensions @@ -28,16 +28,37 @@ #include "taler_signatures.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" +#include "taler_extensions.h" -#if 0 /** - * Function implementing database transaction to add offline signing keys. - * 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. + * TODO oec + */ +struct Extension +{ + char *name; + 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 connection MHD request which triggered the transaction @@ -46,228 +67,23 @@ * @return transaction status */ static enum GNUNET_DB_QueryStatus -add_keys (void *cls, - struct MHD_Connection *connection, - MHD_RESULT *mhd_ret) +set_extension (void *cls, + struct MHD_Connection *connection, + MHD_RESULT *mhd_ret) { - struct AddKeysContext *akc = cls; + // struct SetExtensionContext *sec = cls; - /* activate all denomination keys */ - for (unsigned int i = 0; ind_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; ins_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); - } + // TODO oec return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */ } -#endif - MHD_RESULT TEH_handler_management_post_extensions ( struct MHD_Connection *connection, const json_t *root) { + struct SetExtensionContext sec; json_t *extensions; json_t *extensions_sigs; struct GNUNET_JSON_Specification spec[] = { @@ -291,6 +107,7 @@ TEH_handler_management_post_extensions ( if (GNUNET_NO == res) return MHD_YES; /* failure */ } + if (! (json_is_array (extensions) && json_is_array (extensions_sigs)) ) { @@ -302,42 +119,97 @@ TEH_handler_management_post_extensions ( TALER_EC_GENERIC_PARAMETER_MALFORMED, "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; imaster_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, - json_array_get (denom_sigs, - i), - ispec); - if (GNUNET_SYSERR == res) + 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, + "Received /management/extensions\n"); + + sec.extensions_sigs = GNUNET_new_array (sec.num_extensions, + struct TALER_MasterSignatureP); + ok = true; + for (unsigned int i = 0; i +#include "taler_crypto_lib.h" #define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-" @@ -47,40 +48,11 @@ struct TALER_Extension char *name; bool critical; 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 - * FIXME + * FIXME oec */ diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index bcfa863c1..d9fa7065b 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -97,6 +97,10 @@ */ #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) */ @@ -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 * are used to sign coins of a certain value into existence. diff --git a/src/lib/exchange_api_management_post_extensions.c b/src/lib/exchange_api_management_post_extensions.c index 96a56eb3b..550756827 100644 --- a/src/lib/exchange_api_management_post_extensions.c +++ b/src/lib/exchange_api_management_post_extensions.c @@ -132,7 +132,7 @@ TALER_EXCHANGE_management_post_extensions ( struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph; // FIXME-oec: TODO! CURL *eh = NULL; - json_t *body; + json_t *body = NULL; json_t *extensions = NULL; json_t *extensions_sigs = NULL; @@ -177,7 +177,7 @@ TALER_EXCHANGE_management_post_extensions ( json_array_append_new ( extensions, GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("extension", + GNUNET_JSON_pack_data_auto ("name", &ext->name), GNUNET_JSON_pack_data_auto ("config", config) diff --git a/src/util/offline_signatures.c b/src/util/offline_signatures.c index cd9dceca5..192023578 100644 --- a/src/util/offline_signatures.c +++ b/src/util/offline_signatures.c @@ -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 */