From 2fbc3d6b2cf2a296b74bd806f91252e1363b9d37 Mon Sep 17 00:00:00 2001 From: Quackster Date: Thu, 15 Sep 2022 20:35:42 +1000 Subject: [PATCH] Move from Libsodium to native Java Argon2id hashing --- Havana-Server/build.gradle | 8 +++-- .../main/java/org/alexdev/havana/Havana.java | 11 +++++++ .../alexdev/havana/dao/mysql/PlayerDao.java | 33 +++---------------- .../admin/RecoverAccountCommand.java | 3 +- .../havana/game/player/PlayerManager.java | 21 ++++++++++++ .../controllers/site/ProfileController.java | 3 +- .../controllers/site/RecoveryController.java | 3 +- .../controllers/site/RegisterController.java | 3 +- 8 files changed, 50 insertions(+), 35 deletions(-) diff --git a/Havana-Server/build.gradle b/Havana-Server/build.gradle index 3015d21..a8dbe2b 100644 --- a/Havana-Server/build.gradle +++ b/Havana-Server/build.gradle @@ -47,10 +47,14 @@ dependencies { // https://mvnrepository.com/artifact/com.google.code.gson/gson implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.0' + // https://mvnrepository.com/artifact/org.springframework.security/spring-security-crypto + implementation group: 'org.springframework.security', name: 'spring-security-crypto', version: '5.7.3' + + // https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on + implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.70' + implementation 'com.maxmind.geoip2:geoip2:2.12.0' implementation 'com.github.bhlangonijr:chesslib:1.1.1' - implementation 'com.goterl:lazysodium-java:5.0.1' - implementation "net.java.dev.jna:jna:5.8.0" } // Create fat jar with libraries inside of it. diff --git a/Havana-Server/src/main/java/org/alexdev/havana/Havana.java b/Havana-Server/src/main/java/org/alexdev/havana/Havana.java index cc0f133..d7b41c9 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/Havana.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/Havana.java @@ -39,6 +39,7 @@ import org.alexdev.havana.util.config.writer.DefaultConfigWriter; import org.alexdev.havana.util.config.writer.GameConfigWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.security.crypto.argon2.Argon2PasswordEncoder; import java.io.IOException; import java.net.UnknownHostException; @@ -353,4 +354,14 @@ public class Havana { public static Gson getGson() { return gson; } + + /** + * Get the Argon2 password encoder instance. + * + * @return + */ + public static Argon2PasswordEncoder getPasswordEncoder() { + var encoder =new Argon2PasswordEncoder(16, 32, 1, 65536, 2); + return encoder; + } } \ No newline at end of file diff --git a/Havana-Server/src/main/java/org/alexdev/havana/dao/mysql/PlayerDao.java b/Havana-Server/src/main/java/org/alexdev/havana/dao/mysql/PlayerDao.java index 1444ae8..47f06e1 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/dao/mysql/PlayerDao.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/dao/mysql/PlayerDao.java @@ -1,11 +1,9 @@ package org.alexdev.havana.dao.mysql; -import com.goterl.lazysodium.LazySodiumJava; -import com.goterl.lazysodium.SodiumJava; -import com.goterl.lazysodium.interfaces.PwHash; import org.alexdev.havana.dao.Storage; import org.alexdev.havana.game.player.Player; import org.alexdev.havana.game.player.PlayerDetails; +import org.alexdev.havana.game.player.PlayerManager; import org.alexdev.havana.util.DateUtil; import java.nio.charset.StandardCharsets; @@ -15,7 +13,6 @@ import java.util.List; import java.util.concurrent.TimeUnit; public class PlayerDao { - public static final LazySodiumJava LIB_SODIUM = new LazySodiumJava(new SodiumJava()); private static String figureBlacklist1 = "hd-180-1.hr-100-61.ch-210-66.lg-270-82.sh-290-80"; public static void resetOnline() { @@ -353,13 +350,10 @@ public class PlayerDao { resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { - byte[] hashedPassword = (resultSet.getString("password") + '\0').getBytes(StandardCharsets.UTF_8); - byte[] pass = password.getBytes(StandardCharsets.UTF_8); + String databasePassword = resultSet.getString("password"); - success = ((PwHash.Native) LIB_SODIUM).cryptoPwHashStrVerify(hashedPassword, pass, pass.length); - - if (success) { - fill(playerDetails, resultSet); + if (PlayerManager.getInstance().passwordMatches(databasePassword, password)) { + success = true; } } @@ -1010,23 +1004,4 @@ public class PlayerDao { row.getLong("totem_effect_expiry"), row.getLong("trade_ban_expiration"), row.getInt("favourite_group"), row.getString("created_at")); } - - public static String createPassword(String password) throws Exception { - byte[] pw = password.getBytes(); - byte[] outputHash = new byte[PwHash.STR_BYTES]; - PwHash.Native pwHash = (PwHash.Native) PlayerDao.LIB_SODIUM; - boolean success = pwHash.cryptoPwHashStr( - outputHash, - pw, - pw.length, - PwHash.OPSLIMIT_INTERACTIVE, - PwHash.MEMLIMIT_INTERACTIVE - ); - - if (!success) { - throw new Exception("Password creation was a failure!"); - } - - return new String(outputHash).replace((char)0 + "", ""); - } } diff --git a/Havana-Server/src/main/java/org/alexdev/havana/game/commands/registered/admin/RecoverAccountCommand.java b/Havana-Server/src/main/java/org/alexdev/havana/game/commands/registered/admin/RecoverAccountCommand.java index f49db8a..6acbe32 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/game/commands/registered/admin/RecoverAccountCommand.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/game/commands/registered/admin/RecoverAccountCommand.java @@ -6,6 +6,7 @@ import org.alexdev.havana.game.entity.Entity; import org.alexdev.havana.game.entity.EntityType; import org.alexdev.havana.game.player.Player; import org.alexdev.havana.game.player.PlayerDetails; +import org.alexdev.havana.game.player.PlayerManager; import org.alexdev.havana.game.player.PlayerRank; import org.alexdev.havana.messages.outgoing.alerts.ALERT; @@ -40,7 +41,7 @@ public class RecoverAccountCommand extends Command { } player.send(new ALERT(targetUser.getName() + "'s password has been reset to: changeme123")); - PlayerDao.setPassword(targetUser.getId(), PlayerDao.createPassword("changeme123")); + PlayerDao.setPassword(targetUser.getId(), PlayerManager.getInstance().createPassword("changeme123")); } @Override diff --git a/Havana-Server/src/main/java/org/alexdev/havana/game/player/PlayerManager.java b/Havana-Server/src/main/java/org/alexdev/havana/game/player/PlayerManager.java index be06e2a..6a1ab03 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/game/player/PlayerManager.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/game/player/PlayerManager.java @@ -1,5 +1,6 @@ package org.alexdev.havana.game.player; +import org.alexdev.havana.Havana; import org.alexdev.havana.dao.mysql.PlayerDao; import org.alexdev.havana.game.GameScheduler; import org.alexdev.havana.game.player.statistics.PlayerStatistic; @@ -283,6 +284,26 @@ public class PlayerManager { return activePlayers; } + /** + * Create password hash + * + * @param password password to hash + * @return hashed password + * @throws Exception + */ + public String createPassword(String password) { + return Havana.getPasswordEncoder().encode(password); + } + + /** + * Get whether the hash matches the entered password. + * + * @return true, if success + */ + public boolean passwordMatches(String databasePassword, String enteredPassword) { + return Havana.getPasswordEncoder().matches(enteredPassword, databasePassword); + } + /** * Get daily player peak * diff --git a/Havana-Web/src/main/java/org/alexdev/http/controllers/site/ProfileController.java b/Havana-Web/src/main/java/org/alexdev/http/controllers/site/ProfileController.java index 0219fe6..8ef9142 100644 --- a/Havana-Web/src/main/java/org/alexdev/http/controllers/site/ProfileController.java +++ b/Havana-Web/src/main/java/org/alexdev/http/controllers/site/ProfileController.java @@ -9,6 +9,7 @@ import org.alexdev.havana.dao.mysql.PlayerStatisticsDao; import org.alexdev.havana.dao.mysql.WardrobeDao; import org.alexdev.havana.game.misc.figure.FigureManager; import org.alexdev.havana.game.player.PlayerDetails; +import org.alexdev.havana.game.player.PlayerManager; import org.alexdev.havana.game.player.Wardrobe; import org.alexdev.havana.game.player.statistics.PlayerStatistic; import org.alexdev.havana.game.player.statistics.PlayerStatisticManager; @@ -229,7 +230,7 @@ public class ProfileController { webConnection.session().set("alertMessage", "Your password has been changed successfully. You will need to login again."); webConnection.session().set("alertColour", "green"); - PlayerDao.setPassword(playerDetails.getId(), PlayerDao.createPassword(newPassword)); + PlayerDao.setPassword(playerDetails.getId(), PlayerManager.getInstance().createPassword(newPassword)); logout = true; } } diff --git a/Havana-Web/src/main/java/org/alexdev/http/controllers/site/RecoveryController.java b/Havana-Web/src/main/java/org/alexdev/http/controllers/site/RecoveryController.java index 0b434d7..9f198b5 100644 --- a/Havana-Web/src/main/java/org/alexdev/http/controllers/site/RecoveryController.java +++ b/Havana-Web/src/main/java/org/alexdev/http/controllers/site/RecoveryController.java @@ -4,6 +4,7 @@ import org.alexdev.duckhttpd.server.connection.WebConnection; import org.alexdev.havana.dao.mysql.PlayerDao; import org.alexdev.havana.dao.mysql.PlayerStatisticsDao; import org.alexdev.havana.game.player.PlayerDetails; +import org.alexdev.havana.game.player.PlayerManager; import org.alexdev.havana.game.player.statistics.PlayerStatistic; import org.alexdev.havana.util.DateUtil; import org.alexdev.havana.util.config.GameConfiguration; @@ -137,7 +138,7 @@ public class RecoveryController { webConnection.session().set("alertMessage", "Your password has been changed successfully."); webConnection.session().set("alertColour", "green"); - PlayerDao.setPassword(userId, PlayerDao.createPassword(newPassword)); + PlayerDao.setPassword(userId, PlayerManager.getInstance().createPassword(newPassword)); EmailDao.removeRecoveryCode(userId); } } diff --git a/Havana-Web/src/main/java/org/alexdev/http/controllers/site/RegisterController.java b/Havana-Web/src/main/java/org/alexdev/http/controllers/site/RegisterController.java index a67f3b9..7d5a648 100644 --- a/Havana-Web/src/main/java/org/alexdev/http/controllers/site/RegisterController.java +++ b/Havana-Web/src/main/java/org/alexdev/http/controllers/site/RegisterController.java @@ -9,6 +9,7 @@ import org.alexdev.havana.dao.mysql.PlayerDao; import org.alexdev.havana.dao.mysql.PlayerStatisticsDao; import org.alexdev.havana.dao.mysql.ReferredDao; import org.alexdev.havana.game.misc.figure.FigureManager; +import org.alexdev.havana.game.player.PlayerManager; import org.alexdev.havana.util.DateUtil; import org.alexdev.havana.util.FigureUtil; import org.alexdev.havana.util.config.GameConfiguration; @@ -158,7 +159,7 @@ public class RegisterController { return; } - String hashedPassword = PlayerDao.createPassword(webConnection.session().getString("registerPassword")); + String hashedPassword = PlayerManager.getInstance().createPassword(webConnection.session().getString("registerPassword")); int userId = RegisterDao.newUser( webConnection.session().getString("registerUsername"), hashedPassword,