diff options
author | Aleksi Lindeman <0xdec05eba@gmail.com> | 2018-03-05 22:45:56 +0100 |
---|---|---|
committer | Aleksi Lindeman <0xdec05eba@gmail.com> | 2018-03-05 22:48:26 +0100 |
commit | 2ffb47d0043e57707474e5ae811f97c2e5e93f25 (patch) | |
tree | fd60b300cdf736de5adc68b395105dcfc6a43f09 /src | |
parent | 66661e47dc826f50b690e080057f47a0ea27016c (diff) |
Implement 'create' operation, add seeding
Seeding is currently only done on the key you specify, in the future
the user should request data that it can seed.
Diffstat (limited to 'src')
-rw-r--r-- | src/Database.cpp | 385 | ||||
-rw-r--r-- | src/DatabaseStorage.cpp | 36 | ||||
-rw-r--r-- | src/DhtKey.cpp | 22 | ||||
-rw-r--r-- | src/Encryption.cpp | 60 | ||||
-rw-r--r-- | src/Group.cpp | 11 | ||||
-rw-r--r-- | src/Hash.cpp | 55 | ||||
-rw-r--r-- | src/Signature.cpp | 6 |
7 files changed, 421 insertions, 154 deletions
diff --git a/src/Database.cpp b/src/Database.cpp index 90e83c1..bcf5580 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -2,9 +2,12 @@ #include "../include/Group.hpp" #include "../include/LocalUser.hpp" #include "../include/RemoteUser.hpp" +#include "../include/Encryption.hpp" +#include "../include/DhtKey.hpp" +#include "../include/bin2hex.hpp" #include <opendht.h> #include <fmt/format.h> -#include <sodium/crypto_box_curve25519xchacha20poly1305.h> +#include <sodium/randombytes.h> #include <thread> #include <chrono> #include <sibs/SafeSerializer.hpp> @@ -27,9 +30,54 @@ const int OPENDHT_INFOHASH_LEN = 20; namespace odhtdb { + const u16 DATABASE_CREATE_PACKET_STRUCTURE_VERSION = 0; + const u16 DATABASE_ADD_PACKET_STRUCTURE_VERSION = 0; + + DataView combine(sibs::SafeSerializer &headerSerializer, const Encryption &encryptedData) + { + usize allocationSize = headerSerializer.getBuffer().size() + encryptedData.getNonce().size + encryptedData.getCipherText().size; + char *result = new char[allocationSize]; + memcpy(result, headerSerializer.getBuffer().data(), headerSerializer.getBuffer().size()); + memcpy(result + headerSerializer.getBuffer().size(), encryptedData.getNonce().data, encryptedData.getNonce().size); + memcpy(result + headerSerializer.getBuffer().size() + encryptedData.getNonce().size, encryptedData.getCipherText().data, encryptedData.getCipherText().size); + return DataView(result, allocationSize); + } + + DatabaseCreateResponse::DatabaseCreateResponse(const shared_ptr<char*> &_key, const shared_ptr<Hash> &_hash) : + key(move(_key)), + hash(_hash) + { + + } + + const shared_ptr<char*> DatabaseCreateResponse::getNodeEncryptionKey() const + { + return key; + } + + const shared_ptr<Hash> DatabaseCreateResponse::getRequestHash() const + { + return hash; + } + Database::Database(const char *bootstrapNodeAddr, u16 port, boost::filesystem::path storageDir) { - node.run(port, dht::crypto::generateIdentity(), true); + // TODO: Cache this in storage. It takes pretty long time to generate new identity + auto identity = dht::crypto::generateIdentity(); + node.run(port , { + /*.dht_config = */{ + /*.node_config = */{ + /*.node_id = */{}, + /*.network = */0, + /*.is_bootstrap = */false, + /*.maintain_storage*/false + }, + /*.id = */identity + }, + /*.threaded = */true, + /*.proxy_server = */"", + /*.push_node_id = */"" + }); fmt::MemoryWriter portStr; portStr << port; node.bootstrap(bootstrapNodeAddr, portStr.c_str()); @@ -73,33 +121,139 @@ namespace odhtdb node.join(); } - void Database::seed() + void Database::seed(const shared_ptr<Hash> hash, const shared_ptr<char*> encryptionKey) { // TODO: Use cached files and seed those. If none exists, request new files to seed. // If nobody requests my cached files in a long time, request new files to seed and remove cached files // (only if there are plenty of other seeders for the cached files. This could also cause race issue // where all nodes with a cached file delete it at same time) + + printf("Seeding key: %s\n", hash->toString().c_str()); + DhtKey dhtKey(*hash); + + node.listen(dhtKey.getNewDataListenerKey(), [this, hash, encryptionKey](const shared_ptr<Value> &value) + { + return listenAddData(value, *hash, encryptionKey); + }); + + u8 responseKey[OPENDHT_INFOHASH_LEN]; + randombytes_buf(responseKey, OPENDHT_INFOHASH_LEN); + + // TODO: If this response key is spammed, generate a new one + node.listen(InfoHash(responseKey, OPENDHT_INFOHASH_LEN), [this, hash, encryptionKey](const shared_ptr<Value> &value) + { + const Hash requestHash(value->data.data(), value->data.size()); + if(requestHash == *hash) + return listenCreateData(value, requestHash, encryptionKey); + else + return listenAddData(value, requestHash, encryptionKey); + }); + + // TODO: Before listening on this key, we should check how many remote peers are also providing this data. + // This is to prevent too many peers from responding to a request to get old data. + node.listen(dhtKey.getRequestOldDataKey(), [this, hash](const shared_ptr<Value> &value) + { + printf("Request: Got request to send old data\n"); + try + { + sibs::SafeDeserializer deserializer(value->data.data(), value->data.size()); + u64 dataStartTimestamp = deserializer.extract<u64>(); + u8 requestResponseKey[OPENDHT_INFOHASH_LEN]; + deserializer.extract(requestResponseKey, OPENDHT_INFOHASH_LEN); + + auto requestedData = databaseStorage.getStorage(*hash); + if(!requestedData) + { + fprintf(stderr, "Warning: No data found for hash %s, unable to serve peer\n", hash->toString().c_str()); + return true; + } + + if(dataStartTimestamp == 0) + { + printf("Request: Sent create packet to requesting peer\n"); + node.put(InfoHash(requestResponseKey, OPENDHT_INFOHASH_LEN), Value(requestedData->createData, requestedData->createDataSize), [](bool ok) + { + if(!ok) + fprintf(stderr, "Failed to put response for old data\n"); + }); + } + else + { + assert(false); + printf("TODO: Send 'add' packets to requesting remote peer\n"); + } + } + catch (sibs::DeserializeException &e) + { + fprintf(stderr, "Warning: Failed to deserialize 'get old data' request: %s\n", e.what()); + } + return true; + }); + + sibs::SafeSerializer serializer; + serializer.add((u64)0); // Timestamp in microseconds, fetch data newer than this + serializer.add(responseKey, OPENDHT_INFOHASH_LEN); + node.put(dhtKey.getRequestOldDataKey(), Value(serializer.getBuffer().data(), serializer.getBuffer().size()), [](bool ok) + { + if(!ok) + fprintf(stderr, "Failed to put request to get old data\n"); + }); - using std::placeholders::_1; - node.listen(CREATE_DATA_HASH, bind(&Database::listenCreateData, this, _1)); - node.listen(ADD_DATA_HASH, bind(&Database::listenAddData, this, _1)); + //node.listen(CREATE_DATA_HASH, bind(&Database::listenCreateData, this, _1)); + //node.listen(ADD_DATA_HASH, bind(&Database::listenAddData, this, _1)); } - void Database::create(LocalUser *owner, const Key &key) + unique_ptr<DatabaseCreateResponse> Database::create(const LocalUser *owner, const std::string &name) { - Group *primaryAdminGroup = new Group("admin"); - primaryAdminGroup->addUser(owner); + // Header + sibs::SafeSerializer serializer; + serializer.add(DATABASE_CREATE_PACKET_STRUCTURE_VERSION); // Packet structure version // TODO: Append fractions to get real microseconds time - u64 timeMicroseconds = ((u64)getSyncedTimestampUtc().seconds) * 1000000ull; - stagedCreateObjects.emplace_back(StagedCreateObject(key, primaryAdminGroup, timeMicroseconds)); + u64 timestampMicroseconds = ((u64)getSyncedTimestampUtc().seconds) * 1000000ull; + serializer.add(timestampMicroseconds); + serializer.add((u8*)owner->getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES); + + // Encrypted body + sibs::SafeSerializer encryptedSerializer; + assert(owner->getName().size() <= 255); + encryptedSerializer.add((u8)owner->getName().size()); + encryptedSerializer.add((u8*)owner->getName().data(), owner->getName().size()); + assert(name.size() <= 255); + encryptedSerializer.add((u8)name.size()); + encryptedSerializer.add((u8*)name.data(), name.size()); + + try + { + Encryption encryptedBody(DataView(encryptedSerializer.getBuffer().data(), encryptedSerializer.getBuffer().size())); + DataView requestData = combine(serializer, encryptedBody); + shared_ptr<Hash> hashRequestKey = make_shared<Hash>(requestData.data, requestData.size); + auto adminGroup = new Group("administrator"); + adminGroup->addUser(owner); + databaseStorage.createStorage(*hashRequestKey, adminGroup, timestampMicroseconds, (const u8*)requestData.data, requestData.size); + stagedCreateObjects.emplace_back(make_unique<StagedCreateObject>(requestData, hashRequestKey)); + + assert(encryptedBody.getKey().size == KEY_BYTE_SIZE); + char *key = new char[encryptedBody.getKey().size]; + memcpy(key, encryptedBody.getKey().data, encryptedBody.getKey().size); + return make_unique<DatabaseCreateResponse>(make_shared<char*>(key), hashRequestKey); + } + catch (EncryptionException &e) + { + throw DatabaseCreateException("Failed to encrypt data for 'create' request"); + } } - void Database::add(LocalUser *owner, const Key &key, DataView data) + void Database::add(const LocalUser *owner, const Key &key, DataView data) { +#if 0 + if(nodeEncryptionKeys.find(key) == nodeEncryptionKeys.end()) + throw DatabaseAddException("Data for key needs to be created before data can be appended to it"); + unique_ptr<string> signedData = make_unique<string>(owner->getPrivateKey().sign(data)); // TODO: Append fractions to get real microseconds time u64 timeMicroseconds = ((u64)getSyncedTimestampUtc().seconds) * 1000000ull; stagedAddObjects.emplace_back(StagedAddObject(key, move(signedData), timeMicroseconds, owner->getPublicKey())); +#endif } void Database::commit() @@ -107,86 +261,71 @@ namespace odhtdb // TODO: Combine staged objects into one object for efficiency. // TODO: Add rollback - printf("Num objects to create: %d\n", stagedCreateObjects.size()); - for(StagedCreateObject &stagedObject : stagedCreateObjects) + try { - commitStagedCreateObject(stagedObject); - delete stagedObject.primaryAdminGroup; + printf("Num objects to create: %zu\n", stagedCreateObjects.size()); + for(const auto &stagedObject : stagedCreateObjects) + { + commitStagedCreateObject(stagedObject); + } + } + catch (exception &e) + { + fprintf(stderr, "Error: Failed to commit, reason: %s\n", e.what()); + } + + for(const auto &stagedObject : stagedCreateObjects) + { + free(stagedObject->encryptedBody.data); } stagedCreateObjects.clear(); - +#if 0 printf("Num objects to add: %d\n", stagedAddObjects.size()); for(StagedAddObject &stagedObject : stagedAddObjects) { commitStagedAddObject(stagedObject); } stagedAddObjects.clear(); - +#endif // TODO: Add node.listen here to get notified when remote peers got the commit, then we can say we can return } - // TODO: If same key already exists, fail the operation. - // Security issue: A malicious remote peer (or routing peer) could listen to this create request and build their own - // create request using same key, to steal ownership of the key. - // Possible solution: If odhtdb is for example used to build a chat application, then the key could be the chat channel id - // which could be created by hashing channel generated id and ownership information. - // Remote peers would then not be able to steal ownership of the key since hash of ownership data has to match the key. - // The key (channel id + ownership info) could then be shared with friends and they can use the key to join your channel. - void Database::commitStagedCreateObject(const StagedCreateObject &stagedObject) + void Database::commitStagedCreateObject(const unique_ptr<StagedCreateObject> &stagedObject) { - // TODO: Use (ed25519 or poly1305) and curve25519 - // TODO: Implement gas and price (refill when serving content (seeding) or by waiting. This is done to prevent spamming and bandwidth leeching) - sibs::SafeSerializer serializer; - assert(stagedObject.key.hashedKey.size() == OPENDHT_INFOHASH_LEN); - serializer.add(stagedObject.key.hashedKey.data(), stagedObject.key.hashedKey.size()); - serializer.add(stagedObject.timestamp); - serializer.add((u8)stagedObject.primaryAdminGroup->getName().size()); - serializer.add((u8*)stagedObject.primaryAdminGroup->getName().data(), stagedObject.primaryAdminGroup->getName().size()); - assert(stagedObject.primaryAdminGroup->getUsers().size() <= 255); - serializer.add((u8)stagedObject.primaryAdminGroup->getUsers().size()); - for(User *user : stagedObject.primaryAdminGroup->getUsers()) - { - serializer.add((u8*)user->getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES); - serializer.add((u8)user->getName().size()); - serializer.add((u8*)user->getName().data(), user->getName().size()); - } - - // TODO: Verify if serializer buffer needs to survive longer than this scope - Value createDataValue(serializer.getBuffer().data(), serializer.getBuffer().size()); - node.put(CREATE_DATA_HASH, move(createDataValue), [](bool ok) + DhtKey dhtKey(*stagedObject->requestKey); + Value createDataValue((u8*)stagedObject->encryptedBody.data, stagedObject->encryptedBody.size); + node.put(dhtKey.getNewDataListenerKey(), move(createDataValue), [](bool ok) { // TODO: Handle failure to put data if(!ok) fprintf(stderr, "Failed to put: %s, what to do?\n", "commitStagedCreateObject"); }/* TODO: How to make this work?, time_point(), false*/); - - // Post data for listeners of this key - /* - Value putKeyValue(serializer.getBuffer().data() + OPENDHT_INFOHASH_LEN, serializer.getBuffer().size() - OPENDHT_INFOHASH_LEN); - node.put(stagedObject.key.hashedKey, move(putKeyValue), [](bool ok) - { - // TODO: Handle failure to put data - if(!ok) - fprintf(stderr, "Failed to put for listeners: %s, what to do?\n", "commitStagedCreateObject"); - }); - */ } - void Database::commitStagedAddObject(const StagedAddObject &stagedObject) + void Database::commitStagedAddObject(const DataView &stagedObject) { - // TODO: Use (ed25519 or poly1305) and curve25519 +#if 0 // TODO: Implement gas and price (refill when serving content (seeding) or by waiting. This is done to prevent spamming and bandwidth leeching) - sibs::SafeSerializer serializer; + sibs::SafeSerializer headerSerializer; assert(stagedObject.key.hashedKey.size() == OPENDHT_INFOHASH_LEN); - serializer.add(stagedObject.key.hashedKey.data(), OPENDHT_INFOHASH_LEN); - serializer.add(stagedObject.timestamp); - serializer.add((u8*)stagedObject.creatorPublicKey.getData(), PUBLIC_KEY_NUM_BYTES); + headerSerializer.add(stagedObject.key.hashedKey.data(), OPENDHT_INFOHASH_LEN); + headerSerializer.add(stagedObject.timestamp); + + sibs::SafeSerializer bodySerializer; + bodySerializer.add((u8*)stagedObject.creatorPublicKey.getData(), PUBLIC_KEY_NUM_BYTES); assert(stagedObject.data->size() < 0xFFFF - 120); - serializer.add((u16)stagedObject.data->size()); - serializer.add((u8*)stagedObject.data->data(), stagedObject.data->size()); + bodySerializer.add((u16)stagedObject.data->size()); + bodySerializer.add((u8*)stagedObject.data->data(), stagedObject.data->size()); + + EncryptedData encryptedData; + if(encrypt(&encryptedData, (EncryptionKey*)nodeEncryptionKeys[stagedObject.key], bodySerializer.getBuffer().data(), bodySerializer.getBuffer().size()) < 0) + throw CommitAddException("Failed to encrypt staged add object"); + + Blob serializedData; + combine(&serializedData, headerSerializer, encryptedData); // TODO: Verify if serializer buffer needs to survive longer than this scope - Value addDataValue(serializer.getBuffer().data(), serializer.getBuffer().size()); + Value addDataValue(move(serializedData)); node.put(ADD_DATA_HASH, move(addDataValue), [](bool ok) { // TODO: Handle failure to put data @@ -204,6 +343,7 @@ namespace odhtdb fprintf(stderr, "Failed to put for listeners: %s, what to do?\n", "commitStagedAddObject"); }); */ +#endif } ntp::NtpTimestamp Database::getSyncedTimestampUtc() const @@ -215,44 +355,59 @@ namespace odhtdb return timestamp; } - StagedCreateObject Database::deserializeCreateRequest(const std::shared_ptr<dht::Value> &value) + DatabaseCreateRequest Database::deserializeCreateRequest(const std::shared_ptr<dht::Value> &value, const Hash &hash, const shared_ptr<char*> encryptionKey) { - StagedCreateObject result; - sibs::SafeDeserializer deserializer(value->data.data(), value->data.size()); - u8 entryKeyRaw[OPENDHT_INFOHASH_LEN]; - deserializer.extract(entryKeyRaw, OPENDHT_INFOHASH_LEN); - result.key.hashedKey = InfoHash(entryKeyRaw, OPENDHT_INFOHASH_LEN); - result.timestamp = deserializer.extract<u64>(); - - u8 adminGroupNameSize = deserializer.extract<u8>(); - string adminGroupName; - adminGroupName.resize(adminGroupNameSize); - deserializer.extract((u8*)&adminGroupName[0], adminGroupNameSize); - result.primaryAdminGroup = new Group(adminGroupName); - - u8 numUsers = deserializer.extract<u8>(); - for(int i = 0; i < numUsers; ++i) + u16 packetStructureVersion = deserializer.extract<u16>(); + if(packetStructureVersion != DATABASE_CREATE_PACKET_STRUCTURE_VERSION) { - char userPublicKeyRaw[PUBLIC_KEY_NUM_BYTES]; - deserializer.extract((u8*)userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES); - Signature::PublicKey userPublicKey(userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES); - - u8 userNameSize = deserializer.extract<u8>(); - string userName; - userName.resize(userNameSize); - deserializer.extract((u8*)&userName[0], userNameSize); - - RemoteUser *user = RemoteUser::create(userPublicKey, userName); - result.primaryAdminGroup->addUser(user); + string errMsg = "Received 'create' request with packet structure version "; + errMsg += to_string(packetStructureVersion); + errMsg += ", but our packet structure version is "; + errMsg += to_string(DATABASE_CREATE_PACKET_STRUCTURE_VERSION); + throw sibs::DeserializeException(errMsg); } - // NOTE: There might be more data in deserializer, but we can ignore those; we already got all data we need - return result; + u64 creationDate = deserializer.extract<u64>(); + // TODO: Append fractions to get real microseconds time + u64 timestampMicroseconds = ((u64)getSyncedTimestampUtc().seconds) * 1000000ull; + if(creationDate > timestampMicroseconds) + throw sibs::DeserializeException("Packet is from the future"); + + char creatorPublicKeyRaw[PUBLIC_KEY_NUM_BYTES]; + deserializer.extract((u8*)creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES); + Signature::PublicKey userPublicKey(creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES); + + if(deserializer.getSize() < NONCE_BYTE_SIZE) + throw sibs::DeserializeException("Unsigned encrypted body is too small (unable to extract nonce)"); + + auto adminGroup = new Group("administrator"); + auto creatorUser = RemoteUser::create(userPublicKey, ""); // Username is encrypted, we dont know it... + adminGroup->addUser(creatorUser); + databaseStorage.createStorage(hash, adminGroup, creationDate, value->data.data(), value->data.size()); + + u8 nonce[NONCE_BYTE_SIZE]; + deserializer.extract(nonce, NONCE_BYTE_SIZE); + + DataView dataToDecrypt((void*)deserializer.getBuffer(), deserializer.getSize()); + Decryption decryptedBody(dataToDecrypt, DataView(nonce, NONCE_BYTE_SIZE), DataView(*encryptionKey, KEY_BYTE_SIZE)); + sibs::SafeDeserializer bodyDeserializer((const u8*)decryptedBody.getDecryptedText().data, decryptedBody.getDecryptedText().size); + u8 creatorNameLength = bodyDeserializer.extract<u8>(); + string creatorName; + creatorName.resize(creatorNameLength); + bodyDeserializer.extract((u8*)&creatorName[0], creatorNameLength); + + u8 nameLength = bodyDeserializer.extract<u8>(); + string name; + name.resize(nameLength); + bodyDeserializer.extract((u8*)&name[0], nameLength); + + return { creationDate, adminGroup, move(name) }; } - StagedAddObject Database::deserializeAddRequest(const std::shared_ptr<dht::Value> &value) + DatabaseAddRequest Database::deserializeAddRequest(const std::shared_ptr<dht::Value> &value, const Hash &hash, const shared_ptr<char*> encryptionKey) { + /* StagedAddObject result; sibs::SafeDeserializer deserializer(value->data.data(), value->data.size()); @@ -277,47 +432,41 @@ namespace odhtdb result.data = make_unique<string>(creatorPublicKey.unsign(DataView((void*)signedData.data(), signedData.size()))); return result; + */ + Signature::PublicKey publicKey(nullptr, 0); + DataView d; + return { 0, 0, move(publicKey), d }; } - bool Database::listenCreateData(std::shared_ptr<dht::Value> value) + bool Database::listenCreateData(std::shared_ptr<dht::Value> value, const Hash &hash, const shared_ptr<char*> encryptionKey) { printf("Got create data\n"); try { - // TODO: Verify createObject timestamp is not in the future - StagedCreateObject createObject = deserializeCreateRequest(value); - databaseStorage.createStorage(createObject.key, { createObject.primaryAdminGroup }, createObject.timestamp); - } - catch (sibs::DeserializeException &e) - { - fprintf(stderr, "Warning: Failed to deserialize 'create' request: %s\n", e.what()); + if(databaseStorage.getStorage(hash)) + throw DatabaseStorageAlreadyExists("Create request hash is equal to hash already in storage (duplicate data?)"); + DatabaseCreateRequest createObject = deserializeCreateRequest(value, hash, encryptionKey); + printf("Got create object, name: %s\n", createObject.name.c_str()); } - catch (DatabaseStorageAlreadyExists &e) + catch (exception &e) { fprintf(stderr, "Warning: Failed to deserialize 'create' request: %s\n", e.what()); } return true; } - bool Database::listenAddData(std::shared_ptr<dht::Value> value) + bool Database::listenAddData(std::shared_ptr<dht::Value> value, const Hash &hash, const shared_ptr<char*> encryptionKey) { printf("Got add data\n"); try { + if(databaseStorage.getStorage(hash)) + throw DatabaseStorageAlreadyExists("Add request hash is equal to hash already in storage (duplicate data?)"); // TODO: Verify createObject timestamp is not in the future - StagedAddObject addObject = deserializeAddRequest(value); - DataView data((void*)addObject.data->data(), addObject.data->size()); - databaseStorage.appendStorage(addObject.key, data, addObject.timestamp, addObject.creatorPublicKey); - } - catch (sibs::DeserializeException &e) - { - fprintf(stderr, "Warning: Failed to deserialize 'add' request: %s\n", e.what()); - } - catch (DatabaseStorageNotFound &e) - { - fprintf(stderr, "Warning: Failed to deserialize 'add' request: %s\n", e.what()); + //StagedAddObject addObject = deserializeAddRequest(value); + //DataView data((void*)addObject.data->data(), addObject.data->size()); } - catch (UnsignException &e) + catch (exception &e) { fprintf(stderr, "Warning: Failed to deserialize 'add' request: %s\n", e.what()); } diff --git a/src/DatabaseStorage.cpp b/src/DatabaseStorage.cpp index 62a2e77..25d41fb 100644 --- a/src/DatabaseStorage.cpp +++ b/src/DatabaseStorage.cpp @@ -1,35 +1,49 @@ #include "../include/DatabaseStorage.hpp" +#include <cstring> using namespace std; namespace odhtdb { - void DatabaseStorage::createStorage(const Key &key, vector<Group*> &&groups, u64 timestamp) + void DatabaseStorage::createStorage(const Hash &hash, Group *creatorGroup, u64 timestamp, const u8 *data, usize dataSize) { - if(storageMap.find(key) != storageMap.end()) + /* + if(storageMap.find(hash) != storageMap.end()) { - string errMsg = "Database storage with key "; - errMsg += key.hashedKey.toString(); + string errMsg = "Database storage with hash "; + errMsg += hash.toString(); errMsg += " already exists"; throw DatabaseStorageAlreadyExists(errMsg); } + */ DatabaseStorageObjectList *databaseStorageObjectList = new DatabaseStorageObjectList(); databaseStorageObjectList->timestamp = timestamp; - databaseStorageObjectList->groups = move(groups); - storageMap[key] = databaseStorageObjectList; + databaseStorageObjectList->groups.push_back(creatorGroup); + databaseStorageObjectList->createData = new u8[dataSize]; + memcpy(databaseStorageObjectList->createData, data, dataSize); + databaseStorageObjectList->createDataSize = dataSize; + storageMap[hash] = databaseStorageObjectList; } - void DatabaseStorage::appendStorage(const Key &key, DataView &data, u64 timestamp, const Signature::PublicKey &creatorPublicKey) + void DatabaseStorage::appendStorage(const Hash &hash, DataView &data, u64 timestamp, const Signature::PublicKey &creatorPublicKey) { - auto it = storageMap.find(key); + auto it = storageMap.find(hash); if(it == storageMap.end()) { - string errMsg = "Database storage with key "; - errMsg += key.hashedKey.toString(); - errMsg += " not found. Storage for a key needs to be created before data can be appended to it"; + string errMsg = "Database storage with hash "; + errMsg += hash.toString(); + errMsg += " not found. Storage for a hash needs to be created before data can be appended to it"; throw DatabaseStorageNotFound(errMsg); } it->second->objects.push_back({data, timestamp, creatorPublicKey}); } + + const DatabaseStorageObjectList* DatabaseStorage::getStorage(const Hash &hash) const + { + auto it = storageMap.find(hash); + if(it != storageMap.end()) + return it->second; + return nullptr; + } } diff --git a/src/DhtKey.cpp b/src/DhtKey.cpp new file mode 100644 index 0000000..ea59740 --- /dev/null +++ b/src/DhtKey.cpp @@ -0,0 +1,22 @@ +#include "../include/DhtKey.hpp" +#include "../include/types.hpp" + +namespace odhtdb +{ + DhtKey::DhtKey(const Hash &key) : infoHash((const u8*)key.getData(), key.getSize()) + { + firstByteOriginalValue = infoHash[0]; + } + + const dht::InfoHash& DhtKey::getNewDataListenerKey() + { + infoHash[0] = firstByteOriginalValue; + return infoHash; + } + + const dht::InfoHash& DhtKey::getRequestOldDataKey() + { + infoHash[0] = firstByteOriginalValue + 1; + return infoHash; + } +} diff --git a/src/Encryption.cpp b/src/Encryption.cpp index 8e87a8d..c4e6a2c 100644 --- a/src/Encryption.cpp +++ b/src/Encryption.cpp @@ -1,30 +1,60 @@ #include "../include/Encryption.hpp" -#include <sodium/crypto_stream_xchacha20.h> +#include <sodium/crypto_aead_xchacha20poly1305.h> #include <sodium/randombytes.h> #include <string> namespace odhtdb { - void generateEncryptionKey(EncryptionKey *output) + Encryption::Encryption(const DataView &data, const DataView &additionalData) { - if(!output) return; - crypto_stream_xchacha20_keygen((unsigned char*)output); + cipherText = new unsigned char[crypto_aead_xchacha20poly1305_ietf_ABYTES + data.size]; + crypto_aead_xchacha20poly1305_ietf_keygen(key); + randombytes_buf(nonce, NONCE_BYTE_SIZE); + if(crypto_aead_xchacha20poly1305_ietf_encrypt(cipherText, &cipherTextLength, (const unsigned char*)data.data, data.size, (const unsigned char*)additionalData.data, additionalData.size, nullptr, nonce, key) != 0) + throw EncryptionException("Failed to encrypt data"); } - int encrypt(EncryptedData *output, const EncryptionKey *key, const void *data, size_t dataSize) + Encryption::~Encryption() { - if(!output || !key) return -1; - if(dataSize == 0) return 0; - output->data.resize(dataSize); - randombytes_buf(output->nonce, NONCE_BYTE_SIZE); - return crypto_stream_xchacha20_xor((unsigned char*)&output->data[0], (const unsigned char*)data, dataSize, (const unsigned char*)output->nonce, (const unsigned char*)key); + delete[](cipherText); } - int decrypt(std::string *output, const EncryptionKey *key, const EncryptedData *encryptedData) + DataView Encryption::getKey() const { - if(!encryptedData || !key || !output) return -1; - if(encryptedData->data.empty()) return 0; - output->resize(encryptedData->data.size()); - return crypto_stream_xchacha20_xor((unsigned char*)&(*output)[0], (const unsigned char*)&encryptedData->data[0], encryptedData->data.size(), (const unsigned char*)encryptedData->nonce, (const unsigned char*)key); + return DataView((void*)key, KEY_BYTE_SIZE); + } + + DataView Encryption::getNonce() const + { + return DataView((void*)nonce, NONCE_BYTE_SIZE); + } + + DataView Encryption::getCipherText() const + { + return DataView((void*)cipherText, cipherTextLength); + } + + Decryption::Decryption(const DataView &data, const DataView &nonce, const DataView &key) + { + decryptedText = new unsigned char[data.size]; + + if(nonce.size < NONCE_BYTE_SIZE) + throw DecryptionException("Nonce is not big enough"); + + if(key.size < KEY_BYTE_SIZE) + throw DecryptionException("Key is not big enough"); + + if(crypto_aead_xchacha20poly1305_ietf_decrypt(decryptedText, &decryptedTextLength, nullptr, (const unsigned char*)data.data, data.size, nullptr, 0, (const unsigned char*)nonce.data, (const unsigned char*)key.data) != 0) + throw DecryptionException("Failed to decrypt data"); + } + + Decryption::~Decryption() + { + delete[](decryptedText); + } + + DataView Decryption::getDecryptedText() const + { + return DataView((void*)decryptedText, decryptedTextLength); } } diff --git a/src/Group.cpp b/src/Group.cpp index c87be1e..93a0688 100644 --- a/src/Group.cpp +++ b/src/Group.cpp @@ -14,13 +14,10 @@ namespace odhtdb Group::~Group() { - for(User *user : users) - { - delete user; - } + } - void Group::addUser(User *user) + void Group::addUser(const User *user) { users.push_back(user); } @@ -30,8 +27,8 @@ namespace odhtdb return name; } - const vector<User*>& Group::getUsers() const + const vector<const User*>& Group::getUsers() const { return users; } -}
\ No newline at end of file +} diff --git a/src/Hash.cpp b/src/Hash.cpp new file mode 100644 index 0000000..5d2f914 --- /dev/null +++ b/src/Hash.cpp @@ -0,0 +1,55 @@ +#include "../include/Hash.hpp" +#include "../include/bin2hex.hpp" +#include <sodium/crypto_generichash_blake2b.h> +#include <sodium/core.h> +#include <cstring> + +namespace odhtdb +{ + struct SodiumInitializer + { + SodiumInitializer() + { + if(sodium_init() < 0) + throw std::runtime_error("Failed to initialize libsodium"); + } + }; + + static SodiumInitializer __sodiumInitializer; + + // Source: https://stackoverflow.com/a/11414104 (public license) + static size_t fnvHash(const unsigned char *key, int len) + { + size_t h = 2166136261; + for (int i = 0; i < len; i++) + h = (h * 16777619) ^ key[i]; + return h; + } + + Hash::Hash() + { + memset(data, 0, HASH_BYTE_SIZE); + } + + Hash::Hash(const void *input, const size_t inputSize) + { + int result = crypto_generichash_blake2b((unsigned char*)data, HASH_BYTE_SIZE, (const unsigned char*)input, inputSize, nullptr, 0); + if(result < 0) + throw HashException("Failed to hash data using blake2b"); + } + + size_t Hash::operator()() const + { + return fnvHash((const unsigned char*)data, HASH_BYTE_SIZE); + } + + bool Hash::operator==(const Hash &other) const + { + return memcmp(data, other.data, HASH_BYTE_SIZE) == 0; + } + + std::string Hash::toString() const + { + return bin2hex(data, HASH_BYTE_SIZE); + } +} diff --git a/src/Signature.cpp b/src/Signature.cpp index 8d3654d..34f6190 100644 --- a/src/Signature.cpp +++ b/src/Signature.cpp @@ -42,7 +42,7 @@ namespace odhtdb string result; result.resize(signedMessage.size - SIGNED_HASH_SIZE); - if(crypto_sign_ed25519_open((unsigned char*)&result[0], nullptr, (const unsigned char*)signedMessage.data, signedMessage.size, (unsigned char*)data) != 0) + if(crypto_sign_ed25519_open((unsigned char*)&result[0], nullptr, (const unsigned char*)signedMessage.data, signedMessage.size, (unsigned char*)data) < 0) throw UnsignWrongKeyException("Message was not signed with matching private key"); return result; @@ -86,7 +86,7 @@ namespace odhtdb result.resize(crypto_sign_ed25519_BYTES + dataToSign.size); unsigned long long resultSize; - if(crypto_sign_ed25519((unsigned char*)&result[0], &resultSize, (unsigned char*)dataToSign.data, dataToSign.size, (unsigned char*)data) != 0) + if(crypto_sign_ed25519((unsigned char*)&result[0], &resultSize, (unsigned char*)dataToSign.data, dataToSign.size, (unsigned char*)data) < 0) throw DataSignException("Failed to sign data. Is private key invalid?"); if(resultSize != result.size()) @@ -105,7 +105,7 @@ namespace odhtdb KeyPair::KeyPair() { - if(crypto_sign_ed25519_keypair((unsigned char*)publicKey.data, (unsigned char*)privateKey.data) != 0) + if(crypto_sign_ed25519_keypair((unsigned char*)publicKey.data, (unsigned char*)privateKey.data) < 0) throw SignatureGenerationException("Failed to generate signature keypair"); } } |