From 0170b93d0417a39e9836abb17ea8d8702455a92a Mon Sep 17 00:00:00 2001
From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com>
Date: Sat, 11 Oct 2025 12:45:27 -0600
Subject: [PATCH 1/5] Add global keys feature with comprehensive multi-client
testing and keywrap compatibility.
Key changes:
- Add WOLFHSM_CFG_GLOBAL_KEYS feature flag and configuration
- Implement global key cache in NVM context separate from local caches
- Add keyId translation layer between client flags and server encoding
- Create unified cache routing infrastructure for local/global keys
- Add comprehensive multi-client test suite with 15+ test cases
- Update all crypto/keystore operations to support global keys
- Major refactor to keywrap feature to add clientId-based access control and
addiional test coverage for wrap/unwrap scenarios with global and local keys
- Standardize client_id usage across benchmarks and tests
- Add new wh_keyid module for keyId manipulation helpers
---
benchmark/wh_bench.c | 9 +-
examples/demo/client/wh_demo_client_keywrap.c | 62 +-
examples/demo/client/wh_demo_client_keywrap.h | 3 +
.../posix/wh_posix_client/wh_posix_client.c | 9 +
examples/posix/wh_posix_client/wolfhsm_cfg.h | 1 +
.../posix/wh_posix_server/wh_posix_server.c | 1 -
examples/posix/wh_posix_server/wolfhsm_cfg.h | 4 +-
src/wh_client_keywrap.c | 9 +-
src/wh_keyid.c | 48 +
src/wh_nvm.c | 10 +
src/wh_server.c | 9 +
src/wh_server_cert.c | 9 +-
src/wh_server_crypto.c | 141 +-
src/wh_server_keystore.c | 687 +++++---
test/config/wolfhsm_cfg.h | 3 +
test/wh_test.c | 6 +-
test/wh_test_cert.c | 2 +-
test/wh_test_clientserver.c | 6 +-
test/wh_test_comm.c | 8 +-
test/wh_test_common.h | 1 +
test/wh_test_crypto.c | 25 +-
test/wh_test_keywrap.c | 43 +-
test/wh_test_keywrap.h | 1 -
test/wh_test_multiclient.c | 1531 +++++++++++++++++
test/wh_test_multiclient.h | 46 +
test/wh_test_she.c | 2 +-
test/wh_test_wolfcrypt_test.c | 2 +-
wolfhsm/wh_client.h | 98 ++
wolfhsm/wh_common.h | 49 +-
wolfhsm/wh_error.h | 4 +-
wolfhsm/wh_keyid.h | 100 ++
wolfhsm/wh_nvm.h | 6 +-
wolfhsm/wh_server.h | 16 +-
wolfhsm/wh_server_cache.h | 63 +
wolfhsm/wh_server_keystore.h | 21 +-
wolfhsm/wh_settings.h | 4 +
wolfhsm/wh_transport_mem.h | 4 +-
37 files changed, 2602 insertions(+), 441 deletions(-)
create mode 100644 src/wh_keyid.c
create mode 100644 test/wh_test_multiclient.c
create mode 100644 test/wh_test_multiclient.h
create mode 100644 wolfhsm/wh_keyid.h
create mode 100644 wolfhsm/wh_server_cache.h
diff --git a/benchmark/wh_bench.c b/benchmark/wh_bench.c
index 2dc4eefef..66bddfed8 100644
--- a/benchmark/wh_bench.c
+++ b/benchmark/wh_bench.c
@@ -52,6 +52,9 @@
#if defined(WOLFHSM_CFG_BENCH_ENABLE)
+/* Default client ID for benchmarks */
+#define WH_BENCH_CLIENT_ID (1)
+
/* Buffer sizes for transport */
/* Large enough to handle an RSA 4096 key */
#define BUFFER_SIZE \
@@ -815,7 +818,7 @@ static whCommClientConfig g_mem_cc_conf = {
.transport_cb = &g_mem_tccb,
.transport_context = (void*)&g_mem_tmcc,
.transport_config = (void*)&g_mem_tmcf,
- .client_id = 123,
+ .client_id = WH_BENCH_CLIENT_ID,
};
static whTransportServerCb g_mem_tscb = WH_TRANSPORT_MEM_SERVER_CB;
@@ -867,7 +870,7 @@ static int _configureClientTransport(whBenchTransportType transport,
.transport_cb = pttcClientShmCb,
.transport_context = (void*)&tccShm,
.transport_config = (void*)&myshmconfig,
- .client_id = 12,
+ .client_id = WH_BENCH_CLIENT_ID,
};
memset(&tccShm, 0, sizeof(posixTransportShmClientContext));
@@ -887,7 +890,7 @@ static int _configureClientTransport(whBenchTransportType transport,
.transport_cb = &pttcClientTcpCb,
.transport_context = (void*)&tccTcp,
.transport_config = (void*)&mytcpconfig,
- .client_id = 12,
+ .client_id = WH_BENCH_CLIENT_ID,
};
memset(&tccTcp, 0, sizeof(posixTransportTcpClientContext));
diff --git a/examples/demo/client/wh_demo_client_keywrap.c b/examples/demo/client/wh_demo_client_keywrap.c
index 92e1fa251..eebb382c9 100644
--- a/examples/demo/client/wh_demo_client_keywrap.c
+++ b/examples/demo/client/wh_demo_client_keywrap.c
@@ -35,13 +35,13 @@
#ifdef WOLFHSM_CFG_KEYWRAP
-#define WH_TEST_KEKID 1
+#define WH_DEMO_KEYWRAP_KEKID 1
static int _InitServerKek(whClientContext* ctx)
{
/* IMPORTANT NOTE: Server KEK is typically intrinsic or set during
* provisioning. Uploading the KEK via the client is for testing purposes
* only and not intended as a recommendation */
- whKeyId serverKeyId = WH_TEST_KEKID;
+ whKeyId serverKeyId = WH_DEMO_KEYWRAP_KEKID;
whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE;
uint8_t label[WH_NVM_LABEL_LEN] = "Server KEK key";
uint8_t kek[] = {0x03, 0x03, 0x0d, 0xd9, 0xeb, 0x18, 0x17, 0x2e,
@@ -55,43 +55,44 @@ static int _InitServerKek(whClientContext* ctx)
static int _CleanupServerKek(whClientContext* ctx)
{
- return wh_Client_KeyErase(ctx, WH_TEST_KEKID);
+ return wh_Client_KeyErase(ctx, WH_DEMO_KEYWRAP_KEKID);
}
#ifndef NO_AES
#ifdef HAVE_AESGCM
-#define WH_TEST_AES_KEYSIZE 16
-#define WH_TEST_AES_TEXTSIZE 16
-#define WH_TEST_AES_IVSIZE 12
-#define WH_TEST_AES_TAGSIZE 16
-#define WH_TEST_AES_WRAPPED_KEYSIZE \
- (WH_TEST_AES_IVSIZE + WH_TEST_AES_TAGSIZE + WH_TEST_AES_KEYSIZE + \
- sizeof(whNvmMetadata))
-#define WH_TEST_AESGCM_WRAPKEY_ID 8
+#define WH_DEMO_KEYWRAP_AES_KEYSIZE 16
+#define WH_DEMO_KEYWRAP_AES_TEXTSIZE 16
+#define WH_DEMO_KEYWRAP_AES_IVSIZE 12
+#define WH_DEMO_KEYWRAP_AES_TAGSIZE 16
+#define WH_DEMO_KEYWRAP_AES_WRAPPED_KEYSIZE \
+ (WH_DEMO_KEYWRAP_AES_IVSIZE + WH_DEMO_KEYWRAP_AES_TAGSIZE + \
+ WH_DEMO_KEYWRAP_AES_KEYSIZE + sizeof(whNvmMetadata))
+#define WH_DEMO_KEYWRAP_AESGCM_WRAPKEY_ID 8
int wh_DemoClient_AesGcmKeyWrap(whClientContext* client)
{
int ret = 0;
Aes aes[1];
WC_RNG rng[1];
- uint8_t key[WH_TEST_AES_KEYSIZE];
- uint8_t exportedKey[WH_TEST_AES_KEYSIZE];
+ uint8_t key[WH_DEMO_KEYWRAP_AES_KEYSIZE];
+ uint8_t exportedKey[WH_DEMO_KEYWRAP_AES_KEYSIZE];
whNvmMetadata metadata = {
- .id = WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, 0, WH_TEST_AESGCM_WRAPKEY_ID),
- .label = "AES Key Label",
+ .id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(
+ client->comm->client_id, WH_DEMO_KEYWRAP_AESGCM_WRAPKEY_ID),
+ .label = "AES Key Label",
.access = WH_NVM_ACCESS_ANY,
- .len = WH_TEST_AES_KEYSIZE};
+ .len = WH_DEMO_KEYWRAP_AES_KEYSIZE};
whNvmMetadata exportedMetadata;
- uint8_t wrappedKey[WH_TEST_AES_WRAPPED_KEYSIZE];
+ uint8_t wrappedKey[WH_DEMO_KEYWRAP_AES_WRAPPED_KEYSIZE];
whKeyId wrappedKeyId;
const uint8_t plaintext[] = "hello, wolfSSL AES-GCM!";
uint8_t ciphertext[sizeof(plaintext)];
uint8_t decrypted[sizeof(plaintext)];
- uint8_t tag[WH_TEST_AES_TAGSIZE];
- uint8_t iv[WH_TEST_AES_IVSIZE];
+ uint8_t tag[WH_DEMO_KEYWRAP_AES_TAGSIZE];
+ uint8_t iv[WH_DEMO_KEYWRAP_AES_IVSIZE];
const uint8_t aad[] = {0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe,
0xef, 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad,
0xbe, 0xef, 0xab, 0xad, 0xda, 0xd2};
@@ -127,8 +128,8 @@ int wh_DemoClient_AesGcmKeyWrap(whClientContext* client)
/* Now we request the server to wrap the key using the KEK we
* establish above in the first step. */
- ret = wh_Client_KeyWrap(client, WC_CIPHER_AES_GCM, WH_TEST_KEKID, key,
- sizeof(key), &metadata, wrappedKey,
+ ret = wh_Client_KeyWrap(client, WC_CIPHER_AES_GCM, WH_DEMO_KEYWRAP_KEKID,
+ key, sizeof(key), &metadata, wrappedKey,
sizeof(wrappedKey));
if (ret != 0) {
printf("Failed to wh_Client_KeyWrap %d\n", ret);
@@ -144,9 +145,9 @@ int wh_DemoClient_AesGcmKeyWrap(whClientContext* client)
/* Request the server to unwrap and cache the wrapped key we just created.
* This will provide us back a key ID that the client can use to do crypto
* operations */
- ret = wh_Client_KeyUnwrapAndCache(client, WC_CIPHER_AES_GCM, WH_TEST_KEKID,
- wrappedKey, sizeof(wrappedKey),
- &wrappedKeyId);
+ ret = wh_Client_KeyUnwrapAndCache(client, WC_CIPHER_AES_GCM,
+ WH_DEMO_KEYWRAP_KEKID, wrappedKey,
+ sizeof(wrappedKey), &wrappedKeyId);
if (ret != 0) {
printf("Failed to wh_Client_KeyUnwrapAndCache %d\n", ret);
goto cleanup_rng;
@@ -161,7 +162,8 @@ int wh_DemoClient_AesGcmKeyWrap(whClientContext* client)
/* Set the key id for this AES context to the wrapped key ID that the server
* provided us */
- ret = wh_Client_AesSetKeyId(aes, wrappedKeyId);
+ ret =
+ wh_Client_AesSetKeyId(aes, WH_CLIENT_KEYID_MAKE_WRAPPED(wrappedKeyId));
if (ret != 0) {
printf("Failed to wh_Client_AesSetKeyId %d\n", ret);
goto cleanup_aes;
@@ -207,12 +209,12 @@ int wh_DemoClient_AesGcmKeyWrap(whClientContext* client)
/* Exporting a wrapped key */
/* Request the server to unwrap and export the wrapped key we created */
- ret = wh_Client_KeyUnwrapAndExport(client, WC_CIPHER_AES_GCM, WH_TEST_KEKID,
- wrappedKey, sizeof(wrappedKey),
- &exportedMetadata, exportedKey,
- sizeof(exportedKey));
+ ret = wh_Client_KeyUnwrapAndExport(client, WC_CIPHER_AES_GCM,
+ WH_DEMO_KEYWRAP_KEKID, wrappedKey,
+ sizeof(wrappedKey), &exportedMetadata,
+ exportedKey, sizeof(exportedKey));
if (ret != 0) {
- printf("Failed to wh_Client_KeyUnwrapAndCache %d\n", ret);
+ printf("Failed to wh_Client_KeyUnwrapAndExport %d\n", ret);
goto cleanup_aes;
}
diff --git a/examples/demo/client/wh_demo_client_keywrap.h b/examples/demo/client/wh_demo_client_keywrap.h
index ec2475fac..84842302c 100644
--- a/examples/demo/client/wh_demo_client_keywrap.h
+++ b/examples/demo/client/wh_demo_client_keywrap.h
@@ -3,6 +3,9 @@
#include "wolfhsm/wh_client.h"
+/* Exposed in header so the demo server can obtain the ID for registration */
+#define WH_DEMO_KEYWRAP_AESGCM_WRAPKEY_ID 8
+
int wh_DemoClient_KeyWrap(whClientContext* clientContext);
#endif /* !DEMO_CLIENT_KEYWRAP_H_ */
diff --git a/examples/posix/wh_posix_client/wh_posix_client.c b/examples/posix/wh_posix_client/wh_posix_client.c
index 9e98b5da0..15d0fced4 100644
--- a/examples/posix/wh_posix_client/wh_posix_client.c
+++ b/examples/posix/wh_posix_client/wh_posix_client.c
@@ -69,6 +69,14 @@ static int wh_ClientTask(void* cf, const char* type, int test)
ret = wh_Client_Init(client, config);
+ if (ret == 0) {
+ ret = wh_Client_CommInit(client, NULL, NULL);
+ if (ret != 0) {
+ printf("Failed to initialize client communication\n");
+ return -1;
+ }
+ }
+
if (strcmp(type, "dma") == 0) {
#ifdef WOLFSSL_STATIC_MEMORY
printf("Setting up DMA heap with static memory buckets\n");
@@ -85,6 +93,7 @@ static int wh_ClientTask(void* cf, const char* type, int test)
printf("Client connecting to server...\n");
if (ret == 0 && test) {
+ printf("Running client demos...\n");
return wh_DemoClient_All(client);
}
diff --git a/examples/posix/wh_posix_client/wolfhsm_cfg.h b/examples/posix/wh_posix_client/wolfhsm_cfg.h
index 35e01b274..4f5153a03 100644
--- a/examples/posix/wh_posix_client/wolfhsm_cfg.h
+++ b/examples/posix/wh_posix_client/wolfhsm_cfg.h
@@ -30,5 +30,6 @@
#define WOLFHSM_CFG_HEXDUMP
#define WOLFHSM_CFG_COMM_DATA_LEN 5000
#define WOLFHSM_CFG_KEYWRAP
+#define WOLFHSM_CFG_GLOBAL_KEYS
#endif /* WOLFHSM_CFG_H_ */
diff --git a/examples/posix/wh_posix_server/wh_posix_server.c b/examples/posix/wh_posix_server/wh_posix_server.c
index dc97c24cf..adc9a183f 100644
--- a/examples/posix/wh_posix_server/wh_posix_server.c
+++ b/examples/posix/wh_posix_server/wh_posix_server.c
@@ -118,7 +118,6 @@ static int loadAndStoreKeys(whServerContext* server, whKeyId* outKeyId,
return ret;
}
-
static int wh_ServerTask(void* cf, const char* keyFilePath, int keyId,
int clientId)
{
diff --git a/examples/posix/wh_posix_server/wolfhsm_cfg.h b/examples/posix/wh_posix_server/wolfhsm_cfg.h
index 6ff18ecac..fd4b33db1 100644
--- a/examples/posix/wh_posix_server/wolfhsm_cfg.h
+++ b/examples/posix/wh_posix_server/wolfhsm_cfg.h
@@ -44,9 +44,11 @@
#define WOLFHSM_CFG_CERTIFICATE_MANAGER
#define WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT
+#define WOLFHSM_CFG_KEYWRAP
#define WOLFHSM_CFG_KEYWRAP_MAX_KEY_SIZE 5000
+#define WOLFHSM_CFG_GLOBAL_KEYS
+
#define XMEMFENCE() __atomic_thread_fence(__ATOMIC_SEQ_CST)
-#define WOLFHSM_CFG_KEYWRAP
#endif /* WOLFHSM_CFG_H_ */
diff --git a/src/wh_client_keywrap.c b/src/wh_client_keywrap.c
index 0d126b8a1..8945c20fc 100644
--- a/src/wh_client_keywrap.c
+++ b/src/wh_client_keywrap.c
@@ -185,13 +185,16 @@ int wh_Client_KeyUnwrapAndExportResponse(whClientContext* ctx,
if (group != WH_MESSAGE_GROUP_KEY || action != WH_KEY_UNWRAPEXPORT ||
size < sizeof(*resp) ||
size > sizeof(*resp) + sizeof(*metadataOut) + keySz ||
- resp->keySz != keySz || resp->cipherType != cipherType) {
+ resp->cipherType != cipherType) {
return WH_ERROR_ABORTED;
}
- if (resp->rc != 0) {
+ if (resp->rc != WH_ERROR_OK) {
return resp->rc;
}
+ else if (resp->keySz != keySz) {
+ return WH_ERROR_BUFFER_SIZE;
+ }
/* Copy the metadata and key from the response data into metadataOut and
* keyOut */
@@ -298,6 +301,8 @@ int wh_Client_KeyUnwrapAndCacheResponse(whClientContext* ctx,
return resp->rc;
}
+ /* Server returns ID portion only. Client must track ownership
+ * and specify appropriate flags when later using the key. */
*keyIdOut = resp->keyId;
return WH_ERROR_OK;
diff --git a/src/wh_keyid.c b/src/wh_keyid.c
new file mode 100644
index 000000000..c2427ced0
--- /dev/null
+++ b/src/wh_keyid.c
@@ -0,0 +1,48 @@
+/*
+ * 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_keyid.c
+ *
+ * KeyId helper function implementations for wolfHSM
+ */
+
+#include "wolfhsm/wh_keyid.h"
+
+whKeyId wh_KeyId_TranslateClient(uint16_t type, uint16_t clientId,
+ whKeyId reqId)
+{
+ uint16_t user = clientId;
+ whKeyId id = reqId & WH_KEYID_MASK;
+
+#ifdef WOLFHSM_CFG_GLOBAL_KEYS
+ /* Check for global flag (bit 8: 0x0100) */
+ if ((reqId & 0x0100) != 0) {
+ user = WH_KEYUSER_GLOBAL;
+ }
+#endif
+
+#ifdef WOLFHSM_CFG_KEYWRAP
+ /* Check for wrapped flag (bit 9: 0x0200) */
+ if ((reqId & 0x0200) != 0) {
+ type = WH_KEYTYPE_WRAPPED;
+ }
+#endif
+
+ return WH_MAKE_KEYID(type, user, id);
+}
diff --git a/src/wh_nvm.c b/src/wh_nvm.c
index 2ea7d7c82..c91667562 100644
--- a/src/wh_nvm.c
+++ b/src/wh_nvm.c
@@ -46,6 +46,11 @@ int wh_Nvm_Init(whNvmContext* context, const whNvmConfig *config)
context->cb = config->cb;
context->context = config->context;
+#if !defined(WOLFHSM_CFG_NO_CRYPTO) && defined(WOLFHSM_CFG_GLOBAL_KEYS)
+ /* Initialize the global key cache */
+ memset(&context->globalCache, 0, sizeof(context->globalCache));
+#endif
+
if (context->cb->Init != NULL) {
rc = context->cb->Init(context->context, config->config);
if (rc != 0) {
@@ -64,6 +69,11 @@ int wh_Nvm_Cleanup(whNvmContext* context)
return WH_ERROR_BADARGS;
}
+#if !defined(WOLFHSM_CFG_NO_CRYPTO) && defined(WOLFHSM_CFG_GLOBAL_KEYS)
+ /* Clear the global key cache */
+ memset(&context->globalCache, 0, sizeof(context->globalCache));
+#endif
+
/* No callback? Return ABORTED */
if (context->cb->Cleanup == NULL) {
return WH_ERROR_ABORTED;
diff --git a/src/wh_server.c b/src/wh_server.c
index c2da38972..a21c48152 100644
--- a/src/wh_server.c
+++ b/src/wh_server.c
@@ -188,8 +188,17 @@ static int _wh_Server_HandleCommRequest(whServerContext* server,
wh_MessageComm_TranslateInitRequest(magic,
(whMessageCommInitRequest*)req_packet, &req);
+#ifdef WOLFHSM_CFG_GLOBAL_KEYS
+ /* USER=0 is reserved for global keys, client_id must be non-zero */
+ if (req.client_id == WH_KEYUSER_GLOBAL) {
+ *out_resp_size = 0;
+ return WH_ERROR_BADARGS;
+ }
+#endif
+
/* Process the init action */
server->comm->client_id = req.client_id;
+
resp.client_id = server->comm->client_id;
resp.server_id = server->comm->server_id;
diff --git a/src/wh_server_cert.c b/src/wh_server_cert.c
index 583e8d10d..cc66a8842 100644
--- a/src/wh_server_cert.c
+++ b/src/wh_server_cert.c
@@ -122,8 +122,9 @@ static int _verifyChainAgainstCmStore(whServerContext* server,
/* Grab the cache slot and dump the public key from the cert
* into it */
- rc = wh_Server_KeystoreGetCacheSlot(server, cacheBufSize,
- &cacheBuf, &cacheMeta);
+ rc = wh_Server_KeystoreGetCacheSlot(server, *inout_keyId,
+ cacheBufSize, &cacheBuf,
+ &cacheMeta);
if (rc == WH_ERROR_OK) {
rc = wc_GetSubjectPubKeyInfoDerFromCert(
cert_ptr, cert_len + idx, cacheBuf, &cacheBufSize);
@@ -488,7 +489,7 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic,
cert_data = (const uint8_t*)req_packet + sizeof(req);
/* Map client keyId to server keyId space */
- whKeyId keyId = WH_MAKE_KEYID(
+ whKeyId keyId = wh_KeyId_TranslateClient(
WH_KEYTYPE_CRYPTO, server->comm->client_id, req.keyId);
/* Process the verify action */
@@ -617,7 +618,7 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic,
}
if (resp.rc == WH_ERROR_OK) {
/* Map client keyId to server keyId space */
- whKeyId keyId = WH_MAKE_KEYID(
+ whKeyId keyId = wh_KeyId_TranslateClient(
WH_KEYTYPE_CRYPTO, server->comm->client_id, req.keyId);
/* Process the verify action */
diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c
index 80b386d6a..48a1626a3 100644
--- a/src/wh_server_crypto.c
+++ b/src/wh_server_crypto.c
@@ -221,7 +221,8 @@ int wh_Server_CacheImportRsaKey(whServerContext* ctx, RsaKey* key,
}
/* get a free slot */
- ret = wh_Server_KeystoreGetCacheSlot(ctx, max_size, &cacheBuf, &cacheMeta);
+ ret = wh_Server_KeystoreGetCacheSlot(ctx, keyId, max_size, &cacheBuf,
+ &cacheMeta);
if (ret == 0) {
ret = wh_Crypto_RsaSerializeKeyDer(key, max_size, cacheBuf, &der_size);
}
@@ -286,8 +287,8 @@ static int _HandleRsaKeyGen(whServerContext* ctx, uint16_t magic,
long e = req.e;
/* Force incoming key_id to have current user/type */
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
whNvmFlags flags = req.flags;
uint8_t* label = req.label;
uint32_t label_size = WH_NVM_LABEL_LEN;
@@ -385,8 +386,8 @@ static int _HandleRsaFunction( whServerContext* ctx, uint16_t magic,
int op_type = (int)(req.opType);
uint32_t options = req.options;
int evict = !!(options & WH_MESSAGE_CRYPTO_RSA_OPTIONS_EVICT);
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
word32 in_len = (word32)(req.inLen);
word32 out_len = (word32)(req.outLen);
/* in and out are after the fixed size fields */
@@ -470,8 +471,8 @@ static int _HandleRsaGetSize(whServerContext* ctx, uint16_t magic,
}
/* Extract parameters from translated request */
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
uint32_t options = req.options;
int evict = !!(options & WH_MESSAGE_CRYPTO_RSA_GET_SIZE_OPTIONS_EVICT);
@@ -533,7 +534,8 @@ int wh_Server_EccKeyCacheImport(whServerContext* ctx, ecc_key* key,
return WH_ERROR_BADARGS;
}
/* get a free slot */
- ret = wh_Server_KeystoreGetCacheSlot(ctx, max_size, &cacheBuf, &cacheMeta);
+ ret = wh_Server_KeystoreGetCacheSlot(ctx, keyId, max_size, &cacheBuf,
+ &cacheMeta);
if (ret == WH_ERROR_OK) {
ret = wh_Crypto_EccSerializeKeyDer(key, max_size, cacheBuf, &der_size);
}
@@ -597,7 +599,7 @@ int wh_Server_CacheImportCurve25519Key(whServerContext* server,
/* if successful, find a free cache slot and copy in the key data */
if (ret == 0) {
- ret = wh_Server_KeystoreGetCacheSlot(server, keySz, &cacheBuf,
+ ret = wh_Server_KeystoreGetCacheSlot(server, keyId, keySz, &cacheBuf,
&cacheMeta);
if (ret == 0) {
memcpy(cacheBuf, der_buf, keySz);
@@ -664,8 +666,8 @@ int wh_Server_MlDsaKeyCacheImport(whServerContext* ctx, MlDsaKey* key,
return WH_ERROR_BADARGS;
}
- ret = wh_Server_KeystoreGetCacheSlot(ctx, MAX_MLDSA_DER_SIZE, &cacheBuf,
- &cacheMeta);
+ ret = wh_Server_KeystoreGetCacheSlot(ctx, keyId, MAX_MLDSA_DER_SIZE,
+ &cacheBuf, &cacheMeta);
if (ret == WH_ERROR_OK) {
ret = wh_Crypto_MlDsaSerializeKeyDer(key, MAX_MLDSA_DER_SIZE, cacheBuf,
&der_size);
@@ -736,8 +738,8 @@ static int _HandleEccKeyGen(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
int key_size = req.sz;
int curve_id = req.curveId;
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
whNvmFlags flags = req.flags;
uint8_t* label = req.label;
uint16_t label_size = WH_NVM_LABEL_LEN;
@@ -839,10 +841,10 @@ static int _HandleEccSharedSecret(whServerContext* ctx, uint16_t magic,
uint32_t options = req.options;
int evict_pub = !!(options & WH_MESSAGE_CRYPTO_ECDH_OPTIONS_EVICTPUB);
int evict_prv = !!(options & WH_MESSAGE_CRYPTO_ECDH_OPTIONS_EVICTPRV);
- whKeyId pub_key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.publicKeyId);
- whKeyId prv_key_id = WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id,
- req.privateKeyId);
+ whKeyId pub_key_id = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.publicKeyId);
+ whKeyId prv_key_id = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.privateKeyId);
/* Response message */
byte* res_out =
@@ -917,8 +919,8 @@ static int _HandleEccSign(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
uint8_t* in =
(uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_EccSignRequest);
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
word32 in_len = req.sz;
uint32_t options = req.options;
int evict = !!(options & WH_MESSAGE_CRYPTO_ECCSIGN_OPTIONS_EVICT);
@@ -989,8 +991,8 @@ static int _HandleEccVerify(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
uint32_t options = req.options;
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
uint32_t hash_len = req.hashSz;
uint32_t sig_len = req.sigSz;
uint8_t* req_sig =
@@ -1166,7 +1168,8 @@ int wh_Server_HkdfKeyCacheImport(whServerContext* ctx, const uint8_t* keyData,
}
/* Get a free slot */
- ret = wh_Server_KeystoreGetCacheSlot(ctx, keySize, &cacheBuf, &cacheMeta);
+ ret = wh_Server_KeystoreGetCacheSlot(ctx, keyId, keySize, &cacheBuf,
+ &cacheMeta);
if (ret == WH_ERROR_OK) {
/* Copy the key data to cache buffer */
memcpy(cacheBuf, keyData, keySize);
@@ -1210,10 +1213,10 @@ static int _HandleHkdf(whServerContext* ctx, uint16_t magic,
uint32_t saltSz = req.saltSz;
uint32_t infoSz = req.infoSz;
uint32_t outSz = req.outSz;
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyIdOut);
- whKeyId keyIdIn =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyIdIn);
+ whKeyId key_id = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyIdOut);
+ whKeyId keyIdIn = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyIdIn);
whNvmFlags flags = req.flags;
uint8_t* label = req.label;
uint16_t label_size = WH_NVM_LABEL_LEN;
@@ -1332,8 +1335,8 @@ static int _HandleCurve25519KeyGen(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
int key_size = req.sz;
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
whNvmFlags flags = req.flags;
uint8_t* label = req.label;
uint16_t label_size = WH_NVM_LABEL_LEN;
@@ -1426,12 +1429,10 @@ static int _HandleCurve25519SharedSecret(whServerContext* ctx, uint16_t magic,
uint32_t options = req.options;
int evict_pub = !!(options & WH_MESSAGE_CRYPTO_CURVE25519_OPTIONS_EVICTPUB);
int evict_prv = !!(options & WH_MESSAGE_CRYPTO_CURVE25519_OPTIONS_EVICTPRV);
- whKeyId pub_key_id = WH_MAKE_KEYID( WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id,
- req.publicKeyId);
- whKeyId prv_key_id = WH_MAKE_KEYID( WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id,
- req.privateKeyId);
+ whKeyId pub_key_id = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.publicKeyId);
+ whKeyId prv_key_id = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.privateKeyId);
int endian = req.endian;
/* Response message */
@@ -1516,8 +1517,8 @@ static int _HandleAesCtr(whServerContext* ctx, uint16_t magic,
if (needed_size > inSize) {
return WH_ERROR_BADARGS;
}
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
/* in, key, iv, and out are after fixed size fields */
uint8_t* in =
(uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_AesCtrRequest);
@@ -1633,8 +1634,8 @@ static int _HandleAesEcb(whServerContext* ctx, uint16_t magic,
return WH_ERROR_BADARGS;
}
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
/* in, key, iv, and out are after fixed size fields */
uint8_t* in =
@@ -1741,8 +1742,8 @@ static int _HandleAesCbc(whServerContext* ctx, uint16_t magic, const void* crypt
return WH_ERROR_BADARGS;
}
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
/* in, key, iv, and out are after fixed size fields */
uint8_t* in =
@@ -1850,8 +1851,8 @@ static int _HandleAesGcm(whServerContext* ctx, uint16_t magic,
uint32_t iv_len = req.ivSz;
uint32_t authin_len = req.authInSz;
uint32_t tag_len = req.authTagSz;
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
/* in, key, iv, authin, tag, and out are after fixed size fields */
uint8_t* in = (uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_AesGcmRequest);
@@ -2074,8 +2075,8 @@ static int _HandleAesGcmDma(whServerContext* ctx, uint16_t magic, uint16_t seq,
/* Handle keyId-based keys if no direct key was provided */
if (ret == WH_ERROR_OK && req.key.sz == 0) {
- keyId =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ keyId = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
keyLen = sizeof(tmpKey);
ret = wh_Server_KeystoreReadKey(ctx, keyId, NULL, tmpKey, &keyLen);
if (ret == WH_ERROR_OK) {
@@ -2221,8 +2222,8 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq,
* cache slot until CmacFinal() is called, at which point we evict the
* struct from the cache. TODO: client should hold CMAC state */
len = sizeof(ctx->crypto->algoCtx.cmac);
- keyId = WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id,
- req.keyId);
+ keyId = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
ret = wh_Server_KeystoreReadKey(
ctx, keyId, NULL, (uint8_t*)ctx->crypto->algoCtx.cmac,
(uint32_t*)&len);
@@ -2298,8 +2299,9 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq,
if (!WH_KEYID_ISERASED(keyId)) {
/* Don't override return value except on failure */
int tmpRet = wh_Server_KeystoreEvictKey(
- ctx, WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, keyId));
+ ctx,
+ wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, keyId));
if (tmpRet != 0) {
ret = tmpRet;
}
@@ -2310,8 +2312,9 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq,
/* Handle cancellation - evict key and abandon state */
if (!WH_KEYID_ISERASED(req.keyId)) {
wh_Server_KeystoreEvictKey(
- ctx, WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId));
+ ctx, wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id,
+ req.keyId));
}
}
#endif
@@ -2327,9 +2330,8 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq,
return ret;
}
else {
- keyId = WH_MAKE_KEYID( WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id,
- req.keyId);
+ keyId = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
}
/* evict the aes sized key in the normal cache */
if (moveToBigCache == 1) {
@@ -2714,8 +2716,8 @@ static int _HandleMlDsaKeyGen(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
int key_size = req.sz;
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
int level = req.level;
whNvmFlags flags = req.flags;
uint8_t* label = req.label;
@@ -2828,8 +2830,8 @@ static int _HandleMlDsaSign(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
byte* in = (uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_MlDsaSignRequest);
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
word32 in_len = req.sz;
uint32_t options = req.options;
int evict = !!(options & WH_MESSAGE_CRYPTO_MLDSA_SIGN_OPTIONS_EVICT);
@@ -2908,8 +2910,8 @@ static int _HandleMlDsaVerify(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
uint32_t options = req.options;
- whKeyId key_id =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
uint32_t hash_len = req.hashSz;
uint32_t sig_len = req.sigSz;
byte* req_sig =
@@ -3909,9 +3911,8 @@ static int _HandleMlDsaKeyGenDma(whServerContext* ctx, uint16_t magic,
else {
/* Must import the key into the cache and return keyid
*/
- whKeyId keyId =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId keyId = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
if (WH_KEYID_ISERASED(keyId)) {
/* Generate a new id */
@@ -3999,7 +4000,8 @@ static int _HandleMlDsaSignDma(whServerContext* ctx, uint16_t magic,
/* Get key ID and evict flag */
- key_id = WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO, ctx->comm->client_id,
+ req.keyId);
evict = !!(req.options & WH_MESSAGE_CRYPTO_MLDSA_SIGN_OPTIONS_EVICT);
/* Initialize key */
@@ -4107,7 +4109,8 @@ static int _HandleMlDsaVerifyDma(whServerContext* ctx, uint16_t magic,
int evict = 0;
/* Get key ID and evict flag */
- key_id = WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO, ctx->comm->client_id,
+ req.keyId);
evict = !!(req.options & WH_MESSAGE_CRYPTO_MLDSA_VERIFY_OPTIONS_EVICT);
/* Initialize key */
@@ -4376,9 +4379,9 @@ static int _HandleCmacDma(whServerContext* ctx, uint16_t magic, uint16_t seq,
* that has been initialized to use a keyId by
* reference. We need to load the key from cache and
* initialize a new context with it */
- keyId =
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, clientKeyId);
+ keyId = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id,
+ clientKeyId);
keyLen = sizeof(tmpKey);
/* Load key from cache */
@@ -4441,8 +4444,8 @@ static int _HandleCmacDma(whServerContext* ctx, uint16_t magic, uint16_t seq,
whNvmId nvmId = WH_DEVCTX_TO_KEYID(cmac->devCtx);
if (nvmId != WH_KEYID_ERASED) {
/* Get key ID from CMAC context */
- keyId = WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, nvmId);
+ keyId = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, nvmId);
keyLen = sizeof(tmpKey);
/* Load key from cache */
diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c
index 51e7a45c2..941543573 100644
--- a/src/wh_server_keystore.c
+++ b/src/wh_server_keystore.c
@@ -52,121 +52,277 @@ static int _FindInCache(whServerContext* server, whKeyId keyId, int* out_index,
int* out_big, uint8_t** out_buffer,
whNvmMetadata** out_meta);
+#ifdef WOLFHSM_CFG_GLOBAL_KEYS
+/*
+ * @brief Check if keyId represents a global key (USER == 0)
+ */
+static int _IsGlobalKey(whKeyId keyId)
+{
+ return (WH_KEYID_USER(keyId) == WH_KEYUSER_GLOBAL);
+}
+#endif /* WOLFHSM_CFG_GLOBAL_KEYS */
-int wh_Server_KeystoreGetUniqueId(whServerContext* server, whNvmId* inout_id)
+/*
+ * @brief Get the appropriate cache context based on keyId
+ *
+ * When WOLFHSM_CFG_GLOBAL_KEYS is enabled, routes to global cache if keyId
+ * has USER == 0, otherwise routes to local cache. When disabled, always
+ * routes to local cache.
+ */
+static whKeyCacheContext* _GetCacheContext(whServerContext* server,
+ whKeyId keyId)
{
- int i;
- int ret = 0;
- whNvmId id;
- /* apply client_id and type which should be set by caller on outId */
- whKeyId key_id = *inout_id;
- int type = WH_KEYID_TYPE(key_id);
- int user = WH_KEYID_USER(key_id);
- whNvmId buildId;
- whNvmId nvmId = 0;
- whNvmId keyCount;
+#ifdef WOLFHSM_CFG_GLOBAL_KEYS
+ if (_IsGlobalKey(keyId)) {
+ return &server->nvm->globalCache;
+ }
+#else
+ (void)keyId;
+#endif
+ return &server->localCache;
+}
- /* try every index until we find a unique one, don't worry about capacity */
- for (id = WH_KEYID_IDMAX; id > WH_KEYID_ERASED; id--) {
- buildId = WH_MAKE_KEYID(type, user, id);
- /* check against cache keys */
- for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; i++) {
- if (buildId == server->cache[i].meta->id)
- break;
+/**
+ * @brief Find a key in the specified cache context
+ */
+static int _FindInKeyCache(whKeyCacheContext* ctx, whKeyId keyId,
+ int* out_index, int* out_big, uint8_t** out_buffer,
+ whNvmMetadata** out_meta)
+{
+ int ret = WH_ERROR_NOTFOUND;
+ int i;
+ int index = -1;
+ int big = -1;
+ whNvmMetadata* meta = NULL;
+ uint8_t* buffer = NULL;
+
+ /* Search regular cache */
+ for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; i++) {
+ if (ctx->cache[i].meta->id == keyId) {
+ big = 0;
+ index = i;
+ meta = ctx->cache[i].meta;
+ buffer = ctx->cache[i].buffer;
+ break;
}
- /* try again if match */
- if (i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT)
- continue;
- /* check against big cache keys */
+ }
+
+ /* Search big cache if not found */
+ if (index == -1) {
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT; i++) {
- if (buildId == server->bigCache[i].meta->id)
+ if (ctx->bigCache[i].meta->id == keyId) {
+ big = 1;
+ index = i;
+ meta = ctx->bigCache[i].meta;
+ buffer = ctx->bigCache[i].buffer;
break;
+ }
}
- /* try again if match */
- if (i < WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT)
- continue;
- /* if keyId exists */
- ret = wh_Nvm_List(server->nvm, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_ANY,
- buildId, &keyCount, &nvmId);
- /* break if we didn't find a match */
- if (ret == WH_ERROR_NOTFOUND || nvmId != buildId)
- break;
}
- /* unlikely but cover the case where we've run out of ids */
- if (id > WH_KEYID_IDMAX)
- ret = WH_ERROR_NOSPACE;
- /* ultimately, return found id */
- if (ret == 0)
- *inout_id = buildId;
+
+ /* Set output parameters if found */
+ if (index != -1) {
+ if (out_index != NULL)
+ *out_index = index;
+ if (out_big != NULL)
+ *out_big = big;
+ if (out_meta != NULL)
+ *out_meta = meta;
+ if (out_buffer != NULL)
+ *out_buffer = buffer;
+ ret = WH_ERROR_OK;
+ }
+
return ret;
}
-/* find an available slot for the size, return the slots buffer and meta */
-int wh_Server_KeystoreGetCacheSlot(whServerContext* server, uint16_t keySz,
- uint8_t** outBuf, whNvmMetadata** outMeta)
+/**
+ * @brief Get an available cache slot from the specified cache context
+ */
+static int _GetKeyCacheSlot(whKeyCacheContext* ctx, uint16_t keySz,
+ uint8_t** outBuf, whNvmMetadata** outMeta)
{
- int i;
int foundIndex = -1;
- if (server == NULL || (keySz > WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE &&
- keySz > WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE)) {
+ int i;
+
+ if (ctx == NULL || outBuf == NULL || outMeta == NULL) {
return WH_ERROR_BADARGS;
}
+ /* Determine which cache to use based on key size */
if (keySz <= WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE) {
+ /* Search regular cache for empty slot */
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; i++) {
- /* check for empty slot or rewrite slot */
- if (foundIndex == -1 &&
- server->cache[i].meta->id == WH_KEYID_ERASED) {
+ if (ctx->cache[i].meta->id == WH_KEYID_ERASED) {
foundIndex = i;
break;
}
}
- /* if no empty slots, check for a commited key we can evict */
+
+ /* If no empty slots, find committed key to evict */
if (foundIndex == -1) {
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; i++) {
- if (server->cache[i].commited == 1) {
+ if (ctx->cache[i].committed == 1) {
foundIndex = i;
break;
}
}
}
- /* zero the cache slot and set the output buffers */
+ /* Zero slot and return pointers */
if (foundIndex >= 0) {
- memset(&server->cache[foundIndex], 0, sizeof(whServerCacheSlot));
- *outBuf = server->cache[foundIndex].buffer;
- *outMeta = server->cache[foundIndex].meta;
+ memset(&ctx->cache[foundIndex], 0, sizeof(whServerCacheSlot));
+ *outBuf = ctx->cache[foundIndex].buffer;
+ *outMeta = ctx->cache[foundIndex].meta;
}
}
else {
+ /* Search big cache for empty slot */
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT; i++) {
- /* check for empty slot or rewrite slot */
- if (foundIndex == -1 &&
- server->bigCache[i].meta->id == WH_KEYID_ERASED) {
+ if (ctx->bigCache[i].meta->id == WH_KEYID_ERASED) {
foundIndex = i;
break;
}
}
- /* if no empty slots, check for a commited key we can evict */
+
+ /* If no empty slots, find committed key to evict */
if (foundIndex == -1) {
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT; i++) {
- if (server->bigCache[i].commited == 1) {
+ if (ctx->bigCache[i].committed == 1) {
foundIndex = i;
break;
}
}
}
+
+ /* Zero slot and return pointers */
if (foundIndex >= 0) {
- memset(&server->bigCache[foundIndex], 0,
- sizeof(whServerBigCacheSlot));
- *outBuf = server->bigCache[foundIndex].buffer;
- *outMeta = server->bigCache[foundIndex].meta;
+ memset(&ctx->bigCache[foundIndex], 0, sizeof(whServerBigCacheSlot));
+ *outBuf = ctx->bigCache[foundIndex].buffer;
+ *outMeta = ctx->bigCache[foundIndex].meta;
}
}
- /* return error if we are out of cache slots */
- if (foundIndex == -1)
+
+ if (foundIndex == -1) {
return WH_ERROR_NOSPACE;
- return 0;
+ }
+
+ return WH_ERROR_OK;
+}
+
+/**
+ * @brief Evict a key from the specified cache context
+ */
+static int _EvictKeyFromCache(whKeyCacheContext* ctx, whKeyId keyId)
+{
+ whNvmMetadata* meta = NULL;
+ int ret = _FindInKeyCache(ctx, keyId, NULL, NULL, NULL, &meta);
+
+ if (ret == WH_ERROR_OK && meta != NULL) {
+ meta->id = WH_KEYID_ERASED;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Mark a cached key as committed
+ */
+static int _MarkKeyCommitted(whKeyCacheContext* ctx, whKeyId keyId,
+ int committed)
+{
+ int index = -1;
+ int big = -1;
+ int ret = _FindInKeyCache(ctx, keyId, &index, &big, NULL, NULL);
+
+ if (ret == WH_ERROR_OK) {
+ if (big == 0) {
+ ctx->cache[index].committed = committed;
+ }
+ else {
+ ctx->bigCache[index].committed = committed;
+ }
+ }
+
+ return ret;
+}
+
+int wh_Server_KeystoreGetUniqueId(whServerContext* server, whNvmId* inout_id)
+{
+ int ret = WH_ERROR_OK;
+ int found = 0;
+ whNvmId id;
+ /* apply client_id and type which should be set by caller on outId */
+ whKeyId key_id = *inout_id;
+ int type = WH_KEYID_TYPE(key_id);
+ int user = WH_KEYID_USER(key_id);
+ whNvmId buildId;
+ whNvmId nvmId = 0;
+ whNvmId keyCount;
+
+ whKeyCacheContext* ctx = _GetCacheContext(server, key_id);
+
+ /* Wrapped keys must be provisioned with explicit identifiers */
+ if (type == WH_KEYTYPE_WRAPPED) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* try every index until we find a unique one, don't worry about capacity */
+ for (id = WH_KEYID_IDMAX; id > WH_KEYID_ERASED; id--) {
+ /* id loop var is not an input client ID so we don't need to handle the
+ * global case */
+ buildId = WH_MAKE_KEYID(type, user, id);
+
+ /* Check against cache keys using unified cache functions */
+ ret = _FindInKeyCache(ctx, buildId, NULL, NULL, NULL, NULL);
+ if (ret == WH_ERROR_OK) {
+ /* Found in cache, try next ID */
+ continue;
+ }
+ else if (ret != WH_ERROR_NOTFOUND) {
+ return ret;
+ }
+
+ /* Check if keyId exists in NVM */
+ ret = wh_Nvm_List(server->nvm, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_ANY,
+ buildId, &keyCount, &nvmId);
+ if (ret == WH_ERROR_NOTFOUND || nvmId != buildId) {
+ /* key doesn't exist in NVM, we found a candidate ID */
+ found = 1;
+ break;
+ }
+
+ if (ret != WH_ERROR_OK) {
+ return ret;
+ }
+ }
+
+ if (!found) {
+ return WH_ERROR_NOSPACE;
+ }
+
+ /* Return found id */
+ *inout_id = buildId;
+ return WH_ERROR_OK;
+}
+
+/* find an available slot for the size, return the slots buffer and meta */
+int wh_Server_KeystoreGetCacheSlot(whServerContext* server, whKeyId keyId,
+ uint16_t keySz, uint8_t** outBuf,
+ whNvmMetadata** outMeta)
+{
+ whKeyCacheContext* ctx;
+
+ if (server == NULL || (keySz > WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE &&
+ keySz > WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Get the appropriate cache context for this key */
+ ctx = _GetCacheContext(server, keyId);
+
+ /* Use the unified cache slot function */
+ return _GetKeyCacheSlot(ctx, keySz, outBuf, outMeta);
}
int wh_Server_KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta,
@@ -174,6 +330,7 @@ int wh_Server_KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta,
{
int i;
int foundIndex = -1;
+ whKeyCacheContext* ctx;
/* make sure id is valid */
if ((server == NULL) || (meta == NULL) || (in == NULL) ||
@@ -183,13 +340,16 @@ int wh_Server_KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta,
return WH_ERROR_BADARGS;
}
+ /* Get the appropriate cache context for this key */
+ ctx = _GetCacheContext(server, meta->id);
+
/* Check for cross-cache duplicates and evict from other cache if found */
if (meta->len <= WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE) {
/* We're going to use regular cache, check if key exists in big cache */
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT; i++) {
- if (server->bigCache[i].meta->id == meta->id) {
+ if (ctx->bigCache[i].meta->id == meta->id) {
/* Evict the key from big cache */
- server->bigCache[i].meta->id = WH_KEYID_ERASED;
+ ctx->bigCache[i].meta->id = WH_KEYID_ERASED;
break;
}
}
@@ -197,9 +357,9 @@ int wh_Server_KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta,
else {
/* We're going to use big cache, check if key exists in regular cache */
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; i++) {
- if (server->cache[i].meta->id == meta->id) {
+ if (ctx->cache[i].meta->id == meta->id) {
/* Evict the key from regular cache */
- server->cache[i].meta->id = WH_KEYID_ERASED;
+ ctx->cache[i].meta->id = WH_KEYID_ERASED;
break;
}
}
@@ -209,17 +369,17 @@ int wh_Server_KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta,
if (meta->len <= WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE) {
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; i++) {
/* check for empty slot or rewrite slot */
- if (WH_KEYID_ISERASED(server->cache[i].meta->id) ||
- (server->cache[i].meta->id == meta->id)) {
+ if (WH_KEYID_ISERASED(ctx->cache[i].meta->id) ||
+ (ctx->cache[i].meta->id == meta->id)) {
foundIndex = i;
break;
}
}
- /* if no empty slots, check for a commited key we can evict */
+ /* if no empty slots, check for a committed key we can evict */
if (foundIndex == -1) {
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; i++) {
- if (server->cache[i].commited == 1) {
+ if (ctx->cache[i].committed == 1) {
foundIndex = i;
break;
}
@@ -228,16 +388,16 @@ int wh_Server_KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta,
/* write key if slot found */
if (foundIndex != -1) {
- memcpy((uint8_t*)server->cache[foundIndex].buffer, in, meta->len);
- memcpy((uint8_t*)server->cache[foundIndex].meta, (uint8_t*)meta,
+ memcpy((uint8_t*)ctx->cache[foundIndex].buffer, in, meta->len);
+ memcpy((uint8_t*)ctx->cache[foundIndex].meta, (uint8_t*)meta,
sizeof(whNvmMetadata));
- /* check if the key is already commited */
+ /* check if the key is already committed */
if (wh_Nvm_GetMetadata(server->nvm, meta->id, meta) ==
WH_ERROR_NOTFOUND) {
- server->cache[foundIndex].commited = 0;
+ ctx->cache[foundIndex].committed = 0;
}
else {
- server->cache[foundIndex].commited = 1;
+ ctx->cache[foundIndex].committed = 1;
}
#if defined(DEBUG_CRYPTOCB) && defined(DEBUG_CRYPTOCB_VERBOSE)
printf("[server] cacheKey: caching keyid=%u\n", meta->id);
@@ -249,17 +409,17 @@ int wh_Server_KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta,
/* try big key cache, don't put small keys into big cache if full */
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT; i++) {
/* check for empty slot or rewrite slot */
- if (WH_KEYID_ISERASED(server->bigCache[i].meta->id) ||
- (server->bigCache[i].meta->id == meta->id)) {
+ if (WH_KEYID_ISERASED(ctx->bigCache[i].meta->id) ||
+ (ctx->bigCache[i].meta->id == meta->id)) {
foundIndex = i;
break;
}
}
- /* if no empty slots, check for a commited key we can evict */
+ /* if no empty slots, check for a committed key we can evict */
if (foundIndex == -1) {
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT; i++) {
- if (server->bigCache[i].commited == 1) {
+ if (ctx->bigCache[i].committed == 1) {
foundIndex = i;
break;
}
@@ -268,17 +428,16 @@ int wh_Server_KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta,
/* write key if slot found */
if (foundIndex != -1) {
- memcpy((uint8_t*)server->bigCache[foundIndex].buffer, in,
- meta->len);
- memcpy((uint8_t*)server->bigCache[foundIndex].meta, (uint8_t*)meta,
+ memcpy((uint8_t*)ctx->bigCache[foundIndex].buffer, in, meta->len);
+ memcpy((uint8_t*)ctx->bigCache[foundIndex].meta, (uint8_t*)meta,
sizeof(whNvmMetadata));
- /* check if the key is already commited */
+ /* check if the key is already committed */
if (wh_Nvm_GetMetadata(server->nvm, meta->id, meta) ==
WH_ERROR_NOTFOUND) {
- server->bigCache[foundIndex].commited = 0;
+ ctx->bigCache[foundIndex].committed = 0;
}
else {
- server->bigCache[foundIndex].commited = 1;
+ ctx->bigCache[foundIndex].committed = 1;
}
}
}
@@ -299,49 +458,9 @@ static int _FindInCache(whServerContext* server, whKeyId keyId, int* out_index,
int* out_big, uint8_t** out_buffer,
whNvmMetadata** out_meta)
{
- int ret = WH_ERROR_NOTFOUND;
- int i;
- int index = -1;
- int big = -1;
- whNvmMetadata* meta = NULL;
- uint8_t* buffer = NULL;
-
- for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; i++) {
- if (server->cache[i].meta->id == keyId) {
- big = 0;
- index = i;
- meta = server->cache[i].meta;
- buffer = server->cache[i].buffer;
- break;
- }
- }
- if (index == -1) {
- for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT; i++) {
- if (server->bigCache[i].meta->id == keyId) {
- big = 1;
- index = i;
- meta = server->bigCache[i].meta;
- buffer = server->bigCache[i].buffer;
- break;
- }
- }
- }
- if (index != -1) {
- if (out_index != NULL) {
- *out_index = index;
- }
- if (out_big != NULL) {
- *out_big = big;
- }
- if (out_meta != NULL) {
- *out_meta = meta;
- }
- if (out_buffer != NULL) {
- *out_buffer = buffer;
- }
- ret = WH_ERROR_OK;
- }
- return ret;
+ whKeyCacheContext* ctx = _GetCacheContext(server, keyId);
+ return _FindInKeyCache(ctx, keyId, out_index, out_big, out_buffer,
+ out_meta);
}
#ifdef WOLFHSM_CFG_KEYWRAP
@@ -364,7 +483,26 @@ static int _ExistsInCache(whServerContext* server, whKeyId keyId)
/* Key exists in the cache */
return 1;
}
+#endif /* WOLFHSM_CFG_KEYWRAP */
+#ifdef WOLFHSM_CFG_KEYWRAP
+int wh_Server_KeystoreIsWrappedKey(whServerContext* server, whKeyId keyId,
+ int* outIsWrapped)
+{
+ int isWrapped;
+
+ if (server == NULL || WH_KEYID_ISERASED(keyId)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ (void)server;
+ isWrapped = (WH_KEYID_TYPE(keyId) == WH_KEYTYPE_WRAPPED);
+ if (outIsWrapped != NULL) {
+ *outIsWrapped = isWrapped;
+ }
+
+ return WH_ERROR_OK;
+}
#endif /* WOLFHSM_CFG_KEYWRAP */
/* try to put the specified key into cache if it isn't already, return pointers
@@ -381,6 +519,11 @@ int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId,
return WH_ERROR_BADARGS;
}
+ /* Reject attempts to freshen wrapped keys from NVM */
+ if (WH_KEYID_TYPE(keyId) == WH_KEYTYPE_WRAPPED) {
+ return WH_ERROR_ABORTED;
+ }
+
ret = _FindInCache(server, keyId, &foundIndex, &foundBigIndex, outBuf,
outMeta);
if (ret != WH_ERROR_OK) {
@@ -388,8 +531,8 @@ int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId,
ret = wh_Nvm_GetMetadata(server->nvm, keyId, tmpMeta);
if (ret == WH_ERROR_OK) {
/* Key found in NVM, get a free cache slot */
- ret = wh_Server_KeystoreGetCacheSlot(server, tmpMeta->len, outBuf,
- outMeta);
+ ret = wh_Server_KeystoreGetCacheSlot(server, keyId, tmpMeta->len,
+ outBuf, outMeta);
if (ret == WH_ERROR_OK) {
/* Read the key from NVM into the cache slot */
ret = wh_Nvm_Read(server->nvm, keyId, 0, tmpMeta->len, *outBuf);
@@ -405,13 +548,16 @@ int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId,
return ret;
}
+/* Reads key from cache or NVM. If keyId is a wrapped key will attempt to read
+ * from cache but NOT from NVM */
int wh_Server_KeystoreReadKey(whServerContext* server, whKeyId keyId,
whNvmMetadata* outMeta, uint8_t* out,
uint32_t* outSz)
{
- int ret = 0;
- int i;
- whNvmMetadata meta[1];
+ int ret = 0;
+ whNvmMetadata meta[1];
+ whNvmMetadata* cacheMeta = NULL;
+ uint8_t* cacheBuffer = NULL;
if ((server == NULL) || (outSz == NULL) ||
(WH_KEYID_ISERASED(keyId) &&
@@ -419,45 +565,29 @@ int wh_Server_KeystoreReadKey(whServerContext* server, whKeyId keyId,
return WH_ERROR_BADARGS;
}
- /* check the cache */
- for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; i++) {
- /* copy the meta and key before returning */
- if (server->cache[i].meta->id == keyId) {
- /* check outSz */
- if (server->cache[i].meta->len > *outSz)
- return WH_ERROR_NOSPACE;
- if (outMeta != NULL) {
- memcpy((uint8_t*)outMeta, (uint8_t*)server->cache[i].meta,
- sizeof(whNvmMetadata));
- }
- if (out != NULL) {
- memcpy(out, server->cache[i].buffer,
- server->cache[i].meta->len);
- }
- *outSz = server->cache[i].meta->len;
- return 0;
+ /* Check the cache using unified function */
+ ret = _FindInCache(server, keyId, NULL, NULL, &cacheBuffer, &cacheMeta);
+ if (ret == WH_ERROR_OK) {
+ /* Found in cache */
+ if (cacheMeta->len > *outSz)
+ return WH_ERROR_NOSPACE;
+ if (outMeta != NULL) {
+ memcpy((uint8_t*)outMeta, (uint8_t*)cacheMeta,
+ sizeof(whNvmMetadata));
}
- }
- /* check the big cache */
- for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT; i++) {
- /* copy the meta and key before returning */
- if (server->bigCache[i].meta->id == keyId) {
- /* check outSz */
- if (server->bigCache[i].meta->len > *outSz)
- return WH_ERROR_NOSPACE;
- if (outMeta != NULL) {
- memcpy((uint8_t*)outMeta, (uint8_t*)server->bigCache[i].meta,
- sizeof(whNvmMetadata));
- }
- if (out != NULL) {
- memcpy(out, server->bigCache[i].buffer,
- server->bigCache[i].meta->len);
- }
- *outSz = server->bigCache[i].meta->len;
- return 0;
+ if (out != NULL) {
+ memcpy(out, cacheBuffer, cacheMeta->len);
}
+ *outSz = cacheMeta->len;
+ return 0;
}
- /* try to read the metadata */
+
+ /* Prevent exposing wrapped blobs through the unwrapped read path */
+ if (WH_KEYID_TYPE(keyId) == WH_KEYTYPE_WRAPPED) {
+ return WH_ERROR_NOTFOUND;
+ }
+
+ /* Not in cache, try to read the metadata from NVM */
ret = wh_Nvm_GetMetadata(server->nvm, keyId, meta);
if (ret == 0) {
/* set outSz */
@@ -469,7 +599,7 @@ int wh_Server_KeystoreReadKey(whServerContext* server, whKeyId keyId,
if (out != NULL)
ret = wh_Nvm_Read(server->nvm, keyId, 0, *outSz, out);
}
- /* cache key if free slot, will only kick out other commited keys */
+ /* cache key if free slot, will only kick out other committed keys */
if (ret == 0 && out != NULL) {
(void)wh_Server_KeystoreCacheKey(server, meta, out);
}
@@ -494,48 +624,56 @@ int wh_Server_KeystoreReadKey(whServerContext* server, whKeyId keyId,
int wh_Server_KeystoreEvictKey(whServerContext* server, whNvmId keyId)
{
- int ret = 0;
- whNvmMetadata* meta;
+ int ret = 0;
+ whKeyCacheContext* ctx;
if ((server == NULL) || WH_KEYID_ISERASED(keyId)) {
return WH_ERROR_BADARGS;
}
- ret = _FindInCache(server, keyId, NULL, NULL, NULL, &meta);
- if (ret == 0) {
+ /* Get the appropriate cache context for this key */
+ ctx = _GetCacheContext(server, keyId);
+
+ /* Use the unified evict function */
+ ret = _EvictKeyFromCache(ctx, keyId);
+
#if defined(DEBUG_CRYPTOCB) && defined(DEBUG_CRYPTOCB_VERBOSE)
+ if (ret == 0) {
printf("[server] wh_Server_KeystoreEvictKey: evicted keyid=0x%X\n",
keyId);
-#endif
- meta->id = WH_KEYID_ERASED;
}
+#endif
+
return ret;
}
int wh_Server_KeystoreCommitKey(whServerContext* server, whNvmId keyId)
{
- uint8_t* slotBuf;
- whNvmMetadata* slotMeta;
- whNvmSize size;
- int ret;
- int index;
- int big;
+ uint8_t* slotBuf;
+ whNvmMetadata* slotMeta;
+ whNvmSize size;
+ int ret;
+ whKeyCacheContext* ctx;
if ((server == NULL) || WH_KEYID_ISERASED(keyId)) {
return WH_ERROR_BADARGS;
}
- ret = _FindInCache(server, keyId, &index, &big, &slotBuf, &slotMeta);
+ if (WH_KEYID_TYPE(keyId) == WH_KEYTYPE_WRAPPED) {
+ return WH_ERROR_ABORTED;
+ }
+
+ /* Get the appropriate cache context for this key */
+ ctx = _GetCacheContext(server, keyId);
+
+ /* Find the key in the appropriate cache context obtained above. */
+ ret = _FindInKeyCache(ctx, keyId, NULL, NULL, &slotBuf, &slotMeta);
if (ret == WH_ERROR_OK) {
size = slotMeta->len;
ret = wh_Nvm_AddObjectWithReclaim(server->nvm, slotMeta, size, slotBuf);
if (ret == 0) {
- if (big == 0) {
- server->cache[index].commited = 1;
- }
- else {
- server->bigCache[index].commited = 1;
- }
+ /* Mark key as committed using unified function */
+ (void)_MarkKeyCommitted(ctx, keyId, 1);
}
}
return ret;
@@ -547,6 +685,10 @@ int wh_Server_KeystoreEraseKey(whServerContext* server, whNvmId keyId)
return WH_ERROR_BADARGS;
}
+ if (WH_KEYID_TYPE(keyId) == WH_KEYTYPE_WRAPPED) {
+ return WH_ERROR_ABORTED;
+ }
+
/* remove the key from the cache if present */
(void)wh_Server_KeystoreEvictKey(server, keyId);
@@ -555,6 +697,7 @@ int wh_Server_KeystoreEraseKey(whServerContext* server, whNvmId keyId)
}
#ifdef WOLFHSM_CFG_KEYWRAP
+
#ifndef NO_AES
#ifdef HAVE_AESGCM
@@ -590,7 +733,8 @@ static int _AesGcmWrapKey(whServerContext* server, whKeyId serverKeyId,
/* Get the server side key */
ret = wh_Server_KeystoreReadKey(
server,
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, server->comm->client_id, serverKeyId),
+ wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO, server->comm->client_id,
+ serverKeyId),
NULL, serverKey, &serverKeySz);
if (ret != WH_ERROR_OK) {
return ret;
@@ -662,7 +806,8 @@ static int _AesGcmUnwrapKey(whServerContext* server, uint16_t serverKeyId,
/* Get the server side key */
ret = wh_Server_KeystoreReadKey(
server,
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, server->comm->client_id, serverKeyId),
+ wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO, server->comm->client_id,
+ serverKeyId),
NULL, serverKey, &serverKeySz);
if (ret != WH_ERROR_OK) {
return ret;
@@ -728,6 +873,11 @@ static int _HandleWrapKeyRequest(whServerContext* server,
memcpy(&metadata, reqData, sizeof(metadata));
memcpy(key, reqData + sizeof(metadata), req->keySz);
+ /* Ensure the keyId in the wrapped metadata has the wrapped flag set */
+ if (!WH_KEYID_ISWRAPPED(metadata.id)) {
+ return WH_ERROR_BADARGS;
+ }
+
/* Store the wrapped key in the response data */
wrappedKey = respData;
@@ -804,6 +954,10 @@ static int _HandleUnwrapAndExportKeyRequest(
req->wrappedKeySz - WOLFHSM_KEYWRAP_AES_GCM_IV_SIZE -
WOLFHSM_KEYWRAP_AES_GCM_TAG_SIZE - sizeof(*metadata);
+ resp->cipherType = WC_CIPHER_AES_GCM;
+ /* Key size is only passed back to the client on success */
+ resp->keySz = 0;
+
/* Check if the response data can fit the metadata + key */
if (respDataSz < sizeof(*metadata) + keySz) {
return WH_ERROR_BUFFER_SIZE;
@@ -816,15 +970,36 @@ static int _HandleUnwrapAndExportKeyRequest(
return ret;
}
+ /* Ensure unwrapped metadata has the wrapped flag set */
+ if (!WH_KEYID_ISWRAPPED(metadata->id)) {
+ return WH_ERROR_ABORTED;
+ }
+
/* Check if the key is exportable */
if (metadata->flags & WH_NVM_FLAGS_NONEXPORTABLE) {
return WH_ERROR_ACCESS;
}
- /* Tell the client how big the key is */
- resp->keySz = keySz;
- resp->cipherType = WC_CIPHER_AES_GCM;
+ /* Validate client ownership.
+ * The USER field in wrapped key metadata specifies the owner.
+ * Only the owning client can export wrapped keys. */
+ uint16_t keyUser = WH_KEYID_USER(metadata->id);
+#ifdef WOLFHSM_CFG_GLOBAL_KEYS
+ /* Global keys (USER=0) can be exported by any client */
+ if (keyUser != WH_KEYUSER_GLOBAL &&
+ keyUser != server->comm->client_id) {
+ return WH_ERROR_ACCESS;
+ }
+#else
+ /* Without global keys, USER must match requesting client */
+ if (keyUser != server->comm->client_id) {
+ return WH_ERROR_ACCESS;
+ }
+#endif /* WOLFHSM_CFG_GLOBAL_KEYS */
+
+ /* Tell the client how big the key is on success */
+ resp->keySz = keySz;
} break;
#endif /* HAVE_AESGCM */
#endif /* !NO_AES */
@@ -854,7 +1029,7 @@ _HandleUnwrapAndCacheKeyRequest(whServerContext* server,
int ret;
uint8_t* wrappedKey;
- whNvmMetadata metadata;
+ whNvmMetadata metadata = {0};
uint16_t keySz = 0;
uint8_t key[WOLFHSM_CFG_KEYWRAP_MAX_KEY_SIZE];
@@ -873,6 +1048,7 @@ _HandleUnwrapAndCacheKeyRequest(whServerContext* server,
case WC_CIPHER_AES_GCM: {
keySz = req->wrappedKeySz - WOLFHSM_KEYWRAP_AES_GCM_IV_SIZE -
WOLFHSM_KEYWRAP_AES_GCM_TAG_SIZE - sizeof(metadata);
+ resp->cipherType = WC_CIPHER_AES_GCM;
ret = _AesGcmUnwrapKey(server, req->serverKeyId, wrappedKey,
req->wrappedKeySz, &metadata, key, keySz);
@@ -880,8 +1056,6 @@ _HandleUnwrapAndCacheKeyRequest(whServerContext* server,
return ret;
}
- resp->cipherType = WC_CIPHER_AES_GCM;
- resp->keyId = metadata.id;
} break;
#endif /* HAVE_AESGCM */
@@ -895,15 +1069,49 @@ _HandleUnwrapAndCacheKeyRequest(whServerContext* server,
return WH_ERROR_BADARGS;
}
- /* Check if this key already exists in the cache */
+ /* Dynamic keyId generation for wrapped keys is not allowed */
+ if (WH_KEYID_ISERASED(metadata.id)) {
+ /* Wrapped keys must use explicit identifiers */
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Extract ownership from unwrapped metadata (preserves original owner) */
+ uint16_t wrappedKeyUser = WH_KEYID_USER(metadata.id);
+ uint16_t wrappedKeyType = WH_KEYID_TYPE(metadata.id);
+
+ /* Require explicit wrapped-key encoding */
+ if (wrappedKeyType != WH_KEYTYPE_WRAPPED) {
+ return WH_ERROR_ABORTED;
+ }
+
+ /* Validate ownership: USER field must match requesting client.
+ * The USER field specifies who owns this wrapped key. */
+#ifdef WOLFHSM_CFG_GLOBAL_KEYS
+ /* Global keys (USER=0): any client can unwrap and cache to global cache
+ * Local keys (USER!=0): only owning client can unwrap and cache */
+ if (wrappedKeyUser != WH_KEYUSER_GLOBAL &&
+ wrappedKeyUser != server->comm->client_id) {
+ return WH_ERROR_ACCESS;
+ }
+#else
+ /* Without global keys, USER must match requesting client */
+ if (wrappedKeyUser != server->comm->client_id) {
+ return WH_ERROR_ACCESS;
+ }
+#endif /* WOLFHSM_CFG_GLOBAL_KEYS */
+
+ /* Ensure a key with the unwrapped ID does not already exist in cache */
if (_ExistsInCache(server, metadata.id)) {
return WH_ERROR_ABORTED;
}
+ /* Store the assigned key ID in the response (ID portion only). We should
+ * NOT return the upper bits back to the client */
+ resp->keyId = WH_KEYID_ID(metadata.id);
+
/* Cache the key */
return wh_Server_KeystoreCacheKey(server, &metadata, key);
}
-
#endif /* WOLFHSM_CFG_KEYWRAP */
int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
@@ -937,8 +1145,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
in = (uint8_t*)req_packet + sizeof(req);
/* set the metadata fields */
- meta->id = WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, server->comm->client_id,
- req.id);
+ meta->id = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, server->comm->client_id, req.id);
meta->access = WH_NVM_ACCESS_ANY;
meta->flags = req.flags;
meta->len = req.sz;
@@ -986,8 +1194,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
magic, (whMessageKeystore_CacheDmaRequest*)req_packet, &req);
/* set the metadata fields */
- meta->id = WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, server->comm->client_id,
- req.id);
+ meta->id = wh_KeyId_TranslateClient(
+ WH_KEYTYPE_CRYPTO, server->comm->client_id, req.id);
meta->access = WH_NVM_ACCESS_ANY;
meta->flags = req.flags;
meta->len = req.key.sz;
@@ -1038,8 +1246,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
ret = wh_Server_KeystoreExportKeyDma(
server,
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, server->comm->client_id,
- req.id),
+ wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id, req.id),
req.key.addr, req.key.sz, meta);
resp.rc = ret;
/* propagate bad address to client if DMA operation failed */
@@ -1071,8 +1279,9 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
magic, (whMessageKeystore_EvictRequest*)req_packet, &req);
ret = wh_Server_KeystoreEvictKey(
- server, WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO,
- server->comm->client_id, req.id));
+ server,
+ wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id, req.id));
resp.rc = ret;
/* TODO: Are there any fatal server errors? */
ret = WH_ERROR_OK;
@@ -1103,8 +1312,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
/* read the key */
ret = wh_Server_KeystoreReadKey(
server,
- WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, server->comm->client_id,
- req.id),
+ wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id, req.id),
meta, out, &keySz);
/* Check if key is non-exportable */
@@ -1146,8 +1355,9 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
magic, (whMessageKeystore_CommitRequest*)req_packet, &req);
ret = wh_Server_KeystoreCommitKey(
- server, WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO,
- server->comm->client_id, req.id));
+ server,
+ wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id, req.id));
resp.rc = ret;
/* TODO: Are there any fatal server errors? */
ret = WH_ERROR_OK;
@@ -1172,8 +1382,9 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
magic, (whMessageKeystore_EraseRequest*)req_packet, &req);
ret = wh_Server_KeystoreEraseKey(
- server, WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO,
- server->comm->client_id, req.id));
+ server,
+ wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id, req.id));
resp.rc = ret;
/* TODO: Are there any fatal server errors? */
ret = WH_ERROR_OK;
@@ -1216,10 +1427,9 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
respData =
(uint8_t*)resp_packet + sizeof(whMessageKeystore_WrapResponse);
- ret = _HandleWrapKeyRequest(server, &wrapReq, reqData, reqDataSz,
- &wrapResp, respData, respDataSz);
- wrapResp.rc = ret;
-
+ wrapResp.rc =
+ _HandleWrapKeyRequest(server, &wrapReq, reqData, reqDataSz,
+ &wrapResp, respData, respDataSz);
(void)wh_MessageKeystore_TranslateWrapResponse(magic, &wrapResp,
resp_packet);
*out_resp_size = sizeof(wrapResp) + wrapResp.wrappedKeySz;
@@ -1252,10 +1462,9 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
respData = (uint8_t*)resp_packet +
sizeof(whMessageKeystore_UnwrapAndExportResponse);
- ret = _HandleUnwrapAndExportKeyRequest(server, &unwrapReq, reqData,
- reqDataSz, &unwrapResp,
- respData, respDataSz);
- unwrapResp.rc = ret;
+ unwrapResp.rc = _HandleUnwrapAndExportKeyRequest(
+ server, &unwrapReq, reqData, reqDataSz, &unwrapResp, respData,
+ respDataSz);
(void)wh_MessageKeystore_TranslateUnwrapAndExportResponse(
magic, &unwrapResp, resp_packet);
@@ -1289,10 +1498,9 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
respData = (uint8_t*)resp_packet +
sizeof(whMessageKeystore_UnwrapAndCacheResponse);
- ret = _HandleUnwrapAndCacheKeyRequest(server, &cacheReq, reqData,
- reqDataSz, &cacheResp,
- respData, respDataSz);
- cacheResp.rc = ret;
+ cacheResp.rc = _HandleUnwrapAndCacheKeyRequest(
+ server, &cacheReq, reqData, reqDataSz, &cacheResp, respData,
+ respDataSz);
(void)wh_MessageKeystore_TranslateUnwrapAndCacheResponse(
magic, &cacheResp, resp_packet);
@@ -1315,18 +1523,22 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
int wh_Server_KeystoreCacheKeyDma(whServerContext* server, whNvmMetadata* meta,
uint64_t keyAddr)
{
- int ret;
- uint8_t* buffer;
- whNvmMetadata* slotMeta;
- int i;
+ int ret;
+ uint8_t* buffer;
+ whNvmMetadata* slotMeta;
+ int i;
+ whKeyCacheContext* ctx;
+
+ /* Get the appropriate cache context for this key */
+ ctx = _GetCacheContext(server, meta->id);
/* Check for cross-cache duplicates and evict from other cache if found */
if (meta->len <= WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE) {
/* We're going to use regular cache, check if key exists in big cache */
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT; i++) {
- if (server->bigCache[i].meta->id == meta->id) {
+ if (ctx->bigCache[i].meta->id == meta->id) {
/* Evict the key from big cache */
- server->bigCache[i].meta->id = WH_KEYID_ERASED;
+ ctx->bigCache[i].meta->id = WH_KEYID_ERASED;
break;
}
}
@@ -1334,16 +1546,17 @@ int wh_Server_KeystoreCacheKeyDma(whServerContext* server, whNvmMetadata* meta,
else {
/* We're going to use big cache, check if key exists in regular cache */
for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; i++) {
- if (server->cache[i].meta->id == meta->id) {
+ if (ctx->cache[i].meta->id == meta->id) {
/* Evict the key from regular cache */
- server->cache[i].meta->id = WH_KEYID_ERASED;
+ ctx->cache[i].meta->id = WH_KEYID_ERASED;
break;
}
}
}
/* Get a cache slot */
- ret = wh_Server_KeystoreGetCacheSlot(server, meta->len, &buffer, &slotMeta);
+ ret = wh_Server_KeystoreGetCacheSlot(server, meta->id, meta->len, &buffer,
+ &slotMeta);
if (ret != 0) {
return ret;
}
diff --git a/test/config/wolfhsm_cfg.h b/test/config/wolfhsm_cfg.h
index 4a0762140..9a0d19154 100644
--- a/test/config/wolfhsm_cfg.h
+++ b/test/config/wolfhsm_cfg.h
@@ -32,6 +32,9 @@
#define WOLFHSM_CFG_COMM_DATA_LEN (1280 * 4)
+/* Enable global keys feature for testing */
+#define WOLFHSM_CFG_GLOBAL_KEYS
+
#define WOLFHSM_CFG_NVM_OBJECT_COUNT 30
#define WOLFHSM_CFG_SERVER_KEYCACHE_COUNT 9
#define WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE 300
diff --git a/test/wh_test.c b/test/wh_test.c
index 31124a6e4..413077ab9 100644
--- a/test/wh_test.c
+++ b/test/wh_test.c
@@ -37,6 +37,7 @@
#include "wh_test_she.h"
#include "wh_test_clientserver.h"
#include "wh_test_keywrap.h"
+#include "wh_test_multiclient.h"
#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER)
#include "wh_test_cert.h"
@@ -93,6 +94,9 @@ int whTest_Unit(void)
#endif /* WOLFHSM_CFG_SERVER_IMG_MGR && !WOLFHSM_CFG_NO_CRYPTO */
+ /* Multi-Client Tests (includes Global Keys when enabled) */
+ WH_TEST_ASSERT(0 == whTest_MultiClient());
+
#if defined(WOLFHSM_CFG_SHE_EXTENSION)
WH_TEST_ASSERT(0 == whTest_She());
#endif /* WOLFHSM_SHE_EXTENTION */
@@ -158,7 +162,7 @@ int whTest_ClientTcp(void)
.transport_cb = pttccb,
.transport_context = (void*)tcc,
.transport_config = (void*)mytcpconfig,
- .client_id = 0,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
}};
whClientConfig c_conf[1] = {{
.comm = cc_conf,
diff --git a/test/wh_test_cert.c b/test/wh_test_cert.c
index b48cda9f9..cba107440 100644
--- a/test/wh_test_cert.c
+++ b/test/wh_test_cert.c
@@ -334,7 +334,7 @@ int whTest_CertClientDma_ClientServerTestInternal(whClientContext* client)
int32_t out_rc;
whNvmId rootCertA_id = 1;
whNvmId rootCertB_id = 2;
- whKeyId out_keyId;
+ whKeyId out_keyId = WH_KEYID_ERASED;
uint8_t exportedPubKey[LEAF_A_PUBKEY_len];
uint16_t exportedPubKeyLen = sizeof(exportedPubKey);
diff --git a/test/wh_test_clientserver.c b/test/wh_test_clientserver.c
index 8c1a86151..3b946ed4e 100644
--- a/test/wh_test_clientserver.c
+++ b/test/wh_test_clientserver.c
@@ -747,7 +747,7 @@ int whTest_ClientServerSequential(whTestNvmBackendType nvmType)
.transport_cb = tccb,
.transport_context = (void*)tmcc,
.transport_config = (void*)tmcf,
- .client_id = 123,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
.connect_cb = _clientServerSequentialTestConnectCb,
}};
@@ -1775,7 +1775,7 @@ static int wh_ClientServer_MemThreadTest(whTestNvmBackendType nvmType)
.transport_cb = tccb,
.transport_context = (void*)tmcc,
.transport_config = (void*)tmcf,
- .client_id = 123,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
}};
whClientConfig c_conf[1] = {{
.comm = cc_conf,
@@ -1860,7 +1860,7 @@ static int wh_ClientServer_PosixMemMapThreadTest(whTestNvmBackendType nvmType)
.transport_cb = tccb,
.transport_context = (void*)tmcc,
.transport_config = (void*)tmcf,
- .client_id = 123,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
}};
whClientConfig c_conf[1] = {{
.comm = cc_conf,
diff --git a/test/wh_test_comm.c b/test/wh_test_comm.c
index f3f3ebf29..d5678ed38 100644
--- a/test/wh_test_comm.c
+++ b/test/wh_test_comm.c
@@ -82,7 +82,7 @@ int whTest_CommMem(void)
.transport_cb = tccb,
.transport_context = (void*)tmcc,
.transport_config = (void*)tmcf,
- .client_id = 123,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
}};
whCommClient client[1] = {0};
@@ -405,7 +405,7 @@ void wh_CommClientServer_MemThreadTest(void)
.transport_cb = tmccb,
.transport_context = (void*)csc,
.transport_config = (void*)tmcf,
- .client_id = 0x1,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
}};
/* Server configuration/contexts */
@@ -444,7 +444,7 @@ void wh_CommClientServer_ShMemThreadTest(void)
.transport_cb = tmccb,
.transport_context = (void*)csc,
.transport_config = (void*)tmcf,
- .client_id = 0x2,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
}};
/* Server configuration/contexts */
@@ -475,7 +475,7 @@ void wh_CommClientServer_TcpThreadTest(void)
.transport_cb = pttccb,
.transport_context = (void*)tcc,
.transport_config = (void*)mytcpconfig,
- .client_id = 0x3,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
}};
/* Server configuration/contexts */
diff --git a/test/wh_test_common.h b/test/wh_test_common.h
index c1bc5a08f..792ec36be 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_DEFAULT_CLIENT_ID (1)
/* Helper macro to print a message with caller source file info */
#ifdef WOLFHSM_CFG_TEST_VERBOSE
diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c
index eff6a6baa..1175e9e76 100644
--- a/test/wh_test_crypto.c
+++ b/test/wh_test_crypto.c
@@ -46,6 +46,8 @@
#ifdef WOLFHSM_CFG_ENABLE_CLIENT
#include "wolfhsm/wh_client.h"
#include "wolfhsm/wh_client_crypto.h"
+/* Pull in client keywrap tests to run against server */
+#include "wh_test_keywrap.h"
#endif
#ifdef WOLFHSM_CFG_ENABLE_SERVER
@@ -68,6 +70,8 @@
#define FLASH_SECTOR_SIZE (128 * 1024) /* 128KB */
#define FLASH_PAGE_SIZE (8) /* 8B */
+#define ALT_CLIENT_ID (2)
+
enum {
/* Total size needs to fit:
* - Transport CSR (whTransportMemCsr)
@@ -1352,7 +1356,7 @@ static int whTest_KeyCache(whClientContext* ctx, int devId, WC_RNG* rng)
if (ret != 0) {
WH_ERROR_PRINT("Failed to CommClose:%d\n",ret);
} else {
- ctx->comm->client_id = 2;
+ ctx->comm->client_id = ALT_CLIENT_ID;
ret = wh_Client_CommInit(ctx, NULL, NULL);
if (ret != 0) {
WH_ERROR_PRINT("Failed to CommInit:%d\n", ret);
@@ -1376,7 +1380,7 @@ static int whTest_KeyCache(whClientContext* ctx, int devId, WC_RNG* rng)
}
/* switch back and verify original key */
(void)wh_Client_CommClose(ctx);
- ctx->comm->client_id = 1;
+ ctx->comm->client_id = WH_TEST_DEFAULT_CLIENT_ID;
ret = wh_Client_CommInit(ctx, NULL, NULL);
if (ret != 0) {
WH_ERROR_PRINT("Failed to reconnect: %d\n", ret);
@@ -2620,7 +2624,7 @@ static int whTestCrypto_Cmac(whClientContext* ctx, int devId, WC_RNG* rng)
}
if (ret == 0) {
- /* test oneshot verify with commited key */
+ /* test oneshot verify with committed key */
keyId = WH_KEYID_ERASED;
ret = wh_Client_KeyCache(ctx, 0, labelIn, sizeof(labelIn), knownCmacKey,
sizeof(knownCmacKey), &keyId);
@@ -3595,6 +3599,13 @@ int whTest_CryptoClientConfig(whClientConfig* config)
ret = whTest_NonExportableKeystore(client, WH_DEV_ID, rng);
}
+#ifdef WOLFHSM_CFG_KEYWRAP
+ if (ret == 0) {
+ /* Test keywrap functionality */
+ ret = whTest_Client_KeyWrap(client);
+ }
+#endif
+
#ifndef NO_AES
i = 0;
while ((ret == WH_ERROR_OK) && (i < WH_NUM_DEVIDS)) {
@@ -3761,7 +3772,7 @@ int whTest_CryptoServerConfig(whServerConfig* config)
WH_TEST_RETURN_ON_FAIL(wh_Server_Init(server, config));
WH_TEST_RETURN_ON_FAIL(wh_Server_SetConnected(server, am_connected));
- server->comm->client_id = 1;
+ server->comm->client_id = WH_TEST_DEFAULT_CLIENT_ID;
while(am_connected == WH_COMM_CONNECTED) {
#ifdef WOLFHSM_CFG_IS_TEST_SERVER
@@ -3784,9 +3795,9 @@ int whTest_CryptoServerConfig(whServerConfig* config)
/* keep alive for 2 user changes */
if (am_connected != WH_COMM_CONNECTED && userChange < 2) {
if (userChange == 0)
- server->comm->client_id = 2;
+ server->comm->client_id = ALT_CLIENT_ID;
else if (userChange == 1)
- server->comm->client_id = 1;
+ server->comm->client_id = WH_TEST_DEFAULT_CLIENT_ID;
userChange++;
am_connected = WH_COMM_CONNECTED;
WH_TEST_RETURN_ON_FAIL(wh_Server_SetConnected(server, am_connected));
@@ -3881,7 +3892,7 @@ static int wh_ClientServer_MemThreadTest(whTestNvmBackendType nvmType)
.transport_cb = tccb,
.transport_context = (void*)tmcc,
.transport_config = (void*)tmcf,
- .client_id = 1,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
}};
#ifdef WOLFHSM_CFG_DMA
diff --git a/test/wh_test_keywrap.c b/test/wh_test_keywrap.c
index a492e980e..bca4e574f 100644
--- a/test/wh_test_keywrap.c
+++ b/test/wh_test_keywrap.c
@@ -85,14 +85,15 @@ static int _AesGcm_KeyWrap(whClientContext* client, WC_RNG* rng)
uint8_t plainKey[WH_TEST_AES_KEYSIZE];
uint8_t tmpPlainKey[WH_TEST_AES_KEYSIZE];
uint8_t wrappedKey[WH_TEST_AES_WRAPPED_KEYSIZE];
- whKeyId wrappedKeyId;
- whNvmMetadata metadata = {
- .id = WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, client->comm->client_id, WH_TEST_AESGCM_KEYID),
- .label = "AES Key Label",
- .len = WH_TEST_AES_KEYSIZE,
- .flags = WH_NVM_FLAGS_NONE,
+ whKeyId wrappedKeyId = WH_KEYID_ERASED;
+ whNvmMetadata metadata = {
+ .id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(WH_TEST_DEFAULT_CLIENT_ID,
+ WH_TEST_AESGCM_KEYID),
+ .label = "AES Key Label",
+ .len = WH_TEST_AES_KEYSIZE,
+ .flags = WH_NVM_FLAGS_NONE,
};
- whNvmMetadata tmpMetadata;
+ whNvmMetadata tmpMetadata = {0};
Aes aes[1];
const uint8_t plaintext[] = "hello, wolfSSL AES-GCM!";
@@ -135,7 +136,8 @@ static int _AesGcm_KeyWrap(whClientContext* client, WC_RNG* rng)
return ret;
}
- ret = wh_Client_AesSetKeyId(aes, wrappedKeyId);
+ ret =
+ wh_Client_AesSetKeyId(aes, WH_CLIENT_KEYID_MAKE_WRAPPED(wrappedKeyId));
if (ret != 0) {
WH_ERROR_PRINT("Failed to wh_Client_AesSetKeyId %d\n", ret);
return ret;
@@ -195,6 +197,28 @@ static int _AesGcm_KeyWrap(whClientContext* client, WC_RNG* rng)
return ret;
}
+ /* Cache a local key using the same numeric ID to confirm coexistence */
+ {
+ whKeyId localKeyId = WH_TEST_AESGCM_KEYID;
+ uint8_t localLabel[WH_NVM_LABEL_LEN] = "LocalKeySameId";
+ const uint8_t localKey[WH_TEST_AES_KEYSIZE] = {0};
+
+ ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONE, localLabel,
+ (uint16_t)sizeof("LocalKeySameId"),
+ (uint8_t*)localKey, sizeof(localKey),
+ &localKeyId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to cache local key with shared ID %d\n", ret);
+ return ret;
+ }
+ if (localKeyId != WH_TEST_AESGCM_KEYID) {
+ WH_ERROR_PRINT("Local key ID mismatch (expected %u, got %u)\n",
+ WH_TEST_AESGCM_KEYID, localKeyId);
+ return WH_ERROR_ABORTED;
+ }
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyErase(client, localKeyId));
+ }
+
wh_Client_KeyErase(client, wrappedKeyId);
wc_AesFree(aes);
@@ -254,6 +278,9 @@ int whTest_KeyWrapClientConfig(whClientConfig* clientCfg)
if (ret != 0) {
WH_ERROR_PRINT("Failed to whTest_Client_KeyWrap %d\n", ret);
}
+ else {
+ printf("KEYWRAP TESTS SUCCESS\n");
+ }
/* Clean up used resources */
cleanup_and_exit:
diff --git a/test/wh_test_keywrap.h b/test/wh_test_keywrap.h
index 44262f293..94ed032e3 100644
--- a/test/wh_test_keywrap.h
+++ b/test/wh_test_keywrap.h
@@ -21,7 +21,6 @@
#include "wolfhsm/wh_server.h"
#include "wolfhsm/wh_client.h"
-#include "wolfhsm/wh_flash.h"
int whTest_Client_KeyWrap(whClientContext* ctx);
int whTest_KeyWrapClientConfig(whClientConfig* cf);
diff --git a/test/wh_test_multiclient.c b/test/wh_test_multiclient.c
new file mode 100644
index 000000000..c4de85943
--- /dev/null
+++ b/test/wh_test_multiclient.c
@@ -0,0 +1,1531 @@
+/*
+ * 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_multiclient.c
+ *
+ * Multi-client test framework and test suites
+ *
+ * Provides reusable setup/teardown infrastructure for testing features that
+ * require multiple clients. Each client connects to its own server instance,
+ * but both servers share a common NVM context to enable testing of shared
+ * resources (global keys, shared counters, etc.).
+ */
+
+#include "wolfhsm/wh_settings.h"
+
+#if defined(WOLFHSM_CFG_ENABLE_CLIENT) && defined(WOLFHSM_CFG_ENABLE_SERVER)
+
+#include
+#include
+#include
+
+#include "wolfssl/wolfcrypt/settings.h"
+#include "wolfssl/wolfcrypt/types.h"
+#include "wolfssl/wolfcrypt/rsa.h"
+#include "wolfssl/wolfcrypt/ecc.h"
+#include "wolfssl/wolfcrypt/asn.h"
+
+#include "wolfhsm/wh_error.h"
+#include "wolfhsm/wh_comm.h"
+#include "wolfhsm/wh_message.h"
+#include "wolfhsm/wh_transport_mem.h"
+#include "wolfhsm/wh_client.h"
+#include "wolfhsm/wh_server.h"
+#include "wolfhsm/wh_server_keystore.h"
+#include "wolfhsm/wh_nvm.h"
+#include "wolfhsm/wh_nvm_flash.h"
+#include "wolfhsm/wh_flash_ramsim.h"
+
+#include "wh_test_common.h"
+
+/* Test configuration */
+#define FLASH_RAM_SIZE (1024 * 1024) /* 1MB */
+#define FLASH_SECTOR_SIZE (128 * 1024) /* 128KB */
+#define FLASH_PAGE_SIZE (8) /* 8B */
+#define BUFFER_SIZE 4096
+
+#ifdef WOLFHSM_CFG_GLOBAL_KEYS
+/* Test key data */
+static const uint8_t TEST_KEY_DATA_1[] = "TestGlobalKey1Data";
+static const uint8_t TEST_KEY_DATA_2[] = "TestLocalKey2Data";
+static const uint8_t TEST_KEY_DATA_3[] = "TestGlobalKey3DataLonger";
+#endif
+
+/* ============================================================================
+ * DUMMY KEY ID DEFINITIONS
+ *
+ * Generic key ID values for use in tests. Actual keyIds are defined locally
+ * within each test function and macros (WH_CLIENT_KEYID_MAKE_GLOBAL, etc.) are
+ * applied at assignment time.
+ * ========================================================================== */
+
+#define DUMMY_KEYID_1 1
+#define DUMMY_KEYID_2 2
+
+/* ============================================================================
+ * MULTI-CLIENT TEST FRAMEWORK INFRASTRUCTURE
+ * ========================================================================== */
+
+/* Server contexts for connect callbacks */
+static whServerContext* testServer1 = NULL;
+static whServerContext* testServer2 = NULL;
+
+/* Connect callback for client 1 */
+static int _connectCb1(void* context, whCommConnected connected)
+{
+ (void)context;
+ if (testServer1 == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+ return wh_Server_SetConnected(testServer1, connected);
+}
+
+/* Connect callback for client 2 */
+static int _connectCb2(void* context, whCommConnected connected)
+{
+ (void)context;
+ if (testServer2 == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+ return wh_Server_SetConnected(testServer2, connected);
+}
+
+/* ============================================================================
+ * GLOBAL KEYS TEST SUITE
+ * ========================================================================== */
+
+#ifdef WOLFHSM_CFG_GLOBAL_KEYS
+
+/*
+ * Test 1: Basic global key operations
+ * - Client 1 caches a global key
+ * - Client 2 reads the same global key and verifies the data matches
+ */
+static int _testGlobalKeyBasic(whClientContext* client1,
+ whServerContext* server1,
+ whClientContext* client2,
+ whServerContext* server2)
+{
+ int ret;
+ whKeyId keyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ uint8_t label[WH_NVM_LABEL_LEN];
+ uint16_t labelSz = sizeof(label);
+ uint8_t outBuf[sizeof(TEST_KEY_DATA_1)] = {0};
+ uint16_t outSz = sizeof(outBuf);
+
+ printf("Test: Global key basic operations\n");
+
+ /* Client 1 caches a global key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"GlobalKey5", sizeof("GlobalKey5"),
+ (uint8_t*)TEST_KEY_DATA_1, sizeof(TEST_KEY_DATA_1), keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &keyId));
+
+ /* Client 2 reads the same global key */
+ keyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(client2, keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportResponse(client2, label, labelSz, outBuf, &outSz));
+
+ /* Verify the key data matches */
+ WH_TEST_ASSERT_RETURN(outSz == sizeof(TEST_KEY_DATA_1));
+ WH_TEST_ASSERT_RETURN(0 == memcmp(outBuf, TEST_KEY_DATA_1, outSz));
+
+ printf(" PASS: Basic global key operations\n");
+
+ (void)ret;
+ return 0;
+}
+
+/*
+ * Test 2: Local key isolation
+ * - Both clients cache local keys with the same ID but different data
+ * - Each client verifies they can only read their own local key data, not the
+ * other's
+ */
+static int _testLocalKeyIsolation(whClientContext* client1,
+ whServerContext* server1,
+ whClientContext* client2,
+ whServerContext* server2)
+{
+ int ret;
+ whKeyId keyId1 = DUMMY_KEYID_1; /* Local key for client 1 */
+ whKeyId keyId2 =
+ DUMMY_KEYID_1; /* Same ID for client 2 - should be different key */
+ uint8_t label[WH_NVM_LABEL_LEN];
+ uint16_t labelSz = sizeof(label);
+ uint8_t outBuf[32] = {0};
+ uint16_t outSz;
+
+ printf("Test: Local key isolation\n");
+
+ /* Client 1 caches a local key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"LocalKey10_C1", sizeof("LocalKey10_C1"),
+ (uint8_t*)TEST_KEY_DATA_1, sizeof(TEST_KEY_DATA_1), keyId1));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &keyId1));
+
+ /* Client 2 caches a different local key with same ID */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client2, 0, (uint8_t*)"LocalKey10_C2", sizeof("LocalKey10_C2"),
+ (uint8_t*)TEST_KEY_DATA_2, sizeof(TEST_KEY_DATA_2), keyId2));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client2, &keyId2));
+
+ /* Client 1 reads its own key */
+ outSz = sizeof(outBuf);
+ memset(outBuf, 0, sizeof(outBuf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(client1, keyId1));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportResponse(client1, label, labelSz, outBuf, &outSz));
+ WH_TEST_ASSERT_RETURN(
+ 0 == memcmp(outBuf, TEST_KEY_DATA_1, sizeof(TEST_KEY_DATA_1)));
+
+ /* Client 2 reads its own key (different data) */
+ outSz = sizeof(outBuf);
+ memset(outBuf, 0, sizeof(outBuf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(client2, keyId2));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportResponse(client2, label, labelSz, outBuf, &outSz));
+ WH_TEST_ASSERT_RETURN(
+ 0 == memcmp(outBuf, TEST_KEY_DATA_2, sizeof(TEST_KEY_DATA_2)));
+
+ printf(" PASS: Local key isolation\n");
+
+ (void)ret;
+ return 0;
+}
+
+/*
+ * Test 3: Mixed global and local keys with no cross-cache interference
+ * - Client 1 caches both a global key and a local key with the same ID number
+ * - Client 2 caches a local key with the same ID (different data)
+ * - Client 1 can read both its global and local keys correctly
+ * - Client 2 can access the global key
+ * - Client 2 can read its own local key (different data than client 1's)
+ * - Client 2 correctly fails to access Client 1's local key
+ */
+static int _testMixedGlobalLocal(whClientContext* client1,
+ whServerContext* server1,
+ whClientContext* client2,
+ whServerContext* server2)
+{
+ int ret;
+ whKeyId globalKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ whKeyId localKeyId = DUMMY_KEYID_1; /* Same ID number but local */
+ uint8_t label[WH_NVM_LABEL_LEN];
+ uint16_t labelSz = sizeof(label);
+ uint8_t outBuf[32] = {0};
+ uint16_t outSz;
+
+ printf(
+ "Test: Mixed global and local keys with no cross-cache interference\n");
+
+ /* Client 1 caches global key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"Global15", sizeof("Global15"),
+ (uint8_t*)TEST_KEY_DATA_3, sizeof(TEST_KEY_DATA_3), globalKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &globalKeyId));
+
+ /* Client 1 caches local key with same ID number */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"Local15_C1", sizeof("Local15_C1"),
+ (uint8_t*)TEST_KEY_DATA_1, sizeof(TEST_KEY_DATA_1), localKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &localKeyId));
+
+ /* Client 2 caches local key with same ID number (different data) */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client2, 0, (uint8_t*)"Local15_C2", sizeof("Local15_C2"),
+ (uint8_t*)TEST_KEY_DATA_2, sizeof(TEST_KEY_DATA_2), localKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client2, &localKeyId));
+
+ /* Client 1 reads its global key */
+ globalKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ outSz = sizeof(outBuf);
+ memset(outBuf, 0, sizeof(outBuf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(client1, globalKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportResponse(client1, label, labelSz, outBuf, &outSz));
+ WH_TEST_ASSERT_RETURN(
+ 0 == memcmp(outBuf, TEST_KEY_DATA_3, sizeof(TEST_KEY_DATA_3)));
+
+ /* Client 1 reads its local key */
+ outSz = sizeof(outBuf);
+ memset(outBuf, 0, sizeof(outBuf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(client1, localKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportResponse(client1, label, labelSz, outBuf, &outSz));
+ WH_TEST_ASSERT_RETURN(
+ 0 == memcmp(outBuf, TEST_KEY_DATA_1, sizeof(TEST_KEY_DATA_1)));
+
+ /* Client 2 accesses global key (should work) */
+ globalKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ outSz = sizeof(outBuf);
+ memset(outBuf, 0, sizeof(outBuf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(client2, globalKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportResponse(client2, label, labelSz, outBuf, &outSz));
+ WH_TEST_ASSERT_RETURN(
+ 0 == memcmp(outBuf, TEST_KEY_DATA_3, sizeof(TEST_KEY_DATA_3)));
+
+ /* Client 2 reads its own local key 15 (different data) */
+ outSz = sizeof(outBuf);
+ memset(outBuf, 0, sizeof(outBuf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(client2, localKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportResponse(client2, label, labelSz, outBuf, &outSz));
+ WH_TEST_ASSERT_RETURN(
+ 0 == memcmp(outBuf, TEST_KEY_DATA_2, sizeof(TEST_KEY_DATA_2)));
+
+ /* Client 1 tries to access Client 2's local key 15 - should fail */
+ outSz = sizeof(outBuf);
+ memset(outBuf, 0, sizeof(outBuf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(client1, localKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ ret = wh_Client_KeyExportResponse(client1, label, labelSz, outBuf, &outSz);
+ /* Should get client 1's own local key, not client 2's */
+ WH_TEST_ASSERT_RETURN(ret == 0);
+ WH_TEST_ASSERT_RETURN(
+ 0 == memcmp(outBuf, TEST_KEY_DATA_1, sizeof(TEST_KEY_DATA_1)));
+
+ printf(" PASS: Mixed global and local keys with no cross-cache "
+ "interference\n");
+
+ (void)ret;
+ return 0;
+}
+
+/*
+ * Test 4: NVM persistence of global keys
+ * - Client 1 caches a global key and commits it to NVM, then evicts it from
+ * cache
+ * - Client 2 successfully reloads the global key from NVM
+ */
+static int _testGlobalKeyNvmPersistence(whClientContext* client1,
+ whServerContext* server1,
+ whClientContext* client2,
+ whServerContext* server2)
+{
+ int ret;
+ whKeyId keyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ uint8_t label[WH_NVM_LABEL_LEN];
+ uint16_t labelSz = sizeof(label);
+ uint8_t outBuf[sizeof(TEST_KEY_DATA_1)] = {0};
+ uint16_t outSz;
+
+ printf("Test: NVM persistence of global keys\n");
+
+ /* Client 1 caches and commits a global key to NVM */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"GlobalNVM20", sizeof("GlobalNVM20"),
+ (uint8_t*)TEST_KEY_DATA_1, sizeof(TEST_KEY_DATA_1), keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &keyId));
+
+ /* Commit to NVM */
+ keyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommitRequest(client1, keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommitResponse(client1));
+
+ /* Evict from cache on server1 */
+ keyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ /* Client 2 reads from NVM (will reload to cache) */
+ keyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ outSz = sizeof(outBuf);
+ memset(outBuf, 0, sizeof(outBuf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(client2, keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportResponse(client2, label, labelSz, outBuf, &outSz));
+ WH_TEST_ASSERT_RETURN(
+ 0 == memcmp(outBuf, TEST_KEY_DATA_1, sizeof(TEST_KEY_DATA_1)));
+
+ /* Clean up - erase the key */
+ keyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEraseRequest(client1, keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEraseResponse(client1));
+
+ printf(" PASS: NVM persistence of global keys\n");
+
+ (void)ret;
+ return 0;
+}
+
+/*
+ * Test 5: Export protection on global keys
+ * - Client 1 caches a non-exportable global key
+ * - Client 2 correctly fails when attempting to export the protected key
+ */
+static int _testGlobalKeyExportProtection(whClientContext* client1,
+ whServerContext* server1,
+ whClientContext* client2,
+ whServerContext* server2)
+{
+ int ret;
+ whKeyId keyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ uint8_t label[WH_NVM_LABEL_LEN];
+ uint16_t labelSz = sizeof(label);
+ uint8_t outBuf[sizeof(TEST_KEY_DATA_1)] = {0};
+ uint16_t outSz;
+
+ printf("Test: Export protection on global keys\n");
+
+ /* Client 1 caches a non-exportable global key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, WH_NVM_FLAGS_NONEXPORTABLE, (uint8_t*)"NoExport25",
+ sizeof("NoExport25"), (uint8_t*)TEST_KEY_DATA_1,
+ sizeof(TEST_KEY_DATA_1), keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &keyId));
+
+ /* Client 2 tries to export it - should fail */
+ keyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ outSz = sizeof(outBuf);
+ memset(outBuf, 0, sizeof(outBuf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(client2, keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ ret = wh_Client_KeyExportResponse(client2, label, labelSz, outBuf, &outSz);
+ /* Should fail due to non-exportable flag */
+ WH_TEST_ASSERT_RETURN(ret != 0);
+
+ /* Clean up */
+ keyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Export protection on global keys\n");
+
+ return 0;
+}
+
+#ifdef WOLFHSM_CFG_DMA
+/*
+ * Test 6: DMA operations with global keys
+ * - Client 1 caches a global key using DMA transfer
+ * - Client 2 reads the DMA-cached global key via regular export
+ * - Client 1 caches another global key via regular method
+ * - Client 2 exports that global key via DMA transfer
+ */
+static int _testGlobalKeyDma(whClientContext* client1, whServerContext* server1,
+ whClientContext* client2, whServerContext* server2)
+{
+ int ret;
+ whKeyId keyId1 = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ whKeyId keyId2 = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_2);
+ uint8_t keyData1[32] = "GlobalDmaCacheTestKey123456!";
+ uint8_t keyData2[32] = "GlobalDmaExportTestKey12345!";
+ uint8_t outBuf[32] = {0};
+ uint8_t label[WH_NVM_LABEL_LEN];
+ uint16_t labelSz = sizeof(label);
+ uint16_t outSz;
+
+ printf("Test: DMA operations with global keys\n");
+
+ /* Part 1: Cache via DMA, export via regular */
+ /* Client 1 caches a global key using DMA */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheDmaRequest(
+ client1, 0, (uint8_t*)"DmaGlobal35", sizeof("DmaGlobal35"), keyData1,
+ sizeof(keyData1), keyId1));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheDmaResponse(client1, &keyId1));
+
+ /* Client 2 reads the global key via regular export */
+ keyId1 = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ outSz = sizeof(outBuf);
+ memset(outBuf, 0, sizeof(outBuf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(client2, keyId1));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportResponse(client2, label, labelSz, outBuf, &outSz));
+
+ /* Verify the key data matches */
+ WH_TEST_ASSERT_RETURN(outSz == sizeof(keyData1));
+ WH_TEST_ASSERT_RETURN(0 == memcmp(outBuf, keyData1, outSz));
+
+ /* Part 2: Cache via regular, export via DMA */
+ /* Client 1 caches a global key using regular method */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"DmaExport40", sizeof("DmaExport40"), keyData2,
+ sizeof(keyData2), keyId2));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &keyId2));
+
+ /* Client 2 exports the global key via DMA */
+ keyId2 = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_2);
+ outSz = sizeof(outBuf);
+ memset(outBuf, 0, sizeof(outBuf));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportDmaRequest(client2, keyId2, outBuf, sizeof(outBuf)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportDmaResponse(client2, label, labelSz, &outSz));
+
+ /* Verify the key data matches */
+ WH_TEST_ASSERT_RETURN(outSz == sizeof(keyData2));
+ WH_TEST_ASSERT_RETURN(0 == memcmp(outBuf, keyData2, outSz));
+
+ /* Clean up */
+ keyId1 = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, keyId1));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ keyId2 = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_2);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, keyId2));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: DMA operations with global keys\n");
+
+ (void)ret;
+ return 0;
+}
+#endif /* WOLFHSM_CFG_DMA */
+
+#ifdef WOLFHSM_CFG_KEYWRAP
+/*
+ * Test 7: Key wrap with global server key
+ * - Client 1 caches a global wrapping key
+ * - Client 2 wraps a key using that global server key
+ * - Client 1 unwraps the key using the same global server key
+ */
+static int _testGlobalKeyWrapExport(whClientContext* client1,
+ whServerContext* server1,
+ whClientContext* client2,
+ whServerContext* server2)
+{
+ int ret;
+ whKeyId serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ uint8_t wrapKey[AES_256_KEY_SIZE] = "GlobalWrapKey123456789012345!";
+ uint8_t plainKey[AES_256_KEY_SIZE] = "PlainKeyToWrap1234567890123!";
+ /* Wrapped key size = IV(12) + TAG(16) + KEYSIZE(32) + metadata */
+#define WRAPPED_KEY_SIZE (12 + 16 + AES_256_KEY_SIZE + sizeof(whNvmMetadata))
+ uint8_t wrappedKey[WRAPPED_KEY_SIZE] = {0};
+ uint8_t unwrappedKey[AES_256_KEY_SIZE] = {0};
+ whNvmMetadata meta = {0};
+
+ printf("Test: Key wrap with global server key\n");
+
+ /* Client 1 caches a global wrapping key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"WrapKey45", sizeof("WrapKey45"), wrapKey,
+ sizeof(wrapKey), serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId));
+
+ /* Client 2 wraps a global key using the global server key */
+ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ meta.id =
+ WH_CLIENT_KEYID_MAKE_WRAPPED_META(WH_KEYUSER_GLOBAL, DUMMY_KEYID_2);
+ meta.len = sizeof(plainKey);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapRequest(client2, WC_CIPHER_AES_GCM,
+ serverKeyId, plainKey,
+ sizeof(plainKey), &meta));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapResponse(
+ client2, WC_CIPHER_AES_GCM, wrappedKey, sizeof(wrappedKey)));
+
+ /* Client 1 unwraps the key using the same global server key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndExportRequest(
+ client1, WC_CIPHER_AES_GCM, serverKeyId, wrappedKey,
+ sizeof(wrappedKey)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndExportResponse(
+ client1, WC_CIPHER_AES_GCM, &meta, unwrappedKey, sizeof(unwrappedKey)));
+
+ /* Verify the unwrapped key matches the original */
+ WH_TEST_ASSERT_RETURN(0 ==
+ memcmp(unwrappedKey, plainKey, sizeof(plainKey)));
+
+ /* Clean up */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Key wrap with global server key\n");
+
+ (void)ret;
+ return 0;
+#undef WRAPPED_KEY_SIZE
+}
+
+/*
+ * Test 8: Key unwrap and cache with global server key
+ * - Client 1 caches a global wrapping key and wraps a key (also global)
+ * - Client 2 unwraps and caches the key using the global server key
+ * - Client 2 exports and verifies the cached key matches the original
+ */
+static int _testGlobalKeyUnwrapCache(whClientContext* client1,
+ whServerContext* server1,
+ whClientContext* client2,
+ whServerContext* server2)
+{
+ int ret;
+ whKeyId serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ whKeyId cachedKeyId = 0;
+ uint8_t wrapKey[AES_256_KEY_SIZE] = "GlobalUnwrapKey123456789012!";
+ uint8_t plainKey[AES_256_KEY_SIZE] = "KeyToCacheViaUnwrap123456!!";
+#define WRAPPED_KEY_SIZE (12 + 16 + AES_256_KEY_SIZE + sizeof(whNvmMetadata))
+ uint8_t wrappedKey[WRAPPED_KEY_SIZE] = {0};
+ uint8_t verifyBuf[AES_256_KEY_SIZE] = {0};
+ uint8_t label[WH_NVM_LABEL_LEN];
+ uint16_t labelSz = sizeof(label);
+ uint16_t verifySz = sizeof(verifyBuf);
+ whNvmMetadata meta = {0};
+
+ printf("Test: Key unwrap and cache with global server key\n");
+
+ /* Client 1 caches a global wrapping key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"UnwrapKey50", sizeof("UnwrapKey50"), wrapKey,
+ sizeof(wrapKey), serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId));
+
+ /* Client 1 wraps a global key */
+ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ /* key-TODO: client-facing helper macro? */
+ meta.id =
+ WH_MAKE_KEYID(WH_KEYTYPE_WRAPPED, WH_KEYUSER_GLOBAL, DUMMY_KEYID_2);
+ meta.len = sizeof(plainKey);
+
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapRequest(client1, WC_CIPHER_AES_GCM,
+ serverKeyId, plainKey,
+ sizeof(plainKey), &meta));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapResponse(
+ client1, WC_CIPHER_AES_GCM, wrappedKey, sizeof(wrappedKey)));
+
+ /* Client 2 unwraps and caches the key using the global server key */
+ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ ret = wh_Client_KeyUnwrapAndCacheRequest(client2, WC_CIPHER_AES_GCM,
+ serverKeyId, wrappedKey,
+ sizeof(wrappedKey));
+ WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK);
+
+ ret = wh_Server_HandleRequestMessage(server2);
+ WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK);
+
+ ret = wh_Client_KeyUnwrapAndCacheResponse(client2, WC_CIPHER_AES_GCM,
+ &cachedKeyId);
+ WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK);
+
+ /* Verify the cached key by exporting it */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(
+ client2, WH_CLIENT_KEYID_MAKE_WRAPPED_GLOBAL(cachedKeyId)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportResponse(client2, label, labelSz,
+ verifyBuf, &verifySz));
+
+ /* Verify the exported key matches the original */
+ WH_TEST_ASSERT_RETURN(0 == memcmp(verifyBuf, plainKey, sizeof(plainKey)));
+
+ /* Clean up */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(
+ client2, WH_CLIENT_KEYID_MAKE_WRAPPED_GLOBAL(cachedKeyId)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client2));
+
+ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Key unwrap and cache with global server key\n");
+
+ (void)ret;
+ return 0;
+#undef WRAPPED_KEY_SIZE
+}
+
+/*
+ * Test 7a: Global wrapping key + Global wrapped key (Positive)
+ * - Client 1 caches a global wrapping key
+ * - Client 2 wraps a global key using it
+ * - Client 1 unwraps and exports successfully
+ */
+static int _testWrappedKey_GlobalWrap_GlobalKey_Positive(
+ whClientContext* client1, whServerContext* server1,
+ whClientContext* client2, whServerContext* server2)
+{
+ int ret;
+ whKeyId serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ uint8_t wrapKey[AES_256_KEY_SIZE] = "GlobalWrapKey2Test7aXXXXXXXXX!";
+ uint8_t plainKey[AES_256_KEY_SIZE] = "GlobalPlainKey2Test7aXXXXXXXX!";
+#define WRAPPED_KEY_SIZE (12 + 16 + AES_256_KEY_SIZE + sizeof(whNvmMetadata))
+ uint8_t wrappedKey[WRAPPED_KEY_SIZE] = {0};
+ uint8_t unwrappedKey[AES_256_KEY_SIZE] = {0};
+ whNvmMetadata meta = {0};
+
+ printf("Test 7a: Global wrap key + Global wrapped key (Positive)\n");
+
+ /* Client 1 caches a global wrapping key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"WrapKey_7a", sizeof("WrapKey_7a"), wrapKey,
+ sizeof(wrapKey), serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId));
+
+ /* Client 2 wraps a GLOBAL key using the global server key */
+ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ meta.id =
+ WH_CLIENT_KEYID_MAKE_WRAPPED_META(WH_KEYUSER_GLOBAL, DUMMY_KEYID_2);
+ meta.len = sizeof(plainKey);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapRequest(client2, WC_CIPHER_AES_GCM,
+ serverKeyId, plainKey,
+ sizeof(plainKey), &meta));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapResponse(
+ client2, WC_CIPHER_AES_GCM, wrappedKey, sizeof(wrappedKey)));
+
+ /* Client 1 unwraps and exports the global key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndExportRequest(
+ client1, WC_CIPHER_AES_GCM, serverKeyId, wrappedKey,
+ sizeof(wrappedKey)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndExportResponse(
+ client1, WC_CIPHER_AES_GCM, &meta, unwrappedKey, sizeof(unwrappedKey)));
+
+ /* Verify the unwrapped key matches the original */
+ WH_TEST_ASSERT_RETURN(0 ==
+ memcmp(unwrappedKey, plainKey, sizeof(plainKey)));
+
+ /* Clean up */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Global wrap key + Global wrapped key (Positive)\n");
+
+ (void)ret;
+ return 0;
+#undef WRAPPED_KEY_SIZE
+}
+
+/*
+ * Test 7b: Global wrapping key + Global wrapped key (Negative - NONEXPORTABLE)
+ * - Client 1 caches a global wrapping key
+ * - Client 2 wraps a global key with NONEXPORTABLE flag
+ * - Client 1 unwrap-and-export fails with WH_ERROR_ACCESS
+ */
+static int _testWrappedKey_GlobalWrap_GlobalKey_NonExportable(
+ whClientContext* client1, whServerContext* server1,
+ whClientContext* client2, whServerContext* server2)
+{
+ int ret;
+ whKeyId serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ uint8_t wrapKey[AES_256_KEY_SIZE] = "GlobalWrapKey2Test7bXXXXXXXXX!";
+ uint8_t plainKey[AES_256_KEY_SIZE] = "GlobalPlainKey2Test7bXXXXXXXX!";
+#define WRAPPED_KEY_SIZE (12 + 16 + AES_256_KEY_SIZE + sizeof(whNvmMetadata))
+ uint8_t wrappedKey[WRAPPED_KEY_SIZE] = {0};
+ uint8_t unwrappedKey[AES_256_KEY_SIZE] = {0};
+ whNvmMetadata meta = {0};
+
+ printf("Test 7b: Global wrap key + Global wrapped key (Non-exportable)\n");
+
+ /* Client 1 caches a global wrapping key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"WrapKey_7b", sizeof("WrapKey_7b"), wrapKey,
+ sizeof(wrapKey), serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId));
+
+ /* Client 2 wraps a GLOBAL key with NONEXPORTABLE flag */
+ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ meta.id =
+ WH_CLIENT_KEYID_MAKE_WRAPPED_META(WH_KEYUSER_GLOBAL, DUMMY_KEYID_2);
+ meta.len = sizeof(plainKey);
+ meta.flags = WH_NVM_FLAGS_NONEXPORTABLE;
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapRequest(client2, WC_CIPHER_AES_GCM,
+ serverKeyId, plainKey,
+ sizeof(plainKey), &meta));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapResponse(
+ client2, WC_CIPHER_AES_GCM, wrappedKey, sizeof(wrappedKey)));
+
+ /* Client 1 tries to unwrap and export - should fail */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndExportRequest(
+ client1, WC_CIPHER_AES_GCM, serverKeyId, wrappedKey,
+ sizeof(wrappedKey)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ ret = wh_Client_KeyUnwrapAndExportResponse(
+ client1, WC_CIPHER_AES_GCM, &meta, unwrappedKey, sizeof(unwrappedKey));
+
+ /* Should fail due to non-exportable flag */
+ WH_TEST_ASSERT_RETURN(ret != 0);
+
+ /* Clean up */
+ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Global wrap key + Global wrapped key (Non-exportable)\n");
+
+ return 0;
+#undef WRAPPED_KEY_SIZE
+}
+
+/*
+ * Test 8a: Global wrapping key + Local wrapped key (Positive - Owner)
+ * - Client 1 caches a global wrapping key
+ * - Client 2 wraps a LOCAL key (USER=client2_id) using global wrapping key
+ * - Client 2 unwraps and exports successfully (owner)
+ */
+static int _testWrappedKey_GlobalWrap_LocalKey_OwnerExport(
+ whClientContext* client1, whServerContext* server1,
+ whClientContext* client2, whServerContext* server2)
+{
+ int ret;
+ whKeyId serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ uint16_t client2Id = WH_TEST_DEFAULT_CLIENT_ID + 1;
+ uint8_t wrapKey[AES_256_KEY_SIZE] = "GlobalWrapKey2Test8aXXXXXXXXX!";
+ uint8_t plainKey[AES_256_KEY_SIZE] = "LocalPlainKey2Test8aXXXXXXXXX!";
+#define WRAPPED_KEY_SIZE (12 + 16 + AES_256_KEY_SIZE + sizeof(whNvmMetadata))
+ uint8_t wrappedKey[WRAPPED_KEY_SIZE] = {0};
+ uint8_t unwrappedKey[AES_256_KEY_SIZE] = {0};
+ whNvmMetadata meta = {0};
+
+ printf("Test 8a: Global wrap key + Local wrapped key (Owner export)\n");
+
+ /* Client 1 caches a global wrapping key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"WrapKey_8a", sizeof("WrapKey_8a"), wrapKey,
+ sizeof(wrapKey), serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId));
+
+ /* Client 2 wraps a LOCAL key (USER=client2_id) */
+ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ meta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client2Id, DUMMY_KEYID_2);
+ meta.len = sizeof(plainKey);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapRequest(client2, WC_CIPHER_AES_GCM,
+ serverKeyId, plainKey,
+ sizeof(plainKey), &meta));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapResponse(
+ client2, WC_CIPHER_AES_GCM, wrappedKey, sizeof(wrappedKey)));
+
+ /* Client 2 (owner) unwraps and exports the local key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndExportRequest(
+ client2, WC_CIPHER_AES_GCM, serverKeyId, wrappedKey,
+ sizeof(wrappedKey)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndExportResponse(
+ client2, WC_CIPHER_AES_GCM, &meta, unwrappedKey, sizeof(unwrappedKey)));
+
+ /* Verify the unwrapped key matches the original */
+ WH_TEST_ASSERT_RETURN(0 ==
+ memcmp(unwrappedKey, plainKey, sizeof(plainKey)));
+
+ /* Clean up */
+ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Global wrap key + Local wrapped key (Owner export)\n");
+
+ (void)ret;
+ return 0;
+#undef WRAPPED_KEY_SIZE
+}
+
+/*
+ * Test 8b: Global wrapping key + Local wrapped key (Negative - Non-owner)
+ * - Client 1 caches a global wrapping key
+ * - Client 2 wraps a LOCAL key (USER=client2_id)
+ * - Client 1 unwrap-and-export fails with WH_ERROR_ACCESS (not owner)
+ * - Client 1 unwrap-and-cache also fails with WH_ERROR_ACCESS
+ */
+static int _testWrappedKey_GlobalWrap_LocalKey_NonOwnerFails(
+ whClientContext* client1, whServerContext* server1,
+ whClientContext* client2, whServerContext* server2)
+{
+ int ret;
+ whKeyId serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ uint16_t client2Id = WH_TEST_DEFAULT_CLIENT_ID + 1;
+ uint8_t wrapKey[AES_256_KEY_SIZE] = "GlobalWrapKey2Test8bXXXXXXXXX!";
+ uint8_t plainKey[AES_256_KEY_SIZE] = "LocalPlainKey2Test8bXXXXXXXXX!";
+#define WRAPPED_KEY_SIZE (12 + 16 + AES_256_KEY_SIZE + sizeof(whNvmMetadata))
+ uint8_t wrappedKey[WRAPPED_KEY_SIZE] = {0};
+ uint8_t unwrappedKey[AES_256_KEY_SIZE] = {0};
+ whNvmMetadata meta = {0};
+ whKeyId cachedKeyId = 0;
+
+ printf("Test 8b: Global wrap key + Local wrapped key (Non-owner fails)\n");
+
+ /* Client 1 caches a global wrapping key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"WrapKey_8b", sizeof("WrapKey_8b"), wrapKey,
+ sizeof(wrapKey), serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId));
+
+ /* Client 2 wraps a LOCAL key (USER=client2_id) */
+ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ meta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client2Id, DUMMY_KEYID_2);
+ meta.len = sizeof(plainKey);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapRequest(client2, WC_CIPHER_AES_GCM,
+ serverKeyId, plainKey,
+ sizeof(plainKey), &meta));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapResponse(
+ client2, WC_CIPHER_AES_GCM, wrappedKey, sizeof(wrappedKey)));
+
+ /* Client 1 (non-owner) tries to unwrap and export - should fail */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndExportRequest(
+ client1, WC_CIPHER_AES_GCM, serverKeyId, wrappedKey,
+ sizeof(wrappedKey)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ ret = wh_Client_KeyUnwrapAndExportResponse(
+ client1, WC_CIPHER_AES_GCM, &meta, unwrappedKey, sizeof(unwrappedKey));
+
+ /* Should fail - Client 1 is not the owner */
+ WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS);
+
+ /* Client 1 (non-owner) tries to unwrap and cache - should also fail */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndCacheRequest(
+ client1, WC_CIPHER_AES_GCM, serverKeyId, wrappedKey,
+ sizeof(wrappedKey)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ ret = wh_Client_KeyUnwrapAndCacheResponse(client1, WC_CIPHER_AES_GCM,
+ &cachedKeyId);
+
+ /* Should also fail - Client 1 is not the owner */
+ WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS);
+
+ /* Clean up */
+ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Global wrap key + Local wrapped key (Non-owner fails)\n");
+
+ return WH_ERROR_OK;
+#undef WRAPPED_KEY_SIZE
+}
+
+/*
+ * Test 9a: Local wrapping key + Local wrapped key (Positive - Same owner)
+ * - Client 1 caches a local wrapping key
+ * - Client 1 wraps a LOCAL key (USER=client1_id)
+ * - Client 1 unwraps and exports successfully
+ */
+static int _testWrappedKey_LocalWrap_LocalKey_SameOwner(
+ whClientContext* client1, whServerContext* server1,
+ whClientContext* client2, whServerContext* server2)
+{
+ int ret;
+ whKeyId serverKeyId = DUMMY_KEYID_1; /* Local wrapping key */
+ uint16_t client1Id = WH_TEST_DEFAULT_CLIENT_ID;
+ uint8_t wrapKey[AES_256_KEY_SIZE] = "LocalWrapKey2Test9aXXXXXXXXXX!";
+ uint8_t plainKey[AES_256_KEY_SIZE] = "LocalPlainKey2Test9aXXXXXXXXX!";
+#define WRAPPED_KEY_SIZE (12 + 16 + AES_256_KEY_SIZE + sizeof(whNvmMetadata))
+ uint8_t wrappedKey[WRAPPED_KEY_SIZE] = {0};
+ uint8_t unwrappedKey[AES_256_KEY_SIZE] = {0};
+ whNvmMetadata meta = {0};
+
+ printf("Test 9a: Local wrap key + Local wrapped key (Same owner)\n");
+
+ /* Client 1 caches a LOCAL wrapping key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"WrapKey_9a", sizeof("WrapKey_9a"), wrapKey,
+ sizeof(wrapKey), serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId));
+
+ /* Client 1 wraps a LOCAL key (USER=client1_id) */
+ serverKeyId = DUMMY_KEYID_1; /* Use local wrapping key */
+ meta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client1Id, DUMMY_KEYID_2);
+ meta.len = sizeof(plainKey);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapRequest(client1, WC_CIPHER_AES_GCM,
+ serverKeyId, plainKey,
+ sizeof(plainKey), &meta));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapResponse(
+ client1, WC_CIPHER_AES_GCM, wrappedKey, sizeof(wrappedKey)));
+
+ /* Client 1 (owner) unwraps and exports the local key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndExportRequest(
+ client1, WC_CIPHER_AES_GCM, serverKeyId, wrappedKey,
+ sizeof(wrappedKey)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndExportResponse(
+ client1, WC_CIPHER_AES_GCM, &meta, unwrappedKey, sizeof(unwrappedKey)));
+
+ /* Verify the unwrapped key matches the original */
+ WH_TEST_ASSERT_RETURN(0 ==
+ memcmp(unwrappedKey, plainKey, sizeof(plainKey)));
+
+ /* Clean up */
+ serverKeyId = DUMMY_KEYID_1;
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Local wrap key + Local wrapped key (Same owner)\n");
+
+ (void)ret;
+ (void)client2;
+ (void)server2;
+ return 0;
+#undef WRAPPED_KEY_SIZE
+}
+
+/*
+ * Test 9b: Local wrapping key + Local wrapped key (Negative - No access without
+ * wrap key)
+ * - Client 1 caches a local wrapping key
+ * - Client 1 wraps a local key
+ * - Client 2 cannot unwrap (doesn't have wrapping key)
+ */
+static int _testWrappedKey_LocalWrap_LocalKey_NoAccessWithoutWrapKey(
+ whClientContext* client1, whServerContext* server1,
+ whClientContext* client2, whServerContext* server2)
+{
+ int ret;
+ whKeyId serverKeyId = DUMMY_KEYID_1; /* Local wrapping key */
+ uint16_t client1Id = WH_TEST_DEFAULT_CLIENT_ID;
+ uint8_t wrapKey[AES_256_KEY_SIZE] = "LocalWrapKey2Test9bXXXXXXXXXX!";
+ uint8_t plainKey[AES_256_KEY_SIZE] = "LocalPlainKey2Test9bXXXXXXXXX!";
+#define WRAPPED_KEY_SIZE (12 + 16 + AES_256_KEY_SIZE + sizeof(whNvmMetadata))
+ uint8_t wrappedKey[WRAPPED_KEY_SIZE] = {0};
+ uint8_t unwrappedKey[AES_256_KEY_SIZE] = {0};
+ whNvmMetadata meta = {0};
+
+ printf(
+ "Test 9b: Local wrap key + Local wrapped key (No wrap key access)\n");
+
+ /* Client 1 caches a LOCAL wrapping key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"WrapKey_9b", sizeof("WrapKey_9b"), wrapKey,
+ sizeof(wrapKey), serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId));
+
+ /* Client 1 wraps a LOCAL key */
+ serverKeyId = DUMMY_KEYID_1; /* Use local wrapping key */
+ meta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client1Id, DUMMY_KEYID_2);
+ meta.len = sizeof(plainKey);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapRequest(client1, WC_CIPHER_AES_GCM,
+ serverKeyId, plainKey,
+ sizeof(plainKey), &meta));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapResponse(
+ client1, WC_CIPHER_AES_GCM, wrappedKey, sizeof(wrappedKey)));
+
+ /* Client 2 tries to unwrap - should fail (no wrapping key) */
+ ret = wh_Client_KeyUnwrapAndExportRequest(client2, WC_CIPHER_AES_GCM,
+ serverKeyId, wrappedKey,
+ sizeof(wrappedKey));
+ if (ret == 0) {
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ ret = wh_Client_KeyUnwrapAndExportResponse(client2, WC_CIPHER_AES_GCM,
+ &meta, unwrappedKey,
+ sizeof(unwrappedKey));
+ }
+
+ /* Should fail - Client 2 doesn't have the wrapping key */
+ WH_TEST_ASSERT_RETURN(ret != 0);
+
+ /* Clean up */
+ serverKeyId = DUMMY_KEYID_1;
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Local wrap key + Local wrapped key (No wrap key access)\n");
+
+ return 0;
+#undef WRAPPED_KEY_SIZE
+}
+
+/*
+ * Test 10a: Local wrapping key + Global wrapped key (Positive - Any cache
+ * global)
+ * - Client 1 caches a local wrapping key
+ * - Client 1 wraps a GLOBAL key (USER=0)
+ * - Client 1 unwraps and caches to global cache
+ * - Client 2 can read from global cache via KeyExport
+ */
+static int _testWrappedKey_LocalWrap_GlobalKey_AnyCacheGlobal(
+ whClientContext* client1, whServerContext* server1,
+ whClientContext* client2, whServerContext* server2)
+{
+ int ret;
+ whKeyId serverKeyId = DUMMY_KEYID_1; /* Local wrapping key */
+ uint8_t wrapKey[AES_256_KEY_SIZE] = "LocalWrapKey2Test10aXXXXXXXXX!";
+ uint8_t plainKey[AES_256_KEY_SIZE] = "GlobalPlainKey2Test10aXXXXXXX!";
+#define WRAPPED_KEY_SIZE (12 + 16 + AES_256_KEY_SIZE + sizeof(whNvmMetadata))
+ uint8_t wrappedKey[WRAPPED_KEY_SIZE] = {0};
+ uint8_t exportedKey[AES_256_KEY_SIZE] = {0};
+ uint8_t label[WH_NVM_LABEL_LEN];
+ uint16_t labelSz = sizeof(label);
+ uint16_t exportedSz = sizeof(exportedKey);
+ whNvmMetadata meta = {0};
+ whKeyId cachedKeyId = 0;
+
+ printf("Test 10a: Local wrap key + Global wrapped key (Cache global)\n");
+
+ /* Client 1 caches a LOCAL wrapping key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"WrapKey_10a", sizeof("WrapKey_10a"), wrapKey,
+ sizeof(wrapKey), serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId));
+
+ /* Client 1 wraps a GLOBAL key (USER=0) */
+ serverKeyId = DUMMY_KEYID_1; /* Use local wrapping key */
+ meta.id =
+ WH_CLIENT_KEYID_MAKE_WRAPPED_META(WH_KEYUSER_GLOBAL, DUMMY_KEYID_2);
+ meta.len = sizeof(plainKey);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapRequest(client1, WC_CIPHER_AES_GCM,
+ serverKeyId, plainKey,
+ sizeof(plainKey), &meta));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapResponse(
+ client1, WC_CIPHER_AES_GCM, wrappedKey, sizeof(wrappedKey)));
+
+ /* Client 1 unwraps and caches to global cache */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndCacheRequest(
+ client1, WC_CIPHER_AES_GCM, serverKeyId, wrappedKey,
+ sizeof(wrappedKey)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndCacheResponse(
+ client1, WC_CIPHER_AES_GCM, &cachedKeyId));
+
+ /* Client 2 reads from global cache via KeyExport */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportRequest(
+ client2, WH_CLIENT_KEYID_MAKE_WRAPPED_GLOBAL(cachedKeyId)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportResponse(
+ client2, label, labelSz, exportedKey, &exportedSz));
+
+ /* Verify the exported key matches the original */
+ WH_TEST_ASSERT_RETURN(0 == memcmp(exportedKey, plainKey, sizeof(plainKey)));
+
+ /* Clean up */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(
+ client2, WH_CLIENT_KEYID_MAKE_WRAPPED_GLOBAL(cachedKeyId)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client2));
+
+ serverKeyId = DUMMY_KEYID_1;
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Local wrap key + Global wrapped key (Cache global)\n");
+
+ (void)ret;
+ return 0;
+#undef WRAPPED_KEY_SIZE
+}
+
+/*
+ * Test 10b: Local wrapping key + Global wrapped key (Negative - No wrap key)
+ * - Client 1 caches a local wrapping key
+ * - Client 1 wraps a global key
+ * - Client 2 cannot unwrap (doesn't have wrapping key)
+ */
+static int _testWrappedKey_LocalWrap_GlobalKey_NonOwnerNoWrapKey(
+ whClientContext* client1, whServerContext* server1,
+ whClientContext* client2, whServerContext* server2)
+{
+ int ret;
+ whKeyId serverKeyId = DUMMY_KEYID_1; /* Local wrapping key */
+ uint8_t wrapKey[AES_256_KEY_SIZE] = "LocalWrapKey2Test10bXXXXXXXXX!";
+ uint8_t plainKey[AES_256_KEY_SIZE] = "GlobalPlainKey2Test10bXXXXXXX!";
+#define WRAPPED_KEY_SIZE (12 + 16 + AES_256_KEY_SIZE + sizeof(whNvmMetadata))
+ uint8_t wrappedKey[WRAPPED_KEY_SIZE] = {0};
+ uint8_t unwrappedKey[AES_256_KEY_SIZE] = {0};
+ whNvmMetadata meta = {0};
+
+ printf("Test 10b: Local wrap key + Global wrapped key (No wrap key)\n");
+
+ /* Client 1 caches a LOCAL wrapping key */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"WrapKey_10b", sizeof("WrapKey_10b"), wrapKey,
+ sizeof(wrapKey), serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId));
+
+ /* Client 1 wraps a GLOBAL key */
+ serverKeyId = DUMMY_KEYID_1; /* Use local wrapping key */
+ meta.id =
+ WH_CLIENT_KEYID_MAKE_WRAPPED_META(WH_KEYUSER_GLOBAL, DUMMY_KEYID_2);
+ meta.len = sizeof(plainKey);
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapRequest(client1, WC_CIPHER_AES_GCM,
+ serverKeyId, plainKey,
+ sizeof(plainKey), &meta));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapResponse(
+ client1, WC_CIPHER_AES_GCM, wrappedKey, sizeof(wrappedKey)));
+
+ /* Client 2 tries to unwrap - should fail (no wrapping key) */
+ ret = wh_Client_KeyUnwrapAndExportRequest(client2, WC_CIPHER_AES_GCM,
+ serverKeyId, wrappedKey,
+ sizeof(wrappedKey));
+ if (ret == 0) {
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server2));
+ ret = wh_Client_KeyUnwrapAndExportResponse(client2, WC_CIPHER_AES_GCM,
+ &meta, unwrappedKey,
+ sizeof(unwrappedKey));
+ }
+
+ /* Should fail - Client 2 doesn't have the wrapping key */
+ WH_TEST_ASSERT_RETURN(ret != 0);
+
+ /* Clean up */
+ serverKeyId = DUMMY_KEYID_1;
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictRequest(client1, serverKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Local wrap key + Global wrapped key (No wrap key)\n");
+
+ return 0;
+#undef WRAPPED_KEY_SIZE
+}
+#endif /* WOLFHSM_CFG_KEYWRAP */
+
+/* Helper function to run all global keys tests */
+static int _runGlobalKeysTests(whClientContext* client1,
+ whServerContext* server1,
+ whClientContext* client2,
+ whServerContext* server2)
+{
+ WH_TEST_RETURN_ON_FAIL(
+ _testGlobalKeyBasic(client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(
+ _testLocalKeyIsolation(client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(
+ _testMixedGlobalLocal(client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(
+ _testGlobalKeyNvmPersistence(client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(
+ _testGlobalKeyExportProtection(client1, server1, client2, server2));
+
+#ifdef WOLFHSM_CFG_DMA
+ WH_TEST_RETURN_ON_FAIL(
+ _testGlobalKeyDma(client1, server1, client2, server2));
+#endif
+
+#ifdef WOLFHSM_CFG_KEYWRAP
+ WH_TEST_RETURN_ON_FAIL(
+ _testGlobalKeyWrapExport(client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(
+ _testGlobalKeyUnwrapCache(client1, server1, client2, server2));
+
+ /* Comprehensive wrapped key access control tests */
+ WH_TEST_RETURN_ON_FAIL(_testWrappedKey_GlobalWrap_GlobalKey_Positive(
+ client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(_testWrappedKey_GlobalWrap_GlobalKey_NonExportable(
+ client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(_testWrappedKey_GlobalWrap_LocalKey_OwnerExport(
+ client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(_testWrappedKey_GlobalWrap_LocalKey_NonOwnerFails(
+ client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(_testWrappedKey_LocalWrap_LocalKey_SameOwner(
+ client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(
+ _testWrappedKey_LocalWrap_LocalKey_NoAccessWithoutWrapKey(
+ client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(_testWrappedKey_LocalWrap_GlobalKey_AnyCacheGlobal(
+ client1, server1, client2, server2));
+
+ WH_TEST_RETURN_ON_FAIL(
+ _testWrappedKey_LocalWrap_GlobalKey_NonOwnerNoWrapKey(
+ client1, server1, client2, server2));
+#endif
+
+ printf("All Global Keys Tests PASSED ===\n");
+ return 0;
+}
+
+#endif /* WOLFHSM_CFG_GLOBAL_KEYS */
+
+/* ============================================================================
+ * MULTI-CLIENT SEQUENTIAL TEST FRAMEWORK
+ * ========================================================================== */
+
+/* Generic setup/teardown for multi-client sequential tests using shared memory
+ */
+static int whTest_MultiClientSequential(void)
+{
+ int ret = 0;
+
+ /* Transport memory configurations for both clients */
+ static uint8_t req1[BUFFER_SIZE];
+ static uint8_t resp1[BUFFER_SIZE];
+ whTransportMemConfig tmcf1[1] = {{
+ .req = (whTransportMemCsr*)req1,
+ .req_size = sizeof(req1),
+ .resp = (whTransportMemCsr*)resp1,
+ .resp_size = sizeof(resp1),
+ }};
+
+ static uint8_t req2[BUFFER_SIZE];
+ static uint8_t resp2[BUFFER_SIZE];
+ whTransportMemConfig tmcf2[1] = {{
+ .req = (whTransportMemCsr*)req2,
+ .req_size = sizeof(req2),
+ .resp = (whTransportMemCsr*)resp2,
+ .resp_size = sizeof(resp2),
+ }};
+
+ /* Client 1 configuration */
+ whTransportClientCb tccb1[1] = {WH_TRANSPORT_MEM_CLIENT_CB};
+ whTransportMemClientContext tmcc1[1] = {0};
+ whCommClientConfig cc_conf1[1] = {{
+ .transport_cb = tccb1,
+ .transport_context = (void*)tmcc1,
+ .transport_config = (void*)tmcf1,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
+ .connect_cb = _connectCb1,
+ }};
+ whClientContext client1[1] = {0};
+ whClientConfig c_conf1[1] = {{
+ .comm = cc_conf1,
+ }};
+
+ /* Client 2 configuration */
+ whTransportClientCb tccb2[1] = {WH_TRANSPORT_MEM_CLIENT_CB};
+ whTransportMemClientContext tmcc2[1] = {0};
+ whCommClientConfig cc_conf2[1] = {{
+ .transport_cb = tccb2,
+ .transport_context = (void*)tmcc2,
+ .transport_config = (void*)tmcf2,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID + 1,
+ .connect_cb = _connectCb2,
+ }};
+ whClientContext client2[1] = {0};
+ whClientConfig c_conf2[1] = {{
+ .comm = cc_conf2,
+ }};
+
+ /* Shared NVM configuration using RamSim Flash */
+ static uint8_t memory[FLASH_RAM_SIZE] = {0};
+ whFlashRamsimCtx fc[1] = {0};
+ whFlashRamsimCfg fc_conf[1] = {{
+ .size = FLASH_RAM_SIZE,
+ .sectorSize = FLASH_SECTOR_SIZE,
+ .pageSize = FLASH_PAGE_SIZE,
+ .erasedByte = ~(uint8_t)0,
+ .memory = memory,
+ }};
+ const whFlashCb fcb[1] = {WH_FLASH_RAMSIM_CB};
+
+ whNvmFlashConfig nf_conf[1] = {{
+ .cb = fcb,
+ .context = fc,
+ .config = fc_conf,
+ }};
+ whNvmFlashContext nfc[1] = {0};
+ whNvmCb nfcb[1] = {WH_NVM_FLASH_CB};
+
+ whNvmConfig n_conf[1] = {{
+ .cb = nfcb,
+ .context = nfc,
+ .config = nf_conf,
+ }};
+ whNvmContext nvm[1] = {0}; /* Shared NVM */
+
+#if !defined(WOLFHSM_CFG_NO_CRYPTO)
+ /* Crypto contexts for both servers */
+ whServerCryptoContext crypto1[1] = {{.devId = INVALID_DEVID}};
+ whServerCryptoContext crypto2[1] = {{.devId = INVALID_DEVID}};
+#endif
+
+ /* Server 1 configuration */
+ whTransportServerCb tscb1[1] = {WH_TRANSPORT_MEM_SERVER_CB};
+ whTransportMemServerContext tmsc1[1] = {0};
+ whCommServerConfig cs_conf1[1] = {{
+ .transport_cb = tscb1,
+ .transport_context = (void*)tmsc1,
+ .transport_config = (void*)tmcf1,
+ .server_id = 101,
+ }};
+ whServerConfig s_conf1[1] = {{
+ .comm_config = cs_conf1,
+ .nvm = nvm, /* Shared NVM */
+#if !defined(WOLFHSM_CFG_NO_CRYPTO)
+ .crypto = crypto1,
+#endif
+ }};
+ whServerContext server1[1] = {0};
+
+ /* Server 2 configuration */
+ whTransportServerCb tscb2[1] = {WH_TRANSPORT_MEM_SERVER_CB};
+ whTransportMemServerContext tmsc2[1] = {0};
+ whCommServerConfig cs_conf2[1] = {{
+ .transport_cb = tscb2,
+ .transport_context = (void*)tmsc2,
+ .transport_config = (void*)tmcf2,
+ .server_id = 102,
+ }};
+ whServerConfig s_conf2[1] = {{
+ .comm_config = cs_conf2,
+ .nvm = nvm, /* Shared NVM */
+
+#if !defined(WOLFHSM_CFG_NO_CRYPTO)
+ .crypto = crypto2,
+#endif
+ }};
+ whServerContext server2[1] = {0};
+
+ /* Expose server contexts to connect callbacks */
+ testServer1 = server1;
+ testServer2 = server2;
+
+#if !defined(WOLFHSM_CFG_NO_CRYPTO)
+ /* Initialize wolfCrypt */
+ ret = wolfCrypt_Init();
+ if (ret != 0)
+ return ret;
+#endif
+
+ /* Initialize NVM (shared) */
+ ret = wh_Nvm_Init(nvm, n_conf);
+ if (ret != 0)
+ return ret;
+
+#if !defined(WOLFHSM_CFG_NO_CRYPTO)
+ /* Initialize RNGs */
+ ret = wc_InitRng_ex(crypto1->rng, NULL, crypto1->devId);
+ if (ret != 0)
+ return ret;
+
+ ret = wc_InitRng_ex(crypto2->rng, NULL, crypto2->devId);
+ if (ret != 0)
+ return ret;
+#endif
+
+ /* Initialize servers */
+ ret = wh_Server_Init(server1, s_conf1);
+ if (ret != 0)
+ return ret;
+
+ ret = wh_Server_Init(server2, s_conf2);
+ if (ret != 0)
+ return ret;
+
+ /* Initialize clients */
+ ret = wh_Client_Init(client1, c_conf1);
+ if (ret != 0)
+ return ret;
+
+ ret = wh_Client_Init(client2, c_conf2);
+ if (ret != 0)
+ return ret;
+
+ /* Initialize communication for both clients */
+ uint32_t client_id = 0;
+ uint32_t server_id = 0;
+
+ ret = wh_Client_CommInitRequest(client1);
+ if (ret != 0)
+ return ret;
+ ret = wh_Server_HandleRequestMessage(server1);
+ if (ret != 0)
+ return ret;
+ ret = wh_Client_CommInitResponse(client1, &client_id, &server_id);
+ if (ret != 0)
+ return ret;
+
+ ret = wh_Client_CommInitRequest(client2);
+ if (ret != 0)
+ return ret;
+ ret = wh_Server_HandleRequestMessage(server2);
+ if (ret != 0)
+ return ret;
+ ret = wh_Client_CommInitResponse(client2, &client_id, &server_id);
+ if (ret != 0)
+ return ret;
+
+ printf("=== Multi-Client Sequential Tests Begin ===\n");
+ /* Run test suites that require multiple clients */
+#ifdef WOLFHSM_CFG_GLOBAL_KEYS
+ WH_TEST_RETURN_ON_FAIL(
+ _runGlobalKeysTests(client1, server1, client2, server2));
+#endif
+
+ /* Future test suites here */
+
+ /* Cleanup */
+ wh_Client_Cleanup(client1);
+ wh_Client_Cleanup(client2);
+ wh_Server_Cleanup(server1);
+ wh_Server_Cleanup(server2);
+#if !defined(WOLFHSM_CFG_NO_CRYPTO)
+ wc_FreeRng(crypto1->rng);
+ wc_FreeRng(crypto2->rng);
+ wolfCrypt_Cleanup();
+#endif
+ wh_Nvm_Cleanup(nvm);
+
+ printf("=== Multi-Client Sequential Tests Complete ===\n");
+
+ return 0;
+}
+
+/* ============================================================================
+ * PUBLIC API
+ * ========================================================================== */
+
+/* Main entry point for multi-client tests */
+int whTest_MultiClient(void)
+{
+ return whTest_MultiClientSequential();
+}
+
+#endif /* WOLFHSM_CFG_ENABLE_CLIENT && WOLFHSM_CFG_ENABLE_SERVER */
diff --git a/test/wh_test_multiclient.h b/test/wh_test_multiclient.h
new file mode 100644
index 000000000..c1b39d7e4
--- /dev/null
+++ b/test/wh_test_multiclient.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 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 .
+ */
+#ifndef WH_TEST_MULTICLIENT_H_
+#define WH_TEST_MULTICLIENT_H_
+
+/**
+ * @brief Multi-client test framework and test suite
+ *
+ * This test module provides a framework for testing features that require
+ * multiple clients connecting to separate servers sharing a common NVM context.
+ *
+ * The framework provides generic setup/teardown for:
+ * - Two client contexts with separate transport memory configs
+ * - Two server contexts sharing a single NVM context
+ * - Shared flash/NVM and crypto initialization
+ * - Sequential single-threaded test execution using memory transport
+ *
+ * Current test suites:
+ * - Global keys: Tests shared key functionality across multiple clients
+ *
+ * Future test suites can be added for features like:
+ * - Access control policies
+ * - Shared counter synchronization
+ * - Cross-client key operations
+ *
+ * @return 0 on success, error code on failure
+ */
+int whTest_MultiClient(void);
+
+#endif /* WH_TEST_MULTICLIENT_H_ */
diff --git a/test/wh_test_she.c b/test/wh_test_she.c
index 7e5777820..64e3cc6a1 100644
--- a/test/wh_test_she.c
+++ b/test/wh_test_she.c
@@ -523,7 +523,7 @@ static int wh_ClientServer_MemThreadTest(void)
.transport_cb = tccb,
.transport_context = (void*)tmcc,
.transport_config = (void*)tmcf,
- .client_id = 1,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
}};
whClientConfig c_conf[1] = {{
.comm = cc_conf,
diff --git a/test/wh_test_wolfcrypt_test.c b/test/wh_test_wolfcrypt_test.c
index bf963a0de..50494a9de 100644
--- a/test/wh_test_wolfcrypt_test.c
+++ b/test/wh_test_wolfcrypt_test.c
@@ -196,7 +196,7 @@ static int wh_ClientServer_MemThreadTest(void)
.transport_cb = tccb,
.transport_context = (void*)tmcc,
.transport_config = (void*)tmcf,
- .client_id = 123,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
}};
whClientConfig c_conf[1] = {{
.comm = cc_conf,
diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h
index 87f082ea0..f5ca405fa 100644
--- a/wolfhsm/wh_client.h
+++ b/wolfhsm/wh_client.h
@@ -2485,4 +2485,102 @@ int wh_Client_CertVerifyAcertDma(whClientContext* c, const void* cert,
#endif /* WOLFHSM_CFG_DMA */
+/*
+ * @brief Client-side keyId manipulation API
+ *
+ * This section defines the client-facing API for working with key identifiers.
+ * Clients use simple numeric IDs (0-255) with optional flags to indicate
+ * global or wrapped keys. The server translates these to full internal
+ * representations with TYPE/USER/ID fields.
+ *
+ * Client keyId usage:
+ * - Regular keys: Simple numeric ID (e.g., 5)
+ * - Global keys: ID with WH_CLIENT_KEYID_GLOBAL_FLAG set
+ * - Wrapped keys: ID with WH_CLIENT_KEYID_WRAPPED_FLAG set
+ * - Wrapped metadata: Must use full WH_MAKE_KEYID() construction including type
+ * and metadata when populating the ID field in metadata to be wrapped
+ */
+
+/* Client-facing key flags (temporary, stripped by server during translation) */
+
+/* Bit 8: Client-to-server signal for global key (shared across all clients) */
+#define WH_CLIENT_KEYID_GLOBAL_FLAG ((whKeyId)0x0100)
+
+/* Bit 9: Client-to-server signal for wrapped key */
+#define WH_CLIENT_KEYID_WRAPPED_FLAG ((whKeyId)0x0200)
+
+/* Combined mask of all client-facing flags */
+#define WH_CLIENT_KEYID_FLAGS_MASK \
+ (WH_CLIENT_KEYID_GLOBAL_FLAG | WH_CLIENT_KEYID_WRAPPED_FLAG)
+
+/**
+ * @brief Mark a key ID as global (shared across all clients)
+ *
+ * Sets the global flag in a client keyId to indicate to the server that this
+ * key should be stored/accessed as a global key. The server will translate this
+ * to USER=0 encoding.
+ *
+ * @param _id The key ID (0-255)
+ * @return keyId with global flag set
+ *
+ * Example:
+ * whKeyId globalKey = WH_CLIENT_KEYID_MAKE_GLOBAL(5);
+ * wh_Client_KeyCache(client, globalKey, ...); // Stored as global key
+ */
+#define WH_CLIENT_KEYID_MAKE_GLOBAL(_id) ((_id) | WH_CLIENT_KEYID_GLOBAL_FLAG)
+
+/**
+ * @brief Mark a key ID as wrapped
+ *
+ * Sets the wrapped flag in a client keyId to indicate to the server that this
+ * is a wrapped key identifier. The server will translate this to
+ * KEYTYPE=WH_KEYTYPE_WRAPPED.
+ *
+ * @param _id The key ID (0-255)
+ * @return keyId with wrapped flag set
+ *
+ * Example:
+ * whKeyId wrappedKey = WH_CLIENT_KEYID_MAKE_WRAPPED(2);
+ * wh_Client_KeyExportRequest(client, wrappedKey, ...);
+ */
+#define WH_CLIENT_KEYID_MAKE_WRAPPED(_id) ((_id) | WH_CLIENT_KEYID_WRAPPED_FLAG)
+
+/**
+ * @brief Mark a key ID as both global and wrapped
+ *
+ * Convenience macro that sets both global and wrapped flags.
+ * The server will translate this to KEYTYPE=WH_KEYTYPE_WRAPPED, USER=0.
+ *
+ * @param _id The key ID (0-255)
+ * @return keyId with global and wrapped flags set
+ *
+ * Example:
+ * whKeyId globalWrappedKey = WH_CLIENT_KEYID_MAKE_WRAPPED_GLOBAL(2);
+ * wh_Client_AesSetKeyId(aes, globalWrappedKey);
+ */
+#define WH_CLIENT_KEYID_MAKE_WRAPPED_GLOBAL(_id) \
+ ((_id) | WH_CLIENT_KEYID_GLOBAL_FLAG | WH_CLIENT_KEYID_WRAPPED_FLAG)
+
+/**
+ * @brief Construct wrapped key metadata ID with explicit ownership
+ *
+ * Creates the full internal keyId representation for wrapped key metadata.
+ * This is used when constructing the metadata structure that will be
+ * encrypted in the wrapped key blob. The user field specifies which
+ * client(s) can unwrap the key.
+ *
+ * @param _clientId Client ID that can unwrap (or WH_KEYUSER_GLOBAL for global)
+ * @param _id The key ID (0-255)
+ * @return Full keyId with TYPE=WH_KEYTYPE_WRAPPED, USER=_clientId, ID=_id
+ *
+ * Example:
+ * whNvmMetadata meta = {
+ * .id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(WH_KEYUSER_GLOBAL, 5),
+ * .len = keySize,
+ * };
+ * wh_Client_KeyWrapRequest(client, ..., &meta, ...);
+ */
+#define WH_CLIENT_KEYID_MAKE_WRAPPED_META(_clientId, _id) \
+ WH_MAKE_KEYID(WH_KEYTYPE_WRAPPED, (_clientId), (_id))
+
#endif /* !WOLFHSM_WH_CLIENT_H_ */
diff --git a/wolfhsm/wh_common.h b/wolfhsm/wh_common.h
index 7d079d213..74be57a7a 100644
--- a/wolfhsm/wh_common.h
+++ b/wolfhsm/wh_common.h
@@ -24,54 +24,13 @@
#ifndef WOLFHSM_WH_COMMON_H_
#define WOLFHSM_WH_COMMON_H_
+#include
+
/* Pick up compile-time configuration */
#include "wolfhsm/wh_settings.h"
-#include
-
-/** Non-volatile counters */
-/* HSM Counter identifier type. */
-typedef uint16_t whCounterId;
-#define WH_COUNTER_ID_INVALID ((whCounterId)0)
-
-
-/** Key Management */
-/* HSM key identifier type. Top nibble identifies key type/location */
-typedef uint16_t whKeyId;
-
-/* KeyId Constants */
-#define WH_KEYID_ERASED 0x0000
-#define WH_KEYID_IDMAX 0xFF
-
-/* Key Masks */
-#define WH_KEYID_MASK 0x00FF
-#define WH_KEYID_SHIFT 0
-#define WH_KEYUSER_MASK 0x0F00
-#define WH_KEYUSER_SHIFT 8
-#define WH_KEYTYPE_MASK 0xF000
-#define WH_KEYTYPE_SHIFT 12
-
-/* Macro to construct a keyid */
-#define WH_MAKE_KEYID(_type, _user, _id) \
- ((whKeyId)( \
- (((_type) << WH_KEYTYPE_SHIFT) & WH_KEYTYPE_MASK) | \
- (((_user) << WH_KEYUSER_SHIFT) & WH_KEYUSER_MASK) | \
- (((_id) << WH_KEYID_SHIFT) & WH_KEYID_MASK)))
-#define WH_KEYID_TYPE(_kid) (((_kid) & WH_KEYTYPE_MASK) >> WH_KEYTYPE_SHIFT)
-#define WH_KEYID_USER(_kid) (((_kid) & WH_KEYUSER_MASK) >> WH_KEYUSER_SHIFT)
-#define WH_KEYID_ID(_kid) (((_kid) & WH_KEYID_MASK) >> WH_KEYID_SHIFT)
-
-#define WH_KEYID_ISERASED(_kid) (WH_KEYID_ID(_kid) == WH_KEYID_ERASED)
-
-/* Key Types */
-#define WH_KEYTYPE_NVM 0x0 /* Ordinary NvmId. Not a key */
-#define WH_KEYTYPE_CRYPTO 0x1 /* Key for Crypto operations */
-#define WH_KEYTYPE_SHE 0x2 /* SKE keys are AES or CMAC binary arrays */
-#define WH_KEYTYPE_COUNTER 0x3 /* Monotonic counter */
-
-/* Convert a keyId to a pointer to be stored in wolfcrypt devctx */
-#define WH_KEYID_TO_DEVCTX(_k) ((void*)((intptr_t)(_k)))
-#define WH_DEVCTX_TO_KEYID(_d) ((whKeyId)((intptr_t)(_d)))
+/* Key management types and helpers */
+#include "wolfhsm/wh_keyid.h"
/** NVM Management */
/* HSM NVM object identifier type. */
diff --git a/wolfhsm/wh_error.h b/wolfhsm/wh_error.h
index 35ad26706..c2b7682f1 100644
--- a/wolfhsm/wh_error.h
+++ b/wolfhsm/wh_error.h
@@ -41,8 +41,8 @@ enum WH_ERROR_ENUM {
WH_ERROR_BUFFER_SIZE = -2006, /* Generic buffer size mismatch. Buffer
* length is not what was expected */
WH_ERROR_NOHANDLER = -2007, /* No customcb handler registered */
- WH_ERROR_NOTIMPL = -2008, /* Functionality not implemented given the
- compile-time configuration */
+ WH_ERROR_NOTIMPL = -2008, /* Functionality not implemented given the
+ compile-time configuration */
/* NVM and keystore specific status returns */
WH_ERROR_LOCKED = -2100, /* Unlock and retry if necessary */
diff --git a/wolfhsm/wh_keyid.h b/wolfhsm/wh_keyid.h
new file mode 100644
index 000000000..28831475b
--- /dev/null
+++ b/wolfhsm/wh_keyid.h
@@ -0,0 +1,100 @@
+/*
+ * 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_keyid.h
+ *
+ * KeyId type definitions, constants, and helper functions for wolfHSM
+ */
+
+#ifndef WOLFHSM_WH_KEYID_H_
+#define WOLFHSM_WH_KEYID_H_
+
+/* Pick up compile-time configuration */
+#include "wolfhsm/wh_settings.h"
+
+#include
+
+/** Key Management */
+/* HSM key identifier type. Top nibble identifies key type/location */
+typedef uint16_t whKeyId;
+
+/* KeyId Constants */
+#define WH_KEYID_ERASED 0x0000
+#define WH_KEYID_IDMAX 0xFF
+
+/* Key Masks */
+#define WH_KEYID_MASK 0x00FF
+#define WH_KEYID_SHIFT 0
+#define WH_KEYUSER_MASK 0x0F00
+#define WH_KEYUSER_SHIFT 8
+#define WH_KEYTYPE_MASK 0xF000
+#define WH_KEYTYPE_SHIFT 12
+
+/* Client-facing key flags are defined in wh_client.h:
+ * - WH_CLIENT_KEYID_GLOBAL_FLAG
+ * - WH_CLIENT_KEYID_WRAPPED_FLAG
+ */
+
+/* Macro to construct a server-unique keyid */
+#define WH_MAKE_KEYID(_type, _user, _id) \
+ ((whKeyId)((((_type) << WH_KEYTYPE_SHIFT) & WH_KEYTYPE_MASK) | \
+ (((_user) << WH_KEYUSER_SHIFT) & WH_KEYUSER_MASK) | \
+ (((_id) << WH_KEYID_SHIFT) & WH_KEYID_MASK)))
+#define WH_KEYID_TYPE(_kid) (((_kid)&WH_KEYTYPE_MASK) >> WH_KEYTYPE_SHIFT)
+#define WH_KEYID_USER(_kid) (((_kid)&WH_KEYUSER_MASK) >> WH_KEYUSER_SHIFT)
+#define WH_KEYID_ID(_kid) (((_kid)&WH_KEYID_MASK) >> WH_KEYID_SHIFT)
+
+#define WH_KEYID_ISERASED(_kid) (WH_KEYID_ID(_kid) == WH_KEYID_ERASED)
+#define WH_KEYID_ISWRAPPED(_kid) (WH_KEYID_TYPE(_kid) == WH_KEYTYPE_WRAPPED)
+
+/* Reserve USER=0 for global keys in the internal keyId encoding.
+ * This is server-internal; clients use WH_CLIENT_KEYID_GLOBAL_FLAG from
+ * wh_client.h */
+#define WH_KEYUSER_GLOBAL 0
+
+/* Key Types */
+#define WH_KEYTYPE_NVM 0x0 /* Ordinary NvmId. Not a key */
+#define WH_KEYTYPE_CRYPTO 0x1 /* Key for Crypto operations */
+#define WH_KEYTYPE_SHE 0x2 /* SKE keys are AES or CMAC binary arrays */
+#define WH_KEYTYPE_COUNTER 0x3 /* Monotonic counter */
+#define WH_KEYTYPE_WRAPPED 0x4 /* Wrapped key metadata */
+
+/* Convert a keyId to a pointer to be stored in wolfcrypt devctx */
+#define WH_KEYID_TO_DEVCTX(_k) ((void*)((intptr_t)(_k)))
+#define WH_DEVCTX_TO_KEYID(_d) ((whKeyId)((intptr_t)(_d)))
+
+/**
+ * @brief Translate client keyId (with flags) to server keyId encoding
+ *
+ * Translates client-facing keyId format (ID + flags) to server-internal format
+ * (TYPE + USER + ID). Client flags are:
+ * - 0x0100 (bit 8): WH_CLIENT_KEYID_GLOBAL_FLAG → USER = 0
+ * - 0x0200 (bit 9): WH_CLIENT_KEYID_WRAPPED_FLAG → TYPE = WH_KEYTYPE_WRAPPED
+ *
+ * @param type Key type to use as the TYPE field. Input value is ignored and
+ * WH_KEYTYPE_WRAPPED is used if the input clientId has the
+ * WH_CLIENT_KEYID_WRAPPED flag set.
+ * @param clientId Client identifier to use as USER field
+ * @param reqId Requested keyId from client (may include flags)
+ * @return Server-internal keyId with TYPE, USER, and ID fields properly set.
+ */
+whKeyId wh_KeyId_TranslateClient(uint16_t type, uint16_t clientId,
+ whKeyId reqId);
+
+#endif /* !WOLFHSM_WH_KEYID_H_ */
diff --git a/wolfhsm/wh_nvm.h b/wolfhsm/wh_nvm.h
index a0c8cefd1..6e911f90e 100644
--- a/wolfhsm/wh_nvm.h
+++ b/wolfhsm/wh_nvm.h
@@ -42,7 +42,8 @@
#include
-#include "wolfhsm/wh_common.h" /* For whNvm types */
+#include "wolfhsm/wh_common.h" /* For whNvm types */
+#include "wolfhsm/wh_server_cache.h" /* For whKeyCacheContext */
typedef struct {
int (*Init)(void* context, const void *config);
@@ -92,6 +93,9 @@ typedef struct {
typedef struct whNvmContext_t {
whNvmCb *cb;
void* context;
+#if !defined(WOLFHSM_CFG_NO_CRYPTO) && defined(WOLFHSM_CFG_GLOBAL_KEYS)
+ whKeyCacheContext globalCache; /* Global key cache */
+#endif
} whNvmContext;
/* Simple helper configuration structure associated with an NVM instance */
diff --git a/wolfhsm/wh_server.h b/wolfhsm/wh_server.h
index 664fcd993..000a1307b 100644
--- a/wolfhsm/wh_server.h
+++ b/wolfhsm/wh_server.h
@@ -38,6 +38,7 @@ typedef struct whServerContext_t whServerContext;
#include "wolfhsm/wh_common.h"
#include "wolfhsm/wh_comm.h"
+#include "wolfhsm/wh_server_cache.h"
#include "wolfhsm/wh_nvm.h"
#include "wolfhsm/wh_message_customcb.h"
#ifdef WOLFHSM_CFG_DMA
@@ -62,18 +63,6 @@ typedef struct whServerContext_t whServerContext;
#ifndef WOLFHSM_CFG_NO_CRYPTO
-/** Server crypto context and resource allocation */
-typedef struct whServerCacheSlot {
- uint8_t commited;
- whNvmMetadata meta[1];
- uint8_t buffer[WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE];
-} whServerCacheSlot;
-
-typedef struct whServerBigCacheSlot {
- uint8_t commited;
- whNvmMetadata meta[1];
- uint8_t buffer[WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE];
-} whServerBigCacheSlot;
typedef struct whServerCryptoContext {
int devId;
@@ -184,8 +173,7 @@ struct whServerContext_t {
whCommServer comm[1];
#ifndef WOLFHSM_CFG_NO_CRYPTO
whServerCryptoContext* crypto;
- whServerCacheSlot cache[WOLFHSM_CFG_SERVER_KEYCACHE_COUNT];
- whServerBigCacheSlot bigCache[WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT];
+ whKeyCacheContext localCache; /* Unified cache structure */
#ifdef WOLFHSM_CFG_SHE_EXTENSION
whServerSheContext* she;
#endif
diff --git a/wolfhsm/wh_server_cache.h b/wolfhsm/wh_server_cache.h
new file mode 100644
index 000000000..3046a8451
--- /dev/null
+++ b/wolfhsm/wh_server_cache.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 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_cache.h
+ *
+ * Exists as a separate header so it can be consumed by server, server keystore, and NVM
+ * layer without creating circular dependencies
+ */
+
+#ifndef WOLFHSM_WH_SERVER_CACHE_H_
+#define WOLFHSM_WH_SERVER_CACHE_H_
+
+/* Pick up compile-time configuration */
+#include "wolfhsm/wh_settings.h"
+#include "wolfhsm/wh_common.h"
+
+#ifndef WOLFHSM_CFG_NO_CRYPTO
+
+/** Server cache slot structures */
+typedef struct whServerCacheSlot {
+ uint8_t committed;
+ whNvmMetadata meta[1];
+ uint8_t buffer[WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE];
+} whServerCacheSlot;
+
+typedef struct whServerBigCacheSlot {
+ uint8_t committed;
+ whNvmMetadata meta[1];
+ uint8_t buffer[WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE];
+} whServerBigCacheSlot;
+
+/**
+ * @brief Unified key cache context
+ *
+ * Holds both regular and big cache arrays. Used for client-local caches
+ * (embedded in whServerContext) and global caches (embedded in whNvmContext
+ * when WOLFHSM_CFG_GLOBAL_KEYS is enabled).
+ */
+typedef struct whKeyCacheContext_t {
+ whServerCacheSlot cache[WOLFHSM_CFG_SERVER_KEYCACHE_COUNT];
+ whServerBigCacheSlot bigCache[WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT];
+} whKeyCacheContext;
+
+#endif /* !WOLFHSM_CFG_NO_CRYPTO */
+
+#endif /* !WOLFHSM_WH_SERVER_CACHE_H_ */
diff --git a/wolfhsm/wh_server_keystore.h b/wolfhsm/wh_server_keystore.h
index 155cf2a55..267572567 100644
--- a/wolfhsm/wh_server_keystore.h
+++ b/wolfhsm/wh_server_keystore.h
@@ -49,16 +49,19 @@ int wh_Server_KeystoreGetUniqueId(whServerContext* server, whNvmId* inout_id);
* @brief Find an available cache slot for the specified key size
*
* Searches for an empty slot or a slot with a committed key that can be
- * evicted. Returns the slot's buffer (zeroed) and metadata.
+ * evicted. Returns the slot's buffer (zeroed) and metadata. Routes to the
+ * appropriate cache (global or local) based on keyId.
*
* @param[in] server Server context
+ * @param[in] keyId Key ID (used to route to correct cache)
* @param[in] keySz Size of the key in bytes
* @param[out] outBuf Pointer to the cache buffer
* @param[out] outMeta Pointer to the metadata structure
* @return 0 on success, error code on failure
*/
-int wh_Server_KeystoreGetCacheSlot(whServerContext* server, uint16_t keySz,
- uint8_t** outBuf, whNvmMetadata** outMeta);
+int wh_Server_KeystoreGetCacheSlot(whServerContext* server, whKeyId keyId,
+ uint16_t keySz, uint8_t** outBuf,
+ whNvmMetadata** outMeta);
/**
* @brief Cache a key in server memory
@@ -186,4 +189,16 @@ int wh_Server_KeystoreCacheKeyDma(whServerContext* server, whNvmMetadata* meta,
int wh_Server_KeystoreExportKeyDma(whServerContext* server, whKeyId keyId,
uint64_t keyAddr, uint64_t keySz,
whNvmMetadata* outMeta);
+
+/**
+ * @brief Query whether a key identifier refers to wrapped material
+ *
+ * @param[in] server Server context (unused, reserved for parity)
+ * @param[in] keyId Key identifier to inspect
+ * @param[out] outIsWrapped Optional pointer receiving 1 if wrapped, 0 otherwise
+ * @return 0 on success, error code on failure
+ */
+int wh_Server_KeystoreIsWrappedKey(whServerContext* server, whKeyId keyId,
+ int* outIsWrapped);
+
#endif /* !WOLFHSM_WH_SERVER_KEYSTORE_H_ */
diff --git a/wolfhsm/wh_settings.h b/wolfhsm/wh_settings.h
index 015c2d8b3..f92047d34 100644
--- a/wolfhsm/wh_settings.h
+++ b/wolfhsm/wh_settings.h
@@ -36,6 +36,10 @@
* WOLFHSM_CFG_SHE_EXTENSION - If defined, include AutoSAR SHE functionality
* Default: Not defined
*
+ * WOLFHSM_CFG_GLOBAL_KEYS - If defined, enable global key support allowing
+ * keys to be shared across multiple clients
+ * Default: Not defined
+ *
* WOLFHSM_CFG_KEYWRAP - If defined, include the key wrap functionality
* Default: Not defined
*
diff --git a/wolfhsm/wh_transport_mem.h b/wolfhsm/wh_transport_mem.h
index 924703d26..032e95185 100644
--- a/wolfhsm/wh_transport_mem.h
+++ b/wolfhsm/wh_transport_mem.h
@@ -70,7 +70,7 @@
* .transport_cb = tmccb,
* .transport_context = tmcc,
* .transport_config = tmcfg,
- * .client_id = 1234,
+ * .client_id = 1
* }};
* whCommClient cc[1] = {0};
* wh_CommClient_Init(cc, ccc);
@@ -81,7 +81,7 @@
* .transport_cb = tmscb,
* .transport_context = tmsc,
* .transport_config = tmcfg,
- * .server_id = 5678,
+ * .server_id = 2,
* }};
* whCommServer cs[1] = {0};
* wh_CommServer_Init(cs, csc);
From e8663e319ef2e8bbd3ff85dc908e2bb1ab3d94d4 Mon Sep 17 00:00:00 2001
From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com>
Date: Tue, 28 Oct 2025 15:06:16 -0600
Subject: [PATCH 2/5] support cache probe on freshen
---
src/wh_server_keystore.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c
index 941543573..a62ef06cf 100644
--- a/src/wh_server_keystore.c
+++ b/src/wh_server_keystore.c
@@ -527,6 +527,13 @@ int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId,
ret = _FindInCache(server, keyId, &foundIndex, &foundBigIndex, outBuf,
outMeta);
if (ret != WH_ERROR_OK) {
+ /* For wrapped keys, just probe the cache and error if not found. We
+ * don't support automatically unwrapping and caching outside of the
+ * keywrap API */
+ if (WH_KEYID_TYPE(keyId) == WH_KEYTYPE_WRAPPED) {
+ return WH_ERROR_NOTFOUND;
+ }
+
/* Not in cache. Check if it is in NVM */
ret = wh_Nvm_GetMetadata(server->nvm, keyId, tmpMeta);
if (ret == WH_ERROR_OK) {
@@ -582,7 +589,9 @@ int wh_Server_KeystoreReadKey(whServerContext* server, whKeyId keyId,
return 0;
}
- /* Prevent exposing wrapped blobs through the unwrapped read path */
+ /* For wrapped keys, just probe the cache and error if not found. We
+ * don't support automatically unwrapping and caching outside of the
+ * keywrap API */
if (WH_KEYID_TYPE(keyId) == WH_KEYTYPE_WRAPPED) {
return WH_ERROR_NOTFOUND;
}
From 37f0c8bfec03d649b6cbab88bb4dd41af8b230dc Mon Sep 17 00:00:00 2001
From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com>
Date: Tue, 28 Oct 2025 15:57:19 -0600
Subject: [PATCH 3/5] keyId flag preservation
---
src/wh_keyid.c | 21 +++++++
src/wh_server_cert.c | 10 ++--
src/wh_server_crypto.c | 14 ++---
src/wh_server_keystore.c | 13 ++---
test/wh_test_multiclient.c | 111 +++++++++++++++++++++++++++++++++++++
wolfhsm/wh_keyid.h | 16 ++++++
6 files changed, 167 insertions(+), 18 deletions(-)
diff --git a/src/wh_keyid.c b/src/wh_keyid.c
index c2427ced0..031b7daab 100644
--- a/src/wh_keyid.c
+++ b/src/wh_keyid.c
@@ -46,3 +46,24 @@ whKeyId wh_KeyId_TranslateClient(uint16_t type, uint16_t clientId,
return WH_MAKE_KEYID(type, user, id);
}
+
+whKeyId wh_KeyId_ToClient(whKeyId serverId)
+{
+ whKeyId clientId = WH_KEYID_ID(serverId);
+
+#ifdef WOLFHSM_CFG_GLOBAL_KEYS
+ /* Convert USER=0 to global flag (bit 8: 0x0100) */
+ if (WH_KEYID_USER(serverId) == WH_KEYUSER_GLOBAL) {
+ clientId |= 0x0100; /* WH_CLIENT_KEYID_GLOBAL_FLAG */
+ }
+#endif
+
+#ifdef WOLFHSM_CFG_KEYWRAP
+ /* Convert TYPE=WRAPPED to wrapped flag (bit 9: 0x0200) */
+ if (WH_KEYID_TYPE(serverId) == WH_KEYTYPE_WRAPPED) {
+ clientId |= 0x0200; /* WH_CLIENT_KEYID_WRAPPED_FLAG */
+ }
+#endif
+
+ return clientId;
+}
diff --git a/src/wh_server_cert.c b/src/wh_server_cert.c
index cc66a8842..3fbd47de1 100644
--- a/src/wh_server_cert.c
+++ b/src/wh_server_cert.c
@@ -497,8 +497,9 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic,
req.trustedRootNvmId, req.flags,
&keyId);
- /* Propagate the keyId back to the client */
- resp.keyId = WH_KEYID_ID(keyId);
+ /* Propagate the keyId back to the client with flags preserved
+ */
+ resp.keyId = wh_KeyId_ToClient(keyId);
}
/* Convert the response struct */
@@ -626,8 +627,9 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic,
req.trustedRootNvmId, req.flags,
&keyId);
- /* Propagate the keyId back to the client */
- resp.keyId = WH_KEYID_ID(keyId);
+ /* Propagate the keyId back to the client with flags preserved
+ */
+ resp.keyId = wh_KeyId_ToClient(keyId);
}
if (resp.rc == WH_ERROR_OK) {
/* Post-process client address */
diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c
index 48a1626a3..fa93cc8de 100644
--- a/src/wh_server_crypto.c
+++ b/src/wh_server_crypto.c
@@ -346,7 +346,7 @@ static int _HandleRsaKeyGen(whServerContext* ctx, uint16_t magic,
key_id, ret);
#endif
if (ret == 0) {
- res.keyId = WH_KEYID_ID(key_id);
+ res.keyId = wh_KeyId_ToClient(key_id);
res.len = 0;
}
}
@@ -807,7 +807,7 @@ static int _HandleEccKeyGen(whServerContext* ctx, uint16_t magic,
}
if (ret == WH_ERROR_OK) {
- res.keyId = WH_KEYID_ID(key_id);
+ res.keyId = wh_KeyId_ToClient(key_id);
res.len = res_size;
wh_MessageCrypto_TranslateEccKeyGenResponse(
@@ -1291,7 +1291,7 @@ static int _HandleHkdf(whServerContext* ctx, uint16_t magic,
key_id, ret);
#endif
if (ret == WH_ERROR_OK) {
- res.keyIdOut = WH_KEYID_ID(key_id);
+ res.keyIdOut = wh_KeyId_ToClient(key_id);
res.outSz = 0;
/* clear the output buffer */
memset(out, 0, outSz);
@@ -1391,7 +1391,7 @@ static int _HandleCurve25519KeyGen(whServerContext* ctx, uint16_t magic,
}
if (ret == 0) {
- res.keyId = WH_KEYID_ID(key_id);
+ res.keyId = wh_KeyId_ToClient(key_id);
res.len = ser_size;
/* Translate response */
@@ -2343,7 +2343,7 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq,
ret = wh_Server_KeystoreCacheKey(
ctx, meta, (uint8_t*)ctx->crypto->algoCtx.cmac);
if (ret == 0) {
- res.keyId = WH_KEYID_ID(keyId);
+ res.keyId = wh_KeyId_ToClient(keyId);
res.outSz = 0;
}
}
@@ -2788,7 +2788,7 @@ static int _HandleMlDsaKeyGen(whServerContext* ctx, uint16_t magic,
}
if (ret == WH_ERROR_OK) {
- res.keyId = WH_KEYID_ID(key_id);
+ res.keyId = wh_KeyId_ToClient(key_id);
res.len = res_size;
wh_MessageCrypto_TranslateMlDsaKeyGenResponse(magic, &res,
@@ -3939,7 +3939,7 @@ static int _HandleMlDsaKeyGenDma(whServerContext* ctx, uint16_t magic,
__func__, keyId, ret);
#endif
if (ret == 0) {
- res.keyId = WH_KEYID_ID(keyId);
+ res.keyId = wh_KeyId_ToClient(keyId);
res.keySize = keySize;
}
}
diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c
index a62ef06cf..da96d2d80 100644
--- a/src/wh_server_keystore.c
+++ b/src/wh_server_keystore.c
@@ -1114,9 +1114,8 @@ _HandleUnwrapAndCacheKeyRequest(whServerContext* server,
return WH_ERROR_ABORTED;
}
- /* Store the assigned key ID in the response (ID portion only). We should
- * NOT return the upper bits back to the client */
- resp->keyId = WH_KEYID_ID(metadata.id);
+ /* Store the assigned key ID in the response, preserving client flags */
+ resp->keyId = wh_KeyId_ToClient(metadata.id);
/* Cache the key */
return wh_Server_KeystoreCacheKey(server, &metadata, key);
@@ -1181,8 +1180,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
ret = WH_ERROR_OK;
}
if (ret == WH_ERROR_OK) {
- /* remove the client_id, client may set type */
- resp.id = WH_KEYID_ID(meta->id);
+ /* Translate server keyId back to client format with flags */
+ resp.id = wh_KeyId_ToClient(meta->id);
(void)wh_MessageKeystore_TranslateCacheResponse(
magic, &resp,
@@ -1236,8 +1235,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
ret = WH_ERROR_OK;
}
- /* remove the client_id, client may set type */
- resp.id = WH_KEYID_ID(meta->id);
+ /* Translate server keyId back to client format with flags */
+ resp.id = wh_KeyId_ToClient(meta->id);
(void)wh_MessageKeystore_TranslateCacheDmaResponse(
magic, &resp, (whMessageKeystore_CacheDmaResponse*)resp_packet);
diff --git a/test/wh_test_multiclient.c b/test/wh_test_multiclient.c
index c4de85943..134e34753 100644
--- a/test/wh_test_multiclient.c
+++ b/test/wh_test_multiclient.c
@@ -1227,12 +1227,123 @@ static int _testWrappedKey_LocalWrap_GlobalKey_NonOwnerNoWrapKey(
}
#endif /* WOLFHSM_CFG_KEYWRAP */
+/*
+ * Test: KeyId flag preservation
+ * - Tests that global and wrapped flags are preserved in server responses
+ * - Verifies keyCache operations return correct flags
+ */
+static int _testKeyIdFlagPreservation(whClientContext* client1,
+ whServerContext* server1,
+ whClientContext* client2,
+ whServerContext* server2)
+{
+ (void)client2;
+ (void)server2;
+
+ printf("Test: KeyId flag preservation\n");
+
+ /* Test 1: Global key cache preserves global flag */
+ {
+ whKeyId keyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ whKeyId returnedKeyId = 0;
+
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"GlobalKeyFlags", sizeof("GlobalKeyFlags"),
+ (uint8_t*)TEST_KEY_DATA_1, sizeof(TEST_KEY_DATA_1), keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyCacheResponse(client1, &returnedKeyId));
+
+ /* Verify global flag is preserved */
+ WH_TEST_ASSERT_RETURN((returnedKeyId & WH_CLIENT_KEYID_GLOBAL_FLAG) !=
+ 0);
+ WH_TEST_ASSERT_RETURN((returnedKeyId & WH_KEYID_MASK) == DUMMY_KEYID_1);
+
+ /* Clean up */
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyEvictRequest(client1, returnedKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Global key cache preserves global flag\n");
+ }
+
+ /* Test 2: Local key cache does not have global flag */
+ {
+ whKeyId keyId = DUMMY_KEYID_2; /* Local key - no flags */
+ whKeyId returnedKeyId = 0;
+
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"LocalKeyFlags", sizeof("LocalKeyFlags"),
+ (uint8_t*)TEST_KEY_DATA_2, sizeof(TEST_KEY_DATA_2), keyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyCacheResponse(client1, &returnedKeyId));
+
+ /* Verify no global flag */
+ WH_TEST_ASSERT_RETURN((returnedKeyId & WH_CLIENT_KEYID_GLOBAL_FLAG) ==
+ 0);
+ WH_TEST_ASSERT_RETURN((returnedKeyId & WH_KEYID_MASK) == DUMMY_KEYID_2);
+
+ /* Clean up */
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyEvictRequest(client1, returnedKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Local key cache has no global flag\n");
+ }
+
+ /* Test 3: Reusing returned keyId works correctly */
+ {
+ whKeyId requestKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1);
+ whKeyId returnedKeyId = 0;
+ uint8_t outBuf[sizeof(TEST_KEY_DATA_1)] = {0};
+ uint16_t outSz = sizeof(outBuf);
+ uint8_t label[WH_NVM_LABEL_LEN];
+ uint16_t labelSz = sizeof(label);
+
+ /* Cache a global key and get keyId back */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex(
+ client1, 0, (uint8_t*)"ReuseTest", sizeof("ReuseTest"),
+ (uint8_t*)TEST_KEY_DATA_1, sizeof(TEST_KEY_DATA_1), requestKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyCacheResponse(client1, &returnedKeyId));
+
+ /* Use the returned keyId to export the key (common pattern) */
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyExportRequest(client1, returnedKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExportResponse(
+ client1, label, labelSz, outBuf, &outSz));
+
+ /* Verify data matches */
+ WH_TEST_ASSERT_RETURN(outSz == sizeof(TEST_KEY_DATA_1));
+ WH_TEST_ASSERT_RETURN(
+ 0 == memcmp(outBuf, TEST_KEY_DATA_1, sizeof(TEST_KEY_DATA_1)));
+
+ /* Clean up using returned keyId */
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_KeyEvictRequest(client1, returnedKeyId));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvictResponse(client1));
+
+ printf(" PASS: Reusing returned keyId works correctly\n");
+ }
+
+ return 0;
+}
+
/* Helper function to run all global keys tests */
static int _runGlobalKeysTests(whClientContext* client1,
whServerContext* server1,
whClientContext* client2,
whServerContext* server2)
{
+ WH_TEST_RETURN_ON_FAIL(
+ _testKeyIdFlagPreservation(client1, server1, client2, server2));
+
WH_TEST_RETURN_ON_FAIL(
_testGlobalKeyBasic(client1, server1, client2, server2));
diff --git a/wolfhsm/wh_keyid.h b/wolfhsm/wh_keyid.h
index 28831475b..712a353e7 100644
--- a/wolfhsm/wh_keyid.h
+++ b/wolfhsm/wh_keyid.h
@@ -97,4 +97,20 @@ typedef uint16_t whKeyId;
whKeyId wh_KeyId_TranslateClient(uint16_t type, uint16_t clientId,
whKeyId reqId);
+/**
+ * @brief Translate server keyId to client keyId format (with flags)
+ *
+ * Translates server-internal keyId format (TYPE + USER + ID) back to
+ * client-facing format (ID + flags). Server encoding is converted to flags:
+ * - USER = 0 (WH_KEYUSER_GLOBAL) → 0x0100 (WH_CLIENT_KEYID_GLOBAL_FLAG)
+ * - TYPE = WH_KEYTYPE_WRAPPED → 0x0200 (WH_CLIENT_KEYID_WRAPPED_FLAG)
+ *
+ * This ensures clients can identify global and wrapped keys after they are
+ * returned from server operations (cache, key generation, etc.).
+ *
+ * @param serverId Server-internal keyId with TYPE, USER, and ID fields
+ * @return Client-facing keyId with ID portion and appropriate flag bits set
+ */
+whKeyId wh_KeyId_ToClient(whKeyId serverId);
+
#endif /* !WOLFHSM_WH_KEYID_H_ */
From d026f0c6fdfe38da7525bcfdb7d6b1ca0b8d3712 Mon Sep 17 00:00:00 2001
From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com>
Date: Wed, 29 Oct 2025 13:14:29 -0600
Subject: [PATCH 4/5] fixes for NOCRYPTO after scan-build PR
---
test/wh_test_multiclient.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/test/wh_test_multiclient.c b/test/wh_test_multiclient.c
index 134e34753..8a90ac14b 100644
--- a/test/wh_test_multiclient.c
+++ b/test/wh_test_multiclient.c
@@ -36,12 +36,6 @@
#include
#include
-#include "wolfssl/wolfcrypt/settings.h"
-#include "wolfssl/wolfcrypt/types.h"
-#include "wolfssl/wolfcrypt/rsa.h"
-#include "wolfssl/wolfcrypt/ecc.h"
-#include "wolfssl/wolfcrypt/asn.h"
-
#include "wolfhsm/wh_error.h"
#include "wolfhsm/wh_comm.h"
#include "wolfhsm/wh_message.h"
From 4da2aaf6692aea821ed73dcb20a3de49a6d221d6 Mon Sep 17 00:00:00 2001
From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com>
Date: Thu, 30 Oct 2025 09:57:48 -0600
Subject: [PATCH 5/5] Review feedback:
- Renamed whServerCacheXXX to whKeyCacheXXX
- Relocated client global+wrapped flags to wh_keyid.h from wh_client.h
- Fixed copyright year
- Fixed wh_settings.h include order
---
src/wh_keyid.c | 22 ++--
src/wh_server_cert.c | 8 +-
src/wh_server_crypto.c | 120 +++++++++----------
src/wh_server_keystore.c | 62 ++++------
test/wh_test_multiclient.c | 4 +-
test/wh_test_multiclient.h | 2 +-
wolfhsm/wh_client.h | 34 +-----
wolfhsm/wh_common.h | 4 +-
wolfhsm/{wh_server_cache.h => wh_keycache.h} | 18 +--
wolfhsm/wh_keyid.h | 42 +++++--
wolfhsm/wh_nvm.h | 2 +-
wolfhsm/wh_server.h | 2 +-
wolfhsm/wh_server_keystore.h | 11 --
13 files changed, 147 insertions(+), 184 deletions(-)
rename wolfhsm/{wh_server_cache.h => wh_keycache.h} (82%)
diff --git a/src/wh_keyid.c b/src/wh_keyid.c
index 031b7daab..239d615a7 100644
--- a/src/wh_keyid.c
+++ b/src/wh_keyid.c
@@ -24,22 +24,22 @@
#include "wolfhsm/wh_keyid.h"
-whKeyId wh_KeyId_TranslateClient(uint16_t type, uint16_t clientId,
- whKeyId reqId)
+whKeyId wh_KeyId_TranslateFromClient(uint16_t type, uint16_t clientId,
+ whKeyId reqId)
{
uint16_t user = clientId;
whKeyId id = reqId & WH_KEYID_MASK;
#ifdef WOLFHSM_CFG_GLOBAL_KEYS
- /* Check for global flag (bit 8: 0x0100) */
- if ((reqId & 0x0100) != 0) {
+ /* Convert global flag to USER=0 */
+ if ((reqId & WH_KEYID_CLIENT_GLOBAL_FLAG) != 0) {
user = WH_KEYUSER_GLOBAL;
}
#endif
#ifdef WOLFHSM_CFG_KEYWRAP
- /* Check for wrapped flag (bit 9: 0x0200) */
- if ((reqId & 0x0200) != 0) {
+ /* Convert wrapped flag to TYPE=WH_KETYPE_WRAPPED */
+ if ((reqId & WH_KEYID_CLIENT_WRAPPED_FLAG) != 0) {
type = WH_KEYTYPE_WRAPPED;
}
#endif
@@ -47,21 +47,21 @@ whKeyId wh_KeyId_TranslateClient(uint16_t type, uint16_t clientId,
return WH_MAKE_KEYID(type, user, id);
}
-whKeyId wh_KeyId_ToClient(whKeyId serverId)
+whKeyId wh_KeyId_TranslateToClient(whKeyId serverId)
{
whKeyId clientId = WH_KEYID_ID(serverId);
#ifdef WOLFHSM_CFG_GLOBAL_KEYS
- /* Convert USER=0 to global flag (bit 8: 0x0100) */
+ /* Convert USER=0 to global flag */
if (WH_KEYID_USER(serverId) == WH_KEYUSER_GLOBAL) {
- clientId |= 0x0100; /* WH_CLIENT_KEYID_GLOBAL_FLAG */
+ clientId |= WH_KEYID_CLIENT_GLOBAL_FLAG;
}
#endif
#ifdef WOLFHSM_CFG_KEYWRAP
- /* Convert TYPE=WRAPPED to wrapped flag (bit 9: 0x0200) */
+ /* Convert TYPE=WRAPPED to wrapped flag */
if (WH_KEYID_TYPE(serverId) == WH_KEYTYPE_WRAPPED) {
- clientId |= 0x0200; /* WH_CLIENT_KEYID_WRAPPED_FLAG */
+ clientId |= WH_KEYID_CLIENT_WRAPPED_FLAG;
}
#endif
diff --git a/src/wh_server_cert.c b/src/wh_server_cert.c
index 3fbd47de1..79d7b5033 100644
--- a/src/wh_server_cert.c
+++ b/src/wh_server_cert.c
@@ -489,7 +489,7 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic,
cert_data = (const uint8_t*)req_packet + sizeof(req);
/* Map client keyId to server keyId space */
- whKeyId keyId = wh_KeyId_TranslateClient(
+ whKeyId keyId = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, server->comm->client_id, req.keyId);
/* Process the verify action */
@@ -499,7 +499,7 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic,
/* Propagate the keyId back to the client with flags preserved
*/
- resp.keyId = wh_KeyId_ToClient(keyId);
+ resp.keyId = wh_KeyId_TranslateToClient(keyId);
}
/* Convert the response struct */
@@ -619,7 +619,7 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic,
}
if (resp.rc == WH_ERROR_OK) {
/* Map client keyId to server keyId space */
- whKeyId keyId = wh_KeyId_TranslateClient(
+ whKeyId keyId = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, server->comm->client_id, req.keyId);
/* Process the verify action */
@@ -629,7 +629,7 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic,
/* Propagate the keyId back to the client with flags preserved
*/
- resp.keyId = wh_KeyId_ToClient(keyId);
+ resp.keyId = wh_KeyId_TranslateToClient(keyId);
}
if (resp.rc == WH_ERROR_OK) {
/* Post-process client address */
diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c
index fa93cc8de..6713b5f4d 100644
--- a/src/wh_server_crypto.c
+++ b/src/wh_server_crypto.c
@@ -287,8 +287,8 @@ static int _HandleRsaKeyGen(whServerContext* ctx, uint16_t magic,
long e = req.e;
/* Force incoming key_id to have current user/type */
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
whNvmFlags flags = req.flags;
uint8_t* label = req.label;
uint32_t label_size = WH_NVM_LABEL_LEN;
@@ -346,7 +346,7 @@ static int _HandleRsaKeyGen(whServerContext* ctx, uint16_t magic,
key_id, ret);
#endif
if (ret == 0) {
- res.keyId = wh_KeyId_ToClient(key_id);
+ res.keyId = wh_KeyId_TranslateToClient(key_id);
res.len = 0;
}
}
@@ -386,8 +386,8 @@ static int _HandleRsaFunction( whServerContext* ctx, uint16_t magic,
int op_type = (int)(req.opType);
uint32_t options = req.options;
int evict = !!(options & WH_MESSAGE_CRYPTO_RSA_OPTIONS_EVICT);
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
word32 in_len = (word32)(req.inLen);
word32 out_len = (word32)(req.outLen);
/* in and out are after the fixed size fields */
@@ -471,8 +471,8 @@ static int _HandleRsaGetSize(whServerContext* ctx, uint16_t magic,
}
/* Extract parameters from translated request */
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
uint32_t options = req.options;
int evict = !!(options & WH_MESSAGE_CRYPTO_RSA_GET_SIZE_OPTIONS_EVICT);
@@ -738,8 +738,8 @@ static int _HandleEccKeyGen(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
int key_size = req.sz;
int curve_id = req.curveId;
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
whNvmFlags flags = req.flags;
uint8_t* label = req.label;
uint16_t label_size = WH_NVM_LABEL_LEN;
@@ -807,7 +807,7 @@ static int _HandleEccKeyGen(whServerContext* ctx, uint16_t magic,
}
if (ret == WH_ERROR_OK) {
- res.keyId = wh_KeyId_ToClient(key_id);
+ res.keyId = wh_KeyId_TranslateToClient(key_id);
res.len = res_size;
wh_MessageCrypto_TranslateEccKeyGenResponse(
@@ -841,9 +841,9 @@ static int _HandleEccSharedSecret(whServerContext* ctx, uint16_t magic,
uint32_t options = req.options;
int evict_pub = !!(options & WH_MESSAGE_CRYPTO_ECDH_OPTIONS_EVICTPUB);
int evict_prv = !!(options & WH_MESSAGE_CRYPTO_ECDH_OPTIONS_EVICTPRV);
- whKeyId pub_key_id = wh_KeyId_TranslateClient(
+ whKeyId pub_key_id = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.publicKeyId);
- whKeyId prv_key_id = wh_KeyId_TranslateClient(
+ whKeyId prv_key_id = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.privateKeyId);
/* Response message */
@@ -919,8 +919,8 @@ static int _HandleEccSign(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
uint8_t* in =
(uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_EccSignRequest);
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
word32 in_len = req.sz;
uint32_t options = req.options;
int evict = !!(options & WH_MESSAGE_CRYPTO_ECCSIGN_OPTIONS_EVICT);
@@ -991,8 +991,8 @@ static int _HandleEccVerify(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
uint32_t options = req.options;
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
uint32_t hash_len = req.hashSz;
uint32_t sig_len = req.sigSz;
uint8_t* req_sig =
@@ -1213,9 +1213,9 @@ static int _HandleHkdf(whServerContext* ctx, uint16_t magic,
uint32_t saltSz = req.saltSz;
uint32_t infoSz = req.infoSz;
uint32_t outSz = req.outSz;
- whKeyId key_id = wh_KeyId_TranslateClient(
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyIdOut);
- whKeyId keyIdIn = wh_KeyId_TranslateClient(
+ whKeyId keyIdIn = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyIdIn);
whNvmFlags flags = req.flags;
uint8_t* label = req.label;
@@ -1291,7 +1291,7 @@ static int _HandleHkdf(whServerContext* ctx, uint16_t magic,
key_id, ret);
#endif
if (ret == WH_ERROR_OK) {
- res.keyIdOut = wh_KeyId_ToClient(key_id);
+ res.keyIdOut = wh_KeyId_TranslateToClient(key_id);
res.outSz = 0;
/* clear the output buffer */
memset(out, 0, outSz);
@@ -1335,8 +1335,8 @@ static int _HandleCurve25519KeyGen(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
int key_size = req.sz;
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
whNvmFlags flags = req.flags;
uint8_t* label = req.label;
uint16_t label_size = WH_NVM_LABEL_LEN;
@@ -1391,7 +1391,7 @@ static int _HandleCurve25519KeyGen(whServerContext* ctx, uint16_t magic,
}
if (ret == 0) {
- res.keyId = wh_KeyId_ToClient(key_id);
+ res.keyId = wh_KeyId_TranslateToClient(key_id);
res.len = ser_size;
/* Translate response */
@@ -1429,9 +1429,9 @@ static int _HandleCurve25519SharedSecret(whServerContext* ctx, uint16_t magic,
uint32_t options = req.options;
int evict_pub = !!(options & WH_MESSAGE_CRYPTO_CURVE25519_OPTIONS_EVICTPUB);
int evict_prv = !!(options & WH_MESSAGE_CRYPTO_CURVE25519_OPTIONS_EVICTPRV);
- whKeyId pub_key_id = wh_KeyId_TranslateClient(
+ whKeyId pub_key_id = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.publicKeyId);
- whKeyId prv_key_id = wh_KeyId_TranslateClient(
+ whKeyId prv_key_id = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.privateKeyId);
int endian = req.endian;
@@ -1517,8 +1517,8 @@ static int _HandleAesCtr(whServerContext* ctx, uint16_t magic,
if (needed_size > inSize) {
return WH_ERROR_BADARGS;
}
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
/* in, key, iv, and out are after fixed size fields */
uint8_t* in =
(uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_AesCtrRequest);
@@ -1634,8 +1634,8 @@ static int _HandleAesEcb(whServerContext* ctx, uint16_t magic,
return WH_ERROR_BADARGS;
}
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
/* in, key, iv, and out are after fixed size fields */
uint8_t* in =
@@ -1742,8 +1742,8 @@ static int _HandleAesCbc(whServerContext* ctx, uint16_t magic, const void* crypt
return WH_ERROR_BADARGS;
}
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
/* in, key, iv, and out are after fixed size fields */
uint8_t* in =
@@ -1851,8 +1851,8 @@ static int _HandleAesGcm(whServerContext* ctx, uint16_t magic,
uint32_t iv_len = req.ivSz;
uint32_t authin_len = req.authInSz;
uint32_t tag_len = req.authTagSz;
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
/* in, key, iv, authin, tag, and out are after fixed size fields */
uint8_t* in = (uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_AesGcmRequest);
@@ -2075,8 +2075,8 @@ static int _HandleAesGcmDma(whServerContext* ctx, uint16_t magic, uint16_t seq,
/* Handle keyId-based keys if no direct key was provided */
if (ret == WH_ERROR_OK && req.key.sz == 0) {
- keyId = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ keyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
keyLen = sizeof(tmpKey);
ret = wh_Server_KeystoreReadKey(ctx, keyId, NULL, tmpKey, &keyLen);
if (ret == WH_ERROR_OK) {
@@ -2222,7 +2222,7 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq,
* cache slot until CmacFinal() is called, at which point we evict the
* struct from the cache. TODO: client should hold CMAC state */
len = sizeof(ctx->crypto->algoCtx.cmac);
- keyId = wh_KeyId_TranslateClient(
+ keyId = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
ret = wh_Server_KeystoreReadKey(
ctx, keyId, NULL, (uint8_t*)ctx->crypto->algoCtx.cmac,
@@ -2299,9 +2299,9 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq,
if (!WH_KEYID_ISERASED(keyId)) {
/* Don't override return value except on failure */
int tmpRet = wh_Server_KeystoreEvictKey(
- ctx,
- wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, keyId));
+ ctx, wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id,
+ keyId));
if (tmpRet != 0) {
ret = tmpRet;
}
@@ -2312,9 +2312,9 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq,
/* Handle cancellation - evict key and abandon state */
if (!WH_KEYID_ISERASED(req.keyId)) {
wh_Server_KeystoreEvictKey(
- ctx, wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id,
- req.keyId));
+ ctx, wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id,
+ req.keyId));
}
}
#endif
@@ -2330,7 +2330,7 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq,
return ret;
}
else {
- keyId = wh_KeyId_TranslateClient(
+ keyId = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
}
/* evict the aes sized key in the normal cache */
@@ -2343,7 +2343,7 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq,
ret = wh_Server_KeystoreCacheKey(
ctx, meta, (uint8_t*)ctx->crypto->algoCtx.cmac);
if (ret == 0) {
- res.keyId = wh_KeyId_ToClient(keyId);
+ res.keyId = wh_KeyId_TranslateToClient(keyId);
res.outSz = 0;
}
}
@@ -2716,8 +2716,8 @@ static int _HandleMlDsaKeyGen(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
int key_size = req.sz;
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
int level = req.level;
whNvmFlags flags = req.flags;
uint8_t* label = req.label;
@@ -2788,7 +2788,7 @@ static int _HandleMlDsaKeyGen(whServerContext* ctx, uint16_t magic,
}
if (ret == WH_ERROR_OK) {
- res.keyId = wh_KeyId_ToClient(key_id);
+ res.keyId = wh_KeyId_TranslateToClient(key_id);
res.len = res_size;
wh_MessageCrypto_TranslateMlDsaKeyGenResponse(magic, &res,
@@ -2830,8 +2830,8 @@ static int _HandleMlDsaSign(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
byte* in = (uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_MlDsaSignRequest);
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
word32 in_len = req.sz;
uint32_t options = req.options;
int evict = !!(options & WH_MESSAGE_CRYPTO_MLDSA_SIGN_OPTIONS_EVICT);
@@ -2910,8 +2910,8 @@ static int _HandleMlDsaVerify(whServerContext* ctx, uint16_t magic,
/* Extract parameters from translated request */
uint32_t options = req.options;
- whKeyId key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id, req.keyId);
+ whKeyId key_id = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
uint32_t hash_len = req.hashSz;
uint32_t sig_len = req.sigSz;
byte* req_sig =
@@ -3911,7 +3911,7 @@ static int _HandleMlDsaKeyGenDma(whServerContext* ctx, uint16_t magic,
else {
/* Must import the key into the cache and return keyid
*/
- whKeyId keyId = wh_KeyId_TranslateClient(
+ whKeyId keyId = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
if (WH_KEYID_ISERASED(keyId)) {
@@ -3939,7 +3939,7 @@ static int _HandleMlDsaKeyGenDma(whServerContext* ctx, uint16_t magic,
__func__, keyId, ret);
#endif
if (ret == 0) {
- res.keyId = wh_KeyId_ToClient(keyId);
+ res.keyId = wh_KeyId_TranslateToClient(keyId);
res.keySize = keySize;
}
}
@@ -4000,8 +4000,8 @@ static int _HandleMlDsaSignDma(whServerContext* ctx, uint16_t magic,
/* Get key ID and evict flag */
- key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO, ctx->comm->client_id,
- req.keyId);
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
evict = !!(req.options & WH_MESSAGE_CRYPTO_MLDSA_SIGN_OPTIONS_EVICT);
/* Initialize key */
@@ -4109,8 +4109,8 @@ static int _HandleMlDsaVerifyDma(whServerContext* ctx, uint16_t magic,
int evict = 0;
/* Get key ID and evict flag */
- key_id = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO, ctx->comm->client_id,
- req.keyId);
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
evict = !!(req.options & WH_MESSAGE_CRYPTO_MLDSA_VERIFY_OPTIONS_EVICT);
/* Initialize key */
@@ -4379,9 +4379,9 @@ static int _HandleCmacDma(whServerContext* ctx, uint16_t magic, uint16_t seq,
* that has been initialized to use a keyId by
* reference. We need to load the key from cache and
* initialize a new context with it */
- keyId = wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- ctx->comm->client_id,
- clientKeyId);
+ keyId = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id,
+ clientKeyId);
keyLen = sizeof(tmpKey);
/* Load key from cache */
@@ -4444,7 +4444,7 @@ static int _HandleCmacDma(whServerContext* ctx, uint16_t magic, uint16_t seq,
whNvmId nvmId = WH_DEVCTX_TO_KEYID(cmac->devCtx);
if (nvmId != WH_KEYID_ERASED) {
/* Get key ID from CMAC context */
- keyId = wh_KeyId_TranslateClient(
+ keyId = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, ctx->comm->client_id, nvmId);
keyLen = sizeof(tmpKey);
diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c
index da96d2d80..898ad4f75 100644
--- a/src/wh_server_keystore.c
+++ b/src/wh_server_keystore.c
@@ -171,7 +171,7 @@ static int _GetKeyCacheSlot(whKeyCacheContext* ctx, uint16_t keySz,
/* Zero slot and return pointers */
if (foundIndex >= 0) {
- memset(&ctx->cache[foundIndex], 0, sizeof(whServerCacheSlot));
+ memset(&ctx->cache[foundIndex], 0, sizeof(whCacheSlot));
*outBuf = ctx->cache[foundIndex].buffer;
*outMeta = ctx->cache[foundIndex].meta;
}
@@ -197,7 +197,7 @@ static int _GetKeyCacheSlot(whKeyCacheContext* ctx, uint16_t keySz,
/* Zero slot and return pointers */
if (foundIndex >= 0) {
- memset(&ctx->bigCache[foundIndex], 0, sizeof(whServerBigCacheSlot));
+ memset(&ctx->bigCache[foundIndex], 0, sizeof(whBigCacheSlot));
*outBuf = ctx->bigCache[foundIndex].buffer;
*outMeta = ctx->bigCache[foundIndex].meta;
}
@@ -485,26 +485,6 @@ static int _ExistsInCache(whServerContext* server, whKeyId keyId)
}
#endif /* WOLFHSM_CFG_KEYWRAP */
-#ifdef WOLFHSM_CFG_KEYWRAP
-int wh_Server_KeystoreIsWrappedKey(whServerContext* server, whKeyId keyId,
- int* outIsWrapped)
-{
- int isWrapped;
-
- if (server == NULL || WH_KEYID_ISERASED(keyId)) {
- return WH_ERROR_BADARGS;
- }
-
- (void)server;
- isWrapped = (WH_KEYID_TYPE(keyId) == WH_KEYTYPE_WRAPPED);
- if (outIsWrapped != NULL) {
- *outIsWrapped = isWrapped;
- }
-
- return WH_ERROR_OK;
-}
-#endif /* WOLFHSM_CFG_KEYWRAP */
-
/* try to put the specified key into cache if it isn't already, return pointers
* to meta and the cached data*/
int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId,
@@ -742,8 +722,8 @@ static int _AesGcmWrapKey(whServerContext* server, whKeyId serverKeyId,
/* Get the server side key */
ret = wh_Server_KeystoreReadKey(
server,
- wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO, server->comm->client_id,
- serverKeyId),
+ wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, server->comm->client_id,
+ serverKeyId),
NULL, serverKey, &serverKeySz);
if (ret != WH_ERROR_OK) {
return ret;
@@ -815,8 +795,8 @@ static int _AesGcmUnwrapKey(whServerContext* server, uint16_t serverKeyId,
/* Get the server side key */
ret = wh_Server_KeystoreReadKey(
server,
- wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO, server->comm->client_id,
- serverKeyId),
+ wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, server->comm->client_id,
+ serverKeyId),
NULL, serverKey, &serverKeySz);
if (ret != WH_ERROR_OK) {
return ret;
@@ -1115,7 +1095,7 @@ _HandleUnwrapAndCacheKeyRequest(whServerContext* server,
}
/* Store the assigned key ID in the response, preserving client flags */
- resp->keyId = wh_KeyId_ToClient(metadata.id);
+ resp->keyId = wh_KeyId_TranslateToClient(metadata.id);
/* Cache the key */
return wh_Server_KeystoreCacheKey(server, &metadata, key);
@@ -1153,7 +1133,7 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
in = (uint8_t*)req_packet + sizeof(req);
/* set the metadata fields */
- meta->id = wh_KeyId_TranslateClient(
+ meta->id = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, server->comm->client_id, req.id);
meta->access = WH_NVM_ACCESS_ANY;
meta->flags = req.flags;
@@ -1181,7 +1161,7 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
}
if (ret == WH_ERROR_OK) {
/* Translate server keyId back to client format with flags */
- resp.id = wh_KeyId_ToClient(meta->id);
+ resp.id = wh_KeyId_TranslateToClient(meta->id);
(void)wh_MessageKeystore_TranslateCacheResponse(
magic, &resp,
@@ -1202,7 +1182,7 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
magic, (whMessageKeystore_CacheDmaRequest*)req_packet, &req);
/* set the metadata fields */
- meta->id = wh_KeyId_TranslateClient(
+ meta->id = wh_KeyId_TranslateFromClient(
WH_KEYTYPE_CRYPTO, server->comm->client_id, req.id);
meta->access = WH_NVM_ACCESS_ANY;
meta->flags = req.flags;
@@ -1236,7 +1216,7 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
}
/* Translate server keyId back to client format with flags */
- resp.id = wh_KeyId_ToClient(meta->id);
+ resp.id = wh_KeyId_TranslateToClient(meta->id);
(void)wh_MessageKeystore_TranslateCacheDmaResponse(
magic, &resp, (whMessageKeystore_CacheDmaResponse*)resp_packet);
@@ -1254,8 +1234,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
ret = wh_Server_KeystoreExportKeyDma(
server,
- wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- server->comm->client_id, req.id),
+ wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id, req.id),
req.key.addr, req.key.sz, meta);
resp.rc = ret;
/* propagate bad address to client if DMA operation failed */
@@ -1288,8 +1268,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
ret = wh_Server_KeystoreEvictKey(
server,
- wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- server->comm->client_id, req.id));
+ wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id, req.id));
resp.rc = ret;
/* TODO: Are there any fatal server errors? */
ret = WH_ERROR_OK;
@@ -1320,8 +1300,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
/* read the key */
ret = wh_Server_KeystoreReadKey(
server,
- wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- server->comm->client_id, req.id),
+ wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id, req.id),
meta, out, &keySz);
/* Check if key is non-exportable */
@@ -1364,8 +1344,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
ret = wh_Server_KeystoreCommitKey(
server,
- wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- server->comm->client_id, req.id));
+ wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id, req.id));
resp.rc = ret;
/* TODO: Are there any fatal server errors? */
ret = WH_ERROR_OK;
@@ -1391,8 +1371,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
ret = wh_Server_KeystoreEraseKey(
server,
- wh_KeyId_TranslateClient(WH_KEYTYPE_CRYPTO,
- server->comm->client_id, req.id));
+ wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id, req.id));
resp.rc = ret;
/* TODO: Are there any fatal server errors? */
ret = WH_ERROR_OK;
diff --git a/test/wh_test_multiclient.c b/test/wh_test_multiclient.c
index 8a90ac14b..d3b3629ad 100644
--- a/test/wh_test_multiclient.c
+++ b/test/wh_test_multiclient.c
@@ -1249,7 +1249,7 @@ static int _testKeyIdFlagPreservation(whClientContext* client1,
wh_Client_KeyCacheResponse(client1, &returnedKeyId));
/* Verify global flag is preserved */
- WH_TEST_ASSERT_RETURN((returnedKeyId & WH_CLIENT_KEYID_GLOBAL_FLAG) !=
+ WH_TEST_ASSERT_RETURN((returnedKeyId & WH_KEYID_CLIENT_GLOBAL_FLAG) !=
0);
WH_TEST_ASSERT_RETURN((returnedKeyId & WH_KEYID_MASK) == DUMMY_KEYID_1);
@@ -1275,7 +1275,7 @@ static int _testKeyIdFlagPreservation(whClientContext* client1,
wh_Client_KeyCacheResponse(client1, &returnedKeyId));
/* Verify no global flag */
- WH_TEST_ASSERT_RETURN((returnedKeyId & WH_CLIENT_KEYID_GLOBAL_FLAG) ==
+ WH_TEST_ASSERT_RETURN((returnedKeyId & WH_KEYID_CLIENT_GLOBAL_FLAG) ==
0);
WH_TEST_ASSERT_RETURN((returnedKeyId & WH_KEYID_MASK) == DUMMY_KEYID_2);
diff --git a/test/wh_test_multiclient.h b/test/wh_test_multiclient.h
index c1b39d7e4..5d2de4cd0 100644
--- a/test/wh_test_multiclient.h
+++ b/test/wh_test_multiclient.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 wolfSSL Inc.
+ * Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfHSM.
*
diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h
index acaf1f49f..12b02c68a 100644
--- a/wolfhsm/wh_client.h
+++ b/wolfhsm/wh_client.h
@@ -52,6 +52,7 @@
#ifdef WOLFHSM_CFG_DMA
#include "wolfhsm/wh_dma.h"
#endif /* WOLFHSM_CFG_DMA */
+#include "wolfhsm/wh_keyid.h"
/* Forward declaration of the client structure so its elements can reference
@@ -2493,33 +2494,6 @@ int wh_Client_CertVerifyAcertDma(whClientContext* c, const void* cert,
#endif /* WOLFHSM_CFG_DMA */
-/*
- * @brief Client-side keyId manipulation API
- *
- * This section defines the client-facing API for working with key identifiers.
- * Clients use simple numeric IDs (0-255) with optional flags to indicate
- * global or wrapped keys. The server translates these to full internal
- * representations with TYPE/USER/ID fields.
- *
- * Client keyId usage:
- * - Regular keys: Simple numeric ID (e.g., 5)
- * - Global keys: ID with WH_CLIENT_KEYID_GLOBAL_FLAG set
- * - Wrapped keys: ID with WH_CLIENT_KEYID_WRAPPED_FLAG set
- * - Wrapped metadata: Must use full WH_MAKE_KEYID() construction including type
- * and metadata when populating the ID field in metadata to be wrapped
- */
-
-/* Client-facing key flags (temporary, stripped by server during translation) */
-
-/* Bit 8: Client-to-server signal for global key (shared across all clients) */
-#define WH_CLIENT_KEYID_GLOBAL_FLAG ((whKeyId)0x0100)
-
-/* Bit 9: Client-to-server signal for wrapped key */
-#define WH_CLIENT_KEYID_WRAPPED_FLAG ((whKeyId)0x0200)
-
-/* Combined mask of all client-facing flags */
-#define WH_CLIENT_KEYID_FLAGS_MASK \
- (WH_CLIENT_KEYID_GLOBAL_FLAG | WH_CLIENT_KEYID_WRAPPED_FLAG)
/**
* @brief Mark a key ID as global (shared across all clients)
@@ -2535,7 +2509,7 @@ int wh_Client_CertVerifyAcertDma(whClientContext* c, const void* cert,
* whKeyId globalKey = WH_CLIENT_KEYID_MAKE_GLOBAL(5);
* wh_Client_KeyCache(client, globalKey, ...); // Stored as global key
*/
-#define WH_CLIENT_KEYID_MAKE_GLOBAL(_id) ((_id) | WH_CLIENT_KEYID_GLOBAL_FLAG)
+#define WH_CLIENT_KEYID_MAKE_GLOBAL(_id) ((_id) | WH_KEYID_CLIENT_GLOBAL_FLAG)
/**
* @brief Mark a key ID as wrapped
@@ -2551,7 +2525,7 @@ int wh_Client_CertVerifyAcertDma(whClientContext* c, const void* cert,
* whKeyId wrappedKey = WH_CLIENT_KEYID_MAKE_WRAPPED(2);
* wh_Client_KeyExportRequest(client, wrappedKey, ...);
*/
-#define WH_CLIENT_KEYID_MAKE_WRAPPED(_id) ((_id) | WH_CLIENT_KEYID_WRAPPED_FLAG)
+#define WH_CLIENT_KEYID_MAKE_WRAPPED(_id) ((_id) | WH_KEYID_CLIENT_WRAPPED_FLAG)
/**
* @brief Mark a key ID as both global and wrapped
@@ -2567,7 +2541,7 @@ int wh_Client_CertVerifyAcertDma(whClientContext* c, const void* cert,
* wh_Client_AesSetKeyId(aes, globalWrappedKey);
*/
#define WH_CLIENT_KEYID_MAKE_WRAPPED_GLOBAL(_id) \
- ((_id) | WH_CLIENT_KEYID_GLOBAL_FLAG | WH_CLIENT_KEYID_WRAPPED_FLAG)
+ ((_id) | WH_KEYID_CLIENT_GLOBAL_FLAG | WH_KEYID_CLIENT_WRAPPED_FLAG)
/**
* @brief Construct wrapped key metadata ID with explicit ownership
diff --git a/wolfhsm/wh_common.h b/wolfhsm/wh_common.h
index 74be57a7a..13bfb995c 100644
--- a/wolfhsm/wh_common.h
+++ b/wolfhsm/wh_common.h
@@ -24,11 +24,11 @@
#ifndef WOLFHSM_WH_COMMON_H_
#define WOLFHSM_WH_COMMON_H_
-#include
-
/* Pick up compile-time configuration */
#include "wolfhsm/wh_settings.h"
+#include
+
/* Key management types and helpers */
#include "wolfhsm/wh_keyid.h"
diff --git a/wolfhsm/wh_server_cache.h b/wolfhsm/wh_keycache.h
similarity index 82%
rename from wolfhsm/wh_server_cache.h
rename to wolfhsm/wh_keycache.h
index 3046a8451..1e0e7795d 100644
--- a/wolfhsm/wh_server_cache.h
+++ b/wolfhsm/wh_keycache.h
@@ -18,10 +18,10 @@
*/
/*
- * wolfhsm/wh_server_cache.h
+ * wolfhsm/wh_keycache.h
*
- * Exists as a separate header so it can be consumed by server, server keystore, and NVM
- * layer without creating circular dependencies
+ * Exists as a separate header so it can be consumed by server, server keystore,
+ * and NVM layer without creating circular dependencies
*/
#ifndef WOLFHSM_WH_SERVER_CACHE_H_
@@ -34,17 +34,17 @@
#ifndef WOLFHSM_CFG_NO_CRYPTO
/** Server cache slot structures */
-typedef struct whServerCacheSlot {
+typedef struct whCacheSlot {
uint8_t committed;
whNvmMetadata meta[1];
uint8_t buffer[WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE];
-} whServerCacheSlot;
+} whCacheSlot;
-typedef struct whServerBigCacheSlot {
+typedef struct whBigCacheSlot {
uint8_t committed;
whNvmMetadata meta[1];
uint8_t buffer[WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE];
-} whServerBigCacheSlot;
+} whBigCacheSlot;
/**
* @brief Unified key cache context
@@ -54,8 +54,8 @@ typedef struct whServerBigCacheSlot {
* when WOLFHSM_CFG_GLOBAL_KEYS is enabled).
*/
typedef struct whKeyCacheContext_t {
- whServerCacheSlot cache[WOLFHSM_CFG_SERVER_KEYCACHE_COUNT];
- whServerBigCacheSlot bigCache[WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT];
+ whCacheSlot cache[WOLFHSM_CFG_SERVER_KEYCACHE_COUNT];
+ whBigCacheSlot bigCache[WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT];
} whKeyCacheContext;
#endif /* !WOLFHSM_CFG_NO_CRYPTO */
diff --git a/wolfhsm/wh_keyid.h b/wolfhsm/wh_keyid.h
index 712a353e7..a60056bbd 100644
--- a/wolfhsm/wh_keyid.h
+++ b/wolfhsm/wh_keyid.h
@@ -46,10 +46,30 @@ typedef uint16_t whKeyId;
#define WH_KEYTYPE_MASK 0xF000
#define WH_KEYTYPE_SHIFT 12
-/* Client-facing key flags are defined in wh_client.h:
- * - WH_CLIENT_KEYID_GLOBAL_FLAG
- * - WH_CLIENT_KEYID_WRAPPED_FLAG
+/*
+ * Client-facing key flags (temporary, stripped by server during translation)
+ *
+ * Clients use simple numeric IDs (0-255) with optional flags to indicate
+ * global or wrapped keys. The server translates these to full internal
+ * representations with TYPE/USER/ID fields.
+
+ * Client keyId usage:
+ * - Regular keys: Simple numeric ID (e.g., 5)
+ * - Global keys: ID with WH_KEYID_CLIENT_GLOBAL_FLAG set
+ * - Wrapped keys: ID with WH_KEYID_CLIENT_WRAPPED_FLAG set
+ * - Wrapped metadata: Must use full WH_MAKE_KEYID() construction including type
+ * and metadata when populating the ID field in metadata to be wrapped
+ *
*/
+/* Bit 8: Client-to-server signal for global key (shared across all clients) */
+#define WH_KEYID_CLIENT_GLOBAL_FLAG ((whKeyId)0x0100)
+
+/* Bit 9: Client-to-server signal for wrapped key */
+#define WH_KEYID_CLIENT_WRAPPED_FLAG ((whKeyId)0x0200)
+
+/* Combined mask of all client-facing flags */
+#define WH_CLIENT_KEYID_FLAGS_MASK \
+ (WH_KEYID_CLIENT_GLOBAL_FLAG | WH_KEYID_CLIENT_WRAPPED_FLAG)
/* Macro to construct a server-unique keyid */
#define WH_MAKE_KEYID(_type, _user, _id) \
@@ -64,7 +84,7 @@ typedef uint16_t whKeyId;
#define WH_KEYID_ISWRAPPED(_kid) (WH_KEYID_TYPE(_kid) == WH_KEYTYPE_WRAPPED)
/* Reserve USER=0 for global keys in the internal keyId encoding.
- * This is server-internal; clients use WH_CLIENT_KEYID_GLOBAL_FLAG from
+ * This is server-internal; clients use WH_KEYID_CLIENT_GLOBAL_FLAG from
* wh_client.h */
#define WH_KEYUSER_GLOBAL 0
@@ -84,8 +104,8 @@ typedef uint16_t whKeyId;
*
* Translates client-facing keyId format (ID + flags) to server-internal format
* (TYPE + USER + ID). Client flags are:
- * - 0x0100 (bit 8): WH_CLIENT_KEYID_GLOBAL_FLAG → USER = 0
- * - 0x0200 (bit 9): WH_CLIENT_KEYID_WRAPPED_FLAG → TYPE = WH_KEYTYPE_WRAPPED
+ * - 0x0100 (bit 8): WH_KEYID_CLIENT_GLOBAL_FLAG → USER = 0
+ * - 0x0200 (bit 9): WH_KEYID_CLIENT_WRAPPED_FLAG → TYPE = WH_KEYTYPE_WRAPPED
*
* @param type Key type to use as the TYPE field. Input value is ignored and
* WH_KEYTYPE_WRAPPED is used if the input clientId has the
@@ -94,16 +114,16 @@ typedef uint16_t whKeyId;
* @param reqId Requested keyId from client (may include flags)
* @return Server-internal keyId with TYPE, USER, and ID fields properly set.
*/
-whKeyId wh_KeyId_TranslateClient(uint16_t type, uint16_t clientId,
- whKeyId reqId);
+whKeyId wh_KeyId_TranslateFromClient(uint16_t type, uint16_t clientId,
+ whKeyId reqId);
/**
* @brief Translate server keyId to client keyId format (with flags)
*
* Translates server-internal keyId format (TYPE + USER + ID) back to
* client-facing format (ID + flags). Server encoding is converted to flags:
- * - USER = 0 (WH_KEYUSER_GLOBAL) → 0x0100 (WH_CLIENT_KEYID_GLOBAL_FLAG)
- * - TYPE = WH_KEYTYPE_WRAPPED → 0x0200 (WH_CLIENT_KEYID_WRAPPED_FLAG)
+ * - USER = 0 (WH_KEYUSER_GLOBAL) → 0x0100 (WH_KEYID_CLIENT_GLOBAL_FLAG)
+ * - TYPE = WH_KEYTYPE_WRAPPED → 0x0200 (WH_KEYID_CLIENT_WRAPPED_FLAG)
*
* This ensures clients can identify global and wrapped keys after they are
* returned from server operations (cache, key generation, etc.).
@@ -111,6 +131,6 @@ whKeyId wh_KeyId_TranslateClient(uint16_t type, uint16_t clientId,
* @param serverId Server-internal keyId with TYPE, USER, and ID fields
* @return Client-facing keyId with ID portion and appropriate flag bits set
*/
-whKeyId wh_KeyId_ToClient(whKeyId serverId);
+whKeyId wh_KeyId_TranslateToClient(whKeyId serverId);
#endif /* !WOLFHSM_WH_KEYID_H_ */
diff --git a/wolfhsm/wh_nvm.h b/wolfhsm/wh_nvm.h
index 6e911f90e..17d1405fc 100644
--- a/wolfhsm/wh_nvm.h
+++ b/wolfhsm/wh_nvm.h
@@ -43,7 +43,7 @@
#include
#include "wolfhsm/wh_common.h" /* For whNvm types */
-#include "wolfhsm/wh_server_cache.h" /* For whKeyCacheContext */
+#include "wolfhsm/wh_keycache.h" /* For whKeyCacheContext */
typedef struct {
int (*Init)(void* context, const void *config);
diff --git a/wolfhsm/wh_server.h b/wolfhsm/wh_server.h
index 000a1307b..2c3dc02a3 100644
--- a/wolfhsm/wh_server.h
+++ b/wolfhsm/wh_server.h
@@ -38,7 +38,7 @@ typedef struct whServerContext_t whServerContext;
#include "wolfhsm/wh_common.h"
#include "wolfhsm/wh_comm.h"
-#include "wolfhsm/wh_server_cache.h"
+#include "wolfhsm/wh_keycache.h"
#include "wolfhsm/wh_nvm.h"
#include "wolfhsm/wh_message_customcb.h"
#ifdef WOLFHSM_CFG_DMA
diff --git a/wolfhsm/wh_server_keystore.h b/wolfhsm/wh_server_keystore.h
index 267572567..2cf0d6642 100644
--- a/wolfhsm/wh_server_keystore.h
+++ b/wolfhsm/wh_server_keystore.h
@@ -190,15 +190,4 @@ int wh_Server_KeystoreExportKeyDma(whServerContext* server, whKeyId keyId,
uint64_t keyAddr, uint64_t keySz,
whNvmMetadata* outMeta);
-/**
- * @brief Query whether a key identifier refers to wrapped material
- *
- * @param[in] server Server context (unused, reserved for parity)
- * @param[in] keyId Key identifier to inspect
- * @param[out] outIsWrapped Optional pointer receiving 1 if wrapped, 0 otherwise
- * @return 0 on success, error code on failure
- */
-int wh_Server_KeystoreIsWrappedKey(whServerContext* server, whKeyId keyId,
- int* outIsWrapped);
-
#endif /* !WOLFHSM_WH_SERVER_KEYSTORE_H_ */