From a1a631efe456d2a048e82fd56d0cee9d5c3ebc70 Mon Sep 17 00:00:00 2001 From: Karel Miko Date: Thu, 30 Jul 2026 20:17:44 +0200 Subject: [PATCH] make sha1/sha states clonable via memcpy --- src/hashes/sha1.c | 2 -- src/hashes/sha1_x86.c | 8 ++---- src/hashes/sha2/sha224.c | 2 -- src/hashes/sha2/sha224_x86.c | 2 -- src/hashes/sha2/sha256.c | 2 -- src/hashes/sha2/sha256_x86.c | 12 ++++---- src/headers/tomcrypt_hash.h | 6 ++-- tests/cipher_hash_test.c | 53 ++++++++++++++++++++++++++++++++++++ 8 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/hashes/sha1.c b/src/hashes/sha1.c index 72ef692d1..6a9763ab5 100644 --- a/src/hashes/sha1.c +++ b/src/hashes/sha1.c @@ -188,8 +188,6 @@ int sha1_c_init(hash_state * md) { LTC_ARGCHK(md != NULL); - md->sha1.state = LTC_ALIGN_BUF(md->sha1.state_buf, 16); - md->sha1.state[0] = 0x67452301UL; md->sha1.state[1] = 0xefcdab89UL; md->sha1.state[2] = 0x98badcfeUL; diff --git a/src/hashes/sha1_x86.c b/src/hashes/sha1_x86.c index c1c5408be..0054758a1 100644 --- a/src/hashes/sha1_x86.c +++ b/src/hashes/sha1_x86.c @@ -18,7 +18,7 @@ #elif defined(_MSC_VER) #include #endif -#include /* SSE2 _mm_load_si128 _mm_loadu_si128 _mm_store_si128 _mm_set_epi32 _mm_set_epi64x _mm_setzero_si128 _mm_xor_si128 _mm_add_epi32 _mm_shuffle_epi32 */ +#include /* SSE2 _mm_loadu_si128 _mm_storeu_si128 _mm_set_epi32 _mm_set_epi64x _mm_setzero_si128 _mm_xor_si128 _mm_add_epi32 _mm_shuffle_epi32 */ #include /* SSSE3 _mm_shuffle_epi8 */ #include /* SSE4.1 _mm_extract_epi32 */ #include /* SHA _mm_sha1msg1_epu32 _mm_sha1msg2_epu32 _mm_sha1rnds4_epu32 _mm_sha1nexte_epu32 */ @@ -65,7 +65,7 @@ static int LTC_SHA_TARGET s_sha1_x86_compress(hash_state *md, const unsigned cha __m128i msg_3; reverse_8 = _mm_set_epi64x(0x0001020304050607ull, 0x08090a0b0c0d0e0full); - abcdx = _mm_load_si128(((__m128i const*)(&md->sha1.state[0]))); + abcdx = _mm_loadu_si128(((__m128i const*)(&md->sha1.state[0]))); abcdx = _mm_shuffle_epi32(abcdx, k_reverse_32); e = _mm_set_epi32(*((int const*)(&md->sha1.state[4])), 0, 0, 0); @@ -173,7 +173,7 @@ static int LTC_SHA_TARGET s_sha1_x86_compress(hash_state *md, const unsigned cha e = _mm_add_epi32(e, old_e); abcdx = _mm_shuffle_epi32(abcdx, k_reverse_32); - _mm_store_si128(((__m128i*)(&md->sha1.state[0])), abcdx); + _mm_storeu_si128(((__m128i*)(&md->sha1.state[0])), abcdx); *((int*)(&md->sha1.state[4])) = _mm_extract_epi32(e, 3); return CRYPT_OK; @@ -200,8 +200,6 @@ int sha1_x86_init(hash_state * md) { LTC_ARGCHK(md != NULL); - md->sha1.state = LTC_ALIGN_BUF(md->sha1.state_buf, 16); - md->sha1.state[0] = 0x67452301UL; md->sha1.state[1] = 0xefcdab89UL; md->sha1.state[2] = 0x98badcfeUL; diff --git a/src/hashes/sha2/sha224.c b/src/hashes/sha2/sha224.c index 15fc36767..fa3d54545 100644 --- a/src/hashes/sha2/sha224.c +++ b/src/hashes/sha2/sha224.c @@ -37,8 +37,6 @@ int sha224_c_init(hash_state * md) { LTC_ARGCHK(md != NULL); - md->sha256.state = LTC_ALIGN_BUF(md->sha256.state_buf, 16); - md->sha256.curlen = 0; md->sha256.length = 0; md->sha256.state[0] = 0xc1059ed8UL; diff --git a/src/hashes/sha2/sha224_x86.c b/src/hashes/sha2/sha224_x86.c index f562b34a0..f6a1a1ffb 100644 --- a/src/hashes/sha2/sha224_x86.c +++ b/src/hashes/sha2/sha224_x86.c @@ -37,8 +37,6 @@ int sha224_x86_init(hash_state * md) { LTC_ARGCHK(md != NULL); - md->sha256.state = LTC_ALIGN_BUF(md->sha256.state_buf, 16); - md->sha256.curlen = 0; md->sha256.length = 0; md->sha256.state[0] = 0xc1059ed8UL; diff --git a/src/hashes/sha2/sha256.c b/src/hashes/sha2/sha256.c index a37028d64..31661c49f 100644 --- a/src/hashes/sha2/sha256.c +++ b/src/hashes/sha2/sha256.c @@ -235,8 +235,6 @@ int sha256_c_init(hash_state * md) { LTC_ARGCHK(md != NULL); - md->sha256.state = LTC_ALIGN_BUF(md->sha256.state_buf, 16); - md->sha256.curlen = 0; md->sha256.length = 0; md->sha256.state[0] = 0x6A09E667UL; diff --git a/src/hashes/sha2/sha256_x86.c b/src/hashes/sha2/sha256_x86.c index 43a23c913..a8bbf7294 100644 --- a/src/hashes/sha2/sha256_x86.c +++ b/src/hashes/sha2/sha256_x86.c @@ -18,7 +18,7 @@ #elif defined(_MSC_VER) #include #endif -#include /* SSE2 _mm_load_si128 _mm_loadu_si128 _mm_store_si128 _mm_set_epi64x _mm_add_epi32 _mm_shuffle_epi32 */ +#include /* SSE2 _mm_load_si128 _mm_loadu_si128 _mm_storeu_si128 _mm_set_epi64x _mm_add_epi32 _mm_shuffle_epi32 */ #include /* SSSE3 _mm_alignr_epi8 _mm_shuffle_epi8 */ #include /* SSE4.1 _mm_blend_epi16 */ #include /* SHA _mm_sha256msg1_epu32 _mm_sha256msg2_epu32 _mm_sha256rnds2_epu32 */ @@ -90,8 +90,8 @@ static int LTC_SHA_TARGET s_sha256_x86_compress(hash_state * md, const unsigned __m128i msg_3; reverse = _mm_set_epi64x(0x0c0d0e0f08090a0bull, 0x0405060700010203ull); - state_0 = _mm_load_si128(((__m128i const*)(&md->sha256.state[0]))); - state_1 = _mm_load_si128(((__m128i const*)(&md->sha256.state[4]))); + state_0 = _mm_loadu_si128(((__m128i const*)(&md->sha256.state[0]))); + state_1 = _mm_loadu_si128(((__m128i const*)(&md->sha256.state[4]))); tmp = _mm_shuffle_epi32(state_0, k_shuffle_epi32(0x2, 0x3, 0x0, 0x1)); state_1 = _mm_shuffle_epi32(state_1, k_shuffle_epi32(0x0, 0x1, 0x2, 0x3)); state_0 = _mm_alignr_epi8(tmp, state_1, k_alignr_epi8(2)); @@ -242,8 +242,8 @@ static int LTC_SHA_TARGET s_sha256_x86_compress(hash_state * md, const unsigned state_1 = _mm_shuffle_epi32(state_1, k_shuffle_epi32(0x2, 0x3, 0x0, 0x1)); state_0 = ltc_mm_blend_epi32(tmp, state_1, k_blend_epi32(0x1, 0x1, 0x0, 0x0)); state_1 = _mm_alignr_epi8(state_1, tmp, k_alignr_epi8(2)); - _mm_store_si128(((__m128i*)(&md->sha256.state[0])), state_0); - _mm_store_si128(((__m128i*)(&md->sha256.state[4])), state_1); + _mm_storeu_si128(((__m128i*)(&md->sha256.state[0])), state_0); + _mm_storeu_si128(((__m128i*)(&md->sha256.state[4])), state_1); return CRYPT_OK; } #undef K @@ -267,8 +267,6 @@ int sha256_x86_init(hash_state * md) { LTC_ARGCHK(md != NULL); - md->sha256.state = LTC_ALIGN_BUF(md->sha256.state_buf, 16); - md->sha256.curlen = 0; md->sha256.length = 0; md->sha256.state[0] = 0x6A09E667UL; diff --git a/src/headers/tomcrypt_hash.h b/src/headers/tomcrypt_hash.h index 0be5b4c56..7861fda8f 100644 --- a/src/headers/tomcrypt_hash.h +++ b/src/headers/tomcrypt_hash.h @@ -37,18 +37,16 @@ struct sha512_state { #ifdef LTC_SHA256 struct sha256_state { ulong64 length; - ulong32 *state, curlen; + ulong32 state[8], curlen; unsigned char buf[64]; - unsigned char state_buf[LTC_ALIGNED_BUF_SIZE(ulong32, 8, 16)]; }; #endif #ifdef LTC_SHA1 struct sha1_state { ulong64 length; - ulong32 *state, curlen; + ulong32 state[5], curlen; unsigned char buf[64]; - unsigned char state_buf[LTC_ALIGNED_BUF_SIZE(ulong32, 5, 16)]; }; #endif diff --git a/tests/cipher_hash_test.c b/tests/cipher_hash_test.c index c296fffd8..924e53a0c 100644 --- a/tests/cipher_hash_test.c +++ b/tests/cipher_hash_test.c @@ -4,6 +4,51 @@ #include +/* cloning a hash_state via memcpy must yield a working/independent copy */ +static int s_hash_state_clone_test(const struct ltc_hash_descriptor *desc, const char *name) +{ + hash_state md, clone1, *clone2; + unsigned char data[200], out[MAXBLOCKSIZE], expect1[MAXBLOCKSIZE], expect2[MAXBLOCKSIZE]; + unsigned long i, hashsize = desc->hashsize; + + for (i = 0; i < sizeof(data); i++) data[i] = (unsigned char)i; + + /* reference digests: expect1 = H(data[0..149]), expect2 = H(data[0..99] || data[150..199]) */ + DO(desc->init(&md)); + DO(desc->process(&md, data, 100)); + DO(desc->process(&md, data + 100, 50)); + DO(desc->done(&md, expect1)); + DO(desc->init(&md)); + DO(desc->process(&md, data, 100)); + DO(desc->process(&md, data + 150, 50)); + DO(desc->done(&md, expect2)); + + /* clone the state after the common prefix, then let the original and the clone diverge */ + DO(desc->init(&md)); + DO(desc->process(&md, data, 100)); + XMEMCPY(&clone1, &md, sizeof(hash_state)); + DO(desc->process(&md, data + 100, 50)); + DO(desc->done(&md, out)); + COMPARE_TESTVECTOR(out, hashsize, expect1, hashsize, name, 0); + DO(desc->process(&clone1, data + 150, 50)); + DO(desc->done(&clone1, out)); + COMPARE_TESTVECTOR(out, hashsize, expect2, hashsize, name, 1); + + /* clone into heap memory and wipe the original before using the clone */ + DO(desc->init(&md)); + DO(desc->process(&md, data, 100)); + clone2 = XMALLOC(sizeof(hash_state)); + if (clone2 == NULL) return CRYPT_MEM; + XMEMCPY(clone2, &md, sizeof(hash_state)); + zeromem(&md, sizeof(hash_state)); + DO(desc->process(clone2, data + 150, 50)); + DO(desc->done(clone2, out)); + XFREE(clone2); + COMPARE_TESTVECTOR(out, hashsize, expect2, hashsize, name, 2); + + return CRYPT_OK; +} + int cipher_hash_test(void) { int x; @@ -57,28 +102,36 @@ int cipher_hash_test(void) /* test hashes */ for (x = 0; hash_descriptor[x].name != NULL; x++) { DOX(hash_descriptor[x].test(), hash_descriptor[x].name); + /* test that state can be cloned via memcpy */ + DOX(s_hash_state_clone_test(&hash_descriptor[x], hash_descriptor[x].name), hash_descriptor[x].name); } /* explicit SHA-NI + portable implementations tests */ if (shani_is_supported()) { #if defined(LTC_SHA256) && defined(LTC_SHA256_X86) DO(sha256_x86_test()); + DO(s_hash_state_clone_test(&sha256_x86_desc, "sha256-x86-clone")); #endif #if defined(LTC_SHA224) && defined(LTC_SHA224_X86) DO(sha224_x86_test()); + DO(s_hash_state_clone_test(&sha224_x86_desc, "sha224-x86-clone")); #endif #if defined(LTC_SHA1) && defined(LTC_SHA1_X86) DO(sha1_x86_test()); + DO(s_hash_state_clone_test(&sha1_x86_desc, "sha1-x86-clone")); #endif } #if defined(LTC_SHA256) DO(sha256_c_test()); + DO(s_hash_state_clone_test(&sha256_portable_desc, "sha256-c-clone")); #endif #if defined(LTC_SHA224) DO(sha224_c_test()); + DO(s_hash_state_clone_test(&sha224_portable_desc, "sha224-c-clone")); #endif #if defined(LTC_SHA1) DO(sha1_c_test()); + DO(s_hash_state_clone_test(&sha1_portable_desc, "sha1-c-clone")); #endif #ifdef LTC_SHA3 /* SHAKE128 + SHAKE256 tests are a bit special */