diff --git a/src/wh_client_crypto.c b/src/wh_client_crypto.c index 22f52360f..118d5a9a7 100644 --- a/src/wh_client_crypto.c +++ b/src/wh_client_crypto.c @@ -3404,6 +3404,11 @@ int wh_Client_Curve25519MakeCacheKey(whClientContext* ctx, uint16_t size, return WH_ERROR_BADARGS; } + /* Ephemeral keygen belongs to the export path, not the cache path. */ + if (flags & WH_NVM_FLAGS_EPHEMERAL) { + return WH_ERROR_BADARGS; + } + return _Curve25519MakeKey(ctx, size, inout_key_id, flags, label, label_len, NULL); } @@ -3920,6 +3925,11 @@ int wh_Client_Ed25519MakeCacheKey(whClientContext* ctx, whKeyId* inout_key_id, return WH_ERROR_BADARGS; } + /* Ephemeral keygen belongs to the export path, not the cache path. */ + if (flags & WH_NVM_FLAGS_EPHEMERAL) { + return WH_ERROR_BADARGS; + } + return _Ed25519MakeKey(ctx, inout_key_id, flags, label_len, label, NULL); } @@ -10324,6 +10334,11 @@ int wh_Client_MlDsaMakeCacheKey(whClientContext* ctx, int size, int level, return WH_ERROR_BADARGS; } + /* Ephemeral keygen belongs to the export path, not the cache path. */ + if (flags & WH_NVM_FLAGS_EPHEMERAL) { + return WH_ERROR_BADARGS; + } + return _MlDsaMakeKey(ctx, size, level, inout_key_id, flags, label_len, label, NULL); } @@ -11503,6 +11518,11 @@ int wh_Client_MlKemMakeCacheKey(whClientContext* ctx, int level, return WH_ERROR_BADARGS; } + /* Ephemeral keygen belongs to the export path, not the cache path. */ + if (flags & WH_NVM_FLAGS_EPHEMERAL) { + return WH_ERROR_BADARGS; + } + return _MlKemMakeKey(ctx, level, inout_key_id, flags, label_len, label, NULL); } diff --git a/test-refactor/client-server/wh_test_crypto_curve25519.c b/test-refactor/client-server/wh_test_crypto_curve25519.c index 0bd8ea6eb..7b35a556b 100644 --- a/test-refactor/client-server/wh_test_crypto_curve25519.c +++ b/test-refactor/client-server/wh_test_crypto_curve25519.c @@ -467,11 +467,30 @@ static int _whTest_CryptoCurve25519CacheKeyAndExportPublic(whClientContext* ctx) return ret; } +/* Cache keygen must reject WH_NVM_FLAGS_EPHEMERAL */ +static int _whTest_CryptoCurve25519MakeCacheKeyEphemeral(whClientContext* ctx) +{ + whKeyId keyId = WH_KEYID_ERASED; + int ret; + + ret = wh_Client_Curve25519MakeCacheKey(ctx, CURVE25519_KEYSIZE, &keyId, + WH_NVM_FLAGS_EPHEMERAL, NULL, 0); + if (ret != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("Curve25519MakeCacheKey with EPHEMERAL returned %d " + "(expected BADARGS)\n", + ret); + return WH_TEST_FAIL; + } + WH_TEST_PRINT("CURVE25519 CACHE-KEY EPHEMERAL REJECT SUCCESS\n"); + return 0; +} + int whTest_Crypto_Curve25519(whClientContext* ctx) { WH_TEST_RETURN_ON_FAIL(_whTest_CryptoCurve25519(ctx)); WH_TEST_RETURN_ON_FAIL(_whTest_CryptoCurve25519ExportPublicKey(ctx)); WH_TEST_RETURN_ON_FAIL(_whTest_CryptoCurve25519CacheKeyAndExportPublic(ctx)); + WH_TEST_RETURN_ON_FAIL(_whTest_CryptoCurve25519MakeCacheKeyEphemeral(ctx)); return 0; } #endif /* HAVE_CURVE25519 */ diff --git a/test-refactor/client-server/wh_test_crypto_ed25519.c b/test-refactor/client-server/wh_test_crypto_ed25519.c index a3eb56921..3a779d835 100644 --- a/test-refactor/client-server/wh_test_crypto_ed25519.c +++ b/test-refactor/client-server/wh_test_crypto_ed25519.c @@ -727,6 +727,24 @@ static int _whTest_CryptoEd25519BufferTooSmall(whClientContext* ctx) return ret; } +/* Cache keygen must reject WH_NVM_FLAGS_EPHEMERAL */ +static int _whTest_CryptoEd25519MakeCacheKeyEphemeral(whClientContext* ctx) +{ + whKeyId keyId = WH_KEYID_ERASED; + int ret; + + ret = wh_Client_Ed25519MakeCacheKey(ctx, &keyId, WH_NVM_FLAGS_EPHEMERAL, 0, + NULL); + if (ret != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("Ed25519MakeCacheKey with EPHEMERAL returned %d " + "(expected BADARGS)\n", + ret); + return WH_TEST_FAIL; + } + WH_TEST_PRINT("Ed25519 CACHE-KEY EPHEMERAL REJECT SUCCESS\n"); + return 0; +} + int whTest_Crypto_Ed25519(whClientContext* ctx) { WH_TEST_RETURN_ON_FAIL(_whTest_CryptoEd25519Inline(ctx)); @@ -737,6 +755,7 @@ int whTest_Crypto_Ed25519(whClientContext* ctx) WH_TEST_RETURN_ON_FAIL(_whTest_CryptoEd25519ExportPublicKey(ctx)); WH_TEST_RETURN_ON_FAIL(_whTest_CryptoEd25519CacheKeyAndExportPublic(ctx)); WH_TEST_RETURN_ON_FAIL(_whTest_CryptoEd25519BufferTooSmall(ctx)); + WH_TEST_RETURN_ON_FAIL(_whTest_CryptoEd25519MakeCacheKeyEphemeral(ctx)); return 0; } #endif /* HAVE_ED25519 */ diff --git a/test-refactor/client-server/wh_test_crypto_mldsa.c b/test-refactor/client-server/wh_test_crypto_mldsa.c index 636962c2a..64f734bd1 100644 --- a/test-refactor/client-server/wh_test_crypto_mldsa.c +++ b/test-refactor/client-server/wh_test_crypto_mldsa.c @@ -1319,6 +1319,24 @@ static int _whTest_CryptoMlDsaBufferTooSmall(whClientContext* ctx) wc_MlDsaKey_Free(key); return ret; } + +/* Cache keygen must reject WH_NVM_FLAGS_EPHEMERAL */ +static int _whTest_CryptoMlDsaMakeCacheKeyEphemeral(whClientContext* ctx) +{ + whKeyId keyId = WH_KEYID_ERASED; + int ret; + + ret = wh_Client_MlDsaMakeCacheKey(ctx, 0, WC_ML_DSA_44, &keyId, + WH_NVM_FLAGS_EPHEMERAL, 0, NULL); + if (ret != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("MlDsaMakeCacheKey with EPHEMERAL returned %d " + "(expected BADARGS)\n", + ret); + return WH_TEST_FAIL; + } + WH_TEST_PRINT("ML-DSA CACHE-KEY EPHEMERAL REJECT SUCCESS\n"); + return 0; +} #endif /* make/sign/verify && ML_DSA_44 */ int whTest_Crypto_MlDsa(whClientContext* ctx) @@ -1345,6 +1363,7 @@ int whTest_Crypto_MlDsa(whClientContext* ctx) WH_TEST_RETURN_ON_FAIL(_whTest_CryptoMlDsaCacheKeyAndExportPublic(ctx)); #endif WH_TEST_RETURN_ON_FAIL(_whTest_CryptoMlDsaBufferTooSmall(ctx)); + WH_TEST_RETURN_ON_FAIL(_whTest_CryptoMlDsaMakeCacheKeyEphemeral(ctx)); #ifdef WOLFHSM_CFG_DMA WH_TEST_RETURN_ON_FAIL(_whTest_CryptoMlDsaDmaClient(ctx)); WH_TEST_RETURN_ON_FAIL(_whTest_CryptoMlDsaExportPublicKeyDma(ctx)); diff --git a/test-refactor/client-server/wh_test_crypto_mlkem.c b/test-refactor/client-server/wh_test_crypto_mlkem.c new file mode 100644 index 000000000..7a252bfb1 --- /dev/null +++ b/test-refactor/client-server/wh_test_crypto_mlkem.c @@ -0,0 +1,82 @@ +/* + * 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_crypto_mlkem.c + * + * ML-KEM tests routed through the server: + * _whTest_CryptoMlKemMakeCacheKeyEphemeral - cache keygen rejects + * WH_NVM_FLAGS_EPHEMERAL + */ + +#include "wolfhsm/wh_settings.h" + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) + +#include + +#include "wolfssl/wolfcrypt/settings.h" +#include "wolfssl/wolfcrypt/types.h" +#include "wolfssl/wolfcrypt/wc_mlkem.h" + +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_common.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_client_crypto.h" + +#include "wh_test_common.h" +#include "wh_test_list.h" + +#ifdef WOLFSSL_HAVE_MLKEM + +/* Cache keygen must reject WH_NVM_FLAGS_EPHEMERAL */ +static int _whTest_CryptoMlKemMakeCacheKeyEphemeral(whClientContext* ctx) +{ + whKeyId keyId = WH_KEYID_ERASED; + int ret; + /* Valid level so the server level check cannot mask the gate */ + const int level = +#if !defined(WOLFSSL_NO_ML_KEM_512) + WC_ML_KEM_512; +#elif !defined(WOLFSSL_NO_ML_KEM_768) + WC_ML_KEM_768; +#else + WC_ML_KEM_1024; +#endif + + ret = wh_Client_MlKemMakeCacheKey(ctx, level, &keyId, + WH_NVM_FLAGS_EPHEMERAL, 0, NULL); + if (ret != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("MlKemMakeCacheKey with EPHEMERAL returned %d " + "(expected BADARGS)\n", + ret); + return WH_TEST_FAIL; + } + WH_TEST_PRINT("ML-KEM CACHE-KEY EPHEMERAL REJECT SUCCESS\n"); + return 0; +} + +int whTest_Crypto_MlKem(whClientContext* ctx) +{ + WH_TEST_RETURN_ON_FAIL(_whTest_CryptoMlKemMakeCacheKeyEphemeral(ctx)); + return 0; +} + +#endif /* WOLFSSL_HAVE_MLKEM */ + +#endif /* !WOLFHSM_CFG_NO_CRYPTO */ diff --git a/test-refactor/wh_test_list.c b/test-refactor/wh_test_list.c index fa2994089..a6824e987 100644 --- a/test-refactor/wh_test_list.c +++ b/test-refactor/wh_test_list.c @@ -74,6 +74,7 @@ WH_TEST_DECL(whTest_Crypto_KeyWrap); WH_TEST_DECL(whTest_Crypto_Keystore); WH_TEST_DECL(whTest_Crypto_Lms); WH_TEST_DECL(whTest_Crypto_MlDsa); +WH_TEST_DECL(whTest_Crypto_MlKem); WH_TEST_DECL(whTest_Crypto_Rng); WH_TEST_DECL(whTest_Crypto_Rsa); WH_TEST_DECL(whTest_Crypto_Sha); @@ -161,6 +162,7 @@ const whTestCase whTestsClient[] = { {"whTest_Crypto_Keystore", whTest_Crypto_Keystore}, {"whTest_Crypto_Lms", whTest_Crypto_Lms}, {"whTest_Crypto_MlDsa", whTest_Crypto_MlDsa}, + {"whTest_Crypto_MlKem", whTest_Crypto_MlKem}, {"whTest_Crypto_Rng", whTest_Crypto_Rng}, {"whTest_Crypto_Rsa", whTest_Crypto_Rsa}, {"whTest_Crypto_Sha", whTest_Crypto_Sha}, diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index b688bc811..d4640e642 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -17958,10 +17958,7 @@ int whTest_CryptoKeyRevocationAesCbc(whClientContext* client, WC_RNG* rng) #endif /* !NO_AES && HAVE_AES_CBC && \ WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS */ -/* Negative tests: every cache-and-export keygen function must reject - * WH_NVM_FLAGS_EPHEMERAL, a NULL inout_key_id, and a NULL pub with - * WH_ERROR_BADARGS, before contacting the server. level is passed as 0 for the - * PQC calls since the argument guards run before any level validation. */ +/* Cache keygen must reject EPHEMERAL flags and NULL args with BADARGS */ static int whTest_CryptoMakeCacheKeyExportPublicArgs(whClientContext* ctx) { int ret = 0; @@ -18010,9 +18007,12 @@ static int whTest_CryptoMakeCacheKeyExportPublicArgs(whClientContext* ctx) #ifdef HAVE_CURVE25519 if (ret == 0) { curve25519_key cv[1] = {0}; - if (wh_Client_Curve25519MakeCacheKeyAndExportPublic( - ctx, CURVE25519_KEYSIZE, &keyId, WH_NVM_FLAGS_EPHEMERAL, NULL, 0, - cv) != WH_ERROR_BADARGS || + if (wh_Client_Curve25519MakeCacheKey(ctx, CURVE25519_KEYSIZE, &keyId, + WH_NVM_FLAGS_EPHEMERAL, NULL, + 0) != WH_ERROR_BADARGS || + wh_Client_Curve25519MakeCacheKeyAndExportPublic( + ctx, CURVE25519_KEYSIZE, &keyId, WH_NVM_FLAGS_EPHEMERAL, NULL, + 0, cv) != WH_ERROR_BADARGS || wh_Client_Curve25519MakeCacheKeyAndExportPublic( ctx, CURVE25519_KEYSIZE, NULL, WH_NVM_FLAGS_NONE, NULL, 0, cv) != WH_ERROR_BADARGS || @@ -18027,11 +18027,14 @@ static int whTest_CryptoMakeCacheKeyExportPublicArgs(whClientContext* ctx) #ifdef HAVE_ED25519 if (ret == 0) { ed25519_key ed[1] = {0}; - if (wh_Client_Ed25519MakeCacheKeyAndExportPublic( + if (wh_Client_Ed25519MakeCacheKey(ctx, &keyId, WH_NVM_FLAGS_EPHEMERAL, + 0, NULL) != WH_ERROR_BADARGS || + wh_Client_Ed25519MakeCacheKeyAndExportPublic( ctx, &keyId, WH_NVM_FLAGS_EPHEMERAL, 0, NULL, ed) != WH_ERROR_BADARGS || wh_Client_Ed25519MakeCacheKeyAndExportPublic( - ctx, NULL, WH_NVM_FLAGS_NONE, 0, NULL, ed) != WH_ERROR_BADARGS || + ctx, NULL, WH_NVM_FLAGS_NONE, 0, NULL, ed) != + WH_ERROR_BADARGS || wh_Client_Ed25519MakeCacheKeyAndExportPublic( ctx, &keyId, WH_NVM_FLAGS_NONE, 0, NULL, NULL) != WH_ERROR_BADARGS) { @@ -18043,7 +18046,19 @@ static int whTest_CryptoMakeCacheKeyExportPublicArgs(whClientContext* ctx) #ifdef WOLFSSL_MLDSA_PUBLIC_KEY if (ret == 0) { wc_MlDsaKey mldsa[1] = {0}; - if (wh_Client_MlDsaMakeCacheKeyAndExportPublic( + /* Valid level so the server level check cannot mask the gate */ + const int mldsaLevel = +#if !defined(WOLFSSL_NO_ML_DSA_44) + WC_ML_DSA_44; +#elif !defined(WOLFSSL_NO_ML_DSA_65) + WC_ML_DSA_65; +#else + WC_ML_DSA_87; +#endif + if (wh_Client_MlDsaMakeCacheKey(ctx, 0, mldsaLevel, &keyId, + WH_NVM_FLAGS_EPHEMERAL, 0, + NULL) != WH_ERROR_BADARGS || + wh_Client_MlDsaMakeCacheKeyAndExportPublic( ctx, 0, 0, &keyId, WH_NVM_FLAGS_EPHEMERAL, 0, NULL, mldsa) != WH_ERROR_BADARGS || wh_Client_MlDsaMakeCacheKeyAndExportPublic( @@ -18075,7 +18090,19 @@ static int whTest_CryptoMakeCacheKeyExportPublicArgs(whClientContext* ctx) #ifdef WOLFSSL_HAVE_MLKEM if (ret == 0) { MlKemKey mlkem[1] = {0}; - if (wh_Client_MlKemMakeCacheKeyAndExportPublic( + /* Valid level so the server level check cannot mask the gate */ + const int mlkemLevel = +#if !defined(WOLFSSL_NO_ML_KEM_512) + WC_ML_KEM_512; +#elif !defined(WOLFSSL_NO_ML_KEM_768) + WC_ML_KEM_768; +#else + WC_ML_KEM_1024; +#endif + if (wh_Client_MlKemMakeCacheKey(ctx, mlkemLevel, &keyId, + WH_NVM_FLAGS_EPHEMERAL, 0, + NULL) != WH_ERROR_BADARGS || + wh_Client_MlKemMakeCacheKeyAndExportPublic( ctx, 0, &keyId, WH_NVM_FLAGS_EPHEMERAL, 0, NULL, mlkem) != WH_ERROR_BADARGS || wh_Client_MlKemMakeCacheKeyAndExportPublic(