Testing more garbage encryption

This commit is contained in:
gamer147
2024-09-07 22:14:24 -04:00
parent f7657c2ec4
commit 7e4bce9ac5
32 changed files with 783 additions and 51 deletions

View File

@@ -0,0 +1,8 @@
namespace SVSim.EmulatedEntrypoint.Constants;
public static class NetworkConstants
{
public const string UdidHeaderName = "UDID";
public const string SessionIdHeaderName = "SID";
public const string ShortUdidHeaderName = "SHORT_UDID";
}

View File

@@ -0,0 +1,7 @@
namespace SVSim.EmulatedEntrypoint.Constants;
public static class ShadowverseClaimTypes
{
public const string ShortUdidClaim = "ShortUdid";
public const string ViewerIdClaim = "ViewerId";
}

View File

@@ -1,6 +1,7 @@
using System.Buffers.Text;
using System.Text;
using MessagePack;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
@@ -22,6 +23,7 @@ namespace SVSim.EmulatedEntrypoint.Controllers
_logger = logger;
}
[AllowAnonymous]
[HttpPost("special_title")]
public async Task<DataWrapper<SpecialTitleCheckResponse>> SpecialTitleCheck(SpecialTitleCheckRequest request)
{

View File

@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using SVSim.EmulatedEntrypoint.Security;
using SVSim.EmulatedEntrypoint.Security.SteamSessionAuthentication;
namespace SVSim.EmulatedEntrypoint.Controllers
{
@@ -9,11 +11,8 @@ namespace SVSim.EmulatedEntrypoint.Controllers
/// </summary>
[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = SteamAuthenticationConstants.SchemeName)]
public abstract class SVSimController : ControllerBase
{
/// <summary>
/// Returns the UdId of the user making the request. Can be null or empty, as only certain requests will send it. Known requests to send this value are: SignUp, CheckSpecialTitle, CheckiCloudUser, MigrateiCloudUser
/// </summary>
public string? UdId => Encryption.Decode(Request.Headers["UDID"]);
}
}

View File

@@ -0,0 +1,30 @@
using SVSim.EmulatedEntrypoint.Constants;
using SVSim.EmulatedEntrypoint.Security;
using SVSim.EmulatedEntrypoint.Services;
namespace SVSim.EmulatedEntrypoint.Middlewares;
/// <summary>
/// Maps an incoming request's session id to a udid if both are present.
/// </summary>
public class SessionidMappingMiddleware : IMiddleware
{
private readonly ShadowverseSessionService _shadowverseSessionService;
public SessionidMappingMiddleware(ShadowverseSessionService shadowverseSessionService)
{
_shadowverseSessionService = shadowverseSessionService;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
bool hasSessionId = context.Request.Headers.TryGetValue(NetworkConstants.UdidHeaderName, out var udid);
bool hasUdid = context.Request.Headers.TryGetValue(NetworkConstants.SessionIdHeaderName, out var sid);
if (hasSessionId && hasUdid)
{
_shadowverseSessionService.StoreUdidForSessionId(sid.FirstOrDefault(), Guid.Parse(Encryption.Decode(udid.FirstOrDefault())));
}
await next.Invoke(context);
}
}

View File

