diff --git a/docs/src/appendix01.md b/docs/src/appendix01.md index 5775054a..72ef50d5 100644 --- a/docs/src/appendix01.md +++ b/docs/src/appendix01.md @@ -1 +1,93 @@ # 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`). 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..2fbb449f 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,131 @@ 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); +``` + +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. + +### 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 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 +498,3 @@ For CMAC operations that need to use cached keys, seperate wolfHSM specific func ## AUTOSAR SHE API - 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/src/wh_client.c b/src/wh_client.c index 605fedbb..927c4bf0 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, whKeyId 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_nvm.c b/src/wh_nvm.c index 5c81981d..bc32b09e 100644 --- a/src/wh_nvm.c +++ b/src/wh_nvm.c @@ -33,17 +33,70 @@ #include "wolfhsm/wh_nvm.h" +typedef enum { + WH_NVM_OP_ADD = 0, + WH_NVM_OP_READ, + WH_NVM_OP_DESTROY, +} whNvmOp; -int wh_Nvm_Init(whNvmContext* context, const whNvmConfig *config) +/* 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 (ret != WH_ERROR_OK) { + return ret; + } + + if (existing_meta != NULL) { + *existing_meta = meta; + } + + switch (op) { + case WH_NVM_OP_ADD: + if (meta.flags & WH_NVM_FLAGS_NONMODIFIABLE) { + return WH_ERROR_ACCESS; + } + break; + + case WH_NVM_OP_DESTROY: + if (meta.flags & + (WH_NVM_FLAGS_NONMODIFIABLE | WH_NVM_FLAGS_NONDESTROYABLE)) { + return WH_ERROR_ACCESS; + } + break; + + case WH_NVM_OP_READ: + 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) { 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) @@ -152,6 +205,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 && ret != WH_ERROR_NOTFOUND) { + 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 +266,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 +301,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 a2a1857c..625246c3 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -83,6 +83,112 @@ static whKeyCacheContext* _GetCacheContext(whServerContext* server, return &server->localCache; } +typedef enum { + WH_KS_OP_CACHE = 0, + WH_KS_OP_COMMIT, + WH_KS_OP_EVICT, + WH_KS_OP_EXPORT, + WH_KS_OP_REVOKE, +} 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_OK) { + return 0; + } + + 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) +{ + 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; + } + + /* Check cache first */ + ret = _FindInCache(server, keyId, NULL, NULL, NULL, &cacheMeta); + if (ret == WH_ERROR_OK && cacheMeta != NULL) { + foundInCache = 1; + } + else if (ret != WH_ERROR_OK && ret != WH_ERROR_NOTFOUND) { + return ret; + } + + /* Check NVM if not in cache */ + if (!foundInCache) { + ret = wh_Nvm_GetMetadata(server->nvm, keyId, &nvmMeta); + if (ret == WH_ERROR_OK) { + foundInNvm = 1; + } + 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_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; + default: + /* unknown operation */ + return WH_ERROR_BADARGS; + } + + return WH_ERROR_OK; +} /** * @brief Find a key in the specified cache context */ @@ -137,6 +243,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 +258,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 +280,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 +310,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 +345,17 @@ 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; @@ -268,8 +393,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); @@ -295,9 +418,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; @@ -317,31 +439,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 evicted 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); + if (ret != WH_ERROR_OK && ret != WH_ERROR_NOTFOUND) { + 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 +502,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; - } - } + memcpy(slotBuf, in, meta->len); + memcpy((uint8_t*)slotMeta, (uint8_t*)meta, sizeof(whNvmMetadata)); + _MarkKeyCommitted(_GetCacheContext(server, meta->id), meta->id, 0); - /* 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; - } - } - } + 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); - /* 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; - } - } - - /* 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, @@ -516,30 +591,35 @@ int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId, ret = _FindInCache(server, keyId, &foundIndex, &foundBigIndex, cacheBufOut, cacheMetaOut); - if (ret != WH_ERROR_OK) { - /* For wrapped keys, just probe the cache and error if not found. We - * don't support automatically unwrapping and caching outside of the - * keywrap API */ - if (WH_KEYID_TYPE(keyId) == WH_KEYTYPE_WRAPPED) { - return WH_ERROR_NOTFOUND; - } + if (ret != WH_ERROR_NOTFOUND) { + return ret; + } + + /* 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); + /* 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)); - } + /* 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); } } } @@ -623,6 +703,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); + 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 +739,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); + 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 +782,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); + 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 +810,90 @@ 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); +} + +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); + 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 @@ -1475,7 +1674,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 +1718,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 +1745,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 +1778,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 +1804,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 +1835,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 +1856,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)); @@ -1680,6 +1869,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}; @@ -1879,43 +2088,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 +2119,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, @@ -1943,17 +2145,12 @@ 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; } - /* 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 +2163,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); + 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/test/config/wolfhsm_cfg.h b/test/config/wolfhsm_cfg.h index 6c8b926f..940512a5 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 @@ -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_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/test/wh_test_clientserver.c b/test/wh_test_clientserver.c index 1c540120..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,11 +84,6 @@ typedef struct { #define TEST_MEM_UNMAPPED_BYTE ((uint8_t)0xBB) #endif /* WOLFHSM_CFG_DMA */ - -#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() */ @@ -491,164 +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; -} #endif /* WOLFHSM_CFG_ENABLE_CLIENT */ #if defined(WOLFHSM_CFG_ENABLE_CLIENT) && defined(WOLFHSM_CFG_ENABLE_SERVER) @@ -1383,6 +1221,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 +1491,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 +1522,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); diff --git a/test/wh_test_nvmflags.c b/test/wh_test_nvmflags.c new file mode 100644 index 00000000..d1427944 --- /dev/null +++ b/test/wh_test_nvmflags.c @@ -0,0 +1,749 @@ +/* + * 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 + +#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 */ diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index 4a5a7b92..f1b1b70a 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, whKeyId 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, whKeyId keyId); + #ifdef WOLFHSM_CFG_DMA /** diff --git a/wolfhsm/wh_common.h b/wolfhsm/wh_common.h index cfe0d936..6d60c5d2 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 */ @@ -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_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_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..d34588e7 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,20 @@ 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 Revoke a key (clears usage and marks non-modifiable) + * + * Placeholder implementation for key revocation. + */ +int wh_Server_KeystoreRevokeKey(whServerContext* server, whKeyId keyId); + /** * @brief Handle key management requests from clients * @@ -174,6 +224,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 +247,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