From c282d42c61292c4f56bc7ceb5d1a06f3145d8dd9 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Sun, 13 Aug 2023 22:27:24 +0200 Subject: [PATCH] feat: login handling & proper password storage --- Server/DB/Documents/Account.cs | 6 ++-- Server/PacketHandlers/LoginHandler.cs | 36 ++++++++++++++++--- Server/Packets/Incoming/LoginInfoPacket.cs | 4 +-- .../Packets/Outgoing/LoginResponseReason.cs | 2 +- Server/Server.csproj | 7 ++++ Server/Services/PacketDistributorService.cs | 8 ++--- 6 files changed, 48 insertions(+), 15 deletions(-) diff --git a/Server/DB/Documents/Account.cs b/Server/DB/Documents/Account.cs index 0eb5643..8914684 100644 --- a/Server/DB/Documents/Account.cs +++ b/Server/DB/Documents/Account.cs @@ -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; } } diff --git a/Server/PacketHandlers/LoginHandler.cs b/Server/PacketHandlers/LoginHandler.cs index 8478b86..a85f34a 100644 --- a/Server/PacketHandlers/LoginHandler.cs +++ b/Server/PacketHandlers/LoginHandler.cs @@ -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 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("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(), "", + 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 diff --git a/Server/Packets/Incoming/LoginInfoPacket.cs b/Server/Packets/Incoming/LoginInfoPacket.cs index 59a2f6a..3181457 100644 --- a/Server/Packets/Incoming/LoginInfoPacket.cs +++ b/Server/Packets/Incoming/LoginInfoPacket.cs @@ -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() diff --git a/Server/Packets/Outgoing/LoginResponseReason.cs b/Server/Packets/Outgoing/LoginResponseReason.cs index e58b5f6..574513a 100644 --- a/Server/Packets/Outgoing/LoginResponseReason.cs +++ b/Server/Packets/Outgoing/LoginResponseReason.cs @@ -3,7 +3,7 @@ public enum LoginResponseReason : byte { Ok, - AcountNotExist, + AccountDoesNotExit, WrongPassword, Error, DuplicateConnection, diff --git a/Server/Server.csproj b/Server/Server.csproj index 3c0fdfa..108f613 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -30,6 +30,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + all @@ -70,4 +71,10 @@ + + + + PreserveNewest + + diff --git a/Server/Services/PacketDistributorService.cs b/Server/Services/PacketDistributorService.cs index b1f3ab1..e2dd552 100644 --- a/Server/Services/PacketDistributorService.cs +++ b/Server/Services/PacketDistributorService.cs @@ -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)