Skip to content

Commit

Permalink
Initial Support for Minecraft Education Edition
Browse files Browse the repository at this point in the history
* This uses my private build of protocol v363 which is really just v361 rebadged
* Will proxy through the MCEE signedToken correctly and enable encryption to both ends
  • Loading branch information
Brendan Grieve committed May 8, 2020
1 parent e2d98c4 commit d511b95
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 5 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
</dependency>
<dependency>
<groupId>com.nukkitx.protocol</groupId>
<artifactId>bedrock-v390</artifactId>
<version>2.5.5</version>
<artifactId>bedrock-v363</artifactId>
<version>2.5.6</version>
<scope>compile</scope>
</dependency>
<dependency>
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/nukkitx/proxypass/ProxyPass.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import com.nukkitx.protocol.bedrock.BedrockClient;
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
import com.nukkitx.protocol.bedrock.BedrockServer;
import com.nukkitx.protocol.bedrock.v390.Bedrock_v390;
import com.nukkitx.protocol.bedrock.v363.Bedrock_v363;
import com.nukkitx.proxypass.network.ProxyBedrockEventHandler;
import lombok.AccessLevel;
import lombok.Getter;
Expand All @@ -37,7 +37,7 @@ public class ProxyPass {
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
public static final YAMLMapper YAML_MAPPER = (YAMLMapper) new YAMLMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
public static final String MINECRAFT_VERSION;
public static final BedrockPacketCodec CODEC = Bedrock_v390.V390_CODEC;
public static final BedrockPacketCodec CODEC = Bedrock_v363.V363_CODEC;
public static final int PROTOCOL_VERSION = CODEC.getProtocolVersion();
private static final DefaultPrettyPrinter PRETTY_PRINTER = new DefaultPrettyPrinter();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class ProxyBedrockEventHandler implements BedrockServerEventHandler {
private final ProxyPass proxy;

static {
ADVERTISEMENT.setEdition("MCPE");
ADVERTISEMENT.setEdition("MCEE");
ADVERTISEMENT.setGameType("Survival");
ADVERTISEMENT.setVersion(ProxyPass.MINECRAFT_VERSION);
ADVERTISEMENT.setProtocolVersion(ProxyPass.PROTOCOL_VERSION);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.nukkitx.protocol.bedrock.util.EncryptionUtils;
import com.nukkitx.proxypass.ProxyPass;
import com.nukkitx.proxypass.network.bedrock.util.BlockPaletteUtils;
import com.nukkitx.proxypass.network.bedrock.util.ForgeryUtils;
import com.nukkitx.proxypass.network.bedrock.util.RecipeUtils;
import lombok.RequiredArgsConstructor;
import lombok.Value;
Expand Down Expand Up @@ -46,6 +47,17 @@ public boolean handle(ServerToClientHandshakePacket packet) {
SecretKey key = EncryptionUtils.getSecretKey(this.player.getProxyKeyPair().getPrivate(), serverKey,
Base64.getDecoder().decode(saltJwt.getJWTClaimsSet().getStringClaim("salt")));
session.enableEncryption(key);

ServerToClientHandshakePacket p = new ServerToClientHandshakePacket();
p.setJwt(ForgeryUtils.forgeHandshake(
player.getProxyKeyPair(),
saltJwt.getJWTClaimsSet().getStringClaim("signedToken"),
Base64.getDecoder().decode(saltJwt.getJWTClaimsSet().getStringClaim("salt"))).serialize()
);
player.getUpstream().sendPacketImmediately(p);
player.getUpstream().enableEncryption(EncryptionUtils.getSecretKey(player.getProxyKeyPair().getPrivate(),
((UpstreamPacketHandler)player.getUpstream().getPacketHandler()).getRemotePublicKey(),Base64.getDecoder().decode(saltJwt.getJWTClaimsSet().getStringClaim("salt"))));

} catch (ParseException | NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
import com.nimbusds.jwt.SignedJWT;
import com.nukkitx.protocol.bedrock.BedrockServerSession;
import com.nukkitx.protocol.bedrock.handler.BedrockPacketHandler;
import com.nukkitx.protocol.bedrock.packet.ClientToServerHandshakePacket;
import com.nukkitx.protocol.bedrock.packet.LoginPacket;
import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
import com.nukkitx.protocol.bedrock.util.EncryptionUtils;
import com.nukkitx.proxypass.ProxyPass;
import com.nukkitx.proxypass.network.bedrock.util.ForgeryUtils;
import io.netty.util.AsciiString;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import net.minidev.json.JSONObject;
Expand All @@ -36,6 +38,8 @@ public class UpstreamPacketHandler implements BedrockPacketHandler {
private ArrayNode chainData;
private AuthData authData;
private ProxyPlayerSession player;
@Getter
private ECPublicKey remotePublicKey;

private static boolean validateChainData(JsonNode data) throws Exception {
ECPublicKey lastKey = null;
Expand Down Expand Up @@ -63,6 +67,10 @@ private static boolean verifyJwt(JWSObject jwt, ECPublicKey key) throws JOSEExce
return jwt.verify(new DefaultJWSVerifierFactory().createJWSVerifier(jwt.getHeader(), key));
}

public boolean handle(ClientToServerHandshakePacket packet) {
// This is handled ourselves and we don't want a duplicate packet
return true;
}
@Override
public boolean handle(LoginPacket packet) {
int protocolVersion = packet.getProtocolVersion();
Expand Down Expand Up @@ -111,6 +119,7 @@ public boolean handle(LoginPacket packet) {
throw new RuntimeException("Identity Public Key was not found!");
}
ECPublicKey identityPublicKey = EncryptionUtils.generateKey(payload.get("identityPublicKey").textValue());
this.remotePublicKey = identityPublicKey;

JWSObject clientJwt = JWSObject.parse(packet.getSkinData().toString());
verifyJwt(clientJwt, identityPublicKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,30 @@ public static JWSObject forgeSkinData(KeyPair pair, JSONObject skinData) {

return jws;
}

public static SignedJWT forgeHandshake(KeyPair pair, String signedToken, byte[] token) {
String publicKeyBase64 = Base64.getEncoder().encodeToString(pair.getPublic().getEncoded());
URI x5u = URI.create(publicKeyBase64);

JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES384).x509CertURL(x5u).build();

long timestamp = System.currentTimeMillis();
Date nbf = new Date(timestamp - TimeUnit.SECONDS.toMillis(1));
Date exp = new Date(timestamp + TimeUnit.DAYS.toMillis(1));

JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.claim("signedToken", signedToken)
.claim("salt", Base64.getEncoder().encodeToString(token))
.build();

SignedJWT jwt = new SignedJWT(header, claimsSet);

try {
EncryptionUtils.signJwt(jwt, (ECPrivateKey) pair.getPrivate());
} catch (JOSEException e) {
throw new RuntimeException(e);
}

return jwt;
}
}

0 comments on commit d511b95

Please sign in to comment.