Skip to content

Commit 35a983e

Browse files
authored
Global keystore (#224)
* 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 * support cache probe on freshen * keyId flag preservation * fixes for NOCRYPTO after scan-build PR * 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
1 parent d0c67d5 commit 35a983e

37 files changed

+2732
-455
lines changed

benchmark/wh_bench.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252

5353
#if defined(WOLFHSM_CFG_BENCH_ENABLE)
5454

55+
/* Default client ID for benchmarks */
56+
#define WH_BENCH_CLIENT_ID (1)
57+
5558
/* Buffer sizes for transport */
5659
/* Large enough to handle an RSA 4096 key */
5760
#define BUFFER_SIZE \
@@ -816,7 +819,7 @@ static whCommClientConfig g_mem_cc_conf = {
816819
.transport_cb = &g_mem_tccb,
817820
.transport_context = (void*)&g_mem_tmcc,
818821
.transport_config = (void*)&g_mem_tmcf,
819-
.client_id = 123,
822+
.client_id = WH_BENCH_CLIENT_ID,
820823
};
821824

822825
static whTransportServerCb g_mem_tscb = WH_TRANSPORT_MEM_SERVER_CB;
@@ -868,7 +871,7 @@ static int _configureClientTransport(whBenchTransportType transport,
868871
.transport_cb = pttcClientShmCb,
869872
.transport_context = (void*)&tccShm,
870873
.transport_config = (void*)&myshmconfig,
871-
.client_id = 12,
874+
.client_id = WH_BENCH_CLIENT_ID,
872875
};
873876

874877
memset(&tccShm, 0, sizeof(posixTransportShmClientContext));
@@ -888,7 +891,7 @@ static int _configureClientTransport(whBenchTransportType transport,
888891
.transport_cb = &pttcClientTcpCb,
889892
.transport_context = (void*)&tccTcp,
890893
.transport_config = (void*)&mytcpconfig,
891-
.client_id = 12,
894+
.client_id = WH_BENCH_CLIENT_ID,
892895
};
893896

894897
memset(&tccTcp, 0, sizeof(posixTransportTcpClientContext));

examples/demo/client/wh_demo_client_keywrap.c

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@
3737

3838
#ifdef WOLFHSM_CFG_KEYWRAP
3939

40-
#define WH_TEST_KEKID 1
40+
#define WH_DEMO_KEYWRAP_KEKID 1
4141
static int _InitServerKek(whClientContext* ctx)
4242
{
4343
/* IMPORTANT NOTE: Server KEK is typically intrinsic or set during
4444
* provisioning. Uploading the KEK via the client is for testing purposes
4545
* only and not intended as a recommendation */
46-
whKeyId serverKeyId = WH_TEST_KEKID;
46+
whKeyId serverKeyId = WH_DEMO_KEYWRAP_KEKID;
4747
whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE;
4848
uint8_t label[WH_NVM_LABEL_LEN] = "Server KEK key";
4949
uint8_t kek[] = {0x03, 0x03, 0x0d, 0xd9, 0xeb, 0x18, 0x17, 0x2e,
@@ -57,43 +57,44 @@ static int _InitServerKek(whClientContext* ctx)
5757

5858
static int _CleanupServerKek(whClientContext* ctx)
5959
{
60-
return wh_Client_KeyErase(ctx, WH_TEST_KEKID);
60+
return wh_Client_KeyErase(ctx, WH_DEMO_KEYWRAP_KEKID);
6161
}
6262

6363
#ifndef NO_AES
6464
#ifdef HAVE_AESGCM
6565

66-
#define WH_TEST_AES_KEYSIZE 16
67-
#define WH_TEST_AES_TEXTSIZE 16
68-
#define WH_TEST_AES_IVSIZE 12
69-
#define WH_TEST_AES_TAGSIZE 16
70-
#define WH_TEST_AES_WRAPPED_KEYSIZE \
71-
(WH_TEST_AES_IVSIZE + WH_TEST_AES_TAGSIZE + WH_TEST_AES_KEYSIZE + \
72-
sizeof(whNvmMetadata))
73-
#define WH_TEST_AESGCM_WRAPKEY_ID 8
66+
#define WH_DEMO_KEYWRAP_AES_KEYSIZE 16
67+
#define WH_DEMO_KEYWRAP_AES_TEXTSIZE 16
68+
#define WH_DEMO_KEYWRAP_AES_IVSIZE 12
69+
#define WH_DEMO_KEYWRAP_AES_TAGSIZE 16
70+
#define WH_DEMO_KEYWRAP_AES_WRAPPED_KEYSIZE \
71+
(WH_DEMO_KEYWRAP_AES_IVSIZE + WH_DEMO_KEYWRAP_AES_TAGSIZE + \
72+
WH_DEMO_KEYWRAP_AES_KEYSIZE + sizeof(whNvmMetadata))
73+
#define WH_DEMO_KEYWRAP_AESGCM_WRAPKEY_ID 8
7474

7575
int wh_DemoClient_AesGcmKeyWrap(whClientContext* client)
7676
{
7777
int ret = 0;
7878
Aes aes[1];
7979
WC_RNG rng[1];
80-
uint8_t key[WH_TEST_AES_KEYSIZE];
81-
uint8_t exportedKey[WH_TEST_AES_KEYSIZE];
80+
uint8_t key[WH_DEMO_KEYWRAP_AES_KEYSIZE];
81+
uint8_t exportedKey[WH_DEMO_KEYWRAP_AES_KEYSIZE];
8282
whNvmMetadata metadata = {
83-
.id = WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, 0, WH_TEST_AESGCM_WRAPKEY_ID),
84-
.label = "AES Key Label",
83+
.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(
84+
client->comm->client_id, WH_DEMO_KEYWRAP_AESGCM_WRAPKEY_ID),
85+
.label = "AES Key Label",
8586
.access = WH_NVM_ACCESS_ANY,
86-
.len = WH_TEST_AES_KEYSIZE};
87+
.len = WH_DEMO_KEYWRAP_AES_KEYSIZE};
8788
whNvmMetadata exportedMetadata;
88-
uint8_t wrappedKey[WH_TEST_AES_WRAPPED_KEYSIZE];
89+
uint8_t wrappedKey[WH_DEMO_KEYWRAP_AES_WRAPPED_KEYSIZE];
8990
whKeyId wrappedKeyId;
9091

9192
const uint8_t plaintext[] = "hello, wolfSSL AES-GCM!";
9293
uint8_t ciphertext[sizeof(plaintext)];
9394
uint8_t decrypted[sizeof(plaintext)];
9495

95-
uint8_t tag[WH_TEST_AES_TAGSIZE];
96-
uint8_t iv[WH_TEST_AES_IVSIZE];
96+
uint8_t tag[WH_DEMO_KEYWRAP_AES_TAGSIZE];
97+
uint8_t iv[WH_DEMO_KEYWRAP_AES_IVSIZE];
9798
const uint8_t aad[] = {0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe,
9899
0xef, 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad,
99100
0xbe, 0xef, 0xab, 0xad, 0xda, 0xd2};
@@ -129,8 +130,8 @@ int wh_DemoClient_AesGcmKeyWrap(whClientContext* client)
129130

130131
/* Now we request the server to wrap the key using the KEK we
131132
* establish above in the first step. */
132-
ret = wh_Client_KeyWrap(client, WC_CIPHER_AES_GCM, WH_TEST_KEKID, key,
133-
sizeof(key), &metadata, wrappedKey,
133+
ret = wh_Client_KeyWrap(client, WC_CIPHER_AES_GCM, WH_DEMO_KEYWRAP_KEKID,
134+
key, sizeof(key), &metadata, wrappedKey,
134135
sizeof(wrappedKey));
135136
if (ret != 0) {
136137
printf("Failed to wh_Client_KeyWrap %d\n", ret);
@@ -146,9 +147,9 @@ int wh_DemoClient_AesGcmKeyWrap(whClientContext* client)
146147
/* Request the server to unwrap and cache the wrapped key we just created.
147148
* This will provide us back a key ID that the client can use to do crypto
148149
* operations */
149-
ret = wh_Client_KeyUnwrapAndCache(client, WC_CIPHER_AES_GCM, WH_TEST_KEKID,
150-
wrappedKey, sizeof(wrappedKey),
151-
&wrappedKeyId);
150+
ret = wh_Client_KeyUnwrapAndCache(client, WC_CIPHER_AES_GCM,
151+
WH_DEMO_KEYWRAP_KEKID, wrappedKey,
152+
sizeof(wrappedKey), &wrappedKeyId);
152153
if (ret != 0) {
153154
printf("Failed to wh_Client_KeyUnwrapAndCache %d\n", ret);
154155
goto cleanup_rng;
@@ -163,7 +164,8 @@ int wh_DemoClient_AesGcmKeyWrap(whClientContext* client)
163164

164165
/* Set the key id for this AES context to the wrapped key ID that the server
165166
* provided us */
166-
ret = wh_Client_AesSetKeyId(aes, wrappedKeyId);
167+
ret =
168+
wh_Client_AesSetKeyId(aes, WH_CLIENT_KEYID_MAKE_WRAPPED(wrappedKeyId));
167169
if (ret != 0) {
168170
printf("Failed to wh_Client_AesSetKeyId %d\n", ret);
169171
goto cleanup_aes;
@@ -209,12 +211,12 @@ int wh_DemoClient_AesGcmKeyWrap(whClientContext* client)
209211
/* Exporting a wrapped key */
210212

211213
/* Request the server to unwrap and export the wrapped key we created */
212-
ret = wh_Client_KeyUnwrapAndExport(client, WC_CIPHER_AES_GCM, WH_TEST_KEKID,
213-
wrappedKey, sizeof(wrappedKey),
214-
&exportedMetadata, exportedKey,
215-
sizeof(exportedKey));
214+
ret = wh_Client_KeyUnwrapAndExport(client, WC_CIPHER_AES_GCM,
215+
WH_DEMO_KEYWRAP_KEKID, wrappedKey,
216+
sizeof(wrappedKey), &exportedMetadata,
217+
exportedKey, sizeof(exportedKey));
216218
if (ret != 0) {
217-
printf("Failed to wh_Client_KeyUnwrapAndCache %d\n", ret);
219+
printf("Failed to wh_Client_KeyUnwrapAndExport %d\n", ret);
218220
goto cleanup_aes;
219221
}
220222

examples/demo/client/wh_demo_client_keywrap.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
#include "wolfhsm/wh_client.h"
55

6+
/* Exposed in header so the demo server can obtain the ID for registration */
7+
#define WH_DEMO_KEYWRAP_AESGCM_WRAPKEY_ID 8
8+
69
int wh_DemoClient_KeyWrap(whClientContext* clientContext);
710

811
#endif /* !DEMO_CLIENT_KEYWRAP_H_ */

examples/posix/wh_posix_client/wh_posix_client.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ static int wh_ClientTask(void* cf, const char* type, int test)
6969

7070
ret = wh_Client_Init(client, config);
7171

72+
if (ret == 0) {
73+
ret = wh_Client_CommInit(client, NULL, NULL);
74+
if (ret != 0) {
75+
printf("Failed to initialize client communication\n");
76+
return -1;
77+
}
78+
}
79+
7280
if (strcmp(type, "dma") == 0) {
7381
#ifdef WOLFSSL_STATIC_MEMORY
7482
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)
8593

8694
printf("Client connecting to server...\n");
8795
if (ret == 0 && test) {
96+
printf("Running client demos...\n");
8897
return wh_DemoClient_All(client);
8998
}
9099

examples/posix/wh_posix_client/wolfhsm_cfg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define WOLFHSM_CFG_COMM_DATA_LEN 5000
3232
#ifndef WOLFHSM_CFG_NO_CRYPTO
3333
#define WOLFHSM_CFG_KEYWRAP
34+
#define WOLFHSM_CFG_GLOBAL_KEYS
3435
#endif
3536

3637
#endif /* WOLFHSM_CFG_H_ */

examples/posix/wh_posix_server/wh_posix_server.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ static int loadAndStoreKeys(whServerContext* server, whKeyId* outKeyId,
128128
#endif /* !WOLFHSM_CFG_NO_CRYPTO */
129129
}
130130

131-
132131
static int wh_ServerTask(void* cf, const char* keyFilePath, int keyId,
133132
int clientId)
134133
{

examples/posix/wh_posix_server/wolfhsm_cfg.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,13 @@
4444
#define WOLFHSM_CFG_CERTIFICATE_MANAGER
4545
#define WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT
4646

47+
#define XMEMFENCE() __atomic_thread_fence(__ATOMIC_SEQ_CST)
48+
4749
#define WOLFHSM_CFG_KEYWRAP_MAX_KEY_SIZE 5000
4850

49-
#define XMEMFENCE() __atomic_thread_fence(__ATOMIC_SEQ_CST)
5051
#ifndef WOLFHSM_CFG_NO_CRYPTO
5152
#define WOLFHSM_CFG_KEYWRAP
53+
#define WOLFHSM_CFG_GLOBAL_KEYS
5254
#endif
55+
5356
#endif /* WOLFHSM_CFG_H_ */

src/wh_client_keywrap.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,16 @@ int wh_Client_KeyUnwrapAndExportResponse(whClientContext* ctx,
185185
if (group != WH_MESSAGE_GROUP_KEY || action != WH_KEY_UNWRAPEXPORT ||
186186
size < sizeof(*resp) ||
187187
size > sizeof(*resp) + sizeof(*metadataOut) + keySz ||
188-
resp->keySz != keySz || resp->cipherType != cipherType) {
188+
resp->cipherType != cipherType) {
189189
return WH_ERROR_ABORTED;
190190
}
191191

192-
if (resp->rc != 0) {
192+
if (resp->rc != WH_ERROR_OK) {
193193
return resp->rc;
194194
}
195+
else if (resp->keySz != keySz) {
196+
return WH_ERROR_BUFFER_SIZE;
197+
}
195198

196199
/* Copy the metadata and key from the response data into metadataOut and
197200
* keyOut */
@@ -298,6 +301,8 @@ int wh_Client_KeyUnwrapAndCacheResponse(whClientContext* ctx,
298301
return resp->rc;
299302
}
300303

304+
/* Server returns ID portion only. Client must track ownership
305+
* and specify appropriate flags when later using the key. */
301306
*keyIdOut = resp->keyId;
302307

303308
return WH_ERROR_OK;

src/wh_keyid.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (C) 2025 wolfSSL Inc.
3+
*
4+
* This file is part of wolfHSM.
5+
*
6+
* wolfHSM is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* wolfHSM is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with wolfHSM. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
/*
20+
* src/wh_keyid.c
21+
*
22+
* KeyId helper function implementations for wolfHSM
23+
*/
24+
25+
#include "wolfhsm/wh_keyid.h"
26+
27+
whKeyId wh_KeyId_TranslateFromClient(uint16_t type, uint16_t clientId,
28+
whKeyId reqId)
29+
{
30+
uint16_t user = clientId;
31+
whKeyId id = reqId & WH_KEYID_MASK;
32+
33+
#ifdef WOLFHSM_CFG_GLOBAL_KEYS
34+
/* Convert global flag to USER=0 */
35+
if ((reqId & WH_KEYID_CLIENT_GLOBAL_FLAG) != 0) {
36+
user = WH_KEYUSER_GLOBAL;
37+
}
38+
#endif
39+
40+
#ifdef WOLFHSM_CFG_KEYWRAP
41+
/* Convert wrapped flag to TYPE=WH_KETYPE_WRAPPED */
42+
if ((reqId & WH_KEYID_CLIENT_WRAPPED_FLAG) != 0) {
43+
type = WH_KEYTYPE_WRAPPED;
44+
}
45+
#endif
46+
47+
return WH_MAKE_KEYID(type, user, id);
48+
}
49+
50+
whKeyId wh_KeyId_TranslateToClient(whKeyId serverId)
51+
{
52+
whKeyId clientId = WH_KEYID_ID(serverId);
53+
54+
#ifdef WOLFHSM_CFG_GLOBAL_KEYS
55+
/* Convert USER=0 to global flag */
56+
if (WH_KEYID_USER(serverId) == WH_KEYUSER_GLOBAL) {
57+
clientId |= WH_KEYID_CLIENT_GLOBAL_FLAG;
58+
}
59+
#endif
60+
61+
#ifdef WOLFHSM_CFG_KEYWRAP
62+
/* Convert TYPE=WRAPPED to wrapped flag */
63+
if (WH_KEYID_TYPE(serverId) == WH_KEYTYPE_WRAPPED) {
64+
clientId |= WH_KEYID_CLIENT_WRAPPED_FLAG;
65+
}
66+
#endif
67+
68+
return clientId;
69+
}

src/wh_nvm.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ int wh_Nvm_Init(whNvmContext* context, const whNvmConfig *config)
4646
context->cb = config->cb;
4747
context->context = config->context;
4848

49+
#if !defined(WOLFHSM_CFG_NO_CRYPTO) && defined(WOLFHSM_CFG_GLOBAL_KEYS)
50+
/* Initialize the global key cache */
51+
memset(&context->globalCache, 0, sizeof(context->globalCache));
52+
#endif
53+
4954
if (context->cb->Init != NULL) {
5055
rc = context->cb->Init(context->context, config->config);
5156
if (rc != 0) {
@@ -64,6 +69,11 @@ int wh_Nvm_Cleanup(whNvmContext* context)
6469
return WH_ERROR_BADARGS;
6570
}
6671

72+
#if !defined(WOLFHSM_CFG_NO_CRYPTO) && defined(WOLFHSM_CFG_GLOBAL_KEYS)
73+
/* Clear the global key cache */
74+
memset(&context->globalCache, 0, sizeof(context->globalCache));
75+
#endif
76+
6777
/* No callback? Return ABORTED */
6878
if (context->cb->Cleanup == NULL) {
6979
return WH_ERROR_ABORTED;

0 commit comments

Comments
 (0)