Skip to content

Commit

Permalink
💥 Attempt to avoid decryption error
Browse files Browse the repository at this point in the history
  • Loading branch information
acrylic-style committed Aug 26, 2022
1 parent e527443 commit f69b301
Show file tree
Hide file tree
Showing 14 changed files with 161 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import net.azisaba.azipluginmessaging.api.protocol.handler.ProxyboundCheckRankExpirationPacket;
import net.azisaba.azipluginmessaging.api.protocol.handler.ProxyboundClearPrefixPacket;
import net.azisaba.azipluginmessaging.api.protocol.handler.ProxyboundEncryptionPacket;
import net.azisaba.azipluginmessaging.api.protocol.handler.ProxyboundEncryptionResponsePacket;
import net.azisaba.azipluginmessaging.api.protocol.handler.ProxyboundGiveGamingSaraPacket;
import net.azisaba.azipluginmessaging.api.protocol.handler.ProxyboundGiveNitroSaraPacket;
import net.azisaba.azipluginmessaging.api.protocol.handler.ProxyboundGiveSaraPacket;
Expand All @@ -21,6 +22,8 @@
import net.azisaba.azipluginmessaging.api.protocol.handler.ServerboundActionResponsePacket;
import net.azisaba.azipluginmessaging.api.protocol.handler.ServerboundCheckRankExpirationPacket;
import net.azisaba.azipluginmessaging.api.protocol.handler.ServerboundEncryptionPacket;
import net.azisaba.azipluginmessaging.api.protocol.handler.ServerboundEncryptionResponsePacket;
import net.azisaba.azipluginmessaging.api.protocol.message.EmptyMessage;
import net.azisaba.azipluginmessaging.api.protocol.message.EncryptionMessage;
import net.azisaba.azipluginmessaging.api.protocol.message.Message;
import net.azisaba.azipluginmessaging.api.protocol.message.PlayerMessage;
Expand All @@ -47,6 +50,7 @@
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

public final class Protocol<T extends MessageHandler<M>, M extends Message> {
Expand All @@ -64,21 +68,23 @@ public final class Protocol<T extends MessageHandler<M>, M extends Message> {
public static final String CHANNEL_ID = "azipm:main";

public static final Protocol<ProxyboundEncryptionPacket, EncryptionMessage> P_ENCRYPTION = new Protocol<>(PacketFlow.TO_PROXY, 0x00, new ProxyboundEncryptionPacket());
public static final Protocol<ProxyboundSetRankPacket, ProxyboundSetRankMessage> P_SET_RANK = new Protocol<>(PacketFlow.TO_PROXY, 0x01, new ProxyboundSetRankPacket());
public static final Protocol<ProxyboundGiveGamingSaraPacket, PlayerMessage> P_GIVE_GAMING_SARA = new Protocol<>(PacketFlow.TO_PROXY, 0x02, new ProxyboundGiveGamingSaraPacket());
public static final Protocol<ProxyboundGiveSaraPacket, ProxyboundGiveSaraMessage> P_GIVE_SARA = new Protocol<>(PacketFlow.TO_PROXY, 0x03, new ProxyboundGiveSaraPacket());
public static final Protocol<ProxyboundToggleGamingSaraPacket, PlayerMessage> P_TOGGLE_GAMING_SARA = new Protocol<>(PacketFlow.TO_PROXY, 0x04, new ProxyboundToggleGamingSaraPacket());
public static final Protocol<ProxyboundToggleSaraHidePacket, PlayerMessage> P_TOGGLE_SARA_HIDE = new Protocol<>(PacketFlow.TO_PROXY, 0x05, new ProxyboundToggleSaraHidePacket()); // non-contextual
public static final Protocol<ProxyboundToggleSaraShowPacket, PlayerWithServerMessage> P_TOGGLE_SARA_SHOW = new Protocol<>(PacketFlow.TO_PROXY, 0x06, new ProxyboundToggleSaraShowPacket()); // contextual
public static final Protocol<ProxyboundSetPrefixPacket, ProxyboundSetPrefixMessage> P_SET_PREFIX = new Protocol<>(PacketFlow.TO_PROXY, 0x07, new ProxyboundSetPrefixPacket()); // may be contextual
public static final Protocol<ProxyboundClearPrefixPacket, ProxyboundClearPrefixMessage> P_CLEAR_PREFIX = new Protocol<>(PacketFlow.TO_PROXY, 0x08, new ProxyboundClearPrefixPacket()); // may be contextual
public static final Protocol<ProxyboundGiveNitroSaraPacket, ProxyboundGiveNitroSaraMessage> P_GIVE_NITRO_SARA = new Protocol<>(PacketFlow.TO_PROXY, 0x09, new ProxyboundGiveNitroSaraPacket());
public static final Protocol<ProxyboundToggleNitroSaraPacket, PlayerMessage> P_TOGGLE_NITRO_SARA = new Protocol<>(PacketFlow.TO_PROXY, 0x0A, new ProxyboundToggleNitroSaraPacket());
public static final Protocol<ProxyboundCheckRankExpirationPacket, ProxyboundCheckRankExpirationMessage> P_CHECK_RANK_EXPIRATION = new Protocol<>(PacketFlow.TO_PROXY, 0x0B, new ProxyboundCheckRankExpirationPacket());
public static final Protocol<ProxyboundEncryptionResponsePacket, EmptyMessage> P_ENCRYPTION_RESPONSE = new Protocol<>(PacketFlow.TO_PROXY, 0x01, new ProxyboundEncryptionResponsePacket());
public static final Protocol<ProxyboundSetRankPacket, ProxyboundSetRankMessage> P_SET_RANK = new Protocol<>(PacketFlow.TO_PROXY, 0x02, new ProxyboundSetRankPacket());
public static final Protocol<ProxyboundGiveGamingSaraPacket, PlayerMessage> P_GIVE_GAMING_SARA = new Protocol<>(PacketFlow.TO_PROXY, 0x03, new ProxyboundGiveGamingSaraPacket());
public static final Protocol<ProxyboundGiveSaraPacket, ProxyboundGiveSaraMessage> P_GIVE_SARA = new Protocol<>(PacketFlow.TO_PROXY, 0x04, new ProxyboundGiveSaraPacket());
public static final Protocol<ProxyboundToggleGamingSaraPacket, PlayerMessage> P_TOGGLE_GAMING_SARA = new Protocol<>(PacketFlow.TO_PROXY, 0x05, new ProxyboundToggleGamingSaraPacket());
public static final Protocol<ProxyboundToggleSaraHidePacket, PlayerMessage> P_TOGGLE_SARA_HIDE = new Protocol<>(PacketFlow.TO_PROXY, 0x06, new ProxyboundToggleSaraHidePacket()); // non-contextual
public static final Protocol<ProxyboundToggleSaraShowPacket, PlayerWithServerMessage> P_TOGGLE_SARA_SHOW = new Protocol<>(PacketFlow.TO_PROXY, 0x07, new ProxyboundToggleSaraShowPacket()); // contextual
public static final Protocol<ProxyboundSetPrefixPacket, ProxyboundSetPrefixMessage> P_SET_PREFIX = new Protocol<>(PacketFlow.TO_PROXY, 0x08, new ProxyboundSetPrefixPacket()); // may be contextual
public static final Protocol<ProxyboundClearPrefixPacket, ProxyboundClearPrefixMessage> P_CLEAR_PREFIX = new Protocol<>(PacketFlow.TO_PROXY, 0x09, new ProxyboundClearPrefixPacket()); // may be contextual
public static final Protocol<ProxyboundGiveNitroSaraPacket, ProxyboundGiveNitroSaraMessage> P_GIVE_NITRO_SARA = new Protocol<>(PacketFlow.TO_PROXY, 0x0A, new ProxyboundGiveNitroSaraPacket());
public static final Protocol<ProxyboundToggleNitroSaraPacket, PlayerMessage> P_TOGGLE_NITRO_SARA = new Protocol<>(PacketFlow.TO_PROXY, 0x0B, new ProxyboundToggleNitroSaraPacket());
public static final Protocol<ProxyboundCheckRankExpirationPacket, ProxyboundCheckRankExpirationMessage> P_CHECK_RANK_EXPIRATION = new Protocol<>(PacketFlow.TO_PROXY, 0x0C, new ProxyboundCheckRankExpirationPacket());

public static final Protocol<ServerboundEncryptionPacket, EncryptionMessage> S_ENCRYPTION = new Protocol<>(PacketFlow.TO_SERVER, 0x00, new ServerboundEncryptionPacket());
public static final Protocol<ServerboundActionResponsePacket, ServerboundActionResponseMessage> S_ACTION_RESPONSE = new Protocol<>(PacketFlow.TO_SERVER, 0x01, new ServerboundActionResponsePacket());
public static final Protocol<ServerboundCheckRankExpirationPacket, ServerboundCheckRankExpirationMessage> S_CHECK_RANK_EXPIRATION = new Protocol<>(PacketFlow.TO_SERVER, 0x02, new ServerboundCheckRankExpirationPacket());
public static final Protocol<ServerboundEncryptionResponsePacket, EmptyMessage> S_ENCRYPTION_RESPONSE = new Protocol<>(PacketFlow.TO_SERVER, 0x01, new ServerboundEncryptionResponsePacket());
public static final Protocol<ServerboundActionResponsePacket, ServerboundActionResponseMessage> S_ACTION_RESPONSE = new Protocol<>(PacketFlow.TO_SERVER, 0x02, new ServerboundActionResponsePacket());
public static final Protocol<ServerboundCheckRankExpirationPacket, ServerboundCheckRankExpirationMessage> S_CHECK_RANK_EXPIRATION = new Protocol<>(PacketFlow.TO_SERVER, 0x03, new ServerboundCheckRankExpirationPacket());

private final PacketFlow packetFlow;
private final byte id;
Expand Down Expand Up @@ -186,7 +192,8 @@ public boolean sendPacket(@Nullable PacketSender sender, @NotNull M msg) {
*/
public static void handleProxySide(ServerConnection server, byte[] rawData) {
byte[] data;
if (server.isEncrypted()) {
boolean encrypted = server.isEncrypted() || server.consumeEncryptedOnce();
if (encrypted) {
try {
data = EncryptionUtil.decrypt(rawData, server.getKeyPair().getPrivate());
} catch (Exception e) {
Expand All @@ -198,7 +205,7 @@ public static void handleProxySide(ServerConnection server, byte[] rawData) {
try (ByteArrayInputStream bin = new ByteArrayInputStream(data);
DataInputStream in = new DataInputStream(bin)) {
byte id = (byte) (in.readByte() & 0xFF);
if (id != 0 && !server.isEncrypted()) {
if (id != 0 && !encrypted) {
throw new RuntimeException("Packet " + id + " must be received encrypted (server: " + server + ")");
}
Protocol<?, ?> protocol = Protocol.getById(PacketFlow.TO_PROXY, id);
Expand All @@ -219,6 +226,7 @@ public static void handleProxySide(ServerConnection server, byte[] rawData) {
@SuppressWarnings("unchecked")
ProxyMessageHandler<Message> handler = (ProxyMessageHandler<Message>) protocol.getHandler();
Message message = handler.read(server, in);
Objects.requireNonNull(message, "handler.read(in) returned null");
handler.handle(server, message);
} catch (Exception | AssertionError e) {
Logger.getCurrentLogger().warn("Failed to handle plugin message from " + server, e);
Expand All @@ -233,7 +241,8 @@ public static void handleProxySide(ServerConnection server, byte[] rawData) {
*/
public static void handleServerSide(@NotNull PacketSender sender, byte[] rawData) {
byte[] data;
if (sender.isEncrypted()) {
boolean encrypted = sender.isEncrypted() || sender.consumeEncryptedOnce();
if (encrypted) {
try {
data = EncryptionUtil.decrypt(rawData, sender.getKeyPair().getPrivate());
} catch (Exception e) {
Expand All @@ -245,7 +254,7 @@ public static void handleServerSide(@NotNull PacketSender sender, byte[] rawData
try (ByteArrayInputStream bin = new ByteArrayInputStream(data);
DataInputStream in = new DataInputStream(bin)) {
byte id = (byte) (in.readByte() & 0xFF);
if (id != 0 && !sender.isEncrypted()) {
if (id != 0 && !encrypted) {
throw new RuntimeException("Packet " + id + " must be received encrypted (sender: " + sender + ")");
}
Protocol<?, ?> protocol = Protocol.getById(PacketFlow.TO_SERVER, id);
Expand All @@ -264,6 +273,7 @@ public static void handleServerSide(@NotNull PacketSender sender, byte[] rawData
@SuppressWarnings("unchecked")
ServerMessageHandler<Message> handler = (ServerMessageHandler<Message>) protocol.getHandler();
Message message = handler.read(in);
Objects.requireNonNull(message, "handler.read(in) returned null");
handler.handle(sender, message);
} catch (Exception | AssertionError e) {
Logger.getCurrentLogger().warn("Failed to handle plugin message from " + sender, e);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package net.azisaba.azipluginmessaging.api.protocol.handler;

import net.azisaba.azipluginmessaging.api.AziPluginMessagingConfig;
import net.azisaba.azipluginmessaging.api.Logger;
import net.azisaba.azipluginmessaging.api.protocol.Protocol;
import net.azisaba.azipluginmessaging.api.protocol.message.EncryptionMessage;
Expand Down Expand Up @@ -40,11 +39,7 @@ public void handle(@NotNull PacketSender sender, @NotNull EncryptionMessage msg)
Logger.getCurrentLogger().warn("Failed to send public key to the server " + sender);
}

// Enable encryption
sender.setEncrypted(true);

if (AziPluginMessagingConfig.debug) {
Logger.getCurrentLogger().info("Encryption enabled for " + sender);
}
// Enable encryption here, otherwise we will not be able to receive the encrypted response
sender.setEncryptedOnce();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package net.azisaba.azipluginmessaging.api.protocol.handler;

import net.azisaba.azipluginmessaging.api.AziPluginMessagingConfig;
import net.azisaba.azipluginmessaging.api.Logger;
import net.azisaba.azipluginmessaging.api.protocol.Protocol;
import net.azisaba.azipluginmessaging.api.protocol.message.EmptyMessage;
import net.azisaba.azipluginmessaging.api.server.PacketSender;
import net.azisaba.azipluginmessaging.api.server.ServerConnection;
import org.jetbrains.annotations.NotNull;

import java.io.DataInputStream;
import java.io.IOException;

public class ProxyboundEncryptionResponsePacket implements ProxyMessageHandler<EmptyMessage> {
@Override
public @NotNull EmptyMessage read(@NotNull ServerConnection server, @NotNull DataInputStream in) throws IOException {
return EmptyMessage.INSTANCE;
}

@Override
public void handle(@NotNull PacketSender sender, @NotNull EmptyMessage msg) throws Exception {
// Enable encryption
sender.setEncrypted(true);

if (AziPluginMessagingConfig.debug) {
Logger.getCurrentLogger().info("Encryption enabled for " + sender);
}

// Send response to server to set the "encrypted" flag to true
Protocol.S_ENCRYPTION_RESPONSE.sendPacket(sender, EmptyMessage.INSTANCE);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package net.azisaba.azipluginmessaging.api.protocol.handler;

import net.azisaba.azipluginmessaging.api.AziPluginMessagingConfig;
import net.azisaba.azipluginmessaging.api.AziPluginMessagingProvider;
import net.azisaba.azipluginmessaging.api.Logger;
import net.azisaba.azipluginmessaging.api.entity.Player;
import net.azisaba.azipluginmessaging.api.protocol.Protocol;
import net.azisaba.azipluginmessaging.api.protocol.message.EmptyMessage;
import net.azisaba.azipluginmessaging.api.protocol.message.EncryptionMessage;
import net.azisaba.azipluginmessaging.api.server.PacketSender;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -33,13 +32,15 @@ public void handle(@NotNull PacketSender sender, @NotNull EncryptionMessage msg)
// Set the public key from the proxy
sender.setRemotePublicKey(msg.getPublicKey());

// Enable encryption
// Enable encryption to be able to send packet
sender.setEncrypted(true);

if (AziPluginMessagingConfig.debug) {
Logger.getCurrentLogger().info("Encryption enabled for " + sender);
sender.setEncryptedOnce();
try {
// Send response to proxy
Protocol.P_ENCRYPTION_RESPONSE.sendPacket(sender, EmptyMessage.INSTANCE);
} finally {
// Disable encryption because response is not guaranteed to be received on our side
sender.setEncrypted(false);
}

AziPluginMessagingProvider.get().getPacketQueue().flush(sender);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package net.azisaba.azipluginmessaging.api.protocol.handler;

import net.azisaba.azipluginmessaging.api.AziPluginMessagingConfig;
import net.azisaba.azipluginmessaging.api.AziPluginMessagingProvider;
import net.azisaba.azipluginmessaging.api.Logger;
import net.azisaba.azipluginmessaging.api.protocol.message.EmptyMessage;
import net.azisaba.azipluginmessaging.api.server.PacketSender;
import org.jetbrains.annotations.NotNull;

import java.io.DataInputStream;
import java.io.IOException;

public class ServerboundEncryptionResponsePacket implements ServerMessageHandler<EmptyMessage> {
@Override
public @NotNull EmptyMessage read(@NotNull DataInputStream in) throws IOException {
return EmptyMessage.INSTANCE;
}

@Override
public void handle(@NotNull PacketSender sender, @NotNull EmptyMessage msg) throws Exception {
// Enable encryption
sender.setEncrypted(true);

if (AziPluginMessagingConfig.debug) {
Logger.getCurrentLogger().info("Encryption enabled for " + sender);
}

AziPluginMessagingProvider.get().getPacketQueue().flush(sender);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package net.azisaba.azipluginmessaging.api.protocol.message;

import org.jetbrains.annotations.NotNull;

import java.io.DataOutputStream;
import java.io.IOException;

public class EmptyMessage implements Message {
public static final EmptyMessage INSTANCE = new EmptyMessage();

private EmptyMessage() {}

@Override
public void write(@NotNull DataOutputStream out) throws IOException {
// no-op
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public interface Connection {
*/
boolean isEncrypted();

void setEncryptedOnce();

boolean consumeEncryptedOnce();

/**
* Gets the local key pair for encrypting packets.
* @return the local key pair
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import org.jetbrains.annotations.NotNull;

import java.security.KeyPair;
import java.security.PublicKey;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
public class ProtocolTest {
@Test
public void load() {
// check packets
Protocol.getById(PacketFlow.TO_SERVER, (byte) 0);
}
}
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
}

group = "net.azisaba.azipluginmessaging"
version = "2.3.1-SNAPSHOT"
version = "3.0.0"

repositories {
mavenCentral()
Expand Down
12 changes: 12 additions & 0 deletions spigot/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ dependencies {
}

tasks {
processResources {
from(sourceSets.main.get().resources.srcDirs) {
include("**")
val tokenReplacementMap = mapOf(
"VERSION" to project.version
)
filter<org.apache.tools.ant.filters.ReplaceTokens>("tokens" to tokenReplacementMap)
}
filteringCharset = "UTF-8"
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}

shadowJar {
archiveFileName.set("AziPluginMessaging-Spigot-${project.version}.jar")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class PlayerImpl implements Player, PacketSender {
Expand All @@ -25,6 +26,7 @@ public class PlayerImpl implements Player, PacketSender {
private boolean encrypted = false;
public String challenge = null;
public AtomicInteger joins = new AtomicInteger();
private final AtomicBoolean encryptedOnce = new AtomicBoolean(false);

@Contract(value = "null -> fail", pure = true)
private PlayerImpl(@Nullable org.bukkit.entity.Player handle) {
Expand Down Expand Up @@ -90,6 +92,16 @@ public boolean isEncrypted() {
return this.encrypted;
}

@Override
public void setEncryptedOnce() {
encryptedOnce.set(true);
}

@Override
public boolean consumeEncryptedOnce() {
return encryptedOnce.compareAndSet(true, false);
}

@NotNull
@Override
public PublicKey getRemotePublicKey() {
Expand Down
2 changes: 1 addition & 1 deletion spigot/src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: AziPluginMessaging
main: net.azisaba.azipluginmessaging.spigot.SpigotPlugin
version: 2.3.0-SNAPSHOT
version: @VERSION@
depend:
- LuckPerms
commands:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

public class ServerConnectionImpl implements ServerConnection {
private static long lastCleaned = System.currentTimeMillis();
Expand All @@ -26,6 +27,7 @@ public class ServerConnectionImpl implements ServerConnection {
private KeyPair keyPair;
private PublicKey publicKey;
private boolean encrypted = false;
private final AtomicBoolean encryptedOnce = new AtomicBoolean(false);

public ServerConnectionImpl(@NotNull com.velocitypowered.api.proxy.ServerConnection handle) {
this.handle = handle;
Expand Down Expand Up @@ -85,6 +87,16 @@ public boolean isEncrypted() {
return encrypted;
}

@Override
public void setEncryptedOnce() {
encryptedOnce.set(true);
}

@Override
public boolean consumeEncryptedOnce() {
return encryptedOnce.compareAndSet(true, false);
}

@Override
public @NotNull KeyPair getKeyPair() {
return Objects.requireNonNull(keyPair);
Expand Down

0 comments on commit f69b301

Please sign in to comment.