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_ */