diff --git a/.github/configs/os-check-linux.json b/.github/configs/os-check-linux.json index 5fdf80daa5c..ec452d91f97 100644 --- a/.github/configs/os-check-linux.json +++ b/.github/configs/os-check-linux.json @@ -176,6 +176,11 @@ "configure": ["--enable-cryptocb", "--enable-sha3", "--enable-shake128", "--enable-shake256", "--enable-cryptocbutils=copy,free"]}, +{"name": "cryptocb-shake-xof", "minutes": 2.2, + "comment": "Only WOLF_CRYPTO_CB_SHAKE_XOF dispatches SHAKE absorb and squeeze to the callback, so without this entry the offload counter tests never compile in CI.", + "configure": ["--enable-cryptocb", "--enable-sha3", + "--enable-shake128", "--enable-shake256", + "CPPFLAGS=-DWOLF_CRYPTO_CB_SHAKE_XOF"]}, {"name": "cryptocb-aes-cfb-ofb", "minutes": 2.2, "comment": "Exercises the AES-CFB/OFB crypto callback wiring (wc_CryptoCb_AesCfb/Ofb Encrypt/Decrypt, the aes.c hooks, and the dedicated offload unit tests). A normal (non-ONLY) cryptocb build keeps the host software AES present as the callbacks' offload fallback; WOLF_CRYPTO_CB_ONLY_AES (no software fallback) is covered separately by cryptocb-only.yml via swdev.", "configure": ["--enable-cryptocb", "--enable-aescfb", diff --git a/.github/workflows/cryptocb-only.yml b/.github/workflows/cryptocb-only.yml index 15b4d724db0..c31f875d76f 100644 --- a/.github/workflows/cryptocb-only.yml +++ b/.github/workflows/cryptocb-only.yml @@ -151,6 +151,9 @@ jobs: {"name": "falcon-onlycb-no-swdev", "minutes": 1.0, "comment": "WOLF_CRYPTO_CB_ONLY_FALCON without swdev, which has no Falcon handlers: builds the Falcon key API that a callback-only build keeps, including its TLS and ASN callers, and runs the tests that need no device.", "configure": ["--disable-swdev", "--enable-falcon", "--enable-experimental", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_FALCON"]}, + {"name": "shake-xof", "minutes": 4.0, + "comment": "WOLF_CRYPTO_CB_SHAKE_XOF: swdev handles SHAKE absorb and squeeze. No ONLY_* strip exists for SHAKE, so software SHAKE stays in. ML-KEM and ML-DSA reach swdev_shake through WOLF_CRYPTO_CB_FIND, and cryptocb_test runs shake_cb_xof_test.", + "configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_SHAKE_XOF"]}, {"name": "all", "minutes": 19, "comment": "All nine ONLY_* macros at once: every supported software primitive is stripped and dispatched through cryptocb. Catches any cross-algorithm call that a single-strip entry would still resolve via the remaining software paths.", "configure": ["--enable-slhdsa=yes,sha2", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_ECC -DWOLF_CRYPTO_CB_ONLY_RSA -DWOLF_CRYPTO_CB_ONLY_SHA256 -DWOLF_CRYPTO_CB_ONLY_SHA512 -DWOLF_CRYPTO_CB_ONLY_AES -DWOLF_CRYPTO_CB_ONLY_ED25519 -DWOLF_CRYPTO_CB_ONLY_CURVE25519 -DWOLF_CRYPTO_CB_ONLY_CURVE448 -DWOLF_CRYPTO_CB_ONLY_SLHDSA"]}, diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 9be201c7fb5..6d71539ed75 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -1224,6 +1224,7 @@ WOLFSSL_ZEPHYR_MAIN_ARGS WOLF_ALLOW_BUILTIN WOLF_CONF_ASN_TIME WOLF_CRYPTO_CB_ASYNC_POLL +WOLF_CRYPTO_CB_SHAKE_XOF WOLF_CRYPTO_DEV WOLF_NO_TRAILING_ENUM_COMMAS WindowsCE diff --git a/tests/swdev/swdev.c b/tests/swdev/swdev.c index 5ac589412ac..3872140c736 100644 --- a/tests/swdev/swdev.c +++ b/tests/swdev/swdev.c @@ -650,6 +650,117 @@ static int swdev_pqc_sig(wc_CryptoInfo* info, int type, int pkType) } #endif /* WOLFSSL_HAVE_SLHDSA */ + +#if defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256) +/* Copy sponge state between the caller's wc_Shake and swdev's shadow */ +static void swdev_shake_copy_state(wc_Shake* dst, const wc_Shake* src) +{ + XMEMCPY(dst->s, src->s, sizeof(dst->s)); + XMEMCPY(dst->t, src->t, sizeof(dst->t)); + dst->i = src->i; +#ifdef WOLFSSL_HASH_FLAGS + dst->flags = src->flags; +#endif +} + +static int swdev_shake_op(const wc_CryptoInfo* info) +{ +#ifdef WOLF_CRYPTO_CB_SHAKE_XOF + return info->hash.shakeOp; +#else + (void)info; + return WC_SHAKE_OP_NONE; +#endif +} + +typedef struct swdev_shake_funcs { + int type; + word32 rate; + int (*initFn)(wc_Shake*, void*, int); + int (*updateFn)(wc_Shake*, const byte*, word32); + int (*finalFn)(wc_Shake*, byte*, word32); + int (*absorbFn)(wc_Shake*, const byte*, word32); + int (*squeezeFn)(wc_Shake*, byte*, word32); + void (*freeFn)(wc_Shake*); +} swdev_shake_funcs; + +static const swdev_shake_funcs swdev_shake_table[] = { +#ifdef WOLFSSL_SHAKE128 + { WC_HASH_TYPE_SHAKE128, WC_SHA3_128_COUNT * 8U, wc_InitShake128, + wc_Shake128_Update, wc_Shake128_Final, wc_Shake128_Absorb, + wc_Shake128_SqueezeBlocks, wc_Shake128_Free }, +#endif +#ifdef WOLFSSL_SHAKE256 + { WC_HASH_TYPE_SHAKE256, WC_SHA3_256_COUNT * 8U, wc_InitShake256, + wc_Shake256_Update, wc_Shake256_Final, wc_Shake256_Absorb, + wc_Shake256_SqueezeBlocks, wc_Shake256_Free }, +#endif +}; + +/* SHAKE handler. When shakeOp is WC_SHAKE_OP_NONE, update and final operations + * are determined by hash.digest. Otherwise hash.shakeOp selects the op. */ +static int swdev_shake(wc_CryptoInfo* info) +{ + wc_Shake* shake = info->hash.sha3; + wc_Shake shadow; + const swdev_shake_funcs* f = NULL; + size_t idx; + int ret; + + if (shake == NULL) + return BAD_FUNC_ARG; + + for (idx = 0; idx < sizeof(swdev_shake_table) / + sizeof(swdev_shake_table[0]); idx++) { + if (swdev_shake_table[idx].type == info->hash.type) { + f = &swdev_shake_table[idx]; + break; + } + } + if (f == NULL) + return CRYPTOCB_UNAVAILABLE; + + ret = f->initFn(&shadow, NULL, INVALID_DEVID); + if (ret != 0) + return ret; + + swdev_shake_copy_state(&shadow, shake); + + switch (swdev_shake_op(info)) { + case WC_SHAKE_OP_ABSORB: + ret = f->absorbFn(&shadow, info->hash.in, info->hash.inSz); + break; + + case WC_SHAKE_OP_SQUEEZE: + /* outSz is the byte count; the API takes whole blocks. */ + if ((info->hash.outSz % f->rate) != 0) { + ret = BAD_FUNC_ARG; + break; + } + ret = f->squeezeFn(&shadow, info->hash.digest, + info->hash.outSz / f->rate); + break; + + default: + if (info->hash.in != NULL) { + ret = f->updateFn(&shadow, info->hash.in, info->hash.inSz); + } + if ((ret == 0) && (info->hash.digest != NULL)) { + ret = f->finalFn(&shadow, info->hash.digest, info->hash.outSz); + } + break; + } + + if (ret == 0) { + swdev_shake_copy_state(shake, &shadow); + } + + f->freeFn(&shadow); + + return ret; +} +#endif /* WOLFSSL_SHAKE128 || WOLFSSL_SHAKE256 */ + #ifndef NO_SHA256 /* Copy hash state between caller's wc_Sha256 and swdev's shadow, leaving * admin fields (heap, devId, devCtx, W, async, HW ctx) per-side. */ @@ -1399,7 +1510,9 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info, return CRYPTOCB_UNAVAILABLE; } #endif -#if !defined(NO_SHA256) || defined(WOLFSSL_SHA512) || defined(WOLFSSL_SHA384) +#if !defined(NO_SHA256) || defined(WOLFSSL_SHA512) || \ + defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHAKE128) || \ + defined(WOLFSSL_SHAKE256) case WC_ALGO_TYPE_HASH: switch (info->hash.type) { #ifndef NO_SHA256 @@ -1428,6 +1541,15 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info, !defined(WOLFSSL_SWDEV_SHA512_GENERAL_ONLY) case WC_HASH_TYPE_SHA384: return swdev_sha384(info); + #endif + #if defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256) + #ifdef WOLFSSL_SHAKE128 + case WC_HASH_TYPE_SHAKE128: + #endif + #ifdef WOLFSSL_SHAKE256 + case WC_HASH_TYPE_SHAKE256: + #endif + return swdev_shake(info); #endif default: return CRYPTOCB_UNAVAILABLE; diff --git a/tests/unit-mcdc/test_cryptocb_whitebox.c b/tests/unit-mcdc/test_cryptocb_whitebox.c index f8e96289ecf..3399b464c4e 100644 --- a/tests/unit-mcdc/test_cryptocb_whitebox.c +++ b/tests/unit-mcdc/test_cryptocb_whitebox.c @@ -816,7 +816,7 @@ int main(void) #else WC_HASH_TYPE_SHAKE256, #endif - in, sizeof(in), out, outLen)); + in, sizeof(in), out, outLen, WC_SHAKE_OP_NONE)); WB_NOTE("SHAKE: Shake dev&&dev->cb driven"); #endif } diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index d544b6cf4b0..35cfbbfb056 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -66,6 +66,10 @@ Crypto Callback Build Options: * WOLF_CRYPTO_CB_ONLY_AES: Use only callbacks for AES default: off * WOLF_CRYPTO_CB_ONLY_ED25519: Use only callbacks for Ed25519 default: off * WOLF_CRYPTO_CB_ONLY_CURVE25519: Use only callbacks for X25519 default: off + * WOLF_CRYPTO_CB_SHAKE_XOF: Dispatch SHAKE absorb and squeeze default: off + * as well as update and final. Off by + * default because a callback that predates + * hash.shakeOp would misread them. */ #include @@ -3699,11 +3703,15 @@ int wc_CryptoCb_Sha3Hash(wc_Sha3* sha3, int type, const byte* in, #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) + word32 inSz, byte* out, word32 outSz, int shakeOp) { int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); CryptoCb* dev; +#ifndef WOLF_CRYPTO_CB_SHAKE_XOF + (void)shakeOp; +#endif + /* locate registered callback */ if (shake) { dev = wc_CryptoCb_FindDevice(shake->devId, WC_ALGO_TYPE_HASH); @@ -3723,6 +3731,9 @@ int wc_CryptoCb_Shake(wc_Sha3* shake, int type, const byte* in, cryptoInfo.hash.inSz = inSz; cryptoInfo.hash.digest = out; cryptoInfo.hash.outSz = outSz; +#ifdef WOLF_CRYPTO_CB_SHAKE_XOF + cryptoInfo.hash.shakeOp = shakeOp; +#endif ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); } diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hash.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hash.c index 94aad4e9f86..edb6946562e 100644 --- a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hash.c +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_hash.c @@ -372,6 +372,13 @@ static int wc_AsuHashCompute(wc_CryptoInfo* info) if (info == NULL) { return BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB_SHAKE_XOF + /* No sponge state is kept here, so a software absorb would miss saved + * updates. Leave all of SHAKE256 to software. */ + if (info->hash.type == WC_HASH_TYPE_SHAKE256) { + return CRYPTOCB_UNAVAILABLE; + } +#endif ret = wc_AsuHashResolve(info, &devCtxPtr, &shaType, &shaMode, &hashLen); if (ret != 0) { diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index e7db61ad48c..4c699c2f4cb 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -2235,7 +2235,7 @@ int wc_Shake128_Update(wc_Shake* shake, const byte* data, word32 len) #endif { int ret = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE128, data, len, - NULL, 0); + NULL, 0, WC_SHAKE_OP_NONE); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; /* fall-through when unavailable */ @@ -2266,7 +2266,7 @@ int wc_Shake128_Final(wc_Shake* shake, byte* hash, word32 hashLen) #endif { ret = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE128, NULL, 0, hash, - hashLen); + hashLen, WC_SHAKE_OP_NONE); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; /* fall-through when unavailable */ @@ -2303,6 +2303,19 @@ int wc_Shake128_Absorb(wc_Shake* shake, const byte* data, word32 len) return BAD_FUNC_ARG; } +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SHAKE_XOF) + #ifndef WOLF_CRYPTO_CB_FIND + if (shake->devId != INVALID_DEVID) + #endif + { + int cbRet = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE128, + data, len, NULL, 0, WC_SHAKE_OP_ABSORB); + if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return cbRet; + /* fall-through when unavailable */ + } +#endif + ret = Sha3Update(shake, data, len, WC_SHA3_128_COUNT); if (ret == 0) { byte hash[1]; @@ -2338,6 +2351,22 @@ int wc_Shake128_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) return BAD_FUNC_ARG; } +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SHAKE_XOF) + /* Use software when the byte count does not fit in outSz. */ + if (blockCnt <= WOLFSSL_MAX_32BIT / (WC_SHA3_128_COUNT * 8U) + #ifndef WOLF_CRYPTO_CB_FIND + && shake->devId != INVALID_DEVID + #endif + ) { + int cbRet = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE128, + NULL, 0, out, blockCnt * (WC_SHA3_128_COUNT * 8U), + WC_SHAKE_OP_SQUEEZE); + if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return cbRet; + /* fall-through when unavailable */ + } +#endif + #if defined(USE_INTEL_SPEEDUP) || defined(SHA3_NEEDS_VREG_CLAIM) #ifdef WC_C_DYNAMIC_FALLBACK sha3_block = SHA3_BLOCK; @@ -2558,7 +2587,7 @@ int wc_Shake256_Update(wc_Shake* shake, const byte* data, word32 len) #endif { int ret = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE256, data, len, - NULL, 0); + NULL, 0, WC_SHAKE_OP_NONE); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; /* fall-through when unavailable */ @@ -2590,7 +2619,7 @@ int wc_Shake256_Final(wc_Shake* shake, byte* hash, word32 hashLen) #endif { ret = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE256, NULL, 0, hash, - hashLen); + hashLen, WC_SHAKE_OP_NONE); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) return ret; /* fall-through when unavailable */ @@ -2627,6 +2656,19 @@ int wc_Shake256_Absorb(wc_Shake* shake, const byte* data, word32 len) return BAD_FUNC_ARG; } +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SHAKE_XOF) + #ifndef WOLF_CRYPTO_CB_FIND + if (shake->devId != INVALID_DEVID) + #endif + { + int cbRet = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE256, + data, len, NULL, 0, WC_SHAKE_OP_ABSORB); + if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return cbRet; + /* fall-through when unavailable */ + } +#endif + ret = Sha3Update(shake, data, len, WC_SHA3_256_COUNT); if (ret == 0) { byte hash[1]; @@ -2655,6 +2697,22 @@ int wc_Shake256_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) return BAD_FUNC_ARG; } +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SHAKE_XOF) + /* Use software when the byte count does not fit in outSz. */ + if (blockCnt <= WOLFSSL_MAX_32BIT / (WC_SHA3_256_COUNT * 8U) + #ifndef WOLF_CRYPTO_CB_FIND + && shake->devId != INVALID_DEVID + #endif + ) { + int cbRet = wc_CryptoCb_Shake(shake, WC_HASH_TYPE_SHAKE256, + NULL, 0, out, blockCnt * (WC_SHA3_256_COUNT * 8U), + WC_SHAKE_OP_SQUEEZE); + if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return cbRet; + /* fall-through when unavailable */ + } +#endif + #if defined(USE_INTEL_SPEEDUP) || defined(SHA3_NEEDS_VREG_CLAIM) #ifdef WC_C_DYNAMIC_FALLBACK sha3_block = SHA3_BLOCK; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 643dd6374fb..4e89beb2437 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -87594,6 +87594,11 @@ typedef struct { #if defined(WOLFSSL_CMAC) && defined(WOLF_CRYPTO_CB_FREE) int cmacFreeCount; /* CMAC free callback invocations */ #endif +#ifdef WOLF_CRYPTO_CB_SHAKE_XOF + int shakeAbsorbCount; /* SHAKE absorb callback invocations */ + int shakeSqueezeCount; /* SHAKE squeeze callback invocations */ + int shakeXofDecline; /* when set, decline SHAKE absorb and squeeze */ +#endif #ifdef WOLF_CRYPTO_CB_COPY int hashCopyType; /* hash type seen by last hash copy dispatch */ #endif @@ -90746,17 +90751,40 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) /* set devId to invalid, so software is used */ info->hash.sha3->devId = INVALID_DEVID; - if (info->hash.in != NULL) { - ret = wc_Shake128_Update( +#ifdef WOLF_CRYPTO_CB_SHAKE_XOF + if (myCtx->shakeXofDecline && + info->hash.shakeOp != WC_SHAKE_OP_NONE) { + ret = CRYPTOCB_UNAVAILABLE; + } + else if (info->hash.shakeOp == WC_SHAKE_OP_ABSORB) { + ret = wc_Shake128_Absorb( info->hash.sha3, info->hash.in, info->hash.inSz); + myCtx->shakeAbsorbCount++; } - if (info->hash.digest != NULL) { - ret = wc_Shake128_Final( + else if (info->hash.shakeOp == WC_SHAKE_OP_SQUEEZE) { + ret = wc_Shake128_SqueezeBlocks( info->hash.sha3, info->hash.digest, - info->hash.outSz); + info->hash.outSz / WC_SHA3_128_BLOCK_SIZE); + myCtx->shakeSqueezeCount++; + } + else +#endif + { + 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 */ @@ -90771,17 +90799,40 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) /* set devId to invalid, so software is used */ info->hash.sha3->devId = INVALID_DEVID; - if (info->hash.in != NULL) { - ret = wc_Shake256_Update( +#ifdef WOLF_CRYPTO_CB_SHAKE_XOF + if (myCtx->shakeXofDecline && + info->hash.shakeOp != WC_SHAKE_OP_NONE) { + ret = CRYPTOCB_UNAVAILABLE; + } + else if (info->hash.shakeOp == WC_SHAKE_OP_ABSORB) { + ret = wc_Shake256_Absorb( info->hash.sha3, info->hash.in, info->hash.inSz); + myCtx->shakeAbsorbCount++; } - if (info->hash.digest != NULL) { - ret = wc_Shake256_Final( + else if (info->hash.shakeOp == WC_SHAKE_OP_SQUEEZE) { + ret = wc_Shake256_SqueezeBlocks( info->hash.sha3, info->hash.digest, - info->hash.outSz); + info->hash.outSz / WC_SHA3_256_BLOCK_SIZE); + myCtx->shakeSqueezeCount++; + } + else +#endif + { + 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 */ @@ -91848,6 +91899,72 @@ static int myCryptoCbFind(int currentId, int algoType) } #endif /* WOLF_CRYPTO_CB_FIND */ +#if defined(WOLFSSL_SHA3) && defined(WOLF_CRYPTO_CB_SHAKE_XOF) && \ + (defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256)) && \ + !defined(HAVE_FIPS) +#define SHAKE_CB_XOF_BLOCKS 2 +static wc_test_ret_t shake_cb_xof_test(myCryptoDevCtx* myCtx, int decline, + int (*initFn)(wc_Shake*, void*, int), + int (*updateFn)(wc_Shake*, const byte*, word32), + int (*absorbFn)(wc_Shake*, const byte*, word32), + int (*squeezeFn)(wc_Shake*, byte*, word32), + void (*freeFn)(wc_Shake*)) +{ + wc_test_ret_t ret = 0; + int i; + const int expectCount = decline ? 0 : 1; + byte shakeIn[32]; + byte cbOut[SHAKE_CB_XOF_BLOCKS * WC_SHA3_128_BLOCK_SIZE]; + byte swOut[SHAKE_CB_XOF_BLOCKS * WC_SHA3_128_BLOCK_SIZE]; + WC_DECLARE_VAR(shake, wc_Shake, 1, HEAP_HINT); + + WC_ALLOC_VAR_EX(shake, wc_Shake, 1, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER, ret = WC_TEST_RET_ENC_EC(MEMORY_E)); + + XMEMSET(shakeIn, 0x5a, sizeof(shakeIn)); + XMEMSET(cbOut, 0, sizeof(cbOut)); + XMEMSET(swOut, 0, sizeof(swOut)); + myCtx->shakeAbsorbCount = 0; + myCtx->shakeSqueezeCount = 0; + myCtx->shakeXofDecline = decline; + + /* First pass uses the callback, second pass uses software. */ + for (i = 0; i < 2 && ret == 0; i++) { + byte* out = (i == 0) ? cbOut : swOut; + + ret = initFn(shake, HEAP_HINT, (i == 0) ? devId : INVALID_DEVID); + if (ret != 0) { + ret = WC_TEST_RET_ENC_EC(ret); + break; + } + /* Update first so a declined absorb continues from device state. */ + ret = updateFn(shake, shakeIn, (word32)sizeof(shakeIn) / 2); + if (ret == 0) { + ret = absorbFn(shake, shakeIn + sizeof(shakeIn) / 2, + (word32)sizeof(shakeIn) / 2); + } + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + if (ret == 0) { + ret = squeezeFn(shake, out, SHAKE_CB_XOF_BLOCKS); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + freeFn(shake); + } + myCtx->shakeXofDecline = 0; + + if (ret == 0 && (myCtx->shakeAbsorbCount != expectCount || + myCtx->shakeSqueezeCount != expectCount)) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0 && XMEMCMP(cbOut, swOut, sizeof(cbOut)) != 0) + ret = WC_TEST_RET_ENC_NC; + + WC_FREE_VAR_EX(shake, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + return ret; +} +#endif /* WOLFSSL_SHA3 && WOLF_CRYPTO_CB_SHAKE_XOF && !HAVE_FIPS */ + #if defined(WOLFSSL_SHA3) && \ (defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256)) && \ (defined(WOLF_CRYPTO_CB_COPY) || defined(WOLF_CRYPTO_CB_FREE)) @@ -92375,6 +92492,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) #if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) myCtx.rsaPssVerifyCount = 0; #endif +#ifdef WOLF_CRYPTO_CB_SHAKE_XOF + myCtx.shakeAbsorbCount = 0; + myCtx.shakeSqueezeCount = 0; + myCtx.shakeXofDecline = 0; +#endif #if defined(HAVE_HKDF) && !defined(NO_HMAC) /* myCtx is uninitialized stack: a garbage arm would inject * WC_PENDING_E into callers that are not polling. */ @@ -93268,6 +93390,28 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) #endif #endif /* WOLFSSL_SHA3 && (CB_COPY || CB_FREE) */ +#if defined(WOLFSSL_SHA3) && defined(WOLF_CRYPTO_CB_SHAKE_XOF) && \ + !defined(HAVE_FIPS) + { + int decline; + + /* Second round covers the software fallback. */ + for (decline = 0; decline <= 1 && ret == 0; decline++) { +#ifdef WOLFSSL_SHAKE128 + ret = shake_cb_xof_test(&myCtx, decline, wc_InitShake128, + wc_Shake128_Update, wc_Shake128_Absorb, + wc_Shake128_SqueezeBlocks, wc_Shake128_Free); +#endif +#ifdef WOLFSSL_SHAKE256 + if (ret == 0) + ret = shake_cb_xof_test(&myCtx, decline, wc_InitShake256, + wc_Shake256_Update, wc_Shake256_Absorb, + wc_Shake256_SqueezeBlocks, wc_Shake256_Free); +#endif + } + } +#endif /* WOLFSSL_SHA3 && WOLF_CRYPTO_CB_SHAKE_XOF && !HAVE_FIPS */ + #if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) && \ !defined(NO_RSA) && !defined(WC_NO_RNG) && defined(WOLFSSL_KEY_GEN) && \ !defined(NO_SHA256) && !defined(HAVE_FIPS) diff --git a/wolfssl/wolfcrypt/cryptocb.h b/wolfssl/wolfcrypt/cryptocb.h index de39caeb2e6..5db410a777c 100644 --- a/wolfssl/wolfcrypt/cryptocb.h +++ b/wolfssl/wolfcrypt/cryptocb.h @@ -30,7 +30,7 @@ /* Defines the Crypto Callback interface version, for compatibility */ /* Increment this when Crypto Callback interface changes are made */ -#define CRYPTO_CB_VER 3 +#define CRYPTO_CB_VER 4 #ifdef WOLF_CRYPTO_CB @@ -253,6 +253,17 @@ enum wc_KeyWrapFormat { #define WC_KEYSTORE_ATTR_PERSISTENT 0x0004 /* survives reset, if supported */ #endif /* WOLF_CRYPTO_CB_KEYSTORE */ +/* SHAKE ops in hash.shakeOp. WC_SHAKE_OP_NONE is an update (hash.in set) + * and/or final (hash.digest set). A device must keep the wc_Shake state + * current, as software continues from it when the device returns + * CRYPTOCB_UNAVAILABLE or a request is too large to dispatch. Without + * software a decline is an error, so split large requests in the device. */ +enum wc_ShakeOp { + WC_SHAKE_OP_NONE = 0, + WC_SHAKE_OP_ABSORB = 1, + WC_SHAKE_OP_SQUEEZE = 2 +}; + /* Crypto Information Structure for callbacks */ typedef struct wc_CryptoInfo { int algo_type; /* enum wc_AlgoType */ @@ -804,6 +815,9 @@ typedef struct wc_CryptoInfo { word32 inSz; byte* digest; word32 outSz; /* SHAKE extendable output length (0 for fixed hashes) */ +#ifdef WOLF_CRYPTO_CB_SHAKE_XOF + int shakeOp; /* enum wc_ShakeOp; 0 for update and final */ +#endif #ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES union { #endif @@ -1482,7 +1496,7 @@ WOLFSSL_LOCAL int wc_CryptoCb_Sha3Hash(wc_Sha3* sha3, int type, const byte* in, /* 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); + word32 inSz, byte* out, word32 outSz, int shakeOp); #endif #endif