diff --git a/.github/workflows/build-and-run-examples.yml b/.github/workflows/build-and-run-examples.yml
index 0ba90e663..d047ea8bc 100644
--- a/.github/workflows/build-and-run-examples.yml
+++ b/.github/workflows/build-and-run-examples.yml
@@ -91,3 +91,30 @@ jobs:
done
done
done
+
+ # The client and the server are separate binaries with separate
+ # user_settings.h, so the client can be built with no software SLH-DSA at
+ # all while the server keeps its own. That is the only way to prove the
+ # callback-only path really reaches the server: with the software stripped
+ # the client has nothing to fall back to, so a passing demo means every
+ # SLH-DSA operation was served over the wire. Only one transport needs to
+ # run it; the ONLY macro is orthogonal to the transport.
+ - name: Build and run the callback-only SLH-DSA client
+ if: ${{ matrix.transport == 'tcp' }}
+ run: |
+ WS="$(pwd)"
+ SERVER_DIR="$WS/examples/posix/wh_posix_server"
+ CLIENT_DIR="$WS/examples/posix/wh_posix_client"
+ make -C "$SERVER_DIR" clean
+ make -C "$CLIENT_DIR" clean
+ DEMO_KEK=1 make -C "$SERVER_DIR" -j WOLFSSL_DIR=../../../wolfssl
+ SLHDSA_CB_ONLY=1 make -C "$CLIENT_DIR" -j WOLFSSL_DIR=../../../wolfssl
+ rm -f "$SERVER_DIR"/*.bin
+ cd "$SERVER_DIR"
+ ./Build/wh_posix_server.elf --type tcp &
+ SERVER_PID=$!
+ sleep 1
+ cd "$CLIENT_DIR"
+ ./Build/wh_posix_client.elf --type tcp --test
+ kill $SERVER_PID 2>/dev/null || true
+ wait $SERVER_PID 2>/dev/null || true
diff --git a/examples/demo/client/wh_demo_client_all.c b/examples/demo/client/wh_demo_client_all.c
index 2b1169dde..225e203c9 100644
--- a/examples/demo/client/wh_demo_client_all.c
+++ b/examples/demo/client/wh_demo_client_all.c
@@ -201,5 +201,12 @@ int wh_DemoClient_All(whClientContext* clientContext)
#endif /* WOLFSSL_CMAC */
+#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
+ rc = wh_DemoClient_CryptoSlhDsa(clientContext);
+ if (rc != 0) {
+ return rc;
+ }
+#endif /* WOLFSSL_HAVE_SLHDSA && !WOLFSSL_SLHDSA_VERIFY_ONLY */
+
return rc;
}
diff --git a/examples/demo/client/wh_demo_client_crypto.c b/examples/demo/client/wh_demo_client_crypto.c
index a7c3dc91d..3cc0c6b25 100644
--- a/examples/demo/client/wh_demo_client_crypto.c
+++ b/examples/demo/client/wh_demo_client_crypto.c
@@ -44,6 +44,10 @@
#include "wolfssl/wolfcrypt/kdf.h"
#endif
+#ifdef WOLFSSL_HAVE_SLHDSA
+#include "wolfssl/wolfcrypt/wc_slhdsa.h"
+#endif
+
#include "wh_demo_client_crypto.h"
#if !defined(NO_RSA)
@@ -1692,4 +1696,91 @@ int wh_DemoClient_CryptoCmacKdfCacheInputs(whClientContext* clientContext)
#endif /* HAVE_CMAC_KDF && WOLFSSL_CMAC */
+#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
+
+/* Generate an SLH-DSA key that stays on the server and use it purely by key
+ * id. Only the smallest parameter set produces a signature that fits the comm
+ * buffer, so that is what this demo asks for. */
+int wh_DemoClient_CryptoSlhDsa(whClientContext* clientContext)
+{
+ int ret;
+ int devId = WH_CLIENT_DEVID(clientContext);
+ whKeyId keyId = WH_KEYID_ERASED;
+ SlhDsaKey pub[1];
+ SlhDsaKey handle[1];
+ uint8_t label[] = "slhdsa-demo";
+ byte message[] = "wolfHSM SLH-DSA demo message";
+ byte signature[WC_SLHDSA_SHAKE128S_SIG_LEN];
+ word32 sigLen = sizeof(signature);
+
+ ret = wc_SlhDsaKey_Init(pub, SLHDSA_SHAKE128S, NULL, devId);
+ if (ret != 0) {
+ WOLFHSM_CFG_PRINTF("Failed to wc_SlhDsaKey_Init %d\n", ret);
+ return ret;
+ }
+
+ ret = wc_SlhDsaKey_Init(handle, SLHDSA_SHAKE128S, NULL, devId);
+ if (ret != 0) {
+ WOLFHSM_CFG_PRINTF("Failed to wc_SlhDsaKey_Init %d\n", ret);
+ wc_SlhDsaKey_Free(pub);
+ return ret;
+ }
+
+ /* The private key is generated on and never leaves the HSM; only the
+ * public key comes back. */
+ ret = wh_Client_SlhDsaMakeCacheKeyAndExportPublic(
+ clientContext, SLHDSA_SHAKE128S, &keyId,
+ WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY, sizeof(label),
+ label, pub);
+ if (ret != 0) {
+ WOLFHSM_CFG_PRINTF("Failed to generate SLH-DSA key %d\n", ret);
+ goto exit;
+ }
+
+ /* handle holds no key material at all, just the server key id */
+ ret = wh_Client_SlhDsaSetKeyId(handle, keyId);
+ if (ret != 0) {
+ WOLFHSM_CFG_PRINTF("Failed to wh_Client_SlhDsaSetKeyId %d\n", ret);
+ goto exit;
+ }
+
+ ret = wc_SlhDsaKey_SignDeterministic(handle, NULL, 0, message,
+ sizeof(message), signature, &sigLen);
+ if (ret != 0) {
+ WOLFHSM_CFG_PRINTF("Failed to wc_SlhDsaKey_SignDeterministic %d\n",
+ ret);
+ goto exit;
+ }
+
+ ret = wc_SlhDsaKey_Verify(pub, NULL, 0, message, sizeof(message),
+ signature, sigLen);
+ if (ret != 0) {
+ WOLFHSM_CFG_PRINTF("Failed to wc_SlhDsaKey_Verify %d\n", ret);
+ goto exit;
+ }
+
+ /* A tampered signature must not verify */
+ signature[0] ^= 0xFF;
+ if (wc_SlhDsaKey_Verify(pub, NULL, 0, message, sizeof(message), signature,
+ sigLen) == 0) {
+ WOLFHSM_CFG_PRINTF("SLH-DSA verified a tampered signature\n");
+ ret = -1;
+ goto exit;
+ }
+
+ WOLFHSM_CFG_PRINTF("SLH-DSA sign/verify with a server-resident key: "
+ "SUCCESS\n");
+ ret = 0;
+
+exit:
+ if (!WH_KEYID_ISERASED(keyId)) {
+ (void)wh_Client_KeyEvict(clientContext, keyId);
+ }
+ wc_SlhDsaKey_Free(handle);
+ wc_SlhDsaKey_Free(pub);
+ return ret;
+}
+
+#endif /* WOLFSSL_HAVE_SLHDSA && !WOLFSSL_SLHDSA_VERIFY_ONLY */
+
#endif /* WOLFHSM_CFG_NO_CRYPTO */
diff --git a/examples/demo/client/wh_demo_client_crypto.h b/examples/demo/client/wh_demo_client_crypto.h
index 72fbb187d..f7c14417c 100644
--- a/examples/demo/client/wh_demo_client_crypto.h
+++ b/examples/demo/client/wh_demo_client_crypto.h
@@ -30,4 +30,6 @@ int wh_DemoClient_CryptoCmacKdfExport(whClientContext* clientContext);
int wh_DemoClient_CryptoCmacKdfCache(whClientContext* clientContext);
int wh_DemoClient_CryptoCmacKdfCacheInputs(whClientContext* clientContext);
+int wh_DemoClient_CryptoSlhDsa(whClientContext* clientContext);
+
#endif /* !DEMO_CLIENT_CRYPTO_H_ */
diff --git a/examples/posix/wh_posix_client/Makefile b/examples/posix/wh_posix_client/Makefile
index dbd1c39ed..b7d10552f 100644
--- a/examples/posix/wh_posix_client/Makefile
+++ b/examples/posix/wh_posix_client/Makefile
@@ -143,6 +143,13 @@ ifeq ($(AUTH),1)
DEF += -DWOLFHSM_CFG_ENABLE_AUTHENTICATION
endif
+# Strip the software SLH-DSA from the client so every operation has to reach
+# the server. The server is a separate binary with its own settings, so it
+# keeps its software implementation.
+ifeq ($(SLHDSA_CB_ONLY),1)
+DEF += -DWH_CFG_SLHDSA_CB_ONLY
+endif
+
else
DEF += -DWOLFHSM_CFG_NO_CRYPTO
endif
diff --git a/examples/posix/wh_posix_client/user_settings.h b/examples/posix/wh_posix_client/user_settings.h
index d25542bc9..ce8b2baf4 100644
--- a/examples/posix/wh_posix_client/user_settings.h
+++ b/examples/posix/wh_posix_client/user_settings.h
@@ -44,6 +44,23 @@
#define WOLFSSL_CMAC
#define HAVE_HKDF
+/* SLH-DSA. Only the smallest parameter set is built: its 7856-byte signature
+ * is the only one that fits WOLFHSM_CFG_COMM_DATA_LEN. */
+#define WOLFSSL_HAVE_SLHDSA
+#define WOLFSSL_SHA3
+#define WOLFSSL_SHAKE128
+#define WOLFSSL_SHAKE256
+#define WOLFSSL_SLHDSA_PARAM_NO_128F
+#define WOLFSSL_SLHDSA_PARAM_NO_192
+#define WOLFSSL_SLHDSA_PARAM_NO_256
+
+/* Build the client with no software SLH-DSA at all, so every operation must
+ * reach the server or fail closed. Set -DWH_CFG_SLHDSA_CB_ONLY to select it;
+ * the server keeps its software implementation either way. */
+#ifdef WH_CFG_SLHDSA_CB_ONLY
+#define WOLF_CRYPTO_CB_ONLY_SLHDSA
+#endif
+
/* wolfCrypt benchmark settings */
#define NO_MAIN_DRIVER
#define BENCH_EMBEDDED
diff --git a/examples/posix/wh_posix_server/user_settings.h b/examples/posix/wh_posix_server/user_settings.h
index efb06ce4b..4c4708075 100644
--- a/examples/posix/wh_posix_server/user_settings.h
+++ b/examples/posix/wh_posix_server/user_settings.h
@@ -150,6 +150,13 @@ extern "C" {
#define WOLFSSL_MLDSA_NO_MAKE_KEY
#endif
+/* SLH-DSA Options. Only the smallest parameter set is built: its 7856-byte
+ * signature is the only one that fits WOLFHSM_CFG_COMM_DATA_LEN. */
+#define WOLFSSL_HAVE_SLHDSA
+#define WOLFSSL_SLHDSA_PARAM_NO_128F
+#define WOLFSSL_SLHDSA_PARAM_NO_192
+#define WOLFSSL_SLHDSA_PARAM_NO_256
+
/* ML-KEM Options */
#define WOLFSSL_HAVE_MLKEM
diff --git a/src/wh_client_crypto.c b/src/wh_client_crypto.c
index f7ecc05da..b14c2958a 100644
--- a/src/wh_client_crypto.c
+++ b/src/wh_client_crypto.c
@@ -11152,6 +11152,1361 @@ int wh_Client_MlDsaCheckPrivKeyDma(whClientContext* ctx, wc_MlDsaKey* key,
#endif /* WOLFHSM_CFG_DMA */
#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+
+/* Parameter set the caller's key was initialized with. The server needs it to
+ * rebuild the key, and it is not recoverable from the key id alone. */
+static int _SlhDsaKeyParam(const SlhDsaKey* key)
+{
+ if ((key == NULL) || (key->params == NULL)) {
+ return WC_SLHDSA_DEFAULT_PARAM;
+ }
+ return (int)key->params->param;
+}
+
+int wh_Client_SlhDsaSetKeyId(SlhDsaKey* key, whKeyId keyId)
+{
+ if (key == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ key->devCtx = WH_KEYID_TO_DEVCTX(keyId);
+
+ return WH_ERROR_OK;
+}
+
+int wh_Client_SlhDsaGetKeyId(SlhDsaKey* 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_SlhDsaImportKey(whClientContext* ctx, SlhDsaKey* 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[WH_CRYPTO_SLHDSA_MAX_KEY_DER_SIZE];
+ uint16_t buffer_len = 0;
+
+ if ((ctx == NULL) || (key == NULL) ||
+ ((label_len != 0) && (label == NULL))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (inout_keyId != NULL) {
+ key_id = *inout_keyId;
+ }
+
+ ret = wh_Crypto_SlhDsaSerializeKeyDer(key, sizeof(buffer), buffer,
+ &buffer_len);
+ if (ret == WH_ERROR_OK) {
+ /* Cache the key and get the keyID */
+ 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("label:%.*s ret:%d keyid:%u\n", label_len, label,
+ ret, key_id);
+ return ret;
+}
+
+int wh_Client_SlhDsaExportKey(whClientContext* ctx, whKeyId keyId,
+ SlhDsaKey* key, uint16_t label_len,
+ uint8_t* label)
+{
+ int ret = WH_ERROR_OK;
+ byte buffer[WH_CRYPTO_SLHDSA_MAX_KEY_DER_SIZE];
+ uint16_t buffer_len = sizeof(buffer);
+
+ if ((ctx == NULL) || WH_KEYID_ISERASED(keyId) || (key == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret =
+ wh_Client_KeyExport(ctx, keyId, label, label_len, buffer, &buffer_len);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Crypto_SlhDsaDeserializeKeyDer(buffer, buffer_len, key);
+ }
+
+ WH_DEBUG_CLIENT_VERBOSE("keyid:%x key:%p ret:%d label:%.*s\n", keyId, key,
+ ret, (int)label_len, label);
+ return ret;
+}
+
+int wh_Client_SlhDsaExportPublicKey(whClientContext* ctx, whKeyId keyId,
+ SlhDsaKey* key, uint16_t label_len,
+ uint8_t* label)
+{
+ int ret;
+ byte buffer[WH_CRYPTO_SLHDSA_MAX_KEY_DER_SIZE] = {0};
+ uint16_t buffer_len = sizeof(buffer);
+
+ if ((ctx == NULL) || WH_KEYID_ISERASED(keyId) || (key == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_Client_KeyExportPublic(ctx, keyId, WH_KEY_ALGO_SLHDSA, label,
+ label_len, buffer, &buffer_len);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Crypto_SlhDsaDeserializeKeyDer(buffer, buffer_len, key);
+ }
+ return ret;
+}
+
+static int _SlhDsaMakeKey(whClientContext* ctx, int param, const byte* seed,
+ word32 seedSz, whKeyId* inout_key_id,
+ whNvmFlags flags, uint16_t label_len,
+ const uint8_t* label, SlhDsaKey* key)
+{
+ int ret = WH_ERROR_OK;
+ whKeyId key_id = WH_KEYID_ERASED;
+ uint8_t* dataPtr = NULL;
+ whMessageCrypto_SlhDsaKeyGenRequest* req = NULL;
+ whMessageCrypto_SlhDsaKeyGenResponse* res = NULL;
+ uint16_t pkType;
+
+ if ((ctx == NULL) || ((seed == NULL) && (seedSz > 0))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Get data pointer from the context to use as request/response storage */
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* A seeded generation is a distinct operation from a random one */
+ pkType = (seed != NULL) ? WC_PK_TYPE_PQC_SIG_KEYGEN_SEEDED
+ : WC_PK_TYPE_PQC_SIG_KEYGEN;
+
+ /* Setup generic header and get pointer to request data */
+ req = (whMessageCrypto_SlhDsaKeyGenRequest*)_createCryptoRequestWithSubtype(
+ dataPtr, pkType, WC_PQC_SIG_TYPE_SLHDSA, ctx->cryptoAffinity);
+
+ /* Use the supplied key id if provided */
+ if (inout_key_id != NULL) {
+ key_id = *inout_key_id;
+ }
+
+ {
+ /* Request Message */
+ uint16_t group = WH_MESSAGE_GROUP_CRYPTO;
+ uint16_t action = WC_ALGO_TYPE_PK;
+
+ uint32_t total_len = sizeof(whMessageCrypto_GenericRequestHeader) +
+ sizeof(*req) + seedSz;
+
+ if (total_len <= WOLFHSM_CFG_COMM_DATA_LEN) {
+ uint16_t req_len = (uint16_t)total_len;
+
+ memset(req, 0, sizeof(*req));
+ req->param = param;
+ req->sz = 0;
+ req->flags = flags;
+ req->keyId = key_id;
+ req->seedSz = seedSz;
+ if (seedSz > 0) {
+ memcpy((uint8_t*)(req + 1), seed, seedSz);
+ }
+ 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);
+ if (ret == WH_ERROR_OK) {
+ uint16_t res_len = 0;
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &res_len,
+ WOLFHSM_CFG_COMM_DATA_LEN,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+
+ if (ret == WH_ERROR_OK) {
+ /* Get response structure pointer, validates generic header
+ * rc */
+ ret = _getCryptoResponse(dataPtr, pkType, (uint8_t**)&res);
+ /* wolfCrypt allows positive error codes on success in some
+ * scenarios */
+ if (ret >= 0) {
+ /* Key is cached on server or is ephemeral */
+ key_id = (whKeyId)(res->keyId);
+
+ /* Update output variable if requested */
+ if (inout_key_id != NULL) {
+ *inout_key_id = key_id;
+ }
+
+ /* Update the context if provided */
+ if (key != NULL) {
+ uint16_t der_size = (uint16_t)(res->len);
+ const size_t hdr_sz =
+ sizeof(whMessageCrypto_GenericResponseHeader) +
+ sizeof(*res);
+ /* Set the key_id. ERASED for EPHEMERAL, cached id
+ * otherwise. */
+ wh_Client_SlhDsaSetKeyId(key, key_id);
+
+ /* Response carries the exported key (EPHEMERAL) or
+ * the public key (cached keygen). An empty body
+ * means the caller requested key material the
+ * server did not return; also reject a length that
+ * does not fit the received frame before
+ * deserializing. */
+ if (der_size == 0) {
+ ret = WH_ERROR_ABORTED;
+ }
+ else if ((res_len < hdr_sz) ||
+ (res->len > (res_len - hdr_sz))) {
+ ret = WH_ERROR_ABORTED;
+ }
+ else {
+ uint8_t* key_der = (uint8_t*)(res + 1);
+ ret = wh_Crypto_SlhDsaDeserializeKeyDer(
+ key_der, der_size, key);
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+ return ret;
+}
+
+int wh_Client_SlhDsaMakeCacheKey(whClientContext* ctx, int param,
+ whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label)
+{
+ if (inout_key_id == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return _SlhDsaMakeKey(ctx, param, NULL, 0, inout_key_id, flags, label_len,
+ label, NULL);
+}
+
+int wh_Client_SlhDsaMakeCacheKeyAndExportPublic(
+ whClientContext* ctx, int param, whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, const uint8_t* label, SlhDsaKey* pub)
+{
+ int ret;
+ whKeyId in_keyId;
+
+ if ((ctx == NULL) || (inout_key_id == NULL) || (pub == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Ephemeral keygen belongs to the export path, not the cache path. */
+ if (flags & WH_NVM_FLAGS_EPHEMERAL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ in_keyId = *inout_key_id;
+ ret = _SlhDsaMakeKey(ctx, param, NULL, 0, inout_key_id, flags, label_len,
+ label, pub);
+ if (ret >= 0) {
+ /* Stamp the cached keyId and the client's HSM devId so pub is
+ * immediately usable as a handle to the cached private key. The keyId
+ * is set here as well because the public-key deserialize re-inits
+ * pub and clears it. */
+ wh_Client_SlhDsaSetKeyId(pub, *inout_key_id);
+ pub->devId = WH_CLIENT_DEVID(ctx);
+ }
+ else if (!WH_KEYID_ISERASED(*inout_key_id) &&
+ (WH_KEYID_ISERASED(in_keyId) || (ret == WH_ERROR_ABORTED))) {
+ /* The server committed a key but the best-effort export returned no
+ * public key. Roll back so the operation is atomic and no cache slot
+ * is orphaned. */
+ (void)wh_Client_KeyEvict(ctx, *inout_key_id);
+ *inout_key_id = WH_KEYID_ERASED;
+ }
+ return ret;
+}
+
+int wh_Client_SlhDsaMakeExportKey(whClientContext* ctx, int param,
+ SlhDsaKey* key)
+{
+ if (key == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return _SlhDsaMakeKey(ctx, param, NULL, 0, NULL, WH_NVM_FLAGS_EPHEMERAL, 0,
+ NULL, key);
+}
+
+int wh_Client_SlhDsaMakeExportKeyFromSeed(whClientContext* ctx, int param,
+ const byte* seed, word32 seedSz,
+ SlhDsaKey* key)
+{
+ if ((key == NULL) || (seed == NULL) || (seedSz == 0)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return _SlhDsaMakeKey(ctx, param, seed, seedSz, NULL,
+ WH_NVM_FLAGS_EPHEMERAL, 0, NULL, key);
+}
+
+int wh_Client_SlhDsaMakeCacheKeyFromSeed(whClientContext* ctx, int param,
+ const byte* seed, word32 seedSz,
+ whKeyId* inout_key_id,
+ whNvmFlags flags, uint16_t label_len,
+ uint8_t* label)
+{
+ if ((inout_key_id == NULL) || (seed == NULL) || (seedSz == 0)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return _SlhDsaMakeKey(ctx, param, seed, seedSz, inout_key_id, flags,
+ label_len, label, NULL);
+}
+
+int wh_Client_SlhDsaSign(whClientContext* ctx, const byte* in, word32 in_len,
+ byte* out, word32* inout_len, SlhDsaKey* key,
+ const byte* context, byte contextLen,
+ word32 preHashType, const byte* addRnd, byte addRndSz,
+ int randomized, int isMPrime)
+{
+ int ret = WH_ERROR_OK;
+ whMessageCrypto_SlhDsaSignRequest* req = NULL;
+ whMessageCrypto_SlhDsaSignResponse* res = NULL;
+ uint8_t* dataPtr = NULL;
+ uint16_t pkType;
+
+ /* Transaction state */
+ whKeyId key_id;
+ int evict = 0;
+
+ if ((ctx == NULL) || (key == NULL) || ((in == NULL) && (in_len > 0)) ||
+ (out == NULL) || (inout_len == NULL) ||
+ ((addRnd == NULL) && (addRndSz > 0))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ pkType = (isMPrime != 0) ? WC_PK_TYPE_PQC_SIG_SIGN_MSG
+ : WC_PK_TYPE_PQC_SIG_SIGN;
+
+ key_id = WH_DEVCTX_TO_KEYID(key->devCtx);
+
+ /* Import key if necessary */
+ if (WH_KEYID_ISERASED(key_id)) {
+ /* Must import the key to the server and evict it afterwards */
+ uint8_t keyLabel[] = "TempSlhDsaSign";
+ whNvmFlags flags = WH_NVM_FLAGS_USAGE_SIGN;
+
+ ret = wh_Client_SlhDsaImportKey(ctx, key, &key_id, flags,
+ sizeof(keyLabel), keyLabel);
+ if (ret == WH_ERROR_OK) {
+ evict = 1;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ /* Request Message */
+ uint16_t group = WH_MESSAGE_GROUP_CRYPTO;
+ uint16_t action = WC_ALGO_TYPE_PK;
+
+ uint32_t total_len = sizeof(whMessageCrypto_GenericRequestHeader) +
+ sizeof(*req) + in_len + contextLen + addRndSz;
+ uint32_t options = 0;
+
+ /* Get data pointer from the context to use as request/response storage
+ */
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Setup generic header and get pointer to request data */
+ req =
+ (whMessageCrypto_SlhDsaSignRequest*)_createCryptoRequestWithSubtype(
+ dataPtr, pkType, WC_PQC_SIG_TYPE_SLHDSA, ctx->cryptoAffinity);
+
+ if (total_len <= WOLFHSM_CFG_COMM_DATA_LEN) {
+ uint16_t req_len = (uint16_t)total_len;
+ uint8_t* req_data = (uint8_t*)(req + 1);
+ if (evict != 0) {
+ options |= WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_EVICT;
+ }
+ if (isMPrime != 0) {
+ options |= WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_MPRIME;
+ }
+ if (randomized != 0) {
+ options |= WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_RANDOMIZED;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->options = options;
+ req->param = _SlhDsaKeyParam(key);
+ req->keyId = key_id;
+ req->sz = in_len;
+ req->contextSz = contextLen;
+ req->preHashType = preHashType;
+ req->addRndSz = addRndSz;
+ if ((in != NULL) && (in_len > 0)) {
+ memcpy(req_data, in, in_len);
+ }
+ if ((context != NULL) && (contextLen > 0)) {
+ memcpy(req_data + in_len, context, contextLen);
+ }
+ if (addRndSz > 0) {
+ memcpy(req_data + in_len + contextLen, addRnd, addRndSz);
+ }
+
+ /* Send Request */
+ ret = wh_Client_SendRequest(ctx, group, action, req_len,
+ (uint8_t*)dataPtr);
+ if (ret == WH_ERROR_OK) {
+ /* Server will evict at this point. Reset evict */
+ uint16_t res_len = 0;
+ evict = 0;
+
+ /* Recv Response */
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &res_len,
+ WOLFHSM_CFG_COMM_DATA_LEN,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+
+ if (ret == WH_ERROR_OK) {
+ /* Get response structure pointer, validates generic header
+ * rc */
+ ret = _getCryptoResponse(dataPtr, pkType, (uint8_t**)&res);
+ /* wolfCrypt allows positive error codes on success in some
+ * scenarios */
+ if (ret >= 0) {
+ const uint32_t hdr_sz =
+ sizeof(whMessageCrypto_GenericResponseHeader) +
+ sizeof(*res);
+ if ((res_len < hdr_sz) ||
+ (res->sz > (res_len - hdr_sz))) {
+ ret = WH_ERROR_ABORTED;
+ }
+ else {
+ uint8_t* res_sig = (uint8_t*)(res + 1);
+ if (res->sz > *inout_len) {
+ ret = WH_ERROR_BUFFER_SIZE;
+ }
+ else {
+ memcpy(out, res_sig, res->sz);
+ }
+ *inout_len = res->sz;
+ }
+ }
+ }
+ }
+ }
+ else {
+ /* Request length is too long */
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+ /* Evict the key manually on error */
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+ WH_DEBUG_CLIENT_VERBOSE("ret:%d\n", ret);
+ return ret;
+}
+
+int wh_Client_SlhDsaVerify(whClientContext* ctx, const byte* sig,
+ word32 sig_len, const byte* msg, word32 msg_len,
+ int* out_res, SlhDsaKey* key, const byte* context,
+ byte contextLen, word32 preHashType, int isMPrime)
+{
+ int ret = WH_ERROR_OK;
+ uint8_t* dataPtr = NULL;
+ whMessageCrypto_SlhDsaVerifyRequest* req = NULL;
+ whMessageCrypto_SlhDsaVerifyResponse* res = NULL;
+ uint16_t pkType;
+
+ /* Transaction state */
+ whKeyId key_id;
+ int evict = 0;
+
+ if ((ctx == NULL) || (key == NULL) || ((sig == NULL) && (sig_len > 0)) ||
+ (out_res == NULL) || ((msg == NULL) && (msg_len > 0))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ pkType = (isMPrime != 0) ? WC_PK_TYPE_PQC_SIG_VERIFY_MSG
+ : WC_PK_TYPE_PQC_SIG_VERIFY;
+
+ key_id = WH_DEVCTX_TO_KEYID(key->devCtx);
+
+ /* Import key if necessary */
+ if (WH_KEYID_ISERASED(key_id)) {
+ /* Must import the key to the server and evict it afterwards */
+ uint8_t keyLabel[] = "TempSlhDsaVerify";
+ whNvmFlags flags = WH_NVM_FLAGS_USAGE_VERIFY;
+
+ ret = wh_Client_SlhDsaImportKey(ctx, key, &key_id, flags,
+ sizeof(keyLabel), keyLabel);
+ if (ret == WH_ERROR_OK) {
+ evict = 1;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ /* Request Message */
+ uint16_t group = WH_MESSAGE_GROUP_CRYPTO;
+ uint16_t action = WC_ALGO_TYPE_PK;
+ uint32_t options = 0;
+
+ uint32_t total_len = sizeof(whMessageCrypto_GenericRequestHeader) +
+ sizeof(*req) + sig_len + msg_len + contextLen;
+
+ /* Get data pointer from the context to use as request/response storage
+ */
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Setup generic header and get pointer to request data */
+ req = (whMessageCrypto_SlhDsaVerifyRequest*)
+ _createCryptoRequestWithSubtype(dataPtr, pkType,
+ WC_PQC_SIG_TYPE_SLHDSA,
+ ctx->cryptoAffinity);
+
+ if (total_len <= WOLFHSM_CFG_COMM_DATA_LEN) {
+ uint16_t req_len = (uint16_t)total_len;
+ uint8_t* req_sig = (uint8_t*)(req + 1);
+ uint8_t* req_hash = req_sig + sig_len;
+
+ /* Set request packet members */
+ if (evict != 0) {
+ options |= WH_MESSAGE_CRYPTO_SLHDSA_VERIFY_OPTIONS_EVICT;
+ }
+ if (isMPrime != 0) {
+ options |= WH_MESSAGE_CRYPTO_SLHDSA_VERIFY_OPTIONS_MPRIME;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->options = options;
+ req->param = _SlhDsaKeyParam(key);
+ req->keyId = key_id;
+ req->sigSz = sig_len;
+ if ((sig != NULL) && (sig_len > 0)) {
+ memcpy(req_sig, sig, sig_len);
+ }
+ req->hashSz = msg_len;
+ if ((msg != NULL) && (msg_len > 0)) {
+ memcpy(req_hash, msg, msg_len);
+ }
+ req->contextSz = contextLen;
+ req->preHashType = preHashType;
+ if ((context != NULL) && (contextLen > 0)) {
+ memcpy(req_hash + msg_len, context, contextLen);
+ }
+
+ /* write request */
+ ret = wh_Client_SendRequest(ctx, group, action, req_len,
+ (uint8_t*)dataPtr);
+
+ if (ret == WH_ERROR_OK) {
+ /* Server will evict at this point. Reset evict */
+ uint16_t res_len = 0;
+ evict = 0;
+
+ /* Recv Response */
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &res_len,
+ WOLFHSM_CFG_COMM_DATA_LEN,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+ if (ret == WH_ERROR_OK) {
+ /* Get response structure pointer, validates generic header
+ * rc */
+ ret = _getCryptoResponse(dataPtr, pkType, (uint8_t**)&res);
+ /* wolfCrypt allows positive error codes on success in some
+ * scenarios */
+ if (ret >= 0) {
+ const uint32_t hdr_sz =
+ sizeof(whMessageCrypto_GenericResponseHeader) +
+ sizeof(*res);
+ /* Note whMessageCrypto_SlhDsaVerifyResponse has no
+ * size field */
+ if (res_len < hdr_sz) {
+ ret = WH_ERROR_ABORTED;
+ }
+ else {
+ *out_res = res->res;
+ }
+ }
+ }
+ }
+ }
+ else {
+ /* Request length is too long */
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+ /* Evict the key manually on error */
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+ WH_DEBUG_CLIENT_VERBOSE("ret:%d\n", ret);
+ return ret;
+}
+
+int wh_Client_SlhDsaCheckPrivKey(whClientContext* ctx, SlhDsaKey* key,
+ const byte* pubKey, word32 pubKeySz)
+{
+ int ret = WH_ERROR_OK;
+ uint8_t* dataPtr = NULL;
+ whMessageCrypto_SlhDsaCheckPrivKeyRequest* req = NULL;
+ whMessageCrypto_SlhDsaCheckPrivKeyResponse* res = NULL;
+
+ /* Transaction state */
+ whKeyId key_id;
+ int evict = 0;
+
+ /* A NULL public key asks the server to check its own copy of the private
+ * key for consistency, with nothing to compare it against. */
+ if ((ctx == NULL) || (key == NULL) ||
+ ((pubKey == NULL) != (pubKeySz == 0))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ key_id = WH_DEVCTX_TO_KEYID(key->devCtx);
+
+ /* Import key if necessary */
+ if (WH_KEYID_ISERASED(key_id)) {
+ uint8_t keyLabel[] = "TempSlhDsaCheck";
+ whNvmFlags flags = WH_NVM_FLAGS_USAGE_SIGN;
+
+ ret = wh_Client_SlhDsaImportKey(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;
+
+ uint32_t total_len = sizeof(whMessageCrypto_GenericRequestHeader) +
+ sizeof(*req) + pubKeySz;
+
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ req = (whMessageCrypto_SlhDsaCheckPrivKeyRequest*)
+ _createCryptoRequestWithSubtype(
+ dataPtr, WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY,
+ WC_PQC_SIG_TYPE_SLHDSA, ctx->cryptoAffinity);
+
+ if (total_len <= WOLFHSM_CFG_COMM_DATA_LEN) {
+ uint16_t req_len = (uint16_t)total_len;
+
+ if (evict != 0) {
+ options |=
+ WH_MESSAGE_CRYPTO_SLHDSA_CHECKPRIVKEY_OPTIONS_EVICT;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->options = options;
+ req->param = _SlhDsaKeyParam(key);
+ req->keyId = key_id;
+ req->pubSz = pubKeySz;
+ if (pubKeySz > 0) {
+ memcpy((uint8_t*)(req + 1), pubKey, pubKeySz);
+ }
+
+ ret = wh_Client_SendRequest(ctx, group, action, req_len,
+ (uint8_t*)dataPtr);
+ if (ret == WH_ERROR_OK) {
+ uint16_t res_len = 0;
+ evict = 0;
+
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &res_len,
+ WOLFHSM_CFG_COMM_DATA_LEN,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+
+ if (ret == WH_ERROR_OK) {
+ ret = _getCryptoResponse(
+ dataPtr, WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY,
+ (uint8_t**)&res);
+ if (ret >= 0) {
+ const uint32_t hdr_sz =
+ sizeof(whMessageCrypto_GenericResponseHeader) +
+ sizeof(*res);
+ if (res_len < hdr_sz) {
+ ret = WH_ERROR_ABORTED;
+ }
+ else {
+ ret = (int)res->res;
+ }
+ }
+ }
+ }
+ }
+ else {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+ return ret;
+}
+
+#ifdef WOLFHSM_CFG_DMA
+
+int wh_Client_SlhDsaImportKeyDma(whClientContext* ctx, SlhDsaKey* 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[WH_CRYPTO_SLHDSA_MAX_KEY_DER_SIZE];
+ uint16_t buffer_len = 0;
+
+ if ((ctx == NULL) || (key == NULL) ||
+ ((label_len != 0) && (label == NULL))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ if (inout_keyId != NULL) {
+ key_id = *inout_keyId;
+ }
+
+ /* Serialize the key to a temporary buffer first */
+ ret = wh_Crypto_SlhDsaSerializeKeyDer(key, sizeof(buffer), buffer,
+ &buffer_len);
+ if (ret == WH_ERROR_OK) {
+ /* Cache the key using DMA and get the keyID */
+ 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;
+ }
+ }
+
+ return ret;
+}
+
+int wh_Client_SlhDsaExportKeyDma(whClientContext* ctx, whKeyId keyId,
+ SlhDsaKey* key, uint16_t label_len,
+ uint8_t* label)
+{
+ int ret = WH_ERROR_OK;
+ byte buffer[WH_CRYPTO_SLHDSA_MAX_KEY_DER_SIZE] = {0};
+ uint16_t buffer_len = sizeof(buffer);
+
+ if ((ctx == NULL) || WH_KEYID_ISERASED(keyId) || (key == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Export the key from server using DMA */
+ ret = wh_Client_KeyExportDma(ctx, keyId, buffer, buffer_len, label,
+ label_len, &buffer_len);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Crypto_SlhDsaDeserializeKeyDer(buffer, buffer_len, key);
+ }
+
+ return ret;
+}
+
+int wh_Client_SlhDsaExportPublicKeyDma(whClientContext* ctx, whKeyId keyId,
+ SlhDsaKey* key, uint16_t label_len,
+ uint8_t* label)
+{
+ int ret;
+ byte buffer[WH_CRYPTO_SLHDSA_MAX_KEY_DER_SIZE] = {0};
+ uint16_t buffer_len = sizeof(buffer);
+
+ if ((ctx == NULL) || WH_KEYID_ISERASED(keyId) || (key == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_Client_KeyExportPublicDma(ctx, keyId, WH_KEY_ALGO_SLHDSA, buffer,
+ buffer_len, label, label_len,
+ &buffer_len);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Crypto_SlhDsaDeserializeKeyDer(buffer, buffer_len, key);
+ }
+ return ret;
+}
+
+static int _SlhDsaMakeKeyDma(whClientContext* ctx, int param, const byte* seed,
+ word32 seedSz, whKeyId* inout_key_id,
+ whNvmFlags flags, uint16_t label_len,
+ const uint8_t* label, SlhDsaKey* key)
+{
+ int ret = WH_ERROR_OK;
+ whKeyId key_id = WH_KEYID_ERASED;
+ byte buffer[WH_CRYPTO_SLHDSA_MAX_KEY_DER_SIZE];
+ uint8_t* dataPtr = NULL;
+ whMessageCrypto_SlhDsaKeyGenDmaRequest* req = NULL;
+ whMessageCrypto_SlhDsaKeyGenDmaResponse* res = NULL;
+ uintptr_t keyAddr = 0;
+ uintptr_t seedAddr = 0;
+ uint64_t keyAddrSz = 0;
+ uint16_t pkType;
+ uint16_t req_len;
+ uint16_t res_len = 0;
+ uint16_t group;
+ uint16_t action;
+
+ if ((ctx == NULL) || ((seed == NULL) && (seedSz > 0))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Get data pointer from the context to use as request/response storage */
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ pkType = (seed != NULL) ? WC_PK_TYPE_PQC_SIG_KEYGEN_SEEDED
+ : WC_PK_TYPE_PQC_SIG_KEYGEN;
+
+ /* Setup generic header and get pointer to request data */
+ req =
+ (whMessageCrypto_SlhDsaKeyGenDmaRequest*)
+ _createCryptoRequestWithSubtype(dataPtr, pkType,
+ WC_PQC_SIG_TYPE_SLHDSA,
+ ctx->cryptoAffinity);
+
+ /* Use the supplied key id if provided */
+ if (inout_key_id != NULL) {
+ key_id = *inout_key_id;
+ }
+
+ group = WH_MESSAGE_GROUP_CRYPTO_DMA;
+ action = WC_ALGO_TYPE_PK;
+
+ req_len = sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req);
+
+ if (req_len <= WOLFHSM_CFG_COMM_DATA_LEN) {
+ memset(req, 0, sizeof(*req));
+ req->param = param;
+ req->flags = flags;
+ req->keyId = key_id;
+ req->key.sz = keyAddrSz = sizeof(buffer);
+
+ ret = wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)buffer, (void**)&keyAddr, keyAddrSz,
+ WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ req->key.addr = (uint64_t)(uintptr_t)keyAddr;
+ }
+
+ if ((ret == WH_ERROR_OK) && (seedSz > 0)) {
+ req->seed.sz = seedSz;
+ ret = wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)seed, (void**)&seedAddr, seedSz,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ req->seed.addr = (uint64_t)(uintptr_t)seedAddr;
+ }
+ }
+
+ 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, &res_len,
+ WOLFHSM_CFG_COMM_DATA_LEN,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+ }
+
+ if (seedSz > 0) {
+ (void)wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)seed, (void**)&seedAddr, seedSz,
+ WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0});
+ }
+ (void)wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)buffer, (void**)&keyAddr, keyAddrSz,
+ WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0});
+
+ if (ret == WH_ERROR_OK) {
+ /* Get response structure pointer, validates generic header rc */
+ ret = _getCryptoResponse(dataPtr, pkType, (uint8_t**)&res);
+ /* wolfCrypt allows positive error codes on success in some
+ * scenarios */
+ if (ret >= 0) {
+ const uint32_t hdr_sz =
+ sizeof(whMessageCrypto_GenericResponseHeader) +
+ sizeof(*res);
+ /* The response has no trailing payload; keySize bounds the
+ * DMA buffer write */
+ if (res_len < hdr_sz) {
+ ret = WH_ERROR_ABORTED;
+ }
+ }
+ if (ret >= 0) {
+ /* Key is cached on server or is ephemeral */
+ key_id = (whKeyId)(res->keyId);
+
+ /* Update output variable if requested */
+ if (inout_key_id != NULL) {
+ *inout_key_id = key_id;
+ }
+
+ /* Update the context if provided */
+ if (key != NULL) {
+ /* Set the key_id. ERASED for EPHEMERAL, cached id
+ * otherwise. */
+ wh_Client_SlhDsaSetKeyId(key, key_id);
+
+ if (res->keySize == 0) {
+ ret = WH_ERROR_ABORTED;
+ }
+ /* Bound the server-reported key size to the DMA buffer
+ * capacity before deserializing */
+ else if (res->keySize > sizeof(buffer)) {
+ ret = WH_ERROR_ABORTED;
+ }
+ else {
+ ret = wh_Crypto_SlhDsaDeserializeKeyDer(
+ buffer, (uint16_t)res->keySize, key);
+ }
+ }
+ }
+ }
+ }
+ else {
+ ret = WH_ERROR_BADARGS;
+ }
+ return ret;
+}
+
+int wh_Client_SlhDsaMakeExportKeyDma(whClientContext* ctx, int param,
+ SlhDsaKey* key)
+{
+ if (key == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return _SlhDsaMakeKeyDma(ctx, param, NULL, 0, NULL, WH_NVM_FLAGS_EPHEMERAL,
+ 0, NULL, key);
+}
+
+int wh_Client_SlhDsaMakeExportKeyFromSeedDma(whClientContext* ctx, int param,
+ const byte* seed, word32 seedSz,
+ SlhDsaKey* key)
+{
+ if ((key == NULL) || (seed == NULL) || (seedSz == 0)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ return _SlhDsaMakeKeyDma(ctx, param, seed, seedSz, NULL,
+ WH_NVM_FLAGS_EPHEMERAL, 0, NULL, key);
+}
+
+int wh_Client_SlhDsaMakeCacheKeyDma(whClientContext* ctx, int param,
+ whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, const uint8_t* label,
+ SlhDsaKey* pub)
+{
+ int ret;
+ whKeyId in_keyId;
+
+ if ((ctx == NULL) || (inout_key_id == NULL) || (pub == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Ephemeral keygen belongs to the export path, not the cache path. */
+ if (flags & WH_NVM_FLAGS_EPHEMERAL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ in_keyId = *inout_key_id;
+ ret = _SlhDsaMakeKeyDma(ctx, param, NULL, 0, inout_key_id, flags, label_len,
+ label, pub);
+ if (ret >= 0) {
+ wh_Client_SlhDsaSetKeyId(pub, *inout_key_id);
+ pub->devId = WH_CLIENT_DEVID(ctx);
+ }
+ else if (WH_KEYID_ISERASED(in_keyId) && !WH_KEYID_ISERASED(*inout_key_id)) {
+ /* The server auto-assigned and committed a key but the export failed.
+ * Roll back so the operation is atomic and no cache slot is
+ * orphaned. */
+ (void)wh_Client_KeyEvict(ctx, *inout_key_id);
+ *inout_key_id = WH_KEYID_ERASED;
+ }
+ return ret;
+}
+
+int wh_Client_SlhDsaSignDma(whClientContext* ctx, const byte* in,
+ word32 in_len, byte* out, word32* out_len,
+ SlhDsaKey* key, const byte* context,
+ byte contextLen, word32 preHashType,
+ const byte* addRnd, byte addRndSz, int randomized,
+ int isMPrime)
+{
+ int ret = WH_ERROR_OK;
+ whMessageCrypto_SlhDsaSignDmaRequest* req = NULL;
+ whMessageCrypto_SlhDsaSignDmaResponse* res = NULL;
+ uint8_t* dataPtr = NULL;
+ uintptr_t inAddr = 0;
+ uintptr_t outAddr = 0;
+ word32 sigCap = 0;
+ uint16_t pkType;
+
+ /* Transaction state */
+ whKeyId key_id;
+ int evict = 0;
+
+ if ((ctx == NULL) || (key == NULL) || ((in == NULL) && (in_len > 0)) ||
+ (out == NULL) || (out_len == NULL) ||
+ ((addRnd == NULL) && (addRndSz > 0))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Caller's signature buffer capacity, before the response overwrites it */
+ sigCap = *out_len;
+
+ pkType = (isMPrime != 0) ? WC_PK_TYPE_PQC_SIG_SIGN_MSG
+ : WC_PK_TYPE_PQC_SIG_SIGN;
+
+ key_id = WH_DEVCTX_TO_KEYID(key->devCtx);
+
+ /* Import key if necessary */
+ if (WH_KEYID_ISERASED(key_id)) {
+ uint8_t keyLabel[] = "TempSlhDsaSign";
+ whNvmFlags flags = WH_NVM_FLAGS_USAGE_SIGN;
+
+ ret = wh_Client_SlhDsaImportKeyDma(ctx, key, &key_id, flags,
+ sizeof(keyLabel), keyLabel);
+ if (ret == WH_ERROR_OK) {
+ evict = 1;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ /* Request Message */
+ uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA;
+ uint16_t action = WC_ALGO_TYPE_PK;
+
+ uint16_t req_len = sizeof(whMessageCrypto_GenericRequestHeader) +
+ sizeof(*req) + contextLen + addRndSz;
+ uint32_t options = 0;
+
+ /* Get data pointer from the context to use as request/response storage
+ */
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Setup generic header and get pointer to request data */
+ req = (whMessageCrypto_SlhDsaSignDmaRequest*)
+ _createCryptoRequestWithSubtype(dataPtr, pkType,
+ WC_PQC_SIG_TYPE_SLHDSA,
+ ctx->cryptoAffinity);
+
+ if (req_len <= WOLFHSM_CFG_COMM_DATA_LEN) {
+ uint8_t* req_data = (uint8_t*)(req + 1);
+
+ if (evict != 0) {
+ options |= WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_EVICT;
+ }
+ if (isMPrime != 0) {
+ options |= WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_MPRIME;
+ }
+ if (randomized != 0) {
+ options |= WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_RANDOMIZED;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->options = options;
+ req->param = _SlhDsaKeyParam(key);
+ req->keyId = key_id;
+ req->contextSz = contextLen;
+ req->preHashType = preHashType;
+ req->addRndSz = addRndSz;
+ if ((context != NULL) && (contextLen > 0)) {
+ memcpy(req_data, context, contextLen);
+ }
+ if (addRndSz > 0) {
+ memcpy(req_data + contextLen, addRnd, addRndSz);
+ }
+
+ /* Set up DMA buffers */
+ req->msg.sz = in_len;
+ ret = wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)in, (void**)&inAddr, req->msg.sz,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ req->msg.addr = inAddr;
+ }
+
+ if (ret == WH_ERROR_OK) {
+ req->sig.sz = sigCap;
+ ret = wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)out, (void**)&outAddr, req->sig.sz,
+ WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ req->sig.addr = outAddr;
+ }
+ }
+
+ /* Send Request */
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Client_SendRequest(ctx, group, action, req_len,
+ (uint8_t*)dataPtr);
+ }
+ if (ret == WH_ERROR_OK) {
+ /* Server will evict at this point if requested */
+ uint16_t res_len = 0;
+ evict = 0;
+
+ /* Recv Response */
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &res_len,
+ WOLFHSM_CFG_COMM_DATA_LEN,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+
+ if (ret == WH_ERROR_OK) {
+ /* Get response structure pointer, validates generic header
+ * rc */
+ ret = _getCryptoResponse(dataPtr, pkType, (uint8_t**)&res);
+ /* wolfCrypt allows positive error codes on success in some
+ * scenarios */
+ if (ret >= 0) {
+ const uint32_t hdr_sz =
+ sizeof(whMessageCrypto_GenericResponseHeader) +
+ sizeof(*res);
+ if (res_len < hdr_sz) {
+ ret = WH_ERROR_ABORTED;
+ }
+ else if (res->sigLen > sigCap) {
+ ret = WH_ERROR_BADARGS;
+ }
+ else {
+ /* Update signature length */
+ *out_len = res->sigLen;
+ }
+ }
+ }
+ }
+
+ (void)wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)out, (void**)&outAddr, sigCap,
+ WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0});
+ (void)wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)in, (void**)&inAddr, in_len,
+ WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0});
+ }
+ else {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+ /* Evict the key manually on error if needed */
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+
+ return ret;
+}
+
+int wh_Client_SlhDsaVerifyDma(whClientContext* ctx, const byte* sig,
+ word32 sig_len, const byte* msg, word32 msg_len,
+ int* out_res, SlhDsaKey* key, const byte* context,
+ byte contextLen, word32 preHashType,
+ int isMPrime)
+{
+ int ret = WH_ERROR_OK;
+ whMessageCrypto_SlhDsaVerifyDmaRequest* req = NULL;
+ whMessageCrypto_SlhDsaVerifyDmaResponse* res = NULL;
+ uint8_t* dataPtr = NULL;
+ uint16_t pkType;
+
+ /* Transaction state */
+ whKeyId key_id;
+ int evict = 0;
+
+ if ((ctx == NULL) || (key == NULL) || ((sig == NULL) && (sig_len > 0)) ||
+ ((msg == NULL) && (msg_len > 0)) || (out_res == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ pkType = (isMPrime != 0) ? WC_PK_TYPE_PQC_SIG_VERIFY_MSG
+ : WC_PK_TYPE_PQC_SIG_VERIFY;
+
+ key_id = WH_DEVCTX_TO_KEYID(key->devCtx);
+
+ /* Import key if necessary */
+ if (WH_KEYID_ISERASED(key_id)) {
+ uint8_t keyLabel[] = "TempSlhDsaVerify";
+ whNvmFlags flags = WH_NVM_FLAGS_USAGE_VERIFY;
+
+ ret = wh_Client_SlhDsaImportKeyDma(ctx, key, &key_id, flags,
+ sizeof(keyLabel), keyLabel);
+ if (ret == WH_ERROR_OK) {
+ evict = 1;
+ }
+ }
+
+ if (ret == WH_ERROR_OK) {
+ /* Request Message */
+ uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA;
+ uint16_t action = WC_ALGO_TYPE_PK;
+ uint32_t options = 0;
+ uintptr_t sigAddr = 0;
+ uintptr_t msgAddr = 0;
+
+ uint16_t req_len = sizeof(whMessageCrypto_GenericRequestHeader) +
+ sizeof(*req) + contextLen;
+
+ /* Get data pointer from the context to use as request/response storage
+ */
+ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm);
+ if (dataPtr == NULL) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Setup generic header and get pointer to request data */
+ req = (whMessageCrypto_SlhDsaVerifyDmaRequest*)
+ _createCryptoRequestWithSubtype(dataPtr, pkType,
+ WC_PQC_SIG_TYPE_SLHDSA,
+ ctx->cryptoAffinity);
+
+ if (req_len <= WOLFHSM_CFG_COMM_DATA_LEN) {
+ if (evict != 0) {
+ options |= WH_MESSAGE_CRYPTO_SLHDSA_VERIFY_OPTIONS_EVICT;
+ }
+ if (isMPrime != 0) {
+ options |= WH_MESSAGE_CRYPTO_SLHDSA_VERIFY_OPTIONS_MPRIME;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->options = options;
+ req->param = _SlhDsaKeyParam(key);
+ req->keyId = key_id;
+ req->contextSz = contextLen;
+ req->preHashType = preHashType;
+ if ((context != NULL) && (contextLen > 0)) {
+ memcpy((uint8_t*)(req + 1), context, contextLen);
+ }
+
+ /* Set up DMA buffers */
+ req->sig.sz = sig_len;
+ ret = wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)sig, (void**)&sigAddr, sig_len,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ req->sig.addr = sigAddr;
+ }
+ if (ret == WH_ERROR_OK) {
+ req->msg.sz = msg_len;
+ ret = wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)msg, (void**)&msgAddr, msg_len,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ req->msg.addr = msgAddr;
+ }
+ }
+
+ /* Send Request */
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Client_SendRequest(ctx, group, action, req_len,
+ (uint8_t*)dataPtr);
+ }
+ if (ret == WH_ERROR_OK) {
+ /* Server will evict at this point if requested */
+ uint16_t res_len = 0;
+ evict = 0;
+
+ /* Recv Response */
+ do {
+ ret = wh_Client_RecvResponse(ctx, &group, &action, &res_len,
+ WOLFHSM_CFG_COMM_DATA_LEN,
+ (uint8_t*)dataPtr);
+ } while (ret == WH_ERROR_NOTREADY);
+
+ if (ret == WH_ERROR_OK) {
+ /* Get response structure pointer, validates generic header
+ * rc */
+ ret = _getCryptoResponse(dataPtr, pkType, (uint8_t**)&res);
+ /* wolfCrypt allows positive error codes on success in some
+ * scenarios */
+ if (ret >= 0) {
+ const uint32_t hdr_sz =
+ sizeof(whMessageCrypto_GenericResponseHeader) +
+ sizeof(*res);
+ /* Note whMessageCrypto_SlhDsaVerifyDmaResponse has no
+ * size field */
+ if (res_len < hdr_sz) {
+ ret = WH_ERROR_ABORTED;
+ }
+ else {
+ /* Set verification result */
+ *out_res = res->verifyResult;
+ }
+ }
+ }
+ }
+
+ (void)wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)msg, (void**)&msgAddr, msg_len,
+ WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0});
+ (void)wh_Client_DmaProcessClientAddress(
+ ctx, (uintptr_t)sig, (void**)&sigAddr, sig_len,
+ WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0});
+ }
+ else {
+ ret = WH_ERROR_BADARGS;
+ }
+ }
+
+ /* Evict the key manually on error if needed */
+ if (evict != 0) {
+ (void)wh_Client_KeyEvict(ctx, key_id);
+ }
+
+ return ret;
+}
+
+int wh_Client_SlhDsaCheckPrivKeyDma(whClientContext* ctx, SlhDsaKey* key,
+ const byte* pubKey, word32 pubKeySz)
+{
+ /* The public key is 2n bytes, so the non-DMA request always fits. There is
+ * nothing for DMA to carry, so reuse the comm-buffer path. */
+ return wh_Client_SlhDsaCheckPrivKey(ctx, key, pubKey, pubKeySz);
+}
+
+#endif /* WOLFHSM_CFG_DMA */
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
#ifdef WOLFSSL_HAVE_MLKEM
int wh_Client_MlKemSetKeyId(MlKemKey* key, whKeyId keyId)
diff --git a/src/wh_client_cryptocb.c b/src/wh_client_cryptocb.c
index 8ad889399..5d5d13b61 100644
--- a/src/wh_client_cryptocb.c
+++ b/src/wh_client_cryptocb.c
@@ -81,7 +81,8 @@ static int _handlePqcStatefulSigSigsLeft(whClientContext* ctx,
#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */
-#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON)
+#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON) || \
+ defined(WOLFSSL_HAVE_SLHDSA)
static int _handlePqcSigKeyGen(whClientContext* ctx, wc_CryptoInfo* info,
int useDma);
static int _handlePqcSign(whClientContext* ctx, wc_CryptoInfo* info,
@@ -90,7 +91,7 @@ static int _handlePqcVerify(whClientContext* ctx, wc_CryptoInfo* info,
int useDma);
static int _handlePqcSigCheckPrivKey(whClientContext* ctx, wc_CryptoInfo* info,
int useDma);
-#endif /* WOLFSSL_HAVE_MLDSA || HAVE_FALCON */
+#endif /* WOLFSSL_HAVE_MLDSA || HAVE_FALCON || WOLFSSL_HAVE_SLHDSA */
int wh_Client_CryptoCb(int devId, wc_CryptoInfo* info, void* inCtx)
{
@@ -564,7 +565,8 @@ int wh_Client_CryptoCbStd(int devId, wc_CryptoInfo* info, void* inCtx)
#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */
-#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON)
+#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON) || \
+ defined(WOLFSSL_HAVE_SLHDSA)
case WC_PK_TYPE_PQC_SIG_KEYGEN:
ret = _handlePqcSigKeyGen(ctx, info, 0);
break;
@@ -581,7 +583,23 @@ int wh_Client_CryptoCbStd(int devId, wc_CryptoInfo* info, void* inCtx)
ret = _handlePqcSigCheckPrivKey(ctx, info, 0);
break;
-#endif /* WOLFSSL_HAVE_MLDSA || HAVE_FALCON */
+#ifdef WOLFSSL_HAVE_SLHDSA
+ /* Only SLH-DSA reaches these: a seeded key generation and the
+ * FIPS 205 internal interface, where the caller builds M' itself. */
+ case WC_PK_TYPE_PQC_SIG_KEYGEN_SEEDED:
+ ret = _handlePqcSigKeyGen(ctx, info, 0);
+ break;
+
+ case WC_PK_TYPE_PQC_SIG_SIGN_MSG:
+ ret = _handlePqcSign(ctx, info, 0);
+ break;
+
+ case WC_PK_TYPE_PQC_SIG_VERIFY_MSG:
+ ret = _handlePqcVerify(ctx, info, 0);
+ break;
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
+#endif /* WOLFSSL_HAVE_MLDSA || HAVE_FALCON || WOLFSSL_HAVE_SLHDSA */
case WC_PK_TYPE_NONE:
default:
@@ -1229,7 +1247,8 @@ static int _handlePqcStatefulSigSigsLeft(whClientContext* ctx,
}
#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */
-#if defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA)
+#if defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \
+ defined(WOLFSSL_HAVE_SLHDSA)
static int _handlePqcSigKeyGen(whClientContext* ctx, wc_CryptoInfo* info,
int useDma)
{
@@ -1263,6 +1282,35 @@ static int _handlePqcSigKeyGen(whClientContext* ctx, wc_CryptoInfo* info,
} break;
#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+ case WC_PQC_SIG_TYPE_SLHDSA: {
+ /* size carries the SlhDsaParam enum, not a byte count */
+ const byte* seed = info->pk.pqc_sig_kg.seed;
+ word32 seedSz = info->pk.pqc_sig_kg.seedSz;
+#ifdef WOLFHSM_CFG_DMA
+ if (useDma) {
+ if (seed != NULL) {
+ ret = wh_Client_SlhDsaMakeExportKeyFromSeedDma(
+ ctx, size, seed, seedSz, key);
+ }
+ else {
+ ret = wh_Client_SlhDsaMakeExportKeyDma(ctx, size, key);
+ }
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ if (seed != NULL) {
+ ret = wh_Client_SlhDsaMakeExportKeyFromSeed(
+ ctx, size, seed, seedSz, key);
+ }
+ else {
+ ret = wh_Client_SlhDsaMakeExportKey(ctx, size, key);
+ }
+ }
+ } break;
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
/* Support for additional PQC algorithms should be added here */
default:
@@ -1273,6 +1321,19 @@ static int _handlePqcSigKeyGen(whClientContext* ctx, wc_CryptoInfo* info,
return ret;
}
+#ifdef WOLFSSL_HAVE_SLHDSA
+/* Whether the caller's key carries actual key bytes rather than being a bare
+ * handle to a server-resident key. Several wolfCrypt SLH-DSA entry points read
+ * PK.seed or PK.root straight out of the key struct, and for a bare handle
+ * those are zeroes that the server must supply from its own copy instead. */
+static int _SlhDsaKeyHasMaterial(const SlhDsaKey* key)
+{
+ return (key != NULL) &&
+ ((key->flags &
+ (WC_SLHDSA_FLAG_PRIVATE | WC_SLHDSA_FLAG_PUBLIC)) != 0);
+}
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
static int _handlePqcSign(whClientContext* ctx, wc_CryptoInfo* info, int useDma)
{
int ret = CRYPTOCB_UNAVAILABLE;
@@ -1313,6 +1374,34 @@ static int _handlePqcSign(whClientContext* ctx, wc_CryptoInfo* info, int useDma)
break;
#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+ case WC_PQC_SIG_TYPE_SLHDSA: {
+ const byte* addRnd = info->pk.pqc_sign.addRnd;
+ byte addRndSz = info->pk.pqc_sign.addRndSz;
+ int randomized = (info->pk.pqc_sign.rng != NULL);
+ int isMPrime =
+ (info->pk.type == WC_PK_TYPE_PQC_SIG_SIGN_MSG);
+
+ if (!_SlhDsaKeyHasMaterial((const SlhDsaKey*)key)) {
+ addRnd = NULL;
+ addRndSz = 0;
+ }
+#ifdef WOLFHSM_CFG_DMA
+ if (useDma) {
+ ret = wh_Client_SlhDsaSignDma(
+ ctx, in, in_len, out, out_len, key, context, contextLen,
+ preHashType, addRnd, addRndSz, randomized, isMPrime);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_SlhDsaSign(
+ ctx, in, in_len, out, out_len, key, context, contextLen,
+ preHashType, addRnd, addRndSz, randomized, isMPrime);
+ }
+ } break;
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
/* Support for additional PQC algorithms should be added here */
default:
@@ -1366,6 +1455,25 @@ static int _handlePqcVerify(whClientContext* ctx, wc_CryptoInfo* info,
break;
#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+ case WC_PQC_SIG_TYPE_SLHDSA: {
+ int isMPrime = (info->pk.type == WC_PK_TYPE_PQC_SIG_VERIFY_MSG);
+#ifdef WOLFHSM_CFG_DMA
+ if (useDma) {
+ ret = wh_Client_SlhDsaVerifyDma(ctx, sig, sig_len, msg, msg_len,
+ res, key, context, contextLen,
+ preHashType, isMPrime);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_SlhDsaVerify(ctx, sig, sig_len, msg, msg_len,
+ res, key, context, contextLen,
+ preHashType, isMPrime);
+ }
+ } break;
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
/* Support for additional PQC algorithms should be added here */
default:
@@ -1410,6 +1518,32 @@ static int _handlePqcSigCheckPrivKey(whClientContext* ctx, wc_CryptoInfo* info,
break;
#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+ case WC_PQC_SIG_TYPE_SLHDSA: {
+ /* wc_SlhDsaKey_CheckKey takes the expected public key out of the
+ * key struct. A bare handle holds none, so drop it and let the
+ * server check its own copy for consistency instead of comparing
+ * against zeroes. */
+ const byte* slhPub = pubKey;
+ word32 slhPubSz = pubKeySz;
+
+ if (!_SlhDsaKeyHasMaterial((const SlhDsaKey*)key)) {
+ slhPub = NULL;
+ slhPubSz = 0;
+ }
+#ifdef WOLFHSM_CFG_DMA
+ if (useDma) {
+ ret = wh_Client_SlhDsaCheckPrivKeyDma(ctx, key, slhPub,
+ slhPubSz);
+ }
+ else
+#endif /* WOLFHSM_CFG_DMA */
+ {
+ ret = wh_Client_SlhDsaCheckPrivKey(ctx, key, slhPub, slhPubSz);
+ }
+ } break;
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
/* Support for additional PQC algorithms should be added here */
default:
@@ -1419,7 +1553,7 @@ static int _handlePqcSigCheckPrivKey(whClientContext* ctx, wc_CryptoInfo* info,
return ret;
}
-#endif /* HAVE_FALCON || WOLFSSL_HAVE_MLDSA */
+#endif /* HAVE_FALCON || WOLFSSL_HAVE_MLDSA || WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFHSM_CFG_DMA
@@ -1563,7 +1697,8 @@ int wh_Client_CryptoCbDma(int devId, wc_CryptoInfo* info, void* inCtx)
ret = _handlePqcStatefulSigSigsLeft(ctx, info, 1);
break;
#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */
-#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON)
+#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON) || \
+ defined(WOLFSSL_HAVE_SLHDSA)
case WC_PK_TYPE_PQC_SIG_KEYGEN:
ret = _handlePqcSigKeyGen(ctx, info, 1);
break;
@@ -1576,7 +1711,18 @@ int wh_Client_CryptoCbDma(int devId, wc_CryptoInfo* info, void* inCtx)
case WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY:
ret = _handlePqcSigCheckPrivKey(ctx, info, 1);
break;
-#endif /* WOLFSSL_HAVE_MLDSA || HAVE_FALCON */
+#ifdef WOLFSSL_HAVE_SLHDSA
+ case WC_PK_TYPE_PQC_SIG_KEYGEN_SEEDED:
+ ret = _handlePqcSigKeyGen(ctx, info, 1);
+ break;
+ case WC_PK_TYPE_PQC_SIG_SIGN_MSG:
+ ret = _handlePqcSign(ctx, info, 1);
+ break;
+ case WC_PK_TYPE_PQC_SIG_VERIFY_MSG:
+ ret = _handlePqcVerify(ctx, info, 1);
+ break;
+#endif /* WOLFSSL_HAVE_SLHDSA */
+#endif /* WOLFSSL_HAVE_MLDSA || HAVE_FALCON || WOLFSSL_HAVE_SLHDSA */
#ifdef HAVE_ED25519
case WC_PK_TYPE_ED25519_KEYGEN: {
ed25519_key* key = info->pk.ed25519kg.key;
diff --git a/src/wh_crypto.c b/src/wh_crypto.c
index 3c786c7ae..b8c83ca79 100644
--- a/src/wh_crypto.c
+++ b/src/wh_crypto.c
@@ -387,6 +387,67 @@ int wh_Crypto_MlDsaDeserializeKeyDer(const uint8_t* buffer, uint16_t size,
}
#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+int wh_Crypto_SlhDsaSerializeKeyDer(SlhDsaKey* key, uint16_t max_size,
+ uint8_t* buffer, uint16_t* out_size)
+{
+ int ret = 0;
+
+ if ((key == NULL) || (buffer == NULL) || (out_size == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Choose appropriate serialization based on key flags */
+ if (key->flags & WC_SLHDSA_FLAG_PRIVATE) {
+#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
+ /* RFC 9909 always carries the public key alongside the private one */
+ ret = wc_SlhDsaKey_KeyToDer(key, buffer, max_size);
+#else
+ ret = WH_ERROR_BADARGS;
+#endif
+ }
+ else if (key->flags & WC_SLHDSA_FLAG_PUBLIC) {
+ /* Public key only - use SPKI format */
+ ret = wc_SlhDsaKey_PublicKeyToDer(key, buffer, max_size, 1);
+ }
+ else {
+ /* No key data set */
+ return WH_ERROR_BADARGS;
+ }
+
+ /* ASN.1 functions return the size of the DER encoded key on success */
+ if (ret > 0) {
+ *out_size = ret;
+ ret = WH_ERROR_OK;
+ }
+ return ret;
+}
+
+int wh_Crypto_SlhDsaDeserializeKeyDer(const uint8_t* buffer, uint16_t size,
+ SlhDsaKey* key)
+{
+ word32 idx = 0;
+ int ret;
+
+ if ((buffer == NULL) || (key == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
+ /* Try private key first, if that fails try public key */
+ ret = wc_SlhDsaKey_PrivateKeyDecode(buffer, &idx, key, size);
+ if (ret != 0) {
+ /* Reset index before trying public key */
+ idx = 0;
+ ret = wc_SlhDsaKey_PublicKeyDecode(buffer, &idx, key, size);
+ }
+#else
+ ret = wc_SlhDsaKey_PublicKeyDecode(buffer, &idx, key, size);
+#endif
+ return ret;
+}
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
#ifdef WOLFSSL_HAVE_MLKEM
int wh_Crypto_MlKemSerializeKey(MlKemKey* key, uint16_t max_size,
uint8_t* buffer, uint16_t* out_size)
diff --git a/src/wh_message_crypto.c b/src/wh_message_crypto.c
index 45579b7be..22a763176 100644
--- a/src/wh_message_crypto.c
+++ b/src/wh_message_crypto.c
@@ -923,6 +923,127 @@ int wh_MessageCrypto_TranslateMlDsaVerifyResponse(
return 0;
}
+/* SLH-DSA Key Generation Request translation */
+int wh_MessageCrypto_TranslateSlhDsaKeyGenRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaKeyGenRequest* src,
+ whMessageCrypto_SlhDsaKeyGenRequest* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, sz);
+ WH_T32(magic, dest, src, param);
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, flags);
+ WH_T32(magic, dest, src, access);
+ WH_T32(magic, dest, src, seedSz);
+ /* Label is just a byte array, no translation needed */
+ if (src != dest) {
+ memcpy(dest->label, src->label, sizeof(src->label));
+ }
+ return 0;
+}
+
+/* SLH-DSA Key Generation Response translation */
+int wh_MessageCrypto_TranslateSlhDsaKeyGenResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaKeyGenResponse* src,
+ whMessageCrypto_SlhDsaKeyGenResponse* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, len);
+ return 0;
+}
+
+/* SLH-DSA Sign Request translation */
+int wh_MessageCrypto_TranslateSlhDsaSignRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaSignRequest* src,
+ whMessageCrypto_SlhDsaSignRequest* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, options);
+ WH_T32(magic, dest, src, param);
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, sz);
+ WH_T32(magic, dest, src, contextSz);
+ WH_T32(magic, dest, src, preHashType);
+ WH_T32(magic, dest, src, addRndSz);
+ return 0;
+}
+
+/* SLH-DSA Sign Response translation */
+int wh_MessageCrypto_TranslateSlhDsaSignResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaSignResponse* src,
+ whMessageCrypto_SlhDsaSignResponse* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, sz);
+ return 0;
+}
+
+/* SLH-DSA Verify Request translation */
+int wh_MessageCrypto_TranslateSlhDsaVerifyRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaVerifyRequest* src,
+ whMessageCrypto_SlhDsaVerifyRequest* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, options);
+ WH_T32(magic, dest, src, param);
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, sigSz);
+ WH_T32(magic, dest, src, hashSz);
+ WH_T32(magic, dest, src, contextSz);
+ WH_T32(magic, dest, src, preHashType);
+ return 0;
+}
+
+/* SLH-DSA Verify Response translation */
+int wh_MessageCrypto_TranslateSlhDsaVerifyResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaVerifyResponse* src,
+ whMessageCrypto_SlhDsaVerifyResponse* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, res);
+ return 0;
+}
+
+/* SLH-DSA Check Private Key Request translation */
+int wh_MessageCrypto_TranslateSlhDsaCheckPrivKeyRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaCheckPrivKeyRequest* src,
+ whMessageCrypto_SlhDsaCheckPrivKeyRequest* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, options);
+ WH_T32(magic, dest, src, param);
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, pubSz);
+ return 0;
+}
+
+/* SLH-DSA Check Private Key Response translation */
+int wh_MessageCrypto_TranslateSlhDsaCheckPrivKeyResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaCheckPrivKeyResponse* src,
+ whMessageCrypto_SlhDsaCheckPrivKeyResponse* dest)
+{
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+ WH_T32(magic, dest, src, res);
+ return 0;
+}
+
/* ML-KEM Key Generation Request translation */
int wh_MessageCrypto_TranslateMlKemKeyGenRequest(
uint16_t magic, const whMessageCrypto_MlKemKeyGenRequest* src,
@@ -1363,6 +1484,165 @@ int wh_MessageCrypto_TranslateMlDsaVerifyDmaResponse(
return 0;
}
+/* SLH-DSA DMA Key Generation Request translation */
+int wh_MessageCrypto_TranslateSlhDsaKeyGenDmaRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaKeyGenDmaRequest* src,
+ whMessageCrypto_SlhDsaKeyGenDmaRequest* 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;
+ }
+
+ ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->seed, &dest->seed);
+ if (ret != 0) {
+ return ret;
+ }
+
+ WH_T32(magic, dest, src, param);
+ WH_T32(magic, dest, src, flags);
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, access);
+ WH_T32(magic, dest, src, labelSize);
+ /* Label is just a byte array, no translation needed */
+ if (src != dest) {
+ memcpy(dest->label, src->label, sizeof(src->label));
+ }
+
+ return 0;
+}
+
+/* SLH-DSA DMA Key Generation Response translation */
+int wh_MessageCrypto_TranslateSlhDsaKeyGenDmaResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaKeyGenDmaResponse* src,
+ whMessageCrypto_SlhDsaKeyGenDmaResponse* 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;
+}
+
+/* SLH-DSA DMA Sign Request translation */
+int wh_MessageCrypto_TranslateSlhDsaSignDmaRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaSignDmaRequest* src,
+ whMessageCrypto_SlhDsaSignDmaRequest* dest)
+{
+ int ret;
+
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->msg, &dest->msg);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->sig, &dest->sig);
+ if (ret != 0) {
+ return ret;
+ }
+
+ WH_T32(magic, dest, src, options);
+ WH_T32(magic, dest, src, param);
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, contextSz);
+ WH_T32(magic, dest, src, preHashType);
+ WH_T32(magic, dest, src, addRndSz);
+
+ return 0;
+}
+
+/* SLH-DSA DMA Sign Response translation */
+int wh_MessageCrypto_TranslateSlhDsaSignDmaResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaSignDmaResponse* src,
+ whMessageCrypto_SlhDsaSignDmaResponse* 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, sigLen);
+ return 0;
+}
+
+/* SLH-DSA DMA Verify Request translation */
+int wh_MessageCrypto_TranslateSlhDsaVerifyDmaRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaVerifyDmaRequest* src,
+ whMessageCrypto_SlhDsaVerifyDmaRequest* dest)
+{
+ int ret;
+
+ if ((src == NULL) || (dest == NULL)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->sig, &dest->sig);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->msg, &dest->msg);
+ if (ret != 0) {
+ return ret;
+ }
+
+ WH_T32(magic, dest, src, options);
+ WH_T32(magic, dest, src, param);
+ WH_T32(magic, dest, src, keyId);
+ WH_T32(magic, dest, src, contextSz);
+ WH_T32(magic, dest, src, preHashType);
+
+ return 0;
+}
+
+/* SLH-DSA DMA Verify Response translation */
+int wh_MessageCrypto_TranslateSlhDsaVerifyDmaResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaVerifyDmaResponse* src,
+ whMessageCrypto_SlhDsaVerifyDmaResponse* 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, verifyResult);
+ return 0;
+}
+
/* ML-KEM DMA Key Generation Request translation */
int wh_MessageCrypto_TranslateMlKemKeyGenDmaRequest(
uint16_t magic, const whMessageCrypto_MlKemKeyGenDmaRequest* src,
diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c
index 23e3b703c..04034410f 100644
--- a/src/wh_server_crypto.c
+++ b/src/wh_server_crypto.c
@@ -1030,6 +1030,103 @@ static int _MlDsaKeyCacheExportEnforce(whServerContext* ctx, whKeyId keyId,
}
#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+#define WH_SERVER_SLHDSA_MAX_CACHE_DER_SIZE WH_CRYPTO_SLHDSA_MAX_KEY_DER_SIZE
+
+WH_UTILS_STATIC_ASSERT(WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE >=
+ WH_SERVER_SLHDSA_MAX_CACHE_DER_SIZE,
+ "WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE too small for "
+ "SLH-DSA key DER");
+
+int wh_Server_SlhDsaKeyCacheImport(whServerContext* ctx, SlhDsaKey* key,
+ whKeyId keyId, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label)
+{
+ int ret = WH_ERROR_OK;
+ uint8_t* cacheBuf;
+ whNvmMetadata* cacheMeta;
+ uint16_t der_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, WH_SERVER_SLHDSA_MAX_CACHE_DER_SIZE, &cacheBuf, &cacheMeta);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Crypto_SlhDsaSerializeKeyDer(
+ key, WH_SERVER_SLHDSA_MAX_CACHE_DER_SIZE, cacheBuf, &der_size);
+ WH_DEBUG_SERVER_VERBOSE("keyId:%u, ret:%d\n", keyId, ret);
+ }
+
+ if (ret == WH_ERROR_OK) {
+ cacheMeta->id = keyId;
+ cacheMeta->len = der_size;
+ /* clients can't set server-only flags (e.g. trusted KEK) */
+ cacheMeta->flags = flags & ~WH_NVM_FLAGS_SERVER_ONLY;
+ cacheMeta->access = WH_NVM_ACCESS_ANY;
+
+ if ((label != NULL) && (label_len > 0)) {
+ memcpy(cacheMeta->label, label, label_len);
+ }
+ }
+
+ return ret;
+}
+
+int wh_Server_SlhDsaKeyCacheExport(whServerContext* ctx, whKeyId keyId,
+ SlhDsaKey* 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_SlhDsaDeserializeKeyDer(cacheBuf, cacheMeta->len, key);
+ WH_DEBUG_SERVER_VERBOSE("keyId:%u, ret:%d\n", keyId, ret);
+ }
+ return ret;
+}
+
+static int _SlhDsaKeyCacheExportEnforce(whServerContext* ctx, whKeyId keyId,
+ whNvmFlags requiredUsage,
+ SlhDsaKey* key)
+{
+ uint8_t* cacheBuf;
+ whNvmMetadata* cacheMeta;
+ int ret;
+
+ if ((ctx == NULL) || (key == NULL) || (WH_KEYID_ISERASED(keyId))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Freshen, check usage and deserialize under one hold of the NVM lock so
+ * the policy verdict, the metadata length and the key bytes all come from
+ * the same snapshot of the shared cache slot. */
+ ret = WH_SERVER_NVM_LOCK(ctx);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Server_KeystoreFreshenKey(ctx, keyId, &cacheBuf, &cacheMeta);
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Server_KeystoreEnforceKeyUsage(cacheMeta, requiredUsage);
+ }
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Crypto_SlhDsaDeserializeKeyDer(cacheBuf, cacheMeta->len,
+ key);
+ WH_DEBUG_SERVER_VERBOSE("keyId:%u, ret:%d\n", keyId, ret);
+ }
+ (void)WH_SERVER_NVM_UNLOCK(ctx);
+ } /* WH_SERVER_NVM_LOCK() */
+ return ret;
+}
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
#ifdef WOLFSSL_HAVE_MLKEM
/* The cache import below always requests a max-size slot, so a build whose big
* cache buffer cannot hold one has no working ML-KEM cache keygen or import. */
@@ -5538,40 +5635,141 @@ static int _HandleMlDsaCheckPrivKey(whServerContext* ctx, uint16_t magic,
}
#endif /* WOLFSSL_HAVE_MLDSA */
-#ifdef WOLFSSL_HAVE_MLKEM
-static int _IsMlKemLevelSupported(int level)
+#ifdef WOLFSSL_HAVE_SLHDSA
+static int _IsSlhDsaParamSupported(int param)
{
- int ret = 0;
+ /* A parameter set with no table row cannot be initialized, so ask the
+ * library rather than tracking the build guards here. */
+ return (wc_SlhDsaKey_SigSizeFromParam((enum SlhDsaParam)param) > 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;
+/* Initialize a key to load a cached one into. req.param is the parameter set
+ * the client believes the key has; it is only a starting point, because the
+ * DER the key was cached as carries the real one and the decoder switches to
+ * it. It is deliberately not enforced: a caller holding nothing but a key id
+ * legitimately initializes its handle with a placeholder parameter set, so a
+ * mismatch here is not an error. */
+static int _SlhDsaInitForCachedKey(SlhDsaKey* key, uint32_t param, int devId)
+{
+ enum SlhDsaParam hint = (enum SlhDsaParam)param;
+
+ if (0 == _IsSlhDsaParamSupported((int)param)) {
+ hint = WC_SLHDSA_DEFAULT_PARAM;
+ }
+ return wc_SlhDsaKey_Init(key, hint, NULL, devId);
+}
+
+/* Load the signing or verifying key named by the request. */
+static int _SlhDsaLoadKey(whServerContext* ctx, whKeyId key_id,
+ whNvmFlags requiredUsage, SlhDsaKey* key)
+{
+ return _SlhDsaKeyCacheExportEnforce(ctx, key_id, requiredUsage, key);
+}
+
+/* Sign with whichever FIPS 205 entry point the request selected. */
+static int _SlhDsaSignDispatch(whServerContext* ctx, SlhDsaKey* key,
+ uint32_t options, const byte* in, word32 in_len,
+ const byte* context, uint32_t contextSz,
+ uint32_t preHashType, const byte* addRnd,
+ uint32_t addRndSz, byte* sig, word32* sigLen)
+{
+ int ret;
+ int mprime = !!(options & WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_MPRIME);
+ int randomized = !!(options &
+ WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_RANDOMIZED);
+
+ if (mprime) {
+ if (addRndSz > 0) {
+ ret = wc_SlhDsaKey_SignMsgWithRandom(key, in, in_len, sig, sigLen,
+ addRnd);
+ }
+ else {
+ /* The randomizer is the key's own PK.seed, which only the server
+ * copy of the key has. */
+ ret = wc_SlhDsaKey_SignMsgDeterministic(key, in, in_len, sig,
+ sigLen);
+ }
+ }
+ else if (preHashType != WC_HASH_TYPE_NONE) {
+ if (addRndSz > 0) {
+ ret = wc_SlhDsaKey_SignHashWithRandom(
+ key, context, (byte)contextSz, in, in_len,
+ (enum wc_HashType)preHashType, sig, sigLen, addRnd);
+ }
+ else if (randomized) {
+ ret = wc_SlhDsaKey_SignHash(key, context, (byte)contextSz, in,
+ in_len, (enum wc_HashType)preHashType,
+ sig, sigLen, ctx->crypto->rng);
+ }
+ else {
+ ret = wc_SlhDsaKey_SignHashDeterministic(
+ key, context, (byte)contextSz, in, in_len,
+ (enum wc_HashType)preHashType, sig, sigLen);
+ }
+ }
+ else {
+ if (addRndSz > 0) {
+ ret = wc_SlhDsaKey_SignWithRandom(key, context, (byte)contextSz, in,
+ in_len, sig, sigLen, addRnd);
+ }
+ else if (randomized) {
+ ret = wc_SlhDsaKey_Sign(key, context, (byte)contextSz, in, in_len,
+ sig, sigLen, ctx->crypto->rng);
+ }
+ else {
+ ret = wc_SlhDsaKey_SignDeterministic(key, context, (byte)contextSz,
+ in, in_len, sig, sigLen);
+ }
}
return ret;
}
-static int _HandleMlKemKeyGen(whServerContext* ctx, uint16_t magic, int devId,
- const void* cryptoDataIn, uint16_t inSize,
- void* cryptoDataOut, uint16_t* outSize)
+/* Verify with whichever FIPS 205 entry point the request selected. wolfCrypt
+ * reports a bad signature as an error code, but the client interface wants a
+ * boolean, so translate here. */
+static int _SlhDsaVerifyDispatch(SlhDsaKey* key, uint32_t options,
+ const byte* sig, word32 sig_len,
+ const byte* msg, word32 msg_len,
+ const byte* context, uint32_t contextSz,
+ uint32_t preHashType, int* out_result)
{
-#ifdef WOLFSSL_MLKEM_NO_MAKE_KEY
+ int ret;
+ int mprime = !!(options & WH_MESSAGE_CRYPTO_SLHDSA_VERIFY_OPTIONS_MPRIME);
+
+ if (mprime) {
+ ret = wc_SlhDsaKey_VerifyMsg(key, msg, msg_len, sig, sig_len);
+ }
+ else if (preHashType != WC_HASH_TYPE_NONE) {
+ ret = wc_SlhDsaKey_VerifyHash(key, context, (byte)contextSz, msg,
+ msg_len, (enum wc_HashType)preHashType,
+ sig, sig_len);
+ }
+ else {
+ ret = wc_SlhDsaKey_Verify(key, context, (byte)contextSz, msg, msg_len,
+ sig, sig_len);
+ }
+
+ if (ret == 0) {
+ *out_result = 1;
+ }
+ else if (ret == WC_NO_ERR_TRACE(SIG_VERIFY_E)) {
+ /* A signature that does not verify is a result, not a failure. Every
+ * other code, BAD_LENGTH_E in particular, describes a malformed
+ * request and is propagated so the caller can tell the two apart the
+ * same way a software-only build would. */
+ *out_result = 0;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int _HandleSlhDsaKeyGen(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize)
+{
+#ifdef WOLFSSL_SLHDSA_VERIFY_ONLY
(void)ctx;
(void)magic;
(void)devId;
@@ -5581,46 +5779,84 @@ static int _HandleMlKemKeyGen(whServerContext* ctx, uint16_t magic, int devId,
(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;
+ int ret = WH_ERROR_OK;
+ SlhDsaKey key[1];
+ whMessageCrypto_SlhDsaKeyGenRequest req;
+ whMessageCrypto_SlhDsaKeyGenResponse res;
+ whKeyId key_id;
+ whNvmFlags flags;
+ uint8_t* label;
+ const byte* seed;
+ uint32_t seedSz;
+ uint8_t* res_out;
+ uint16_t max_size;
+ uint16_t res_size = 0;
+ int param;
- if (inSize < sizeof(whMessageCrypto_MlKemKeyGenRequest)) {
+ if (inSize < sizeof(whMessageCrypto_SlhDsaKeyGenRequest)) {
return WH_ERROR_BADARGS;
}
- ret = wh_MessageCrypto_TranslateMlKemKeyGenRequest(
- magic, (whMessageCrypto_MlKemKeyGenRequest*)cryptoDataIn, &req);
+ /* Translate the request */
+ ret = wh_MessageCrypto_TranslateSlhDsaKeyGenRequest(
+ magic, (whMessageCrypto_SlhDsaKeyGenRequest*)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);
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
+ param = (int)req.param;
+ flags = req.flags;
+ label = req.label;
+ seedSz = req.seedSz;
+ seed = (const byte*)cryptoDataIn +
+ sizeof(whMessageCrypto_SlhDsaKeyGenRequest);
+
+ if (seedSz > (uint32_t)(inSize -
+ sizeof(whMessageCrypto_SlhDsaKeyGenRequest))) {
+ return WH_ERROR_BADARGS;
+ }
+
+ /* Response message. cryptoDataOut already points past the generic
+ * response header, so that header comes out of the budget too. */
+ res_out =
+ (uint8_t*)cryptoDataOut + sizeof(whMessageCrypto_SlhDsaKeyGenResponse);
max_size = (uint16_t)(WOLFHSM_CFG_COMM_DATA_LEN -
- (res_out - (uint8_t*)cryptoDataOut));
+ sizeof(whMessageCrypto_GenericResponseHeader) -
+ sizeof(whMessageCrypto_SlhDsaKeyGenResponse));
- if (!_IsMlKemLevelSupported((int)req.level)) {
+ if (0 == _IsSlhDsaParamSupported(param)) {
return WH_ERROR_BADARGS;
}
- ret = wc_MlKemKey_Init(key, (int)req.level, NULL, devId);
+ ret = wc_SlhDsaKey_Init(key, (enum SlhDsaParam)param, NULL, devId);
if (ret == 0) {
- ret = wc_MlKemKey_MakeKey(key, ctx->crypto->rng);
+ if (seedSz > 0) {
+ /* The seed is the contiguous SK.seed || SK.prf || PK.seed */
+ word32 n = seedSz / 3;
+ if ((seedSz % 3) != 0) {
+ ret = WH_ERROR_BADARGS;
+ }
+ else {
+ ret = wc_SlhDsaKey_MakeKeyWithRandom(key, seed, n, seed + n, n,
+ seed + 2 * n, n);
+ }
+ }
+ else {
+ ret = wc_SlhDsaKey_MakeKey(key, ctx->crypto->rng);
+ }
+
if (ret == 0) {
- if ((req.flags & WH_NVM_FLAGS_EPHEMERAL) != 0) {
+ if (flags & WH_NVM_FLAGS_EPHEMERAL) {
+ /* Must serialize the key into the response message. */
key_id = WH_KEYID_ERASED;
- ret = wh_Crypto_MlKemSerializeKey(key, max_size, res_out,
- &res_size);
+ ret = wh_Crypto_SlhDsaSerializeKeyDer(key, max_size, res_out,
+ &res_size);
}
else {
+ /* Must import the key into the cache and return keyid */
+ res_size = 0;
/* Hold the NVM lock so id allocation and cache import are
* atomic with respect to other server contexts under
* THREADSAFE. */
@@ -5630,53 +5866,42 @@ static int _HandleMlKemKeyGen(whServerContext* ctx, uint16_t magic, int devId,
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);
+ ret = wh_Server_SlhDsaKeyCacheImport(
+ ctx, key, key_id, flags, WH_NVM_LABEL_LEN, label);
}
(void)WH_SERVER_NVM_UNLOCK(ctx);
} /* WH_SERVER_NVM_LOCK() */
- if (ret == WH_ERROR_OK) {
- /* Best-effort public key export: when the serialized
- * public key fits in the response body, return it so the
- * client can skip a separate ExportPublicKey call. When it
- * does not fit (small comm buffer or a large key), leave the
- * body empty and keep the cached key. Plain MakeCacheKey
- * callers ignore the body and see no regression;
- * MakeCacheKeyAndExportPublic callers detect the empty body
- * and evict the key themselves. */
- word32 pubSize = 0;
- if ((wc_MlKemKey_PublicKeySize(key, &pubSize) == 0) &&
- ((uint32_t)pubSize <= (uint32_t)max_size) &&
- (wc_MlKemKey_EncodePublicKey(key, res_out, pubSize) ==
- 0)) {
- res_size = (uint16_t)pubSize;
- }
- else {
- res_size = 0;
- }
+ if (ret == 0) {
+ /* Best-effort public key export so the client can skip a
+ * separate ExportPublicKey call. An empty body is not an
+ * error; MakeCacheKeyAndExportPublic callers detect it. */
+ int pub_ret =
+ wc_SlhDsaKey_PublicKeyToDer(key, res_out, max_size, 1);
+ res_size = (pub_ret > 0) ? (uint16_t)pub_ret : 0;
}
}
}
- wc_MlKemKey_Free(key);
+ wc_SlhDsaKey_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;
- }
+ wh_MessageCrypto_TranslateSlhDsaKeyGenResponse(magic, &res,
+ cryptoDataOut);
+
+ *outSize = sizeof(whMessageCrypto_SlhDsaKeyGenResponse) + res_size;
+ }
return ret;
-#endif /* WOLFSSL_MLKEM_NO_MAKE_KEY */
+#endif /* WOLFSSL_SLHDSA_VERIFY_ONLY */
}
-static int _HandleMlKemEncaps(whServerContext* ctx, uint16_t magic, int devId,
- const void* cryptoDataIn, uint16_t inSize,
- void* cryptoDataOut, uint16_t* outSize)
+static int _HandleSlhDsaSign(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize)
{
-#ifdef WOLFSSL_MLKEM_NO_ENCAPSULATE
+#ifdef WOLFSSL_SLHDSA_VERIFY_ONLY
(void)ctx;
(void)magic;
(void)devId;
@@ -5686,100 +5911,202 @@ static int _HandleMlKemEncaps(whServerContext* ctx, uint16_t magic, int devId,
(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;
-
- if (inSize < sizeof(whMessageCrypto_MlKemEncapsRequest)) {
+ int ret;
+ SlhDsaKey key[1];
+ whMessageCrypto_SlhDsaSignRequest req;
+ whMessageCrypto_SlhDsaSignResponse res;
+ byte* in;
+ byte* req_context;
+ byte* req_addRnd;
+ byte* res_out;
+ whKeyId key_id;
+ word32 in_len;
+ word32 available;
+ word32 max_len;
+ word32 res_len;
+ uint32_t contextSz;
+ uint32_t preHashType;
+ uint32_t addRndSz;
+ uint32_t options;
+ int evict;
+ int sigLen;
+
+ if (inSize < sizeof(whMessageCrypto_SlhDsaSignRequest)) {
return WH_ERROR_BADARGS;
}
- ret = wh_MessageCrypto_TranslateMlKemEncapsRequest(
- magic, (whMessageCrypto_MlKemEncapsRequest*)cryptoDataIn, &req);
+ /* Translate the request */
+ ret = wh_MessageCrypto_TranslateSlhDsaSignRequest(
+ magic, (whMessageCrypto_SlhDsaSignRequest*)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);
+ in = (uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_SlhDsaSignRequest);
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
+ in_len = req.sz;
+ contextSz = req.contextSz;
+ preHashType = req.preHashType;
+ addRndSz = req.addRndSz;
+ options = req.options;
+ evict = !!(options & WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_EVICT);
- if (!_IsMlKemLevelSupported((int)req.level)) {
- ret = WH_ERROR_BADARGS;
- goto cleanup;
+ /* Validate the declared lengths against the remaining payload */
+ available = inSize - sizeof(whMessageCrypto_SlhDsaSignRequest);
+ if (in_len > available) {
+ return WH_ERROR_BADARGS;
}
-
- ret = wc_MlKemKey_Init(key, (int)req.level, NULL, devId);
- if (ret == 0) {
- keyInited = 1;
- /* Export the key, enforcing the derive usage policy against the same
- * locked snapshot that is exported */
- ret = _MlKemKeyCacheExportEnforce(ctx, key_id,
- WH_NVM_FLAGS_USAGE_DERIVE, key);
+ if (contextSz > (available - in_len)) {
+ return WH_ERROR_BADARGS;
}
-
- /* Verify the exported key matches the requested level */
- if (ret == WH_ERROR_OK && key->type != (int)req.level) {
- ret = WH_ERROR_BADARGS;
+ if (addRndSz > (available - in_len - contextSz)) {
+ return WH_ERROR_BADARGS;
+ }
+ if (contextSz > WH_CRYPTO_SLHDSA_MAX_CTX_LEN) {
+ return WH_ERROR_BADARGS;
}
+ req_context = (contextSz > 0) ? (in + in_len) : NULL;
+ req_addRnd = (addRndSz > 0) ? (in + in_len + contextSz) : NULL;
- if (ret == WH_ERROR_OK) {
- ret = wc_MlKemKey_CipherTextSize(key, &ct_len);
+ /* Response message. cryptoDataOut already points past the generic
+ * response header, so that header comes out of the budget too. */
+ res_out =
+ (uint8_t*)(cryptoDataOut) + sizeof(whMessageCrypto_SlhDsaSignResponse);
+ max_len = (word32)(WOLFHSM_CFG_COMM_DATA_LEN -
+ sizeof(whMessageCrypto_GenericResponseHeader) -
+ sizeof(whMessageCrypto_SlhDsaSignResponse));
+ res_len = max_len;
+
+ ret = _SlhDsaInitForCachedKey(key, req.param, devId);
+ if (ret == 0) {
+ ret = _SlhDsaLoadKey(ctx, key_id, WH_NVM_FLAGS_USAGE_SIGN, key);
if (ret == WH_ERROR_OK) {
- ret = wc_MlKemKey_SharedSecretSize(key, &ss_len);
+ /* SLH-DSA signatures run from 7856 to 49856 bytes, so most
+ * parameter sets cannot be returned through the comm buffer at
+ * all. Report that up front rather than as a wolfCrypt length
+ * error from inside the sign call. */
+ sigLen = wc_SlhDsaKey_SigSize(key);
+ if (sigLen <= 0) {
+ ret = WH_ERROR_ABORTED;
+ }
+ else if ((word32)sigLen > max_len) {
+ ret = WH_ERROR_BUFFER_SIZE;
+ }
+ }
+ if (ret == WH_ERROR_OK) {
+ ret = _SlhDsaSignDispatch(ctx, key, options, in, in_len,
+ req_context, contextSz, preHashType,
+ req_addRnd, addRndSz, res_out, &res_len);
}
+ wc_SlhDsaKey_Free(key);
+ }
+ if (evict != 0) {
+ /* User requested to evict from cache, even if the call failed */
+ _CryptoEvictKeyLocked(ctx, key_id);
}
+ if (ret == 0) {
+ res.sz = res_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;
- }
+ wh_MessageCrypto_TranslateSlhDsaSignResponse(
+ magic, &res, (whMessageCrypto_SlhDsaSignResponse*)cryptoDataOut);
+
+ *outSize = sizeof(whMessageCrypto_SlhDsaSignResponse) + res_len;
}
+ return ret;
+#endif /* WOLFSSL_SLHDSA_VERIFY_ONLY */
+}
- 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);
- }
+static int _HandleSlhDsaVerify(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize)
+{
+ int ret;
+ SlhDsaKey key[1];
+ whMessageCrypto_SlhDsaVerifyRequest req;
+ whMessageCrypto_SlhDsaVerifyResponse res;
+ byte* req_sig;
+ byte* req_hash;
+ byte* req_context;
+ whKeyId key_id;
+ uint32_t options;
+ uint32_t hash_len;
+ uint32_t sig_len;
+ uint32_t contextSz;
+ uint32_t preHashType;
+ uint32_t available;
+ int evict;
+ int result = 0;
+
+ if (inSize < sizeof(whMessageCrypto_SlhDsaVerifyRequest)) {
+ return WH_ERROR_BADARGS;
}
- if (keyInited) {
- wc_MlKemKey_Free(key);
+ /* Translate the request */
+ ret = wh_MessageCrypto_TranslateSlhDsaVerifyRequest(
+ magic, (whMessageCrypto_SlhDsaVerifyRequest*)cryptoDataIn, &req);
+ if (ret != 0) {
+ return ret;
+ }
+
+ options = req.options;
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
+ hash_len = req.hashSz;
+ sig_len = req.sigSz;
+ contextSz = req.contextSz;
+ preHashType = req.preHashType;
+ req_sig =
+ (uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_SlhDsaVerifyRequest);
+ evict = !!(options & WH_MESSAGE_CRYPTO_SLHDSA_VERIFY_OPTIONS_EVICT);
+
+ /* Validate lengths against available payload (overflow-safe) */
+ available = inSize - sizeof(whMessageCrypto_SlhDsaVerifyRequest);
+ if ((sig_len > available) || (hash_len > available) ||
+ (sig_len > (available - hash_len))) {
+ return WH_ERROR_BADARGS;
+ }
+ if (contextSz > (available - sig_len - hash_len)) {
+ return WH_ERROR_BADARGS;
+ }
+ if (contextSz > WH_CRYPTO_SLHDSA_MAX_CTX_LEN) {
+ return WH_ERROR_BADARGS;
+ }
+
+ req_hash = req_sig + sig_len;
+ req_context = (contextSz > 0) ? (req_hash + hash_len) : NULL;
+
+ ret = _SlhDsaInitForCachedKey(key, req.param, devId);
+ if (ret == 0) {
+ ret = _SlhDsaLoadKey(ctx, key_id, WH_NVM_FLAGS_USAGE_VERIFY, key);
+ if (ret == WH_ERROR_OK) {
+ ret = _SlhDsaVerifyDispatch(key, options, req_sig, sig_len,
+ req_hash, hash_len, req_context,
+ contextSz, preHashType, &result);
+ }
+ wc_SlhDsaKey_Free(key);
}
-cleanup:
if (evict != 0) {
+ /* User requested to evict from cache, even if the call failed */
_CryptoEvictKeyLocked(ctx, key_id);
}
+ if (ret == 0) {
+ res.res = result;
+
+ wh_MessageCrypto_TranslateSlhDsaVerifyResponse(
+ magic, &res, (whMessageCrypto_SlhDsaVerifyResponse*)cryptoDataOut);
+
+ *outSize = sizeof(whMessageCrypto_SlhDsaVerifyResponse);
+ }
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)
+static int _HandleSlhDsaCheckPrivKey(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize)
{
-#ifdef WOLFSSL_MLKEM_NO_DECAPSULATE
+#ifdef WOLFSSL_SLHDSA_VERIFY_ONLY
(void)ctx;
(void)magic;
(void)devId;
@@ -5789,162 +6116,523 @@ static int _HandleMlKemDecaps(whServerContext* ctx, uint16_t magic, int devId,
(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;
+ int ret;
+ SlhDsaKey key[1];
+ whMessageCrypto_SlhDsaCheckPrivKeyRequest req;
+ whMessageCrypto_SlhDsaCheckPrivKeyResponse res;
+ byte* req_pub;
+ whKeyId key_id;
+ uint32_t pubSz;
+ uint32_t available;
+ int evict;
+ int result = 0;
- if (inSize < sizeof(whMessageCrypto_MlKemDecapsRequest)) {
+ if (inSize < sizeof(whMessageCrypto_SlhDsaCheckPrivKeyRequest)) {
return WH_ERROR_BADARGS;
}
- ret = wh_MessageCrypto_TranslateMlKemDecapsRequest(
- magic, (whMessageCrypto_MlKemDecapsRequest*)cryptoDataIn, &req);
+ ret = wh_MessageCrypto_TranslateSlhDsaCheckPrivKeyRequest(
+ magic, (whMessageCrypto_SlhDsaCheckPrivKeyRequest*)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_DECAPS_OPTIONS_EVICT);
-
- 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;
- /* Export the key, enforcing the derive usage policy against the same
- * locked snapshot that is exported */
- ret = _MlKemKeyCacheExportEnforce(ctx, key_id,
- WH_NVM_FLAGS_USAGE_DERIVE, key);
- }
-
- /* Verify the exported key matches the requested level */
- if (ret == WH_ERROR_OK && key->type != (int)req.level) {
- ret = WH_ERROR_BADARGS;
- }
-
- if (ret == WH_ERROR_OK) {
- ret = wc_MlKemKey_SharedSecretSize(key, &ss_len);
- }
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
+ pubSz = req.pubSz;
+ evict = !!(req.options &
+ WH_MESSAGE_CRYPTO_SLHDSA_CHECKPRIVKEY_OPTIONS_EVICT);
+ req_pub = (uint8_t*)(cryptoDataIn) +
+ sizeof(whMessageCrypto_SlhDsaCheckPrivKeyRequest);
- 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;
- }
+ available = inSize - sizeof(whMessageCrypto_SlhDsaCheckPrivKeyRequest);
+ if (pubSz > available) {
+ return WH_ERROR_BADARGS;
}
- if (ret == WH_ERROR_OK) {
- ret = wc_MlKemKey_Decapsulate(key, res_ss, req_ct, req.ctSz);
+ ret = _SlhDsaInitForCachedKey(key, req.param, devId);
+ if (ret == 0) {
+ ret = _SlhDsaLoadKey(ctx, key_id, WH_NVM_FLAGS_USAGE_SIGN, key);
if (ret == WH_ERROR_OK) {
- res.ssSz = ss_len;
- (void)wh_MessageCrypto_TranslateMlKemDecapsResponse(
- magic, &res, (whMessageCrypto_MlKemDecapsResponse*)cryptoDataOut);
- *outSize = sizeof(whMessageCrypto_MlKemDecapsResponse) + ss_len;
+ /* Recompute the public root from the private seeds */
+ ret = wc_SlhDsaKey_CheckKey(key);
}
- else {
- /* Zero sensitive data on failure */
- wc_ForceZero(res_ss, ss_len);
+ if (ret == WH_ERROR_OK) {
+ /* PK.seed || PK.root sits at the end of the key data. An absent
+ * public key means the caller had none to offer, so the
+ * consistency check above is the whole answer. */
+ uint32_t expected = 2U * (uint32_t)key->params->n;
+ if (pubSz == 0) {
+ result = 1;
+ }
+ else if (pubSz != expected) {
+ result = 0;
+ }
+ else {
+ result = (memcmp(key->sk + 2 * key->params->n, req_pub,
+ pubSz) == 0);
+ }
}
+ wc_SlhDsaKey_Free(key);
}
-
- if (keyInited) {
- wc_MlKemKey_Free(key);
- }
-cleanup:
if (evict != 0) {
_CryptoEvictKeyLocked(ctx, key_id);
}
+ if (ret == 0) {
+ /* Mirror wc_SlhDsaKey_CheckKey: 0 on match, WC_KEY_MISMATCH_E on not */
+ res.res = (result != 0) ? 0 : WC_KEY_MISMATCH_E;
+
+ wh_MessageCrypto_TranslateSlhDsaCheckPrivKeyResponse(
+ magic, &res,
+ (whMessageCrypto_SlhDsaCheckPrivKeyResponse*)cryptoDataOut);
+
+ *outSize = sizeof(whMessageCrypto_SlhDsaCheckPrivKeyResponse);
+ }
return ret;
-#endif /* WOLFSSL_MLKEM_NO_DECAPSULATE */
+#endif /* WOLFSSL_SLHDSA_VERIFY_ONLY */
}
-#endif /* WOLFSSL_HAVE_MLKEM */
+#endif /* WOLFSSL_HAVE_SLHDSA */
-#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON)
-static int _HandlePqcSigAlgorithm(whServerContext* ctx, uint16_t magic,
- int devId, const void* cryptoDataIn,
- uint16_t cryptoInSize, void* cryptoDataOut,
- uint16_t* cryptoOutSize, uint32_t pkAlgoType,
- uint32_t pqAlgoType)
+#ifdef WOLFSSL_HAVE_MLKEM
+static int _IsMlKemLevelSupported(int level)
{
- int ret = WH_ERROR_NOHANDLER;
+ int ret = 0;
- /* Dispatch the appropriate algorithm handler based on the requested PK type
- * and the algorithm type. */
- switch (pqAlgoType) {
-#ifdef WOLFSSL_HAVE_MLDSA
- case WC_PQC_SIG_TYPE_MLDSA: {
- switch (pkAlgoType) {
- case WC_PK_TYPE_PQC_SIG_KEYGEN:
- ret = _HandleMlDsaKeyGen(ctx, magic, devId, cryptoDataIn,
- cryptoInSize, cryptoDataOut,
- cryptoOutSize);
- break;
- case WC_PK_TYPE_PQC_SIG_SIGN:
- ret = _HandleMlDsaSign(ctx, magic, devId, cryptoDataIn,
- cryptoInSize, cryptoDataOut,
- cryptoOutSize);
- break;
- case WC_PK_TYPE_PQC_SIG_VERIFY:
- ret = _HandleMlDsaVerify(ctx, magic, devId, cryptoDataIn,
- cryptoInSize, cryptoDataOut,
- cryptoOutSize);
- break;
- case WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY:
- ret = _HandleMlDsaCheckPrivKey(
- ctx, magic, devId, cryptoDataIn, cryptoInSize,
- cryptoDataOut, cryptoOutSize);
- break;
- default:
- ret = WH_ERROR_NOHANDLER;
- break;
- }
- } break;
-#endif /* WOLFSSL_HAVE_MLDSA */
+ 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 = WH_ERROR_NOHANDLER;
+ ret = 0;
break;
}
return ret;
}
-#endif
-#if defined(WOLFSSL_HAVE_MLKEM)
-static int _HandlePqcKemAlgorithm(whServerContext* ctx, uint16_t magic,
- int devId, const void* cryptoDataIn,
- uint16_t cryptoInSize, void* cryptoDataOut,
- uint16_t* cryptoOutSize, uint32_t pkAlgoType,
- uint32_t pqAlgoType)
+static int _HandleMlKemKeyGen(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize)
{
- 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,
+#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];
+ 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;
+
+ if (inSize < sizeof(whMessageCrypto_MlKemKeyGenRequest)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ 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 {
+ /* Hold the NVM lock so id allocation and cache import are
+ * atomic with respect to other server contexts under
+ * THREADSAFE. */
+ ret = WH_SERVER_NVM_LOCK(ctx);
+ if (ret == WH_ERROR_OK) {
+ 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);
+ }
+ (void)WH_SERVER_NVM_UNLOCK(ctx);
+ } /* WH_SERVER_NVM_LOCK() */
+ if (ret == WH_ERROR_OK) {
+ /* Best-effort public key export: when the serialized
+ * public key fits in the response body, return it so the
+ * client can skip a separate ExportPublicKey call. When it
+ * does not fit (small comm buffer or a large key), leave the
+ * body empty and keep the cached key. Plain MakeCacheKey
+ * callers ignore the body and see no regression;
+ * MakeCacheKeyAndExportPublic callers detect the empty body
+ * and evict the key themselves. */
+ word32 pubSize = 0;
+ if ((wc_MlKemKey_PublicKeySize(key, &pubSize) == 0) &&
+ ((uint32_t)pubSize <= (uint32_t)max_size) &&
+ (wc_MlKemKey_EncodePublicKey(key, res_out, pubSize) ==
+ 0)) {
+ res_size = (uint16_t)pubSize;
+ }
+ else {
+ res_size = 0;
+ }
+ }
+ }
+ }
+ 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;
+
+ if (inSize < sizeof(whMessageCrypto_MlKemEncapsRequest)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ 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 (!_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;
+ /* Export the key, enforcing the derive usage policy against the same
+ * locked snapshot that is exported */
+ ret = _MlKemKeyCacheExportEnforce(ctx, key_id,
+ WH_NVM_FLAGS_USAGE_DERIVE, key);
+ }
+
+ /* Verify the exported key matches the requested level */
+ if (ret == WH_ERROR_OK && key->type != (int)req.level) {
+ ret = WH_ERROR_BADARGS;
+ }
+
+ 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) {
+ _CryptoEvictKeyLocked(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;
+
+ if (inSize < sizeof(whMessageCrypto_MlKemDecapsRequest)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateMlKemDecapsRequest(
+ magic, (whMessageCrypto_MlKemDecapsRequest*)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_DECAPS_OPTIONS_EVICT);
+
+ 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;
+ /* Export the key, enforcing the derive usage policy against the same
+ * locked snapshot that is exported */
+ ret = _MlKemKeyCacheExportEnforce(ctx, key_id,
+ WH_NVM_FLAGS_USAGE_DERIVE, key);
+ }
+
+ /* Verify the exported key matches the requested level */
+ if (ret == WH_ERROR_OK && key->type != (int)req.level) {
+ ret = WH_ERROR_BADARGS;
+ }
+
+ 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) {
+ _CryptoEvictKeyLocked(ctx, key_id);
+ }
+ return ret;
+#endif /* WOLFSSL_MLKEM_NO_DECAPSULATE */
+}
+#endif /* WOLFSSL_HAVE_MLKEM */
+
+#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON) || \
+ defined(WOLFSSL_HAVE_SLHDSA)
+static int _HandlePqcSigAlgorithm(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;
+
+ /* Dispatch the appropriate algorithm handler based on the requested PK type
+ * and the algorithm type. */
+ switch (pqAlgoType) {
+#ifdef WOLFSSL_HAVE_MLDSA
+ case WC_PQC_SIG_TYPE_MLDSA: {
+ switch (pkAlgoType) {
+ case WC_PK_TYPE_PQC_SIG_KEYGEN:
+ ret = _HandleMlDsaKeyGen(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_SIG_SIGN:
+ ret = _HandleMlDsaSign(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_SIG_VERIFY:
+ ret = _HandleMlDsaVerify(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY:
+ ret = _HandleMlDsaCheckPrivKey(
+ ctx, magic, devId, cryptoDataIn, cryptoInSize,
+ cryptoDataOut, cryptoOutSize);
+ break;
+ default:
+ ret = WH_ERROR_NOHANDLER;
+ break;
+ }
+ } break;
+#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+ case WC_PQC_SIG_TYPE_SLHDSA: {
+ switch (pkAlgoType) {
+ case WC_PK_TYPE_PQC_SIG_KEYGEN:
+ case WC_PK_TYPE_PQC_SIG_KEYGEN_SEEDED:
+ ret = _HandleSlhDsaKeyGen(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_SIG_SIGN:
+ case WC_PK_TYPE_PQC_SIG_SIGN_MSG:
+ ret = _HandleSlhDsaSign(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_SIG_VERIFY:
+ case WC_PK_TYPE_PQC_SIG_VERIFY_MSG:
+ ret = _HandleSlhDsaVerify(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY:
+ ret = _HandleSlhDsaCheckPrivKey(
+ ctx, magic, devId, cryptoDataIn, cryptoInSize,
+ cryptoDataOut, cryptoOutSize);
+ break;
+ default:
+ ret = WH_ERROR_NOHANDLER;
+ break;
+ }
+ } break;
+#endif /* WOLFSSL_HAVE_SLHDSA */
+ default:
+ ret = WH_ERROR_NOHANDLER;
+ break;
+ }
+
+ return ret;
+}
+#endif
+
+#if defined(WOLFSSL_HAVE_MLKEM)
+static int _HandlePqcKemAlgorithm(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 = _HandleMlKemKeyGen(ctx, magic, devId, cryptoDataIn,
cryptoInSize, cryptoDataOut,
cryptoOutSize);
break;
@@ -6156,11 +6844,17 @@ int wh_Server_HandleCryptoRequest(whServerContext* ctx, uint16_t magic,
break;
#endif /* HAVE_ED25519 */
-#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON)
+#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON) || \
+ defined(WOLFSSL_HAVE_SLHDSA)
case WC_PK_TYPE_PQC_SIG_KEYGEN:
case WC_PK_TYPE_PQC_SIG_SIGN:
case WC_PK_TYPE_PQC_SIG_VERIFY:
case WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY:
+#ifdef WOLFSSL_HAVE_SLHDSA
+ case WC_PK_TYPE_PQC_SIG_KEYGEN_SEEDED:
+ case WC_PK_TYPE_PQC_SIG_SIGN_MSG:
+ case WC_PK_TYPE_PQC_SIG_VERIFY_MSG:
+#endif
ret = _HandlePqcSigAlgorithm(
ctx, magic, devId, cryptoDataIn, cryptoInSize,
cryptoDataOut, &cryptoOutSize, rqstHeader.algoType,
@@ -6552,49 +7246,289 @@ static int _HandleSha384Dma(whServerContext* ctx, uint16_t magic, int devId,
const uint8_t* inlineData;
void* inAddr = NULL;
- res.hashType = WC_HASH_TYPE_SHA384;
+ res.hashType = WC_HASH_TYPE_SHA384;
+
+ if (inSize < sizeof(whMessageCrypto_Sha512DmaRequest)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateSha512DmaRequest(
+ magic, (const whMessageCrypto_Sha512DmaRequest*)cryptoDataIn, &req);
+ if (ret != WH_ERROR_OK) {
+ return ret;
+ }
+
+ if ((uint32_t)req.inSz >
+ (uint32_t)(inSize - sizeof(whMessageCrypto_Sha512DmaRequest))) {
+ return WH_ERROR_BADARGS;
+ }
+ /* Non-final: inline and DMA input must be multiples of block size */
+ if (!req.isLastBlock && ((req.inSz % WC_SHA384_BLOCK_SIZE) != 0 ||
+ (req.input.sz % WC_SHA384_BLOCK_SIZE) != 0)) {
+ return WH_ERROR_BADARGS;
+ }
+ /* Final: inline data must be less than one block, no DMA input */
+ if (req.isLastBlock &&
+ (req.inSz >= WC_SHA384_BLOCK_SIZE || req.input.sz != 0)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ inlineData =
+ (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha512DmaRequest);
+
+ ret = wc_InitSha384_ex(sha384, NULL, devId);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* SHA384 shares SHA512's internal 64-byte digest state */
+ memcpy(sha384->digest, req.resumeState.hash, WC_SHA512_DIGEST_SIZE);
+ sha384->loLen = req.resumeState.loLen;
+ sha384->hiLen = req.resumeState.hiLen;
+ sha384->buffLen = 0;
+
+ if (ret == 0 && req.inSz > 0) {
+ ret = wc_Sha384Update(sha384, inlineData, req.inSz);
+ }
+
+ if (ret == 0 && req.input.sz > 0) {
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, req.input.addr, &inAddr, req.input.sz,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ preOk = 1;
+ ret = wc_Sha384Update(sha384, inAddr, req.input.sz);
+ }
+ if (ret == WH_ERROR_ACCESS) {
+ res.dmaAddrStatus.badAddr = req.input;
+ }
+ }
+ /* Pair every successful PRE with a POST so DMA callbacks can release any
+ * resources they acquired, even if the Update failed. */
+ if (preOk) {
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, req.input.addr, &inAddr, req.input.sz,
+ WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
+ }
+
+ if (ret == 0) {
+ if (req.isLastBlock) {
+ ret = wc_Sha384Final(sha384, res.hash);
+ }
+ else {
+ if (sha384->buffLen != 0) {
+ ret = WH_ERROR_ABORTED;
+ }
+ else {
+ memcpy(res.hash, sha384->digest, WC_SHA512_DIGEST_SIZE);
+ res.loLen = sha384->loLen;
+ res.hiLen = sha384->hiLen;
+ }
+ }
+ }
+
+ (void)wh_MessageCrypto_TranslateSha2DmaResponse(
+ magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut);
+ *outSize = sizeof(res);
+
+ return ret;
+}
+#endif /* WOLFSSL_SHA384 */
+
+#ifdef WOLFSSL_SHA512
+static int _HandleSha512Dma(whServerContext* ctx, uint16_t magic, int devId,
+ uint16_t seq, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize)
+{
+ (void)seq;
+ int ret = 0;
+ int preOk = 0;
+ whMessageCrypto_Sha512DmaRequest req;
+ whMessageCrypto_Sha2DmaResponse res = {0};
+ wc_Sha512 sha512[1];
+ const uint8_t* inlineData;
+ void* inAddr = NULL;
+ int hashType;
+
+ if (inSize < sizeof(whMessageCrypto_Sha512DmaRequest)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ ret = wh_MessageCrypto_TranslateSha512DmaRequest(
+ magic, (const whMessageCrypto_Sha512DmaRequest*)cryptoDataIn, &req);
+ if (ret != WH_ERROR_OK) {
+ return ret;
+ }
+
+ if ((uint32_t)req.inSz >
+ (uint32_t)(inSize - sizeof(whMessageCrypto_Sha512DmaRequest))) {
+ return WH_ERROR_BADARGS;
+ }
+ /* Non-final: inline and DMA input must be multiples of block size */
+ if (!req.isLastBlock && ((req.inSz % WC_SHA512_BLOCK_SIZE) != 0 ||
+ (req.input.sz % WC_SHA512_BLOCK_SIZE) != 0)) {
+ return WH_ERROR_BADARGS;
+ }
+ /* Final: inline data must be less than one block, no DMA input */
+ if (req.isLastBlock &&
+ (req.inSz >= WC_SHA512_BLOCK_SIZE || req.input.sz != 0)) {
+ return WH_ERROR_BADARGS;
+ }
+
+ inlineData =
+ (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha512DmaRequest);
+ hashType = req.resumeState.hashType;
+
+ /* If the client requested a variant the server does not have compiled in,
+ * normalize hashType to plain SHA512 so the response reflects what was
+ * actually executed; the client detects the mismatch against its own
+ * hashType and returns an error. */
+ switch (hashType) {
+#ifndef WOLFSSL_NOSHA512_224
+ case WC_HASH_TYPE_SHA512_224:
+ ret = wc_InitSha512_224_ex(sha512, NULL, devId);
+ break;
+#endif
+#ifndef WOLFSSL_NOSHA512_256
+ case WC_HASH_TYPE_SHA512_256:
+ ret = wc_InitSha512_256_ex(sha512, NULL, devId);
+ break;
+#endif
+ default:
+ ret = wc_InitSha512_ex(sha512, NULL, devId);
+ hashType = WC_HASH_TYPE_SHA512;
+ break;
+ }
+ if (ret != 0) {
+ return ret;
+ }
+
+ res.hashType = hashType;
+
+ memcpy(sha512->digest, req.resumeState.hash, WC_SHA512_DIGEST_SIZE);
+ sha512->loLen = req.resumeState.loLen;
+ sha512->hiLen = req.resumeState.hiLen;
+ sha512->buffLen = 0;
+
+ if (ret == 0 && req.inSz > 0) {
+ ret = wc_Sha512Update(sha512, inlineData, req.inSz);
+ }
+
+ if (ret == 0 && req.input.sz > 0) {
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, req.input.addr, &inAddr, req.input.sz,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
+ if (ret == WH_ERROR_OK) {
+ preOk = 1;
+ ret = wc_Sha512Update(sha512, inAddr, req.input.sz);
+ }
+ if (ret == WH_ERROR_ACCESS) {
+ res.dmaAddrStatus.badAddr = req.input;
+ }
+ }
+ /* Pair every successful PRE with a POST so DMA callbacks can release any
+ * resources they acquired, even if the Update failed. */
+ if (preOk) {
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, req.input.addr, &inAddr, req.input.sz,
+ WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
+ }
+
+ if (ret == 0) {
+ if (req.isLastBlock) {
+ switch (hashType) {
+#ifndef WOLFSSL_NOSHA512_224
+ case WC_HASH_TYPE_SHA512_224:
+ ret = wc_Sha512_224Final(sha512, res.hash);
+ break;
+#endif
+#ifndef WOLFSSL_NOSHA512_256
+ case WC_HASH_TYPE_SHA512_256:
+ ret = wc_Sha512_256Final(sha512, res.hash);
+ break;
+#endif
+ default:
+ ret = wc_Sha512Final(sha512, res.hash);
+ break;
+ }
+ }
+ else {
+ if (sha512->buffLen != 0) {
+ ret = WH_ERROR_ABORTED;
+ }
+ else {
+ memcpy(res.hash, sha512->digest, WC_SHA512_DIGEST_SIZE);
+ res.loLen = sha512->loLen;
+ res.hiLen = sha512->hiLen;
+ }
+ }
+ }
+
+ (void)wh_MessageCrypto_TranslateSha2DmaResponse(
+ magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut);
+ *outSize = sizeof(res);
+
+ return ret;
+}
+#endif /* WOLFSSL_SHA512 */
+
+#if defined(WOLFSSL_SHA3)
+static int _HandleSha3Dma(whServerContext* ctx, int hashType, uint16_t magic,
+ int devId, uint16_t seq, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize)
+{
+ (void)seq;
+ int ret = 0;
+ int preOk = 0;
+ whMessageCrypto_Sha3DmaRequest req;
+ whMessageCrypto_Sha3DmaResponse res = {0};
+ wc_Sha3 sha3[1];
+ const uint8_t* inlineData;
+ void* inAddr = NULL;
+ _Sha3VariantOps ops;
+
+ ret = _Sha3LookupOps(hashType, &ops);
+ if (ret != 0) {
+ return ret;
+ }
- if (inSize < sizeof(whMessageCrypto_Sha512DmaRequest)) {
+ if (inSize < sizeof(whMessageCrypto_Sha3DmaRequest)) {
return WH_ERROR_BADARGS;
}
- ret = wh_MessageCrypto_TranslateSha512DmaRequest(
- magic, (const whMessageCrypto_Sha512DmaRequest*)cryptoDataIn, &req);
+ ret = wh_MessageCrypto_TranslateSha3DmaRequest(
+ magic, (const whMessageCrypto_Sha3DmaRequest*)cryptoDataIn, &req);
if (ret != WH_ERROR_OK) {
return ret;
}
if ((uint32_t)req.inSz >
- (uint32_t)(inSize - sizeof(whMessageCrypto_Sha512DmaRequest))) {
+ (uint32_t)(inSize - sizeof(whMessageCrypto_Sha3DmaRequest))) {
return WH_ERROR_BADARGS;
}
- /* Non-final: inline and DMA input must be multiples of block size */
- if (!req.isLastBlock && ((req.inSz % WC_SHA384_BLOCK_SIZE) != 0 ||
- (req.input.sz % WC_SHA384_BLOCK_SIZE) != 0)) {
+ if (!req.isLastBlock && ((req.inSz % ops.blockSize) != 0 ||
+ (req.input.sz % ops.blockSize) != 0)) {
return WH_ERROR_BADARGS;
}
- /* Final: inline data must be less than one block, no DMA input */
- if (req.isLastBlock &&
- (req.inSz >= WC_SHA384_BLOCK_SIZE || req.input.sz != 0)) {
+ if (req.isLastBlock && (req.inSz >= ops.blockSize || req.input.sz != 0)) {
return WH_ERROR_BADARGS;
}
inlineData =
- (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha512DmaRequest);
+ (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha3DmaRequest);
- ret = wc_InitSha384_ex(sha384, NULL, devId);
+ ret = ops.initFn(sha3, NULL, devId);
if (ret != 0) {
return ret;
}
- /* SHA384 shares SHA512's internal 64-byte digest state */
- memcpy(sha384->digest, req.resumeState.hash, WC_SHA512_DIGEST_SIZE);
- sha384->loLen = req.resumeState.loLen;
- sha384->hiLen = req.resumeState.hiLen;
- sha384->buffLen = 0;
+ /* Restore Keccak state from client. initFn already zeroed t[] and i. */
+ memcpy(sha3->s, req.resumeState.s, sizeof(sha3->s));
if (ret == 0 && req.inSz > 0) {
- ret = wc_Sha384Update(sha384, inlineData, req.inSz);
+ ret = ops.updateFn(sha3, inlineData, req.inSz);
}
if (ret == 0 && req.input.sz > 0) {
@@ -6603,14 +7537,12 @@ static int _HandleSha384Dma(whServerContext* ctx, uint16_t magic, int devId,
WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
if (ret == WH_ERROR_OK) {
preOk = 1;
- ret = wc_Sha384Update(sha384, inAddr, req.input.sz);
+ ret = ops.updateFn(sha3, inAddr, req.input.sz);
}
if (ret == WH_ERROR_ACCESS) {
res.dmaAddrStatus.badAddr = req.input;
}
}
- /* Pair every successful PRE with a POST so DMA callbacks can release any
- * resources they acquired, even if the Update failed. */
if (preOk) {
(void)wh_Server_DmaProcessClientAddress(
ctx, req.input.addr, &inAddr, req.input.sz,
@@ -6619,272 +7551,487 @@ static int _HandleSha384Dma(whServerContext* ctx, uint16_t magic, int devId,
if (ret == 0) {
if (req.isLastBlock) {
- ret = wc_Sha384Final(sha384, res.hash);
+ ret = ops.finalFn(sha3, res.hash);
}
else {
- if (sha384->buffLen != 0) {
+ if (sha3->i != 0) {
ret = WH_ERROR_ABORTED;
}
else {
- memcpy(res.hash, sha384->digest, WC_SHA512_DIGEST_SIZE);
- res.loLen = sha384->loLen;
- res.hiLen = sha384->hiLen;
+ memcpy(res.resumeState.s, sha3->s, sizeof(res.resumeState.s));
}
}
}
- (void)wh_MessageCrypto_TranslateSha2DmaResponse(
- magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut);
+ (void)wh_MessageCrypto_TranslateSha3DmaResponse(
+ magic, &res, (whMessageCrypto_Sha3DmaResponse*)cryptoDataOut);
*outSize = sizeof(res);
return ret;
}
-#endif /* WOLFSSL_SHA384 */
+#endif /* WOLFSSL_SHA3 */
-#ifdef WOLFSSL_SHA512
-static int _HandleSha512Dma(whServerContext* ctx, uint16_t magic, int devId,
- uint16_t seq, const void* cryptoDataIn,
- uint16_t inSize, void* cryptoDataOut,
- uint16_t* outSize)
+#if defined(WOLFSSL_HAVE_MLDSA)
+
+static int _HandleMlDsaKeyGenDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize)
{
- (void)seq;
- int ret = 0;
- int preOk = 0;
- whMessageCrypto_Sha512DmaRequest req;
- whMessageCrypto_Sha2DmaResponse res = {0};
- wc_Sha512 sha512[1];
- const uint8_t* inlineData;
- void* inAddr = NULL;
- int hashType;
+#ifdef WOLFSSL_MLDSA_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;
+ wc_MlDsaKey key[1];
+ void* clientOutAddr = NULL;
+ uint16_t keySize = 0;
- if (inSize < sizeof(whMessageCrypto_Sha512DmaRequest)) {
+ whMessageCrypto_MlDsaKeyGenDmaRequest req;
+ whMessageCrypto_MlDsaKeyGenDmaResponse res;
+
+ memset(&res, 0, sizeof(res));
+
+ if (inSize < sizeof(whMessageCrypto_MlDsaKeyGenDmaRequest)) {
return WH_ERROR_BADARGS;
}
- ret = wh_MessageCrypto_TranslateSha512DmaRequest(
- magic, (const whMessageCrypto_Sha512DmaRequest*)cryptoDataIn, &req);
+ /* Translate the request */
+ ret = wh_MessageCrypto_TranslateMlDsaKeyGenDmaRequest(
+ magic, (whMessageCrypto_MlDsaKeyGenDmaRequest*)cryptoDataIn, &req);
if (ret != WH_ERROR_OK) {
return ret;
}
- if ((uint32_t)req.inSz >
- (uint32_t)(inSize - sizeof(whMessageCrypto_Sha512DmaRequest))) {
- return WH_ERROR_BADARGS;
+ /* Check the ML-DSA security level is valid and supported */
+ if (0 == _IsMlDsaLevelSupported(req.level)) {
+ ret = WH_ERROR_BADARGS;
}
- /* Non-final: inline and DMA input must be multiples of block size */
- if (!req.isLastBlock && ((req.inSz % WC_SHA512_BLOCK_SIZE) != 0 ||
- (req.input.sz % WC_SHA512_BLOCK_SIZE) != 0)) {
- return WH_ERROR_BADARGS;
+ else {
+ /* init mldsa key */
+ ret = wc_MlDsaKey_Init(key, NULL, devId);
+ if (ret == 0) {
+ /* Set the ML-DSA security level */
+ ret = wc_MlDsaKey_SetParams(key, req.level);
+ if (ret == 0) {
+ /* generate the key */
+ ret = wc_MlDsaKey_MakeKey(key, ctx->crypto->rng);
+ if (ret == 0) {
+ /* Check incoming flags */
+ if (req.flags & WH_NVM_FLAGS_EPHEMERAL) {
+ /* Must serialize the key into client memory */
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, req.key.addr, &clientOutAddr, req.key.sz,
+ WH_DMA_OPER_CLIENT_WRITE_PRE,
+ (whServerDmaFlags){0});
+
+ if (ret == 0) {
+ ret = wh_Crypto_MlDsaSerializeKeyDer(
+ key, req.key.sz, clientOutAddr, &keySize);
+ if (ret == 0) {
+ res.keyId = WH_KEYID_ERASED;
+ res.keySize = keySize;
+ }
+ }
+
+ if (ret == 0) {
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, req.key.addr, &clientOutAddr, keySize,
+ WH_DMA_OPER_CLIENT_WRITE_POST,
+ (whServerDmaFlags){0});
+ }
+ }
+ else {
+ /* Must import the key into the cache and return keyid
+ */
+ whKeyId keyId = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+
+ /* Hold the NVM lock so id allocation and cache import
+ * are atomic with respect to other server contexts
+ * under THREADSAFE. */
+ ret = WH_SERVER_NVM_LOCK(ctx);
+ if (ret == WH_ERROR_OK) {
+ if (WH_KEYID_ISERASED(keyId)) {
+ /* Generate a new id */
+ ret =
+ wh_Server_KeystoreGetUniqueId(ctx, &keyId);
+ WH_DEBUG_SERVER("UniqueId: keyId:%u, ret:%d\n",
+ keyId, ret);
+ }
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Server_MlDsaKeyCacheImport(
+ ctx, key, keyId, req.flags, req.labelSize,
+ req.label);
+ WH_DEBUG_SERVER(
+ "CacheImport: keyId:%u, ret:%d\n", keyId,
+ ret);
+ }
+ (void)WH_SERVER_NVM_UNLOCK(ctx);
+ } /* WH_SERVER_NVM_LOCK() */
+#ifdef WOLFSSL_MLDSA_PUBLIC_KEY
+ /* Stream the public key back through the client's DMA
+ * buffer so it gets the pubkey without a separate
+ * ExportPublicKey call. A freshly generated key must
+ * serialize, so treat a failure as fatal: evict the
+ * just-committed key and propagate the error rather
+ * than returning a keyId with no public key. */
+ if (ret == 0) {
+ int rc = wh_Server_DmaProcessClientAddress(
+ ctx, req.key.addr, &clientOutAddr, req.key.sz,
+ WH_DMA_OPER_CLIENT_WRITE_PRE,
+ (whServerDmaFlags){0});
+ if (rc == 0) {
+ int pub_ret = wc_MlDsaKey_PublicKeyToDer(
+ key, (byte*)clientOutAddr,
+ (word32)req.key.sz, 1);
+ if (pub_ret > 0) {
+ keySize = (uint16_t)pub_ret;
+ }
+ else {
+ ret = (pub_ret < 0) ? pub_ret
+ : WH_ERROR_ABORTED;
+ }
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, req.key.addr, &clientOutAddr, keySize,
+ WH_DMA_OPER_CLIENT_WRITE_POST,
+ (whServerDmaFlags){0});
+ }
+ else {
+ ret = rc;
+ }
+ if (ret != 0) {
+ _CryptoEvictKeyLocked(ctx, keyId);
+ }
+ }
+#endif /* WOLFSSL_MLDSA_PUBLIC_KEY */
+ if (ret == 0) {
+ res.keyId = wh_KeyId_TranslateToClient(keyId);
+ res.keySize = keySize;
+ }
+ }
+ }
+ }
+ wc_MlDsaKey_Free(key);
+ }
}
- /* Final: inline data must be less than one block, no DMA input */
- if (req.isLastBlock &&
- (req.inSz >= WC_SHA512_BLOCK_SIZE || req.input.sz != 0)) {
- return WH_ERROR_BADARGS;
+
+ if (ret == WH_ERROR_ACCESS) {
+ res.dmaAddrStatus.badAddr = req.key;
}
- inlineData =
- (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha512DmaRequest);
- hashType = req.resumeState.hashType;
+ /* Translate the response */
+ (void)wh_MessageCrypto_TranslateMlDsaKeyGenDmaResponse(
+ magic, &res, (whMessageCrypto_MlDsaKeyGenDmaResponse*)cryptoDataOut);
+
+ *outSize = sizeof(res);
+
+ return ret;
+#endif /* WOLFSSL_MLDSA_NO_MAKE_KEY */
+}
+
+static int _HandleMlDsaSignDma(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize)
+{
+#ifdef WOLFSSL_MLDSA_NO_SIGN
+ (void)ctx;
+ (void)magic;
+ (void)devId;
+ (void)cryptoDataIn;
+ (void)inSize;
+ (void)cryptoDataOut;
+ (void)outSize;
+ return WH_ERROR_NOHANDLER;
+#else
+ int ret = 0;
+ wc_MlDsaKey key[1];
+ void* msgAddr = NULL;
+ void* sigAddr = NULL;
+ word32 sigLen = 0;
+
+ whMessageCrypto_MlDsaSignDmaRequest req;
+ whMessageCrypto_MlDsaSignDmaResponse res;
- /* If the client requested a variant the server does not have compiled in,
- * normalize hashType to plain SHA512 so the response reflects what was
- * actually executed; the client detects the mismatch against its own
- * hashType and returns an error. */
- switch (hashType) {
-#ifndef WOLFSSL_NOSHA512_224
- case WC_HASH_TYPE_SHA512_224:
- ret = wc_InitSha512_224_ex(sha512, NULL, devId);
- break;
-#endif
-#ifndef WOLFSSL_NOSHA512_256
- case WC_HASH_TYPE_SHA512_256:
- ret = wc_InitSha512_256_ex(sha512, NULL, devId);
- break;
-#endif
- default:
- ret = wc_InitSha512_ex(sha512, NULL, devId);
- hashType = WC_HASH_TYPE_SHA512;
- break;
+ if (inSize < sizeof(whMessageCrypto_MlDsaSignDmaRequest)) {
+ return WH_ERROR_BADARGS;
}
- if (ret != 0) {
+
+ /* Translate the request */
+ ret = wh_MessageCrypto_TranslateMlDsaSignDmaRequest(
+ magic, (whMessageCrypto_MlDsaSignDmaRequest*)cryptoDataIn, &req);
+ if (ret != WH_ERROR_OK) {
return ret;
}
- res.hashType = hashType;
+ /* Transaction state */
+ whKeyId key_id;
+ int evict = 0;
- memcpy(sha512->digest, req.resumeState.hash, WC_SHA512_DIGEST_SIZE);
- sha512->loLen = req.resumeState.loLen;
- sha512->hiLen = req.resumeState.hiLen;
- sha512->buffLen = 0;
- if (ret == 0 && req.inSz > 0) {
- ret = wc_Sha512Update(sha512, inlineData, req.inSz);
- }
+ /* Get key ID and evict flag */
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
+ evict = !!(req.options & WH_MESSAGE_CRYPTO_MLDSA_SIGN_OPTIONS_EVICT);
- if (ret == 0 && req.input.sz > 0) {
- ret = wh_Server_DmaProcessClientAddress(
- ctx, req.input.addr, &inAddr, req.input.sz,
- WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
- if (ret == WH_ERROR_OK) {
- preOk = 1;
- ret = wc_Sha512Update(sha512, inAddr, req.input.sz);
- }
- if (ret == WH_ERROR_ACCESS) {
- res.dmaAddrStatus.badAddr = req.input;
- }
+ /* Extract context from inline data after the struct */
+ uint32_t contextSz = req.contextSz;
+ uint32_t preHashType = req.preHashType;
+ byte* req_context = NULL;
+ if (contextSz > WH_CRYPTO_MLDSA_MAX_CTX_LEN) {
+ return WH_ERROR_BADARGS;
}
- /* Pair every successful PRE with a POST so DMA callbacks can release any
- * resources they acquired, even if the Update failed. */
- if (preOk) {
- (void)wh_Server_DmaProcessClientAddress(
- ctx, req.input.addr, &inAddr, req.input.sz,
- WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
+ if (contextSz > 0) {
+ if (inSize < sizeof(whMessageCrypto_MlDsaSignDmaRequest) + contextSz) {
+ return WH_ERROR_BADARGS;
+ }
+ req_context = (uint8_t*)(cryptoDataIn) +
+ sizeof(whMessageCrypto_MlDsaSignDmaRequest);
}
+ /* Initialize key */
+ ret = wc_MlDsaKey_Init(key, NULL, devId);
if (ret == 0) {
- if (req.isLastBlock) {
- switch (hashType) {
-#ifndef WOLFSSL_NOSHA512_224
- case WC_HASH_TYPE_SHA512_224:
- ret = wc_Sha512_224Final(sha512, res.hash);
- break;
-#endif
-#ifndef WOLFSSL_NOSHA512_256
- case WC_HASH_TYPE_SHA512_256:
- ret = wc_Sha512_256Final(sha512, res.hash);
- break;
-#endif
- default:
- ret = wc_Sha512Final(sha512, res.hash);
- break;
- }
- }
- else {
- if (sha512->buffLen != 0) {
- ret = WH_ERROR_ABORTED;
- }
- else {
- memcpy(res.hash, sha512->digest, WC_SHA512_DIGEST_SIZE);
- res.loLen = sha512->loLen;
- res.hiLen = sha512->hiLen;
+ /* Export key from cache */
+ /* TODO: sanity check security level against key pulled from cache? */
+ /* Export the key, enforcing the sign usage policy against the same
+ * locked snapshot that is exported. The non-DMA sign handler enforces
+ * the same policy. */
+ ret = _MlDsaKeyCacheExportEnforce(ctx, key_id, WH_NVM_FLAGS_USAGE_SIGN,
+ key);
+ if (ret == 0) {
+ /* Process client message buffer address */
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.msg.addr, &msgAddr, req.msg.sz,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
+
+ if (ret == 0) {
+ /* Process client signature buffer address */
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.sig.addr, &sigAddr, req.sig.sz,
+ WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0});
+
+ if (ret == 0) {
+ /* Sign the message using appropriate FIPS 204 API */
+ sigLen = req.sig.sz;
+ if (preHashType != WC_HASH_TYPE_NONE) {
+ ret = wc_MlDsaKey_SignCtxHash(
+ key, req_context, (byte)contextSz,
+ sigAddr, &sigLen, msgAddr, req.msg.sz,
+ preHashType, ctx->crypto->rng);
+ }
+ else {
+ ret = wc_MlDsaKey_SignCtx(
+ key, req_context, (byte)contextSz,
+ sigAddr, &sigLen, msgAddr, req.msg.sz,
+ ctx->crypto->rng);
+ }
+ }
+
+ if (sigAddr != NULL) {
+ /* Post-write processing of signature buffer */
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.sig.addr, &sigAddr, sigLen,
+ WH_DMA_OPER_CLIENT_WRITE_POST,
+ (whServerDmaFlags){0});
+ }
+ if (msgAddr != NULL) {
+ /* Post-read processing of message buffer */
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.msg.addr, &msgAddr,
+ req.msg.sz, WH_DMA_OPER_CLIENT_READ_POST,
+ (whServerDmaFlags){0});
+ }
}
}
+ wc_MlDsaKey_Free(key);
}
- (void)wh_MessageCrypto_TranslateSha2DmaResponse(
- magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut);
- *outSize = sizeof(res);
+ /* Evict key if requested */
+ if (evict) {
+ /* User requested to evict from cache, even if the call failed */
+ _CryptoEvictKeyLocked(ctx, key_id);
+ }
+
+ if (ret == 0) {
+ /* Set response signature length */
+ res.sigLen = sigLen;
+ *outSize = sizeof(res);
+
+ /* Translate the response */
+ (void)wh_MessageCrypto_TranslateMlDsaSignDmaResponse(
+ magic, &res, (whMessageCrypto_MlDsaSignDmaResponse*)cryptoDataOut);
+ }
return ret;
+#endif /* WOLFSSL_MLDSA_NO_SIGN */
}
-#endif /* WOLFSSL_SHA512 */
-#if defined(WOLFSSL_SHA3)
-static int _HandleSha3Dma(whServerContext* ctx, int hashType, uint16_t magic,
- int devId, uint16_t seq, const void* cryptoDataIn,
- uint16_t inSize, void* cryptoDataOut,
- uint16_t* outSize)
+static int _HandleMlDsaVerifyDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize)
{
- (void)seq;
- int ret = 0;
- int preOk = 0;
- whMessageCrypto_Sha3DmaRequest req;
- whMessageCrypto_Sha3DmaResponse res = {0};
- wc_Sha3 sha3[1];
- const uint8_t* inlineData;
- void* inAddr = NULL;
- _Sha3VariantOps ops;
+#ifdef WOLFSSL_MLDSA_NO_VERIFY
+ (void)ctx;
+ (void)magic;
+ (void)devId;
+ (void)cryptoDataIn;
+ (void)inSize;
+ (void)cryptoDataOut;
+ (void)outSize;
+ return WH_ERROR_NOHANDLER;
+#else
+ int ret = 0;
+ wc_MlDsaKey key[1];
+ void* msgAddr = NULL;
+ void* sigAddr = NULL;
+ int verified = 0;
- ret = _Sha3LookupOps(hashType, &ops);
- if (ret != 0) {
- return ret;
- }
+ whMessageCrypto_MlDsaVerifyDmaRequest req;
+ whMessageCrypto_MlDsaVerifyDmaResponse res;
- if (inSize < sizeof(whMessageCrypto_Sha3DmaRequest)) {
+ if (inSize < sizeof(whMessageCrypto_MlDsaVerifyDmaRequest)) {
return WH_ERROR_BADARGS;
}
- ret = wh_MessageCrypto_TranslateSha3DmaRequest(
- magic, (const whMessageCrypto_Sha3DmaRequest*)cryptoDataIn, &req);
+ /* Translate the request */
+ ret = wh_MessageCrypto_TranslateMlDsaVerifyDmaRequest(
+ magic, (whMessageCrypto_MlDsaVerifyDmaRequest*)cryptoDataIn, &req);
if (ret != WH_ERROR_OK) {
return ret;
}
- if ((uint32_t)req.inSz >
- (uint32_t)(inSize - sizeof(whMessageCrypto_Sha3DmaRequest))) {
- return WH_ERROR_BADARGS;
- }
- if (!req.isLastBlock && ((req.inSz % ops.blockSize) != 0 ||
- (req.input.sz % ops.blockSize) != 0)) {
- return WH_ERROR_BADARGS;
- }
- if (req.isLastBlock && (req.inSz >= ops.blockSize || req.input.sz != 0)) {
+ /* Transaction state */
+ whKeyId key_id;
+ int evict = 0;
+
+ /* Get key ID and evict flag */
+ key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
+ ctx->comm->client_id, req.keyId);
+ evict = !!(req.options & WH_MESSAGE_CRYPTO_MLDSA_VERIFY_OPTIONS_EVICT);
+
+ /* Extract context from inline data after the struct */
+ uint32_t contextSz = req.contextSz;
+ uint32_t preHashType = req.preHashType;
+ byte* req_context = NULL;
+ if (contextSz > WH_CRYPTO_MLDSA_MAX_CTX_LEN) {
return WH_ERROR_BADARGS;
}
+ if (contextSz > 0) {
+ if (inSize < sizeof(whMessageCrypto_MlDsaVerifyDmaRequest) + contextSz) {
+ return WH_ERROR_BADARGS;
+ }
+ req_context = (uint8_t*)(cryptoDataIn) +
+ sizeof(whMessageCrypto_MlDsaVerifyDmaRequest);
+ }
- inlineData =
- (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha3DmaRequest);
-
- ret = ops.initFn(sha3, NULL, devId);
+ /* Initialize key */
+ ret = wc_MlDsaKey_Init(key, NULL, devId);
if (ret != 0) {
return ret;
}
- /* Restore Keccak state from client. initFn already zeroed t[] and i. */
- memcpy(sha3->s, req.resumeState.s, sizeof(sha3->s));
-
- if (ret == 0 && req.inSz > 0) {
- ret = ops.updateFn(sha3, inlineData, req.inSz);
- }
-
- if (ret == 0 && req.input.sz > 0) {
+ /* Export the key, enforcing the verify usage policy against the same
+ * locked snapshot that is exported. The non-DMA verify handler enforces
+ * the same policy. */
+ ret = _MlDsaKeyCacheExportEnforce(ctx, key_id, WH_NVM_FLAGS_USAGE_VERIFY,
+ key);
+ if (ret == 0) {
+ /* Process client signature buffer address */
ret = wh_Server_DmaProcessClientAddress(
- ctx, req.input.addr, &inAddr, req.input.sz,
+ ctx, (uintptr_t)req.sig.addr, &sigAddr, req.sig.sz,
WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
- if (ret == WH_ERROR_OK) {
- preOk = 1;
- ret = ops.updateFn(sha3, inAddr, req.input.sz);
- }
- if (ret == WH_ERROR_ACCESS) {
- res.dmaAddrStatus.badAddr = req.input;
- }
- }
- if (preOk) {
- (void)wh_Server_DmaProcessClientAddress(
- ctx, req.input.addr, &inAddr, req.input.sz,
- WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
- }
- if (ret == 0) {
- if (req.isLastBlock) {
- ret = ops.finalFn(sha3, res.hash);
- }
- else {
- if (sha3->i != 0) {
- ret = WH_ERROR_ABORTED;
+ if (ret == 0) {
+ /* Process client message buffer address */
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.msg.addr, &msgAddr, req.msg.sz,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
+
+ if (ret == 0) {
+ /* Verify the signature using appropriate FIPS 204 API */
+ if (preHashType != WC_HASH_TYPE_NONE) {
+ ret = wc_MlDsaKey_VerifyCtxHash(
+ key, sigAddr, req.sig.sz, req_context, (byte)contextSz,
+ msgAddr, req.msg.sz, preHashType, &verified);
+ }
+ else {
+ ret = wc_MlDsaKey_VerifyCtx(
+ key, sigAddr, req.sig.sz, req_context, (byte)contextSz,
+ msgAddr, req.msg.sz, &verified);
+ }
}
- else {
- memcpy(res.resumeState.s, sha3->s, sizeof(res.resumeState.s));
+
+ if (sigAddr != NULL) {
+ /* Post-read processing of signature buffer */
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.sig.addr, &sigAddr, req.sig.sz,
+ WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
+ }
+
+ if (msgAddr != NULL) {
+ /* Post-read processing of message buffer */
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.msg.addr, &msgAddr,
+ req.msg.sz, WH_DMA_OPER_CLIENT_READ_POST,
+ (whServerDmaFlags){0});
}
}
}
- (void)wh_MessageCrypto_TranslateSha3DmaResponse(
- magic, &res, (whMessageCrypto_Sha3DmaResponse*)cryptoDataOut);
- *outSize = sizeof(res);
+ /* Evict key if requested */
+ if (evict) {
+ /* User requested to evict from cache, even if the call failed */
+ _CryptoEvictKeyLocked(ctx, key_id);
+ }
+
+ if (ret == 0) {
+ /* Set verification result */
+ res.verifyResult = verified;
+
+ /* Translate the response */
+ (void)wh_MessageCrypto_TranslateMlDsaVerifyDmaResponse(
+ magic, &res,
+ (whMessageCrypto_MlDsaVerifyDmaResponse*)cryptoDataOut);
+ *outSize = sizeof(res);
+ }
+
+ wc_MlDsaKey_Free(key);
return ret;
+#endif /* WOLFSSL_MLDSA_NO_VERIFY */
}
-#endif /* WOLFSSL_SHA3 */
-#if defined(WOLFSSL_HAVE_MLDSA)
+static int _HandleMlDsaCheckPrivKeyDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize)
+{
+ (void)ctx;
+ (void)magic;
+ (void)devId;
+ (void)cryptoDataIn;
+ (void)inSize;
+ (void)cryptoDataOut;
+ (void)outSize;
+ return WH_ERROR_NOHANDLER;
+}
+#endif /* WOLFSSL_HAVE_MLDSA */
-static int _HandleMlDsaKeyGenDma(whServerContext* ctx, uint16_t magic,
- int devId, const void* cryptoDataIn,
- uint16_t inSize, void* cryptoDataOut,
- uint16_t* outSize)
+#ifdef WOLFSSL_HAVE_SLHDSA
+static int _HandleSlhDsaKeyGenDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize)
{
-#ifdef WOLFSSL_MLDSA_NO_MAKE_KEY
+#ifdef WOLFSSL_SLHDSA_VERIFY_ONLY
(void)ctx;
(void)magic;
(void)devId;
@@ -6894,159 +8041,158 @@ static int _HandleMlDsaKeyGenDma(whServerContext* ctx, uint16_t magic,
(void)outSize;
return WH_ERROR_NOHANDLER;
#else
- int ret = WH_ERROR_OK;
- wc_MlDsaKey key[1];
- void* clientOutAddr = NULL;
- uint16_t keySize = 0;
+ int ret = WH_ERROR_OK;
+ SlhDsaKey key[1];
+ void* clientOutAddr = NULL;
+ void* clientSeedAddr = NULL;
+ uint16_t keySize = 0;
- whMessageCrypto_MlDsaKeyGenDmaRequest req;
- whMessageCrypto_MlDsaKeyGenDmaResponse res;
+ whMessageCrypto_SlhDsaKeyGenDmaRequest req;
+ whMessageCrypto_SlhDsaKeyGenDmaResponse res;
memset(&res, 0, sizeof(res));
- if (inSize < sizeof(whMessageCrypto_MlDsaKeyGenDmaRequest)) {
+ if (inSize < sizeof(whMessageCrypto_SlhDsaKeyGenDmaRequest)) {
return WH_ERROR_BADARGS;
}
- /* Translate the request */
- ret = wh_MessageCrypto_TranslateMlDsaKeyGenDmaRequest(
- magic, (whMessageCrypto_MlDsaKeyGenDmaRequest*)cryptoDataIn, &req);
+ ret = wh_MessageCrypto_TranslateSlhDsaKeyGenDmaRequest(
+ magic, (whMessageCrypto_SlhDsaKeyGenDmaRequest*)cryptoDataIn, &req);
if (ret != WH_ERROR_OK) {
return ret;
}
- /* Check the ML-DSA security level is valid and supported */
- if (0 == _IsMlDsaLevelSupported(req.level)) {
- ret = WH_ERROR_BADARGS;
+ if (0 == _IsSlhDsaParamSupported((int)req.param)) {
+ return WH_ERROR_BADARGS;
}
- else {
- /* init mldsa key */
- ret = wc_MlDsaKey_Init(key, NULL, devId);
- if (ret == 0) {
- /* Set the ML-DSA security level */
- ret = wc_MlDsaKey_SetParams(key, req.level);
+
+ ret = wc_SlhDsaKey_Init(key, (enum SlhDsaParam)req.param, NULL, devId);
+ if (ret == 0) {
+ if (req.seed.sz > 0) {
+ /* The seed is the contiguous SK.seed || SK.prf || PK.seed */
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, req.seed.addr, &clientSeedAddr, req.seed.sz,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
if (ret == 0) {
- /* generate the key */
- ret = wc_MlDsaKey_MakeKey(key, ctx->crypto->rng);
- if (ret == 0) {
- /* Check incoming flags */
- if (req.flags & WH_NVM_FLAGS_EPHEMERAL) {
- /* Must serialize the key into client memory */
- ret = wh_Server_DmaProcessClientAddress(
- ctx, req.key.addr, &clientOutAddr, req.key.sz,
- WH_DMA_OPER_CLIENT_WRITE_PRE,
- (whServerDmaFlags){0});
+ word32 n = (word32)(req.seed.sz / 3);
+ if ((req.seed.sz % 3) != 0) {
+ ret = WH_ERROR_BADARGS;
+ }
+ else {
+ const byte* seed = (const byte*)clientSeedAddr;
+ ret = wc_SlhDsaKey_MakeKeyWithRandom(
+ key, seed, n, seed + n, n, seed + 2 * n, n);
+ }
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, req.seed.addr, &clientSeedAddr, req.seed.sz,
+ WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
+ }
+ }
+ else {
+ ret = wc_SlhDsaKey_MakeKey(key, ctx->crypto->rng);
+ }
- if (ret == 0) {
- ret = wh_Crypto_MlDsaSerializeKeyDer(
- key, req.key.sz, clientOutAddr, &keySize);
- if (ret == 0) {
- res.keyId = WH_KEYID_ERASED;
- res.keySize = keySize;
- }
- }
+ if (ret == 0) {
+ if (req.flags & WH_NVM_FLAGS_EPHEMERAL) {
+ /* Must serialize the key into client memory */
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, req.key.addr, &clientOutAddr, req.key.sz,
+ WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0});
- if (ret == 0) {
- ret = wh_Server_DmaProcessClientAddress(
- ctx, req.key.addr, &clientOutAddr, keySize,
- WH_DMA_OPER_CLIENT_WRITE_POST,
- (whServerDmaFlags){0});
- }
+ if (ret == 0) {
+ ret = wh_Crypto_SlhDsaSerializeKeyDer(
+ key, (uint16_t)req.key.sz, clientOutAddr, &keySize);
+ if (ret == 0) {
+ res.keyId = WH_KEYID_ERASED;
+ res.keySize = keySize;
}
- else {
- /* Must import the key into the cache and return keyid
- */
- whKeyId keyId = wh_KeyId_TranslateFromClient(
- WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+ }
- /* Hold the NVM lock so id allocation and cache import
- * are atomic with respect to other server contexts
- * under THREADSAFE. */
- ret = WH_SERVER_NVM_LOCK(ctx);
- if (ret == WH_ERROR_OK) {
- if (WH_KEYID_ISERASED(keyId)) {
- /* Generate a new id */
- ret =
- wh_Server_KeystoreGetUniqueId(ctx, &keyId);
- WH_DEBUG_SERVER("UniqueId: keyId:%u, ret:%d\n",
- keyId, ret);
- }
- if (ret == WH_ERROR_OK) {
- ret = wh_Server_MlDsaKeyCacheImport(
- ctx, key, keyId, req.flags, req.labelSize,
- req.label);
- WH_DEBUG_SERVER(
- "CacheImport: keyId:%u, ret:%d\n", keyId,
- ret);
- }
- (void)WH_SERVER_NVM_UNLOCK(ctx);
- } /* WH_SERVER_NVM_LOCK() */
-#ifdef WOLFSSL_MLDSA_PUBLIC_KEY
- /* Stream the public key back through the client's DMA
- * buffer so it gets the pubkey without a separate
- * ExportPublicKey call. A freshly generated key must
- * serialize, so treat a failure as fatal: evict the
- * just-committed key and propagate the error rather
- * than returning a keyId with no public key. */
- if (ret == 0) {
- int rc = wh_Server_DmaProcessClientAddress(
- ctx, req.key.addr, &clientOutAddr, req.key.sz,
- WH_DMA_OPER_CLIENT_WRITE_PRE,
- (whServerDmaFlags){0});
- if (rc == 0) {
- int pub_ret = wc_MlDsaKey_PublicKeyToDer(
- key, (byte*)clientOutAddr,
- (word32)req.key.sz, 1);
- if (pub_ret > 0) {
- keySize = (uint16_t)pub_ret;
- }
- else {
- ret = (pub_ret < 0) ? pub_ret
- : WH_ERROR_ABORTED;
- }
- (void)wh_Server_DmaProcessClientAddress(
- ctx, req.key.addr, &clientOutAddr, keySize,
- WH_DMA_OPER_CLIENT_WRITE_POST,
- (whServerDmaFlags){0});
- }
- else {
- ret = rc;
- }
- if (ret != 0) {
- _CryptoEvictKeyLocked(ctx, keyId);
- }
+ if (ret == 0) {
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, req.key.addr, &clientOutAddr, keySize,
+ WH_DMA_OPER_CLIENT_WRITE_POST, (whServerDmaFlags){0});
+ }
+ }
+ else {
+ /* Must import the key into the cache and return keyid */
+ whKeyId keyId = wh_KeyId_TranslateFromClient(
+ WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId);
+
+ /* Hold the NVM lock so id allocation and cache import are
+ * atomic with respect to other server contexts under
+ * THREADSAFE. */
+ ret = WH_SERVER_NVM_LOCK(ctx);
+ if (ret == WH_ERROR_OK) {
+ if (WH_KEYID_ISERASED(keyId)) {
+ ret = wh_Server_KeystoreGetUniqueId(ctx, &keyId);
+ }
+ if (ret == WH_ERROR_OK) {
+ ret = wh_Server_SlhDsaKeyCacheImport(
+ ctx, key, keyId, req.flags, req.labelSize,
+ req.label);
+ }
+ (void)WH_SERVER_NVM_UNLOCK(ctx);
+ } /* WH_SERVER_NVM_LOCK() */
+
+ /* Stream the public key back through the client's DMA buffer
+ * so it gets the pubkey without a separate ExportPublicKey
+ * call. A freshly generated key must serialize, so treat a
+ * failure as fatal: evict the just-committed key and propagate
+ * the error rather than returning a keyId with no public
+ * key. */
+ if (ret == 0) {
+ int rc = wh_Server_DmaProcessClientAddress(
+ ctx, req.key.addr, &clientOutAddr, req.key.sz,
+ WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0});
+ if (rc == 0) {
+ int pub_ret = wc_SlhDsaKey_PublicKeyToDer(
+ key, (byte*)clientOutAddr, (word32)req.key.sz, 1);
+ if (pub_ret > 0) {
+ keySize = (uint16_t)pub_ret;
}
-#endif /* WOLFSSL_MLDSA_PUBLIC_KEY */
- if (ret == 0) {
- res.keyId = wh_KeyId_TranslateToClient(keyId);
- res.keySize = keySize;
+ else {
+ ret = (pub_ret < 0) ? pub_ret : WH_ERROR_ABORTED;
}
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, req.key.addr, &clientOutAddr, keySize,
+ WH_DMA_OPER_CLIENT_WRITE_POST,
+ (whServerDmaFlags){0});
+ }
+ else {
+ ret = rc;
+ }
+ if (ret != 0) {
+ _CryptoEvictKeyLocked(ctx, keyId);
}
}
+ if (ret == 0) {
+ res.keyId = wh_KeyId_TranslateToClient(keyId);
+ res.keySize = keySize;
+ }
}
- wc_MlDsaKey_Free(key);
}
+ wc_SlhDsaKey_Free(key);
}
if (ret == WH_ERROR_ACCESS) {
res.dmaAddrStatus.badAddr = req.key;
}
- /* Translate the response */
- (void)wh_MessageCrypto_TranslateMlDsaKeyGenDmaResponse(
- magic, &res, (whMessageCrypto_MlDsaKeyGenDmaResponse*)cryptoDataOut);
+ (void)wh_MessageCrypto_TranslateSlhDsaKeyGenDmaResponse(
+ magic, &res, (whMessageCrypto_SlhDsaKeyGenDmaResponse*)cryptoDataOut);
*outSize = sizeof(res);
return ret;
-#endif /* WOLFSSL_MLDSA_NO_MAKE_KEY */
+#endif /* WOLFSSL_SLHDSA_VERIFY_ONLY */
}
-static int _HandleMlDsaSignDma(whServerContext* ctx, uint16_t magic, int devId,
- const void* cryptoDataIn, uint16_t inSize,
- void* cryptoDataOut, uint16_t* outSize)
+static int _HandleSlhDsaSignDma(whServerContext* ctx, uint16_t magic, int devId,
+ const void* cryptoDataIn, uint16_t inSize,
+ void* cryptoDataOut, uint16_t* outSize)
{
-#ifdef WOLFSSL_MLDSA_NO_SIGN
+#ifdef WOLFSSL_SLHDSA_VERIFY_ONLY
(void)ctx;
(void)magic;
(void)devId;
@@ -7056,282 +8202,230 @@ static int _HandleMlDsaSignDma(whServerContext* ctx, uint16_t magic, int devId,
(void)outSize;
return WH_ERROR_NOHANDLER;
#else
- int ret = 0;
- wc_MlDsaKey key[1];
- void* msgAddr = NULL;
- void* sigAddr = NULL;
- word32 sigLen = 0;
+ int ret = 0;
+ SlhDsaKey key[1];
+ void* msgAddr = NULL;
+ void* sigAddr = NULL;
+ word32 sigLen = 0;
+ whKeyId key_id;
+ int evict;
+ uint32_t contextSz;
+ uint32_t preHashType;
+ uint32_t addRndSz;
+ byte* req_context = NULL;
+ byte* req_addRnd = NULL;
+ uint32_t inline_len;
+
+ whMessageCrypto_SlhDsaSignDmaRequest req;
+ whMessageCrypto_SlhDsaSignDmaResponse res;
- whMessageCrypto_MlDsaSignDmaRequest req;
- whMessageCrypto_MlDsaSignDmaResponse res;
+ memset(&res, 0, sizeof(res));
- if (inSize < sizeof(whMessageCrypto_MlDsaSignDmaRequest)) {
+ if (inSize < sizeof(whMessageCrypto_SlhDsaSignDmaRequest)) {
return WH_ERROR_BADARGS;
}
- /* Translate the request */
- ret = wh_MessageCrypto_TranslateMlDsaSignDmaRequest(
- magic, (whMessageCrypto_MlDsaSignDmaRequest*)cryptoDataIn, &req);
+ ret = wh_MessageCrypto_TranslateSlhDsaSignDmaRequest(
+ magic, (whMessageCrypto_SlhDsaSignDmaRequest*)cryptoDataIn, &req);
if (ret != WH_ERROR_OK) {
return ret;
}
- /* Transaction state */
- whKeyId key_id;
- int evict = 0;
-
-
- /* Get key ID and evict flag */
key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
ctx->comm->client_id, req.keyId);
- evict = !!(req.options & WH_MESSAGE_CRYPTO_MLDSA_SIGN_OPTIONS_EVICT);
+ evict = !!(req.options & WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_EVICT);
- /* Extract context from inline data after the struct */
- uint32_t contextSz = req.contextSz;
- uint32_t preHashType = req.preHashType;
- byte* req_context = NULL;
- if (contextSz > WH_CRYPTO_MLDSA_MAX_CTX_LEN) {
+ contextSz = req.contextSz;
+ preHashType = req.preHashType;
+ addRndSz = req.addRndSz;
+ if (contextSz > WH_CRYPTO_SLHDSA_MAX_CTX_LEN) {
+ return WH_ERROR_BADARGS;
+ }
+ inline_len = inSize - sizeof(whMessageCrypto_SlhDsaSignDmaRequest);
+ if ((contextSz > inline_len) || (addRndSz > (inline_len - contextSz))) {
return WH_ERROR_BADARGS;
}
if (contextSz > 0) {
- if (inSize < sizeof(whMessageCrypto_MlDsaSignDmaRequest) + contextSz) {
- return WH_ERROR_BADARGS;
- }
req_context = (uint8_t*)(cryptoDataIn) +
- sizeof(whMessageCrypto_MlDsaSignDmaRequest);
+ sizeof(whMessageCrypto_SlhDsaSignDmaRequest);
+ }
+ if (addRndSz > 0) {
+ req_addRnd = (uint8_t*)(cryptoDataIn) +
+ sizeof(whMessageCrypto_SlhDsaSignDmaRequest) + contextSz;
}
- /* Initialize key */
- ret = wc_MlDsaKey_Init(key, NULL, devId);
+ ret = _SlhDsaInitForCachedKey(key, req.param, devId);
if (ret == 0) {
- /* Export key from cache */
- /* TODO: sanity check security level against key pulled from cache? */
- /* Export the key, enforcing the sign usage policy against the same
- * locked snapshot that is exported. The non-DMA sign handler enforces
- * the same policy. */
- ret = _MlDsaKeyCacheExportEnforce(ctx, key_id, WH_NVM_FLAGS_USAGE_SIGN,
- key);
+ ret = _SlhDsaLoadKey(ctx, key_id, WH_NVM_FLAGS_USAGE_SIGN, key);
if (ret == 0) {
- /* Process client message buffer address */
ret = wh_Server_DmaProcessClientAddress(
ctx, (uintptr_t)req.msg.addr, &msgAddr, req.msg.sz,
WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
+ if (ret == WH_ERROR_ACCESS) {
+ res.dmaAddrStatus.badAddr = req.msg;
+ }
if (ret == 0) {
- /* Process client signature buffer address */
ret = wh_Server_DmaProcessClientAddress(
ctx, (uintptr_t)req.sig.addr, &sigAddr, req.sig.sz,
WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0});
+ if (ret == WH_ERROR_ACCESS) {
+ res.dmaAddrStatus.badAddr = req.sig;
+ }
if (ret == 0) {
- /* Sign the message using appropriate FIPS 204 API */
- sigLen = req.sig.sz;
- if (preHashType != WC_HASH_TYPE_NONE) {
- ret = wc_MlDsaKey_SignCtxHash(
- key, req_context, (byte)contextSz,
- sigAddr, &sigLen, msgAddr, req.msg.sz,
- preHashType, ctx->crypto->rng);
- }
- else {
- ret = wc_MlDsaKey_SignCtx(
- key, req_context, (byte)contextSz,
- sigAddr, &sigLen, msgAddr, req.msg.sz,
- ctx->crypto->rng);
- }
+ sigLen = (word32)req.sig.sz;
+ ret = _SlhDsaSignDispatch(
+ ctx, key, req.options, (const byte*)msgAddr,
+ (word32)req.msg.sz, req_context, contextSz,
+ preHashType, req_addRnd, addRndSz, (byte*)sigAddr,
+ &sigLen);
}
if (sigAddr != NULL) {
- /* Post-write processing of signature buffer */
(void)wh_Server_DmaProcessClientAddress(
ctx, (uintptr_t)req.sig.addr, &sigAddr, sigLen,
- WH_DMA_OPER_CLIENT_WRITE_POST,
- (whServerDmaFlags){0});
+ WH_DMA_OPER_CLIENT_WRITE_POST, (whServerDmaFlags){0});
}
if (msgAddr != NULL) {
- /* Post-read processing of message buffer */
(void)wh_Server_DmaProcessClientAddress(
- ctx, (uintptr_t)req.msg.addr, &msgAddr,
- req.msg.sz, WH_DMA_OPER_CLIENT_READ_POST,
- (whServerDmaFlags){0});
+ ctx, (uintptr_t)req.msg.addr, &msgAddr, req.msg.sz,
+ WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
}
}
}
- wc_MlDsaKey_Free(key);
+ wc_SlhDsaKey_Free(key);
}
- /* Evict key if requested */
if (evict) {
/* User requested to evict from cache, even if the call failed */
_CryptoEvictKeyLocked(ctx, key_id);
}
if (ret == 0) {
- /* Set response signature length */
res.sigLen = sigLen;
- *outSize = sizeof(res);
-
- /* Translate the response */
- (void)wh_MessageCrypto_TranslateMlDsaSignDmaResponse(
- magic, &res, (whMessageCrypto_MlDsaSignDmaResponse*)cryptoDataOut);
}
+ (void)wh_MessageCrypto_TranslateSlhDsaSignDmaResponse(
+ magic, &res, (whMessageCrypto_SlhDsaSignDmaResponse*)cryptoDataOut);
+
+ *outSize = sizeof(res);
+
return ret;
-#endif /* WOLFSSL_MLDSA_NO_SIGN */
+#endif /* WOLFSSL_SLHDSA_VERIFY_ONLY */
}
-static int _HandleMlDsaVerifyDma(whServerContext* ctx, uint16_t magic,
- int devId, const void* cryptoDataIn,
- uint16_t inSize, void* cryptoDataOut,
- uint16_t* outSize)
+static int _HandleSlhDsaVerifyDma(whServerContext* ctx, uint16_t magic,
+ int devId, const void* cryptoDataIn,
+ uint16_t inSize, void* cryptoDataOut,
+ uint16_t* outSize)
{
-#ifdef WOLFSSL_MLDSA_NO_VERIFY
- (void)ctx;
- (void)magic;
- (void)devId;
- (void)cryptoDataIn;
- (void)inSize;
- (void)cryptoDataOut;
- (void)outSize;
- return WH_ERROR_NOHANDLER;
-#else
- int ret = 0;
- wc_MlDsaKey key[1];
- void* msgAddr = NULL;
- void* sigAddr = NULL;
- int verified = 0;
+ int ret = 0;
+ SlhDsaKey key[1];
+ void* msgAddr = NULL;
+ void* sigAddr = NULL;
+ whKeyId key_id;
+ int evict;
+ int result = 0;
+ uint32_t contextSz;
+ uint32_t preHashType;
+ byte* req_context = NULL;
+
+ whMessageCrypto_SlhDsaVerifyDmaRequest req;
+ whMessageCrypto_SlhDsaVerifyDmaResponse res;
- whMessageCrypto_MlDsaVerifyDmaRequest req;
- whMessageCrypto_MlDsaVerifyDmaResponse res;
+ memset(&res, 0, sizeof(res));
- if (inSize < sizeof(whMessageCrypto_MlDsaVerifyDmaRequest)) {
+ if (inSize < sizeof(whMessageCrypto_SlhDsaVerifyDmaRequest)) {
return WH_ERROR_BADARGS;
}
- /* Translate the request */
- ret = wh_MessageCrypto_TranslateMlDsaVerifyDmaRequest(
- magic, (whMessageCrypto_MlDsaVerifyDmaRequest*)cryptoDataIn, &req);
+ ret = wh_MessageCrypto_TranslateSlhDsaVerifyDmaRequest(
+ magic, (whMessageCrypto_SlhDsaVerifyDmaRequest*)cryptoDataIn, &req);
if (ret != WH_ERROR_OK) {
return ret;
}
- /* Transaction state */
- whKeyId key_id;
- int evict = 0;
-
- /* Get key ID and evict flag */
key_id = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO,
ctx->comm->client_id, req.keyId);
- evict = !!(req.options & WH_MESSAGE_CRYPTO_MLDSA_VERIFY_OPTIONS_EVICT);
+ evict = !!(req.options & WH_MESSAGE_CRYPTO_SLHDSA_VERIFY_OPTIONS_EVICT);
- /* Extract context from inline data after the struct */
- uint32_t contextSz = req.contextSz;
- uint32_t preHashType = req.preHashType;
- byte* req_context = NULL;
- if (contextSz > WH_CRYPTO_MLDSA_MAX_CTX_LEN) {
+ contextSz = req.contextSz;
+ preHashType = req.preHashType;
+ if (contextSz > WH_CRYPTO_SLHDSA_MAX_CTX_LEN) {
return WH_ERROR_BADARGS;
}
if (contextSz > 0) {
- if (inSize < sizeof(whMessageCrypto_MlDsaVerifyDmaRequest) + contextSz) {
+ if (inSize <
+ sizeof(whMessageCrypto_SlhDsaVerifyDmaRequest) + contextSz) {
return WH_ERROR_BADARGS;
}
req_context = (uint8_t*)(cryptoDataIn) +
- sizeof(whMessageCrypto_MlDsaVerifyDmaRequest);
- }
-
- /* Initialize key */
- ret = wc_MlDsaKey_Init(key, NULL, devId);
- if (ret != 0) {
- return ret;
+ sizeof(whMessageCrypto_SlhDsaVerifyDmaRequest);
}
- /* Export the key, enforcing the verify usage policy against the same
- * locked snapshot that is exported. The non-DMA verify handler enforces
- * the same policy. */
- ret = _MlDsaKeyCacheExportEnforce(ctx, key_id, WH_NVM_FLAGS_USAGE_VERIFY,
- key);
+ ret = _SlhDsaInitForCachedKey(key, req.param, devId);
if (ret == 0) {
- /* Process client signature buffer address */
- ret = wh_Server_DmaProcessClientAddress(
- ctx, (uintptr_t)req.sig.addr, &sigAddr, req.sig.sz,
- WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
-
+ ret = _SlhDsaLoadKey(ctx, key_id, WH_NVM_FLAGS_USAGE_VERIFY, key);
if (ret == 0) {
- /* Process client message buffer address */
ret = wh_Server_DmaProcessClientAddress(
- ctx, (uintptr_t)req.msg.addr, &msgAddr, req.msg.sz,
+ ctx, (uintptr_t)req.sig.addr, &sigAddr, req.sig.sz,
WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
+ if (ret == WH_ERROR_ACCESS) {
+ res.dmaAddrStatus.badAddr = req.sig;
+ }
if (ret == 0) {
- /* Verify the signature using appropriate FIPS 204 API */
- if (preHashType != WC_HASH_TYPE_NONE) {
- ret = wc_MlDsaKey_VerifyCtxHash(
- key, sigAddr, req.sig.sz, req_context, (byte)contextSz,
- msgAddr, req.msg.sz, preHashType, &verified);
+ ret = wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.msg.addr, &msgAddr, req.msg.sz,
+ WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
+ if (ret == WH_ERROR_ACCESS) {
+ res.dmaAddrStatus.badAddr = req.msg;
}
- else {
- ret = wc_MlDsaKey_VerifyCtx(
- key, sigAddr, req.sig.sz, req_context, (byte)contextSz,
- msgAddr, req.msg.sz, &verified);
+
+ if (ret == 0) {
+ ret = _SlhDsaVerifyDispatch(
+ key, req.options, (const byte*)sigAddr,
+ (word32)req.sig.sz, (const byte*)msgAddr,
+ (word32)req.msg.sz, req_context, contextSz,
+ preHashType, &result);
}
- }
+ if (msgAddr != NULL) {
+ (void)wh_Server_DmaProcessClientAddress(
+ ctx, (uintptr_t)req.msg.addr, &msgAddr, req.msg.sz,
+ WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
+ }
+ }
if (sigAddr != NULL) {
- /* Post-read processing of signature buffer */
(void)wh_Server_DmaProcessClientAddress(
ctx, (uintptr_t)req.sig.addr, &sigAddr, req.sig.sz,
WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
}
-
- if (msgAddr != NULL) {
- /* Post-read processing of message buffer */
- (void)wh_Server_DmaProcessClientAddress(
- ctx, (uintptr_t)req.msg.addr, &msgAddr,
- req.msg.sz, WH_DMA_OPER_CLIENT_READ_POST,
- (whServerDmaFlags){0});
- }
}
+ wc_SlhDsaKey_Free(key);
}
- /* Evict key if requested */
if (evict) {
/* User requested to evict from cache, even if the call failed */
_CryptoEvictKeyLocked(ctx, key_id);
}
if (ret == 0) {
- /* Set verification result */
- res.verifyResult = verified;
+ res.verifyResult = result;
+ }
- /* Translate the response */
- (void)wh_MessageCrypto_TranslateMlDsaVerifyDmaResponse(
- magic, &res,
- (whMessageCrypto_MlDsaVerifyDmaResponse*)cryptoDataOut);
+ (void)wh_MessageCrypto_TranslateSlhDsaVerifyDmaResponse(
+ magic, &res, (whMessageCrypto_SlhDsaVerifyDmaResponse*)cryptoDataOut);
- *outSize = sizeof(res);
- }
+ *outSize = sizeof(res);
- wc_MlDsaKey_Free(key);
return ret;
-#endif /* WOLFSSL_MLDSA_NO_VERIFY */
-}
-
-static int _HandleMlDsaCheckPrivKeyDma(whServerContext* ctx, uint16_t magic,
- int devId, const void* cryptoDataIn,
- uint16_t inSize, void* cryptoDataOut,
- uint16_t* outSize)
-{
- (void)ctx;
- (void)magic;
- (void)devId;
- (void)cryptoDataIn;
- (void)inSize;
- (void)cryptoDataOut;
- (void)outSize;
- return WH_ERROR_NOHANDLER;
}
-#endif /* WOLFSSL_HAVE_MLDSA */
+#endif /* WOLFSSL_HAVE_SLHDSA */
-#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON)
+#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON) || \
+ defined(WOLFSSL_HAVE_SLHDSA)
static int _HandlePqcSigAlgorithmDma(whServerContext* ctx, uint16_t magic,
int devId, const void* cryptoDataIn,
uint16_t cryptoInSize, void* cryptoDataOut,
@@ -7372,6 +8466,40 @@ static int _HandlePqcSigAlgorithmDma(whServerContext* ctx, uint16_t magic,
}
} break;
#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+ case WC_PQC_SIG_TYPE_SLHDSA: {
+ switch (pkAlgoType) {
+ case WC_PK_TYPE_PQC_SIG_KEYGEN:
+ case WC_PK_TYPE_PQC_SIG_KEYGEN_SEEDED:
+ ret = _HandleSlhDsaKeyGenDma(ctx, magic, devId,
+ cryptoDataIn, cryptoInSize,
+ cryptoDataOut, cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_SIG_SIGN:
+ case WC_PK_TYPE_PQC_SIG_SIGN_MSG:
+ ret = _HandleSlhDsaSignDma(ctx, magic, devId, cryptoDataIn,
+ cryptoInSize, cryptoDataOut,
+ cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_SIG_VERIFY:
+ case WC_PK_TYPE_PQC_SIG_VERIFY_MSG:
+ ret = _HandleSlhDsaVerifyDma(ctx, magic, devId,
+ cryptoDataIn, cryptoInSize,
+ cryptoDataOut, cryptoOutSize);
+ break;
+ case WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY:
+ /* The public key is 2n bytes, so the comm-buffer handler
+ * carries it and there is nothing for DMA to move. */
+ ret = _HandleSlhDsaCheckPrivKey(
+ ctx, magic, devId, cryptoDataIn, cryptoInSize,
+ cryptoDataOut, cryptoOutSize);
+ break;
+ default:
+ ret = WH_ERROR_NOHANDLER;
+ break;
+ }
+ } break;
+#endif /* WOLFSSL_HAVE_SLHDSA */
default:
ret = WH_ERROR_NOHANDLER;
break;
@@ -9344,17 +10472,23 @@ int wh_Server_HandleCryptoDmaRequest(whServerContext* ctx, uint16_t magic,
case WC_ALGO_TYPE_PK:
switch (rqstHeader.algoType) {
-#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON)
+#if defined(WOLFSSL_HAVE_MLDSA) || defined(HAVE_FALCON) || \
+ defined(WOLFSSL_HAVE_SLHDSA)
case WC_PK_TYPE_PQC_SIG_KEYGEN:
case WC_PK_TYPE_PQC_SIG_SIGN:
case WC_PK_TYPE_PQC_SIG_VERIFY:
case WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY:
+#ifdef WOLFSSL_HAVE_SLHDSA
+ case WC_PK_TYPE_PQC_SIG_KEYGEN_SEEDED:
+ case WC_PK_TYPE_PQC_SIG_SIGN_MSG:
+ case WC_PK_TYPE_PQC_SIG_VERIFY_MSG:
+#endif
ret = _HandlePqcSigAlgorithmDma(
ctx, magic, devId, cryptoDataIn, cryptoInSize,
cryptoDataOut, &cryptoOutSize, rqstHeader.algoType,
rqstHeader.algoSubType);
break;
-#endif /* WOLFSSL_HAVE_MLDSA || HAVE_FALCON */
+#endif /* WOLFSSL_HAVE_MLDSA || HAVE_FALCON || WOLFSSL_HAVE_SLHDSA */
#if defined(WOLFSSL_HAVE_MLKEM)
case WC_PK_TYPE_PQC_KEM_KEYGEN:
case WC_PK_TYPE_PQC_KEM_ENCAPS:
diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c
index 94512ef1f..9139efc7a 100644
--- a/src/wh_server_keystore.c
+++ b/src/wh_server_keystore.c
@@ -581,6 +581,35 @@ static int _ExportMldsaPublicKey(whServerContext* server, whKeyId keyId,
}
#endif
+#ifdef WOLFSSL_HAVE_SLHDSA
+static int _ExportSlhDsaPublicKey(whServerContext* server, whKeyId keyId,
+ uint8_t* out, uint16_t* outSz)
+{
+ int ret = WH_ERROR_OK;
+ SlhDsaKey key[1];
+ int pub_ret;
+ int devId = (server->crypto != NULL) ? server->devId
+ : INVALID_DEVID;
+
+ /* The decoder detects the real parameter set from the key OID */
+ ret = wc_SlhDsaKey_Init(key, WC_SLHDSA_DEFAULT_PARAM, NULL, devId);
+ if (ret == 0) {
+ ret = wh_Server_SlhDsaKeyCacheExport(server, keyId, key);
+ if (ret == 0) {
+ pub_ret = wc_SlhDsaKey_PublicKeyToDer(key, out, (word32)*outSz, 1);
+ if (pub_ret > 0) {
+ *outSz = (uint16_t)pub_ret;
+ }
+ else {
+ ret = (pub_ret == 0) ? WH_ERROR_ABORTED : pub_ret;
+ }
+ }
+ wc_SlhDsaKey_Free(key);
+ }
+ return ret;
+}
+#endif
+
#ifdef HAVE_CURVE25519
static int _ExportCurve25519PublicKey(whServerContext* server, whKeyId keyId,
uint8_t* out, uint16_t* outSz)
@@ -2917,6 +2946,12 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
stage, &stageMax);
break;
#endif /* WOLFSSL_HAVE_MLDSA && WOLFSSL_MLDSA_PUBLIC_KEY */
+ #ifdef WOLFSSL_HAVE_SLHDSA
+ case WH_KEY_ALGO_SLHDSA:
+ ret = _ExportSlhDsaPublicKey(server, serverKeyId,
+ stage, &stageMax);
+ break;
+ #endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef HAVE_CURVE25519
case WH_KEY_ALGO_CURVE25519:
ret = _ExportCurve25519PublicKey(server, serverKeyId,
@@ -3132,6 +3167,12 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic,
out, &max_der);
break;
#endif /* WOLFSSL_HAVE_MLDSA && WOLFSSL_MLDSA_PUBLIC_KEY */
+ #ifdef WOLFSSL_HAVE_SLHDSA
+ case WH_KEY_ALGO_SLHDSA:
+ ret = _ExportSlhDsaPublicKey(server, serverKeyId,
+ out, &max_der);
+ break;
+ #endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef HAVE_CURVE25519
case WH_KEY_ALGO_CURVE25519:
ret = _ExportCurve25519PublicKey(server,
diff --git a/test-refactor/client-server/wh_test_crypto_slhdsa.c b/test-refactor/client-server/wh_test_crypto_slhdsa.c
new file mode 100644
index 000000000..e6308d93d
--- /dev/null
+++ b/test-refactor/client-server/wh_test_crypto_slhdsa.c
@@ -0,0 +1,1002 @@
+/*
+ * Copyright (C) 2026 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with wolfHSM. If not, see .
+ */
+/*
+ * test-refactor/client-server/wh_test_crypto_slhdsa.c
+ *
+ * SLH-DSA tests routed through the server.
+ *
+ * Parameter sets are chosen around the comm buffer: an SLH-DSA signature is
+ * 7856 bytes at the smallest parameter set and 49856 at the largest, so the
+ * comm-buffer path only works for 128s and everything else needs DMA. The
+ * 'f' variant signs roughly twenty times faster, so the DMA tests use it.
+ */
+
+#include "wolfhsm/wh_settings.h"
+
+#if !defined(WOLFHSM_CFG_NO_CRYPTO)
+
+#include
+#include
+
+#include "wolfssl/wolfcrypt/settings.h"
+#include "wolfssl/wolfcrypt/types.h"
+#include "wolfssl/wolfcrypt/random.h"
+#include "wolfssl/wolfcrypt/wc_slhdsa.h"
+#include "wolfssl/wolfcrypt/error-crypt.h"
+
+#include "wolfhsm/wh_error.h"
+#include "wolfhsm/wh_common.h"
+#include "wolfhsm/wh_client.h"
+#include "wolfhsm/wh_client_crypto.h"
+
+#include "wh_test_common.h"
+#include "wh_test_list.h"
+
+#ifdef WOLFSSL_HAVE_SLHDSA
+
+#if defined(WOLFSSL_SLHDSA_PARAM_128S) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
+#define WH_TEST_SLHDSA_COMM_PARAM SLHDSA_SHAKE128S
+#define WH_TEST_SLHDSA_COMM_SIG_LEN WC_SLHDSA_SHAKE128S_SIG_LEN
+#endif
+
+#if defined(WOLFSSL_SLHDSA_PARAM_128F) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
+#define WH_TEST_SLHDSA_FAST_PARAM SLHDSA_SHAKE128F
+#define WH_TEST_SLHDSA_FAST_SIG_LEN WC_SLHDSA_SHAKE128F_SIG_LEN
+#endif
+
+#ifdef WH_TEST_SLHDSA_COMM_PARAM
+
+/* Drives the crypto callback through the plain wolfCrypt API, which is how an
+ * application reaches the HSM. The key is ephemeral so the whole generate,
+ * sign and verify chain crosses the wire. */
+static int _whTest_SlhDsaWolfCryptImpl(whClientContext* ctx, int devId)
+{
+ int ret = 0;
+ int verified = 0;
+ SlhDsaKey key[1];
+ WC_RNG rng[1];
+ byte msg[] = "Test message for SLH-DSA signing";
+ byte sig[WH_TEST_SLHDSA_COMM_SIG_LEN];
+ word32 sigSz = sizeof(sig);
+
+ ret = wc_InitRng_ex(rng, NULL, WH_CLIENT_DEVID(ctx));
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to wc_InitRng_ex %d\n", ret);
+ return ret;
+ }
+
+ ret = wc_SlhDsaKey_Init(key, WH_TEST_SLHDSA_COMM_PARAM, NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to initialize SLH-DSA key: %d\n", ret);
+ (void)wc_FreeRng(rng);
+ return ret;
+ }
+
+ if (ret == 0) {
+ ret = wc_SlhDsaKey_MakeKey(key, rng);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to generate SLH-DSA key: %d\n", ret);
+ }
+ }
+ if (ret == 0) {
+ ret = wc_SlhDsaKey_Sign(key, NULL, 0, msg, sizeof(msg), sig, &sigSz,
+ rng);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to sign with SLH-DSA: %d\n", ret);
+ }
+ }
+ if (ret == 0) {
+ ret = wc_SlhDsaKey_Verify(key, NULL, 0, msg, sizeof(msg), sig, sigSz);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to verify SLH-DSA signature: %d\n", ret);
+ }
+ else {
+ verified = 1;
+ }
+ }
+ /* Tamper check: a corrupted signature must not verify. */
+ if ((ret == 0) && verified) {
+ sig[0] ^= 1;
+ ret = wc_SlhDsaKey_Verify(key, NULL, 0, msg, sizeof(msg), sig, sigSz);
+ if (ret == 0) {
+ WH_ERROR_PRINT("SLH-DSA verified a tampered signature\n");
+ ret = -1;
+ }
+ else {
+ ret = 0;
+ }
+ }
+
+ if (ret == 0) {
+ WH_TEST_PRINT("SLH-DSA WOLFCRYPT DEVID=0x%X SUCCESS\n", devId);
+ }
+
+ wc_SlhDsaKey_Free(key);
+ (void)wc_FreeRng(rng);
+ return ret;
+}
+
+/* Ephemeral generate plus the pure, context and pre-hash signing shapes over
+ * the comm buffer. */
+static int _whTest_CryptoSlhDsaClient(whClientContext* ctx)
+{
+ int devId = WH_CLIENT_DEVID(ctx);
+ int ret;
+ SlhDsaKey key[1];
+
+ ret = wc_SlhDsaKey_Init(key, WH_TEST_SLHDSA_COMM_PARAM, NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to initialize SLH-DSA key: %d\n", ret);
+ return ret;
+ }
+
+ ret = wh_Client_SlhDsaMakeExportKey(ctx, WH_TEST_SLHDSA_COMM_PARAM, key);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to generate SLH-DSA key: %d\n", ret);
+ goto done;
+ }
+
+ {
+ byte msg[] = "Test message for non-DMA SLH-DSA";
+ byte sig[WH_TEST_SLHDSA_COMM_SIG_LEN];
+ word32 sigLen = sizeof(sig);
+ int verified = 0;
+
+ ret = wh_Client_SlhDsaSign(ctx, msg, sizeof(msg), sig, &sigLen, key,
+ NULL, 0, WC_HASH_TYPE_NONE, NULL, 0, 1, 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to sign using SLH-DSA non-DMA: %d\n", ret);
+ goto done;
+ }
+ if (sigLen != WH_TEST_SLHDSA_COMM_SIG_LEN) {
+ WH_ERROR_PRINT("SLH-DSA signature length %u, expected %u\n",
+ (unsigned)sigLen,
+ (unsigned)WH_TEST_SLHDSA_COMM_SIG_LEN);
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+
+ ret = wh_Client_SlhDsaVerify(ctx, sig, sigLen, msg, sizeof(msg),
+ &verified, key, NULL, 0,
+ WC_HASH_TYPE_NONE, 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to verify SLH-DSA non-DMA: %d\n", ret);
+ goto done;
+ }
+ if (!verified) {
+ WH_ERROR_PRINT("SLH-DSA non-DMA verification failed\n");
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+
+ /* A tampered signature must come back as a result, not an error */
+ sig[0] ^= 0xFF;
+ ret = wh_Client_SlhDsaVerify(ctx, sig, sigLen, msg, sizeof(msg),
+ &verified, key, NULL, 0,
+ WC_HASH_TYPE_NONE, 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Verify with modified sig returned %d\n", ret);
+ goto done;
+ }
+ if (verified) {
+ WH_ERROR_PRINT("SLH-DSA non-DMA verified a bad signature\n");
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+ }
+
+ /* FIPS 205 context string */
+ {
+ byte msg[] = "Context test message non-DMA";
+ const byte context[] = {'w', 'o', 'l', 'f', 'H', 'S', 'M'};
+ byte sig[WH_TEST_SLHDSA_COMM_SIG_LEN];
+ word32 sigLen = sizeof(sig);
+ int verified = 0;
+
+ ret = wh_Client_SlhDsaSign(ctx, msg, sizeof(msg), sig, &sigLen, key,
+ context, (byte)sizeof(context),
+ WC_HASH_TYPE_NONE, NULL, 0, 1, 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to sign with context: %d\n", ret);
+ goto done;
+ }
+
+ ret = wh_Client_SlhDsaVerify(ctx, sig, sigLen, msg, sizeof(msg),
+ &verified, key, context,
+ (byte)sizeof(context), WC_HASH_TYPE_NONE,
+ 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to verify with context: %d\n", ret);
+ goto done;
+ }
+ if (!verified) {
+ WH_ERROR_PRINT("SLH-DSA context verification failed\n");
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+
+ /* The context is bound into the signature, so a different one fails */
+ ret = wh_Client_SlhDsaVerify(ctx, sig, sigLen, msg, sizeof(msg),
+ &verified, key, NULL, 0,
+ WC_HASH_TYPE_NONE, 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Verify with dropped context returned %d\n", ret);
+ goto done;
+ }
+ if (verified) {
+ WH_ERROR_PRINT("SLH-DSA verified across differing contexts\n");
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+ }
+
+#ifndef NO_SHA256
+ /* HashSLH-DSA over a caller-supplied digest */
+ {
+ byte digest[WC_SHA256_DIGEST_SIZE];
+ byte sig[WH_TEST_SLHDSA_COMM_SIG_LEN];
+ word32 sigLen = sizeof(sig);
+ int verified = 0;
+
+ memset(digest, 0x5A, sizeof(digest));
+
+ ret = wh_Client_SlhDsaSign(ctx, digest, sizeof(digest), sig, &sigLen,
+ key, NULL, 0, WC_HASH_TYPE_SHA256, NULL, 0,
+ 1, 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to pre-hash sign: %d\n", ret);
+ goto done;
+ }
+
+ ret = wh_Client_SlhDsaVerify(ctx, sig, sigLen, digest, sizeof(digest),
+ &verified, key, NULL, 0,
+ WC_HASH_TYPE_SHA256, 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to pre-hash verify: %d\n", ret);
+ goto done;
+ }
+ if (!verified) {
+ WH_ERROR_PRINT("SLH-DSA pre-hash verification failed\n");
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+ }
+#endif /* !NO_SHA256 */
+
+ WH_TEST_PRINT("SLH-DSA NON-DMA DEVID=0x%X SUCCESS\n", devId);
+ ret = 0;
+
+done:
+ wc_SlhDsaKey_Free(key);
+ return ret;
+}
+
+/* Generate a key that stays on the server and use it purely by key ID. This is
+ * the case that has no local key material at all, so it also covers the
+ * deterministic signing path where the randomizer has to be derived from the
+ * server's copy of the key. */
+static int _whTest_CryptoSlhDsaCachedKey(whClientContext* ctx)
+{
+ int devId = WH_CLIENT_DEVID(ctx);
+ int ret;
+ whKeyId keyId = WH_KEYID_ERASED;
+ SlhDsaKey pub[1];
+ SlhDsaKey handle[1];
+ int pubInit = 0;
+ int handleInit = 0;
+ uint8_t label[] = "SlhDsaCached";
+
+ ret = wc_SlhDsaKey_Init(pub, WH_TEST_SLHDSA_COMM_PARAM, NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to init SLH-DSA pub key: %d\n", ret);
+ return ret;
+ }
+ pubInit = 1;
+
+ ret = wh_Client_SlhDsaMakeCacheKeyAndExportPublic(
+ ctx, WH_TEST_SLHDSA_COMM_PARAM, &keyId,
+ WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY, sizeof(label),
+ label, pub);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to cache SLH-DSA key: %d\n", ret);
+ goto done;
+ }
+
+ /* A bare handle: no key material, only the server key ID */
+ ret = wc_SlhDsaKey_Init(handle, WH_TEST_SLHDSA_COMM_PARAM, NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to init SLH-DSA handle: %d\n", ret);
+ goto done;
+ }
+ handleInit = 1;
+
+ ret = wh_Client_SlhDsaSetKeyId(handle, keyId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to set SLH-DSA key id: %d\n", ret);
+ goto done;
+ }
+
+ {
+ byte msg[] = "Signed by a key that never left the HSM";
+ byte sig[WH_TEST_SLHDSA_COMM_SIG_LEN];
+ word32 sigLen = sizeof(sig);
+
+ /* Deterministic: wolfCrypt derives the randomizer from the key's own
+ * PK.seed, which only the server has. */
+ ret = wc_SlhDsaKey_SignDeterministic(handle, NULL, 0, msg, sizeof(msg),
+ sig, &sigLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to sign with a cached SLH-DSA key: %d\n",
+ ret);
+ goto done;
+ }
+
+ /* The exported public key verifies it, through the server */
+ ret = wc_SlhDsaKey_Verify(pub, NULL, 0, msg, sizeof(msg), sig, sigLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Cached-key signature did not verify: %d\n", ret);
+ goto done;
+ }
+
+ /* Signing the same message twice deterministically must repeat */
+ {
+ byte sig2[WH_TEST_SLHDSA_COMM_SIG_LEN];
+ word32 sig2Len = sizeof(sig2);
+
+ ret = wc_SlhDsaKey_SignDeterministic(handle, NULL, 0, msg,
+ sizeof(msg), sig2, &sig2Len);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Second deterministic sign failed: %d\n", ret);
+ goto done;
+ }
+ if ((sig2Len != sigLen) || (memcmp(sig, sig2, sigLen) != 0)) {
+ WH_ERROR_PRINT("Deterministic SLH-DSA signatures differ\n");
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+ }
+ }
+
+ WH_TEST_PRINT("SLH-DSA CACHED KEY DEVID=0x%X SUCCESS\n", devId);
+ ret = 0;
+
+done:
+ if (!WH_KEYID_ISERASED(keyId)) {
+ (void)wh_Client_KeyEvict(ctx, keyId);
+ }
+ if (handleInit) {
+ wc_SlhDsaKey_Free(handle);
+ }
+ if (pubInit) {
+ wc_SlhDsaKey_Free(pub);
+ }
+ return ret;
+}
+
+/* FIPS 205 internal interface: the caller builds M' and the server signs it
+ * directly. */
+static int _whTest_CryptoSlhDsaMPrime(whClientContext* ctx)
+{
+ int devId = WH_CLIENT_DEVID(ctx);
+ int ret;
+ SlhDsaKey key[1];
+ /* M' for a pure signature with an empty context: 0x00 || ctxSz || msg */
+ byte mprime[] = {0x00, 0x00, 'm', 'p', 'r', 'i', 'm', 'e'};
+ byte sig[WH_TEST_SLHDSA_COMM_SIG_LEN];
+ word32 sigLen = sizeof(sig);
+ byte addRnd[WC_SLHDSA_MAX_SEED];
+
+ memset(addRnd, 0x42, sizeof(addRnd));
+
+ ret = wc_SlhDsaKey_Init(key, WH_TEST_SLHDSA_COMM_PARAM, NULL, devId);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to initialize SLH-DSA key: %d\n", ret);
+ return ret;
+ }
+
+ ret = wh_Client_SlhDsaMakeExportKey(ctx, WH_TEST_SLHDSA_COMM_PARAM, key);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to generate SLH-DSA key: %d\n", ret);
+ goto done;
+ }
+
+ ret = wc_SlhDsaKey_SignMsgWithRandom(key, mprime, sizeof(mprime), sig,
+ &sigLen, addRnd);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to sign M': %d\n", ret);
+ goto done;
+ }
+
+ ret = wc_SlhDsaKey_VerifyMsg(key, mprime, sizeof(mprime), sig, sigLen);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to verify M' signature: %d\n", ret);
+ goto done;
+ }
+
+ /* The same signature must not verify against a different M' */
+ mprime[2] ^= 0xFF;
+ ret = wc_SlhDsaKey_VerifyMsg(key, mprime, sizeof(mprime), sig, sigLen);
+ if (ret == 0) {
+ WH_ERROR_PRINT("M' verification accepted the wrong message\n");
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+
+ WH_TEST_PRINT("SLH-DSA MPRIME DEVID=0x%X SUCCESS\n", devId);
+ ret = 0;
+
+done:
+ wc_SlhDsaKey_Free(key);
+ return ret;
+}
+
+/* wc_SlhDsaKey_CheckKey against a server-resident private key. */
+static int _whTest_CryptoSlhDsaCheckPrivKey(whClientContext* ctx)
+{
+ int devId = WH_CLIENT_DEVID(ctx);
+ int ret;
+ whKeyId keyId = WH_KEYID_ERASED;
+ SlhDsaKey pub[1];
+ SlhDsaKey handle[1];
+ int pubInit = 0;
+ int handleInit = 0;
+ uint8_t label[] = "SlhDsaCheck";
+
+ ret = wc_SlhDsaKey_Init(pub, WH_TEST_SLHDSA_COMM_PARAM, NULL, devId);
+ if (ret != 0) {
+ return ret;
+ }
+ pubInit = 1;
+
+ ret = wh_Client_SlhDsaMakeCacheKeyAndExportPublic(
+ ctx, WH_TEST_SLHDSA_COMM_PARAM, &keyId, WH_NVM_FLAGS_USAGE_SIGN,
+ sizeof(label), label, pub);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to cache SLH-DSA key: %d\n", ret);
+ goto done;
+ }
+
+ ret = wc_SlhDsaKey_Init(handle, WH_TEST_SLHDSA_COMM_PARAM, NULL, devId);
+ if (ret != 0) {
+ goto done;
+ }
+ handleInit = 1;
+ (void)wh_Client_SlhDsaSetKeyId(handle, keyId);
+
+ {
+ byte expected[WC_SLHDSA_MAX_PUB_LEN];
+ int pubSz;
+
+ pubSz = wc_SlhDsaKey_PublicSize(pub);
+ if ((pubSz <= 0) || ((word32)pubSz > sizeof(expected))) {
+ WH_ERROR_PRINT("Bad SLH-DSA public size %d\n", pubSz);
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+ ret = wc_SlhDsaKey_ExportPublic(pub, expected, (word32*)&pubSz);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to export SLH-DSA public key: %d\n", ret);
+ goto done;
+ }
+
+ /* wc_SlhDsaKey_CheckKey takes the public key from the key struct, so
+ * it exercises the callback with the handle's own (server-held)
+ * material. */
+ ret = wc_SlhDsaKey_CheckKey(handle);
+ if (ret != 0) {
+ WH_ERROR_PRINT("CheckKey rejected the cached key: %d\n", ret);
+ goto done;
+ }
+
+ ret = wh_Client_SlhDsaCheckPrivKey(ctx, handle, expected,
+ (word32)pubSz);
+ if (ret != 0) {
+ WH_ERROR_PRINT("CheckPrivKey rejected the matching key: %d\n", ret);
+ goto done;
+ }
+
+ /* A public key that does not belong to the private key must be
+ * rejected rather than silently accepted. */
+ expected[0] ^= 0xFF;
+ ret = wh_Client_SlhDsaCheckPrivKey(ctx, handle, expected,
+ (word32)pubSz);
+ if (ret != WC_KEY_MISMATCH_E) {
+ WH_ERROR_PRINT("CheckPrivKey accepted a mismatched key: %d\n", ret);
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+ }
+
+ WH_TEST_PRINT("SLH-DSA CHECKPRIVKEY DEVID=0x%X SUCCESS\n", devId);
+ ret = 0;
+
+done:
+ if (!WH_KEYID_ISERASED(keyId)) {
+ (void)wh_Client_KeyEvict(ctx, keyId);
+ }
+ if (handleInit) {
+ wc_SlhDsaKey_Free(handle);
+ }
+ if (pubInit) {
+ wc_SlhDsaKey_Free(pub);
+ }
+ return ret;
+}
+
+#ifndef NO_SHA256
+/* A digest whose length does not match the declared pre-hash algorithm is a
+ * malformed request, not a signature that failed to verify. The two must stay
+ * distinguishable: reporting res=0 would tell the caller the signature is bad
+ * when the real problem is their own argument. */
+static int _whTest_CryptoSlhDsaBadDigestLen(whClientContext* ctx)
+{
+ int devId = WH_CLIENT_DEVID(ctx);
+ int ret;
+ SlhDsaKey key[1];
+ byte digest[WC_SHA256_DIGEST_SIZE];
+ byte sig[WH_TEST_SLHDSA_COMM_SIG_LEN];
+ word32 sigLen = sizeof(sig);
+ int verified = 1;
+
+ memset(digest, 0x3C, sizeof(digest));
+ memset(sig, 0, sizeof(sig));
+
+ ret = wc_SlhDsaKey_Init(key, WH_TEST_SLHDSA_COMM_PARAM, NULL, devId);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = wh_Client_SlhDsaMakeExportKey(ctx, WH_TEST_SLHDSA_COMM_PARAM, key);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to generate SLH-DSA key: %d\n", ret);
+ goto done;
+ }
+
+ ret = wh_Client_SlhDsaSign(ctx, digest, sizeof(digest), sig, &sigLen, key,
+ NULL, 0, WC_HASH_TYPE_SHA256, NULL, 0, 1, 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to pre-hash sign: %d\n", ret);
+ goto done;
+ }
+
+ /* Same signature, same hash type, digest one byte short */
+ ret = wh_Client_SlhDsaVerify(ctx, sig, sigLen, digest,
+ (word32)sizeof(digest) - 1, &verified, key,
+ NULL, 0, WC_HASH_TYPE_SHA256, 0);
+ if (ret == 0) {
+ WH_ERROR_PRINT("Short digest reported as a verify result (res=%d) "
+ "instead of an error\n",
+ verified);
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+
+ WH_TEST_PRINT("SLH-DSA BAD DIGEST LEN DEVID=0x%X SUCCESS\n", devId);
+ ret = 0;
+
+done:
+ wc_SlhDsaKey_Free(key);
+ return ret;
+}
+#endif /* !NO_SHA256 */
+
+/* A caller buffer smaller than the signature must report WH_ERROR_BUFFER_SIZE
+ * and the length that would have been needed, without writing past the end. */
+static int _whTest_CryptoSlhDsaBufferTooSmall(whClientContext* ctx)
+{
+ int devId = WH_CLIENT_DEVID(ctx);
+ int ret;
+ SlhDsaKey key[1];
+ const byte msg[] = "slh-dsa buf size test";
+ uint8_t small_sig[16] = {0};
+ word32 small_buf_sz = (word32)sizeof(small_sig);
+ word32 sig_len;
+
+ ret = wc_SlhDsaKey_Init(key, WH_TEST_SLHDSA_COMM_PARAM, NULL, devId);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = wh_Client_SlhDsaMakeExportKey(ctx, WH_TEST_SLHDSA_COMM_PARAM, key);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to generate SLH-DSA key: %d\n", ret);
+ goto done;
+ }
+
+ sig_len = small_buf_sz;
+ ret = wh_Client_SlhDsaSign(ctx, msg, (word32)sizeof(msg), small_sig,
+ &sig_len, key, NULL, 0, WC_HASH_TYPE_NONE, NULL,
+ 0, 1, 0);
+ if (ret != WH_ERROR_BUFFER_SIZE) {
+ WH_ERROR_PRINT("SlhDsaSign small buf expected WH_ERROR_BUFFER_SIZE, "
+ "got %d\n",
+ ret);
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+ if (sig_len <= small_buf_sz) {
+ WH_ERROR_PRINT("SlhDsaSign small buf reported size %u not greater "
+ "than %u\n",
+ (unsigned)sig_len, (unsigned)small_buf_sz);
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+
+ WH_TEST_PRINT("SLH-DSA BUFFER SIZE DEVID=0x%X SUCCESS\n", devId);
+ ret = 0;
+
+done:
+ wc_SlhDsaKey_Free(key);
+ return ret;
+}
+
+#endif /* WH_TEST_SLHDSA_COMM_PARAM */
+
+#ifdef WH_TEST_SLHDSA_FAST_PARAM
+/* NIST CAVP SLH-DSA-SHAKE-128f keyGen vector (tgId=4, tcId=31). The seeded
+ * generation path is what makes a known-answer test possible at all: a random
+ * key generation has no expected output to compare against. */
+static const byte whTestSlhDsaKatSeed[] = {
+ /* SK.seed */
+ 0x39, 0x56, 0xAB, 0x39, 0x1B, 0x4D, 0x22, 0xFC,
+ 0x90, 0x7A, 0xF0, 0x74, 0x03, 0x26, 0xD0, 0x61,
+ /* SK.prf */
+ 0xAB, 0x0E, 0xB2, 0x06, 0x43, 0x6F, 0x2B, 0x86,
+ 0xEB, 0xE0, 0x86, 0xD7, 0x77, 0x39, 0xB3, 0xE4,
+ /* PK.seed */
+ 0x56, 0x50, 0x5C, 0x22, 0x9F, 0x4E, 0x7F, 0xA6,
+ 0xB2, 0x01, 0x71, 0x4C, 0x7D, 0xCC, 0x9D, 0xA3
+};
+
+static const byte whTestSlhDsaKatPub[] = {
+ /* PK.seed */
+ 0x56, 0x50, 0x5C, 0x22, 0x9F, 0x4E, 0x7F, 0xA6,
+ 0xB2, 0x01, 0x71, 0x4C, 0x7D, 0xCC, 0x9D, 0xA3,
+ /* PK.root */
+ 0x66, 0x57, 0x8F, 0x1F, 0x24, 0xC3, 0xFE, 0x37,
+ 0x1C, 0x97, 0xC1, 0x4C, 0xE0, 0xE7, 0x9C, 0xDC
+};
+
+/* Seeded generation over the comm buffer. Only the tiny key material crosses
+ * the wire here, so the fast parameter set is usable even without DMA. */
+static int _whTest_CryptoSlhDsaSeededKat(whClientContext* ctx)
+{
+ int devId = WH_CLIENT_DEVID(ctx);
+ int ret;
+ SlhDsaKey key[1];
+ byte pub[WC_SLHDSA_MAX_PUB_LEN];
+ word32 pubSz = sizeof(pub);
+
+ ret = wc_SlhDsaKey_Init(key, WH_TEST_SLHDSA_FAST_PARAM, NULL, devId);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Through wc_SlhDsaKey_MakeKeyWithRandom so the seeded key generation
+ * takes the callback path an application would. */
+ {
+ word32 n = (word32)(sizeof(whTestSlhDsaKatSeed) / 3);
+
+ ret = wc_SlhDsaKey_MakeKeyWithRandom(
+ key, whTestSlhDsaKatSeed, n, whTestSlhDsaKatSeed + n, n,
+ whTestSlhDsaKatSeed + 2 * n, n);
+ }
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed seeded SLH-DSA keygen: %d\n", ret);
+ goto done;
+ }
+
+ ret = wc_SlhDsaKey_ExportPublic(key, pub, &pubSz);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to export seeded public key: %d\n", ret);
+ goto done;
+ }
+
+ if ((pubSz != sizeof(whTestSlhDsaKatPub)) ||
+ (memcmp(pub, whTestSlhDsaKatPub, pubSz) != 0)) {
+ WH_ERROR_PRINT("Seeded SLH-DSA public key does not match the KAT\n");
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+
+ WH_TEST_PRINT("SLH-DSA SEEDED KAT DEVID=0x%X SUCCESS\n", devId);
+ ret = 0;
+
+done:
+ wc_SlhDsaKey_Free(key);
+ return ret;
+}
+
+#ifdef WOLFHSM_CFG_DMA
+/* The fast parameter set signs a 17088-byte signature, which no reasonable
+ * comm buffer holds, so this is the DMA path end to end. */
+static int _whTest_CryptoSlhDsaDmaClient(whClientContext* ctx)
+{
+ int devId = WH_CLIENT_DEVID(ctx);
+ int ret;
+ SlhDsaKey key[1];
+ byte msg[] = "Test message for DMA SLH-DSA";
+ byte sig[WH_TEST_SLHDSA_FAST_SIG_LEN];
+ word32 sigLen = sizeof(sig);
+ int verified = 0;
+
+ ret = wc_SlhDsaKey_Init(key, WH_TEST_SLHDSA_FAST_PARAM, NULL, devId);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = wh_Client_SlhDsaMakeExportKeyDma(ctx, WH_TEST_SLHDSA_FAST_PARAM, key);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to generate SLH-DSA key over DMA: %d\n", ret);
+ goto done;
+ }
+
+ ret = wh_Client_SlhDsaSignDma(ctx, msg, sizeof(msg), sig, &sigLen, key,
+ NULL, 0, WC_HASH_TYPE_NONE, NULL, 0, 1, 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to sign over DMA: %d\n", ret);
+ goto done;
+ }
+ if (sigLen != WH_TEST_SLHDSA_FAST_SIG_LEN) {
+ WH_ERROR_PRINT("DMA signature length %u, expected %u\n",
+ (unsigned)sigLen,
+ (unsigned)WH_TEST_SLHDSA_FAST_SIG_LEN);
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+
+ ret = wh_Client_SlhDsaVerifyDma(ctx, sig, sigLen, msg, sizeof(msg),
+ &verified, key, NULL, 0, WC_HASH_TYPE_NONE,
+ 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to verify over DMA: %d\n", ret);
+ goto done;
+ }
+ if (!verified) {
+ WH_ERROR_PRINT("SLH-DSA DMA verification failed\n");
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+
+ sig[0] ^= 0xFF;
+ ret = wh_Client_SlhDsaVerifyDma(ctx, sig, sigLen, msg, sizeof(msg),
+ &verified, key, NULL, 0, WC_HASH_TYPE_NONE,
+ 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("DMA verify with modified sig returned %d\n", ret);
+ goto done;
+ }
+ if (verified) {
+ WH_ERROR_PRINT("SLH-DSA DMA verified a bad signature\n");
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+
+ WH_TEST_PRINT("SLH-DSA DMA DEVID=0x%X SUCCESS\n", devId);
+ ret = 0;
+
+done:
+ wc_SlhDsaKey_Free(key);
+ return ret;
+}
+
+/* A cached key driven over DMA, plus a round trip of the key material through
+ * the DMA import and export calls. */
+static int _whTest_CryptoSlhDsaDmaCachedKey(whClientContext* ctx)
+{
+ int devId = WH_CLIENT_DEVID(ctx);
+ int ret;
+ whKeyId keyId = WH_KEYID_ERASED;
+ SlhDsaKey pub[1];
+ SlhDsaKey handle[1];
+ int pubInit = 0;
+ int handleInit = 0;
+ uint8_t label[] = "SlhDsaDmaCached";
+
+ ret = wc_SlhDsaKey_Init(pub, WH_TEST_SLHDSA_FAST_PARAM, NULL, devId);
+ if (ret != 0) {
+ return ret;
+ }
+ pubInit = 1;
+
+ ret = wh_Client_SlhDsaMakeCacheKeyDma(
+ ctx, WH_TEST_SLHDSA_FAST_PARAM, &keyId,
+ WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY, sizeof(label),
+ label, pub);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to cache SLH-DSA key over DMA: %d\n", ret);
+ goto done;
+ }
+
+ ret = wc_SlhDsaKey_Init(handle, WH_TEST_SLHDSA_FAST_PARAM, NULL, devId);
+ if (ret != 0) {
+ goto done;
+ }
+ handleInit = 1;
+ (void)wh_Client_SlhDsaSetKeyId(handle, keyId);
+
+ {
+ byte msg[] = "DMA signed by a key that never left the HSM";
+ byte sig[WH_TEST_SLHDSA_FAST_SIG_LEN];
+ word32 sigLen = sizeof(sig);
+ int verified = 0;
+
+ ret = wh_Client_SlhDsaSignDma(ctx, msg, sizeof(msg), sig, &sigLen,
+ handle, NULL, 0, WC_HASH_TYPE_NONE, NULL,
+ 0, 1, 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to DMA sign with a cached key: %d\n", ret);
+ goto done;
+ }
+
+ ret = wh_Client_SlhDsaVerifyDma(ctx, sig, sigLen, msg, sizeof(msg),
+ &verified, pub, NULL, 0,
+ WC_HASH_TYPE_NONE, 0);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to DMA verify a cached-key sig: %d\n", ret);
+ goto done;
+ }
+ if (!verified) {
+ WH_ERROR_PRINT("Cached-key DMA signature did not verify\n");
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+ }
+
+ /* Export the public key on its own and check it matches what keygen
+ * already handed back. */
+ {
+ SlhDsaKey exported[1];
+ byte a[WC_SLHDSA_MAX_PUB_LEN];
+ byte b[WC_SLHDSA_MAX_PUB_LEN];
+ word32 aSz = sizeof(a);
+ word32 bSz = sizeof(b);
+
+ ret = wc_SlhDsaKey_Init(exported, WH_TEST_SLHDSA_FAST_PARAM, NULL,
+ devId);
+ if (ret != 0) {
+ goto done;
+ }
+ ret = wh_Client_SlhDsaExportPublicKeyDma(ctx, keyId, exported, 0, NULL);
+ if (ret == 0) {
+ ret = wc_SlhDsaKey_ExportPublic(pub, a, &aSz);
+ }
+ if (ret == 0) {
+ ret = wc_SlhDsaKey_ExportPublic(exported, b, &bSz);
+ }
+ if ((ret == 0) && ((aSz != bSz) || (memcmp(a, b, aSz) != 0))) {
+ WH_ERROR_PRINT("Exported SLH-DSA public key does not match\n");
+ ret = WH_TEST_FAIL;
+ }
+ wc_SlhDsaKey_Free(exported);
+ if (ret != 0) {
+ WH_ERROR_PRINT("SLH-DSA DMA public key export failed: %d\n", ret);
+ goto done;
+ }
+ }
+
+ WH_TEST_PRINT("SLH-DSA DMA CACHED KEY DEVID=0x%X SUCCESS\n", devId);
+ ret = 0;
+
+done:
+ if (!WH_KEYID_ISERASED(keyId)) {
+ (void)wh_Client_KeyEvict(ctx, keyId);
+ }
+ if (handleInit) {
+ wc_SlhDsaKey_Free(handle);
+ }
+ if (pubInit) {
+ wc_SlhDsaKey_Free(pub);
+ }
+ return ret;
+}
+#endif /* WOLFHSM_CFG_DMA */
+#endif /* WH_TEST_SLHDSA_FAST_PARAM */
+
+#ifdef WH_TEST_SLHDSA_COMM_PARAM
+/* The comm buffer cannot carry the larger parameter sets, so the server must
+ * say so rather than truncating the signature. */
+#if defined(WOLFSSL_SLHDSA_PARAM_192S) || defined(WOLFSSL_SLHDSA_PARAM_256S)
+#if defined(WOLFSSL_SLHDSA_PARAM_192S)
+#define WH_TEST_SLHDSA_OVERSIZE_PARAM SLHDSA_SHAKE192S
+#else
+#define WH_TEST_SLHDSA_OVERSIZE_PARAM SLHDSA_SHAKE256S
+#endif
+static int _whTest_CryptoSlhDsaCommBufferLimit(whClientContext* ctx)
+{
+ int devId = WH_CLIENT_DEVID(ctx);
+ int ret;
+ SlhDsaKey key[1];
+ byte msg[] = "too big for the comm buffer";
+ byte sig[WC_SLHDSA_MAX_SIG_LEN];
+ word32 sigLen = sizeof(sig);
+
+ ret = wc_SlhDsaKey_Init(key, WH_TEST_SLHDSA_OVERSIZE_PARAM, NULL, devId);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = wh_Client_SlhDsaMakeExportKey(ctx, WH_TEST_SLHDSA_OVERSIZE_PARAM,
+ key);
+ if (ret != 0) {
+ WH_ERROR_PRINT("Failed to generate oversize SLH-DSA key: %d\n", ret);
+ goto done;
+ }
+
+ ret = wh_Client_SlhDsaSign(ctx, msg, sizeof(msg), sig, &sigLen, key, NULL,
+ 0, WC_HASH_TYPE_NONE, NULL, 0, 1, 0);
+ if (ret != WH_ERROR_BUFFER_SIZE) {
+ WH_ERROR_PRINT("Oversize sign expected WH_ERROR_BUFFER_SIZE, got %d\n",
+ ret);
+ ret = WH_TEST_FAIL;
+ goto done;
+ }
+
+ WH_TEST_PRINT("SLH-DSA COMM LIMIT DEVID=0x%X SUCCESS\n", devId);
+ ret = 0;
+
+done:
+ wc_SlhDsaKey_Free(key);
+ return ret;
+}
+#endif /* 192S || 256S */
+#endif /* WH_TEST_SLHDSA_COMM_PARAM */
+
+int whTest_Crypto_SlhDsa(whClientContext* ctx)
+{
+#ifdef WH_TEST_SLHDSA_COMM_PARAM
+ /* The wolfCrypt-API driver is the path an application actually takes, so
+ * run it in each dispatch mode the build offers. */
+ int i;
+
+ for (i = 0; i < WH_TEST_DMA_MODE_CNT; i++) {
+ (void)wh_Client_SetDmaMode(ctx, i);
+ WH_TEST_RETURN_ON_FAIL(
+ _whTest_SlhDsaWolfCryptImpl(ctx, WH_CLIENT_DEVID(ctx)));
+ }
+ (void)wh_Client_SetDmaMode(ctx, 0);
+
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoSlhDsaClient(ctx));
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoSlhDsaCachedKey(ctx));
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoSlhDsaMPrime(ctx));
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoSlhDsaCheckPrivKey(ctx));
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoSlhDsaBufferTooSmall(ctx));
+#ifndef NO_SHA256
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoSlhDsaBadDigestLen(ctx));
+#endif
+#ifdef WH_TEST_SLHDSA_OVERSIZE_PARAM
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoSlhDsaCommBufferLimit(ctx));
+#endif
+#endif /* WH_TEST_SLHDSA_COMM_PARAM */
+
+#ifdef WH_TEST_SLHDSA_FAST_PARAM
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoSlhDsaSeededKat(ctx));
+#ifdef WOLFHSM_CFG_DMA
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoSlhDsaDmaClient(ctx));
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoSlhDsaDmaCachedKey(ctx));
+#endif
+#endif /* WH_TEST_SLHDSA_FAST_PARAM */
+
+ (void)ctx;
+ return 0;
+}
+
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
+#endif /* !WOLFHSM_CFG_NO_CRYPTO */
diff --git a/test-refactor/misc/wh_test_check_struct_padding.c b/test-refactor/misc/wh_test_check_struct_padding.c
index 403c93d1d..134e123aa 100644
--- a/test-refactor/misc/wh_test_check_struct_padding.c
+++ b/test-refactor/misc/wh_test_check_struct_padding.c
@@ -141,6 +141,14 @@ whMessageCrypto_MlDsaSignRequest pkMldsaSignReq;
whMessageCrypto_MlDsaSignResponse pkMldsaSignRes;
whMessageCrypto_MlDsaVerifyRequest pkMldsaVerifyReq;
whMessageCrypto_MlDsaVerifyResponse pkMldsaVerifyRes;
+whMessageCrypto_SlhDsaKeyGenRequest pkSlhdsaKeygenReq;
+whMessageCrypto_SlhDsaKeyGenResponse pkSlhdsaKeygenRes;
+whMessageCrypto_SlhDsaSignRequest pkSlhdsaSignReq;
+whMessageCrypto_SlhDsaSignResponse pkSlhdsaSignRes;
+whMessageCrypto_SlhDsaVerifyRequest pkSlhdsaVerifyReq;
+whMessageCrypto_SlhDsaVerifyResponse pkSlhdsaVerifyRes;
+whMessageCrypto_SlhDsaCheckPrivKeyRequest pkSlhdsaCheckReq;
+whMessageCrypto_SlhDsaCheckPrivKeyResponse pkSlhdsaCheckRes;
whMessageCrypto_MlKemKeyGenRequest pkMlkemKeygenReq;
whMessageCrypto_MlKemKeyGenResponse pkMlkemKeygenRes;
whMessageCrypto_MlKemEncapsRequest pkMlkemEncapsReq;
@@ -163,6 +171,12 @@ whMessageCrypto_MlDsaSignDmaRequest pqMldsaSignDmaReq;
whMessageCrypto_MlDsaSignDmaResponse pqMldsaSignDmaRes;
whMessageCrypto_MlDsaVerifyDmaRequest pqMldsaVerifyDmaReq;
whMessageCrypto_MlDsaVerifyDmaResponse pqMldsaVerifyDmaRes;
+whMessageCrypto_SlhDsaKeyGenDmaRequest pqSlhdsaKeygenDmaReq;
+whMessageCrypto_SlhDsaKeyGenDmaResponse pqSlhdsaKeygenDmaRes;
+whMessageCrypto_SlhDsaSignDmaRequest pqSlhdsaSignDmaReq;
+whMessageCrypto_SlhDsaSignDmaResponse pqSlhdsaSignDmaRes;
+whMessageCrypto_SlhDsaVerifyDmaRequest pqSlhdsaVerifyDmaReq;
+whMessageCrypto_SlhDsaVerifyDmaResponse pqSlhdsaVerifyDmaRes;
whMessageCrypto_CmacAesDmaRequest cmacDmaReq;
whMessageCrypto_CmacAesDmaResponse cmacDmaRes;
whMessageCrypto_MlKemKeyGenDmaRequest pkMlkemKeygenDmaReq;
diff --git a/test-refactor/wh_test_list.c b/test-refactor/wh_test_list.c
index 02e121ebe..57a144c67 100644
--- a/test-refactor/wh_test_list.c
+++ b/test-refactor/wh_test_list.c
@@ -75,6 +75,7 @@ WH_TEST_DECL(whTest_Crypto_MlDsa);
WH_TEST_DECL(whTest_Crypto_Rng);
WH_TEST_DECL(whTest_Crypto_Rsa);
WH_TEST_DECL(whTest_Crypto_Sha);
+WH_TEST_DECL(whTest_Crypto_SlhDsa);
WH_TEST_DECL(whTest_Crypto_Sha3);
WH_TEST_DECL(whTest_Crypto_Xmss);
WH_TEST_DECL(whTest_CryptoEcc256);
@@ -153,6 +154,7 @@ const whTestCase whTestsClient[] = {
{"whTest_Crypto_Rng", whTest_Crypto_Rng},
{"whTest_Crypto_Rsa", whTest_Crypto_Rsa},
{"whTest_Crypto_Sha", whTest_Crypto_Sha},
+ {"whTest_Crypto_SlhDsa", whTest_Crypto_SlhDsa},
{"whTest_Crypto_Sha3", whTest_Crypto_Sha3},
{"whTest_Crypto_Xmss", whTest_Crypto_Xmss},
{"whTest_CryptoEcc256", whTest_CryptoEcc256},
diff --git a/test/config/user_settings.h b/test/config/user_settings.h
index f237ccaa4..6a00ed9a9 100644
--- a/test/config/user_settings.h
+++ b/test/config/user_settings.h
@@ -141,6 +141,12 @@
* compiled and exercised by the test suite. */
#define WOLFSSL_HASH_FLAGS
+/* SLH-DSA Options. Only the 128-bit category is built: 128s is the one
+ * signature that fits the comm buffer and 128f keeps the DMA tests quick. */
+#define WOLFSSL_HAVE_SLHDSA
+#define WOLFSSL_SLHDSA_PARAM_NO_192
+#define WOLFSSL_SLHDSA_PARAM_NO_256
+
/* ML-KEM Options */
#define WOLFSSL_HAVE_MLKEM
/* LMS / HSS Options (RFC 8554, NIST SP 800-208) */
diff --git a/test/wh_test_check_struct_padding.c b/test/wh_test_check_struct_padding.c
index bf29822a6..556fa471b 100644
--- a/test/wh_test_check_struct_padding.c
+++ b/test/wh_test_check_struct_padding.c
@@ -139,6 +139,14 @@ whMessageCrypto_MlDsaSignRequest pkMldsaSignReq;
whMessageCrypto_MlDsaSignResponse pkMldsaSignRes;
whMessageCrypto_MlDsaVerifyRequest pkMldsaVerifyReq;
whMessageCrypto_MlDsaVerifyResponse pkMldsaVerifyRes;
+whMessageCrypto_SlhDsaKeyGenRequest pkSlhdsaKeygenReq;
+whMessageCrypto_SlhDsaKeyGenResponse pkSlhdsaKeygenRes;
+whMessageCrypto_SlhDsaSignRequest pkSlhdsaSignReq;
+whMessageCrypto_SlhDsaSignResponse pkSlhdsaSignRes;
+whMessageCrypto_SlhDsaVerifyRequest pkSlhdsaVerifyReq;
+whMessageCrypto_SlhDsaVerifyResponse pkSlhdsaVerifyRes;
+whMessageCrypto_SlhDsaCheckPrivKeyRequest pkSlhdsaCheckReq;
+whMessageCrypto_SlhDsaCheckPrivKeyResponse pkSlhdsaCheckRes;
whMessageCrypto_MlKemKeyGenRequest pkMlkemKeygenReq;
whMessageCrypto_MlKemKeyGenResponse pkMlkemKeygenRes;
whMessageCrypto_MlKemEncapsRequest pkMlkemEncapsReq;
@@ -161,6 +169,12 @@ whMessageCrypto_MlDsaSignDmaRequest pqMldsaSignDmaReq;
whMessageCrypto_MlDsaSignDmaResponse pqMldsaSignDmaRes;
whMessageCrypto_MlDsaVerifyDmaRequest pqMldsaVerifyDmaReq;
whMessageCrypto_MlDsaVerifyDmaResponse pqMldsaVerifyDmaRes;
+whMessageCrypto_SlhDsaKeyGenDmaRequest pqSlhdsaKeygenDmaReq;
+whMessageCrypto_SlhDsaKeyGenDmaResponse pqSlhdsaKeygenDmaRes;
+whMessageCrypto_SlhDsaSignDmaRequest pqSlhdsaSignDmaReq;
+whMessageCrypto_SlhDsaSignDmaResponse pqSlhdsaSignDmaRes;
+whMessageCrypto_SlhDsaVerifyDmaRequest pqSlhdsaVerifyDmaReq;
+whMessageCrypto_SlhDsaVerifyDmaResponse pqSlhdsaVerifyDmaRes;
whMessageCrypto_CmacAesDmaRequest cmacDmaReq;
whMessageCrypto_CmacAesDmaResponse cmacDmaRes;
whMessageCrypto_MlKemKeyGenDmaRequest pkMlkemKeygenDmaReq;
diff --git a/wolfhsm/wh_client_crypto.h b/wolfhsm/wh_client_crypto.h
index 9a0e921c6..cf11eef13 100644
--- a/wolfhsm/wh_client_crypto.h
+++ b/wolfhsm/wh_client_crypto.h
@@ -52,6 +52,7 @@
#include "wolfssl/wolfcrypt/ed25519.h"
#include "wolfssl/wolfcrypt/wc_mldsa.h"
#include "wolfssl/wolfcrypt/wc_mlkem.h"
+#include "wolfssl/wolfcrypt/wc_slhdsa.h"
#include "wolfssl/wolfcrypt/hmac.h"
#ifdef WOLFSSL_SHA3
#include "wolfssl/wolfcrypt/sha3.h"
@@ -3374,6 +3375,385 @@ int wh_Client_MlDsaCheckPrivKeyDma(whClientContext* ctx, wc_MlDsaKey* key,
#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+
+/**
+ * @brief Associates an SLH-DSA key with a specific key ID.
+ *
+ * This function sets the device context of an SLH-DSA 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 SLH-DSA key structure.
+ * @param[in] keyId Key ID to be associated with the SLH-DSA key.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaSetKeyId(SlhDsaKey* key, whKeyId keyId);
+
+/**
+ * @brief Gets the wolfHSM keyId being used by the wolfCrypt struct.
+ *
+ * @param[in] key Pointer to the SLH-DSA key structure.
+ * @param[out] outId Pointer to the key ID to return.
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaGetKeyId(SlhDsaKey* key, whKeyId* outId);
+
+/**
+ * @brief Import an SLH-DSA key to the server key cache.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] key Pointer to the 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
+ * @param[in] label Optional label to associate with key
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaImportKey(whClientContext* ctx, SlhDsaKey* key,
+ whKeyId* inout_keyId, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label);
+
+/**
+ * @brief Export an SLH-DSA key from the server.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] keyId ID of key to export
+ * @param[out] key Pointer to receive exported key
+ * @param[in] label_len Length of optional label buffer
+ * @param[in] label Optional buffer to receive key label
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaExportKey(whClientContext* ctx, whKeyId keyId,
+ SlhDsaKey* key, uint16_t label_len,
+ uint8_t* label);
+
+/**
+ * @brief Exports only the public part of a cached SLH-DSA key.
+ *
+ * The private key stays inside the HSM. The caller is responsible for
+ * initializing key with wc_SlhDsaKey_Init; the parameter set is taken from
+ * the key OID in the exported DER, so the placeholder set the caller used
+ * does not have to match.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] keyId Server key ID whose public key should be exported
+ * @param[out] key Pointer to receive the exported public key
+ * @param[in] label_len Length of optional label buffer
+ * @param[in] label Optional buffer to receive key label
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaExportPublicKey(whClientContext* ctx, whKeyId keyId,
+ SlhDsaKey* key, uint16_t label_len,
+ uint8_t* label);
+
+/**
+ * @brief Generate an SLH-DSA key on the server and leave it cached there.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] param Parameter set to generate (enum SlhDsaParam)
+ * @param[in,out] inout_key_id Pointer to key ID to use/receive
+ * @param[in] flags Flags to control key persistence
+ * @param[in] label_len Length of optional label
+ * @param[in] label Optional label to associate with key
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaMakeCacheKey(whClientContext* ctx, int param,
+ whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label);
+
+/**
+ * @brief Generate a cached SLH-DSA key and export its public part.
+ *
+ * On success pub is a usable handle to the cached private key: its key ID and
+ * the client's HSM devId are stamped into it.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] param Parameter set to generate (enum SlhDsaParam)
+ * @param[in,out] inout_key_id Pointer to key ID to use/receive
+ * @param[in] flags Flags to control key persistence, must not be EPHEMERAL
+ * @param[in] label_len Length of optional label
+ * @param[in] label Optional label to associate with key
+ * @param[out] pub Pointer to receive the public key
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaMakeCacheKeyAndExportPublic(
+ whClientContext* ctx, int param, whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, const uint8_t* label, SlhDsaKey* pub);
+
+/**
+ * @brief Generate an ephemeral SLH-DSA key and export it to the caller.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] param Parameter set to generate (enum SlhDsaParam)
+ * @param[out] key Pointer to receive the generated key
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaMakeExportKey(whClientContext* ctx, int param,
+ SlhDsaKey* key);
+
+/**
+ * @brief Generate an ephemeral SLH-DSA key from a caller-supplied seed.
+ *
+ * The seed is the contiguous SK.seed || SK.prf || PK.seed, 3n bytes for the
+ * requested parameter set. Deterministic generation makes known-answer tests
+ * and reproducible provisioning possible.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] param Parameter set to generate (enum SlhDsaParam)
+ * @param[in] seed Pointer to the 3n seed bytes
+ * @param[in] seedSz Length of seed in bytes
+ * @param[out] key Pointer to receive the generated key
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaMakeExportKeyFromSeed(whClientContext* ctx, int param,
+ const byte* seed, word32 seedSz,
+ SlhDsaKey* key);
+
+/**
+ * @brief Generate a cached SLH-DSA key from a caller-supplied seed.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] param Parameter set to generate (enum SlhDsaParam)
+ * @param[in] seed Pointer to the 3n seed bytes
+ * @param[in] seedSz Length of seed in bytes
+ * @param[in,out] inout_key_id Pointer to key ID to use/receive
+ * @param[in] flags Flags to control key persistence
+ * @param[in] label_len Length of optional label
+ * @param[in] label Optional label to associate with key
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaMakeCacheKeyFromSeed(whClientContext* ctx, int param,
+ const byte* seed, word32 seedSz,
+ whKeyId* inout_key_id,
+ whNvmFlags flags, uint16_t label_len,
+ uint8_t* label);
+
+/**
+ * @brief Sign a message or digest with an SLH-DSA key held by the server.
+ *
+ * Covers the whole FIPS 205 signing surface. preHashType selects pure
+ * SLH-DSA (WC_HASH_TYPE_NONE) or HashSLH-DSA. isMPrime signs a caller-built
+ * M' directly, in which case context and preHashType are ignored. A non-empty
+ * addRnd supplies the randomizer explicitly; otherwise randomized selects
+ * between a server-generated randomizer and deterministic signing.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] in Message, digest, or M' to sign
+ * @param[in] in_len Length of in in bytes
+ * @param[out] out Buffer to receive the signature
+ * @param[in,out] inout_len Capacity of out on entry, signature length on exit
+ * @param[in] key Key handle, either server-resident or holding key material
+ * @param[in] context FIPS 205 context string, may be NULL
+ * @param[in] contextLen Length of context, 0 to 255
+ * @param[in] preHashType Pre-hash algorithm (enum wc_HashType)
+ * @param[in] addRnd Explicit randomizer, may be NULL
+ * @param[in] addRndSz Length of addRnd in bytes, 0 if none
+ * @param[in] randomized Non-zero to have the server pick the randomizer
+ * @param[in] isMPrime Non-zero when in is a caller-built M'
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaSign(whClientContext* ctx, const byte* in, word32 in_len,
+ byte* out, word32* inout_len, SlhDsaKey* key,
+ const byte* context, byte contextLen,
+ word32 preHashType, const byte* addRnd, byte addRndSz,
+ int randomized, int isMPrime);
+
+/**
+ * @brief Verify an SLH-DSA signature with a key held by the server.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] sig Signature to verify
+ * @param[in] sig_len Length of sig in bytes
+ * @param[in] msg Message, digest, or M' that was signed
+ * @param[in] msg_len Length of msg in bytes
+ * @param[out] out_res Set to 1 when the signature verifies, 0 otherwise
+ * @param[in] key Key handle, either server-resident or holding key material
+ * @param[in] context FIPS 205 context string, may be NULL
+ * @param[in] contextLen Length of context, 0 to 255
+ * @param[in] preHashType Pre-hash algorithm (enum wc_HashType)
+ * @param[in] isMPrime Non-zero when msg is a caller-built M'
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaVerify(whClientContext* ctx, const byte* sig,
+ word32 sig_len, const byte* msg, word32 msg_len,
+ int* out_res, SlhDsaKey* key, const byte* context,
+ byte contextLen, word32 preHashType, int isMPrime);
+
+/**
+ * @brief Check that a server-held SLH-DSA private key matches a public key.
+ *
+ * Passing NULL for pubKey (with pubKeySz 0) asks the server to check its copy
+ * of the private key for internal consistency, with nothing to compare it
+ * against. That is what a caller holding only a key ID can ask for.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] key Key handle for the private key
+ * @param[in] pubKey Public key bytes, PK.seed || PK.root, or NULL
+ * @param[in] pubKeySz Length of pubKey in bytes, 2n, or 0 when pubKey is NULL
+ * @return int Returns 0 when they match, WC_KEY_MISMATCH_E when they do not,
+ * or a negative error code on failure.
+ */
+int wh_Client_SlhDsaCheckPrivKey(whClientContext* ctx, SlhDsaKey* key,
+ const byte* pubKey, word32 pubKeySz);
+
+#ifdef WOLFHSM_CFG_DMA
+
+/**
+ * @brief Import an SLH-DSA key to the server key cache using DMA.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] key Pointer to the 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
+ * @param[in] label Optional label to associate with key
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaImportKeyDma(whClientContext* ctx, SlhDsaKey* key,
+ whKeyId* inout_keyId, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label);
+
+/**
+ * @brief Export an SLH-DSA key from the server using DMA.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] keyId ID of key to export
+ * @param[out] key Pointer to receive exported key
+ * @param[in] label_len Length of optional label buffer
+ * @param[in] label Optional buffer to receive key label
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaExportKeyDma(whClientContext* ctx, whKeyId keyId,
+ SlhDsaKey* key, uint16_t label_len,
+ uint8_t* label);
+
+/**
+ * @brief Export only the public part of a cached SLH-DSA key using DMA.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] keyId Server key ID whose public key should be exported
+ * @param[out] key Pointer to receive the exported public key
+ * @param[in] label_len Length of optional label buffer
+ * @param[in] label Optional buffer to receive key label
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaExportPublicKeyDma(whClientContext* ctx, whKeyId keyId,
+ SlhDsaKey* key, uint16_t label_len,
+ uint8_t* label);
+
+/**
+ * @brief Generate an ephemeral SLH-DSA key and export it using DMA.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] param Parameter set to generate (enum SlhDsaParam)
+ * @param[out] key Pointer to receive the generated key
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaMakeExportKeyDma(whClientContext* ctx, int param,
+ SlhDsaKey* key);
+
+/**
+ * @brief Generate an ephemeral SLH-DSA key from a seed using DMA.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] param Parameter set to generate (enum SlhDsaParam)
+ * @param[in] seed Pointer to the 3n seed bytes
+ * @param[in] seedSz Length of seed in bytes
+ * @param[out] key Pointer to receive the generated key
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaMakeExportKeyFromSeedDma(whClientContext* ctx, int param,
+ const byte* seed, word32 seedSz,
+ SlhDsaKey* key);
+
+/**
+ * @brief Generate a cached SLH-DSA key and export its public part using DMA.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] param Parameter set to generate (enum SlhDsaParam)
+ * @param[in,out] inout_key_id Pointer to key ID to use/receive
+ * @param[in] flags Flags to control key persistence, must not be EPHEMERAL
+ * @param[in] label_len Length of optional label
+ * @param[in] label Optional label to associate with key
+ * @param[out] pub Pointer to receive the public key
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaMakeCacheKeyDma(whClientContext* ctx, int param,
+ whKeyId* inout_key_id, whNvmFlags flags,
+ uint16_t label_len, const uint8_t* label,
+ SlhDsaKey* pub);
+
+/**
+ * @brief Sign with an SLH-DSA key held by the server using DMA.
+ *
+ * Arguments match wh_Client_SlhDsaSign; the message and signature travel by
+ * DMA rather than through the comm buffer, which is what makes the larger
+ * parameter sets usable.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] in Message, digest, or M' to sign
+ * @param[in] in_len Length of in in bytes
+ * @param[out] out Buffer to receive the signature
+ * @param[in,out] out_len Capacity of out on entry, signature length on exit
+ * @param[in] key Key handle, either server-resident or holding key material
+ * @param[in] context FIPS 205 context string, may be NULL
+ * @param[in] contextLen Length of context, 0 to 255
+ * @param[in] preHashType Pre-hash algorithm (enum wc_HashType)
+ * @param[in] addRnd Explicit randomizer, may be NULL
+ * @param[in] addRndSz Length of addRnd in bytes, 0 if none
+ * @param[in] randomized Non-zero to have the server pick the randomizer
+ * @param[in] isMPrime Non-zero when in is a caller-built M'
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaSignDma(whClientContext* ctx, const byte* in,
+ word32 in_len, byte* out, word32* out_len,
+ SlhDsaKey* key, const byte* context,
+ byte contextLen, word32 preHashType,
+ const byte* addRnd, byte addRndSz, int randomized,
+ int isMPrime);
+
+/**
+ * @brief Verify an SLH-DSA signature using DMA.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] sig Signature to verify
+ * @param[in] sig_len Length of sig in bytes
+ * @param[in] msg Message, digest, or M' that was signed
+ * @param[in] msg_len Length of msg in bytes
+ * @param[out] out_res Set to 1 when the signature verifies, 0 otherwise
+ * @param[in] key Key handle, either server-resident or holding key material
+ * @param[in] context FIPS 205 context string, may be NULL
+ * @param[in] contextLen Length of context, 0 to 255
+ * @param[in] preHashType Pre-hash algorithm (enum wc_HashType)
+ * @param[in] isMPrime Non-zero when msg is a caller-built M'
+ * @return int Returns 0 on success or a negative error code on failure.
+ */
+int wh_Client_SlhDsaVerifyDma(whClientContext* ctx, const byte* sig,
+ word32 sig_len, const byte* msg, word32 msg_len,
+ int* out_res, SlhDsaKey* key,
+ const byte* context, byte contextLen,
+ word32 preHashType, int isMPrime);
+
+/**
+ * @brief Check a server-held SLH-DSA private key against a public key.
+ *
+ * The public key is only 2n bytes, so this forwards to the comm-buffer path.
+ *
+ * @param[in] ctx Pointer to the client context
+ * @param[in] key Key handle for the private key
+ * @param[in] pubKey Public key bytes, PK.seed || PK.root
+ * @param[in] pubKeySz Length of pubKey in bytes, 2n
+ * @return int Returns 0 when they match, WC_KEY_MISMATCH_E when they do not,
+ * or a negative error code on failure.
+ */
+int wh_Client_SlhDsaCheckPrivKeyDma(whClientContext* ctx, SlhDsaKey* key,
+ const byte* pubKey, word32 pubKeySz);
+
+#endif /* WOLFHSM_CFG_DMA */
+
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
#ifdef WOLFSSL_HAVE_MLKEM
/**
diff --git a/wolfhsm/wh_common.h b/wolfhsm/wh_common.h
index e8289111f..95d4ecff7 100644
--- a/wolfhsm/wh_common.h
+++ b/wolfhsm/wh_common.h
@@ -177,6 +177,7 @@ enum WH_KEY_ALGO_ENUM {
WH_KEY_ALGO_MLKEM = 6,
WH_KEY_ALGO_LMS = 7,
WH_KEY_ALGO_XMSS = 8,
+ WH_KEY_ALGO_SLHDSA = 9,
};
#endif /* !WOLFHSM_WH_COMMON_H_ */
diff --git a/wolfhsm/wh_crypto.h b/wolfhsm/wh_crypto.h
index c1a97ae51..b15eee2fa 100644
--- a/wolfhsm/wh_crypto.h
+++ b/wolfhsm/wh_crypto.h
@@ -44,6 +44,7 @@
#include "wolfssl/wolfcrypt/ed25519.h"
#include "wolfssl/wolfcrypt/wc_mldsa.h"
#include "wolfssl/wolfcrypt/wc_mlkem.h"
+#include "wolfssl/wolfcrypt/wc_slhdsa.h"
#include "wolfhsm/wh_message_crypto.h"
@@ -119,6 +120,20 @@ int wh_Crypto_MlDsaDeserializeKeyDer(const uint8_t* buffer, uint16_t size,
wc_MlDsaKey* key);
#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+#define WH_CRYPTO_SLHDSA_MAX_CTX_LEN (255U)
+/* RFC 9909 wraps the raw 4n private and 2n public key in a OneAsymmetricKey.
+ * The slack covers the algorithm identifier and the ASN.1 headers. */
+#define WH_CRYPTO_SLHDSA_MAX_KEY_DER_SIZE \
+ (WC_SLHDSA_MAX_PRIV_LEN + WC_SLHDSA_MAX_PUB_LEN + 128U)
+/* Store a SlhDsaKey to a byte sequence */
+int wh_Crypto_SlhDsaSerializeKeyDer(SlhDsaKey* key, uint16_t max_size,
+ uint8_t* buffer, uint16_t* out_size);
+/* Restore a SlhDsaKey from a byte sequence */
+int wh_Crypto_SlhDsaDeserializeKeyDer(const uint8_t* buffer, uint16_t size,
+ SlhDsaKey* key);
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
#ifdef WOLFSSL_HAVE_MLKEM
/* Store a MlKemKey to a byte sequence */
int wh_Crypto_MlKemSerializeKey(MlKemKey* key, uint16_t max_size,
diff --git a/wolfhsm/wh_message_crypto.h b/wolfhsm/wh_message_crypto.h
index 809513bc9..4d9353450 100644
--- a/wolfhsm/wh_message_crypto.h
+++ b/wolfhsm/wh_message_crypto.h
@@ -1193,6 +1193,142 @@ int wh_MessageCrypto_TranslateMlDsaVerifyResponse(
uint16_t magic, const whMessageCrypto_MlDsaVerifyResponse* src,
whMessageCrypto_MlDsaVerifyResponse* dest);
+
+/*
+ * SLH-DSA
+ */
+
+/* SLH-DSA Key Generation Request */
+typedef struct {
+ uint32_t sz;
+ uint32_t param; /* enum SlhDsaParam parameter set */
+ uint32_t keyId;
+ uint32_t flags;
+ uint32_t access;
+ uint32_t seedSz; /* 0 for random keygen, else SK.seed||SK.prf||PK.seed */
+ uint8_t label[WH_NVM_LABEL_LEN];
+ /* Data follows:
+ * uint8_t seed[seedSz];
+ */
+} whMessageCrypto_SlhDsaKeyGenRequest;
+
+/* SLH-DSA Key Generation Response */
+typedef struct {
+ uint32_t keyId;
+ uint32_t len;
+ /* Data follows:
+ * uint8_t out[len];
+ */
+} whMessageCrypto_SlhDsaKeyGenResponse;
+
+int wh_MessageCrypto_TranslateSlhDsaKeyGenRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaKeyGenRequest* src,
+ whMessageCrypto_SlhDsaKeyGenRequest* dest);
+
+int wh_MessageCrypto_TranslateSlhDsaKeyGenResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaKeyGenResponse* src,
+ whMessageCrypto_SlhDsaKeyGenResponse* dest);
+
+/* SLH-DSA Sign Request */
+typedef struct {
+ uint32_t options;
+#define WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_EVICT (1 << 0)
+/* Input is a caller-built M', so context and pre-hash do not apply */
+#define WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_MPRIME (1 << 1)
+/* Caller asked for a hedged signature; the server supplies the randomizer */
+#define WH_MESSAGE_CRYPTO_SLHDSA_SIGN_OPTIONS_RANDOMIZED (1 << 2)
+ uint32_t param;
+ uint32_t keyId;
+ uint32_t sz;
+ uint32_t contextSz; /* FIPS 205 context length (0-255) */
+ uint32_t preHashType; /* enum wc_HashType, 0 for pure SLH-DSA */
+ uint32_t addRndSz; /* Caller-supplied randomizer length, 0 if none */
+ uint8_t WH_PAD[4];
+ /* Data follows:
+ * uint8_t in[sz];
+ * uint8_t context[contextSz];
+ * uint8_t addRnd[addRndSz];
+ */
+} whMessageCrypto_SlhDsaSignRequest;
+
+/* SLH-DSA Sign Response */
+typedef struct {
+ uint32_t sz;
+ uint8_t WH_PAD[4];
+ /* Data follows:
+ * uint8_t out[sz];
+ */
+} whMessageCrypto_SlhDsaSignResponse;
+
+int wh_MessageCrypto_TranslateSlhDsaSignRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaSignRequest* src,
+ whMessageCrypto_SlhDsaSignRequest* dest);
+
+int wh_MessageCrypto_TranslateSlhDsaSignResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaSignResponse* src,
+ whMessageCrypto_SlhDsaSignResponse* dest);
+
+/* SLH-DSA Verify Request */
+typedef struct {
+ uint32_t options;
+#define WH_MESSAGE_CRYPTO_SLHDSA_VERIFY_OPTIONS_EVICT (1 << 0)
+#define WH_MESSAGE_CRYPTO_SLHDSA_VERIFY_OPTIONS_EXPORTPUB (1 << 1)
+/* Message is a caller-built M', so context and pre-hash do not apply */
+#define WH_MESSAGE_CRYPTO_SLHDSA_VERIFY_OPTIONS_MPRIME (1 << 2)
+ uint32_t param;
+ uint32_t keyId;
+ uint32_t sigSz;
+ uint32_t hashSz;
+ uint32_t contextSz; /* FIPS 205 context length (0-255) */
+ uint32_t preHashType; /* enum wc_HashType, 0 for pure SLH-DSA */
+ uint8_t WH_PAD[4];
+ /* Data follows:
+ * uint8_t sig[sigSz];
+ * uint8_t hash[hashSz];
+ * uint8_t context[contextSz];
+ */
+} whMessageCrypto_SlhDsaVerifyRequest;
+
+/* SLH-DSA Verify Response */
+typedef struct {
+ uint32_t res;
+ uint8_t WH_PAD[4];
+} whMessageCrypto_SlhDsaVerifyResponse;
+
+int wh_MessageCrypto_TranslateSlhDsaVerifyRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaVerifyRequest* src,
+ whMessageCrypto_SlhDsaVerifyRequest* dest);
+
+int wh_MessageCrypto_TranslateSlhDsaVerifyResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaVerifyResponse* src,
+ whMessageCrypto_SlhDsaVerifyResponse* dest);
+
+/* SLH-DSA Check Private Key Request */
+typedef struct {
+ uint32_t options;
+#define WH_MESSAGE_CRYPTO_SLHDSA_CHECKPRIVKEY_OPTIONS_EVICT (1 << 0)
+ uint32_t param;
+ uint32_t keyId;
+ uint32_t pubSz; /* PK.seed||PK.root, 2n bytes */
+ /* Data follows:
+ * uint8_t pub[pubSz];
+ */
+} whMessageCrypto_SlhDsaCheckPrivKeyRequest;
+
+/* SLH-DSA Check Private Key Response */
+typedef struct {
+ uint32_t res;
+ uint8_t WH_PAD[4];
+} whMessageCrypto_SlhDsaCheckPrivKeyResponse;
+
+int wh_MessageCrypto_TranslateSlhDsaCheckPrivKeyRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaCheckPrivKeyRequest* src,
+ whMessageCrypto_SlhDsaCheckPrivKeyRequest* dest);
+
+int wh_MessageCrypto_TranslateSlhDsaCheckPrivKeyResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaCheckPrivKeyResponse* src,
+ whMessageCrypto_SlhDsaCheckPrivKeyResponse* dest);
+
/*
* ML-KEM
*/
@@ -1690,6 +1826,96 @@ int wh_MessageCrypto_TranslateMlDsaVerifyDmaResponse(
uint16_t magic, const whMessageCrypto_MlDsaVerifyDmaResponse* src,
whMessageCrypto_MlDsaVerifyDmaResponse* dest);
+/* SLH-DSA DMA Key Generation Request */
+typedef struct {
+ whMessageCrypto_DmaBuffer key;
+ whMessageCrypto_DmaBuffer seed; /* sz 0 for random keygen */
+ uint32_t param;
+ 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_PAD[4];
+} whMessageCrypto_SlhDsaKeyGenDmaRequest;
+
+/* SLH-DSA DMA Key Generation Response */
+typedef struct {
+ whMessageCrypto_DmaAddrStatus dmaAddrStatus;
+ uint32_t keyId; /* Assigned key ID */
+ uint32_t keySize; /* Actual size of generated key */
+} whMessageCrypto_SlhDsaKeyGenDmaResponse;
+
+/* SLH-DSA DMA Sign Request */
+typedef struct {
+ whMessageCrypto_DmaBuffer msg; /* Message buffer */
+ whMessageCrypto_DmaBuffer sig; /* Signature buffer */
+ uint32_t options; /* Same options as non-DMA version */
+ uint32_t param; /* enum SlhDsaParam parameter set */
+ uint32_t keyId; /* Key ID to use for signing */
+ uint32_t contextSz; /* FIPS 205 context length (0-255) */
+ uint32_t preHashType; /* enum wc_HashType */
+ uint32_t addRndSz; /* Randomizer length, 0 if none */
+ /* Data follows:
+ * uint8_t context[contextSz];
+ * uint8_t addRnd[addRndSz];
+ */
+} whMessageCrypto_SlhDsaSignDmaRequest;
+
+/* SLH-DSA DMA Sign Response */
+typedef struct {
+ whMessageCrypto_DmaAddrStatus dmaAddrStatus;
+ uint32_t sigLen; /* Actual signature length */
+ uint8_t WH_PAD[4]; /* Pad to 8-byte alignment */
+} whMessageCrypto_SlhDsaSignDmaResponse;
+
+/* SLH-DSA DMA Verify Request */
+typedef struct {
+ whMessageCrypto_DmaBuffer sig; /* Signature buffer */
+ whMessageCrypto_DmaBuffer msg; /* Message buffer */
+ uint32_t options; /* Same options as non-DMA version */
+ uint32_t param; /* enum SlhDsaParam parameter set */
+ uint32_t keyId; /* Key ID to use for verification */
+ uint32_t contextSz; /* FIPS 205 context length (0-255) */
+ uint32_t preHashType; /* enum wc_HashType */
+ uint8_t WH_PAD[4]; /* Pad to 8-byte alignment */
+ /* Data follows:
+ * uint8_t context[contextSz];
+ */
+} whMessageCrypto_SlhDsaVerifyDmaRequest;
+
+/* SLH-DSA DMA Verify Response */
+typedef struct {
+ whMessageCrypto_DmaAddrStatus dmaAddrStatus;
+ int32_t verifyResult; /* Result of verification */
+ uint8_t WH_PAD[4]; /* Pad to 8-byte alignment */
+} whMessageCrypto_SlhDsaVerifyDmaResponse;
+
+/* SLH-DSA DMA translation functions */
+int wh_MessageCrypto_TranslateSlhDsaKeyGenDmaRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaKeyGenDmaRequest* src,
+ whMessageCrypto_SlhDsaKeyGenDmaRequest* dest);
+
+int wh_MessageCrypto_TranslateSlhDsaKeyGenDmaResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaKeyGenDmaResponse* src,
+ whMessageCrypto_SlhDsaKeyGenDmaResponse* dest);
+
+int wh_MessageCrypto_TranslateSlhDsaSignDmaRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaSignDmaRequest* src,
+ whMessageCrypto_SlhDsaSignDmaRequest* dest);
+
+int wh_MessageCrypto_TranslateSlhDsaSignDmaResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaSignDmaResponse* src,
+ whMessageCrypto_SlhDsaSignDmaResponse* dest);
+
+int wh_MessageCrypto_TranslateSlhDsaVerifyDmaRequest(
+ uint16_t magic, const whMessageCrypto_SlhDsaVerifyDmaRequest* src,
+ whMessageCrypto_SlhDsaVerifyDmaRequest* dest);
+
+int wh_MessageCrypto_TranslateSlhDsaVerifyDmaResponse(
+ uint16_t magic, const whMessageCrypto_SlhDsaVerifyDmaResponse* src,
+ whMessageCrypto_SlhDsaVerifyDmaResponse* dest);
+
/* ML-KEM DMA Key Generation Request */
typedef struct {
whMessageCrypto_DmaBuffer key;
diff --git a/wolfhsm/wh_server_crypto.h b/wolfhsm/wh_server_crypto.h
index 739172d37..8698d4b07 100644
--- a/wolfhsm/wh_server_crypto.h
+++ b/wolfhsm/wh_server_crypto.h
@@ -104,6 +104,16 @@ int wh_Server_MlDsaKeyCacheExport(whServerContext* ctx, whKeyId keyId,
wc_MlDsaKey* key);
#endif /* WOLFSSL_HAVE_MLDSA */
+#ifdef WOLFSSL_HAVE_SLHDSA
+/* Store a SlhDsaKey into a server key cache with optional metadata */
+int wh_Server_SlhDsaKeyCacheImport(whServerContext* ctx, SlhDsaKey* key,
+ whKeyId keyId, whNvmFlags flags,
+ uint16_t label_len, uint8_t* label);
+/* Restore a SlhDsaKey from a server key cache */
+int wh_Server_SlhDsaKeyCacheExport(whServerContext* ctx, whKeyId keyId,
+ SlhDsaKey* key);
+#endif /* WOLFSSL_HAVE_SLHDSA */
+
#ifdef WOLFSSL_HAVE_MLKEM
/* Store a MlKemKey into a server key cache with optional metadata */
int wh_Server_MlKemKeyCacheImport(whServerContext* ctx, MlKemKey* key,
diff --git a/wolfhsm/wh_settings.h b/wolfhsm/wh_settings.h
index f9974d905..66fb61219 100644
--- a/wolfhsm/wh_settings.h
+++ b/wolfhsm/wh_settings.h
@@ -136,6 +136,14 @@
* operation in DMA requests.
* Default: Not defined
*
+ * SLH-DSA signatures range from 7856 bytes (128s) to 49856 bytes (256f), so
+ * the non-DMA sign and verify paths only work for a parameter set whose
+ * signature fits WOLFHSM_CFG_COMM_DATA_LEN alongside the message headers.
+ * With the default buffer no parameter set fits; at 8192 only 128s does, and
+ * a maximum-length context with a 64-byte pre-hash still overflows it. The
+ * server reports WH_ERROR_BUFFER_SIZE rather than truncating. Use the DMA
+ * sign and verify calls for the other parameter sets.
+ *
* WOLFHSM_CFG_CERT_MAX_VERIFY_ROOTS - Maximum number of trusted root NVM IDs
* accepted in a single wh_Server_CertVerifyMultiRoot request. Bounded so the
* non-DMA wire request fits within WOLFHSM_CFG_COMM_DATA_LEN alongside the