Store password hash instead of plaintext for login persistence

Save SHA1(UPPER(user):UPPER(pass)) hash to login.cfg instead of the
plaintext password. On subsequent logins, use the stored hash directly
with a new authenticateWithHash() method that bypasses password hashing.
The password field shows a placeholder when using a stored hash.
This commit is contained in:
Kelsi 2026-02-05 15:09:16 -08:00
parent de8c195eaf
commit 669d89c108
6 changed files with 107 additions and 4 deletions

View file

@ -1,13 +1,33 @@
#include "ui/auth_screen.hpp"
#include "auth/crypto.hpp"
#include "core/logger.hpp"
#include <imgui.h>
#include <sstream>
#include <fstream>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <algorithm>
#include <iomanip>
namespace wowee { namespace ui {
static std::string hexEncode(const std::vector<uint8_t>& data) {
std::ostringstream ss;
for (uint8_t b : data)
ss << std::hex << std::setfill('0') << std::setw(2) << (int)b;
return ss.str();
}
static std::vector<uint8_t> hexDecode(const std::string& hex) {
std::vector<uint8_t> bytes;
for (size_t i = 0; i + 1 < hex.size(); i += 2) {
uint8_t b = static_cast<uint8_t>(std::stoul(hex.substr(i, 2), nullptr, 16));
bytes.push_back(b);
}
return bytes;
}
AuthScreen::AuthScreen() {
}
@ -79,6 +99,18 @@ void AuthScreen::render(auth::AuthHandler& authHandler) {
setStatus("Authentication successful!", false);
authenticating = false;
// Compute and save password hash if user typed a fresh password
if (!usingStoredHash) {
std::string upperUser = username;
std::string upperPass = password;
std::transform(upperUser.begin(), upperUser.end(), upperUser.begin(), ::toupper);
std::transform(upperPass.begin(), upperPass.end(), upperPass.begin(), ::toupper);
std::string combined = upperUser + ":" + upperPass;
auto hash = auth::Crypto::sha1(combined);
savedPasswordHash = hexEncode(hash);
}
saveLoginInfo();
// Call success callback
if (onSuccess) {
onSuccess();
@ -139,7 +171,10 @@ void AuthScreen::attemptAuth(auth::AuthHandler& authHandler) {
return;
}
if (strlen(password) == 0) {
// Check if using stored hash (password field contains placeholder)
bool useHash = usingStoredHash && std::strcmp(password, PASSWORD_PLACEHOLDER) == 0;
if (!useHash && strlen(password) == 0) {
setStatus("Password cannot be empty", true);
return;
}
@ -169,7 +204,13 @@ void AuthScreen::attemptAuth(auth::AuthHandler& authHandler) {
saveLoginInfo();
// Send authentication credentials
authHandler.authenticate(username, password);
if (useHash) {
auto hashBytes = hexDecode(savedPasswordHash);
authHandler.authenticateWithHash(username, hashBytes);
} else {
usingStoredHash = false;
authHandler.authenticate(username, password);
}
} else {
std::stringstream errSs;
errSs << "Failed to connect to " << hostname << ":" << port
@ -210,6 +251,9 @@ void AuthScreen::saveLoginInfo() {
out << "hostname=" << hostname << "\n";
out << "port=" << port << "\n";
out << "username=" << username << "\n";
if (!savedPasswordHash.empty()) {
out << "password_hash=" << savedPasswordHash << "\n";
}
LOG_INFO("Login info saved to ", path);
}
@ -234,9 +278,18 @@ void AuthScreen::loadLoginInfo() {
} else if (key == "username" && !val.empty()) {
strncpy(username, val.c_str(), sizeof(username) - 1);
username[sizeof(username) - 1] = '\0';
} else if (key == "password_hash" && !val.empty()) {
savedPasswordHash = val;
}
}
// If we have a saved hash, fill password with placeholder
if (!savedPasswordHash.empty()) {
strncpy(password, PASSWORD_PLACEHOLDER, sizeof(password) - 1);
password[sizeof(password) - 1] = '\0';
usingStoredHash = true;
}
LOG_INFO("Login info loaded from ", path);
}