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.
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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) */
|
||||
|
||||
@@ -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 <wolfssl/options.h>
|
||||
#include "wolfssl_common.h"
|
||||
|
||||
#ifdef WOLFSSL_SHAKE256
|
||||
#include <wolfssl/version.h>
|
||||
|
||||
/* the Absorb and SqueezeBlocks functions were added with 5.5.1 */
|
||||
#if (defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256)) && \
|
||||
LIBWOLFSSL_VERSION_HEX >= 0x05005001
|
||||
|
||||
#include <wolfssl/wolfcrypt/sha3.h>
|
||||
|
||||
#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 */
|
||||
|
||||
Reference in New Issue
Block a user