Skip to content

Commit 3b2ae4a

Browse files
authored
c2c packet improvements (#485)
* implement over-engineered coinflip * exchange hashes first so player2 cant easily cheat via guess/check * fix self coinflip and multi coinflips with new method * generalize to dice rolling * fix function names * bump to 1024 bytes, add timeout safety. make message too long say calculated length * forgot to make dynamic change on decrypt * missed a spot * fix timeout breaking things * revert dice roll * revert lang file
1 parent 09055af commit 3b2ae4a

File tree

7 files changed

+82
-73
lines changed

7 files changed

+82
-73
lines changed
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package net.earthcomputer.clientcommands.c2c;
22

3+
import com.mojang.brigadier.exceptions.CommandSyntaxException;
4+
import net.minecraft.network.PacketByteBuf;
5+
36
public interface C2CPacket {
4-
void write(StringBuf buf);
7+
void write(PacketByteBuf buf);
58

6-
void apply(CCPacketListener listener);
9+
void apply(CCPacketListener listener) throws CommandSyntaxException;
710
}

src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package net.earthcomputer.clientcommands.c2c;
22

33
import com.mojang.brigadier.exceptions.CommandSyntaxException;
4+
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
45
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
56
import com.mojang.logging.LogUtils;
7+
import io.netty.buffer.Unpooled;
68
import net.earthcomputer.clientcommands.c2c.packets.MessageC2CPacket;
79
import net.minecraft.client.MinecraftClient;
810
import net.minecraft.client.network.PlayerListEntry;
11+
import net.minecraft.network.PacketByteBuf;
912
import net.minecraft.network.encryption.PlayerPublicKey;
1013
import net.minecraft.network.encryption.PublicPlayerSession;
1114
import net.minecraft.text.MutableText;
@@ -17,7 +20,7 @@
1720

1821
public class CCNetworkHandler implements CCPacketListener {
1922

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

@@ -47,21 +50,36 @@ public void sendPacket(C2CPacket packet, PlayerListEntry recipient) throws Comma
4750
throw PUBLIC_KEY_NOT_FOUND_EXCEPTION.create();
4851
}
4952
PublicKey key = ppk.data().key();
50-
StringBuf buf = new StringBuf();
53+
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
5154
buf.writeInt(id);
5255
packet.write(buf);
53-
byte[] compressed = ConversionHelper.Gzip.compress(buf.bytes());
54-
if (compressed.length > 245) {
55-
throw MESSAGE_TOO_LONG_EXCEPTION.create();
56+
byte[] compressed = ConversionHelper.Gzip.compress(buf.getWrittenBytes());
57+
// split compressed into 245 byte chunks
58+
int chunks = (compressed.length + 244) / 245;
59+
byte[][] chunked = new byte[chunks][];
60+
for (int i = 0; i < chunks; i++) {
61+
int start = i * 245;
62+
int end = Math.min(start + 245, compressed.length);
63+
chunked[i] = new byte[end - start];
64+
System.arraycopy(compressed, start, chunked[i], 0, end - start);
5665
}
57-
byte[] encrypted = ConversionHelper.RsaEcb.encrypt(compressed, key);
58-
if (encrypted == null || encrypted.length == 0) {
59-
throw ENCRYPTION_FAILED_EXCEPTION.create();
66+
// encrypt each chunk
67+
byte[][] encrypted = new byte[chunks][];
68+
for (int i = 0; i < chunks; i++) {
69+
encrypted[i] = ConversionHelper.RsaEcb.encrypt(chunked[i], key);
70+
if (encrypted[i] == null || encrypted[i].length == 0) {
71+
throw ENCRYPTION_FAILED_EXCEPTION.create();
72+
}
6073
}
61-
String packetString = ConversionHelper.BaseUTF8.toUnicode(encrypted);
74+
// join encrypted chunks into one byte array
75+
byte[] joined = new byte[encrypted.length * 256];
76+
for (int i = 0; i < encrypted.length; i++) {
77+
System.arraycopy(encrypted[i], 0, joined, i * 256, 256);
78+
}
79+
String packetString = ConversionHelper.BaseUTF8.toUnicode(joined);
6280
String commandString = "w " + recipient.getProfile().getName() + " CCENC:" + packetString;
6381
if (commandString.length() >= 256) {
64-
throw MESSAGE_TOO_LONG_EXCEPTION.create();
82+
throw MESSAGE_TOO_LONG_EXCEPTION.create(commandString.length());
6583
}
6684
MinecraftClient.getInstance().getNetworkHandler().sendChatCommand(commandString);
6785
OutgoingPacketFilter.addPacket(packetString);

src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import it.unimi.dsi.fastutil.objects.Object2IntMap;
44
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
55
import net.earthcomputer.clientcommands.c2c.packets.MessageC2CPacket;
6+
import net.minecraft.network.PacketByteBuf;
67
import net.minecraft.util.Util;
78
import org.jetbrains.annotations.Nullable;
89

@@ -13,13 +14,13 @@
1314
public class CCPacketHandler {
1415

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

1819
static {
1920
CCPacketHandler.register(MessageC2CPacket.class, MessageC2CPacket::new);
2021
}
2122

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

3839
@Nullable
39-
public static C2CPacket createPacket(int id, StringBuf buf) {
40-
Function<StringBuf, ? extends C2CPacket> function = packetFactories.get(id);
40+
public static C2CPacket createPacket(int id, PacketByteBuf buf) {
41+
Function<PacketByteBuf, ? extends C2CPacket> function = packetFactories.get(id);
4142
return function == null ? null : function.apply(buf);
4243
}
4344
}

src/main/java/net/earthcomputer/clientcommands/c2c/StringBuf.java

Lines changed: 0 additions & 45 deletions
This file was deleted.

src/main/java/net/earthcomputer/clientcommands/c2c/packets/MessageC2CPacket.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import net.earthcomputer.clientcommands.c2c.C2CPacket;
44
import net.earthcomputer.clientcommands.c2c.CCPacketListener;
5-
import net.earthcomputer.clientcommands.c2c.StringBuf;
5+
import net.minecraft.network.PacketByteBuf;
6+
7+
import java.nio.ByteBuffer;
68

79
public class MessageC2CPacket implements C2CPacket {
810

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

17-
public MessageC2CPacket(StringBuf raw) {
19+
public MessageC2CPacket(PacketByteBuf raw) {
1820
this.sender = raw.readString();
1921
this.message = raw.readString();
2022
}
2123

2224
@Override
23-
public void write(StringBuf buf) {
25+
public void write(PacketByteBuf buf) {
2426
buf.writeString(this.sender);
2527
buf.writeString(this.message);
2628
}

src/main/java/net/earthcomputer/clientcommands/mixin/MixinChatHud.java

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package net.earthcomputer.clientcommands.mixin;
22

3+
import com.mojang.brigadier.exceptions.CommandSyntaxException;
4+
import io.netty.buffer.Unpooled;
35
import net.earthcomputer.clientcommands.TempRules;
46
import net.earthcomputer.clientcommands.c2c.*;
57
import net.earthcomputer.clientcommands.interfaces.IHasPrivateKey;
68
import net.minecraft.client.MinecraftClient;
79
import net.minecraft.client.gui.hud.ChatHud;
810
import net.minecraft.client.gui.hud.MessageIndicator;
11+
import net.minecraft.network.PacketByteBuf;
912
import net.minecraft.network.message.MessageSignatureData;
1013
import net.minecraft.text.HoverEvent;
1114
import net.minecraft.text.Text;
@@ -17,7 +20,6 @@
1720
import org.spongepowered.asm.mixin.injection.Inject;
1821
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
1922

20-
import java.nio.charset.StandardCharsets;
2123
import java.security.PrivateKey;
2224
import java.util.Arrays;
2325
import java.util.Optional;
@@ -61,29 +63,57 @@ private void handleIfPacket(Text content, CallbackInfo ci) {
6163

6264
private static boolean handleC2CPacket(String content) {
6365
byte[] encrypted = ConversionHelper.BaseUTF8.fromUnicode(content);
64-
encrypted = Arrays.copyOf(encrypted, 256);
66+
// round down to multiple of 256 bytes
67+
int length = encrypted.length & ~0xFF;
68+
// copy to new array of arrays
69+
byte[][] encryptedArrays = new byte[length / 256][];
70+
for (int i = 0; i < length; i += 256) {
71+
encryptedArrays[i / 256] = Arrays.copyOfRange(encrypted, i, i + 256);
72+
}
6573
if (!(MinecraftClient.getInstance().getProfileKeys() instanceof IHasPrivateKey privateKeyHolder)) {
6674
return false;
6775
}
6876
Optional<PrivateKey> key = privateKeyHolder.getPrivateKey();
6977
if (key.isEmpty()) {
7078
return false;
7179
}
72-
byte[] decrypted = ConversionHelper.RsaEcb.decrypt(encrypted, key.get());
73-
if (decrypted == null) {
74-
return false;
80+
// decrypt
81+
int len = 0;
82+
byte[][] decryptedArrays = new byte[encryptedArrays.length][];
83+
for (int i = 0; i < encryptedArrays.length; i++) {
84+
decryptedArrays[i] = ConversionHelper.RsaEcb.decrypt(encryptedArrays[i], key.get());
85+
if (decryptedArrays[i] == null) {
86+
return false;
87+
}
88+
len += decryptedArrays[i].length;
89+
}
90+
// copy to new array
91+
byte[] decrypted = new byte[len];
92+
int pos = 0;
93+
for (byte[] decryptedArray : decryptedArrays) {
94+
System.arraycopy(decryptedArray, 0, decrypted, pos, decryptedArray.length);
95+
pos += decryptedArray.length;
7596
}
7697
byte[] uncompressed = ConversionHelper.Gzip.uncompress(decrypted);
77-
StringBuf buf = new StringBuf(new String(uncompressed, StandardCharsets.UTF_8));
98+
PacketByteBuf buf = new PacketByteBuf(Unpooled.wrappedBuffer(uncompressed));
7899
int id = buf.readInt();
79100
C2CPacket c2CPacket = CCPacketHandler.createPacket(id, buf);
80101
if (c2CPacket == null) {
81102
return false;
82103
}
83-
if (buf.getRemainingLength() > 0) {
104+
if (buf.readableBytes() > 0) {
84105
return false;
85106
}
86-
c2CPacket.apply(CCNetworkHandler.getInstance());
107+
try {
108+
c2CPacket.apply(CCNetworkHandler.getInstance());
109+
} catch (CommandSyntaxException e) {
110+
if (e.getRawMessage() instanceof Text) {
111+
MinecraftClient.getInstance().inGameHud.getChatHud().addMessage((Text) e.getRawMessage());
112+
}
113+
e.printStackTrace();
114+
} catch (Exception e) {
115+
e.printStackTrace();
116+
}
87117
return true;
88118
}
89119
}

src/main/resources/assets/clientcommands/lang/en_us.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@
299299
"snakeGame.title": "Snake",
300300
"snakeGame.score": "Score: %d",
301301

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

0 commit comments

Comments
 (0)