@@ -4,7 +4,9 @@ using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using SVSim.EmulatedEntrypoint.Constants;
using SVSim.EmulatedEntrypoint.Security;
using SVSim.EmulatedEntrypoint.Services;
namespace SVSim.EmulatedEntrypoint.Middlewares;
@@ -14,10 +16,12 @@ namespace SVSim.EmulatedEntrypoint.Middlewares;
public class ShadowverseTranslationMiddleware : IMiddleware
{
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
private readonly ShadowverseSessionService _sessionService;
public ShadowverseTranslationMiddleware(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
public ShadowverseTranslationMiddleware(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider, ShadowverseSessionService sessionService)
{
_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
_sessionService = sessionService;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
@@ -32,21 +36,33 @@ public class ShadowverseTranslationMiddleware : IMiddleware
await next.Invoke(context);
return;
}
using var requestBytesStream = new MemoryStream();
// Replace response body stream to re-access it.
using var tempResponseBody = new MemoryStream();
var originalResponsebody = context.Response.Body;
context.Response.Body = tempResponseBody;
// Pull out the request bytes into a stream
using var requestBytesStream = new MemoryStream();
await context.Request.Body.CopyToAsync(requestBytesStream);
byte[] requestBytes = requestBytesStream.ToArray();
// Decrypt incoming data. Placeholder.
requestBytes = Encryption.Decrypt(requestBytes, Encryption.Decode(context.Request.Headers["UDID"]));
// Get encryption values for this request
string sid = context.Request.Headers[NetworkConstants.SessionIdHeaderName];
string udid = _sessionService.GetUdidFromSessionId(sid).GetValueOrDefault().ToString();
// Decrypt incoming data.
requestBytes = Encryption.Decrypt(requestBytes, udid);
object? data = MessagePackSerializer.Deserialize(endpointDescriptor.Parameters.FirstOrDefault().ParameterType,
requestBytes);
var json = JsonConvert.SerializeObject(data);
var newStream = new StringContent(json, Encoding.UTF8, "application/json");
context.Request.Body = newStream.ReadAsStream();
context.Request.Headers.ContentType = new StringValues("application/json");
await next.Invoke(context);
// Convert the response into a messagepack, encrypt it
var responseType = ((ControllerActionDescriptor)endpointDescriptor).MethodInfo.ReturnType;
if (responseType.IsGenericType && responseType.GetGenericTypeDefinition() == typeof(Task<>))
{
@@ -58,7 +74,7 @@ public class ShadowverseTranslationMiddleware : IMiddleware
var responseBytes = responseBytesStream.ToArray();
var responseData = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(responseBytes), responseType);
var packedData = MessagePackSerializer.Serialize(responseType, responseData);
packedData = Encryption.Encrypt(packedData, Encryption.Decode(context.Request.Headers["UDID"]));
packedData = Encryption.Encrypt(packedData, udid);
await originalResponsebody.WriteAsync(Encoding.UTF8.GetBytes(Convert.ToBase64String(packedData)));
context.Response.Body = originalResponsebody;
}

View File

@@ -3,12 +3,12 @@ using MessagePack;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
[MessagePackObject]
public abstract class BaseRequest
public class BaseRequest
{
[Key("viewer_id")]
public string ViewerId { get; set; }
[Key("steam_id")]
public long SteamId { get; set; }
public ulong SteamId { get; set; }
[Key("steam_session_ticket")]
public string SteamSessionTicket { get; set; }
}

View File

@@ -7,8 +7,10 @@ public class TransitionAccountData
{
[Key("social_account_id")]
public string SocialAccountId { get; set; }
[Key("social_account_type")]
public string SocialAccountType { get; set; }
[Key("connected_viewer_id")]
public string ConnectedViewerId { get; set; }
}

View File

@@ -4,7 +4,10 @@ using DCGEngine.Database.Configuration;
using Microsoft.EntityFrameworkCore;
using SVSim.Database;
using SVSim.Database.Models;
using SVSim.Database.Repositories.Viewer;
using SVSim.EmulatedEntrypoint.Middlewares;
using SVSim.EmulatedEntrypoint.Security.SteamSessionAuthentication;
using SVSim.EmulatedEntrypoint.Services;
namespace SVSim.EmulatedEntrypoint;
@@ -24,17 +27,41 @@ public class Program
{
});
#region Database Services
builder.Services.AddDbContext<SVSimDbContext>(opt =>
{
opt.UseSqlite();
opt.UseSqlite(builder.Configuration.GetConnectionString("Sqlite"));
});
builder.Services.AddTransient<IViewerRepository, ViewerRepository>();
#endregion
builder.Services.AddTransient<ShadowverseTranslationMiddleware>();
builder.Services.AddTransient<SessionidMappingMiddleware>();
builder.Services.Configure<DCGEDatabaseConfiguration>(opt =>
{
opt.DbSetSearchAssemblies = new List<Assembly> { Assembly.GetAssembly(typeof(SVSimDbContext)) };
});
builder.Services.AddSingleton<ShadowverseSessionService>();
builder.Services.AddSingleton<SteamSessionService>();
builder.Services.AddAuthentication()
.AddScheme<SteamAuthenticationHandlerOptions, SteamSessionAuthenticationHandler>(
SteamAuthenticationConstants.SchemeName,
opt =>
{
});
var app = builder.Build();
// Update database
using (var scope = app.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
dbContext.UpdateDatabase();
}
app.UseHttpLogging();
@@ -47,8 +74,12 @@ public class Program
//app.UseHttpsRedirection();
app.UseMiddleware<SessionidMappingMiddleware>();
app.UseMiddleware<ShadowverseTranslationMiddleware>();
app.UseAuthentication();
app.UseAuthorization();

