From e52be3a6b82025b6795b73d448381953821d18bb Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 15 May 2018 18:24:50 +0200 Subject: Add methods to store/retrieve encrypted user (using argon2 for hash) --- src/Database.cpp | 10 +++++ src/DatabaseStorage.cpp | 113 ++++++++++++++++++++++++++---------------------- src/Signature.cpp | 10 +++++ 3 files changed, 81 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/Database.cpp b/src/Database.cpp index 5eedc7a..17773ed 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -654,4 +654,14 @@ namespace odhtdb } return true; } + + void Database::storeUserPasswordEncrypted(const Hash &nodeHash, const std::string &username, const std::string &password, const Signature::KeyPair &keyPair) + { + return databaseStorage.storeUserPasswordEncrypted(nodeHash, username, password, keyPair); + } + + vector Database::getStoredUserNodeDataDecrypted(const std::string &username, const std::string &password) + { + return databaseStorage.getStoredUserNodeDataDecrypted(username, password); + } } diff --git a/src/DatabaseStorage.cpp b/src/DatabaseStorage.cpp index d5053c5..66d6d89 100644 --- a/src/DatabaseStorage.cpp +++ b/src/DatabaseStorage.cpp @@ -5,8 +5,6 @@ #include "../include/odhtdb/PasswordHash.hpp" #include "../include/odhtdb/Log.hpp" #include "../include/odhtdb/Database.hpp" -#include "../include/odhtdb/sql/SqlQuery.hpp" -#include "../include/odhtdb/sql/SqlExec.hpp" #include #include #include @@ -101,6 +99,8 @@ namespace odhtdb throw DatabaseStorageException(errMsg); } + // TODO: GroupId (and maybe other fields) have to be unique, but by malicious intervention group id can be copied from one node and then used when creating another one. + // This will result in error for users that are part of the other Node, meaning not all users... 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(id INTEGER PRIMARY KEY, node BLOB NOT NULL, publicKey BLOB NOT NULL, latestActionCounter INTEGER NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));" @@ -109,15 +109,17 @@ namespace odhtdb "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 NodeUserGroupAssoc(node BLOB NOT NULL, userPublicKey BLOB NOT NULL, groupId BLOB NOT NULL, FOREIGN KEY(node, userPublicKey) REFERENCES NodeUser(node, 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(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 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 UniqueUserGroupAssoc ON NodeUserGroupAssoc(node, userPublicKey, groupId);" + "CREATE UNIQUE INDEX IF NOT EXISTS UniqueEncryptedUserDataInNode ON NodeEncryptedUserData(node, username);"); 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); @@ -781,7 +783,7 @@ namespace odhtdb void DatabaseStorage::fetchNodeUserLatestActionCounter(const Hash &nodeHash, FetchNodeUserLatestActionCounterCallbackFunc callbackFunc) { SqlQuery query(sqliteDb, "SELECT publicKey, latestActionCounter FROM NodeUser WHERE node = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) }); - while(!query.next()) + while(query.next()) { const DataView userPublicKey = query.getBlob(0); u64 latestActionCounter = query.getInt64(1); @@ -833,67 +835,74 @@ namespace odhtdb } return query.getInt64(0); } -#if 0 - bool DatabaseStorage::storeLocalUser(const string &username, const Signature::KeyPair &keyPair, const string &password) + + void DatabaseStorage::storeUserPasswordEncrypted(const Hash &nodeHash, const std::string &username, const std::string &password, const Signature::KeyPair &keyPair) { - auto it = nameLocalUsersMap.find(username); - if(it != nameLocalUsersMap.end()) - return false; - OwnedMemory hashedPassword = hashPassword(DataView((void*)password.data(), password.size()), DataView((void*)passwordSalt, PASSWORD_SALT_LEN)); - DataView privateKeyView((void*)keyPair.getPrivateKey().getData(), PRIVATE_KEY_NUM_BYTES); DataView hashedPasswordView(hashedPassword.data, hashedPassword.size); + DataView privateKeyView((void*)keyPair.getPrivateKey().getData(), PRIVATE_KEY_NUM_BYTES); Encryption encryptedPrivateKey(privateKeyView, {}, hashedPasswordView); - EncryptedPrivateKey userEncryptedPrivateKey; - memcpy(userEncryptedPrivateKey.nonce, encryptedPrivateKey.getNonce().data, ENCRYPTION_NONCE_BYTE_SIZE); - assert(sizeof(userEncryptedPrivateKey.encryptedPrivateKey) == encryptedPrivateKey.getCipherText().size); - memcpy(userEncryptedPrivateKey.encryptedPrivateKey, encryptedPrivateKey.getCipherText().data, encryptedPrivateKey.getCipherText().size); + SqlQuery query(sqliteDb, "SELECT nonce, userPrivateKeyEncrypted FROM NodeEncryptedUserData WHERE username = ?", + { DataView((void*)username.data(), username.size()) }); - LocalUserEncrypted *localUserEncrypted = LocalUserEncrypted::create(keyPair.getPublicKey(), userEncryptedPrivateKey); - nameLocalUsersMap[username] = localUserEncrypted; - - sibs::SafeSerializer serializer; - serializer.add((u8)username.size()); - serializer.add((const u8*)username.data(), username.size()); - serializer.add((const u8*)keyPair.getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES); - serializer.add((const u8*)encryptedPrivateKey.getNonce().data, ENCRYPTION_NONCE_BYTE_SIZE); - serializer.add((const u8*)encryptedPrivateKey.getCipherText().data, ENCRYPTION_CHECKSUM_BYTE_SIZE + PRIVATE_KEY_NUM_BYTES); + if(query.next()) + { + 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()); + } + } - fileAppend(localUsersFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); - return true; + SqlExec sqlExec(sqliteDb, "INSERT INTO NodeEncryptedUserData(node, username, userPublicKey, nonce, userPrivateKeyEncrypted) VALUES(?, ?, ?, ?, ?)"); + sqlExec.execWithArgs({ + DataView(nodeHash.getData(), nodeHash.getSize()), + DataView((void*)username.data(), username.size()), + DataView((void*)keyPair.getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES), + encryptedPrivateKey.getNonce(), + encryptedPrivateKey.getCipherText() + }); } - Signature::KeyPair DatabaseStorage::decryptLocalEncryptedUser(const string &username, const string &password) + vector DatabaseStorage::getStoredUserNodeDataDecrypted(const std::string &username, const std::string &password) { - auto localUserIt = nameLocalUsersMap.find(username); - if(localUserIt == nameLocalUsersMap.end()) - { - string errMsg = "User "; - errMsg += username; - errMsg += " does not exist in local storage"; - throw DatabaseStorageNoSuchLocalStorageUser(errMsg); - } + OwnedMemory hashedPassword = hashPassword(DataView((void*)password.data(), password.size()), DataView((void*)passwordSalt, PASSWORD_SALT_LEN)); + DataView hashedPasswordView(hashedPassword.data, hashedPassword.size); - DataView passwordView((void*)password.data(), password.size()); - DataView saltView((void*)passwordSalt, PASSWORD_SALT_LEN); - try - { - auto privateKey = localUserIt->second->getPrivateKey().decrypt(passwordView, saltView); - Signature::KeyPair keyPair(localUserIt->second->getPublicKey(), privateKey); - return keyPair; - } - catch(DecryptionException &e) + vector result; + SqlQuery query(sqliteDb, "SELECT node, userPublicKey, nonce, userPrivateKeyEncrypted FROM NodeEncryptedUserData WHERE username = ?", { DataView((void*)username.data(), username.size()) }); + while(query.next()) { - string errMsg = "Wrong password provided for user "; - errMsg += username; - errMsg += " in local storage ("; - errMsg += e.what(); - errMsg += ")"; - throw DatabaseStorageWrongPassword(errMsg); + Hash nodeHash; + const DataView storedNodeHash = query.getBlob(0); + memcpy(nodeHash.getData(), storedNodeHash.data, HASH_BYTE_SIZE); + + const DataView storedUserPublicKey = query.getBlob(1); + Signature::PublicKey userPublicKey((const char*)storedUserPublicKey.data, storedUserPublicKey.size); + const DataView storedNonce = query.getBlob(2); + const DataView storedPrivateKeyEncrypted = query.getBlob(3); + try + { + // TODO: We can bypass decrypting several times by storing several nodes private key user the same field (as a blob) + Decryption decryptedStoredPrivateKey(storedPrivateKeyEncrypted, storedNonce, hashedPasswordView); + Signature::PrivateKey userPrivateKey((const char*)decryptedStoredPrivateKey.getDecryptedText().data, decryptedStoredPrivateKey.getDecryptedText().size); + Signature::KeyPair keyPair(userPublicKey, userPrivateKey); + result.push_back({ nodeHash, keyPair }); + } + catch(DecryptionException &e) + { + throw DatabaseStorageWrongPassword(e.what()); + } } + return result; } -#endif + pair> DatabaseStorage::getNodeDecryptionKey(const Hash &nodeHash) { sqlite3_reset(getNodeDecryptionKeyStmt); diff --git a/src/Signature.cpp b/src/Signature.cpp index ba3ebde..3d75f24 100644 --- a/src/Signature.cpp +++ b/src/Signature.cpp @@ -105,6 +105,16 @@ namespace odhtdb return *this; } + bool PrivateKey::operator==(const PrivateKey &other) const + { + return memcmp(data, other.data, PRIVATE_KEY_NUM_BYTES) == 0; + } + + bool PrivateKey::operator!=(const PrivateKey &other) const + { + return !operator==(other); + } + string PrivateKey::sign(const DataView &dataToSign) const { string result; -- cgit v1.2.3