diff --git a/CMakeLists.txt b/CMakeLists.txt index e77dc7c..d502bb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,7 @@ include_directories( add_executable(${TARGET_NAME} # sqlite3 modules/cpp-libraries/src/sqlite3.c + modules/cpp-libraries/src/monocypher.c src/main.cpp src/utils.hpp diff --git a/modules/cpp-libraries b/modules/cpp-libraries index 6f1d51c..70b723b 160000 --- a/modules/cpp-libraries +++ b/modules/cpp-libraries @@ -1 +1 @@ -Subproject commit 6f1d51cabccb8b2758de2441be0efa907d8b10f9 +Subproject commit 70b723bfc10ae6988725ea55cc97ba508ba00892 diff --git a/src/database/database.hpp b/src/database/database.hpp index dd03683..9cb03ae 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -87,7 +87,8 @@ inline auto make_database() { auto storage = sqlite_orm::make_storage(Database::dbFile, sqlite_orm::make_table("users", sqlite_orm::make_column("id", &login::User::id, sqlite_orm::primary_key()), - sqlite_orm::make_column("username", &login::User::username, sqlite_orm::not_null()), + sqlite_orm::make_column("username", &login::User::username, sqlite_orm::unique() ), + sqlite_orm::make_column("salt", &login::User::salt, sqlite_orm::not_null()), sqlite_orm::make_column("password_hash", &login::User::password_hash, sqlite_orm::not_null()), sqlite_orm::make_column("created_at", &login::User::created_at, sqlite_orm::default_value("CURRENT_TIMESTAMP")) ), diff --git a/src/login/login.cpp b/src/login/login.cpp index 1d67b37..85f9538 100644 --- a/src/login/login.cpp +++ b/src/login/login.cpp @@ -1,38 +1,14 @@ -#include #include "login.hpp" #include "crow/http_response.h" #include "databasepool.h" #include "utils.hpp" #include "SessionHandler.hpp" + namespace login { SessionHandler sessionHandler; -std::string hashPassword(const std::string& password) -{ - // Allocate storage for the hash - char hash[crypto_pwhash_STRBYTES]; - - // Hash the password using Argon2id - if (crypto_pwhash_str( - hash, - password.c_str(), - password.size(), - crypto_pwhash_OPSLIMIT_INTERACTIVE, - crypto_pwhash_MEMLIMIT_INTERACTIVE - ) != 0) { - CROW_LOG_ERROR << "Out of memory while hashing password!"; - return ""; - } - return hash; -} - -bool verifyHashWithPassword(const std::string& hash, std::string const& password) -{ - return crypto_pwhash_str_verify(hash.c_str(), password.c_str(), password.size()) == 0; -} - std::string getSessionId(const crow::request& req) { auto cookie_header = req.get_header_value("Cookie"); std::string prefix = "session_id="; @@ -51,23 +27,15 @@ bool isLoggedIn(const crow::request& req) { std::optional loginUser(const std::string& username, const std::string& password) { - auto user = getUser(username); + auto user = getVerifiedUser(username, password); if (user.has_value()) { - if (verifyHashWithPassword(user.value().password_hash, password)) - { - return sessionHandler.createSession(user.value().id); - } + return sessionHandler.createSession(user.value().id); } return {}; } bool initLogin(crow::SimpleApp& app) { - if (sodium_init() < 0) { - CROW_LOG_ERROR << "Failed to Init Sodium"; - return false; - } - // createUser("lukas", "Trollar4928"); CROW_ROUTE(app, "/login").methods("POST"_method) diff --git a/src/login/loginDb.cpp b/src/login/loginDb.cpp index f910ef8..aea5683 100644 --- a/src/login/loginDb.cpp +++ b/src/login/loginDb.cpp @@ -1,13 +1,60 @@ #include "loginDb.hpp" #include "databasepool.h" +#include #include - +#include +#include +extern "C" { + #include "monocypher.h" +} using namespace sqlite_orm; namespace login { -int createUser(const std::string& username, const std::string& password_hash){ - if (username.empty() || password_hash.empty()) +constexpr uint32_t SALT_SIZE = 16; +constexpr uint32_t PASSWORD_HASH_SIZE = 32; + +static std::vector getPasswordHash(const std::vector& salt, const std::string password){ + std::vector password_hash; + password_hash.resize(PASSWORD_HASH_SIZE); + + crypto_argon2_config config = { + .algorithm = CRYPTO_ARGON2_I, /* Argon2i */ + .nb_blocks = 100000, /* 100 megabytes */ + .nb_passes = 3, /* 3 iterations */ + .nb_lanes = 1 /* Single-threaded */ + }; + crypto_argon2_inputs inputs = { + .pass = (uint8_t*)(password.data()), /* User password */ + .salt = (uint8_t*)salt.data(), /* Salt for the password */ + .pass_size = static_cast(password.size()), /* Password length */ + .salt_size = 16 + }; + crypto_argon2_extras extras = {0}; /* Extra parameters unused */ + + void *work_area = malloc((size_t)config.nb_blocks * 1024); + crypto_argon2((uint8_t*)password_hash.data(), PASSWORD_HASH_SIZE, work_area, + config, inputs, extras); + free(work_area); + return password_hash; +} + +static void createPasswordHash(User& user, const std::string password){ + user.salt.resize(SALT_SIZE); + user.password_hash.resize(PASSWORD_HASH_SIZE); + + arc4random_buf(user.salt.data(), SALT_SIZE); + user.password_hash = getPasswordHash(user.salt, password); +} + +static bool verifyUser(const User& user, std::string const& password) +{ + const auto hash = getPasswordHash(user.salt, password); + return crypto_verify32((uint8_t*)hash.data(), (uint8_t*)user.password_hash.data()) == 0; +} + +int createUser(const std::string& username, const std::string& password){ + if (username.empty() || password.empty()) return -1; int64_t id; @@ -20,8 +67,9 @@ int createUser(const std::string& username, const std::string& password_hash){ if (user.has_value()) { id = user.value().id; } else { - auto c = newUser(username, password_hash); - id = db->insert(c); + User usr = newUser(username); + createPasswordHash(usr, password); + id = db->insert(usr); } dbpool.release(db); return id; @@ -35,4 +83,16 @@ std::optional getUser(const std::string& username){ dbpool.release(db); return user; } + +std::optional getVerifiedUser(const std::string& username, const std::string& password){ + auto user = getUser(username); + if (!user.has_value()) + return {}; + + if (verifyUser(user.value(), password)){ + return user; + } + return {}; +} + } \ No newline at end of file diff --git a/src/login/loginDb.hpp b/src/login/loginDb.hpp index 804a9bd..1a552bb 100644 --- a/src/login/loginDb.hpp +++ b/src/login/loginDb.hpp @@ -16,19 +16,20 @@ namespace login struct User { int id; std::string username; - std::string password_hash; + std::vector salt; + std::vector password_hash; std::string last_login; std::string created_at; // SQLite stores DATETIME as TEXT }; -inline User newUser(const std::string& username, std::string password_hash){ - return User {-1, username, password_hash, utils::currentTime(), utils::currentTime()}; +inline User newUser(const std::string& username){ + return User {-1, username, {}, {}, utils::currentTime(), utils::currentTime()}; } -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(User, id, username, password_hash, last_login, created_at) +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(User, id, username, salt, password_hash, last_login, created_at) -int createUser(const std::string& username, const std::string& password_hash); +int createUser(const std::string& username, const std::string& password); std::optional getUser(const std::string& username); - +std::optional getVerifiedUser(const std::string& username, const std::string& password); } #endif // __LOGINDB_H__ \ No newline at end of file