Add Server Digest support. Note that the server digest key must be added to an environment variable.

This commit is contained in:
Michael VanOverbeek 2021-10-30 20:54:33 -04:00
commit 37abf75071
5 changed files with 63 additions and 27 deletions

View file

@ -13,8 +13,10 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[SuppressMessage("ReSharper", "StringLiteralTypo")] [SuppressMessage("ReSharper", "StringLiteralTypo")]
public IActionResult NetworkSettings() public IActionResult NetworkSettings()
{ {
var hostname = this.Request.Host;
return this.Ok( return this.Ok(
"ProbabilityOfPacketDelay 0.0\nMinPacketDelayFrames 0\nMaxPacketDelayFrames 3\nProbabilityOfPacketDrop 0.0\nEnableFakeConditionsForLoopback true\nNumberOfFramesPredictionAllowedForNonLocalPlayer 1000\nEnablePrediction true\nMinPredictedFrames 0\nMaxPredictedFrames 10\nAllowGameRendCameraSplit true\nFramesBeforeAgressiveCatchup 30\nPredictionPadSides 200\nPredictionPadTop 200\nPredictionPadBottom 200\nShowErrorNumbers true\nAllowModeratedLevels true\nAllowModeratedPoppetItems true\nShowLevelBoos true\nTIMEOUT_WAIT_FOR_JOIN_RESPONSE_FROM_PREV_PARTY_HOST 50.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_HOST 30.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_MEMBER 45.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN_FRIEND 15.0\nTIMEOUT_WAIT_FOR_CONNECTION_FROM_HOST 30.0\nTIMEOUT_WAIT_FOR_ROOM_ID_TO_JOIN 60.0\nTIMEOUT_WAIT_FOR_GET_NUM_PLAYERS_ONLINE 60.0\nTIMEOUT_WAIT_FOR_SIGNALLING_CONNECTIONS 120.0\nTIMEOUT_WAIT_FOR_PARTY_DATA 60.0\nTIME_TO_WAIT_FOR_LEAVE_MESSAGE_TO_COME_BACK 20.0\nTIME_TO_WAIT_FOR_FOLLOWING_REQUESTS_TO_ARRIVE 30.0\nTIMEOUT_WAIT_FOR_FINISHED_MIGRATING_HOST 30.0\nTIMEOUT_WAIT_FOR_PARTY_LEADER_FINISH_JOINING 45.0\nTIMEOUT_WAIT_FOR_QUICKPLAY_LEVEL 60.0\nTIMEOUT_WAIT_FOR_PLAYERS_TO_JOIN 30.0\nTIMEOUT_WAIT_FOR_DIVE_IN_PLAYERS 120.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 30.0\nTIMEOUT_DIVE_IN_TOTAL 1000000.0\nTIMEOUT_WAIT_FOR_SOCKET_CONNECTION 120.0\nTIMEOUT_WAIT_FOR_REQUEST_RESOURCE_MESSAGE 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_GET_RESOURCE_LIST 120.0\nTIMEOUT_WAIT_FOR_CLIENT_TO_LOAD_RESOURCES 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_SAVE_GAME_STATE 30.0\nTIMEOUT_WAIT_FOR_ADD_PLAYERS_TO_TAKE 30.0\nTIMEOUT_WAIT_FOR_UPDATE_FROM_CLIENT 90.0\nTIMEOUT_WAIT_FOR_HOST_TO_GET_RESOURCE_LIST 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_SAVE_GAME_STATE 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_ADD_US 30.0\nTIMEOUT_WAIT_FOR_UPDATE 60.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN 50.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_PRESENCE 60.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_CONNECTION 120.0\nSECONDS_BETWEEN_PINS_AWARDED_UPLOADS 300.0\nEnableKeepAlive true\nAllowVoIPRecordingPlayback true\nCDNHostName localhost\nTelemetryServer localhost\nOverheatingThresholdDisallowMidgameJoin 0.95\nMaxCatchupFrames 3\nMaxLagBeforeShowLoading 23\nMinLagBeforeHideLoading 30\nLagImprovementInflectionPoint -1.0\nFlickerThreshold 2.0\nClosedDemo2014Version 1\nClosedDemo2014Expired false\nEnablePlayedFilter true\nEnableCommunityDecorations true\nGameStateUpdateRate 10.0\nGameStateUpdateRateWithConsumers 1.0\nDisableDLCPublishCheck false\nEnableDiveIn true\nEnableHackChecks false"); "ProbabilityOfPacketDelay 0.0\nMinPacketDelayFrames 0\nMaxPacketDelayFrames 3\nProbabilityOfPacketDrop 0.0\nEnableFakeConditionsForLoopback true\nNumberOfFramesPredictionAllowedForNonLocalPlayer 1000\nEnablePrediction true\nMinPredictedFrames 0\nMaxPredictedFrames 10\nAllowGameRendCameraSplit true\nFramesBeforeAgressiveCatchup 30\nPredictionPadSides 200\nPredictionPadTop 200\nPredictionPadBottom 200\nShowErrorNumbers true\nAllowModeratedLevels true\nAllowModeratedPoppetItems true\nShowLevelBoos true\nTIMEOUT_WAIT_FOR_JOIN_RESPONSE_FROM_PREV_PARTY_HOST 50.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_HOST 30.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_MEMBER 45.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN_FRIEND 15.0\nTIMEOUT_WAIT_FOR_CONNECTION_FROM_HOST 30.0\nTIMEOUT_WAIT_FOR_ROOM_ID_TO_JOIN 60.0\nTIMEOUT_WAIT_FOR_GET_NUM_PLAYERS_ONLINE 60.0\nTIMEOUT_WAIT_FOR_SIGNALLING_CONNECTIONS 120.0\nTIMEOUT_WAIT_FOR_PARTY_DATA 60.0\nTIME_TO_WAIT_FOR_LEAVE_MESSAGE_TO_COME_BACK 20.0\nTIME_TO_WAIT_FOR_FOLLOWING_REQUESTS_TO_ARRIVE 30.0\nTIMEOUT_WAIT_FOR_FINISHED_MIGRATING_HOST 30.0\nTIMEOUT_WAIT_FOR_PARTY_LEADER_FINISH_JOINING 45.0\nTIMEOUT_WAIT_FOR_QUICKPLAY_LEVEL 60.0\nTIMEOUT_WAIT_FOR_PLAYERS_TO_JOIN 30.0\nTIMEOUT_WAIT_FOR_DIVE_IN_PLAYERS 120.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 30.0\nTIMEOUT_DIVE_IN_TOTAL 1000000.0\nTIMEOUT_WAIT_FOR_SOCKET_CONNECTION 120.0\nTIMEOUT_WAIT_FOR_REQUEST_RESOURCE_MESSAGE 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_GET_RESOURCE_LIST 120.0\nTIMEOUT_WAIT_FOR_CLIENT_TO_LOAD_RESOURCES 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_SAVE_GAME_STATE 30.0\nTIMEOUT_WAIT_FOR_ADD_PLAYERS_TO_TAKE 30.0\nTIMEOUT_WAIT_FOR_UPDATE_FROM_CLIENT 90.0\nTIMEOUT_WAIT_FOR_HOST_TO_GET_RESOURCE_LIST 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_SAVE_GAME_STATE 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_ADD_US 30.0\nTIMEOUT_WAIT_FOR_UPDATE 60.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN 50.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_PRESENCE 60.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_CONNECTION 120.0\nSECONDS_BETWEEN_PINS_AWARDED_UPLOADS 300.0\nEnableKeepAlive true\nAllowVoIPRecordingPlayback true\nCDNHostName localhost\nTelemetryServer localhost\nOverheatingThresholdDisallowMidgameJoin 0.95\nMaxCatchupFrames 3\nMaxLagBeforeShowLoading 23\nMinLagBeforeHideLoading 30\nLagImprovementInflectionPoint -1.0\nFlickerThreshold 2.0\nClosedDemo2014Version 1\nClosedDemo2014Expired false\nEnablePlayedFilter true\nEnableCommunityDecorations true\nGameStateUpdateRate 10.0\nGameStateUpdateRateWithConsumers 1.0\nDisableDLCPublishCheck false\nEnableDiveIn true\nEnableHackChecks false\n"
+ $"TelemetryServer {hostname}\nCDNHostName {hostname}");
} }
[HttpGet("t_conf")] [HttpGet("t_conf")]

