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 */