Compare commits

...

322 Commits

Author SHA1 Message Date
31d9f3cb4a Fix parsing of illegal uuids with spaces 2024-04-04 21:46:58 +02:00
56e43fb146 Add support for postgresql 2024-04-04 17:43:23 +02:00
a65a5f3020 Make ignore states more verbose in Velocity 2024-02-13 16:54:23 +01:00
8a729e069d Use current link for hastebin 2024-02-13 16:54:22 +01:00
d89d332273 Minor code recommendations 2024-02-13 16:54:22 +01:00
cd3cfef6d4 Do not encapsulate the task twice in Velocity 2024-02-13 16:54:22 +01:00
6ec6e4844d Bump dependencies 2024-02-13 16:54:22 +01:00
60a65377b8 Hint proxy setup guide and out of date versions 2024-02-13 16:54:22 +01:00
ffbf0ae3f4 Merge pull request #1151 from games647/dependabot/github_actions/advanced-security/maven-dependency-submission-action-4.0.0
Bump advanced-security/maven-dependency-submission-action from 3.0.2 to 4.0.0
2024-02-01 11:38:47 +01:00
4cf65cf7e5 Bump advanced-security/maven-dependency-submission-action
Bumps [advanced-security/maven-dependency-submission-action](https://github.com/advanced-security/maven-dependency-submission-action) from 3.0.2 to 4.0.0.
- [Release notes](https://github.com/advanced-security/maven-dependency-submission-action/releases)
- [Commits](https://github.com/advanced-security/maven-dependency-submission-action/compare/v3.0.2...v4.0.0)

---
updated-dependencies:
- dependency-name: advanced-security/maven-dependency-submission-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 07:45:51 +00:00
dbf5ae2fae [Velocity] Fix plugin resuming event processing if login is not handled
This causes login timeouts if this plugin doesn't handle the login for
cases like:

* Anti-Bot is triggered
* Another plugin already denied the login

Fixes #1146
2024-01-23 13:59:49 +01:00
707572a007 Add core module name 2024-01-16 14:08:47 +01:00
108f5ad324 Transform velocity MariaDB transformers too
Otherwise, authentication plugin drivers couldn't be found.
2024-01-15 17:01:02 +01:00
2112d06162 Increase encryption key size for Spigot protocol
1024 bits are very weak keys. This size equals vanillas implementation.
Therefore, 2048 goes out of spec, but works and requires only minimal
additional performance for improved security.

The consequence will be during the login and playing in onlinemode.
First for authenticating and second for the encryption of all following
packets.
2024-01-15 16:37:48 +01:00
aece7f855d Update Java CI version 2024-01-15 16:29:26 +01:00
33b4ac8494 [ci-skip] Fully qualify dependabot versions 2024-01-15 16:29:16 +01:00
09161b86dd Merge pull request #1142 from games647/dependabot/maven/production-dependencies-eb968b0e20
Bump the production-dependencies group with 5 updates
2024-01-15 16:25:55 +01:00
57833bb40b Merge pull request #1133 from games647/dependabot/github_actions/github/codeql-action-3
Bump github/codeql-action from 2 to 3
2024-01-15 16:23:45 +01:00
a1414a38ff Merge pull request #1141 from games647/dependabot/maven/development-dependencies-ca802ab3b6
Bump the development-dependencies group with 1 update
2024-01-15 16:23:24 +01:00
0021bc476d Merge pull request #709 from Smart123s/feature/floodgate/newdb
Differentiate Floodgate players in database
2024-01-15 16:22:34 +01:00
4b1e08a2f8 Bump the production-dependencies group with 5 updates
Bumps the production-dependencies group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) | `10.12.5` | `10.12.7` |
| [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) | `3.2.2` | `3.2.5` |
| org.slf4j:slf4j-jdk14 | `2.0.9` | `2.0.11` |
| com.lenis0012.bukkit:loginsecurity | `3.2.1` | `3.3.0` |
| [org.mariadb.jdbc:mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) | `3.3.1` | `3.3.2` |


Updates `com.puppycrawl.tools:checkstyle` from 10.12.5 to 10.12.7
- [Release notes](https://github.com/checkstyle/checkstyle/releases)
- [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.12.5...checkstyle-10.12.7)

Updates `org.apache.maven.plugins:maven-surefire-plugin` from 3.2.2 to 3.2.5
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.2.2...surefire-3.2.5)

Updates `org.slf4j:slf4j-jdk14` from 2.0.9 to 2.0.11

Updates `com.lenis0012.bukkit:loginsecurity` from 3.2.1 to 3.3.0

Updates `org.mariadb.jdbc:mariadb-java-client` from 3.3.1 to 3.3.2
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.3.1...3.3.2)

---
updated-dependencies:
- dependency-name: com.puppycrawl.tools:checkstyle
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: org.slf4j:slf4j-jdk14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: com.lenis0012.bukkit:loginsecurity
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-15 07:40:02 +00:00
dbf5b8907e Bump the development-dependencies group with 1 update
Bumps the development-dependencies group with 1 update: [org.mockito:mockito-core](https://github.com/mockito/mockito).


Updates `org.mockito:mockito-core` from 5.8.0 to 5.9.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v5.8.0...v5.9.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-15 07:38:05 +00:00
e6dfa97e70 Bump github/codeql-action from 2 to 3
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 07:12:00 +00:00
434a276d30 Invert if and document migration flow for better readability 2023-12-11 16:16:16 +01:00
485af5e044 Document states 2023-12-11 16:16:16 +01:00
e566740dde Differentiate Floodgate players during login 2023-12-11 16:16:15 +01:00
db5b818a80 Add Floodgate variable to StoredProfile/Database 2023-12-11 16:16:15 +01:00
b0b998d1aa Use columnName instead of columnIndex
It's safer to rely on names instead of indexes.
It's also easier to add new columns at the end of the table.
2023-12-11 16:16:15 +01:00
62ca46fe6a Add checksytle suppression via comments 2023-12-11 16:16:15 +01:00
cd67797b5b Merge pull request #1122 from games647/dependabot/maven/development-dependencies-a116437099
Bump the development-dependencies group with 3 updates
2023-12-06 16:19:28 +01:00
48e8b45f1e Merge pull request #1123 from games647/dependabot/maven/production-dependencies-d650319dc7
Bump the production-dependencies group with 6 updates
2023-12-06 16:19:09 +01:00
1c9b8cb971 Merge pull request #1120 from games647/dependabot/github_actions/actions/setup-java-4
Bump actions/setup-java from 3 to 4
2023-12-06 16:18:34 +01:00
dcb9540a2f Bump the production-dependencies group with 6 updates
Bumps the production-dependencies group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) | `10.12.4` | `10.12.5` |
| [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) | `3.3.0` | `3.3.1` |
| [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) | `3.1.2` | `3.2.2` |
| me.clip:placeholderapi | `2.11.4` | `2.11.5` |
| [org.mariadb.jdbc:mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) | `3.2.0` | `3.3.1` |
| [org.codehaus.mojo:templating-maven-plugin](https://github.com/mojohaus/templating-maven-plugin) | `1.0.0` | `3.0.0` |


Updates `com.puppycrawl.tools:checkstyle` from 10.12.4 to 10.12.5
- [Release notes](https://github.com/checkstyle/checkstyle/releases)
- [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.12.4...checkstyle-10.12.5)

Updates `org.apache.maven.plugins:maven-checkstyle-plugin` from 3.3.0 to 3.3.1
- [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.3.0...maven-checkstyle-plugin-3.3.1)

Updates `org.apache.maven.plugins:maven-surefire-plugin` from 3.1.2 to 3.2.2
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.1.2...surefire-3.2.2)

Updates `me.clip:placeholderapi` from 2.11.4 to 2.11.5

Updates `org.mariadb.jdbc:mariadb-java-client` from 3.2.0 to 3.3.1
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.2.0...3.3.1)

Updates `org.codehaus.mojo:templating-maven-plugin` from 1.0.0 to 3.0.0
- [Release notes](https://github.com/mojohaus/templating-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/templating-maven-plugin/compare/templating-maven-plugin-1.0.0...3.0.0)

---
updated-dependencies:
- dependency-name: com.puppycrawl.tools:checkstyle
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: me.clip:placeholderapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: org.codehaus.mojo:templating-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-04 07:49:44 +00:00
cacf3c23f1 Bump the development-dependencies group with 3 updates
Bumps the development-dependencies group with 3 updates: [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5), [org.mockito:mockito-core](https://github.com/mockito/mockito) and [org.bouncycastle:bcprov-jdk18on](https://github.com/bcgit/bc-java).


Updates `org.junit.jupiter:junit-jupiter` from 5.10.0 to 5.10.1
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.10.0...r5.10.1)

Updates `org.mockito:mockito-core` from 5.6.0 to 5.8.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v5.6.0...v5.8.0)

Updates `org.bouncycastle:bcprov-jdk18on` from 1.76 to 1.77
- [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html)
- [Commits](https://github.com/bcgit/bc-java/commits)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: org.bouncycastle:bcprov-jdk18on
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-04 07:48:07 +00:00
67f170694f Bump actions/setup-java from 3 to 4
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-01 07:49:23 +00:00
e15ea9ce85 Restore support for fully qualified storage drivers
Related #1100
2023-10-12 10:47:14 +02:00
70960661b4 [ci-skip] Fix typo in issue templates 2023-10-11 13:42:17 +02:00
ec657faaff Force lowercase SQL driver name to improve type checks 2023-10-11 13:40:57 +02:00
51898b4b34 Migrate enhancement requests to newer issue templates 2023-10-11 13:28:12 +02:00
e30de1b700 Include CI server in issue template 2023-10-11 13:28:11 +02:00
ce17441edb Merge pull request #1097 from games647/dependabot/maven/production-dependencies-84d7f6773a
Bump the production-dependencies group with 6 updates
2023-10-11 12:39:04 +02:00
3244b7a87a Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-11 12:38:02 +02:00
ecacd2a715 Update toolchain for Java 21 compiling 2023-10-11 12:37:07 +02:00
ea6d4cf2f6 Add proper support for 1.19.3-1.20.1 2023-10-11 12:37:07 +02:00
91b05289ad Add 1.20.2 support 2023-10-11 12:37:07 +02:00
4e0f8cfb65 Bump the production-dependencies group with 6 updates
Bumps the production-dependencies group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) | `10.12.3` | `10.12.4` |
| [org.projectlombok:lombok](https://github.com/projectlombok/lombok) | `1.18.28` | `1.18.30` |
| com.mycila:license-maven-plugin | `4.2` | `4.3` |
| org.slf4j:slf4j-jdk14 | `2.0.7` | `2.0.9` |
| me.clip:placeholderapi | `2.11.3` | `2.11.4` |
| [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) | `3.5.0` | `3.5.1` |


Updates `com.puppycrawl.tools:checkstyle` from 10.12.3 to 10.12.4
- [Release notes](https://github.com/checkstyle/checkstyle/releases)
- [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.12.3...checkstyle-10.12.4)

Updates `org.projectlombok:lombok` from 1.18.28 to 1.18.30
- [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown)
- [Commits](https://github.com/projectlombok/lombok/compare/v1.18.28...v1.18.30)

Updates `com.mycila:license-maven-plugin` from 4.2 to 4.3

Updates `org.slf4j:slf4j-jdk14` from 2.0.7 to 2.0.9

Updates `me.clip:placeholderapi` from 2.11.3 to 2.11.4

Updates `org.apache.maven.plugins:maven-shade-plugin` from 3.5.0 to 3.5.1
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.5.0...maven-shade-plugin-3.5.1)

---
updated-dependencies:
- dependency-name: com.puppycrawl.tools:checkstyle
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: org.projectlombok:lombok
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: com.mycila:license-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: org.slf4j:slf4j-jdk14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: me.clip:placeholderapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-02 07:56:41 +00:00
d76690dcbe Merge pull request #1087 from games647/dependabot/maven/production-dependencies-ce791421b1
Bump the production-dependencies group with 7 updates
2023-08-31 15:23:30 +02:00
3559d067ad Bump the production-dependencies group with 7 updates
Bumps the production-dependencies group with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) | `10.9.3` | `10.12.3` |
| [org.projectlombok:lombok](https://github.com/projectlombok/lombok) | `1.18.26` | `1.18.28` |
| [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) | `2.22.2` | `3.1.2` |
| [com.google.code.gson:gson](https://github.com/google/gson) | `2.10` | `2.10.1` |
| com.lenis0012.bukkit:loginsecurity | `3.1.1` | `3.2.1` |
| [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) | `3.4.1` | `3.5.0` |
| [org.mariadb.jdbc:mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) | `3.1.4` | `3.2.0` |


Updates `com.puppycrawl.tools:checkstyle` from 10.9.3 to 10.12.3
- [Release notes](https://github.com/checkstyle/checkstyle/releases)
- [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.9.3...checkstyle-10.12.3)

Updates `org.projectlombok:lombok` from 1.18.26 to 1.18.28
- [Release notes](https://github.com/projectlombok/lombok/releases)
- [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown)
- [Commits](https://github.com/projectlombok/lombok/compare/v1.18.26...v1.18.28)

Updates `org.apache.maven.plugins:maven-surefire-plugin` from 2.22.2 to 3.1.2
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-2.22.2...surefire-3.1.2)

Updates `com.google.code.gson:gson` from 2.10 to 2.10.1
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.10...gson-parent-2.10.1)

Updates `com.lenis0012.bukkit:loginsecurity` from 3.1.1 to 3.2.1

Updates `org.apache.maven.plugins:maven-shade-plugin` from 3.4.1 to 3.5.0
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.4.1...maven-shade-plugin-3.5.0)

Updates `org.mariadb.jdbc:mariadb-java-client` from 3.1.4 to 3.2.0
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.1.4...3.2.0)

---
updated-dependencies:
- dependency-name: com.puppycrawl.tools:checkstyle
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: org.projectlombok:lombok
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: production-dependencies
- dependency-name: com.google.code.gson:gson
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: com.lenis0012.bukkit:loginsecurity
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-31 11:53:12 +00:00
48a92ed35e Use wildcard exclude patterns 2023-08-31 13:51:42 +02:00
a4eec19f53 [ci-skip] Fix dependency name 2023-08-31 13:19:17 +02:00
32eda941ee Ignore Hikari and SnakeYAML in dependency updates 2023-08-31 12:42:32 +02:00
92d88caf0a Merge pull request #1085 from games647/dependabot/maven/development-dependencies-424a594065
Bump the development-dependencies group with 2 updates
2023-08-31 12:28:11 +02:00
d2202d7a12 Bump the development-dependencies group with 2 updates
Bumps the development-dependencies group with 2 updates: [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) and [org.bouncycastle:bcprov-jdk18on](https://github.com/bcgit/bc-java).


Updates `org.junit.jupiter:junit-jupiter` from 5.9.1 to 5.10.0
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.9.1...r5.10.0)

Updates `org.bouncycastle:bcprov-jdk18on` from 1.75 to 1.76
- [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html)
- [Commits](https://github.com/bcgit/bc-java/commits)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: org.bouncycastle:bcprov-jdk18on
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-31 10:21:23 +00:00
6a68e04c87 Fix grouping for maven packages 2023-08-31 12:18:10 +02:00
b702f48907 Add version number back 2023-08-31 12:14:06 +02:00
159a6ed37c Add JavaDoc snippets and tiny reformats 2023-08-31 12:08:40 +02:00
b48dfed709 Add NPE check on velocity for logins 2023-08-31 12:08:40 +02:00
2db327ccc4 Group dependency updates together 2023-08-31 12:08:39 +02:00
9d907983a6 Only run CodeQL if successful 2023-08-31 12:08:39 +02:00
4a3d7f448f Merge pull request #1071 from xrh0905/floodgate_enabled
Floodgate enabled
2023-08-31 12:07:53 +02:00
31cbb2ff11 Add floodgate support
Based on: https://github.com/games647/FastLogin/pull/649

Solve hooks not found

Add defination of floodgateservice

Geyser API
2023-08-31 12:04:23 +02:00
1715c08ee6 Merge pull request #1041 from games647/dependabot/maven/org.apache.maven.plugins-maven-checkstyle-plugin-3.3.0
Bump maven-checkstyle-plugin from 3.1.2 to 3.3.0
2023-07-20 12:02:43 +02:00
5682df9ad5 Merge pull request #1048 from games647/dependabot/maven/org.bouncycastle-bcprov-jdk18on-1.75
Bump bcprov-jdk18on from 1.72 to 1.75
2023-07-20 12:01:27 +02:00
132b560130 Merge pull request #1036 from games647/dependabot/maven/org.mariadb.jdbc-mariadb-java-client-3.1.4
Bump mariadb-java-client from 3.1.0 to 3.1.4
2023-07-20 12:01:13 +02:00
e204c9c579 Merge pull request #1021 from games647/dependabot/maven/com.mycila-license-maven-plugin-4.2
Bump license-maven-plugin from 4.1 to 4.2
2023-07-20 12:00:56 +02:00
f92863e53a Merge branch 'main' into dependabot/maven/org.apache.maven.plugins-maven-checkstyle-plugin-3.3.0 2023-07-20 12:00:33 +02:00
23fec56ecd Merge branch 'main' into dependabot/maven/org.bouncycastle-bcprov-jdk18on-1.75 2023-07-20 11:57:50 +02:00
5cd10fd795 Run CodeQL after main build to leverage caching 2023-07-17 17:04:28 +02:00
ecbf07c0b2 Manually start build process in CodeQL 2023-07-17 16:26:52 +02:00
053b51e96b Relax java build version requirement
GitHub action seems to always download Java 19 even though it is
already downloaded and installed. This could also help with running
the CodeQL action.
2023-07-17 16:17:57 +02:00
44d365b15d Use java dot file to help autobuild select the correct version 2023-07-17 15:52:54 +02:00
610c49d62c Enable write for dependency graph 2023-07-17 15:49:36 +02:00
dbd12742fa Only upload dependency graph on push 2023-07-17 15:47:02 +02:00
f2fd51d4e2 Use suggestion dependency version 2023-07-17 15:17:29 +02:00
f9ca318775 Apply Java version selection after CodeQL init 2023-07-17 15:13:40 +02:00
f06ace2f5b Add dependency graph to help GitHub find dependencies 2023-07-17 15:09:16 +02:00
ee465e381c Rephrase invalid session warning to make it more clear 2023-07-17 10:10:03 +02:00
6ab81b1a2f Fix formatting errors 2023-07-17 09:55:24 +02:00
36ee52aa6e Print out a warning if we detect local IP addresses during connections
Reference #1055
2023-07-10 15:52:34 +02:00
e5c70d8597 Bump bcprov-jdk18on from 1.72 to 1.75
Bumps [bcprov-jdk18on](https://github.com/bcgit/bc-java) from 1.72 to 1.75.
- [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html)
- [Commits](https://github.com/bcgit/bc-java/commits)

---
updated-dependencies:
- dependency-name: org.bouncycastle:bcprov-jdk18on
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-26 07:58:13 +00:00
8b1485f0b1 Bump maven-checkstyle-plugin from 3.1.2 to 3.3.0
Bumps [maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.1.2 to 3.3.0.
- [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.1.2...maven-checkstyle-plugin-3.3.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-29 07:57:19 +00:00
c106089a72 Include updated yaml for older Spigot platforms
Fixes #1031
2023-05-22 15:16:42 +02:00
41f83988fe Fix code style 2023-05-22 14:43:52 +02:00
dc4510aba3 Print an extra warning if the id list is empty. 2023-05-22 10:35:50 +02:00
dd0dd15200 Print an extra warning if the id list is empty. 2023-05-22 10:21:58 +02:00
5fc27f6c4a Bump mariadb-java-client from 3.1.0 to 3.1.4
Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.1.0 to 3.1.4.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/commits/3.1.4)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-08 07:57:09 +00:00
a182976207 Update license date 2023-04-13 12:00:07 +02:00
e8811de62a Inline paper detection 2023-04-12 16:34:14 +02:00
0aedad8857 Allow to use Java 11 for Velocity 2023-04-12 11:44:09 +02:00
477ec06d55 Encapsulate storage implementation 2023-04-12 11:30:07 +02:00
e5d61101ae Typo fixes 2023-04-11 10:36:42 +02:00
c12a88966c Correct CodeQL action runner file
This appears to be a standard file name
2023-04-11 10:35:49 +02:00
73720277b3 Bump license-maven-plugin from 4.1 to 4.2
Bumps license-maven-plugin from 4.1 to 4.2.

---
updated-dependencies:
- dependency-name: com.mycila:license-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 07:58:12 +00:00
7d7d91f4da Apply the latest runner recommendations 2023-04-05 10:47:15 +02:00
8447285ef8 Remove redundant cache from action runner 2023-04-05 10:42:03 +02:00
edae388b37 Update Java action version 2023-04-05 10:07:39 +02:00
7c41b8a5e8 Merge pull request #1006 from games647/dependabot/maven/org.projectlombok-lombok-1.18.26
Bump lombok from 1.18.24 to 1.18.26
2023-04-05 09:51:13 +02:00
dfbfe9c4e5 Merge pull request #1003 from games647/dependabot/maven/com.puppycrawl.tools-checkstyle-10.9.3
Bump checkstyle from 10.9.2 to 10.9.3
2023-04-05 09:51:05 +02:00
2fa6700666 Merge pull request #1004 from games647/dependabot/maven/me.clip-placeholderapi-2.11.3
Bump placeholderapi from 2.11.2 to 2.11.3
2023-04-05 09:50:54 +02:00
0db205691b Merge pull request #1005 from games647/dependabot/maven/com.lenis0012.bukkit-loginsecurity-3.1.1
Bump loginsecurity from 3.1 to 3.1.1
2023-04-05 09:50:50 +02:00
d13414097a Merge pull request #1007 from games647/dependabot/maven/org.slf4j-slf4j-jdk14-2.0.7
Bump slf4j-jdk14 from 2.0.6 to 2.0.7
2023-04-05 09:50:42 +02:00
d6389f0474 Bump loginsecurity from 3.1 to 3.1.1
Bumps loginsecurity from 3.1 to 3.1.1.

---
updated-dependencies:
- dependency-name: com.lenis0012.bukkit:loginsecurity
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-05 09:48:09 +02:00
d153b5e2d5 Bump placeholderapi from 2.11.2 to 2.11.3
Bumps placeholderapi from 2.11.2 to 2.11.3.

---
updated-dependencies:
- dependency-name: me.clip:placeholderapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-05 09:48:08 +02:00
b21d73e9dc Bump checkstyle from 10.9.2 to 10.9.3
Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 10.9.2 to 10.9.3.
- [Release notes](https://github.com/checkstyle/checkstyle/releases)
- [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.9.2...checkstyle-10.9.3)

---
updated-dependencies:
- dependency-name: com.puppycrawl.tools:checkstyle
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-05 09:48:07 +02:00
730520fd77 Merge branch 'main' into dependabot/maven/org.projectlombok-lombok-1.18.26 2023-04-05 09:47:56 +02:00
652653bc45 Merge branch 'main' into dependabot/maven/org.slf4j-slf4j-jdk14-2.0.7 2023-04-05 09:47:54 +02:00
010f9b3827 Merge pull request #1012 from games647/dependabot/github_actions/actions/cache-3.3.1
Bump actions/cache from 3.2.6 to 3.3.1
2023-04-05 09:46:59 +02:00
42911e978f Rephrase auto-login documentation
Fixes #465
2023-04-01 14:24:51 +02:00
46bfbb9ca0 Bump actions/cache from 3.2.6 to 3.3.1
Bumps [actions/cache](https://github.com/actions/cache) from 3.2.6 to 3.3.1.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.2.6...v3.3.1)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-01 07:57:40 +00:00
546bbede0b Fix compatibility with older guava versions like Minecraft < 1.19 2023-03-31 13:06:12 +02:00
487bc24a8a Fix breaking ProtocolLib Minecraft version change
Fixes #1008
2023-03-30 17:26:59 +02:00
23d7594b67 Fix 404 for cracked handling
Fixes #998
2023-03-30 17:18:46 +02:00
87121979ce Bump slf4j-jdk14 from 2.0.6 to 2.0.7
Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.6 to 2.0.7.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.6...v_2.0.7)

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-jdk14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-27 07:57:59 +00:00
a42b824c3d Bump lombok from 1.18.24 to 1.18.26
Bumps [lombok](https://github.com/projectlombok/lombok) from 1.18.24 to 1.18.26.
- [Release notes](https://github.com/projectlombok/lombok/releases)
- [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown)
- [Commits](https://github.com/projectlombok/lombok/compare/v1.18.24...v1.18.26)

---
updated-dependencies:
- dependency-name: org.projectlombok:lombok
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-27 07:57:58 +00:00
a50f8b7210 Merge pull request #996 from games647/dependabot/maven/org.mockito-mockito-inline-5.2.0
Bump mockito-inline from 4.10.0 to 5.2.0
2023-03-24 17:17:59 +01:00
598d384516 Merge pull request #999 from games647/dependabot/maven/com.puppycrawl.tools-checkstyle-10.9.2
Bump checkstyle from 10.5.0 to 10.9.2
2023-03-24 17:17:46 +01:00
48d4a4861a Merge pull request #994 from games647/dependabot/github_actions/actions/cache-3.2.6
Bump actions/cache from 3.0.5 to 3.2.6
2023-03-24 17:16:14 +01:00
6b4a6cb66c Merge branch 'main' into dependabot/maven/com.puppycrawl.tools-checkstyle-10.9.2 2023-03-24 17:14:34 +01:00
43e507d032 Merge branch 'main' into dependabot/maven/org.mockito-mockito-inline-5.2.0 2023-03-24 17:14:31 +01:00
40b70c367c Merge branch 'main' into dependabot/github_actions/actions/cache-3.2.6 2023-03-24 17:14:26 +01:00
e7208e6a5d Merge pull request #971 from games647/dependabot/maven/io.papermc-paperlib-1.0.8
Bump paperlib from 1.0.7 to 1.0.8
2023-03-24 17:13:51 +01:00
2646ebac40 Revert "Pin the exact geyser version"
This reverts commit b20000cae8.
2023-03-24 17:10:20 +01:00
b20000cae8 Pin the exact geyser version 2023-03-24 17:06:58 +01:00
0adadd02a1 Find the correct floodgate version 2023-03-24 17:03:37 +01:00
28cf02129f Drop BungeeCordAuthenticator support as it is no longer available 2023-03-24 16:38:07 +01:00
b4ea6f19b5 Update craftapi to Treat FileNotFound as cracked 2023-03-24 16:38:07 +01:00
06c0e64073 Fix packet constructing for < 1.19.3 2023-03-24 16:38:07 +01:00
984ad723d2 Bump checkstyle from 10.5.0 to 10.9.2
Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 10.5.0 to 10.9.2.
- [Release notes](https://github.com/checkstyle/checkstyle/releases)
- [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.5.0...checkstyle-10.9.2)

---
updated-dependencies:
- dependency-name: com.puppycrawl.tools:checkstyle
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-20 07:57:53 +00:00
acdd0d577a Bump mockito-inline from 4.10.0 to 5.2.0
Bumps [mockito-inline](https://github.com/mockito/mockito) from 4.10.0 to 5.2.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.10.0...v5.2.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-inline
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-13 07:58:02 +00:00
f4e098589b Bump actions/cache from 3.0.5 to 3.2.6
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.5 to 3.2.6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.5...v3.2.6)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-01 07:57:15 +00:00
f706f428a3 Bump paperlib from 1.0.7 to 1.0.8
Bumps [paperlib](https://github.com/PaperMC/PaperLib) from 1.0.7 to 1.0.8.
- [Release notes](https://github.com/PaperMC/PaperLib/releases)
- [Changelog](https://github.com/PaperMC/PaperLib/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PaperMC/PaperLib/compare/v1.0.7...v1.0.8)

---
updated-dependencies:
- dependency-name: io.papermc:paperlib
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-02 07:01:34 +00:00
971b0e1f09 Merge pull request #963 from games647/dependabot/maven/org.apache.maven.plugins-maven-shade-plugin-3.4.1
Bump maven-shade-plugin from 3.3.0 to 3.4.1
2022-12-29 14:48:34 +01:00
c63712a949 Bump maven-shade-plugin from 3.3.0 to 3.4.1
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.3.0 to 3.4.1.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.3.0...maven-shade-plugin-3.4.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-26 07:00:58 +00:00
cb5598f2f4 Fix creating login start packets with ProtocolLib version 5
Fixes #955
2022-12-21 11:57:47 +01:00
b242a7b3ce Update dependencies 2022-12-21 11:56:39 +01:00
889cabb9e1 Merge pull request #943 from games647/dependabot/maven/org.mariadb.jdbc-mariadb-java-client-3.1.0
Bump mariadb-java-client from 3.0.7 to 3.1.0
2022-12-19 09:08:13 +01:00
7cbcf5b465 Merge pull request #947 from games647/dependabot/maven/com.puppycrawl.tools-checkstyle-10.5.0
Bump checkstyle from 10.3.2 to 10.5.0
2022-12-19 09:07:05 +01:00
9d99e1e6e0 Merge branch 'main' into dependabot/maven/org.mariadb.jdbc-mariadb-java-client-3.1.0 2022-12-19 09:06:22 +01:00
85f7f9262b Merge branch 'main' into dependabot/maven/com.puppycrawl.tools-checkstyle-10.5.0 2022-12-19 09:05:22 +01:00
020291271a Merge pull request #954 from games647/dependabot/maven/org.slf4j-slf4j-jdk14-2.0.6
Bump slf4j-jdk14 from 2.0.0 to 2.0.6
2022-12-19 09:05:07 +01:00
96be14cc73 Bump slf4j-jdk14 from 2.0.0 to 2.0.6
Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0 to 2.0.6.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0...v_2.0.6)

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-jdk14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-19 07:01:09 +00:00
1038453469 Fix 1.19.3 compat of removed public key on login
Fixes #951
2022-12-18 14:15:30 +01:00
ca17c3f377 Fix project building 2022-12-18 14:03:49 +01:00
c56d55f23d Bump checkstyle from 10.3.2 to 10.5.0
Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 10.3.2 to 10.5.0.
- [Release notes](https://github.com/checkstyle/checkstyle/releases)
- [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.3.2...checkstyle-10.5.0)

---
updated-dependencies:
- dependency-name: com.puppycrawl.tools:checkstyle
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 07:02:29 +00:00
19af059538 Bump mariadb-java-client from 3.0.7 to 3.1.0
Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.7 to 3.1.0.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/commits)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-21 07:02:54 +00:00
2989d9a62d Dump BungeeCord dependency to the snapshot version 2022-09-22 10:47:59 +02:00
84bfce9f84 Merge pull request #923 from Smart123s/feature/floodgate/extensions
Bump Geyser version to 2.1.0-SNAPSHOT
2022-09-22 10:40:24 +02:00
2fae5060dc Change Geyser AuthType classpath 2022-09-21 13:26:35 +02:00
5a29eede69 Bump Geyser version to 2.1.0-SNAPSHOT 2022-09-21 09:46:29 +02:00
7425df86b5 Merge pull request #887 from games647/dependabot/maven/org.mockito-mockito-inline-4.7.0
Bump mockito-inline from 4.6.1 to 4.7.0
2022-08-25 15:04:47 +02:00
2e60f9bc15 [ci skip] Highlight that the stats identifier is only 'stats' 2022-08-23 16:18:34 +02:00
ee6aeafafc Merge pull request #892 from LaboCraft/fix-ProtocolLib-unregister
Fix unregister ProtocolLib
2022-08-22 12:19:51 +02:00
47644afa19 Merge pull request #897 from games647/dependabot/maven/org.slf4j-slf4j-jdk14-2.0.0
Bump slf4j-jdk14 from 2.0.0-beta1 to 2.0.0
2022-08-22 12:01:21 +02:00
9441d6551d Bump slf4j-jdk14 from 2.0.0-beta1 to 2.0.0
Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0-beta1 to 2.0.0.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-beta1...v_2.0.0)

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-jdk14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-22 07:02:54 +00:00
0867719878 Use own config to prevent single redundant loading config after start
Fixes #893
2022-08-21 16:20:39 +02:00
ace180976d Fix unregister ProtocolLib 2022-08-17 14:49:38 +02:00
749974bca7 Bump mockito-inline from 4.6.1 to 4.7.0
Bumps [mockito-inline](https://github.com/mockito/mockito) from 4.6.1 to 4.7.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.6.1...v4.7.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-inline
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-15 07:03:14 +00:00
feb46399da Fetch player injection handler by type rather than name 2022-08-12 10:23:42 +02:00
18b0c427c4 Move annotation usage to the jetbrains package 2022-08-12 09:09:52 +02:00
520bb0d761 Reformat work and build files 2022-08-12 09:09:26 +02:00
6fe7eb2c24 Do not set date format for older SQLite versions
Fixes #877
2022-08-10 18:20:57 +02:00
ed9e295c1b Merge pull request #876 from Smart123s/fix/floodgate/plib-chnext
Fix Floodgate prefix with ProtocolLib
2022-08-10 16:33:40 +02:00
c8c4aa522d Remove Floodgate 1.0 warning
Floodgate 2.0 has been released a long time ago.
2022-08-09 17:27:29 +02:00
649ce8cb1a Revert "Workaround for Floodgate prefixes with ProtocolLlib"
This reverts commit 94979a3
This reverts commit e82e7c7
This reverts commit b92911b
This reverts commit 03850ae
This reverts commit 8859ebb
2022-08-09 17:27:28 +02:00
61f949cf97 Revert "Fix Floodgate detection for buggy ProtocolLib"
This reverts commit 9978fe69
2022-08-09 17:27:28 +02:00
8571feef6d Reimplement skipped Floodgate tasks
Due to a bug in ProtocolLib, Floodgate will never execute some of its tasks if an async listener is registered.

 Related: https://github.com/GeyserMC/Floodgate/issues/143
 Skipped code: 5d5713ed9e/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java (L121-L175)

 Fixes #786
 Fixes #703
 Fixes #689
 Fixes #647
2022-08-09 17:27:27 +02:00
bf86042c52 Disable checkstyle line length for JavaDoc links
GitHub permalinks can easily get longer than 120 characters.
2022-08-09 16:54:11 +02:00
55e3531718 Extract channel from ProtocolLib event 2022-08-09 16:54:04 +02:00
1694a7a4f3 Do not override driver setting if using the simplified version 2022-08-09 09:25:56 +02:00
ba7613c32a Bump slf4j-jdk14 from 2.0.0-alpha7 to 2.0.0-beta1 (#874)
Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0-alpha7 to 2.0.0-beta1.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha7...v_2.0.0-beta1)

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-jdk14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-08 10:17:58 +02:00
3244a3ab64 Resolve the mysql driver if using the simplified version 2022-08-08 10:16:12 +02:00
833177933a Remove and fix storage driver check 2022-08-04 14:28:30 +02:00
091b558826 Allow any newer SQLite version 2022-08-03 13:31:19 +02:00
df5e6db183 Set SQLite setting using the correct data class
Fixes #870
2022-08-03 13:21:44 +02:00
845d16dd04 Simplify storage driver setting 2022-08-02 10:40:00 +02:00
6beaf194ce Let the JDBC DriveManager pick the right driver for us
According to HikariCP, it's recommended to use the DataSource version
if available. For MySQL, it's not recommended, because of bugs in the
network timeout implementation. Therefore, we use here the jdbc url
version still, but let the driver be picked by the DriveManager.

Fixes #591
2022-08-02 10:38:53 +02:00
62d7320a6e Bump junit-jupiter from 5.8.2 to 5.9.0 (#866)
Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.0.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-01 12:15:39 +02:00
c1207d884d Bump craftapi from 0.5.3 to 0.6 (#867)
Bumps [craftapi](https://github.com/games647/CraftAPI) from 0.5.3 to 0.6.
- [Release notes](https://github.com/games647/CraftAPI/releases)
- [Commits](https://github.com/games647/CraftAPI/commits)

---
updated-dependencies:
- dependency-name: com.github.games647:craftapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-01 12:15:25 +02:00
4a8e903773 Bump gson from 2.9.0 to 2.9.1 (#868)
Bumps [gson](https://github.com/google/gson) from 2.9.0 to 2.9.1.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.9.0...gson-parent-2.9.1)

---
updated-dependencies:
- dependency-name: com.google.code.gson:gson
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-01 12:15:14 +02:00
e53c4f89f0 Bump checkstyle from 10.3.1 to 10.3.2 (#865)
Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 10.3.1 to 10.3.2.
- [Release notes](https://github.com/checkstyle/checkstyle/releases)
- [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.3.1...checkstyle-10.3.2)

---
updated-dependencies:
- dependency-name: com.puppycrawl.tools:checkstyle
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-01 12:14:53 +02:00
ed45fada59 Update 1.19.1 public key verification 2022-07-28 17:06:06 +02:00
045f980f38 Forward client key even if verification is disabled 2022-07-28 16:26:35 +02:00
25c8be65ed Disable metadata checking if login packets are re-used on login
Some servers appear to re-use login packets. However, ProtocolLib
metadata sets the data based on the identity of the packet handle. This
makes the metadata available for fresh incoming packets too.

Related #855
2022-07-28 14:14:10 +02:00
e3778c865c Update dependencies 2022-07-28 13:02:58 +02:00
4765d6d4ef Bump geyser.version from 2.0.0-SNAPSHOT to 2.0.5-SNAPSHOT (#842)
Bumps `geyser.version` from 2.0.0-SNAPSHOT to 2.0.5-SNAPSHOT.

Updates `core` from 2.0.0-SNAPSHOT to 2.0.5-SNAPSHOT
- [Release notes](https://github.com/GeyserMC/Geyser/releases)
- [Commits](https://github.com/GeyserMC/Geyser/commits)

Updates `geyser-api` from 2.0.0-SNAPSHOT to 2.0.5-SNAPSHOT

---
updated-dependencies:
- dependency-name: org.geysermc:core
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.geysermc:geyser-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-28 12:42:40 +02:00
c886ccb9ef Remove commented out code 2022-07-27 14:37:11 +02:00
bbb615bb0f Merge branch 'tcpshield' 2022-07-27 14:31:55 +02:00
0e1774a175 Comment out potential debug code 2022-07-27 14:30:48 +02:00
f50bef3eb2 Fix TCPShield compat by using raw address for sessions
TCPShield overwrites the IP address during connection. ProtocolLib
doesn't notice this change, because it uses the end-to-end connection
which is the proxy IP. This causes getAddress calls during Spigot play
state and ProtocolLib auth state not match and then have conflicting
session ids.

A solution is also to hold onto the temporary player object. However
since we don't get a notification for a disconnect, holding it will
prevent to get GCed until the timeout occurs (1 minute).

Fixes #595
2022-07-27 14:30:42 +02:00
0e2e431d87 Use the address field for verify task 2022-07-27 14:30:04 +02:00
1e732d62cb Replace Field- and MethodUtils from breaking changes in ProtocolLib 2022-07-27 10:06:46 +02:00
94979a3cf6 Fix reading username with 1.19 and Floodgate
Related #856
2022-07-27 09:34:36 +02:00
564f713fce Simplify version setting 2022-07-25 16:28:38 +02:00
05708a256c Perform check for available reflection during compile time 2022-07-25 13:02:30 +02:00
1d45cad2ea Add connection debug messages for #855 2022-07-25 10:30:48 +02:00
e82e7c7856 Fix manual name change workaround for floodgate with 1.19
Fixes #854
2022-07-25 10:27:09 +02:00
8df5b11276 Fix Java 8 API compatibility 2022-07-22 18:46:46 +02:00
c090278f82 Reset Hikari to the Java 8 version 2022-07-22 13:31:43 +02:00
23d30eee1a Migrate to Java 8 (sponsored contribution)
Java >8 includes many helpful features and API additions. Current
phoronix benchmarks indicated a very similar performance. Furthermore,
the fact that Java 18 even works with 1.8.8 contributed to the
decision to move to Java 17 like vanilla Java in 1.19.

This also helps us to learn about the newer features added for our
personal interests. As this is a free project, this motivated us to make
this step.

Nevertheless, many server owners were frustrated about this decision.
Thanks to financial contribution, we revised this decision until Java 8
is end of life or no longer used actively used according to bstats.org
2022-07-22 13:26:52 +02:00
bd65c792d0 Use correct generics 2022-07-22 13:26:39 +02:00
79a83ce84d Throw exception for utilities 2022-07-22 13:26:39 +02:00
9fdbdeaf9a Use Lombok to create a record-like struct 2022-07-22 13:26:39 +02:00
dd2fca2ea1 Replace var with val to prevent conflicts with newer Java version
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 81abad1..d148bc1 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
@@ -59,7 +59,7 @@ import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;

-import lombok.var;
+import lombok.val;

 /**
  * Encryption and decryption minecraft util for connection between servers
@@ -179,9 +179,9 @@ final class EncryptionUtil {

     private static PublicKey loadMojangSessionKey()
             throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
-        var keyUrl = FastLoginBukkit.class.getClassLoader().getResource("yggdrasil_session_pubkey.der");
-        var keyData = Resources.toByteArray(keyUrl);
-        var keySpec = new X509EncodedKeySpec(keyData);
+        val keyUrl = FastLoginBukkit.class.getClassLoader().getResource("yggdrasil_session_pubkey.der");
+        val keyData = Resources.toByteArray(keyUrl);
+        val keySpec = new X509EncodedKeySpec(keyData);

         return KeyFactory.getInstance("RSA").generatePublic(keySpec);
     }
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 dc208ff..1363cb3 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
@@ -60,7 +60,7 @@ import java.util.UUID;
 import javax.crypto.Cipher;
 import javax.crypto.SecretKey;

-import lombok.var;
+import lombok.val;
 import org.bukkit.entity.Player;

 import static com.comphenix.protocol.PacketType.Login.Client.START;
@@ -269,7 +269,7 @@ public class VerifyResponseTask implements Runnable {
             startPacket.getStrings().write(0, username);

             EquivalentConverter<WrappedProfileKeyData> converter = BukkitConverters.getWrappedPublicKeyDataConverter();
-            var wrappedKey = Optional.ofNullable(clientKey).map(key ->
+            val wrappedKey = Optional.ofNullable(clientKey).map(key ->
                 new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature())
             );

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 e4ce55a..9d6832e 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,7 +30,7 @@ import com.github.games647.fastlogin.core.CommonUtil;
 import net.md_5.bungee.api.chat.TextComponent;
 import net.md_5.bungee.chat.ComponentSerializer;

-import lombok.var;
+import lombok.val;
 import org.junit.jupiter.api.Test;

 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -39,12 +39,12 @@ class FastLoginBukkitTest {

     @Test
     void testRGB() {
-        var message = "&x00002a00002b&lText";
-        var msg = CommonUtil.translateColorCodes(message);
+        val message = "&x00002a00002b&lText";
+        val msg = CommonUtil.translateColorCodes(message);
         assertEquals(msg, "§x00002a00002b§lText");

-        var components = TextComponent.fromLegacyText(msg);
-        var expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
+        val components = TextComponent.fromLegacyText(msg);
+        val expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
         assertEquals(ComponentSerializer.toString(components), expected);
     }
 }
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
index 29b2f3d..c2fb34d 100644
--- 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
@@ -32,13 +32,13 @@ import com.google.gson.stream.JsonWriter;
 import java.io.IOException;
 import java.util.Base64;

-import lombok.var;
+import lombok.val;

 public class Base64Adapter extends TypeAdapter<byte[]> {

     @Override
     public void write(JsonWriter out, byte[] value) throws IOException {
-        var encoded = Base64.getEncoder().encodeToString(value);
+        val encoded = Base64.getEncoder().encodeToString(value);
         out.value(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 df3888e..1bbe864 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,7 +50,7 @@ import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;

-import lombok.var;
+import lombok.val;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
@@ -61,7 +61,7 @@ class EncryptionUtilTest {

     @Test
     void testVerifyToken() {
-        var random = ThreadLocalRandom.current();
+        val random = ThreadLocalRandom.current();
         byte[] token = EncryptionUtil.generateVerifyToken(random);

         assertAll(
@@ -89,10 +89,10 @@ class EncryptionUtilTest {

     @Test
     void testExpiredClientKey() throws Exception {
-        var clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
+        val 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();
+        val expiredTimestamp = clientKey.expiry();
         assertFalse(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp));
     }

@@ -106,7 +106,7 @@ class EncryptionUtilTest {
             "client_keys/invalid_wrong_signature.json"
     })
     void testInvalidClientKey(String clientKeySource) throws Exception {
-        var clientKey = ResourceLoader.loadClientKey(clientKeySource);
+        val clientKey = ResourceLoader.loadClientKey(clientKeySource);
         Instant expireTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);

         assertFalse(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp));
@@ -114,8 +114,8 @@ class EncryptionUtilTest {

     @Test
     void testValidClientKey() throws Exception {
-        var clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
-        var verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
+        val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
+        val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);

         assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp));
     }
@@ -123,7 +123,7 @@ class EncryptionUtilTest {
     @Test
     void testDecryptSharedSecret() throws Exception {
         KeyPair serverPair = EncryptionUtil.generateKeyPair();
-        var serverPK = serverPair.getPublic();
+        val serverPK = serverPair.getPublic();

         SecretKey secretKey = generateSharedKey();
         byte[] encryptedSecret = encrypt(serverPK, secretKey.getEncoded());
@@ -135,7 +135,7 @@ class EncryptionUtilTest {
     private static byte[] encrypt(PublicKey receiverKey, byte... message)
             throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
             IllegalBlockSizeException, BadPaddingException {
-        var encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm());
+        val encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm());
         encryptCipher.init(Cipher.ENCRYPT_MODE, receiverKey);
         return encryptCipher.doFinal(message);
     }
@@ -151,9 +151,9 @@ class EncryptionUtilTest {

     @Test
     void testServerIdHash() throws Exception {
-        var serverId = "";
-        var sharedSecret = generateSharedKey();
-        var serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
+        val serverId = "";
+        val sharedSecret = generateSharedKey();
+        val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();

         String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
         assertEquals(EncryptionUtil.getServerIdHashString(serverId, sharedSecret, serverPK), sessionHash);
@@ -167,7 +167,7 @@ class EncryptionUtilTest {
         // sha1.update(server's encoded public key from Encryption Request)
         // hash := sha1.hexdigest() # String of hex characters
         @SuppressWarnings("deprecation")
-        var hasher = Hashing.sha1().newHasher();
+        val hasher = Hashing.sha1().newHasher();
         hasher.putString(serverId, StandardCharsets.US_ASCII);
         hasher.putBytes(sharedSecret.getEncoded());
         hasher.putBytes(serverPK.getEncoded());
@@ -180,9 +180,9 @@ class EncryptionUtilTest {

     @Test
     void testServerIdHashWrongSecret() throws Exception {
-        var serverId = "";
-        var sharedSecret = generateSharedKey();
-        var serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
+        val serverId = "";
+        val sharedSecret = generateSharedKey();
+        val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();

         String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
         assertNotEquals(EncryptionUtil.getServerIdHashString("", generateSharedKey(), serverPK), sessionHash);
@@ -190,12 +190,12 @@ class EncryptionUtilTest {

     @Test
     void testServerIdHashWrongServerKey() {
-        var serverId = "";
-        var sharedSecret = generateSharedKey();
-        var serverPK = EncryptionUtil.generateKeyPair().getPublic();
+        val serverId = "";
+        val sharedSecret = generateSharedKey();
+        val serverPK = EncryptionUtil.generateKeyPair().getPublic();

         String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
-        var wrongPK = EncryptionUtil.generateKeyPair().getPublic();
+        val wrongPK = EncryptionUtil.generateKeyPair().getPublic();
         assertNotEquals(EncryptionUtil.getServerIdHashString("", sharedSecret, wrongPK), sessionHash);
     }

@@ -239,8 +239,8 @@ class EncryptionUtilTest {
     @Test
     void testNonce() throws Exception {
         byte[] expected = {1, 2, 3, 4};
-        var serverKey = EncryptionUtil.generateKeyPair();
-        var encryptedNonce = encrypt(serverKey.getPublic(), expected);
+        val serverKey = EncryptionUtil.generateKeyPair();
+        val encryptedNonce = encrypt(serverKey.getPublic(), expected);

         assertTrue(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
     }
@@ -248,19 +248,19 @@ class EncryptionUtilTest {
     @Test
     void testNonceIncorrect() throws Exception {
         byte[] expected = {1, 2, 3, 4};
-        var serverKey = EncryptionUtil.generateKeyPair();
+        val serverKey = EncryptionUtil.generateKeyPair();

         // flipped first character
-        var encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4});
+        val encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4});
         assertFalse(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
     }

     @Test
     void testNonceFailedDecryption() throws Exception {
         byte[] expected = {1, 2, 3, 4};
-        var serverKey = EncryptionUtil.generateKeyPair();
+        val serverKey = EncryptionUtil.generateKeyPair();
         // generate a new keypair that is different
-        var encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected);
+        val encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected);

         assertThrows(GeneralSecurityException.class,
                 () -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)
@@ -270,7 +270,7 @@ class EncryptionUtilTest {
     @Test
     void testNonceIncorrectEmpty() {
         byte[] expected = {1, 2, 3, 4};
-        var serverKey = EncryptionUtil.generateKeyPair();
+        val serverKey = EncryptionUtil.generateKeyPair();
         byte[] encryptedNonce = {};

         assertThrows(GeneralSecurityException.class,
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
index 9a75fc5..01b734e 100644
--- 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
@@ -32,13 +32,13 @@ import com.google.gson.annotations.JsonAdapter;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;

-import lombok.var;
+import lombok.val;

 public class SignatureTestData {

     public static SignatureTestData fromResource(String resourceName) throws IOException {
-        var keyUrl = Resources.getResource(resourceName);
-        var encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
+        val keyUrl = Resources.getResource(resourceName);
+        val encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII);

         return new Gson().fromJson(encodedSignature, SignatureTestData.class);
     }
diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java
index 148282c..c83c14d 100644
--- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java
+++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java
@@ -57,6 +57,7 @@ import net.md_5.bungee.api.chat.TextComponent;
 import net.md_5.bungee.api.connection.PendingConnection;
 import net.md_5.bungee.api.connection.ProxiedPlayer;
 import net.md_5.bungee.api.connection.Server;
+import net.md_5.bungee.api.plugin.Listener;
 import net.md_5.bungee.api.plugin.Plugin;
 import net.md_5.bungee.api.plugin.PluginManager;
 import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
@@ -100,7 +101,7 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
         //events
         PluginManager pluginManager = getProxy().getPluginManager();

-        ConnectListener connectListener = new ConnectListener(this, core.getAntiBot());
+        Listener connectListener = new ConnectListener(this, core.getAntiBot());
         pluginManager.registerListener(this, connectListener);
         pluginManager.registerListener(this, new PluginMessageListener(this));
2022-07-22 13:26:38 +02:00
7e9da9fd7c Migrate to Java 8 (sponsored contribution)
Java >8 includes many helpful features and API additions. Current
phoronix benchmarks indicated a very similar performance. Furthermore,
the fact that Java 18 even works with 1.8.8 contributed to the
decision to move to Java 17 like vanilla Java in 1.19.

This also helps us to learn about the newer features added for our
personal interests. As this is a free project, this motivated us to make
this step.

Nevertheless, many server owners were frustrated about this decision.
Thanks to financial contribution, we revised this decision until Java 8
is end of life or no longer used actively used according to bstats.org

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 68095f6..81abad1 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
@@ -50,7 +50,7 @@ import java.time.Instant;
 import java.util.Arrays;
 import java.util.Base64;
 import java.util.Base64.Encoder;
-import java.util.random.RandomGenerator;
+import java.util.Random;

 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
@@ -59,6 +59,8 @@ import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;

+import lombok.var;
+
 /**
  * Encryption and decryption minecraft util for connection between servers
  * and paid Minecraft account clients.
@@ -112,7 +114,7 @@ final class EncryptionUtil {
      * @param random random generator
      * @return a token with 4 bytes long
      */
-    public static byte[] generateVerifyToken(RandomGenerator random) {
+    public static byte[] generateVerifyToken(Random random) {
         byte[] token = new byte[VERIFY_TOKEN_LENGTH];
         random.nextBytes(token);
         return token;
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index 2e21ccb..4ce14ff 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -56,6 +56,7 @@ import javax.crypto.BadPaddingException;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;

+import lombok.var;
 import org.bukkit.entity.Player;

 import static com.comphenix.protocol.PacketType.Login.Client.ENCRYPTION_BEGIN;
@@ -171,7 +172,7 @@ public class ProtocolLibListener extends PacketAdapter {
                 Either<byte[], ?> either = packet.getSpecificModifier(Either.class).read(0);
                 if (clientPublicKey == null) {
                     Optional<byte[]> left = either.left();
-                    if (left.isEmpty()) {
+                    if (!left.isPresent()) {
                         plugin.getLog().error("No verify token sent if requested without player signed key {}", sender);
                         return false;
                     }
@@ -179,7 +180,7 @@ public class ProtocolLibListener extends PacketAdapter {
                     return EncryptionUtil.verifyNonce(expectedToken, keyPair.getPrivate(), left.get());
                 } else {
                     Optional<?> optSignatureData = either.right();
-                    if (optSignatureData.isEmpty()) {
+                    if (!optSignatureData.isPresent()) {
                         plugin.getLog().error("No signature given to sent player signing key {}", sender);
                         return false;
                     }
@@ -219,7 +220,7 @@ public class ProtocolLibListener extends PacketAdapter {
             .optionRead(0);

         var clientKey = profileKey.flatMap(opt -> opt).flatMap(this::verifyPublicKey);
-        if (verifyClientKeys && clientKey.isEmpty()) {
+        if (verifyClientKeys && !clientKey.isPresent()) {
             // missing or incorrect
             // expired always not allowed
             player.kickPlayer(plugin.getCore().getMessage("invalid-public-key"));
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 829d764..dc208ff 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
@@ -60,6 +60,7 @@ import java.util.UUID;
 import javax.crypto.Cipher;
 import javax.crypto.SecretKey;

+import lombok.var;
 import org.bukkit.entity.Player;

 import static com.comphenix.protocol.PacketType.Login.Client.START;
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/packet/ClientPublicKey.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/packet/ClientPublicKey.java
index e375e29..619d41d 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/packet/ClientPublicKey.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/packet/ClientPublicKey.java
@@ -27,8 +27,62 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib.packet;

 import java.security.PublicKey;
 import java.time.Instant;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.StringJoiner;

-public record ClientPublicKey(Instant expiry, PublicKey key, byte[] signature) {
+public final class ClientPublicKey {
+    private final Instant expiry;
+    private final PublicKey key;
+    private final byte[] signature;
+
+    public ClientPublicKey(Instant expiry, PublicKey key, byte[] signature) {
+        this.expiry = expiry;
+        this.key = key;
+        this.signature = signature;
+    }
+
+    public Instant expiry() {
+        return expiry;
+    }
+
+    public PublicKey key() {
+        return key;
+    }
+
+    public byte[] signature() {
+        return signature;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (obj == null || obj.getClass() != this.getClass()) {
+            return false;
+        }
+
+        ClientPublicKey that = (ClientPublicKey) obj;
+        return Objects.equals(this.expiry, that.expiry)
+                && Objects.equals(this.key, that.key)
+                && Arrays.equals(this.signature, that.signature);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(expiry, key, signature);
+    }
+
+    @Override
+    public String toString() {
+        return new StringJoiner(", ", ClientPublicKey.class.getSimpleName() + "[", "]")
+                .add("expiry=" + expiry)
+                .add("key=" + key)
+                .add("signature=" + Arrays.toString(signature))
+                .toString();
+    }

     public boolean isExpired(Instant verifyTimestamp) {
         return !verifyTimestamp.isBefore(expiry);
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 07ff623..e4ce55a 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,6 +30,7 @@ import com.github.games647.fastlogin.core.CommonUtil;
 import net.md_5.bungee.api.chat.TextComponent;
 import net.md_5.bungee.chat.ComponentSerializer;

+import lombok.var;
 import org.junit.jupiter.api.Test;

 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -43,8 +44,7 @@ class FastLoginBukkitTest {
         assertEquals(msg, "§x00002a00002b§lText");

         var components = TextComponent.fromLegacyText(msg);
-        var expected = """
-            {"bold":true,"color":"#00a00b","text":"Text"}""";
+        var expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
         assertEquals(ComponentSerializer.toString(components), expected);
     }
 }
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
index c2a2d1a..29b2f3d 100644
--- 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
@@ -32,6 +32,8 @@ import com.google.gson.stream.JsonWriter;
 import java.io.IOException;
 import java.util.Base64;

+import lombok.var;
+
 public class Base64Adapter extends TypeAdapter<byte[]> {

     @Override
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 e1b8bea..df3888e 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,6 +50,7 @@ import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;

+import lombok.var;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
diff --git a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ResourceLoader.java b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ResourceLoader.java
index 4361fe9..ac0991e 100644
--- a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ResourceLoader.java
+++ b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ResourceLoader.java
@@ -33,6 +33,7 @@ import com.google.gson.JsonObject;
 import java.io.IOException;
 import java.io.Reader;
 import java.io.StringReader;
+import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
@@ -57,19 +58,19 @@ public class ResourceLoader {
         ) {
             PemObject pemObject = pemReader.readPemObject();
             byte[] content = pemObject.getContent();
-            var privateKeySpec = new PKCS8EncodedKeySpec(content);
+            PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(content);

-            var factory = KeyFactory.getInstance("RSA");
+            KeyFactory factory = KeyFactory.getInstance("RSA");
             return (RSAPrivateKey) factory.generatePrivate(privateKeySpec);
         }
     }

     protected static ClientPublicKey loadClientKey(String path)
         throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
-        var keyUrl = Resources.getResource(path);
+        URL keyUrl = Resources.getResource(path);

-        var lines = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
-        var object = new Gson().fromJson(lines, JsonObject.class);
+        String lines = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
+        JsonObject object = new Gson().fromJson(lines, JsonObject.class);

         Instant expires = Instant.parse(object.getAsJsonPrimitive("expires_at").getAsString());
         String key = object.getAsJsonPrimitive("key").getAsString();
@@ -87,9 +88,9 @@ public class ResourceLoader {
         ) {
             PemObject pemObject = pemReader.readPemObject();
             byte[] content = pemObject.getContent();
-            var pubKeySpec = new X509EncodedKeySpec(content);
+            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);

-            var factory = KeyFactory.getInstance("RSA");
+            KeyFactory factory = KeyFactory.getInstance("RSA");
             return (RSAPublicKey) factory.generatePublic(pubKeySpec);
         }
     }
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
index ee62e24..9a75fc5 100644
--- 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
@@ -32,6 +32,8 @@ import com.google.gson.annotations.JsonAdapter;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;

+import lombok.var;
+
 public class SignatureTestData {

     public static SignatureTestData fromResource(String resourceName) throws IOException {
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java b/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java
index 324e240..89dc7ee 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java
@@ -25,8 +25,7 @@
  */
 package com.github.games647.fastlogin.core;

-import com.github.games647.craftapi.cache.SafeCacheBuilder;
-import com.google.common.cache.CacheLoader;
+import com.google.common.cache.CacheBuilder;

 import java.lang.reflect.Constructor;
 import java.util.concurrent.ConcurrentMap;
@@ -43,7 +42,7 @@ public final class CommonUtil {
     private static final char TRANSLATED_CHAR = '§';

     public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) {
-        SafeCacheBuilder<Object, Object> builder = SafeCacheBuilder.newBuilder();
+        CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();

         if (expireAfterWrite > 0) {
             builder.expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES);
@@ -53,9 +52,7 @@ public final class CommonUtil {
             builder.maximumSize(maxSize);
         }

-        return builder.build(CacheLoader.from(() -> {
-            throw new UnsupportedOperationException();
-        }));
+        return (ConcurrentMap<K, V>) builder.build().asMap();
     }

     public static String translateColorCodes(String rawMessage) {
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/StoredProfile.java b/core/src/main/java/com/github/games647/fastlogin/core/StoredProfile.java
index ee58bde..26492d3 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/StoredProfile.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/StoredProfile.java
@@ -118,10 +118,11 @@ public class StoredProfile extends Profile {
             return true;
         }

-        if (!(o instanceof StoredProfile that)) {
+        if (!(o instanceof StoredProfile)) {
             return false;
         }

+        StoredProfile that = (StoredProfile) o;
         if (!super.equals(o)) {
             return false;
         }
diff --git a/pom.xml b/pom.xml
index f2f3950..e1b26b0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,7 +48,7 @@
         <!-- Set default for non-git clones -->
         <git.commit.id>Unknown</git.commit.id>

-        <java.version>17</java.version>
+        <java.version>8</java.version>
         <maven.compiler.source>${java.version}</maven.compiler.source>
         <maven.compiler.target>${java.version}</maven.compiler.target>

@@ -189,5 +189,12 @@
             <version>5.8.2</version>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.24</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 </project>
2022-07-22 13:26:38 +02:00
f513cffbaf Migrate tests to use Junit5 2022-07-22 13:24:57 +02:00
cdb3c50b87 Merge pull request #837 from games647/dependabot/maven/org.mariadb.jdbc-mariadb-java-client-3.0.6
Bump mariadb-java-client from 3.0.5 to 3.0.6
2022-07-17 16:56:31 +02:00
e338a768ca Use class loader for loading the session key
Related #849
2022-07-17 16:54:06 +02:00
18a8d7a5dc Replace Guava with Java joiner 2022-07-17 16:54:06 +02:00
6cf1a79ae1 Reset ignore list of dependabot 2022-07-17 16:54:06 +02:00
0e8b51e148 Minimize dependency jars 2022-07-17 16:54:06 +02:00
ef34550a53 Merge pull request #848 from Smart123s/fix/duplicate/forwardccommand
Remove duplicate forwardCrackedCommand()
2022-07-15 08:56:45 +02:00
fe1d4944a8 Remove duplicate forwardCrackedCommand()
The forwardCrackedCommand() method is invoked before, the removed code, that does the exact same thing.
2022-07-14 17:11:25 +02:00
423bfa2275 Re-enable force checkstyle check 2022-07-11 13:00:15 +02:00
06b0cf9e02 Merge pull request #779 from Smart123s/feature/codeformatter
Add a code formatter
2022-07-11 12:20:42 +02:00
752600f0e2 Fix code formatting according to checkstyle config 2022-07-11 12:14:31 +02:00
cf53ecacdf Disable hard to meet checkstyle requirements
This is temporary.
2022-07-11 12:14:31 +02:00
b740331d31 Add checkstyle.xml
Source: 0332cbe0ba/src/main/resources/sun_checks.xml
2022-07-11 12:14:30 +02:00
13b2f22e31 Add code formatter
Add checkstyle as maven goal
This will fail the build if the code is not formatted properly
2022-07-11 12:14:30 +02:00
d5d4a32c3f Fix forwarding the correct player signature
Fixes #843
2022-07-11 12:13:36 +02:00
bea3503eff Merge pull request #821 from games647/809-119-compatibility-1
1.19 Support
2022-07-11 09:43:17 +02:00
adfae507ac Flip velocity check to scan for newer Paper configurations first
The PaperConfig class file still exists in newer versions
2022-07-08 16:43:02 +02:00
e89cb3293a Disable verify client keys by default for older compatibility
This also mimics the default vanilla configuration.
2022-07-08 16:30:41 +02:00
b4ddf4fb19 Reduce dependency list to improve on build time 2022-07-08 16:29:25 +02:00
eb47dd3254 Restore compatibility with older Minecraft versions 2022-07-08 16:28:56 +02:00
0b0a46a18a Make client public key verification optional 2022-07-06 17:08:58 +02:00
7c8de84a34 Bump dependencies 2022-07-06 17:08:57 +02:00
c448be8c5f Bump mariadb-java-client from 3.0.5 to 3.0.6
Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.5 to 3.0.6.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.5...3.0.6)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-04 07:04:41 +00:00
339156be18 Use multiple threads to build the project 2022-07-02 12:37:49 +02:00
944db748e8 Upgrade GitHub actions to use Java 18 2022-07-02 12:36:58 +02:00
bb1cbb79f2 Support newer Paper configuration with clearer error messages 2022-06-28 18:40:51 +02:00
cf356099a0 Fix public key timestamp verification. 2022-06-28 18:34:25 +02:00
91c01e3422 Reuse verify token tick message for signature verification
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index 6c578ff..92e1dcd 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -153,11 +153,11 @@ public class ProtocolLibListener extends PacketAdapter {
                     Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, sharedSecret, keyPair);
                     plugin.getScheduler().runAsync(verifyTask);
                 } else {
-                    sender.kickPlayer("Invalid signature");
+                    sender.kickPlayer(plugin.getCore().getMessage("invalid-verify-token"));
                     plugin.getLog().error("Invalid signature from player {}", sender);
                 }
             } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException signatureEx) {
-                sender.kickPlayer("Invalid signature");
+                sender.kickPlayer(plugin.getCore().getMessage("invalid-verify-token"));
                 plugin.getLog().error("Invalid signature from player {}", sender, signatureEx);
             }
         }
2022-06-28 18:34:25 +02:00
c118430bf5 Kick players using an invalid public key 2022-06-28 18:34:25 +02:00
ce59172839 Log signature verification errors to help administrators
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index c3e159a..da28f38 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -154,9 +154,11 @@ public class ProtocolLibListener extends PacketAdapter {
                     plugin.getScheduler().runAsync(verifyTask);
                 } else {
                     sender.kickPlayer("Invalid signature");
+                    plugin.getLog().error("Invalid signature from player {}", sender);
                 }
-            } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
+            } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException signatureEx) {
                 sender.kickPlayer("Invalid signature");
+                plugin.getLog().error("Invalid signature from player {}", sender, signatureEx);
             }
         }
     }
2022-06-28 18:34:25 +02:00
34e63b7eae Make profile key optional 2022-06-28 18:34:25 +02:00
e8bb3ec7a9 Validate other encryption methods 2022-06-28 18:34:24 +02:00
1d46640b42 Limit length of server keys 2022-06-28 18:34:24 +02:00
a0fddd69aa Decrease necessary entropy for running tests 2022-06-28 18:34:24 +02:00
700b889aa9 Improve precision and flexibility of encrypt methods 2022-06-28 18:34:24 +02:00
3767b022a9 Migrate to guava hashing to replace unneeded exceptions 2022-06-28 18:34:24 +02:00
11077a002d Migrate public key to record 2022-06-28 18:34:24 +02:00
d9bf7267a6 Test valid server key pairs 2022-06-28 18:34:23 +02:00
53e6fe6ddf Missing synchronization access to the username 2022-06-28 18:34:23 +02:00
1c528fb9cb Clean up
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BungeeManager.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BungeeManager.java
index 49ff879..7149238 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BungeeManager.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BungeeManager.java
@@ -36,6 +36,7 @@ import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
@@ -62,7 +63,7 @@ public class BungeeManager {
     private final FastLoginBukkit plugin;
     private boolean enabled;

-    private final Set<UUID> firedJoinEvents = new HashSet<>();
+    private final Collection<UUID> firedJoinEvents = new HashSet<>();

     public BungeeManager(FastLoginBukkit plugin) {
         this.plugin = plugin;
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/CrackedCommand.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/CrackedCommand.java
index a6ac9b7..6153338 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/CrackedCommand.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/CrackedCommand.java
@@ -44,7 +44,7 @@ public class CrackedCommand extends ToggleCommand {
     @Override
     public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
         if (args.length == 0) {
-            onCrackedSelf(sender, command, args);
+            onCrackedSelf(sender);
         } else {
             onCrackedOther(sender, command, args);
         }
@@ -52,7 +52,7 @@ public class CrackedCommand extends ToggleCommand {
         return true;
     }

-    private void onCrackedSelf(CommandSender sender, Command cmd, String[] args) {
+    private void onCrackedSelf(CommandSender sender) {
         if (isConsole(sender)) {
             return;
         }
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/PremiumCommand.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/PremiumCommand.java
index 26b6d31..9f1ef98 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/PremiumCommand.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/PremiumCommand.java
@@ -51,7 +51,7 @@ public class PremiumCommand extends ToggleCommand {
     @Override
     public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
         if (args.length == 0) {
-            onPremiumSelf(sender, command, args);
+            onPremiumSelf(sender);
         } else {
             onPremiumOther(sender, command, args);
         }
@@ -59,7 +59,7 @@ public class PremiumCommand extends ToggleCommand {
         return true;
     }

-    private void onPremiumSelf(CommandSender sender, Command cmd, String[] args) {
+    private void onPremiumSelf(CommandSender sender) {
         if (isConsole(sender)) {
             return;
         }
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginAutoLoginEvent.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginAutoLoginEvent.java
index 8c61330..6412c6d 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginAutoLoginEvent.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginAutoLoginEvent.java
@@ -28,6 +28,7 @@ package com.github.games647.fastlogin.bukkit.event;
 import com.github.games647.fastlogin.core.StoredProfile;
 import com.github.games647.fastlogin.core.shared.LoginSession;
 import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
+
 import org.bukkit.event.Cancellable;
 import org.bukkit.event.Event;
 import org.bukkit.event.HandlerList;
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginPreLoginEvent.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginPreLoginEvent.java
index 6b2edfc..5bf6df9 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginPreLoginEvent.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginPreLoginEvent.java
@@ -28,6 +28,7 @@ package com.github.games647.fastlogin.bukkit.event;
 import com.github.games647.fastlogin.core.StoredProfile;
 import com.github.games647.fastlogin.core.shared.LoginSource;
 import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
+
 import org.bukkit.event.Event;
 import org.bukkit.event.HandlerList;
 import org.jetbrains.annotations.NotNull;
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginPremiumToggleEvent.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginPremiumToggleEvent.java
index 146efb0..0904826 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginPremiumToggleEvent.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/event/BukkitFastLoginPremiumToggleEvent.java
@@ -27,6 +27,7 @@ package com.github.games647.fastlogin.bukkit.event;

 import com.github.games647.fastlogin.core.StoredProfile;
 import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
+
 import org.bukkit.event.Event;
 import org.bukkit.event.HandlerList;
 import org.jetbrains.annotations.NotNull;
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hook/AuthMeHook.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hook/AuthMeHook.java
index fecfb73..45d5a5f 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hook/AuthMeHook.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hook/AuthMeHook.java
@@ -28,18 +28,20 @@ package com.github.games647.fastlogin.bukkit.hook;
 import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
 import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
 import com.github.games647.fastlogin.core.hooks.AuthPlugin;
+
 import fr.xephi.authme.api.v3.AuthMeApi;
 import fr.xephi.authme.events.RestoreSessionEvent;
 import fr.xephi.authme.process.Management;
 import fr.xephi.authme.process.register.executors.ApiPasswordRegisterParams;
 import fr.xephi.authme.process.register.executors.RegistrationMethod;
+
+import java.lang.reflect.Field;
+
 import org.bukkit.entity.Player;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.EventPriority;
 import org.bukkit.event.Listener;

-import java.lang.reflect.Field;
-
 /**
  * GitHub: <a href="https://github.com/Xephi/AuthMeReloaded/">...</a>
  * <p>
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/PaperCacheListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/PaperCacheListener.java
index e444ed9..44ff6ea 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/PaperCacheListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/PaperCacheListener.java
@@ -29,6 +29,7 @@ import com.destroystokyo.paper.profile.ProfileProperty;
 import com.github.games647.craftapi.model.skin.Textures;
 import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
 import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
+
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.EventPriority;
 import org.bukkit.event.Listener;
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ManualNameChange.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ManualNameChange.java
index cdf3999..2a36e88 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ManualNameChange.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ManualNameChange.java
@@ -25,6 +25,7 @@
  */
 package com.github.games647.fastlogin.bukkit.listener.protocollib;

+import com.comphenix.protocol.ProtocolLibrary;
 import com.comphenix.protocol.events.PacketAdapter;
 import com.comphenix.protocol.events.PacketContainer;
 import com.comphenix.protocol.events.PacketEvent;
@@ -36,8 +37,6 @@ import org.geysermc.floodgate.api.FloodgateApi;

 import static com.comphenix.protocol.PacketType.Login.Client.START;

-import com.comphenix.protocol.ProtocolLibrary;
-
 /**
  * Manually inject Floodgate player name prefixes.
  * <br>
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 485c065..d3b38ea 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
@@ -49,7 +49,7 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco

     private final FastLoginBukkit plugin;
     private final PacketEvent packetEvent;
-    private final PublicKey publicKey;
+    private final PublicKey serverKey;

     private final Random random;

@@ -57,12 +57,12 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
     private final String username;

     public NameCheckTask(FastLoginBukkit plugin, Random random, Player player, PacketEvent packetEvent,
-                         String username, PublicKey publicKey) {
+                         String username, PublicKey serverKey) {
         super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());

         this.plugin = plugin;
         this.packetEvent = packetEvent;
-        this.publicKey = publicKey;
+        this.serverKey = serverKey;
         this.random = random;
         this.player = player;
         this.username = username;
@@ -71,9 +71,9 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
     @Override
     public void run() {
         try {
-            Optional<WrappedProfileKeyData> publicKey = packetEvent.getPacket().getOptionals(BukkitConverters.getWrappedPublicKeyDataConverter()).read(0);
+            Optional<WrappedProfileKeyData> clientKey = packetEvent.getPacket().getOptionals(BukkitConverters.getWrappedPublicKeyDataConverter()).read(0);

-            super.onLogin(username, new ProtocolLibLoginSource(player, random, publicKey.get(), this.publicKey));
+            super.onLogin(username, new ProtocolLibLoginSource(player, random, clientKey.get(), serverKey));
         } finally {
             ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
         }
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index 55a8f33..89d855d 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -127,7 +127,7 @@ public class ProtocolLibListener extends PacketAdapter {
         }
     }

-    private Boolean isFastLoginPacket(PacketEvent packetEvent) {
+    private boolean isFastLoginPacket(PacketEvent packetEvent) {
         return packetEvent.getPacket().getMeta(SOURCE_META_KEY)
                 .map(val -> val.equals(plugin.getName()))
                 .orElse(false);
@@ -146,7 +146,7 @@ public class ProtocolLibListener extends PacketAdapter {
             long salt = FuzzyReflection.getFieldValue(signatureData, Long.TYPE, true);
             byte[] signature = FuzzyReflection.getFieldValue(signatureData, byte[].class, true);

-            PublicKey publicKey = session.getClientPublicKey().getKey();
+            PublicKey publicKey = session.getClientPublicKey().key();
             try {
                 if (EncryptionUtil.verifySignedNonce(session.getVerifyToken(), publicKey, salt, signature)) {
                     packetEvent.getAsyncMarker().incrementProcessingDelay();
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 d87b602..d4c1130 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
@@ -33,7 +33,6 @@ 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;
 import java.net.InetSocketAddress;
 import java.security.PublicKey;
 import java.util.Arrays;
@@ -64,7 +63,7 @@ class ProtocolLibLoginSource implements LoginSource {
     }

     @Override
-    public void enableOnlinemode() throws InvocationTargetException {
+    public void enableOnlinemode() {
         verifyToken = EncryptionUtil.generateVerifyToken(random);

         /*
@@ -92,7 +91,7 @@ class ProtocolLibLoginSource implements LoginSource {
     }

     @Override
-    public void kick(String message) throws InvocationTargetException {
+    public void kick(String message) {
         ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();

         PacketContainer kickPacket = new PacketContainer(DISCONNECT);
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/SkinApplyListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/SkinApplyListener.java
index 7d43835..3d4a807 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/SkinApplyListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/SkinApplyListener.java
@@ -34,6 +34,9 @@ import com.comphenix.protocol.wrappers.WrappedSignedProperty;
 import com.github.games647.craftapi.model.skin.Textures;
 import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
 import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
+
+import java.lang.reflect.InvocationTargetException;
+
 import org.bukkit.entity.Player;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.EventPriority;
@@ -41,8 +44,6 @@ import org.bukkit.event.Listener;
 import org.bukkit.event.player.PlayerLoginEvent;
 import org.bukkit.event.player.PlayerLoginEvent.Result;

-import java.lang.reflect.InvocationTargetException;
-
 public class SkinApplyListener implements Listener {

     private static final Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/task/FloodgateAuthTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/task/FloodgateAuthTask.java
index acb3597..ef9fed0 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/task/FloodgateAuthTask.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/task/FloodgateAuthTask.java
@@ -25,6 +25,11 @@
  */
 package com.github.games647.fastlogin.bukkit.task;

+import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
+import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
+import com.github.games647.fastlogin.core.shared.FastLoginCore;
+import com.github.games647.fastlogin.core.shared.FloodgateManagement;
+
 import java.net.InetSocketAddress;
 import java.util.UUID;

@@ -33,11 +38,6 @@ import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
 import org.geysermc.floodgate.api.player.FloodgatePlayer;

-import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
-import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
-import com.github.games647.fastlogin.core.shared.FastLoginCore;
-import com.github.games647.fastlogin.core.shared.FloodgateManagement;
-
 public class FloodgateAuthTask extends FloodgateManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {

     public FloodgateAuthTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player, FloodgatePlayer floodgatePlayer) {
diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/event/BungeeFastLoginAutoLoginEvent.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/event/BungeeFastLoginAutoLoginEvent.java
index 365a198..6503ff9 100644
--- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/event/BungeeFastLoginAutoLoginEvent.java
+++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/event/BungeeFastLoginAutoLoginEvent.java
@@ -28,6 +28,7 @@ package com.github.games647.fastlogin.bungee.event;
 import com.github.games647.fastlogin.core.StoredProfile;
 import com.github.games647.fastlogin.core.shared.LoginSession;
 import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
+
 import net.md_5.bungee.api.plugin.Cancellable;
 import net.md_5.bungee.api.plugin.Event;

diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/event/BungeeFastLoginPreLoginEvent.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/event/BungeeFastLoginPreLoginEvent.java
index b350763..2128cab 100644
--- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/event/BungeeFastLoginPreLoginEvent.java
+++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/event/BungeeFastLoginPreLoginEvent.java
@@ -28,6 +28,7 @@ package com.github.games647.fastlogin.bungee.event;
 import com.github.games647.fastlogin.core.StoredProfile;
 import com.github.games647.fastlogin.core.shared.LoginSource;
 import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
+
 import net.md_5.bungee.api.plugin.Event;

 public class BungeeFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {
diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/AsyncToggleMessage.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/AsyncToggleMessage.java
index 98c384c..2965855 100644
--- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/AsyncToggleMessage.java
+++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/AsyncToggleMessage.java
@@ -29,8 +29,8 @@ import com.github.games647.fastlogin.bungee.FastLoginBungee;
 import com.github.games647.fastlogin.bungee.event.BungeeFastLoginPremiumToggleEvent;
 import com.github.games647.fastlogin.core.StoredProfile;
 import com.github.games647.fastlogin.core.shared.FastLoginCore;
-
 import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
+
 import net.md_5.bungee.api.CommandSender;
 import net.md_5.bungee.api.ProxyServer;
 import net.md_5.bungee.api.chat.TextComponent;
diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/FloodgateAuthTask.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/FloodgateAuthTask.java
index 72719cf..5133198 100644
--- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/FloodgateAuthTask.java
+++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/FloodgateAuthTask.java
@@ -25,19 +25,19 @@
  */
 package com.github.games647.fastlogin.bungee.task;

+import com.github.games647.fastlogin.bungee.BungeeLoginSession;
+import com.github.games647.fastlogin.bungee.FastLoginBungee;
+import com.github.games647.fastlogin.core.shared.FastLoginCore;
+import com.github.games647.fastlogin.core.shared.FloodgateManagement;
+
 import java.net.InetSocketAddress;
 import java.util.UUID;

-import org.geysermc.floodgate.api.player.FloodgatePlayer;
-
 import net.md_5.bungee.api.CommandSender;
 import net.md_5.bungee.api.connection.ProxiedPlayer;
 import net.md_5.bungee.api.connection.Server;

-import com.github.games647.fastlogin.bungee.BungeeLoginSession;
-import com.github.games647.fastlogin.bungee.FastLoginBungee;
-import com.github.games647.fastlogin.core.shared.FastLoginCore;
-import com.github.games647.fastlogin.core.shared.FloodgateManagement;
+import org.geysermc.floodgate.api.player.FloodgatePlayer;

 public class FloodgateAuthTask
         extends FloodgateManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/hooks/DefaultPasswordGenerator.java b/core/src/main/java/com/github/games647/fastlogin/core/hooks/DefaultPasswordGenerator.java
index b6802a6..9caadef 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/hooks/DefaultPasswordGenerator.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/hooks/DefaultPasswordGenerator.java
@@ -26,7 +26,7 @@
 package com.github.games647.fastlogin.core.hooks;

 import java.security.SecureRandom;
-import java.util.Random;
+import java.util.random.RandomGenerator;
 import java.util.stream.IntStream;

 public class DefaultPasswordGenerator<P> implements PasswordGenerator<P> {
@@ -35,7 +35,7 @@ public class DefaultPasswordGenerator<P> implements PasswordGenerator<P> {
     private static final char[] PASSWORD_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
             .toCharArray();

-    private final Random random = new SecureRandom();
+    private final RandomGenerator random = new SecureRandom();

     @Override
     public String getRandomPassword(P player) {
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/hooks/bedrock/FloodgateService.java b/core/src/main/java/com/github/games647/fastlogin/core/hooks/bedrock/FloodgateService.java
index 7e6a1c2..1b555f1 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/hooks/bedrock/FloodgateService.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/hooks/bedrock/FloodgateService.java
@@ -54,14 +54,13 @@ public class FloodgateService extends BedrockService<FloodgatePlayer> {
      * <li>autoLoginFloodgate
      * <li>autoRegisterFloodgate
      * </ul>
-     * </p>
      *
      * @param key the key of the entry in config.yml
      * @return <b>true</b> if the entry's value is "true", "false", or "linked"
      */
     public boolean isValidFloodgateConfigString(String key) {
         String value = core.getConfig().get(key).toString().toLowerCase(Locale.ENGLISH);
-        if (!value.equals("true") && !value.equals("linked") && !value.equals("false") && !value.equals("no-conflict")) {
+        if (!"true".equals(value) && !"linked".equals(value) && !"false".equals(value) && !"no-conflict".equals(value)) {
             core.getPlugin().getLog().error("Invalid value detected for {} in FastLogin/config.yml.", key);
             return false;
         }
@@ -87,7 +86,7 @@ public class FloodgateService extends BedrockService<FloodgatePlayer> {
         } else {
             core.getPlugin().getLog().info("Skipping name conflict checking for player {}", username);
         }
-
+
         //Floodgate users don't need Java specific checks
         return true;
     }
@@ -98,7 +97,7 @@ public class FloodgateService extends BedrockService<FloodgatePlayer> {
      * username can be found
      * <br>
      * <i>Falls back to non-prefixed name checks, if ProtocolLib is installed</i>
-     *
+     *
      * @param prefixedUsername the name of the player with the prefix appended
      * @return FloodgatePlayer if found, null otherwise
      */
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/ForceLoginManagement.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/ForceLoginManagement.java
index c8c7bea..873a855 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/shared/ForceLoginManagement.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/ForceLoginManagement.java
@@ -25,10 +25,10 @@
  */
 package com.github.games647.fastlogin.core.shared;

-import com.github.games647.fastlogin.core.storage.SQLStorage;
 import com.github.games647.fastlogin.core.StoredProfile;
 import com.github.games647.fastlogin.core.hooks.AuthPlugin;
 import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
+import com.github.games647.fastlogin.core.storage.SQLStorage;

 public abstract class ForceLoginManagement<P extends C, C, L extends LoginSession, T extends PlatformPlugin<C>>
         implements Runnable {
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 3f753cd..34f196a 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
@@ -32,8 +32,8 @@ import java.util.concurrent.TimeUnit;

 import org.junit.Test;

-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;

 public class TickingRateLimiterTest {

@@ -43,7 +43,7 @@ public class TickingRateLimiterTest {
      * Always expired
      */
     @Test
-    public void allowExpire() throws InterruptedException {
+    public void allowExpire() {
         int size = 3;

         FakeTicker ticker = new FakeTicker(5_000_000L);
@@ -51,17 +51,17 @@ public class TickingRateLimiterTest {
         // 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++) {
-            assertTrue("Filling up", rateLimiter.tryAcquire());
+            assertThat("Filling up", rateLimiter.tryAcquire(), is(true));
         }

         for (int i = 0; i < size; i++) {
             ticker.add(Duration.ofSeconds(1));
-            assertTrue("Should be expired", rateLimiter.tryAcquire());
+            assertThat("Should be expired", rateLimiter.tryAcquire(), is(true));
         }
     }

     @Test
-    public void allowExpireNegative() throws InterruptedException {
+    public void allowExpireNegative() {
         int size = 3;

         FakeTicker ticker = new FakeTicker(-5_000_000L);
@@ -69,12 +69,12 @@ public class TickingRateLimiterTest {
         // 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++) {
-            assertTrue("Filling up", rateLimiter.tryAcquire());
+            assertThat("Filling up", rateLimiter.tryAcquire(), is(true));
         }

         for (int i = 0; i < size; i++) {
             ticker.add(Duration.ofSeconds(1));
-            assertTrue("Should be expired", rateLimiter.tryAcquire());
+            assertThat("Should be expired", rateLimiter.tryAcquire(), is(true));
         }
     }

@@ -90,10 +90,10 @@ public class TickingRateLimiterTest {
         // fill the size
         TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, size, TimeUnit.SECONDS.toMillis(30));
         for (int i = 0; i < size; i++) {
-            assertTrue("Filling up", rateLimiter.tryAcquire());
+            assertThat("Filling up", rateLimiter.tryAcquire(), is(true));
         }

-        assertFalse("Should be full and no entry should be expired", rateLimiter.tryAcquire());
+        assertThat("Should be full and no entry should be expired", rateLimiter.tryAcquire(), is(false));
     }

     /**
@@ -108,51 +108,51 @@ public class TickingRateLimiterTest {
         // fill the size
         TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, size, TimeUnit.SECONDS.toMillis(30));
         for (int i = 0; i < size; i++) {
-            assertTrue("Filling up", rateLimiter.tryAcquire());
+            assertThat("Filling up", rateLimiter.tryAcquire(), is(true));
         }

-        assertFalse("Should be full and no entry should be expired", rateLimiter.tryAcquire());
+        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() throws InterruptedException {
+    public void blockedNotAdded() {
         FakeTicker ticker = new FakeTicker(5_000_000L);

         // fill the size - 100ms should be reasonable high
         TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, 1, 100);
-        assertTrue("Filling up", rateLimiter.tryAcquire());
+        assertThat("Filling up", rateLimiter.tryAcquire(), is(true));

         ticker.add(Duration.ofMillis(50));

         // still is full - should fail
-        assertFalse("Expired too early", rateLimiter.tryAcquire());
+        assertThat("Expired too early", rateLimiter.tryAcquire(), is(false));

         // wait the remaining time and add a threshold, because
         ticker.add(Duration.ofMillis(50));
-        assertTrue("Request not released", rateLimiter.tryAcquire());
+        assertThat("Request not released", rateLimiter.tryAcquire(), is(true));
     }

     /**
      * Blocked attempts shouldn't replace existing ones.
      */
     @Test
-    public void blockedNotAddedNegative() throws InterruptedException {
+    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);
-        assertTrue("Filling up", rateLimiter.tryAcquire());
+        assertThat("Filling up", rateLimiter.tryAcquire(), is(true));

         ticker.add(Duration.ofMillis(50));

         // still is full - should fail
-        assertFalse("Expired too early", rateLimiter.tryAcquire());
+        assertThat("Expired too early", rateLimiter.tryAcquire(), is(false));

         // wait the remaining time and add a threshold, because
         ticker.add(Duration.ofMillis(50));
-        assertTrue("Request not released", rateLimiter.tryAcquire());
+        assertThat("Request not released", rateLimiter.tryAcquire(), is(true));
     }
 }
diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java
index 33c8c31..902fb03 100644
--- a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java
+++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java
@@ -45,9 +45,11 @@ import com.velocitypowered.api.proxy.InboundConnection;
 import com.velocitypowered.api.proxy.Player;
 import com.velocitypowered.api.proxy.server.RegisteredServer;
 import com.velocitypowered.api.util.GameProfile;
+import com.velocitypowered.api.util.GameProfile.Property;

 import java.net.InetSocketAddress;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
@@ -121,7 +123,7 @@ public class ConnectListener {
         }
     }

-    private List<GameProfile.Property> removeSkin(List<GameProfile.Property> oldProperties) {
+    private List<GameProfile.Property> removeSkin(Collection<Property> oldProperties) {
         List<GameProfile.Property> newProperties = new ArrayList<>(oldProperties.size() - 1);
         for (GameProfile.Property property : oldProperties) {
             if (!"textures".equals(property.getName()))
diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/AsyncToggleMessage.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/AsyncToggleMessage.java
index 12444fc..8dc7d1c 100644
--- a/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/AsyncToggleMessage.java
+++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/task/AsyncToggleMessage.java
@@ -32,6 +32,7 @@ import com.github.games647.fastlogin.velocity.FastLoginVelocity;
 import com.github.games647.fastlogin.velocity.event.VelocityFastLoginPremiumToggleEvent;
 import com.velocitypowered.api.command.CommandSource;
 import com.velocitypowered.api.proxy.Player;
+
 import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;

 public class AsyncToggleMessage implements Runnable {
2022-06-28 18:34:23 +02:00
b041a89209 Typo fixes 2022-06-28 18:34:23 +02:00
a5942cba74 Use HTML links 2022-06-28 18:34:23 +02:00
f44d7a6780 Forward client key to server
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index a3bb3d0..55a8f33 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -119,7 +119,7 @@ public class ProtocolLibListener extends PacketAdapter {
                 case Continue:
                 default:
                     //player.getName() won't work at this state
-                    onLogin(packetEvent, sender, username);
+                    onLoginStart(packetEvent, sender, username);
                     break;
             }
         } else {
@@ -146,7 +146,6 @@ public class ProtocolLibListener extends PacketAdapter {
             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)) {
@@ -162,7 +161,7 @@ public class ProtocolLibListener extends PacketAdapter {
         }
     }

-    private void onLogin(PacketEvent packetEvent, Player player, String username) {
+    private void onLoginStart(PacketEvent packetEvent, Player player, String username) {
         //this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
         String sessionKey = player.getAddress().toString();

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 d13a5c9..ed84298 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
@@ -43,6 +43,7 @@ import com.github.games647.craftapi.model.skin.SkinProperty;
 import com.github.games647.craftapi.resolver.MojangResolver;
 import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
 import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
+import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;

 import java.io.IOException;
 import java.lang.reflect.Method;
@@ -168,7 +169,7 @@ public class VerifyResponseTask implements Runnable {
         session.setVerified(true);

         setPremiumUUID(session.getUuid());
-        receiveFakeStartPacket(realUsername);
+        receiveFakeStartPacket(realUsername, session.getClientPublicKey());
     }

     private void setPremiumUUID(UUID premiumUUID) {
@@ -253,7 +254,7 @@ public class VerifyResponseTask implements Runnable {
     }

     //fake a new login packet in order to let the server handle all the other stuff
-    private void receiveFakeStartPacket(String username) {
+    private void receiveFakeStartPacket(String username, ClientPublicKey clientKey) {
         //see StartPacketListener for packet information
         PacketContainer startPacket = new PacketContainer(START);

@@ -261,7 +262,8 @@ public class VerifyResponseTask implements Runnable {
             startPacket.getStrings().write(0, username);

             EquivalentConverter<WrappedProfileKeyData> converter = BukkitConverters.getWrappedPublicKeyDataConverter();
-            startPacket.getOptionals(converter).write(0, Optional.empty());
+            var key = new WrappedProfileKeyData(clientKey.getExpiry(), clientKey.getKey(), sharedSecret);
+            startPacket.getOptionals(converter).write(0, Optional.of(key));
         } else {
             //uuid is ignored by the packet definition
             WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
2022-06-28 18:34:23 +02:00
0f17fe18f9 Document origin of signing keys 2022-06-28 18:34:22 +02:00
dac5cd7639 Verify signed nonce
diff --git a/bukkit/pom.xml b/bukkit/pom.xml
index 9b3c5c5..c1f63ba 100644
--- a/bukkit/pom.xml
+++ b/bukkit/pom.xml
@@ -179,6 +179,19 @@
             <version>1.0.7</version>
         </dependency>

+        <dependency>
+            <groupId>com.mojang</groupId>
+            <artifactId>datafixerupper</artifactId>
+            <version>5.0.28</version>
+            <scope>provided</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>*</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
         <!--Library for listening and sending Minecraft packets-->
         <dependency>
             <groupId>com.comphenix.protocol</groupId>
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 556def1..4f4af4d 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 956a60f..713fa68 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 f0a4083..485c065 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<Player, CommandSender, ProtocolLibLoginSource>
-        implements Runnable {
+    implements Runnable {

     private final FastLoginBukkit plugin;
     private final PacketEvent packetEvent;
@@ -67,7 +71,9 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
     @Override
     public void run() {
         try {
-            super.onLogin(username, new ProtocolLibLoginSource(player, random, publicKey));
+            Optional<WrappedProfileKeyData> 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<Player, CommandSender, Protoco
     //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
     @Override
     public void requestPremiumLogin(ProtocolLibLoginSource source, StoredProfile profile
-            , String username, boolean registered) {
+        , String username, boolean registered) {
         try {
             source.enableOnlinemode();
         } catch (Exception ex) {
@@ -97,8 +103,10 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
         core.getPendingLogin().put(ip + username, new Object());

         byte[] verify = source.getVerifyToken();
+        WrappedProfileKeyData key = source.getClientPublicKey();
+        ClientPublicKey clientKey = new ClientPublicKey(key.getExpireTime(), key.getKey(), key.getSignature());

-        BukkitLoginSession playerSession = new BukkitLoginSession(username, verify, registered, profile);
+        BukkitLoginSession playerSession = new BukkitLoginSession(username, verify, clientKey, registered, profile);
         plugin.putSession(player.getAddress(), playerSession);
         //cancel only if the player has a paid account otherwise login as normal offline player
         synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index 27c0335..a3bb3d0 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -30,6 +30,7 @@ import com.comphenix.protocol.ProtocolLibrary;
 import com.comphenix.protocol.events.PacketAdapter;
 import com.comphenix.protocol.events.PacketContainer;
 import com.comphenix.protocol.events.PacketEvent;
+import com.comphenix.protocol.reflect.FuzzyReflection;
 import com.comphenix.protocol.wrappers.WrappedGameProfile;
 import com.comphenix.protocol.wrappers.WrappedProfilePublicKey;
 import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
@@ -38,6 +39,7 @@ import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
 import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
 import com.github.games647.fastlogin.core.antibot.AntiBotService;
 import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
+import com.mojang.datafixers.util.Either;

 import java.net.InetSocketAddress;
 import java.security.InvalidKeyException;
@@ -139,9 +141,24 @@ public class ProtocolLibListener extends PacketAdapter {
             plugin.getLog().warn("GameProfile {} tried to send encryption response at invalid state", sender.getAddress());
             sender.kickPlayer(plugin.getCore().getMessage("invalid-request"));
         } else {
-            packetEvent.getAsyncMarker().incrementProcessingDelay();
-            Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, session, sharedSecret, keyPair);
-            plugin.getScheduler().runAsync(verifyTask);
+            Either<byte[], ?> 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, 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 dd191f1..d87b602 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 e974b09..d13a5c9 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<WrappedProfileKeyData> 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 0000000..c2a2d1a
--- /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<byte[]> {
+
+    @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 904405a..63a24ba 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 0000000..8ea85f6
--- /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/resources/client_keys/README.md b/bukkit/src/test/resources/client_keys/README.md
new file mode 100644
index 0000000..1165e23
--- /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 7ecc12e..ea509fe 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 37bb3ad..98ea33f 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 cbca4b1..2b80c2c 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 a2d6a41..0000000
--- 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 0000000..8943e87
--- /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 0000000..cc603bc
--- /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 0000000..b88c0f5
--- /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 0000000..8edffb6
--- /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 0000000..ba6bac5
--- /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 0000000..7f4f4ad
--- /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
+    }
+}
2022-06-28 18:34:22 +02:00
bc7c4eea60 Use PublicKey instead of byte representation 2022-06-28 18:34:22 +02:00
456342c686 Migrate to ProtocolLib field extractors for better compatibility
diff --git a/bukkit/pom.xml b/bukkit/pom.xml
index c4caae8..9b3c5c5 100644
--- a/bukkit/pom.xml
+++ b/bukkit/pom.xml
@@ -183,7 +183,7 @@
         <dependency>
             <groupId>com.comphenix.protocol</groupId>
             <artifactId>ProtocolLib</artifactId>
-            <version>5.0.0-20220603.031413-2</version>
+            <version>5.0.0-SNAPSHOT</version>
             <scope>provided</scope>
             <exclusions>
                 <exclusion>
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index c9e72fc..3c66e7b 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -27,12 +27,12 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib;

 import com.comphenix.protocol.PacketType;
 import com.comphenix.protocol.ProtocolLibrary;
-import com.comphenix.protocol.events.InternalStructure;
 import com.comphenix.protocol.events.PacketAdapter;
 import com.comphenix.protocol.events.PacketContainer;
 import com.comphenix.protocol.events.PacketEvent;
-import com.comphenix.protocol.reflect.FuzzyReflection;
 import com.comphenix.protocol.wrappers.WrappedGameProfile;
+import com.comphenix.protocol.wrappers.WrappedProfilePublicKey;
+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.listener.protocollib.packet.ClientPublicKey;
@@ -47,7 +47,6 @@ import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.SignatureException;
 import java.time.Instant;
-import java.util.Optional;

 import org.bukkit.entity.Player;

@@ -171,15 +170,15 @@ public class ProtocolLibListener extends PacketAdapter {
     }

     private boolean verifyPublicKey(PacketContainer packet) {
-        Optional<InternalStructure> internalStructure = packet.getOptionalStructures().readSafely(0);
-        if (internalStructure == null) {
+        WrappedProfileKeyData profileKey = packet.getProfilePublicKeys().optionRead(0)
+            .map(WrappedProfilePublicKey::getKeyData).orElse(null);
+        if (profileKey == null) {
             return true;
         }

-        Object instance = internalStructure.get().getHandle();
-        Instant expires = FuzzyReflection.getFieldValue(instance, Instant.class, true);
-        PublicKey key = FuzzyReflection.getFieldValue(instance, PublicKey.class, true);
-        byte[] signature = FuzzyReflection.getFieldValue(instance, byte[].class, true);
+        Instant expires = profileKey.getExpireTime();
+        PublicKey key = profileKey.getKey();
+        byte[] signature = profileKey.getSignature();
         ClientPublicKey clientKey = new ClientPublicKey(expires, key.getEncoded(), signature);
         try {
             return EncryptionUtil.verifyClientKey(clientKey, Instant.now());
2022-06-28 18:34:22 +02:00
e85d0d3f0e Add session key to plugin 2022-06-28 18:34:22 +02:00
d6c6108a7e Do not modify certificate file during packaging phase 2022-06-28 18:34:22 +02:00
d353ac66ab Verify public keys from players
diff --git a/bukkit/pom.xml b/bukkit/pom.xml
index 2e1ccc4..c4caae8 100644
--- a/bukkit/pom.xml
+++ b/bukkit/pom.xml
@@ -119,9 +119,6 @@
         <repository>
             <id>dmulloy2-repo</id>
             <url>https://repo.dmulloy2.net/repository/public/</url>
-            <snapshots>
-                <enabled>false</enabled>
-            </snapshots>
         </repository>

         <!-- AuthMe Reloaded, xAuth and LoginSecurity -->
@@ -186,7 +183,7 @@
         <dependency>
             <groupId>com.comphenix.protocol</groupId>
             <artifactId>ProtocolLib</artifactId>
-            <version>5.0.0-SNAPSHOT</version>
+            <version>5.0.0-20220603.031413-2</version>
             <scope>provided</scope>
             <exclusions>
                 <exclusion>
@@ -351,5 +348,12 @@
             <scope>system</scope>
             <systemPath>${project.basedir}/lib/UltraAuth v2.1.2.jar</systemPath>
         </dependency>
+
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk18on</artifactId>
+            <version>1.71</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
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 1197514..143dda9 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
@@ -25,15 +25,28 @@
  */
 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 java.io.IOException;
 import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.time.Instant;
+import java.util.Base64;
+import java.util.Base64.Encoder;
 import java.util.Random;

 import javax.crypto.Cipher;
@@ -46,11 +59,25 @@ import javax.crypto.spec.SecretKeySpec;
  *
  * @see net.minecraft.server.MinecraftEncryption
  */
-public class EncryptionUtil {
+class EncryptionUtil {

     public static final int VERIFY_TOKEN_LENGTH = 4;
     public static final String KEY_PAIR_ALGORITHM = "RSA";

+    private static final int RSA_LENGTH = 1_024;
+
+    private static final PublicKey mojangSessionKey;
+    private static final int LINE_LENGTH = 76;
+    private static final Encoder KEY_ENCODER = Base64.getMimeEncoder(LINE_LENGTH, "\n".getBytes(StandardCharsets.UTF_8));
+
+    static {
+        try {
+            mojangSessionKey = loadMojangSessionKey();
+        } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException ex) {
+            throw new RuntimeException("Failed to load Mojang session key", ex);
+        }
+    }
+
     private EncryptionUtil() {
         // utility
     }
@@ -65,7 +92,7 @@ public class EncryptionUtil {
         try {
             KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALGORITHM);

-            keyPairGenerator.initialize(1_024);
+            keyPairGenerator.initialize(RSA_LENGTH);
             return keyPairGenerator.generateKeyPair();
         } catch (NoSuchAlgorithmException nosuchalgorithmexception) {
             // Should be existing in every vm
@@ -120,6 +147,33 @@ public class EncryptionUtil {
         return new SecretKeySpec(decrypt(privateKey, sharedKey), "AES");
     }

+    public static boolean verifyClientKey(ClientPublicKey clientKey, Instant verifyTimstamp)
+        throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
+        if (!verifyTimstamp.isBefore(clientKey.getExpiry())) {
+            return false;
+        }
+
+        Signature signature = Signature.getInstance("SHA1withRSA");
+        signature.initVerify(mojangSessionKey);
+        signature.update(toSignable(clientKey).getBytes(StandardCharsets.US_ASCII));
+        return signature.verify(clientKey.getSignature());
+    }
+
+    private static PublicKey loadMojangSessionKey()
+        throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
+        var keyUrl = Resources.getResource("yggdrasil_session_pubkey.der");
+        var keyData = Resources.toByteArray(keyUrl);
+        var keySpec = new X509EncodedKeySpec(keyData);
+
+        return KeyFactory.getInstance("RSA").generatePublic(keySpec);
+    }
+
+    private static String toSignable(ClientPublicKey clientPublicKey) {
+        long expiry = clientPublicKey.getExpiry().toEpochMilli();
+        String encoded = KEY_ENCODER.encodeToString(clientPublicKey.getKey());
+        return expiry + "-----BEGIN RSA PUBLIC KEY-----\n" + encoded + "\n-----END RSA PUBLIC KEY-----\n";
+    }
+
     public static byte[] decrypt(PrivateKey key, byte[] data) throws GeneralSecurityException {
         // b(Key var0, byte[] var1)
         Cipher cipher = Cipher.getInstance(key.getAlgorithm());
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index f8b8de2..c9e72fc 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -27,18 +27,27 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib;

 import com.comphenix.protocol.PacketType;
 import com.comphenix.protocol.ProtocolLibrary;
+import com.comphenix.protocol.events.InternalStructure;
 import com.comphenix.protocol.events.PacketAdapter;
 import com.comphenix.protocol.events.PacketContainer;
 import com.comphenix.protocol.events.PacketEvent;
-import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
+import com.comphenix.protocol.reflect.FuzzyReflection;
 import com.comphenix.protocol.wrappers.WrappedGameProfile;
+import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
 import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
+import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
 import com.github.games647.fastlogin.core.antibot.AntiBotService;
 import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;

 import java.net.InetSocketAddress;
+import java.security.InvalidKeyException;
 import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
 import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.time.Instant;
+import java.util.Optional;

 import org.bukkit.entity.Player;

@@ -149,6 +158,11 @@ public class ProtocolLibListener extends PacketAdapter {
             username = (String) packetEvent.getPacket().getMeta("original_name").get();
         }

+        if (!verifyPublicKey(packet)) {
+            plugin.getLog().warn("Invalid public key from player {}", username);
+            return;
+        }
+
         plugin.getLog().trace("GameProfile {} with {} connecting", sessionKey, username);

         packetEvent.getAsyncMarker().incrementProcessingDelay();
@@ -156,6 +170,24 @@ public class ProtocolLibListener extends PacketAdapter {
         plugin.getScheduler().runAsync(nameCheckTask);
     }

+    private boolean verifyPublicKey(PacketContainer packet) {
+        Optional<InternalStructure> internalStructure = packet.getOptionalStructures().readSafely(0);
+        if (internalStructure == null) {
+            return true;
+        }
+
+        Object instance = internalStructure.get().getHandle();
+        Instant expires = FuzzyReflection.getFieldValue(instance, Instant.class, true);
+        PublicKey key = FuzzyReflection.getFieldValue(instance, PublicKey.class, true);
+        byte[] signature = FuzzyReflection.getFieldValue(instance, byte[].class, true);
+        ClientPublicKey clientKey = new ClientPublicKey(expires, key.getEncoded(), signature);
+        try {
+            return EncryptionUtil.verifyClientKey(clientKey, Instant.now());
+        } catch (SignatureException | InvalidKeyException | NoSuchAlgorithmException ex) {
+            return false;
+        }
+    }
+
     private String getUsername(PacketContainer packet) {
         WrappedGameProfile profile = packet.getGameProfiles().readSafely(0);
         if (profile == null) {
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/packet/ClientPublicKey.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/packet/ClientPublicKey.java
new file mode 100644
index 0000000..495adb1
--- /dev/null
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/packet/ClientPublicKey.java
@@ -0,0 +1,53 @@
+/*
+ * 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.packet;
+
+import java.time.Instant;
+
+public class ClientPublicKey {
+
+    private final Instant expiry;
+    private final byte[] key;
+    private final byte[] signature;
+
+    public ClientPublicKey(Instant expiry, byte[] key, byte[] signature) {
+        this.expiry = expiry;
+        this.key = key;
+        this.signature = signature;
+    }
+
+    public Instant getExpiry() {
+        return expiry;
+    }
+
+    public byte[] getKey() {
+        return key;
+    }
+
+    public byte[] getSignature() {
+        return signature;
+    }
+}
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 7a097f1..11a52f7 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,8 +25,27 @@
  */
 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.gson.Gson;
+import com.google.gson.JsonObject;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Base64;

+import org.bouncycastle.util.io.pem.PemObject;
+import org.bouncycastle.util.io.pem.PemReader;
 import org.junit.Test;

 import static org.hamcrest.CoreMatchers.is;
@@ -43,4 +62,77 @@ public class EncryptionUtilTest {
         assertThat(token, notNullValue());
         assertThat(token.length, is(4));
     }
+
+    @Test
+    public void testExpiredClientKey() throws Exception {
+        var clientKey = loadClientKey("client_keys/valid.json");
+
+        // Client expires at the exact second mentioned, so use it for verification
+        var expiredTimestamp = clientKey.getExpiry();
+        assertThat(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp), is(false));
+    }
+
+    // @Test(expected = Exception.class)
+    @Test
+    public void testInvalidChangedExpiration() throws Exception {
+        // 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));
+    }
+
+    // @Test(expected = Exception.class)
+    @Test
+    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));
+    }
+
+    @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));
+    }
+
+    @Test
+    public void testValidClientKey() throws Exception {
+        var clientKey = loadClientKey("client_keys/valid.json");
+
+        var verificationTimestamp = clientKey.getExpiry().minus(5, ChronoUnit.HOURS);
+        assertThat(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp), is(true));
+    }
+
+    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);
+
+        Instant expires = Instant.parse(object.getAsJsonPrimitive("expires_at").getAsString());
+        String key = object.getAsJsonPrimitive("key").getAsString();
+        RSAPublicKey publicKey = parsePublicKey(key);
+
+        byte[] signature = Base64.getDecoder().decode(object.getAsJsonPrimitive("signature").getAsString());
+        return new ClientPublicKey(expires, publicKey.getEncoded(), signature);
+    }
+
+    private RSAPublicKey parsePublicKey(String lines)
+        throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
+        try (
+            Reader reader = new StringReader(lines);
+            PemReader pemReader = new PemReader(reader)
+        ) {
+
+            PemObject pemObject = pemReader.readPemObject();
+            byte[] content = pemObject.getContent();
+            var pubKeySpec = new X509EncodedKeySpec(content);
+
+            var factory = KeyFactory.getInstance("RSA");
+            return (RSAPublicKey) factory.generatePublic(pubKeySpec);
+        }
+    }
 }
diff --git a/bukkit/src/test/resources/client_keys/invalid_wrong_expiration.json b/bukkit/src/test/resources/client_keys/invalid_wrong_expiration.json
new file mode 100644
index 0000000..7ecc12e
--- /dev/null
+++ b/bukkit/src/test/resources/client_keys/invalid_wrong_expiration.json
@@ -0,0 +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="
+}
diff --git a/bukkit/src/test/resources/client_keys/invalid_wrong_key.json b/bukkit/src/test/resources/client_keys/invalid_wrong_key.json
new file mode 100644
index 0000000..37bb3ad
--- /dev/null
+++ b/bukkit/src/test/resources/client_keys/invalid_wrong_key.json
@@ -0,0 +1,5 @@
+{
+    "expires_at": "2022-06-12T10:46:26.421156927Z",
+    "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="
+}
diff --git a/bukkit/src/test/resources/client_keys/invalid_wrong_signature.json b/bukkit/src/test/resources/client_keys/invalid_wrong_signature.json
new file mode 100644
index 0000000..cbca4b1
--- /dev/null
+++ b/bukkit/src/test/resources/client_keys/invalid_wrong_signature.json
@@ -0,0 +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="
+}
diff --git a/bukkit/src/test/resources/client_keys/valid.json b/bukkit/src/test/resources/client_keys/valid.json
new file mode 100644
index 0000000..a2d6a41
--- /dev/null
+++ b/bukkit/src/test/resources/client_keys/valid.json
@@ -0,0 +1,5 @@
+{
+    "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="
+}
2022-06-28 18:34:21 +02:00
4fa28f2c69 Support username extraction in 1.19
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index d1f83a6..f8b8de2 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -31,6 +31,7 @@ import com.comphenix.protocol.events.PacketAdapter;
 import com.comphenix.protocol.events.PacketContainer;
 import com.comphenix.protocol.events.PacketEvent;
 import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
+import com.comphenix.protocol.wrappers.WrappedGameProfile;
 import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
 import com.github.games647.fastlogin.core.antibot.AntiBotService;
 import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
@@ -94,7 +95,7 @@ public class ProtocolLibListener extends PacketAdapter {
             PacketContainer packet = packetEvent.getPacket();

             InetSocketAddress address = sender.getAddress();
-            String username = packet.getGameProfiles().read(0).getName();
+            String username = getUsername(packet);

             Action action = antiBotService.onIncomingConnection(address, username);
             switch (action) {
@@ -154,4 +155,14 @@ public class ProtocolLibListener extends PacketAdapter {
         Runnable nameCheckTask = new NameCheckTask(plugin, random, player, packetEvent, username, keyPair.getPublic());
         plugin.getScheduler().runAsync(nameCheckTask);
     }
+
+    private String getUsername(PacketContainer packet) {
+        WrappedGameProfile profile = packet.getGameProfiles().readSafely(0);
+        if (profile == null) {
+            return packet.getStrings().read(0);
+        }
+
+        //player.getName() won't work at this state
+        return profile.getName();
+    }
 }
2022-06-28 18:34:21 +02:00
d8a6e2a6fa Update ProtocolLib to 5.0
diff --git a/bukkit/pom.xml b/bukkit/pom.xml
index 13e466e..2494a2b 100644
--- a/bukkit/pom.xml
+++ b/bukkit/pom.xml
@@ -186,7 +186,7 @@
         <dependency>
             <groupId>com.comphenix.protocol</groupId>
             <artifactId>ProtocolLib</artifactId>
-            <version>4.8.0</version>
+            <version>5.0.0-SNAPSHOT</version>
             <scope>provided</scope>
             <exclusions>
                 <exclusion>
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 5a2794d..2e60cd1 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
@@ -28,7 +28,7 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib;
 import com.comphenix.protocol.ProtocolLibrary;
 import com.comphenix.protocol.events.PacketContainer;
 import com.comphenix.protocol.events.PacketEvent;
-import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
+import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
 import com.comphenix.protocol.reflect.FieldUtils;
 import com.comphenix.protocol.reflect.FuzzyReflection;
 import com.comphenix.protocol.utility.MinecraftReflection;
@@ -37,18 +37,14 @@ import com.comphenix.protocol.wrappers.WrappedChatComponent;
 import com.comphenix.protocol.wrappers.WrappedGameProfile;
 import com.github.games647.craftapi.model.auth.Verification;
 import com.github.games647.craftapi.model.skin.SkinProperty;
-import com.github.games647.craftapi.resolver.AbstractResolver;
 import com.github.games647.craftapi.resolver.MojangResolver;
 import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
 import com.github.games647.fastlogin.bukkit.FastLoginBukkit;

 import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.net.*;
-import java.nio.charset.StandardCharsets;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
 import java.security.GeneralSecurityException;
 import java.security.Key;
 import java.security.KeyPair;
@@ -263,15 +259,11 @@ public class VerifyResponseTask implements Runnable {
     private void kickPlayer(String reason) {
         PacketContainer kickPacket = new PacketContainer(DISCONNECT);
         kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(reason));
-        try {
-            //send kick packet at login state
-            //the normal event.getPlayer.kickPlayer(String) method does only work at play state
-            ProtocolLibrary.getProtocolManager().sendServerPacket(player, kickPacket);
-            //tell the server that we want to close the connection
-            player.kickPlayer("Disconnect");
-        } catch (InvocationTargetException ex) {
-            plugin.getLog().error("Error sending kick packet for: {}", player, ex);
-        }
+        //send kick packet at login state
+        //the normal event.getPlayer.kickPlayer(String) method does only work at play state
+        ProtocolLibrary.getProtocolManager().sendServerPacket(player, kickPacket);
+        //tell the server that we want to close the connection
+        player.kickPlayer("Disconnect");
     }

     //fake a new login packet in order to let the server handle all the other stuff
@@ -287,14 +279,8 @@ public class VerifyResponseTask implements Runnable {
             startPacket.getGameProfiles().write(0, fakeProfile);
         }

-        try {
-            //we don't want to handle our own packets so ignore filters
-            startPacket.setMeta(ProtocolLibListener.SOURCE_META_KEY, plugin.getName());
-            ProtocolLibrary.getProtocolManager().recieveClientPacket(player, startPacket, true);
-        } catch (InvocationTargetException | IllegalAccessException ex) {
-            plugin.getLog().warn("Failed to fake a new start packet for: {}", username, ex);
-            //cancel the event in order to prevent the server receiving an invalid packet
-            kickPlayer(plugin.getCore().getMessage("error-kick"));
-        }
+        //we don't want to handle our own packets so ignore filters
+        startPacket.setMeta(ProtocolLibListener.SOURCE_META_KEY, plugin.getName());
+        ProtocolLibrary.getProtocolManager().receiveClientPacket(player, startPacket, true);
     }
 }
2022-06-28 18:34:21 +02:00
219ebb1248 Bump version 2022-06-28 18:34:21 +02:00
87281be8cf Fix start packet containing username in 1.19 2022-06-28 18:34:21 +02:00
e781eb6c68 Revert "Add support for checking velocity support with newer paper configs"
This reverts commit 6f9e8a49f1.

Fixes #825
2022-06-28 18:12:01 +02:00
9ba6e9ba90 Add missing license header 2022-06-24 16:28:18 +02:00
f182adc3bc Add support for RGB colors using the color char
Examples include:
`&x00002a00002b&lText`
2022-06-24 16:21:53 +02:00
5e159cac64 Explicitly disable ProtocolLib listener on stop
This will stop Paper complaining about still running asynchronous tasks
which is a useful feature.
2022-06-19 12:10:31 +02:00
6f9e8a49f1 Add support for checking velocity support with newer paper configs
Fixes #802
2022-06-18 21:23:17 +02:00
57b7fe949f Merge pull request #823 from games647/805-more-antibot-features
Add support for blocking incoming connection on AntiBot detection
2022-06-18 18:15:52 +02:00
32fe2cdf4e Check for valid session before starting a new thread
This could reduce load for malicious senders as there have to be session
active to start the expensive operation of creating a new thread.
2022-06-18 18:09:35 +02:00
7cab5993b7 Add license
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java
index ab4ceed..9c2fa3a 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java
@@ -114,9 +114,9 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
             }

             if (pluginManager.isPluginEnabled("ProtocolSupport")) {
-                pluginManager.registerEvents(new ProtocolSupportListener(this, core.getRateLimiter()), this);
+                pluginManager.registerEvents(new ProtocolSupportListener(this, core.getAntiBot()), this);
             } else if (pluginManager.isPluginEnabled("ProtocolLib")) {
-                ProtocolLibListener.register(this, core.getRateLimiter());
+                ProtocolLibListener.register(this, core.getAntiBot());

                 if (isPluginInstalled("floodgate")) {
                     if (getConfig().getBoolean("floodgatePrefixWorkaround")){
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index 8b3d601..3c161b7 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -31,8 +31,10 @@ import com.comphenix.protocol.events.PacketAdapter;
 import com.comphenix.protocol.events.PacketContainer;
 import com.comphenix.protocol.events.PacketEvent;
 import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
-import com.github.games647.fastlogin.core.RateLimiter;
+import com.github.games647.fastlogin.core.antibot.AntiBotService;
+import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;

+import java.net.InetSocketAddress;
 import java.security.KeyPair;
 import java.security.SecureRandom;

@@ -50,9 +52,9 @@ public class ProtocolLibListener extends PacketAdapter {
     //just create a new once on plugin enable. This used for verify token generation
     private final SecureRandom random = new SecureRandom();
     private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
-    private final RateLimiter rateLimiter;
+    private final AntiBotService antiBotService;

-    public ProtocolLibListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
+    public ProtocolLibListener(FastLoginBukkit plugin, AntiBotService antiBotService) {
         //run async in order to not block the server, because we are making api calls to Mojang
         super(params()
                 .plugin(plugin)
@@ -60,15 +62,15 @@ public class ProtocolLibListener extends PacketAdapter {
                 .optionAsync());

         this.plugin = plugin;
-        this.rateLimiter = rateLimiter;
+        this.antiBotService = antiBotService;
     }

-    public static void register(FastLoginBukkit plugin, RateLimiter rateLimiter) {
+    public static void register(FastLoginBukkit plugin, AntiBotService antiBotService) {
         // they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
         // TODO: make synchronous processing, but do web or database requests async
         ProtocolLibrary.getProtocolManager()
                 .getAsynchronousManager()
-                .registerAsyncHandler(new ProtocolLibListener(plugin, rateLimiter))
+                .registerAsyncHandler(new ProtocolLibListener(plugin, antiBotService))
                 .start();
     }

@@ -88,12 +90,26 @@ public class ProtocolLibListener extends PacketAdapter {
         Player sender = packetEvent.getPlayer();
         PacketType packetType = packetEvent.getPacketType();
         if (packetType == START) {
-            if (!rateLimiter.tryAcquire()) {
-                plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", sender);
-                return;
+            PacketContainer packet = packetEvent.getPacket();
+
+            InetSocketAddress address = sender.getAddress();
+            String username = packet.getGameProfiles().read(0).getName();
+
+            Action action = antiBotService.onIncomingConnection(address, username);
+            switch (action) {
+                case Ignore:
+                    // just ignore
+                    return;
+                case Block:
+                    String message = plugin.getCore().getMessage("kick-antibot");
+                    sender.kickPlayer(message);
+                    break;
+                case Continue:
+                default:
+                    //player.getName() won't work at this state
+                    onLogin(packetEvent, sender, username);
+                    break;
             }
-
-            onLogin(packetEvent, sender);
         } else {
             onEncryptionBegin(packetEvent, sender);
         }
@@ -113,18 +129,13 @@ public class ProtocolLibListener extends PacketAdapter {
         plugin.getScheduler().runAsync(verifyTask);
     }

-    private void onLogin(PacketEvent packetEvent, Player player) {
+    private void onLogin(PacketEvent packetEvent, Player player, String username) {
         //this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
         String sessionKey = player.getAddress().toString();

         //remove old data every time on a new login in order to keep the session only for one person
         plugin.removeSession(player.getAddress());

-        //player.getName() won't work at this state
-        PacketContainer packet = packetEvent.getPacket();
-
-        String username = packet.getGameProfiles().read(0).getName();
-
         if (packetEvent.getPacket().getMeta("original_name").isPresent()) {
             //username has been injected by ManualNameChange.java
             username = (String) packetEvent.getPacket().getMeta("original_name").get();
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java
index 608078c..ce84824 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java
@@ -29,8 +29,9 @@ import com.github.games647.craftapi.UUIDAdapter;
 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.core.RateLimiter;
 import com.github.games647.fastlogin.core.StoredProfile;
+import com.github.games647.fastlogin.core.antibot.AntiBotService;
+import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
 import com.github.games647.fastlogin.core.shared.JoinManagement;
 import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;

@@ -49,13 +50,13 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
         implements Listener {

     private final FastLoginBukkit plugin;
-    private final RateLimiter rateLimiter;
+    private final AntiBotService antiBotService;

-    public ProtocolSupportListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
+    public ProtocolSupportListener(FastLoginBukkit plugin, AntiBotService antiBotService) {
         super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());

         this.plugin = plugin;
-        this.rateLimiter = rateLimiter;
+        this.antiBotService = antiBotService;
     }

     @EventHandler
@@ -64,19 +65,28 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
             return;
         }

-        if (!rateLimiter.tryAcquire()) {
-            plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", loginStartEvent.getConnection());
-            return;
-        }
-
         String username = loginStartEvent.getConnection().getProfile().getName();
         InetSocketAddress address = loginStartEvent.getAddress();
-
-        //remove old data every time on a new login in order to keep the session only for one person
-        plugin.removeSession(address);
-
-        ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent);
-        super.onLogin(username, source);
+        plugin.getLog().info("Incoming login request for {} from {}", username, address);
+
+        Action action = antiBotService.onIncomingConnection(address, username);
+        switch (action) {
+            case Ignore:
+                // just ignore
+                return;
+            case Block:
+                String message = plugin.getCore().getMessage("kick-antibot");
+                loginStartEvent.denyLogin(message);
+                break;
+            case Continue:
+            default:
+                //remove old data every time on a new login in order to keep the session only for one person
+                plugin.removeSession(address);
+
+                ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent);
+                super.onLogin(username, source);
+                break;
+        }
     }

     @EventHandler
diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java
index a8c894d..ddd7e97 100644
--- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java
+++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java
@@ -32,7 +32,8 @@ import com.github.games647.fastlogin.bungee.task.AsyncPremiumCheck;
 import com.github.games647.fastlogin.bungee.task.FloodgateAuthTask;
 import com.github.games647.fastlogin.bungee.task.ForceLoginTask;
 import com.github.games647.fastlogin.core.StoredProfile;
-import com.github.games647.fastlogin.core.antibot.RateLimiter;
+import com.github.games647.fastlogin.core.antibot.AntiBotService;
+import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
 import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
 import com.github.games647.fastlogin.core.shared.LoginSession;
 import com.google.common.base.Throwables;
@@ -41,8 +42,10 @@ import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.reflect.Field;
+import java.net.InetSocketAddress;
 import java.util.UUID;

+import net.md_5.bungee.api.chat.TextComponent;
 import net.md_5.bungee.api.connection.PendingConnection;
 import net.md_5.bungee.api.connection.ProxiedPlayer;
 import net.md_5.bungee.api.connection.Server;
@@ -92,12 +95,12 @@ public class ConnectListener implements Listener {
     }

     private final FastLoginBungee plugin;
-    private final RateLimiter rateLimiter;
+    private final AntiBotService antiBotService;
     private final Property[] emptyProperties = {};

-    public ConnectListener(FastLoginBungee plugin, RateLimiter rateLimiter) {
+    public ConnectListener(FastLoginBungee plugin, AntiBotService antiBotService) {
         this.plugin = plugin;
-        this.rateLimiter = rateLimiter;
+        this.antiBotService = antiBotService;
     }

     @EventHandler
@@ -107,17 +110,28 @@ public class ConnectListener implements Listener {
             return;
         }

-        if (!rateLimiter.tryAcquire()) {
-            plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection);
-            return;
-        }
-
+        InetSocketAddress address = preLoginEvent.getConnection().getAddress();
         String username = connection.getName();
+
         plugin.getLog().info("Incoming login request for {} from {}", username, connection.getSocketAddress());

-        preLoginEvent.registerIntent(plugin);
-        Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
-        plugin.getScheduler().runAsync(asyncPremiumCheck);
+        Action action = antiBotService.onIncomingConnection(address, username);
+        switch (action) {
+            case Ignore:
+                // just ignore
+                return;
+            case Block:
+                String message = plugin.getCore().getMessage("kick-antibot");
+                preLoginEvent.setCancelReason(TextComponent.fromLegacyText(message));
+                preLoginEvent.setCancelled(true);
+                break;
+            case Continue:
+            default:
+                preLoginEvent.registerIntent(plugin);
+                Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
+                plugin.getScheduler().runAsync(asyncPremiumCheck);
+                break;
+        }
     }

     @EventHandler(priority = EventPriority.LOWEST)
@@ -140,7 +154,7 @@ public class ConnectListener implements Listener {
             StoredProfile playerProfile = session.getProfile();
             playerProfile.setId(verifiedUUID);

-            // bungeecord will do this automatically so override it on disabled option
+            // BungeeCord will do this automatically so override it on disabled option
             if (uniqueIdSetter != null) {
                 InitialHandler initialHandler = (InitialHandler) connection;

diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/AntiBotService.java
similarity index 57%
rename from core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java
rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/AntiBotService.java
index 4321d6f..2848335 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/antibot/AntiBotService.java
@@ -23,3 +23,40 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
+package com.github.games647.fastlogin.core.antibot;
+
+import java.net.InetSocketAddress;
+
+import org.slf4j.Logger;
+
+public class AntiBotService {
+
+    private final Logger logger;
+
+    private final RateLimiter rateLimiter;
+    private final Action limitReachedAction;
+
+    public AntiBotService(Logger logger, RateLimiter rateLimiter, Action limitReachedAction) {
+        this.logger = logger;
+
+        this.rateLimiter = rateLimiter;
+        this.limitReachedAction = limitReachedAction;
+    }
+
+    public Action onIncomingConnection(InetSocketAddress clientAddress, String username) {
+        if (!rateLimiter.tryAcquire()) {
+            logger.warn("Anti-Bot join limit - Ignoring {}", clientAddress);
+            return limitReachedAction;
+        }
+
+        return Action.Continue;
+    }
+
+    public enum Action {
+        Ignore,
+
+        Block,
+
+        Continue;
+    }
+}
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/ProxyAgnosticMojangResolver.java
similarity index 100%
rename from core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java
rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/ProxyAgnosticMojangResolver.java
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/RateLimiter.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/RateLimiter.java
similarity index 96%
rename from core/src/main/java/com/github/games647/fastlogin/core/RateLimiter.java
rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/RateLimiter.java
index 3ec2f75..b791ca5 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/RateLimiter.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/antibot/RateLimiter.java
@@ -23,7 +23,7 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-package com.github.games647.fastlogin.core;
+package com.github.games647.fastlogin.core.antibot;

 @FunctionalInterface
 public interface RateLimiter {
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/TickingRateLimiter.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/TickingRateLimiter.java
similarity index 98%
rename from core/src/main/java/com/github/games647/fastlogin/core/TickingRateLimiter.java
rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/TickingRateLimiter.java
index 6064229..ced7da1 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/TickingRateLimiter.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/antibot/TickingRateLimiter.java
@@ -23,7 +23,7 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-package com.github.games647.fastlogin.core;
+package com.github.games647.fastlogin.core.antibot;

 import com.google.common.base.Ticker;

diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java b/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java
deleted file mode 100644
index 4321d6f..0000000
--- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.
- */
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java
index 9e2a49a..474a8e2 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java
@@ -28,8 +28,10 @@ package com.github.games647.fastlogin.core.shared;
 import com.github.games647.craftapi.resolver.MojangResolver;
 import com.github.games647.craftapi.resolver.http.RotatingProxySelector;
 import com.github.games647.fastlogin.core.CommonUtil;
-import com.github.games647.fastlogin.core.RateLimiter;
-import com.github.games647.fastlogin.core.TickingRateLimiter;
+import com.github.games647.fastlogin.core.antibot.AntiBotService;
+import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
+import com.github.games647.fastlogin.core.antibot.RateLimiter;
+import com.github.games647.fastlogin.core.antibot.TickingRateLimiter;
 import com.github.games647.fastlogin.core.hooks.AuthPlugin;
 import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator;
 import com.github.games647.fastlogin.core.hooks.PasswordGenerator;
@@ -88,7 +90,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {

     private Configuration config;
     private SQLStorage storage;
-    private RateLimiter rateLimiter;
+    private AntiBotService antiBot;
     private PasswordGenerator<P> passwordGenerator = new DefaultPasswordGenerator<>();
     private AuthPlugin<P> authPlugin;

@@ -122,7 +124,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
         // Initialize the resolver based on the config parameter
         this.resolver = this.config.getBoolean("useProxyAgnosticResolver", false) ? new ProxyAgnosticMojangResolver() : new MojangResolver();

-        rateLimiter = createRateLimiter(config.getSection("anti-bot"));
+        antiBot = createAntiBotService(config.getSection("anti-bot"));
         Set<Proxy> proxies = config.getStringList("proxies")
                 .stream()
                 .map(HostAndPort::fromString)
@@ -144,20 +146,34 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
         resolver.setOutgoingAddresses(addresses);
     }

-    private RateLimiter createRateLimiter(Configuration botSection) {
-        boolean enabled = botSection.getBoolean("enabled", true);
-        if (!enabled) {
+    private AntiBotService createAntiBotService(Configuration botSection) {
+        RateLimiter rateLimiter;
+        if (botSection.getBoolean("enabled", true)) {
+            int maxCon = botSection.getInt("connections", 200);
+            long expireTime = botSection.getLong("expire", 5) * 60 * 1_000L;
+            if (expireTime > MAX_EXPIRE_RATE) {
+                expireTime = MAX_EXPIRE_RATE;
+            }
+
+            rateLimiter = new TickingRateLimiter(Ticker.systemTicker(), maxCon, expireTime);
+        } else {
             // no-op rate limiter
-            return () -> true;
+            rateLimiter = () -> true;
         }

-        int maxCon = botSection.getInt("connections", 200);
-        long expireTime = botSection.getLong("expire", 5) * 60 * 1_000L;
-        if (expireTime > MAX_EXPIRE_RATE) {
-            expireTime = MAX_EXPIRE_RATE;
+        Action action = Action.Ignore;
+        switch (botSection.getString("action", "ignore")) {
+            case "ignore":
+                action = Action.Ignore;
+                break;
+            case "block":
+                action = Action.Block;
+                break;
+            default:
+                plugin.getLog().warn("Invalid anti bot action - defaulting to ignore");
         }

-        return new TickingRateLimiter(Ticker.systemTicker(), maxCon, expireTime);
+        return new AntiBotService(plugin.getLog(), rateLimiter, action);
     }

     private Configuration loadFile(String fileName) throws IOException {
@@ -285,8 +301,8 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
         return authPlugin;
     }

-    public RateLimiter getRateLimiter() {
-        return rateLimiter;
+    public AntiBotService getAntiBot() {
+        return antiBot;
     }

     public void setAuthPluginHook(AuthPlugin<P> authPlugin) {
diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml
index 1b0905d..3a2ab7a 100644
--- a/core/src/main/resources/config.yml
+++ b/core/src/main/resources/config.yml
@@ -20,6 +20,9 @@ anti-bot:
   connections: 600
   # Amount of minutes after the first connection got inserted will expire and made available
   expire: 10
+  # Action - Which action should be performed when the bucket is full (too many connections)
+  # Allowed values are: 'ignore' (FastLogin drops handling the player) or 'block' (block this incoming connection)
+  action: 'ignore'

 # Request a premium login without forcing the player to type a command
 #
diff --git a/core/src/main/resources/messages.yml b/core/src/main/resources/messages.yml
index 8b315ba..33cadaa 100644
--- a/core/src/main/resources/messages.yml
+++ b/core/src/main/resources/messages.yml
@@ -48,6 +48,9 @@ not-premium-other: '&4Player is not in the premium list'
 # Admin wanted to change the premium of a user that isn't known to the plugin
 player-unknown: '&4Player not in the database'

+# Player kicked from anti bot feature
+kick-antibot: '&4Please wait a moment!'
+
 # ========= Bukkit/Spigot ================

 # The user skipped the authentication, because it was a premium player
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 2c04b6e..3f753cd 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,6 +25,8 @@
  */
 package com.github.games647.fastlogin.core;

+import com.github.games647.fastlogin.core.antibot.TickingRateLimiter;
+
 import java.time.Duration;
 import java.util.concurrent.TimeUnit;

diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java
index 2b51bd2..33c8c31 100644
--- a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java
+++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java
@@ -27,7 +27,8 @@ package com.github.games647.fastlogin.velocity.listener;

 import com.github.games647.craftapi.UUIDAdapter;
 import com.github.games647.fastlogin.core.StoredProfile;
-import com.github.games647.fastlogin.core.antibot.RateLimiter;
+import com.github.games647.fastlogin.core.antibot.AntiBotService;
+import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
 import com.github.games647.fastlogin.core.shared.LoginSession;
 import com.github.games647.fastlogin.velocity.FastLoginVelocity;
 import com.github.games647.fastlogin.velocity.VelocityLoginSession;
@@ -37,6 +38,7 @@ import com.velocitypowered.api.event.Continuation;
 import com.velocitypowered.api.event.Subscribe;
 import com.velocitypowered.api.event.connection.DisconnectEvent;
 import com.velocitypowered.api.event.connection.PreLoginEvent;
+import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentResult;
 import com.velocitypowered.api.event.player.GameProfileRequestEvent;
 import com.velocitypowered.api.event.player.ServerConnectedEvent;
 import com.velocitypowered.api.proxy.InboundConnection;
@@ -44,19 +46,23 @@ import com.velocitypowered.api.proxy.Player;
 import com.velocitypowered.api.proxy.server.RegisteredServer;
 import com.velocitypowered.api.util.GameProfile;

+import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;

+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+
 public class ConnectListener {

     private final FastLoginVelocity plugin;
-    private final RateLimiter rateLimiter;
+    private final AntiBotService antiBotService;

-    public ConnectListener(FastLoginVelocity plugin, RateLimiter rateLimiter) {
+    public ConnectListener(FastLoginVelocity plugin, AntiBotService antiBotService) {
         this.plugin = plugin;
-        this.rateLimiter = rateLimiter;
+        this.antiBotService = antiBotService;
     }

     @Subscribe
@@ -66,16 +72,28 @@ public class ConnectListener {
         }

         InboundConnection connection = preLoginEvent.getConnection();
-        if (!rateLimiter.tryAcquire()) {
-            plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection);
-            return;
-        }
-
         String username = preLoginEvent.getUsername();
-        plugin.getLog().info("Incoming login request for {} from {}", username, connection.getRemoteAddress());
-
-        Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, connection, username, continuation, preLoginEvent);
-        plugin.getScheduler().runAsync(asyncPremiumCheck);
+        InetSocketAddress address = connection.getRemoteAddress();
+        plugin.getLog().info("Incoming login request for {} from {}", username, address);
+
+        Action action = antiBotService.onIncomingConnection(address, username);
+        switch (action) {
+            case Ignore:
+                // just ignore
+                return;
+            case Block:
+                String message = plugin.getCore().getMessage("kick-antibot");
+                TextComponent messageParsed = LegacyComponentSerializer.legacyAmpersand().deserialize(message);
+
+                PreLoginComponentResult reason = PreLoginComponentResult.denied(messageParsed);
+                preLoginEvent.setResult(reason);
+                break;
+            case Continue:
+            default:
+                Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, connection, username, continuation, preLoginEvent);
+                plugin.getScheduler().runAsync(asyncPremiumCheck);
+                break;
+        }
     }

     @Subscribe
2022-06-18 18:09:31 +02:00
10785b32ac Fix anti bot reading the correct values 2022-06-18 18:08:40 +02:00
ae8c040d3e Merge pull request #814 from games647/dependabot/maven/org.geysermc.floodgate-api-2.2.0-SNAPSHOT
Bump api from 2.0-SNAPSHOT to 2.2.0-SNAPSHOT
2022-06-18 18:04:15 +02:00
b221a3f294 Merge pull request #822 from games647/dependabot/github_actions/actions/cache-3.0.4
Bump actions/cache from 3.0.1 to 3.0.4
2022-06-18 16:35:24 +02:00
fb32f0b386 Bump requried Java version to 17 2022-06-18 16:15:26 +02:00
752cb1c3a7 Bump actions/cache from 3.0.1 to 3.0.4
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.1 to 3.0.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.1...v3.0.4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-18 14:10:22 +00:00
f5ead50441 Fix typo in pull request template 2022-06-18 16:09:44 +02:00
b878ec34c8 Fix Java workflow setup 2022-06-18 16:09:44 +02:00
3d72e77345 Fix Java version name 2022-06-18 16:09:44 +02:00
18bd778b7e General minor code clean up and typo fixes 2022-06-18 16:09:44 +02:00
a9e4c90970 Merge pull request #799 from games647/dependabot/maven/org.mariadb.jdbc-mariadb-java-client-3.0.5
Bump mariadb-java-client from 3.0.4 to 3.0.5
2022-06-15 13:15:15 +02:00
c92f94d2e8 Bump api from 2.0-SNAPSHOT to 2.2.0-SNAPSHOT
Bumps api from 2.0-SNAPSHOT to 2.2.0-SNAPSHOT.

---
updated-dependencies:
- dependency-name: org.geysermc.floodgate:api
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-13 07:03:18 +00:00
0af22d9927 Merge pull request #811 from Enginecrafter77/main
Support for transparent proxies / DNAT
2022-06-11 08:40:48 +02:00
5abd864fa5 fix: Typo in config.yml comment about useProxyAgnosticResolver 2022-06-10 11:55:31 +02:00
d7d0caf6e7 style: Re-phrasing of config.yml comment for useProxyAgnosticResolver
The comment for useProxyAgnosticResolver in config.yml was re-phrased and supplied with additional information regarding its effective context and its relation to the prevent-proxy setting in server.properties.
2022-06-10 11:40:21 +02:00
edd82f7978 style: ProxyAgnosticMojangResolver: Extracted the URL into a constant
The URL of the mojang session server call in ProxyAgnosticMojangResolver was extracted into a constant in order to mitigate the "magic constants" problem.

Additionally, the 204 in the response code check was replaced by a constant from HttpURLConnection, again, in order to not use any magic numbers.
2022-06-10 11:38:22 +02:00
6413ca4d10 fix: Reverted the invalid session log entry modification
The initial debug-entry modification regarding the invalid session message was reverted. Now the logging and parameter expansion is done solely by the SLF4J library.
2022-06-10 11:14:10 +02:00
5f494e5a04 feat: Config option to enable ProxyAgnosticMojangResolver
A config option was added to allow users to use the ProxyAgnosticMojangResolver if they feel the need to do so. This setting won't affect existing users since it's disabled by default.
2022-06-09 19:24:53 +02:00
5d4483224d refactor: ProxyAgnosticMojangResolver
ProxyAgnosticMojangResolver serves as a cleaner implementation of the hasJoined static method from previous commit. All the reflection involved was removed due to no longer being necessary. This way, a lot cleaner code could be made. All the inner workings remained the same. For more information on how it works, check out ProxyAgnosticMojangResolver.java.
2022-06-09 19:04:49 +02:00
61b86fba52 fix: Initial addressing of #810
This commit contains a crude but functional fix for problem described by issue #810. It works by reimplementing the MojangResolver#hasJoined method using reflection to access the inaccessible fields. The significant difference is that unlike in the CraftAPI implementation, which sends the "ip" parameter when the hostIp parameter is an IPv4 address, but skips it for IPv6, this implementation ommits the "ip" parameter also for IPv4, effectively enabling transparent proxies to work.

WARNING: This commit contains only a proof-of-concept code with crude hacks to make it work with the least amount of code edits. Improvements upon the code will be included in following commits. The code is at the moment really ineffective, and therefore SHOULD NOT BE USED IN PRODUCTION! You have been warned.
2022-06-09 18:41:03 +02:00
6665c0359b Note latest proxy version 2022-06-08 10:20:15 +02:00
896291be53 Bump BungeeCord required version
Fixes #808
2022-06-08 10:08:15 +02:00
5828e1abde Remove last sodion reference 2022-06-07 18:51:22 +02:00
8d50a14371 Drop SodionAuth plugin, because it's no longer reachable in Maven 2022-06-07 18:26:24 +02:00
0ae18f3ac6 Always log invalid state messages
Related #803
Related #806
2022-06-07 18:24:27 +02:00
82d8b4d2be Fix typo in messages for invalid-request 2022-06-07 18:18:28 +02:00
41890679ed Bump mariadb-java-client from 3.0.4 to 3.0.5
Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.4 to 3.0.5.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.4...3.0.5)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-30 07:03:27 +00:00
fc50c997ba Dump craftapi for updated GSON version and allow . in legit usernames 2022-05-21 14:02:37 +02:00
b04b8f5806 Merge pull request #790 from games647/dependabot/github_actions/github/codeql-action-2
Bump github/codeql-action from 1 to 2
2022-05-20 12:34:03 +02:00
16f8a49b4c Inform users about the delayed session start for proxies 2022-05-20 12:32:58 +02:00
830d6b1e35 Bump github/codeql-action from 1 to 2
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1 to 2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-01 07:04:13 +00:00
a32791687c Merge pull request #774 from games647/dependabot/github_actions/actions/checkout-3
Bump actions/checkout from 2 to 3
2022-04-01 13:21:52 +02:00
5d63d304e6 Merge pull request #775 from games647/dependabot/github_actions/actions/cache-3.0.1
Bump actions/cache from 2.1.4 to 3.0.1
2022-04-01 13:21:29 +02:00
f25d8b3a99 Bump actions/cache from 2.1.4 to 3.0.1
Bumps [actions/cache](https://github.com/actions/cache) from 2.1.4 to 3.0.1.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v2.1.4...v3.0.1)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-01 07:02:15 +00:00
cac96408a0 Bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-01 07:02:13 +00:00
0bdcc935ad Merge pull request #771 from games647/dependabot/maven/org.apache.maven.plugins-maven-shade-plugin-3.3.0
Bump maven-shade-plugin from 3.2.4 to 3.3.0
2022-03-30 17:34:12 +02:00
07dea4ac0f Bump maven-shade-plugin from 3.2.4 to 3.3.0
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.2.4 to 3.3.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.2.4...maven-shade-plugin-3.3.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-30 14:04:11 +00:00
f4a3fed017 Highlight build definition 2022-03-30 16:02:52 +02:00
0105c29710 Reduce memory consumption of anti bot feature 2022-03-30 16:02:52 +02:00
d11cd4f9a1 Ignore META-Info in shading 2022-03-30 16:02:52 +02:00
6c0baea278 Make questions section more specific 2022-03-30 16:02:52 +02:00
55270702a5 Document event behavior that is platform specific 2022-03-30 16:02:52 +02:00
9d2c346235 Use platform scheduler to reuse threads between plugins
The platforms usually use a caching thread executor. It makes to use
this executor to reuse threads as they are expensive to create.
2022-03-30 16:02:51 +02:00
1d3fcb9929 Use custom repository settings for faster fetching
Order of repository seems to indicate maven priority fetching
2022-03-30 16:02:51 +02:00
35b044651c Update dependencies
diff --git a/bukkit/pom.xml b/bukkit/pom.xml
index e57b9d2..939697a 100644
--- a/bukkit/pom.xml
+++ b/bukkit/pom.xml
@@ -272,7 +272,7 @@
         <dependency>
             <groupId>com.lenis0012.bukkit</groupId>
             <artifactId>loginsecurity</artifactId>
-            <version>3.0.2</version>
+            <version>3.1</version>
             <scope>provided</scope>
             <optional>true</optional>
             <exclusions>
diff --git a/core/pom.xml b/core/pom.xml
index 15a1eb7..3738e9d 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -179,7 +179,7 @@
         <dependency>
             <groupId>com.google.code.gson</groupId>
             <artifactId>gson</artifactId>
-            <version>2.8.9</version>
+            <version>2.9.0</version>
         </dependency>
     </dependencies>
 </project>
2022-03-30 16:02:51 +02:00
666fabd76c Bump mariadb-java-client from 3.0.3 to 3.0.4
Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.3 to 3.0.4.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.3...3.0.4)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-28 13:17:13 +02:00
5d49c497b5 Bump slf4j-jdk14 from 2.0.0-alpha6 to 2.0.0-alpha7 (#756)
Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0-alpha6 to 2.0.0-alpha7.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha6...v_2.0.0-alpha7)

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-jdk14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-28 13:16:30 +02:00
812cf21ad0 Merge pull request #750 from Smart123s/license/autoheader
Fix License header
2022-03-14 14:54:17 +01:00
e7a915a6cd Check license headers before building
If a header is missing, then the build should fail. If someone commits
a new file, WITHOUT building it beforehnd, than that header won't be
added. The CI will build the plugin, but changes made by the CI are not
retained in the source tree.
2022-03-13 10:41:33 +01:00
a856356c49 Update license headers to 02db104
Done using `mvn license:format`
2022-03-13 10:08:57 +01:00
e9ea930be7 Update copyright year in LICENSE 2022-03-13 10:06:17 +01:00
59eb9ced8b Merge pull request #741 from games647/dependabot/github_actions/actions/setup-java-3
Bump actions/setup-java from 2.3.0 to 3
2022-03-01 08:32:54 +01:00
60d414eacb Bump actions/setup-java from 2.3.0 to 3
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2.3.0 to 3.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v2.3.0...v3)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-01 07:02:58 +00:00
ae93b3215a Merge pull request #737 from Smart123s/fix/736
Fix 'no-conflict' option of 'autoLoginFloodgate' in config
2022-02-26 17:37:37 +01:00
7e9bd1639b Move similar config checks to a common function
'autoRegisterFloodgate' and 'autoLoginFloodgate' have the same possible
options, and they are checked against the player in the exact same way.
For example, if either one of them is set to isLinked, linked status is
checked in the same way, regardless of wether it's checked for login or
for registering.
2022-02-25 18:14:25 +01:00
817eedd4ac Move autoLoginFloodgate check to a common function
Reduces duplicate code.
Added missing check for "no-conflict".
2022-02-25 18:13:44 +01:00
033fdd55ae [ci-skip] Update proxy setup guide 2022-02-15 12:01:13 +01:00
148 changed files with 4379 additions and 1927 deletions

View File

@ -1,6 +1,6 @@
name: 🐞 Bug Report
description: Something isn't working, broken, not expected behavior
labels: [bug]
labels: [ bug ]
body:
- type: markdown
attributes:
@ -36,8 +36,7 @@ body:
attributes:
label: Server log
description: The error, stacktrace or link the complete log. You can use the links above for long versions.
render: shell
placeholder: https://hastebin.com/ / https://gist.github.com/
placeholder: https://www.toptal.com/developers/hastebin / https://gist.github.com/
- type: input
attributes:
label: Plugin version
@ -60,9 +59,11 @@ body:
label: Relevance
description: Check list for previous tickets
options:
- label: I tried the latest build
- label: |
I tried the [latest build](https://ci.codemc.io/job/Games647/job/FastLogin/)
(build refers to development builds not necessary a release version; i.e. v1.10 is out of date)
required: true
- label: |
I checked for existing tickets -
If there are, please vote them with a thumps reaction and not create new ones
If there are, please vote them with a thumbs reaction and not create new ones
required: true

View File

@ -7,5 +7,6 @@
contact_links:
- name: 📌 Questions
url: https://github.com/games647/FastLogin/discussions
about: You want to ask something
about:
You want to ask something - general questions. Example includes how to set it up or how it is working internally

View File

@ -1,25 +0,0 @@
---
name: 💡 Enhancement request
about: New feature or change request
title: ''
labels: 'enhancement'
assignees: ''
---
[//]: # (Lines in this format are considered as comments and will not be displayed.)
[//]: # (Before reporting make sure you're running the **latest build** of the plugin and checked for existing issues!)
[//]: # (This ticket is about suggestions for a feature or particular enhancement)
### Is your feature request related to a problem? Please describe.
[//]: # (A clear and concise description of what the problem is. Ex. I'm always frustrated when [...])
### Describe the solution you'd like
[//]: # (A clear and concise description of what you want to happen.)
### Describe alternatives you've considered
[//]: # (A clear and concise description of any alternative solutions or features you've considered.)
### Additional context
[//]: # (Add any other context or screenshots about the feature request here.)

View File

@ -0,0 +1,43 @@
name: 💡 Enhancement request
description: New feature or change request
labels: [ enhancement ]
body:
- type: markdown
attributes:
value: |
This ticket is about suggestions for a feature or particular enhancement.
Feedback about this form is appreciated.
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
- type: dropdown
attributes:
label: Platform
description: Server software - choose your proxy software if you have multiple servers
options:
- Spigot
- BungeeCord
- Velocity
- All / Shared
validations:
required: true
- type: checkboxes
attributes:
label: Relevance
description: Check list for previous tickets
options:
- label: |
I checked for existing tickets -
If there are, please vote them with a thumbs reaction and not create new ones
required: true

View File

@ -14,22 +14,22 @@ updates:
directory: "/"
schedule:
interval: weekly
groups:
production-dependencies:
dependency-type: "production"
development-dependencies:
dependency-type: "development"
exclude-patterns:
# Create single PR for these
# Plugin require special evaluation about their compatibility
- "com.lenis0012.bukkit:loginsecurity"
- "com.comphenix.protocol:ProtocolLib"
ignore:
- dependency-name: com.google.code.gson:gson
versions:
- "> 2.2.4"
- dependency-name: com.google.guava
- dependency-name: me.clip:placeholderapi
versions:
- "> 2.10.8, < 2.11"
- dependency-name: net.md-5:bungeecord-config
versions:
- "> 1.12-SNAPSHOT"
- dependency-name: de.xxschrandxx.bca:BungeeCordAuthenticator
versions:
- 0.0.3
- dependency-name: com.zaxxer:HikariCP
versions:
- 4.0.0
- 4.0.2
- 4.0.3
# HikariCP dropped Java 8 support with 5.0
- dependency-name: "com.zaxxer:HikariCP"
update-types: ["version-update:semver-major"]
# SnakeYAML has breaking changes with 2.0
- dependency-name: "org.yaml:snakeyaml"
update-types: ["version-update:semver-major"]

View File

@ -1,8 +1,11 @@
[//]: # (Lines in this format are considered as comments and will not be displayed.)
[//]: # (If your work is in progress, please consider making a draft pull request.)
### Summary of your change
[//]: # (Example: motiviation, enhancement)
[//]: # (Example: motivation, enhancement)
### Related issue
[//]: # (Reference it using '#NUMBER'. Ex: Fixes/Related #...)

View File

@ -4,11 +4,11 @@
name: "CodeQL"
on:
# Scan only for push on the primary branch for now
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_run:
workflows: ["Maven Build"]
branches: [main]
types:
- completed
jobs:
# job i
@ -20,48 +20,46 @@ jobs:
# Environment
runs-on: ubuntu-latest
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
if: ${{ github.event.workflow_run.conclusion == 'success' }}
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
fail-fast: true
matrix:
# Languages to scan
language: [ 'java' ]
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Checkout repository
uses: actions/checkout@v4
# Setup Java
- name: Set up JDK
uses: actions/setup-java@v2.3.0
with:
distribution: 'adopt'
# Use Java 16, because it's minimum required version by Geyser
java-version: 16
cache: 'maven'
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# Setup Java
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version-file: '.java-version'
cache: 'maven'
# Cache build process too like in the maven config
- uses: actions/cache@v2.1.4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
# Manually start the autobuild process, because autobuild always selects Java 8 as build toolchain, but
# we are doing cross-crompiling from a newer Java version
- name: Build with Maven
# Extracted from autobuild
run: mvn package -f "pom.xml" --batch-mode -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip -DskipTests -Dmaven.test.skip.exec -Dlicense.skip=true -Drat.skip=true -Dspotless.check.skip=true -t /home/runner/.m2/toolchains.xml
# Auto build attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@ -2,7 +2,7 @@
# including making pull requests review easier
# Human-readable name in the actions tab
name: Java CI
name: Maven Build
# Build on every pull request regardless of the branch
# Wiki: https://help.github.com/en/actions/reference/events-that-trigger-workflows
@ -20,24 +20,28 @@ jobs:
# Environment image - always use the newest OS
runs-on: ubuntu-latest
permissions:
contents: write
# Run steps
steps:
# Pull changes
- uses: actions/checkout@v2.3.4
- uses: actions/checkout@v4
# Setup Java
- name: Set up JDK
uses: actions/setup-java@v2.3.0
uses: actions/setup-java@v4
with:
distribution: 'temurin'
# Use Java 16, because it's minimum required version by Geyser
java-version: 16
java-version-file: '.java-version'
cache: 'maven'
# Build and test (included in package)
- name: Build with Maven and test
# Run non-interactive, package (with compile+test),
# ignore snapshot updates, because they are likely to have breaking changes, enforce checksums to validate
# possible errors in dependencies
run: mvn test --batch-mode --no-snapshot-updates --strict-checksums --file pom.xml
# ignore snapshot updates, because they are likely to have breaking changes, enforce checksums
run: mvn test --batch-mode --threads 2.0C --no-snapshot-updates --strict-checksums --file pom.xml
- name: Update dependency graph
if: ${{ github.event_name == 'push' }}
uses: advanced-security/maven-dependency-submission-action@v4.0.0

2
.java-version Normal file
View File

@ -0,0 +1,2 @@
# Latest GitHub Action java release that is installed on the runners
21

View File

@ -35,7 +35,7 @@
* Automatically register accounts if they are not in the auth plugin database but in the FastLogin database
* Update BungeeAuth dependency and use the new API. Please update your plugin if you still use the old one.
* Remove deprecated API methods from the last version
* Finally update the IP column on every login
* Finally, update the IP column on every login
* No duplicate session login
* Fix timestamp parsing in newer versions of SQLite
* Fix Spigot console command invocation sends result to in game players
@ -82,7 +82,7 @@
* Fix player entry is not saved if namechangecheck is enabled
* Fix skin applies for third-party plugins
* Switch to mcapi.ca for uuid lookups
* Fix BungeeCord not setting an premium uuid
* Fix BungeeCord not setting a premium uuid
* Fix setting skin on Cauldron
* Fix saving on name change
@ -148,7 +148,7 @@
### 1.2
* Fix race condition in BungeeCord
* Fix dead lock in xAuth
* Fix deadlock in xAuth
* Added API methods for plugins to set their own password generator
* Added API methods for plugins to set their own auth plugin hook
=> Added support for AdvancedLogin
@ -182,7 +182,7 @@
* Added a forwardSkin config option
* Added premium UUID support
* Updated to the newest changes of Spigot
* Removes the need of an Bukkit auth plugin if you use a bungeecord one
* Removes the need of a Bukkit auth plugin if you use a bungeecord one
* Optimize performance and thread-safety
* Fixed BungeeCord support
* Changed config option auto-login to auto-register to clarify the usage

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015-2021 games647 and contributors
Copyright (c) 2015-2023 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
@ -19,4 +19,3 @@ 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.

View File

@ -19,7 +19,7 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
* No client modifications needed
* Good performance by using async operations
* Locale messages
* Support for Bedrock players proxied through FloodGate
* Support for Bedrock players proxies through FloodGate
## Issues
@ -60,12 +60,13 @@ Possible values: `Premium`, `Cracked`, `Unknown`
## Requirements
* Plugin:
* [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) or
* [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
* [Spigot](https://www.spigotmc.org) 1.8.8+
* Java 8+
* Run Spigot (or a fork e.g. Paper) and/or BungeeCord (or a fork e.g. Waterfall) in offline mode
* Java 17+ (Recommended)
* Server software in offlinemode:
* Spigot (or a fork e.g. Paper) 1.8.8+
* Protocol plugin:
* [ProtocolLib 5.1+](https://www.spigotmc.org/resources/protocollib.1997/) or
* [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
* Latest BungeeCord (or a fork e.g. Waterfall) or Velocity
* An auth plugin.
### Supported auth plugins
@ -77,7 +78,6 @@ Possible values: `Premium`, `Cracked`, `Unknown`
* [CrazyLogin](https://dev.bukkit.org/bukkit-plugins/crazylogin/)
* [LoginSecurity](https://dev.bukkit.org/bukkit-plugins/loginsecurity/)
* [LogIt](https://github.com/games647/LogIt)
* [SodionAuth (2.0+)](https://github.com/MohistMC/SodionAuth)
* [UltraAuth](https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/)
* [UserLogin](https://www.spigotmc.org/resources/userlogin.80669/)
* [xAuth](https://dev.bukkit.org/bukkit-plugins/xauth/)
@ -86,7 +86,6 @@ Possible values: `Premium`, `Cracked`, `Unknown`
* [BungeeAuth](https://www.spigotmc.org/resources/bungeeauth.493/)
* [BungeeAuthenticator](https://www.spigotmc.org/resources/bungeecordauthenticator.87669/)
* [SodionAuth (2.0+)](https://github.com/MohistMC/SodionAuth)
## Network requests
@ -102,20 +101,28 @@ This plugin performs network requests to:
### Spigot/Paper
1. Download and install ProtocolLib/ProtocolSupport
2. Download and install FastLogin (or FastLoginBukkit for newer versions)
3. Set your server in offline mode by setting the value onlinemode in your server.properties to false
2. Download and install FastLogin (or `FastLoginBukkit` for newer versions)
3. Set your server in offline mode by setting the value `onlinemode` in your server.properties to false
### BungeeCord/Waterfall
### BungeeCord/Waterfall or Velocity
1. Activate BungeeCord in the Spigot configuration
2. Restart your server
3. Now there is `allowed-proxies.txt` file in the FastLogin folder
Put your stats id from the BungeeCord config into this file
4. Activate ipForward in your BungeeCord config
5. Download and Install FastLogin (or FastLoginBungee/FastLoginBukkit in newer versions) on BungeeCord AND Spigot
(on the servers where your login plugin is or where player should be able to execute the commands of FastLogin)
6. Check your database settings in the config of FastLogin on BungeeCord
7. Set proxy and Spigot in offline mode by setting the value onlinemode in your config.yml to false
8. You should *always* firewall your Spigot server that it's only accessible through BungeeCord
* https://www.spigotmc.org/wiki/bungeecord-installation/#post-installation
* BungeeCord doesn't support SQLite per default, so you should change the configuration to MySQL or MariaDB. For that you have to install MariaDB/MySQL on your root server first and put the credentials you made in the FastLogin config files.
Install the plugin on both platforms, that is proxy (BungeeCord or Velocity) and backend server (Spigot).
1. Activate proxy support in the server configuration
* This is often found in `spigot.yml` or `paper.yml`
2. Restart the backend server
3. Now there is `allowed-proxies.txt` file in the FastLogin folder of the restarted server
* BungeeCord: Put your `stats`-id from the BungeeCord config into this file
* Velocity: On plugin startup the plugin generates a `proxyId.txt` inside the plugins folder of the proxy
4. Activate ip forwarding in your proxy config
5. Check your database settings in the config of FastLogin on your proxy
* The proxies only ship with a limited set of drivers where Spigot supports more. Therefore, these are supported:
* BungeeCord: `com.mysql.jdbc.Driver` for MySQL/MariaDB/PostgreSQL
* Velocity: `fastlogin.mariadb.jdbc.Driver` for MySQL/MariaDB/PostgreSQL
* Note the embedded file storage SQLite is not available
* MySQL/MariaDB/PostgreSQL requires an external database server running. Check your server provider if there is one available
or install one.
6. Set proxy and Spigot in offline mode by setting the value `onlinemode` in your `config.yml` to false
7. You should *always* configure the firewall for your Spigot server so that it's only accessible through your proxy
* This is also the case without this plugin
* https://www.spigotmc.org/wiki/bungeecord-installation/#post-installation

View File

@ -4,7 +4,7 @@
The MIT License (MIT)
Copyright (c) 2015-2021 <Your name and contributors>
Copyright (c) 2015-2023 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
@ -25,14 +25,18 @@
SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<nettyVersion>4.1.79.Final</nettyVersion>
</properties>
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.11-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -45,13 +49,16 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<version>3.5.1</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>false</shadedArtifactAttached>
<relocations>
<relocation>
<pattern>org.yaml.snakeyaml</pattern>
<shadedPattern>fastlogin.yaml</shadedPattern>
</relocation>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>fastlogin.hikari</shadedPattern>
@ -83,9 +90,19 @@
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
<!-- Located in META-INF/services -->
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<minimizeJar>true</minimizeJar>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
<exclude>**/module-info.class</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
@ -109,7 +126,7 @@
<!-- ProtocolLib -->
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/nexus/repository/public/</url>
<url>https://repo.dmulloy2.net/repository/public/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
@ -119,12 +136,6 @@
<repository>
<id>codemc-releases</id>
<url>https://repo.codemc.io/repository/maven-public/</url>
</repository>
<!-- GitHub automatic maven builds -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
@ -138,6 +149,15 @@
<enabled>false</enabled>
</snapshots>
</repository>
<!-- GitHub automatic maven builds -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
@ -148,11 +168,18 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
<scope>compile</scope>
</dependency>
<!-- PaperSpigot API for correcting user cache usage -->
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<version>1.19-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<!-- Use our own newer api version -->
<exclusions>
@ -163,18 +190,24 @@
</exclusions>
</dependency>
<!-- PaperLib for checking if server uses PaperSpigot -->
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.7</version>
<groupId>com.mojang</groupId>
<artifactId>datafixerupper</artifactId>
<version>5.0.28</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Library for listening and sending Minecraft packets-->
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>4.7.0</version>
<version>5.1.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
@ -189,7 +222,7 @@
<groupId>com.github.ProtocolSupport</groupId>
<artifactId>ProtocolSupport</artifactId>
<!--4.29.dev after commit about API improvements-->
<version>3a80c661fe</version>
<version>master-66b494a8dd-1</version>
<scope>provided</scope>
<exclusions>
<exclusion>
@ -203,15 +236,11 @@
<dependency>
<groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId>
<version>2.0-SNAPSHOT</version>
<version>${floodgate.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.geysermc.cumulus</groupId>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
@ -219,7 +248,7 @@
<!-- Bedrock player bridge -->
<dependency>
<groupId>org.geysermc</groupId>
<groupId>org.geysermc.geyser</groupId>
<artifactId>core</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
@ -233,17 +262,23 @@
<!-- We need the API, but it was excluded above -->
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>geyser-api</artifactId>
<groupId>org.geysermc.geyser</groupId>
<artifactId>api</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Provide premium placeholders-->
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.11.0</version>
<version>2.11.5</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
@ -258,7 +293,7 @@
<dependency>
<groupId>fr.xephi</groupId>
<artifactId>authme</artifactId>
<version>5.4.0</version>
<version>5.6.0-beta2</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
@ -272,7 +307,7 @@
<dependency>
<groupId>com.lenis0012.bukkit</groupId>
<artifactId>loginsecurity</artifactId>
<version>3.0.2</version>
<version>3.3.0</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
@ -341,17 +376,32 @@
</dependency>
<dependency>
<groupId>com.github.Mohist-Community.SodionAuth</groupId>
<artifactId>SodionAuth-Bukkit</artifactId>
<version>2bdfdc854b</version>
<exclusions>
<exclusion>
<groupId>com.github.Mohist-Community.SodionAuth</groupId>
<artifactId>SodionAuth-Libs</artifactId>
</exclusion>
</exclusions>
<optional>true</optional>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>${nettyVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
<version>${nettyVersion}</version>
<scope>test</scope>
</dependency>
<!-- Provided by the spigot, required for testing ProtocolLib -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -26,14 +26,16 @@
package com.github.games647.fastlogin.bukkit;
import com.github.games647.craftapi.model.skin.SkinProperty;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
/**
* Represents a client connecting to the server.
*
* <p>
* This session is invalid if the player disconnects or the login was successful
*/
public class BukkitLoginSession extends LoginSession {
@ -42,35 +44,38 @@ 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
, StoredProfile profile) {
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);
}
/**
* Gets the verify-token the server sent to the client.
*
* <p>
* Empty if it's a BungeeCord connection
*
* @return verify token from the server
@ -79,6 +84,11 @@ public class BukkitLoginSession extends LoginSession {
return verifyToken.clone();
}
@Nullable
public ClientPublicKey getClientPublicKey() {
return clientPublicKey;
}
/**
* @return premium skin if available
*/

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -26,22 +26,20 @@
package com.github.games647.fastlogin.bukkit;
import com.github.games647.fastlogin.core.AsyncScheduler;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.slf4j.Logger;
import java.util.concurrent.Executor;
public class BukkitScheduler extends AsyncScheduler {
private final Executor syncExecutor;
public BukkitScheduler(Plugin plugin, Logger logger, ThreadFactory threadFactory) {
super(logger, threadFactory);
public BukkitScheduler(Plugin plugin, Logger logger) {
super(logger, command -> Bukkit.getScheduler().runTaskAsynchronously(plugin, command));
syncExecutor = r -> Bukkit.getScheduler().runTask(plugin, r);
syncExecutor = task -> Bukkit.getScheduler().runTask(plugin, task);
}
public Executor getSyncExecutor() {

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -31,21 +31,23 @@ import com.github.games647.fastlogin.core.message.LoginActionMessage;
import com.github.games647.fastlogin.core.message.NamespaceKey;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageRecipient;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Stream;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageRecipient;
import static com.github.games647.fastlogin.core.message.ChangePremiumMessage.CHANGE_CHANNEL;
import static com.github.games647.fastlogin.core.message.SuccessMessage.SUCCESS_CHANNEL;
import static java.util.stream.Collectors.toSet;
@ -61,7 +63,7 @@ public class BungeeManager {
private final FastLoginBukkit plugin;
private boolean enabled;
private final Set<UUID> firedJoinEvents = new HashSet<>();
private final Collection<UUID> firedJoinEvents = new HashSet<>();
public BungeeManager(FastLoginBukkit plugin) {
this.plugin = plugin;
@ -87,33 +89,70 @@ public class BungeeManager {
}
public void initialize() {
try {
enabled = detectProxy();
} catch (Exception ex) {
plugin.getLog().warn("Cannot check proxy support. Fallback to non-proxy mode", ex);
}
enabled = detectProxy();
if (enabled) {
proxyIds = loadBungeeCordIds();
if (proxyIds.isEmpty()) {
plugin.getLog().info("No valid IDs found. Minecraft proxy support cannot work in the current state");
}
registerPluginChannels();
plugin.getLog().info("Found enabled proxy configuration");
plugin.getLog().info("Remember to follow the proxy guide to complete your setup");
} else {
plugin.getLog().warn("Disabling Minecraft proxy configuration. Assuming direct connections from now on.");
}
}
private boolean isProxySupported(String className, String fieldName) {
private boolean isProxySupported(String className, String fieldName)
throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
return Class.forName(className).getDeclaredField(fieldName).getBoolean(null);
}
private boolean isVelocityEnabled()
throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException, ClassNotFoundException,
NoSuchMethodException, InvocationTargetException {
try {
return Class.forName(className).getDeclaredField(fieldName).getBoolean(null);
} catch (ClassNotFoundException notFoundEx) {
//ignore server has no proxy support
} catch (NoSuchFieldException | IllegalAccessException noSuchFieldException) {
plugin.getLog().warn("Cannot access proxy field", noSuchFieldException);
Class<?> globalConfig = Class.forName("io.papermc.paper.configuration.GlobalConfiguration");
Object global = globalConfig.getDeclaredMethod("get").invoke(null);
Object proxiesConfiguration = global.getClass().getDeclaredField("proxies").get(global);
Field velocitySectionField = proxiesConfiguration.getClass().getDeclaredField("velocity");
Object velocityConfig = velocitySectionField.get(proxiesConfiguration);
return velocityConfig.getClass().getDeclaredField("enabled").getBoolean(velocityConfig);
} catch (ClassNotFoundException classNotFoundException) {
// try again using the older Paper configuration, because the old class file still exists in newer versions
if (isProxySupported("com.destroystokyo.paper.PaperConfig", "velocitySupport")) {
return true;
}
}
return false;
}
private boolean detectProxy() {
return isProxySupported("org.spigotmc.SpigotConfig", "bungee")
|| isProxySupported("com.destroystokyo.paper.PaperConfig", "velocitySupport");
try {
if (isProxySupported("org.spigotmc.SpigotConfig", "bungee")) {
return true;
}
} catch (ClassNotFoundException classNotFoundException) {
// leave stacktrace for class not found out
plugin.getLog().warn("Cannot check for BungeeCord support: {}", classNotFoundException.getMessage());
} catch (NoSuchFieldException | IllegalAccessException ex) {
plugin.getLog().warn("Cannot check for BungeeCord support", ex);
}
try {
return isVelocityEnabled();
} catch (ClassNotFoundException classNotFoundException) {
plugin.getLog().warn("Cannot check for Velocity support in Paper: {}", classNotFoundException.getMessage());
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
plugin.getLog().warn("Cannot check for Velocity support in Paper", ex);
}
return false;
}
private void registerPluginChannels() {
@ -147,9 +186,7 @@ public class BungeeManager {
Files.deleteIfExists(legacyFile);
try (Stream<String> lines = Files.lines(proxiesFile)) {
return lines.map(String::trim)
.map(UUID::fromString)
.collect(toSet());
return lines.map(String::trim).map(UUID::fromString).collect(toSet());
}
} catch (IOException ex) {
plugin.getLog().error("Failed to read proxies", ex);
@ -176,7 +213,7 @@ public class BungeeManager {
/**
* Check if the event fired including with the task delay. This necessary to restore the order of processing the
* BungeeCord messages after the PlayerJoinEvent fires including the delay.
*
* <p>
* If the join event fired, the delay exceeded, but it ran earlier and couldn't find the recently started login
* session. If not fired, we can start a new force login task. This will still match the requirement that we wait
* a certain time after the player join event fired.

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,11 +25,11 @@
*/
package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.ProtocolLibrary;
import com.github.games647.fastlogin.bukkit.command.CrackedCommand;
import com.github.games647.fastlogin.bukkit.command.PremiumCommand;
import com.github.games647.fastlogin.bukkit.listener.ConnectionListener;
import com.github.games647.fastlogin.bukkit.listener.PaperCacheListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.ManualNameChange;
import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.SkinApplyListener;
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
@ -41,8 +41,15 @@ import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
import io.papermc.lib.PaperLib;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.geyser.GeyserImpl;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import java.net.InetSocketAddress;
import java.nio.file.Path;
@ -52,15 +59,6 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.geyser.GeyserImpl;
import org.slf4j.Logger;
/**
* This plugin checks if a player has a paid account and if so tries to skip offline mode authentication.
*/
@ -82,7 +80,7 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
public FastLoginBukkit() {
this.logger = CommonUtil.initializeLoggerService(getLogger());
this.scheduler = new BukkitScheduler(this, logger, getThreadFactory());
this.scheduler = new BukkitScheduler(this, logger);
}
@Override
@ -114,27 +112,12 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
}
if (pluginManager.isPluginEnabled("ProtocolSupport")) {
pluginManager.registerEvents(new ProtocolSupportListener(this, core.getRateLimiter()), this);
pluginManager.registerEvents(new ProtocolSupportListener(this, core.getAntiBot()), this);
} else if (pluginManager.isPluginEnabled("ProtocolLib")) {
ProtocolLibListener.register(this, core.getRateLimiter());
if (isPluginInstalled("floodgate")) {
if (getConfig().getBoolean("floodgatePrefixWorkaround")){
ManualNameChange.register(this, floodgateService);
logger.info("Floodgate prefix injection workaround has been enabled.");
logger.info("If you have problems joining the server, try disabling it in the configuration.");
} else {
logger.warn("We have detected that you are runnging FastLogin alongside Floodgate and ProtocolLib.");
logger.warn("Currently there is an issue with FastLogin that prevents Floodgate name prefixes from showing up "
+ "when it is together used with ProtocolLib.");
logger.warn("If you would like to use Floodgate name prefixes, you can enable an experimental workaround by changing "
+ "the value 'floodgatePrefixWorkaround' to true in config.yml.");
logger.warn("For more information visit https://github.com/games647/FastLogin/issues/493");
}
}
ProtocolLibListener.register(this, core.getAntiBot(), core.getConfig().getBoolean("verifyClientKeys"));
//if server is using paper - we need to set the skin at pre login anyway, so no need for this listener
if (!PaperLib.isPaper() && getConfig().getBoolean("forwardSkin")) {
if (!isPaper() && getConfig().getBoolean("forwardSkin")) {
pluginManager.registerEvents(new SkinApplyListener(this), this);
}
} else {
@ -150,7 +133,7 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
pluginManager.registerEvents(new ConnectionListener(this), this);
//if server is using paper - we need to add one more listener to correct the user cache usage
if (PaperLib.isPaper()) {
if (isPaper()) {
pluginManager.registerEvents(new PaperCacheListener(this), this);
}
@ -162,8 +145,6 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
premiumPlaceholder = new PremiumPlaceholder(this);
premiumPlaceholder.register();
}
dependencyWarnings();
}
private boolean initializeFloodgate() {
@ -202,6 +183,10 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
logger.error("Failed to unregister placeholder", exception);
}
}
if (getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
ProtocolLibrary.getProtocolManager().getAsynchronousManager().unregisterAsyncHandlers(this);
}
}
public FastLoginCore<Player, CommandSender, FastLoginBukkit> getCore() {
@ -243,12 +228,28 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
/**
* Fetches the premium status of an online player.
* {@snippet :
* // Bukkit's players object after successful authentication i.e. PlayerJoinEvent
* // except for proxies like BungeeCord and Velocity where the details are sent delayed (1-2 seconds)
* Player player;
* PremiumStatus status = JavaPlugin.getPlugin(FastLoginBukkit.class).getStatus(player.getUniqueId());
* switch (status) {
* case CRACKED:
* // player is offline
* break;
* case PREMIUM:
* // account is premium and player passed the verification
* break;
* case UNKNOWN:
* // no record about this player
* }
* }
*
* @param onlinePlayer player that is currently online player (play state)
* @return the online status or unknown if an error happened, the player isn't online or BungeeCord doesn't send
* us the status message yet (This means you cannot check the login status on the PlayerJoinEvent).
*/
public PremiumStatus getStatus(UUID onlinePlayer) {
public @NotNull PremiumStatus getStatus(@NotNull UUID onlinePlayer) {
return premiumPlayers.getOrDefault(onlinePlayer, PremiumStatus.UNKNOWN);
}
@ -290,16 +291,17 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
receiver.sendMessage(message);
}
/**
* Checks if a plugin is installed on the server
* @param name the name of the plugin
* @return true if the plugin is installed
*/
@Override
public boolean isPluginInstalled(String name) {
// the plugin may be enabled after FastLogin, so isPluginEnabled() won't work here
return Bukkit.getServer().getPluginManager().getPlugin(name) != null;
}
/**
* Checks if a plugin is installed on the server
*
* @param name the name of the plugin
* @return true if the plugin is installed
*/
@Override
public boolean isPluginInstalled(String name) {
// the plugin may be enabled after FastLogin, so isPluginEnabled() won't work here
return Bukkit.getServer().getPluginManager().getPlugin(name) != null;
}
public FloodgateService getFloodgateService() {
return floodgateService;
@ -317,17 +319,16 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
return geyserService;
}
/**
* Send warning messages to log if incompatible plugins are used
*/
private void dependencyWarnings() {
if (isPluginInstalled("floodgate-bukkit")) {
logger.warn("We have detected that you are running Floodgate 1.0 which is not supported by the Bukkit "
+ "version of FastLogin.");
logger.warn("If you would like to use FastLogin with Floodgate, you can download development builds of "
+ "Floodgate 2.0 from https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/dev%252F2.0/");
logger.warn("Don't forget to update Geyser to a supported version as well from "
+ "https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/floodgate-2.0/");
}
private boolean isPaper() {
return isClassAvailable("com.destroystokyo.paper.PaperConfig").isPresent()
|| isClassAvailable("io.papermc.paper.configuration.Configuration").isPresent();
}
private Optional<Class<?>> isClassAvailable(String clazzName) {
try {
return Optional.of(Class.forName(clazzName));
} catch (ClassNotFoundException e) {
return Optional.empty();
}
}
}

View File

@ -0,0 +1,63 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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;
import java.net.InetAddress;
public final class InetUtils {
private InetUtils() {
// Utility
}
/**
* Verifies if the given IP address is from the local network
*
* @param address IP address
* @return true if address is from local network or even from the device itself (loopback)
*/
public static boolean isLocalAddress(InetAddress address) {
// Loopback addresses like 127.0.* (IPv4) or [::1] (IPv6)
return address.isLoopbackAddress()
// Example: 10.0.0.0, 172.16.0.0, 192.168.0.0, fec0::/10 (deprecated)
// Ref: https://en.wikipedia.org/wiki/IP_address#Private_addresses
|| address.isSiteLocalAddress()
// Example: 169.254.0.0/16, fe80::/10
// Ref: https://en.wikipedia.org/wiki/IP_address#Address_autoconfiguration
|| address.isLinkLocalAddress()
// non deprecated unique site-local that java doesn't check yet -> fc00::/7
|| isIPv6UniqueSiteLocal(address);
}
private static boolean isIPv6UniqueSiteLocal(InetAddress address) {
// ref: https://en.wikipedia.org/wiki/Unique_local_address
// currently undefined but could be used in the near future fc00::/8
return (address.getAddress()[0] & 0xFF) == 0xFC
// in use for unique site-local fd00::/8
|| (address.getAddress()[0] & 0xFF) == 0xFD;
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -26,10 +26,12 @@
package com.github.games647.fastlogin.bukkit;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
public class PremiumPlaceholder extends PlaceholderExpansion {
private static final String PLACEHOLDER_VARIABLE = "status";
@ -40,6 +42,16 @@ public class PremiumPlaceholder extends PlaceholderExpansion {
this.plugin = plugin;
}
@Override
public boolean persist() {
return true;
}
@Override
public @NotNull List<String> getPlaceholders() {
return Collections.singletonList(PLACEHOLDER_VARIABLE);
}
@Override
public String onRequest(OfflinePlayer player, @NotNull String identifier) {
// player is null if offline

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -27,8 +27,7 @@ package com.github.games647.fastlogin.bukkit.command;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -42,9 +41,10 @@ public class CrackedCommand extends ToggleCommand {
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
String[] args) {
if (args.length == 0) {
onCrackedSelf(sender, command, args);
onCrackedSelf(sender);
} else {
onCrackedOther(sender, command, args);
}
@ -52,7 +52,7 @@ public class CrackedCommand extends ToggleCommand {
return true;
}
private void onCrackedSelf(CommandSender sender, Command cmd, String[] args) {
private void onCrackedSelf(CommandSender sender) {
if (isConsole(sender)) {
return;
}
@ -61,25 +61,20 @@ public class CrackedCommand extends ToggleCommand {
return;
}
if (plugin.getBungeeManager().isEnabled()) {
sendBungeeActivateMessage(sender, sender.getName(), false);
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
} else {
//todo: load async if
StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isPremium()) {
plugin.getCore().sendLocaleMessage("remove-premium", sender);
// todo: load async if
StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isPremium()) {
plugin.getCore().sendLocaleMessage("remove-premium", sender);
profile.setPremium(false);
profile.setId(null);
plugin.getScheduler().runAsync(() -> {
plugin.getCore().getStorage().save(profile);
plugin.getServer().getPluginManager().callEvent(
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
});
} else {
plugin.getCore().sendLocaleMessage("not-premium", sender);
}
profile.setPremium(false);
profile.setId(null);
plugin.getScheduler().runAsync(() -> {
plugin.getCore().getStorage().save(profile);
plugin.getServer().getPluginManager().callEvent(
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
});
} else {
plugin.getCore().sendLocaleMessage("not-premium", sender);
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -27,16 +27,15 @@ package com.github.games647.fastlogin.bukkit.command;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
import java.util.UUID;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Let users activate fast login by command. This only be accessible if
* the user has access to its account. So we can make sure that not another
@ -49,9 +48,10 @@ public class PremiumCommand extends ToggleCommand {
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
String[] args) {
if (args.length == 0) {
onPremiumSelf(sender, command, args);
onPremiumSelf(sender);
} else {
onPremiumOther(sender, command, args);
}
@ -59,7 +59,7 @@ public class PremiumCommand extends ToggleCommand {
return true;
}
private void onPremiumSelf(CommandSender sender, Command cmd, String[] args) {
private void onPremiumSelf(CommandSender sender) {
if (isConsole(sender)) {
return;
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -28,9 +28,6 @@ package com.github.games647.fastlogin.bukkit.command;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
import com.github.games647.fastlogin.core.message.ChannelMessage;
import java.util.Optional;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
@ -38,6 +35,8 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageRecipient;
import java.util.Optional;
public abstract class ToggleCommand implements CommandExecutor {
protected final FastLoginBukkit plugin;

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,9 +25,9 @@
*/
package com.github.games647.fastlogin.bukkit.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
@ -35,7 +35,7 @@ import org.jetbrains.annotations.NotNull;
public class BukkitFastLoginAutoLoginEvent extends Event implements FastLoginAutoLoginEvent, Cancellable {
private static final HandlerList handlers = new HandlerList();
private static final HandlerList HANDLERS = new HandlerList();
private final LoginSession session;
private final StoredProfile profile;
private boolean cancelled;
@ -69,10 +69,10 @@ public class BukkitFastLoginAutoLoginEvent extends Event implements FastLoginAut
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
return HANDLERS;
}
public static HandlerList getHandlerList() {
return handlers;
return HANDLERS;
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,16 +25,16 @@
*/
package com.github.games647.fastlogin.bukkit.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSource;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
public class BukkitFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {
private static final HandlerList handlers = new HandlerList();
private static final HandlerList HANDLERS = new HandlerList();
private final String username;
private final LoginSource source;
private final StoredProfile profile;
@ -64,10 +64,10 @@ public class BukkitFastLoginPreLoginEvent extends Event implements FastLoginPreL
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
return HANDLERS;
}
public static HandlerList getHandlerList() {
return handlers;
return HANDLERS;
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,15 +25,15 @@
*/
package com.github.games647.fastlogin.bukkit.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
private static final HandlerList handlers = new HandlerList();
private static final HandlerList HANDLERS = new HandlerList();
private final StoredProfile profile;
private final PremiumToggleReason reason;
@ -55,10 +55,10 @@ public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLogi
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
return HANDLERS;
}
public static HandlerList getHandlerList() {
return handlers;
return HANDLERS;
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -41,13 +41,13 @@ import org.bukkit.event.Listener;
import java.lang.reflect.Field;
/**
* GitHub: https://github.com/Xephi/AuthMeReloaded/
* GitHub: <a href="https://github.com/Xephi/AuthMeReloaded/">...</a>
* <p>
* Project page:
* <p>
* Bukkit: https://dev.bukkit.org/bukkit-plugins/authme-reloaded/
* Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/authme-reloaded/">...</a>
* <p>
* Spigot: https://www.spigotmc.org/resources/authme-reloaded.6269/
* Spigot: <a href="https://www.spigotmc.org/resources/authme-reloaded.6269/">...</a>
*/
public class AuthMeHook implements AuthPlugin<Player>, Listener {
@ -60,7 +60,7 @@ public class AuthMeHook implements AuthPlugin<Player>, Listener {
this.plugin = plugin;
this.authmeAPI = AuthMeApi.getInstance();
if (plugin.getConfig().getBoolean("respectIpLimit", false)) {
if (plugin.getCore().getConfig().getBoolean("respectIpLimit", false)) {
try {
Field managementField = this.authmeAPI.getClass().getDeclaredField("management");
managementField.setAccessible(true);
@ -75,7 +75,7 @@ public class AuthMeHook implements AuthPlugin<Player>, Listener {
public void onSessionRestore(RestoreSessionEvent restoreSessionEvent) {
Player player = restoreSessionEvent.getPlayer();
BukkitLoginSession session = plugin.getSession(player.getAddress());
BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress());
if (session != null && session.isVerified()) {
restoreSessionEvent.setCancelled(true);
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,29 +25,28 @@
*/
package com.github.games647.fastlogin.bukkit.hook;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import de.st_ddt.crazylogin.CrazyLogin;
import de.st_ddt.crazylogin.data.LoginPlayerData;
import de.st_ddt.crazylogin.databases.CrazyLoginDataDatabase;
import de.st_ddt.crazylogin.listener.PlayerListener;
import de.st_ddt.crazylogin.metadata.Authenticated;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* GitHub: https://github.com/ST-DDT/CrazyLogin
* GitHub: <a href="https://github.com/ST-DDT/CrazyLogin">...</a>
* <p>
* Project page:
* <p>
* Bukkit: https://dev.bukkit.org/server-mods/crazylogin/
* Bukkit: <a href="https://dev.bukkit.org/server-mods/crazylogin/">...</a>
*/
public class CrazyLoginHook implements AuthPlugin<Player> {
@ -134,15 +133,8 @@ public class CrazyLoginHook implements AuthPlugin<Player> {
return false;
}
private PlayerListener getListener() {
PlayerListener listener;
try {
listener = (PlayerListener) FieldUtils.readField(crazyLoginPlugin, "playerListener", true);
} catch (IllegalAccessException ex) {
plugin.getLog().error("Failed to get the listener instance for auto login", ex);
listener = null;
}
return listener;
protected PlayerListener getListener() {
FieldAccessor accessor = Accessors.getFieldAccessor(crazyLoginPlugin.getClass(), PlayerListener.class, true);
return (PlayerListener) accessor.get(crazyLoginPlugin);
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -27,18 +27,16 @@ package com.github.games647.fastlogin.bukkit.hook;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import io.github.lucaseasedup.logit.CancelledState;
import io.github.lucaseasedup.logit.LogItCore;
import io.github.lucaseasedup.logit.account.Account;
import io.github.lucaseasedup.logit.session.SessionManager;
import org.bukkit.entity.Player;
import java.time.Instant;
import org.bukkit.entity.Player;
/**
* GitHub: https://github.com/XziomekX/LogIt
* GitHub: <a href="https://github.com/XziomekX/LogIt">...</a>
* <p>
* Project page:
* <p>

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -32,17 +32,16 @@ import com.lenis0012.bukkit.loginsecurity.session.AuthService;
import com.lenis0012.bukkit.loginsecurity.session.PlayerSession;
import com.lenis0012.bukkit.loginsecurity.session.action.LoginAction;
import com.lenis0012.bukkit.loginsecurity.session.action.RegisterAction;
import org.bukkit.entity.Player;
/**
* GitHub: https://github.com/lenis0012/LoginSecurity-2
* GitHub: <a href="https://github.com/lenis0012/LoginSecurity-2">...</a>
* <p>
* Project page:
* <p>
* Bukkit: https://dev.bukkit.org/bukkit-plugins/loginsecurity/
* Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/loginsecurity/">...</a>
* <p>
* Spigot: https://www.spigotmc.org/resources/loginsecurity.19362/
* Spigot: <a href="https://www.spigotmc.org/resources/loginsecurity.19362/">...</a>
*/
public class LoginSecurityHook implements AuthPlugin<Player> {

View File

@ -1,78 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name 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.hook;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import org.bukkit.entity.Player;
import red.mohist.sodionauth.bukkit.implementation.BukkitPlayer;
import red.mohist.sodionauth.core.SodionAuthApi;
import red.mohist.sodionauth.core.exception.AuthenticatedException;
/**
* GitHub: https://github.com/Mohist-Community/SodionAuth
* <p>
* Project page: https://gitea.e-loli.com/SodionAuth/SodionAuth
* <p>
* Bukkit: Unknown
* <p>
* Spigot: https://www.spigotmc.org/resources/sodionauth.76944/
*/
public class SodionAuthHook implements AuthPlugin<Player> {
private final FastLoginBukkit plugin;
public SodionAuthHook(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean forceLogin(Player player) {
try {
SodionAuthApi.login(new BukkitPlayer(player));
} catch (AuthenticatedException e) {
plugin.getLog().warn(ALREADY_AUTHENTICATED, player);
return false;
}
return true;
}
@Override
public boolean forceRegister(Player player, String password) {
try{
return SodionAuthApi.register(new BukkitPlayer(player), password);
} catch (UnsupportedOperationException e){
plugin.getLog().warn("Currently SodionAuth is not accepting forceRegister, " +
"It may be caused by unsupported AuthBackend");
return false;
}
}
@Override
public boolean isRegistered(String playerName) {
return SodionAuthApi.isRegistered(playerName);
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -27,21 +27,20 @@ package com.github.games647.fastlogin.bukkit.hook;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import ultraauth.api.UltraAuthAPI;
import ultraauth.managers.PlayerManager;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* Project page:
* <p>
* Bukkit: https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/
* Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/">...</a>
* <p>
* Spigot: https://www.spigotmc.org/resources/ultraauth.17044/
* Spigot: <a href="https://www.spigotmc.org/resources/ultraauth.17044/">...</a>
*/
public class UltraAuthHook implements AuthPlugin<Player> {

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -27,29 +27,27 @@ package com.github.games647.fastlogin.bukkit.hook;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import de.luricos.bukkit.xAuth.xAuth;
import de.luricos.bukkit.xAuth.xAuthPlayer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* GitHub: https://github.com/LycanDevelopment/xAuth/
* GitHub: <a href="https://github.com/LycanDevelopment/xAuth/">...</a>
* <p>
* Project page:
* <p>
* Bukkit: https://dev.bukkit.org/bukkit-plugins/xauth/
* Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/xauth/">...</a>
*/
public class xAuthHook implements AuthPlugin<Player> {
public class XAuthHook implements AuthPlugin<Player> {
private final xAuth xAuthPlugin = xAuth.getPlugin();
private final FastLoginBukkit plugin;
public xAuthHook(FastLoginBukkit plugin) {
public XAuthHook(FastLoginBukkit plugin) {
this.plugin = plugin;
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -34,18 +34,16 @@ import com.github.games647.fastlogin.core.message.LoginActionMessage;
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import java.net.InetSocketAddress;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Responsible for receiving messages from a BungeeCord instance.
*
* <p>
* This class also receives the plugin message from the bungeecord version of this plugin in order to get notified if
* the connection is in online mode.
*/
@ -93,24 +91,23 @@ public class BungeeListener implements PluginMessageListener {
String playerName = message.getPlayerName();
Type type = message.getType();
InetSocketAddress address = player.getAddress();
plugin.getLog().info("Player info {} command for {} from proxy", type, playerName);
if (type == Type.LOGIN) {
onLoginMessage(player, playerName, address);
onLoginMessage(player, playerName);
} else if (type == Type.REGISTER) {
onRegisterMessage(player, playerName, address);
onRegisterMessage(player, playerName);
} else if (type == Type.CRACKED) {
//we don't start a force login task here so update it manually
plugin.getPremiumPlayers().put(player.getUniqueId(), PremiumStatus.CRACKED);
}
}
private void onLoginMessage(Player player, String playerName, InetSocketAddress address) {
private void onLoginMessage(Player player, String playerName) {
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, true);
startLoginTaskIfReady(player, playerSession);
}
private void onRegisterMessage(Player player, String playerName, InetSocketAddress address) {
private void onRegisterMessage(Player player, String playerName) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
try {
@ -127,7 +124,7 @@ public class BungeeListener implements PluginMessageListener {
private void startLoginTaskIfReady(Player player, BukkitLoginSession session) {
session.setVerified(true);
plugin.putSession(player.getAddress(), session);
plugin.putSession(player.spigot().getRawAddress(), session);
// only start a new login task if the join event fired earlier. This event then didn't
boolean result = plugin.getBungeeManager().didJoinEventFired(player);

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -30,7 +30,6 @@ import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.task.FloodgateAuthTask;
import com.github.games647.fastlogin.bukkit.task.ForceLoginTask;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -80,7 +79,7 @@ public class ConnectionListener implements Listener {
// session exists so the player is ready for force login
// cases: Paper (firing BungeeCord message before PlayerJoinEvent) or not running BungeeCord and already
// having the login session from the login process
BukkitLoginSession session = plugin.getSession(player.getAddress());
BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress());
if (session == null) {
// Floodgate players usually don't have a session at this point
@ -95,8 +94,10 @@ public class ConnectionListener implements Listener {
}
}
String sessionId = plugin.getSessionId(player.getAddress());
plugin.getLog().info("No on-going login session for player: {} with ID {}", player, sessionId);
String sessionId = plugin.getSessionId(player.spigot().getRawAddress());
plugin.getLog().info("No on-going login session for player: {} with ID {}. ", player, sessionId);
plugin.getLog().info("Setups using Minecraft proxies will start delayed "
+ "when the command from the proxy is received");
} else {
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
Bukkit.getScheduler().runTaskAsynchronously(plugin, forceLoginTask);

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,34 +25,71 @@
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.common.io.Resources;
import com.google.common.primitives.Longs;
import lombok.val;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.time.Instant;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.UUID;
/**
* Encryption and decryption minecraft util for connection between servers
* and paid Minecraft account clients.
*
* @see net.minecraft.server.MinecraftEncryption
*/
public class EncryptionUtil {
final class EncryptionUtil {
public static final int VERIFY_TOKEN_LENGTH = 4;
public static final String KEY_PAIR_ALGORITHM = "RSA";
private static final int RSA_LENGTH = 2_048;
private static final PublicKey MOJANG_SESSION_KEY;
private static final int LINE_LENGTH = 76;
private static final Encoder KEY_ENCODER = Base64.getMimeEncoder(
LINE_LENGTH, "\n".getBytes(StandardCharsets.UTF_8)
);
private static final int MILLISECOND_SIZE = 8;
private static final int UUID_SIZE = 2 * MILLISECOND_SIZE;
static {
try {
MOJANG_SESSION_KEY = loadMojangSessionKey();
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException ex) {
throw new RuntimeException("Failed to load Mojang session key", ex);
}
}
private EncryptionUtil() {
// utility
throw new RuntimeException("No instantiation of utility classes allowed");
}
/**
@ -61,11 +98,10 @@ public class EncryptionUtil {
* @return The RSA key pair.
*/
public static KeyPair generateKeyPair() {
// KeyPair b()
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALGORITHM);
keyPairGenerator.initialize(1_024);
keyPairGenerator.initialize(RSA_LENGTH);
return keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException nosuchalgorithmexception) {
// Should be existing in every vm
@ -78,10 +114,9 @@ public 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(Random random) {
// extracted from LoginListener
byte[] token = new byte[VERIFY_TOKEN_LENGTH];
random.nextBytes(token);
return token;
@ -90,68 +125,102 @@ public class EncryptionUtil {
/**
* Generate the server id based on client and server data.
*
* @param sessionId session for the current login attempt
* @param serverId 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) {
// found in LoginListener
try {
byte[] serverHash = getServerIdHash(sessionId, publicKey, sharedSecret);
return (new BigInteger(serverHash)).toString(16);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
public static String getServerIdHashString(String serverId, SecretKey sharedSecret, PublicKey publicKey) {
byte[] serverHash = getServerIdHash(serverId, publicKey, sharedSecret);
return (new BigInteger(serverHash)).toString(16);
}
/**
* 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
*/
public static SecretKey decryptSharedKey(PrivateKey privateKey, byte[] sharedKey) throws GeneralSecurityException {
// SecretKey a(PrivateKey var0, byte[] var1)
public static SecretKey decryptSharedKey(PrivateKey privateKey, byte[] sharedKey)
throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
BadPaddingException, InvalidKeyException {
return new SecretKeySpec(decrypt(privateKey, sharedKey), "AES");
}
public static byte[] decrypt(PrivateKey key, byte[] data) throws GeneralSecurityException {
// b(Key var0, byte[] var1)
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, key);
return decrypt(cipher, data);
public static boolean verifyClientKey(ClientPublicKey clientKey, Instant verifyTimestamp, UUID premiumId)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
if (clientKey.isExpired(verifyTimestamp)) {
return false;
}
Signature verifier = Signature.getInstance("SHA1withRSA");
// key of the signer
verifier.initVerify(MOJANG_SESSION_KEY);
verifier.update(toSignable(clientKey, premiumId));
return verifier.verify(clientKey.signature());
}
/**
* Decrypted the given data using the cipher.
*
* @param cipher decryption cypher initialized with the private key
* @param data the encrypted data
* @return clear text data
* @throws GeneralSecurityException if it fails to decrypt the data
*/
private static byte[] decrypt(Cipher cipher, byte[] data) throws GeneralSecurityException {
// inlined: byte[] a(int var0, Key var1, byte[] var2), Cipher a(int var0, String var1, Key
// var2)
private static byte[] toSignable(ClientPublicKey clientPublicKey, UUID ownerPremiumId) {
if (ownerPremiumId == null) {
long expiry = clientPublicKey.expiry().toEpochMilli();
String encoded = KEY_ENCODER.encodeToString(clientPublicKey.key().getEncoded());
return (expiry + "-----BEGIN RSA PUBLIC KEY-----\n" + encoded + "\n-----END RSA PUBLIC KEY-----\n")
.getBytes(StandardCharsets.US_ASCII);
}
byte[] keyData = clientPublicKey.key().getEncoded();
return ByteBuffer.allocate(keyData.length + UUID_SIZE + MILLISECOND_SIZE)
.putLong(ownerPremiumId.getMostSignificantBits())
.putLong(ownerPremiumId.getLeastSignificantBits())
.putLong(clientPublicKey.expiry().toEpochMilli())
.put(keyData)
.array();
}
public static boolean verifyNonce(byte[] expected, PrivateKey decryptionKey, byte[] encryptedNonce)
throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
BadPaddingException, InvalidKeyException {
byte[] decryptedNonce = decrypt(decryptionKey, encryptedNonce);
return Arrays.equals(expected, decryptedNonce);
}
public static boolean verifySignedNonce(byte[] nonce, PublicKey clientKey, long signatureSalt, byte[] signature)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature verifier = Signature.getInstance("SHA256withRSA");
// key of the signer
verifier.initVerify(clientKey);
verifier.update(nonce);
verifier.update(Longs.toByteArray(signatureSalt));
return verifier.verify(signature);
}
private static PublicKey loadMojangSessionKey()
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
val keyUrl = FastLoginBukkit.class.getClassLoader().getResource("yggdrasil_session_pubkey.der");
val keyData = Resources.toByteArray(keyUrl);
val keySpec = new X509EncodedKeySpec(keyData);
return KeyFactory.getInstance("RSA").generatePublic(keySpec);
}
private static byte[] decrypt(PrivateKey key, byte[] data)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(data);
}
private static byte[] getServerIdHash(
String sessionId, PublicKey publicKey, SecretKey sharedSecret)
throws NoSuchAlgorithmException {
// byte[] a(String var0, PublicKey var1, SecretKey var2)
MessageDigest digest = MessageDigest.getInstance("SHA-1");
private static byte[] getServerIdHash(String sessionId, PublicKey publicKey, SecretKey sharedSecret) {
@SuppressWarnings("deprecation")
Hasher hasher = Hashing.sha1().newHasher();
// inlined from byte[] a(String var0, byte[]... var1)
digest.update(sessionId.getBytes(StandardCharsets.ISO_8859_1));
digest.update(sharedSecret.getEncoded());
digest.update(publicKey.getEncoded());
hasher.putBytes(sessionId.getBytes(StandardCharsets.ISO_8859_1));
hasher.putBytes(sharedSecret.getEncoded());
hasher.putBytes(publicKey.getEncoded());
return digest.digest();
return hasher.hash().asBytes();
}
}

View File

@ -1,85 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name 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.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import org.geysermc.floodgate.api.FloodgateApi;
import static com.comphenix.protocol.PacketType.Login.Client.START;
import com.comphenix.protocol.ProtocolLibrary;
/**
* Manually inject Floodgate player name prefixes.
* <br>
* This is used as a workaround, because Floodgate fails to inject
* the prefixes when it's used together with ProtocolLib and FastLogin.
* <br>
* For more information visit: https://github.com/games647/FastLogin/issues/493
*/
public class ManualNameChange extends PacketAdapter {
private final FloodgateService floodgate;
public ManualNameChange(FastLoginBukkit plugin, FloodgateService floodgate) {
super(params()
.plugin(plugin)
.types(START));
this.plugin = plugin;
this.floodgate = floodgate;
}
public static void register(FastLoginBukkit plugin, FloodgateService floodgate) {
// they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
ProtocolLibrary.getProtocolManager()
.getAsynchronousManager()
.registerAsyncHandler(new ManualNameChange(plugin, floodgate))
.start();
}
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
PacketContainer packet = packetEvent.getPacket();
WrappedGameProfile originalProfile = packet.getGameProfiles().read(0);
if (floodgate.getBedrockPlayer(originalProfile.getName()) == null) {
//not a Floodgate player, no need to add a prefix
return;
}
packet.setMeta("original_name", originalProfile.getName());
String prefixedName = FloodgateApi.getInstance().getPlayerPrefix() + originalProfile.getName();
WrappedGameProfile updatedProfile = originalProfile.withName(prefixedName);
packet.getGameProfiles().write(0, updatedProfile);
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -30,22 +30,24 @@ import com.comphenix.protocol.events.PacketEvent;
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.core.StoredProfile;
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import com.github.games647.fastlogin.core.shared.JoinManagement;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.security.PublicKey;
import java.util.Random;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class NameCheckTask extends JoinManagement<Player, CommandSender, ProtocolLibLoginSource>
implements Runnable {
implements Runnable {
private final FastLoginBukkit plugin;
private final PacketEvent packetEvent;
private final PublicKey publicKey;
private final ClientPublicKey clientKey;
private final PublicKey serverKey;
private final Random random;
@ -53,12 +55,13 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
private final String username;
public NameCheckTask(FastLoginBukkit plugin, Random random, Player player, PacketEvent packetEvent,
String username, PublicKey publicKey) {
String username, ClientPublicKey clientKey, PublicKey serverKey) {
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
this.plugin = plugin;
this.packetEvent = packetEvent;
this.publicKey = publicKey;
this.clientKey = clientKey;
this.serverKey = serverKey;
this.random = random;
this.player = player;
this.username = username;
@ -67,7 +70,7 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
@Override
public void run() {
try {
super.onLogin(username, new ProtocolLibLoginSource(player, random, publicKey));
super.onLogin(username, new ProtocolLibLoginSource(player, random, serverKey, clientKey));
} finally {
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
}
@ -84,8 +87,8 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
//Minecraft server implementation
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
@Override
public void requestPremiumLogin(ProtocolLibLoginSource source, StoredProfile profile
, String username, boolean registered) {
public void requestPremiumLogin(ProtocolLibLoginSource source, StoredProfile profile,
String username, boolean registered) {
try {
source.enableOnlinemode();
} catch (Exception ex) {
@ -97,8 +100,9 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
core.getPendingLogin().put(ip + username, new Object());
byte[] verify = source.getVerifyToken();
ClientPublicKey clientKey = source.getClientKey();
BukkitLoginSession playerSession = new BukkitLoginSession(username, verify, registered, profile);
BukkitLoginSession playerSession = new BukkitLoginSession(username, verify, clientKey, registered, profile);
plugin.putSession(player.getAddress(), playerSession);
//cancel only if the player has a paid account otherwise login as normal offline player
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -30,29 +30,59 @@ import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.Converters;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.RateLimiter;
import java.security.KeyPair;
import java.security.SecureRandom;
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import com.github.games647.fastlogin.core.antibot.AntiBotService;
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
import com.mojang.datafixers.util.Either;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.util.AttributeKey;
import lombok.val;
import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.net.InetSocketAddress;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
import static com.comphenix.protocol.PacketType.Login.Client.ENCRYPTION_BEGIN;
import static com.comphenix.protocol.PacketType.Login.Client.START;
public class ProtocolLibListener extends PacketAdapter {
public static final String SOURCE_META_KEY = "source";
private final FastLoginBukkit plugin;
private final PlayerInjectionHandler handler;
//just create a new once on plugin enable. This used for verify token generation
private final SecureRandom random = new SecureRandom();
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
private final RateLimiter rateLimiter;
private final AntiBotService antiBotService;
public ProtocolLibListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
private final boolean verifyClientKeys;
public ProtocolLibListener(FastLoginBukkit plugin, AntiBotService antiBotService, boolean verifyClientKeys) {
//run async in order to not block the server, because we are making api calls to Mojang
super(params()
.plugin(plugin)
@ -60,15 +90,17 @@ public class ProtocolLibListener extends PacketAdapter {
.optionAsync());
this.plugin = plugin;
this.rateLimiter = rateLimiter;
this.antiBotService = antiBotService;
this.verifyClientKeys = verifyClientKeys;
this.handler = getHandler();
}
public static void register(FastLoginBukkit plugin, RateLimiter rateLimiter) {
public static void register(FastLoginBukkit plugin, AntiBotService antiBotService, boolean verifyClientKeys) {
// they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
// TODO: make synchronous processing, but do web or database requests async
ProtocolLibrary.getProtocolManager()
.getAsynchronousManager()
.registerAsyncHandler(new ProtocolLibListener(plugin, rateLimiter))
.registerAsyncHandler(new ProtocolLibListener(plugin, antiBotService, verifyClientKeys))
.start();
}
@ -80,60 +112,218 @@ public class ProtocolLibListener extends PacketAdapter {
return;
}
if (isFastLoginPacket(packetEvent)) {
// this is our own packet
return;
}
plugin.getLog().info("New packet {} from {}", packetEvent.getPacketType(), packetEvent.getPlayer());
Player sender = packetEvent.getPlayer();
PacketType packetType = packetEvent.getPacketType();
if (packetType == START) {
if (!rateLimiter.tryAcquire()) {
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", sender);
return;
if (plugin.getFloodgateService() != null) {
boolean success = processFloodgateTasks(packetEvent);
// don't continue execution if the player was kicked by Floodgate
if (!success) {
return;
}
}
onLogin(packetEvent, sender);
PacketContainer packet = packetEvent.getPacket();
InetSocketAddress address = sender.getAddress();
String username = getUsername(packet);
Action action = antiBotService.onIncomingConnection(address, username);
switch (action) {
case Ignore:
// just ignore
return;
case Block:
String message = plugin.getCore().getMessage("kick-antibot");
sender.kickPlayer(message);
break;
case Continue:
default:
//player.getName() won't work at this state
onLoginStart(packetEvent, sender, username);
break;
}
} else {
onEncryptionBegin(packetEvent, sender);
}
}
private Boolean isFastLoginPacket(PacketEvent packetEvent) {
return packetEvent.getPacket().getMeta(SOURCE_META_KEY)
.map(val -> val.equals(plugin.getName()))
.orElse(false);
}
private void onEncryptionBegin(PacketEvent packetEvent, Player sender) {
byte[] sharedSecret = packetEvent.getPacket().getByteArrays().read(0);
packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, sharedSecret, keyPair);
plugin.getScheduler().runAsync(verifyTask);
BukkitLoginSession session = plugin.getSession(sender.getAddress());
if (session == null) {
plugin.getLog().warn("Profile {} tried to send encryption response at invalid state", sender.getAddress());
sender.kickPlayer(plugin.getCore().getMessage("invalid-request"));
} else {
byte[] expectedVerifyToken = session.getVerifyToken();
if (verifyNonce(sender, packetEvent.getPacket(), session.getClientPublicKey(), expectedVerifyToken)) {
packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable verifyTask = new VerifyResponseTask(
plugin, packetEvent, sender, session, sharedSecret, keyPair
);
plugin.getScheduler().runAsync(verifyTask);
} else {
sender.kickPlayer(plugin.getCore().getMessage("invalid-verify-token"));
}
}
}
private void onLogin(PacketEvent packetEvent, Player player) {
private boolean verifyNonce(Player sender, PacketContainer packet,
ClientPublicKey clientPublicKey, byte[] expectedToken) {
try {
if (new MinecraftVersion(1, 19, 0).atOrAbove()
&& !new MinecraftVersion(1, 19, 3).atOrAbove()) {
Either<byte[], ?> either = packet.getSpecificModifier(Either.class).read(0);
if (clientPublicKey == null) {
Optional<byte[]> left = either.left();
if (!left.isPresent()) {
plugin.getLog().error("No verify token sent if requested without player signed key {}", sender);
return false;
}
return EncryptionUtil.verifyNonce(expectedToken, keyPair.getPrivate(), left.get());
} else {
Optional<?> optSignatureData = either.right();
if (!optSignatureData.isPresent()) {
plugin.getLog().error("No signature given to sent player signing key {}", sender);
return false;
}
Object signatureData = optSignatureData.get();
long salt = FuzzyReflection.getFieldValue(signatureData, Long.TYPE, true);
byte[] signature = FuzzyReflection.getFieldValue(signatureData, byte[].class, true);
PublicKey publicKey = clientPublicKey.key();
return EncryptionUtil.verifySignedNonce(expectedToken, publicKey, salt, signature);
}
} else {
byte[] nonce = packet.getByteArrays().read(1);
return EncryptionUtil.verifyNonce(expectedToken, keyPair.getPrivate(), nonce);
}
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | NoSuchPaddingException
| IllegalBlockSizeException | BadPaddingException signatureEx) {
plugin.getLog().error("Invalid signature from player {}", sender, signatureEx);
return false;
}
}
private void onLoginStart(PacketEvent packetEvent, Player player, String username) {
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
String sessionKey = player.getAddress().toString();
//remove old data every time on a new login in order to keep the session only for one person
plugin.removeSession(player.getAddress());
//player.getName() won't work at this state
PacketContainer packet = packetEvent.getPacket();
Optional<ClientPublicKey> clientKey;
if (new MinecraftVersion(1, 19, 3).atOrAbove()) {
// public key sent separate
clientKey = Optional.empty();
} else {
val profileKey = packet.getOptionals(BukkitConverters.getWrappedPublicKeyDataConverter())
.optionRead(0);
String username = packet.getGameProfiles().read(0).getName();
clientKey = profileKey.flatMap(Function.identity()).flatMap(data -> {
Instant expires = data.getExpireTime();
PublicKey key = data.getKey();
byte[] signature = data.getSignature();
return Optional.of(ClientPublicKey.of(expires, key, signature));
});
if (packetEvent.getPacket().getMeta("original_name").isPresent()) {
//username has been injected by ManualNameChange.java
username = (String) packetEvent.getPacket().getMeta("original_name").get();
// start reading from index 1, because 0 is already used by the public key
Optional<UUID> sessionUUID = packet.getOptionals(Converters.passthrough(UUID.class)).readSafely(1);
if (verifyClientKeys && sessionUUID.isPresent() && clientKey.isPresent()
&& verifyPublicKey(clientKey.get(), sessionUUID.get())) {
// missing or incorrect
// expired always not allowed
player.kickPlayer(plugin.getCore().getMessage("invalid-public-key"));
plugin.getLog().warn("Invalid public key from player {}", username);
return;
}
}
plugin.getLog().trace("GameProfile {} with {} connecting", sessionKey, username);
packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable nameCheckTask = new NameCheckTask(plugin, random, player, packetEvent, username, keyPair.getPublic());
Runnable nameCheckTask = new NameCheckTask(
plugin, random, player, packetEvent, username, clientKey.orElse(null), keyPair.getPublic()
);
plugin.getScheduler().runAsync(nameCheckTask);
}
private boolean verifyPublicKey(ClientPublicKey clientKey, UUID sessionPremiumUUID) {
try {
return EncryptionUtil.verifyClientKey(clientKey, Instant.now(), sessionPremiumUUID);
} catch (SignatureException | InvalidKeyException | NoSuchAlgorithmException ex) {
return false;
}
}
private String getUsername(PacketContainer packet) {
WrappedGameProfile profile = packet.getGameProfiles().readSafely(0);
if (profile == null) {
return packet.getStrings().read(0);
}
//player.getName() won't work at this state
return profile.getName();
}
private static PlayerInjectionHandler getHandler() {
PacketFilterManager manager = (PacketFilterManager) ProtocolLibrary.getProtocolManager();
FieldAccessor accessor = Accessors.getFieldAccessor(manager.getClass(), PlayerInjectionHandler.class, true);
return (PlayerInjectionHandler) accessor.get(manager);
}
private FloodgatePlayer getFloodgatePlayer(Player player) {
Channel channel = handler.getChannel(player);
AttributeKey<FloodgatePlayer> floodgateAttribute = AttributeKey.valueOf("floodgate-player");
return channel.attr(floodgateAttribute).get();
}
/**
* Reimplementation of the tasks injected Floodgate in ProtocolLib that are not run due to a bug
* @see <a href="https://github.com/GeyserMC/Floodgate/issues/143">Issue Floodgate#143</a>
* @see <a href="https://github.com/GeyserMC/Floodgate/blob/5d5713ed9e9eeab0f4abdaa9cf5cd8619dc1909b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java#L121-L175">Floodgate/SpigotDataHandler</a>
* @param packetEvent the PacketEvent that won't be processed by Floodgate
* @return false if the player was kicked
*/
private boolean processFloodgateTasks(PacketEvent packetEvent) {
PacketContainer packet = packetEvent.getPacket();
Player player = packetEvent.getPlayer();
FloodgatePlayer floodgatePlayer = getFloodgatePlayer(player);
if (floodgatePlayer == null) {
return true;
}
// kick the player, if necessary
Channel channel = handler.getChannel(packetEvent.getPlayer());
AttributeKey<String> kickMessageAttribute = AttributeKey.valueOf("floodgate-kick-message");
String kickMessage = channel.attr(kickMessageAttribute).get();
if (kickMessage != null) {
player.kickPlayer(kickMessage);
return false;
}
// add prefix
String username = floodgatePlayer.getCorrectUsername();
if (packet.getGameProfiles().size() > 0) {
packet.getGameProfiles().write(0,
new WrappedGameProfile(floodgatePlayer.getCorrectUniqueId(), username));
} else {
packet.getStrings().write(0, username);
}
// remove real Floodgate data handler
ChannelHandler floodgateHandler = channel.pipeline().get("floodgate_data_handler");
channel.pipeline().remove(floodgateHandler);
return true;
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -30,16 +30,15 @@ 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.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import com.github.games647.fastlogin.core.shared.LoginSource;
import org.bukkit.entity.Player;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Random;
import org.bukkit.entity.Player;
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
import static com.comphenix.protocol.PacketType.Login.Server.ENCRYPTION_BEGIN;
@ -48,19 +47,22 @@ class ProtocolLibLoginSource implements LoginSource {
private final Player player;
private final Random random;
private final ClientPublicKey clientKey;
private final PublicKey publicKey;
private final String serverId = "";
private byte[] verifyToken;
public ProtocolLibLoginSource(Player player, Random random, PublicKey publicKey) {
ProtocolLibLoginSource(Player player, Random random, PublicKey serverPublicKey, ClientPublicKey clientKey) {
this.player = player;
this.random = random;
this.publicKey = publicKey;
this.publicKey = serverPublicKey;
this.clientKey = clientKey;
}
@Override
public void enableOnlinemode() throws InvocationTargetException {
public void enableOnlinemode() {
verifyToken = EncryptionUtil.generateVerifyToken(random);
/*
@ -88,7 +90,7 @@ class ProtocolLibLoginSource implements LoginSource {
}
@Override
public void kick(String message) throws InvocationTargetException {
public void kick(String message) {
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
@ -109,6 +111,10 @@ class ProtocolLibLoginSource implements LoginSource {
return player.getAddress();
}
public ClientPublicKey getClientKey() {
return clientKey;
}
public String getServerId() {
return serverId;
}
@ -119,11 +125,11 @@ class ProtocolLibLoginSource implements LoginSource {
@Override
public String toString() {
return this.getClass().getSimpleName() + '{' +
"player=" + player +
", random=" + random +
", serverId='" + serverId + '\'' +
", verifyToken=" + Arrays.toString(verifyToken) +
'}';
return this.getClass().getSimpleName() + '{'
+ "player=" + player
+ ", random=" + random
+ ", serverId='" + serverId + '\''
+ ", verifyToken=" + Arrays.toString(verifyToken)
+ '}';
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,10 +25,6 @@
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.reflect.MethodUtils;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
import com.github.games647.craftapi.model.skin.Textures;
@ -41,13 +37,8 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import java.lang.reflect.InvocationTargetException;
public class SkinApplyListener implements Listener {
private static final Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
private static final MethodAccessor GET_PROPERTIES = Accessors.getMethodAccessor(GAME_PROFILE, "getProperties");
private final FastLoginBukkit plugin;
public SkinApplyListener(FastLoginBukkit plugin) {
@ -77,16 +68,6 @@ public class SkinApplyListener implements Listener {
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
WrappedSignedProperty skin = WrappedSignedProperty.fromValues(Textures.KEY, skinData, signature);
try {
gameProfile.getProperties().put(Textures.KEY, skin);
} catch (ClassCastException castException) {
//Cauldron, MCPC, Thermos, ...
Object map = GET_PROPERTIES.invoke(gameProfile.getHandle());
try {
MethodUtils.invokeMethod(map, "put", new Object[]{Textures.KEY, skin.getHandle()});
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
plugin.getLog().error("Error setting premium skin of: {}", player, ex);
}
}
gameProfile.getProperties().put(Textures.KEY, skin);
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -28,20 +28,33 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.Converters;
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;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.InetUtils;
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import lombok.val;
import org.bukkit.entity.Player;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@ -53,11 +66,6 @@ import java.util.Arrays;
import java.util.Optional;
import java.util.UUID;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import org.bukkit.entity.Player;
import static com.comphenix.protocol.PacketType.Login.Client.START;
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
@ -65,9 +73,18 @@ public class VerifyResponseTask implements Runnable {
private static final String ENCRYPTION_CLASS_NAME = "MinecraftEncryption";
private static final Class<?> ENCRYPTION_CLASS;
private static final String ADDRESS_VERIFY_WARNING = "This indicates the use of reverse-proxy like HAProxy, "
+ "TCPShield, BungeeCord, Velocity, etc. "
+ "By default (configurable in the config) this plugin requests Mojang to verify the connecting IP "
+ "to this server with the one used to log into Minecraft to prevent MITM attacks. In "
+ "order to work this security feature, the actual client IP needs to be forwarding "
+ "(keyword IP forwarding). This process will also be useful for other server "
+ "features like IP banning, so that it doesn't ban the proxy IP.";
static {
ENCRYPTION_CLASS = MinecraftReflection.getMinecraftClass("util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME);
ENCRYPTION_CLASS = MinecraftReflection.getMinecraftClass(
"util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME
);
}
private final FastLoginBukkit plugin;
@ -76,16 +93,20 @@ public class VerifyResponseTask implements Runnable {
private final Player player;
private final BukkitLoginSession session;
private final byte[] sharedSecret;
private static Method encryptMethod;
private static Method cipherMethod;
public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent, Player player,
public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent,
Player player, BukkitLoginSession session,
byte[] sharedSecret, KeyPair keyPair) {
this.plugin = plugin;
this.packetEvent = packetEvent;
this.player = player;
this.session = session;
this.sharedSecret = Arrays.copyOf(sharedSecret, sharedSecret.length);
this.serverKey = keyPair;
}
@ -93,13 +114,7 @@ public class VerifyResponseTask implements Runnable {
@Override
public void run() {
try {
BukkitLoginSession session = plugin.getSession(player.getAddress());
if (session == null) {
disconnect("invalid-request", true
, "GameProfile {0} tried to send encryption response at invalid state", player.getAddress());
} else {
verifyResponse(session);
}
verifyResponse(session);
} finally {
//this is a fake packet; it shouldn't be sent to the server
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
@ -117,16 +132,16 @@ public class VerifyResponseTask implements Runnable {
try {
loginKey = EncryptionUtil.decryptSharedKey(privateKey, sharedSecret);
} catch (GeneralSecurityException securityEx) {
disconnect("error-kick", false, "Cannot decrypt received contents", securityEx);
disconnect("error-kick", "Cannot decrypt received contents", securityEx);
return;
}
try {
if (!checkVerifyToken(session) || !enableEncryption(loginKey)) {
if (!enableEncryption(loginKey)) {
return;
}
} catch (Exception ex) {
disconnect("error-kick", false, "Cannot decrypt received contents", ex);
disconnect("error-kick", "Cannot decrypt received contents", ex);
return;
}
@ -139,77 +154,88 @@ public class VerifyResponseTask implements Runnable {
InetAddress address = socketAddress.getAddress();
Optional<Verification> response = resolver.hasJoined(requestedUsername, serverId, address);
if (response.isPresent()) {
Verification verification = response.get();
plugin.getLog().info("Profile {} has a verified premium account", requestedUsername);
String realUsername = verification.getName();
if (realUsername == null) {
disconnect("invalid-session", true, "Username field null for {}", requestedUsername);
return;
}
SkinProperty[] properties = verification.getProperties();
if (properties.length > 0) {
session.setSkinProperty(properties[0]);
}
session.setVerifiedUsername(realUsername);
session.setUuid(verification.getId());
session.setVerified(true);
setPremiumUUID(session.getUuid());
receiveFakeStartPacket(realUsername);
encryptConnection(session, requestedUsername, response.get());
} else {
//user tried to fake an authentication
disconnect("invalid-session", true
, "GameProfile {0} ({1}) tried to log in with an invalid session ServerId: {2}"
, session.getRequestUsername(), socketAddress, serverId);
disconnect(
"invalid-session",
"Session server rejected incoming connection for GameProfile {} ({}). Possible reasons are"
+ "1) Client IP address contacting Mojang and server during server join were different "
+ "(Do you use a reverse proxy? -> Enable IP forwarding, "
+ "or disable the feature in the config). "
+ "2) Player is offline, but tried to bypass the authentication"
+ "3) Client uses an outdated username for connecting (Fix: Restart client)",
requestedUsername, address
);
if (InetUtils.isLocalAddress(address)) {
plugin.getLog().warn(
"The incoming request for player {} uses a local IP address",
requestedUsername
);
plugin.getLog().warn(ADDRESS_VERIFY_WARNING);
} else {
plugin.getLog().warn("If you think this is an error, please verify that the incoming "
+ "IP address {} is not associated with a server hosting company.", address);
plugin.getLog().warn(ADDRESS_VERIFY_WARNING);
}
}
} catch (IOException ioEx) {
disconnect("error-kick", false, "Failed to connect to session server", ioEx);
disconnect("error-kick", "Failed to connect to session server", ioEx);
}
}
private void encryptConnection(BukkitLoginSession session, String requestedUsername, Verification verification) {
plugin.getLog().info("Profile {} has a verified premium account", requestedUsername);
String realUsername = verification.getName();
if (realUsername == null) {
disconnect("invalid-session", "Username field null for {}", requestedUsername);
return;
}
SkinProperty[] properties = verification.getProperties();
if (properties.length > 0) {
session.setSkinProperty(properties[0]);
}
session.setVerifiedUsername(realUsername);
session.setUuid(verification.getId());
session.setVerified(true);
setPremiumUUID(session.getUuid());
receiveFakeStartPacket(realUsername, session.getClientPublicKey(), session.getUuid());
}
private void setPremiumUUID(UUID premiumUUID) {
if (plugin.getConfig().getBoolean("premiumUuid") && premiumUUID != null) {
try {
Object networkManager = getNetworkManager();
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/NetworkManager.java#L69
FieldUtils.writeField(networkManager, "spoofedUUID", premiumUUID, true);
Class<?> managerClass = networkManager.getClass();
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(managerClass, "spoofedUUID", UUID.class);
accessor.set(networkManager, premiumUUID);
} catch (Exception exc) {
plugin.getLog().error("Error setting premium uuid of {}", player, exc);
}
}
}
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", true
, "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 {
private Object getNetworkManager() throws ClassNotFoundException {
Object injectorContainer = TemporaryPlayerFactory.getInjectorFromPlayer(player);
// ChannelInjector
Class<?> injectorClass = Class.forName("com.comphenix.protocol.injector.netty.Injector");
Object rawInjector = FuzzyReflection.getFieldValue(injectorContainer, injectorClass, true);
return FieldUtils.readField(rawInjector, "networkManager", true);
Class<?> rawInjectorClass = rawInjector.getClass();
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(rawInjectorClass, "networkManager", Object.class);
return accessor.get(rawInjector);
}
private boolean enableEncryption(SecretKey loginKey) throws IllegalArgumentException {
plugin.getLog().info("Enabling onlinemode encryption for {}", player.getName());
plugin.getLog().info("Enabling onlinemode encryption for {}", player.getAddress());
// Initialize method reflections
if (encryptMethod == null) {
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
@ -245,53 +271,60 @@ public class VerifyResponseTask implements Runnable {
encryptMethod.invoke(networkManager, decryptionCipher, encryptionCipher);
}
} catch (Exception ex) {
disconnect("error-kick", false, "Couldn't enable encryption", ex);
disconnect("error-kick", "Couldn't enable encryption", ex);
return false;
}
return true;
}
private void disconnect(String reasonKey, boolean debug, String logMessage, Object... arguments) {
if (debug) {
plugin.getLog().debug(logMessage, arguments);
} else {
plugin.getLog().error(logMessage, arguments);
}
private void disconnect(String reasonKey, String logMessage, Object... arguments) {
plugin.getLog().error(logMessage, arguments);
kickPlayer(plugin.getCore().getMessage(reasonKey));
}
private void kickPlayer(String reason) {
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(reason));
try {
//send kick packet at login state
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
ProtocolLibrary.getProtocolManager().sendServerPacket(player, kickPacket);
//tell the server that we want to close the connection
player.kickPlayer("Disconnect");
} catch (InvocationTargetException ex) {
plugin.getLog().error("Error sending kick packet for: {}", player, ex);
}
//send kick packet at login state
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
ProtocolLibrary.getProtocolManager().sendServerPacket(player, kickPacket);
//tell the server that we want to close the connection
player.kickPlayer("Disconnect");
}
//fake a new login packet in order to let the server handle all the other stuff
private void receiveFakeStartPacket(String username) {
//see StartPacketListener for packet information
PacketContainer startPacket = new PacketContainer(START);
private void receiveFakeStartPacket(String username, ClientPublicKey clientKey, UUID uuid) {
PacketContainer startPacket;
if (new MinecraftVersion(1, 20, 2).atOrAbove()) {
startPacket = new PacketContainer(START);
startPacket.getStrings().write(0, username);
startPacket.getUUIDs().write(0, uuid);
} else if (new MinecraftVersion(1, 19, 3).atOrAbove()) {
startPacket = new PacketContainer(START);
startPacket.getStrings().write(0, username);
startPacket.getOptionals(Converters.passthrough(UUID.class)).write(0, Optional.of(uuid));
} else if (new MinecraftVersion(1, 19, 0).atOrAbove()) {
startPacket = new PacketContainer(START);
startPacket.getStrings().write(0, username);
//uuid is ignored by the packet definition
WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
startPacket.getGameProfiles().write(0, fakeProfile);
try {
//we don't want to handle our own packets so ignore filters
startPacket.setMeta(ProtocolLibListener.SOURCE_META_KEY, plugin.getName());
ProtocolLibrary.getProtocolManager().recieveClientPacket(player, startPacket, true);
} catch (InvocationTargetException | IllegalAccessException ex) {
plugin.getLog().warn("Failed to fake a new start packet for: {}", username, ex);
//cancel the event in order to prevent the server receiving an invalid packet
kickPlayer(plugin.getCore().getMessage("error-kick"));
EquivalentConverter<WrappedProfileKeyData> converter = BukkitConverters.getWrappedPublicKeyDataConverter();
val wrappedKey = Optional.ofNullable(clientKey).map(key ->
new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature())
);
startPacket.getOptionals(converter).write(0, wrappedKey);
} else {
//uuid is ignored by the packet definition
WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
Class<?> profileHandleType = fakeProfile.getHandleType();
Class<?> packetHandleType = PacketRegistry.getPacketClassFromType(START);
ConstructorAccessor startCons = Accessors.getConstructorAccessorOrNull(packetHandleType, profileHandleType);
startPacket = new PacketContainer(START, startCons.invoke(fakeProfile.getHandle()));
}
//we don't want to handle our own packets so ignore filters
ProtocolLibrary.getProtocolManager().receiveClientPacket(player, startPacket, false);
}
}

View File

@ -0,0 +1,55 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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.packet;
import lombok.Value;
import lombok.experimental.Accessors;
import java.security.PublicKey;
import java.time.Instant;
import java.util.Base64;
import java.util.StringJoiner;
@Accessors(fluent = true)
@Value(staticConstructor = "of")
public class ClientPublicKey {
Instant expiry;
PublicKey key;
byte[] signature;
public boolean isExpired(Instant verifyTimestamp) {
return !verifyTimestamp.isBefore(expiry);
}
@Override
public String toString() {
return new StringJoiner(", ", ClientPublicKey.class.getSimpleName() + '[', "]")
.add("expiry=" + expiry)
.add("key=" + Base64.getEncoder().encodeToString(key.getEncoded()))
.add("signature=" + Base64.getEncoder().encodeToString(signature))
.toString();
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -26,11 +26,10 @@
package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
import com.github.games647.fastlogin.core.shared.LoginSource;
import protocolsupport.api.events.PlayerLoginStartEvent;
import java.net.InetSocketAddress;
import protocolsupport.api.events.PlayerLoginStartEvent;
public class ProtocolLoginSource implements LoginSource {
private final PlayerLoginStartEvent loginStartEvent;
@ -51,7 +50,7 @@ public class ProtocolLoginSource implements LoginSource {
@Override
public InetSocketAddress getAddress() {
return loginStartEvent.getAddress();
return loginStartEvent.getConnection().getRawAddress();
}
public PlayerLoginStartEvent getLoginStartEvent() {
@ -60,8 +59,8 @@ public class ProtocolLoginSource implements LoginSource {
@Override
public String toString() {
return this.getClass().getSimpleName() + '{' +
"loginStartEvent=" + loginStartEvent +
'}';
return this.getClass().getSimpleName() + '{'
+ "loginStartEvent=" + loginStartEvent
+ '}';
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -29,14 +29,11 @@ import com.github.games647.craftapi.UUIDAdapter;
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.core.RateLimiter;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.antibot.AntiBotService;
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
import com.github.games647.fastlogin.core.shared.JoinManagement;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import java.net.InetSocketAddress;
import java.util.Optional;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -45,17 +42,20 @@ import protocolsupport.api.events.ConnectionCloseEvent;
import protocolsupport.api.events.PlayerLoginStartEvent;
import protocolsupport.api.events.PlayerProfileCompleteEvent;
import java.net.InetSocketAddress;
import java.util.Optional;
public class ProtocolSupportListener extends JoinManagement<Player, CommandSender, ProtocolLoginSource>
implements Listener {
private final FastLoginBukkit plugin;
private final RateLimiter rateLimiter;
private final AntiBotService antiBotService;
public ProtocolSupportListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
public ProtocolSupportListener(FastLoginBukkit plugin, AntiBotService antiBotService) {
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
this.plugin = plugin;
this.rateLimiter = rateLimiter;
this.antiBotService = antiBotService;
}
@EventHandler
@ -64,30 +64,40 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
return;
}
if (!rateLimiter.tryAcquire()) {
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", loginStartEvent.getConnection());
return;
}
String username = loginStartEvent.getConnection().getProfile().getName();
InetSocketAddress address = loginStartEvent.getAddress();
InetSocketAddress address = loginStartEvent.getConnection().getRawAddress();
plugin.getLog().info("Incoming login request for {} from {}", username, address);
//remove old data every time on a new login in order to keep the session only for one person
plugin.removeSession(address);
Action action = antiBotService.onIncomingConnection(address, username);
switch (action) {
case Ignore:
// just ignore
return;
case Block:
String message = plugin.getCore().getMessage("kick-antibot");
loginStartEvent.denyLogin(message);
break;
case Continue:
default:
//remove old data every time on a new login in order to keep the session only for one person
plugin.removeSession(address);
ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent);
super.onLogin(username, source);
ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent);
super.onLogin(username, source);
break;
}
}
@EventHandler
public void onConnectionClosed(ConnectionCloseEvent closeEvent) {
InetSocketAddress address = closeEvent.getConnection().getAddress();
InetSocketAddress address = closeEvent.getConnection().getRawAddress();
plugin.removeSession(address);
}
@EventHandler
public void onPropertiesResolve(PlayerProfileCompleteEvent profileCompleteEvent) {
InetSocketAddress address = profileCompleteEvent.getAddress();
InetSocketAddress address = profileCompleteEvent.getConnection().getRawAddress();
BukkitLoginSession session = plugin.getSession(address);
if (session != null && profileCompleteEvent.getConnection().getProfile().isOnlineMode()) {
@ -102,15 +112,16 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
}
@Override
public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, ProtocolLoginSource source, StoredProfile profile) {
public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, ProtocolLoginSource source,
StoredProfile profile) {
BukkitFastLoginPreLoginEvent event = new BukkitFastLoginPreLoginEvent(username, source, profile);
plugin.getServer().getPluginManager().callEvent(event);
return event;
}
@Override
public void requestPremiumLogin(ProtocolLoginSource source, StoredProfile profile, String username
, boolean registered) {
public void requestPremiumLogin(ProtocolLoginSource source, StoredProfile profile, String username,
boolean registered) {
source.enableOnlinemode();
String ip = source.getAddress().getAddress().getHostAddress();

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -31,18 +31,16 @@ import com.github.games647.fastlogin.bukkit.hook.CrazyLoginHook;
import com.github.games647.fastlogin.bukkit.hook.LogItHook;
import com.github.games647.fastlogin.bukkit.hook.LoginSecurityHook;
import com.github.games647.fastlogin.bukkit.hook.UltraAuthHook;
import com.github.games647.fastlogin.bukkit.hook.xAuthHook;
import com.github.games647.fastlogin.bukkit.hook.SodionAuthHook;
import com.github.games647.fastlogin.bukkit.hook.XAuthHook;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
public class DelayedAuthHook implements Runnable {
private final FastLoginBukkit plugin;
@ -95,8 +93,8 @@ public class DelayedAuthHook implements Runnable {
private AuthPlugin<Player> getAuthHook() {
try {
List<Class<? extends AuthPlugin<Player>>> hooks = Arrays.asList(AuthMeHook.class,
CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class,
SodionAuthHook.class, UltraAuthHook.class, xAuthHook.class);
CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class, UltraAuthHook.class,
XAuthHook.class);
for (Class<? extends AuthPlugin<Player>> clazz : hooks) {
String pluginName = clazz.getSimpleName();
@ -114,7 +112,7 @@ public class DelayedAuthHook implements Runnable {
return null;
}
private AuthPlugin<Player> newInstance(Class<? extends AuthPlugin<Player>> clazz)
protected AuthPlugin<Player> newInstance(Class<? extends AuthPlugin<Player>> clazz)
throws ReflectiveOperationException {
try {
Constructor<? extends AuthPlugin<Player>> cons = clazz.getDeclaredConstructor(FastLoginBukkit.class);

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,22 +25,22 @@
*/
package com.github.games647.fastlogin.bukkit.task;
import java.net.InetSocketAddress;
import java.util.UUID;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.FloodgateManagement;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.FloodgateManagement;
import java.net.InetSocketAddress;
import java.util.UUID;
public class FloodgateAuthTask extends FloodgateManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
public FloodgateAuthTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player, FloodgatePlayer floodgatePlayer) {
public FloodgateAuthTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player,
FloodgatePlayer floodgatePlayer) {
super(core, player, floodgatePlayer);
}
@ -49,8 +49,7 @@ public class FloodgateAuthTask extends FloodgateManagement<Player, CommandSender
BukkitLoginSession session = new BukkitLoginSession(player.getName(), isRegistered, profile);
// enable auto login based on the value of 'autoLoginFloodgate' in config.yml
session.setVerified(autoLoginFloodgate.equals("true")
|| (autoLoginFloodgate.equals("linked") && isLinked));
session.setVerified(isAutoAuthAllowed(autoLoginFloodgate));
// run login task
Runnable forceLoginTask = new ForceLoginTask(core.getPlugin().getCore(), player, session);

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -29,20 +29,19 @@ import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginAutoLoginEvent;
import com.github.games647.fastlogin.core.PremiumStatus;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.message.SuccessMessage;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
import java.util.concurrent.ExecutionException;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
import java.util.concurrent.ExecutionException;
public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
public ForceLoginTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player,

Binary file not shown.

View File

@ -0,0 +1,48 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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;
import com.github.games647.fastlogin.core.CommonUtil;
import lombok.val;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class FastLoginBukkitTest {
@Test
void testRGB() {
val message = "&x00002a00002b&lText";
val msg = CommonUtil.translateColorCodes(message);
assertEquals(msg, "§x00002a00002b§lText");
val components = TextComponent.fromLegacyText(msg);
val expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
assertEquals(ComponentSerializer.toString(components), expected);
}
}

View File

@ -0,0 +1,43 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import fr.xephi.authme.api.v3.AuthMeApi;
import fr.xephi.authme.process.Management;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class ReflectionTest {
@Test
void testAuthMeManagementField() {
FieldAccessor accessor = Accessors.getFieldAccessor(AuthMeApi.class, Management.class, true);
assertNotNull(accessor.getField());
}
}

View File

@ -0,0 +1,44 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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.hook;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import de.st_ddt.crazylogin.CrazyLogin;
import de.st_ddt.crazylogin.listener.PlayerListener;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class CrazyLoginHookTest {
@Test
void testPlayerListener() {
FieldAccessor accessor = Accessors.getFieldAccessor(CrazyLogin.class, PlayerListener.class, true);
assertNotNull(accessor.getField());
}
}

View File

@ -0,0 +1,49 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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 lombok.val;
import java.io.IOException;
import java.util.Base64;
public class Base64Adapter extends TypeAdapter<byte[]> {
@Override
public void write(JsonWriter out, byte[] value) throws IOException {
val 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);
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,22 +25,274 @@
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import java.security.SecureRandom;
import com.github.games647.fastlogin.bukkit.listener.protocollib.SignatureTestData.SignatureData;
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import com.google.common.hash.Hashing;
import lombok.val;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.Test;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.interfaces.RSAPublicKey;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
public class EncryptionUtilTest {
class EncryptionUtilTest {
@Test
public void testVerifyToken() {
SecureRandom random = new SecureRandom();
void testVerifyToken() {
val random = ThreadLocalRandom.current();
byte[] token = EncryptionUtil.generateVerifyToken(random);
assertThat(token, notNullValue());
assertThat(token.length, is(4));
assertAll(
() -> assertNotNull(token),
() -> assertEquals(token.length, 4)
);
}
@Test
void testServerKey() {
KeyPair keyPair = EncryptionUtil.generateKeyPair();
Key privateKey = keyPair.getPrivate();
assertEquals(privateKey.getAlgorithm(), "RSA");
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
assertEquals(publicKey.getAlgorithm(), "RSA");
// clients accept larger values than the standard vanilla server, but we shouldn't crash them
assertAll(
() -> assertTrue(publicKey.getModulus().bitLength() >= 1024),
() -> assertTrue(publicKey.getModulus().bitLength() < 8192)
);
}
@Test
void testExpiredClientKey() throws Exception {
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
// Client expires at the exact second mentioned, so use it for verification
val expiredTimestamp = clientKey.expiry();
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp, null));
}
@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 {
val clientKey = ResourceLoader.loadClientKey(clientKeySource);
Instant expireTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp, null));
}
@Test
void testValidClientKey() throws Exception {
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, null));
}
@Test
void testValid191ClientKey() throws Exception {
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key_19_1.json");
val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
val ownerPremiumId = UUID.fromString("0aaa2c13-922a-411b-b655-9b8c08404695");
assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, ownerPremiumId));
}
@Test
void testIncorrect191ClientOwner() throws Exception {
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key_19_1.json");
val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
val ownerPremiumId = UUID.fromString("61699b2e-d327-4a01-9f1e-0ea8c3f06bc6");
assertFalse(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, ownerPremiumId));
}
@Test
void testDecryptSharedSecret() throws Exception {
KeyPair serverPair = EncryptionUtil.generateKeyPair();
val serverPK = serverPair.getPublic();
SecretKey secretKey = generateSharedKey();
byte[] encryptedSecret = encrypt(serverPK, secretKey.getEncoded());
SecretKey decryptSharedKey = EncryptionUtil.decryptSharedKey(serverPair.getPrivate(), encryptedSecret);
assertEquals(decryptSharedKey, secretKey);
}
private static byte[] encrypt(PublicKey receiverKey, byte... message)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException {
val encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm());
encryptCipher.init(Cipher.ENCRYPT_MODE, receiverKey);
return encryptCipher.doFinal(message);
}
private static SecretKeySpec generateSharedKey() {
// according to wiki.vg 16 bytes long
byte[] sharedKey = new byte[16];
ThreadLocalRandom.current().nextBytes(sharedKey);
// shared key is to be used for the AES/CFB8 stream cipher to encrypt the traffic
// therefore the encryption/decryption has to be AES
return new SecretKeySpec(sharedKey, "AES");
}
@Test
void testServerIdHash() throws Exception {
val serverId = "";
val sharedSecret = generateSharedKey();
val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
assertEquals(EncryptionUtil.getServerIdHashString(serverId, sharedSecret, serverPK), sessionHash);
}
private static String getServerHash(@SuppressWarnings("SameParameterValue") CharSequence serverId,
SecretKey sharedSecret, PublicKey serverPK) {
// https://wiki.vg/Protocol_Encryption#Client
// sha1 := Sha1()
// sha1.update(ASCII encoding of the server id string from Encryption Request)
// sha1.update(shared secret)
// sha1.update(server's encoded public key from Encryption Request)
// hash := sha1.hexdigest() # String of hex characters
@SuppressWarnings("deprecation")
val hasher = Hashing.sha1().newHasher();
hasher.putString(serverId, StandardCharsets.US_ASCII);
hasher.putBytes(sharedSecret.getEncoded());
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
return new BigInteger(hasher.hash().asBytes()).toString(16);
}
@Test
void testServerIdHashWrongSecret() throws Exception {
val serverId = "";
val sharedSecret = generateSharedKey();
val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
assertNotEquals(EncryptionUtil.getServerIdHashString("", generateSharedKey(), serverPK), sessionHash);
}
@Test
void testServerIdHashWrongServerKey() {
val serverId = "";
val sharedSecret = generateSharedKey();
val serverPK = EncryptionUtil.generateKeyPair().getPublic();
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
val wrongPK = EncryptionUtil.generateKeyPair().getPublic();
assertNotEquals(EncryptionUtil.getServerIdHashString("", sharedSecret, wrongPK), sessionHash);
}
@Test
void testValidSignedNonce() throws Exception {
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
SignatureTestData testData = SignatureTestData.fromResource("signature/valid_signature.json");
assertTrue(verifySignedNonce(testData, clientKey));
}
@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(signatureSource);
assertFalse(verifySignedNonce(testData, clientKey));
}
@Test
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");
assertFalse(verifySignedNonce(testData, clientKey));
}
private static boolean verifySignedNonce(SignatureTestData testData, ClientPublicKey clientKey)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
PublicKey clientPublicKey = clientKey.key();
byte[] nonce = testData.getNonce();
SignatureData signature = testData.getSignature();
long salt = signature.getSalt();
return EncryptionUtil.verifySignedNonce(nonce, clientPublicKey, salt, signature.getSignature());
}
@Test
void testNonce() throws Exception {
byte[] expected = {1, 2, 3, 4};
val serverKey = EncryptionUtil.generateKeyPair();
val encryptedNonce = encrypt(serverKey.getPublic(), expected);
assertTrue(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
}
@Test
void testNonceIncorrect() throws Exception {
byte[] expected = {1, 2, 3, 4};
val serverKey = EncryptionUtil.generateKeyPair();
// flipped first character
val encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4});
assertFalse(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
}
@Test
void testNonceFailedDecryption() throws Exception {
byte[] expected = {1, 2, 3, 4};
val serverKey = EncryptionUtil.generateKeyPair();
// generate a new keypair that is different
val encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected);
assertThrows(GeneralSecurityException.class,
() -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)
);
}
@Test
void testNonceIncorrectEmpty() {
byte[] expected = {1, 2, 3, 4};
val serverKey = EncryptionUtil.generateKeyPair();
byte[] encryptedNonce = {};
assertThrows(GeneralSecurityException.class,
() -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)
);
}
}

View File

@ -0,0 +1,97 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import com.google.common.io.Resources;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.Instant;
import java.util.Base64;
public class ResourceLoader {
public static 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();
KeySpec privateKeySpec = new PKCS8EncodedKeySpec(content);
KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) factory.generatePrivate(privateKeySpec);
}
}
protected static ClientPublicKey loadClientKey(String path)
throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
URL keyUrl = Resources.getResource(path);
String lines = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
JsonObject object = new Gson().fromJson(lines, JsonObject.class);
Instant expires = Instant.parse(object.getAsJsonPrimitive("expires_at").getAsString());
String key = object.getAsJsonPrimitive("key").getAsString();
RSAPublicKey publicKey = parsePublicKey(key);
byte[] signature = Base64.getDecoder().decode(object.getAsJsonPrimitive("signature").getAsString());
return ClientPublicKey.of(expires, publicKey, signature);
}
private static RSAPublicKey parsePublicKey(String keySpec)
throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
try (
Reader reader = new StringReader(keySpec);
PemReader pemReader = new PemReader(reader)
) {
PemObject pemObject = pemReader.readPemObject();
byte[] content = pemObject.getContent();
KeySpec pubKeySpec = new X509EncodedKeySpec(content);
KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPublicKey) factory.generatePublic(pubKeySpec);
}
}
}

View File

@ -0,0 +1,73 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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.common.io.Resources;
import com.google.gson.Gson;
import com.google.gson.annotations.JsonAdapter;
import lombok.val;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class SignatureTestData {
public static SignatureTestData fromResource(String resourceName) throws IOException {
val keyUrl = Resources.getResource(resourceName);
val encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
return new Gson().fromJson(encodedSignature, SignatureTestData.class);
}
@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;
}
}
}

View File

@ -0,0 +1,66 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.Bukkit;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;
class VerifyResponseTaskTest {
private static final String NETTY_INJECTOR_CLASS =
"com.comphenix.protocol.injector.netty.channel.NettyChannelInjector";
@Test
void getNetworkManagerReflection() throws ClassNotFoundException {
try (
MockedStatic<Bukkit> bukkitMock = mockStatic(Bukkit.class);
MockedStatic<MinecraftReflection> reflectionMock = mockStatic(MinecraftReflection.class);
MockedStatic<PacketRegistry> registryMock = mockStatic(PacketRegistry.class)
) {
bukkitMock.when(Bukkit::getVersion).thenReturn("git-Bukkit-18fbb24 (MC: 1.17)");
reflectionMock.when(MinecraftReflection::getMinecraftPackage).thenReturn("xyz");
reflectionMock.when(MinecraftReflection::getEnumProtocolClass).thenReturn(Object.class);
registryMock.when(() -> PacketRegistry.tryGetPacketClass(any())).thenReturn(Optional.empty());
Class<?> injectorClass = Class.forName(NETTY_INJECTOR_CLASS);
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(injectorClass, "networkManager", Object.class);
assertNotNull(accessor.getField());
}
}
}

View File

@ -0,0 +1,60 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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.task;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import lombok.val;
import org.bukkit.entity.Player;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class DelayedAuthHookTest {
@Test
void createNewReflectiveInstance() throws ReflectiveOperationException {
val authHook = new DelayedAuthHook(null);
assertNotNull(authHook.newInstance(DummyHook.class));
}
public static class DummyHook implements AuthPlugin<Player> {
@Override
public boolean forceLogin(Player player) {
return false;
}
@Override
public boolean forceRegister(Player player, String password) {
return false;
}
@Override
public boolean isRegistered(String playerName) throws Exception {
return false;
}
}
}

View File

@ -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.

View File

@ -0,0 +1,27 @@
# About
This contains test resources for the unit tests. The files 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
* `valid_public_key.json`: Extracted from the actual file
* `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
## 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

View File

@ -0,0 +1,5 @@
{
"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="
}

View File

@ -0,0 +1,5 @@
{
"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": "lfRXK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU="
}

View File

@ -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="
}

View File

@ -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="
}

View File

@ -0,0 +1,5 @@
{
"expires_at": "2022-07-29T12:57:39.011Z",
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtYOUXdid0c09/eYoseLf8qG9fKQ/G2DY9wlSyEZaFMflwZ8ZpLFYigxzfaimpT3A5cbFIdIH2W2sYl5PwsKSs128GBh/rxXUEZlLkIkS+EfxyuMp9ITclxAjCqvFgfJbZHugtB9Ofi6knCEEgjFwMDh2efdpOXkCxtHuPFfnVzDJBbHWdlCCtJesMAnA2jCT7CqCwsi7sW2QxuTarqHP/cHKiBeBIu/SngGUB6eWmvAwERW5x2D+O26w8Z5sQCND3xQ4D868RALiPNG94TyKoJV+jKi0tTUmjGGs/1ksbSGDQb5xqIH0NYKZhoZrczYPNmJX4k7g5BA5RHX8AGORaQIDAQAB\n-----END RSA PUBLIC KEY-----\n",
"signature": "Fto/GDqEMTWpNrktWSi3tnP3ZZlo8r4Jled/5PKYRvaL/zksfjB2RK2O8pZL+w5mI2VAViTVAQmSJEF2o/BCb2L0zXp3/hC9VhZj5NTVi4KbHfnfMorj7/WJP2vvMgVxIxgLb3EEQXGS2Mmo0w2ikUVauwXgLWECvVt10KAZnTAWNIvpM8NUoZ2oCCxVimYHBtlwWQ7WvowAatP4ypa7fo3xhQg8Im1hvQDsFTNp58pgnd7l3l99xLj1uYOUJM06HGZJ/Xd0kzzJz44Csh4m50Q0RP4Nq5L+fYPeUx990Z1r1lw0sSayk+vA2Qnxgbs/z6KgkxfhBg7oOlp4ykl9cLC2kA/LdV6igqsdr/KoP4GWxwTA7RgQbhMkDFdmIg1W+gh3XqwASFQK2BAN/eAfmDTf8u9BtOEF7Ehn9uPOaiFiGztyaHxXNIkVSPTG2GXMFSijnd3Ms28jHYVY/67INTnDRmN0//KzBAoTRMe1S5idai19kug4EUVIRKDziipowzCPdbD18trdQGpK0dYOrw9XQiQd4N4V3eItpyAULGiZd8KcjgKo4orqgsUfNyhLI1keig7TyJUE3FkBOfX4hlZBm7Q/Wq4hwarlc5yZIjhsuivKV/q4tcnYYPwjP7kNMRsIApWG+yHmSIo8QfZhBiPxvtWSSLZgoFgnlxfaEko="
}

View File

@ -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 an 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 that will be signed
* `signature`: Nonce and salt signed using the client key from `valid_public_key.json`

View File

@ -0,0 +1,7 @@
{
"nonce": "galNig\u003d\u003d",
"signature": {
"signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
"salt": -2985008842905108412
}
}

View File

@ -0,0 +1,7 @@
{
"nonce": "GalNig\u003d\u003d",
"signature": {
"signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
"salt": -1985008842905108412
}
}

View File

@ -0,0 +1,7 @@
{
"nonce": "GalNig\u003d\u003d",
"signature": {
"signature": "jlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
"salt": -2985008842905108412
}
}

View File

@ -0,0 +1,7 @@
{
"nonce": "GalNig\u003d\u003d",
"signature": {
"signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
"salt": -2985008842905108412
}
}

View File

@ -4,7 +4,7 @@
The MIT License (MIT)
Copyright (c) 2015-2021 <Your name and contributors>
Copyright (c) 2015-2023 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
@ -25,14 +25,14 @@
SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.11-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -46,10 +46,11 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<version>3.5.1</version>
<configuration>
<minimizeJar>true</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>false</shadedArtifactAttached>
<artifactSet>
@ -73,8 +74,18 @@
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
<!-- Located in META-INF/services -->
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
<exclude>**/module-info.class</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
@ -94,11 +105,6 @@
<url>https://repo.codemc.io/repository/maven-public/</url>
</repository>
<repository>
<id>spigotplugins-repo</id>
<url>https://maven.gamestrike.de/mvn/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
@ -120,7 +126,7 @@
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<version>1.19-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<!-- Use our own newer api version -->
<exclusions>
@ -128,6 +134,30 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>mysql</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-native</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-query</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-slf4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.maven</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
@ -139,11 +169,7 @@
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.geysermc.cumulus</groupId>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
@ -151,7 +177,7 @@
<!-- Bedrock player bridge -->
<dependency>
<groupId>org.geysermc</groupId>
<groupId>org.geysermc.geyser</groupId>
<artifactId>core</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
@ -165,10 +191,16 @@
<!-- We need the API, but it was excluded above -->
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>geyser-api</artifactId>
<groupId>org.geysermc.geyser</groupId>
<artifactId>api</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Login plugin-->
@ -179,32 +211,5 @@
<scope>system</scope>
<systemPath>${project.basedir}/lib/BungeeAuth-1.4.jar</systemPath>
</dependency>
<dependency>
<groupId>de.xxschrandxx.bca</groupId>
<artifactId>BungeeCordAuthenticator</artifactId>
<version>0.0.2</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.Mohist-Community.SodionAuth</groupId>
<artifactId>SodionAuth-Bungee</artifactId>
<version>2bdfdc854b</version>
<exclusions>
<exclusion>
<groupId>com.github.Mohist-Community.SodionAuth</groupId>
<artifactId>SodionAuth-Libs</artifactId>
</exclusion>
</exclusions>
<optional>true</optional>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,8 +25,8 @@
*/
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.storage.StoredProfile;
public class BungeeLoginSession extends LoginSession {
@ -59,10 +59,10 @@ public class BungeeLoginSession extends LoginSession {
@Override
public synchronized String toString() {
return this.getClass().getSimpleName() + '{' +
"alreadySaved=" + alreadySaved +
", alreadyLogged=" + alreadyLogged +
", registered=" + registered +
"} " + super.toString();
return this.getClass().getSimpleName() + '{'
+ "alreadySaved=" + alreadySaved
+ ", alreadyLogged=" + alreadyLogged
+ ", registered=" + registered
+ "} " + super.toString();
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -26,15 +26,14 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.shared.LoginSource;
import java.net.InetSocketAddress;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.event.PreLoginEvent;
import java.net.InetSocketAddress;
public class BungeeLoginSource implements LoginSource {
private final PendingConnection connection;
@ -72,8 +71,8 @@ public class BungeeLoginSource implements LoginSource {
@Override
public String toString() {
return this.getClass().getSimpleName() + '{' +
"connection=" + connection +
'}';
return this.getClass().getSimpleName() + '{'
+ "connection=" + connection
+ '}';
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -26,8 +26,6 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.bungee.hook.BungeeAuthHook;
import com.github.games647.fastlogin.bungee.hook.BungeeCordAuthenticatorBungeeHook;
import com.github.games647.fastlogin.bungee.hook.SodionAuthHook;
import com.github.games647.fastlogin.bungee.listener.ConnectListener;
import com.github.games647.fastlogin.bungee.listener.PluginMessageListener;
import com.github.games647.fastlogin.core.AsyncScheduler;
@ -46,26 +44,25 @@ import com.google.common.collect.MapMaker;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadFactory;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.geyser.GeyserImpl;
import org.slf4j.Logger;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadFactory;
/**
* BungeeCord version of FastLogin. This plugin keeps track on online mode connections.
*/
@ -82,7 +79,7 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
@Override
public void onEnable() {
logger = CommonUtil.initializeLoggerService(getLogger());
scheduler = new AsyncScheduler(logger, getThreadFactory());
scheduler = new AsyncScheduler(logger, task -> getProxy().getScheduler().runAsync(this, task));
core = new FastLoginCore<>(this);
core.load();
@ -101,7 +98,7 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
//events
PluginManager pluginManager = getProxy().getPluginManager();
ConnectListener connectListener = new ConnectListener(this, core.getRateLimiter());
Listener connectListener = new ConnectListener(this, core.getAntiBot());
pluginManager.registerListener(this, connectListener);
pluginManager.registerListener(this, new PluginMessageListener(this));
@ -129,8 +126,9 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
private void registerHook() {
try {
List<Class<? extends AuthPlugin<ProxiedPlayer>>> hooks = Arrays.asList(
BungeeAuthHook.class, BungeeCordAuthenticatorBungeeHook.class, SodionAuthHook.class);
List<Class<? extends AuthPlugin<ProxiedPlayer>>> hooks = Collections.singletonList(
BungeeAuthHook.class
);
for (Class<? extends AuthPlugin<ProxiedPlayer>> clazz : hooks) {
String pluginName = clazz.getSimpleName();

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,9 +25,9 @@
*/
package com.github.games647.fastlogin.bungee.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.api.plugin.Event;

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,9 +25,9 @@
*/
package com.github.games647.fastlogin.bungee.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSource;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import net.md_5.bungee.api.plugin.Event;
public class BungeeFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,9 +25,8 @@
*/
package com.github.games647.fastlogin.bungee.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import net.md_5.bungee.api.plugin.Event;
public class BungeeFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -27,18 +27,18 @@ package com.github.games647.fastlogin.bungee.hook;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import me.vik1395.BungeeAuth.Main;
import me.vik1395.BungeeAuthAPI.RequestHandler;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* GitHub: https://github.com/vik1395/BungeeAuth-Minecraft
*
* GitHub:
* <a href="https://github.com/vik1395/BungeeAuth-Minecraft">...</a>
* <p>
* Project page:
*
* Spigot: https://www.spigotmc.org/resources/bungeeauth.493/
* <p>
* Spigot:
* <a href="https://www.spigotmc.org/resources/bungeeauth.493/">...</a>
*/
public class BungeeAuthHook implements AuthPlugin<ProxiedPlayer> {

View File

@ -1,92 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name 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.bungee.hook;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import de.xxschrandxx.bca.bungee.BungeeCordAuthenticatorBungee;
import de.xxschrandxx.bca.bungee.api.BungeeCordAuthenticatorBungeeAPI;
import java.sql.SQLException;
import java.util.logging.Level;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* GitHub:
* https://github.com/xXSchrandXx/SpigotPlugins/tree/master/BungeeCordAuthenticator
* <p>
* Project page:
* <p>
* Spigot: https://www.spigotmc.org/resources/bungeecordauthenticator.87669/
*/
public class BungeeCordAuthenticatorBungeeHook implements AuthPlugin<ProxiedPlayer> {
public final BungeeCordAuthenticatorBungeeAPI api;
public BungeeCordAuthenticatorBungeeHook(FastLoginBungee plugin) {
api = ((BungeeCordAuthenticatorBungee) plugin.getProxy().getPluginManager()
.getPlugin("BungeeCordAuthenticatorBungee")).getAPI();
plugin.getLog().info("BungeeCordAuthenticatorHook | Hooked successful!");
}
@Override
public boolean forceLogin(ProxiedPlayer player) {
if (api.isAuthenticated(player)) {
return true;
}
try {
api.setAuthenticated(player);
} catch (SQLException sqlEx) {
api.getLogger().log(Level.WARNING, "Failed to force login", sqlEx);
return false;
}
return true;
}
@Override
public boolean isRegistered(String playerName) {
try {
return api.getSQL().checkPlayerEntry(playerName);
} catch (SQLException sqlEx) {
api.getLogger().log(Level.WARNING, "Failed to check registration", sqlEx);
return false;
}
}
@Override
public boolean forceRegister(ProxiedPlayer player, String password) {
try {
return api.createPlayerEntry(player, password);
} catch (SQLException sqlEx) {
api.getLogger().log(Level.WARNING, "Failed to force register", sqlEx);
return false;
}
}
}

View File

@ -1,78 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name 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.bungee.hook;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import red.mohist.sodionauth.bungee.implementation.BungeePlayer;
import red.mohist.sodionauth.core.SodionAuthApi;
import red.mohist.sodionauth.core.exception.AuthenticatedException;
/**
* GitHub: https://github.com/Mohist-Community/SodionAuth
* <p>
* Project page: https://gitea.e-loli.com/SodionAuth/SodionAuth
* <p>
* Bukkit: Unknown
* <p>
* Spigot: https://www.spigotmc.org/resources/sodionauth.76944/
*/
public class SodionAuthHook implements AuthPlugin<ProxiedPlayer> {
private final FastLoginBungee plugin;
public SodionAuthHook(FastLoginBungee plugin) {
this.plugin = plugin;
}
@Override
public boolean forceLogin(ProxiedPlayer player) {
try {
SodionAuthApi.login(new BungeePlayer(player));
} catch (AuthenticatedException e) {
plugin.getLog().warn(ALREADY_AUTHENTICATED, player);
return false;
}
return true;
}
@Override
public boolean forceRegister(ProxiedPlayer player, String password) {
try{
return SodionAuthApi.register(new BungeePlayer(player), password);
} catch (UnsupportedOperationException e){
plugin.getLog().warn("Currently SodionAuth is not accepting forceRegister, " +
"It may be caused by unsupported AuthBackend");
return false;
}
}
@Override
public boolean isRegistered(String playerName) {
return SodionAuthApi.isRegistered(playerName);
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -31,18 +31,13 @@ import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.task.AsyncPremiumCheck;
import com.github.games647.fastlogin.bungee.task.FloodgateAuthTask;
import com.github.games647.fastlogin.bungee.task.ForceLoginTask;
import com.github.games647.fastlogin.core.RateLimiter;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.antibot.AntiBotService;
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import com.google.common.base.Throwables;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.util.UUID;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
@ -53,14 +48,20 @@ import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.connection.LoginResult.Property;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
import net.md_5.bungee.protocol.Property;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.util.UUID;
/**
* Enables online mode logins for specified users and sends plugin message to the Bukkit version of this plugin in
* order to clear that the connection is online mode.
@ -68,7 +69,7 @@ import org.slf4j.LoggerFactory;
public class ConnectListener implements Listener {
private static final String UUID_FIELD_NAME = "uniqueId";
private static final MethodHandle uniqueIdSetter;
protected static final MethodHandle UNIQUE_ID_SETTER;
static {
MethodHandle setHandle = null;
@ -84,20 +85,20 @@ public class ConnectListener implements Listener {
Logger logger = LoggerFactory.getLogger(ConnectListener.class);
logger.error(
"Cannot find Bungee initial handler; Disabling premium UUID and skin won't work.",
reflectiveOperationException
reflectiveOperationException
);
}
uniqueIdSetter = setHandle;
UNIQUE_ID_SETTER = setHandle;
}
private final FastLoginBungee plugin;
private final RateLimiter rateLimiter;
private final AntiBotService antiBotService;
private final Property[] emptyProperties = {};
public ConnectListener(FastLoginBungee plugin, RateLimiter rateLimiter) {
public ConnectListener(FastLoginBungee plugin, AntiBotService antiBotService) {
this.plugin = plugin;
this.rateLimiter = rateLimiter;
this.antiBotService = antiBotService;
}
@EventHandler
@ -107,17 +108,28 @@ public class ConnectListener implements Listener {
return;
}
if (!rateLimiter.tryAcquire()) {
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection);
return;
}
InetSocketAddress address = preLoginEvent.getConnection().getAddress();
String username = connection.getName();
plugin.getLog().info("Incoming login request for {} from {}", username, connection.getSocketAddress());
preLoginEvent.registerIntent(plugin);
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
plugin.getScheduler().runAsync(asyncPremiumCheck);
Action action = antiBotService.onIncomingConnection(address, username);
switch (action) {
case Ignore:
// just ignore
return;
case Block:
String message = plugin.getCore().getMessage("kick-antibot");
preLoginEvent.setCancelReason(TextComponent.fromLegacyText(message));
preLoginEvent.setCancelled(true);
break;
case Continue:
default:
preLoginEvent.registerIntent(plugin);
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
plugin.getScheduler().runAsync(asyncPremiumCheck);
break;
}
}
@EventHandler(priority = EventPriority.LOWEST)
@ -140,8 +152,8 @@ public class ConnectListener implements Listener {
StoredProfile playerProfile = session.getProfile();
playerProfile.setId(verifiedUUID);
// bungeecord will do this automatically so override it on disabled option
if (uniqueIdSetter != null) {
// BungeeCord will do this automatically so override it on disabled option
if (UNIQUE_ID_SETTER != null) {
InitialHandler initialHandler = (InitialHandler) connection;
if (!plugin.getCore().getConfig().get("premiumUuid", true)) {
@ -157,7 +169,7 @@ public class ConnectListener implements Listener {
}
}
private void setOfflineId(InitialHandler connection, String username) {
protected void setOfflineId(InitialHandler connection, String username) {
try {
UUID oldPremiumId = connection.getUniqueId();
UUID offlineUUID = UUIDAdapter.generateOfflineId(username);
@ -165,7 +177,7 @@ public class ConnectListener implements Listener {
// BungeeCord only allows setting the UUID in PreLogin events and before requesting online mode
// However if online mode is requested, it will override previous values
// So we have to do it with reflection
uniqueIdSetter.invokeExact(connection, offlineUUID);
UNIQUE_ID_SETTER.invokeExact(connection, offlineUUID);
String format = "Overridden UUID from {} to {} (based of {}) on {}";
plugin.getLog().info(format, oldPremiumId, offlineUUID, username, connection);

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -28,17 +28,14 @@ package com.github.games647.fastlogin.bungee.listener;
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.task.AsyncToggleMessage;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
import com.github.games647.fastlogin.core.message.NamespaceKey;
import com.github.games647.fastlogin.core.message.SuccessMessage;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import java.util.Arrays;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
@ -47,6 +44,8 @@ import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import java.util.Arrays;
public class PluginMessageListener implements Listener {
private final FastLoginBungee plugin;

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -29,10 +29,9 @@ import com.github.games647.fastlogin.bungee.BungeeLoginSession;
import com.github.games647.fastlogin.bungee.BungeeLoginSource;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginPreLoginEvent;
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 com.github.games647.fastlogin.core.storage.StoredProfile;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -27,10 +27,9 @@ package com.github.games647.fastlogin.bungee.task;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginPremiumToggleEvent;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
@ -73,8 +72,8 @@ public class AsyncToggleMessage implements Runnable {
playerProfile.setPremium(false);
playerProfile.setId(null);
core.getStorage().save(playerProfile);
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName())) ?
PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName()))
? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
core.getPlugin().getProxy().getPluginManager().callEvent(
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
sendMessage("remove-premium");
@ -89,8 +88,8 @@ public class AsyncToggleMessage implements Runnable {
playerProfile.setPremium(true);
core.getStorage().save(playerProfile);
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName())) ?
PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName()))
? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
core.getPlugin().getProxy().getPluginManager().callEvent(
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
sendMessage("add-premium");

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,19 +25,17 @@
*/
package com.github.games647.fastlogin.bungee.task;
import java.net.InetSocketAddress;
import java.util.UUID;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.FloodgateManagement;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import java.net.InetSocketAddress;
import java.util.UUID;
public class FloodgateAuthTask
extends FloodgateManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
@ -55,13 +53,9 @@ public class FloodgateAuthTask
BungeeLoginSession session = new BungeeLoginSession(player.getName(), isRegistered, profile);
core.getPlugin().getSession().put(player.getPendingConnection(), session);
// enable auto login based on the value of 'autoLoginFloodgate' in config.yml
boolean forcedOnlineMode = autoLoginFloodgate.equals("true")
|| (autoLoginFloodgate.equals("linked") && isLinked);
// run login task
Runnable forceLoginTask = new ForceLoginTask(core.getPlugin().getCore(), player, server, session,
forcedOnlineMode);
isAutoAuthAllowed(autoLoginFloodgate));
core.getPlugin().getScheduler().runAsync(forceLoginTask);
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -28,7 +28,6 @@ package com.github.games647.fastlogin.bungee.task;
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginAutoLoginEvent;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.message.ChannelMessage;
import com.github.games647.fastlogin.core.message.LoginActionMessage;
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
@ -36,14 +35,14 @@ import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
import java.util.UUID;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import java.util.UUID;
public class ForceLoginTask
extends ForceLoginManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {

View File

@ -0,0 +1,57 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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.bungee.listener;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.conf.Configuration;
import net.md_5.bungee.connection.InitialHandler;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
class ConnectListenerTest {
@Test
void testUUIDSetter() throws Throwable {
BungeeCord proxyMock = mock(BungeeCord.class);
BungeeCord.setInstance(proxyMock);
Configuration configMock = mock(Configuration.class);
Field configField = proxyMock.getClass().getField("config");
configField.setAccessible(true);
configField.set(proxyMock, configMock);
InitialHandler handler = new InitialHandler(proxyMock, null);
UUID expectedUUID = UUID.randomUUID();
ConnectListener.UNIQUE_ID_SETTER.invokeExact(handler, expectedUUID);
assertEquals(expectedUUID, handler.getUniqueId());
}
}

233
checkstyle.xml Normal file
View File

@ -0,0 +1,233 @@
<?xml version="1.0"?>
<!--
SPDX-License-Identifier: MIT
The MIT License (MIT)
Copyright (c) 2015-2023 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.
-->
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!--
Checkstyle configuration that checks the sun coding conventions from:
- the Java Language Specification at
https://docs.oracle.com/javase/specs/jls/se11/html/index.html
- the Sun Code Conventions at https://www.oracle.com/java/technologies/javase/codeconventions-contents.html
- the Javadoc guidelines at
https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html
- the JDK Api documentation https://docs.oracle.com/en/java/javase/11/
- some best practices
Checkstyle is very configurable. Be sure to read the documentation at
https://checkstyle.org (or in your downloaded distribution).
Most Checks are configurable, be sure to consult the documentation.
To completely disable a check, just comment it out or delete it from the file.
To suppress certain violations please review suppression filters.
Finally, it is worth reading the documentation.
-->
<module name="Checker">
<!--
If you set the basedir property below, then all reported file
names will be relative to the specified directory. See
https://checkstyle.org/config.html#Checker
<property name="basedir" value="${basedir}"/>
-->
<property name="severity" value="error"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Excludes all 'module-info.java' files -->
<!-- See https://checkstyle.org/config_filefilters.html -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
<module name="SuppressionFilter">
<property name="file" value="${org.checkstyle.sun.suppressionfilter.config}"
default="checkstyle-suppressions.xml" />
<property name="optional" value="true"/>
</module>
<!-- Checks that a package-info.java file exists for each package. -->
<!-- See https://checkstyle.org/config_javadoc.html#JavadocPackage -->
<!--<module name="JavadocPackage"/>-->
<!-- Checks whether files end with a new line. -->
<!-- See https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
<module name="NewlineAtEndOfFile"/>
<!-- Checks that property files contain the same keys. -->
<!-- See https://checkstyle.org/config_misc.html#Translation -->
<module name="Translation"/>
<!-- Checks for Size Violations. -->
<!-- See https://checkstyle.org/config_sizes.html -->
<module name="FileLength"/>
<module name="LineLength">
<property name="max" value="120"/>
<property name="fileExtensions" value="java"/>
<property name="ignorePattern" value="^ *\* *@see.+$"/>
</module>
<!-- Checks for whitespace -->
<!-- See https://checkstyle.org/config_whitespace.html -->
<module name="FileTabCharacter"/>
<!-- Miscellaneous other checks. -->
<!-- See https://checkstyle.org/config_misc.html -->
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="minimum" value="0"/>
<property name="maximum" value="0"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<!-- Checks for Headers -->
<!-- See https://checkstyle.org/config_header.html -->
<!-- <module name="Header"> -->
<!-- <property name="headerFile" value="${checkstyle.header.file}"/> -->
<!-- <property name="fileExtensions" value="java"/> -->
<!-- </module> -->
<module name="TreeWalker">
<!-- Checks for Javadoc comments. -->
<!-- See https://checkstyle.org/config_javadoc.html -->
<module name="InvalidJavadocPosition"/>
<module name="JavadocMethod"/>
<!--<module name="JavadocType"/>-->
<!--<module name="JavadocVariable"/>-->
<!--<module name="JavadocStyle"/>-->
<!--<module name="MissingJavadocMethod"/>-->
<!-- Checks for Naming Conventions. -->
<!-- See https://checkstyle.org/config_naming.html -->
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<!-- Checks for imports -->
<!-- See https://checkstyle.org/config_imports.html -->
<module name="AvoidStarImport"/>
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
<module name="RedundantImport"/>
<module name="UnusedImports">
<property name="processJavadoc" value="false"/>
</module>
<!-- Checks for Size Violations. -->
<!-- See https://checkstyle.org/config_sizes.html -->
<module name="MethodLength"/>
<module name="ParameterNumber"/>
<!-- Checks for whitespace -->
<!-- See https://checkstyle.org/config_whitespace.html -->
<module name="EmptyForIteratorPad"/>
<module name="GenericWhitespace"/>
<module name="MethodParamPad"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<!-- Modifier Checks -->
<!-- See https://checkstyle.org/config_modifier.html -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Checks for blocks. You know, those {}'s -->
<!-- See https://checkstyle.org/config_blocks.html -->
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock"/>
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<module name="RightCurly"/>
<!-- Checks for common coding problems -->
<!-- See https://checkstyle.org/config_coding.html -->
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<!--<module name="MagicNumber"/>-->
<module name="MissingSwitchDefault"/>
<module name="MultipleVariableDeclarations"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<!-- Checks for class design -->
<!-- See https://checkstyle.org/config_design.html -->
<!--<module name="DesignForExtension"/>-->
<module name="FinalClass"/>
<module name="HideUtilityClassConstructor"/>
<module name="InterfaceIsType"/>
<!-- Miscellaneous other checks. -->
<!-- See https://checkstyle.org/config_misc.html -->
<module name="ArrayTypeStyle"/>
<!--<module name="FinalParameters"/>-->
<!-- <module name="TodoComment"/>-->
<module name="UpperEll"/>
<!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
<module name="SuppressionXpathFilter">
<property name="file" value="${org.checkstyle.sun.suppressionxpathfilter.config}"
default="checkstyle-xpath-suppressions.xml" />
<property name="optional" value="true"/>
</module>
<!-- Suppress filters via comments -->
<!-- https://stackoverflow.com/a/4023351/9767089 -->
<module name="SuppressionCommentFilter">
<property name="offCommentFormat" value="CHECKSTYLE.OFF\: ([\w\|]+)"/>
<property name="onCommentFormat" value="CHECKSTYLE.ON\: ([\w\|]+)"/>
<property name="checkFormat" value="$1"/>
</module>
</module>
</module>

View File

@ -4,7 +4,7 @@
The MIT License (MIT)
Copyright (c) 2015-2021 <Your name and contributors>
Copyright (c) 2015-2023 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
@ -25,14 +25,14 @@
SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.11-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -44,7 +44,7 @@
<repositories>
<repository>
<id>luck-repo</id>
<url>https://ci.lucko.me/plugin/repository/everything</url>
<url>https://ci.lucko.me/plugin/repository/everything/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
@ -57,9 +57,29 @@
<repository>
<id>opencollab-snapshot</id>
<url>https://repo.opencollab.dev/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>com.github.games647.fastlogin.core</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Libraries that we shade into the project -->
@ -82,20 +102,14 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>2.0.0-alpha6</version>
<version>2.0.11</version>
</dependency>
<!-- snakeyaml is present in Bungee, Spigot, Cauldron, so we could use this independent implementation -->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-config</artifactId>
<version>1.16-R0.4</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
<version>1.19-R0.1-SNAPSHOT</version>
</dependency>
<!--Floodgate for Xbox Live Authentication-->
@ -106,11 +120,7 @@
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.geysermc.cumulus</groupId>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
@ -118,7 +128,7 @@
<!-- Bedrock player bridge -->
<dependency>
<groupId>org.geysermc</groupId>
<groupId>org.geysermc.geyser</groupId>
<artifactId>core</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
@ -132,17 +142,23 @@
<!-- We need the API, but it was excluded above -->
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>geyser-api</artifactId>
<groupId>org.geysermc.geyser</groupId>
<artifactId>api</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Common component for contacting the Mojang API-->
<dependency>
<groupId>com.github.games647</groupId>
<artifactId>craftapi</artifactId>
<version>0.5.1</version>
<version>0.6.2</version>
</dependency>
<!-- APIs we can use because they are available in all platforms (Spigot, Bungee, Velocity) -->
@ -179,7 +195,14 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>[3.36,)</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,16 +25,11 @@
*/
package com.github.games647.fastlogin.core;
import com.google.common.util.concurrent.MoreExecutors;
import org.slf4j.Logger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
/**
* This limits the number of threads that are used at maximum. Thread creation can be very heavy for the CPU and
@ -53,42 +48,28 @@ public class AsyncScheduler {
// 30 threads are still too many - the optimal solution is to separate into processing and blocking threads
// where processing threads could only be max number of cores while blocking threads could be minimized using
// non-blocking I/O and a single event executor
private final ExecutorService processingPool;
private final Executor processingPool;
/*
private final ExecutorService databaseExecutor = new ThreadPoolExecutor(1, 10,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(MAX_CAPACITY));
*/
private final AtomicInteger currentlyRunning = new AtomicInteger();
public AsyncScheduler(Logger logger, ThreadFactory threadFactory) {
public AsyncScheduler(Logger logger, Executor processingPool) {
this.logger = logger;
processingPool = new ThreadPoolExecutor(6, 32,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(MAX_CAPACITY), threadFactory);
this.processingPool = processingPool;
}
/*
public <R> CompletableFuture<R> runDatabaseTask(Supplier<R> databaseTask) {
return CompletableFuture.supplyAsync(databaseTask, databaseExecutor)
.exceptionally(error -> {
logger.warn("Error occurred on thread pool", error);
return null;
})
// change context to the processing pool
.thenApplyAsync(r -> r, processingPool);
}
*/
public CompletableFuture<Void> runAsync(Runnable task) {
return CompletableFuture.runAsync(task, processingPool).exceptionally(error -> {
return CompletableFuture.runAsync(() -> process(task), processingPool).exceptionally(error -> {
logger.warn("Error occurred on thread pool", error);
return null;
});
}
public void shutdown() {
MoreExecutors.shutdownAndAwaitTermination(processingPool, 1, TimeUnit.MINUTES);
//MoreExecutors.shutdownAndAwaitTermination(databaseExecutor, 1, TimeUnit.MINUTES);
private void process(Runnable task) {
currentlyRunning.incrementAndGet();
try {
task.run();
} finally {
currentlyRunning.getAndDecrement();
}
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -25,25 +25,24 @@
*/
package com.github.games647.fastlogin.core;
import com.github.games647.craftapi.cache.SafeCacheBuilder;
import com.google.common.cache.CacheLoader;
import java.lang.reflect.Constructor;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import com.google.common.cache.CacheBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.jul.JDK14LoggerAdapter;
public class CommonUtil {
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public final class CommonUtil {
private static final char COLOR_CHAR = '&';
private static final char TRANSLATED_CHAR = '§';
public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) {
SafeCacheBuilder<Object, Object> builder = SafeCacheBuilder.newBuilder();
CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
if (expireAfterWrite > 0) {
builder.expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES);
@ -53,15 +52,13 @@ public class CommonUtil {
builder.maximumSize(maxSize);
}
return builder.build(CacheLoader.from(() -> {
throw new UnsupportedOperationException();
}));
return builder.<K, V>build().asMap();
}
public static String translateColorCodes(String rawMessage) {
char[] chars = rawMessage.toCharArray();
for (int i = 0; i < chars.length - 1; i++) {
if (chars[i] == COLOR_CHAR && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(chars[i + 1]) > -1) {
if (chars[i] == COLOR_CHAR && "x0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(chars[i + 1]) > -1) {
chars[i] = TRANSLATED_CHAR;
chars[i + 1] = Character.toLowerCase(chars[i + 1]);
}
@ -74,7 +71,7 @@ public class CommonUtil {
* This creates a SLF4J logger. In the process it initializes the SLF4J service provider. This method looks
* for the provider in the plugin jar instead of in the server jar when creating a Logger. The provider is only
* initialized once, so this method should be called early.
*
* <p>
* The provider is bound to the service class `SLF4JServiceProvider`. Relocating this class makes it available
* for exclusive own usage. Other dependencies will use the relocated service too, and therefore will find the
* initialized provider.
@ -93,12 +90,9 @@ public class CommonUtil {
LoggerFactory.getLogger(parent.getName()).info("Initialize logging service");
try {
parent.setLevel(Level.ALL);
Class<JDK14LoggerAdapter> adapterClass = JDK14LoggerAdapter.class;
Constructor<JDK14LoggerAdapter> cons = adapterClass.getDeclaredConstructor(java.util.logging.Logger.class);
cons.setAccessible(true);
return cons.newInstance(parent);
} catch (ReflectiveOperationException reflectEx) {
return createJDKLogger(parent);
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException reflectEx) {
parent.log(Level.WARNING, "Cannot create slf4j logging adapter", reflectEx);
parent.log(Level.WARNING, "Creating logger instance manually...");
return LoggerFactory.getLogger(parent.getName());
@ -108,7 +102,15 @@ public class CommonUtil {
}
}
protected static JDK14LoggerAdapter createJDKLogger(java.util.logging.Logger parent)
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class<JDK14LoggerAdapter> adapterClass = JDK14LoggerAdapter.class;
Constructor<JDK14LoggerAdapter> cons = adapterClass.getDeclaredConstructor(java.util.logging.Logger.class);
cons.setAccessible(true);
return cons.newInstance(parent);
}
private CommonUtil() {
//Utility class
throw new RuntimeException("No instantiation of utility classes allowed");
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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

View File

@ -0,0 +1,81 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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.core;
import com.github.games647.craftapi.model.auth.Verification;
import com.github.games647.craftapi.resolver.MojangResolver;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.util.Optional;
/**
* An extension to {@link MojangResolver} which allows connection using transparent reverse proxies.
* The significant difference is that unlike MojangResolver from the CraftAPI implementation, which sends the
* "ip" parameter when the hostIp parameter is an IPv4 address, but skips it for IPv6, this implementation leaves out
* the "ip" parameter also for IPv4, effectively enabling transparent proxies to work.
*
* @author games647, Enginecrafter77
*/
public class ProxyAgnosticMojangResolver extends MojangResolver {
private static final String HOST = "sessionserver.mojang.com";
/**
* A formatting string containing a URL used to call the {@code hasJoined} method on mojang session servers.
* <p>
* Formatting parameters:
* 1. The username of the player in question
* 2. The serverId of this server
*/
public static final String ENDPOINT = "https://" + HOST + "/session/minecraft/hasJoined?username=%s&serverId=%s";
@Override
public Optional<Verification> hasJoined(String username, String serverHash, InetAddress hostIp)
throws IOException {
String url = String.format(ENDPOINT, username, serverHash);
HttpURLConnection conn = this.getConnection(url);
int responseCode = conn.getResponseCode();
Verification verification = null;
// Mojang session servers send HTTP 204 (NO CONTENT) when the authentication seems invalid
// If that's not our case, the authentication is valid, and so we can parse the response.
if (responseCode != HttpURLConnection.HTTP_NO_CONTENT) {
verification = this.parseRequest(conn, this::parseVerification);
}
return Optional.ofNullable(verification);
}
// Functional implementation of InputStreamAction, used in hasJoined method in parseRequest call
protected Verification parseVerification(InputStream input) throws IOException {
return this.readJson(input, Verification.class);
}
}

View File

@ -1,7 +0,0 @@
package com.github.games647.fastlogin.core;
@FunctionalInterface
public interface RateLimiter {
boolean tryAcquire();
}

View File

@ -1,77 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name 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.core;
import com.google.common.base.Ticker;
import java.util.Arrays;
/**
* Limit the number of requests with a maximum size. Each requests expire after the specified time making it available
* for another request.
*/
public class TickingRateLimiter implements RateLimiter {
private final Ticker ticker;
private final long[] requests;
private final long expireTime;
private int position;
public TickingRateLimiter(Ticker ticker, int maxLimit, long expireTime) {
this.ticker = ticker;
this.requests = new long[maxLimit];
this.expireTime = expireTime;
// fill the array with expired entry, because nanoTime could overflow and include negative numbers
long nowMilli = ticker.read() / 1_000_000;
long initialVal = nowMilli - expireTime;
Arrays.fill(requests, initialVal);
}
/**
* Ask if access is allowed. If so register the request.
*
* @return true if allowed - false otherwise without any side effects
*/
@Override
public boolean tryAcquire() {
// current time millis is not monotonic - it can jump back depending on user choice or NTP
long nowMilli = ticker.read() / 1_000_000;
synchronized (this) {
// having synchronized will limit the amount of concurrency a lot
long oldest = requests[position];
if (nowMilli - oldest >= expireTime) {
requests[position] = nowMilli;
position = (position + 1) % requests.length;
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,62 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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.core.antibot;
import org.slf4j.Logger;
import java.net.InetSocketAddress;
public class AntiBotService {
private final Logger logger;
private final RateLimiter rateLimiter;
private final Action limitReachedAction;
public AntiBotService(Logger logger, RateLimiter rateLimiter, Action limitReachedAction) {
this.logger = logger;
this.rateLimiter = rateLimiter;
this.limitReachedAction = limitReachedAction;
}
public Action onIncomingConnection(InetSocketAddress clientAddress, String username) {
if (!rateLimiter.tryAcquire()) {
logger.warn("Anti-Bot join limit - Ignoring {}", clientAddress);
return limitReachedAction;
}
return Action.Continue;
}
public enum Action {
Ignore,
Block,
Continue
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -23,3 +23,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.antibot;
@FunctionalInterface
public interface RateLimiter {
boolean tryAcquire();
}

View File

@ -0,0 +1,152 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 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.core.antibot;
import com.google.common.base.Ticker;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
/**
* Limit the number of requests with a maximum size. Each requests expire after the specified time making it available
* for another request.
*/
public class TickingRateLimiter implements RateLimiter {
private final Ticker ticker;
// amount of milliseconds to expire
private final long expireTime;
// total request limit
private final int requestLimit;
private final Deque<TimeRecord> records;
private int totalRequests;
public TickingRateLimiter(Ticker ticker, int maxLimit, long expireTime) {
this.ticker = ticker;
this.requestLimit = maxLimit;
this.expireTime = expireTime;
records = new ArrayDeque<>(10);
}
/**
* Ask if access is allowed. If so register the request.
*
* @return true if allowed - false otherwise without any side effects
*/
@Override
public boolean tryAcquire() {
// current time millis is not monotonic - it can jump back depending on user choice or NTP
long nowMilli = ticker.read() / 1_000_000;
synchronized (this) {
// having synchronized will limit the amount of concurrency a lot
TimeRecord oldest = records.peekFirst();
if (oldest != null && oldest.hasExpired(nowMilli)) {
records.pop();
totalRequests -= oldest.getRequestCount();
}
// total requests reached block any further requests
if (totalRequests >= requestLimit) {
return false;
}
TimeRecord latest = records.peekLast();
if (latest == null) {
// empty list - add new record
records.add(new TimeRecord(nowMilli, expireTime));
totalRequests++;
return true;
}
int res = latest.compareTo(nowMilli);
if (res < 0) {
// now is before than the record means time jumps
throw new IllegalStateException("Time jumped back");
}
if (res == 0) {
// same minute record
latest.hit();
totalRequests++;
return true;
}
// now is one minute newer
records.add(new TimeRecord(nowMilli, expireTime));
totalRequests++;
return true;
}
}
private static class TimeRecord implements Comparable<TimeRecord> {
private final long firstMinuteRecord;
private final long expireTime;
private int count;
TimeRecord(long firstMinuteRecord, long expireTime) {
this.firstMinuteRecord = firstMinuteRecord;
this.expireTime = expireTime;
this.count = 1;
}
public void hit() {
count++;
}
public int getRequestCount() {
return count;
}
public boolean hasExpired(long now) {
return firstMinuteRecord + expireTime <= now;
}
public int compareTo(long other) {
if (other < firstMinuteRecord) {
// other is earlier
return -1;
}
if (other > firstMinuteRecord + TimeUnit.MINUTES.toMillis(1)) {
return +1;
}
return 0;
}
@Override
public int compareTo(TimeRecord other) {
return compareTo(other.firstMinuteRecord);
}
}
}

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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
@ -37,7 +37,7 @@ public interface AuthPlugin<P> {
/**
* Login the premium (paid account) player after the player joined successfully the server.
*
* <p>
* <strong>This operation will be performed async while the player successfully
* joined the server.</strong>
*
@ -48,17 +48,17 @@ public interface AuthPlugin<P> {
/**
* Forces a register in order to protect the paid account.
*
* <p>
* <strong>This operation will be performed async while the player successfully
* joined the server.</strong>
*
* <p>
* After a successful registration the player should be logged
* in too.
*
* <p>
* The method will be called only for premium accounts.
* So it's recommended to set additionally premium property
* if possible.
*
* <p>
* Background: If we don't register an account, cracked players
* could steal the unregistered account from the paid
* player account
@ -71,11 +71,11 @@ public interface AuthPlugin<P> {
/**
* Checks whether an account exists for this player name.
*
* <p>
* This check should check if a cracked player account exists,
* so we can be sure the premium player doesn't steal the account
* of that player.
*
* <p>
* This operation will be performed async while the player is
* connecting.
*

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
* Copyright (c) 2015-2023 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

Some files were not shown because too many files have changed in this diff Show More