using System;
using NetcodeIO.NET.Utils;
using NetcodeIO.NET.Utils.IO;
using Org.BouncyCastle.Crypto.TlsExt;
namespace NetcodeIO.NET.Internal
{
///
/// Helper class for reading/writing packets
///
internal static class PacketIO
{
///
/// Read and decrypt packet data into an output buffer
///
public static int ReadPacketData(NetcodePacketHeader header, ByteArrayReaderWriter stream, int length, ulong protocolID, byte[] key, byte[] outputBuffer)
{
byte[] encryptedBuffer = BufferPool.GetBuffer(2048);
stream.ReadBytesIntoBuffer(encryptedBuffer, length);
int decryptedBytes;
try
{
decryptedBytes = DecryptPacketData(header, protocolID, encryptedBuffer, length, key, outputBuffer);
}
catch(Exception e)
{
BufferPool.ReturnBuffer(encryptedBuffer);
throw e;
}
BufferPool.ReturnBuffer(encryptedBuffer);
return decryptedBytes;
}
///
/// Encrypt a packet's data
///
public static int EncryptPacketData(NetcodePacketHeader header, ulong protocolID, byte[] packetData, int packetDataLen, byte[] key, byte[] outBuffer)
{
byte[] additionalData = BufferPool.GetBuffer(Defines.NETCODE_VERSION_INFO_BYTES + 8 + 1);
using (var writer = ByteArrayReaderWriter.Get(additionalData))
{
writer.WriteASCII(Defines.NETCODE_VERSION_INFO_STR);
writer.Write(protocolID);
writer.Write(header.GetPrefixByte());
}
byte[] nonce = BufferPool.GetBuffer(12);
using (var writer = ByteArrayReaderWriter.Get(nonce))
{
writer.Write((UInt32)0);
writer.Write(header.SequenceNumber);
}
int ret;
try
{
ret = AEAD_Chacha20_Poly1305.Encrypt(packetData, 0, packetDataLen, additionalData, nonce, key, outBuffer);
}
catch (Exception e)
{
BufferPool.ReturnBuffer(additionalData);
BufferPool.ReturnBuffer(nonce);
throw e;
}
BufferPool.ReturnBuffer(additionalData);
BufferPool.ReturnBuffer(nonce);
return ret;
}
///
/// Decrypt a packet's data
///
public static int DecryptPacketData(NetcodePacketHeader header, ulong protocolID, byte[] packetData, int packetDataLen, byte[] key, byte[] outBuffer)
{
byte[] additionalData = BufferPool.GetBuffer(Defines.NETCODE_VERSION_INFO_BYTES + 8 + 1);
using (var writer = ByteArrayReaderWriter.Get(additionalData))
{
writer.WriteASCII(Defines.NETCODE_VERSION_INFO_STR);
writer.Write(protocolID);
writer.Write(header.ReadSequenceByte);
}
byte[] nonce = BufferPool.GetBuffer(12);
using (var writer = ByteArrayReaderWriter.Get(nonce))
{
writer.Write((UInt32)0);
writer.Write(header.SequenceNumber);
}
int ret;
try
{
ret = AEAD_Chacha20_Poly1305.Decrypt(packetData, 0, packetDataLen, additionalData, nonce, key, outBuffer);
}
catch(Exception e)
{
BufferPool.ReturnBuffer(additionalData);
BufferPool.ReturnBuffer(nonce);
throw e;
}
BufferPool.ReturnBuffer(additionalData);
BufferPool.ReturnBuffer(nonce);
return ret;
}
///
/// Encrypt a challenge token
///
public static int EncryptChallengeToken(ulong sequenceNum, byte[] packetData, byte[] key, byte[] outBuffer)
{
byte[] additionalData = BufferPool.GetBuffer(0);
byte[] nonce = BufferPool.GetBuffer(12);
using (var writer = ByteArrayReaderWriter.Get(nonce))
{
writer.Write((UInt32)0);
writer.Write(sequenceNum);
}
int ret;
try
{
ret = AEAD_Chacha20_Poly1305.Encrypt(packetData, 0, 300 - Defines.MAC_SIZE, additionalData, nonce, key, outBuffer);
}
catch (Exception e)
{
BufferPool.ReturnBuffer(additionalData);
BufferPool.ReturnBuffer(nonce);
throw e;
}
BufferPool.ReturnBuffer(additionalData);
BufferPool.ReturnBuffer(nonce);
return ret;
}
///
/// Decrypt a challenge token
///
public static int DecryptChallengeToken(ulong sequenceNum, byte[] packetData, byte[] key, byte[] outBuffer)
{
byte[] additionalData = BufferPool.GetBuffer(0);
byte[] nonce = BufferPool.GetBuffer(12);
using (var writer = ByteArrayReaderWriter.Get(nonce))
{
writer.Write((UInt32)0);
writer.Write(sequenceNum);
}
int ret;
try
{
ret = AEAD_Chacha20_Poly1305.Decrypt(packetData, 0, 300, additionalData, nonce, key, outBuffer);
}
catch (Exception e)
{
BufferPool.ReturnBuffer(additionalData);
BufferPool.ReturnBuffer(nonce);
throw e;
}
BufferPool.ReturnBuffer(additionalData);
BufferPool.ReturnBuffer(nonce);
return ret;
}
// Encrypt a private connect token
public static int EncryptPrivateConnectToken(byte[] privateConnectToken, ulong protocolID, ulong expireTimestamp, ulong sequence, byte[] key, byte[] outBuffer)
{
int len = privateConnectToken.Length;
byte[] additionalData = BufferPool.GetBuffer(Defines.NETCODE_VERSION_INFO_BYTES + 8 + 8);
using (var writer = ByteArrayReaderWriter.Get(additionalData))
{
writer.WriteASCII(Defines.NETCODE_VERSION_INFO_STR);
writer.Write(protocolID);
writer.Write(expireTimestamp);
}
byte[] nonce = BufferPool.GetBuffer(12);
using (var writer = ByteArrayReaderWriter.Get(nonce))
{
writer.Write((UInt32)0);
writer.Write(sequence);
}
var ret = AEAD_Chacha20_Poly1305.Encrypt(privateConnectToken, 0, len - Defines.MAC_SIZE, additionalData, nonce, key, outBuffer);
BufferPool.ReturnBuffer(additionalData);
BufferPool.ReturnBuffer(nonce);
return ret;
}
// Decrypt a private connect token
public static int DecryptPrivateConnectToken(byte[] encryptedConnectToken, ulong protocolID, ulong expireTimestamp, ulong sequence, byte[] key, byte[] outBuffer)
{
int len = encryptedConnectToken.Length;
byte[] additionalData = BufferPool.GetBuffer(Defines.NETCODE_VERSION_INFO_BYTES + 8 + 8);
using (var writer = ByteArrayReaderWriter.Get(additionalData))
{
writer.WriteASCII(Defines.NETCODE_VERSION_INFO_STR);
writer.Write(protocolID);
writer.Write(expireTimestamp);
}
byte[] nonce = BufferPool.GetBuffer(12);
using (var writer = ByteArrayReaderWriter.Get(nonce))
{
writer.Write((UInt32)0);
writer.Write(sequence);
}
var ret = AEAD_Chacha20_Poly1305.Decrypt(encryptedConnectToken, 0, len, additionalData, nonce, key, outBuffer);
BufferPool.ReturnBuffer(additionalData);
BufferPool.ReturnBuffer(nonce);
return ret;
}
}
}