donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

commit ade143d8dcda67578aa3d8479b5ab45fd69303d7
parent 939058c634a4b99069db5b86c6e88af3a81c1c8c
Author: Pius Loosli <loosp2@bfh.ch>
Date:   Sun, 14 Jan 2024 22:41:57 +0100

[secmod/helpers] Add helpers from Exchange and adapt to Donau

Diffstat:
Msrc/include/donau_crypto_lib.h | 565+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/util/Makefile.am | 5++++-
Asrc/util/crypto_helper_common.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/util/crypto_helper_common.h | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/util/crypto_helper_cs.c | 1317+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/util/crypto_helper_esign.c | 557+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/util/crypto_helper_rsa.c | 917+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 3443 insertions(+), 10 deletions(-)

diff --git a/src/include/donau_crypto_lib.h b/src/include/donau_crypto_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2023 Taler Systems SA + Copyright (C) 2023-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -21,6 +21,7 @@ * @author Christian Grothoff <christian@grothoff.org> * @author Özgür Kesim <oec-taler@kesim.org> * @author Lukas Matyja + * @author Pius Loosli */ #if ! defined (__DONAU_UTIL_LIB_H_INSIDE__) #error "Only <donau_util.h> can be included directly." @@ -126,8 +127,10 @@ struct DONAU_DonationUnitHashP * @return 0 if the keys are equal, otherwise -1 or 1 */ int -DONAU_donation_unit_pub_cmp (const struct DONAU_DonationUnitPublicKey *donation_unit1, - const struct DONAU_DonationUnitPublicKey *donation_unit2); +DONAU_donation_unit_pub_cmp (const struct + DONAU_DonationUnitPublicKey *donation_unit1, + const struct + DONAU_DonationUnitPublicKey *donation_unit2); /** * Make a (deep) copy of the given @a donation_unit_src to @@ -137,8 +140,12 @@ DONAU_donation_unit_pub_cmp (const struct DONAU_DonationUnitPublicKey *donation_ * @param donation_unit_src public key to copy */ void -DONAU_donation_unit_pub_deep_copy (struct DONAU_DonationUnitPublicKey *donation_unit_dst, - const struct DONAU_DonationUnitPublicKey *donation_unit_src); +DONAU_donation_unit_pub_deep_copy (struct + DONAU_DonationUnitPublicKey * + donation_unit_dst, + const struct + DONAU_DonationUnitPublicKey * + donation_unit_src); /** * Free internals of @a donation_unit_pub, but not @a donation_unit_pub itself. @@ -146,7 +153,8 @@ DONAU_donation_unit_pub_deep_copy (struct DONAU_DonationUnitPublicKey *donation_ * @param[in] donation_unit_pub key to free */ void -DONAU_donation_unit_pub_free (struct DONAU_DonationUnitPublicKey *donation_unit_pub); +DONAU_donation_unit_pub_free (struct + DONAU_DonationUnitPublicKey *donation_unit_pub); /** * Hash used to represent a Donation Receipt @@ -174,9 +182,9 @@ struct DONAU_HashDonorTaxId struct DONAU_BlindedDonationUnitSignature { /** - * The blinded signature + * Donation Units use blind signatures. */ - struct TALER_DenominationSignature b_sig; + struct GNUNET_CRYPTO_BlindedSignature *blinded_sig; }; @@ -324,10 +332,135 @@ DONAU_donation_statement_verify ( const struct DONAU_DonauSignatureP *statement_sig); -/* ********************* helper ************************** */ +///* ********************* donau blind signing ************************** */ /** + * Free internals of @a donation_unit_sig, but not @a donation_unit_sig itself. + * + * @param[in] donation_unit_sig signature to free +// */ +void +DONAU_blinded_donation_unit_sig_free ( + struct DONAU_BlindedDonationUnitSignature *donation_unit_sig); + +// FIXME: Copied from taler_crypto_lib.h, is anything of this necessary? +///** +// * Compute the hash of the given @a donation_unit_pub. +// * +// * @param donation_unit_pub public key to hash +// * @param[out] donation_unit_hash resulting hash value +// */ +// void +// TALER_donation_unit_pub_hash (const struct DONAU_DonationUnitPublicKey *donation_unit_pub, +// struct DONAU_DonationUnitHashP *donation_unit_hash); +// +// +///** +// * Make a (deep) copy of the given @a donation_unit_src to +// * @a donation_unit_dst. +// * +// * @param[out] donation_unit_dst target to copy to +// * @param donation_unit_src public key to copy +// */ +// void +// TALER_donation_unit_pub_deep_copy (struct DONAU_DonationUnitPublicKey *donation_unit_dst, +// const struct DONAU_DonationUnitPublicKey *donation_unit_src); +// +// +///** +// * Make a (deep) copy of the given @a donation_unit_src to +// * @a donation_unit_dst. +// * +// * @param[out] donation_unit_dst target to copy to +// * @param donation_unit_src public key to copy +// */ +// void +// TALER_donation_unit_sig_deep_copy (struct DONAU_DonationUnitSignature *donation_unit_dst, +// const struct DONAU_DonationUnitSignature *donation_unit_src); +// +// +///** +// * Make a (deep) copy of the given @a donation_unit_src to +// * @a donation_unit_dst. +// * +// * @param[out] donation_unit_dst target to copy to +// * @param donation_unit_src public key to copy +// */ +// void +// TALER_blinded_donation_unit_sig_deep_copy ( +// struct DONAU_BlindedDonationUnitSignature *donation_unit_dst, +// const struct DONAU_BlindedDonationUnitSignature *donation_unit_src); +// +// +///** +// * Compare two donation unit public keys. +// * +// * @param donation_unit1 first key +// * @param donation_unit2 second key +// * @return 0 if the keys are equal, otherwise -1 or 1 +// */ +// int +// TALER_donation_unit_pub_cmp (const struct DONAU_DonationUnitPublicKey *donation_unit1, +// const struct DONAU_DonationUnitPublicKey *donation_unit2); +// +// +///** +// * Compare two donation unit signatures. +// * +// * @param sig1 first signature +// * @param sig2 second signature +// * @return 0 if the keys are equal, otherwise -1 or 1 +// */ +// int +// TALER_donation_unit_sig_cmp (const struct DONAU_DonationUnitSignature *sig1, +// const struct DONAU_DonationUnitSignature *sig2); +// +// +///** +// * Compare two blinded donation unit signatures. +// * +// * @param sig1 first signature +// * @param sig2 second signature +// * @return 0 if the keys are equal, otherwise -1 or 1 +// */ +// int +// TALER_blinded_donation_unit_sig_cmp ( +// const struct DONAU_BlindedDonationUnitSignature *sig1, +// const struct DONAU_BlindedDonationUnitSignature *sig2); +// +// +///** +// * Compare two blinded planchets. +// * +// * @param bp1 first blinded planchet +// * @param bp2 second blinded planchet +// * @return 0 if the keys are equal, otherwise -1 or 1 +// */ +// int +// TALER_blinded_planchet_cmp ( +// const struct DONAU_BlindedPlanchet *bp1, +// const struct DONAU_BlindedPlanchet *bp2); +// +// +///** +// * Verify signature made with a donation unit public key +// * over a donation receipt. +// * +// * @param donation_unit_pub public donation unit key +// * @param donation_unit_sig signature made with the private key +// * @param c_hash hash over the coin +// * @return #GNUNET_OK if the signature is valid +// */ +// enum GNUNET_GenericReturnValue +// TALER_donation_unit_pub_verify (const struct DONAU_DonationUnitPublicKey *donation_unit_pub, +// const struct DONAU_DonationUnitSignature *donation_unit_sig, +// const struct DONAU_ *c_hash); +// + + +/*********************** helpers ************************************************/ +/** * Group of donation units. These are the common fields of an array of * donation units. */ @@ -369,4 +502,418 @@ struct TALER_DonauBatchIssueValues }; +/* ********************* Helper-based RSA operations ************************** */ +/** + * 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 + * @param section_name name of the donation unit 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 RSA @a donation_unit_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, NULL if the key was revoked or purged + * The signature was already verified against @a sm_pub. + */ +typedef void +(*DONAU_CRYPTO_RsaDonationUnitKeyStatusCallback)( + 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); + +/** + * Handle for talking to an Donation unit key signing helper. + */ +struct DONAU_CRYPTO_RsaDonationUnitHelper; + + +/** + * Initiate connection to an donation unit key helper. + * + * @param cfg configuration to use + * @param section configuration section prefix to use, usually 'taler' or 'donau' + * @param dkc function to call with key information + * @param dkc_cls closure for @a dkc + * @return NULL on error (such as bad @a cfg). + */ +struct DONAU_CRYPTO_RsaDonationUnitHelper * +DONAU_CRYPTO_helper_rsa_connect ( + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, + DONAU_CRYPTO_RsaDonationUnitKeyStatusCallback dkc, + void *dkc_cls); + + +/** + * Function to call to 'poll' for updates to the available key material. + * Should be called whenever it is important that the key material status is + * current, like when handling a "/keys" request. This function basically + * briefly checks if there are messages from the helper announcing changes to + * donation unit keys. + * + * @param dh helper process connection + */ +void +DONAU_CRYPTO_helper_rsa_poll (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh); + +/** + * Close connection to @a dh. + * + * @param[in] dh connection to close + */ +void +DONAU_CRYPTO_helper_rsa_disconnect ( + struct DONAU_CRYPTO_RsaDonationUnitHelper *dh); + + +/* **************** Helper-based CS operations **************** */ + +/** + * Handle for talking to an DonationUnit key signing helper. + */ +struct DONAU_CRYPTO_CsDonationUnitHelper; + +/** + * 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 + * @param section_name name of the donation unit 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 CS @a donation_unit_pub that is available (or was purged) + * @param bsign_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, NULL if the key was revoked or purged + * The signature was already verified against @a sm_pub. + */ +typedef void +(*DONAU_CRYPTO_CsDonationUnitKeyStatusCallback)( + 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 *bsign_pub, + const struct TALER_SecurityModulePublicKeyP *sm_pub, + const struct TALER_SecurityModuleSignatureP *sm_sig); + + +/** + * Initiate connection to an donation unit key helper. + * + * @param cfg configuration to use + * @param section configuration section prefix to use, usually 'taler' or 'donau' + * @param dkc function to call with key information + * @param dkc_cls closure for @a dkc + * @return NULL on error (such as bad @a cfg). + */ +struct DONAU_CRYPTO_CsDonationUnitHelper * +DONAU_CRYPTO_helper_cs_connect ( + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, + DONAU_CRYPTO_CsDonationUnitKeyStatusCallback dkc, + void *dkc_cls); + + +/** + * Function to call to 'poll' for updates to the available key material. + * Should be called whenever it is important that the key material status is + * current, like when handling a "/keys" request. This function basically + * briefly checks if there are messages from the helper announcing changes to + * donation unit keys. + * + * @param dh helper process connection + */ +void +DONAU_CRYPTO_helper_cs_poll (struct DONAU_CRYPTO_CsDonationUnitHelper *dh); + + +/** + * Request helper @a dh to sign @a req. + * + * This operation will block until the signature has been obtained. Should + * this process receive a signal (that is not ignored) while the operation is + * pending, the operation will fail. Note that the helper may still believe + * that it created the signature. Thus, signals may result in a small + * differences in the signature counters. Retrying in this case may work. + * + * @param dh helper process connection + * @param req information about the key to sign with and the value to sign + * @param for_melt true if for melt operation + * @param[out] bs set to the blind signature + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +DONAU_CRYPTO_helper_cs_sign ( + struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + const struct TALER_CRYPTO_CsSignRequest *req, + bool for_melt, + struct DONAU_BlindedDonationUnitSignature *bs); + + +/** + * Request helper @a dh to sign batch of @a reqs requests. + * + * This operation will block until the signature has been obtained. Should + * this process receive a signal (that is not ignored) while the operation is + * pending, the operation will fail. Note that the helper may still believe + * that it created the signature. Thus, signals may result in a small + * differences in the signature counters. Retrying in this case may work. + * + * @param dh helper process connection + * @param reqs information about the keys to sign with and the values to sign + * @param reqs_length length of the @a reqs array + * @param for_melt true if this is for a melt operation + * @param[out] bss array set to the blind signatures, must be of length @a reqs_length! + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +DONAU_CRYPTO_helper_cs_batch_sign ( + struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + unsigned int reqs_length, + const struct TALER_CRYPTO_CsSignRequest reqs[static reqs_length], + bool for_melt, + struct DONAU_BlindedDonationUnitSignature bss[static reqs_length]); + + +/** + * Ask the helper to revoke the public key associated with @a h_cs. + * Will cause the helper to tell all clients that the key is now unavailable, + * and to create a replacement key. + * + * This operation will block until the revocation request has been + * transmitted. Should this process receive a signal (that is not ignored) + * while the operation is pending, the operation may fail. If the key is + * unknown, this function will also appear to have succeeded. To be sure that + * the revocation worked, clients must watch the donation unit key status + * callback. + * + * @param dh helper to process connection + * @param h_cs hash of the CS public key to revoke + */ +void +DONAU_CRYPTO_helper_cs_revoke ( + struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + const struct TALER_CsPubHashP *h_cs); + + +/** + * Ask the helper to derive R using the information + * from @a cdr. + * + * This operation will block until the R has been obtained. Should + * this process receive a signal (that is not ignored) while the operation is + * pending, the operation will fail. Note that the helper may still believe + * that it created the signature. Thus, signals may result in a small + * differences in the signature counters. Retrying in this case may work. + * + * @param dh helper to process connection + * @param cdr derivation input data + * @param for_melt true if this is for a melt operation + * @param[out] crp set to the pair of R values + * @return set to the error code (or #TALER_EC_NONE on success) + */ +enum TALER_ErrorCode +DONAU_CRYPTO_helper_cs_r_derive ( + struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + const struct TALER_CRYPTO_CsDeriveRequest *cdr, + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP *crp); + + +/** + * Ask the helper to derive R using the information from @a cdrs. + * + * This operation will block until the R has been obtained. Should + * this process receive a signal (that is not ignored) while the operation is + * pending, the operation will fail. Note that the helper may still believe + * that it created the signature. Thus, signals may result in a small + * differences in the signature counters. Retrying in this case may work. + * + * @param dh helper to process connection + * @param cdrs_length length of the @a cdrs array + * @param cdrs array with derivation input data + * @param for_melt true if this is for a melt operation + * @param[out] crps array set to the pair of R values, must be of length @a cdrs_length + * @return set to the error code (or #TALER_EC_NONE on success) + */ +enum TALER_ErrorCode +DONAU_CRYPTO_helper_cs_r_batch_derive ( + struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + unsigned int cdrs_length, + const struct TALER_CRYPTO_CsDeriveRequest cdrs[static cdrs_length], + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP crps[static cdrs_length]); + + +/** + * Close connection to @a dh. + * + * @param[in] dh connection to close + */ +void +DONAU_CRYPTO_helper_cs_disconnect ( + struct DONAU_CRYPTO_CsDonationUnitHelper *dh); + + +/*********************** Helper-based EDDSA operations *****************************/ + +/** + * Handle for talking to an online key signing helper. + */ +struct DONAU_CRYPTO_DonauSignHelper; + +/** + * 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 + * @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, NULL if the key was revoked or purged + * The signature was already verified against @a sm_pub. + */ +typedef void +(*DONAU_CRYPTO_DonauKeyStatusCallback)( + void *cls, + struct GNUNET_TIME_Timestamp start_time, + struct GNUNET_TIME_Relative validity_duration, + const struct DONAU_DonauPublicKeyP *exchange_pub, + const struct TALER_SecurityModulePublicKeyP *sm_pub, + const struct TALER_SecurityModuleSignatureP *sm_sig); + + +/** + * Initiate connection to an online signing key helper. + * + * @param cfg configuration to use + * @param section configuration section prefix to use, usually 'taler' or 'donau' + * @param ekc function to call with key information + * @param ekc_cls closure for @a ekc + * @return NULL on error (such as bad @a cfg). + */ +struct DONAU_CRYPTO_DonauSignHelper * +DONAU_CRYPTO_helper_esign_connect ( + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, + DONAU_CRYPTO_DonauKeyStatusCallback ekc, + void *ekc_cls); + + +/** + * Function to call to 'poll' for updates to the available key material. + * Should be called whenever it is important that the key material status is + * current, like when handling a "/keys" request. This function basically + * briefly checks if there are messages from the helper announcing changes to + * donau online signing keys. + * + * @param esh helper process connection + */ +void +DONAU_CRYPTO_helper_esign_poll (struct DONAU_CRYPTO_DonauSignHelper *esh); + + +/** + * Request helper @a esh to sign @a msg using the current online + * signing key. + * + * This operation will block until the signature has been obtained. Should + * this process receive a signal (that is not ignored) while the operation is + * pending, the operation will fail. Note that the helper may still believe + * that it created the signature. Thus, signals may result in a small + * differences in the signature counters. Retrying in this case may work. + * + * @param esh helper process connection + * @param purpose message to sign (must extend beyond the purpose) + * @param[out] donau_pub set to the public key used for the signature upon success + * @param[out] donau_sig set to the signature upon success + * @return the error code (or #TALER_EC_NONE on success) + */ +enum TALER_ErrorCode +DONAU_CRYPTO_helper_esign_sign_ ( + struct DONAU_CRYPTO_DonauSignHelper *esh, + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct DONAU_DonauPublicKeyP *donau_pub, + struct DONAU_DonauSignatureP *donau_sig); + + +/** + * Request helper @a esh to sign @a msg using the current online + * signing key. + * + * This operation will block until the signature has been obtained. Should + * this process receive a signal (that is not ignored) while the operation is + * pending, the operation will fail. Note that the helper may still believe + * that it created the signature. Thus, signals may result in a small + * differences in the signature counters. Retrying in this case may work. + * + * @param esh helper process connection + * @param ps message to sign (MUST begin with a purpose) + * @param[out] epub set to the public key used for the signature upon success + * @param[out] esig set to the signature upon success + * @return the error code (or #TALER_EC_NONE on success) + */ +#define DONAU_CRYPTO_helper_esign_sign(esh,ps,epub,esig) ( \ + /* check size is set correctly */ \ + GNUNET_assert (ntohl ((ps)->purpose.size) == sizeof (*ps)), \ + /* check 'ps' begins with the purpose */ \ + GNUNET_static_assert (((void*) (ps)) == \ + ((void*) &(ps)->purpose)), \ + DONAU_CRYPTO_helper_esign_sign_ (esh, \ + &(ps)->purpose, \ + epub, \ + esig) ) + + +/** + * Ask the helper to revoke the public key @a donau_pub . + * Will cause the helper to tell all clients that the key is now unavailable, + * and to create a replacement key. + * + * This operation will block until the revocation request has been + * transmitted. Should this process receive a signal (that is not ignored) + * while the operation is pending, the operation may fail. If the key is + * unknown, this function will also appear to have succeeded. To be sure that + * the revocation worked, clients must watch the signing key status callback. + * + * @param esh helper to process connection + * @param donau_pub the public key to revoke + */ +void +DONAU_CRYPTO_helper_esign_revoke ( + struct DONAU_CRYPTO_DonauSignHelper *esh, + const struct DONAU_DonauPublicKeyP *donau_pub); + + +/** + * Close connection to @a esh. + * + * @param[in] esh connection to close + */ +void +DONAU_CRYPTO_helper_esign_disconnect ( + struct DONAU_CRYPTO_DonauSignHelper *esh); + + #endif diff --git a/src/util/Makefile.am b/src/util/Makefile.am @@ -15,7 +15,10 @@ lib_LTLIBRARIES = \ libdonauutil_la_SOURCES = \ charity_signatures.c \ donau_signatures.c \ - donau_os_installation.c + donau_os_installation.c \ + crypto_helper_cs.c \ + crypto_helper_rsa.c \ + crypto_helper_esign.c libdonauutil_la_LIBADD = \ -ltalerutil \ diff --git a/src/util/crypto_helper_common.c b/src/util/crypto_helper_common.c @@ -0,0 +1,51 @@ +/* + This file is part of TALER + Copyright (C) 2020, 2021 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file util/crypto_helper_common.c + * @brief Common functions for the exchange security modules + * @author Florian Dold <dold@taler.net> + */ +#include <taler/platform.h> +#include <taler/taler_util.h> +#include <taler/taler_signatures.h> + + +enum GNUNET_GenericReturnValue +TALER_crypto_helper_send_all (int sock, + const void *buf, + size_t buf_size) +{ + size_t off = 0; + + while (off < buf_size) + { + ssize_t ret; + + ret = send (sock, + buf + off, + buf_size - off, + 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + return GNUNET_SYSERR; + } + GNUNET_assert (ret > 0); + off += ret; + } + return GNUNET_OK; +} diff --git a/src/util/crypto_helper_common.h b/src/util/crypto_helper_common.h @@ -0,0 +1,41 @@ +/* + This file is part of GNU Taler + Copyright (C) 2021 Taler Systems SA + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file util/crypto_helper_common.h + * @brief Common functions for the exchange security modules + * @author Florian Dold <dold@taler.net> + */ +#ifndef CRYPTO_HELPER_COMMON_H +#define CRYPTO_HELPER_COMMON_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_network_lib.h> + +/** + * Send all @a buf_size bytes from @a buf to @a sock. + * + * @param sock socket to send on + * @param buf data to send + * @param buf_size number of bytes in @a buf + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +TALER_crypto_helper_send_all (int sock, + const void *buf, + size_t buf_size); + +#endif diff --git a/src/util/crypto_helper_cs.c b/src/util/crypto_helper_cs.c @@ -0,0 +1,1317 @@ +/* + This file is part of TALER + Copyright (C) 2020-2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file util/crypto_helper_cs.c + * @brief utility functions for running out-of-process private key operations + * @author Christian Grothoff + * @author Pius Loosli + */ +#include "taler/platform.h" +#include "taler/taler_util.h" +#include "taler/taler_signatures.h" +#include "taler/taler-exchange-secmod-cs.h" +#include <poll.h> +#include "crypto_helper_common.h" +#include "donau_util.h" + +struct DONAU_CRYPTO_CsDonationUnitHelper +{ + /** + * Function to call with updates to available key material. + */ + DONAU_CRYPTO_CsDonationUnitKeyStatusCallback dkc; + + /** + * Closure for @e dkc + */ + void *dkc_cls; + + /** + * Socket address of the donation unit helper process. + * Used to reconnect if the connection breaks. + */ + struct sockaddr_un sa; + + /** + * The UNIX domain socket, -1 if we are currently not connected. + */ + int sock; + + /** + * Have we ever been sync'ed? + */ + bool synced; +}; + + +/** + * Disconnect from the helper process. Updates + * @e sock field in @a dh. + * + * @param[in,out] dh handle to tear down connection of + */ +static void +do_disconnect (struct DONAU_CRYPTO_CsDonationUnitHelper *dh) +{ + GNUNET_break (0 == close (dh->sock)); + dh->sock = -1; + dh->synced = false; +} + + +/** + * Try to connect to the helper process. Updates + * @e sock field in @a dh. + * + * @param[in,out] dh handle to establish connection for + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +try_connect (struct DONAU_CRYPTO_CsDonationUnitHelper *dh) +{ + if (-1 != dh->sock) + return GNUNET_OK; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Establishing connection!\n"); + dh->sock = socket (AF_UNIX, + SOCK_STREAM, + 0); + if (-1 == dh->sock) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "socket"); + return GNUNET_SYSERR; + } + if (0 != + connect (dh->sock, + (const struct sockaddr *) &dh->sa, + sizeof (dh->sa))) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "connect", + dh->sa.sun_path); + do_disconnect (dh); + return GNUNET_SYSERR; + } + DONAU_CRYPTO_helper_cs_poll (dh); + return GNUNET_OK; +} + + +struct DONAU_CRYPTO_CsDonationUnitHelper * +DONAU_CRYPTO_helper_cs_connect ( + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, + DONAU_CRYPTO_CsDonationUnitKeyStatusCallback dkc, + void *dkc_cls) +{ + struct DONAU_CRYPTO_CsDonationUnitHelper *dh; + char *unixpath; + char *secname; + + GNUNET_asprintf (&secname, + "%s-exchange-secmod-cs", + section); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + secname, + "UNIXPATH", + &unixpath)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + secname, + "UNIXPATH"); + GNUNET_free (secname); + return NULL; + } + /* we use >= here because we want the sun_path to always + be 0-terminated */ + if (strlen (unixpath) >= sizeof (dh->sa.sun_path)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + secname, + "UNIXPATH", + "path too long"); + GNUNET_free (unixpath); + GNUNET_free (secname); + return NULL; + } + GNUNET_free (secname); + dh = GNUNET_new (struct DONAU_CRYPTO_CsDonationUnitHelper); + dh->dkc = dkc; + dh->dkc_cls = dkc_cls; + dh->sa.sun_family = AF_UNIX; + strncpy (dh->sa.sun_path, + unixpath, + sizeof (dh->sa.sun_path) - 1); + GNUNET_free (unixpath); + dh->sock = -1; + if (GNUNET_OK != + try_connect (dh)) + { + DONAU_CRYPTO_helper_cs_disconnect (dh); + return NULL; + } + return dh; +} + + +/** + * Handle a #DONAU_HELPER_CS_MT_AVAIL message from the helper. + * + * @param dh helper context + * @param hdr message that we received + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +handle_mt_avail (struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + const struct GNUNET_MessageHeader *hdr) +{ + const struct TALER_CRYPTO_CsKeyAvailableNotification *kan + = (const struct TALER_CRYPTO_CsKeyAvailableNotification *) hdr; + const char *buf = (const char *) &kan[1]; + const char *section_name; + uint16_t snl; + + if (sizeof (*kan) > ntohs (hdr->size)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + snl = ntohs (kan->section_name_len); + if (ntohs (hdr->size) != sizeof (*kan) + snl) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (0 == snl) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + section_name = buf; + if ('\0' != section_name[snl - 1]) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + { + struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub; + struct TALER_CsPubHashP h_cs; + + bsign_pub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey); + bsign_pub->cipher = GNUNET_CRYPTO_BSA_CS; + bsign_pub->rc = 1; + bsign_pub->details.cs_public_key = kan->denom_pub; + + GNUNET_CRYPTO_hash (&bsign_pub->details.cs_public_key, + sizeof (bsign_pub->details.cs_public_key), + &bsign_pub->pub_key_hash); + h_cs.hash = bsign_pub->pub_key_hash; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received CS key %s (%s)\n", + GNUNET_h2s (&h_cs.hash), + section_name); + if (GNUNET_OK != + TALER_exchange_secmod_cs_verify ( + &h_cs, + section_name, + GNUNET_TIME_timestamp_ntoh (kan->anchor_time), + GNUNET_TIME_relative_ntoh (kan->duration_withdraw), + &kan->secm_pub, + &kan->secm_sig)) + { + GNUNET_break_op (0); + GNUNET_CRYPTO_blind_sign_pub_decref (bsign_pub); + return GNUNET_SYSERR; + } + dh->dkc (dh->dkc_cls, + section_name, + GNUNET_TIME_timestamp_ntoh (kan->anchor_time), + GNUNET_TIME_relative_ntoh (kan->duration_withdraw), + &h_cs, + bsign_pub, + &kan->secm_pub, + &kan->secm_sig); + GNUNET_CRYPTO_blind_sign_pub_decref (bsign_pub); + } + return GNUNET_OK; +} + + +/** + * Handle a #DONAU_HELPER_CS_MT_PURGE message from the helper. + * + * @param dh helper context + * @param hdr message that we received + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +handle_mt_purge (struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + const struct GNUNET_MessageHeader *hdr) +{ + const struct TALER_CRYPTO_CsKeyPurgeNotification *pn + = (const struct TALER_CRYPTO_CsKeyPurgeNotification *) hdr; + + if (sizeof (*pn) != ntohs (hdr->size)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received revocation of donation unit key %s\n", + GNUNET_h2s (&pn->h_cs.hash)); + dh->dkc (dh->dkc_cls, + NULL, + GNUNET_TIME_UNIT_ZERO_TS, + GNUNET_TIME_UNIT_ZERO, + &pn->h_cs, + NULL, + NULL, + NULL); + return GNUNET_OK; +} + + +void +DONAU_CRYPTO_helper_cs_poll (struct DONAU_CRYPTO_CsDonationUnitHelper *dh) +{ + char buf[UINT16_MAX]; + size_t off = 0; + unsigned int retry_limit = 3; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + + if (GNUNET_OK != + try_connect (dh)) + return; /* give up */ + while (1) + { + uint16_t msize; + ssize_t ret; + + ret = recv (dh->sock, + buf + off, + sizeof (buf) - off, + (dh->synced && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (dh->synced); + GNUNET_assert (0 == off); + break; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + if (0 == retry_limit) + return; /* give up */ + if (GNUNET_OK != + try_connect (dh)) + return; /* give up */ + retry_limit--; + continue; + } + if (0 == ret) + { + GNUNET_break (0 == off); + return; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received message of type %u and length %u\n", + (unsigned int) ntohs (hdr->type), + (unsigned int) msize); + switch (ntohs (hdr->type)) + { + case TALER_HELPER_CS_MT_AVAIL: + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return; + } + break; + case TALER_HELPER_CS_MT_PURGE: + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return; + } + break; + case TALER_HELPER_CS_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Now synchronized with CS helper\n"); + dh->synced = true; + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %d (len: %u)\n", + (unsigned int) ntohs (hdr->type), + (unsigned int) msize); + GNUNET_break_op (0); + do_disconnect (dh); + return; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } +} + + +enum TALER_ErrorCode +DONAU_CRYPTO_helper_cs_sign ( + struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + const struct TALER_CRYPTO_CsSignRequest *req, + bool for_melt, + struct DONAU_BlindedDonationUnitSignature *bs) +{ + enum TALER_ErrorCode ec = TALER_EC_INVALID; + const struct TALER_CsPubHashP *h_cs = req->h_cs; + + memset (bs, + 0, + sizeof (*bs)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting signature process\n"); + if (GNUNET_OK != + try_connect (dh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to helper\n"); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting signature\n"); + { + char buf[sizeof (struct TALER_CRYPTO_CsSignRequestMessage)]; + struct TALER_CRYPTO_CsSignRequestMessage *sr + = (struct TALER_CRYPTO_CsSignRequestMessage *) buf; + + sr->header.size = htons (sizeof (buf)); + sr->header.type = htons (TALER_HELPER_CS_MT_REQ_SIGN); + sr->for_melt = htonl (for_melt ? 1 : 0); + sr->h_cs = *h_cs; + sr->message = *req->blinded_planchet; + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + buf, + sizeof (buf))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting reply\n"); + { + char buf[UINT16_MAX]; + size_t off = 0; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + bool finished = false; + + while (1) + { + uint16_t msize; + ssize_t ret; + + ret = recv (dh->sock, + &buf[off], + sizeof (buf) - off, + (finished && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (finished); + GNUNET_assert (0 == off); + return ec; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + break; + } + if (0 == ret) + { + GNUNET_break (0 == off); + if (! finished) + ec = TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; + return ec; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + switch (ntohs (hdr->type)) + { + case TALER_HELPER_CS_MT_RES_SIGNATURE: + if (msize != sizeof (struct TALER_CRYPTO_SignResponse)) + { + GNUNET_break_op (0); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + if (finished) + { + GNUNET_break_op (0); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + { + const struct TALER_CRYPTO_SignResponse *sr = + (const struct TALER_CRYPTO_SignResponse *) buf; + struct GNUNET_CRYPTO_BlindedSignature *blinded_sig; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received signature\n"); + ec = TALER_EC_NONE; + finished = true; + blinded_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + blinded_sig->cipher = GNUNET_CRYPTO_BSA_CS; + blinded_sig->rc = 1; + blinded_sig->details.blinded_cs_answer.b = ntohl (sr->b); + blinded_sig->details.blinded_cs_answer.s_scalar = sr->cs_answer; + bs->blinded_sig = blinded_sig; + break; + } + case TALER_HELPER_CS_MT_RES_SIGN_FAILURE: + if (msize != sizeof (struct TALER_CRYPTO_SignFailure)) + { + GNUNET_break_op (0); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + { + const struct TALER_CRYPTO_SignFailure *sf = + (const struct TALER_CRYPTO_SignFailure *) buf; + + ec = (enum TALER_ErrorCode) ntohl (sf->ec); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Signing failed with status %d!\n", + ec); + finished = true; + break; + } + case TALER_HELPER_CS_MT_AVAIL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received new key!\n"); + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_MT_PURGE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received revocation!\n"); + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Synchronized add odd time with CS helper!\n"); + dh->synced = true; + break; + default: + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %u\n", + ntohs (hdr->type)); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } /* while(1) */ +end: + if (finished) +// DONAU_blinded_donation_unit_sig_free (bs); + return ec; + } +} + + +void +DONAU_CRYPTO_helper_cs_revoke ( + struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + const struct TALER_CsPubHashP *h_cs) +{ + struct TALER_CRYPTO_CsRevokeRequest rr = { + .header.size = htons (sizeof (rr)), + .header.type = htons (TALER_HELPER_CS_MT_REQ_REVOKE), + .h_cs = *h_cs + }; + + if (GNUNET_OK != + try_connect (dh)) + return; /* give up */ + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + &rr, + sizeof (rr))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Requested revocation of donation unit key %s\n", + GNUNET_h2s (&h_cs->hash)); +} + + +enum TALER_ErrorCode +DONAU_CRYPTO_helper_cs_r_derive (struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + const struct TALER_CRYPTO_CsDeriveRequest *cdr, + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP *crp) +{ + enum TALER_ErrorCode ec = TALER_EC_INVALID; + const struct TALER_CsPubHashP *h_cs = cdr->h_cs; + const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdr->nonce; + + memset (crp, + 0, + sizeof (*crp)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting R derivation process\n"); + if (GNUNET_OK != + try_connect (dh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to helper\n"); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting R\n"); + { + struct TALER_CRYPTO_CsRDeriveRequest rdr = { + .header.size = htons (sizeof (rdr)), + .header.type = htons (TALER_HELPER_CS_MT_REQ_RDERIVE), + .for_melt = htonl (for_melt ? 1 : 0), + .h_cs = *h_cs, + .nonce = *nonce + }; + + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + &rdr, + sizeof (rdr))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting reply\n"); + { + char buf[UINT16_MAX]; + size_t off = 0; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + bool finished = false; + + while (1) + { + uint16_t msize; + ssize_t ret; + + ret = recv (dh->sock, + &buf[off], + sizeof (buf) - off, + (finished && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (finished); + GNUNET_assert (0 == off); + return ec; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + if (0 == ret) + { + GNUNET_break (0 == off); + if (! finished) + return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; + return ec; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + switch (ntohs (hdr->type)) + { + case TALER_HELPER_CS_MT_RES_RDERIVE: + if (msize != sizeof (struct TALER_CRYPTO_RDeriveResponse)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + if (finished) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_RDeriveResponse *rdr = + (const struct TALER_CRYPTO_RDeriveResponse *) buf; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received R\n"); + finished = true; + ec = TALER_EC_NONE; + *crp = rdr->r_pub; + break; + } + case TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE: + if (msize != sizeof (struct TALER_CRYPTO_RDeriveFailure)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_RDeriveFailure *rdf = + (const struct TALER_CRYPTO_RDeriveFailure *) buf; + + ec = (enum TALER_ErrorCode) ntohl (rdf->ec); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "R derivation failed!\n"); + finished = true; + break; + } + case TALER_HELPER_CS_MT_AVAIL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received new key!\n"); + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_MT_PURGE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received revocation!\n"); + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Synchronized add odd time with CS helper!\n"); + dh->synced = true; + break; + default: + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %u\n", + ntohs (hdr->type)); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } /* while(1) */ + } +} + + +enum TALER_ErrorCode +DONAU_CRYPTO_helper_cs_batch_sign ( + struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + unsigned int reqs_length, + const struct TALER_CRYPTO_CsSignRequest reqs[static reqs_length], + bool for_melt, + struct DONAU_BlindedDonationUnitSignature bss[static reqs_length]) +{ + enum TALER_ErrorCode ec = TALER_EC_INVALID; + unsigned int rpos; + unsigned int rend; + unsigned int wpos; + + memset (bss, + 0, + sizeof (*bss) * reqs_length); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting signature process\n"); + if (GNUNET_OK != + try_connect (dh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to helper\n"); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting %u signatures\n", + reqs_length); + rpos = 0; + rend = 0; + wpos = 0; + while (rpos < reqs_length) + { + unsigned int mlen = sizeof (struct TALER_CRYPTO_BatchSignRequest); + + while ( (rend < reqs_length) && + (mlen + sizeof (struct TALER_CRYPTO_CsSignRequestMessage) + < UINT16_MAX) ) + { + mlen += sizeof (struct TALER_CRYPTO_CsSignRequestMessage); + rend++; + } + { + char obuf[mlen] GNUNET_ALIGN; + struct TALER_CRYPTO_BatchSignRequest *bsr + = (struct TALER_CRYPTO_BatchSignRequest *) obuf; + void *wbuf; + + bsr->header.type = htons (TALER_HELPER_CS_MT_REQ_BATCH_SIGN); + bsr->header.size = htons (mlen); + bsr->batch_size = htonl (rend - rpos); + wbuf = &bsr[1]; + for (unsigned int i = rpos; i<rend; i++) + { + struct TALER_CRYPTO_CsSignRequestMessage *csm = wbuf; + const struct TALER_CRYPTO_CsSignRequest *csr = &reqs[i]; + + csm->header.size = htons (sizeof (*csm)); + csm->header.type = htons (TALER_HELPER_CS_MT_REQ_SIGN); + csm->for_melt = htonl (for_melt ? 1 : 0); + csm->h_cs = *csr->h_cs; + csm->message = *csr->blinded_planchet; + wbuf += sizeof (*csm); + } + GNUNET_assert (wbuf == &obuf[mlen]); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending batch request [%u-%u)\n", + rpos, + rend); + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + obuf, + sizeof (obuf))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + } /* end of obuf scope */ + rpos = rend; + { + char buf[UINT16_MAX]; + size_t off = 0; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + bool finished = false; + + while (1) + { + uint16_t msize; + ssize_t ret; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting reply at %u (up to %u)\n", + wpos, + rend); + ret = recv (dh->sock, + &buf[off], + sizeof (buf) - off, + (finished && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (finished); + GNUNET_assert (0 == off); + break; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + if (0 == ret) + { + GNUNET_break (0 == off); + if (! finished) + return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; + if (TALER_EC_NONE == ec) + break; + return ec; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + switch (ntohs (hdr->type)) + { + case TALER_HELPER_CS_MT_RES_SIGNATURE: + if (msize != sizeof (struct TALER_CRYPTO_SignResponse)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + if (finished) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_SignResponse *sr = + (const struct TALER_CRYPTO_SignResponse *) buf; + struct GNUNET_CRYPTO_BlindedSignature *blinded_sig; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %u signature\n", + wpos); + blinded_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + blinded_sig->cipher = GNUNET_CRYPTO_BSA_CS; + blinded_sig->rc = 1; + blinded_sig->details.blinded_cs_answer.b = ntohl (sr->b); + blinded_sig->details.blinded_cs_answer.s_scalar = sr->cs_answer; + + bss[wpos].blinded_sig = blinded_sig; + wpos++; + if (wpos == rend) + { + if (TALER_EC_INVALID == ec) + ec = TALER_EC_NONE; + finished = true; + } + break; + } + + case TALER_HELPER_CS_MT_RES_SIGN_FAILURE: + if (msize != sizeof (struct TALER_CRYPTO_SignFailure)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_SignFailure *sf = + (const struct TALER_CRYPTO_SignFailure *) buf; + + ec = (enum TALER_ErrorCode) ntohl (sf->ec); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Signing %u failed with status %d!\n", + wpos, + ec); + wpos++; + if (wpos == rend) + { + finished = true; + } + break; + } + case TALER_HELPER_CS_MT_AVAIL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received new key!\n"); + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_MT_PURGE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received revocation!\n"); + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Synchronized add odd time with CS helper!\n"); + dh->synced = true; + break; + default: + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %u\n", + ntohs (hdr->type)); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } /* while(1) */ + } /* scope */ + } /* while (rpos < cdrs_length) */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Existing with %u signatures and status %d\n", + wpos, + ec); + return ec; +} + + +enum TALER_ErrorCode +DONAU_CRYPTO_helper_cs_r_batch_derive ( + struct DONAU_CRYPTO_CsDonationUnitHelper *dh, + unsigned int cdrs_length, + const struct TALER_CRYPTO_CsDeriveRequest cdrs[static cdrs_length], + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP crps[static cdrs_length]) +{ + enum TALER_ErrorCode ec = TALER_EC_INVALID; + unsigned int rpos; + unsigned int rend; + unsigned int wpos; + + memset (crps, + 0, + sizeof (*crps) * cdrs_length); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting R derivation process\n"); + if (GNUNET_OK != + try_connect (dh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to helper\n"); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting %u R pairs\n", + cdrs_length); + rpos = 0; + rend = 0; + wpos = 0; + while (rpos < cdrs_length) + { + unsigned int mlen = sizeof (struct TALER_CRYPTO_BatchDeriveRequest); + + while ( (rend < cdrs_length) && + (mlen + sizeof (struct TALER_CRYPTO_CsRDeriveRequest) + < UINT16_MAX) ) + { + mlen += sizeof (struct TALER_CRYPTO_CsRDeriveRequest); + rend++; + } + { + char obuf[mlen] GNUNET_ALIGN; + struct TALER_CRYPTO_BatchDeriveRequest *bdr + = (struct TALER_CRYPTO_BatchDeriveRequest *) obuf; + void *wbuf; + + bdr->header.type = htons (TALER_HELPER_CS_MT_REQ_BATCH_RDERIVE); + bdr->header.size = htons (mlen); + bdr->batch_size = htonl (rend - rpos); + wbuf = &bdr[1]; + for (unsigned int i = rpos; i<rend; i++) + { + struct TALER_CRYPTO_CsRDeriveRequest *rdr = wbuf; + const struct TALER_CRYPTO_CsDeriveRequest *cdr = &cdrs[i]; + + rdr->header.size = htons (sizeof (*rdr)); + rdr->header.type = htons (TALER_HELPER_CS_MT_REQ_RDERIVE); + rdr->for_melt = htonl (for_melt ? 1 : 0); + rdr->h_cs = *cdr->h_cs; + rdr->nonce = *cdr->nonce; + wbuf += sizeof (*rdr); + } + GNUNET_assert (wbuf == &obuf[mlen]); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending batch request [%u-%u)\n", + rpos, + rend); + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + obuf, + sizeof (obuf))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + } /* end of obuf scope */ + rpos = rend; + { + char buf[UINT16_MAX]; + size_t off = 0; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + bool finished = false; + + while (1) + { + uint16_t msize; + ssize_t ret; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting reply at %u (up to %u)\n", + wpos, + rend); + ret = recv (dh->sock, + &buf[off], + sizeof (buf) - off, + (finished && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (finished); + GNUNET_assert (0 == off); + break; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + if (0 == ret) + { + GNUNET_break (0 == off); + if (! finished) + return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; + if (TALER_EC_NONE == ec) + break; + return ec; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + switch (ntohs (hdr->type)) + { + case TALER_HELPER_CS_MT_RES_RDERIVE: + if (msize != sizeof (struct TALER_CRYPTO_RDeriveResponse)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + if (finished) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_RDeriveResponse *rdr = + (const struct TALER_CRYPTO_RDeriveResponse *) buf; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %u R pair\n", + wpos); + crps[wpos] = rdr->r_pub; + wpos++; + if (wpos == rend) + { + if (TALER_EC_INVALID == ec) + ec = TALER_EC_NONE; + finished = true; + } + break; + } + case TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE: + if (msize != sizeof (struct TALER_CRYPTO_RDeriveFailure)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_RDeriveFailure *rdf = + (const struct TALER_CRYPTO_RDeriveFailure *) buf; + + ec = (enum TALER_ErrorCode) ntohl (rdf->ec); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "R derivation %u failed with status %d!\n", + wpos, + ec); + wpos++; + if (wpos == rend) + { + finished = true; + } + break; + } + case TALER_HELPER_CS_MT_AVAIL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received new key!\n"); + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_MT_PURGE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received revocation!\n"); + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Synchronized add odd time with CS helper!\n"); + dh->synced = true; + break; + default: + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %u\n", + ntohs (hdr->type)); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } /* while(1) */ + } /* scope */ + } /* while (rpos < cdrs_length) */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Existing with %u signatures and status %d\n", + wpos, + ec); + return ec; +} + + +void +DONAU_CRYPTO_helper_cs_disconnect ( + struct DONAU_CRYPTO_CsDonationUnitHelper *dh) +{ + if (-1 != dh->sock) + do_disconnect (dh); + GNUNET_free (dh); +} + + +/* end of crypto_helper_cs.c */ diff --git a/src/util/crypto_helper_esign.c b/src/util/crypto_helper_esign.c @@ -0,0 +1,557 @@ +/* + This file is part of TALER + Copyright (C) 2020-2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file util/crypto_helper_esign.c + * @brief utility functions for running out-of-process private key operations + * @author Christian Grothoff + * @author Pius Loosli + */ +#include <taler/platform.h> +#include <taler/taler_util.h> +#include <taler/taler_signatures.h> +#include <taler/taler-exchange-secmod-eddsa.h> +#include <poll.h> +#include <crypto_helper_common.h> +#include "donau_util.h" + +struct DONAU_CRYPTO_DonauSignHelper +{ + /** + * Function to call with updates to available key material. + */ + DONAU_CRYPTO_DonauKeyStatusCallback dkc; + + /** + * Closure for @e dkc + */ + void *dkc_cls; + + /** + * Socket address of the denomination helper process. + * Used to reconnect if the connection breaks. + */ + struct sockaddr_un sa; + + /** + * The UNIX domain socket, -1 if we are currently not connected. + */ + int sock; + + /** + * Have we reached the sync'ed state? + */ + bool synced; + +}; + + +/** + * Disconnect from the helper process. Updates + * @e sock field in @a dsh. + * + * @param[in,out] dsh handle to tear down connection of + */ +static void +do_disconnect (struct DONAU_CRYPTO_DonauSignHelper *dsh) +{ + GNUNET_break (0 == close (dsh->sock)); + dsh->sock = -1; + dsh->synced = false; +} + + +/** + * Try to connect to the helper process. Updates + * @e sock field in @a dsh. + * + * @param[in,out] dsh handle to establish connection for + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +try_connect (struct DONAU_CRYPTO_DonauSignHelper *dsh) +{ + if (-1 != dsh->sock) + return GNUNET_OK; + dsh->sock = socket (AF_UNIX, + SOCK_STREAM, + 0); + if (-1 == dsh->sock) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "socket"); + return GNUNET_SYSERR; + } + if (0 != + connect (dsh->sock, + (const struct sockaddr *) &dsh->sa, + sizeof (dsh->sa))) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "connect", + dsh->sa.sun_path); + do_disconnect (dsh); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +struct DONAU_CRYPTO_DonauSignHelper * +DONAU_CRYPTO_helper_esign_connect ( + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, + DONAU_CRYPTO_DonauKeyStatusCallback dkc, + void *dkc_cls) +{ + struct DONAU_CRYPTO_DonauSignHelper *dsh; + char *unixpath; + char *secname; + + GNUNET_asprintf (&secname, + "%s-exchange-secmod-eddsa", + section); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + secname, + "UNIXPATH", + &unixpath)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + secname, + "UNIXPATH"); + GNUNET_free (secname); + return NULL; + } + /* we use >= here because we want the sun_path to always + be 0-terminated */ + if (strlen (unixpath) >= sizeof (dsh->sa.sun_path)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + secname, + "UNIXPATH", + "path too long"); + GNUNET_free (unixpath); + GNUNET_free (secname); + return NULL; + } + GNUNET_free (secname); + dsh = GNUNET_new (struct DONAU_CRYPTO_DonauSignHelper); + dsh->dkc = dkc; + dsh->dkc_cls = dkc_cls; + dsh->sa.sun_family = AF_UNIX; + strncpy (dsh->sa.sun_path, + unixpath, + sizeof (dsh->sa.sun_path) - 1); + GNUNET_free (unixpath); + dsh->sock = -1; + if (GNUNET_OK != + try_connect (dsh)) + { + DONAU_CRYPTO_helper_esign_disconnect (dsh); + return NULL; + } + +// DONAU_CRYPTO_helper_esign_poll (dsh); + return dsh; +} + + +/** + * Handle a #TALER_HELPER_EDDSA_MT_AVAIL message from the helper. + * + * @param dsh helper context + * @param hdr message that we received + * @return #GNUNET_OK on success + */ +// static enum GNUNET_GenericReturnValue +// handle_mt_avail (struct DONAU_CRYPTO_DonauSignHelper *dsh, +// const struct GNUNET_MessageHeader *hdr) +// { +// const struct TALER_CRYPTO_EddsaKeyAvailableNotification *kan +// = (const struct TALER_CRYPTO_EddsaKeyAvailableNotification *) hdr; +// +// if (sizeof (*kan) != ntohs (hdr->size)) +// { +// GNUNET_break_op (0); +// return GNUNET_SYSERR; +// } +// +// if (GNUNET_OK != +// TALER_exchange_secmod_eddsa_verify ( +// &kan->exchange_pub, +// GNUNET_TIME_timestamp_ntoh (kan->anchor_time), +// GNUNET_TIME_relative_ntoh (kan->duration), +// &kan->secm_pub, +// &kan->secm_sig)) +// { +// GNUNET_break_op (0); +// return GNUNET_SYSERR; +// } +// dsh->dkc (dsh->dkc_cls, +// GNUNET_TIME_timestamp_ntoh (kan->anchor_time), +// GNUNET_TIME_relative_ntoh (kan->duration), +// &kan->donau_pub, +// &kan->secm_pub, +// &kan->secm_sig); +// return GNUNET_OK; +// } + + +/** + * Handle a #TALER_HELPER_EDDSA_MT_PURGE message from the helper. + * + * @param dsh helper context + * @param hdr message that we received + * @return #GNUNET_OK on success + */ +// static enum GNUNET_GenericReturnValue +// handle_mt_purge (struct DONAU_CRYPTO_DonauSignHelper *dsh, +// const struct GNUNET_MessageHeader *hdr) +// { +// const struct TALER_CRYPTO_EddsaKeyPurgeNotification *pn +// = (const struct TALER_CRYPTO_EddsaKeyPurgeNotification *) hdr; +// +// if (sizeof (*pn) != ntohs (hdr->size)) +// { +// GNUNET_break_op (0); +// return GNUNET_SYSERR; +// } +// dsh->dkc (dsh->dkc_cls, +// GNUNET_TIME_UNIT_ZERO_TS, +// GNUNET_TIME_UNIT_ZERO, +// &pn->exchange_pub, +// NULL, +// NULL); +// return GNUNET_OK; +// } + + +// void +// DONAU_CRYPTO_helper_esign_poll (struct DONAU_CRYPTO_DonauSignHelper *dsh) +// { +// char buf[UINT16_MAX]; +// size_t off = 0; +// unsigned int retry_limit = 3; +// const struct GNUNET_MessageHeader *hdr +// = (const struct GNUNET_MessageHeader *) buf; +// +// if (GNUNET_OK != +// try_connect (dsh)) +// return; /* give up */ +// while (1) +// { +// uint16_t msize; +// ssize_t ret; +// +// ret = recv (dsh->sock, +// buf + off, +// sizeof (buf) - off, +// (dsh->synced && (0 == off)) +// ? MSG_DONTWAIT +// : 0); +// if (ret < 0) +// { +// if (EINTR == errno) +// continue; +// if (EAGAIN == errno) +// { +// GNUNET_assert (dsh->synced); +// GNUNET_assert (0 == off); +// break; +// } +// GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, +// "recv"); +// do_disconnect (dsh); +// if (0 == retry_limit) +// return; /* give up */ +// if (GNUNET_OK != +// try_connect (dsh)) +// return; /* give up */ +// retry_limit--; +// continue; +// } +// if (0 == ret) +// { +// GNUNET_break (0 == off); +// return; +// } +// off += ret; +// more: +// if (off < sizeof (struct GNUNET_MessageHeader)) +// continue; +// msize = ntohs (hdr->size); +// if (off < msize) +// continue; +// switch (ntohs (hdr->type)) +// { +// case TALER_HELPER_EDDSA_MT_AVAIL: +// if (GNUNET_OK != +// handle_mt_avail (dsh, +// hdr)) +// { +// GNUNET_break_op (0); +// do_disconnect (dsh); +// return; +// } +// break; +// case TALER_HELPER_EDDSA_MT_PURGE: +// if (GNUNET_OK != +// handle_mt_purge (dsh, +// hdr)) +// { +// GNUNET_break_op (0); +// do_disconnect (dsh); +// return; +// } +// break; +// case TALER_HELPER_EDDSA_SYNCED: +// GNUNET_log (GNUNET_ERROR_TYPE_INFO, +// "Now synchronized with EdDSA helper\n"); +// dsh->synced = true; +// break; +// default: +// GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +// "Received unexpected message of type %d (len: %u)\n", +// (unsigned int) ntohs (hdr->type), +// (unsigned int) msize); +// GNUNET_break_op (0); +// do_disconnect (dsh); +// return; +// } +// memmove (buf, +// &buf[msize], +// off - msize); +// off -= msize; +// goto more; +// } +// } + + +// enum TALER_ErrorCode +// DONAU_CRYPTO_helper_esign_sign_ ( +// struct DONAU_CRYPTO_DonauSignHelper *dsh, +// const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, +// struct DONAU_DonauPublicKeyP *exchange_pub, +// struct DONAU_DonauSignatureP *exchange_sig) +// { +// uint32_t purpose_size = ntohl (purpose->size); +// +// if (GNUNET_OK != +// try_connect (dsh)) +// { +// GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +// "Failed to connect to helper\n"); +// return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE; +// } +// GNUNET_assert (purpose_size < +// UINT16_MAX - sizeof (struct TALER_CRYPTO_EddsaSignRequest)); +// { +// char buf[sizeof (struct TALER_CRYPTO_EddsaSignRequest) + purpose_size +// - sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose)]; +// struct TALER_CRYPTO_EddsaSignRequest *sr +// = (struct TALER_CRYPTO_EddsaSignRequest *) buf; +// +// sr->header.size = htons (sizeof (buf)); +// sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN); +// sr->reserved = htonl (0); +// GNUNET_memcpy (&sr->purpose, +// purpose, +// purpose_size); +// if (GNUNET_OK != +// TALER_crypto_helper_send_all (dsh->sock, +// buf, +// sizeof (buf))) +// { +// GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, +// "send", +// dsh->sa.sun_path); +// do_disconnect (dsh); +// return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE; +// } +// } +// +// { +// char buf[UINT16_MAX]; +// size_t off = 0; +// const struct GNUNET_MessageHeader *hdr +// = (const struct GNUNET_MessageHeader *) buf; +// bool finished = false; +// enum TALER_ErrorCode ec = TALER_EC_INVALID; +// +// while (1) +// { +// ssize_t ret; +// uint16_t msize; +// +// ret = recv (dsh->sock, +// &buf[off], +// sizeof (buf) - off, +// (finished && (0 == off)) +// ? MSG_DONTWAIT +// : 0); +// if (ret < 0) +// { +// if (EINTR == errno) +// continue; +// if (EAGAIN == errno) +// { +// GNUNET_assert (finished); +// GNUNET_assert (0 == off); +// break; +// } +// GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, +// "recv"); +// do_disconnect (dsh); +// return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE; +// } +// if (0 == ret) +// { +// GNUNET_break (0 == off); +// if (finished) +// return TALER_EC_NONE; +// return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; +// } +// off += ret; +// more: +// if (off < sizeof (struct GNUNET_MessageHeader)) +// continue; +// msize = ntohs (hdr->size); +// if (off < msize) +// continue; +// switch (ntohs (hdr->type)) +// { +// case TALER_HELPER_EDDSA_MT_RES_SIGNATURE: +// if (msize != sizeof (struct TALER_CRYPTO_EddsaSignResponse)) +// { +// GNUNET_break_op (0); +// do_disconnect (dsh); +// return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; +// } +// if (finished) +// { +// GNUNET_break_op (0); +// do_disconnect (dsh); +// return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; +// } +// { +// const struct TALER_CRYPTO_EddsaSignResponse *sr = +// (const struct TALER_CRYPTO_EddsaSignResponse *) buf; +// *exchange_sig = sr->exchange_sig; +// *exchange_pub = sr->exchange_pub; +// finished = true; +// ec = TALER_EC_NONE; +// break; +// } +// case TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE: +// if (msize != sizeof (struct TALER_CRYPTO_EddsaSignFailure)) +// { +// GNUNET_break_op (0); +// do_disconnect (dsh); +// return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; +// } +// { +// const struct TALER_CRYPTO_EddsaSignFailure *sf = +// (const struct TALER_CRYPTO_EddsaSignFailure *) buf; +// +// finished = true; +// ec = (enum TALER_ErrorCode) ntohl (sf->ec); +// break; +// } +// case TALER_HELPER_EDDSA_MT_AVAIL: +// if (GNUNET_OK != +// handle_mt_avail (dsh, +// hdr)) +// { +// GNUNET_break_op (0); +// do_disconnect (dsh); +// return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; +// } +// break; /* while(1) loop ensures we recv() again */ +// case TALER_HELPER_EDDSA_MT_PURGE: +// if (GNUNET_OK != +// handle_mt_purge (dsh, +// hdr)) +// { +// GNUNET_break_op (0); +// do_disconnect (dsh); +// return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; +// } +// break; /* while(1) loop ensures we recv() again */ +// case TALER_HELPER_EDDSA_SYNCED: +// GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +// "Synchronized add odd time with EdDSA helper!\n"); +// dsh->synced = true; +// break; +// default: +// GNUNET_break_op (0); +// GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +// "Received unexpected message of type %u\n", +// ntohs (hdr->type)); +// do_disconnect (dsh); +// return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; +// } +// memmove (buf, +// &buf[msize], +// off - msize); +// off -= msize; +// goto more; +// } /* while(1) */ +// return ec; +// } +// } + + +// void +// DONAU_CRYPTO_helper_esign_revoke ( +// struct DONAU_CRYPTO_DonauSignHelper *dsh, +// const struct DONAU_DonauPublicKeyP *exchange_pub) +// { +// if (GNUNET_OK != +// try_connect (dsh)) +// return; /* give up */ +// { +// struct TALER_CRYPTO_EddsaRevokeRequest rr = { +// .header.size = htons (sizeof (rr)), +// .header.type = htons (TALER_HELPER_EDDSA_MT_REQ_REVOKE), +// .exchange_pub = *exchange_pub +// }; +// +// if (GNUNET_OK != +// TALER_crypto_helper_send_all (dsh->sock, +// &rr, +// sizeof (rr))) +// { +// GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, +// "send"); +// do_disconnect (dsh); +// return; +// } +// } +// } + + +void +DONAU_CRYPTO_helper_esign_disconnect ( + struct DONAU_CRYPTO_DonauSignHelper *dsh) +{ + if (-1 != dsh->sock) + do_disconnect (dsh); + GNUNET_free (dsh); +} + + +/* end of crypto_helper_esign.c */ diff --git a/src/util/crypto_helper_rsa.c b/src/util/crypto_helper_rsa.c @@ -0,0 +1,917 @@ +/* + This file is part of TALER + Copyright (C) 2020-2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file util/crypto_helper_rsa.c + * @brief utility functions for running out-of-process private key operations + * @author Christian Grothoff + * @author Pius Loosli + */ +#include <taler/platform.h> +#include <taler/taler_util.h> +#include <taler/taler_signatures.h> +#include <taler/taler-exchange-secmod-rsa.h> +#include <poll.h> +#include <crypto_helper_common.h> +#include "donau_util.h" + +struct DONAU_CRYPTO_RsaDonationUnitHelper +{ + /** + * Function to call with updates to available key material. + */ + DONAU_CRYPTO_RsaDonationUnitKeyStatusCallback dkc; + + /** + * Closure for @e dkc + */ + void *dkc_cls; + + /** + * Socket address of the donation unitination helper process. + * Used to reconnect if the connection breaks. + */ + struct sockaddr_un sa; + + /** + * The UNIX domain socket, -1 if we are currently not connected. + */ + int sock; + + /** + * Have we ever been sync'ed? + */ + bool synced; +}; + + +/** + * Disconnect from the helper process. Updates + * @e sock field in @a dh. + * + * @param[in,out] dh handle to tear down connection of + */ +static void +do_disconnect (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh) +{ + GNUNET_break (0 == close (dh->sock)); + dh->sock = -1; + dh->synced = false; +} + + +/** + * Try to connect to the helper process. Updates + * @e sock field in @a dh. + * + * @param[in,out] dh handle to establish connection for + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +try_connect (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh) +{ + if (-1 != dh->sock) + return GNUNET_OK; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Establishing connection!\n"); + dh->sock = socket (AF_UNIX, + SOCK_STREAM, + 0); + if (-1 == dh->sock) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "socket"); + return GNUNET_SYSERR; + } + if (0 != + connect (dh->sock, + (const struct sockaddr *) &dh->sa, + sizeof (dh->sa))) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "connect", + dh->sa.sun_path); + do_disconnect (dh); + return GNUNET_SYSERR; + } + DONAU_CRYPTO_helper_rsa_poll (dh); + return GNUNET_OK; +} + + +struct DONAU_CRYPTO_RsaDonationUnitHelper * +DONAU_CRYPTO_helper_rsa_connect ( + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, + DONAU_CRYPTO_RsaDonationUnitKeyStatusCallback dkc, + void *dkc_cls) +{ + struct DONAU_CRYPTO_RsaDonationUnitHelper *dh; + char *unixpath; + char *secname; + + GNUNET_asprintf (&secname, + "%s-exchange-secmod-rsa", + section); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + secname, + "UNIXPATH", + &unixpath)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + secname, + "UNIXPATH"); + GNUNET_free (secname); + return NULL; + } + /* we use >= here because we want the sun_path to always + be 0-terminated */ + if (strlen (unixpath) >= sizeof (dh->sa.sun_path)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "taler-exchange-secmod-rsa", + "UNIXPATH", + "path too long"); + GNUNET_free (unixpath); + GNUNET_free (secname); + return NULL; + } + GNUNET_free (secname); + dh = GNUNET_new (struct DONAU_CRYPTO_RsaDonationUnitHelper); + dh->dkc = dkc; + dh->dkc_cls = dkc_cls; + dh->sa.sun_family = AF_UNIX; + strncpy (dh->sa.sun_path, + unixpath, + sizeof (dh->sa.sun_path) - 1); + GNUNET_free (unixpath); + dh->sock = -1; + if (GNUNET_OK != + try_connect (dh)) + { + DONAU_CRYPTO_helper_rsa_disconnect (dh); + return NULL; + } + return dh; +} + + +/** + * Handle a #TALER_HELPER_RSA_MT_AVAIL message from the helper. + * + * @param dh helper context + * @param hdr message that we received + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +handle_mt_avail (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh, + const struct GNUNET_MessageHeader *hdr) +{ + const struct TALER_CRYPTO_RsaKeyAvailableNotification *kan + = (const struct TALER_CRYPTO_RsaKeyAvailableNotification *) hdr; + const char *buf = (const char *) &kan[1]; + const char *section_name; + uint16_t ps; + uint16_t snl; + + if (sizeof (*kan) > ntohs (hdr->size)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + ps = ntohs (kan->pub_size); + snl = ntohs (kan->section_name_len); + if (ntohs (hdr->size) != sizeof (*kan) + ps + snl) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (0 == snl) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + section_name = &buf[ps]; + if ('\0' != section_name[snl - 1]) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + { + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub; + struct TALER_RsaPubHashP h_rsa; + + bs_pub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey); + bs_pub->cipher = GNUNET_CRYPTO_BSA_RSA; + bs_pub->details.rsa_public_key + = GNUNET_CRYPTO_rsa_public_key_decode (buf, + ntohs (kan->pub_size)); + if (NULL == bs_pub->details.rsa_public_key) + { + GNUNET_break_op (0); + GNUNET_free (bs_pub); + return GNUNET_SYSERR; + } + bs_pub->rc = 1; + GNUNET_CRYPTO_rsa_public_key_hash (bs_pub->details.rsa_public_key, + &bs_pub->pub_key_hash); + h_rsa.hash = bs_pub->pub_key_hash; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received RSA key %s (%s)\n", + GNUNET_h2s (&bs_pub->pub_key_hash), + section_name); + if (GNUNET_OK != + TALER_exchange_secmod_rsa_verify ( + &h_rsa, + section_name, + GNUNET_TIME_timestamp_ntoh (kan->anchor_time), + GNUNET_TIME_relative_ntoh (kan->duration_withdraw), + &kan->secm_pub, + &kan->secm_sig)) + { + GNUNET_break_op (0); + GNUNET_CRYPTO_blind_sign_pub_decref (bs_pub); + return GNUNET_SYSERR; + } + dh->dkc (dh->dkc_cls, + section_name, + GNUNET_TIME_timestamp_ntoh (kan->anchor_time), + GNUNET_TIME_relative_ntoh (kan->duration_withdraw), + &h_rsa, + bs_pub, + &kan->secm_pub, + &kan->secm_sig); + GNUNET_CRYPTO_blind_sign_pub_decref (bs_pub); + } + return GNUNET_OK; +} + + +/** + * Handle a #TALER_HELPER_RSA_MT_PURGE message from the helper. + * + * @param dh helper context + * @param hdr message that we received + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +handle_mt_purge (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh, + const struct GNUNET_MessageHeader *hdr) +{ + const struct TALER_CRYPTO_RsaKeyPurgeNotification *pn + = (const struct TALER_CRYPTO_RsaKeyPurgeNotification *) hdr; + + if (sizeof (*pn) != ntohs (hdr->size)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received revocation of donation unit key %s\n", + GNUNET_h2s (&pn->h_rsa.hash)); + dh->dkc (dh->dkc_cls, + NULL, + GNUNET_TIME_UNIT_ZERO_TS, + GNUNET_TIME_UNIT_ZERO, + &pn->h_rsa, + NULL, + NULL, + NULL); + return GNUNET_OK; +} + + +void +DONAU_CRYPTO_helper_rsa_poll (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh) +{ + char buf[UINT16_MAX]; + size_t off = 0; + unsigned int retry_limit = 3; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + + if (GNUNET_OK != + try_connect (dh)) + return; /* give up */ + while (1) + { + uint16_t msize; + ssize_t ret; + + ret = recv (dh->sock, + buf + off, + sizeof (buf) - off, + (dh->synced && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (dh->synced); + GNUNET_assert (0 == off); + break; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + if (0 == retry_limit) + return; /* give up */ + if (GNUNET_OK != + try_connect (dh)) + return; /* give up */ + retry_limit--; + continue; + } + if (0 == ret) + { + GNUNET_break (0 == off); + return; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received message of type %u and length %u\n", + (unsigned int) ntohs (hdr->type), + (unsigned int) msize); + switch (ntohs (hdr->type)) + { + case TALER_HELPER_RSA_MT_AVAIL: + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return; + } + break; + case TALER_HELPER_RSA_MT_PURGE: + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return; + } + break; + case TALER_HELPER_RSA_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Now synchronized with RSA helper\n"); + dh->synced = true; + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %d (len: %u)\n", + (unsigned int) ntohs (hdr->type), + (unsigned int) msize); + GNUNET_break_op (0); + do_disconnect (dh); + return; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } +} + + +enum TALER_ErrorCode +DONAU_CRYPTO_helper_rsa_sign ( + struct DONAU_CRYPTO_RsaDonationUnitHelper *dh, + const struct TALER_CRYPTO_RsaSignRequest *rsr, + struct DONAU_BlindedDonationUnitSignature *bs) +{ + enum TALER_ErrorCode ec = TALER_EC_INVALID; + + memset (bs, + 0, + sizeof (*bs)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting signature process\n"); + if (GNUNET_OK != + try_connect (dh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to helper\n"); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting signature\n"); + { + char buf[sizeof (struct TALER_CRYPTO_SignRequest) + rsr->msg_size]; + struct TALER_CRYPTO_SignRequest *sr + = (struct TALER_CRYPTO_SignRequest *) buf; + + sr->header.size = htons (sizeof (buf)); + sr->header.type = htons (TALER_HELPER_RSA_MT_REQ_SIGN); + sr->reserved = htonl (0); + sr->h_rsa = *rsr->h_rsa; + GNUNET_memcpy (&sr[1], + rsr->msg, + rsr->msg_size); + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + buf, + sizeof (buf))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting reply\n"); + { + char buf[UINT16_MAX]; + size_t off = 0; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + bool finished = false; + + while (1) + { + uint16_t msize; + ssize_t ret; + + ret = recv (dh->sock, + &buf[off], + sizeof (buf) - off, + (finished && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (finished); + GNUNET_assert (0 == off); + return ec; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + break; + } + if (0 == ret) + { + GNUNET_break (0 == off); + if (! finished) + ec = TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; + return ec; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + switch (ntohs (hdr->type)) + { + case TALER_HELPER_RSA_MT_RES_SIGNATURE: + if (msize < sizeof (struct TALER_CRYPTO_SignResponse)) + { + GNUNET_break_op (0); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + if (finished) + { + GNUNET_break_op (0); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + { + const struct TALER_CRYPTO_SignResponse *sr = + (const struct TALER_CRYPTO_SignResponse *) buf; + struct GNUNET_CRYPTO_RsaSignature *rsa_signature; + struct GNUNET_CRYPTO_BlindedSignature *blind_sig; + + rsa_signature = GNUNET_CRYPTO_rsa_signature_decode ( + &sr[1], + msize - sizeof (*sr)); + if (NULL == rsa_signature) + { + GNUNET_break_op (0); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received signature\n"); + ec = TALER_EC_NONE; + finished = true; + blind_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + blind_sig->cipher = GNUNET_CRYPTO_BSA_RSA; + blind_sig->rc = 1; + blind_sig->details.blinded_rsa_signature = rsa_signature; + bs->blinded_sig = blind_sig; + break; + } + case TALER_HELPER_RSA_MT_RES_SIGN_FAILURE: + if (msize != sizeof (struct TALER_CRYPTO_SignFailure)) + { + GNUNET_break_op (0); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + { + const struct TALER_CRYPTO_SignFailure *sf = + (const struct TALER_CRYPTO_SignFailure *) buf; + + ec = (enum TALER_ErrorCode) ntohl (sf->ec); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Signing failed!\n"); + finished = true; + break; + } + case TALER_HELPER_RSA_MT_AVAIL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received new key!\n"); + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_RSA_MT_PURGE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received revocation!\n"); + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_RSA_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Synchronized add odd time with RSA helper!\n"); + dh->synced = true; + break; + default: + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %u\n", + ntohs (hdr->type)); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } /* while(1) */ +end: + if (finished) +// DONAU_blinded_donation_unit_sig_free(bs); + return ec; + } +} + + +enum TALER_ErrorCode +DONAU_CRYPTO_helper_rsa_batch_sign ( + struct DONAU_CRYPTO_RsaDonationUnitHelper *dh, + unsigned int rsrs_length, + const struct TALER_CRYPTO_RsaSignRequest rsrs[static rsrs_length], + struct DONAU_BlindedDonationUnitSignature bss[static rsrs_length]) +{ + enum TALER_ErrorCode ec = TALER_EC_INVALID; + unsigned int rpos; + unsigned int rend; + unsigned int wpos; + + memset (bss, + 0, + sizeof (*bss) * rsrs_length); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting signature process\n"); + if (GNUNET_OK != + try_connect (dh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to helper\n"); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting %u signatures\n", + rsrs_length); + rpos = 0; + rend = 0; + wpos = 0; + while (rpos < rsrs_length) + { + unsigned int mlen = sizeof (struct TALER_CRYPTO_BatchSignRequest); + + while ( (rend < rsrs_length) && + (mlen + + sizeof (struct TALER_CRYPTO_SignRequest) + + rsrs[rend].msg_size < UINT16_MAX) ) + { + mlen += sizeof (struct TALER_CRYPTO_SignRequest) + rsrs[rend].msg_size; + rend++; + } + { + char obuf[mlen] GNUNET_ALIGN; + struct TALER_CRYPTO_BatchSignRequest *bsr + = (struct TALER_CRYPTO_BatchSignRequest *) obuf; + void *wbuf; + + bsr->header.type = htons (TALER_HELPER_RSA_MT_REQ_BATCH_SIGN); + bsr->header.size = htons (mlen); + bsr->batch_size = htonl (rend - rpos); + wbuf = &bsr[1]; + for (unsigned int i = rpos; i<rend; i++) + { + struct TALER_CRYPTO_SignRequest *sr = wbuf; + const struct TALER_CRYPTO_RsaSignRequest *rsr = &rsrs[i]; + + sr->header.type = htons (TALER_HELPER_RSA_MT_REQ_SIGN); + sr->header.size = htons (sizeof (*sr) + rsr->msg_size); + sr->reserved = htonl (0); + sr->h_rsa = *rsr->h_rsa; + GNUNET_memcpy (&sr[1], + rsr->msg, + rsr->msg_size); + wbuf += sizeof (*sr) + rsr->msg_size; + } + GNUNET_assert (wbuf == &obuf[mlen]); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending batch request [%u-%u)\n", + rpos, + rend); + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + obuf, + sizeof (obuf))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + } + rpos = rend; + { + char buf[UINT16_MAX]; + size_t off = 0; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + bool finished = false; + + while (1) + { + uint16_t msize; + ssize_t ret; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting reply at %u (up to %u)\n", + wpos, + rend); + ret = recv (dh->sock, + &buf[off], + sizeof (buf) - off, + (finished && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (finished); + GNUNET_assert (0 == off); + break; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + break; + } + if (0 == ret) + { + GNUNET_break (0 == off); + if (! finished) + ec = TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; + if (TALER_EC_NONE == ec) + break; + return ec; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + switch (ntohs (hdr->type)) + { + case TALER_HELPER_RSA_MT_RES_SIGNATURE: + if (msize < sizeof (struct TALER_CRYPTO_SignResponse)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + if (finished) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_SignResponse *sr = + (const struct TALER_CRYPTO_SignResponse *) buf; + struct GNUNET_CRYPTO_RsaSignature *rsa_signature; + struct GNUNET_CRYPTO_BlindedSignature *blind_sig; + + rsa_signature = GNUNET_CRYPTO_rsa_signature_decode ( + &sr[1], + msize - sizeof (*sr)); + if (NULL == rsa_signature) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %u signature\n", + wpos); + blind_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + blind_sig->cipher = GNUNET_CRYPTO_BSA_RSA; + blind_sig->rc = 1; + blind_sig->details.blinded_rsa_signature = rsa_signature; + bss[wpos].blinded_sig = blind_sig; + wpos++; + if (wpos == rend) + { + if (TALER_EC_INVALID == ec) + ec = TALER_EC_NONE; + finished = true; + } + break; + } + case TALER_HELPER_RSA_MT_RES_SIGN_FAILURE: + if (msize != sizeof (struct TALER_CRYPTO_SignFailure)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_SignFailure *sf = + (const struct TALER_CRYPTO_SignFailure *) buf; + + ec = (enum TALER_ErrorCode) ntohl (sf->ec); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Signing %u failed with status %d!\n", + wpos, + ec); + wpos++; + if (wpos == rend) + { + finished = true; + } + break; + } + case TALER_HELPER_RSA_MT_AVAIL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received new key!\n"); + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_RSA_MT_PURGE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received revocation!\n"); + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_RSA_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Synchronized add odd time with RSA helper!\n"); + dh->synced = true; + break; + default: + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %u\n", + ntohs (hdr->type)); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } /* while(1) */ + } /* scope */ + } /* while (rpos < rsrs_length) */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Existing with %u signatures and status %d\n", + wpos, + ec); + return ec; +} + + +void +DONAU_CRYPTO_helper_rsa_revoke ( + struct DONAU_CRYPTO_RsaDonationUnitHelper *dh, + const struct TALER_RsaPubHashP *h_rsa) +{ + struct TALER_CRYPTO_RevokeRequest rr = { + .header.size = htons (sizeof (rr)), + .header.type = htons (TALER_HELPER_RSA_MT_REQ_REVOKE), + .h_rsa = *h_rsa + }; + + if (GNUNET_OK != + try_connect (dh)) + return; /* give up */ + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + &rr, + sizeof (rr))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Requested revocation of donation unit key %s\n", + GNUNET_h2s (&h_rsa->hash)); +} + + +void +DONAU_CRYPTO_helper_rsa_disconnect ( + struct DONAU_CRYPTO_RsaDonationUnitHelper *dh) +{ + if (-1 != dh->sock) + do_disconnect (dh); + GNUNET_free (dh); +} + + +/* end of crypto_helper_donation_unit.c */