View File

@@ -32,4 +32,29 @@
<ProjectReference Include="..\SVSim.Database\SVSim.Database.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="lib\libsteam_api.so">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="lib\libsteam_api.so.meta">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="lib\steam_api.dll">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="lib\steam_api.lib">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="lib\steam_api64.dll">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="lib\steam_api64.lib">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="COPY &quot;$(ProjectDir)\lib\*&quot; &quot;$(TargetDir)&quot;" />
</Target>
</Project>

View File

@@ -58,27 +58,29 @@ public static class Encryption
/// <returns>the decrypted bytes</returns>
public static byte[] Decrypt(byte[] encryptedData, string udId)
{
using (var rj = Aes.Create())
using (var rj = new RijndaelManaged())
{
rj.KeySize = EncryptionKeySize;
rj.Mode = EncryptionMode;
rj.BlockSize = EncryptionBlockSize;
//rj.Padding = PaddingMode.None;
byte[] rgbIv = Encoding.UTF8.GetBytes(udId.Replace("-", string.Empty).Substring(0, UdIdKeySize));
byte[] keyBytes = new byte[KeyStringSize];
byte[] encryptedValueBytes = new byte[encryptedData.Length - KeyStringSize];
Array.Copy(encryptedData, encryptedData.Length - keyBytes.Length, keyBytes, 0, keyBytes.Length);
Array.Copy(encryptedData, 0, encryptedValueBytes, 0, encryptedValueBytes.Length);
ICryptoTransform transform = rj.CreateDecryptor(keyBytes, rgbIv);
byte[] decryptedValueBytes = new byte[encryptedValueBytes.Length];
using (MemoryStream ms = new MemoryStream(encryptedValueBytes))
{
using (CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Read))
{
byte[] decryptedValueBytes = new byte[encryptedValueBytes.Length];
cs.Read(decryptedValueBytes, 0, encryptedValueBytes.Length);
cs.FlushFinalBlock();
return decryptedValueBytes;
cs.CopyTo(decryptedValueBytes);
cs.Flush();
ms.Flush();
}
}
return decryptedValueBytes;
}
}

View File

@@ -0,0 +1,7 @@
namespace SVSim.EmulatedEntrypoint.Security.SteamSessionAuthentication;
public static class SteamAuthenticationConstants
{
public const string SchemeName = "SteamAuthentication";
public const string SteamIdClaim = "SteamId";
}

View File

