aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.vscode/c_cpp_properties.json3
-rw-r--r--.vscode/settings.json5
-rw-r--r--Scheme.md14
-rw-r--r--include/odhtdb/Database.hpp56
-rw-r--r--include/odhtdb/DatabaseStorage.hpp134
-rw-r--r--include/odhtdb/Encryption.hpp3
-rw-r--r--include/odhtdb/Group.hpp15
-rw-r--r--include/odhtdb/LocalUser.hpp6
-rw-r--r--include/odhtdb/LocalUserEncrypted.hpp15
-rw-r--r--include/odhtdb/Permission.hpp2
-rw-r--r--include/odhtdb/RemoteUser.hpp6
-rw-r--r--include/odhtdb/Signature.hpp8
-rw-r--r--include/odhtdb/User.hpp15
-rw-r--r--include/odhtdb/sql/SqlQuery.hpp65
-rw-r--r--project.conf3
-rw-r--r--src/Database.cpp254
-rw-r--r--src/DatabaseStorage.cpp1503
-rw-r--r--src/Encryption.cpp7
-rw-r--r--src/Group.cpp10
-rw-r--r--src/LocalUser.cpp4
-rw-r--r--src/Signature.cpp10
-rw-r--r--src/User.cpp4
-rw-r--r--src/sql/SqlQuery.cpp129
-rw-r--r--tests/assert.hpp12
-rw-r--r--tests/main.cpp86
26 files changed, 1173 insertions, 1197 deletions
diff --git a/.gitignore b/.gitignore
index 88bf288..960c8ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ sibs-build/
.kdev4/
odhtdb.kdev4
storage/
+.gdb_history
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
index e294ff4..73ef30c 100644
--- a/.vscode/c_cpp_properties.json
+++ b/.vscode/c_cpp_properties.json
@@ -40,7 +40,6 @@
],
"defines": [],
"intelliSenseMode": "clang-x64",
- "compileCommands": "${workspaceRoot}/compile_commands.json",
"browse": {
"path": [
"/usr/lib/gcc/x86_64-pc-linux-gnu/7.2.1/../../../../include/c++/7.2.1",
@@ -82,5 +81,5 @@
}
}
],
- "version": 3
+ "version": 4
} \ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 48e2d5a..aeb76fb 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -52,6 +52,9 @@
"type_traits": "cpp",
"utility": "cpp",
"valarray": "cpp",
- "__config": "cpp"
+ "__config": "cpp",
+ "__nullptr": "cpp",
+ "hash_map": "cpp",
+ "hash_set": "cpp"
}
} \ No newline at end of file
diff --git a/Scheme.md b/Scheme.md
index 865339e..9a757d2 100644
--- a/Scheme.md
+++ b/Scheme.md
@@ -1,13 +1,9 @@
# Create
Packet
- Header
- packet structure version
- timestamp
- creator public key
- admin group id (uuid, 16 bytes)
- Body (Encrypted)
- creator name
- name
+ packet structure version
+ timestamp
+ creator public key
+ admin group id (uuid, 16 bytes)
# Add data
Packet
creator public key
@@ -27,6 +23,6 @@ Packet
timestamp (not strictly accurate, mostly used for visual representation)
operation type
Body
- name (of the user to add to group, encrypted)
public key (of user to add to group)
group id (the group which the user should be added to)
+ PADDING (Random data, to ensure this packet gets an unique hash)
diff --git a/include/odhtdb/Database.hpp b/include/odhtdb/Database.hpp
index 12c618b..1e4d470 100644
--- a/include/odhtdb/Database.hpp
+++ b/include/odhtdb/Database.hpp
@@ -55,14 +55,14 @@ namespace odhtdb
const Hash *nodeHash;
u64 timestamp; // In microseconds
- const User *creatorUser;
- std::string name;
+ const Signature::PublicKey *creatorPublicKey;
+ const DataView groupId;
- DatabaseCreateNodeRequest(const Hash *_nodeHash, u64 _timestamp, const User *_creatorUser, std::string &&_name) :
+ DatabaseCreateNodeRequest(const Hash *_nodeHash, u64 _timestamp, const Signature::PublicKey *_creatorPublicKey, const DataView &_groupId) :
nodeHash(_nodeHash),
timestamp(_timestamp),
- creatorUser(_creatorUser),
- name(std::move(_name))
+ creatorPublicKey(_creatorPublicKey),
+ groupId(_groupId)
{
}
@@ -75,14 +75,14 @@ namespace odhtdb
const Hash *nodeHash;
const Hash *requestHash;
u64 timestamp; // In microseconds
- const User *creatorUser;
+ const Signature::PublicKey *creatorPublicKey;
const DataView decryptedData;
- DatabaseAddNodeRequest(const Hash *_nodeHash, const Hash *_requestHash, u64 _timestamp, const User *_creatorUser, const DataView &_decryptedData) :
+ DatabaseAddNodeRequest(const Hash *_nodeHash, const Hash *_requestHash, u64 _timestamp, const Signature::PublicKey *_creatorPublicKey, const DataView &_decryptedData) :
nodeHash(_nodeHash),
requestHash(_requestHash),
timestamp(_timestamp),
- creatorUser(_creatorUser),
+ creatorPublicKey(_creatorPublicKey),
decryptedData(_decryptedData)
{
@@ -96,16 +96,16 @@ namespace odhtdb
const Hash *nodeHash;
const Hash *requestHash;
u64 timestamp; // In microseconds
- const User *creatorUser;
- const User *userToAdd;
- const Group *groupToAddUserTo;
+ const Signature::PublicKey *creatorPublicKey;
+ const Signature::PublicKey *userToAddPublicKey;
+ const DataView groupToAddUserTo;
- DatabaseAddUserRequest(const Hash *_nodeHash, const Hash *_requestHash, u64 _timestamp, const User *_creatorUser, const User *_userToAdd, const Group *_groupToAddUserTo) :
+ DatabaseAddUserRequest(const Hash *_nodeHash, const Hash *_requestHash, u64 _timestamp, const Signature::PublicKey *_creatorPublicKey, const Signature::PublicKey *_userToAddPublicKey, const DataView &_groupToAddUserTo) :
nodeHash(_nodeHash),
requestHash(_requestHash),
timestamp(_timestamp),
- creatorUser(_creatorUser),
- userToAdd(_userToAdd),
+ creatorPublicKey(_creatorPublicKey),
+ userToAddPublicKey(_userToAddPublicKey),
groupToAddUserTo(_groupToAddUserTo)
{
@@ -115,7 +115,7 @@ namespace odhtdb
class DatabaseCreateResponse
{
public:
- DatabaseCreateResponse(LocalUser *nodeAdminUser, const std::shared_ptr<OwnedMemory> &key, const std::shared_ptr<Hash> &hash);
+ DatabaseCreateResponse(LocalUser *nodeAdminUser, std::shared_ptr<OwnedMemory> key, std::shared_ptr<Hash> hash);
const LocalUser* getNodeAdminUser() const;
// Size of encryption key is odhtdb::KEY_BYTE_SIZE (found in Encryption.hpp)
@@ -144,32 +144,40 @@ namespace odhtdb
reponseKeyInfoHash = other.reponseKeyInfoHash;
}
};
+
+ using CreateNodeCallbackFunc = std::function<void(const DatabaseCreateNodeRequest&)>;
+ using AddNodeCallbackFunc = std::function<void(const DatabaseAddNodeRequest&)>;
+ using AddUserCallbackFunc = std::function<void(const DatabaseAddUserRequest&)>;
+
+ struct DatabaseCallbackFuncs
+ {
+ CreateNodeCallbackFunc createNodeCallbackFunc;
+ AddNodeCallbackFunc addNodeCallbackFunc;
+ AddUserCallbackFunc addUserCallbackFunc;
+ };
+
class Database
{
DISABLE_COPY(Database)
friend class DatabaseStorage;
public:
- Database(const char *bootstrapNodeAddr, u16 port, const boost::filesystem::path &storageDir);
+ Database(const char *bootstrapNodeAddr, u16 port, const boost::filesystem::path &storageDir, DatabaseCallbackFuncs callbackFuncs);
~Database();
// Safe to call multiple times with same node hash, will be ignored if the node is already beeing seeded
void seed(const DatabaseNode &nodeToSeed);
void stopSeeding(const Hash &nodeHash);
+ void loadNode(const Hash &nodeHash);
// Throws DatabaseCreateException on failure.
- std::unique_ptr<DatabaseCreateResponse> create(const std::string &ownerName, const std::string &nodeName);
+ std::unique_ptr<DatabaseCreateResponse> create();
// Throws DatabaseCreateException on failure.
- std::unique_ptr<DatabaseCreateResponse> create(const std::string &ownerName, const Signature::KeyPair &keyPair, const std::string &nodeName);
+ std::unique_ptr<DatabaseCreateResponse> create(const Signature::KeyPair &creatorKeyPair);
// Throws PermissionDeniedException if user @userToPerformActionWith is not allowed to add data to node
void addData(const DatabaseNode &nodeInfo, const LocalUser *userToPerformActionWith, DataView dataToAdd);
// Throws PermissionDeniedException if user @userToPerformActionWith is not allowed to add user @userToAdd to group @groupToAddUserTo
- void addUser(const DatabaseNode &nodeInfo, const LocalUser *userToPerformActionWith, const std::string &userToAddName, const Signature::PublicKey &userToAddPublicKey, Group *groupToAddUserTo);
-
- void setOnCreateNodeCallback(std::function<void(const DatabaseCreateNodeRequest&)> callbackFunc);
- void setOnAddNodeCallback(std::function<void(const DatabaseAddNodeRequest&)> callbackFunc);
- void setOnAddUserCallback(std::function<void(const DatabaseAddUserRequest&)> callbackFunc);
+ void addUser(const DatabaseNode &nodeInfo, const LocalUser *userToPerformActionWith, const Signature::PublicKey &userToAddPublicKey, Group *groupToAddUserTo);
- DatabaseStorage& getStorage();
ntp::NtpTimestamp getSyncedTimestampUtc() const;
private:
void deserializeCreateRequest(const std::shared_ptr<dht::Value> &value, const Hash &hash, const std::shared_ptr<OwnedMemory> encryptionKey);
diff --git a/include/odhtdb/DatabaseStorage.hpp b/include/odhtdb/DatabaseStorage.hpp
index 11e243a..3c4d9bc 100644
--- a/include/odhtdb/DatabaseStorage.hpp
+++ b/include/odhtdb/DatabaseStorage.hpp
@@ -15,6 +15,10 @@
#include <boost/filesystem/path.hpp>
#include <sibs/SafeDeserializer.hpp>
#include <opendht/crypto.h>
+#include <functional>
+
+class sqlite3;
+class sqlite3_stmt;
namespace odhtdb
{
@@ -41,16 +45,12 @@ namespace odhtdb
{
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)
+ creatorPublicKey(_creatorPublicKey)
{
}
@@ -115,63 +115,46 @@ namespace odhtdb
LocalUser *localUser;
};
+ using FetchNodeRawCallbackFunc = std::function<void(const DataView)>;
+ using FetchNodeAddDataRawCallbackFunc = std::function<void(const DataView)>;
+
class DatabaseStorage
{
public:
- // Throws DatabaseStorageCorrupt if storage is corrupted
+ // Throws DatabaseStorageCorrupt if storage is corrupted. Throws DatabaseStorageException on other failures
DatabaseStorage(Database *database, const boost::filesystem::path &storagePath);
+ ~DatabaseStorage();
+
+ void loadNode(const Hash &nodeHash);
+
+ bool doesNodeExist(const Hash &nodeHash) const;
+ bool doesDataExist(const Hash &requestHash) const;
// Throws DatabaseStorageAlreadyExists if data with hash already exists
- void createStorage(const Hash &hash, Group *creatorGroup, u64 timestamp, const u8 *data, usize dataSize, u32 offsetToEncryptedData);
+ void createStorage(const Hash &hash, Group *creatorGroup, u64 timestamp, const void *data, usize size);
// 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);
+ void appendStorage(const Hash &nodeHash, const Hash &dataHash, DatabaseOperation operation, const Signature::PublicKey &creatorPublicKey, u64 timestamp, const void *data, usize size, const DataView &additionalDataView);
- // Return false if user public key already exists, otherwise return true
- bool addUser(const Hash &nodeHash, User *user);
+ // Throws DatabaseStorageAlreadyExists if group already exists in node
+ void addGroup(const Hash &nodeHash, Group *group);
- // Increase user action counter, returning the new value. If user has not performed any action yet, then 0 is returned.
- // Returns DatabaseStorageException if no node with id @nodeHash exists or if user with public key @userPublicKey doesn't exist in node @nodeHash
- //u64 increaseUserActionCounter(const Hash &nodeHash, const Signature::PublicKey &userPublicKey);
+ void addUserToGroup(const Hash &nodeHash, const Signature::PublicKey &userPublicKey, const DataView &groupId);
- // Returns nullptr if no storage with provided hash exists
- const DatabaseStorageObjectList* getStorage(const Hash &hash) const;
+ // Throws DatabaseStorageAlreadyExists is user already exists in node
+ void addUser(const Hash &nodeHash, const Signature::PublicKey &userPublicKey, const DataView &groupId);
- // 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;
+ void fetchNodeRaw(const Hash &nodeHash, FetchNodeRawCallbackFunc callbackFunc);
+ void fetchNodeAddDataRaw(const Hash &nodeHash, FetchNodeAddDataRawCallbackFunc callbackFunc);
// 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);
+ //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 data in node by its id. Doesn't include 'create' requests.
- // Returns nullptr if not data with @dataHash exists.
- DatabaseStorageObject* getDataById(const Hash &dataHash);
+ //Signature::KeyPair decryptLocalEncryptedUser(const std::string &username, const std::string &password);
// 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
@@ -183,37 +166,48 @@ namespace odhtdb
// 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 init(const boost::filesystem::path &storagePath);
+ void cleanup();
+
+ void bindCheckError(int sqliteBindResult);
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);
+ bool decryptNodeData(const Hash &nodeHash, const std::shared_ptr<OwnedMemory> decryptionKey);
+ bool decryptNodeData(const Hash &nodeHash, const std::shared_ptr<OwnedMemory> decryptionKey, const Signature::PublicKey *creatorPublicKey, const DataView &adminGroupId, u64 timestamp);
+ bool decryptNodeAddData(i64 rowId, const Hash &nodeHash, const Hash &dataHash, u64 timestamp, const Signature::PublicKey *creatorPublicKey, const DataView &encryptedData, const std::shared_ptr<OwnedMemory> decryptionKey);
+ bool decryptNodeAddUser(i64 rowId, const Hash &nodeHash, const Hash &dataHash, u64 timestamp, const Signature::PublicKey *creatorPublicKey, const Signature::PublicKey *userToAddPublicKey, const DataView &groupToAddUserTo, const std::shared_ptr<OwnedMemory> decryptionKey);
+ i64 getNodeRowId(const Hash &nodeHash);
+ i64 getNodeAddDataRowId(const Hash &requestHash);
+
+ void setNodeAddDataDecrypted(i64 rowId);
+ void setNodeAddDataDecryptedData(i64 rowId, const DataView &decryptedData);
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;
+ sqlite3 *sqliteDb;
+ sqlite3_stmt *insertNodeStmt;
+ sqlite3_stmt *insertUserStmt;
+ sqlite3_stmt *insertGroupStmt;
+ sqlite3_stmt *insertNodeAddDataStmt;
+ sqlite3_stmt *setNodeDecryptionKeyStmt;
+ sqlite3_stmt *getNodeDecryptionKeyStmt;
+ sqlite3_stmt *insertNodeUserGroupAssocStmt;
+
+ sqlite3_stmt *selectNodeStmt;
+ sqlite3_stmt *selectNodeAddDataByNodeStmt;
+ sqlite3_stmt *selectNodeIdStatement;
+ sqlite3_stmt *selectNodeAddDataIdStatement;
+ sqlite3_stmt *insertNodeRawStmt;
+ sqlite3_stmt *insertNodeAddDataRawStmt;
+
+ sqlite3_stmt *insertNodeAddDataAdditionalStmt;
+ sqlite3_stmt *insertNodeAddUserDataStmt;
+
+ sqlite3_stmt *selectNodeAddDataAdditionalStmt;
+ sqlite3_stmt *selectNodeAddUserDataStmt;
+
+ sqlite3_stmt *setNodeAddDataDecryptedStmt;
+ sqlite3_stmt *setNodeAddDataAdditionalDataStmt;
+
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;
};
diff --git a/include/odhtdb/Encryption.hpp b/include/odhtdb/Encryption.hpp
index e710760..2457630 100644
--- a/include/odhtdb/Encryption.hpp
+++ b/include/odhtdb/Encryption.hpp
@@ -38,6 +38,9 @@ namespace odhtdb
DataView getKey() const;
DataView getNonce() const;
DataView getCipherText() const;
+
+ // Size of output should be at least @ENCRYPTION_KEY_BYTE_SIZE bytes
+ static void generateKey(unsigned char *output);
private:
unsigned char key[ENCRYPTION_KEY_BYTE_SIZE];
unsigned char nonce[ENCRYPTION_NONCE_BYTE_SIZE];
diff --git a/include/odhtdb/Group.hpp b/include/odhtdb/Group.hpp
index 99307e1..f8de78e 100644
--- a/include/odhtdb/Group.hpp
+++ b/include/odhtdb/Group.hpp
@@ -4,7 +4,6 @@
#include "DataView.hpp"
#include "Permission.hpp"
#include "utils.hpp"
-#include <string>
#include <vector>
#include <stdexcept>
@@ -12,16 +11,6 @@ namespace odhtdb
{
class User;
- class GroupNameTooLongException : public std::runtime_error
- {
- public:
- GroupNameTooLongException(const std::string &groupName) :
- std::runtime_error(std::string("The group name ") + groupName + " is longer than 255 bytes")
- {
-
- }
- };
-
const int GROUP_ID_LENGTH = 16;
class Group
@@ -29,10 +18,9 @@ namespace odhtdb
DISABLE_COPY(Group)
friend class User;
public:
- Group(const std::string &name, uint8_t id[GROUP_ID_LENGTH], const Permission &permission);
+ Group(uint8_t id[GROUP_ID_LENGTH], const Permission &permission);
~Group();
- const std::string& getName() const;
DataView getId() const;
const Permission& getPermission() const;
const std::vector<const User*>& getUsers() const;
@@ -40,7 +28,6 @@ namespace odhtdb
void addUser(const User *user);
bool removeUser(const User *user);
private:
- std::string name;
uint8_t id[GROUP_ID_LENGTH];
Permission permission;
std::vector<const User*> users;
diff --git a/include/odhtdb/LocalUser.hpp b/include/odhtdb/LocalUser.hpp
index c87ba45..b60b516 100644
--- a/include/odhtdb/LocalUser.hpp
+++ b/include/odhtdb/LocalUser.hpp
@@ -8,9 +8,9 @@ namespace odhtdb
class LocalUser : public User
{
public:
- static LocalUser* create(const Signature::KeyPair &keyPair, const std::string &name, Group *group)
+ static LocalUser* create(const Signature::KeyPair &keyPair, Group *group)
{
- return new LocalUser(keyPair, name, group);
+ return new LocalUser(keyPair, group);
}
const Signature::PublicKey& getPublicKey() const override
@@ -28,7 +28,7 @@ namespace odhtdb
return keyPair;
}
private:
- LocalUser(const Signature::KeyPair &_keyPair, const std::string &name, Group *group);
+ LocalUser(const Signature::KeyPair &_keyPair, Group *group);
private:
Signature::KeyPair keyPair;
};
diff --git a/include/odhtdb/LocalUserEncrypted.hpp b/include/odhtdb/LocalUserEncrypted.hpp
index 952892f..7919cb3 100644
--- a/include/odhtdb/LocalUserEncrypted.hpp
+++ b/include/odhtdb/LocalUserEncrypted.hpp
@@ -22,9 +22,9 @@ namespace odhtdb
class LocalUserEncrypted
{
public:
- static LocalUserEncrypted* create(const Signature::PublicKey &publicKey, const EncryptedPrivateKey &encryptedPrivateKey, const std::string &name)
+ static LocalUserEncrypted* create(const Signature::PublicKey &publicKey, const EncryptedPrivateKey &encryptedPrivateKey)
{
- return new LocalUserEncrypted(publicKey, encryptedPrivateKey, name);
+ return new LocalUserEncrypted(publicKey, encryptedPrivateKey);
}
const Signature::PublicKey& getPublicKey() const
@@ -36,22 +36,15 @@ namespace odhtdb
{
return encryptedPrivateKey;
}
-
- const std::string& getName() const
- {
- return name;
- }
private:
- LocalUserEncrypted(const Signature::PublicKey &_publicKey, const EncryptedPrivateKey &_encryptedPrivateKey, const std::string &_name) :
+ LocalUserEncrypted(const Signature::PublicKey &_publicKey, const EncryptedPrivateKey &_encryptedPrivateKey) :
publicKey(_publicKey),
- encryptedPrivateKey(_encryptedPrivateKey),
- name(_name)
+ encryptedPrivateKey(_encryptedPrivateKey)
{
}
private:
Signature::PublicKey publicKey;
EncryptedPrivateKey encryptedPrivateKey;
- std::string name;
};
}
diff --git a/include/odhtdb/Permission.hpp b/include/odhtdb/Permission.hpp
index 1e7bb0c..0978c5e 100644
--- a/include/odhtdb/Permission.hpp
+++ b/include/odhtdb/Permission.hpp
@@ -16,7 +16,7 @@ namespace odhtdb
{
ADD_DATA = (1 << 0),
ADD_USER_SAME_LEVEL = (1 << 1),
- ADD_USER_LOWER_LEVEL = (1 << 2),
+ ADD_USER_HIGHER_LEVEL = (1 << 2),
ADD_GROUP = (1 << 3),
REMOVE_GROUP = (1 << 4)
};
diff --git a/include/odhtdb/RemoteUser.hpp b/include/odhtdb/RemoteUser.hpp
index 9dc8f96..2658132 100644
--- a/include/odhtdb/RemoteUser.hpp
+++ b/include/odhtdb/RemoteUser.hpp
@@ -7,9 +7,9 @@ namespace odhtdb
class RemoteUser : public User
{
public:
- static RemoteUser* create(const Signature::PublicKey &publicKey, const std::string &name, Group *group)
+ static RemoteUser* create(const Signature::PublicKey &publicKey, Group *group)
{
- return new RemoteUser(publicKey, name, group);
+ return new RemoteUser(publicKey, group);
}
const Signature::PublicKey& getPublicKey() const override
@@ -17,7 +17,7 @@ namespace odhtdb
return publicKey;
}
private:
- RemoteUser(const Signature::PublicKey &_publicKey, const std::string &name, Group *group) : User(User::Type::REMOTE, name, group), publicKey(_publicKey){}
+ RemoteUser(const Signature::PublicKey &_publicKey, Group *group) : User(User::Type::REMOTE, group), publicKey(_publicKey) {}
private:
Signature::PublicKey publicKey;
};
diff --git a/include/odhtdb/Signature.hpp b/include/odhtdb/Signature.hpp
index 1f5158e..0fc9087 100644
--- a/include/odhtdb/Signature.hpp
+++ b/include/odhtdb/Signature.hpp
@@ -55,11 +55,13 @@ namespace odhtdb
public:
const static PublicKey ZERO;
+ PublicKey();
// Throws InvalidSignatureKeySize if size is not PUBLIC_KEY_NUM_BYTES
PublicKey(const char *data, size_t size);
PublicKey(const PublicKey &other);
PublicKey& operator=(const PublicKey &other);
+ char* getData() { return data; }
const char* getData() const { return data; }
size_t getSize() const { return PUBLIC_KEY_NUM_BYTES; }
@@ -74,8 +76,6 @@ namespace odhtdb
std::string toString() const;
private:
- PublicKey(){}
- private:
char data[PUBLIC_KEY_NUM_BYTES];
};
@@ -96,11 +96,13 @@ namespace odhtdb
public:
const static PrivateKey ZERO;
+ PrivateKey();
// Throws InvalidSignatureKeySize if size is not PRIVATE_KEY_NUM_BYTES
PrivateKey(const char *data, size_t size);
PrivateKey(const PrivateKey &other);
PrivateKey& operator=(const PrivateKey &other);
+ char* getData() { return data; }
const char* getData() const { return data; }
size_t getSize() const { return PRIVATE_KEY_NUM_BYTES; }
@@ -108,8 +110,6 @@ namespace odhtdb
std::string sign(const DataView &dataToSign) const;
std::string toString() const;
private:
- PrivateKey(){}
- private:
char data[PRIVATE_KEY_NUM_BYTES];
};
diff --git a/include/odhtdb/User.hpp b/include/odhtdb/User.hpp
index 4d6d9b3..15e6492 100644
--- a/include/odhtdb/User.hpp
+++ b/include/odhtdb/User.hpp
@@ -3,7 +3,6 @@
#include "Signature.hpp"
#include "types.hpp"
#include "Permission.hpp"
-#include <string>
#include <stdexcept>
#include <vector>
@@ -11,16 +10,6 @@ namespace odhtdb
{
class Group;
class DatabaseStorage;
-
- class UserNameTooLongException : public std::runtime_error
- {
- public:
- UserNameTooLongException(const std::string &userName) :
- std::runtime_error(std::string("The username ") + userName + " is longer than 255 bytes")
- {
-
- }
- };
class User
{
@@ -37,16 +26,14 @@ namespace odhtdb
virtual void addToGroup(Group *group);
Type getType() const { return type; }
- const std::string& getName() const { return name; }
virtual const std::vector<Group*>& getGroups() const { return groups; }
virtual const Signature::PublicKey& getPublicKey() const = 0;
virtual bool isAllowedToPerformAction(PermissionType action) const;
protected:
- User(Type type, const std::string &name, Group *group);
+ User(Type type, Group *group);
protected:
Type type;
- std::string name;
std::vector<Group*> groups;
};
}
diff --git a/include/odhtdb/sql/SqlQuery.hpp b/include/odhtdb/sql/SqlQuery.hpp
new file mode 100644
index 0000000..f26ee1a
--- /dev/null
+++ b/include/odhtdb/sql/SqlQuery.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "../DataView.hpp"
+#include <utility>
+#include <stdexcept>
+
+class sqlite3;
+class sqlite3_stmt;
+
+namespace odhtdb
+{
+ class SqlQueryException : public std::runtime_error
+ {
+ public:
+ SqlQueryException(const std::string &errMsg) : std::runtime_error(errMsg) {}
+ };
+
+ class SqlArg
+ {
+ public:
+ enum class Type : u8
+ {
+ DATA_VIEW,
+ INT,
+ INT64
+ };
+
+ SqlArg(const DataView &data) : dataView(data), type(Type::DATA_VIEW) {}
+ SqlArg(int data) : integer(data), type(Type::INT) {}
+ SqlArg(i64 data) : integer64(data), type(Type::INT64) {}
+
+ int bind(sqlite3_stmt *stmt, int paramIndex) const;
+ private:
+ union
+ {
+ const DataView dataView;
+ const int integer;
+ const i64 integer64;
+ };
+ const Type type;
+ };
+
+ class SqlQuery
+ {
+ public:
+ // Throws SqlQueryException on failure
+ SqlQuery(sqlite3 *db, const char *sql, std::initializer_list<SqlArg> args);
+ ~SqlQuery();
+
+ // Return true if we got result, false if there are not rows left.
+ // Throws SqlQueryException on failure
+ bool next();
+
+ int getInt(int index);
+ i64 getInt64(int index);
+ // The returned blob is NOT a copy, it will be invalid after next call to @next or when SqlQuery is deallocated
+ const DataView getBlob(int index);
+ private:
+ void checkColumnIndex(int index);
+ private:
+ sqlite3 *db;
+ sqlite3_stmt *stmt;
+ int numColumns;
+ };
+}
diff --git a/project.conf b/project.conf
index c878112..c51f4a7 100644
--- a/project.conf
+++ b/project.conf
@@ -1,6 +1,6 @@
[package]
name = "odhtdb"
-version = "0.0.1"
+version = "0.0.2"
type = "library"
platforms = ["linux32", "linux64"]
tests = "tests"
@@ -16,3 +16,4 @@ sibs-serializer = "0.2.1"
boost-filesystem = "1.66.0"
boost-uuid = "1.66.0"
argon2 = "2017.12.27"
+sqlite3 = "3.0.0"
diff --git a/src/Database.cpp b/src/Database.cpp
index 41d3798..88ac8e4 100644
--- a/src/Database.cpp
+++ b/src/Database.cpp
@@ -61,7 +61,7 @@ namespace odhtdb
return DataView(result, allocationSize);
}
- DatabaseCreateResponse::DatabaseCreateResponse(LocalUser *_nodeAdminUser, const shared_ptr<OwnedMemory> &_key, const shared_ptr<Hash> &_hash) :
+ DatabaseCreateResponse::DatabaseCreateResponse(LocalUser *_nodeAdminUser, shared_ptr<OwnedMemory> _key, shared_ptr<Hash> _hash) :
nodeAdminUser(_nodeAdminUser),
key(_key),
hash(_hash)
@@ -84,10 +84,10 @@ namespace odhtdb
return hash;
}
- Database::Database(const char *bootstrapNodeAddr, u16 port, const boost::filesystem::path &storageDir) :
- onCreateNodeCallbackFunc(nullptr),
- onAddNodeCallbackFunc(nullptr),
- onAddUserCallbackFunc(nullptr),
+ Database::Database(const char *bootstrapNodeAddr, u16 port, const boost::filesystem::path &storageDir, DatabaseCallbackFuncs callbackFuncs) :
+ onCreateNodeCallbackFunc(callbackFuncs.createNodeCallbackFunc),
+ onAddNodeCallbackFunc(callbackFuncs.addNodeCallbackFunc),
+ onAddUserCallbackFunc(callbackFuncs.addUserCallbackFunc),
databaseStorage(this, storageDir)
{
node.run(port , {
@@ -218,38 +218,35 @@ namespace odhtdb
u64 dataStartTimestamp = deserializer.extract<u64>();
u8 requestResponseKey[OPENDHT_INFOHASH_LEN];
deserializer.extract(requestResponseKey, OPENDHT_INFOHASH_LEN);
-
- auto requestedData = databaseStorage.getStorage(*nodeToSeed.getRequestHash());
- if(!requestedData)
- {
- Log::debug("No data found for hash %s, unable to serve peer", nodeToSeed.getRequestHash()->toString().c_str());
- return true;
- }
-
InfoHash requestResponseInfoHash(requestResponseKey, OPENDHT_INFOHASH_LEN);
if(dataStartTimestamp == 0)
{
- Log::debug("Request: Sent create packet to requesting peer");
- node.put(requestResponseInfoHash, Value((u8*)requestedData->data.data, requestedData->data.size), [](bool ok)
+ databaseStorage.fetchNodeRaw(*nodeToSeed.getRequestHash(), [this, requestResponseInfoHash](const DataView rawData)
{
- if(!ok)
- Log::error("Failed to put response for old data for 'create' data");
+ Log::debug("Request: Sent create packet to requesting peer");
+ Value value((u8*)rawData.data, rawData.size);
+ node.put(requestResponseInfoHash, move(value), [](bool ok)
+ {
+ if(!ok)
+ Log::error("Failed to put response for old data for 'create' data");
+ });
});
}
- for(auto requestedObject : requestedData->objects)
+ databaseStorage.fetchNodeAddDataRaw(*nodeToSeed.getRequestHash(), [this, requestResponseInfoHash](const DataView rawData)
{
- node.put(requestResponseInfoHash, Value((u8*)requestedObject->data.data, requestedObject->data.size), [](bool ok)
+ Value value((u8*)rawData.data, rawData.size);
+ node.put(requestResponseInfoHash, move(value), [](bool ok)
{
if(!ok)
Log::error("Failed to put response for old data for 'add' data");
});
- }
+ });
}
- catch (sibs::DeserializeException &e)
+ catch (std::exception &e)
{
- Log::warn("Failed to deserialize 'get old data' request: %s", e.what());
+ Log::warn("Failed while serving peer, error: %s", e.what());
}
return true;
});
@@ -283,19 +280,25 @@ namespace odhtdb
seedInfoMap.erase(seedInfoIt);
}
}
+
+ void Database::loadNode(const Hash &nodeHash)
+ {
+ databaseStorage.loadNode(nodeHash);
+ }
- unique_ptr<DatabaseCreateResponse> Database::create(const string &ownerName, const string &nodeName)
+ unique_ptr<DatabaseCreateResponse> Database::create()
{
- return create(ownerName, Signature::KeyPair(), nodeName);
+ return create(Signature::KeyPair());
}
- unique_ptr<DatabaseCreateResponse> Database::create(const string &ownerName, const Signature::KeyPair &keyPair, const string &nodeName)
+ unique_ptr<DatabaseCreateResponse> Database::create(const Signature::KeyPair &creatorKeyPair)
{
// 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(keyPair, ownerName, adminGroup);
+ assert(adminGroupId.size() == GROUP_ID_LENGTH);
+ auto adminGroup = new Group(adminGroupId.data, ADMIN_PERMISSION);
+ LocalUser *nodeAdminUser = LocalUser::create(creatorKeyPair, adminGroup);
// Header
sibs::SafeSerializer serializer;
@@ -305,30 +308,18 @@ namespace odhtdb
serializer.add((u8*)nodeAdminUser->getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES);
serializer.add(adminGroupId.data, adminGroupId.size());
- // Encrypted body
- sibs::SafeSerializer encryptedSerializer;
- assert(nodeAdminUser->getName().size() <= 255);
- encryptedSerializer.add((u8)nodeAdminUser->getName().size());
- encryptedSerializer.add((u8*)nodeAdminUser->getName().data(), nodeAdminUser->getName().size());
- assert(nodeName.size() <= 255);
- encryptedSerializer.add((u8)nodeName.size());
- encryptedSerializer.add((u8*)nodeName.data(), nodeName.size());
-
try
{
- Encryption encryptedBody(DataView(encryptedSerializer.getBuffer().data(), encryptedSerializer.getBuffer().size()));
- DataView requestData = combine(serializer, encryptedBody);
- shared_ptr<Hash> hashRequestKey = make_shared<Hash>(requestData.data, requestData.size);
- databaseStorage.setNodeDecryptionKey(*hashRequestKey, DataView(encryptedBody.getKey().data, encryptedBody.getKey().size));
- databaseStorage.createStorage(*hashRequestKey, adminGroup, timestampCombined, (const u8*)requestData.data, requestData.size, serializer.getBuffer().size());
+ unsigned char *encryptionKeyRaw = new unsigned char[ENCRYPTION_KEY_BYTE_SIZE];
+ Encryption::generateKey(encryptionKeyRaw);
+ shared_ptr<OwnedMemory> encryptionKey = make_shared<OwnedMemory>(encryptionKeyRaw, ENCRYPTION_KEY_BYTE_SIZE);
+ shared_ptr<Hash> hashRequestKey = make_shared<Hash>(serializer.getBuffer().data(), serializer.getBuffer().size());
- assert(encryptedBody.getKey().size == ENCRYPTION_KEY_BYTE_SIZE);
- auto key = make_shared<OwnedMemory>(new char[encryptedBody.getKey().size], encryptedBody.getKey().size);
- memcpy(key->data, encryptedBody.getKey().data, encryptedBody.getKey().size);
+ databaseStorage.setNodeDecryptionKey(*hashRequestKey, DataView(encryptionKey->data, encryptionKey->size));
+ databaseStorage.createStorage(*hashRequestKey, adminGroup, timestampCombined, (const u8*)serializer.getBuffer().data(), serializer.getBuffer().size());
DhtKey dhtKey(*hashRequestKey);
- Value createDataValue((u8*)requestData.data, requestData.size);
- delete[] (char*)requestData.data;
+ Value createDataValue(move(serializer.getBuffer()));
node.put(dhtKey.getNewDataListenerKey(), move(createDataValue), [](bool ok)
{
// TODO: Handle failure to put data
@@ -336,7 +327,7 @@ namespace odhtdb
Log::warn("Failed to put: %s, what to do?", "Database::create");
});
- return make_unique<DatabaseCreateResponse>(nodeAdminUser, move(key), hashRequestKey);
+ return make_unique<DatabaseCreateResponse>(nodeAdminUser, encryptionKey, hashRequestKey);
}
catch (EncryptionException &e)
{
@@ -354,7 +345,7 @@ namespace odhtdb
// and remote peers would accept our request to perform operation if they haven't received the operation that removes the user from the group.
// How to handle this?
string errMsg = "User ";
- errMsg += userToPerformActionWith->getName();
+ errMsg += userToPerformActionWith->getPublicKey().toString();
errMsg += " is not allowed to perform the operation: ADD_USER";
throw PermissionDeniedException(errMsg);
}
@@ -372,7 +363,7 @@ namespace odhtdb
DataView stagedAddObject = combine(userToPerformActionWith->getPublicKey(), signedRequestData);
Hash requestDataHash(stagedAddObject.data, stagedAddObject.size);
DataView encryptedDataView((char*)requestData.data + serializer.getBuffer().size(), requestData.size - serializer.getBuffer().size());
- databaseStorage.appendStorage(*nodeInfo.getRequestHash(), requestDataHash, DatabaseOperation::ADD_DATA, userToPerformActionWith, timestampCombined, (u8*)stagedAddObject.data, stagedAddObject.size, encryptedDataView);
+ databaseStorage.appendStorage(*nodeInfo.getRequestHash(), requestDataHash, DatabaseOperation::ADD_DATA, userToPerformActionWith->getPublicKey(), timestampCombined, (u8*)stagedAddObject.data, stagedAddObject.size, encryptedDataView);
delete[] (char*)requestData.data;
DhtKey dhtKey(requestDataHash);
@@ -391,7 +382,7 @@ namespace odhtdb
for(auto group : groups)
{
const auto &groupPermission = group->getPermission();
- if(groupPermission.getFlag(PermissionType::ADD_USER_LOWER_LEVEL) && groupPermission.getPermissionLevel() < groupToAddUserTo->getPermission().getPermissionLevel())
+ if(groupPermission.getFlag(PermissionType::ADD_USER_HIGHER_LEVEL) && groupPermission.getPermissionLevel() < groupToAddUserTo->getPermission().getPermissionLevel())
{
return group;
}
@@ -403,15 +394,15 @@ namespace odhtdb
return nullptr;
}
- void Database::addUser(const DatabaseNode &nodeInfo, const LocalUser *userToPerformActionWith, const string &userToAddName, const Signature::PublicKey &userToAddPublicKey, Group *groupToAddUserTo)
+ void Database::addUser(const DatabaseNode &nodeInfo, const LocalUser *userToPerformActionWith, const Signature::PublicKey &userToAddPublicKey, Group *groupToAddUserTo)
{
auto groupWithAddUserRights = getGroupWithRightsToAddUserToGroup(userToPerformActionWith->getGroups(), groupToAddUserTo);
if(!groupWithAddUserRights)
{
string errMsg = "The user ";
- errMsg += userToPerformActionWith->getName();
+ errMsg += userToPerformActionWith->getPublicKey().toString();
errMsg += " does not belong to any group that is allowed to add an user to the group ";
- errMsg += groupToAddUserTo->getName();
+ errMsg += bin2hex((const char*)groupToAddUserTo->getId().data, groupToAddUserTo->getId().size).c_str();
throw PermissionDeniedException(errMsg);
}
@@ -420,27 +411,22 @@ namespace odhtdb
u64 timestampCombined = getSyncedTimestampUtc().getCombined();
serializer.add(timestampCombined);
serializer.add(DatabaseOperation::ADD_USER);
-
- assert(userToAddName.size() <= 255);
- usize serializedEncryptedDataOffset = serializer.getBuffer().size();
- serializer.add((u8)userToAddName.size());
- DataView encryptionKey(nodeInfo.getNodeEncryptionKey()->data, ENCRYPTION_KEY_BYTE_SIZE);
- Encryption encryptedUserName(DataView((void*)userToAddName.data(), userToAddName.size()), DataView(), encryptionKey);
- serializer.add((u8*)encryptedUserName.getNonce().data, ENCRYPTION_NONCE_BYTE_SIZE);
- assert(encryptedUserName.getCipherText().size == ENCRYPTION_CHECKSUM_BYTE_SIZE + userToAddName.size());
- serializer.add((u8*)encryptedUserName.getCipherText().data, ENCRYPTION_CHECKSUM_BYTE_SIZE + userToAddName.size());
- usize serializedEncryptedDataSize = serializer.getBuffer().size() - serializedEncryptedDataOffset;
+ usize additionalDataOffset = serializer.getBuffer().size();
serializer.add((u8*)userToAddPublicKey.getData(), PUBLIC_KEY_NUM_BYTES);
serializer.add((uint8_t*)groupToAddUserTo->getId().data, groupToAddUserTo->getId().size);
+ // TODO: Should this be declared static? is there any difference in behavior/performance?
+ boost::uuids::random_generator uuidGen;
+ auto padding = uuidGen();
+ assert(padding.size() == 16);
+ serializer.add(padding.data, padding.size());
+
DataView requestData { serializer.getBuffer().data(), serializer.getBuffer().size() };
string signedRequestData = userToPerformActionWith->getPrivateKey().sign(requestData);
DataView stagedAddObject = combine(userToPerformActionWith->getPublicKey(), signedRequestData);
Hash requestDataHash(stagedAddObject.data, stagedAddObject.size);
- DataView encryptedDataView(nullptr, 0);
- auto userToAdd = RemoteUser::create(userToAddPublicKey, userToAddName, groupToAddUserTo);
- databaseStorage.addUser(*nodeInfo.getRequestHash(), userToAdd);
- databaseStorage.appendStorage(*nodeInfo.getRequestHash(), requestDataHash, DatabaseOperation::ADD_USER, userToPerformActionWith, timestampCombined, (u8*)stagedAddObject.data, stagedAddObject.size, encryptedDataView);
+ DataView additionalDataView((void*)(static_cast<const char*>(requestData.data) + additionalDataOffset), requestData.size - additionalDataOffset);
+ databaseStorage.appendStorage(*nodeInfo.getRequestHash(), requestDataHash, DatabaseOperation::ADD_USER, userToPerformActionWith->getPublicKey(), timestampCombined, (u8*)stagedAddObject.data, stagedAddObject.size, additionalDataView);
DhtKey dhtKey(requestDataHash);
Value addDataValue((u8*)stagedAddObject.data, stagedAddObject.size);
@@ -506,10 +492,10 @@ namespace odhtdb
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);
+ auto adminGroup = new Group(adminGroupId, ADMIN_PERMISSION);
// TODO: Username is encrypted, we dont know it... unless we have encryption key, in which case we should modify the user name and set it
- auto creatorUser = RemoteUser::create(userPublicKey, "ENCRYPTED USER NAME", adminGroup);
- databaseStorage.createStorage(hash, adminGroup, creationDate, value->data.data(), value->data.size(), value->data.size() - deserializer.getSize());
+ auto creatorUser = RemoteUser::create(userPublicKey, adminGroup);
+ databaseStorage.createStorage(hash, adminGroup, creationDate, value->data.data(), value->data.size());
}
void Database::deserializeAddRequest(const shared_ptr<dht::Value> &value, const Hash &requestDataHash, const std::shared_ptr<Hash> &nodeHash, const shared_ptr<OwnedMemory> encryptionKey)
@@ -548,108 +534,8 @@ namespace odhtdb
*/
DatabaseOperation operation = deserializerUnsigned.extract<DatabaseOperation>();
-#if 0
- const Hash *node = databaseStorage.getNodeByUserPublicKey(creatorPublicKey);
- if(!node)
- {
- // The user (public key) could belong to a node but we might not have retrieved the node info yet since data may
- // not be retrieved in order.
- // Data in quarantine is processed when 'create' packet is received or removed after 60 seconds
- databaseStorage.addToQuarantine(requestDataHash, creatorPublicKey, creationDate, value->data.data(), value->data.size());
- throw RequestQuarantineException();
- }
-#endif
- auto creatorUser = databaseStorage.getUserByPublicKey(*nodeHash, creatorPublicKey);
- if(!creatorUser)
- {
- // TODO: Add to quarantine
- string errMsg = "User with public key ";
- errMsg += creatorPublicKey.toString();
- errMsg += " does not exist in code ";
- errMsg += nodeHash->toString();
- throw sibs::DeserializeException(errMsg);
- }
-
- DataView encryptedDataView((void*)deserializerUnsigned.getBuffer(), deserializerUnsigned.getSize());
-
- if(operation == DatabaseOperation::ADD_DATA)
- {
- if(deserializerUnsigned.getSize() < ENCRYPTION_NONCE_BYTE_SIZE)
- throw sibs::DeserializeException("Unsigned encrypted body is too small (unable to extract nonce)");
-
- if(!creatorUser->isAllowedToPerformAction(PermissionType::ADD_DATA))
- {
- // TODO: User might have permission to perform operation, but we haven't got the packet that adds user to the group with the permission,
- // or we haven't received the packet that modifies group with the permission to perform the operation.
- // This also means that an user can be in a group that has permission to perform the operation and then later be removed from it,
- // and remote peers would accept our request to perform operation if they haven't received the operation that removes the user from the group.
- // How to handle this?
- string errMsg = "User ";
- errMsg += creatorUser->getName();
- errMsg += " is not allowed to add data to node ";
- errMsg += nodeHash->toString();
- throw PermissionDeniedException(errMsg);
- }
-
- // 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(*nodeHash, requestDataHash, operation, creatorUser, creationDate, value->data.data(), value->data.size(), encryptedDataView);
- }
- else if(operation == DatabaseOperation::ADD_USER)
- {
- u8 nameLength = deserializerUnsigned.extract<u8>();
-
- u8 nonce[ENCRYPTION_NONCE_BYTE_SIZE];
- deserializerUnsigned.extract(nonce, ENCRYPTION_NONCE_BYTE_SIZE);
- DataView dataToDecrypt((void*)deserializerUnsigned.getBuffer(), ENCRYPTION_CHECKSUM_BYTE_SIZE + nameLength);
-
- sibs::SafeDeserializer deserializerSkippedEncryptedData(deserializerUnsigned.getBuffer() + ENCRYPTION_CHECKSUM_BYTE_SIZE + nameLength, PUBLIC_KEY_NUM_BYTES + GROUP_ID_LENGTH);
-
- char userToAddPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializerSkippedEncryptedData.extract((u8*)userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey userToAddPublicKey(userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
-
- uint8_t groupId[GROUP_ID_LENGTH];
- deserializerSkippedEncryptedData.extract(groupId, GROUP_ID_LENGTH);
-
- auto group = databaseStorage.getGroupById(*nodeHash, groupId);
- if(group)
- {
- auto user = RemoteUser::create(userToAddPublicKey, "ENCRYPTED USER NAME", group);
- // 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)
- {
- // TODO: User might have permission to perform operation, but we haven't got the packet that adds user to the group with the permission,
- // or we haven't received the packet that modifies group with the permission to perform the operation.
- // This also means that an user can be in a group that has permission to perform the operation and then later be removed from it,
- // and remote peers would accept our request to perform operation if they haven't received the operation that removes the user from the group.
- // How to handle this?
- string errMsg = "User ";
- errMsg += creatorUser->getName();
- errMsg += " is not allowed to perform the operation: ";
- errMsg += to_string((u8)operation);
- throw PermissionDeniedException(errMsg);
- }
-
- // 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(*nodeHash, requestDataHash, operation, creatorUser, creationDate, value->data.data(), value->data.size(), encryptedDataView);
- }
- else
- {
- throw sibs::DeserializeException("TODO: Add to quarantine? You can receive ADD_USER packet before you receive ADD_GROUP");
- }
- }
- else
- {
- string errMsg = "Got unexpected operation: ";
- errMsg += to_string((u8)operation);
- throw sibs::DeserializeException(errMsg);
- }
+ DataView additionalDataView((void*)deserializerUnsigned.getBuffer(), deserializerUnsigned.getSize());
+ databaseStorage.appendStorage(*nodeHash, requestDataHash, operation, creatorPublicKey, creationDate, value->data.data(), value->data.size(), additionalDataView);
}
bool Database::listenCreateData(shared_ptr<dht::Value> value, const Hash &hash, const shared_ptr<OwnedMemory> encryptionKey)
@@ -657,7 +543,7 @@ namespace odhtdb
Log::debug("Got create data");
try
{
- if(databaseStorage.getStorage(hash))
+ if(databaseStorage.doesNodeExist(hash))
throw DatabaseStorageAlreadyExists("Create request hash is equal to hash already in storage (duplicate data?)");
deserializeCreateRequest(value, hash, encryptionKey);
}
@@ -673,7 +559,7 @@ namespace odhtdb
Log::debug("Got add data");
try
{
- if(databaseStorage.getDataById(requestDataHash))
+ if(databaseStorage.doesDataExist(requestDataHash))
throw DatabaseStorageAlreadyExists("Add data request hash is equal to hash already in storage (duplicate data?)");
deserializeAddRequest(value, requestDataHash, nodeHash, encryptionKey);
//Log::debug("Got add object, timestamp: %zu", addObject.timestamp);
@@ -688,24 +574,4 @@ namespace odhtdb
}
return true;
}
-
- void Database::setOnCreateNodeCallback(function<void(const DatabaseCreateNodeRequest&)> callbackFunc)
- {
- onCreateNodeCallbackFunc = callbackFunc;
- }
-
- void Database::setOnAddNodeCallback(function<void(const DatabaseAddNodeRequest&)> callbackFunc)
- {
- onAddNodeCallbackFunc = callbackFunc;
- }
-
- void Database::setOnAddUserCallback(function<void(const DatabaseAddUserRequest&)> callbackFunc)
- {
- onAddUserCallbackFunc = callbackFunc;
- }
-
- DatabaseStorage& Database::getStorage()
- {
- return databaseStorage;
- }
}
diff --git a/src/DatabaseStorage.cpp b/src/DatabaseStorage.cpp
index 0e7a1a8..501bd35 100644
--- a/src/DatabaseStorage.cpp
+++ b/src/DatabaseStorage.cpp
@@ -8,11 +8,13 @@
#include "../include/odhtdb/PasswordHash.hpp"
#include "../include/odhtdb/Log.hpp"
#include "../include/odhtdb/Database.hpp"
+#include "../include/odhtdb/sql/SqlQuery.hpp"
#include <cstring>
#include <chrono>
#include <boost/filesystem/convenience.hpp>
#include <sibs/SafeSerializer.hpp>
#include <sodium/randombytes.h>
+#include <sqlite3.h>
using namespace std;
@@ -24,15 +26,8 @@ namespace odhtdb
STORAGE_TYPE_APPEND
};
- enum class DecryptedDataType : u8
- {
- STORAGE_CREATE,
- STORAGE_ADD_DATA,
- STORAGE_ADD_USER
- };
-
const u64 QUARANTINE_STORAGE_TIME_MICROSECONDS = 60 * 1.0e6;
- const u16 STORAGE_VERSION = 2;
+ const u16 STORAGE_VERSION = 3;
DatabaseStorageObject::DatabaseStorageObject(const Hash &_requestHash, DataView &_data, u64 _timestamp, const Signature::PublicKey &_creatorPublicKey) :
requestHash(_requestHash),
@@ -50,30 +45,124 @@ namespace odhtdb
storedTimestamp = chrono::duration_cast<chrono::microseconds>(time).count();
}
+ static void sqlite_exec_checked(sqlite3 *db, const char *sql)
+ {
+ char *dbErrMsg = nullptr;
+ int rc = sqlite3_exec(db, sql, nullptr, nullptr, &dbErrMsg);
+ if(rc != SQLITE_OK)
+ {
+ string errMsg = "Failed to run sqlite exec, error: ";
+ errMsg += dbErrMsg;
+ sqlite3_free(dbErrMsg);
+ throw DatabaseStorageException(errMsg);
+ }
+ }
+
+ static void sqlite_prepare_checked(sqlite3 *db, const char *sql, sqlite3_stmt **stmt)
+ {
+ int rc = sqlite3_prepare_v2(db, sql, -1, stmt, nullptr);
+ if(rc != SQLITE_OK)
+ {
+ string errMsg = "Failed to run sqlite prepare statement, error: ";
+ errMsg += sqlite3_errmsg(db);
+ throw DatabaseStorageException(errMsg);
+ }
+ }
+
DatabaseStorage::DatabaseStorage(Database *_database, const boost::filesystem::path &storagePath) :
database(_database),
- groupsFilePath(storagePath / "groups"),
- usersFilePath(storagePath / "users"),
- dataFilePath(storagePath / "data"),
- metadataFilePath(storagePath / "metadata"),
- localUsersFilePath(storagePath / "local_users"),
- nodeDecryptionKeysFilePath(storagePath / "node_keys"),
- decryptedDataFilePath(storagePath / "decrypted_data")
+ sqliteDb(nullptr),
+ insertNodeStmt(nullptr),
+ insertUserStmt(nullptr),
+ insertGroupStmt(nullptr),
+ insertNodeAddDataStmt(nullptr),
+ setNodeDecryptionKeyStmt(nullptr),
+ getNodeDecryptionKeyStmt(nullptr),
+ selectNodeStmt(nullptr),
+ selectNodeAddDataByNodeStmt(nullptr),
+ selectNodeIdStatement(nullptr),
+ selectNodeAddDataIdStatement(nullptr),
+ insertNodeUserGroupAssocStmt(nullptr),
+ insertNodeRawStmt(nullptr),
+ insertNodeAddDataRawStmt(nullptr),
+ insertNodeAddDataAdditionalStmt(nullptr),
+ insertNodeAddUserDataStmt(nullptr),
+ selectNodeAddDataAdditionalStmt(nullptr),
+ selectNodeAddUserDataStmt(nullptr),
+ setNodeAddDataDecryptedStmt(nullptr),
+ setNodeAddDataAdditionalDataStmt(nullptr),
+ metadataFilePath(storagePath / "metadata")
+ {
+ try
+ {
+ init(storagePath);
+ }
+ catch(DatabaseStorageException &e)
+ {
+ cleanup();
+ throw e;
+ }
+ }
+
+ void DatabaseStorage::init(const boost::filesystem::path &storagePath)
{
boost::filesystem::create_directories(storagePath);
+ Log::debug("sqlite version: %s", sqlite3_libversion());
+
+ int rc = sqlite3_open((storagePath / "database.sqlite3").string().c_str(), &sqliteDb);
+ if(rc != SQLITE_OK)
+ {
+ string errMsg = "Failed to open database: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ throw DatabaseStorageException(errMsg);
+ }
+
+ sqlite_exec_checked(sqliteDb,
+ "CREATE TABLE IF NOT EXISTS Node(id INTEGER PRIMARY KEY, nodeHash BLOB UNIQUE NOT NULL, timestamp INTEGER NOT NULL, creatorPublicKey BLOB NOT NULL, adminGroupId BLOB NOT NULL);"
+ "CREATE TABLE IF NOT EXISTS NodeUser(node BLOB NOT NULL, publicKey BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));"
+ "CREATE TABLE IF NOT EXISTS NodeGroup(node BLOB NOT NULL, groupId BLOB UNIQUE NOT NULL, permissionLevel INT NOT NULL, permissionFlags INTEGER NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));"
+ "CREATE TABLE IF NOT EXISTS NodeAddData(id INTEGER PRIMARY KEY, node BLOB NOT NULL, requestHash BLOB UNIQUE NOT NULL, operation INT NOT NULL, timestamp INTEGER NOT NULL, creatorPublicKey BLOB NOT NULL, decrypted INT NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));"
+ "CREATE TABLE IF NOT EXISTS NodeAddDataAdditional(id INTEGER PRIMARY KEY, nodeAddDataId INTEGER NOT NULL, data BLOB NOT NULL, FOREIGN KEY(nodeAddDataId) REFERENCES NodeAddData(id));"
+ "CREATE TABLE IF NOT EXISTS NodeAddUserData(id INTEGER PRIMARY KEY, nodeAddDataId INTEGER NOT NULL, userToAddPublicKey BLOB NOT NULL, groupId BLOB NOT NULL, FOREIGN KEY(nodeAddDataId) REFERENCES NodeAddData(id));"
+ "CREATE TABLE IF NOT EXISTS NodeDecryptionKey(node BLOB UNIQUE NOT NULL, decryptionKey BLOB NOT NULL);"
+ "CREATE TABLE IF NOT EXISTS NodeUserGroupAssoc(node BLOB NOT NULL, userPublicKey BLOB NOT NULL, groupId BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash), FOREIGN KEY(userPublicKey) REFERENCES NodeUser(publicKey), FOREIGN KEY(groupId) REFERENCES NodeGroup(groupId));"
+
+ "CREATE TABLE IF NOT EXISTS NodeRaw(node INTEGER NOT NULL, data BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(id));"
+ "CREATE TABLE IF NOT EXISTS NodeAddDataRaw(node INTEGER NOT NULL, nodeAddData INTEGER NOT NULL, data BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(id), FOREIGN KEY(nodeAddData) REFERENCES NodeAddData(id));"
+
+ "CREATE UNIQUE INDEX IF NOT EXISTS UniqueUserInNode ON NodeUser(node, publicKey);"
+ "CREATE UNIQUE INDEX IF NOT EXISTS UniqueUserGroupAssoc ON NodeUserGroupAssoc(node, userPublicKey, groupId);");
+
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO Node(nodeHash, timestamp, creatorPublicKey, adminGroupId) VALUES(?, ?, ?, ?)", &insertNodeStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeUser(node, publicKey) VALUES(?, ?)", &insertUserStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeGroup(node, groupId, permissionLevel, permissionFlags) VALUES(?, ?, ?, ?)", &insertGroupStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddData(node, requestHash, operation, timestamp, creatorPublicKey, decrypted) VALUES(?, ?, ?, ?, ?, ?)", &insertNodeAddDataStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT OR REPLACE INTO NodeDecryptionKey(node, decryptionKey) VALUES(?, ?)", &setNodeDecryptionKeyStmt);
+ sqlite_prepare_checked(sqliteDb, "SELECT decryptionKey FROM NodeDecryptionKey WHERE node = ?", &getNodeDecryptionKeyStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeUserGroupAssoc(node, userPublicKey, groupId) VALUES(?, ?, ?)", &insertNodeUserGroupAssocStmt);
+
+ sqlite_prepare_checked(sqliteDb, "SELECT timestamp, creatorPublicKey, adminGroupId FROM Node WHERE nodeHash = ?", &selectNodeStmt);
+ sqlite_prepare_checked(sqliteDb, "SELECT id, requestHash, operation, timestamp, creatorPublicKey From NodeAddData WHERE node = ?", &selectNodeAddDataByNodeStmt);
+ sqlite_prepare_checked(sqliteDb, "SELECT id FROM Node WHERE nodeHash = ?", &selectNodeIdStatement);
+ sqlite_prepare_checked(sqliteDb, "SELECT id FROM NodeAddData WHERE requestHash = ?", &selectNodeAddDataIdStatement);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeRaw(node, data) VALUES(?, ?)", &insertNodeRawStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddDataRaw(node, nodeAddData, data) VALUES(?, ?, ?)", &insertNodeAddDataRawStmt);
+
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddDataAdditional(nodeAddDataId, data) VALUES(?, ?)", &insertNodeAddDataAdditionalStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddUserData(nodeAddDataId, userToAddPublicKey, groupId) VALUES(?, ?, ?)", &insertNodeAddUserDataStmt);
+
+ sqlite_prepare_checked(sqliteDb, "SELECT data From NodeAddDataAdditional WHERE nodeAddDataId = ?", &selectNodeAddDataAdditionalStmt);
+ sqlite_prepare_checked(sqliteDb, "SELECT userToAddPublicKey, groupId From NodeAddUserData WHERE nodeAddDataId = ?", &selectNodeAddUserDataStmt);
+
+ sqlite_prepare_checked(sqliteDb, "UPDATE NodeAddData SET decrypted = ? WHERE id = ?", &setNodeAddDataDecryptedStmt);
+ sqlite_prepare_checked(sqliteDb, "UPDATE NodeAddDataAdditional SET data = ? WHERE nodeAddDataId = ?", &setNodeAddDataAdditionalDataStmt);
+
bool metadataLoaded = false;
try
{
loadMetadataFromFile();
metadataLoaded = true;
- loadGroupsFromFile();
- loadUsersFromFile();
- loadDataFromFile();
- loadLocalUsersFromFile();
- loadNodeDecryptionKeysFromFile();
- loadDecryptedDataFromFile();
- //loadQuarantineFromFile();
}
catch(FileException &e)
{
@@ -113,651 +202,474 @@ namespace odhtdb
}
}
- void DatabaseStorage::loadGroupsFromFile()
+ void DatabaseStorage::cleanup()
{
- 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<Group*> *groupByIdMap = nullptr;
- auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash);
- if(groupByIdMapIt == nodeGroupByIdMap.end())
- {
- groupByIdMap = new DataViewMap<Group*>();
- nodeGroupByIdMap[nodeHash] = groupByIdMap;
- }
- else
- groupByIdMap = groupByIdMapIt->second;
-
- u8 groupNameSize = deserializer.extract<u8>();
- 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<u8>();
- u32 permissionFlags = deserializer.extract<u32>();
-
- 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 %s from file", groupIdStr.c_str());
- }
+ sqlite3_finalize(insertNodeStmt);
+ sqlite3_finalize(insertUserStmt);
+ sqlite3_finalize(insertGroupStmt);
+ sqlite3_finalize(insertNodeAddDataStmt);
+ sqlite3_finalize(setNodeDecryptionKeyStmt);
+ sqlite3_finalize(getNodeDecryptionKeyStmt);
+ sqlite3_finalize(insertNodeUserGroupAssocStmt);
+ sqlite3_finalize(selectNodeStmt);
+ sqlite3_finalize(selectNodeAddDataByNodeStmt);
+ sqlite3_finalize(selectNodeIdStatement);
+ sqlite3_finalize(selectNodeAddDataIdStatement);
+ sqlite3_finalize(insertNodeRawStmt);
+ sqlite3_finalize(insertNodeAddDataRawStmt);
+ sqlite3_finalize(insertNodeAddDataAdditionalStmt);
+ sqlite3_finalize(insertNodeAddUserDataStmt);
+ sqlite3_finalize(selectNodeAddDataAdditionalStmt);
+ sqlite3_finalize(selectNodeAddUserDataStmt);
+ sqlite3_finalize(setNodeAddDataDecryptedStmt);
+ sqlite3_finalize(setNodeAddDataAdditionalDataStmt);
+ sqlite3_close(sqliteDb);
}
- void DatabaseStorage::loadUsersFromFile()
+ DatabaseStorage::~DatabaseStorage()
{
- if(!boost::filesystem::exists(usersFilePath)) return;
-
- OwnedMemory usersFileContent = fileGetContent(usersFilePath);
- sibs::SafeDeserializer deserializer((u8*)usersFileContent.data, usersFileContent.size);
-
- while(!deserializer.empty())
+ cleanup();
+ }
+
+ void DatabaseStorage::loadNode(const Hash &nodeHash)
+ {
+ if(database->onCreateNodeCallbackFunc)
{
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
-
- Signature::MapPublicKey<User*> *publicKeyUserDataMap = nullptr;
- auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash);
- if(publicKeyUserDataMapIt == nodePublicKeyUserDataMap.end())
+ SqlQuery nodeQuery(sqliteDb, "SELECT timestamp, creatorPublicKey, adminGroupId From Node WHERE nodeHash = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ while(nodeQuery.next())
{
- publicKeyUserDataMap = new Signature::MapPublicKey<User*>();
- nodePublicKeyUserDataMap[nodeHash] = publicKeyUserDataMap;
+ u64 timestamp = nodeQuery.getInt64(0);
+ const DataView creatorPublicKeyRaw = nodeQuery.getBlob(1);
+ Signature::PublicKey creatorPublicKey((const char*)creatorPublicKeyRaw.data, creatorPublicKeyRaw.size);
+ u8 adminGroupId[GROUP_ID_LENGTH];
+ const DataView adminGroupRaw = nodeQuery.getBlob(2);
+ memcpy(adminGroupId, adminGroupRaw.data, GROUP_ID_LENGTH);
+
+ const DatabaseCreateNodeRequest createNodeRequest(&nodeHash, timestamp, &creatorPublicKey, DataView(adminGroupId, GROUP_ID_LENGTH));
+ database->onCreateNodeCallbackFunc(createNodeRequest);
}
- else
- publicKeyUserDataMap = publicKeyUserDataMapIt->second;
+ }
+
+ SqlQuery nodeQuery(sqliteDb, "SELECT id, requestHash, operation, timestamp, creatorPublicKey From NodeAddData WHERE node = ? AND decrypted = 1", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ while(nodeQuery.next())
+ {
+ i64 rowId = nodeQuery.getInt64(0);
- DataViewMap<Group*> *groupByIdMap = nullptr;
- auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash);
- if(groupByIdMapIt == nodeGroupByIdMap.end())
- {
- groupByIdMap = new DataViewMap<Group*>();
- nodeGroupByIdMap[nodeHash] = groupByIdMap;
- }
- else
- groupByIdMap = groupByIdMapIt->second;
+ const DataView requestHashRaw = nodeQuery.getBlob(1);
+ Hash requestHash;
+ memcpy(requestHash.getData(), requestHashRaw.data, HASH_BYTE_SIZE);
- u8 usernameSize = deserializer.extract<u8>();
- string username;
- username.resize(usernameSize);
- deserializer.extract((u8*)&username[0], usernameSize);
+ DatabaseOperation operation = (DatabaseOperation)nodeQuery.getInt(2);
- u8 userPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializer.extract(userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey userPublicKey((const char*)userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+ u64 timestamp = nodeQuery.getInt64(3);
- User *user = RemoteUser::create(userPublicKey, username, nullptr);
+ const DataView creatorPublicKeyRaw = nodeQuery.getBlob(4);
+ Signature::PublicKey creatorPublicKey((const char*)creatorPublicKeyRaw.data, creatorPublicKeyRaw.size);
- u8 numGroups = deserializer.extract<u8>();
- for(int i = 0; i < numGroups; ++i)
+ if(operation == DatabaseOperation::ADD_DATA)
{
- u8 groupId[GROUP_ID_LENGTH];
- deserializer.extract(groupId, GROUP_ID_LENGTH);
+ if(!database->onAddNodeCallbackFunc) continue;
+ sqlite3_reset(selectNodeAddDataAdditionalStmt);
+ sqlite3_clear_bindings(selectNodeAddDataAdditionalStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(selectNodeAddDataAdditionalStmt, 1, rowId);
+ bindCheckError(rc);
- auto groupIt = groupByIdMap->find(DataView(groupId, GROUP_ID_LENGTH));
- if(groupIt == groupByIdMap->end())
+ rc = sqlite3_step(selectNodeAddDataAdditionalStmt);
+ if(rc != SQLITE_ROW)
{
- string errMsg = "User group with id ";
- errMsg += bin2hex((const char*)groupId, GROUP_ID_LENGTH);
- errMsg += " does not exist";
- throw DatabaseStorageCorrupt(errMsg);
+ string errMsg = "select NodeAddDataAdditional failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ throw DatabaseStorageException(errMsg);
}
- user->addToGroup(groupIt->second);
+
+ // TODO: There is no need to allocate/deallocate several times, this can be moved outside the while loop
+ const void *decryptedDataRaw = sqlite3_column_blob(selectNodeAddDataAdditionalStmt, 0);
+ int decryptedDataSize = sqlite3_column_bytes(selectNodeAddDataAdditionalStmt, 0);
+ OwnedMemory decryptedData(new u8[decryptedDataSize], decryptedDataSize);
+ memcpy(decryptedData.data, decryptedDataRaw, decryptedDataSize);
+
+ const DatabaseAddNodeRequest addNodeRequest(&nodeHash, &requestHash, timestamp, &creatorPublicKey, DataView(decryptedData.data, decryptedData.size));
+ database->onAddNodeCallbackFunc(addNodeRequest);
+ }
+ else if(operation == DatabaseOperation::ADD_USER)
+ {
+ if(!database->onAddUserCallbackFunc) continue;
+ sqlite3_reset(selectNodeAddUserDataStmt);
+ sqlite3_clear_bindings(selectNodeAddUserDataStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(selectNodeAddUserDataStmt, 1, rowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeAddUserDataStmt);
+ if(rc != SQLITE_ROW)
+ {
+ string errMsg = "select NodeAddUserData failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ throw DatabaseStorageException(errMsg);
+ }
+
+ const void *userToAddPublicKeyRaw = sqlite3_column_blob(selectNodeAddUserDataStmt, 0);
+ Signature::PublicKey userToAddPublicKey((const char*)userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+
+ const void *groupToAddUserToRaw = sqlite3_column_blob(selectNodeAddUserDataStmt, 1);
+ u8 groupToAddUserTo[GROUP_ID_LENGTH];
+ memcpy(groupToAddUserTo, groupToAddUserToRaw, GROUP_ID_LENGTH);
+
+ DatabaseAddUserRequest addUserRequest(&nodeHash, &requestHash, timestamp, &creatorPublicKey, &userToAddPublicKey, DataView(groupToAddUserTo, GROUP_ID_LENGTH));
+ database->onAddUserCallbackFunc(addUserRequest);
}
-
- (*publicKeyUserDataMap)[user->getPublicKey()] = user;
}
}
- void DatabaseStorage::loadStorageCreate(sibs::SafeDeserializer &deserializer)
+ void DatabaseStorage::loadMetadataFromFile()
{
- u64 timestamp = deserializer.extract<u64>();
-
- u8 userPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializer.extract(userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey userPublicKey((const char*)userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
-
- u8 groupId[GROUP_ID_LENGTH];
- deserializer.extract(groupId, GROUP_ID_LENGTH);
+ OwnedMemory metadataFileContent = fileGetContent(metadataFilePath);
+ sibs::SafeDeserializer deserializer((u8*)metadataFileContent.data, metadataFileContent.size);
- u32 dataSize = deserializer.extract<u32>();
- u8 *data = new u8[dataSize];
- deserializer.extract(data, dataSize);
- u32 offsetToEncryptedData = deserializer.extract<u32>();
+ u16 storageVersion = deserializer.extract<u16>();
+ if(storageVersion != STORAGE_VERSION)
+ throw std::runtime_error("Wrong storage version!");
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
+ deserializer.extract(passwordSalt, PASSWORD_SALT_LEN);
- auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash);
- if(groupByIdMapIt == nodeGroupByIdMap.end())
- {
- string errMsg = "No groups exists in node ";
- errMsg += nodeHash.toString();
- throw DatabaseStorageCorrupt(errMsg);
- }
+ u16 privateKeySize = deserializer.extract<u16>();
+ dht::Blob privateKeyRaw;
+ privateKeyRaw.resize(privateKeySize);
+ deserializer.extract(&privateKeyRaw[0], privateKeySize);
+ identity.first = make_shared<dht::crypto::PrivateKey>(privateKeyRaw);
- 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);
- }
+ u16 certificateSize = deserializer.extract<u16>();
+ dht::Blob certificateRaw;
+ certificateRaw.resize(certificateSize);
+ deserializer.extract(&certificateRaw[0], certificateSize);
+ identity.second = make_shared<dht::crypto::Certificate>(certificateRaw);
- DatabaseStorageObjectList *databaseStorageObjectList = new DatabaseStorageObjectList(userPublicKey);
- databaseStorageObjectList->createdTimestamp = timestamp;
- databaseStorageObjectList->groups.push_back(groupIt->second);
- databaseStorageObjectList->data = DataView(data, dataSize);
- databaseStorageObjectList->offsetToEncryptedData = offsetToEncryptedData;
- storageMap[nodeHash] = databaseStorageObjectList;
+ assert(deserializer.empty());
}
- void DatabaseStorage::loadStorageAppend(sibs::SafeDeserializer &deserializer)
+ static void sqlite_step_rollback_on_failure(sqlite3 *db, sqlite3_stmt *stmt, const char *description)
{
- u64 timestamp = deserializer.extract<u64>();
-
- 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<u32>();
- 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);
-
- auto storageIt = storageMap.find(nodeHash);
- if(storageIt == storageMap.end())
+ int rc = sqlite3_step(stmt);
+ if(rc != SQLITE_DONE)
{
- string errMsg = "Database storage with hash ";
- errMsg += nodeHash.toString();
- errMsg += " not found";
- throw DatabaseStorageCorrupt(errMsg);
+ string errMsg = description;
+ errMsg += " failed with error: ";
+ errMsg += sqlite3_errmsg(db);
+ sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
+ if(rc == SQLITE_CONSTRAINT)
+ throw DatabaseStorageAlreadyExists(errMsg);
+ else
+ throw DatabaseStorageException(errMsg);
}
-
- DataView storageData { data, dataSize };
- DatabaseStorageObject *databaseStorageObject = new DatabaseStorageObject(dataHash, storageData, timestamp, creatorPublicKey);
- storageIt->second->objects.push_back(databaseStorageObject);
- storedDataHash[dataHash] = databaseStorageObject;
}
- void DatabaseStorage::loadDataFromFile()
+ void DatabaseStorage::bindCheckError(int sqliteBindResult)
{
- if(!boost::filesystem::exists(dataFilePath)) return;
-
- OwnedMemory dataFileContent = fileGetContent(dataFilePath);
- sibs::SafeDeserializer deserializer((u8*)dataFileContent.data, dataFileContent.size);
-
- while(!deserializer.empty())
+ if(sqliteBindResult != SQLITE_OK)
{
- StorageType storageType = deserializer.extract<StorageType>();
- switch(storageType)
- {
- case STORAGE_TYPE_CREATE:
- loadStorageCreate(deserializer);
- break;
- case STORAGE_TYPE_APPEND:
- loadStorageAppend(deserializer);
- break;
- }
+ string errMsg = "Failed to bind param, error code: ";
+ errMsg += to_string(sqliteBindResult);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
}
}
- void DatabaseStorage::loadLocalUsersFromFile()
+ i64 DatabaseStorage::getNodeRowId(const Hash &nodeHash)
{
- if(!boost::filesystem::exists(localUsersFilePath)) return;
-
- OwnedMemory localUsersFileContent = fileGetContent(localUsersFilePath);
- sibs::SafeDeserializer deserializer((u8*)localUsersFileContent.data, localUsersFileContent.size);
+ sqlite3_reset(selectNodeIdStatement);
+ sqlite3_clear_bindings(selectNodeIdStatement);
- while(!deserializer.empty())
+ int rc;
+ rc = sqlite3_bind_blob(selectNodeIdStatement, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeIdStatement);
+ if(rc != SQLITE_ROW)
{
- u8 usernameSize = deserializer.extract<u8>();
- 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);
-
- EncryptedPrivateKey encryptedPrivateKey;
- deserializer.extract(encryptedPrivateKey.nonce, ENCRYPTION_NONCE_BYTE_SIZE);
- deserializer.extract(encryptedPrivateKey.encryptedPrivateKey, ENCRYPTION_CHECKSUM_BYTE_SIZE + PRIVATE_KEY_NUM_BYTES);
-
- nameLocalUsersMap[username] = LocalUserEncrypted::create(userPublicKey, encryptedPrivateKey, username);
+ string errMsg = "select Node id failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
}
+
+ return sqlite3_column_int64(selectNodeIdStatement, 0);
}
- void DatabaseStorage::loadNodeDecryptionKeysFromFile()
+ i64 DatabaseStorage::getNodeAddDataRowId(const Hash &requestHash)
{
- if(!boost::filesystem::exists(nodeDecryptionKeysFilePath)) return;
+ sqlite3_reset(selectNodeAddDataIdStatement);
+ sqlite3_clear_bindings(selectNodeAddDataIdStatement);
- OwnedMemory nodeKeysFileContent = fileGetContent(nodeDecryptionKeysFilePath);
- sibs::SafeDeserializer deserializer((u8*)nodeKeysFileContent.data, nodeKeysFileContent.size);
-
- while(!deserializer.empty())
+ int rc;
+ rc = sqlite3_bind_blob(selectNodeAddDataIdStatement, 1, requestHash.getData(), requestHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeAddDataIdStatement);
+ if(rc != SQLITE_ROW)
{
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
-
- u8 *nodeKeyRaw = new u8[ENCRYPTION_KEY_BYTE_SIZE];
- deserializer.extract(nodeKeyRaw, ENCRYPTION_KEY_BYTE_SIZE);
- nodeDecryptionKeyMap[nodeHash] = make_shared<OwnedMemory>(nodeKeyRaw, ENCRYPTION_KEY_BYTE_SIZE);
+ string errMsg = "select NodeAddData id failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
}
+
+ return sqlite3_column_int64(selectNodeAddDataIdStatement, 0);
}
- void DatabaseStorage::loadDecryptedStorageCreate(sibs::SafeDeserializer &deserializer)
+ bool DatabaseStorage::doesNodeExist(const Hash &nodeHash) const
{
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
-
- auto storageIt = storageMap.find(nodeHash);
- if(storageIt == storageMap.end())
- {
- string errMsg = "Database storage with hash ";
- errMsg += nodeHash.toString();
- errMsg += " not found";
- throw DatabaseStorageCorrupt(errMsg);
- }
-
- u8 creatorNameLength = deserializer.extract<u8>();
- auto creator = getUserByPublicKey(nodeHash, storageIt->second->creatorPublicKey);
- if(!creator)
- {
- string errMsg = "User with public key ";
- errMsg += storageIt->second->creatorPublicKey.toString();
- errMsg += " does not exist in node with hash ";
- errMsg += nodeHash.toString();
- throw DatabaseStorageCorrupt(errMsg);
- }
-
- creator->name.resize(creatorNameLength);
- deserializer.extract((u8*)&creator->name[0], creatorNameLength);
-
- u8 nodeNameLength = deserializer.extract<u8>();
- storageIt->second->nodeName.resize(nodeNameLength);
- deserializer.extract((u8*)&storageIt->second->nodeName[0], nodeNameLength);
- storageIt->second->isDecrypted = true;
+ SqlQuery query(sqliteDb, "SELECT id FROM Node WHERE nodeHash = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ return query.next();
}
- void DatabaseStorage::loadDecryptedStorageAddData(sibs::SafeDeserializer &deserializer)
+ bool DatabaseStorage::doesDataExist(const Hash &requestHash) const
{
- Hash requestHash;
- deserializer.extract((u8*)requestHash.getData(), HASH_BYTE_SIZE);
-
- auto storedDataIt = storedDataHash.find(requestHash);
- if(storedDataIt == storedDataHash.end())
- {
- string errMsg = "Database doesn't contain data with hash ";
- errMsg += requestHash.toString();
- throw DatabaseStorageCorrupt(errMsg);
- }
-
- u32 decryptedDataSize = deserializer.extract<u32>();
- u8 *decryptedDataRaw = new u8[decryptedDataSize];
- deserializer.extract(decryptedDataRaw, decryptedDataSize);
-
- storedDataIt->second->decryptedObject.data.data = decryptedDataRaw;
- storedDataIt->second->decryptedObject.data.size = decryptedDataSize;
- storedDataIt->second->decryptedObject.operation = DatabaseOperation::ADD_DATA;
+ SqlQuery query(sqliteDb, "SELECT id FROM NodeAddData WHERE requestHash = ?", { DataView(requestHash.getData(), requestHash.getSize()) });
+ return query.next();
}
- void DatabaseStorage::loadDecryptedStorageAddUser(sibs::SafeDeserializer &deserializer)
+ void DatabaseStorage::createStorage(const Hash &hash, Group *creatorGroup, u64 timestamp, const void *data, usize size)
{
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
-
- Hash requestHash;
- deserializer.extract((u8*)requestHash.getData(), HASH_BYTE_SIZE);
+ assert(creatorGroup->getUsers().size() == 1);
+ User *creator = (User*)creatorGroup->getUsers()[0];
- auto storedDataIt = storedDataHash.find(requestHash);
- if(storedDataIt == storedDataHash.end())
+ sqlite3_exec(sqliteDb, "BEGIN", 0, 0, 0);
{
- string errMsg = "Database doesn't contain data with hash ";
- errMsg += requestHash.toString();
- throw DatabaseStorageCorrupt(errMsg);
+ sqlite3_reset(insertNodeStmt);
+ sqlite3_clear_bindings(insertNodeStmt);
+
+ int rc;
+ rc = sqlite3_bind_blob(insertNodeStmt, 1, hash.getData(), hash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int64(insertNodeStmt, 2, timestamp);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeStmt, 3, creator->getPublicKey().getData(), creator->getPublicKey().getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeStmt, 4, creatorGroup->getId().data, GROUP_ID_LENGTH, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeStmt, "insert data into Node");
+ addGroup(hash, creatorGroup);
+ addUser(hash, creator->getPublicKey(), creatorGroup->getId());
}
-
- u8 addedUserPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializer.extract(addedUserPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey addedUserPublicKey((const char*)addedUserPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- auto addedUser = getUserByPublicKey(nodeHash, addedUserPublicKey);
- if(!addedUser)
{
- string errMsg = "User with public key ";
- errMsg += bin2hex((const char*)addedUserPublicKeyRaw, PUBLIC_KEY_NUM_BYTES).c_str();
- errMsg += " does not exist in node with hash ";
- errMsg += nodeHash.toString();
- throw DatabaseStorageCorrupt(errMsg);
+ sqlite3_reset(insertNodeRawStmt);
+ sqlite3_clear_bindings(insertNodeRawStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(insertNodeRawStmt, 1, getNodeRowId(hash));
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeRawStmt, 2, data, size, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeRawStmt, "insert data into NodeRaw");
}
+ sqlite3_exec(sqliteDb, "COMMIT", 0, 0, 0);
- u32 decryptedUserNameSize = deserializer.extract<u32>();
- addedUser->name.resize(decryptedUserNameSize);
- deserializer.extract((u8*)&addedUser->name[0], decryptedUserNameSize);
+ auto nodeDecryptionKeyResult = getNodeDecryptionKey(hash);
+ if(nodeDecryptionKeyResult.first)
+ decryptNodeData(hash, nodeDecryptionKeyResult.second, &creator->getPublicKey(), creatorGroup->getId(), timestamp);
}
- void DatabaseStorage::loadDecryptedDataFromFile()
+ void DatabaseStorage::appendStorage(const Hash &nodeHash, const Hash &dataHash, DatabaseOperation operation, const Signature::PublicKey &creatorPublicKey, u64 timestamp, const void *data, usize size, const DataView &additionalDataView)
{
- if(!boost::filesystem::exists(decryptedDataFilePath)) return;
-
- OwnedMemory decryptedDataFileContent = fileGetContent(decryptedDataFilePath);
- sibs::SafeDeserializer deserializer((u8*)decryptedDataFileContent.data, decryptedDataFileContent.size);
-
- while(!deserializer.empty())
+ sqlite3_exec(sqliteDb, "BEGIN", 0, 0, 0);
{
- DecryptedDataType decryptedDataType = deserializer.extract<DecryptedDataType>();
- switch(decryptedDataType)
- {
- case DecryptedDataType::STORAGE_CREATE:
- loadDecryptedStorageCreate(deserializer);
- break;
- case DecryptedDataType::STORAGE_ADD_DATA:
- loadDecryptedStorageAddData(deserializer);
- break;
- case DecryptedDataType::STORAGE_ADD_USER:
- loadDecryptedStorageAddUser(deserializer);
- break;
- }
+ sqlite3_reset(insertNodeAddDataStmt);
+ sqlite3_clear_bindings(insertNodeAddDataStmt);
+
+ int rc;
+ rc = sqlite3_bind_blob(insertNodeAddDataStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddDataStmt, 2, dataHash.getData(), dataHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int(insertNodeAddDataStmt, 3, (u8)operation);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int64(insertNodeAddDataStmt, 4, timestamp);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddDataStmt, 5, creatorPublicKey.getData(), creatorPublicKey.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int(insertNodeAddDataStmt, 6, 0);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeAddDataStmt, "insert data into NodeAddData");
}
- }
-
- void DatabaseStorage::loadMetadataFromFile()
- {
- OwnedMemory metadataFileContent = fileGetContent(metadataFilePath);
- sibs::SafeDeserializer deserializer((u8*)metadataFileContent.data, metadataFileContent.size);
- u16 storageVersion = deserializer.extract<u16>();
- if(storageVersion != STORAGE_VERSION)
- throw std::runtime_error("Wrong storage version!");
+ i64 nodeRowId = getNodeRowId(nodeHash);
+ i64 nodeAddRowId = getNodeAddDataRowId(dataHash);
- deserializer.extract(passwordSalt, PASSWORD_SALT_LEN);
- //string passwordSaltStr((const char*)passwordSalt, PASSWORD_SALT_LEN);
+ Signature::PublicKey userToAddPublicKey;
+ u8 groupToAddUserTo[GROUP_ID_LENGTH];
- u16 privateKeySize = deserializer.extract<u16>();
- dht::Blob privateKeyRaw;
- privateKeyRaw.resize(privateKeySize);
- deserializer.extract(&privateKeyRaw[0], privateKeySize);
- identity.first = make_shared<dht::crypto::PrivateKey>(privateKeyRaw);
-
- u16 certificateSize = deserializer.extract<u16>();
- dht::Blob certificateRaw;
- certificateRaw.resize(certificateSize);
- deserializer.extract(&certificateRaw[0], certificateSize);
- identity.second = make_shared<dht::crypto::Certificate>(certificateRaw);
-
- assert(deserializer.empty());
- }
-
- void DatabaseStorage::createStorage(const Hash &hash, Group *creatorGroup, u64 timestamp, const u8 *data, usize dataSize, u32 offsetToEncryptedData)
- {
- if(storageMap.find(hash) != storageMap.end())
+ if(operation == DatabaseOperation::ADD_DATA)
{
- string errMsg = "Database storage with hash ";
- errMsg += hash.toString();
- errMsg += " already exists";
- throw DatabaseStorageAlreadyExists(errMsg);
+ sqlite3_reset(insertNodeAddDataAdditionalStmt);
+ sqlite3_clear_bindings(insertNodeAddDataAdditionalStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(insertNodeAddDataAdditionalStmt, 1, nodeAddRowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddDataAdditionalStmt, 2, additionalDataView.data, additionalDataView.size, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeAddDataAdditionalStmt, "insert data into NodeAddDataAdditional");
}
-
- addGroup(hash, creatorGroup);
- assert(creatorGroup->getUsers().size() == 1);
- User *creator = (User*)creatorGroup->getUsers()[0];
- addUser(hash, creator);
-
- DatabaseStorageObjectList *databaseStorageObjectList = new DatabaseStorageObjectList(creator->getPublicKey());
- databaseStorageObjectList->createdTimestamp = timestamp;
- databaseStorageObjectList->groups.push_back(creatorGroup);
- databaseStorageObjectList->data = DataView(new u8[dataSize], dataSize);
- memcpy(databaseStorageObjectList->data.data, data, dataSize);
- databaseStorageObjectList->offsetToEncryptedData = offsetToEncryptedData;
- storageMap[hash] = databaseStorageObjectList;
-
- sibs::SafeSerializer serializer;
- serializer.add(STORAGE_TYPE_CREATE);
- serializer.add(timestamp);
- serializer.add((u8*)creator->getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES);
- serializer.add((u8*)creatorGroup->getId().data, GROUP_ID_LENGTH);
-
- serializer.add((u32)dataSize);
- serializer.add(data, dataSize);
- serializer.add(offsetToEncryptedData);
-
- serializer.add((u8*)hash.getData(), HASH_BYTE_SIZE);
-
- fileAppend(dataFilePath, { serializer.getBuffer().data(), serializer.getBuffer().size() });
-
- auto nodeDecryptionKeyIt = nodeDecryptionKeyMap.find(hash);
- if(nodeDecryptionKeyIt != nodeDecryptionKeyMap.end())
- decryptNodeData(hash, databaseStorageObjectList, nodeDecryptionKeyIt->second);
- }
-
- // TODO: Use encryptedDataView to remove duplicate of unsigning request, jumping straight to decrypting encrypted data and calling callback func
- void DatabaseStorage::appendStorage(const Hash &nodeHash, const Hash &dataHash, DatabaseOperation operation, const User *creatorUser, u64 timestamp, const u8 *data, usize dataSize, const DataView &encryptedDataView)
- {
- auto it = storageMap.find(nodeHash);
- if(it == storageMap.end())
+ else if(operation == DatabaseOperation::ADD_USER)
{
- string errMsg = "Database storage with hash ";
- errMsg += nodeHash.toString();
- errMsg += " not found. Storage for a hash needs to be created before data can be appended to it";
- throw DatabaseStorageNotFound(errMsg);
+ try
+ {
+ sibs::SafeDeserializer deserializer((const u8*)additionalDataView.data, additionalDataView.size);
+ deserializer.extract((u8*)userToAddPublicKey.getData(), PUBLIC_KEY_NUM_BYTES);
+ deserializer.extract(groupToAddUserTo, GROUP_ID_LENGTH);
+
+ sqlite3_reset(insertNodeAddUserDataStmt);
+ sqlite3_clear_bindings(insertNodeAddUserDataStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(insertNodeAddUserDataStmt, 1, nodeAddRowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddUserDataStmt, 2, userToAddPublicKey.getData(), PUBLIC_KEY_NUM_BYTES, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddUserDataStmt, 3, groupToAddUserTo, GROUP_ID_LENGTH, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeAddUserDataStmt, "insert data into NodeAddUserData");
+ }
+ catch(sibs::DeserializeException &e)
+ {
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw e;
+ }
}
-
- auto storeDataHashResult = storedDataHash.find(dataHash);
- if(storeDataHashResult != storedDataHash.end())
+ else
{
- string errMsg = "Database already contains data with hash: ";
- errMsg += dataHash.toString();
- throw DatabaseStorageAlreadyExists(errMsg);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw std::runtime_error("Unexpected operation type");
}
- DataView storageData { new u8[dataSize], dataSize };
- memcpy(storageData.data, data, dataSize);
- DatabaseStorageObject *databaseStorageObject = new DatabaseStorageObject(dataHash, storageData, timestamp, creatorUser->getPublicKey());
- it->second->objects.push_back(databaseStorageObject);
- storedDataHash[dataHash] = 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() });
+ {
+ sqlite3_reset(insertNodeAddDataRawStmt);
+ sqlite3_clear_bindings(insertNodeAddDataRawStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(insertNodeAddDataRawStmt, 1, nodeRowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int64(insertNodeAddDataRawStmt, 2, nodeAddRowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddDataRawStmt, 3, data, size, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeAddDataRawStmt, "insert data into NodeAddDataRaw");
+ }
- auto nodeDecryptionKeyIt = nodeDecryptionKeyMap.find(nodeHash);
- if(nodeDecryptionKeyIt != nodeDecryptionKeyMap.end())
- decryptNodeAppendedData(nodeHash, databaseStorageObject, nodeDecryptionKeyIt->second);
- }
-
- void DatabaseStorage::addToQuarantine(const Hash &dataHash, const Signature::PublicKey &creatorPublicKey, u64 timestamp, const u8 *data, usize dataSize)
- {
- auto storeDataHashResult = storedDataHash.find(dataHash);
- if(storeDataHashResult != storedDataHash.end())
+ auto nodeDecryptionKeyResult = getNodeDecryptionKey(nodeHash);
+ if(nodeDecryptionKeyResult.first)
{
- string errMsg = "Database already contains data with hash: ";
- errMsg += dataHash.toString();
- throw DatabaseStorageAlreadyExists(errMsg);
+ if(operation == DatabaseOperation::ADD_DATA)
+ decryptNodeAddData(nodeAddRowId, nodeHash, dataHash, timestamp, &creatorPublicKey, additionalDataView, nodeDecryptionKeyResult.second);
+ else if(operation == DatabaseOperation::ADD_USER)
+ decryptNodeAddUser(nodeAddRowId, nodeHash, dataHash, timestamp, &creatorPublicKey, &userToAddPublicKey, DataView(groupToAddUserTo, GROUP_ID_LENGTH), nodeDecryptionKeyResult.second);
}
- DataView storageData { new u8[dataSize], dataSize };
- memcpy(storageData.data, data, dataSize);
- DatabaseStorageQuarantineObject *databaseQuarantineStorageObject = new DatabaseStorageQuarantineObject(storageData, timestamp, creatorPublicKey);
- quarantineStorageMap[creatorPublicKey].emplace_back(databaseQuarantineStorageObject);
- // TODO: Add quarantine object to storedDataHash
+ sqlite3_exec(sqliteDb, "COMMIT", 0, 0, 0);
}
- bool DatabaseStorage::addGroup(const Hash &nodeHash, Group *group)
+ void DatabaseStorage::addGroup(const Hash &nodeHash, Group *group)
{
- DataViewMap<Group*> *groupByIdMap = nullptr;
- auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash);
- if(groupByIdMapIt == nodeGroupByIdMap.end())
- {
- groupByIdMap = new DataViewMap<Group*>();
- nodeGroupByIdMap[nodeHash] = groupByIdMap;
- }
- else
- groupByIdMap = groupByIdMapIt->second;
+ sqlite3_reset(insertGroupStmt);
+ sqlite3_clear_bindings(insertGroupStmt);
- if(groupByIdMap->find(group->getId()) != groupByIdMap->end())
- return false;
-
- (*groupByIdMap)[group->getId()] = group;
+ int rc;
+ rc = sqlite3_bind_blob(insertGroupStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
- sibs::SafeSerializer serializer;
+ rc = sqlite3_bind_blob(insertGroupStmt, 2, group->getId().data, GROUP_ID_LENGTH, SQLITE_STATIC);
+ bindCheckError(rc);
- serializer.add((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
+ rc = sqlite3_bind_int(insertGroupStmt, 3, group->getPermission().getPermissionLevel());
+ bindCheckError(rc);
- serializer.add((u8)group->getName().size());
- serializer.add((u8*)group->getName().data(), group->getName().size());
+ rc = sqlite3_bind_int64(insertGroupStmt, 4, group->getPermission().getPermissionFlags());
+ bindCheckError(rc);
- 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()));
-
+ sqlite_step_rollback_on_failure(sqliteDb, insertGroupStmt, "insert data into NodeGroup");
Log::debug("Created group %s in node %s", bin2hex((const char*)group->getId().data, GROUP_ID_LENGTH).c_str(), nodeHash.toString().c_str());
-
- return true;
}
- bool DatabaseStorage::addUser(const Hash &nodeHash, User *user)
+ void DatabaseStorage::addUserToGroup(const Hash &nodeHash, const Signature::PublicKey &userPublicKey, const DataView &groupId)
{
- Signature::MapPublicKey<User*> *publicKeyUserDataMap = nullptr;
- auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash);
- if(publicKeyUserDataMapIt == nodePublicKeyUserDataMap.end())
- {
- publicKeyUserDataMap = new Signature::MapPublicKey<User*>();
- nodePublicKeyUserDataMap[nodeHash] = publicKeyUserDataMap;
- }
- else
- publicKeyUserDataMap = publicKeyUserDataMapIt->second;
+ sqlite3_reset(insertNodeUserGroupAssocStmt);
+ sqlite3_clear_bindings(insertNodeUserGroupAssocStmt);
- if(publicKeyUserDataMap->find(user->getPublicKey()) != publicKeyUserDataMap->end())
- return false;
+ int rc;
+ rc = sqlite3_bind_blob(insertNodeUserGroupAssocStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
- (*publicKeyUserDataMap)[user->getPublicKey()] = user;
+ rc = sqlite3_bind_blob(insertNodeUserGroupAssocStmt, 2, userPublicKey.getData(), userPublicKey.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
- // 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)
+ rc = sqlite3_bind_blob(insertNodeUserGroupAssocStmt, 3, groupId.data, groupId.size, SQLITE_STATIC);
+ bindCheckError(rc);
- sibs::SafeSerializer serializer;
-
- serializer.add((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeUserGroupAssocStmt, "insert data into NodeUserGroupAssoc");
+ }
+
+ void DatabaseStorage::addUser(const Hash &nodeHash, const Signature::PublicKey &userPublicKey, const DataView &groupId)
+ {
+ sqlite3_reset(insertUserStmt);
+ sqlite3_clear_bindings(insertUserStmt);
- serializer.add((u8)user->getName().size());
- serializer.add((u8*)user->getName().data(), user->getName().size());
+ int rc;
+ rc = sqlite3_bind_blob(insertUserStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
- serializer.add((u8*)user->getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES);
+ rc = sqlite3_bind_blob(insertUserStmt, 2, userPublicKey.getData(), userPublicKey.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
- serializer.add((u8)user->getGroups().size());
- for(Group *group : user->getGroups())
- {
- serializer.add((u8*)group->getId().data, GROUP_ID_LENGTH);
- }
+ sqlite_step_rollback_on_failure(sqliteDb, insertUserStmt, "insert data into NodeUser");
- fileAppend(usersFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
-
- Log::debug("Created user %s in node %s", user->getPublicKey().toString().c_str(), nodeHash.toString().c_str());
+ addUserToGroup(nodeHash, userPublicKey, groupId);
- return true;
+ Log::debug("Created user %s in node %s", userPublicKey.toString().c_str(), nodeHash.toString().c_str());
}
- /*
- u64 DatabaseStorage::increaseUserActionCounter(const Hash &nodeHash, const Signature::PublicKey &userPublicKey)
+ void DatabaseStorage::fetchNodeRaw(const Hash &nodeHash, FetchNodeRawCallbackFunc callbackFunc)
{
- auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash);
- if(publicKeyUserDataMapIt != nodePublicKeyUserDataMap.end())
+ SqlQuery query(sqliteDb, "SELECT data FROM NodeRaw WHERE node = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ while(query.next())
{
- auto it = publicKeyUserDataMapIt->second->find(userPublicKey);
- if(it != publicKeyUserDataMapIt->second->end())
- {
- return ++it->second->actionCounter;
- }
-
- string errMsg = "User with id ";
- errMsg += userPublicKey.toString();
- errMsg += " doesn't exist in node ";
- errMsg += nodeHash.toString();
- throw DatabaseStorageException(errMsg);
- }
-
- string errMsg = "Node with id ";
- errMsg += nodeHash.toString();
- errMsg += " doesn't exist";
- throw DatabaseStorageException(errMsg);
- }
- */
-
- const DatabaseStorageObjectList* DatabaseStorage::getStorage(const Hash &hash) const
- {
- auto it = storageMap.find(hash);
- if(it != storageMap.end())
- return it->second;
- return nullptr;
- }
-
- const DataViewMap<Group*>* DatabaseStorage::getNodeGroups(const Hash &nodeHash)
- {
- auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash);
- if(groupByIdMapIt != nodeGroupByIdMap.end())
- return groupByIdMapIt->second;
- return nullptr;
- }
-
- Group* DatabaseStorage::getGroupById(const Hash &nodeHash, uint8_t groupId[GROUP_ID_LENGTH]) const
- {
- 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;
+ const DataView data = query.getBlob(0);
+ callbackFunc(data);
}
- return nullptr;
}
- const Signature::MapPublicKey<User*>* DatabaseStorage::getNodeUsers(const Hash &nodeHash)
+ void DatabaseStorage::fetchNodeAddDataRaw(const Hash &nodeHash, FetchNodeAddDataRawCallbackFunc callbackFunc)
{
- auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash);
- if(publicKeyUserDataMapIt != nodePublicKeyUserDataMap.end())
- return publicKeyUserDataMapIt->second;
- return nullptr;
- }
-
- User* DatabaseStorage::getUserByPublicKey(const Hash &nodeHash, const Signature::PublicKey &userPublicKey) const
- {
- auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash);
- if(publicKeyUserDataMapIt != nodePublicKeyUserDataMap.end())
+ SqlQuery query(sqliteDb, "SELECT data FROM NodeAddDataRaw WHERE node = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ while(query.next())
{
- auto it = publicKeyUserDataMapIt->second->find(userPublicKey);
- if(it != publicKeyUserDataMapIt->second->end())
- return it->second;
+ const DataView data = query.getBlob(0);
+ callbackFunc(data);
}
- return nullptr;
}
-
+#if 0
bool DatabaseStorage::storeLocalUser(const string &username, const Signature::KeyPair &keyPair, const string &password)
{
auto it = nameLocalUsersMap.find(username);
@@ -774,7 +686,7 @@ namespace odhtdb
assert(sizeof(userEncryptedPrivateKey.encryptedPrivateKey) == encryptedPrivateKey.getCipherText().size);
memcpy(userEncryptedPrivateKey.encryptedPrivateKey, encryptedPrivateKey.getCipherText().data, encryptedPrivateKey.getCipherText().size);
- LocalUserEncrypted *localUserEncrypted = LocalUserEncrypted::create(keyPair.getPublicKey(), userEncryptedPrivateKey, username);
+ LocalUserEncrypted *localUserEncrypted = LocalUserEncrypted::create(keyPair.getPublicKey(), userEncryptedPrivateKey);
nameLocalUsersMap[username] = localUserEncrypted;
sibs::SafeSerializer serializer;
@@ -817,271 +729,288 @@ namespace odhtdb
throw DatabaseStorageWrongPassword(errMsg);
}
}
-
- vector<NodeLocalUser> DatabaseStorage::getLocalNodeUsers(const Signature::KeyPair &keyPair)
- {
- vector<NodeLocalUser> localUsers;
-
- for(auto nodeIt : nodePublicKeyUserDataMap)
- {
- auto userIt = nodeIt.second->find(keyPair.getPublicKey());
- if(userIt != nodeIt.second->end())
- {
- User *user = userIt->second;
- if(user->getType() != User::Type::LOCAL)
- {
- LocalUser *localUser = LocalUser::create(keyPair, user->getName(), nullptr);
- for(Group *group : user->getGroups())
- {
- localUser->addToGroup(group);
- }
-
- (*nodeIt.second)[keyPair.getPublicKey()] = localUser;
- localUsers.push_back({ nodeIt.first, localUser });
- delete user;
- }
- else
- localUsers.push_back({ nodeIt.first, static_cast<LocalUser*>(user) });
- }
- }
-
- return localUsers;
- }
-
- DatabaseStorageObject* DatabaseStorage::getDataById(const Hash &dataHash)
- {
- auto storageIt = storedDataHash.find(dataHash);
- if(storageIt != storedDataHash.end())
- return storageIt->second;
- return nullptr;
- }
-
- std::pair<bool, std::shared_ptr<OwnedMemory>> DatabaseStorage::getNodeDecryptionKey(const Hash &nodeHash)
+#endif
+ pair<bool, shared_ptr<OwnedMemory>> DatabaseStorage::getNodeDecryptionKey(const Hash &nodeHash)
{
- auto nodeDecryptionKeyIt = nodeDecryptionKeyMap.find(nodeHash);
- if(nodeDecryptionKeyIt != nodeDecryptionKeyMap.end())
- return make_pair(true, nodeDecryptionKeyIt->second);
- return make_pair(false, make_shared<OwnedMemory>());
+ sqlite3_reset(getNodeDecryptionKeyStmt);
+ sqlite3_clear_bindings(getNodeDecryptionKeyStmt);
+
+ int rc;
+ rc = sqlite3_bind_blob(getNodeDecryptionKeyStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(getNodeDecryptionKeyStmt);
+ if(rc != SQLITE_ROW)
+ return make_pair(false, make_shared<OwnedMemory>());
+
+ const void *decryptionKeyRaw = sqlite3_column_blob(getNodeDecryptionKeyStmt, 0);
+ u8 *decryptionKeyRawCopy = new u8[ENCRYPTION_KEY_BYTE_SIZE];
+ memcpy(decryptionKeyRawCopy, decryptionKeyRaw, ENCRYPTION_KEY_BYTE_SIZE);
+ shared_ptr<OwnedMemory> decryptionKey = make_shared<OwnedMemory>(decryptionKeyRawCopy, ENCRYPTION_KEY_BYTE_SIZE);
+ return make_pair(true, decryptionKey);
}
void DatabaseStorage::setNodeDecryptionKey(const Hash &nodeHash, const DataView &decryptionKeyView)
{
- bool nodeHasExistingEncryptionKey = nodeDecryptionKeyMap.find(nodeHash) != nodeDecryptionKeyMap.end();
+ auto nodeDecryptionKeyResult = getNodeDecryptionKey(nodeHash);
+ bool nodeHasExistingEncryptionKey = nodeDecryptionKeyResult.first;
- char *decryptionKeyRaw = new char[decryptionKeyView.size];
- memcpy(decryptionKeyRaw, decryptionKeyView.data, decryptionKeyView.size);
- shared_ptr<OwnedMemory> decryptionKey = make_shared<OwnedMemory>(decryptionKeyRaw, decryptionKeyView.size);
- nodeDecryptionKeyMap[nodeHash] = decryptionKey;
+ sqlite3_reset(setNodeDecryptionKeyStmt);
+ sqlite3_clear_bindings(setNodeDecryptionKeyStmt);
- sibs::SafeSerializer serializer;
- serializer.add((const u8*)nodeHash.getData(), HASH_BYTE_SIZE);
- serializer.add((const u8*)decryptionKeyView.data, ENCRYPTION_KEY_BYTE_SIZE);
- fileAppend(nodeDecryptionKeysFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
+ int rc;
+ rc = sqlite3_bind_blob(setNodeDecryptionKeyStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(setNodeDecryptionKeyStmt, 2, decryptionKeyView.data, decryptionKeyView.size, SQLITE_STATIC);
+ bindCheckError(rc);
- auto storageIt = storageMap.find(nodeHash);
- if(storageIt == storageMap.end()) return;
+ sqlite_step_rollback_on_failure(sqliteDb, setNodeDecryptionKeyStmt, "insert or replace node decryption key");
// When changing existing encryption key, do not decrypt the existing data as it has already been decrypted,
// the new key should only be used for new data
if(!nodeHasExistingEncryptionKey)
- decryptNodeData(nodeHash, storageIt->second, decryptionKey);
+ decryptNodeData(nodeHash, nodeDecryptionKeyResult.second);
}
- bool DatabaseStorage::decryptNodeData(const Hash &nodeHash, DatabaseStorageObjectList *databaseCreateObject, const shared_ptr<OwnedMemory> decryptionKey)
+ bool DatabaseStorage::decryptNodeData(const Hash &nodeHash, const shared_ptr<OwnedMemory> decryptionKey)
{
- sibs::SafeDeserializer deserializer((u8*)databaseCreateObject->data.data + databaseCreateObject->offsetToEncryptedData, databaseCreateObject->data.size - databaseCreateObject->offsetToEncryptedData);
-
- 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, ENCRYPTION_NONCE_BYTE_SIZE), DataView(decryptionKey->data, ENCRYPTION_KEY_BYTE_SIZE));
- sibs::SafeDeserializer bodyDeserializer((const u8*)decryptedBody.getDecryptedText().data, decryptedBody.getDecryptedText().size);
-
- u8 creatorNameLength = bodyDeserializer.extract<u8>();
- string creatorName; // TODO: Add this user name to storage creator name
- creatorName.resize(creatorNameLength);
- bodyDeserializer.extract((u8*)&creatorName[0], creatorNameLength);
+ sqlite3_reset(selectNodeStmt);
+ sqlite3_clear_bindings(selectNodeStmt);
- u8 nameLength = bodyDeserializer.extract<u8>();
- string name;
- name.resize(nameLength);
- bodyDeserializer.extract((u8*)&name[0], nameLength);
-
- sibs::SafeSerializer serializer;
- serializer.add(DecryptedDataType::STORAGE_CREATE);
- serializer.add((const u8*)nodeHash.getData(), HASH_BYTE_SIZE);
- serializer.add(creatorNameLength);
- serializer.add((const u8*)creatorName.data(), creatorNameLength);
- serializer.add(nameLength);
- serializer.add((const u8*)name.data(), nameLength);
- fileAppend(decryptedDataFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
-
- User *creator = getUserByPublicKey(nodeHash, databaseCreateObject->creatorPublicKey);
- if(!creator)
+ int rc;
+ rc = sqlite3_bind_blob(selectNodeStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeStmt);
+ if(rc == SQLITE_DONE)
+ return true;
+ else if(rc != SQLITE_ROW)
{
- Log::error("Creator with public key %s does not exist in node %s", databaseCreateObject->creatorPublicKey.toString().c_str(), nodeHash.toString().c_str());
- return false;
+ string errMsg = "select node failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ throw DatabaseStorageException(errMsg);
}
- creator->name = move(creatorName);
- databaseCreateObject->nodeName = name;
- databaseCreateObject->isDecrypted = true;
- Log::debug("Deserialized node create data, name: %s", name.c_str());
- const DatabaseCreateNodeRequest createNodeRequest(&nodeHash, databaseCreateObject->createdTimestamp, creator, move(name));
+ u64 timestamp = sqlite3_column_int64(selectNodeStmt, 0);
+ const void *creatorPublicKeyRaw = sqlite3_column_blob(selectNodeStmt, 1);
+ Signature::PublicKey creatorPublicKey((const char*)creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+ const void *adminGroupIdRaw = sqlite3_column_blob(selectNodeStmt, 2);
+ u8 adminGroup[GROUP_ID_LENGTH];
+ memcpy(adminGroup, adminGroupIdRaw, GROUP_ID_LENGTH);
+
+ return decryptNodeData(nodeHash, decryptionKey, &creatorPublicKey, DataView(adminGroup, GROUP_ID_LENGTH), timestamp);
+ }
+
+ bool DatabaseStorage::decryptNodeData(const Hash &nodeHash, const shared_ptr<OwnedMemory> decryptionKey, const Signature::PublicKey *creatorPublicKey, const DataView &adminGroupId, u64 timestamp)
+ {
+ const DatabaseCreateNodeRequest createNodeRequest(&nodeHash, timestamp, creatorPublicKey, adminGroupId);
if(database->onCreateNodeCallbackFunc)
database->onCreateNodeCallbackFunc(createNodeRequest);
+ sqlite3_reset(selectNodeAddDataByNodeStmt);
+ sqlite3_clear_bindings(selectNodeAddDataByNodeStmt);
+
+ int rc;
+ rc = sqlite3_bind_blob(selectNodeAddDataByNodeStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite3_exec(sqliteDb, "BEGIN", 0, 0, 0);
bool success = true;
- for(auto appendObject : databaseCreateObject->objects)
+ while(true)
{
- bool appendObjectResult = decryptNodeAppendedData(nodeHash, appendObject, decryptionKey);
- if(!appendObjectResult)
- success = false;
+ rc = sqlite3_step(selectNodeAddDataByNodeStmt);
+ if(rc == SQLITE_ROW)
+ {
+ i64 rowId = sqlite3_column_int64(selectNodeAddDataByNodeStmt, 0);
+
+ const void *requestHashRaw = sqlite3_column_blob(selectNodeAddDataByNodeStmt, 1);
+ Hash requestHash;
+ memcpy(requestHash.getData(), requestHashRaw, HASH_BYTE_SIZE);
+
+ DatabaseOperation operation = (DatabaseOperation)sqlite3_column_int(selectNodeAddDataByNodeStmt, 2);
+
+ u64 timestamp = sqlite3_column_int64(selectNodeAddDataByNodeStmt, 3);
+
+ const void *creatorPublicKeyRaw = sqlite3_column_blob(selectNodeAddDataByNodeStmt, 4);
+ Signature::PublicKey creatorPublicKey((const char*)creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+
+ if(operation == DatabaseOperation::ADD_DATA)
+ {
+ sqlite3_reset(selectNodeAddDataAdditionalStmt);
+ sqlite3_clear_bindings(selectNodeAddDataAdditionalStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(selectNodeAddDataAdditionalStmt, 1, rowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeAddDataAdditionalStmt);
+ if(rc != SQLITE_ROW)
+ {
+ string errMsg = "select NodeAddDataAdditional failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
+ }
+
+ // TODO: There is no need to allocate/deallocate several times, this can be moved outside the while loop
+ const void *encryptedDataRaw = sqlite3_column_blob(selectNodeAddDataAdditionalStmt, 0);
+ int encryptedDataSize = sqlite3_column_bytes(selectNodeAddDataAdditionalStmt, 0);
+ OwnedMemory encryptedData(new u8[encryptedDataSize], encryptedDataSize);
+ memcpy(encryptedData.data, encryptedDataRaw, encryptedDataSize);
+
+ bool appendObjectResult = decryptNodeAddData(rowId, nodeHash, requestHash, timestamp, &creatorPublicKey, DataView(encryptedData.data, encryptedData.size), decryptionKey);
+ if(!appendObjectResult)
+ success = false;
+ }
+ else if(operation == DatabaseOperation::ADD_USER)
+ {
+ sqlite3_reset(selectNodeAddUserDataStmt);
+ sqlite3_clear_bindings(selectNodeAddUserDataStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(selectNodeAddUserDataStmt, 1, rowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeAddUserDataStmt);
+ if(rc != SQLITE_ROW)
+ {
+ string errMsg = "select NodeAddUserData failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
+ }
+
+ const void *userToAddPublicKeyRaw = sqlite3_column_blob(selectNodeAddUserDataStmt, 0);
+ Signature::PublicKey userToAddPublicKey((const char*)userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+
+ const void *groupToAddUserToRaw = sqlite3_column_blob(selectNodeAddUserDataStmt, 1);
+ u8 groupToAddUserTo[GROUP_ID_LENGTH];
+ memcpy(groupToAddUserTo, groupToAddUserToRaw, GROUP_ID_LENGTH);
+
+ bool appendObjectResult = decryptNodeAddUser(rowId, nodeHash, requestHash, timestamp, &creatorPublicKey, &userToAddPublicKey, DataView(groupToAddUserTo, GROUP_ID_LENGTH), decryptionKey);
+ if(!appendObjectResult)
+ success = false;
+ }
+ }
+ else if(rc == SQLITE_DONE)
+ {
+ break;
+ }
+ else
+ {
+ string errMsg = "select NodeAddData by node failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
+ }
}
+ sqlite3_exec(sqliteDb, "COMMIT", 0, 0, 0);
+
return success;
}
- bool DatabaseStorage::decryptNodeAppendedData(const Hash &nodeHash, DatabaseStorageObject *databaseAppendObject, const shared_ptr<OwnedMemory> decryptionKey)
+ void DatabaseStorage::setNodeAddDataDecrypted(i64 rowId)
{
- sibs::SafeDeserializer deserializer((u8*)databaseAppendObject->data.data, databaseAppendObject->data.size);
- char creatorPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializer.extract((u8*)creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey creatorPublicKey(creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+ sqlite3_reset(setNodeAddDataDecryptedStmt);
+ sqlite3_clear_bindings(setNodeAddDataDecryptedStmt);
- DataView signedData((void*)deserializer.getBuffer(), deserializer.getSize());
- string unsignedData = creatorPublicKey.unsign(signedData);
- sibs::SafeDeserializer deserializerUnsigned((u8*)unsignedData.data(), unsignedData.size());
+ int rc;
+ rc = sqlite3_bind_int(setNodeAddDataDecryptedStmt, 1, 1);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int64(setNodeAddDataDecryptedStmt, 2, rowId);
+ bindCheckError(rc);
- u16 packetStructureVersion = deserializerUnsigned.extract<u16>();
- u64 creationDate = deserializerUnsigned.extract<u64>();
+ sqlite_step_rollback_on_failure(sqliteDb, setNodeAddDataDecryptedStmt, "set NodeAddData decrypted");
+ }
+
+ void DatabaseStorage::setNodeAddDataDecryptedData(i64 rowId, const DataView &decryptedData)
+ {
+ setNodeAddDataDecrypted(rowId);
+ sqlite3_reset(setNodeAddDataAdditionalDataStmt);
+ sqlite3_clear_bindings(setNodeAddDataAdditionalDataStmt);
- DatabaseOperation operation = deserializerUnsigned.extract<DatabaseOperation>();
+ int rc;
+ rc = sqlite3_bind_blob(setNodeAddDataAdditionalDataStmt, 1, decryptedData.data, decryptedData.size, SQLITE_STATIC);
+ bindCheckError(rc);
- auto creatorUser = getUserByPublicKey(nodeHash, creatorPublicKey);
- if(!creatorUser)
+ rc = sqlite3_bind_int64(setNodeAddDataAdditionalDataStmt, 2, rowId);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, setNodeAddDataAdditionalDataStmt, "set NodeAddData decrypted");
+ }
+
+ bool DatabaseStorage::decryptNodeAddData(i64 rowId, const Hash &nodeHash, const Hash &dataHash, u64 timestamp, const Signature::PublicKey *creatorPublicKey, const DataView &encryptedData, const shared_ptr<OwnedMemory> decryptionKey)
+ {
+ SqlQuery queryCreatorGroupWithRightsToAddData(sqliteDb,
+ "SELECT nodeGroup.rowid FROM NodeUserGroupAssoc AS userGroupAssoc"
+ " INNER JOIN NodeGroup AS nodeGroup ON nodeGroup.groupId = userGroupAssoc.groupId"
+ " WHERE userGroupAssoc.node = ? AND userGroupAssoc.userPublicKey = ? AND (nodeGroup.permissionFlags & ?) != 0",
+ { DataView(nodeHash.getData(), nodeHash.getSize()), DataView((void*)creatorPublicKey->getData(), creatorPublicKey->getSize()), (i64)PermissionType::ADD_DATA });
+ if(!queryCreatorGroupWithRightsToAddData.next())
{
- Log::error("User with public key %s does not exist in node %s", creatorPublicKey.toString().c_str(), nodeHash.toString().c_str());
+ // TODO: User might have permission to perform operation, but we haven't got the packet that adds user to the group with the permission,
+ // or we haven't received the packet that modifies group with the permission to perform the operation.
+ // This also means that an user can be in a group that has permission to perform the operation and then later be removed from it,
+ // and remote peers would accept our request to perform operation if they haven't received the operation that removes the user from the group.
+ // How to handle this?
+ Log::error("User %s is not allowed to add data to node %s", creatorPublicKey->toString().c_str(), nodeHash.toString().c_str());
return false;
}
-
- if(operation == DatabaseOperation::ADD_DATA)
+
+ sibs::SafeDeserializer deserializer((const u8*)encryptedData.data, encryptedData.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, ENCRYPTION_NONCE_BYTE_SIZE), DataView(decryptionKey->data, ENCRYPTION_KEY_BYTE_SIZE));
+ setNodeAddDataDecryptedData(rowId, decryptedBody.getDecryptedText());
+
+ Log::debug("Got add object, timestamp: %zu, data: %.*s", timestamp, decryptedBody.getDecryptedText().size, decryptedBody.getDecryptedText().data);
+ const DatabaseAddNodeRequest addNodeRequest(&nodeHash, &dataHash, timestamp, creatorPublicKey, decryptedBody.getDecryptedText());
+ if(database->onAddNodeCallbackFunc)
+ database->onAddNodeCallbackFunc(addNodeRequest);
+ return true;
+ }
+
+ bool DatabaseStorage::decryptNodeAddUser(i64 rowId, const Hash &nodeHash, const Hash &dataHash, u64 timestamp, const Signature::PublicKey *creatorPublicKey, const Signature::PublicKey *userToAddPublicKey, const DataView &groupToAddUserTo, const shared_ptr<OwnedMemory> decryptionKey)
+ {
+ SqlQuery queryGroupToAdd(sqliteDb, "SELECT permissionLevel FROM NodeGroup WHERE groupId = ?", { groupToAddUserTo });
+ if(!queryGroupToAdd.next())
{
- if(deserializerUnsigned.getSize() < ENCRYPTION_NONCE_BYTE_SIZE)
- {
- Log::error("Unsigned encrypted body is too small (unable to extract nonce)");
- return false;
- }
-#if 0
- if(!creatorUser->isAllowedToPerformAction(PermissionType::ADD_DATA))
- {
- // TODO: User might have permission to perform operation, but we haven't got the packet that adds user to the group with the permission,
- // or we haven't received the packet that modifies group with the permission to perform the operation.
- // This also means that an user can be in a group that has permission to perform the operation and then later be removed from it,
- // and remote peers would accept our request to perform operation if they haven't received the operation that removes the user from the group.
- // How to handle this?
- Log::error("User %s is not allowed to add data to node %s", creatorUser->getName().c_str(), nodeHash.toString().c_str());
- return false;
- }
-#endif
-
- 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, ENCRYPTION_NONCE_BYTE_SIZE), DataView(decryptionKey->data, ENCRYPTION_KEY_BYTE_SIZE));
-
- sibs::SafeSerializer serializer;
- serializer.add(DecryptedDataType::STORAGE_ADD_DATA);
- serializer.add((const u8*)databaseAppendObject->requestHash.getData(), HASH_BYTE_SIZE);
- serializer.add((u32)decryptedBody.getDecryptedText().size);
- serializer.add((const u8*)decryptedBody.getDecryptedText().data, decryptedBody.getDecryptedText().size);
- fileAppend(decryptedDataFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
- databaseAppendObject->decryptedObject.data.data = new char[decryptedBody.getDecryptedText().size];
- memcpy(databaseAppendObject->decryptedObject.data.data, decryptedBody.getDecryptedText().data, decryptedBody.getDecryptedText().size);
- databaseAppendObject->decryptedObject.data.size = decryptedBody.getDecryptedText().size;
- databaseAppendObject->decryptedObject.operation = operation;
-
- Log::debug("Got add object, timestamp: %zu, data: %.*s", creationDate, decryptedBody.getDecryptedText().size, decryptedBody.getDecryptedText().data);
- const DatabaseAddNodeRequest addNodeRequest(&nodeHash, &databaseAppendObject->requestHash, creationDate, creatorUser, decryptedBody.getDecryptedText());
- if(database->onAddNodeCallbackFunc)
- database->onAddNodeCallbackFunc(addNodeRequest);
- }
- else if(operation == DatabaseOperation::ADD_USER)
- {
- u8 nameLength = deserializerUnsigned.extract<u8>();
-
- u8 nonce[ENCRYPTION_NONCE_BYTE_SIZE];
- deserializerUnsigned.extract(nonce, ENCRYPTION_NONCE_BYTE_SIZE);
- DataView dataToDecrypt((void*)deserializerUnsigned.getBuffer(), ENCRYPTION_CHECKSUM_BYTE_SIZE + nameLength);
- Decryption decryptedUserName(dataToDecrypt, DataView(nonce, ENCRYPTION_NONCE_BYTE_SIZE), DataView(decryptionKey->data, ENCRYPTION_KEY_BYTE_SIZE));
-
- string username;
- username.resize(nameLength);
- assert(decryptedUserName.getDecryptedText().size == nameLength);
- memcpy(&username[0], decryptedUserName.getDecryptedText().data, nameLength);
-
- sibs::SafeDeserializer deserializerSkippedEncryptedData(deserializerUnsigned.getBuffer() + ENCRYPTION_CHECKSUM_BYTE_SIZE + nameLength, PUBLIC_KEY_NUM_BYTES + GROUP_ID_LENGTH);
-
- char userToAddPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializerSkippedEncryptedData.extract((u8*)userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey userToAddPublicKey(userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
-
- uint8_t groupId[GROUP_ID_LENGTH];
- deserializerSkippedEncryptedData.extract(groupId, GROUP_ID_LENGTH);
-
- auto group = getGroupById(nodeHash, groupId);
- if(!group)
- {
- // TODO: Add to quarantine?
- Log::error("There is no group with id %s in node %s", bin2hex((const char*)groupId, GROUP_ID_LENGTH).c_str(), nodeHash.toString().c_str());
- return false;
- }
-
- auto user = getUserByPublicKey(nodeHash, userToAddPublicKey);
- if(!user)
- {
- Log::error("User to add with public key %s doesn't exist in node %s", userToAddPublicKey.toString().c_str(), nodeHash.toString().c_str());
- return false;
- }
-#if 0
- auto creatorUserGroupWithRights = getGroupWithRightsToAddUserToGroup(creatorUser->getGroups(), group);
- if(!creatorUserGroupWithRights)
- {
- // TODO: User might have permission to perform operation, but we haven't got the packet that adds user to the group with the permission,
- // or we haven't received the packet that modifies group with the permission to perform the operation.
- // This also means that an user can be in a group that has permission to perform the operation and then later be removed from it,
- // and remote peers would accept our request to perform operation if they haven't received the operation that removes the user from the group.
- // How to handle this?
- string errMsg = "User ";
- errMsg += creatorUser->getName();
- errMsg += " is not allowed to perform the operation: ";
- errMsg += to_string((u8)operation);
- throw PermissionDeniedException(errMsg);
- }
-#endif
- sibs::SafeSerializer serializer;
- serializer.add(DecryptedDataType::STORAGE_ADD_USER);
- serializer.add((const u8*)nodeHash.getData(), HASH_BYTE_SIZE);
- serializer.add((const u8*)databaseAppendObject->requestHash.getData(), HASH_BYTE_SIZE);
- serializer.add((const u8*)userToAddPublicKey.getData(), PUBLIC_KEY_NUM_BYTES);
- serializer.add((u32)decryptedUserName.getDecryptedText().size);
- serializer.add((const u8*)decryptedUserName.getDecryptedText().data, decryptedUserName.getDecryptedText().size);
- fileAppend(decryptedDataFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
- databaseAppendObject->decryptedObject.data.data = new char[decryptedUserName.getDecryptedText().size];
- memcpy(databaseAppendObject->decryptedObject.data.data, decryptedUserName.getDecryptedText().data, decryptedUserName.getDecryptedText().size);
- databaseAppendObject->decryptedObject.data.size = decryptedUserName.getDecryptedText().size;
- databaseAppendObject->decryptedObject.operation = operation;
-
- Log::debug("Got add user object, timestamp: %zu, user added: %.*s", creationDate, nameLength, username.c_str());
- DatabaseAddUserRequest addUserRequest(&nodeHash, &databaseAppendObject->requestHash, creationDate, creatorUser, user, group);
- if(database->onAddUserCallbackFunc)
- database->onAddUserCallbackFunc(addUserRequest);
+ // TODO: Add to quarantine?
+ Log::error("There is no group with id %s in node %s", bin2hex((const char*)groupToAddUserTo.data, groupToAddUserTo.size).c_str(), nodeHash.toString().c_str());
+ return false;
}
- else
+
+ int groupToAddPermissionLevel = queryGroupToAdd.getInt(0);
+
+ SqlQuery queryCreatorGroupWithRightsToAddUserToGroup(sqliteDb,
+ "SELECT nodeGroup.rowid FROM NodeUserGroupAssoc AS userGroupAssoc"
+ " INNER JOIN NodeGroup AS nodeGroup ON nodeGroup.groupId = userGroupAssoc.groupId"
+ " WHERE userGroupAssoc.node = ? AND userGroupAssoc.userPublicKey = ? AND (nodeGroup.permissionLevel = ? AND ((nodeGroup.permissionFlags & ?) != 0) OR (nodeGroup.permissionLevel > ? AND (nodeGroup.permissionFlags & ?) != 0))",
+ { DataView(nodeHash.getData(), nodeHash.getSize()), DataView((void*)creatorPublicKey->getData(), creatorPublicKey->getSize()), groupToAddPermissionLevel, (i64)PermissionType::ADD_USER_SAME_LEVEL, groupToAddPermissionLevel, (i64)PermissionType::ADD_USER_HIGHER_LEVEL });
+ if(!queryCreatorGroupWithRightsToAddUserToGroup.next())
{
- Log::error("Got unexpected operation %d", operation);
- return false;
+ // TODO: User might have permission to perform operation, but we haven't got the packet that adds user to the group with the permission,
+ // or we haven't received the packet that modifies group with the permission to perform the operation.
+ // This also means that an user can be in a group that has permission to perform the operation and then later be removed from it,
+ // and remote peers would accept our request to perform operation if they haven't received the operation that removes the user from the group.
+ // How to handle this?
+ string errMsg = "User ";
+ errMsg += creatorPublicKey->toString();
+ errMsg += " is not allowed to perform the operation: ADD USER";
+ throw PermissionDeniedException(errMsg);
}
+
+ addUser(nodeHash, *userToAddPublicKey, groupToAddUserTo);
+ setNodeAddDataDecrypted(rowId);
+
+ Log::debug("Got add user object, timestamp: %zu, user added: %s", timestamp, userToAddPublicKey->toString().c_str());
+ DatabaseAddUserRequest addUserRequest(&nodeHash, &dataHash, timestamp, creatorPublicKey, userToAddPublicKey, groupToAddUserTo);
+ if(database->onAddUserCallbackFunc)
+ database->onAddUserCallbackFunc(addUserRequest);
return true;
}
@@ -1092,23 +1021,7 @@ namespace odhtdb
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<chrono::microseconds>(time).count();
- for(auto mapIt = quarantineStorageMap.begin(); mapIt != quarantineStorageMap.end(); )
- {
- for(auto vecIt = mapIt->second.begin(); vecIt != mapIt->second.end(); )
- {
- if(timeMicroseconds - (*vecIt)->storedTimestamp > QUARANTINE_STORAGE_TIME_MICROSECONDS)
- vecIt = mapIt->second.erase(vecIt);
- else
- ++vecIt;
- }
-
- if(mapIt->second.empty())
- mapIt = quarantineStorageMap.erase(mapIt);
- else
- ++mapIt;
- }
}
}
diff --git a/src/Encryption.cpp b/src/Encryption.cpp
index 9000519..ff37270 100644
--- a/src/Encryption.cpp
+++ b/src/Encryption.cpp
@@ -19,7 +19,7 @@ namespace odhtdb
memcpy(key, _key.data, _key.size);
}
else
- crypto_aead_xchacha20poly1305_ietf_keygen(key);
+ generateKey(key);
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)
@@ -46,6 +46,11 @@ namespace odhtdb
return DataView((void*)cipherText, cipherTextLength);
}
+ void Encryption::generateKey(unsigned char *output)
+ {
+ crypto_aead_xchacha20poly1305_ietf_keygen(output);
+ }
+
Decryption::Decryption(const DataView &data, const DataView &nonce, const DataView &key)
{
decryptedTextLength = data.size;
diff --git a/src/Group.cpp b/src/Group.cpp
index 4210142..9db3e4e 100644
--- a/src/Group.cpp
+++ b/src/Group.cpp
@@ -6,12 +6,9 @@ using namespace std;
namespace odhtdb
{
- Group::Group(const string &_name, uint8_t _id[GROUP_ID_LENGTH], const Permission &_permission) :
- name(_name),
+ Group::Group(uint8_t _id[GROUP_ID_LENGTH], const Permission &_permission) :
permission(_permission)
{
- if(name.size() > 255)
- throw GroupNameTooLongException(name);
memcpy(id, _id, GROUP_ID_LENGTH);
}
@@ -37,11 +34,6 @@ namespace odhtdb
}
return false;
}
-
- const string& Group::getName() const
- {
- return name;
- }
DataView Group::getId() const
{
diff --git a/src/LocalUser.cpp b/src/LocalUser.cpp
index 665f05d..950c953 100644
--- a/src/LocalUser.cpp
+++ b/src/LocalUser.cpp
@@ -2,8 +2,8 @@
namespace odhtdb
{
- LocalUser::LocalUser(const Signature::KeyPair &_keyPair, const std::string &name, Group *group) :
- User(User::Type::LOCAL, name, group),
+ LocalUser::LocalUser(const Signature::KeyPair &_keyPair, Group *group) :
+ User(User::Type::LOCAL, group),
keyPair(_keyPair)
{
diff --git a/src/Signature.cpp b/src/Signature.cpp
index c64af01..ba3ebde 100644
--- a/src/Signature.cpp
+++ b/src/Signature.cpp
@@ -12,6 +12,11 @@ namespace odhtdb
{
const PublicKey PublicKey::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", PUBLIC_KEY_NUM_BYTES);
+ PublicKey::PublicKey()
+ {
+ memset(data, 0, PUBLIC_KEY_NUM_BYTES);
+ }
+
PublicKey::PublicKey(const char *_data, size_t size)
{
if(size != PUBLIC_KEY_NUM_BYTES)
@@ -71,6 +76,11 @@ namespace odhtdb
const 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()
+ {
+ memset(data, 0, PRIVATE_KEY_NUM_BYTES);
+ }
+
PrivateKey::PrivateKey(const char *_data, size_t size)
{
if(size != PRIVATE_KEY_NUM_BYTES)
diff --git a/src/User.cpp b/src/User.cpp
index 58e350a..559f8db 100644
--- a/src/User.cpp
+++ b/src/User.cpp
@@ -3,10 +3,8 @@
namespace odhtdb
{
- User::User(Type _type, const std::string &_name, Group *group) : type(_type), name(_name)
+ User::User(Type _type, Group *group) : type(_type)
{
- if(name.size() > 255)
- throw UserNameTooLongException(name);
addToGroup(group);
}
diff --git a/src/sql/SqlQuery.cpp b/src/sql/SqlQuery.cpp
new file mode 100644
index 0000000..47f1463
--- /dev/null
+++ b/src/sql/SqlQuery.cpp
@@ -0,0 +1,129 @@
+#include "../../include/odhtdb/sql/SqlQuery.hpp"
+#include <sqlite3.h>
+
+namespace odhtdb
+{
+ int SqlArg::bind(sqlite3_stmt *stmt, int paramIndex) const
+ {
+ switch(type)
+ {
+ case Type::DATA_VIEW:
+ return sqlite3_bind_blob(stmt, paramIndex, dataView.data, dataView.size, SQLITE_STATIC);
+ case Type::INT:
+ return sqlite3_bind_int(stmt, paramIndex, integer);
+ case Type::INT64:
+ return sqlite3_bind_int64(stmt, paramIndex, integer64);
+ }
+ return SQLITE_OK;
+ }
+
+ SqlQuery::SqlQuery(sqlite3 *_db, const char *sql, std::initializer_list<SqlArg> args) :
+ db(_db),
+ stmt(nullptr),
+ numColumns(0)
+ {
+ int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
+ if(rc != SQLITE_OK)
+ {
+ std::string errMsg = "Failed to prepare sqlite statement, error: ";
+ errMsg += sqlite3_errmsg(db);
+ sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
+ throw SqlQueryException(errMsg);
+ }
+
+ int numParams = sqlite3_bind_parameter_count(stmt);
+ if(args.size() != numParams)
+ {
+ std::string errMsg = "Failed to prepare sqlite statement, error: Sql has ";
+ errMsg += std::to_string(numParams);
+ errMsg += " parameters, got ";
+ errMsg += std::to_string(args.size());
+ errMsg += " arguments";
+ sqlite3_finalize(stmt);
+ stmt = nullptr;
+ sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
+ throw SqlQueryException(errMsg);
+ }
+
+ int paramIndex = 1;
+ for(const SqlArg &arg : args)
+ {
+ rc = arg.bind(stmt, paramIndex);
+ if(rc != SQLITE_OK)
+ {
+ std::string errMsg = "Failed to bind param, error code: ";
+ errMsg += std::to_string(rc);
+ sqlite3_finalize(stmt);
+ stmt = nullptr;
+ sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
+ throw SqlQueryException(errMsg);
+ }
+ ++paramIndex;
+ }
+ }
+
+ SqlQuery::~SqlQuery()
+ {
+ sqlite3_finalize(stmt);
+ }
+
+ bool SqlQuery::next()
+ {
+ int rc = sqlite3_step(stmt);
+ if(rc == SQLITE_DONE)
+ return false;
+ else if(rc != SQLITE_ROW)
+ {
+ std::string errMsg = "Failed to perform sql select, error: ";
+ errMsg += sqlite3_errmsg(db);
+ sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
+ throw SqlQueryException(errMsg);
+ }
+
+ numColumns = sqlite3_data_count(stmt);
+ return true;
+ }
+
+ void SqlQuery::checkColumnIndex(int index)
+ {
+ if(index < 0 || index >= numColumns)
+ {
+ std::string errMsg;
+ if(numColumns == 0)
+ {
+ errMsg += "Attempt to get column ";
+ errMsg += std::to_string(index);
+ errMsg += " but result does not have any columns";
+ }
+ else
+ {
+ errMsg += "Column index ";
+ errMsg += std::to_string(index);
+ errMsg += " has to be between 0 and ";
+ errMsg += std::to_string(numColumns - 1);
+ }
+ sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
+ throw SqlQueryException(errMsg);
+ }
+ }
+
+ int SqlQuery::getInt(int index)
+ {
+ checkColumnIndex(index);
+ return sqlite3_column_int(stmt, index);
+ }
+
+ i64 SqlQuery::getInt64(int index)
+ {
+ checkColumnIndex(index);
+ return sqlite3_column_int64(stmt, index);
+ }
+
+ const DataView SqlQuery::getBlob(int index)
+ {
+ checkColumnIndex(index);
+ const void *data = sqlite3_column_blob(stmt, index);
+ int size = sqlite3_column_bytes(stmt, index);
+ return { (void*)data, (usize)size };
+ }
+}
diff --git a/tests/assert.hpp b/tests/assert.hpp
index 86f74f2..03157c6 100644
--- a/tests/assert.hpp
+++ b/tests/assert.hpp
@@ -2,19 +2,23 @@
#include <cstdlib>
#include <iostream>
+#include <sstream>
+#include <stdexcept>
template <typename T>
static void assertEquals(const T &expected, const T &actual)
{
if(expected != actual)
{
- std::cerr << "Assertion failed!\nExpected: " << expected << ", actual: " << actual << std::endl;
- exit(1);
+ std::stringstream ss;
+ ss << "Assertion failed!\nExpected: " << expected << ", actual: " << actual << std::endl;
+ throw std::runtime_error(ss.str());
}
}
static void fail(const std::string &errMsg)
{
- fprintf(stderr, "Fail:\n%.*s\n", errMsg.size(), errMsg.c_str());
- exit(1);
+ std::stringstream ss;
+ ss << "Fail:\n" << errMsg << std::endl;
+ throw std::runtime_error(ss.str());
}
diff --git a/tests/main.cpp b/tests/main.cpp
index 8f19838..6e1a72f 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -12,6 +12,7 @@
#include <chrono>
#include <thread>
#include <opendht.h>
+#include <boost/filesystem.hpp>
using namespace std;
using namespace chrono_literals;
@@ -144,59 +145,80 @@ void testTimestamp(const Database &database)
int main()
{
Log::debug("Starting tests...");
+ boost::filesystem::path storagePath("/tmp/odhtdbTest");
+ boost::filesystem::remove_all(storagePath);
+ boost::filesystem::create_directory(storagePath);
+
testCachedIdentity();
testBinHexConvert();
testHash();
testEncryption();
- LocalUser *localUser = LocalUser::create(Signature::KeyPair(), "dec05eba", nullptr);
- testSignData(localUser);
-
- // TODO: Setup local bootstrap node for tests
- Database database("bootstrap.ring.cx", 4222, "storage");
-
- testTimestamp(database);
-
int createNodeCounter = 0;
int addDataCounter = 0;
int addUserCounter = 0;
- database.setOnCreateNodeCallback([&createNodeCounter](const DatabaseCreateNodeRequest &request)
+ auto createNodeCallback = [&createNodeCounter](const DatabaseCreateNodeRequest &request)
{
Log::debug("Create node callback");
++createNodeCounter;
- });
-
- database.setOnAddNodeCallback([&addDataCounter](const DatabaseAddNodeRequest &request)
+ };
+
+ auto addNodeCallback = [&addDataCounter](const DatabaseAddNodeRequest &request)
{
Log::debug("Add node callback");
++addDataCounter;
- });
-
- database.setOnAddUserCallback([&addUserCounter](const DatabaseAddUserRequest &request)
+ };
+
+ auto addUserCallback = [&addUserCounter](const DatabaseAddUserRequest &request)
{
Log::debug("Add user callback");
++addUserCounter;
- });
+ };
- auto databaseCreateResponse = database.create("adminUserName", "latenight");
- DatabaseNode databaseNode(databaseCreateResponse->getNodeEncryptionKey(), databaseCreateResponse->getRequestHash());
- auto adminUser = (LocalUser*)databaseCreateResponse->getNodeAdminUser();
- database.addData(databaseNode, adminUser, DataView{ (void*)"hello, world!", 13 });
- database.addUser(databaseNode, adminUser, localUser->getName(), localUser->getPublicKey(), adminUser->getGroups()[0]);
- localUser->addToGroup(adminUser->getGroups()[0]);
- database.addData(databaseNode, localUser, DataView{ (void*)"hello, aaald!", 13 });
-
- database.seed(databaseNode);
- auto start = chrono::high_resolution_clock::now();
- while(chrono::high_resolution_clock::now() - start < 3s)
+ DatabaseCallbackFuncs callbackFuncs { createNodeCallback, addNodeCallback, addUserCallback };
+ DatabaseNode databaseNode;
+
{
- this_thread::sleep_for(10ms);
+ LocalUser *localUser = LocalUser::create(Signature::KeyPair(), nullptr);
+ testSignData(localUser);
+
+ // TODO: Setup local bootstrap node for tests
+ Database database("bootstrap.ring.cx", 4222, storagePath, callbackFuncs);
+ testTimestamp(database);
+
+ auto databaseCreateResponse = database.create();
+ databaseNode = { databaseCreateResponse->getNodeEncryptionKey(), databaseCreateResponse->getRequestHash() };
+ auto adminUser = (LocalUser*)databaseCreateResponse->getNodeAdminUser();
+ database.addData(databaseNode, adminUser, DataView{ (void*)"hello, world!", 13 });
+ database.addUser(databaseNode, adminUser, localUser->getPublicKey(), adminUser->getGroups()[0]);
+ localUser->addToGroup(adminUser->getGroups()[0]);
+ database.addData(databaseNode, localUser, DataView{ (void*)"hello, aaald!", 13 });
+
+ database.seed(databaseNode);
+ this_thread::sleep_for(chrono::seconds(3));
+
+ assertEquals(1, createNodeCounter);
+ assertEquals(2, addDataCounter);
+ assertEquals(1, addUserCounter);
+ }
+ Log::debug("Callback works when adding data while connected, now testing to reconnect and check if data remains...");
+ {
+ createNodeCounter = 0;
+ addDataCounter = 0;
+ addUserCounter = 0;
+
+ // TODO: Setup local bootstrap node for tests
+ Database database("bootstrap.ring.cx", 4222, storagePath, callbackFuncs);
+ database.loadNode(*databaseNode.getRequestHash());
+
+ database.seed(databaseNode);
+ this_thread::sleep_for(chrono::seconds(3));
+
+ assertEquals(1, createNodeCounter);
+ assertEquals(2, addDataCounter);
+ assertEquals(1, addUserCounter);
}
-
- assertEquals(1, createNodeCounter);
- assertEquals(2, addDataCounter);
- assertEquals(1, addUserCounter);
return 0;
}