Skip to content

Commit

Permalink
Janky reliable-udp implementation.
Browse files Browse the repository at this point in the history
Added a super janky reliable-udp implementation, it only implements the incoming connection side, as we are only worried about the server right now.

Its enough now to successfully handshake a negotiate a connection. It falls over once the first DAT message is received as we don't handle them yet. Message protocol needs implementing next so we can handle the DAT messages and actually do something useful.
  • Loading branch information
TLeonardUK committed Aug 9, 2021
1 parent 9e085bb commit fb33123
Show file tree
Hide file tree
Showing 18 changed files with 1,101 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// Dark Souls 3 - Open Server

#include "Core/Crypto/CWCUDPCipher.h"
#include "Core/Crypto/CWCClientUDPCipher.h"

#include "Core/Utils/Logging.h"
#include "Core/Utils/Random.h"
#include "Core/Utils/Endian.h"
#include "Core/Utils/Strings.h"

CWCUDPCipher::CWCUDPCipher(const std::vector<uint8_t>& InKey, uint64_t InAuthToken)
// Basically the same as CWCCipher except we include a packet-type and auth token.

CWCClientUDPCipher::CWCClientUDPCipher(const std::vector<uint8_t>& InKey, uint64_t InAuthToken)
: Key(InKey)
, AuthToken(InAuthToken)
{
Expand All @@ -18,8 +20,9 @@ CWCUDPCipher::CWCUDPCipher(const std::vector<uint8_t>& InKey, uint64_t InAuthTok
AuthTokenHeaderBytes.assign(InAuthTokenBytes, InAuthTokenBytes + 8);
}

bool CWCUDPCipher::Encrypt(const std::vector<uint8_t>& Input, std::vector<uint8_t>& Output)
bool CWCClientUDPCipher::Encrypt(const std::vector<uint8_t>& Input, std::vector<uint8_t>& Output)
{
std::vector<uint8_t> AuthToken = AuthTokenHeaderBytes;
std::vector<uint8_t> IV(11, 0);
std::vector<uint8_t> Tag(16, 0);
std::vector<uint8_t> Payload = Input;
Expand All @@ -32,52 +35,55 @@ bool CWCUDPCipher::Encrypt(const std::vector<uint8_t>& Input, std::vector<uint8_
std::vector<uint8_t> Header;
Header.resize(20);
memcpy(Header.data(), IV.data(), 11);
memcpy(Header.data() + 11, AuthTokenHeaderBytes.data(), 8);
memcpy(Header.data() + 11, AuthToken.data(), 8);
memcpy(Header.data() + 19, PacketType.data(), 1);

if (cwc_encrypt_message(IV.data(), 11, Header.data(), Header.size(), (unsigned char*)Payload.data(), Payload.size(), Tag.data(), 16, &CwcContext) == RETURN_ERROR)
{
return false;
}

Output.resize(Payload.size() + 11 + 16 + 1);
Output.resize(Payload.size() + 8 + 11 + 16 + 1);

memcpy(Output.data(), IV.data(), 11);
memcpy(Output.data() + 11, Tag.data(), 16);
memcpy(Output.data() + 11 + 16, PacketType.data(), 1);
memcpy(Output.data() + 11 + 16 + 1, Payload.data(), Payload.size());
memcpy(Output.data(), AuthToken.data(), 8);
memcpy(Output.data() + 8, IV.data(), 11);
memcpy(Output.data() + 8 + 11, Tag.data(), 16);
memcpy(Output.data() + 8 + 11 + 16, PacketType.data(), 1);
memcpy(Output.data() + 8 + 11 + 16 + 1, Payload.data(), Payload.size());

Log("Encrypt: PayloadSize=%i IV=%s Tag=%s Header=%s", Payload.size(), BytesToHex(IV).c_str(), BytesToHex(Tag).c_str(), BytesToHex(Header).c_str());
//Log("EncryptClient: PayloadSize=%i IV=%s Tag=%s Header=%s", Payload.size(), BytesToHex(IV).c_str(), BytesToHex(Tag).c_str(), BytesToHex(Header).c_str());

return true;
}

bool CWCUDPCipher::Decrypt(const std::vector<uint8_t>& Input, std::vector<uint8_t>& Output)
bool CWCClientUDPCipher::Decrypt(const std::vector<uint8_t>& Input, std::vector<uint8_t>& Output)
{
std::vector<uint8_t> AuthToken(8);
std::vector<uint8_t> IV(11);
std::vector<uint8_t> Tag(16);
std::vector<uint8_t> PacketType(1);

// Actually enough data for any data?
if (Input.size() < 11 + 16 + 1 + 1)
if (Input.size() < 8 + 11 + 16 + 1 + 1)
{
return false;
}

Output.resize(Input.size() - 11 - 16 - 1);
Output.resize(Input.size() - 8 - 11 - 16 - 1);

memcpy(IV.data(), Input.data(), 11);
memcpy(Tag.data(), Input.data() + 11, 16);
memcpy(PacketType.data(), Input.data() + 11 + 16, 1);
memcpy(Output.data(), Input.data() + 11 + 16 + 1, Output.size());
memcpy(AuthToken.data(), Input.data(), 8);
memcpy(IV.data(), Input.data() + 8, 11);
memcpy(Tag.data(), Input.data() + 8 + 11, 16);
memcpy(PacketType.data(), Input.data() + 8 + 11 + 16, 1);
memcpy(Output.data(), Input.data() + 8 + 11 + 16 + 1, Output.size());

std::vector<uint8_t> Header;
Header.resize(20);
memcpy(Header.data(), IV.data(), 11);
memcpy(Header.data() + 11, AuthTokenHeaderBytes.data(), 8);
memcpy(Header.data() + 11, AuthToken.data(), 8);
memcpy(Header.data() + 19, PacketType.data(), 1);

Log("Decrypt: PayloadSize=%i IV=%s Tag=%s Header=%s", Output.size(), BytesToHex(IV).c_str(), BytesToHex(Tag).c_str(), BytesToHex(Header).c_str());
//Log("DecryptClient: PayloadSize=%i IV=%s Tag=%s Header=%s", Output.size(), BytesToHex(IV).c_str(), BytesToHex(Tag).c_str(), BytesToHex(Header).c_str());

if (cwc_decrypt_message(IV.data(), 11, Header.data(), Header.size(), (unsigned char*)Output.data(), Output.size(), Tag.data(), 16, &CwcContext) == RETURN_ERROR)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@

#include <vector>

class CWCUDPCipher
class CWCClientUDPCipher
: public Cipher
{
public:

CWCUDPCipher(const std::vector<uint8_t>& key, uint64_t AuthToken);
CWCClientUDPCipher(const std::vector<uint8_t>& key, uint64_t AuthToken);

bool Encrypt(const std::vector<uint8_t>& input, std::vector<uint8_t>& Output) override;
bool Decrypt(const std::vector<uint8_t>& input, std::vector<uint8_t>& Output) override;
Expand Down
80 changes: 80 additions & 0 deletions Source/Server/Core/Crypto/CWCServerUDPCipher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Dark Souls 3 - Open Server

#include "Core/Crypto/CWCServerUDPCipher.h"

#include "Core/Utils/Logging.h"
#include "Core/Utils/Random.h"
#include "Core/Utils/Endian.h"
#include "Core/Utils/Strings.h"

// Basically the same as CWCCipher except for different header verification.

CWCServerUDPCipher::CWCServerUDPCipher(const std::vector<uint8_t>& InKey, uint64_t InAuthToken)
: Key(InKey)
, AuthToken(InAuthToken)
{
cwc_init_and_key(InKey.data(), InKey.size(), &CwcContext);

// Auth token bytes to encode in the header are the reversed auth token.
uint8_t* InAuthTokenBytes = reinterpret_cast<uint8_t*>(&InAuthToken);
AuthTokenHeaderBytes.assign(InAuthTokenBytes, InAuthTokenBytes + 8);
}

bool CWCServerUDPCipher::Encrypt(const std::vector<uint8_t>& Input, std::vector<uint8_t>& Output)
{
std::vector<uint8_t> IV(11, 0);
std::vector<uint8_t> Tag(16, 0);
std::vector<uint8_t> Payload = Input;

FillRandomBytes(IV);

std::vector<uint8_t> Header;
Header.resize(11);
memcpy(Header.data(), IV.data(), 11);

if (cwc_encrypt_message(IV.data(), 11, Header.data(), Header.size(), (unsigned char*)Payload.data(), Payload.size(), Tag.data(), 16, &CwcContext) == RETURN_ERROR)
{
return false;
}

Output.resize(Payload.size() + 11 + 16);

memcpy(Output.data(), IV.data(), 11);
memcpy(Output.data() + 11, Tag.data(), 16);
memcpy(Output.data() + 11 + 16, Payload.data(), Payload.size());

//Log("EncryptServer: PayloadSize=%i IV=%s Tag=%s Header=%s", Payload.size(), BytesToHex(IV).c_str(), BytesToHex(Tag).c_str(), BytesToHex(Header).c_str());

return true;
}

bool CWCServerUDPCipher::Decrypt(const std::vector<uint8_t>& Input, std::vector<uint8_t>& Output)
{
std::vector<uint8_t> IV(11);
std::vector<uint8_t> Tag(16);

// Actually enough data for any data?
if (Input.size() < 11 + 16 + 1)
{
return false;
}

Output.resize(Input.size() - 11 - 16);

memcpy(IV.data(), Input.data(), 11);
memcpy(Tag.data(), Input.data() + 11, 16);
memcpy(Output.data(), Input.data() + 11 + 16, Output.size());

std::vector<uint8_t> Header;
Header.resize(11);
memcpy(Header.data(), IV.data(), 11);

//Log("DecryptServer: PayloadSize=%i IV=%s Tag=%s Header=%s", Output.size(), BytesToHex(IV).c_str(), BytesToHex(Tag).c_str(), BytesToHex(Header).c_str());

if (cwc_decrypt_message(IV.data(), 11, Header.data(), Header.size(), (unsigned char*)Output.data(), Output.size(), Tag.data(), 16, &CwcContext) == RETURN_ERROR)
{
return false;
}

return true;
}
29 changes: 29 additions & 0 deletions Source/Server/Core/Crypto/CWCServerUDPCipher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Dark Souls 3 - Open Server

#pragma once

#include "Core/Crypto/Cipher.h"

#include "cwc.h"

#include <vector>

class CWCServerUDPCipher
: public Cipher
{
public:

CWCServerUDPCipher(const std::vector<uint8_t>& key, uint64_t AuthToken);

bool Encrypt(const std::vector<uint8_t>& input, std::vector<uint8_t>& Output) override;
bool Decrypt(const std::vector<uint8_t>& input, std::vector<uint8_t>& Output) override;

private:
std::vector<uint8_t> Key;

cwc_ctx CwcContext;

uint64_t AuthToken;
std::vector<uint8_t> AuthTokenHeaderBytes;

};
12 changes: 8 additions & 4 deletions Source/Server/Core/Network/NetConnectionUDP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ NetConnectionUDP::NetConnectionUDP(const std::string& InName)
RecieveBuffer.resize(64 * 1024);
}

NetConnectionUDP::NetConnectionUDP(sockaddr_in InDestination, const std::string& InName)
NetConnectionUDP::NetConnectionUDP(SocketType ParentSocket, sockaddr_in InDestination, const std::string& InName)
: Destination(InDestination)
, Name(InName)
, bChild(true)
, Socket(ParentSocket)
{
}

Expand Down Expand Up @@ -159,7 +160,7 @@ bool NetConnectionUDP::Recieve(std::vector<uint8_t>& Buffer, int Offset, int Cou

bool NetConnectionUDP::Send(const std::vector<uint8_t>& Buffer, int Offset, int Count)
{
int Result = sendto(Socket, (char*)RecieveBuffer.data(), RecieveBuffer.size(), 0, (sockaddr*)&Destination, sizeof(sockaddr_in));
int Result = sendto(Socket, (char*)Buffer.data() + Offset, Count, 0, (sockaddr*)&Destination, sizeof(sockaddr_in));
if (Result < 0)
{
#if defined(_WIN32)
Expand Down Expand Up @@ -199,7 +200,10 @@ bool NetConnectionUDP::Disconnect()
return false;
}

closesocket(Socket);
if (!bChild)
{
closesocket(Socket);
}
Socket = INVALID_SOCKET_VALUE;

return false;
Expand Down Expand Up @@ -281,7 +285,7 @@ bool NetConnectionUDP::Pump()
ClientName.resize(64);
snprintf(ClientName.data(), ClientName.size(), "%s{%s:%i}", Name.c_str(), inet_ntoa(SourceAddress.sin_addr), SourceAddress.sin_port);

std::shared_ptr<NetConnectionUDP> NewConnection = std::make_shared<NetConnectionUDP>(SourceAddress, ClientName.data());
std::shared_ptr<NetConnectionUDP> NewConnection = std::make_shared<NetConnectionUDP>(Socket, SourceAddress, ClientName.data());
NewConnection->RecieveQueue.push_back(Packet);
NewConnections.push_back(NewConnection);
ChildConnections.push_back(NewConnection);
Expand Down
2 changes: 1 addition & 1 deletion Source/Server/Core/Network/NetConnectionUDP.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class NetConnectionUDP
#endif

public:
NetConnectionUDP(sockaddr_in DestinationIP, const std::string& InName);
NetConnectionUDP(SocketType ParentSocket, sockaddr_in DestinationIP, const std::string& InName);
NetConnectionUDP(const std::string& InName);
virtual ~NetConnectionUDP();

Expand Down
9 changes: 7 additions & 2 deletions Source/Server/Server.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@
<ClInclude Include="Config\RuntimeConfig.h" />
<ClInclude Include="Core\Crypto\Cipher.h" />
<ClInclude Include="Core\Crypto\CWCCipher.h" />
<ClInclude Include="Core\Crypto\CWCUDPCipher.h" />
<ClInclude Include="Core\Crypto\CWCClientUDPCipher.h" />
<ClInclude Include="Core\Crypto\CWCServerUDPCipher.h" />
<ClInclude Include="Core\Crypto\RSACipher.h" />
<ClInclude Include="Core\Crypto\RSAKeyPair.h" />
<ClInclude Include="Core\Network\NetConnection.h" />
Expand All @@ -118,12 +119,15 @@
<ClInclude Include="Server\Streams\Frpg2MessageStream.h" />
<ClInclude Include="Server\Streams\Frpg2Packet.h" />
<ClInclude Include="Server\Streams\Frpg2PacketStream.h" />
<ClInclude Include="Server\Streams\Frpg2ReliableUdpPacket.h" />
<ClInclude Include="Server\Streams\Frpg2ReliableUdpPacketStream.h" />
<ClInclude Include="Server\Streams\Frpg2UdpPacket.h" />
<ClInclude Include="Server\Streams\Frpg2UdpPacketStream.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Core\Crypto\CWCCipher.cpp" />
<ClCompile Include="Core\Crypto\CWCUDPCipher.cpp" />
<ClCompile Include="Core\Crypto\CWCClientUDPCipher.cpp" />
<ClCompile Include="Core\Crypto\CWCServerUDPCipher.cpp" />
<ClCompile Include="Core\Crypto\RSACipher.cpp" />
<ClCompile Include="Core\Crypto\RSAKeyPair.cpp" />
<ClCompile Include="Core\Network\NetConnectionTCP.cpp" />
Expand All @@ -145,6 +149,7 @@
<ClCompile Include="Server\Server.cpp" />
<ClCompile Include="Server\Streams\Frpg2MessageStream.cpp" />
<ClCompile Include="Server\Streams\Frpg2PacketStream.cpp" />
<ClCompile Include="Server\Streams\Frpg2ReliableUdpPacketStream.cpp" />
<ClCompile Include="Server\Streams\Frpg2UdpPacketStream.cpp" />
</ItemGroup>
<ItemGroup>
Expand Down
19 changes: 17 additions & 2 deletions Source/Server/Server.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,16 @@
<ClInclude Include="Core\Utils\Strings.h">
<Filter>Core\Utils</Filter>
</ClInclude>
<ClInclude Include="Core\Crypto\CWCUDPCipher.h">
<ClInclude Include="Server\Streams\Frpg2ReliableUdpPacket.h">
<Filter>Server\Streams</Filter>
</ClInclude>
<ClInclude Include="Server\Streams\Frpg2ReliableUdpPacketStream.h">
<Filter>Server\Streams</Filter>
</ClInclude>
<ClInclude Include="Core\Crypto\CWCServerUDPCipher.h">
<Filter>Core\Crypto</Filter>
</ClInclude>
<ClInclude Include="Core\Crypto\CWCClientUDPCipher.h">
<Filter>Core\Crypto</Filter>
</ClInclude>
</ItemGroup>
Expand Down Expand Up @@ -210,7 +219,13 @@
<ClCompile Include="Core\Utils\Strings.cpp">
<Filter>Core\Utils</Filter>
</ClCompile>
<ClCompile Include="Core\Crypto\CWCUDPCipher.cpp">
<ClCompile Include="Server\Streams\Frpg2ReliableUdpPacketStream.cpp">
<Filter>Server\Streams</Filter>
</ClCompile>
<ClCompile Include="Core\Crypto\CWCServerUDPCipher.cpp">
<Filter>Core\Crypto</Filter>
</ClCompile>
<ClCompile Include="Core\Crypto\CWCClientUDPCipher.cpp">
<Filter>Core\Crypto</Filter>
</ClCompile>
</ItemGroup>
Expand Down
15 changes: 8 additions & 7 deletions Source/Server/Server/GameService/GameClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

#include "Server/GameService/GameClient.h"
#include "Server/GameService/GameService.h"
#include "Server/Streams/Frpg2UdpPacketStream.h"
#include "Server/Streams/Frpg2UdpPacket.h"
#include "Server/Streams/Frpg2ReliableUdpPacketStream.h"
#include "Server/Streams/Frpg2ReliableUdpPacket.h"

#include "Server/Server.h"

Expand All @@ -17,13 +17,14 @@

#include "Protobuf/Frpg2RequestMessage.pb.h"

GameClient::GameClient(GameService* OwningService, std::shared_ptr<NetConnection> InConnection, const std::vector<uint8_t>& CwcKey, uint64_t AuthToken)
GameClient::GameClient(GameService* OwningService, std::shared_ptr<NetConnection> InConnection, const std::vector<uint8_t>& CwcKey, uint64_t InAuthToken)
: Service(OwningService)
, Connection(InConnection)
, AuthToken(InAuthToken)
{
LastMessageRecievedTime = GetSeconds();

MessageStream = std::make_shared<Frpg2UdpPacketStream>(InConnection, CwcKey, AuthToken);
MessageStream = std::make_shared<Frpg2ReliableUdpPacketStream>(InConnection, CwcKey, AuthToken);
}

bool GameClient::Poll()
Expand Down Expand Up @@ -56,11 +57,11 @@ bool GameClient::Poll()
}

// Process all packets.
Frpg2UdpPacket Packet;
Frpg2ReliableUdpPacket Packet;
while (MessageStream->Recieve(&Packet))
{
// TODO: Refresh authentication state each time a packet is recieved.
Service->RefreshAuthToken(Packet.Header.auth_token);
// Keep authentication token alive while recieving packets.
Service->RefreshAuthToken(AuthToken);
}

return false;
Expand Down
Loading

0 comments on commit fb33123

Please sign in to comment.