exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 22ac409a1116d97050d17771dfe8649e8b79c301
parent fafdb7a576870999ab3d6c761094fe0e79a8a619
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed,  6 May 2026 21:49:18 +0200

clean up /keys logic: separate configuration and SECMOD logic

Diffstat:
Msrc/exchange/meson.build | 2++
Asrc/exchange/taler-exchange-httpd_configuration.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/exchange/taler-exchange-httpd_configuration.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/exchange/taler-exchange-httpd_get-keys.c | 1219+++----------------------------------------------------------------------------
Msrc/exchange/taler-exchange-httpd_get-keys.h | 172+++----------------------------------------------------------------------------
Msrc/exchange/taler-exchange-httpd_post-blinding-prepare.c | 14+++++++-------
Msrc/exchange/taler-exchange-httpd_post-management-denominations-H_DENOM_PUB-revoke.c | 3++-
Msrc/exchange/taler-exchange-httpd_post-management-keys.c | 12++++++------
Msrc/exchange/taler-exchange-httpd_post-melt.c | 9+++++----
Msrc/exchange/taler-exchange-httpd_post-withdraw.c | 9+++++----
Asrc/exchange/taler-exchange-httpd_secmod-helpers.c | 966+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/exchange/taler-exchange-httpd_secmod-helpers.h | 386+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testing/test_exchange_api.conf | 2+-
13 files changed, 1615 insertions(+), 1364 deletions(-)

