From f1f5745f506aa2abfbf69f6e20780c7e173113ee Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 2 May 2026 13:06:13 +0200 Subject: [PATCH] Improve Keccak performance --- .../keccak1600/armsha3/keccak1600_armsha3.c | 22 +++++- .../keccak1600/ref/keccak1600_ref.c | 22 +++++- src/libsodium/crypto_hash/sha3/hash_sha3.c | 34 ++++++--- .../crypto_xof/shake128/ref/shake128_ref.c | 69 ++++++++++++++----- .../crypto_xof/shake256/ref/shake256_ref.c | 69 ++++++++++++++----- .../turboshake128/ref/turboshake128_ref.c | 69 ++++++++++++++----- .../turboshake256/ref/turboshake256_ref.c | 69 ++++++++++++++----- 7 files changed, 272 insertions(+), 82 deletions(-) diff --git a/src/libsodium/crypto_core/keccak1600/armsha3/keccak1600_armsha3.c b/src/libsodium/crypto_core/keccak1600/armsha3/keccak1600_armsha3.c index 12c105cd..9d7ee815 100644 --- a/src/libsodium/crypto_core/keccak1600/armsha3/keccak1600_armsha3.c +++ b/src/libsodium/crypto_core/keccak1600/armsha3/keccak1600_armsha3.c @@ -254,10 +254,26 @@ void keccak1600_armsha3_xor_bytes(void *state, const unsigned char *data, size_t offset, size_t length) { unsigned char *st = (unsigned char *) state; - size_t i; + uint64_t t; - for (i = 0U; i < length; i++) { - st[offset + i] ^= data[i]; + while (length > 0U && (offset & 7U) != 0U) { + st[offset] ^= *data; + data++; + offset++; + length--; + } + while (length >= 8U) { + t = LOAD64_LE(st + offset) ^ LOAD64_LE(data); + STORE64_LE(st + offset, t); + data += 8U; + offset += 8U; + length -= 8U; + } + while (length > 0U) { + st[offset] ^= *data; + data++; + offset++; + length--; } } diff --git a/src/libsodium/crypto_core/keccak1600/ref/keccak1600_ref.c b/src/libsodium/crypto_core/keccak1600/ref/keccak1600_ref.c index 88115779..ce000ec1 100644 --- a/src/libsodium/crypto_core/keccak1600/ref/keccak1600_ref.c +++ b/src/libsodium/crypto_core/keccak1600/ref/keccak1600_ref.c @@ -431,10 +431,26 @@ void keccak1600_ref_xor_bytes(void *state, const unsigned char *data, size_t offset, size_t length) { unsigned char *st = (unsigned char *) state; - size_t i; + uint64_t t; - for (i = 0U; i < length; i++) { - st[offset + i] ^= data[i]; + while (length > 0U && (offset & 7U) != 0U) { + st[offset] ^= *data; + data++; + offset++; + length--; + } + while (length >= 8U) { + t = LOAD64_LE(st + offset) ^ LOAD64_LE(data); + STORE64_LE(st + offset, t); + data += 8U; + offset += 8U; + length -= 8U; + } + while (length > 0U) { + st[offset] ^= *data; + data++; + offset++; + length--; } } diff --git a/src/libsodium/crypto_hash/sha3/hash_sha3.c b/src/libsodium/crypto_hash/sha3/hash_sha3.c index 842d19ff..6f07f06d 100644 --- a/src/libsodium/crypto_hash/sha3/hash_sha3.c +++ b/src/libsodium/crypto_hash/sha3/hash_sha3.c @@ -46,18 +46,36 @@ sha3_update(sha3_state_internal *state, const unsigned char *in, size_t inlen) ret = -1; } - while (consumed < inlen) { - if (state->offset == state->rate) { + if (state->offset == state->rate && inlen > 0U) { + crypto_core_keccak1600_permute_24(&state->state); + state->offset = 0; + } + if (state->offset != 0U && inlen > 0U) { + chunk_size = state->rate - state->offset; + if (chunk_size > inlen) { + chunk_size = inlen; + } + crypto_core_keccak1600_xor_bytes(&state->state, in, state->offset, chunk_size); + state->offset += chunk_size; + consumed = chunk_size; + if (state->offset == state->rate && consumed < inlen) { crypto_core_keccak1600_permute_24(&state->state); state->offset = 0; } - chunk_size = state->rate - state->offset; - if (chunk_size > inlen - consumed) { - chunk_size = inlen - consumed; + } + while (inlen - consumed >= state->rate) { + crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], 0U, state->rate); + consumed += state->rate; + state->offset = state->rate; + if (consumed < inlen) { + crypto_core_keccak1600_permute_24(&state->state); + state->offset = 0; } - crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], state->offset, chunk_size); - state->offset += chunk_size; - consumed += chunk_size; + } + if (consumed < inlen) { + chunk_size = inlen - consumed; + crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], 0U, chunk_size); + state->offset = chunk_size; } return ret; diff --git a/src/libsodium/crypto_xof/shake128/ref/shake128_ref.c b/src/libsodium/crypto_xof/shake128/ref/shake128_ref.c index 94a151a7..0bf5539c 100644 --- a/src/libsodium/crypto_xof/shake128/ref/shake128_ref.c +++ b/src/libsodium/crypto_xof/shake128/ref/shake128_ref.c @@ -37,18 +37,36 @@ shake128_ref_update(shake128_state_internal *state, const unsigned char *in, siz ret = -1; } - while (consumed < inlen) { - if (state->offset == SHAKE128_RATE) { + if (state->offset == SHAKE128_RATE && inlen > 0U) { + crypto_core_keccak1600_permute_24(&state->state); + state->offset = 0; + } + if (state->offset != 0U && inlen > 0U) { + chunk_size = SHAKE128_RATE - state->offset; + if (chunk_size > inlen) { + chunk_size = inlen; + } + crypto_core_keccak1600_xor_bytes(&state->state, in, state->offset, chunk_size); + state->offset += chunk_size; + consumed = chunk_size; + if (state->offset == SHAKE128_RATE && consumed < inlen) { crypto_core_keccak1600_permute_24(&state->state); state->offset = 0; } - chunk_size = SHAKE128_RATE - state->offset; - if (chunk_size > inlen - consumed) { - chunk_size = inlen - consumed; + } + while (inlen - consumed >= SHAKE128_RATE) { + crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], 0U, SHAKE128_RATE); + consumed += SHAKE128_RATE; + state->offset = SHAKE128_RATE; + if (consumed < inlen) { + crypto_core_keccak1600_permute_24(&state->state); + state->offset = 0; } - crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], state->offset, chunk_size); - state->offset += chunk_size; - consumed += chunk_size; + } + if (consumed < inlen) { + chunk_size = inlen - consumed; + crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], 0U, chunk_size); + state->offset = chunk_size; } return ret; @@ -94,19 +112,36 @@ shake128_ref_squeeze(shake128_state_internal *state, unsigned char *out, size_t shake128_finalize(state); } - while (extracted < outlen) { - if (state->offset == SHAKE128_RATE) { + if (state->offset == SHAKE128_RATE && outlen > 0U) { + crypto_core_keccak1600_permute_24(&state->state); + state->offset = 0; + } + if (state->offset != 0U && outlen > 0U) { + chunk_size = SHAKE128_RATE - state->offset; + if (chunk_size > outlen) { + chunk_size = outlen; + } + crypto_core_keccak1600_extract_bytes(&state->state, out, state->offset, chunk_size); + state->offset += chunk_size; + extracted = chunk_size; + if (state->offset == SHAKE128_RATE && extracted < outlen) { crypto_core_keccak1600_permute_24(&state->state); state->offset = 0; } - chunk_size = SHAKE128_RATE - state->offset; - if (chunk_size > outlen - extracted) { - chunk_size = outlen - extracted; + } + while (outlen - extracted >= SHAKE128_RATE) { + crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], 0U, SHAKE128_RATE); + extracted += SHAKE128_RATE; + state->offset = SHAKE128_RATE; + if (extracted < outlen) { + crypto_core_keccak1600_permute_24(&state->state); + state->offset = 0; } - crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], state->offset, - chunk_size); - state->offset += chunk_size; - extracted += chunk_size; + } + if (extracted < outlen) { + chunk_size = outlen - extracted; + crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], 0U, chunk_size); + state->offset = chunk_size; } return 0; diff --git a/src/libsodium/crypto_xof/shake256/ref/shake256_ref.c b/src/libsodium/crypto_xof/shake256/ref/shake256_ref.c index 1ca46206..bda09365 100644 --- a/src/libsodium/crypto_xof/shake256/ref/shake256_ref.c +++ b/src/libsodium/crypto_xof/shake256/ref/shake256_ref.c @@ -37,18 +37,36 @@ shake256_ref_update(shake256_state_internal *state, const unsigned char *in, siz ret = -1; } - while (consumed < inlen) { - if (state->offset == SHAKE256_RATE) { + if (state->offset == SHAKE256_RATE && inlen > 0U) { + crypto_core_keccak1600_permute_24(&state->state); + state->offset = 0; + } + if (state->offset != 0U && inlen > 0U) { + chunk_size = SHAKE256_RATE - state->offset; + if (chunk_size > inlen) { + chunk_size = inlen; + } + crypto_core_keccak1600_xor_bytes(&state->state, in, state->offset, chunk_size); + state->offset += chunk_size; + consumed = chunk_size; + if (state->offset == SHAKE256_RATE && consumed < inlen) { crypto_core_keccak1600_permute_24(&state->state); state->offset = 0; } - chunk_size = SHAKE256_RATE - state->offset; - if (chunk_size > inlen - consumed) { - chunk_size = inlen - consumed; + } + while (inlen - consumed >= SHAKE256_RATE) { + crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], 0U, SHAKE256_RATE); + consumed += SHAKE256_RATE; + state->offset = SHAKE256_RATE; + if (consumed < inlen) { + crypto_core_keccak1600_permute_24(&state->state); + state->offset = 0; } - crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], state->offset, chunk_size); - state->offset += chunk_size; - consumed += chunk_size; + } + if (consumed < inlen) { + chunk_size = inlen - consumed; + crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], 0U, chunk_size); + state->offset = chunk_size; } return ret; @@ -94,19 +112,36 @@ shake256_ref_squeeze(shake256_state_internal *state, unsigned char *out, size_t shake256_finalize(state); } - while (extracted < outlen) { - if (state->offset == SHAKE256_RATE) { + if (state->offset == SHAKE256_RATE && outlen > 0U) { + crypto_core_keccak1600_permute_24(&state->state); + state->offset = 0; + } + if (state->offset != 0U && outlen > 0U) { + chunk_size = SHAKE256_RATE - state->offset; + if (chunk_size > outlen) { + chunk_size = outlen; + } + crypto_core_keccak1600_extract_bytes(&state->state, out, state->offset, chunk_size); + state->offset += chunk_size; + extracted = chunk_size; + if (state->offset == SHAKE256_RATE && extracted < outlen) { crypto_core_keccak1600_permute_24(&state->state); state->offset = 0; } - chunk_size = SHAKE256_RATE - state->offset; - if (chunk_size > outlen - extracted) { - chunk_size = outlen - extracted; + } + while (outlen - extracted >= SHAKE256_RATE) { + crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], 0U, SHAKE256_RATE); + extracted += SHAKE256_RATE; + state->offset = SHAKE256_RATE; + if (extracted < outlen) { + crypto_core_keccak1600_permute_24(&state->state); + state->offset = 0; } - crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], state->offset, - chunk_size); - state->offset += chunk_size; - extracted += chunk_size; + } + if (extracted < outlen) { + chunk_size = outlen - extracted; + crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], 0U, chunk_size); + state->offset = chunk_size; } return 0; diff --git a/src/libsodium/crypto_xof/turboshake128/ref/turboshake128_ref.c b/src/libsodium/crypto_xof/turboshake128/ref/turboshake128_ref.c index 6e281891..5aa31f3e 100644 --- a/src/libsodium/crypto_xof/turboshake128/ref/turboshake128_ref.c +++ b/src/libsodium/crypto_xof/turboshake128/ref/turboshake128_ref.c @@ -37,18 +37,36 @@ turboshake128_ref_update(turboshake128_state_internal *state, const unsigned cha ret = -1; } - while (consumed < inlen) { - if (state->offset == TURBOSHAKE128_RATE) { + if (state->offset == TURBOSHAKE128_RATE && inlen > 0U) { + crypto_core_keccak1600_permute_12(&state->state); + state->offset = 0; + } + if (state->offset != 0U && inlen > 0U) { + chunk_size = TURBOSHAKE128_RATE - state->offset; + if (chunk_size > inlen) { + chunk_size = inlen; + } + crypto_core_keccak1600_xor_bytes(&state->state, in, state->offset, chunk_size); + state->offset += chunk_size; + consumed = chunk_size; + if (state->offset == TURBOSHAKE128_RATE && consumed < inlen) { crypto_core_keccak1600_permute_12(&state->state); state->offset = 0; } - chunk_size = TURBOSHAKE128_RATE - state->offset; - if (chunk_size > inlen - consumed) { - chunk_size = inlen - consumed; + } + while (inlen - consumed >= TURBOSHAKE128_RATE) { + crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], 0U, TURBOSHAKE128_RATE); + consumed += TURBOSHAKE128_RATE; + state->offset = TURBOSHAKE128_RATE; + if (consumed < inlen) { + crypto_core_keccak1600_permute_12(&state->state); + state->offset = 0; } - crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], state->offset, chunk_size); - state->offset += chunk_size; - consumed += chunk_size; + } + if (consumed < inlen) { + chunk_size = inlen - consumed; + crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], 0U, chunk_size); + state->offset = chunk_size; } return ret; @@ -94,19 +112,36 @@ turboshake128_ref_squeeze(turboshake128_state_internal *state, unsigned char *ou turboshake128_finalize(state); } - while (extracted < outlen) { - if (state->offset == TURBOSHAKE128_RATE) { + if (state->offset == TURBOSHAKE128_RATE && outlen > 0U) { + crypto_core_keccak1600_permute_12(&state->state); + state->offset = 0; + } + if (state->offset != 0U && outlen > 0U) { + chunk_size = TURBOSHAKE128_RATE - state->offset; + if (chunk_size > outlen) { + chunk_size = outlen; + } + crypto_core_keccak1600_extract_bytes(&state->state, out, state->offset, chunk_size); + state->offset += chunk_size; + extracted = chunk_size; + if (state->offset == TURBOSHAKE128_RATE && extracted < outlen) { crypto_core_keccak1600_permute_12(&state->state); state->offset = 0; } - chunk_size = TURBOSHAKE128_RATE - state->offset; - if (chunk_size > outlen - extracted) { - chunk_size = outlen - extracted; + } + while (outlen - extracted >= TURBOSHAKE128_RATE) { + crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], 0U, TURBOSHAKE128_RATE); + extracted += TURBOSHAKE128_RATE; + state->offset = TURBOSHAKE128_RATE; + if (extracted < outlen) { + crypto_core_keccak1600_permute_12(&state->state); + state->offset = 0; } - crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], state->offset, - chunk_size); - state->offset += chunk_size; - extracted += chunk_size; + } + if (extracted < outlen) { + chunk_size = outlen - extracted; + crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], 0U, chunk_size); + state->offset = chunk_size; } return 0; diff --git a/src/libsodium/crypto_xof/turboshake256/ref/turboshake256_ref.c b/src/libsodium/crypto_xof/turboshake256/ref/turboshake256_ref.c index 3d5e6f06..8e80c8a2 100644 --- a/src/libsodium/crypto_xof/turboshake256/ref/turboshake256_ref.c +++ b/src/libsodium/crypto_xof/turboshake256/ref/turboshake256_ref.c @@ -37,18 +37,36 @@ turboshake256_ref_update(turboshake256_state_internal *state, const unsigned cha ret = -1; } - while (consumed < inlen) { - if (state->offset == TURBOSHAKE256_RATE) { + if (state->offset == TURBOSHAKE256_RATE && inlen > 0U) { + crypto_core_keccak1600_permute_12(&state->state); + state->offset = 0; + } + if (state->offset != 0U && inlen > 0U) { + chunk_size = TURBOSHAKE256_RATE - state->offset; + if (chunk_size > inlen) { + chunk_size = inlen; + } + crypto_core_keccak1600_xor_bytes(&state->state, in, state->offset, chunk_size); + state->offset += chunk_size; + consumed = chunk_size; + if (state->offset == TURBOSHAKE256_RATE && consumed < inlen) { crypto_core_keccak1600_permute_12(&state->state); state->offset = 0; } - chunk_size = TURBOSHAKE256_RATE - state->offset; - if (chunk_size > inlen - consumed) { - chunk_size = inlen - consumed; + } + while (inlen - consumed >= TURBOSHAKE256_RATE) { + crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], 0U, TURBOSHAKE256_RATE); + consumed += TURBOSHAKE256_RATE; + state->offset = TURBOSHAKE256_RATE; + if (consumed < inlen) { + crypto_core_keccak1600_permute_12(&state->state); + state->offset = 0; } - crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], state->offset, chunk_size); - state->offset += chunk_size; - consumed += chunk_size; + } + if (consumed < inlen) { + chunk_size = inlen - consumed; + crypto_core_keccak1600_xor_bytes(&state->state, &in[consumed], 0U, chunk_size); + state->offset = chunk_size; } return ret; @@ -94,19 +112,36 @@ turboshake256_ref_squeeze(turboshake256_state_internal *state, unsigned char *ou turboshake256_finalize(state); } - while (extracted < outlen) { - if (state->offset == TURBOSHAKE256_RATE) { + if (state->offset == TURBOSHAKE256_RATE && outlen > 0U) { + crypto_core_keccak1600_permute_12(&state->state); + state->offset = 0; + } + if (state->offset != 0U && outlen > 0U) { + chunk_size = TURBOSHAKE256_RATE - state->offset; + if (chunk_size > outlen) { + chunk_size = outlen; + } + crypto_core_keccak1600_extract_bytes(&state->state, out, state->offset, chunk_size); + state->offset += chunk_size; + extracted = chunk_size; + if (state->offset == TURBOSHAKE256_RATE && extracted < outlen) { crypto_core_keccak1600_permute_12(&state->state); state->offset = 0; } - chunk_size = TURBOSHAKE256_RATE - state->offset; - if (chunk_size > outlen - extracted) { - chunk_size = outlen - extracted; + } + while (outlen - extracted >= TURBOSHAKE256_RATE) { + crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], 0U, TURBOSHAKE256_RATE); + extracted += TURBOSHAKE256_RATE; + state->offset = TURBOSHAKE256_RATE; + if (extracted < outlen) { + crypto_core_keccak1600_permute_12(&state->state); + state->offset = 0; } - crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], state->offset, - chunk_size); - state->offset += chunk_size; - extracted += chunk_size; + } + if (extracted < outlen) { + chunk_size = outlen - extracted; + crypto_core_keccak1600_extract_bytes(&state->state, &out[extracted], 0U, chunk_size); + state->offset = chunk_size; } return 0;