feat: login handling & proper password storage
All checks were successful
Test if Server can be built / build-server (push) Successful in 37s
All checks were successful
Test if Server can be built / build-server (push) Successful in 37s
This commit is contained in:
parent
1dd62a0491
commit
c282d42c61
6 changed files with 48 additions and 15 deletions
|
@ -4,16 +4,18 @@ using CouchDB.Driver.Types;
|
|||
|
||||
public class Account : CouchDocument
|
||||
{
|
||||
public Account(string username, string password, string email, byte permissionLevel)
|
||||
public Account(string username, byte[] password, string email, byte permissionLevel, byte[] salt)
|
||||
{
|
||||
this.Username = username;
|
||||
this.Password = password;
|
||||
this.Email = email;
|
||||
this.PermissionLevel = permissionLevel;
|
||||
this.Salt = salt;
|
||||
}
|
||||
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public byte[] Password { get; set; }
|
||||
public string Email { get; set; }
|
||||
public byte PermissionLevel { get; set; }
|
||||
public byte[] Salt { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
namespace Server.PacketHandlers;
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using CouchDB.Driver.Query.Extensions;
|
||||
using DB;
|
||||
using DB.Documents;
|
||||
using DotNext;
|
||||
using Konscious.Security.Cryptography;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetCoreServer;
|
||||
|
@ -23,27 +28,50 @@ public class LoginHandler : IPacketHandler<LoginInfoPacket>
|
|||
|
||||
public async Task HandleAsync(LoginInfoPacket packet, TcpSession session)
|
||||
{
|
||||
var loginResponseReason = LoginResponseReason.Error;
|
||||
this.logger.LogInformation("Login data: Username {Username} & Password {Password}", packet.Username,
|
||||
packet.Password);
|
||||
var account = this.wonderkingContext.Accounts.FirstOrDefault(a => a.Username == packet.Username);
|
||||
|
||||
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
|
||||
// "Use Argon2id with a minimum configuration of 19 MiB of memory, an iteration count of 2, and 1 degree of parallelism."
|
||||
var argon2Id = new Argon2id(Encoding.ASCII.GetBytes(packet.Password));
|
||||
argon2Id.MemorySize = 1024 * 40;
|
||||
argon2Id.Iterations = 4;
|
||||
argon2Id.DegreeOfParallelism = 2;
|
||||
if (account == null)
|
||||
{
|
||||
if (this.configuration.GetSection("Testing").GetValue<bool>("CreateAccountOnLogin"))
|
||||
{
|
||||
var result =
|
||||
this.wonderkingContext.Accounts.AddAsync(new Account(packet.Username, packet.Password, "", 0));
|
||||
await result;
|
||||
argon2Id.Salt = RandomNumberGenerator.GetBytes(128);
|
||||
var finalAccount =
|
||||
await this.wonderkingContext.Accounts.AddAsync(new Account(packet.Username, Array.Empty<byte>(), "",
|
||||
0, argon2Id.Salt));
|
||||
argon2Id.AssociatedData = Guid.Parse(finalAccount.Id).ToByteArray();
|
||||
finalAccount.Password = await argon2Id.GetBytesAsync(128);
|
||||
await this.wonderkingContext.Accounts.AddOrUpdateAsync(finalAccount);
|
||||
loginResponseReason = LoginResponseReason.Ok;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Send Message that account does not exist
|
||||
this.logger.LogInformation("Requested account for user: {Username} does not exist", packet.Username);
|
||||
loginResponseReason = LoginResponseReason.AccountDoesNotExit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
argon2Id.Salt = account.Salt;
|
||||
argon2Id.AssociatedData = Guid.Parse(account.Id).ToByteArray();
|
||||
var tempPasswordBytes = await argon2Id.GetBytesAsync(128);
|
||||
loginResponseReason = tempPasswordBytes.SequenceEqual(account.Password)
|
||||
? LoginResponseReason.Ok
|
||||
: LoginResponseReason.WrongPassword;
|
||||
}
|
||||
|
||||
var loginResponsePacket = new LoginResponsePacket
|
||||
{
|
||||
ResponseReason = LoginResponseReason.Ok,
|
||||
ResponseReason = loginResponseReason,
|
||||
ChannelData = new[] { new ServerChannelData { ChannelId = 0, LoadPercentage = 75, ServerId = 0 } },
|
||||
UnknownFlag = 1,
|
||||
IsGameMaster = true
|
||||
|
|
|
@ -11,8 +11,8 @@ public class LoginInfoPacket : IPacket
|
|||
|
||||
public void Deserialize(byte[] data)
|
||||
{
|
||||
this.Username = Encoding.ASCII.GetString(data, 0, 20);
|
||||
this.Password = Encoding.ASCII.GetString(data, 20, 31);
|
||||
this.Username = Encoding.ASCII.GetString(data, 0, 20).TrimEnd('\0');
|
||||
this.Password = Encoding.ASCII.GetString(data, 20, 31).TrimEnd('\0');
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
public enum LoginResponseReason : byte
|
||||
{
|
||||
Ok,
|
||||
AcountNotExist,
|
||||
AccountDoesNotExit,
|
||||
WrongPassword,
|
||||
Error,
|
||||
DuplicateConnection,
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.2.0"/>
|
||||
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.0" />
|
||||
<PackageReference Include="MassTransit" Version="8.0.16"/>
|
||||
<PackageReference Include="MassTransit.Analyzers" Version="8.0.16">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
@ -70,4 +71,10 @@
|
|||
</PackageReference>
|
||||
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="settings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -102,21 +102,17 @@ public class PacketDistributorService : IHostedService
|
|||
public void AddPacket(RawPacket rawPacket)
|
||||
{
|
||||
this.concurrentQueue.Enqueue(rawPacket);
|
||||
Task.Run(this.DequeueRawPacketAsync);
|
||||
this.DequeueRawPacket();
|
||||
this.logger.LogInformation("Packet with ID: {MessageOperationCode} has been received",
|
||||
rawPacket.OperationCode);
|
||||
}
|
||||
|
||||
private async Task DequeueRawPacketAsync()
|
||||
private void DequeueRawPacket()
|
||||
{
|
||||
if (this.concurrentQueue.TryDequeue(out var item))
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(this.InvokePacketHandler, item, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(100); // Delay to prevent busy-waiting, can be adjusted based on needs
|
||||
}
|
||||
}
|
||||
|
||||
private void InvokePacketHandler(RawPacket item)
|
||||
|
|
Loading…
Reference in a new issue