diff --git a/benchmark/README.md b/benchmark/README.md
index 71e7e7321..b46fb0e04 100644
--- a/benchmark/README.md
+++ b/benchmark/README.md
@@ -10,7 +10,7 @@ The wolfHSM benchmarks provide a framework for testing and measuring the perform
- Hash functions (SHA-2, SHA-3)
- Message Authentication Codes (HMAC, CMAC)
- Public Key Cryptography (RSA, ECC, Curve25519)
-- Post-Quantum Cryptography (ML-DSA)
+- Post-Quantum Cryptography (ML-DSA, ML-KEM)
- Basic communication (Echo)
The benchmark system measures the runtime of registered operations, as well as reports the throughput in either operations per second or bytes per second depending on the algorithm.
diff --git a/benchmark/bench_modules/wh_bench_mod_all.h b/benchmark/bench_modules/wh_bench_mod_all.h
index 458da0104..629be885a 100644
--- a/benchmark/bench_modules/wh_bench_mod_all.h
+++ b/benchmark/bench_modules/wh_bench_mod_all.h
@@ -379,4 +379,49 @@ int wh_Bench_Mod_MlDsa87KeyGen(whClientContext* client, whBenchOpContext* ctx,
int wh_Bench_Mod_MlDsa87KeyGenDma(whClientContext* client,
whBenchOpContext* ctx, int id, void* params);
+/*
+ * ML-KEM benchmark module prototypes (wh_bench_mod_mlkem.c)
+ */
+int wh_Bench_Mod_MlKem512KeyGen(whClientContext* client, whBenchOpContext* ctx,
+ int id, void* params);
+int wh_Bench_Mod_MlKem512KeyGenDma(whClientContext* client,
+ whBenchOpContext* ctx, int id, void* params);
+int wh_Bench_Mod_MlKem512Encaps(whClientContext* client, whBenchOpContext* ctx,
+ int id, void* params);
+int wh_Bench_Mod_MlKem512EncapsDma(whClientContext* client,
+ whBenchOpContext* ctx, int id, void* params);
+int wh_Bench_Mod_MlKem512Decaps(whClientContext* client, whBenchOpContext* ctx,
+ int id, void* params);
+int wh_Bench_Mod_MlKem512DecapsDma(whClientContext* client,
+ whBenchOpContext* ctx, int id, void* params);
+
+int wh_Bench_Mod_MlKem768KeyGen(whClientContext* client, whBenchOpContext* ctx,
+ int id, void* params);
+int wh_Bench_Mod_MlKem768KeyGenDma(whClientContext* client,
+ whBenchOpContext* ctx, int id, void* params);
+int wh_Bench_Mod_MlKem768Encaps(whClientContext* client, whBenchOpContext* ctx,
+ int id, void* params);
+int wh_Bench_Mod_MlKem768EncapsDma(whClientContext* client,
+ whBenchOpContext* ctx, int id, void* params);
+int wh_Bench_Mod_MlKem768Decaps(whClientContext* client, whBenchOpContext* ctx,
+ int id, void* params);
+int wh_Bench_Mod_MlKem768DecapsDma(whClientContext* client,
+ whBenchOpContext* ctx, int id, void* params);
+
+int wh_Bench_Mod_MlKem1024KeyGen(whClientContext* client,
+ whBenchOpContext* ctx, int id, void* params);
+int wh_Bench_Mod_MlKem1024KeyGenDma(whClientContext* client,
+ whBenchOpContext* ctx, int id,
+ void* params);
+int wh_Bench_Mod_MlKem1024Encaps(whClientContext* client,
+ whBenchOpContext* ctx, int id, void* params);
+int wh_Bench_Mod_MlKem1024EncapsDma(whClientContext* client,
+ whBenchOpContext* ctx, int id,
+ void* params);
+int wh_Bench_Mod_MlKem1024Decaps(whClientContext* client,
+ whBenchOpContext* ctx, int id, void* params);
+int wh_Bench_Mod_MlKem1024DecapsDma(whClientContext* client,
+ whBenchOpContext* ctx, int id,
+ void* params);
+
#endif /* WH_BENCH_MOD_ALL_H_ */
diff --git a/benchmark/bench_modules/wh_bench_mod_mlkem.c b/benchmark/bench_modules/wh_bench_mod_mlkem.c
new file mode 100644
index 000000000..5d75b6a8c
--- /dev/null
+++ b/benchmark/bench_modules/wh_bench_mod_mlkem.c
@@ -0,0 +1,343 @@
+/*
+ * 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 .
+ */
+#include
+
+#include "wh_bench_mod.h"
+#include "wolfhsm/wh_error.h"
+#include "wolfhsm/wh_client.h"
+#include "wolfhsm/wh_client_crypto.h"
+
+#if !defined(WOLFHSM_CFG_NO_CRYPTO) && defined(WOLFHSM_CFG_BENCH_ENABLE)
+#include "wolfssl/wolfcrypt/wc_mlkem.h"
+
+#if defined(WOLFSSL_HAVE_MLKEM)
+
+static int _benchMlKemKeyGen(whClientContext* client, whBenchOpContext* ctx,
+ int id, int securityLevel, int devId)
+{
+ int ret = WH_ERROR_OK;
+ int i;
+
+ for (i = 0; i < WOLFHSM_CFG_BENCH_KG_ITERS && ret == WH_ERROR_OK; i++) {
+ MlKemKey key[1];
+ int benchStartRet;
+ int benchStopRet;
+
+ ret = wc_MlKemKey_Init(key, securityLevel, NULL, devId);
+ if (ret != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed to wc_MlKemKey_Init %d\n", ret);
+ break;
+ }
+
+ benchStartRet = wh_Bench_StartOp(ctx, id);
+#ifdef WOLFHSM_CFG_DMA
+ if (devId == WH_DEV_ID_DMA) {
+ ret = wh_Client_MlKemMakeExportKeyDma(client, securityLevel, key);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_MlKemMakeExportKey(client, securityLevel, key);
+ }
+ benchStopRet = wh_Bench_StopOp(ctx, id);
+
+ if (benchStartRet != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed to wh_Bench_StartOp %d\n", benchStartRet);
+ ret = benchStartRet;
+ }
+ else if (ret != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed ML-KEM keygen %d\n", ret);
+ }
+ else if (benchStopRet != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed to wh_Bench_StopOp %d\n", benchStopRet);
+ ret = benchStopRet;
+ }
+
+ wc_MlKemKey_Free(key);
+ }
+
+ return ret;
+}
+
+static int _benchMlKemEncaps(whClientContext* client, whBenchOpContext* ctx,
+ int id, int securityLevel, int devId)
+{
+ int ret = WH_ERROR_OK;
+ int i;
+ MlKemKey key[1];
+ byte ct[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
+ byte ss[WC_ML_KEM_SS_SZ];
+
+ ret = wc_MlKemKey_Init(key, securityLevel, NULL, devId);
+ if (ret != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed to wc_MlKemKey_Init %d\n", ret);
+ return ret;
+ }
+
+#ifdef WOLFHSM_CFG_DMA
+ if (devId == WH_DEV_ID_DMA) {
+ ret = wh_Client_MlKemMakeExportKeyDma(client, securityLevel, key);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_MlKemMakeExportKey(client, securityLevel, key);
+ }
+ if (ret != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed ML-KEM key setup %d\n", ret);
+ wc_MlKemKey_Free(key);
+ return ret;
+ }
+
+ for (i = 0; i < WOLFHSM_CFG_BENCH_PK_ITERS && ret == WH_ERROR_OK; i++) {
+ word32 ctLen = sizeof(ct);
+ word32 ssLen = sizeof(ss);
+ int benchStartRet;
+ int benchStopRet;
+
+ memset(ct, 0, sizeof(ct));
+ memset(ss, 0, sizeof(ss));
+
+ benchStartRet = wh_Bench_StartOp(ctx, id);
+#ifdef WOLFHSM_CFG_DMA
+ if (devId == WH_DEV_ID_DMA) {
+ ret = wh_Client_MlKemEncapsulateDma(client, key, ct, &ctLen, ss,
+ &ssLen);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_MlKemEncapsulate(client, key, ct, &ctLen, ss, &ssLen);
+ }
+ benchStopRet = wh_Bench_StopOp(ctx, id);
+
+ if (benchStartRet != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed to wh_Bench_StartOp %d\n", benchStartRet);
+ ret = benchStartRet;
+ }
+ else if (ret != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed ML-KEM encapsulate %d\n", ret);
+ }
+ else if (benchStopRet != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed to wh_Bench_StopOp %d\n", benchStopRet);
+ ret = benchStopRet;
+ }
+ }
+
+ wc_MlKemKey_Free(key);
+ return ret;
+}
+
+static int _benchMlKemDecaps(whClientContext* client, whBenchOpContext* ctx,
+ int id, int securityLevel, int devId)
+{
+ int ret = WH_ERROR_OK;
+ int i;
+ MlKemKey key[1];
+ byte ct[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
+ byte ssEnc[WC_ML_KEM_SS_SZ];
+ byte ssDec[WC_ML_KEM_SS_SZ];
+ word32 ctLen = sizeof(ct);
+ word32 ssEncLen = sizeof(ssEnc);
+
+ ret = wc_MlKemKey_Init(key, securityLevel, NULL, devId);
+ if (ret != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed to wc_MlKemKey_Init %d\n", ret);
+ return ret;
+ }
+
+#ifdef WOLFHSM_CFG_DMA
+ if (devId == WH_DEV_ID_DMA) {
+ ret = wh_Client_MlKemMakeExportKeyDma(client, securityLevel, key);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_MlKemMakeExportKey(client, securityLevel, key);
+ }
+ if (ret != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed ML-KEM key setup %d\n", ret);
+ wc_MlKemKey_Free(key);
+ return ret;
+ }
+
+#ifdef WOLFHSM_CFG_DMA
+ if (devId == WH_DEV_ID_DMA) {
+ ret = wh_Client_MlKemEncapsulateDma(client, key, ct, &ctLen, ssEnc,
+ &ssEncLen);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_MlKemEncapsulate(client, key, ct, &ctLen, ssEnc,
+ &ssEncLen);
+ }
+ if (ret != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed ML-KEM setup encapsulate %d\n", ret);
+ wc_MlKemKey_Free(key);
+ return ret;
+ }
+
+ for (i = 0; i < WOLFHSM_CFG_BENCH_PK_ITERS && ret == WH_ERROR_OK; i++) {
+ word32 ssDecLen = sizeof(ssDec);
+ int benchStartRet;
+ int benchStopRet;
+
+ memset(ssDec, 0, sizeof(ssDec));
+
+ benchStartRet = wh_Bench_StartOp(ctx, id);
+#ifdef WOLFHSM_CFG_DMA
+ if (devId == WH_DEV_ID_DMA) {
+ ret = wh_Client_MlKemDecapsulateDma(client, key, ct, ctLen, ssDec,
+ &ssDecLen);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_MlKemDecapsulate(client, key, ct, ctLen, ssDec,
+ &ssDecLen);
+ }
+ benchStopRet = wh_Bench_StopOp(ctx, id);
+
+ if (benchStartRet != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed to wh_Bench_StartOp %d\n", benchStartRet);
+ ret = benchStartRet;
+ }
+ else if (ret != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed ML-KEM decapsulate %d\n", ret);
+ }
+ else if ((ssDecLen != ssEncLen) ||
+ (memcmp(ssDec, ssEnc, ssEncLen) != 0)) {
+ WH_BENCH_PRINTF("ML-KEM decapsulate mismatch\n");
+ ret = WH_ERROR_ABORTED;
+ }
+ else if (benchStopRet != WH_ERROR_OK) {
+ WH_BENCH_PRINTF("Failed to wh_Bench_StopOp %d\n", benchStopRet);
+ ret = benchStopRet;
+ }
+ }
+
+ wc_MlKemKey_Free(key);
+ return ret;
+}
+
+#define WH_DEFINE_MLKEM_BENCH_NON_DMA_FNS(_Suffix, _Level) \
+int wh_Bench_Mod_MlKem##_Suffix##KeyGen(whClientContext* client, \
+ whBenchOpContext* ctx, int id, \
+ void* params) \
+{ \
+ (void)params; \
+ return _benchMlKemKeyGen(client, ctx, id, _Level, WH_DEV_ID); \
+} \
+ \
+int wh_Bench_Mod_MlKem##_Suffix##Encaps(whClientContext* client, \
+ whBenchOpContext* ctx, int id, \
+ void* params) \
+{ \
+ (void)params; \
+ return _benchMlKemEncaps(client, ctx, id, _Level, WH_DEV_ID); \
+} \
+ \
+int wh_Bench_Mod_MlKem##_Suffix##Decaps(whClientContext* client, \
+ whBenchOpContext* ctx, int id, \
+ void* params) \
+{ \
+ (void)params; \
+ return _benchMlKemDecaps(client, ctx, id, _Level, WH_DEV_ID); \
+}
+
+#ifdef WOLFHSM_CFG_DMA
+#define WH_DEFINE_MLKEM_BENCH_DMA_FNS(_Suffix, _Level) \
+int wh_Bench_Mod_MlKem##_Suffix##KeyGenDma(whClientContext* client, \
+ whBenchOpContext* ctx, int id, \
+ void* params) \
+{ \
+ (void)params; \
+ return _benchMlKemKeyGen(client, ctx, id, _Level, WH_DEV_ID_DMA); \
+} \
+ \
+int wh_Bench_Mod_MlKem##_Suffix##EncapsDma(whClientContext* client, \
+ whBenchOpContext* ctx, int id, \
+ void* params) \
+{ \
+ (void)params; \
+ return _benchMlKemEncaps(client, ctx, id, _Level, WH_DEV_ID_DMA); \
+} \
+ \
+int wh_Bench_Mod_MlKem##_Suffix##DecapsDma(whClientContext* client, \
+ whBenchOpContext* ctx, int id, \
+ void* params) \
+{ \
+ (void)params; \
+ return _benchMlKemDecaps(client, ctx, id, _Level, WH_DEV_ID_DMA); \
+}
+#else
+#define WH_DEFINE_MLKEM_BENCH_DMA_FNS(_Suffix, _Level) \
+int wh_Bench_Mod_MlKem##_Suffix##KeyGenDma(whClientContext* client, \
+ whBenchOpContext* ctx, int id, \
+ void* params) \
+{ \
+ (void)client; \
+ (void)ctx; \
+ (void)id; \
+ (void)params; \
+ (void)_Level; \
+ return WH_ERROR_NOTIMPL; \
+} \
+ \
+int wh_Bench_Mod_MlKem##_Suffix##EncapsDma(whClientContext* client, \
+ whBenchOpContext* ctx, int id, \
+ void* params) \
+{ \
+ (void)client; \
+ (void)ctx; \
+ (void)id; \
+ (void)params; \
+ (void)_Level; \
+ return WH_ERROR_NOTIMPL; \
+} \
+ \
+int wh_Bench_Mod_MlKem##_Suffix##DecapsDma(whClientContext* client, \
+ whBenchOpContext* ctx, int id, \
+ void* params) \
+{ \
+ (void)client; \
+ (void)ctx; \
+ (void)id; \
+ (void)params; \
+ (void)_Level; \
+ return WH_ERROR_NOTIMPL; \
+}
+#endif /* WOLFHSM_CFG_DMA */
+
+#ifndef WOLFSSL_NO_ML_KEM_512
+WH_DEFINE_MLKEM_BENCH_NON_DMA_FNS(512, WC_ML_KEM_512)
+WH_DEFINE_MLKEM_BENCH_DMA_FNS(512, WC_ML_KEM_512)
+#endif
+#ifndef WOLFSSL_NO_ML_KEM_768
+WH_DEFINE_MLKEM_BENCH_NON_DMA_FNS(768, WC_ML_KEM_768)
+WH_DEFINE_MLKEM_BENCH_DMA_FNS(768, WC_ML_KEM_768)
+#endif
+#ifndef WOLFSSL_NO_ML_KEM_1024
+WH_DEFINE_MLKEM_BENCH_NON_DMA_FNS(1024, WC_ML_KEM_1024)
+WH_DEFINE_MLKEM_BENCH_DMA_FNS(1024, WC_ML_KEM_1024)
+#endif
+
+#endif /* WOLFSSL_HAVE_MLKEM */
+#endif /* !WOLFHSM_CFG_NO_CRYPTO && WOLFHSM_CFG_BENCH_ENABLE */
diff --git a/benchmark/config/user_settings.h b/benchmark/config/user_settings.h
index 81a5264a7..c2e5b6794 100644
--- a/benchmark/config/user_settings.h
+++ b/benchmark/config/user_settings.h
@@ -155,6 +155,10 @@ extern "C" {
#define WOLFSSL_DILITHIUM_NO_MAKE_KEY
#endif
+/* ML-KEM Options */
+#define WOLFSSL_HAVE_MLKEM
+#define WOLFSSL_WC_MLKEM
+
/** Composite features */
#define HAVE_HKDF
#define HAVE_CMAC_KDF
diff --git a/benchmark/wh_bench.c b/benchmark/wh_bench.c
index 15773f568..dc68f04df 100644
--- a/benchmark/wh_bench.c
+++ b/benchmark/wh_bench.c
@@ -254,6 +254,34 @@ typedef enum BenchModuleIdx {
BENCH_MODULE_IDX_ML_DSA_87_KEY_GEN_DMA,
#endif /* !(WOLFSSL_NO_ML_DSA_87) */
#endif /* HAVE_DILITHIUM */
+
+/* ML-KEM */
+#if defined(WOLFSSL_HAVE_MLKEM)
+#ifndef WOLFSSL_NO_ML_KEM_512
+ BENCH_MODULE_IDX_ML_KEM_512_KEY_GEN,
+ BENCH_MODULE_IDX_ML_KEM_512_KEY_GEN_DMA,
+ BENCH_MODULE_IDX_ML_KEM_512_ENCAPS,
+ BENCH_MODULE_IDX_ML_KEM_512_ENCAPS_DMA,
+ BENCH_MODULE_IDX_ML_KEM_512_DECAPS,
+ BENCH_MODULE_IDX_ML_KEM_512_DECAPS_DMA,
+#endif /* !WOLFSSL_NO_ML_KEM_512 */
+#ifndef WOLFSSL_NO_ML_KEM_768
+ BENCH_MODULE_IDX_ML_KEM_768_KEY_GEN,
+ BENCH_MODULE_IDX_ML_KEM_768_KEY_GEN_DMA,
+ BENCH_MODULE_IDX_ML_KEM_768_ENCAPS,
+ BENCH_MODULE_IDX_ML_KEM_768_ENCAPS_DMA,
+ BENCH_MODULE_IDX_ML_KEM_768_DECAPS,
+ BENCH_MODULE_IDX_ML_KEM_768_DECAPS_DMA,
+#endif /* !WOLFSSL_NO_ML_KEM_768 */
+#ifndef WOLFSSL_NO_ML_KEM_1024
+ BENCH_MODULE_IDX_ML_KEM_1024_KEY_GEN,
+ BENCH_MODULE_IDX_ML_KEM_1024_KEY_GEN_DMA,
+ BENCH_MODULE_IDX_ML_KEM_1024_ENCAPS,
+ BENCH_MODULE_IDX_ML_KEM_1024_ENCAPS_DMA,
+ BENCH_MODULE_IDX_ML_KEM_1024_DECAPS,
+ BENCH_MODULE_IDX_ML_KEM_1024_DECAPS_DMA,
+#endif /* !WOLFSSL_NO_ML_KEM_1024 */
+#endif /* WOLFSSL_HAVE_MLKEM */
#endif /* !(WOLFHSM_CFG_NO_CRYPTO) */
/* number of modules. This must be the last entry and will be used as the
* size of the global modules array */
@@ -440,6 +468,34 @@ static BenchModule g_benchModules[] = {
[BENCH_MODULE_IDX_ML_DSA_87_KEY_GEN_DMA] = {"ML-DSA-87-KEY-GEN-DMA", wh_Bench_Mod_MlDsa87KeyGenDma, BENCH_THROUGHPUT_OPS, 0, NULL},
#endif /* !(WOLFSSL_NO_ML_DSA_87) */
#endif /* HAVE_DILITHIUM */
+
+ /* ML-KEM */
+#if defined(WOLFSSL_HAVE_MLKEM)
+#ifndef WOLFSSL_NO_ML_KEM_512
+ [BENCH_MODULE_IDX_ML_KEM_512_KEY_GEN] = {"ML-KEM-512-KEY-GEN", wh_Bench_Mod_MlKem512KeyGen, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_512_KEY_GEN_DMA] = {"ML-KEM-512-KEY-GEN-DMA", wh_Bench_Mod_MlKem512KeyGenDma, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_512_ENCAPS] = {"ML-KEM-512-ENCAPS", wh_Bench_Mod_MlKem512Encaps, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_512_ENCAPS_DMA] = {"ML-KEM-512-ENCAPS-DMA", wh_Bench_Mod_MlKem512EncapsDma, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_512_DECAPS] = {"ML-KEM-512-DECAPS", wh_Bench_Mod_MlKem512Decaps, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_512_DECAPS_DMA] = {"ML-KEM-512-DECAPS-DMA", wh_Bench_Mod_MlKem512DecapsDma, BENCH_THROUGHPUT_OPS, 0, NULL},
+#endif /* !WOLFSSL_NO_ML_KEM_512 */
+#ifndef WOLFSSL_NO_ML_KEM_768
+ [BENCH_MODULE_IDX_ML_KEM_768_KEY_GEN] = {"ML-KEM-768-KEY-GEN", wh_Bench_Mod_MlKem768KeyGen, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_768_KEY_GEN_DMA] = {"ML-KEM-768-KEY-GEN-DMA", wh_Bench_Mod_MlKem768KeyGenDma, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_768_ENCAPS] = {"ML-KEM-768-ENCAPS", wh_Bench_Mod_MlKem768Encaps, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_768_ENCAPS_DMA] = {"ML-KEM-768-ENCAPS-DMA", wh_Bench_Mod_MlKem768EncapsDma, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_768_DECAPS] = {"ML-KEM-768-DECAPS", wh_Bench_Mod_MlKem768Decaps, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_768_DECAPS_DMA] = {"ML-KEM-768-DECAPS-DMA", wh_Bench_Mod_MlKem768DecapsDma, BENCH_THROUGHPUT_OPS, 0, NULL},
+#endif /* !WOLFSSL_NO_ML_KEM_768 */
+#ifndef WOLFSSL_NO_ML_KEM_1024
+ [BENCH_MODULE_IDX_ML_KEM_1024_KEY_GEN] = {"ML-KEM-1024-KEY-GEN", wh_Bench_Mod_MlKem1024KeyGen, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_1024_KEY_GEN_DMA] = {"ML-KEM-1024-KEY-GEN-DMA", wh_Bench_Mod_MlKem1024KeyGenDma, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_1024_ENCAPS] = {"ML-KEM-1024-ENCAPS", wh_Bench_Mod_MlKem1024Encaps, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_1024_ENCAPS_DMA] = {"ML-KEM-1024-ENCAPS-DMA", wh_Bench_Mod_MlKem1024EncapsDma, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_1024_DECAPS] = {"ML-KEM-1024-DECAPS", wh_Bench_Mod_MlKem1024Decaps, BENCH_THROUGHPUT_OPS, 0, NULL},
+ [BENCH_MODULE_IDX_ML_KEM_1024_DECAPS_DMA] = {"ML-KEM-1024-DECAPS-DMA", wh_Bench_Mod_MlKem1024DecapsDma, BENCH_THROUGHPUT_OPS, 0, NULL},
+#endif /* !WOLFSSL_NO_ML_KEM_1024 */
+#endif /* WOLFSSL_HAVE_MLKEM */
#endif /* !(WOLFHSM_CFG_NO_CRYPTO) */
};
/* clang-format on */
diff --git a/benchmark/wh_bench_ops.h b/benchmark/wh_bench_ops.h
index 30329664f..d2d0d3aad 100644
--- a/benchmark/wh_bench_ops.h
+++ b/benchmark/wh_bench_ops.h
@@ -26,7 +26,7 @@
#include
/* Maximum number of operations that can be registered */
-#define MAX_BENCH_OPS 101
+#define MAX_BENCH_OPS 119
/* Maximum length of operation name */
#define MAX_OP_NAME 64
diff --git a/src/wh_client_crypto.c b/src/wh_client_crypto.c
index e47ae92f8..437eb8559 100644
--- a/src/wh_client_crypto.c
+++ b/src/wh_client_crypto.c
@@ -55,6 +55,7 @@
#include "wolfssl/wolfcrypt/curve25519.h"
#include "wolfssl/wolfcrypt/ed25519.h"
#include "wolfssl/wolfcrypt/dilithium.h"
+#include "wolfssl/wolfcrypt/wc_mlkem.h"
#include "wolfssl/wolfcrypt/sha256.h"
#include "wolfssl/wolfcrypt/sha512.h"
#endif
@@ -124,6 +125,17 @@ static int _MlDsaMakeKeyDma(whClientContext* ctx, int level,
#endif /* WOLFHSM_CFG_DMA */
#endif /* HAVE_DILITHIUM */
+#ifdef WOLFSSL_HAVE_MLKEM
+static int _MlKemMakeKey(whClientContext* ctx, int level,
+ whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label, MlKemKey* key);
+#ifdef WOLFHSM_CFG_DMA
+static int _MlKemMakeKeyDma(whClientContext* ctx, int level,
+ whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label, MlKemKey* key);
+#endif /* WOLFHSM_CFG_DMA */
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#ifndef NO_SHA256
/* Helper function to transfer SHA256 block and update digest */
static int _xferSha256BlockAndUpdateDigest(whClientContext* ctx,
@@ -6443,4 +6455,883 @@ int wh_Client_MlDsaCheckPrivKeyDma(whClientContext* ctx, MlDsaKey* key,
#endif /* WOLFHSM_CFG_DMA */
#endif /* HAVE_DILITHIUM */
+#ifdef WOLFSSL_HAVE_MLKEM
+
+int wh_Client_MlKemSetKeyId(MlKemKey* key, whKeyId keyId)
+{
+ if (key == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ key->devCtx = WH_KEYID_TO_DEVCTX(keyId);
+ return WH_ERROR_OK;
+}
+
+int wh_Client_MlKemGetKeyId(MlKemKey* key, whKeyId* outId)
+{
+ if ((key == NULL) || (outId == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ *outId = WH_DEVCTX_TO_KEYID(key->devCtx);
+ return WH_ERROR_OK;
+}
+
+int wh_Client_MlKemImportKey(whClientContext* ctx, MlKemKey* key,
+ whKeyId* inout_keyId, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label)
+{
+ int ret = WH_ERROR_OK;
+ whKeyId key_id = WH_KEYID_ERASED;
+ byte* buffer = NULL;
+ uint16_t buffer_len = 0;
+ word32 allocSz = 0;
+
+ if ((ctx == NULL) || (key == NULL) ||
+ ((label_len != 0) && (label == NULL))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Use exact key size based on level to avoid over-allocation */
+ ret = wc_MlKemKey_PrivateKeySize(key, &allocSz);
+ if (ret != 0) {
+ /* Fall back to public key size if no private key */
+ ret = wc_MlKemKey_PublicKeySize(key, &allocSz);
+ }
+ if (ret != 0 || allocSz == 0) {
+ return WH_ERROR_BADARGS;
+ }
+
+ buffer = (byte*)XMALLOC(allocSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (buffer == NULL) {
+ return WH_ERROR_ABORTED;
+ }
+
+ if (inout_keyId != NULL) {
+ key_id = *inout_keyId;
+ }
+
+ ret = wh_Crypto_MlKemSerializeKey(key, (uint16_t)allocSz, buffer,
+ &buffer_len);
+ WH_DEBUG_CLIENT_VERBOSE("MlKemImportKey: serialize ret:%d, len:%u\n",
+ ret, (unsigned int)buffer_len);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Client_KeyCache(ctx, flags, label, label_len, buffer,
+ buffer_len, &key_id);
+ if ((ret == WH_ERROR_OK) && (inout_keyId != NULL)) {
+ *inout_keyId = key_id;
+ }
+ }
+ WH_DEBUG_CLIENT_VERBOSE("MlKemImportKey: ret:%d keyId:%u\n", ret, key_id);
+
+ wc_ForceZero(buffer, allocSz);
+ XFREE(buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return ret;
+}
+
+int wh_Client_MlKemExportKey(whClientContext* ctx, whKeyId keyId, MlKemKey* key,
+ uint16_t label_len, uint8_t* label)
+{
+ int ret = WH_ERROR_OK;
+ byte* buffer = NULL;
+ uint16_t buffer_len = WC_ML_KEM_MAX_PRIVATE_KEY_SIZE;
+
+ if ((ctx == NULL) || WH_KEYID_ISERASED(keyId) || (key == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ buffer = (byte*)XMALLOC(WC_ML_KEM_MAX_PRIVATE_KEY_SIZE, NULL,
+ DYNAMIC_TYPE_TMP_BUFFER);
+ if (buffer == NULL) {
+ return WH_ERROR_ABORTED;
+ }
+
+ ret =
+ wh_Client_KeyExport(ctx, keyId, label, label_len, buffer, &buffer_len);
+ WH_DEBUG_CLIENT_VERBOSE("MlKemExportKey: export ret:%d, len:%u\n",
+ ret, (unsigned int)buffer_len);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Crypto_MlKemDeserializeKey(buffer, buffer_len, key);
+ }
+ WH_DEBUG_CLIENT_VERBOSE("MlKemExportKey: keyId:%x ret:%d\n", keyId, ret);
+
+ wc_ForceZero(buffer, WC_ML_KEM_MAX_PRIVATE_KEY_SIZE);
+ XFREE(buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return ret;
+}
+
+static int _MlKemMakeKey(whClientContext* ctx, int level,
+ whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label, MlKemKey* key)
+{
+ int ret = WH_ERROR_OK;
+ whKeyId key_id = WH_KEYID_ERASED;
+ uint8_t* dataPtr = NULL;
+ whMessageCrypto_MlKemKeyGenRequest* req = NULL;
+ whMessageCrypto_MlKemKeyGenResponse* res = NULL;
+ uint16_t group = WH_MESSAGE_GROUP_CRYPTO;
+ uint16_t action = WC_ALGO_TYPE_PK;
+ uint16_t req_len;
+ uint16_t res_len;
+
+ if (ctx == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ req = (whMessageCrypto_MlKemKeyGenRequest*)_createCryptoRequestWithSubtype(
+ dataPtr, WC_PK_TYPE_PQC_KEM_KEYGEN, WC_PQC_KEM_TYPE_KYBER,
+ ctx->cryptoAffinity);
+
+ if (inout_key_id != NULL) {
+ key_id = *inout_key_id;
+ }
+
+ req_len = sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req);
+
+ /* Defense in depth: ensure request fits in comm buffer */
+ if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) {
+ return WH_ERROR_BADARGS;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->level = level;
+ req->flags = flags;
+ req->keyId = key_id;
+ if ((label != NULL) && (label_len > 0)) {
+ if (label_len > WH_NVM_LABEL_LEN) {
+ label_len = WH_NVM_LABEL_LEN;
+ }
+ memcpy(req->label, label, label_len);
+ }
+
+ ret = wh_Client_SendRequest(ctx, group, action, req_len,
+ (uint8_t*)dataPtr);
+ WH_DEBUG_CLIENT_VERBOSE("MlKemMakeKey: Req sent:level:%d, ret:%d\n",
+ level, ret);
+ if (ret != WH_ERROR_OK) {
+ return ret;
+ }
+
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &res_len,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+ if (ret != WH_ERROR_OK) {
+ return ret;
+ }
+
+ ret = _getCryptoResponse(dataPtr, WC_PK_TYPE_PQC_KEM_KEYGEN,
+ (uint8_t**)&res);
+ if (ret >= 0) {
+ key_id = (whKeyId)res->keyId;
+ WH_DEBUG_CLIENT_VERBOSE("MlKemMakeKey: Res recv:"
+ "keyId:%u, len:%u, ret:%d\n",
+ (unsigned int)res->keyId,
+ (unsigned int)res->len, ret);
+ if (inout_key_id != NULL) {
+ *inout_key_id = key_id;
+ }
+ if (key != NULL) {
+ wh_Client_MlKemSetKeyId(key, key_id);
+ if ((flags & WH_NVM_FLAGS_EPHEMERAL) != 0) {
+ uint8_t* key_raw = (uint8_t*)(res + 1);
+ ret = wh_Crypto_MlKemDeserializeKey(
+ key_raw, (uint16_t)res->len, key);
+ }
+ }
+ }
+ return ret;
+}
+
+int wh_Client_MlKemMakeCacheKey(whClientContext* ctx, int level,
+ whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label)
+{
+ if (inout_key_id == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return _MlKemMakeKey(ctx, level, inout_key_id, flags, label_len,
+ label, NULL);
+}
+
+int wh_Client_MlKemMakeExportKey(whClientContext* ctx, int level,
+ MlKemKey* key)
+{
+ if (key == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return _MlKemMakeKey(ctx, level, NULL, WH_NVM_FLAGS_EPHEMERAL, 0,
+ NULL, key);
+}
+
+int wh_Client_MlKemEncapsulate(whClientContext* ctx, MlKemKey* key,
+ byte* ct, word32* inout_ct_len, byte* ss,
+ word32* inout_ss_len)
+{
+ int ret = WH_ERROR_OK;
+ uint8_t* dataPtr = NULL;
+ whMessageCrypto_MlKemEncapsRequest* req = NULL;
+ whMessageCrypto_MlKemEncapsResponse* res = NULL;
+
+ whKeyId key_id;
+ int evict = 0;
+
+ if ((ctx == NULL) || (key == NULL) || (ct == NULL) || (ss == NULL) ||
+ (inout_ct_len == NULL) || (inout_ss_len == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if ((*inout_ct_len == 0) || (*inout_ss_len == 0)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ key_id = WH_DEVCTX_TO_KEYID(key->devCtx);
+ if (WH_KEYID_ISERASED(key_id)) {
+ uint8_t keyLabel[] = "TempMlKemEncaps";
+ whNvmFlags flags = WH_NVM_FLAGS_USAGE_DERIVE;
+ ret = wh_Client_MlKemImportKey(ctx, key, &key_id, flags,
+ sizeof(keyLabel), keyLabel);
+ if (ret == WH_ERROR_OK) {
+ evict = 1;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ uint16_t group = WH_MESSAGE_GROUP_CRYPTO;
+ uint16_t action = WC_ALGO_TYPE_PK;
+ uint16_t req_len =
+ sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req);
+ uint32_t options = 0;
+
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+ return WH_ERROR_BADARGS;
+ }
+
+ req = (whMessageCrypto_MlKemEncapsRequest*)
+ _createCryptoRequestWithSubtype(dataPtr, WC_PK_TYPE_PQC_KEM_ENCAPS,
+ WC_PQC_KEM_TYPE_KYBER,
+ ctx->cryptoAffinity);
+
+ if (req_len <= WOLFHSM_CFG_COMM_DATA_LEN) {
+ if (evict != 0) {
+ options |= WH_MESSAGE_CRYPTO_MLKEM_ENCAPS_OPTIONS_EVICT;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->options = options;
+ req->level = key->type;
+ req->keyId = key_id;
+
+ ret = wh_Client_SendRequest(ctx, group, action, req_len,
+ (uint8_t*)dataPtr);
+ WH_DEBUG_CLIENT_VERBOSE("MlKemEncapsulate: Req sent:keyId:%u, "
+ "level:%u, ret:%d\n",
+ (unsigned int)key_id,
+ (unsigned int)key->type, ret);
+ if (ret == WH_ERROR_OK) {
+ evict = 0;
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = _getCryptoResponse(dataPtr, WC_PK_TYPE_PQC_KEM_ENCAPS,
+ (uint8_t**)&res);
+ if (ret >= 0) {
+ uint8_t* resp_data = (uint8_t*)(res + 1);
+ word32 out_ct_len = res->ctSz;
+ word32 out_ss_len = res->ssSz;
+ word32 max_resp = WOLFHSM_CFG_COMM_DATA_LEN -
+ (uint16_t)((uint8_t*)resp_data - dataPtr);
+ WH_DEBUG_CLIENT_VERBOSE("MlKemEncapsulate: Res recv:"
+ "ctSz:%u, ssSz:%u, ret:%d\n",
+ (unsigned int)out_ct_len,
+ (unsigned int)out_ss_len, ret);
+ if (out_ct_len + out_ss_len > max_resp ||
+ *inout_ct_len < out_ct_len ||
+ *inout_ss_len < out_ss_len) {
+ ret = WH_ERROR_BADARGS;
+ }
+ else {
+ memcpy(ct, resp_data, out_ct_len);
+ memcpy(ss, resp_data + out_ct_len, out_ss_len);
+ *inout_ct_len = out_ct_len;
+ *inout_ss_len = out_ss_len;
+ }
+ }
+ }
+ }
+ else {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+
+ return ret;
+}
+
+int wh_Client_MlKemDecapsulate(whClientContext* ctx, MlKemKey* key,
+ const byte* ct, word32 ct_len, byte* ss,
+ word32* inout_ss_len)
+{
+ int ret = WH_ERROR_OK;
+ uint8_t* dataPtr = NULL;
+ whMessageCrypto_MlKemDecapsRequest* req = NULL;
+ whMessageCrypto_MlKemDecapsResponse* res = NULL;
+
+ whKeyId key_id;
+ int evict = 0;
+
+ if ((ctx == NULL) || (key == NULL) || ((ct == NULL) && (ct_len > 0)) ||
+ (ss == NULL) || (inout_ss_len == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ key_id = WH_DEVCTX_TO_KEYID(key->devCtx);
+ if (WH_KEYID_ISERASED(key_id)) {
+ uint8_t keyLabel[] = "TempMlKemDecaps";
+ whNvmFlags flags = WH_NVM_FLAGS_USAGE_DERIVE;
+ ret = wh_Client_MlKemImportKey(ctx, key, &key_id, flags,
+ sizeof(keyLabel), keyLabel);
+ if (ret == WH_ERROR_OK) {
+ evict = 1;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ uint16_t group = WH_MESSAGE_GROUP_CRYPTO;
+ uint16_t action = WC_ALGO_TYPE_PK;
+ uint32_t options = 0;
+ uint64_t total_len = (uint64_t)sizeof(whMessageCrypto_GenericRequestHeader) +
+ sizeof(*req) + ct_len;
+
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+ return WH_ERROR_BADARGS;
+ }
+
+ req = (whMessageCrypto_MlKemDecapsRequest*)
+ _createCryptoRequestWithSubtype(dataPtr, WC_PK_TYPE_PQC_KEM_DECAPS,
+ WC_PQC_KEM_TYPE_KYBER,
+ ctx->cryptoAffinity);
+
+ if (total_len <= WOLFHSM_CFG_COMM_DATA_LEN) {
+ uint8_t* req_ct = (uint8_t*)(req + 1);
+ uint16_t req_len = (uint16_t)total_len;
+
+ if (evict != 0) {
+ options |= WH_MESSAGE_CRYPTO_MLKEM_DECAPS_OPTIONS_EVICT;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->options = options;
+ req->level = key->type;
+ req->keyId = key_id;
+ req->ctSz = ct_len;
+ if ((ct != NULL) && (ct_len > 0)) {
+ memcpy(req_ct, ct, ct_len);
+ }
+
+ ret = wh_Client_SendRequest(ctx, group, action, req_len,
+ (uint8_t*)dataPtr);
+ WH_DEBUG_CLIENT_VERBOSE("MlKemDecapsulate: Req sent:keyId:%u, "
+ "ctSz:%u, ret:%d\n",
+ (unsigned int)key_id,
+ (unsigned int)ct_len, ret);
+ if (ret == WH_ERROR_OK) {
+ evict = 0;
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = _getCryptoResponse(dataPtr, WC_PK_TYPE_PQC_KEM_DECAPS,
+ (uint8_t**)&res);
+ if (ret >= 0) {
+ uint8_t* resp_ss = (uint8_t*)(res + 1);
+ word32 out_ss_len = res->ssSz;
+ word32 max_resp = WOLFHSM_CFG_COMM_DATA_LEN -
+ (uint16_t)((uint8_t*)resp_ss - dataPtr);
+ WH_DEBUG_CLIENT_VERBOSE("MlKemDecapsulate: Res recv:"
+ "ssSz:%u, ret:%d\n",
+ (unsigned int)out_ss_len, ret);
+ if (out_ss_len > max_resp ||
+ *inout_ss_len < out_ss_len) {
+ ret = WH_ERROR_BADARGS;
+ }
+ else {
+ memcpy(ss, resp_ss, out_ss_len);
+ *inout_ss_len = out_ss_len;
+ }
+ }
+ }
+ }
+ else {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+
+ return ret;
+}
+
+#ifdef WOLFHSM_CFG_DMA
+int wh_Client_MlKemImportKeyDma(whClientContext* ctx, MlKemKey* key,
+ whKeyId* inout_keyId, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label)
+{
+ int ret = WH_ERROR_OK;
+ whKeyId key_id = WH_KEYID_ERASED;
+ byte* buffer = NULL;
+ uint16_t buffer_len = 0;
+ word32 allocSz = 0;
+
+ if ((ctx == NULL) || (key == NULL) ||
+ ((label_len != 0) && (label == NULL))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Use exact key size based on level to avoid over-allocation */
+ ret = wc_MlKemKey_PrivateKeySize(key, &allocSz);
+ if (ret != 0) {
+ ret = wc_MlKemKey_PublicKeySize(key, &allocSz);
+ }
+ if (ret != 0 || allocSz == 0) {
+ return WH_ERROR_BADARGS;
+ }
+
+ buffer = (byte*)XMALLOC(allocSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (buffer == NULL) {
+ return WH_ERROR_ABORTED;
+ }
+
+ if (inout_keyId != NULL) {
+ key_id = *inout_keyId;
+ }
+
+ ret = wh_Crypto_MlKemSerializeKey(key, (uint16_t)allocSz, buffer,
+ &buffer_len);
+ WH_DEBUG_CLIENT_VERBOSE("MlKemImportKeyDma: serialize ret:%d, len:%u\n",
+ ret, (unsigned int)buffer_len);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Client_KeyCacheDma(ctx, flags, label, label_len, buffer,
+ buffer_len, &key_id);
+ if ((ret == WH_ERROR_OK) && (inout_keyId != NULL)) {
+ *inout_keyId = key_id;
+ }
+ }
+ WH_DEBUG_CLIENT_VERBOSE("MlKemImportKeyDma: ret:%d keyId:%u\n",
+ ret, key_id);
+
+ wc_ForceZero(buffer, allocSz);
+ XFREE(buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return ret;
+}
+
+int wh_Client_MlKemExportKeyDma(whClientContext* ctx, whKeyId keyId,
+ MlKemKey* key, uint16_t label_len,
+ uint8_t* label)
+{
+ int ret = WH_ERROR_OK;
+ byte* buffer = NULL;
+ uint16_t buffer_len = WC_ML_KEM_MAX_PRIVATE_KEY_SIZE;
+
+ if ((ctx == NULL) || WH_KEYID_ISERASED(keyId) || (key == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ buffer = (byte*)XMALLOC(WC_ML_KEM_MAX_PRIVATE_KEY_SIZE, NULL,
+ DYNAMIC_TYPE_TMP_BUFFER);
+ if (buffer == NULL) {
+ return WH_ERROR_ABORTED;
+ }
+ memset(buffer, 0, WC_ML_KEM_MAX_PRIVATE_KEY_SIZE);
+
+ ret = wh_Client_KeyExportDma(ctx, keyId, buffer, buffer_len, label,
+ label_len, &buffer_len);
+ WH_DEBUG_CLIENT_VERBOSE("MlKemExportKeyDma: export ret:%d, len:%u\n",
+ ret, (unsigned int)buffer_len);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Crypto_MlKemDeserializeKey(buffer, buffer_len, key);
+ }
+ WH_DEBUG_CLIENT_VERBOSE("MlKemExportKeyDma: keyId:%x ret:%d\n",
+ keyId, ret);
+
+ wc_ForceZero(buffer, WC_ML_KEM_MAX_PRIVATE_KEY_SIZE);
+ XFREE(buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return ret;
+}
+
+static int _MlKemMakeKeyDma(whClientContext* ctx, int level,
+ whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label, MlKemKey* key)
+{
+ int ret = WH_ERROR_OK;
+ whKeyId key_id = WH_KEYID_ERASED;
+ byte* buffer = NULL;
+ uint8_t* dataPtr = NULL;
+ whMessageCrypto_MlKemKeyGenDmaRequest* req = NULL;
+ whMessageCrypto_MlKemKeyGenDmaResponse* res = NULL;
+ uintptr_t keyAddr = 0;
+ uint32_t allocSz = 0;
+
+ if ((ctx == NULL) || (key == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wc_MlKemKey_PrivateKeySize(key, &allocSz);
+ if (ret != 0) {
+ return WH_ERROR_BADARGS;
+ }
+ else {
+ buffer = (byte*)XMALLOC(allocSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (buffer == NULL) {
+ return WH_ERROR_ABORTED;
+ }
+ }
+
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ XFREE(buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return WH_ERROR_BADARGS;
+ }
+
+ req = (whMessageCrypto_MlKemKeyGenDmaRequest*)_createCryptoRequestWithSubtype(
+ dataPtr, WC_PK_TYPE_PQC_KEM_KEYGEN, WC_PQC_KEM_TYPE_KYBER,
+ ctx->cryptoAffinity);
+
+ if (inout_key_id != NULL) {
+ key_id = *inout_key_id;
+ }
+
+ uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA;
+ uint16_t action = WC_ALGO_TYPE_PK;
+ uint16_t req_len =
+ sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req);
+
+ if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) {
+ XFREE(buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return WH_ERROR_BADARGS;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->level = level;
+ req->flags = flags;
+ req->keyId = key_id;
+ req->key.sz = allocSz;
+
+ ret = wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)buffer, (void**)&keyAddr, allocSz,
+ WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ req->key.addr = (uint64_t)(uintptr_t)keyAddr;
+ }
+
+ if ((label != NULL) && (label_len > 0)) {
+ if (label_len > WH_NVM_LABEL_LEN) {
+ label_len = WH_NVM_LABEL_LEN;
+ }
+ memcpy(req->label, label, label_len);
+ req->labelSize = label_len;
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Client_SendRequest(ctx, group, action, req_len,
+ (uint8_t*)dataPtr);
+ }
+ if (ret == WH_ERROR_OK) {
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+ }
+
+ (void)wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)buffer, (void**)&keyAddr, allocSz,
+ WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0});
+
+ if (ret == WH_ERROR_OK) {
+ ret = _getCryptoResponse(dataPtr, WC_PK_TYPE_PQC_KEM_KEYGEN,
+ (uint8_t**)&res);
+ if (ret >= 0) {
+ key_id = (whKeyId)res->keyId;
+ if (inout_key_id != NULL) {
+ *inout_key_id = key_id;
+ }
+ if (key != NULL) {
+ wh_Client_MlKemSetKeyId(key, key_id);
+ if ((flags & WH_NVM_FLAGS_EPHEMERAL) != 0) {
+ ret = wh_Crypto_MlKemDeserializeKey(
+ buffer, (uint16_t)res->keySize, key);
+ }
+ }
+ }
+ }
+
+ wc_ForceZero(buffer, allocSz);
+ XFREE(buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return ret;
+}
+
+int wh_Client_MlKemMakeExportKeyDma(whClientContext* ctx, int level,
+ MlKemKey* key)
+{
+ if (key == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return _MlKemMakeKeyDma(ctx, level, NULL, WH_NVM_FLAGS_EPHEMERAL, 0, NULL,
+ key);
+}
+
+int wh_Client_MlKemEncapsulateDma(whClientContext* ctx, MlKemKey* key,
+ byte* ct, word32* inout_ct_len, byte* ss,
+ word32* inout_ss_len)
+{
+ int ret = WH_ERROR_OK;
+ uint8_t* dataPtr = NULL;
+ whMessageCrypto_MlKemEncapsDmaRequest* req = NULL;
+ whMessageCrypto_MlKemEncapsDmaResponse* res = NULL;
+ uintptr_t ctAddr = 0;
+ whKeyId key_id;
+ int evict = 0;
+ uint32_t options = 0;
+ word32 origCtSz;
+
+ if ((ctx == NULL) || (key == NULL) || (ct == NULL) || (ss == NULL) ||
+ (inout_ct_len == NULL) || (inout_ss_len == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ origCtSz = *inout_ct_len;
+
+ key_id = WH_DEVCTX_TO_KEYID(key->devCtx);
+ if (WH_KEYID_ISERASED(key_id)) {
+ uint8_t keyLabel[] = "TempMlKemEncaps";
+ whNvmFlags flags = WH_NVM_FLAGS_USAGE_DERIVE;
+ ret = wh_Client_MlKemImportKeyDma(ctx, key, &key_id, flags,
+ sizeof(keyLabel), keyLabel);
+ if (ret == WH_ERROR_OK) {
+ evict = 1;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA;
+ uint16_t action = WC_ALGO_TYPE_PK;
+ uint16_t req_len =
+ sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req);
+
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+ return WH_ERROR_BADARGS;
+ }
+
+ req = (whMessageCrypto_MlKemEncapsDmaRequest*)
+ _createCryptoRequestWithSubtype(dataPtr, WC_PK_TYPE_PQC_KEM_ENCAPS,
+ WC_PQC_KEM_TYPE_KYBER,
+ ctx->cryptoAffinity);
+
+ if (req_len <= WOLFHSM_CFG_COMM_DATA_LEN) {
+ if (evict != 0) {
+ options |= WH_MESSAGE_CRYPTO_MLKEM_ENCAPS_OPTIONS_EVICT;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->options = options;
+ req->level = key->type;
+ req->keyId = key_id;
+
+ req->ct.sz = origCtSz;
+ ret = wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)ct, (void**)&ctAddr, req->ct.sz,
+ WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ req->ct.addr = ctAddr;
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Client_SendRequest(ctx, group, action, req_len,
+ (uint8_t*)dataPtr);
+ }
+ if (ret == WH_ERROR_OK) {
+ evict = 0;
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = _getCryptoResponse(dataPtr, WC_PK_TYPE_PQC_KEM_ENCAPS,
+ (uint8_t**)&res);
+ if (ret >= 0) {
+ /* ct was transferred via DMA, ss is inline in response */
+ uint8_t* resp_ss = (uint8_t*)(res + 1);
+ *inout_ct_len = res->ctLen;
+ if (res->ssLen > *inout_ss_len) {
+ ret = WH_ERROR_BADARGS;
+ }
+ else {
+ memcpy(ss, resp_ss, res->ssLen);
+ *inout_ss_len = res->ssLen;
+ }
+ }
+ }
+
+ (void)wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)ct, (void**)&ctAddr, origCtSz,
+ WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0});
+ }
+ else {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+
+ return ret;
+}
+
+int wh_Client_MlKemDecapsulateDma(whClientContext* ctx, MlKemKey* key,
+ const byte* ct, word32 ct_len, byte* ss,
+ word32* inout_ss_len)
+{
+ int ret = WH_ERROR_OK;
+ uint8_t* dataPtr = NULL;
+ whMessageCrypto_MlKemDecapsDmaRequest* req = NULL;
+ whMessageCrypto_MlKemDecapsDmaResponse* res = NULL;
+ uintptr_t ctAddr = 0;
+ whKeyId key_id;
+ int evict = 0;
+ uint32_t options = 0;
+
+ if ((ctx == NULL) || (key == NULL) || ((ct == NULL) && (ct_len > 0)) ||
+ (ss == NULL) || (inout_ss_len == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ key_id = WH_DEVCTX_TO_KEYID(key->devCtx);
+ if (WH_KEYID_ISERASED(key_id)) {
+ uint8_t keyLabel[] = "TempMlKemDecaps";
+ whNvmFlags flags = WH_NVM_FLAGS_USAGE_DERIVE;
+ ret = wh_Client_MlKemImportKeyDma(ctx, key, &key_id, flags,
+ sizeof(keyLabel), keyLabel);
+ if (ret == WH_ERROR_OK) {
+ evict = 1;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA;
+ uint16_t action = WC_ALGO_TYPE_PK;
+ uint16_t req_len =
+ sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req);
+
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+ return WH_ERROR_BADARGS;
+ }
+
+ req = (whMessageCrypto_MlKemDecapsDmaRequest*)
+ _createCryptoRequestWithSubtype(dataPtr, WC_PK_TYPE_PQC_KEM_DECAPS,
+ WC_PQC_KEM_TYPE_KYBER,
+ ctx->cryptoAffinity);
+
+ if (req_len <= WOLFHSM_CFG_COMM_DATA_LEN) {
+ if (evict != 0) {
+ options |= WH_MESSAGE_CRYPTO_MLKEM_DECAPS_OPTIONS_EVICT;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->options = options;
+ req->level = key->type;
+ req->keyId = key_id;
+
+ req->ct.sz = ct_len;
+ ret = wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)ct, (void**)&ctAddr, req->ct.sz,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ req->ct.addr = ctAddr;
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Client_SendRequest(ctx, group, action, req_len,
+ (uint8_t*)dataPtr);
+ }
+ if (ret == WH_ERROR_OK) {
+ evict = 0;
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = _getCryptoResponse(dataPtr, WC_PK_TYPE_PQC_KEM_DECAPS,
+ (uint8_t**)&res);
+ if (ret >= 0) {
+ /* ss is inline in response, not via DMA */
+ uint8_t* resp_ss = (uint8_t*)(res + 1);
+ if (res->ssLen > *inout_ss_len) {
+ ret = WH_ERROR_BADARGS;
+ }
+ else {
+ memcpy(ss, resp_ss, res->ssLen);
+ *inout_ss_len = res->ssLen;
+ }
+ }
+ }
+
+ (void)wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)ct, (void**)&ctAddr, ct_len,
+ WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0});
+ }
+ else {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+
+ return ret;
+}
+#endif /* WOLFHSM_CFG_DMA */
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#endif /* !WOLFHSM_CFG_NO_CRYPTO && WOLFHSM_CFG_ENABLE_CLIENT */
diff --git a/src/wh_client_cryptocb.c b/src/wh_client_cryptocb.c
index 43ff0a32e..e283dd932 100644
--- a/src/wh_client_cryptocb.c
+++ b/src/wh_client_cryptocb.c
@@ -47,6 +47,7 @@
#include "wolfssl/wolfcrypt/ecc.h"
#include "wolfssl/wolfcrypt/sha256.h"
#include "wolfssl/wolfcrypt/sha512.h"
+#include "wolfssl/wolfcrypt/wc_mlkem.h"
#include "wolfhsm/wh_crypto.h"
#include "wolfhsm/wh_client_crypto.h"
@@ -54,6 +55,15 @@
#include "wolfhsm/wh_message_crypto.h"
+#if defined(WOLFSSL_HAVE_MLKEM)
+static int _handlePqcKemKeyGen(whClientContext* ctx, wc_CryptoInfo* info,
+ int useDma);
+static int _handlePqcEncaps(whClientContext* ctx, wc_CryptoInfo* info,
+ int useDma);
+static int _handlePqcDecaps(whClientContext* ctx, wc_CryptoInfo* info,
+ int useDma);
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#if defined(HAVE_DILITHIUM) || defined(HAVE_FALCON)
static int _handlePqcSigKeyGen(whClientContext* ctx, wc_CryptoInfo* info,
int useDma);
@@ -418,6 +428,21 @@ int wh_Client_CryptoCb(int devId, wc_CryptoInfo* info, void* inCtx)
#endif /* HAVE_ED25519 */
#endif /* HAVE_CURVE25519 */
+#if defined(WOLFSSL_HAVE_MLKEM)
+ case WC_PK_TYPE_PQC_KEM_KEYGEN:
+ ret = _handlePqcKemKeyGen(ctx, info, 0);
+ break;
+
+ case WC_PK_TYPE_PQC_KEM_ENCAPS:
+ ret = _handlePqcEncaps(ctx, info, 0);
+ break;
+
+ case WC_PK_TYPE_PQC_KEM_DECAPS:
+ ret = _handlePqcDecaps(ctx, info, 0);
+ break;
+
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#if defined(HAVE_DILITHIUM) || defined(HAVE_FALCON)
case WC_PK_TYPE_PQC_SIG_KEYGEN:
ret = _handlePqcSigKeyGen(ctx, info, 0);
@@ -593,6 +618,146 @@ int wh_Client_CryptoCb(int devId, wc_CryptoInfo* info, void* inCtx)
return ret;
}
+#if defined(WOLFSSL_HAVE_MLKEM)
+static int _handlePqcKemKeyGen(whClientContext* ctx, wc_CryptoInfo* info,
+ int useDma)
+{
+ int ret = CRYPTOCB_UNAVAILABLE;
+
+ /* Extract info parameters */
+ int size = info->pk.pqc_kem_kg.size;
+ void* key = info->pk.pqc_kem_kg.key;
+ int type = info->pk.pqc_kem_kg.type;
+
+#ifndef WOLFHSM_CFG_DMA
+ if (useDma) {
+ /* TODO: proper error code? */
+ return WC_HW_E;
+ }
+#endif
+
+ (void)size;
+
+ switch (type) {
+ case WC_PQC_KEM_TYPE_KYBER: {
+ int level = ((MlKemKey*)key)->type;
+#ifdef WOLFHSM_CFG_DMA
+ if (useDma) {
+ ret = wh_Client_MlKemMakeExportKeyDma(ctx, level, key);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_MlKemMakeExportKey(ctx, level, key);
+ }
+ } break;
+
+ default:
+ ret = CRYPTOCB_UNAVAILABLE;
+ break;
+ }
+
+ return ret;
+}
+
+static int _handlePqcEncaps(whClientContext* ctx, wc_CryptoInfo* info,
+ int useDma)
+{
+ int ret = CRYPTOCB_UNAVAILABLE;
+
+ /* Extract info parameters */
+ byte* ciphertext = info->pk.pqc_encaps.ciphertext;
+ word32 ciphertextLen = info->pk.pqc_encaps.ciphertextLen;
+ byte* sharedSecret = info->pk.pqc_encaps.sharedSecret;
+ word32 sharedSecLen = info->pk.pqc_encaps.sharedSecretLen;
+ void* key = info->pk.pqc_encaps.key;
+ int type = info->pk.pqc_encaps.type;
+
+#ifndef WOLFHSM_CFG_DMA
+ if (useDma) {
+ /* TODO: proper error code? */
+ return WC_HW_E;
+ }
+#endif
+
+ switch (type) {
+ case WC_PQC_KEM_TYPE_KYBER:
+#ifdef WOLFHSM_CFG_DMA
+ if (useDma) {
+ ret = wh_Client_MlKemEncapsulateDma(ctx, key, ciphertext,
+ &ciphertextLen,
+ sharedSecret, &sharedSecLen);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_MlKemEncapsulate(ctx, key, ciphertext,
+ &ciphertextLen, sharedSecret,
+ &sharedSecLen);
+ }
+ if (ret == WH_ERROR_OK) {
+ info->pk.pqc_encaps.ciphertextLen = ciphertextLen;
+ info->pk.pqc_encaps.sharedSecretLen = sharedSecLen;
+ }
+ break;
+
+ default:
+ ret = CRYPTOCB_UNAVAILABLE;
+ break;
+ }
+
+ return ret;
+}
+
+static int _handlePqcDecaps(whClientContext* ctx, wc_CryptoInfo* info,
+ int useDma)
+{
+ int ret = CRYPTOCB_UNAVAILABLE;
+
+ /* Extract info parameters */
+ const byte* ciphertext = info->pk.pqc_decaps.ciphertext;
+ word32 ciphertextLen = info->pk.pqc_decaps.ciphertextLen;
+ byte* sharedSecret = info->pk.pqc_decaps.sharedSecret;
+ word32 sharedSecLen = info->pk.pqc_decaps.sharedSecretLen;
+ void* key = info->pk.pqc_decaps.key;
+ int type = info->pk.pqc_decaps.type;
+
+#ifndef WOLFHSM_CFG_DMA
+ if (useDma) {
+ /* TODO: proper error code? */
+ return WC_HW_E;
+ }
+#endif
+
+ switch (type) {
+ case WC_PQC_KEM_TYPE_KYBER:
+#ifdef WOLFHSM_CFG_DMA
+ if (useDma) {
+ ret = wh_Client_MlKemDecapsulateDma(
+ ctx, key, ciphertext, ciphertextLen, sharedSecret,
+ &sharedSecLen);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_MlKemDecapsulate(ctx, key, ciphertext,
+ ciphertextLen, sharedSecret,
+ &sharedSecLen);
+ }
+ if (ret == WH_ERROR_OK) {
+ info->pk.pqc_decaps.sharedSecretLen = sharedSecLen;
+ }
+ break;
+
+ default:
+ ret = CRYPTOCB_UNAVAILABLE;
+ break;
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#if defined(HAVE_FALCON) || defined(HAVE_DILITHIUM)
static int _handlePqcSigKeyGen(whClientContext* ctx, wc_CryptoInfo* info,
int useDma)
@@ -857,6 +1022,17 @@ int wh_Client_CryptoCbDma(int devId, wc_CryptoInfo* info, void* inCtx)
case WC_ALGO_TYPE_PK: {
switch (info->pk.type) {
+#if defined(WOLFSSL_HAVE_MLKEM)
+ case WC_PK_TYPE_PQC_KEM_KEYGEN:
+ ret = _handlePqcKemKeyGen(ctx, info, 1);
+ break;
+ case WC_PK_TYPE_PQC_KEM_ENCAPS:
+ ret = _handlePqcEncaps(ctx, info, 1);
+ break;
+ case WC_PK_TYPE_PQC_KEM_DECAPS:
+ ret = _handlePqcDecaps(ctx, info, 1);
+ break;
+#endif /* WOLFSSL_HAVE_MLKEM */
#if defined(HAVE_DILITHIUM) || defined(HAVE_FALCON)
case WC_PK_TYPE_PQC_SIG_KEYGEN:
ret = _handlePqcSigKeyGen(ctx, info, 1);
diff --git a/src/wh_crypto.c b/src/wh_crypto.c
index d43c73ff8..f3c473238 100644
--- a/src/wh_crypto.c
+++ b/src/wh_crypto.c
@@ -44,6 +44,7 @@
#include "wolfssl/wolfcrypt/ecc.h"
#include "wolfssl/wolfcrypt/ed25519.h"
#include "wolfssl/wolfcrypt/dilithium.h"
+#include "wolfssl/wolfcrypt/wc_mlkem.h"
#include "wolfhsm/wh_error.h"
#include "wolfhsm/wh_utils.h"
@@ -379,6 +380,103 @@ int wh_Crypto_MlDsaDeserializeKeyDer(const uint8_t* buffer, uint16_t size,
}
#endif /* HAVE_DILITHIUM */
+#ifdef WOLFSSL_HAVE_MLKEM
+int wh_Crypto_MlKemSerializeKey(MlKemKey* key, uint16_t max_size,
+ uint8_t* buffer, uint16_t* out_size)
+{
+ int ret = WH_ERROR_OK;
+ word32 keySize;
+
+ if ((key == NULL) || (buffer == NULL) || (out_size == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Try to encode the private key first. wc_MlKemKey_PrivateKeySize()
+ * returns the size regardless of whether a private key is present, so we
+ * must attempt encoding and check the return to detect public-only keys. */
+ ret = wc_MlKemKey_PrivateKeySize(key, &keySize);
+ if (ret == WH_ERROR_OK) {
+ if (keySize > max_size) {
+ return WH_ERROR_BADARGS;
+ }
+ ret = wc_MlKemKey_EncodePrivateKey(key, buffer, keySize);
+ }
+ if (ret != WH_ERROR_OK) {
+ /* Private key encoding failed - try public key only */
+ ret = wc_MlKemKey_PublicKeySize(key, &keySize);
+ if (ret == WH_ERROR_OK) {
+ if (keySize > max_size) {
+ return WH_ERROR_BADARGS;
+ }
+ ret = wc_MlKemKey_EncodePublicKey(key, buffer, keySize);
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ *out_size = (uint16_t)keySize;
+ }
+
+ return ret;
+}
+
+int wh_Crypto_MlKemDeserializeKey(const uint8_t* buffer, uint16_t size,
+ MlKemKey* key)
+{
+ static const int levels[] = {
+ WC_ML_KEM_512,
+ WC_ML_KEM_768,
+ WC_ML_KEM_1024
+ };
+ int ret;
+ int origLevel;
+ int origDevId;
+ word32 i;
+
+ if ((buffer == NULL) || (key == NULL) || (size == 0)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Save original key properties so we can restore on failure */
+ origLevel = key->type;
+ origDevId = key->devId;
+
+ /* First, try decoding with the level already set in the key */
+ ret = wc_MlKemKey_DecodePrivateKey(key, buffer, size);
+ if (ret == WH_ERROR_OK) {
+ return ret;
+ }
+ ret = wc_MlKemKey_DecodePublicKey(key, buffer, size);
+ if (ret == WH_ERROR_OK) {
+ return ret;
+ }
+
+ /* Current level didn't work, try other levels in place */
+ for (i = 0; i < (word32)(sizeof(levels) / sizeof(levels[0])); i++) {
+ if (levels[i] == origLevel) {
+ continue;
+ }
+ wc_MlKemKey_Free(key);
+ ret = wc_MlKemKey_Init(key, levels[i], NULL, origDevId);
+ if (ret != WH_ERROR_OK) {
+ continue;
+ }
+ ret = wc_MlKemKey_DecodePrivateKey(key, buffer, size);
+ if (ret == WH_ERROR_OK) {
+ return ret;
+ }
+ ret = wc_MlKemKey_DecodePublicKey(key, buffer, size);
+ if (ret == WH_ERROR_OK) {
+ return ret;
+ }
+ }
+
+ /* None of the levels worked, restore original level and devId */
+ wc_MlKemKey_Free(key);
+ (void)wc_MlKemKey_Init(key, origLevel, NULL, origDevId);
+ return ret;
+}
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#ifdef WOLFSSL_CMAC
void wh_Crypto_CmacAesSaveStateToMsg(whMessageCrypto_CmacAesState* state,
const Cmac* cmac)
diff --git a/src/wh_message_crypto.c b/src/wh_message_crypto.c
index 83289caf1..7bd08d633 100644
--- a/src/wh_message_crypto.c
+++ b/src/wh_message_crypto.c
@@ -835,6 +835,91 @@ int wh_MessageCrypto_TranslateMlDsaVerifyResponse(
return 0;
}
+/* ML-KEM Key Generation Request translation */
+int wh_MessageCrypto_TranslateMlKemKeyGenRequest(
+ uint16_t magic, const whMessageCrypto_MlKemKeyGenRequest* src,
+ whMessageCrypto_MlKemKeyGenRequest* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, level);
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, flags);
+ WH_T32(magic, dest, src, access);
+ if (src != dest) {
+ memcpy(dest->label, src->label, sizeof(src->label));
+ }
+ return 0;
+}
+
+/* ML-KEM Key Generation Response translation */
+int wh_MessageCrypto_TranslateMlKemKeyGenResponse(
+ uint16_t magic, const whMessageCrypto_MlKemKeyGenResponse* src,
+ whMessageCrypto_MlKemKeyGenResponse* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, len);
+ return 0;
+}
+
+/* ML-KEM Encapsulation Request translation */
+int wh_MessageCrypto_TranslateMlKemEncapsRequest(
+ uint16_t magic, const whMessageCrypto_MlKemEncapsRequest* src,
+ whMessageCrypto_MlKemEncapsRequest* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, options);
+ WH_T32(magic, dest, src, level);
+ WH_T32(magic, dest, src, keyId);
+ return 0;
+}
+
+/* ML-KEM Encapsulation Response translation */
+int wh_MessageCrypto_TranslateMlKemEncapsResponse(
+ uint16_t magic, const whMessageCrypto_MlKemEncapsResponse* src,
+ whMessageCrypto_MlKemEncapsResponse* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, ctSz);
+ WH_T32(magic, dest, src, ssSz);
+ return 0;
+}
+
+/* ML-KEM Decapsulation Request translation */
+int wh_MessageCrypto_TranslateMlKemDecapsRequest(
+ uint16_t magic, const whMessageCrypto_MlKemDecapsRequest* src,
+ whMessageCrypto_MlKemDecapsRequest* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, options);
+ WH_T32(magic, dest, src, level);
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, ctSz);
+ return 0;
+}
+
+/* ML-KEM Decapsulation Response translation */
+int wh_MessageCrypto_TranslateMlKemDecapsResponse(
+ uint16_t magic, const whMessageCrypto_MlKemDecapsResponse* src,
+ whMessageCrypto_MlKemDecapsResponse* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, ssSz);
+ return 0;
+}
+
/*
* DMA Messages
*/
@@ -1112,6 +1197,143 @@ int wh_MessageCrypto_TranslateMlDsaVerifyDmaResponse(
return 0;
}
+/* ML-KEM DMA Key Generation Request translation */
+int wh_MessageCrypto_TranslateMlKemKeyGenDmaRequest(
+ uint16_t magic, const whMessageCrypto_MlKemKeyGenDmaRequest* src,
+ whMessageCrypto_MlKemKeyGenDmaRequest* dest)
+{
+ int ret;
+
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->key, &dest->key);
+ if (ret != 0) {
+ return ret;
+ }
+
+ WH_T32(magic, dest, src, level);
+ WH_T32(magic, dest, src, flags);
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, access);
+ WH_T32(magic, dest, src, labelSize);
+ if (src != dest) {
+ memcpy(dest->label, src->label, sizeof(src->label));
+ }
+
+ return 0;
+}
+
+/* ML-KEM DMA Key Generation Response translation */
+int wh_MessageCrypto_TranslateMlKemKeyGenDmaResponse(
+ uint16_t magic, const whMessageCrypto_MlKemKeyGenDmaResponse* src,
+ whMessageCrypto_MlKemKeyGenDmaResponse* dest)
+{
+ int ret;
+
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateDmaAddrStatus(magic, &src->dmaAddrStatus,
+ &dest->dmaAddrStatus);
+ if (ret != 0) {
+ return ret;
+ }
+
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, keySize);
+ return 0;
+}
+
+/* ML-KEM DMA Encapsulation Request translation */
+int wh_MessageCrypto_TranslateMlKemEncapsDmaRequest(
+ uint16_t magic, const whMessageCrypto_MlKemEncapsDmaRequest* src,
+ whMessageCrypto_MlKemEncapsDmaRequest* dest)
+{
+ int ret;
+
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->ct, &dest->ct);
+ if (ret != 0) {
+ return ret;
+ }
+
+ WH_T32(magic, dest, src, options);
+ WH_T32(magic, dest, src, level);
+ WH_T32(magic, dest, src, keyId);
+ return 0;
+}
+
+/* ML-KEM DMA Encapsulation Response translation */
+int wh_MessageCrypto_TranslateMlKemEncapsDmaResponse(
+ uint16_t magic, const whMessageCrypto_MlKemEncapsDmaResponse* src,
+ whMessageCrypto_MlKemEncapsDmaResponse* dest)
+{
+ int ret;
+
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateDmaAddrStatus(magic, &src->dmaAddrStatus,
+ &dest->dmaAddrStatus);
+ if (ret != 0) {
+ return ret;
+ }
+
+ WH_T32(magic, dest, src, ctLen);
+ WH_T32(magic, dest, src, ssLen);
+ return 0;
+}
+
+/* ML-KEM DMA Decapsulation Request translation */
+int wh_MessageCrypto_TranslateMlKemDecapsDmaRequest(
+ uint16_t magic, const whMessageCrypto_MlKemDecapsDmaRequest* src,
+ whMessageCrypto_MlKemDecapsDmaRequest* dest)
+{
+ int ret;
+
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->ct, &dest->ct);
+ if (ret != 0) {
+ return ret;
+ }
+
+ WH_T32(magic, dest, src, options);
+ WH_T32(magic, dest, src, level);
+ WH_T32(magic, dest, src, keyId);
+ return 0;
+}
+
+/* ML-KEM DMA Decapsulation Response translation */
+int wh_MessageCrypto_TranslateMlKemDecapsDmaResponse(
+ uint16_t magic, const whMessageCrypto_MlKemDecapsDmaResponse* src,
+ whMessageCrypto_MlKemDecapsDmaResponse* dest)
+{
+ int ret;
+
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateDmaAddrStatus(magic, &src->dmaAddrStatus,
+ &dest->dmaAddrStatus);
+ if (ret != 0) {
+ return ret;
+ }
+
+ WH_T32(magic, dest, src, ssLen);
+ return 0;
+}
+
/* Ed25519 DMA Sign Request translation */
int wh_MessageCrypto_TranslateEd25519SignDmaRequest(
uint16_t magic, const whMessageCrypto_Ed25519SignDmaRequest* src,
diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c
index 0fb7bd130..89ee1468c 100644
--- a/src/wh_server_crypto.c
+++ b/src/wh_server_crypto.c
@@ -44,6 +44,7 @@
#include "wolfssl/wolfcrypt/sha512.h"
#include "wolfssl/wolfcrypt/cmac.h"
#include "wolfssl/wolfcrypt/dilithium.h"
+#include "wolfssl/wolfcrypt/wc_mlkem.h"
#include "wolfssl/wolfcrypt/hmac.h"
#include "wolfssl/wolfcrypt/kdf.h"
@@ -194,6 +195,32 @@ static int _HandleMlDsaCheckPrivKey(whServerContext* ctx, uint16_t magic,
uint16_t* outSize);
#endif /* HAVE_DILITHIUM */
+#ifdef WOLFSSL_HAVE_MLKEM
+static int _HandleMlKemKeyGen(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize);
+static int _HandleMlKemEncaps(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize);
+static int _HandleMlKemDecaps(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize);
+#ifdef WOLFHSM_CFG_DMA
+static int _HandleMlKemKeyGenDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize);
+static int _HandleMlKemEncapsDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize);
+static int _HandleMlKemDecapsDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize);
+#endif /* WOLFHSM_CFG_DMA */
+#endif /* WOLFSSL_HAVE_MLKEM */
+
/** Public server crypto functions */
#ifndef NO_RSA
@@ -798,6 +825,60 @@ int wh_Server_MlDsaKeyCacheExport(whServerContext* ctx, whKeyId keyId,
}
#endif /* HAVE_DILITHIUM */
+#ifdef WOLFSSL_HAVE_MLKEM
+int wh_Server_MlKemKeyCacheImport(whServerContext* ctx, MlKemKey* key,
+ whKeyId keyId, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label)
+{
+ int ret = WH_ERROR_OK;
+ uint8_t* cacheBuf;
+ whNvmMetadata* cacheMeta;
+ uint16_t keySize = WC_ML_KEM_MAX_PRIVATE_KEY_SIZE;
+
+ if ((ctx == NULL) || (key == NULL) || (WH_KEYID_ISERASED(keyId)) ||
+ ((label != NULL) && (label_len > sizeof(cacheMeta->label)))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_Server_KeystoreGetCacheSlotChecked(ctx, keyId, keySize, &cacheBuf,
+ &cacheMeta);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Crypto_MlKemSerializeKey(key, keySize, cacheBuf, &keySize);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ cacheMeta->id = keyId;
+ cacheMeta->len = keySize;
+ cacheMeta->flags = flags;
+ cacheMeta->access = WH_NVM_ACCESS_ANY;
+ if ((label != NULL) && (label_len > 0)) {
+ memcpy(cacheMeta->label, label, label_len);
+ }
+ }
+
+ return ret;
+}
+
+int wh_Server_MlKemKeyCacheExport(whServerContext* ctx, whKeyId keyId,
+ MlKemKey* key)
+{
+ uint8_t* cacheBuf;
+ whNvmMetadata* cacheMeta;
+ int ret = WH_ERROR_OK;
+
+ if ((ctx == NULL) || (key == NULL) || (WH_KEYID_ISERASED(keyId))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_Server_KeystoreFreshenKey(ctx, keyId, &cacheBuf, &cacheMeta);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Crypto_MlKemDeserializeKey(cacheBuf, cacheMeta->len, key);
+ WH_DEBUG_SERVER_VERBOSE("keyId:%u, ret:%d\n", keyId, ret);
+ }
+ return ret;
+}
+#endif /* WOLFSSL_HAVE_MLKEM */
+
/** Request/Response Handling functions */
@@ -4359,6 +4440,318 @@ static int _HandleMlDsaCheckPrivKey(whServerContext* ctx, uint16_t magic,
}
#endif /* HAVE_DILITHIUM */
+#ifdef WOLFSSL_HAVE_MLKEM
+static int _IsMlKemLevelSupported(int level)
+{
+ int ret = 0;
+
+ switch (level) {
+#ifndef WOLFSSL_NO_ML_KEM_512
+ case WC_ML_KEM_512:
+ ret = 1;
+ break;
+#endif
+#ifndef WOLFSSL_NO_ML_KEM_768
+ case WC_ML_KEM_768:
+ ret = 1;
+ break;
+#endif
+#ifndef WOLFSSL_NO_ML_KEM_1024
+ case WC_ML_KEM_1024:
+ ret = 1;
+ break;
+#endif
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static int _HandleMlKemKeyGen(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize)
+{
+#ifdef WOLFSSL_MLKEM_NO_MAKE_KEY
+ (void)ctx;
+ (void)magic;
+ (void)cryptoDataIn;
+ (void)inSize;
+ (void)cryptoDataOut;
+ (void)outSize;
+ return WH_ERROR_NOHANDLER;
+#else
+ int ret = WH_ERROR_OK;
+ MlKemKey key[1];
+ whMessageCrypto_MlKemKeyGenRequest req;
+ whMessageCrypto_MlKemKeyGenResponse res;
+ uint16_t res_size = 0;
+ uint8_t* res_out;
+ uint16_t max_size;
+ whKeyId key_id;
+ uint16_t label_size = WH_NVM_LABEL_LEN;
+
+ (void)inSize;
+
+ ret = wh_MessageCrypto_TranslateMlKemKeyGenRequest(
+ magic, (whMessageCrypto_MlKemKeyGenRequest*)cryptoDataIn, &req);
+ if (ret != 0) {
+ return ret;
+ }
+
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, ctx->comm->client_id,
+ req.keyId);
+ res_out = (uint8_t*)cryptoDataOut + sizeof(whMessageCrypto_MlKemKeyGenResponse);
+ max_size = (uint16_t)(WOLFHSM_CFG_COMM_DATA_LEN -
+ (res_out - (uint8_t*)cryptoDataOut));
+
+ if (!_IsMlKemLevelSupported((int)req.level)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wc_MlKemKey_Init(key, (int)req.level, NULL, devId);
+ if (ret == 0) {
+ ret = wc_MlKemKey_MakeKey(key, ctx->crypto->rng);
+ if (ret == 0) {
+ if ((req.flags & WH_NVM_FLAGS_EPHEMERAL) != 0) {
+ key_id = WH_KEYID_ERASED;
+ ret = wh_Crypto_MlKemSerializeKey(key, max_size, res_out,
+ &res_size);
+ }
+ else {
+ if (WH_KEYID_ISERASED(key_id)) {
+ ret = wh_Server_KeystoreGetUniqueId(ctx, &key_id);
+ }
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Server_MlKemKeyCacheImport(ctx, key, key_id,
+ req.flags, label_size,
+ req.label);
+ }
+ }
+ }
+ wc_MlKemKey_Free(key);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ res.keyId = wh_KeyId_TranslateToClient(key_id);
+ res.len = res_size;
+ (void)wh_MessageCrypto_TranslateMlKemKeyGenResponse(
+ magic, &res, (whMessageCrypto_MlKemKeyGenResponse*)cryptoDataOut);
+ *outSize = sizeof(whMessageCrypto_MlKemKeyGenResponse) + res_size;
+ }
+
+ return ret;
+#endif /* WOLFSSL_MLKEM_NO_MAKE_KEY */
+}
+
+static int _HandleMlKemEncaps(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize)
+{
+#ifdef WOLFSSL_MLKEM_NO_ENCAPSULATE
+ (void)ctx;
+ (void)magic;
+ (void)devId;
+ (void)cryptoDataIn;
+ (void)inSize;
+ (void)cryptoDataOut;
+ (void)outSize;
+ return WH_ERROR_NOHANDLER;
+#else
+ int ret = WH_ERROR_OK;
+ MlKemKey key[1];
+ whMessageCrypto_MlKemEncapsRequest req;
+ whMessageCrypto_MlKemEncapsResponse res;
+ whKeyId key_id;
+ uint8_t* res_ct;
+ uint8_t* res_ss;
+ word32 ct_len;
+ word32 ss_len;
+ word32 max_out;
+ int evict = 0;
+ int keyInited = 0;
+
+ (void)inSize;
+
+ ret = wh_MessageCrypto_TranslateMlKemEncapsRequest(
+ magic, (whMessageCrypto_MlKemEncapsRequest*)cryptoDataIn, &req);
+ if (ret != 0) {
+ return ret;
+ }
+
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, ctx->comm->client_id,
+ req.keyId);
+ evict = !!(req.options & WH_MESSAGE_CRYPTO_MLKEM_ENCAPS_OPTIONS_EVICT);
+
+ if (!WH_KEYID_ISERASED(key_id)) {
+ ret = wh_Server_KeystoreFindEnforceKeyUsage(ctx, key_id,
+ WH_NVM_FLAGS_USAGE_DERIVE);
+ if (ret != WH_ERROR_OK) {
+ goto cleanup;
+ }
+ }
+
+ if (!_IsMlKemLevelSupported((int)req.level)) {
+ ret = WH_ERROR_BADARGS;
+ goto cleanup;
+ }
+
+ ret = wc_MlKemKey_Init(key, (int)req.level, NULL, devId);
+ if (ret == 0) {
+ keyInited = 1;
+ ret = wh_Server_MlKemKeyCacheExport(ctx, key_id, key);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = wc_MlKemKey_CipherTextSize(key, &ct_len);
+ if (ret == WH_ERROR_OK) {
+ ret = wc_MlKemKey_SharedSecretSize(key, &ss_len);
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ res_ct = (uint8_t*)cryptoDataOut + sizeof(whMessageCrypto_MlKemEncapsResponse);
+ res_ss = res_ct + ct_len;
+ max_out = (word32)(WOLFHSM_CFG_COMM_DATA_LEN -
+ ((uint8_t*)res_ct - (uint8_t*)cryptoDataOut));
+ if (ct_len + ss_len > max_out) {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = wc_MlKemKey_Encapsulate(key, res_ct, res_ss, ctx->crypto->rng);
+ if (ret == WH_ERROR_OK) {
+ res.ctSz = ct_len;
+ res.ssSz = ss_len;
+ (void)wh_MessageCrypto_TranslateMlKemEncapsResponse(
+ magic, &res, (whMessageCrypto_MlKemEncapsResponse*)cryptoDataOut);
+ *outSize = sizeof(whMessageCrypto_MlKemEncapsResponse) + ct_len + ss_len;
+ }
+ else {
+ /* Zero sensitive data on failure */
+ wc_ForceZero(res_ss, ss_len);
+ }
+ }
+
+ if (keyInited) {
+ wc_MlKemKey_Free(key);
+ }
+cleanup:
+ if (evict != 0) {
+ (void)wh_Server_KeystoreEvictKey(ctx, key_id);
+ }
+ return ret;
+#endif /* WOLFSSL_MLKEM_NO_ENCAPSULATE */
+}
+
+static int _HandleMlKemDecaps(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize)
+{
+#ifdef WOLFSSL_MLKEM_NO_DECAPSULATE
+ (void)ctx;
+ (void)magic;
+ (void)devId;
+ (void)cryptoDataIn;
+ (void)inSize;
+ (void)cryptoDataOut;
+ (void)outSize;
+ return WH_ERROR_NOHANDLER;
+#else
+ int ret = WH_ERROR_OK;
+ MlKemKey key[1];
+ whMessageCrypto_MlKemDecapsRequest req;
+ whMessageCrypto_MlKemDecapsResponse res;
+ whKeyId key_id;
+ byte* req_ct;
+ byte* res_ss;
+ uint32_t available;
+ word32 ss_len;
+ word32 max_out;
+ int evict = 0;
+ int keyInited = 0;
+
+ ret = wh_MessageCrypto_TranslateMlKemDecapsRequest(
+ magic, (whMessageCrypto_MlKemDecapsRequest*)cryptoDataIn, &req);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (inSize < sizeof(whMessageCrypto_MlKemDecapsRequest)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, ctx->comm->client_id,
+ req.keyId);
+ evict = !!(req.options & WH_MESSAGE_CRYPTO_MLKEM_DECAPS_OPTIONS_EVICT);
+
+ if (!WH_KEYID_ISERASED(key_id)) {
+ ret = wh_Server_KeystoreFindEnforceKeyUsage(ctx, key_id,
+ WH_NVM_FLAGS_USAGE_DERIVE);
+ if (ret != WH_ERROR_OK) {
+ goto cleanup;
+ }
+ }
+
+ if (!_IsMlKemLevelSupported((int)req.level)) {
+ ret = WH_ERROR_BADARGS;
+ goto cleanup;
+ }
+
+ available = inSize - sizeof(whMessageCrypto_MlKemDecapsRequest);
+ if (req.ctSz > available) {
+ ret = WH_ERROR_BADARGS;
+ goto cleanup;
+ }
+ req_ct = (byte*)cryptoDataIn + sizeof(whMessageCrypto_MlKemDecapsRequest);
+
+ ret = wc_MlKemKey_Init(key, (int)req.level, NULL, devId);
+ if (ret == WH_ERROR_OK) {
+ keyInited = 1;
+ ret = wh_Server_MlKemKeyCacheExport(ctx, key_id, key);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = wc_MlKemKey_SharedSecretSize(key, &ss_len);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ res_ss = (byte*)cryptoDataOut + sizeof(whMessageCrypto_MlKemDecapsResponse);
+ max_out = (word32)(WOLFHSM_CFG_COMM_DATA_LEN -
+ ((uint8_t*)res_ss - (uint8_t*)cryptoDataOut));
+ if (ss_len > max_out) {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = wc_MlKemKey_Decapsulate(key, res_ss, req_ct, req.ctSz);
+ if (ret == WH_ERROR_OK) {
+ res.ssSz = ss_len;
+ (void)wh_MessageCrypto_TranslateMlKemDecapsResponse(
+ magic, &res, (whMessageCrypto_MlKemDecapsResponse*)cryptoDataOut);
+ *outSize = sizeof(whMessageCrypto_MlKemDecapsResponse) + ss_len;
+ }
+ else {
+ /* Zero sensitive data on failure */
+ wc_ForceZero(res_ss, ss_len);
+ }
+ }
+
+ if (keyInited) {
+ wc_MlKemKey_Free(key);
+ }
+cleanup:
+ if (evict != 0) {
+ (void)wh_Server_KeystoreEvictKey(ctx, key_id);
+ }
+ return ret;
+#endif /* WOLFSSL_MLKEM_NO_DECAPSULATE */
+}
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#if defined(HAVE_DILITHIUM) || defined(HAVE_FALCON)
static int _HandlePqcSigAlgorithm(whServerContext* ctx, uint16_t magic,
int devId, const void* cryptoDataIn,
@@ -4409,21 +4802,44 @@ static int _HandlePqcSigAlgorithm(whServerContext* ctx, uint16_t magic,
}
#endif
-#if defined(HAVE_KYBER)
+#if defined(WOLFSSL_HAVE_MLKEM)
static int _HandlePqcKemAlgorithm(whServerContext* ctx, uint16_t magic,
int devId, const void* cryptoDataIn,
- uint16_t inSize, void* cryptoDataOut,
- uint16_t* outSize)
+ uint16_t cryptoInSize, void* cryptoDataOut,
+ uint16_t* cryptoOutSize, uint32_t pkAlgoType,
+ uint32_t pqAlgoType)
{
- (void)ctx;
- (void)magic;
- (void)devId;
- (void)cryptoDataIn;
- (void)inSize;
- (void)cryptoDataOut;
- (void)outSize;
- /* Placeholder for KEM algorithm handling */
- return WH_ERROR_NOHANDLER;
+ int ret = WH_ERROR_NOHANDLER;
+
+ switch (pqAlgoType) {
+ case WC_PQC_KEM_TYPE_KYBER: {
+ switch (pkAlgoType) {
+ case WC_PK_TYPE_PQC_KEM_KEYGEN:
+ ret = _HandleMlKemKeyGen(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_KEM_ENCAPS:
+ ret = _HandleMlKemEncaps(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_KEM_DECAPS:
+ ret = _HandleMlKemDecaps(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ default:
+ ret = WH_ERROR_NOHANDLER;
+ break;
+ }
+ } break;
+ default:
+ ret = WH_ERROR_NOHANDLER;
+ break;
+ }
+
+ return ret;
}
#endif
@@ -4605,13 +5021,15 @@ int wh_Server_HandleCryptoRequest(whServerContext* ctx, uint16_t magic,
break;
#endif
-#if defined(HAVE_KYBER)
+#if defined(WOLFSSL_HAVE_MLKEM)
case WC_PK_TYPE_PQC_KEM_KEYGEN:
case WC_PK_TYPE_PQC_KEM_ENCAPS:
case WC_PK_TYPE_PQC_KEM_DECAPS:
ret = _HandlePqcKemAlgorithm(ctx, magic, devId,
cryptoDataIn, cryptoInSize,
- cryptoDataOut, &cryptoOutSize);
+ cryptoDataOut, &cryptoOutSize,
+ rqstHeader.algoType,
+ rqstHeader.algoSubType);
break;
#endif
@@ -5702,6 +6120,397 @@ static int _HandlePqcSigAlgorithmDma(whServerContext* ctx, uint16_t magic,
}
#endif /* HAVE_DILITHIUM || HAVE_FALCON */
+#if defined(WOLFSSL_HAVE_MLKEM)
+static int _HandleMlKemKeyGenDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize)
+{
+#ifdef WOLFSSL_MLKEM_NO_MAKE_KEY
+ (void)ctx;
+ (void)magic;
+ (void)devId;
+ (void)cryptoDataIn;
+ (void)inSize;
+ (void)cryptoDataOut;
+ (void)outSize;
+ return WH_ERROR_NOHANDLER;
+#else
+ int ret = WH_ERROR_OK;
+ MlKemKey key[1];
+ void* clientOutAddr = NULL;
+ uint16_t keySize = 0;
+ whMessageCrypto_MlKemKeyGenDmaRequest req;
+ whMessageCrypto_MlKemKeyGenDmaResponse res;
+
+ memset(&res, 0, sizeof(res));
+
+ if (inSize < sizeof(whMessageCrypto_MlKemKeyGenDmaRequest)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateMlKemKeyGenDmaRequest(
+ magic, (whMessageCrypto_MlKemKeyGenDmaRequest*)cryptoDataIn, &req);
+ if (ret != WH_ERROR_OK) {
+ return ret;
+ }
+
+ if (!_IsMlKemLevelSupported((int)req.level)) {
+ ret = WH_ERROR_BADARGS;
+ }
+ else {
+ ret = wc_MlKemKey_Init(key, (int)req.level, NULL, devId);
+ if (ret == WH_ERROR_OK) {
+ ret = wc_MlKemKey_MakeKey(key, ctx->crypto->rng);
+ if (ret == WH_ERROR_OK) {
+ if ((req.flags & WH_NVM_FLAGS_EPHEMERAL) != 0) {
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, req.key.addr, &clientOutAddr, req.key.sz,
+ WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Crypto_MlKemSerializeKey(
+ key, req.key.sz, (uint8_t*)clientOutAddr, &keySize);
+ if (ret == WH_ERROR_OK) {
+ res.keyId = WH_KEYID_ERASED;
+ res.keySize = keySize;
+ }
+ }
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, req.key.addr, &clientOutAddr, keySize,
+ WH_DMA_OPER_CLIENT_WRITE_POST,
+ (whServerDmaFlags){0});
+ }
+ }
+ else {
+ whKeyId keyId = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+
+ if (WH_KEYID_ISERASED(keyId)) {
+ ret = wh_Server_KeystoreGetUniqueId(ctx, &keyId);
+ }
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Server_MlKemKeyCacheImport(
+ ctx, key, keyId, req.flags, req.labelSize,
+ req.label);
+ if (ret == WH_ERROR_OK) {
+ res.keyId = wh_KeyId_TranslateToClient(keyId);
+ res.keySize = keySize;
+ }
+ }
+ }
+ }
+ wc_MlKemKey_Free(key);
+ }
+ }
+
+ if (ret == WH_ERROR_ACCESS) {
+ res.dmaAddrStatus.badAddr = req.key;
+ }
+
+ (void)wh_MessageCrypto_TranslateMlKemKeyGenDmaResponse(
+ magic, &res, (whMessageCrypto_MlKemKeyGenDmaResponse*)cryptoDataOut);
+ *outSize = sizeof(res);
+ return ret;
+#endif
+}
+
+static int _HandleMlKemEncapsDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize)
+{
+#ifdef WOLFSSL_MLKEM_NO_ENCAPSULATE
+ (void)ctx;
+ (void)magic;
+ (void)devId;
+ (void)cryptoDataIn;
+ (void)inSize;
+ (void)cryptoDataOut;
+ (void)outSize;
+ return WH_ERROR_NOHANDLER;
+#else
+ int ret = WH_ERROR_OK;
+ MlKemKey key[1];
+ void* ctAddr = NULL;
+ word32 ctLen = 0;
+ word32 ssLen = 0;
+ whKeyId key_id;
+ int evict = 0;
+ int keyInited = 0;
+ uint8_t* res_ss;
+ word32 max_ss;
+ whMessageCrypto_MlKemEncapsDmaRequest req;
+ whMessageCrypto_MlKemEncapsDmaResponse res;
+
+ memset(&res, 0, sizeof(res));
+
+ if (inSize < sizeof(whMessageCrypto_MlKemEncapsDmaRequest)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateMlKemEncapsDmaRequest(
+ magic, (whMessageCrypto_MlKemEncapsDmaRequest*)cryptoDataIn, &req);
+ if (ret != WH_ERROR_OK) {
+ return ret;
+ }
+
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
+ evict = !!(req.options & WH_MESSAGE_CRYPTO_MLKEM_ENCAPS_OPTIONS_EVICT);
+
+ if (!WH_KEYID_ISERASED(key_id)) {
+ ret = wh_Server_KeystoreFindEnforceKeyUsage(ctx, key_id,
+ WH_NVM_FLAGS_USAGE_DERIVE);
+ if (ret != WH_ERROR_OK) {
+ goto cleanup;
+ }
+ }
+
+ if (!_IsMlKemLevelSupported((int)req.level)) {
+ ret = WH_ERROR_BADARGS;
+ goto cleanup;
+ }
+
+ ret = wc_MlKemKey_Init(key, (int)req.level, NULL, devId);
+ if (ret == WH_ERROR_OK) {
+ keyInited = 1;
+ ret = wh_Server_MlKemKeyCacheExport(ctx, key_id, key);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = wc_MlKemKey_CipherTextSize(key, &ctLen);
+ }
+ if (ret == WH_ERROR_OK && ctLen > req.ct.sz) {
+ ret = WH_ERROR_BADARGS;
+ goto cleanup_key;
+ }
+ if (ret == WH_ERROR_OK) {
+ ret = wc_MlKemKey_SharedSecretSize(key, &ssLen);
+ }
+
+ /* Validate that the inline shared secret fits in the comm buffer */
+ if (ret == WH_ERROR_OK) {
+ res_ss = (uint8_t*)cryptoDataOut +
+ sizeof(whMessageCrypto_MlKemEncapsDmaResponse);
+ max_ss = (word32)(WOLFHSM_CFG_COMM_DATA_LEN -
+ ((uint8_t*)res_ss - (uint8_t*)cryptoDataOut));
+ if (ssLen > max_ss) {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.ct.addr, &ctAddr, ctLen,
+ WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0});
+ if (ret == WH_ERROR_ACCESS) {
+ res.dmaAddrStatus.badAddr = req.ct;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ /* Shared secret goes inline in response, not via DMA */
+ res_ss = (uint8_t*)cryptoDataOut +
+ sizeof(whMessageCrypto_MlKemEncapsDmaResponse);
+ ret = wc_MlKemKey_Encapsulate(key, (byte*)ctAddr, res_ss,
+ ctx->crypto->rng);
+ if (ret != WH_ERROR_OK) {
+ /* Zero sensitive data on failure */
+ wc_ForceZero(res_ss, ssLen);
+ }
+ }
+
+ if (ctAddr != NULL) {
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.ct.addr, &ctAddr, ctLen,
+ WH_DMA_OPER_CLIENT_WRITE_POST, (whServerDmaFlags){0});
+ }
+
+ if (ret == WH_ERROR_OK) {
+ res.ctLen = ctLen;
+ res.ssLen = ssLen;
+ }
+
+cleanup_key:
+ if (keyInited) {
+ wc_MlKemKey_Free(key);
+ }
+cleanup:
+ if (evict != 0) {
+ (void)wh_Server_KeystoreEvictKey(ctx, key_id);
+ }
+
+ (void)wh_MessageCrypto_TranslateMlKemEncapsDmaResponse(
+ magic, &res, (whMessageCrypto_MlKemEncapsDmaResponse*)cryptoDataOut);
+ *outSize = sizeof(res) + ssLen;
+ return ret;
+#endif
+}
+
+static int _HandleMlKemDecapsDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize)
+{
+#ifdef WOLFSSL_MLKEM_NO_DECAPSULATE
+ (void)ctx;
+ (void)magic;
+ (void)devId;
+ (void)cryptoDataIn;
+ (void)inSize;
+ (void)cryptoDataOut;
+ (void)outSize;
+ return WH_ERROR_NOHANDLER;
+#else
+ int ret = WH_ERROR_OK;
+ MlKemKey key[1];
+ void* ctAddr = NULL;
+ word32 ssLen = 0;
+ whKeyId key_id;
+ int evict = 0;
+ int keyInited = 0;
+ uint8_t* res_ss;
+ word32 max_ss;
+ whMessageCrypto_MlKemDecapsDmaRequest req;
+ whMessageCrypto_MlKemDecapsDmaResponse res;
+
+ memset(&res, 0, sizeof(res));
+
+ if (inSize < sizeof(whMessageCrypto_MlKemDecapsDmaRequest)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateMlKemDecapsDmaRequest(
+ magic, (whMessageCrypto_MlKemDecapsDmaRequest*)cryptoDataIn, &req);
+ if (ret != WH_ERROR_OK) {
+ return ret;
+ }
+
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
+ evict = !!(req.options & WH_MESSAGE_CRYPTO_MLKEM_DECAPS_OPTIONS_EVICT);
+
+ if (!WH_KEYID_ISERASED(key_id)) {
+ ret = wh_Server_KeystoreFindEnforceKeyUsage(ctx, key_id,
+ WH_NVM_FLAGS_USAGE_DERIVE);
+ if (ret != WH_ERROR_OK) {
+ goto cleanup;
+ }
+ }
+
+ if (!_IsMlKemLevelSupported((int)req.level)) {
+ ret = WH_ERROR_BADARGS;
+ goto cleanup;
+ }
+
+ ret = wc_MlKemKey_Init(key, (int)req.level, NULL, devId);
+ if (ret == WH_ERROR_OK) {
+ keyInited = 1;
+ ret = wh_Server_MlKemKeyCacheExport(ctx, key_id, key);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = wc_MlKemKey_SharedSecretSize(key, &ssLen);
+ }
+
+ /* Validate that the inline shared secret fits in the comm buffer */
+ if (ret == WH_ERROR_OK) {
+ res_ss = (uint8_t*)cryptoDataOut +
+ sizeof(whMessageCrypto_MlKemDecapsDmaResponse);
+ max_ss = (word32)(WOLFHSM_CFG_COMM_DATA_LEN -
+ ((uint8_t*)res_ss - (uint8_t*)cryptoDataOut));
+ if (ssLen > max_ss) {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.ct.addr, &ctAddr, req.ct.sz,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
+ if (ret == WH_ERROR_ACCESS) {
+ res.dmaAddrStatus.badAddr = req.ct;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ /* Shared secret goes inline in response, not via DMA */
+ res_ss = (uint8_t*)cryptoDataOut +
+ sizeof(whMessageCrypto_MlKemDecapsDmaResponse);
+ ret = wc_MlKemKey_Decapsulate(key, res_ss, (const byte*)ctAddr,
+ (word32)req.ct.sz);
+ if (ret != WH_ERROR_OK) {
+ /* Zero sensitive data on failure */
+ wc_ForceZero(res_ss, ssLen);
+ }
+ }
+
+ if (ctAddr != NULL) {
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.ct.addr, &ctAddr, req.ct.sz,
+ WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
+ }
+
+ if (ret == WH_ERROR_OK) {
+ res.ssLen = ssLen;
+ }
+
+ if (keyInited) {
+ wc_MlKemKey_Free(key);
+ }
+cleanup:
+ if (evict != 0) {
+ (void)wh_Server_KeystoreEvictKey(ctx, key_id);
+ }
+
+ (void)wh_MessageCrypto_TranslateMlKemDecapsDmaResponse(
+ magic, &res, (whMessageCrypto_MlKemDecapsDmaResponse*)cryptoDataOut);
+ *outSize = sizeof(res) + ssLen;
+ return ret;
+#endif
+}
+
+static int _HandlePqcKemAlgorithmDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t cryptoInSize, void* cryptoDataOut,
+ uint16_t* cryptoOutSize,
+ uint32_t pkAlgoType, uint32_t pqAlgoType)
+{
+ int ret = WH_ERROR_NOHANDLER;
+
+ switch (pqAlgoType) {
+ case WC_PQC_KEM_TYPE_KYBER: {
+ switch (pkAlgoType) {
+ case WC_PK_TYPE_PQC_KEM_KEYGEN:
+ ret = _HandleMlKemKeyGenDma(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_KEM_ENCAPS:
+ ret = _HandleMlKemEncapsDma(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_KEM_DECAPS:
+ ret = _HandleMlKemDecapsDma(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ default:
+ ret = WH_ERROR_NOHANDLER;
+ break;
+ }
+ } break;
+ default:
+ ret = WH_ERROR_NOHANDLER;
+ break;
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#if defined(WOLFSSL_CMAC) && !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT)
static int _HandleCmacDma(whServerContext* ctx, uint16_t magic, int devId,
uint16_t seq, const void* cryptoDataIn,
@@ -6073,6 +6882,16 @@ int wh_Server_HandleCryptoDmaRequest(whServerContext* ctx, uint16_t magic,
rqstHeader.algoSubType);
break;
#endif /* HAVE_DILITHIUM || HAVE_FALCON */
+#if defined(WOLFSSL_HAVE_MLKEM)
+ case WC_PK_TYPE_PQC_KEM_KEYGEN:
+ case WC_PK_TYPE_PQC_KEM_ENCAPS:
+ case WC_PK_TYPE_PQC_KEM_DECAPS:
+ ret = _HandlePqcKemAlgorithmDma(
+ ctx, magic, devId, cryptoDataIn, cryptoInSize,
+ cryptoDataOut, &cryptoOutSize, rqstHeader.algoType,
+ rqstHeader.algoSubType);
+ break;
+#endif /* WOLFSSL_HAVE_MLKEM */
#ifdef HAVE_ED25519
case WC_PK_TYPE_ED25519_SIGN:
ret = _HandleEd25519SignDma(ctx, magic, devId, cryptoDataIn,
diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c
index e3724474e..ec7a6789a 100644
--- a/src/wh_server_keystore.c
+++ b/src/wh_server_keystore.c
@@ -1237,8 +1237,8 @@ static int _HandleKeyWrapRequest(whServerContext* server,
}
/* Translate the server key id passed in from the client */
- serverKeyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
- server->comm->client_id,
+ serverKeyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id,
req->serverKeyId);
/* Store the wrapped key in the response data */
@@ -1304,8 +1304,8 @@ static int _HandleKeyUnwrapAndExportRequest(
wrappedKey = reqData;
/* Translate the server key id passed in from the client */
- serverKeyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
- server->comm->client_id,
+ serverKeyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id,
req->serverKeyId);
/* Ensure the cipher type in the response matches the request */
@@ -1424,8 +1424,8 @@ static int _HandleKeyUnwrapAndCacheRequest(
wrappedKey = reqData;
/* Translate the server key id passed in from the client */
- serverKeyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
- server->comm->client_id,
+ serverKeyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id,
req->serverKeyId);
/* Ensure the cipher type in the response matches the request */
@@ -1535,8 +1535,8 @@ static int _HandleDataWrapRequest(whServerContext* server,
memcpy(data, reqData, req->dataSz);
/* Translate the server key id passed in from the client */
- serverKeyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
- server->comm->client_id,
+ serverKeyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ server->comm->client_id,
req->serverKeyId);
/* Ensure the cipher type in the response matches the request */
@@ -1806,7 +1806,7 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
}
if (ret == WH_ERROR_OK) {
- resp.len = req.key.sz;
+ resp.len = meta->len;
memcpy(resp.label, meta->label, sizeof(meta->label));
}
diff --git a/test/config/user_settings.h b/test/config/user_settings.h
index e86389345..e6fdf0629 100644
--- a/test/config/user_settings.h
+++ b/test/config/user_settings.h
@@ -139,6 +139,10 @@
#define WOLFSSL_SHAKE128
#define WOLFSSL_SHAKE256
+/* ML-KEM Options */
+#define WOLFSSL_HAVE_MLKEM
+#define WOLFSSL_WC_MLKEM
+
/* Ed25519 Options */
#define HAVE_ED25519
diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c
index 0217912fe..c9cd470dc 100644
--- a/test/wh_test_crypto.c
+++ b/test/wh_test_crypto.c
@@ -32,6 +32,7 @@
#include "wolfssl/wolfcrypt/types.h"
#include "wolfssl/wolfcrypt/kdf.h"
#include "wolfssl/wolfcrypt/ed25519.h"
+#include "wolfssl/wolfcrypt/wc_mlkem.h"
#include "wolfhsm/wh_error.h"
@@ -57,6 +58,7 @@
#endif
#include "wolfhsm/wh_transport_mem.h"
+#include "wolfhsm/wh_crypto.h"
#include "wh_test_common.h"
@@ -4304,14 +4306,14 @@ static int whTestCrypto_MlDsaClient(whClientContext* ctx, int devId,
word32 sigLen = sizeof(sig);
int verified = 0;
- ret = wh_Client_MlDsaSign(ctx, msg, sizeof(msg), sig, &sigLen, key,
+ ret = wh_Client_MlDsaSign(ctx, msg, sizeof(msg), sig, &sigLen, key,
NULL, 0, WC_HASH_TYPE_NONE);
if (ret != 0) {
WH_ERROR_PRINT("Failed to sign using ML-DSA non-DMA: %d\n", ret);
}
else {
ret = wh_Client_MlDsaVerify(ctx, sig, sigLen, msg, sizeof(msg),
- &verified, key, NULL, 0,
+ &verified, key, NULL, 0,
WC_HASH_TYPE_NONE);
if (ret != 0) {
WH_ERROR_PRINT("Failed to verify ML-DSA non-DMA: %d\n", ret);
@@ -4505,7 +4507,7 @@ static int whTestCrypto_MlDsaDmaClient(whClientContext* ctx, int devId,
else {
/* Verify the signature - should succeed */
ret = wh_Client_MlDsaVerifyDma(ctx, sig, sigLen, msg, sizeof(msg),
- &verified, key, NULL, 0,
+ &verified, key, NULL, 0,
WC_HASH_TYPE_NONE);
if (ret != 0) {
WH_ERROR_PRINT("Failed to verify signature using ML-DSA: %d\n",
@@ -5073,6 +5075,533 @@ int whTestCrypto_MlDsaVerifyOnlyDma(whClientContext* ctx, int devId,
#endif /* HAVE_DILITHIUM */
+#ifdef WOLFSSL_HAVE_MLKEM
+#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
+ !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
+ !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
+static int whTestCrypto_MlKemGetLevels(int* levels, int maxLevels)
+{
+ int count = 0;
+
+#ifndef WOLFSSL_NO_ML_KEM_512
+ if (count < maxLevels) {
+ levels[count++] = WC_ML_KEM_512;
+ }
+#endif
+#ifndef WOLFSSL_NO_ML_KEM_768
+ if (count < maxLevels) {
+ levels[count++] = WC_ML_KEM_768;
+ }
+#endif
+#ifndef WOLFSSL_NO_ML_KEM_1024
+ if (count < maxLevels) {
+ levels[count++] = WC_ML_KEM_1024;
+ }
+#endif
+
+ return count;
+}
+
+static int whTestCrypto_MlKemWolfCrypt(whClientContext* ctx, int devId,
+ WC_RNG* rng)
+{
+ int ret = 0;
+ int levels[3];
+ int levelCnt = 0;
+ int i;
+ byte ct[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
+ byte ssEnc[WC_ML_KEM_SS_SZ];
+ byte ssDec[WC_ML_KEM_SS_SZ];
+ word32 ctLen;
+ word32 ssEncLen;
+ word32 ssDecLen;
+
+ (void)ctx;
+
+ levelCnt =
+ whTestCrypto_MlKemGetLevels(levels, (int)(sizeof(levels) / sizeof(levels[0])));
+
+ for (i = 0; (ret == 0) && (i < levelCnt); i++) {
+ MlKemKey key[1];
+ int keyInited = 0;
+
+ ctLen = sizeof(ct);
+ ssEncLen = sizeof(ssEnc);
+ ssDecLen = sizeof(ssDec);
+ memset(ct, 0, sizeof(ct));
+ memset(ssEnc, 0, sizeof(ssEnc));
+ memset(ssDec, 0, sizeof(ssDec));
+
+ ret = wc_MlKemKey_Init(key, levels[i], NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to init ML-KEM key level=%d ret=%d\n",
+ levels[i], ret);
+ break;
+ }
+ keyInited = 1;
+
+ ret = wc_MlKemKey_MakeKey(key, rng);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to make ML-KEM key level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ if (ret == 0) {
+ ret = wc_MlKemKey_CipherTextSize(key, &ctLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to get ML-KEM ct size level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ }
+ if (ret == 0) {
+ ret = wc_MlKemKey_SharedSecretSize(key, &ssEncLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to get ML-KEM ss size level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ else {
+ ssDecLen = ssEncLen;
+ }
+ }
+ if (ret == 0) {
+ ret = wc_MlKemKey_Encapsulate(key, ct, ssEnc, rng);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM encapsulate level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ }
+ if (ret == 0) {
+ ret = wc_MlKemKey_Decapsulate(key, ssDec, ct, ctLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM decapsulate level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ else if ((ssEncLen != ssDecLen) ||
+ (memcmp(ssEnc, ssDec, ssEncLen) != 0)) {
+ WH_ERROR_PRINT("ML-KEM shared secret mismatch level=%d\n",
+ levels[i]);
+ ret = -1;
+ }
+ }
+
+ if (keyInited) {
+ wc_MlKemKey_Free(key);
+ }
+ }
+
+ if (ret == 0) {
+ WH_TEST_PRINT("ML-KEM DEVID=0x%X SUCCESS\n", devId);
+ }
+
+ return ret;
+}
+
+static int whTestCrypto_MlKemClient(whClientContext* ctx, int devId, WC_RNG* rng)
+{
+ int ret = 0;
+ int levels[3];
+ int levelCnt = 0;
+ int i;
+ byte ct[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
+ byte ssEnc[WC_ML_KEM_SS_SZ];
+ byte ssDec[WC_ML_KEM_SS_SZ];
+ byte ssWrong[WC_ML_KEM_SS_SZ];
+ byte usageCt[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
+ byte usageSs[WC_ML_KEM_SS_SZ];
+ word32 ctLen;
+ word32 ssEncLen;
+ word32 ssDecLen;
+ word32 ssWrongLen;
+ word32 usageCtLen;
+ word32 usageSsLen;
+ const uint8_t usageLabel[] = "mlkem-no-derive";
+
+ (void)rng;
+
+ levelCnt =
+ whTestCrypto_MlKemGetLevels(levels, (int)(sizeof(levels) / sizeof(levels[0])));
+
+ for (i = 0; (ret == 0) && (i < levelCnt); i++) {
+ MlKemKey key[1];
+ MlKemKey wrongKey[1];
+ MlKemKey usageKey[1];
+ int keyInited = 0;
+ int wrongInited = 0;
+ int usageInited = 0;
+ whKeyId usageKeyId = WH_KEYID_ERASED;
+ int usageKeyCached = 0;
+
+ ctLen = sizeof(ct);
+ ssEncLen = sizeof(ssEnc);
+ ssDecLen = sizeof(ssDec);
+ ssWrongLen = sizeof(ssWrong);
+ usageCtLen = sizeof(usageCt);
+ usageSsLen = sizeof(usageSs);
+ memset(ct, 0, sizeof(ct));
+ memset(ssEnc, 0, sizeof(ssEnc));
+ memset(ssDec, 0, sizeof(ssDec));
+ memset(ssWrong, 0, sizeof(ssWrong));
+ memset(usageCt, 0, sizeof(usageCt));
+ memset(usageSs, 0, sizeof(usageSs));
+
+ ret = wc_MlKemKey_Init(key, levels[i], NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to init ML-KEM client key level=%d ret=%d\n",
+ levels[i], ret);
+ break;
+ }
+ keyInited = 1;
+
+ ret = wc_MlKemKey_Init(wrongKey, levels[i], NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT(
+ "Failed to init ML-KEM wrong key level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ else {
+ wrongInited = 1;
+ }
+
+ if (ret == 0) {
+ ret = wh_Client_MlKemMakeExportKey(ctx, levels[i], key);
+ if (ret != 0) {
+ WH_ERROR_PRINT(
+ "Failed ML-KEM make export key level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ }
+ if (ret == 0) {
+ ret = wh_Client_MlKemMakeExportKey(ctx, levels[i], wrongKey);
+ if (ret != 0) {
+ WH_ERROR_PRINT(
+ "Failed ML-KEM make wrong export key level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ }
+
+ if (ret == 0) {
+ ret = wh_Client_MlKemEncapsulate(ctx, key, ct, &ctLen, ssEnc,
+ &ssEncLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM encapsulate level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ }
+ if (ret == 0) {
+ ret = wh_Client_MlKemDecapsulate(ctx, key, ct, ctLen, ssDec,
+ &ssDecLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM decapsulate level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ else if ((ssEncLen != ssDecLen) ||
+ (memcmp(ssEnc, ssDec, ssEncLen) != 0)) {
+ WH_ERROR_PRINT("ML-KEM client shared secret mismatch level=%d\n",
+ levels[i]);
+ ret = -1;
+ }
+ }
+ if (ret == 0) {
+ ret = wh_Client_MlKemDecapsulate(ctx, wrongKey, ct, ctLen, ssWrong,
+ &ssWrongLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT(
+ "Failed ML-KEM wrong-key decapsulate level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ else if ((ssWrongLen == ssEncLen) &&
+ (memcmp(ssWrong, ssEnc, ssEncLen) == 0)) {
+ WH_ERROR_PRINT(
+ "ML-KEM wrong-key decaps unexpectedly matched level=%d\n",
+ levels[i]);
+ ret = -1;
+ }
+ }
+
+ if (ret == 0) {
+ ret = wh_Client_MlKemMakeCacheKey(
+ ctx, levels[i], &usageKeyId, WH_NVM_FLAGS_NONE,
+ (uint16_t)strlen((const char*)usageLabel), (uint8_t*)usageLabel);
+ if (ret != 0) {
+ WH_ERROR_PRINT(
+ "Failed ML-KEM cache key without derive level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ else {
+ usageKeyCached = 1;
+ }
+ }
+ if (ret == 0) {
+ ret = wc_MlKemKey_Init(usageKey, levels[i], NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed init ML-KEM usage key level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ else {
+ usageInited = 1;
+ }
+ }
+ if (ret == 0) {
+ ret = wh_Client_MlKemSetKeyId(usageKey, usageKeyId);
+ if (ret != 0) {
+ WH_ERROR_PRINT(
+ "Failed set ML-KEM usage key ID level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ }
+ if (ret == 0) {
+ ret = wh_Client_MlKemEncapsulate(ctx, usageKey, usageCt, &usageCtLen,
+ usageSs, &usageSsLen);
+ if (ret == WH_ERROR_USAGE) {
+ ret = 0;
+ }
+ else {
+ WH_ERROR_PRINT("Expected WH_ERROR_USAGE for ML-KEM derive "
+ "policy level=%d got=%d\n",
+ levels[i], ret);
+ ret = WH_ERROR_ABORTED;
+ }
+ }
+
+ if (usageKeyCached) {
+ int evictRet = wh_Client_KeyEvict(ctx, usageKeyId);
+ if ((evictRet != 0) && (ret == 0)) {
+ WH_ERROR_PRINT("Failed ML-KEM usage key evict level=%d ret=%d\n",
+ levels[i], evictRet);
+ ret = evictRet;
+ }
+ }
+ if (usageInited) {
+ wc_MlKemKey_Free(usageKey);
+ }
+ if (wrongInited) {
+ wc_MlKemKey_Free(wrongKey);
+ }
+ if (keyInited) {
+ wc_MlKemKey_Free(key);
+ }
+ }
+
+ if (ret == 0) {
+ WH_TEST_PRINT("ML-KEM Client Non-DMA API SUCCESS\n");
+ }
+
+ return ret;
+}
+
+#ifdef WOLFHSM_CFG_DMA
+static int whTestCrypto_MlKemDmaClient(whClientContext* ctx, int devId,
+ WC_RNG* rng)
+{
+ int ret = 0;
+ int levels[3];
+ int levelCnt = 0;
+ int i;
+ byte ct[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
+ byte ssEnc[WC_ML_KEM_SS_SZ];
+ byte ssDec[WC_ML_KEM_SS_SZ];
+ byte ssWrong[WC_ML_KEM_SS_SZ];
+ byte keyBuf1[WC_ML_KEM_MAX_PRIVATE_KEY_SIZE];
+ byte keyBuf2[WC_ML_KEM_MAX_PRIVATE_KEY_SIZE];
+ word32 ctLen;
+ word32 ssEncLen;
+ word32 ssDecLen;
+ word32 ssWrongLen;
+ uint16_t keyBuf1Len;
+ uint16_t keyBuf2Len;
+ whKeyId keyId;
+ const uint8_t cacheLabel[] = "mlkem-dma-cache";
+
+ (void)rng;
+
+ levelCnt =
+ whTestCrypto_MlKemGetLevels(levels, (int)(sizeof(levels) / sizeof(levels[0])));
+
+ for (i = 0; (ret == 0) && (i < levelCnt); i++) {
+ MlKemKey key[1];
+ MlKemKey importedKey[1];
+ MlKemKey wrongKey[1];
+ int keyInited = 0;
+ int importedInited = 0;
+ int wrongInited = 0;
+ int keyCached = 0;
+
+ ctLen = sizeof(ct);
+ ssEncLen = sizeof(ssEnc);
+ ssDecLen = sizeof(ssDec);
+ ssWrongLen = sizeof(ssWrong);
+ keyBuf1Len = sizeof(keyBuf1);
+ keyBuf2Len = sizeof(keyBuf2);
+ keyId = WH_KEYID_ERASED;
+
+ memset(ct, 0, sizeof(ct));
+ memset(ssEnc, 0, sizeof(ssEnc));
+ memset(ssDec, 0, sizeof(ssDec));
+ memset(ssWrong, 0, sizeof(ssWrong));
+ memset(keyBuf1, 0, sizeof(keyBuf1));
+ memset(keyBuf2, 0, sizeof(keyBuf2));
+
+ ret = wc_MlKemKey_Init(key, levels[i], NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed init ML-KEM DMA key level=%d ret=%d\n",
+ levels[i], ret);
+ break;
+ }
+ keyInited = 1;
+
+ ret = wc_MlKemKey_Init(importedKey, levels[i], NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed init ML-KEM DMA imported key level=%d "
+ "ret=%d\n",
+ levels[i], ret);
+ }
+ else {
+ importedInited = 1;
+ }
+
+ if (ret == 0) {
+ ret = wc_MlKemKey_Init(wrongKey, levels[i], NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed init ML-KEM DMA wrong key level=%d "
+ "ret=%d\n",
+ levels[i], ret);
+ }
+ else {
+ wrongInited = 1;
+ }
+ }
+
+ if (ret == 0) {
+ ret = wh_Client_MlKemMakeExportKeyDma(ctx, levels[i], key);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM DMA keygen level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ }
+ if (ret == 0) {
+ ret = wh_Client_MlKemMakeExportKeyDma(ctx, levels[i], wrongKey);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM DMA wrong keygen level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ }
+
+ if (ret == 0) {
+ ret = wh_Crypto_MlKemSerializeKey(key, keyBuf1Len, keyBuf1,
+ &keyBuf1Len);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM DMA serialize key level=%d "
+ "ret=%d\n",
+ levels[i], ret);
+ }
+ }
+ if (ret == 0) {
+ ret = wh_Client_MlKemImportKeyDma(
+ ctx, key, &keyId, WH_NVM_FLAGS_NONE,
+ (uint16_t)strlen((const char*)cacheLabel), (uint8_t*)cacheLabel);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM DMA import key level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ else {
+ keyCached = 1;
+ }
+ }
+ if (ret == 0) {
+ ret = wh_Client_MlKemExportKeyDma(
+ ctx, keyId, importedKey,
+ (uint16_t)strlen((const char*)cacheLabel), (uint8_t*)cacheLabel);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM DMA export key level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ }
+ if (ret == 0) {
+ ret = wh_Crypto_MlKemSerializeKey(importedKey, keyBuf2Len, keyBuf2,
+ &keyBuf2Len);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM DMA serialize imported key "
+ "level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ else if ((keyBuf1Len != keyBuf2Len) ||
+ (memcmp(keyBuf1, keyBuf2, keyBuf1Len) != 0)) {
+ WH_ERROR_PRINT("ML-KEM DMA imported key mismatch level=%d\n",
+ levels[i]);
+ ret = -1;
+ }
+ }
+
+ if (ret == 0) {
+ ret = wh_Client_MlKemEncapsulateDma(ctx, key, ct, &ctLen, ssEnc,
+ &ssEncLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM DMA encapsulate level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ }
+ if (ret == 0) {
+ ret = wh_Client_MlKemDecapsulateDma(ctx, key, ct, ctLen, ssDec,
+ &ssDecLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM DMA decapsulate level=%d ret=%d\n",
+ levels[i], ret);
+ }
+ else if ((ssEncLen != ssDecLen) ||
+ (memcmp(ssEnc, ssDec, ssEncLen) != 0)) {
+ WH_ERROR_PRINT("ML-KEM DMA shared secret mismatch level=%d\n",
+ levels[i]);
+ ret = -1;
+ }
+ }
+ if (ret == 0) {
+ ret = wh_Client_MlKemDecapsulateDma(ctx, wrongKey, ct, ctLen,
+ ssWrong, &ssWrongLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed ML-KEM DMA wrong-key decaps level=%d "
+ "ret=%d\n",
+ levels[i], ret);
+ }
+ else if ((ssWrongLen == ssEncLen) &&
+ (memcmp(ssWrong, ssEnc, ssEncLen) == 0)) {
+ WH_ERROR_PRINT("ML-KEM DMA wrong-key decaps unexpectedly "
+ "matched level=%d\n",
+ levels[i]);
+ ret = -1;
+ }
+ }
+
+ if (keyCached) {
+ int evictRet = wh_Client_KeyEvict(ctx, keyId);
+ if ((evictRet != 0) && (ret == 0)) {
+ WH_ERROR_PRINT("Failed ML-KEM DMA evict cached key level=%d "
+ "ret=%d\n",
+ levels[i], evictRet);
+ ret = evictRet;
+ }
+ }
+ if (wrongInited) {
+ wc_MlKemKey_Free(wrongKey);
+ }
+ if (importedInited) {
+ wc_MlKemKey_Free(importedKey);
+ }
+ if (keyInited) {
+ wc_MlKemKey_Free(key);
+ }
+ }
+
+ if (ret == 0) {
+ WH_TEST_PRINT("ML-KEM Client DMA API SUCCESS\n");
+ }
+
+ return ret;
+}
+#endif /* WOLFHSM_CFG_DMA */
+#endif /* !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
+ !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
+ !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) */
+#endif /* WOLFSSL_HAVE_MLKEM */
+
/* Test key usage policy enforcement for various crypto operations */
int whTest_CryptoKeyUsagePolicies(whClientContext* client, WC_RNG* rng)
{
@@ -5976,6 +6505,38 @@ int whTest_CryptoClientConfig(whClientConfig* config)
#endif /* HAVE_DILITHIUM */
+#ifdef WOLFSSL_HAVE_MLKEM
+#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
+ !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
+ !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
+ i = 0;
+ while (ret == WH_ERROR_OK && i < WH_NUM_DEVIDS) {
+#ifdef WOLFHSM_CFG_TEST_CLIENT_LARGE_DATA_DMA_ONLY
+ if (WH_DEV_IDS_ARRAY[i] != WH_DEV_ID_DMA) {
+ i++;
+ continue;
+ }
+#endif /* WOLFHSM_CFG_TEST_CLIENT_LARGE_DATA_DMA_ONLY */
+ ret = whTestCrypto_MlKemWolfCrypt(client, WH_DEV_IDS_ARRAY[i], rng);
+ if (ret == WH_ERROR_OK) {
+ i++;
+ }
+ }
+
+ if (ret == 0) {
+ ret = whTestCrypto_MlKemClient(client, WH_DEV_ID, rng);
+ }
+
+#ifdef WOLFHSM_CFG_DMA
+ if (ret == 0) {
+ ret = whTestCrypto_MlKemDmaClient(client, WH_DEV_ID_DMA, rng);
+ }
+#endif /* WOLFHSM_CFG_DMA */
+#endif /* !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
+ !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
+ !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) */
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#ifdef WOLFHSM_CFG_DEBUG_VERBOSE
if (ret == 0) {
(void)whTest_ShowNvmAvailable(client);
diff --git a/wolfhsm/wh_client_crypto.h b/wolfhsm/wh_client_crypto.h
index b09514a28..22e68bc9a 100644
--- a/wolfhsm/wh_client_crypto.h
+++ b/wolfhsm/wh_client_crypto.h
@@ -50,6 +50,7 @@
#include "wolfssl/wolfcrypt/ecc.h"
#include "wolfssl/wolfcrypt/ed25519.h"
#include "wolfssl/wolfcrypt/dilithium.h"
+#include "wolfssl/wolfcrypt/wc_mlkem.h"
#include "wolfssl/wolfcrypt/hmac.h"
/**
@@ -1292,5 +1293,204 @@ int wh_Client_MlDsaCheckPrivKeyDma(whClientContext* ctx, MlDsaKey* key,
#endif /* HAVE_DILITHIUM */
+#ifdef WOLFSSL_HAVE_MLKEM
+
+/**
+ * @brief Associate a ML-KEM key with a specific key ID.
+ *
+ * Sets the device context of a ML-KEM key to the specified key ID. On the
+ * server side, this key ID is used to reference the key stored in the HSM.
+ *
+ * @param[in] key Pointer to the ML-KEM key structure.
+ * @param[in] keyId Key ID to be associated with the ML-KEM key.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemSetKeyId(MlKemKey* key, whKeyId keyId);
+
+/**
+ * @brief Retrieve the key ID associated with a ML-KEM key.
+ *
+ * @param[in] key Pointer to the ML-KEM key structure.
+ * @param[out] outId Pointer to store the retrieved key ID.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemGetKeyId(MlKemKey* key, whKeyId* outId);
+
+/**
+ * @brief Import a ML-KEM key to the server key cache.
+ *
+ * @param[in] ctx Pointer to the client context.
+ * @param[in] key Pointer to the ML-KEM key to import.
+ * @param[in,out] inout_keyId Pointer to key ID to use/receive.
+ * @param[in] flags Flags to control key persistence.
+ * @param[in] label_len Length of optional label in bytes.
+ * @param[in] label Optional label to associate with the key.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemImportKey(whClientContext* ctx, MlKemKey* key,
+ whKeyId* inout_keyId, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label);
+
+/**
+ * @brief Export a ML-KEM key from the server key cache.
+ *
+ * @param[in] ctx Pointer to the client context.
+ * @param[in] keyId Key ID of the key to export.
+ * @param[out] key Pointer to the ML-KEM key structure to populate.
+ * @param[in] label_len Length of optional label in bytes.
+ * @param[out] label Optional label buffer to receive the key label.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemExportKey(whClientContext* ctx, whKeyId keyId, MlKemKey* key,
+ uint16_t label_len, uint8_t* label);
+
+/**
+ * @brief Generate a ML-KEM key pair and return it as an ephemeral key.
+ *
+ * The key pair is generated on the server, serialized, and returned to the
+ * client without being cached.
+ *
+ * @param[in] ctx Pointer to the client context.
+ * @param[in] level ML-KEM security level (WC_ML_KEM_512/768/1024).
+ * @param[out] key Pointer to the ML-KEM key to populate with the generated key.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemMakeExportKey(whClientContext* ctx, int level,
+ MlKemKey* key);
+
+/**
+ * @brief Generate a ML-KEM key pair and cache it on the server.
+ *
+ * @param[in] ctx Pointer to the client context.
+ * @param[in] level ML-KEM security level (WC_ML_KEM_512/768/1024).
+ * @param[in,out] inout_key_id Pointer to key ID to use/receive.
+ * @param[in] flags Flags to control key persistence and usage.
+ * @param[in] label_len Length of optional label in bytes.
+ * @param[in] label Optional label to associate with the key.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemMakeCacheKey(whClientContext* ctx, int level,
+ whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label);
+
+/**
+ * @brief Perform ML-KEM encapsulation using a server-cached public key.
+ *
+ * Generates a shared secret and ciphertext using the public key identified by
+ * the key ID stored in the provided MlKemKey. If the key is not yet cached,
+ * it will be auto-imported and evicted after use.
+ *
+ * @param[in] ctx Pointer to the client context.
+ * @param[in] key Pointer to the ML-KEM key (must have key ID set).
+ * @param[out] ct Buffer to receive the ciphertext.
+ * @param[in,out] inout_ct_len On input, size of ct buffer; on output, actual
+ * ciphertext length.
+ * @param[out] ss Buffer to receive the shared secret.
+ * @param[in,out] inout_ss_len On input, size of ss buffer; on output, actual
+ * shared secret length.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemEncapsulate(whClientContext* ctx, MlKemKey* key,
+ byte* ct, word32* inout_ct_len,
+ byte* ss, word32* inout_ss_len);
+
+/**
+ * @brief Perform ML-KEM decapsulation using a server-cached private key.
+ *
+ * Recovers the shared secret from the ciphertext using the private key
+ * identified by the key ID stored in the provided MlKemKey. If the key is not
+ * yet cached, it will be auto-imported and evicted after use.
+ *
+ * @param[in] ctx Pointer to the client context.
+ * @param[in] key Pointer to the ML-KEM key (must have key ID set).
+ * @param[in] ct Pointer to the ciphertext.
+ * @param[in] ct_len Length of the ciphertext in bytes.
+ * @param[out] ss Buffer to receive the shared secret.
+ * @param[in,out] inout_ss_len On input, size of ss buffer; on output, actual
+ * shared secret length.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemDecapsulate(whClientContext* ctx, MlKemKey* key,
+ const byte* ct, word32 ct_len, byte* ss,
+ word32* inout_ss_len);
+
+#ifdef WOLFHSM_CFG_DMA
+
+/**
+ * @brief Import a ML-KEM key using DMA.
+ *
+ * @param[in] ctx Pointer to the client context.
+ * @param[in] key Pointer to the ML-KEM key to import.
+ * @param[in,out] inout_keyId Pointer to store/provide the key ID.
+ * @param[in] flags NVM flags for key storage.
+ * @param[in] label_len Length of the key label in bytes.
+ * @param[in] label Pointer to the key label.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemImportKeyDma(whClientContext* ctx, MlKemKey* key,
+ whKeyId* inout_keyId, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label);
+
+/**
+ * @brief Export a ML-KEM key from the server using DMA.
+ *
+ * @param[in] ctx Pointer to the client context.
+ * @param[in] keyId Key ID of the key to export.
+ * @param[out] key Pointer to the ML-KEM key structure to populate.
+ * @param[in] label_len Length of the key label in bytes.
+ * @param[out] label Pointer to the key label buffer.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemExportKeyDma(whClientContext* ctx, whKeyId keyId,
+ MlKemKey* key, uint16_t label_len,
+ uint8_t* label);
+
+/**
+ * @brief Generate an ephemeral ML-KEM key pair using DMA.
+ *
+ * @param[in] ctx Pointer to the client context.
+ * @param[in] level ML-KEM security level (WC_ML_KEM_512/768/1024).
+ * @param[out] key Pointer to the ML-KEM key to populate.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemMakeExportKeyDma(whClientContext* ctx, int level,
+ MlKemKey* key);
+
+/**
+ * @brief Perform ML-KEM encapsulation using DMA.
+ *
+ * @param[in] ctx Pointer to the client context.
+ * @param[in] key Pointer to the ML-KEM key (must have key ID set).
+ * @param[out] ct Buffer to receive the ciphertext.
+ * @param[in,out] inout_ct_len On input, size of ct buffer; on output, actual
+ * ciphertext length.
+ * @param[out] ss Buffer to receive the shared secret.
+ * @param[in,out] inout_ss_len On input, size of ss buffer; on output, actual
+ * shared secret length.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemEncapsulateDma(whClientContext* ctx, MlKemKey* key,
+ byte* ct, word32* inout_ct_len, byte* ss,
+ word32* inout_ss_len);
+
+/**
+ * @brief Perform ML-KEM decapsulation using DMA.
+ *
+ * @param[in] ctx Pointer to the client context.
+ * @param[in] key Pointer to the ML-KEM key (must have key ID set).
+ * @param[in] ct Pointer to the ciphertext.
+ * @param[in] ct_len Length of the ciphertext in bytes.
+ * @param[out] ss Buffer to receive the shared secret.
+ * @param[in,out] inout_ss_len On input, size of ss buffer; on output, actual
+ * shared secret length.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_MlKemDecapsulateDma(whClientContext* ctx, MlKemKey* key,
+ const byte* ct, word32 ct_len, byte* ss,
+ word32* inout_ss_len);
+#endif /* WOLFHSM_CFG_DMA */
+
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#endif /* !WOLFHSM_CFG_NO_CRYPTO */
#endif /* !WOLFHSM_WH_CLIENT_CRYPTO_H_ */
diff --git a/wolfhsm/wh_crypto.h b/wolfhsm/wh_crypto.h
index 7254d783c..6f5cfc9c3 100644
--- a/wolfhsm/wh_crypto.h
+++ b/wolfhsm/wh_crypto.h
@@ -43,6 +43,7 @@
#include "wolfssl/wolfcrypt/ecc.h"
#include "wolfssl/wolfcrypt/ed25519.h"
#include "wolfssl/wolfcrypt/dilithium.h"
+#include "wolfssl/wolfcrypt/wc_mlkem.h"
#include "wolfhsm/wh_message_crypto.h"
@@ -118,6 +119,16 @@ int wh_Crypto_MlDsaDeserializeKeyDer(const uint8_t* buffer, uint16_t size,
MlDsaKey* key);
#endif /* HAVE_DILITHIUM */
+#ifdef WOLFSSL_HAVE_MLKEM
+/* Store a MlKemKey to a byte sequence */
+int wh_Crypto_MlKemSerializeKey(MlKemKey* key, uint16_t max_size,
+ uint8_t* buffer, uint16_t* out_size);
+/* Restore a MlKemKey from a byte sequence. Tries the level already set in the
+ * key first, then probes other supported ML-KEM levels if needed. */
+int wh_Crypto_MlKemDeserializeKey(const uint8_t* buffer, uint16_t size,
+ MlKemKey* key);
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#endif /* !WOLFHSM_CFG_NO_CRYPTO */
#endif /* WOLFHSM_WH_CRYPTO_H_ */
diff --git a/wolfhsm/wh_message_crypto.h b/wolfhsm/wh_message_crypto.h
index 6f33d89cf..82aa4a6d8 100644
--- a/wolfhsm/wh_message_crypto.h
+++ b/wolfhsm/wh_message_crypto.h
@@ -947,6 +947,92 @@ int wh_MessageCrypto_TranslateMlDsaVerifyResponse(
uint16_t magic, const whMessageCrypto_MlDsaVerifyResponse* src,
whMessageCrypto_MlDsaVerifyResponse* dest);
+/*
+ * ML-KEM
+ */
+
+/* ML-KEM Key Generation Request */
+typedef struct {
+ uint32_t level;
+ uint32_t keyId;
+ uint32_t flags;
+ uint32_t access;
+ uint8_t label[WH_NVM_LABEL_LEN];
+} whMessageCrypto_MlKemKeyGenRequest;
+
+/* ML-KEM Key Generation Response */
+typedef struct {
+ uint32_t keyId;
+ uint32_t len;
+ /* Data follows:
+ * uint8_t out[len];
+ */
+} whMessageCrypto_MlKemKeyGenResponse;
+
+int wh_MessageCrypto_TranslateMlKemKeyGenRequest(
+ uint16_t magic, const whMessageCrypto_MlKemKeyGenRequest* src,
+ whMessageCrypto_MlKemKeyGenRequest* dest);
+
+int wh_MessageCrypto_TranslateMlKemKeyGenResponse(
+ uint16_t magic, const whMessageCrypto_MlKemKeyGenResponse* src,
+ whMessageCrypto_MlKemKeyGenResponse* dest);
+
+/* ML-KEM Encapsulation Request */
+typedef struct {
+ uint32_t options;
+#define WH_MESSAGE_CRYPTO_MLKEM_ENCAPS_OPTIONS_EVICT (1 << 0)
+ uint32_t level;
+ uint32_t keyId;
+ uint8_t WH_PAD[4];
+} whMessageCrypto_MlKemEncapsRequest;
+
+/* ML-KEM Encapsulation Response */
+typedef struct {
+ uint32_t ctSz;
+ uint32_t ssSz;
+ /* Data follows:
+ * uint8_t ct[ctSz];
+ * uint8_t ss[ssSz];
+ */
+} whMessageCrypto_MlKemEncapsResponse;
+
+int wh_MessageCrypto_TranslateMlKemEncapsRequest(
+ uint16_t magic, const whMessageCrypto_MlKemEncapsRequest* src,
+ whMessageCrypto_MlKemEncapsRequest* dest);
+
+int wh_MessageCrypto_TranslateMlKemEncapsResponse(
+ uint16_t magic, const whMessageCrypto_MlKemEncapsResponse* src,
+ whMessageCrypto_MlKemEncapsResponse* dest);
+
+/* ML-KEM Decapsulation Request */
+typedef struct {
+ uint32_t options;
+#define WH_MESSAGE_CRYPTO_MLKEM_DECAPS_OPTIONS_EVICT (1 << 0)
+ uint32_t level;
+ uint32_t keyId;
+ uint32_t ctSz;
+ /* Data follows:
+ * uint8_t ct[ctSz];
+ */
+} whMessageCrypto_MlKemDecapsRequest;
+
+/* ML-KEM Decapsulation Response */
+typedef struct {
+ uint32_t ssSz;
+ uint8_t WH_PAD[4];
+ /* Data follows:
+ * uint8_t ss[ssSz];
+ */
+} whMessageCrypto_MlKemDecapsResponse;
+
+int wh_MessageCrypto_TranslateMlKemDecapsRequest(
+ uint16_t magic, const whMessageCrypto_MlKemDecapsRequest* src,
+ whMessageCrypto_MlKemDecapsRequest* dest);
+
+int wh_MessageCrypto_TranslateMlKemDecapsResponse(
+ uint16_t magic, const whMessageCrypto_MlKemDecapsResponse* src,
+ whMessageCrypto_MlKemDecapsResponse* dest);
+
/*
* DMA-based crypto messages
@@ -1237,6 +1323,92 @@ int wh_MessageCrypto_TranslateMlDsaVerifyDmaResponse(
uint16_t magic, const whMessageCrypto_MlDsaVerifyDmaResponse* src,
whMessageCrypto_MlDsaVerifyDmaResponse* dest);
+/* ML-KEM DMA Key Generation Request */
+typedef struct {
+ whMessageCrypto_DmaBuffer key;
+ uint32_t level;
+ uint32_t flags;
+ uint32_t keyId;
+ uint32_t access; /* Key access permissions */
+ uint32_t labelSize;
+ uint8_t label[WH_NVM_LABEL_LEN];
+ uint8_t WH_PAD2[4];
+} whMessageCrypto_MlKemKeyGenDmaRequest;
+
+/* ML-KEM DMA Key Generation Response */
+typedef struct {
+ whMessageCrypto_DmaAddrStatus dmaAddrStatus;
+ uint32_t keyId;
+ uint32_t keySize;
+} whMessageCrypto_MlKemKeyGenDmaResponse;
+
+/* ML-KEM DMA Encapsulation Request
+ * Note: The shared secret is transferred inline in the response (not via DMA)
+ * since it is always WC_ML_KEM_SS_SZ (32) bytes, similar to AES key handling.
+ */
+typedef struct {
+ whMessageCrypto_DmaBuffer ct;
+ uint32_t options;
+ uint32_t level;
+ uint32_t keyId;
+ uint8_t WH_PAD[4];
+} whMessageCrypto_MlKemEncapsDmaRequest;
+
+/* ML-KEM DMA Encapsulation Response */
+typedef struct {
+ whMessageCrypto_DmaAddrStatus dmaAddrStatus;
+ uint32_t ctLen;
+ uint32_t ssLen;
+ /* Data follows:
+ * uint8_t ss[ssLen];
+ */
+} whMessageCrypto_MlKemEncapsDmaResponse;
+
+/* ML-KEM DMA Decapsulation Request
+ * Note: The shared secret is transferred inline in the response (not via DMA).
+ */
+typedef struct {
+ whMessageCrypto_DmaBuffer ct;
+ uint32_t options;
+ uint32_t level;
+ uint32_t keyId;
+ uint8_t WH_PAD[4];
+} whMessageCrypto_MlKemDecapsDmaRequest;
+
+/* ML-KEM DMA Decapsulation Response */
+typedef struct {
+ whMessageCrypto_DmaAddrStatus dmaAddrStatus;
+ uint32_t ssLen;
+ /* Data follows:
+ * uint8_t ss[ssLen];
+ */
+} whMessageCrypto_MlKemDecapsDmaResponse;
+
+/* ML-KEM DMA translation functions */
+int wh_MessageCrypto_TranslateMlKemKeyGenDmaRequest(
+ uint16_t magic, const whMessageCrypto_MlKemKeyGenDmaRequest* src,
+ whMessageCrypto_MlKemKeyGenDmaRequest* dest);
+
+int wh_MessageCrypto_TranslateMlKemKeyGenDmaResponse(
+ uint16_t magic, const whMessageCrypto_MlKemKeyGenDmaResponse* src,
+ whMessageCrypto_MlKemKeyGenDmaResponse* dest);
+
+int wh_MessageCrypto_TranslateMlKemEncapsDmaRequest(
+ uint16_t magic, const whMessageCrypto_MlKemEncapsDmaRequest* src,
+ whMessageCrypto_MlKemEncapsDmaRequest* dest);
+
+int wh_MessageCrypto_TranslateMlKemEncapsDmaResponse(
+ uint16_t magic, const whMessageCrypto_MlKemEncapsDmaResponse* src,
+ whMessageCrypto_MlKemEncapsDmaResponse* dest);
+
+int wh_MessageCrypto_TranslateMlKemDecapsDmaRequest(
+ uint16_t magic, const whMessageCrypto_MlKemDecapsDmaRequest* src,
+ whMessageCrypto_MlKemDecapsDmaRequest* dest);
+
+int wh_MessageCrypto_TranslateMlKemDecapsDmaResponse(
+ uint16_t magic, const whMessageCrypto_MlKemDecapsDmaResponse* src,
+ whMessageCrypto_MlKemDecapsDmaResponse* dest);
+
/* Ed25519 DMA Sign Request */
typedef struct {
whMessageCrypto_DmaBuffer msg; /* Message buffer */
diff --git a/wolfhsm/wh_server_crypto.h b/wolfhsm/wh_server_crypto.h
index 655f67690..af945ce92 100644
--- a/wolfhsm/wh_server_crypto.h
+++ b/wolfhsm/wh_server_crypto.h
@@ -37,6 +37,7 @@
#include "wolfssl/wolfcrypt/curve25519.h"
#include "wolfssl/wolfcrypt/ecc.h"
#include "wolfssl/wolfcrypt/ed25519.h"
+#include "wolfssl/wolfcrypt/wc_mlkem.h"
#include "wolfssl/wolfcrypt/aes.h"
#include "wolfssl/wolfcrypt/sha256.h"
#include "wolfssl/wolfcrypt/cmac.h"
@@ -103,6 +104,16 @@ int wh_Server_MlDsaKeyCacheExport(whServerContext* ctx, whKeyId keyId,
MlDsaKey* key);
#endif /* HAVE_DILITHIUM */
+#ifdef WOLFSSL_HAVE_MLKEM
+/* Store a MlKemKey into a server key cache with optional metadata */
+int wh_Server_MlKemKeyCacheImport(whServerContext* ctx, MlKemKey* key,
+ whKeyId keyId, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label);
+/* Restore a MlKemKey from a server key cache */
+int wh_Server_MlKemKeyCacheExport(whServerContext* ctx, whKeyId keyId,
+ MlKemKey* key);
+#endif /* WOLFSSL_HAVE_MLKEM */
+
#ifdef HAVE_HKDF
/* Store HKDF output into a server key cache with optional metadata */
int wh_Server_HkdfKeyCacheImport(whServerContext* ctx, const uint8_t* keyData,