#pragma once #include "types.hpp" #include "Hash.hpp" #include "DataView.hpp" #include "Signature.hpp" #include "Encryption.hpp" #include "Group.hpp" #include "LocalUser.hpp" #include "LocalUserEncrypted.hpp" #include "OwnedMemory.hpp" #include "DatabaseOperation.hpp" #include #include #include #include #include namespace odhtdb { class Database; struct DatabaseStorageObjectDecrypted { DatabaseOperation operation; OwnedMemory data; }; struct DatabaseStorageObject { Hash requestHash; DataView data; u64 createdTimestamp; // In microseconds Signature::PublicKey creatorPublicKey; DatabaseStorageObjectDecrypted decryptedObject; DatabaseStorageObject(const Hash &_requestHash, DataView &_data, u64 _timestamp, const Signature::PublicKey &_creatorPublicKey); }; struct DatabaseStorageObjectList { Signature::PublicKey creatorPublicKey; DataView data; u32 offsetToEncryptedData; u64 createdTimestamp; // In microseconds std::string nodeName; bool isDecrypted; std::vector groups; std::vector objects; DatabaseStorageObjectList(const Signature::PublicKey &_creatorPublicKey) : creatorPublicKey(_creatorPublicKey), isDecrypted(false) { } }; struct DatabaseStorageQuarantineObject { DataView data; u64 createdTimestamp; // In microseconds u64 storedTimestamp; // In microseconds Signature::PublicKey creatorPublicKey; DatabaseStorageQuarantineObject(DataView &_data, u64 _timestamp, const Signature::PublicKey &_creatorPublicKey); }; class DatabaseStorageException : public std::runtime_error { public: DatabaseStorageException(const std::string &errMsg) : std::runtime_error(errMsg) {} virtual ~DatabaseStorageException() {} }; class DatabaseStorageAlreadyExists : public DatabaseStorageException { public: DatabaseStorageAlreadyExists(const std::string &errMsg) : DatabaseStorageException(errMsg) {} }; class DatabaseStorageNotFound : public DatabaseStorageException { public: DatabaseStorageNotFound(const std::string &errMsg) : DatabaseStorageException(errMsg) {} }; class DatabaseStorageCorrupt : public DatabaseStorageException { public: DatabaseStorageCorrupt(const std::string &errMsg) : DatabaseStorageException(errMsg) {} }; class DatabaseStorageNoSuchLocalStorageUser : public DatabaseStorageException { public: DatabaseStorageNoSuchLocalStorageUser(const std::string &errMsg) : DatabaseStorageException(errMsg) {} }; class DatabaseStorageWrongPassword : public DatabaseStorageException { public: DatabaseStorageWrongPassword(const std::string &errMsg) : DatabaseStorageException(errMsg) {} }; using DatabaseStorageMap = MapHash; using DatabaseStorageQuarantineMap = Signature::MapPublicKey>; const int PASSWORD_SALT_LEN = 16; const int HASHED_PASSWORD_LEN = 32; struct NodeLocalUser { Hash nodeHash; LocalUser *localUser; }; class DatabaseStorage { public: // Throws DatabaseStorageCorrupt if storage is corrupted DatabaseStorage(Database *database, const boost::filesystem::path &storagePath); // Throws DatabaseStorageAlreadyExists if data with hash already exists void createStorage(const Hash &hash, Group *creatorGroup, u64 timestamp, const u8 *data, usize dataSize, u32 offsetToEncryptedData); // Throws DatabaseStorageNotFound if data with @nodeHash hash has not been created yet. // Throws DatabaseStorageAlreadyExists if same data has been added before (hash of @data, in @dataHash) void appendStorage(const Hash &nodeHash, const Hash &dataHash, DatabaseOperation operation, const User *creatorUser, u64 timestamp, const u8 *data, usize dataSize, const DataView &encryptedDataView); // Throws DatabaseStorageAlreadyExists if same data has been added before (hash of @data, in @dataHash) void addToQuarantine(const Hash &dataHash, const Signature::PublicKey &creatorPublicKey, u64 timestamp, const u8 *data, usize dataSize); // Return false if group with id already exists, otherwise return true bool addGroup(const Hash &nodeHash, Group *group); // Return false if user public key already exists, otherwise return true bool addUser(const Hash &nodeHash, User *user); // Returns nullptr if no storage with provided hash exists const DatabaseStorageObjectList* getStorage(const Hash &hash) const; // Returns nullptr if node @nodeHash doesn't exist const DataViewMap* getNodeGroups(const Hash &nodeHash); // Returns nullptr if a group with id @groupId doesn't exist in node @nodeHash or if no node with id @nodeHash exists Group* getGroupById(const Hash &nodeHash, uint8_t groupId[GROUP_ID_LENGTH]) const; // Returns nullptr if node @nodeHash doesn't exist const Signature::MapPublicKey* getNodeUsers(const Hash &nodeHash); // Returns nullptr if a user with public key @publicKey doesn't exist in node @nodeHash or if no node with id @nodeHash exists User* getUserByPublicKey(const Hash &nodeHash, const Signature::PublicKey &userPublicKey) const; // Username and key pair has to be unique, returns true on success bool storeLocalUser(const std::string &username, const Signature::KeyPair &keyPair, const std::string &password); // Returns public key and private key of encrypted local user. // Throws DatabaseStorageNoSuchLocalStorageUser if user does not exist in local storage. // Throws DatabaseStorageWrongPassword if password for the stored local user is wrong. Signature::KeyPair decryptLocalEncryptedUser(const std::string &username, const std::string &password); // Get stored local user by public & private key in all nodes they exist. // Creates a new user object and replaces user object in the nodes. // Safe to call multiple times. std::vector getLocalNodeUsers(const Signature::KeyPair &keyPair); // Returns true and node decryption key if node exists and we have the decryption key, // otherwise return false and OwnedMemory with data set to nullptr std::pair> getNodeDecryptionKey(const Hash &nodeHash); void setNodeDecryptionKey(const Hash &nodeHash, const DataView &decryptionKey); const dht::crypto::Identity& getIdentity() const; // Update storage state (remove quarantine objects if they are too old, etc) void update(); private: void loadGroupsFromFile(); void loadUsersFromFile(); void loadDataFromFile(); void loadLocalUsersFromFile(); void loadNodeDecryptionKeysFromFile(); void loadDecryptedDataFromFile(); void loadMetadataFromFile(); void loadStorageCreate(sibs::SafeDeserializer &deserializer); void loadStorageAppend(sibs::SafeDeserializer &deserializer); void loadDecryptedStorageCreate(sibs::SafeDeserializer &deserializer); void loadDecryptedStorageAddData(sibs::SafeDeserializer &deserializer); void loadDecryptedStorageAddUser(sibs::SafeDeserializer &deserializer); bool decryptNodeData(const Hash &nodeHash, DatabaseStorageObjectList *databaseCreateObject, const std::shared_ptr decryptionKey); bool decryptNodeAppendedData(const Hash &nodeHash, DatabaseStorageObject *databaseAppendObject, const std::shared_ptr decryptionKey); private: Database *database; DatabaseStorageMap storageMap; DatabaseStorageQuarantineMap quarantineStorageMap; MapHash storedDataHash; // Prevent duplicate data from being added MapHash*> nodePublicKeyUserDataMap; MapHash*> nodeGroupByIdMap; MapHash> nodeDecryptionKeyMap; std::unordered_map nameLocalUsersMap; boost::filesystem::path groupsFilePath; boost::filesystem::path usersFilePath; boost::filesystem::path dataFilePath; boost::filesystem::path metadataFilePath; boost::filesystem::path localUsersFilePath; boost::filesystem::path nodeDecryptionKeysFilePath; boost::filesystem::path decryptedDataFilePath; u8 passwordSalt[PASSWORD_SALT_LEN]; std::pair, std::shared_ptr> identity; }; }