Original c++ code :

C# is not my primary language so maybe some things can probably be done easier.
LoginEncryption.cs:
Code:
public sealed class LoginEncryption
{
private static readonly Random Random = new Random(DateTime.Now.Millisecond);
private readonly string _dxHash;
private readonly string _glHash;
private readonly string _version;
/// <summary>
/// Create a new LoginEncryption instance
/// </summary>
/// <param name="dxHash">NostaleClientX.exe hash</param>
/// <param name="glHash">NostaleClient hash</param>
/// <param name="version">Version of the client</param>
public LoginEncryption(string dxHash, string glHash, string version)
{
_dxHash = dxHash;
_glHash = glHash;
_version = version;
}
/// <summary>
/// Decrypt the raw packet (byte array) to a readable string
/// </summary>
/// <param name="bytes">Bytes to decrypt</param>
/// <param name="size">Amount of byte to translate</param>
/// <returns>Decrypted packet as string</returns>
public string Decrypt(byte[] bytes, int size)
{
var output = "";
for (var i = 0; i < size; i++)
{
output += Convert.ToChar(bytes[i] - 0xF);
}
return output;
}
/// <summary>
/// Encrypt the string packet to byte array
/// </summary>
/// <param name="value">String to encrypt</param>
/// <returns>Encrypted packet as byte array</returns>
public byte[] Encrypt(string value)
{
var output = new byte[value.Length + 1];
for (var i = 0; i < value.Length; i++)
{
output[i] = (byte) ((value[i] ^ 0xC3) + 0xF);
}
output[output.Length - 1] = 0xD8;
return output;
}
/// <summary>
/// Create the NoS0575 login packet
/// </summary>
/// <param name="username">Username of the account</param>
/// <param name="password">Password of the account</param>
/// <param name="gameforgeId">Gameforge ID</param>
/// <returns>Packet as string created with the value</returns>
public string CreateLoginPacket(string username, string password, string gameforgeId = "1234-1234-1234-1234")
{
string usernameHash = Cryptography.ToMd5(_dxHash.ToUpper() + _glHash.ToUpper() + username);
string passwordHash = Cryptography.ToSha512(password);
string generatedVersion = "00" + Random.Next(0, 126).ToString("X")
+ Random.Next(0, 126).ToString("X")
+ Random.Next(0, 126).ToString("X")
+ "\v" + _version;
return $"NoS0575 7040266 {username} {passwordHash} {gameforgeId} {generatedVersion} 0 {usernameHash}";
}
public override string ToString()
{
return $"{nameof(_dxHash)}: {_dxHash}, {nameof(_glHash)}: {_glHash}, {nameof(_version)}: {_version}";
}
}
Code:
public sealed class WorldEncryption
{
private static readonly char[] Keys = {' ', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'n'};
public int EncryptionKey { get; }
/// <summary>
/// Create a new WorldEncryption instance
/// </summary>
/// <param name="encryptionKey">Encryption key received by LoginServer</param>
public WorldEncryption(int encryptionKey)
{
EncryptionKey = encryptionKey;
}
/// <summary>
/// Decrypt the raw packet (byte array) to a readable list string
/// </summary>
/// <param name="bytes">Bytes to decrypt</param>
/// <param name="size">Amount of byte to read</param>
/// <returns>Decrypted packet to string list</returns>
public List<string> Decrypt(byte[] bytes, int size)
{
var output = new List<string>();
var currentPacket = "";
var index = 0;
while (index < size)
{
byte currentByte = bytes[index];
index++;
if (currentByte == 0xFF)
{
output.Add(currentPacket);
currentPacket = "";
continue;
}
var length = (byte) (currentByte & 0x7F);
if ((currentByte & 0x80) != 0)
{
while (length != 0)
{
if (index <= size)
{
currentByte = bytes[index];
index++;
var firstIndex = (byte)(((currentByte & 0xF0u) >> 4) - 1);
var first = (byte)(firstIndex != 255 ? firstIndex != 14 ? Keys[firstIndex] : '\u0000' : '?');
if (first != 0x6E)
currentPacket += Convert.ToChar(first);
if (length <= 1)
break;
var secondIndex = (byte)((currentByte & 0xF) - 1);
var second = (byte)(secondIndex != 255 ? secondIndex != 14 ? Keys[secondIndex] : '\u0000' : '?');
if (second != 0x6E)
currentPacket += Convert.ToChar(second);
length -= 2;
}
else
{
length--;
}
}
}
else
{
while (length != 0)
{
if (index < size)
{
currentPacket += Convert.ToChar(bytes[index] ^ 0xFF);
index++;
}
else if (index == size)
{
currentPacket += Convert.ToChar(0xFF);
index++;
}
length--;
}
}
}
return output;
}
/// <summary>
/// Encrypt the string packet to byte array
/// </summary>
/// <param name="value">String to encrypt</param>
/// <param name="session">Define if it's a session packet or not</param>
/// <returns>Encrypted packet as byte array</returns>
public byte[] Encrypt(string value, bool session = false)
{
var output = new List<byte>();
var mask = new string(value.Select(c =>
{
var b = (sbyte) c;
if (c == '#' || c == '/' || c == '%')
return '0';
if ((b -= 0x20) == 0 || (b += unchecked((sbyte) 0xF1)) < 0 || (b -= 0xB) < 0 ||
b - unchecked((sbyte) 0xC5) == 0)
return '1';
return '0';
}).ToArray());
int packetLength = value.Length;
var sequenceCounter = 0;
var currentPosition = 0;
while (currentPosition <= packetLength)
{
int lastPosition = currentPosition;
while (currentPosition < packetLength && mask[currentPosition] == '0')
currentPosition++;
int sequences;
int length;
if (currentPosition != 0)
{
length = currentPosition - lastPosition;
sequences = length / 0x7E;
for (var i = 0; i < length; i++, lastPosition++)
{
if (i == sequenceCounter * 0x7E)
{
if (sequences == 0)
{
output.Add((byte) (length - i));
}
else
{
output.Add(0x7E);
sequences--;
sequenceCounter++;
}
}
output.Add((byte) ((byte) value[lastPosition] ^ 0xFF));
}
}
if (currentPosition >= packetLength)
break;
lastPosition = currentPosition;
while (currentPosition < packetLength && mask[currentPosition] == '1')
currentPosition++;
if (currentPosition == 0) continue;
length = currentPosition - lastPosition;
sequences = length / 0x7E;
for (var i = 0; i < length; i++, lastPosition++)
{
if (i == sequenceCounter * 0x7E)
{
if (sequences == 0)
{
output.Add((byte) ((length - i) | 0x80));
}
else
{
output.Add(0x7E | 0x80);
sequences--;
sequenceCounter++;
}
}
var currentByte = (byte) value[lastPosition];
switch (currentByte)
{
case 0x20:
currentByte = 1;
break;
case 0x2D:
currentByte = 2;
break;
case 0xFF:
currentByte = 0xE;
break;
default:
currentByte -= 0x2C;
break;
}
if (currentByte == 0x00) continue;
if (i % 2 == 0)
output.Add((byte) (currentByte << 4));
else
output[output.Count - 1] = (byte) (output.Last() | currentByte);
}
}
output.Add(0xFF);
var sessionNumber = (sbyte) ((EncryptionKey >> 6) & 0xFF & 0x80000003);
if (sessionNumber < 0)
sessionNumber = (sbyte) (((sessionNumber - 1) | 0xFFFFFFFC) + 1);
var sessionKey = (byte) (EncryptionKey & 0xFF);
if (session)
sessionNumber = -1;
switch (sessionNumber)
{
case 0:
for (var i = 0; i < output.Count; i++)
output[i] = (byte) (output[i] + sessionKey + 0x40);
break;
case 1:
for (var i = 0; i < output.Count; i++)
output[i] = (byte) (output[i] - (sessionKey + 0x40));
break;
case 2:
for (var i = 0; i < output.Count; i++)
output[i] = (byte) ((output[i] ^ 0xC3) + sessionKey + 0x40);
break;
case 3:
for (var i = 0; i < output.Count; i++)
output[i] = (byte) ((output[i] ^ 0xC3) - (sessionKey + 0x40));
break;
default:
for (var i = 0; i < output.Count; i++)
output[i] = (byte) (output[i] + 0x0F);
break;
}
return output.ToArray();
}
}
Code:
public static class Cryptography
{
public static string ToMd5(string value)
{
using (var md5 = MD5.Create())
{
return Hash(value, md5);
}
}
public static string ToSha512(string value)
{
using (var sha512 = SHA512.Create())
{
return Hash(value, sha512);
}
}
private static string Hash(string value, HashAlgorithm hash)
{
var bytes = hash.ComputeHash(Encoding.ASCII.GetBytes(value));
var sb = new StringBuilder();
foreach (byte b in bytes)
{
sb.Append(b.ToString("X2"));
}
return sb.ToString();
}
}






