sha3: make post-final misuse safe and deterministic

This commit is contained in:
Frank Denis
2026-04-08 00:16:01 +02:00
parent fd52fd61a2
commit f54f20abbf
5 changed files with 183 additions and 17 deletions
+33 -17
View File
@@ -37,6 +37,14 @@ sha3_update(sha3_state_internal *state, const unsigned char *in, size_t inlen)
{
size_t consumed = 0;
size_t chunk_size;
int ret = 0;
if (state->phase != SHA3_PHASE_ABSORBING) {
crypto_core_keccak1600_permute_24(&state->state);
state->phase = SHA3_PHASE_ABSORBING;
state->offset = 0;
ret = -1;
}
while (consumed < inlen) {
if (state->offset == state->rate) {
@@ -52,36 +60,42 @@ sha3_update(sha3_state_internal *state, const unsigned char *in, size_t inlen)
consumed += chunk_size;
}
return 0;
return ret;
}
static int
sha3_final(sha3_state_internal *state, unsigned char *out)
{
unsigned char pad;
int ret = 0;
if (state->offset == state->rate) {
if (state->phase != SHA3_PHASE_ABSORBING) {
crypto_core_keccak1600_permute_24(&state->state);
state->offset = 0;
}
if (state->offset == state->rate - 1) {
pad = (unsigned char) (SHA3_DOMAIN ^ 0x80);
crypto_core_keccak1600_xor_bytes(&state->state, &pad, state->offset, 1);
ret = -1;
} else {
pad = SHA3_DOMAIN;
crypto_core_keccak1600_xor_bytes(&state->state, &pad, state->offset, 1);
pad = 0x80;
crypto_core_keccak1600_xor_bytes(&state->state, &pad, state->rate - 1, 1);
}
if (state->offset == state->rate) {
crypto_core_keccak1600_permute_24(&state->state);
state->offset = 0;
}
crypto_core_keccak1600_permute_24(&state->state);
if (state->offset == state->rate - 1) {
pad = (unsigned char) (SHA3_DOMAIN ^ 0x80);
crypto_core_keccak1600_xor_bytes(&state->state, &pad, state->offset, 1);
} else {
pad = SHA3_DOMAIN;
crypto_core_keccak1600_xor_bytes(&state->state, &pad, state->offset, 1);
pad = 0x80;
crypto_core_keccak1600_xor_bytes(&state->state, &pad, state->rate - 1, 1);
}
crypto_core_keccak1600_permute_24(&state->state);
}
crypto_core_keccak1600_extract_bytes(&state->state, out, 0, state->outlen);
state->offset = 0;
state->phase = SHA3_PHASE_FINALIZED;
sodium_memzero(state, sizeof *state);
return 0;
return ret;
}
size_t
@@ -125,6 +139,7 @@ crypto_hash_sha3256(unsigned char *out, const unsigned char *in, unsigned long l
crypto_hash_sha3256_init(&state);
crypto_hash_sha3256_update(&state, in, inlen);
crypto_hash_sha3256_final(&state, out);
sodium_memzero(&state, sizeof state);
return 0;
}
@@ -170,6 +185,7 @@ crypto_hash_sha3512(unsigned char *out, const unsigned char *in, unsigned long l
crypto_hash_sha3512_init(&state);
crypto_hash_sha3512_update(&state, in, inlen);
crypto_hash_sha3512_final(&state, out);
sodium_memzero(&state, sizeof state);
return 0;
}
+73
View File
@@ -2,6 +2,8 @@
#define TEST_NAME "hash_sha3256"
#include "cmptest.h"
#define TEST_SHA3_256_BYTES 32U
int
main(void)
{
@@ -125,6 +127,77 @@ main(void)
printf("Rate-1 boundary test passed\n");
}
{
static const unsigned char resumed_msg[] = { 0x78, 0x79 };
static const unsigned char resumed_msg_other[] = { 0x78, 0x7a };
unsigned char resumed_out1[TEST_SHA3_256_BYTES];
unsigned char resumed_out2[TEST_SHA3_256_BYTES];
unsigned char resumed_out3[TEST_SHA3_256_BYTES];
crypto_hash_sha3256_state state2;
crypto_hash_sha3256_state state3;
int ret;
crypto_hash_sha3256_init(&state);
crypto_hash_sha3256_init(&state2);
crypto_hash_sha3256_init(&state3);
crypto_hash_sha3256_update(&state, msg_abc, 3);
crypto_hash_sha3256_update(&state2, msg_abc, 3);
crypto_hash_sha3256_update(&state3, msg_abc, 3);
crypto_hash_sha3256_final(&state, out);
crypto_hash_sha3256_final(&state2, resumed_out2);
crypto_hash_sha3256_final(&state3, resumed_out3);
assert(memcmp(out, out_abc, 32) == 0);
assert(memcmp(resumed_out2, out_abc, 32) == 0);
assert(memcmp(resumed_out3, out_abc, 32) == 0);
ret = crypto_hash_sha3256_update(&state, resumed_msg, sizeof resumed_msg);
assert(ret == -1);
ret = crypto_hash_sha3256_update(&state2, resumed_msg, sizeof resumed_msg);
assert(ret == -1);
ret = crypto_hash_sha3256_update(&state3, resumed_msg_other, sizeof resumed_msg_other);
assert(ret == -1);
ret = crypto_hash_sha3256_final(&state, resumed_out1);
assert(ret == 0);
ret = crypto_hash_sha3256_final(&state2, resumed_out2);
assert(ret == 0);
ret = crypto_hash_sha3256_final(&state3, resumed_out3);
assert(ret == 0);
assert(memcmp(resumed_out1, resumed_out2, TEST_SHA3_256_BYTES) == 0);
assert(memcmp(resumed_out1, resumed_out3, TEST_SHA3_256_BYTES) != 0);
printf("Update-after-final recovery test passed\n");
}
{
unsigned char repeated_out1[TEST_SHA3_256_BYTES];
unsigned char repeated_out2[TEST_SHA3_256_BYTES];
crypto_hash_sha3256_state state2;
int ret;
memset(repeated_out1, 0xAA, sizeof repeated_out1);
memset(repeated_out2, 0xAA, sizeof repeated_out2);
crypto_hash_sha3256_init(&state);
crypto_hash_sha3256_init(&state2);
crypto_hash_sha3256_update(&state, msg_abc, 3);
crypto_hash_sha3256_update(&state2, msg_abc, 3);
ret = crypto_hash_sha3256_final(&state, out);
assert(ret == 0);
ret = crypto_hash_sha3256_final(&state2, out);
assert(ret == 0);
ret = crypto_hash_sha3256_final(&state, repeated_out1);
assert(ret == -1);
ret = crypto_hash_sha3256_final(&state2, repeated_out2);
assert(ret == -1);
assert(memcmp(repeated_out1, repeated_out2, TEST_SHA3_256_BYTES) == 0);
assert(memcmp(repeated_out1, out, TEST_SHA3_256_BYTES) != 0);
assert(repeated_out1[0] != 0xAA);
printf("Final-after-final test passed\n");
}
printf("OK\n");
return 0;
+2
View File
@@ -6,4 +6,6 @@ Chunked update test passed
Rate boundary test passed
Rate+1 boundary test passed
Rate-1 boundary test passed
Update-after-final recovery test passed
Final-after-final test passed
OK
+73
View File
@@ -2,6 +2,8 @@
#define TEST_NAME "hash_sha3512"
#include "cmptest.h"
#define TEST_SHA3_512_BYTES 64U
int
main(void)
{
@@ -134,6 +136,77 @@ main(void)
printf("Rate-1 boundary test passed\n");
}
{
static const unsigned char resumed_msg[] = { 0x78, 0x79 };
static const unsigned char resumed_msg_other[] = { 0x78, 0x7a };
unsigned char resumed_out1[TEST_SHA3_512_BYTES];
unsigned char resumed_out2[TEST_SHA3_512_BYTES];
unsigned char resumed_out3[TEST_SHA3_512_BYTES];
crypto_hash_sha3512_state state2;
crypto_hash_sha3512_state state3;
int ret;
crypto_hash_sha3512_init(&state);
crypto_hash_sha3512_init(&state2);
crypto_hash_sha3512_init(&state3);
crypto_hash_sha3512_update(&state, msg_abc, 3);
crypto_hash_sha3512_update(&state2, msg_abc, 3);
crypto_hash_sha3512_update(&state3, msg_abc, 3);
crypto_hash_sha3512_final(&state, out);
crypto_hash_sha3512_final(&state2, resumed_out2);
crypto_hash_sha3512_final(&state3, resumed_out3);
assert(memcmp(out, out_abc, 64) == 0);
assert(memcmp(resumed_out2, out_abc, 64) == 0);
assert(memcmp(resumed_out3, out_abc, 64) == 0);
ret = crypto_hash_sha3512_update(&state, resumed_msg, sizeof resumed_msg);
assert(ret == -1);
ret = crypto_hash_sha3512_update(&state2, resumed_msg, sizeof resumed_msg);
assert(ret == -1);
ret = crypto_hash_sha3512_update(&state3, resumed_msg_other, sizeof resumed_msg_other);
assert(ret == -1);
ret = crypto_hash_sha3512_final(&state, resumed_out1);
assert(ret == 0);
ret = crypto_hash_sha3512_final(&state2, resumed_out2);
assert(ret == 0);
ret = crypto_hash_sha3512_final(&state3, resumed_out3);
assert(ret == 0);
assert(memcmp(resumed_out1, resumed_out2, TEST_SHA3_512_BYTES) == 0);
assert(memcmp(resumed_out1, resumed_out3, TEST_SHA3_512_BYTES) != 0);
printf("Update-after-final recovery test passed\n");
}
{
unsigned char repeated_out1[TEST_SHA3_512_BYTES];
unsigned char repeated_out2[TEST_SHA3_512_BYTES];
crypto_hash_sha3512_state state2;
int ret;
memset(repeated_out1, 0xAA, sizeof repeated_out1);
memset(repeated_out2, 0xAA, sizeof repeated_out2);
crypto_hash_sha3512_init(&state);
crypto_hash_sha3512_init(&state2);
crypto_hash_sha3512_update(&state, msg_abc, 3);
crypto_hash_sha3512_update(&state2, msg_abc, 3);
ret = crypto_hash_sha3512_final(&state, out);
assert(ret == 0);
ret = crypto_hash_sha3512_final(&state2, out);
assert(ret == 0);
ret = crypto_hash_sha3512_final(&state, repeated_out1);
assert(ret == -1);
ret = crypto_hash_sha3512_final(&state2, repeated_out2);
assert(ret == -1);
assert(memcmp(repeated_out1, repeated_out2, TEST_SHA3_512_BYTES) == 0);
assert(memcmp(repeated_out1, out, TEST_SHA3_512_BYTES) != 0);
assert(repeated_out1[0] != 0xAA);
printf("Final-after-final test passed\n");
}
printf("OK\n");
return 0;
+2
View File
@@ -6,4 +6,6 @@ Chunked update test passed
Rate boundary test passed
Rate+1 boundary test passed
Rate-1 boundary test passed
Update-after-final recovery test passed
Final-after-final test passed
OK