From ea93c13a3d9386256c6392d08db1d2038beda9e9 Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Fri, 20 Feb 2026 10:58:10 -0700 Subject: [PATCH] add NVM for base authentication manager users --- examples/demo/client/wh_demo_client_auth.c | 33 ++++++ .../wh_posix_server/wh_posix_server_cfg.c | 48 ++++---- src/wh_auth_base.c | 107 +++++++++++++++++- wolfhsm/wh_auth_base.h | 13 +++ 4 files changed, 176 insertions(+), 25 deletions(-) diff --git a/examples/demo/client/wh_demo_client_auth.c b/examples/demo/client/wh_demo_client_auth.c index 74772808b..411b1afc4 100644 --- a/examples/demo/client/wh_demo_client_auth.c +++ b/examples/demo/client/wh_demo_client_auth.c @@ -381,11 +381,44 @@ static int wh_DemoClient_AuthUserSetPermissions(whClientContext* clientContext) } +/** + * Persistence verification: when the server uses NVM-backed auth, users survive + * restarts. Run the full demo once (adds demo user), then restart the server + * and run again. This check will detect the persisted demo user and print a + * verification message. + */ +static int wh_DemoClient_AuthPersistenceCheck(whClientContext* clientContext) +{ + int32_t serverRc = 0; + whUserId userId = WH_USER_ID_INVALID; + + /* Try to log in as demo user - if successful, user was persisted from a + * previous server run (NVM-backed auth) */ + int rc = wh_Client_AuthLogin(clientContext, WH_AUTH_METHOD_PIN, "demo", + "1234", 4, &serverRc, &userId); + if (serverRc == WH_AUTH_NOT_ENABLED) { + return WH_ERROR_OK; /* Auth not enabled, skip */ + } + if (rc == 0 && serverRc == 0) { + printf("[AUTH-DEMO] NVM persistence verified: demo user found from " + "previous server run\n"); + (void)wh_Client_AuthLogout(clientContext, userId, &serverRc); + } + return WH_ERROR_OK; +} + int wh_DemoClient_Auth(whClientContext* clientContext) { int rc = 0; printf("[AUTH-DEMO] Starting authentication demo...\n"); + + /* Check for persisted users from a previous server run (NVM-backed auth) */ + rc = wh_DemoClient_AuthPersistenceCheck(clientContext); + if (rc != WH_ERROR_OK) { + return rc; + } + rc = wh_DemoClient_AuthCertificate(clientContext); if (rc != WH_ERROR_OK) { return rc; 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 c826438f0..84c1b4a2d 100644 --- a/examples/posix/wh_posix_server/wh_posix_server_cfg.c +++ b/examples/posix/wh_posix_server/wh_posix_server_cfg.c @@ -685,14 +685,14 @@ static whAuthContext auth_ctx = {0}; */ 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; + int rc; + whServerConfig* s_conf = (whServerConfig*)conf; + static void* auth_backend_context = NULL; + static whAuthConfig auth_config = {0}; + static whAuthBaseConfig auth_base_config = {0}; + whAuthPermissions permissions; + uint16_t out_user_id; + int i; if (s_conf == NULL) { return WH_ERROR_BADARGS; @@ -701,6 +701,9 @@ int wh_PosixServer_ExampleAuthConfig(void* conf) /* Set up the auth config with default callbacks */ auth_config.cb = &default_auth_cb; auth_config.context = auth_backend_context; + /* NVM-backed user database: persist users across server restarts */ + auth_base_config.nvm = s_conf->nvm; + auth_config.config = &auth_base_config; /* Initialize the auth context */ rc = wh_Auth_Init(&auth_ctx, &auth_config); @@ -713,19 +716,22 @@ int wh_PosixServer_ExampleAuthConfig(void* conf) 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; + "Default auth context configured (NVM-backed user database)\n"); + + /* Add admin user only if not already present (e.g. from NVM load) */ + rc = wh_Auth_BaseUserGet(&auth_ctx, "admin", &out_user_id, &permissions); + if (rc == WH_ERROR_NOTFOUND) { + 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; diff --git a/src/wh_auth_base.c b/src/wh_auth_base.c index 9b49f7001..37b5632e7 100644 --- a/src/wh_auth_base.c +++ b/src/wh_auth_base.c @@ -36,6 +36,7 @@ #include "wolfhsm/wh_message.h" #include "wolfhsm/wh_message_auth.h" #include "wolfhsm/wh_auth_base.h" +#include "wolfhsm/wh_nvm.h" /* hash pin with use as credentials */ #ifndef WOLFHSM_CFG_NO_CRYPTO @@ -56,23 +57,116 @@ typedef struct whAuthBase_User { * wrapper functions in wh_auth.c. */ static whAuthBase_User users[WH_AUTH_BASE_MAX_USERS]; +/* NVM persistence: when non-NULL, user database is stored in NVM */ +static whNvmContext* s_auth_base_nvm = NULL; + +/* Serialization format: magic (4) + version (2) + users array */ +#define WH_AUTH_BASE_NVM_MAGIC 0x57484142u /* "WHAB" */ +#define WH_AUTH_BASE_NVM_VERSION 1 +#define WH_AUTH_BASE_NVM_HEADER_SIZE 6 +#define WH_AUTH_BASE_NVM_DATA_SIZE \ + (WH_AUTH_BASE_NVM_HEADER_SIZE + sizeof(whAuthBase_User) * WH_AUTH_BASE_MAX_USERS) + #if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO) #include #include #endif +/* Persist users array to NVM. Caller must hold auth lock. */ +static int wh_Auth_BasePersistToNvm(void) +{ + whNvmMetadata meta = {0}; + uint8_t buf[WH_AUTH_BASE_NVM_DATA_SIZE]; + int i; + + if (s_auth_base_nvm == NULL) { + return WH_ERROR_OK; + } + + /* Serialize: magic + version + users (clear is_active before storing) */ + ((uint32_t*)buf)[0] = WH_AUTH_BASE_NVM_MAGIC; + ((uint16_t*)(buf + 4))[0] = WH_AUTH_BASE_NVM_VERSION; + memcpy(buf + WH_AUTH_BASE_NVM_HEADER_SIZE, users, sizeof(users)); + /* Clear is_active in serialized copy - session state is not persisted */ + for (i = 0; i < WH_AUTH_BASE_MAX_USERS; i++) { + ((whAuthBase_User*)(buf + WH_AUTH_BASE_NVM_HEADER_SIZE))[i].user.is_active = false; + } + + meta.id = WH_NVM_ID_AUTH_USER_DB; + meta.access = WH_NVM_ACCESS_NONE; + meta.flags = WH_NVM_FLAGS_SENSITIVE; + meta.len = WH_AUTH_BASE_NVM_DATA_SIZE; + memset(meta.label, 0, sizeof(meta.label)); + memcpy(meta.label, "auth_user_db", 12); + + return wh_Nvm_AddObject(s_auth_base_nvm, &meta, WH_AUTH_BASE_NVM_DATA_SIZE, buf); +} + +/* Load users from NVM. Caller must hold auth lock. Returns WH_ERROR_OK on success. */ +static int wh_Auth_BaseLoadFromNvm(void) +{ + whNvmMetadata meta = {0}; + uint8_t buf[WH_AUTH_BASE_NVM_DATA_SIZE]; + int rc; + + if (s_auth_base_nvm == NULL) { + return WH_ERROR_OK; + } + + rc = wh_Nvm_GetMetadata(s_auth_base_nvm, WH_NVM_ID_AUTH_USER_DB, &meta); + if (rc == WH_ERROR_NOTFOUND) { + return WH_ERROR_OK; /* No stored data, keep empty */ + } + if (rc != WH_ERROR_OK) { + return rc; + } + if (meta.len != WH_AUTH_BASE_NVM_DATA_SIZE) { + return WH_ERROR_OK; /* Version mismatch, ignore */ + } + + rc = wh_Nvm_Read(s_auth_base_nvm, WH_NVM_ID_AUTH_USER_DB, 0, + WH_AUTH_BASE_NVM_DATA_SIZE, buf); + if (rc != WH_ERROR_OK) { + return rc; + } + + if (((uint32_t*)buf)[0] != WH_AUTH_BASE_NVM_MAGIC || + ((uint16_t*)(buf + 4))[0] != WH_AUTH_BASE_NVM_VERSION) { + return WH_ERROR_OK; /* Unknown format, start fresh */ + } + + memcpy(users, buf + WH_AUTH_BASE_NVM_HEADER_SIZE, sizeof(users)); + /* Ensure is_active is false after load (session state is not persisted) */ + { + int i; + for (i = 0; i < WH_AUTH_BASE_MAX_USERS; i++) { + users[i].user.is_active = false; + } + } + return WH_ERROR_OK; +} + int wh_Auth_BaseInit(void* context, const void* config) { (void)context; - (void)config; memset(users, 0, sizeof(users)); + s_auth_base_nvm = NULL; + + if (config != NULL) { + const whAuthBaseConfig* base_config = (const whAuthBaseConfig*)config; + if (base_config->nvm != NULL) { + s_auth_base_nvm = (whNvmContext*)base_config->nvm; + return wh_Auth_BaseLoadFromNvm(); + } + } return WH_ERROR_OK; } int wh_Auth_BaseCleanup(void* context) { (void)context; + s_auth_base_nvm = NULL; wh_Utils_ForceZero(users, sizeof(users)); return WH_ERROR_OK; } @@ -266,6 +360,7 @@ int wh_Auth_BaseUserAdd(void* context, const char* username, whAuthContext* auth_context = (whAuthContext*)context; whAuthBase_User* new_user; int i; + int rc; int userId = WH_USER_ID_INVALID; if (username == NULL || out_user_id == NULL) { @@ -362,8 +457,9 @@ int wh_Auth_BaseUserAdd(void* context, const char* username, } } + rc = wh_Auth_BasePersistToNvm(); (void)auth_context; - return WH_ERROR_OK; + return rc; } int wh_Auth_BaseUserDelete(void* context, uint16_t current_user_id, @@ -392,7 +488,7 @@ int wh_Auth_BaseUserDelete(void* context, uint16_t current_user_id, wh_Utils_ForceZero(user, sizeof(whAuthBase_User)); (void)context; - return WH_ERROR_OK; + return wh_Auth_BasePersistToNvm(); } int wh_Auth_BaseUserSetPermissions(void* context, uint16_t current_user_id, @@ -433,7 +529,7 @@ int wh_Auth_BaseUserSetPermissions(void* context, uint16_t current_user_id, } } (void)context; - return WH_ERROR_OK; + return wh_Auth_BasePersistToNvm(); } @@ -580,6 +676,9 @@ int wh_Auth_BaseUserSetCredentials(void* context, uint16_t current_user_id, } user->method = method; + if (rc == WH_ERROR_OK) { + rc = wh_Auth_BasePersistToNvm(); + } (void)auth_context; return rc; } diff --git a/wolfhsm/wh_auth_base.h b/wolfhsm/wh_auth_base.h index 0f966b3cd..a1b2fc7f8 100644 --- a/wolfhsm/wh_auth_base.h +++ b/wolfhsm/wh_auth_base.h @@ -33,6 +33,19 @@ #include "wolfhsm/wh_common.h" #include "wolfhsm/wh_auth.h" +/** NVM object ID for the auth user database (reserved, do not use for other objects) */ +#define WH_NVM_ID_AUTH_USER_DB ((whNvmId)0xFE00) + +/** + * @brief Configuration for the auth base implementation. + * + * When nvm is non-NULL and NVM is built in, the user database is persisted to NVM. + * When nvm is NULL, the user database remains in-memory only (lost on restart). + */ +typedef struct { + void* nvm; /**< NVM context (whNvmContext*) for persistent storage; NULL for in-memory only */ +} whAuthBaseConfig; + /** * @brief Initialize the auth base implementation. *