Add encryption routines to the server
This commit is contained in:
parent
d7cc626144
commit
38eb7c6acd
6 changed files with 472 additions and 13 deletions
|
@ -3,7 +3,9 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:libac_dart/utils/Hashing.dart';
|
||||
import 'package:libac_dart/encryption/aes.dart';
|
||||
import 'package:libac_dart/encryption/rsa.dart';
|
||||
import 'package:libac_dart/encryption/xxtea.dart';
|
||||
|
||||
import '../nbt/NbtIo.dart';
|
||||
import '../nbt/Stream.dart';
|
||||
|
@ -11,23 +13,67 @@ import '../nbt/Tag.dart';
|
|||
import '../nbt/impl/CompoundTag.dart';
|
||||
import '../nbt/impl/StringTag.dart';
|
||||
|
||||
enum EncryptionType {
|
||||
RSA(value: 3),
|
||||
AES(value: 2),
|
||||
XXTEA(value: 1),
|
||||
NONE(value: 0);
|
||||
|
||||
final int value;
|
||||
const EncryptionType({required this.value});
|
||||
|
||||
static EncryptionType valueOf(int val) {
|
||||
switch (val) {
|
||||
case 1:
|
||||
{
|
||||
return EncryptionType.XXTEA;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
return EncryptionType.AES;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
return EncryptionType.RSA;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return EncryptionType.NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PacketServer {
|
||||
static ServerSocket? socket;
|
||||
static bool shouldRestart = true;
|
||||
static EncryptionType encryptionType = EncryptionType.NONE;
|
||||
static Uint8List AESKey = Uint8List(0);
|
||||
static RSAPrivateKey rsaPrivateKey = RSAPrivateKey(BigInt.zero, BigInt.zero);
|
||||
static RSAPublicKey rsaPublicKey = RSAPublicKey(BigInt.zero, BigInt.zero);
|
||||
static String PSK = "";
|
||||
static String TEA_SALT = "Harbinger 01/05/2025 @ 03:59:17 AM";
|
||||
|
||||
/// Version of the packets system. Bumped when there is a major change to protocol
|
||||
static const VERSION = 2;
|
||||
|
||||
/// Packet Data Format:
|
||||
///
|
||||
/// 8 Bytes (Long) - Total expected bytes of packet minus the 8 bytes here.
|
||||
/// 4 bytes (int) - Version
|
||||
/// 8 Bytes (Long) - Total expected bytes of packet
|
||||
/// 8 bytes (Long) - Sequence ID
|
||||
/// 1 byte - Encryption Type
|
||||
/// 8 bytes (Long) - Number of bytes to read
|
||||
/// <arbitrary> - NBT Data
|
||||
/// <arbitrary> - NBT Data / Encrypted NBT Data
|
||||
///
|
||||
/// Response Format:
|
||||
///
|
||||
/// 8 bytes (Long) - Total expected bytes in packet minus the 8 bytes here.
|
||||
/// 4 Bytes (int) - Version
|
||||
/// 8 bytes (Long) - Total expected bytes in packet
|
||||
/// 1 byte - Success flag, Zero or 255 currently.
|
||||
/// 1 byte - Encryption Type
|
||||
/// 8 byes (Long) - Packet Length
|
||||
/// <arbitrary> - NBT Data
|
||||
/// <arbitrary> - NBT Data / Encrypted NBT Data
|
||||
///
|
||||
static Future<void> start(int port) async {
|
||||
socket = await ServerSocket.bind(InternetAddress.anyIPv4, port);
|
||||
|
@ -45,8 +91,10 @@ class PacketServer {
|
|||
layer.writeBytes(data);
|
||||
var oldPos = layer.currentPosition;
|
||||
layer.resetPosition();
|
||||
int version = layer.readInt();
|
||||
|
||||
int pktTotalExpected = layer.readLong();
|
||||
if (pktTotalExpected + 8 <= layer.length) {
|
||||
if (pktTotalExpected <= layer.length) {
|
||||
// Allow Processing
|
||||
} else {
|
||||
layer.restorePosition(oldPos);
|
||||
|
@ -54,14 +102,30 @@ class PacketServer {
|
|||
}
|
||||
|
||||
layer.resetPosition();
|
||||
layer.readInt();
|
||||
layer.readLong(); // This is unused outside of the above sanity check.
|
||||
try {
|
||||
int encryptType = layer.readByte();
|
||||
EncryptionType ENCType = EncryptionType.valueOf(encryptType);
|
||||
|
||||
int sequenceID = layer.readLong();
|
||||
print("Sequence ID in request: $sequenceID");
|
||||
|
||||
int numBytes = layer.readLong();
|
||||
|
||||
List<int> remainingBytes = layer.readBytes(numBytes);
|
||||
if (ENCType == EncryptionType.AES) {
|
||||
AES aes = await AES.useKey(AESKey);
|
||||
remainingBytes = aes.decrypt(Uint8List.fromList(remainingBytes));
|
||||
} else if (ENCType == EncryptionType.RSA) {
|
||||
RSA rsa = await RSA.fromKeyPair(rsaPrivateKey, rsaPublicKey);
|
||||
remainingBytes = rsa.decrypt(Uint8List.fromList(remainingBytes));
|
||||
} else if (ENCType == EncryptionType.XXTEA) {
|
||||
XXTEA xtea = await XXTEA.fromPassword(PSK, salt: TEA_SALT);
|
||||
|
||||
remainingBytes =
|
||||
xtea.decryptBytes(Uint8List.fromList(remainingBytes));
|
||||
}
|
||||
|
||||
CompoundTag tag =
|
||||
await NbtIo.readFromStream(Uint8List.fromList(remainingBytes));
|
||||
|
@ -85,13 +149,28 @@ class PacketServer {
|
|||
layer.clear();
|
||||
layer.writeLong(sequenceID);
|
||||
layer.writeByte(0xFF); // Successful receipt
|
||||
layer.writeByte(ENCType.value);
|
||||
|
||||
// Encryption Subroutine
|
||||
if (ENCType == EncryptionType.AES) {
|
||||
AES aes = AES.useKey(AESKey);
|
||||
nbtData = aes.encrypt(nbtData);
|
||||
} else if (ENCType == EncryptionType.RSA) {
|
||||
RSA rsa = RSA.fromKeyPair(rsaPrivateKey, rsaPublicKey);
|
||||
nbtData = rsa.encrypt(nbtData);
|
||||
} else if (ENCType == EncryptionType.XXTEA) {
|
||||
XXTEA tea = await XXTEA.fromPassword(PSK, salt: TEA_SALT);
|
||||
nbtData = tea.encryptBytes(nbtData);
|
||||
}
|
||||
|
||||
layer.writeLong(nbtData.lengthInBytes);
|
||||
layer.writeBytes(nbtData);
|
||||
nbtData = layer.bytes;
|
||||
|
||||
// NOTE: Added a length indicator because SocketServer is apparently... really really dumb in its impl, and has no way to know when all data has been received, so no special event. We just have to check for it based on this initial value.
|
||||
layer.clear();
|
||||
layer.writeLong(nbtData.lengthInBytes);
|
||||
layer.writeInt(VERSION);
|
||||
layer.writeLong(nbtData.lengthInBytes + layer.currentPosition + 8);
|
||||
layer.writeBytes(nbtData);
|
||||
|
||||
sock.add(layer.bytes);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue