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:
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));
+}