From 2ffb47d0043e57707474e5ae811f97c2e5e93f25 Mon Sep 17 00:00:00 2001 From: Aleksi Lindeman <0xdec05eba@gmail.com> Date: Mon, 5 Mar 2018 22:45:56 +0100 Subject: 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. --- src/Database.cpp | 385 +++++++++++++++++++++++++++++++++--------------- src/DatabaseStorage.cpp | 36 +++-- src/DhtKey.cpp | 22 +++ src/Encryption.cpp | 60 ++++++-- src/Group.cpp | 11 +- src/Hash.cpp | 55 +++++++ src/Signature.cpp | 6 +- 7 files changed, 421 insertions(+), 154 deletions(-) create mode 100644 src/DhtKey.cpp create mode 100644 src/Hash.cpp (limited to 'src') 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 #include -#include +#include #include #include #include @@ -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 &_key, const shared_ptr &_hash) : + key(move(_key)), + hash(_hash) + { + + } + + const shared_ptr DatabaseCreateResponse::getNodeEncryptionKey() const + { + return key; + } + + const shared_ptr 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, const shared_ptr 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) + { + 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) + { + 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) + { + printf("Request: Got request to send old data\n"); + try + { + sibs::SafeDeserializer deserializer(value->data.data(), value->data.size()); + u64 dataStartTimestamp = deserializer.extract(); + 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 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 hashRequestKey = make_shared(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(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(make_shared(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 signedData = make_unique(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 &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 &value) + DatabaseCreateRequest Database::deserializeCreateRequest(const std::shared_ptr &value, const Hash &hash, const shared_ptr 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(); - - u8 adminGroupNameSize = deserializer.extract(); - string adminGroupName; - adminGroupName.resize(adminGroupNameSize); - deserializer.extract((u8*)&adminGroupName[0], adminGroupNameSize); - result.primaryAdminGroup = new Group(adminGroupName); - - u8 numUsers = deserializer.extract(); - for(int i = 0; i < numUsers; ++i) + u16 packetStructureVersion = deserializer.extract(); + 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(); - 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(); + // 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(); + string creatorName; + creatorName.resize(creatorNameLength); + bodyDeserializer.extract((u8*)&creatorName[0], creatorNameLength); + + u8 nameLength = bodyDeserializer.extract(); + string name; + name.resize(nameLength); + bodyDeserializer.extract((u8*)&name[0], nameLength); + + return { creationDate, adminGroup, move(name) }; } - StagedAddObject Database::deserializeAddRequest(const std::shared_ptr &value) + DatabaseAddRequest Database::deserializeAddRequest(const std::shared_ptr &value, const Hash &hash, const shared_ptr encryptionKey) { + /* StagedAddObject result; sibs::SafeDeserializer deserializer(value->data.data(), value->data.size()); @@ -277,47 +432,41 @@ namespace odhtdb result.data = make_unique(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 value) + bool Database::listenCreateData(std::shared_ptr value, const Hash &hash, const shared_ptr 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 value) + bool Database::listenAddData(std::shared_ptr value, const Hash &hash, const shared_ptr 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 using namespace std; namespace odhtdb { - void DatabaseStorage::createStorage(const Key &key, vector &&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 +#include #include #include 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& Group::getUsers() const + const vector& 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 +#include +#include + +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"); } } -- cgit v1.2.3