From 55e790c58976ce7de1208b90830ecd95619e43d4 Mon Sep 17 00:00:00 2001 From: Fabrice Benhamouda Date: Mon, 6 May 2024 11:06:53 -0400 Subject: [PATCH] Initial design for export of Merkle-Damgard hash state This commit provides a building block to implement HMAC with precomputed keys from https://github.com/aws/aws-lc/pull/1574. It adds internal functions to export the state of SHA-256 and use such exported state to initialize a SHA-256 context. Once the design is reviewed and approved, we will similar functions to the other Merkle-Damgard hash functions (MD4, MD5, SHA-1, SHA-2). --- crypto/digest_extra/digest_test.cc | 43 ++++++++++++++++++++ crypto/fipsmodule/sha/internal.h | 20 ++++++++++ crypto/fipsmodule/sha/sha256.c | 64 +++++++++++++++++++++++++++++- include/openssl/digest.h | 6 +++ include/openssl/sha.h | 8 ++++ 5 files changed, 140 insertions(+), 1 deletion(-) diff --git a/crypto/digest_extra/digest_test.cc b/crypto/digest_extra/digest_test.cc index 784db1ff797..0d3d7772f1d 100644 --- a/crypto/digest_extra/digest_test.cc +++ b/crypto/digest_extra/digest_test.cc @@ -476,3 +476,46 @@ TEST(DigestTest, TransformBlocks) { EXPECT_TRUE(0 == OPENSSL_memcmp(ctx1.h, ctx2.h, sizeof(ctx1.h))); } + +// FIXME: Need to implement this for all hash functions used by HMAC +TEST(DigestTest, InitAndGetStateSHA256) { + const size_t nb_blocks = 10; + const size_t block_size = SHA256_CBLOCK; + uint8_t data[block_size * nb_blocks]; + for (size_t i = 0; i < sizeof(data); i++) { + data[i] = i*3; + } + + // SHA-256 + + // Compute the hash of the data for the baseline + SHA256_CTX ctx1; + EXPECT_TRUE(SHA256_Init(&ctx1)); + EXPECT_TRUE(SHA256_Update(&ctx1, data, sizeof(data))); + uint8_t hash1[SHA256_DIGEST_LENGTH]; + EXPECT_TRUE(SHA256_Final(hash1, &ctx1)); + + // Compute it by stopping in the middle, getting the state, and restoring it + SHA256_CTX ctx2; + EXPECT_TRUE(SHA256_Init(&ctx2)); + EXPECT_TRUE(SHA256_Update(&ctx2, data, 1)); + uint8_t state_h[SHA256_DIGEST_LENGTH]; + uint64_t state_n; + // we should not be able to export the state before a full block + EXPECT_FALSE(SHA256_get_state(&ctx2, state_h, &state_n)); + // finish 2 blocks + EXPECT_TRUE(SHA256_Update(&ctx2, data+1, 2*block_size-1)); + // now we should be able to export the state + EXPECT_TRUE(SHA256_get_state(&ctx2, state_h, &state_n)); + // check that state_n corresponds to 2 blocks + EXPECT_EQ(2*block_size*8, state_n); + + // and we continue on a fresh new context + SHA256_CTX ctx3; + EXPECT_TRUE(SHA256_Init_from_state(&ctx3, state_h, state_n)); + EXPECT_TRUE(SHA256_Update(&ctx2, data+2*block_size, (nb_blocks-2)*block_size)); + uint8_t hash2[SHA256_DIGEST_LENGTH]; + EXPECT_TRUE(SHA256_Final(hash2, &ctx2)); + + EXPECT_EQ(Bytes(hash1), Bytes(hash2)); +} diff --git a/crypto/fipsmodule/sha/internal.h b/crypto/fipsmodule/sha/internal.h index caeac4a976f..b05a483a8b1 100644 --- a/crypto/fipsmodule/sha/internal.h +++ b/crypto/fipsmodule/sha/internal.h @@ -92,6 +92,26 @@ void sha512_block_data_order(uint64_t *state, const uint8_t *in, #define KECCAK1600_ASM #endif +// SHA256_Init_from_state is a low-level function that initializes |sha| with a +// custom state. |h| is the hash state in big endian. |n| is the number of bits +// processed at this point. It must be a multiple of |SHA256_CBLOCK*8|. +// It returns one on success and zero on programmer error. +// External users should never use directly this function. +OPENSSL_EXPORT int SHA256_Init_from_state( + SHA256_CTX *sha, const uint8_t h[SHA256_CHAINING_LENGTH], uint64_t n); + +// SHA256_get_state is a low-level function that exports the hash state in big +// endian into |out_n| and the number of bits processed at this point in +// |out_n|. SHA256_Final must not have been called before (otherwise results +// are not guaranteed). Furthermore, the number of bytes processed by +// SHA256_Update must be a multiple of the block length |SHA256_CBLOCK| +// (otherwise it fails). It returns one on success and zero on programmer +// error. +// External users should never use directly this function. +OPENSSL_EXPORT int SHA256_get_state(SHA256_CTX *ctx, + uint8_t out_h[SHA256_CHAINING_LENGTH], + uint64_t *out_n); + // SHA3_224 writes the digest of |len| bytes from |data| to |out| and returns |out|. // There must be at least |SHA3_224_DIGEST_LENGTH| bytes of space in |out|. // On failure |SHA3_224| returns NULL. diff --git a/crypto/fipsmodule/sha/sha256.c b/crypto/fipsmodule/sha/sha256.c index 38164535cf3..380935f8b4d 100644 --- a/crypto/fipsmodule/sha/sha256.c +++ b/crypto/fipsmodule/sha/sha256.c @@ -93,6 +93,41 @@ int SHA256_Init(SHA256_CTX *sha) { return 1; } +OPENSSL_STATIC_ASSERT(SHA256_CHAINING_LENGTH==SHA224_CHAINING_LENGTH, + sha256_and_sha224_have_same_chaining_length) + +// sha256_init_from_state_impl is the implementation of +// SHA256_Init_from_state and SHA224_Init_from_state +// Note that the state h is always SHA256_CHAINING_LENGTH-byte long +static int sha256_init_from_state_impl(SHA256_CTX *sha, int md_len, + const uint8_t h[SHA256_CHAINING_LENGTH], + uint64_t n) { + if(n % ((uint64_t) SHA256_CBLOCK * 8) != 0) { + // n is not a multiple of the block size in bits, so it fails + return 0; + } + + OPENSSL_memset(sha, 0, sizeof(SHA256_CTX)); + sha->md_len = md_len; + + const size_t out_words = SHA256_CHAINING_LENGTH / 4; + for (size_t i = 0; i < out_words; i++) { + sha->h[i] = CRYPTO_load_u32_be(h); + h += 4; + } + + sha->Nh = n >> 32; + sha->Nl = n & 0xffffffff; + + return 1; +} + +int SHA256_Init_from_state(SHA256_CTX *sha, + const uint8_t h[SHA256_CHAINING_LENGTH], + uint64_t n) { + return sha256_init_from_state_impl(sha, SHA256_DIGEST_LENGTH, h, n); +} + uint8_t *SHA224(const uint8_t *data, size_t len, uint8_t out[SHA224_DIGEST_LENGTH]) { // We have to verify that all the SHA services actually succeed before @@ -163,7 +198,7 @@ static int sha256_final_impl(uint8_t *out, size_t md_len, SHA256_CTX *c) { return 1; } -int SHA256_Final(uint8_t out[SHA256_DIGEST_LENGTH], SHA256_CTX *c) { +int SHA256_Final(uint8_t out[SHA256_CHAINING_LENGTH], SHA256_CTX *c) { return sha256_final_impl(out, SHA256_DIGEST_LENGTH, c); } @@ -171,6 +206,33 @@ int SHA224_Final(uint8_t out[SHA224_DIGEST_LENGTH], SHA256_CTX *ctx) { return sha256_final_impl(out, SHA224_DIGEST_LENGTH, ctx); } +// sha256_get_state_impl is the implementation of +// SHA256_get_state and SHA224_get_state +// Note that the state out_h is always SHA256_CHAINING_LENGTH-byte long +static int sha256_get_state_impl(SHA256_CTX *ctx, + uint8_t out_h[SHA256_CHAINING_LENGTH], + uint64_t *out_n) { + if (ctx->Nl % ((uint64_t)SHA256_CBLOCK * 8) != 0) { + // ctx->Nl is not a multiple of the block size in bits, so it fails + return 0; + } + + const size_t out_words = SHA256_CHAINING_LENGTH / 4; + for (size_t i = 0; i < out_words; i++) { + CRYPTO_store_u32_be(out_h, ctx->h[i]); + out_h += 4; + } + + *out_n = (((uint64_t)ctx->Nh) << 32) + ctx->Nl; + + return 1; +} + +int SHA256_get_state(SHA256_CTX *ctx, uint8_t out_h[SHA256_CHAINING_LENGTH], + uint64_t *out_n) { + return sha256_get_state_impl(ctx, out_h, out_n); +} + #ifndef SHA256_ASM static const uint32_t K256[64] = { 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, diff --git a/include/openssl/digest.h b/include/openssl/digest.h index 2dcd47b110f..7d6f058e186 100644 --- a/include/openssl/digest.h +++ b/include/openssl/digest.h @@ -171,6 +171,12 @@ OPENSSL_EXPORT int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data, // at least this much space. #define EVP_MAX_MD_SIZE 64 // SHA-512 is the longest so far. +// EVP_MAX_MD_CHAINING_LENGTH is the largest chaining length supported, in +// bytes. This constant is only for Merkle-Damgard-based hashed functions +// like SHA-1, SHA-2, and MD5. +// This constant is only used internally by HMAC. +#define EVP_MAX_MD_CHAINING_LENGTH 64 // SHA-512 has the longest chaining length so far + // EVP_MAX_MD_BLOCK_SIZE is the largest digest block size supported, in // bytes. #define EVP_MAX_MD_BLOCK_SIZE 128 // SHA-512 is the longest so far. diff --git a/include/openssl/sha.h b/include/openssl/sha.h index e1d0bd51781..7844fa3f0a2 100644 --- a/include/openssl/sha.h +++ b/include/openssl/sha.h @@ -112,6 +112,10 @@ struct sha_state_st { // SHA224_DIGEST_LENGTH is the length of a SHA-224 digest. #define SHA224_DIGEST_LENGTH 28 +// SHA224_CHAINING_LENGTH is the chaining length in bytes of SHA-256 +// It corresponds to the length in bytes of the h part of the state +#define SHA224_CHAINING_LENGTH 32 + // SHA224_Init initialises |sha| and returns 1. OPENSSL_EXPORT int SHA224_Init(SHA256_CTX *sha); @@ -139,6 +143,10 @@ OPENSSL_EXPORT uint8_t *SHA224(const uint8_t *data, size_t len, // SHA256_DIGEST_LENGTH is the length of a SHA-256 digest. #define SHA256_DIGEST_LENGTH 32 +// SHA256_CHAINING_LENGTH is the chaining length in bytes of SHA-256 +// It corresponds to the length in bytes of the h part of the state +#define SHA256_CHAINING_LENGTH 32 + // SHA256_Init initialises |sha| and returns 1. OPENSSL_EXPORT int SHA256_Init(SHA256_CTX *sha);