mirror of
https://github.com/raphaeIl/Novaria.git
synced 2025-12-14 07:24:48 +01:00
dh fully works, refactor packet handling stuff
This commit is contained in:
@@ -1,23 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Novaria.Common.Crypto;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json;
|
||||
using System.Text;
|
||||
using Serilog;
|
||||
using Novaria.Common.Utils;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Novaria.Common.Core;
|
||||
using Proto;
|
||||
using Google.Protobuf;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Net.Sockets;
|
||||
using Novaria.SDKServer.Controllers.Api.ProtocolHandlers;
|
||||
//using Mono.Security.Cryptography;
|
||||
using Novaria.Common.Util;
|
||||
|
||||
namespace Novaria.SDKServer.Controllers.Api
|
||||
{
|
||||
@@ -44,7 +33,7 @@ namespace Novaria.SDKServer.Controllers.Api
|
||||
|
||||
Utils.PrintByteArray(rawPayload);
|
||||
|
||||
Packet requestPacket = ParseRequest(rawPayload);
|
||||
Packet requestPacket = HttpNetworkManager.Instance.ParseRequest(rawPayload);
|
||||
|
||||
Log.Information("");
|
||||
|
||||
@@ -65,7 +54,7 @@ namespace Novaria.SDKServer.Controllers.Api
|
||||
throw new InvalidDataException("invalid protocol msgid");
|
||||
}
|
||||
|
||||
IMessage decodedPayload = DecodePacket(requestType, requestPacket);
|
||||
IMessage decodedPayload = HttpNetworkManager.Instance.DecodePacket(requestType, requestPacket);
|
||||
|
||||
// originally i returned a IMessage from handlers,
|
||||
// but since in this game apprently one req can return diff resp msgids thus they can not be hardcoded in the packet or increments,
|
||||
@@ -88,7 +77,7 @@ namespace Novaria.SDKServer.Controllers.Api
|
||||
|
||||
Log.Information($"Response Packet msgid: {responsePacket.msgId}: {(short)responsePacket.msgId}");
|
||||
|
||||
responsePackeBytes = BuildResponse(responsePacket);
|
||||
responsePackeBytes = HttpNetworkManager.Instance.BuildResponse(responsePacket);
|
||||
|
||||
if (responsePackeBytes == null)
|
||||
{
|
||||
@@ -97,8 +86,6 @@ namespace Novaria.SDKServer.Controllers.Api
|
||||
}
|
||||
|
||||
Log.Information("Sucessfully responsed with a respone packet msgid: " + (short)responsePacket.msgId);
|
||||
//Console.WriteLine("Built bytes: ");
|
||||
//Utils.PrintByteArray(responsePackeBytes);
|
||||
|
||||
Response.Body.Write(responsePackeBytes, 0, responsePackeBytes.Length);
|
||||
|
||||
@@ -115,27 +102,27 @@ namespace Novaria.SDKServer.Controllers.Api
|
||||
Request.Body.CopyTo(memoryStream); // Copy request body to MemoryStream
|
||||
byte[] rawPayload = memoryStream.ToArray(); // Get raw bytes from MemoryStream
|
||||
|
||||
//Utils.PrintByteArray(rawPayload);
|
||||
|
||||
IKEReq ikeRequest = ParseIkeRequest(rawPayload);
|
||||
IKEReq ikeRequest = HttpNetworkManager.Instance.ParseIkeRequest(rawPayload);
|
||||
|
||||
Log.Information("Decoded Packet: " + JsonSerializer.Serialize(ikeRequest));
|
||||
|
||||
AeadTool.CLIENT_PUBLIC_KEY = ikeRequest.PubKey.ToArray();
|
||||
byte[] clientPublicKey = ikeRequest.PubKey.ToArray();
|
||||
|
||||
Log.Information("RECEIVED client public key: ");
|
||||
Utils.PrintByteArray(clientPublicKey);
|
||||
|
||||
AeadTool.key3 = DiffieHellman.Instance.CalculateKey(clientPublicKey);
|
||||
Log.Information("KEY3 (chacha20 key) calculated: ");
|
||||
Utils.PrintByteArray(AeadTool.key3);
|
||||
|
||||
IKEResp ikeResponse = new IKEResp()
|
||||
{
|
||||
PubKey = ByteString.CopyFrom(DiffieHellman.Instance.PublicKey),
|
||||
PubKey = ByteString.CopyFrom(AeadTool.PS_PUBLIC_KEY.Reverse().ToArray()),
|
||||
Token = AeadTool.TOKEN
|
||||
};
|
||||
|
||||
Log.Information("RECEIVED client public key: ");
|
||||
Utils.PrintByteArray(AeadTool.CLIENT_PUBLIC_KEY);
|
||||
|
||||
byte[] calculatedKey = DiffieHellman.Instance.CalculateSharedSecret(AeadTool.CLIENT_PUBLIC_KEY);
|
||||
|
||||
Log.Information("CALCULATED KEY: ");
|
||||
Utils.PrintByteArray(calculatedKey);
|
||||
Log.Information("Sending ps server public key: ");
|
||||
Utils.PrintByteArray(AeadTool.PS_PUBLIC_KEY);
|
||||
|
||||
Packet ikePacket = new Packet()
|
||||
{
|
||||
@@ -143,7 +130,7 @@ namespace Novaria.SDKServer.Controllers.Api
|
||||
msgBody = ikeResponse.ToByteArray()
|
||||
};
|
||||
|
||||
var responsePayload = BuildIkeResponse(ikePacket);
|
||||
var responsePayload = HttpNetworkManager.Instance. BuildIkeResponse(ikePacket);
|
||||
|
||||
Log.Information("Sending ike response packet: " + JsonSerializer.Serialize(ikeResponse));
|
||||
Log.Information("Built bytes: ");
|
||||
@@ -153,311 +140,5 @@ namespace Novaria.SDKServer.Controllers.Api
|
||||
|
||||
return new EmptyResult();
|
||||
}
|
||||
|
||||
public static byte[] BuildIkeResponse(Packet packet)
|
||||
{
|
||||
BinaryWriter packetWriter = new BinaryWriter(new MemoryStream());
|
||||
|
||||
byte[] msgIdBytes = BitConverter.GetBytes(packet.msgId);
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse<byte>(msgIdBytes);
|
||||
}
|
||||
|
||||
packetWriter.Write(msgIdBytes.AsSpan<byte>());
|
||||
packetWriter.Write(packet.msgBody.AsSpan<byte>());
|
||||
|
||||
|
||||
byte[] packetData = ((MemoryStream)packetWriter.BaseStream).ToArray();
|
||||
Span<byte> encryptedPacketData = (new byte[packetData.Length + 16]).AsSpan();
|
||||
|
||||
AeadTool.Encrypt_ChaCha20(encryptedPacketData, AeadTool.FIRST_IKE_KEY, AeadTool.PS_REQUEST_NONCE, packetData, false);
|
||||
|
||||
Log.Information("build: encrypted data:");
|
||||
Utils.PrintByteArray(encryptedPacketData.ToArray());
|
||||
|
||||
BinaryWriter rawResponseWriter = new BinaryWriter(new MemoryStream());
|
||||
rawResponseWriter.Write(AeadTool.PS_REQUEST_NONCE);
|
||||
rawResponseWriter.Write(encryptedPacketData);
|
||||
|
||||
return ((MemoryStream)rawResponseWriter.BaseStream).ToArray();
|
||||
}
|
||||
|
||||
public static T DecodePacket<T>(Packet packet) where T : IMessage
|
||||
{
|
||||
Assembly assembly = Assembly.GetAssembly(typeof(LoginReq));
|
||||
Type targetType = typeof(T);
|
||||
|
||||
PropertyInfo parserProperty = targetType.GetProperty("Parser", BindingFlags.Static | BindingFlags.Public);
|
||||
object parserInstance = parserProperty.GetValue(null);
|
||||
MethodInfo parseFromMethod = parserInstance.GetType().GetMethod("ParseFrom", new[] { typeof(byte[]) });
|
||||
|
||||
IMessage parsedMessage = (IMessage)parseFromMethod.Invoke(parserInstance, new object[] { packet.msgBody });
|
||||
|
||||
if (parsedMessage == null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to parse message.");
|
||||
}
|
||||
|
||||
return (T)parsedMessage;
|
||||
}
|
||||
|
||||
public static IMessage DecodePacket(Type targetType, Packet packet)
|
||||
{
|
||||
PropertyInfo parserProperty = targetType.GetProperty("Parser", BindingFlags.Static | BindingFlags.Public);
|
||||
object parserInstance = parserProperty.GetValue(null);
|
||||
MethodInfo parseFromMethod = parserInstance.GetType().GetMethod("ParseFrom", new[] { typeof(byte[]) });
|
||||
|
||||
IMessage parsedMessage = (IMessage)parseFromMethod.Invoke(parserInstance, new object[] { packet.msgBody });
|
||||
|
||||
if (parsedMessage == null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to parse message.");
|
||||
}
|
||||
|
||||
return parsedMessage;
|
||||
}
|
||||
|
||||
public static byte[] BuildResponse(Packet packet)
|
||||
{
|
||||
BinaryWriter packetWriter = new BinaryWriter(new MemoryStream());
|
||||
|
||||
byte[] msgIdBytes = BitConverter.GetBytes(packet.msgId);
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse<byte>(msgIdBytes);
|
||||
}
|
||||
|
||||
packetWriter.Write(msgIdBytes.AsSpan<byte>());
|
||||
packetWriter.Write(packet.msgBody.AsSpan<byte>());
|
||||
|
||||
byte[] packetData = ((MemoryStream)packetWriter.BaseStream).ToArray();
|
||||
Span<byte> encryptedPacketData = (new byte[packetData.Length + 16]).AsSpan();
|
||||
|
||||
AeadTool.Encrypt_ChaCha20(encryptedPacketData, AeadTool.key3, AeadTool.PS_REQUEST_NONCE, packetData, false);
|
||||
|
||||
Log.Information("build: encrypted data:");
|
||||
Utils.PrintByteArray(encryptedPacketData.ToArray());
|
||||
|
||||
BinaryWriter rawResponseWriter = new BinaryWriter(new MemoryStream());
|
||||
rawResponseWriter.Write(AeadTool.PS_REQUEST_NONCE);
|
||||
rawResponseWriter.Write(encryptedPacketData);
|
||||
|
||||
return ((MemoryStream)rawResponseWriter.BaseStream).ToArray();
|
||||
}
|
||||
|
||||
public static Packet ParseRequest(byte[] encryptedRawPayload)
|
||||
{
|
||||
BinaryReader reader = new BinaryReader(new MemoryStream(encryptedRawPayload));
|
||||
|
||||
byte[] nonceBytes = new byte[12]; // nonce 12 bytes length
|
||||
reader.Read(nonceBytes);
|
||||
|
||||
int packetSize = encryptedRawPayload.Length - nonceBytes.Length; // skip nonce length (12)
|
||||
|
||||
byte[] packetBytes = new byte[packetSize];
|
||||
reader.Read(packetBytes);
|
||||
|
||||
if (reader.BaseStream.Position != encryptedRawPayload.Length)
|
||||
{
|
||||
Log.Error("something went wrong, not all the bytes were read");
|
||||
Log.Error("reader pos: " + reader.BaseStream.Position);
|
||||
Log.Error("original len:" + encryptedRawPayload.Length);
|
||||
}
|
||||
|
||||
Span<byte> decrypt_result = new Span<byte>(new byte[packetSize - 16]); // for chacha20 decrypt, the result is 16 bytes less than the input data
|
||||
|
||||
Span<byte> nonce = nonceBytes.AsSpan();
|
||||
Span<byte> packet_data = packetBytes.AsSpan();
|
||||
|
||||
bool success = AeadTool.Dencrypt_ChaCha20(decrypt_result, AeadTool.key3, nonce, packet_data, null);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Log.Error("something went wrong when chacha20 decrypting the data");
|
||||
}
|
||||
|
||||
byte[] decrypted_bytes = decrypt_result.ToArray();
|
||||
|
||||
// might wanna use reader here
|
||||
byte[] msgid_bytes = decrypted_bytes[10..12]; // two bytes before msg data is msgid
|
||||
Array.Reverse<byte>(msgid_bytes); // should check BitConverter.IsLittleEndian (if true -> reverse, was true on my pc)
|
||||
|
||||
short msgId = BitConverter.ToInt16(msgid_bytes);
|
||||
|
||||
Packet packet = new Packet()
|
||||
{
|
||||
msgId = msgId,
|
||||
msgBody = decrypted_bytes[12..], // 2 + 2 (seqid) + 8 (timestamp)
|
||||
};
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
// used for parsing ike requests in ps (or any request ig) client -> server
|
||||
public static IKEReq ParseIkeRequest(byte[] encryptedRawPayload)
|
||||
{
|
||||
bool flag = true; // original this flag is: flag = msg.msgId == 1, so true
|
||||
|
||||
BinaryReader reader = new BinaryReader(new MemoryStream(encryptedRawPayload));
|
||||
|
||||
byte[] nonceBytes = new byte[12]; // nonce 12 bytes length
|
||||
reader.Read(nonceBytes);
|
||||
|
||||
byte[] aeadBytes = new byte[1]; // aead byte is 1 byte, original field in AeadTool
|
||||
int packetSize = encryptedRawPayload.Length - nonceBytes.Length; // skip nonce length (12)
|
||||
|
||||
if (flag)
|
||||
{
|
||||
reader.Read(aeadBytes); // read to skip in memory, idk whats the use for this
|
||||
packetSize--; // skip in payload
|
||||
}
|
||||
|
||||
byte[] packetBytes = new byte[packetSize];
|
||||
reader.Read(packetBytes);
|
||||
|
||||
if (reader.BaseStream.Position != encryptedRawPayload.Length)
|
||||
{
|
||||
Log.Error("something went wrong, not all the bytes were read");
|
||||
Log.Error("reader pos: " + reader.BaseStream.Position);
|
||||
Log.Error("original len:" + encryptedRawPayload.Length);
|
||||
}
|
||||
|
||||
Span<byte> decrypt_result = new Span<byte>(new byte[packetSize - 16]); // for chacha20 decrypt, the result is 16 bytes less than the input data
|
||||
|
||||
Span<byte> nonce = nonceBytes.AsSpan();
|
||||
Span<byte> packet_data = packetBytes.AsSpan();
|
||||
|
||||
//Console.WriteLine($"Nonce[{nonce.Length}]");
|
||||
//Utils.PrintByteArray(nonce.ToArray());
|
||||
|
||||
//Console.WriteLine($"packet_data[{packet_data.Length}]: ");
|
||||
//Utils.PrintByteArray(packet_data.ToArray());
|
||||
|
||||
//Console.WriteLine($"key[{AeadTool.FIRST_IKE_KEY.Length}]: ");
|
||||
//Utils.PrintByteArray(AeadTool.FIRST_IKE_KEY.ToArray());
|
||||
|
||||
//Console.WriteLine($"Decrypted bytes[{decrypt_result.Length}]: ");
|
||||
|
||||
byte[] associateData = new byte[13]; // this is needed for req decrypt (size: nonce(12) + 1)
|
||||
|
||||
nonceBytes.CopyTo(associateData, 0); // associateData: [nonce, 1], 1 means AesGcm not supported
|
||||
associateData[associateData.Length - 1] = 1;
|
||||
|
||||
bool success = AeadTool.Dencrypt_ChaCha20(decrypt_result, AeadTool.FIRST_IKE_KEY, nonce, packet_data, associateData);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Log.Error("something went wrong when chacha20 decrypting the data");
|
||||
}
|
||||
|
||||
//Utils.PrintByteArray(decrypt_result.ToArray());
|
||||
|
||||
byte[] decrypted_bytes = decrypt_result.ToArray();
|
||||
//Utils.PrintByteArray(decrypted_bytes);
|
||||
|
||||
byte[] msgid_bytes = decrypted_bytes[..2]; // first two bytes is msgid
|
||||
Array.Reverse<byte>(msgid_bytes); // should check BitConverter.IsLittleEndian (if true -> reverse, was true on my pc)
|
||||
|
||||
short msgId = BitConverter.ToInt16(msgid_bytes);
|
||||
|
||||
Packet packet = new Packet()
|
||||
{
|
||||
msgId = msgId,
|
||||
msgBody = decrypted_bytes[2..],
|
||||
};
|
||||
|
||||
//Console.WriteLine("msgid: " + msgId);
|
||||
//Console.WriteLine("msgBody length: " + packet.msgBody.Length);
|
||||
|
||||
IKEReq ike_request = IKEReq.Parser.ParseFrom(packet.msgBody);
|
||||
|
||||
return ike_request;
|
||||
}
|
||||
|
||||
// used to parse offical pcaps, server -> client
|
||||
public static IKEResp ParseIkeResponse(byte[] encryptedRawPayload)
|
||||
{
|
||||
bool flag = false; // false for this pcap ike resp
|
||||
|
||||
BinaryReader reader = new BinaryReader(new MemoryStream(encryptedRawPayload));
|
||||
|
||||
byte[] nonceBytes = new byte[12]; // nonce 12 bytes length
|
||||
reader.Read(nonceBytes);
|
||||
|
||||
byte[] aeadBytes = new byte[1]; // aead byte is 1 byte, original field in AeadTool
|
||||
int packetSize = encryptedRawPayload.Length - nonceBytes.Length; // skip nonce length (12)
|
||||
|
||||
if (flag)
|
||||
{
|
||||
reader.Read(aeadBytes); // read to skip in memory, idk whats the use for this
|
||||
packetSize--; // skip in payload
|
||||
}
|
||||
|
||||
byte[] packetBytes = new byte[packetSize];
|
||||
reader.Read(packetBytes);
|
||||
|
||||
if (reader.BaseStream.Position != encryptedRawPayload.Length)
|
||||
{
|
||||
Log.Error("something went wrong, not all the bytes were read");
|
||||
Log.Error("reader pos: " + reader.BaseStream.Position);
|
||||
Log.Error("original len:" + encryptedRawPayload.Length);
|
||||
}
|
||||
|
||||
Span<byte> decrypt_result = new Span<byte>(new byte[packetSize - 16]); // for chacha20, the result is 16 bytes less than the input data
|
||||
|
||||
Span<byte> nonce = nonceBytes.AsSpan();
|
||||
Span<byte> packet_data = packetBytes.AsSpan();
|
||||
|
||||
bool success = AeadTool.Dencrypt_ChaCha20(decrypt_result, AeadTool.FIRST_IKE_KEY, nonce, packet_data, null); // associateData NULL FOR THIS response pcap
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Log.Error("something went wrong when chacha20 decrypting the data");
|
||||
}
|
||||
|
||||
|
||||
byte[] decrypted_bytes = decrypt_result.ToArray();
|
||||
byte[] msgid_bytes = decrypted_bytes[..2]; // first two bytes is msgid
|
||||
Array.Reverse<byte>(msgid_bytes); // should check BitConverter.IsLittleEndian (if true -> reverse, was true on my pc)
|
||||
|
||||
short msgId = BitConverter.ToInt16(msgid_bytes);
|
||||
|
||||
Packet packet = new Packet()
|
||||
{
|
||||
msgId = msgId,
|
||||
msgBody = decrypted_bytes[2..],
|
||||
};
|
||||
|
||||
IKEResp ike_response = IKEResp.Parser.ParseFrom(packet.msgBody);
|
||||
|
||||
return ike_response;
|
||||
}
|
||||
//private DiffieHellmanManaged SendKey;
|
||||
|
||||
//// put in connection prob
|
||||
//public static void SetAeadKey(byte[] pubKey) // original lead to HttpNetworkManager
|
||||
//{
|
||||
// byte[] array = this.SendKey.DecryptKeyExchange(pubKey);
|
||||
// int num = array.Length;
|
||||
// if (num > 32)
|
||||
// {
|
||||
// num = 32;
|
||||
// }
|
||||
// this.key3 = new byte[32];
|
||||
// Buffer.BlockCopy(array, 0, this.key3, 0, num);
|
||||
// this.HasKey3 = true;
|
||||
// this.seq = 1;
|
||||
//}
|
||||
|
||||
//public byte[] DecryptKeyExchange(byte[] keyEx)
|
||||
//{
|
||||
// BigInteger bigInteger = new BigInteger(keyEx).ModPow(this.m_X, this.m_P);
|
||||
// byte[] bytes = bigInteger.GetBytes();
|
||||
// bigInteger.Clear();
|
||||
// return bytes;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ namespace Novaria.SDKServer.CoNovariaollers
|
||||
""Data"": {
|
||||
""Identity"": {
|
||||
""BirthDate"": """",
|
||||
""IDCard"": ""500*********11861*"",
|
||||
""IDCard"": ""123*********34567*"",
|
||||
""PI"": """",
|
||||
""RealName"": ""*思"",
|
||||
""RealName"": ""**"",
|
||||
""State"": 1,
|
||||
""Type"": 0,
|
||||
""Underage"": false
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Google.Protobuf;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Novaria.Common.Crypto;
|
||||
using Novaria.Common.Utils;
|
||||
using Novaria.Common.Util;
|
||||
using Pb;
|
||||
using Serilog;
|
||||
|
||||
|
||||
@@ -46,9 +46,9 @@ namespace Novaria.SDKServer.CoNovariaollers
|
||||
},
|
||||
""Identity"": {
|
||||
""BirthDate"": """",
|
||||
""IDCard"": ""500*********11861*"",
|
||||
""IDCard"": ""123*********34567*"",
|
||||
""PI"": """",
|
||||
""RealName"": ""*思"",
|
||||
""RealName"": ""**"",
|
||||
""State"": 1,
|
||||
""Type"": 0,
|
||||
""Underage"": false
|
||||
@@ -65,13 +65,13 @@ namespace Novaria.SDKServer.CoNovariaollers
|
||||
""ID"": 1,
|
||||
""PID"": ""CN-NOVA"",
|
||||
""State"": 1,
|
||||
""Token"": ""f94d936f7235f84493564ee0282e845cccd44828""
|
||||
""Token"": ""f94d936f723asdasd5f84493564ee0282e845cccd44828""
|
||||
},
|
||||
""Yostar"": {
|
||||
""CreatedAt"": 1735902342,
|
||||
""CreatedAt"": 1735902322,
|
||||
""DefaultNickName"": """",
|
||||
""ID"": 1,
|
||||
""NickName"": ""夏萝莉是小楠梁"",
|
||||
""NickName"": ""seggs"",
|
||||
""Picture"": """",
|
||||
""State"": 1
|
||||
}
|
||||
@@ -106,13 +106,13 @@ namespace Novaria.SDKServer.CoNovariaollers
|
||||
""ID"": 1,
|
||||
""PID"": ""CN-NOVA"",
|
||||
""State"": 1,
|
||||
""Token"": ""f94d936f7235f84493564ee0282e845cccd44828""
|
||||
""Token"": ""f9s243e483e3e322138""
|
||||
},
|
||||
""Yostar"": {
|
||||
""CreatedAt"": 1,
|
||||
""DefaultNickName"": """",
|
||||
""ID"": 1,
|
||||
""NickName"": ""夏萝莉是小楠梁"",
|
||||
""NickName"": ""seggs"",
|
||||
""Picture"": """",
|
||||
""State"": 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user