Hello,

We need to calculate SHA1 over a large sequence of messages. Related messages can arrive with huge intervals (hours, days worst case), including across program shutdown, and we can not keep old messages around to do the SHA1 all at once.

Our situation requires the ability to save the state of a SHA1 calculation in progress to a file or database, and reload it later to contiue with it.

Attached to this email is a patch against 0.9.8e that adds this ability to OpenSSL.
 cd openssl-0.9.8.e/crypto
 patch -p0 <openssl-sha-save-v1.patch

Use example:
   SHA1_Init();
   SHA1_Update();
   SHA1_Save_State(VERSION_DONT_CARE);
SHA1_Drop(); // Application must call this if it doesn't do SHA1_Final on a context
   ...
   anything, including program shutdown
   ...
   SHA1_Load_State();
   SHA1_Update();
   SHA1_Final();

We think that some other users may encounter a situation similar to ours, and we therefore submit this patch for inclusion.

If the patch is not rejected outright, I am willing to modify the patch to make it eligible for inclusion. E.g. add the documentation, any cleanups, etc.

   Regards,
   Nanno

diff -Nru ../../openssl-0.9.8e/crypto/sha/sha.h sha/sha.h
--- ../../openssl-0.9.8e/crypto/sha/sha.h	2006-12-22 17:04:56.000000000 +0100
+++ sha/sha.h	2007-06-08 01:47:07.203240896 +0200
@@ -97,6 +97,9 @@
 #define SHA_LAST_BLOCK  (SHA_CBLOCK-8)
 #define SHA_DIGEST_LENGTH 20
 
+#define SHA_STATE_VERSION_DONT_CARE	0
+#define SHA_STATE_VERSION_1		1
+
 typedef struct SHAstate_st
 	{
 	SHA_LONG h0,h1,h2,h3,h4;
@@ -118,6 +121,9 @@
 int SHA1_Final(unsigned char *md, SHA_CTX *c);
 unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md);
 void SHA1_Transform(SHA_CTX *c, const unsigned char *data);
+void SHA1_Save_State(SHA_CTX *c, unsigned char **data, unsigned int *len, int requested_version);
+int SHA1_Load_State(SHA_CTX *c, unsigned char *data, unsigned int len);
+void SHA1_Drop(SHA_CTX *c);
 #endif
 
 #define SHA256_CBLOCK	(SHA_LBLOCK*4)	/* SHA-256 treats input data as a
diff -Nru ../../openssl-0.9.8e/crypto/sha/sha_locl.h sha/sha_locl.h
--- ../../openssl-0.9.8e/crypto/sha/sha_locl.h	2005-07-20 01:10:04.000000000 +0200
+++ sha/sha_locl.h	2007-06-08 02:36:44.043692360 +0200
@@ -58,6 +58,8 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <stdint.h>		// For uint32_t
+#include <netinet/in.h>		// For htonl/ntohl
 
 #include <openssl/opensslconf.h>
 #include <openssl/sha.h>
@@ -603,3 +605,93 @@
 #endif
 
 #endif
+
+
+#if defined(SHA_1)
+#define memcat_htobe32(dst, src)	{ *(uint32_t*)(dst) = htonl(src); (dst) += sizeof(uint32_t); }
+#define memget_betoh32(dst, src)	{ (dst) = ntohl( *((uint32_t*)(src)) ); (src) += sizeof(uint32_t); }
+
+#define SHA_STATE_V1_SIZE		((1 + 5 + 2 + SHA_LBLOCK + 1) * sizeof(uint32_t))
+
+void SHA1_Save_State (SHA_CTX *c, unsigned char **data, unsigned int *len, int requested_version)
+	{
+	unsigned char *p;
+	int i;
+
+	switch (requested_version)
+		{
+	case SHA_STATE_VERSION_DONT_CARE:
+		requested_version = SHA_STATE_VERSION_1;
+		break;
+	case SHA_STATE_VERSION_1:
+		break;
+	default:
+		*data = NULL;
+		return;
+		}
+
+	*len = SHA_STATE_V1_SIZE;
+	*data = malloc(*len);
+	if (*data == NULL) {
+		return;
+	}
+
+	p = *data;
+
+	memcat_htobe32(p, requested_version);
+
+	memcat_htobe32(p, c->h0);
+	memcat_htobe32(p, c->h1);
+	memcat_htobe32(p, c->h2);
+	memcat_htobe32(p, c->h3);
+	memcat_htobe32(p, c->h4);
+
+	memcat_htobe32(p, c->Nl);
+	memcat_htobe32(p, c->Nh);
+
+	for (i=0; i<SHA_LBLOCK; i++)
+		{
+		memcat_htobe32(p, c->data[i]);
+		}
+
+	memcat_htobe32(p, c->num);
+	}
+
+int SHA1_Load_State (SHA_CTX *c, unsigned char *data, unsigned int len)
+	{
+	int version, i;
+
+	if (len < sizeof(uint32_t))
+		return -1;
+
+	memget_betoh32(version, data);
+	if (version != SHA_STATE_VERSION_1)
+		return -2;
+	if (len < SHA_STATE_V1_SIZE)
+		return -3;
+
+	memget_betoh32(c->h0, data);
+	memget_betoh32(c->h1, data);
+	memget_betoh32(c->h2, data);
+	memget_betoh32(c->h3, data);
+	memget_betoh32(c->h4, data);
+
+	memget_betoh32(c->Nl, data);
+	memget_betoh32(c->Nh, data);
+
+	for (i=0; i<SHA_LBLOCK; i++)
+		{
+		memget_betoh32(c->data[i], data);
+		}
+
+	memget_betoh32(c->num, data);
+
+	return 1;
+	}
+
+void SHA1_Drop (SHA_CTX *c)
+	{
+	// There is no dynamically allocated memory to clean up.
+	}
+#endif
+
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <openssl/sha.h>

int main()
{
	SHA_CTX		a, b;
	uint8_t		digest_a[SHA_DIGEST_LENGTH], digest_b[SHA_DIGEST_LENGTH];
	uint8_t		data[67];
	bool		equal;
	unsigned char	*state;
	unsigned int	state_len;
	int		i, cut, ret;

	equal = true;

	for (i=0; i<sizeof(data); i++)
		data[i] = i;

	for (cut=0; cut<sizeof(data); cut++) {
		SHA1_Init(&a);
		memset(&b, 0, sizeof(b));

		for (i=0; i<sizeof(data); i++) {
			if (i == cut) {
				SHA1_Save_State(&a, &state, &state_len, SHA_STATE_VERSION_DONT_CARE);
				if (state == NULL) {
					printf("Error!\n");
					return 1;
				}

				ret = SHA1_Load_State(&b, state, state_len);
				if (ret != 1) {
					printf("Error!\n");
					return 1;
				}
			}

			SHA1_Update(&a, data, i);
			if (i >= cut)
				SHA1_Update(&b, data, i);
		}

		SHA1_Final(digest_a, &a);
		SHA1_Final(digest_b, &b);

		if (memcmp(digest_a, digest_b, SHA_DIGEST_LENGTH) != 0)
			equal = false;
	}

	if ( ! equal)
		printf("not ");
	printf("equal!\n");

	return 0;
}

Reply via email to