From e01c4e808a7c36c2644dddf8a6e0eab0d600b9ed Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Tue, 5 May 2026 12:21:30 -0700 Subject: [PATCH] Add support for LMS and XMSS --- src/wh_client_crypto.c | 768 +++++++++++++++ src/wh_client_cryptocb.c | 312 ++++++ src/wh_crypto.c | 279 ++++++ src/wh_message_crypto.c | 179 ++++ src/wh_server_crypto.c | 1366 +++++++++++++++++++++++++-- test/config/user_settings.h | 9 + test/wh_test_check_struct_padding.c | 8 + test/wh_test_crypto.c | 318 +++++++ wolfhsm/wh_client_crypto.h | 71 ++ wolfhsm/wh_crypto.h | 61 ++ wolfhsm/wh_message_crypto.h | 117 +++ wolfhsm/wh_server_crypto.h | 23 + 12 files changed, 3441 insertions(+), 70 deletions(-) diff --git a/src/wh_client_crypto.c b/src/wh_client_crypto.c index 96ec5747e..42f3bb4e6 100644 --- a/src/wh_client_crypto.c +++ b/src/wh_client_crypto.c @@ -55,6 +55,12 @@ #include "wolfssl/wolfcrypt/curve25519.h" #include "wolfssl/wolfcrypt/ed25519.h" #include "wolfssl/wolfcrypt/dilithium.h" +#if defined(WOLFSSL_HAVE_LMS) +#include "wolfssl/wolfcrypt/wc_lms.h" +#endif +#if defined(WOLFSSL_HAVE_XMSS) +#include "wolfssl/wolfcrypt/wc_xmss.h" +#endif #include "wolfssl/wolfcrypt/sha256.h" #include "wolfssl/wolfcrypt/sha512.h" #endif @@ -7970,4 +7976,766 @@ int wh_Client_MlDsaCheckPrivKeyDma(whClientContext* ctx, MlDsaKey* key, #endif /* WOLFHSM_CFG_DMA */ #endif /* HAVE_DILITHIUM */ +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) +#ifdef WOLFHSM_CFG_DMA + +#ifdef WOLFSSL_HAVE_LMS + +int wh_Client_LmsSetKeyId(LmsKey* key, whKeyId keyId) +{ + if (key == NULL) { + return WH_ERROR_BADARGS; + } + key->devCtx = WH_KEYID_TO_DEVCTX(keyId); + return WH_ERROR_OK; +} + +int wh_Client_LmsGetKeyId(LmsKey* 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_LmsMakeKeyDma(whClientContext* ctx, LmsKey* key, + whKeyId* inout_key_id, whNvmFlags flags, + uint16_t label_len, uint8_t* label) +{ + int ret = WH_ERROR_OK; + whKeyId key_id = WH_KEYID_ERASED; + uint8_t* dataPtr; + whMessageCrypto_PqcStatefulSigKeyGenDmaRequest* req; + whMessageCrypto_PqcStatefulSigKeyGenDmaResponse* res; + word32 pubLen32 = 0; + uintptr_t pubAddr = 0; + + if ((ctx == NULL) || (key == NULL) || (key->params == NULL)) { + return WH_ERROR_BADARGS; + } + + ret = wc_LmsKey_GetPubLen(key, &pubLen32); + if (ret != 0) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_PqcStatefulSigKeyGenDmaRequest*) + _createCryptoRequestWithSubtype( + dataPtr, WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN, + WC_PQC_STATEFUL_SIG_TYPE_LMS, ctx->cryptoAffinity); + + if (inout_key_id != NULL) { + key_id = *inout_key_id; + } + + { + uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; + uint16_t action = WC_ALGO_TYPE_PK; + uint16_t req_len = + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); + + if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { + return WH_ERROR_BADARGS; + } + + memset(req, 0, sizeof(*req)); + req->flags = flags; + req->keyId = key_id; + req->access = WH_NVM_ACCESS_ANY; + req->lmsLevels = key->params->levels; + req->lmsHeight = key->params->height; + req->lmsWinternitz = key->params->width; + req->pub.sz = pubLen32; + + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)key->pub, (void**)&pubAddr, pubLen32, + WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); + if (ret == WH_ERROR_OK) { + req->pub.addr = (uint64_t)(uintptr_t)pubAddr; + } + + if ((label != NULL) && (label_len > 0)) { + if (label_len > WH_NVM_LABEL_LEN) { + label_len = WH_NVM_LABEL_LEN; + } + memcpy(req->label, label, label_len); + req->labelSize = label_len; + } + + if (ret == WH_ERROR_OK) { + ret = wh_Client_SendRequest(ctx, group, action, req_len, + (uint8_t*)dataPtr); + } + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len, + (uint8_t*)dataPtr); + } while (ret == WH_ERROR_NOTREADY); + } + + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)key->pub, (void**)&pubAddr, pubLen32, + WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, + WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN, + (uint8_t**)&res); + if (ret >= 0) { + key_id = (whKeyId)res->keyId; + if (inout_key_id != NULL) { + *inout_key_id = key_id; + } + wh_Client_LmsSetKeyId(key, key_id); + } + } + } + + return ret; +} + +int wh_Client_LmsMakeExportKeyDma(whClientContext* ctx, LmsKey* key) +{ + return wh_Client_LmsMakeKeyDma(ctx, key, NULL, WH_NVM_FLAGS_EPHEMERAL, 0, + NULL); +} + +int wh_Client_LmsSignDma(whClientContext* ctx, const byte* msg, word32 msgSz, + byte* sig, word32* sigSz, LmsKey* key) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr; + whMessageCrypto_PqcStatefulSigSignDmaRequest* req; + whMessageCrypto_PqcStatefulSigSignDmaResponse* res; + uintptr_t msgAddr = 0; + uintptr_t sigAddr = 0; + whKeyId key_id; + word32 sigCap; + + if ((ctx == NULL) || (key == NULL) || (msg == NULL) || (sig == NULL) || + (sigSz == NULL)) { + return WH_ERROR_BADARGS; + } + + sigCap = *sigSz; + key_id = WH_DEVCTX_TO_KEYID(key->devCtx); + if (WH_KEYID_ISERASED(key_id)) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_PqcStatefulSigSignDmaRequest*) + _createCryptoRequestWithSubtype( + dataPtr, WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN, + WC_PQC_STATEFUL_SIG_TYPE_LMS, ctx->cryptoAffinity); + + { + uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; + uint16_t action = WC_ALGO_TYPE_PK; + uint16_t req_len = + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); + + if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { + return WH_ERROR_BADARGS; + } + + memset(req, 0, sizeof(*req)); + req->keyId = key_id; + req->options = 0; + req->msg.sz = msgSz; + req->sig.sz = sigCap; + + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)msg, (void**)&msgAddr, msgSz, + WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); + if (ret == WH_ERROR_OK) { + req->msg.addr = (uint64_t)(uintptr_t)msgAddr; + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)sig, (void**)&sigAddr, sigCap, + WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); + } + if (ret == WH_ERROR_OK) { + req->sig.addr = (uint64_t)(uintptr_t)sigAddr; + ret = wh_Client_SendRequest(ctx, group, action, req_len, + (uint8_t*)dataPtr); + } + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len, + (uint8_t*)dataPtr); + } while (ret == WH_ERROR_NOTREADY); + } + + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)msg, (void**)&msgAddr, msgSz, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)sig, (void**)&sigAddr, sigCap, + WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN, + (uint8_t**)&res); + if (ret >= 0) { + if (res->sigLen > sigCap) { + ret = WH_ERROR_BADARGS; + } + else { + *sigSz = res->sigLen; + ret = WH_ERROR_OK; + } + } + } + } + + return ret; +} + +int wh_Client_LmsVerifyDma(whClientContext* ctx, const byte* sig, word32 sigSz, + const byte* msg, word32 msgSz, int* res, LmsKey* key) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr; + whMessageCrypto_PqcStatefulSigVerifyDmaRequest* req; + whMessageCrypto_PqcStatefulSigVerifyDmaResponse* resp; + uintptr_t sigAddr = 0; + uintptr_t msgAddr = 0; + whKeyId key_id; + + if ((ctx == NULL) || (key == NULL) || (sig == NULL) || (msg == NULL) || + (res == NULL)) { + return WH_ERROR_BADARGS; + } + + key_id = WH_DEVCTX_TO_KEYID(key->devCtx); + if (WH_KEYID_ISERASED(key_id)) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_PqcStatefulSigVerifyDmaRequest*) + _createCryptoRequestWithSubtype( + dataPtr, WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY, + WC_PQC_STATEFUL_SIG_TYPE_LMS, ctx->cryptoAffinity); + + { + uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; + uint16_t action = WC_ALGO_TYPE_PK; + uint16_t req_len = + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); + + if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { + return WH_ERROR_BADARGS; + } + + memset(req, 0, sizeof(*req)); + req->keyId = key_id; + req->sig.sz = sigSz; + req->msg.sz = msgSz; + + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)sig, (void**)&sigAddr, sigSz, + WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); + if (ret == WH_ERROR_OK) { + req->sig.addr = (uint64_t)(uintptr_t)sigAddr; + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)msg, (void**)&msgAddr, msgSz, + WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); + } + if (ret == WH_ERROR_OK) { + req->msg.addr = (uint64_t)(uintptr_t)msgAddr; + ret = wh_Client_SendRequest(ctx, group, action, req_len, + (uint8_t*)dataPtr); + } + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len, + (uint8_t*)dataPtr); + } while (ret == WH_ERROR_NOTREADY); + } + + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)sig, (void**)&sigAddr, sigSz, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)msg, (void**)&msgAddr, msgSz, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, + WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY, + (uint8_t**)&resp); + if (ret >= 0) { + *res = (int)resp->res; + ret = WH_ERROR_OK; + } + } + } + + return ret; +} + +int wh_Client_LmsSigsLeftDma(whClientContext* ctx, LmsKey* key, + word32* sigsLeft) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr; + whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest* req; + whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse* res; + whKeyId key_id; + + if ((ctx == NULL) || (key == NULL) || (sigsLeft == NULL)) { + return WH_ERROR_BADARGS; + } + + key_id = WH_DEVCTX_TO_KEYID(key->devCtx); + if (WH_KEYID_ISERASED(key_id)) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest*) + _createCryptoRequestWithSubtype( + dataPtr, WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT, + WC_PQC_STATEFUL_SIG_TYPE_LMS, ctx->cryptoAffinity); + + { + uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; + uint16_t action = WC_ALGO_TYPE_PK; + uint16_t req_len = + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); + + memset(req, 0, sizeof(*req)); + req->keyId = key_id; + + ret = wh_Client_SendRequest(ctx, group, action, req_len, + (uint8_t*)dataPtr); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len, + (uint8_t*)dataPtr); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, + WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT, + (uint8_t**)&res); + if (ret >= 0) { + *sigsLeft = res->sigsLeft; + ret = WH_ERROR_OK; + } + } + } + + return ret; +} + +#endif /* WOLFSSL_HAVE_LMS */ + +#ifdef WOLFSSL_HAVE_XMSS + +int wh_Client_XmssSetKeyId(XmssKey* key, whKeyId keyId) +{ + if (key == NULL) { + return WH_ERROR_BADARGS; + } + key->devCtx = WH_KEYID_TO_DEVCTX(keyId); + return WH_ERROR_OK; +} + +int wh_Client_XmssGetKeyId(XmssKey* key, whKeyId* outId) +{ + if (key == NULL || outId == NULL) { + return WH_ERROR_BADARGS; + } + *outId = WH_DEVCTX_TO_KEYID(key->devCtx); + return WH_ERROR_OK; +} + +/* The XMSS implementations mirror the LMS ones; the only differences are the + * subType passed to _createCryptoRequestWithSubtype and the key field names + * (key->pk instead of key->pub, key->params is XmssParams). */ +int wh_Client_XmssMakeKeyDma(whClientContext* ctx, XmssKey* key, + whKeyId* inout_key_id, whNvmFlags flags, + uint16_t label_len, uint8_t* label) +{ + int ret = WH_ERROR_OK; + whKeyId key_id = WH_KEYID_ERASED; + uint8_t* dataPtr; + whMessageCrypto_PqcStatefulSigKeyGenDmaRequest* req; + whMessageCrypto_PqcStatefulSigKeyGenDmaResponse* res; + word32 pubLen32 = 0; + uintptr_t pubAddr = 0; + + if ((ctx == NULL) || (key == NULL) || (key->params == NULL)) { + return WH_ERROR_BADARGS; + } + + ret = wc_XmssKey_GetPubLen(key, &pubLen32); + if (ret != 0) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_PqcStatefulSigKeyGenDmaRequest*) + _createCryptoRequestWithSubtype( + dataPtr, WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN, + WC_PQC_STATEFUL_SIG_TYPE_XMSS, ctx->cryptoAffinity); + + if (inout_key_id != NULL) { + key_id = *inout_key_id; + } + + { + uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; + uint16_t action = WC_ALGO_TYPE_PK; + uint16_t req_len = + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); + + if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { + return WH_ERROR_BADARGS; + } + + memset(req, 0, sizeof(*req)); + req->flags = flags; + req->keyId = key_id; + req->access = WH_NVM_ACCESS_ANY; + req->pub.sz = pubLen32; + + { + const char* paramStr = NULL; + ret = wc_XmssKey_GetParamStr(key, ¶mStr); + if (ret != 0) { + return WH_ERROR_BADARGS; + } + if (XSTRLEN(paramStr) >= sizeof(req->xmssParamStr)) { + return WH_ERROR_BADARGS; + } + XSTRNCPY(req->xmssParamStr, paramStr, sizeof(req->xmssParamStr)); + req->xmssParamStr[sizeof(req->xmssParamStr) - 1] = '\0'; + } + + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)key->pk, (void**)&pubAddr, pubLen32, + WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); + if (ret == WH_ERROR_OK) { + req->pub.addr = (uint64_t)(uintptr_t)pubAddr; + } + + if ((label != NULL) && (label_len > 0)) { + if (label_len > WH_NVM_LABEL_LEN) { + label_len = WH_NVM_LABEL_LEN; + } + memcpy(req->label, label, label_len); + req->labelSize = label_len; + } + + if (ret == WH_ERROR_OK) { + ret = wh_Client_SendRequest(ctx, group, action, req_len, + (uint8_t*)dataPtr); + } + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len, + (uint8_t*)dataPtr); + } while (ret == WH_ERROR_NOTREADY); + } + + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)key->pk, (void**)&pubAddr, pubLen32, + WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, + WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN, + (uint8_t**)&res); + if (ret >= 0) { + key_id = (whKeyId)res->keyId; + if (inout_key_id != NULL) { + *inout_key_id = key_id; + } + wh_Client_XmssSetKeyId(key, key_id); + } + } + } + + return ret; +} + +int wh_Client_XmssMakeExportKeyDma(whClientContext* ctx, XmssKey* key) +{ + return wh_Client_XmssMakeKeyDma(ctx, key, NULL, WH_NVM_FLAGS_EPHEMERAL, 0, + NULL); +} + +int wh_Client_XmssSignDma(whClientContext* ctx, const byte* msg, word32 msgSz, + byte* sig, word32* sigSz, XmssKey* key) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr; + whMessageCrypto_PqcStatefulSigSignDmaRequest* req; + whMessageCrypto_PqcStatefulSigSignDmaResponse* res; + uintptr_t msgAddr = 0; + uintptr_t sigAddr = 0; + whKeyId key_id; + word32 sigCap; + + if ((ctx == NULL) || (key == NULL) || (msg == NULL) || (sig == NULL) || + (sigSz == NULL)) { + return WH_ERROR_BADARGS; + } + + sigCap = *sigSz; + key_id = WH_DEVCTX_TO_KEYID(key->devCtx); + if (WH_KEYID_ISERASED(key_id)) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_PqcStatefulSigSignDmaRequest*) + _createCryptoRequestWithSubtype( + dataPtr, WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN, + WC_PQC_STATEFUL_SIG_TYPE_XMSS, ctx->cryptoAffinity); + + { + uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; + uint16_t action = WC_ALGO_TYPE_PK; + uint16_t req_len = + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); + + if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { + return WH_ERROR_BADARGS; + } + + memset(req, 0, sizeof(*req)); + req->keyId = key_id; + req->options = 0; + req->msg.sz = msgSz; + req->sig.sz = sigCap; + + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)msg, (void**)&msgAddr, msgSz, + WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); + if (ret == WH_ERROR_OK) { + req->msg.addr = (uint64_t)(uintptr_t)msgAddr; + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)sig, (void**)&sigAddr, sigCap, + WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); + } + if (ret == WH_ERROR_OK) { + req->sig.addr = (uint64_t)(uintptr_t)sigAddr; + ret = wh_Client_SendRequest(ctx, group, action, req_len, + (uint8_t*)dataPtr); + } + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len, + (uint8_t*)dataPtr); + } while (ret == WH_ERROR_NOTREADY); + } + + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)msg, (void**)&msgAddr, msgSz, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)sig, (void**)&sigAddr, sigCap, + WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN, + (uint8_t**)&res); + if (ret >= 0) { + if (res->sigLen > sigCap) { + ret = WH_ERROR_BADARGS; + } + else { + *sigSz = res->sigLen; + ret = WH_ERROR_OK; + } + } + } + } + + return ret; +} + +int wh_Client_XmssVerifyDma(whClientContext* ctx, const byte* sig, + word32 sigSz, const byte* msg, word32 msgSz, + int* res, XmssKey* key) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr; + whMessageCrypto_PqcStatefulSigVerifyDmaRequest* req; + whMessageCrypto_PqcStatefulSigVerifyDmaResponse* resp; + uintptr_t sigAddr = 0; + uintptr_t msgAddr = 0; + whKeyId key_id; + + if ((ctx == NULL) || (key == NULL) || (sig == NULL) || (msg == NULL) || + (res == NULL)) { + return WH_ERROR_BADARGS; + } + + key_id = WH_DEVCTX_TO_KEYID(key->devCtx); + if (WH_KEYID_ISERASED(key_id)) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_PqcStatefulSigVerifyDmaRequest*) + _createCryptoRequestWithSubtype( + dataPtr, WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY, + WC_PQC_STATEFUL_SIG_TYPE_XMSS, ctx->cryptoAffinity); + + { + uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; + uint16_t action = WC_ALGO_TYPE_PK; + uint16_t req_len = + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); + + if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { + return WH_ERROR_BADARGS; + } + + memset(req, 0, sizeof(*req)); + req->keyId = key_id; + req->sig.sz = sigSz; + req->msg.sz = msgSz; + + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)sig, (void**)&sigAddr, sigSz, + WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); + if (ret == WH_ERROR_OK) { + req->sig.addr = (uint64_t)(uintptr_t)sigAddr; + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)msg, (void**)&msgAddr, msgSz, + WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); + } + if (ret == WH_ERROR_OK) { + req->msg.addr = (uint64_t)(uintptr_t)msgAddr; + ret = wh_Client_SendRequest(ctx, group, action, req_len, + (uint8_t*)dataPtr); + } + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len, + (uint8_t*)dataPtr); + } while (ret == WH_ERROR_NOTREADY); + } + + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)sig, (void**)&sigAddr, sigSz, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)msg, (void**)&msgAddr, msgSz, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, + WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY, + (uint8_t**)&resp); + if (ret >= 0) { + *res = (int)resp->res; + ret = WH_ERROR_OK; + } + } + } + + return ret; +} + +int wh_Client_XmssSigsLeftDma(whClientContext* ctx, XmssKey* key, + word32* sigsLeft) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr; + whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest* req; + whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse* res; + whKeyId key_id; + + if ((ctx == NULL) || (key == NULL) || (sigsLeft == NULL)) { + return WH_ERROR_BADARGS; + } + + key_id = WH_DEVCTX_TO_KEYID(key->devCtx); + if (WH_KEYID_ISERASED(key_id)) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest*) + _createCryptoRequestWithSubtype( + dataPtr, WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT, + WC_PQC_STATEFUL_SIG_TYPE_XMSS, ctx->cryptoAffinity); + + { + uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; + uint16_t action = WC_ALGO_TYPE_PK; + uint16_t req_len = + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); + + memset(req, 0, sizeof(*req)); + req->keyId = key_id; + + ret = wh_Client_SendRequest(ctx, group, action, req_len, + (uint8_t*)dataPtr); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_RecvResponse(ctx, &group, &action, &req_len, + (uint8_t*)dataPtr); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, + WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT, + (uint8_t**)&res); + if (ret >= 0) { + *sigsLeft = res->sigsLeft; + ret = WH_ERROR_OK; + } + } + } + + return ret; +} + +#endif /* WOLFSSL_HAVE_XMSS */ + +#endif /* WOLFHSM_CFG_DMA */ +#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */ + #endif /* !WOLFHSM_CFG_NO_CRYPTO && WOLFHSM_CFG_ENABLE_CLIENT */ diff --git a/src/wh_client_cryptocb.c b/src/wh_client_cryptocb.c index 48c620d68..32b5f281c 100644 --- a/src/wh_client_cryptocb.c +++ b/src/wh_client_cryptocb.c @@ -47,6 +47,12 @@ #include "wolfssl/wolfcrypt/ecc.h" #include "wolfssl/wolfcrypt/sha256.h" #include "wolfssl/wolfcrypt/sha512.h" +#if defined(WOLFSSL_HAVE_LMS) +#include "wolfssl/wolfcrypt/wc_lms.h" +#endif +#if defined(WOLFSSL_HAVE_XMSS) +#include "wolfssl/wolfcrypt/wc_xmss.h" +#endif #include "wolfhsm/wh_crypto.h" #include "wolfhsm/wh_client_crypto.h" @@ -54,6 +60,17 @@ #include "wolfhsm/wh_message_crypto.h" +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) +static int _handlePqcStatefulSigKeyGen(whClientContext* ctx, + wc_CryptoInfo* info, int useDma); +static int _handlePqcStatefulSigSign(whClientContext* ctx, wc_CryptoInfo* info, + int useDma); +static int _handlePqcStatefulSigVerify(whClientContext* ctx, + wc_CryptoInfo* info, int useDma); +static int _handlePqcStatefulSigSigsLeft(whClientContext* ctx, + wc_CryptoInfo* info, int useDma); +#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */ + #if defined(HAVE_DILITHIUM) || defined(HAVE_FALCON) static int _handlePqcSigKeyGen(whClientContext* ctx, wc_CryptoInfo* info, int useDma); @@ -422,6 +439,25 @@ int wh_Client_CryptoCb(int devId, wc_CryptoInfo* info, void* inCtx) #endif /* HAVE_ED25519 */ #endif /* HAVE_CURVE25519 */ +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) + case WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN: + ret = _handlePqcStatefulSigKeyGen(ctx, info, 0); + break; + + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN: + ret = _handlePqcStatefulSigSign(ctx, info, 0); + break; + + case WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY: + ret = _handlePqcStatefulSigVerify(ctx, info, 0); + break; + + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT: + ret = _handlePqcStatefulSigSigsLeft(ctx, info, 0); + break; + +#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */ + #if defined(HAVE_DILITHIUM) || defined(HAVE_FALCON) case WC_PK_TYPE_PQC_SIG_KEYGEN: ret = _handlePqcSigKeyGen(ctx, info, 0); @@ -600,6 +636,268 @@ int wh_Client_CryptoCb(int devId, wc_CryptoInfo* info, void* inCtx) return ret; } +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) +static int _handlePqcStatefulSigKeyGen(whClientContext* ctx, + wc_CryptoInfo* info, int useDma) +{ + int ret = CRYPTOCB_UNAVAILABLE; + int type = info->pk.pqc_stateful_sig_kg.type; + +#ifndef WOLFHSM_CFG_DMA + (void)ctx; + if (useDma) { + return WC_HW_E; + } +#endif + + switch (type) { +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_LMS: +#ifdef WOLFHSM_CFG_DMA + if (useDma) { + ret = wh_Client_LmsMakeExportKeyDma( + ctx, (LmsKey*)info->pk.pqc_stateful_sig_kg.key); + } + else +#endif /* WOLFHSM_CFG_DMA */ + { + /* Non-DMA transport not supported in v1; signatures exceed the + * default WOLFHSM_CFG_COMM_DATA_LEN. */ + ret = CRYPTOCB_UNAVAILABLE; + } + break; +#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */ +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: +#ifdef WOLFHSM_CFG_DMA + if (useDma) { + ret = wh_Client_XmssMakeExportKeyDma( + ctx, (XmssKey*)info->pk.pqc_stateful_sig_kg.key); + } + else +#endif /* WOLFHSM_CFG_DMA */ + { + ret = CRYPTOCB_UNAVAILABLE; + } + break; +#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */ + + default: + ret = CRYPTOCB_UNAVAILABLE; + break; + } + + if (ret == WH_ERROR_BADARGS) { + ret = BAD_FUNC_ARG; + } + else if (ret == WH_ERROR_NOTIMPL) { + ret = CRYPTOCB_UNAVAILABLE; + } + + return ret; +} + +static int _handlePqcStatefulSigSign(whClientContext* ctx, wc_CryptoInfo* info, + int useDma) +{ + int ret = CRYPTOCB_UNAVAILABLE; + int type = info->pk.pqc_stateful_sig_sign.type; + +#ifndef WOLFHSM_CFG_DMA + (void)ctx; + if (useDma) { + return WC_HW_E; + } +#endif + + switch (type) { +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_LMS: +#ifdef WOLFHSM_CFG_DMA + if (useDma) { + ret = wh_Client_LmsSignDma( + ctx, + info->pk.pqc_stateful_sig_sign.msg, + info->pk.pqc_stateful_sig_sign.msgSz, + info->pk.pqc_stateful_sig_sign.out, + info->pk.pqc_stateful_sig_sign.outSz, + (LmsKey*)info->pk.pqc_stateful_sig_sign.key); + } + else +#endif /* WOLFHSM_CFG_DMA */ + { + ret = CRYPTOCB_UNAVAILABLE; + } + break; +#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */ +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: +#ifdef WOLFHSM_CFG_DMA + if (useDma) { + ret = wh_Client_XmssSignDma( + ctx, + info->pk.pqc_stateful_sig_sign.msg, + info->pk.pqc_stateful_sig_sign.msgSz, + info->pk.pqc_stateful_sig_sign.out, + info->pk.pqc_stateful_sig_sign.outSz, + (XmssKey*)info->pk.pqc_stateful_sig_sign.key); + } + else +#endif /* WOLFHSM_CFG_DMA */ + { + ret = CRYPTOCB_UNAVAILABLE; + } + break; +#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */ + + default: + ret = CRYPTOCB_UNAVAILABLE; + break; + } + + if (ret == WH_ERROR_BADARGS) { + ret = BAD_FUNC_ARG; + } + else if (ret == WH_ERROR_NOTIMPL) { + ret = CRYPTOCB_UNAVAILABLE; + } + + return ret; +} + +static int _handlePqcStatefulSigVerify(whClientContext* ctx, + wc_CryptoInfo* info, int useDma) +{ + int ret = CRYPTOCB_UNAVAILABLE; + int type = info->pk.pqc_stateful_sig_verify.type; + +#ifndef WOLFHSM_CFG_DMA + (void)ctx; + if (useDma) { + return WC_HW_E; + } +#endif + + switch (type) { +#ifdef WOLFSSL_HAVE_LMS + case WC_PQC_STATEFUL_SIG_TYPE_LMS: +#ifdef WOLFHSM_CFG_DMA + if (useDma) { + ret = wh_Client_LmsVerifyDma( + ctx, + info->pk.pqc_stateful_sig_verify.sig, + info->pk.pqc_stateful_sig_verify.sigSz, + info->pk.pqc_stateful_sig_verify.msg, + info->pk.pqc_stateful_sig_verify.msgSz, + info->pk.pqc_stateful_sig_verify.res, + (LmsKey*)info->pk.pqc_stateful_sig_verify.key); + } + else +#endif /* WOLFHSM_CFG_DMA */ + { + ret = CRYPTOCB_UNAVAILABLE; + } + break; +#endif /* WOLFSSL_HAVE_LMS */ +#ifdef WOLFSSL_HAVE_XMSS + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: +#ifdef WOLFHSM_CFG_DMA + if (useDma) { + ret = wh_Client_XmssVerifyDma( + ctx, + info->pk.pqc_stateful_sig_verify.sig, + info->pk.pqc_stateful_sig_verify.sigSz, + info->pk.pqc_stateful_sig_verify.msg, + info->pk.pqc_stateful_sig_verify.msgSz, + info->pk.pqc_stateful_sig_verify.res, + (XmssKey*)info->pk.pqc_stateful_sig_verify.key); + } + else +#endif /* WOLFHSM_CFG_DMA */ + { + ret = CRYPTOCB_UNAVAILABLE; + } + break; +#endif /* WOLFSSL_HAVE_XMSS */ + + default: + ret = CRYPTOCB_UNAVAILABLE; + break; + } + + if (ret == WH_ERROR_BADARGS) { + ret = BAD_FUNC_ARG; + } + else if (ret == WH_ERROR_NOTIMPL) { + ret = CRYPTOCB_UNAVAILABLE; + } + + return ret; +} + +static int _handlePqcStatefulSigSigsLeft(whClientContext* ctx, + wc_CryptoInfo* info, int useDma) +{ + int ret = CRYPTOCB_UNAVAILABLE; + int type = info->pk.pqc_stateful_sig_sigs_left.type; + +#ifndef WOLFHSM_CFG_DMA + (void)ctx; + if (useDma) { + return WC_HW_E; + } +#endif + + switch (type) { +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_LMS: +#ifdef WOLFHSM_CFG_DMA + if (useDma) { + ret = wh_Client_LmsSigsLeftDma( + ctx, + (LmsKey*)info->pk.pqc_stateful_sig_sigs_left.key, + info->pk.pqc_stateful_sig_sigs_left.sigsLeft); + } + else +#endif /* WOLFHSM_CFG_DMA */ + { + ret = CRYPTOCB_UNAVAILABLE; + } + break; +#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */ +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: +#ifdef WOLFHSM_CFG_DMA + if (useDma) { + ret = wh_Client_XmssSigsLeftDma( + ctx, + (XmssKey*)info->pk.pqc_stateful_sig_sigs_left.key, + info->pk.pqc_stateful_sig_sigs_left.sigsLeft); + } + else +#endif /* WOLFHSM_CFG_DMA */ + { + ret = CRYPTOCB_UNAVAILABLE; + } + break; +#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */ + + default: + ret = CRYPTOCB_UNAVAILABLE; + break; + } + + if (ret == WH_ERROR_BADARGS) { + ret = BAD_FUNC_ARG; + } + else if (ret == WH_ERROR_NOTIMPL) { + ret = CRYPTOCB_UNAVAILABLE; + } + + return ret; +} +#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */ + #if defined(HAVE_FALCON) || defined(HAVE_DILITHIUM) static int _handlePqcSigKeyGen(whClientContext* ctx, wc_CryptoInfo* info, int useDma) @@ -864,6 +1162,20 @@ int wh_Client_CryptoCbDma(int devId, wc_CryptoInfo* info, void* inCtx) case WC_ALGO_TYPE_PK: { switch (info->pk.type) { +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) + case WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN: + ret = _handlePqcStatefulSigKeyGen(ctx, info, 1); + break; + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN: + ret = _handlePqcStatefulSigSign(ctx, info, 1); + break; + case WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY: + ret = _handlePqcStatefulSigVerify(ctx, info, 1); + break; + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT: + ret = _handlePqcStatefulSigSigsLeft(ctx, info, 1); + break; +#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */ #if defined(HAVE_DILITHIUM) || defined(HAVE_FALCON) case WC_PK_TYPE_PQC_SIG_KEYGEN: ret = _handlePqcSigKeyGen(ctx, info, 1); diff --git a/src/wh_crypto.c b/src/wh_crypto.c index d43c73ff8..c944fea94 100644 --- a/src/wh_crypto.c +++ b/src/wh_crypto.c @@ -44,6 +44,12 @@ #include "wolfssl/wolfcrypt/ecc.h" #include "wolfssl/wolfcrypt/ed25519.h" #include "wolfssl/wolfcrypt/dilithium.h" +#if defined(WOLFSSL_HAVE_LMS) +#include "wolfssl/wolfcrypt/wc_lms.h" +#endif +#if defined(WOLFSSL_HAVE_XMSS) +#include "wolfssl/wolfcrypt/wc_xmss.h" +#endif #include "wolfhsm/wh_error.h" #include "wolfhsm/wh_utils.h" @@ -379,6 +385,279 @@ int wh_Crypto_MlDsaDeserializeKeyDer(const uint8_t* buffer, uint16_t size, } #endif /* HAVE_DILITHIUM */ +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) +/* Stateful hash-based signature key serialization helpers. + * + * Slot blob layout: + * uint32_t magic; + * uint16_t pubLen; + * uint16_t privLen; + * uint16_t paramLen; + * uint16_t reserved; + * uint8_t paramDescriptor[paramLen]; + * uint8_t pub[pubLen]; + * uint8_t priv[privLen]; + * + * Native byte order: the blob is server-internal (NVM-stored) and never + * traverses the wire. */ + +#define WH_CRYPTO_STATEFUL_SIG_HEADER_SZ 12 /* magic + 4*uint16 */ + +static int _StatefulSigEncodeHeader(uint8_t* buffer, uint32_t magic, + uint16_t pubLen, uint16_t privLen, + uint16_t paramLen) +{ + uint16_t reserved = 0; + memcpy(buffer + 0, &magic, sizeof(magic)); + memcpy(buffer + 4, &pubLen, sizeof(pubLen)); + memcpy(buffer + 6, &privLen, sizeof(privLen)); + memcpy(buffer + 8, ¶mLen, sizeof(paramLen)); + memcpy(buffer + 10, &reserved, sizeof(reserved)); + return WH_ERROR_OK; +} + +static int _StatefulSigDecodeHeader(const uint8_t* buffer, uint16_t size, + uint32_t expectMagic, uint16_t* pubLen, + uint16_t* privLen, uint16_t* paramLen) +{ + uint32_t magic; + + if (size < WH_CRYPTO_STATEFUL_SIG_HEADER_SZ) { + return WH_ERROR_BADARGS; + } + memcpy(&magic, buffer + 0, sizeof(magic)); + if (magic != expectMagic) { + return WH_ERROR_BADARGS; + } + memcpy(pubLen, buffer + 4, sizeof(*pubLen)); + memcpy(privLen, buffer + 6, sizeof(*privLen)); + memcpy(paramLen, buffer + 8, sizeof(*paramLen)); + if ((uint32_t)WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + *paramLen + *pubLen + + *privLen > size) { + return WH_ERROR_BADARGS; + } + return WH_ERROR_OK; +} +#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */ + +#ifdef WOLFSSL_HAVE_LMS +int wh_Crypto_LmsSerializeKey(LmsKey* key, uint16_t max_size, uint8_t* buffer, + uint16_t* out_size) +{ + word32 pubLen32 = 0; + uint16_t pubLen; + uint16_t privLen; + uint16_t paramLen = 3; /* levels, height, winternitz */ + uint32_t totalLen; + int ret; + + if ((key == NULL) || (buffer == NULL) || (out_size == NULL) || + (key->params == NULL)) { + return WH_ERROR_BADARGS; + } + + ret = wc_LmsKey_GetPubLen(key, &pubLen32); + if (ret != 0) { + return WH_ERROR_BADARGS; + } + pubLen = (uint16_t)pubLen32; + privLen = (uint16_t)HSS_PRIVATE_KEY_LEN(key->params->hash_len); + + totalLen = (uint32_t)WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + paramLen + pubLen + + privLen; + if (totalLen > max_size) { + return WH_ERROR_BUFFER_SIZE; + } + + (void)_StatefulSigEncodeHeader(buffer, + WH_CRYPTO_STATEFUL_SIG_BLOB_MAGIC_LMS, + pubLen, privLen, paramLen); + + /* paramDescriptor: levels, height, winternitz */ + buffer[WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + 0] = key->params->levels; + buffer[WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + 1] = key->params->height; + buffer[WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + 2] = key->params->width; + + memcpy(buffer + WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + paramLen, + key->pub, pubLen); + memcpy(buffer + WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + paramLen + pubLen, + key->priv_raw, privLen); + + *out_size = (uint16_t)totalLen; + return WH_ERROR_OK; +} + +int wh_Crypto_LmsDeserializeKey(const uint8_t* buffer, uint16_t size, + LmsKey* key) +{ + uint16_t pubLen; + uint16_t privLen; + uint16_t paramLen; + word32 expectPubLen = 0; + int ret; + int levels; + int height; + int winternitz; + const uint8_t* p; + + if ((buffer == NULL) || (key == NULL)) { + return WH_ERROR_BADARGS; + } + + ret = _StatefulSigDecodeHeader(buffer, size, + WH_CRYPTO_STATEFUL_SIG_BLOB_MAGIC_LMS, + &pubLen, &privLen, ¶mLen); + if (ret != WH_ERROR_OK) { + return ret; + } + if (paramLen != 3) { + return WH_ERROR_BADARGS; + } + + p = buffer + WH_CRYPTO_STATEFUL_SIG_HEADER_SZ; + levels = (int)p[0]; + height = (int)p[1]; + winternitz = (int)p[2]; + + ret = wc_LmsKey_SetParameters(key, levels, height, winternitz); + if (ret != 0) { + return ret; + } + + /* Sanity-check pub size against the bound parameter set */ + ret = wc_LmsKey_GetPubLen(key, &expectPubLen); + if ((ret != 0) || (expectPubLen != pubLen)) { + return WH_ERROR_BADARGS; + } + if (privLen != (uint16_t)HSS_PRIVATE_KEY_LEN(key->params->hash_len)) { + return WH_ERROR_BADARGS; + } + + p = buffer + WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + paramLen; + memcpy(key->pub, p, pubLen); + p += pubLen; + memcpy(key->priv_raw, p, privLen); + + return WH_ERROR_OK; +} +#endif /* WOLFSSL_HAVE_LMS */ + +#ifdef WOLFSSL_HAVE_XMSS +int wh_Crypto_XmssSerializeKey(XmssKey* key, const char* paramStr, + uint16_t max_size, uint8_t* buffer, + uint16_t* out_size) +{ + word32 pubLen32 = 0; + word32 privLen32 = 0; + uint16_t pubLen; + uint16_t privLen; + uint16_t paramLen; + uint32_t totalLen; + size_t strLen; + int ret; + + if ((key == NULL) || (paramStr == NULL) || (buffer == NULL) || + (out_size == NULL) || (key->params == NULL) || (key->sk == NULL)) { + return WH_ERROR_BADARGS; + } + + ret = wc_XmssKey_GetPubLen(key, &pubLen32); + if (ret != 0) { + return WH_ERROR_BADARGS; + } + ret = wc_XmssKey_GetPrivLen(key, &privLen32); + if (ret != 0) { + return WH_ERROR_BADARGS; + } + pubLen = (uint16_t)pubLen32; + privLen = (uint16_t)privLen32; + + strLen = strlen(paramStr); + if (strLen >= 0xFFFFu) { + return WH_ERROR_BADARGS; + } + paramLen = (uint16_t)(strLen + 1); /* include NUL */ + + totalLen = (uint32_t)WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + paramLen + pubLen + + privLen; + if (totalLen > max_size) { + return WH_ERROR_BUFFER_SIZE; + } + + (void)_StatefulSigEncodeHeader(buffer, + WH_CRYPTO_STATEFUL_SIG_BLOB_MAGIC_XMSS, + pubLen, privLen, paramLen); + + memcpy(buffer + WH_CRYPTO_STATEFUL_SIG_HEADER_SZ, paramStr, paramLen); + memcpy(buffer + WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + paramLen, + key->pk, pubLen); + memcpy(buffer + WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + paramLen + pubLen, + key->sk, privLen); + + *out_size = (uint16_t)totalLen; + return WH_ERROR_OK; +} + +int wh_Crypto_XmssDeserializeKey(const uint8_t* buffer, uint16_t size, + XmssKey* key) +{ + uint16_t pubLen; + uint16_t privLen; + uint16_t paramLen; + word32 expectPubLen = 0; + word32 expectPrivLen = 0; + int ret; + const char* paramStr; + const uint8_t* p; + + if ((buffer == NULL) || (key == NULL)) { + return WH_ERROR_BADARGS; + } + + ret = _StatefulSigDecodeHeader(buffer, size, + WH_CRYPTO_STATEFUL_SIG_BLOB_MAGIC_XMSS, + &pubLen, &privLen, ¶mLen); + if (ret != WH_ERROR_OK) { + return ret; + } + if (paramLen == 0) { + return WH_ERROR_BADARGS; + } + + /* paramDescriptor must be NUL-terminated and within paramLen */ + paramStr = (const char*)(buffer + WH_CRYPTO_STATEFUL_SIG_HEADER_SZ); + if (paramStr[paramLen - 1] != '\0') { + return WH_ERROR_BADARGS; + } + + /* SetParamStr binds key->params; sk is allocated later by Reload via + * the read callback path (or directly if the caller wants to pre-load + * it). */ + ret = wc_XmssKey_SetParamStr(key, paramStr); + if (ret != 0) { + return ret; + } + + ret = wc_XmssKey_GetPubLen(key, &expectPubLen); + if ((ret != 0) || (expectPubLen != pubLen)) { + return WH_ERROR_BADARGS; + } + ret = wc_XmssKey_GetPrivLen(key, &expectPrivLen); + if ((ret != 0) || (expectPrivLen != privLen)) { + return WH_ERROR_BADARGS; + } + + p = buffer + WH_CRYPTO_STATEFUL_SIG_HEADER_SZ + paramLen; + memcpy(key->pk, p, pubLen); + /* The private key is left in the slot blob; downstream paths read it + * via the bridge ReadCb against the cached slot (sk is allocated by + * Reload, not by deserialize). */ + (void)privLen; + + return WH_ERROR_OK; +} +#endif /* WOLFSSL_HAVE_XMSS */ + #ifdef WOLFSSL_CMAC void wh_Crypto_CmacAesSaveStateToMsg(whMessageCrypto_CmacAesState* state, const Cmac* cmac) diff --git a/src/wh_message_crypto.c b/src/wh_message_crypto.c index a4e6d697b..793860662 100644 --- a/src/wh_message_crypto.c +++ b/src/wh_message_crypto.c @@ -1143,6 +1143,185 @@ int wh_MessageCrypto_TranslateMlDsaVerifyDmaResponse( return 0; } +/* Stateful sig DMA Key Generation Request translation */ +int wh_MessageCrypto_TranslatePqcStatefulSigKeyGenDmaRequest( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigKeyGenDmaRequest* src, + whMessageCrypto_PqcStatefulSigKeyGenDmaRequest* dest) +{ + int ret; + + if ((src == NULL) || (dest == NULL)) { + return WH_ERROR_BADARGS; + } + + ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->pub, &dest->pub); + if (ret != 0) { + return ret; + } + + WH_T32(magic, dest, src, flags); + WH_T32(magic, dest, src, keyId); + WH_T32(magic, dest, src, access); + WH_T32(magic, dest, src, labelSize); + WH_T32(magic, dest, src, lmsLevels); + WH_T32(magic, dest, src, lmsHeight); + WH_T32(magic, dest, src, lmsWinternitz); + if (src != dest) { + memcpy(dest->label, src->label, sizeof(src->label)); + memcpy(dest->xmssParamStr, src->xmssParamStr, + sizeof(src->xmssParamStr)); + } + return 0; +} + +/* Stateful sig DMA Key Generation Response translation */ +int wh_MessageCrypto_TranslatePqcStatefulSigKeyGenDmaResponse( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigKeyGenDmaResponse* src, + whMessageCrypto_PqcStatefulSigKeyGenDmaResponse* 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, pubSize); + return 0; +} + +/* Stateful sig DMA Sign Request translation */ +int wh_MessageCrypto_TranslatePqcStatefulSigSignDmaRequest( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigSignDmaRequest* src, + whMessageCrypto_PqcStatefulSigSignDmaRequest* 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, keyId); + return 0; +} + +/* Stateful sig DMA Sign Response translation */ +int wh_MessageCrypto_TranslatePqcStatefulSigSignDmaResponse( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigSignDmaResponse* src, + whMessageCrypto_PqcStatefulSigSignDmaResponse* 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; +} + +/* Stateful sig DMA Verify Request translation */ +int wh_MessageCrypto_TranslatePqcStatefulSigVerifyDmaRequest( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigVerifyDmaRequest* src, + whMessageCrypto_PqcStatefulSigVerifyDmaRequest* 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, keyId); + return 0; +} + +/* Stateful sig DMA Verify Response translation */ +int wh_MessageCrypto_TranslatePqcStatefulSigVerifyDmaResponse( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigVerifyDmaResponse* src, + whMessageCrypto_PqcStatefulSigVerifyDmaResponse* 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, res); + return 0; +} + +/* Stateful sig DMA Signatures-Left Request translation */ +int wh_MessageCrypto_TranslatePqcStatefulSigSigsLeftDmaRequest( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest* src, + whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest* dest) +{ + if ((src == NULL) || (dest == NULL)) { + return WH_ERROR_BADARGS; + } + + WH_T32(magic, dest, src, keyId); + return 0; +} + +/* Stateful sig DMA Signatures-Left Response translation */ +int wh_MessageCrypto_TranslatePqcStatefulSigSigsLeftDmaResponse( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse* src, + whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse* dest) +{ + if ((src == NULL) || (dest == NULL)) { + return WH_ERROR_BADARGS; + } + + WH_T32(magic, dest, src, sigsLeft); + return 0; +} + /* Ed25519 DMA Sign Request translation */ int wh_MessageCrypto_TranslateEd25519SignDmaRequest( uint16_t magic, const whMessageCrypto_Ed25519SignDmaRequest* src, diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c index 33f7b1fb0..9d8afcae6 100644 --- a/src/wh_server_crypto.c +++ b/src/wh_server_crypto.c @@ -44,6 +44,12 @@ #include "wolfssl/wolfcrypt/sha512.h" #include "wolfssl/wolfcrypt/cmac.h" #include "wolfssl/wolfcrypt/dilithium.h" +#if defined(WOLFSSL_HAVE_LMS) +#include "wolfssl/wolfcrypt/wc_lms.h" +#endif +#if defined(WOLFSSL_HAVE_XMSS) +#include "wolfssl/wolfcrypt/wc_xmss.h" +#endif #include "wolfssl/wolfcrypt/hmac.h" #include "wolfssl/wolfcrypt/kdf.h" @@ -798,6 +804,255 @@ int wh_Server_MlDsaKeyCacheExport(whServerContext* ctx, whKeyId keyId, } #endif /* HAVE_DILITHIUM */ +#if (defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)) && \ + defined(WOLFHSM_CFG_DMA) +/* Stateful-key persistence bridge. + * + * wolfCrypt's wc_LmsKey_Sign and wc_XmssKey_Sign require write/read callbacks + * for the software path. We wire write_private_key directly to atomic NVM + * commit (wh_Nvm_AddObjectWithReclaim): wolfCrypt's contract is to advance + * the index, call write_cb, and only emit the signature if write_cb returned + * success. That gives us pre-commit-then-emit ordering for free — see + * doc/LMS_XMSS_CryptoCb.md and the plan for the crash-safety analysis. + * + * The bridge keeps a pointer into the server's cache slot blob (laid out by + * wh_Crypto_{Lms,Xmss}SerializeKey). Each write_cb invocation overwrites the + * priv region of the slot in place and re-commits the entire slot. */ +typedef struct whServerStatefulSigBridge { + whServerContext* server; + whKeyId keyId; + whNvmMetadata* meta; /* points at the cache slot's metadata */ + uint8_t* slotBuf; /* points at the cache slot's data buffer */ + uint16_t hdrSz; /* offset to priv region inside slotBuf */ + uint16_t pubLen; /* offset of priv = hdrSz + paramLen + pubLen */ + uint16_t paramLen; + uint16_t slotCapacity; +} whServerStatefulSigBridge; + +/* Compute the priv-region offset inside the slot blob from a bridge. */ +static uint16_t _StatefulBridgePrivOffset(const whServerStatefulSigBridge* b) +{ + return (uint16_t)(b->hdrSz + b->paramLen + b->pubLen); +} + +/* Update the slot blob's privLen field (header + 6 -> priv length). */ +static void _StatefulBridgeWritePrivLen(uint8_t* slotBuf, uint16_t privLen) +{ + /* See wh_crypto.c for layout: privLen is at offset +6. */ + memcpy(slotBuf + 6, &privLen, sizeof(privLen)); +} + +#if defined(WOLFSSL_HAVE_LMS) && defined(WOLFHSM_CFG_DMA) +static int _LmsBridgeWriteCb(const byte* priv, word32 privSz, void* context) +{ + whServerStatefulSigBridge* b = (whServerStatefulSigBridge*)context; + uint16_t privOff; + uint32_t newLen; + int rc; + + if ((b == NULL) || (priv == NULL) || (b->slotBuf == NULL) || + (b->meta == NULL)) { + return WC_LMS_RC_BAD_ARG; + } + + privOff = _StatefulBridgePrivOffset(b); + newLen = (uint32_t)privOff + privSz; + if (newLen > b->slotCapacity) { + return WC_LMS_RC_WRITE_FAIL; + } + + memcpy(b->slotBuf + privOff, priv, privSz); + _StatefulBridgeWritePrivLen(b->slotBuf, (uint16_t)privSz); + b->meta->len = (whNvmSize)newLen; + + /* Atomic dual-partition commit. Wolfcrypt aborts the sign if this + * returns anything other than _SAVED_TO_NV_MEMORY, so the signature + * never escapes for an un-persisted index. */ + rc = wh_Nvm_AddObjectWithReclaim(b->server->nvm, b->meta, b->meta->len, + b->slotBuf); + return (rc == WH_ERROR_OK) ? WC_LMS_RC_SAVED_TO_NV_MEMORY + : WC_LMS_RC_WRITE_FAIL; +} + +static int _LmsBridgeReadCb(byte* priv, word32 privSz, void* context) +{ + whServerStatefulSigBridge* b = (whServerStatefulSigBridge*)context; + uint16_t privOff; + + if ((b == NULL) || (priv == NULL) || (b->slotBuf == NULL)) { + return WC_LMS_RC_BAD_ARG; + } + + privOff = _StatefulBridgePrivOffset(b); + if ((uint32_t)privOff + privSz > b->meta->len) { + return WC_LMS_RC_READ_FAIL; + } + + memcpy(priv, b->slotBuf + privOff, privSz); + return WC_LMS_RC_READ_TO_MEMORY; +} +#endif /* WOLFSSL_HAVE_LMS && WOLFHSM_CFG_DMA */ + +#if defined(WOLFSSL_HAVE_XMSS) && defined(WOLFHSM_CFG_DMA) +static enum wc_XmssRc _XmssBridgeWriteCb(const byte* priv, word32 privSz, + void* context) +{ + whServerStatefulSigBridge* b = (whServerStatefulSigBridge*)context; + uint16_t privOff; + uint32_t newLen; + int rc; + + if ((b == NULL) || (priv == NULL) || (b->slotBuf == NULL) || + (b->meta == NULL)) { + return WC_XMSS_RC_BAD_ARG; + } + + privOff = _StatefulBridgePrivOffset(b); + newLen = (uint32_t)privOff + privSz; + if (newLen > b->slotCapacity) { + return WC_XMSS_RC_WRITE_FAIL; + } + + memcpy(b->slotBuf + privOff, priv, privSz); + _StatefulBridgeWritePrivLen(b->slotBuf, (uint16_t)privSz); + b->meta->len = (whNvmSize)newLen; + + rc = wh_Nvm_AddObjectWithReclaim(b->server->nvm, b->meta, b->meta->len, + b->slotBuf); + return (rc == WH_ERROR_OK) ? WC_XMSS_RC_SAVED_TO_NV_MEMORY + : WC_XMSS_RC_WRITE_FAIL; +} + +static enum wc_XmssRc _XmssBridgeReadCb(byte* priv, word32 privSz, + void* context) +{ + whServerStatefulSigBridge* b = (whServerStatefulSigBridge*)context; + uint16_t privOff; + + if ((b == NULL) || (priv == NULL) || (b->slotBuf == NULL)) { + return WC_XMSS_RC_BAD_ARG; + } + + privOff = _StatefulBridgePrivOffset(b); + if ((uint32_t)privOff + privSz > b->meta->len) { + return WC_XMSS_RC_READ_FAIL; + } + + memcpy(priv, b->slotBuf + privOff, privSz); + return WC_XMSS_RC_READ_TO_MEMORY; +} +#endif /* WOLFSSL_HAVE_XMSS && WOLFHSM_CFG_DMA */ +#endif /* (WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS) && WOLFHSM_CFG_DMA */ + +#ifdef WOLFSSL_HAVE_LMS +int wh_Server_LmsKeyCacheImport(whServerContext* ctx, LmsKey* key, + whKeyId keyId, whNvmFlags flags, + uint16_t label_len, uint8_t* label) +{ + int ret = WH_ERROR_OK; + uint8_t* cacheBuf; + whNvmMetadata* cacheMeta; + uint16_t slotCapacity = WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE; + uint16_t blobSize; + + 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, slotCapacity, + &cacheBuf, &cacheMeta); + if (ret == WH_ERROR_OK) { + ret = wh_Crypto_LmsSerializeKey(key, slotCapacity, cacheBuf, &blobSize); + } + if (ret == WH_ERROR_OK) { + cacheMeta->id = keyId; + cacheMeta->len = blobSize; + cacheMeta->flags = flags; + cacheMeta->access = WH_NVM_ACCESS_ANY; + if ((label != NULL) && (label_len > 0)) { + memcpy(cacheMeta->label, label, label_len); + } + } + return ret; +} + +int wh_Server_LmsKeyCacheExport(whServerContext* ctx, whKeyId keyId, + LmsKey* key) +{ + uint8_t* cacheBuf; + whNvmMetadata* cacheMeta; + int ret; + + 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_LmsDeserializeKey(cacheBuf, (uint16_t)cacheMeta->len, + key); + } + return ret; +} +#endif /* WOLFSSL_HAVE_LMS */ + +#ifdef WOLFSSL_HAVE_XMSS +int wh_Server_XmssKeyCacheImport(whServerContext* ctx, XmssKey* key, + const char* paramStr, whKeyId keyId, + whNvmFlags flags, uint16_t label_len, + uint8_t* label) +{ + int ret = WH_ERROR_OK; + uint8_t* cacheBuf; + whNvmMetadata* cacheMeta; + uint16_t slotCapacity = WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE; + uint16_t blobSize; + + if ((ctx == NULL) || (key == NULL) || (paramStr == NULL) || + (WH_KEYID_ISERASED(keyId)) || + ((label != NULL) && (label_len > sizeof(cacheMeta->label)))) { + return WH_ERROR_BADARGS; + } + + ret = wh_Server_KeystoreGetCacheSlotChecked(ctx, keyId, slotCapacity, + &cacheBuf, &cacheMeta); + if (ret == WH_ERROR_OK) { + ret = wh_Crypto_XmssSerializeKey(key, paramStr, slotCapacity, cacheBuf, + &blobSize); + } + if (ret == WH_ERROR_OK) { + cacheMeta->id = keyId; + cacheMeta->len = blobSize; + cacheMeta->flags = flags; + cacheMeta->access = WH_NVM_ACCESS_ANY; + if ((label != NULL) && (label_len > 0)) { + memcpy(cacheMeta->label, label, label_len); + } + } + return ret; +} + +int wh_Server_XmssKeyCacheExport(whServerContext* ctx, whKeyId keyId, + XmssKey* key) +{ + uint8_t* cacheBuf; + whNvmMetadata* cacheMeta; + int ret; + + 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_XmssDeserializeKey(cacheBuf, (uint16_t)cacheMeta->len, + key); + } + return ret; +} +#endif /* WOLFSSL_HAVE_XMSS */ + /** Request/Response Handling functions */ @@ -5734,101 +5989,1061 @@ static int _HandlePqcSigAlgorithmDma(whServerContext* ctx, uint16_t magic, } #endif /* HAVE_DILITHIUM || HAVE_FALCON */ -#if defined(WOLFSSL_CMAC) && !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) -static int _HandleCmacDma(whServerContext* ctx, uint16_t magic, int devId, - uint16_t seq, const void* cryptoDataIn, - uint16_t inSize, void* cryptoDataOut, - uint16_t* outSize) +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) +/* Decode the slot blob's header lengths into the bridge struct. The blob + * format (see wh_crypto.c) places pubLen at +4, privLen at +6, paramLen at + * +8. */ +static int _StatefulBridgeFromSlot(whServerStatefulSigBridge* b, + whServerContext* server, + whKeyId keyId, + uint8_t* slotBuf, whNvmMetadata* meta, + uint16_t slotCapacity) { - (void)seq; + uint16_t pubLen, paramLen; - int ret = 0; - whMessageCrypto_CmacAesDmaRequest req; - whMessageCrypto_CmacAesDmaResponse res; + if ((b == NULL) || (server == NULL) || (slotBuf == NULL) || (meta == NULL)) { + return WH_ERROR_BADARGS; + } + memcpy(&pubLen, slotBuf + 4, sizeof(pubLen)); + memcpy(¶mLen, slotBuf + 8, sizeof(paramLen)); - if (inSize < sizeof(whMessageCrypto_CmacAesDmaRequest)) { + b->server = server; + b->keyId = keyId; + b->meta = meta; + b->slotBuf = slotBuf; + b->hdrSz = 12; /* WH_CRYPTO_STATEFUL_SIG_HEADER_SZ */ + b->paramLen = paramLen; + b->pubLen = pubLen; + b->slotCapacity = slotCapacity; + return WH_ERROR_OK; +} +#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */ + +#ifdef WOLFSSL_HAVE_LMS +/* Dummy cbs used during keygen. wc_LmsKey_MakeKey requires both cbs to be + * set; we don't actually persist via cb during keygen because the slot blob + * (including pub) is assembled after MakeKey populates key->pub and + * key->priv_raw. See _HandleLmsKeyGenDma for the full sequence. */ +static int _LmsDummyWriteCb(const byte* priv, word32 privSz, void* context) +{ + (void)priv; (void)privSz; (void)context; + return WC_LMS_RC_SAVED_TO_NV_MEMORY; +} +static int _LmsDummyReadCb(byte* priv, word32 privSz, void* context) +{ + (void)priv; (void)privSz; (void)context; + return WC_LMS_RC_READ_TO_MEMORY; +} + +static int _HandleLmsKeyGenDma(whServerContext* ctx, uint16_t magic, int devId, + const void* cryptoDataIn, uint16_t inSize, + void* cryptoDataOut, uint16_t* outSize) +{ +#ifdef WOLFSSL_LMS_VERIFY_ONLY + (void)ctx; (void)magic; (void)devId; (void)cryptoDataIn; (void)inSize; + (void)cryptoDataOut; (void)outSize; + return WH_ERROR_NOHANDLER; +#else + int ret; + LmsKey key[1]; + void* clientPubAddr = NULL; + word32 pubLen32 = 0; + whKeyId keyId; + whMessageCrypto_PqcStatefulSigKeyGenDmaRequest req; + whMessageCrypto_PqcStatefulSigKeyGenDmaResponse res; + + memset(&res, 0, sizeof(res)); + + if (inSize < sizeof(req)) { return WH_ERROR_BADARGS; } - /* Translate request */ - ret = wh_MessageCrypto_TranslateCmacAesDmaRequest( - magic, (whMessageCrypto_CmacAesDmaRequest*)cryptoDataIn, &req); + ret = wh_MessageCrypto_TranslatePqcStatefulSigKeyGenDmaRequest( + magic, (whMessageCrypto_PqcStatefulSigKeyGenDmaRequest*)cryptoDataIn, + &req); if (ret != WH_ERROR_OK) { return ret; } - /* Validate variable-length fields fit within inSize */ - uint32_t available = inSize - sizeof(whMessageCrypto_CmacAesDmaRequest); - if (req.keySz > available) { - return WH_ERROR_BADARGS; - } - if (req.keySz > AES_256_KEY_SIZE) { - return WH_ERROR_BADARGS; + ret = wc_LmsKey_Init(key, NULL, devId); + if (ret != 0) { + return ret; } - word32 len; - - /* Pointers to inline trailing data */ - uint8_t* key = - (uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_CmacAesDmaRequest); - uint8_t* out = - (uint8_t*)(cryptoDataOut) + sizeof(whMessageCrypto_CmacAesDmaResponse); - - memset(&res, 0, sizeof(res)); + ret = wc_LmsKey_SetParameters(key, (int)req.lmsLevels, (int)req.lmsHeight, + (int)req.lmsWinternitz); + if (ret == 0) { + ret = wc_LmsKey_SetWriteCb(key, _LmsDummyWriteCb); + } + if (ret == 0) { + ret = wc_LmsKey_SetReadCb(key, _LmsDummyReadCb); + } + if (ret == 0) { + ret = wc_LmsKey_SetContext(key, NULL); + } + if (ret == 0) { + ret = wc_LmsKey_MakeKey(key, ctx->crypto->rng); + } - /* DMA translated address for input */ - void* inAddr = NULL; + if (ret == 0) { + keyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, + ctx->comm->client_id, req.keyId); + if (WH_KEYID_ISERASED(keyId)) { + ret = wh_Server_KeystoreGetUniqueId(ctx, &keyId); + } + } - uint8_t tmpKey[AES_256_KEY_SIZE]; - uint32_t tmpKeyLen = sizeof(tmpKey); - Cmac cmac[1]; + if (ret == 0) { + ret = wh_Server_LmsKeyCacheImport(ctx, key, keyId, req.flags, + (uint16_t)req.labelSize, req.label); + } - /* Attempt oneshot if input and output are both present */ - if (req.input.sz != 0 && req.outSz != 0) { - len = req.outSz; + /* For non-ephemeral keys, commit to NVM so the key survives a server + * restart. Ephemeral keys are cache-only. */ + if ((ret == 0) && ((req.flags & WH_NVM_FLAGS_EPHEMERAL) == 0)) { + ret = wh_Server_KeystoreCommitKey(ctx, keyId); + } - /* Translate DMA address for input */ + /* Stream the public key out via the client-supplied DMA buffer. */ + if (ret == 0) { + ret = wc_LmsKey_GetPubLen(key, &pubLen32); + } + if (ret == 0 && req.pub.sz < pubLen32) { + ret = WH_ERROR_BUFFER_SIZE; + } + if (ret == 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_ACCESS) { - res.dmaAddrStatus.badAddr = req.input; + ctx, (uintptr_t)req.pub.addr, &clientPubAddr, pubLen32, + WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0}); + if (ret != 0) { + res.dmaAddrStatus.badAddr = req.pub; } + } + if (ret == 0) { + memcpy(clientPubAddr, key->pub, pubLen32); + res.keyId = wh_KeyId_TranslateToClient(keyId); + res.pubSize = pubLen32; + } + (void)wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.pub.addr, &clientPubAddr, pubLen32, + WH_DMA_OPER_CLIENT_WRITE_POST, (whServerDmaFlags){0}); - /* Resolve key */ - if (ret == WH_ERROR_OK) { - ret = _CmacResolveKey(ctx, key, req.keySz, req.keyId, tmpKey, - &tmpKeyLen); - } + wc_LmsKey_Free(key); - if (ret == WH_ERROR_OK && req.keySz != 0) { - /* Client-supplied key - direct one-shot */ - WH_DEBUG_SERVER_VERBOSE("dma cmac generate oneshot\n"); + (void)wh_MessageCrypto_TranslatePqcStatefulSigKeyGenDmaResponse( + magic, &res, + (whMessageCrypto_PqcStatefulSigKeyGenDmaResponse*)cryptoDataOut); + *outSize = sizeof(res); + return ret; +#endif /* WOLFSSL_LMS_VERIFY_ONLY */ +} - ret = wc_AesCmacGenerate_ex(cmac, out, &len, inAddr, req.input.sz, - tmpKey, (word32)tmpKeyLen, NULL, devId); - } - else if (ret == WH_ERROR_OK) { - /* HSM-local key via keyId - init then generate */ - WH_DEBUG_SERVER_VERBOSE("dma cmac generate oneshot with keyId:%x\n", - req.keyId); +static int _HandleLmsSignDma(whServerContext* ctx, uint16_t magic, int devId, + const void* cryptoDataIn, uint16_t inSize, + void* cryptoDataOut, uint16_t* outSize) +{ +#ifdef WOLFSSL_LMS_VERIFY_ONLY + (void)ctx; (void)magic; (void)devId; (void)cryptoDataIn; (void)inSize; + (void)cryptoDataOut; (void)outSize; + return WH_ERROR_NOHANDLER; +#else + int ret; + LmsKey key[1]; + int keyInited = 0; + void* msgAddr = NULL; + void* sigAddr = NULL; + word32 sigLen; + whKeyId keyId; + uint8_t* cacheBuf; + whNvmMetadata* cacheMeta; + whServerStatefulSigBridge bridge; + whMessageCrypto_PqcStatefulSigSignDmaRequest req; + whMessageCrypto_PqcStatefulSigSignDmaResponse res; - ret = wc_InitCmac_ex(cmac, tmpKey, (word32)tmpKeyLen, WC_CMAC_AES, - NULL, NULL, devId); + memset(&res, 0, sizeof(res)); - if (ret == WH_ERROR_OK) { - ret = wc_AesCmacGenerate_ex(cmac, out, &len, inAddr, - req.input.sz, NULL, 0, NULL, devId); - } - } + if (inSize < sizeof(req)) { + return WH_ERROR_BADARGS; + } + ret = wh_MessageCrypto_TranslatePqcStatefulSigSignDmaRequest( + magic, (whMessageCrypto_PqcStatefulSigSignDmaRequest*)cryptoDataIn, + &req); + if (ret != WH_ERROR_OK) { + return ret; + } - if (ret == 0) { - res.outSz = len; - res.keyId = WH_KEYID_ERASED; - } + keyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, + ctx->comm->client_id, req.keyId); + if (WH_KEYID_ISERASED(keyId)) { + return WH_ERROR_BADARGS; } - else { - WH_DEBUG_SERVER_VERBOSE( + + sigLen = (word32)req.sig.sz; + + /* Hold the NVM lock for the entire load -> sign -> commit sequence so + * concurrent sign requests on the same keyId can't race past each other. + * Pattern from wh_server_counter.c. */ + ret = WH_SERVER_NVM_LOCK(ctx); + if (ret != WH_ERROR_OK) { + return ret; + } + + ret = wh_Server_KeystoreFreshenKey(ctx, keyId, &cacheBuf, &cacheMeta); + if (ret == WH_ERROR_OK) { + ret = wc_LmsKey_Init(key, NULL, devId); + if (ret == 0) { + keyInited = 1; + } + } + if (ret == WH_ERROR_OK) { + ret = wh_Crypto_LmsDeserializeKey(cacheBuf, (uint16_t)cacheMeta->len, + key); + } + if (ret == WH_ERROR_OK) { + ret = _StatefulBridgeFromSlot( + &bridge, ctx, keyId, cacheBuf, cacheMeta, + WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE); + } + if (ret == WH_ERROR_OK) { + (void)wc_LmsKey_SetWriteCb(key, _LmsBridgeWriteCb); + (void)wc_LmsKey_SetReadCb(key, _LmsBridgeReadCb); + (void)wc_LmsKey_SetContext(key, &bridge); + ret = wc_LmsKey_Reload(key); + } + if (ret == WH_ERROR_OK) { + 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_OK) { + res.dmaAddrStatus.badAddr = req.msg; + } + } + if (ret == WH_ERROR_OK) { + 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_OK) { + res.dmaAddrStatus.badAddr = req.sig; + } + } + if (ret == WH_ERROR_OK) { + /* wolfCrypt's flow (verified against wc_lms.c:1439-1474 post-patch): + * 1. wc_hss_sign computes the signature into sig and advances + * key->priv_raw in memory. + * 2. write_private_key (our bridge) is called with the new + * priv_raw and atomically commits it to NVM. + * 3. If the bridge returns anything other than + * WC_LMS_RC_SAVED_TO_NV_MEMORY, wolfCrypt does ForceZero(sig) + * and returns IO_FAILED_E. + * Net effect: a signature is exposed to the caller only if the NVM + * commit succeeded. A process crash anywhere in the sequence either + * (a) leaves the old state in NVM with no signature exposed, or + * (b) commits the new state with the signature lost in transit - + * one wasted index but never an index reused with a fresh sig. */ + ret = wc_LmsKey_Sign(key, sigAddr, &sigLen, msgAddr, (int)req.msg.sz); + if (ret == 0) { + res.sigLen = sigLen; + } + } + + (void)wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.sig.addr, &sigAddr, sigLen, + WH_DMA_OPER_CLIENT_WRITE_POST, (whServerDmaFlags){0}); + (void)wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.msg.addr, &msgAddr, req.msg.sz, + WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + + if (keyInited) { + wc_LmsKey_Free(key); + } + + if ((req.options & WH_MESSAGE_CRYPTO_STATEFUL_SIG_OPTIONS_EVICT) != 0) { + (void)wh_Server_KeystoreEvictKey(ctx, keyId); + } + + (void)WH_SERVER_NVM_UNLOCK(ctx); + + (void)wh_MessageCrypto_TranslatePqcStatefulSigSignDmaResponse( + magic, &res, + (whMessageCrypto_PqcStatefulSigSignDmaResponse*)cryptoDataOut); + *outSize = sizeof(res); + return ret; +#endif /* WOLFSSL_LMS_VERIFY_ONLY */ +} + +static int _HandleLmsVerifyDma(whServerContext* ctx, uint16_t magic, int devId, + const void* cryptoDataIn, uint16_t inSize, + void* cryptoDataOut, uint16_t* outSize) +{ + int ret; + LmsKey key[1]; + int keyInited = 0; + void* sigAddr = NULL; + void* msgAddr = NULL; + whKeyId keyId; + whMessageCrypto_PqcStatefulSigVerifyDmaRequest req; + whMessageCrypto_PqcStatefulSigVerifyDmaResponse res; + + memset(&res, 0, sizeof(res)); + + if (inSize < sizeof(req)) { + return WH_ERROR_BADARGS; + } + ret = wh_MessageCrypto_TranslatePqcStatefulSigVerifyDmaRequest( + magic, (whMessageCrypto_PqcStatefulSigVerifyDmaRequest*)cryptoDataIn, + &req); + if (ret != WH_ERROR_OK) { + return ret; + } + + keyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, + ctx->comm->client_id, req.keyId); + if (WH_KEYID_ISERASED(keyId)) { + return WH_ERROR_BADARGS; + } + + ret = wc_LmsKey_Init(key, NULL, devId); + if (ret == 0) { + keyInited = 1; + ret = wh_Server_LmsKeyCacheExport(ctx, keyId, key); + } + if (ret == WH_ERROR_OK) { + /* Deserialize leaves the key in PARMSET; wc_LmsKey_Verify needs + * OK or VERIFYONLY. Pub is populated and that's all verify uses. */ + key->state = WC_LMS_STATE_VERIFYONLY; + } + + if (ret == WH_ERROR_OK) { + ret = wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.sig.addr, &sigAddr, req.sig.sz, + WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0}); + if (ret != WH_ERROR_OK) { + res.dmaAddrStatus.badAddr = req.sig; + } + } + if (ret == WH_ERROR_OK) { + 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_OK) { + res.dmaAddrStatus.badAddr = req.msg; + } + } + if (ret == WH_ERROR_OK) { + int verifyRet = wc_LmsKey_Verify(key, sigAddr, (word32)req.sig.sz, + msgAddr, (int)req.msg.sz); + if (verifyRet == 0) { + res.res = 1; + } + else if (verifyRet == WC_NO_ERR_TRACE(SIG_VERIFY_E)) { + res.res = 0; + } + else { + ret = verifyRet; + } + } + + (void)wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.sig.addr, &sigAddr, req.sig.sz, + WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + (void)wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.msg.addr, &msgAddr, req.msg.sz, + WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + + if (keyInited) { + wc_LmsKey_Free(key); + } + + if ((req.options & WH_MESSAGE_CRYPTO_STATEFUL_SIG_OPTIONS_EVICT) != 0) { + (void)wh_Server_KeystoreEvictKey(ctx, keyId); + } + + (void)wh_MessageCrypto_TranslatePqcStatefulSigVerifyDmaResponse( + magic, &res, + (whMessageCrypto_PqcStatefulSigVerifyDmaResponse*)cryptoDataOut); + *outSize = sizeof(res); + return ret; +} + +static int _HandleLmsSigsLeftDma(whServerContext* ctx, uint16_t magic, + int devId, const void* cryptoDataIn, + uint16_t inSize, void* cryptoDataOut, + uint16_t* outSize) +{ +#ifdef WOLFSSL_LMS_VERIFY_ONLY + (void)ctx; (void)magic; (void)devId; (void)cryptoDataIn; (void)inSize; + (void)cryptoDataOut; (void)outSize; + return WH_ERROR_NOHANDLER; +#else + int ret; + LmsKey key[1]; + int keyInited = 0; + whKeyId keyId; + whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest req; + whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse res; + + memset(&res, 0, sizeof(res)); + + if (inSize < sizeof(req)) { + return WH_ERROR_BADARGS; + } + ret = wh_MessageCrypto_TranslatePqcStatefulSigSigsLeftDmaRequest( + magic, + (whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest*)cryptoDataIn, &req); + if (ret != WH_ERROR_OK) { + return ret; + } + + keyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, + ctx->comm->client_id, req.keyId); + if (WH_KEYID_ISERASED(keyId)) { + return WH_ERROR_BADARGS; + } + + ret = wc_LmsKey_Init(key, NULL, devId); + if (ret == 0) { + keyInited = 1; + ret = wh_Server_LmsKeyCacheExport(ctx, keyId, key); + } + if (ret == WH_ERROR_OK) { + res.sigsLeft = (uint32_t)wc_LmsKey_SigsLeft(key); + } + + if (keyInited) { + wc_LmsKey_Free(key); + } + + (void)wh_MessageCrypto_TranslatePqcStatefulSigSigsLeftDmaResponse( + magic, &res, + (whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse*)cryptoDataOut); + *outSize = sizeof(res); + return ret; +#endif /* WOLFSSL_LMS_VERIFY_ONLY */ +} +#endif /* WOLFSSL_HAVE_LMS */ + +#ifdef WOLFSSL_HAVE_XMSS +/* wolfCrypt's wc_XmssKey_MakeKey calls write_private_key with the freshly + * generated sk and then ForceZero's key->sk (see wc_xmss.c). To get a usable + * sk back into key->sk for the subsequent serialize step, we capture it here + * via a context-pointed buffer. */ +typedef struct { + byte* buf; + word32 cap; + word32 len; +} _XmssSkCapture; + +static enum wc_XmssRc _XmssKeygenWriteCb(const byte* priv, word32 privSz, + void* context) +{ + _XmssSkCapture* cap = (_XmssSkCapture*)context; + if ((cap == NULL) || (priv == NULL) || (privSz > cap->cap)) { + return WC_XMSS_RC_WRITE_FAIL; + } + memcpy(cap->buf, priv, privSz); + cap->len = privSz; + return WC_XMSS_RC_SAVED_TO_NV_MEMORY; +} +static enum wc_XmssRc _XmssDummyReadCb(byte* priv, word32 privSz, + void* context) +{ + (void)priv; (void)privSz; (void)context; + return WC_XMSS_RC_READ_TO_MEMORY; +} + +static int _HandleXmssKeyGenDma(whServerContext* ctx, uint16_t magic, + int devId, const void* cryptoDataIn, + uint16_t inSize, void* cryptoDataOut, + uint16_t* outSize) +{ +#ifdef WOLFSSL_XMSS_VERIFY_ONLY + (void)ctx; (void)magic; (void)devId; (void)cryptoDataIn; (void)inSize; + (void)cryptoDataOut; (void)outSize; + return WH_ERROR_NOHANDLER; +#else + int ret; + XmssKey key[1]; + void* clientPubAddr = NULL; + word32 pubLen32 = 0; + word32 privLen32 = 0; + whKeyId keyId; + whMessageCrypto_PqcStatefulSigKeyGenDmaRequest req; + whMessageCrypto_PqcStatefulSigKeyGenDmaResponse res; + _XmssSkCapture sk_cap; + /* WC_XMSS_MAX_SK comes from the params table; sized for the largest + * supported XMSS variant. The variants enabled in user_settings.h all + * fit in 4 KiB, but use the wolfCrypt-reported priv length to be + * exact. */ + byte sk_buf[4096]; + + memset(&res, 0, sizeof(res)); + memset(&sk_cap, 0, sizeof(sk_cap)); + sk_cap.buf = sk_buf; + sk_cap.cap = (word32)sizeof(sk_buf); + + if (inSize < sizeof(req)) { + return WH_ERROR_BADARGS; + } + ret = wh_MessageCrypto_TranslatePqcStatefulSigKeyGenDmaRequest( + magic, (whMessageCrypto_PqcStatefulSigKeyGenDmaRequest*)cryptoDataIn, + &req); + if (ret != WH_ERROR_OK) { + return ret; + } + + /* xmssParamStr arrives via the request struct. The client + * (wh_Client_XmssMakeKeyDma) currently has a TODO for populating it; for + * v1 the server enforces NUL-termination. */ + req.xmssParamStr[sizeof(req.xmssParamStr) - 1] = '\0'; + + ret = wc_XmssKey_Init(key, NULL, devId); + if (ret != 0) { + return ret; + } + + ret = wc_XmssKey_SetParamStr(key, req.xmssParamStr); + if (ret == 0) { + /* Use a real capture cb: wolfCrypt ForceZero's key->sk after MakeKey + * (see wc_xmss.c), so we copy sk into sk_buf via the cb and restore + * it on key->sk before serializing into the cache slot. */ + ret = wc_XmssKey_SetWriteCb(key, _XmssKeygenWriteCb); + } + if (ret == 0) { + ret = wc_XmssKey_SetReadCb(key, _XmssDummyReadCb); + } + if (ret == 0) { + ret = wc_XmssKey_SetContext(key, &sk_cap); + } + if (ret == 0) { + ret = wc_XmssKey_MakeKey(key, ctx->crypto->rng); + } + + if (ret == 0) { + /* Sanity-check the captured sk size against what wolfCrypt expects. */ + ret = wc_XmssKey_GetPrivLen(key, &privLen32); + if ((ret == 0) && (sk_cap.len != privLen32)) { + ret = WH_ERROR_ABORTED; + } + } + if (ret == 0) { + /* Restore sk so SerializeKey captures real bytes, not the + * MakeKey-zeroed buffer. */ + memcpy(key->sk, sk_cap.buf, sk_cap.len); + } + + if (ret == 0) { + keyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, + ctx->comm->client_id, req.keyId); + if (WH_KEYID_ISERASED(keyId)) { + ret = wh_Server_KeystoreGetUniqueId(ctx, &keyId); + } + } + + if (ret == 0) { + ret = wh_Server_XmssKeyCacheImport(ctx, key, req.xmssParamStr, keyId, + req.flags, (uint16_t)req.labelSize, + req.label); + } + + if ((ret == 0) && ((req.flags & WH_NVM_FLAGS_EPHEMERAL) == 0)) { + ret = wh_Server_KeystoreCommitKey(ctx, keyId); + } + + if (ret == 0) { + ret = wc_XmssKey_GetPubLen(key, &pubLen32); + } + if (ret == 0 && req.pub.sz < pubLen32) { + ret = WH_ERROR_BUFFER_SIZE; + } + if (ret == 0) { + ret = wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.pub.addr, &clientPubAddr, pubLen32, + WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0}); + if (ret != 0) { + res.dmaAddrStatus.badAddr = req.pub; + } + } + if (ret == 0) { + memcpy(clientPubAddr, key->pk, pubLen32); + res.keyId = wh_KeyId_TranslateToClient(keyId); + res.pubSize = pubLen32; + } + (void)wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.pub.addr, &clientPubAddr, pubLen32, + WH_DMA_OPER_CLIENT_WRITE_POST, (whServerDmaFlags){0}); + + wc_XmssKey_Free(key); + + (void)wh_MessageCrypto_TranslatePqcStatefulSigKeyGenDmaResponse( + magic, &res, + (whMessageCrypto_PqcStatefulSigKeyGenDmaResponse*)cryptoDataOut); + *outSize = sizeof(res); + return ret; +#endif /* WOLFSSL_XMSS_VERIFY_ONLY */ +} + +static int _HandleXmssSignDma(whServerContext* ctx, uint16_t magic, int devId, + const void* cryptoDataIn, uint16_t inSize, + void* cryptoDataOut, uint16_t* outSize) +{ +#ifdef WOLFSSL_XMSS_VERIFY_ONLY + (void)ctx; (void)magic; (void)devId; (void)cryptoDataIn; (void)inSize; + (void)cryptoDataOut; (void)outSize; + return WH_ERROR_NOHANDLER; +#else + int ret; + XmssKey key[1]; + int keyInited = 0; + void* msgAddr = NULL; + void* sigAddr = NULL; + word32 sigLen; + whKeyId keyId; + uint8_t* cacheBuf; + whNvmMetadata* cacheMeta; + whServerStatefulSigBridge bridge; + whMessageCrypto_PqcStatefulSigSignDmaRequest req; + whMessageCrypto_PqcStatefulSigSignDmaResponse res; + + memset(&res, 0, sizeof(res)); + + if (inSize < sizeof(req)) { + return WH_ERROR_BADARGS; + } + ret = wh_MessageCrypto_TranslatePqcStatefulSigSignDmaRequest( + magic, (whMessageCrypto_PqcStatefulSigSignDmaRequest*)cryptoDataIn, + &req); + if (ret != WH_ERROR_OK) { + return ret; + } + + keyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, + ctx->comm->client_id, req.keyId); + if (WH_KEYID_ISERASED(keyId)) { + return WH_ERROR_BADARGS; + } + + sigLen = (word32)req.sig.sz; + + /* See _HandleLmsSignDma for the NVM-lock rationale. */ + ret = WH_SERVER_NVM_LOCK(ctx); + if (ret != WH_ERROR_OK) { + return ret; + } + + ret = wh_Server_KeystoreFreshenKey(ctx, keyId, &cacheBuf, &cacheMeta); + if (ret == WH_ERROR_OK) { + ret = wc_XmssKey_Init(key, NULL, devId); + if (ret == 0) { + keyInited = 1; + } + } + if (ret == WH_ERROR_OK) { + ret = wh_Crypto_XmssDeserializeKey(cacheBuf, (uint16_t)cacheMeta->len, + key); + } + if (ret == WH_ERROR_OK) { + ret = _StatefulBridgeFromSlot( + &bridge, ctx, keyId, cacheBuf, cacheMeta, + WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE); + } + if (ret == WH_ERROR_OK) { + (void)wc_XmssKey_SetWriteCb(key, _XmssBridgeWriteCb); + (void)wc_XmssKey_SetReadCb(key, _XmssBridgeReadCb); + (void)wc_XmssKey_SetContext(key, &bridge); + ret = wc_XmssKey_Reload(key); + } + if (ret == WH_ERROR_OK) { + 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_OK) { + res.dmaAddrStatus.badAddr = req.msg; + } + } + if (ret == WH_ERROR_OK) { + 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_OK) { + res.dmaAddrStatus.badAddr = req.sig; + } + } + if (ret == WH_ERROR_OK) { + ret = wc_XmssKey_Sign(key, sigAddr, &sigLen, msgAddr, (int)req.msg.sz); + if (ret == 0) { + res.sigLen = sigLen; + } + } + + (void)wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.sig.addr, &sigAddr, sigLen, + WH_DMA_OPER_CLIENT_WRITE_POST, (whServerDmaFlags){0}); + (void)wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.msg.addr, &msgAddr, req.msg.sz, + WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + + if (keyInited) { + wc_XmssKey_Free(key); + } + + if ((req.options & WH_MESSAGE_CRYPTO_STATEFUL_SIG_OPTIONS_EVICT) != 0) { + (void)wh_Server_KeystoreEvictKey(ctx, keyId); + } + + (void)WH_SERVER_NVM_UNLOCK(ctx); + + (void)wh_MessageCrypto_TranslatePqcStatefulSigSignDmaResponse( + magic, &res, + (whMessageCrypto_PqcStatefulSigSignDmaResponse*)cryptoDataOut); + *outSize = sizeof(res); + return ret; +#endif /* WOLFSSL_XMSS_VERIFY_ONLY */ +} + +static int _HandleXmssVerifyDma(whServerContext* ctx, uint16_t magic, + int devId, const void* cryptoDataIn, + uint16_t inSize, void* cryptoDataOut, + uint16_t* outSize) +{ + int ret; + XmssKey key[1]; + int keyInited = 0; + void* sigAddr = NULL; + void* msgAddr = NULL; + whKeyId keyId; + whMessageCrypto_PqcStatefulSigVerifyDmaRequest req; + whMessageCrypto_PqcStatefulSigVerifyDmaResponse res; + + memset(&res, 0, sizeof(res)); + + if (inSize < sizeof(req)) { + return WH_ERROR_BADARGS; + } + ret = wh_MessageCrypto_TranslatePqcStatefulSigVerifyDmaRequest( + magic, (whMessageCrypto_PqcStatefulSigVerifyDmaRequest*)cryptoDataIn, + &req); + if (ret != WH_ERROR_OK) { + return ret; + } + + keyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, + ctx->comm->client_id, req.keyId); + if (WH_KEYID_ISERASED(keyId)) { + return WH_ERROR_BADARGS; + } + + ret = wc_XmssKey_Init(key, NULL, devId); + if (ret == 0) { + keyInited = 1; + ret = wh_Server_XmssKeyCacheExport(ctx, keyId, key); + } + if (ret == WH_ERROR_OK) { + /* Deserialize leaves the key in PARMSET; wc_XmssKey_Verify needs + * OK or VERIFYONLY. Pub is populated and that's all verify uses. */ + key->state = WC_XMSS_STATE_VERIFYONLY; + } + + if (ret == WH_ERROR_OK) { + ret = wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.sig.addr, &sigAddr, req.sig.sz, + WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0}); + if (ret != WH_ERROR_OK) { + res.dmaAddrStatus.badAddr = req.sig; + } + } + if (ret == WH_ERROR_OK) { + 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_OK) { + res.dmaAddrStatus.badAddr = req.msg; + } + } + if (ret == WH_ERROR_OK) { + int verifyRet = wc_XmssKey_Verify(key, sigAddr, (word32)req.sig.sz, + msgAddr, (int)req.msg.sz); + if (verifyRet == 0) { + res.res = 1; + } + else if (verifyRet == WC_NO_ERR_TRACE(SIG_VERIFY_E)) { + res.res = 0; + } + else { + ret = verifyRet; + } + } + + (void)wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.sig.addr, &sigAddr, req.sig.sz, + WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + (void)wh_Server_DmaProcessClientAddress( + ctx, (uintptr_t)req.msg.addr, &msgAddr, req.msg.sz, + WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + + if (keyInited) { + wc_XmssKey_Free(key); + } + + if ((req.options & WH_MESSAGE_CRYPTO_STATEFUL_SIG_OPTIONS_EVICT) != 0) { + (void)wh_Server_KeystoreEvictKey(ctx, keyId); + } + + (void)wh_MessageCrypto_TranslatePqcStatefulSigVerifyDmaResponse( + magic, &res, + (whMessageCrypto_PqcStatefulSigVerifyDmaResponse*)cryptoDataOut); + *outSize = sizeof(res); + return ret; +} + +static int _HandleXmssSigsLeftDma(whServerContext* ctx, uint16_t magic, + int devId, const void* cryptoDataIn, + uint16_t inSize, void* cryptoDataOut, + uint16_t* outSize) +{ +#ifdef WOLFSSL_XMSS_VERIFY_ONLY + (void)ctx; (void)magic; (void)devId; (void)cryptoDataIn; (void)inSize; + (void)cryptoDataOut; (void)outSize; + return WH_ERROR_NOHANDLER; +#else + int ret; + XmssKey key[1]; + int keyInited = 0; + whKeyId keyId; + uint8_t* cacheBuf; + whNvmMetadata* cacheMeta; + whServerStatefulSigBridge bridge; + whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest req; + whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse res; + + memset(&res, 0, sizeof(res)); + + if (inSize < sizeof(req)) { + return WH_ERROR_BADARGS; + } + ret = wh_MessageCrypto_TranslatePqcStatefulSigSigsLeftDmaRequest( + magic, + (whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest*)cryptoDataIn, &req); + if (ret != WH_ERROR_OK) { + return ret; + } + + keyId = wh_KeyId_TranslateFromClient(WH_KEYTYPE_CRYPTO, + ctx->comm->client_id, req.keyId); + if (WH_KEYID_ISERASED(keyId)) { + return WH_ERROR_BADARGS; + } + + ret = wh_Server_KeystoreFreshenKey(ctx, keyId, &cacheBuf, &cacheMeta); + if (ret == WH_ERROR_OK) { + ret = wc_XmssKey_Init(key, NULL, devId); + if (ret == 0) { + keyInited = 1; + } + } + if (ret == WH_ERROR_OK) { + ret = wh_Crypto_XmssDeserializeKey(cacheBuf, (uint16_t)cacheMeta->len, + key); + } + if (ret == WH_ERROR_OK) { + ret = _StatefulBridgeFromSlot( + &bridge, ctx, keyId, cacheBuf, cacheMeta, + WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE); + } + if (ret == WH_ERROR_OK) { + /* Reload uses the bridge ReadCb to populate sk from the cached blob, + * then transitions state to OK so SigsLeft can run. */ + (void)wc_XmssKey_SetWriteCb(key, _XmssBridgeWriteCb); + (void)wc_XmssKey_SetReadCb(key, _XmssBridgeReadCb); + (void)wc_XmssKey_SetContext(key, &bridge); + ret = wc_XmssKey_Reload(key); + } + if (ret == WH_ERROR_OK) { + res.sigsLeft = (uint32_t)wc_XmssKey_SigsLeft(key); + } + + if (keyInited) { + wc_XmssKey_Free(key); + } + + (void)wh_MessageCrypto_TranslatePqcStatefulSigSigsLeftDmaResponse( + magic, &res, + (whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse*)cryptoDataOut); + *outSize = sizeof(res); + return ret; +#endif /* WOLFSSL_XMSS_VERIFY_ONLY */ +} +#endif /* WOLFSSL_HAVE_XMSS */ + +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) +static int _HandlePqcStatefulSigAlgorithmDma( + 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) { +#ifdef WOLFSSL_HAVE_LMS + case WC_PQC_STATEFUL_SIG_TYPE_LMS: + switch (pkAlgoType) { + case WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN: + ret = _HandleLmsKeyGenDma(ctx, magic, devId, cryptoDataIn, + cryptoInSize, cryptoDataOut, + cryptoOutSize); + break; + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN: + ret = _HandleLmsSignDma(ctx, magic, devId, cryptoDataIn, + cryptoInSize, cryptoDataOut, + cryptoOutSize); + break; + case WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY: + ret = _HandleLmsVerifyDma(ctx, magic, devId, cryptoDataIn, + cryptoInSize, cryptoDataOut, + cryptoOutSize); + break; + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT: + ret = _HandleLmsSigsLeftDma(ctx, magic, devId, + cryptoDataIn, cryptoInSize, + cryptoDataOut, cryptoOutSize); + break; + default: + ret = WH_ERROR_NOHANDLER; + break; + } + break; +#endif /* WOLFSSL_HAVE_LMS */ +#ifdef WOLFSSL_HAVE_XMSS + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: + switch (pkAlgoType) { + case WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN: + ret = _HandleXmssKeyGenDma(ctx, magic, devId, cryptoDataIn, + cryptoInSize, cryptoDataOut, + cryptoOutSize); + break; + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN: + ret = _HandleXmssSignDma(ctx, magic, devId, cryptoDataIn, + cryptoInSize, cryptoDataOut, + cryptoOutSize); + break; + case WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY: + ret = _HandleXmssVerifyDma(ctx, magic, devId, cryptoDataIn, + cryptoInSize, cryptoDataOut, + cryptoOutSize); + break; + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT: + ret = _HandleXmssSigsLeftDma(ctx, magic, devId, + cryptoDataIn, cryptoInSize, + cryptoDataOut, cryptoOutSize); + break; + default: + ret = WH_ERROR_NOHANDLER; + break; + } + break; +#endif /* WOLFSSL_HAVE_XMSS */ + default: + ret = WH_ERROR_NOHANDLER; + break; + } + + return ret; +} +#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */ +#if defined(WOLFSSL_CMAC) && !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) +static int _HandleCmacDma(whServerContext* ctx, uint16_t magic, int devId, + uint16_t seq, const void* cryptoDataIn, + uint16_t inSize, void* cryptoDataOut, + uint16_t* outSize) +{ + (void)seq; + + int ret = 0; + whMessageCrypto_CmacAesDmaRequest req; + whMessageCrypto_CmacAesDmaResponse res; + + if (inSize < sizeof(whMessageCrypto_CmacAesDmaRequest)) { + return WH_ERROR_BADARGS; + } + + /* Translate request */ + ret = wh_MessageCrypto_TranslateCmacAesDmaRequest( + magic, (whMessageCrypto_CmacAesDmaRequest*)cryptoDataIn, &req); + if (ret != WH_ERROR_OK) { + return ret; + } + + /* Validate variable-length fields fit within inSize */ + uint32_t available = inSize - sizeof(whMessageCrypto_CmacAesDmaRequest); + if (req.keySz > available) { + return WH_ERROR_BADARGS; + } + if (req.keySz > AES_256_KEY_SIZE) { + return WH_ERROR_BADARGS; + } + + word32 len; + + /* Pointers to inline trailing data */ + uint8_t* key = + (uint8_t*)(cryptoDataIn) + sizeof(whMessageCrypto_CmacAesDmaRequest); + uint8_t* out = + (uint8_t*)(cryptoDataOut) + sizeof(whMessageCrypto_CmacAesDmaResponse); + + memset(&res, 0, sizeof(res)); + + /* DMA translated address for input */ + void* inAddr = NULL; + + uint8_t tmpKey[AES_256_KEY_SIZE]; + uint32_t tmpKeyLen = sizeof(tmpKey); + Cmac cmac[1]; + + /* Attempt oneshot if input and output are both present */ + if (req.input.sz != 0 && req.outSz != 0) { + len = req.outSz; + + /* Translate DMA address for input */ + ret = wh_Server_DmaProcessClientAddress( + ctx, req.input.addr, &inAddr, req.input.sz, + WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0}); + if (ret == WH_ERROR_ACCESS) { + res.dmaAddrStatus.badAddr = req.input; + } + + /* Resolve key */ + if (ret == WH_ERROR_OK) { + ret = _CmacResolveKey(ctx, key, req.keySz, req.keyId, tmpKey, + &tmpKeyLen); + } + + if (ret == WH_ERROR_OK && req.keySz != 0) { + /* Client-supplied key - direct one-shot */ + WH_DEBUG_SERVER_VERBOSE("dma cmac generate oneshot\n"); + + ret = wc_AesCmacGenerate_ex(cmac, out, &len, inAddr, req.input.sz, + tmpKey, (word32)tmpKeyLen, NULL, devId); + } + else if (ret == WH_ERROR_OK) { + /* HSM-local key via keyId - init then generate */ + WH_DEBUG_SERVER_VERBOSE("dma cmac generate oneshot with keyId:%x\n", + req.keyId); + + ret = wc_InitCmac_ex(cmac, tmpKey, (word32)tmpKeyLen, WC_CMAC_AES, + NULL, NULL, devId); + + if (ret == WH_ERROR_OK) { + ret = wc_AesCmacGenerate_ex(cmac, out, &len, inAddr, + req.input.sz, NULL, 0, NULL, devId); + } + } + + if (ret == 0) { + res.outSz = len; + res.keyId = WH_KEYID_ERASED; + } + } + else { + WH_DEBUG_SERVER_VERBOSE( "dma cmac begin keySz:%d inSz:%d outSz:%d keyId:%x\n", (int)req.keySz, (int)req.input.sz, (int)req.outSz, req.keyId); @@ -6105,6 +7320,17 @@ int wh_Server_HandleCryptoDmaRequest(whServerContext* ctx, uint16_t magic, rqstHeader.algoSubType); break; #endif /* HAVE_DILITHIUM || HAVE_FALCON */ +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) + case WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN: + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN: + case WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY: + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT: + ret = _HandlePqcStatefulSigAlgorithmDma( + ctx, magic, devId, cryptoDataIn, cryptoInSize, + cryptoDataOut, &cryptoOutSize, rqstHeader.algoType, + rqstHeader.algoSubType); + break; +#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */ #ifdef HAVE_ED25519 case WC_PK_TYPE_ED25519_SIGN: ret = _HandleEd25519SignDma(ctx, magic, devId, cryptoDataIn, diff --git a/test/config/user_settings.h b/test/config/user_settings.h index e86389345..98902416b 100644 --- a/test/config/user_settings.h +++ b/test/config/user_settings.h @@ -139,6 +139,15 @@ #define WOLFSSL_SHAKE128 #define WOLFSSL_SHAKE256 +/* LMS / HSS Options (RFC 8554, NIST SP 800-208) */ +#define WOLFSSL_HAVE_LMS +#define WOLFSSL_WC_LMS + +/* XMSS / XMSS^MT Options (RFC 8391, NIST SP 800-208) */ +#define WOLFSSL_HAVE_XMSS +#define WOLFSSL_WC_XMSS + + /* Ed25519 Options */ #define HAVE_ED25519 diff --git a/test/wh_test_check_struct_padding.c b/test/wh_test_check_struct_padding.c index fe224c1fa..2d6cfc535 100644 --- a/test/wh_test_check_struct_padding.c +++ b/test/wh_test_check_struct_padding.c @@ -134,6 +134,14 @@ whMessageCrypto_MlDsaVerifyDmaRequest pqMldsaVerifyDmaReq; whMessageCrypto_MlDsaVerifyDmaResponse pqMldsaVerifyDmaRes; whMessageCrypto_CmacAesDmaRequest cmacDmaReq; whMessageCrypto_CmacAesDmaResponse cmacDmaRes; +whMessageCrypto_PqcStatefulSigKeyGenDmaRequest pqStatefulSigKeygenDmaReq; +whMessageCrypto_PqcStatefulSigKeyGenDmaResponse pqStatefulSigKeygenDmaRes; +whMessageCrypto_PqcStatefulSigSignDmaRequest pqStatefulSigSignDmaReq; +whMessageCrypto_PqcStatefulSigSignDmaResponse pqStatefulSigSignDmaRes; +whMessageCrypto_PqcStatefulSigVerifyDmaRequest pqStatefulSigVerifyDmaReq; +whMessageCrypto_PqcStatefulSigVerifyDmaResponse pqStatefulSigVerifyDmaRes; +whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest pqStatefulSigSigsLeftDmaReq; +whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse pqStatefulSigSigsLeftDmaRes; #endif /* WOLFHSM_CFG_DMA */ #endif /* !WOLFHSM_CFG_NO_CRYPTO */ diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index d33fa192e..89b868bfd 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -32,6 +32,12 @@ #include "wolfssl/wolfcrypt/types.h" #include "wolfssl/wolfcrypt/kdf.h" #include "wolfssl/wolfcrypt/ed25519.h" +#if defined(WOLFSSL_HAVE_LMS) +#include "wolfssl/wolfcrypt/wc_lms.h" +#endif +#if defined(WOLFSSL_HAVE_XMSS) +#include "wolfssl/wolfcrypt/wc_xmss.h" +#endif #include "wolfhsm/wh_error.h" @@ -7849,6 +7855,304 @@ int whTestCrypto_MlDsaVerifyOnlyDma(whClientContext* ctx, int devId, #endif /* HAVE_DILITHIUM */ +#if defined(WOLFHSM_CFG_DMA) && \ + defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) +/* L=1, H=5, W=8 keeps the signature ~1.3 KB and gives 2^5 = 32 signatures. */ +#define WH_TEST_LMS_LEVELS (1) +#define WH_TEST_LMS_HEIGHT (5) +#define WH_TEST_LMS_WINTERNITZ (8) +/* Generous buffer that fits L1_H5_W8 (~1328) and any W<8 variant of the same + * height (W=1 ~8688). Keeps off the stack so ASAN builds stay happy. */ +static byte whTest_LmsSigBuf[8800]; + +static int whTestCrypto_LmsCryptoCb(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = 0; + LmsKey key[1]; + int keyInited = 0; + word32 sigLen = 0; + word32 sigCap = 0; + const byte msg[] = "wolfHSM LMS cryptocb test"; + word32 msgSz = (word32)sizeof(msg) - 1; + + (void)rng; + + memset(whTest_LmsSigBuf, 0, sizeof(whTest_LmsSigBuf)); + + ret = wc_LmsKey_Init(key, NULL, devId); + if (ret != 0) { + WH_ERROR_PRINT("Failed wc_LmsKey_Init devId=0x%X ret=%d\n", devId, ret); + return ret; + } + keyInited = 1; + + if (ret == 0) { + ret = wc_LmsKey_SetParameters(key, WH_TEST_LMS_LEVELS, + WH_TEST_LMS_HEIGHT, + WH_TEST_LMS_WINTERNITZ); + if (ret != 0) { + WH_ERROR_PRINT("Failed LMS SetParameters ret=%d\n", ret); + } + } + + if (ret == 0) { + ret = wc_LmsKey_GetSigLen(key, &sigCap); + if (ret != 0) { + WH_ERROR_PRINT("Failed LMS GetSigLen ret=%d\n", ret); + } + else if (sigCap > sizeof(whTest_LmsSigBuf)) { + WH_ERROR_PRINT("LMS sig buffer too small: need=%u have=%u\n", + (unsigned)sigCap, + (unsigned)sizeof(whTest_LmsSigBuf)); + ret = BUFFER_E; + } + } + + /* MakeKey via cryptocb: server caches private key (ephemeral) and + * returns the public key over DMA. */ + if (ret == 0) { + ret = wc_LmsKey_MakeKey(key, rng); + if (ret != 0) { + WH_ERROR_PRINT("Failed LMS MakeKey ret=%d\n", ret); + } + } + + /* wc_LmsKey_SigsLeft returns a boolean: nonzero = signatures available, + * 0 = exhausted. Fresh key should report nonzero. */ + if (ret == 0) { + if (wc_LmsKey_SigsLeft(key) == 0) { + WH_ERROR_PRINT("LMS reported exhausted on fresh key\n"); + ret = -1; + } + } + + /* Sign via cryptocb. */ + if (ret == 0) { + sigLen = sigCap; + ret = wc_LmsKey_Sign(key, whTest_LmsSigBuf, &sigLen, msg, (int)msgSz); + if (ret != 0) { + WH_ERROR_PRINT("Failed LMS Sign ret=%d\n", ret); + } + else if (sigLen != sigCap) { + WH_ERROR_PRINT("LMS Sign produced unexpected length=%u expected=%u\n", + (unsigned)sigLen, (unsigned)sigCap); + ret = -1; + } + } + + /* Verify the signature via cryptocb. */ + if (ret == 0) { + ret = wc_LmsKey_Verify(key, whTest_LmsSigBuf, sigLen, msg, (int)msgSz); + if (ret != 0) { + WH_ERROR_PRINT("Failed LMS Verify ret=%d\n", ret); + } + } + + /* Tampered signature must fail to verify. */ + if (ret == 0) { + whTest_LmsSigBuf[0] ^= 0xFF; + ret = wc_LmsKey_Verify(key, whTest_LmsSigBuf, sigLen, msg, (int)msgSz); + whTest_LmsSigBuf[0] ^= 0xFF; + if (ret == 0) { + WH_ERROR_PRINT("LMS Verify unexpectedly accepted tampered sig\n"); + ret = -1; + } + else { + ret = 0; + } + } + + /* Wrong message must also fail to verify. */ + if (ret == 0) { + const byte wrongMsg[] = "wolfHSM LMS cryptocb wrong"; + ret = wc_LmsKey_Verify(key, whTest_LmsSigBuf, sigLen, wrongMsg, + (int)(sizeof(wrongMsg) - 1)); + if (ret == 0) { + WH_ERROR_PRINT("LMS Verify unexpectedly accepted wrong message\n"); + ret = -1; + } + else { + ret = 0; + } + } + + /* H=5 means 32 sigs total; after one sign, the key is still not + * exhausted. */ + if (ret == 0) { + if (wc_LmsKey_SigsLeft(key) == 0) { + WH_ERROR_PRINT("LMS reported exhausted after one sign\n"); + ret = -1; + } + } + + if (keyInited) { + whKeyId evictId = WH_KEYID_ERASED; + if ((wh_Client_LmsGetKeyId(key, &evictId) == 0) && + !WH_KEYID_ISERASED(evictId)) { + int evictRet = wh_Client_KeyEvict(ctx, evictId); + if ((evictRet != 0) && (ret == 0)) { + WH_ERROR_PRINT("Failed LMS evict keyId=0x%X ret=%d\n", + (unsigned)evictId, evictRet); + ret = evictRet; + } + } + wc_LmsKey_Free(key); + } + + if (ret == 0) { + WH_TEST_PRINT("LMS CryptoCb DEVID=0x%X SUCCESS\n", devId); + } + + return ret; +} +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */ + +#if defined(WOLFHSM_CFG_DMA) && \ + defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) +/* "XMSS-SHA2_10_256" is the smallest standardized XMSS parameter set + * (height 10, 1024 signatures). pubLen=68, sigLen=2500. */ +#define WH_TEST_XMSS_PARAM_STR "XMSS-SHA2_10_256" +static byte whTest_XmssSigBuf[2500]; + +static int whTestCrypto_XmssCryptoCb(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = 0; + XmssKey key[1]; + int keyInited = 0; + word32 sigLen = 0; + word32 sigCap = 0; + const byte msg[] = "wolfHSM XMSS cryptocb test"; + word32 msgSz = (word32)sizeof(msg) - 1; + + (void)rng; + + memset(whTest_XmssSigBuf, 0, sizeof(whTest_XmssSigBuf)); + + ret = wc_XmssKey_Init(key, NULL, devId); + if (ret != 0) { + WH_ERROR_PRINT("Failed wc_XmssKey_Init devId=0x%X ret=%d\n", devId, ret); + return ret; + } + keyInited = 1; + + if (ret == 0) { + ret = wc_XmssKey_SetParamStr(key, WH_TEST_XMSS_PARAM_STR); + if (ret != 0) { + WH_ERROR_PRINT("Failed XMSS SetParamStr=\"%s\" ret=%d\n", + WH_TEST_XMSS_PARAM_STR, ret); + } + } + + if (ret == 0) { + ret = wc_XmssKey_GetSigLen(key, &sigCap); + if (ret != 0) { + WH_ERROR_PRINT("Failed XMSS GetSigLen ret=%d\n", ret); + } + else if (sigCap > sizeof(whTest_XmssSigBuf)) { + WH_ERROR_PRINT("XMSS sig buffer too small: need=%u have=%u\n", + (unsigned)sigCap, + (unsigned)sizeof(whTest_XmssSigBuf)); + ret = BUFFER_E; + } + } + + /* MakeKey via cryptocb: server caches private key (ephemeral) and + * returns the public key over DMA. */ + if (ret == 0) { + ret = wc_XmssKey_MakeKey(key, rng); + if (ret != 0) { + WH_ERROR_PRINT("Failed XMSS MakeKey ret=%d\n", ret); + } + } + + /* wc_XmssKey_SigsLeft returns a boolean: nonzero = signatures available, + * 0 = exhausted. */ + if (ret == 0) { + if (wc_XmssKey_SigsLeft(key) == 0) { + WH_ERROR_PRINT("XMSS reported exhausted on fresh key\n"); + ret = -1; + } + } + + if (ret == 0) { + sigLen = sigCap; + ret = wc_XmssKey_Sign(key, whTest_XmssSigBuf, &sigLen, msg, (int)msgSz); + if (ret != 0) { + WH_ERROR_PRINT("Failed XMSS Sign ret=%d\n", ret); + } + else if (sigLen != sigCap) { + WH_ERROR_PRINT("XMSS Sign produced unexpected length=%u expected=%u\n", + (unsigned)sigLen, (unsigned)sigCap); + ret = -1; + } + } + + if (ret == 0) { + ret = wc_XmssKey_Verify(key, whTest_XmssSigBuf, sigLen, msg, (int)msgSz); + if (ret != 0) { + WH_ERROR_PRINT("Failed XMSS Verify ret=%d\n", ret); + } + } + + if (ret == 0) { + whTest_XmssSigBuf[0] ^= 0xFF; + ret = wc_XmssKey_Verify(key, whTest_XmssSigBuf, sigLen, msg, (int)msgSz); + whTest_XmssSigBuf[0] ^= 0xFF; + if (ret == 0) { + WH_ERROR_PRINT("XMSS Verify unexpectedly accepted tampered sig\n"); + ret = -1; + } + else { + ret = 0; + } + } + + if (ret == 0) { + const byte wrongMsg[] = "wolfHSM XMSS cryptocb wrong"; + ret = wc_XmssKey_Verify(key, whTest_XmssSigBuf, sigLen, wrongMsg, + (int)(sizeof(wrongMsg) - 1)); + if (ret == 0) { + WH_ERROR_PRINT("XMSS Verify unexpectedly accepted wrong message\n"); + ret = -1; + } + else { + ret = 0; + } + } + + /* H=10 means 1024 sigs total; after one sign, the key is still not + * exhausted. */ + if (ret == 0) { + if (wc_XmssKey_SigsLeft(key) == 0) { + WH_ERROR_PRINT("XMSS reported exhausted after one sign\n"); + ret = -1; + } + } + + if (keyInited) { + whKeyId evictId = WH_KEYID_ERASED; + if ((wh_Client_XmssGetKeyId(key, &evictId) == 0) && + !WH_KEYID_ISERASED(evictId)) { + int evictRet = wh_Client_KeyEvict(ctx, evictId); + if ((evictRet != 0) && (ret == 0)) { + WH_ERROR_PRINT("Failed XMSS evict keyId=0x%X ret=%d\n", + (unsigned)evictId, evictRet); + ret = evictRet; + } + } + wc_XmssKey_Free(key); + } + + if (ret == 0) { + WH_TEST_PRINT("XMSS CryptoCb DEVID=0x%X SUCCESS\n", devId); + } + + return ret; +} +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */ + /* Test key usage policy enforcement for various crypto operations */ int whTest_CryptoKeyUsagePolicies(whClientContext* client, WC_RNG* rng) { @@ -8825,6 +9129,20 @@ int whTest_CryptoClientConfig(whClientConfig* config) #endif /* HAVE_DILITHIUM */ +#if defined(WOLFHSM_CFG_DMA) && \ + defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + if (ret == 0) { + ret = whTestCrypto_LmsCryptoCb(client, WH_DEV_ID_DMA, rng); + } +#endif + +#if defined(WOLFHSM_CFG_DMA) && \ + defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + if (ret == 0) { + ret = whTestCrypto_XmssCryptoCb(client, WH_DEV_ID_DMA, rng); + } +#endif + #ifdef WOLFHSM_CFG_DEBUG_VERBOSE if (ret == 0) { (void)whTest_ShowNvmAvailable(client); diff --git a/wolfhsm/wh_client_crypto.h b/wolfhsm/wh_client_crypto.h index 044fb8c05..ec83055d8 100644 --- a/wolfhsm/wh_client_crypto.h +++ b/wolfhsm/wh_client_crypto.h @@ -2040,5 +2040,76 @@ int wh_Client_MlDsaCheckPrivKeyDma(whClientContext* ctx, MlDsaKey* key, #endif /* HAVE_DILITHIUM */ +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) +#ifdef WOLFHSM_CFG_DMA + +#ifdef WOLFSSL_HAVE_LMS + +/* Bind / read the wolfHSM key id stored in key->devCtx. */ +int wh_Client_LmsSetKeyId(LmsKey* key, whKeyId keyId); +int wh_Client_LmsGetKeyId(LmsKey* key, whKeyId* outId); + +/* Generate an LMS key on the server. The key's parameter set + * (levels/height/winternitz) must be bound on the in-memory key before this + * call (e.g. via wc_LmsKey_SetParameters). On success the key's devCtx + * carries the server-side keyId. + * + * If flags include WH_NVM_FLAGS_EPHEMERAL, the server returns the public key + * via DMA and the caller can sign with it as long as it remains cached on + * the server. Otherwise the key is committed to the keystore. */ +int wh_Client_LmsMakeKeyDma(whClientContext* ctx, LmsKey* key, + whKeyId* inout_key_id, whNvmFlags flags, + uint16_t label_len, uint8_t* label); + +/* Convenience wrapper: WH_NVM_FLAGS_EPHEMERAL keygen, returns pub via DMA. */ +int wh_Client_LmsMakeExportKeyDma(whClientContext* ctx, LmsKey* key); + +/* Sign msg with an HSM-resident LMS key (key->devCtx carries the keyId). + * The new private state is committed atomically to NVM by the server before + * the signature is returned. */ +int wh_Client_LmsSignDma(whClientContext* ctx, const byte* msg, word32 msgSz, + byte* sig, word32* sigSz, LmsKey* key); + +/* Verify sig against msg using an HSM-resident LMS key. *res is set to 1 on + * success, 0 on signature mismatch. */ +int wh_Client_LmsVerifyDma(whClientContext* ctx, const byte* sig, word32 sigSz, + const byte* msg, word32 msgSz, int* res, + LmsKey* key); + +/* Query remaining signatures on an HSM-resident LMS key. */ +int wh_Client_LmsSigsLeftDma(whClientContext* ctx, LmsKey* key, + word32* sigsLeft); + +#endif /* WOLFSSL_HAVE_LMS */ + +#ifdef WOLFSSL_HAVE_XMSS + +int wh_Client_XmssSetKeyId(XmssKey* key, whKeyId keyId); +int wh_Client_XmssGetKeyId(XmssKey* key, whKeyId* outId); + +/* Generate an XMSS / XMSS^MT key on the server. The parameter string must be + * bound on the in-memory key (via wc_XmssKey_SetParamStr) before this call. + */ +int wh_Client_XmssMakeKeyDma(whClientContext* ctx, XmssKey* key, + whKeyId* inout_key_id, whNvmFlags flags, + uint16_t label_len, uint8_t* label); + +int wh_Client_XmssMakeExportKeyDma(whClientContext* ctx, XmssKey* key); + +int wh_Client_XmssSignDma(whClientContext* ctx, const byte* msg, word32 msgSz, + byte* sig, word32* sigSz, XmssKey* key); + +int wh_Client_XmssVerifyDma(whClientContext* ctx, const byte* sig, + word32 sigSz, const byte* msg, word32 msgSz, + int* res, XmssKey* key); + +int wh_Client_XmssSigsLeftDma(whClientContext* ctx, XmssKey* key, + word32* sigsLeft); + +#endif /* WOLFSSL_HAVE_XMSS */ + +#endif /* WOLFHSM_CFG_DMA */ +#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */ + #endif /* !WOLFHSM_CFG_NO_CRYPTO */ #endif /* !WOLFHSM_WH_CLIENT_CRYPTO_H_ */ diff --git a/wolfhsm/wh_crypto.h b/wolfhsm/wh_crypto.h index 7254d783c..5bcb07947 100644 --- a/wolfhsm/wh_crypto.h +++ b/wolfhsm/wh_crypto.h @@ -118,6 +118,67 @@ int wh_Crypto_MlDsaDeserializeKeyDer(const uint8_t* buffer, uint16_t size, MlDsaKey* key); #endif /* HAVE_DILITHIUM */ +/* Stateful hash-based signature key serialization (LMS / XMSS). + * + * The slot blob layout is: + * uint32_t magic; + * uint16_t pubLen; + * uint16_t privLen; + * uint16_t paramLen; + * uint16_t reserved; (must be 0) + * uint8_t paramDescriptor[paramLen]; + * uint8_t pub[pubLen]; + * uint8_t priv[privLen]; + * + * paramDescriptor encodes the parameter set: + * LMS : 3 bytes (levels, height, winternitz) - paramLen == 3 + * XMSS : NUL-terminated parameter string, paramLen == strlen+1 + * + * The blob is server-internal (NVM-stored) and uses native byte order. */ +#define WH_CRYPTO_STATEFUL_SIG_BLOB_MAGIC_LMS 0x4C4D5301u /* 'LMS\1' */ +#define WH_CRYPTO_STATEFUL_SIG_BLOB_MAGIC_XMSS 0x584D5301u /* 'XMS\1' */ + +#ifdef WOLFSSL_HAVE_LMS +/* Store an LmsKey (parameter set + public key + priv_raw) into a byte + * sequence. The key must have a parameter set bound (params != NULL) and pub + * populated. priv_raw is read directly from the key. + * + * @param [in] key LmsKey to serialize. + * @param [in] max_size Capacity of buffer in bytes. + * @param [out] buffer Destination buffer. + * @param [in,out] out_size On success, total blob size. + * @return WH_ERROR_OK on success, WH_ERROR_BUFFER_SIZE if max_size is too + * small, WH_ERROR_BADARGS otherwise. */ +int wh_Crypto_LmsSerializeKey(LmsKey* key, uint16_t max_size, uint8_t* buffer, + uint16_t* out_size); + +/* Restore an LmsKey from a byte sequence. The caller must pass a key that + * has been wc_LmsKey_Init'd. After this call returns, the key has its params + * set, key->pub populated, and key->priv_raw populated. The caller must still + * install read/write callbacks and call wc_LmsKey_Reload before signing. + * + * @param [in] buffer Source blob. + * @param [in] size Blob size in bytes. + * @param [in,out] key Initialized LmsKey to populate. + * @return WH_ERROR_OK on success, WH_ERROR_BADARGS on malformed blob. */ +int wh_Crypto_LmsDeserializeKey(const uint8_t* buffer, uint16_t size, + LmsKey* key); +#endif /* WOLFSSL_HAVE_LMS */ + +#ifdef WOLFSSL_HAVE_XMSS +/* Store an XmssKey (param string + public key + secret state) into a byte + * sequence. */ +int wh_Crypto_XmssSerializeKey(XmssKey* key, const char* paramStr, + uint16_t max_size, uint8_t* buffer, + uint16_t* out_size); + +/* Restore an XmssKey from a byte sequence. The caller must pass a key that + * has been wc_XmssKey_Init'd. The function calls wc_XmssKey_SetParamStr + * (which allocates key->sk) and copies pub and sk from the blob. */ +int wh_Crypto_XmssDeserializeKey(const uint8_t* buffer, uint16_t size, + XmssKey* key); +#endif /* WOLFSSL_HAVE_XMSS */ + #endif /* !WOLFHSM_CFG_NO_CRYPTO */ #endif /* WOLFHSM_WH_CRYPTO_H_ */ diff --git a/wolfhsm/wh_message_crypto.h b/wolfhsm/wh_message_crypto.h index ac3417743..1a3e2b490 100644 --- a/wolfhsm/wh_message_crypto.h +++ b/wolfhsm/wh_message_crypto.h @@ -1337,6 +1337,123 @@ int wh_MessageCrypto_TranslateMlDsaVerifyDmaResponse( uint16_t magic, const whMessageCrypto_MlDsaVerifyDmaResponse* src, whMessageCrypto_MlDsaVerifyDmaResponse* dest); +/* Stateful hash-based signature (LMS / XMSS) DMA messages. + * + * The discriminator (LMS vs XMSS) rides on the generic request header's + * algoSubType field, set to WC_PQC_STATEFUL_SIG_TYPE_LMS or _XMSS by the + * client. Parameter selection on keygen uses lmsLevels/lmsHeight/lmsWinternitz + * when algoSubType == LMS, or xmssParamStr when algoSubType == XMSS. + * xmssParamStr is sized to fit the longest XMSS^MT name (e.g. + * "XMSSMT-SHAKE256_60/12_256") plus NUL. + */ + +/* Stateful sig DMA Key Generation Request */ +typedef struct { + whMessageCrypto_DmaBuffer pub; /* Server writes pub key here */ + uint32_t flags; + uint32_t keyId; + uint32_t access; + uint32_t labelSize; + uint32_t lmsLevels; + uint32_t lmsHeight; + uint32_t lmsWinternitz; + uint8_t label[WH_NVM_LABEL_LEN]; + char xmssParamStr[32]; + uint8_t WH_PAD[4]; /* Pad to 8-byte alignment */ +} whMessageCrypto_PqcStatefulSigKeyGenDmaRequest; + +/* Stateful sig DMA Key Generation Response */ +typedef struct { + whMessageCrypto_DmaAddrStatus dmaAddrStatus; + uint32_t keyId; + uint32_t pubSize; +} whMessageCrypto_PqcStatefulSigKeyGenDmaResponse; + +/* Stateful sig DMA Sign Request */ +typedef struct { + whMessageCrypto_DmaBuffer msg; /* Message to sign */ + whMessageCrypto_DmaBuffer sig; /* Server writes signature here */ + uint32_t options; +#define WH_MESSAGE_CRYPTO_STATEFUL_SIG_OPTIONS_EVICT (1 << 0) + uint32_t keyId; +} whMessageCrypto_PqcStatefulSigSignDmaRequest; + +/* Stateful sig DMA Sign Response */ +typedef struct { + whMessageCrypto_DmaAddrStatus dmaAddrStatus; + uint32_t sigLen; + uint8_t WH_PAD[4]; +} whMessageCrypto_PqcStatefulSigSignDmaResponse; + +/* Stateful sig DMA Verify Request */ +typedef struct { + whMessageCrypto_DmaBuffer sig; /* Signature to verify */ + whMessageCrypto_DmaBuffer msg; /* Message that was signed */ + uint32_t options; + uint32_t keyId; +} whMessageCrypto_PqcStatefulSigVerifyDmaRequest; + +/* Stateful sig DMA Verify Response */ +typedef struct { + whMessageCrypto_DmaAddrStatus dmaAddrStatus; + uint32_t res; /* 1 if signature valid, 0 otherwise */ + uint8_t WH_PAD[4]; +} whMessageCrypto_PqcStatefulSigVerifyDmaResponse; + +/* Stateful sig DMA Signatures-Left Request. + * + * No DMA buffers are required for this query; the request is named with the + * Dma suffix purely for naming consistency with the rest of the family. */ +typedef struct { + uint32_t keyId; +} whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest; + +/* Stateful sig DMA Signatures-Left Response. */ +typedef struct { + uint32_t sigsLeft; +} whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse; + +/* Stateful sig DMA translation functions */ +int wh_MessageCrypto_TranslatePqcStatefulSigKeyGenDmaRequest( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigKeyGenDmaRequest* src, + whMessageCrypto_PqcStatefulSigKeyGenDmaRequest* dest); + +int wh_MessageCrypto_TranslatePqcStatefulSigKeyGenDmaResponse( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigKeyGenDmaResponse* src, + whMessageCrypto_PqcStatefulSigKeyGenDmaResponse* dest); + +int wh_MessageCrypto_TranslatePqcStatefulSigSignDmaRequest( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigSignDmaRequest* src, + whMessageCrypto_PqcStatefulSigSignDmaRequest* dest); + +int wh_MessageCrypto_TranslatePqcStatefulSigSignDmaResponse( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigSignDmaResponse* src, + whMessageCrypto_PqcStatefulSigSignDmaResponse* dest); + +int wh_MessageCrypto_TranslatePqcStatefulSigVerifyDmaRequest( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigVerifyDmaRequest* src, + whMessageCrypto_PqcStatefulSigVerifyDmaRequest* dest); + +int wh_MessageCrypto_TranslatePqcStatefulSigVerifyDmaResponse( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigVerifyDmaResponse* src, + whMessageCrypto_PqcStatefulSigVerifyDmaResponse* dest); + +int wh_MessageCrypto_TranslatePqcStatefulSigSigsLeftDmaRequest( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest* src, + whMessageCrypto_PqcStatefulSigSigsLeftDmaRequest* dest); + +int wh_MessageCrypto_TranslatePqcStatefulSigSigsLeftDmaResponse( + uint16_t magic, + const whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse* src, + whMessageCrypto_PqcStatefulSigSigsLeftDmaResponse* dest); + /* Ed25519 DMA Sign Request */ typedef struct { whMessageCrypto_DmaBuffer msg; /* Message buffer */ diff --git a/wolfhsm/wh_server_crypto.h b/wolfhsm/wh_server_crypto.h index 655f67690..0d0d5f6e2 100644 --- a/wolfhsm/wh_server_crypto.h +++ b/wolfhsm/wh_server_crypto.h @@ -103,6 +103,29 @@ int wh_Server_MlDsaKeyCacheExport(whServerContext* ctx, whKeyId keyId, MlDsaKey* key); #endif /* HAVE_DILITHIUM */ +#ifdef WOLFSSL_HAVE_LMS +/* Persist an LmsKey (param descriptor + pub + priv_raw) into the server key + * cache. Subsequent sign operations reload state from this slot via + * wh_Server_LmsKeyCacheExport. */ +int wh_Server_LmsKeyCacheImport(whServerContext* ctx, LmsKey* key, + whKeyId keyId, whNvmFlags flags, + uint16_t label_len, uint8_t* label); +/* Restore an LmsKey from a server key cache slot. The key is left in a state + * suitable for installing read/write callbacks before invoking + * wc_LmsKey_Reload. */ +int wh_Server_LmsKeyCacheExport(whServerContext* ctx, whKeyId keyId, + LmsKey* key); +#endif /* WOLFSSL_HAVE_LMS */ + +#ifdef WOLFSSL_HAVE_XMSS +int wh_Server_XmssKeyCacheImport(whServerContext* ctx, XmssKey* key, + const char* paramStr, whKeyId keyId, + whNvmFlags flags, uint16_t label_len, + uint8_t* label); +int wh_Server_XmssKeyCacheExport(whServerContext* ctx, whKeyId keyId, + XmssKey* key); +#endif /* WOLFSSL_HAVE_XMSS */ + #ifdef HAVE_HKDF /* Store HKDF output into a server key cache with optional metadata */ int wh_Server_HkdfKeyCacheImport(whServerContext* ctx, const uint8_t* keyData,