Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
05fddaf
Enhance OtpSystem with improved code handling
Koaio Mar 10, 2026
2e42714
Enhance OtpSystem with configurable settings
Koaio Mar 10, 2026
00d8d4a
Update PrivateChatOtpConstant value
Koaio Mar 10, 2026
7424cb9
Merge branch 'master' into Harden-OTP-system
Koaio Mar 10, 2026
b35da4b
Add default constructor to OtpSystem Settings
Koaio Mar 10, 2026
404492f
Add default constructor for OtpSystem
Koaio Mar 10, 2026
f401dfd
Refactor OtpSystem constructor overloads
Koaio Mar 10, 2026
c4e008a
Change code generation to use uint32_t type
Koaio Mar 10, 2026
b26f90c
Update include/server/system/OtpSystem.hpp
Koaio Mar 11, 2026
de2fe62
Update include/server/system/OtpSystem.hpp
Koaio Mar 11, 2026
a133f76
Update include/server/system/OtpSystem.hpp
Koaio Mar 11, 2026
293db6f
Update include/server/system/OtpSystem.hpp
Koaio Mar 11, 2026
38f5b15
Merge branch 'Story-Of-Alicia:master' into Harden-OTP-system
Koaio Mar 12, 2026
f36079e
Merge branch 'master' into Harden-OTP-system
Koaio Mar 13, 2026
d954d14
Update OtpSystem.hpp
Koaio Mar 14, 2026
d5b80a1
Merge branch 'master' into Harden-OTP-system
Koaio Mar 14, 2026
c87ae10
Merge branch 'Story-Of-Alicia:master' into Harden-OTP-system
Koaio Mar 15, 2026
143775d
config: add Otp settings struct to Config
Koaio Mar 15, 2026
b48a7fd
config: parse OTP section from server config file
Koaio Mar 15, 2026
e373823
OtpSystem: take ServerInstance& and drop local Settings
Koaio Mar 15, 2026
0c966b2
OtpSystem: read config from ServerInstance::GetSettings().otp
Koaio Mar 15, 2026
7719f81
ServerInstance: construct OtpSystem with *this
Koaio Mar 15, 2026
dc1e4c9
OtpSystem: merge AttemptRecord into Code, drop _attempts map
Koaio Mar 15, 2026
33ec0f6
OtpSystem: invalidate code after 5 failed attempts, single _codes map
Koaio Mar 15, 2026
6dde14c
config: remove lockoutDuration from Otp settings
Koaio Mar 15, 2026
aa545b8
config: stop parsing otp lockout_duration
Koaio Mar 15, 2026
3a2a90e
Merge branch 'master' into Harden-OTP-system
Koaio Mar 15, 2026
316fa01
Merge branch 'master' into Harden-OTP-system
Koaio Mar 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions include/server/Config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
#ifndef CONFIG_HPP
#define CONFIG_HPP

#include <nlohmann/json.hpp>
#include <boost/asio/ip/address.hpp>
#include <chrono>
#include <cstdint>
#include <nlohmann/json.hpp>

namespace server
{
Expand Down Expand Up @@ -131,7 +133,8 @@ class Config
{
enum class Source
{
File, Postgres
File,
Postgres
} source{Source::File};

struct File
Expand All @@ -145,6 +148,14 @@ class Config
} postgres{};
} data{};

//!
struct Otp
{
std::chrono::seconds codeTtl{30};
uint32_t maxFailedAttempts{5};
std::chrono::seconds purgeInterval{60};
} otp{};

//! Loads the config from the environment.
void LoadFromEnvironment();
//! Loads the config from the specified file.
Expand All @@ -154,4 +165,4 @@ class Config

} // namespace server

