diff --git a/build/premake/premake.lua b/build/premake/premake.lua
index b0bb784c6e..bb092a08d8 100755
--- a/build/premake/premake.lua
+++ b/build/premake/premake.lua
@@ -417,7 +417,6 @@ function setup_all_libs ()
}
extern_libs = {
"boost",
- "cryptopp",
"spidermonkey",
}
setup_static_lib_package("simulation2", source_dirs, extern_libs, {})
@@ -542,7 +541,6 @@ function setup_all_libs ()
"openal",
"vorbis",
"libjpg",
- "cryptopp",
"valgrind",
"cxxtest",
}
@@ -615,7 +613,6 @@ used_extern_libs = {
"cxxtest",
"comsuppw",
"enet",
- "cryptopp",
}
-- Bundles static libs together with main.cpp and builds game executable.
diff --git a/source/lib/self_test.h b/source/lib/self_test.h
index b5611c7af1..8f9a6d0dba 100644
--- a/source/lib/self_test.h
+++ b/source/lib/self_test.h
@@ -255,8 +255,11 @@ namespace CxxTest
}
#define TS_ASSERT_OK(expr) TS_ASSERT_EQUALS((expr), INFO::OK)
+#define TSM_ASSERT_OK(m, expr) TSM_ASSERT_EQUALS(m, (expr), INFO::OK)
#define TS_ASSERT_STR_EQUALS(str1, str2) TS_ASSERT_EQUALS(std::string(str1), std::string(str2))
+#define TSM_ASSERT_STR_EQUALS(m, str1, str2) TSM_ASSERT_EQUALS(m, std::string(str1), std::string(str2))
#define TS_ASSERT_WSTR_EQUALS(str1, str2) TS_ASSERT_EQUALS(std::wstring(str1), std::wstring(str2))
+#define TSM_ASSERT_WSTR_EQUALS(m, str1, str2) TSM_ASSERT_EQUALS(m, std::wstring(str1), std::wstring(str2))
bool ts_str_contains(const std::wstring& str1, const std::wstring& str2); // defined in test_setup.cpp
#define TS_ASSERT_WSTR_CONTAINS(str1, str2) TSM_ASSERT(str1, ts_str_contains(str1, str2))
diff --git a/source/maths/MD5.cpp b/source/maths/MD5.cpp
new file mode 100644
index 0000000000..b77439323d
--- /dev/null
+++ b/source/maths/MD5.cpp
@@ -0,0 +1,202 @@
+/* Copyright (C) 2010 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+// Based on md5.cpp from Crypto++ 5.6.0:
+// "md5.cpp - modified by Wei Dai from Colin Plumb's public domain md5.c"
+// "any modifications are placed in the public domain"
+
+#include "MD5.h"
+
+MD5::MD5()
+{
+ InitState();
+}
+
+void MD5::InitState()
+{
+ m_Digest[0] = 0x67452301L;
+ m_Digest[1] = 0xefcdab89L;
+ m_Digest[2] = 0x98badcfeL;
+ m_Digest[3] = 0x10325476L;
+ m_BufLen = 0;
+ m_InputLen = 0;
+ memset(m_Buf, 0xcc, sizeof(m_Buf));
+}
+
+void MD5::Update(const u8* data, size_t len)
+{
+ const size_t CHUNK_SIZE = sizeof(m_Buf);
+ debug_assert(m_BufLen < CHUNK_SIZE);
+
+ m_InputLen += len;
+
+ // If we have enough space in m_Buf and won't flush, simply append the input
+ if (m_BufLen + len < CHUNK_SIZE)
+ {
+ memcpy(m_Buf + m_BufLen, data, len);
+ m_BufLen += len;
+ return;
+ }
+
+ // Add as much data as possible to the buffer
+ size_t n = CHUNK_SIZE - m_BufLen;
+ debug_assert(len >= n);
+ memcpy(m_Buf + m_BufLen, data, n);
+ data += n;
+ len -= n;
+
+ // Flush the (now full) buffer
+ Transform((const u32*)m_Buf); // assumes little-endian
+
+ // Process whole chunks of the input
+ while (len >= CHUNK_SIZE)
+ {
+ Transform((const u32*)data); // assumes little-endian; ignores alignment
+ data += CHUNK_SIZE;
+ len -= CHUNK_SIZE;
+ }
+
+ // Add the remainder to the buffer
+ memcpy(m_Buf, data, len);
+ m_BufLen = len;
+}
+
+void MD5::Final(u8* digest)
+{
+ // Compute the message length in bits (before padding)
+ u64 len = m_InputLen * 8;
+
+ // Pad with 1-bit
+ const u8 pad = 0x80;
+ Update(&pad, 1);
+
+ // Fill with zeros until length % 64 = 56 (bytes)
+ while (m_BufLen % 64 != 56)
+ {
+ const u8 zero = 0;
+ Update(&zero, 1);
+ }
+
+ // Append the length (assumes little-endian)
+ Update((const u8*)&len, 8);
+
+ // Return the digest (assumes little-endian)
+ memcpy(digest, m_Digest, DIGESTSIZE);
+
+ // Reset
+ InitState();
+}
+
+template inline T rotlFixed(T x, unsigned int y)
+{
+ debug_assert(y < sizeof(T)*8);
+ return T((x<>(sizeof(T)*8-y)));
+}
+// TODO: Crypto++ has an overload using _lrotl on MSVC - is that worthwhile?
+
+void MD5::Transform(const u32* in)
+{
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+#define MD5STEP(f, w, x, y, z, data, s) \
+ w = rotlFixed(w + f(x, y, z) + data, s) + x
+
+ u32* digest = m_Digest;
+
+ u32 a, b, c, d;
+
+ a = digest[0];
+ b = digest[1];
+ c = digest[2];
+ d = digest[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ digest[0] += a;
+ digest[1] += b;
+ digest[2] += c;
+ digest[3] += d;
+}
diff --git a/source/maths/MD5.h b/source/maths/MD5.h
new file mode 100644
index 0000000000..b9972f90db
--- /dev/null
+++ b/source/maths/MD5.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2010 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_MD5
+#define INCLUDED_MD5
+
+/**
+ * MD5 hashing algorithm. Note that MD5 is broken and must not be used for
+ * anything that requires security.
+ */
+class MD5
+{
+public:
+ static const size_t DIGESTSIZE = 16;
+
+ MD5();
+ void Update(const u8* data, size_t len);
+ void Final(u8* digest);
+private:
+ void InitState();
+ void Transform(const u32* in);
+ u32 m_Digest[4]; // internal state
+ u8 m_Buf[64]; // buffered input bytes
+ size_t m_BufLen; // bytes in m_Buf that are valid
+ u64 m_InputLen; // bytes
+};
+
+#endif // INCLUDED_MD5
diff --git a/source/maths/tests/test_MD5.h b/source/maths/tests/test_MD5.h
new file mode 100644
index 0000000000..c833d4239c
--- /dev/null
+++ b/source/maths/tests/test_MD5.h
@@ -0,0 +1,142 @@
+/* Copyright (C) 2010 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "lib/self_test.h"
+
+#include "maths/MD5.h"
+
+class TestMD5 : public CxxTest::TestSuite
+{
+public:
+ std::string decode(u8* digest)
+ {
+ char digeststr[MD5::DIGESTSIZE*2+1];
+ for (size_t i = 0; i < MD5::DIGESTSIZE; ++i)
+ sprintf_s(digeststr+2*i, 4, "%02x", digest[i]);
+ return digeststr;
+ }
+
+ void compare(const char* input, const char* expected)
+ {
+ u8 digest[MD5::DIGESTSIZE];
+
+ MD5 m;
+ m.Update((const u8*)input, strlen(input));
+ m.Final(digest);
+
+ TSM_ASSERT_STR_EQUALS(input, decode(digest), expected);
+ }
+
+ void test_rfc()
+ {
+ compare("", "d41d8cd98f00b204e9800998ecf8427e");
+ compare("a", "0cc175b9c0f1b6a831c399e269772661");
+ compare("abc", "900150983cd24fb0d6963f7d28e17f72");
+ compare("message digest", "f96b697d7cb7938d525a2f31aaf161d0");
+ compare("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b");
+ compare("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "d174ab98d277d9f5a5611c2c9f419d9f");
+ compare("12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ "57edf4a22be3c955ac49da2e2107b67a");
+ }
+
+ void test_align()
+ {
+ // Make sure it's not sensitive to alignment
+ const char* a0 = "a";
+ const char* a1 = "?a";
+ const char* a2 = "??a";
+ const char* a3 = "???a";
+ compare(a0+0, "0cc175b9c0f1b6a831c399e269772661");
+ compare(a1+1, "0cc175b9c0f1b6a831c399e269772661");
+ compare(a2+2, "0cc175b9c0f1b6a831c399e269772661");
+ compare(a3+3, "0cc175b9c0f1b6a831c399e269772661");
+ }
+
+ void test_align_long()
+ {
+ // Make sure it's not sensitive to alignment
+ // when processing long chunks (where it won't memcpy to an intermediate buffer)
+ std::string a0 (1000, 'a');
+ std::string a1 ("?" + a0);
+ std::string a2 ("??" + a0);
+ std::string a3 ("???" + a0);
+ compare(a0.c_str()+0, "cabe45dcc9ae5b66ba86600cca6b8ba8");
+ compare(a1.c_str()+1, "cabe45dcc9ae5b66ba86600cca6b8ba8");
+ compare(a2.c_str()+2, "cabe45dcc9ae5b66ba86600cca6b8ba8");
+ compare(a3.c_str()+3, "cabe45dcc9ae5b66ba86600cca6b8ba8");
+ // Matches output from:
+ // perl -e'print "a" x 1000'|openssl md5
+ }
+
+ void test_padding()
+ {
+ // Edge cases for padding
+ compare(std::string(54, 'x').c_str(), "61ea0974c662328da964d977a8253873");
+ compare(std::string(55, 'x').c_str(), "04364420e25c512fd958a70738aa8f72");
+ compare(std::string(56, 'x').c_str(), "668a72d5ba17f08e62dabcafad6db14b");
+ compare(std::string(57, 'x').c_str(), "693037871c4a9d3d8685018905cb530a");
+ }
+
+ void test_chunks()
+ {
+ u8 digest[MD5::DIGESTSIZE];
+
+ const u8* in = (const u8*)"12345678901234567890123456789012345678901234567890123456789012345678901234567890";
+ size_t len = 80;
+ const char* expected = "57edf4a22be3c955ac49da2e2107b67a";
+
+ // Process in one chunk
+ {
+ MD5 m;
+ m.Update(in, len);
+ m.Final(digest);
+ TS_ASSERT_STR_EQUALS(decode(digest), expected);
+ }
+
+ // Process one byte at a time
+ {
+ MD5 m;
+ for (size_t i = 0; i < len; ++i)
+ m.Update(in+i, 1);
+ m.Final(digest);
+ TS_ASSERT_STR_EQUALS(decode(digest), expected);
+ }
+
+ // Zero-length updates
+ {
+ MD5 m;
+ m.Update(in+0, 1);
+ m.Update(in+1, 0);
+ m.Update(in+1, len-1);
+ m.Update(in+len, 0);
+ m.Final(digest);
+ TS_ASSERT_STR_EQUALS(decode(digest), expected);
+ }
+
+ // Split at various points
+ for (size_t i = 0; i <= len; ++i)
+ {
+ MD5 m;
+ m.Update(in, i);
+ m.Update(in+i, len-i);
+ m.Final(digest);
+ TS_ASSERT_STR_EQUALS(decode(digest), expected);
+ }
+ }
+};
+
diff --git a/source/simulation2/serialization/HashSerializer.h b/source/simulation2/serialization/HashSerializer.h
index 903c375b95..8092c7e80e 100644
--- a/source/simulation2/serialization/HashSerializer.h
+++ b/source/simulation2/serialization/HashSerializer.h
@@ -20,14 +20,13 @@
#include "BinarySerializer.h"
-#include "lib/external_libraries/cryptopp.h"
+#include "maths/MD5.h"
class CHashSerializer : public CBinarySerializer
{
// We don't care about cryptographic strength, just about detection of
- // unintended changes and about performance, but we do want to use an algorithm
- // that's included in Crypto++'s Windows DLL, so SHA1 is an adequate choice
- typedef CryptoPP::SHA1 HashFunc;
+ // unintended changes and about performance, so MD5 is an adequate choice
+ typedef MD5 HashFunc;
public:
CHashSerializer(ScriptInterface& scriptInterface);
size_t GetHashLength();
diff --git a/source/simulation2/tests/test_ComponentManager.h b/source/simulation2/tests/test_ComponentManager.h
index efada553da..46bed3dda0 100644
--- a/source/simulation2/tests/test_ComponentManager.h
+++ b/source/simulation2/tests/test_ComponentManager.h
@@ -434,9 +434,9 @@ public:
std::string hash;
TS_ASSERT(man.ComputeStateHash(hash));
- TS_ASSERT_EQUALS(hash.length(), (size_t)20);
- TS_ASSERT_SAME_DATA(hash.data(), "\x32\x73\x30\x3a\xf2\x52\xda\x23\x5a\x25\xca\xc8\x1e\xe3\x57\xa7\x63\xc9\x5f\x0f", 20);
- // echo -en "\x01\0\0\0\x01\0\0\0\xf8\x2a\0\0\x02\0\0\0\xd2\x04\0\0\x04\0\0\0\x01\0\0\0\x08\x52\0\0" | openssl dgst -sha1 -hex | perl -pe 's/(..)/\\x$1/g'
+ TS_ASSERT_EQUALS(hash.length(), (size_t)16);
+ TS_ASSERT_SAME_DATA(hash.data(), "\xea\xd8\xe6\x94\xc0\x6b\x2a\xa1\xcc\x6d\x5d\xab\x48\x45\x75\xed", 16);
+ // echo -en "\x01\0\0\0\x01\0\0\0\xf8\x2a\0\0\x02\0\0\0\xd2\x04\0\0\x04\0\0\0\x01\0\0\0\x08\x52\0\0" | openssl md5 | perl -pe 's/(..)/\\x$1/g'
// ^^Test1A^^ ^^^ent1^^ ^^^11000^^^ ^^^ent2^^ ^^^1234^^^ ^^Test2A^^ ^^ent1^^ ^^^21000^^^
std::stringstream stateStream;
diff --git a/source/simulation2/tests/test_Serializer.h b/source/simulation2/tests/test_Serializer.h
index d94aff790e..738737f72c 100644
--- a/source/simulation2/tests/test_Serializer.h
+++ b/source/simulation2/tests/test_Serializer.h
@@ -221,9 +221,9 @@ public:
serialize.NumberU32_Unbounded("y", 1234);
serialize.NumberI32("z", 12345, 0, 65535);
- TS_ASSERT_EQUALS(serialize.GetHashLength(), (size_t)20);
- TS_ASSERT_SAME_DATA(serialize.ComputeHash(), "\x22\xb6\x38\xc5\xc5\x6d\x70\x2b\xa7\xc6\x22\x1a\xde\x3b\x97\x1a\xdc\x27\x3d\x0b", 20);
- // echo -en "\x85\xff\xff\xff\xd2\x04\x00\x00\x39\x30\x00\x00" | openssl dgst -sha1 -hex | perl -pe 's/(..)/\\x$1/g'
+ TS_ASSERT_EQUALS(serialize.GetHashLength(), (size_t)16);
+ TS_ASSERT_SAME_DATA(serialize.ComputeHash(), "\xa0\x3a\xe5\x3e\x9b\xd7\xfb\x11\x88\x35\xc6\xfb\xb9\x94\xa9\x72", 16);
+ // echo -en "\x85\xff\xff\xff\xd2\x04\x00\x00\x39\x30\x00\x00" | openssl md5 | perl -pe 's/(..)/\\x$1/g'
}
void test_bounds()