diff --git a/examples/demo/client/wh_demo_client_all.c b/examples/demo/client/wh_demo_client_all.c
index 15ee86d7..688e5dac 100644
--- a/examples/demo/client/wh_demo_client_all.c
+++ b/examples/demo/client/wh_demo_client_all.c
@@ -1,6 +1,7 @@
#include "wh_demo_client_wctest.h"
#include "wh_demo_client_wcbench.h"
#include "wh_demo_client_nvm.h"
+#include "wh_demo_client_auth.h"
#include "wh_demo_client_keystore.h"
#include "wh_demo_client_crypto.h"
#include "wh_demo_client_secboot.h"
@@ -10,6 +11,21 @@
int wh_DemoClient_All(whClientContext* clientContext)
{
int rc = 0;
+ whUserId userId = WH_USER_ID_INVALID;
+ /* Auth demos */
+ rc = wh_DemoClient_Auth(clientContext);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Log in as an admin user for the rest of the tests */
+ if (wh_Client_AuthLogin(clientContext, WH_AUTH_METHOD_PIN, "admin", "1234",
+ 4, &rc, &userId) != 0) {
+ return -1;
+ }
+ if (rc != 0) {
+ return rc;
+ }
/* wolfCrypt test and benchmark */
#ifdef WH_DEMO_WCTEST
diff --git a/examples/demo/client/wh_demo_client_auth.c b/examples/demo/client/wh_demo_client_auth.c
new file mode 100644
index 00000000..42c43d5b
--- /dev/null
+++ b/examples/demo/client/wh_demo_client_auth.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2026 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+
+
+#include
+#include
+
+#include "wolfhsm/wh_error.h"
+#include "wolfhsm/wh_client.h"
+#include "wolfhsm/wh_auth.h"
+#include "wolfhsm/wh_message.h"
+
+#include "wh_demo_client_auth.h"
+#include "wh_demo_client_crypto.h"
+
+static int wh_DemoClient_AuthPin(whClientContext* clientContext)
+{
+ int rc = 0;
+ int32_t serverRc = 0;
+ const uint8_t pin[] = "1234"; /* demo PIN */
+ const uint8_t newPin[] = "5678"; /* new PIN */
+ whUserId userId = WH_USER_ID_INVALID;
+ whUserId adminUserId = WH_USER_ID_INVALID;
+ whAuthPermissions out_permissions;
+
+ /* give permissions for everything */
+ memset(&out_permissions, 0xFF, sizeof(whAuthPermissions));
+
+ if (clientContext == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* login as the admin and add a new user */
+ rc = wh_Client_AuthLogin(clientContext,
+ WH_AUTH_METHOD_PIN, "admin", "1234", 4, &serverRc, &adminUserId);
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Failed to login as admin: %d\n", rc);
+ return rc;
+ }
+ if (serverRc != 0) {
+ printf("[AUTH-DEMO] Server-side error logging in as admin: %d\n",
+ (int)serverRc);
+ return (int)serverRc;
+ }
+
+ memset(&out_permissions, 0, sizeof(whAuthPermissions));
+ rc = wh_Client_AuthUserAdd(clientContext, "demo", out_permissions,
+ WH_AUTH_METHOD_PIN, pin, (uint16_t)(sizeof(pin) - 1),
+ &serverRc, &userId);
+ if (rc != 0 || serverRc != 0) {
+ printf("[AUTH-DEMO] Failed to add user: %d, server error %d\n", rc,
+ serverRc);
+ return rc;
+ }
+
+ rc = wh_Client_AuthLogout(clientContext, adminUserId, &serverRc);
+ if (rc != 0 || serverRc != 0) {
+ printf("[AUTH-DEMO] Failed to logout user: %d\n", rc);
+ return rc;
+ }
+
+ /* Log in as the newly created 'demo' user */
+ rc = wh_Client_AuthLogin(clientContext,
+ WH_AUTH_METHOD_PIN, "demo", pin,
+ (uint16_t)(sizeof(pin) - 1), &serverRc,
+ &userId);
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Login message failure, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (serverRc != 0) {
+ printf("[AUTH-DEMO] Server-side login failed, rc=%d.\n", (int)serverRc);
+ return (int)serverRc;
+ }
+
+ /* Update user credentials */
+ rc = wh_Client_AuthUserSetCredentials(clientContext, userId,
+ WH_AUTH_METHOD_PIN,
+ pin, (uint16_t)(sizeof(pin) - 1), /* current credentials */
+ newPin, (uint16_t)(sizeof(newPin) - 1), /* new credentials */
+ &serverRc);
+
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Failed to update credentials: %d\n", rc);
+ return rc;
+ }
+
+ if (serverRc != 0) {
+ printf("[AUTH-DEMO] Server-side error updating credentials: %d\n",
+ (int)serverRc);
+ return (int)serverRc;
+ }
+
+ /* logout the user */
+ rc = wh_Client_AuthLogout(clientContext, userId, &serverRc);
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Failed to logout user: %d\n", rc);
+ return rc;
+ }
+
+ if (serverRc != 0) {
+ printf("[AUTH-DEMO] Server-side error logging out user: %d\n",
+ (int)serverRc);
+ return (int)serverRc;
+ }
+
+ /* Verify old PIN no longer works */
+ rc = wh_Client_AuthLogin(clientContext,
+ WH_AUTH_METHOD_PIN,
+ "demo",
+ pin,
+ (uint16_t)(sizeof(pin) - 1),
+ &serverRc,
+ &userId);
+
+ if (rc == 0 && serverRc == 0) {
+ printf("[AUTH-DEMO] Old PIN still works (unexpected)\n");
+ }
+
+ /* Verify new PIN works */
+ rc = wh_Client_AuthLogin(clientContext,
+ WH_AUTH_METHOD_PIN,
+ "demo",
+ newPin,
+ (uint16_t)(sizeof(newPin) - 1),
+ &serverRc,
+ &userId);
+
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Client-side error with new PIN: %d\n", rc);
+ return rc;
+ }
+
+ if (serverRc != 0) {
+ printf("[AUTH-DEMO] Server-side error with new PIN: %d\n", (int)serverRc);
+ return (int)serverRc;
+ }
+
+ rc = wh_Client_AuthLogout(clientContext, userId, &serverRc);
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Failed to logout user: %d\n", rc);
+ return rc;
+ }
+ return rc;
+}
+
+#include "../../test/wh_test_cert_data.h"
+static int wh_DemoClient_AuthCertificate(whClientContext* clientContext)
+{
+ int rc = 0;
+ int32_t serverRc = 0;
+ whUserId userId = WH_USER_ID_INVALID;
+ whUserId adminUserId = WH_USER_ID_INVALID;
+ whAuthPermissions out_permissions;
+
+ /* Include test certificates - prefer wolfssl/certs_test.h if available,
+ * otherwise use test certificates from wh_test_cert_data.h */
+ const unsigned char* ca_cert;
+ uint16_t ca_cert_len;
+ const unsigned char* server_cert;
+ uint16_t server_cert_len;
+
+ /* Use INTERMEDIATE_A_CERT as the CA since it directly signs LEAF_A_CERT
+ * The chain is: ROOT_A_CERT -> INTERMEDIATE_A_CERT -> LEAF_A_CERT */
+ ca_cert = INTERMEDIATE_A_CERT;
+ ca_cert_len = (uint16_t)INTERMEDIATE_A_CERT_len;
+ server_cert = LEAF_A_CERT;
+ server_cert_len = (uint16_t)LEAF_A_CERT_len;
+
+ memset(&out_permissions, 0, sizeof(whAuthPermissions));
+
+ if (clientContext == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* login as the admin and add a new user with CA certificate */
+ rc = wh_Client_AuthLogin(clientContext,
+ WH_AUTH_METHOD_PIN,
+ "admin",
+ "1234", 4,
+ &serverRc,
+ &adminUserId);
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Failed to login as admin: %d\n", rc);
+ return rc;
+ }
+ if (serverRc != 0) {
+ printf("[AUTH-DEMO] Server-side error logging in as admin: %d\n",
+ (int)serverRc);
+ return (int)serverRc;
+ }
+
+ rc = wh_Client_AuthUserAdd(clientContext, "certuser", out_permissions,
+ WH_AUTH_METHOD_CERTIFICATE, ca_cert, ca_cert_len,
+ &serverRc, &userId);
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Failed to add user: %d\n", rc);
+ return rc;
+ }
+
+ if (serverRc != 0) {
+ printf("[AUTH-DEMO] Server-side error adding user: %d\n",
+ (int)serverRc);
+ return (int)serverRc;
+ }
+
+ rc = wh_Client_AuthLogout(clientContext, adminUserId, &serverRc);
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Failed to logout user: %d\n", rc);
+ return rc;
+ }
+
+ /* Authenticate user with server certificate */
+ rc = wh_Client_AuthLogin(clientContext,
+ WH_AUTH_METHOD_CERTIFICATE,
+ "certuser",
+ server_cert,
+ server_cert_len,
+ &serverRc,
+ &userId);
+ if (rc != 0 || serverRc != 0) {
+ printf("[AUTH-DEMO] Error logging in rc=%d server rc = %d.\n", rc,
+ serverRc);
+ return rc;
+ }
+
+ /* Try doing a crypto operation, with permissions all 0, this should fail */
+ rc = wh_DemoClient_CryptoAesCbc(clientContext);
+ if (rc == 0 || rc == WH_ERROR_OK) {
+ /* found success when should have failed */
+ printf("[AUTH-DEMO] Crypto operation should have failed\n");
+ return -1;
+ }
+
+ rc = wh_Client_AuthLogout(clientContext, userId, &serverRc);
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Failed to logout user: %d\n", rc);
+ return rc;
+ }
+ return rc;
+}
+
+
+static int wh_DemoClient_AuthUserDelete(whClientContext* clientContext)
+{
+ int rc = 0;
+ int32_t serverRc = 0;
+ whUserId userId = WH_USER_ID_INVALID;
+ whUserId adminUserId = WH_USER_ID_INVALID;
+ whAuthPermissions permissions;
+
+ rc = wh_Client_AuthLogin(clientContext,
+ WH_AUTH_METHOD_PIN,
+ "admin",
+ "1234", 4,
+ &serverRc,
+ &adminUserId);
+ if (rc != 0) {
+ return rc;
+ }
+ if (serverRc != 0) {
+ return (int)serverRc;
+ }
+
+ rc = wh_Client_AuthUserGet(clientContext, "certuser", &serverRc, &userId,
+ &permissions);
+ if (rc != 0) {
+ return rc;
+ }
+ if (serverRc != 0) {
+ printf("[AUTH-DEMO] Server-side error %d while getting user: %d\n",
+ (int)serverRc, userId);
+ return (int)serverRc;
+ }
+
+ rc = wh_Client_AuthUserDelete(clientContext, userId, &serverRc);
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Failed to delete user: %d\n", rc);
+ return rc;
+ }
+ if (serverRc != 0) {
+ printf("[AUTH-DEMO] Server-side error deleting user: %d\n",
+ (int)serverRc);
+ return (int)serverRc;
+ }
+
+ rc = wh_Client_AuthLogout(clientContext, adminUserId, &serverRc);
+ if (rc != 0) {
+ return rc;
+ }
+ if (serverRc != 0) {
+ return (int)serverRc;
+ }
+
+ return rc;
+}
+
+
+static int wh_DemoClient_AuthUserSetPermissions(whClientContext* clientContext)
+{
+ int rc = 0;
+ int32_t serverRc = 0;
+ whUserId userId = WH_USER_ID_INVALID;
+ whUserId adminUserId = WH_USER_ID_INVALID;
+ whAuthPermissions permissions;
+
+ rc = wh_Client_AuthLogin(clientContext,
+ WH_AUTH_METHOD_PIN,
+ "admin",
+ "1234", 4,
+ &serverRc,
+ &adminUserId);
+ if (rc != 0) {
+ return rc;
+ }
+ if (serverRc != 0) {
+ printf("[AUTH-DEMO] Error %d while logging in as admin: %d\n",
+ (int)serverRc, adminUserId);
+ return (int)serverRc;
+ }
+
+ rc = wh_Client_AuthUserGet(clientContext, "demo", &serverRc, &userId,
+ &permissions);
+ if (rc != 0) {
+ printf("[AUTH-DEMO] Failed to get user: %d\n", rc);
+ return rc;
+ }
+ if (serverRc != 0) {
+ printf("[AUTH-DEMO] Server-side error %d while getting user: %d\n",
+ (int)serverRc, userId);
+ return (int)serverRc;
+ }
+
+ /* Enable CRYPTO group and all CRYPTO actions */
+ memset(&permissions, 0, sizeof(permissions));
+ permissions.groupPermissions |= WH_MESSAGE_GROUP_CRYPTO;
+
+ /* Enable all CRYPTO actions by setting all bits in all words, an example of
+ * a CRYPTO action is WC_ALGO_TYPE_CIPHER or WC_ALGO_TYPE_PK */
+ {
+ int groupIndex = (WH_MESSAGE_GROUP_CRYPTO >> 8) & 0xFF;
+ int wordIndex;
+ /* Set all action bits for CRYPTO group (allows all actions) */
+ for (wordIndex = 0; wordIndex < WH_AUTH_ACTION_WORDS; wordIndex++) {
+ permissions.actionPermissions[groupIndex][wordIndex] = 0xFFFFFFFF;
+ }
+ }
+
+ rc = wh_Client_AuthUserSetPermissions(clientContext, userId, permissions,
+ &serverRc);
+ if (rc != 0 || serverRc != 0) {
+ printf("[AUTH-DEMO] Failed to set permissions: %d, server error %d\n",
+ rc, serverRc);
+ return rc != 0 ? rc : (int)serverRc;
+ }
+
+ rc = wh_Client_AuthUserGet(clientContext, "demo", &serverRc, &userId, &permissions);
+ if (rc != 0 || serverRc != 0) {
+ printf("[AUTH-DEMO] Failed to get user: %d, server error %d\n", rc,
+ serverRc);
+ return (rc != 0) ? rc : (int)serverRc;
+ }
+
+ rc = wh_Client_AuthLogout(clientContext, adminUserId, &serverRc);
+ if (serverRc != 0) {
+ return (int)serverRc;
+ }
+
+ return rc;
+}
+
+
+int wh_DemoClient_Auth(whClientContext* clientContext)
+{
+ int rc = 0;
+
+ printf("[AUTH-DEMO] Starting authentication demo...\n");
+ rc = wh_DemoClient_AuthCertificate(clientContext);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = wh_DemoClient_AuthPin(clientContext);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = wh_DemoClient_AuthUserDelete(clientContext);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = wh_DemoClient_AuthUserSetPermissions(clientContext);
+ if (rc != 0) {
+ return rc;
+ }
+ printf("[AUTH-DEMO] Authentication demo completed.\n");
+ return rc;
+}
diff --git a/examples/demo/client/wh_demo_client_auth.h b/examples/demo/client/wh_demo_client_auth.h
new file mode 100644
index 00000000..0eef61e5
--- /dev/null
+++ b/examples/demo/client/wh_demo_client_auth.h
@@ -0,0 +1,12 @@
+#ifndef DEMO_CLIENT_AUTH_H_
+#define DEMO_CLIENT_AUTH_H_
+
+#include "wolfhsm/wh_client.h"
+#include "wolfhsm/wh_auth.h"
+
+/*
+ * Simple Auth Manager demo entry point.
+ */
+int wh_DemoClient_Auth(whClientContext* clientContext);
+
+#endif /* !DEMO_CLIENT_AUTH_H_ */
diff --git a/examples/posix/wh_posix_server/Makefile b/examples/posix/wh_posix_server/Makefile
index c5a86cd9..9805b1de 100644
--- a/examples/posix/wh_posix_server/Makefile
+++ b/examples/posix/wh_posix_server/Makefile
@@ -59,6 +59,12 @@ endif
# Set to @ if you want to suppress command echo
CMD_ECHO ?=
+# Add code coverage option
+ifeq ($(COVERAGE),1)
+ CFLAGS += --coverage
+ LDFLAGS += --coverage
+endif
+
# Check if DEBUG is set to 1 and append debug flags
ifeq ($(DEBUG),1)
DBGFLAGS = -ggdb -g3
diff --git a/examples/posix/wh_posix_server/wh_posix_server.c b/examples/posix/wh_posix_server/wh_posix_server.c
index 0f0d9bca..e272e642 100644
--- a/examples/posix/wh_posix_server/wh_posix_server.c
+++ b/examples/posix/wh_posix_server/wh_posix_server.c
@@ -414,6 +414,14 @@ int main(int argc, char** argv)
WOLFHSM_CFG_PRINTF("Failed to initialize NVM: %d\n", rc);
return rc;
}
+
+ /* Auth Manager Configuration */
+ rc = wh_PosixServer_ExampleAuthConfig(s_conf);
+ if (rc != WH_ERROR_OK) {
+ WOLFHSM_CFG_PRINTF("Failed to initialize Auth Manager: %d\n", rc);
+ return rc;
+ }
+
#if !defined(WOLFHSM_CFG_NO_CRYPTO)
/* Crypto context */
whServerCryptoContext crypto[1] = {{
diff --git a/examples/posix/wh_posix_server/wh_posix_server_cfg.c b/examples/posix/wh_posix_server/wh_posix_server_cfg.c
index 754a0b82..0f1d9577 100644
--- a/examples/posix/wh_posix_server/wh_posix_server_cfg.c
+++ b/examples/posix/wh_posix_server/wh_posix_server_cfg.c
@@ -14,12 +14,14 @@
#include "wolfhsm/wh_nvm.h"
#include "wolfhsm/wh_nvm_flash.h"
#include "wolfhsm/wh_flash_ramsim.h"
+#include "wolfhsm/wh_auth.h"
#include "port/posix/posix_transport_shm.h"
#include "port/posix/posix_transport_tcp.h"
#ifdef WOLFHSM_CFG_TLS
#include "port/posix/posix_transport_tls.h"
#endif
+#include "port/posix/posix_auth.h"
posixTransportShmConfig shmConfig;
posixTransportTcpConfig tcpConfig;
@@ -650,3 +652,78 @@ int wh_PosixServer_ExampleNvmConfig(void* conf, const char* nvmInitFilePath)
return WH_ERROR_OK;
}
+
+
+/* Default auth callback structure */
+static whAuthCb default_auth_cb = {
+ .Init = posixAuth_Init,
+ .Cleanup = posixAuth_Cleanup,
+ .Login = posixAuth_Login,
+ .Logout = posixAuth_Logout,
+ .CheckRequestAuthorization = posixAuth_CheckRequestAuthorization,
+ .CheckKeyAuthorization = posixAuth_CheckKeyAuthorization,
+ .UserAdd = posixAuth_UserAdd,
+ .UserDelete = posixAuth_UserDelete,
+ .UserSetPermissions = posixAuth_UserSetPermissions,
+ .UserGet = posixAuth_UserGet,
+ .UserSetCredentials = posixAuth_UserSetCredentials};
+static whAuthContext auth_ctx = {0};
+
+/**
+ * @brief Configure a default auth context for the server
+ *
+ * This function sets up a basic auth context with example implementations that
+ * allow all operations. This is suitable for development and testing.
+ * For production use, a proper auth backend should be implemented.
+ *
+ * @param[in] conf Pointer to the server configuration
+ * @return int Returns WH_ERROR_OK on success, or a negative error code on
+ * failure
+ */
+int wh_PosixServer_ExampleAuthConfig(void* conf)
+{
+ int rc;
+ whServerConfig* s_conf = (whServerConfig*)conf;
+ static void* auth_backend_context =
+ NULL; /* No backend context needed for stubs */
+ static whAuthConfig auth_config = {0};
+ whAuthPermissions permissions;
+ uint16_t out_user_id;
+ int i;
+
+ if (s_conf == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Set up the auth config with default callbacks */
+ auth_config.cb = &default_auth_cb;
+ auth_config.context = auth_backend_context;
+
+ /* Initialize the auth context */
+ rc = wh_Auth_Init(&auth_ctx, &auth_config);
+ if (rc != WH_ERROR_OK) {
+ WOLFHSM_CFG_PRINTF("Failed to initialize Auth Manager: %d\n", rc);
+ return rc;
+ }
+
+ /* Set the auth context in the server configuration */
+ s_conf->auth = &auth_ctx;
+
+ WOLFHSM_CFG_PRINTF(
+ "Default auth context configured (stub implementation)\n");
+
+ /* Add an admin user with permissions for everything */
+ memset(&permissions, 0xFF, sizeof(whAuthPermissions));
+ permissions.keyIdCount = 0;
+ for (i = 0; i < WH_AUTH_MAX_KEY_IDS; i++) {
+ permissions.keyIds[i] = 0;
+ }
+ rc = posixAuth_UserAdd(&auth_ctx, "admin", &out_user_id, permissions,
+ WH_AUTH_METHOD_PIN, "1234", 4);
+ if (rc != WH_ERROR_OK) {
+ WOLFHSM_CFG_PRINTF("Failed to add admin user: %d\n", rc);
+ return rc;
+ }
+
+ return WH_ERROR_OK;
+}
diff --git a/examples/posix/wh_posix_server/wh_posix_server_cfg.h b/examples/posix/wh_posix_server/wh_posix_server_cfg.h
index 1b95d26f..d25852a7 100644
--- a/examples/posix/wh_posix_server/wh_posix_server_cfg.h
+++ b/examples/posix/wh_posix_server/wh_posix_server_cfg.h
@@ -14,5 +14,6 @@ int wh_PosixServer_ExamplePskConfig(void* s_conf);
#endif /* WOLFHSM_CFG_TLS */
int wh_PosixServer_ExampleNvmConfig(void* conf, const char* nvmInitFilePath);
int wh_PosixServer_ExampleRamSimConfig(void* conf, uint8_t* memory);
+int wh_PosixServer_ExampleAuthConfig(void* conf);
#endif /* WH_POSIX_SERVER_CFG_H */
diff --git a/port/posix/posix_auth.c b/port/posix/posix_auth.c
new file mode 100644
index 00000000..4b7f839c
--- /dev/null
+++ b/port/posix/posix_auth.c
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2025 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+
+/* This contains a basic authentication implementation. */
+
+
+/* Pick up compile-time configuration */
+#include "wolfhsm/wh_settings.h"
+
+#include
+#include
+#include
+
+#include "wolfhsm/wh_common.h"
+#include "wolfhsm/wh_error.h"
+
+#include "wolfhsm/wh_message.h"
+#include "wolfhsm/wh_message_auth.h"
+#include "posix_auth.h"
+
+/* simple base user list */
+#define WH_AUTH_BASE_MAX_USERS 5
+#define WH_AUTH_BASE_MAX_CREDENTIALS_LEN 2048
+typedef struct whAuthBase_User {
+ whAuthUser user;
+ whAuthMethod method;
+ unsigned char credentials[WH_AUTH_BASE_MAX_CREDENTIALS_LEN];
+ uint16_t credentials_len;
+} whAuthBase_User;
+/* TODO: Thread safety - The global users array is not protected by any
+ * synchronization mechanism. In a multi-threaded environment, concurrent
+ * access could lead to race conditions. Consider adding appropriate locking
+ * mechanisms (mutex, rwlock) to protect concurrent access. */
+static whAuthBase_User users[WH_AUTH_BASE_MAX_USERS];
+
+#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO)
+#include
+#include
+#endif
+
+int posixAuth_Init(void* context, const void* config)
+{
+ (void)context;
+ (void)config;
+
+ return WH_ERROR_OK;
+}
+
+int posixAuth_Cleanup(void* context)
+{
+ (void)context;
+ return WH_ERROR_OK;
+}
+
+static whAuthBase_User* posixAuth_FindUser(const char* username)
+{
+ int i;
+ for (i = 0; i < WH_AUTH_BASE_MAX_USERS; i++) {
+ if (strcmp(users[i].user.username, username) == 0) {
+ return &users[i];
+ }
+ }
+ return NULL;
+}
+
+static whAuthBase_User* posixAuth_CheckPin(const char* username, const void* auth_data,
+ uint16_t auth_data_len)
+{
+ whAuthBase_User* found_user;
+ found_user = posixAuth_FindUser(username);
+ if (found_user != NULL && found_user->credentials_len == auth_data_len &&
+ memcmp(found_user->credentials, auth_data, auth_data_len) == 0) {
+ return found_user;
+ }
+ return NULL;
+}
+
+#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO)
+static int posixAuth_VerifyCertificate(whAuthBase_User* found_user,
+ const uint8_t* certificate,
+ uint16_t certificate_len)
+{
+ int rc = WH_ERROR_OK;
+ int err;
+ WOLFSSL_CERT_MANAGER* cm = NULL;
+ cm = wolfSSL_CertManagerNew();
+ if (cm == NULL) {
+ return WH_ERROR_ABORTED;
+ }
+ err = wolfSSL_CertManagerLoadCABuffer(cm, found_user->credentials,
+ found_user->credentials_len,
+ WOLFSSL_FILETYPE_ASN1);
+ if (err != WOLFSSL_SUCCESS) {
+ rc = WH_ERROR_ABORTED;
+ }
+ err = wolfSSL_CertManagerVerifyBuffer(cm, certificate, certificate_len,
+ WOLFSSL_FILETYPE_ASN1);
+ if (err != WOLFSSL_SUCCESS) {
+ rc = WH_ERROR_ABORTED;
+ }
+ wolfSSL_CertManagerFree(cm);
+ return rc;
+}
+
+static whAuthBase_User* posixAuth_CheckCertificate(const char* username,
+ const void* auth_data,
+ uint16_t auth_data_len)
+{
+ whAuthBase_User* found_user;
+ found_user = posixAuth_FindUser(username);
+ if (found_user != NULL &&
+ found_user->method == WH_AUTH_METHOD_CERTIFICATE &&
+ found_user->credentials_len > 0) {
+ if (posixAuth_VerifyCertificate(found_user, auth_data, auth_data_len) ==
+ WH_ERROR_OK) {
+ return found_user;
+ }
+ }
+ return NULL;
+}
+#endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !WOLFHSM_CFG_NO_CRYPTO */
+
+int posixAuth_Login(void* context, uint8_t client_id, whAuthMethod method,
+ const char* username, const void* auth_data,
+ uint16_t auth_data_len, uint16_t* out_user_id,
+ whAuthPermissions* out_permissions, int* loggedIn)
+{
+ whAuthBase_User* current_user = NULL;
+
+ if ((out_user_id == NULL) || (out_permissions == NULL) ||
+ (loggedIn == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ *loggedIn = 0;
+
+ (void)client_id;
+ switch (method) {
+ case WH_AUTH_METHOD_PIN:
+ current_user = posixAuth_CheckPin(username, auth_data, auth_data_len);
+ break;
+#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO)
+ case WH_AUTH_METHOD_CERTIFICATE:
+ current_user = posixAuth_CheckCertificate(username, auth_data, auth_data_len);
+ break;
+#endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !WOLFHSM_CFG_NO_CRYPTO */
+ default:
+ return WH_ERROR_BADARGS;
+ }
+
+ if (current_user != NULL) {
+ if (current_user->user.is_active) {
+ /* Can not be logged in if already logged in */
+ *loggedIn = 0;
+ }
+ else {
+ *loggedIn = 1;
+ *out_user_id = current_user->user.user_id;
+ current_user->user.is_active = true;
+ *out_permissions = current_user->user.permissions;
+ }
+ }
+
+ (void)context;
+ return WH_ERROR_OK;
+}
+
+int posixAuth_Logout(void* context, uint16_t current_user_id,
+ uint16_t user_id)
+{
+ whAuthBase_User* user;
+
+ if (user_id == WH_USER_ID_INVALID) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (user_id > WH_AUTH_BASE_MAX_USERS) {
+ return WH_ERROR_NOTFOUND;
+ }
+
+ /* @TODO there likely should be restrictions here on who can logout who */
+ (void)current_user_id;
+
+ user = &users[user_id - 1];
+ user->user.is_active = false;
+ (void)context;
+ return WH_ERROR_OK;
+}
+
+
+int posixAuth_CheckRequestAuthorization(void* context, uint16_t user_id,
+ uint16_t group, uint16_t action)
+{
+ int rc;
+
+ if (user_id == WH_USER_ID_INVALID) {
+ /* allow user login request attempt and comm */
+ if (group == WH_MESSAGE_GROUP_COMM ||
+ (group == WH_MESSAGE_GROUP_AUTH &&
+ action == WH_MESSAGE_AUTH_ACTION_LOGIN)) {
+ rc = WH_ERROR_OK;
+ }
+ else {
+ rc = WH_ERROR_ACCESS;
+ }
+ }
+ else {
+ int groupIndex = (group >> 8) & 0xFF;
+ whAuthBase_User* user;
+
+ if (user_id > WH_AUTH_BASE_MAX_USERS) {
+ return WH_ERROR_ACCESS;
+ }
+ user = &users[user_id - 1];
+
+ /* check if user has permissions for the group and action */
+
+ /* some operations a user logged in should by default have access to;
+ * - logging out
+ * - updating own credentials */
+ if (group == WH_MESSAGE_GROUP_AUTH &&
+ (action == WH_MESSAGE_AUTH_ACTION_LOGOUT ||
+ action == WH_MESSAGE_AUTH_ACTION_USER_SET_CREDENTIALS)) {
+ rc = WH_ERROR_OK;
+ }
+ else {
+ if (user->user.permissions.groupPermissions & group) {
+ /* Check if action is within supported range */
+ if (action < WH_AUTH_ACTIONS_PER_GROUP) {
+ /* Get word index and bitmask for this action */
+ uint32_t wordAndBit = WH_AUTH_ACTION_TO_WORD_AND_BIT(action);
+ uint32_t wordIndex = WH_AUTH_ACTION_WORD(wordAndBit);
+ uint32_t bitmask = WH_AUTH_ACTION_BIT(wordAndBit);
+
+ if (wordIndex < WH_AUTH_ACTION_WORDS &&
+ (user->user.permissions.actionPermissions[groupIndex]
+ [wordIndex] &
+ bitmask)) {
+ rc = WH_ERROR_OK;
+ }
+ else {
+ rc = WH_ERROR_ACCESS;
+ }
+ }
+ else {
+ rc = WH_ERROR_ACCESS;
+ }
+ }
+ else {
+ rc = WH_ERROR_ACCESS;
+ }
+ }
+ }
+
+ (void)context;
+ return rc;
+}
+
+/* authorization check on key usage after the request has been parsed and before
+ * the action is done */
+int posixAuth_CheckKeyAuthorization(void* context, uint16_t user_id,
+ uint32_t key_id, uint16_t action)
+{
+ int rc = WH_ERROR_ACCESS;
+ int i;
+ whAuthBase_User* user;
+
+ if (user_id == WH_USER_ID_INVALID) {
+ return WH_ERROR_ACCESS;
+ }
+
+ if (user_id > WH_AUTH_BASE_MAX_USERS) {
+ return WH_ERROR_NOTFOUND;
+ }
+
+ user = &users[user_id - 1];
+
+ if (user->user.user_id == WH_USER_ID_INVALID) {
+ return WH_ERROR_NOTFOUND;
+ }
+
+ /* Check if the requested key_id is in the user's keyIds array */
+ for (i = 0;
+ i < user->user.permissions.keyIdCount && i < WH_AUTH_MAX_KEY_IDS;
+ i++) {
+ if (user->user.permissions.keyIds[i] == key_id) {
+ rc = WH_ERROR_OK;
+ break;
+ }
+ }
+
+ (void)context;
+ (void)action; /* Action could be used for future fine-grained key access
+ control */
+ return rc;
+}
+
+
+int posixAuth_UserAdd(void* context, const char* username,
+ uint16_t* out_user_id, whAuthPermissions permissions,
+ whAuthMethod method, const void* credentials,
+ uint16_t credentials_len)
+{
+ whAuthContext* auth_context = (whAuthContext*)context;
+ whAuthBase_User* new_user;
+ int i;
+ int userId = WH_USER_ID_INVALID;
+
+ /* Validate method is supported if credentials are provided */
+ if (credentials != NULL && credentials_len > 0) {
+ if (method != WH_AUTH_METHOD_PIN
+#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO)
+ && method != WH_AUTH_METHOD_CERTIFICATE
+#endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !WOLFHSM_CFG_NO_CRYPTO */
+ ) {
+ return WH_ERROR_BADARGS;
+ }
+ }
+
+ for (i = 0; i < WH_AUTH_BASE_MAX_USERS; i++) {
+ if (users[i].user.user_id == WH_USER_ID_INVALID) {
+ break;
+ }
+ }
+
+ if (i >= WH_AUTH_BASE_MAX_USERS) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+ userId = i + 1; /* save 0 for WH_USER_ID_INVALID */
+ new_user = &users[i];
+
+ memset(new_user, 0, sizeof(whAuthBase_User));
+ new_user->user.user_id = userId;
+ *out_user_id = userId;
+ new_user->user.permissions = permissions;
+ /* Clamp keyIdCount to valid range and zero out unused keyIds */
+ if (new_user->user.permissions.keyIdCount > WH_AUTH_MAX_KEY_IDS) {
+ new_user->user.permissions.keyIdCount = WH_AUTH_MAX_KEY_IDS;
+ }
+ /* Zero out unused keyIds beyond keyIdCount */
+ if (new_user->user.permissions.keyIdCount < WH_AUTH_MAX_KEY_IDS) {
+ int j;
+ for (j = new_user->user.permissions.keyIdCount; j < WH_AUTH_MAX_KEY_IDS;
+ j++) {
+ new_user->user.permissions.keyIds[j] = 0;
+ }
+ }
+ strncpy(new_user->user.username, username,
+ sizeof(new_user->user.username) - 1);
+ new_user->user.username[sizeof(new_user->user.username) - 1] = '\0';
+ new_user->user.is_active = false;
+
+ /* Set credentials if provided */
+ if (credentials != NULL && credentials_len > 0) {
+ if (credentials_len > WH_AUTH_BASE_MAX_CREDENTIALS_LEN) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+ new_user->method = method;
+ memcpy(new_user->credentials, credentials, credentials_len);
+ new_user->credentials_len = credentials_len;
+ }
+
+ (void)auth_context;
+ return WH_ERROR_OK;
+}
+
+int posixAuth_UserDelete(void* context, uint16_t current_user_id,
+ uint16_t user_id)
+{
+ whAuthBase_User* user;
+
+ if (user_id == WH_USER_ID_INVALID || user_id > WH_AUTH_BASE_MAX_USERS) {
+ return WH_ERROR_NOTFOUND;
+ }
+
+ user = &users[user_id - 1];
+ if (user->user.user_id == WH_USER_ID_INVALID) {
+ return WH_ERROR_NOTFOUND;
+ }
+
+ memset(user, 0, sizeof(whAuthBase_User));
+ (void)context;
+ (void)current_user_id;
+ return WH_ERROR_OK;
+}
+
+int posixAuth_UserSetPermissions(void* context, uint16_t current_user_id,
+ uint16_t user_id,
+ whAuthPermissions permissions)
+{
+ whAuthBase_User* user;
+
+ if (user_id == WH_USER_ID_INVALID || user_id > WH_AUTH_BASE_MAX_USERS) {
+ return WH_ERROR_NOTFOUND;
+ }
+
+ user = &users[user_id - 1];
+ if (user->user.user_id == WH_USER_ID_INVALID) {
+ return WH_ERROR_NOTFOUND;
+ }
+ user->user.permissions = permissions;
+ /* Clamp keyIdCount to valid range and zero out unused keyIds */
+ if (user->user.permissions.keyIdCount > WH_AUTH_MAX_KEY_IDS) {
+ user->user.permissions.keyIdCount = WH_AUTH_MAX_KEY_IDS;
+ }
+ /* Zero out unused keyIds beyond keyIdCount */
+ if (user->user.permissions.keyIdCount < WH_AUTH_MAX_KEY_IDS) {
+ int j;
+ for (j = user->user.permissions.keyIdCount; j < WH_AUTH_MAX_KEY_IDS;
+ j++) {
+ user->user.permissions.keyIds[j] = 0;
+ }
+ }
+ (void)context;
+ (void)current_user_id;
+ return WH_ERROR_OK;
+}
+
+
+int posixAuth_UserGet(void* context, const char* username,
+ uint16_t* out_user_id,
+ whAuthPermissions* out_permissions)
+{
+ whAuthBase_User* user = posixAuth_FindUser(username);
+ if (user == NULL) {
+ return WH_ERROR_NOTFOUND;
+ }
+ *out_user_id = user->user.user_id;
+ *out_permissions = user->user.permissions;
+ (void)context;
+ return WH_ERROR_OK;
+}
+
+
+int posixAuth_UserSetCredentials(void* context, uint16_t user_id,
+ whAuthMethod method,
+ const void* current_credentials,
+ uint16_t current_credentials_len,
+ const void* new_credentials,
+ uint16_t new_credentials_len)
+{
+ whAuthContext* auth_context = (whAuthContext*)context;
+ whAuthBase_User* user;
+ int rc = WH_ERROR_OK;
+
+ if (user_id == WH_USER_ID_INVALID || user_id > WH_AUTH_BASE_MAX_USERS) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Validate method is supported */
+ if (method != WH_AUTH_METHOD_PIN
+#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO)
+ && method != WH_AUTH_METHOD_CERTIFICATE
+#endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !WOLFHSM_CFG_NO_CRYPTO */
+ ) {
+ return WH_ERROR_BADARGS;
+ }
+
+ user = &users[user_id - 1]; /* subtract 1 to get the index */
+ if (user->user.user_id == WH_USER_ID_INVALID) {
+ return WH_ERROR_NOTFOUND;
+ }
+
+ /* Verify current credentials if user has existing credentials */
+ if (user->credentials_len > 0) {
+ /* User has existing credentials, so current_credentials must be
+ * provided and match */
+ if (current_credentials == NULL || current_credentials_len == 0) {
+ return WH_ERROR_ACCESS;
+ }
+ if (user->credentials_len != current_credentials_len ||
+ memcmp(user->credentials, current_credentials,
+ current_credentials_len) != 0) {
+ return WH_ERROR_ACCESS;
+ }
+ }
+ else {
+ /* User has no existing credentials, current_credentials should be NULL
+ */
+ if (current_credentials != NULL && current_credentials_len > 0) {
+ return WH_ERROR_BADARGS;
+ }
+ }
+
+ /* Set new credentials */
+ if (new_credentials_len > WH_AUTH_BASE_MAX_CREDENTIALS_LEN) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+ user->method = method;
+ if (new_credentials_len > 0) {
+ memcpy(user->credentials, new_credentials, new_credentials_len);
+ user->credentials_len = new_credentials_len;
+ }
+ else {
+ /* Allow clearing credentials by setting length to 0 */
+ user->credentials_len = 0;
+ }
+
+ (void)auth_context;
+ return rc;
+}
diff --git a/port/posix/posix_auth.h b/port/posix/posix_auth.h
new file mode 100644
index 00000000..4ce2e97f
--- /dev/null
+++ b/port/posix/posix_auth.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2025 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+/*
+ * posix_auth.h
+ *
+ * Basic authentication and authorization implementation.
+ */
+
+#ifndef PORT_POSIX_POSIX_AUTH_H_
+#define PORT_POSIX_POSIX_AUTH_H_
+
+/* Pick up compile-time configuration */
+#include "wolfhsm/wh_settings.h"
+
+#include
+
+#include "wolfhsm/wh_common.h"
+#include "wolfhsm/wh_auth.h"
+
+/**
+ * @brief Initialize the auth base backend.
+ *
+ * @param[in] context Pointer to the auth base context.
+ * @param[in] config Pointer to the configuration data.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int posixAuth_Init(void* context, const void* config);
+
+/**
+ * @brief Cleanup the auth base backend.
+ *
+ * @param[in] context Pointer to the auth base context.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int posixAuth_Cleanup(void* context);
+
+/**
+ * @brief Authenticate a user using the specified method.
+ *
+ * @param[in] context Pointer to the auth base context.
+ * @param[in] client_id The client ID making the request.
+ * @param[in] method The authentication method to use.
+ * @param[in] username The username to authenticate.
+ * @param[in] auth_data Pointer to the authentication data.
+ * @param[in] auth_data_len Length of the authentication data.
+ * @param[out] out_user_id Pointer to store the authenticated user ID.
+ * @param[out] out_permissions Pointer to store the user permissions.
+ * @param[out] loggedIn Pointer to store the login status.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int posixAuth_Login(void* context, uint8_t client_id, whAuthMethod method,
+ const char* username, const void* auth_data,
+ uint16_t auth_data_len, uint16_t* out_user_id,
+ whAuthPermissions* out_permissions, int* loggedIn);
+
+/**
+ * @brief Logout a user.
+ *
+ * @param[in] context Pointer to the auth base context.
+ * @param[in] current_user_id The user ID of the current user performing the logout.
+ * @param[in] user_id The user ID to logout.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int posixAuth_Logout(void* context, uint16_t current_user_id,
+ uint16_t user_id);
+
+/**
+ * @brief Check if an action is authorized for a session.
+ *
+ * @param[in] context Pointer to the auth base context.
+ * @param[in] user_id The user ID to check authorization for.
+ * @param[in] group The group to check authorization for.
+ * @param[in] action The action to check authorization for.
+ * @return int Returns 0 if authorized, or a negative error code on failure.
+ */
+int posixAuth_CheckRequestAuthorization(void* context, uint16_t user_id,
+ uint16_t group, uint16_t action);
+
+/* authorization check on key usage after the request has been parsed and before
+ * the action is done */
+int posixAuth_CheckKeyAuthorization(void* context, uint16_t user_id,
+ uint32_t key_id, uint16_t action);
+
+/**
+ * @brief Add a new user.
+ *
+ * @param[in] context Pointer to the auth base context.
+ * @param[in] username The username for the new user.
+ * @param[out] out_user_id Pointer to store the new user ID.
+ * @param[in] permissions The permissions for the new user.
+ * @param[in] method The authentication method for the new user.
+ * @param[in] credentials Pointer to the credentials data.
+ * @param[in] credentials_len Length of the credentials data.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int posixAuth_UserAdd(void* context, const char* username,
+ uint16_t* out_user_id, whAuthPermissions permissions,
+ whAuthMethod method, const void* credentials,
+ uint16_t credentials_len);
+
+/**
+ * @brief Delete a user.
+ *
+ * @param[in] context Pointer to the auth base context.
+ * @param[in] current_user_id The user ID of the current user performing the deletion.
+ * @param[in] user_id The user ID to delete.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int posixAuth_UserDelete(void* context, uint16_t current_user_id,
+ uint16_t user_id);
+
+/**
+ * @brief Set user permissions.
+ *
+ * @param[in] context Pointer to the auth base context.
+ * @param[in] current_user_id The user ID of the current user performing the operation.
+ * @param[in] user_id The user ID to set permissions for.
+ * @param[in] permissions The new permissions to set.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int posixAuth_UserSetPermissions(void* context, uint16_t current_user_id,
+ uint16_t user_id,
+ whAuthPermissions permissions);
+
+/**
+ * @brief Get user information by username.
+ *
+ * @param[in] context Pointer to the auth base context.
+ * @param[in] username The username to look up.
+ * @param[out] out_user_id Pointer to store the user ID.
+ * @param[out] out_permissions Pointer to store the user permissions.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int posixAuth_UserGet(void* context, const char* username,
+ uint16_t* out_user_id,
+ whAuthPermissions* out_permissions);
+
+/**
+ * @brief Set user credentials (PIN, etc.).
+ *
+ * @param[in] context Pointer to the auth base context.
+ * @param[in] user_id The user ID to set credentials for.
+ * @param[in] method The authentication method.
+ * @param[in] current_credentials Pointer to the current credentials data.
+ * @param[in] current_credentials_len Length of the current credentials data.
+ * @param[in] new_credentials Pointer to the new credentials data.
+ * @param[in] new_credentials_len Length of the new credentials data.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int posixAuth_UserSetCredentials(void* context, uint16_t user_id,
+ whAuthMethod method,
+ const void* current_credentials,
+ uint16_t current_credentials_len,
+ const void* new_credentials,
+ uint16_t new_credentials_len);
+
+#endif /* PORT_POSIX_POSIX_AUTH_H_ */
\ No newline at end of file
diff --git a/src/wh_auth.c b/src/wh_auth.c
new file mode 100644
index 00000000..3dff7286
--- /dev/null
+++ b/src/wh_auth.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2025 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+
+/*
+ * src/wh_auth.c
+ *
+ * Core Auth Manager implementation. Provides wrapper functions that delegate
+ * to the configured auth backend callbacks.
+ *
+ * - Verifies PINs/credentials
+ * - Calls to implemented callbacks for managing users and permissions
+ * - Authorization decisions are routed through the implemented callbacks
+ *
+ * The Auth Manager is agnostic to the transport used and manages authentication
+ * of a session. It can take a PIN or certificate for verification. An
+ * authenticated session is separate from a comm connection and sits on top of
+ * a comm connection. Allowing for multiple authenticated sessions opened and
+ * closed multiple times through out the span of a single comm connection
+ * established. Currently there is a restriction of one user logged in at a time
+ * per comm connection.
+ */
+
+/* Pick up compile-time configuration */
+#include "wolfhsm/wh_settings.h"
+
+#include
+#include
+#include
+
+#include "wolfhsm/wh_common.h"
+#include "wolfhsm/wh_error.h"
+
+#include "wolfhsm/wh_auth.h"
+
+
+int wh_Auth_Init(whAuthContext* context, const whAuthConfig* config)
+{
+ int rc = 0;
+
+ if ((context == NULL) || (config == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ context->cb = config->cb;
+ context->context = config->context;
+ memset(&context->user, 0, sizeof(whAuthUser));
+
+ if (context->cb != NULL && context->cb->Init != NULL) {
+ rc = context->cb->Init(context->context, config->config);
+ if (rc != 0) {
+ context->cb = NULL;
+ context->context = NULL;
+ }
+ }
+
+ return rc;
+}
+
+
+int wh_Auth_Cleanup(whAuthContext* context)
+{
+ if ((context == NULL) || (context->cb == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (context->cb->Cleanup == NULL) {
+ return WH_ERROR_ABORTED;
+ }
+ return context->cb->Cleanup(context->context);
+}
+
+
+/* Returns a wolfHSM error code: WH_ERROR_OK (0) if the call completed
+ * successfully (regardless of authentication result), or a negative error code
+ * if a fatal error occurred. The result of the login attempt is stored in
+ * loggedIn: 1 for successful authentication, 0 for failed authentication. */
+int wh_Auth_Login(whAuthContext* context, uint8_t client_id,
+ whAuthMethod method, const char* username,
+ const void* auth_data, uint16_t auth_data_len, int* loggedIn)
+{
+ int rc;
+ whUserId out_user_id;
+ whAuthPermissions out_permissions;
+
+ if (loggedIn == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+ *loggedIn = 0;
+
+ if ((context == NULL) || (context->cb == NULL) ||
+ (context->cb->Login == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* allowing only one user logged in to an open connection at a time */
+ if (context->user.user_id != WH_USER_ID_INVALID) {
+ *loggedIn = 0;
+ rc = WH_ERROR_OK; /* login attempt happened but failed */
+ }
+ else {
+ rc = context->cb->Login(context->context, client_id, method, username,
+ auth_data, auth_data_len, &out_user_id,
+ &out_permissions, loggedIn);
+ if (rc == WH_ERROR_OK && *loggedIn) {
+ context->user.user_id = out_user_id;
+ context->user.permissions = out_permissions;
+ context->user.is_active = true;
+ }
+ }
+
+ return rc;
+}
+
+
+int wh_Auth_Logout(whAuthContext* context, whUserId user_id)
+{
+ int rc;
+
+ if ((context == NULL) || (context->cb == NULL) ||
+ (context->cb->Logout == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ rc = context->cb->Logout(context->context, context->user.user_id, user_id);
+ if (rc != WH_ERROR_OK) {
+ return rc;
+ }
+
+ /* Clear the user context */
+ memset(&context->user, 0, sizeof(whAuthUser));
+ return WH_ERROR_OK;
+}
+
+
+/* Check on request authorization and action permissions for current user
+ * logged in */
+int wh_Auth_CheckRequestAuthorization(whAuthContext* context, uint16_t group,
+ uint16_t action)
+{
+ uint16_t user_id;
+ int rc;
+
+ if ((context == NULL) || (context->cb == NULL) ||
+ (context->cb->CheckRequestAuthorization == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ user_id = context->user.user_id;
+ /* @TODO add logging call here and with resulting return value */
+
+ rc = context->cb->CheckRequestAuthorization(context->context, user_id,
+ group, action);
+ return rc;
+}
+
+
+/* Check on key ID use after request has been parsed */
+int wh_Auth_CheckKeyAuthorization(whAuthContext* context, uint32_t key_id,
+ uint16_t action)
+{
+ uint16_t user_id;
+ int rc;
+
+ if ((context == NULL) || (context->cb == NULL) ||
+ (context->cb->CheckKeyAuthorization == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ user_id = context->user.user_id;
+
+ rc = context->cb->CheckKeyAuthorization(context->context, user_id, key_id,
+ action);
+ return rc;
+}
+
+/********** API That Manages User Database ******************************/
+
+int wh_Auth_UserAdd(whAuthContext* context, const char* username,
+ whUserId* out_user_id, whAuthPermissions permissions,
+ whAuthMethod method, const void* credentials,
+ uint16_t credentials_len)
+{
+ if ((context == NULL) || (context->cb == NULL) ||
+ (context->cb->UserAdd == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return context->cb->UserAdd(context->context, username, out_user_id,
+ permissions, method, credentials,
+ credentials_len);
+}
+
+
+int wh_Auth_UserDelete(whAuthContext* context, whUserId user_id)
+{
+ if ((context == NULL) || (context->cb == NULL) ||
+ (context->cb->UserDelete == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return context->cb->UserDelete(context->context, context->user.user_id,
+ user_id);
+}
+
+
+int wh_Auth_UserSetPermissions(whAuthContext* context, whUserId user_id,
+ whAuthPermissions permissions)
+{
+ if ((context == NULL) || (context->cb == NULL) ||
+ (context->cb->UserSetPermissions == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return context->cb->UserSetPermissions(
+ context->context, context->user.user_id, user_id, permissions);
+}
+
+int wh_Auth_UserGet(whAuthContext* context, const char* username,
+ whUserId* out_user_id, whAuthPermissions* out_permissions)
+{
+ if ((context == NULL) || (context->cb == NULL) ||
+ (context->cb->UserGet == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return context->cb->UserGet(context->context, username, out_user_id,
+ out_permissions);
+}
+
+int wh_Auth_UserSetCredentials(whAuthContext* context, whUserId user_id,
+ whAuthMethod method,
+ const void* current_credentials,
+ uint16_t current_credentials_len,
+ const void* new_credentials,
+ uint16_t new_credentials_len)
+{
+ if ((context == NULL) || (context->cb == NULL) ||
+ (context->cb->UserSetCredentials == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return context->cb->UserSetCredentials(
+ context->context, user_id, method, current_credentials,
+ current_credentials_len, new_credentials, new_credentials_len);
+}
diff --git a/src/wh_client.c b/src/wh_client.c
index 927c4bf0..b373bd6a 100644
--- a/src/wh_client.c
+++ b/src/wh_client.c
@@ -1392,7 +1392,7 @@ int wh_Client_KeyCacheDmaRequest(whClientContext* c, uint32_t flags,
int ret;
whMessageKeystore_CacheDmaRequest* req = NULL;
uintptr_t keyAddrPtr = 0;
- uint16_t capSz = 0;
+ uint16_t capSz = 0;
if (c == NULL || (labelSz > 0 && label == NULL)) {
return WH_ERROR_BADARGS;
@@ -1415,7 +1415,7 @@ int wh_Client_KeyCacheDmaRequest(whClientContext* c, uint32_t flags,
req->key.addr = keyAddrPtr;
/* Copy label if provided, truncate if necessary */
- if (labelSz > 0 && label != NULL) {
+ if (labelSz > 0 && label != NULL) {
capSz = (labelSz > WH_NVM_LABEL_LEN) ? WH_NVM_LABEL_LEN : labelSz;
req->labelSz = capSz;
memcpy(req->label, label, capSz);
diff --git a/src/wh_client_auth.c b/src/wh_client_auth.c
new file mode 100644
index 00000000..668b4ea8
--- /dev/null
+++ b/src/wh_client_auth.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) 2025 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+/*
+ * src/wh_client_auth.c
+ *
+ * Client-side Auth Manager API
+ */
+
+/* Pick up compile-time configuration */
+#include "wolfhsm/wh_settings.h"
+
+#ifdef WOLFHSM_CFG_ENABLE_CLIENT
+
+/* System libraries */
+#include /* For memcpy, strncpy */
+
+/* Common WolfHSM types and defines shared with the server */
+#include "wolfhsm/wh_common.h"
+#include "wolfhsm/wh_error.h"
+#include "wolfhsm/wh_comm.h"
+
+#include "wolfhsm/wh_message.h"
+#include "wolfhsm/wh_message_auth.h"
+
+#include "wolfhsm/wh_client.h"
+#include "wolfhsm/wh_auth.h"
+
+static int _wh_Client_AuthUserNameSanityCheck(const char* username)
+{
+ size_t len;
+
+ if (username == NULL) {
+ return 0;
+ }
+
+ len = strnlen(username, WH_MESSAGE_AUTH_MAX_USERNAME_LEN);
+ return (len < WH_MESSAGE_AUTH_MAX_USERNAME_LEN);
+}
+
+/** Authenticate */
+int wh_Client_AuthLoginRequest(whClientContext* c, whAuthMethod method,
+ const char* username, const void* auth_data,
+ uint16_t auth_data_len)
+{
+ uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN] = {0};
+ whMessageAuth_LoginRequest* msg = (whMessageAuth_LoginRequest*)buffer;
+ uint8_t* msg_auth_data = buffer + sizeof(*msg);
+ int msg_size;
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (!_wh_Client_AuthUserNameSanityCheck(username)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (auth_data_len > WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN) {
+ return WH_ERROR_BADARGS;
+ }
+
+ msg_size = (int)sizeof(*msg) + (int)auth_data_len;
+ if (msg_size > WOLFHSM_CFG_COMM_DATA_LEN) {
+ return WH_ERROR_BADARGS;
+ }
+
+ strncpy(msg->username, username, sizeof(msg->username) - 1);
+ msg->username[sizeof(msg->username) - 1] = '\0';
+ msg->method = method;
+ msg->auth_data_len = auth_data_len;
+ if (auth_data_len > 0 && auth_data != NULL) {
+ memcpy(msg_auth_data, auth_data, auth_data_len);
+ }
+
+ return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_AUTH,
+ WH_MESSAGE_AUTH_ACTION_LOGIN,
+ (uint16_t)msg_size, buffer);
+}
+
+int wh_Client_AuthLoginResponse(whClientContext* c, int32_t* out_rc,
+ whUserId* out_user_id)
+{
+ uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN] = {0};
+ whMessageAuth_LoginResponse* msg = (whMessageAuth_LoginResponse*)buffer;
+
+ int rc = 0;
+ uint16_t resp_group = 0;
+ uint16_t resp_action = 0;
+ uint16_t resp_size = 0;
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ rc = wh_Client_RecvResponse(c, &resp_group, &resp_action, &resp_size,
+ buffer);
+ if (rc == 0) {
+ /* Validate response */
+ if ((resp_group != WH_MESSAGE_GROUP_AUTH) ||
+ (resp_action != WH_MESSAGE_AUTH_ACTION_LOGIN) ||
+ (resp_size != sizeof(whMessageAuth_LoginResponse))) {
+ /* Invalid message */
+ rc = WH_ERROR_ABORTED;
+ }
+ else {
+ /* Valid message */
+ if (out_rc != NULL) {
+ *out_rc = msg->rc;
+ }
+ if (out_user_id != NULL) {
+ *out_user_id = msg->user_id;
+ }
+ }
+ }
+ return rc;
+}
+
+int wh_Client_AuthLogin(whClientContext* c, whAuthMethod method,
+ const char* username, const void* auth_data,
+ uint16_t auth_data_len, int32_t* out_rc,
+ whUserId* out_user_id)
+{
+ int rc;
+
+ do {
+ rc = wh_Client_AuthLoginRequest(c, method, username, auth_data,
+ auth_data_len);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ if (rc != 0) {
+ return rc;
+ }
+
+ do {
+ rc = wh_Client_AuthLoginResponse(c, out_rc, out_user_id);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ return rc;
+}
+
+int wh_Client_AuthLogoutRequest(whClientContext* c, whUserId user_id)
+{
+ whMessageAuth_LogoutRequest msg = {0};
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ msg.user_id = user_id;
+ return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_AUTH,
+ WH_MESSAGE_AUTH_ACTION_LOGOUT, sizeof(msg),
+ &msg);
+}
+
+
+int wh_Client_AuthLogoutResponse(whClientContext* c, int32_t* out_rc)
+{
+ uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN] = {0};
+ whMessageAuth_SimpleResponse* msg = (whMessageAuth_SimpleResponse*)buffer;
+
+ int rc = 0;
+ uint16_t resp_group = 0;
+ uint16_t resp_action = 0;
+ uint16_t resp_size = 0;
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ rc = wh_Client_RecvResponse(c, &resp_group, &resp_action, &resp_size,
+ buffer);
+ if (rc == 0) {
+ /* Validate response */
+ if ((resp_group != WH_MESSAGE_GROUP_AUTH) ||
+ (resp_action != WH_MESSAGE_AUTH_ACTION_LOGOUT) ||
+ (resp_size != sizeof(whMessageAuth_SimpleResponse))) {
+ /* Invalid message */
+ rc = WH_ERROR_ABORTED;
+ }
+ else {
+ /* Valid message */
+ if (out_rc != NULL) {
+ *out_rc = msg->rc;
+ }
+ }
+ }
+ return rc;
+}
+
+int wh_Client_AuthLogout(whClientContext* c, whUserId user_id, int32_t* out_rc)
+{
+ int rc;
+
+ do {
+ rc = wh_Client_AuthLogoutRequest(c, user_id);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ if (rc != 0) {
+ return rc;
+ }
+
+ do {
+ rc = wh_Client_AuthLogoutResponse(c, out_rc);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ return rc;
+}
+
+/** User Add */
+int wh_Client_AuthUserAddRequest(whClientContext* c, const char* username,
+ whAuthPermissions permissions,
+ whAuthMethod method, const void* credentials,
+ uint16_t credentials_len)
+{
+ uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN] = {0};
+ whMessageAuth_UserAddRequest* msg = (whMessageAuth_UserAddRequest*)buffer;
+ uint8_t* msg_credentials = buffer + sizeof(*msg);
+ int msg_size;
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (!_wh_Client_AuthUserNameSanityCheck(username)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ strncpy(msg->username, username, sizeof(msg->username) - 1);
+ msg->username[sizeof(msg->username) - 1] = '\0';
+
+ if (wh_MessageAuth_FlattenPermissions(&permissions, msg->permissions,
+ sizeof(msg->permissions)) != 0) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+
+ msg->method = method;
+ msg->credentials_len = credentials_len;
+ if (credentials != NULL && credentials_len > 0) {
+ if (credentials_len > WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+ memcpy(msg_credentials, credentials, credentials_len);
+ }
+
+ msg_size = (int)sizeof(*msg) + (int)credentials_len;
+ if (msg_size > WOLFHSM_CFG_COMM_DATA_LEN) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+ return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_AUTH,
+ WH_MESSAGE_AUTH_ACTION_USER_ADD,
+ (uint16_t)msg_size, buffer);
+}
+
+int wh_Client_AuthUserAddResponse(whClientContext* c, int32_t* out_rc,
+ whUserId* out_user_id)
+{
+ uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN] = {0};
+ whMessageAuth_UserAddResponse* msg = (whMessageAuth_UserAddResponse*)buffer;
+ uint16_t hdr_len = sizeof(*msg);
+
+ int rc = 0;
+ uint16_t resp_group = 0;
+ uint16_t resp_action = 0;
+ uint16_t resp_size = 0;
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ rc = wh_Client_RecvResponse(c, &resp_group, &resp_action, &resp_size,
+ buffer);
+ if (rc == 0) {
+ /* Validate response */
+ if ((resp_group != WH_MESSAGE_GROUP_AUTH) ||
+ (resp_action != WH_MESSAGE_AUTH_ACTION_USER_ADD) ||
+ (resp_size != hdr_len) || (resp_size > (uint16_t)sizeof(buffer))) {
+ /* Invalid message */
+ rc = WH_ERROR_ABORTED;
+ }
+ else {
+ /* Valid message */
+ if (out_rc != NULL) {
+ *out_rc = msg->rc;
+ }
+ if (out_user_id != NULL) {
+ *out_user_id = msg->user_id;
+ }
+ }
+ }
+ return rc;
+}
+
+int wh_Client_AuthUserAdd(whClientContext* c, const char* username,
+ whAuthPermissions permissions, whAuthMethod method,
+ const void* credentials, uint16_t credentials_len,
+ int32_t* out_rc, whUserId* out_user_id)
+{
+ int rc;
+
+ do {
+ rc = wh_Client_AuthUserAddRequest(c, username, permissions, method,
+ credentials, credentials_len);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ if (rc != 0) {
+ return rc;
+ }
+
+ do {
+ rc = wh_Client_AuthUserAddResponse(c, out_rc, out_user_id);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ return rc;
+}
+
+/** User Delete */
+int wh_Client_AuthUserDeleteRequest(whClientContext* c, whUserId user_id)
+{
+ whMessageAuth_UserDeleteRequest msg = {0};
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ msg.user_id = user_id;
+ return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_AUTH,
+ WH_MESSAGE_AUTH_ACTION_USER_DELETE,
+ sizeof(msg), &msg);
+}
+
+int wh_Client_AuthUserDeleteResponse(whClientContext* c, int32_t* out_rc)
+{
+ uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN] = {0};
+ whMessageAuth_SimpleResponse* msg = (whMessageAuth_SimpleResponse*)buffer;
+
+ int rc = 0;
+ uint16_t resp_group = 0;
+ uint16_t resp_action = 0;
+ uint16_t resp_size = 0;
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ rc = wh_Client_RecvResponse(c, &resp_group, &resp_action, &resp_size,
+ buffer);
+ if (rc == 0) {
+ /* Validate response */
+ if ((resp_group != WH_MESSAGE_GROUP_AUTH) ||
+ (resp_action != WH_MESSAGE_AUTH_ACTION_USER_DELETE) ||
+ (resp_size != sizeof(whMessageAuth_SimpleResponse))) {
+ /* Invalid message */
+ rc = WH_ERROR_ABORTED;
+ }
+ else {
+ /* Valid message */
+ if (out_rc != NULL) {
+ *out_rc = msg->rc;
+ }
+ }
+ }
+ return rc;
+}
+
+int wh_Client_AuthUserDelete(whClientContext* c, whUserId user_id,
+ int32_t* out_rc)
+{
+ int rc;
+
+ do {
+ rc = wh_Client_AuthUserDeleteRequest(c, user_id);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ if (rc != 0) {
+ return rc;
+ }
+
+ do {
+ rc = wh_Client_AuthUserDeleteResponse(c, out_rc);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ return rc;
+}
+
+/** User Get */
+int wh_Client_AuthUserGetRequest(whClientContext* c, const char* username)
+{
+ whMessageAuth_UserGetRequest msg = {0};
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (!_wh_Client_AuthUserNameSanityCheck(username)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ strncpy(msg.username, username, sizeof(msg.username) - 1);
+ msg.username[sizeof(msg.username) - 1] = '\0';
+ return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_AUTH,
+ WH_MESSAGE_AUTH_ACTION_USER_GET, sizeof(msg),
+ &msg);
+}
+
+int wh_Client_AuthUserGetResponse(whClientContext* c, int32_t* out_rc,
+ whUserId* out_user_id,
+ whAuthPermissions* out_permissions)
+{
+ uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN] = {0};
+ whMessageAuth_UserGetResponse* msg = (whMessageAuth_UserGetResponse*)buffer;
+
+ int rc = 0;
+ uint16_t resp_group = 0;
+ uint16_t resp_action = 0;
+ uint16_t resp_size = 0;
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ rc = wh_Client_RecvResponse(c, &resp_group, &resp_action, &resp_size,
+ buffer);
+ if (rc == 0) {
+ /* Validate response */
+ if ((resp_group != WH_MESSAGE_GROUP_AUTH) ||
+ (resp_action != WH_MESSAGE_AUTH_ACTION_USER_GET) ||
+ (resp_size != sizeof(whMessageAuth_UserGetResponse))) {
+ /* Invalid message */
+ rc = WH_ERROR_ABORTED;
+ }
+ else {
+ /* Valid message */
+ if (out_rc != NULL) {
+ *out_rc = msg->rc;
+ }
+ if (out_user_id != NULL) {
+ *out_user_id = msg->user_id;
+ }
+ if (out_permissions != NULL) {
+ wh_MessageAuth_UnflattenPermissions(msg->permissions,
+ sizeof(msg->permissions),
+ out_permissions);
+ }
+ }
+ }
+ return rc;
+}
+
+
+int wh_Client_AuthUserGet(whClientContext* c, const char* username,
+ int32_t* out_rc, whUserId* out_user_id,
+ whAuthPermissions* out_permissions)
+{
+ int rc;
+
+ do {
+ rc = wh_Client_AuthUserGetRequest(c, username);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ if (rc != 0) {
+ return rc;
+ }
+
+ do {
+ rc = wh_Client_AuthUserGetResponse(c, out_rc, out_user_id,
+ out_permissions);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ return rc;
+}
+
+/** User Set Permissions */
+int wh_Client_AuthUserSetPermissionsRequest(whClientContext* c,
+ whUserId user_id,
+ whAuthPermissions permissions)
+{
+ whMessageAuth_UserSetPermissionsRequest msg = {0};
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ msg.user_id = user_id;
+ if (wh_MessageAuth_FlattenPermissions(&permissions, msg.permissions,
+ sizeof(msg.permissions)) != 0) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+ return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_AUTH,
+ WH_MESSAGE_AUTH_ACTION_USER_SET_PERMISSIONS,
+ sizeof(msg), &msg);
+}
+
+int wh_Client_AuthUserSetPermissionsResponse(whClientContext* c,
+ int32_t* out_rc)
+{
+ uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN] = {0};
+ whMessageAuth_SimpleResponse* msg = (whMessageAuth_SimpleResponse*)buffer;
+
+ int rc = 0;
+ uint16_t resp_group = 0;
+ uint16_t resp_action = 0;
+ uint16_t resp_size = 0;
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ rc = wh_Client_RecvResponse(c, &resp_group, &resp_action, &resp_size,
+ buffer);
+ if (rc == 0) {
+ /* Validate response */
+ if ((resp_group != WH_MESSAGE_GROUP_AUTH) ||
+ (resp_action != WH_MESSAGE_AUTH_ACTION_USER_SET_PERMISSIONS) ||
+ (resp_size != sizeof(whMessageAuth_SimpleResponse))) {
+ /* Invalid message */
+ rc = WH_ERROR_ABORTED;
+ }
+ else {
+ /* Valid message */
+ if (out_rc != NULL) {
+ *out_rc = msg->rc;
+ }
+ }
+ }
+ return rc;
+}
+
+int wh_Client_AuthUserSetPermissions(whClientContext* c, whUserId user_id,
+ whAuthPermissions permissions,
+ int32_t* out_rc)
+{
+ int rc;
+
+ do {
+ rc = wh_Client_AuthUserSetPermissionsRequest(c, user_id, permissions);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ if (rc != 0) {
+ return rc;
+ }
+
+ do {
+ rc = wh_Client_AuthUserSetPermissionsResponse(c, out_rc);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ return rc;
+}
+
+/** User Set Credentials */
+int wh_Client_AuthUserSetCredentialsRequest(
+ whClientContext* c, whUserId user_id, whAuthMethod method,
+ const void* current_credentials, uint16_t current_credentials_len,
+ const void* new_credentials, uint16_t new_credentials_len)
+{
+ uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN] = {0};
+ whMessageAuth_UserSetCredentialsRequest* msg =
+ (whMessageAuth_UserSetCredentialsRequest*)buffer;
+ uint8_t* msg_current_creds = buffer + sizeof(*msg);
+ uint8_t* msg_new_creds = msg_current_creds + current_credentials_len;
+ uint16_t total_size;
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Validate lengths */
+ if (current_credentials_len > WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+ if (new_credentials_len > WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+
+ /* Calculate total message size */
+ total_size = sizeof(*msg) + current_credentials_len + new_credentials_len;
+ if (total_size > WOLFHSM_CFG_COMM_DATA_LEN) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+
+ /* Build message header */
+ msg->user_id = user_id;
+ msg->method = method;
+ msg->current_credentials_len = current_credentials_len;
+ msg->new_credentials_len = new_credentials_len;
+
+ /* Copy variable-length credential data */
+ if (current_credentials != NULL && current_credentials_len > 0) {
+ memcpy(msg_current_creds, current_credentials, current_credentials_len);
+ }
+ if (new_credentials != NULL && new_credentials_len > 0) {
+ memcpy(msg_new_creds, new_credentials, new_credentials_len);
+ }
+
+ return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_AUTH,
+ WH_MESSAGE_AUTH_ACTION_USER_SET_CREDENTIALS,
+ total_size, buffer);
+}
+
+int wh_Client_AuthUserSetCredentialsResponse(whClientContext* c,
+ int32_t* out_rc)
+{
+ uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN] = {0};
+ whMessageAuth_SimpleResponse* msg = (whMessageAuth_SimpleResponse*)buffer;
+
+ int rc = 0;
+ uint16_t resp_group = 0;
+ uint16_t resp_action = 0;
+ uint16_t resp_size = 0;
+
+ if (c == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ rc = wh_Client_RecvResponse(c, &resp_group, &resp_action, &resp_size,
+ buffer);
+ if (rc == 0) {
+ /* Validate response */
+ if ((resp_group != WH_MESSAGE_GROUP_AUTH) ||
+ (resp_action != WH_MESSAGE_AUTH_ACTION_USER_SET_CREDENTIALS) ||
+ (resp_size != sizeof(whMessageAuth_SimpleResponse))) {
+ /* Invalid message */
+ rc = WH_ERROR_ABORTED;
+ }
+ else {
+ /* Valid message */
+ if (out_rc != NULL) {
+ *out_rc = msg->rc;
+ }
+ }
+ }
+ return rc;
+}
+
+int wh_Client_AuthUserSetCredentials(
+ whClientContext* c, whUserId user_id, whAuthMethod method,
+ const void* current_credentials, uint16_t current_credentials_len,
+ const void* new_credentials, uint16_t new_credentials_len, int32_t* out_rc)
+{
+ int rc;
+
+ do {
+ rc = wh_Client_AuthUserSetCredentialsRequest(
+ c, user_id, method, current_credentials, current_credentials_len,
+ new_credentials, new_credentials_len);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ if (rc != 0) {
+ return rc;
+ }
+
+ do {
+ rc = wh_Client_AuthUserSetCredentialsResponse(c, out_rc);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ return rc;
+}
+
+#endif /* WOLFHSM_CFG_ENABLE_CLIENT */
diff --git a/src/wh_message_auth.c b/src/wh_message_auth.c
new file mode 100644
index 00000000..25db9d8f
--- /dev/null
+++ b/src/wh_message_auth.c
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2025 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+/*
+ * src/wh_message_auth.c
+ *
+ * Message translation functions for Auth Manager messages
+ */
+
+/* Pick up compile-time configuration */
+#include "wolfhsm/wh_settings.h"
+
+#include
+#include
+#include
+#include "wolfhsm/wh_error.h"
+#include "wolfhsm/wh_comm.h"
+#include "wolfhsm/wh_message.h"
+
+#include "wolfhsm/wh_message_auth.h"
+
+
+int wh_MessageAuth_TranslateSimpleResponse(
+ uint16_t magic, const whMessageAuth_SimpleResponse* src,
+ whMessageAuth_SimpleResponse* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, rc);
+ return 0;
+}
+
+int wh_MessageAuth_TranslateLoginRequest(
+ uint16_t magic, const void* src_packet, uint16_t src_size,
+ whMessageAuth_LoginRequest* dest_header, uint8_t* dest_auth_data)
+{
+ const whMessageAuth_LoginRequest* src_header;
+ const uint8_t* src_data;
+ uint16_t header_size = sizeof(whMessageAuth_LoginRequest);
+ uint16_t expected_size;
+
+ if ((src_packet == NULL) || (dest_header == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (src_size < header_size) {
+ return WH_ERROR_BADARGS;
+ }
+
+ src_header = (const whMessageAuth_LoginRequest*)src_packet;
+ src_data = (const uint8_t*)src_packet + header_size;
+
+ WH_T16(magic, dest_header, src_header, method);
+ if (src_header != dest_header) {
+ memcpy(dest_header->username, src_header->username,
+ sizeof(dest_header->username));
+ }
+ WH_T16(magic, dest_header, src_header, auth_data_len);
+
+ expected_size = (uint16_t)(header_size + dest_header->auth_data_len);
+ if (dest_header->auth_data_len > WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN ||
+ src_size < expected_size) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (dest_auth_data != NULL && dest_header->auth_data_len > 0) {
+ memcpy(dest_auth_data, src_data, dest_header->auth_data_len);
+ }
+ return 0;
+}
+
+int wh_MessageAuth_TranslateLoginResponse(
+ uint16_t magic, const whMessageAuth_LoginResponse* src,
+ whMessageAuth_LoginResponse* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ WH_T32(magic, dest, src, rc);
+ WH_T16(magic, dest, src, user_id);
+
+ return 0;
+}
+
+int wh_MessageAuth_TranslateLogoutRequest(
+ uint16_t magic, const whMessageAuth_LogoutRequest* src,
+ whMessageAuth_LogoutRequest* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ WH_T16(magic, dest, src, user_id);
+ return 0;
+}
+
+
+int wh_MessageAuth_FlattenPermissions(whAuthPermissions* permissions,
+ uint8_t* buffer, uint16_t buffer_len)
+{
+ int idx = 0, i;
+ uint16_t keyIdCount;
+ uint32_t keyId;
+
+ if (permissions == NULL || buffer == NULL ||
+ buffer_len < WH_FLAT_PERMISSIONS_LEN) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Serialize groupPermissions (2 bytes) */
+ buffer[idx++] = (uint8_t)(permissions->groupPermissions & 0xFF);
+ buffer[idx++] = (uint8_t)((permissions->groupPermissions >> 8) & 0xFF);
+
+ /* Serialize actionPermissions array (4*WH_NUMBER_OF_GROUPS*WH_AUTH_ACTION_WORDS bytes) */
+ for (i = 0; i < WH_NUMBER_OF_GROUPS; i++) {
+ int j;
+ for (j = 0; j < WH_AUTH_ACTION_WORDS; j++) {
+ uint32_t actionPerm = permissions->actionPermissions[i][j];
+ buffer[idx++] = (uint8_t)(actionPerm & 0xFF);
+ buffer[idx++] = (uint8_t)((actionPerm >> 8) & 0xFF);
+ buffer[idx++] = (uint8_t)((actionPerm >> 16) & 0xFF);
+ buffer[idx++] = (uint8_t)((actionPerm >> 24) & 0xFF);
+ }
+ }
+
+ /* Serialize keyIdCount (2 bytes) */
+ keyIdCount = (permissions->keyIdCount > WH_AUTH_MAX_KEY_IDS)
+ ? WH_AUTH_MAX_KEY_IDS
+ : permissions->keyIdCount;
+ buffer[idx++] = (uint8_t)(keyIdCount & 0xFF);
+ buffer[idx++] = (uint8_t)((keyIdCount >> 8) & 0xFF);
+
+ /* Serialize keyIds array (4*WH_AUTH_MAX_KEY_IDS bytes) */
+ for (i = 0; i < WH_AUTH_MAX_KEY_IDS; i++) {
+ if (i < keyIdCount) {
+ keyId = permissions->keyIds[i];
+ }
+ else {
+ keyId = 0; /* Pad with zeros */
+ }
+ buffer[idx++] = (uint8_t)(keyId & 0xFF);
+ buffer[idx++] = (uint8_t)((keyId >> 8) & 0xFF);
+ buffer[idx++] = (uint8_t)((keyId >> 16) & 0xFF);
+ buffer[idx++] = (uint8_t)((keyId >> 24) & 0xFF);
+ }
+
+ return 0;
+}
+
+
+int wh_MessageAuth_UnflattenPermissions(uint8_t* buffer, uint16_t buffer_len,
+ whAuthPermissions* permissions)
+{
+ int idx = 0, i;
+ uint16_t keyIdCount;
+ uint32_t keyId;
+
+ if (buffer == NULL || permissions == NULL ||
+ buffer_len < WH_FLAT_PERMISSIONS_LEN) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Deserialize groupPermissions (2 bytes) */
+ permissions->groupPermissions = buffer[idx] | (buffer[idx + 1] << 8);
+ idx += 2;
+
+ /* Deserialize actionPermissions array (4*WH_NUMBER_OF_GROUPS*WH_AUTH_ACTION_WORDS bytes) */
+ for (i = 0; i < WH_NUMBER_OF_GROUPS; i++) {
+ int j;
+ for (j = 0; j < WH_AUTH_ACTION_WORDS; j++) {
+ permissions->actionPermissions[i][j] =
+ buffer[idx] |
+ (buffer[idx + 1] << 8) |
+ (buffer[idx + 2] << 16) |
+ (buffer[idx + 3] << 24);
+ idx += 4;
+ }
+ }
+
+ /* Deserialize keyIdCount (2 bytes) */
+ keyIdCount = buffer[idx] | (buffer[idx + 1] << 8);
+ idx += 2;
+ if (keyIdCount > WH_AUTH_MAX_KEY_IDS) {
+ keyIdCount = WH_AUTH_MAX_KEY_IDS;
+ }
+ permissions->keyIdCount = keyIdCount;
+
+ /* Deserialize keyIds array (4*WH_AUTH_MAX_KEY_IDS bytes) */
+ for (i = 0; i < WH_AUTH_MAX_KEY_IDS; i++) {
+ keyId = buffer[idx] |
+ (buffer[idx + 1] << 8) |
+ (buffer[idx + 2] << 16) |
+ (buffer[idx + 3] << 24);
+ permissions->keyIds[i] = keyId;
+ idx += 4;
+ }
+
+ return 0;
+}
+
+
+int wh_MessageAuth_TranslateUserAddRequest(
+ uint16_t magic, const void* src_packet, uint16_t src_size,
+ whMessageAuth_UserAddRequest* dest_header, uint8_t* dest_credentials)
+{
+ const whMessageAuth_UserAddRequest* src_header;
+ const uint8_t* src_data;
+ uint16_t header_size = sizeof(whMessageAuth_UserAddRequest);
+ uint16_t expected_size;
+
+ if ((src_packet == NULL) || (dest_header == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (src_size < header_size) {
+ return WH_ERROR_BADARGS;
+ }
+
+ src_header = (const whMessageAuth_UserAddRequest*)src_packet;
+ src_data = (const uint8_t*)src_packet + header_size;
+
+ if (src_header != dest_header) {
+ memcpy(dest_header->username, src_header->username,
+ sizeof(dest_header->username));
+ memcpy(dest_header->permissions, src_header->permissions,
+ sizeof(dest_header->permissions));
+ }
+
+ WH_T16(magic, dest_header, src_header, method);
+ WH_T16(magic, dest_header, src_header, credentials_len);
+
+ expected_size = (uint16_t)(header_size + dest_header->credentials_len);
+ if (dest_header->credentials_len > WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN ||
+ src_size < expected_size) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+
+ if (dest_credentials != NULL && dest_header->credentials_len > 0) {
+ memcpy(dest_credentials, src_data, dest_header->credentials_len);
+ }
+ return 0;
+}
+
+int wh_MessageAuth_TranslateUserAddResponse(
+ uint16_t magic, const whMessageAuth_UserAddResponse* src,
+ whMessageAuth_UserAddResponse* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, rc);
+ WH_T16(magic, dest, src, user_id);
+ return 0;
+}
+
+int wh_MessageAuth_TranslateUserDeleteRequest(
+ uint16_t magic, const whMessageAuth_UserDeleteRequest* src,
+ whMessageAuth_UserDeleteRequest* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ WH_T16(magic, dest, src, user_id);
+ return 0;
+}
+
+int wh_MessageAuth_TranslateUserGetRequest(
+ uint16_t magic, const whMessageAuth_UserGetRequest* src,
+ whMessageAuth_UserGetRequest* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (src != dest) {
+ memcpy(dest->username, src->username, sizeof(dest->username));
+ }
+ (void)magic;
+ return 0;
+}
+
+int wh_MessageAuth_TranslateUserGetResponse(
+ uint16_t magic, const whMessageAuth_UserGetResponse* src,
+ whMessageAuth_UserGetResponse* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, rc);
+ WH_T16(magic, dest, src, user_id);
+ if (src != dest) {
+ memcpy(dest->permissions, src->permissions, sizeof(dest->permissions));
+ }
+ return 0;
+}
+
+int wh_MessageAuth_TranslateUserSetPermissionsRequest(
+ uint16_t magic, const whMessageAuth_UserSetPermissionsRequest* src,
+ whMessageAuth_UserSetPermissionsRequest* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T16(magic, dest, src, user_id);
+ if (src != dest) {
+ memcpy(dest->permissions, src->permissions, sizeof(dest->permissions));
+ }
+ return 0;
+}
+
+int wh_MessageAuth_TranslateUserSetCredentialsRequest(
+ uint16_t magic, const void* src_packet, uint16_t src_size,
+ whMessageAuth_UserSetCredentialsRequest* dest_header,
+ uint8_t* dest_current_creds, uint8_t* dest_new_creds)
+{
+ const whMessageAuth_UserSetCredentialsRequest* src_header;
+ const uint8_t* src_data;
+ uint16_t header_size = sizeof(whMessageAuth_UserSetCredentialsRequest);
+ uint16_t expected_size;
+
+ if ((src_packet == NULL) || (dest_header == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (src_size < header_size) {
+ return WH_ERROR_BADARGS;
+ }
+
+ src_header = (const whMessageAuth_UserSetCredentialsRequest*)src_packet;
+ src_data = (const uint8_t*)src_packet + header_size;
+
+ /* Translate header fields */
+ WH_T16(magic, dest_header, src_header, user_id);
+ WH_T16(magic, dest_header, src_header, method);
+ WH_T16(magic, dest_header, src_header, current_credentials_len);
+ WH_T16(magic, dest_header, src_header, new_credentials_len);
+
+ if (src_header->current_credentials_len >
+ WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+ if (src_header->new_credentials_len > WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
+
+ /* Validate lengths */
+ expected_size = header_size + src_header->current_credentials_len +
+ src_header->new_credentials_len;
+ if (src_size < expected_size) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Copy variable-length credential data */
+ if (dest_current_creds != NULL && src_header->current_credentials_len > 0) {
+ memcpy(dest_current_creds, src_data,
+ src_header->current_credentials_len);
+ }
+ if (dest_new_creds != NULL && src_header->new_credentials_len > 0) {
+ memcpy(dest_new_creds, src_data + src_header->current_credentials_len,
+ src_header->new_credentials_len);
+ }
+
+ return 0;
+}
diff --git a/src/wh_server.c b/src/wh_server.c
index 83df8acb..88df2fc3 100644
--- a/src/wh_server.c
+++ b/src/wh_server.c
@@ -42,10 +42,18 @@
#include "wolfhsm/wh_message.h"
#include "wolfhsm/wh_message_comm.h"
#include "wolfhsm/wh_message_nvm.h"
+#include "wolfhsm/wh_message_auth.h"
+#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO)
+#include "wolfhsm/wh_message_cert.h"
+#endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !WOLFHSM_CFG_NO_CRYPTO */
/* Server API's */
#include "wolfhsm/wh_server.h"
#include "wolfhsm/wh_server_nvm.h"
+#ifndef WOLFHSM_CFG_NO_AUTHENTICATION
+#include "wolfhsm/wh_auth.h"
+#endif /* WOLFHSM_CFG_NO_AUTHENTICATION */
+#include "wolfhsm/wh_server_auth.h"
#include "wolfhsm/wh_server_crypto.h"
#include "wolfhsm/wh_server_keystore.h"
#include "wolfhsm/wh_server_counter.h"
@@ -75,6 +83,7 @@ int wh_Server_Init(whServerContext* server, whServerConfig* config)
memset(server, 0, sizeof(*server));
server->nvm = config->nvm;
+ server->auth = config->auth;
#ifndef WOLFHSM_CFG_NO_CRYPTO
server->crypto = config->crypto;
@@ -271,6 +280,16 @@ static int _wh_Server_HandleCommRequest(whServerContext* server,
{
/* No message */
/* Process the close action */
+
+#ifndef WOLFHSM_CFG_NO_AUTHENTICATION
+ /* Log out the current user when communication channel closes */
+ if (server->auth != NULL && server->auth->user.user_id !=
+ WH_USER_ID_INVALID) {
+ whUserId user_id = server->auth->user.user_id;
+ (void)wh_Auth_Logout(server->auth, user_id);
+ }
+#endif /* WOLFHSM_CFG_NO_AUTHENTICATION */
+
wh_Server_SetConnected(server, WH_COMM_DISCONNECTED);
*out_resp_size = 0;
@@ -316,6 +335,173 @@ static int _wh_Server_HandlePkcs11Request(whServerContext* server,
return rc;
}
+/* Helper to format an authorization error response for any group/action.
+ * All response structures have int32_t rc as the first field.
+ * Returns the response size to send. */
+static uint16_t _wh_Server_FormatAuthErrorResponse(uint16_t magic,
+ uint16_t group,
+ uint16_t action,
+ int32_t error_code,
+ void* resp_packet)
+{
+ uint16_t resp_size = sizeof(int32_t); /* Minimum: just the rc field */
+
+ if (resp_packet == NULL) {
+ return 0;
+ }
+
+ /* Write error code to first int32_t (rc field) - all responses start with
+ * this */
+ *(int32_t*)resp_packet =
+ (int32_t)wh_Translate32(magic, (uint32_t)error_code);
+
+ switch (group) {
+ case WH_MESSAGE_GROUP_AUTH:
+ /* Auth group has some responses larger than SimpleResponse */
+ switch (action) {
+ case WH_MESSAGE_AUTH_ACTION_LOGIN: {
+ whMessageAuth_LoginResponse resp = {0};
+ resp.rc = error_code;
+ resp.user_id = WH_USER_ID_INVALID;
+ wh_MessageAuth_TranslateLoginResponse(
+ magic, &resp,
+ (whMessageAuth_LoginResponse*)resp_packet);
+ resp_size = sizeof(resp);
+ break;
+ }
+ case WH_MESSAGE_AUTH_ACTION_USER_ADD: {
+ whMessageAuth_UserAddResponse resp = {0};
+ resp.rc = error_code;
+ resp.user_id = WH_USER_ID_INVALID;
+ wh_MessageAuth_TranslateUserAddResponse(
+ magic, &resp,
+ (whMessageAuth_UserAddResponse*)resp_packet);
+ resp_size = sizeof(resp);
+ break;
+ }
+ case WH_MESSAGE_AUTH_ACTION_USER_GET: {
+ whMessageAuth_UserGetResponse resp = {0};
+ resp.rc = error_code;
+ resp.user_id = WH_USER_ID_INVALID;
+ memset(resp.permissions, 0, sizeof(resp.permissions));
+ wh_MessageAuth_TranslateUserGetResponse(
+ magic, &resp,
+ (whMessageAuth_UserGetResponse*)resp_packet);
+ resp_size = sizeof(resp);
+ break;
+ }
+ default: {
+ /* Use SimpleResponse for other auth actions */
+ whMessageAuth_SimpleResponse resp = {0};
+ resp.rc = error_code;
+ wh_MessageAuth_TranslateSimpleResponse(
+ magic, &resp,
+ (whMessageAuth_SimpleResponse*)resp_packet);
+ resp_size = sizeof(resp);
+ break;
+ }
+ }
+ break;
+
+ case WH_MESSAGE_GROUP_NVM:
+ /* NVM group - some actions have larger responses than
+ * SimpleResponse */
+ switch (action) {
+ case WH_MESSAGE_NVM_ACTION_INIT: {
+ whMessageNvm_InitResponse resp = {0};
+ resp.rc = error_code;
+ resp.servernvm_id = 0;
+ resp.clientnvm_id = 0;
+ wh_MessageNvm_TranslateInitResponse(
+ magic, &resp, (whMessageNvm_InitResponse*)resp_packet);
+ resp_size = sizeof(resp);
+ break;
+ }
+ case WH_MESSAGE_NVM_ACTION_GETAVAILABLE: {
+ whMessageNvm_GetAvailableResponse resp = {0};
+ resp.rc = error_code;
+ resp.avail_size = 0;
+ resp.reclaim_size = 0;
+ resp.avail_objects = 0;
+ resp.reclaim_objects = 0;
+ wh_MessageNvm_TranslateGetAvailableResponse(
+ magic, &resp,
+ (whMessageNvm_GetAvailableResponse*)resp_packet);
+ resp_size = sizeof(resp);
+ break;
+ }
+ case WH_MESSAGE_NVM_ACTION_LIST: {
+ whMessageNvm_ListResponse resp = {0};
+ resp.rc = error_code;
+ resp.count = 0;
+ resp.id = 0;
+ wh_MessageNvm_TranslateListResponse(
+ magic, &resp, (whMessageNvm_ListResponse*)resp_packet);
+ resp_size = sizeof(resp);
+ break;
+ }
+ case WH_MESSAGE_NVM_ACTION_GETMETADATA: {
+ whMessageNvm_GetMetadataResponse resp = {0};
+ resp.rc = error_code;
+ resp.id = 0;
+ resp.access = 0;
+ resp.flags = 0;
+ resp.len = 0;
+ memset(resp.label, 0, sizeof(resp.label));
+ wh_MessageNvm_TranslateGetMetadataResponse(
+ magic, &resp,
+ (whMessageNvm_GetMetadataResponse*)resp_packet);
+ resp_size = sizeof(resp);
+ break;
+ }
+ case WH_MESSAGE_NVM_ACTION_READ: {
+ whMessageNvm_ReadResponse resp = {0};
+ resp.rc = error_code;
+ wh_MessageNvm_TranslateReadResponse(
+ magic, &resp, (whMessageNvm_ReadResponse*)resp_packet);
+ resp_size = sizeof(resp);
+ break;
+ }
+ default: {
+ /* Use SimpleResponse for other NVM actions */
+ whMessageNvm_SimpleResponse resp = {0};
+ resp.rc = error_code;
+ wh_MessageNvm_TranslateSimpleResponse(
+ magic, &resp,
+ (whMessageNvm_SimpleResponse*)resp_packet);
+ resp_size = sizeof(resp);
+ break;
+ }
+ }
+ break;
+
+#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO)
+ case WH_MESSAGE_GROUP_CERT:
+ /* Cert group - use SimpleResponse for all actions */
+ {
+ whMessageCert_SimpleResponse resp = {0};
+ resp.rc = error_code;
+ wh_MessageCert_TranslateSimpleResponse(
+ magic, &resp, (whMessageCert_SimpleResponse*)resp_packet);
+ resp_size = sizeof(resp);
+ }
+ break;
+#endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !WOLFHSM_CFG_NO_CRYPTO */
+
+ default:
+ /* For other groups, use minimum size (just rc field).
+ * Most response structures have int32_t rc as first field, so
+ * clients should be able to read at least the error code. If a
+ * group needs special handling, it can be added above. */
+ /* Error code already written above */
+ resp_size = sizeof(int32_t);
+ break;
+ }
+
+ return resp_size;
+}
+
+
int wh_Server_HandleRequestMessage(whServerContext* server)
{
uint16_t magic = 0;
@@ -346,6 +532,38 @@ int wh_Server_HandleRequestMessage(whServerContext* server)
if (rc == WH_ERROR_OK) {
group = WH_MESSAGE_GROUP(kind);
action = WH_MESSAGE_ACTION(kind);
+
+#ifndef WOLFHSM_CFG_NO_AUTHENTICATION
+ /* General authentication check for if user has permissions for the
+ * group and action requested. When dealing with key ID's there should
+ * be an additional authorization check after parsing the request and
+ * translating the key ID and before it is used. */
+ if (server->auth != NULL) {
+ rc = wh_Auth_CheckRequestAuthorization(server->auth, group, action);
+ if (rc != WH_ERROR_OK) {
+ /* Authorization failed - format and send error response to
+ * client */
+ int32_t error_code = (int32_t)WH_AUTH_PERMISSION_ERROR;
+ uint16_t resp_size = _wh_Server_FormatAuthErrorResponse(
+ magic, group, action, error_code, data);
+
+ /* Send error response to client */
+ do {
+ rc = wh_CommServer_SendResponse(server->comm, magic, kind,
+ seq, resp_size, data);
+ } while (rc == WH_ERROR_NOTREADY);
+
+ /* Log the authorization failure */
+ WH_LOG_ON_ERROR_F(
+ &server->log, WH_LOG_LEVEL_ERROR, WH_AUTH_PERMISSION_ERROR,
+ "Authorization failed for (group=%d, action=%d, seq=%d)",
+ group, action, seq);
+
+ return rc;
+ }
+ }
+#endif /* WOLFHSM_CFG_NO_AUTHENTICATION */
+
switch (group) {
case WH_MESSAGE_GROUP_COMM:
@@ -358,6 +576,11 @@ int wh_Server_HandleRequestMessage(whServerContext* server)
size, data, &size, data);
break;
+ case WH_MESSAGE_GROUP_AUTH:
+ rc = wh_Server_HandleAuthRequest(server, magic, action, seq, size,
+ data, &size, data);
+ break;
+
case WH_MESSAGE_GROUP_COUNTER:
rc = wh_Server_HandleCounter(server, magic, action, size, data,
&size, data);
diff --git a/src/wh_server_auth.c b/src/wh_server_auth.c
new file mode 100644
index 00000000..f836d0d0
--- /dev/null
+++ b/src/wh_server_auth.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2025 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+/*
+ * src/wh_server_auth.c
+ *
+ * Server-side Auth Manager request handler
+ */
+
+/* Pick up compile-time configuration */
+#include "wolfhsm/wh_settings.h"
+
+#ifdef WOLFHSM_CFG_ENABLE_SERVER
+
+/* System libraries */
+#include
+#include /* For NULL */
+
+/* Common WolfHSM types and defines shared with the server */
+#include "wolfhsm/wh_error.h"
+#include "wolfhsm/wh_comm.h"
+
+#include "wolfhsm/wh_auth.h"
+
+#include "wolfhsm/wh_message.h"
+#include "wolfhsm/wh_message_auth.h"
+
+#include "wolfhsm/wh_server.h"
+#include "wolfhsm/wh_server_auth.h"
+
+/* This function is responsible for handling all authentication and
+ * authorization requests from the client.
+ */
+int wh_Server_HandleAuthRequest(whServerContext* server, uint16_t magic,
+ uint16_t action, uint16_t seq,
+ uint16_t req_size, const void* req_packet,
+ uint16_t* out_resp_size, void* resp_packet)
+{
+ int rc = 0;
+
+ if ((server == NULL) || (req_packet == NULL) || (resp_packet == NULL) ||
+ (out_resp_size == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* III: Translate function returns do not need to be checked since args
+ * are not NULL */
+
+ switch (action) {
+
+ case WH_MESSAGE_AUTH_ACTION_LOGIN: {
+ whMessageAuth_LoginRequest req = {0};
+ whMessageAuth_LoginResponse resp = {0};
+ int loggedIn = 0;
+ uint8_t auth_data[WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN] = {0};
+
+ rc = wh_MessageAuth_TranslateLoginRequest(
+ magic, req_packet, req_size, &req, auth_data);
+ if (rc != WH_ERROR_OK) {
+ resp.rc = rc;
+ }
+
+ if (resp.rc == WH_ERROR_OK) {
+ rc = wh_Auth_Login(server->auth, server->comm->client_id,
+ req.method, req.username, auth_data,
+ req.auth_data_len, &loggedIn);
+ resp.rc = rc;
+ if (rc == WH_ERROR_OK) {
+ if (loggedIn == 0) {
+ resp.rc = WH_AUTH_LOGIN_FAILED;
+ resp.user_id = WH_USER_ID_INVALID;
+ }
+ else {
+ resp.user_id = server->auth->user.user_id;
+ }
+ }
+ }
+ wh_MessageAuth_TranslateLoginResponse(
+ magic, &resp, (whMessageAuth_LoginResponse*)resp_packet);
+ *out_resp_size = sizeof(resp);
+ } break;
+
+ case WH_MESSAGE_AUTH_ACTION_LOGOUT: {
+ whMessageAuth_LogoutRequest req = {0};
+ whMessageAuth_SimpleResponse resp = {0};
+
+ if (req_size != sizeof(req)) {
+ resp.rc = WH_ERROR_BADARGS;
+ }
+ else {
+ wh_MessageAuth_TranslateLogoutRequest(magic, req_packet, &req);
+
+ rc = wh_Auth_Logout(server->auth, req.user_id);
+ resp.rc = rc;
+ }
+ wh_MessageAuth_TranslateSimpleResponse(
+ magic, &resp, (whMessageAuth_SimpleResponse*)resp_packet);
+ *out_resp_size = sizeof(resp);
+ } break;
+
+ case WH_MESSAGE_AUTH_ACTION_USER_ADD: {
+ whMessageAuth_UserAddRequest req = {0};
+ whMessageAuth_UserAddResponse resp = {0};
+ whAuthPermissions permissions = {0};
+ uint8_t credentials[WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN] = {0};
+
+ rc = wh_MessageAuth_TranslateUserAddRequest(
+ magic, req_packet, req_size, &req, credentials);
+ if (rc != WH_ERROR_OK) {
+ resp.rc = rc;
+ }
+ else {
+ if (wh_MessageAuth_UnflattenPermissions(req.permissions,
+ sizeof(req.permissions),
+ &permissions) != 0) {
+ resp.rc = WH_ERROR_BADARGS;
+ }
+ else {
+ rc = wh_Auth_UserAdd(server->auth, req.username,
+ &resp.user_id, permissions,
+ req.method, credentials,
+ req.credentials_len);
+ resp.rc = rc;
+ memset(credentials, 0, req.credentials_len);
+ }
+ }
+ wh_MessageAuth_TranslateUserAddResponse(
+ magic, &resp, (whMessageAuth_UserAddResponse*)resp_packet);
+ *out_resp_size = sizeof(resp);
+ } break;
+
+ case WH_MESSAGE_AUTH_ACTION_USER_DELETE: {
+ whMessageAuth_UserDeleteRequest req = {0};
+ whMessageAuth_SimpleResponse resp = {0};
+
+ if (req_size != sizeof(req)) {
+ resp.rc = WH_ERROR_BADARGS;
+ }
+ else {
+ wh_MessageAuth_TranslateUserDeleteRequest(magic, req_packet,
+ &req);
+ rc = wh_Auth_UserDelete(server->auth, req.user_id);
+ resp.rc = rc;
+ }
+ wh_MessageAuth_TranslateSimpleResponse(
+ magic, &resp, (whMessageAuth_SimpleResponse*)resp_packet);
+ *out_resp_size = sizeof(resp);
+ } break;
+
+ case WH_MESSAGE_AUTH_ACTION_USER_GET: {
+ whMessageAuth_UserGetRequest req = {0};
+ whMessageAuth_UserGetResponse resp = {0};
+
+ if (req_size != sizeof(req)) {
+ resp.rc = WH_ERROR_BADARGS;
+ }
+ else {
+ whUserId out_user_id = WH_USER_ID_INVALID;
+ whAuthPermissions out_permissions = {0};
+
+ wh_MessageAuth_TranslateUserGetRequest(magic, req_packet, &req);
+
+ rc = wh_Auth_UserGet(server->auth, req.username,
+ &out_user_id, &out_permissions);
+ resp.rc = rc;
+ if (rc == WH_ERROR_OK) {
+ resp.user_id = out_user_id;
+ wh_MessageAuth_FlattenPermissions(&out_permissions,
+ resp.permissions,
+ sizeof(resp.permissions));
+ }
+ }
+ wh_MessageAuth_TranslateUserGetResponse(
+ magic, &resp, (whMessageAuth_UserGetResponse*)resp_packet);
+ *out_resp_size = sizeof(resp);
+ } break;
+
+ case WH_MESSAGE_AUTH_ACTION_USER_SET_PERMISSIONS: {
+ whMessageAuth_UserSetPermissionsRequest req = {0};
+ whMessageAuth_SimpleResponse resp = {0};
+ whAuthPermissions permissions = {0};
+
+ if (req_size != sizeof(req)) {
+ resp.rc = WH_ERROR_BADARGS;
+ }
+ else {
+ wh_MessageAuth_TranslateUserSetPermissionsRequest(
+ magic, req_packet, &req);
+ if (wh_MessageAuth_UnflattenPermissions(req.permissions,
+ sizeof(req.permissions),
+ &permissions) != 0) {
+ resp.rc = WH_ERROR_BADARGS;
+ }
+ else {
+ rc = wh_Auth_UserSetPermissions(server->auth, req.user_id,
+ permissions);
+ resp.rc = rc;
+ }
+ }
+ wh_MessageAuth_TranslateSimpleResponse(
+ magic, &resp, (whMessageAuth_SimpleResponse*)resp_packet);
+ *out_resp_size = sizeof(resp);
+ } break;
+
+ case WH_MESSAGE_AUTH_ACTION_USER_SET_CREDENTIALS: {
+ whMessageAuth_UserSetCredentialsRequest req_header = {0};
+ uint8_t current_creds[WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN] = {0};
+ uint8_t new_creds[WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN] = {0};
+ whMessageAuth_SimpleResponse resp = {0};
+ uint16_t min_size = sizeof(whMessageAuth_UserSetCredentialsRequest);
+
+ if (req_size < min_size) {
+ resp.rc = WH_ERROR_BADARGS;
+ }
+ else {
+ rc = wh_MessageAuth_TranslateUserSetCredentialsRequest(
+ magic, req_packet, req_size, &req_header, current_creds,
+ new_creds);
+ if (rc != 0) {
+ resp.rc = rc;
+ }
+ else {
+ rc = wh_Auth_UserSetCredentials(
+ server->auth, req_header.user_id, req_header.method,
+ (req_header.current_credentials_len > 0) ? current_creds
+ : NULL,
+ req_header.current_credentials_len,
+ (req_header.new_credentials_len > 0) ? new_creds : NULL,
+ req_header.new_credentials_len);
+ resp.rc = rc;
+ memset(current_creds, 0, sizeof(current_creds));
+ memset(new_creds, 0, sizeof(new_creds));
+ }
+ }
+ wh_MessageAuth_TranslateSimpleResponse(
+ magic, &resp, (whMessageAuth_SimpleResponse*)resp_packet);
+ *out_resp_size = sizeof(resp);
+ } break;
+
+ default:
+ /* Unknown request. Respond with empty packet */
+ /* TODO: Use ErrorResponse packet instead */
+ *out_resp_size = 0;
+ rc = WH_ERROR_NOTIMPL;
+ }
+
+ (void)seq;
+ return rc;
+}
+
+#endif /* WOLFHSM_CFG_ENABLE_SERVER */
diff --git a/test/wh_test.c b/test/wh_test.c
index 29ba86b8..1cd3f9ce 100644
--- a/test/wh_test.c
+++ b/test/wh_test.c
@@ -40,6 +40,7 @@
#include "wh_test_keywrap.h"
#include "wh_test_multiclient.h"
#include "wh_test_log.h"
+#include "wh_test_auth.h"
#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER)
#include "wh_test_cert.h"
@@ -87,6 +88,9 @@ int whTest_Unit(void)
WH_TEST_ASSERT(0 == whTest_Comm());
WH_TEST_ASSERT(0 == whTest_ClientServer());
+ /* Auth tests */
+ WH_TEST_ASSERT(0 == whTest_AuthMEM());
+
#ifndef WOLFHSM_CFG_NO_CRYPTO
/* Crypto Tests */
WH_TEST_ASSERT(0 == whTest_Crypto());
@@ -113,7 +117,6 @@ int whTest_Unit(void)
#endif
#endif /* !WOLFHSM_CFG_NO_CRYPTO */
-
return 0;
}
#endif /* WOLFHSM_CFG_ENABLE_CLIENT && WOLFHSM_CFG_ENABLE_SERVER */
@@ -147,6 +150,8 @@ int whTest_ClientConfig(whClientConfig* clientCfg)
WH_TEST_RETURN_ON_FAIL(whTest_WolfCryptTestCfg(clientCfg));
#endif /* WOLFHSM_CFG_TEST_WOLFCRYPTTEST */
+ WH_TEST_RETURN_ON_FAIL(whTest_AuthTCP(clientCfg));
+
return WH_ERROR_OK;
}
diff --git a/test/wh_test_auth.c b/test/wh_test_auth.c
new file mode 100644
index 00000000..5bb6e98f
--- /dev/null
+++ b/test/wh_test_auth.c
@@ -0,0 +1,1237 @@
+/*
+ * Copyright (C) 2025 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+/*
+ * test/wh_test_auth.c
+ */
+
+#include
+#include
+#include
+
+#include "wolfhsm/wh_settings.h"
+#include "wolfhsm/wh_error.h"
+#include "wolfhsm/wh_comm.h"
+#include "wolfhsm/wh_transport_mem.h"
+#include "wolfhsm/wh_client.h"
+#include "wolfhsm/wh_server.h"
+#include "wolfhsm/wh_auth.h"
+#include "wolfhsm/wh_nvm.h"
+#include "wolfhsm/wh_nvm_flash.h"
+#include "wolfhsm/wh_flash_ramsim.h"
+#include "wolfhsm/wh_message.h"
+#include "wolfhsm/wh_message_auth.h"
+
+#include "wh_test_common.h"
+#include "wh_test_auth.h"
+
+#if defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP)
+#include "port/posix/posix_transport_tcp.h"
+#endif
+
+#include "port/posix/posix_auth.h"
+
+#define FLASH_RAM_SIZE (1024 * 1024) /* 1MB */
+#define BUFFER_SIZE 4096
+
+#ifndef TEST_ADMIN_USERNAME
+#define TEST_ADMIN_USERNAME "admin"
+#endif
+#ifndef TEST_ADMIN_PIN
+#define TEST_ADMIN_PIN "1234"
+#endif
+
+#if !defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) && defined(WOLFHSM_CFG_ENABLE_SERVER)
+/* Memory transport mode - setup structures */
+static uint8_t req_buffer[BUFFER_SIZE] = {0};
+static uint8_t resp_buffer[BUFFER_SIZE] = {0};
+static whTransportMemConfig tmcf[1] = {0};
+static whTransportClientCb tccb[1] = {WH_TRANSPORT_MEM_CLIENT_CB};
+static whTransportMemClientContext tmcc[1] = {0};
+static whCommClientConfig cc_conf[1] = {0};
+static whClientConfig c_conf[1] = {0};
+static whTransportServerCb tscb[1] = {WH_TRANSPORT_MEM_SERVER_CB};
+static whTransportMemServerContext tmsc[1] = {0};
+static whCommServerConfig cs_conf[1] = {0};
+static whServerContext server[1] = {0};
+static whClientContext client[1] = {0};
+
+/* NVM setup */
+static uint8_t memory[FLASH_RAM_SIZE] = {0};
+static whFlashRamsimCtx fc[1] = {0};
+static whFlashRamsimCfg fc_conf[1];
+static const whFlashCb fcb[1] = {WH_FLASH_RAMSIM_CB};
+static whTestNvmBackendUnion nvm_setup;
+static whNvmConfig n_conf[1] = {0};
+static whNvmContext nvm[1] = {{0}};
+
+/* Auth setup following wh_posix_server pattern */
+static whAuthCb default_auth_cb = {
+ .Init = posixAuth_Init,
+ .Cleanup = posixAuth_Cleanup,
+ .Login = posixAuth_Login,
+ .Logout = posixAuth_Logout,
+ .CheckRequestAuthorization = posixAuth_CheckRequestAuthorization,
+ .CheckKeyAuthorization = posixAuth_CheckKeyAuthorization,
+ .UserAdd = posixAuth_UserAdd,
+ .UserDelete = posixAuth_UserDelete,
+ .UserSetPermissions = posixAuth_UserSetPermissions,
+ .UserGet = posixAuth_UserGet,
+ .UserSetCredentials = posixAuth_UserSetCredentials};
+static whAuthContext auth_ctx = {0};
+
+#ifndef WOLFHSM_CFG_NO_CRYPTO
+static whServerCryptoContext crypto[1] = {{.devId = INVALID_DEVID}};
+#endif
+
+/* Setup helper for memory transport mode */
+static int _whTest_Auth_SetupMemory(whClientContext** out_client)
+{
+ int rc = WH_ERROR_OK;
+ whAuthPermissions permissions;
+ uint16_t out_user_id;
+ int i;
+
+ /* Initialize transport memory config - avoid compound literals for C90 */
+ tmcf->req = (whTransportMemCsr*)req_buffer;
+ tmcf->req_size = sizeof(req_buffer);
+ tmcf->resp = (whTransportMemCsr*)resp_buffer;
+ tmcf->resp_size = sizeof(resp_buffer);
+
+ /* Client configuration - avoid compound literals for C90 compatibility */
+ cc_conf->transport_cb = tccb;
+ cc_conf->transport_context = (void*)tmcc;
+ cc_conf->transport_config = (void*)tmcf;
+ cc_conf->client_id = WH_TEST_DEFAULT_CLIENT_ID;
+ c_conf->comm = cc_conf;
+
+ /* Server configuration */
+ cs_conf->transport_cb = tscb;
+ cs_conf->transport_context = (void*)tmsc;
+ cs_conf->transport_config = (void*)tmcf;
+ cs_conf->server_id = 124;
+
+ /* Flash RAM sim configuration */
+ fc_conf->size = FLASH_RAM_SIZE;
+ fc_conf->sectorSize = FLASH_RAM_SIZE / 2;
+ fc_conf->pageSize = 8;
+ fc_conf->erasedByte = (uint8_t)0;
+ fc_conf->memory = memory;
+
+ /* Initialize NVM */
+ WH_TEST_RETURN_ON_FAIL(whTest_NvmCfgBackend(
+ WH_NVM_TEST_BACKEND_FLASH, &nvm_setup, n_conf, fc_conf, fc, fcb));
+ WH_TEST_RETURN_ON_FAIL(wh_Nvm_Init(nvm, n_conf));
+
+#ifndef WOLFHSM_CFG_NO_CRYPTO
+ WH_TEST_RETURN_ON_FAIL(wolfCrypt_Init());
+ WH_TEST_RETURN_ON_FAIL(wc_InitRng_ex(crypto->rng, NULL, crypto->devId));
+#endif
+
+ /* Set up auth context following wh_posix_server pattern */
+ static void* auth_backend_context = NULL;
+ static whAuthConfig auth_config = {0};
+
+ auth_config.cb = &default_auth_cb;
+ auth_config.context = auth_backend_context;
+
+ rc = wh_Auth_Init(&auth_ctx, &auth_config);
+ if (rc != WH_ERROR_OK) {
+ WH_ERROR_PRINT("Failed to initialize Auth Manager: %d\n", rc);
+ return rc;
+ }
+
+ /* Add and admin user with permissions for everything */
+ memset(&permissions, 0xFF, sizeof(whAuthPermissions));
+ permissions.keyIdCount = 0;
+ for (i = 0; i < WH_AUTH_MAX_KEY_IDS; i++) {
+ permissions.keyIds[i] = 0;
+ }
+ rc = posixAuth_UserAdd(&auth_ctx, TEST_ADMIN_USERNAME, &out_user_id, permissions,
+ WH_AUTH_METHOD_PIN, TEST_ADMIN_PIN, strlen(TEST_ADMIN_PIN));
+ if (rc != WH_ERROR_OK) {
+ WH_ERROR_PRINT("Failed to add admin user: %d\n", rc);
+ return rc;
+ }
+
+ /* Server config with auth - avoid compound literals for C90 */
+ whServerConfig s_conf[1] = {{0}};
+ s_conf->comm_config = cs_conf;
+ s_conf->nvm = nvm;
+ s_conf->auth = &auth_ctx;
+#ifndef WOLFHSM_CFG_NO_CRYPTO
+ s_conf->crypto = crypto;
+#if defined WOLF_CRYPTO_CB
+ s_conf->devId = INVALID_DEVID;
+#endif
+#endif
+
+ /* Initialize server first (must be before client) */
+ WH_TEST_RETURN_ON_FAIL(wh_Server_Init(server, s_conf));
+
+ /* Initialize client */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_Init(client, c_conf));
+
+ /* Verify client comm is initialized */
+ WH_TEST_ASSERT_RETURN(client->comm != NULL);
+ WH_TEST_ASSERT_RETURN(client->comm->initialized == 1);
+
+ /* For memory transport, set server as connected (connect callback should
+ * handle this, but we set it explicitly to ensure it's connected) */
+ WH_TEST_RETURN_ON_FAIL(wh_Server_SetConnected(server, WH_COMM_CONNECTED));
+
+ /* Verify server is connected */
+ whCommConnected server_connected;
+ WH_TEST_RETURN_ON_FAIL(wh_Server_GetConnected(server, &server_connected));
+ WH_TEST_ASSERT_RETURN(server_connected == WH_COMM_CONNECTED);
+
+ /* Connect client to server - use non-blocking approach for memory transport
+ */
+ uint32_t client_id, server_id;
+
+ /* Verify server is ready (should return NOTREADY if no message) */
+ WH_TEST_ASSERT_RETURN(WH_ERROR_NOTREADY ==
+ wh_Server_HandleRequestMessage(server));
+
+ /* Send comm init request */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_CommInitRequest(client));
+
+ /* Process server message */
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+
+ /* Get comm init response */
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_CommInitResponse(client, &client_id, &server_id));
+ WH_TEST_ASSERT_RETURN(client_id == client->comm->client_id);
+
+ *out_client = client;
+ return WH_ERROR_OK;
+}
+
+/* Cleanup helper for memory transport mode */
+static int _whTest_Auth_CleanupMemory(void)
+{
+ wh_Client_Cleanup(client);
+ wh_Server_Cleanup(server);
+ wh_Auth_Cleanup(&auth_ctx);
+ wh_Nvm_Cleanup(nvm);
+#ifndef WOLFHSM_CFG_NO_CRYPTO
+ wc_FreeRng(crypto->rng);
+ wolfCrypt_Cleanup();
+#endif
+ return WH_ERROR_OK;
+}
+#endif /* !WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP && WOLFHSM_CFG_ENABLE_SERVER */
+
+
+/* ============================================================================
+ * Test Functions
+ * ============================================================================
+ */
+
+static int _whTest_Auth_LoginOp(whClientContext* client, whAuthMethod method,
+ const char* username, const void* auth_data,
+ uint16_t auth_data_len, int32_t* out_rc,
+ whUserId* out_user_id)
+{
+#if defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) || !defined(WOLFHSM_CFG_ENABLE_SERVER)
+ return wh_Client_AuthLogin(client, method, username, auth_data,
+ auth_data_len, out_rc, out_user_id);
+#else
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AuthLoginRequest(
+ client, method, username, auth_data, auth_data_len));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ return wh_Client_AuthLoginResponse(client, out_rc, out_user_id);
+#endif
+}
+
+static int _whTest_Auth_LogoutOp(whClientContext* client, whUserId user_id,
+ int32_t* out_rc)
+{
+#if defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) || !defined(WOLFHSM_CFG_ENABLE_SERVER)
+ return wh_Client_AuthLogout(client, user_id, out_rc);
+#else
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AuthLogoutRequest(client, user_id));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ return wh_Client_AuthLogoutResponse(client, out_rc);
+#endif
+}
+
+static int _whTest_Auth_UserAddOp(whClientContext* client, const char* username,
+ whAuthPermissions permissions,
+ whAuthMethod method, const void* credentials,
+ uint16_t credentials_len, int32_t* out_rc,
+ whUserId* out_user_id)
+{
+#if defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) || !defined(WOLFHSM_CFG_ENABLE_SERVER)
+ return wh_Client_AuthUserAdd(client, username, permissions, method,
+ credentials, credentials_len, out_rc,
+ out_user_id);
+#else
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AuthUserAddRequest(
+ client, username, permissions, method, credentials, credentials_len));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ return wh_Client_AuthUserAddResponse(client, out_rc, out_user_id);
+#endif
+}
+
+static int _whTest_Auth_UserDeleteOp(whClientContext* client, whUserId user_id,
+ int32_t* out_rc)
+{
+#if defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) || !defined(WOLFHSM_CFG_ENABLE_SERVER)
+ return wh_Client_AuthUserDelete(client, user_id, out_rc);
+#else
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AuthUserDeleteRequest(client, user_id));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ return wh_Client_AuthUserDeleteResponse(client, out_rc);
+#endif
+}
+
+static int _whTest_Auth_UserSetPermsOp(whClientContext* client,
+ whUserId user_id,
+ whAuthPermissions permissions,
+ int32_t* out_rc)
+{
+#if defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) || !defined(WOLFHSM_CFG_ENABLE_SERVER)
+ return wh_Client_AuthUserSetPermissions(client, user_id, permissions,
+ out_rc);
+#else
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_AuthUserSetPermissionsRequest(client, user_id, permissions));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ return wh_Client_AuthUserSetPermissionsResponse(client, out_rc);
+#endif
+}
+
+static int _whTest_Auth_UserSetCredsOp(
+ whClientContext* client, whUserId user_id, whAuthMethod method,
+ const void* current_credentials, uint16_t current_credentials_len,
+ const void* new_credentials, uint16_t new_credentials_len, int32_t* out_rc)
+{
+#if defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) || !defined(WOLFHSM_CFG_ENABLE_SERVER)
+ return wh_Client_AuthUserSetCredentials(
+ client, user_id, method, current_credentials, current_credentials_len,
+ new_credentials, new_credentials_len, out_rc);
+#else
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AuthUserSetCredentialsRequest(
+ client, user_id, method, current_credentials, current_credentials_len,
+ new_credentials, new_credentials_len));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ return wh_Client_AuthUserSetCredentialsResponse(client, out_rc);
+#endif
+}
+
+static int _whTest_Auth_UserGetOp(whClientContext* client, const char* username,
+ int32_t* out_rc, whUserId* out_user_id,
+ whAuthPermissions* out_permissions)
+{
+#if defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) || !defined(WOLFHSM_CFG_ENABLE_SERVER)
+ return wh_Client_AuthUserGet(client, username, out_rc, out_user_id,
+ out_permissions);
+#else
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AuthUserGetRequest(client, username));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ return wh_Client_AuthUserGetResponse(client, out_rc, out_user_id,
+ out_permissions);
+#endif
+}
+
+static void _whTest_Auth_DeleteUserByName(whClientContext* client,
+ const char* username)
+{
+ int32_t server_rc = 0;
+ whUserId user_id = WH_USER_ID_INVALID;
+ whAuthPermissions perms;
+
+ memset(&perms, 0, sizeof(perms));
+ _whTest_Auth_UserGetOp(client, username, &server_rc, &user_id, &perms);
+ if (server_rc == WH_ERROR_OK && user_id != WH_USER_ID_INVALID) {
+ _whTest_Auth_UserDeleteOp(client, user_id, &server_rc);
+ }
+}
+
+static int _whTest_Auth_BadArgs(void)
+{
+ int rc = 0;
+ int loggedIn = 1;
+ whAuthContext ctx;
+ whAuthConfig config;
+ whAuthPermissions perms;
+ whUserId user_id = WH_USER_ID_INVALID;
+ int32_t server_rc = 0;
+
+ memset(&ctx, 0, sizeof(ctx));
+ memset(&config, 0, sizeof(config));
+ memset(&perms, 0, sizeof(perms));
+
+ WH_TEST_PRINT(" Test: Auth core bad args\n");
+ rc = wh_Auth_Init(NULL, &config);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Auth_Init(&ctx, NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ rc = wh_Auth_Cleanup(NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Auth_Cleanup(&ctx);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ rc =
+ wh_Auth_Login(NULL, 0, WH_AUTH_METHOD_PIN, "user", "pin", 3, &loggedIn);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Auth_Login(&ctx, 0, WH_AUTH_METHOD_PIN, "user", "pin", 3, NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Client_AuthLoginRequest(NULL, WH_AUTH_METHOD_PIN, "user", "pin", 3);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Client_AuthLoginResponse(NULL, &server_rc, &user_id);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ rc = wh_Auth_Logout(NULL, 1);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Auth_Logout(&ctx, 1);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ rc = wh_Auth_UserAdd(&ctx, "user", &user_id, perms, WH_AUTH_METHOD_PIN,
+ "pin", 3);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Auth_UserDelete(&ctx, 1);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Auth_UserSetPermissions(&ctx, 1, perms);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Auth_UserGet(&ctx, "user", &user_id, &perms);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Auth_UserSetCredentials(&ctx, 1, WH_AUTH_METHOD_PIN, "pin", 3,
+ "new", 3);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Auth_Logout(NULL, 999); /* This test may be troublesome if the port
+ * supports 999 users */
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ WH_TEST_PRINT(" Test: Auth client bad args\n");
+ rc = wh_Client_AuthLoginRequest(NULL, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME,
+ TEST_ADMIN_PIN, strlen(TEST_ADMIN_PIN));
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Client_AuthLogoutRequest(NULL, WH_USER_ID_INVALID);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ rc = wh_Auth_CheckRequestAuthorization(NULL,
+ WH_MESSAGE_GROUP_AUTH,
+ WH_MESSAGE_AUTH_ACTION_LOGIN);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ rc = wh_Client_AuthUserAddRequest(NULL, "baduser", perms,
+ WH_AUTH_METHOD_NONE, "x", 1);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Client_AuthUserDeleteRequest(NULL, WH_USER_ID_INVALID);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Client_AuthUserSetPermissionsRequest(NULL, WH_USER_ID_INVALID, perms);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Client_AuthUserSetCredentialsRequest(NULL, WH_USER_ID_INVALID,
+ WH_AUTH_METHOD_PIN, NULL, 0, "new", 3);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Client_AuthUserGetRequest(NULL, "missing");
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ return WH_TEST_SUCCESS;
+}
+
+static int _whTest_Auth_MessageBadArgs(void)
+{
+ int rc = 0;
+ whMessageAuth_SimpleResponse simple = {0};
+ whMessageAuth_LoginRequest login_hdr = {0};
+ whMessageAuth_LoginRequest login_out = {0};
+ whMessageAuth_UserAddRequest add_hdr = {0};
+ whMessageAuth_UserAddRequest add_out = {0};
+ whMessageAuth_UserSetCredentialsRequest set_hdr = {0};
+
+ WH_TEST_PRINT(" Test: Auth message bad args\n");
+ rc = wh_MessageAuth_TranslateSimpleResponse(0, NULL, &simple);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_MessageAuth_TranslateSimpleResponse(0, &simple, NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ rc = wh_MessageAuth_TranslateLoginRequest(0, NULL, 0, &login_out, NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_MessageAuth_TranslateLoginRequest(0, &login_hdr, 0, &login_out,
+ NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ memset(&login_hdr, 0, sizeof(login_hdr));
+ login_hdr.auth_data_len =
+ (uint16_t)(WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN + 1);
+ rc = wh_MessageAuth_TranslateLoginRequest(0, &login_hdr, sizeof(login_hdr),
+ &login_out, NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ rc = wh_MessageAuth_TranslateUserAddRequest(0, NULL, 0, &add_out, NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ memset(&add_hdr, 0, sizeof(add_hdr));
+ add_hdr.credentials_len =
+ (uint16_t)(WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN + 1);
+ rc = wh_MessageAuth_TranslateUserAddRequest(0, &add_hdr, sizeof(add_hdr),
+ &add_out, NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BUFFER_SIZE);
+
+ rc = wh_MessageAuth_TranslateUserSetCredentialsRequest(0, NULL, 0, &set_hdr,
+ NULL, NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ memset(&set_hdr, 0, sizeof(set_hdr));
+ set_hdr.current_credentials_len = 4;
+ set_hdr.new_credentials_len = 4;
+ rc = wh_MessageAuth_TranslateUserSetCredentialsRequest(
+ 0, &set_hdr, sizeof(set_hdr), &set_hdr, NULL, NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ return WH_TEST_SUCCESS;
+}
+
+/* Logout Tests */
+int whTest_AuthLogout(whClientContext* client)
+{
+ int32_t server_rc;
+ whUserId user_id;
+ int32_t login_rc;
+ whAuthPermissions out_perms;
+
+ if (client == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Test 2: Logout after login */
+ WH_TEST_PRINT(" Test: Logout after login\n");
+ /* First login */
+ memset(&out_perms, 0, sizeof(out_perms));
+ login_rc = 0;
+ user_id = WH_USER_ID_INVALID;
+ /* Verify client is valid and comm is initialized */
+ WH_TEST_ASSERT_RETURN(client != NULL);
+ WH_TEST_ASSERT_RETURN(client->comm != NULL);
+ WH_TEST_ASSERT_RETURN(client->comm->initialized == 1);
+ WH_TEST_ASSERT_RETURN(client->comm->hdr != NULL);
+ WH_TEST_ASSERT_RETURN(client->comm->transport_cb != NULL);
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(
+ client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, 4,
+ &login_rc, &user_id));
+ WH_TEST_ASSERT_RETURN(login_rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID);
+
+ /* Then logout - use blocking version */
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LogoutOp(client, user_id, &server_rc));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ WH_TEST_PRINT(" Test: Logout before login\n");
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LogoutOp(client, user_id, &server_rc));
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ /* Test 3: Logout with invalid user id */
+ WH_TEST_PRINT(
+ " Test: Logout attempt with invalid user ID (should fail)\n");
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_LogoutOp(client, WH_USER_ID_INVALID, &server_rc));
+ /* Should return error for invalid user ID */
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ return WH_TEST_SUCCESS;
+}
+
+/* Login Tests */
+int whTest_AuthLogin(whClientContext* client)
+{
+ int32_t server_rc;
+ whUserId user_id;
+
+ if (client == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Test 1: Login with invalid credentials */
+ WH_TEST_PRINT(" Test: Login with invalid credentials\n");
+ server_rc = 0;
+ user_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME,
+ "wrong", 5, &server_rc, &user_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_AUTH_LOGIN_FAILED ||
+ server_rc != WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(user_id == WH_USER_ID_INVALID);
+
+ /* Test 2: Login with valid credentials - use blocking version */
+ WH_TEST_PRINT(" Test: Login with valid credentials\n");
+ server_rc = 0;
+ user_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(
+ client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, 4,
+ &server_rc, &user_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID);
+
+ /* Logout for next test */
+ _whTest_Auth_LogoutOp(client, user_id, &server_rc);
+
+ /* Test 3: Login with invalid username */
+ WH_TEST_PRINT(" Test: Login with invalid username\n");
+ server_rc = 0;
+ user_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(
+ client, WH_AUTH_METHOD_PIN, "nonexistent", TEST_ADMIN_PIN, 4,
+ &server_rc, &user_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_AUTH_LOGIN_FAILED ||
+ server_rc != WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(user_id == WH_USER_ID_INVALID);
+
+ /* Test 4: Login if already logged in */
+ WH_TEST_PRINT(" Test: Login if already logged in\n");
+ /* First login */
+ server_rc = 0;
+ user_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(
+ client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, 4,
+ &server_rc, &user_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID);
+
+ /* Try to login again without logout */
+ server_rc = 0;
+ whUserId user_id2 = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(
+ client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, 4,
+ &server_rc, &user_id2));
+ /* Second login should fail */
+ WH_TEST_ASSERT_RETURN(server_rc == WH_AUTH_LOGIN_FAILED ||
+ server_rc != WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(user_id2 == WH_USER_ID_INVALID);
+
+ /* Cleanup */
+ _whTest_Auth_LogoutOp(client, user_id, &server_rc);
+
+ return WH_TEST_SUCCESS;
+}
+
+/* Add User Tests */
+int whTest_AuthAddUser(whClientContext* client)
+{
+ int32_t server_rc;
+ whUserId user_id;
+ whAuthPermissions perms;
+ char long_username[34]; /* 33 chars + null terminator */
+ int rc;
+
+ if (client == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Login as admin first */
+ whAuthPermissions admin_perms;
+ memset(&admin_perms, 0, sizeof(admin_perms));
+ server_rc = 0;
+ whUserId admin_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(
+ client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, 4,
+ &server_rc, &admin_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ /* Test 1: Add user with invalid username (too long) */
+ WH_TEST_PRINT(" Test: Add user with invalid username (too long)\n");
+ memset(long_username, 'a', 33);
+ long_username[33] = '\0';
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ user_id = WH_USER_ID_INVALID;
+
+ /* Expect client-side rejection due to username length */
+ rc = wh_Client_AuthUserAddRequest(client, long_username, perms,
+ WH_AUTH_METHOD_PIN, "test", 4);
+ WH_TEST_ASSERT_RETURN(rc != WH_ERROR_OK || server_rc != WH_ERROR_OK ||
+ user_id == WH_USER_ID_INVALID);
+
+ /* Test 2: Add user with invalid permissions (keyIdCount > max) */
+ WH_TEST_PRINT(" Test: Add user with invalid permissions\n");
+ memset(&perms, 0, sizeof(perms));
+ perms.keyIdCount = WH_AUTH_MAX_KEY_IDS + 1; /* Invalid: exceeds max */
+ server_rc = 0;
+ user_id = WH_USER_ID_INVALID;
+ _whTest_Auth_UserAddOp(client, "testuser1", perms, WH_AUTH_METHOD_PIN,
+ "test", 4, &server_rc, &user_id);
+ /* Should clamp or reject invalid keyIdCount */
+ if (server_rc == WH_ERROR_OK) {
+ /* If it succeeds, keyIdCount should be clamped */
+ WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID);
+ }
+
+ /* Test 3: Add user if already exists */
+ WH_TEST_PRINT(" Test: Add user if already exists\n");
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ user_id = WH_USER_ID_INVALID;
+ _whTest_Auth_UserAddOp(client, "testuser2", perms, WH_AUTH_METHOD_PIN,
+ "test", 4, &server_rc, &user_id);
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID);
+
+ /* Try to add same user again */
+ whUserId user_id2 = WH_USER_ID_INVALID;
+ server_rc = 0;
+ _whTest_Auth_UserAddOp(client, "testuser2", perms, WH_AUTH_METHOD_PIN,
+ "test", 4, &server_rc, &user_id2);
+ /* Should fail - user already exists (allow success if backend does not
+ * check) */
+ if (server_rc == WH_ERROR_OK && user_id2 != WH_USER_ID_INVALID) {
+ WH_TEST_PRINT(" Note: duplicate username allowed by backend\n");
+ _whTest_Auth_UserDeleteOp(client, user_id2, &server_rc);
+ }
+
+ /* Cleanup */
+ server_rc = 0;
+ _whTest_Auth_DeleteUserByName(client, "testuser1");
+ _whTest_Auth_DeleteUserByName(client, "testuser2");
+ _whTest_Auth_LogoutOp(client, admin_id, &server_rc);
+
+ return WH_TEST_SUCCESS;
+}
+
+/* Delete User Tests */
+int whTest_AuthDeleteUser(whClientContext* client)
+{
+ int32_t server_rc;
+ whAuthPermissions admin_perms;
+ whUserId admin_id = WH_USER_ID_INVALID;
+ whAuthPermissions perms;
+ whAuthPermissions out_perms;
+ whUserId delete_user_id = WH_USER_ID_INVALID;
+
+ if (client == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Login as admin to perform delete operations */
+ memset(&admin_perms, 0, sizeof(admin_perms));
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN,
+ "admin", "1234", 4, &server_rc,
+ &admin_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ /* Test 1: Delete user with invalid user id */
+ WH_TEST_PRINT(" Test: Delete user with invalid user ID\n");
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_UserDeleteOp(client, WH_USER_ID_INVALID, &server_rc));
+ /* Should fail for invalid user ID */
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ /* Test 2: Delete user that does not exist */
+ WH_TEST_PRINT(" Test: Delete user that does not exist\n");
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserDeleteOp(client, 999, &server_rc));
+ /* Should fail - user doesn't exist */
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_NOTFOUND ||
+ server_rc != WH_ERROR_OK);
+
+ /* Test 2b: Delete existing user (success path) */
+ WH_TEST_PRINT(" Test: Delete existing user\n");
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "deleteuser", perms,
+ WH_AUTH_METHOD_PIN, "pass", 4,
+ &server_rc, &delete_user_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(delete_user_id != WH_USER_ID_INVALID);
+
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserGetOp(
+ client, "deleteuser", &server_rc, &delete_user_id, &out_perms));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_UserDeleteOp(client, delete_user_id, &server_rc));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ server_rc = 0;
+ delete_user_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserGetOp(
+ client, "deleteuser", &server_rc, &delete_user_id, &out_perms));
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK ||
+ delete_user_id == WH_USER_ID_INVALID);
+
+ /* Test 3: Delete user when not logged in */
+ WH_TEST_PRINT(" Test: Delete user when not logged in\n");
+ /* Ensure we're logged out */
+ server_rc = 0;
+ _whTest_Auth_LogoutOp(client, admin_id, &server_rc);
+
+ /* Try to delete without being logged in */
+ server_rc = 0;
+ _whTest_Auth_UserDeleteOp(client, 1, &server_rc);
+ /* Should fail authorization - not logged in */
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ return WH_TEST_SUCCESS;
+}
+
+/* Set User Permissions Tests */
+int whTest_AuthSetPermissions(whClientContext* client)
+{
+ int32_t server_rc;
+ whUserId user_id;
+ whAuthPermissions perms, new_perms;
+ whAuthPermissions fetched_perms;
+ whUserId fetched_user_id = WH_USER_ID_INVALID;
+ int32_t get_rc = 0;
+
+ if (client == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Login as admin first */
+ whAuthPermissions admin_perms;
+ memset(&admin_perms, 0, sizeof(admin_perms));
+ server_rc = 0;
+ whUserId admin_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN,
+ "admin", "1234", 4, &server_rc,
+ &admin_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ /* Create a test user first */
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ user_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "testuser3", perms,
+ WH_AUTH_METHOD_PIN, "test", 4,
+ &server_rc, &user_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID);
+
+ /* Test 1: Set user permissions with invalid user id */
+ WH_TEST_PRINT(" Test: Set user permissions with invalid user ID\n");
+ memset(&new_perms, 0xFF, sizeof(new_perms));
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserSetPermsOp(
+ client, WH_USER_ID_INVALID, new_perms, &server_rc));
+ /* Should fail for invalid user ID */
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ /* Test 2: Set user permissions with invalid permissions */
+ WH_TEST_PRINT(" Test: Set user permissions with invalid permissions\n");
+ memset(&new_perms, 0, sizeof(new_perms));
+ new_perms.keyIdCount = WH_AUTH_MAX_KEY_IDS + 1; /* Invalid */
+ server_rc = 0;
+ _whTest_Auth_UserSetPermsOp(client, user_id, new_perms, &server_rc);
+ /* Should clamp or reject invalid keyIdCount */
+ if (server_rc == WH_ERROR_OK) {
+ /* If it succeeds, keyIdCount should be clamped */
+ }
+
+ /* Test 2b: Set user permissions success path */
+ WH_TEST_PRINT(" Test: Set user permissions success\n");
+ memset(&new_perms, 0, sizeof(new_perms));
+ new_perms.groupPermissions = WH_MESSAGE_GROUP_AUTH;
+ /* Convert action enum value to bitmask: action 0x04 -> word 0, bit 4 -> 0x10 */
+ {
+ int groupIndex = (WH_MESSAGE_GROUP_AUTH >> 8) & 0xFF;
+ uint32_t wordAndBit = WH_AUTH_ACTION_TO_WORD_AND_BIT(WH_MESSAGE_AUTH_ACTION_USER_ADD);
+ uint32_t wordIndex = WH_AUTH_ACTION_WORD(wordAndBit);
+ uint32_t bitmask = WH_AUTH_ACTION_BIT(wordAndBit);
+ new_perms.actionPermissions[groupIndex][wordIndex] = bitmask;
+ }
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_UserSetPermsOp(client, user_id, new_perms, &server_rc));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ memset(&fetched_perms, 0, sizeof(fetched_perms));
+ fetched_user_id = WH_USER_ID_INVALID;
+ get_rc = 0;
+ /* Use blocking version to verify permissions were set */
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserGetOp(
+ client, "testuser3", &get_rc, &fetched_user_id, &fetched_perms));
+ WH_TEST_ASSERT_RETURN(get_rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(fetched_user_id == user_id);
+ WH_TEST_ASSERT_RETURN(fetched_perms.groupPermissions ==
+ new_perms.groupPermissions);
+ {
+ /* Compare all action permission words for this group */
+ int groupIndex = (WH_MESSAGE_GROUP_AUTH >> 8) & 0xFF;
+ int j;
+ int permissions_match = 1;
+ for (j = 0; j < WH_AUTH_ACTION_WORDS; j++) {
+ if (fetched_perms.actionPermissions[groupIndex][j] !=
+ new_perms.actionPermissions[groupIndex][j]) {
+ permissions_match = 0;
+ break;
+ }
+ }
+ WH_TEST_ASSERT_RETURN(permissions_match);
+ }
+
+ /* Test 3: Set user permissions for non-existent user */
+ WH_TEST_PRINT(" Test: Set user permissions for non-existent user\n");
+ memset(&new_perms, 0, sizeof(new_perms));
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_UserSetPermsOp(client, 999, new_perms, &server_rc));
+ /* Should fail - user doesn't exist */
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_NOTFOUND ||
+ server_rc != WH_ERROR_OK);
+
+ /* Test 4: Set user permissions when not logged in */
+ WH_TEST_PRINT(" Test: Set user permissions when not logged in\n");
+ /* Logout */
+ _whTest_Auth_LogoutOp(client, admin_id, &server_rc);
+
+ /* Try to set permissions without being logged in */
+ memset(&new_perms, 0, sizeof(new_perms));
+ server_rc = 0;
+ _whTest_Auth_UserSetPermsOp(client, user_id, new_perms, &server_rc);
+ /* Should fail authorization - not logged in */
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ /* Cleanup */
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN,
+ "admin", "1234", 4, &server_rc,
+ &admin_id));
+ _whTest_Auth_DeleteUserByName(client, "testuser3");
+ _whTest_Auth_LogoutOp(client, admin_id, &server_rc);
+
+ return WH_TEST_SUCCESS;
+}
+
+/* Set User Credentials Tests */
+int whTest_AuthSetCredentials(whClientContext* client)
+{
+ int32_t server_rc;
+ whUserId user_id;
+ whAuthPermissions perms;
+
+ if (client == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Login as admin first */
+ whAuthPermissions admin_perms;
+ memset(&admin_perms, 0, sizeof(admin_perms));
+ server_rc = 0;
+ whUserId admin_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN,
+ "admin", "1234", 4, &server_rc,
+ &admin_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ /* Create a test user first */
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ user_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "testuser4", perms,
+ WH_AUTH_METHOD_PIN, "test", 4,
+ &server_rc, &user_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID);
+
+ /* Test 1: Set user credentials with invalid user id */
+ WH_TEST_PRINT(" Test: Set user credentials with invalid user ID\n");
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserSetCredsOp(
+ client, WH_USER_ID_INVALID, WH_AUTH_METHOD_PIN, "test", 4, "newpass", 7,
+ &server_rc));
+ /* Should fail for invalid user ID */
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ /* Test 2: Set user credentials with invalid method */
+ WH_TEST_PRINT(" Test: Set user credentials with invalid method\n");
+ server_rc = 0;
+ _whTest_Auth_UserSetCredsOp(client, user_id, WH_AUTH_METHOD_NONE, "test", 4,
+ "newpass", 7, &server_rc);
+ /* Should fail for invalid method */
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ /* Test 3: Set user credentials for non-existent user */
+ WH_TEST_PRINT(" Test: Set user credentials for non-existent user\n");
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserSetCredsOp(
+ client, 999, WH_AUTH_METHOD_PIN, NULL, 0, "newpass", 7, &server_rc));
+ /* Should fail - user doesn't exist */
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_NOTFOUND ||
+ server_rc != WH_ERROR_OK);
+
+ WH_TEST_PRINT(" Test: Admin setting credentials for non-admin user\n");
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_UserSetCredsOp(client, user_id, WH_AUTH_METHOD_PIN, "test",
+ 4, "newpass", 7, &server_rc));
+
+ /* Should succeed - admin can set credentials for other users */
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ /* Verify new credentials work */
+ _whTest_Auth_LogoutOp(client, admin_id, &server_rc);
+ memset(&admin_perms, 0, sizeof(admin_perms));
+ server_rc = 0;
+ whUserId test_user_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, "testuser4", "newpass",
+ 7, &server_rc, &test_user_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(test_user_id == user_id);
+
+ /* Cleanup */
+ _whTest_Auth_LogoutOp(client, test_user_id, &server_rc);
+ _whTest_Auth_DeleteUserByName(client, "testuser4");
+
+ return WH_TEST_SUCCESS;
+}
+
+/* Authorization Checks Tests */
+int whTest_AuthRequestAuthorization(whClientContext* client)
+{
+ int32_t server_rc;
+ whUserId user_id;
+ whUserId temp_id3 = WH_USER_ID_INVALID;
+ whAuthPermissions perms;
+
+ if (client == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Test 1: Operation when not logged in and not allowed */
+ WH_TEST_PRINT(" Test: Operation when not logged in and not allowed\n");
+ /* Ensure logged out */
+ server_rc = 0;
+ _whTest_Auth_LogoutOp(client, WH_USER_ID_INVALID, &server_rc);
+
+ /* Try an operation that requires auth (e.g., add user) */
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ whUserId temp_id = WH_USER_ID_INVALID;
+ _whTest_Auth_UserAddOp(client, "testuser5", perms, WH_AUTH_METHOD_PIN,
+ "test", 4, &server_rc, &temp_id);
+ /* Should fail authorization - not logged in */
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ /* Test 2: Operation when logged in and allowed */
+ WH_TEST_PRINT(" Test: Operation when logged in and allowed\n");
+ /* Login as admin */
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ whUserId admin_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN,
+ "admin", "1234", 4, &server_rc,
+ &admin_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ /* Retry operation after login (admin should be allowed) - use blocking
+ * version */
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ user_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "testuser6", perms,
+ WH_AUTH_METHOD_PIN, "test", 4,
+ &server_rc, &user_id));
+ /* Should succeed - admin has permissions */
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID);
+
+ /* Test 3: Operation when logged in and not allowed */
+ WH_TEST_PRINT(" Test: Operation when logged in and not allowed\n");
+ /* Create a user with limited permissions */
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ whUserId limited_user_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_UserAddOp(client, "limiteduser", perms, WH_AUTH_METHOD_PIN,
+ "pass", 4, &server_rc, &limited_user_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ /* Logout admin and login as limited user */
+ _whTest_Auth_LogoutOp(client, admin_id, &server_rc);
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ whUserId logged_in_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, "limiteduser", "pass",
+ 4, &server_rc, &logged_in_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ /* Try an operation that requires permissions */
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ whUserId temp_id2 = WH_USER_ID_INVALID;
+ _whTest_Auth_UserAddOp(client, "testuser7", perms, WH_AUTH_METHOD_PIN,
+ "test", 4, &server_rc, &temp_id2);
+ /* Should fail authorization - user doesn't have permissions */
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ /* Test 4: Logged in as different user and allowed */
+ WH_TEST_PRINT(" Test: Logged in as different user and allowed\n");
+ _whTest_Auth_LogoutOp(client, logged_in_id, &server_rc);
+
+ server_rc = 0;
+ whUserId allowed_user_id = WH_USER_ID_INVALID;
+ /* Login as admin to create allowed user */
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN,
+ "admin", "1234", 4, &server_rc,
+ &admin_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ memset(&perms, 0, sizeof(perms));
+ perms.groupPermissions = WH_MESSAGE_GROUP_AUTH;
+ /* Convert action enum value to bitmask: action 0x04 -> word 0, bit 4 -> 0x10 */
+ {
+ int groupIndex = (WH_MESSAGE_GROUP_AUTH >> 8) & 0xFF;
+ uint32_t wordAndBit = WH_AUTH_ACTION_TO_WORD_AND_BIT(WH_MESSAGE_AUTH_ACTION_USER_ADD);
+ uint32_t wordIndex = WH_AUTH_ACTION_WORD(wordAndBit);
+ uint32_t bitmask = WH_AUTH_ACTION_BIT(wordAndBit);
+ perms.actionPermissions[groupIndex][wordIndex] = bitmask;
+ }
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_UserAddOp(client, "alloweduser", perms, WH_AUTH_METHOD_PIN,
+ "pass", 4, &server_rc, &allowed_user_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+ _whTest_Auth_LogoutOp(client, admin_id, &server_rc);
+
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ logged_in_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, "alloweduser", "pass",
+ 4, &server_rc, &logged_in_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ server_rc = 0;
+ temp_id3 = WH_USER_ID_INVALID;
+ _whTest_Auth_UserAddOp(client, "testuser8", perms, WH_AUTH_METHOD_PIN,
+ "test", 4, &server_rc, &temp_id3);
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ /* Test 5: Logged in as different user and not allowed */
+ WH_TEST_PRINT(" Test: Logged in as different user and not allowed\n");
+ _whTest_Auth_LogoutOp(client, logged_in_id, &server_rc);
+
+ memset(&perms, 0, sizeof(perms));
+ server_rc = 0;
+ logged_in_id = WH_USER_ID_INVALID;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, "limiteduser", "pass",
+ 4, &server_rc, &logged_in_id));
+ WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
+
+ server_rc = 0;
+ temp_id3 = WH_USER_ID_INVALID;
+ _whTest_Auth_UserAddOp(client, "testuser9", perms, WH_AUTH_METHOD_PIN,
+ "test", 4, &server_rc, &temp_id3);
+ WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK);
+
+ /* Cleanup */
+ _whTest_Auth_LogoutOp(client, logged_in_id, &server_rc);
+ server_rc = 0;
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME,
+ TEST_ADMIN_PIN, 4, &server_rc, &admin_id));
+ _whTest_Auth_DeleteUserByName(client, "limiteduser");
+ _whTest_Auth_DeleteUserByName(client, "alloweduser");
+ _whTest_Auth_DeleteUserByName(client, "testuser5");
+ _whTest_Auth_DeleteUserByName(client, "testuser6");
+ _whTest_Auth_DeleteUserByName(client, "testuser7");
+ _whTest_Auth_DeleteUserByName(client, "testuser8");
+ _whTest_Auth_DeleteUserByName(client, "testuser9");
+ _whTest_Auth_LogoutOp(client, admin_id, &server_rc);
+
+ return WH_TEST_SUCCESS;
+}
+
+/* Main Test Function */
+int whTest_AuthTest(whClientContext* client_ctx)
+{
+ WH_TEST_PRINT("Testing authentication functionality...\n");
+
+ WH_TEST_PRINT("Running auth bad-args tests...\n");
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_BadArgs());
+ WH_TEST_PRINT("Running auth message bad-args tests...\n");
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_MessageBadArgs());
+
+ /* Run authentication test groups */
+ WH_TEST_PRINT("Running logout tests...\n");
+ /* Verify client context is valid */
+ WH_TEST_ASSERT_RETURN(client_ctx != NULL);
+ WH_TEST_ASSERT_RETURN(client_ctx->comm != NULL);
+ WH_TEST_RETURN_ON_FAIL(whTest_AuthLogout(client_ctx));
+
+ WH_TEST_PRINT("Running login tests...\n");
+ WH_TEST_RETURN_ON_FAIL(whTest_AuthLogin(client_ctx));
+
+ WH_TEST_PRINT("Running add user tests...\n");
+ WH_TEST_RETURN_ON_FAIL(whTest_AuthAddUser(client_ctx));
+
+ WH_TEST_PRINT("Running delete user tests...\n");
+ WH_TEST_RETURN_ON_FAIL(whTest_AuthDeleteUser(client_ctx));
+
+ WH_TEST_PRINT("Running set permissions tests...\n");
+ WH_TEST_RETURN_ON_FAIL(whTest_AuthSetPermissions(client_ctx));
+
+ WH_TEST_PRINT("Running set credentials tests...\n");
+ WH_TEST_RETURN_ON_FAIL(whTest_AuthSetCredentials(client_ctx));
+
+ WH_TEST_PRINT("Running authorization checks tests...\n");
+ WH_TEST_RETURN_ON_FAIL(whTest_AuthRequestAuthorization(client_ctx));
+
+ WH_TEST_PRINT("All authentication tests completed successfully\n");
+
+ return WH_TEST_SUCCESS;
+}
+
+
+/* Run all the tests against a remote server running */
+int whTest_AuthTCP(whClientConfig* clientCfg)
+{
+ whClientContext client[1] = {0};
+
+ if (clientCfg == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ WH_TEST_RETURN_ON_FAIL(wh_Client_Init(client, clientCfg));
+
+ WH_TEST_RETURN_ON_FAIL(wh_Client_CommInit(client, NULL, NULL));
+ WH_TEST_RETURN_ON_FAIL(whTest_AuthTest(client));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_Cleanup(client));
+
+ return WH_TEST_SUCCESS;
+}
+
+
+int whTest_AuthMEM(void)
+{
+#if !defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) && defined(WOLFHSM_CFG_ENABLE_SERVER)
+ whClientContext* client_ctx = NULL;
+
+ /* Memory transport mode */
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_SetupMemory(&client_ctx));
+ WH_TEST_RETURN_ON_FAIL(whTest_AuthTest(client_ctx));
+ WH_TEST_RETURN_ON_FAIL(_whTest_Auth_CleanupMemory());
+
+ return WH_TEST_SUCCESS;
+#else
+ return WH_TEST_FAIL;
+#endif
+}
diff --git a/test/wh_test_auth.h b/test/wh_test_auth.h
new file mode 100644
index 00000000..3e0a51bb
--- /dev/null
+++ b/test/wh_test_auth.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2025 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+/*
+ * test/wh_test_auth.h
+ */
+
+#ifndef WOLFHSM_WH_TEST_AUTH_H_
+#define WOLFHSM_WH_TEST_AUTH_H_
+
+#include "wolfhsm/wh_server.h"
+#include "wolfhsm/wh_client.h"
+
+#include "wolfhsm/wh_auth.h"
+#include "wh_test_common.h"
+
+
+/* Self-contained test that creates client and server with auth */
+int whTest_AuthMEM(void);
+int whTest_AuthTCP(whClientConfig* clientCfg);
+
+/* Individual test functions that require a connected client */
+int whTest_AuthLogin(whClientContext* client);
+int whTest_AuthLogout(whClientContext* client);
+int whTest_AuthAddUser(whClientContext* client);
+int whTest_AuthDeleteUser(whClientContext* client);
+int whTest_AuthSetPermissions(whClientContext* client);
+int whTest_AuthSetCredentials(whClientContext* client);
+int whTest_AuthRequestAuthorization(whClientContext* client);
+int whTest_AuthKeyAuthorization(whClientContext* client);
+
+#endif /* WOLFHSM_WH_TEST_AUTH_H_ */
\ No newline at end of file
diff --git a/test/wh_test_clientserver.c b/test/wh_test_clientserver.c
index 82003597..55be7496 100644
--- a/test/wh_test_clientserver.c
+++ b/test/wh_test_clientserver.c
@@ -56,6 +56,12 @@
#include "port/posix/posix_transport_shm.h"
#endif
+#ifndef TEST_ADMIN_USERNAME
+#define TEST_ADMIN_USERNAME "admin"
+#endif
+#ifndef TEST_ADMIN_PIN
+#define TEST_ADMIN_PIN "1234"
+#endif
#define BUFFER_SIZE 4096
#define REQ_SIZE 32
@@ -1184,6 +1190,10 @@ int whTest_ClientServerClientConfig(whClientConfig* clientCfg)
WH_TEST_RETURN_ON_FAIL(wh_Client_CommInit(client, &client_id, &server_id));
WH_TEST_ASSERT_RETURN(client_id == client->comm->client_id);
+ /* Attempt to log in as an admin user for the rest of the tests */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AuthLogin(client, WH_AUTH_METHOD_PIN,
+ TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, strlen(TEST_ADMIN_PIN), &server_rc,
+ NULL));
for (counter = 0; counter < REPEAT_COUNT; counter++) {
@@ -1759,6 +1769,8 @@ static int wh_ClientServer_PosixMemMapThreadTest(whTestNvmBackendType nvmType)
.crypto = crypto,
#endif
}};
+ s_conf->auth = NULL; /* For non authenticated tests set auth context to NULL
+ * which avoids authentication checks. */
WH_TEST_RETURN_ON_FAIL(wh_Nvm_Init(nvm, n_conf));
diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c
index b925cfa6..344f2a83 100644
--- a/test/wh_test_crypto.c
+++ b/test/wh_test_crypto.c
@@ -72,6 +72,13 @@
#define FLASH_SECTOR_SIZE (128 * 1024) /* 128KB */
#define FLASH_PAGE_SIZE (8) /* 8B */
+#ifndef TEST_ADMIN_USERNAME
+#define TEST_ADMIN_USERNAME "admin"
+#endif
+#ifndef TEST_ADMIN_PIN
+#define TEST_ADMIN_PIN "1234"
+#endif
+
#define ALT_CLIENT_ID (2)
enum {
@@ -5061,6 +5068,13 @@ int whTest_CryptoClientConfig(whClientConfig* config)
WH_ERROR_PRINT("Failed to comm init:%d\n", ret);
}
+ if (ret == 0) {
+ /* Attempt log in as an admin user for the rest of the tests */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AuthLogin(client, WH_AUTH_METHOD_PIN,
+ TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, strlen(TEST_ADMIN_PIN),
+ NULL, NULL));
+ }
+
#ifdef WOLFHSM_CFG_DEBUG_VERBOSE
if (ret == 0) {
(void)whTest_ShowNvmAvailable(client);
diff --git a/test/wh_test_keywrap.c b/test/wh_test_keywrap.c
index 6d675217..636b1d42 100644
--- a/test/wh_test_keywrap.c
+++ b/test/wh_test_keywrap.c
@@ -54,6 +54,13 @@
#endif /* HAVE_AESGCM */
+#ifndef TEST_ADMIN_USERNAME
+#define TEST_ADMIN_USERNAME "admin"
+#endif
+#ifndef TEST_ADMIN_PIN
+#define TEST_ADMIN_PIN "1234"
+#endif
+
static int _InitServerKek(whClientContext* client)
{
/* IMPORTANT NOTE: Server KEK is typically intrinsic or set during
@@ -328,6 +335,12 @@ int whTest_KeyWrapClientConfig(whClientConfig* clientCfg)
goto cleanup_and_exit;
}
+ /* Log in as an admin user for the rest of the tests */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AuthLogin(client, WH_AUTH_METHOD_PIN,
+ TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, strlen(TEST_ADMIN_PIN), &ret,
+ NULL));
+ WH_TEST_ASSERT_RETURN(ret == 0);
+
ret = whTest_Client_KeyWrap(client);
if (ret != 0) {
WH_ERROR_PRINT("Failed to whTest_Client_KeyWrap %d\n", ret);
diff --git a/test/wh_test_she.c b/test/wh_test_she.c
index 3e710e93..83808ad9 100644
--- a/test/wh_test_she.c
+++ b/test/wh_test_she.c
@@ -81,6 +81,13 @@ enum {
#define FLASH_SECTOR_SIZE (128 * 1024) /* 128KB */
#define FLASH_PAGE_SIZE (8) /* 8B */
+#ifndef TEST_ADMIN_USERNAME
+#define TEST_ADMIN_USERNAME "admin"
+#endif
+#ifndef TEST_ADMIN_PIN
+#define TEST_ADMIN_PIN "1234"
+#endif
+
#ifdef WOLFHSM_CFG_ENABLE_CLIENT
/* Helper function to destroy a SHE key so the unit tests don't
* leak NVM objects across invocations. Necessary, as SHE doesn't expose a
@@ -164,6 +171,11 @@ int whTest_SheClientConfig(whClientConfig* config)
WH_TEST_RETURN_ON_FAIL(wh_Client_Init(client, config));
WH_TEST_RETURN_ON_FAIL(wh_Client_CommInit(client, &outClientId, &outServerId));
+ /* Attempt log in as an admin user for the rest of the tests */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AuthLogin(client, WH_AUTH_METHOD_PIN,
+ TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, strlen(TEST_ADMIN_PIN),
+ NULL, NULL));
+
#ifdef WOLFHSM_CFG_DEBUG_VERBOSE
{
int32_t server_rc = 0;
diff --git a/wolfhsm/wh_auth.h b/wolfhsm/wh_auth.h
new file mode 100644
index 00000000..75ea80e2
--- /dev/null
+++ b/wolfhsm/wh_auth.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2025 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+/*
+ * wolfhsm/wh_auth.h
+ *
+ * Abstract library to provide authentication and authorization management.
+ * The Auth Manager is transport-agnostic and protocol-agnostic, providing
+ * core security services for all wolfHSM operations.
+ *
+ * The Auth Manager:
+ * - Verifies PINs/credentials
+ * - Manages sessions
+ * - Makes authorization decisions
+ * - Tracks session state and logs authentication attempts
+ */
+
+#ifndef WOLFHSM_WH_AUTH_H_
+#define WOLFHSM_WH_AUTH_H_
+
+/* Pick up compile-time configuration */
+#include "wolfhsm/wh_settings.h"
+
+#include
+#include
+
+#include "wolfhsm/wh_common.h"
+
+/** Auth Manager Types */
+
+/* User identifier type */
+typedef uint16_t whUserId;
+#define WH_USER_ID_INVALID ((whUserId)0)
+
+/* Authentication method enumeration */
+typedef enum {
+ WH_AUTH_METHOD_NONE = 0,
+ WH_AUTH_METHOD_PIN,
+ WH_AUTH_METHOD_CERTIFICATE,
+} whAuthMethod;
+
+#define WH_NUMBER_OF_GROUPS 14
+#define WH_AUTH_MAX_KEY_IDS \
+ 2 /* Maximum number of key IDs a user can have access to */
+#define WH_AUTH_ACTIONS_PER_GROUP 256 /* Support up to 256 actions (0-255) */
+#define WH_AUTH_ACTION_WORDS \
+ ((WH_AUTH_ACTIONS_PER_GROUP + 31) / 32) /* 8 uint32_t words for 256 bits */
+
+/* Convert action enum value (0-255) to bitmask and word index.
+ * Returns the word index in the upper 16 bits and bitmask in lower 32 bits.
+ * Use WH_AUTH_ACTION_WORD() and WH_AUTH_ACTION_BIT() to extract. */
+#define WH_AUTH_ACTION_TO_WORD_AND_BIT(_action) \
+ ((((_action) / 32) << 16) | (1UL << ((_action) % 32)))
+#define WH_AUTH_ACTION_WORD(_word_and_bit) (((_word_and_bit) >> 16) & 0xFF)
+#define WH_AUTH_ACTION_BIT(_word_and_bit) ((_word_and_bit) & 0xFFFFFFFFUL)
+
+/* Legacy macro for backward compatibility - only works for actions < 32 */
+#define WH_AUTH_ACTION_TO_BITMASK(_action) \
+ (((_action) < 32) ? (1UL << (_action)) : 0)
+
+typedef struct {
+ uint16_t groupPermissions; /* bit mask of if allowed for use in group */
+ uint32_t actionPermissions[WH_NUMBER_OF_GROUPS]
+ [WH_AUTH_ACTION_WORDS]; /* multi-word bit array
+ for action permissions
+ (256 bits per group) */
+ uint16_t keyIdCount; /* Number of key IDs in the keyIds array (0 to
+ WH_AUTH_MAX_KEY_IDS) */
+ uint32_t keyIds[WH_AUTH_MAX_KEY_IDS]; /* Array of key IDs that user has
+ access to */
+} whAuthPermissions;
+
+/* User information */
+typedef struct {
+ whUserId user_id;
+ char username[32]; /* Max username length */
+ whAuthPermissions permissions;
+ bool is_active;
+} whAuthUser;
+
+/** Auth Manager Callback Structure */
+
+typedef struct {
+ /* Initialize the auth backend */
+ int (*Init)(void* context, const void* config);
+
+ /* Cleanup the auth backend */
+ int (*Cleanup)(void* context);
+
+ /* Authenticate a user using the specified method */
+ int (*Login)(void* context, uint8_t client_id, whAuthMethod method,
+ const char* username, const void* auth_data,
+ uint16_t auth_data_len, whUserId* out_user_id,
+ whAuthPermissions* out_permissions, int* loggedIn);
+
+ /* Logout a user */
+ int (*Logout)(void* context, whUserId current_user_id, whUserId user_id);
+
+
+ /* Check if an action is authorized for a session */
+ int (*CheckRequestAuthorization)(void* context, uint16_t user_id,
+ uint16_t group, uint16_t action);
+
+ /* Check if a key is authorized for use */
+ int (*CheckKeyAuthorization)(void* context, uint16_t user_id,
+ uint32_t key_id, uint16_t action);
+
+ /* Add a new user */
+ int (*UserAdd)(void* context, const char* username, whUserId* out_user_id,
+ whAuthPermissions permissions, whAuthMethod method,
+ const void* credentials, uint16_t credentials_len);
+
+ /* Delete a user */
+ int (*UserDelete)(void* context, whUserId current_user_id,
+ whUserId user_id);
+
+ /* Set user permissions */
+ int (*UserSetPermissions)(void* context, whUserId current_user_id,
+ whUserId user_id, whAuthPermissions permissions);
+
+ /* Get user information by username */
+ int (*UserGet)(void* context, const char* username, whUserId* out_user_id,
+ whAuthPermissions* out_permissions);
+
+ /* Set user credentials (PIN, etc.) */
+ int (*UserSetCredentials)(void* context, whUserId user_id,
+ whAuthMethod method,
+ const void* current_credentials,
+ uint16_t current_credentials_len,
+ const void* new_credentials,
+ uint16_t new_credentials_len);
+} whAuthCb;
+
+/** Auth Manager Context and Config */
+
+/* Simple helper context structure associated with an Auth Manager instance */
+typedef struct whAuthContext_t {
+ whAuthCb* cb;
+ whAuthUser user;
+ void* context;
+} whAuthContext;
+
+#define WOLFHSM_MAX_CERTIFICATE_LEN 2048
+
+/* Simple helper configuration structure associated with an Auth Manager
+ * instance */
+typedef struct whAuthConfig_t {
+ whAuthCb* cb;
+ void* context;
+ void* config;
+} whAuthConfig;
+
+/** Public Auth Manager API Functions */
+
+/**
+ * @brief Initialize the auth manager.
+ *
+ * @param[in] context Pointer to the auth context.
+ * @param[in] config Pointer to the auth configuration.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Auth_Init(whAuthContext* context, const whAuthConfig* config);
+
+/**
+ * @brief Cleanup the auth manager.
+ *
+ * @param[in] context Pointer to the auth context.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Auth_Cleanup(whAuthContext* context);
+
+/**
+ * @brief Authenticate and login a user.
+ *
+ * @param[in] context Pointer to the auth context.
+ * @param[in] client_id The client ID making the request.
+ * @param[in] method The authentication method to use.
+ * @param[in] username The username to authenticate.
+ * @param[in] auth_data Pointer to the authentication data.
+ * @param[in] auth_data_len Length of the authentication data.
+ * @param[out] loggedIn Pointer to store the login status (1 for success).
+ * @return int Returns 0 if the authentication attempt was processed successfully
+ * (regardless of authentication result), or a negative error code if a
+ * fatal error occurred. The authentication result is returned in the
+ * loggedIn parameter.
+ */
+int wh_Auth_Login(whAuthContext* context, uint8_t client_id,
+ whAuthMethod method, const char* username,
+ const void* auth_data, uint16_t auth_data_len, int* loggedIn);
+
+/**
+ * @brief Logout a user.
+ *
+ * @param[in] context Pointer to the auth context.
+ * @param[in] user_id The user ID to logout.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Auth_Logout(whAuthContext* context, whUserId user_id);
+
+/**
+ * @brief Check authorization for an action.
+ *
+ * @param[in] context Pointer to the auth context.
+ * @param[in] group The group to check authorization for.
+ * @param[in] action The action to check authorization for.
+ * @return int Returns 0 if authorized, or a negative error code on failure.
+ */
+int wh_Auth_CheckRequestAuthorization(whAuthContext* context, uint16_t group,
+ uint16_t action);
+
+/**
+ * @brief Check if a key is authorized for use. @TODO, this is a place holder
+ * for calls to check key use but wolfHSM currently does not call it before key
+ * use.
+ *
+ * @param[in] context Pointer to the auth context.
+ * @param[in] key_id The key ID to check authorization for.
+ * @param[in] action The action to check authorization for.
+ * @return int Returns 0 if authorized, or a negative error code on failure.
+ */
+int wh_Auth_CheckKeyAuthorization(whAuthContext* context, uint32_t key_id,
+ uint16_t action);
+
+/**
+ * @brief Add a new user.
+ *
+ * @param[in] context Pointer to the auth context.
+ * @param[in] username The username for the new user.
+ * @param[out] out_user_id Pointer to store the new user ID.
+ * @param[in] permissions The permissions for the new user.
+ * @param[in] method The authentication method for the new user.
+ * @param[in] credentials Pointer to the credentials data.
+ * @param[in] credentials_len Length of the credentials data.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Auth_UserAdd(whAuthContext* context, const char* username,
+ whUserId* out_user_id, whAuthPermissions permissions,
+ whAuthMethod method, const void* credentials,
+ uint16_t credentials_len);
+
+/**
+ * @brief Delete a user.
+ *
+ * @param[in] context Pointer to the auth context.
+ * @param[in] user_id The user ID to delete.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Auth_UserDelete(whAuthContext* context, whUserId user_id);
+
+/**
+ * @brief Set user permissions.
+ *
+ * @param[in] context Pointer to the auth context.
+ * @param[in] user_id The user ID to set permissions for.
+ * @param[in] permissions The new permissions to set.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Auth_UserSetPermissions(whAuthContext* context, whUserId user_id,
+ whAuthPermissions permissions);
+
+/**
+ * @brief Get user information.
+ *
+ * @param[in] context Pointer to the auth context.
+ * @param[in] username The username to look up.
+ * @param[out] out_user_id Pointer to store the user ID.
+ * @param[out] out_permissions Pointer to store the user permissions.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Auth_UserGet(whAuthContext* context, const char* username,
+ whUserId* out_user_id, whAuthPermissions* out_permissions);
+
+/**
+ * @brief Set user credentials.
+ *
+ * @param[in] context Pointer to the auth context.
+ * @param[in] user_id The user ID to set credentials for.
+ * @param[in] method The authentication method.
+ * @param[in] current_credentials Pointer to the current credentials data.
+ * @param[in] current_credentials_len Length of the current credentials data.
+ * @param[in] new_credentials Pointer to the new credentials data.
+ * @param[in] new_credentials_len Length of the new credentials data.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Auth_UserSetCredentials(whAuthContext* context, whUserId user_id,
+ whAuthMethod method,
+ const void* current_credentials,
+ uint16_t current_credentials_len,
+ const void* new_credentials,
+ uint16_t new_credentials_len);
+#endif /* !WOLFHSM_WH_AUTH_H_ */
diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h
index f1b1b70a..79adb99d 100644
--- a/wolfhsm/wh_client.h
+++ b/wolfhsm/wh_client.h
@@ -53,6 +53,7 @@
#include "wolfhsm/wh_dma.h"
#endif /* WOLFHSM_CFG_DMA */
#include "wolfhsm/wh_keyid.h"
+#include "wolfhsm/wh_auth.h"
/* Forward declaration of the client structure so its elements can reference
@@ -1906,6 +1907,363 @@ int wh_Client_CustomCbCheckRegisteredResponse(whClientContext* c,
int wh_Client_CustomCbCheckRegistered(whClientContext* c, uint16_t id,
int* responseError);
+/* Auth Manager functions */
+
+/**
+ * @brief Sends an authentication request to the server.
+ *
+ * This function prepares and sends an authentication request message to the
+ * server. The request includes the authentication method and authentication
+ * data (e.g., PIN). This function does not block; it returns immediately after
+ * sending the request.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] method The authentication method to use (e.g.,
+ * WH_AUTH_METHOD_PIN).
+ * @param[in] username The user name to login
+ * @param[in] auth_data Pointer to the authentication data.
+ * @param[in] auth_data_len Length of the authentication data.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthLoginRequest(whClientContext* c, whAuthMethod method,
+ const char* username, const void* auth_data,
+ uint16_t auth_data_len);
+
+/**
+ * @brief Receives an authentication response from the server.
+ *
+ * This function attempts to process an authentication response message from the
+ * server. It validates the response and extracts the return code and user ID.
+ * This function does not block; it returns
+ * WH_ERROR_NOTREADY if a response has not been received.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @param[out] out_user_id Pointer to store the authenticated user ID.
+ * @return int Returns 0 on success, WH_ERROR_NOTREADY if no response is
+ * available, or a negative error code on failure.
+ */
+int wh_Client_AuthLoginResponse(whClientContext* c, int32_t* out_rc,
+ whUserId* out_user_id);
+
+/**
+ * @brief Authenticates a user with the server (blocking convenience wrapper).
+ *
+ * This function handles the complete process of sending an authentication
+ * request to the server and receiving the response. It sends the request and
+ * repeatedly attempts to receive a valid response. This function blocks until
+ * the entire operation is complete or an error occurs.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] method The authentication method to use (e.g.,
+ * WH_AUTH_METHOD_PIN).
+ * @param[in] username The user name to login
+ * @param[in] auth_data Pointer to the authentication data.
+ * @param[in] auth_data_len Length of the authentication data.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @param[out] out_user_id Pointer to store the authenticated user ID.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthLogin(whClientContext* c, whAuthMethod method,
+ const char* username, const void* auth_data,
+ uint16_t auth_data_len, int32_t* out_rc,
+ whUserId* out_user_id);
+
+/**
+ * @brief Sends a logout request to the server.
+ *
+ * This function prepares and sends a logout request message to the server.
+ * This function does not block; it returns immediately after sending the request.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] user_id The user ID to logout.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthLogoutRequest(whClientContext* c, whUserId user_id);
+
+/**
+ * @brief Receives a logout response from the server.
+ *
+ * This function attempts to process a logout response message from the server.
+ * This function does not block; it returns WH_ERROR_NOTREADY if a response has
+ * not been received.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @return int Returns 0 on success, WH_ERROR_NOTREADY if no response is
+ * available, or a negative error code on failure.
+ */
+int wh_Client_AuthLogoutResponse(whClientContext* c, int32_t* out_rc);
+
+/**
+ * @brief Logs out a user from the server (blocking convenience wrapper).
+ *
+ * This function handles the complete process of sending a logout request to the
+ * server and receiving the response. It sends the request and repeatedly attempts
+ * to receive a valid response. This function blocks until the entire operation is
+ * complete or an error occurs.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] user_id The user ID to logout.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthLogout(whClientContext* c, whUserId user_id, int32_t* out_rc);
+
+/**
+ * @brief Receives a user add response from the server.
+ *
+ * This function attempts to process a user add response message from the server.
+ * This function does not block; it returns WH_ERROR_NOTREADY if a response has
+ * not been received.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @param[out] out_user_id Pointer to store the new user ID.
+ * @return int Returns 0 on success, WH_ERROR_NOTREADY if no response is
+ * available, or a negative error code on failure.
+ */
+int wh_Client_AuthUserAddResponse(whClientContext* c, int32_t* out_rc,
+ whUserId* out_user_id);
+
+/**
+ * @brief Sends a user add request to the server.
+ *
+ * This function prepares and sends a user add request message to the server.
+ * This function does not block; it returns immediately after sending the request.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] username The username for the new user.
+ * @param[in] permissions The permissions for the new user.
+ * @param[in] method The authentication method for the new user.
+ * @param[in] credentials Pointer to the credentials data.
+ * @param[in] credentials_len Length of the credentials data.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthUserAddRequest(whClientContext* c, const char* username,
+ whAuthPermissions permissions,
+ whAuthMethod method, const void* credentials,
+ uint16_t credentials_len);
+
+/**
+ * @brief Adds a new user to the server (blocking convenience wrapper).
+ *
+ * This function handles the complete process of sending a user add request to the
+ * server and receiving the response. It sends the request and repeatedly attempts
+ * to receive a valid response. This function blocks until the entire operation is
+ * complete or an error occurs.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] username The username for the new user.
+ * @param[in] permissions The permissions for the new user.
+ * @param[in] method The authentication method for the new user.
+ * @param[in] credentials Pointer to the credentials data.
+ * @param[in] credentials_len Length of the credentials data.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @param[out] out_user_id Pointer to store the new user ID.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthUserAdd(whClientContext* c, const char* username,
+ whAuthPermissions permissions, whAuthMethod method,
+ const void* credentials, uint16_t credentials_len,
+ int32_t* out_rc, whUserId* out_user_id);
+
+/**
+ * @brief Sends a user get request to the server.
+ *
+ * This function prepares and sends a user get request message to the server.
+ * This function does not block; it returns immediately after sending the request.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] username The username to look up.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthUserGetRequest(whClientContext* c, const char* username);
+
+/**
+ * @brief Receives a user get response from the server.
+ *
+ * This function attempts to process a user get response message from the server.
+ * This function does not block; it returns WH_ERROR_NOTREADY if a response has
+ * not been received.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @param[out] out_user_id Pointer to store the user ID.
+ * @param[out] out_permissions Pointer to store the user permissions.
+ * @return int Returns 0 on success, WH_ERROR_NOTREADY if no response is
+ * available, or a negative error code on failure.
+ */
+int wh_Client_AuthUserGetResponse(whClientContext* c, int32_t* out_rc,
+ whUserId* out_user_id,
+ whAuthPermissions* out_permissions);
+
+/**
+ * @brief Gets user information from the server (blocking convenience wrapper).
+ *
+ * This function handles the complete process of sending a user get request to the
+ * server and receiving the response. It sends the request and repeatedly attempts
+ * to receive a valid response. This function blocks until the entire operation is
+ * complete or an error occurs.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] username The username to look up.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @param[out] out_user_id Pointer to store the user ID.
+ * @param[out] out_permissions Pointer to store the user permissions.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthUserGet(whClientContext* c, const char* username,
+ int32_t* out_rc, whUserId* out_user_id,
+ whAuthPermissions* out_permissions);
+
+/**
+ * @brief Sends a user delete request to the server.
+ *
+ * This function prepares and sends a user delete request message to the server.
+ * This function does not block; it returns immediately after sending the request.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] user_id The user ID to delete.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthUserDeleteRequest(whClientContext* c, whUserId user_id);
+
+/**
+ * @brief Receives a user delete response from the server.
+ *
+ * This function attempts to process a user delete response message from the server.
+ * This function does not block; it returns WH_ERROR_NOTREADY if a response has
+ * not been received.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @return int Returns 0 on success, WH_ERROR_NOTREADY if no response is
+ * available, or a negative error code on failure.
+ */
+int wh_Client_AuthUserDeleteResponse(whClientContext* c, int32_t* out_rc);
+
+/**
+ * @brief Deletes a user from the server (blocking convenience wrapper).
+ *
+ * This function handles the complete process of sending a user delete request to the
+ * server and receiving the response. It sends the request and repeatedly attempts
+ * to receive a valid response. This function blocks until the entire operation is
+ * complete or an error occurs.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] user_id The user ID to delete.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthUserDelete(whClientContext* c, whUserId user_id,
+ int32_t* out_rc);
+
+/**
+ * @brief Sends a user set permissions request to the server.
+ *
+ * This function prepares and sends a user set permissions request message to the server.
+ * This function does not block; it returns immediately after sending the request.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] user_id The user ID to set permissions for.
+ * @param[in] permissions The new permissions to set.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthUserSetPermissionsRequest(whClientContext* c,
+ whUserId user_id,
+ whAuthPermissions permissions);
+
+/**
+ * @brief Receives a user set permissions response from the server.
+ *
+ * This function attempts to process a user set permissions response message from the server.
+ * This function does not block; it returns WH_ERROR_NOTREADY if a response has
+ * not been received.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @return int Returns 0 on success, WH_ERROR_NOTREADY if no response is
+ * available, or a negative error code on failure.
+ */
+int wh_Client_AuthUserSetPermissionsResponse(whClientContext* c,
+ int32_t* out_rc);
+
+/**
+ * @brief Sets user permissions on the server (blocking convenience wrapper).
+ *
+ * This function handles the complete process of sending a user set permissions request to the
+ * server and receiving the response. It sends the request and repeatedly attempts
+ * to receive a valid response. This function blocks until the entire operation is
+ * complete or an error occurs.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] user_id The user ID to set permissions for.
+ * @param[in] permissions The new permissions to set.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthUserSetPermissions(whClientContext* c, whUserId user_id,
+ whAuthPermissions permissions,
+ int32_t* out_rc);
+
+/**
+ * @brief Sends a user set credentials request to the server.
+ *
+ * This function prepares and sends a user set credentials request message to the server.
+ * This function does not block; it returns immediately after sending the request.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] user_id The user ID to set credentials for.
+ * @param[in] method The authentication method.
+ * @param[in] current_credentials Pointer to the current credentials data.
+ * @param[in] current_credentials_len Length of the current credentials data.
+ * @param[in] new_credentials Pointer to the new credentials data.
+ * @param[in] new_credentials_len Length of the new credentials data.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthUserSetCredentialsRequest(
+ whClientContext* c, whUserId user_id, whAuthMethod method,
+ const void* current_credentials, uint16_t current_credentials_len,
+ const void* new_credentials, uint16_t new_credentials_len);
+
+/**
+ * @brief Receives a user set credentials response from the server.
+ *
+ * This function attempts to process a user set credentials response message from the server.
+ * This function does not block; it returns WH_ERROR_NOTREADY if a response has
+ * not been received.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @return int Returns 0 on success, WH_ERROR_NOTREADY if no response is
+ * available, or a negative error code on failure.
+ */
+int wh_Client_AuthUserSetCredentialsResponse(whClientContext* c,
+ int32_t* out_rc);
+
+/**
+ * @brief Sets user credentials on the server (blocking convenience wrapper).
+ *
+ * This function handles the complete process of sending a user set credentials request to the
+ * server and receiving the response. It sends the request and repeatedly attempts
+ * to receive a valid response. This function blocks until the entire operation is
+ * complete or an error occurs.
+ *
+ * @param[in] c Pointer to the client context.
+ * @param[in] user_id The user ID to set credentials for.
+ * @param[in] method The authentication method.
+ * @param[in] current_credentials Pointer to the current credentials data.
+ * @param[in] current_credentials_len Length of the current credentials data.
+ * @param[in] new_credentials Pointer to the new credentials data.
+ * @param[in] new_credentials_len Length of the new credentials data.
+ * @param[out] out_rc Pointer to store the return code from the server.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Client_AuthUserSetCredentials(
+ whClientContext* c, whUserId user_id, whAuthMethod method,
+ const void* current_credentials, uint16_t current_credentials_len,
+ const void* new_credentials, uint16_t new_credentials_len, int32_t* out_rc);
/* Certificate functions */
/**
diff --git a/wolfhsm/wh_error.h b/wolfhsm/wh_error.h
index d918fe61..29ff8373 100644
--- a/wolfhsm/wh_error.h
+++ b/wolfhsm/wh_error.h
@@ -67,6 +67,10 @@ enum WH_ERROR_ENUM {
WH_SHE_ERC_BUSY = -2209,
WH_SHE_ERC_MEMORY_FAILURE = -2210,
WH_SHE_ERC_GENERAL_ERROR = -2211,
+
+ /* Auth error codes */
+ WH_AUTH_LOGIN_FAILED = -2300, /* user login attempt failed */
+ WH_AUTH_PERMISSION_ERROR = -2301, /* user attempted an action not allowed */
};
#define WH_SHE_ERC_NO_ERROR WH_ERROR_OK
diff --git a/wolfhsm/wh_message.h b/wolfhsm/wh_message.h
index 60167cbf..92702db3 100644
--- a/wolfhsm/wh_message.h
+++ b/wolfhsm/wh_message.h
@@ -47,6 +47,7 @@ enum WH_MESSAGE_ENUM {
WH_MESSAGE_GROUP_CUSTOM = 0x0A00, /* User-specified features */
WH_MESSAGE_GROUP_CRYPTO_DMA = 0x0B00, /* DMA crypto operations */
WH_MESSAGE_GROUP_CERT = 0x0C00, /* Certificate operations */
+ WH_MESSAGE_GROUP_AUTH = 0x0D00, /* Authentication and authorization */
WH_MESSAGE_ACTION_MASK = 0x00FF, /* 255 subtypes per group*/
WH_MESSAGE_ACTION_NONE = 0x0000, /* No action. Invalid. */
@@ -98,6 +99,16 @@ enum {
WH_COUNTER_DESTROY,
};
+/* auth actions */
+enum {
+ WH_AUTH_ACTION_LOGIN,
+ WH_AUTH_ACTION_LOGOUT,
+ WH_AUTH_ACTION_USER_ADD,
+ WH_AUTH_ACTION_USER_DELETE,
+ WH_AUTH_ACTION_USER_MODIFY,
+ WH_AUTH_ACTION_PERMISSION_SET,
+};
+
/* Construct the message kind based on group and action */
#define WH_MESSAGE_KIND(_G, _S) ( ((_G) & WH_MESSAGE_GROUP_MASK) | \
((_S) & WH_MESSAGE_ACTION_MASK))
diff --git a/wolfhsm/wh_message_auth.h b/wolfhsm/wh_message_auth.h
new file mode 100644
index 00000000..199b54b4
--- /dev/null
+++ b/wolfhsm/wh_message_auth.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2025 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+/*
+ * wolfhsm/wh_message_auth.h
+ *
+ * Message definitions for Auth Manager operations
+ */
+
+#ifndef WOLFHSM_WH_MESSAGE_AUTH_H_
+#define WOLFHSM_WH_MESSAGE_AUTH_H_
+
+/* Pick up compile-time configuration */
+#include "wolfhsm/wh_settings.h"
+
+#include
+
+#include "wolfhsm/wh_common.h"
+#include "wolfhsm/wh_comm.h"
+#include "wolfhsm/wh_message.h"
+#include "wolfhsm/wh_auth.h"
+
+enum WH_MESSAGE_AUTH_ACTION_ENUM {
+ WH_MESSAGE_AUTH_ACTION_AUTHENTICATE = 0x01,
+ WH_MESSAGE_AUTH_ACTION_LOGIN = 0x02,
+ WH_MESSAGE_AUTH_ACTION_LOGOUT = 0x03,
+ WH_MESSAGE_AUTH_ACTION_USER_ADD = 0x04,
+ WH_MESSAGE_AUTH_ACTION_USER_DELETE = 0x05,
+ WH_MESSAGE_AUTH_ACTION_USER_GET = 0x06,
+ WH_MESSAGE_AUTH_ACTION_USER_SET_PERMISSIONS = 0x07,
+ WH_MESSAGE_AUTH_ACTION_USER_SET_CREDENTIALS = 0x08,
+};
+
+enum WH_MESSAGE_AUTH_MAX_ENUM {
+ WH_MESSAGE_AUTH_MAX_USERNAME_LEN = 32,
+ /* Reserve space for UserAddRequest fixed fields:
+ * username (32) + permissions (WH_FLAT_PERMISSIONS_LEN) + method (2) +
+ * credentials_len (2) + overhead (2) = 32 + 460 + 2 + 2 + 2 = 498 bytes */
+ WH_MESSAGE_AUTH_MAX_CREDENTIALS_LEN = WOLFHSM_CFG_COMM_DATA_LEN - 498,
+ WH_MESSAGE_AUTH_MAX_SESSIONS = 16,
+};
+
+/* Simple reusable response message */
+typedef struct {
+ int32_t rc;
+} whMessageAuth_SimpleResponse;
+
+/**
+ * @brief Translate a simple response message between different magic numbers.
+ *
+ * @param[in] magic The magic number for translation.
+ * @param[in] src Pointer to the source simple response message.
+ * @param[out] dest Pointer to the destination simple response message.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_TranslateSimpleResponse(
+ uint16_t magic, const whMessageAuth_SimpleResponse* src,
+ whMessageAuth_SimpleResponse* dest);
+
+/** Login Request */
+typedef struct {
+ uint16_t method;
+ char username[WH_MESSAGE_AUTH_MAX_USERNAME_LEN];
+ uint16_t auth_data_len;
+ /* auth_data follows */
+} whMessageAuth_LoginRequest;
+
+/**
+ * @brief Translate a login request message between different magic numbers.
+ *
+ * @param[in] magic The magic number for translation.
+ * @param[in] src_packet Pointer to the source packet data.
+ * @param[in] src_size Size of the source packet.
+ * @param[out] dest_header Pointer to the destination login request header.
+ * @param[out] dest_auth_data Pointer to the destination buffer for auth data.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_TranslateLoginRequest(
+ uint16_t magic, const void* src_packet, uint16_t src_size,
+ whMessageAuth_LoginRequest* dest_header, uint8_t* dest_auth_data);
+
+/** Login Response */
+typedef struct {
+ int32_t rc;
+ uint16_t user_id;
+} whMessageAuth_LoginResponse;
+
+/**
+ * @brief Translate a login response message between different magic numbers.
+ *
+ * @param[in] magic The magic number for translation.
+ * @param[in] src Pointer to the source login response message.
+ * @param[out] dest Pointer to the destination login response message.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_TranslateLoginResponse(
+ uint16_t magic, const whMessageAuth_LoginResponse* src,
+ whMessageAuth_LoginResponse* dest);
+
+/** Logout Request */
+typedef struct {
+ uint16_t user_id;
+ uint8_t WH_PAD[2];
+} whMessageAuth_LogoutRequest;
+
+/**
+ * @brief Translate a logout request message between different magic numbers.
+ *
+ * @param[in] magic The magic number for translation.
+ * @param[in] src Pointer to the source logout request message.
+ * @param[out] dest Pointer to the destination logout request message.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_TranslateLogoutRequest(
+ uint16_t magic, const whMessageAuth_LogoutRequest* src,
+ whMessageAuth_LogoutRequest* dest);
+
+/** Logout Response (SimpleResponse) */
+
+/* whAuthPermissions struct
+ * uint16_t (groupPermissions) + uint32_t[WH_NUMBER_OF_GROUPS][WH_AUTH_ACTION_WORDS]
+ * (actionPermissions) + uint16_t (keyIdCount) + uint32_t[WH_AUTH_MAX_KEY_IDS]
+ * (keyIds) */
+#define WH_FLAT_PERMISSIONS_LEN \
+ (2 + (4 * WH_NUMBER_OF_GROUPS * WH_AUTH_ACTION_WORDS) + 2 + \
+ (4 * WH_AUTH_MAX_KEY_IDS))
+
+/**
+ * @brief Flatten permissions structure into a byte buffer.
+ *
+ * @param[in] permissions Pointer to the permissions structure to flatten.
+ * @param[out] buffer Pointer to the destination buffer.
+ * @param[in] buffer_len Length of the destination buffer.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_FlattenPermissions(whAuthPermissions* permissions,
+ uint8_t* buffer, uint16_t buffer_len);
+
+/**
+ * @brief Unflatten a byte buffer into a permissions structure.
+ *
+ * @param[in] buffer Pointer to the source buffer.
+ * @param[in] buffer_len Length of the source buffer.
+ * @param[out] permissions Pointer to the destination permissions structure.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_UnflattenPermissions(uint8_t* buffer, uint16_t buffer_len,
+ whAuthPermissions* permissions);
+
+
+/** User Add Request */
+typedef struct {
+ char username[WH_MESSAGE_AUTH_MAX_USERNAME_LEN];
+ uint8_t permissions[WH_FLAT_PERMISSIONS_LEN];
+ uint16_t method;
+ uint16_t credentials_len;
+ /* credentials follow */
+} whMessageAuth_UserAddRequest;
+
+/**
+ * @brief Translate a user add request message between different magic numbers.
+ *
+ * @param[in] magic The magic number for translation.
+ * @param[in] src_packet Pointer to the source packet data.
+ * @param[in] src_size Size of the source packet.
+ * @param[out] dest_header Pointer to the destination user add request header.
+ * @param[out] dest_credentials Pointer to the destination buffer for credentials.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_TranslateUserAddRequest(
+ uint16_t magic, const void* src_packet, uint16_t src_size,
+ whMessageAuth_UserAddRequest* dest_header, uint8_t* dest_credentials);
+
+/** User Add Response */
+typedef struct {
+ int32_t rc;
+ uint16_t user_id;
+ uint8_t WH_PAD[2];
+} whMessageAuth_UserAddResponse;
+
+/**
+ * @brief Translate a user add response message between different magic numbers.
+ *
+ * @param[in] magic The magic number for translation.
+ * @param[in] src Pointer to the source user add response message.
+ * @param[out] dest Pointer to the destination user add response message.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_TranslateUserAddResponse(
+ uint16_t magic, const whMessageAuth_UserAddResponse* src,
+ whMessageAuth_UserAddResponse* dest);
+
+/** User Delete Request */
+typedef struct {
+ uint16_t user_id;
+ uint8_t WH_PAD[2];
+} whMessageAuth_UserDeleteRequest;
+
+/**
+ * @brief Translate a user delete request message between different magic numbers.
+ *
+ * @param[in] magic The magic number for translation.
+ * @param[in] src Pointer to the source user delete request message.
+ * @param[out] dest Pointer to the destination user delete request message.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_TranslateUserDeleteRequest(
+ uint16_t magic, const whMessageAuth_UserDeleteRequest* src,
+ whMessageAuth_UserDeleteRequest* dest);
+
+/** User Delete Response */
+/* Use SimpleResponse */
+
+/** User Get Request */
+typedef struct {
+ char username[WH_MESSAGE_AUTH_MAX_USERNAME_LEN];
+ uint8_t WH_PAD[2];
+} whMessageAuth_UserGetRequest;
+
+/**
+ * @brief Translate a user get request message between different magic numbers.
+ *
+ * @param[in] magic The magic number for translation.
+ * @param[in] src Pointer to the source user get request message.
+ * @param[out] dest Pointer to the destination user get request message.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_TranslateUserGetRequest(
+ uint16_t magic, const whMessageAuth_UserGetRequest* src,
+ whMessageAuth_UserGetRequest* dest);
+
+/** User Get Response */
+typedef struct {
+ int32_t rc;
+ uint16_t user_id;
+ uint8_t permissions[WH_FLAT_PERMISSIONS_LEN];
+} whMessageAuth_UserGetResponse;
+
+/**
+ * @brief Translate a user get response message between different magic numbers.
+ *
+ * @param[in] magic The magic number for translation.
+ * @param[in] src Pointer to the source user get response message.
+ * @param[out] dest Pointer to the destination user get response message.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_TranslateUserGetResponse(
+ uint16_t magic, const whMessageAuth_UserGetResponse* src,
+ whMessageAuth_UserGetResponse* dest);
+
+/** User Set Permissions Request */
+typedef struct {
+ uint16_t user_id;
+ uint8_t permissions[WH_FLAT_PERMISSIONS_LEN];
+} whMessageAuth_UserSetPermissionsRequest;
+
+/**
+ * @brief Translate a user set permissions request message between different magic numbers.
+ *
+ * @param[in] magic The magic number for translation.
+ * @param[in] src Pointer to the source user set permissions request message.
+ * @param[out] dest Pointer to the destination user set permissions request message.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_TranslateUserSetPermissionsRequest(
+ uint16_t magic, const whMessageAuth_UserSetPermissionsRequest* src,
+ whMessageAuth_UserSetPermissionsRequest* dest);
+
+/** User Set Permissions Response */
+/* Use SimpleResponse */
+
+/** User Set Credentials Request */
+/* Header structure - credentials follow as variable-length data */
+typedef struct {
+ uint16_t user_id;
+ uint16_t method;
+ uint16_t current_credentials_len;
+ uint16_t new_credentials_len;
+ /* Variable-length data follows:
+ * current_credentials[current_credentials_len]
+ * new_credentials[new_credentials_len]
+ */
+} whMessageAuth_UserSetCredentialsRequest;
+
+/**
+ * @brief Translate a user set credentials request message between different magic numbers.
+ *
+ * @param[in] magic The magic number for translation.
+ * @param[in] src_packet Pointer to the source packet data.
+ * @param[in] src_size Size of the source packet.
+ * @param[out] dest_header Pointer to the destination user set credentials request header.
+ * @param[out] dest_current_creds Pointer to the destination buffer for current credentials.
+ * @param[out] dest_new_creds Pointer to the destination buffer for new credentials.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_MessageAuth_TranslateUserSetCredentialsRequest(
+ uint16_t magic, const void* src_packet, uint16_t src_size,
+ whMessageAuth_UserSetCredentialsRequest* dest_header,
+ uint8_t* dest_current_creds, uint8_t* dest_new_creds);
+
+/** User Set Credentials Response */
+/* Use SimpleResponse */
+#endif /* !WOLFHSM_WH_MESSAGE_AUTH_H_ */
diff --git a/wolfhsm/wh_server.h b/wolfhsm/wh_server.h
index 4e922658..7c7ba0b1 100644
--- a/wolfhsm/wh_server.h
+++ b/wolfhsm/wh_server.h
@@ -40,6 +40,7 @@ typedef struct whServerContext_t whServerContext;
#include "wolfhsm/wh_comm.h"
#include "wolfhsm/wh_keycache.h"
#include "wolfhsm/wh_nvm.h"
+#include "wolfhsm/wh_auth.h"
#include "wolfhsm/wh_message_customcb.h"
#include "wolfhsm/wh_log.h"
#ifdef WOLFHSM_CFG_DMA
@@ -152,6 +153,7 @@ typedef struct {
typedef struct whServerConfig_t {
whCommServerConfig* comm_config;
whNvmContext* nvm;
+ whAuthContext* auth;
#ifndef WOLFHSM_CFG_NO_CRYPTO
whServerCryptoContext* crypto;
@@ -174,6 +176,7 @@ typedef struct whServerConfig_t {
/* Context structure to maintain the state of an HSM server */
struct whServerContext_t {
whNvmContext* nvm;
+ whAuthContext* auth;
whCommServer comm[1];
#ifndef WOLFHSM_CFG_NO_CRYPTO
whServerCryptoContext* crypto;
diff --git a/wolfhsm/wh_server_auth.h b/wolfhsm/wh_server_auth.h
new file mode 100644
index 00000000..cc80ff74
--- /dev/null
+++ b/wolfhsm/wh_server_auth.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2025 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM 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 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM 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 wolfHSM. If not, see .
+ */
+/*
+ * wolfhsm/wh_server_auth.h
+ *
+ * Server-side Auth Manager API
+ */
+
+#ifndef WOLFHSM_WH_SERVER_AUTH_H_
+#define WOLFHSM_WH_SERVER_AUTH_H_
+
+/* Pick up compile-time configuration */
+#include "wolfhsm/wh_settings.h"
+
+#include
+
+#include "wolfhsm/wh_server.h"
+
+#ifdef WOLFHSM_CFG_ENABLE_SERVER
+
+/**
+ * @brief Handles incoming authentication and authorization requests.
+ *
+ * This function processes incoming auth request messages from the communication
+ * server and dispatches them to the appropriate auth manager functions.
+ *
+ * @param[in] server Pointer to the server context.
+ * @param[in] magic The magic number for the request.
+ * @param[in] action The action ID of the request.
+ * @param[in] seq The sequence number of the request.
+ * @param[in] req_size The size of the request packet.
+ * @param[in] req_packet Pointer to the request packet data.
+ * @param[out] out_resp_size Pointer to store the size of the response packet.
+ * @param[out] resp_packet Pointer to store the response packet data.
+ * @return int Returns 0 on success, or a negative error code on failure.
+ */
+int wh_Server_HandleAuthRequest(whServerContext* server, uint16_t magic,
+ uint16_t action, uint16_t seq,
+ uint16_t req_size, const void* req_packet,
+ uint16_t* out_resp_size, void* resp_packet);
+
+#endif /* WOLFHSM_CFG_ENABLE_SERVER */
+
+#endif /* !WOLFHSM_WH_SERVER_AUTH_H_ */