diff --git a/.github/workflows/build-and-run-examples.yml b/.github/workflows/build-and-run-examples.yml index ca3c6bd34..da2bd9548 100644 --- a/.github/workflows/build-and-run-examples.yml +++ b/.github/workflows/build-and-run-examples.yml @@ -14,6 +14,7 @@ jobs: asan: [ 'ASAN=1', 'ASAN=0' ] debug: [ '', 'DEBUG_VERBOSE=1' ] test: [ '', '--test' ] + auth: [ '', 'AUTH=1' ] runs-on: ubuntu-latest timeout-minutes: 5 @@ -39,17 +40,17 @@ jobs: - name: Build POSIX server run: | if [ "${{ matrix.transport }}" = "dma" ]; then - cd examples/posix/wh_posix_server && ${{ matrix.asan }} ${{ matrix.debug }} DMA=1 make -j WOLFSSL_DIR=../../../wolfssl + cd examples/posix/wh_posix_server && ${{ matrix.asan }} ${{ matrix.debug }} ${{ matrix.auth }} DMA=1 make -j WOLFSSL_DIR=../../../wolfssl else - cd examples/posix/wh_posix_server && ${{ matrix.asan }} ${{ matrix.debug }} TLS=${{ env.TLS }} make -j WOLFSSL_DIR=../../../wolfssl + cd examples/posix/wh_posix_server && ${{ matrix.asan }} ${{ matrix.debug }} ${{ matrix.auth }} TLS=${{ env.TLS }} make -j WOLFSSL_DIR=../../../wolfssl fi - name: Build POSIX client run: | if [ "${{ matrix.transport }}" = "dma" ]; then - cd examples/posix/wh_posix_client && ${{ matrix.asan }} ${{ matrix.debug }} DMA=1 make -j WOLFSSL_DIR=../../../wolfssl + cd examples/posix/wh_posix_client && ${{ matrix.asan }} ${{ matrix.debug }} ${{ matrix.auth }} DMA=1 make -j WOLFSSL_DIR=../../../wolfssl else - cd examples/posix/wh_posix_client && ${{ matrix.asan }} ${{ matrix.debug }} TLS=${{ env.TLS }} make -j WOLFSSL_DIR=../../../wolfssl + cd examples/posix/wh_posix_client && ${{ matrix.asan }} ${{ matrix.debug }} ${{ matrix.auth }} TLS=${{ env.TLS }} make -j WOLFSSL_DIR=../../../wolfssl fi # Start the server in the background diff --git a/.github/workflows/build-and-test-clientonly.yml b/.github/workflows/build-and-test-clientonly.yml index af34da4af..7e636faf3 100644 --- a/.github/workflows/build-and-test-clientonly.yml +++ b/.github/workflows/build-and-test-clientonly.yml @@ -82,6 +82,56 @@ jobs: make -j CLIENT_ONLY=1 TLS=1 SHE=1 DEBUG_VERBOSE=1 WOLFSSL_DIR=../wolfssl && make run fi + # Restart server with fresh state for AUTH test run, even with the server + # not supporting AUTH and the client supporting AUTH -- the client should + # still be able to connect and run tests while not authenticated. + - name: Restart POSIX server for AUTH + run: | + kill $SERVER_PID || true + cd examples/posix/wh_posix_server + rm -f *.bin || true + ./Build/wh_posix_server.elf --type ${{ matrix.transport }} & + SERVER_PID=$! + echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV + sleep 2 + + - name: Build client-only unit tests with AUTH against non-AUTH server + run: | + cd test + make clean + if [ "${{ matrix.transport }}" = "tcp" ]; then + make -j CLIENT_ONLY=1 SHE=1 AUTH=1 WOLFSSL_DIR=../wolfssl && make run + else + make -j CLIENT_ONLY=1 TLS=1 SHE=1 AUTH=1 WOLFSSL_DIR=../wolfssl && make run + fi + + # Rebuild the server with AUTH support and restart + - name: Rebuild and restart POSIX server for AUTH + run: | + kill $SERVER_PID || true + cd examples/posix/wh_posix_server + make clean + if [ "${{ matrix.transport }}" = "tcp" ]; then + make -j SHE=1 AUTH=1 WOLFSSL_DIR=../../../wolfssl + else + make -j TLS=1 SHE=1 AUTH=1 WOLFSSL_DIR=../../../wolfssl + fi + rm -f *.bin || true + ./Build/wh_posix_server.elf --type ${{ matrix.transport }} & + SERVER_PID=$! + echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV + sleep 2 + + - name: Build client-only unit tests with AUTH against AUTH server + run: | + cd test + make clean + if [ "${{ matrix.transport }}" = "tcp" ]; then + make -j CLIENT_ONLY=1 SHE=1 AUTH=1 WOLFSSL_DIR=../wolfssl && make run + else + make -j CLIENT_ONLY=1 TLS=1 SHE=1 AUTH=1 WOLFSSL_DIR=../wolfssl && make run + fi + # Optional: Kill the server process if it doesn't exit on its own - name: Cleanup POSIX server if: always() diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 9e77d6349..544f6c9b3 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -76,3 +76,19 @@ jobs: # Build and test in multithreaded mode with everything enabled and wolfCrypt tests with dma - name: Build and test with THREADSAFE and TESTWOLFCRYPT with DMA run: cd test && make clean && make -j THREADSAFE=1 TESTWOLFCRYPT=1 TESTWOLFCRYPT_DMA=1 DMA=1 SHE=1 ASAN=1 WOLFSSL_DIR=../wolfssl && make run + + # Build and test with AUTH=1 + - name: Build and test with AUTH + run: cd test && make clean && make -j AUTH=1 WOLFSSL_DIR=../wolfssl && make run + + # Build and test with AUTH=1 and ASAN + - name: Build and test with AUTH ASAN + run: cd test && make clean && make -j AUTH=1 ASAN=1 WOLFSSL_DIR=../wolfssl && make run + + # Build and test with AUTH=1 and THREADSAFE + - name: Build and test with AUTH THREADSAFE ASAN + run: cd test && make clean && make -j AUTH=1 THREADSAFE=1 ASAN=1 WOLFSSL_DIR=../wolfssl && make run + + # Build and test with AUTH=1 and NOCRYPTO=1 (auth on, crypto off) + - name: Build and test with AUTH NOCRYPTO + run: cd test && make clean && make -j AUTH=1 NOCRYPTO=1 WOLFSSL_DIR=../wolfssl && make run diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 0b9037c4d..7cd612d8b 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -37,11 +37,12 @@ jobs: run: cd test && make coverage WOLFSSL_DIR=../wolfssl # Display coverage summary in the action log + # Using gcov-ignore-parse-errors to avoid (GCC bug #68080) - name: Display coverage summary run: | echo "=== Coverage Summary ===" cd test - gcovr Build --root .. --filter '\.\./src/.*' --filter '\.\./wolfhsm/.*' --print-summary + gcovr --gcov-ignore-parse-errors="negative_hits.warn" Build --root .. --filter '\.\./src/.*' --filter '\.\./wolfhsm/.*' --print-summary # Upload coverage report as artifact - name: Upload coverage report diff --git a/Makefile b/Makefile index 9e8ce39ab..6d4763e3f 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ export DEBUG export DEBUG_VERBOSE +export AUTH all: test benchmark tools examples diff --git a/docs/src/chapter09.md b/docs/src/chapter09.md new file mode 100644 index 000000000..d5092a25a --- /dev/null +++ b/docs/src/chapter09.md @@ -0,0 +1,219 @@ +# Authentication Manager + +The wolfHSM Authentication Manager is a transport-agnostic component that provides authentication (PIN and certificate verification), session management, and authorization for wolfHSM operations. It is configured via a callback structure (`whAuthCb`) and can use the default in-memory implementation in `wh_auth_base.c`, or a custom backend that implements the same interface. + +## Table of Contents + +- [Enabling and Configuring the Authentication Manager](#enabling-and-configuring-the-authentication-manager) +- [WH_AUTH_* Macro Helpers](#wh_auth_-macro-helpers) +- [Default User Database (wh_auth_base.c)](#default-user-database-wh_auth_basec) +- [Admin vs Non-Admin Users and Restrictions](#admin-vs-non-admin-users-and-restrictions) +- [Auth Message Group and Actions](#auth-message-group-and-actions) +- [Authorization Callbacks (Override)](#authorization-callbacks-override) +- [Error Codes](#error-codes) +- [Thread Safety and Locking](#thread-safety-and-locking) + +## Enabling and Configuring the Authentication Manager + +### Build-time + +The Authentication Manager feature is off by default. To enable it, define `WOLFHSM_CFG_ENABLE_AUTHENTICATION` when building wolfHSM (e.g., in `wh_config.h` or via compiler flags). Without this macro, auth-related code is excluded from the build and auth requests return `WH_AUTH_NOT_ENABLED`. + +### Building examples with auth + +The POSIX server, POSIX client, and test Makefiles support an authentication-capable build via `AUTH=1`. Pass `AUTH=1` to `make` when building these targets: + +```bash +# From the examples/posix/wh_posix_server or wh_posix_client directory +make AUTH=1 + +# From the test directory +make AUTH=1 + +# From the top-level directory (exports AUTH to subdirectories) +make AUTH=1 examples +``` + +The auth demo client (`wh_demo_client_auth.c`) and related examples require this build to function. + +### Runtime configuration + +Even when auth is compiled in, the server must have an auth context configured. The auth context is set via `whServerConfig.auth` and stored in `server->auth`. If `server->auth == NULL`, no authentication is attempted: + +- Auth group requests (LOGIN, USER_ADD, USER_DELETE, etc.) return `WH_AUTH_NOT_ENABLED` +- Authorization checks for other message groups (NVM, key, crypto, etc.) are skipped entirely + +The server will process requests without requiring login. To enable authentication, the application must initialize an auth context and pass it in the server configuration. + +## WH_AUTH_* Macro Helpers + +The following macros in `wolfhsm/wh_auth.h` simplify setting and checking permissions in a `whAuthPermissions` structure: + +| Macro | Purpose | +|-------|---------| +| `WH_AUTH_IS_ADMIN(permissions)` | Returns non-zero if admin flag is set | +| `WH_AUTH_SET_IS_ADMIN(permissions, value)` | Sets admin flag (0 = non-admin, non-zero = admin) | +| `WH_AUTH_ACTION_TO_WORD_AND_BITMASK(action, wordIdx, bitMask)` | Internal: maps action (0-255) to word index and bitmask | +| `WH_AUTH_SET_ALLOWED_GROUP(permissions, group)` | Enables a message group and allows all actions in that group | +| `WH_AUTH_SET_ALLOWED_ACTION(permissions, group, action)` | Enables group and only the specified action bit | +| `WH_AUTH_CLEAR_ALLOWED_GROUP(permissions, group)` | Disables group and clears all action bits | +| `WH_AUTH_CLEAR_ALLOWED_ACTION(permissions, group, action)` | Clears permission for a specific action | + +Related constants: + +- `WH_AUTH_MAX_KEY_IDS` (2): Maximum number of key IDs a user can have access to +- `WH_AUTH_ACTIONS_PER_GROUP` (256): Support for up to 256 actions per group +- `WH_AUTH_ACTION_WORDS`: Number of `uint32_t` words used for the action bit array per group + +Example: creating a non-admin user with permission to add users but not perform other auth operations: + +```c +#include "wolfhsm/wh_auth.h" +#include "wolfhsm/wh_message.h" + +whAuthPermissions perms; + +memset(&perms, 0, sizeof(perms)); +WH_AUTH_SET_ALLOWED_ACTION(perms, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_ADD); +WH_AUTH_SET_IS_ADMIN(perms, 0); +perms.keyIdCount = 0; + +/* Use perms when adding the user via wh_Auth_UserAdd or in UserAdd request */ +``` + +## Default User Database (wh_auth_base.c) + +The in-memory implementation in `src/wh_auth_base.c` provides a simple user database suitable for development and testing. It can be used as the auth backend by registering the `wh_Auth_Base*` callbacks in `whAuthCb`. + +### Storage + +- Static array of `whAuthBase_User` structures (max 5 users by default via `WH_AUTH_BASE_MAX_USERS`) +- Each entry holds `whAuthUser`, authentication method, and credentials (max 2048 bytes via `WH_AUTH_BASE_MAX_CREDENTIALS_LEN`) + +### Init and Cleanup + +- `wh_Auth_BaseInit` zeros the user array +- `wh_Auth_BaseCleanup` force-zeros sensitive data in memory + +### Login + +- `wh_Auth_BaseLogin` supports: + - `WH_AUTH_METHOD_PIN`: Credentials are SHA256-hashed when crypto is enabled; stored as plain copy when `WOLFHSM_CFG_NO_CRYPTO` is defined + - `WH_AUTH_METHOD_CERTIFICATE`: When `WOLFHSM_CFG_CERTIFICATE_MANAGER` is defined + +### Operations + +- `wh_Auth_BaseUserAdd`, `wh_Auth_BaseUserDelete`, `wh_Auth_BaseUserSetPermissions`, `wh_Auth_BaseUserGet`, `wh_Auth_BaseUserSetCredentials` + +### Lookup + +- `wh_Auth_BaseFindUser` looks up users by username +- User IDs are 1-based (0 reserved for `WH_USER_ID_INVALID`) + +### Usernames + +The default user database does not support multiple users with the same username. Duplicate usernames are rejected in `wh_Auth_BaseUserAdd` with `WH_ERROR_BADARGS`. + +### Example: seeding a default admin user + +The POSIX server example in `examples/posix/wh_posix_server/wh_posix_server_cfg.c` seeds a default admin user at configuration time: + +```c +/* 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 = wh_Auth_BaseUserAdd(&auth_ctx, "admin", &out_user_id, permissions, + WH_AUTH_METHOD_PIN, "1234", 4); +``` + +## Admin vs Non-Admin Users and Restrictions + +### Admin users + +- Identified by `WH_AUTH_IS_ADMIN(permissions)` returning non-zero (stored in `groupPermissions[WH_NUMBER_OF_GROUPS]`) +- Can add users (including other admins), delete users, set permissions, and set credentials for any user +- Can log out other users (in addition to themselves) + +### Non-admin users + +- Identified by `WH_AUTH_SET_IS_ADMIN(permissions, 0)` or admin flag cleared +- **Key restriction**: Cannot add a user with admin permissions. If a non-admin attempts to add a user whose permissions include the admin flag, the operation fails with `WH_AUTH_PERMISSION_ERROR` (-2301). This is enforced in `wh_Auth_UserAdd` in `src/wh_auth.c` before the backend callback is invoked. +- Cannot delete users (enforced in `wh_Auth_BaseUserDelete`: only admin may delete) +- Cannot set permissions for other users (enforced in `wh_Auth_BaseUserSetPermissions`: only admin may change permissions) +- Can log out only themselves (enforced in `wh_Auth_BaseLogout`: non-admin cannot log out another user) +- Can add non-admin users if they have `WH_MESSAGE_AUTH_ACTION_USER_ADD` in the auth group + +Example: a non-admin user with user-add permission can add other non-admin users but will fail when attempting to add an admin: + +```c +/* Create non-admin with only USER_ADD permission */ +whAuthPermissions nonadmin_perms; +memset(&nonadmin_perms, 0, sizeof(nonadmin_perms)); +WH_AUTH_SET_ALLOWED_ACTION(nonadmin_perms, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_ADD); +WH_AUTH_SET_IS_ADMIN(nonadmin_perms, 0); + +/* After logging in as this user: adding a non-admin succeeds, + * but adding a user with admin permissions (e.g. memset(&perms, 0xFF, ...)) + * returns WH_AUTH_PERMISSION_ERROR. */ +``` + +## Auth Message Group and Actions + +The auth message group is `WH_MESSAGE_GROUP_AUTH` (0x0D00). Available actions in `wolfhsm/wh_message.h`: + +- `WH_MESSAGE_AUTH_ACTION_LOGIN` +- `WH_MESSAGE_AUTH_ACTION_LOGOUT` +- `WH_MESSAGE_AUTH_ACTION_USER_ADD` +- `WH_MESSAGE_AUTH_ACTION_USER_DELETE` +- `WH_MESSAGE_AUTH_ACTION_USER_GET` +- `WH_MESSAGE_AUTH_ACTION_USER_SET_PERMISSIONS` +- `WH_MESSAGE_AUTH_ACTION_USER_SET_CREDENTIALS` + +Unauthenticated users can only perform LOGIN and communications (comm) operations. Logout is always allowed for logged-in users. All other auth actions require the corresponding permission bits to be set in the user's permissions for the auth group. + +## Authorization Callbacks (Override) + +The `whAuthCb` structure defines optional callbacks that allow the auth backend to override default authorization results: + +### CheckRequestAuthorization + +Allows the auth backend to override the default authorization result for a given group and action. After the Auth Manager computes the default result (allowed or denied based on the user's permissions), if this callback is set, it is invoked with the preliminary result. The callback may change the result (e.g., grant access that would otherwise be denied, or deny access that would otherwise be allowed). + +The callback is invoked from `wh_Auth_CheckRequestAuthorization` in `src/wh_auth.c`. Parameters: context, preliminary err, user_id, group, action. Returns the final authorization result. + +### CheckKeyAuthorization + +Placeholder for checking whether a user is authorized to use a specific key ID. This callback is defined in the interface but wolfHSM currently does not invoke it before key use; it is a TODO for future integration. When implemented, it would allow the backend to override key-access decisions (e.g., based on the user's `keyIds` array in permissions). + +## Error Codes + +Auth-related error codes in `wolfhsm/wh_error.h`: + +| Code | Value | Description | +|------|-------|-------------| +| `WH_AUTH_LOGIN_FAILED` | -2300 | User login attempt failed | +| `WH_AUTH_PERMISSION_ERROR` | -2301 | User attempted an action not allowed | +| `WH_AUTH_NOT_ENABLED` | -2302 | Server does not have auth feature | + +## Thread Safety and Locking + +### Conditional compilation + +When `WOLFHSM_CFG_THREADSAFE` is defined, the Auth Manager uses a lock (`whLock`) stored in `whAuthContext` to serialize auth operations. When undefined, locking is disabled and `WH_AUTH_LOCK`/`WH_AUTH_UNLOCK` expand to no-ops (return `WH_ERROR_OK`). + +### Lock acquisition + +All public Auth Manager API functions in `src/wh_auth.c` acquire the lock via `WH_AUTH_LOCK` at entry and release via `WH_AUTH_UNLOCK` before return. Callbacks (Login, Logout, UserAdd, etc.) are invoked while holding the lock. + +### Base implementation + +The default user database in `wh_auth_base.c` uses a static global users array. When `WOLFHSM_CFG_THREADSAFE` is defined, this array is protected by the auth context lock; locking is performed by the `wh_Auth_*` wrapper functions, not by the base implementation itself. + +### Configuration + +`whAuthConfig` includes an optional `lockConfig` (of type `whLockConfig`) when `WOLFHSM_CFG_THREADSAFE` is defined; this is passed to `wh_Lock_Init` during `wh_Auth_Init`. Custom backends that maintain shared state must either rely on this lock or implement their own synchronization. diff --git a/examples/demo/client/wh_demo_client_all.c b/examples/demo/client/wh_demo_client_all.c index 15ee86d78..2b1169dde 100644 --- a/examples/demo/client/wh_demo_client_all.c +++ b/examples/demo/client/wh_demo_client_all.c @@ -1,6 +1,10 @@ #include "wh_demo_client_wctest.h" #include "wh_demo_client_wcbench.h" #include "wh_demo_client_nvm.h" +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#include "wh_demo_client_auth.h" +#include "wolfhsm/wh_error.h" +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ #include "wh_demo_client_keystore.h" #include "wh_demo_client_crypto.h" #include "wh_demo_client_secboot.h" @@ -10,6 +14,23 @@ int wh_DemoClient_All(whClientContext* clientContext) { int rc = 0; +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + 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 != WH_ERROR_OK && rc != WH_AUTH_NOT_ENABLED) { + return rc; + } +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ /* 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 000000000..5ba72266b --- /dev/null +++ b/examples/demo/client/wh_demo_client_auth.c @@ -0,0 +1,411 @@ +/* + * 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" +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#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 (serverRc == WH_AUTH_NOT_ENABLED) { + printf("[AUTH-DEMO] Authentication not enabled on server, " + "skipping PIN demo.\n"); + return WH_ERROR_OK; + } + + 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)); + /* Allow demo user to change own credentials */ + WH_AUTH_SET_ALLOWED_ACTION(out_permissions, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_SET_CREDENTIALS); + 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 (serverRc == WH_AUTH_NOT_ENABLED) { + printf("[AUTH-DEMO] Authentication not enabled on server, " + "skipping certificate demo.\n"); + return WH_ERROR_OK; + } + + 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 (serverRc == WH_AUTH_NOT_ENABLED) { + printf("[AUTH-DEMO] Authentication not enabled on server, " + "skipping user delete demo.\n"); + return WH_ERROR_OK; + } + + 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 (serverRc == WH_AUTH_NOT_ENABLED) { + printf("[AUTH-DEMO] Authentication not enabled on server, " + "skipping user set permissions demo.\n"); + return WH_ERROR_OK; + } + + 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)); + WH_AUTH_SET_ALLOWED_GROUP(permissions, WH_MESSAGE_GROUP_CRYPTO); + + 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 != WH_ERROR_OK) { + return rc; + } + + rc = wh_DemoClient_AuthPin(clientContext); + if (rc != WH_ERROR_OK) { + return rc; + } + + rc = wh_DemoClient_AuthUserDelete(clientContext); + if (rc != WH_ERROR_OK) { + return rc; + } + + rc = wh_DemoClient_AuthUserSetPermissions(clientContext); + if (rc != WH_ERROR_OK) { + return rc; + } + printf("[AUTH-DEMO] Authentication demo completed.\n"); + return rc; +} +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ 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 000000000..8970c6c95 --- /dev/null +++ b/examples/demo/client/wh_demo_client_auth.h @@ -0,0 +1,15 @@ +#ifndef DEMO_CLIENT_AUTH_H_ +#define DEMO_CLIENT_AUTH_H_ + +#include "wolfhsm/wh_client.h" + +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#include "wolfhsm/wh_auth.h" + +/* + * Simple Auth Manager demo entry point. + */ +int wh_DemoClient_Auth(whClientContext* clientContext); +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + +#endif /* !DEMO_CLIENT_AUTH_H_ */ diff --git a/examples/posix/wh_posix_client/Makefile b/examples/posix/wh_posix_client/Makefile index be0481230..53cc9fc0b 100644 --- a/examples/posix/wh_posix_client/Makefile +++ b/examples/posix/wh_posix_client/Makefile @@ -118,6 +118,11 @@ ifeq ($(TLS),1) CFLAGS += -DWOLFHSM_CFG_TLS endif +# Support an authentication-capable build +ifeq ($(AUTH),1) +DEF += -DWOLFHSM_CFG_ENABLE_AUTHENTICATION +endif + #wolfCrypt test/benchmark source files SRC_C += $(wildcard $(WOLFSSL_DIR)/wolfcrypt/test/*.c) SRC_C += $(wildcard $(WOLFSSL_DIR)/wolfcrypt/benchmark/*.c) diff --git a/examples/posix/wh_posix_server/Makefile b/examples/posix/wh_posix_server/Makefile index c5a86cd90..2d101a387 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 @@ -92,6 +98,11 @@ ifeq ($(TLS),1) CFLAGS += -DWOLFHSM_CFG_TLS endif +# Support an authentication-capable build +ifeq ($(AUTH),1) +DEF += -DWOLFHSM_CFG_ENABLE_AUTHENTICATION +endif + ifeq ($(DMA),1) CFLAGS += -DWOLFHSM_CFG_DMA endif diff --git a/examples/posix/wh_posix_server/wh_posix_server.c b/examples/posix/wh_posix_server/wh_posix_server.c index 0f0d9bca1..b006e4fb4 100644 --- a/examples/posix/wh_posix_server/wh_posix_server.c +++ b/examples/posix/wh_posix_server/wh_posix_server.c @@ -414,6 +414,16 @@ int main(int argc, char** argv) WOLFHSM_CFG_PRINTF("Failed to initialize NVM: %d\n", rc); return rc; } + +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + /* 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; + } +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + #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 754a0b821..d365f3284 100644 --- a/examples/posix/wh_posix_server/wh_posix_server_cfg.c +++ b/examples/posix/wh_posix_server/wh_posix_server_cfg.c @@ -14,6 +14,10 @@ #include "wolfhsm/wh_nvm.h" #include "wolfhsm/wh_nvm_flash.h" #include "wolfhsm/wh_flash_ramsim.h" +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#include "wolfhsm/wh_auth.h" +#include "wolfhsm/wh_auth_base.h" +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ #include "port/posix/posix_transport_shm.h" #include "port/posix/posix_transport_tcp.h" @@ -650,3 +654,86 @@ int wh_PosixServer_ExampleNvmConfig(void* conf, const char* nvmInitFilePath) return WH_ERROR_OK; } + + +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +/* Default auth callback structure */ +static whAuthCb default_auth_cb = { + .Init = wh_Auth_BaseInit, + .Cleanup = wh_Auth_BaseCleanup, + .Login = wh_Auth_BaseLogin, + .Logout = wh_Auth_BaseLogout, + .CheckRequestAuthorization = NULL, /* authorization override not used */ + .CheckKeyAuthorization = NULL, + .UserAdd = wh_Auth_BaseUserAdd, + .UserDelete = wh_Auth_BaseUserDelete, + .UserSetPermissions = wh_Auth_BaseUserSetPermissions, + .UserGet = wh_Auth_BaseUserGet, + .UserSetCredentials = wh_Auth_BaseUserSetCredentials}; +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 = wh_Auth_BaseUserAdd(&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; +} +#else +int wh_PosixServer_ExampleAuthConfig(void* conf) +{ + (void)conf; + return WH_ERROR_OK; +} +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ 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 1b95d26f1..d25852a76 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_transport_tls.c b/port/posix/posix_transport_tls.c index eba2322f3..7af2fa4dd 100644 --- a/port/posix/posix_transport_tls.c +++ b/port/posix/posix_transport_tls.c @@ -207,11 +207,12 @@ int posixTransportTls_SendRequest(void* context, uint16_t size, if (ctx->ssl == NULL) { int fd; - if (posixTransportTcp_GetConnectFd( - (void*)&ctx->tcpCtx, &fd) != WH_ERROR_OK) { + if (posixTransportTcp_GetConnectFd((void*)&ctx->tcpCtx, &fd) != + WH_ERROR_OK) { return WH_ERROR_NOTREADY; } - ctx->connect_fd_p1 = fd + 1; /* follow +1 convetions, 0 is invalid */ + ctx->connect_fd_p1 = + fd + 1; /* follow +1 convetions, 0 is invalid */ ctx->ssl = wolfSSL_new(ctx->ssl_ctx); if (!ctx->ssl) { diff --git a/src/wh_auth.c b/src/wh_auth.c new file mode 100644 index 000000000..2dd920dd3 --- /dev/null +++ b/src/wh_auth.c @@ -0,0 +1,449 @@ +/* + * 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" +#include "wolfhsm/wh_message_auth.h" + + +int wh_Auth_Init(whAuthContext* context, const whAuthConfig* config) +{ + int rc = WH_ERROR_OK; + + if ((context == NULL) || (config == NULL)) { + return WH_ERROR_BADARGS; + } + + context->cb = config->cb; + context->context = config->context; + memset(&context->user, 0, sizeof(whAuthUser)); + +#ifdef WOLFHSM_CFG_THREADSAFE + /* Initialize the lock for thread-safe auth operations */ + rc = wh_Lock_Init(&context->lock, config->lockConfig); + if (rc != WH_ERROR_OK) { + context->cb = NULL; + context->context = NULL; + return rc; + } +#endif /* WOLFHSM_CFG_THREADSAFE */ + + if (context->cb != NULL && context->cb->Init != NULL) { + rc = context->cb->Init(context->context, config->config); + if (rc != WH_ERROR_OK) { +#ifdef WOLFHSM_CFG_THREADSAFE + (void)wh_Lock_Cleanup(&context->lock); +#endif + context->cb = NULL; + context->context = NULL; + } + } + + return rc; +} + + +int wh_Auth_Cleanup(whAuthContext* context) +{ + int rc = WH_ERROR_OK; + + if ((context == NULL) || (context->cb == NULL)) { + return WH_ERROR_BADARGS; + } + + if (context->cb->Cleanup == NULL) { + return WH_ERROR_ABORTED; + } + + rc = context->cb->Cleanup(context->context); + +#ifdef WOLFHSM_CFG_THREADSAFE + /* Cleanup the lock for thread-safe auth operations */ + (void)wh_Lock_Cleanup(&context->lock); +#endif /* WOLFHSM_CFG_THREADSAFE */ + + return rc; +} + + +/* 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; + } + + rc = WH_AUTH_LOCK(context); + if (rc == WH_ERROR_OK) { + /* 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; + } + } + + (void)WH_AUTH_UNLOCK(context); + } /* LOCK() */ + 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 = WH_AUTH_LOCK(context); + if (rc == WH_ERROR_OK) { + rc = context->cb->Logout(context->context, context->user.user_id, + user_id); + if (rc == WH_ERROR_OK) { + /* Clear the user context */ + memset(&context->user, 0, sizeof(whAuthUser)); + } + + (void)WH_AUTH_UNLOCK(context); + } /* LOCK() */ + return rc; +} + + +/* 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; + whAuthUser* user; + + if ((context == NULL) || (context->cb == NULL)) { + return WH_ERROR_BADARGS; + } + + rc = WH_AUTH_LOCK(context); + if (rc == WH_ERROR_OK) { + user = &context->user; + user_id = user->user_id; + /* @TODO add logging call here and with resulting return value */ + + 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; + + /* A user logged in should by default have access to logging out */ + if (group == WH_MESSAGE_GROUP_AUTH && + action == WH_MESSAGE_AUTH_ACTION_LOGOUT) { + rc = WH_ERROR_OK; + } + else { + /* Validate groupIndex is within bounds */ + if (groupIndex >= WH_NUMBER_OF_GROUPS || groupIndex < 0) { + rc = WH_ERROR_ACCESS; + } + else if (user->permissions.groupPermissions[groupIndex]) { + /* Check if action is within supported range */ + if (action < WH_AUTH_ACTIONS_PER_GROUP) { + /* Get word index and bitmask for this action */ + uint32_t wordIndex; + uint32_t bitmask; + + WH_AUTH_ACTION_TO_WORD_AND_BITMASK(action, wordIndex, + bitmask); + + if (wordIndex < WH_AUTH_ACTION_WORDS && + (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; + } + } + } + + /* allow authorization override if callback is set */ + if (context->cb->CheckRequestAuthorization != NULL) { + rc = context->cb->CheckRequestAuthorization(context->context, rc, + user_id, group, action); + } + (void)WH_AUTH_UNLOCK(context); + } /* LOCK() */ + 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 = WH_ERROR_ACCESS; + int i; + whAuthUser* user; + + if ((context == NULL) || (context->cb == NULL)) { + return WH_ERROR_BADARGS; + } + + rc = WH_AUTH_LOCK(context); + if (rc == WH_ERROR_OK) { + /* Reset rc to default access denied after successful lock */ + rc = WH_ERROR_ACCESS; + + user_id = context->user.user_id; + user = &context->user; + if (user->user_id == WH_USER_ID_INVALID) { + (void)WH_AUTH_UNLOCK(context); + return WH_ERROR_ACCESS; + } + + /* Check if the requested key_id is in the user's keyIds array */ + for (i = 0; i < user->permissions.keyIdCount && i < WH_AUTH_MAX_KEY_IDS; + i++) { + if (user->permissions.keyIds[i] == key_id) { + rc = WH_ERROR_OK; + break; + } + } + + if (context->cb->CheckKeyAuthorization != NULL) { + rc = context->cb->CheckKeyAuthorization(context->context, rc, + user_id, key_id, action); + } + (void)WH_AUTH_UNLOCK(context); + } /* LOCK() */ + 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) +{ + int rc; + + if ((context == NULL) || (context->cb == NULL) || + (context->cb->UserAdd == NULL)) { + return WH_ERROR_BADARGS; + } + + rc = WH_AUTH_LOCK(context); + if (rc == WH_ERROR_OK) { + /* only an admin level user can add another admin level user */ + if (WH_AUTH_IS_ADMIN(permissions) && + !WH_AUTH_IS_ADMIN(context->user.permissions)) { + rc = WH_AUTH_PERMISSION_ERROR; + } + else { + rc = + context->cb->UserAdd(context->context, username, out_user_id, + permissions, method, credentials, credentials_len); + } + (void)WH_AUTH_UNLOCK(context); + } /* LOCK() */ + return rc; +} + + +int wh_Auth_UserDelete(whAuthContext* context, whUserId user_id) +{ + int rc; + + if ((context == NULL) || (context->cb == NULL) || + (context->cb->UserDelete == NULL)) { + return WH_ERROR_BADARGS; + } + + rc = WH_AUTH_LOCK(context); + if (rc == WH_ERROR_OK) { + rc = context->cb->UserDelete(context->context, context->user.user_id, + user_id); + + (void)WH_AUTH_UNLOCK(context); + } /* LOCK() */ + return rc; +} + + +int wh_Auth_UserSetPermissions(whAuthContext* context, whUserId user_id, + whAuthPermissions permissions) +{ + int rc; + + if ((context == NULL) || (context->cb == NULL) || + (context->cb->UserSetPermissions == NULL)) { + return WH_ERROR_BADARGS; + } + + rc = WH_AUTH_LOCK(context); + if (rc != WH_ERROR_OK) { + return rc; + } + + rc = context->cb->UserSetPermissions( + context->context, context->user.user_id, user_id, permissions); + + (void)WH_AUTH_UNLOCK(context); + return rc; +} + +int wh_Auth_UserGet(whAuthContext* context, const char* username, + whUserId* out_user_id, whAuthPermissions* out_permissions) +{ + int rc; + + if ((context == NULL) || (context->cb == NULL) || + (context->cb->UserGet == NULL)) { + return WH_ERROR_BADARGS; + } + + rc = WH_AUTH_LOCK(context); + if (rc == WH_ERROR_OK) { + rc = context->cb->UserGet(context->context, username, out_user_id, + out_permissions); + + (void)WH_AUTH_UNLOCK(context); + } /* LOCK() */ + return rc; +} + +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) +{ + int rc; + + if ((context == NULL) || (context->cb == NULL) || + (context->cb->UserSetCredentials == NULL)) { + return WH_ERROR_BADARGS; + } + + rc = WH_AUTH_LOCK(context); + if (rc == WH_ERROR_OK) { + rc = context->cb->UserSetCredentials( + context->context, user_id, method, current_credentials, + current_credentials_len, new_credentials, new_credentials_len); + + (void)WH_AUTH_UNLOCK(context); + } /* LOCK() */ + return rc; +} + + +/********** Lock/Unlock Functions for Thread Safety *************************/ + +#ifdef WOLFHSM_CFG_THREADSAFE +int wh_Auth_Lock(whAuthContext* auth) +{ + if (auth == NULL) { + return WH_ERROR_BADARGS; + } + return wh_Lock_Acquire(&auth->lock); +} + + +int wh_Auth_Unlock(whAuthContext* auth) +{ + if (auth == NULL) { + return WH_ERROR_BADARGS; + } + return wh_Lock_Release(&auth->lock); +} +#endif /* WOLFHSM_CFG_THREADSAFE */ diff --git a/src/wh_auth_base.c b/src/wh_auth_base.c new file mode 100644 index 000000000..29078bc63 --- /dev/null +++ b/src/wh_auth_base.c @@ -0,0 +1,551 @@ +/* + * 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_utils.h" + +#include "wolfhsm/wh_message.h" +#include "wolfhsm/wh_message_auth.h" +#include "wolfhsm/wh_auth_base.h" + +/* hash pin with use as credentials */ +#ifndef WOLFHSM_CFG_NO_CRYPTO +#include +#endif /* !WOLFHSM_CFG_NO_CRYPTO */ + +/* 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; +/* The global users array is protected by the auth context lock when + * WOLFHSM_CFG_THREADSAFE is defined. Locking is performed by the wh_Auth_* + * wrapper functions in wh_auth.c. */ +static whAuthBase_User users[WH_AUTH_BASE_MAX_USERS]; + +#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO) +#include +#include +#endif + +int wh_Auth_BaseInit(void* context, const void* config) +{ + (void)context; + (void)config; + + memset(users, 0, sizeof(users)); + return WH_ERROR_OK; +} + +int wh_Auth_BaseCleanup(void* context) +{ + (void)context; + wh_Utils_ForceZero(users, sizeof(users)); + return WH_ERROR_OK; +} + +static whAuthBase_User* wh_Auth_BaseFindUser(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; +} + +/* Hash PIN credentials using SHA256 (if crypto is available) */ +static int wh_Auth_BaseHashPin(const void* pin, uint16_t pin_len, + unsigned char* hash_out) +{ +#ifndef WOLFHSM_CFG_NO_CRYPTO + int ret = wc_Sha256Hash_ex((const unsigned char*)pin, (word32)pin_len, + hash_out, NULL, INVALID_DEVID); + if (ret != 0) { + return WH_ERROR_ABORTED; + } + return WH_ERROR_OK; +#else + /* When crypto is disabled, just copy the PIN as-is */ + if (pin_len > WH_AUTH_BASE_MAX_CREDENTIALS_LEN) { + return WH_ERROR_BUFFER_SIZE; + } + memcpy(hash_out, pin, pin_len); + return WH_ERROR_OK; +#endif /* WOLFHSM_CFG_NO_CRYPTO */ +} + +static whAuthBase_User* wh_Auth_BaseCheckPin(const char* username, + const void* auth_data, + uint16_t auth_data_len) +{ + whAuthBase_User* found_user; + unsigned char authCheck[WH_AUTH_BASE_MAX_CREDENTIALS_LEN]; + uint16_t authCheck_len; + int rc; + + /* Process auth_data: hash if crypto enabled, copy if disabled */ + rc = wh_Auth_BaseHashPin(auth_data, auth_data_len, authCheck); + if (rc != WH_ERROR_OK) { + return NULL; + } +#ifndef WOLFHSM_CFG_NO_CRYPTO + authCheck_len = WC_SHA256_DIGEST_SIZE; +#else + authCheck_len = auth_data_len; +#endif /* WOLFHSM_CFG_NO_CRYPTO */ + + found_user = wh_Auth_BaseFindUser(username); + if (found_user != NULL && found_user->method == WH_AUTH_METHOD_PIN && + found_user->credentials_len == authCheck_len && + wh_Utils_ConstantCompare(found_user->credentials, authCheck, + authCheck_len) == 0) { + return found_user; + } + return NULL; +} + +#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO) +static int wh_Auth_BaseVerifyCertificate(whAuthBase_User* found_user, + const uint8_t* certificate, + uint16_t certificate_len) +{ + int rc = WH_ERROR_ABORTED; + WOLFSSL_CERT_MANAGER* cm = NULL; + cm = wolfSSL_CertManagerNew(); + if (cm != NULL) { + if (wolfSSL_CertManagerLoadCABuffer(cm, found_user->credentials, + found_user->credentials_len, + WOLFSSL_FILETYPE_ASN1) == + WOLFSSL_SUCCESS) { + if (wolfSSL_CertManagerVerifyBuffer(cm, certificate, + certificate_len, WOLFSSL_FILETYPE_ASN1) == + WOLFSSL_SUCCESS) { + rc = WH_ERROR_OK; + } + } + wolfSSL_CertManagerFree(cm); + } + return rc; +} + +static whAuthBase_User* wh_Auth_BaseCheckCertificate(const char* username, + const void* auth_data, + uint16_t auth_data_len) +{ + whAuthBase_User* found_user; + found_user = wh_Auth_BaseFindUser(username); + if (found_user != NULL && + found_user->method == WH_AUTH_METHOD_CERTIFICATE && + found_user->credentials_len > 0) { + if (wh_Auth_BaseVerifyCertificate(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 wh_Auth_BaseLogin(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) +{ + whAuthBase_User* current_user = NULL; + + if ((out_user_id == NULL) || (out_permissions == NULL) || + (loggedIn == NULL) || (username == NULL)) { + return WH_ERROR_BADARGS; + } + + *loggedIn = 0; + + (void)client_id; + switch (method) { + case WH_AUTH_METHOD_PIN: + current_user = + wh_Auth_BaseCheckPin(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 = wh_Auth_BaseCheckCertificate(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 wh_Auth_BaseLogout(void* context, uint16_t current_user_id, + uint16_t user_id) +{ + whAuthBase_User* user; + + if (current_user_id == WH_USER_ID_INVALID || + user_id == WH_USER_ID_INVALID) { + return WH_ERROR_BADARGS; + } + + if (current_user_id > WH_AUTH_BASE_MAX_USERS || + user_id > WH_AUTH_BASE_MAX_USERS) { + return WH_ERROR_NOTFOUND; + } + + if (current_user_id != user_id && + !WH_AUTH_IS_ADMIN(users[current_user_id - 1].user.permissions)) { + return WH_ERROR_ACCESS; + } + + user = &users[user_id - 1]; + user->user.is_active = false; + (void)context; + return WH_ERROR_OK; +} + + +int wh_Auth_BaseUserAdd(void* context, const char* username, + whUserId* 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; + } + + /* do not allow duplicate users with same name */ + if (strcmp(users[i].user.username, username) == 0) { + return WH_ERROR_BADARGS; + } + } + + 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) { + new_user->method = method; + if (method == WH_AUTH_METHOD_PIN) { +#ifndef WOLFHSM_CFG_NO_CRYPTO + /* Hash PIN before storing */ + unsigned char hash[WC_SHA256_DIGEST_SIZE]; + int rc = wh_Auth_BaseHashPin(credentials, credentials_len, hash); + if (rc != WH_ERROR_OK) { + return rc; + } + memcpy(new_user->credentials, hash, WC_SHA256_DIGEST_SIZE); + new_user->credentials_len = WC_SHA256_DIGEST_SIZE; +#else + /* When crypto is disabled, store PIN as-is */ + if (credentials_len > WH_AUTH_BASE_MAX_CREDENTIALS_LEN) { + return WH_ERROR_BUFFER_SIZE; + } + memcpy(new_user->credentials, credentials, credentials_len); + new_user->credentials_len = credentials_len; +#endif /* WOLFHSM_CFG_NO_CRYPTO */ + } + else { + /* For non-PIN methods (e.g., certificate), store as-is */ + if (credentials_len > WH_AUTH_BASE_MAX_CREDENTIALS_LEN) { + return WH_ERROR_BUFFER_SIZE; + } + memcpy(new_user->credentials, credentials, credentials_len); + new_user->credentials_len = credentials_len; + } + } + + (void)auth_context; + return WH_ERROR_OK; +} + +int wh_Auth_BaseUserDelete(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; + } + + if (current_user_id == WH_USER_ID_INVALID || + current_user_id > WH_AUTH_BASE_MAX_USERS) { + return WH_ERROR_BADARGS; + } + + /* Only allow an admin user to delete users */ + if (!WH_AUTH_IS_ADMIN(users[current_user_id - 1].user.permissions)) { + return WH_ERROR_ACCESS; + } + + 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; + return WH_ERROR_OK; +} + +int wh_Auth_BaseUserSetPermissions(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; + } + + if (current_user_id == WH_USER_ID_INVALID || + current_user_id > WH_AUTH_BASE_MAX_USERS) { + return WH_ERROR_NOTFOUND; + } + + /* Only allow an admin user to change permissions */ + if (!WH_AUTH_IS_ADMIN(users[current_user_id - 1].user.permissions)) { + return WH_ERROR_ACCESS; + } + + 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; + return WH_ERROR_OK; +} + + +int wh_Auth_BaseUserGet(void* context, const char* username, + whUserId* out_user_id, + whAuthPermissions* out_permissions) +{ + whAuthBase_User* user = wh_Auth_BaseFindUser(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 wh_Auth_BaseUserSetCredentials(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->method == WH_AUTH_METHOD_PIN) { +#ifndef WOLFHSM_CFG_NO_CRYPTO + /* For PIN, hash the provided credentials before comparing */ + unsigned char hash[WC_SHA256_DIGEST_SIZE]; + rc = wh_Auth_BaseHashPin(current_credentials, + current_credentials_len, hash); + if (rc != WH_ERROR_OK) { + return rc; + } + if (user->credentials_len != WC_SHA256_DIGEST_SIZE || + wh_Utils_ConstantCompare(user->credentials, hash, + WC_SHA256_DIGEST_SIZE) != 0) { + return WH_ERROR_ACCESS; + } +#else + /* When crypto is disabled, compare PINs directly */ + if (user->credentials_len != current_credentials_len || + wh_Utils_ConstantCompare(user->credentials, current_credentials, + current_credentials_len) != 0) { + return WH_ERROR_ACCESS; + } +#endif /* WOLFHSM_CFG_NO_CRYPTO */ + } + else { + /* For non-PIN methods, compare as-is */ + if (user->credentials_len != current_credentials_len || + wh_Utils_ConstantCompare(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 */ + user->method = method; + if (new_credentials_len > 0) { + if (method == WH_AUTH_METHOD_PIN) { +#ifndef WOLFHSM_CFG_NO_CRYPTO + /* Hash PIN before storing */ + unsigned char hash[WC_SHA256_DIGEST_SIZE]; + rc = wh_Auth_BaseHashPin(new_credentials, new_credentials_len, hash); + if (rc != WH_ERROR_OK) { + return rc; + } + memcpy(user->credentials, hash, WC_SHA256_DIGEST_SIZE); + user->credentials_len = WC_SHA256_DIGEST_SIZE; +#else + /* When crypto is disabled, store PIN as-is */ + if (new_credentials_len > WH_AUTH_BASE_MAX_CREDENTIALS_LEN) { + return WH_ERROR_BUFFER_SIZE; + } + memcpy(user->credentials, new_credentials, new_credentials_len); + user->credentials_len = new_credentials_len; +#endif /* WOLFHSM_CFG_NO_CRYPTO */ + } + else { + /* For non-PIN methods (e.g., certificate), store as-is */ + if (new_credentials_len > WH_AUTH_BASE_MAX_CREDENTIALS_LEN) { + return WH_ERROR_BUFFER_SIZE; + } + 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/src/wh_client.c b/src/wh_client.c index 132841b7b..495aca756 100644 --- a/src/wh_client.c +++ b/src/wh_client.c @@ -1321,7 +1321,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; @@ -1344,7 +1344,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 000000000..389ba043d --- /dev/null +++ b/src/wh_client_auth.c @@ -0,0 +1,729 @@ +/* + * 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" +#include "wolfhsm/wh_utils.h" + +/* Does not find the user name in the list, only verifies that the user name is + * not too long and not null. */ +static int _UserNameIsValid(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); + size_t msg_size; + int rc; + + if (c == NULL) { + return WH_ERROR_BADARGS; + } + + if (!_UserNameIsValid(username)) { + return WH_ERROR_BADARGS; + } + + if (auth_data_len > WH_MESSAGE_AUTH_LOGIN_MAX_AUTH_DATA_LEN) { + return WH_ERROR_BADARGS; + } + + if (auth_data_len > 0 && auth_data == NULL) { + return WH_ERROR_BADARGS; + } + + msg_size = sizeof(*msg) + 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) { + memcpy(msg_auth_data, auth_data, auth_data_len); + } + + rc = wh_Client_SendRequest(c, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_LOGIN, + (uint16_t)msg_size, buffer); + + /* Zeroize sensitive credential data before returning */ + wh_Utils_ForceZero(buffer, sizeof(buffer)); + + return rc; +} + +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; + } + + if (out_user_id != NULL) { + *out_user_id = WH_USER_ID_INVALID; + } + + rc = wh_Client_RecvResponse(c, &resp_group, &resp_action, &resp_size, + buffer); + if (rc == WH_ERROR_OK) { + /* Validate response */ + if ((resp_group != WH_MESSAGE_GROUP_AUTH) || + (resp_action != WH_MESSAGE_AUTH_ACTION_LOGIN) || + (resp_size != sizeof(whMessageAuth_LoginResponse))) { + rc = WH_ERROR_ABORTED; + + /* check if server did not understand the request and responded with + * a simple error response */ + if (resp_size == sizeof(whMessageAuth_SimpleResponse)) { + /* NOT accepting WH_ERROR_OK from server if we got a response + * other than a login response */ + if (out_rc != NULL && msg->rc != WH_ERROR_OK) { + *out_rc = msg->rc; + rc = WH_ERROR_OK; + } + } + } + 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 != WH_ERROR_OK) { + 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 == WH_ERROR_OK) { + /* 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 != WH_ERROR_OK) { + 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); + size_t msg_size; + int rc = WH_ERROR_OK; + + if (c == NULL) { + return WH_ERROR_BADARGS; + } + + if (!_UserNameIsValid(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) { + rc = WH_ERROR_BUFFER_SIZE; + } + else { + msg->method = method; + msg->credentials_len = credentials_len; + if (credentials_len > 0) { + if (credentials == NULL) { + rc = WH_ERROR_BADARGS; + } + else if (credentials_len > + WH_MESSAGE_AUTH_USERADD_MAX_CREDENTIALS_LEN) { + rc = WH_ERROR_BUFFER_SIZE; + } + else { + memcpy(msg_credentials, credentials, credentials_len); + } + } + + if (rc == WH_ERROR_OK) { + msg_size = sizeof(*msg) + credentials_len; + if (msg_size > WOLFHSM_CFG_COMM_DATA_LEN) { + rc = WH_ERROR_BUFFER_SIZE; + } + else { + rc = wh_Client_SendRequest(c, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_ADD, + (uint16_t)msg_size, buffer); + } + } + } + + /* Zeroize sensitive credential data before returning */ + wh_Utils_ForceZero(buffer, sizeof(buffer)); + + return rc; +} + +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 == WH_ERROR_OK) { + /* 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 != WH_ERROR_OK) { + 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 == WH_ERROR_OK) { + /* 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 != WH_ERROR_OK) { + 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 (!_UserNameIsValid(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 == WH_ERROR_OK) { + /* 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 != WH_ERROR_OK) { + 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 == WH_ERROR_OK) { + /* 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 != WH_ERROR_OK) { + 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; + int rc; + + if (c == NULL) { + return WH_ERROR_BADARGS; + } + + if (current_credentials_len > WH_MESSAGE_AUTH_SETCREDS_MAX_CREDENTIALS_LEN) { + return WH_ERROR_BUFFER_SIZE; + } + if (new_credentials_len > WH_MESSAGE_AUTH_SETCREDS_MAX_CREDENTIALS_LEN) { + return WH_ERROR_BUFFER_SIZE; + } + if (current_credentials_len > 0 && current_credentials == NULL) { + return WH_ERROR_BADARGS; + } + if (new_credentials_len > 0 && new_credentials == NULL) { + return WH_ERROR_BADARGS; + } + + /* 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_len > 0) { + memcpy(msg_current_creds, current_credentials, current_credentials_len); + } + if (new_credentials_len > 0) { + memcpy(msg_new_creds, new_credentials, new_credentials_len); + } + + rc = wh_Client_SendRequest(c, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_SET_CREDENTIALS, + total_size, buffer); + + /* Zeroize sensitive credential data before returning */ + wh_Utils_ForceZero(buffer, sizeof(buffer)); + + return rc; +} + +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 == WH_ERROR_OK) { + /* 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 != WH_ERROR_OK) { + 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 000000000..a8c774c3f --- /dev/null +++ b/src/wh_message_auth.c @@ -0,0 +1,370 @@ +/* + * 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) +{ + const whMessageAuth_LoginRequest* src_header; + 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; + + WH_T16(magic, dest_header, src_header, method); + if (src_header != dest_header) { + memcpy(dest_header->username, src_header->username, + sizeof(dest_header->username)); + /* make sure the destination username is null terminated */ + dest_header->username[sizeof(dest_header->username) - 1] = '\0'; + } + 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_LOGIN_MAX_AUTH_DATA_LEN || + src_size < expected_size) { + return WH_ERROR_BADARGS; + } + + 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 array (WH_NUMBER_OF_GROUPS + 1 bytes) + * The last byte is the admin flag */ + for (i = 0; i < WH_NUMBER_OF_GROUPS + 1; i++) { + buffer[idx++] = permissions->groupPermissions[i]; + } + + /* 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 array (WH_NUMBER_OF_GROUPS + 1 bytes) + * The last byte is the admin flag */ + for (i = 0; i < WH_NUMBER_OF_GROUPS + 1; i++) { + permissions->groupPermissions[i] = buffer[idx++]; + } + + /* 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] = + ((uint32_t)buffer[idx]) | ((uint32_t)buffer[idx + 1] << 8) | + ((uint32_t)buffer[idx + 2] << 16) | + ((uint32_t)buffer[idx + 3] << 24); + idx += 4; + } + } + + /* Deserialize keyIdCount (2 bytes) */ + keyIdCount = (uint16_t)(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 = ((uint32_t)buffer[idx]) | ((uint32_t)buffer[idx + 1] << 8) | + ((uint32_t)buffer[idx + 2] << 16) | + ((uint32_t)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) +{ + const whMessageAuth_UserAddRequest* src_header; + 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; + + if (src_header != dest_header) { + memcpy(dest_header->username, src_header->username, + sizeof(dest_header->username)); + /* make sure the destination username is null terminated */ + dest_header->username[sizeof(dest_header->username) - 1] = '\0'; + 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_USERADD_MAX_CREDENTIALS_LEN || + src_size < expected_size) { + return WH_ERROR_BUFFER_SIZE; + } + + 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)); + /* make sure the destination username is null terminated */ + dest->username[sizeof(dest->username) - 1] = '\0'; + } + (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) +{ + const whMessageAuth_UserSetCredentialsRequest* src_header; + 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; + + /* 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 (dest_header->current_credentials_len > + WH_MESSAGE_AUTH_SETCREDS_MAX_CREDENTIALS_LEN) { + return WH_ERROR_BUFFER_SIZE; + } + if (dest_header->new_credentials_len > + WH_MESSAGE_AUTH_SETCREDS_MAX_CREDENTIALS_LEN) { + return WH_ERROR_BUFFER_SIZE; + } + + /* Validate lengths */ + expected_size = header_size + dest_header->current_credentials_len + + dest_header->new_credentials_len; + if (src_size < expected_size) { + return WH_ERROR_BADARGS; + } + + return 0; +} diff --git a/src/wh_server.c b/src/wh_server.c index 5098a2da4..ee19046d8 100644 --- a/src/wh_server.c +++ b/src/wh_server.c @@ -42,10 +42,20 @@ #include "wolfhsm/wh_message.h" #include "wolfhsm/wh_message_comm.h" #include "wolfhsm/wh_message_nvm.h" +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#include "wolfhsm/wh_message_auth.h" +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ +#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" +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#include "wolfhsm/wh_auth.h" +#include "wolfhsm/wh_server_auth.h" +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ #include "wolfhsm/wh_server_crypto.h" #include "wolfhsm/wh_server_keystore.h" #include "wolfhsm/wh_server_counter.h" @@ -75,6 +85,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; @@ -252,6 +263,16 @@ static int _wh_Server_HandleCommRequest(whServerContext* server, { /* No message */ /* Process the close action */ + +#ifdef WOLFHSM_CFG_ENABLE_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_ENABLE_AUTHENTICATION */ + wh_Server_SetConnected(server, WH_COMM_DISCONNECTED); *out_resp_size = 0; @@ -297,6 +318,175 @@ static int _wh_Server_HandlePkcs11Request(whServerContext* server, return rc; } +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +/* 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 _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) { +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + 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; +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + + 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; +} +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + + int wh_Server_HandleRequestMessage(whServerContext* server) { uint16_t magic = 0; @@ -327,6 +517,38 @@ int wh_Server_HandleRequestMessage(whServerContext* server) if (rc == WH_ERROR_OK) { group = WH_MESSAGE_GROUP(kind); action = WH_MESSAGE_ACTION(kind); + +#ifdef WOLFHSM_CFG_ENABLE_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 = _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_ENABLE_AUTHENTICATION */ + switch (group) { case WH_MESSAGE_GROUP_COMM: @@ -339,6 +561,38 @@ int wh_Server_HandleRequestMessage(whServerContext* server) size, data, &size, data); break; + case WH_MESSAGE_GROUP_AUTH: +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + if (server->auth == NULL) { + /* Auth compiled in but not configured - format error response + */ + rc = WH_AUTH_NOT_ENABLED; + if (data != NULL) { + *(int32_t*)data = + (int32_t)wh_Translate32(magic, (uint32_t)rc); + size = sizeof(int32_t); + } + else { + size = 0; + } + } + else { + rc = wh_Server_HandleAuthRequest(server, magic, action, seq, + size, data, &size, data); + } +#else + /* Format simple error response indicating auth is not enabled */ + rc = WH_AUTH_NOT_ENABLED; + if (data != NULL) { + *(int32_t*)data = (int32_t)wh_Translate32(magic, (uint32_t)rc); + size = sizeof(int32_t); + } + else { + size = 0; + } +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + 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 000000000..e949e2bb9 --- /dev/null +++ b/src/wh_server_auth.c @@ -0,0 +1,288 @@ +/* + * 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" +#include "wolfhsm/wh_utils.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 = NULL; + + rc = wh_MessageAuth_TranslateLoginRequest( + magic, req_packet, req_size, &req); + if (rc != WH_ERROR_OK) { + resp.rc = rc; + } + else { + auth_data = (uint8_t*)req_packet + + sizeof(whMessageAuth_LoginRequest); + 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; + } + } + } + /* Zeroize sensitive credential data before returning */ + if (auth_data != NULL && req.auth_data_len > 0) { + wh_Utils_ForceZero(auth_data, req.auth_data_len); + } + + 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 = NULL; + + rc = wh_MessageAuth_TranslateUserAddRequest( + magic, req_packet, req_size, &req); + if (rc != WH_ERROR_OK) { + resp.rc = rc; + } + else { + credentials = (uint8_t*)req_packet + + sizeof(whMessageAuth_UserAddRequest); + 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; + } + } + /* Zeroize sensitive credential data before returning */ + if (credentials != NULL && req.credentials_len > 0) { + wh_Utils_ForceZero(credentials, 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 = NULL; + uint8_t* new_creds = NULL; + 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); + if (rc != 0) { + resp.rc = rc; + } + else { + current_creds = (uint8_t*)req_packet + + sizeof(whMessageAuth_UserSetCredentialsRequest); + new_creds = current_creds + + req_header.current_credentials_len; + 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; + } + } + /* Zeroize sensitive credential data before returning */ + if (current_creds != NULL && req_header.current_credentials_len > 0) { + wh_Utils_ForceZero(current_creds, + req_header.current_credentials_len); + } + if (new_creds != NULL && req_header.new_credentials_len > 0) { + wh_Utils_ForceZero(new_creds, req_header.new_credentials_len); + } + + 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/src/wh_server_she.c b/src/wh_server_she.c index cbb7ed680..e98fbc92f 100644 --- a/src/wh_server_she.c +++ b/src/wh_server_she.c @@ -456,7 +456,7 @@ static int _LoadKey(whServerContext* server, uint16_t magic, uint16_t req_size, uint8_t kdfInput[WH_SHE_KEY_SZ * 2]; uint8_t cmacOutput[AES_BLOCK_SIZE]; uint8_t tmpKey[WH_SHE_KEY_SZ]; - whNvmMetadata meta[1] = {0}; + whNvmMetadata meta[1] = {0}; uint32_t she_meta_count = 0; uint32_t she_meta_flags = 0; uint32_t msg_counter_val; diff --git a/src/wh_utils.c b/src/wh_utils.c index 286224f07..4e50bb0c0 100644 --- a/src/wh_utils.c +++ b/src/wh_utils.c @@ -91,6 +91,32 @@ int wh_Utils_memeqzero(uint8_t* buffer, uint32_t size) return 1; } +/* Secure zeroization that resists compiler optimization. + * Uses volatile to prevent the compiler from optimizing away the writes. */ +void wh_Utils_ForceZero(void* mem, uint32_t size) +{ + volatile uint8_t* p = (volatile uint8_t*)mem; + while (size > 0) { + *p = 0; + p++; + size--; + } +} + +/** Constant time compare of two buffers to mitigate side channel leaks + * returns 0 on success where buffer a is equal to buffer b for length bytes */ +int wh_Utils_ConstantCompare(const uint8_t* a, const uint8_t* b, size_t length) +{ + size_t i; + size_t ret = 0; + + for (i = 0; i < length; i++) { + ret |= a[i] ^ b[i]; + } + + return (int)ret; +} + /** Cache helper functions */ const void* wh_Utils_CacheInvalidate(const void* p, size_t n) { diff --git a/test/Makefile b/test/Makefile index 259218045..dc5efa7cb 100644 --- a/test/Makefile +++ b/test/Makefile @@ -152,6 +152,11 @@ ifeq ($(STRESS),1) DEF += -DWOLFHSM_CFG_TEST_STRESS endif +# Support an authentication-capable build +ifeq ($(AUTH),1) + DEF += -DWOLFHSM_CFG_ENABLE_AUTHENTICATION +endif + ## Project defines # Option to build wolfcrypt tests ifeq ($(TESTWOLFCRYPT),1) @@ -328,6 +333,7 @@ coverage: $(BUILD_DIR)/$(BIN).elf @echo "Generating coverage report..." mkdir -p ../coverage && gcovr Build \ + --gcov-ignore-parse-errors="negative_hits.warn" \ --root .. \ --gcov-executable gcov \ --filter '\.\./src/.*' \ diff --git a/test/wh_test.c b/test/wh_test.c index 3fd9a1bb5..478c58078 100644 --- a/test/wh_test.c +++ b/test/wh_test.c @@ -42,6 +42,9 @@ #include "wh_test_log.h" #include "wh_test_lock.h" #include "wh_test_posix_threadsafe_stress.h" +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#include "wh_test_auth.h" +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ #if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) #include "wh_test_cert.h" @@ -89,6 +92,11 @@ int whTest_Unit(void) WH_TEST_ASSERT(0 == whTest_Comm()); WH_TEST_ASSERT(0 == whTest_ClientServer()); +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + /* Auth tests */ + WH_TEST_ASSERT(0 == whTest_AuthMEM()); +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + #ifndef WOLFHSM_CFG_NO_CRYPTO /* Crypto Tests */ WH_TEST_ASSERT(0 == whTest_Crypto()); @@ -119,7 +127,6 @@ int whTest_Unit(void) #endif #endif /* !WOLFHSM_CFG_NO_CRYPTO */ - return 0; } #endif /* WOLFHSM_CFG_ENABLE_CLIENT && WOLFHSM_CFG_ENABLE_SERVER */ @@ -153,6 +160,10 @@ int whTest_ClientConfig(whClientConfig* clientCfg) WH_TEST_RETURN_ON_FAIL(whTest_WolfCryptTestCfg(clientCfg)); #endif /* WOLFHSM_CFG_TEST_WOLFCRYPTTEST */ +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + WH_TEST_RETURN_ON_FAIL(whTest_AuthTCP(clientCfg)); +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + return WH_ERROR_OK; } diff --git a/test/wh_test_auth.c b/test/wh_test_auth.c new file mode 100644 index 000000000..a0c4a8d47 --- /dev/null +++ b/test/wh_test_auth.c @@ -0,0 +1,1442 @@ +/* + * 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" +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#include "wolfhsm/wh_auth.h" +#include "wolfhsm/wh_message_auth.h" +#include "wolfhsm/wh_auth_base.h" +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ +#include "wolfhsm/wh_nvm.h" +#include "wolfhsm/wh_nvm_flash.h" +#include "wolfhsm/wh_flash_ramsim.h" +#include "wolfhsm/wh_message.h" + +#include "wh_test_common.h" +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#include "wh_test_auth.h" +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + +#if defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) +#include "port/posix/posix_transport_tcp.h" +#endif + +#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 + +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + +#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}}; + +/* Test-specific authorization override callbacks to verify they are invoked */ +static int test_checkRequestAuthorizationCalled = 0; +static int test_checkKeyAuthorizationCalled = 0; + +static int test_CheckRequestAuthorization(void* context, int err, + uint16_t user_id, uint16_t group, + uint16_t action) +{ + (void)context; + (void)user_id; + (void)group; + (void)action; + test_checkRequestAuthorizationCalled++; + /* Pass through the error code unchanged */ + return err; +} + +static int test_CheckKeyAuthorization(void* context, int err, uint16_t user_id, + uint32_t key_id, uint16_t action) +{ + (void)context; + (void)user_id; + (void)key_id; + (void)action; + test_checkKeyAuthorizationCalled++; + /* Pass through the error code unchanged */ + return err; +} + +/* Auth setup following wh_posix_server pattern */ +static whAuthCb default_auth_cb = { + .Init = wh_Auth_BaseInit, + .Cleanup = wh_Auth_BaseCleanup, + .Login = wh_Auth_BaseLogin, + .Logout = wh_Auth_BaseLogout, + .CheckRequestAuthorization = test_CheckRequestAuthorization, + .CheckKeyAuthorization = test_CheckKeyAuthorization, + .UserAdd = wh_Auth_BaseUserAdd, + .UserDelete = wh_Auth_BaseUserDelete, + .UserSetPermissions = wh_Auth_BaseUserSetPermissions, + .UserGet = wh_Auth_BaseUserGet, + .UserSetCredentials = wh_Auth_BaseUserSetCredentials}; +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 = wh_Auth_BaseUserAdd(&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); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_MessageAuth_TranslateLoginRequest(0, &login_hdr, 0, &login_out); + 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_LOGIN_MAX_AUTH_DATA_LEN + 1); + rc = wh_MessageAuth_TranslateLoginRequest(0, &login_hdr, sizeof(login_hdr), + &login_out); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + rc = wh_MessageAuth_TranslateUserAddRequest(0, NULL, 0, &add_out); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + memset(&add_hdr, 0, sizeof(add_hdr)); + add_hdr.credentials_len = + (uint16_t)(WH_MESSAGE_AUTH_USERADD_MAX_CREDENTIALS_LEN + 1); + rc = wh_MessageAuth_TranslateUserAddRequest(0, &add_hdr, sizeof(add_hdr), + &add_out); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BUFFER_SIZE); + + rc = wh_MessageAuth_TranslateUserSetCredentialsRequest(0, NULL, 0, &set_hdr); + 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); + 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)); + if (server_rc == WH_AUTH_NOT_ENABLED) { + WH_TEST_PRINT("Server does not support authentication, skipping " + "authentication tests\n"); + return WH_AUTH_NOT_ENABLED; + } + 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 - should fail duplicate username */ + 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); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id2 == WH_USER_ID_INVALID); + + /* Test 4: Non-admin cannot add admin user */ + WH_TEST_PRINT(" Test: Non-admin cannot add admin user\n"); + { + whAuthPermissions nonadmin_add_perms; + + memset(&nonadmin_add_perms, 0, sizeof(nonadmin_add_perms)); + WH_AUTH_SET_ALLOWED_ACTION(nonadmin_add_perms, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_ADD); + WH_AUTH_SET_IS_ADMIN(nonadmin_add_perms, 0); + + server_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "addadmin_testuser", + nonadmin_add_perms, + WH_AUTH_METHOD_PIN, + "pass", 4, &server_rc, + &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID); + + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "addadmin_testuser", "pass", 4, + &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Non-admin can add other non-admin users */ + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + user_id2 = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "other_nonadmin", + perms, WH_AUTH_METHOD_PIN, + "test", 4, &server_rc, + &user_id2)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id2 != WH_USER_ID_INVALID); + + /* Non-admin cannot add admin user */ + memset(&perms, 0xFF, sizeof(perms)); + perms.keyIdCount = 0; + server_rc = 0; + user_id2 = WH_USER_ID_INVALID; + _whTest_Auth_UserAddOp(client, "wouldbe_admin", perms, + WH_AUTH_METHOD_PIN, "test", 4, &server_rc, + &user_id2); + WH_TEST_ASSERT_RETURN(server_rc == WH_AUTH_PERMISSION_ERROR); + WH_TEST_ASSERT_RETURN(user_id2 == WH_USER_ID_INVALID); + } + + _whTest_Auth_LogoutOp(client, user_id, &server_rc); + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + TEST_ADMIN_USERNAME, + TEST_ADMIN_PIN, 4, + &server_rc, &admin_id)); + + /* Cleanup */ + server_rc = 0; + _whTest_Auth_DeleteUserByName(client, "testuser1"); + _whTest_Auth_DeleteUserByName(client, "testuser2"); + _whTest_Auth_DeleteUserByName(client, "addadmin_testuser"); + _whTest_Auth_DeleteUserByName(client, "other_nonadmin"); + _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: Non-admin user trying to delete another user */ + WH_TEST_PRINT(" Test: Non-admin user trying to delete another user\n"); + { + whUserId nonadmin_id = WH_USER_ID_INVALID; + whUserId target_id = WH_USER_ID_INVALID; + whAuthPermissions nonadmin_perms; + + /* non-admin user with all auth group actions (includes delete) */ + memset(&nonadmin_perms, 0, sizeof(nonadmin_perms)); + WH_AUTH_SET_ALLOWED_GROUP(nonadmin_perms, WH_MESSAGE_GROUP_AUTH); + WH_AUTH_SET_IS_ADMIN(nonadmin_perms, 0); + + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp( + client, "nonadmin", nonadmin_perms, WH_AUTH_METHOD_PIN, "pass", 4, + &server_rc, &nonadmin_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(nonadmin_id != WH_USER_ID_INVALID); + + /* Create a target user to try to delete */ + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp( + client, "targetuser", perms, WH_AUTH_METHOD_PIN, "pass", 4, + &server_rc, &target_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(target_id != WH_USER_ID_INVALID); + + /* Logout admin and login as non-admin user */ + server_rc = 0; + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "nonadmin", "pass", 4, + &server_rc, &nonadmin_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Try to delete the target user as non-admin - should fail */ + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserDeleteOp(client, target_id, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + WH_TEST_PRINT(" Non-admin delete attempt correctly denied\n"); + + /* Logout non-admin and login as admin to cleanup */ + server_rc = 0; + _whTest_Auth_LogoutOp(client, nonadmin_id, &server_rc); + + 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); + + /* Cleanup - delete both test users */ + _whTest_Auth_UserDeleteOp(client, nonadmin_id, &server_rc); + _whTest_Auth_UserDeleteOp(client, target_id, &server_rc); + } + + /* Test 4: 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)); + WH_AUTH_SET_ALLOWED_ACTION(new_perms, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_ADD); + 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); + { + /* Compare group permission and all action permission words */ + int groupIndex = (WH_MESSAGE_GROUP_AUTH >> 8) & 0xFF; + int j; + int permissions_match = 1; + /* Verify groupPermissions for this group */ + WH_TEST_ASSERT_RETURN(fetched_perms.groupPermissions[groupIndex] == + new_perms.groupPermissions[groupIndex]); + 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 auth group but not USER_ADD action */ + memset(&perms, 0, sizeof(perms)); + WH_AUTH_SET_ALLOWED_GROUP(perms, WH_MESSAGE_GROUP_AUTH); + WH_AUTH_CLEAR_ALLOWED_ACTION(perms, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_ADD); + 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 USER_ADD permission */ + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + /* Test 3b: User with auth group cleared cannot add (WH_AUTH_CLEAR_ALLOWED_GROUP) */ + WH_TEST_PRINT(" Test: User with no auth group cannot add\n"); + _whTest_Auth_LogoutOp(client, logged_in_id, &server_rc); + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp( + client, WH_AUTH_METHOD_PIN, "admin", "1234", 4, &server_rc, &admin_id)); + memset(&perms, 0xFF, sizeof(perms)); + perms.keyIdCount = 0; + WH_AUTH_CLEAR_ALLOWED_GROUP(perms, WH_MESSAGE_GROUP_AUTH); + WH_AUTH_SET_IS_ADMIN(perms, 0); + server_rc = 0; + whUserId noauth_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "noauthuser", perms, + WH_AUTH_METHOD_PIN, "pass", 4, + &server_rc, &noauth_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "noauthuser", "pass", 4, + &server_rc, &logged_in_id)); + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + temp_id2 = WH_USER_ID_INVALID; + _whTest_Auth_UserAddOp(client, "testuser7b", perms, WH_AUTH_METHOD_PIN, + "test", 4, &server_rc, &temp_id2); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + _whTest_Auth_LogoutOp(client, logged_in_id, &server_rc); + + /* 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)); + WH_AUTH_SET_ALLOWED_ACTION(perms, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_ADD); + /* Free slot: noauthuser no longer needed (WH_AUTH_BASE_MAX_USERS=5) */ + _whTest_Auth_DeleteUserByName(client, "noauthuser"); + 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; +} + + +static int CheckServerSupportsAuth(whClientContext* client_ctx) +{ + int32_t server_rc; + whUserId user_id; + int isSupported = 0; + + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp( + client_ctx, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, + strlen(TEST_ADMIN_PIN), &server_rc, &user_id)); + if (server_rc != WH_AUTH_NOT_ENABLED) { + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_LogoutOp(client_ctx, user_id, &server_rc)); + isSupported = 1; + } + return isSupported; +} + +/* Main Test Function */ +int whTest_AuthTest(whClientContext* client_ctx) +{ + WH_TEST_PRINT("Testing authentication functionality...\n"); + + if (!CheckServerSupportsAuth(client_ctx)) { + WH_TEST_PRINT("Server does not support authentication, skipping " + "authentication tests\n"); + return WH_TEST_SKIP; + } + + 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)); + + /* Verify that authorization callbacks were invoked during tests */ + WH_TEST_PRINT( + "Verifying authorization override callbacks were called...\n"); + WH_TEST_ASSERT_RETURN(test_checkRequestAuthorizationCalled > 0); + + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_CleanupMemory()); + + return WH_TEST_SUCCESS; +#else + return WH_TEST_FAIL; +#endif +} +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ diff --git a/test/wh_test_auth.h b/test/wh_test_auth.h new file mode 100644 index 000000000..e853634ca --- /dev/null +++ b/test/wh_test_auth.h @@ -0,0 +1,49 @@ +/* + * 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" + +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#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_CFG_ENABLE_AUTHENTICATION */ +#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 56107adb7..e2e5f4119 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 @@ -1160,6 +1166,12 @@ 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); +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + /* 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)); +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ for (counter = 0; counter < REPEAT_COUNT; counter++) { @@ -1710,6 +1722,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_common.h b/test/wh_test_common.h index ab5630253..aab655162 100644 --- a/test/wh_test_common.h +++ b/test/wh_test_common.h @@ -33,6 +33,7 @@ #define WH_TEST_FAIL (-1) #define WH_TEST_SUCCESS (0) +#define WH_TEST_SKIP (1) #define WH_TEST_DEFAULT_CLIENT_ID (1) /* Test-specific print macro that always prints (replacement for printf in tests) @@ -72,13 +73,13 @@ * stringified call argument along with caller source file info and * causes the calling function to return the value of "call" */ -#define WH_TEST_RETURN_ON_FAIL(call) \ - do { \ - int ret = (call); \ - if (ret != 0) { \ - WH_ERROR_PRINT(#call ": ret=%d\n", ret); \ - return ret; \ - } \ +#define WH_TEST_RETURN_ON_FAIL(call) \ + do { \ + int ret = (call); \ + if (ret != WH_TEST_SUCCESS && ret != WH_TEST_SKIP) { \ + WH_ERROR_PRINT(#call ": ret=%d\n", ret); \ + return ret; \ + } \ } while (0) diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index 93851fc01..5279ed7d3 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -71,6 +71,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 { @@ -695,10 +702,8 @@ static int whTest_CryptoEccCacheDuplicate(whClientContext* client) #define WH_TEST_ECC_HASH_SIZE WC_MAX_DIGEST_SIZE static int whTest_CryptoEccCrossVerify_OneCurve(whClientContext* ctx, - WC_RNG* rng, - int keySize, - int curveId, - const char* name) + WC_RNG* rng, int keySize, + int curveId, const char* name) { ecc_key hsmKey[1] = {0}; ecc_key swKey[1] = {0}; @@ -837,7 +842,7 @@ static int whTest_CryptoEccCrossVerify_OneCurve(whClientContext* ctx, if (ret == 0) { /* Sign with software */ sigLen = sizeof(sig); - ret = wc_ecc_sign_hash(hash, sizeof(hash), sig, &sigLen, rng, swKey); + ret = wc_ecc_sign_hash(hash, sizeof(hash), sig, &sigLen, rng, swKey); if (ret != 0) { WH_ERROR_PRINT("%s: SW sign failed: %d\n", name, ret); } @@ -5293,6 +5298,17 @@ int whTest_CryptoClientConfig(whClientConfig* config) WH_ERROR_PRINT("Failed to comm init:%d\n", ret); } + if (ret == 0) { +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + int serverRc; + + /* 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), &serverRc, NULL)); +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + } + #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 6d6752172..df477ec77 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,14 @@ int whTest_KeyWrapClientConfig(whClientConfig* clientCfg) goto cleanup_and_exit; } +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + /* 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); +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + 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_posix_threadsafe_stress.c b/test/wh_test_posix_threadsafe_stress.c index fb4c6a0fc..c0fa0a1d5 100644 --- a/test/wh_test_posix_threadsafe_stress.c +++ b/test/wh_test_posix_threadsafe_stress.c @@ -39,9 +39,11 @@ #include "wolfhsm/wh_settings.h" +/* Note: pthread_barrier_t is not available on macOS, so skip this test */ #if defined(WOLFHSM_CFG_THREADSAFE) && defined(WOLFHSM_CFG_TEST_POSIX) && \ defined(WOLFHSM_CFG_GLOBAL_KEYS) && defined(WOLFHSM_CFG_ENABLE_CLIENT) && \ - defined(WOLFHSM_CFG_ENABLE_SERVER) && !defined(WOLFHSM_CFG_NO_CRYPTO) + defined(WOLFHSM_CFG_ENABLE_SERVER) && !defined(WOLFHSM_CFG_NO_CRYPTO) && \ + !defined(__APPLE__) #include #include diff --git a/test/wh_test_she.c b/test/wh_test_she.c index ea918fcd9..c3c6703f7 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,13 @@ 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)); +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + /* 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), &ret, NULL)); +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + { int32_t server_rc = 0; whNvmId avail_objects = 0; diff --git a/wolfhsm/wh_auth.h b/wolfhsm/wh_auth.h new file mode 100644 index 000000000..fccab72eb --- /dev/null +++ b/wolfhsm/wh_auth.h @@ -0,0 +1,393 @@ +/* + * 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" +#include "wolfhsm/wh_message.h" /* for WH_NUMBER_OF_GROUPS */ + +#ifdef WOLFHSM_CFG_THREADSAFE +#include "wolfhsm/wh_lock.h" +#endif + +/** 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_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 \ + */ + +/* Check if the user has admin permission flag set */ +#define WH_AUTH_IS_ADMIN(permissions) \ + (permissions.groupPermissions[WH_NUMBER_OF_GROUPS]) + +/* Set the admin permission flag (value: 0 = non-admin, non-zero = admin) */ +#define WH_AUTH_SET_IS_ADMIN(permissions, value) \ + ((permissions).groupPermissions[WH_NUMBER_OF_GROUPS] = (value) ? 1 : 0) + +/* Convert action enum value (0-255) to word index and bitmask. + * Sets wordIdx to the array index (0-7) and bitMask to the bit position. */ +#define WH_AUTH_ACTION_TO_WORD_AND_BITMASK(action, wordIdx, bitMask) \ + do { \ + (wordIdx) = ((action) / 32); \ + (bitMask) = (1UL << ((action) % 32)); \ + } while (0) + +/* Enable a message group in permissions. Allows all actions in the group. */ +#define WH_AUTH_SET_ALLOWED_GROUP(permissions, group) \ + do { \ + int _g = ((group) >> 8) & 0xFF; \ + int _i; \ + (permissions).groupPermissions[_g] = 1; \ + for (_i = 0; _i < (int)WH_AUTH_ACTION_WORDS; _i++) { \ + (permissions).actionPermissions[_g][_i] = 0xFFFFFFFFU; \ + } \ + } while (0) + +/* Set permissions to allow a specific action in a group. + * Enables the group and only the given action bit. */ +#define WH_AUTH_SET_ALLOWED_ACTION(permissions, group, action) \ + do { \ + int _g = ((group) >> 8) & 0xFF; \ + uint32_t _w, _b; \ + WH_AUTH_ACTION_TO_WORD_AND_BITMASK((action), _w, _b); \ + (permissions).groupPermissions[_g] = 1; \ + (permissions).actionPermissions[_g][_w] |= (_b); \ + } while (0) + +/* Set permissions to disallow a group. Clears group and all action bits. */ +#define WH_AUTH_CLEAR_ALLOWED_GROUP(permissions, group) \ + do { \ + int _g = ((group) >> 8) & 0xFF; \ + int _i; \ + (permissions).groupPermissions[_g] = 0; \ + for (_i = 0; _i < (int)WH_AUTH_ACTION_WORDS; _i++) { \ + (permissions).actionPermissions[_g][_i] = 0; \ + } \ + } while (0) + +/* Clear permission for a specific action in a group. */ +#define WH_AUTH_CLEAR_ALLOWED_ACTION(permissions, group, action) \ + do { \ + int _g = ((group) >> 8) & 0xFF; \ + uint32_t _w, _b; \ + WH_AUTH_ACTION_TO_WORD_AND_BITMASK((action), _w, _b); \ + (permissions).actionPermissions[_g][_w] &= ~(_b); \ + } while (0) + +typedef struct { + uint8_t groupPermissions[WH_NUMBER_OF_GROUPS + 1]; /* boolean array of if group + is allowed, last boolean + is admin permission */ + 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); + + + /* Allow override of if action is authorized for a user */ + int (*CheckRequestAuthorization)(void* context, int err, uint16_t user_id, + uint16_t group, uint16_t action); + + /* Allow override of if a key is authorized for use */ + int (*CheckKeyAuthorization)(void* context, int err, 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; +#ifdef WOLFHSM_CFG_THREADSAFE + whLock lock; /* Lock for serializing auth operations */ +#endif +} whAuthContext; + +/* Simple helper configuration structure associated with an Auth Manager + * instance */ +typedef struct whAuthConfig_t { + whAuthCb* cb; + void* context; + void* config; +#ifdef WOLFHSM_CFG_THREADSAFE + whLockConfig* lockConfig; /* Lock configuration for thread safety */ +#endif +} 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); + +#ifdef WOLFHSM_CFG_THREADSAFE +/** + * @brief Acquires the auth lock. + * + * @param[in] auth Pointer to the auth context. Must not be NULL. + * @return int WH_ERROR_OK on success. + * WH_ERROR_BADARGS if auth is NULL. + * Other negative error codes on lock acquisition failure. + */ +int wh_Auth_Lock(whAuthContext* auth); + +/** + * @brief Releases the auth lock. + * + * @param[in] auth Pointer to the auth context. Must not be NULL. + * @return int WH_ERROR_OK on success. + * WH_ERROR_BADARGS if auth is NULL. + * Other negative error codes on lock release failure. + */ +int wh_Auth_Unlock(whAuthContext* auth); + +#define WH_AUTH_LOCK(auth) wh_Auth_Lock(auth) +#define WH_AUTH_UNLOCK(auth) wh_Auth_Unlock(auth) +#else +#define WH_AUTH_LOCK(auth) (WH_ERROR_OK) +#define WH_AUTH_UNLOCK(auth) (WH_ERROR_OK) +#endif + +#endif /* !WOLFHSM_WH_AUTH_H_ */ diff --git a/wolfhsm/wh_auth_base.h b/wolfhsm/wh_auth_base.h new file mode 100644 index 000000000..3975f8c05 --- /dev/null +++ b/wolfhsm/wh_auth_base.h @@ -0,0 +1,159 @@ +/* + * 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 . + */ +/* + * wh_auth_base.h + * + * Basic authentication and authorization implementation. + */ + +#ifndef WOLFHSM_WH_AUTH_BASE_H_ +#define WOLFHSM_WH_AUTH_BASE_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 implementation. + * + * @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 wh_Auth_BaseInit(void* context, const void* config); + +/** + * @brief Cleanup the auth base implementation. + * + * @param[in] context Pointer to the auth base context. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Auth_BaseCleanup(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 wh_Auth_BaseLogin(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); + +/** + * @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 wh_Auth_BaseLogout(void* context, uint16_t current_user_id, + uint16_t user_id); + +/** + * @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 wh_Auth_BaseUserAdd(void* 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 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 wh_Auth_BaseUserDelete(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 wh_Auth_BaseUserSetPermissions(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 wh_Auth_BaseUserGet(void* context, const char* username, + whUserId* 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 wh_Auth_BaseUserSetCredentials(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 /* WOLFHSM_WH_AUTH_BASE_H_ */ diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index 2990ed297..b4621db5b 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 @@ -1833,6 +1834,369 @@ 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 (null-terminated C string). + * @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 (null-terminated C string). + * @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 5ce75cdde..699d2949e 100644 --- a/wolfhsm/wh_error.h +++ b/wolfhsm/wh_error.h @@ -67,6 +67,11 @@ 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 */ + WH_AUTH_NOT_ENABLED = -2302, /* server does not have auth feature */ }; #define WH_SHE_ERC_NO_ERROR WH_ERROR_OK diff --git a/wolfhsm/wh_message.h b/wolfhsm/wh_message.h index 7ae54c249..26aeda9b7 100644 --- a/wolfhsm/wh_message.h +++ b/wolfhsm/wh_message.h @@ -47,10 +47,13 @@ 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_GROUP_MAX = 0x0D00, /* Last group in list */ WH_MESSAGE_ACTION_MASK = 0x00FF, /* 255 subtypes per group*/ WH_MESSAGE_ACTION_NONE = 0x0000, /* No action. Invalid. */ }; +#define WH_NUMBER_OF_GROUPS ((WH_MESSAGE_GROUP_MAX >> 8) + 1) /* keystore actions */ enum WH_KEY_ENUM { @@ -98,6 +101,17 @@ enum { WH_COUNTER_DESTROY, }; +/* auth actions */ +enum { + WH_MESSAGE_AUTH_ACTION_LOGIN, + WH_MESSAGE_AUTH_ACTION_LOGOUT, + WH_MESSAGE_AUTH_ACTION_USER_ADD, + WH_MESSAGE_AUTH_ACTION_USER_DELETE, + WH_MESSAGE_AUTH_ACTION_USER_GET, + WH_MESSAGE_AUTH_ACTION_USER_SET_PERMISSIONS, + WH_MESSAGE_AUTH_ACTION_USER_SET_CREDENTIALS, +}; + /* 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 000000000..e36078688 --- /dev/null +++ b/wolfhsm/wh_message_auth.h @@ -0,0 +1,331 @@ +/* + * 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" + +#define WH_MESSAGE_AUTH_MAX_USERNAME_LEN 32 +#define 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. + * + * Translates the fixed-size header only. Auth data follows the header in + * src_packet at offset sizeof(whMessageAuth_LoginRequest). + * + * @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. + * @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); + +/** 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 + * uint8_t[WH_NUMBER_OF_GROUPS + 1] (groupPermissions, last byte is admin flag) + + * 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 \ + ((WH_NUMBER_OF_GROUPS + 1) + (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. + * + * Translates the fixed-size header only. Credentials follow the header in + * src_packet at offset sizeof(whMessageAuth_UserAddRequest). + * + * @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. + * @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); + +/** 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. + * + * Translates the fixed-size header only. Current and new credentials follow + * the header in src_packet at offsets sizeof(header) and + * sizeof(header) + current_credentials_len. + * + * @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. + * @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); + +/** User Set Credentials Response */ +/* Use SimpleResponse */ + +/* + * Per-message maximum credential lengths based on actual header sizes. + * These ensure each message type can use the maximum available space + * in WOLFHSM_CFG_COMM_DATA_LEN for its variable-length credential data. + */ + +/* Login: auth_data follows the LoginRequest header */ +#define WH_MESSAGE_AUTH_LOGIN_MAX_AUTH_DATA_LEN \ + (WOLFHSM_CFG_COMM_DATA_LEN - sizeof(whMessageAuth_LoginRequest)) + +/* UserAdd: credentials follow the UserAddRequest header */ +#define WH_MESSAGE_AUTH_USERADD_MAX_CREDENTIALS_LEN \ + (WOLFHSM_CFG_COMM_DATA_LEN - sizeof(whMessageAuth_UserAddRequest)) + +/* UserSetCredentials: both current and new credentials follow the header. + * Each credential buffer can use up to half of the remaining space. */ +#define WH_MESSAGE_AUTH_SETCREDS_MAX_CREDENTIALS_LEN \ + ((WOLFHSM_CFG_COMM_DATA_LEN - sizeof(whMessageAuth_UserSetCredentialsRequest)) / 2) + +#endif /* !WOLFHSM_WH_MESSAGE_AUTH_H_ */ diff --git a/wolfhsm/wh_server.h b/wolfhsm/wh_server.h index 6cf08d3d3..1eebbb892 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 @@ -136,6 +137,7 @@ typedef struct { typedef struct whServerConfig_t { whCommServerConfig* comm_config; whNvmContext* nvm; + whAuthContext* auth; #ifndef WOLFHSM_CFG_NO_CRYPTO whServerCryptoContext* crypto; @@ -158,6 +160,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 000000000..cc80ff74a --- /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_ */ diff --git a/wolfhsm/wh_utils.h b/wolfhsm/wh_utils.h index 415c90d98..2f796904c 100644 --- a/wolfhsm/wh_utils.h +++ b/wolfhsm/wh_utils.h @@ -69,6 +69,12 @@ uint32_t wh_Utils_ntohl(uint32_t networklong); int wh_Utils_memeqzero(uint8_t* buffer, uint32_t size); +/* Secure zeroization that resists compiler optimization */ +void wh_Utils_ForceZero(void* mem, uint32_t size); + +/* Constant compare of 2 buffers that mitigates side channel leaks */ +int wh_Utils_ConstantCompare(const uint8_t* a, const uint8_t* b, size_t length); + /** Cache helper functions */ /* Flush the cache lines starting at p for at least n bytes */ void* wh_Utils_CacheFlush(void* p, size_t n);