Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial design for export of Merkle-Damgard hash state #1577

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions crypto/digest_extra/digest_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
31 changes: 31 additions & 0 deletions crypto/fipsmodule/sha/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@
extern "C" {
#endif

// Internal SHA2 constants

// SHA224_CHAINING_LENGTH is the chaining length in bytes of SHA-224
// It corresponds to the length in bytes of the h part of the state
#define SHA224_CHAINING_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


// SHA3 constants, from NIST FIPS202.
// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
#define SHA3_ROWS 5
Expand Down Expand Up @@ -92,6 +103,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.
Expand Down
64 changes: 63 additions & 1 deletion crypto/fipsmodule/sha/sha256.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -163,14 +198,41 @@ 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);
}

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,
Expand Down
6 changes: 6 additions & 0 deletions include/openssl/digest.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading