diff --git a/bukkit/pom.xml b/bukkit/pom.xml index 9b3c5c5d..c1f63ba1 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -179,6 +179,19 @@ 1.0.7 + + com.mojang + datafixerupper + 5.0.28 + provided + + + * + * + + + + com.comphenix.protocol diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitLoginSession.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitLoginSession.java index 556def1b..4f4af4dd 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitLoginSession.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitLoginSession.java @@ -26,6 +26,7 @@ package com.github.games647.fastlogin.bukkit; import com.github.games647.craftapi.model.skin.SkinProperty; +import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey; import com.github.games647.fastlogin.core.StoredProfile; import com.github.games647.fastlogin.core.shared.LoginSession; @@ -42,30 +43,33 @@ public class BukkitLoginSession extends LoginSession { private final byte[] verifyToken; + private final ClientPublicKey clientPublicKey; + private boolean verified; private SkinProperty skinProperty; - public BukkitLoginSession(String username, byte[] verifyToken, boolean registered + public BukkitLoginSession(String username, byte[] verifyToken, ClientPublicKey publicKey, boolean registered , StoredProfile profile) { super(username, registered, profile); + this.clientPublicKey = publicKey; this.verifyToken = verifyToken.clone(); } //available for BungeeCord public BukkitLoginSession(String username, boolean registered) { - this(username, EMPTY_ARRAY, registered, null); + this(username, EMPTY_ARRAY, null, registered, null); } //cracked player public BukkitLoginSession(String username, StoredProfile profile) { - this(username, EMPTY_ARRAY, false, profile); + this(username, EMPTY_ARRAY, null, false, profile); } //ProtocolSupport public BukkitLoginSession(String username, boolean registered, StoredProfile profile) { - this(username, EMPTY_ARRAY, registered, profile); + this(username, EMPTY_ARRAY, null, registered, profile); } /** @@ -79,6 +83,10 @@ public class BukkitLoginSession extends LoginSession { return verifyToken.clone(); } + public ClientPublicKey getClientPublicKey() { + return clientPublicKey; + } + /** * @return premium skin if available */ diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtil.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtil.java index 956a60f7..713fa68a 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtil.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtil.java @@ -27,6 +27,7 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib; import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey; import com.google.common.io.Resources; +import com.google.common.primitives.Longs; import java.io.IOException; import java.math.BigInteger; @@ -115,9 +116,9 @@ class EncryptionUtil { /** * Generate the server id based on client and server data. * - * @param sessionId session for the current login attempt + * @param sessionId session for the current login attempt * @param sharedSecret shared secret between the client and the server - * @param publicKey public key of the server + * @param publicKey public key of the server * @return the server id formatted as a hexadecimal string. */ public static String getServerIdHashString(String sessionId, SecretKey sharedSecret, PublicKey publicKey) { @@ -136,7 +137,7 @@ class EncryptionUtil { * Decrypts the content and extracts the key spec. * * @param privateKey private server key - * @param sharedKey the encrypted shared key + * @param sharedKey the encrypted shared key * @return shared secret key * @throws GeneralSecurityException if it fails to decrypt the data */ @@ -151,10 +152,20 @@ class EncryptionUtil { return false; } - Signature signature = Signature.getInstance("SHA1withRSA"); - signature.initVerify(mojangSessionKey); - signature.update(toSignable(clientKey).getBytes(StandardCharsets.US_ASCII)); - return signature.verify(clientKey.getSignature()); + Signature verifier = Signature.getInstance("SHA1withRSA"); + verifier.initVerify(mojangSessionKey); + verifier.update(toSignable(clientKey).getBytes(StandardCharsets.US_ASCII)); + return verifier.verify(clientKey.getSignature()); + } + + public static boolean verifySignedNonce(byte[] nonce, PublicKey clientKey, long signatureSalt, byte[] signature) + throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + Signature verifier = Signature.getInstance("SHA256withRSA"); + verifier.initVerify(clientKey); + + verifier.update(nonce); + verifier.update(Longs.toByteArray(signatureSalt)); + return verifier.verify(signature); } private static PublicKey loadMojangSessionKey() @@ -183,7 +194,7 @@ class EncryptionUtil { * Decrypted the given data using the cipher. * * @param cipher decryption cypher initialized with the private key - * @param data the encrypted data + * @param data the encrypted data * @return clear text data * @throws GeneralSecurityException if it fails to decrypt the data */ @@ -194,7 +205,7 @@ class EncryptionUtil { } private static byte[] getServerIdHash(String sessionId, PublicKey publicKey, SecretKey sharedSecret) - throws NoSuchAlgorithmException { + throws NoSuchAlgorithmException { // byte[] a(String var0, PublicKey var1, SecretKey var2) MessageDigest digest = MessageDigest.getInstance("SHA-1"); diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java index f0a40830..485c065d 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java @@ -27,21 +27,25 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib; import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.wrappers.BukkitConverters; +import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData; import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent; +import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey; import com.github.games647.fastlogin.core.StoredProfile; import com.github.games647.fastlogin.core.shared.JoinManagement; import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; import java.security.PublicKey; +import java.util.Optional; import java.util.Random; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; public class NameCheckTask extends JoinManagement - implements Runnable { + implements Runnable { private final FastLoginBukkit plugin; private final PacketEvent packetEvent; @@ -67,7 +71,9 @@ public class NameCheckTask extends JoinManagement publicKey = packetEvent.getPacket().getOptionals(BukkitConverters.getWrappedPublicKeyDataConverter()).read(0); + + super.onLogin(username, new ProtocolLibLoginSource(player, random, publicKey.get(), this.publicKey)); } finally { ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent); } @@ -85,7 +91,7 @@ public class NameCheckTask extends JoinManagement either = packetEvent.getPacket().getSpecificModifier(Either.class).read(0); + Object signatureData = either.right().get(); + long salt = FuzzyReflection.getFieldValue(signatureData, Long.TYPE, true); + byte[] signature = FuzzyReflection.getFieldValue(signatureData, byte[].class, true); + + BukkitLoginSession session = plugin.getSession(sender.getAddress()); + PublicKey publicKey = session.getClientPublicKey().getKey(); + try { + if (EncryptionUtil.verifySignedNonce(session.getVerifyToken(), publicKey, salt, signature)) { + packetEvent.getAsyncMarker().incrementProcessingDelay(); + Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, session, sharedSecret, keyPair); + plugin.getScheduler().runAsync(verifyTask); + } else { + sender.kickPlayer("Invalid signature"); + } + } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) { + sender.kickPlayer("Invalid signature"); + } } } diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibLoginSource.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibLoginSource.java index dd191f1c..d87b602d 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibLoginSource.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibLoginSource.java @@ -30,6 +30,7 @@ import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.wrappers.WrappedChatComponent; +import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData; import com.github.games647.fastlogin.core.shared.LoginSource; import java.lang.reflect.InvocationTargetException; @@ -48,15 +49,18 @@ class ProtocolLibLoginSource implements LoginSource { private final Player player; private final Random random; + + private final WrappedProfileKeyData clientPublicKey; private final PublicKey publicKey; private final String serverId = ""; private byte[] verifyToken; - public ProtocolLibLoginSource(Player player, Random random, PublicKey publicKey) { + public ProtocolLibLoginSource(Player player, Random random, WrappedProfileKeyData clientPublicKey, PublicKey serverPublicKey) { this.player = player; this.random = random; - this.publicKey = publicKey; + this.clientPublicKey = clientPublicKey; + this.publicKey = serverPublicKey; } @Override @@ -109,6 +113,10 @@ class ProtocolLibLoginSource implements LoginSource { return player.getAddress(); } + public WrappedProfileKeyData getClientPublicKey() { + return clientPublicKey; + } + public String getServerId() { return serverId; } diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java index e974b09e..d13a5c9c 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java @@ -29,12 +29,15 @@ import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory; +import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftVersion; +import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedGameProfile; +import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData; import com.github.games647.craftapi.model.auth.Verification; import com.github.games647.craftapi.model.skin.SkinProperty; import com.github.games647.craftapi.resolver.MojangResolver; @@ -120,7 +123,7 @@ public class VerifyResponseTask implements Runnable { } try { - if (!checkVerifyToken(session) || !enableEncryption(loginKey)) { + if (!enableEncryption(loginKey)) { return; } } catch (Exception ex) { @@ -180,23 +183,6 @@ public class VerifyResponseTask implements Runnable { } } - private boolean checkVerifyToken(BukkitLoginSession session) throws GeneralSecurityException { - byte[] requestVerify = session.getVerifyToken(); - //encrypted verify token - byte[] responseVerify = packetEvent.getPacket().getByteArrays().read(1); - - //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L182 - if (!Arrays.equals(requestVerify, EncryptionUtil.decrypt(serverKey.getPrivate(), responseVerify))) { - //check if the verify-token are equal to the server sent one - disconnect("invalid-verify-token", - "GameProfile {0} ({1}) tried to login with an invalid verify token. Server: {2} Client: {3}", - session.getRequestUsername(), packetEvent.getPlayer().getAddress(), requestVerify, responseVerify); - return false; - } - - return true; - } - //try to get the networkManager from ProtocolLib private Object getNetworkManager() throws IllegalAccessException, ClassNotFoundException { Object injectorContainer = TemporaryPlayerFactory.getInjectorFromPlayer(player); @@ -273,6 +259,9 @@ public class VerifyResponseTask implements Runnable { if (MinecraftVersion.atOrAbove(new MinecraftVersion(1, 19, 0))) { startPacket.getStrings().write(0, username); + + EquivalentConverter converter = BukkitConverters.getWrappedPublicKeyDataConverter(); + startPacket.getOptionals(converter).write(0, Optional.empty()); } else { //uuid is ignored by the packet definition WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username); diff --git a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/Base64Adapter.java b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/Base64Adapter.java new file mode 100644 index 00000000..c2a2d1a4 --- /dev/null +++ b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/Base64Adapter.java @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2022 games647 and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.bukkit.listener.protocollib; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.util.Base64; + +public class Base64Adapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, byte[] value) throws IOException { + var encoded = Base64.getEncoder().encodeToString(value); + out.value(encoded); + } + + @Override + public byte[] read(JsonReader in) throws IOException { + String encoded = in.nextString(); + return Base64.getDecoder().decode(encoded); + } +} diff --git a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtilTest.java b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtilTest.java index 904405ae..63a24bac 100644 --- a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtilTest.java +++ b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtilTest.java @@ -25,6 +25,7 @@ */ package com.github.games647.fastlogin.bukkit.listener.protocollib; +import com.github.games647.fastlogin.bukkit.listener.protocollib.SignatureTestData.SignatureData; import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey; import com.google.common.io.Resources; import com.google.gson.Gson; @@ -36,9 +37,12 @@ import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; import java.security.SecureRandom; +import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -65,7 +69,7 @@ public class EncryptionUtilTest { @Test public void testExpiredClientKey() throws Exception { - var clientKey = loadClientKey("client_keys/valid.json"); + var clientKey = loadClientKey("client_keys/valid_public_key.json"); // Client expires at the exact second mentioned, so use it for verification var expiredTimestamp = clientKey.getExpiry(); @@ -78,7 +82,9 @@ public class EncryptionUtilTest { // expiration date changed should make the signature invalid // expiration should still be valid var clientKey = loadClientKey("client_keys/invalid_wrong_expiration.json"); - assertThat(EncryptionUtil.verifyClientKey(clientKey, clientKey.getExpiry().minus(5, ChronoUnit.HOURS)), is(false)); + Instant expireTimestamp = clientKey.getExpiry().minus(5, ChronoUnit.HOURS); + + assertThat(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp), is(false)); } // @Test(expected = Exception.class) @@ -86,31 +92,119 @@ public class EncryptionUtilTest { public void testInvalidChangedKey() throws Exception { // changed public key no longer corresponding to the signature var clientKey = loadClientKey("client_keys/invalid_wrong_key.json"); - assertThat(EncryptionUtil.verifyClientKey(clientKey, clientKey.getExpiry().minus(5, ChronoUnit.HOURS)), is(false)); + Instant expireTimestamp = clientKey.getExpiry().minus(5, ChronoUnit.HOURS); + + assertThat(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp), is(false)); } @Test public void testInvalidChangedSignature() throws Exception { // signature modified no longer corresponding to key and expiration date var clientKey = loadClientKey("client_keys/invalid_wrong_signature.json"); - assertThat(EncryptionUtil.verifyClientKey(clientKey, clientKey.getExpiry().minus(5, ChronoUnit.HOURS)), is(false)); + Instant expireTimestamp = clientKey.getExpiry().minus(5, ChronoUnit.HOURS); + + assertThat(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp), is(false)); } @Test public void testValidClientKey() throws Exception { - var clientKey = loadClientKey("client_keys/valid.json"); - + var clientKey = loadClientKey("client_keys/valid_public_key.json"); var verificationTimestamp = clientKey.getExpiry().minus(5, ChronoUnit.HOURS); + assertThat(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp), is(true)); } + @Test + public void testValidSignedNonce() throws Exception { + ClientPublicKey clientKey = loadClientKey("client_keys/valid_public_key.json"); + PublicKey clientPublicKey = clientKey.getKey(); + + SignatureTestData testData = loadSignatureResource("signature/valid_signature.json"); + byte[] nonce = testData.getNonce(); + SignatureData signature = testData.getSignature(); + long salt = signature.getSalt(); + assertThat(EncryptionUtil.verifySignedNonce(nonce, clientPublicKey, salt, signature.getSignature()), is(true)); + } + + @Test + public void testIncorrectNonce() throws Exception { + ClientPublicKey clientKey = loadClientKey("client_keys/valid_public_key.json"); + PublicKey clientPublicKey = clientKey.getKey(); + + SignatureTestData testData = loadSignatureResource("signature/incorrect_nonce.json"); + byte[] nonce = testData.getNonce(); + SignatureData signature = testData.getSignature(); + long salt = signature.getSalt(); + assertThat(EncryptionUtil.verifySignedNonce(nonce, clientPublicKey, salt, signature.getSignature()), is(false)); + } + + @Test + public void testIncorrectSalt() throws Exception { + // client generated + ClientPublicKey clientKey = loadClientKey("client_keys/valid_public_key.json"); + PublicKey clientPublicKey = clientKey.getKey(); + + SignatureTestData testData = loadSignatureResource("signature/incorrect_salt.json"); + byte[] nonce = testData.getNonce(); + SignatureData signature = testData.getSignature(); + long salt = signature.getSalt(); + assertThat(EncryptionUtil.verifySignedNonce(nonce, clientPublicKey, salt, signature.getSignature()), is(false)); + } + + @Test + public void testIncorrectSignature() throws Exception { + // client generated + ClientPublicKey clientKey = loadClientKey("client_keys/valid_public_key.json"); + PublicKey clientPublicKey = clientKey.getKey(); + + SignatureTestData testData = loadSignatureResource("signature/incorrect_signature.json"); + byte[] nonce = testData.getNonce(); + SignatureData signature = testData.getSignature(); + long salt = signature.getSalt(); + assertThat(EncryptionUtil.verifySignedNonce(nonce, clientPublicKey, salt, signature.getSignature()), is(false)); + } + + @Test + public void testWrongPublicKeySigned() throws Exception { + // load a different public key + ClientPublicKey clientKey = loadClientKey("client_keys/invalid_wrong_key.json"); + PublicKey clientPublicKey = clientKey.getKey(); + + SignatureTestData testData = loadSignatureResource("signature/valid_signature.json"); + byte[] nonce = testData.getNonce(); + SignatureData signature = testData.getSignature(); + long salt = signature.getSalt(); + assertThat(EncryptionUtil.verifySignedNonce(nonce, clientPublicKey, salt, signature.getSignature()), is(false)); + } + + private SignatureTestData loadSignatureResource(String resourceName) throws IOException { + var keyUrl = Resources.getResource(resourceName); + var encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII); + + return new Gson().fromJson(encodedSignature, SignatureTestData.class); + } + + private RSAPrivateKey parsePrivateKey(String keySpec) + throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { + try ( + Reader reader = new StringReader(keySpec); + PemReader pemReader = new PemReader(reader) + ) { + PemObject pemObject = pemReader.readPemObject(); + byte[] content = pemObject.getContent(); + var privateKeySpec = new PKCS8EncodedKeySpec(content); + + var factory = KeyFactory.getInstance("RSA"); + return (RSAPrivateKey) factory.generatePrivate(privateKeySpec); + } + } + private ClientPublicKey loadClientKey(String path) throws NoSuchAlgorithmException, IOException, InvalidKeySpecException { var keyUrl = Resources.getResource(path); - var gson = new Gson(); var lines = Resources.toString(keyUrl, StandardCharsets.US_ASCII); - var object = gson.fromJson(lines, JsonObject.class); + var object = new Gson().fromJson(lines, JsonObject.class); Instant expires = Instant.parse(object.getAsJsonPrimitive("expires_at").getAsString()); String key = object.getAsJsonPrimitive("key").getAsString(); @@ -120,10 +214,10 @@ public class EncryptionUtilTest { return new ClientPublicKey(expires, publicKey, signature); } - private RSAPublicKey parsePublicKey(String lines) + private RSAPublicKey parsePublicKey(String keySpec) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { try ( - Reader reader = new StringReader(lines); + Reader reader = new StringReader(keySpec); PemReader pemReader = new PemReader(reader) ) { PemObject pemObject = pemReader.readPemObject(); diff --git a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/SignatureTestData.java b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/SignatureTestData.java new file mode 100644 index 00000000..8ea85f6e --- /dev/null +++ b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/SignatureTestData.java @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2022 games647 and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.bukkit.listener.protocollib; + +import com.google.gson.annotations.JsonAdapter; + +public class SignatureTestData { + + @JsonAdapter(Base64Adapter.class) + private byte[] nonce; + + private SignatureData signature; + + public byte[] getNonce() { + return nonce; + } + + public SignatureData getSignature() { + return signature; + } + + public static class SignatureData { + + private long salt; + + @JsonAdapter(Base64Adapter.class) + private byte[] signature; + + public long getSalt() { + return salt; + } + + public byte[] getSignature() { + return signature; + } + } +} diff --git a/bukkit/src/test/java/integration/README.md b/bukkit/src/test/java/integration/README.md new file mode 100644 index 00000000..d4efc537 --- /dev/null +++ b/bukkit/src/test/java/integration/README.md @@ -0,0 +1,53 @@ +# Integration tests for authentication + +## Description + +Projects require integration tests in order to check against errors that could only occur if connected to other +components. However, they are heavier in terms of performance and require a more complex setup. Unit tests often make +use of fake, mock, stubs, etc. implementations to test the unit in isolation and thus could hide issues across +boundaries of a unit. Nevertheless, both are not replacement for each other. + +## Usage in this project + +The authentication system is a core component, so it requires some kind of testing. Here we are going to +spin up a Spigot server and test with the supported authentication schemes against the implementation of MCProtocolLib. + +### Goals + +* OS platform independent +* Reproducible, but not fixed to a specific image hash + * This is a dev container, so fixing it to feature/major version is enough instead of a version fixed by hash +* Improve container spin up + * E.g. Remove/Reduce world generation + +### Note on automation + +The simplest solution it to use the official Mojang session and authentication servers. However, this would require +a spare Minecraft account. Mocking the auth servers would be a solution to avoid this. + +## Related + +Interest blog article about integration tests and why they are necessary. +https://software.rajivprab.com/2019/04/28/rethinking-software-testing-perspectives-from-the-world-of-hardware/ + +## Issues + +### Slow startup + +Tried a lot of optimizations like only loading a single world without the nether or the end. However, there the startup +is still slow. If you have any ideas on how to tune the startup parameters of the Minecraft server or the JVM +itself to reduce the startup time, please suggest it. + +### Checkpoint + +An idea to optimize the time is to use CRIU (checkpoint and restore). So to save the process at a certain stage and +restore all data multiple times. This could cause a lot of issues like open files have to be present. However, the +impact is significant and since it runs inside the container all files, pids (pid=1) should be matching. Potential +checkpoint locations are: + +* Direct before loading the plugins + * Likely before binding the port to prevent issues +* After loading the libraries + +Nevertheless, the current state requires to run it with root and the Java support is currently still in progress. + diff --git a/bukkit/src/test/resources/client_keys/README.md b/bukkit/src/test/resources/client_keys/README.md new file mode 100644 index 00000000..1165e23d --- /dev/null +++ b/bukkit/src/test/resources/client_keys/README.md @@ -0,0 +1,27 @@ +# About + +This contains test resources for the unit tests. The file are extracted from the Minecraft directory with slight +modifications. The files are found in `$MINECRAFT_HOME$/profilekeys/`, where `$MINECRAFT_HOME$` represents the +OS-dependent minecraft folder. + +**Notable the files in this folder do not contain the private key information. It should be explicitly +stripped before including it.** + +## Minecraft folder + +* Windows: `%appdata%\.minecraft` +* Linux: `/home/username/.minecraft` +* Mac: `~/Library/Application Support/minecraft` + +## Directory structure + +* `invalid_wrong_expiration.json`: Changed the expiration date +* `invalid_wrong_key.json`: Modified public key while keeping the RSA structure valid +* `invalid_wrong_signature.json`: Changed a character in the public key signature +* `valid_public_key.json`: Extracted from actual file + +## File content + +* `expires_at`: Expiration date +* `key`: Public key from the original file out of `public_key.key` +* `signature`: Mojang signed signature of this public key diff --git a/bukkit/src/test/resources/client_keys/invalid_wrong_expiration.json b/bukkit/src/test/resources/client_keys/invalid_wrong_expiration.json index 7ecc12e4..ea509fec 100644 --- a/bukkit/src/test/resources/client_keys/invalid_wrong_expiration.json +++ b/bukkit/src/test/resources/client_keys/invalid_wrong_expiration.json @@ -1,5 +1,5 @@ { - "expires_at": "2022-06-12T09:46:26.421156927Z", - "key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoOv23jt2QPyab6bPRBwH2ggmzQU4I+xm\nDpi3X5ZB5Em/4uzyZqNVLJc0gShpk0XsdoB28Nq1bPxczOTBxuXi3rg5ax5gL+iymDSU27DLM8s/\n33lOofzGPJUEQGKlFm0QelDKZ/q5Y/9inHE3hEJKf7h0tnmGahXFmZSF/nRz9GHnfSYpjtDr9bsZ\nOzQuLhHXT5E4ksNRTFW41h0MlZ1qOhO+NiiVgk7LmgVYiV7RRbgO8U6RaCEqg5n28Ewo6QtzB+DF\n4NTDeu3E9BLH5G0npdUrVNhdRUWCFDmH6n9hqSIz2J7o6GvWqEvp0h9e/3qtLsoS60hnQXunrcWc\nPaEIYQIDAQAB\n-----END RSA PUBLIC KEY-----\n", - "signature": "BYv2mKJvz5O3Wo5V5sbJI0L6zAjfzQSkTNd7ykd/MB7KPPDg4zoTuOqphmh042xz1EYbMcfqEZvP04NTaoZDx+IxGieBB+LuxqnmYKIgtpcR2SEpzbSKznSHkupr1hKwF7kCVWLlwSbxc/XRlWPPyT6FE9m628A/jFb/obgfzLLQWfTFWp6kq2oBoUUQV5om2ihdrJ8oLCsw10SGdcFtK4+UuLzz+wjwv3JpvIX93IKdjFnw0KNd110HOPWZgp2n8+f6GsecysorqvwaE1rJC0m9Qa/wFsK2TY7twSMreCrbXPaBiWrkRovtm9gnaBwD+iuZYlnLvO0ld8qW928LL2vFBTi3TsPUWC3i/xYnAR2m8YP2hiCLHuPfSJmgfxHsM2iRdrR8qdOUkiC9h34STEA7Q2+rWkNWJ+YKYVTIkyqHEuXqU87txhVTaRJi6UDGDn49cMKmZwQnn+23JQf1chcn1YFkrivDaJPhm17GhoEldQHSLQfxb0ifja5WBNDbkKBF/h9JqvG3Ya9anxlyxY6g7/m2zP73xfkvUnejoX4GKjffEqezQmTe9RIeuWyz94nfZNLr0Ps363kAfP4KSW+f4zkTU/UVg19ccAY0ZhiwDetKyksU5WqLO8xMPZ6PNFYhNeBb2yhGdT8PidkRYkC4XBn1k7F7apiNUuZU8aA=" + "expires_at": "2022-06-20T07:31:47.318722344Z", + "key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd3ZxDhcRWWru1XEBke6uYqmbnS2Oxyk\nOMj+QDKrkwUqhVJYciyXGsMx46Mgr/KIoGCcokP5OtIxc6+69/ZLqJ9PvM81kLIxAqyvfBMKMGjP\n376LgxTF1FeDpbe5zXaNRxfmnvQhS5YTLbzgk36qWVjqxJMG4VLVmh7RV5zWsb7XlckZb2zRHM2Y\nMHbEC+ggX+l6zQyfG1KK0MH5k+O6b0xD0rv1wm24sLOesTXH6RZG8cNE3ofdnavxjFodTOnra6w1\naiVcoUTdEPSS86wQwq9j0YCcAKOwMXsqbk9NhpujrdyJ94dev+ELwkNS7P0pPrcfiyFTQeJCZTXz\nJB36MwIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "signature": "lfRXK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU=" } diff --git a/bukkit/src/test/resources/client_keys/invalid_wrong_key.json b/bukkit/src/test/resources/client_keys/invalid_wrong_key.json index 37bb3ad0..98ea33f5 100644 --- a/bukkit/src/test/resources/client_keys/invalid_wrong_key.json +++ b/bukkit/src/test/resources/client_keys/invalid_wrong_key.json @@ -1,5 +1,5 @@ { - "expires_at": "2022-06-12T10:46:26.421156927Z", + "expires_at": "2022-06-20T08:31:47.318722344Z", "key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoOv23jt2QPyab6bPRBwH2ggmzQU3I+xmDpi3X5ZB5Em/4uzyZqNVLJc0gShpk0XsdoB28Nq1bPxczOTBxuXi3rg5ax5gL+iymDSU27DLM8s/33lOofzGPJUEQGKlFm0QelDKZ/q5Y/9inHE3hEJKf7h0tnmGahXFmZSF/nRz9GHnfSYpjtDr9bsZOzQuLhHXT5E4ksNRTFW41h0MlZ1qOhO+NiiVgk7LmgVYiV7RRbgO8U6RaCEqg5n28Ewo6QtzB+DF4NTDeu3E9BLH5G0npdUrVNhdRUWCFDmH6n9hqSIz2J7o6GvWqEvp0h9e/3qtLsoS60hnQXunrcWcPaEIYQIDAQAB\n-----END RSA PUBLIC KEY-----\n", - "signature": "BYv2mKJvz5O3Wo5V5sbJI0L6zAjfzQSkTNd7ykd/MB7KPPDg4zoTuOqphmh042xz1EYbMcfqEZvP04NTaoZDx+IxGieBB+LuxqnmYKIgtpcR2SEpzbSKznSHkupr1hKwF7kCVWLlwSbxc/XRlWPPyT6FE9m628A/jFb/obgfzLLQWfTFWp6kq2oBoUUQV5om2ihdrJ8oLCsw10SGdcFtK4+UuLzz+wjwv3JpvIX93IKdjFnw0KNd110HOPWZgp2n8+f6GsecysorqvwaE1rJC0m9Qa/wFsK2TY7twSMreCrbXPaBiWrkRovtm9gnaBwD+iuZYlnLvO0ld8qW928LL2vFBTi3TsPUWC3i/xYnAR2m8YP2hiCLHuPfSJmgfxHsM2iRdrR8qdOUkiC9h34STEA7Q2+rWkNWJ+YKYVTIkyqHEuXqU87txhVTaRJi6UDGDn49cMKmZwQnn+23JQf1chcn1YFkrivDaJPhm17GhoEldQHSLQfxb0ifja5WBNDbkKBF/h9JqvG3Ya9anxlyxY6g7/m2zP73xfkvUnejoX4GKjffEqezQmTe9RIeuWyz94nfZNLr0Ps363kAfP4KSW+f4zkTU/UVg19ccAY0ZhiwDetKyksU5WqLO8xMPZ6PNFYhNeBb2yhGdT8PidkRYkC4XBn1k7F7apiNUuZU8aA=" + "signature": "lfRXK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU=" } diff --git a/bukkit/src/test/resources/client_keys/invalid_wrong_signature.json b/bukkit/src/test/resources/client_keys/invalid_wrong_signature.json index cbca4b16..2b80c2c9 100644 --- a/bukkit/src/test/resources/client_keys/invalid_wrong_signature.json +++ b/bukkit/src/test/resources/client_keys/invalid_wrong_signature.json @@ -1,5 +1,5 @@ { - "expires_at": "2022-06-12T10:46:26.421156927Z", - "key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoOv23jt2QPyab6bPRBwH2ggmzQU4I+xm\nDpi3X5ZB5Em/4uzyZqNVLJc0gShpk0XsdoB28Nq1bPxczOTBxuXi3rg5ax5gL+iymDSU27DLM8s/\n33lOofzGPJUEQGKlFm0QelDKZ/q5Y/9inHE3hEJKf7h0tnmGahXFmZSF/nRz9GHnfSYpjtDr9bsZ\nOzQuLhHXT5E4ksNRTFW41h0MlZ1qOhO+NiiVgk7LmgVYiV7RRbgO8U6RaCEqg5n28Ewo6QtzB+DF\n4NTDeu3E9BLH5G0npdUrVNhdRUWCFDmH6n9hqSIz2J7o6GvWqEvp0h9e/3qtLsoS60hnQXunrcWc\nPaEIYQIDAQAB\n-----END RSA PUBLIC KEY-----\n", - "signature": "bYv2mKJvz5O3Wo5V5sbJI0L6zAjfzQSkTNd7ykd/MB7KPPDg4zoTuOqphmh042xz1EYbMcfqEZvP04NTaoZDx+IxGieBB+LuxqnmYKIgtpcR2SEpzbSKznSHkupr1hKwF7kCVWLlwSbxc/XRlWPPyT6FE9m628A/jFb/obgfzLLQWfTFWp6kq2oBoUUQV5om2ihdrJ8oLCsw10SGdcFtK4+UuLzz+wjwv3JpvIX93IKdjFnw0KNd110HOPWZgp2n8+f6GsecysorqvwaE1rJC0m9Qa/wFsK2TY7twSMreCrbXPaBiWrkRovtm9gnaBwD+iuZYlnLvO0ld8qW928LL2vFBTi3TsPUWC3i/xYnAR2m8YP2hiCLHuPfSJmgfxHsM2iRdrR8qdOUkiC9h34STEA7Q2+rWkNWJ+YKYVTIkyqHEuXqU87txhVTaRJi6UDGDn49cMKmZwQnn+23JQf1chcn1YFkrivDaJPhm17GhoEldQHSLQfxb0ifja5WBNDbkKBF/h9JqvG3Ya9anxlyxY6g7/m2zP73xfkvUnejoX4GKjffEqezQmTe9RIeuWyz94nfZNLr0Ps363kAfP4KSW+f4zkTU/UVg19ccAY0ZhiwDetKyksU5WqLO8xMPZ6PNFYhNeBb2yhGdT8PidkRYkC4XBn1k7F7apiNUuZU8aA=" + "expires_at": "2022-06-20T08:31:47.318722344Z", + "key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd3ZxDhcRWWru1XEBke6uYqmbnS2Oxyk\nOMj+QDKrkwUqhVJYciyXGsMx46Mgr/KIoGCcokP5OtIxc6+69/ZLqJ9PvM81kLIxAqyvfBMKMGjP\n376LgxTF1FeDpbe5zXaNRxfmnvQhS5YTLbzgk36qWVjqxJMG4VLVmh7RV5zWsb7XlckZb2zRHM2Y\nMHbEC+ggX+l6zQyfG1KK0MH5k+O6b0xD0rv1wm24sLOesTXH6RZG8cNE3ofdnavxjFodTOnra6w1\naiVcoUTdEPSS86wQwq9j0YCcAKOwMXsqbk9NhpujrdyJ94dev+ELwkNS7P0pPrcfiyFTQeJCZTXz\nJB36MwIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "signature": "lfRxK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU=" } diff --git a/bukkit/src/test/resources/client_keys/valid.json b/bukkit/src/test/resources/client_keys/valid.json deleted file mode 100644 index a2d6a41a..00000000 --- a/bukkit/src/test/resources/client_keys/valid.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "expires_at": "2022-06-12T10:46:26.421156927Z", - "key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoOv23jt2QPyab6bPRBwH2ggmzQU4I+xmDpi3X5ZB5Em/4uzyZqNVLJc0gShpk0XsdoB28Nq1bPxczOTBxuXi3rg5ax5gL+iymDSU27DLM8s/33lOofzGPJUEQGKlFm0QelDKZ/q5Y/9inHE3hEJKf7h0tnmGahXFmZSF/nRz9GHnfSYpjtDr9bsZOzQuLhHXT5E4ksNRTFW41h0MlZ1qOhO+NiiVgk7LmgVYiV7RRbgO8U6RaCEqg5n28Ewo6QtzB+DF4NTDeu3E9BLH5G0npdUrVNhdRUWCFDmH6n9hqSIz2J7o6GvWqEvp0h9e/3qtLsoS60hnQXunrcWcPaEIYQIDAQAB\n-----END RSA PUBLIC KEY-----\n", - "signature": "BYv2mKJvz5O3Wo5V5sbJI0L6zAjfzQSkTNd7ykd/MB7KPPDg4zoTuOqphmh042xz1EYbMcfqEZvP04NTaoZDx+IxGieBB+LuxqnmYKIgtpcR2SEpzbSKznSHkupr1hKwF7kCVWLlwSbxc/XRlWPPyT6FE9m628A/jFb/obgfzLLQWfTFWp6kq2oBoUUQV5om2ihdrJ8oLCsw10SGdcFtK4+UuLzz+wjwv3JpvIX93IKdjFnw0KNd110HOPWZgp2n8+f6GsecysorqvwaE1rJC0m9Qa/wFsK2TY7twSMreCrbXPaBiWrkRovtm9gnaBwD+iuZYlnLvO0ld8qW928LL2vFBTi3TsPUWC3i/xYnAR2m8YP2hiCLHuPfSJmgfxHsM2iRdrR8qdOUkiC9h34STEA7Q2+rWkNWJ+YKYVTIkyqHEuXqU87txhVTaRJi6UDGDn49cMKmZwQnn+23JQf1chcn1YFkrivDaJPhm17GhoEldQHSLQfxb0ifja5WBNDbkKBF/h9JqvG3Ya9anxlyxY6g7/m2zP73xfkvUnejoX4GKjffEqezQmTe9RIeuWyz94nfZNLr0Ps363kAfP4KSW+f4zkTU/UVg19ccAY0ZhiwDetKyksU5WqLO8xMPZ6PNFYhNeBb2yhGdT8PidkRYkC4XBn1k7F7apiNUuZU8aA=" -} diff --git a/bukkit/src/test/resources/client_keys/valid_public_key.json b/bukkit/src/test/resources/client_keys/valid_public_key.json new file mode 100644 index 00000000..8943e87b --- /dev/null +++ b/bukkit/src/test/resources/client_keys/valid_public_key.json @@ -0,0 +1,5 @@ +{ + "expires_at": "2022-06-20T08:31:47.318722344Z", + "key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd3ZxDhcRWWru1XEBke6uYqmbnS2Oxyk\nOMj+QDKrkwUqhVJYciyXGsMx46Mgr/KIoGCcokP5OtIxc6+69/ZLqJ9PvM81kLIxAqyvfBMKMGjP\n376LgxTF1FeDpbe5zXaNRxfmnvQhS5YTLbzgk36qWVjqxJMG4VLVmh7RV5zWsb7XlckZb2zRHM2Y\nMHbEC+ggX+l6zQyfG1KK0MH5k+O6b0xD0rv1wm24sLOesTXH6RZG8cNE3ofdnavxjFodTOnra6w1\naiVcoUTdEPSS86wQwq9j0YCcAKOwMXsqbk9NhpujrdyJ94dev+ELwkNS7P0pPrcfiyFTQeJCZTXz\nJB36MwIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "signature": "lfRXK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU=" +} diff --git a/bukkit/src/test/resources/signature/README.md b/bukkit/src/test/resources/signature/README.md new file mode 100644 index 00000000..cc603bcc --- /dev/null +++ b/bukkit/src/test/resources/signature/README.md @@ -0,0 +1,16 @@ +# About + +This contains test resources for the unit tests. Files in this folder include pre-made cryptographic signatures. + +## Directory structure + +* `valid_signature.json`: Extracted using packet extract from actual authentication +* `incorrect_nonce.json`: Different nonce token simulating that the server expected a different token than signed +* `incorrect_salt.json`: Salt sent is different to the content signed by the signature (changed salt field) +* `incorrect_signature.json`: Changed signature + +## File content + +* `nonce`: Server generated nonce token +* `salt`: Client generated random token +* `signature`: Nonce and salt signed using the client key from `valid_public_key.json` diff --git a/bukkit/src/test/resources/signature/incorrect_nonce.json b/bukkit/src/test/resources/signature/incorrect_nonce.json new file mode 100644 index 00000000..b88c0f5d --- /dev/null +++ b/bukkit/src/test/resources/signature/incorrect_nonce.json @@ -0,0 +1,7 @@ +{ + "nonce": "galNig\u003d\u003d", + "signature": { + "signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d", + "salt": -2985008842905108412 + } +} diff --git a/bukkit/src/test/resources/signature/incorrect_salt.json b/bukkit/src/test/resources/signature/incorrect_salt.json new file mode 100644 index 00000000..8edffb62 --- /dev/null +++ b/bukkit/src/test/resources/signature/incorrect_salt.json @@ -0,0 +1,7 @@ +{ + "nonce": "GalNig\u003d\u003d", + "signature": { + "signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d", + "salt": -1985008842905108412 + } +} diff --git a/bukkit/src/test/resources/signature/incorrect_signature.json b/bukkit/src/test/resources/signature/incorrect_signature.json new file mode 100644 index 00000000..ba6bac53 --- /dev/null +++ b/bukkit/src/test/resources/signature/incorrect_signature.json @@ -0,0 +1,7 @@ +{ + "nonce": "GalNig\u003d\u003d", + "signature": { + "signature": "jlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d", + "salt": -2985008842905108412 + } +} diff --git a/bukkit/src/test/resources/signature/valid_signature.json b/bukkit/src/test/resources/signature/valid_signature.json new file mode 100644 index 00000000..7f4f4ad5 --- /dev/null +++ b/bukkit/src/test/resources/signature/valid_signature.json @@ -0,0 +1,7 @@ +{ + "nonce": "GalNig\u003d\u003d", + "signature": { + "signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d", + "salt": -2985008842905108412 + } +}