From c3bc7067b9e7514b90d4fe6f753c3c7d66934ced Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Tue, 16 Dec 2025 08:49:03 +0100 Subject: [PATCH 01/15] nvm: rename IMMUTABLE flag to NONMODIFIABLE --- src/wh_server_keystore.c | 2 +- test/wh_test_cert.c | 49 ++++++++++++++++++++-------------------- wolfhsm/wh_common.h | 4 ++-- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index a2a1857c..52bf0f72 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -1,4 +1,4 @@ -/* +/*keystdd * Copyright (C) 2024 wolfSSL Inc. * * This file is part of wolfHSM. diff --git a/test/wh_test_cert.c b/test/wh_test_cert.c index 3ad87a67..b249609a 100644 --- a/test/wh_test_cert.c +++ b/test/wh_test_cert.c @@ -74,14 +74,14 @@ int whTest_CertServerCfg(whServerConfig* serverCfg) /* Add trusted root certificate for chain A */ WH_TEST_DEBUG_PRINT("Adding trusted root certificate for chain A...\n"); WH_TEST_RETURN_ON_FAIL(wh_Server_CertAddTrusted( - server, rootCertA, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_IMMUTABLE, NULL, 0, - ROOT_A_CERT, ROOT_A_CERT_len)); + server, rootCertA, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONMODIFIABLE, NULL, + 0, ROOT_A_CERT, ROOT_A_CERT_len)); /* Add trusted root certificate for chain B */ WH_TEST_DEBUG_PRINT("Adding trusted root certificate for chain B...\n"); WH_TEST_RETURN_ON_FAIL(wh_Server_CertAddTrusted( - server, rootCertB, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_IMMUTABLE, NULL, 0, - ROOT_B_CERT, ROOT_B_CERT_len)); + server, rootCertB, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONMODIFIABLE, NULL, + 0, ROOT_B_CERT, ROOT_B_CERT_len)); /* Verify valid single cert (intermediate) */ WH_TEST_DEBUG_PRINT( @@ -165,14 +165,14 @@ int whTest_CertClient(whClientContext* client) /* Add root certificates to NVM */ WH_TEST_DEBUG_PRINT("Adding root certificate A to NVM...\n"); WH_TEST_RETURN_ON_FAIL(wh_Client_CertAddTrusted( - client, rootCertA_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_IMMUTABLE, NULL, - 0, ROOT_A_CERT, ROOT_A_CERT_len, &out_rc)); + client, rootCertA_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONMODIFIABLE, + NULL, 0, ROOT_A_CERT, ROOT_A_CERT_len, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); WH_TEST_DEBUG_PRINT("Adding root certificate B to NVM...\n"); WH_TEST_RETURN_ON_FAIL(wh_Client_CertAddTrusted( - client, rootCertB_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_IMMUTABLE, NULL, - 0, ROOT_B_CERT, ROOT_B_CERT_len, &out_rc)); + client, rootCertB_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONMODIFIABLE, + NULL, 0, ROOT_B_CERT, ROOT_B_CERT_len, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); /* Verify valid single cert (intermediate) */ @@ -283,14 +283,14 @@ int whTest_CertClientAcert(whClientContext* client) /* Add trusted certificate to NVM */ WH_TEST_DEBUG_PRINT("Adding trusted certificate to NVM...\n"); WH_TEST_RETURN_ON_FAIL(wh_Client_CertAddTrusted( - client, trustedCertId, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_IMMUTABLE, NULL, - 0, caCert_der, caCert_der_len, &out_rc)); + client, trustedCertId, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONMODIFIABLE, + NULL, 0, caCert_der, caCert_der_len, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); WH_TEST_DEBUG_PRINT("Adding root certificate B to NVM...\n"); WH_TEST_RETURN_ON_FAIL(wh_Client_CertAddTrusted( - client, rootCertB_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_IMMUTABLE, NULL, - 0, ROOT_B_CERT, ROOT_B_CERT_len, &out_rc)); + client, rootCertB_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONMODIFIABLE, + NULL, 0, ROOT_B_CERT, ROOT_B_CERT_len, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); /* Verify attribute certificate */ @@ -350,14 +350,14 @@ int whTest_CertClientDma_ClientServerTestInternal(whClientContext* client) /* Add root certificates to NVM */ WH_TEST_DEBUG_PRINT("Adding root certificate A to NVM...\n"); WH_TEST_RETURN_ON_FAIL(wh_Client_CertAddTrustedDma( - client, rootCertA_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_IMMUTABLE, NULL, - 0, ROOT_A_CERT, ROOT_A_CERT_len, &out_rc)); + client, rootCertA_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONMODIFIABLE, + NULL, 0, ROOT_A_CERT, ROOT_A_CERT_len, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); WH_TEST_DEBUG_PRINT("Adding root certificate B to NVM...\n"); WH_TEST_RETURN_ON_FAIL(wh_Client_CertAddTrustedDma( - client, rootCertB_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_IMMUTABLE, NULL, - 0, ROOT_B_CERT, ROOT_B_CERT_len, &out_rc)); + client, rootCertB_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONMODIFIABLE, + NULL, 0, ROOT_B_CERT, ROOT_B_CERT_len, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); /* Verify valid single cert (intermediate) */ @@ -470,14 +470,14 @@ int whTest_CertClientAcertDma_ClientServerTestInternal(whClientContext* client) /* Add trusted certificate to NVM */ WH_TEST_DEBUG_PRINT("Adding trusted certificate to NVM...\n"); WH_TEST_RETURN_ON_FAIL(wh_Client_CertAddTrustedDma( - client, trustedCertId, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_IMMUTABLE, NULL, - 0, caCert_der, caCert_der_len, &out_rc)); + client, trustedCertId, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONMODIFIABLE, + NULL, 0, caCert_der, caCert_der_len, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); WH_TEST_DEBUG_PRINT("Adding root certificate B to NVM...\n"); WH_TEST_RETURN_ON_FAIL(wh_Client_CertAddTrustedDma( - client, rootCertB_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_IMMUTABLE, NULL, - 0, ROOT_B_CERT, ROOT_B_CERT_len, &out_rc)); + client, rootCertB_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONMODIFIABLE, + NULL, 0, ROOT_B_CERT, ROOT_B_CERT_len, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); /* Verify attribute certificate */ @@ -526,16 +526,17 @@ static int whTest_CertNonExportable(whClientContext* client) /* Add exportable certificate */ WH_TEST_DEBUG_PRINT("Adding exportable certificate...\n"); - WH_TEST_RETURN_ON_FAIL(wh_Client_CertAddTrusted( - client, exportable_cert_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_IMMUTABLE, - NULL, 0, ROOT_A_CERT, ROOT_A_CERT_len, &out_rc)); + WH_TEST_RETURN_ON_FAIL( + wh_Client_CertAddTrusted(client, exportable_cert_id, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONMODIFIABLE, NULL, 0, + ROOT_A_CERT, ROOT_A_CERT_len, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); /* Add non-exportable certificate */ WH_TEST_DEBUG_PRINT("Adding non-exportable certificate...\n"); WH_TEST_RETURN_ON_FAIL(wh_Client_CertAddTrusted( client, nonexportable_cert_id, WH_NVM_ACCESS_ANY, - WH_NVM_FLAGS_IMMUTABLE | WH_NVM_FLAGS_NONEXPORTABLE, NULL, 0, + WH_NVM_FLAGS_NONMODIFIABLE | WH_NVM_FLAGS_NONEXPORTABLE, NULL, 0, ROOT_B_CERT, ROOT_B_CERT_len, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); diff --git a/wolfhsm/wh_common.h b/wolfhsm/wh_common.h index cfe0d936..3806eb4b 100644 --- a/wolfhsm/wh_common.h +++ b/wolfhsm/wh_common.h @@ -68,8 +68,8 @@ typedef uint16_t whNvmAccess; typedef uint16_t whNvmFlags; /* Generic NVM flags */ -/* Cannot be overwritten */ -#define WH_NVM_FLAGS_IMMUTABLE ((whNvmFlags)1 << 0) +/* Cannot be modified */ +#define WH_NVM_FLAGS_NONMODIFIABLE ((whNvmFlags)1 << 0) /* Holds private/secret data */ #define WH_NVM_FLAGS_SENSITIVE ((whNvmFlags)1 << 1) /* Cannot be exported */ From d43a3f85179821584c9dd93a4cf5f23d5efdafd6 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Tue, 16 Dec 2025 14:50:33 +0100 Subject: [PATCH 02/15] nvm, keystore: enforce NVM flags controls --- src/wh_nvm.c | 109 ++++++++ src/wh_server_cert.c | 6 +- src/wh_server_crypto.c | 28 +- src/wh_server_keystore.c | 498 +++++++++++++++++++++++------------ src/wh_server_nvm.c | 31 +-- wolfhsm/wh_common.h | 2 + wolfhsm/wh_nvm.h | 6 + wolfhsm/wh_server_keystore.h | 60 +++++ 8 files changed, 534 insertions(+), 206 deletions(-) diff --git a/src/wh_nvm.c b/src/wh_nvm.c index 5c81981d..0fd150f5 100644 --- a/src/wh_nvm.c +++ b/src/wh_nvm.c @@ -33,6 +33,69 @@ #include "wolfhsm/wh_nvm.h" +typedef enum { + WH_NVM_OP_ADD = 0, + WH_NVM_OP_READ, + WH_NVM_OP_DESTROY, +} whNvmOp; + +/* Centralized policy check. + * existing_meta is optionally populated when the object is present. */ +static int wh_Nvm_CheckPolicy(whNvmContext* context, whNvmOp op, whNvmId id, + whNvmMetadata* existing_meta) +{ + whNvmMetadata meta; + int ret; + + if ((context == NULL) || (context->cb == NULL)) { + return WH_ERROR_BADARGS; + } + + ret = wh_Nvm_GetMetadata(context, id, &meta); + if (existing_meta != NULL && ret == WH_ERROR_OK) { + *existing_meta = meta; + } + + switch (op) { + case WH_NVM_OP_ADD: + if (ret == WH_ERROR_OK) { + if (meta.flags & WH_NVM_FLAGS_NONMODIFIABLE) { + return WH_ERROR_ACCESS; + } + } + else if (ret != WH_ERROR_NOTFOUND) { + return ret; + } + break; + + case WH_NVM_OP_DESTROY: + if (ret == WH_ERROR_OK) { + if (meta.flags & (WH_NVM_FLAGS_NONMODIFIABLE | + WH_NVM_FLAGS_NONDESTROYABLE)) { + return WH_ERROR_ACCESS; + } + } + else if (ret != WH_ERROR_NOTFOUND) { + return ret; + } + break; + + case WH_NVM_OP_READ: + if (ret != WH_ERROR_OK) { + return ret; + } + if (meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) { + return WH_ERROR_ACCESS; + } + break; + + default: + return WH_ERROR_BADARGS; + } + + return WH_ERROR_OK; +} + int wh_Nvm_Init(whNvmContext* context, const whNvmConfig *config) { @@ -152,6 +215,19 @@ int wh_Nvm_AddObject(whNvmContext* context, whNvmMetadata *meta, return context->cb->AddObject(context->context, meta, data_len, data); } +int wh_Nvm_AddObjectChecked(whNvmContext* context, whNvmMetadata* meta, + whNvmSize data_len, const uint8_t* data) +{ + int ret; + + ret = wh_Nvm_CheckPolicy(context, WH_NVM_OP_ADD, meta->id, NULL); + if (ret != WH_ERROR_OK) { + return ret; + } + + return wh_Nvm_AddObject(context, meta, data_len, data); +} + int wh_Nvm_List(whNvmContext* context, whNvmAccess access, whNvmFlags flags, whNvmId start_id, whNvmId *out_count, whNvmId *out_id) @@ -200,6 +276,26 @@ int wh_Nvm_DestroyObjects(whNvmContext* context, whNvmId list_count, return context->cb->DestroyObjects(context->context, list_count, id_list); } +int wh_Nvm_DestroyObjectsChecked(whNvmContext* context, whNvmId list_count, + const whNvmId* id_list) +{ + whNvmId i; + int ret; + + if (id_list == NULL && list_count != 0) { + return WH_ERROR_BADARGS; + } + + for (i = 0; i < list_count; i++) { + ret = wh_Nvm_CheckPolicy(context, WH_NVM_OP_DESTROY, id_list[i], NULL); + if (ret != WH_ERROR_OK) { + return ret; + } + } + + return wh_Nvm_DestroyObjects(context, list_count, id_list); +} + int wh_Nvm_Read(whNvmContext* context, whNvmId id, whNvmSize offset, whNvmSize data_len, uint8_t* data) @@ -215,3 +311,16 @@ int wh_Nvm_Read(whNvmContext* context, whNvmId id, whNvmSize offset, } return context->cb->Read(context->context, id, offset, data_len, data); } + +int wh_Nvm_ReadChecked(whNvmContext* context, whNvmId id, whNvmSize offset, + whNvmSize data_len, uint8_t* data) +{ + int ret; + + ret = wh_Nvm_CheckPolicy(context, WH_NVM_OP_READ, id, NULL); + if (ret != WH_ERROR_OK) { + return ret; + } + + return wh_Nvm_Read(context, id, offset, data_len, data); +} diff --git a/src/wh_server_cert.c b/src/wh_server_cert.c index 1edaf9bd..0d44e778 100644 --- a/src/wh_server_cert.c +++ b/src/wh_server_cert.c @@ -124,9 +124,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, *inout_keyId, - cacheBufSize, &cacheBuf, - &cacheMeta); + rc = wh_Server_KeystoreGetCacheSlotChecked( + server, *inout_keyId, cacheBufSize, &cacheBuf, + &cacheMeta); if (rc == WH_ERROR_OK) { rc = wc_GetSubjectPubKeyInfoDerFromCert( cert_ptr, cert_len + idx, cacheBuf, &cacheBufSize); diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c index afb19b60..282dab6c 100644 --- a/src/wh_server_crypto.c +++ b/src/wh_server_crypto.c @@ -243,8 +243,8 @@ int wh_Server_CacheImportRsaKey(whServerContext* ctx, RsaKey* key, } /* get a free slot */ - ret = wh_Server_KeystoreGetCacheSlot(ctx, keyId, max_size, &cacheBuf, - &cacheMeta); + ret = wh_Server_KeystoreGetCacheSlotChecked(ctx, keyId, max_size, &cacheBuf, + &cacheMeta); if (ret == 0) { ret = wh_Crypto_RsaSerializeKeyDer(key, max_size, cacheBuf, &der_size); } @@ -586,8 +586,8 @@ int wh_Server_EccKeyCacheImport(whServerContext* ctx, ecc_key* key, return WH_ERROR_BADARGS; } /* get a free slot */ - ret = wh_Server_KeystoreGetCacheSlot(ctx, keyId, max_size, &cacheBuf, - &cacheMeta); + ret = wh_Server_KeystoreGetCacheSlotChecked(ctx, keyId, max_size, &cacheBuf, + &cacheMeta); if (ret == WH_ERROR_OK) { ret = wh_Crypto_EccSerializeKeyDer(key, max_size, cacheBuf, &der_size); } @@ -646,8 +646,8 @@ int wh_Server_CacheImportEd25519Key(whServerContext* ctx, ed25519_key* key, return WH_ERROR_BADARGS; } - ret = wh_Server_KeystoreGetCacheSlot(ctx, keyId, max_size, &cacheBuf, - &cacheMeta); + ret = wh_Server_KeystoreGetCacheSlotChecked(ctx, keyId, max_size, &cacheBuf, + &cacheMeta); if (ret == WH_ERROR_OK) { ret = wh_Crypto_Ed25519SerializeKeyDer(key, max_size, cacheBuf, &der_size); @@ -708,8 +708,8 @@ 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, keyId, keySz, &cacheBuf, - &cacheMeta); + ret = wh_Server_KeystoreGetCacheSlotChecked(server, keyId, keySz, + &cacheBuf, &cacheMeta); if (ret == 0) { memcpy(cacheBuf, der_buf, keySz); /* Update metadata to cache the key */ @@ -774,8 +774,8 @@ int wh_Server_MlDsaKeyCacheImport(whServerContext* ctx, MlDsaKey* key, return WH_ERROR_BADARGS; } - ret = wh_Server_KeystoreGetCacheSlot(ctx, keyId, MAX_MLDSA_DER_SIZE, - &cacheBuf, &cacheMeta); + ret = wh_Server_KeystoreGetCacheSlotChecked(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); @@ -1308,8 +1308,8 @@ int wh_Server_HkdfKeyCacheImport(whServerContext* ctx, const uint8_t* keyData, } /* Get a free slot */ - ret = wh_Server_KeystoreGetCacheSlot(ctx, keyId, keySize, &cacheBuf, - &cacheMeta); + ret = wh_Server_KeystoreGetCacheSlotChecked(ctx, keyId, keySize, &cacheBuf, + &cacheMeta); if (ret == WH_ERROR_OK) { /* Copy the key data to cache buffer */ memcpy(cacheBuf, keyData, keySize); @@ -1347,8 +1347,8 @@ int wh_Server_CmacKdfKeyCacheImport(whServerContext* ctx, return WH_ERROR_BADARGS; } - ret = wh_Server_KeystoreGetCacheSlot(ctx, keyId, keySize, &cacheBuf, - &cacheMeta); + ret = wh_Server_KeystoreGetCacheSlotChecked(ctx, keyId, keySize, &cacheBuf, + &cacheMeta); if (ret == WH_ERROR_OK) { memcpy(cacheBuf, keyData, keySize); } diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index 52bf0f72..cf9f43a9 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -1,4 +1,4 @@ -/*keystdd +/* * Copyright (C) 2024 wolfSSL Inc. * * This file is part of wolfHSM. @@ -83,6 +83,147 @@ static whKeyCacheContext* _GetCacheContext(whServerContext* server, return &server->localCache; } +typedef enum { + WH_KS_OP_CACHE = 0, + WH_KS_OP_COMMIT, + WH_KS_OP_ERASE, + WH_KS_OP_EVICT, + WH_KS_OP_EXPORT, +} whKsOp; + +static int _KeyIsCommitted(whServerContext* server, whKeyId keyId) +{ + int ret; + int big; + int index; + + whKeyCacheContext* ctx = _GetCacheContext(server, keyId); + ret = _FindInCache(server, keyId, &index, &big, NULL, NULL); + if (ret == WH_ERROR_NOTFOUND) { + return 0; + } + else if (ret != WH_ERROR_OK) { + return ret; + } + + if (big == 0) { + return ctx->cache[index].committed; + } + else { + return ctx->bigCache[index].committed; + } +} +/* Centralized cache/NVM policy: enforce NONMODIFIABLE/NONEXPORTABLE at the + * keystore layer. Usage enforcement remains separate. */ +static int _KeystoreCheckPolicy(whServerContext* server, whKsOp op, + whKeyId keyId, const whNvmMetadata* metaOpt) +{ + whNvmMetadata* cacheMeta = NULL; + whNvmMetadata nvmMeta; + const whNvmMetadata* meta = metaOpt; + int ret; + + if ((server == NULL) || WH_KEYID_ISERASED(keyId)) { + return WH_ERROR_BADARGS; + } + + /* See if the key is already in cache to use its flags */ + ret = _FindInCache(server, keyId, NULL, NULL, NULL, &cacheMeta); + if (ret == WH_ERROR_OK) { + if (cacheMeta != NULL) { + switch (op) { + case WH_KS_OP_CACHE: + if (cacheMeta->flags & WH_NVM_FLAGS_NONMODIFIABLE) { + return WH_ERROR_ACCESS; + } + else { + return WH_ERROR_OK; + } + case WH_KS_OP_ERASE: + if (cacheMeta->flags & (WH_NVM_FLAGS_NONMODIFIABLE | + WH_NVM_FLAGS_NONDESTROYABLE)) { + return WH_ERROR_ACCESS; + } + else { + return WH_ERROR_OK; + } + break; + case WH_KS_OP_EVICT: + /* committed key can be evicted */ + if (_KeyIsCommitted(server, keyId)) { + return WH_ERROR_OK; + } + if (cacheMeta->flags & WH_NVM_FLAGS_NONMODIFIABLE) { + return WH_ERROR_ACCESS; + } + else { + return WH_ERROR_OK; + } + break; + case WH_KS_OP_COMMIT: + return WH_ERROR_OK; + case WH_KS_OP_EXPORT: + if (cacheMeta->flags & WH_NVM_FLAGS_NONEXPORTABLE) { + return WH_ERROR_ACCESS; + } + else { + return WH_ERROR_OK; + } + break; + } + meta = cacheMeta; + } + } + else if (ret != WH_ERROR_NOTFOUND) { + return ret; + } + + /* Get NVM metadata if not found in cache */ + ret = wh_Nvm_GetMetadata(server->nvm, keyId, &nvmMeta); + if (ret == WH_ERROR_OK) { + switch (op) { + /* should never happen */ + case WH_KS_OP_COMMIT: + return WH_ERROR_ABORTED; + case WH_KS_OP_CACHE: + if (nvmMeta.flags & WH_NVM_FLAGS_NONMODIFIABLE) { + return WH_ERROR_ACCESS; + } + else { + return WH_ERROR_OK; + } + break; + case WH_KS_OP_ERASE: + if (nvmMeta.flags & (WH_NVM_FLAGS_NONMODIFIABLE | + WH_NVM_FLAGS_NONDESTROYABLE)) { + return WH_ERROR_ACCESS; + } + else { + return WH_ERROR_OK; + } + break; + case WH_KS_OP_EXPORT: + if (nvmMeta.flags & WH_NVM_FLAGS_NONEXPORTABLE) { + return WH_ERROR_ACCESS; + } + else { + return WH_ERROR_OK; + } + break; + case WH_KS_OP_EVICT: + /* no-op */ + return WH_ERROR_OK; + } + if (meta == NULL) { + meta = &nvmMeta; + } + } + else if (ret != WH_ERROR_NOTFOUND) { + return ret; + } + + return WH_ERROR_OK; +} /** * @brief Find a key in the specified cache context */ @@ -137,6 +278,13 @@ static int _FindInKeyCache(whKeyCacheContext* ctx, whKeyId keyId, return ret; } +static int _EvictSlot(uint8_t* buf, whNvmMetadata* meta) +{ + meta->id = WH_KEYID_ERASED; + memset(buf, 0, meta->len); + return WH_ERROR_OK; +} + /** * @brief Get an available cache slot from the specified cache context */ @@ -145,6 +293,7 @@ static int _GetKeyCacheSlot(whKeyCacheContext* ctx, uint16_t keySz, { int foundIndex = -1; int i; + int evictRet = WH_ERROR_OK; uint8_t* slotBuf = NULL; whNvmMetadata* slotMeta = NULL; @@ -166,8 +315,12 @@ static int _GetKeyCacheSlot(whKeyCacheContext* ctx, uint16_t keySz, if (foundIndex == -1) { for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; i++) { if (ctx->cache[i].committed == 1) { - foundIndex = i; - break; + evictRet = + _EvictSlot(ctx->cache[i].buffer, ctx->cache[i].meta); + if (evictRet == WH_ERROR_OK) { + foundIndex = i; + break; + } } } } @@ -192,8 +345,12 @@ static int _GetKeyCacheSlot(whKeyCacheContext* ctx, uint16_t keySz, if (foundIndex == -1) { for (i = 0; i < WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT; i++) { if (ctx->bigCache[i].committed == 1) { - foundIndex = i; - break; + evictRet = _EvictSlot(ctx->bigCache[i].buffer, + ctx->bigCache[i].meta); + if (evictRet == WH_ERROR_OK) { + foundIndex = i; + break; + } } } } @@ -223,14 +380,18 @@ static int _GetKeyCacheSlot(whKeyCacheContext* ctx, uint16_t keySz, /** * @brief Evict a key from the specified cache context + * zeroes the buffer */ static int _EvictKeyFromCache(whKeyCacheContext* ctx, whKeyId keyId) { - whNvmMetadata* meta = NULL; - int ret = _FindInKeyCache(ctx, keyId, NULL, NULL, NULL, &meta); + whNvmMetadata* meta = NULL; + uint8_t* outBuffer = NULL; + + + int ret = _FindInKeyCache(ctx, keyId, NULL, NULL, &outBuffer, &meta); if (ret == WH_ERROR_OK && meta != NULL) { - meta->id = WH_KEYID_ERASED; + return _EvictSlot(outBuffer, meta); } return ret; @@ -317,31 +478,60 @@ int wh_Server_KeystoreGetUniqueId(whServerContext* server, whNvmId* inout_id) return WH_ERROR_OK; } -/* find an available slot for the size, return the slots buffer and meta */ +/* find a slot to cache a key. If key is already there, is evicetd first */ int wh_Server_KeystoreGetCacheSlot(whServerContext* server, whKeyId keyId, uint16_t keySz, uint8_t** outBuf, whNvmMetadata** outMeta) { whKeyCacheContext* ctx; + int ret; + int idx = -1; + int isBig = -1; + uint8_t* buf = NULL; + whNvmMetadata* foundMeta = NULL; 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 */ + ret = _FindInCache(server, keyId, &idx, &isBig, &buf, &foundMeta); + if (ret == WH_ERROR_OK) { + /* Key is already cached; evict it first */ + ret = wh_Server_KeystoreEvictKey(server, keyId); + if (ret != WH_ERROR_OK) { + return ret; + } + } + else if (ret != WH_ERROR_NOTFOUND) { + return ret; + } + + ctx = _GetCacheContext(server, keyId); return _GetKeyCacheSlot(ctx, keySz, outBuf, outMeta); } -int wh_Server_KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta, - uint8_t* in) +int wh_Server_KeystoreGetCacheSlotChecked(whServerContext* server, + whKeyId keyId, uint16_t keySz, + uint8_t** outBuf, + whNvmMetadata** outMeta) { - int i; - int foundIndex = -1; - whKeyCacheContext* ctx; + int ret; + ret = _KeystoreCheckPolicy(server, WH_KS_OP_CACHE, keyId, NULL); + if (ret != WH_ERROR_OK) { + return ret; + } + return wh_Server_KeystoreGetCacheSlot(server, keyId, keySz, outBuf, + outMeta); +} + +static int _KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta, + uint8_t* in, int checked) +{ + uint8_t* slotBuf; + whNvmMetadata* slotMeta; + int ret; /* make sure id is valid */ if ((server == NULL) || (meta == NULL) || (in == NULL) || @@ -351,114 +541,38 @@ 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 (ctx->bigCache[i].meta->id == meta->id) { - /* Evict the key from big cache */ - ctx->bigCache[i].meta->id = WH_KEYID_ERASED; - break; - } - } + if (checked) { + ret = wh_Server_KeystoreGetCacheSlotChecked(server, meta->id, meta->len, + &slotBuf, &slotMeta); } 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 (ctx->cache[i].meta->id == meta->id) { - /* Evict the key from regular cache */ - ctx->cache[i].meta->id = WH_KEYID_ERASED; - break; - } - } + ret = wh_Server_KeystoreGetCacheSlot(server, meta->id, meta->len, + &slotBuf, &slotMeta); + } + if (ret != WH_ERROR_OK) { + return ret; } - /* check if we need to use big cache instead */ - 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(ctx->cache[i].meta->id) || - (ctx->cache[i].meta->id == meta->id)) { - foundIndex = i; - break; - } - } - - /* 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 (ctx->cache[i].committed == 1) { - foundIndex = i; - break; - } - } - } + memcpy(slotBuf, in, meta->len); + memcpy((uint8_t*)slotMeta, (uint8_t*)meta, sizeof(whNvmMetadata)); + _MarkKeyCommitted(_GetCacheContext(server, meta->id), meta->id, 0); - /* write key if slot found */ - if (foundIndex != -1) { - 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 committed */ - if (wh_Nvm_GetMetadata(server->nvm, meta->id, meta) == - WH_ERROR_NOTFOUND) { - ctx->cache[foundIndex].committed = 0; - } - else { - ctx->cache[foundIndex].committed = 1; - } - WH_DEBUG_SERVER_VERBOSE("cacheKey: caching keyid=%u\n", meta->id); - WH_DEBUG_VERBOSE_HEXDUMP("[server] cacheKey: key=", in, meta->len); - } - } - else { - /* 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(ctx->bigCache[i].meta->id) || - (ctx->bigCache[i].meta->id == meta->id)) { - foundIndex = i; - break; - } - } + WH_DEBUG_SERVER_VERBOSE("hsmCacheKey: cached keyid=0x%X, len=%u\n", + meta->id, meta->len); + WH_DEBUG_VERBOSE_HEXDUMP("[server] cacheKey: key=", in, meta->len); - /* 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 (ctx->bigCache[i].committed == 1) { - foundIndex = i; - break; - } - } - } + return WH_ERROR_OK; +} - /* write key if slot found */ - if (foundIndex != -1) { - 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 committed */ - if (wh_Nvm_GetMetadata(server->nvm, meta->id, meta) == - WH_ERROR_NOTFOUND) { - ctx->bigCache[foundIndex].committed = 0; - } - else { - ctx->bigCache[foundIndex].committed = 1; - } - } - } - /* return error if we are out of cache slots */ - if (foundIndex == -1) { - return WH_ERROR_NOSPACE; - } - else { - WH_DEBUG_SERVER_VERBOSE("hsmCacheKey: cached keyid=0x%X in slot %d, len=%u\n", - meta->id, foundIndex, meta->len); - } - return 0; +int wh_Server_KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta, + uint8_t* in) +{ + return _KeystoreCacheKey(server, meta, in, 0); +} +int wh_Server_KeystoreCacheKeyChecked(whServerContext* server, + whNvmMetadata* meta, uint8_t* in) +{ + return _KeystoreCacheKey(server, meta, in, 1); } static int _FindInCache(whServerContext* server, whKeyId keyId, int* out_index, @@ -623,6 +737,19 @@ int wh_Server_KeystoreReadKey(whServerContext* server, whKeyId keyId, return ret; } +int wh_Server_KeystoreReadKeyChecked(whServerContext* server, whKeyId keyId, + whNvmMetadata* outMeta, uint8_t* out, + uint32_t* outSz) +{ + int ret; + + ret = _KeystoreCheckPolicy(server, WH_KS_OP_EXPORT, keyId, NULL); + if (ret != WH_ERROR_OK) { + return ret; + } + return wh_Server_KeystoreReadKey(server, keyId, outMeta, out, outSz); +} + int wh_Server_KeystoreEvictKey(whServerContext* server, whNvmId keyId) { int ret = 0; @@ -646,6 +773,17 @@ int wh_Server_KeystoreEvictKey(whServerContext* server, whNvmId keyId) return ret; } +int wh_Server_KeystoreEvictKeyChecked(whServerContext* server, whNvmId keyId) +{ + int ret; + + ret = _KeystoreCheckPolicy(server, WH_KS_OP_EVICT, keyId, NULL); + if (ret != WH_ERROR_OK) { + return ret; + } + return wh_Server_KeystoreEvictKey(server, keyId); +} + int wh_Server_KeystoreCommitKey(whServerContext* server, whNvmId keyId) { uint8_t* slotBuf; @@ -678,6 +816,17 @@ int wh_Server_KeystoreCommitKey(whServerContext* server, whNvmId keyId) return ret; } +int wh_Server_KeystoreCommitKeyChecked(whServerContext* server, whNvmId keyId) +{ + int ret; + + ret = _KeystoreCheckPolicy(server, WH_KS_OP_COMMIT, keyId, NULL); + if (ret != WH_ERROR_OK) { + return ret; + } + return wh_Server_KeystoreCommitKey(server, keyId); +} + int wh_Server_KeystoreEraseKey(whServerContext* server, whNvmId keyId) { if ((server == NULL) || (WH_KEYID_ISERASED(keyId))) { @@ -695,6 +844,23 @@ int wh_Server_KeystoreEraseKey(whServerContext* server, whNvmId keyId) return wh_Nvm_DestroyObjects(server->nvm, 1, &keyId); } +int wh_Server_KeystoreEraseKeyChecked(whServerContext* server, whNvmId keyId) +{ + if ((server == NULL) || (WH_KEYID_ISERASED(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_KeystoreEvictKeyChecked(server, keyId); + + /* destroy the object */ + return wh_Nvm_DestroyObjectsChecked(server->nvm, 1, &keyId); +} + #ifdef WOLFHSM_CFG_KEYWRAP #ifndef NO_AES @@ -1475,7 +1641,7 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, } /* write the key */ if (ret == WH_ERROR_OK) { - ret = wh_Server_KeystoreCacheKey(server, meta, in); + ret = wh_Server_KeystoreCacheKeyChecked(server, meta, in); resp.rc = ret; } if (ret == WH_ERROR_OK) { @@ -1519,7 +1685,8 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, /* write the key using DMA */ if (ret == WH_ERROR_OK) { - ret = wh_Server_KeystoreCacheKeyDma(server, meta, req.key.addr); + ret = wh_Server_KeystoreCacheKeyDmaChecked(server, meta, + req.key.addr); resp.rc = ret; /* propagate bad address to client if DMA operation failed */ if (ret != WH_ERROR_OK) { @@ -1545,7 +1712,7 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, (void)wh_MessageKeystore_TranslateExportDmaRequest( magic, (whMessageKeystore_ExportDmaRequest*)req_packet, &req); - ret = wh_Server_KeystoreExportKeyDma( + ret = wh_Server_KeystoreExportKeyDmaChecked( server, wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, server->comm->client_id, req.id), @@ -1578,7 +1745,7 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, (void)wh_MessageKeystore_TranslateEvictRequest( magic, (whMessageKeystore_EvictRequest*)req_packet, &req); - ret = wh_Server_KeystoreEvictKey( + ret = wh_Server_KeystoreEvictKeyChecked( server, wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, server->comm->client_id, req.id)); @@ -1604,23 +1771,12 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, keySz = WOLFHSM_CFG_COMM_DATA_LEN - sizeof(resp); /* read the key */ - ret = wh_Server_KeystoreReadKey( + ret = wh_Server_KeystoreReadKeyChecked( server, wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, server->comm->client_id, req.id), meta, out, &keySz); - /* Check if key is non-exportable */ - if (ret == WH_ERROR_OK && - (meta->flags & WH_NVM_FLAGS_NONEXPORTABLE)) { - ret = WH_ERROR_ACCESS; - /* Clear any key data that may have been read */ - memset(out, 0, keySz); - WH_LOG_F(&server->log, WH_LOG_LEVEL_SECEVENT, - "Export attempt for non-exportable keyId 0x%08X", - meta->id); - } - resp.rc = ret; /* Only provide key output if no error */ @@ -1646,7 +1802,7 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, (void)wh_MessageKeystore_TranslateCommitRequest( magic, (whMessageKeystore_CommitRequest*)req_packet, &req); - ret = wh_Server_KeystoreCommitKey( + ret = wh_Server_KeystoreCommitKeyChecked( server, wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, server->comm->client_id, req.id)); @@ -1667,7 +1823,7 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, (void)wh_MessageKeystore_TranslateEraseRequest( magic, (whMessageKeystore_EraseRequest*)req_packet, &req); - ret = wh_Server_KeystoreEraseKey( + ret = wh_Server_KeystoreEraseKeyChecked( server, wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, server->comm->client_id, req.id)); @@ -1879,43 +2035,22 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, #ifdef WOLFHSM_CFG_DMA -int wh_Server_KeystoreCacheKeyDma(whServerContext* server, whNvmMetadata* meta, - uint64_t keyAddr) +int _KeystoreCacheKeyDma(whServerContext* server, whNvmMetadata* meta, + uint64_t keyAddr, int checked) { 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 (ctx->bigCache[i].meta->id == meta->id) { - /* Evict the key from big cache */ - ctx->bigCache[i].meta->id = WH_KEYID_ERASED; - break; - } - } + /* Get a cache slot */ + if (checked) { + ret = wh_Server_KeystoreGetCacheSlotChecked(server, meta->id, meta->len, + &buffer, &slotMeta); } 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 (ctx->cache[i].meta->id == meta->id) { - /* Evict the key from regular cache */ - ctx->cache[i].meta->id = WH_KEYID_ERASED; - break; - } - } + ret = wh_Server_KeystoreGetCacheSlot(server, meta->id, meta->len, + &buffer, &slotMeta); } - - /* Get a cache slot */ - ret = wh_Server_KeystoreGetCacheSlot(server, meta->id, meta->len, &buffer, - &slotMeta); if (ret != 0) { return ret; } @@ -1931,9 +2066,23 @@ int wh_Server_KeystoreCacheKeyDma(whServerContext* server, whNvmMetadata* meta, memset(buffer, 0, meta->len); slotMeta->id = WH_KEYID_ERASED; } + else { + _MarkKeyCommitted(_GetCacheContext(server, meta->id), meta->id, 0); + } return ret; } +int wh_Server_KeystoreCacheKeyDma(whServerContext* server, whNvmMetadata* meta, + uint64_t keyAddr) +{ + return _KeystoreCacheKeyDma(server, meta, keyAddr, 0); +} + +int wh_Server_KeystoreCacheKeyDmaChecked(whServerContext* server, + whNvmMetadata* meta, uint64_t keyAddr) +{ + return _KeystoreCacheKeyDma(server, meta, keyAddr, 1); +} int wh_Server_KeystoreExportKeyDma(whServerContext* server, whKeyId keyId, uint64_t keyAddr, uint64_t keySz, @@ -1949,11 +2098,6 @@ int wh_Server_KeystoreExportKeyDma(whServerContext* server, whKeyId keyId, return ret; } - /* Check if key is non-exportable */ - if (cacheMeta->flags & WH_NVM_FLAGS_NONEXPORTABLE) { - return WH_ERROR_ACCESS; - } - if (keySz < cacheMeta->len) { return WH_ERROR_NOSPACE; } @@ -1966,6 +2110,20 @@ int wh_Server_KeystoreExportKeyDma(whServerContext* server, whKeyId keyId, return ret; } +int wh_Server_KeystoreExportKeyDmaChecked(whServerContext* server, + whKeyId keyId, uint64_t keyAddr, + uint64_t keySz, + whNvmMetadata* outMeta) +{ + int ret; + + ret = _KeystoreCheckPolicy(server, WH_KS_OP_EXPORT, keyId, NULL); + if (ret != WH_ERROR_OK) { + return ret; + } + return wh_Server_KeystoreExportKeyDma(server, keyId, keyAddr, keySz, + outMeta); +} #endif /* WOLFHSM_CFG_DMA */ int wh_Server_KeystoreEnforceKeyUsage(const whNvmMetadata* meta, diff --git a/src/wh_server_nvm.c b/src/wh_server_nvm.c index 59e385f7..6425dd1e 100644 --- a/src/wh_server_nvm.c +++ b/src/wh_server_nvm.c @@ -64,10 +64,6 @@ static int _HandleNvmRead(whServerContext* server, uint8_t* out_data, return rc; } - if (meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) { - return WH_ERROR_ACCESS; - } - if (offset >= meta.len) return WH_ERROR_BADARGS; @@ -76,7 +72,7 @@ static int _HandleNvmRead(whServerContext* server, uint8_t* out_data, len = meta.len - offset; } - rc = wh_Nvm_Read(server->nvm, id, offset, len, out_data); + rc = wh_Nvm_ReadChecked(server->nvm, id, offset, len, out_data); if (rc != WH_ERROR_OK) return rc; *out_len = len; @@ -241,7 +237,8 @@ int wh_Server_HandleNvmRequest(whServerContext* server, meta.flags = req.flags; meta.len = req.len; memcpy(meta.label, req.label, sizeof(meta.label)); - resp.rc = wh_Nvm_AddObject(server->nvm, &meta, req.len, data); + resp.rc = + wh_Nvm_AddObjectChecked(server->nvm, &meta, req.len, data); } } /* Convert the response struct */ @@ -265,9 +262,10 @@ int wh_Server_HandleNvmRequest(whServerContext* server, if (req.list_count <= WH_MESSAGE_NVM_MAX_DESTROY_OBJECTS_COUNT) { /* Process the DestroyObjects action */ - resp.rc = wh_Nvm_DestroyObjects(server->nvm, - req.list_count, req.list); - } else { + resp.rc = wh_Nvm_DestroyObjectsChecked( + server->nvm, req.list_count, req.list); + } + else { /* Problem in transport or request */ resp.rc = WH_ERROR_ABORTED; } @@ -337,10 +335,9 @@ int wh_Server_HandleNvmRequest(whServerContext* server, } if (resp.rc == 0) { /* Process the AddObject action */ - resp.rc = wh_Nvm_AddObject(server->nvm, - (whNvmMetadata*)metadata, - req.data_len, - (const uint8_t*)data); + resp.rc = + wh_Nvm_AddObjectChecked(server->nvm, (whNvmMetadata*)metadata, + req.data_len, (const uint8_t*)data); } if (resp.rc == 0) { /* perform platform-specific host address processing */ @@ -376,11 +373,7 @@ int wh_Server_HandleNvmRequest(whServerContext* server, wh_MessageNvm_TranslateReadDmaRequest(magic, (whMessageNvm_ReadDmaRequest*)req_packet, &req); - /* Check metadata for non-exportable flag */ resp.rc = wh_Nvm_GetMetadata(server->nvm, req.id, &meta); - if (resp.rc == 0 && (meta.flags & WH_NVM_FLAGS_NONEXPORTABLE)) { - resp.rc = WH_ERROR_ACCESS; - } } if (resp.rc == 0) { @@ -407,8 +400,8 @@ int wh_Server_HandleNvmRequest(whServerContext* server, } if (resp.rc == 0) { /* Process the Read action */ - resp.rc = wh_Nvm_Read(server->nvm, req.id, req.offset, read_len, - (uint8_t*)data); + resp.rc = wh_Nvm_ReadChecked(server->nvm, req.id, req.offset, + read_len, (uint8_t*)data); } if (resp.rc == 0) { /* perform platform-specific host address processing */ diff --git a/wolfhsm/wh_common.h b/wolfhsm/wh_common.h index 3806eb4b..6d60c5d2 100644 --- a/wolfhsm/wh_common.h +++ b/wolfhsm/wh_common.h @@ -78,6 +78,8 @@ typedef uint16_t whNvmFlags; #define WH_NVM_FLAGS_LOCAL ((whNvmFlags)1 << 3) /* Cannot be cached nor committed */ #define WH_NVM_FLAGS_EPHEMERAL ((whNvmFlags)1 << 4) +/* Cannot be destroyed (but can be modified) */ +#define WH_NVM_FLAGS_NONDESTROYABLE ((whNvmFlags)1 << 11) /* Key usage policy flags * diff --git a/wolfhsm/wh_nvm.h b/wolfhsm/wh_nvm.h index 17d1405f..374180d0 100644 --- a/wolfhsm/wh_nvm.h +++ b/wolfhsm/wh_nvm.h @@ -118,6 +118,8 @@ int wh_Nvm_AddObjectWithReclaim(whNvmContext* context, whNvmMetadata *meta, int wh_Nvm_AddObject(whNvmContext* context, whNvmMetadata *meta, whNvmSize data_len, const uint8_t* data); +int wh_Nvm_AddObjectChecked(whNvmContext* context, whNvmMetadata* meta, + whNvmSize data_len, const uint8_t* data); int wh_Nvm_List(whNvmContext* context, whNvmAccess access, whNvmFlags flags, whNvmId start_id, @@ -128,8 +130,12 @@ int wh_Nvm_GetMetadata(whNvmContext* context, whNvmId id, int wh_Nvm_DestroyObjects(whNvmContext* context, whNvmId list_count, const whNvmId* id_list); +int wh_Nvm_DestroyObjectsChecked(whNvmContext* context, whNvmId list_count, + const whNvmId* id_list); int wh_Nvm_Read(whNvmContext* context, whNvmId id, whNvmSize offset, whNvmSize data_len, uint8_t* data); +int wh_Nvm_ReadChecked(whNvmContext* context, whNvmId id, whNvmSize offset, + whNvmSize data_len, uint8_t* data); #endif /* !WOLFHSM_WH_NVM_H_ */ diff --git a/wolfhsm/wh_server_keystore.h b/wolfhsm/wh_server_keystore.h index d891b180..2b065933 100644 --- a/wolfhsm/wh_server_keystore.h +++ b/wolfhsm/wh_server_keystore.h @@ -62,6 +62,10 @@ int wh_Server_KeystoreGetUniqueId(whServerContext* server, whNvmId* inout_id); int wh_Server_KeystoreGetCacheSlot(whServerContext* server, whKeyId keyId, uint16_t keySz, uint8_t** outBuf, whNvmMetadata** outMeta); +int wh_Server_KeystoreGetCacheSlotChecked(whServerContext* server, + whKeyId keyId, uint16_t keySz, + uint8_t** outBuf, + whNvmMetadata** outMeta); /** * @brief Cache a key in server memory @@ -77,6 +81,15 @@ int wh_Server_KeystoreGetCacheSlot(whServerContext* server, whKeyId keyId, int wh_Server_KeystoreCacheKey(whServerContext* server, whNvmMetadata* meta, uint8_t* in); +/** + * @brief Cache a key after enforcing keystore policy + * + * Runs policy checks (access/usage/etc.) before calling + * wh_Server_KeystoreCacheKey. + */ +int wh_Server_KeystoreCacheKeyChecked(whServerContext* server, + whNvmMetadata* meta, uint8_t* in); + /** * @brief Ensure a key is in cache, loading it from NVM if necessary * @@ -108,6 +121,15 @@ int wh_Server_KeystoreReadKey(whServerContext* server, whKeyId keyId, whNvmMetadata* outMeta, uint8_t* out, uint32_t* outSz); +/** + * @brief Read a key with policy enforcement + * + * Performs keystore policy checks before reading from cache/NVM. + */ +int wh_Server_KeystoreReadKeyChecked(whServerContext* server, whKeyId keyId, + whNvmMetadata* outMeta, uint8_t* out, + uint32_t* outSz); + /** * @brief Remove a key from cache * @@ -119,6 +141,13 @@ int wh_Server_KeystoreReadKey(whServerContext* server, whKeyId keyId, */ int wh_Server_KeystoreEvictKey(whServerContext* server, whNvmId keyId); +/** + * @brief Evict a key with policy enforcement + * + * Checks policy before removing the key from cache. + */ +int wh_Server_KeystoreEvictKeyChecked(whServerContext* server, whNvmId keyId); + /** * @brief Commit a cached key to NVM storage * @@ -130,6 +159,13 @@ int wh_Server_KeystoreEvictKey(whServerContext* server, whNvmId keyId); */ int wh_Server_KeystoreCommitKey(whServerContext* server, whNvmId keyId); +/** + * @brief Commit a cached key to NVM with policy enforcement + * + * Runs keystore policy checks before committing. + */ +int wh_Server_KeystoreCommitKeyChecked(whServerContext* server, whNvmId keyId); + /** * @brief Erase a key from both cache and NVM * @@ -141,6 +177,13 @@ int wh_Server_KeystoreCommitKey(whServerContext* server, whNvmId keyId); */ int wh_Server_KeystoreEraseKey(whServerContext* server, whNvmId keyId); +/** + * @brief Erase a key with policy enforcement + * + * Runs keystore policy checks before evicting/destroying. + */ +int wh_Server_KeystoreEraseKeyChecked(whServerContext* server, whNvmId keyId); + /** * @brief Handle key management requests from clients * @@ -174,6 +217,13 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, int wh_Server_KeystoreCacheKeyDma(whServerContext* server, whNvmMetadata* meta, uint64_t keyAddr); +/** + * @brief cache a key with DMA after policy enforcement + * + * Performs policy checks before exporting a key via DMA. + */ +int wh_Server_KeystoreCacheKeyDmaChecked(whServerContext* server, + whNvmMetadata* meta, uint64_t keyAddr); /** * @brief Export a key using DMA transfer * @@ -190,6 +240,16 @@ int wh_Server_KeystoreExportKeyDma(whServerContext* server, whKeyId keyId, uint64_t keyAddr, uint64_t keySz, whNvmMetadata* outMeta); +/** + * @brief Export a key with DMA after policy enforcement + * + * Performs policy checks before exporting a key via DMA. + */ +int wh_Server_KeystoreExportKeyDmaChecked(whServerContext* server, + whKeyId keyId, uint64_t keyAddr, + uint64_t keySz, + whNvmMetadata* outMeta); + /** * @brief Enforce key usage policy given metadata From 01361ff0a315e165a272b633087800b97f8e4b34 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Wed, 17 Dec 2025 16:56:06 +0100 Subject: [PATCH 03/15] keystore: support key revocation --- src/wh_client.c | 56 ++++++++++++++++++++ src/wh_message_keystore.c | 24 +++++++++ src/wh_server_keystore.c | 98 ++++++++++++++++++++++++++++++++++- wolfhsm/wh_client.h | 40 ++++++++++++++ wolfhsm/wh_message.h | 1 + wolfhsm/wh_message_keystore.h | 21 ++++++++ wolfhsm/wh_server_keystore.h | 7 +++ 7 files changed, 246 insertions(+), 1 deletion(-) diff --git a/src/wh_client.c b/src/wh_client.c index 605fedbb..cb219da0 100644 --- a/src/wh_client.c +++ b/src/wh_client.c @@ -1070,6 +1070,62 @@ int wh_Client_KeyErase(whClientContext* c, whNvmId keyId) return ret; } +int wh_Client_KeyRevokeRequest(whClientContext* c, whNvmId keyId) +{ + whMessageKeystore_RevokeRequest* req = NULL; + + if (c == NULL || keyId == WH_KEYID_ERASED) { + return WH_ERROR_BADARGS; + } + + req = (whMessageKeystore_RevokeRequest*)wh_CommClient_GetDataPtr(c->comm); + if (req == NULL) { + return WH_ERROR_BADARGS; + } + req->id = keyId; + + return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_KEY, WH_KEY_REVOKE, + sizeof(*req), (uint8_t*)req); +} + +int wh_Client_KeyRevokeResponse(whClientContext* c) +{ + uint16_t group; + uint16_t action; + uint16_t size; + int ret; + whMessageKeystore_RevokeResponse* resp = NULL; + + if (c == NULL) { + return WH_ERROR_BADARGS; + } + + resp = (whMessageKeystore_RevokeResponse*)wh_CommClient_GetDataPtr(c->comm); + if (resp == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(c, &group, &action, &size, (uint8_t*)resp); + if (ret == 0) { + if (resp->rc != 0) { + ret = resp->rc; + } + } + return ret; +} + +int wh_Client_KeyRevoke(whClientContext* c, whNvmId keyId) +{ + int ret; + ret = wh_Client_KeyRevokeRequest(c, keyId); + if (ret == 0) { + do { + ret = wh_Client_KeyRevokeResponse(c); + } while (ret == WH_ERROR_NOTREADY); + } + return ret; +} + int wh_Client_CounterInitRequest(whClientContext* c, whNvmId counterId, uint32_t counter) { diff --git a/src/wh_message_keystore.c b/src/wh_message_keystore.c index 3463b760..4c0369ff 100644 --- a/src/wh_message_keystore.c +++ b/src/wh_message_keystore.c @@ -163,6 +163,30 @@ int wh_MessageKeystore_TranslateEraseResponse( return 0; } +/* Key Revoke Request translation */ +int wh_MessageKeystore_TranslateRevokeRequest( + uint16_t magic, const whMessageKeystore_RevokeRequest* src, + whMessageKeystore_RevokeRequest* dest) +{ + if ((src == NULL) || (dest == NULL)) { + return WH_ERROR_BADARGS; + } + WH_T16(magic, dest, src, id); + return 0; +} + +/* Key Revoke Response translation */ +int wh_MessageKeystore_TranslateRevokeResponse( + uint16_t magic, const whMessageKeystore_RevokeResponse* src, + whMessageKeystore_RevokeResponse* dest) +{ + if ((src == NULL) || (dest == NULL)) { + return WH_ERROR_BADARGS; + } + WH_T32(magic, dest, src, rc); + return 0; +} + #ifdef WOLFHSM_CFG_DMA /* * DMA-based keystore operations diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index cf9f43a9..3d5cc2c5 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -89,6 +89,7 @@ typedef enum { WH_KS_OP_ERASE, WH_KS_OP_EVICT, WH_KS_OP_EXPORT, + WH_KS_OP_REVOKE, } whKsOp; static int _KeyIsCommitted(whServerContext* server, whKeyId keyId) @@ -170,6 +171,9 @@ static int _KeystoreCheckPolicy(whServerContext* server, whKsOp op, return WH_ERROR_OK; } break; + case WH_KS_OP_REVOKE: + /* revoke is allowed even if already NONMODIFIABLE */ + return WH_ERROR_OK; } meta = cacheMeta; } @@ -210,6 +214,9 @@ static int _KeystoreCheckPolicy(whServerContext* server, whKsOp op, return WH_ERROR_OK; } break; + case WH_KS_OP_REVOKE: + /* allow revoke even if already NONMODIFIABLE */ + return WH_ERROR_OK; case WH_KS_OP_EVICT: /* no-op */ return WH_ERROR_OK; @@ -630,7 +637,7 @@ int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId, ret = _FindInCache(server, keyId, &foundIndex, &foundBigIndex, cacheBufOut, cacheMetaOut); - if (ret != WH_ERROR_OK) { + if (ret == WH_ERROR_NOTFOUND) { /* 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 */ @@ -653,6 +660,8 @@ int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId, * successful*/ memcpy((uint8_t*)*cacheMetaOut, (uint8_t*)tmpMeta, sizeof(whNvmMetadata)); + _MarkKeyCommitted(_GetCacheContext(server, keyId), keyId, + 1); } } } @@ -861,6 +870,73 @@ int wh_Server_KeystoreEraseKeyChecked(whServerContext* server, whNvmId keyId) return wh_Nvm_DestroyObjectsChecked(server->nvm, 1, &keyId); } +static void _revokeKey(whNvmMetadata* meta) +{ + + /* Set NONMODIFIABLE flag and clear all usage flags */ + meta->flags |= WH_NVM_FLAGS_NONMODIFIABLE; + meta->flags &= ~WH_NVM_FLAGS_USAGE_ANY; +} + +static int _isKeyRevoked(whNvmMetadata* meta) +{ + if ((meta->flags & WH_NVM_FLAGS_NONMODIFIABLE) && + ((meta->flags & WH_NVM_FLAGS_USAGE_ANY) == 0)) { + return 1; + } + + return 0; +} + +int wh_Server_KeystoreRevokeKey(whServerContext* server, whNvmId keyId) +{ + int ret; + int isInNvm = 0; + uint8_t* cacheBuf = NULL; + whNvmMetadata* cacheMeta = NULL; + + if ((server == NULL) || WH_KEYID_ISERASED(keyId)) { + return WH_ERROR_BADARGS; + } + + ret = _KeystoreCheckPolicy(server, WH_KS_OP_REVOKE, keyId, NULL); + if (ret != WH_ERROR_OK) { + return ret; + } + + ret = wh_Nvm_GetMetadata(server->nvm, keyId, NULL); + if (ret == WH_ERROR_OK) { + isInNvm = 1; + } + else if (ret != WH_ERROR_NOTFOUND) { + return ret; + } + + /* be sure to have the key in the cache */ + ret = wh_Server_KeystoreFreshenKey(server, keyId, &cacheBuf, &cacheMeta); + if (ret != WH_ERROR_OK) { + return ret; + } + + /* if already revoked and committed, nothing to do */ + if (_isKeyRevoked(cacheMeta) && _KeyIsCommitted(server, keyId)) { + return WH_ERROR_OK; + } + + /* Revoke the key by updating its metadata */ + _revokeKey(cacheMeta); + /* commit the changes */ + if (isInNvm) { + ret = wh_Nvm_AddObjectWithReclaim(server->nvm, cacheMeta, + cacheMeta->len, cacheBuf); + if (ret == WH_ERROR_OK) { + _MarkKeyCommitted(_GetCacheContext(server, keyId), keyId, 1); + } + } + + return ret; +} + #ifdef WOLFHSM_CFG_KEYWRAP #ifndef NO_AES @@ -1836,6 +1912,26 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, *out_resp_size = sizeof(resp); } break; + case WH_KEY_REVOKE: { + whMessageKeystore_RevokeRequest req; + whMessageKeystore_RevokeResponse resp; + + (void)wh_MessageKeystore_TranslateRevokeRequest( + magic, (whMessageKeystore_RevokeRequest*)req_packet, &req); + + ret = wh_Server_KeystoreRevokeKey( + server, + wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, + server->comm->client_id, req.id)); + resp.rc = ret; + ret = WH_ERROR_OK; + + (void)wh_MessageKeystore_TranslateRevokeResponse( + magic, &resp, (whMessageKeystore_RevokeResponse*)resp_packet); + + *out_resp_size = sizeof(resp); + } break; + #ifdef WOLFHSM_CFG_KEYWRAP case WH_KEY_KEYWRAP: { whMessageKeystore_KeyWrapRequest wrapReq = {0}; diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index 4a5a7b92..15c00e56 100644 --- a/wolfhsm/wh_client.h +++ b/wolfhsm/wh_client.h @@ -762,6 +762,46 @@ int wh_Client_KeyEraseResponse(whClientContext* c); */ int wh_Client_KeyErase(whClientContext* c, whNvmId keyId); +/** + * @brief Sends a key revoke request to the server. + * + * This function prepares and sends a key revoke request message to the server. + * The message contains the specified key ID. This function does not block; + * it returns immediately after sending the request. + * + * @param[in] c Pointer to the client context. + * @param[in] keyId Key ID to be revoked. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_KeyRevokeRequest(whClientContext* c, whNvmId keyId); + +/** + * @brief Receives a key revoke response from the server. + * + * This function attempts to process a key revoke response message from the + * server. It validates the response. This function does not block; it returns + * WH_ERROR_NOTREADY if a response has not been received. + * + * @param[in] c Pointer to the client context. + * @return int Returns 0 on success, WH_ERROR_NOTREADY if no response is + * available, or a negative error code on failure. + */ +int wh_Client_KeyRevokeResponse(whClientContext* c); + +/** + * @brief Sends a key revoke request to the server and receives the response. + * + * This function handles the complete process of sending a key revoke request + * to the server and receiving the response. It sends the request and + * repeatedly attempts to receive a valid response. This function blocks until + * the entire operation is complete or an error occurs. + * + * @param[in] c Pointer to the client context. + * @param[in] keyId Key ID to be revoked. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_KeyRevoke(whClientContext* c, whNvmId keyId); + #ifdef WOLFHSM_CFG_DMA /** diff --git a/wolfhsm/wh_message.h b/wolfhsm/wh_message.h index 1b34c2ef..60167cbf 100644 --- a/wolfhsm/wh_message.h +++ b/wolfhsm/wh_message.h @@ -59,6 +59,7 @@ enum WH_KEY_ENUM { WH_KEY_EXPORT, WH_KEY_COMMIT, WH_KEY_ERASE, + WH_KEY_REVOKE, WH_KEY_CACHE_DMA, WH_KEY_EXPORT_DMA, WH_KEY_KEYWRAP, diff --git a/wolfhsm/wh_message_keystore.h b/wolfhsm/wh_message_keystore.h index a0588c3a..d598a295 100644 --- a/wolfhsm/wh_message_keystore.h +++ b/wolfhsm/wh_message_keystore.h @@ -153,6 +153,27 @@ int wh_MessageKeystore_TranslateEraseResponse( uint16_t magic, const whMessageKeystore_EraseResponse* src, whMessageKeystore_EraseResponse* dest); +/* Key Revoke Request */ +typedef struct { + uint16_t id; + uint8_t WH_PAD[6]; +} whMessageKeystore_RevokeRequest; + +/* Key Revoke Response */ +typedef struct { + uint32_t rc; + uint8_t WH_PAD[4]; +} whMessageKeystore_RevokeResponse; + +/* Key Revoke translation functions */ +int wh_MessageKeystore_TranslateRevokeRequest( + uint16_t magic, const whMessageKeystore_RevokeRequest* src, + whMessageKeystore_RevokeRequest* dest); + +int wh_MessageKeystore_TranslateRevokeResponse( + uint16_t magic, const whMessageKeystore_RevokeResponse* src, + whMessageKeystore_RevokeResponse* dest); + /* * DMA-based keystore operations */ diff --git a/wolfhsm/wh_server_keystore.h b/wolfhsm/wh_server_keystore.h index 2b065933..a029a16e 100644 --- a/wolfhsm/wh_server_keystore.h +++ b/wolfhsm/wh_server_keystore.h @@ -184,6 +184,13 @@ int wh_Server_KeystoreEraseKey(whServerContext* server, whNvmId keyId); */ int wh_Server_KeystoreEraseKeyChecked(whServerContext* server, whNvmId keyId); +/** + * @brief Revoke a key (clears usage and marks non-modifiable) + * + * Placeholder implementation for key revocation. + */ +int wh_Server_KeystoreRevokeKey(whServerContext* server, whNvmId keyId); + /** * @brief Handle key management requests from clients * From c21082de17c2cb4fefbcf5226ecf9132be3d19fd Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Wed, 17 Dec 2025 18:24:42 +0100 Subject: [PATCH 04/15] keystore: use wh_Nvm_GetMetadata instead of wh_Nvm_List in GetUniqueId --- src/wh_server_keystore.c | 7 ++----- test/config/wolfhsm_cfg.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index 3d5cc2c5..19045f2e 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -436,8 +436,6 @@ int wh_Server_KeystoreGetUniqueId(whServerContext* server, whNvmId* 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); @@ -463,9 +461,8 @@ int wh_Server_KeystoreGetUniqueId(whServerContext* server, whNvmId* inout_id) } /* 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) { + ret = wh_Nvm_GetMetadata(server->nvm, buildId, NULL); + if (ret == WH_ERROR_NOTFOUND) { /* key doesn't exist in NVM, we found a candidate ID */ found = 1; break; diff --git a/test/config/wolfhsm_cfg.h b/test/config/wolfhsm_cfg.h index 6c8b926f..c3484199 100644 --- a/test/config/wolfhsm_cfg.h +++ b/test/config/wolfhsm_cfg.h @@ -44,7 +44,7 @@ #define WOLFHSM_CFG_NVM_OBJECT_COUNT 30 #define WOLFHSM_CFG_SERVER_KEYCACHE_COUNT 9 #define WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE 300 -#define WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT 2 +#define WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT 3 #define WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE WOLFHSM_CFG_COMM_DATA_LEN #define WOLFHSM_CFG_DMAADDR_COUNT 8 #define WOLFHSM_CFG_SERVER_CUSTOMCB_COUNT 6 From 0cdd77e4e502c2dab4e5d0b9a95381ffe7a7c434 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Thu, 18 Dec 2025 00:14:06 +0100 Subject: [PATCH 05/15] keystore: invert check and avoid a nested if level, typos --- src/wh_server_keystore.c | 58 ++++++++++++++++++------------------ wolfhsm/wh_server_keystore.h | 2 +- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index 19045f2e..908a27d1 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -100,12 +100,9 @@ static int _KeyIsCommitted(whServerContext* server, whKeyId keyId) whKeyCacheContext* ctx = _GetCacheContext(server, keyId); ret = _FindInCache(server, keyId, &index, &big, NULL, NULL); - if (ret == WH_ERROR_NOTFOUND) { + if (ret != WH_ERROR_OK) { return 0; } - else if (ret != WH_ERROR_OK) { - return ret; - } if (big == 0) { return ctx->cache[index].committed; @@ -394,7 +391,6 @@ static int _EvictKeyFromCache(whKeyCacheContext* ctx, whKeyId keyId) whNvmMetadata* meta = NULL; uint8_t* outBuffer = NULL; - int ret = _FindInKeyCache(ctx, keyId, NULL, NULL, &outBuffer, &meta); if (ret == WH_ERROR_OK && meta != NULL) { @@ -482,7 +478,7 @@ int wh_Server_KeystoreGetUniqueId(whServerContext* server, whNvmId* inout_id) return WH_ERROR_OK; } -/* find a slot to cache a key. If key is already there, is evicetd first */ +/* find a slot to cache a key. If key is already there, is evicted first */ int wh_Server_KeystoreGetCacheSlot(whServerContext* server, whKeyId keyId, uint16_t keySz, uint8_t** outBuf, whNvmMetadata** outMeta) @@ -634,32 +630,36 @@ int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId, ret = _FindInCache(server, keyId, &foundIndex, &foundBigIndex, cacheBufOut, cacheMetaOut); - if (ret == WH_ERROR_NOTFOUND) { - /* 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; - } + if (ret != WH_ERROR_NOTFOUND) { + return ret; + } - /* Not in cache. Check if it is in NVM */ - ret = wh_Nvm_GetMetadata(server->nvm, keyId, tmpMeta); + /* key not in the cache */ + + /* 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) { + /* Key found in NVM, get a free cache slot */ + ret = wh_Server_KeystoreGetCacheSlot(server, keyId, tmpMeta->len, + cacheBufOut, cacheMetaOut); if (ret == WH_ERROR_OK) { - /* Key found in NVM, get a free cache slot */ - ret = wh_Server_KeystoreGetCacheSlot(server, keyId, tmpMeta->len, - cacheBufOut, cacheMetaOut); + /* Read the key from NVM into the cache slot */ + ret = wh_Nvm_Read(server->nvm, keyId, 0, tmpMeta->len, + *cacheBufOut); if (ret == WH_ERROR_OK) { - /* Read the key from NVM into the cache slot */ - ret = wh_Nvm_Read(server->nvm, keyId, 0, tmpMeta->len, - *cacheBufOut); - if (ret == WH_ERROR_OK) { - /* Copy the metadata to the cache slot if key read is - * successful*/ - memcpy((uint8_t*)*cacheMetaOut, (uint8_t*)tmpMeta, - sizeof(whNvmMetadata)); - _MarkKeyCommitted(_GetCacheContext(server, keyId), keyId, - 1); - } + /* Copy the metadata to the cache slot if key read is + * successful*/ + memcpy((uint8_t*)*cacheMetaOut, (uint8_t*)tmpMeta, + sizeof(whNvmMetadata)); + _MarkKeyCommitted(_GetCacheContext(server, keyId), keyId, + 1); } } } diff --git a/wolfhsm/wh_server_keystore.h b/wolfhsm/wh_server_keystore.h index a029a16e..70e32452 100644 --- a/wolfhsm/wh_server_keystore.h +++ b/wolfhsm/wh_server_keystore.h @@ -225,7 +225,7 @@ int wh_Server_KeystoreCacheKeyDma(whServerContext* server, whNvmMetadata* meta, uint64_t keyAddr); /** - * @brief cache a key with DMA after policy enforcement + * @brief Cache a key with DMA after policy enforcement * * Performs policy checks before exporting a key via DMA. */ From 46fcb7ae3fd425a36fd30ee2c9860d814ee435b9 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 19 Dec 2025 10:16:37 +0100 Subject: [PATCH 06/15] keystore: bring the key in the cache in wh_Server_KeystoreExportKeyDma This uniform the behaviour with `wh_Server_KeystoreExportKey` --- src/wh_server_keystore.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index 908a27d1..4eb2b4fd 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -2185,9 +2185,9 @@ int wh_Server_KeystoreExportKeyDma(whServerContext* server, whKeyId keyId, uint8_t* buffer; whNvmMetadata* cacheMeta; - /* Find key in cache */ - ret = _FindInCache(server, keyId, NULL, NULL, &buffer, &cacheMeta); - if (ret != 0) { + /* bring key in cache */ + ret = wh_Server_KeystoreFreshenKey(server, keyId, &buffer, &cacheMeta); + if (ret != WH_ERROR_OK) { return ret; } From d6d8311170dd8493f47f837477241c25eed9df83 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 19 Dec 2025 10:37:36 +0100 Subject: [PATCH 07/15] keystore: improvements * use whKeyId instead of whNvmId in wh_Client_KeyRevoke API * propagate WH_ERROR_NOT_FOUND in check policy functions * simplify check policies functions --- src/wh_client.c | 2 +- src/wh_nvm.c | 38 +++----- src/wh_server_keystore.c | 179 ++++++++++++++--------------------- wolfhsm/wh_client.h | 4 +- wolfhsm/wh_server_keystore.h | 2 +- 5 files changed, 90 insertions(+), 135 deletions(-) diff --git a/src/wh_client.c b/src/wh_client.c index cb219da0..927c4bf0 100644 --- a/src/wh_client.c +++ b/src/wh_client.c @@ -1114,7 +1114,7 @@ int wh_Client_KeyRevokeResponse(whClientContext* c) return ret; } -int wh_Client_KeyRevoke(whClientContext* c, whNvmId keyId) +int wh_Client_KeyRevoke(whClientContext* c, whKeyId keyId) { int ret; ret = wh_Client_KeyRevokeRequest(c, keyId); diff --git a/src/wh_nvm.c b/src/wh_nvm.c index 0fd150f5..bc32b09e 100644 --- a/src/wh_nvm.c +++ b/src/wh_nvm.c @@ -52,38 +52,29 @@ static int wh_Nvm_CheckPolicy(whNvmContext* context, whNvmOp op, whNvmId id, } ret = wh_Nvm_GetMetadata(context, id, &meta); - if (existing_meta != NULL && ret == WH_ERROR_OK) { + if (ret != WH_ERROR_OK) { + return ret; + } + + if (existing_meta != NULL) { *existing_meta = meta; } switch (op) { case WH_NVM_OP_ADD: - if (ret == WH_ERROR_OK) { - if (meta.flags & WH_NVM_FLAGS_NONMODIFIABLE) { - return WH_ERROR_ACCESS; - } - } - else if (ret != WH_ERROR_NOTFOUND) { - return ret; + if (meta.flags & WH_NVM_FLAGS_NONMODIFIABLE) { + return WH_ERROR_ACCESS; } break; case WH_NVM_OP_DESTROY: - if (ret == WH_ERROR_OK) { - if (meta.flags & (WH_NVM_FLAGS_NONMODIFIABLE | - WH_NVM_FLAGS_NONDESTROYABLE)) { - return WH_ERROR_ACCESS; - } - } - else if (ret != WH_ERROR_NOTFOUND) { - return ret; + if (meta.flags & + (WH_NVM_FLAGS_NONMODIFIABLE | WH_NVM_FLAGS_NONDESTROYABLE)) { + return WH_ERROR_ACCESS; } break; case WH_NVM_OP_READ: - if (ret != WH_ERROR_OK) { - return ret; - } if (meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) { return WH_ERROR_ACCESS; } @@ -97,16 +88,15 @@ static int wh_Nvm_CheckPolicy(whNvmContext* context, whNvmOp op, whNvmId id, } -int wh_Nvm_Init(whNvmContext* context, const whNvmConfig *config) +int wh_Nvm_Init(whNvmContext* context, const whNvmConfig* config) { int rc = 0; - if ( (context == NULL) || - (config == NULL) ) { + if ((context == NULL) || (config == NULL)) { return WH_ERROR_BADARGS; } - context->cb = config->cb; + context->cb = config->cb; context->context = config->context; #if !defined(WOLFHSM_CFG_NO_CRYPTO) && defined(WOLFHSM_CFG_GLOBAL_KEYS) @@ -221,7 +211,7 @@ int wh_Nvm_AddObjectChecked(whNvmContext* context, whNvmMetadata* meta, int ret; ret = wh_Nvm_CheckPolicy(context, WH_NVM_OP_ADD, meta->id, NULL); - if (ret != WH_ERROR_OK) { + if (ret != WH_ERROR_OK && ret != WH_ERROR_NOTFOUND) { return ret; } diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index 4eb2b4fd..1838e617 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -114,116 +114,82 @@ static int _KeyIsCommitted(whServerContext* server, whKeyId keyId) /* Centralized cache/NVM policy: enforce NONMODIFIABLE/NONEXPORTABLE at the * keystore layer. Usage enforcement remains separate. */ static int _KeystoreCheckPolicy(whServerContext* server, whKsOp op, - whKeyId keyId, const whNvmMetadata* metaOpt) + whKeyId keyId) { - whNvmMetadata* cacheMeta = NULL; - whNvmMetadata nvmMeta; - const whNvmMetadata* meta = metaOpt; - int ret; + whNvmMetadata* cacheMeta = NULL; + whNvmMetadata nvmMeta; + whNvmFlags flags; + int ret; + int foundInCache = 0; + int foundInNvm = 0; if ((server == NULL) || WH_KEYID_ISERASED(keyId)) { return WH_ERROR_BADARGS; } - /* See if the key is already in cache to use its flags */ + /* Check cache first */ ret = _FindInCache(server, keyId, NULL, NULL, NULL, &cacheMeta); - if (ret == WH_ERROR_OK) { - if (cacheMeta != NULL) { - switch (op) { - case WH_KS_OP_CACHE: - if (cacheMeta->flags & WH_NVM_FLAGS_NONMODIFIABLE) { - return WH_ERROR_ACCESS; - } - else { - return WH_ERROR_OK; - } - case WH_KS_OP_ERASE: - if (cacheMeta->flags & (WH_NVM_FLAGS_NONMODIFIABLE | - WH_NVM_FLAGS_NONDESTROYABLE)) { - return WH_ERROR_ACCESS; - } - else { - return WH_ERROR_OK; - } - break; - case WH_KS_OP_EVICT: - /* committed key can be evicted */ - if (_KeyIsCommitted(server, keyId)) { - return WH_ERROR_OK; - } - if (cacheMeta->flags & WH_NVM_FLAGS_NONMODIFIABLE) { - return WH_ERROR_ACCESS; - } - else { - return WH_ERROR_OK; - } - break; - case WH_KS_OP_COMMIT: - return WH_ERROR_OK; - case WH_KS_OP_EXPORT: - if (cacheMeta->flags & WH_NVM_FLAGS_NONEXPORTABLE) { - return WH_ERROR_ACCESS; - } - else { - return WH_ERROR_OK; - } - break; - case WH_KS_OP_REVOKE: - /* revoke is allowed even if already NONMODIFIABLE */ - return WH_ERROR_OK; - } - meta = cacheMeta; - } + if (ret == WH_ERROR_OK && cacheMeta != NULL) { + foundInCache = 1; } - else if (ret != WH_ERROR_NOTFOUND) { + else if (ret != WH_ERROR_OK && ret != WH_ERROR_NOTFOUND) { return ret; } - /* Get NVM metadata if not found in cache */ - ret = wh_Nvm_GetMetadata(server->nvm, keyId, &nvmMeta); - if (ret == WH_ERROR_OK) { - switch (op) { - /* should never happen */ - case WH_KS_OP_COMMIT: - return WH_ERROR_ABORTED; - case WH_KS_OP_CACHE: - if (nvmMeta.flags & WH_NVM_FLAGS_NONMODIFIABLE) { - return WH_ERROR_ACCESS; - } - else { - return WH_ERROR_OK; - } - break; - case WH_KS_OP_ERASE: - if (nvmMeta.flags & (WH_NVM_FLAGS_NONMODIFIABLE | - WH_NVM_FLAGS_NONDESTROYABLE)) { - return WH_ERROR_ACCESS; - } - else { - return WH_ERROR_OK; - } - break; - case WH_KS_OP_EXPORT: - if (nvmMeta.flags & WH_NVM_FLAGS_NONEXPORTABLE) { - return WH_ERROR_ACCESS; - } - else { - return WH_ERROR_OK; - } - break; - case WH_KS_OP_REVOKE: - /* allow revoke even if already NONMODIFIABLE */ - return WH_ERROR_OK; - case WH_KS_OP_EVICT: - /* no-op */ - return WH_ERROR_OK; + /* Check NVM if not in cache */ + if (!foundInCache) { + ret = wh_Nvm_GetMetadata(server->nvm, keyId, &nvmMeta); + if (ret == WH_ERROR_OK) { + foundInNvm = 1; } - if (meta == NULL) { - meta = &nvmMeta; + else if (ret != WH_ERROR_NOTFOUND) { + return ret; } } - else if (ret != WH_ERROR_NOTFOUND) { - return ret; + + /* Key not found */ + if (!foundInCache && !foundInNvm) { + return WH_ERROR_NOTFOUND; + } + + /* Get flags from the appropriate source */ + flags = (foundInCache) ? cacheMeta->flags : nvmMeta.flags; + + switch (op) { + case WH_KS_OP_CACHE: + if (flags & WH_NVM_FLAGS_NONMODIFIABLE) { + return WH_ERROR_ACCESS; + } + break; + + case WH_KS_OP_EVICT: + if (_KeyIsCommitted(server, keyId)) { + /* Committed keys can always be evicted */ + break; + } + if (flags & + (WH_NVM_FLAGS_NONMODIFIABLE | WH_NVM_FLAGS_NONDESTROYABLE)) { + return WH_ERROR_ACCESS; + } + break; + + case WH_KS_OP_ERASE: + if (flags & + (WH_NVM_FLAGS_NONMODIFIABLE | WH_NVM_FLAGS_NONDESTROYABLE)) { + return WH_ERROR_ACCESS; + } + break; + + case WH_KS_OP_EXPORT: + if (flags & WH_NVM_FLAGS_NONEXPORTABLE) { + return WH_ERROR_ACCESS; + } + break; + + case WH_KS_OP_COMMIT: + case WH_KS_OP_REVOKE: + /* Always allowed */ + break; } return WH_ERROR_OK; @@ -518,8 +484,8 @@ int wh_Server_KeystoreGetCacheSlotChecked(whServerContext* server, whNvmMetadata** outMeta) { int ret; - ret = _KeystoreCheckPolicy(server, WH_KS_OP_CACHE, keyId, NULL); - if (ret != WH_ERROR_OK) { + ret = _KeystoreCheckPolicy(server, WH_KS_OP_CACHE, keyId); + if (ret != WH_ERROR_OK && ret != WH_ERROR_NOTFOUND) { return ret; } return wh_Server_KeystoreGetCacheSlot(server, keyId, keySz, outBuf, @@ -651,15 +617,14 @@ int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId, cacheBufOut, cacheMetaOut); if (ret == WH_ERROR_OK) { /* Read the key from NVM into the cache slot */ - ret = wh_Nvm_Read(server->nvm, keyId, 0, tmpMeta->len, - *cacheBufOut); + ret = + wh_Nvm_Read(server->nvm, keyId, 0, tmpMeta->len, *cacheBufOut); if (ret == WH_ERROR_OK) { /* Copy the metadata to the cache slot if key read is * successful*/ memcpy((uint8_t*)*cacheMetaOut, (uint8_t*)tmpMeta, sizeof(whNvmMetadata)); - _MarkKeyCommitted(_GetCacheContext(server, keyId), keyId, - 1); + _MarkKeyCommitted(_GetCacheContext(server, keyId), keyId, 1); } } } @@ -749,7 +714,7 @@ int wh_Server_KeystoreReadKeyChecked(whServerContext* server, whKeyId keyId, { int ret; - ret = _KeystoreCheckPolicy(server, WH_KS_OP_EXPORT, keyId, NULL); + ret = _KeystoreCheckPolicy(server, WH_KS_OP_EXPORT, keyId); if (ret != WH_ERROR_OK) { return ret; } @@ -783,7 +748,7 @@ int wh_Server_KeystoreEvictKeyChecked(whServerContext* server, whNvmId keyId) { int ret; - ret = _KeystoreCheckPolicy(server, WH_KS_OP_EVICT, keyId, NULL); + ret = _KeystoreCheckPolicy(server, WH_KS_OP_EVICT, keyId); if (ret != WH_ERROR_OK) { return ret; } @@ -826,7 +791,7 @@ int wh_Server_KeystoreCommitKeyChecked(whServerContext* server, whNvmId keyId) { int ret; - ret = _KeystoreCheckPolicy(server, WH_KS_OP_COMMIT, keyId, NULL); + ret = _KeystoreCheckPolicy(server, WH_KS_OP_COMMIT, keyId); if (ret != WH_ERROR_OK) { return ret; } @@ -896,7 +861,7 @@ int wh_Server_KeystoreRevokeKey(whServerContext* server, whNvmId keyId) return WH_ERROR_BADARGS; } - ret = _KeystoreCheckPolicy(server, WH_KS_OP_REVOKE, keyId, NULL); + ret = _KeystoreCheckPolicy(server, WH_KS_OP_REVOKE, keyId); if (ret != WH_ERROR_OK) { return ret; } @@ -2210,7 +2175,7 @@ int wh_Server_KeystoreExportKeyDmaChecked(whServerContext* server, { int ret; - ret = _KeystoreCheckPolicy(server, WH_KS_OP_EXPORT, keyId, NULL); + ret = _KeystoreCheckPolicy(server, WH_KS_OP_EXPORT, keyId); if (ret != WH_ERROR_OK) { return ret; } diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index 15c00e56..f1b1b70a 100644 --- a/wolfhsm/wh_client.h +++ b/wolfhsm/wh_client.h @@ -773,7 +773,7 @@ int wh_Client_KeyErase(whClientContext* c, whNvmId keyId); * @param[in] keyId Key ID to be revoked. * @return int Returns 0 on success, or a negative error code on failure. */ -int wh_Client_KeyRevokeRequest(whClientContext* c, whNvmId keyId); +int wh_Client_KeyRevokeRequest(whClientContext* c, whKeyId keyId); /** * @brief Receives a key revoke response from the server. @@ -800,7 +800,7 @@ int wh_Client_KeyRevokeResponse(whClientContext* c); * @param[in] keyId Key ID to be revoked. * @return int Returns 0 on success, or a negative error code on failure. */ -int wh_Client_KeyRevoke(whClientContext* c, whNvmId keyId); +int wh_Client_KeyRevoke(whClientContext* c, whKeyId keyId); #ifdef WOLFHSM_CFG_DMA diff --git a/wolfhsm/wh_server_keystore.h b/wolfhsm/wh_server_keystore.h index 70e32452..d34588e7 100644 --- a/wolfhsm/wh_server_keystore.h +++ b/wolfhsm/wh_server_keystore.h @@ -189,7 +189,7 @@ int wh_Server_KeystoreEraseKeyChecked(whServerContext* server, whNvmId keyId); * * Placeholder implementation for key revocation. */ -int wh_Server_KeystoreRevokeKey(whServerContext* server, whNvmId keyId); +int wh_Server_KeystoreRevokeKey(whServerContext* server, whKeyId keyId); /** * @brief Handle key management requests from clients From 3a917042a31b9ff3ebdb1ab76f0e8f232003d0e1 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Tue, 16 Dec 2025 11:24:14 +0100 Subject: [PATCH 08/15] nvm: add revocation and nvm flag tests --- examples/posix/wh_posix_server/wolfhsm_cfg.h | 1 + test/config/wolfhsm_cfg.h | 3 + test/wh_test.c | 1 - test/wh_test_clientserver.c | 583 ++++++++++++++++++- test/wh_test_crypto.c | 245 ++++++++ 5 files changed, 828 insertions(+), 5 deletions(-) diff --git a/examples/posix/wh_posix_server/wolfhsm_cfg.h b/examples/posix/wh_posix_server/wolfhsm_cfg.h index af828259..2c0e4e3f 100644 --- a/examples/posix/wh_posix_server/wolfhsm_cfg.h +++ b/examples/posix/wh_posix_server/wolfhsm_cfg.h @@ -41,6 +41,7 @@ #define WOLFHSM_CFG_SERVER_KEYCACHE_COUNT 9 #define WOLFHSM_CFG_SERVER_KEYCACHE_SIZE 1024 #define WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE 4096 +#define WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT 5 #define WOLFHSM_CFG_SERVER_DMAADDR_COUNT 8 #define WOLFHSM_CFG_SERVER_CUSTOMCB_COUNT 6 diff --git a/test/config/wolfhsm_cfg.h b/test/config/wolfhsm_cfg.h index c3484199..940512a5 100644 --- a/test/config/wolfhsm_cfg.h +++ b/test/config/wolfhsm_cfg.h @@ -68,4 +68,7 @@ /* Test log-based NVM flash backend */ #define WOLFHSM_CFG_SERVER_NVM_FLASH_LOG +/* Allow persistent NVM artifacts in tests */ +#define WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS + #endif /* WOLFHSM_CFG_H_ */ diff --git a/test/wh_test.c b/test/wh_test.c index d5d7eb1b..eb80cf03 100644 --- a/test/wh_test.c +++ b/test/wh_test.c @@ -79,7 +79,6 @@ int whTest_Unit(void) #endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !WOLFHSM_CFG_NO_CRYPTO */ - /* Comm tests */ WH_TEST_ASSERT(0 == whTest_Comm()); WH_TEST_ASSERT(0 == whTest_ClientServer()); diff --git a/test/wh_test_clientserver.c b/test/wh_test_clientserver.c index 1c540120..de34618e 100644 --- a/test/wh_test_clientserver.c +++ b/test/wh_test_clientserver.c @@ -83,6 +83,18 @@ typedef struct { #define TEST_MEM_UNMAPPED_BYTE ((uint8_t)0xBB) #endif /* WOLFHSM_CFG_DMA */ +#define TEST_NVM_ID_NONMOD 0x0001 +#define TEST_NVM_ID_MODIFIABLE 0x0002 +#define TEST_NVM_ID_NONDESTROYABLE 0x0003 +#define TEST_NVM_ID_NONMOD_DMA 0x0004 +#define TEST_NVM_ID_NONDESTROYABLE_DMA 0x0005 +#define TEST_KEY_ID_NONMOD 0x0001 +#define TEST_KEY_ID_MODIFIABLE 0x0002 +#define TEST_KEY_ID_PROMOTE 0x0003 +#define TEST_KEY_ID_NONDESTROYABLE 0x0004 +#define TEST_KEY_ID_NONMOD_DMA 0x0005 +#define TEST_KEY_ID_PROMOTE_DMA 0x0006 +#define TEST_KEY_ID_NONDESTROYABLE_DMA 0x0007 #if defined(WOLFHSM_CFG_ENABLE_CLIENT) static int _testNonExportableNvmAccess(whClientContext* client); @@ -649,6 +661,557 @@ static int _testNonExportableNvmAccess(whClientContext* client) WH_TEST_PRINT("NON-EXPORTABLE NVM ACCESS TEST SUCCESS\n"); return 0; } + +#if defined(WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS) +static int _testNvmNonmodifiableNoOverwrite(whClientContext* client) +{ + int32_t server_rc; + uint8_t data1[] = {0x11, 0x22, 0x33, 0x44}; + uint8_t data2[] = {0xAA, 0xBB, 0xCC, 0xDD}; + uint8_t readData[sizeof(data1)] = {0}; + whNvmSize readLen = sizeof(readData); + uint8_t label[] = "TST"; + uint8_t labelLen = (uint8_t)strlen((const char*)label); + whNvmId list[1] = {TEST_NVM_ID_NONMOD}; + + WH_TEST_PRINT("Testing NVM NONMODIFIABLE: no overwrite...\n"); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmAddObject(client, TEST_NVM_ID_NONMOD, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONMODIFIABLE, labelLen, label, + sizeof(data1), data1, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONMOD, 0, + sizeof(readData), &server_rc, + &readLen, readData)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmAddObject(client, TEST_NVM_ID_NONMOD, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONMODIFIABLE, labelLen, label, + sizeof(data2), data2, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + memset(readData, 0, sizeof(readData)); + readLen = sizeof(readData); + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONMOD, 0, + sizeof(readData), &server_rc, + &readLen, readData)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); + + WH_TEST_PRINT(" NVM NONMODIFIABLE no overwrite: PASS\n"); + + + WH_TEST_PRINT("Testing NVM NONMODIFIABLE: no destroy...\n"); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONMOD, 0, + sizeof(readData), &server_rc, + &readLen, readData)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_PRINT(" NVM NONMODIFIABLE no destroy: PASS\n"); + return WH_ERROR_OK; +} + +static int _testNvmNondestroyableModifyNoDestroy(whClientContext* client) +{ + int32_t server_rc; + uint8_t data1[] = {0x55, 0x66, 0x77, 0x88}; + uint8_t data2[] = {0x99, 0xAA, 0xBB, 0xCC}; + uint8_t readData[sizeof(data2)] = {0}; + whNvmSize readLen = sizeof(readData); + whNvmId list[1] = {TEST_NVM_ID_NONDESTROYABLE}; + uint8_t label[] = "NDY"; + uint8_t labelLen = (uint8_t)strlen((const char*)label); + + WH_TEST_PRINT( + "Testing NVM NONDESTROYABLE: modify allowed, destroy denied...\n"); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObject( + client, TEST_NVM_ID_NONDESTROYABLE, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONDESTROYABLE, labelLen, label, sizeof(data1), data1, + &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* modify the object */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObject( + client, TEST_NVM_ID_NONDESTROYABLE, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONDESTROYABLE, labelLen, label, sizeof(data2), data2, + &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONDESTROYABLE, + 0, sizeof(readData), &server_rc, + &readLen, readData)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + memset(readData, 0, sizeof(readData)); + readLen = sizeof(readData); + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONDESTROYABLE, + 0, sizeof(readData), &server_rc, + &readLen, readData)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); + + WH_TEST_PRINT( + " NVM NONDESTROYABLE modify allowed, destroy denied: PASS\n"); + return WH_ERROR_OK; +} + +#ifdef WOLFHSM_CFG_DMA +static int _testNvmNonmodifiableNoOverwriteDma(whClientContext* client) +{ + int32_t server_rc; + uint8_t data1[] = {0x11, 0x22, 0x33, 0x44}; + uint8_t data2[] = {0xAA, 0xBB, 0xCC, 0xDD}; + uint8_t readData[sizeof(data1)] = {0}; + whNvmSize readLen = sizeof(readData); + whNvmId list[1] = {TEST_NVM_ID_NONMOD_DMA}; + whNvmMetadata meta = {0}; + + WH_TEST_PRINT("Testing NVM DMA NONMODIFIABLE: no overwrite...\n"); + + meta.id = TEST_NVM_ID_NONMOD_DMA; + meta.access = WH_NVM_ACCESS_ANY; + meta.flags = WH_NVM_FLAGS_NONMODIFIABLE; + meta.len = sizeof(data1); + memcpy(meta.label, "DMANONMOD", sizeof("DMANONMOD")); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( + client, &meta, sizeof(data1), data1, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadDma(client, TEST_NVM_ID_NONMOD_DMA, + 0, sizeof(readData), readData, + &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); + + meta.len = sizeof(data2); + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( + client, &meta, sizeof(data2), data2, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + memset(readData, 0, sizeof(readData)); + readLen = sizeof(readData); + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadDma( + client, TEST_NVM_ID_NONMOD_DMA, 0, readLen, readData, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); + + WH_TEST_PRINT(" NVM DMA NONMODIFIABLE no overwrite: PASS\n"); + + + WH_TEST_PRINT("Testing NVM DMA NONMODIFIABLE: no destroy...\n"); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadDma(client, TEST_NVM_ID_NONMOD_DMA, + 0, sizeof(readData), readData, + &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_PRINT(" NVM DMA NONMODIFIABLE no destroy: PASS\n"); + return WH_ERROR_OK; +} + +static int _testNvmNondestroyableModifyNoDestroyDma(whClientContext* client) +{ + int32_t server_rc; + uint8_t data1[] = {0x55, 0x66, 0x77, 0x88}; + uint8_t data2[] = {0x99, 0xAA, 0xBB, 0xCC}; + uint8_t readData[sizeof(data2)] = {0}; + whNvmSize readLen = sizeof(readData); + whNvmId list[1] = {TEST_NVM_ID_NONDESTROYABLE_DMA}; + whNvmMetadata meta = {0}; + + WH_TEST_PRINT( + "Testing NVM DMA NONDESTROYABLE: modify allowed, destroy denied...\n"); + + meta.id = TEST_NVM_ID_NONDESTROYABLE_DMA; + meta.access = WH_NVM_ACCESS_ANY; + meta.flags = WH_NVM_FLAGS_NONDESTROYABLE; + meta.len = sizeof(data1); + memcpy(meta.label, "DMANONDEST", sizeof("DMANONDEST")); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( + client, &meta, sizeof(data1), data1, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* modify the object via DMA */ + meta.len = sizeof(data2); + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( + client, &meta, sizeof(data2), data2, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmReadDma(client, TEST_NVM_ID_NONDESTROYABLE_DMA, 0, + sizeof(readData), readData, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + memset(readData, 0, sizeof(readData)); + readLen = sizeof(readData); + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmReadDma(client, TEST_NVM_ID_NONDESTROYABLE_DMA, 0, readLen, + readData, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); + + WH_TEST_PRINT( + " NVM DMA NONDESTROYABLE modify allowed, destroy denied: PASS\n"); + return WH_ERROR_OK; +} +#endif /* WOLFHSM_CFG_DMA */ + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) +static int _testKeyNonmodifiableNoRecache(whClientContext* client) +{ + int ret; + uint16_t keyId = TEST_KEY_ID_NONMOD; + uint8_t key1[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + uint8_t key2[] = {0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; + uint8_t label[] = "nonmod_key"; + + WH_TEST_PRINT("Testing Key NONMODIFIABLE: no re-cache...\n"); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key NONMODIFIABLE no re-cache: PASS\n"); + + WH_TEST_PRINT("Testing Key NONMODIFIABLE: no erase...\n"); + + ret = wh_Client_KeyErase(client, TEST_KEY_ID_NONMOD); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key NONMODIFIABLE no erase: PASS\n"); + + return WH_ERROR_OK; +} + +static int _testKeyNonmodifiableInNvm(whClientContext* client) +{ + uint16_t keyId = TEST_KEY_ID_PROMOTE; + uint8_t key1[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}; + uint8_t key2[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F}; + uint8_t label[] = "promote_key"; + int ret; + + WH_TEST_PRINT("Testing Key NONMODIFIABLE: commit then enforce...\n"); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONE, label, sizeof(label), + key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONE, label, sizeof(label), + key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key NONMODIFIABLE commit then enforce: PASS\n"); + return WH_ERROR_OK; +} + +static int _testKeyNondestroyableNoErase(whClientContext* client) +{ + int ret; + uint16_t keyId = TEST_KEY_ID_NONDESTROYABLE; + uint8_t key1[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE}; + uint8_t key2[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22, + 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00}; + uint8_t exported[sizeof(key2)] = {0}; + uint16_t exportedLen = sizeof(exported); + uint8_t label[] = "nondestroyable_key"; + + WH_TEST_PRINT( + "Testing Key NONDESTROYABLE: erase denied, modify allowed...\n"); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONDESTROYABLE, label, + sizeof(label), key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyErase(client, keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + exportedLen = sizeof(exported); + ret = wh_Client_KeyExport(client, keyId, NULL, 0, exported, &exportedLen); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key1)); + WH_TEST_ASSERT_RETURN(memcmp(exported, key1, sizeof(key1)) == 0); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONDESTROYABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + exportedLen = sizeof(exported); + ret = wh_Client_KeyExport(client, keyId, NULL, 0, exported, &exportedLen); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key2)); + WH_TEST_ASSERT_RETURN(memcmp(exported, key2, sizeof(key2)) == 0); + + WH_TEST_PRINT(" Key NONDESTROYABLE erase denied, modify allowed: PASS\n"); + return WH_ERROR_OK; +} + +#ifdef WOLFHSM_CFG_DMA +static int _testKeyNonmodifiableNoRecacheDma(whClientContext* client) +{ + int ret; + uint16_t keyId = TEST_KEY_ID_NONMOD_DMA; + uint8_t key1[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + uint8_t key2[] = {0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; + uint8_t label[] = "nonmod_key_dma"; + + WH_TEST_PRINT("Testing Key DMA NONMODIFIABLE: no re-cache...\n"); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key DMA NONMODIFIABLE no re-cache: PASS\n"); + + WH_TEST_PRINT("Testing Key DMA NONMODIFIABLE: no erase...\n"); + + ret = wh_Client_KeyErase(client, TEST_KEY_ID_NONMOD_DMA); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key DMA NONMODIFIABLE no erase: PASS\n"); + + return WH_ERROR_OK; +} + +static int _testKeyNonmodifiableInNvmDma(whClientContext* client) +{ + uint16_t keyId = TEST_KEY_ID_PROMOTE_DMA; + uint8_t key1[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}; + uint8_t key2[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F}; + uint8_t label[] = "promote_key_dma"; + int ret; + + WH_TEST_PRINT("Testing Key DMA NONMODIFIABLE: commit then enforce...\n"); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONE, label, sizeof(label), + key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONE, label, sizeof(label), + key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key DMA NONMODIFIABLE commit then enforce: PASS\n"); + return WH_ERROR_OK; +} + +static int _testKeyNondestroyableNoEraseDma(whClientContext* client) +{ + int ret; + uint16_t keyId = TEST_KEY_ID_NONDESTROYABLE_DMA; + uint8_t key1[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE}; + uint8_t key2[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22, + 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00}; + uint8_t exported[sizeof(key2)] = {0}; + uint16_t exportedLen = sizeof(exported); + uint8_t label[WH_NVM_LABEL_LEN] = "nondestroyable_dma"; + + WH_TEST_PRINT( + "Testing Key DMA NONDESTROYABLE: erase denied, modify allowed...\n"); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONDESTROYABLE, label, + sizeof(label), key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyErase(client, keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + exportedLen = sizeof(exported); + ret = wh_Client_KeyExportDma(client, keyId, exported, sizeof(exported), + label, sizeof(label), &exportedLen); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key1)); + WH_TEST_ASSERT_RETURN(memcmp(exported, key1, sizeof(key1)) == 0); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONDESTROYABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + exportedLen = sizeof(exported); + ret = wh_Client_KeyExportDma(client, keyId, exported, sizeof(exported), + label, sizeof(label), &exportedLen); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key2)); + WH_TEST_ASSERT_RETURN(memcmp(exported, key2, sizeof(key2)) == 0); + + WH_TEST_PRINT( + " Key DMA NONDESTROYABLE erase denied, modify allowed: PASS\n"); + return WH_ERROR_OK; +} +#endif /* WOLFHSM_CFG_DMA */ +#endif /* !WOLFHSM_CFG_NO_CRYPTO */ +#endif /* WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS */ + +static int whTest_NvmFlags(whClientContext* client) +{ + int ret = 0; + + WH_TEST_PRINT("=== NVM Flags Enforcement Tests ===\n\n"); + + WH_TEST_PRINT("=== NVM Object Tests (NONEXPORTABLE) ===\n"); + + ret = _testNonExportableNvmAccess(client); + if (ret != WH_ERROR_OK) + return ret; + + +#if defined(WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS) + WH_TEST_PRINT("--- NVM Object Tests (NONMODIFIABLE/NODESTROYABLE) ---\n"); + + ret = _testNvmNonmodifiableNoOverwrite(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testNvmNondestroyableModifyNoDestroy(client); + if (ret != WH_ERROR_OK) + return ret; + +#ifdef WOLFHSM_CFG_DMA + WH_TEST_PRINT( + "\n--- NVM Object DMA Tests (NONMODIFIABLE/NONDESTROYABLE) DMA ---\n"); + + ret = _testNvmNonmodifiableNoOverwriteDma(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testNvmNondestroyableModifyNoDestroyDma(client); + if (ret != WH_ERROR_OK) + return ret; +#endif /* WOLFHSM_CFG_DMA */ + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) + WH_TEST_PRINT("\n--- Key Object Tests (NONMODIFIABLE/NODESTROYABLE) ---\n"); + + ret = _testKeyNonmodifiableNoRecache(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testKeyNonmodifiableInNvm(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testKeyNondestroyableNoErase(client); + if (ret != WH_ERROR_OK) + return ret; + +#ifdef WOLFHSM_CFG_DMA + WH_TEST_PRINT( + "\n--- Key Object DMA Tests (NONMODIFIABLE/NONDESTROYABLE) DMA ---\n"); + + ret = _testKeyNonmodifiableNoRecacheDma(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testKeyNonmodifiableInNvmDma(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testKeyNondestroyableNoEraseDma(client); + if (ret != WH_ERROR_OK) + return ret; +#endif /* WOLFHSM_CFG_DMA */ +#endif /* !WOLFHSM_CFG_NO_CRYPTO */ +#else + WH_TEST_PRINT("\n--- Skipping NVM Object Tests " + "(NONMODIFIABLE/NONDESTROYABLE) due to persistent " + "NVM artifacts not being allowed ---\n"); +#endif /* WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS */ + + WH_TEST_PRINT("\n=== All NVM Flags Tests Complete ===\n"); + return ret; +} #endif /* WOLFHSM_CFG_ENABLE_CLIENT */ #if defined(WOLFHSM_CFG_ENABLE_CLIENT) && defined(WOLFHSM_CFG_ENABLE_SERVER) @@ -1383,6 +1946,18 @@ int whTest_ClientServerClientConfig(whClientConfig* clientCfg) WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); WH_TEST_ASSERT_RETURN(avail_objects == WOLFHSM_CFG_NVM_OBJECT_COUNT); + /* Reset NVM state after flag tests */ + WH_TEST_RETURN_ON_FAIL(ret = wh_Client_NvmCleanup(client, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL( + ret = wh_Client_NvmInit(client, &server_rc, &client_id, &server_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(ret = wh_Client_NvmGetAvailable( + client, &server_rc, &avail_size, &avail_objects, + &reclaim_size, &reclaim_objects)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(avail_objects == WOLFHSM_CFG_NVM_OBJECT_COUNT); + for (counter = 0; counter < 5; counter++) { whNvmId id = counter + 20; @@ -1641,10 +2216,6 @@ int whTest_ClientServerClientConfig(whClientConfig* clientCfg) #endif /* WOLFHSM_CFG_DMA */ - /* Test non-exportable flag enforcement for NVM and certificate operations - */ - WH_TEST_RETURN_ON_FAIL(_testNonExportableNvmAccess(client)); - /* Test client counter API */ WH_TEST_RETURN_ON_FAIL(_testClientCounter(client)); @@ -1676,6 +2247,10 @@ int whTest_ClientServerClientConfig(whClientConfig* clientCfg) #endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !WOLFHSM_CFG_NO_CRYPTO */ + /* Test NVM flag enforcement, thest last as it may create non-destroyable + * artifacts */ + WH_TEST_RETURN_ON_FAIL(ret = whTest_NvmFlags(client)); + WH_TEST_RETURN_ON_FAIL(wh_Client_CommClose(client)); WH_TEST_RETURN_ON_FAIL(wh_Client_Cleanup(client)); diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index 8534d4e9..7bcb11a4 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -641,6 +641,55 @@ static int whTest_CryptoEcc(whClientContext* ctx, int devId, WC_RNG* rng) } return ret; } + +static int whTest_CryptoEccCacheDuplicate(whClientContext* client) +{ + int ret = WH_ERROR_OK; + whKeyId keyId = WH_KEYID_ERASED; + uint8_t key1[ECC_BUFSIZE]; + uint8_t key2[ECC_BUFSIZE]; + uint16_t key1Len = sizeof(key1); + uint16_t key2Len = sizeof(key2); + + WH_TEST_PRINT(" Testing ECC cache duplicate returns latest key...\n"); + + /* Generate first cached key and export it */ + ret = wh_Client_EccMakeCacheKey(client, 32, ECC_SECP256R1, &keyId, + WH_NVM_FLAGS_NONE, 0, NULL); + if (ret == WH_ERROR_OK) { + ret = wh_Client_KeyExport(client, keyId, NULL, 0, key1, &key1Len); + } + + /* Generate a second key using the same keyId to create a duplicate slot */ + if (ret == WH_ERROR_OK) { + ret = wh_Client_EccMakeCacheKey(client, 32, ECC_SECP256R1, &keyId, + WH_NVM_FLAGS_NONE, 0, NULL); + } + + /* Export again; result should match the most recent key, not the first */ + if (ret == WH_ERROR_OK) { + key2Len = sizeof(key2); + ret = wh_Client_KeyExport(client, keyId, NULL, 0, key2, &key2Len); + } + + if (ret == WH_ERROR_OK) { + if ((key1Len == key2Len) && (memcmp(key1, key2, key1Len) == 0)) { + WH_ERROR_PRINT(" FAIL: Export returned original ECC key after " + "duplicate insert\n"); + ret = WH_ERROR_ABORTED; + } + else { + WH_TEST_PRINT( + " PASS: Export returned most recent cached ECC key\n"); + } + } + + if (!WH_KEYID_ISERASED(keyId)) { + wh_Client_KeyEvict(client, keyId); + } + + return ret; +} #endif /* HAVE_ECC */ #ifdef HAVE_ED25519 @@ -4808,6 +4857,190 @@ int whTest_CryptoKeyUsagePolicies(whClientContext* client, WC_RNG* rng) return 0; } +#if !defined(NO_AES) && defined(HAVE_AES_CBC) && \ + defined(WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS) +int _testRevocationTryAESEncrypt(whKeyId keyId, WC_RNG* rng, int* encryptRes) +{ + int ret; + Aes aes[1]; + uint8_t iv[AES_BLOCK_SIZE]; + uint8_t plaintext[16]; + uint8_t ciphertext[16] = {0}; + /* generate random iv and plaintext */ + ret = wc_RNG_GenerateBlock(rng, iv, sizeof(iv)); + if (ret == 0) { + ret = wc_RNG_GenerateBlock(rng, plaintext, sizeof(plaintext)); + } + if (ret != 0) { + WH_ERROR_PRINT("Failed to generate AES revocation test inputs: %d\n", + ret); + return ret; + } + /* try to encrypt with the given keyId */ + ret = wc_AesInit(aes, NULL, WH_DEV_ID); + if (ret != 0) { + WH_ERROR_PRINT("Failed to init AES for revoked key test: %d\n", ret); + return ret; + } + ret = wh_Client_AesSetKeyId(aes, keyId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to set AES keyId for revoked key test: %d\n", + ret); + wc_AesFree(aes); + return ret; + } + ret = wc_AesSetIV(aes, iv); + if (ret != 0) { + WH_ERROR_PRINT("Failed to set AES IV for revoked key test: %d\n", ret); + wc_AesFree(aes); + return ret; + } + ret = + wc_AesCbcEncrypt(aes, ciphertext, plaintext, (word32)sizeof(plaintext)); + wc_AesFree(aes); + *encryptRes = ret; + return WH_ERROR_OK; +} + +int whTest_CryptoKeyRevocationAesCbc(whClientContext* client, WC_RNG* rng) +{ + int ret = 0; + + WH_TEST_PRINT("Testing Key Revocation...\n"); + + { + /* AES-CBC: revoked keys should be unusable and non-erasable */ + uint8_t key[32] = {0}; + const uint8_t label[] = "revocation-aes-cbc"; + whKeyId keyId = WH_KEYID_ERASED; + const int expectedEraseErr = WH_ERROR_ACCESS; + int encryptRes = 0; + + ret = wc_RNG_GenerateBlock(rng, key, sizeof(key)); + if (ret != 0) { + WH_ERROR_PRINT("Failed to generate AES revocation inputs: %d\n", + ret); + return ret; + } + + WH_TEST_PRINT(" AES-CBC key revoke flow...\n"); + + ret = + wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)label, + sizeof(label), key, sizeof(key), &keyId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to cache AES key: %d\n", ret); + return ret; + } + + /* encrypt should work */ + ret = _testRevocationTryAESEncrypt(keyId, rng, &encryptRes); + if (ret != 0) { + WH_ERROR_PRINT("Failed to encrypt with unrevoked AES key: %d\n", + ret); + (void)wh_Client_KeyEvict(client, keyId); + return ret; + } + + if (encryptRes != 0) { + WH_ERROR_PRINT("Encrypt with unrevoked AES key failed: %d\n", + encryptRes); + return encryptRes; + } + + /* revoke a key in the cache */ + ret = wh_Client_KeyRevoke(client, keyId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to revoke AES key: %d\n", ret); + return ret; + } + + /* now encrypt should fail */ + ret = _testRevocationTryAESEncrypt(keyId, rng, &encryptRes); + if (ret != 0 || encryptRes != WH_ERROR_USAGE) { + WH_ERROR_PRINT( + "Encrypt with revoked AES key should fail (%d), got %d\n", + WH_ERROR_USAGE, encryptRes); + return WH_ERROR_ABORTED; + } + + /* commit the key */ + ret = wh_Client_KeyCommit(client, keyId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to commit revoked AES key: %d\n", ret); + return ret; + } + + /* keep failing */ + ret = _testRevocationTryAESEncrypt(keyId, rng, &encryptRes); + if (ret != 0 || encryptRes != WH_ERROR_USAGE) { + WH_ERROR_PRINT( + "Encrypt with revoked AES key should fail (%d), got %d\n", + WH_ERROR_USAGE, encryptRes); + return WH_ERROR_ABORTED; + } + + ret = wh_Client_KeyErase(client, keyId); + if (ret != expectedEraseErr) { + WH_ERROR_PRINT("Revoked key erase should fail (%d), got %d\n", + expectedEraseErr, ret); + return WH_ERROR_ABORTED; + } + + /* try a slightly different flow */ + keyId = WH_KEYID_ERASED; + ret = + wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)label, + sizeof(label), key, sizeof(key), &keyId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to cache AES key (2nd time): %d\n", ret); + return ret; + } + /* commit the key */ + ret = wh_Client_KeyCommit(client, keyId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to commit AES key (2nd time): %d\n", ret); + return ret; + } + /* try encrypt first */ + ret = _testRevocationTryAESEncrypt(keyId, rng, &encryptRes); + if (ret != 0 || encryptRes != 0) { + WH_ERROR_PRINT("Failed to encrypt with unrevoked AES key (2nd " + "time): %d\n", + ret); + (void)wh_Client_KeyEvict(client, keyId); + return ret != 0 ? ret : encryptRes; + } + /* evict the key */ + ret = wh_Client_KeyEvict(client, keyId); + if (ret != 0 && ret != WH_ERROR_NOTFOUND) { + WH_ERROR_PRINT("Failed to evict AES key (2nd time): %d\n", ret); + return ret; + } + /* revoke with key in the NVM */ + ret = wh_Client_KeyRevoke(client, keyId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to revoke AES key (2nd time): %d\n", ret); + return ret; + } + /* this should still fail */ + ret = _testRevocationTryAESEncrypt(keyId, rng, &encryptRes); + if (ret != 0 || encryptRes != WH_ERROR_USAGE) { + WH_ERROR_PRINT( + "Encrypt with revoked AES key should fail (%d), got %d\n", + WH_ERROR_USAGE, encryptRes); + (void)wh_Client_KeyEvict(client, keyId); + return WH_ERROR_ABORTED; + } + + + WH_TEST_PRINT(" AES-CBC revocation enforcement: PASS\n"); + } + + return ret; +} +#endif /* !NO_AES && HAVE_AES_CBC && \ + WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS */ int whTest_CryptoClientConfig(whClientConfig* config) { @@ -4911,6 +5144,9 @@ int whTest_CryptoClientConfig(whClientConfig* config) if (ret == 0) { ret = whTest_CryptoEcc(client, WH_DEV_ID, rng); } + if (ret == 0) { + ret = whTest_CryptoEccCacheDuplicate(client); + } #endif /* HAVE_ECC */ #ifdef HAVE_ED25519 @@ -5048,6 +5284,15 @@ int whTest_CryptoClientConfig(whClientConfig* config) } #endif /* WOLFHSM_CFG_DEBUG_VERBOSE */ +#if !defined(NO_AES) && defined(HAVE_AES_CBC) && \ + defined(WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS) + /* keep last, leaves artifact in the NVM layer */ + if (ret == 0) { + /* Test key revocation */ + ret = whTest_CryptoKeyRevocationAesCbc(client, rng); + } +#endif + /* Clean up used resources */ if (rngInited) { (void)wc_FreeRng(rng); From b7b719394ea4f0ad190a902731d8c6198f461bd2 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 19 Dec 2025 13:15:47 +0100 Subject: [PATCH 09/15] ci: temporarly fix wolfssl tag as master broke testing --- .github/workflows/build-and-test.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index aa6d4f32..2d799cd5 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -22,12 +22,25 @@ jobs: - name: List compiler version run: gcc --version - # pull and build wolfssl + # get last release version fo wolfssl + - name: Get latest wolfssl release tag + uses: actions/github-script@v7 + id: latest-release + with: + script: | + const release = await github.rest.repos.getLatestRelease({ + owner: 'wolfssl', + repo: 'wolfssl' + }); + return release.data.tag_name; + result-encoding: string + - name: Checkout wolfssl uses: actions/checkout@v4 with: repository: wolfssl/wolfssl - path: wolfssl + ref: ${{ steps.latest-release.outputs.result }} + path: wolfssl # pull and build wolfssl # Build and test standard build - name: Build and test From edb28d0ea3050eee4c49b87dee93311239a41264 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 19 Dec 2025 15:18:44 +0100 Subject: [PATCH 10/15] docs: updates documentation with nvm enforcing and key revocation --- docs/src/appendix01.md | 177 +++++++++++++++++++++++++++++++++++++++++ docs/src/chapter04.md | 29 +++++++ docs/src/chapter05.md | 71 ++++++++++++++++- 3 files changed, 276 insertions(+), 1 deletion(-) diff --git a/docs/src/appendix01.md b/docs/src/appendix01.md index 5775054a..7830037b 100644 --- a/docs/src/appendix01.md +++ b/docs/src/appendix01.md @@ -1 +1,178 @@ # wolfHSM API reference + +## Key Revocation + +### wh_Client_KeyRevokeRequest + +Send a key revocation request to the server (non-blocking). + +This function prepares and sends a revoke request for the specified key ID. It +returns after the request is sent; use `wh_Client_KeyRevokeResponse()` to +retrieve the result. + +Parameters: + +- `c`: Client context. +- `keyId`: Key ID to revoke. + +Return values: + +- `WH_ERROR_OK` on successful request send. +- A negative error code on failure. + +Error codes: + +- `WH_ERROR_BADARGS` if `c` is NULL or `keyId` is invalid. +- Propagates comm layer errors on send failure. + +### wh_Client_KeyRevokeResponse + +Receive a key revocation response. + +This function polls for the revoke response and returns `WH_ERROR_NOTREADY` +until the server reply is available. + +Parameters: + +- `c`: Client context. + +Return values: + +- `WH_ERROR_OK` on success. +- `WH_ERROR_NOTREADY` if the response has not arrived. +- A negative error code on failure. + +Error codes: + +- `WH_ERROR_BADARGS` if `c` is NULL. +- Server error codes such as `WH_ERROR_NOTFOUND`. + +### wh_Client_KeyRevoke + +Revoke a key using a blocking request/response. + +This helper sends a revoke request and waits for the response. + +Parameters: + +- `c`: Client context. +- `keyId`: Key ID to revoke. + +Return values: + +- `WH_ERROR_OK` on success. +- A negative error code on failure. + +Error codes: + +- Any error code returned by `wh_Client_KeyRevokeRequest()` or + `wh_Client_KeyRevokeResponse()`. + +### wh_Server_KeystoreRevokeKey + +Revoke a key by updating its metadata. + +This server-side function marks a key as non-modifiable and clears all usage +flags. If the key exists in NVM, the metadata update is committed so the revoke +state persists. + +Parameters: + +- `server`: Server context. +- `keyId`: Key ID to revoke. + +Return values: + +- `WH_ERROR_OK` on success. +- A negative error code on failure. + +Error codes: + +- `WH_ERROR_BADARGS` if parameters are invalid. +- `WH_ERROR_NOTFOUND` if the key is missing. +- Propagates NVM/storage errors (for example `WH_ERROR_NOSPACE`). + +## NVM Access and Flag Controls + +### whNvmFlags + +Policy flags for NVM objects and keys. + +Flags include `WH_NVM_FLAGS_NONMODIFIABLE`, `WH_NVM_FLAGS_NONDESTROYABLE`, +`WH_NVM_FLAGS_NONEXPORTABLE`, and the usage policy flags `WH_NVM_FLAGS_USAGE_*`. +If no usage flags are set, the key is not permitted for cryptographic use. + +### wh_Nvm_AddObjectChecked + +Add an NVM object with policy enforcement. + +This function applies NVM policy checks (for example non-modifiable objects) +before writing the object. + +Parameters: + +- `context`: NVM context. +- `meta`: Metadata describing the object. +- `data_len`: Length of object data. +- `data`: Object data buffer. + +Return values: + +- `WH_ERROR_OK` on success. +- A negative error code on failure. + +Error codes: + +- `WH_ERROR_BADARGS` if parameters are invalid. +- `WH_ERROR_ACCESS` if the object is non-modifiable. +- Propagates backend errors (for example `WH_ERROR_NOSPACE`). + +### wh_Nvm_ReadChecked + +Read an NVM object with policy enforcement. + +This function applies NVM policy checks (for example non-exportable objects) +before reading the object data. + +Parameters: + +- `context`: NVM context. +- `id`: Object ID to read. +- `offset`: Byte offset into the object. +- `data_len`: Length of data to read. +- `data`: Output buffer. + +Return values: + +- `WH_ERROR_OK` on success. +- A negative error code on failure. + +Error codes: + +- `WH_ERROR_BADARGS` if parameters are invalid. +- `WH_ERROR_ACCESS` if the object is non-exportable. +- `WH_ERROR_NOTFOUND` if the object does not exist. + +### wh_Nvm_DestroyObjectsChecked + +Destroy NVM objects with policy enforcement. + +This function applies NVM policy checks (for example non-destroyable objects) +before erasing the objects. + +Parameters: + +- `context`: NVM context. +- `list_count`: Number of IDs in the list. +- `id_list`: Array of object IDs to destroy. + +Return values: + +- `WH_ERROR_OK` on success. +- A negative error code on failure. + +Error codes: + +- `WH_ERROR_BADARGS` if parameters are invalid. +- `WH_ERROR_ACCESS` if any object is non-destroyable or non-modifiable. +- `WH_ERROR_NOTFOUND` if a listed object is missing. diff --git a/docs/src/chapter04.md b/docs/src/chapter04.md index 3792aca8..cfecdfc9 100644 --- a/docs/src/chapter04.md +++ b/docs/src/chapter04.md @@ -11,9 +11,11 @@ wolfHSM is a modular and extensible library designed to provide a secure and eff - [Comms Layer](#comms-layer) - [Non Volatile Memory](#non-volatile-memory) - [NVM Metadata](#nvm-metadata) + - [NVM Access and Flags](#nvm-access-and-flags) - [NVM Architecture](#nvm-architecture) - [NVM Back-Ends](#nvm-back-ends) - [Key Management](#key-management) + - [Key Revocation](#key-revocation) - [Cryptographic Operations](#cryptographic-operations) - [Hardware Cryptography Support](#hardware-cryptography-support) @@ -222,6 +224,18 @@ typedef struct { Length (whNvmSize len): The length of the data associated with the object, in bytes. - Label (`uint8_t label[]`): A human-readable label or name for the object. +### NVM Access and Flags + +NVM access controls are stored in the metadata for all objects and are returned by `wh_Nvm_GetMetadata()` and related client APIs. + +NVM flags provide object and key policy hints that are enforced by the NVM library and keystore. Relevant flags include: + +- `WH_NVM_FLAGS_NONMODIFIABLE`: Object cannot be modified and/or destroyed. +- `WH_NVM_FLAGS_NONDESTROYABLE`: Object cannot be destroyed. +- `WH_NVM_FLAGS_NONEXPORTABLE`: Object data cannot be read/exported. +- `WH_NVM_FLAGS_USAGE_*`: Key usage policy flags for encrypt/decrypt/sign/verify/wrap/derive. +- `WH_NVM_FLAGS_USAGE_ANY`: Allow all usage flags. + ### NVM Architecture The wolfHSM server follows the generic component architecture approach to handle Non-Volatile Memory (NVM) operations. The configuration is divided into generic and specific parts, allowing for flexibility and customization. @@ -249,6 +263,21 @@ Currently, wolfHSM only supports one NVM back-end provider: the NVM flash module The wolfHSM library provides comprehensive key management capabilities, including storing, loading, and exporting keys from non-volatile memory, caching of frequently used keys in RAM for fast access, and interacting with hardware-exclusive device keys. Keys are stored in non-volatile memory along side other NVM objects with corresponding access protections. wolfHSM will automatically load keys into the necessary cryptographic hardware when the key is selected for use with a specific consumer. More information on the key management API can be found in the [client library](./chapter05.md) and [API documentation](./appendix01.md) sections. +### Key Revocation + +Key revocation provides a lightweight mechanism to invalidate a key without destroying its storage. When a key is revoked on the server, its metadata is updated by setting `WH_NVM_FLAGS_NONMODIFIABLE` and clearing all `WH_NVM_FLAGS_USAGE_*` bits. The key remains present in cache/NVM, but is no longer eligible for cryptographic operations that enforce usage flags. + +Runtime behavior for revoked keys: + +- Cryptographic operations that enforce usage flags return `WH_ERROR_USAGE` when the required usage bit is no longer set. +- The `WH_NVM_FLAGS_NONMODIFIABLE` setting prevents further key metadata changes and blocks NVM modification or destruction checks. +- Revocation does not automatically set `WH_NVM_FLAGS_NONEXPORTABLE`; export behavior remains controlled by those flags. + +Persistence behavior: + +- Revocation is committed to NVM for keys that already exist in NVM, so the revoked state persists across resets or power cycles. +- If a key exists only in cache and has not been committed, the revoked state is limited to the cache lifetime. + ## Cryptographic Operations One of the defining features of wolfHSM is that it enables the client application to use the wolfCrypt API directly, but with the underlying cryptographic operations actually being executed on the HSM core. This is an incredibly powerful feature for a number of reasons: diff --git a/docs/src/chapter05.md b/docs/src/chapter05.md index dab16951..4179d1be 100644 --- a/docs/src/chapter05.md +++ b/docs/src/chapter05.md @@ -9,7 +9,9 @@ The client library API is the primary mechanism through which users will interac - [The Client Context](#the-client-context) - [Initializing the client context](#initializing-the-client-context) - [NVM Operations](#nvm-operations) +- [NVM Access and Flags](#nvm-access-and-flags) - [Key Management](#key-management) +- [Key Revocation](#key-revocation) - [Cryptography](#cryptography) - [AUTOSAR SHE API](#autosar-she-api) @@ -250,6 +252,10 @@ int wh_Client_NvmList(whClientContext* c, For a full description of all the NVM API functions, please refer to the [API documentation](./appendix01.md). +## NVM Flags + +NVM objects include flags in their metadata. Flags (such as `WH_NVM_FLAGS_NONMODIFIABLE`, `WH_NVM_FLAGS_NONDESTROYABLE`, and `WH_NVM_FLAGS_NONEXPORTABLE`) are enforced by the server NVM policy checks. Key usage flags (`WH_NVM_FLAGS_USAGE_*`) are enforced by the keystore during cryptographic operations. + ## Key Management Keys meant for use with wolfCrypt can be loaded into the HSM's keystore and optionally saved to NVM with the following APIs: @@ -281,6 +287,70 @@ wh_Client_KeyErase(clientCtx, keyId); `wh_Client_KeyExport` will read the key contents out of the HSM back to the client. `wh_Client_KeyErase` will remove the indicated key from cache and erase it from NVM. +## Key Revocation + +Key revocation updates key metadata to prevent further cryptographic use without destroying storage. Revocation clears all `WH_NVM_FLAGS_USAGE_*` bits and sets `WH_NVM_FLAGS_NONMODIFIABLE`. The revoked state is persisted when the key is already committed to NVM. + +Creating a key with NVM usage flags: + +```c +int rc; +uint16_t keyId = WH_KEYID_ERASED; +byte label[WH_NVM_LABEL_LEN] = "app-signing"; +byte key[AES_128_KEY_SIZE] = { /* key bytes */ }; +whNvmFlags flags = WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_NONEXPORTABLE; + +rc = wh_Client_KeyCache(&clientCtx, flags, label, sizeof(label), + key, sizeof(key), &keyId); +if (rc == WH_ERROR_OK) { + rc = wh_Client_KeyCommit(&clientCtx, keyId); +} +``` + +Revoking a key: + +```c +int rc; + +rc = wh_Client_KeyRevoke(&clientCtx, keyId); +if (rc != WH_ERROR_OK) { + /* handle error */ +} +``` + +Attempting to use a revoked key and handling failure: + +```c +int rc; +Aes aes; +byte iv[AES_BLOCK_SIZE] = {0}; +byte plain[AES_BLOCK_SIZE] = {0}; +byte cipher[AES_BLOCK_SIZE] = {0}; + +wc_AesInit(&aes, NULL, WOLFHSM_DEV_ID); +wh_Client_AesSetKeyId(&aes, keyId); +wc_AesSetIV(&aes, iv); + +rc = wh_Client_AesCbc(&clientCtx, &aes, AES_ENCRYPTION, + plain, sizeof(plain), cipher); +if (rc == WH_ERROR_USAGE) { + /* key revoked or usage not permitted */ +} +wc_AesFree(&aes); +``` + +Security notes: + +- Set explicit usage flags for each key; avoid `WH_NVM_FLAGS_USAGE_ANY` unless required. +- Use `WH_NVM_FLAGS_NONEXPORTABLE` for private keys and long-lived secrets. +- Revoke keys on compromise and rotate to new key IDs rather than reusing revoked IDs. + +Compatibility notes: + +- Keys stored with `WH_NVM_FLAGS_NONE` (no usage flags) are treated as not permitted for cryptographic use and will return `WH_ERROR_USAGE`. +- Keys committed to NVM retain revocation state across resets; cached-only keys do not persist after reset or eviction. +- Keys cached via `wh_Client_KeyCache` are stored with `WH_NVM_ACCESS_ANY` on the server side. + ## Cryptography When using wolfCrypt in the client application, compatible crypto operations can be executed on the wolfHSM server by passing `WOLFHSM_DEV_ID` as the `devId` argument. The wolfHSM client must be initialized before using any wolfHSM remote crypto. @@ -367,4 +437,3 @@ For CMAC operations that need to use cached keys, seperate wolfHSM specific func ## AUTOSAR SHE API - From 6cd6fd9907b8af5afd6172f2f5ee8cfab180c1c5 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 19 Dec 2025 17:21:52 +0100 Subject: [PATCH 11/15] fixup! docs: updates documentation with nvm enforcing and key revocation --- docs/src/appendix01.md | 85 ------------------------------------------ docs/src/chapter05.md | 75 +++++++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 92 deletions(-) diff --git a/docs/src/appendix01.md b/docs/src/appendix01.md index 7830037b..72ef50d5 100644 --- a/docs/src/appendix01.md +++ b/docs/src/appendix01.md @@ -91,88 +91,3 @@ Error codes: - `WH_ERROR_BADARGS` if parameters are invalid. - `WH_ERROR_NOTFOUND` if the key is missing. - Propagates NVM/storage errors (for example `WH_ERROR_NOSPACE`). - -## NVM Access and Flag Controls - -### whNvmFlags - -Policy flags for NVM objects and keys. - -Flags include `WH_NVM_FLAGS_NONMODIFIABLE`, `WH_NVM_FLAGS_NONDESTROYABLE`, -`WH_NVM_FLAGS_NONEXPORTABLE`, and the usage policy flags `WH_NVM_FLAGS_USAGE_*`. -If no usage flags are set, the key is not permitted for cryptographic use. - -### wh_Nvm_AddObjectChecked - -Add an NVM object with policy enforcement. - -This function applies NVM policy checks (for example non-modifiable objects) -before writing the object. - -Parameters: - -- `context`: NVM context. -- `meta`: Metadata describing the object. -- `data_len`: Length of object data. -- `data`: Object data buffer. - -Return values: - -- `WH_ERROR_OK` on success. -- A negative error code on failure. - -Error codes: - -- `WH_ERROR_BADARGS` if parameters are invalid. -- `WH_ERROR_ACCESS` if the object is non-modifiable. -- Propagates backend errors (for example `WH_ERROR_NOSPACE`). - -### wh_Nvm_ReadChecked - -Read an NVM object with policy enforcement. - -This function applies NVM policy checks (for example non-exportable objects) -before reading the object data. - -Parameters: - -- `context`: NVM context. -- `id`: Object ID to read. -- `offset`: Byte offset into the object. -- `data_len`: Length of data to read. -- `data`: Output buffer. - -Return values: - -- `WH_ERROR_OK` on success. -- A negative error code on failure. - -Error codes: - -- `WH_ERROR_BADARGS` if parameters are invalid. -- `WH_ERROR_ACCESS` if the object is non-exportable. -- `WH_ERROR_NOTFOUND` if the object does not exist. - -### wh_Nvm_DestroyObjectsChecked - -Destroy NVM objects with policy enforcement. - -This function applies NVM policy checks (for example non-destroyable objects) -before erasing the objects. - -Parameters: - -- `context`: NVM context. -- `list_count`: Number of IDs in the list. -- `id_list`: Array of object IDs to destroy. - -Return values: - -- `WH_ERROR_OK` on success. -- A negative error code on failure. - -Error codes: - -- `WH_ERROR_BADARGS` if parameters are invalid. -- `WH_ERROR_ACCESS` if any object is non-destroyable or non-modifiable. -- `WH_ERROR_NOTFOUND` if a listed object is missing. diff --git a/docs/src/chapter05.md b/docs/src/chapter05.md index 4179d1be..2fbb449f 100644 --- a/docs/src/chapter05.md +++ b/docs/src/chapter05.md @@ -339,17 +339,78 @@ if (rc == WH_ERROR_USAGE) { wc_AesFree(&aes); ``` -Security notes: - -- Set explicit usage flags for each key; avoid `WH_NVM_FLAGS_USAGE_ANY` unless required. -- Use `WH_NVM_FLAGS_NONEXPORTABLE` for private keys and long-lived secrets. -- Revoke keys on compromise and rotate to new key IDs rather than reusing revoked IDs. - Compatibility notes: - Keys stored with `WH_NVM_FLAGS_NONE` (no usage flags) are treated as not permitted for cryptographic use and will return `WH_ERROR_USAGE`. - Keys committed to NVM retain revocation state across resets; cached-only keys do not persist after reset or eviction. -- Keys cached via `wh_Client_KeyCache` are stored with `WH_NVM_ACCESS_ANY` on the server side. + +### Key revocation client API + +#### wh_Client_KeyRevokeRequest + +Send a key revocation request to the server (non-blocking). + +This function prepares and sends a revoke request for the specified key ID. It +returns after the request is sent; use `wh_Client_KeyRevokeResponse()` to +retrieve the result. + +Parameters: + +- `c`: Client context. +- `keyId`: Key ID to revoke. + +Return values: + +- `WH_ERROR_OK` on successful request send. +- A negative error code on failure. + +Error codes: + +- `WH_ERROR_BADARGS` if `c` is NULL or `keyId` is invalid. +- Propagates comm layer errors on send failure. + +#### wh_Client_KeyRevokeResponse + +Receive a key revocation response. + +This function polls for the revoke response and returns `WH_ERROR_NOTREADY` +until the server reply is available. + +Parameters: + +- `c`: Client context. + +Return values: + +- `WH_ERROR_OK` on success. +- `WH_ERROR_NOTREADY` if the response has not arrived. +- A negative error code on failure. + +Error codes: + +- `WH_ERROR_BADARGS` if `c` is NULL. +- Server error codes such as `WH_ERROR_NOTFOUND`. + +#### wh_Client_KeyRevoke + +Revoke a key using a blocking request/response. + +This helper sends a revoke request and waits for the response. + +Parameters: + +- `c`: Client context. +- `keyId`: Key ID to revoke. + +Return values: + +- `WH_ERROR_OK` on success. +- A negative error code on failure. + +Error codes: + +- Any error code returned by `wh_Client_KeyRevokeRequest()` or + `wh_Client_KeyRevokeResponse()`. ## Cryptography From 83a8a0a055b5c9ec23d064e6512fcd3a1d9b3d5e Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 19 Dec 2025 18:08:27 +0100 Subject: [PATCH 12/15] keystore: remove unused WH_KS_OP_ERASE operation --- src/wh_server_keystore.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index 1838e617..625246c3 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -86,7 +86,6 @@ static whKeyCacheContext* _GetCacheContext(whServerContext* server, typedef enum { WH_KS_OP_CACHE = 0, WH_KS_OP_COMMIT, - WH_KS_OP_ERASE, WH_KS_OP_EVICT, WH_KS_OP_EXPORT, WH_KS_OP_REVOKE, @@ -173,13 +172,6 @@ static int _KeystoreCheckPolicy(whServerContext* server, whKsOp op, } break; - case WH_KS_OP_ERASE: - if (flags & - (WH_NVM_FLAGS_NONMODIFIABLE | WH_NVM_FLAGS_NONDESTROYABLE)) { - return WH_ERROR_ACCESS; - } - break; - case WH_KS_OP_EXPORT: if (flags & WH_NVM_FLAGS_NONEXPORTABLE) { return WH_ERROR_ACCESS; @@ -190,6 +182,9 @@ static int _KeystoreCheckPolicy(whServerContext* server, whKsOp op, case WH_KS_OP_REVOKE: /* Always allowed */ break; + default: + /* unknown operation */ + return WH_ERROR_BADARGS; } return WH_ERROR_OK; From d90ba22f7d0becfa3748232b02e3b3ba56054642 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 19 Dec 2025 18:24:29 +0100 Subject: [PATCH 13/15] test: move nvmflags test in its own file --- test/wh_test_clientserver.c | 727 +---------------------------------- test/wh_test_nvmflags.c | 747 ++++++++++++++++++++++++++++++++++++ test/wh_test_nvmflags.h | 30 ++ 3 files changed, 778 insertions(+), 726 deletions(-) create mode 100644 test/wh_test_nvmflags.c create mode 100644 test/wh_test_nvmflags.h diff --git a/test/wh_test_clientserver.c b/test/wh_test_clientserver.c index de34618e..989dd537 100644 --- a/test/wh_test_clientserver.c +++ b/test/wh_test_clientserver.c @@ -42,6 +42,7 @@ #ifdef WOLFHSM_CFG_ENABLE_CLIENT #include "wolfhsm/wh_client.h" +#include "wh_test_nvmflags.h" #endif #if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO) @@ -83,23 +84,6 @@ typedef struct { #define TEST_MEM_UNMAPPED_BYTE ((uint8_t)0xBB) #endif /* WOLFHSM_CFG_DMA */ -#define TEST_NVM_ID_NONMOD 0x0001 -#define TEST_NVM_ID_MODIFIABLE 0x0002 -#define TEST_NVM_ID_NONDESTROYABLE 0x0003 -#define TEST_NVM_ID_NONMOD_DMA 0x0004 -#define TEST_NVM_ID_NONDESTROYABLE_DMA 0x0005 -#define TEST_KEY_ID_NONMOD 0x0001 -#define TEST_KEY_ID_MODIFIABLE 0x0002 -#define TEST_KEY_ID_PROMOTE 0x0003 -#define TEST_KEY_ID_NONDESTROYABLE 0x0004 -#define TEST_KEY_ID_NONMOD_DMA 0x0005 -#define TEST_KEY_ID_PROMOTE_DMA 0x0006 -#define TEST_KEY_ID_NONDESTROYABLE_DMA 0x0007 - -#if defined(WOLFHSM_CFG_ENABLE_CLIENT) -static int _testNonExportableNvmAccess(whClientContext* client); -#endif - #ifdef WOLFHSM_CFG_ENABLE_SERVER /* Pointer to a local server context so a connect callback can access it. Should * be set before calling wh_ClientInit() */ @@ -503,715 +487,6 @@ static int _testClientCounter(whClientContext* client) return WH_ERROR_OK; } - -static int _testNonExportableNvmAccess(whClientContext* client) -{ - int ret = 0; - whNvmId nvmId = 2; /* Arbitrary NVM ID */ - uint8_t nvmData[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; - uint8_t exportedNvmData[sizeof(nvmData)] = {0}; - uint8_t nvmLabel[WH_NVM_LABEL_LEN] = "NonExportableNvmObj"; - int32_t out_rc = 0; - whNvmSize out_len = 0; - - WH_TEST_PRINT("Testing non-exportable NVM object access protection...\n"); - - /* Test 1: Regular NVM Read Protection */ - /* Create NVM object with non-exportable flag */ - ret = wh_Client_NvmAddObject(client, nvmId, WH_NVM_ACCESS_ANY, - WH_NVM_FLAGS_NONEXPORTABLE, sizeof(nvmLabel), - nvmLabel, sizeof(nvmData), nvmData, &out_rc); - if (ret != 0 || out_rc != 0) { - WH_ERROR_PRINT( - "Failed to add non-exportable NVM object: ret=%d, out_rc=%d\n", ret, - (int)out_rc); - return ret != 0 ? ret : out_rc; - } - - /* Try to read the non-exportable NVM object - should fail */ - out_rc = 0; - ret = wh_Client_NvmRead(client, nvmId, 0, sizeof(exportedNvmData), &out_rc, - &out_len, exportedNvmData); - if (ret != 0 || out_rc != WH_ERROR_ACCESS) { - WH_ERROR_PRINT("Non-exportable NVM object was read unexpectedly: " - "ret=%d, out_rc=%d\n", - ret, (int)out_rc); - return -1; - } - - WH_TEST_DEBUG_PRINT("Non-exportable NVM object read correctly denied\n"); - - /* Clean up NVM object */ - whNvmId destroyList[] = {nvmId}; - out_rc = 0; - wh_Client_NvmDestroyObjects(client, 1, destroyList, &out_rc); - - /* Test 2: Verify exportable NVM objects can still be read */ - memcpy(nvmLabel, "ExportableNvmObject", sizeof("ExportableNvmObject")); - - ret = wh_Client_NvmAddObject(client, nvmId, WH_NVM_ACCESS_ANY, - WH_NVM_FLAGS_NONE, sizeof(nvmLabel), nvmLabel, - sizeof(nvmData), nvmData, &out_rc); - if (ret != 0 || out_rc != 0) { - WH_ERROR_PRINT( - "Failed to add exportable NVM object: ret=%d, out_rc=%d\n", ret, - (int)out_rc); - return ret != 0 ? ret : out_rc; - } - - /* Try to read the exportable NVM object - should succeed */ - memset(exportedNvmData, 0, sizeof(exportedNvmData)); - out_rc = 0; - out_len = 0; - ret = wh_Client_NvmRead(client, nvmId, 0, sizeof(exportedNvmData), &out_rc, - &out_len, exportedNvmData); - if (ret != 0 || out_rc != 0) { - WH_ERROR_PRINT( - "Failed to read exportable NVM object: ret=%d, out_rc=%d\n", ret, - (int)out_rc); - return ret != 0 ? ret : out_rc; - } - - /* Verify data matches */ - if (out_len != sizeof(nvmData) || - memcmp(nvmData, exportedNvmData, out_len) != 0) { - WH_ERROR_PRINT("Exported NVM data doesn't match original\n"); - return -1; - } - - WH_TEST_DEBUG_PRINT("Exportable NVM object read succeeded\n"); - - /* Clean up */ - out_rc = 0; - wh_Client_NvmDestroyObjects(client, 1, &nvmId, &out_rc); - -#ifdef WOLFHSM_CFG_DMA - /* Test 3: DMA NVM Read Protection */ - WH_TEST_PRINT("Testing DMA NVM read protection...\n"); - - /* Create NVM object with non-exportable flag */ - memcpy(nvmLabel, "NonExportDmaNvmObj", sizeof("NonExportDmaNvmObj")); - - ret = wh_Client_NvmAddObject(client, nvmId, WH_NVM_ACCESS_ANY, - WH_NVM_FLAGS_NONEXPORTABLE, sizeof(nvmLabel), - nvmLabel, sizeof(nvmData), nvmData, &out_rc); - if (ret != 0 || out_rc != 0) { - WH_ERROR_PRINT("Failed to add non-exportable NVM object for DMA: " - "ret=%d, out_rc=%d\n", - ret, (int)out_rc); - return ret != 0 ? ret : out_rc; - } - - /* Try to read the non-exportable NVM object via DMA - should fail */ - memset(exportedNvmData, 0, sizeof(exportedNvmData)); - out_rc = 0; - ret = wh_Client_NvmReadDma(client, nvmId, 0, sizeof(exportedNvmData), - exportedNvmData, &out_rc); - if (ret != 0 || out_rc != WH_ERROR_ACCESS) { - WH_ERROR_PRINT("Non-exportable NVM object was read via DMA " - "unexpectedly: ret=%d, out_rc=%d\n", - ret, (int)out_rc); - return -1; - } - - WH_TEST_DEBUG_PRINT("Non-exportable NVM object DMA read correctly denied\n"); - - /* Clean up */ - out_rc = 0; - wh_Client_NvmDestroyObjects(client, 1, &nvmId, &out_rc); - - /* Test 4: Verify exportable NVM objects can be read via DMA */ - memcpy(nvmLabel, "ExportableDmaNvmObj", sizeof("ExportableDmaNvmObj")); - - ret = wh_Client_NvmAddObject(client, nvmId, WH_NVM_ACCESS_ANY, - WH_NVM_FLAGS_NONE, sizeof(nvmLabel), nvmLabel, - sizeof(nvmData), nvmData, &out_rc); - if (ret != 0 || out_rc != 0) { - WH_ERROR_PRINT( - "Failed to add exportable NVM object for DMA: ret=%d, out_rc=%d\n", - ret, (int)out_rc); - return ret != 0 ? ret : out_rc; - } - - /* Try to read the exportable NVM object via DMA - should succeed */ - memset(exportedNvmData, 0, sizeof(exportedNvmData)); - out_rc = 0; - ret = wh_Client_NvmReadDma(client, nvmId, 0, sizeof(exportedNvmData), - exportedNvmData, &out_rc); - if (ret != 0 || out_rc != 0) { - WH_ERROR_PRINT( - "Failed to read exportable NVM object via DMA: ret=%d, out_rc=%d\n", - ret, (int)out_rc); - return ret != 0 ? ret : out_rc; - } - - /* Verify data matches */ - if (memcmp(nvmData, exportedNvmData, sizeof(nvmData)) != 0) { - WH_ERROR_PRINT("DMA exported NVM data doesn't match original\n"); - return -1; - } - - WH_TEST_DEBUG_PRINT("Exportable NVM object DMA read succeeded\n"); - - /* Clean up */ - out_rc = 0; - wh_Client_NvmDestroyObjects(client, 1, &nvmId, &out_rc); -#endif /* WOLFHSM_CFG_DMA */ - - WH_TEST_PRINT("NON-EXPORTABLE NVM ACCESS TEST SUCCESS\n"); - return 0; -} - -#if defined(WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS) -static int _testNvmNonmodifiableNoOverwrite(whClientContext* client) -{ - int32_t server_rc; - uint8_t data1[] = {0x11, 0x22, 0x33, 0x44}; - uint8_t data2[] = {0xAA, 0xBB, 0xCC, 0xDD}; - uint8_t readData[sizeof(data1)] = {0}; - whNvmSize readLen = sizeof(readData); - uint8_t label[] = "TST"; - uint8_t labelLen = (uint8_t)strlen((const char*)label); - whNvmId list[1] = {TEST_NVM_ID_NONMOD}; - - WH_TEST_PRINT("Testing NVM NONMODIFIABLE: no overwrite...\n"); - - WH_TEST_RETURN_ON_FAIL( - wh_Client_NvmAddObject(client, TEST_NVM_ID_NONMOD, WH_NVM_ACCESS_ANY, - WH_NVM_FLAGS_NONMODIFIABLE, labelLen, label, - sizeof(data1), data1, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONMOD, 0, - sizeof(readData), &server_rc, - &readLen, readData)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); - - WH_TEST_RETURN_ON_FAIL( - wh_Client_NvmAddObject(client, TEST_NVM_ID_NONMOD, WH_NVM_ACCESS_ANY, - WH_NVM_FLAGS_NONMODIFIABLE, labelLen, label, - sizeof(data2), data2, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); - - memset(readData, 0, sizeof(readData)); - readLen = sizeof(readData); - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONMOD, 0, - sizeof(readData), &server_rc, - &readLen, readData)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); - - WH_TEST_PRINT(" NVM NONMODIFIABLE no overwrite: PASS\n"); - - - WH_TEST_PRINT("Testing NVM NONMODIFIABLE: no destroy...\n"); - - WH_TEST_RETURN_ON_FAIL( - wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); - - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONMOD, 0, - sizeof(readData), &server_rc, - &readLen, readData)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - - WH_TEST_PRINT(" NVM NONMODIFIABLE no destroy: PASS\n"); - return WH_ERROR_OK; -} - -static int _testNvmNondestroyableModifyNoDestroy(whClientContext* client) -{ - int32_t server_rc; - uint8_t data1[] = {0x55, 0x66, 0x77, 0x88}; - uint8_t data2[] = {0x99, 0xAA, 0xBB, 0xCC}; - uint8_t readData[sizeof(data2)] = {0}; - whNvmSize readLen = sizeof(readData); - whNvmId list[1] = {TEST_NVM_ID_NONDESTROYABLE}; - uint8_t label[] = "NDY"; - uint8_t labelLen = (uint8_t)strlen((const char*)label); - - WH_TEST_PRINT( - "Testing NVM NONDESTROYABLE: modify allowed, destroy denied...\n"); - - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObject( - client, TEST_NVM_ID_NONDESTROYABLE, WH_NVM_ACCESS_ANY, - WH_NVM_FLAGS_NONDESTROYABLE, labelLen, label, sizeof(data1), data1, - &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - - /* modify the object */ - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObject( - client, TEST_NVM_ID_NONDESTROYABLE, WH_NVM_ACCESS_ANY, - WH_NVM_FLAGS_NONDESTROYABLE, labelLen, label, sizeof(data2), data2, - &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONDESTROYABLE, - 0, sizeof(readData), &server_rc, - &readLen, readData)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); - - WH_TEST_RETURN_ON_FAIL( - wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); - - memset(readData, 0, sizeof(readData)); - readLen = sizeof(readData); - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONDESTROYABLE, - 0, sizeof(readData), &server_rc, - &readLen, readData)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); - - WH_TEST_PRINT( - " NVM NONDESTROYABLE modify allowed, destroy denied: PASS\n"); - return WH_ERROR_OK; -} - -#ifdef WOLFHSM_CFG_DMA -static int _testNvmNonmodifiableNoOverwriteDma(whClientContext* client) -{ - int32_t server_rc; - uint8_t data1[] = {0x11, 0x22, 0x33, 0x44}; - uint8_t data2[] = {0xAA, 0xBB, 0xCC, 0xDD}; - uint8_t readData[sizeof(data1)] = {0}; - whNvmSize readLen = sizeof(readData); - whNvmId list[1] = {TEST_NVM_ID_NONMOD_DMA}; - whNvmMetadata meta = {0}; - - WH_TEST_PRINT("Testing NVM DMA NONMODIFIABLE: no overwrite...\n"); - - meta.id = TEST_NVM_ID_NONMOD_DMA; - meta.access = WH_NVM_ACCESS_ANY; - meta.flags = WH_NVM_FLAGS_NONMODIFIABLE; - meta.len = sizeof(data1); - memcpy(meta.label, "DMANONMOD", sizeof("DMANONMOD")); - - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( - client, &meta, sizeof(data1), data1, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadDma(client, TEST_NVM_ID_NONMOD_DMA, - 0, sizeof(readData), readData, - &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); - - meta.len = sizeof(data2); - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( - client, &meta, sizeof(data2), data2, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); - - memset(readData, 0, sizeof(readData)); - readLen = sizeof(readData); - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadDma( - client, TEST_NVM_ID_NONMOD_DMA, 0, readLen, readData, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); - - WH_TEST_PRINT(" NVM DMA NONMODIFIABLE no overwrite: PASS\n"); - - - WH_TEST_PRINT("Testing NVM DMA NONMODIFIABLE: no destroy...\n"); - - WH_TEST_RETURN_ON_FAIL( - wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); - - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadDma(client, TEST_NVM_ID_NONMOD_DMA, - 0, sizeof(readData), readData, - &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - - WH_TEST_PRINT(" NVM DMA NONMODIFIABLE no destroy: PASS\n"); - return WH_ERROR_OK; -} - -static int _testNvmNondestroyableModifyNoDestroyDma(whClientContext* client) -{ - int32_t server_rc; - uint8_t data1[] = {0x55, 0x66, 0x77, 0x88}; - uint8_t data2[] = {0x99, 0xAA, 0xBB, 0xCC}; - uint8_t readData[sizeof(data2)] = {0}; - whNvmSize readLen = sizeof(readData); - whNvmId list[1] = {TEST_NVM_ID_NONDESTROYABLE_DMA}; - whNvmMetadata meta = {0}; - - WH_TEST_PRINT( - "Testing NVM DMA NONDESTROYABLE: modify allowed, destroy denied...\n"); - - meta.id = TEST_NVM_ID_NONDESTROYABLE_DMA; - meta.access = WH_NVM_ACCESS_ANY; - meta.flags = WH_NVM_FLAGS_NONDESTROYABLE; - meta.len = sizeof(data1); - memcpy(meta.label, "DMANONDEST", sizeof("DMANONDEST")); - - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( - client, &meta, sizeof(data1), data1, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - - /* modify the object via DMA */ - meta.len = sizeof(data2); - WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( - client, &meta, sizeof(data2), data2, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - - WH_TEST_RETURN_ON_FAIL( - wh_Client_NvmReadDma(client, TEST_NVM_ID_NONDESTROYABLE_DMA, 0, - sizeof(readData), readData, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); - - WH_TEST_RETURN_ON_FAIL( - wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); - - memset(readData, 0, sizeof(readData)); - readLen = sizeof(readData); - WH_TEST_RETURN_ON_FAIL( - wh_Client_NvmReadDma(client, TEST_NVM_ID_NONDESTROYABLE_DMA, 0, readLen, - readData, &server_rc)); - WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); - - WH_TEST_PRINT( - " NVM DMA NONDESTROYABLE modify allowed, destroy denied: PASS\n"); - return WH_ERROR_OK; -} -#endif /* WOLFHSM_CFG_DMA */ - -#if !defined(WOLFHSM_CFG_NO_CRYPTO) -static int _testKeyNonmodifiableNoRecache(whClientContext* client) -{ - int ret; - uint16_t keyId = TEST_KEY_ID_NONMOD; - uint8_t key1[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; - uint8_t key2[] = {0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, - 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; - uint8_t label[] = "nonmod_key"; - - WH_TEST_PRINT("Testing Key NONMODIFIABLE: no re-cache...\n"); - - ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, - sizeof(label), key1, sizeof(key1), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - - ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, - sizeof(label), key2, sizeof(key2), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); - - ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, - sizeof(label), key2, sizeof(key2), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); - - ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, - sizeof(label), key2, sizeof(key2), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - WH_TEST_PRINT(" Key NONMODIFIABLE no re-cache: PASS\n"); - - WH_TEST_PRINT("Testing Key NONMODIFIABLE: no erase...\n"); - - ret = wh_Client_KeyErase(client, TEST_KEY_ID_NONMOD); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - WH_TEST_PRINT(" Key NONMODIFIABLE no erase: PASS\n"); - - return WH_ERROR_OK; -} - -static int _testKeyNonmodifiableInNvm(whClientContext* client) -{ - uint16_t keyId = TEST_KEY_ID_PROMOTE; - uint8_t key1[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}; - uint8_t key2[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F}; - uint8_t label[] = "promote_key"; - int ret; - - WH_TEST_PRINT("Testing Key NONMODIFIABLE: commit then enforce...\n"); - - ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONE, label, sizeof(label), - key1, sizeof(key1), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); - - ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, - sizeof(label), key2, sizeof(key2), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); - - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); - - ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONE, label, sizeof(label), - key1, sizeof(key1), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - WH_TEST_PRINT(" Key NONMODIFIABLE commit then enforce: PASS\n"); - return WH_ERROR_OK; -} - -static int _testKeyNondestroyableNoErase(whClientContext* client) -{ - int ret; - uint16_t keyId = TEST_KEY_ID_NONDESTROYABLE; - uint8_t key1[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, - 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE}; - uint8_t key2[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22, - 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00}; - uint8_t exported[sizeof(key2)] = {0}; - uint16_t exportedLen = sizeof(exported); - uint8_t label[] = "nondestroyable_key"; - - WH_TEST_PRINT( - "Testing Key NONDESTROYABLE: erase denied, modify allowed...\n"); - - ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONDESTROYABLE, label, - sizeof(label), key1, sizeof(key1), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); - - ret = wh_Client_KeyErase(client, keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - exportedLen = sizeof(exported); - ret = wh_Client_KeyExport(client, keyId, NULL, 0, exported, &exportedLen); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key1)); - WH_TEST_ASSERT_RETURN(memcmp(exported, key1, sizeof(key1)) == 0); - - ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONDESTROYABLE, label, - sizeof(label), key2, sizeof(key2), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); - - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); - - exportedLen = sizeof(exported); - ret = wh_Client_KeyExport(client, keyId, NULL, 0, exported, &exportedLen); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key2)); - WH_TEST_ASSERT_RETURN(memcmp(exported, key2, sizeof(key2)) == 0); - - WH_TEST_PRINT(" Key NONDESTROYABLE erase denied, modify allowed: PASS\n"); - return WH_ERROR_OK; -} - -#ifdef WOLFHSM_CFG_DMA -static int _testKeyNonmodifiableNoRecacheDma(whClientContext* client) -{ - int ret; - uint16_t keyId = TEST_KEY_ID_NONMOD_DMA; - uint8_t key1[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; - uint8_t key2[] = {0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, - 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; - uint8_t label[] = "nonmod_key_dma"; - - WH_TEST_PRINT("Testing Key DMA NONMODIFIABLE: no re-cache...\n"); - - ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, - sizeof(label), key1, sizeof(key1), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - - ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, - sizeof(label), key2, sizeof(key2), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); - - ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, - sizeof(label), key2, sizeof(key2), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); - - ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, - sizeof(label), key2, sizeof(key2), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - WH_TEST_PRINT(" Key DMA NONMODIFIABLE no re-cache: PASS\n"); - - WH_TEST_PRINT("Testing Key DMA NONMODIFIABLE: no erase...\n"); - - ret = wh_Client_KeyErase(client, TEST_KEY_ID_NONMOD_DMA); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - WH_TEST_PRINT(" Key DMA NONMODIFIABLE no erase: PASS\n"); - - return WH_ERROR_OK; -} - -static int _testKeyNonmodifiableInNvmDma(whClientContext* client) -{ - uint16_t keyId = TEST_KEY_ID_PROMOTE_DMA; - uint8_t key1[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}; - uint8_t key2[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F}; - uint8_t label[] = "promote_key_dma"; - int ret; - - WH_TEST_PRINT("Testing Key DMA NONMODIFIABLE: commit then enforce...\n"); - - ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONE, label, sizeof(label), - key1, sizeof(key1), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); - - ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, - sizeof(label), key2, sizeof(key2), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); - - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); - - ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONE, label, sizeof(label), - key1, sizeof(key1), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - WH_TEST_PRINT(" Key DMA NONMODIFIABLE commit then enforce: PASS\n"); - return WH_ERROR_OK; -} - -static int _testKeyNondestroyableNoEraseDma(whClientContext* client) -{ - int ret; - uint16_t keyId = TEST_KEY_ID_NONDESTROYABLE_DMA; - uint8_t key1[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, - 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE}; - uint8_t key2[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22, - 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00}; - uint8_t exported[sizeof(key2)] = {0}; - uint16_t exportedLen = sizeof(exported); - uint8_t label[WH_NVM_LABEL_LEN] = "nondestroyable_dma"; - - WH_TEST_PRINT( - "Testing Key DMA NONDESTROYABLE: erase denied, modify allowed...\n"); - - ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONDESTROYABLE, label, - sizeof(label), key1, sizeof(key1), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); - - ret = wh_Client_KeyErase(client, keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); - - exportedLen = sizeof(exported); - ret = wh_Client_KeyExportDma(client, keyId, exported, sizeof(exported), - label, sizeof(label), &exportedLen); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key1)); - WH_TEST_ASSERT_RETURN(memcmp(exported, key1, sizeof(key1)) == 0); - - ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONDESTROYABLE, label, - sizeof(label), key2, sizeof(key2), &keyId); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); - - WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); - - exportedLen = sizeof(exported); - ret = wh_Client_KeyExportDma(client, keyId, exported, sizeof(exported), - label, sizeof(label), &exportedLen); - WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); - WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key2)); - WH_TEST_ASSERT_RETURN(memcmp(exported, key2, sizeof(key2)) == 0); - - WH_TEST_PRINT( - " Key DMA NONDESTROYABLE erase denied, modify allowed: PASS\n"); - return WH_ERROR_OK; -} -#endif /* WOLFHSM_CFG_DMA */ -#endif /* !WOLFHSM_CFG_NO_CRYPTO */ -#endif /* WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS */ - -static int whTest_NvmFlags(whClientContext* client) -{ - int ret = 0; - - WH_TEST_PRINT("=== NVM Flags Enforcement Tests ===\n\n"); - - WH_TEST_PRINT("=== NVM Object Tests (NONEXPORTABLE) ===\n"); - - ret = _testNonExportableNvmAccess(client); - if (ret != WH_ERROR_OK) - return ret; - - -#if defined(WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS) - WH_TEST_PRINT("--- NVM Object Tests (NONMODIFIABLE/NODESTROYABLE) ---\n"); - - ret = _testNvmNonmodifiableNoOverwrite(client); - if (ret != WH_ERROR_OK) - return ret; - - ret = _testNvmNondestroyableModifyNoDestroy(client); - if (ret != WH_ERROR_OK) - return ret; - -#ifdef WOLFHSM_CFG_DMA - WH_TEST_PRINT( - "\n--- NVM Object DMA Tests (NONMODIFIABLE/NONDESTROYABLE) DMA ---\n"); - - ret = _testNvmNonmodifiableNoOverwriteDma(client); - if (ret != WH_ERROR_OK) - return ret; - - ret = _testNvmNondestroyableModifyNoDestroyDma(client); - if (ret != WH_ERROR_OK) - return ret; -#endif /* WOLFHSM_CFG_DMA */ - -#if !defined(WOLFHSM_CFG_NO_CRYPTO) - WH_TEST_PRINT("\n--- Key Object Tests (NONMODIFIABLE/NODESTROYABLE) ---\n"); - - ret = _testKeyNonmodifiableNoRecache(client); - if (ret != WH_ERROR_OK) - return ret; - - ret = _testKeyNonmodifiableInNvm(client); - if (ret != WH_ERROR_OK) - return ret; - - ret = _testKeyNondestroyableNoErase(client); - if (ret != WH_ERROR_OK) - return ret; - -#ifdef WOLFHSM_CFG_DMA - WH_TEST_PRINT( - "\n--- Key Object DMA Tests (NONMODIFIABLE/NONDESTROYABLE) DMA ---\n"); - - ret = _testKeyNonmodifiableNoRecacheDma(client); - if (ret != WH_ERROR_OK) - return ret; - - ret = _testKeyNonmodifiableInNvmDma(client); - if (ret != WH_ERROR_OK) - return ret; - - ret = _testKeyNondestroyableNoEraseDma(client); - if (ret != WH_ERROR_OK) - return ret; -#endif /* WOLFHSM_CFG_DMA */ -#endif /* !WOLFHSM_CFG_NO_CRYPTO */ -#else - WH_TEST_PRINT("\n--- Skipping NVM Object Tests " - "(NONMODIFIABLE/NONDESTROYABLE) due to persistent " - "NVM artifacts not being allowed ---\n"); -#endif /* WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS */ - - WH_TEST_PRINT("\n=== All NVM Flags Tests Complete ===\n"); - return ret; -} #endif /* WOLFHSM_CFG_ENABLE_CLIENT */ #if defined(WOLFHSM_CFG_ENABLE_CLIENT) && defined(WOLFHSM_CFG_ENABLE_SERVER) diff --git a/test/wh_test_nvmflags.c b/test/wh_test_nvmflags.c new file mode 100644 index 00000000..6a8cbf28 --- /dev/null +++ b/test/wh_test_nvmflags.c @@ -0,0 +1,747 @@ +/* + * 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 . + */ + +#include "wolfhsm/wh_settings.h" + +#if defined(WOLFHSM_CFG_ENABLE_CLIENT) +#include "wh_test_common.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_error.h" + +#define TEST_NVM_ID_NONMOD 0x0001 +#define TEST_NVM_ID_MODIFIABLE 0x0002 +#define TEST_NVM_ID_NONDESTROYABLE 0x0003 +#define TEST_NVM_ID_NONMOD_DMA 0x0004 +#define TEST_NVM_ID_NONDESTROYABLE_DMA 0x0005 +#define TEST_KEY_ID_NONMOD 0x0001 +#define TEST_KEY_ID_MODIFIABLE 0x0002 +#define TEST_KEY_ID_PROMOTE 0x0003 +#define TEST_KEY_ID_NONDESTROYABLE 0x0004 +#define TEST_KEY_ID_NONMOD_DMA 0x0005 +#define TEST_KEY_ID_PROMOTE_DMA 0x0006 +#define TEST_KEY_ID_NONDESTROYABLE_DMA 0x0007 + +static int _testNonExportableNvmAccess(whClientContext* client) +{ + int ret = 0; + whNvmId nvmId = 2; /* Arbitrary NVM ID */ + uint8_t nvmData[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + uint8_t exportedNvmData[sizeof(nvmData)] = {0}; + uint8_t nvmLabel[WH_NVM_LABEL_LEN] = "NonExportableNvmObj"; + int32_t out_rc = 0; + whNvmSize out_len = 0; + + WH_TEST_PRINT("Testing non-exportable NVM object access protection...\n"); + + /* Test 1: Regular NVM Read Protection */ + /* Create NVM object with non-exportable flag */ + ret = wh_Client_NvmAddObject(client, nvmId, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONEXPORTABLE, sizeof(nvmLabel), + nvmLabel, sizeof(nvmData), nvmData, &out_rc); + if (ret != 0 || out_rc != 0) { + WH_ERROR_PRINT( + "Failed to add non-exportable NVM object: ret=%d, out_rc=%d\n", ret, + (int)out_rc); + return ret != 0 ? ret : out_rc; + } + + /* Try to read the non-exportable NVM object - should fail */ + out_rc = 0; + ret = wh_Client_NvmRead(client, nvmId, 0, sizeof(exportedNvmData), &out_rc, + &out_len, exportedNvmData); + if (ret != 0 || out_rc != WH_ERROR_ACCESS) { + WH_ERROR_PRINT("Non-exportable NVM object was read unexpectedly: " + "ret=%d, out_rc=%d\n", + ret, (int)out_rc); + return -1; + } + + WH_TEST_DEBUG_PRINT("Non-exportable NVM object read correctly denied\n"); + + /* Clean up NVM object */ + whNvmId destroyList[] = {nvmId}; + out_rc = 0; + wh_Client_NvmDestroyObjects(client, 1, destroyList, &out_rc); + + /* Test 2: Verify exportable NVM objects can still be read */ + memcpy(nvmLabel, "ExportableNvmObject", sizeof("ExportableNvmObject")); + + ret = wh_Client_NvmAddObject(client, nvmId, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONE, sizeof(nvmLabel), nvmLabel, + sizeof(nvmData), nvmData, &out_rc); + if (ret != 0 || out_rc != 0) { + WH_ERROR_PRINT( + "Failed to add exportable NVM object: ret=%d, out_rc=%d\n", ret, + (int)out_rc); + return ret != 0 ? ret : out_rc; + } + + /* Try to read the exportable NVM object - should succeed */ + memset(exportedNvmData, 0, sizeof(exportedNvmData)); + out_rc = 0; + out_len = 0; + ret = wh_Client_NvmRead(client, nvmId, 0, sizeof(exportedNvmData), &out_rc, + &out_len, exportedNvmData); + if (ret != 0 || out_rc != 0) { + WH_ERROR_PRINT( + "Failed to read exportable NVM object: ret=%d, out_rc=%d\n", ret, + (int)out_rc); + return ret != 0 ? ret : out_rc; + } + + /* Verify data matches */ + if (out_len != sizeof(nvmData) || + memcmp(nvmData, exportedNvmData, out_len) != 0) { + WH_ERROR_PRINT("Exported NVM data doesn't match original\n"); + return -1; + } + + WH_TEST_DEBUG_PRINT("Exportable NVM object read succeeded\n"); + + /* Clean up */ + out_rc = 0; + wh_Client_NvmDestroyObjects(client, 1, &nvmId, &out_rc); + +#ifdef WOLFHSM_CFG_DMA + /* Test 3: DMA NVM Read Protection */ + WH_TEST_PRINT("Testing DMA NVM read protection...\n"); + + /* Create NVM object with non-exportable flag */ + memcpy(nvmLabel, "NonExportDmaNvmObj", sizeof("NonExportDmaNvmObj")); + + ret = wh_Client_NvmAddObject(client, nvmId, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONEXPORTABLE, sizeof(nvmLabel), + nvmLabel, sizeof(nvmData), nvmData, &out_rc); + if (ret != 0 || out_rc != 0) { + WH_ERROR_PRINT("Failed to add non-exportable NVM object for DMA: " + "ret=%d, out_rc=%d\n", + ret, (int)out_rc); + return ret != 0 ? ret : out_rc; + } + + /* Try to read the non-exportable NVM object via DMA - should fail */ + memset(exportedNvmData, 0, sizeof(exportedNvmData)); + out_rc = 0; + ret = wh_Client_NvmReadDma(client, nvmId, 0, sizeof(exportedNvmData), + exportedNvmData, &out_rc); + if (ret != 0 || out_rc != WH_ERROR_ACCESS) { + WH_ERROR_PRINT("Non-exportable NVM object was read via DMA " + "unexpectedly: ret=%d, out_rc=%d\n", + ret, (int)out_rc); + return -1; + } + + WH_TEST_DEBUG_PRINT("Non-exportable NVM object DMA read correctly denied\n"); + + /* Clean up */ + out_rc = 0; + wh_Client_NvmDestroyObjects(client, 1, &nvmId, &out_rc); + + /* Test 4: Verify exportable NVM objects can be read via DMA */ + memcpy(nvmLabel, "ExportableDmaNvmObj", sizeof("ExportableDmaNvmObj")); + + ret = wh_Client_NvmAddObject(client, nvmId, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONE, sizeof(nvmLabel), nvmLabel, + sizeof(nvmData), nvmData, &out_rc); + if (ret != 0 || out_rc != 0) { + WH_ERROR_PRINT( + "Failed to add exportable NVM object for DMA: ret=%d, out_rc=%d\n", + ret, (int)out_rc); + return ret != 0 ? ret : out_rc; + } + + /* Try to read the exportable NVM object via DMA - should succeed */ + memset(exportedNvmData, 0, sizeof(exportedNvmData)); + out_rc = 0; + ret = wh_Client_NvmReadDma(client, nvmId, 0, sizeof(exportedNvmData), + exportedNvmData, &out_rc); + if (ret != 0 || out_rc != 0) { + WH_ERROR_PRINT( + "Failed to read exportable NVM object via DMA: ret=%d, out_rc=%d\n", + ret, (int)out_rc); + return ret != 0 ? ret : out_rc; + } + + /* Verify data matches */ + if (memcmp(nvmData, exportedNvmData, sizeof(nvmData)) != 0) { + WH_ERROR_PRINT("DMA exported NVM data doesn't match original\n"); + return -1; + } + + WH_TEST_DEBUG_PRINT("Exportable NVM object DMA read succeeded\n"); + + /* Clean up */ + out_rc = 0; + wh_Client_NvmDestroyObjects(client, 1, &nvmId, &out_rc); +#endif /* WOLFHSM_CFG_DMA */ + + WH_TEST_PRINT("NON-EXPORTABLE NVM ACCESS TEST SUCCESS\n"); + return 0; +} + +#if defined(WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS) +static int _testNvmNonmodifiableNoOverwrite(whClientContext* client) +{ + int32_t server_rc; + uint8_t data1[] = {0x11, 0x22, 0x33, 0x44}; + uint8_t data2[] = {0xAA, 0xBB, 0xCC, 0xDD}; + uint8_t readData[sizeof(data1)] = {0}; + whNvmSize readLen = sizeof(readData); + uint8_t label[] = "TST"; + uint8_t labelLen = (uint8_t)strlen((const char*)label); + whNvmId list[1] = {TEST_NVM_ID_NONMOD}; + + WH_TEST_PRINT("Testing NVM NONMODIFIABLE: no overwrite...\n"); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmAddObject(client, TEST_NVM_ID_NONMOD, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONMODIFIABLE, labelLen, label, + sizeof(data1), data1, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONMOD, 0, + sizeof(readData), &server_rc, + &readLen, readData)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmAddObject(client, TEST_NVM_ID_NONMOD, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONMODIFIABLE, labelLen, label, + sizeof(data2), data2, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + memset(readData, 0, sizeof(readData)); + readLen = sizeof(readData); + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONMOD, 0, + sizeof(readData), &server_rc, + &readLen, readData)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); + + WH_TEST_PRINT(" NVM NONMODIFIABLE no overwrite: PASS\n"); + + + WH_TEST_PRINT("Testing NVM NONMODIFIABLE: no destroy...\n"); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONMOD, 0, + sizeof(readData), &server_rc, + &readLen, readData)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_PRINT(" NVM NONMODIFIABLE no destroy: PASS\n"); + return WH_ERROR_OK; +} + +static int _testNvmNondestroyableModifyNoDestroy(whClientContext* client) +{ + int32_t server_rc; + uint8_t data1[] = {0x55, 0x66, 0x77, 0x88}; + uint8_t data2[] = {0x99, 0xAA, 0xBB, 0xCC}; + uint8_t readData[sizeof(data2)] = {0}; + whNvmSize readLen = sizeof(readData); + whNvmId list[1] = {TEST_NVM_ID_NONDESTROYABLE}; + uint8_t label[] = "NDY"; + uint8_t labelLen = (uint8_t)strlen((const char*)label); + + WH_TEST_PRINT( + "Testing NVM NONDESTROYABLE: modify allowed, destroy denied...\n"); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObject( + client, TEST_NVM_ID_NONDESTROYABLE, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONDESTROYABLE, labelLen, label, sizeof(data1), data1, + &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* modify the object */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObject( + client, TEST_NVM_ID_NONDESTROYABLE, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONDESTROYABLE, labelLen, label, sizeof(data2), data2, + &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONDESTROYABLE, + 0, sizeof(readData), &server_rc, + &readLen, readData)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + memset(readData, 0, sizeof(readData)); + readLen = sizeof(readData); + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead(client, TEST_NVM_ID_NONDESTROYABLE, + 0, sizeof(readData), &server_rc, + &readLen, readData)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); + + WH_TEST_PRINT( + " NVM NONDESTROYABLE modify allowed, destroy denied: PASS\n"); + return WH_ERROR_OK; +} + +#ifdef WOLFHSM_CFG_DMA +static int _testNvmNonmodifiableNoOverwriteDma(whClientContext* client) +{ + int32_t server_rc; + uint8_t data1[] = {0x11, 0x22, 0x33, 0x44}; + uint8_t data2[] = {0xAA, 0xBB, 0xCC, 0xDD}; + uint8_t readData[sizeof(data1)] = {0}; + whNvmSize readLen = sizeof(readData); + whNvmId list[1] = {TEST_NVM_ID_NONMOD_DMA}; + whNvmMetadata meta = {0}; + + WH_TEST_PRINT("Testing NVM DMA NONMODIFIABLE: no overwrite...\n"); + + meta.id = TEST_NVM_ID_NONMOD_DMA; + meta.access = WH_NVM_ACCESS_ANY; + meta.flags = WH_NVM_FLAGS_NONMODIFIABLE; + meta.len = sizeof(data1); + memcpy(meta.label, "DMANONMOD", sizeof("DMANONMOD")); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( + client, &meta, sizeof(data1), data1, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadDma(client, TEST_NVM_ID_NONMOD_DMA, + 0, sizeof(readData), readData, + &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); + + meta.len = sizeof(data2); + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( + client, &meta, sizeof(data2), data2, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + memset(readData, 0, sizeof(readData)); + readLen = sizeof(readData); + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadDma( + client, TEST_NVM_ID_NONMOD_DMA, 0, readLen, readData, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data1, sizeof(data1)) == 0); + + WH_TEST_PRINT(" NVM DMA NONMODIFIABLE no overwrite: PASS\n"); + + + WH_TEST_PRINT("Testing NVM DMA NONMODIFIABLE: no destroy...\n"); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadDma(client, TEST_NVM_ID_NONMOD_DMA, + 0, sizeof(readData), readData, + &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_PRINT(" NVM DMA NONMODIFIABLE no destroy: PASS\n"); + return WH_ERROR_OK; +} + +static int _testNvmNondestroyableModifyNoDestroyDma(whClientContext* client) +{ + int32_t server_rc; + uint8_t data1[] = {0x55, 0x66, 0x77, 0x88}; + uint8_t data2[] = {0x99, 0xAA, 0xBB, 0xCC}; + uint8_t readData[sizeof(data2)] = {0}; + whNvmSize readLen = sizeof(readData); + whNvmId list[1] = {TEST_NVM_ID_NONDESTROYABLE_DMA}; + whNvmMetadata meta = {0}; + + WH_TEST_PRINT( + "Testing NVM DMA NONDESTROYABLE: modify allowed, destroy denied...\n"); + + meta.id = TEST_NVM_ID_NONDESTROYABLE_DMA; + meta.access = WH_NVM_ACCESS_ANY; + meta.flags = WH_NVM_FLAGS_NONDESTROYABLE; + meta.len = sizeof(data1); + memcpy(meta.label, "DMANONDEST", sizeof("DMANONDEST")); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( + client, &meta, sizeof(data1), data1, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* modify the object via DMA */ + meta.len = sizeof(data2); + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObjectDma( + client, &meta, sizeof(data2), data2, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmReadDma(client, TEST_NVM_ID_NONDESTROYABLE_DMA, 0, + sizeof(readData), readData, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmDestroyObjects(client, 1, list, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + + memset(readData, 0, sizeof(readData)); + readLen = sizeof(readData); + WH_TEST_RETURN_ON_FAIL( + wh_Client_NvmReadDma(client, TEST_NVM_ID_NONDESTROYABLE_DMA, 0, readLen, + readData, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(readData, data2, sizeof(data2)) == 0); + + WH_TEST_PRINT( + " NVM DMA NONDESTROYABLE modify allowed, destroy denied: PASS\n"); + return WH_ERROR_OK; +} +#endif /* WOLFHSM_CFG_DMA */ + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) +static int _testKeyNonmodifiableNoRecache(whClientContext* client) +{ + int ret; + uint16_t keyId = TEST_KEY_ID_NONMOD; + uint8_t key1[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + uint8_t key2[] = {0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; + uint8_t label[] = "nonmod_key"; + + WH_TEST_PRINT("Testing Key NONMODIFIABLE: no re-cache...\n"); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key NONMODIFIABLE no re-cache: PASS\n"); + + WH_TEST_PRINT("Testing Key NONMODIFIABLE: no erase...\n"); + + ret = wh_Client_KeyErase(client, TEST_KEY_ID_NONMOD); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key NONMODIFIABLE no erase: PASS\n"); + + return WH_ERROR_OK; +} + +static int _testKeyNonmodifiableInNvm(whClientContext* client) +{ + uint16_t keyId = TEST_KEY_ID_PROMOTE; + uint8_t key1[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}; + uint8_t key2[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F}; + uint8_t label[] = "promote_key"; + int ret; + + WH_TEST_PRINT("Testing Key NONMODIFIABLE: commit then enforce...\n"); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONE, label, sizeof(label), + key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONE, label, sizeof(label), + key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key NONMODIFIABLE commit then enforce: PASS\n"); + return WH_ERROR_OK; +} + +static int _testKeyNondestroyableNoErase(whClientContext* client) +{ + int ret; + uint16_t keyId = TEST_KEY_ID_NONDESTROYABLE; + uint8_t key1[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE}; + uint8_t key2[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22, + 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00}; + uint8_t exported[sizeof(key2)] = {0}; + uint16_t exportedLen = sizeof(exported); + uint8_t label[] = "nondestroyable_key"; + + WH_TEST_PRINT( + "Testing Key NONDESTROYABLE: erase denied, modify allowed...\n"); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONDESTROYABLE, label, + sizeof(label), key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyErase(client, keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + exportedLen = sizeof(exported); + ret = wh_Client_KeyExport(client, keyId, NULL, 0, exported, &exportedLen); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key1)); + WH_TEST_ASSERT_RETURN(memcmp(exported, key1, sizeof(key1)) == 0); + + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONDESTROYABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + exportedLen = sizeof(exported); + ret = wh_Client_KeyExport(client, keyId, NULL, 0, exported, &exportedLen); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key2)); + WH_TEST_ASSERT_RETURN(memcmp(exported, key2, sizeof(key2)) == 0); + + WH_TEST_PRINT(" Key NONDESTROYABLE erase denied, modify allowed: PASS\n"); + return WH_ERROR_OK; +} + +#ifdef WOLFHSM_CFG_DMA +static int _testKeyNonmodifiableNoRecacheDma(whClientContext* client) +{ + int ret; + uint16_t keyId = TEST_KEY_ID_NONMOD_DMA; + uint8_t key1[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + uint8_t key2[] = {0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; + uint8_t label[] = "nonmod_key_dma"; + + WH_TEST_PRINT("Testing Key DMA NONMODIFIABLE: no re-cache...\n"); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key DMA NONMODIFIABLE no re-cache: PASS\n"); + + WH_TEST_PRINT("Testing Key DMA NONMODIFIABLE: no erase...\n"); + + ret = wh_Client_KeyErase(client, TEST_KEY_ID_NONMOD_DMA); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key DMA NONMODIFIABLE no erase: PASS\n"); + + return WH_ERROR_OK; +} + +static int _testKeyNonmodifiableInNvmDma(whClientContext* client) +{ + uint16_t keyId = TEST_KEY_ID_PROMOTE_DMA; + uint8_t key1[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}; + uint8_t key2[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F}; + uint8_t label[] = "promote_key_dma"; + int ret; + + WH_TEST_PRINT("Testing Key DMA NONMODIFIABLE: commit then enforce...\n"); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONE, label, sizeof(label), + key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONMODIFIABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONE, label, sizeof(label), + key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + WH_TEST_PRINT(" Key DMA NONMODIFIABLE commit then enforce: PASS\n"); + return WH_ERROR_OK; +} + +static int _testKeyNondestroyableNoEraseDma(whClientContext* client) +{ + int ret; + uint16_t keyId = TEST_KEY_ID_NONDESTROYABLE_DMA; + uint8_t key1[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE}; + uint8_t key2[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22, + 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00}; + uint8_t exported[sizeof(key2)] = {0}; + uint16_t exportedLen = sizeof(exported); + uint8_t label[WH_NVM_LABEL_LEN] = "nondestroyable_dma"; + + WH_TEST_PRINT( + "Testing Key DMA NONDESTROYABLE: erase denied, modify allowed...\n"); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONDESTROYABLE, label, + sizeof(label), key1, sizeof(key1), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + ret = wh_Client_KeyErase(client, keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_ACCESS); + + exportedLen = sizeof(exported); + ret = wh_Client_KeyExportDma(client, keyId, exported, sizeof(exported), + label, sizeof(label), &exportedLen); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key1)); + WH_TEST_ASSERT_RETURN(memcmp(exported, key1, sizeof(key1)) == 0); + + ret = wh_Client_KeyCacheDma(client, WH_NVM_FLAGS_NONDESTROYABLE, label, + sizeof(label), key2, sizeof(key2), &keyId); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCommit(client, keyId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + + exportedLen = sizeof(exported); + ret = wh_Client_KeyExportDma(client, keyId, exported, sizeof(exported), + label, sizeof(label), &exportedLen); + WH_TEST_ASSERT_RETURN(ret == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(exportedLen == sizeof(key2)); + WH_TEST_ASSERT_RETURN(memcmp(exported, key2, sizeof(key2)) == 0); + + WH_TEST_PRINT( + " Key DMA NONDESTROYABLE erase denied, modify allowed: PASS\n"); + return WH_ERROR_OK; +} +#endif /* WOLFHSM_CFG_DMA */ +#endif /* !WOLFHSM_CFG_NO_CRYPTO */ +#endif /* WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS */ +int whTest_NvmFlags(whClientContext* client) +{ + int ret = 0; + + WH_TEST_PRINT("=== NVM Flags Enforcement Tests ===\n\n"); + + WH_TEST_PRINT("=== NVM Object Tests (NONEXPORTABLE) ===\n"); + + ret = _testNonExportableNvmAccess(client); + if (ret != WH_ERROR_OK) + return ret; + + +#if defined(WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS) + WH_TEST_PRINT("--- NVM Object Tests (NONMODIFIABLE/NODESTROYABLE) ---\n"); + + ret = _testNvmNonmodifiableNoOverwrite(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testNvmNondestroyableModifyNoDestroy(client); + if (ret != WH_ERROR_OK) + return ret; + +#ifdef WOLFHSM_CFG_DMA + WH_TEST_PRINT( + "\n--- NVM Object DMA Tests (NONMODIFIABLE/NONDESTROYABLE) DMA ---\n"); + + ret = _testNvmNonmodifiableNoOverwriteDma(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testNvmNondestroyableModifyNoDestroyDma(client); + if (ret != WH_ERROR_OK) + return ret; +#endif /* WOLFHSM_CFG_DMA */ + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) + WH_TEST_PRINT("\n--- Key Object Tests (NONMODIFIABLE/NODESTROYABLE) ---\n"); + + ret = _testKeyNonmodifiableNoRecache(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testKeyNonmodifiableInNvm(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testKeyNondestroyableNoErase(client); + if (ret != WH_ERROR_OK) + return ret; + +#ifdef WOLFHSM_CFG_DMA + WH_TEST_PRINT( + "\n--- Key Object DMA Tests (NONMODIFIABLE/NONDESTROYABLE) DMA ---\n"); + + ret = _testKeyNonmodifiableNoRecacheDma(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testKeyNonmodifiableInNvmDma(client); + if (ret != WH_ERROR_OK) + return ret; + + ret = _testKeyNondestroyableNoEraseDma(client); + if (ret != WH_ERROR_OK) + return ret; +#endif /* WOLFHSM_CFG_DMA */ +#endif /* !WOLFHSM_CFG_NO_CRYPTO */ +#else + WH_TEST_PRINT("\n--- Skipping NVM Object Tests " + "(NONMODIFIABLE/NONDESTROYABLE) due to persistent " + "NVM artifacts not being allowed ---\n"); +#endif /* WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS */ + + WH_TEST_PRINT("\n=== All NVM Flags Tests Complete ===\n"); + return ret; +} +#endif /* WOLFHSM_CFG_ENABLE_CLIENT */ diff --git a/test/wh_test_nvmflags.h b/test/wh_test_nvmflags.h new file mode 100644 index 00000000..e07f19f0 --- /dev/null +++ b/test/wh_test_nvmflags.h @@ -0,0 +1,30 @@ +/* + * 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/test/wh_test_clientserver.h + * + */ +#ifndef TEST_WH_TEST_NVMFLAGS_H_ +#define TEST_WH_TEST_NVMFLAGS_H_ + +#include "wolfhsm/wh_client.h" + +int whTest_NvmFlags(whClientContext* client); + +#endif /* TEST_WH_TEST_NVMFLAGS_H */ From 6ee24891ab24453bbbb34994fb7b4d5e2f9a8212 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 19 Dec 2025 18:27:14 +0100 Subject: [PATCH 14/15] test: nvmflags: add missing string.h header --- test/wh_test_nvmflags.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/wh_test_nvmflags.c b/test/wh_test_nvmflags.c index 6a8cbf28..d1427944 100644 --- a/test/wh_test_nvmflags.c +++ b/test/wh_test_nvmflags.c @@ -20,6 +20,8 @@ #include "wolfhsm/wh_settings.h" #if defined(WOLFHSM_CFG_ENABLE_CLIENT) +#include + #include "wh_test_common.h" #include "wolfhsm/wh_client.h" #include "wolfhsm/wh_error.h" From 490454e07068e87f81439b0006e2743814850dd0 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Mon, 22 Dec 2025 16:46:45 +0100 Subject: [PATCH 15/15] Revert "ci: temporarly fix wolfssl tag as master broke testing" This reverts commit b7b719394ea4f0ad190a902731d8c6198f461bd2. --- .github/workflows/build-and-test.yml | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 2d799cd5..aa6d4f32 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -22,25 +22,12 @@ jobs: - name: List compiler version run: gcc --version - # get last release version fo wolfssl - - name: Get latest wolfssl release tag - uses: actions/github-script@v7 - id: latest-release - with: - script: | - const release = await github.rest.repos.getLatestRelease({ - owner: 'wolfssl', - repo: 'wolfssl' - }); - return release.data.tag_name; - result-encoding: string - + # pull and build wolfssl - name: Checkout wolfssl uses: actions/checkout@v4 with: repository: wolfssl/wolfssl - ref: ${{ steps.latest-release.outputs.result }} - path: wolfssl # pull and build wolfssl + path: wolfssl # Build and test standard build - name: Build and test