mirror of
https://github.com/openssl/openssl.git
synced 2026-05-07 20:12:39 +00:00
Add serialization for SHA-2 digest contexts
This commit introduces the ability to serialize and deserialize the internal state of SHA-2 digest contexts (SHA-256 and SHA-512 families). This functionality is exposed via the new OSSL_DIGEST_SERIALIZATION parameter, which can be used with EVP_MD_CTX_get_params() to retrieve the state and with EVP_DigestInit_ex2() to restore it into a new context. This allows an application to save the state of a hash operation and resume it later, which is useful for process migration or for saving the state of long- unning computations. A new test case has been added to verify this. 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)
This commit is contained in:
committed by
Dmitry Belyavskiy
parent
c1f66c1ec3
commit
1afb05b603
@@ -150,6 +150,7 @@ StatementMacros:
|
||||
- "IMPLEMENT_DIGEST"
|
||||
- "IMPLEMENT_digest_functions"
|
||||
- "IMPLEMENT_digest_functions_with_settable_ctx"
|
||||
- "IMPLEMENT_digest_functions_with_serialize"
|
||||
- "IMPLEMENT_dtls1_meth_func"
|
||||
- "IMPLEMENT_DYNAMIC_BIND_FN"
|
||||
- "IMPLEMENT_DYNAMIC_CHECK_FN"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
*/
|
||||
#include "internal/deprecated.h"
|
||||
|
||||
#include <openssl/byteorder.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/core_dispatch.h>
|
||||
#include <openssl/evp.h>
|
||||
@@ -56,6 +57,216 @@ static int sha1_set_ctx_params(void *vctx, const OSSL_PARAM params[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const unsigned char sha256magic[] = "SHA256v1";
|
||||
#define SHA256MAGIC_LEN (sizeof(sha256magic) - 1)
|
||||
#define SHA256_SERIALIZATION_LEN \
|
||||
( \
|
||||
SHA256MAGIC_LEN /* magic */ \
|
||||
+ sizeof(uint32_t) /* c->md_len */ \
|
||||
+ sizeof(uint32_t) * 8 /* c->h */ \
|
||||
+ sizeof(uint32_t) * 2 /* c->Nl + c->Nh */ \
|
||||
+ sizeof(uint32_t) * SHA_LBLOCK /* c->data */ \
|
||||
+ sizeof(uint32_t) /* c->num */ \
|
||||
)
|
||||
|
||||
static int SHA256_Serialize(SHA256_CTX *c, unsigned char *out,
|
||||
size_t *outlen)
|
||||
{
|
||||
unsigned char *p;
|
||||
unsigned long i;
|
||||
|
||||
if (out == NULL) {
|
||||
if (outlen == NULL)
|
||||
return 0;
|
||||
|
||||
*outlen = SHA256_SERIALIZATION_LEN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (outlen != NULL && *outlen < SHA256_SERIALIZATION_LEN)
|
||||
return 0;
|
||||
|
||||
p = out;
|
||||
|
||||
/* Magic code */
|
||||
memcpy(p, sha256magic, SHA256MAGIC_LEN);
|
||||
p += SHA256MAGIC_LEN;
|
||||
|
||||
/* md_len */
|
||||
p = OPENSSL_store_u32_le(p, c->md_len);
|
||||
|
||||
/* h */
|
||||
for (i = 0; i < sizeof(c->h) / sizeof(SHA_LONG); i++)
|
||||
p = OPENSSL_store_u32_le(p, c->h[i]);
|
||||
|
||||
/* Nl, Nh */
|
||||
p = OPENSSL_store_u32_le(p, c->Nl);
|
||||
p = OPENSSL_store_u32_le(p, c->Nh);
|
||||
|
||||
/* data */
|
||||
for (i = 0; i < SHA_LBLOCK; i++)
|
||||
p = OPENSSL_store_u32_le(p, c->data[i]);
|
||||
|
||||
/* num */
|
||||
p = OPENSSL_store_u32_le(p, c->num);
|
||||
|
||||
if (outlen != NULL)
|
||||
*outlen = SHA256_SERIALIZATION_LEN;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int SHA256_Deserialize(SHA256_CTX *c, const unsigned char *in,
|
||||
size_t inlen)
|
||||
{
|
||||
const unsigned char *p;
|
||||
uint32_t val;
|
||||
unsigned long i;
|
||||
|
||||
if (c == NULL || in == NULL || inlen != SHA256_SERIALIZATION_LEN)
|
||||
return 0;
|
||||
|
||||
/* Magic code check */
|
||||
if (memcmp(in, sha256magic, SHA256MAGIC_LEN) != 0)
|
||||
return 0;
|
||||
|
||||
p = in + SHA256MAGIC_LEN;
|
||||
|
||||
/* md_len check */
|
||||
p = OPENSSL_load_u32_le(&val, p);
|
||||
if ((unsigned int)val != c->md_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* h */
|
||||
for (i = 0; i < (sizeof(c->h) / sizeof(SHA_LONG)); i++) {
|
||||
p = OPENSSL_load_u32_le(&val, p);
|
||||
c->h[i] = (SHA_LONG)val;
|
||||
}
|
||||
|
||||
/* Nl, Nh */
|
||||
p = OPENSSL_load_u32_le(&val, p);
|
||||
c->Nl = (SHA_LONG)val;
|
||||
p = OPENSSL_load_u32_le(&val, p);
|
||||
c->Nh = (SHA_LONG)val;
|
||||
|
||||
/* data */
|
||||
for (i = 0; i < SHA_LBLOCK; i++) {
|
||||
p = OPENSSL_load_u32_le(&val, p);
|
||||
c->data[i] = (SHA_LONG)val;
|
||||
}
|
||||
|
||||
/* num */
|
||||
p = OPENSSL_load_u32_le(&val, p);
|
||||
c->num = (unsigned int)val;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const unsigned char sha512magic[] = "SHA512v1";
|
||||
#define SHA512MAGIC_LEN (sizeof(sha512magic) - 1)
|
||||
#define SHA512_SERIALIZATION_LEN \
|
||||
( \
|
||||
SHA512MAGIC_LEN /* magic */ \
|
||||
+ sizeof(uint32_t) /* c->md_len */ \
|
||||
+ sizeof(uint64_t) * 8 /* c->h */ \
|
||||
+ sizeof(uint64_t) * 2 /* c->Nl + c->Nh */ \
|
||||
+ SHA512_CBLOCK /* c->u.d/c->u.p */ \
|
||||
+ sizeof(uint32_t) /* c->num */ \
|
||||
)
|
||||
|
||||
static int SHA512_Serialize(SHA512_CTX *c, unsigned char *out,
|
||||
size_t *outlen)
|
||||
{
|
||||
unsigned char *p;
|
||||
unsigned long i;
|
||||
|
||||
if (out == NULL) {
|
||||
if (outlen == NULL)
|
||||
return 0;
|
||||
|
||||
*outlen = SHA512_SERIALIZATION_LEN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (outlen != NULL && *outlen < SHA512_SERIALIZATION_LEN)
|
||||
return 0;
|
||||
|
||||
p = out;
|
||||
|
||||
/* Magic code */
|
||||
memcpy(p, sha512magic, SHA512MAGIC_LEN);
|
||||
p += SHA512MAGIC_LEN;
|
||||
|
||||
/* md_len */
|
||||
p = OPENSSL_store_u32_le(p, c->md_len);
|
||||
|
||||
/* h */
|
||||
for (i = 0; i < sizeof(c->h) / sizeof(SHA_LONG64); i++)
|
||||
p = OPENSSL_store_u64_le(p, c->h[i]);
|
||||
|
||||
/* Nl, Nh */
|
||||
p = OPENSSL_store_u64_le(p, c->Nl);
|
||||
p = OPENSSL_store_u64_le(p, c->Nh);
|
||||
|
||||
/* data */
|
||||
memcpy(p, c->u.p, SHA512_CBLOCK);
|
||||
p += SHA512_CBLOCK;
|
||||
|
||||
/* num */
|
||||
p = OPENSSL_store_u32_le(p, c->num);
|
||||
|
||||
if (outlen != NULL)
|
||||
*outlen = SHA512_SERIALIZATION_LEN;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int SHA512_Deserialize(SHA512_CTX *c, const unsigned char *in,
|
||||
size_t inlen)
|
||||
{
|
||||
const unsigned char *p;
|
||||
uint32_t val32;
|
||||
uint64_t val;
|
||||
unsigned long i;
|
||||
|
||||
if (c == NULL || in == NULL || inlen != SHA512_SERIALIZATION_LEN)
|
||||
return 0;
|
||||
|
||||
/* Magic code */
|
||||
if (memcmp(in, sha512magic, SHA512MAGIC_LEN) != 0)
|
||||
return 0;
|
||||
|
||||
p = in + SHA512MAGIC_LEN;
|
||||
|
||||
/* md_len check */
|
||||
p = OPENSSL_load_u32_le(&val32, p);
|
||||
if ((unsigned int)val32 != c->md_len)
|
||||
return 0;
|
||||
|
||||
/* h */
|
||||
for (i = 0; i < (sizeof(c->h) / sizeof(SHA_LONG64)); i++) {
|
||||
p = OPENSSL_load_u64_le(&val, p);
|
||||
c->h[i] = (SHA_LONG64)val;
|
||||
}
|
||||
|
||||
/* Nl, Nh */
|
||||
p = OPENSSL_load_u64_le(&val, p);
|
||||
c->Nl = (SHA_LONG64)val;
|
||||
p = OPENSSL_load_u64_le(&val, p);
|
||||
c->Nh = (SHA_LONG64)val;
|
||||
|
||||
/* data */
|
||||
memcpy(c->u.p, p, SHA512_CBLOCK);
|
||||
p += SHA512_CBLOCK;
|
||||
|
||||
/* num */
|
||||
p = OPENSSL_load_u32_le(&val32, p);
|
||||
c->num = (unsigned int)val32;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ossl_sha1_functions */
|
||||
IMPLEMENT_digest_functions_with_settable_ctx(
|
||||
sha1, SHA_CTX, SHA_CBLOCK, SHA_DIGEST_LENGTH, SHA2_FLAGS,
|
||||
@@ -63,34 +274,48 @@ IMPLEMENT_digest_functions_with_settable_ctx(
|
||||
sha1_settable_ctx_params, sha1_set_ctx_params)
|
||||
|
||||
/* ossl_sha224_functions */
|
||||
IMPLEMENT_digest_functions(sha224, SHA256_CTX,
|
||||
SHA256_CBLOCK, SHA224_DIGEST_LENGTH, SHA2_FLAGS,
|
||||
SHA224_Init, SHA224_Update, SHA224_Final)
|
||||
IMPLEMENT_digest_functions_with_serialize(sha224, SHA256_CTX,
|
||||
SHA256_CBLOCK, SHA224_DIGEST_LENGTH,
|
||||
SHA2_FLAGS, SHA224_Init,
|
||||
SHA224_Update, SHA224_Final,
|
||||
SHA256_Serialize, SHA256_Deserialize)
|
||||
|
||||
/* ossl_sha256_functions */
|
||||
IMPLEMENT_digest_functions(sha256, SHA256_CTX,
|
||||
SHA256_CBLOCK, SHA256_DIGEST_LENGTH, SHA2_FLAGS,
|
||||
SHA256_Init, SHA256_Update, SHA256_Final)
|
||||
IMPLEMENT_digest_functions_with_serialize(sha256, SHA256_CTX,
|
||||
SHA256_CBLOCK, SHA256_DIGEST_LENGTH,
|
||||
SHA2_FLAGS, SHA256_Init,
|
||||
SHA256_Update, SHA256_Final,
|
||||
SHA256_Serialize, SHA256_Deserialize)
|
||||
/* ossl_sha256_192_internal_functions */
|
||||
IMPLEMENT_digest_functions(sha256_192_internal, SHA256_CTX,
|
||||
SHA256_CBLOCK, SHA256_192_DIGEST_LENGTH, SHA2_FLAGS,
|
||||
ossl_sha256_192_init, SHA256_Update, SHA256_Final)
|
||||
IMPLEMENT_digest_functions_with_serialize(sha256_192_internal, SHA256_CTX,
|
||||
SHA256_CBLOCK, SHA256_192_DIGEST_LENGTH,
|
||||
SHA2_FLAGS, ossl_sha256_192_init,
|
||||
SHA256_Update, SHA256_Final,
|
||||
SHA256_Serialize, SHA256_Deserialize)
|
||||
/* ossl_sha384_functions */
|
||||
IMPLEMENT_digest_functions(sha384, SHA512_CTX,
|
||||
SHA512_CBLOCK, SHA384_DIGEST_LENGTH, SHA2_FLAGS,
|
||||
SHA384_Init, SHA384_Update, SHA384_Final)
|
||||
IMPLEMENT_digest_functions_with_serialize(sha384, SHA512_CTX,
|
||||
SHA512_CBLOCK, SHA384_DIGEST_LENGTH,
|
||||
SHA2_FLAGS, SHA384_Init,
|
||||
SHA384_Update, SHA384_Final,
|
||||
SHA512_Serialize, SHA512_Deserialize)
|
||||
|
||||
/* ossl_sha512_functions */
|
||||
IMPLEMENT_digest_functions(sha512, SHA512_CTX,
|
||||
SHA512_CBLOCK, SHA512_DIGEST_LENGTH, SHA2_FLAGS,
|
||||
SHA512_Init, SHA512_Update, SHA512_Final)
|
||||
IMPLEMENT_digest_functions_with_serialize(sha512, SHA512_CTX,
|
||||
SHA512_CBLOCK, SHA512_DIGEST_LENGTH,
|
||||
SHA2_FLAGS, SHA512_Init,
|
||||
SHA512_Update, SHA512_Final,
|
||||
SHA512_Serialize, SHA512_Deserialize)
|
||||
|
||||
/* ossl_sha512_224_functions */
|
||||
IMPLEMENT_digest_functions(sha512_224, SHA512_CTX,
|
||||
SHA512_CBLOCK, SHA224_DIGEST_LENGTH, SHA2_FLAGS,
|
||||
sha512_224_init, SHA512_Update, SHA512_Final)
|
||||
IMPLEMENT_digest_functions_with_serialize(sha512_224, SHA512_CTX,
|
||||
SHA512_CBLOCK, SHA224_DIGEST_LENGTH,
|
||||
SHA2_FLAGS, sha512_224_init,
|
||||
SHA512_Update, SHA512_Final,
|
||||
SHA512_Serialize, SHA512_Deserialize)
|
||||
|
||||
/* ossl_sha512_256_functions */
|
||||
IMPLEMENT_digest_functions(sha512_256, SHA512_CTX,
|
||||
SHA512_CBLOCK, SHA256_DIGEST_LENGTH, SHA2_FLAGS,
|
||||
sha512_256_init, SHA512_Update, SHA512_Final)
|
||||
IMPLEMENT_digest_functions_with_serialize(sha512_256, SHA512_CTX,
|
||||
SHA512_CBLOCK, SHA256_DIGEST_LENGTH,
|
||||
SHA2_FLAGS, sha512_256_init,
|
||||
SHA512_Update, SHA512_Final,
|
||||
SHA512_Serialize, SHA512_Deserialize)
|
||||
|
||||
@@ -126,6 +126,21 @@ extern "C" {
|
||||
{ OSSL_FUNC_DIGEST_SET_CTX_PARAMS, (void (*)(void))set_ctx_params }, \
|
||||
PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END
|
||||
|
||||
#define IMPLEMENT_digest_functions_with_serialize( \
|
||||
name, CTX, blksize, dgstsize, flags, init, upd, fin, \
|
||||
serialize, deserialize) \
|
||||
static OSSL_FUNC_digest_init_fn name##_internal_init; \
|
||||
static int name##_internal_init(void *ctx, const OSSL_PARAM params[]) \
|
||||
{ \
|
||||
return ossl_prov_is_running() && init(ctx); \
|
||||
} \
|
||||
PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_START(name, CTX, blksize, dgstsize, flags, \
|
||||
upd, fin), \
|
||||
{ OSSL_FUNC_DIGEST_INIT, (void (*)(void))name##_internal_init }, \
|
||||
{ OSSL_FUNC_DIGEST_SERIALIZE, (void (*)(void))serialize }, \
|
||||
{ OSSL_FUNC_DIGEST_DESERIALIZE, (void (*)(void))deserialize }, \
|
||||
PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END
|
||||
|
||||
const OSSL_PARAM *ossl_digest_default_gettable_params(void *provctx);
|
||||
int ossl_digest_default_get_params(OSSL_PARAM params[], size_t blksz,
|
||||
size_t paramsz, unsigned long flags);
|
||||
|
||||
@@ -3429,6 +3429,67 @@ end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_evp_md_ctx_serialize(int tstid)
|
||||
{
|
||||
static const char *algs[] = {
|
||||
"SHA224", "SHA256", "SHA256-192",
|
||||
"SHA384", "SHA512", "SHA512-224", "SHA512-256"
|
||||
};
|
||||
OSSL_LIB_CTX *ctx = NULL;
|
||||
EVP_MD_CTX *mdctx1 = NULL, *mdctx2 = NULL;
|
||||
EVP_MD *md = NULL;
|
||||
unsigned char *buf = NULL;
|
||||
size_t buflen;
|
||||
unsigned char d1[EVP_MAX_MD_SIZE], d2[EVP_MAX_MD_SIZE];
|
||||
unsigned int d1_len, d2_len;
|
||||
int ret = 0;
|
||||
const char *data1 = "some data";
|
||||
const char *data2 = "some more data";
|
||||
|
||||
if (!TEST_ptr(ctx = OSSL_LIB_CTX_new())
|
||||
|| !TEST_ptr(md = EVP_MD_fetch(ctx, algs[tstid], NULL)))
|
||||
goto end;
|
||||
|
||||
mdctx1 = EVP_MD_CTX_new();
|
||||
mdctx2 = EVP_MD_CTX_new();
|
||||
|
||||
/* Initiate a digest with data */
|
||||
if (!TEST_ptr(mdctx2) || !TEST_ptr(mdctx1)
|
||||
|| !TEST_true(EVP_DigestInit_ex2(mdctx1, md, NULL))
|
||||
|| !TEST_true(EVP_DigestUpdate(mdctx1, data1, strlen(data1))))
|
||||
goto end;
|
||||
|
||||
/* Get required buffer size and serialize */
|
||||
if (!TEST_true(EVP_MD_CTX_serialize(mdctx1, NULL, &buflen))
|
||||
|| !TEST_ptr(buf = OPENSSL_malloc(buflen))
|
||||
|| !TEST_true(EVP_MD_CTX_serialize(mdctx1, buf, &buflen)))
|
||||
goto end;
|
||||
|
||||
/* Deserialize */
|
||||
if (!TEST_true(EVP_DigestInit_ex2(mdctx2, md, NULL))
|
||||
|| !TEST_true(EVP_MD_CTX_deserialize(mdctx2, buf, buflen)))
|
||||
goto end;
|
||||
|
||||
/* Test that updating in parallel will now yield the same values */
|
||||
if (!TEST_true(EVP_DigestUpdate(mdctx1, data2, strlen(data2)))
|
||||
|| !TEST_true(EVP_DigestUpdate(mdctx2, data2, strlen(data2)))
|
||||
|| !TEST_true(EVP_DigestFinal_ex(mdctx1, d1, &d1_len))
|
||||
|| !TEST_true(EVP_DigestFinal_ex(mdctx2, d2, &d2_len))
|
||||
|| !TEST_uint_eq(d1_len, d2_len)
|
||||
|| !TEST_mem_eq(d1, d1_len, d2, d2_len))
|
||||
goto end;
|
||||
|
||||
ret = 1;
|
||||
|
||||
end:
|
||||
OPENSSL_free(buf);
|
||||
EVP_MD_CTX_free(mdctx1);
|
||||
EVP_MD_CTX_free(mdctx2);
|
||||
EVP_MD_free(md);
|
||||
OSSL_LIB_CTX_free(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if !defined OPENSSL_NO_DES && !defined OPENSSL_NO_MD5
|
||||
static int test_evp_pbe_alg_add(void)
|
||||
{
|
||||
@@ -3524,6 +3585,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_provider_unload_effective, 2);
|
||||
#if !defined OPENSSL_NO_DES && !defined OPENSSL_NO_MD5
|
||||
ADD_TEST(test_evp_pbe_alg_add);
|
||||
|
||||
Reference in New Issue
Block a user