mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Add password checking system
Co-authored-by: toaster <rollerorbital@gmail.com>
This commit is contained in:
parent
a0dcde98c1
commit
acbb7eb463
11 changed files with 340 additions and 0 deletions
|
|
@ -43,6 +43,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
|
|||
m_memcpy.c
|
||||
m_misc.cpp
|
||||
m_perfstats.c
|
||||
m_pw.cpp
|
||||
m_pw_hash.c
|
||||
m_random.c
|
||||
m_queue.c
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@
|
|||
#include "k_credits.h"
|
||||
#include "r_debug.hpp"
|
||||
#include "k_director.h"
|
||||
#include "m_pw.h"
|
||||
|
||||
#ifdef HWRENDER
|
||||
#include "hardware/hw_main.h" // 3D View Rendering
|
||||
|
|
@ -1746,6 +1747,8 @@ void D_SRB2Main(void)
|
|||
|
||||
CON_SetLoadingProgress(LOADED_PWAD);
|
||||
|
||||
M_PasswordInit();
|
||||
|
||||
//---------------------------------------------------- READY SCREEN
|
||||
// we need to check for dedicated before initialization of some subsystems
|
||||
|
||||
|
|
|
|||
|
|
@ -469,6 +469,7 @@ void D_RegisterServerCommands(void)
|
|||
|
||||
#ifdef DEVELOP
|
||||
COM_AddDebugCommand("fastforward", Command_FastForward);
|
||||
COM_AddDebugCommand("crypt", Command_Crypt_f);
|
||||
#endif
|
||||
|
||||
K_RegisterMidVoteCVars();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
/// \file deh_soc.c
|
||||
/// \brief Load SOC file and change tables and text
|
||||
|
||||
#include "modp_b64/modp_b64.h"
|
||||
|
||||
#include "doomdef.h"
|
||||
#include "d_main.h" // for srb2home
|
||||
#include "g_game.h"
|
||||
|
|
@ -42,6 +44,7 @@
|
|||
#endif
|
||||
|
||||
#include "m_cond.h"
|
||||
#include "m_pw_hash.h"
|
||||
|
||||
#include "dehacked.h"
|
||||
#include "deh_soc.h"
|
||||
|
|
@ -2589,6 +2592,27 @@ static void readcondition(UINT16 set, UINT32 id, char *word2)
|
|||
|
||||
stringvar = Z_StrDup(spos);
|
||||
}
|
||||
else if (fastcmp(params[0], "PASSWORD"))
|
||||
{
|
||||
size_t slen = strlen(spos);
|
||||
|
||||
EXTENDEDPARAMCHECK(spos, 1);
|
||||
ty = UC_PASSWORD;
|
||||
|
||||
if (slen > modp_b64_encode_len(M_PW_BUF_SIZE)-1)
|
||||
{
|
||||
deh_warning("Password hash is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
stringvar = Z_Malloc(modp_b64_decode_len(slen), PU_STATIC, NULL);
|
||||
if (modp_b64_decode(stringvar, spos, slen) != M_PW_BUF_SIZE)
|
||||
{
|
||||
deh_warning("Password hash is invalid");
|
||||
Z_Free(stringvar);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ty != UC_NONE)
|
||||
goto setcondition;
|
||||
|
|
|
|||
|
|
@ -88,6 +88,9 @@ void Command_Goto_f(void);
|
|||
void Command_Angle_f(void);
|
||||
void Command_RespawnAt_f(void);
|
||||
void Command_GotoSkybox_f(void);
|
||||
#ifdef DEVELOP
|
||||
void Command_Crypt_f(void);
|
||||
#endif
|
||||
#ifdef _DEBUG
|
||||
void Command_CauseCfail_f(void);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1608,6 +1608,8 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
|
|||
return false;
|
||||
case UC_TUTORIALSKIP:
|
||||
return (gamedata->finishedtutorialchallenge == true);
|
||||
case UC_PASSWORD:
|
||||
return (cn->stringvar == NULL);
|
||||
|
||||
case UC_SPRAYCAN:
|
||||
{
|
||||
|
|
@ -2475,6 +2477,8 @@ static const char *M_GetConditionString(condition_t *cn)
|
|||
return NULL;
|
||||
case UC_TUTORIALSKIP:
|
||||
return "successfully skip the Tutorial";
|
||||
case UC_PASSWORD:
|
||||
return "enter a secret password";
|
||||
|
||||
case UC_SPRAYCAN:
|
||||
{
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ typedef enum
|
|||
UC_CRASH, // Hee ho !
|
||||
UC_TUTORIALSKIP, // Complete the Tutorial Challenge
|
||||
|
||||
UC_PASSWORD, // Type in something funny
|
||||
|
||||
UC_SPRAYCAN, // Grab a spraycan
|
||||
|
||||
UC_PRISONEGGCD, // Grab a CD from a Prison Egg
|
||||
|
|
|
|||
225
src/m_pw.cpp
Normal file
225
src/m_pw.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2022-2023 by Vivian "toastergrl" Grannell.
|
||||
// Copyright (C) 2024 by James Robert Roman.
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "modp_b64/modp_b64.h"
|
||||
|
||||
#include "cxxutil.hpp"
|
||||
|
||||
#include "command.h"
|
||||
#include "d_main.h"
|
||||
#include "doomdef.h"
|
||||
#include "doomstat.h"
|
||||
#include "doomtype.h"
|
||||
#include "g_game.h"
|
||||
#include "k_menu.h"
|
||||
#include "m_cheat.h"
|
||||
#include "m_cond.h"
|
||||
#include "m_pw.h"
|
||||
#include "m_pw_hash.h"
|
||||
#include "s_sound.h"
|
||||
#include "sounds.h"
|
||||
#include "stun.h" // csprng
|
||||
#include "z_zone.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct Pw
|
||||
{
|
||||
Pw(void (*cb)(), const char *encoded_hash) : cb_(cb), hash_(decode_hash(encoded_hash)) {}
|
||||
|
||||
void (*cb_)();
|
||||
const std::array<UINT8, M_PW_BUF_SIZE> hash_;
|
||||
|
||||
private:
|
||||
static std::array<UINT8, M_PW_BUF_SIZE> decode_hash(std::string encoded)
|
||||
{
|
||||
std::array<UINT8, M_PW_BUF_SIZE> decoded;
|
||||
if (modp::b64_decode(encoded).size() != decoded.size())
|
||||
throw std::invalid_argument("hash is incorrectly sized");
|
||||
std::copy(encoded.begin(), encoded.end(), decoded.begin());
|
||||
return decoded;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<Pw> passwords;
|
||||
|
||||
// m_cond.c
|
||||
template <typename F>
|
||||
void iter_conditions(F&& f)
|
||||
{
|
||||
UINT32 i, j;
|
||||
conditionset_t *c;
|
||||
condition_t *cn;
|
||||
|
||||
for (i = 0; i < MAXCONDITIONSETS; ++i)
|
||||
{
|
||||
c = &conditionSets[i];
|
||||
|
||||
if (!c->numconditions || gamedata->achieved[i])
|
||||
continue;
|
||||
|
||||
for (j = 0; j < c->numconditions; ++j)
|
||||
{
|
||||
cn = &c->condition[j];
|
||||
|
||||
if (cn->type != UC_PASSWORD)
|
||||
continue;
|
||||
|
||||
if (cn->stringvar == NULL)
|
||||
continue;
|
||||
|
||||
f(cn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
|
||||
try_password_e M_TryPassword(const char *password, boolean conditions)
|
||||
{
|
||||
using var = std::variant<std::monostate, condition_t*, Pw*>;
|
||||
|
||||
// Normalize input casing
|
||||
std::string key = password;
|
||||
strlwr(key.data());
|
||||
|
||||
auto worker = [&key](const UINT8* hash, var result)
|
||||
{
|
||||
if (M_HashCompare(hash, key.c_str()))
|
||||
result = std::monostate {}; // fail state
|
||||
return result;
|
||||
};
|
||||
|
||||
// Because hashing is time consuming, do the work in parallel.
|
||||
std::vector<std::future<var>> jobs;
|
||||
auto add_job = [&](auto&&... args)
|
||||
{
|
||||
jobs.push_back(std::move(std::async(std::launch::async, worker, args...)));
|
||||
};
|
||||
|
||||
for (Pw& pw : passwords)
|
||||
add_job(pw.hash_.data(), &pw);
|
||||
|
||||
// Only consider challenges passwords as needed.
|
||||
if (conditions)
|
||||
iter_conditions([&](condition_t* cn) { add_job((const UINT8*)cn->stringvar, cn); });
|
||||
|
||||
var result;
|
||||
for (auto& job : jobs)
|
||||
{
|
||||
SRB2_ASSERT(job.valid());
|
||||
// Wait for every thread to finish, then retrieve the last matched password (if any).
|
||||
if (var n = job.get(); !std::holds_alternative<std::monostate>(n))
|
||||
result = n;
|
||||
}
|
||||
|
||||
try_password_e return_code = M_PW_INVALID;
|
||||
if (!std::holds_alternative<std::monostate>(result))
|
||||
{
|
||||
// Evaluate the password's function.
|
||||
auto visitor = srb2::Overload {
|
||||
[&](condition_t* cn)
|
||||
{
|
||||
// Remove the password for this session.
|
||||
Z_Free(cn->stringvar);
|
||||
cn->stringvar = NULL;
|
||||
return_code = M_PW_CHALLENGES;
|
||||
},
|
||||
[&](Pw* pw)
|
||||
{
|
||||
pw->cb_();
|
||||
return_code = M_PW_EXTRAS;
|
||||
},
|
||||
[](std::monostate) {},
|
||||
};
|
||||
std::visit(visitor, result);
|
||||
}
|
||||
|
||||
return return_code;
|
||||
}
|
||||
|
||||
#ifdef DEVELOP
|
||||
void Command_Crypt_f(void)
|
||||
{
|
||||
if (COM_Argc() == 1)
|
||||
{
|
||||
CONS_Printf(
|
||||
"crypt <password>: generate a password hash\n"
|
||||
"crypt -i <file>: generate multiple hashes by reading from file\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto gen = [](char *input)
|
||||
{
|
||||
UINT8 bin[M_PW_BUF_SIZE];
|
||||
UINT8* salt = &bin[M_PW_HASH_SIZE];
|
||||
csprng(salt, M_PW_SALT_SIZE); // randomize salt
|
||||
|
||||
strlwr(input);
|
||||
M_HashPassword(bin, input, salt);
|
||||
CONS_Printf("%s %s\n", input, modp::b64_encode((const char*)bin, M_PW_BUF_SIZE).c_str());
|
||||
};
|
||||
|
||||
if (!stricmp(COM_Argv(1), "-i"))
|
||||
{
|
||||
if (COM_Argc() != 3)
|
||||
{
|
||||
CONS_Printf("crypt: missing file argument\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::ifstream file{va(pandf, srb2home, COM_Argv(2))};
|
||||
if (!file.is_open())
|
||||
{
|
||||
CONS_Printf("crypt: file error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::string line; std::getline(file, line);)
|
||||
{
|
||||
// remove comments
|
||||
std::size_t p = line.find("#");
|
||||
if (p == line.npos)
|
||||
p = line.size();
|
||||
|
||||
// remove trailing whitespace
|
||||
while (p > 0 && std::isspace(line[p - 1]))
|
||||
p--;
|
||||
|
||||
line.erase(p);
|
||||
|
||||
// ignore empty or completely filtered lines
|
||||
if (!line.empty())
|
||||
gen(line.data());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
gen(COM_Args());
|
||||
}
|
||||
#endif
|
||||
|
||||
void M_PasswordInit(void)
|
||||
{
|
||||
}
|
||||
34
src/m_pw.h
Normal file
34
src/m_pw.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2024 by James Robert Roman
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef m_pw_H
|
||||
#define m_pw_H
|
||||
|
||||
#include "doomtype.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
M_PW_INVALID,
|
||||
M_PW_EXTRAS,
|
||||
M_PW_CHALLENGES,
|
||||
}
|
||||
try_password_e;
|
||||
|
||||
void M_PasswordInit(void);
|
||||
try_password_e M_TryPassword(const char *password, boolean challenges);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif/*m_pw_H*/
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#include "../s_sound.h"
|
||||
#include "../f_finale.h"
|
||||
#include "../k_credits.h"
|
||||
#include "../m_pw.h"
|
||||
|
||||
static void M_Credits(INT32 choice)
|
||||
{
|
||||
|
|
@ -170,6 +171,31 @@ void M_ExtrasTick(void)
|
|||
extrasmenu.textx = 160;
|
||||
extrasmenu.texty = 50;
|
||||
}
|
||||
|
||||
if (menutyping.active == false && cv_dummyextraspassword.string[0] != '\0')
|
||||
{
|
||||
switch (M_TryPassword(cv_dummyextraspassword.string, true))
|
||||
{
|
||||
case M_PW_CHALLENGES:
|
||||
if (M_UpdateUnlockablesAndExtraEmblems(true, true))
|
||||
{
|
||||
M_Challenges(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case M_PW_EXTRAS:
|
||||
if (menuactive == true)
|
||||
{
|
||||
M_InitExtras(-1);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
CV_StealthSet(&cv_dummyextraspassword, "");
|
||||
}
|
||||
}
|
||||
|
||||
boolean M_ExtrasInputs(INT32 ch)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "../r_main.h"
|
||||
#include "../m_easing.h"
|
||||
#include "../g_input.h"
|
||||
#include "../m_pw.h"
|
||||
|
||||
#include <forward_list>
|
||||
|
||||
|
|
@ -851,6 +852,22 @@ void M_GonerTick(void)
|
|||
if (menutyping.active || menumessage.active || P_AutoPause())
|
||||
return;
|
||||
|
||||
if (cv_dummyextraspassword.string[0] != '\0')
|
||||
{
|
||||
// Challenges are not interpreted at this stage.
|
||||
// See M_ExtraTick for the full behaviour.
|
||||
|
||||
if (M_TryPassword(cv_dummyextraspassword.string, false) != M_PW_EXTRAS)
|
||||
{
|
||||
goner_delay = 0;
|
||||
LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE,
|
||||
"Aha! Nice try. You're tricky enough WITHOUT admin access, thank you.");
|
||||
M_GonerHidePassword();
|
||||
}
|
||||
|
||||
CV_StealthSet(&cv_dummyextraspassword, "");
|
||||
}
|
||||
|
||||
if (goner_typewriter.textDone)
|
||||
{
|
||||
if (!LinesOutput.empty())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue