Commit b760334f8f for openssl.org
commit b760334f8f10487f972b321069b1c928b9996b0d
Author: Simo Sorce <simo@redhat.com>
Date: Fri Oct 10 13:47:38 2025 -0400
Add EVP_MD_CTX serialization for SHA3/SHAKE
Implement context serialization and deserialization for the SHA3, KECCAK,
SHAKE, and KMAC provider-based digests.
This is achieved by handling the `OSSL_DIGEST_SERIALIZATION` parameter in
get_ctx_params and set_ctx_params. A custom format is used to store the KECCAK
state, including a magic number and an algorithm identifier to ensure the
context is not loaded into an incompatible digest instance.
This allows an EVP_MD_CTX to be saved and restored, which is useful for
applications that need to checkpoint hashing operations. The existing EVP
serialization tests have been extended to cover these new algorithms.
Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
(Merged from https://github.com/openssl/openssl/pull/28837)
diff --git a/providers/implementations/digests/sha3_prov.c b/providers/implementations/digests/sha3_prov.c
index 2b342b1ca8..39afa0e15c 100644
--- a/providers/implementations/digests/sha3_prov.c
+++ b/providers/implementations/digests/sha3_prov.c
@@ -8,6 +8,7 @@
*/
#include <string.h>
+#include <openssl/byteorder.h>
#include <openssl/core_names.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
@@ -522,15 +523,17 @@ static PROV_SHA3_METHOD shake_ARMSHA3_md = {
return ctx; \
}
-#define PROV_FUNC_SHA3_DIGEST_COMMON(name, bitlen, blksize, dgstsize, flags) \
- PROV_FUNC_DIGEST_GET_PARAM(name, blksize, dgstsize, flags) \
- const OSSL_DISPATCH ossl_##name##_functions[] = { \
- { OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))name##_newctx }, \
- { OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))keccak_update }, \
- { OSSL_FUNC_DIGEST_FINAL, (void (*)(void))keccak_final }, \
- { OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))keccak_freectx }, \
- { OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))keccak_dupctx }, \
- { OSSL_FUNC_DIGEST_COPYCTX, (void (*)(void))keccak_copyctx }, \
+#define PROV_FUNC_SHA3_DIGEST_COMMON(name, bitlen, blksize, dgstsize, flags) \
+ PROV_FUNC_DIGEST_GET_PARAM(name, blksize, dgstsize, flags) \
+ const OSSL_DISPATCH ossl_##name##_functions[] = { \
+ { OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))name##_newctx }, \
+ { OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))keccak_update }, \
+ { OSSL_FUNC_DIGEST_FINAL, (void (*)(void))keccak_final }, \
+ { OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))keccak_freectx }, \
+ { OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))keccak_dupctx }, \
+ { OSSL_FUNC_DIGEST_COPYCTX, (void (*)(void))keccak_copyctx }, \
+ { OSSL_FUNC_DIGEST_SERIALIZE, (void (*)(void))name##_serialize }, \
+ { OSSL_FUNC_DIGEST_DESERIALIZE, (void (*)(void))name##_deserialize }, \
PROV_DISPATCH_FUNC_DIGEST_GET_PARAMS(name)
#define PROV_FUNC_SHA3_DIGEST(name, bitlen, blksize, dgstsize, flags) \
@@ -576,6 +579,126 @@ static void *keccak_dupctx(void *ctx)
return ret;
}
+static const unsigned char keccakmagic[] = "KECCAKv1";
+#define KECCAKMAGIC_LEN (sizeof(keccakmagic) - 1)
+#define KECCAK_SERIALIZATION_LEN \
+ ( \
+ KECCAKMAGIC_LEN /* magic string */ \
+ + sizeof(uint64_t) /* impl-ID */ \
+ + sizeof(uint64_t) /* c->md_size */ \
+ + (sizeof(uint64_t) * 5 * 5) /* c->A */ \
+ + (sizeof(uint64_t) * 4) /* c->block_size, c->bufsz, c->pad, c->xof_state */ \
+ + (KECCAK1600_WIDTH / 8 - 32) /* c->buf */ \
+ )
+
+static int KECCAK_Serialize(KECCAK1600_CTX *c, int impl_id,
+ unsigned char *output, size_t *outlen)
+{
+ unsigned char *p;
+ int i, j;
+
+ if (output == NULL) {
+ if (outlen == NULL)
+ return 0;
+
+ *outlen = KECCAK_SERIALIZATION_LEN;
+ return 1;
+ }
+
+ if (outlen != NULL && *outlen < KECCAK_SERIALIZATION_LEN)
+ return 0;
+
+ p = output;
+
+ /* Magic code */
+ memcpy(p, keccakmagic, KECCAKMAGIC_LEN);
+ p += KECCAKMAGIC_LEN;
+
+ /* Additional check data */
+ p = OPENSSL_store_u64_le(p, impl_id);
+ p = OPENSSL_store_u64_le(p, c->md_size);
+
+ /* A matrix */
+ for (i = 0; i < 5; i++) {
+ for (j = 0; j < 5; j++)
+ p = OPENSSL_store_u64_le(p, c->A[i][j]);
+ }
+
+ p = OPENSSL_store_u64_le(p, c->block_size);
+ p = OPENSSL_store_u64_le(p, c->bufsz);
+ p = OPENSSL_store_u64_le(p, c->pad);
+ p = OPENSSL_store_u64_le(p, c->xof_state);
+
+ if (outlen != NULL)
+ *outlen = KECCAK_SERIALIZATION_LEN;
+
+ /* buf */
+ memcpy(p, c->buf, sizeof(c->buf));
+
+ return 1;
+}
+
+static int KECCAK_Deserialize(KECCAK1600_CTX *c, int impl_id,
+ const unsigned char *input, size_t len)
+{
+ const unsigned char *p;
+ uint64_t val;
+ int i, j;
+
+ if (c == NULL || input == NULL || len != KECCAK_SERIALIZATION_LEN)
+ return 0;
+
+ /* Magic code */
+ if (memcmp(input, keccakmagic, KECCAKMAGIC_LEN) != 0)
+ return 0;
+
+ p = input + KECCAKMAGIC_LEN;
+
+ /* Check for matching Impl ID */
+ p = OPENSSL_load_u64_le(&val, p);
+ if (val != (uint64_t)impl_id)
+ return 0;
+
+ /* Check for matching md_size */
+ p = OPENSSL_load_u64_le(&val, p);
+ if (val != (uint64_t)c->md_size)
+ return 0;
+
+ /* A matrix */
+ for (i = 0; i < 5; i++) {
+ for (j = 0; j < 5; j++) {
+ p = OPENSSL_load_u64_le(&val, p);
+ c->A[i][j] = val;
+ }
+ }
+
+ p = OPENSSL_load_u64_le(&val, p);
+ c->block_size = (size_t)val;
+ p = OPENSSL_load_u64_le(&val, p);
+ c->bufsz = (size_t)val;
+ p = OPENSSL_load_u64_le(&val, p);
+ c->pad = (unsigned char)val;
+ p = OPENSSL_load_u64_le(&val, p);
+ c->xof_state = (int)val;
+
+ /* buf */
+ memcpy(c->buf, p, sizeof(c->buf));
+
+ return 1;
+}
+
+#define IMPLEMENT_SERIALIZE_FNS(name, id) \
+ static int name##_serialize(void *vctx, unsigned char *out, \
+ size_t *outlen) \
+ { \
+ return KECCAK_Serialize(vctx, id, out, outlen); \
+ } \
+ static int name##_deserialize(void *vctx, const unsigned char *in, \
+ size_t inlen) \
+ { \
+ return KECCAK_Deserialize(vctx, id, in, inlen); \
+ }
+
static const OSSL_PARAM *shake_gettable_ctx_params(ossl_unused void *ctx,
ossl_unused void *provctx)
{
@@ -624,30 +747,39 @@ static int shake_set_ctx_params(void *vctx, const OSSL_PARAM params[])
return 1;
}
-#define IMPLEMENT_SHA3_functions(bitlen) \
- SHA3_newctx(sha3, SHA3_##bitlen, sha3_##bitlen, bitlen, '\x06') \
- PROV_FUNC_SHA3_DIGEST(sha3_##bitlen, bitlen, \
- SHA3_BLOCKSIZE(bitlen), SHA3_MDSIZE(bitlen), \
- SHA3_FLAGS)
+#define KECCAK_SER_ID 0x010000
+#define SHAKE_SER_ID 0x020000
+#define SHA3_SER_ID 0x040000
+#define KMAK_SER_ID 0x080000
+
+#define IMPLEMENT_SHA3_functions(bitlen) \
+ SHA3_newctx(sha3, SHA3_##bitlen, sha3_##bitlen, bitlen, '\x06') \
+ IMPLEMENT_SERIALIZE_FNS(sha3_##bitlen, SHA3_SER_ID + bitlen) \
+ PROV_FUNC_SHA3_DIGEST(sha3_##bitlen, bitlen, \
+ SHA3_BLOCKSIZE(bitlen), SHA3_MDSIZE(bitlen), \
+ SHA3_FLAGS)
#define IMPLEMENT_KECCAK_functions(bitlen) \
SHA3_newctx(keccak, KECCAK_##bitlen, keccak_##bitlen, bitlen, '\x01') \
- PROV_FUNC_SHA3_DIGEST(keccak_##bitlen, bitlen, \
- SHA3_BLOCKSIZE(bitlen), SHA3_MDSIZE(bitlen), \
- SHA3_FLAGS)
-
-#define IMPLEMENT_SHAKE_functions(bitlen) \
- SHAKE_newctx(shake, SHAKE_##bitlen, shake_##bitlen, bitlen, \
- 0 /* no default md length */, '\x1f') \
- PROV_FUNC_SHAKE_DIGEST(shake_##bitlen, bitlen, \
- SHA3_BLOCKSIZE(bitlen), 0, \
- SHAKE_FLAGS)
-
-#define IMPLEMENT_KMAC_functions(bitlen) \
- KMAC_newctx(keccak_kmac_##bitlen, bitlen, '\x04') \
- PROV_FUNC_SHAKE_DIGEST(keccak_kmac_##bitlen, bitlen, \
- SHA3_BLOCKSIZE(bitlen), KMAC_MDSIZE(bitlen), \
- KMAC_FLAGS)
+ IMPLEMENT_SERIALIZE_FNS(keccak_##bitlen, KECCAK_SER_ID + bitlen) \
+ PROV_FUNC_SHA3_DIGEST(keccak_##bitlen, bitlen, \
+ SHA3_BLOCKSIZE(bitlen), SHA3_MDSIZE(bitlen), \
+ SHA3_FLAGS)
+
+#define IMPLEMENT_SHAKE_functions(bitlen) \
+ SHAKE_newctx(shake, SHAKE_##bitlen, shake_##bitlen, bitlen, \
+ 0 /* no default md length */, '\x1f') \
+ IMPLEMENT_SERIALIZE_FNS(shake_##bitlen, SHAKE_SER_ID + bitlen) \
+ PROV_FUNC_SHAKE_DIGEST(shake_##bitlen, bitlen, \
+ SHA3_BLOCKSIZE(bitlen), 0, \
+ SHAKE_FLAGS)
+
+#define IMPLEMENT_KMAC_functions(bitlen) \
+ KMAC_newctx(keccak_kmac_##bitlen, bitlen, '\x04') \
+ IMPLEMENT_SERIALIZE_FNS(keccak_kmac_##bitlen, KMAK_SER_ID + bitlen) \
+ PROV_FUNC_SHAKE_DIGEST(keccak_kmac_##bitlen, bitlen, \
+ SHA3_BLOCKSIZE(bitlen), KMAC_MDSIZE(bitlen), \
+ KMAC_FLAGS)
/* ossl_sha3_224_functions */
IMPLEMENT_SHA3_functions(224)
diff --git a/test/evp_extra_test2.c b/test/evp_extra_test2.c
index 5e278ae260..edff52975d 100644
--- a/test/evp_extra_test2.c
+++ b/test/evp_extra_test2.c
@@ -3433,7 +3433,9 @@ static int test_evp_md_ctx_serialize(int tstid)
{
static const char *algs[] = {
"SHA224", "SHA256", "SHA256-192",
- "SHA384", "SHA512", "SHA512-224", "SHA512-256"
+ "SHA384", "SHA512", "SHA512-224", "SHA512-256",
+ "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512",
+ "KECCAK-KMAC-128", "KECCAK-KMAC-256"
};
OSSL_LIB_CTX *ctx = NULL;
EVP_MD_CTX *mdctx1 = NULL, *mdctx2 = NULL;
@@ -3585,7 +3587,7 @@ int setup_tests(void)
ADD_TEST(test_evp_md_ctx_dup);
ADD_TEST(test_evp_md_ctx_copy);
ADD_TEST(test_evp_md_ctx_copy2);
- ADD_ALL_TESTS(test_evp_md_ctx_serialize, 7);
+ ADD_ALL_TESTS(test_evp_md_ctx_serialize, 13);
ADD_ALL_TESTS(test_provider_unload_effective, 2);
#if !defined OPENSSL_NO_DES && !defined OPENSSL_NO_MD5
ADD_TEST(test_evp_pbe_alg_add);