aboutsummaryrefslogtreecommitdiff
path: root/include/odhtdb/DatabaseStorage.hpp
blob: 9cfe12d3f9896ee04ae31f0f3d16611d9108fa56 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#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 <vector>
#include <stdexcept>
#include <boost/filesystem/path.hpp>
#include <sibs/SafeDeserializer.hpp>
#include <opendht/crypto.h>

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<Group*> groups;
        std::vector<DatabaseStorageObject*> 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<DatabaseStorageObjectList*>;
    using DatabaseStorageQuarantineMap = Signature::MapPublicKey<std::vector<DatabaseStorageQuarantineObject*>>;
    
    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<Group*>* 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<User*>* 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<NodeLocalUser> 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<bool, std::shared_ptr<OwnedMemory>> 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<OwnedMemory> decryptionKey);
        bool decryptNodeAppendedData(const Hash &nodeHash, DatabaseStorageObject *databaseAppendObject, const std::shared_ptr<OwnedMemory> decryptionKey);
    private:
        Database *database;
        DatabaseStorageMap storageMap;
        DatabaseStorageQuarantineMap quarantineStorageMap;
        MapHash<DatabaseStorageObject*> storedDataHash; // Prevent duplicate data from being added
        MapHash<Signature::MapPublicKey<User*>*> nodePublicKeyUserDataMap;
        MapHash<DataViewMap<Group*>*> nodeGroupByIdMap;
        MapHash<std::shared_ptr<OwnedMemory>> nodeDecryptionKeyMap;
        std::unordered_map<std::string, LocalUserEncrypted*> 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<dht::crypto::PrivateKey>, std::shared_ptr<dht::crypto::Certificate>> identity;
    };
}