1
0
forked from 0ad/0ad

Remove Crypto++ because it causes too much trouble. Replace with a custom MD5 implementation for simulation state hashing.

This was SVN commit r7269.
This commit is contained in:
Ykkrosh 2010-01-10 19:29:27 +00:00
parent e8008edf1c
commit 17718981cf
8 changed files with 398 additions and 13 deletions

View File

@ -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.

View File

@ -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))

202
source/maths/MD5.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
// 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 <class T> inline T rotlFixed(T x, unsigned int y)
{
debug_assert(y < sizeof(T)*8);
return T((x<<y) | (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;
}

42
source/maths/MD5.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}
}
};

View File

@ -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();

View File

@ -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;

View File

@ -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()