mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 09:33:51 +00:00
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:
parent
45466f3d11
commit
ca84384402
6 changed files with 107 additions and 4 deletions
|
|
@ -43,6 +43,7 @@ public:
|
|||
|
||||
// Authentication
|
||||
void authenticate(const std::string& username, const std::string& password);
|
||||
void authenticateWithHash(const std::string& username, const std::vector<uint8_t>& authHash);
|
||||
|
||||
// Realm list
|
||||
void requestRealmList();
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ public:
|
|||
// Initialize with username and password
|
||||
void initialize(const std::string& username, const std::string& password);
|
||||
|
||||
// Initialize with username and pre-computed auth hash (SHA1(UPPER(user):UPPER(pass)))
|
||||
void initializeWithHash(const std::string& username, const std::vector<uint8_t>& authHash);
|
||||
|
||||
// Feed server challenge data (B, g, N, salt)
|
||||
void feed(const std::vector<uint8_t>& B,
|
||||
const std::vector<uint8_t>& g,
|
||||
|
|
@ -67,6 +70,7 @@ private:
|
|||
// Stored credentials
|
||||
std::string stored_username;
|
||||
std::string stored_password;
|
||||
std::vector<uint8_t> stored_auth_hash; // Pre-computed SHA1(UPPER(user):UPPER(pass))
|
||||
|
||||
bool initialized = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -57,6 +57,11 @@ private:
|
|||
float authTimer = 0.0f; // Timeout tracker
|
||||
static constexpr float AUTH_TIMEOUT = 10.0f;
|
||||
|
||||
// Saved password hash (SHA1(UPPER(user):UPPER(pass)) as hex)
|
||||
std::string savedPasswordHash;
|
||||
bool usingStoredHash = false;
|
||||
static constexpr const char* PASSWORD_PLACEHOLDER = "\x01\x01\x01\x01\x01\x01\x01\x01";
|
||||
|
||||
// Callbacks
|
||||
std::function<void()> onSuccess;
|
||||
std::function<void()> onSinglePlayer;
|
||||
|
|
|
|||
|
|
@ -93,6 +93,32 @@ void AuthHandler::authenticate(const std::string& user, const std::string& pass)
|
|||
sendLogonChallenge();
|
||||
}
|
||||
|
||||
void AuthHandler::authenticateWithHash(const std::string& user, const std::vector<uint8_t>& authHash) {
|
||||
if (!isConnected()) {
|
||||
LOG_ERROR("Cannot authenticate: not connected to auth server");
|
||||
fail("Not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
if (state != AuthState::CONNECTED) {
|
||||
LOG_ERROR("Cannot authenticate: invalid state");
|
||||
fail("Invalid state");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Starting authentication for user (with hash): ", user);
|
||||
|
||||
username = user;
|
||||
password.clear();
|
||||
|
||||
// Initialize SRP with pre-computed hash
|
||||
srp = std::make_unique<SRP>();
|
||||
srp->initializeWithHash(username, authHash);
|
||||
|
||||
// Send LOGON_CHALLENGE
|
||||
sendLogonChallenge();
|
||||
}
|
||||
|
||||
void AuthHandler::sendLogonChallenge() {
|
||||
LOG_DEBUG("Sending LOGON_CHALLENGE");
|
||||
|
||||
|
|
|
|||
|
|
@ -19,11 +19,23 @@ void SRP::initialize(const std::string& username, const std::string& password) {
|
|||
// Store credentials for later use
|
||||
stored_username = username;
|
||||
stored_password = password;
|
||||
stored_auth_hash.clear();
|
||||
|
||||
initialized = true;
|
||||
LOG_DEBUG("SRP initialized");
|
||||
}
|
||||
|
||||
void SRP::initializeWithHash(const std::string& username, const std::vector<uint8_t>& authHash) {
|
||||
LOG_DEBUG("Initializing SRP with username and pre-computed hash: ", username);
|
||||
|
||||
stored_username = username;
|
||||
stored_password.clear();
|
||||
stored_auth_hash = authHash;
|
||||
|
||||
initialized = true;
|
||||
LOG_DEBUG("SRP initialized with hash");
|
||||
}
|
||||
|
||||
void SRP::feed(const std::vector<uint8_t>& B_bytes,
|
||||
const std::vector<uint8_t>& g_bytes,
|
||||
const std::vector<uint8_t>& N_bytes,
|
||||
|
|
@ -50,8 +62,10 @@ void SRP::feed(const std::vector<uint8_t>& B_bytes,
|
|||
|
||||
// Now compute everything in sequence
|
||||
|
||||
// 1. Compute auth hash: H(I:P)
|
||||
std::vector<uint8_t> auth_hash = computeAuthHash(stored_username, stored_password);
|
||||
// 1. Compute auth hash: H(I:P) — use stored hash if available
|
||||
std::vector<uint8_t> auth_hash = stored_auth_hash.empty()
|
||||
? computeAuthHash(stored_username, stored_password)
|
||||
: stored_auth_hash;
|
||||
|
||||
// 2. Compute x = H(s | H(I:P))
|
||||
std::vector<uint8_t> x_input;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue