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 92ef77cd..68095f66 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 @@ -110,7 +110,7 @@ final class EncryptionUtil { * in a login session. * * @param random random generator - * @return an error with 4 bytes long + * @return a token with 4 bytes long */ public static byte[] generateVerifyToken(RandomGenerator random) { byte[] token = new byte[VERIFY_TOKEN_LENGTH]; @@ -157,11 +157,11 @@ final class EncryptionUtil { return verifier.verify(clientKey.signature()); } - public static boolean verifyNonce(byte[] exptected, PrivateKey decryptionKey, byte[] encryptedNonce) + public static boolean verifyNonce(byte[] expected, PrivateKey decryptionKey, byte[] encryptedNonce) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { byte[] decryptedNonce = decrypt(decryptionKey, encryptedNonce); - return Arrays.equals(exptected, decryptedNonce); + return Arrays.equals(expected, decryptedNonce); } public static boolean verifySignedNonce(byte[] nonce, PublicKey clientKey, long signatureSalt, byte[] signature) @@ -199,6 +199,7 @@ final class EncryptionUtil { } private static byte[] getServerIdHash(String sessionId, PublicKey publicKey, SecretKey sharedSecret) { + @SuppressWarnings("deprecation") Hasher hasher = Hashing.sha1().newHasher(); hasher.putBytes(sessionId.getBytes(StandardCharsets.ISO_8859_1)); diff --git a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/FastLoginBukkitTest.java b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/FastLoginBukkitTest.java index 3dd35135..07ff6232 100644 --- a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/FastLoginBukkitTest.java +++ b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/FastLoginBukkitTest.java @@ -30,23 +30,21 @@ import com.github.games647.fastlogin.core.CommonUtil; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.chat.ComponentSerializer; -import junit.framework.TestCase; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class FastLoginBukkitTest extends TestCase { +class FastLoginBukkitTest { @Test - public void testRGB() { + void testRGB() { var message = "&x00002a00002b&lText"; var msg = CommonUtil.translateColorCodes(message); - assertThat(msg, is("§x00002a00002b§lText")); + assertEquals(msg, "§x00002a00002b§lText"); var components = TextComponent.fromLegacyText(msg); var expected = """ {"bold":true,"color":"#00a00b","text":"Text"}"""; - assertThat(ComponentSerializer.toString(components), is(expected)); + assertEquals(ComponentSerializer.toString(components), expected); } } 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 3a8adbfb..e1b8bea2 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 @@ -50,88 +50,77 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.*; -import static org.junit.Assert.assertTrue; - -public class EncryptionUtilTest { +class EncryptionUtilTest { @Test - public void testVerifyToken() { + void testVerifyToken() { var random = ThreadLocalRandom.current(); byte[] token = EncryptionUtil.generateVerifyToken(random); - assertThat(token, notNullValue()); - assertThat(token.length, is(4)); + assertAll( + () -> assertNotNull(token), + () -> assertEquals(token.length, 4) + ); } @Test - public void testServerKey() { + void testServerKey() { KeyPair keyPair = EncryptionUtil.generateKeyPair(); Key privateKey = keyPair.getPrivate(); - assertThat(privateKey.getAlgorithm(), is("RSA")); + assertEquals(privateKey.getAlgorithm(), "RSA"); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); - assertThat(publicKey.getAlgorithm(), is("RSA")); + assertEquals(publicKey.getAlgorithm(), "RSA"); // clients accept larger values than the standard vanilla server, but we shouldn't crash them - assertTrue(publicKey.getModulus().bitLength() >= 1024); - assertTrue(publicKey.getModulus().bitLength() < 8192); + assertAll( + () -> assertTrue(publicKey.getModulus().bitLength() >= 1024), + () -> assertTrue(publicKey.getModulus().bitLength() < 8192) + ); } @Test - public void testExpiredClientKey() throws Exception { + void testExpiredClientKey() throws Exception { var clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json"); // Client expires at the exact second mentioned, so use it for verification var expiredTimestamp = clientKey.expiry(); - assertThat(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp), is(false)); + assertFalse(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp)); } - @Test - public void testInvalidChangedExpiration() throws Exception { - // expiration date changed should make the signature invalid - // expiration should still be valid - var clientKey = ResourceLoader.loadClientKey("client_keys/invalid_wrong_expiration.json"); + @ParameterizedTest + @ValueSource(strings = { + // expiration date changed should make the signature invalid while still being not expired + "client_keys/invalid_wrong_expiration.json", + // changed public key no longer corresponding to the signature + "client_keys/invalid_wrong_key.json", + // signature modified no longer corresponding to key and expiration date + "client_keys/invalid_wrong_signature.json" + }) + void testInvalidClientKey(String clientKeySource) throws Exception { + var clientKey = ResourceLoader.loadClientKey(clientKeySource); Instant expireTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS); - assertThat(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp), is(false)); + assertFalse(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp)); } @Test - public void testInvalidChangedKey() throws Exception { - // changed public key no longer corresponding to the signature - var clientKey = ResourceLoader.loadClientKey("client_keys/invalid_wrong_key.json"); - Instant expireTimestamp = clientKey.expiry().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 = ResourceLoader.loadClientKey("client_keys/invalid_wrong_signature.json"); - Instant expireTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS); - - assertThat(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp), is(false)); - } - - @Test - public void testValidClientKey() throws Exception { + void testValidClientKey() throws Exception { var clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json"); var verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS); - assertThat(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp), is(true)); + assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp)); } @Test - public void testDecryptSharedSecret() throws Exception { + void testDecryptSharedSecret() throws Exception { KeyPair serverPair = EncryptionUtil.generateKeyPair(); var serverPK = serverPair.getPublic(); @@ -139,12 +128,12 @@ public class EncryptionUtilTest { byte[] encryptedSecret = encrypt(serverPK, secretKey.getEncoded()); SecretKey decryptSharedKey = EncryptionUtil.decryptSharedKey(serverPair.getPrivate(), encryptedSecret); - assertThat(decryptSharedKey, is(secretKey)); + assertEquals(decryptSharedKey, secretKey); } private static byte[] encrypt(PublicKey receiverKey, byte... message) - throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, - IllegalBlockSizeException, BadPaddingException { + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException { var encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm()); encryptCipher.init(Cipher.ENCRYPT_MODE, receiverKey); return encryptCipher.doFinal(message); @@ -160,13 +149,13 @@ public class EncryptionUtilTest { } @Test - public void testServerIdHash() throws Exception { + void testServerIdHash() throws Exception { var serverId = ""; var sharedSecret = generateSharedKey(); var serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key(); String sessionHash = getServerHash(serverId, sharedSecret, serverPK); - assertThat(EncryptionUtil.getServerIdHashString(serverId, sharedSecret, serverPK), is(sessionHash)); + assertEquals(EncryptionUtil.getServerIdHashString(serverId, sharedSecret, serverPK), sessionHash); } private static String getServerHash(CharSequence serverId, SecretKey sharedSecret, PublicKey serverPK) { @@ -183,71 +172,61 @@ public class EncryptionUtilTest { hasher.putBytes(serverPK.getEncoded()); // It works by treating the sha1 output bytes as one large integer in two's complement and then printing the // integer in base 16, placing a minus sign if the interpreted number is negative. - // reference: https://github.com/SpigotMC/BungeeCord/blob/ff5727c5ef9c0b56ad35f9816ae6bd660b622cf0/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java#L456 + // reference: + // https://github.com/SpigotMC/BungeeCord/blob/ff5727c5ef9c0b56ad35f9816ae6bd660b622cf0/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java#L456 return new BigInteger(hasher.hash().asBytes()).toString(16); } @Test - public void testServerIdHashWrongSecret() throws Exception { + void testServerIdHashWrongSecret() throws Exception { var serverId = ""; var sharedSecret = generateSharedKey(); var serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key(); String sessionHash = getServerHash(serverId, sharedSecret, serverPK); - assertThat(EncryptionUtil.getServerIdHashString("", generateSharedKey(), serverPK), not(sessionHash)); + assertNotEquals(EncryptionUtil.getServerIdHashString("", generateSharedKey(), serverPK), sessionHash); } @Test - public void testServerIdHashWrongServerKey() { + void testServerIdHashWrongServerKey() { var serverId = ""; var sharedSecret = generateSharedKey(); var serverPK = EncryptionUtil.generateKeyPair().getPublic(); String sessionHash = getServerHash(serverId, sharedSecret, serverPK); var wrongPK = EncryptionUtil.generateKeyPair().getPublic(); - assertThat(EncryptionUtil.getServerIdHashString("", sharedSecret, wrongPK), not(sessionHash)); + assertNotEquals(EncryptionUtil.getServerIdHashString("", sharedSecret, wrongPK), sessionHash); } @Test - public void testValidSignedNonce() throws Exception { + void testValidSignedNonce() throws Exception { ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json"); SignatureTestData testData = SignatureTestData.fromResource("signature/valid_signature.json"); - assertThat(verifySignedNonce(testData, clientKey), is(true)); + assertTrue(verifySignedNonce(testData, clientKey)); } - @Test - public void testIncorrectNonce() throws Exception { + @ParameterizedTest + @ValueSource(strings = { + "signature/incorrect_nonce.json", + "signature/incorrect_salt.json", + "signature/incorrect_signature.json", + }) + void testIncorrectNonce(String signatureSource) throws Exception { ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json"); - SignatureTestData testData = SignatureTestData.fromResource("signature/incorrect_nonce.json"); - assertThat(verifySignedNonce(testData, clientKey), is(false)); + SignatureTestData testData = SignatureTestData.fromResource(signatureSource); + assertFalse(verifySignedNonce(testData, clientKey)); } @Test - public void testIncorrectSalt() throws Exception { - // client generated - ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json"); - SignatureTestData testData = SignatureTestData.fromResource("signature/incorrect_salt.json"); - assertThat(verifySignedNonce(testData, clientKey), is(false)); - } - - @Test - public void testIncorrectSignature() throws Exception { - // client generated - ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json"); - SignatureTestData testData = SignatureTestData.fromResource("signature/incorrect_signature.json"); - assertThat(verifySignedNonce(testData, clientKey), is(false)); - } - - @Test - public void testWrongPublicKeySigned() throws Exception { + void testWrongPublicKeySigned() throws Exception { // load a different public key ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/invalid_wrong_key.json"); SignatureTestData testData = SignatureTestData.fromResource("signature/valid_signature.json"); - assertThat(verifySignedNonce(testData, clientKey), is(false)); + assertFalse(verifySignedNonce(testData, clientKey)); } private static boolean verifySignedNonce(SignatureTestData testData, ClientPublicKey clientKey) - throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { PublicKey clientPublicKey = clientKey.key(); byte[] nonce = testData.getNonce(); @@ -257,41 +236,44 @@ public class EncryptionUtilTest { } @Test - public void testNonce() throws Exception { + void testNonce() throws Exception { byte[] expected = {1, 2, 3, 4}; var serverKey = EncryptionUtil.generateKeyPair(); var encryptedNonce = encrypt(serverKey.getPublic(), expected); - assertThat(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce), is(true)); + assertTrue(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)); } @Test - public void testNonceIncorrect() throws Exception { + void testNonceIncorrect() throws Exception { byte[] expected = {1, 2, 3, 4}; var serverKey = EncryptionUtil.generateKeyPair(); // flipped first character - var encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3 , 4}); - - assertThat(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce), is(false)); + var encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4}); + assertFalse(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)); } - @Test(expected = GeneralSecurityException.class) - public void testNonceFailedDecryption() throws Exception { + @Test + void testNonceFailedDecryption() throws Exception { byte[] expected = {1, 2, 3, 4}; var serverKey = EncryptionUtil.generateKeyPair(); - // generate a new keypair that iss different + // generate a new keypair that is different var encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected); - EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce); + assertThrows(GeneralSecurityException.class, + () -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce) + ); } - @Test(expected = GeneralSecurityException.class) - public void testNonceIncorrectEmpty() throws Exception { + @Test + void testNonceIncorrectEmpty() { byte[] expected = {1, 2, 3, 4}; var serverKey = EncryptionUtil.generateKeyPair(); byte[] encryptedNonce = {}; - assertThat(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce), is(false)); + assertThrows(GeneralSecurityException.class, + () -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce) + ); } } diff --git a/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java b/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java index cedb61b5..bcb9867a 100644 --- a/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java +++ b/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java @@ -25,132 +25,80 @@ */ package com.github.games647.fastlogin.core; +import com.github.games647.fastlogin.core.antibot.RateLimiter; import com.github.games647.fastlogin.core.antibot.TickingRateLimiter; import java.time.Duration; import java.util.concurrent.TimeUnit; -import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class TickingRateLimiterTest { +class TickingRateLimiterTest { /** * Always expired */ - @Test - public void allowExpire() { + @ParameterizedTest + @ValueSource(longs = {5_000_000L, -5_000_000L}) + void allowExpire(long initial) { int size = 3; - FakeTicker ticker = new FakeTicker(5_000_000L); + FakeTicker ticker = new FakeTicker(initial); // run twice the size to fill it first and then test it - TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, size, 0); + RateLimiter rateLimiter = new TickingRateLimiter(ticker, size, 0); for (int i = 0; i < size; i++) { - assertThat("Filling up", rateLimiter.tryAcquire(), is(true)); + assertTrue(rateLimiter.tryAcquire(), "Filling up"); } for (int i = 0; i < size; i++) { ticker.add(Duration.ofSeconds(1)); - assertThat("Should be expired", rateLimiter.tryAcquire(), is(true)); - } - } - - @Test - public void allowExpireNegative() { - int size = 3; - - FakeTicker ticker = new FakeTicker(-5_000_000L); - - // run twice the size to fill it first and then test it - TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, size, 0); - for (int i = 0; i < size; i++) { - assertThat("Filling up", rateLimiter.tryAcquire(), is(true)); - } - - for (int i = 0; i < size; i++) { - ticker.add(Duration.ofSeconds(1)); - assertThat("Should be expired", rateLimiter.tryAcquire(), is(true)); + assertTrue(rateLimiter.tryAcquire(), "Should be expired"); } } /** * Too many requests */ - @Test - public void shouldBlock() { + @ParameterizedTest + @ValueSource(longs = {5_000_000L, -5_000_000L}) + void shouldBlock(long initial) { int size = 3; - FakeTicker ticker = new FakeTicker(5_000_000L); + FakeTicker ticker = new FakeTicker(initial); // fill the size - TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, size, TimeUnit.SECONDS.toMillis(30)); + RateLimiter rateLimiter = new TickingRateLimiter(ticker, size, TimeUnit.SECONDS.toMillis(30)); for (int i = 0; i < size; i++) { - assertThat("Filling up", rateLimiter.tryAcquire(), is(true)); + assertTrue(rateLimiter.tryAcquire(), "Filling up"); } - assertThat("Should be full and no entry should be expired", rateLimiter.tryAcquire(), is(false)); + assertFalse(rateLimiter.tryAcquire(), "Should be full and no entry should be expired"); } - - /** - * Too many requests - */ - @Test - public void shouldBlockNegative() { - int size = 3; - - FakeTicker ticker = new FakeTicker(-5_000_000L); - - // fill the size - TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, size, TimeUnit.SECONDS.toMillis(30)); - for (int i = 0; i < size; i++) { - assertThat("Filling up", rateLimiter.tryAcquire(), is(true)); - } - - assertThat("Should be full and no entry should be expired", rateLimiter.tryAcquire(), is(false)); - } - + /** * Blocked attempts shouldn't replace existing ones. */ - @Test - public void blockedNotAdded() { - FakeTicker ticker = new FakeTicker(5_000_000L); + @ParameterizedTest + @ValueSource(longs = {5_000_000L, -5_000_000L}) + void blockedNotAdded(long initial) { + FakeTicker ticker = new FakeTicker(initial); // fill the size - 100ms should be reasonable high - TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, 1, 100); - assertThat("Filling up", rateLimiter.tryAcquire(), is(true)); + RateLimiter rateLimiter = new TickingRateLimiter(ticker, 1, 100); + assertTrue(rateLimiter.tryAcquire(), "Filling up"); ticker.add(Duration.ofMillis(50)); // still is full - should fail - assertThat("Expired too early", rateLimiter.tryAcquire(), is(false)); + assertFalse(rateLimiter.tryAcquire(), "Expired too early"); // wait the remaining time and add a threshold, because ticker.add(Duration.ofMillis(50)); - assertThat("Request not released", rateLimiter.tryAcquire(), is(true)); - } - - /** - * Blocked attempts shouldn't replace existing ones. - */ - @Test - public void blockedNotAddedNegative() { - FakeTicker ticker = new FakeTicker(-5_000_000L); - - // fill the size - 100ms should be reasonable high - TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, 1, 100); - assertThat("Filling up", rateLimiter.tryAcquire(), is(true)); - - ticker.add(Duration.ofMillis(50)); - - // still is full - should fail - assertThat("Expired too early", rateLimiter.tryAcquire(), is(false)); - - // wait the remaining time and add a threshold, because - ticker.add(Duration.ofMillis(50)); - assertThat("Request not released", rateLimiter.tryAcquire(), is(true)); + assertTrue(rateLimiter.tryAcquire(), "Request not released"); } } diff --git a/pom.xml b/pom.xml index 0f9326e8..f2f3950b 100644 --- a/pom.xml +++ b/pom.xml @@ -152,6 +152,14 @@ + + maven-surefire-plugin + 2.22.2 + + + maven-failsafe-plugin + 2.22.2 + @@ -176,9 +184,9 @@ - junit - junit - 4.13.2 + org.junit.jupiter + junit-jupiter + 5.8.2 test