From 0f0072a33771c9993af575a1c0398c71048454ca Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Fri, 31 Oct 2025 11:06:31 -0600 Subject: [PATCH 1/2] Add key usage policies and enforcement for cryptographic operations --- benchmark/bench_modules/wh_bench_mod_aes.c | 20 +- benchmark/bench_modules/wh_bench_mod_cmac.c | 4 +- .../bench_modules/wh_bench_mod_curve25519.c | 10 +- benchmark/bench_modules/wh_bench_mod_ecc.c | 18 +- benchmark/bench_modules/wh_bench_mod_mldsa.c | 16 +- benchmark/bench_modules/wh_bench_mod_rsa.c | 12 +- examples/demo/client/wh_demo_client_crypto.c | 73 ++- .../demo/client/wh_demo_client_keystore.c | 5 +- examples/demo/client/wh_demo_client_keywrap.c | 3 +- examples/demo/client/wh_demo_client_secboot.c | 7 +- src/wh_client_crypto.c | 36 +- src/wh_client_keywrap.c | 2 +- src/wh_server_crypto.c | 273 ++++++++-- src/wh_server_keystore.c | 89 ++- test/wh_test_crypto.c | 513 +++++++++++++++++- test/wh_test_keywrap.c | 4 +- test/wh_test_multiclient.c | 43 +- wolfhsm/wh_common.h | 51 +- wolfhsm/wh_error.h | 2 + wolfhsm/wh_server_keystore.h | 41 ++ 20 files changed, 1033 insertions(+), 189 deletions(-) diff --git a/benchmark/bench_modules/wh_bench_mod_aes.c b/benchmark/bench_modules/wh_bench_mod_aes.c index a1814a30..cb6394e6 100644 --- a/benchmark/bench_modules/wh_bench_mod_aes.c +++ b/benchmark/bench_modules/wh_bench_mod_aes.c @@ -71,8 +71,8 @@ static int _benchAesCtr(whClientContext* client, whBenchOpContext* ctx, int id, } /* cache the key on the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, sizeof(keyLabel), - (uint8_t*)key, keyLen, &keyId); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + sizeof(keyLabel), (uint8_t*)key, keyLen, &keyId); if (ret != 0) { WH_BENCH_PRINTF("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -204,8 +204,8 @@ static int _benchAesEcb(whClientContext* client, whBenchOpContext* ctx, int id, } /* cache the key on the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, sizeof(keyLabel), - (uint8_t*)key, keyLen, &keyId); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + sizeof(keyLabel), (uint8_t*)key, keyLen, &keyId); if (ret != 0) { WH_BENCH_PRINTF("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -337,8 +337,8 @@ static int _benchAesCbc(whClientContext* client, whBenchOpContext* ctx, int id, } /* cache the key on the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, sizeof(keyLabel), - (uint8_t*)key, keyLen, &keyId); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + sizeof(keyLabel), (uint8_t*)key, keyLen, &keyId); if (ret != 0) { WH_BENCH_PRINTF("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -503,8 +503,8 @@ static int _benchAesGcmDma(whClientContext* client, whBenchOpContext* ctx, } /* cache the key on the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, sizeof(keyLabel), - (uint8_t*)key, keyLen, &keyId); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + sizeof(keyLabel), (uint8_t*)key, keyLen, &keyId); if (ret != 0) { WH_BENCH_PRINTF("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -635,8 +635,8 @@ static int _benchAesGcm(whClientContext* client, whBenchOpContext* ctx, int id, } /* cache the key on the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, sizeof(keyLabel), - (uint8_t*)key, keyLen, &keyId); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + sizeof(keyLabel), (uint8_t*)key, keyLen, &keyId); if (ret != 0) { WH_BENCH_PRINTF("Failed to wh_Client_KeyCache %d\n", ret); goto exit; diff --git a/benchmark/bench_modules/wh_bench_mod_cmac.c b/benchmark/bench_modules/wh_bench_mod_cmac.c index c0301854..0bbfa50e 100644 --- a/benchmark/bench_modules/wh_bench_mod_cmac.c +++ b/benchmark/bench_modules/wh_bench_mod_cmac.c @@ -55,8 +55,8 @@ int _benchCmacAes(whClientContext* client, whBenchOpContext* ctx, int id, uint8_t* out = NULL; /* cache the key on the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, sizeof(keyLabel), - (uint8_t*)key, keyLen, &keyId); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + sizeof(keyLabel), (uint8_t*)key, keyLen, &keyId); if (ret != 0) { WH_BENCH_PRINTF("Failed to wh_Client_KeyCache %d\n", ret); return ret; diff --git a/benchmark/bench_modules/wh_bench_mod_curve25519.c b/benchmark/bench_modules/wh_bench_mod_curve25519.c index 230a396e..954ac2a4 100644 --- a/benchmark/bench_modules/wh_bench_mod_curve25519.c +++ b/benchmark/bench_modules/wh_bench_mod_curve25519.c @@ -141,16 +141,18 @@ int wh_Bench_Mod_Curve25519SharedSecret(whClientContext* client, char keyLabel[] = "bench-key"; /* Cache Alice's key in the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, strlen(keyLabel), - key1_der, sizeof(key1_der), &keyIdAlice); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + strlen(keyLabel), key1_der, sizeof(key1_der), + &keyIdAlice); if (ret != 0) { WH_BENCH_PRINTF("Failed to cache Alice's key %d\n", ret); return ret; } /* Cache Bob's key in the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, strlen(keyLabel), - key2_der, sizeof(key2_der), &keyIdBob); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + strlen(keyLabel), key2_der, sizeof(key2_der), + &keyIdBob); if (ret != 0) { WH_BENCH_PRINTF("Failed to cache Bob's key %d\n", ret); wh_Client_KeyEvict(client, keyIdAlice); diff --git a/benchmark/bench_modules/wh_bench_mod_ecc.c b/benchmark/bench_modules/wh_bench_mod_ecc.c index fad069d1..a944e218 100644 --- a/benchmark/bench_modules/wh_bench_mod_ecc.c +++ b/benchmark/bench_modules/wh_bench_mod_ecc.c @@ -86,8 +86,8 @@ int _benchEccSign(whClientContext* client, whBenchOpContext* ctx, int id, initialized_rng = 1; /* Cache key in the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, strlen(keyLabel), - (uint8_t*)key, keyLen, &keyId); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + strlen(keyLabel), (uint8_t*)key, keyLen, &keyId); if (ret != 0) { WH_BENCH_PRINTF("Failed to cache key %d\n", ret); goto exit; @@ -197,8 +197,8 @@ int _benchEccVerify(whClientContext* client, whBenchOpContext* ctx, int id, initialized_rng = 1; /* Cache the key in the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, strlen(keyLabel), - (uint8_t*)key, keyLen, &keyId); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + strlen(keyLabel), (uint8_t*)key, keyLen, &keyId); if (ret != 0) { WH_BENCH_PRINTF("Failed to cache key %d\n", ret); goto exit; @@ -392,16 +392,18 @@ int _benchEccEcdh(whClientContext* client, whBenchOpContext* ctx, int id, initialized_rng = 1; /* Cache Alice's key in the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, strlen(keyLabel), - (uint8_t*)aliceKeyData, aliceKeyLen, &keyIdAlice); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + strlen(keyLabel), (uint8_t*)aliceKeyData, + aliceKeyLen, &keyIdAlice); if (ret != 0) { WH_BENCH_PRINTF("Failed to cache Alice's key %d\n", ret); goto exit; } /* Cache Bob's key in the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, strlen(keyLabel), - (uint8_t*)bobKeyData, bobKeyLen, &keyIdBob); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + strlen(keyLabel), (uint8_t*)bobKeyData, bobKeyLen, + &keyIdBob); if (ret != 0) { WH_BENCH_PRINTF("Failed to cache Bob's key %d\n", ret); goto exit; diff --git a/benchmark/bench_modules/wh_bench_mod_mldsa.c b/benchmark/bench_modules/wh_bench_mod_mldsa.c index c1e31f64..09205c04 100644 --- a/benchmark/bench_modules/wh_bench_mod_mldsa.c +++ b/benchmark/bench_modules/wh_bench_mod_mldsa.c @@ -664,14 +664,16 @@ static int _benchMlDsaSign(whClientContext* client, whBenchOpContext* ctx, /* Import key to the HSM */ #if defined(WOLFHSM_CFG_DMA) if (devId == WH_DEV_ID_DMA) { - ret = wh_Client_MlDsaImportKeyDma(client, &key, &keyId, 0, + ret = wh_Client_MlDsaImportKeyDma(client, &key, &keyId, + WH_NVM_FLAGS_USAGE_ANY, strlen(keyLabel), (uint8_t*)keyLabel); } else #endif /* !(WOLFHSM_CFG_DMA) */ { - ret = wh_Client_MlDsaImportKey(client, &key, &keyId, 0, - strlen(keyLabel), (uint8_t*)keyLabel); + ret = wh_Client_MlDsaImportKey(client, &key, &keyId, + WH_NVM_FLAGS_USAGE_ANY, strlen(keyLabel), + (uint8_t*)keyLabel); } if (ret != 0) { WH_BENCH_PRINTF("Failed to cache key %d\n", ret); @@ -812,14 +814,16 @@ static int _benchMlDsaVerify(whClientContext* client, whBenchOpContext* ctx, /* Import key to the HSM */ #if defined(WOLFHSM_CFG_DMA) if (devId == WH_DEV_ID_DMA) { - ret = wh_Client_MlDsaImportKeyDma(client, &key, &keyId, 0, + ret = wh_Client_MlDsaImportKeyDma(client, &key, &keyId, + WH_NVM_FLAGS_USAGE_ANY, strlen(keyLabel), (uint8_t*)keyLabel); } else #endif /* !(WOLFHSM_CFG_DMA) */ { - ret = wh_Client_MlDsaImportKey(client, &key, &keyId, 0, - strlen(keyLabel), (uint8_t*)keyLabel); + ret = wh_Client_MlDsaImportKey(client, &key, &keyId, + WH_NVM_FLAGS_USAGE_ANY, strlen(keyLabel), + (uint8_t*)keyLabel); } if (ret != 0) { WH_BENCH_PRINTF("Failed to cache key %d\n", ret); diff --git a/benchmark/bench_modules/wh_bench_mod_rsa.c b/benchmark/bench_modules/wh_bench_mod_rsa.c index bf759f48..0e1433a6 100644 --- a/benchmark/bench_modules/wh_bench_mod_rsa.c +++ b/benchmark/bench_modules/wh_bench_mod_rsa.c @@ -361,8 +361,8 @@ int _benchRsaCrypt(whClientContext* client, whBenchOpContext* ctx, int id, initialized_rng = 1; /* Cache the RSA key in the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, strlen(keyLabel), - (uint8_t*)key, keyLen, &keyId); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + strlen(keyLabel), (uint8_t*)key, keyLen, &keyId); if (ret != 0) { WH_BENCH_PRINTF("Failed to cache RSA key %d\n", ret); goto exit; @@ -493,8 +493,8 @@ int _benchRsaVerify(whClientContext* client, whBenchOpContext* ctx, int id, initialized_rng = 1; /* Cache the RSA key in the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, strlen(keyLabel), - (uint8_t*)key, keyLen, &keyId); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + strlen(keyLabel), (uint8_t*)key, keyLen, &keyId); if (ret != 0) { WH_BENCH_PRINTF("Failed to cache RSA key %d\n", ret); goto exit; @@ -615,8 +615,8 @@ int _benchRsaSign(whClientContext* client, whBenchOpContext* ctx, int id, initialized_rng = 1; /* Cache the RSA key in the HSM */ - ret = wh_Client_KeyCache(client, 0, (uint8_t*)keyLabel, strlen(keyLabel), - (uint8_t*)key, keyLen, &keyId); + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ANY, (uint8_t*)keyLabel, + strlen(keyLabel), (uint8_t*)key, keyLen, &keyId); if (ret != 0) { WH_BENCH_PRINTF("Failed to cache RSA key %d\n", ret); goto exit; diff --git a/examples/demo/client/wh_demo_client_crypto.c b/examples/demo/client/wh_demo_client_crypto.c index c21d0d10..bec7ae86 100644 --- a/examples/demo/client/wh_demo_client_crypto.c +++ b/examples/demo/client/wh_demo_client_crypto.c @@ -170,8 +170,9 @@ int wh_DemoClient_CryptoRsaImport(whClientContext* clientContext) close(keyFd); /* cache the key in the HSM, get HSM assigned keyId */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - strlen(keyLabel), keyBuf, keySz, &keyId); + ret = wh_Client_KeyCache( + clientContext, WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT, + (uint8_t*)keyLabel, strlen(keyLabel), keyBuf, keySz, &keyId); if (ret != 0) { printf("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -359,8 +360,9 @@ int wh_DemoClient_CryptoCurve25519Import(whClientContext* clientContext) /* cache the key in the HSM, get HSM assigned keyId */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - strlen(keyLabel), keyBuf, keySz, &keyIdBob); + ret = wh_Client_KeyCache(clientContext, WH_NVM_FLAGS_USAGE_DERIVE, + (uint8_t*)keyLabel, strlen(keyLabel), keyBuf, + keySz, &keyIdBob); if (ret != 0) { printf("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -394,8 +396,9 @@ int wh_DemoClient_CryptoCurve25519Import(whClientContext* clientContext) close(keyFd); /* cache the key in the HSM, get HSM assigned keyId */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - strlen(keyLabel), keyBuf, keySz, &keyIdAlice); + ret = wh_Client_KeyCache(clientContext, WH_NVM_FLAGS_USAGE_DERIVE, + (uint8_t*)keyLabel, strlen(keyLabel), keyBuf, + keySz, &keyIdAlice); if (ret != 0) { printf("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -649,8 +652,11 @@ int wh_DemoClient_CryptoEccImport(whClientContext* clientContext) close(keyFd); /* Cache the key in the HSM, get HSM assigned keyId. From here on out, the * keys are stored in the HSM and can be referred to by keyId */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - strlen(keyLabel), keyBuf, keySz, &keyIdAlice); + ret = wh_Client_KeyCache( + clientContext, + WH_NVM_FLAGS_USAGE_DERIVE | WH_NVM_FLAGS_USAGE_SIGN | + WH_NVM_FLAGS_USAGE_VERIFY, + (uint8_t*)keyLabel, strlen(keyLabel), keyBuf, keySz, &keyIdAlice); if (ret != 0) { printf("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -694,8 +700,11 @@ int wh_DemoClient_CryptoEccImport(whClientContext* clientContext) } close(keyFd); /* Cache the key in the HSM, get HSM assigned keyId */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - strlen(keyLabel), keyBuf, keySz, &keyIdBob); + ret = wh_Client_KeyCache( + clientContext, + WH_NVM_FLAGS_USAGE_DERIVE | WH_NVM_FLAGS_USAGE_SIGN | + WH_NVM_FLAGS_USAGE_VERIFY, + (uint8_t*)keyLabel, strlen(keyLabel), keyBuf, keySz, &keyIdBob); if (ret != 0) { printf("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -902,8 +911,9 @@ int wh_DemoClient_CryptoAesCbcImport(whClientContext* clientContext) } /* cache the key on the HSM */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - sizeof(keyLabel), key, sizeof(key), &keyId); + ret = wh_Client_KeyCache( + clientContext, WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT, + (uint8_t*)keyLabel, sizeof(keyLabel), key, sizeof(key), &keyId); if (ret != 0) { printf("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -1050,8 +1060,9 @@ int wh_DemoClient_CryptoAesGcmImport(whClientContext* clientContext) } /* cache the key on the HSM */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - sizeof(keyLabel), key, sizeof(key), &keyId); + ret = wh_Client_KeyCache( + clientContext, WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT, + (uint8_t*)keyLabel, sizeof(keyLabel), key, sizeof(key), &keyId); if (ret != 0) { printf("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -1193,8 +1204,9 @@ int wh_DemoClient_CryptoCmacImport(whClientContext* clientContext) } /* cache the key on the HSM */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - sizeof(keyLabel), key, sizeof(key), &keyId); + ret = wh_Client_KeyCache( + clientContext, WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY, + (uint8_t*)keyLabel, sizeof(keyLabel), key, sizeof(key), &keyId); if (ret != 0) { printf("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -1235,8 +1247,9 @@ int wh_DemoClient_CryptoCmacImport(whClientContext* clientContext) /* cache the key on the HSM. This is required because the key is evicted * after the non-DMA CMAC operation is finalized */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - sizeof(keyLabel), key, sizeof(key), &keyId); + ret = wh_Client_KeyCache( + clientContext, WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY, + (uint8_t*)keyLabel, sizeof(keyLabel), key, sizeof(key), &keyId); if (ret != 0) { printf("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -1289,8 +1302,9 @@ int wh_DemoClient_CryptoCmacOneshotImport(whClientContext* clientContext) } /* cache the key on the HSM */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - sizeof(keyLabel), key, sizeof(key), &keyId); + ret = wh_Client_KeyCache( + clientContext, WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY, + (uint8_t*)keyLabel, sizeof(keyLabel), key, sizeof(key), &keyId); if (ret != 0) { printf("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -1327,8 +1341,9 @@ int wh_DemoClient_CryptoCmacOneshotImport(whClientContext* clientContext) /* cache the key on the HSM again, cmac keys are evicted after non-DMA CMAC * operations are finalized is called */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - sizeof(keyLabel), key, sizeof(key), &keyId); + ret = wh_Client_KeyCache( + clientContext, WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY, + (uint8_t*)keyLabel, sizeof(keyLabel), key, sizeof(key), &keyId); if (ret != 0) { printf("Failed to wh_Client_KeyCache %d\n", ret); goto exit; @@ -1419,7 +1434,7 @@ int wh_DemoClient_CryptoHkdfCache(whClientContext* clientContext) const uint32_t outSz = 32; /* arbitrary output size */ /* Metadata flags/label for the cached key. Adjust to your requirements. */ - whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE; + whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE | WH_NVM_FLAGS_USAGE_DERIVE; char label[] = "hkdf-derived key"; /* Request the HSM to derive HKDF output and store it in the key cache. @@ -1467,9 +1482,9 @@ int wh_DemoClient_CryptoHkdfCacheInputKey(whClientContext* clientContext) byte okm[32]; /* Output key material */ /* First, cache the input key material in the HSM */ - ret = wh_Client_KeyCache(clientContext, 0, (uint8_t*)keyLabel, - (uint32_t)strlen(keyLabel), ikm, - (uint32_t)sizeof(ikm), &keyIdIn); + ret = wh_Client_KeyCache(clientContext, WH_NVM_FLAGS_USAGE_DERIVE, + (uint8_t*)keyLabel, (uint32_t)strlen(keyLabel), + ikm, (uint32_t)sizeof(ikm), &keyIdIn); if (ret != WH_ERROR_OK) { printf("Failed to wh_Client_KeyCache %d\n", ret); return ret; @@ -1565,7 +1580,7 @@ int wh_DemoClient_CryptoCmacKdfCache(whClientContext* clientContext) { int ret = 0; whKeyId keyId = WH_KEYID_ERASED; - whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE; + whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE | WH_NVM_FLAGS_USAGE_DERIVE; const char label[] = "cmac-kdf cache"; ret = wh_Client_CmacKdfMakeCacheKey( @@ -1609,7 +1624,7 @@ int wh_DemoClient_CryptoCmacKdfCacheInputs(whClientContext* clientContext) * as the salt is usually not sensitive information. If it is desired to use * HSM-only information as salt, then this would likely be provisioned * as an offline step */ - ret = wh_Client_KeyCache(clientContext, WH_NVM_FLAGS_NONE, NULL, 0, + ret = wh_Client_KeyCache(clientContext, WH_NVM_FLAGS_USAGE_DERIVE, NULL, 0, demoCmacKdfSalt, (uint32_t)sizeof(demoCmacKdfSalt), &saltKeyId); if (ret != WH_ERROR_OK) { @@ -1619,7 +1634,7 @@ int wh_DemoClient_CryptoCmacKdfCacheInputs(whClientContext* clientContext) /* Cache the Z input. This would typically be done offline during the HSM * provisioning step, but is shown here for completeness */ - ret = wh_Client_KeyCache(clientContext, WH_NVM_FLAGS_NONE, NULL, 0, + ret = wh_Client_KeyCache(clientContext, WH_NVM_FLAGS_USAGE_DERIVE, NULL, 0, demoCmacKdfZ, (uint32_t)sizeof(demoCmacKdfZ), &zKeyId); if (ret != WH_ERROR_OK) { diff --git a/examples/demo/client/wh_demo_client_keystore.c b/examples/demo/client/wh_demo_client_keystore.c index b09eae82..523d0f08 100644 --- a/examples/demo/client/wh_demo_client_keystore.c +++ b/examples/demo/client/wh_demo_client_keystore.c @@ -139,8 +139,9 @@ int wh_DemoClient_KeystoreAes(whClientContext* clientContext) uint16_t keyId = WH_KEYID_ERASED; /* Cache the AES key in the HSM */ - ret = wh_Client_KeyCache(clientContext, 0, label, sizeof(label), key, - sizeof(key), &keyId); + ret = wh_Client_KeyCache( + clientContext, WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT, + label, sizeof(label), key, sizeof(key), &keyId); if (ret != 0) { printf("Failed to cache key: %d\n", ret); return ret; diff --git a/examples/demo/client/wh_demo_client_keywrap.c b/examples/demo/client/wh_demo_client_keywrap.c index 79378c6b..04ca899d 100644 --- a/examples/demo/client/wh_demo_client_keywrap.c +++ b/examples/demo/client/wh_demo_client_keywrap.c @@ -44,7 +44,7 @@ static int _InitServerKek(whClientContext* ctx) * provisioning. Uploading the KEK via the client is for testing purposes * only and not intended as a recommendation */ whKeyId serverKeyId = WH_DEMO_KEYWRAP_KEKID; - whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE; + whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE | WH_NVM_FLAGS_USAGE_WRAP; uint8_t label[WH_NVM_LABEL_LEN] = "Server KEK key"; uint8_t kek[] = {0x03, 0x03, 0x0d, 0xd9, 0xeb, 0x18, 0x17, 0x2e, 0x06, 0x6e, 0x19, 0xce, 0x98, 0x44, 0x54, 0x0d, @@ -84,6 +84,7 @@ int wh_DemoClient_AesGcmKeyWrap(whClientContext* client) client->comm->client_id, WH_DEMO_KEYWRAP_AESGCM_WRAPKEY_ID), .label = "AES Key Label", .access = WH_NVM_ACCESS_ANY, + .flags = WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT, .len = WH_DEMO_KEYWRAP_AES_KEYSIZE}; whNvmMetadata exportedMetadata; uint8_t wrappedKey[WH_DEMO_KEYWRAP_AES_WRAPPED_KEYSIZE]; diff --git a/examples/demo/client/wh_demo_client_secboot.c b/examples/demo/client/wh_demo_client_secboot.c index 113200d6..506d6c46 100644 --- a/examples/demo/client/wh_demo_client_secboot.c +++ b/examples/demo/client/wh_demo_client_secboot.c @@ -107,8 +107,9 @@ static int _provisionMakeCommitKey(whClientContext* clientContext) memcpy(keyLabel, prov_keyLabel, sizeof(prov_keyLabel)); ret = wh_Client_EccMakeCacheKey(clientContext, 32, ECC_CURVE_DEF, &keyId, - WH_NVM_FLAGS_NONE, sizeof(prov_keyLabel), - keyLabel); + WH_NVM_FLAGS_USAGE_SIGN | + WH_NVM_FLAGS_USAGE_VERIFY, + sizeof(prov_keyLabel), keyLabel); if (ret == WH_ERROR_OK) { ret = wh_Client_KeyCommit(clientContext, prov_keyId); } @@ -333,4 +334,4 @@ int wh_DemoClient_SecBoot_Zeroize(whClientContext* clientContext) return ret; } -#endif /* !WOLFHSM_CFG_NO_CRYPTO */ \ No newline at end of file +#endif /* !WOLFHSM_CFG_NO_CRYPTO */ diff --git a/src/wh_client_crypto.c b/src/wh_client_crypto.c index d83aef2b..457701c0 100644 --- a/src/wh_client_crypto.c +++ b/src/wh_client_crypto.c @@ -1317,7 +1317,7 @@ int wh_Client_EccSharedSecret(whClientContext* ctx, ecc_key* priv_key, if ((ret == WH_ERROR_OK) && WH_KEYID_ISERASED(pub_key_id)) { /* Must import the key to the server and evict it afterwards */ uint8_t keyLabel[] = "TempEccDh-pub"; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + whNvmFlags flags = WH_NVM_FLAGS_USAGE_DERIVE; ret = wh_Client_EccImportKey(ctx, pub_key, &pub_key_id, flags, sizeof(keyLabel), keyLabel); @@ -1330,7 +1330,7 @@ int wh_Client_EccSharedSecret(whClientContext* ctx, ecc_key* priv_key, if ((ret == WH_ERROR_OK) && WH_KEYID_ISERASED(prv_key_id)) { /* Must import the key to the server and evict it afterwards */ uint8_t keyLabel[] = "TempEccDh-prv"; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + whNvmFlags flags = WH_NVM_FLAGS_USAGE_DERIVE; ret = wh_Client_EccImportKey(ctx, priv_key, &prv_key_id, flags, sizeof(keyLabel), keyLabel); @@ -1469,7 +1469,7 @@ int wh_Client_EccSign(whClientContext* ctx, ecc_key* key, const uint8_t* hash, if (WH_KEYID_ISERASED(key_id)) { /* Must import the key to the server and evict it afterwards */ uint8_t keyLabel[] = "TempEccSign"; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + whNvmFlags flags = WH_NVM_FLAGS_USAGE_SIGN; ret = wh_Client_EccImportKey(ctx, key, &key_id, flags, sizeof(keyLabel), keyLabel); @@ -1618,7 +1618,7 @@ int wh_Client_EccVerify(whClientContext* ctx, ecc_key* key, const uint8_t* sig, if (WH_KEYID_ISERASED(key_id)) { /* Must import the key to the server and evict it afterwards */ uint8_t keyLabel[] = "TempEccVerify"; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + whNvmFlags flags = WH_NVM_FLAGS_USAGE_VERIFY; ret = wh_Client_EccImportKey(ctx, key, &key_id, flags, sizeof(keyLabel), keyLabel); @@ -2018,7 +2018,7 @@ int wh_Client_Curve25519SharedSecret(whClientContext* ctx, if ((ret == WH_ERROR_OK) && WH_KEYID_ISERASED(pub_key_id)) { /* Must import the key to the server and evict it afterwards */ uint8_t keyLabel[] = "TempX25519-pub"; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + whNvmFlags flags = WH_NVM_FLAGS_USAGE_DERIVE; ret = wh_Client_Curve25519ImportKey(ctx, pub_key, &pub_key_id, flags, sizeof(keyLabel), keyLabel); @@ -2031,7 +2031,7 @@ int wh_Client_Curve25519SharedSecret(whClientContext* ctx, if ((ret == WH_ERROR_OK) && WH_KEYID_ISERASED(prv_key_id)) { /* Must import the key to the server and evict it afterwards */ uint8_t keyLabel[] = "TempX25519-prv"; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + whNvmFlags flags = WH_NVM_FLAGS_USAGE_DERIVE; ret = wh_Client_Curve25519ImportKey(ctx, priv_key, &prv_key_id, flags, sizeof(keyLabel), keyLabel); @@ -2389,7 +2389,21 @@ int wh_Client_RsaFunction(whClientContext* ctx, RsaKey* key, int rsa_type, if (WH_KEYID_ISERASED(key_id)) { /* Must import the key to the server and evict it afterwards */ uint8_t keyLabel[] = "TempRsaFunction"; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + /* Set usage flags based on requested RSA operation */ + whNvmFlags flags = WH_NVM_FLAGS_NONE; + switch (rsa_type) { + case RSA_PUBLIC_ENCRYPT: + case RSA_PRIVATE_ENCRYPT: + flags = WH_NVM_FLAGS_USAGE_ENCRYPT; + break; + case RSA_PUBLIC_DECRYPT: + case RSA_PRIVATE_DECRYPT: + flags = WH_NVM_FLAGS_USAGE_DECRYPT; + break; + default: + flags = WH_NVM_FLAGS_ANY; + break; + } ret = wh_Client_RsaImportKey(ctx, key, &key_id, flags, sizeof(keyLabel), keyLabel); @@ -4728,7 +4742,7 @@ int wh_Client_MlDsaSign(whClientContext* ctx, const byte* in, word32 in_len, if (WH_KEYID_ISERASED(key_id)) { /* Must import the key to the server and evict it afterwards */ uint8_t keyLabel[] = "TempMlDsaSign"; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + whNvmFlags flags = WH_NVM_FLAGS_USAGE_SIGN; ret = wh_Client_MlDsaImportKey(ctx, key, &key_id, flags, sizeof(keyLabel), keyLabel); @@ -4859,7 +4873,7 @@ int wh_Client_MlDsaVerify(whClientContext* ctx, const byte* sig, word32 sig_len, if (WH_KEYID_ISERASED(key_id)) { /* Must import the key to the server and evict it afterwards */ uint8_t keyLabel[] = "TempMlDsaVerify"; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + whNvmFlags flags = WH_NVM_FLAGS_USAGE_VERIFY; ret = wh_Client_MlDsaImportKey(ctx, key, &key_id, flags, sizeof(keyLabel), keyLabel); @@ -5177,7 +5191,7 @@ int wh_Client_MlDsaSignDma(whClientContext* ctx, const byte* in, word32 in_len, if (WH_KEYID_ISERASED(key_id)) { /* Must import the key to the server and evict it afterwards */ uint8_t keyLabel[] = "TempMlDsaSign"; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + whNvmFlags flags = WH_NVM_FLAGS_USAGE_SIGN; ret = wh_Client_MlDsaImportKeyDma(ctx, key, &key_id, flags, sizeof(keyLabel), keyLabel); @@ -5316,7 +5330,7 @@ int wh_Client_MlDsaVerifyDma(whClientContext* ctx, const byte* sig, if (WH_KEYID_ISERASED(key_id)) { /* Must import the key to the server and evict it afterwards */ uint8_t keyLabel[] = "TempMlDsaVerify"; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + whNvmFlags flags = WH_NVM_FLAGS_USAGE_VERIFY; ret = wh_Client_MlDsaImportKeyDma(ctx, key, &key_id, flags, sizeof(keyLabel), keyLabel); diff --git a/src/wh_client_keywrap.c b/src/wh_client_keywrap.c index 84dbe5ce..2a22625c 100644 --- a/src/wh_client_keywrap.c +++ b/src/wh_client_keywrap.c @@ -78,7 +78,7 @@ int wh_Client_KeyWrapResponse(whClientContext* ctx, if (group != WH_MESSAGE_GROUP_KEY || action != WH_KEY_KEYWRAP || size < sizeof(*resp) || size > sizeof(*resp) + wrappedKeySz || - resp->wrappedKeySz != wrappedKeySz || resp->cipherType != cipherType) { + resp->cipherType != cipherType) { return WH_ERROR_ABORTED; } diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c index 584009b7..44877714 100644 --- a/src/wh_server_crypto.c +++ b/src/wh_server_crypto.c @@ -417,6 +417,46 @@ static int _HandleRsaFunction( whServerContext* ctx, uint16_t magic, return BAD_FUNC_ARG; } + /* Validate key usage policy based on RSA operation type */ + if (!WH_KEYID_ISERASED(key_id)) { + whNvmFlags requiredUsage = WH_NVM_FLAGS_NONE; + switch (op_type) { + case RSA_PUBLIC_ENCRYPT: + case RSA_PRIVATE_ENCRYPT: + requiredUsage = WH_NVM_FLAGS_USAGE_ENCRYPT; + break; + case RSA_PUBLIC_DECRYPT: + case RSA_PRIVATE_DECRYPT: + requiredUsage = WH_NVM_FLAGS_USAGE_DECRYPT; + break; + } + ret = wh_Server_KeystoreFindEnforceKeyUsage(ctx, key_id, requiredUsage); + if (ret != WH_ERROR_OK) { + /* Currently wolfCrypt doesn't have a way for crypto callbacks to + distinguish if a low level RSA operation (like encrypt/decrypt) is + being performed as part of a higher level operation like + sign/verify. Until that information is propagated to the + callback, the usage flags are treated as equivalent. */ + if (ret == WH_ERROR_USAGE) { + if (op_type == RSA_PUBLIC_DECRYPT) { + /* Decrypt usage flag wasn't set so this might be a verify + * operation. Attempt to enforce against the verify flag */ + ret = wh_Server_KeystoreFindEnforceKeyUsage( + ctx, key_id, WH_NVM_FLAGS_USAGE_VERIFY); + } + else if (op_type == RSA_PRIVATE_ENCRYPT) { + /* Encrypt usage flag wasn't set so this might be a sign + * operation. Attempt to enforce against the sign flag */ + ret = wh_Server_KeystoreFindEnforceKeyUsage( + ctx, key_id, WH_NVM_FLAGS_USAGE_SIGN); + } + } + if (ret != WH_ERROR_OK) { + return ret; + } + } + } + /* init rsa key */ ret = wc_InitRsaKey_ex(rsa, NULL, ctx->crypto->devId); /* load the key from the keystore */ @@ -847,6 +887,15 @@ static int _HandleEccSharedSecret(whServerContext* ctx, uint16_t magic, whKeyId prv_key_id = wh_KeyId_TranslateFromClient( WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.privateKeyId); + /* Validate key usage policy for key derivation (private key) */ + if (!WH_KEYID_ISERASED(prv_key_id)) { + ret = wh_Server_KeystoreFindEnforceKeyUsage(ctx, prv_key_id, + WH_NVM_FLAGS_USAGE_DERIVE); + if (ret != WH_ERROR_OK) { + return ret; + } + } + /* Response message */ byte* res_out = (byte*)cryptoDataOut + sizeof(whMessageCrypto_EcdhResponse); @@ -926,6 +975,15 @@ static int _HandleEccSign(whServerContext* ctx, uint16_t magic, uint32_t options = req.options; int evict = !!(options & WH_MESSAGE_CRYPTO_ECCSIGN_OPTIONS_EVICT); + /* Validate key usage policy for signing */ + if (!WH_KEYID_ISERASED(key_id)) { + ret = wh_Server_KeystoreFindEnforceKeyUsage(ctx, key_id, + WH_NVM_FLAGS_USAGE_SIGN); + if (ret != WH_ERROR_OK) { + return ret; + } + } + /* Response message */ byte* res_out = (byte*)cryptoDataOut + sizeof(whMessageCrypto_EccSignResponse); @@ -1003,6 +1061,15 @@ static int _HandleEccVerify(whServerContext* ctx, uint16_t magic, int export_pub_key = !!(options & WH_MESSAGE_CRYPTO_ECCVERIFY_OPTIONS_EXPORTPUB); + /* Validate key usage policy for verification */ + if (!WH_KEYID_ISERASED(key_id)) { + ret = wh_Server_KeystoreFindEnforceKeyUsage(ctx, key_id, + WH_NVM_FLAGS_USAGE_VERIFY); + if (ret != WH_ERROR_OK) { + return ret; + } + } + /* Response message */ byte* res_pub = (uint8_t*)(cryptoDataOut) + sizeof(whMessageCrypto_EccVerifyResponse); @@ -1279,6 +1346,12 @@ static int _HandleHkdf(whServerContext* ctx, uint16_t magic, if (ret != WH_ERROR_OK) { return ret; } + /* Validate key usage policy for key derivation (input key) */ + ret = wh_Server_KeystoreEnforceKeyUsage(cachedKeyMeta, + WH_NVM_FLAGS_USAGE_DERIVE); + if (ret != WH_ERROR_OK) { + return ret; + } /* Update inKey pointer and size to use cached key */ inKey = cachedKeyBuf; inKeySz = cachedKeyMeta->len; @@ -1405,6 +1478,12 @@ static int _HandleCmacKdf(whServerContext* ctx, uint16_t magic, if (ret != WH_ERROR_OK) { return ret; } + /* Validate key usage policy for cached salt */ + ret = wh_Server_KeystoreEnforceKeyUsage(cachedSaltMeta, + WH_NVM_FLAGS_USAGE_DERIVE); + if (ret != WH_ERROR_OK) { + return ret; + } salt = cachedSaltBuf; saltSz = cachedSaltMeta->len; } @@ -1418,6 +1497,12 @@ static int _HandleCmacKdf(whServerContext* ctx, uint16_t magic, if (ret != WH_ERROR_OK) { return ret; } + /* Validate key usage policy for key derivation (Z key) */ + ret = wh_Server_KeystoreEnforceKeyUsage(cachedZMeta, + WH_NVM_FLAGS_USAGE_DERIVE); + if (ret != WH_ERROR_OK) { + return ret; + } z = cachedZBuf; zSz = cachedZMeta->len; } @@ -1596,6 +1681,15 @@ static int _HandleCurve25519SharedSecret(whServerContext* ctx, uint16_t magic, WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.privateKeyId); int endian = req.endian; + /* Validate key usage policy for key derivation (private key) */ + if (!WH_KEYID_ISERASED(prv_key_id)) { + ret = wh_Server_KeystoreFindEnforceKeyUsage(ctx, prv_key_id, + WH_NVM_FLAGS_USAGE_DERIVE); + if (ret != WH_ERROR_OK) { + return ret; + } + } + /* Response message */ uint8_t* res_out = (uint8_t*)cryptoDataOut + sizeof(whMessageCrypto_Curve25519Response); @@ -1661,8 +1755,8 @@ static int _HandleAesCtr(whServerContext* ctx, uint16_t magic, Aes aes[1] = {0}; whMessageCrypto_AesCtrRequest req; whMessageCrypto_AesCtrResponse res; - uint8_t read_key[AES_MAX_KEY_SIZE]; - uint32_t read_key_len = sizeof(read_key); + uint8_t* cachedKey = NULL; + whNvmMetadata* keyMeta = NULL; /* Translate request */ ret = wh_MessageCrypto_TranslateAesCtrRequest( magic, (const whMessageCrypto_AesCtrRequest*)cryptoDataIn, &req); @@ -1680,6 +1774,7 @@ static int _HandleAesCtr(whServerContext* ctx, uint16_t magic, } whKeyId key_id = wh_KeyId_TranslateFromClient( WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId); + /* in, key, iv, and out are after fixed size fields */ uint8_t* in = (uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_AesCtrRequest); @@ -1697,14 +1792,19 @@ static int _HandleAesCtr(whServerContext* ctx, uint16_t magic, wh_Utils_Hexdump("[AesCtr] IV ", iv, AES_BLOCK_SIZE); wh_Utils_Hexdump("[AesCtr] tmp ", tmp, AES_BLOCK_SIZE); #endif - /* Read the key if it is not erased */ + /* Freshen key and validate usage policy if key is not erased */ if (!WH_KEYID_ISERASED(key_id)) { - ret = wh_Server_KeystoreReadKey(ctx, key_id, NULL, read_key, - &read_key_len); + ret = wh_Server_KeystoreFreshenKey(ctx, key_id, &cachedKey, &keyMeta); + if (ret == 0) { + /* Validate key usage policy */ + ret = wh_Server_KeystoreEnforceKeyUsage( + keyMeta, enc != 0 ? WH_NVM_FLAGS_USAGE_ENCRYPT + : WH_NVM_FLAGS_USAGE_DECRYPT); + } if (ret == 0) { - /* override the incoming values */ - key = read_key; - key_len = read_key_len; + /* override the incoming values with cached key */ + key = cachedKey; + key_len = keyMeta->len; #ifdef DEBUG_CRYPTOCB_VERBOSE wh_Utils_Hexdump("[AesCtr] Key from HSM", key, key_len); #endif @@ -1776,8 +1876,8 @@ static int _HandleAesEcb(whServerContext* ctx, uint16_t magic, Aes aes[1] = {0}; whMessageCrypto_AesEcbRequest req; whMessageCrypto_AesEcbResponse res; - uint8_t read_key[AES_MAX_KEY_SIZE]; - uint32_t read_key_len = sizeof(read_key); + uint8_t* cachedKey = NULL; + whNvmMetadata* keyMeta = NULL; /* Translate request */ ret = wh_MessageCrypto_TranslateAesEcbRequest( @@ -1812,14 +1912,19 @@ static int _HandleAesEcb(whServerContext* ctx, uint16_t magic, wh_Utils_Hexdump("[AesEcb] Input data", in, len); wh_Utils_Hexdump("[AesEcb] IV", iv, AES_BLOCK_SIZE); #endif - /* Read the key if it is not erased */ + /* Freshen key and validate usage policy if key is not erased */ if (!WH_KEYID_ISERASED(key_id)) { - ret = wh_Server_KeystoreReadKey(ctx, key_id, NULL, read_key, - &read_key_len); + ret = wh_Server_KeystoreFreshenKey(ctx, key_id, &cachedKey, &keyMeta); if (ret == 0) { - /* override the incoming values */ - key = read_key; - key_len = read_key_len; + /* Validate key usage policy */ + ret = wh_Server_KeystoreEnforceKeyUsage( + keyMeta, enc != 0 ? WH_NVM_FLAGS_USAGE_ENCRYPT + : WH_NVM_FLAGS_USAGE_DECRYPT); + } + if (ret == 0) { + /* override the incoming values with cached key */ + key = cachedKey; + key_len = keyMeta->len; #ifdef DEBUG_CRYPTOCB_VERBOSE wh_Utils_Hexdump("[AesEcb] Key from HSM", key, key_len); #endif @@ -1884,8 +1989,8 @@ static int _HandleAesCbc(whServerContext* ctx, uint16_t magic, const void* crypt Aes aes[1] = {0}; whMessageCrypto_AesCbcRequest req; whMessageCrypto_AesCbcResponse res; - uint8_t read_key[AES_MAX_KEY_SIZE]; - uint32_t read_key_len = sizeof(read_key); + uint8_t* cachedKey = NULL; + whNvmMetadata* keyMeta = NULL; /* Translate request */ ret = wh_MessageCrypto_TranslateAesCbcRequest( @@ -1920,14 +2025,19 @@ static int _HandleAesCbc(whServerContext* ctx, uint16_t magic, const void* crypt wh_Utils_Hexdump("[AesCbc] Input data", in, len); wh_Utils_Hexdump("[AesCbc] IV", iv, AES_BLOCK_SIZE); #endif - /* Read the key if it is not erased */ + /* Freshen key and validate usage policy if key is not erased */ if (!WH_KEYID_ISERASED(key_id)) { - ret = wh_Server_KeystoreReadKey(ctx, key_id, NULL, read_key, - &read_key_len); + ret = wh_Server_KeystoreFreshenKey(ctx, key_id, &cachedKey, &keyMeta); + if (ret == 0) { + /* Validate key usage policy */ + ret = wh_Server_KeystoreEnforceKeyUsage( + keyMeta, enc != 0 ? WH_NVM_FLAGS_USAGE_ENCRYPT + : WH_NVM_FLAGS_USAGE_DECRYPT); + } if (ret == 0) { - /* override the incoming values */ - key = read_key; - key_len = read_key_len; + /* override the incoming values with cached key */ + key = cachedKey; + key_len = keyMeta->len; #ifdef DEBUG_CRYPTOCB_VERBOSE wh_Utils_Hexdump("[AesCbc] Key from HSM", key, key_len); #endif @@ -1988,10 +2098,10 @@ static int _HandleAesGcm(whServerContext* ctx, uint16_t magic, { (void)inSize; - int ret = 0; - Aes aes[1] = {0}; - uint8_t read_key[AES_MAX_KEY_SIZE]; - uint32_t read_key_len = sizeof(read_key); + int ret = 0; + Aes aes[1] = {0}; + uint8_t* cachedKey = NULL; + whNvmMetadata* keyMeta = NULL; /* Translate request */ whMessageCrypto_AesGcmRequest req; @@ -2046,18 +2156,22 @@ static int _HandleAesGcm(whServerContext* ctx, uint16_t magic, req_len); #endif - /* Read the key if it is not erased */ + /* Freshen key and validate usage policy if key is not erased */ if (!WH_KEYID_ISERASED(key_id)) { - ret = wh_Server_KeystoreReadKey(ctx, key_id, NULL, read_key, - &read_key_len); + ret = wh_Server_KeystoreFreshenKey(ctx, key_id, &cachedKey, &keyMeta); #ifdef DEBUG_CRYPTOCB_VERBOSE - printf("[server] AesGcm ReadKey key_id:%u, key_len:%d ret:%d\n", key_id, - read_key_len, ret); + printf("[server] AesGcm FreshenKey key_id:%u ret:%d\n", key_id, ret); #endif if (ret == 0) { - /* override the incoming values */ - key = read_key; - key_len = read_key_len; + /* Validate key usage policy */ + ret = wh_Server_KeystoreEnforceKeyUsage( + keyMeta, enc != 0 ? WH_NVM_FLAGS_USAGE_ENCRYPT + : WH_NVM_FLAGS_USAGE_DECRYPT); + } + if (ret == 0) { + /* override the incoming values with cached key */ + key = cachedKey; + key_len = keyMeta->len; } } if (ret == 0) { @@ -2385,9 +2499,24 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq, len = sizeof(ctx->crypto->algoCtx.cmac); keyId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId); - ret = wh_Server_KeystoreReadKey( - ctx, keyId, NULL, (uint8_t*)ctx->crypto->algoCtx.cmac, - (uint32_t*)&len); + + /* Validate key usage policy - CMAC accepts sign or verify */ + if (!WH_KEYID_ISERASED(keyId)) { + ret = wh_Server_KeystoreFindEnforceKeyUsage( + ctx, keyId, WH_NVM_FLAGS_USAGE_SIGN); + if (ret == WH_ERROR_USAGE) { + /* Sign not allowed, try verify */ + ret = wh_Server_KeystoreFindEnforceKeyUsage( + ctx, keyId, WH_NVM_FLAGS_USAGE_VERIFY); + } + if (ret != WH_ERROR_OK) { + return ret; + } + } + + ret = wh_Server_KeystoreReadKey( + ctx, keyId, meta, (uint8_t*)ctx->crypto->algoCtx.cmac, + (uint32_t*)&len); if (ret == WH_ERROR_OK) { /* if the key size is a multiple of aes, init the key and * overwrite the existing key on exit */ @@ -2498,12 +2627,22 @@ static int _HandleCmac(whServerContext* ctx, uint16_t magic, uint16_t seq, if (moveToBigCache == 1) { ret = wh_Server_KeystoreEvictKey(ctx, keyId); } - if (ret == 0) { + if (ret == WH_ERROR_OK) { meta->id = keyId; meta->len = sizeof(ctx->crypto->algoCtx.cmac); - ret = wh_Server_KeystoreCacheKey( + /* Nonzero key size means the client provided the key and + * wasn't able to provide flags, therefore we tag the key as + * useable for sign/verify operations. If the client instead + * wants to refer to the key by ID, meta->flags should + * already hold the flags set on the original AES key from + * the earlier read operation */ + if (req.keySz != 0) { + meta->flags = + WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY; + } + ret = wh_Server_KeystoreCacheKey( ctx, meta, (uint8_t*)ctx->crypto->algoCtx.cmac); - if (ret == 0) { + if (ret == WH_ERROR_OK) { res.keyId = wh_KeyId_TranslateToClient(keyId); res.outSz = 0; } @@ -2997,6 +3136,15 @@ static int _HandleMlDsaSign(whServerContext* ctx, uint16_t magic, uint32_t options = req.options; int evict = !!(options & WH_MESSAGE_CRYPTO_MLDSA_SIGN_OPTIONS_EVICT); + /* Validate key usage policy for signing */ + if (!WH_KEYID_ISERASED(key_id)) { + ret = wh_Server_KeystoreFindEnforceKeyUsage(ctx, key_id, + WH_NVM_FLAGS_USAGE_SIGN); + if (ret != WH_ERROR_OK) { + return ret; + } + } + /* Validate input length against available data to prevent buffer overread */ if (inSize < sizeof(whMessageCrypto_MlDsaSignRequest)) { @@ -3078,6 +3226,15 @@ static int _HandleMlDsaVerify(whServerContext* ctx, uint16_t magic, byte* req_sig = (uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_MlDsaVerifyRequest); + /* Validate key usage policy for verification */ + if (!WH_KEYID_ISERASED(key_id)) { + ret = wh_Server_KeystoreFindEnforceKeyUsage(ctx, key_id, + WH_NVM_FLAGS_USAGE_VERIFY); + if (ret != WH_ERROR_OK) { + return ret; + } + } + /* Validate lengths against available payload (overflow-safe) */ if (inSize < sizeof(whMessageCrypto_MlDsaVerifyRequest)) { return WH_ERROR_BADARGS; @@ -4560,6 +4717,22 @@ static int _HandleCmacDma(whServerContext* ctx, uint16_t magic, uint16_t seq, keyId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_CRYPTO, ctx->comm->client_id, clientKeyId); + + /* Validate key usage policy - CMAC accepts sign or + * verify */ + if (!WH_KEYID_ISERASED(keyId)) { + ret = wh_Server_KeystoreFindEnforceKeyUsage( + ctx, keyId, WH_NVM_FLAGS_USAGE_SIGN); + if (ret == WH_ERROR_USAGE) { + /* Sign not allowed, try verify */ + ret = wh_Server_KeystoreFindEnforceKeyUsage( + ctx, keyId, WH_NVM_FLAGS_USAGE_VERIFY); + } + if (ret != WH_ERROR_OK) { + return ret; + } + } + keyLen = sizeof(tmpKey); /* Load key from cache */ @@ -4624,6 +4797,22 @@ static int _HandleCmacDma(whServerContext* ctx, uint16_t magic, uint16_t seq, /* Get key ID from CMAC context */ keyId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_CRYPTO, ctx->comm->client_id, nvmId); + + /* Validate key usage policy - CMAC accepts sign or + * verify */ + if (!WH_KEYID_ISERASED(keyId)) { + ret = wh_Server_KeystoreFindEnforceKeyUsage( + ctx, keyId, WH_NVM_FLAGS_USAGE_SIGN); + if (ret == WH_ERROR_USAGE) { + /* Sign not allowed, try verify */ + ret = wh_Server_KeystoreFindEnforceKeyUsage( + ctx, keyId, WH_NVM_FLAGS_USAGE_VERIFY); + } + if (ret != WH_ERROR_OK) { + return ret; + } + } + keyLen = sizeof(tmpKey); /* Load key from cache */ diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index ea33d50e..5fc488ed 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -720,6 +720,13 @@ static int _AesGcmKeyWrap(whServerContext* server, whKeyId serverKeyId, } serverKeySz = serverKeyMetadata->len; + /* Validate key usage policy for wrapping (KEK) */ + ret = wh_Server_KeystoreEnforceKeyUsage(serverKeyMetadata, + WH_NVM_FLAGS_USAGE_WRAP); + if (ret != WH_ERROR_OK) { + return ret; + } + /* Initialize AES context and set it to use the server side key */ ret = wc_AesInit(aes, NULL, server->crypto->devId); if (ret != 0) { @@ -784,6 +791,7 @@ static int _AesGcmKeyUnwrap(whServerContext* server, uint16_t serverKeyId, return WH_ERROR_BADARGS; } + /* Get the server side key */ ret = wh_Server_KeystoreFreshenKey(server, serverKeyId, &serverKey, &serverKeyMetadata); @@ -792,6 +800,13 @@ static int _AesGcmKeyUnwrap(whServerContext* server, uint16_t serverKeyId, } serverKeySz = serverKeyMetadata->len; + /* Validate key usage policy for unwrapping (KEK) */ + ret = wh_Server_KeystoreEnforceKeyUsage(serverKeyMetadata, + WH_NVM_FLAGS_USAGE_WRAP); + if (ret != WH_ERROR_OK) { + return ret; + } + /* Initialize AES context and set it to use the server side key */ ret = wc_AesInit(aes, NULL, server->crypto->devId); if (ret != 0) { @@ -988,6 +1003,10 @@ static int _HandleKeyWrapRequest(whServerContext* server, server->comm->client_id, req->serverKeyId); + /* Ensure the cipher type in the response matches the request */ + resp->cipherType = req->cipherType; + /* Wrapped key size is only passed back to the client on success */ + resp->wrappedKeySz = 0; /* Store the wrapped key in the response data */ wrappedKey = respData; @@ -1015,7 +1034,6 @@ static int _HandleKeyWrapRequest(whServerContext* server, /* Tell the client how big the wrapped key is */ resp->wrappedKeySz = wrappedKeySz; - resp->cipherType = WC_CIPHER_AES_GCM; } break; #endif /* HAVE_AESGCM */ @@ -1058,6 +1076,11 @@ static int _HandleKeyUnwrapAndExportRequest( server->comm->client_id, req->serverKeyId); + /* Ensure the cipher type in the response matches the request */ + resp->cipherType = req->cipherType; + /* Key size is only passed back to the client on success */ + resp->keySz = 0; + /* Store the metadata and key in the respData */ metadata = (whNvmMetadata*)respData; key = respData + sizeof(*metadata); @@ -1071,10 +1094,6 @@ static int _HandleKeyUnwrapAndExportRequest( req->wrappedKeySz - WOLFHSM_KEYWRAP_AES_GCM_IV_SIZE - WOLFHSM_KEYWRAP_AES_GCM_TAG_SIZE - sizeof(*metadata); - resp->cipherType = WC_CIPHER_AES_GCM; - /* Key size is only passed back to the client on success */ - resp->keySz = 0; - /* Check if the response data can fit the metadata + key */ if (respDataSz < sizeof(*metadata) + keySz) { return WH_ERROR_BUFFER_SIZE; @@ -1163,6 +1182,11 @@ static int _HandleKeyUnwrapAndCacheRequest( server->comm->client_id, req->serverKeyId); + /* Ensure the cipher type in the response matches the request */ + resp->cipherType = req->cipherType; + /* Key ID is only passed back to the client on success */ + resp->keyId = WH_KEYID_ERASED; + /* Unwrap the key based on the cipher type */ switch (req->cipherType) { #ifndef NO_AES @@ -1264,6 +1288,11 @@ static int _HandleDataWrapRequest(whServerContext* server, server->comm->client_id, req->serverKeyId); + /* Ensure the cipher type in the response matches the request */ + resp->cipherType = req->cipherType; + /* Wrapped data size is only passed back to the client on success */ + resp->wrappedDataSz = 0; + /* Store the wrapped data in the response data */ wrappedData = respData; @@ -1333,6 +1362,11 @@ static int _HandleDataUnwrapRequest(whServerContext* server, server->comm->client_id, req->serverKeyId); + /* Ensure the cipher type in the response matches the request */ + resp->cipherType = req->cipherType; + /* Data size is only passed back to the client on success */ + resp->dataSz = 0; + /* Store the unwrapped data in the respData */ data = respData; @@ -1942,4 +1976,49 @@ int wh_Server_KeystoreExportKeyDma(whServerContext* server, whKeyId keyId, } #endif /* WOLFHSM_CFG_DMA */ +int wh_Server_KeystoreEnforceKeyUsage(const whNvmMetadata* meta, + whNvmFlags requiredUsage) +{ + whNvmFlags actualFlags; + + /* Validate input parameters */ + if (meta == NULL) { + return WH_ERROR_BADARGS; + } + + /* We only care about the usage flags */ + requiredUsage &= WH_NVM_FLAGS_USAGE_ANY; + + /* Check if the key has ALL the required usage flags set */ + actualFlags = meta->flags & WH_NVM_FLAGS_USAGE_ANY; + if ((actualFlags & requiredUsage) == requiredUsage) { + return WH_ERROR_OK; + } + + /* Key does not have ALL the required usage flags */ + return WH_ERROR_USAGE; +} + +int wh_Server_KeystoreFindEnforceKeyUsage(whServerContext* server, + whKeyId keyId, + whNvmFlags requiredUsage) +{ + int ret; + whNvmMetadata* meta = NULL; + + /* Validate input parameters */ + if (server == NULL) { + return WH_ERROR_BADARGS; + } + + /* Freshen the key to obtain the metadata */ + ret = wh_Server_KeystoreFreshenKey(server, keyId, NULL, &meta); + if (ret != WH_ERROR_OK) { + return ret; + } + + /* Enforce the usage policy with the obtained metadata */ + return wh_Server_KeystoreEnforceKeyUsage(meta, requiredUsage); +} + #endif /* !WOLFHSM_CFG_NO_CRYPTO && WOLFHSM_CFG_ENABLE_SERVER */ diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index 4ea731b5..63302065 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -254,8 +254,9 @@ static int whTest_CryptoRsa(whClientContext* ctx, int devId, WC_RNG* rng) /* Using keyCache key */ memset(cipherText, 0, sizeof(cipherText)); memset(finalText, 0, sizeof(finalText)); - ret = wh_Client_RsaMakeCacheKey(ctx, RSA_KEY_BITS, RSA_EXPONENT, - &keyId, WH_NVM_FLAGS_NONE, 0, NULL); + ret = wh_Client_RsaMakeCacheKey( + ctx, RSA_KEY_BITS, RSA_EXPONENT, &keyId, + WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT, 0, NULL); if (ret != 0) { WH_ERROR_PRINT("Failed to make cached key %d\n", ret); } else { @@ -320,14 +321,6 @@ static int whTest_CryptoEcc(whClientContext* ctx, int devId, WC_RNG* rng) uint8_t hash[TEST_ECC_KEYSIZE] = {0}; uint8_t sig[ECC_MAX_SIG_SIZE] = {0}; -#if 0 - whNvmFlags flags = WH_NVM_FLAGS_NONE; - whKeyId key_id_a = WH_KEYID_ERASED; - uint8_t* label_a = (uint8_t*)("Ecc Label A"); - whKeyId key_id_b = 24; - uint8_t* label_b = (uint8_t*)("Ecc Label B"); -#endif - ret = wc_ecc_init_ex(eccPrivate, NULL, devId); if (ret != 0) { WH_ERROR_PRINT("Failed to wc_ecc_init_ex %d\n", ret); @@ -414,7 +407,7 @@ static int whTest_CryptoCurve25519(whClientContext* ctx, int devId, WC_RNG* rng) uint8_t shared_ab[CURVE25519_KEYSIZE] = {0}; uint8_t shared_ba[CURVE25519_KEYSIZE] = {0}; int key_size = CURVE25519_KEYSIZE; - whNvmFlags flags = WH_NVM_FLAGS_NONE; + whNvmFlags flags = WH_NVM_FLAGS_USAGE_DERIVE; whKeyId key_id_a = WH_KEYID_ERASED; uint8_t label_a[WH_NVM_LABEL_LEN] = "Curve25519 Label A"; whKeyId key_id_b = 42; @@ -1196,8 +1189,9 @@ static int whTest_CryptoHkdf(whClientContext* ctx, int devId, WC_RNG* rng) byte okm_direct[WH_TEST_HKDF_OKM_SIZE]; /* First, cache the input key */ - ret = wh_Client_KeyCache(ctx, 0, label_in, sizeof(label_in), ikm2, - sizeof(ikm2), &keyIdIn); + ret = + wh_Client_KeyCache(ctx, WH_NVM_FLAGS_USAGE_DERIVE, label_in, + sizeof(label_in), ikm2, sizeof(ikm2), &keyIdIn); if (ret != 0) { WH_ERROR_PRINT("Failed to cache input key: %d\n", ret); return ret; @@ -1360,13 +1354,13 @@ static int whTest_CryptoCmacKdf(whClientContext* ctx, int devId, WC_RNG* rng) /* 4. Use cached salt and Z inputs */ whKeyId saltKeyId = WH_KEYID_ERASED; whKeyId zKeyId = WH_KEYID_ERASED; - ret = wh_Client_KeyCache(ctx, WH_NVM_FLAGS_NONE, NULL, 0, salt, + ret = wh_Client_KeyCache(ctx, WH_NVM_FLAGS_USAGE_DERIVE, NULL, 0, salt, WH_TEST_CMAC_KDF_SALT_SIZE, &saltKeyId); if (ret != 0) { WH_ERROR_PRINT("Failed to cache CMAC KDF salt: %d\n", ret); return ret; } - ret = wh_Client_KeyCache(ctx, WH_NVM_FLAGS_NONE, NULL, 0, z, + ret = wh_Client_KeyCache(ctx, WH_NVM_FLAGS_USAGE_DERIVE, NULL, 0, z, WH_TEST_CMAC_KDF_Z_SIZE, &zKeyId); if (ret != 0) { WH_ERROR_PRINT("Failed to cache CMAC KDF Z input: %d\n", ret); @@ -2289,8 +2283,9 @@ static int whTestCrypto_Aes(whClientContext* ctx, int devId, WC_RNG* rng) } else { keyId = WH_KEYID_ERASED; - ret = wh_Client_KeyCache(ctx, 0, labelIn, sizeof(labelIn), key, - sizeof(key), &keyId); + ret = wh_Client_KeyCache( + ctx, WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT, + labelIn, sizeof(labelIn), key, sizeof(key), &keyId); if (ret != 0) { WH_ERROR_PRINT("Failed to wh_Client_KeyCache %d\n", ret); } @@ -2402,8 +2397,9 @@ static int whTestCrypto_Aes(whClientContext* ctx, int devId, WC_RNG* rng) } else { keyId = WH_KEYID_ERASED; - ret = wh_Client_KeyCache(ctx, 0, labelIn, sizeof(labelIn), key, - sizeof(key), &keyId); + ret = wh_Client_KeyCache( + ctx, WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT, + labelIn, sizeof(labelIn), key, sizeof(key), &keyId); if (ret != 0) { WH_ERROR_PRINT("Failed to wh_Client_KeyCache %d\n", ret); } @@ -2517,8 +2513,9 @@ static int whTestCrypto_Aes(whClientContext* ctx, int devId, WC_RNG* rng) WH_ERROR_PRINT("Failed to wc_AesInit %d\n", ret); } else { keyId = WH_KEYID_ERASED; - ret = wh_Client_KeyCache(ctx, 0, labelIn, sizeof(labelIn), - key, sizeof(key), &keyId); + ret = wh_Client_KeyCache( + ctx, WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT, + labelIn, sizeof(labelIn), key, sizeof(key), &keyId); if (ret != 0) { WH_ERROR_PRINT("Failed to wh_Client_KeyCache %d\n", ret); } else { @@ -2627,8 +2624,9 @@ static int whTestCrypto_Aes(whClientContext* ctx, int devId, WC_RNG* rng) WH_ERROR_PRINT("Failed to wc_AesInit %d\n", ret); } else { keyId = WH_KEYID_ERASED; - ret = wh_Client_KeyCache(ctx, 0, labelIn, sizeof(labelIn), key, - sizeof(key), &keyId); + ret = wh_Client_KeyCache( + ctx, WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT, + labelIn, sizeof(labelIn), key, sizeof(key), &keyId); if (ret != 0) { WH_ERROR_PRINT("Failed to wh_Client_KeyCache %d\n", ret); } else { @@ -2747,8 +2745,9 @@ static int whTestCrypto_Cmac(whClientContext* ctx, int devId, WC_RNG* rng) if (ret == 0) { /* test oneshot generate with pre-cached key */ keyId = WH_KEYID_ERASED; - ret = wh_Client_KeyCache(ctx, 0, labelIn, sizeof(labelIn), knownCmacKey, - sizeof(knownCmacKey), &keyId); + ret = wh_Client_KeyCache(ctx, WH_NVM_FLAGS_USAGE_SIGN, labelIn, + sizeof(labelIn), knownCmacKey, + sizeof(knownCmacKey), &keyId); if (ret != 0) { WH_ERROR_PRINT("Failed to wh_Client_KeyCache %d\n", ret); } @@ -2814,8 +2813,9 @@ static int whTestCrypto_Cmac(whClientContext* ctx, int devId, WC_RNG* rng) if (ret == 0) { /* test oneshot verify with committed key */ keyId = WH_KEYID_ERASED; - ret = wh_Client_KeyCache(ctx, 0, labelIn, sizeof(labelIn), knownCmacKey, - sizeof(knownCmacKey), &keyId); + ret = wh_Client_KeyCache(ctx, WH_NVM_FLAGS_USAGE_VERIFY, labelIn, + sizeof(labelIn), knownCmacKey, + sizeof(knownCmacKey), &keyId); if (ret != 0) { WH_ERROR_PRINT("Failed to wh_Client_KeyCache %d\n", ret); } @@ -3730,6 +3730,460 @@ int whTestCrypto_MlDsaVerifyOnlyDma(whClientContext* ctx, int devId, #endif /* HAVE_DILITHIUM */ +/* Test key usage policy enforcement for various crypto operations */ +int whTest_CryptoKeyUsagePolicies(whClientContext* client, WC_RNG* rng) +{ + int ret = 0; + uint8_t plaintext[16] = {0}; + uint8_t ciphertext[16] = {0}; + uint8_t key[32] = {0}; + uint32_t keyLen = sizeof(key); + whKeyId keyId = WH_KEYID_ERASED; + + printf("Testing Key Usage Policies...\n"); + + /* Generate random test data */ + ret = wc_RNG_GenerateBlock(rng, plaintext, sizeof(plaintext)); + if (ret != 0) { + WH_ERROR_PRINT("Failed to generate random data: %d\n", ret); + return ret; + } + + ret = wc_RNG_GenerateBlock(rng, key, sizeof(key)); + if (ret != 0) { + WH_ERROR_PRINT("Failed to generate random key: %d\n", ret); + return ret; + } + +#ifndef NO_AES +#ifdef HAVE_AES_CBC + /* AES encrypt without ENCRYPT flag */ + printf(" Testing AES CBC encrypt without ENCRYPT flag...\n"); + { + Aes aes[1]; + uint8_t iv[AES_BLOCK_SIZE] = {0}; + + /* Cache key WITHOUT encrypt flag */ + keyId = WH_KEYID_ERASED; + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_NONE, + (uint8_t*)"aes-no-enc", strlen("aes-no-enc"), + key, keyLen, &keyId); + if (ret == 0) { + /* Initialize AES with HSM device ID */ + ret = wc_AesInit(aes, NULL, WH_DEV_ID); + if (ret == 0) { + /* Set the cached keyId (not raw key material) */ + ret = wh_Client_AesSetKeyId(aes, keyId); + if (ret == 0) { + /* Set IV */ + ret = wc_AesSetIV(aes, iv); + if (ret == 0) { + /* Try to encrypt - should fail with WH_ERROR_USAGE */ + ret = wc_AesCbcEncrypt(aes, ciphertext, plaintext, + sizeof(plaintext)); + if (ret == WH_ERROR_USAGE) { + printf(" PASS: Correctly denied encryption\n"); + ret = 0; /* Test passed */ + } + else { + WH_ERROR_PRINT( + " FAIL: Expected WH_ERROR_USAGE, got %d\n", + ret); + ret = WH_ERROR_ABORTED; + } + } + } + wc_AesFree(aes); + } + wh_Client_KeyEvict(client, keyId); + } + } + if (ret != 0) + return ret; + + /* AES decrypt without DECRYPT flag */ + printf(" Testing AES CBC decrypt without DECRYPT flag...\n"); + { + Aes aes[1]; + uint8_t iv[AES_BLOCK_SIZE] = {0}; + uint8_t decrypted[16] = {0}; + uint8_t tempCipher[16] = {0}; + + /* First, create some ciphertext using a key with ENCRYPT flag */ + keyId = WH_KEYID_ERASED; + ret = wh_Client_KeyCache(client, WH_NVM_FLAGS_USAGE_ENCRYPT, + (uint8_t*)"aes-enc-only", + strlen("aes-enc-only"), key, keyLen, &keyId); + if (ret == 0) { + ret = wc_AesInit(aes, NULL, WH_DEV_ID); + if (ret == 0) { + ret = wh_Client_AesSetKeyId(aes, keyId); + if (ret == 0) { + ret = wc_AesSetIV(aes, iv); + if (ret == 0) { + /* Encrypt to create ciphertext */ + ret = wc_AesCbcEncrypt(aes, tempCipher, plaintext, + sizeof(plaintext)); + } + } + wc_AesFree(aes); + } + wh_Client_KeyEvict(client, keyId); + } + + if (ret == 0) { + /* Now cache same key WITHOUT decrypt flag */ + keyId = WH_KEYID_ERASED; + ret = wh_Client_KeyCache( + client, + WH_NVM_FLAGS_USAGE_ENCRYPT, /* Only ENCRYPT, no DECRYPT */ + (uint8_t*)"aes-no-dec", strlen("aes-no-dec"), key, keyLen, + &keyId); + if (ret == 0) { + /* Initialize AES with HSM device ID */ + ret = wc_AesInit(aes, NULL, WH_DEV_ID); + if (ret == 0) { + /* Set the cached keyId */ + ret = wh_Client_AesSetKeyId(aes, keyId); + if (ret == 0) { + /* Set IV */ + ret = wc_AesSetIV(aes, iv); + if (ret == 0) { + /* Try to decrypt - should fail with WH_ERROR_USAGE + */ + ret = wc_AesCbcDecrypt(aes, decrypted, tempCipher, + sizeof(tempCipher)); + if (ret == WH_ERROR_USAGE) { + printf( + " PASS: Correctly denied decryption\n"); + ret = 0; /* Test passed */ + } + else { + WH_ERROR_PRINT(" FAIL: Expected " + "WH_ERROR_USAGE, got %d\n", + ret); + ret = WH_ERROR_ABORTED; + } + } + } + wc_AesFree(aes); + } + wh_Client_KeyEvict(client, keyId); + } + } + } + if (ret != 0) + return ret; +#endif /* HAVE_AES_CBC */ +#endif /* !NO_AES */ + +#ifdef HAVE_ECC +#ifdef HAVE_ECC_SIGN + /* ECDSA sign without SIGN flag */ + printf(" Testing ECDSA sign without SIGN flag...\n"); + { + ecc_key eccKey[1]; + uint8_t sig[ECC_MAX_SIG_SIZE] = {0}; + word32 sigLen = sizeof(sig); + uint8_t hash[WC_SHA256_DIGEST_SIZE] = {0}; + + /* Generate key on server and cache it WITHOUT sign flag */ + keyId = WH_KEYID_ERASED; + ret = wh_Client_EccMakeCacheKey( + client, 32, ECC_SECP256R1, &keyId, WH_NVM_FLAGS_NONE, + strlen("ecc-no-sign"), (uint8_t*)"ecc-no-sign"); + if (ret == 0) { + /* Initialize ecc_key with HSM device ID and set curve */ + ret = wc_ecc_init_ex(eccKey, NULL, WH_DEV_ID); + if (ret == 0) { + ret = wc_ecc_set_curve(eccKey, 32, ECC_SECP256R1); + if (ret == 0) { + /* Associate the cached keyId with this ecc_key */ + ret = wh_Client_EccSetKeyId(eccKey, keyId); + if (ret == 0) { + /* Generate a hash to sign */ + ret = wc_RNG_GenerateBlock(rng, hash, sizeof(hash)); + if (ret == 0) { + /* Try to sign - should fail with WH_ERROR_USAGE */ + ret = wc_ecc_sign_hash(hash, sizeof(hash), sig, + &sigLen, rng, eccKey); + if (ret == WH_ERROR_USAGE) { + printf(" PASS: Correctly denied signing\n"); + ret = 0; /* Test passed */ + } + else { + WH_ERROR_PRINT(" FAIL: Expected " + "WH_ERROR_USAGE, got %d\n", + ret); + ret = WH_ERROR_ABORTED; + } + } + } + } + wc_ecc_free(eccKey); + } + wh_Client_KeyEvict(client, keyId); + } + } + if (ret != 0) + return ret; +#endif /* HAVE_ECC_SIGN */ + +#ifdef HAVE_ECC_DHE + /* ECDH without DERIVE flag */ + printf(" Testing ECDH without DERIVE flag...\n"); + { + ecc_key privKey[1]; + ecc_key pubKey[1]; + uint8_t sharedSecret[ECC_MAXSIZE] = {0}; + word32 secretLen = sizeof(sharedSecret); + + /* Generate private key on server WITHOUT derive flag */ + keyId = WH_KEYID_ERASED; + ret = wh_Client_EccMakeCacheKey( + client, 32, ECC_SECP256R1, &keyId, WH_NVM_FLAGS_NONE, + strlen("ecc-no-derive"), (uint8_t*)"ecc-no-derive"); + if (ret == 0) { + /* Initialize private key with HSM device ID and set curve */ + ret = wc_ecc_init_ex(privKey, NULL, WH_DEV_ID); + if (ret == 0) { + ret = wc_ecc_set_curve(privKey, 32, ECC_SECP256R1); + if (ret == 0) { + /* Associate the cached keyId with private key */ + ret = wh_Client_EccSetKeyId(privKey, keyId); + } + if (ret == 0) { + /* Generate a public key locally for ECDH */ + ret = wc_ecc_init_ex(pubKey, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_ecc_make_key(rng, 32, pubKey); + if (ret == 0) { + /* Try ECDH - should fail with WH_ERROR_USAGE */ + ret = wc_ecc_shared_secret( + privKey, pubKey, sharedSecret, &secretLen); + if (ret == WH_ERROR_USAGE) { + printf(" PASS: Correctly denied key " + "derivation\n"); + ret = 0; /* Test passed */ + } + else { + WH_ERROR_PRINT(" FAIL: Expected " + "WH_ERROR_USAGE, got %d\n", + ret); + ret = WH_ERROR_ABORTED; + } + } + wc_ecc_free(pubKey); + } + } + wc_ecc_free(privKey); + } + wh_Client_KeyEvict(client, keyId); + } + } + if (ret != 0) + return ret; +#endif /* HAVE_ECC_DHE */ +#endif /* HAVE_ECC */ + +#ifdef HAVE_HKDF + /* HKDF without DERIVE flag */ + printf(" Testing HKDF without DERIVE flag...\n"); + { + uint8_t ikm[32] = {0}; + whKeyId outKeyId = WH_KEYID_ERASED; + + ret = wc_RNG_GenerateBlock(rng, ikm, sizeof(ikm)); + if (ret == 0) { + /* Cache IKM without DERIVE flag */ + keyId = WH_KEYID_ERASED; + ret = wh_Client_KeyCache( + client, WH_NVM_FLAGS_NONE, (uint8_t*)"hkdf-no-derive", + strlen("hkdf-no-derive"), ikm, sizeof(ikm), &keyId); + if (ret == 0) { + /* Try HKDF using cached key - should fail */ + ret = wh_Client_HkdfMakeCacheKey( + client, WC_SHA256, keyId, NULL, 0, /* Use cached key */ + NULL, 0, /* salt */ + NULL, 0, /* info */ + &outKeyId, /* output key ID */ + WH_NVM_FLAGS_EPHEMERAL, (uint8_t*)"hkdf-out", + strlen("hkdf-out"), 32); /* output size */ + if (ret == WH_ERROR_USAGE) { + printf(" PASS: Correctly denied HKDF derivation\n"); + ret = 0; /* Test passed */ + } + else { + WH_ERROR_PRINT( + " FAIL: Expected WH_ERROR_USAGE, got %d\n", ret); + ret = WH_ERROR_ABORTED; + } + wh_Client_KeyEvict(client, keyId); + if (!WH_KEYID_ISERASED(outKeyId)) { + wh_Client_KeyEvict(client, outKeyId); + } + } + } + } + if (ret != 0) + return ret; +#endif /* HAVE_HKDF */ + +#if defined(WOLFSSL_CMAC) && !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) + /* CMAC Generate without SIGN flag */ + printf(" Testing CMAC generate without SIGN flag...\n"); + { + Cmac cmac; + whKeyId keyId = WH_KEYID_ERASED; + uint8_t message[64]; + uint8_t tag[AES_BLOCK_SIZE]; + word32 tagLen = sizeof(tag); + + /* Generate random message */ + ret = wc_RNG_GenerateBlock(rng, message, sizeof(message)); + if (ret == 0) { + /* Cache AES key without SIGN flag */ + ret = wh_Client_KeyCache( + client, WH_NVM_FLAGS_NONE, (uint8_t*)"cmac-no-sign", + strlen("cmac-no-sign"), key, AES_128_KEY_SIZE, &keyId); + } + + if (ret == 0) { + /* Initialize CMAC with HSM device ID */ + ret = wc_InitCmac_ex(&cmac, NULL, 0, WC_CMAC_AES, NULL, NULL, + WH_DEV_ID); + if (ret == 0) { + /* Associate cached key */ + ret = wh_Client_CmacSetKeyId(&cmac, keyId); + if (ret == 0) { + /* Try to generate CMAC - should fail */ + ret = wc_AesCmacGenerate_ex(&cmac, tag, &tagLen, message, + sizeof(message), NULL, 0, NULL, + WH_DEV_ID); + if (ret == WH_ERROR_USAGE) { + printf(" PASS: Correctly denied CMAC generate\n"); + ret = 0; /* Test passed */ + } + else { + WH_ERROR_PRINT( + " FAIL: Expected WH_ERROR_USAGE, got %d\n", ret); + ret = WH_ERROR_ABORTED; + } + } + wc_CmacFree(&cmac); + } + wh_Client_KeyEvict(client, keyId); + } + } + if (ret != 0) + return ret; + + /* CMAC Verify without VERIFY flag */ + printf(" Testing CMAC verify without VERIFY flag...\n"); + { + Cmac cmac; + whKeyId keyId = WH_KEYID_ERASED; + uint8_t message[64]; + uint8_t tag[AES_BLOCK_SIZE]; + word32 tagLen = sizeof(tag); + + /* Generate random message and tag */ + ret = wc_RNG_GenerateBlock(rng, message, sizeof(message)); + if (ret == 0) { + ret = wc_RNG_GenerateBlock(rng, tag, sizeof(tag)); + } + + if (ret == 0) { + /* Cache AES key without VERIFY flag */ + ret = wh_Client_KeyCache( + client, WH_NVM_FLAGS_NONE, (uint8_t*)"cmac-no-verify", + strlen("cmac-no-verify"), key, AES_128_KEY_SIZE, &keyId); + } + + if (ret == 0) { + /* Initialize CMAC with HSM device ID */ + ret = wc_InitCmac_ex(&cmac, NULL, 0, WC_CMAC_AES, NULL, NULL, + WH_DEV_ID); + if (ret == 0) { + /* Associate cached key */ + ret = wh_Client_CmacSetKeyId(&cmac, keyId); + if (ret == 0) { + /* Try to verify CMAC - should fail */ + ret = wc_AesCmacVerify_ex(&cmac, tag, tagLen, message, + sizeof(message), NULL, 0, NULL, + WH_DEV_ID); + if (ret == WH_ERROR_USAGE) { + printf(" PASS: Correctly denied CMAC verify\n"); + ret = 0; /* Test passed */ + } + else { + WH_ERROR_PRINT( + " FAIL: Expected WH_ERROR_USAGE, got %d\n", ret); + ret = WH_ERROR_ABORTED; + } + } + wc_CmacFree(&cmac); + } + wh_Client_KeyEvict(client, keyId); + } + } + if (ret != 0) + return ret; +#endif /* WOLFSSL_CMAC && !NO_AES && WOLFSSL_AES_DIRECT */ + +#ifdef WOLFHSM_CFG_KEYWRAP + /* Key wrap without WRAP flag */ + printf(" Testing key wrap without WRAP flag...\n"); + { + uint8_t kek[32] = {0}; + uint8_t dataKey[32] = {0}; + uint8_t wrappedKey[256] = {0}; + whKeyId kekId = WH_KEYID_ERASED; + const whKeyId wrappedId = 1; + whNvmMetadata meta = {0}; + + ret = wc_RNG_GenerateBlock(rng, kek, sizeof(kek)); + if (ret == 0) { + ret = wc_RNG_GenerateBlock(rng, dataKey, sizeof(dataKey)); + } + + if (ret == 0) { + /* Cache KEK without WRAP flag */ + ret = wh_Client_KeyCache( + client, WH_NVM_FLAGS_NONE, (uint8_t*)"kek-no-wrap", + strlen("kek-no-wrap"), kek, sizeof(kek), &kekId); + if (ret == 0) { + /* Setup metadata for data key */ + meta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META( + client->comm->client_id, wrappedId); + meta.flags = WH_NVM_FLAGS_NONE; + meta.len = sizeof(dataKey); + + /* Try to wrap - should fail */ + ret = wh_Client_KeyWrap(client, WC_CIPHER_AES_GCM, kekId, + dataKey, sizeof(dataKey), &meta, + wrappedKey, sizeof(wrappedKey)); + if (ret == WH_ERROR_USAGE) { + printf(" PASS: Correctly denied key wrapping\n"); + ret = 0; /* Test passed */ + } + else { + WH_ERROR_PRINT( + " FAIL: Expected WH_ERROR_USAGE, got %d\n", ret); + ret = WH_ERROR_ABORTED; + } + wh_Client_KeyEvict(client, kekId); + } + } + } + if (ret != 0) + return ret; +#endif /* WOLFHSM_CFG_KEYWRAP */ + + printf("Key Usage Policy Tests PASSED\n"); + return 0; +} + int whTest_CryptoClientConfig(whClientConfig* config) { @@ -3791,6 +4245,11 @@ int whTest_CryptoClientConfig(whClientConfig* config) ret = whTest_NonExportableKeystore(client, WH_DEV_ID, rng); } + if (ret == 0) { + /* Test Key Usage Policy enforcement */ + ret = whTest_CryptoKeyUsagePolicies(client, rng); + } + #ifdef WOLFHSM_CFG_KEYWRAP if (ret == 0) { /* Test keywrap functionality */ diff --git a/test/wh_test_keywrap.c b/test/wh_test_keywrap.c index 9c3373a4..b5de6f82 100644 --- a/test/wh_test_keywrap.c +++ b/test/wh_test_keywrap.c @@ -60,7 +60,7 @@ static int _InitServerKek(whClientContext* client) * provisioning. Uploading the KEK via the client is for testing purposes * only and not intended as a recommendation */ whKeyId serverKeyId = WH_TEST_KEKID; - whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE; + whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE | WH_NVM_FLAGS_USAGE_WRAP; uint8_t label[WH_NVM_LABEL_LEN] = "Server KEK key"; uint8_t kek[] = {0x03, 0x03, 0x0d, 0xd9, 0xeb, 0x18, 0x17, 0x2e, 0x06, 0x6e, 0x19, 0xce, 0x98, 0x44, 0x54, 0x0d, @@ -91,7 +91,7 @@ static int _AesGcm_TestKeyWrap(whClientContext* client, WC_RNG* rng) WH_TEST_AESGCM_KEYID), .label = "AES Key Label", .len = WH_TEST_AES_KEYSIZE, - .flags = WH_NVM_FLAGS_NONE, + .flags = WH_NVM_FLAGS_USAGE_ANY, }; whNvmMetadata tmpMetadata = {0}; diff --git a/test/wh_test_multiclient.c b/test/wh_test_multiclient.c index d3b3629a..12b35170 100644 --- a/test/wh_test_multiclient.c +++ b/test/wh_test_multiclient.c @@ -538,8 +538,8 @@ static int _testGlobalKeyWrapExport(whClientContext* client1, /* Client 1 caches a global wrapping key */ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex( - client1, 0, (uint8_t*)"WrapKey45", sizeof("WrapKey45"), wrapKey, - sizeof(wrapKey), serverKeyId)); + client1, WH_NVM_FLAGS_USAGE_WRAP, (uint8_t*)"WrapKey45", + sizeof("WrapKey45"), wrapKey, sizeof(wrapKey), serverKeyId)); WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1)); WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId)); @@ -607,16 +607,15 @@ static int _testGlobalKeyUnwrapCache(whClientContext* client1, /* Client 1 caches a global wrapping key */ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex( - client1, 0, (uint8_t*)"UnwrapKey50", sizeof("UnwrapKey50"), wrapKey, - sizeof(wrapKey), serverKeyId)); + client1, WH_NVM_FLAGS_USAGE_WRAP, (uint8_t*)"UnwrapKey50", + sizeof("UnwrapKey50"), wrapKey, sizeof(wrapKey), serverKeyId)); WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1)); WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId)); /* Client 1 wraps a global key */ serverKeyId = WH_CLIENT_KEYID_MAKE_GLOBAL(DUMMY_KEYID_1); - /* key-TODO: client-facing helper macro? */ meta.id = - WH_MAKE_KEYID(WH_KEYTYPE_WRAPPED, WH_KEYUSER_GLOBAL, DUMMY_KEYID_2); + WH_CLIENT_KEYID_MAKE_WRAPPED_META(WH_KEYUSER_GLOBAL, DUMMY_KEYID_2); meta.len = sizeof(plainKey); WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrapRequest(client1, WC_CIPHER_AES_GCM, @@ -691,8 +690,8 @@ static int _testWrappedKey_GlobalWrap_GlobalKey_Positive( /* Client 1 caches a global wrapping key */ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex( - client1, 0, (uint8_t*)"WrapKey_7a", sizeof("WrapKey_7a"), wrapKey, - sizeof(wrapKey), serverKeyId)); + client1, WH_NVM_FLAGS_USAGE_WRAP, (uint8_t*)"WrapKey_7a", + sizeof("WrapKey_7a"), wrapKey, sizeof(wrapKey), serverKeyId)); WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1)); WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId)); @@ -755,8 +754,8 @@ static int _testWrappedKey_GlobalWrap_GlobalKey_NonExportable( /* Client 1 caches a global wrapping key */ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex( - client1, 0, (uint8_t*)"WrapKey_7b", sizeof("WrapKey_7b"), wrapKey, - sizeof(wrapKey), serverKeyId)); + client1, WH_NVM_FLAGS_USAGE_WRAP, (uint8_t*)"WrapKey_7b", + sizeof("WrapKey_7b"), wrapKey, sizeof(wrapKey), serverKeyId)); WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1)); WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId)); @@ -820,8 +819,8 @@ static int _testWrappedKey_GlobalWrap_LocalKey_OwnerExport( /* Client 1 caches a global wrapping key */ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex( - client1, 0, (uint8_t*)"WrapKey_8a", sizeof("WrapKey_8a"), wrapKey, - sizeof(wrapKey), serverKeyId)); + client1, WH_NVM_FLAGS_USAGE_WRAP, (uint8_t*)"WrapKey_8a", + sizeof("WrapKey_8a"), wrapKey, sizeof(wrapKey), serverKeyId)); WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1)); WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId)); @@ -887,8 +886,8 @@ static int _testWrappedKey_GlobalWrap_LocalKey_NonOwnerFails( /* Client 1 caches a global wrapping key */ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex( - client1, 0, (uint8_t*)"WrapKey_8b", sizeof("WrapKey_8b"), wrapKey, - sizeof(wrapKey), serverKeyId)); + client1, WH_NVM_FLAGS_USAGE_WRAP, (uint8_t*)"WrapKey_8b", + sizeof("WrapKey_8b"), wrapKey, sizeof(wrapKey), serverKeyId)); WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1)); WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId)); @@ -961,8 +960,8 @@ static int _testWrappedKey_LocalWrap_LocalKey_SameOwner( /* Client 1 caches a LOCAL wrapping key */ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex( - client1, 0, (uint8_t*)"WrapKey_9a", sizeof("WrapKey_9a"), wrapKey, - sizeof(wrapKey), serverKeyId)); + client1, WH_NVM_FLAGS_USAGE_WRAP, (uint8_t*)"WrapKey_9a", + sizeof("WrapKey_9a"), wrapKey, sizeof(wrapKey), serverKeyId)); WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1)); WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId)); @@ -1030,8 +1029,8 @@ static int _testWrappedKey_LocalWrap_LocalKey_NoAccessWithoutWrapKey( /* Client 1 caches a LOCAL wrapping key */ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex( - client1, 0, (uint8_t*)"WrapKey_9b", sizeof("WrapKey_9b"), wrapKey, - sizeof(wrapKey), serverKeyId)); + client1, WH_NVM_FLAGS_USAGE_WRAP, (uint8_t*)"WrapKey_9b", + sizeof("WrapKey_9b"), wrapKey, sizeof(wrapKey), serverKeyId)); WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1)); WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId)); @@ -1101,8 +1100,8 @@ static int _testWrappedKey_LocalWrap_GlobalKey_AnyCacheGlobal( /* Client 1 caches a LOCAL wrapping key */ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex( - client1, 0, (uint8_t*)"WrapKey_10a", sizeof("WrapKey_10a"), wrapKey, - sizeof(wrapKey), serverKeyId)); + client1, WH_NVM_FLAGS_USAGE_WRAP, (uint8_t*)"WrapKey_10a", + sizeof("WrapKey_10a"), wrapKey, sizeof(wrapKey), serverKeyId)); WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1)); WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId)); @@ -1177,8 +1176,8 @@ static int _testWrappedKey_LocalWrap_GlobalKey_NonOwnerNoWrapKey( /* Client 1 caches a LOCAL wrapping key */ WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheRequest_ex( - client1, 0, (uint8_t*)"WrapKey_10b", sizeof("WrapKey_10b"), wrapKey, - sizeof(wrapKey), serverKeyId)); + client1, WH_NVM_FLAGS_USAGE_WRAP, (uint8_t*)"WrapKey_10b", + sizeof("WrapKey_10b"), wrapKey, sizeof(wrapKey), serverKeyId)); WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server1)); WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCacheResponse(client1, &serverKeyId)); diff --git a/wolfhsm/wh_common.h b/wolfhsm/wh_common.h index 029613a2..a62e0c77 100644 --- a/wolfhsm/wh_common.h +++ b/wolfhsm/wh_common.h @@ -66,14 +66,49 @@ typedef uint16_t whNvmAccess; /* HSM NVM Flags type */ typedef uint16_t whNvmFlags; -#define WH_NVM_FLAGS_NONE ((whNvmFlags)0) -#define WH_NVM_FLAGS_ANY ((whNvmFlags)-1) - -#define WH_NVM_FLAGS_IMMUTABLE ((whNvmFlags)1 << 0) /* Cannot be overwritten */ -#define WH_NVM_FLAGS_SENSITIVE ((whNvmFlags)1 << 1) /* Holds private/secret data */ -#define WH_NVM_FLAGS_NONEXPORTABLE ((whNvmFlags)1 << 2) /* Cannot be exported */ -#define WH_NVM_FLAGS_LOCAL ((whNvmFlags)1 << 3) /* Was generated locally */ -#define WH_NVM_FLAGS_EPHEMERAL ((whNvmFlags)1 << 4) /* Cannot be cached nor committed */ + +/* Generic NVM flags */ +/* Cannot be overwritten */ +#define WH_NVM_FLAGS_IMMUTABLE ((whNvmFlags)1 << 0) +/* Holds private/secret data */ +#define WH_NVM_FLAGS_SENSITIVE ((whNvmFlags)1 << 1) +/* Cannot be exported */ +#define WH_NVM_FLAGS_NONEXPORTABLE ((whNvmFlags)1 << 2) +/* Was generated locally */ +#define WH_NVM_FLAGS_LOCAL ((whNvmFlags)1 << 3) +/* Cannot be cached nor committed */ +#define WH_NVM_FLAGS_EPHEMERAL ((whNvmFlags)1 << 4) + +/* Key usage policy flags + * + * Key usage flags control which cryptographic operations are permitted. + * Multiple usage flags can be combined. If no usage flags are set, the key + * cannot be used for any operation. Use WH_NVM_FLAGS_USAGE_ANY to allow all + * operations. + */ +/* Key can be used for encryption */ +#define WH_NVM_FLAGS_USAGE_ENCRYPT ((whNvmFlags)1 << 5) +/* Key can be used for decryption */ +#define WH_NVM_FLAGS_USAGE_DECRYPT ((whNvmFlags)1 << 6) +/* Key can be used for signing */ +#define WH_NVM_FLAGS_USAGE_SIGN ((whNvmFlags)1 << 7) +/* Key can be used for verification */ +#define WH_NVM_FLAGS_USAGE_VERIFY ((whNvmFlags)1 << 8) +/* Key can be used for key wrapping */ +#define WH_NVM_FLAGS_USAGE_WRAP ((whNvmFlags)1 << 9) +/* Key can be used for key derivation */ +#define WH_NVM_FLAGS_USAGE_DERIVE ((whNvmFlags)1 << 10) + +/* No flags set */ +#define WH_NVM_FLAGS_NONE ((whNvmFlags)0) +/* All flags set */ +#define WH_NVM_FLAGS_ANY ((whNvmFlags)-1) +/* All usage flags set */ +#define WH_NVM_FLAGS_USAGE_ANY \ + ((whNvmFlags)(WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT | \ + WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY | \ + WH_NVM_FLAGS_USAGE_WRAP | \ + WH_NVM_FLAGS_USAGE_DERIVE)) /* HSM NVM metadata structure */ enum WH_NVM_ENUM { diff --git a/wolfhsm/wh_error.h b/wolfhsm/wh_error.h index c2b7682f..9eb1f07c 100644 --- a/wolfhsm/wh_error.h +++ b/wolfhsm/wh_error.h @@ -43,6 +43,8 @@ enum WH_ERROR_ENUM { WH_ERROR_NOHANDLER = -2007, /* No customcb handler registered */ WH_ERROR_NOTIMPL = -2008, /* Functionality not implemented given the compile-time configuration */ + WH_ERROR_USAGE = + -2009, /* Operation not permitted based on object/key usage flags */ /* NVM and keystore specific status returns */ WH_ERROR_LOCKED = -2100, /* Unlock and retry if necessary */ diff --git a/wolfhsm/wh_server_keystore.h b/wolfhsm/wh_server_keystore.h index 2cf0d664..d891b180 100644 --- a/wolfhsm/wh_server_keystore.h +++ b/wolfhsm/wh_server_keystore.h @@ -190,4 +190,45 @@ int wh_Server_KeystoreExportKeyDma(whServerContext* server, whKeyId keyId, uint64_t keyAddr, uint64_t keySz, whNvmMetadata* outMeta); + +/** + * @brief Enforce key usage policy given metadata + * + * Validates that a key has the required usage policy flags set in its metadata. + * This is a pure policy check function that does not perform any key lookups. + * Use this when you already have the key metadata available to avoid duplicate + * key freshening operations. + * + * @param[in] meta Pointer to key metadata + * @param[in] requiredUsage Required usage policy flags (e.g., + * WH_NVM_FLAGS_USAGE_ENCRYPT | + * WH_NVM_FLAGS_USAGE_DECRYPT) + * @return WH_ERROR_OK if the key has all required usage flags set + * @return WH_ERROR_USAGE if the key does not have the required flags + * @return WH_ERROR_BADARGS if meta is NULL + */ +int wh_Server_KeystoreEnforceKeyUsage(const whNvmMetadata* meta, + whNvmFlags requiredUsage); + +/** + * Validates that a key has the required usage policy flags set + * + * This function enforces key usage policies by checking that the specified + * key has all the required usage flags set in its metadata. It retrieves + * the key metadata from the cache or NVM storage and performs a bitwise + * check against the required flags. + * + * @param server Pointer to the server context + * @param keyId The translated server keyId (after client keyId translation) + * @param requiredUsage The required usage policy flags (e.g., + * WH_NVM_FLAGS_USAGE_ENCRYPT | WH_NVM_FLAGS_USAGE_DECRYPT) + * + * @return WH_ERROR_OK if the key has all required usage flags set + * @return WH_ERROR_USAGE if the key does not have the required flags + * @return Other error codes if key metadata cannot be retrieved + */ +int wh_Server_KeystoreFindEnforceKeyUsage(whServerContext* server, + whKeyId keyId, + whNvmFlags requiredUsage); + #endif /* !WOLFHSM_WH_SERVER_KEYSTORE_H_ */ From 264d9a81e556136692a8de42a664d3cf2132fb04 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:07:14 -0700 Subject: [PATCH 2/2] relax NULL checks in freshen key and helper function to support optional output arguments --- src/wh_server_keystore.c | 52 +++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index 5fc488ed..720020a9 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -144,8 +144,10 @@ static int _GetKeyCacheSlot(whKeyCacheContext* ctx, uint16_t keySz, { int foundIndex = -1; int i; + uint8_t* slotBuf = NULL; + whNvmMetadata* slotMeta = NULL; - if (ctx == NULL || outBuf == NULL || outMeta == NULL) { + if (ctx == NULL) { return WH_ERROR_BADARGS; } @@ -169,11 +171,11 @@ static int _GetKeyCacheSlot(whKeyCacheContext* ctx, uint16_t keySz, } } - /* Zero slot and return pointers */ + /* Zero slot and capture pointers */ if (foundIndex >= 0) { memset(&ctx->cache[foundIndex], 0, sizeof(whCacheSlot)); - *outBuf = ctx->cache[foundIndex].buffer; - *outMeta = ctx->cache[foundIndex].meta; + slotBuf = ctx->cache[foundIndex].buffer; + slotMeta = ctx->cache[foundIndex].meta; } } else { @@ -195,11 +197,11 @@ static int _GetKeyCacheSlot(whKeyCacheContext* ctx, uint16_t keySz, } } - /* Zero slot and return pointers */ + /* Zero slot and capture pointers */ if (foundIndex >= 0) { memset(&ctx->bigCache[foundIndex], 0, sizeof(whBigCacheSlot)); - *outBuf = ctx->bigCache[foundIndex].buffer; - *outMeta = ctx->bigCache[foundIndex].meta; + slotBuf = ctx->bigCache[foundIndex].buffer; + slotMeta = ctx->bigCache[foundIndex].meta; } } @@ -207,6 +209,14 @@ static int _GetKeyCacheSlot(whKeyCacheContext* ctx, uint16_t keySz, return WH_ERROR_NOSPACE; } + /* Copy out pointers only if caller provided non-NULL output parameters */ + if (outBuf != NULL) { + *outBuf = slotBuf; + } + if (outMeta != NULL) { + *outMeta = slotMeta; + } + return WH_ERROR_OK; } @@ -490,17 +500,25 @@ static int _ExistsInCache(whServerContext* server, whKeyId keyId) int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId, uint8_t** outBuf, whNvmMetadata** outMeta) { - int ret = 0; - int foundIndex = -1; - int foundBigIndex = -1; - whNvmMetadata tmpMeta[1]; + int ret = 0; + int foundIndex = -1; + int foundBigIndex = -1; + uint8_t* cacheBufLocal = NULL; + whNvmMetadata* cacheMetaLocal = NULL; + uint8_t** cacheBufOut; + whNvmMetadata** cacheMetaOut; + whNvmMetadata tmpMeta[1]; if ((server == NULL) || WH_KEYID_ISERASED(keyId)) { return WH_ERROR_BADARGS; } - ret = _FindInCache(server, keyId, &foundIndex, &foundBigIndex, outBuf, - outMeta); + /* Use local buffers to allow for optional (NULL) output parameters */ + cacheBufOut = (outBuf != NULL) ? outBuf : (uint8_t**)&cacheBufLocal; + cacheMetaOut = (outMeta != NULL) ? outMeta : &cacheMetaLocal; + + 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 @@ -514,19 +532,21 @@ int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId, if (ret == WH_ERROR_OK) { /* Key found in NVM, get a free cache slot */ ret = wh_Server_KeystoreGetCacheSlot(server, keyId, tmpMeta->len, - outBuf, outMeta); + cacheBufOut, cacheMetaOut); if (ret == WH_ERROR_OK) { /* Read the key from NVM into the cache slot */ - ret = wh_Nvm_Read(server->nvm, keyId, 0, tmpMeta->len, *outBuf); + 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*)*outMeta, (uint8_t*)tmpMeta, + memcpy((uint8_t*)*cacheMetaOut, (uint8_t*)tmpMeta, sizeof(whNvmMetadata)); } } } } + return ret; }