Skip to content

c2c packet improvements #485

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 8, 2023
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package net.earthcomputer.clientcommands.c2c;

import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.network.PacketByteBuf;

public interface C2CPacket {
void write(StringBuf buf);
void write(PacketByteBuf buf);

void apply(CCPacketListener listener);
void apply(CCPacketListener listener) throws CommandSyntaxException;
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package net.earthcomputer.clientcommands.c2c;

import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.logging.LogUtils;
import io.netty.buffer.Unpooled;
import net.earthcomputer.clientcommands.c2c.packets.MessageC2CPacket;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.encryption.PlayerPublicKey;
import net.minecraft.network.encryption.PublicPlayerSession;
import net.minecraft.text.MutableText;
Expand All @@ -17,7 +20,7 @@

public class CCNetworkHandler implements CCPacketListener {

private static final SimpleCommandExceptionType MESSAGE_TOO_LONG_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("ccpacket.messageTooLong"));
private static final DynamicCommandExceptionType MESSAGE_TOO_LONG_EXCEPTION = new DynamicCommandExceptionType(d -> Text.translatable("ccpacket.messageTooLong", d));
private static final SimpleCommandExceptionType PUBLIC_KEY_NOT_FOUND_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("ccpacket.publicKeyNotFound"));
private static final SimpleCommandExceptionType ENCRYPTION_FAILED_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("ccpacket.encryptionFailed"));

