From fc63258c0908eea6ba56671b180074d457aa21b0 Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Wed, 3 Jun 2026 12:50:10 -0700 Subject: [PATCH 1/2] Set committed=1 on cached keys read back from NVM --- src/wh_server_keystore.c | 6 +++- test/wh_test_crypto.c | 62 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index 277db543f..bd4775dcf 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -886,7 +886,11 @@ int wh_Server_KeystoreReadKey(whServerContext* server, whKeyId keyId, } /* cache key if free slot, will only kick out other committed keys */ if (ret == 0 && out != NULL) { - (void)wh_Server_KeystoreCacheKey(server, meta, out); + if (wh_Server_KeystoreCacheKey(server, meta, out) == WH_ERROR_OK) { + /* Cached key found in NVM. Mark it committed so it can be + evicted later. */ + _MarkKeyCommitted(_GetCacheContext(server, keyId), keyId, 1); + } } #ifdef WOLFHSM_CFG_SHE_EXTENSION /* use empty key of zeros if we couldn't find the master ecu key */ diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index da9434743..5bd5f0dc5 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -7304,6 +7304,68 @@ static int whTest_KeyCache(whClientContext* ctx, int devId, WC_RNG* rng) } #endif /* WOLFHSM_CFG_DMA */ + /* Ensure cache entries read from NVM are evictable. + * Max out the cache with NVM-backed keys, then try caching a new key. */ + if (ret == 0) { + const int nvmKeyCount = WOLFHSM_CFG_SERVER_KEYCACHE_COUNT; + /* {0} == WH_KEYID_ERASED, so unused entries are skipped on cleanup + * and each cache call requests a server-assigned id. */ + uint16_t nvmKeyIds[WOLFHSM_CFG_SERVER_KEYCACHE_COUNT] = {0}; + uint16_t extraKeyId = WH_KEYID_ERASED; + + /* Commit each key to NVM then evict it, so the keys live only in + * the backing store and not in the cache. */ + for (i = 0; (i < nvmKeyCount) && (ret == 0); i++) { + ret = wh_Client_KeyCache(ctx, 0, labelIn, sizeof(labelIn), key, + sizeof(key), &nvmKeyIds[i]); + if (ret == 0) { + ret = wh_Client_KeyCommit(ctx, nvmKeyIds[i]); + } + if (ret == 0) { + ret = wh_Client_KeyEvict(ctx, nvmKeyIds[i]); + } + if (ret != 0) { + WH_ERROR_PRINT("Failed to stage NVM key %d: %d\n", i, ret); + } + } + + /* Read each key back, which caches it from NVM and fills the + * cache with NVM-backed entries. */ + for (i = 0; (i < nvmKeyCount) && (ret == 0); i++) { + outLen = sizeof(keyOut); + ret = wh_Client_KeyExport(ctx, nvmKeyIds[i], labelOut, + sizeof(labelOut), keyOut, &outLen); + if (ret != 0) { + WH_ERROR_PRINT("Failed to read back NVM key %d: %d\n", i, ret); + } + } + + /* With the cache full of NVM-backed keys, caching a new key must + * still succeed by evicting one of them. */ + if (ret == 0) { + ret = wh_Client_KeyCache(ctx, 0, labelIn, sizeof(labelIn), key, + sizeof(key), &extraKeyId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to cache key with NVM-backed cache " + "full: %d\n", ret); + } + } + + /* Restore state regardless of the outcome above. */ + if (extraKeyId != WH_KEYID_ERASED) { + (void)wh_Client_KeyEvict(ctx, extraKeyId); + } + for (i = 0; i < nvmKeyCount; i++) { + if (nvmKeyIds[i] != WH_KEYID_ERASED) { + (void)wh_Client_KeyErase(ctx, nvmKeyIds[i]); + } + } + + if (ret == 0) { + WH_TEST_PRINT("KEY CACHE NVM-BACKED EVICTION SUCCESS\n"); + } + } + return ret; } From 6c959e9924626bc0684a229840783c0b3238f6ab Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Tue, 2 Jun 2026 14:12:04 -0700 Subject: [PATCH 2/2] Add refactored SHE tests --- test-refactor/README.md | 3 +- test-refactor/client-server/wh_test_she.c | 552 +++++++++++++++++++++ test-refactor/posix/wh_test_posix_server.c | 14 + test-refactor/server/wh_test_she_server.c | 482 ++++++++++++++++++ test-refactor/wh_test_list.c | 6 + 5 files changed, 1056 insertions(+), 1 deletion(-) create mode 100644 test-refactor/client-server/wh_test_she.c create mode 100644 test-refactor/server/wh_test_she_server.c diff --git a/test-refactor/README.md b/test-refactor/README.md index cd37861f9..5c37719c2 100644 --- a/test-refactor/README.md +++ b/test-refactor/README.md @@ -87,6 +87,8 @@ Translated tests: | `wh_test_flash_ramsim.c::whTest_Flash_RamSim` | `posix/wh_test_flash_ramsim.c::{whTest_FlashWriteLock, whTest_FlashEraseProgramVerify, whTest_FlashUnitOps}` | POSIX port-specific (`whTestGroup_RunOne`) | remove ramsim coupling and migrate to server group | | `wh_test_nvm_flash.c::whTest_NvmFlash` | `posix/wh_test_nvm_flash.c::whTest_NvmAddOverwriteDestroy` | POSIX port-specific (`whTestGroup_RunOne`) | remove ramsim coupling and migrate to server group | | `wh_test_posix_threadsafe_stress.c::whTest_ThreadSafeStress` | called directly from `posix/wh_test_posix_main.c` | POSIX port-specific (direct call) | | +| `wh_test_she.c` (`whTest_SheMasterEcuKeyFallback`, `whTest_SheReqSizeChecking`) | `server/wh_test_she_server.c::{whTest_SheMasterEcuKeyFallback, whTest_SheReqSizeChecking}` | Server | server-internal checks reworked to use the shared server context; the POSIX server config gains a `whServerSheContext` under `WOLFHSM_CFG_SHE_EXTENSION` | +| `wh_test_she.c::whTest_She` (client flows) | `client-server/wh_test_she.c::whTest_She` | Client | SHE UID/secure-boot state is one-shot per server lifetime, so the three legacy client flows are folded into one test that does `SetUid` plus a single comm-boundary-sized secure boot, then the load-key vectors, UID handling, RND, ECB/CBC/MAC, and write-protect rejection -- all of which only need UID set and secure boot complete. Build with `make SHE=1` | Not yet migrated (still live in `wolfHSM/test/`): @@ -100,7 +102,6 @@ Not yet migrated (still live in `wolfHSM/test/`): | `wh_test_multiclient.c::whTest_MultiClient` | | | `wh_test_lock.c::whTest_LockConfig`, `whTest_LockPosix` | `whTest_LockConfig` to be reworked to fit the Misc group, likely with a context param. | | `wh_test_log.c::whTest_Log`, `whTest_LogBackend_RunAll` | `whTest_LogBackend_RunAll` to be reworked to fit the Misc group, likely with a context param. | -| `wh_test_she.c::whTest_She` | | | `wh_test_timeout.c::whTest_TimeoutPosix` | | | `wh_test_auth.c::whTest_AuthMEM`, `whTest_AuthTCP` | | | `wh_test_server_img_mgr.c::whTest_ServerImgMgr` | | diff --git a/test-refactor/client-server/wh_test_she.c b/test-refactor/client-server/wh_test_she.c new file mode 100644 index 000000000..f5883e403 --- /dev/null +++ b/test-refactor/client-server/wh_test_she.c @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ +/* + * test-refactor/client-server/wh_test_she.c + * + * Client-side SHE flow. SHE UID and secure-boot state is one-shot + * per server lifetime, so the whole sequence runs as a single test + * against one connected client: one SetUid and one secure boot + * (sized to the comm-buffer boundary), then the load-key vectors, + * UID handling, RND, ECB/CBC/MAC round-trips, and a write-protect + * rejection, which only require that UID is set and secure boot has + * completed. + */ + +#include "wolfhsm/wh_settings.h" + +#if defined(WOLFHSM_CFG_SHE_EXTENSION) && !defined(WOLFHSM_CFG_NO_CRYPTO) && \ + defined(WOLFHSM_CFG_ENABLE_CLIENT) + +#include +#include + +#include "wolfssl/wolfcrypt/settings.h" +#include "wolfssl/wolfcrypt/types.h" +#include "wolfssl/wolfcrypt/cmac.h" + +#include "wolfhsm/wh_common.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_client_she.h" +#include "wolfhsm/wh_she_common.h" +#include "wolfhsm/wh_she_crypto.h" +#include "wolfhsm/wh_message_she.h" + +#include "wh_test_common.h" + +#ifndef TEST_ADMIN_USERNAME +#define TEST_ADMIN_USERNAME "admin" +#endif +#ifndef TEST_ADMIN_PIN +#define TEST_ADMIN_PIN "1234" +#endif + + +/* Destroy a SHE key so the unit tests don't leak NVM objects across + * invocations. Necessary, as SHE doesn't expose a destroy key API since + * SHE keys are supposed to be fixed hardware keys. */ +static int _destroySheKey(whClientContext* client, whNvmId clientSheKeyId) +{ + int rc = 0; + int32_t serverRc = 0; + whNvmId id = WH_MAKE_KEYID(WH_KEYTYPE_SHE, client->comm->client_id, + clientSheKeyId); + + rc = wh_Client_NvmDestroyObjects(client, 1, &id, &serverRc); + if (rc == WH_ERROR_OK) { + rc = serverRc; + } + + return rc; +} + + +/* + * Full SHE flow against a freshly connected client. SetUid and secure + * boot are one-shot per server lifetime, so they run once up front; the + * remaining checks (load-key vectors, UID handling, RND, ECB/CBC/MAC, + * write protect) only need UID set and secure boot complete. + */ +int whTest_She(whClientContext* client) +{ + int ret = 0; + WC_RNG rng[1]; + Cmac cmac[1]; + /* key doubles as the boot MAC key (secure boot) and later the RND + * output that gets loaded as the RAM plain key. */ + uint8_t key[16] = {0}; + uint32_t keySz = sizeof(key); + uint8_t iv[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + uint8_t plainText[64]; + uint8_t cipherText[64]; + uint8_t finalText[64]; + /* secretKey and prngSeed are taken from SHE test vectors */ + uint8_t sheUid[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + uint8_t secretKey[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; + uint8_t prngSeed[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; + uint8_t zeros[WH_SHE_BOOT_MAC_PREFIX_LEN] = {0}; + /* Bootloader sized to the comm-buffer boundary so the single secure + * boot exercises the secure-boot update path at the maximum chunk. */ + uint8_t bootloader[WOLFHSM_CFG_COMM_DATA_LEN - + sizeof(whMessageShe_SecureBootUpdateRequest)]; + uint8_t bootMacDigest[16] = {0}; + uint8_t vectorMasterEcuKey[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f}; + uint32_t digestSz = sizeof(bootMacDigest); + uint32_t bootloaderSz; + uint32_t serverCommDataLen = WOLFHSM_CFG_COMM_DATA_LEN; + uint32_t maxBoundaryUpdateChunk = + WOLFHSM_CFG_COMM_DATA_LEN - + sizeof(whMessageShe_SecureBootUpdateRequest); + uint8_t vectorMessageOne[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41}; + uint8_t vectorMessageTwo[] = {0x2b, 0x11, 0x1e, 0x2d, 0x93, 0xf4, 0x86, + 0x56, 0x6b, 0xcb, 0xba, 0x1d, 0x7f, 0x7a, 0x97, 0x97, 0xc9, 0x46, 0x43, + 0xb0, 0x50, 0xfc, 0x5d, 0x4d, 0x7d, 0xe1, 0x4c, 0xff, 0x68, 0x22, 0x03, + 0xc3}; + uint8_t vectorMessageThree[] = {0xb9, 0xd7, 0x45, 0xe5, 0xac, 0xe7, 0xd4, + 0x18, 0x60, 0xbc, 0x63, 0xc2, 0xb9, 0xf5, 0xbb, 0x46}; + uint8_t vectorMessageFour[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0xb4, 0x72, 0xe8, + 0xd8, 0x72, 0x7d, 0x70, 0xd5, 0x72, 0x95, 0xe7, 0x48, 0x49, 0xa2, 0x79, + 0x17}; + uint8_t vectorMessageFive[] = {0x82, 0x0d, 0x8d, 0x95, 0xdc, 0x11, 0xb4, + 0x66, 0x88, 0x78, 0x16, 0x0c, 0xb2, 0xa4, 0xe2, 0x3e}; + uint8_t vectorRawKey[] = {0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00}; + uint8_t outMessageFour[sizeof(vectorMessageFour)]; + uint8_t outMessageFive[sizeof(vectorMessageFive)]; + uint8_t entropy[] = {0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, + 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51}; + uint8_t sreg; + uint8_t messageOne[WH_SHE_M1_SZ]; + uint8_t messageTwo[WH_SHE_M2_SZ]; + uint8_t messageThree[WH_SHE_M3_SZ]; + uint8_t messageFour[WH_SHE_M4_SZ]; + uint8_t messageFive[WH_SHE_M5_SZ]; + const uint32_t SHE_TEST_VECTOR_KEY_ID = 4; + const uint32_t SHE_WP_KEY_ID = 6; + + if (client == NULL) { + return WH_ERROR_BADARGS; + } + +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + /* Log in as an admin user for the rest of the test */ + WH_TEST_RETURN_ON_FAIL(wh_Client_AuthLogin( + client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, + strlen(TEST_ADMIN_PIN), &ret, NULL)); +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ + + /* === SetUid + boundary-sized secure boot (one-shot per server) === */ + + /* Size the bootloader to the server's comm-buffer boundary so the + * single secure boot drives a maximum-sized secure-boot update. */ + WH_TEST_RETURN_ON_FAIL(wh_Client_CommInfo( + client, NULL, NULL, &serverCommDataLen, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL)); + if (serverCommDataLen <= sizeof(whMessageShe_SecureBootUpdateRequest)) { + WH_ERROR_PRINT("Invalid server cfg_comm_data_len %u\n", + (unsigned int)serverCommDataLen); + return WH_ERROR_ABORTED; + } + if (serverCommDataLen < WOLFHSM_CFG_COMM_DATA_LEN) { + maxBoundaryUpdateChunk = + serverCommDataLen - sizeof(whMessageShe_SecureBootUpdateRequest); + } + bootloaderSz = maxBoundaryUpdateChunk; + + /* generate the boot MAC key and a fake bootloader */ + if ((ret = wc_InitRng_ex(rng, NULL, WH_DEV_ID)) != 0) { + WH_ERROR_PRINT("Failed to wc_InitRng_ex %d\n", ret); + goto exit; + } + if ((ret = wc_RNG_GenerateBlock(rng, key, sizeof(key))) != 0) { + WH_ERROR_PRINT("Failed to wc_RNG_GenerateBlock %d\n", ret); + wc_FreeRng(rng); + goto exit; + } + if ((ret = wc_RNG_GenerateBlock(rng, bootloader, + maxBoundaryUpdateChunk)) != 0) { + WH_ERROR_PRINT("Failed to wc_RNG_GenerateBlock %d\n", ret); + wc_FreeRng(rng); + goto exit; + } + /* Done generating test data, free RNG */ + wc_FreeRng(rng); + + /* boot MAC digest: CMAC(0..0 | size | bootloader) */ + if ((ret = wc_InitCmac(cmac, key, sizeof(key), WC_CMAC_AES, NULL)) != 0) { + WH_ERROR_PRINT("Failed to wc_InitCmac %d\n", ret); + goto exit; + } + if ((ret = wc_CmacUpdate(cmac, zeros, sizeof(zeros))) != 0) { + WH_ERROR_PRINT("Failed to wc_CmacUpdate %d\n", ret); + goto exit; + } + if ((ret = wc_CmacUpdate(cmac, (uint8_t*)&bootloaderSz, + sizeof(bootloaderSz))) != 0) { + WH_ERROR_PRINT("Failed to wc_CmacUpdate %d\n", ret); + goto exit; + } + if ((ret = wc_CmacUpdate(cmac, bootloader, bootloaderSz)) != 0) { + WH_ERROR_PRINT("Failed to wc_CmacUpdate %d\n", ret); + goto exit; + } + digestSz = AES_BLOCK_SIZE; + if ((ret = wc_CmacFinal(cmac, bootMacDigest, (word32*)&digestSz)) != 0) { + WH_ERROR_PRINT("Failed to wc_CmacFinal %d\n", ret); + goto exit; + } + /* store the boot MAC key and digest */ + if ((ret = wh_Client_ShePreProgramKey(client, WH_SHE_BOOT_MAC_KEY_ID, 0, + key, sizeof(key))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_ShePreProgramKey %d\n", ret); + goto exit; + } + if ((ret = wh_Client_ShePreProgramKey(client, WH_SHE_BOOT_MAC, 0, + bootMacDigest, + sizeof(bootMacDigest))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_ShePreProgramKey %d\n", ret); + goto exit; + } + /* set the she uid */ + if ((ret = wh_Client_SheSetUid(client, sheUid, sizeof(sheUid))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheSetUid %d\n", ret); + goto exit; + } + /* verify bootloader at the comm-buffer boundary */ + if ((ret = wh_Client_SheSecureBoot(client, bootloader, bootloaderSz)) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheSecureBoot %d\n", ret); + goto exit; + } + if ((ret = wh_Client_SheGetStatus(client, &sreg)) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheGetStatus %d\n", ret); + goto exit; + } + /* verify bootOk, bootFinished and secureBoot */ + if ((sreg & WH_SHE_SREG_BOOT_OK) == 0 || + (sreg & WH_SHE_SREG_BOOT_FINISHED) == 0 || + (sreg & WH_SHE_SREG_SECURE_BOOT) == 0) { + ret = WH_ERROR_ABORTED; + WH_ERROR_PRINT("Failed to secureBoot with SHE CMAC\n"); + goto exit; + } + WH_TEST_PRINT("SHE secure boot SUCCESS\n"); + + /* === Loadable keys and test vectors === */ + + /* load the secret key and prng seed using pre program */ + if ((ret = wh_Client_ShePreProgramKey(client, WH_SHE_SECRET_KEY_ID, 0, + secretKey, sizeof(secretKey))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_ShePreProgramKey %d\n", ret); + goto exit; + } + if ((ret = wh_Client_ShePreProgramKey(client, WH_SHE_PRNG_SEED_ID, 0, + prngSeed, sizeof(prngSeed))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_ShePreProgramKey %d\n", ret); + goto exit; + } + /* load the vector master ecu key */ + if ((ret = wh_She_GenerateLoadableKey(WH_SHE_MASTER_ECU_KEY_ID, + WH_SHE_SECRET_KEY_ID, 1, 0, sheUid, vectorMasterEcuKey, secretKey, + messageOne, messageTwo, messageThree, messageFour, + messageFive)) != 0) { + WH_ERROR_PRINT("Failed to wh_She_GenerateLoadableKey %d\n", ret); + goto exit; + } + if ((ret = wh_Client_SheLoadKey(client, messageOne, messageTwo, + messageThree, outMessageFour, outMessageFive)) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheLoadKey %d\n", ret); + goto exit; + } + /* verify that our helper function output matches the vector */ + if ((ret = wh_She_GenerateLoadableKey(SHE_TEST_VECTOR_KEY_ID, + WH_SHE_MASTER_ECU_KEY_ID, 1, 0, sheUid, vectorRawKey, + vectorMasterEcuKey, messageOne, messageTwo, messageThree, + messageFour, messageFive)) != 0) { + WH_ERROR_PRINT("Failed to wh_She_GenerateLoadableKey %d\n", ret); + goto exit; + } + if (memcmp(messageOne, vectorMessageOne, sizeof(vectorMessageOne)) != 0 || + memcmp(messageTwo, vectorMessageTwo, sizeof(vectorMessageTwo)) != 0 || + memcmp(messageThree, vectorMessageThree, + sizeof(vectorMessageThree)) != 0 || + memcmp(messageFour, vectorMessageFour, sizeof(vectorMessageFour)) != 0 || + memcmp(messageFive, vectorMessageFive, sizeof(vectorMessageFive)) != 0) { + ret = WH_ERROR_ABORTED; + WH_ERROR_PRINT("Failed to generate a loadable key to match the " + "vector\n"); + goto exit; + } + WH_TEST_PRINT("SHE wh_SheGenerateLoadableKey SUCCESS\n"); + /* test CMD_LOAD_KEY with test vector */ + if ((ret = wh_Client_SheLoadKey(client, vectorMessageOne, vectorMessageTwo, + vectorMessageThree, outMessageFour, outMessageFive)) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheLoadKey %d\n", ret); + goto exit; + } + if (memcmp(outMessageFour, vectorMessageFour, sizeof(vectorMessageFour)) + != 0 || + memcmp(outMessageFive, vectorMessageFive, + sizeof(vectorMessageFive)) != 0) { + ret = WH_ERROR_ABORTED; + WH_ERROR_PRINT("wh_Client_SheLoadKey FAILED TO MATCH\n"); + goto exit; + } + WH_TEST_PRINT("SHE LOAD KEY SUCCESS\n"); + + /* === LoadKey UID handling === */ + + /* A non-matching UID must be rejected, an all-zero UID must be + * rejected unless the stored target key has WH_SHE_FLAG_WILDCARD set. + * Use wh_She_GenerateLoadableKey with the authKey bytes so M3 is valid + * and the server reaches the UID check instead of failing earlier on + * CMAC verification. */ + { + uint8_t badUid[WH_SHE_UID_SZ]; + uint8_t zeroUid[WH_SHE_UID_SZ] = {0}; + const uint32_t SHE_WILDCARD_KEY_ID = 5; + + memset(badUid, 0xAA, sizeof(badUid)); + + /* Wrong UID targeting an existing key slot. Server must reject + * with WH_SHE_ERC_KEY_UPDATE_ERROR. */ + if ((ret = wh_She_GenerateLoadableKey(SHE_TEST_VECTOR_KEY_ID, + WH_SHE_MASTER_ECU_KEY_ID, 2, 0, badUid, vectorRawKey, + vectorMasterEcuKey, messageOne, messageTwo, messageThree, + messageFour, messageFive)) != 0) { + WH_ERROR_PRINT("Failed to generate bad-UID M1/M2/M3 %d\n", ret); + goto exit; + } + ret = wh_Client_SheLoadKey(client, messageOne, messageTwo, messageThree, + outMessageFour, outMessageFive); + if (ret != WH_SHE_ERC_KEY_UPDATE_ERROR) { + WH_ERROR_PRINT("SHE LOAD KEY bad UID: expected KEY_UPDATE_ERROR, " + "got %d\n", ret); + ret = WH_ERROR_ABORTED; + goto exit; + } + + /* Zero UID targeting an unused slot (stored flags == 0, so + * WH_SHE_FLAG_WILDCARD is clear). Server must reject. */ + if ((ret = wh_She_GenerateLoadableKey(SHE_WILDCARD_KEY_ID, + WH_SHE_MASTER_ECU_KEY_ID, 1, 0, zeroUid, vectorRawKey, + vectorMasterEcuKey, messageOne, messageTwo, messageThree, + messageFour, messageFive)) != 0) { + WH_ERROR_PRINT("Failed to generate zero-UID no-wildcard " + "M1/M2/M3 %d\n", ret); + goto exit; + } + ret = wh_Client_SheLoadKey(client, messageOne, messageTwo, messageThree, + outMessageFour, outMessageFive); + if (ret != WH_SHE_ERC_KEY_UPDATE_ERROR) { + WH_ERROR_PRINT("SHE LOAD KEY zero UID without wildcard: expected " + "KEY_UPDATE_ERROR, got %d\n", ret); + ret = WH_ERROR_ABORTED; + goto exit; + } + + /* Preload the target slot with WH_SHE_FLAG_WILDCARD and count + * 0 via ShePreProgramKey, which writes the meta label directly + * (wh_She_GenerateLoadableKey cannot encode flags > 4 bits due + * to the M2 layout overlap between flags and count). Then + * re-load the slot with an all-zero UID; the server must + * accept it because the stored flags contain WILDCARD. */ + if ((ret = wh_Client_ShePreProgramKey(client, SHE_WILDCARD_KEY_ID, + WH_SHE_FLAG_WILDCARD, vectorRawKey, sizeof(vectorRawKey))) + != 0) { + WH_ERROR_PRINT("Failed to preload wildcard key %d\n", ret); + goto exit; + } + if ((ret = wh_She_GenerateLoadableKey(SHE_WILDCARD_KEY_ID, + WH_SHE_MASTER_ECU_KEY_ID, 1, 0, zeroUid, vectorRawKey, + vectorMasterEcuKey, messageOne, messageTwo, messageThree, + messageFour, messageFive)) != 0) { + WH_ERROR_PRINT("Failed to generate zero-UID wildcard " + "M1/M2/M3 %d\n", ret); + goto exit; + } + if ((ret = wh_Client_SheLoadKey(client, messageOne, messageTwo, + messageThree, outMessageFour, outMessageFive)) != 0) { + WH_ERROR_PRINT("SHE LOAD KEY zero UID with wildcard: expected " + "success, got %d\n", ret); + goto exit; + } + + if ((ret = _destroySheKey(client, SHE_WILDCARD_KEY_ID)) != 0) { + WH_ERROR_PRINT("Failed to _destroySheKey wildcard slot, ret=%d\n", + ret); + goto exit; + } + WH_TEST_PRINT("SHE LOAD KEY UID checks SUCCESS\n"); + } + + /* === RND === */ + + if ((ret = wh_Client_SheInitRnd(client)) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheInitRnd %d\n", ret); + goto exit; + } + if ((ret = wh_Client_SheRnd(client, key, &keySz)) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheRnd %d\n", ret); + goto exit; + } + if ((ret = wh_Client_SheExtendSeed(client, entropy, sizeof(entropy))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheExtendSeed %d\n", ret); + goto exit; + } + WH_TEST_PRINT("SHE RND SUCCESS\n"); + + /* === RAM key ECB/CBC/MAC round-trips === */ + + if ((ret = wh_Client_SheLoadPlainKey(client, key, sizeof(key))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheLoadPlainKey %d\n", ret); + goto exit; + } + if ((ret = wh_Client_SheEncEcb(client, WH_SHE_RAM_KEY_ID, plainText, + cipherText, sizeof(plainText))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheEncEcb %d\n", ret); + goto exit; + } + if ((ret = wh_Client_SheExportRamKey(client, messageOne, messageTwo, + messageThree, messageFour, messageFive)) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheExportRamKey %d\n", ret); + goto exit; + } + if ((ret = wh_Client_SheLoadKey(client, messageOne, messageTwo, + messageThree, messageFour, messageFive)) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheLoadKey %d\n", ret); + goto exit; + } + if ((ret = wh_Client_SheDecEcb(client, WH_SHE_RAM_KEY_ID, cipherText, + finalText, sizeof(cipherText))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheDecEcb %d\n", ret); + goto exit; + } + if (memcmp(finalText, plainText, sizeof(plainText)) != 0) { + ret = WH_ERROR_ABORTED; + WH_ERROR_PRINT("SHE ECB FAILED TO MATCH\n"); + goto exit; + } + WH_TEST_PRINT("SHE ECB SUCCESS\n"); + if ((ret = wh_Client_SheEncCbc(client, WH_SHE_RAM_KEY_ID, iv, sizeof(iv), + plainText, cipherText, sizeof(plainText))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheEncCbc %d\n", ret); + goto exit; + } + if ((ret = wh_Client_SheDecCbc(client, WH_SHE_RAM_KEY_ID, iv, sizeof(iv), + cipherText, finalText, sizeof(cipherText))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheDecCbc %d\n", ret); + goto exit; + } + if (memcmp(finalText, plainText, sizeof(plainText)) != 0) { + ret = WH_ERROR_ABORTED; + WH_ERROR_PRINT("SHE CBC FAILED TO MATCH\n"); + goto exit; + } + WH_TEST_PRINT("SHE CBC SUCCESS\n"); + if ((ret = wh_Client_SheGenerateMac(client, WH_SHE_RAM_KEY_ID, plainText, + sizeof(plainText), cipherText, sizeof(cipherText))) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheGenerateMac %d\n", ret); + goto exit; + } + if ((ret = wh_Client_SheVerifyMac(client, WH_SHE_RAM_KEY_ID, plainText, + sizeof(plainText), cipherText, sizeof(cipherText), &sreg)) != 0) { + WH_ERROR_PRINT("Failed to wh_Client_SheVerifyMac %d\n", ret); + goto exit; + } + if (sreg != 0) { + ret = WH_ERROR_ABORTED; + WH_ERROR_PRINT("SHE CMAC FAILED TO VERIFY\n"); + goto exit; + } + WH_TEST_PRINT("SHE CMAC SUCCESS\n"); + + /* === Write protect === */ + + /* A key pre-programmed with WH_SHE_FLAG_WRITE_PROTECT cannot be + * overwritten via SHE LoadKey; the server must return + * WH_SHE_ERC_WRITE_PROTECTED. Reuses the secret key (auth) and the + * secure boot established above; uses a clean slot of its own. */ + if ((ret = wh_Client_ShePreProgramKey(client, SHE_WP_KEY_ID, + WH_SHE_FLAG_WRITE_PROTECT, + vectorRawKey, + sizeof(vectorRawKey))) != 0) { + WH_ERROR_PRINT("Failed to pre-program write-protected key %d\n", ret); + goto exit; + } + if ((ret = wh_She_GenerateLoadableKey(SHE_WP_KEY_ID, WH_SHE_SECRET_KEY_ID, + 1, 0, sheUid, vectorRawKey, secretKey, messageOne, messageTwo, + messageThree, messageFour, messageFive)) != 0) { + WH_ERROR_PRINT("Failed to generate loadable key %d\n", ret); + goto exit; + } + ret = wh_Client_SheLoadKey(client, messageOne, messageTwo, messageThree, + messageFour, messageFive); + if (ret != WH_SHE_ERC_WRITE_PROTECTED) { + WH_ERROR_PRINT("Expected WH_SHE_ERC_WRITE_PROTECTED, got %d\n", ret); + ret = WH_ERROR_ABORTED; + goto exit; + } + ret = 0; + WH_TEST_PRINT("SHE write protect SUCCESS\n"); + + /* === Cleanup: destroy provisioned keys so we don't leak NVM === */ + + if ((ret = _destroySheKey(client, WH_SHE_BOOT_MAC_KEY_ID)) != 0) { + WH_ERROR_PRINT("Failed to _destroySheKey, ret=%d\n", ret); + goto exit; + } + if ((ret = _destroySheKey(client, WH_SHE_BOOT_MAC)) != 0) { + WH_ERROR_PRINT("Failed to _destroySheKey, ret=%d\n", ret); + goto exit; + } + if ((ret = _destroySheKey(client, WH_SHE_SECRET_KEY_ID)) != 0) { + WH_ERROR_PRINT("Failed to _destroySheKey, ret=%d\n", ret); + goto exit; + } + if ((ret = _destroySheKey(client, WH_SHE_PRNG_SEED_ID)) != 0) { + WH_ERROR_PRINT("Failed to _destroySheKey, ret=%d\n", ret); + goto exit; + } + if ((ret = _destroySheKey(client, WH_SHE_MASTER_ECU_KEY_ID)) != 0) { + WH_ERROR_PRINT("Failed to _destroySheKey, ret=%d\n", ret); + goto exit; + } + if ((ret = _destroySheKey(client, SHE_TEST_VECTOR_KEY_ID)) != 0) { + WH_ERROR_PRINT("Failed to _destroySheKey, ret=%d\n", ret); + goto exit; + } + if ((ret = _destroySheKey(client, SHE_WP_KEY_ID)) != 0) { + WH_ERROR_PRINT("Failed to _destroySheKey, ret=%d\n", ret); + goto exit; + } + +exit: + return ret; +} + +#endif /* WOLFHSM_CFG_SHE_EXTENSION && !WOLFHSM_CFG_NO_CRYPTO && \ + WOLFHSM_CFG_ENABLE_CLIENT */ diff --git a/test-refactor/posix/wh_test_posix_server.c b/test-refactor/posix/wh_test_posix_server.c index 8ced55d32..30ea32339 100644 --- a/test-refactor/posix/wh_test_posix_server.c +++ b/test-refactor/posix/wh_test_posix_server.c @@ -37,6 +37,10 @@ #include "wolfhsm/wh_comm.h" #include "wolfhsm/wh_server.h" +#ifdef WOLFHSM_CFG_SHE_EXTENSION +#include "wolfhsm/wh_server_she.h" +#endif + #ifndef WOLFHSM_CFG_NO_CRYPTO #include "wolfssl/wolfcrypt/settings.h" #include "wolfssl/wolfcrypt/random.h" @@ -70,6 +74,11 @@ static whNvmContext _nvm; static whServerCryptoContext _crypto; #endif +#ifdef WOLFHSM_CFG_SHE_EXTENSION +/* SHE keystore/crypto state for the shared server (uidSet, sbState) */ +static whServerSheContext _she; +#endif + /* Mem transport -- buffers and server-side state. * The client side re-uses these buffers via * whTestPosix_Server_GetTransportConfig. */ @@ -139,6 +148,11 @@ int whTestPosix_Server_Init(whServerContext* server) sCfg.devId = INVALID_DEVID; #endif +#ifdef WOLFHSM_CFG_SHE_EXTENSION + memset(&_she, 0, sizeof(_she)); + sCfg.she = &_she; +#endif + return wh_Server_Init(server, &sCfg); } diff --git a/test-refactor/server/wh_test_she_server.c b/test-refactor/server/wh_test_she_server.c new file mode 100644 index 000000000..c772f34fb --- /dev/null +++ b/test-refactor/server/wh_test_she_server.c @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ +/* + * test-refactor/server/wh_test_she_server.c + * + * Server-side SHE test suite. Exercises internal server SHE + * behavior through direct server API calls against the shared + * server context: the master ECU key metadata fallback and the + * per-action request-size validation in the SHE handlers. + */ + +#include "wolfhsm/wh_settings.h" + +#if defined(WOLFHSM_CFG_SHE_EXTENSION) && !defined(WOLFHSM_CFG_NO_CRYPTO) + +#include +#include + +#include "wolfhsm/wh_common.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_nvm.h" +#include "wolfhsm/wh_server.h" +#include "wolfhsm/wh_server_keystore.h" +#include "wolfhsm/wh_server_she.h" +#include "wolfhsm/wh_she_common.h" +#include "wolfhsm/wh_message.h" +#include "wolfhsm/wh_message_she.h" +#include "wolfhsm/wh_comm.h" + +#include "wh_test_common.h" +#include "wh_test_list.h" + +/* Value of WH_SHE_SB_SUCCESS from the wh_server_she.c internal enum. + * Mirrored here since the enum is private to that translation unit. */ +#define TEST_SHE_SB_STATE_SUCCESS 3 + + +/* + * Reading the master ECU key when it has never been provisioned must + * succeed and return an all-zero key with correctly populated metadata. + */ +int whTest_SheMasterEcuKeyFallback(whServerContext* server) +{ + int ret; + whNvmMetadata outMeta[1] = {0}; + uint8_t keyBuf[WH_SHE_KEY_SZ] = {0}; + uint32_t keySz = sizeof(keyBuf); + uint8_t zeros[WH_SHE_KEY_SZ] = {0}; + whKeyId masterEcuKeyId; + + if (server == NULL) { + return WH_ERROR_BADARGS; + } + + masterEcuKeyId = WH_MAKE_KEYID(WH_KEYTYPE_SHE, server->comm->client_id, + WH_SHE_MASTER_ECU_KEY_ID); + + /* Fill keyBuf with non-zero to ensure it gets overwritten */ + memset(keyBuf, 0xFF, sizeof(keyBuf)); + + ret = wh_Server_KeystoreReadKey(server, masterEcuKeyId, outMeta, keyBuf, + &keySz); + + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(keySz == WH_SHE_KEY_SZ); + WH_TEST_ASSERT_RETURN(memcmp(keyBuf, zeros, WH_SHE_KEY_SZ) == 0); + WH_TEST_ASSERT_RETURN(outMeta->len == WH_SHE_KEY_SZ); + WH_TEST_ASSERT_RETURN(outMeta->id == masterEcuKeyId); + + WH_TEST_PRINT("SHE master ECU key fallback metadata test SUCCESS\n"); + + return 0; +} + + +/* + * Test that SHE server handlers reject requests with an invalid + * req_size while still producing an action-specific response packet. + * Each handler is called directly via wh_Server_HandleSheRequest() + * with a realistic but incorrectly sized request packet. + */ +int whTest_SheReqSizeChecking(whServerContext* server) +{ + int ret = 0; + uint16_t req_size = 0; + uint16_t resp_size = 0; + + /* Buffers for request and response packets */ + uint8_t req_packet[WOLFHSM_CFG_COMM_DATA_LEN]; + uint8_t resp_packet[WOLFHSM_CFG_COMM_DATA_LEN]; + + if (server == NULL) { + return WH_ERROR_BADARGS; + } + + /* + * Set SHE state so _ReportInvalidSheState allows requests through. + * WH_SHE_SET_UID always passes the state gate, but most other handlers + * require uidSet=1 and sbState=WH_SHE_SB_SUCCESS. + */ + server->she->uidSet = 1; + server->she->sbState = TEST_SHE_SB_STATE_SUCCESS; + + /* + * Test 1: WH_SHE_SET_UID with truncated request. + * Populate a valid UID in the packet, but pass req_size one byte short. + */ + { + whMessageShe_SetUidRequest* req = + (whMessageShe_SetUidRequest*)req_packet; + whMessageShe_SetUidResponse* setUidResp = + (whMessageShe_SetUidResponse*)resp_packet; + memset(setUidResp, 0, sizeof(*setUidResp)); + memset(req->uid, 0xAA, WH_SHE_UID_SZ); + req_size = sizeof(whMessageShe_SetUidRequest) - 1; + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_SET_UID, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*setUidResp)); + WH_TEST_ASSERT_RETURN(setUidResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 2: WH_SHE_SECURE_BOOT_INIT with truncated request. + * Set a valid bootloader size, but pass req_size one byte short. + */ + { + whMessageShe_SecureBootInitRequest* req = + (whMessageShe_SecureBootInitRequest*)req_packet; + whMessageShe_SecureBootInitResponse* secureBootInitResp = + (whMessageShe_SecureBootInitResponse*)resp_packet; + /* The state gate allows SECURE_BOOT_INIT through regardless of + * sbState; we are testing the size check which happens first. */ + memset(secureBootInitResp, 0, sizeof(*secureBootInitResp)); + req->sz = 256; + req_size = sizeof(whMessageShe_SecureBootInitRequest) - 1; + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_SECURE_BOOT_INIT, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*secureBootInitResp)); + WH_TEST_ASSERT_RETURN(secureBootInitResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* Secure boot failure resets sbState to SB_INIT, restore it */ + server->she->sbState = TEST_SHE_SB_STATE_SUCCESS; + + /* + * Test 3: WH_SHE_SECURE_BOOT_UPDATE with truncated fixed header. + * Set a valid chunk size but pass req_size smaller than the header. + */ + { + whMessageShe_SecureBootUpdateRequest* req = + (whMessageShe_SecureBootUpdateRequest*)req_packet; + whMessageShe_SecureBootUpdateResponse* secureBootUpdateResp = + (whMessageShe_SecureBootUpdateResponse*)resp_packet; + memset(secureBootUpdateResp, 0, sizeof(*secureBootUpdateResp)); + req->sz = 64; + req_size = sizeof(whMessageShe_SecureBootUpdateRequest) - 1; + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_SECURE_BOOT_UPDATE, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*secureBootUpdateResp)); + WH_TEST_ASSERT_RETURN(secureBootUpdateResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* Secure boot failure resets sbState to SB_INIT, restore it */ + server->she->sbState = TEST_SHE_SB_STATE_SUCCESS; + + /* + * Test 4: WH_SHE_SECURE_BOOT_FINISH expects no request body. + * Send a nonzero req_size to trigger the check. + */ + { + whMessageShe_SecureBootFinishResponse* secureBootFinishResp = + (whMessageShe_SecureBootFinishResponse*)resp_packet; + memset(secureBootFinishResp, 0, sizeof(*secureBootFinishResp)); + req_size = 1; + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_SECURE_BOOT_FINISH, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*secureBootFinishResp)); + WH_TEST_ASSERT_RETURN(secureBootFinishResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* Secure boot failure resets sbState to SB_INIT, restore it */ + server->she->sbState = TEST_SHE_SB_STATE_SUCCESS; + + /* + * Test 5: WH_SHE_LOAD_KEY with truncated request. + * Fill M1/M2/M3 with nonzero data, pass req_size one byte short. + * _LoadKey maps the malformed request to an action-specific response; + * verify the request still completes with a response packet instead + * of failing the transport path. + */ + { + whMessageShe_LoadKeyRequest* req = + (whMessageShe_LoadKeyRequest*)req_packet; + whMessageShe_LoadKeyResponse* loadKeyResp = + (whMessageShe_LoadKeyResponse*)resp_packet; + memset(loadKeyResp, 0, sizeof(*loadKeyResp)); + memset(req->messageOne, 0x11, WH_SHE_M1_SZ); + memset(req->messageTwo, 0x22, WH_SHE_M2_SZ); + memset(req->messageThree, 0x33, WH_SHE_M3_SZ); + req_size = sizeof(whMessageShe_LoadKeyRequest) - 1; + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_LOAD_KEY, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*loadKeyResp)); + WH_TEST_ASSERT_RETURN(loadKeyResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 6: WH_SHE_LOAD_PLAIN_KEY with truncated request. + * Fill a valid key, pass req_size one byte short. + */ + { + whMessageShe_LoadPlainKeyRequest* req = + (whMessageShe_LoadPlainKeyRequest*)req_packet; + whMessageShe_LoadPlainKeyResponse* loadPlainKeyResp = + (whMessageShe_LoadPlainKeyResponse*)resp_packet; + memset(loadPlainKeyResp, 0, sizeof(*loadPlainKeyResp)); + memset(req->key, 0xBB, WH_SHE_KEY_SZ); + req_size = sizeof(whMessageShe_LoadPlainKeyRequest) - 1; + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_LOAD_PLAIN_KEY, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*loadPlainKeyResp)); + WH_TEST_ASSERT_RETURN(loadPlainKeyResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 7: WH_SHE_EXPORT_RAM_KEY expects no request body. + * Send a nonzero req_size to trigger the check. + */ + { + whMessageShe_ExportRamKeyResponse* exportRamKeyResp = + (whMessageShe_ExportRamKeyResponse*)resp_packet; + memset(exportRamKeyResp, 0, sizeof(*exportRamKeyResp)); + req_size = 1; + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_EXPORT_RAM_KEY, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*exportRamKeyResp)); + WH_TEST_ASSERT_RETURN(exportRamKeyResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 8: WH_SHE_INIT_RND expects no request body. + * Send a nonzero req_size to trigger the check. + */ + { + whMessageShe_InitRngResponse* initRngResp = + (whMessageShe_InitRngResponse*)resp_packet; + memset(initRngResp, 0, sizeof(*initRngResp)); + req_size = 1; + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_INIT_RND, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*initRngResp)); + WH_TEST_ASSERT_RETURN(initRngResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 9: WH_SHE_RND expects no request body. + * Send a nonzero req_size to trigger the check. + */ + { + whMessageShe_RndResponse* rndResp = + (whMessageShe_RndResponse*)resp_packet; + memset(rndResp, 0, sizeof(*rndResp)); + req_size = 1; + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_RND, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*rndResp)); + WH_TEST_ASSERT_RETURN(rndResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 10: WH_SHE_EXTEND_SEED with truncated request. + * Fill valid entropy data, pass req_size one byte short. + */ + { + whMessageShe_ExtendSeedRequest* req = + (whMessageShe_ExtendSeedRequest*)req_packet; + whMessageShe_ExtendSeedResponse* extendSeedResp = + (whMessageShe_ExtendSeedResponse*)resp_packet; + memset(extendSeedResp, 0, sizeof(*extendSeedResp)); + memset(req->entropy, 0xCC, WH_SHE_KEY_SZ); + req_size = sizeof(whMessageShe_ExtendSeedRequest) - 1; + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_EXTEND_SEED, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*extendSeedResp)); + WH_TEST_ASSERT_RETURN(extendSeedResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 11: WH_SHE_ENC_ECB with valid header but truncated payload. + * Set sz to 16 (one AES block) but only include the header, no data. + */ + { + whMessageShe_EncEcbRequest* req = + (whMessageShe_EncEcbRequest*)req_packet; + whMessageShe_EncEcbResponse* encEcbResp = + (whMessageShe_EncEcbResponse*)resp_packet; + memset(encEcbResp, 0, sizeof(*encEcbResp)); + req->sz = 16; + req->keyId = WH_SHE_RAM_KEY_ID; + req_size = sizeof(whMessageShe_EncEcbRequest); + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_ENC_ECB, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*encEcbResp)); + WH_TEST_ASSERT_RETURN(encEcbResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 12: WH_SHE_ENC_ECB with truncated header. + * Pass req_size one byte short of the header struct. + */ + { + whMessageShe_EncEcbRequest* req = + (whMessageShe_EncEcbRequest*)req_packet; + whMessageShe_EncEcbResponse* encEcbResp = + (whMessageShe_EncEcbResponse*)resp_packet; + memset(encEcbResp, 0, sizeof(*encEcbResp)); + req->sz = 16; + req->keyId = WH_SHE_RAM_KEY_ID; + req_size = sizeof(whMessageShe_EncEcbRequest) - 1; + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_ENC_ECB, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*encEcbResp)); + WH_TEST_ASSERT_RETURN(encEcbResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 13: WH_SHE_ENC_CBC with valid header but truncated payload. + * Set sz to 16 (one AES block), fill a valid IV, but only include the + * header with no cipher data following it. + */ + { + whMessageShe_EncCbcRequest* req = + (whMessageShe_EncCbcRequest*)req_packet; + whMessageShe_EncCbcResponse* encCbcResp = + (whMessageShe_EncCbcResponse*)resp_packet; + memset(encCbcResp, 0, sizeof(*encCbcResp)); + req->sz = 16; + req->keyId = WH_SHE_RAM_KEY_ID; + memset(req->iv, 0xDD, WH_SHE_KEY_SZ); + req_size = sizeof(whMessageShe_EncCbcRequest); + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_ENC_CBC, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*encCbcResp)); + WH_TEST_ASSERT_RETURN(encCbcResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 14: WH_SHE_DEC_ECB with valid header but truncated payload. + */ + { + whMessageShe_DecEcbRequest* req = + (whMessageShe_DecEcbRequest*)req_packet; + whMessageShe_DecEcbResponse* decEcbResp = + (whMessageShe_DecEcbResponse*)resp_packet; + memset(decEcbResp, 0, sizeof(*decEcbResp)); + req->sz = 16; + req->keyId = WH_SHE_RAM_KEY_ID; + req_size = sizeof(whMessageShe_DecEcbRequest); + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_DEC_ECB, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*decEcbResp)); + WH_TEST_ASSERT_RETURN(decEcbResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 15: WH_SHE_DEC_CBC with valid header but truncated payload. + */ + { + whMessageShe_DecCbcRequest* req = + (whMessageShe_DecCbcRequest*)req_packet; + whMessageShe_DecCbcResponse* decCbcResp = + (whMessageShe_DecCbcResponse*)resp_packet; + memset(decCbcResp, 0, sizeof(*decCbcResp)); + req->sz = 16; + req->keyId = WH_SHE_RAM_KEY_ID; + memset(req->iv, 0xEE, WH_SHE_KEY_SZ); + req_size = sizeof(whMessageShe_DecCbcRequest); + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_DEC_CBC, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*decCbcResp)); + WH_TEST_ASSERT_RETURN(decCbcResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 16: WH_SHE_GEN_MAC with valid header but truncated payload. + * Set sz to 16 bytes of message data, but only pass the header. + */ + { + whMessageShe_GenMacRequest* req = + (whMessageShe_GenMacRequest*)req_packet; + whMessageShe_GenMacResponse* genMacResp = + (whMessageShe_GenMacResponse*)resp_packet; + memset(genMacResp, 0, sizeof(*genMacResp)); + req->keyId = WH_SHE_RAM_KEY_ID; + req->sz = 16; + req_size = sizeof(whMessageShe_GenMacRequest); + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_GEN_MAC, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*genMacResp)); + WH_TEST_ASSERT_RETURN(genMacResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* + * Test 17: WH_SHE_VERIFY_MAC with valid header but truncated payload. + * Set messageLen=16 and macLen=16 but only pass the header. + */ + { + whMessageShe_VerifyMacRequest* req = + (whMessageShe_VerifyMacRequest*)req_packet; + whMessageShe_VerifyMacResponse* verifyMacResp = + (whMessageShe_VerifyMacResponse*)resp_packet; + memset(verifyMacResp, 0, sizeof(*verifyMacResp)); + req->keyId = WH_SHE_RAM_KEY_ID; + req->messageLen = 16; + req->macLen = 16; + req_size = sizeof(whMessageShe_VerifyMacRequest); + ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE, + WH_SHE_VERIFY_MAC, req_size, + req_packet, &resp_size, resp_packet); + WH_TEST_ASSERT_RETURN(ret == 0); + WH_TEST_ASSERT_RETURN(resp_size == sizeof(*verifyMacResp)); + WH_TEST_ASSERT_RETURN(verifyMacResp->rc != WH_SHE_ERC_NO_ERROR); + } + + /* Restore a clean SHE context so the poked uidSet/sbState don't + * leak into the live request loop the server enters next. */ + memset(server->she, 0, sizeof(*server->she)); + + WH_TEST_PRINT("SHE req_size checking test SUCCESS\n"); + + return 0; +} + +#endif /* WOLFHSM_CFG_SHE_EXTENSION && !WOLFHSM_CFG_NO_CRYPTO */ diff --git a/test-refactor/wh_test_list.c b/test-refactor/wh_test_list.c index ac29019a9..9ecc769eb 100644 --- a/test-refactor/wh_test_list.c +++ b/test-refactor/wh_test_list.c @@ -36,6 +36,8 @@ WH_TEST_DECL(whTest_Comm); WH_TEST_DECL(whTest_Dma); WH_TEST_DECL(whTest_KeystoreReqSize); WH_TEST_DECL(whTest_CertVerify); +WH_TEST_DECL(whTest_SheMasterEcuKeyFallback); +WH_TEST_DECL(whTest_SheReqSizeChecking); WH_TEST_DECL(whTest_ClientCerts); WH_TEST_DECL(whTest_CryptoAes); WH_TEST_DECL(whTest_CryptoEcc256); @@ -43,6 +45,7 @@ WH_TEST_DECL(whTest_CryptoEd25519BufferTooSmall); WH_TEST_DECL(whTest_CryptoMlDsaBufferTooSmall); WH_TEST_DECL(whTest_CryptoRsaBufferTooSmall); WH_TEST_DECL(whTest_CryptoSha256); +WH_TEST_DECL(whTest_She); WH_TEST_DECL(whTest_Echo); WH_TEST_DECL(whTest_ServerInfo); WH_TEST_DECL(whTest_WolfCryptTest); @@ -56,6 +59,8 @@ const size_t whTestsMiscCount = sizeof(whTestsMisc) / sizeof(whTestsMisc[0]); const whTestCase whTestsServer[] = { { "whTest_CertVerify", whTest_CertVerify }, + { "whTest_SheMasterEcuKeyFallback", whTest_SheMasterEcuKeyFallback }, + { "whTest_SheReqSizeChecking", whTest_SheReqSizeChecking }, }; const size_t whTestsServerCount = sizeof(whTestsServer) / sizeof(whTestsServer[0]); @@ -68,6 +73,7 @@ const whTestCase whTestsClient[] = { { "whTest_CryptoMlDsaBufferTooSmall", whTest_CryptoMlDsaBufferTooSmall }, { "whTest_CryptoRsaBufferTooSmall", whTest_CryptoRsaBufferTooSmall }, { "whTest_CryptoSha256", whTest_CryptoSha256 }, + { "whTest_She", whTest_She }, { "whTest_Echo", whTest_Echo }, { "whTest_ServerInfo", whTest_ServerInfo }, { "whTest_WolfCryptTest", whTest_WolfCryptTest },