diff --git a/README.md b/README.md index 173a22a..5d730aa 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,13 @@ [![Closed Issues](https://img.shields.io/github/issues-closed/samolego/simpleauth.svg)](https://github.com/samolego/SimpleAuth/issues?q=is%3Aissue+is%3Aclosed) [![Curseforge downloads](http://cf.way2muchnoise.eu/full_simpleauth_downloads.svg)](https://www.curseforge.com/minecraft/mc-mods/simpleauth) -Requires Fabric API. +[Requires Fabric API.](https://www.curseforge.com/minecraft/mc-mods/fabric-api) ## License Libraries that the project is using: - `Argon2 (LGPLv3)` https://github.com/phxql/argon2-jvm +- `BCrypt (Apache 2)` https://github.com/patrickfav/bcrypt +- `Bytes (Apache 2)` https://github.com/patrickfav/bytes-java - `leveldb (BSD-3-Clause)` https://github.com/google/leveldb - `JNA (Apache 2 || LGPLv3)` https://github.com/java-native-access/jna @@ -51,7 +53,7 @@ And this to your `gradle.properties` ```properties # By tag (version) # SimpleAuth version (this might not be the latest version) -simpleauth_version = 1.4.6 +simpleauth_version = 1.4.8 # Or this (by branch) # SimpleAuth branches diff --git a/build.gradle b/build.gradle index 11f089b..d79725c 100644 --- a/build.gradle +++ b/build.gradle @@ -32,11 +32,20 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - // Argon2 library for password hashing - implementation 'de.mkammerer:argon2-jvm:2.6' - include 'de.mkammerer:argon2-jvm:2.6' - // leveldb + // Password hashing + // Argon2 + implementation "de.mkammerer:argon2-jvm:${argon2_version}" + include "de.mkammerer:argon2-jvm:${argon2_version}" + + // BCrypt + implementation "at.favre.lib:bcrypt:${bcrypt_version}" + implementation "at.favre.lib:bytes:${bytes_version}" + include "at.favre.lib:bcrypt:${bcrypt_version}" + include "at.favre.lib:bytes:${bytes_version}" + + // Storage + // leveldb database implementation group: 'org.iq80.leveldb', name: 'leveldb', version: '0.12' implementation group: 'org.iq80.leveldb', name: 'leveldb-api', version: '0.12' include group: 'org.iq80.leveldb', name: 'leveldb', version: '0.12' diff --git a/gradle.properties b/gradle.properties index 4764301..727e569 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,10 +10,15 @@ loader_version=0.9.0+build.204 fabric_version=0.16.0+build.386-1.16 # Mod Properties -mod_version = 1.4.8 +mod_version = 1.5.0 maven_group = org.samo_lego archives_base_name = simpleauth # Carpet for debugging carpet_core_version = 1.4.0+v200623 carpet_branch = 1.16 + +# Hashing +argon2_version = 2.7 +bcrypt_version = 0.9.0 +bytes_version = 1.3.0 diff --git a/src/main/java/org/samo_lego/simpleauth/commands/AccountCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/AccountCommand.java index 0afb645..c0af8af 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/AccountCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/AccountCommand.java @@ -120,7 +120,7 @@ public class AccountCommand { } // JSON object holding password (may hold some other info in the future) JsonObject playerdata = new JsonObject(); - String hash = AuthHelper.hashPass(newPass.toCharArray()); + String hash = AuthHelper.hashPassword(newPass.toCharArray()); playerdata.addProperty("password", hash); SimpleAuth.DB.updateUserData(convertUuid(player), playerdata.toString()); diff --git a/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java index 65b7d23..865cbdb 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java @@ -114,7 +114,7 @@ public class AuthCommand { // Different thread to avoid lag spikes THREADPOOL.submit(() -> { // Writing the global pass to config - config.main.globalPassword = AuthHelper.hashPass(pass.toCharArray()); + config.main.globalPassword = AuthHelper.hashPassword(pass.toCharArray()); config.main.enableGlobalPassword = true; config.save(new File("./mods/SimpleAuth/config.json")); }); @@ -133,6 +133,7 @@ public class AuthCommand { config.worldSpawn.x = x; config.worldSpawn.y = y; config.worldSpawn.z = z; + config.main.spawnOnJoin = true; config.save(new File("./mods/SimpleAuth/config.json")); // Getting sender @@ -167,7 +168,7 @@ public class AuthCommand { THREADPOOL.submit(() -> { // JSON object holding password (may hold some other info in the future) JsonObject playerdata = new JsonObject(); - String hash = AuthHelper.hashPass(password.toCharArray()); + String hash = AuthHelper.hashPassword(password.toCharArray()); playerdata.addProperty("password", hash); if (DB.registerUser(uuid, playerdata.toString())) { @@ -188,7 +189,7 @@ public class AuthCommand { THREADPOOL.submit(() -> { // JSON object holding password (may hold some other info in the future) JsonObject playerdata = new JsonObject(); - String hash = AuthHelper.hashPass(password.toCharArray()); + String hash = AuthHelper.hashPassword(password.toCharArray()); playerdata.addProperty("password", hash); DB.updateUserData(uuid, playerdata.toString()); diff --git a/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java index 1fcdbd4..a5d4878 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java @@ -63,7 +63,7 @@ public class RegisterCommand { ), false); return; } - String hash = AuthHelper.hashPass(pass1.toCharArray()); + String hash = AuthHelper.hashPassword(pass1.toCharArray()); // JSON object holding password (may hold some other info in the future) JsonObject playerdata = new JsonObject(); playerdata.addProperty("password", hash); diff --git a/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java b/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java index f2a7ef8..3934c70 100644 --- a/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java +++ b/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java @@ -12,7 +12,6 @@ import net.minecraft.text.LiteralText; import net.minecraft.util.ActionResult; import net.minecraft.util.TypedActionResult; import net.minecraft.util.math.BlockPos; -import org.samo_lego.simpleauth.SimpleAuth; import org.samo_lego.simpleauth.mixin.BlockUpdateS2CPacketAccessor; import org.samo_lego.simpleauth.storage.PlayerCache; @@ -81,8 +80,8 @@ public class AuthEventHandler { return; } // Ugly fix for #13 - player.setInvulnerable(SimpleAuth.config.experimental.playerInvulnerable); - player.setInvisible(SimpleAuth.config.experimental.playerInvisible); + player.setInvulnerable(config.experimental.playerInvulnerable); + player.setInvisible(config.experimental.playerInvisible); // Invalidating session playerCache.wasAuthenticated = false; @@ -91,6 +90,7 @@ public class AuthEventHandler { else { deauthenticatePlayer(player); playerCache = deauthenticatedUsers.get(uuid); + playerCache.wasOnFire = false; } if(config.main.spawnOnJoin) diff --git a/src/main/java/org/samo_lego/simpleauth/storage/AuthConfig.java b/src/main/java/org/samo_lego/simpleauth/storage/AuthConfig.java index e62e1fe..3a0e2d2 100644 --- a/src/main/java/org/samo_lego/simpleauth/storage/AuthConfig.java +++ b/src/main/java/org/samo_lego/simpleauth/storage/AuthConfig.java @@ -115,6 +115,8 @@ public class AuthConfig { public boolean allowItemUse = false; // Allows attacking mobs public boolean allowEntityPunch = false; + // Whether to use BCrypt instead of Argon2 (GLIBC_2.25 error) + public boolean useBCryptLibrary = false; } private static final Gson gson = new GsonBuilder() .setPrettyPrinting() diff --git a/src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java b/src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java index 4c68820..1e6301f 100644 --- a/src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java +++ b/src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java @@ -17,7 +17,7 @@ public class PlayerCache { public long validUntil; public int lastAir = 300; - public boolean wasOnFire = false; + public boolean wasOnFire; public String lastDim; public double lastX; @@ -44,6 +44,9 @@ public class PlayerCache { this.lastY = player.getY(); this.lastZ = player.getZ(); } + else { + this.wasOnFire = false; + } if(DB.isUserRegistered(uuid)) { String data = DB.getData(uuid); @@ -73,10 +76,10 @@ public class PlayerCache { if (lastLoc != null) { // Getting DB coords JsonObject lastLocation = gson.fromJson(lastLoc.getAsString(), JsonObject.class); - this.lastDim = lastLocation.get("dim").getAsString(); - this.lastX = lastLocation.get("x").getAsDouble(); - this.lastY = lastLocation.get("y").getAsDouble(); - this.lastZ = lastLocation.get("z").getAsDouble(); + this.lastDim = lastLocation.get("dim").isJsonNull() ? config.worldSpawn.dimension : lastLocation.get("dim").getAsString(); + this.lastX = lastLocation.get("x").isJsonNull() ? config.worldSpawn.x : lastLocation.get("x").getAsDouble(); + this.lastY = lastLocation.get("y").isJsonNull() ? config.worldSpawn.y : lastLocation.get("y").getAsDouble(); + this.lastZ = lastLocation.get("z").isJsonNull() ? config.worldSpawn.z : lastLocation.get("z").getAsDouble(); // Removing location data from DB json.remove("lastLocation"); diff --git a/src/main/java/org/samo_lego/simpleauth/utils/AuthHelper.java b/src/main/java/org/samo_lego/simpleauth/utils/AuthHelper.java index 5938248..9ad0d3f 100644 --- a/src/main/java/org/samo_lego/simpleauth/utils/AuthHelper.java +++ b/src/main/java/org/samo_lego/simpleauth/utils/AuthHelper.java @@ -2,68 +2,55 @@ package org.samo_lego.simpleauth.utils; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import de.mkammerer.argon2.Argon2; -import de.mkammerer.argon2.Argon2Factory; import org.samo_lego.simpleauth.SimpleAuth; +import org.samo_lego.simpleauth.utils.hashing.HasherArgon2; +import org.samo_lego.simpleauth.utils.hashing.HasherBCrypt; -import static org.samo_lego.simpleauth.utils.SimpleLogger.logError; +import static org.samo_lego.simpleauth.SimpleAuth.config; public class AuthHelper { - - // Creating the instance - private static final Argon2 argon2 = Argon2Factory.create(); - // Json parser private static final JsonParser parser = new JsonParser(); // Returns 1 if password is correct, 0 if not // and -1 if user is not registered yet public static int checkPass(String uuid, char[] pass) { - if(SimpleAuth.config.main.enableGlobalPassword) { + if(config.main.enableGlobalPassword) { // We have global password enabled - try { - return argon2.verify(SimpleAuth.config.main.globalPassword, pass) ? 1 : 0; - } - catch (Error e) { - logError("Argon2 error: " + e); - return 0; - } finally { - // Wipe confidential data - argon2.wipeArray(pass); - } + return verifyPassword(pass, config.main.globalPassword) ? 1 : 0; } else { - try { - String hashed; - // Password from cache - if(SimpleAuth.deauthenticatedUsers.containsKey(uuid)) - hashed = SimpleAuth.deauthenticatedUsers.get(uuid).password; - // Hashed password from DB - else { - JsonObject json = parser.parse(SimpleAuth.DB.getData(uuid)).getAsJsonObject(); - hashed = json.get("password").getAsString(); - } + String hashed; + // Password from cache + if(SimpleAuth.deauthenticatedUsers.containsKey(uuid)) + hashed = SimpleAuth.deauthenticatedUsers.get(uuid).password; - if(hashed.equals("")) - return -1; // User is not yet registered - // Verify password - return argon2.verify(hashed, pass) ? 1 : 0; - } catch (Error e) { - logError("Argon2 error: " + e); - return 0; - } finally { - // Wipe confidential data - argon2.wipeArray(pass); + // Hashed password from DB + else { + JsonObject json = parser.parse(SimpleAuth.DB.getData(uuid)).getAsJsonObject(); + hashed = json.get("password").getAsString(); } + + if(hashed.equals("")) + return -1; // User is not yet registered + + // Verify password + return verifyPassword(pass, hashed) ? 1 : 0; } } - // Hashing the password with the Argon2 power - public static String hashPass(char[] pass) { - try { - return argon2.hash(10, 65536, 1, pass); - } catch (Error e) { - logError(e.getMessage()); - } - return null; + + public static String hashPassword(char[] pass) { + if(config.experimental.useBCryptLibrary) + return HasherBCrypt.hash(pass); + else + return HasherArgon2.hash(pass); + } + + + private static boolean verifyPassword(char[] pass, String hashed) { + if(config.experimental.useBCryptLibrary) + return HasherBCrypt.verify(pass, hashed); + else + return HasherArgon2.verify(pass, hashed); } } diff --git a/src/main/java/org/samo_lego/simpleauth/utils/hashing/HasherArgon2.java b/src/main/java/org/samo_lego/simpleauth/utils/hashing/HasherArgon2.java new file mode 100644 index 0000000..ed32931 --- /dev/null +++ b/src/main/java/org/samo_lego/simpleauth/utils/hashing/HasherArgon2.java @@ -0,0 +1,35 @@ +package org.samo_lego.simpleauth.utils.hashing; + +import de.mkammerer.argon2.Argon2; +import de.mkammerer.argon2.Argon2Factory; + +import static org.samo_lego.simpleauth.utils.SimpleLogger.logError; + +public class HasherArgon2 { + + // Creating the instance + private static final Argon2 HASHER = Argon2Factory.create(); + + public static boolean verify(char[] pass, String hashed) { + try { + return HASHER.verify(hashed, pass); + } + catch (Error e) { + logError("Argon2 password verification error: " + e); + return false; + } finally { + // Wipe confidential data + HASHER.wipeArray(pass); + } + } + + // Hashing the password with the Argon2 power + public static String hash(char[] pass) { + try { + return HASHER.hash(10, 65536, 1, pass); + } catch (Error e) { + logError("Argon2 password hashing error: " + e); + } + return null; + } +} diff --git a/src/main/java/org/samo_lego/simpleauth/utils/hashing/HasherBCrypt.java b/src/main/java/org/samo_lego/simpleauth/utils/hashing/HasherBCrypt.java new file mode 100644 index 0000000..afdf72d --- /dev/null +++ b/src/main/java/org/samo_lego/simpleauth/utils/hashing/HasherBCrypt.java @@ -0,0 +1,28 @@ +package org.samo_lego.simpleauth.utils.hashing; + +import at.favre.lib.crypto.bcrypt.BCrypt; + +import static org.samo_lego.simpleauth.utils.SimpleLogger.logError; + +public class HasherBCrypt { + + public static boolean verify(char[] pass, String hashed) { + try { + return BCrypt.verifyer().verify(pass, hashed).verified; + } + catch (Error e) { + logError("BCrypt password verification error: " + e); + return false; + } + } + + // Hashing the password with the Argon2 power + public static String hash(char[] pass) { + try { + return BCrypt.withDefaults().hashToString(12, pass); + } catch (Error e) { + logError("BCrypt password hashing error: " + e); + } + return null; + } +}