View file

@ -7,7 +7,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{ {
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/plain")] // [Produces("text/plain")]
public class MessageController : ControllerBase public class MessageController : ControllerBase
{ {
private readonly Database database; private readonly Database database;
@ -22,15 +22,19 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{ {
User user = await this.database.UserFromRequest(this.Request); User user = await this.database.UserFromRequest(this.Request);
return user == null return user == null
? this.Ok("You aren't logged in, but you're connected to a private LBP server.") ? this.Forbid()
: this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n" + : this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n" +
"This is a private testing instance. Please do not make anything public for now, and keep in mind security isn't as tight as a full release would."); "This is a private testing instance. Please do not make anything public for now, and keep in mind security isn't as tight as a full release would.");
} }
[HttpGet("announce")] [HttpGet("announce")]
public IActionResult Announce() public async Task<IActionResult> Announce()
{ {
return this.Ok(""); User user = await this.database.UserFromRequest(this.Request);
return user == null
? this.Forbid()
: this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n" +
"This is a private testing instance. Please do not make anything public for now, and keep in mind security isn't as tight as a full release would.");
} }
[HttpGet("notification")] [HttpGet("notification")]

View file

@ -0,0 +1,17 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
public class StoreController : Controller
{
[HttpGet("promotions")]
public async Task<IActionResult> Promotions()
{
return Ok();
}
}
}

