aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/odhtdb/DataView.hpp1
-rw-r--r--include/odhtdb/Database.hpp5
-rw-r--r--include/odhtdb/DatabaseStorage.hpp15
-rw-r--r--src/DataView.cpp5
-rw-r--r--src/Database.cpp14
-rw-r--r--src/DatabaseStorage.cpp96
-rw-r--r--tests/main.cpp20
7 files changed, 133 insertions, 23 deletions
diff --git a/include/odhtdb/DataView.hpp b/include/odhtdb/DataView.hpp
index 0ecf9fb..2f22d83 100644
--- a/include/odhtdb/DataView.hpp
+++ b/include/odhtdb/DataView.hpp
@@ -12,6 +12,7 @@ namespace odhtdb
DataView() : data(nullptr), size(0) {}
DataView(void *_data, usize _size) : data(_data), size(_size) {}
bool operator == (const DataView &other) const;
+ bool operator != (const DataView &other) const;
void *data;
usize size;
diff --git a/include/odhtdb/Database.hpp b/include/odhtdb/Database.hpp
index 43c1ad9..ca9369f 100644
--- a/include/odhtdb/Database.hpp
+++ b/include/odhtdb/Database.hpp
@@ -176,6 +176,11 @@ namespace odhtdb
ntp::NtpTimestamp getSyncedTimestampUtc() const;
+ bool doesStoredUserExist(const std::string &username) const;
+
+ // Throws SqlExecException if user with same name already exists
+ void storeUserWithoutNodes(const std::string &username, const std::string &password);
+
// Username has to be either unique or if it's the same as existing one, then password has to match.
// Node has to be unique for the user.
// Throws DatabaseStorageWrongPassword or SqlExecException on failure (if username is not unique in node).
diff --git a/include/odhtdb/DatabaseStorage.hpp b/include/odhtdb/DatabaseStorage.hpp
index 613f2e8..f03bd9e 100644
--- a/include/odhtdb/DatabaseStorage.hpp
+++ b/include/odhtdb/DatabaseStorage.hpp
@@ -58,6 +58,12 @@ namespace odhtdb
DatabaseStorageWrongPassword(const std::string &errMsg) : DatabaseStorageException(errMsg) {}
};
+ class DatabaseStorageNoSuchStoredUser : public DatabaseStorageException
+ {
+ public:
+ DatabaseStorageNoSuchStoredUser(const std::string &errMsg) : DatabaseStorageException(errMsg) {}
+ };
+
const int PASSWORD_SALT_LEN = 16;
const int HASHED_PASSWORD_LEN = 32;
@@ -111,6 +117,11 @@ namespace odhtdb
// Throws DatabaseStorageNotFound if user doesn't exist in node
u64 getUserActionCounter(const Hash &nodeHash, const Signature::PublicKey &userPublicKey) const;
+ bool doesStoredUserExist(const std::string &username) const;
+
+ // Throws SqlExecException if user with same name already exists
+ void storeUserWithoutNodes(const std::string &username, const std::string &password);
+
// Username has to be either unique or if it's the same as existing one, then password has to match.
// Node has to be unique for the user.
// Throws DatabaseStorageWrongPassword or SqlExecException on failure (if username is not unique in node).
@@ -118,6 +129,7 @@ namespace odhtdb
// Returns nodes, public key and private key of encrypted user.
// Throws DatabaseStorageWrongPassword if password for the stored user is wrong.
+ // Throws DatabaseStorageNoSuchStoredUser if user doesn't exist.
std::vector<NodeUserKeyPair> getStoredUserNodeDataDecrypted(const std::string &username, const std::string &password);
// Returns true and node decryption key if node exists and we have the decryption key,
@@ -148,6 +160,9 @@ namespace odhtdb
void setNodeAddDataDecrypted(i64 rowId);
void setNodeAddDataDecryptedData(i64 rowId, const DataView &decryptedData);
+
+ // Throws DatabaseStorageNoSuchStoredUser or DatabaseStorageWrongPassword
+ i64 getStoredUserId(const std::string &username, const DataView &hashedPassword);
private:
Database *database;
sqlite3 *sqliteDb;
diff --git a/src/DataView.cpp b/src/DataView.cpp
index b9cef47..a5232f3 100644
--- a/src/DataView.cpp
+++ b/src/DataView.cpp
@@ -7,4 +7,9 @@ namespace odhtdb
{
return size == other.size && memcmp(data, other.data, size) == 0;
}
+
+ bool DataView::operator != (const DataView &other) const
+ {
+ return !operator==(other);
+ }
}
diff --git a/src/Database.cpp b/src/Database.cpp
index 17773ed..874b225 100644
--- a/src/Database.cpp
+++ b/src/Database.cpp
@@ -655,12 +655,22 @@ namespace odhtdb
return true;
}
- void Database::storeUserPasswordEncrypted(const Hash &nodeHash, const std::string &username, const std::string &password, const Signature::KeyPair &keyPair)
+ bool Database::doesStoredUserExist(const string &username) const
+ {
+ return databaseStorage.doesStoredUserExist(username);
+ }
+
+ void Database::storeUserWithoutNodes(const string &username, const string &password)
+ {
+ return databaseStorage.storeUserWithoutNodes(username, password);
+ }
+
+ void Database::storeUserPasswordEncrypted(const Hash &nodeHash, const string &username, const string &password, const Signature::KeyPair &keyPair)
{
return databaseStorage.storeUserPasswordEncrypted(nodeHash, username, password, keyPair);
}
- vector<NodeUserKeyPair> Database::getStoredUserNodeDataDecrypted(const std::string &username, const std::string &password)
+ vector<NodeUserKeyPair> Database::getStoredUserNodeDataDecrypted(const string &username, const string &password)
{
return databaseStorage.getStoredUserNodeDataDecrypted(username, password);
}
diff --git a/src/DatabaseStorage.cpp b/src/DatabaseStorage.cpp
index 66d6d89..3140c96 100644
--- a/src/DatabaseStorage.cpp
+++ b/src/DatabaseStorage.cpp
@@ -115,11 +115,13 @@ namespace odhtdb
"CREATE TABLE IF NOT EXISTS NodeAddDataRaw(nodeId INTEGER NOT NULL, nodeAddDataId INTEGER NOT NULL, data BLOB NOT NULL, FOREIGN KEY(nodeId) REFERENCES Node(id), FOREIGN KEY(nodeAddDataId) REFERENCES NodeAddData(id));"
"CREATE TABLE IF NOT EXISTS NodeUserActionGap(id INTEGER PRIMARY KEY, nodeUserId INTEGER NOT NULL, start INTEGER NOT NULL, range INTEGER NOT NULL, FOREIGN KEY(nodeUserId) REFERENCES NodeUser(id));"
- "CREATE TABLE IF NOT EXISTS NodeEncryptedUserData(node INTEGER NOT NULL, username BLOB NOT NULL, userPublicKey BLOB NOT NULL, nonce BLOB NOT NULL, userPrivateKeyEncrypted BLOB NOT NULL, FOREIGN KEY(node, userPublicKey) REFERENCES NodeUser(node, publicKey));"
+
+ "CREATE TABLE IF NOT EXISTS NodeEncryptedUser(id INTEGER PRIMARY KEY, username BLOB UNIQUE NOT NULL, nonce BLOB NOT NULL, encryptedPassword BLOB NOT NULL);"
+ "CREATE TABLE IF NOT EXISTS NodeEncryptedUserData(node INTEGER NOT NULL, usernameId INTEGER NOT NULL, userPublicKey BLOB NOT NULL, nonce BLOB NOT NULL, userPrivateKeyEncrypted BLOB NOT NULL, FOREIGN KEY(usernameId) REFERENCES NodeEncryptedUser(id), FOREIGN KEY(node, userPublicKey) REFERENCES NodeUser(node, publicKey));"
"CREATE UNIQUE INDEX IF NOT EXISTS UniqueUserInNode ON NodeUser(node, publicKey);"
"CREATE UNIQUE INDEX IF NOT EXISTS UniqueUserGroupAssoc ON NodeUserGroupAssoc(node, userPublicKey, groupId);"
- "CREATE UNIQUE INDEX IF NOT EXISTS UniqueEncryptedUserDataInNode ON NodeEncryptedUserData(node, username);");
+ "CREATE UNIQUE INDEX IF NOT EXISTS UniqueEncryptedUserDataInNode ON NodeEncryptedUserData(node, usernameId);");
sqlite_prepare_checked(sqliteDb, "INSERT INTO Node(nodeHash, timestamp, creatorPublicKey, adminGroupId) VALUES(?, ?, ?, ?)", &insertNodeStmt);
sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeUser(node, publicKey, latestActionCounter) VALUES(?, ?, 0)", &insertUserStmt);
@@ -836,47 +838,99 @@ namespace odhtdb
return query.getInt64(0);
}
- void DatabaseStorage::storeUserPasswordEncrypted(const Hash &nodeHash, const std::string &username, const std::string &password, const Signature::KeyPair &keyPair)
+ bool DatabaseStorage::doesStoredUserExist(const string &username) const
+ {
+ SqlQuery query(sqliteDb, "SELECT id from NodeEncryptedUser WHERE username = ?", { DataView((void*)username.data(), username.size()) });
+ return query.next();
+ }
+
+ void DatabaseStorage::storeUserWithoutNodes(const string &username, const string &password)
+ {
+ OwnedMemory hashedPassword = hashPassword(DataView((void*)password.data(), password.size()), DataView((void*)passwordSalt, PASSWORD_SALT_LEN));
+ DataView hashedPasswordView(hashedPassword.data, hashedPassword.size);
+ DataView usernameView((void*)username.data(), username.size());
+
+ DataView saltView((void*)passwordSalt, PASSWORD_SALT_LEN);
+ Encryption encryptedPassword(saltView, {}, hashedPasswordView);
+ SqlExec addUserSql(sqliteDb, "INSERT INTO NodeEncryptedUser(username, nonce, encryptedPassword) VALUES(?, ?, ?)");
+ addUserSql.execWithArgs({ usernameView, encryptedPassword.getNonce(), encryptedPassword.getCipherText() });
+ }
+
+ i64 DatabaseStorage::getStoredUserId(const string &username, const DataView &hashedPassword)
+ {
+ DataView usernameView((void*)username.data(), username.size());
+ SqlQuery userQuery(sqliteDb, "SELECT id, nonce, encryptedPassword FROM NodeEncryptedUser WHERE username = ?", { usernameView });
+ if(!userQuery.next())
+ {
+ string errMsg = "User with name ";
+ errMsg += username;
+ errMsg += " doesn't exist in storage";
+ throw DatabaseStorageNoSuchStoredUser(errMsg);
+ }
+
+ i64 encryptedUserRowId = userQuery.getInt64(0);
+ const DataView storedEncryptedPasswordNonce = userQuery.getBlob(1);
+ const DataView storedEncryptedPassword = userQuery.getBlob(2);
+ try
+ {
+ Decryption decryptedStoredPassword(storedEncryptedPassword, storedEncryptedPasswordNonce, hashedPassword);
+ return encryptedUserRowId;
+ }
+ catch(DecryptionException &e)
+ {
+ throw DatabaseStorageWrongPassword(e.what());
+ }
+ }
+
+ void DatabaseStorage::storeUserPasswordEncrypted(const Hash &nodeHash, const string &username, const string &password, const Signature::KeyPair &keyPair)
{
OwnedMemory hashedPassword = hashPassword(DataView((void*)password.data(), password.size()), DataView((void*)passwordSalt, PASSWORD_SALT_LEN));
DataView hashedPasswordView(hashedPassword.data, hashedPassword.size);
DataView privateKeyView((void*)keyPair.getPrivateKey().getData(), PRIVATE_KEY_NUM_BYTES);
Encryption encryptedPrivateKey(privateKeyView, {}, hashedPasswordView);
- SqlQuery query(sqliteDb, "SELECT nonce, userPrivateKeyEncrypted FROM NodeEncryptedUserData WHERE username = ?",
- { DataView((void*)username.data(), username.size()) });
-
- if(query.next())
+ SqlTransaction transaction(sqliteDb);
+ DataView usernameView((void*)username.data(), username.size());
+ SqlQuery userQuery(sqliteDb, "SELECT id, nonce, encryptedPassword FROM NodeEncryptedUser WHERE username = ?", { usernameView });
+ i64 encryptedUserRowId;
+ try
{
- const DataView storedNonce = query.getBlob(0);
- const DataView storedPrivateKeyEncrypted = query.getBlob(1);
- try
- {
- Decryption decryptedStoredPrivateKey(storedPrivateKeyEncrypted, storedNonce, hashedPasswordView);
- }
- catch(DecryptionException &e)
- {
- throw DatabaseStorageWrongPassword(e.what());
- }
+ encryptedUserRowId = getStoredUserId(username, hashedPasswordView);
+ }
+ catch(DatabaseStorageNoSuchStoredUser &e)
+ {
+ DataView saltView((void*)passwordSalt, PASSWORD_SALT_LEN);
+ Encryption encryptedPassword(saltView, {}, hashedPasswordView);
+
+ SqlExec addUserSql(sqliteDb, "INSERT INTO NodeEncryptedUser(username, nonce, encryptedPassword) VALUES(?, ?, ?)");
+ addUserSql.execWithArgs({ usernameView, encryptedPassword.getNonce(), encryptedPassword.getCipherText() });
+
+ SqlQuery userQuery(sqliteDb, "SELECT id FROM NodeEncryptedUser WHERE username = ?", { usernameView });
+ if(!userQuery.next())
+ throw DatabaseStorageException("Unexpected error when retrieving stored user");
+
+ encryptedUserRowId = userQuery.getInt64(0);
}
- SqlExec sqlExec(sqliteDb, "INSERT INTO NodeEncryptedUserData(node, username, userPublicKey, nonce, userPrivateKeyEncrypted) VALUES(?, ?, ?, ?, ?)");
+ SqlExec sqlExec(sqliteDb, "INSERT INTO NodeEncryptedUserData(node, usernameId, userPublicKey, nonce, userPrivateKeyEncrypted) VALUES(?, ?, ?, ?, ?)");
sqlExec.execWithArgs({
DataView(nodeHash.getData(), nodeHash.getSize()),
- DataView((void*)username.data(), username.size()),
+ encryptedUserRowId,
DataView((void*)keyPair.getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES),
encryptedPrivateKey.getNonce(),
encryptedPrivateKey.getCipherText()
});
+ transaction.commit();
}
- vector<NodeUserKeyPair> DatabaseStorage::getStoredUserNodeDataDecrypted(const std::string &username, const std::string &password)
+ vector<NodeUserKeyPair> DatabaseStorage::getStoredUserNodeDataDecrypted(const string &username, const string &password)
{
OwnedMemory hashedPassword = hashPassword(DataView((void*)password.data(), password.size()), DataView((void*)passwordSalt, PASSWORD_SALT_LEN));
DataView hashedPasswordView(hashedPassword.data, hashedPassword.size);
+ i64 encryptedUserRowId = getStoredUserId(username, hashedPasswordView);
vector<NodeUserKeyPair> result;
- SqlQuery query(sqliteDb, "SELECT node, userPublicKey, nonce, userPrivateKeyEncrypted FROM NodeEncryptedUserData WHERE username = ?", { DataView((void*)username.data(), username.size()) });
+ SqlQuery query(sqliteDb, "SELECT node, userPublicKey, nonce, userPrivateKeyEncrypted FROM NodeEncryptedUserData WHERE usernameId = ?", { encryptedUserRowId });
while(query.next())
{
Hash nodeHash;
diff --git a/tests/main.cpp b/tests/main.cpp
index d2e49f9..5374c2d 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -202,6 +202,9 @@ int main()
string username = "dec05eba";
string password = "secretPassword";
+ if(database.doesStoredUserExist(username))
+ fail("Expected stored to not exist until it has been added");
+
database.storeUserPasswordEncrypted(*databaseNode.getRequestHash(), username, password, *adminUserKey);
try
{
@@ -213,6 +216,9 @@ int main()
Log::debug("Failed with sql exception as expected, since we already have an user in the node: %s", e.what());
}
+ if(!database.doesStoredUserExist(username))
+ fail("Expected stored to exist after it has been added");
+
auto nodeUserData = database.getStoredUserNodeDataDecrypted(username, password);
assertEquals((size_t)1, nodeUserData.size());
if(nodeUserData[0].nodeHash != *databaseNode.getRequestHash())
@@ -221,6 +227,20 @@ int main()
fail("Expected stored public key to match admin user public key");
if(nodeUserData[0].keyPair.getPrivateKey() != adminUserKey->getPrivateKey())
fail("Expected stored private key to match admin user private key");
+
+ try
+ {
+ database.storeUserWithoutNodes(username, password);
+ fail("Expected store user to fail since the user already exists in database");
+ }
+ catch(SqlExecException &e)
+ {
+
+ }
+
+ database.storeUserWithoutNodes("anotherUser", password);
+ if(!database.doesStoredUserExist("anotherUser"))
+ fail("Added user 'anotherUser' to database without any nodes, but it doesn't seem to be stored");
}
Log::debug("Callback works when adding data while connected, now testing to reconnect and check if data remains...");
{