diff --git a/.github/workflows/os-check.yml b/.github/workflows/os-check.yml index a6b9b945940..f5237b355a2 100644 --- a/.github/workflows/os-check.yml +++ b/.github/workflows/os-check.yml @@ -243,6 +243,10 @@ jobs: {"name": "cryptocb-utils-setkey-export-find", "minutes": 2.2, "configure": ["--enable-cryptocb", "--enable-keygen", "--enable-cryptocbutils=setkey,export", "CPPFLAGS=-DWOLF_CRYPTO_CB_FIND"]}, + {"name": "cryptocb-shake", "minutes": 2.2, + "comment": "Exercises the SHAKE128/SHAKE256 crypto callback wiring (wc_CryptoCb_Shake, the sha3.c Update/Final hooks, and the dedicated offload unit tests). A normal (non-ONLY) cryptocb build keeps the host software SHA3/SHAKE present as the callbacks' offload fallback.", + "configure": ["--enable-cryptocb", "--enable-sha3", + "--enable-shake128", "--enable-shake256"]}, {"name": "opensslall-rng-seed-cb-no-getpid", "minutes": 2.1, "configure": ["--enable-opensslall", "--enable-opensslextra", "CPPFLAGS=-DWC_RNG_SEED_CB -DWOLFSSL_NO_GETPID"]}, diff --git a/tests/api/test_sha3.c b/tests/api/test_sha3.c index 90bbd37c906..e0bf144e5ad 100644 --- a/tests/api/test_sha3.c +++ b/tests/api/test_sha3.c @@ -1456,3 +1456,190 @@ int test_wc_Shake256_XOF(void) return EXPECT_RESULT(); } +/*----------------------------------------------------------------------------* + | CryptoCB SHAKE128/SHAKE256 End-to-End Offload Tests + *----------------------------------------------------------------------------*/ + +#if defined(WOLF_CRYPTO_CB) && \ + (defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256)) + +#include + +#define TEST_CRYPTOCB_SHAKE_DEVID 12 + +static int cryptoCbShakeUpdateCalled = 0; +static int cryptoCbShakeFinalCalled = 0; + +/* Mock CryptoCB callback that "offloads" SHAKE. It routes the request back to + * the software implementation, temporarily setting devId to INVALID_DEVID so + * the nested wc_Shake*_Update/Final() call runs in software (a SHAKE lookup by + * INVALID_DEVID finds no device) instead of recursing into the callback. */ +static int test_CryptoCb_Shake_Cb(int devId, wc_CryptoInfo* info, void* ctx) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + wc_Shake* shake; + + (void)ctx; + + if (devId != TEST_CRYPTOCB_SHAKE_DEVID) + return CRYPTOCB_UNAVAILABLE; + if (info->algo_type != WC_ALGO_TYPE_HASH) + return CRYPTOCB_UNAVAILABLE; + if (info->hash.type != WC_HASH_TYPE_SHAKE128 && + info->hash.type != WC_HASH_TYPE_SHAKE256) + return CRYPTOCB_UNAVAILABLE; + + shake = info->hash.sha3; /* wc_Shake is a wc_Sha3 */ + if (shake == NULL) + return BAD_FUNC_ARG; + + /* run software, no recursion */ + shake->devId = INVALID_DEVID; +#ifdef WOLFSSL_SHAKE128 + if (info->hash.type == WC_HASH_TYPE_SHAKE128) { + if (info->hash.in != NULL) { + cryptoCbShakeUpdateCalled++; + ret = wc_Shake128_Update(shake, info->hash.in, info->hash.inSz); + } + if (info->hash.digest != NULL) { + cryptoCbShakeFinalCalled++; + ret = wc_Shake128_Final(shake, info->hash.digest, info->hash.outSz); + } + } +#endif +#ifdef WOLFSSL_SHAKE256 + if (info->hash.type == WC_HASH_TYPE_SHAKE256) { + if (info->hash.in != NULL) { + cryptoCbShakeUpdateCalled++; + ret = wc_Shake256_Update(shake, info->hash.in, info->hash.inSz); + } + if (info->hash.digest != NULL) { + cryptoCbShakeFinalCalled++; + ret = wc_Shake256_Final(shake, info->hash.digest, info->hash.outSz); + } + } +#endif + shake->devId = TEST_CRYPTOCB_SHAKE_DEVID; + + return ret; +} +#endif /* WOLF_CRYPTO_CB && (WOLFSSL_SHAKE128 || WOLFSSL_SHAKE256) */ + +/* + * Test: End-to-End SHAKE128 Offload via CryptoCB + * Verifies that wc_Shake128_Update/Final route through a registered CryptoCB + * device, that the callback is invoked for both the update and final calls, and + * that the offloaded digest matches a software-only reference. + */ +int test_wc_CryptoCb_Shake128_HashOffload(void) +{ + EXPECT_DECLS; +#if defined(WOLF_CRYPTO_CB) && defined(WOLFSSL_SHAKE128) + wc_Shake shake; + wc_Shake ref; + static const byte msg[] = { + 0x6b,0xc1,0xbe,0xe2, 0x2e,0x40,0x9f,0x96, + 0xe9,0x3d,0x7e,0x11, 0x73,0x93,0x17,0x2a, + 0xae,0x2d,0x8a,0x57, 0x1e,0x03,0xac,0x9c + }; + byte refDigest[32]; + byte digest[32]; + int devRegistered = 0; + + XMEMSET(&shake, 0, sizeof(shake)); + XMEMSET(&ref, 0, sizeof(ref)); + XMEMSET(refDigest, 0, sizeof(refDigest)); + XMEMSET(digest, 0, sizeof(digest)); + + cryptoCbShakeUpdateCalled = 0; + cryptoCbShakeFinalCalled = 0; + + /* Software-only reference digest (no devId, no callback). */ + ExpectIntEQ(wc_InitShake128(&ref, HEAP_HINT, INVALID_DEVID), 0); + ExpectIntEQ(wc_Shake128_Update(&ref, msg, (word32)sizeof(msg)), 0); + ExpectIntEQ(wc_Shake128_Final(&ref, refDigest, (word32)sizeof(refDigest)), 0); + wc_Shake128_Free(&ref); + + /* Register the offload callback. */ + ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_CRYPTOCB_SHAKE_DEVID, + test_CryptoCb_Shake_Cb, NULL), 0); + if (EXPECT_SUCCESS()) + devRegistered = 1; + + /* Drive the public streaming API with the offload devId. */ + ExpectIntEQ(wc_InitShake128(&shake, HEAP_HINT, TEST_CRYPTOCB_SHAKE_DEVID), 0); + ExpectIntEQ(wc_Shake128_Update(&shake, msg, (word32)sizeof(msg)), 0); + ExpectIntEQ(cryptoCbShakeUpdateCalled, 1); + ExpectIntEQ(wc_Shake128_Final(&shake, digest, (word32)sizeof(digest)), 0); + ExpectIntEQ(cryptoCbShakeFinalCalled, 1); + + /* Offloaded digest must match the software reference. */ + ExpectBufEQ(digest, refDigest, sizeof(refDigest)); + + wc_Shake128_Free(&shake); + + if (devRegistered) + wc_CryptoCb_UnRegisterDevice(TEST_CRYPTOCB_SHAKE_DEVID); +#endif /* WOLF_CRYPTO_CB && WOLFSSL_SHAKE128 */ + return EXPECT_RESULT(); +} + +/* + * Test: End-to-End SHAKE256 Offload via CryptoCB + * Verifies that wc_Shake256_Update/Final route through a registered CryptoCB + * device, that the callback is invoked for both the update and final calls, and + * that the offloaded digest matches a software-only reference. + */ +int test_wc_CryptoCb_Shake256_HashOffload(void) +{ + EXPECT_DECLS; +#if defined(WOLF_CRYPTO_CB) && defined(WOLFSSL_SHAKE256) + wc_Shake shake; + wc_Shake ref; + static const byte msg[] = { + 0x6b,0xc1,0xbe,0xe2, 0x2e,0x40,0x9f,0x96, + 0xe9,0x3d,0x7e,0x11, 0x73,0x93,0x17,0x2a, + 0xae,0x2d,0x8a,0x57, 0x1e,0x03,0xac,0x9c + }; + byte refDigest[64]; + byte digest[64]; + int devRegistered = 0; + + XMEMSET(&shake, 0, sizeof(shake)); + XMEMSET(&ref, 0, sizeof(ref)); + XMEMSET(refDigest, 0, sizeof(refDigest)); + XMEMSET(digest, 0, sizeof(digest)); + + cryptoCbShakeUpdateCalled = 0; + cryptoCbShakeFinalCalled = 0; + + /* Software-only reference digest (no devId, no callback). */ + ExpectIntEQ(wc_InitShake256(&ref, HEAP_HINT, INVALID_DEVID), 0); + ExpectIntEQ(wc_Shake256_Update(&ref, msg, (word32)sizeof(msg)), 0); + ExpectIntEQ(wc_Shake256_Final(&ref, refDigest, (word32)sizeof(refDigest)), 0); + wc_Shake256_Free(&ref); + + /* Register the offload callback. */ + ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_CRYPTOCB_SHAKE_DEVID, + test_CryptoCb_Shake_Cb, NULL), 0); + if (EXPECT_SUCCESS()) + devRegistered = 1; + + /* Drive the public streaming API with the offload devId. */ + ExpectIntEQ(wc_InitShake256(&shake, HEAP_HINT, TEST_CRYPTOCB_SHAKE_DEVID), 0); + ExpectIntEQ(wc_Shake256_Update(&shake, msg, (word32)sizeof(msg)), 0); + ExpectIntEQ(cryptoCbShakeUpdateCalled, 1); + ExpectIntEQ(wc_Shake256_Final(&shake, digest, (word32)sizeof(digest)), 0); + ExpectIntEQ(cryptoCbShakeFinalCalled, 1); + + /* Offloaded digest must match the software reference. */ + ExpectBufEQ(digest, refDigest, sizeof(refDigest)); + + wc_Shake256_Free(&shake); + + if (devRegistered) + wc_CryptoCb_UnRegisterDevice(TEST_CRYPTOCB_SHAKE_DEVID); +#endif /* WOLF_CRYPTO_CB && WOLFSSL_SHAKE256 */ + return EXPECT_RESULT(); +} + diff --git a/tests/api/test_sha3.h b/tests/api/test_sha3.h index c4edc8f0faf..308a6be7b34 100644 --- a/tests/api/test_sha3.h +++ b/tests/api/test_sha3.h @@ -58,6 +58,9 @@ int test_wc_Shake256_Absorb(void); int test_wc_Shake256_SqueezeBlocks(void); int test_wc_Shake256_XOF(void); +int test_wc_CryptoCb_Shake128_HashOffload(void); +int test_wc_CryptoCb_Shake256_HashOffload(void); + #define TEST_SHA3_DECLS \ TEST_DECL_GROUP("sha3", test_wc_InitSha3), \ TEST_DECL_GROUP("sha3", test_wc_Sha3_Update), \ @@ -81,7 +84,8 @@ int test_wc_Shake256_XOF(void); TEST_DECL_GROUP("shake128", test_wc_Shake128Hash), \ TEST_DECL_GROUP("shake128", test_wc_Shake128_Absorb), \ TEST_DECL_GROUP("shake128", test_wc_Shake128_SqueezeBlocks), \ - TEST_DECL_GROUP("shake128", test_wc_Shake128_XOF) + TEST_DECL_GROUP("shake128", test_wc_Shake128_XOF), \ + TEST_DECL_GROUP("shake128", test_wc_CryptoCb_Shake128_HashOffload) #define TEST_SHAKE256_DECLS \ TEST_DECL_GROUP("shake256", test_wc_InitShake256), \ @@ -93,6 +97,7 @@ int test_wc_Shake256_XOF(void); TEST_DECL_GROUP("shake256", test_wc_Shake256Hash), \ TEST_DECL_GROUP("shake256", test_wc_Shake256_Absorb), \ TEST_DECL_GROUP("shake256", test_wc_Shake256_SqueezeBlocks), \ - TEST_DECL_GROUP("shake256", test_wc_Shake256_XOF) + TEST_DECL_GROUP("shake256", test_wc_Shake256_XOF), \ + TEST_DECL_GROUP("shake256", test_wc_CryptoCb_Shake256_HashOffload) #endif /* WOLFCRYPT_TEST_SHA3_H */ diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index 3218e6efb27..02e44f5f039 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -2219,6 +2219,40 @@ int wc_CryptoCb_Sha3Hash(wc_Sha3* sha3, int type, const byte* in, return wc_CryptoCb_TranslateErrorCode(ret); } + +#if defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256) +int wc_CryptoCb_Shake(wc_Sha3* shake, int type, const byte* in, + word32 inSz, byte* out, word32 outSz) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + CryptoCb* dev; + + /* locate registered callback */ + if (shake) { + dev = wc_CryptoCb_FindDevice(shake->devId, WC_ALGO_TYPE_HASH); + } + else { + /* locate first callback and try using it */ + dev = wc_CryptoCb_FindDeviceByIndex(0); + } + + if (dev && dev->cb) { + wc_CryptoInfo cryptoInfo; + XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo)); + cryptoInfo.algo_type = WC_ALGO_TYPE_HASH; + cryptoInfo.hash.type = type; + cryptoInfo.hash.sha3 = shake; /* wc_Shake is a wc_Sha3 */ + cryptoInfo.hash.in = in; + cryptoInfo.hash.inSz = inSz; + cryptoInfo.hash.digest = out; + cryptoInfo.hash.outSz = outSz; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} +#endif /* WOLFSSL_SHAKE128 || WOLFSSL_SHAKE256 */ #endif /* WOLFSSL_SHA3 && (!HAVE_FIPS || FIPS_VERSION_GE(6, 0)) */ #ifndef NO_HMAC diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 33d06928834..560f3c4d2a0 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -1122,6 +1122,7 @@ static int wc_InitSha3(wc_Sha3* sha3, void* heap, int devId) #endif #if defined(WOLF_CRYPTO_CB) sha3->devId = devId; + sha3->devCtx = NULL; /* Set to none to determine the hash type later */ /* in the update/final functions based on the p value */ sha3->hashType = WC_HASH_TYPE_NONE; @@ -1832,6 +1833,19 @@ int wc_Shake128_Update(wc_Shake* shake, const byte* data, word32 len) return 0; } +#ifdef WOLF_CRYPTO_CB + #ifndef WOLF_CRYPTO_CB_FIND + if (shake->devId != INVALID_DEVID) + #endif + { + int ret = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE128, data, len, + NULL, 0); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ + } +#endif + return Sha3Update(shake, data, len, WC_SHA3_128_COUNT); } @@ -1850,6 +1864,19 @@ int wc_Shake128_Final(wc_Shake* shake, byte* hash, word32 hashLen) return BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB + #ifndef WOLF_CRYPTO_CB_FIND + if (shake->devId != INVALID_DEVID) + #endif + { + ret = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE128, NULL, 0, hash, + hashLen); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ + } +#endif + ret = Sha3Final(shake, 0x1f, hash, WC_SHA3_128_COUNT, hashLen); if (ret != 0) return ret; @@ -2079,6 +2106,19 @@ int wc_Shake256_Update(wc_Shake* shake, const byte* data, word32 len) return 0; } +#ifdef WOLF_CRYPTO_CB + #ifndef WOLF_CRYPTO_CB_FIND + if (shake->devId != INVALID_DEVID) + #endif + { + int ret = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE256, data, len, + NULL, 0); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ + } +#endif + return Sha3Update(shake, data, len, WC_SHA3_256_COUNT); } @@ -2098,6 +2138,19 @@ int wc_Shake256_Final(wc_Shake* shake, byte* hash, word32 hashLen) return BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB + #ifndef WOLF_CRYPTO_CB_FIND + if (shake->devId != INVALID_DEVID) + #endif + { + ret = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE256, NULL, 0, hash, + hashLen); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ + } +#endif + ret = Sha3Final(shake, 0x1f, hash, WC_SHA3_256_COUNT, hashLen); if (ret != 0) return ret; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index e95bba5ae5e..a6bdbf43b3e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -73755,6 +73755,56 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) /* reset devId */ info->hash.sha3->devId = devIdArg; } + #ifdef WOLFSSL_SHAKE128 + else if (info->hash.type == WC_HASH_TYPE_SHAKE128) { + if (info->hash.sha3 == NULL) + return NOT_COMPILED_IN; + + /* set devId to invalid, so software is used */ + info->hash.sha3->devId = INVALID_DEVID; + + if (info->hash.in != NULL) { + ret = wc_Shake128_Update( + info->hash.sha3, + info->hash.in, + info->hash.inSz); + } + if (info->hash.digest != NULL) { + ret = wc_Shake128_Final( + info->hash.sha3, + info->hash.digest, + info->hash.outSz); + } + + /* reset devId */ + info->hash.sha3->devId = devIdArg; + } + #endif /* WOLFSSL_SHAKE128 */ + #ifdef WOLFSSL_SHAKE256 + else if (info->hash.type == WC_HASH_TYPE_SHAKE256) { + if (info->hash.sha3 == NULL) + return NOT_COMPILED_IN; + + /* set devId to invalid, so software is used */ + info->hash.sha3->devId = INVALID_DEVID; + + if (info->hash.in != NULL) { + ret = wc_Shake256_Update( + info->hash.sha3, + info->hash.in, + info->hash.inSz); + } + if (info->hash.digest != NULL) { + ret = wc_Shake256_Final( + info->hash.sha3, + info->hash.digest, + info->hash.outSz); + } + + /* reset devId */ + info->hash.sha3->devId = devIdArg; + } + #endif /* WOLFSSL_SHAKE256 */ else #endif { @@ -74844,6 +74894,14 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) #ifdef WOLFSSL_SHA3 if (ret == 0) ret = sha3_test(); +#ifdef WOLFSSL_SHAKE128 + if (ret == 0) + ret = shake128_test(); +#endif +#ifdef WOLFSSL_SHAKE256 + if (ret == 0) + ret = shake256_test(); +#endif #endif #endif #ifndef NO_HMAC diff --git a/wolfssl/wolfcrypt/cryptocb.h b/wolfssl/wolfcrypt/cryptocb.h index b0aaad2f374..efc8c3a7f63 100644 --- a/wolfssl/wolfcrypt/cryptocb.h +++ b/wolfssl/wolfcrypt/cryptocb.h @@ -455,6 +455,7 @@ typedef struct wc_CryptoInfo { const byte* in; word32 inSz; byte* digest; + word32 outSz; /* SHAKE extendable output length (0 for fixed hashes) */ #ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES union { #endif @@ -881,6 +882,12 @@ WOLFSSL_LOCAL int wc_CryptoCb_Sha512Hash(wc_Sha512* sha512, const byte* in, #ifdef WOLFSSL_SHA3 WOLFSSL_LOCAL int wc_CryptoCb_Sha3Hash(wc_Sha3* sha3, int type, const byte* in, word32 inSz, byte* digest); +#if defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256) +/* SHAKE is an extendable output function: out/outSz carry the requested output + * on the final call (in/inSz carry message data on update calls). */ +WOLFSSL_LOCAL int wc_CryptoCb_Shake(wc_Sha3* shake, int type, const byte* in, + word32 inSz, byte* out, word32 outSz); +#endif #endif #ifndef NO_HMAC