From 0a0c980ff80d863e51bdafc40644af1901fb96b7 Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Fri, 20 Mar 2026 14:59:04 -0700 Subject: [PATCH] Fix for ML-DSA to fall back when no context is provided --- src/wh_server_crypto.c | 32 ++++++++++++++-- test/wh_test_crypto.c | 86 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c index d480da32..6aa930e2 100644 --- a/src/wh_server_crypto.c +++ b/src/wh_server_crypto.c @@ -4213,11 +4213,17 @@ static int _HandleMlDsaSign(whServerContext* ctx, uint16_t magic, int devId, key, req_context, (byte)contextSz, res_out, &res_len, in, in_len, preHashType, ctx->crypto->rng); } - else { + else if (contextSz > 0) { ret = wc_MlDsaKey_SignCtx( key, req_context, (byte)contextSz, res_out, &res_len, in, in_len, ctx->crypto->rng); } + else { + /* Fall back to legacy method if no context is provided */ + ret = wc_MlDsaKey_Sign( + key, res_out, &res_len, + in, in_len, ctx->crypto->rng); + } } wc_MlDsaKey_Free(key); } @@ -4318,11 +4324,17 @@ static int _HandleMlDsaVerify(whServerContext* ctx, uint16_t magic, int devId, key, req_sig, sig_len, req_context, (byte)contextSz, req_hash, hash_len, preHashType, &result); } - else { + else if (contextSz > 0) { ret = wc_MlDsaKey_VerifyCtx( key, req_sig, sig_len, req_context, (byte)contextSz, req_hash, hash_len, &result); } + else { + /* Fall back to legacy method if no context is provided */ + ret = wc_MlDsaKey_Verify( + key, req_sig, sig_len, + req_hash, hash_len, &result); + } } wc_MlDsaKey_Free(key); } @@ -5455,12 +5467,18 @@ static int _HandleMlDsaSignDma(whServerContext* ctx, uint16_t magic, int devId, sigAddr, &sigLen, msgAddr, req.msg.sz, preHashType, ctx->crypto->rng); } - else { + else if (contextSz > 0) { ret = wc_MlDsaKey_SignCtx( key, req_context, (byte)contextSz, sigAddr, &sigLen, msgAddr, req.msg.sz, ctx->crypto->rng); } + else { + /* Fall back to legacy method if no context is provided */ + ret = wc_MlDsaKey_Sign( + key, sigAddr, &sigLen, + msgAddr, req.msg.sz, ctx->crypto->rng); + } if (ret == 0) { /* Post-write processing of signature buffer */ @@ -5592,11 +5610,17 @@ static int _HandleMlDsaVerifyDma(whServerContext* ctx, uint16_t magic, key, sigAddr, req.sig.sz, req_context, (byte)contextSz, msgAddr, req.msg.sz, preHashType, &verified); } - else { + else if (contextSz > 0) { ret = wc_MlDsaKey_VerifyCtx( key, sigAddr, req.sig.sz, req_context, (byte)contextSz, msgAddr, req.msg.sz, &verified); } + else { + /* Fall back to legacy method if no context is provided */ + ret = wc_MlDsaKey_Verify( + key, sigAddr, req.sig.sz, + msgAddr, req.msg.sz, &verified); + } if (ret == 0) { /* Post-read processing of signature buffer */ diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index 34ea5f4c..892e3b8e 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -4182,6 +4182,88 @@ static int whTestCrypto_Cmac(whClientContext* ctx, int devId, WC_RNG* rng) #if !defined(WOLFSSL_DILITHIUM_NO_VERIFY) && \ !defined(WOLFSSL_DILITHIUM_NO_SIGN) && \ !defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && !defined(WOLFSSL_NO_ML_DSA_44) +/* Test that a signature created locally with wc_MlDsaKey_Sign (basic API, + * no FIPS 204 context) can be verified through the wolfHSM server using + * wh_Client_MlDsaVerify. This is the flow that wolfBoot uses: sign at build + * time with wolfCrypt, verify at boot through the HSM crypto callback. */ +static int whTestCrypto_MlDsaLocalSignServerVerify(whClientContext* ctx, + int devId, WC_RNG* rng) +{ + (void)devId; + + int ret = 0; + int verified = 0; + MlDsaKey key[1]; + byte msg[] = "wolfBoot-style local sign, HSM verify"; + byte sig[DILITHIUM_ML_DSA_44_SIG_SIZE]; + word32 sigSz = sizeof(sig); + + /* Initialize and generate a key pair locally */ + ret = wc_MlDsaKey_Init(key, NULL, INVALID_DEVID); + if (ret != 0) { + WH_ERROR_PRINT("Failed to init ML-DSA key: %d\n", ret); + return ret; + } + + ret = wc_MlDsaKey_SetParams(key, WC_ML_DSA_44); + if (ret != 0) { + WH_ERROR_PRINT("Failed to set ML-DSA params: %d\n", ret); + wc_MlDsaKey_Free(key); + return ret; + } + + ret = wc_MlDsaKey_MakeKey(key, rng); + if (ret != 0) { + WH_ERROR_PRINT("Failed to generate ML-DSA key: %d\n", ret); + wc_MlDsaKey_Free(key); + return ret; + } + + /* Sign locally using the basic wolfCrypt API (no context, no HSM) */ + ret = wc_MlDsaKey_Sign(key, sig, &sigSz, msg, sizeof(msg), rng); + if (ret != 0) { + WH_ERROR_PRINT("Failed to sign locally with ML-DSA: %d\n", ret); + wc_MlDsaKey_Free(key); + return ret; + } + + /* Now verify through the wolfHSM server using the client API. + * The server must use wc_MlDsaKey_Verify (not VerifyCtx) when + * contextSz == 0 and preHashType == WC_HASH_TYPE_NONE. */ + ret = wh_Client_MlDsaVerify(ctx, sig, sigSz, msg, sizeof(msg), &verified, + key, NULL, 0, WC_HASH_TYPE_NONE); + if (ret != 0) { + WH_ERROR_PRINT("Failed to verify via HSM: %d\n", ret); + wc_MlDsaKey_Free(key); + return ret; + } + + if (!verified) { + WH_ERROR_PRINT("ML-DSA local-sign/HSM-verify: signature invalid\n"); + wc_MlDsaKey_Free(key); + return -1; + } + + /* Tampered signature should fail */ + sig[0] ^= 0xFF; + ret = wh_Client_MlDsaVerify(ctx, sig, sigSz, msg, sizeof(msg), &verified, + key, NULL, 0, WC_HASH_TYPE_NONE); + if (ret != 0) { + WH_ERROR_PRINT("Failed to call verify with tampered sig: %d\n", ret); + wc_MlDsaKey_Free(key); + return ret; + } + if (verified) { + WH_ERROR_PRINT("ML-DSA local-sign/HSM-verify: accepted tampered sig\n"); + wc_MlDsaKey_Free(key); + return -1; + } + + WH_TEST_PRINT("ML-DSA Local-Sign/HSM-Verify SUCCESS\n"); + wc_MlDsaKey_Free(key); + return 0; +} + static int whTestCrypto_MlDsaWolfCrypt(whClientContext* ctx, int devId, WC_RNG* rng) { @@ -5946,6 +6028,10 @@ int whTest_CryptoClientConfig(whClientConfig* config) } } + if (ret == 0) { + ret = whTestCrypto_MlDsaLocalSignServerVerify(client, WH_DEV_ID, rng); + } + if (ret == 0) { ret = whTestCrypto_MlDsaClient(client, WH_DEV_ID, rng); }