From 3ec8d3cff1a87e956c4bf31865a311657880be5a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Jul 2026 16:27:29 +0200 Subject: [PATCH] wolfssl: Use Absorb/SqueezeBlocks API for SHAKE-128/256 XOFs This API is available since 5.5.1 (released in 2022) and allows us to avoid the inefficient previous implementation (don't think it's worth keeping that around for older versions). Also added support for SHAKE-128. Note that the only user of SHAKE is the ml plugin nowadays and since wolfSSL also provides ML-KEM, this might not actually get used much. --- .github/active-transforms/wolfssl | 1 + .../plugins/wolfssl/wolfssl_plugin.c | 9 +- .../plugins/wolfssl/wolfssl_xof.c | 160 +++++++++++++----- 3 files changed, 130 insertions(+), 40 deletions(-) diff --git a/.github/active-transforms/wolfssl b/.github/active-transforms/wolfssl index b3e46c061..704ccbb4a 100644 --- a/.github/active-transforms/wolfssl +++ b/.github/active-transforms/wolfssl @@ -64,6 +64,7 @@ PRF_HMAC_SHA1[wolfssl] PRF_HMAC_SHA2_256[wolfssl] PRF_HMAC_SHA2_384[wolfssl] PRF_HMAC_SHA2_512[wolfssl] +XOF_SHAKE128[wolfssl] XOF_SHAKE256[wolfssl] KDF_PRF[wolfssl] KDF_PRF_PLUS[wolfssl] diff --git a/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c b/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c index 73b73780c..463cbf04d 100644 --- a/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c +++ b/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c @@ -145,9 +145,16 @@ METHOD(plugin_t, get_features, int, #if defined(WOLFSSL_SHA3) && !defined(WOLFSSL_NOSHA3_512) PLUGIN_PROVIDE(HASHER, HASH_SHA3_512), #endif -#if defined(WOLFSSL_SHAKE256) && LIBWOLFSSL_VERSION_HEX >= 0x04007001 +#if LIBWOLFSSL_VERSION_HEX >= 0x05005001 +#if defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256) PLUGIN_REGISTER(XOF, wolfssl_xof_create), + #ifdef WOLFSSL_SHAKE128 + PLUGIN_PROVIDE(XOF, XOF_SHAKE_128), + #endif + #ifdef WOLFSSL_SHAKE256 PLUGIN_PROVIDE(XOF, XOF_SHAKE_256), + #endif +#endif #endif #ifndef NO_SHA /* keyed sha1 hasher (aka prf) */ diff --git a/src/libstrongswan/plugins/wolfssl/wolfssl_xof.c b/src/libstrongswan/plugins/wolfssl/wolfssl_xof.c index 5bf2614e6..7cf70ece2 100644 --- a/src/libstrongswan/plugins/wolfssl/wolfssl_xof.c +++ b/src/libstrongswan/plugins/wolfssl/wolfssl_xof.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2026 Tobias Brunner * Copyright (C) 2021 Andreas Steffen, strongSec GmbH * * Copyright (C) secunet Security Networks AG @@ -14,19 +15,53 @@ * for more details. */ -#include +#include "wolfssl_common.h" -#ifdef WOLFSSL_SHAKE256 +#include + +/* the Absorb and SqueezeBlocks functions were added with 5.5.1 */ +#if (defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256)) && \ + LIBWOLFSSL_VERSION_HEX >= 0x05005001 #include #include "wolfssl_xof.h" #define KECCAK_STATE_SIZE 200 /* 1600 bits */ -#define SHAKE256_CAPACITY 64 /* 512 bits */ typedef struct private_xof_t private_xof_t; +/** + * Helper struct to avoid switch statements all over the place + */ +typedef struct { + int (*init)(wc_Shake*, void*, int); + int (*absorb)(wc_Shake*, const byte*, word32); + int (*squeeze)(wc_Shake*, byte*, word32); + void (*free)(wc_Shake*); + size_t block_size; +} shake_impl_t; + +#ifdef WOLFSSL_SHAKE128 +static const shake_impl_t shake128 = { + .init = wc_InitShake128, + .absorb = wc_Shake128_Absorb, + .squeeze = wc_Shake128_SqueezeBlocks, + .free = wc_Shake128_Free, + .block_size = WC_SHA3_128_BLOCK_SIZE, +}; +#endif + +#ifdef WOLFSSL_SHAKE256 +static const shake_impl_t shake256 = { + .init = wc_InitShake256, + .absorb = wc_Shake256_Absorb, + .squeeze = wc_Shake256_SqueezeBlocks, + .free = wc_Shake256_Free, + .block_size = WC_SHA3_256_BLOCK_SIZE, +}; +#endif + /** * Private data */ @@ -37,84 +72,120 @@ struct private_xof_t { */ xof_t public; + /** + * XOF algorithm + */ + ext_out_function_t algorithm; + + /** + * Implementation based on the type + */ + const shake_impl_t *impl; + /** * Internal context */ wc_Shake shake; /** - * Current seed + * Bytes available in the cached block */ - chunk_t seed; + size_t available; /** - * Offset into generated data + * Cached squeezed block partially handed out already */ - size_t offset; + uint8_t cache[KECCAK_STATE_SIZE]; }; METHOD(xof_t, get_type, ext_out_function_t, private_xof_t *this) { - return XOF_SHAKE_256; + return this->algorithm; } METHOD(xof_t, get_bytes, bool, private_xof_t *this, size_t out_len, uint8_t *buffer) { - bool success = FALSE; - chunk_t data; + size_t index = 0, len, blocks; - /* we can call wc_Shake256_Final() only once, so to support an arbitrary - * number of calls to get_bytes(), we request all the data we already - * requested previously and just ignore what we already handed out */ - if (wc_Shake256_Update(&this->shake, this->seed.ptr, this->seed.len) == 0) + /* copy data from the cached block first if any */ + len = min(out_len, this->available); + if (len) { - data = chunk_alloc(out_len + this->offset); - if (wc_Shake256_Final(&this->shake, data.ptr, data.len) == 0) - { - memcpy(buffer, data.ptr + this->offset, out_len); - this->offset += out_len; - success = TRUE; - } - chunk_clear(&data); + memcpy(buffer, this->cache + this->impl->block_size - this->available, + len); + index += len; + this->available -= len; } - return success; + + /* squeeze complete blocks directly to the output buffer first */ + blocks = (out_len - index) / this->impl->block_size; + if (blocks) + { + if (this->impl->squeeze(&this->shake, buffer + index, blocks) != 0) + { + return FALSE; + } + index += blocks * this->impl->block_size; + } + + /* squeeze another block if we need some more output bytes */ + len = out_len - index; + if (len) + { + if (this->impl->squeeze(&this->shake, this->cache, 1) != 0) + { + return FALSE; + } + memcpy(buffer + index, this->cache, len); + this->available = this->impl->block_size - len; + } + return TRUE; } METHOD(xof_t, allocate_bytes, bool, private_xof_t *this, size_t out_len, chunk_t *chunk) { *chunk = chunk_alloc(out_len); - return get_bytes(this, out_len, chunk->ptr); + if (!get_bytes(this, out_len, chunk->ptr)) + { + chunk_free(chunk); + return FALSE; + } + return TRUE; } METHOD(xof_t, get_block_size, size_t, private_xof_t *this) { - return KECCAK_STATE_SIZE - SHAKE256_CAPACITY; + return this->impl->block_size; } METHOD(xof_t, get_seed_size, size_t, private_xof_t *this) { - return SHAKE256_CAPACITY; + return KECCAK_STATE_SIZE - this->impl->block_size; } METHOD(xof_t, set_seed, bool, private_xof_t *this, chunk_t seed) { - chunk_clear(&this->seed); - this->seed = chunk_clone(seed); - this->offset = 0; + this->available = 0; + + if (this->impl->init(&this->shake, NULL, INVALID_DEVID) != 0 || + this->impl->absorb(&this->shake, seed.ptr, seed.len) != 0) + { + return FALSE; + } return TRUE; } METHOD(xof_t, destroy, void, private_xof_t *this) { - wc_Shake256_Free(&this->shake); - chunk_clear(&this->seed); + this->impl->free(&this->shake); + memwipe(this->cache, sizeof(this->cache)); free(this); } @@ -125,11 +196,6 @@ xof_t *wolfssl_xof_create(ext_out_function_t algorithm) { private_xof_t *this; - if (algorithm != XOF_SHAKE_256) - { - return NULL; - } - INIT(this, .public = { .get_type = _get_type, @@ -140,15 +206,31 @@ xof_t *wolfssl_xof_create(ext_out_function_t algorithm) .set_seed = _set_seed, .destroy = _destroy, }, + .algorithm = algorithm, ); - if (wc_InitShake256(&this->shake, NULL, 0) != 0) + switch (algorithm) + { +#ifdef WOLFSSL_SHAKE128 + case XOF_SHAKE_128: + this->impl = &shake128; + break; +#endif +#ifdef WOLFSSL_SHAKE256 + case XOF_SHAKE_256: + this->impl = &shake256; + break; +#endif + default: + free(this); + return NULL; + } + if (this->impl->init(&this->shake, NULL, INVALID_DEVID) != 0) { free(this); return NULL; } - return &this->public; } -#endif /* WOLFSSL_SHAKE256 */ +#endif /* (WOLFSSL_SHAKE128 || WOLFSSL_SHAKE256) && LIBWOLFSSL_VERSION_HEX */