From 6e4d46f8cf911b82a10e8cd25b65fcc421bbc712 Mon Sep 17 00:00:00 2001 From: Aleksi Lindeman <0xdec05eba@gmail.com> Date: Sat, 14 Apr 2018 19:45:15 +0200 Subject: Store database storage to files, also loading --- src/Database.cpp | 107 +++++------ src/DatabaseStorage.cpp | 442 ++++++++++++++++++++++++++++++++++++++++++--- src/Encryption.cpp | 12 +- src/FileUtils.cpp | 57 ++++++ src/Group.cpp | 6 +- src/LocalUser.cpp | 12 ++ src/LocalUserEncrypted.cpp | 27 +++ src/Log.cpp | 9 +- src/OwnedMemory.cpp | 21 +++ src/PasswordHash.cpp | 23 +++ src/Permission.cpp | 7 + src/Signature.cpp | 9 + src/User.cpp | 2 +- 13 files changed, 645 insertions(+), 89 deletions(-) create mode 100644 src/FileUtils.cpp create mode 100644 src/LocalUser.cpp create mode 100644 src/LocalUserEncrypted.cpp create mode 100644 src/OwnedMemory.cpp create mode 100644 src/PasswordHash.cpp (limited to 'src') diff --git a/src/Database.cpp b/src/Database.cpp index 41b0d73..226aa05 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -60,9 +60,9 @@ namespace odhtdb return DataView(result, allocationSize); } - DatabaseCreateResponse::DatabaseCreateResponse(LocalUser *_nodeAdminUser, const shared_ptr &_key, const shared_ptr &_hash) : + DatabaseCreateResponse::DatabaseCreateResponse(LocalUser *_nodeAdminUser, const shared_ptr &_key, const shared_ptr &_hash) : nodeAdminUser(_nodeAdminUser), - key(move(_key)), + key(_key), hash(_hash) { @@ -73,7 +73,7 @@ namespace odhtdb return nodeAdminUser; } - const shared_ptr DatabaseCreateResponse::getNodeEncryptionKey() const + const shared_ptr DatabaseCreateResponse::getNodeEncryptionKey() const { return key; } @@ -83,10 +83,11 @@ namespace odhtdb return hash; } - Database::Database(const char *bootstrapNodeAddr, u16 port, boost::filesystem::path storageDir) : + Database::Database(const char *bootstrapNodeAddr, u16 port, const boost::filesystem::path &storageDir) : onCreateNodeCallbackFunc(nullptr), onAddNodeCallbackFunc(nullptr), - onAddUserCallbackFunc(nullptr) + onAddUserCallbackFunc(nullptr), + databaseStorage(storageDir) { // TODO: Cache this in storage. It takes pretty long time to generate new identity auto identity = dht::crypto::generateIdentity(); @@ -147,43 +148,43 @@ namespace odhtdb node.join(); } - void Database::seed(const shared_ptr hash, const shared_ptr encryptionKey) + void Database::seed(const DatabaseNode &nodeToSeed) { // 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) - Log::debug("Seeding key: %s", hash->toString().c_str()); - DhtKey dhtKey(*hash); + Log::debug("Seeding key: %s", nodeToSeed.getRequestHash()->toString().c_str()); + DhtKey dhtKey(*nodeToSeed.getRequestHash()); - node.listen(dhtKey.getNewDataListenerKey(), [this, hash, encryptionKey](const shared_ptr &value) + node.listen(dhtKey.getNewDataListenerKey(), [this, nodeToSeed](const shared_ptr &value) { Log::debug("Seed: New data listener received data..."); const Hash requestHash(value->data.data(), value->data.size()); - if(requestHash == *hash) + if(requestHash == *nodeToSeed.getRequestHash()) return true; //return listenCreateData(value, requestHash, encryptionKey); else - return listenAddData(value, requestHash, encryptionKey); + return listenAddData(value, requestHash, nodeToSeed.getRequestHash(), nodeToSeed.getNodeEncryptionKey()); }); 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) + node.listen(InfoHash(responseKey, OPENDHT_INFOHASH_LEN), [this, nodeToSeed](const shared_ptr &value) { const Hash requestHash(value->data.data(), value->data.size()); - if(requestHash == *hash) - return listenCreateData(value, requestHash, encryptionKey); + if(requestHash == *nodeToSeed.getRequestHash()) + return listenCreateData(value, requestHash, nodeToSeed.getNodeEncryptionKey()); else - return listenAddData(value, requestHash, encryptionKey); + return listenAddData(value, requestHash, nodeToSeed.getRequestHash(), nodeToSeed.getNodeEncryptionKey()); }); // 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) + node.listen(dhtKey.getRequestOldDataKey(), [this, nodeToSeed](const shared_ptr &value) { Log::debug("Request: Got request to send old data"); try @@ -193,10 +194,10 @@ namespace odhtdb u8 requestResponseKey[OPENDHT_INFOHASH_LEN]; deserializer.extract(requestResponseKey, OPENDHT_INFOHASH_LEN); - auto requestedData = databaseStorage.getStorage(*hash); + auto requestedData = databaseStorage.getStorage(*nodeToSeed.getRequestHash()); if(!requestedData) { - Log::warn("No data found for hash %s, unable to serve peer", hash->toString().c_str()); + Log::warn("No data found for hash %s, unable to serve peer", nodeToSeed.getRequestHash()->toString().c_str()); return true; } @@ -229,7 +230,7 @@ namespace odhtdb }); sibs::SafeSerializer serializer; - serializer.add((u64)0); // Timestamp in microseconds, fetch data newer than this + serializer.add((u64)0); // Timestamp in microseconds, fetch data newer than this. // TODO: Get timestamp from database storage serializer.add(responseKey, OPENDHT_INFOHASH_LEN); node.put(dhtKey.getRequestOldDataKey(), Value(serializer.getBuffer().data(), serializer.getBuffer().size()), [](bool ok) { @@ -241,13 +242,13 @@ namespace odhtdb //node.listen(ADD_DATA_HASH, bind(&Database::listenAddData, this, _1)); } - unique_ptr Database::create(const std::string &ownerName, const std::string &nodeName) + unique_ptr Database::create(const string &ownerName, const std::string &ownerPlainPassword, const string &nodeName) { // TODO: Should this be declared static? is there any difference in behavior/performance? boost::uuids::random_generator uuidGen; auto adminGroupId = uuidGen(); auto adminGroup = new Group("administrator", adminGroupId.data, ADMIN_PERMISSION); - LocalUser *nodeAdminUser = LocalUser::create(Signature::KeyPair(), ownerName, adminGroup); + LocalUser *nodeAdminUser = LocalUser::create(Signature::KeyPair(), ownerName, adminGroup, ownerPlainPassword); // Header sibs::SafeSerializer serializer; @@ -281,10 +282,10 @@ namespace odhtdb 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(nodeAdminUser, make_shared(key), hashRequestKey); + assert(encryptedBody.getKey().size == ENCRYPTION_KEY_BYTE_SIZE); + auto key = make_shared(new char[encryptedBody.getKey().size], encryptedBody.getKey().size); + memcpy(key->data, encryptedBody.getKey().data, encryptedBody.getKey().size); + return make_unique(nodeAdminUser, move(key), hashRequestKey); } catch (EncryptionException &e) { @@ -301,7 +302,7 @@ namespace odhtdb serializer.add(timestampMicroseconds); serializer.add(DatabaseOperation::ADD_DATA); - DataView encryptionKey(*nodeInfo.getNodeEncryptionKey(), KEY_BYTE_SIZE); + DataView encryptionKey(nodeInfo.getNodeEncryptionKey()->data, ENCRYPTION_KEY_BYTE_SIZE); Encryption encryptedBody(dataToAdd, DataView(), encryptionKey); DataView requestData = combine(serializer, encryptedBody); string signedRequestData = userToPerformActionWith->getPrivateKey().sign(requestData); @@ -334,7 +335,7 @@ namespace odhtdb return nullptr; } - void Database::addUserToGroup(const DatabaseNode &nodeInfo, LocalUser *userToPerformActionWith, const std::string &userToAddName, const Signature::PublicKey &userToAddPublicKey, Group *groupToAddUserTo) + void Database::addUser(const DatabaseNode &nodeInfo, LocalUser *userToPerformActionWith, const string &userToAddName, const Signature::PublicKey &userToAddPublicKey, Group *groupToAddUserTo) { auto groupWithAddUserRights = getGroupWithRightsToAddUserToGroup(userToPerformActionWith->getGroups(), groupToAddUserTo); if(!groupWithAddUserRights) @@ -365,7 +366,7 @@ namespace odhtdb Hash requestDataHash(stagedAddObject.data, stagedAddObject.size); databaseStorage.appendStorage(*nodeInfo.getRequestHash(), requestDataHash, userToPerformActionWith, timestampMicroseconds, (u8*)stagedAddObject.data, stagedAddObject.size); auto userToAdd = RemoteUser::create(userToAddPublicKey, userToAddName, groupToAddUserTo); - databaseStorage.addUser(userToAdd, *nodeInfo.getRequestHash()); + databaseStorage.addUser(*nodeInfo.getRequestHash(), userToAdd); DatabaseAddUserRequest addUserRequest(&*nodeInfo.getRequestHash(), &requestDataHash, timestampMicroseconds, userToPerformActionWith, userToAdd, groupToAddUserTo); if(onAddUserCallbackFunc) @@ -446,7 +447,7 @@ namespace odhtdb return timestamp; } - void Database::deserializeCreateRequest(const std::shared_ptr &value, const Hash &hash, const shared_ptr encryptionKey) + void Database::deserializeCreateRequest(const shared_ptr &value, const Hash &hash, const shared_ptr encryptionKey) { sibs::SafeDeserializer deserializer(value->data.data(), value->data.size()); u16 packetStructureVersion = deserializer.extract(); @@ -472,7 +473,7 @@ namespace odhtdb uint8_t adminGroupId[16]; deserializer.extract(adminGroupId, 16); - if(deserializer.getSize() < NONCE_BYTE_SIZE) + if(deserializer.getSize() < ENCRYPTION_NONCE_BYTE_SIZE) throw sibs::DeserializeException("Unsigned encrypted body is too small (unable to extract nonce)"); auto adminGroup = new Group("administrator", adminGroupId, ADMIN_PERMISSION); @@ -480,11 +481,11 @@ namespace odhtdb auto creatorUser = RemoteUser::create(userPublicKey, "ENCRYPTED USER NAME", adminGroup); databaseStorage.createStorage(hash, adminGroup, creationDate, value->data.data(), value->data.size()); - u8 nonce[NONCE_BYTE_SIZE]; - deserializer.extract(nonce, NONCE_BYTE_SIZE); + u8 nonce[ENCRYPTION_NONCE_BYTE_SIZE]; + deserializer.extract(nonce, ENCRYPTION_NONCE_BYTE_SIZE); DataView dataToDecrypt((void*)deserializer.getBuffer(), deserializer.getSize()); - Decryption decryptedBody(dataToDecrypt, DataView(nonce, NONCE_BYTE_SIZE), DataView(*encryptionKey, KEY_BYTE_SIZE)); + Decryption decryptedBody(dataToDecrypt, DataView(nonce, ENCRYPTION_NONCE_BYTE_SIZE), DataView(encryptionKey->data, ENCRYPTION_KEY_BYTE_SIZE)); sibs::SafeDeserializer bodyDeserializer((const u8*)decryptedBody.getDecryptedText().data, decryptedBody.getDecryptedText().size); u8 creatorNameLength = bodyDeserializer.extract(); @@ -513,7 +514,7 @@ namespace odhtdb return false; } - void Database::deserializeAddRequest(const std::shared_ptr &value, const Hash &requestDataHash, const shared_ptr encryptionKey) + void Database::deserializeAddRequest(const shared_ptr &value, const Hash &requestDataHash, const std::shared_ptr &nodeHash, const shared_ptr encryptionKey) { sibs::SafeDeserializer deserializer(value->data.data(), value->data.size()); char creatorPublicKeyRaw[PUBLIC_KEY_NUM_BYTES]; @@ -541,7 +542,7 @@ namespace odhtdb throw sibs::DeserializeException("Packet is from the future"); DatabaseOperation operation = deserializerUnsigned.extract(); - +#if 0 const Hash *node = databaseStorage.getNodeByUserPublicKey(creatorPublicKey); if(!node) { @@ -551,21 +552,21 @@ namespace odhtdb databaseStorage.addToQuarantine(requestDataHash, creatorPublicKey, creationDate, value->data.data(), value->data.size()); throw RequestQuarantineException(); } - - auto creatorUser = databaseStorage.getUserByPublicKey(creatorPublicKey); +#endif + auto creatorUser = databaseStorage.getUserByPublicKey(*nodeHash, creatorPublicKey); // TODO: Verify there isn't already data with same timestamp for this node. Same for quarantine. // TODO: We might receive 'add' data packet before 'create'. If that happens, we should put it in quarantine and process it later. - databaseStorage.appendStorage(*node, requestDataHash, creatorUser, creationDate, value->data.data(), value->data.size()); + databaseStorage.appendStorage(*nodeHash, requestDataHash, creatorUser, creationDate, value->data.data(), value->data.size()); if(operation == DatabaseOperation::ADD_DATA) { - if(deserializerUnsigned.getSize() < NONCE_BYTE_SIZE) + if(deserializerUnsigned.getSize() < ENCRYPTION_NONCE_BYTE_SIZE) throw sibs::DeserializeException("Unsigned encrypted body is too small (unable to extract nonce)"); - u8 nonce[NONCE_BYTE_SIZE]; - deserializerUnsigned.extract(nonce, NONCE_BYTE_SIZE); + u8 nonce[ENCRYPTION_NONCE_BYTE_SIZE]; + deserializerUnsigned.extract(nonce, ENCRYPTION_NONCE_BYTE_SIZE); DataView dataToDecrypt((void*)deserializerUnsigned.getBuffer(), deserializerUnsigned.getSize()); - Decryption decryptedBody(dataToDecrypt, DataView(nonce, NONCE_BYTE_SIZE), DataView(*encryptionKey, KEY_BYTE_SIZE)); + Decryption decryptedBody(dataToDecrypt, DataView(nonce, ENCRYPTION_NONCE_BYTE_SIZE), DataView(encryptionKey->data, ENCRYPTION_KEY_BYTE_SIZE)); if(!isUserAllowedToAddData(creatorUser)) { @@ -582,7 +583,7 @@ namespace odhtdb } Log::debug("Got add object, timestamp: %zu, data: %.*s", creationDate, decryptedBody.getDecryptedText().size, decryptedBody.getDecryptedText().data); - const DatabaseAddNodeRequest addNodeRequest(node, &requestDataHash, creationDate, creatorUser, decryptedBody.getDecryptedText()); + const DatabaseAddNodeRequest addNodeRequest(&*nodeHash, &requestDataHash, creationDate, creatorUser, decryptedBody.getDecryptedText()); if(onAddNodeCallbackFunc) onAddNodeCallbackFunc(addNodeRequest); } @@ -600,11 +601,13 @@ namespace odhtdb uint8_t groupId[16]; deserializerUnsigned.extract(groupId, 16); - auto group = databaseStorage.getGroupById(groupId); + auto group = databaseStorage.getGroupById(*nodeHash, groupId); if(group) { auto user = RemoteUser::create(userToAddPublicKey, name, group); - databaseStorage.addUser(user, *node); + // TODO: What if we receive packets in wrong order? (maliciously or non-maliciously). You would be able to register a user to a group with given name + // and further registration would be dropped (even if that is the correct one) + if(!databaseStorage.addUser(*nodeHash, user)) return; auto creatorUserGroupWithRights = getGroupWithRightsToAddUserToGroup(creatorUser->getGroups(), group); if(!creatorUserGroupWithRights) @@ -622,7 +625,7 @@ namespace odhtdb } Log::debug("Got add user object, timestamp: %zu, user added: %.*s", creationDate, nameLength, name.c_str()); - DatabaseAddUserRequest addUserRequest(node, &requestDataHash, creationDate, creatorUser, user, group); + DatabaseAddUserRequest addUserRequest(&*nodeHash, &requestDataHash, creationDate, creatorUser, user, group); if(onAddUserCallbackFunc) onAddUserCallbackFunc(addUserRequest); } @@ -639,7 +642,7 @@ namespace odhtdb } } - bool Database::listenCreateData(std::shared_ptr value, const Hash &hash, const shared_ptr encryptionKey) + bool Database::listenCreateData(shared_ptr value, const Hash &hash, const shared_ptr encryptionKey) { Log::debug("Got create data"); try @@ -655,12 +658,12 @@ namespace odhtdb return true; } - bool Database::listenAddData(std::shared_ptr value, const Hash &requestDataHash, const shared_ptr encryptionKey) + bool Database::listenAddData(shared_ptr value, const Hash &requestDataHash, const std::shared_ptr nodeHash, const shared_ptr encryptionKey) { Log::debug("Got add data"); try { - deserializeAddRequest(value, requestDataHash, encryptionKey); + deserializeAddRequest(value, requestDataHash, nodeHash, encryptionKey); //Log::debug("Got add object, timestamp: %zu", addObject.timestamp); } catch (RequestQuarantineException &e) @@ -674,17 +677,17 @@ namespace odhtdb return true; } - void Database::setOnCreateNodeCallback(std::function callbackFunc) + void Database::setOnCreateNodeCallback(function callbackFunc) { onCreateNodeCallbackFunc = callbackFunc; } - void Database::setOnAddNodeCallback(std::function callbackFunc) + void Database::setOnAddNodeCallback(function callbackFunc) { onAddNodeCallbackFunc = callbackFunc; } - void Database::setOnAddUserCallback(std::function callbackFunc) + void Database::setOnAddUserCallback(function callbackFunc) { onAddUserCallbackFunc = callbackFunc; } diff --git a/src/DatabaseStorage.cpp b/src/DatabaseStorage.cpp index 212ca0f..bd98b8b 100644 --- a/src/DatabaseStorage.cpp +++ b/src/DatabaseStorage.cpp @@ -1,13 +1,28 @@ #include "../include/odhtdb/DatabaseStorage.hpp" -#include "../include/odhtdb/User.hpp" +#include "../include/odhtdb/RemoteUser.hpp" +#include "../include/odhtdb/LocalUser.hpp" +#include "../include/odhtdb/LocalUserEncrypted.hpp" #include "../include/odhtdb/Group.hpp" +#include "../include/odhtdb/FileUtils.hpp" +#include "../include/odhtdb/bin2hex.hpp" +#include "../include/odhtdb/PasswordHash.hpp" +#include "../include/odhtdb/Log.hpp" #include #include +#include +#include +#include using namespace std; namespace odhtdb { + enum StorageType : u8 + { + STORAGE_TYPE_CREATE, + STORAGE_TYPE_APPEND + }; + const u64 QUARANTINE_STORAGE_TIME_MICROSECONDS = 60 * 1.0e6; DatabaseStorageObject::DatabaseStorageObject(DataView &_data, u64 _timestamp, const Signature::PublicKey &_creatorPublicKey) : @@ -23,6 +38,263 @@ namespace odhtdb storedTimestamp = chrono::duration_cast(time).count(); } + DatabaseStorage::DatabaseStorage(const boost::filesystem::path &storagePath) : + groupsFilePath(storagePath / "groups"), + usersFilePath(storagePath / "users"), + dataFilePath(storagePath / "data"), + metadataFilePath(storagePath / "metadata") + { + boost::filesystem::create_directories(storagePath); + + bool metadataLoaded = false; + try + { + loadMetadataFromFile(); + metadataLoaded = true; + loadGroupsFromFile(); + loadUsersFromFile(); + loadDataFromFile(); + //loadQuarantineFromFile(); + } + catch(FileException &e) + { + Log::warn("Failed to load data from file: %s, reason: %s. Ignoring...", dataFilePath.string().c_str(), e.what()); + if(!metadataLoaded) + { + sibs::SafeSerializer metadataSerializer; + metadataSerializer.add((u16)0); // Storage version + randombytes_buf(passwordSalt, PASSWORD_SALT_LEN); + metadataSerializer.add(passwordSalt, PASSWORD_SALT_LEN); + fileAppend(metadataFilePath, { metadataSerializer.getBuffer().data(), metadataSerializer.getBuffer().size() }); + } + } + } + + void DatabaseStorage::loadGroupsFromFile() + { + if(!boost::filesystem::exists(groupsFilePath)) return; + + OwnedMemory groupsFileContent = fileGetContent(groupsFilePath); + sibs::SafeDeserializer deserializer((u8*)groupsFileContent.data, groupsFileContent.size); + + while(!deserializer.empty()) + { + Hash nodeHash; + deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE); + + DataViewMap *groupByIdMap = nullptr; + auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash); + if(groupByIdMapIt == nodeGroupByIdMap.end()) + { + groupByIdMap = new DataViewMap(); + nodeGroupByIdMap[nodeHash] = groupByIdMap; + } + else + groupByIdMap = groupByIdMapIt->second; + + u8 groupNameSize = deserializer.extract(); + string groupName; + groupName.resize(groupNameSize); + deserializer.extract((u8*)&groupName[0], groupNameSize); + + u8 groupId[GROUP_ID_LENGTH]; + deserializer.extract(groupId, GROUP_ID_LENGTH); + + u8 permissionLevel = deserializer.extract(); + u32 permissionFlags = deserializer.extract(); + + Group *group = new Group(groupName, groupId, { permissionLevel, permissionFlags }); + (*groupByIdMap)[group->getId()] = group; + + string groupIdStr = bin2hex((const char*)groupId, GROUP_ID_LENGTH); + Log::debug("DatabaseStorage: Loaded group from file: %s", groupIdStr.c_str()); + } + } + + void DatabaseStorage::loadUsersFromFile() + { + if(!boost::filesystem::exists(usersFilePath)) return; + + OwnedMemory usersFileContent = fileGetContent(usersFilePath); + sibs::SafeDeserializer deserializer((u8*)usersFileContent.data, usersFileContent.size); + + while(!deserializer.empty()) + { + Hash nodeHash; + deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE); + + Signature::MapPublicKey *publicKeyUserDataMap = nullptr; + auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash); + if(publicKeyUserDataMapIt == nodePublicKeyUserDataMap.end()) + { + publicKeyUserDataMap = new Signature::MapPublicKey(); + nodePublicKeyUserDataMap[nodeHash] = publicKeyUserDataMap; + } + else + publicKeyUserDataMap = publicKeyUserDataMapIt->second; + + DataViewMap *groupByIdMap = nullptr; + auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash); + if(groupByIdMapIt == nodeGroupByIdMap.end()) + { + groupByIdMap = new DataViewMap(); + nodeGroupByIdMap[nodeHash] = groupByIdMap; + } + else + groupByIdMap = groupByIdMapIt->second; + + User::Type userType = deserializer.extract(); + + u8 usernameSize = deserializer.extract(); + string username; + username.resize(usernameSize); + deserializer.extract((u8*)&username[0], usernameSize); + + u8 userPublicKeyRaw[PUBLIC_KEY_NUM_BYTES]; + deserializer.extract(userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES); + Signature::PublicKey userPublicKey((const char*)userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES); + + UserData *userData = new UserData(); + if(userType == User::Type::LOCAL) + { + EncryptedPrivateKey encryptedPrivateKey; + deserializer.extract(encryptedPrivateKey.nonce, ENCRYPTION_NONCE_BYTE_SIZE); + deserializer.extract(encryptedPrivateKey.encryptedPrivateKey, 16 + PRIVATE_KEY_NUM_BYTES); + userData->user = LocalUserEncrypted::create(userPublicKey, encryptedPrivateKey, username, nullptr); + } + else + { + userData->user = RemoteUser::create(userPublicKey, username, nullptr); + } + + u8 numGroups = deserializer.extract(); + for(int i = 0; i < numGroups; ++i) + { + u8 groupId[GROUP_ID_LENGTH]; + deserializer.extract(groupId, GROUP_ID_LENGTH); + + auto groupIt = groupByIdMap->find(DataView(groupId, GROUP_ID_LENGTH)); + if(groupIt == groupByIdMap->end()) + { + string errMsg = "User group with id "; + errMsg += bin2hex((const char*)groupId, GROUP_ID_LENGTH); + errMsg += " does not exist"; + throw DatabaseStorageCorrupt(errMsg); + } + userData->user->addToGroup(groupIt->second); + } + + (*publicKeyUserDataMap)[userData->user->getPublicKey()] = userData; + } + } + + void DatabaseStorage::loadStorageCreate(sibs::SafeDeserializer &deserializer) + { + u64 timestamp = deserializer.extract(); + + u8 groupId[GROUP_ID_LENGTH]; + deserializer.extract(groupId, GROUP_ID_LENGTH); + + u32 dataSize = deserializer.extract(); + u8 *data = new u8[dataSize]; + deserializer.extract(data, dataSize); + + Hash nodeHash; + deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE); + + auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash); + if(groupByIdMapIt == nodeGroupByIdMap.end()) + { + string errMsg = "No groups exists in node "; + errMsg += nodeHash.toString(); + throw DatabaseStorageCorrupt(errMsg); + } + + auto groupIt = groupByIdMapIt->second->find(DataView(groupId, GROUP_ID_LENGTH)); + if(groupIt == groupByIdMapIt->second->end()) + { + string errMsg = "User group with id "; + errMsg += bin2hex((const char*)groupId, GROUP_ID_LENGTH); + errMsg += " does not exist in node "; + errMsg += nodeHash.toString(); + throw DatabaseStorageCorrupt(errMsg); + } + + DatabaseStorageObjectList *databaseStorageObjectList = new DatabaseStorageObjectList(); + databaseStorageObjectList->createdTimestamp = timestamp; + databaseStorageObjectList->groups.push_back(groupIt->second); + databaseStorageObjectList->data = DataView(data, dataSize); + storageMap[nodeHash] = databaseStorageObjectList; + } + + void DatabaseStorage::loadStorageAppend(sibs::SafeDeserializer &deserializer) + { + u64 timestamp = deserializer.extract(); + + u8 creatorPublicKeyRaw[PUBLIC_KEY_NUM_BYTES]; + deserializer.extract(creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES); + Signature::PublicKey creatorPublicKey((const char*)creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES); + + u32 dataSize = deserializer.extract(); + u8 *data = new u8[dataSize]; + deserializer.extract(data, dataSize); + + Hash nodeHash; + deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE); + + Hash dataHash; + deserializer.extract((u8*)dataHash.getData(), HASH_BYTE_SIZE); + storedDataHash.insert(dataHash); + + auto storageIt = storageMap.find(nodeHash); + if(storageIt == storageMap.end()) + { + string errMsg = "Database storage with hash "; + errMsg += nodeHash.toString(); + errMsg += " not found"; + throw DatabaseStorageCorrupt(errMsg); + } + + DataView storageData { data, dataSize }; + DatabaseStorageObject *databaseStorageObject = new DatabaseStorageObject(storageData, timestamp, creatorPublicKey); + storageIt->second->objects.push_back(databaseStorageObject); + } + + void DatabaseStorage::loadDataFromFile() + { + if(!boost::filesystem::exists(dataFilePath)) return; + + OwnedMemory dataFileContent = fileGetContent(dataFilePath); + sibs::SafeDeserializer deserializer((u8*)dataFileContent.data, dataFileContent.size); + + while(!deserializer.empty()) + { + StorageType storageType = deserializer.extract(); + switch(storageType) + { + case STORAGE_TYPE_CREATE: + loadStorageCreate(deserializer); + break; + case STORAGE_TYPE_APPEND: + loadStorageAppend(deserializer); + break; + } + } + } + + void DatabaseStorage::loadMetadataFromFile() + { + OwnedMemory metadataFileContent = fileGetContent(metadataFilePath); + sibs::SafeDeserializer deserializer((u8*)metadataFileContent.data, metadataFileContent.size); + + u16 storageVersion = deserializer.extract(); + + u8 passwordSalt[PASSWORD_SALT_LEN]; + deserializer.extract(passwordSalt, PASSWORD_SALT_LEN); + + assert(deserializer.empty()); + } + void DatabaseStorage::createStorage(const Hash &hash, Group *creatorGroup, u64 timestamp, const u8 *data, usize dataSize) { if(storageMap.find(hash) != storageMap.end()) @@ -33,18 +305,30 @@ namespace odhtdb throw DatabaseStorageAlreadyExists(errMsg); } + addGroup(hash, creatorGroup); + for(auto user : creatorGroup->getUsers()) + { + addUser(hash, (User*)user); + } + DatabaseStorageObjectList *databaseStorageObjectList = new DatabaseStorageObjectList(); databaseStorageObjectList->createdTimestamp = timestamp; databaseStorageObjectList->groups.push_back(creatorGroup); databaseStorageObjectList->data = DataView(new u8[dataSize], dataSize); memcpy(databaseStorageObjectList->data.data, data, dataSize); storageMap[hash] = databaseStorageObjectList; - groupByIdMap[creatorGroup->getId()] = creatorGroup; - for(auto user : creatorGroup->getUsers()) - { - addUser((User*)user, hash); - } + sibs::SafeSerializer serializer; + serializer.add(STORAGE_TYPE_CREATE); + serializer.add(timestamp); + serializer.add((u8*)creatorGroup->getId().data, GROUP_ID_LENGTH); + + serializer.add((u32)dataSize); + serializer.add(data, dataSize); + + serializer.add((u8*)hash.getData(), HASH_BYTE_SIZE); + + fileAppend(dataFilePath, { serializer.getBuffer().data(), serializer.getBuffer().size() }); } void DatabaseStorage::appendStorage(const Hash &nodeHash, const Hash &dataHash, const User *creatorUser, u64 timestamp, const u8 *data, usize dataSize) @@ -70,6 +354,19 @@ namespace odhtdb memcpy(storageData.data, data, dataSize); DatabaseStorageObject *databaseStorageObject = new DatabaseStorageObject(storageData, timestamp, creatorUser->getPublicKey()); it->second->objects.push_back(databaseStorageObject); + + sibs::SafeSerializer serializer; + serializer.add(STORAGE_TYPE_APPEND); + serializer.add(timestamp); + serializer.add((u8*)creatorUser->getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES); + + serializer.add((u32)dataSize); + serializer.add(data, dataSize); + + serializer.add((u8*)nodeHash.getData(), HASH_BYTE_SIZE); + serializer.add((u8*)dataHash.getData(), HASH_BYTE_SIZE); + + fileAppend(dataFilePath, { serializer.getBuffer().data(), serializer.getBuffer().size() }); } void DatabaseStorage::addToQuarantine(const Hash &dataHash, const Signature::PublicKey &creatorPublicKey, u64 timestamp, const u8 *data, usize dataSize) @@ -88,10 +385,103 @@ namespace odhtdb quarantineStorageMap[creatorPublicKey].emplace_back(databaseQuarantineStorageObject); } - void DatabaseStorage::addUser(User *user, const Hash &hash) + bool DatabaseStorage::addGroup(const Hash &nodeHash, Group *group) + { + DataViewMap *groupByIdMap = nullptr; + auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash); + if(groupByIdMapIt == nodeGroupByIdMap.end()) + { + groupByIdMap = new DataViewMap(); + nodeGroupByIdMap[nodeHash] = groupByIdMap; + } + else + groupByIdMap = groupByIdMapIt->second; + + if(groupByIdMap->find(group->getId()) != groupByIdMap->end()) + return false; + + (*groupByIdMap)[group->getId()] = group; + + sibs::SafeSerializer serializer; + + serializer.add((u8*)nodeHash.getData(), HASH_BYTE_SIZE); + + serializer.add((u8)group->getName().size()); + serializer.add((u8*)group->getName().data(), group->getName().size()); + + serializer.add((u8*)group->getId().data, GROUP_ID_LENGTH); + + serializer.add(group->getPermission().getPermissionLevel()); + serializer.add(group->getPermission().getPermissionFlags()); + + fileAppend(groupsFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); + + return true; + } + + bool DatabaseStorage::addUser(const Hash &nodeHash, User *user) { - userPublicKeyNodeMap[user->getPublicKey()] = new Hash(hash); - publicKeyUserMap[user->getPublicKey()] = user; + Signature::MapPublicKey *publicKeyUserDataMap = nullptr; + auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash); + if(publicKeyUserDataMapIt == nodePublicKeyUserDataMap.end()) + { + publicKeyUserDataMap = new Signature::MapPublicKey(); + nodePublicKeyUserDataMap[nodeHash] = publicKeyUserDataMap; + } + else + publicKeyUserDataMap = publicKeyUserDataMapIt->second; + + if(publicKeyUserDataMap->find(user->getPublicKey()) != publicKeyUserDataMap->end()) + return false; + + UserData *userData = new UserData(); + userData->user = user; + if(user->getType() == User::Type::LOCAL) + { + LocalUser *localUser = static_cast(user); + //DataView plainPassword((void*)localUser->getPlainPassword().data(), localUser->getPlainPassword().size()); + OwnedMemory hashedPassword = hashPassword(DataView((void*)localUser->getPlainPassword().data(), localUser->getPlainPassword().size()), DataView(passwordSalt, PASSWORD_SALT_LEN)); + memcpy(userData->hashedPassword, hashedPassword.data, hashedPassword.size); + } + else + { + memset(userData->hashedPassword, 0, HASHED_PASSWORD_LEN); + } + (*publicKeyUserDataMap)[user->getPublicKey()] = userData; + + // TODO: Instead of directly saving user to file, maybe user should be added to buffer and then save to file after we have several users (performance improvement) + + sibs::SafeSerializer serializer; + + serializer.add((u8*)nodeHash.getData(), HASH_BYTE_SIZE); + + serializer.add(user->getType()); + + serializer.add((u8)user->getName().size()); + serializer.add((u8*)user->getName().data(), user->getName().size()); + + serializer.add((u8*)user->getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES); + if(user->getType() == User::Type::LOCAL) + { + LocalUser *localUser = static_cast(user); + static_assert(HASHED_PASSWORD_LEN == ENCRYPTION_KEY_BYTE_SIZE, "Encryption key size has changed but hashed password size hasn't"); + Encryption encryptedPrivateKey(DataView((void*)localUser->getPrivateKey().getData(), localUser->getPrivateKey().getSize()), + DataView(), + DataView(userData->hashedPassword, HASHED_PASSWORD_LEN)); + serializer.add((u8*)encryptedPrivateKey.getNonce().data, ENCRYPTION_NONCE_BYTE_SIZE); + assert(16 + PRIVATE_KEY_NUM_BYTES == encryptedPrivateKey.getCipherText().size); + serializer.add((u8*)encryptedPrivateKey.getCipherText().data, encryptedPrivateKey.getCipherText().size); + } + + serializer.add((u8)user->getGroups().size()); + for(Group *group : user->getGroups()) + { + serializer.add((u8*)group->getId().data, GROUP_ID_LENGTH); + } + + fileAppend(usersFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); + + return true; } const DatabaseStorageObjectList* DatabaseStorage::getStorage(const Hash &hash) const @@ -102,33 +492,41 @@ namespace odhtdb return nullptr; } - const Hash* DatabaseStorage::getNodeByUserPublicKey(const Signature::PublicKey &userPublicKey) const + Group* DatabaseStorage::getGroupById(const Hash &nodeHash, uint8_t groupId[GROUP_ID_LENGTH]) { - auto it = userPublicKeyNodeMap.find(userPublicKey); - if(it != userPublicKeyNodeMap.end()) - return it->second; + auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash); + if(groupByIdMapIt != nodeGroupByIdMap.end()) + { + auto it = groupByIdMapIt->second->find(DataView(groupId, GROUP_ID_LENGTH)); + if(it != groupByIdMapIt->second->end()) + return it->second; + } return nullptr; } - // Returns nullptr if no user with public key exists - const User* DatabaseStorage::getUserByPublicKey(const Signature::PublicKey &userPublicKey) const + User* DatabaseStorage::getUserByPublicKey(const Hash &nodeHash, Signature::PublicKey &userPublicKey) { - auto it = publicKeyUserMap.find(userPublicKey); - if(it != publicKeyUserMap.end()) - return it->second; + auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash); + if(publicKeyUserDataMapIt != nodePublicKeyUserDataMap.end()) + { + auto it = publicKeyUserDataMapIt->second->find(userPublicKey); + if(it != publicKeyUserDataMapIt->second->end()) + return it->second->user; + } return nullptr; } - Group* DatabaseStorage::getGroupById(uint8_t groupId[16]) + const Signature::MapPublicKey* DatabaseStorage::getUsersData(const Hash &nodeHash) const { - auto it = groupByIdMap.find(DataView(groupId, 16)); - if(it != groupByIdMap.end()) - return it->second; + auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash); + if(publicKeyUserDataMapIt != nodePublicKeyUserDataMap.end()) + return publicKeyUserDataMapIt->second; return nullptr; } void DatabaseStorage::update() { + // TODO: Modify this to iterate backwards. Because list is sorted in order of timestamp, we can remove data in range auto time = chrono::high_resolution_clock::now().time_since_epoch(); auto timeMicroseconds = chrono::duration_cast(time).count(); for(auto mapIt = quarantineStorageMap.begin(); mapIt != quarantineStorageMap.end(); ) diff --git a/src/Encryption.cpp b/src/Encryption.cpp index 7f8700b..e67c719 100644 --- a/src/Encryption.cpp +++ b/src/Encryption.cpp @@ -12,14 +12,14 @@ namespace odhtdb if(_key.data) { - if(_key.size != KEY_BYTE_SIZE) + if(_key.size != ENCRYPTION_KEY_BYTE_SIZE) throw EncryptionException("Encryption key is wrong size"); memcpy(key, _key.data, _key.size); } else crypto_aead_xchacha20poly1305_ietf_keygen(key); - randombytes_buf(nonce, NONCE_BYTE_SIZE); + randombytes_buf(nonce, ENCRYPTION_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"); } @@ -31,12 +31,12 @@ namespace odhtdb DataView Encryption::getKey() const { - return DataView((void*)key, KEY_BYTE_SIZE); + return DataView((void*)key, ENCRYPTION_KEY_BYTE_SIZE); } DataView Encryption::getNonce() const { - return DataView((void*)nonce, NONCE_BYTE_SIZE); + return DataView((void*)nonce, ENCRYPTION_NONCE_BYTE_SIZE); } DataView Encryption::getCipherText() const @@ -49,10 +49,10 @@ namespace odhtdb decryptedText = new unsigned char[data.size]; decryptedTextLength = data.size; - if(nonce.size < NONCE_BYTE_SIZE) + if(nonce.size < ENCRYPTION_NONCE_BYTE_SIZE) throw DecryptionException("Nonce is not big enough"); - if(key.size < KEY_BYTE_SIZE) + if(key.size < ENCRYPTION_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) diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp new file mode 100644 index 0000000..3a78a63 --- /dev/null +++ b/src/FileUtils.cpp @@ -0,0 +1,57 @@ +#include "../include/odhtdb/FileUtils.hpp" +#include "../include/odhtdb/env.hpp" + +using namespace std; + +namespace odhtdb +{ + OwnedMemory fileGetContent(const boost::filesystem::path &filepath) + { +#if OS_FAMILY == OS_FAMILY_POSIX + FILE *file = fopen(filepath.string().c_str(), "rb"); +#else + FILE *file = _wfopen(filepath.wstring().c_str(), L"rb"); +#endif + if(!file) + { + int error = errno; + string errMsg = "Failed to open file: "; + errMsg += filepath.string(); + errMsg += "; reason: "; + errMsg += strerror(error); + throw FileException(errMsg); + } + + fseek(file, 0, SEEK_END); + size_t fileSize = ftell(file); + fseek(file, 0, SEEK_SET); + + char *result = new char[fileSize]; + fread(result, 1, fileSize, file); + fclose(file); + return { result, fileSize }; + } + + void fileAppend(const boost::filesystem::path &filepath, const DataView &data) + { +#if OS_FAMILY == OS_FAMILY_POSIX + FILE *file = fopen(filepath.string().c_str(), "ab+"); +#else + FILE *file = _wfopen(filepath.wstring().c_str(), L"ab+"); +#endif + if(!file) + { + int error = errno; + string errMsg = "Failed to append to file: "; + errMsg += filepath.string(); + errMsg += "; reason: "; + errMsg += strerror(error); + throw FileException(errMsg); + } + + flockfile(file); + setbuf(file, NULL); + fwrite(data.data, 1, data.size, file); + fclose(file); + } +} diff --git a/src/Group.cpp b/src/Group.cpp index caf3e05..a99fdf6 100644 --- a/src/Group.cpp +++ b/src/Group.cpp @@ -6,13 +6,13 @@ using namespace std; namespace odhtdb { - Group::Group(const string &_name, uint8_t _id[16], const Permission &_permission) : + Group::Group(const string &_name, uint8_t _id[GROUP_ID_LENGTH], const Permission &_permission) : name(_name), permission(_permission) { if(name.size() > 255) throw GroupNameTooLongException(name); - memcpy(id, _id, 16); + memcpy(id, _id, GROUP_ID_LENGTH); } Group::~Group() @@ -32,7 +32,7 @@ namespace odhtdb DataView Group::getId() const { - return { (void*)id, 16 }; + return { (void*)id, GROUP_ID_LENGTH }; } const Permission& Group::getPermission() const diff --git a/src/LocalUser.cpp b/src/LocalUser.cpp new file mode 100644 index 0000000..660d09a --- /dev/null +++ b/src/LocalUser.cpp @@ -0,0 +1,12 @@ +#include "../include/odhtdb/LocalUser.hpp" + +namespace odhtdb +{ + LocalUser::LocalUser(const Signature::KeyPair &_keyPair, const std::string &name, Group *group, const std::string &_plainPassword) : + User(User::Type::LOCAL, name, group), + keyPair(_keyPair), + plainPassword(_plainPassword) + { + + } +} diff --git a/src/LocalUserEncrypted.cpp b/src/LocalUserEncrypted.cpp new file mode 100644 index 0000000..1c22488 --- /dev/null +++ b/src/LocalUserEncrypted.cpp @@ -0,0 +1,27 @@ +#include "../include/odhtdb/LocalUserEncrypted.hpp" +#include "../include/odhtdb/PasswordHash.hpp" +#include + +namespace odhtdb +{ + EncryptedPrivateKey::EncryptedPrivateKey() + { + memset(nonce, 0, ENCRYPTION_NONCE_BYTE_SIZE); + memset(encryptedPrivateKey, 0, 16 + PRIVATE_KEY_NUM_BYTES); + } + + EncryptedPrivateKey::EncryptedPrivateKey(const EncryptedPrivateKey &other) + { + memcpy(nonce, other.nonce, ENCRYPTION_NONCE_BYTE_SIZE); + memcpy(encryptedPrivateKey, other.encryptedPrivateKey, 16 + PRIVATE_KEY_NUM_BYTES); + } + + Signature::PrivateKey EncryptedPrivateKey::decrypt(const DataView &plainPassword, const DataView &salt) const + { + OwnedMemory hashedPassword = hashPassword(plainPassword, salt); + Decryption decryptedPrivateKey(DataView((void*)encryptedPrivateKey, 16 + PRIVATE_KEY_NUM_BYTES), + DataView((void*)nonce, ENCRYPTION_NONCE_BYTE_SIZE), + DataView(hashedPassword.data, hashedPassword.size)); + return { (const char*)decryptedPrivateKey.getDecryptedText().data, decryptedPrivateKey.getDecryptedText().size }; + } +} diff --git a/src/Log.cpp b/src/Log.cpp index 9c06a62..0bdc0a6 100644 --- a/src/Log.cpp +++ b/src/Log.cpp @@ -2,15 +2,14 @@ #include #include -static std::mutex mutexDebug; - namespace odhtdb { // TODO: Add color (if output is tty)? + static std::mutex mutexLog; void Log::debug(const char *fmt, ...) { - std::lock_guard lock(mutexDebug); + std::lock_guard lock(mutexLog); va_list args; va_start(args, fmt); fputs("Debug: ", stdout); @@ -21,7 +20,7 @@ namespace odhtdb void Log::warn(const char *fmt, ...) { - std::lock_guard lock(mutexDebug); + std::lock_guard lock(mutexLog); va_list args; va_start(args, fmt); fputs("Warning: ", stdout); @@ -32,7 +31,7 @@ namespace odhtdb void Log::error(const char *fmt, ...) { - std::lock_guard lock(mutexDebug); + std::lock_guard lock(mutexLog); va_list args; va_start(args, fmt); fputs("Error: ", stderr); diff --git a/src/OwnedMemory.cpp b/src/OwnedMemory.cpp new file mode 100644 index 0000000..1d53faf --- /dev/null +++ b/src/OwnedMemory.cpp @@ -0,0 +1,21 @@ +#include "../include/odhtdb/OwnedMemory.hpp" +#include + +namespace odhtdb +{ + OwnedMemory::OwnedMemory(OwnedMemory &&other) + { + data = other.data; + size = other.size; + + other.data = nullptr; + other.size = 0; + } + + OwnedMemory::~OwnedMemory() + { + free(data); + data = nullptr; + size = 0; + } +} diff --git a/src/PasswordHash.cpp b/src/PasswordHash.cpp new file mode 100644 index 0000000..f6d3713 --- /dev/null +++ b/src/PasswordHash.cpp @@ -0,0 +1,23 @@ +#include "../include/odhtdb/PasswordHash.hpp" +#include + +namespace odhtdb +{ + OwnedMemory hashPassword(const DataView &plainPassword, const DataView &salt) + { + OwnedMemory result; + + const uint32_t tCost = 2; + const uint32_t mCost = 1 << 16; + const uint32_t parallelism = 1; + const uint32_t HASHLEN = 32; + + result.data = new uint8_t[HASHLEN]; + result.size = HASHLEN; + + if(argon2i_hash_raw(tCost, mCost, parallelism, plainPassword.data, plainPassword.size, salt.data, salt.size, result.data, HASHLEN) != ARGON2_OK) + throw std::runtime_error("Failed to hash password"); + + return result; + } +} diff --git a/src/Permission.cpp b/src/Permission.cpp index 57c1f11..dc1e9b6 100644 --- a/src/Permission.cpp +++ b/src/Permission.cpp @@ -2,6 +2,13 @@ namespace odhtdb { + Permission::Permission(u8 _permissionLevel, u32 _permissionFlags) : + permissionLevel(_permissionLevel), + permissionFlags(_permissionFlags) + { + + } + Permission::Permission(u8 _permissionLevel, std::initializer_list permissions) : permissionLevel(_permissionLevel) { diff --git a/src/Signature.cpp b/src/Signature.cpp index b6fb12a..3fd52ee 100644 --- a/src/Signature.cpp +++ b/src/Signature.cpp @@ -64,6 +64,8 @@ namespace odhtdb return bin2hex(data, PUBLIC_KEY_NUM_BYTES); } + PrivateKey PrivateKey::ZERO("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", PRIVATE_KEY_NUM_BYTES); + PrivateKey::PrivateKey(const char *_data, size_t size) { if(size != PRIVATE_KEY_NUM_BYTES) @@ -113,5 +115,12 @@ namespace odhtdb if(crypto_sign_ed25519_keypair((unsigned char*)publicKey.data, (unsigned char*)privateKey.data) < 0) throw SignatureGenerationException("Failed to generate signature keypair"); } + + KeyPair::KeyPair(const PublicKey &_publicKey, const PrivateKey &_privateKey) : + publicKey(_publicKey), + privateKey(_privateKey) + { + + } } } diff --git a/src/User.cpp b/src/User.cpp index 7b90872..1fb4a11 100644 --- a/src/User.cpp +++ b/src/User.cpp @@ -3,7 +3,7 @@ namespace odhtdb { - User::User(const std::string &_name, Group *group) : name(_name) + User::User(Type _type, const std::string &_name, Group *group) : type(_type), name(_name) { if(name.size() > 255) throw UserNameTooLongException(name); -- cgit v1.2.3