// test the published EncryptedString class #include #include #include #include // Make sure we have an ASSERT() function defined for our test. #ifndef ASSERT #ifdef NDEBUG #undef NDEBUG #endif #include #define ASSERT assert #endif // Uncomment the #define if you want to trace the test harness. #ifndef TRACE //#define TRACE #endif class ScopeTracer { public: ScopeTracer(const char* message) : m_message(NULL) { #ifdef TRACE if (message) { m_message = new char[strlen(message) + 1]; strcpy(m_message, message); } else { m_message = new char[1]; m_message[0] = NULL; } PrintIndents(); printf("->%s\n", m_message); ++g_indentLevel; #else // Make the compiler happy. message = NULL; #endif } ~ScopeTracer(void) { #ifdef TRACE --g_indentLevel; PrintIndents(); printf("<-%s\n", m_message); delete [] m_message; #endif } unsigned int GetIndentLevel(void) const { return g_indentLevel; } private: void PrintIndents(void) { for (unsigned int level = 0; level < g_indentLevel; ++level) { printf(" "); } } char* m_message; static unsigned int g_indentLevel; }; unsigned int ScopeTracer::g_indentLevel = 0; #include "EncryptedString.hpp" #include using namespace std; HINSTANCE hResourceModuleHandle = 0; const wchar_t* HELPFILE_NAME = 0; const wchar_t* RUNTIME_NAME = L"test"; //DWORD DEFAULT_LOGGING_OPTIONS = Burnslib::Log::OUTPUT_TYPICAL; const size_t MAX_CHARACTER_COUNT = 2047; void TestEmptyness(const EncryptedString& empty) { ScopeTracer scope("TestEmptyness"); ASSERT(empty.IsEmpty()); ASSERT(empty.GetLength() == 0); // the cleartext of an empty instance should also be empty WCHAR* emptyClear = empty.GetClearTextCopy(); ASSERT(emptyClear); ASSERT(*emptyClear == 0); empty.DestroyClearTextCopy(emptyClear); } void TestEmptyStrings() { ScopeTracer scope("TestEmptyStrings"); EncryptedString empty1; TestEmptyness(empty1); // copies of empty strings are themselves empty EncryptedString empty2(empty1); // source should still be empty TestEmptyness(empty1); TestEmptyness(empty2); EncryptedString empty3; TestEmptyness(empty3); empty3 = empty2; // source should still be empty TestEmptyness(empty2); TestEmptyness(empty3); empty3 = empty1; TestEmptyness(empty1); TestEmptyness(empty2); TestEmptyness(empty3); // strings built from empty strings should be empty EncryptedString empty4; TestEmptyness(empty4); empty4.Encrypt(L""); TestEmptyness(empty4); // a string made from an empty string is still empty when the source // is made non-empty EncryptedString empty5; EncryptedString empty6(empty5); TestEmptyness(empty5); TestEmptyness(empty6); empty5.Encrypt(L"not empty any more"); ASSERT(!empty5.IsEmpty()); ASSERT(empty5.GetLength() != 0); TestEmptyness(empty6); // A cleared string is empty EncryptedString empty7; empty7.Encrypt(L"some text"); empty7.Clear(); TestEmptyness(empty7); // empty strings are equal ASSERT(empty1 == empty1); ASSERT(empty1 == empty2); ASSERT(empty1 == empty3); ASSERT(empty1 == empty4); ASSERT(empty1 == empty6); ASSERT(empty1 == empty7); ASSERT(empty2 == empty1); ASSERT(empty2 == empty2); ASSERT(empty2 == empty3); ASSERT(empty2 == empty4); ASSERT(empty2 == empty6); ASSERT(empty2 == empty7); ASSERT(empty3 == empty1); ASSERT(empty3 == empty2); ASSERT(empty3 == empty3); ASSERT(empty3 == empty4); ASSERT(empty3 == empty6); ASSERT(empty3 == empty7); ASSERT(empty4 == empty1); ASSERT(empty4 == empty2); ASSERT(empty4 == empty3); ASSERT(empty4 == empty4); ASSERT(empty4 == empty6); ASSERT(empty4 == empty7); ASSERT(empty6 == empty1); ASSERT(empty6 == empty2); ASSERT(empty6 == empty3); ASSERT(empty6 == empty4); ASSERT(empty6 == empty6); ASSERT(empty6 == empty7); ASSERT(empty7 == empty1); ASSERT(empty7 == empty2); ASSERT(empty7 == empty3); ASSERT(empty7 == empty4); ASSERT(empty7 == empty6); ASSERT(empty7 == empty7); } WCHAR* MakeRandomString(size_t length) { //LOG_FUNCTION2(MakeRandomString, String::format(L"%1!d!", length)); WCHAR* result = new WCHAR[length + 1]; for (size_t i = 0; i < length; ++i) { // 32 = space, the lowest printable character int r1 = rand() % 0xFFEE; // careful not to use an expression as a parameter to max... int r2 = max(32, r1); ASSERT(r2); ASSERT(r2 >= 32); ASSERT(r2 < 0xFFEE); result[i] = (WCHAR) r2; ASSERT(result[i]); } result[length] = 0; return result; } void TestEncryption(const EncryptedString& s, const WCHAR* sourceClearText) { ScopeTracer scope("TestEncryption"); // decrypt s, compare it to sourceClearText WCHAR* clearText = s.GetClearTextCopy(); // the decryption shouldn't fail (barring out-of-memory); ASSERT(clearText); ASSERT(wcscmp(clearText, sourceClearText) == 0); s.DestroyClearTextCopy(clearText); } void TestEncryptionForStringOfLengthN(size_t length) { //LOG_FUNCTION2(TestEncryptionForStringOfLengthN, String::format(L"%1!d!", length)); ASSERT(length <= MAX_CHARACTER_COUNT); WCHAR* source = MakeRandomString(length); EncryptedString s; s.Encrypt(source); TestEncryption(s, source); EncryptedString s1(s); TestEncryption(s1, source); EncryptedString s2; s2 = s; TestEncryption(s2, source); delete[] source; } void TestEncryptionFidelity() { ScopeTracer scope("TestEncryptionFidelity"); // do we get out what we put in? srand(time(0)); // test increasing lengths of random strings for ( size_t length = 0; length <= MAX_CHARACTER_COUNT; ++length) { TestEncryptionForStringOfLengthN(length); } // test decreasing lengths of random strings for ( size_t length = MAX_CHARACTER_COUNT; length != 0; --length) { TestEncryptionForStringOfLengthN(length); } // test strings of random length, with lots of outstanding encrypted // strings typedef std::list ESPList; ESPList strings; for ( int count = 0; count < 1000; ++count) { size_t length = rand() % MAX_CHARACTER_COUNT; WCHAR* source = MakeRandomString(length); EncryptedString* ps = new EncryptedString; strings.push_back(ps); ps->Encrypt(source); TestEncryption(*ps, source); // make a copy via copy ctor EncryptedString* ps1 = new EncryptedString(*ps); strings.push_back(ps1); TestEncryption(*ps1, source); // make a copy via operator = EncryptedString* ps2 = new EncryptedString; strings.push_back(ps2); *ps2 = *ps; TestEncryption(*ps2, source); delete[] source; } for ( ESPList::iterator i = strings.begin(); i != strings.end(); ++i) { (*i)->Clear(); TestEmptyness(**i); delete *i; } } void TestClearTextCopying() { ScopeTracer scope("TestClearTextCopying"); // make a bunch of copies, make sure the count balances typedef char foo[2]; // An encrypted string and the source string used to build it. typedef std::pair EncryptedAndSourcePair; // A list of results from EncryptedString::GetClearTextCopy typedef std::list ClearTextList; // A tuple of the encrypted string, its source string, and a list // of clear-text copies made from the encrypted string typedef std::pair MasterAndClearTextCopiesPair; // A list of the above tuples. typedef std::list MasterAndCopiesList; MasterAndCopiesList mcList; for (int count = 0; count < 1000; ++count) { // Make a random source string size_t length = rand() % MAX_CHARACTER_COUNT; WCHAR* source = MakeRandomString(length); // Make an encrypted string from it EncryptedString* ps = new EncryptedString; ps->Encrypt(source); // Make a pair of the encrypted string and its source string EncryptedAndSourcePair* esp = new EncryptedAndSourcePair(ps, source); // Make a list of clear-text copies of the encrypted string ClearTextList* ctList = new ClearTextList; // Make a master and copies pair MasterAndClearTextCopiesPair* mcPair = new MasterAndClearTextCopiesPair(esp, ctList); // add the master and copies pair to the master and copies list mcList.push_back(mcPair); int copyMax = max(1, rand() % 50); for (int copyCount = 0; copyCount < copyMax; ++copyCount) { // make some copies ctList->push_back(ps->GetClearTextCopy()); } } for ( MasterAndCopiesList::iterator i = mcList.begin(); i != mcList.end(); ++i) { EncryptedAndSourcePair* esp = (*i)->first; ClearTextList* ctList = (*i)->second; // delete each element of the ClearTextList for ( ClearTextList::iterator j = ctList->begin(); j != ctList->end(); ++j) { // all copies should be identical ASSERT(wcscmp(esp->second, *j) == 0); esp->first->DestroyClearTextCopy(*j); } // delete the ClearTextList delete ctList; // delete the encrypted string delete esp->first; // delete the source string; delete[] esp->second; // delete the encrypted string/source string pair delete esp; // delete the master and copies pair delete *i; } } void TestAssignment() { ScopeTracer scope("TestAssignment"); } void TestEquality(const EncryptedString& s, const EncryptedString& s1, size_t length) { ScopeTracer scope("TestEquality"); // a string is equal to itself ASSERT(s == s); ASSERT(s1 == s1); // a string is equal to a copy of itself ASSERT(s1 == s); // a copy is equal to its source ASSERT(s == s1); // a copy is equal to itself ASSERT(s1 == s1); // a copy is the same length as its source ASSERT(s1.GetLength() == length); ASSERT(s.GetLength() == length); // a string is the same length as its copy ASSERT(s1.GetLength() == s.GetLength()); } void TestEqualityForStringOfLengthN(size_t length) { //LOG_FUNCTION2(TestEncryptionForStringOfLengthN, String::format(L"%1!d!", length)); ASSERT(length <= MAX_CHARACTER_COUNT); WCHAR* source = MakeRandomString(length); EncryptedString s; s.Encrypt(source); ASSERT(s.GetLength() == length); EncryptedString s1(s); ASSERT(s1.GetLength() == length); TestEquality(s, s1, length); EncryptedString s2; s2 = s; TestEquality(s, s2, length); TestEquality(s1, s2, length); // a copy is not equal to its source when the source is changed s.Encrypt(L"Something else..."); ASSERT(s != s1); ASSERT(s != s2); ASSERT(s2 != s); ASSERT(s1 != s); TestEquality(s1, s2, length); delete[] source; } void DoEqualityTests() { ScopeTracer scope("DoEqualityTests"); for ( size_t length = 0; length <= MAX_CHARACTER_COUNT; ++length) { TestEqualityForStringOfLengthN(length); } } void TestInequality() { ScopeTracer scope("TestInequality"); } void TestBoundaries() { ScopeTracer scope("TestBoundaries"); } void TestLegitimateUse() { ScopeTracer scope("TestLegitimateUse"); TestEmptyStrings(); TestClearTextCopying(); TestEncryptionFidelity(); TestAssignment(); DoEqualityTests(); TestInequality(); TestBoundaries(); } void TestIlllegitimateUse() { ScopeTracer scope("TestIlllegitimateUse"); // make strings that are too long, // make unbalanced cleartext copyies (call Destroy too many times, not enough times) } VOID _cdecl main(int, char **) { ScopeTracer scope("main"); TestLegitimateUse(); TestIlllegitimateUse(); }