#include "assert.hpp" #include "../include/odhtdb/Log.hpp" #include "../include/odhtdb/Database.hpp" #include "../include/odhtdb/Group.hpp" #include "../include/odhtdb/Encryption.hpp" #include "../include/odhtdb/Hash.hpp" #include "../include/odhtdb/hex2bin.hpp" #include "../include/odhtdb/bin2hex.hpp" #include "../include/odhtdb/DataView.hpp" #include #include #include #include #include using namespace std; using namespace chrono_literals; using namespace odhtdb; void testBinHexConvert() { DataView input { (void*)"hello", 5 }; string inputHex = bin2hex((const char*)input.data, input.size); assertEquals("68656c6c6f", inputHex); string inputBin = hex2bin(inputHex.c_str(), inputHex.size()); if(inputBin.size() != input.size) { string errMsg = "Expected input converted to hex and then back to binary size to be 5, was: "; errMsg += to_string(inputBin.size()); fail(errMsg); } if(memcmp(input.data, inputBin.data(), input.size) != 0) { string errMsg = "Expected input converted to hex and then back to binary to be same as original input ('hello'), was: "; errMsg += string(inputBin.c_str(), inputBin.size()); fail(errMsg); } } void testHash() { Hash hash("odhtdb", 6); string hashHex = hash.toString(); assertEquals("a7b30ec8ab92de60e551b26bb8f78d315697f84dd7f5549a143477e095ec934f", hashHex); Log::debug("hash of 'odhtdb' is: a7b30ec8ab92de60e551b26bb8f78d315697f84dd7f5549a143477e095ec934f"); } void testSignData(const Signature::KeyPair &localUserKeyPair) { std::string publicKeyStr = localUserKeyPair.getPublicKey().toString(); Log::debug("Local user public key: %s", publicKeyStr.c_str()); std::string privateKeyStr = localUserKeyPair.getPrivateKey().toString(); Log::debug("Local user private key: %s", privateKeyStr.c_str()); string expectedUnsignedData = "hello, world!"; string signedData = localUserKeyPair.getPrivateKey().sign(DataView((void*)expectedUnsignedData.data(), expectedUnsignedData.size())); assertEquals(SIGNED_HASH_SIZE + expectedUnsignedData.size(), signedData.size()); string unsignedData = localUserKeyPair.getPublicKey().unsign(DataView((void*)signedData.data(), signedData.size())); assertEquals(expectedUnsignedData, unsignedData); try { Signature::KeyPair anotherKeyPair; anotherKeyPair.getPublicKey().unsign(DataView((void*)signedData.data(), signedData.size())); fail("Expected unsign to fail since the data was not signed with the private key matching given public key"); } catch (UnsignWrongKeyException &e) { } catch (exception &e) { string errMsg = "Expected unsign to fail with UnsignWrongKeyException, fail reason: "; errMsg += e.what(); fail(errMsg); } try { Signature::KeyPair anotherKeyPair; anotherKeyPair.getPublicKey().unsign(DataView((void*)signedData.data(), 3)); fail("Expected unsign to fail since the (signed) data is too small to have been signed (signed hash is 64 bytes)"); } catch (UnsignInvalidSizeException &e) { } catch (exception &e) { string errMsg = "Expected unsign to fail with UnsignInvalidSizeException, fail reason: "; errMsg += e.what(); fail(errMsg); } } void testEncryption() { const char *message = "hello, world!"; const unsigned long long messageLength = 13; Encryption encryption(DataView((void*)message, messageLength)); Decryption decryption(encryption.getCipherText(), encryption.getNonce(), encryption.getKey()); assertEquals(messageLength, decryption.getDecryptedText().size); assertEquals(0, strncmp(message, (const char*)decryption.getDecryptedText().data, messageLength)); } void testCachedIdentity() { pair, shared_ptr> identity = dht::crypto::generateIdentity(); dht::Blob privateKeyData = identity.first->serialize(); printf("Private key size: %d, serialized data: %s\n", privateKeyData.size(), Hash(privateKeyData.data(), privateKeyData.size()).toString().c_str()); dht::crypto::PrivateKey privateKeyDeserialized(privateKeyData); privateKeyData = identity.first->serialize(); printf("Private key size: %d, serialized data: %s\n", privateKeyData.size(), Hash(privateKeyData.data(), privateKeyData.size()).toString().c_str()); dht::Blob certificateData; identity.second->pack(certificateData); printf("Certificate data size: %d, serialized data: %s\n", certificateData.size(), Hash(certificateData.data(), certificateData.size()).toString().c_str()); dht::crypto::Certificate certificateDeserialized(certificateData); certificateData.clear(); identity.second->pack(certificateData); printf("Certificate data size: %d, serialized data: %s\n", certificateData.size(), Hash(certificateData.data(), certificateData.size()).toString().c_str()); } void testTimestamp(const Database &database) { auto timestamp1 = database.getSyncedTimestampUtc(); this_thread::sleep_for(chrono::milliseconds(100)); auto timestamp2 = database.getSyncedTimestampUtc(); if(timestamp2.getCombined() > timestamp1.getCombined()) Log::debug("Second timestamp is more than first one, as expected"); else fail("Second timestamp is not more than first one for some reason"); } int main() { Log::debug("Starting tests..."); boost::filesystem::path storagePath("/tmp/odhtdbTest"); boost::filesystem::remove_all(storagePath); boost::filesystem::create_directory(storagePath); testCachedIdentity(); testBinHexConvert(); testHash(); testEncryption(); int createNodeCounter = 0; int addDataCounter = 0; int addUserCounter = 0; auto createNodeCallback = [&createNodeCounter](const DatabaseCreateNodeRequest &request) { Log::debug("Create node callback"); ++createNodeCounter; }; auto addNodeCallback = [&addDataCounter](const DatabaseAddNodeRequest &request) { Log::debug("Add node callback"); ++addDataCounter; }; auto addUserCallback = [&addUserCounter](const DatabaseAddUserRequest &request) { Log::debug("Add user callback"); ++addUserCounter; }; DatabaseCallbackFuncs callbackFuncs { createNodeCallback, addNodeCallback, addUserCallback }; DatabaseNode databaseNode; { Signature::KeyPair localUserKeyPair; testSignData(localUserKeyPair); // TODO: Setup local bootstrap node for tests Database database("bootstrap.ring.cx", 4222, storagePath, callbackFuncs); testTimestamp(database); auto databaseCreateResponse = database.create(); databaseNode = { databaseCreateResponse->getNodeEncryptionKey(), databaseCreateResponse->getRequestHash() }; auto adminUserKey = databaseCreateResponse->getNodeAdminKeyPair(); database.addData(databaseNode, *adminUserKey, DataView{ (void*)"hello, world!", 13 }); database.addUser(databaseNode, *adminUserKey, localUserKeyPair.getPublicKey(), databaseCreateResponse->getNodeAdminGroupId()->getView()); database.addData(databaseNode, localUserKeyPair, DataView{ (void*)"hello, aaald!", 13 }); database.seed(databaseNode, DatabaseFetchOrder::NEWEST_FIRST); this_thread::sleep_for(chrono::seconds(3)); assertEquals(1, createNodeCounter); assertEquals(2, addDataCounter); assertEquals(1, addUserCounter); string username = "dec05eba"; string password = "secretPassword"; if(database.doesStoredUserExist(username)) fail("Expected stored to not exist until it has been added"); database.storeUserPasswordEncrypted(*databaseNode.getRequestHash(), username, password, *adminUserKey); try { database.storeUserPasswordEncrypted(*databaseNode.getRequestHash(), username, password, localUserKeyPair); fail("Expected store user password to fail since we have already stored an user in the node"); } catch(SqlExecException &e) { Log::debug("Failed with sql exception as expected, since we already have an user in the node: %s", e.what()); } if(!database.doesStoredUserExist(username)) fail("Expected stored to exist after it has been added"); auto nodeUserData = database.getStoredUserNodeDataDecrypted(username, password); assertEquals((size_t)1, nodeUserData.size()); if(nodeUserData[0].nodeHash != *databaseNode.getRequestHash()) fail("Expected stored node hash to match node hash"); if(nodeUserData[0].keyPair.getPublicKey() != adminUserKey->getPublicKey()) fail("Expected stored public key to match admin user public key"); if(nodeUserData[0].keyPair.getPrivateKey() != adminUserKey->getPrivateKey()) fail("Expected stored private key to match admin user private key"); try { database.storeUserWithoutNodes(username, password); fail("Expected store user to fail since the user already exists in database"); } catch(SqlExecException &e) { } database.storeUserWithoutNodes("anotherUser", password); if(!database.doesStoredUserExist("anotherUser")) fail("Added user 'anotherUser' to database without any nodes, but it doesn't seem to be stored"); } Log::debug("Callback works when adding data while connected, now testing to reconnect and check if data remains..."); { createNodeCounter = 0; addDataCounter = 0; addUserCounter = 0; // TODO: Setup local bootstrap node for tests Database database("bootstrap.ring.cx", 4222, storagePath, callbackFuncs); database.loadNode(*databaseNode.getRequestHash()); database.seed(databaseNode, DatabaseFetchOrder::NEWEST_FIRST); this_thread::sleep_for(chrono::seconds(3)); assertEquals(1, createNodeCounter); assertEquals(2, addDataCounter); assertEquals(1, addUserCounter); } return 0; }