View file

@ -3,14 +3,18 @@ using System.Linq;
using System.Text.Json; using System.Text.Json;
using LBPUnion.ProjectLighthouse.Types.Match; using LBPUnion.ProjectLighthouse.Types.Match;
namespace LBPUnion.ProjectLighthouse.Helpers { namespace LBPUnion.ProjectLighthouse.Helpers
public static class MatchHelper { {
public static IMatchData? Deserialize(string data) { public static class MatchHelper
{
public static IMatchData? Deserialize(string data)
{
string matchType = ""; string matchType = "";
int i = 1; int i = 1;
while(true) { while (true)
if(data[i] == ',') break; {
if (data[i] == ',') break;
matchType += data[i]; matchType += data[i];
i++; i++;
@ -21,8 +25,10 @@ namespace LBPUnion.ProjectLighthouse.Helpers {
return Deserialize(matchType, matchData); return Deserialize(matchType, matchData);
} }
public static IMatchData? Deserialize(string matchType, string matchData) { public static IMatchData? Deserialize(string matchType, string matchData)
return matchType switch { {
return matchType switch
{
"UpdateMyPlayerData" => JsonSerializer.Deserialize<UpdateMyPlayerData>(matchData), "UpdateMyPlayerData" => JsonSerializer.Deserialize<UpdateMyPlayerData>(matchData),
"UpdatePlayersInRoom" => JsonSerializer.Deserialize<UpdatePlayersInRoom>(matchData), "UpdatePlayersInRoom" => JsonSerializer.Deserialize<UpdatePlayersInRoom>(matchData),
_ => null, _ => null,

View file

@ -2,6 +2,7 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices.ComTypes;
using System.Threading.Tasks; using System.Threading.Tasks;
using Kettu; using Kettu;
using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging;
@ -61,33 +62,36 @@ namespace LBPUnion.ProjectLighthouse
Stopwatch requestStopwatch = new(); Stopwatch requestStopwatch = new();
requestStopwatch.Start(); requestStopwatch.Start();
// Log all headers.
foreach (var header in context.Request.Headers)
Logger.Log($"{header.Key}: {header.Value}");
context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging
// Client digest check. // Client digest check.
var authCookie = null as string; var authCookie = null as string;
if (!context.Request.Cookies.TryGetValue("MM_AUTH", out authCookie)) if (!context.Request.Cookies.TryGetValue("MM_AUTH", out authCookie))
authCookie = string.Empty; authCookie = string.Empty;
if (context.Request.Headers.TryGetValue("X-Digest-A", out var clientDigest)) var digestPath = context.Request.Path;
var body = context.Request.Body;
var clientRequestDigest = await DigestUtils.ComputeDigest(digestPath, authCookie, body, serverDigestKey);
// Check the digest we've just calculated against the X-Digest-A header if the game set the header. They should match.
if (context.Request.Headers.TryGetValue("X-Digest-A", out var sentDigest))
{ {
var digestPath = context.Request.Path; if (clientRequestDigest != sentDigest)
var body = context.Request.Body;
var digest = await DigestUtils.ComputeDigest(digestPath, authCookie, body, serverDigestKey);
if (digest != clientDigest)
{ {
Logger.Log($"Client digest {clientDigest} does not match server digest {digest}."); context.Response.StatusCode = 403;
context.Abort(); context.Abort();
return; return;
} }
else
{
context.Response.Headers.Add("X-Digest-B", digest);
context.Request.Body.Position = 0;
}
} }
// This does the same as above, but for the response stream. context.Response.Headers.Add("X-Digest-B", clientRequestDigest);
context.Request.Body.Position = 0;
// This does the same as above, but for the response stream.
using var responseBuffer = new MemoryStream(); using var responseBuffer = new MemoryStream();
var oldResponseStream = context.Response.Body; var oldResponseStream = context.Response.Body;
context.Response.Body = responseBuffer; context.Response.Body = responseBuffer;
@ -95,7 +99,7 @@ namespace LBPUnion.ProjectLighthouse
await next(); // Handle the request so we can get the status code from it await next(); // Handle the request so we can get the status code from it
// Compute the server digest hash. // Compute the server digest hash.
if (computeDigests && context.Request.Headers.TryGetValue("X-Digest-A", out var a)) if (computeDigests)
{ {
responseBuffer.Position = 0; responseBuffer.Position = 0;
@ -105,6 +109,9 @@ namespace LBPUnion.ProjectLighthouse
context.Response.Headers.Add("X-Digest-A", serverDigest); context.Response.Headers.Add("X-Digest-A", serverDigest);
} }
// Set the X-Original-Content-Length header to the length of the response buffer.
context.Response.Headers.Add("X-Original-Content-Length", responseBuffer.Length.ToString());
// Copy the buffered response to the actual respose stream. // Copy the buffered response to the actual respose stream.
responseBuffer.Position = 0; responseBuffer.Position = 0;