#endif // CONFIG_HPP
#endif // CONFIG_HPP
2 changes: 1 addition & 1 deletion include/server/chat/PrivateChatDirector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace server
{

//! Chat client OTP constant
constexpr uint32_t PrivateChatOtpConstant = 0x14E05CE5;
constexpr uint32_t PrivateChatOtpConstant = 0x7C3D91A2;

class ServerInstance;

Expand Down
40 changes: 35 additions & 5 deletions include/server/system/OtpSystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,61 @@
#define OTPSYSTEM_HPP

#include <chrono>
#include <random>
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <random>
#include <unordered_map>

namespace server
{

class ServerInstance;

class OtpSystem
{
public:
uint32_t GrantCode(size_t key);
bool AuthorizeCode(size_t key, uint32_t code, bool consume = true);
explicit OtpSystem(ServerInstance& serverInstance);

//! Grants a one-time code for the given key.
//! If a code already exists for this key, it is replaced.
//! @param key Identity key the code is bound to.
//! @return The generated one-time code.
[[nodiscard]] uint32_t GrantCode(size_t key);

//! Authorizes a one-time code for the given key.
//! If the code is unsuccessfully authorized multiple times the code is invalidated
//! to protect against brute force.
//! @param key Identity key the code is bound to.
//! @param code The code to verify.
//! @param consume If true, the code is consumed (erased) on success.
//! @retval `true` if the code is valid
//! @retval `false` if the code is invalid.
[[nodiscard]] bool AuthorizeCode(size_t key, uint32_t code, bool consume = true);

//! Removes all expired codes.
//! Called automatically on a periodic basis, but can be invoked manually.
void PurgeExpired();

private:
ServerInstance& _serverInstance;

struct Code
{
//! A time point of when the code expires.
std::chrono::steady_clock::time_point expiry{};
//! A counter of (failed) authorization attempts; code is invalidated after max.
size_t authorizationAttempts{};
//! A value of the code.
uint32_t code{};
};

std::mutex _codesMutex;
std::random_device _rd;
std::mt19937 _rng;
std::unordered_map<size_t, Code> _codes;
std::chrono::steady_clock::time_point _lastPurge;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary. Invalidate the key/code after 5 unsuccessful attempts to use it.

};

} // namespace server

#endif //OTPSYSTEM_HPP
#endif // OTPSYSTEM_HPP
29 changes: 22 additions & 7 deletions src/server/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
#include <format>
#include <fstream>

#include <yaml-cpp/yaml.h>
#include <spdlog/spdlog.h>
#include <yaml-cpp/yaml.h>

namespace server
{
Expand All @@ -48,10 +48,10 @@ void Config::LoadFromEnvironment()
};

const auto getAddressAndPortVariables = [&getEnvValue](
const std::string& addressVariableName,
const std::string& portVariableName,
asio::ip::address_v4& address,
uint16_t& port)
const std::string& addressVariableName,
const std::string& portVariableName,
asio::ip::address_v4& address,
uint16_t& port)
{
try
{
Expand Down Expand Up @@ -150,8 +150,7 @@ void Config::LoadFromFile(const std::filesystem::path& filePath)
{
return Listen{
.address = util::ResolveHostName(node["address"].as<std::string>()),
.port = node["port"].as<uint16_t>()
};
.port = node["port"].as<uint16_t>()};
}
catch (const std::exception& e)
{
Expand Down Expand Up @@ -289,6 +288,22 @@ void Config::LoadFromFile(const std::filesystem::path& filePath)
{
spdlog::error("Unhandled exception parsing the dat config: {}", e.what());
}

// OTP config
try
{
const auto otpYaml = serverYaml["otp"];
if (otpYaml)
{
otp.codeTtl = std::chrono::seconds(otpYaml["code_ttl"].as<int>(30));
otp.maxFailedAttempts = otpYaml["max_failed_attempts"].as<uint32_t>(5);
otp.purgeInterval = std::chrono::seconds(otpYaml["purge_interval"].as<int>(60));
}
}
catch (const std::exception& e)
{
spdlog::error("Unhandled exception parsing the OTP config: {}", e.what());
}
}
catch (const std::exception& e)
{
Expand Down
Loading
Loading