@@ -1,21 +1,74 @@
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using SVSim.Database.Enums;
using SVSim.Database.Models;
using SVSim.Database.Repositories.Viewer;
using SVSim.EmulatedEntrypoint.Constants;
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
using SVSim.EmulatedEntrypoint.Services;
namespace SVSim.EmulatedEntrypoint.Security.SteamSessionAuthentication;
public class SteamSessionAuthenticationHandler : AuthenticationHandler<SteamAuthenticationHandlerOptions>
{
public SteamSessionAuthenticationHandler(IOptionsMonitor<SteamAuthenticationHandlerOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
public SteamSessionAuthenticationHandler(IOptionsMonitor<SteamAuthenticationHandlerOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder)
private readonly SteamSessionService _sessionService;
private readonly IViewerRepository _viewerRepository;
public SteamSessionAuthenticationHandler(IOptionsMonitor<SteamAuthenticationHandlerOptions> options, ILoggerFactory logger, UrlEncoder encoder, SteamSessionService sessionService, IViewerRepository viewerRepository) : base(options, logger, encoder)
{
_sessionService = sessionService;
_viewerRepository = viewerRepository;
}
protected async override Task<AuthenticateResult> HandleAuthenticateAsync()
{
return AuthenticateResult.Fail("Not implemented");
byte[] requestBytes;
using (var requestBytesStream = new MemoryStream())
{
await Request.Body.CopyToAsync(requestBytesStream);
requestBytes = requestBytesStream.ToArray();
}
// Convert bytes to json
string requestString = Encoding.UTF8.GetString(requestBytes);
BaseRequest? requestJson = JsonConvert.DeserializeObject<BaseRequest>(requestString);
// Reset request stream
Request.Body.Seek(0, SeekOrigin.Begin);
if (requestJson is null)
{
return AuthenticateResult.Fail("Invalid request body.");
}
// Check steam session validity
bool sessionIsValid = _sessionService.IsTicketValidForUser(requestJson.SteamSessionTicket, requestJson.SteamId);
if (!sessionIsValid)
{
return AuthenticateResult.Fail("Invalid ticket.");
}
Viewer? viewer =
await _viewerRepository.GetViewerBySocialConnection(SocialAccountType.Steam, requestJson.SteamId);
if (viewer is null)
{
return AuthenticateResult.Fail("User not found.");
}
// Build identity
ClaimsIdentity identity = new ClaimsIdentity();
identity.AddClaim(new Claim(ClaimTypes.Name, viewer.DisplayName));
identity.AddClaim(new Claim(ShadowverseClaimTypes.ShortUdidClaim, viewer.ShortUdid.ToString()));
identity.AddClaim(new Claim(ShadowverseClaimTypes.ViewerIdClaim, viewer.Id.ToString()));
identity.AddClaim(new Claim(SteamAuthenticationConstants.SteamIdClaim, requestJson.SteamId.ToString()));
// Build and return final ticket
AuthenticationTicket ticket =
new AuthenticationTicket(new ClaimsPrincipal(), SteamAuthenticationConstants.SchemeName);
return AuthenticateResult.Success(ticket);
}
}

View File

@@ -0,0 +1,28 @@
using System.Collections.Concurrent;
namespace SVSim.EmulatedEntrypoint.Services;
public class ShadowverseSessionService
{
private readonly ConcurrentDictionary<string, Guid> _sessionIdToUdid;
public ShadowverseSessionService()
{
_sessionIdToUdid = new();
}
public Guid? GetUdidFromSessionId(string sid)
{
if (_sessionIdToUdid.TryGetValue(sid, out var udid))
{
return udid;
}
return null;
}
public void StoreUdidForSessionId(string sid, Guid udid)
{
_sessionIdToUdid.AddOrUpdate(sid, _ => udid, (_, _) => udid);
}
}

View File

@@ -1,12 +0,0 @@
namespace SVSim.EmulatedEntrypoint;
public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}

View File

@@ -6,5 +6,8 @@
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
}
},
"ConnectionStrings": {
"Sqlite": "Data Source=test_db"
},
"AllowedHosts": "*"
}

Binary file not shown.

Binary file not shown.

Binary file not shown.