diff --git a/src/exchange/meson.build b/src/exchange/meson.build @@ -179,6 +179,7 @@ taler_exchange_httpd_SOURCES = [ 'taler-exchange-httpd_common_kyc.c', 'taler-exchange-httpd_get-config.c', 'taler-exchange-httpd_get-contracts-CONTRACT_PUB.c', + 'taler-exchange-httpd_configuration.c', 'taler-exchange-httpd_db.c', 'taler-exchange-httpd_get-deposits.c', 'taler-exchange-httpd_extensions.c', @@ -205,6 +206,7 @@ taler_exchange_httpd_SOURCES = [ 'taler-exchange-httpd_post-management-wire-fee.c', 'taler-exchange-httpd_post-melt.c', 'taler-exchange-httpd_get-metrics.c', + 'taler-exchange-httpd_secmod-helpers.c', 'taler-exchange-httpd_mhd.c', 'taler-exchange-httpd_post-purses-PURSE_PUB-create.c', 'taler-exchange-httpd_post-purses-PURSE_PUB-deposit.c', diff --git a/src/exchange/taler-exchange-httpd_configuration.c b/src/exchange/taler-exchange-httpd_configuration.c @@ -0,0 +1,129 @@ +/* + This file is part of TALER + Copyright (C) 2020-2025 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 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 <http://www.gnu.org/licenses/> + */ +/** + * @file taler-exchange-httpd_configuration.c + * @brief configuration parsing helper functions + * @author Christian Grothoff + * @author Özgür Kesim + */ +#include "taler-exchange-httpd.h" +#include "taler-exchange-httpd_configuration.h" + + +struct TALER_AgeMask +TEH_CONFIG_load_age_mask (const char *section_name) +{ + static const struct TALER_AgeMask null_mask = {0}; + enum GNUNET_GenericReturnValue ret; + + if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( + TEH_cfg, + section_name, + "AGE_RESTRICTED"))) + return null_mask; + + if (GNUNET_SYSERR == + (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg, + section_name, + "AGE_RESTRICTED"))) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section_name, + "AGE_RESTRICTED", + "Value must be YES or NO\n"); + return null_mask; + } + + if (GNUNET_OK == ret) + { + if (! TEH_age_restriction_enabled) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "age restriction set in section %s, yet, age restriction is not enabled\n", + section_name); + return TEH_age_restriction_config.mask; + } + return null_mask; +} + + +enum GNUNET_GenericReturnValue +TEH_CONFIG_load_denom_data ( + const char *section_name, + struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta) +{ + struct GNUNET_TIME_Relative deposit_duration; + struct GNUNET_TIME_Relative legal_duration; + + GNUNET_assert (! GNUNET_TIME_absolute_is_zero (meta->start.abs_time)); /* caller bug */ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (TEH_cfg, + section_name, + "DURATION_SPEND", + &deposit_duration)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section_name, + "DURATION_SPEND"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (TEH_cfg, + section_name, + "DURATION_LEGAL", + &legal_duration)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section_name, + "DURATION_LEGAL"); + return GNUNET_SYSERR; + } + meta->expire_deposit + = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add (meta->expire_withdraw.abs_time, + deposit_duration)); + meta->expire_legal = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add (meta->expire_deposit.abs_time, + legal_duration)); + if (GNUNET_OK != + TALER_config_get_amount (TEH_cfg, + section_name, + "VALUE", + &meta->value)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "Need amount for option `%s' in section `%s'\n", + "VALUE", + section_name); + return GNUNET_SYSERR; + } + if (0 != strcasecmp (TEH_currency, + meta->value.currency)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Need denomination value in section `%s' to use currency `%s'\n", + section_name, + TEH_currency); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_config_get_denom_fees (TEH_cfg, + TEH_currency, + section_name, + &meta->fees)) + return GNUNET_SYSERR; + meta->age_mask = TEH_CONFIG_load_age_mask (section_name); + return GNUNET_OK; +} diff --git a/src/exchange/taler-exchange-httpd_configuration.h b/src/exchange/taler-exchange-httpd_configuration.h @@ -0,0 +1,56 @@ +/* + This file is part of TALER + Copyright (C) 2020-2022, 2026 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 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 <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-exchange-httpd_configuration.h + * @brief helper functions to parse configuration data + * @author Christian Grothoff + */ +#include "taler/taler_json_lib.h" +#include "taler/taler_mhd_lib.h" +#include "taler-exchange-httpd_responses.h" + + +#ifndef TALER_EXCHANGE_HTTPD_CONFIGURATION_H +#define TALER_EXCHANGE_HTTPD_CONFIGURATION_H + + +/** + * Looks up the AGE_RESTRICTED setting for a denomination in the config and + * returns the age restriction (mask) accordingly. + * + * @param section_name Section in the configuration for the particular + * denomination. + */ +struct TALER_AgeMask +TEH_CONFIG_load_age_mask (const char *section_name); + + +/** + * Load denomination configuration data, like fees, expiration times (!) and + * age restriction flags for the denomination type configured in section @a + * section_name. Before calling this function, the `start` and + * `validity_duration` times must already be initialized in @a meta. + * + * @param section_name section in the configuration to use + * @param[in,out] meta denomination type data to complete + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +TEH_CONFIG_load_denom_data ( + const char *section_name, + struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta); + +#endif diff --git a/src/exchange/taler-exchange-httpd_get-keys.c b/src/exchange/taler-exchange-httpd_get-keys.c @@ -27,7 +27,9 @@ #include "exchange-database/preflight.h" #include "taler-exchange-httpd_get-config.h" #include "taler-exchange-httpd_get-keys.h" +#include "taler-exchange-httpd_secmod-helpers.h" #include "taler-exchange-httpd_responses.h" +#include "taler-exchange-httpd_configuration.h" #include "taler/taler_extensions.h" #include "exchange-database/event_listen.h" #include "exchange-database/event_listen_cancel.h" @@ -57,65 +59,6 @@ /** - * Information about a denomination on offer by the denomination helper. - */ -struct HelperDenomination -{ - - /** - * When will the helper start to use this key for signing? - */ - struct GNUNET_TIME_Timestamp start_time; - - /** - * For how long will the helper allow signing? 0 if - * the key was revoked or purged. - */ - struct GNUNET_TIME_Relative validity_duration; - - /** - * Hash of the full denomination key. - */ - struct TALER_DenominationHashP h_denom_pub; - - /** - * Signature over this key from the security module's key. - */ - struct TALER_SecurityModuleSignatureP sm_sig; - - /** - * The (full) public key. - */ - struct TALER_DenominationPublicKey denom_pub; - - /** - * Details depend on the @e denom_pub.cipher type. - */ - union - { - - /** - * Hash of the RSA key. - */ - struct TALER_RsaPubHashP h_rsa; - - /** - * Hash of the CS key. - */ - struct TALER_CsPubHashP h_cs; - - } h_details; - - /** - * Name in configuration section for this denomination type. - */ - char *section_name; - - -}; - - -/** * Signatures of an auditor over a denomination key of this exchange. */ struct TEH_AuditorSignature @@ -144,90 +87,6 @@ struct TEH_AuditorSignature /** - * Information about a signing key on offer by the esign helper. - */ -struct HelperSignkey -{ - /** - * When will the helper start to use this key for signing? - */ - struct GNUNET_TIME_Timestamp start_time; - - /** - * For how long will the helper allow signing? 0 if - * the key was revoked or purged. - */ - struct GNUNET_TIME_Relative validity_duration; - - /** - * The public key. - */ - struct TALER_ExchangePublicKeyP exchange_pub; - - /** - * Signature over this key from the security module's key. - */ - struct TALER_SecurityModuleSignatureP sm_sig; - -}; - - -/** - * State associated with the crypto helpers / security modules. NOT updated - * when the #key_generation is updated (instead constantly kept in sync - * whenever #TEH_keys_get_state() is called). - */ -struct HelperState -{ - - /** - * Handle for the esign/EdDSA helper. - */ - struct TALER_CRYPTO_ExchangeSignHelper *esh; - - /** - * Handle for the denom/RSA helper. - */ - struct TALER_CRYPTO_RsaDenominationHelper *rsadh; - - /** - * Handle for the denom/CS helper. - */ - struct TALER_CRYPTO_CsDenominationHelper *csdh; - - /** - * Map from H(denom_pub) to `struct HelperDenomination` entries. - */ - struct GNUNET_CONTAINER_MultiHashMap *denom_keys; - - /** - * Map from H(rsa_pub) to `struct HelperDenomination` entries. - */ - struct GNUNET_CONTAINER_MultiHashMap *rsa_keys; - - /** - * Map from H(cs_pub) to `struct HelperDenomination` entries. - */ - struct GNUNET_CONTAINER_MultiHashMap *cs_keys; - - /** - * Map from `struct TALER_ExchangePublicKey` to `struct HelperSignkey` - * entries. Based on the fact that a `struct GNUNET_PeerIdentity` is also - * an EdDSA public key. - */ - struct GNUNET_CONTAINER_MultiPeerMap *esign_keys; - -}; - - -/** - * Information we track for the crypto helpers. Preserved - * when the @e key_generation changes, thus kept separate. - */ -static struct HelperState helpers; - - -/** * Entry in (sorted) array with possible pre-build responses for /keys. * We keep pre-build responses for the various (valid) cherry-picking * values around. @@ -514,12 +373,6 @@ static struct GNUNET_DB_EventHandler *wire_eh; */ static uint64_t wire_generation; - -/** - * Stores the latest generation of our key state. - */ -static struct TEH_KeyStateHandle *key_state; - /** * Counter incremented whenever we have a reason to re-build the keys because * something external changed. See #TEH_keys_get_state() and @@ -528,6 +381,11 @@ static struct TEH_KeyStateHandle *key_state; static uint64_t key_generation; /** + * Stores the latest generation of our key state. + */ +static struct TEH_KeyStateHandle *key_state; + +/** * Handler listening for wire updates by other exchange * services. */ @@ -565,21 +423,6 @@ static struct GNUNET_TIME_Relative signkey_legal_duration; static char *asset_type; /** - * RSA security module public key, all zero if not known. - */ -static struct TALER_SecurityModulePublicKeyP denom_rsa_sm_pub; - -/** - * CS security module public key, all zero if not known. - */ -static struct TALER_SecurityModulePublicKeyP denom_cs_sm_pub; - -/** - * EdDSA security module public key, all zero if not known. - */ -static struct TALER_SecurityModulePublicKeyP esign_sm_pub; - -/** * Are we shutting down? */ static bool terminating; @@ -946,6 +789,13 @@ TEH_wire_update_state (void) } +void +TEH_keys_bump_generation (void) +{ + key_generation++; +} + + /** * Return the current key state for this thread. Possibly * re-builds the key state if we have reason to believe @@ -1189,487 +1039,6 @@ clear_response_cache (struct TEH_KeyStateHandle *ksh) /** - * Check that the given RSA security module's public key is the one - * we have pinned. If it does not match, we die hard. - * - * @param sm_pub RSA security module public key to check - */ -static void -check_denom_rsa_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub) -{ - if (0 != - GNUNET_memcmp (sm_pub, - &denom_rsa_sm_pub)) - { - if (! GNUNET_is_zero (&denom_rsa_sm_pub)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Our RSA security module changed its key. This must not happen.\n"); - GNUNET_assert (0); - } - denom_rsa_sm_pub = *sm_pub; /* TOFU ;-) */ - } -} - - -/** - * Check that the given CS security module's public key is the one - * we have pinned. If it does not match, we die hard. - * - * @param sm_pub RSA security module public key to check - */ -static void -check_denom_cs_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub) -{ - if (0 != - GNUNET_memcmp (sm_pub, - &denom_cs_sm_pub)) - { - if (! GNUNET_is_zero (&denom_cs_sm_pub)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Our CS security module changed its key. This must not happen.\n"); - GNUNET_assert (0); - } - denom_cs_sm_pub = *sm_pub; /* TOFU ;-) */ - } -} - - -/** - * Check that the given EdDSA security module's public key is the one - * we have pinned. If it does not match, we die hard. - * - * @param sm_pub EdDSA security module public key to check - */ -static void -check_esign_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub) -{ - if (0 != - GNUNET_memcmp (sm_pub, - &esign_sm_pub)) - { - if (! GNUNET_is_zero (&esign_sm_pub)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Our EdDSA security module changed its key. This must not happen.\n"); - GNUNET_assert (0); - } - esign_sm_pub = *sm_pub; /* TOFU ;-) */ - } -} - - -/** - * Helper function for #destroy_key_helpers to free all entries - * in the `denom_keys` map. - * - * @param cls the `struct HelperDenomination` - * @param h_denom_pub hash of the denomination public key - * @param value the `struct HelperDenomination` to release - * @return #GNUNET_OK (continue to iterate) - */ -static enum GNUNET_GenericReturnValue -free_denom_cb (void *cls, - const struct GNUNET_HashCode *h_denom_pub, - void *value) -{ - struct HelperDenomination *hd = value; - - (void) cls; - (void) h_denom_pub; - TALER_denom_pub_free (&hd->denom_pub); - GNUNET_free (hd->section_name); - GNUNET_free (hd); - return GNUNET_OK; -} - - -/** - * Helper function for #destroy_key_helpers to free all entries - * in the `esign_keys` map. - * - * @param cls the `struct HelperSignkey` - * @param pid unused, matches the exchange public key - * @param value the `struct HelperSignkey` to release - * @return #GNUNET_OK (continue to iterate) - */ -static enum GNUNET_GenericReturnValue -free_esign_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct HelperSignkey *hsk = value; - - (void) cls; - (void) pid; - GNUNET_free (hsk); - return GNUNET_OK; -} - - -/** - * Destroy helper state. Does NOT call free() on @a hs, as that - * state is not separately allocated! Dual to #setup_key_helpers(). - * - * @param[in] hs helper state to free, but NOT the @a hs pointer itself! - */ -static void -destroy_key_helpers (struct HelperState *hs) -{ - GNUNET_CONTAINER_multihashmap_iterate (hs->denom_keys, - &free_denom_cb, - hs); - GNUNET_CONTAINER_multihashmap_destroy (hs->rsa_keys); - hs->rsa_keys = NULL; - GNUNET_CONTAINER_multihashmap_destroy (hs->cs_keys); - hs->cs_keys = NULL; - GNUNET_CONTAINER_multihashmap_destroy (hs->denom_keys); - hs->denom_keys = NULL; - GNUNET_CONTAINER_multipeermap_iterate (hs->esign_keys, - &free_esign_cb, - hs); - GNUNET_CONTAINER_multipeermap_destroy (hs->esign_keys); - hs->esign_keys = NULL; - if (NULL != hs->rsadh) - { - TALER_CRYPTO_helper_rsa_disconnect (hs->rsadh); - hs->rsadh = NULL; - } - if (NULL != hs->csdh) - { - TALER_CRYPTO_helper_cs_disconnect (hs->csdh); - hs->csdh = NULL; - } - if (NULL != hs->esh) - { - TALER_CRYPTO_helper_esign_disconnect (hs->esh); - hs->esh = NULL; - } -} - - -/** - * Looks up the AGE_RESTRICTED setting for a denomination in the config and - * returns the age restriction (mask) accordingly. - * - * @param section_name Section in the configuration for the particular - * denomination. - */ -static struct TALER_AgeMask -load_age_mask (const char *section_name) -{ - static const struct TALER_AgeMask null_mask = {0}; - enum GNUNET_GenericReturnValue ret; - - if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( - TEH_cfg, - section_name, - "AGE_RESTRICTED"))) - return null_mask; - - if (GNUNET_SYSERR == - (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg, - section_name, - "AGE_RESTRICTED"))) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section_name, - "AGE_RESTRICTED", - "Value must be YES or NO\n"); - return null_mask; - } - - if (GNUNET_OK == ret) - { - if (! TEH_age_restriction_enabled) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "age restriction set in section %s, yet, age restriction is not enabled\n", - section_name); - return TEH_age_restriction_config.mask; - } - - - return null_mask; -} - - -/** - * Function called with information about available keys for signing. Usually - * only called once per key upon connect. Also called again in case a key is - * being revoked, in that case with an @a end_time of zero. - * - * @param cls closure with the `struct HelperState *` - * @param section_name name of the denomination type in the configuration; - * NULL if the key has been revoked or purged - * @param start_time when does the key become available for signing; - * zero if the key has been revoked or purged - * @param validity_duration how long does the key remain available for signing; - * zero if the key has been revoked or purged - * @param h_rsa hash of the @a denom_pub that is available (or was purged) - * @param bs_pub the public key itself, NULL if the key was revoked or purged - * @param sm_pub public key of the security module, NULL if the key was revoked or purged - * @param sm_sig signature from the security module - */ -static void -helper_rsa_cb ( - void *cls, - const char *section_name, - struct GNUNET_TIME_Timestamp start_time, - struct GNUNET_TIME_Relative validity_duration, - const struct TALER_RsaPubHashP *h_rsa, - struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, - const struct TALER_SecurityModulePublicKeyP *sm_pub, - const struct TALER_SecurityModuleSignatureP *sm_sig) -{ - struct HelperState *hs = cls; - struct HelperDenomination *hd; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "RSA helper announces key %s for denomination type %s with validity %s\n", - GNUNET_h2s (&h_rsa->hash), - section_name, - GNUNET_STRINGS_relative_time_to_string (validity_duration, - GNUNET_NO)); - key_generation++; - TEH_resume_keys_requests (false); - hd = GNUNET_CONTAINER_multihashmap_get (hs->rsa_keys, - &h_rsa->hash); - if (NULL != hd) - { - /* should be just an update (revocation!), so update existing entry */ - hd->validity_duration = validity_duration; - return; - } - GNUNET_assert (NULL != sm_pub); - check_denom_rsa_sm_pub (sm_pub); - hd = GNUNET_new (struct HelperDenomination); - hd->start_time = start_time; - hd->validity_duration = validity_duration; - hd->h_details.h_rsa = *h_rsa; - hd->sm_sig = *sm_sig; - GNUNET_assert (GNUNET_CRYPTO_BSA_RSA == bs_pub->cipher); - hd->denom_pub.bsign_pub_key = - GNUNET_CRYPTO_bsign_pub_incref (bs_pub); - /* load the age mask for the denomination, if applicable */ - hd->denom_pub.age_mask = load_age_mask (section_name); - TALER_denom_pub_hash (&hd->denom_pub, - &hd->h_denom_pub); - hd->section_name = GNUNET_strdup (section_name); - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put ( - hs->denom_keys, - &hd->h_denom_pub.hash, - hd, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put ( - hs->rsa_keys, - &hd->h_details.h_rsa.hash, - hd, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); -} - - -/** - * Function called with information about available CS keys for signing. Usually - * only called once per key upon connect. Also called again in case a key is - * being revoked, in that case with an @a end_time of zero. - * - * @param cls closure with the `struct HelperState *` - * @param section_name name of the denomination type in the configuration; - * NULL if the key has been revoked or purged - * @param start_time when does the key become available for signing; - * zero if the key has been revoked or purged - * @param validity_duration how long does the key remain available for signing; - * zero if the key has been revoked or purged - * @param h_cs hash of the @a denom_pub that is available (or was purged) - * @param bs_pub the public key itself, NULL if the key was revoked or purged - * @param sm_pub public key of the security module, NULL if the key was revoked or purged - * @param sm_sig signature from the security module - */ -static void -helper_cs_cb ( - void *cls, - const char *section_name, - struct GNUNET_TIME_Timestamp start_time, - struct GNUNET_TIME_Relative validity_duration, - const struct TALER_CsPubHashP *h_cs, - struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, - const struct TALER_SecurityModulePublicKeyP *sm_pub, - const struct TALER_SecurityModuleSignatureP *sm_sig) -{ - struct HelperState *hs = cls; - struct HelperDenomination *hd; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "CS helper announces key %s for denomination type %s with validity %s\n", - GNUNET_h2s (&h_cs->hash), - section_name, - GNUNET_STRINGS_relative_time_to_string (validity_duration, - GNUNET_NO)); - key_generation++; - TEH_resume_keys_requests (false); - hd = GNUNET_CONTAINER_multihashmap_get (hs->cs_keys, - &h_cs->hash); - if (NULL != hd) - { - /* should be just an update (revocation!), so update existing entry */ - hd->validity_duration = validity_duration; - return; - } - GNUNET_assert (NULL != sm_pub); - check_denom_cs_sm_pub (sm_pub); - hd = GNUNET_new (struct HelperDenomination); - hd->start_time = start_time; - hd->validity_duration = validity_duration; - hd->h_details.h_cs = *h_cs; - hd->sm_sig = *sm_sig; - GNUNET_assert (GNUNET_CRYPTO_BSA_CS == bs_pub->cipher); - hd->denom_pub.bsign_pub_key - = GNUNET_CRYPTO_bsign_pub_incref (bs_pub); - /* load the age mask for the denomination, if applicable */ - hd->denom_pub.age_mask = load_age_mask (section_name); - TALER_denom_pub_hash (&hd->denom_pub, - &hd->h_denom_pub); - hd->section_name = GNUNET_strdup (section_name); - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put ( - hs->denom_keys, - &hd->h_denom_pub.hash, - hd, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put ( - hs->cs_keys, - &hd->h_details.h_cs.hash, - hd, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); -} - - -/** - * Function called with information about available keys for signing. Usually - * only called once per key upon connect. Also called again in case a key is - * being revoked, in that case with an @a end_time of zero. - * - * @param cls closure with the `struct HelperState *` - * @param start_time when does the key become available for signing; - * zero if the key has been revoked or purged - * @param validity_duration how long does the key remain available for signing; - * zero if the key has been revoked or purged - * @param exchange_pub the public key itself, NULL if the key was revoked or purged - * @param sm_pub public key of the security module, NULL if the key was revoked or purged - * @param sm_sig signature from the security module - */ -static void -helper_esign_cb ( - void *cls, - struct GNUNET_TIME_Timestamp start_time, - struct GNUNET_TIME_Relative validity_duration, - const struct TALER_ExchangePublicKeyP *exchange_pub, - const struct TALER_SecurityModulePublicKeyP *sm_pub, - const struct TALER_SecurityModuleSignatureP *sm_sig) -{ - struct HelperState *hs = cls; - struct HelperSignkey *hsk; - struct GNUNET_PeerIdentity pid; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "EdDSA helper announces signing key %s with validity %s\n", - TALER_B2S (exchange_pub), - GNUNET_STRINGS_relative_time_to_string (validity_duration, - GNUNET_NO)); - key_generation++; - TEH_resume_keys_requests (false); - pid.public_key = exchange_pub->eddsa_pub; - hsk = GNUNET_CONTAINER_multipeermap_get (hs->esign_keys, - &pid); - if (NULL != hsk) - { - /* should be just an update (revocation!), so update existing entry */ - hsk->validity_duration = validity_duration; - return; - } - GNUNET_assert (NULL != sm_pub); - check_esign_sm_pub (sm_pub); - hsk = GNUNET_new (struct HelperSignkey); - hsk->start_time = start_time; - hsk->validity_duration = validity_duration; - hsk->exchange_pub = *exchange_pub; - hsk->sm_sig = *sm_sig; - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multipeermap_put ( - hs->esign_keys, - &pid, - hsk, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); -} - - -/** - * Setup helper state. - * - * @param[out] hs helper state to initialize - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -setup_key_helpers (struct HelperState *hs) -{ - hs->denom_keys - = GNUNET_CONTAINER_multihashmap_create (1024, - GNUNET_YES); - hs->rsa_keys - = GNUNET_CONTAINER_multihashmap_create (1024, - GNUNET_YES); - hs->cs_keys - = GNUNET_CONTAINER_multihashmap_create (1024, - GNUNET_YES); - hs->esign_keys - = GNUNET_CONTAINER_multipeermap_create (32, - GNUNET_NO /* MUST BE NO! */); - hs->rsadh = TALER_CRYPTO_helper_rsa_connect (TEH_cfg, - "taler-exchange", - &helper_rsa_cb, - hs); - if (NULL == hs->rsadh) - return GNUNET_SYSERR; - hs->csdh = TALER_CRYPTO_helper_cs_connect (TEH_cfg, - "taler-exchange", - &helper_cs_cb, - hs); - if (NULL == hs->csdh) - return GNUNET_SYSERR; - hs->esh = TALER_CRYPTO_helper_esign_connect (TEH_cfg, - "taler-exchange", - &helper_esign_cb, - hs); - if (NULL == hs->esh) - return GNUNET_SYSERR; - return GNUNET_OK; -} - - -/** - * Synchronize helper state. Polls the key helper for updates. - * - * @param[in,out] hs helper state to synchronize - */ -static void -sync_key_helpers (struct HelperState *hs) -{ - TALER_CRYPTO_helper_rsa_poll (hs->rsadh); - TALER_CRYPTO_helper_cs_poll (hs->csdh); - TALER_CRYPTO_helper_esign_poll (hs->esh); -} - - -/** * Free denomination key data. * * @param cls a `struct TEH_KeyStateHandle`, unused @@ -1792,9 +1161,9 @@ enum GNUNET_GenericReturnValue TEH_keys_init () { if (GNUNET_OK != - setup_key_helpers (&helpers)) + TEH_SECMOD_setup_key_helpers ()) { - destroy_key_helpers (&helpers); + TEH_SECMOD_destroy_key_helpers (); return GNUNET_SYSERR; } if (GNUNET_OK != @@ -1887,7 +1256,7 @@ TEH_keys_finished () keys_eh); keys_eh = NULL; } - destroy_key_helpers (&helpers); + TEH_SECMOD_destroy_key_helpers (); } @@ -2766,7 +2135,8 @@ append_signature (struct SignatureContext *sig_ctx, /** - *GroupData is the value we store for each group meta-data */ + * GroupData is the value we store for each group meta-data + */ struct GroupData { /** @@ -3042,18 +2412,11 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) /* Now that we have found/created the right group, add the denomination to the list */ { - struct HelperDenomination *hd; struct GNUNET_JSON_PackSpec key_spec; bool private_key_lost; - hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, - &dk->h_denom_pub.hash); private_key_lost - = (NULL == hd) || - GNUNET_TIME_absolute_is_past ( - GNUNET_TIME_absolute_add ( - hd->start_time.abs_time, - hd->validity_duration)); + = TEH_SECMOD_denom_priv_check_lost (&dk->h_denom_pub); switch (meta.cipher) { case GNUNET_CRYPTO_BSA_RSA: @@ -3397,7 +2760,7 @@ keys_get_state (bool management_only) destroy_key_state (old_ksh); return ksh; } - sync_key_helpers (&helpers); + TEH_SECMOD_sync_key_helpers (); return old_ksh; } @@ -3514,316 +2877,6 @@ TEH_keys_denomination_by_serial_from_state ( enum TALER_ErrorCode -TEH_keys_denomination_batch_sign ( - unsigned int csds_length, - const struct TEH_CoinSignData csds[static csds_length], - bool for_melt, - struct TALER_BlindedDenominationSignature bss[static csds_length]) -{ - struct TEH_KeyStateHandle *ksh; - struct HelperDenomination *hd; - struct TALER_CRYPTO_RsaSignRequest rsrs[csds_length]; - struct TALER_CRYPTO_CsSignRequest csrs[csds_length]; - struct TALER_BlindedDenominationSignature rs[csds_length]; - struct TALER_BlindedDenominationSignature cs[csds_length]; - unsigned int rsrs_pos = 0; - unsigned int csrs_pos = 0; - enum TALER_ErrorCode ec; - - ksh = TEH_keys_get_state (); - if (NULL == ksh) - return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; - for (unsigned int i = 0; i<csds_length; i++) - { - const struct TALER_DenominationHashP *h_denom_pub = csds[i].h_denom_pub; - const struct TALER_BlindedPlanchet *bp = csds[i].bp; - - hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, - &h_denom_pub->hash); - if (NULL == hd) - return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; - if (bp->blinded_message->cipher != - hd->denom_pub.bsign_pub_key->cipher) - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - switch (hd->denom_pub.bsign_pub_key->cipher) - { - case GNUNET_CRYPTO_BSA_RSA: - rsrs[rsrs_pos].h_rsa = &hd->h_details.h_rsa; - rsrs[rsrs_pos].msg - = bp->blinded_message->details.rsa_blinded_message.blinded_msg; - rsrs[rsrs_pos].msg_size - = bp->blinded_message->details.rsa_blinded_message.blinded_msg_size; - rsrs_pos++; - break; - case GNUNET_CRYPTO_BSA_CS: - csrs[csrs_pos].h_cs = &hd->h_details.h_cs; - csrs[csrs_pos].blinded_planchet - = &bp->blinded_message->details.cs_blinded_message; - csrs_pos++; - break; - default: - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - } - } - - if (0 != rsrs_pos) - { - memset (rs, - 0, - sizeof (rs)); - } - if (0 != csrs_pos) - { - memset (cs, - 0, - sizeof (cs)); - } - ec = TALER_EC_NONE; - if (0 != csrs_pos) - { - ec = TALER_CRYPTO_helper_cs_batch_sign ( - helpers.csdh, - csrs_pos, - csrs, - for_melt, - (0 == rsrs_pos) ? bss : cs); - if (TALER_EC_NONE != ec) - { - for (unsigned int i = 0; i<csrs_pos; i++) - TALER_blinded_denom_sig_free (&cs[i]); - return ec; - } - TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_CS] += csrs_pos; - } - if (0 != rsrs_pos) - { - ec = TALER_CRYPTO_helper_rsa_batch_sign ( - helpers.rsadh, - rsrs_pos, - rsrs, - (0 == csrs_pos) ? bss : rs); - if (TALER_EC_NONE != ec) - { - for (unsigned int i = 0; i<csrs_pos; i++) - TALER_blinded_denom_sig_free (&cs[i]); - for (unsigned int i = 0; i<rsrs_pos; i++) - TALER_blinded_denom_sig_free (&rs[i]); - return ec; - } - TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_RSA] += rsrs_pos; - } - - if ( (0 != csrs_pos) && - (0 != rsrs_pos) ) - { - rsrs_pos = 0; - csrs_pos = 0; - for (unsigned int i = 0; i<csds_length; i++) - { - const struct TALER_BlindedPlanchet *bp = csds[i].bp; - - switch (bp->blinded_message->cipher) - { - case GNUNET_CRYPTO_BSA_RSA: - bss[i] = rs[rsrs_pos++]; - break; - case GNUNET_CRYPTO_BSA_CS: - bss[i] = cs[csrs_pos++]; - break; - default: - GNUNET_assert (0); - } - } - } - return TALER_EC_NONE; -} - - -enum TALER_ErrorCode -TEH_keys_denomination_cs_r_pub ( - const struct TEH_CsDeriveData *cdd, - bool for_melt, - struct GNUNET_CRYPTO_CSPublicRPairP *r_pub) -{ - const struct TALER_DenominationHashP *h_denom_pub = cdd->h_denom_pub; - const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdd->nonce; - struct TEH_KeyStateHandle *ksh; - struct HelperDenomination *hd; - - ksh = TEH_keys_get_state (); - if (NULL == ksh) - { - return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; - } - hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, - &h_denom_pub->hash); - if (NULL == hd) - { - return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; - } - if (GNUNET_CRYPTO_BSA_CS != - hd->denom_pub.bsign_pub_key->cipher) - { - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - } - - { - struct TALER_CRYPTO_CsDeriveRequest cdr = { - .h_cs = &hd->h_details.h_cs, - .nonce = nonce - }; - return TALER_CRYPTO_helper_cs_r_derive (helpers.csdh, - &cdr, - for_melt, - r_pub); - } -} - - -enum TALER_ErrorCode -TEH_keys_denomination_cs_batch_r_pub_simple ( - unsigned int cdds_length, - const struct TEH_CsDeriveData cdds[static cdds_length], - bool for_melt, - struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static cdds_length]) -{ - struct TEH_KeyStateHandle *ksh; - struct HelperDenomination *hd; - struct TALER_CRYPTO_CsDeriveRequest cdrs[cdds_length]; - - ksh = TEH_keys_get_state (); - if (NULL == ksh) - { - return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; - } - for (unsigned int i = 0; i<cdds_length; i++) - { - const struct TALER_DenominationHashP *h_denom_pub = cdds[i].h_denom_pub; - const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdds[i].nonce; - - hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, - &h_denom_pub->hash); - if (NULL == hd) - { - return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; - } - if (GNUNET_CRYPTO_BSA_CS != - hd->denom_pub.bsign_pub_key->cipher) - { - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - } - cdrs[i].h_cs = &hd->h_details.h_cs; - cdrs[i].nonce = nonce; - } - - return TALER_CRYPTO_helper_cs_r_batch_derive (helpers.csdh, - cdds_length, - cdrs, - for_melt, - r_pubs); -} - - -enum TALER_ErrorCode -TEH_keys_denomination_cs_batch_r_pub ( - const struct TEH_KeyStateHandle *ksh, - size_t num, - const struct TALER_DenominationHashP h_denom_pubs[static num], - const struct GNUNET_CRYPTO_CsSessionNonce nonces[static num], - bool for_melt, - struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static num], - size_t *err_idx) -{ - struct TALER_CRYPTO_CsDeriveRequest cdrs[num]; - - for (unsigned int i = 0; i<num; i++) - { - const struct TEH_DenominationKey *dk; - const struct HelperDenomination *hd; - - *err_idx = i; - - /* FIXME: right now we need both, - * TEH_DenominationKey and HelperDenomination, - * because only TEH_DenominationKey has .recoup_possible - */ - hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, - &h_denom_pubs[i].hash); - dk = TEH_keys_denomination_by_hash_from_state (ksh, - &h_denom_pubs[i], - NULL, - NULL); - if (NULL == hd) - return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; - - GNUNET_assert (NULL != dk); - - if (GNUNET_CRYPTO_BSA_CS != - hd->denom_pub.bsign_pub_key->cipher) - return TALER_EC_EXCHANGE_GENERIC_INVALID_DENOMINATION_CIPHER_FOR_OPERATION - ; - - if (GNUNET_TIME_absolute_is_future (hd->start_time.abs_time)) - return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE; - - if (dk->recoup_possible) - return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED; - - if (GNUNET_TIME_relative_is_zero (hd->validity_duration)) - return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED; - - cdrs[i].h_cs = &hd->h_details.h_cs; - cdrs[i].nonce = &nonces[i]; - } - - return TALER_CRYPTO_helper_cs_r_batch_derive (helpers.csdh, - num, - cdrs, - for_melt, - r_pubs); -} - - -void -TEH_keys_denomination_revoke (const struct TALER_DenominationHashP *h_denom_pub) -{ - struct TEH_KeyStateHandle *ksh; - struct HelperDenomination *hd; - - ksh = TEH_keys_get_state (); - if (NULL == ksh) - { - GNUNET_break (0); - return; - } - hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, - &h_denom_pub->hash); - if (NULL == hd) - { - GNUNET_break (0); - return; - } - switch (hd->denom_pub.bsign_pub_key->cipher) - { - case GNUNET_CRYPTO_BSA_INVALID: - break; - case GNUNET_CRYPTO_BSA_RSA: - TALER_CRYPTO_helper_rsa_revoke (helpers.rsadh, - &hd->h_details.h_rsa); - TEH_keys_update_states (); - return; - case GNUNET_CRYPTO_BSA_CS: - TALER_CRYPTO_helper_cs_revoke (helpers.csdh, - &hd->h_details.h_cs); - TEH_keys_update_states (); - return; - } - GNUNET_break (0); - return; -} - - -enum TALER_ErrorCode TEH_keys_exchange_sign_ ( const struct GNUNET_CRYPTO_SignaturePurpose *purpose, struct TALER_ExchangePublicKeyP *pub, @@ -3858,10 +2911,9 @@ TEH_keys_exchange_sign2_ ( enum TALER_ErrorCode ec; TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_EDDSA]++; - ec = TALER_CRYPTO_helper_esign_sign_ (helpers.esh, - purpose, - pub, - sig); + ec = TEH_SECMOD_exchange_sign (purpose, + pub, + sig); if (TALER_EC_NONE != ec) return ec; { @@ -3891,23 +2943,6 @@ TEH_keys_exchange_sign2_ ( } -void -TEH_keys_exchange_revoke (const struct TALER_ExchangePublicKeyP *exchange_pub) -{ - struct TEH_KeyStateHandle *ksh; - - ksh = TEH_keys_get_state (); - if (NULL == ksh) - { - GNUNET_break (0); - return; - } - TALER_CRYPTO_helper_esign_revoke (helpers.esh, - exchange_pub); - TEH_keys_update_states (); -} - - /** * Comparator used for a binary search by cherry_pick_date for @a key in the * `struct KeysResponseData` array. See libc's qsort() and bsearch() functions. @@ -4050,164 +3085,6 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc, /** - * Load extension data, like fees, expiration times (!) and age restriction - * flags for the denomination type configured in section @a section_name. - * Before calling this function, the `start` and `validity_duration` times must - * already be initialized in @a meta. - * - * @param section_name section in the configuration to use - * @param[in,out] meta denomination type data to complete - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -load_extension_data (const char *section_name, - struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta) -{ - struct GNUNET_TIME_Relative deposit_duration; - struct GNUNET_TIME_Relative legal_duration; - - GNUNET_assert (! GNUNET_TIME_absolute_is_zero (meta->start.abs_time)); /* caller bug */ - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (TEH_cfg, - section_name, - "DURATION_SPEND", - &deposit_duration)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section_name, - "DURATION_SPEND"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (TEH_cfg, - section_name, - "DURATION_LEGAL", - &legal_duration)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section_name, - "DURATION_LEGAL"); - return GNUNET_SYSERR; - } - meta->expire_deposit - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (meta->expire_withdraw.abs_time, - deposit_duration)); - meta->expire_legal = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (meta->expire_deposit.abs_time, - legal_duration)); - if (GNUNET_OK != - TALER_config_get_amount (TEH_cfg, - section_name, - "VALUE", - &meta->value)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "Need amount for option `%s' in section `%s'\n", - "VALUE", - section_name); - return GNUNET_SYSERR; - } - if (0 != strcasecmp (TEH_currency, - meta->value.currency)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Need denomination value in section `%s' to use currency `%s'\n", - section_name, - TEH_currency); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_config_get_denom_fees (TEH_cfg, - TEH_currency, - section_name, - &meta->fees)) - return GNUNET_SYSERR; - meta->age_mask = load_age_mask (section_name); - return GNUNET_OK; -} - - -enum GNUNET_GenericReturnValue -TEH_keys_load_fees (struct TEH_KeyStateHandle *ksh, - const struct TALER_DenominationHashP *h_denom_pub, - struct TALER_DenominationPublicKey *denom_pub, - struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta) -{ - struct HelperDenomination *hd; - enum GNUNET_GenericReturnValue ok; - - hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, - &h_denom_pub->hash); - if (NULL == hd) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Denomination %s not known\n", - GNUNET_h2s (&h_denom_pub->hash)); - return GNUNET_NO; - } - meta->start = hd->start_time; - meta->expire_withdraw = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (meta->start.abs_time, - hd->validity_duration)); - ok = load_extension_data (hd->section_name, - meta); - if (GNUNET_OK == ok) - { - GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID != - hd->denom_pub.bsign_pub_key->cipher); - TALER_denom_pub_copy (denom_pub, - &hd->denom_pub); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No fees for `%s', voiding key\n", - hd->section_name); - memset (denom_pub, - 0, - sizeof (*denom_pub)); - } - return ok; -} - - -enum GNUNET_GenericReturnValue -TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub, - struct TALER_EXCHANGEDB_SignkeyMetaData *meta) -{ - struct TEH_KeyStateHandle *ksh; - struct HelperSignkey *hsk; - struct GNUNET_PeerIdentity pid; - - ksh = TEH_keys_get_state_for_management_only (); - if (NULL == ksh) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - pid.public_key = exchange_pub->eddsa_pub; - hsk = GNUNET_CONTAINER_multipeermap_get (helpers.esign_keys, - &pid); - if (NULL == hsk) - { - GNUNET_break (0); - return GNUNET_NO; - } - meta->start = hsk->start_time; - - meta->expire_sign = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (meta->start.abs_time, - hsk->validity_duration)); - meta->expire_legal = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (meta->expire_sign.abs_time, - signkey_legal_duration)); - return GNUNET_OK; -} - - -/** * Closure for #add_future_denomkey_cb and #add_future_signkey_cb. */ struct FutureBuilderContext @@ -4262,8 +3139,8 @@ add_future_denomkey_cb (void *cls, GNUNET_TIME_absolute_add (meta.start.abs_time, hd->validity_duration)); if (GNUNET_OK != - load_extension_data (hd->section_name, - &meta)) + TEH_CONFIG_load_denom_data (hd->section_name, + &meta)) { /* Woops, couldn't determine fee structure!? */ return GNUNET_OK; @@ -4363,56 +3240,50 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh, TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, "no key state"); } - sync_key_helpers (&helpers); + TEH_SECMOD_sync_key_helpers (); if (NULL == ksh->management_keys_reply) { struct FutureBuilderContext fbc = { .ksh = ksh, - .denoms = json_array (), - .signkeys = json_array () }; - GNUNET_assert (NULL != fbc.denoms); - GNUNET_assert (NULL != fbc.signkeys); - if ( (GNUNET_is_zero (&denom_rsa_sm_pub)) && - (GNUNET_is_zero (&denom_cs_sm_pub)) ) + if (! TEH_SECMOD_have_denom_sm_pub ()) { /* Either IPC failed, or neither helper had any denominations configured. */ - json_decref (fbc.denoms); - json_decref (fbc.signkeys); return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_GATEWAY, TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE, NULL); } - if (GNUNET_is_zero (&esign_sm_pub)) + if (! TEH_SECMOD_have_esign_sm_pub ()) { - json_decref (fbc.denoms); - json_decref (fbc.signkeys); return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_GATEWAY, TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE, NULL); } - GNUNET_CONTAINER_multihashmap_iterate (helpers.denom_keys, - &add_future_denomkey_cb, - &fbc); - GNUNET_CONTAINER_multipeermap_iterate (helpers.esign_keys, - &add_future_signkey_cb, - &fbc); + fbc.denoms = json_array (); + GNUNET_assert (NULL != fbc.denoms); + fbc.signkeys = json_array (); + GNUNET_assert (NULL != fbc.signkeys); + TEH_SECMOD_iterate_denom_keys (&add_future_denomkey_cb, + &fbc); + TEH_SECMOD_iterate_esign_keys (&add_future_signkey_cb, + &fbc); reply = GNUNET_JSON_PACK ( GNUNET_JSON_pack_array_steal ("future_denoms", fbc.denoms), GNUNET_JSON_pack_array_steal ("future_signkeys", fbc.signkeys), GNUNET_JSON_pack_data_auto ("master_pub", - &TEH_master_public_key), - GNUNET_JSON_pack_data_auto ("denom_secmod_public_key", - &denom_rsa_sm_pub), - GNUNET_JSON_pack_data_auto ("denom_secmod_cs_public_key", - &denom_cs_sm_pub), - GNUNET_JSON_pack_data_auto ("signkey_secmod_public_key", - &esign_sm_pub)); + &TEH_master_public_key)); + { + json_t *sj = TEH_SECMOD_get_sm_pubs_as_json (); + + json_object_update (reply, + sj); + json_decref (sj); + } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Returning GET /management/keys response:\n"); if (NULL == reply) diff --git a/src/exchange/taler-exchange-httpd_get-keys.h b/src/exchange/taler-exchange-httpd_get-keys.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020-2022 Taler Systems SA + Copyright (C) 2020-2022, 2026 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 @@ -286,129 +286,6 @@ TEH_keys_denomination_by_serial_from_state ( /** - * Information needed to create a blind signature. - */ -struct TEH_CoinSignData -{ - /** - * Hash of key to sign with. - */ - const struct TALER_DenominationHashP *h_denom_pub; - - /** - * Blinded planchet to sign over. - */ - const struct TALER_BlindedPlanchet *bp; -}; - - -/** - * Request to sign @a csds. - * - * @param csds array with data to blindly sign (and keys to sign with) - * @param csds_length length of @a csds array - * @param for_melt true if this is for a melt operation - * @param[out] bss array set to the blind signature on success; must be of length @a csds_length - * @return #TALER_EC_NONE on success - */ -enum TALER_ErrorCode -TEH_keys_denomination_batch_sign ( - unsigned int csds_length, - const struct TEH_CoinSignData csds[static csds_length], - bool for_melt, - struct TALER_BlindedDenominationSignature bss[static csds_length]); - - -/** - * Information needed to derive the CS r_pub. - */ -struct TEH_CsDeriveData -{ - /** - * Hash of key to sign with. - */ - const struct TALER_DenominationHashP *h_denom_pub; - - /** - * Nonce to use. - */ - const struct GNUNET_CRYPTO_CsSessionNonce *nonce; -}; - - -/** - * Request to derive CS @a r_pub using the denomination and nonce from @a cdd. - * - * @param cdd data to compute @a r_pub from - * @param for_melt true if this is for a melt operation - * @param[out] r_pub where to write the result - * @return #TALER_EC_NONE on success - */ -enum TALER_ErrorCode -TEH_keys_denomination_cs_r_pub ( - const struct TEH_CsDeriveData *cdd, - bool for_melt, - struct GNUNET_CRYPTO_CSPublicRPairP *r_pub); - - -/** - * Request to derive a bunch of CS @a r_pubs using the - * denominations and nonces from @a cdds. - * - * @param cdds array to compute @a r_pubs from - * @param cdds_length length of the @a cdds array - * @param for_melt true if this is for a melt operation - * @param[out] r_pubs array where to write the result; must be of length @a cdds_length - * @return #TALER_EC_NONE on success - */ -enum TALER_ErrorCode -TEH_keys_denomination_cs_batch_r_pub_simple ( - unsigned int cdds_length, - const struct TEH_CsDeriveData cdds[static cdds_length], - bool for_melt, - struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static cdds_length]); - - -/** - * Request to derive a bunch of CS @a r_pubs using the - * denominations and nonces from @a cdds. - * - * @param ksh keys state to load the keys from - * @param num number of input elements - * @param h_denom_pubs array @a num of hashes of keys to sign with - * @param nonces array @a num of nonces to use - * @param for_melt true if this is for a melt operation - * @param[out] r_pubs array where to write the result; must be of length @a num - * @param[out] err_idx in case of error, the index into @e cdds that caused it - * @return #TALER_EC_NONE on success - */ -enum TALER_ErrorCode -TEH_keys_denomination_cs_batch_r_pub ( - const struct TEH_KeyStateHandle *ksh, - size_t num, - const struct TALER_DenominationHashP h_denom_pubs[static num], - const struct GNUNET_CRYPTO_CsSessionNonce nonces[static num], - bool for_melt, - struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static num], - size_t *err_idx); - -/** - * Revoke the public key associated with @a h_denom_pub. - * This function should be called AFTER the database was - * updated, as it also triggers #TEH_keys_update_states(). - * - * Note that the actual revocation happens asynchronously and - * may thus fail silently. To verify that the revocation succeeded, - * clients must watch for the associated change to the key state. - * - * @param h_denom_pub hash of the public key to revoke - */ -void -TEH_keys_denomination_revoke ( - const struct TALER_DenominationHashP *h_denom_pub); - - -/** * Fully clean up keys subsystem. */ void @@ -529,21 +406,6 @@ TEH_keys_exchange_sign2_ ( /** - * Revoke the given exchange's signing key. - * This function should be called AFTER the database was - * updated, as it also triggers #TEH_keys_update_states(). - * - * Note that the actual revocation happens asynchronously and - * may thus fail silently. To verify that the revocation succeeded, - * clients must watch for the associated change to the key state. - * - * @param exchange_pub key to revoke - */ -void -TEH_keys_exchange_revoke (const struct TALER_ExchangePublicKeyP *exchange_pub); - - -/** * Function to call to handle requests to "/keys" by sending * back our current key material. * @@ -570,35 +432,11 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh, /** - * Load fees and expiration times (!) for the denomination type configured for - * the denomination matching @a h_denom_pub. - * - * @param ksh key state to load fees from - * @param h_denom_pub hash of the denomination public key - * to use to derive the section name of the configuration to use - * @param[out] denom_pub set to the denomination public key (to be freed by caller!) - * @param[out] meta denomination type data to complete - * @return #GNUNET_OK on success, - * #GNUNET_NO if @a h_denom_pub is not known - * #GNUNET_SYSERR on hard errors - */ -enum GNUNET_GenericReturnValue -TEH_keys_load_fees (struct TEH_KeyStateHandle *ksh, - const struct TALER_DenominationHashP *h_denom_pub, - struct TALER_DenominationPublicKey *denom_pub, - struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta); - - -/** - * Load expiration times for the given onling signing key. - * - * @param exchange_pub the online signing key - * @param[out] meta set to meta data about the key - * @return #GNUNET_OK on success + * Increment the key generation, something may have changed. + * Triggers re-building of /keys. */ -enum GNUNET_GenericReturnValue -TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub, - struct TALER_EXCHANGEDB_SignkeyMetaData *meta); +void +TEH_keys_bump_generation (void); /** diff --git a/src/exchange/taler-exchange-httpd_post-blinding-prepare.c b/src/exchange/taler-exchange-httpd_post-blinding-prepare.c @@ -25,6 +25,7 @@ #include <jansson.h> #include "taler/taler_json_lib.h" #include "taler/taler_mhd_lib.h" +#include "taler-exchange-httpd_secmod-helpers.h" #include "taler-exchange-httpd_post-blinding-prepare.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_get-keys.h" @@ -157,13 +158,12 @@ TEH_handler_blinding_prepare (struct TEH_RequestContext *rc, NULL); ec = - TEH_keys_denomination_cs_batch_r_pub (ksh, - num, - h_denom_pubs, - nonces, - is_melt, - r_pubs, - &err_idx); + TEH_SECMOD_denom_cs_batch_r_pub (num, + h_denom_pubs, + nonces, + is_melt, + r_pubs, + &err_idx); switch (ec) { case TALER_EC_NONE: diff --git a/src/exchange/taler-exchange-httpd_post-management-denominations-H_DENOM_PUB-revoke.c b/src/exchange/taler-exchange-httpd_post-management-denominations-H_DENOM_PUB-revoke.c @@ -25,6 +25,7 @@ #include <pthread.h> #include "taler/taler_json_lib.h" #include "taler/taler_mhd_lib.h" +#include "taler-exchange-httpd_secmod-helpers.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_get-keys.h" @@ -81,7 +82,7 @@ TEH_handler_management_denominations_HDP_revoke ( TALER_EC_GENERIC_DB_STORE_FAILED, "denomination revocation"); } - TEH_keys_denomination_revoke (h_denom_pub); + TEH_SECMOD_denom_revoke (h_denom_pub); return TALER_MHD_reply_static ( connection, MHD_HTTP_NO_CONTENT, diff --git a/src/exchange/taler-exchange-httpd_post-management-keys.c b/src/exchange/taler-exchange-httpd_post-management-keys.c @@ -26,6 +26,7 @@ #include "taler/taler_json_lib.h" #include "taler/taler_mhd_lib.h" #include "taler-exchange-httpd_get-keys.h" +#include "taler-exchange-httpd_secmod-helpers.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" #include "exchange-database/lookup_signing_key.h" @@ -424,10 +425,9 @@ TEH_handler_management_post_keys ( return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } - res = TEH_keys_load_fees (akc.ksh, - &d->h_denom_pub, - &d->denom_pub, - &d->meta); + res = TEH_SECMOD_denom_load_meta (&d->h_denom_pub, + &d->denom_pub, + &d->meta); switch (res) { case GNUNET_SYSERR: @@ -500,8 +500,8 @@ TEH_handler_management_post_keys ( cleanup_akc (&akc); return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } - res = TEH_keys_get_timing (&s->exchange_pub, - &s->meta); + res = TEH_SECMOD_esign_load_meta (&s->exchange_pub, + &s->meta); switch (res) { case GNUNET_SYSERR: diff --git a/src/exchange/taler-exchange-httpd_post-melt.c b/src/exchange/taler-exchange-httpd_post-melt.c @@ -29,6 +29,7 @@ #include "taler-exchange-httpd_post-melt.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_get-keys.h" +#include "taler-exchange-httpd_secmod-helpers.h" #include "taler/taler_util.h" #include "exchange-database/do_refresh.h" #include "exchange-database/get_coin_denomination.h" @@ -1140,7 +1141,7 @@ phase_prepare_transaction ( /* Choose and sign the coins */ { - struct TEH_CoinSignData csds[mc->request.refresh.num_coins]; + struct TEH_SECMOD_CoinSignData csds[mc->request.refresh.num_coins]; enum TALER_ErrorCode ec_denomination_sign; size_t noreveal_idx = mc->request.refresh.noreveal_index; @@ -1155,7 +1156,7 @@ phase_prepare_transaction ( csds[i].h_denom_pub = &mc->request.denoms_h[i]; } - ec_denomination_sign = TEH_keys_denomination_batch_sign ( + ec_denomination_sign = TEH_SECMOD_denom_batch_sign ( mc->request.refresh.num_coins, csds, true, /* for melt */ @@ -1187,7 +1188,7 @@ phase_prepare_transaction ( if (0 < mc->request.refresh.num_cs_r_values) { size_t num_cs_r_values = mc->request.refresh.num_cs_r_values; - struct TEH_CsDeriveData cdds[num_cs_r_values]; + struct TEH_SECMOD_CsDeriveData cdds[num_cs_r_values]; struct GNUNET_CRYPTO_CsSessionNonce nonces[num_cs_r_values]; memset (nonces, @@ -1220,7 +1221,7 @@ phase_prepare_transaction ( * make the choices */ if (TALER_EC_NONE != - TEH_keys_denomination_cs_batch_r_pub_simple ( + TEH_SECMOD_denom_cs_batch_r_pub_simple ( mc->request.refresh.num_cs_r_values, cdds, true, /* for melt */ diff --git a/src/exchange/taler-exchange-httpd_post-withdraw.c b/src/exchange/taler-exchange-httpd_post-withdraw.c @@ -34,6 +34,7 @@ #include "taler-exchange-httpd_common_kyc.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_get-keys.h" +#include "taler-exchange-httpd_secmod-helpers.h" #include "taler/taler_util.h" #include "exchange-database/do_withdraw.h" #include "exchange-database/get_withdraw.h" @@ -585,7 +586,7 @@ phase_prepare_transaction ( /* Choose and sign the coins */ { - struct TEH_CoinSignData csds[wc->request.withdraw.num_coins]; + struct TEH_SECMOD_CoinSignData csds[wc->request.withdraw.num_coins]; enum TALER_ErrorCode ec_denomination_sign; memset (csds, @@ -599,7 +600,7 @@ phase_prepare_transaction ( csds[i].h_denom_pub = &wc->request.denoms_h[i]; } - ec_denomination_sign = TEH_keys_denomination_batch_sign ( + ec_denomination_sign = TEH_SECMOD_denom_batch_sign ( wc->request.withdraw.num_coins, csds, false, @@ -627,7 +628,7 @@ phase_prepare_transaction ( if (0 < wc->request.withdraw.num_cs_r_values) { size_t num_cs_r_values = wc->request.withdraw.num_cs_r_values; - struct TEH_CsDeriveData cdds[num_cs_r_values]; + struct TEH_SECMOD_CsDeriveData cdds[num_cs_r_values]; struct GNUNET_CRYPTO_CsSessionNonce nonces[num_cs_r_values]; memset (nonces, @@ -660,7 +661,7 @@ phase_prepare_transaction ( * Let the crypto helper generate the R-values and make the choices. */ if (TALER_EC_NONE != - TEH_keys_denomination_cs_batch_r_pub_simple ( + TEH_SECMOD_denom_cs_batch_r_pub_simple ( wc->request.withdraw.num_cs_r_values, cdds, false, diff --git a/src/exchange/taler-exchange-httpd_secmod-helpers.c b/src/exchange/taler-exchange-httpd_secmod-helpers.c @@ -0,0 +1,966 @@ +/* + This file is part of TALER + Copyright (C) 2020-2026 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 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 <http://www.gnu.org/licenses/> + */ +/** + * @file taler-exchange-httpd_secmod-helpers.c + * @brief management of state with secmod helpers + * @author Christian Grothoff + * @author Özgür Kesim + */ +#include "taler/taler_json_lib.h" +#include "taler/taler_mhd_lib.h" +#include "taler-exchange-httpd.h" +#include "taler-exchange-httpd_get-keys.h" +#include "taler-exchange-httpd_secmod-helpers.h" +#include "taler-exchange-httpd_configuration.h" + + +/** + * For how long should a signing key be legally retained? + * Configuration value. + */ +static struct GNUNET_TIME_Relative signkey_legal_duration; + +/** + * Handle for the esign/EdDSA helper. + */ +static struct TALER_CRYPTO_ExchangeSignHelper *esh; + +/** + * Handle for the denom/RSA helper. + */ +static struct TALER_CRYPTO_RsaDenominationHelper *rsadh; + +/** + * Handle for the denom/CS helper. + */ +static struct TALER_CRYPTO_CsDenominationHelper *csdh; + +/** + * Map from H(denom_pub) to `struct HelperDenomination` entries. + */ +static struct GNUNET_CONTAINER_MultiHashMap *denom_keys; + +/** + * Map from H(rsa_pub) to `struct HelperDenomination` entries. + */ +static struct GNUNET_CONTAINER_MultiHashMap *rsa_keys; + +/** + * Map from H(cs_pub) to `struct HelperDenomination` entries. + */ +static struct GNUNET_CONTAINER_MultiHashMap *cs_keys; + +/** + * Map from `struct TALER_ExchangePublicKey` to `struct HelperSignkey` + * entries. Based on the fact that a `struct GNUNET_PeerIdentity` is also + * an EdDSA public key. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *esign_keys; + +/** + * RSA security module public key, all zero if not known. + */ +static struct TALER_SecurityModulePublicKeyP denom_rsa_sm_pub; + +/** + * CS security module public key, all zero if not known. + */ +static struct TALER_SecurityModulePublicKeyP denom_cs_sm_pub; + +/** + * EdDSA security module public key, all zero if not known. + */ +static struct TALER_SecurityModulePublicKeyP esign_sm_pub; + + +bool +TEH_SECMOD_have_denom_sm_pub () +{ + return + (! GNUNET_is_zero (&denom_rsa_sm_pub)) || + (! GNUNET_is_zero (&denom_cs_sm_pub)); +} + + +bool +TEH_SECMOD_have_esign_sm_pub () +{ + return ! GNUNET_is_zero (&esign_sm_pub); +} + + +json_t * +TEH_SECMOD_get_sm_pubs_as_json () +{ + return GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("denom_secmod_public_key", + &denom_rsa_sm_pub), + GNUNET_JSON_pack_data_auto ("denom_secmod_cs_public_key", + &denom_cs_sm_pub), + GNUNET_JSON_pack_data_auto ("signkey_secmod_public_key", + &esign_sm_pub)); +} + + +/** + * Check that the given RSA security module's public key is the one + * we have pinned. If it does not match, we die hard. + * + * @param sm_pub RSA security module public key to check + */ +static void +check_denom_rsa_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub) +{ + if (0 != + GNUNET_memcmp (sm_pub, + &denom_rsa_sm_pub)) + { + if (! GNUNET_is_zero (&denom_rsa_sm_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Our RSA security module changed its key. This must not happen.\n"); + GNUNET_assert (0); + } + denom_rsa_sm_pub = *sm_pub; /* TOFU ;-) */ + } +} + + +/** + * Function called with information about available keys for signing. Usually + * only called once per key upon connect. Also called again in case a key is + * being revoked, in that case with an @a end_time of zero. + * + * @param cls NULL + * @param section_name name of the denomination type in the configuration; + * NULL if the key has been revoked or purged + * @param start_time when does the key become available for signing; + * zero if the key has been revoked or purged + * @param validity_duration how long does the key remain available for signing; + * zero if the key has been revoked or purged + * @param h_rsa hash of the @a denom_pub that is available (or was purged) + * @param bs_pub the public key itself, NULL if the key was revoked or purged + * @param sm_pub public key of the security module, NULL if the key was revoked or purged + * @param sm_sig signature from the security module + */ +static void +helper_rsa_cb ( + void *cls, + const char *section_name, + struct GNUNET_TIME_Timestamp start_time, + struct GNUNET_TIME_Relative validity_duration, + const struct TALER_RsaPubHashP *h_rsa, + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, + const struct TALER_SecurityModulePublicKeyP *sm_pub, + const struct TALER_SecurityModuleSignatureP *sm_sig) +{ + struct HelperDenomination *hd; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "RSA helper announces key %s for denomination type %s with validity %s\n", + GNUNET_h2s (&h_rsa->hash), + section_name, + GNUNET_STRINGS_relative_time_to_string (validity_duration, + GNUNET_NO)); + TEH_keys_bump_generation (); + TEH_resume_keys_requests (false); + hd = GNUNET_CONTAINER_multihashmap_get (rsa_keys, + &h_rsa->hash); + if (NULL != hd) + { + /* should be just an update (revocation!), so update existing entry */ + hd->validity_duration = validity_duration; + return; + } + GNUNET_assert (NULL != sm_pub); + check_denom_rsa_sm_pub (sm_pub); + hd = GNUNET_new (struct HelperDenomination); + hd->start_time = start_time; + hd->validity_duration = validity_duration; + hd->h_details.h_rsa = *h_rsa; + hd->sm_sig = *sm_sig; + GNUNET_assert (GNUNET_CRYPTO_BSA_RSA == bs_pub->cipher); + hd->denom_pub.bsign_pub_key = + GNUNET_CRYPTO_bsign_pub_incref (bs_pub); + /* load the age mask for the denomination, if applicable */ + hd->denom_pub.age_mask = TEH_CONFIG_load_age_mask (section_name); + TALER_denom_pub_hash (&hd->denom_pub, + &hd->h_denom_pub); + hd->section_name = GNUNET_strdup (section_name); + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + denom_keys, + &hd->h_denom_pub.hash, + hd, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + rsa_keys, + &hd->h_details.h_rsa.hash, + hd, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); +} + + +/** + * Check that the given CS security module's public key is the one + * we have pinned. If it does not match, we die hard. + * + * @param sm_pub RSA security module public key to check + */ +static void +check_denom_cs_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub) +{ + if (0 != + GNUNET_memcmp (sm_pub, + &denom_cs_sm_pub)) + { + if (! GNUNET_is_zero (&denom_cs_sm_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Our CS security module changed its key. This must not happen.\n"); + GNUNET_assert (0); + } + denom_cs_sm_pub = *sm_pub; /* TOFU ;-) */ + } +} + + +/** + * Function called with information about available CS keys for signing. Usually + * only called once per key upon connect. Also called again in case a key is + * being revoked, in that case with an @a end_time of zero. + * + * @param cls NULL + * @param section_name name of the denomination type in the configuration; + * NULL if the key has been revoked or purged + * @param start_time when does the key become available for signing; + * zero if the key has been revoked or purged + * @param validity_duration how long does the key remain available for signing; + * zero if the key has been revoked or purged + * @param h_cs hash of the @a denom_pub that is available (or was purged) + * @param bs_pub the public key itself, NULL if the key was revoked or purged + * @param sm_pub public key of the security module, NULL if the key was revoked or purged + * @param sm_sig signature from the security module + */ +static void +helper_cs_cb ( + void *cls, + const char *section_name, + struct GNUNET_TIME_Timestamp start_time, + struct GNUNET_TIME_Relative validity_duration, + const struct TALER_CsPubHashP *h_cs, + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, + const struct TALER_SecurityModulePublicKeyP *sm_pub, + const struct TALER_SecurityModuleSignatureP *sm_sig) +{ + struct HelperDenomination *hd; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CS helper announces key %s for denomination type %s with validity %s\n", + GNUNET_h2s (&h_cs->hash), + section_name, + GNUNET_STRINGS_relative_time_to_string (validity_duration, + GNUNET_NO)); + TEH_keys_bump_generation (); + TEH_resume_keys_requests (false); + hd = GNUNET_CONTAINER_multihashmap_get (cs_keys, + &h_cs->hash); + if (NULL != hd) + { + /* should be just an update (revocation!), so update existing entry */ + hd->validity_duration = validity_duration; + return; + } + GNUNET_assert (NULL != sm_pub); + check_denom_cs_sm_pub (sm_pub); + hd = GNUNET_new (struct HelperDenomination); + hd->start_time = start_time; + hd->validity_duration = validity_duration; + hd->h_details.h_cs = *h_cs; + hd->sm_sig = *sm_sig; + GNUNET_assert (GNUNET_CRYPTO_BSA_CS == bs_pub->cipher); + hd->denom_pub.bsign_pub_key + = GNUNET_CRYPTO_bsign_pub_incref (bs_pub); + /* load the age mask for the denomination, if applicable */ + hd->denom_pub.age_mask = TEH_CONFIG_load_age_mask (section_name); + TALER_denom_pub_hash (&hd->denom_pub, + &hd->h_denom_pub); + hd->section_name = GNUNET_strdup (section_name); + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + denom_keys, + &hd->h_denom_pub.hash, + hd, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + cs_keys, + &hd->h_details.h_cs.hash, + hd, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); +} + + +/** + * Check that the given EdDSA security module's public key is the one + * we have pinned. If it does not match, we die hard. + * + * @param sm_pub EdDSA security module public key to check + */ +static void +check_esign_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub) +{ + if (0 != + GNUNET_memcmp (sm_pub, + &esign_sm_pub)) + { + if (! GNUNET_is_zero (&esign_sm_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Our EdDSA security module changed its key. This must not happen.\n"); + GNUNET_assert (0); + } + esign_sm_pub = *sm_pub; /* TOFU ;-) */ + } +} + + +/** + * Function called with information about available keys for signing. Usually + * only called once per key upon connect. Also called again in case a key is + * being revoked, in that case with an @a end_time of zero. + * + * @param cls NULL + * @param start_time when does the key become available for signing; + * zero if the key has been revoked or purged + * @param validity_duration how long does the key remain available for signing; + * zero if the key has been revoked or purged + * @param exchange_pub the public key itself, NULL if the key was revoked or purged + * @param sm_pub public key of the security module, NULL if the key was revoked or purged + * @param sm_sig signature from the security module + */ +static void +helper_esign_cb ( + void *cls, + struct GNUNET_TIME_Timestamp start_time, + struct GNUNET_TIME_Relative validity_duration, + const struct TALER_ExchangePublicKeyP *exchange_pub, + const struct TALER_SecurityModulePublicKeyP *sm_pub, + const struct TALER_SecurityModuleSignatureP *sm_sig) +{ + struct HelperSignkey *hsk; + struct GNUNET_PeerIdentity pid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "EdDSA helper announces signing key %s with validity %s\n", + TALER_B2S (exchange_pub), + GNUNET_STRINGS_relative_time_to_string (validity_duration, + GNUNET_NO)); + TEH_keys_bump_generation (); + TEH_resume_keys_requests (false); + pid.public_key = exchange_pub->eddsa_pub; + hsk = GNUNET_CONTAINER_multipeermap_get (esign_keys, + &pid); + if (NULL != hsk) + { + /* should be just an update (revocation!), so update existing entry */ + hsk->validity_duration = validity_duration; + return; + } + GNUNET_assert (NULL != sm_pub); + check_esign_sm_pub (sm_pub); + hsk = GNUNET_new (struct HelperSignkey); + hsk->start_time = start_time; + hsk->validity_duration = validity_duration; + hsk->exchange_pub = *exchange_pub; + hsk->sm_sig = *sm_sig; + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multipeermap_put ( + esign_keys, + &pid, + hsk, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); +} + + +enum GNUNET_GenericReturnValue +TEH_SECMOD_setup_key_helpers () +{ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (TEH_cfg, + "exchange", + "SIGNKEY_LEGAL_DURATION", + &signkey_legal_duration)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "SIGNKEY_LEGAL_DURATION"); + return GNUNET_SYSERR; + } + denom_keys + = GNUNET_CONTAINER_multihashmap_create (1024, + GNUNET_YES); + rsa_keys + = GNUNET_CONTAINER_multihashmap_create (1024, + GNUNET_YES); + cs_keys + = GNUNET_CONTAINER_multihashmap_create (1024, + GNUNET_YES); + esign_keys + = GNUNET_CONTAINER_multipeermap_create (32, + GNUNET_NO /* MUST BE NO! */); + rsadh = TALER_CRYPTO_helper_rsa_connect (TEH_cfg, + "taler-exchange", + &helper_rsa_cb, + NULL); + if (NULL == rsadh) + return GNUNET_SYSERR; + csdh = TALER_CRYPTO_helper_cs_connect (TEH_cfg, + "taler-exchange", + &helper_cs_cb, + NULL); + if (NULL == csdh) + return GNUNET_SYSERR; + esh = TALER_CRYPTO_helper_esign_connect (TEH_cfg, + "taler-exchange", + &helper_esign_cb, + NULL); + if (NULL == esh) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +void +TEH_SECMOD_sync_key_helpers (void) +{ + TALER_CRYPTO_helper_rsa_poll (rsadh); + TALER_CRYPTO_helper_cs_poll (csdh); + TALER_CRYPTO_helper_esign_poll (esh); +} + + +/** + * Helper function for #TEH_SECMOD_destroy_key_helpers to free all entries + * in the `denom_keys` map. + * + * @param cls NULL + * @param h_denom_pub hash of the denomination public key + * @param value the `struct HelperDenomination` to release + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +free_denom_cb (void *cls, + const struct GNUNET_HashCode *h_denom_pub, + void *value) +{ + struct HelperDenomination *hd = value; + + (void) cls; + (void) h_denom_pub; + TALER_denom_pub_free (&hd->denom_pub); + GNUNET_free (hd->section_name); + GNUNET_free (hd); + return GNUNET_OK; +} + + +/** + * Helper function for #TEH_SECMOD_destroy_key_helpers to free all entries + * in the `esign_keys` map. + * + * @param cls NULL + * @param pid unused, matches the exchange public key + * @param value the `struct HelperSignkey` to release + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +free_esign_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct HelperSignkey *hsk = value; + + (void) cls; + (void) pid; + GNUNET_free (hsk); + return GNUNET_OK; +} + + +void +TEH_SECMOD_destroy_key_helpers () +{ + GNUNET_CONTAINER_multihashmap_iterate (denom_keys, + &free_denom_cb, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (rsa_keys); + rsa_keys = NULL; + GNUNET_CONTAINER_multihashmap_destroy (cs_keys); + cs_keys = NULL; + GNUNET_CONTAINER_multihashmap_destroy (denom_keys); + denom_keys = NULL; + GNUNET_CONTAINER_multipeermap_iterate (esign_keys, + &free_esign_cb, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (esign_keys); + esign_keys = NULL; + if (NULL != rsadh) + { + TALER_CRYPTO_helper_rsa_disconnect (rsadh); + rsadh = NULL; + } + if (NULL != csdh) + { + TALER_CRYPTO_helper_cs_disconnect (csdh); + csdh = NULL; + } + if (NULL != esh) + { + TALER_CRYPTO_helper_esign_disconnect (esh); + esh = NULL; + } +} + + +void +TEH_SECMOD_iterate_denom_keys ( + GNUNET_CONTAINER_MultiHashMapIteratorCallback cb, + void *cb_cls) +{ + GNUNET_CONTAINER_multihashmap_iterate (denom_keys, + cb, + cb_cls); +} + + +void +TEH_SECMOD_iterate_esign_keys ( + GNUNET_CONTAINER_PeerMapIterator cb, + void *cb_cls) +{ + GNUNET_CONTAINER_multipeermap_iterate (esign_keys, + cb, + cb_cls); + +} + + +// FIXME: rename! +enum TALER_ErrorCode +TEH_SECMOD_denom_cs_compute_r_pub ( + const struct TEH_SECMOD_CsDeriveData *cdd, + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP *r_pub) +{ + const struct TALER_DenominationHashP *h_denom_pub = cdd->h_denom_pub; + const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdd->nonce; + struct HelperDenomination *hd; + + hd = GNUNET_CONTAINER_multihashmap_get (denom_keys, + &h_denom_pub->hash); + if (NULL == hd) + { + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; + } + if (GNUNET_CRYPTO_BSA_CS != + hd->denom_pub.bsign_pub_key->cipher) + { + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } + + { + struct TALER_CRYPTO_CsDeriveRequest cdr = { + .h_cs = &hd->h_details.h_cs, + .nonce = nonce + }; + return TALER_CRYPTO_helper_cs_r_derive (csdh, + &cdr, + for_melt, + r_pub); + } +} + + +// FIXME: rename! +enum TALER_ErrorCode +TEH_SECMOD_denom_cs_batch_r_pub_simple ( + unsigned int cdds_length, + const struct TEH_SECMOD_CsDeriveData cdds[static cdds_length], + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static cdds_length]) +{ + struct HelperDenomination *hd; + struct TALER_CRYPTO_CsDeriveRequest cdrs[cdds_length]; + + for (unsigned int i = 0; i<cdds_length; i++) + { + const struct TALER_DenominationHashP *h_denom_pub = cdds[i].h_denom_pub; + const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdds[i].nonce; + + hd = GNUNET_CONTAINER_multihashmap_get (denom_keys, + &h_denom_pub->hash); + if (NULL == hd) + { + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; + } + if (GNUNET_CRYPTO_BSA_CS != + hd->denom_pub.bsign_pub_key->cipher) + { + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } + cdrs[i].h_cs = &hd->h_details.h_cs; + cdrs[i].nonce = nonce; + } + + return TALER_CRYPTO_helper_cs_r_batch_derive (csdh, + cdds_length, + cdrs, + for_melt, + r_pubs); +} + + +// FIXME: rename! +enum TALER_ErrorCode +TEH_SECMOD_denom_cs_batch_r_pub ( + size_t num, + const struct TALER_DenominationHashP h_denom_pubs[static num], + const struct GNUNET_CRYPTO_CsSessionNonce nonces[static num], + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static num], + size_t *err_idx) +{ + struct TALER_CRYPTO_CsDeriveRequest cdrs[num]; + + for (unsigned int i = 0; i<num; i++) + { + const struct TEH_DenominationKey *dk; + const struct HelperDenomination *hd; + + *err_idx = i; + + /* FIXME: right now we need both, + * TEH_DenominationKey and HelperDenomination, + * because only TEH_DenominationKey has .recoup_possible + */ + hd = GNUNET_CONTAINER_multihashmap_get (denom_keys, + &h_denom_pubs[i].hash); + dk = TEH_keys_denomination_by_hash (&h_denom_pubs[i], + NULL, + NULL); + if (NULL == hd) + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; + + GNUNET_assert (NULL != dk); + + if (GNUNET_CRYPTO_BSA_CS != + hd->denom_pub.bsign_pub_key->cipher) + return TALER_EC_EXCHANGE_GENERIC_INVALID_DENOMINATION_CIPHER_FOR_OPERATION + ; + + if (GNUNET_TIME_absolute_is_future (hd->start_time.abs_time)) + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE; + if (dk->recoup_possible) + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED; + if (GNUNET_TIME_relative_is_zero (hd->validity_duration)) + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED; + + cdrs[i].h_cs = &hd->h_details.h_cs; + cdrs[i].nonce = &nonces[i]; + } + + return TALER_CRYPTO_helper_cs_r_batch_derive (csdh, + num, + cdrs, + for_melt, + r_pubs); +} + + +enum TALER_ErrorCode +TEH_SECMOD_denom_batch_sign ( + unsigned int csds_length, + const struct TEH_SECMOD_CoinSignData csds[static csds_length], + bool for_melt, + struct TALER_BlindedDenominationSignature bss[static csds_length]) +{ + struct HelperDenomination *hd; + struct TALER_CRYPTO_RsaSignRequest rsrs[csds_length]; + struct TALER_CRYPTO_CsSignRequest csrs[csds_length]; + struct TALER_BlindedDenominationSignature rs[csds_length]; + struct TALER_BlindedDenominationSignature cs[csds_length]; + unsigned int rsrs_pos = 0; + unsigned int csrs_pos = 0; + enum TALER_ErrorCode ec; + + for (unsigned int i = 0; i<csds_length; i++) + { + const struct TALER_DenominationHashP *h_denom_pub = csds[i].h_denom_pub; + const struct TALER_BlindedPlanchet *bp = csds[i].bp; + + hd = GNUNET_CONTAINER_multihashmap_get (denom_keys, + &h_denom_pub->hash); + if (NULL == hd) + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; + if (bp->blinded_message->cipher != + hd->denom_pub.bsign_pub_key->cipher) + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + switch (hd->denom_pub.bsign_pub_key->cipher) + { + case GNUNET_CRYPTO_BSA_RSA: + rsrs[rsrs_pos].h_rsa = &hd->h_details.h_rsa; + rsrs[rsrs_pos].msg + = bp->blinded_message->details.rsa_blinded_message.blinded_msg; + rsrs[rsrs_pos].msg_size + = bp->blinded_message->details.rsa_blinded_message.blinded_msg_size; + rsrs_pos++; + break; + case GNUNET_CRYPTO_BSA_CS: + csrs[csrs_pos].h_cs = &hd->h_details.h_cs; + csrs[csrs_pos].blinded_planchet + = &bp->blinded_message->details.cs_blinded_message; + csrs_pos++; + break; + default: + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } + } + + if (0 != rsrs_pos) + { + memset (rs, + 0, + sizeof (rs)); + } + if (0 != csrs_pos) + { + memset (cs, + 0, + sizeof (cs)); + } + ec = TALER_EC_NONE; + if (0 != csrs_pos) + { + ec = TALER_CRYPTO_helper_cs_batch_sign ( + csdh, + csrs_pos, + csrs, + for_melt, + (0 == rsrs_pos) ? bss : cs); + if (TALER_EC_NONE != ec) + { + for (unsigned int i = 0; i<csrs_pos; i++) + TALER_blinded_denom_sig_free (&cs[i]); + return ec; + } + TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_CS] += csrs_pos; + } + if (0 != rsrs_pos) + { + ec = TALER_CRYPTO_helper_rsa_batch_sign ( + rsadh, + rsrs_pos, + rsrs, + (0 == csrs_pos) ? bss : rs); + if (TALER_EC_NONE != ec) + { + for (unsigned int i = 0; i<csrs_pos; i++) + TALER_blinded_denom_sig_free (&cs[i]); + for (unsigned int i = 0; i<rsrs_pos; i++) + TALER_blinded_denom_sig_free (&rs[i]); + return ec; + } + TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_RSA] += rsrs_pos; + } + + if ( (0 != csrs_pos) && + (0 != rsrs_pos) ) + { + rsrs_pos = 0; + csrs_pos = 0; + for (unsigned int i = 0; i<csds_length; i++) + { + const struct TALER_BlindedPlanchet *bp = csds[i].bp; + + switch (bp->blinded_message->cipher) + { + case GNUNET_CRYPTO_BSA_RSA: + bss[i] = rs[rsrs_pos++]; + break; + case GNUNET_CRYPTO_BSA_CS: + bss[i] = cs[csrs_pos++]; + break; + default: + GNUNET_assert (0); + } + } + } + return TALER_EC_NONE; +} + + +enum TALER_ErrorCode +TEH_SECMOD_exchange_sign ( + const struct GNUNET_CRYPTO_SignaturePurpose *purpose, + struct TALER_ExchangePublicKeyP *pub, + struct TALER_ExchangeSignatureP *sig) +{ + return TALER_CRYPTO_helper_esign_sign_ (esh, + purpose, + pub, + sig); +} + + +// FIXME: rename +enum GNUNET_GenericReturnValue +TEH_SECMOD_denom_load_meta ( + const struct TALER_DenominationHashP *h_denom_pub, + struct TALER_DenominationPublicKey *denom_pub, + struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta) +{ + struct HelperDenomination *hd; + enum GNUNET_GenericReturnValue ok; + + hd = GNUNET_CONTAINER_multihashmap_get (denom_keys, + &h_denom_pub->hash); + if (NULL == hd) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Denomination %s not known\n", + GNUNET_h2s (&h_denom_pub->hash)); + return GNUNET_NO; + } + meta->start = hd->start_time; + meta->expire_withdraw = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add (meta->start.abs_time, + hd->validity_duration)); + ok = TEH_CONFIG_load_denom_data (hd->section_name, + meta); + if (GNUNET_OK == ok) + { + GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID != + hd->denom_pub.bsign_pub_key->cipher); + TALER_denom_pub_copy (denom_pub, + &hd->denom_pub); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No fees for `%s', voiding key\n", + hd->section_name); + memset (denom_pub, + 0, + sizeof (*denom_pub)); + } + return ok; +} + + +// FIXME: rename +enum GNUNET_GenericReturnValue +TEH_SECMOD_esign_load_meta (const struct TALER_ExchangePublicKeyP *exchange_pub, + struct TALER_EXCHANGEDB_SignkeyMetaData *meta) +{ + struct HelperSignkey *hsk; + struct GNUNET_PeerIdentity pid; + + pid.public_key = exchange_pub->eddsa_pub; + hsk = GNUNET_CONTAINER_multipeermap_get (esign_keys, + &pid); + if (NULL == hsk) + { + GNUNET_break (0); + return GNUNET_NO; + } + meta->start = hsk->start_time; + + meta->expire_sign = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add (meta->start.abs_time, + hsk->validity_duration)); + meta->expire_legal = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add (meta->expire_sign.abs_time, + signkey_legal_duration)); + return GNUNET_OK; +} + + +bool +TEH_SECMOD_denom_priv_check_lost ( + const struct TALER_DenominationHashP *h_denom_pub) +{ + struct HelperDenomination *hd; + + hd = GNUNET_CONTAINER_multihashmap_get (denom_keys, + &h_denom_pub->hash); + return (NULL == hd) || + GNUNET_TIME_absolute_is_past ( + GNUNET_TIME_absolute_add ( + hd->start_time.abs_time, + hd->validity_duration)); +} + + +// FIXME: rename! +void +TEH_SECMOD_esign_revoke ( + const struct TALER_ExchangePublicKeyP *exchange_pub) +{ + TALER_CRYPTO_helper_esign_revoke (esh, + exchange_pub); + TEH_keys_update_states (); +} + + +// FIXME: rename! +void +TEH_SECMOD_denom_revoke ( + const struct TALER_DenominationHashP *h_denom_pub) +{ + struct HelperDenomination *hd; + + hd = GNUNET_CONTAINER_multihashmap_get (denom_keys, + &h_denom_pub->hash); + if (NULL == hd) + { + GNUNET_break (0); + return; + } + switch (hd->denom_pub.bsign_pub_key->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + TALER_CRYPTO_helper_rsa_revoke (rsadh, + &hd->h_details.h_rsa); + TEH_keys_update_states (); + return; + case GNUNET_CRYPTO_BSA_CS: + TALER_CRYPTO_helper_cs_revoke (csdh, + &hd->h_details.h_cs); + TEH_keys_update_states (); + return; + } + GNUNET_break (0); + return; +} diff --git a/src/exchange/taler-exchange-httpd_secmod-helpers.h b/src/exchange/taler-exchange-httpd_secmod-helpers.h @@ -0,0 +1,386 @@ +/* + This file is part of TALER + Copyright (C) 2020-2026 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 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 <http://www.gnu.org/licenses/> + */ +/** + * @file taler-exchange-httpd_secmod-helpers.h + * @brief management of state with secmod helpers + * @author Christian Grothoff + * @author Özgür Kesim + */ +#ifndef TALER_EXCHANGE_HTTP_SECMOD_HELPERS_H +#define TALER_EXCHANGE_HTTP_SECMOD_HELPERS_H + +#include "taler-exchange-httpd.h" +#include <jansson.h> + +/** + * Setup SECMOD helper state. + * + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +TEH_SECMOD_setup_key_helpers (void); + + +/** + * Synchronize helper state. Polls the key helper for updates. + */ +void +TEH_SECMOD_sync_key_helpers (void); + + +/** + * Destroy SECMOD helper module state. + */ +void +TEH_SECMOD_destroy_key_helpers (void); + + +/** + * Check if we have at least one SECMOD for denomination public keys. + * + * @return true if we have one + */ +bool +TEH_SECMOD_have_denom_sm_pub (void); + +/** + * Check if we have at least one SECMOD for exchange signing keys. + * + * @return true if we have one + */ +bool +TEH_SECMOD_have_esign_sm_pub (void); + + +/** + * Return JSON with all of the offline security module public keys. + * + * @return JSON object + */ +json_t * +TEH_SECMOD_get_sm_pubs_as_json (void); + + +/** + * Information about a denomination on offer by the denomination helper. + */ +struct HelperDenomination +{ + + /** + * When will the helper start to use this key for signing? + */ + struct GNUNET_TIME_Timestamp start_time; + + /** + * For how long will the helper allow signing? 0 if + * the key was revoked or purged. + */ + struct GNUNET_TIME_Relative validity_duration; + + /** + * Hash of the full denomination key. + */ + struct TALER_DenominationHashP h_denom_pub; + + /** + * Signature over this key from the security module's key. + */ + struct TALER_SecurityModuleSignatureP sm_sig; + + /** + * The (full) public key. + */ + struct TALER_DenominationPublicKey denom_pub; + + /** + * Details depend on the @e denom_pub.cipher type. + */ + union + { + + /** + * Hash of the RSA key. + */ + struct TALER_RsaPubHashP h_rsa; + + /** + * Hash of the CS key. + */ + struct TALER_CsPubHashP h_cs; + + } h_details; + + /** + * Name in configuration section for this denomination type. + */ + char *section_name; + +}; + + +/** + * Iterate over all known denomination keys. + * + * @param cb function to call + * @param cb_cls closure to pass to @a cb + */ +void +TEH_SECMOD_iterate_denom_keys ( + GNUNET_CONTAINER_MultiHashMapIteratorCallback cb, + void *cb_cls); + + +/** + * Information about a signing key on offer by the esign helper. + */ +struct HelperSignkey +{ + /** + * When will the helper start to use this key for signing? + */ + struct GNUNET_TIME_Timestamp start_time; + + /** + * For how long will the helper allow signing? 0 if + * the key was revoked or purged. + */ + struct GNUNET_TIME_Relative validity_duration; + + /** + * The public key. + */ + struct TALER_ExchangePublicKeyP exchange_pub; + + /** + * Signature over this key from the security module's key. + */ + struct TALER_SecurityModuleSignatureP sm_sig; + +}; + + +/** + * Iterate over all known exchange signing keys. + * + * @param cb function to call + * @param cb_cls closure to pass to @a cb + */ +void +TEH_SECMOD_iterate_esign_keys ( + GNUNET_CONTAINER_PeerMapIterator cb, + void *cb_cls); + + +/** + * Load fees and expiration times (!) for the denomination type configured for + * the denomination matching @a h_denom_pub. + * + * @param h_denom_pub hash of the denomination public key + * to use to derive the section name of the configuration to use + * @param[out] denom_pub set to the denomination public key (to be freed by caller!) + * @param[out] meta denomination type data to complete + * @return #GNUNET_OK on success, + * #GNUNET_NO if @a h_denom_pub is not known + * #GNUNET_SYSERR on hard errors + */ +enum GNUNET_GenericReturnValue +TEH_SECMOD_denom_load_meta ( + const struct TALER_DenominationHashP *h_denom_pub, + struct TALER_DenominationPublicKey *denom_pub, + struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta); + + +/** + * Load expiration times for the given onling signing key. + * + * @param exchange_pub the online signing key + * @param[out] meta set to meta data about the key + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +TEH_SECMOD_esign_load_meta (const struct TALER_ExchangePublicKeyP *exchange_pub, + struct TALER_EXCHANGEDB_SignkeyMetaData *meta); + + +/** + * Information needed to derive the CS r_pub. + */ +struct TEH_SECMOD_CsDeriveData +{ + /** + * Hash of key to sign with. + */ + const struct TALER_DenominationHashP *h_denom_pub; + + /** + * Nonce to use. + */ + const struct GNUNET_CRYPTO_CsSessionNonce *nonce; +}; + + +/** + * Request to derive CS @a r_pub using the denomination and nonce from @a cdd. + * + * @param cdd data to compute @a r_pub from + * @param for_melt true if this is for a melt operation + * @param[out] r_pub where to write the result + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +TEH_SECMOD_denom_cs_compute_r_pub ( + const struct TEH_SECMOD_CsDeriveData *cdd, + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP *r_pub); + + +/** + * Request to derive a bunch of CS @a r_pubs using the + * denominations and nonces from @a cdds. + * + * @param cdds array to compute @a r_pubs from + * @param cdds_length length of the @a cdds array + * @param for_melt true if this is for a melt operation + * @param[out] r_pubs array where to write the result; must be of length @a cdds_length + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +TEH_SECMOD_denom_cs_batch_r_pub_simple ( + unsigned int cdds_length, + const struct TEH_SECMOD_CsDeriveData cdds[static cdds_length], + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static cdds_length]); + + +/** + * Request to derive a bunch of CS @a r_pubs using the + * denominations and nonces from @a cdds. + * + * @param num number of input elements + * @param h_denom_pubs array @a num of hashes of keys to sign with + * @param nonces array @a num of nonces to use + * @param for_melt true if this is for a melt operation + * @param[out] r_pubs array where to write the result; must be of length @a num + * @param[out] err_idx in case of error, the index into @e cdds that caused it + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +TEH_SECMOD_denom_cs_batch_r_pub ( + size_t num, + const struct TALER_DenominationHashP h_denom_pubs[static num], + const struct GNUNET_CRYPTO_CsSessionNonce nonces[static num], + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static num], + size_t *err_idx); + + +/** + * Information needed to create a blind signature. + */ +struct TEH_SECMOD_CoinSignData +{ + /** + * Hash of key to sign with. + */ + const struct TALER_DenominationHashP *h_denom_pub; + + /** + * Blinded planchet to sign over. + */ + const struct TALER_BlindedPlanchet *bp; +}; + + +/** + * Request to sign @a csds. + * + * @param csds array with data to blindly sign (and keys to sign with) + * @param csds_length length of @a csds array + * @param for_melt true if this is for a melt operation + * @param[out] bss array set to the blind signature on success; must be of length @a csds_length + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +TEH_SECMOD_denom_batch_sign ( + unsigned int csds_length, + const struct TEH_SECMOD_CoinSignData csds[static csds_length], + bool for_melt, + struct TALER_BlindedDenominationSignature bss[static csds_length]); + + +/** + * Request to sign @a purpose. Note that this function does NOT check if @a + * pub is actually signed by our offline tool. Thus, clients should usually + * use TEH_keys_exchange_sign() and related APIs that include this check. + * + * @param purpose message to sign + * @param[out] pub set to public key used to sign + * @param[out] sig set to resulting signature + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +TEH_SECMOD_exchange_sign ( + const struct GNUNET_CRYPTO_SignaturePurpose *purpose, + struct TALER_ExchangePublicKeyP *pub, + struct TALER_ExchangeSignatureP *sig); + + +/** + * Check if we lost the private denomination key for the + * denomination @a h_denom_pub + * + * @param h_denom_pub hash over the public key + * @return true if we lost the private key + */ +bool +TEH_SECMOD_denom_priv_check_lost ( + const struct TALER_DenominationHashP *h_denom_pub); + + +/** + * Revoke the public key associated with @a h_denom_pub. + * This function should be called AFTER the database was + * updated, as it also triggers #TEH_keys_update_states(). + * + * Note that the actual revocation happens asynchronously and + * may thus fail silently. To verify that the revocation succeeded, + * clients must watch for the associated change to the key state. + * + * @param h_denom_pub hash of the public key to revoke + */ +void +TEH_SECMOD_denom_revoke ( + const struct TALER_DenominationHashP *h_denom_pub); + + +/** + * Revoke the given exchange's signing key. + * This function should be called AFTER the database was + * updated, as it also triggers #TEH_keys_update_states(). + * + * Note that the actual revocation happens asynchronously and + * may thus fail silently. To verify that the revocation succeeded, + * clients must watch for the associated change to the key state. + * + * @param exchange_pub key to revoke + */ +void +TEH_SECMOD_esign_revoke ( + const struct TALER_ExchangePublicKeyP *exchange_pub); + + +#endif diff --git a/src/testing/test_exchange_api.conf b/src/testing/test_exchange_api.conf @@ -57,7 +57,7 @@ TINY_AMOUNT = EUR:0.01 TERMS_ETAG = exchange-tos-tops-v0 PRIVACY_ETAG = 0 PORT = 8081 -MASTER_PUBLIC_KEY = S2PF0H375EQC7C0SQ6T8VH09GA1EVFBDXKS5KRBQAW8XW5KBHT9G +MASTER_PUBLIC_KEY = W91R2NPHGP9TD36EXCAWNTW63QHEED4P12SNTKPE1WD5YM6MVA40 DB = postgres BASE_URL = "http://localhost:8081/" EXPIRE_SHARD_SIZE ="300 ms"