From 1a92834490cda1352a46eaef02a4e7ff76ab94e3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Mon, 4 Apr 2011 22:51:49 +0200
Subject: Make SHA1 stateful.

Resolves: #814

diff --git a/Swiften/StringCodecs/SHA1.cpp b/Swiften/StringCodecs/SHA1.cpp
index 9882f70..a78a7b0 100644
--- a/Swiften/StringCodecs/SHA1.cpp
+++ b/Swiften/StringCodecs/SHA1.cpp
@@ -4,10 +4,14 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
+#include <Swiften/StringCodecs/SHA1.h>
+
 #include "Swiften/Base/Platform.h"
 
 #pragma GCC diagnostic ignored "-Wold-style-cast"
 
+using namespace Swift;
+
 /*
 SHA-1 in C
 By Steve Reid <steve@edmweb.com>
@@ -25,21 +29,9 @@ A million repetitions of "a"
 /* #define LITTLE_ENDIAN * This should be #define'd if true. */
 /* #define SHA1HANDSOFF * Copies data before messing with it. */
 
-#include <boost/cstdint.hpp>
 #include <stdio.h>
 #include <string.h>
 
-typedef struct {
-		boost::uint32_t state[5];
-		boost::uint32_t count[2];
-		boost::uint8_t buffer[64];
-} SHA1_CTX;
-
-void SHA1Transform(boost::uint32_t state[5], boost::uint8_t buffer[64]);
-void SHA1Init(SHA1_CTX* context);
-void SHA1Update(SHA1_CTX* context, boost::uint8_t* data, unsigned int len);
-void SHA1Final(boost::uint8_t digest[20], SHA1_CTX* context);
-
 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
 
 /* blk0() and blk() perform the initial expand. */
@@ -63,7 +55,7 @@ void SHA1Final(boost::uint8_t digest[20], SHA1_CTX* context);
 
 /* Hash a single 512-bit block. This is the core of the algorithm. */
 
