Skip to content

Commit

Permalink
auth: add sodium pwhash authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
PaideiaDilemma committed Dec 19, 2024
1 parent d12b4a7 commit 3f20cc2
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pkg_check_modules(
gbm
hyprutils>=0.2.6
sdbus-c++>=2.0.0
libsodium
hyprgraphics)

file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
Expand Down
2 changes: 2 additions & 0 deletions nix/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
libdrm,
libGL,
libjpeg,
libsodium,
libwebp,
libxkbcommon,
mesa,
Expand Down Expand Up @@ -38,6 +39,7 @@ stdenv.mkDerivation {
buildInputs = [
cairo
file
libsodium
libdrm
libGL
libjpeg
Expand Down
5 changes: 5 additions & 0 deletions src/auth/Auth.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "Auth.hpp"
#include "Pam.hpp"
#include "Fingerprint.hpp"
#include "SodiumPWHash.hpp"
#include "../config/ConfigManager.hpp"
#include "../core/hyprlock.hpp"
#include "src/helpers/Log.hpp"
Expand All @@ -15,7 +16,11 @@ CAuth::CAuth() {
static auto* const PENABLEFINGERPRINT = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("auth:fingerprint:enabled");
if (**PENABLEFINGERPRINT)
m_vImpls.push_back(std::make_shared<CFingerprint>());
static auto* const PENABLESODIUM = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("auth:sodium:enabled");
if (**PENABLESODIUM)
m_vImpls.push_back(std::make_shared<CSodiumPWHash>());

RASSERT(!(**PENABLEPAM && **PENABLESODIUM), "Pam and sodium hash authentication are mutually exclusive! Please enable one or the other.");
RASSERT(!m_vImpls.empty(), "At least one authentication method must be enabled!");
}

Expand Down
1 change: 1 addition & 0 deletions src/auth/Auth.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
enum eAuthImplementations {
AUTH_IMPL_PAM = 0,
AUTH_IMPL_FINGERPRINT = 1,
AUTH_IMPL_SODIUM = 2,
};

class IAuthImplementation {
Expand Down
124 changes: 124 additions & 0 deletions src/auth/SodiumPWHash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include "SodiumPWHash.hpp"

#include "../helpers/Log.hpp"
#include "../core/hyprlock.hpp"

#include <filesystem>
#include <hyprutils/path/Path.hpp>
#include <sodium.h>

static std::string getSecretsConfigPath() {
static const auto [PWHASHPATH, DOTDIR] = Hyprutils::Path::findConfig("hyprlock_pwhash");
(void)DOTDIR;

RASSERT(PWHASHPATH.has_value(), "[SodiumAuth] Failed to find hyprlock_pwhash.conf. Please use \"hyprlock-setpwhash\" to generate it!");
// check permissions
using std::filesystem::perms;
const auto PERMS = std::filesystem::status(PWHASHPATH.value()).permissions();
if ((PERMS & perms::group_read) != perms::none || (PERMS & perms::group_write) != perms::none || (PERMS & perms::others_read) != perms::none ||
(PERMS & perms::others_write) != perms::none) {
RASSERT(false, "[SodiumAuth] hyprlock_pwhash.conf has insecure permissions");
}
return PWHASHPATH.value();
}

void* const* CSodiumPWHash::getConfigValuePtr(const std::string& name) {
return m_config.getConfigValuePtr(name.c_str())->getDataStaticPtr();
}

CSodiumPWHash::CSodiumPWHash() : m_config(getSecretsConfigPath().c_str(), {}) {
m_config.addConfigValue("pw_hash", Hyprlang::STRING{""});
m_config.commence();
auto result = m_config.parse();

if (result.error)
Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError());

m_checkerThread = std::thread([this]() { checkerLoop(); });
}

CSodiumPWHash::~CSodiumPWHash() {
;
}

void CSodiumPWHash::init() {
RASSERT(sodium_init() >= 0, "Failed to initialize libsodium");
}

void CSodiumPWHash::handleInput(const std::string& input) {
std::lock_guard<std::mutex> lk(m_sCheckerState.requestMutex);

m_sCheckerState.input = input;
m_sCheckerState.requested = true;

m_sCheckerState.requestCV.notify_all();
}

bool CSodiumPWHash::checkWaiting() {
return m_sCheckerState.requested;
}

std::optional<std::string> CSodiumPWHash::getLastFailText() {
return m_sCheckerState.failText.empty() ? std::nullopt : std::optional<std::string>(m_sCheckerState.failText);
}

std::optional<std::string> CSodiumPWHash::getLastPrompt() {
return "Password: ";
}

void CSodiumPWHash::terminate() {
m_sCheckerState.requestCV.notify_all();
if (m_checkerThread.joinable())
m_checkerThread.join();
}

void CSodiumPWHash::rehash(std::string& input) {
const auto CONFIGPATH = getSecretsConfigPath();

char hash[crypto_pwhash_STRBYTES];
if (crypto_pwhash_str(hash, input.c_str(), input.size(), crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) != 0) {
Debug::log(ERR, "[Sodium] Failed to hash password");
return;
}

std::ofstream out(CONFIGPATH);
out << "hyprlock {\n pw_hash = " << hash << "\n}\n";
out.close();

// set perms to -rw-------
using std::filesystem::perms;
std::filesystem::permissions(CONFIGPATH, perms::owner_read | perms::owner_write);
}

void CSodiumPWHash::checkerLoop() {
static auto* const PPWHASH = (Hyprlang::STRING*)getConfigValuePtr("pw_hash");
const auto PWHASH = std::string(*PPWHASH);
const bool NEEDSREHASH = crypto_pwhash_str_needs_rehash(PWHASH.c_str(), crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) != 0;
if (NEEDSREHASH)
Debug::log(WARN, "[Sodium] Password hash needs rehashing");

while (true) {
std::unique_lock<std::mutex> lk(m_sCheckerState.requestMutex);
m_sCheckerState.requestCV.wait(lk, [this]() { return m_sCheckerState.requested || g_pHyprlock->m_bTerminate; });

if (g_pHyprlock->isUnlocked())
return;

if (PWHASH.empty() || PWHASH.size() > crypto_pwhash_STRBYTES) {
m_sCheckerState.failText = "Invalid password hash";
Debug::log(ERR, "[SodiumAuth] Invalid password hash set in secrets.conf");
g_pAuth->enqueueFail();
} else if (crypto_pwhash_str_verify(PWHASH.c_str(), m_sCheckerState.input.c_str(), m_sCheckerState.input.length()) == 0) {
if (NEEDSREHASH)
rehash(m_sCheckerState.input);
g_pAuth->enqueueUnlock();
} else {
g_pAuth->enqueueFail();
m_sCheckerState.failText = "Failed to authenticate";
Debug::log(LOG, "[SodiumAuth] Failed to authenticate");
}

m_sCheckerState.input.clear();
m_sCheckerState.requested = false;
}
}
43 changes: 43 additions & 0 deletions src/auth/SodiumPWHash.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

#include "Auth.hpp"

#include <condition_variable>
#include <optional>
#include <string>
#include <hyprlang.hpp>
#include <thread>

class CSodiumPWHash : public IAuthImplementation {
public:
CSodiumPWHash();

virtual ~CSodiumPWHash();
virtual eAuthImplementations getImplType() {
return AUTH_IMPL_SODIUM;
}
virtual void init();
virtual void handleInput(const std::string& input);
virtual bool checkWaiting();
virtual std::optional<std::string> getLastFailText();
virtual std::optional<std::string> getLastPrompt();
virtual void terminate();

private:
void* const* getConfigValuePtr(const std::string& name);

struct {
std::condition_variable requestCV;
std::string input;
bool requested = false;
std::mutex requestMutex;
std::string failText;
} m_sCheckerState;

std::thread m_checkerThread;
void checkerLoop();

void rehash(std::string& input);

Hyprlang::CConfig m_config;
};
1 change: 1 addition & 0 deletions src/config/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ void CConfigManager::init() {
m_config.addConfigValue("auth:fingerprint:enabled", Hyprlang::INT{0});
m_config.addConfigValue("auth:fingerprint:ready_message", Hyprlang::STRING{"(Scan fingerprint to unlock)"});
m_config.addConfigValue("auth:fingerprint:present_message", Hyprlang::STRING{"Scanning fingerprint"});
m_config.addConfigValue("auth:sodium:enabled", Hyprlang::INT{1});

m_config.addSpecialCategory("background", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
m_config.addSpecialConfigValue("background", "monitor", Hyprlang::STRING{""});
Expand Down

0 comments on commit 3f20cc2

Please sign in to comment.