Expand Down Expand Up @@ -47,21 +50,36 @@ public void sendPacket(C2CPacket packet, PlayerListEntry recipient) throws Comma
throw PUBLIC_KEY_NOT_FOUND_EXCEPTION.create();
}
PublicKey key = ppk.data().key();
StringBuf buf = new StringBuf();
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
buf.writeInt(id);
packet.write(buf);
byte[] compressed = ConversionHelper.Gzip.compress(buf.bytes());
if (compressed.length > 245) {
throw MESSAGE_TOO_LONG_EXCEPTION.create();
byte[] compressed = ConversionHelper.Gzip.compress(buf.getWrittenBytes());
// split compressed into 245 byte chunks
int chunks = (compressed.length + 244) / 245;
byte[][] chunked = new byte[chunks][];
for (int i = 0; i < chunks; i++) {
int start = i * 245;
int end = Math.min(start + 245, compressed.length);
chunked[i] = new byte[end - start];
System.arraycopy(compressed, start, chunked[i], 0, end - start);
}
byte[] encrypted = ConversionHelper.RsaEcb.encrypt(compressed, key);
if (encrypted == null || encrypted.length == 0) {
throw ENCRYPTION_FAILED_EXCEPTION.create();
// encrypt each chunk
byte[][] encrypted = new byte[chunks][];
for (int i = 0; i < chunks; i++) {
encrypted[i] = ConversionHelper.RsaEcb.encrypt(chunked[i], key);
if (encrypted[i] == null || encrypted[i].length == 0) {
throw ENCRYPTION_FAILED_EXCEPTION.create();
}
}
String packetString = ConversionHelper.BaseUTF8.toUnicode(encrypted);
// join encrypted chunks into one byte array
byte[] joined = new byte[encrypted.length * 256];
for (int i = 0; i < encrypted.length; i++) {
System.arraycopy(encrypted[i], 0, joined, i * 256, 256);
}
String packetString = ConversionHelper.BaseUTF8.toUnicode(joined);
String commandString = "w " + recipient.getProfile().getName() + " CCENC:" + packetString;
if (commandString.length() >= 256) {
throw MESSAGE_TOO_LONG_EXCEPTION.create();
throw MESSAGE_TOO_LONG_EXCEPTION.create(commandString.length());
}
MinecraftClient.getInstance().getNetworkHandler().sendChatCommand(commandString);
OutgoingPacketFilter.addPacket(packetString);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.earthcomputer.clientcommands.c2c.packets.MessageC2CPacket;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.Util;
import org.jetbrains.annotations.Nullable;

Expand All @@ -13,13 +14,13 @@
public class CCPacketHandler {

private static final Object2IntMap<Class<? extends C2CPacket>> packetIds = Util.make(new Object2IntOpenHashMap<>(), map -> map.defaultReturnValue(-1));
private static final List<Function<StringBuf, ? extends C2CPacket>> packetFactories = new ArrayList<>();
private static final List<Function<PacketByteBuf, ? extends C2CPacket>> packetFactories = new ArrayList<>();

static {
CCPacketHandler.register(MessageC2CPacket.class, MessageC2CPacket::new);
}

public static <P extends C2CPacket> void register(Class<P> packet, Function<StringBuf, P> packetFactory) {
public static <P extends C2CPacket> void register(Class<P> packet, Function<PacketByteBuf, P> packetFactory) {
int id = packetFactories.size();
int i = packetIds.put(packet, id);
if (i != -1) {
Expand All @@ -36,8 +37,8 @@ public static <P extends C2CPacket> Integer getId(Class<P> packet) {
}

@Nullable
public static C2CPacket createPacket(int id, StringBuf buf) {
Function<StringBuf, ? extends C2CPacket> function = packetFactories.get(id);
public static C2CPacket createPacket(int id, PacketByteBuf buf) {
Function<PacketByteBuf, ? extends C2CPacket> function = packetFactories.get(id);
return function == null ? null : function.apply(buf);
}
}
45 changes: 0 additions & 45 deletions src/main/java/net/earthcomputer/clientcommands/c2c/StringBuf.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import net.earthcomputer.clientcommands.c2c.C2CPacket;
import net.earthcomputer.clientcommands.c2c.CCPacketListener;
import net.earthcomputer.clientcommands.c2c.StringBuf;
import net.minecraft.network.PacketByteBuf;

import java.nio.ByteBuffer;

public class MessageC2CPacket implements C2CPacket {

Expand All @@ -14,13 +16,13 @@ public MessageC2CPacket(String sender, String message) {
this.message = message;
}

public MessageC2CPacket(StringBuf raw) {
public MessageC2CPacket(PacketByteBuf raw) {
this.sender = raw.readString();
this.message = raw.readString();
}

@Override
public void write(StringBuf buf) {
public void write(PacketByteBuf buf) {
buf.writeString(this.sender);
buf.writeString(this.message);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package net.earthcomputer.clientcommands.mixin;

import com.mojang.brigadier.exceptions.CommandSyntaxException;
import io.netty.buffer.Unpooled;
import net.earthcomputer.clientcommands.TempRules;
import net.earthcomputer.clientcommands.c2c.*;
import net.earthcomputer.clientcommands.interfaces.IHasPrivateKey;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.MessageIndicator;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.message.MessageSignatureData;
import net.minecraft.text.HoverEvent;
import net.minecraft.text.Text;
Expand All @@ -17,7 +20,6 @@
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.Arrays;
import java.util.Optional;
Expand Down Expand Up @@ -61,29 +63,57 @@ private void handleIfPacket(Text content, CallbackInfo ci) {

private static boolean handleC2CPacket(String content) {
byte[] encrypted = ConversionHelper.BaseUTF8.fromUnicode(content);
encrypted = Arrays.copyOf(encrypted, 256);
// round down to multiple of 256 bytes
int length = encrypted.length & ~0xFF;
// copy to new array of arrays
byte[][] encryptedArrays = new byte[length / 256][];
for (int i = 0; i < length; i += 256) {
encryptedArrays[i / 256] = Arrays.copyOfRange(encrypted, i, i + 256);
}
if (!(MinecraftClient.getInstance().getProfileKeys() instanceof IHasPrivateKey privateKeyHolder)) {
return false;
}
Optional<PrivateKey> key = privateKeyHolder.getPrivateKey();
if (key.isEmpty()) {
return false;
}
byte[] decrypted = ConversionHelper.RsaEcb.decrypt(encrypted, key.get());
if (decrypted == null) {
return false;
// decrypt
int len = 0;
byte[][] decryptedArrays = new byte[encryptedArrays.length][];
for (int i = 0; i < encryptedArrays.length; i++) {
decryptedArrays[i] = ConversionHelper.RsaEcb.decrypt(encryptedArrays[i], key.get());
if (decryptedArrays[i] == null) {
return false;
}
len += decryptedArrays[i].length;
}
// copy to new array
byte[] decrypted = new byte[len];
int pos = 0;
for (byte[] decryptedArray : decryptedArrays) {
System.arraycopy(decryptedArray, 0, decrypted, pos, decryptedArray.length);
pos += decryptedArray.length;
}
byte[] uncompressed = ConversionHelper.Gzip.uncompress(decrypted);
StringBuf buf = new StringBuf(new String(uncompressed, StandardCharsets.UTF_8));
PacketByteBuf buf = new PacketByteBuf(Unpooled.wrappedBuffer(uncompressed));
int id = buf.readInt();
C2CPacket c2CPacket = CCPacketHandler.createPacket(id, buf);
if (c2CPacket == null) {
return false;
}
if (buf.getRemainingLength() > 0) {
if (buf.readableBytes() > 0) {
return false;
}
c2CPacket.apply(CCNetworkHandler.getInstance());
try {
c2CPacket.apply(CCNetworkHandler.getInstance());
} catch (CommandSyntaxException e) {
if (e.getRawMessage() instanceof Text) {
MinecraftClient.getInstance().inGameHud.getChatHud().addMessage((Text) e.getRawMessage());
}
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
}
2 changes: 1 addition & 1 deletion src/main/resources/assets/clientcommands/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@
"snakeGame.title": "Snake",
"snakeGame.score": "Score: %d",

"ccpacket.messageTooLong": "Message too long (max. 255 characters)",
"ccpacket.messageTooLong": "Message too long (max. 255 characters) got %s characters",
"ccpacket.publicKeyNotFound": "Public key not found",
"ccpacket.encryptionFailed": "Something failed while encrypting your message",
"ccpacket.malformedPacket": "You have received a malformed C2C packet:",
Expand Down