-void SHA1Transform(boost::uint32_t state[5], boost::uint8_t buffer[64])
+void SHA1::Transform(boost::uint32_t state[5], boost::uint8_t buffer[64])
 {
 boost::uint32_t a, b, c, d, e;
 typedef union {
@@ -118,7 +110,7 @@ static boost::uint8_t workspace[64];
 
 /* SHA1Init - Initialize new context */
 
-void SHA1Init(SHA1_CTX* context)
+void SHA1::Init(SHA1::CTX* context)
 {
 		/* SHA1 initialization constants */
 		context->state[0] = 0x67452301;
@@ -132,7 +124,7 @@ void SHA1Init(SHA1_CTX* context)
 
 /* Run your data through this. */
 
-void SHA1Update(SHA1_CTX* context, boost::uint8_t* data, unsigned int len)
+void SHA1::Update(SHA1::CTX* context, boost::uint8_t* data, unsigned int len)
 {
 unsigned int i, j;
 
@@ -141,9 +133,9 @@ unsigned int i, j;
 		context->count[1] += (len >> 29);
 		if ((j + len) > 63) {
 				memcpy(&context->buffer[j], data, (i = 64-j));
-				SHA1Transform(context->state, context->buffer);
+				Transform(context->state, context->buffer);
 				for ( ; i + 63 < len; i += 64) {
-						SHA1Transform(context->state, &data[i]);
+						Transform(context->state, &data[i]);
 				}
 				j = 0;
 		}
@@ -154,7 +146,7 @@ unsigned int i, j;
 
 /* Add padding and return the message digest. */
 
-void SHA1Final(boost::uint8_t digest[20], SHA1_CTX* context)
+void SHA1::Final(boost::uint8_t digest[20], SHA1::CTX* context)
 {
 boost::uint32_t i, j;
 boost::uint8_t finalcount[8];
@@ -163,11 +155,11 @@ boost::uint8_t finalcount[8];
 				finalcount[i] = (boost::uint8_t) ((context->count[(i >= 4 ? 0 : 1)]
 				 >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
 		}
-		SHA1Update(context, (boost::uint8_t *)("\200"), 1);
+		Update(context, (boost::uint8_t *)("\200"), 1);
 		while ((context->count[0] & 504) != 448) {
-				SHA1Update(context, (boost::uint8_t *)("\0"), 1);
+				Update(context, (boost::uint8_t *)("\0"), 1);
 		}
-		SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
+		Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
 		for (i = 0; i < 20; i++) {
 				digest[i] = (boost::uint8_t)
 				 ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
@@ -179,24 +171,43 @@ boost::uint8_t finalcount[8];
 		memset(context->count, 0, 8);
 		memset(&finalcount, 0, 8);
 #ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */
-		SHA1Transform(context->state, context->buffer);
+		Transform(context->state, context->buffer);
 #endif
 }
 
 // -----------------------------------------------------------------------------
 
-#include "Swiften/StringCodecs/SHA1.h"
-
 namespace Swift {
 
+SHA1::SHA1() {
+	Init(&context);
+}
+
+SHA1& SHA1::update(const std::vector<unsigned char>& input) {
+	std::vector<unsigned char> inputCopy(input);
+	Update(&context, (boost::uint8_t*) &inputCopy[0], inputCopy.size());
+	return *this;
+}
+
+std::vector<unsigned char> SHA1::getHash() const {
+	std::vector<unsigned char> digest;
+	digest.resize(20);
+	CTX contextCopy(context);
+	Final((boost::uint8_t*) &digest[0], &contextCopy);
+	return digest;
+}
+
 ByteArray SHA1::getHash(const ByteArray& input) {
-	ByteArray inputCopy(input);
+	CTX context;
+	Init(&context);
+
+	std::vector<unsigned char> inputCopy(input.getVector());
+	Update(&context, (boost::uint8_t*) &inputCopy[0], inputCopy.size());
+
 	ByteArray digest;
 	digest.resize(20);
-	SHA1_CTX context;
-	SHA1Init(&context);
-	SHA1Update(&context, (boost::uint8_t*) inputCopy.getData(), inputCopy.getSize());
-	SHA1Final((boost::uint8_t*) digest.getData(), &context);
+	Final((boost::uint8_t*) digest.getData(), &context);
+
 	return digest;
 }
 
diff --git a/Swiften/StringCodecs/SHA1.h b/Swiften/StringCodecs/SHA1.h
index fc5ba0e..9c0232a 100644
--- a/Swiften/StringCodecs/SHA1.h
+++ b/Swiften/StringCodecs/SHA1.h
@@ -6,11 +6,38 @@
 
 #pragma once
 
+#include <vector>
+#include <boost/cstdint.hpp>
+
 #include "Swiften/Base/ByteArray.h"
 
 namespace Swift {
 	class SHA1 {
 		public:
+			SHA1();
+
+			SHA1& update(const std::vector<unsigned char>& data);
+			std::vector<unsigned char> getHash() const;
+
+			/**
+			 * Equivalent of:
+			 *	SHA1().update(data),getHash(), but slightly more efficient and
+			 *	convenient.
+			 */
 			static ByteArray getHash(const ByteArray& data);
+
+		private:
+			typedef struct {
+					boost::uint32_t state[5];
+					boost::uint32_t count[2];
+					boost::uint8_t buffer[64];
+			} CTX;
+			static void Init(CTX* context);
+			static void Transform(boost::uint32_t state[5], boost::uint8_t buffer[64]);
+			static void Update(CTX* context, boost::uint8_t* data, unsigned int len);
+			static void Final(boost::uint8_t digest[20], CTX* context);
+
+		private:
+			CTX context;
 	};
 }
diff --git a/Swiften/StringCodecs/UnitTest/SHA1Test.cpp b/Swiften/StringCodecs/UnitTest/SHA1Test.cpp
index 9434235..984f512 100644
--- a/Swiften/StringCodecs/UnitTest/SHA1Test.cpp
+++ b/Swiften/StringCodecs/UnitTest/SHA1Test.cpp
@@ -16,18 +16,65 @@ using namespace Swift;
 class SHA1Test : public CppUnit::TestFixture {
 		CPPUNIT_TEST_SUITE(SHA1Test);
 		CPPUNIT_TEST(testGetHash);
-		CPPUNIT_TEST(testGetHash_Twice);
+		CPPUNIT_TEST(testGetHash_TwoUpdates);
+		CPPUNIT_TEST(testGetHash_TwoGetHash);
 		CPPUNIT_TEST(testGetHash_NoData);
+		CPPUNIT_TEST(testGetHash_InterleavedUpdate);
+		CPPUNIT_TEST(testGetHashStatic);
+		CPPUNIT_TEST(testGetHashStatic_Twice);
+		CPPUNIT_TEST(testGetHashStatic_NoData);
 		CPPUNIT_TEST_SUITE_END();
 
 	public:
 		void testGetHash() {
+			SHA1 sha;
+			sha.update(ByteArray::create("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<"));
+
+			CPPUNIT_ASSERT_EQUAL(ByteArray::create("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), sha.getHash());
+		}
+
+		void testGetHash_TwoUpdates() {
+			SHA1 sha;
+			sha.update(ByteArray::create("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<"));
+			sha.update(ByteArray::create("http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<"));
+
+			CPPUNIT_ASSERT_EQUAL(ByteArray::create("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), sha.getHash());
+		}
+
+		void testGetHash_TwoGetHash() {
+			SHA1 sha;
+			sha.update(ByteArray::create("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<"));
+
+			sha.getHash();
+
+			CPPUNIT_ASSERT_EQUAL(ByteArray::create("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), sha.getHash());
+		}
+
+		void testGetHash_InterleavedUpdate() {
+			SHA1 sha;
+
+			sha.update(ByteArray::create("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<"));
+			sha.getHash();
+			sha.update(ByteArray::create("http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<"));
+
+			CPPUNIT_ASSERT_EQUAL(ByteArray::create("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), sha.getHash());
+		}
+
+
+		void testGetHash_NoData() {
+			SHA1 sha;
+			sha.update(std::vector<unsigned char>());
+
+			CPPUNIT_ASSERT_EQUAL(ByteArray::create("\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09"), sha.getHash());
+		}
+
+		void testGetHashStatic() {
 			ByteArray result(SHA1::getHash("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<"));
 			CPPUNIT_ASSERT_EQUAL(ByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), result);
 		}
 
 
-		void testGetHash_Twice() {
+		void testGetHashStatic_Twice() {
 			ByteArray input("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<");
 			SHA1::getHash(input);
 			ByteArray result(SHA1::getHash(input));
@@ -35,7 +82,7 @@ class SHA1Test : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT_EQUAL(ByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), result);
 		}
 
-		void testGetHash_NoData() {
+		void testGetHashStatic_NoData() {
 			ByteArray result(SHA1::getHash(ByteArray()));
 
 			CPPUNIT_ASSERT_EQUAL(ByteArray("\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09"), result);
-- 
cgit v0.10.2-6-g49f6