aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Database.cpp385
-rw-r--r--src/DatabaseStorage.cpp36
-rw-r--r--src/DhtKey.cpp22
-rw-r--r--src/Encryption.cpp60
-rw-r--r--src/Group.cpp11
-rw-r--r--src/Hash.cpp55
-rw-r--r--src/Signature.cpp6
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");
}
}