merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 977ff500b47ef6a4abcdbb08ef3a07747144c143
parent 44edf7ea7f488ad0548c73e9f7f39ae0ed570330
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 31 Aug 2025 11:37:41 +0200

add basic MFA data types

Diffstat:
Msrc/include/taler_merchant_util.h | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/util/mfa.c | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 326 insertions(+), 0 deletions(-)

diff --git a/src/include/taler_merchant_util.h b/src/include/taler_merchant_util.h @@ -34,6 +34,155 @@ const struct GNUNET_OS_ProjectData * TALER_MERCHANT_project_data (void); + +/** + * Channel used to transmit MFA authorization request. + */ +enum TALER_MERCHANT_MFA_Channel +{ + /** + * No MFA channel. + */ + TALER_MERCHANT_MFA_CHANNEL_NONE = 0, + + /** + * SMS ("sms") + */ + TALER_MERCHANT_MFA_CHANNEL_SMS = 1, + + /** + * E-mail ("email") + */ + TALER_MERCHANT_MFA_CHANNEL_EMAIL, + + /** + * TOTP ("totp"). (Not yet implemented.) + */ + TALER_MERCHANT_MFA_CHANNEL_TOTP +}; + + +/** + * Types of critical operations of a merchant backend that may require + * multi-factor authorization. + */ +enum TALER_MERCHANT_MFA_CriticalOperation +{ + /** + * Marker used to indicate that this is NOT a critical operation. + */ + TALER_MERCHANT_MFA_CO_NONE = 0, + + /** + * Instance provisioning ("instance_provision"). + */ + TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION, + + /** + * Bank account configuration or reconfiguratio ("account_config"). + */ + TALER_MERCHANT_MFA_CO_ACCOUNT_CONFIGURATION, + + /** + * Authentication configuration change ("auth_config"). + */ + TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION, + + /** + * Instance deletion ("instance_deletion"). + */ + TALER_MERCHANT_MFA_CO_INSTANCE_DELETION, + + /** + * Authentication token creation ("auth_token_creation"). + */ + TALER_MERCHANT_MFA_CO_AUTH_TOKEN_CREATION + + +}; + + +/** + * Convert critical operation enumeration value to string. + * + * @param co input to convert + * @return operation value as string + */ +const char * +TALER_MERCHANT_MFA_co_to_string ( + enum TALER_MERCHANT_MFA_CriticalOperation co); + + +/** + * Convert string to critical operation enumeration value. + * + * @param str input to convert + * @return #TALER_MERCHANT_MFA_CO_NONE on failure + */ +enum TALER_MERCHANT_MFA_CriticalOperation +TALER_MERCHANT_MFA_co_from_string (const char *str); + + +/** + * Convert MFA channel enumeration value to string. + * + * @param ch input to convert + * @return operation value as string + */ +const char * +TALER_MERCHANT_MFA_channel_to_string ( + enum TALER_MERCHANT_MFA_Channel ch); + + +/** + * Convert string to MFA channel enumeration value. + * + * @param str input to convert + * @return #TALER_MERCHANT_MFA_CHANNEL_NONE on failure + */ +enum TALER_MERCHANT_MFA_Channel +TALER_MERCHANT_MFA_channel_from_string (const char *str); + + +/** + * @brief Salted hash of a body for a request that required MFA. + */ +struct TALER_MERCHANT_MFA_BodyHash +{ + /** + * Hash of the body and salt. + */ + struct GNUNET_ShortHashCode hash; +}; + + +/** + * @brief Salt used when computing a `struct TALER_MERCHANT_MFA_BodyHash` + */ +struct TALER_MERCHANT_MFA_BodySalt +{ + /** + * Salt. + */ + uint64_t salt[128 / 64]; +}; + + +/** + * Hash the given request @a body with the given @a salt to + * produce @a h_body for MFA checks. + * + * @param body HTTP request body + * @param salt salt to use + * @param h_body resulting hash + */ +void +TALER_MERCHANT_mfa_body_hash ( + const json_t *body, + const struct TALER_MERCHANT_MFA_BodySalt *salt, + struct TALER_MERCHANT_MFA_BodyHash *h_body); + + /** * Possible versions of the contract terms. */ diff --git a/src/util/mfa.c b/src/util/mfa.c @@ -0,0 +1,177 @@ +/* + This file is part of TALER + Copyright (C) 2025 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/mfa.c + * @brief helper functions for MFA processing + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_json_lib.h> +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include <taler_merchant_util.h> +#include <jansson.h> + +/** + * Mapping from critical operation enum to string. + */ +static const char *co_strings[] = { + [TALER_MERCHANT_MFA_CO_NONE] = NULL, + [TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION] = "instance_provision", + [TALER_MERCHANT_MFA_CO_ACCOUNT_CONFIGURATION] = "account_config", + [TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION] = "auth_config", + [TALER_MERCHANT_MFA_CO_INSTANCE_DELETION] = "instance_deletion", + [TALER_MERCHANT_MFA_CO_AUTH_TOKEN_CREATION] = "auth_token_creation" +}; + +/** + * Mapping from MFA channel enum to string. + */ +static const char *channel_strings[] = { + [TALER_MERCHANT_MFA_CHANNEL_NONE] = NULL, + [TALER_MERCHANT_MFA_CHANNEL_SMS] = "sms", + [TALER_MERCHANT_MFA_CHANNEL_EMAIL] = "email", + [TALER_MERCHANT_MFA_CHANNEL_TOTP] = "totp" +}; + + +/** + * Convert critical operation enumeration value to string. + * + * @param co input to convert + * @return operation value as string + */ +const char * +TALER_MERCHANT_MFA_co_to_string ( + enum TALER_MERCHANT_MFA_CriticalOperation co) +{ + if ( (co < 0) || + (co >= sizeof (co_strings) / sizeof (co_strings[0])) ) + { + GNUNET_break (0); + return NULL; + } + return co_strings[co]; +} + + +/** + * Convert string to critical operation enumeration value. + * + * @param str input to convert + * @return #TALER_MERCHANT_MFA_CO_NONE on failure + */ +enum TALER_MERCHANT_MFA_CriticalOperation +TALER_MERCHANT_MFA_co_from_string (const char *str) +{ + if (NULL == str) + return TALER_MERCHANT_MFA_CO_NONE; + for (unsigned int i = 0; + i < sizeof (co_strings) / sizeof (co_strings[0]); + i++) + { + if (0 == strcmp (str, + co_strings[i])) + return (enum TALER_MERCHANT_MFA_CriticalOperation) i; + } + GNUNET_break (0); + return TALER_MERCHANT_MFA_CO_NONE; +} + + +/** + * Convert MFA channel enumeration value to string. + * + * @param ch input to convert + * @return operation value as string + */ +const char * +TALER_MERCHANT_MFA_channel_to_string ( + enum TALER_MERCHANT_MFA_Channel ch) +{ + if ( (ch < 0) || + (ch >= sizeof (channel_strings) / sizeof (channel_strings[0])) ) + { + GNUNET_break (0); + return NULL; + } + return channel_strings[ch]; +} + + +/** + * Convert string to MFA channel enumeration value. + * + * @param str input to convert + * @return #TALER_MERCHANT_MFA_CHANNEL_NONE on failure + */ +enum TALER_MERCHANT_MFA_Channel +TALER_MERCHANT_MFA_channel_from_string (const char *str) +{ + if (NULL == str) + return TALER_MERCHANT_MFA_CHANNEL_NONE; + for (unsigned int i = 0; + i < sizeof (channel_strings) / sizeof (channel_strings[0]); + i++) + { + if (0 == strcmp (str, + channel_strings[i])) + return (enum TALER_MERCHANT_MFA_Channel) i; + } + GNUNET_break (0); + return TALER_MERCHANT_MFA_CHANNEL_NONE; +} + + +/** + * Hash the given request @a body with the given @a salt to + * produce @a h_body for MFA checks. + * + * @param body HTTP request body + * @param salt salt to use + * @param h_body resulting hash + */ +void +TALER_MERCHANT_mfa_body_hash ( + const json_t *body, + const struct TALER_MERCHANT_MFA_BodySalt *salt, + struct TALER_MERCHANT_MFA_BodyHash *h_body) +{ + char *json_str; + struct GNUNET_HashCode hash; + struct GNUNET_HashContext *hc; + + json_str = json_dumps (body, + JSON_COMPACT | JSON_SORT_KEYS); + GNUNET_assert (NULL != json_str); + hc = GNUNET_CRYPTO_hash_context_start (); + GNUNET_CRYPTO_hash_context_read (hc, + salt, + sizeof (*salt)); + GNUNET_CRYPTO_hash_context_read (hc, + json_str, + strlen (json_str)); + GNUNET_CRYPTO_hash_context_finish (hc, + &hash); + GNUNET_free (json_str); + + GNUNET_static_assert (sizeof (*h_body) <= sizeof (hash)); + /* Truncate to short hash */ + memcpy (&h_body->hash, + &hash, + sizeof (*h_body)); +}