From 3a36f930b11032f1cd6ef58912a906b756e24e1c Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Tue, 21 Jul 2026 16:33:02 +0900 Subject: [PATCH] Translate SHA-2 intermediate state for cross-endian peers --- docs/draft/async-crypto.md | 18 +- src/wh_message_crypto.c | 102 ++++-- src/wh_server_crypto.c | 40 +- test-refactor/misc/wh_test_message_crypto.c | 384 ++++++++++++++++++++ test-refactor/wh_test_list.c | 2 + wolfhsm/wh_message_crypto.h | 12 + 6 files changed, 514 insertions(+), 44 deletions(-) create mode 100644 test-refactor/misc/wh_test_message_crypto.c diff --git a/docs/draft/async-crypto.md b/docs/draft/async-crypto.md index 92fdb3caf..9a57b81b3 100644 --- a/docs/draft/async-crypto.md +++ b/docs/draft/async-crypto.md @@ -128,7 +128,7 @@ is: | Sha256Request / Sha512Request | resumeState + control fields | resumeState.hiLen (4 bytes) | | resumeState.loLen (4 bytes) | -| resumeState.hash (32 or 64 bytes) | intermediate digest +| resumeState.hash (32 or 64 bytes) | intermediate digest (see below) | [resumeState.hashType (4 bytes)] | SHA-512 family only | isLastBlock (4 bytes) | | inSz (4 bytes) | @@ -346,6 +346,22 @@ reaches the server: The hash state (`resumeState`) always travels **inline**, not via DMA, for cross-architecture concerns (endian translation, etc.) +### Endianness of `resumeState.hash` + +`resumeState.hash` is not an opaque byte array. wolfCrypt keeps the SHA-2 +chaining variables in `wc_Sha256.digest` / `wc_Sha512.digest` as **host-order** +`word32` / `word64`, and the client copies them to the wire verbatim, so the +field carries eight chaining words in the *client's* byte order. The server +byte-swaps each word when the client's `magic` reports the opposite endianness, +exactly as it does for the SHA-3 Keccak state (`whMessageCrypto_Sha3State`). + +The response field is context-dependent: on a non-final update it carries the +same host-order chaining words, but on `isLastBlock` it carries the finalized +digest, which wolfCrypt has already emitted in canonical big-endian byte order +and which must not be swapped. The response translation helpers therefore take +the chaining word size (4, 8, or 0 for a finalized digest) from the caller — +see `wh_MessageCrypto_TranslateSha2Response_ex()`. + DMA async functions require the client to stash the translated DMA address across the Request/Response boundary for POST cleanup. This context is stored in `whClientContext.dma.asyncCtx.sha`: diff --git a/src/wh_message_crypto.c b/src/wh_message_crypto.c index 8012252ec..937f42c88 100644 --- a/src/wh_message_crypto.c +++ b/src/wh_message_crypto.c @@ -639,6 +639,43 @@ int wh_MessageCrypto_TranslateEd25519VerifyResponse( return 0; } +/* Number of chaining words in a SHA-2 intermediate state, for every variant */ +#define WH_MESSAGE_CRYPTO_SHA2_STATE_WORDS 8 + +/* Swap the host-order chaining words of a serialized SHA-2 state, as the SHA3 + * Keccak state does. wordSize is 4 (SHA224/256) or 8 (SHA384/512); any other + * value, including 0 for a finalized digest, copies the array verbatim. */ +static void _TranslateSha2State(uint16_t magic, uint8_t* dest, + const uint8_t* src, uint32_t hashSz, + uint32_t wordSize) +{ + uint32_t i; + uint32_t stateSz = WH_MESSAGE_CRYPTO_SHA2_STATE_WORDS * wordSize; + uint32_t w32; + uint64_t w64; + + if (dest != src) { + memcpy(dest, src, hashSz); + } + if (stateSz > hashSz) { + return; + } + if (wordSize == sizeof(uint32_t)) { + for (i = 0; i < stateSz; i += wordSize) { + memcpy(&w32, dest + i, sizeof(w32)); + w32 = wh_Translate32(magic, w32); + memcpy(dest + i, &w32, sizeof(w32)); + } + } + else if (wordSize == sizeof(uint64_t)) { + for (i = 0; i < stateSz; i += wordSize) { + memcpy(&w64, dest + i, sizeof(w64)); + w64 = wh_Translate64(magic, w64); + memcpy(dest + i, &w64, sizeof(w64)); + } + } +} + /* SHA256 Request translation. Only the fixed-size header is translated; any * trailing variable-length input bytes (uint8_t in[inSz]) are raw bytes that * do not need endian translation. */ @@ -651,11 +688,10 @@ int wh_MessageCrypto_TranslateSha256Request( } WH_T32(magic, dest, src, resumeState.hiLen); WH_T32(magic, dest, src, resumeState.loLen); - /* Hash value is just a byte array, no translation needed */ - if (src != dest) { - memcpy(dest->resumeState.hash, src->resumeState.hash, - sizeof(src->resumeState.hash)); - } + /* A request always carries intermediate state, even on the last block */ + _TranslateSha2State(magic, dest->resumeState.hash, src->resumeState.hash, + (uint32_t)sizeof(src->resumeState.hash), + (uint32_t)sizeof(uint32_t)); WH_T32(magic, dest, src, isLastBlock); WH_T32(magic, dest, src, inSz); return 0; @@ -675,21 +711,29 @@ int wh_MessageCrypto_TranslateSha512Request( WH_T32(magic, dest, src, resumeState.hiLen); WH_T32(magic, dest, src, resumeState.loLen); WH_T32(magic, dest, src, resumeState.hashType); - /* Hash value is just a byte array, no translation needed */ - if (src != dest) { - memcpy(dest->resumeState.hash, src->resumeState.hash, - sizeof(src->resumeState.hash)); - } + /* A request always carries intermediate state, even on the last block */ + _TranslateSha2State(magic, dest->resumeState.hash, src->resumeState.hash, + (uint32_t)sizeof(src->resumeState.hash), + (uint32_t)sizeof(uint64_t)); WH_T32(magic, dest, src, isLastBlock); WH_T32(magic, dest, src, inSz); return 0; } #endif /* WOLFSSL_SHA512 || WOLFSSL_SHA384 */ -/* SHA2 Response translation */ +/* SHA2 Response translation, treating hash as a finalized digest */ int wh_MessageCrypto_TranslateSha2Response( uint16_t magic, const whMessageCrypto_Sha2Response* src, whMessageCrypto_Sha2Response* dest) +{ + return wh_MessageCrypto_TranslateSha2Response_ex(magic, src, dest, 0); +} + +/* SHA2 Response translation. stateWordSize is the chaining word size when hash + * carries intermediate state, or 0 when it carries a finalized digest. */ +int wh_MessageCrypto_TranslateSha2Response_ex( + uint16_t magic, const whMessageCrypto_Sha2Response* src, + whMessageCrypto_Sha2Response* dest, uint32_t stateWordSize) { if ((src == NULL) || (dest == NULL)) { return WH_ERROR_BADARGS; @@ -697,10 +741,8 @@ int wh_MessageCrypto_TranslateSha2Response( WH_T32(magic, dest, src, hiLen); WH_T32(magic, dest, src, loLen); WH_T32(magic, dest, src, hashType); - /* Hash value is just a byte array, no translation needed */ - if (src != dest) { - memcpy(dest->hash, src->hash, sizeof(src->hash)); - } + _TranslateSha2State(magic, dest->hash, src->hash, + (uint32_t)sizeof(src->hash), stateWordSize); return 0; } @@ -1024,10 +1066,9 @@ int wh_MessageCrypto_TranslateSha256DmaRequest( WH_T32(magic, dest, src, resumeState.hiLen); WH_T32(magic, dest, src, resumeState.loLen); - if (src != dest) { - memcpy(dest->resumeState.hash, src->resumeState.hash, - sizeof(src->resumeState.hash)); - } + _TranslateSha2State(magic, dest->resumeState.hash, src->resumeState.hash, + (uint32_t)sizeof(src->resumeState.hash), + (uint32_t)sizeof(uint32_t)); ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->input, &dest->input); if (ret != 0) { @@ -1053,10 +1094,9 @@ int wh_MessageCrypto_TranslateSha512DmaRequest( WH_T32(magic, dest, src, resumeState.hiLen); WH_T32(magic, dest, src, resumeState.loLen); - if (src != dest) { - memcpy(dest->resumeState.hash, src->resumeState.hash, - sizeof(src->resumeState.hash)); - } + _TranslateSha2State(magic, dest->resumeState.hash, src->resumeState.hash, + (uint32_t)sizeof(src->resumeState.hash), + (uint32_t)sizeof(uint64_t)); WH_T32(magic, dest, src, resumeState.hashType); ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->input, &dest->input); @@ -1070,10 +1110,19 @@ int wh_MessageCrypto_TranslateSha512DmaRequest( return 0; } -/* SHA2 DMA Response translation */ +/* SHA2 DMA Response translation, treating hash as a finalized digest */ int wh_MessageCrypto_TranslateSha2DmaResponse( uint16_t magic, const whMessageCrypto_Sha2DmaResponse* src, whMessageCrypto_Sha2DmaResponse* dest) +{ + return wh_MessageCrypto_TranslateSha2DmaResponse_ex(magic, src, dest, 0); +} + +/* SHA2 DMA Response translation. stateWordSize is the chaining word size when + * hash carries intermediate state, or 0 when it carries a finalized digest. */ +int wh_MessageCrypto_TranslateSha2DmaResponse_ex( + uint16_t magic, const whMessageCrypto_Sha2DmaResponse* src, + whMessageCrypto_Sha2DmaResponse* dest, uint32_t stateWordSize) { if ((src == NULL) || (dest == NULL)) { return WH_ERROR_BADARGS; @@ -1081,9 +1130,8 @@ int wh_MessageCrypto_TranslateSha2DmaResponse( WH_T32(magic, dest, src, hiLen); WH_T32(magic, dest, src, loLen); - if (src != dest) { - memcpy(dest->hash, src->hash, sizeof(src->hash)); - } + _TranslateSha2State(magic, dest->hash, src->hash, + (uint32_t)sizeof(src->hash), stateWordSize); WH_T32(magic, dest, src, hashType); return wh_MessageCrypto_TranslateDmaAddrStatus(magic, &src->dmaAddrStatus, diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c index f83224dc4..d719f9f84 100644 --- a/src/wh_server_crypto.c +++ b/src/wh_server_crypto.c @@ -4234,8 +4234,9 @@ static int _HandleSha256(whServerContext* ctx, uint16_t magic, int devId, /* Translate the response */ if (ret == 0) { - ret = - wh_MessageCrypto_TranslateSha2Response(magic, &res, cryptoDataOut); + ret = wh_MessageCrypto_TranslateSha2Response_ex( + magic, &res, cryptoDataOut, + req.isLastBlock ? 0 : (uint32_t)sizeof(uint32_t)); if (ret == 0) { *outSize = sizeof(res); } @@ -4325,8 +4326,9 @@ static int _HandleSha224(whServerContext* ctx, uint16_t magic, int devId, /* Translate the response */ if (ret == 0) { - ret = - wh_MessageCrypto_TranslateSha2Response(magic, &res, cryptoDataOut); + ret = wh_MessageCrypto_TranslateSha2Response_ex( + magic, &res, cryptoDataOut, + req.isLastBlock ? 0 : (uint32_t)sizeof(uint32_t)); if (ret == 0) { *outSize = sizeof(res); } @@ -4421,8 +4423,9 @@ static int _HandleSha384(whServerContext* ctx, uint16_t magic, int devId, /* Translate the response */ if (ret == 0) { - ret = - wh_MessageCrypto_TranslateSha2Response(magic, &res, cryptoDataOut); + ret = wh_MessageCrypto_TranslateSha2Response_ex( + magic, &res, cryptoDataOut, + req.isLastBlock ? 0 : (uint32_t)sizeof(uint64_t)); if (ret == 0) { *outSize = sizeof(res); } @@ -4545,8 +4548,9 @@ static int _HandleSha512(whServerContext* ctx, uint16_t magic, int devId, /* Translate the response */ if (ret == 0) { - ret = - wh_MessageCrypto_TranslateSha2Response(magic, &res, cryptoDataOut); + ret = wh_MessageCrypto_TranslateSha2Response_ex( + magic, &res, cryptoDataOut, + req.isLastBlock ? 0 : (uint32_t)sizeof(uint64_t)); if (ret == 0) { *outSize = sizeof(res); } @@ -5920,8 +5924,9 @@ static int _HandleSha256Dma(whServerContext* ctx, uint16_t magic, int devId, } } - (void)wh_MessageCrypto_TranslateSha2DmaResponse( - magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut); + (void)wh_MessageCrypto_TranslateSha2DmaResponse_ex( + magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut, + req.isLastBlock ? 0 : (uint32_t)sizeof(uint32_t)); *outSize = sizeof(res); return ret; @@ -6024,8 +6029,9 @@ static int _HandleSha224Dma(whServerContext* ctx, uint16_t magic, int devId, } } - (void)wh_MessageCrypto_TranslateSha2DmaResponse( - magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut); + (void)wh_MessageCrypto_TranslateSha2DmaResponse_ex( + magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut, + req.isLastBlock ? 0 : (uint32_t)sizeof(uint32_t)); *outSize = sizeof(res); return ret; @@ -6128,8 +6134,9 @@ static int _HandleSha384Dma(whServerContext* ctx, uint16_t magic, int devId, } } - (void)wh_MessageCrypto_TranslateSha2DmaResponse( - magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut); + (void)wh_MessageCrypto_TranslateSha2DmaResponse_ex( + magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut, + req.isLastBlock ? 0 : (uint32_t)sizeof(uint64_t)); *outSize = sizeof(res); return ret; @@ -6266,8 +6273,9 @@ static int _HandleSha512Dma(whServerContext* ctx, uint16_t magic, int devId, } } - (void)wh_MessageCrypto_TranslateSha2DmaResponse( - magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut); + (void)wh_MessageCrypto_TranslateSha2DmaResponse_ex( + magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut, + req.isLastBlock ? 0 : (uint32_t)sizeof(uint64_t)); *outSize = sizeof(res); return ret; diff --git a/test-refactor/misc/wh_test_message_crypto.c b/test-refactor/misc/wh_test_message_crypto.c new file mode 100644 index 000000000..e37d97834 --- /dev/null +++ b/test-refactor/misc/wh_test_message_crypto.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ + +/* Endian-translation coverage for the crypto message structs. A loopback test + * cannot observe peers that disagree on byte order, so these call the + * translation helpers directly with the magic of a foreign-endian peer. */ + +#include "wolfhsm/wh_settings.h" + +#include +#include + +#include "wolfhsm/wh_comm.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_message_crypto.h" + +#include "wh_test_common.h" +#include "wh_test_list.h" + +/* Number of chaining words a SHA-2 intermediate state carries */ +#define WH_TEST_SHA2_STATE_WORDS 8 + +/* Magic of a peer whose byte order is the opposite of ours */ +#define WH_TEST_MAGIC_FOREIGN ((uint16_t)WH_COMM_MAGIC_SWAP) +#define WH_TEST_MAGIC_LOCAL ((uint16_t)WH_COMM_MAGIC_NATIVE) + +/* Fill with distinct byte values so any missed swap is visible */ +static void _whTest_FillPattern(uint8_t* buf, uint32_t len, uint8_t seed) +{ + uint32_t i; + + for (i = 0; i < len; i++) { + buf[i] = (uint8_t)(seed + i); + } +} + +/* Return 1 if each word of dest is the matching word of src, byte-reversed */ +static int _whTest_WordsSwapped(const uint8_t* dest, const uint8_t* src, + uint32_t wordSize, uint32_t words) +{ + uint32_t i; + uint32_t j; + + for (i = 0; i < words; i++) { + for (j = 0; j < wordSize; j++) { + if (dest[(i * wordSize) + j] != + src[(i * wordSize) + (wordSize - 1u - j)]) { + return 0; + } + } + } + return 1; +} + +static int _whTest_MessageCryptoSha256Request(void) +{ + whMessageCrypto_Sha256Request src; + whMessageCrypto_Sha256Request dest; + whMessageCrypto_Sha256Request back; + + memset(&src, 0, sizeof(src)); + memset(&dest, 0, sizeof(dest)); + memset(&back, 0, sizeof(back)); + + src.resumeState.hiLen = 0x01020304u; + src.resumeState.loLen = 0x05060708u; + src.isLastBlock = 1; + src.inSz = 0x090a0b0cu; + _whTest_FillPattern(src.resumeState.hash, + (uint32_t)sizeof(src.resumeState.hash), 0x10); + + /* A same-endian peer must see the message unchanged */ + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha256Request(WH_TEST_MAGIC_LOCAL, &src, + &dest)); + WH_TEST_ASSERT_RETURN(memcmp(&dest, &src, sizeof(src)) == 0); + + /* A cross-endian peer swaps every chaining word. A request carries + * intermediate state even on the last block, so isLastBlock is set here. */ + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha256Request(WH_TEST_MAGIC_FOREIGN, &src, + &dest)); + WH_TEST_ASSERT_RETURN( + _whTest_WordsSwapped(dest.resumeState.hash, src.resumeState.hash, + (uint32_t)sizeof(uint32_t), + WH_TEST_SHA2_STATE_WORDS)); + WH_TEST_ASSERT_RETURN(dest.resumeState.hiLen == + wh_Translate32(WH_TEST_MAGIC_FOREIGN, + src.resumeState.hiLen)); + + /* Translation is its own inverse */ + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha256Request(WH_TEST_MAGIC_FOREIGN, &dest, + &back)); + WH_TEST_ASSERT_RETURN(memcmp(&back, &src, sizeof(src)) == 0); + + /* In-place translation must give the same result */ + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha256Request(WH_TEST_MAGIC_FOREIGN, &back, + &back)); + WH_TEST_ASSERT_RETURN(memcmp(&back, &dest, sizeof(src)) == 0); + + return WH_TEST_SUCCESS; +} + +#if defined(WOLFSSL_SHA512) || defined(WOLFSSL_SHA384) +static int _whTest_MessageCryptoSha512Request(void) +{ + whMessageCrypto_Sha512Request src; + whMessageCrypto_Sha512Request dest; + whMessageCrypto_Sha512Request back; + + memset(&src, 0, sizeof(src)); + memset(&dest, 0, sizeof(dest)); + memset(&back, 0, sizeof(back)); + + src.resumeState.hiLen = 0x01020304u; + src.resumeState.loLen = 0x05060708u; + src.resumeState.hashType = 0x090a0b0cu; + src.isLastBlock = 0; + src.inSz = 128; + _whTest_FillPattern(src.resumeState.hash, + (uint32_t)sizeof(src.resumeState.hash), 0x20); + + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha512Request(WH_TEST_MAGIC_LOCAL, &src, + &dest)); + WH_TEST_ASSERT_RETURN(memcmp(&dest, &src, sizeof(src)) == 0); + + /* The SHA512 family chains 64-bit words */ + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha512Request(WH_TEST_MAGIC_FOREIGN, &src, + &dest)); + WH_TEST_ASSERT_RETURN( + _whTest_WordsSwapped(dest.resumeState.hash, src.resumeState.hash, + (uint32_t)sizeof(uint64_t), + WH_TEST_SHA2_STATE_WORDS)); + + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha512Request(WH_TEST_MAGIC_FOREIGN, &dest, + &back)); + WH_TEST_ASSERT_RETURN(memcmp(&back, &src, sizeof(src)) == 0); + + return WH_TEST_SUCCESS; +} +#endif /* WOLFSSL_SHA512 || WOLFSSL_SHA384 */ + +static int _whTest_MessageCryptoSha2Response(void) +{ + whMessageCrypto_Sha2Response src; + whMessageCrypto_Sha2Response dest; + whMessageCrypto_Sha2Response back; + + memset(&src, 0, sizeof(src)); + memset(&dest, 0, sizeof(dest)); + memset(&back, 0, sizeof(back)); + + src.hiLen = 0x01020304u; + src.loLen = 0x05060708u; + src.hashType = 0x090a0b0cu; + _whTest_FillPattern(src.hash, (uint32_t)sizeof(src.hash), 0x30); + + WH_TEST_RETURN_ON_FAIL(wh_MessageCrypto_TranslateSha2Response_ex( + WH_TEST_MAGIC_LOCAL, &src, &dest, (uint32_t)sizeof(uint32_t))); + WH_TEST_ASSERT_RETURN(memcmp(&dest, &src, sizeof(src)) == 0); + + /* SHA224/256 state: the first 8 words swap, the unused tail is copied */ + WH_TEST_RETURN_ON_FAIL(wh_MessageCrypto_TranslateSha2Response_ex( + WH_TEST_MAGIC_FOREIGN, &src, &dest, (uint32_t)sizeof(uint32_t))); + WH_TEST_ASSERT_RETURN(_whTest_WordsSwapped(dest.hash, src.hash, + (uint32_t)sizeof(uint32_t), + WH_TEST_SHA2_STATE_WORDS)); + WH_TEST_ASSERT_RETURN(memcmp(&dest.hash[32], &src.hash[32], 32) == 0); + WH_TEST_RETURN_ON_FAIL(wh_MessageCrypto_TranslateSha2Response_ex( + WH_TEST_MAGIC_FOREIGN, &dest, &back, (uint32_t)sizeof(uint32_t))); + WH_TEST_ASSERT_RETURN(memcmp(&back, &src, sizeof(src)) == 0); + + /* SHA384/512 state: all 8 words are 64 bits wide */ + WH_TEST_RETURN_ON_FAIL(wh_MessageCrypto_TranslateSha2Response_ex( + WH_TEST_MAGIC_FOREIGN, &src, &dest, (uint32_t)sizeof(uint64_t))); + WH_TEST_ASSERT_RETURN(_whTest_WordsSwapped(dest.hash, src.hash, + (uint32_t)sizeof(uint64_t), + WH_TEST_SHA2_STATE_WORDS)); + WH_TEST_RETURN_ON_FAIL(wh_MessageCrypto_TranslateSha2Response_ex( + WH_TEST_MAGIC_FOREIGN, &dest, &back, (uint32_t)sizeof(uint64_t))); + WH_TEST_ASSERT_RETURN(memcmp(&back, &src, sizeof(src)) == 0); + + /* A finalized digest is already in canonical order and must not move, + * which is what the plain (non _ex) helper is for */ + WH_TEST_RETURN_ON_FAIL(wh_MessageCrypto_TranslateSha2Response_ex( + WH_TEST_MAGIC_FOREIGN, &src, &dest, 0)); + WH_TEST_ASSERT_RETURN(memcmp(dest.hash, src.hash, sizeof(src.hash)) == 0); + WH_TEST_ASSERT_RETURN(dest.loLen == + wh_Translate32(WH_TEST_MAGIC_FOREIGN, src.loLen)); + + memset(&dest, 0, sizeof(dest)); + WH_TEST_RETURN_ON_FAIL(wh_MessageCrypto_TranslateSha2Response( + WH_TEST_MAGIC_FOREIGN, &src, &dest)); + WH_TEST_ASSERT_RETURN(memcmp(dest.hash, src.hash, sizeof(src.hash)) == 0); + + return WH_TEST_SUCCESS; +} + +#if defined(WOLFHSM_CFG_DMA) +static int _whTest_MessageCryptoSha256DmaRequest(void) +{ + whMessageCrypto_Sha256DmaRequest src; + whMessageCrypto_Sha256DmaRequest dest; + whMessageCrypto_Sha256DmaRequest back; + + memset(&src, 0, sizeof(src)); + memset(&dest, 0, sizeof(dest)); + memset(&back, 0, sizeof(back)); + + src.resumeState.hiLen = 0x01020304u; + src.resumeState.loLen = 0x05060708u; + src.input.addr = 0x1122334455667788ull; + src.input.sz = 64; + src.isLastBlock = 0; + src.inSz = 0; + _whTest_FillPattern(src.resumeState.hash, + (uint32_t)sizeof(src.resumeState.hash), 0x40); + + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha256DmaRequest(WH_TEST_MAGIC_LOCAL, &src, + &dest)); + WH_TEST_ASSERT_RETURN(memcmp(&dest, &src, sizeof(src)) == 0); + + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha256DmaRequest(WH_TEST_MAGIC_FOREIGN, &src, + &dest)); + WH_TEST_ASSERT_RETURN( + _whTest_WordsSwapped(dest.resumeState.hash, src.resumeState.hash, + (uint32_t)sizeof(uint32_t), + WH_TEST_SHA2_STATE_WORDS)); + + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha256DmaRequest(WH_TEST_MAGIC_FOREIGN, &dest, + &back)); + WH_TEST_ASSERT_RETURN(memcmp(&back, &src, sizeof(src)) == 0); + + return WH_TEST_SUCCESS; +} + +static int _whTest_MessageCryptoSha512DmaRequest(void) +{ + whMessageCrypto_Sha512DmaRequest src; + whMessageCrypto_Sha512DmaRequest dest; + whMessageCrypto_Sha512DmaRequest back; + + memset(&src, 0, sizeof(src)); + memset(&dest, 0, sizeof(dest)); + memset(&back, 0, sizeof(back)); + + src.resumeState.hiLen = 0x01020304u; + src.resumeState.loLen = 0x05060708u; + src.resumeState.hashType = 0x090a0b0cu; + src.input.addr = 0x1122334455667788ull; + src.input.sz = 128; + _whTest_FillPattern(src.resumeState.hash, + (uint32_t)sizeof(src.resumeState.hash), 0x50); + + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha512DmaRequest(WH_TEST_MAGIC_LOCAL, &src, + &dest)); + WH_TEST_ASSERT_RETURN(memcmp(&dest, &src, sizeof(src)) == 0); + + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha512DmaRequest(WH_TEST_MAGIC_FOREIGN, &src, + &dest)); + WH_TEST_ASSERT_RETURN( + _whTest_WordsSwapped(dest.resumeState.hash, src.resumeState.hash, + (uint32_t)sizeof(uint64_t), + WH_TEST_SHA2_STATE_WORDS)); + + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha512DmaRequest(WH_TEST_MAGIC_FOREIGN, &dest, + &back)); + WH_TEST_ASSERT_RETURN(memcmp(&back, &src, sizeof(src)) == 0); + + return WH_TEST_SUCCESS; +} + +static int _whTest_MessageCryptoSha2DmaResponse(void) +{ + whMessageCrypto_Sha2DmaResponse src; + whMessageCrypto_Sha2DmaResponse dest; + whMessageCrypto_Sha2DmaResponse back; + + memset(&src, 0, sizeof(src)); + memset(&dest, 0, sizeof(dest)); + memset(&back, 0, sizeof(back)); + + src.hiLen = 0x01020304u; + src.loLen = 0x05060708u; + src.hashType = 0x090a0b0cu; + src.dmaAddrStatus.badAddr.addr = 0x1122334455667788ull; + _whTest_FillPattern(src.hash, (uint32_t)sizeof(src.hash), 0x60); + + WH_TEST_RETURN_ON_FAIL(wh_MessageCrypto_TranslateSha2DmaResponse_ex( + WH_TEST_MAGIC_FOREIGN, &src, &dest, (uint32_t)sizeof(uint32_t))); + WH_TEST_ASSERT_RETURN(_whTest_WordsSwapped(dest.hash, src.hash, + (uint32_t)sizeof(uint32_t), + WH_TEST_SHA2_STATE_WORDS)); + WH_TEST_RETURN_ON_FAIL(wh_MessageCrypto_TranslateSha2DmaResponse_ex( + WH_TEST_MAGIC_FOREIGN, &dest, &back, (uint32_t)sizeof(uint32_t))); + WH_TEST_ASSERT_RETURN(memcmp(&back, &src, sizeof(src)) == 0); + + WH_TEST_RETURN_ON_FAIL(wh_MessageCrypto_TranslateSha2DmaResponse_ex( + WH_TEST_MAGIC_FOREIGN, &src, &dest, (uint32_t)sizeof(uint64_t))); + WH_TEST_ASSERT_RETURN(_whTest_WordsSwapped(dest.hash, src.hash, + (uint32_t)sizeof(uint64_t), + WH_TEST_SHA2_STATE_WORDS)); + + /* Final block: the digest is canonical already */ + WH_TEST_RETURN_ON_FAIL(wh_MessageCrypto_TranslateSha2DmaResponse( + WH_TEST_MAGIC_FOREIGN, &src, &dest)); + WH_TEST_ASSERT_RETURN(memcmp(dest.hash, src.hash, sizeof(src.hash)) == 0); + + return WH_TEST_SUCCESS; +} +#endif /* WOLFHSM_CFG_DMA */ + +/* The SHA3 Keccak state has always been translated per word. Checking it with + * the same helper confirms the helper agrees with known-good behavior. */ +static int _whTest_MessageCryptoSha3State(void) +{ + whMessageCrypto_Sha3State src; + whMessageCrypto_Sha3State dest; + int i; + + memset(&src, 0, sizeof(src)); + memset(&dest, 0, sizeof(dest)); + + for (i = 0; i < 25; i++) { + src.s[i] = 0x0102030405060708ull + (uint64_t)i; + } + + WH_TEST_RETURN_ON_FAIL( + wh_MessageCrypto_TranslateSha3State(WH_TEST_MAGIC_FOREIGN, &src, + &dest)); + WH_TEST_ASSERT_RETURN(_whTest_WordsSwapped((const uint8_t*)dest.s, + (const uint8_t*)src.s, + (uint32_t)sizeof(uint64_t), 25)); + + return WH_TEST_SUCCESS; +} + +int whTest_MessageCryptoTranslate(void* ctx) +{ + (void)ctx; + + WH_TEST_PRINT("Testing crypto message translation...\n"); + + WH_TEST_RETURN_ON_FAIL(_whTest_MessageCryptoSha256Request()); +#if defined(WOLFSSL_SHA512) || defined(WOLFSSL_SHA384) + WH_TEST_RETURN_ON_FAIL(_whTest_MessageCryptoSha512Request()); +#endif + WH_TEST_RETURN_ON_FAIL(_whTest_MessageCryptoSha2Response()); +#if defined(WOLFHSM_CFG_DMA) + WH_TEST_RETURN_ON_FAIL(_whTest_MessageCryptoSha256DmaRequest()); + WH_TEST_RETURN_ON_FAIL(_whTest_MessageCryptoSha512DmaRequest()); + WH_TEST_RETURN_ON_FAIL(_whTest_MessageCryptoSha2DmaResponse()); +#endif + WH_TEST_RETURN_ON_FAIL(_whTest_MessageCryptoSha3State()); + + return WH_TEST_SUCCESS; +} diff --git a/test-refactor/wh_test_list.c b/test-refactor/wh_test_list.c index 70f7d0396..88dc37aa9 100644 --- a/test-refactor/wh_test_list.c +++ b/test-refactor/wh_test_list.c @@ -45,6 +45,7 @@ WH_TEST_DECL(whTest_Comm); WH_TEST_DECL(whTest_Dma); WH_TEST_DECL(whTest_HwKeystore); WH_TEST_DECL(whTest_KeystoreReqSize); +WH_TEST_DECL(whTest_MessageCryptoTranslate); WH_TEST_DECL(whTest_MultiClient); WH_TEST_DECL(whTest_Lock); WH_TEST_DECL(whTest_CertVerify); @@ -102,6 +103,7 @@ const whTestCase whTestsMisc[] = { { "whTest_Comm", whTest_Comm }, { "whTest_Dma", whTest_Dma }, { "whTest_KeystoreReqSize", whTest_KeystoreReqSize }, + { "whTest_MessageCryptoTranslate", whTest_MessageCryptoTranslate }, { "whTest_MultiClient", whTest_MultiClient }, { "whTest_HwKeystore", whTest_HwKeystore }, { "whTest_Lock", whTest_Lock }, diff --git a/wolfhsm/wh_message_crypto.h b/wolfhsm/wh_message_crypto.h index f87cab564..442f12c4b 100644 --- a/wolfhsm/wh_message_crypto.h +++ b/wolfhsm/wh_message_crypto.h @@ -883,6 +883,13 @@ int wh_MessageCrypto_TranslateSha2Response( uint16_t magic, const whMessageCrypto_Sha2Response* src, whMessageCrypto_Sha2Response* dest); +/* hash carries the host-order chaining state on a non-final update and a + * finalized digest on the last block, so the caller states which it is: + * stateWordSize is 4 (SHA224/256), 8 (SHA384/512), or 0 for a digest. */ +int wh_MessageCrypto_TranslateSha2Response_ex( + uint16_t magic, const whMessageCrypto_Sha2Response* src, + whMessageCrypto_Sha2Response* dest, uint32_t stateWordSize); + /* * SHA3 (all variants: 224/256/384/512) * @@ -1330,6 +1337,11 @@ int wh_MessageCrypto_TranslateSha2DmaResponse( uint16_t magic, const whMessageCrypto_Sha2DmaResponse* src, whMessageCrypto_Sha2DmaResponse* dest); +/* See wh_MessageCrypto_TranslateSha2Response_ex for stateWordSize */ +int wh_MessageCrypto_TranslateSha2DmaResponse_ex( + uint16_t magic, const whMessageCrypto_Sha2DmaResponse* src, + whMessageCrypto_Sha2DmaResponse* dest, uint32_t stateWordSize); + /* SHA3 DMA Request - state is passed inline (not via DMA) for * cross-architecture safety. Only whole-block input data goes via DMA. * Variant is conveyed in the generic request header's algoType field.