mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-02-17 19:11:30 +00:00
Merge branch 'licenses' into 'master'
Add LICENSE-3RD-PARTY.txt See merge request KartKrew/Kart!1110
This commit is contained in:
commit
495b51f6c2
25 changed files with 6016 additions and 20 deletions
1703
LICENSE-3RD-PARTY.txt
Normal file
1703
LICENSE-3RD-PARTY.txt
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -559,6 +559,7 @@ add_subdirectory(sdl)
|
|||
add_subdirectory(objects)
|
||||
add_subdirectory(acs)
|
||||
add_subdirectory(rhi)
|
||||
add_subdirectory(monocypher)
|
||||
if(SRB2_CONFIG_ENABLE_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
|
|
|||
697
src/d_clisrv.c
697
src/d_clisrv.c
|
|
@ -47,6 +47,8 @@
|
|||
#include "lua_hook.h"
|
||||
#include "md5.h"
|
||||
#include "m_perfstats.h"
|
||||
#include "monocypher/monocypher.h"
|
||||
#include "stun.h"
|
||||
|
||||
// SRB2Kart
|
||||
#include "k_kart.h"
|
||||
|
|
@ -121,6 +123,7 @@ SINT8 nodetoplayer3[MAXNETNODES]; // say the numplayer for this node if any (spl
|
|||
SINT8 nodetoplayer4[MAXNETNODES]; // say the numplayer for this node if any (splitscreen == 3)
|
||||
UINT8 playerpernode[MAXNETNODES]; // used specialy for splitscreen
|
||||
boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
|
||||
boolean nodeneedsauth[MAXNETNODES];
|
||||
|
||||
tic_t servermaxping = 20; // server's max delay, in frames. Defaults to 20
|
||||
static tic_t nettics[MAXNETNODES]; // what tic the client have received
|
||||
|
|
@ -156,9 +159,34 @@ char connectedservername[MAXSERVERNAME];
|
|||
/// \todo WORK!
|
||||
boolean acceptnewnode = true;
|
||||
|
||||
UINT32 ourIP; // Used when populating PT_SERVERCHALLENGE (guards against signature reuse)
|
||||
uint8_t lastReceivedKey[MAXNETNODES][MAXSPLITSCREENPLAYERS][PUBKEYLENGTH]; // Player's public key (join process only! active players have it on player_t)
|
||||
uint8_t lastSentChallenge[MAXNETNODES][CHALLENGELENGTH]; // The random message we asked them to sign in PT_SERVERCHALLENGE, check it in PT_CLIENTJOIN
|
||||
uint8_t awaitingChallenge[CHALLENGELENGTH]; // The message the server asked our client to sign when joining
|
||||
uint8_t lastChallengeAll[CHALLENGELENGTH]; // The message we asked EVERYONE to sign for client-to-client identity proofs
|
||||
uint8_t lastReceivedSignature[MAXPLAYERS][SIGNATURELENGTH]; // Everyone's response to lastChallengeAll
|
||||
uint8_t knownWhenChallenged[MAXPLAYERS][PUBKEYLENGTH]; // Everyone a client saw at the moment a challenge should be initiated
|
||||
boolean expectChallenge = false; // Were we in-game before a client-to-client challenge should have been sent?
|
||||
|
||||
uint8_t priorKeys[MAXPLAYERS][PUBKEYLENGTH]; // Make a note of keys before consuming a new gamestate, and if the server tries to send us a gamestate where keys differ, assume shenanigans
|
||||
|
||||
boolean serverisfull = false; //lets us be aware if the server was full after we check files, but before downloading, so we can ask if the user still wants to download or not
|
||||
tic_t firstconnectattempttime = 0;
|
||||
|
||||
consvar_t cv_allowguests = CVAR_INIT ("allowguests", "On", CV_SAVE, CV_OnOff, NULL);
|
||||
|
||||
#ifdef DEVELOP
|
||||
consvar_t cv_badjoin = CVAR_INIT ("badjoin", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_badtraffic = CVAR_INIT ("badtraffic", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_badresponse = CVAR_INIT ("badresponse", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_noresponse = CVAR_INIT ("noresponse", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_nochallenge = CVAR_INIT ("nochallenge", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_badresults = CVAR_INIT ("badresults", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_noresults = CVAR_INIT ("noresults", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_badtime = CVAR_INIT ("badtime", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_badip = CVAR_INIT ("badip", "0", 0, CV_Unsigned, NULL);
|
||||
#endif
|
||||
|
||||
// engine
|
||||
|
||||
// Must be a power of two
|
||||
|
|
@ -191,6 +219,60 @@ consvar_t cv_httpsource = CVAR_INIT ("http_source", "", CV_SAVE, NULL, NULL);
|
|||
|
||||
consvar_t cv_kicktime = CVAR_INIT ("kicktime", "10", CV_SAVE, CV_Unsigned, NULL);
|
||||
|
||||
// Generate a message for an authenticating client to sign, with some guarantees about who we are.
|
||||
void GenerateChallenge(uint8_t *buf)
|
||||
{
|
||||
#ifndef SRB2_LITTLE_ENDIAN
|
||||
#error "FIXME: 64-bit timestamp field is not supported on Big Endian"
|
||||
#endif
|
||||
|
||||
UINT64 now = time(NULL);
|
||||
csprng(buf, CHALLENGELENGTH); // Random noise as a baseline, but...
|
||||
memcpy(buf, &now, sizeof(now)); // Timestamp limits the reuse window.
|
||||
memcpy(buf + sizeof(now), &ourIP, sizeof(ourIP)); // IP prevents captured signatures from being used elsewhere.
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_badtime.value)
|
||||
{
|
||||
CV_AddValue(&cv_badtime, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badtime enabled, trashing time in auth message\n");
|
||||
memset(buf, 0, sizeof(now));
|
||||
}
|
||||
|
||||
if (cv_badip.value)
|
||||
{
|
||||
CV_AddValue(&cv_badip, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badip enabled, trashing IP in auth message\n");
|
||||
memset(buf + sizeof(now), 0, sizeof(ourIP));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Modified servers can throw softballs or reuse challenges.
|
||||
// Don't sign anything that wasn't generated just for us!
|
||||
shouldsign_t ShouldSignChallenge(uint8_t *message)
|
||||
{
|
||||
#ifndef SRB2_LITTLE_ENDIAN
|
||||
#error "FIXME: 64-bit timestamp field is not supported on Big Endian"
|
||||
#endif
|
||||
|
||||
UINT64 then, now;
|
||||
UINT32 claimedIP, realIP;
|
||||
|
||||
now = time(NULL);
|
||||
memcpy(&then, message, sizeof(then));
|
||||
memcpy(&claimedIP, message + sizeof(then), sizeof(claimedIP));
|
||||
realIP = I_GetNodeAddressInt(servernode);
|
||||
|
||||
if ((max(now, then) - min(now, then)) > 60*5)
|
||||
return SIGN_BADTIME;
|
||||
|
||||
if (realIP != claimedIP && I_IsExternalAddress(&realIP))
|
||||
return SIGN_BADIP;
|
||||
|
||||
return SIGN_OK;
|
||||
}
|
||||
|
||||
static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n)
|
||||
{
|
||||
const size_t d = n / sizeof(ticcmd_t);
|
||||
|
|
@ -553,6 +635,8 @@ typedef enum
|
|||
CL_PREPAREHTTPFILES,
|
||||
CL_DOWNLOADHTTPFILES,
|
||||
#endif
|
||||
CL_SENDKEY,
|
||||
CL_WAITCHALLENGE,
|
||||
} cl_mode_t;
|
||||
|
||||
static void GetPackets(void);
|
||||
|
|
@ -834,9 +918,80 @@ static boolean CL_SendJoin(void)
|
|||
|
||||
memcpy(&netbuffer->u.clientcfg.availabilities, R_GetSkinAvailabilities(false, false), MAXAVAILABILITY*sizeof(UINT8));
|
||||
|
||||
// Don't leak old signatures from prior sessions.
|
||||
memset(&netbuffer->u.clientcfg.challengeResponse, 0, sizeof(((clientconfig_pak *)0)->challengeResponse));
|
||||
|
||||
if (client && netgame)
|
||||
{
|
||||
shouldsign_t safe = ShouldSignChallenge(awaitingChallenge);
|
||||
|
||||
if (safe != SIGN_OK)
|
||||
{
|
||||
if (safe == SIGN_BADIP)
|
||||
{
|
||||
I_Error("External server IP didn't match the message it sent.");
|
||||
}
|
||||
else if (safe == SIGN_BADTIME)
|
||||
{
|
||||
I_Error("External server sent a message with an unusual timestamp.\nCheck your clocks!");
|
||||
}
|
||||
else
|
||||
{
|
||||
I_Error("External server asked for a signature on something strange.\nPlease notify a developer if you've seen this more than once.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
uint8_t signature[SIGNATURELENGTH];
|
||||
profile_t *localProfile = PR_GetLocalPlayerProfile(i);
|
||||
|
||||
if (PR_IsLocalPlayerGuest(i)) // GUESTS don't have keys
|
||||
{
|
||||
memset(signature, 0, sizeof(signature));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If our keys are garbage (corrupted profile?), fail here instead of when the server boots us, so the player knows what's going on.
|
||||
crypto_eddsa_sign(signature, localProfile->secret_key, awaitingChallenge, sizeof(awaitingChallenge));
|
||||
if (crypto_eddsa_check(signature, localProfile->public_key, awaitingChallenge, sizeof(awaitingChallenge)) != 0)
|
||||
I_Error("Couldn't self-verify key associated with player %d, profile %d.\nProfile data may be corrupted.", i, cv_lastprofile[i].value); // I guess this is the most reasonable way to catch a malformed key.
|
||||
}
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_badjoin.value)
|
||||
{
|
||||
CV_AddValue(&cv_badjoin, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badjoin enabled, scrubbing signature from CL_SendJoin\n");
|
||||
memset(signature, 0, sizeof(signature));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Testing
|
||||
// memset(signature, 0, sizeof(signature));
|
||||
|
||||
memcpy(&netbuffer->u.clientcfg.challengeResponse[i], signature, sizeof(signature));
|
||||
}
|
||||
|
||||
return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak));
|
||||
}
|
||||
|
||||
static boolean CL_SendKey(void)
|
||||
{
|
||||
int i;
|
||||
netbuffer->packettype = PT_CLIENTKEY;
|
||||
|
||||
memset(netbuffer->u.clientkey.key, 0, sizeof(((clientkey_pak *)0)->key));
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
// GUEST profiles have all-zero keys. This will be handled at the end of the challenge process, don't worry about it.
|
||||
memcpy(netbuffer->u.clientkey.key[i], PR_GetProfile(cv_lastprofile[i].value)->public_key, PUBKEYLENGTH);
|
||||
}
|
||||
return HSendPacket(servernode, false, 0, sizeof (clientkey_pak) );
|
||||
}
|
||||
|
||||
static void
|
||||
CopyCaretColors (char *p, const char *s, int n)
|
||||
{
|
||||
|
|
@ -1320,6 +1475,19 @@ static void CL_LoadReceivedSavegame(boolean reloading)
|
|||
// so they know they can resume the game
|
||||
netbuffer->packettype = PT_RECEIVEDGAMESTATE;
|
||||
HSendPacket(servernode, true, 0, 0);
|
||||
|
||||
if (reloading)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (memcmp(priorKeys[i], players[i].public_key, sizeof(priorKeys[i])) != 0)
|
||||
{
|
||||
HandleSigfail("Gamestate reload contained new keys");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CL_ReloadReceivedSavegame(void)
|
||||
|
|
@ -1896,7 +2064,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
|
|||
{
|
||||
*asksent = 0; //This ensure the first join ask is right away
|
||||
firstconnectattempttime = I_GetTime();
|
||||
cl_mode = CL_ASKJOIN;
|
||||
cl_mode = CL_SENDKEY;
|
||||
}
|
||||
break;
|
||||
case CL_ASKJOIN:
|
||||
|
|
@ -1932,6 +2100,19 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
|
|||
cl_mode = CL_ASKJOIN;
|
||||
}
|
||||
break;
|
||||
case CL_SENDKEY:
|
||||
if (I_GetTime() >= *asksent && CL_SendKey())
|
||||
{
|
||||
*asksent = I_GetTime() + NEWTICRATE*3;
|
||||
cl_mode = CL_WAITCHALLENGE;
|
||||
}
|
||||
break;
|
||||
case CL_WAITCHALLENGE:
|
||||
if (I_GetTime() >= *asksent)
|
||||
{
|
||||
cl_mode = CL_SENDKEY;
|
||||
}
|
||||
break;
|
||||
case CL_DOWNLOADSAVEGAME:
|
||||
// At this state, the first (and only) needed file is the gamestate
|
||||
if (fileneeded[0].status == FS_FOUND)
|
||||
|
|
@ -2683,6 +2864,8 @@ void CL_Reset(void)
|
|||
serverisfull = false;
|
||||
connectiontimeout = (tic_t)cv_nettimeout.value; //reset this temporary hack
|
||||
|
||||
expectChallenge = false;
|
||||
|
||||
#ifdef HAVE_CURL
|
||||
curl_failedwebdownload = false;
|
||||
curl_transfers = 0;
|
||||
|
|
@ -2766,6 +2949,8 @@ static void Command_Nodes(void)
|
|||
CONS_Printf(" - %s", address);
|
||||
}
|
||||
|
||||
CONS_Printf(" [RRID-%s] ", GetPrettyRRID(players[i].public_key, true));
|
||||
|
||||
if (IsPlayerAdmin(i))
|
||||
CONS_Printf(M_GetText(" (verified admin)"));
|
||||
|
||||
|
|
@ -3121,6 +3306,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
|
|||
HU_AddChatText(va("\x82*%s left the game (Connection timeout)", player_names[pnum]), false);
|
||||
kickreason = KR_TIMEOUT;
|
||||
break;
|
||||
case KICK_MSG_SIGFAIL:
|
||||
HU_AddChatText(va("\x82*%s left the game (Invalid signature)", player_names[pnum]), false);
|
||||
kickreason = KR_TIMEOUT;
|
||||
break;
|
||||
case KICK_MSG_PLAYER_QUIT:
|
||||
if (netgame) // not splitscreen/bots
|
||||
HU_AddChatText(va("\x82*%s left the game", player_names[pnum]), false);
|
||||
|
|
@ -3177,6 +3366,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
|
|||
M_StartMessage(va(M_GetText("You have been kicked\n(%s)\nPress (B)\n"), reason), NULL, MM_NOTHING);
|
||||
else if (msg == KICK_MSG_CUSTOM_BAN)
|
||||
M_StartMessage(va(M_GetText("You have been banned\n(%s)\nPress (B)\n"), reason), NULL, MM_NOTHING);
|
||||
else if (msg == KICK_MSG_SIGFAIL)
|
||||
M_StartMessage(M_GetText("Server closed connection\n(Invalid signature)\nPress (B)\n"), NULL, MM_NOTHING);
|
||||
else
|
||||
M_StartMessage(M_GetText("You have been kicked by the server\n\nPress (B)\n"), NULL, MM_NOTHING);
|
||||
}
|
||||
|
|
@ -3390,9 +3581,7 @@ void D_ClientServerInit(void)
|
|||
COM_AddCommand("drop", Command_Drop);
|
||||
COM_AddCommand("droprate", Command_Droprate);
|
||||
#endif
|
||||
#ifdef _DEBUG
|
||||
COM_AddCommand("numnodes", Command_Numnodes);
|
||||
#endif
|
||||
|
||||
RegisterNetXCmd(XD_KICK, Got_KickCmd);
|
||||
RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer);
|
||||
|
|
@ -3417,6 +3606,7 @@ static void ResetNode(INT32 node)
|
|||
{
|
||||
nodeingame[node] = false;
|
||||
nodewaiting[node] = 0;
|
||||
nodeneedsauth[node] = false;
|
||||
|
||||
nettics[node] = gametic;
|
||||
supposedtics[node] = gametic;
|
||||
|
|
@ -3497,6 +3687,8 @@ void SV_ResetServer(void)
|
|||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||
netUnlocked[i] = (dedicated || gamedata->unlocked[i]);
|
||||
|
||||
expectChallenge = false;
|
||||
|
||||
DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
|
||||
}
|
||||
|
||||
|
|
@ -3581,6 +3773,8 @@ static inline void SV_AddNode(INT32 node)
|
|||
// nodeingame when connected not here
|
||||
if (node)
|
||||
nodeingame[node] = true;
|
||||
|
||||
nodeneedsauth[node] = false;
|
||||
}
|
||||
|
||||
// Xcmd XD_ADDPLAYER
|
||||
|
|
@ -3622,6 +3816,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
|||
newplayer->jointime = 0;
|
||||
|
||||
READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
|
||||
READMEM(*p, players[newplayernum].public_key, PUBKEYLENGTH);
|
||||
|
||||
console = READUINT8(*p);
|
||||
splitscreenplayer = READUINT8(*p);
|
||||
|
|
@ -3781,10 +3976,12 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
LUA_HookInt(newplayernum, HOOK(PlayerJoin));
|
||||
}
|
||||
|
||||
static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities, const char *name, const char *name2, const char *name3, const char *name4)
|
||||
static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities,
|
||||
const char *name, uint8_t *key, const char *name2, uint8_t *key2,
|
||||
const char *name3, uint8_t *key3, const char *name4, uint8_t *key4)
|
||||
{
|
||||
INT32 n, newplayernum, i;
|
||||
UINT8 buf[4 + MAXPLAYERNAME + MAXAVAILABILITY];
|
||||
UINT8 buf[4 + MAXPLAYERNAME + PUBKEYLENGTH + MAXAVAILABILITY];
|
||||
UINT8 *buf_p = buf;
|
||||
boolean newplayer = false;
|
||||
|
||||
|
|
@ -3847,21 +4044,25 @@ static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities, const cha
|
|||
{
|
||||
nodetoplayer[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name, MAXPLAYERNAME);
|
||||
WRITEMEM(buf_p, key, PUBKEYLENGTH);
|
||||
}
|
||||
else if (playerpernode[node] < 2)
|
||||
{
|
||||
nodetoplayer2[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name2, MAXPLAYERNAME);
|
||||
WRITEMEM(buf_p, key2, PUBKEYLENGTH);
|
||||
}
|
||||
else if (playerpernode[node] < 3)
|
||||
{
|
||||
nodetoplayer3[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name3, MAXPLAYERNAME);
|
||||
WRITEMEM(buf_p, key3, PUBKEYLENGTH);
|
||||
}
|
||||
else if (playerpernode[node] < 4)
|
||||
{
|
||||
nodetoplayer4[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name4, MAXPLAYERNAME);
|
||||
WRITEMEM(buf_p, key4, PUBKEYLENGTH);
|
||||
}
|
||||
|
||||
WRITEUINT8(buf_p, nodetoplayer[node]); // consoleplayer
|
||||
|
|
@ -3896,6 +4097,15 @@ void CL_RemoveSplitscreenPlayer(UINT8 p)
|
|||
SendKick(p, KICK_MSG_PLAYER_QUIT);
|
||||
}
|
||||
|
||||
static void GotOurIP(UINT32 address)
|
||||
{
|
||||
const unsigned char * p = (const unsigned char *)&address;
|
||||
#ifdef DEVELOP
|
||||
CONS_Printf("Got IP of %u.%u.%u.%u\n", p[0], p[1], p[2], p[3]);
|
||||
#endif
|
||||
ourIP = address;
|
||||
}
|
||||
|
||||
// is there a game running
|
||||
boolean Playing(void)
|
||||
{
|
||||
|
|
@ -3932,13 +4142,18 @@ boolean SV_SpawnServer(void)
|
|||
else doomcom->numslots = 1;
|
||||
}
|
||||
|
||||
ourIP = 0;
|
||||
if (netgame && server)
|
||||
STUN_bind(GotOurIP);
|
||||
|
||||
// strictly speaking, i'm not convinced the following is necessary
|
||||
// but I'm not confident enough to remove it entirely in case it breaks something
|
||||
{
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false, false);
|
||||
SINT8 node = 0;
|
||||
for (; node < MAXNETNODES; node++)
|
||||
result |= SV_AddWaitingPlayers(node, availabilitiesbuffer, cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring);
|
||||
result |= SV_AddWaitingPlayers(node, availabilitiesbuffer, cv_playername[0].zstring, PR_GetLocalPlayerProfile(0)->public_key, cv_playername[1].zstring, PR_GetLocalPlayerProfile(1)->public_key,
|
||||
cv_playername[2].zstring, PR_GetLocalPlayerProfile(2)->public_key, cv_playername[3].zstring, PR_GetLocalPlayerProfile(3)->public_key);
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
|
|
@ -4012,6 +4227,34 @@ static size_t TotalTextCmdPerTic(tic_t tic)
|
|||
return total;
|
||||
}
|
||||
|
||||
#ifdef SIGNGAMETRAFFIC
|
||||
static boolean IsSplitPlayerOnNodeGuest(int node, int split)
|
||||
{
|
||||
char allZero[PUBKEYLENGTH];
|
||||
memset(allZero, 0, PUBKEYLENGTH);
|
||||
|
||||
if (split == 0)
|
||||
return (memcmp(players[nodetoplayer[node]].public_key, allZero, PUBKEYLENGTH) == 0);
|
||||
else if (split == 1)
|
||||
return (memcmp(players[nodetoplayer2[node]].public_key, allZero, PUBKEYLENGTH) == 0);
|
||||
else if (split == 2)
|
||||
return (memcmp(players[nodetoplayer3[node]].public_key, allZero, PUBKEYLENGTH) == 0);
|
||||
else if (split == 3)
|
||||
return (memcmp(players[nodetoplayer4[node]].public_key, allZero, PUBKEYLENGTH) == 0);
|
||||
else
|
||||
I_Error("IsSplitPlayerOnNodeGuest: Out of bounds");
|
||||
return false; // unreachable
|
||||
}
|
||||
#endif
|
||||
|
||||
static boolean IsPlayerGuest(int player)
|
||||
{
|
||||
char allZero[PUBKEYLENGTH];
|
||||
memset(allZero, 0, PUBKEYLENGTH);
|
||||
|
||||
return (memcmp(players[player].public_key, allZero, PUBKEYLENGTH) == 0);
|
||||
}
|
||||
|
||||
/** Called when a PT_CLIENTJOIN packet is received
|
||||
*
|
||||
* \param node The packet sender
|
||||
|
|
@ -4029,6 +4272,7 @@ static void HandleConnect(SINT8 node)
|
|||
UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value);
|
||||
UINT8 connectedplayers = 0;
|
||||
|
||||
|
||||
for (i = dedicated ? 1 : 0; i < MAXPLAYERS; i++)
|
||||
if (playernode[i] != UINT8_MAX) // We use this to count players because it is affected by SV_AddWaitingPlayers when more than one client joins on the same tic, unlike playeringame and D_NumPlayers. UINT8_MAX denotes no node for that player
|
||||
connectedplayers++;
|
||||
|
|
@ -4116,6 +4360,7 @@ static void HandleConnect(SINT8 node)
|
|||
}
|
||||
else
|
||||
{
|
||||
int sigcheck;
|
||||
boolean newnode = false;
|
||||
|
||||
for (i = 0; i < netbuffer->u.clientcfg.localplayers - playerpernode[node]; i++)
|
||||
|
|
@ -4126,6 +4371,37 @@ static void HandleConnect(SINT8 node)
|
|||
SV_SendRefuse(node, "Bad player name");
|
||||
return;
|
||||
}
|
||||
|
||||
if (node == 0) // Hey, that's us. We're always allowed to do what we want.
|
||||
{
|
||||
memcpy(lastReceivedKey[node][i], PR_GetLocalPlayerProfile(i)->public_key, sizeof(lastReceivedKey[node][i]));
|
||||
}
|
||||
else // Remote player, gotta check their signature.
|
||||
{
|
||||
char allZero[PUBKEYLENGTH];
|
||||
memset(allZero, 0, sizeof(allZero));
|
||||
|
||||
if (memcmp(lastReceivedKey[node][i], allZero, PUBKEYLENGTH) == 0) // IsSplitPlayerOnNodeGuest isn't appropriate here, they're not in-game yet!
|
||||
{
|
||||
if (!cv_allowguests.value)
|
||||
{
|
||||
SV_SendRefuse(node, M_GetText("The server doesn't allow GUESTs.\nCreate a profile to join!"));
|
||||
return;
|
||||
}
|
||||
|
||||
sigcheck = 0; // Always succeeds. Yes, this is a success response. C R Y P T O
|
||||
}
|
||||
else
|
||||
{
|
||||
sigcheck = crypto_eddsa_check(netbuffer->u.clientcfg.challengeResponse[i], lastReceivedKey[node][i], lastSentChallenge[node], CHALLENGELENGTH);
|
||||
}
|
||||
|
||||
if (netgame && sigcheck != 0)
|
||||
{
|
||||
SV_SendRefuse(node, M_GetText("Signature verification failed."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(availabilitiesbuffer, netbuffer->u.clientcfg.availabilities, sizeof(availabilitiesbuffer));
|
||||
|
|
@ -4165,7 +4441,8 @@ static void HandleConnect(SINT8 node)
|
|||
DEBFILE("send savegame\n");
|
||||
}
|
||||
|
||||
SV_AddWaitingPlayers(node, availabilitiesbuffer, names[0], names[1], names[2], names[3]);
|
||||
SV_AddWaitingPlayers(node, availabilitiesbuffer, names[0], lastReceivedKey[node][0], names[1], lastReceivedKey[node][1],
|
||||
names[2], lastReceivedKey[node][2], names[3], lastReceivedKey[node][3]);
|
||||
joindelay += cv_joindelay.value * TICRATE;
|
||||
player_joining = true;
|
||||
}
|
||||
|
|
@ -4204,6 +4481,23 @@ static void HandleTimeout(SINT8 node)
|
|||
M_StartMessage(M_GetText("Server Timeout\n\nPress (B)\n"), NULL, MM_NOTHING);
|
||||
}
|
||||
|
||||
// Called when a signature check fails and we suspect the server is playing games.
|
||||
void HandleSigfail(const char *string)
|
||||
{
|
||||
if (server) // This situation is basically guaranteed to be nonsense.
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "Auth error! %s\n", string);
|
||||
return; // Keep the game running, you're probably testing.
|
||||
}
|
||||
|
||||
LUA_HookBool(false, HOOK(GameQuit));
|
||||
D_QuitNetGame();
|
||||
CL_Reset();
|
||||
D_ClearState();
|
||||
M_StartControlPanel();
|
||||
M_StartMessage(va(M_GetText("Signature check failed.\n(%s)\nPress (B)\n"), string), NULL, MM_NOTHING);
|
||||
}
|
||||
|
||||
/** Called when a PT_SERVERINFO packet is received
|
||||
*
|
||||
* \param node The packet sender
|
||||
|
|
@ -4236,6 +4530,14 @@ static void PT_WillResendGamestate(void)
|
|||
if (server || cl_redownloadinggamestate)
|
||||
return;
|
||||
|
||||
// Don't let the server pull a fast one with everyone's identity!
|
||||
// Save the public keys we see, so if the server tries to swap one, we'll know.
|
||||
int i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
memcpy(priorKeys[i], players[i].public_key, sizeof(priorKeys[i]));
|
||||
}
|
||||
|
||||
// Send back a PT_CANRECEIVEGAMESTATE packet to the server
|
||||
// so they know they can start sending the game state
|
||||
netbuffer->packettype = PT_CANRECEIVEGAMESTATE;
|
||||
|
|
@ -4490,7 +4792,10 @@ static void HandlePacketFromAwayNode(SINT8 node)
|
|||
case PT_NODETIMEOUT:
|
||||
case PT_CLIENTQUIT:
|
||||
if (server)
|
||||
{
|
||||
Net_CloseConnection(node);
|
||||
nodeneedsauth[node] = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_CLIENTCMD:
|
||||
|
|
@ -4501,7 +4806,28 @@ static void HandlePacketFromAwayNode(SINT8 node)
|
|||
if (node == servernode)
|
||||
break;
|
||||
/* FALLTHRU */
|
||||
case PT_CLIENTKEY:
|
||||
if (server)
|
||||
{
|
||||
PT_ClientKey(node);
|
||||
|
||||
// Client's not in the server yet, but we still need to lock up the node.
|
||||
// Otherwise, someone else could request a challenge on the same node and trash it.
|
||||
nodeneedsauth[node] = true;
|
||||
freezetimeout[node] = I_GetTime() + jointimeout;
|
||||
}
|
||||
break;
|
||||
case PT_SERVERCHALLENGE:
|
||||
if (server && serverrunning && node != servernode)
|
||||
{
|
||||
Net_CloseConnection(node);
|
||||
break;
|
||||
}
|
||||
if (cl_mode != CL_WAITCHALLENGE)
|
||||
break;
|
||||
memcpy(awaitingChallenge, netbuffer->u.serverchallenge.secret, sizeof(awaitingChallenge));
|
||||
cl_mode = CL_ASKJOIN;
|
||||
break;
|
||||
default:
|
||||
DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
|
||||
Net_CloseConnection(node);
|
||||
|
|
@ -4534,6 +4860,19 @@ static boolean CheckForSpeedHacks(UINT8 p)
|
|||
return false;
|
||||
}
|
||||
|
||||
static char NodeToSplitPlayer(int node, int split)
|
||||
{
|
||||
if (split == 0)
|
||||
return nodetoplayer[node];
|
||||
else if (split == 1)
|
||||
return nodetoplayer2[node];
|
||||
else if (split == 2)
|
||||
return nodetoplayer3[node];
|
||||
else if (split == 3)
|
||||
return nodetoplayer4[node];
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Handles a packet received from a node that is in game
|
||||
*
|
||||
* \param node The packet sender
|
||||
|
|
@ -4561,6 +4900,49 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
if (netconsole >= MAXPLAYERS)
|
||||
I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef SIGNGAMETRAFFIC
|
||||
if (server)
|
||||
{
|
||||
|
||||
int splitnodes;
|
||||
if (IsPacketSigned(netbuffer->packettype))
|
||||
{
|
||||
for (splitnodes = 0; splitnodes < MAXSPLITSCREENPLAYERS; splitnodes++)
|
||||
{
|
||||
int targetplayer = NodeToSplitPlayer(node, splitnodes);
|
||||
if (targetplayer == -1)
|
||||
continue;
|
||||
|
||||
const void* message = &netbuffer->u;
|
||||
if (IsSplitPlayerOnNodeGuest(node, splitnodes) || demo.playback)
|
||||
{
|
||||
//CONS_Printf("Throwing out a guest signature from node %d player %d\n", node, splitnodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (crypto_eddsa_check(netbuffer->signature[splitnodes], players[targetplayer].public_key, message, doomcom->datalength - BASEPACKETSIZE))
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "SIGFAIL! Packet type %d from node %d player %d\nkey %s size %d netconsole %d\n",
|
||||
netbuffer->packettype, node, splitnodes,
|
||||
GetPrettyRRID(players[targetplayer].public_key, true), doomcom->datalength - BASEPACKETSIZE, netconsole);
|
||||
|
||||
// Something scary can happen when multiple kicks that resolve to the same node are processed in quick succession.
|
||||
// Sometimes, a kick will still be left to process after the player's been disposed, and that causes the kick to resolve on the server instead!
|
||||
// This sucks, so we check for a stale/misfiring kick beforehand.
|
||||
if (netconsole != -1)
|
||||
SendKick(netconsole, KICK_MSG_SIGFAIL);
|
||||
// Net_CloseConnection(node);
|
||||
// nodeingame[node] = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (netbuffer->packettype)
|
||||
{
|
||||
|
|
@ -4851,6 +5233,7 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
}
|
||||
Net_CloseConnection(node);
|
||||
nodeingame[node] = false;
|
||||
nodeneedsauth[node] = false;
|
||||
break;
|
||||
case PT_CANRECEIVEGAMESTATE:
|
||||
PT_CanReceiveGamestate(node);
|
||||
|
|
@ -4980,6 +5363,140 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
if (client)
|
||||
CL_PrepareDownloadLuaFile();
|
||||
break;
|
||||
case PT_CHALLENGEALL:
|
||||
if (demo.playback || node != servernode) // SERVER should still respond to this to prove its own identity, just not from clients.
|
||||
break;
|
||||
|
||||
int challengeplayers;
|
||||
|
||||
memcpy(lastChallengeAll, netbuffer->u.challengeall.secret, sizeof(lastChallengeAll));
|
||||
|
||||
shouldsign_t safe = ShouldSignChallenge(lastChallengeAll);
|
||||
if (safe != SIGN_OK)
|
||||
{
|
||||
if (safe == SIGN_BADIP)
|
||||
HandleSigfail("External server sent the wrong IP");
|
||||
else if (safe == SIGN_BADTIME)
|
||||
HandleSigfail("Bad timestamp - check your clocks");
|
||||
else
|
||||
HandleSigfail("Unknown auth error - contact a developer");
|
||||
break;
|
||||
}
|
||||
|
||||
netbuffer->packettype = PT_RESPONSEALL;
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_noresponse.value)
|
||||
{
|
||||
CV_AddValue(&cv_noresponse, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_noresponse enabled, not sending PT_RESPONSEALL\n");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Don't leak uninitialized memory.
|
||||
memset(&netbuffer->u.responseall, 0, sizeof(netbuffer->u.responseall));
|
||||
|
||||
for (challengeplayers = 0; challengeplayers <= splitscreen; challengeplayers++)
|
||||
{
|
||||
uint8_t signature[SIGNATURELENGTH];
|
||||
profile_t *localProfile = PR_GetLocalPlayerProfile(challengeplayers);
|
||||
if (!PR_IsLocalPlayerGuest(challengeplayers)) // GUESTS don't have keys
|
||||
{
|
||||
crypto_eddsa_sign(signature, localProfile->secret_key, lastChallengeAll, sizeof(lastChallengeAll));
|
||||
|
||||
// If our keys are garbage (corrupted profile?), fail here instead of when the server boots us, so the player knows what's going on.
|
||||
if (crypto_eddsa_check(signature, localProfile->public_key, lastChallengeAll, sizeof(lastChallengeAll)) != 0)
|
||||
I_Error("Couldn't self-verify key associated with player %d, profile %d.\nProfile data may be corrupted.", challengeplayers, cv_lastprofile[challengeplayers].value);
|
||||
}
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_badresponse.value)
|
||||
{
|
||||
CV_AddValue(&cv_badresponse, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badresponse enabled, scrubbing signature from PT_RESPONSEALL\n");
|
||||
memset(signature, 0, sizeof(signature));
|
||||
}
|
||||
#endif
|
||||
|
||||
memcpy(netbuffer->u.responseall.signature[challengeplayers], signature, sizeof(signature));
|
||||
}
|
||||
|
||||
HSendPacket(servernode, true, 0, sizeof(netbuffer->u.responseall));
|
||||
break;
|
||||
case PT_RESPONSEALL:
|
||||
if (demo.playback || client)
|
||||
break;
|
||||
|
||||
int responseplayer;
|
||||
for (responseplayer = 0; responseplayer < MAXSPLITSCREENPLAYERS; responseplayer++)
|
||||
{
|
||||
int targetplayer = NodeToSplitPlayer(node, responseplayer);
|
||||
if (targetplayer == -1)
|
||||
continue;
|
||||
|
||||
if (!IsPlayerGuest(targetplayer))
|
||||
{
|
||||
if (crypto_eddsa_check(netbuffer->u.responseall.signature[responseplayer], players[targetplayer].public_key, lastChallengeAll, sizeof(lastChallengeAll)))
|
||||
{
|
||||
// Something scary can happen when multiple kicks that resolve to the same node are processed in quick succession.
|
||||
// Sometimes, a kick will still be left to process after the player's been disposed, and that causes the kick to resolve on the server instead!
|
||||
// This sucks, so we check for a stale/misfiring kick beforehand.
|
||||
if (playernode[targetplayer] != 0)
|
||||
SendKick(targetplayer, KICK_MSG_SIGFAIL);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(lastReceivedSignature[targetplayer], netbuffer->u.responseall.signature[responseplayer], sizeof(lastReceivedSignature[targetplayer]));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PT_RESULTSALL:
|
||||
if (demo.playback || server || node != servernode || !expectChallenge)
|
||||
break;
|
||||
|
||||
int resultsplayer;
|
||||
uint8_t allZero[PUBKEYLENGTH];
|
||||
memset(allZero, 0, sizeof(PUBKEYLENGTH));
|
||||
|
||||
for (resultsplayer = 0; resultsplayer < MAXPLAYERS; resultsplayer++)
|
||||
{
|
||||
if (!playeringame[resultsplayer])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (IsPlayerGuest(resultsplayer))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (memcmp(knownWhenChallenged[resultsplayer], allZero, sizeof(PUBKEYLENGTH)) == 0)
|
||||
{
|
||||
// Wasn't here for the challenge.
|
||||
continue;
|
||||
}
|
||||
else if (memcmp(knownWhenChallenged[resultsplayer], players[resultsplayer].public_key, sizeof(knownWhenChallenged[resultsplayer])) != 0)
|
||||
{
|
||||
// A player left after the challenge process started, and someone else took their place.
|
||||
// That means they haven't received a challenge either.
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (crypto_eddsa_check(netbuffer->u.resultsall.signature[resultsplayer],
|
||||
knownWhenChallenged[resultsplayer], lastChallengeAll, sizeof(lastChallengeAll)))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "PT_RESULTSALL had invalid signature %s for node %d player %d split %d, something doesn't add up!\n",
|
||||
GetPrettyRRID(netbuffer->u.resultsall.signature[resultsplayer], true), playernode[resultsplayer], resultsplayer, players[resultsplayer].splitscreenindex);
|
||||
HandleSigfail("Server sent invalid client signature.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
csprng(lastChallengeAll, sizeof(lastChallengeAll));
|
||||
expectChallenge = false;
|
||||
break;
|
||||
default:
|
||||
DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n",
|
||||
netbuffer->packettype, node));
|
||||
|
|
@ -5814,6 +6331,168 @@ static void UpdatePingTable(void)
|
|||
}
|
||||
}
|
||||
|
||||
// It's that time again! Send everyone a safe message to sign, so we can show off their signature and prove we're playing fair.
|
||||
static void SendChallenges(void)
|
||||
{
|
||||
int i;
|
||||
netbuffer->packettype = PT_CHALLENGEALL;
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_nochallenge.value)
|
||||
{
|
||||
CV_AddValue(&cv_nochallenge, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_nochallenge enabled, not sending PT_CHALLENGEALL\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(knownWhenChallenged, 0, sizeof(knownWhenChallenged));
|
||||
memset(lastReceivedSignature, 0, sizeof(lastReceivedSignature));
|
||||
|
||||
GenerateChallenge(netbuffer->u.challengeall.secret);
|
||||
memcpy(lastChallengeAll, netbuffer->u.challengeall.secret, sizeof(lastChallengeAll));
|
||||
|
||||
// Take note of everyone's current key, so that players who disconnect and are replaced aren't held to the old player's challenge.
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i])
|
||||
memcpy(knownWhenChallenged[i], players[i].public_key, sizeof(knownWhenChallenged[i]));
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXNETNODES; i++)
|
||||
{
|
||||
if (nodeingame[i])
|
||||
HSendPacket(i, true, 0, sizeof(challengeall_pak));
|
||||
}
|
||||
}
|
||||
|
||||
// Before we start sending out the results, we need to kick everyone who didn't respond.
|
||||
// (If we try to do both at once, clients will still see players who failled in-game when the results arrive...)
|
||||
static void KickUnverifiedPlayers(void)
|
||||
{
|
||||
int i;
|
||||
uint8_t allZero[SIGNATURELENGTH];
|
||||
memset(allZero, 0, SIGNATURELENGTH);
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
if (memcmp(lastReceivedSignature[i], allZero, SIGNATURELENGTH) == 0) // We never got a response!
|
||||
{
|
||||
if (!IsPlayerGuest(i) && memcmp(&knownWhenChallenged[i], &players[i].public_key, sizeof(knownWhenChallenged[i])) == 0)
|
||||
{
|
||||
if (playernode[i] != servernode)
|
||||
SendKick(i, KICK_MSG_SIGFAIL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
static void SendChallengeResults(void)
|
||||
{
|
||||
int i;
|
||||
netbuffer->packettype = PT_RESULTSALL;
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_noresults.value)
|
||||
{
|
||||
CV_AddValue(&cv_noresults, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_noresults enabled, not sending PT_RESULTSALL\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t allZero[SIGNATURELENGTH];
|
||||
memset(allZero, 0, sizeof(allZero));
|
||||
|
||||
memset(&netbuffer->u.resultsall, 0, sizeof(netbuffer->u.resultsall));
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
// Don't try to transmit signatures for players who didn't get here in time to send one.
|
||||
// (Everyone who had their chance should have been kicked by KickUnverifiedPlayers by now.)
|
||||
if (memcmp(lastReceivedSignature[i], allZero, SIGNATURELENGTH) == 0)
|
||||
continue;
|
||||
|
||||
memcpy(netbuffer->u.resultsall.signature[i], lastReceivedSignature[i], sizeof(netbuffer->u.resultsall.signature[i]));
|
||||
#ifdef DEVELOP
|
||||
if (cv_badresults.value)
|
||||
{
|
||||
CV_AddValue(&cv_badresults, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badresults enabled, scrubbing signature from PT_RESULTSALL\n");
|
||||
memset(netbuffer->u.resultsall.signature[i], 0, sizeof(netbuffer->u.resultsall.signature[i]));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXNETNODES; i++)
|
||||
{
|
||||
if (nodeingame[i])
|
||||
HSendPacket(i, true, 0, sizeof(resultsall_pak));
|
||||
}
|
||||
}
|
||||
|
||||
// Who should we try to verify when results come in?
|
||||
// Store a public key for every active slot, so if players shuffle during challenge leniency,
|
||||
// we don't incorrectly try to verify someone who didn't even get a challenge, throw a tantrum, and bail.
|
||||
static void CheckPresentPlayers(void)
|
||||
{
|
||||
int i;
|
||||
memset(knownWhenChallenged, 0, sizeof(knownWhenChallenged));
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (IsPlayerGuest(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(knownWhenChallenged[i], players[i].public_key, sizeof(knownWhenChallenged[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle "client-to-client" auth challenge flow.
|
||||
static void UpdateChallenges(void)
|
||||
{
|
||||
if (!(Playing() && netgame))
|
||||
return;
|
||||
|
||||
if (server)
|
||||
{
|
||||
if (leveltime == CHALLENGEALL_START)
|
||||
SendChallenges();
|
||||
|
||||
if (leveltime == CHALLENGEALL_KICKUNRESPONSIVE)
|
||||
KickUnverifiedPlayers();
|
||||
|
||||
if (leveltime == CHALLENGEALL_SENDRESULTS)
|
||||
SendChallengeResults();
|
||||
|
||||
}
|
||||
else // client
|
||||
{
|
||||
if (leveltime <= CHALLENGEALL_START)
|
||||
expectChallenge = true;
|
||||
|
||||
if (leveltime == CHALLENGEALL_START)
|
||||
CheckPresentPlayers();
|
||||
|
||||
if (leveltime > CHALLENGEALL_CLIENTCUTOFF && expectChallenge)
|
||||
HandleSigfail("Didn't receive client signatures.");
|
||||
}
|
||||
}
|
||||
|
||||
static void RenewHolePunch(void)
|
||||
{
|
||||
static time_t past;
|
||||
|
|
@ -5835,7 +6514,7 @@ static void HandleNodeTimeouts(void)
|
|||
if (server)
|
||||
{
|
||||
for (i = 1; i < MAXNETNODES; i++)
|
||||
if (nodeingame[i] && freezetimeout[i] < I_GetTime())
|
||||
if ((nodeingame[i] || nodeneedsauth[i]) && freezetimeout[i] < I_GetTime())
|
||||
Net_ConnectionTimeout(i);
|
||||
|
||||
// In case the cvar value was lowered
|
||||
|
|
@ -5965,6 +6644,8 @@ void NetUpdate(void)
|
|||
|
||||
UpdatePingTable();
|
||||
|
||||
UpdateChallenges();
|
||||
|
||||
if (client)
|
||||
maketic = neededtic;
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,11 @@ applications may follow different packet versions.
|
|||
// This just works as a quick implementation.
|
||||
#define MAXGENTLEMENDELAY TICRATE
|
||||
|
||||
#define PUBKEYLENGTH 32 // Enforced by Monocypher EdDSA
|
||||
#define PRIVKEYLENGTH 64 // Enforced by Monocypher EdDSA
|
||||
#define SIGNATURELENGTH 64 // Enforced by Monocypher EdDSA
|
||||
#define CHALLENGELENGTH 64 // Servers verify client identity by giving them messages to sign. How long are these messages?
|
||||
|
||||
//
|
||||
// Packet structure
|
||||
//
|
||||
|
|
@ -119,16 +124,29 @@ typedef enum
|
|||
PT_LOGIN, // Login attempt from the client.
|
||||
|
||||
PT_PING, // Packet sent to tell clients the other client's latency to server.
|
||||
|
||||
PT_CLIENTKEY, // "Here's my public key"
|
||||
PT_SERVERCHALLENGE, // "Prove it"
|
||||
|
||||
PT_CHALLENGEALL, // Prove to the other clients you are who you say you are, sign this random bullshit!
|
||||
PT_RESPONSEALL, // OK, here is my signature on that random bullshit
|
||||
PT_RESULTSALL, // Here's what everyone responded to PT_CHALLENGEALL with, if this is wrong or you don't receive it disconnect
|
||||
|
||||
NUMPACKETTYPE
|
||||
} packettype_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SIGN_OK,
|
||||
SIGN_BADTIME, // Timestamp differs by too much, suspect reuse of an old challenge.
|
||||
SIGN_BADIP // Asked to sign the wrong IP by an external host, suspect reuse of another server's challenge.
|
||||
} shouldsign_t;
|
||||
|
||||
#ifdef PACKETDROP
|
||||
void Command_Drop(void);
|
||||
void Command_Droprate(void);
|
||||
#endif
|
||||
#ifdef _DEBUG
|
||||
void Command_Numnodes(void);
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma pack(1)
|
||||
|
|
@ -252,6 +270,7 @@ struct clientconfig_pak
|
|||
UINT8 mode;
|
||||
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME];
|
||||
UINT8 availabilities[MAXAVAILABILITY];
|
||||
uint8_t challengeResponse[MAXSPLITSCREENPLAYERS][SIGNATURELENGTH];
|
||||
} ATTRPACK;
|
||||
|
||||
#define SV_SPEEDMASK 0x03 // used to send kartspeed
|
||||
|
|
@ -346,6 +365,31 @@ struct filesneededconfig_pak
|
|||
UINT8 files[MAXFILENEEDED]; // is filled with writexxx (byteptr.h)
|
||||
} ATTRPACK;
|
||||
|
||||
struct clientkey_pak
|
||||
{
|
||||
uint8_t key[MAXSPLITSCREENPLAYERS][PUBKEYLENGTH];
|
||||
} ATTRPACK;
|
||||
|
||||
struct serverchallenge_pak
|
||||
{
|
||||
uint8_t secret[CHALLENGELENGTH];
|
||||
} ATTRPACK;
|
||||
|
||||
struct challengeall_pak
|
||||
{
|
||||
uint8_t secret[CHALLENGELENGTH];
|
||||
} ATTRPACK;
|
||||
|
||||
struct responseall_pak
|
||||
{
|
||||
uint8_t signature[MAXSPLITSCREENPLAYERS][SIGNATURELENGTH];
|
||||
} ATTRPACK;
|
||||
|
||||
struct resultsall_pak
|
||||
{
|
||||
uint8_t signature[MAXPLAYERS][SIGNATURELENGTH];
|
||||
} ATTRPACK;
|
||||
|
||||
//
|
||||
// Network packet data
|
||||
//
|
||||
|
|
@ -356,6 +400,9 @@ struct doomdata_t
|
|||
UINT8 ackreturn; // The return of the ack number
|
||||
|
||||
UINT8 packettype;
|
||||
#ifdef SIGNGAMETRAFFIC
|
||||
uint8_t signature[MAXSPLITSCREENPLAYERS][SIGNATURELENGTH];
|
||||
#endif
|
||||
UINT8 reserved; // Padding
|
||||
union
|
||||
{
|
||||
|
|
@ -380,6 +427,11 @@ struct doomdata_t
|
|||
INT32 filesneedednum; // 4 bytes
|
||||
filesneededconfig_pak filesneededcfg; // ??? bytes
|
||||
UINT32 pingtable[MAXPLAYERS+1]; // 68 bytes
|
||||
clientkey_pak clientkey; // 32 bytes
|
||||
serverchallenge_pak serverchallenge; // 256 bytes
|
||||
challengeall_pak challengeall; // 256 bytes
|
||||
responseall_pak responseall; // 256 bytes
|
||||
resultsall_pak resultsall; // 1024 bytes. Also, you really shouldn't trust anything here.
|
||||
} u; // This is needed to pack diff packet types data together
|
||||
} ATTRPACK;
|
||||
|
||||
|
|
@ -419,6 +471,7 @@ extern consvar_t cv_playbackspeed;
|
|||
#define KICK_MSG_PING_HIGH 6
|
||||
#define KICK_MSG_CUSTOM_KICK 7
|
||||
#define KICK_MSG_CUSTOM_BAN 8
|
||||
#define KICK_MSG_SIGFAIL 9
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
|
@ -443,6 +496,20 @@ extern UINT16 software_MAXPACKETLENGTH;
|
|||
extern boolean acceptnewnode;
|
||||
extern SINT8 servernode;
|
||||
extern char connectedservername[MAXSERVERNAME];
|
||||
extern UINT32 ourIP;
|
||||
extern uint8_t lastReceivedKey[MAXNETNODES][MAXSPLITSCREENPLAYERS][PUBKEYLENGTH];
|
||||
extern uint8_t lastSentChallenge[MAXNETNODES][CHALLENGELENGTH];
|
||||
extern uint8_t lastChallengeAll[CHALLENGELENGTH];
|
||||
extern uint8_t lastReceivedSignature[MAXPLAYERS][SIGNATURELENGTH];
|
||||
extern uint8_t knownWhenChallenged[MAXPLAYERS][PUBKEYLENGTH];
|
||||
extern boolean expectChallenge;
|
||||
|
||||
// We give clients a chance to verify each other once per race.
|
||||
// When is that challenge sent, and when should clients bail if they don't receive the responses?
|
||||
#define CHALLENGEALL_START (TICRATE*5) // Server sends challenges here.
|
||||
#define CHALLENGEALL_KICKUNRESPONSIVE (TICRATE*10) // Server kicks players that haven't submitted signatures here.
|
||||
#define CHALLENGEALL_SENDRESULTS (TICRATE*15) // Server waits for kicks to process until here. (Failing players shouldn't be in-game when results are received, or clients get spooked.)
|
||||
#define CHALLENGEALL_CLIENTCUTOFF (TICRATE*20) // If challenge process hasn't completed by now, clients who were in-game for CHALLENGEALL_START should leave.
|
||||
|
||||
void Command_Ping_f(void);
|
||||
extern tic_t connectiontimeout;
|
||||
|
|
@ -465,10 +532,27 @@ extern consvar_t cv_joinnextround;
|
|||
|
||||
extern consvar_t cv_discordinvites;
|
||||
|
||||
extern consvar_t cv_allowguests;
|
||||
|
||||
#ifdef DEVELOP
|
||||
extern consvar_t cv_badjoin;
|
||||
extern consvar_t cv_badtraffic;
|
||||
extern consvar_t cv_badresponse;
|
||||
extern consvar_t cv_noresponse;
|
||||
extern consvar_t cv_nochallenge;
|
||||
extern consvar_t cv_badresults;
|
||||
extern consvar_t cv_noresults;
|
||||
extern consvar_t cv_badtime;
|
||||
extern consvar_t cv_badip;
|
||||
#endif
|
||||
|
||||
// Used in d_net, the only dependence
|
||||
tic_t ExpandTics(INT32 low, tic_t basetic);
|
||||
void D_ClientServerInit(void);
|
||||
|
||||
void GenerateChallenge(uint8_t *buf);
|
||||
shouldsign_t ShouldSignChallenge(uint8_t *message);
|
||||
|
||||
// Initialise the other field
|
||||
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum));
|
||||
void SendNetXCmdForPlayer(UINT8 playerid, netxcmd_t id, const void *param, size_t nparam);
|
||||
|
|
@ -549,6 +633,8 @@ void CL_ClearRewinds(void);
|
|||
rewind_t *CL_SaveRewindPoint(size_t demopos);
|
||||
rewind_t *CL_RewindToTime(tic_t time);
|
||||
|
||||
void HandleSigfail(const char *string);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -71,6 +71,9 @@
|
|||
#include "g_input.h" // tutorial mode control scheming
|
||||
#include "m_perfstats.h"
|
||||
|
||||
#include "monocypher/monocypher.h"
|
||||
#include "stun.h"
|
||||
|
||||
// SRB2Kart
|
||||
#include "k_grandprix.h"
|
||||
#include "doomstat.h"
|
||||
|
|
|
|||
81
src/d_net.c
81
src/d_net.c
|
|
@ -29,6 +29,8 @@
|
|||
#include "z_zone.h"
|
||||
#include "i_tcp.h"
|
||||
#include "d_main.h" // srb2home
|
||||
#include "stun.h"
|
||||
#include "monocypher/monocypher.h"
|
||||
|
||||
//
|
||||
// NETWORKING
|
||||
|
|
@ -81,6 +83,7 @@ boolean (*I_NetOpenSocket)(void) = NULL;
|
|||
boolean (*I_Ban) (INT32 node) = NULL;
|
||||
void (*I_ClearBans)(void) = NULL;
|
||||
const char *(*I_GetNodeAddress) (INT32 node) = NULL;
|
||||
UINT32 (*I_GetNodeAddressInt) (INT32 node) = NULL;
|
||||
const char *(*I_GetBanAddress) (size_t ban) = NULL;
|
||||
const char *(*I_GetBanMask) (size_t ban) = NULL;
|
||||
const char *(*I_GetBanUsername) (size_t ban) = NULL;
|
||||
|
|
@ -90,6 +93,7 @@ boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL;
|
|||
boolean (*I_SetBanUsername) (const char *username) = NULL;
|
||||
boolean (*I_SetBanReason) (const char *reason) = NULL;
|
||||
boolean (*I_SetUnbanTime) (time_t timestamp) = NULL;
|
||||
boolean (*I_IsExternalAddress) (const void *p) = NULL;
|
||||
bannednode_t *bannednode = NULL;
|
||||
|
||||
|
||||
|
|
@ -629,7 +633,12 @@ static void InitAck(void)
|
|||
ackpak[i].acknum = 0;
|
||||
|
||||
for (i = 0; i < MAXNETNODES; i++)
|
||||
{
|
||||
InitNode(&nodes[i]);
|
||||
|
||||
csprng(lastSentChallenge[i], sizeof(lastSentChallenge[i]));
|
||||
csprng(lastReceivedKey[i], sizeof(lastReceivedKey[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes all acks of a given packet type
|
||||
|
|
@ -699,6 +708,9 @@ void Net_CloseConnection(INT32 node)
|
|||
if (server)
|
||||
SV_AbortLuaFileTransfer(node);
|
||||
I_NetFreeNodenum(node);
|
||||
|
||||
csprng(lastSentChallenge[node], sizeof(lastSentChallenge[node]));
|
||||
csprng(lastReceivedKey[node], sizeof(lastReceivedKey[node]));
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -803,7 +815,14 @@ static const char *packettypename[NUMPACKETTYPE] =
|
|||
|
||||
"LOGIN",
|
||||
|
||||
"PING"
|
||||
"PING",
|
||||
|
||||
"CLIENTKEY",
|
||||
"SERVERCHALLENGE",
|
||||
|
||||
"CHALLENGEALL",
|
||||
"RESPONSEALL",
|
||||
"RESULTSALL"
|
||||
};
|
||||
|
||||
static void DebugPrintpacket(const char *header)
|
||||
|
|
@ -983,12 +1002,72 @@ static boolean ShouldDropPacket(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
// Unused because Eidolon correctly pointed out that +512b on every packet was scary.
|
||||
#ifdef SIGNGAMETRAFFIC
|
||||
boolean IsPacketSigned(int packettype)
|
||||
{
|
||||
switch (packettype)
|
||||
{
|
||||
case PT_CLIENTCMD:
|
||||
case PT_CLIENT2CMD:
|
||||
case PT_CLIENT3CMD:
|
||||
case PT_CLIENT4CMD:
|
||||
case PT_CLIENTMIS:
|
||||
case PT_CLIENT2MIS:
|
||||
case PT_CLIENT3MIS:
|
||||
case PT_CLIENT4MIS:
|
||||
case PT_TEXTCMD:
|
||||
case PT_TEXTCMD2:
|
||||
case PT_TEXTCMD3:
|
||||
case PT_TEXTCMD4:
|
||||
case PT_LOGIN:
|
||||
case PT_ASKLUAFILE:
|
||||
case PT_SENDINGLUAFILE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// HSendPacket
|
||||
//
|
||||
boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength)
|
||||
{
|
||||
doomcom->datalength = (INT16)(packetlength + BASEPACKETSIZE);
|
||||
|
||||
#ifdef SIGNGAMETRAFFIC
|
||||
if (IsPacketSigned(netbuffer->packettype))
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
{
|
||||
const void* message = &netbuffer->u;
|
||||
//CONS_Printf("Signing packet type %d of length %d\n", netbuffer->packettype, packetlength);
|
||||
if (PR_IsLocalPlayerGuest(i))
|
||||
memset(netbuffer->signature[i], 0, sizeof(netbuffer->signature[i]));
|
||||
else
|
||||
crypto_eddsa_sign(netbuffer->signature[i], PR_GetLocalPlayerProfile(i)->secret_key, message, packetlength);
|
||||
}
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_badtraffic.value)
|
||||
{
|
||||
CV_AddValue(&cv_badtraffic, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badtraffic enabled, scrubbing signature from HSendPacket\n");
|
||||
memset(netbuffer->signature, 0, sizeof(netbuffer->signature));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
//CONS_Printf("NOT signing PT_%d of length %d, it doesn't need to be\n", netbuffer->packettype, packetlength);
|
||||
memset(netbuffer->signature, 0, sizeof(netbuffer->signature));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (node == 0) // Packet is to go back to us
|
||||
{
|
||||
if ((rebound_head+1) % MAXREBOUND == rebound_tail)
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ extern SINT8 nodetoplayer3[MAXNETNODES]; // Say the numplayer for this node if a
|
|||
extern SINT8 nodetoplayer4[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen == 3)
|
||||
extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen
|
||||
extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game
|
||||
extern boolean nodeneedsauth[MAXNETNODES];
|
||||
|
||||
extern boolean serverrunning;
|
||||
|
||||
|
|
@ -68,6 +69,8 @@ void Net_AbortPacketType(UINT8 packettype);
|
|||
void Net_SendAcks(INT32 node);
|
||||
void Net_WaitAllAckReceived(UINT32 timeout);
|
||||
|
||||
boolean IsPacketSigned(int packettype);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -946,6 +946,20 @@ void D_RegisterClientCommands(void)
|
|||
CV_RegisterVar(&cv_netticbuffer);
|
||||
CV_RegisterVar(&cv_mindelay);
|
||||
|
||||
CV_RegisterVar(&cv_allowguests);
|
||||
|
||||
#ifdef DEVELOP
|
||||
CV_RegisterVar(&cv_badjoin);
|
||||
CV_RegisterVar(&cv_badtraffic);
|
||||
CV_RegisterVar(&cv_badresponse);
|
||||
CV_RegisterVar(&cv_noresponse);
|
||||
CV_RegisterVar(&cv_nochallenge);
|
||||
CV_RegisterVar(&cv_badresults);
|
||||
CV_RegisterVar(&cv_noresults);
|
||||
CV_RegisterVar(&cv_badtime);
|
||||
CV_RegisterVar(&cv_badip);
|
||||
#endif
|
||||
|
||||
// HUD
|
||||
CV_RegisterVar(&cv_alttitle);
|
||||
CV_RegisterVar(&cv_itemfinder);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
#include "k_menu.h"
|
||||
#include "md5.h"
|
||||
#include "filesrch.h"
|
||||
#include "stun.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
|
|
@ -1313,6 +1314,22 @@ void PT_FileReceived(void)
|
|||
SV_EndFileSend(doomcom->remotenode);
|
||||
}
|
||||
|
||||
// Someone knocked on the door with their public key.
|
||||
// Give them a challenge to sign in their PT_CLIENTJOIN.
|
||||
void PT_ClientKey(INT32 node)
|
||||
{
|
||||
clientkey_pak *packet = (void*)&netbuffer->u.clientkey;
|
||||
|
||||
memcpy(lastReceivedKey[node], packet->key, sizeof(lastReceivedKey[node]));
|
||||
|
||||
netbuffer->packettype = PT_SERVERCHALLENGE;
|
||||
|
||||
GenerateChallenge(lastSentChallenge[node]);
|
||||
|
||||
memcpy(&netbuffer->u.serverchallenge, lastSentChallenge[node], sizeof(serverchallenge_pak));
|
||||
HSendPacket(node, false, 0, sizeof (serverchallenge_pak));
|
||||
}
|
||||
|
||||
static void SendAckPacket(fileack_pak *packet, UINT8 fileid)
|
||||
{
|
||||
size_t packetsize;
|
||||
|
|
|
|||
|
|
@ -106,6 +106,8 @@ boolean CL_CheckDownloadable(void);
|
|||
boolean CL_SendFileRequest(void);
|
||||
boolean PT_RequestFile(INT32 node);
|
||||
|
||||
void PT_ClientKey(INT32 node);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LFTNS_NONE, // This node is not connected
|
||||
|
|
|
|||
|
|
@ -713,6 +713,8 @@ struct player_t
|
|||
mobj_t *stumbleIndicator;
|
||||
mobj_t *sliptideZipIndicator;
|
||||
|
||||
uint8_t public_key[PUBKEYLENGTH];
|
||||
|
||||
#ifdef HWRENDER
|
||||
fixed_t fovadd; // adjust FOV for hw rendering
|
||||
#endif
|
||||
|
|
@ -721,6 +723,9 @@ struct player_t
|
|||
roundconditions_t roundconditions;
|
||||
};
|
||||
|
||||
// WARNING FOR ANYONE ABOUT TO ADD SOMETHING TO THE PLAYER STRUCT, G_PlayerReborn WANTS YOU TO SUFFER
|
||||
// If data on player_t needs to persist between rounds or during the join process, modify G_PlayerReborn to preserve it.
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2436,6 +2436,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
|
||||
SINT8 xtralife;
|
||||
|
||||
uint8_t public_key[PUBKEYLENGTH];
|
||||
|
||||
// SRB2kart
|
||||
itemroulette_t itemRoulette;
|
||||
respawnvars_t respawn;
|
||||
|
|
@ -2509,6 +2511,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
// SRB2kart
|
||||
memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette));
|
||||
memcpy(&respawn, &players[player].respawn, sizeof (respawn));
|
||||
memcpy(&public_key, &players[player].public_key, sizeof(public_key));
|
||||
|
||||
if (betweenmaps || leveltime < introtime)
|
||||
{
|
||||
|
|
@ -2678,6 +2681,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
memcpy(&p->itemRoulette, &itemRoulette, sizeof (p->itemRoulette));
|
||||
memcpy(&p->respawn, &respawn, sizeof (p->respawn));
|
||||
|
||||
memcpy(&p->public_key, &public_key, sizeof(p->public_key));
|
||||
|
||||
if (saveroundconditions)
|
||||
memcpy(&p->roundconditions, &roundconditions, sizeof (p->roundconditions));
|
||||
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ extern void (*I_NetRegisterHolePunch)(void);
|
|||
extern boolean (*I_Ban) (INT32 node);
|
||||
extern void (*I_ClearBans)(void);
|
||||
extern const char *(*I_GetNodeAddress) (INT32 node);
|
||||
extern UINT32 (*I_GetNodeAddressInt) (INT32 node);
|
||||
extern const char *(*I_GetBanAddress) (size_t ban);
|
||||
extern const char *(*I_GetBanMask) (size_t ban);
|
||||
extern const char *(*I_GetBanUsername) (size_t ban);
|
||||
|
|
@ -175,6 +176,7 @@ extern boolean (*I_SetBanAddress) (const char *address,const char *mask);
|
|||
extern boolean (*I_SetBanUsername) (const char *username);
|
||||
extern boolean (*I_SetBanReason) (const char *reason);
|
||||
extern boolean (*I_SetUnbanTime) (time_t timestamp);
|
||||
extern boolean (*I_IsExternalAddress) (const void *p);
|
||||
|
||||
struct bannednode_t
|
||||
{
|
||||
|
|
|
|||
46
src/i_tcp.c
46
src/i_tcp.c
|
|
@ -387,6 +387,20 @@ static const char *SOCK_GetNodeAddress(INT32 node)
|
|||
return SOCK_AddrToStr(&clientaddress[node]);
|
||||
}
|
||||
|
||||
static UINT32 SOCK_GetNodeAddressInt(INT32 node)
|
||||
{
|
||||
if (nodeconnected[node] && clientaddress[node].any.sa_family == AF_INET)
|
||||
{
|
||||
return clientaddress[node].ip4.sin_addr.s_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
I_Error("SOCK_GetNodeAddressInt: Node %d is not IPv4!\n", node);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *SOCK_GetBanAddress(size_t ban)
|
||||
{
|
||||
if (ban >= numbans)
|
||||
|
|
@ -459,7 +473,7 @@ static void cleanupnodes(void)
|
|||
|
||||
// Why can't I start at zero?
|
||||
for (j = 1; j < MAXNETNODES; j++)
|
||||
if (!(nodeingame[j] || SendingFile(j)))
|
||||
if (!(nodeingame[j] || nodeneedsauth[j] || SendingFile(j)))
|
||||
nodeconnected[j] = false;
|
||||
}
|
||||
|
||||
|
|
@ -489,7 +503,6 @@ static SINT8 getfreenode(void)
|
|||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
void Command_Numnodes(void)
|
||||
{
|
||||
INT32 connected = 0;
|
||||
|
|
@ -527,7 +540,6 @@ void Command_Numnodes(void)
|
|||
"Ingame: %d\n",
|
||||
connected, ingame);
|
||||
}
|
||||
#endif
|
||||
|
||||
static boolean hole_punch(ssize_t c)
|
||||
{
|
||||
|
|
@ -1510,6 +1522,31 @@ static void SOCK_ClearBans(void)
|
|||
banned = NULL;
|
||||
}
|
||||
|
||||
// https://github.com/jameds/holepunch/blob/master/holepunch.c#L75
|
||||
static int SOCK_IsExternalAddress (const void *p)
|
||||
{
|
||||
const int a = ((const unsigned char*)p)[0];
|
||||
const int b = ((const unsigned char*)p)[1];
|
||||
|
||||
if (*(const UINT32*)p == (UINT32)~0)/* 255.255.255.255 */
|
||||
return 0;
|
||||
|
||||
switch (a)
|
||||
{
|
||||
case 0:
|
||||
case 10:
|
||||
case 127:
|
||||
return 0;
|
||||
case 172:
|
||||
return (b & ~15) != 16;/* 16 - 31 */
|
||||
case 192:
|
||||
return b != 168;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean I_InitTcpNetwork(void)
|
||||
{
|
||||
char serverhostname[255];
|
||||
|
|
@ -1600,6 +1637,7 @@ boolean I_InitTcpNetwork(void)
|
|||
I_Ban = SOCK_Ban;
|
||||
I_ClearBans = SOCK_ClearBans;
|
||||
I_GetNodeAddress = SOCK_GetNodeAddress;
|
||||
I_GetNodeAddressInt = SOCK_GetNodeAddressInt;
|
||||
I_GetBanAddress = SOCK_GetBanAddress;
|
||||
I_GetBanMask = SOCK_GetBanMask;
|
||||
I_GetBanUsername = SOCK_GetBanUsername;
|
||||
|
|
@ -1609,6 +1647,8 @@ boolean I_InitTcpNetwork(void)
|
|||
I_SetBanUsername = SOCK_SetBanUsername;
|
||||
I_SetBanReason = SOCK_SetBanReason;
|
||||
I_SetUnbanTime = SOCK_SetUnbanTime;
|
||||
I_IsExternalAddress = SOCK_IsExternalAddress;
|
||||
|
||||
bannednode = SOCK_bannednode;
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -1768,7 +1768,8 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p)
|
|||
if (p != NULL)
|
||||
{
|
||||
V_DrawProfileNum(x + 37 + 10, y + 131, 0, PR_GetProfileNum(p));
|
||||
V_DrawCenteredThinString(x, y + 151, V_GRAYMAP|V_6WIDTHSPACE, p->playername);
|
||||
V_DrawCenteredThinString(x, y + 141, V_GRAYMAP|V_6WIDTHSPACE, p->playername);
|
||||
V_DrawCenteredThinString(x, y + 151, V_GRAYMAP|V_6WIDTHSPACE, GetPrettyRRID(p->public_key, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
#include "k_profiles.h"
|
||||
#include "z_zone.h"
|
||||
#include "r_skins.h"
|
||||
#include "monocypher/monocypher.h"
|
||||
#include "stun.h"
|
||||
|
||||
// List of all the profiles.
|
||||
static profile_t *profilesList[MAXPROFILES+1]; // +1 because we're gonna add a default "GUEST' profile.
|
||||
|
|
@ -28,6 +30,13 @@ INT32 PR_GetNumProfiles(void)
|
|||
return numprofiles;
|
||||
}
|
||||
|
||||
static void PR_GenerateProfileKeys(profile_t *new)
|
||||
{
|
||||
static uint8_t seed[32];
|
||||
csprng(seed, 32);
|
||||
crypto_eddsa_key_pair(new->secret_key, new->public_key, seed);
|
||||
}
|
||||
|
||||
profile_t* PR_MakeProfile(
|
||||
const char *prname,
|
||||
const char *pname,
|
||||
|
|
@ -41,6 +50,14 @@ profile_t* PR_MakeProfile(
|
|||
|
||||
new->version = PROFILEVER;
|
||||
|
||||
memset(new->secret_key, 0, sizeof(new->secret_key));
|
||||
memset(new->public_key, 0, sizeof(new->public_key));
|
||||
|
||||
if (!guest)
|
||||
{
|
||||
PR_GenerateProfileKeys(new);
|
||||
}
|
||||
|
||||
strcpy(new->profilename, prname);
|
||||
new->profilename[sizeof new->profilename - 1] = '\0';
|
||||
|
||||
|
|
@ -238,8 +255,10 @@ void PR_SaveProfiles(void)
|
|||
|
||||
for (i = 1; i < numprofiles; i++)
|
||||
{
|
||||
// Names.
|
||||
// Names and keys, all the string data up front
|
||||
WRITESTRINGN(save.p, profilesList[i]->profilename, PROFILENAMELEN);
|
||||
WRITEMEM(save.p, profilesList[i]->public_key, sizeof(((profile_t *)0)->public_key));
|
||||
WRITEMEM(save.p, profilesList[i]->secret_key, sizeof(((profile_t *)0)->secret_key));
|
||||
WRITESTRINGN(save.p, profilesList[i]->playername, MAXPLAYERNAME);
|
||||
|
||||
// Character and colour.
|
||||
|
|
@ -329,8 +348,21 @@ void PR_LoadProfiles(void)
|
|||
// Version. (We always update this on successful forward step)
|
||||
profilesList[i]->version = PROFILEVER;
|
||||
|
||||
// Names.
|
||||
// Names and keys, all the identity stuff up front
|
||||
READSTRINGN(save.p, profilesList[i]->profilename, PROFILENAMELEN);
|
||||
|
||||
// Profile update 2-->3: Add profile keys.
|
||||
if (version < 3)
|
||||
{
|
||||
// Generate missing keys.
|
||||
PR_GenerateProfileKeys(profilesList[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
READMEM(save.p, profilesList[i]->public_key, sizeof(((profile_t *)0)->public_key));
|
||||
READMEM(save.p, profilesList[i]->secret_key, sizeof(((profile_t *)0)->secret_key));
|
||||
}
|
||||
|
||||
READSTRINGN(save.p, profilesList[i]->playername, MAXPLAYERNAME);
|
||||
|
||||
// Character and colour.
|
||||
|
|
@ -550,3 +582,39 @@ profile_t *PR_GetPlayerProfile(player_t *player)
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
profile_t *PR_GetLocalPlayerProfile(INT32 player)
|
||||
{
|
||||
if (player >= MAXSPLITSCREENPLAYERS)
|
||||
return NULL;
|
||||
return PR_GetProfile(cv_lastprofile[player].value);
|
||||
}
|
||||
|
||||
boolean PR_IsLocalPlayerGuest(INT32 player)
|
||||
{
|
||||
return !(cv_lastprofile[player].value);
|
||||
}
|
||||
|
||||
static char rrid_buf[256];
|
||||
|
||||
char *GetPrettyRRID(const unsigned char *bin, boolean brief)
|
||||
{
|
||||
size_t i;
|
||||
size_t len = PUBKEYLENGTH;
|
||||
|
||||
if (brief)
|
||||
len = 8;
|
||||
|
||||
if (bin == NULL || len == 0)
|
||||
return NULL;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
{
|
||||
rrid_buf[i*2] = "0123456789ABCDEF"[bin[i] >> 4];
|
||||
rrid_buf[i*2+1] = "0123456789ABCDEF"[bin[i] & 0x0F];
|
||||
}
|
||||
|
||||
rrid_buf[len*2] = '\0';
|
||||
|
||||
return rrid_buf;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ extern "C" {
|
|||
#define SKINNAMESIZE 16
|
||||
|
||||
#define PROFILENAMELEN 6
|
||||
#define PROFILEVER 2
|
||||
#define PROFILEVER 3
|
||||
#define MAXPROFILES 16
|
||||
#define PROFILESFILE "ringprofiles.prf"
|
||||
#define PROFILE_GUEST 0
|
||||
|
|
@ -59,6 +59,9 @@ struct profile_t
|
|||
// Profile header
|
||||
char profilename[PROFILENAMELEN+1]; // Profile name (not to be confused with player name)
|
||||
|
||||
uint8_t public_key[PUBKEYLENGTH]; // Netgame authentication
|
||||
uint8_t secret_key[PRIVKEYLENGTH];
|
||||
|
||||
// Player data
|
||||
char playername[MAXPLAYERNAME+1]; // Player name
|
||||
char skinname[SKINNAMESIZE+1]; // Default Skin
|
||||
|
|
@ -156,6 +159,12 @@ SINT8 PR_ProfileUsedBy(profile_t *p);
|
|||
|
||||
profile_t *PR_GetPlayerProfile(player_t *player);
|
||||
|
||||
profile_t *PR_GetLocalPlayerProfile(INT32 player);
|
||||
|
||||
boolean PR_IsLocalPlayerGuest(INT32 player);
|
||||
|
||||
char *GetPrettyRRID(const unsigned char *bin, boolean brief);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "lua_libs.h"
|
||||
#include "lua_hud.h" // hud_running errors
|
||||
#include "lua_hook.h" // hook_cmd_running errors
|
||||
#include "k_profiles.h" // GetPrettyRRID
|
||||
|
||||
static int lib_iteratePlayers(lua_State *L)
|
||||
{
|
||||
|
|
@ -507,6 +508,8 @@ static int player_get(lua_State *L)
|
|||
#endif
|
||||
else if (fastcmp(field,"ping"))
|
||||
lua_pushinteger(L, playerpingtable[( plr - players )]);
|
||||
else if (fastcmp(field, "public_key"))
|
||||
lua_pushstring(L, GetPrettyRRID(plr->public_key, false));
|
||||
else {
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
|
||||
I_Assert(lua_istable(L, -1));
|
||||
|
|
@ -876,7 +879,7 @@ static int player_set(lua_State *L)
|
|||
else if (fastcmp(field,"bot"))
|
||||
return NOSET;
|
||||
else if (fastcmp(field,"jointime"))
|
||||
plr->jointime = (tic_t)luaL_checkinteger(L, 3);
|
||||
return NOSET;
|
||||
else if (fastcmp(field,"splitscreenindex"))
|
||||
return NOSET;
|
||||
#ifdef HWRENDER
|
||||
|
|
|
|||
4
src/monocypher/CMakeLists.txt
Normal file
4
src/monocypher/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
target_sources(SRB2SDL2 PRIVATE
|
||||
monocypher.c
|
||||
monocypher.h
|
||||
)
|
||||
2938
src/monocypher/monocypher.c
Normal file
2938
src/monocypher/monocypher.c
Normal file
File diff suppressed because it is too large
Load diff
321
src/monocypher/monocypher.h
Normal file
321
src/monocypher/monocypher.h
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
// Monocypher version 4.0.0
|
||||
//
|
||||
// This file is dual-licensed. Choose whichever licence you want from
|
||||
// the two licences listed below.
|
||||
//
|
||||
// The first licence is a regular 2-clause BSD licence. The second licence
|
||||
// is the CC-0 from Creative Commons. It is intended to release Monocypher
|
||||
// to the public domain. The BSD licence serves as a fallback option.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0
|
||||
//
|
||||
// ------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2017-2019, Loup Vaillant
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// ------------------------------------------------------------------------
|
||||
//
|
||||
// Written in 2017-2019 by Loup Vaillant
|
||||
//
|
||||
// To the extent possible under law, the author(s) have dedicated all copyright
|
||||
// and related neighboring rights to this software to the public domain
|
||||
// worldwide. This software is distributed without any warranty.
|
||||
//
|
||||
// You should have received a copy of the CC0 Public Domain Dedication along
|
||||
// with this software. If not, see
|
||||
// <https://creativecommons.org/publicdomain/zero/1.0/>
|
||||
|
||||
#ifndef MONOCYPHER_H
|
||||
#define MONOCYPHER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef MONOCYPHER_CPP_NAMESPACE
|
||||
namespace MONOCYPHER_CPP_NAMESPACE {
|
||||
#elif defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Constant time comparisons
|
||||
// -------------------------
|
||||
|
||||
// Return 0 if a and b are equal, -1 otherwise
|
||||
int crypto_verify16(const uint8_t a[16], const uint8_t b[16]);
|
||||
int crypto_verify32(const uint8_t a[32], const uint8_t b[32]);
|
||||
int crypto_verify64(const uint8_t a[64], const uint8_t b[64]);
|
||||
|
||||
|
||||
// Erase sensitive data
|
||||
// --------------------
|
||||
void crypto_wipe(void *secret, size_t size);
|
||||
|
||||
|
||||
// Authenticated encryption
|
||||
// ------------------------
|
||||
void crypto_aead_lock(uint8_t *cipher_text,
|
||||
uint8_t mac [16],
|
||||
const uint8_t key [32],
|
||||
const uint8_t nonce[24],
|
||||
const uint8_t *ad, size_t ad_size,
|
||||
const uint8_t *plain_text, size_t text_size);
|
||||
int crypto_aead_unlock(uint8_t *plain_text,
|
||||
const uint8_t mac [16],
|
||||
const uint8_t key [32],
|
||||
const uint8_t nonce[24],
|
||||
const uint8_t *ad, size_t ad_size,
|
||||
const uint8_t *cipher_text, size_t text_size);
|
||||
|
||||
// Authenticated stream
|
||||
// --------------------
|
||||
typedef struct {
|
||||
uint64_t counter;
|
||||
uint8_t key[32];
|
||||
uint8_t nonce[8];
|
||||
} crypto_aead_ctx;
|
||||
|
||||
void crypto_aead_init_x(crypto_aead_ctx *ctx,
|
||||
const uint8_t key[32], const uint8_t nonce[24]);
|
||||
void crypto_aead_init_djb(crypto_aead_ctx *ctx,
|
||||
const uint8_t key[32], const uint8_t nonce[8]);
|
||||
void crypto_aead_init_ietf(crypto_aead_ctx *ctx,
|
||||
const uint8_t key[32], const uint8_t nonce[12]);
|
||||
|
||||
void crypto_aead_write(crypto_aead_ctx *ctx,
|
||||
uint8_t *cipher_text,
|
||||
uint8_t mac[16],
|
||||
const uint8_t *ad , size_t ad_size,
|
||||
const uint8_t *plain_text, size_t text_size);
|
||||
int crypto_aead_read(crypto_aead_ctx *ctx,
|
||||
uint8_t *plain_text,
|
||||
const uint8_t mac[16],
|
||||
const uint8_t *ad , size_t ad_size,
|
||||
const uint8_t *cipher_text, size_t text_size);
|
||||
|
||||
|
||||
// General purpose hash (BLAKE2b)
|
||||
// ------------------------------
|
||||
|
||||
// Direct interface
|
||||
void crypto_blake2b(uint8_t *hash, size_t hash_size,
|
||||
const uint8_t *message, size_t message_size);
|
||||
|
||||
void crypto_blake2b_keyed(uint8_t *hash, size_t hash_size,
|
||||
const uint8_t *key, size_t key_size,
|
||||
const uint8_t *message, size_t message_size);
|
||||
|
||||
// Incremental interface
|
||||
typedef struct {
|
||||
// Do not rely on the size or contents of this type,
|
||||
// for they may change without notice.
|
||||
uint64_t hash[8];
|
||||
uint64_t input_offset[2];
|
||||
uint64_t input[16];
|
||||
size_t input_idx;
|
||||
size_t hash_size;
|
||||
} crypto_blake2b_ctx;
|
||||
|
||||
void crypto_blake2b_init(crypto_blake2b_ctx *ctx, size_t hash_size);
|
||||
void crypto_blake2b_keyed_init(crypto_blake2b_ctx *ctx, size_t hash_size,
|
||||
const uint8_t *key, size_t key_size);
|
||||
void crypto_blake2b_update(crypto_blake2b_ctx *ctx,
|
||||
const uint8_t *message, size_t message_size);
|
||||
void crypto_blake2b_final(crypto_blake2b_ctx *ctx, uint8_t *hash);
|
||||
|
||||
|
||||
// Password key derivation (Argon2)
|
||||
// --------------------------------
|
||||
#define CRYPTO_ARGON2_D 0
|
||||
#define CRYPTO_ARGON2_I 1
|
||||
#define CRYPTO_ARGON2_ID 2
|
||||
|
||||
typedef struct {
|
||||
uint32_t algorithm; // Argon2d, Argon2i, Argon2id
|
||||
uint32_t nb_blocks; // memory hardness, >= 8 * nb_lanes
|
||||
uint32_t nb_passes; // CPU hardness, >= 1 (>= 3 recommended for Argon2i)
|
||||
uint32_t nb_lanes; // parallelism level (single threaded anyway)
|
||||
} crypto_argon2_config;
|
||||
|
||||
typedef struct {
|
||||
const uint8_t *pass;
|
||||
const uint8_t *salt;
|
||||
uint32_t pass_size;
|
||||
uint32_t salt_size; // 16 bytes recommended
|
||||
} crypto_argon2_inputs;
|
||||
|
||||
typedef struct {
|
||||
const uint8_t *key; // may be NULL if no key
|
||||
const uint8_t *ad; // may be NULL if no additional data
|
||||
uint32_t key_size; // 0 if no key (32 bytes recommended otherwise)
|
||||
uint32_t ad_size; // 0 if no additional data
|
||||
} crypto_argon2_extras;
|
||||
|
||||
extern const crypto_argon2_extras crypto_argon2_no_extras;
|
||||
|
||||
void crypto_argon2(uint8_t *hash, uint32_t hash_size, void *work_area,
|
||||
crypto_argon2_config config,
|
||||
crypto_argon2_inputs inputs,
|
||||
crypto_argon2_extras extras);
|
||||
|
||||
|
||||
// Key exchange (X-25519)
|
||||
// ----------------------
|
||||
|
||||
// Shared secrets are not quite random.
|
||||
// Hash them to derive an actual shared key.
|
||||
void crypto_x25519_public_key(uint8_t public_key[32],
|
||||
const uint8_t secret_key[32]);
|
||||
void crypto_x25519(uint8_t raw_shared_secret[32],
|
||||
const uint8_t your_secret_key [32],
|
||||
const uint8_t their_public_key [32]);
|
||||
|
||||
// Conversion to EdDSA
|
||||
void crypto_x25519_to_eddsa(uint8_t eddsa[32], const uint8_t x25519[32]);
|
||||
|
||||
// scalar "division"
|
||||
// Used for OPRF. Be aware that exponential blinding is less secure
|
||||
// than Diffie-Hellman key exchange.
|
||||
void crypto_x25519_inverse(uint8_t blind_salt [32],
|
||||
const uint8_t private_key[32],
|
||||
const uint8_t curve_point[32]);
|
||||
|
||||
// "Dirty" versions of x25519_public_key().
|
||||
// Use with crypto_elligator_rev().
|
||||
// Leaks 3 bits of the private key.
|
||||
void crypto_x25519_dirty_small(uint8_t pk[32], const uint8_t sk[32]);
|
||||
void crypto_x25519_dirty_fast (uint8_t pk[32], const uint8_t sk[32]);
|
||||
|
||||
|
||||
// Signatures
|
||||
// ----------
|
||||
|
||||
// EdDSA with curve25519 + BLAKE2b
|
||||
void crypto_eddsa_key_pair(uint8_t secret_key[64],
|
||||
uint8_t public_key[32],
|
||||
uint8_t seed[32]);
|
||||
void crypto_eddsa_sign(uint8_t signature [64],
|
||||
const uint8_t secret_key[64],
|
||||
const uint8_t *message, size_t message_size);
|
||||
int crypto_eddsa_check(const uint8_t signature [64],
|
||||
const uint8_t public_key[32],
|
||||
const uint8_t *message, size_t message_size);
|
||||
|
||||
// Conversion to X25519
|
||||
void crypto_eddsa_to_x25519(uint8_t x25519[32], const uint8_t eddsa[32]);
|
||||
|
||||
// EdDSA building blocks
|
||||
void crypto_eddsa_trim_scalar(uint8_t out[32], const uint8_t in[32]);
|
||||
void crypto_eddsa_reduce(uint8_t reduced[32], const uint8_t expanded[64]);
|
||||
void crypto_eddsa_mul_add(uint8_t r[32],
|
||||
const uint8_t a[32],
|
||||
const uint8_t b[32],
|
||||
const uint8_t c[32]);
|
||||
void crypto_eddsa_scalarbase(uint8_t point[32], const uint8_t scalar[32]);
|
||||
int crypto_eddsa_check_equation(const uint8_t signature[64],
|
||||
const uint8_t public_key[32],
|
||||
const uint8_t h_ram[32]);
|
||||
|
||||
|
||||
// Chacha20
|
||||
// --------
|
||||
|
||||
// Specialised hash.
|
||||
// Used to hash X25519 shared secrets.
|
||||
void crypto_chacha20_h(uint8_t out[32],
|
||||
const uint8_t key[32],
|
||||
const uint8_t in [16]);
|
||||
|
||||
// Unauthenticated stream cipher.
|
||||
// Don't forget to add authentication.
|
||||
uint64_t crypto_chacha20_djb(uint8_t *cipher_text,
|
||||
const uint8_t *plain_text,
|
||||
size_t text_size,
|
||||
const uint8_t key[32],
|
||||
const uint8_t nonce[8],
|
||||
uint64_t ctr);
|
||||
uint32_t crypto_chacha20_ietf(uint8_t *cipher_text,
|
||||
const uint8_t *plain_text,
|
||||
size_t text_size,
|
||||
const uint8_t key[32],
|
||||
const uint8_t nonce[12],
|
||||
uint32_t ctr);
|
||||
uint64_t crypto_chacha20_x(uint8_t *cipher_text,
|
||||
const uint8_t *plain_text,
|
||||
size_t text_size,
|
||||
const uint8_t key[32],
|
||||
const uint8_t nonce[24],
|
||||
uint64_t ctr);
|
||||
|
||||
|
||||
// Poly 1305
|
||||
// ---------
|
||||
|
||||
// This is a *one time* authenticator.
|
||||
// Disclosing the mac reveals the key.
|
||||
// See crypto_lock() on how to use it properly.
|
||||
|
||||
// Direct interface
|
||||
void crypto_poly1305(uint8_t mac[16],
|
||||
const uint8_t *message, size_t message_size,
|
||||
const uint8_t key[32]);
|
||||
|
||||
// Incremental interface
|
||||
typedef struct {
|
||||
// Do not rely on the size or contents of this type,
|
||||
// for they may change without notice.
|
||||
uint8_t c[16]; // chunk of the message
|
||||
size_t c_idx; // How many bytes are there in the chunk.
|
||||
uint32_t r [4]; // constant multiplier (from the secret key)
|
||||
uint32_t pad[4]; // random number added at the end (from the secret key)
|
||||
uint32_t h [5]; // accumulated hash
|
||||
} crypto_poly1305_ctx;
|
||||
|
||||
void crypto_poly1305_init (crypto_poly1305_ctx *ctx, const uint8_t key[32]);
|
||||
void crypto_poly1305_update(crypto_poly1305_ctx *ctx,
|
||||
const uint8_t *message, size_t message_size);
|
||||
void crypto_poly1305_final (crypto_poly1305_ctx *ctx, uint8_t mac[16]);
|
||||
|
||||
|
||||
// Elligator 2
|
||||
// -----------
|
||||
|
||||
// Elligator mappings proper
|
||||
void crypto_elligator_map(uint8_t curve [32], const uint8_t hidden[32]);
|
||||
int crypto_elligator_rev(uint8_t hidden[32], const uint8_t curve [32],
|
||||
uint8_t tweak);
|
||||
|
||||
// Easy to use key pair generation
|
||||
void crypto_elligator_key_pair(uint8_t hidden[32], uint8_t secret_key[32],
|
||||
uint8_t seed[32]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MONOCYPHER_H
|
||||
|
|
@ -407,6 +407,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT8(save->p, players[i].sliptideZipDelay);
|
||||
WRITEUINT16(save->p, players[i].sliptideZipBoost);
|
||||
|
||||
WRITEMEM(save->p, players[i].public_key, PUBKEYLENGTH);
|
||||
|
||||
// respawnvars_t
|
||||
WRITEUINT8(save->p, players[i].respawn.state);
|
||||
WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].respawn.wp));
|
||||
|
|
@ -787,6 +789,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].sliptideZipDelay = READUINT8(save->p);
|
||||
players[i].sliptideZipBoost = READUINT16(save->p);
|
||||
|
||||
READMEM(save->p, players[i].public_key, PUBKEYLENGTH);
|
||||
|
||||
// respawnvars_t
|
||||
players[i].respawn.state = READUINT8(save->p);
|
||||
players[i].respawn.wp = (waypoint_t *)(size_t)READUINT32(save->p);
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ STUN_node (void)
|
|||
return node;
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
csprng
|
||||
(
|
||||
void * const buffer,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ extern "C" {
|
|||
|
||||
typedef void (*stun_callback_t)(UINT32 address);
|
||||
|
||||
void csprng (void * const buffer, const size_t size);
|
||||
|
||||
void STUN_bind (stun_callback_t);
|
||||
boolean STUN_got_response (const char * const buffer, const size_t size);
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ TYPEDEF (filesneededconfig_pak);
|
|||
TYPEDEF (doomdata_t);
|
||||
TYPEDEF (serverelem_t);
|
||||
TYPEDEF (rewind_t);
|
||||
TYPEDEF (clientkey_pak);
|
||||
TYPEDEF (serverchallenge_pak);
|
||||
TYPEDEF (challengeall_pak);
|
||||
TYPEDEF (responseall_pak);
|
||||
TYPEDEF (resultsall_pak);
|
||||
|
||||
// d_event.h
|
||||
TYPEDEF (event_t);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue