Add some needed utilities for encryption

This commit is contained in:
Andrew Steinborn 2018-07-26 00:35:54 -04:00
parent ab965108d6
commit b26a17e587
9 changed files with 103 additions and 7 deletions

View File

@ -4,6 +4,7 @@ import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.netty.MinecraftPipelineUtils;
import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler;
import com.velocitypowered.proxy.protocol.packets.EncryptionRequest;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
@ -30,6 +31,10 @@ public class VelocityServer {
return server;
}
public KeyPair getServerKeyPair() {
return serverKeyPair;
}
public void initialize() {
// Create a key pair
try {

View File

@ -3,6 +3,7 @@ package com.velocitypowered.proxy.connection.client;
import com.google.common.base.Preconditions;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packets.EncryptionRequest;
import com.velocitypowered.proxy.protocol.packets.ServerLogin;
import com.velocitypowered.proxy.protocol.packets.ServerLoginSuccess;
import com.velocitypowered.proxy.connection.MinecraftConnection;
@ -13,9 +14,11 @@ import com.velocitypowered.proxy.data.ServerInfo;
import com.velocitypowered.proxy.util.UuidUtils;
import java.net.InetSocketAddress;
import java.util.concurrent.ThreadLocalRandom;
public class LoginSessionHandler implements MinecraftSessionHandler {
private final MinecraftConnection inbound;
private ServerLogin login;
public LoginSessionHandler(MinecraftConnection inbound) {
this.inbound = Preconditions.checkNotNull(inbound, "inbound");
@ -23,12 +26,27 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public void handle(MinecraftPacket packet) {
Preconditions.checkArgument(packet instanceof ServerLogin, "Expected a ServerLogin packet, not " + packet.getClass().getName());
if (packet instanceof ServerLogin) {
this.login = (ServerLogin) packet;
// TODO: Online-mode checks
handleSuccessfulLogin();
}
}
// TODO: Encryption
private EncryptionRequest generateRequest() {
byte[] verify = new byte[4];
ThreadLocalRandom.current().nextBytes(verify);
EncryptionRequest request = new EncryptionRequest();
request.setPublicKey(VelocityServer.getServer().getServerKeyPair().getPublic().getEncoded());
request.setVerifyToken(verify);
return request;
}
private void handleSuccessfulLogin() {
inbound.setCompressionThreshold(256);
String username = ((ServerLogin) packet).getUsername();
String username = login.getUsername();
ServerLoginSuccess success = new ServerLoginSuccess();
success.setUsername(username);
success.setUuid(UuidUtils.generateOfflinePlayerUuid(username));
@ -43,5 +61,4 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
inbound.setSessionHandler(new InitialConnectSessionHandler(player));
connection.connect();
}
}

View File

@ -34,6 +34,7 @@ public enum StateRegistry {
LOGIN {
{
TO_SERVER.register(0x00, ServerLogin.class, ServerLogin::new);
TO_SERVER.register(0x01, EncryptionResponse.class, EncryptionResponse::new);
TO_CLIENT.register(0x00, Disconnect.class, Disconnect::new);
TO_CLIENT.register(0x01, EncryptionRequest.class, EncryptionRequest::new);

View File

@ -1,13 +1,12 @@
package com.velocitypowered.proxy.protocol.compression;
import com.velocitypowered.proxy.util.Disposable;
import io.netty.buffer.ByteBuf;
import java.util.zip.DataFormatException;
public interface VelocityCompressor {
public interface VelocityCompressor extends Disposable {
void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException;
void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException;
void dispose();
}

View File

@ -0,0 +1,8 @@
package com.velocitypowered.proxy.protocol.encryption;
import com.velocitypowered.proxy.util.Disposable;
import io.netty.buffer.ByteBuf;
public interface VelocityEncryptor extends Disposable {
void process(ByteBuf source, ByteBuf destination);
}

View File

@ -0,0 +1,23 @@
package com.velocitypowered.proxy.protocol.packets;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class EncryptionResponse implements MinecraftPacket {
private byte[] sharedSecret;
private byte[] verifyToken;
@Override
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
this.sharedSecret = ProtocolUtils.readByteArray(buf, 256);
this.verifyToken = ProtocolUtils.readByteArray(buf, 4);
}
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
ProtocolUtils.writeByteArray(buf, sharedSecret);
ProtocolUtils.writeByteArray(buf, verifyToken);
}
}

View File

@ -0,0 +1,9 @@
package com.velocitypowered.proxy.util;
/**
* This marker interface indicates that this object should be explicitly disposed before the object can no longer be used.
* Not disposing these objects will likely leak native resources and eventually lead to resource exhaustion.
*/
public interface Disposable {
void dispose();
}

View File

@ -0,0 +1,9 @@
package com.velocitypowered.proxy.util;
import java.math.BigInteger;
public enum EncryptionUtils { ;
public static String twoComplementsSha1Digest(byte[] digest) {
return new BigInteger(digest).toString(16);
}
}

View File

@ -0,0 +1,25 @@
package com.velocitypowered.proxy.util;
import org.junit.Assert;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class EncryptionUtilsTest {
@Test
public void twoComplementsSha1Digest() throws Exception {
String notchHash = hexDigest("Notch");
Assert.assertEquals("4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48", notchHash);
String jebHash = hexDigest("jeb_");
Assert.assertEquals("-7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1", jebHash);
}
private String hexDigest(String str) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(str.getBytes(StandardCharsets.UTF_8));
byte[] digested = digest.digest();
return EncryptionUtils.twoComplementsSha1Digest(digested);
}
}