Merge pull request #85 from AuthMe-Team/358-encryptn-mthd-refactor

358 encryptn mthd refactor
This commit is contained in:
ljacqu 2015-12-30 21:50:30 +01:00
commit 9343cfe9da
94 changed files with 2172 additions and 2073 deletions

View File

@ -82,8 +82,6 @@ typing commands or using the inventory. It can also kick players with uncommonly
<li>MyBB: MYBB</li>
<li>IPB3: IPB3</li>
<li>PhpFusion: PHPFUSION</li>
<li><del>Xenforo SHA1: XFSHA1</del> <strong>(Deprecated)</strong></li>
<li><del>Xenforo SHA256: XFSHA256</del> <strong>(Deprecated)</strong></li>
<li>Joomla: JOOMLA</li>
<li>WBB3: WBB3*</li>
<li>SHA512: SHA512</li>
@ -92,7 +90,7 @@ typing commands or using the inventory. It can also kick players with uncommonly
</ul></li>
<li>Custom MySQL tables/columns names (useful with forums databases)</li>
<li><strong>Cached database queries!</strong></li>
<li><strong>Full compatible with Citizens2, CombatTag, CombatTagPlus and ChestShop!</strong></li>
<li><strong>Fully compatible with Citizens2, CombatTag, CombatTagPlus and ChestShop!</strong></li>
<li>Compatible with Minecraft mods like <strong>BuildCraft or RedstoneCraft</strong></li>
<li>Restricted users (associate a Username with an IP)</li>
<li>Protect player's inventory until a correct Authentication</li>
@ -120,7 +118,7 @@ typing commands or using the inventory. It can also kick players with uncommonly
</li><li><a href="http://dev.bukkit.org/server-mods/authme-reloaded/pages/web-site-integration/">Website Integration</a>
</li><li><a href="https://raw.githubusercontent.com/Xephi/AuthMeReloaded/master/src/main/resources/config.yml">Click here for an example of the Config file</a>
</li><li><a href="http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-import-database-from-rakamak/">How to convert from Rakamak</a>
</li><li>Convert from FlatFile (auths.db but not the sqlite one ) to MySQL: /converter
</li><li>Convert from FlatFile (auths.db but not the sqlite one) to MySQL: /converter
</li></ul>
<hr>
@ -139,5 +137,5 @@ GameHosting.it is leader in Italy as Game Server Provider. With its own DataCent
#####Credits
<p>Team members: look at the <a href="https://github.com/AuthMe-Team/AuthMeReloaded/blob/master/team.txt">team.txt file</a>
<p>Credit for old version of the plugin to: d4rkwarriors, fabe1337 , Whoami2 and pomo4ka</p>
<p>Credit for old version of the plugin to: d4rkwarriors, fabe1337, Whoami2 and pomo4ka</p>
<p>Thanks also to: AS1LV3RN1NJA, Hoeze and eprimex</p>

View File

@ -43,6 +43,7 @@ import fr.xephi.authme.permission.PlayerPermission;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.OtherAccounts;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.Spawn;
@ -101,12 +102,13 @@ public class AuthMe extends JavaPlugin {
private Messages messages;
private JsonCache playerBackup;
private ModuleManager moduleManager;
private PasswordSecurity passwordSecurity;
private DataSource database;
// Public Instances
public NewAPI api;
public SendMailSSL mail;
public DataManager dataManager;
public DataSource database;
public OtherAccounts otherAccounts;
public Location essentialsSpawn;
@ -192,7 +194,6 @@ public class AuthMe extends JavaPlugin {
/**
* Method called when the server enables the plugin.
* @see org.bukkit.plugin.Plugin#onEnable()
*/
@Override
public void onEnable() {
@ -209,12 +210,26 @@ public class AuthMe extends JavaPlugin {
return;
}
// Set up messages
// Set up messages & password security
messages = Messages.getInstance();
// Connect to the database and setup tables
try {
setupDatabase();
} catch (Exception e) {
ConsoleLogger.writeStackTrace(e);
ConsoleLogger.showError(e.getMessage());
ConsoleLogger.showError("Fatal error occurred during database connection! Authme initialization ABORTED!");
stopOrUnload();
return;
}
passwordSecurity = new PasswordSecurity(getDataSource(), Settings.getPasswordHash,
Bukkit.getPluginManager(), Settings.supportOldPassword);
// Set up the permissions manager and command handler
permsMan = initializePermissionsManager();
commandHandler = initializeCommandHandler(permsMan, messages);
commandHandler = initializeCommandHandler(permsMan, messages, passwordSecurity);
// Set up the module manager
setupModuleManager();
@ -256,16 +271,7 @@ public class AuthMe extends JavaPlugin {
// Do a backup on start
new PerformBackup(plugin).doBackup(PerformBackup.BackupCause.START);
// Connect to the database and setup tables
try {
setupDatabase();
} catch (Exception e) {
ConsoleLogger.writeStackTrace(e);
ConsoleLogger.showError(e.getMessage());
ConsoleLogger.showError("Fatal error occurred during database connection! Authme initialization ABORTED!");
stopOrUnload();
return;
}
// Setup the inventory backup
playerBackup = new JsonCache();
@ -412,11 +418,12 @@ public class AuthMe extends JavaPlugin {
}
}
private CommandHandler initializeCommandHandler(PermissionsManager permissionsManager, Messages messages) {
private CommandHandler initializeCommandHandler(PermissionsManager permissionsManager, Messages messages,
PasswordSecurity passwordSecurity) {
HelpProvider helpProvider = new HelpProvider(permissionsManager);
Set<CommandDescription> baseCommands = CommandInitializer.buildCommands();
CommandMapper mapper = new CommandMapper(baseCommands, messages, permissionsManager, helpProvider);
CommandService commandService = new CommandService(this, mapper, helpProvider, messages);
CommandService commandService = new CommandService(this, mapper, helpProvider, messages, passwordSecurity);
return new CommandHandler(commandService);
}
@ -506,11 +513,6 @@ public class AuthMe extends JavaPlugin {
}
}
/**
* Method onDisable.
*
* @see org.bukkit.plugin.Plugin#onDisable()
*/
@Override
public void onDisable() {
// Save player data
@ -525,7 +527,9 @@ public class AuthMe extends JavaPlugin {
new PerformBackup(plugin).doBackup(PerformBackup.BackupCause.STOP);
// Unload modules
moduleManager.unloadModules();
if (moduleManager != null) {
moduleManager.unloadModules();
}
// Close the database
if (database != null) {
@ -539,16 +543,13 @@ public class AuthMe extends JavaPlugin {
// Stop/unload the server/plugin as defined in the configuration
public void stopOrUnload() {
if (Settings.isStopEnabled) {
ConsoleLogger.showError("THE SERVER IS GOING TO SHUTDOWN AS DEFINED IN THE CONFIGURATION!");
ConsoleLogger.showError("THE SERVER IS GOING TO SHUT DOWN AS DEFINED IN THE CONFIGURATION!");
server.shutdown();
} else {
server.getPluginManager().disablePlugin(AuthMe.getInstance());
}
}
/**
* Method setupDatabase.
*/
public void setupDatabase() throws Exception {
if (database != null)
database.close();
@ -574,14 +575,15 @@ public class AuthMe extends JavaPlugin {
int accounts = database.getAccountsRegistered();
if (accounts >= 4000) {
ConsoleLogger.showError("YOU'RE USING THE SQLITE DATABASE WITH "
+ accounts + "+ ACCOUNTS, FOR BETTER PERFORMANCES, PLEASE UPGRADE TO MYSQL!!");
+ accounts + "+ ACCOUNTS; FOR BETTER PERFORMANCE, PLEASE UPGRADE TO MYSQL!!");
}
}
});
}
if (Settings.getDataSource == DataSource.DataSourceType.FILE) {
ConsoleLogger.showError("FlatFile backend has been detected and is now deprecated, it will be changed to SQLite... Connection will be impossible until conversion is done!");
ConsoleLogger.showError("FlatFile backend has been detected and is now deprecated, it will be changed " +
"to SQLite... Connection will be impossible until conversion is done!");
ForceFlatToSqlite converter = new ForceFlatToSqlite(database);
DataSource source = converter.run();
if (source != null) {
@ -590,12 +592,13 @@ public class AuthMe extends JavaPlugin {
}
// TODO: Move this to another place maybe ?
if (Settings.getPasswordHash == HashAlgorithm.PLAINTEXT)
{
ConsoleLogger.showError("Your HashAlgorithm has been detected has plaintext and is now deprecrated, it will be changed and hashed now to AuthMe default hashing method");
for (PlayerAuth auth : database.getAllAuths())
{
auth.setHash(PasswordSecurity.getHash(HashAlgorithm.SHA256, auth.getHash(), auth.getNickname()));
if (Settings.getPasswordHash == HashAlgorithm.PLAINTEXT) {
ConsoleLogger.showError("Your HashAlgorithm has been detected as plaintext and is now deprecated; " +
"it will be changed and hashed now to the AuthMe default hashing method");
for (PlayerAuth auth : database.getAllAuths()) {
EncryptedPassword encryptedPassword = passwordSecurity.computeHash(
HashAlgorithm.SHA256, auth.getPassword().getHash(), auth.getNickname());
auth.setPassword(encryptedPassword);
database.updatePassword(auth);
}
Settings.setValue("settings.security.passwordHash", "SHA256");
@ -720,8 +723,8 @@ public class AuthMe extends JavaPlugin {
}
// Save Player Data
public void savePlayer(Player player) {
if ((Utils.isNPC(player)) || (Utils.isUnrestricted(player))) {
private void savePlayer(Player player) {
if (Utils.isNPC(player) || Utils.isUnrestricted(player)) {
return;
}
String name = player.getName().toLowerCase();
@ -836,10 +839,10 @@ public class AuthMe extends JavaPlugin {
// Return the AuthMe spawn point
private Location getAuthMeSpawn(Player player) {
if ((!database.isAuthAvailable(player.getName().toLowerCase()) || !player.hasPlayedBefore()) && (Spawn.getInstance().getFirstSpawn() != null)) {
if ((!database.isAuthAvailable(player.getName().toLowerCase()) || !player.hasPlayedBefore())
&& (Spawn.getInstance().getFirstSpawn() != null)) {
return Spawn.getInstance().getFirstSpawn();
}
if (Spawn.getInstance().getSpawn() != null) {
} else if (Spawn.getInstance().getSpawn() != null) {
return Spawn.getInstance().getSpawn();
}
return player.getWorld().getSpawnLocation();
@ -889,8 +892,7 @@ public class AuthMe extends JavaPlugin {
*
*/
@Deprecated
public void getVerygamesIp(final Player player)
{
public void getVerygamesIp(final Player player) {
final String name = player.getName().toLowerCase();
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable(){
@Override
@ -986,4 +988,12 @@ public class AuthMe extends JavaPlugin {
return management;
}
public DataSource getDataSource() {
return database;
}
public PasswordSecurity getPasswordSecurity() {
return passwordSecurity;
}
}

View File

@ -4,7 +4,7 @@ import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.util.Utils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@ -12,34 +12,37 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import java.security.NoSuchAlgorithmException;
/**
* Deprecated API of AuthMe. Please use {@link NewAPI} instead.
*/
@Deprecated
public class API {
public static final String newline = System.getProperty("line.separator");
public static AuthMe instance;
private static PasswordSecurity passwordSecurity;
/**
* Constructor for API.
* Constructor for the deprecated API.
*
* @param instance AuthMe
*/
@Deprecated
public API(AuthMe instance) {
API.instance = instance;
passwordSecurity = instance.getPasswordSecurity();
}
/**
* Hook into AuthMe
* Hook into AuthMe.
*
* @return AuthMe instance
*/
@Deprecated
public static AuthMe hookAuthMe() {
if (instance != null)
if (instance != null) {
return instance;
}
Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin("AuthMe");
if (plugin == null || !(plugin instanceof AuthMe)) {
return null;
@ -49,9 +52,10 @@ public class API {
}
/**
* @param player
* Return whether the player is authenticated.
*
* @return true if player is authenticate
* @param player The player to verify
* @return true if the player is authenticated
*/
@Deprecated
public static boolean isAuthenticated(Player player) {
@ -59,8 +63,9 @@ public class API {
}
/**
* @param player
* Return whether the player is unrestricted.
*
* @param player The player to verify
* @return true if the player is unrestricted
*/
@Deprecated
@ -68,13 +73,6 @@ public class API {
return Utils.isUnrestricted(player);
}
/**
* Method getLastLocation.
*
* @param player Player
*
* @return Location
*/
@Deprecated
public static Location getLastLocation(Player player) {
try {
@ -92,13 +90,6 @@ public class API {
}
}
/**
* Method setPlayerInventory.
*
* @param player Player
* @param content ItemStack[]
* @param armor ItemStack[]
*/
@Deprecated
public static void setPlayerInventory(Player player, ItemStack[] content,
ItemStack[] armor) {
@ -110,93 +101,72 @@ public class API {
}
/**
* @param playerName
* Check whether the given player name is registered.
*
* @param playerName The player name to verify
* @return true if player is registered
*/
@Deprecated
public static boolean isRegistered(String playerName) {
String player = playerName.toLowerCase();
return instance.database.isAuthAvailable(player);
return instance.getDataSource().isAuthAvailable(player);
}
/**
* @param playerName String
* @param passwordToCheck String
* Check the password for the given player.
*
* @return true if the password is correct , false else
* @param playerName The name of the player
* @param passwordToCheck The password to check
* @return true if the password is correct, false otherwise
*/
@Deprecated
public static boolean checkPassword(String playerName,
String passwordToCheck) {
if (!isRegistered(playerName))
return false;
String player = playerName.toLowerCase();
PlayerAuth auth = instance.database.getAuth(player);
try {
return PasswordSecurity.comparePasswordWithHash(passwordToCheck, auth.getHash(), playerName);
} catch (NoSuchAlgorithmException e) {
return false;
}
public static boolean checkPassword(String playerName, String passwordToCheck) {
return isRegistered(playerName) && passwordSecurity.comparePassword(passwordToCheck, playerName);
}
/**
* Register a player
* Register a player.
*
* @param playerName String
* @param password String
*
* @return true if the player is register correctly
* @param playerName The name of the player
* @param password The password
* @return true if the player was registered correctly
*/
@Deprecated
public static boolean registerPlayer(String playerName, String password) {
try {
String name = playerName.toLowerCase();
String hash = PasswordSecurity.getHash(Settings.getPasswordHash, password, name);
if (isRegistered(name)) {
return false;
}
PlayerAuth auth = new PlayerAuth(name, hash, "198.18.0.1", 0, "your@email.com", playerName);
return instance.database.saveAuth(auth);
} catch (NoSuchAlgorithmException ex) {
String name = playerName.toLowerCase();
EncryptedPassword encryptedPassword = passwordSecurity.computeHash(password, name);
if (isRegistered(name)) {
return false;
}
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.password(encryptedPassword)
.lastLogin(0)
.realName(playerName)
.build();
return instance.getDataSource().saveAuth(auth);
}
/**
* Force a player to login
* Force a player to log in.
*
* @param player * player
* @param player The player to log in
*/
@Deprecated
public static void forceLogin(Player player) {
instance.getManagement().performLogin(player, "dontneed", true);
}
/**
* Method getPlugin.
*
* @return AuthMe
*/
@Deprecated
public AuthMe getPlugin() {
return instance;
}
/**
* @param player
* Check whether the player is an NPC.
*
* @return true if player is a npc
*/
@Deprecated
public boolean isaNPC(Player player) {
return Utils.isNPC(player);
}
/**
* @param player
*
* @return true if player is a npc
* @param player The player to verify
* @return true if player is an npc
*/
@Deprecated
public boolean isNPC(Player player) {

View File

@ -3,8 +3,7 @@ package fr.xephi.authme.api;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.util.Utils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@ -12,9 +11,8 @@ import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.security.NoSuchAlgorithmException;
/**
* The current API of AuthMe.
*/
public class NewAPI {
@ -42,33 +40,36 @@ public class NewAPI {
/**
* Hook into AuthMe
*
* @return AuthMe plugin
* @return The API object
*/
public static NewAPI getInstance() {
if (singleton != null)
if (singleton != null) {
return singleton;
}
Plugin p = Bukkit.getServer().getPluginManager().getPlugin("AuthMe");
if (p == null || !(p instanceof AuthMe)) {
return null;
}
AuthMe authme = (AuthMe) p;
singleton = (new NewAPI(authme));
singleton = new NewAPI(authme);
return singleton;
}
/**
* Method getPlugin.
* Return the plugin instance.
*
* @return AuthMe
* @return The AuthMe instance
*/
public AuthMe getPlugin() {
return plugin;
}
/**
* @param player
* Return whether the given player is authenticated.
*
* @return true if player is authenticate
* @param player The player to verify
*
* @return true if the player is authenticated
*/
public boolean isAuthenticated(Player player) {
return PlayerCache.getInstance().isAuthenticated(player.getName());
@ -93,15 +94,15 @@ public class NewAPI {
}
/**
* Method getLastLocation.
* Get the last location of a player.
*
* @param player Player
* @param player Player The player to process
*
* @return Location
* @return Location The location of the player
*/
public Location getLastLocation(Player player) {
try {
PlayerAuth auth = PlayerCache.getInstance().getAuth(player.getName().toLowerCase());
PlayerAuth auth = PlayerCache.getInstance().getAuth(player.getName());
if (auth != null) {
return new Location(Bukkit.getWorld(auth.getWorld()), auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ());
@ -115,87 +116,83 @@ public class NewAPI {
}
/**
* @param playerName
* Return whether the player is registered.
*
* @return true if player is registered
* @param playerName The player name to check
*
* @return true if player is registered, false otherwise
*/
public boolean isRegistered(String playerName) {
String player = playerName.toLowerCase();
return plugin.database.isAuthAvailable(player);
return plugin.getDataSource().isAuthAvailable(player);
}
/**
* @param playerName String
* @param passwordToCheck String
* Check the password for the given player.
*
* @return true if the password is correct , false else
* @param playerName The player to check the password for
* @param passwordToCheck The password to check
*
* @return true if the password is correct, false otherwise
*/
public boolean checkPassword(String playerName, String passwordToCheck) {
if (!isRegistered(playerName))
return false;
String player = playerName.toLowerCase();
PlayerAuth auth = plugin.database.getAuth(player);
try {
return PasswordSecurity.comparePasswordWithHash(passwordToCheck, auth.getHash(), playerName);
} catch (NoSuchAlgorithmException e) {
return false;
}
return isRegistered(playerName) && plugin.getPasswordSecurity().comparePassword(passwordToCheck, playerName);
}
/**
* Register a player
* Register a player.
*
* @param playerName String
* @param password String
* @param playerName The player to register
* @param password The password to register the player with
*
* @return true if the player is register correctly
* @return true if the player was registered successfully
*/
public boolean registerPlayer(String playerName, String password) {
try {
String name = playerName.toLowerCase();
String hash = PasswordSecurity.getHash(Settings.getPasswordHash, password, name);
if (isRegistered(name)) {
return false;
}
PlayerAuth auth = new PlayerAuth(name, hash, "192.168.0.1", 0, "your@email.com", playerName);
return plugin.database.saveAuth(auth);
} catch (NoSuchAlgorithmException ex) {
String name = playerName.toLowerCase();
EncryptedPassword result = plugin.getPasswordSecurity().computeHash(password, name);
if (isRegistered(name)) {
return false;
}
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.password(result)
.realName(playerName)
.build();
return plugin.getDataSource().saveAuth(auth);
}
/**
* Force a player to login
* Force a player to login.
*
* @param player * player
* @param player The player to log in
*/
public void forceLogin(Player player) {
plugin.getManagement().performLogin(player, "dontneed", true);
}
/**
* Force a player to logout
* Force a player to logout.
*
* @param player * player
* @param player The player to log out
*/
public void forceLogout(Player player) {
plugin.getManagement().performLogout(player);
}
/**
* Force a player to register
* Force a player to register.
*
* @param player * player
* @param password String
* @param player The player to register
* @param password The password to use
*/
public void forceRegister(Player player, String password) {
plugin.getManagement().performRegister(player, password, null);
}
/**
* Force a player to unregister
* Force a player to unregister.
*
* @param player * player
* @param player The player to unregister
*/
public void forceUnregister(Player player) {
plugin.getManagement().performUnregister(player, "", true);

View File

@ -1,11 +1,10 @@
package fr.xephi.authme.cache.auth;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import org.bukkit.Location;
import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.nullToEmpty;
/**
@ -13,14 +12,13 @@ import static com.google.common.base.Strings.nullToEmpty;
public class PlayerAuth {
private String nickname;
private String hash;
private EncryptedPassword password;
private String ip;
private long lastLogin;
private double x;
private double y;
private double z;
private String world;
private String salt;
private int groupId;
private String email;
private String realName;
@ -41,7 +39,7 @@ public class PlayerAuth {
* @param realName String
*/
public PlayerAuth(String nickname, String ip, long lastLogin, String realName) {
this(nickname, "", "", -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName);
this(nickname, new EncryptedPassword(""), -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName);
}
/**
@ -55,7 +53,8 @@ public class PlayerAuth {
* @param realName String
*/
public PlayerAuth(String nickname, double x, double y, double z, String world, String realName) {
this(nickname, "", "", -1, "127.0.0.1", System.currentTimeMillis(), x, y, z, world, "your@email.com", realName);
this(nickname, new EncryptedPassword(""), -1, "127.0.0.1", System.currentTimeMillis(), x, y, z, world,
"your@email.com", realName);
}
/**
@ -68,7 +67,7 @@ public class PlayerAuth {
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String ip, long lastLogin, String realName) {
this(nickname, hash, "", -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName);
this(nickname, new EncryptedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName);
}
/**
@ -82,7 +81,7 @@ public class PlayerAuth {
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String ip, long lastLogin, String email, String realName) {
this(nickname, hash, "", -1, ip, lastLogin, 0, 0, 0, "world", email, realName);
this(nickname, new EncryptedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", email, realName);
}
/**
@ -96,7 +95,7 @@ public class PlayerAuth {
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String salt, String ip, long lastLogin, String realName) {
this(nickname, hash, salt, -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName);
this(nickname, new EncryptedPassword(hash, salt), -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName);
}
/**
@ -113,8 +112,9 @@ public class PlayerAuth {
* @param email String
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String ip, long lastLogin, double x, double y, double z, String world, String email, String realName) {
this(nickname, hash, "", -1, ip, lastLogin, x, y, z, world, email, realName);
public PlayerAuth(String nickname, String hash, String ip, long lastLogin, double x, double y, double z,
String world, String email, String realName) {
this(nickname, new EncryptedPassword(hash), -1, ip, lastLogin, x, y, z, world, email, realName);
}
/**
@ -132,8 +132,10 @@ public class PlayerAuth {
* @param email String
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String salt, String ip, long lastLogin, double x, double y, double z, String world, String email, String realName) {
this(nickname, hash, salt, -1, ip, lastLogin, x, y, z, world, email, realName);
public PlayerAuth(String nickname, String hash, String salt, String ip, long lastLogin, double x, double y,
double z, String world, String email, String realName) {
this(nickname, new EncryptedPassword(hash, salt), -1, ip, lastLogin,
x, y, z, world, email, realName);
}
/**
@ -147,38 +149,37 @@ public class PlayerAuth {
* @param lastLogin long
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String salt, int groupId, String ip, long lastLogin, String realName) {
this(nickname, hash, salt, groupId, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName);
}
/**
* Constructor for PlayerAuth.
*
* @param nickname String
* @param hash String
* @param salt String
* @param groupId int
* @param ip String
* @param lastLogin long
* @param x double
* @param y double
* @param z double
* @param world String
* @param email String
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String salt, int groupId, String ip,
long lastLogin, double x, double y, double z, String world, String email,
String realName) {
long lastLogin, String realName) {
this(nickname, new EncryptedPassword(hash, salt), groupId, ip, lastLogin,
0, 0, 0, "world", "your@email.com", realName);
}
/**
* Constructor for PlayerAuth.
*
* @param nickname String
* @param password String
* @param groupId int
* @param ip String
* @param lastLogin long
* @param x double
* @param y double
* @param z double
* @param world String
* @param email String
* @param realName String
*/
public PlayerAuth(String nickname, EncryptedPassword password, int groupId, String ip, long lastLogin,
double x, double y, double z, String world, String email, String realName) {
this.nickname = nickname.toLowerCase();
this.hash = hash;
this.password = password;
this.ip = ip;
this.lastLogin = lastLogin;
this.x = x;
this.y = y;
this.z = z;
this.world = world;
this.salt = salt;
this.groupId = groupId;
this.email = email;
this.realName = realName;
@ -191,237 +192,108 @@ public class PlayerAuth {
*/
public void set(PlayerAuth auth) {
this.setEmail(auth.getEmail());
this.setHash(auth.getHash());
this.setPassword(auth.getPassword());
this.setIp(auth.getIp());
this.setLastLogin(auth.getLastLogin());
this.setNickname(auth.getNickname());
this.setQuitLocX(auth.getQuitLocX());
this.setQuitLocY(auth.getQuitLocY());
this.setQuitLocZ(auth.getQuitLocZ());
this.setSalt(auth.getSalt());
this.setWorld(auth.getWorld());
this.setRealName(auth.getRealName());
}
/**
* Method setNickname.
*
* @param nickname String
*/
public void setNickname(String nickname) {
this.nickname = nickname.toLowerCase();
}
/**
* Method getNickname.
*
* @return String
*/
public String getNickname() {
return nickname;
}
/**
* Method getRealName.
*
* @return String
*/
public String getRealName() {
return realName;
}
/**
* Method setRealName.
*
* @param realName String
*/
public void setRealName(String realName) {
this.realName = realName;
}
/**
* Method getGroupId.
*
* @return int
*/
public int getGroupId() {
return groupId;
}
/**
* Method getQuitLocX.
*
* @return double
*/
public double getQuitLocX() {
return x;
}
/**
* Method setQuitLocX.
*
* @param d double
*/
public void setQuitLocX(double d) {
this.x = d;
}
/**
* Method getQuitLocY.
*
* @return double
*/
public double getQuitLocY() {
return y;
}
/**
* Method setQuitLocY.
*
* @param d double
*/
public void setQuitLocY(double d) {
this.y = d;
}
/**
* Method getQuitLocZ.
*
* @return double
*/
public double getQuitLocZ() {
return z;
}
/**
* Method setQuitLocZ.
*
* @param d double
*/
public void setQuitLocZ(double d) {
this.z = d;
}
/**
* Method getWorld.
*
* @return String
*/
public String getWorld() {
return world;
}
/**
* Method setWorld.
*
* @param world String
*/
public void setWorld(String world) {
this.world = world;
}
/**
* Method getIp.
*
* @return String
*/
public String getIp() {
return ip;
}
/**
* Method setIp.
*
* @param ip String
*/
public void setIp(String ip) {
this.ip = ip;
}
/**
* Method getLastLogin.
*
* @return long
*/
public long getLastLogin() {
return lastLogin;
}
/**
* Method setLastLogin.
*
* @param lastLogin long
*/
public void setLastLogin(long lastLogin) {
this.lastLogin = lastLogin;
}
/**
* Method getEmail.
*
* @return String
*/
public String getEmail() {
return email;
}
/**
* Method setEmail.
*
* @param email String
*/
public void setEmail(String email) {
this.email = email;
}
/**
* Method getSalt.
*
* @return String
*/
public String getSalt() {
return this.salt;
}
/**
* Method setSalt.
*
* @param salt String
*/
public void setSalt(String salt) {
this.salt = salt;
}
/**
* Method getHash.
*
* @return String
*/
public String getHash() {
if (Settings.getPasswordHash == HashAlgorithm.MD5VB) {
public EncryptedPassword getPassword() {
// TODO #358: Check whether this check is really necessary. It's been here since the first commit.
/*if (Settings.getPasswordHash == HashAlgorithm.MD5VB) {
if (salt != null && !salt.isEmpty() && Settings.getPasswordHash == HashAlgorithm.MD5VB) {
return "$MD5vb$" + salt + "$" + hash;
}
}
return hash;
}*/
return password;
}
/**
* Method setHash.
*
* @param hash String
*/
public void setHash(String hash) {
this.hash = hash;
public void setPassword(EncryptedPassword password) {
this.password = password;
}
/**
* Method equals.
*
* @param obj Object
*
* @return boolean
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof PlayerAuth)) {
@ -431,11 +303,6 @@ public class PlayerAuth {
return other.getIp().equals(this.ip) && other.getNickname().equals(this.nickname);
}
/**
* Method hashCode.
*
* @return int
*/
@Override
public int hashCode() {
int hashCode = 7;
@ -444,20 +311,14 @@ public class PlayerAuth {
return hashCode;
}
/**
* Method toString.
*
* @return String
*/
@Override
public String toString() {
return ("Player : " + nickname + " | " + realName
return "Player : " + nickname + " | " + realName
+ " ! IP : " + ip
+ " ! LastLogin : " + lastLogin
+ " ! LastPosition : " + x + "," + y + "," + z + "," + world
+ " ! Email : " + email
+ " ! Hash : " + hash
+ " ! Salt : " + salt);
+ " ! Password : {" + password.getHash() + ", " + password.getSalt() + "}";
}
/**
@ -472,8 +333,8 @@ public class PlayerAuth {
str.append(this.realName).append(d);
str.append(this.ip).append(d);
str.append(this.email).append(d);
str.append(this.hash).append(d);
str.append(this.salt).append(d);
str.append(this.password.getHash()).append(d);
str.append(this.password.getSalt()).append(d);
str.append(this.groupId).append(d);
str.append(this.lastLogin).append(d);
str.append(this.world).append(d);
@ -492,8 +353,7 @@ public class PlayerAuth {
this.realName = args[1];
this.ip = args[2];
this.email = args[3];
this.hash = args[4];
this.salt = args[5];
this.password = new EncryptedPassword(args[4], args[5]);
this.groupId = Integer.parseInt(args[6]);
this.lastLogin = Long.parseLong(args[7]);
this.world = args[8];
@ -509,8 +369,7 @@ public class PlayerAuth {
public static final class Builder {
private String name;
private String realName;
private String hash;
private String salt;
private EncryptedPassword password;
private String ip;
private String world;
private String email;
@ -523,8 +382,7 @@ public class PlayerAuth {
public PlayerAuth build() {
return new PlayerAuth(
checkNotNull(name),
nullToEmpty(hash),
nullToEmpty(salt),
firstNonNull(password, new EncryptedPassword("")),
groupId,
firstNonNull(ip, "127.0.0.1"),
lastLogin,
@ -545,14 +403,13 @@ public class PlayerAuth {
return this;
}
public Builder hash(String hash) {
this.hash = hash;
public Builder password(EncryptedPassword password) {
this.password = password;
return this;
}
public Builder salt(String salt) {
this.salt = salt;
return this;
public Builder password(String hash, String salt) {
return password(new EncryptedPassword(hash, salt));
}
public Builder ip(String ip) {
@ -560,6 +417,14 @@ public class PlayerAuth {
return this;
}
public Builder location(Location location) {
this.x = location.getX();
this.y = location.getY();
this.z = location.getZ();
this.world = location.getWorld().getName();
return this;
}
public Builder locWorld(String world) {
this.world = world;
return this;

View File

@ -7,6 +7,7 @@ import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.PasswordSecurity;
import org.bukkit.command.CommandSender;
import java.util.List;
@ -21,6 +22,7 @@ public class CommandService {
private final Messages messages;
private final HelpProvider helpProvider;
private final CommandMapper commandMapper;
private final PasswordSecurity passwordSecurity;
/**
* Constructor.
@ -30,11 +32,13 @@ public class CommandService {
* @param helpProvider Help provider
* @param messages Messages instance
*/
public CommandService(AuthMe authMe, CommandMapper commandMapper, HelpProvider helpProvider, Messages messages) {
public CommandService(AuthMe authMe, CommandMapper commandMapper, HelpProvider helpProvider, Messages messages,
PasswordSecurity passwordSecurity) {
this.authMe = authMe;
this.messages = messages;
this.helpProvider = helpProvider;
this.commandMapper = commandMapper;
this.passwordSecurity = passwordSecurity;
}
/**
@ -88,8 +92,16 @@ public class CommandService {
* @return The used data source
*/
public DataSource getDataSource() {
// TODO ljacqu 20151222: Add getter for .database and rename the field to dataSource
return authMe.database;
return authMe.getDataSource();
}
/**
* Return the PasswordSecurity instance.
*
* @return The password security instance
*/
public PasswordSecurity getPasswordSecurity() {
return passwordSecurity;
}
/**

View File

@ -1,6 +1,5 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
@ -8,13 +7,10 @@ import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import java.security.NoSuchAlgorithmException;
import java.util.List;
/**
@ -60,13 +56,6 @@ public class ChangePasswordAdminCommand implements ExecutableCommand {
@Override
public void run() {
DataSource dataSource = commandService.getDataSource();
String hash;
try {
hash = PasswordSecurity.getHash(Settings.getPasswordHash, playerPass, playerNameLowerCase);
} catch (NoSuchAlgorithmException e) {
commandService.send(sender, MessageKey.ERROR);
return;
}
PlayerAuth auth = null;
if (PlayerCache.getInstance().isAuthenticated(playerNameLowerCase)) {
auth = PlayerCache.getInstance().getAuth(playerNameLowerCase);
@ -77,17 +66,17 @@ public class ChangePasswordAdminCommand implements ExecutableCommand {
commandService.send(sender, MessageKey.UNKNOWN_USER);
return;
}
auth.setHash(hash);
if (PasswordSecurity.userSalt.containsKey(playerNameLowerCase)) {
auth.setSalt(PasswordSecurity.userSalt.get(playerNameLowerCase));
commandService.getDataSource().updateSalt(auth);
}
// TODO #358: Do we always pass lowercase name?? In that case we need to do that in PasswordSecurity!
EncryptedPassword encryptedPassword = commandService.getPasswordSecurity().computeHash(playerPass, playerNameLowerCase);
auth.setPassword(encryptedPassword);
if (!dataSource.updatePassword(auth)) {
commandService.send(sender, MessageKey.ERROR);
return;
} else {
commandService.send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS);
ConsoleLogger.info(playerNameLowerCase + "'s password changed");
}
commandService.send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS);
ConsoleLogger.info(playerNameLowerCase + "'s password changed");
}
});

View File

@ -24,7 +24,7 @@ public class PurgeBannedPlayersCommand implements ExecutableCommand {
}
// Purge the banned players
plugin.database.purgeBanned(bannedPlayers);
plugin.getDataSource().purgeBanned(bannedPlayers);
if (Settings.purgeEssentialsFile && plugin.ess != null)
plugin.dataManager.purgeEssentials(bannedPlayers);
if (Settings.purgePlayerDat)

View File

@ -5,12 +5,11 @@ import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import java.security.NoSuchAlgorithmException;
import java.util.List;
/**
@ -41,7 +40,8 @@ public class RegisterAdminCommand implements ExecutableCommand {
commandService.send(sender, MessageKey.PASSWORD_IS_USERNAME_ERROR);
return;
}
if (playerPassLowerCase.length() < Settings.getPasswordMinLen || playerPassLowerCase.length() > Settings.passwordMaxLength) {
if (playerPassLowerCase.length() < Settings.getPasswordMinLen
|| playerPassLowerCase.length() > Settings.passwordMaxLength) {
commandService.send(sender, MessageKey.INVALID_PASSWORD_LENGTH);
return;
}
@ -53,30 +53,29 @@ public class RegisterAdminCommand implements ExecutableCommand {
@Override
public void run() {
try {
if (commandService.getDataSource().isAuthAvailable(playerNameLowerCase)) {
commandService.send(sender, MessageKey.NAME_ALREADY_REGISTERED);
return;
}
String hash = PasswordSecurity.getHash(Settings.getPasswordHash, playerPass, playerNameLowerCase);
PlayerAuth auth = new PlayerAuth(playerNameLowerCase, hash, "192.168.0.1", 0L, "your@email.com", playerName);
if (PasswordSecurity.userSalt.containsKey(playerNameLowerCase) && PasswordSecurity.userSalt.get(playerNameLowerCase) != null)
auth.setSalt(PasswordSecurity.userSalt.get(playerNameLowerCase));
else auth.setSalt("");
if (!commandService.getDataSource().saveAuth(auth)) {
commandService.send(sender, MessageKey.ERROR);
return;
}
commandService.getDataSource().setUnlogged(playerNameLowerCase);
if (Bukkit.getPlayerExact(playerName) != null)
Bukkit.getPlayerExact(playerName).kickPlayer("An admin just registered you, please log again");
commandService.send(sender, MessageKey.REGISTER_SUCCESS);
ConsoleLogger.info(playerNameLowerCase + " registered");
} catch (NoSuchAlgorithmException ex) {
ConsoleLogger.showError(ex.getMessage());
commandService.send(sender, MessageKey.ERROR);
if (commandService.getDataSource().isAuthAvailable(playerNameLowerCase)) {
commandService.send(sender, MessageKey.NAME_ALREADY_REGISTERED);
return;
}
EncryptedPassword encryptedPassword = commandService.getPasswordSecurity()
.computeHash(playerPass, playerNameLowerCase);
PlayerAuth auth = PlayerAuth.builder()
.name(playerNameLowerCase)
.realName(playerName)
.password(encryptedPassword)
.build();
if (!commandService.getDataSource().saveAuth(auth)) {
commandService.send(sender, MessageKey.ERROR);
return;
}
commandService.getDataSource().setUnlogged(playerNameLowerCase);
if (Bukkit.getPlayerExact(playerName) != null) {
Bukkit.getPlayerExact(playerName).kickPlayer("An admin just registered you, please log again");
} else {
commandService.send(sender, MessageKey.REGISTER_SUCCESS);
ConsoleLogger.info(playerName + " registered");
}
}
});
}

View File

@ -41,7 +41,7 @@ public class CaptchaCommand extends PlayerCommand {
if (Settings.useCaptcha && !captcha.equals(plugin.cap.get(playerNameLowerCase))) {
plugin.cap.remove(playerNameLowerCase);
String randStr = new RandomString(Settings.captchaLength).nextString();
String randStr = RandomString.generate(Settings.captchaLength);
plugin.cap.put(playerNameLowerCase, randStr);
commandService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, plugin.cap.get(playerNameLowerCase));
return;

View File

@ -1,20 +1,18 @@
package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.entity.Player;
import java.security.NoSuchAlgorithmException;
import java.util.List;
public class RecoverEmailCommand extends PlayerCommand {
@ -37,37 +35,32 @@ public class RecoverEmailCommand extends PlayerCommand {
commandService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
return;
}
try {
RandomString rand = new RandomString(Settings.getRecoveryPassLength);
String thePass = rand.nextString();
String hashNew = PasswordSecurity.getHash(Settings.getPasswordHash, thePass, playerName);
PlayerAuth auth;
if (PlayerCache.getInstance().isAuthenticated(playerName)) {
auth = PlayerCache.getInstance().getAuth(playerName);
} else if (dataSource.isAuthAvailable(playerName)) {
auth = dataSource.getAuth(playerName);
} else {
commandService.send(player, MessageKey.UNKNOWN_USER);
return;
}
if (Settings.getmailAccount.equals("") || Settings.getmailAccount.isEmpty()) {
commandService.send(player, MessageKey.ERROR);
return;
}
if (!playerMail.equalsIgnoreCase(auth.getEmail()) || playerMail.equalsIgnoreCase("your@email.com")
|| auth.getEmail().equalsIgnoreCase("your@email.com")) {
commandService.send(player, MessageKey.INVALID_EMAIL);
return;
}
auth.setHash(hashNew);
dataSource.updatePassword(auth);
plugin.mail.main(auth, thePass);
commandService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE);
} catch (NoSuchAlgorithmException | NoClassDefFoundError ex) {
ConsoleLogger.showError(StringUtils.formatException(ex));
commandService.send(player, MessageKey.ERROR);
String thePass = RandomString.generate(Settings.getRecoveryPassLength);
EncryptedPassword hashNew = commandService.getPasswordSecurity().computeHash(thePass, playerName);
PlayerAuth auth;
if (PlayerCache.getInstance().isAuthenticated(playerName)) {
auth = PlayerCache.getInstance().getAuth(playerName);
} else if (dataSource.isAuthAvailable(playerName)) {
auth = dataSource.getAuth(playerName);
} else {
commandService.send(player, MessageKey.UNKNOWN_USER);
return;
}
if (StringUtils.isEmpty(Settings.getmailAccount)) {
commandService.send(player, MessageKey.ERROR);
return;
}
if (!playerMail.equalsIgnoreCase(auth.getEmail()) || playerMail.equalsIgnoreCase("your@email.com")
|| auth.getEmail().equalsIgnoreCase("your@email.com")) {
commandService.send(player, MessageKey.INVALID_EMAIL);
return;
}
auth.setPassword(hashNew);
dataSource.updatePassword(auth);
plugin.mail.main(auth, thePass);
commandService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE);
} else {
commandService.send(player, MessageKey.REGISTER_EMAIL_MESSAGE);
}

View File

@ -27,7 +27,7 @@ public class CrazyLoginConverter implements Converter {
* @param sender CommandSender
*/
public CrazyLoginConverter(AuthMe instance, CommandSender sender) {
this.database = instance.database;
this.database = instance.getDataSource();
this.sender = sender;
}

View File

@ -6,6 +6,7 @@ import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import org.bukkit.command.CommandSender;
@ -13,7 +14,6 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map.Entry;
@ -28,15 +28,12 @@ public class RakamakConverter implements Converter {
public RakamakConverter(AuthMe instance, CommandSender sender) {
this.instance = instance;
this.database = instance.database;
this.database = instance.getDataSource();
this.sender = sender;
}
public RakamakConverter getInstance() {
return this;
}
@Override
// TODO ljacqu 20151229: Restructure this into smaller portions
public void run() {
HashAlgorithm hash = Settings.getPasswordHash;
boolean useIP = Settings.rakamakUseIp;
@ -45,7 +42,7 @@ public class RakamakConverter implements Converter {
File source = new File(Settings.PLUGIN_FOLDER, fileName);
File ipfiles = new File(Settings.PLUGIN_FOLDER, ipFileName);
HashMap<String, String> playerIP = new HashMap<>();
HashMap<String, String> playerPSW = new HashMap<>();
HashMap<String, EncryptedPassword> playerPSW = new HashMap<>();
try {
BufferedReader users;
BufferedReader ipFile;
@ -61,30 +58,29 @@ public class RakamakConverter implements Converter {
}
}
ipFile.close();
users = new BufferedReader(new FileReader(source));
PasswordSecurity passwordSecurity = instance.getPasswordSecurity();
while ((line = users.readLine()) != null) {
if (line.contains("=")) {
String[] arguments = line.split("=");
try {
playerPSW.put(arguments[0], PasswordSecurity.getHash(hash, arguments[1], arguments[0]));
} catch (NoSuchAlgorithmException e) {
ConsoleLogger.showError(e.getMessage());
}
EncryptedPassword encryptedPassword = passwordSecurity.computeHash(hash, arguments[1], arguments[0]);
playerPSW.put(arguments[0], encryptedPassword);
}
}
users.close();
for (Entry<String, String> m : playerPSW.entrySet()) {
for (Entry<String, EncryptedPassword> m : playerPSW.entrySet()) {
String playerName = m.getKey();
String psw = playerPSW.get(playerName);
String ip;
if (useIP) {
ip = playerIP.get(playerName);
} else {
ip = "127.0.0.1";
}
PlayerAuth auth = new PlayerAuth(playerName, psw, ip, System.currentTimeMillis(), playerName);
if (PasswordSecurity.userSalt.containsKey(playerName))
auth.setSalt(PasswordSecurity.userSalt.get(playerName));
EncryptedPassword psw = playerPSW.get(playerName);
String ip = useIP ? playerIP.get(playerName) : "127.0.0.1";
PlayerAuth auth = PlayerAuth.builder()
.name(playerName)
.realName(playerName)
.ip(ip)
.password(psw)
.lastLogin(System.currentTimeMillis())
.build();
database.saveAuth(auth);
}
ConsoleLogger.info("Rakamak database has been imported correctly");

View File

@ -15,7 +15,7 @@ public class RoyalAuthConverter implements Converter {
public RoyalAuthConverter(AuthMe plugin) {
this.plugin = plugin;
this.data = plugin.database;
this.data = plugin.getDataSource();
}
@Override

View File

@ -21,16 +21,14 @@ public class SqliteToSql implements Converter {
@Override
public void run() {
if (plugin.database.getType() != DataSourceType.MYSQL)
{
if (plugin.getDataSource().getType() != DataSourceType.MYSQL) {
sender.sendMessage("Please config your mySQL connection and re-run this command");
return;
}
try {
SQLite data = new SQLite();
for (PlayerAuth auth : data.getAllAuths())
{
plugin.database.saveAuth(auth);
for (PlayerAuth auth : data.getAllAuths()) {
plugin.getDataSource().saveAuth(auth);
}
} catch (Exception e) {
sender.sendMessage(plugin.getMessages().retrieve(MessageKey.ERROR));

View File

@ -23,7 +23,7 @@ class vAuthFileReader {
*/
public vAuthFileReader(AuthMe plugin) {
this.plugin = plugin;
this.database = plugin.database;
this.database = plugin.getDataSource();
}
public void convert() {

View File

@ -30,7 +30,7 @@ class xAuthToFlat {
*/
public xAuthToFlat(AuthMe instance, CommandSender sender) {
this.instance = instance;
this.database = instance.database;
this.database = instance.getDataSource();
this.sender = sender;
}

View File

@ -270,24 +270,6 @@ public class CacheDataSource implements DataSource {
return result;
}
/**
* Method updateSalt.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#updateSalt(PlayerAuth)
*/
@Override
public synchronized boolean updateSalt(final PlayerAuth auth) {
boolean result = source.updateSalt(auth);
if (result) {
cachedAuths.refresh(auth.getNickname());
}
return result;
}
/**
* Method getAllAuthsByName.
*

View File

@ -134,15 +134,6 @@ public interface DataSource {
*/
boolean updateEmail(PlayerAuth auth);
/**
* Method updateSalt.
*
* @param auth PlayerAuth
*
* @return boolean
*/
boolean updateSalt(PlayerAuth auth);
void close();
void reload();

View File

@ -102,7 +102,7 @@ public class FlatFile implements DataSource {
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(source, true));
bw.write(auth.getNickname() + ":" + auth.getHash() + ":" + auth.getIp() + ":" + auth.getLastLogin() + ":" + auth.getQuitLocX() + ":" + auth.getQuitLocY() + ":" + auth.getQuitLocZ() + ":" + auth.getWorld() + ":" + auth.getEmail() + "\n");
bw.write(auth.getNickname() + ":" + auth.getPassword() + ":" + auth.getIp() + ":" + auth.getLastLogin() + ":" + auth.getQuitLocX() + ":" + auth.getQuitLocY() + ":" + auth.getQuitLocZ() + ":" + auth.getWorld() + ":" + auth.getEmail() + "\n");
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
return false;
@ -137,25 +137,26 @@ public class FlatFile implements DataSource {
while ((line = br.readLine()) != null) {
String[] args = line.split(":");
if (args[0].equals(auth.getNickname())) {
// Note ljacqu 20151230: This does not persist the salt; it is not supported in flat file.
switch (args.length) {
case 4: {
newAuth = new PlayerAuth(args[0], auth.getHash(), args[2], Long.parseLong(args[3]), 0, 0, 0, "world", "your@email.com", args[0]);
newAuth = new PlayerAuth(args[0], auth.getPassword().getHash(), args[2], Long.parseLong(args[3]), 0, 0, 0, "world", "your@email.com", args[0]);
break;
}
case 7: {
newAuth = new PlayerAuth(args[0], auth.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "world", "your@email.com", args[0]);
newAuth = new PlayerAuth(args[0], auth.getPassword().getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "world", "your@email.com", args[0]);
break;
}
case 8: {
newAuth = new PlayerAuth(args[0], auth.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]);
newAuth = new PlayerAuth(args[0], auth.getPassword().getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]);
break;
}
case 9: {
newAuth = new PlayerAuth(args[0], auth.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]);
newAuth = new PlayerAuth(args[0], auth.getPassword().getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]);
break;
}
default: {
newAuth = new PlayerAuth(args[0], auth.getHash(), args[2], 0, 0, 0, 0, "world", "your@email.com", args[0]);
newAuth = new PlayerAuth(args[0], auth.getPassword().getHash(), args[2], 0, 0, 0, 0, "world", "your@email.com", args[0]);
break;
}
}
@ -600,18 +601,6 @@ public class FlatFile implements DataSource {
return true;
}
/**
* Method updateSalt.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#updateSalt(PlayerAuth)
*/
@Override
public boolean updateSalt(PlayerAuth auth) {
return false;
}
/**
* Method getAllAuthsByName.
*

View File

@ -7,6 +7,7 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
import java.sql.*;
import java.util.ArrayList;
@ -39,11 +40,6 @@ public class MySQL implements DataSource {
private final List<String> columnOthers;
private HikariDataSource ds;
/**
* Constructor for MySQL.
*
* @throws ClassNotFoundException * @throws SQLException * @throws PoolInitializationException
*/
public MySQL() throws ClassNotFoundException, SQLException, PoolInitializationException {
this.host = Settings.getMySQLHost;
this.port = Settings.getMySQLPort;
@ -96,11 +92,6 @@ public class MySQL implements DataSource {
}
}
/**
* Method setConnectionArguments.
*
* @throws RuntimeException
*/
private synchronized void setConnectionArguments() throws RuntimeException {
ds = new HikariDataSource();
ds.setPoolName("AuthMeMYSQLPool");
@ -116,11 +107,6 @@ public class MySQL implements DataSource {
ConsoleLogger.info("Connection arguments loaded, Hikari ConnectionPool ready!");
}
/**
* Method reloadArguments.
*
* @throws RuntimeException
*/
private synchronized void reloadArguments() throws RuntimeException {
if (ds != null) {
ds.close();
@ -129,20 +115,10 @@ public class MySQL implements DataSource {
ConsoleLogger.info("Hikari ConnectionPool arguments reloaded!");
}
/**
* Method getConnection.
*
* @return Connection * @throws SQLException
*/
private synchronized Connection getConnection() throws SQLException {
return ds.getConnection();
}
/**
* Method setupConnection.
*
* @throws SQLException
*/
private synchronized void setupConnection() throws SQLException {
try (Connection con = getConnection()) {
Statement st = con.createStatement();
@ -186,6 +162,15 @@ public class MySQL implements DataSource {
}
rs.close();
if (!columnSalt.isEmpty()) {
rs = md.getColumns(null, null, tableName, columnSalt);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + columnSalt + " VARCHAR(255);");
}
rs.close();
}
rs = md.getColumns(null, null, tableName, columnIp);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName
@ -244,13 +229,6 @@ public class MySQL implements DataSource {
ConsoleLogger.info("MySQL Setup finished");
}
/**
* Method isAuthAvailable.
*
* @param user String
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#isAuthAvailable(String)
*/
@Override
public synchronized boolean isAuthAvailable(String user) {
try (Connection con = getConnection()) {
@ -266,13 +244,6 @@ public class MySQL implements DataSource {
return false;
}
/**
* Method getAuth.
*
* @param user String
*
* @return PlayerAuth * @see fr.xephi.authme.datasource.DataSource#getAuth(String)
*/
@Override
public synchronized PlayerAuth getAuth(String user) {
PlayerAuth pAuth;
@ -284,13 +255,12 @@ public class MySQL implements DataSource {
if (!rs.next()) {
return null;
}
String salt = !columnSalt.isEmpty() ? rs.getString(columnSalt) : "";
int group = !salt.isEmpty() && !columnGroup.isEmpty() ? rs.getInt(columnGroup) : -1;
int id = rs.getInt(columnID);
String salt = !columnSalt.isEmpty() ? rs.getString(columnSalt) : null;
int group = !columnGroup.isEmpty() ? rs.getInt(columnGroup) : -1;
pAuth = PlayerAuth.builder()
.name(rs.getString(columnName))
.realName(rs.getString(columnRealName))
.hash(rs.getString(columnPassword))
.password(rs.getString(columnPassword), salt)
.lastLogin(rs.getLong(columnLastLogin))
.ip(rs.getString(columnIp))
.locWorld(rs.getString(lastlocWorld))
@ -298,21 +268,10 @@ public class MySQL implements DataSource {
.locY(rs.getDouble(lastlocY))
.locZ(rs.getDouble(lastlocZ))
.email(rs.getString(columnEmail))
.salt(salt)
.groupId(group)
.build();
rs.close();
pst.close();
if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + columnID + "=?;");
pst.setInt(1, id);
rs = pst.executeQuery();
if (rs.next()) {
Blob blob = rs.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
pAuth.setHash(new String(bytes));
}
}
} catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.writeStackTrace(ex);
@ -321,15 +280,6 @@ public class MySQL implements DataSource {
return pAuth;
}
/**
* Method saveAuth.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#saveAuth(PlayerAuth)
*/
@Override
public synchronized boolean saveAuth(PlayerAuth auth) {
try (Connection con = getConnection()) {
@ -338,7 +288,7 @@ public class MySQL implements DataSource {
ResultSet rs;
String sql;
boolean useSalt = !columnSalt.isEmpty() || !auth.getSalt().isEmpty();
boolean useSalt = !columnSalt.isEmpty() || !StringUtils.isEmpty(auth.getPassword().getSalt());
sql = "INSERT INTO " + tableName + "("
+ columnName + "," + columnPassword + "," + columnIp + ","
+ columnLastLogin + "," + columnRealName
@ -346,12 +296,12 @@ public class MySQL implements DataSource {
+ ") VALUES (?,?,?,?,?" + (useSalt ? ",?" : "") + ");";
pst = con.prepareStatement(sql);
pst.setString(1, auth.getNickname());
pst.setString(2, auth.getHash());
pst.setString(2, auth.getPassword().getHash());
pst.setString(3, auth.getIp());
pst.setLong(4, auth.getLastLogin());
pst.setString(5, auth.getRealName());
if (useSalt) {
pst.setString(6, auth.getSalt());
pst.setString(6, auth.getPassword().getSalt());
}
pst.executeUpdate();
pst.close();
@ -502,25 +452,6 @@ public class MySQL implements DataSource {
}
rs.close();
pst.close();
} else if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
pst = con.prepareStatement("SELECT " + columnID + " FROM " + tableName + " WHERE " + columnName + "=?;");
pst.setString(1, auth.getNickname());
rs = pst.executeQuery();
if (rs.next()) {
int id = rs.getInt(columnID);
// Insert password in the correct table
pst2 = con.prepareStatement("INSERT INTO xf_user_authenticate (user_id, scheme_class, data) VALUES (?,?,?);");
pst2.setInt(1, id);
pst2.setString(2, "XenForo_Authentication_Core12");
byte[] bytes = auth.getHash().getBytes();
Blob blob = con.createBlob();
blob.setBytes(1, bytes);
pst2.setBlob(3, blob);
pst2.executeUpdate();
pst2.close();
}
rs.close();
pst.close();
}
return true;
} catch (SQLException ex) {
@ -530,52 +461,27 @@ public class MySQL implements DataSource {
return false;
}
/**
* Method updatePassword.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#updatePassword(PlayerAuth)
*/
@Override
public synchronized boolean updatePassword(PlayerAuth auth) {
try (Connection con = getConnection()) {
String sql = "UPDATE " + tableName + " SET " + columnPassword + "=? WHERE " + columnName + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, auth.getHash());
pst.setString(2, auth.getNickname());
boolean useSalt = !columnSalt.isEmpty();
PreparedStatement pst;
if (useSalt) {
String sql = String.format("UPDATE %s SET %s = ?, %s = ? WHERE %s = ?;",
tableName, columnPassword, columnSalt, columnName);
pst = con.prepareStatement(sql);
pst.setString(1, auth.getPassword().getHash());
pst.setString(2, auth.getPassword().getSalt());
pst.setString(3, auth.getNickname());
} else {
String sql = String.format("UPDATE %s SET %s = ? WHERE %s = ?;",
tableName, columnPassword, columnName);
pst = con.prepareStatement(sql);
pst.setString(1, auth.getPassword().getHash());
pst.setString(2, auth.getNickname());
}
pst.executeUpdate();
pst.close();
if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
sql = "SELECT " + columnID + " FROM " + tableName + " WHERE " + columnName + "=?;";
pst = con.prepareStatement(sql);
pst.setString(1, auth.getNickname());
ResultSet rs = pst.executeQuery();
if (rs.next()) {
int id = rs.getInt(columnID);
// Insert password in the correct table
sql = "UPDATE xf_user_authenticate SET data=? WHERE " + columnID + "=?;";
PreparedStatement pst2 = con.prepareStatement(sql);
byte[] bytes = auth.getHash().getBytes();
Blob blob = con.createBlob();
blob.setBytes(1, bytes);
pst2.setBlob(1, blob);
pst2.setInt(2, id);
pst2.executeUpdate();
pst2.close();
// ...
sql = "UPDATE xf_user_authenticate SET scheme_class=? WHERE " + columnID + "=?;";
pst2 = con.prepareStatement(sql);
pst2.setString(1, "XenForo_Authentication_Core12");
pst2.setInt(2, id);
pst2.executeUpdate();
pst2.close();
}
rs.close();
pst.close();
}
return true;
} catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage());
@ -584,15 +490,6 @@ public class MySQL implements DataSource {
return false;
}
/**
* Method updateSession.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#updateSession(PlayerAuth)
*/
@Override
public synchronized boolean updateSession(PlayerAuth auth) {
try (Connection con = getConnection()) {
@ -612,15 +509,6 @@ public class MySQL implements DataSource {
return false;
}
/**
* Method purgeDatabase.
*
* @param until long
*
* @return int
*
* @see fr.xephi.authme.datasource.DataSource#purgeDatabase(long)
*/
@Override
public synchronized int purgeDatabase(long until) {
int result = 0;
@ -636,15 +524,6 @@ public class MySQL implements DataSource {
return result;
}
/**
* Method autoPurgeDatabase.
*
* @param until long
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#autoPurgeDatabase(long)
*/
@Override
public synchronized List<String> autoPurgeDatabase(long until) {
List<String> list = new ArrayList<>();
@ -666,37 +545,11 @@ public class MySQL implements DataSource {
return list;
}
/**
* Method removeAuth.
*
* @param user String
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#removeAuth(String)
*/
@Override
public synchronized boolean removeAuth(String user) {
user = user.toLowerCase();
try (Connection con = getConnection()) {
String sql;
PreparedStatement pst;
if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
sql = "SELECT " + columnID + " FROM " + tableName + " WHERE " + columnName + "=?;";
pst = con.prepareStatement(sql);
pst.setString(1, user);
ResultSet rs = pst.executeQuery();
if (rs.next()) {
int id = rs.getInt(columnID);
sql = "DELETE FROM xf_user_authenticate WHERE " + columnID + "=" + id;
Statement st = con.createStatement();
st.executeUpdate(sql);
st.close();
}
rs.close();
pst.close();
}
pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + columnName + "=?;");
PreparedStatement pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + columnName + "=?;");
pst.setString(1, user);
pst.executeUpdate();
return true;
@ -707,15 +560,6 @@ public class MySQL implements DataSource {
return false;
}
/**
* Method updateQuitLoc.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#updateQuitLoc(PlayerAuth)
*/
@Override
public synchronized boolean updateQuitLoc(PlayerAuth auth) {
try (Connection con = getConnection()) {
@ -738,15 +582,6 @@ public class MySQL implements DataSource {
return false;
}
/**
* Method getIps.
*
* @param ip String
*
* @return int
*
* @see fr.xephi.authme.datasource.DataSource#getIps(String)
*/
@Override
public synchronized int getIps(String ip) {
int countIp = 0;
@ -767,15 +602,6 @@ public class MySQL implements DataSource {
return countIp;
}
/**
* Method updateEmail.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#updateEmail(PlayerAuth)
*/
@Override
public synchronized boolean updateEmail(PlayerAuth auth) {
try (Connection con = getConnection()) {
@ -793,40 +619,6 @@ public class MySQL implements DataSource {
return false;
}
/**
* Method updateSalt.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#updateSalt(PlayerAuth)
*/
@Override
public synchronized boolean updateSalt(PlayerAuth auth) {
if (columnSalt.isEmpty()) {
return false;
}
try (Connection con = getConnection()) {
String sql = "UPDATE " + tableName + " SET " + columnSalt + " =? WHERE " + columnName + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, auth.getSalt());
pst.setString(2, auth.getNickname());
pst.executeUpdate();
pst.close();
return true;
} catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.writeStackTrace(ex);
}
return false;
}
/**
* Method reload.
*
* @see fr.xephi.authme.datasource.DataSource#reload()
*/
@Override
public void reload() {
try {
@ -839,11 +631,6 @@ public class MySQL implements DataSource {
}
}
/**
* Method close.
*
* @see fr.xephi.authme.datasource.DataSource#close()
*/
@Override
public synchronized void close() {
if (ds != null && !ds.isClosed()) {
@ -851,15 +638,6 @@ public class MySQL implements DataSource {
}
}
/**
* Method getAllAuthsByName.
*
* @param auth PlayerAuth
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#getAllAuthsByName(PlayerAuth)
*/
@Override
public synchronized List<String> getAllAuthsByName(PlayerAuth auth) {
List<String> result = new ArrayList<>();
@ -880,15 +658,6 @@ public class MySQL implements DataSource {
return result;
}
/**
* Method getAllAuthsByIp.
*
* @param ip String
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#getAllAuthsByIp(String)
*/
@Override
public synchronized List<String> getAllAuthsByIp(String ip) {
List<String> result = new ArrayList<>();
@ -909,15 +678,6 @@ public class MySQL implements DataSource {
return result;
}
/**
* Method getAllAuthsByEmail.
*
* @param email String
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#getAllAuthsByEmail(String)
*/
@Override
public synchronized List<String> getAllAuthsByEmail(String email){
List<String> countEmail = new ArrayList<>();
@ -938,13 +698,6 @@ public class MySQL implements DataSource {
return countEmail;
}
/**
* Method purgeBanned.
*
* @param banned List<String>
*
* @see fr.xephi.authme.datasource.DataSource#purgeBanned(List)
*/
@Override
public synchronized void purgeBanned(List<String> banned) {
try (Connection con = getConnection()) {
@ -960,23 +713,11 @@ public class MySQL implements DataSource {
}
}
/**
* Method getType.
*
* @return DataSourceType * @see fr.xephi.authme.datasource.DataSource#getType()
*/
@Override
public DataSourceType getType() {
return DataSourceType.MYSQL;
}
/**
* Method isLogged.
*
* @param user String
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#isLogged(String)
*/
@Override
public boolean isLogged(String user) {
boolean isLogged = false;
@ -993,13 +734,6 @@ public class MySQL implements DataSource {
return isLogged;
}
/**
* Method setLogged.
*
* @param user String
*
* @see fr.xephi.authme.datasource.DataSource#setLogged(String)
*/
@Override
public void setLogged(String user) {
try (Connection con = getConnection()) {
@ -1015,13 +749,6 @@ public class MySQL implements DataSource {
}
}
/**
* Method setUnlogged.
*
* @param user String
*
* @see fr.xephi.authme.datasource.DataSource#setUnlogged(String)
*/
@Override
public void setUnlogged(String user) {
try (Connection con = getConnection()) {
@ -1037,11 +764,6 @@ public class MySQL implements DataSource {
}
}
/**
* Method purgeLogged.
*
* @see fr.xephi.authme.datasource.DataSource#purgeLogged()
*/
@Override
public void purgeLogged() {
try (Connection con = getConnection()) {
@ -1057,13 +779,6 @@ public class MySQL implements DataSource {
}
}
/**
* Method getAccountsRegistered.
*
* @return int
*
* @see fr.xephi.authme.datasource.DataSource#getAccountsRegistered()
*/
@Override
public int getAccountsRegistered() {
int result = 0;
@ -1082,14 +797,6 @@ public class MySQL implements DataSource {
return result;
}
/**
* Method updateName.
*
* @param oldOne String
* @param newOne String
*
* @see fr.xephi.authme.datasource.DataSource#updateName(String, String)
*/
@Override
public void updateName(String oldOne, String newOne) {
try (Connection con = getConnection()) {
@ -1104,13 +811,6 @@ public class MySQL implements DataSource {
}
}
/**
* Method getAllAuths.
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#getAllAuths()
*/
@Override
public List<PlayerAuth> getAllAuths() {
List<PlayerAuth> auths = new ArrayList<>();
@ -1119,12 +819,12 @@ public class MySQL implements DataSource {
ResultSet rs = st.executeQuery("SELECT * FROM " + tableName);
PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + columnID + "=?;");
while (rs.next()) {
String salt = !columnSalt.isEmpty() ? rs.getString(columnSalt) : "";
int group = !salt.isEmpty() && !columnGroup.isEmpty() ? rs.getInt(columnGroup) : -1;
String salt = !columnSalt.isEmpty() ? rs.getString(columnSalt) : null;
int group = !columnGroup.isEmpty() ? rs.getInt(columnGroup) : -1;
PlayerAuth pAuth = PlayerAuth.builder()
.name(rs.getString(columnName))
.realName(rs.getString(columnRealName))
.hash(rs.getString(columnPassword))
.password(rs.getString(columnPassword), salt)
.lastLogin(rs.getLong(columnLastLogin))
.ip(rs.getString(columnIp))
.locWorld(rs.getString(lastlocWorld))
@ -1132,21 +832,9 @@ public class MySQL implements DataSource {
.locY(rs.getDouble(lastlocY))
.locZ(rs.getDouble(lastlocZ))
.email(rs.getString(columnEmail))
.salt(salt)
.groupId(group)
.build();
if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
int id = rs.getInt(columnID);
pst.setInt(1, id);
ResultSet rs2 = pst.executeQuery();
if (rs2.next()) {
Blob blob = rs2.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
pAuth.setHash(new String(bytes));
}
rs2.close();
}
auths.add(pAuth);
}
pst.close();
@ -1159,13 +847,6 @@ public class MySQL implements DataSource {
return auths;
}
/**
* Method getLoggedPlayers.
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#getLoggedPlayers()
*/
@Override
public List<PlayerAuth> getLoggedPlayers() {
List<PlayerAuth> auths = new ArrayList<>();
@ -1174,12 +855,12 @@ public class MySQL implements DataSource {
ResultSet rs = st.executeQuery("SELECT * FROM " + tableName + " WHERE " + columnLogged + "=1;");
PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + columnID + "=?;");
while (rs.next()) {
String salt = !columnSalt.isEmpty() ? rs.getString(columnSalt) : "";
int group = !salt.isEmpty() && !columnGroup.isEmpty() ? rs.getInt(columnGroup) : -1;
String salt = !columnSalt.isEmpty() ? rs.getString(columnSalt) : null;
int group = !columnGroup.isEmpty() ? rs.getInt(columnGroup) : -1;
PlayerAuth pAuth = PlayerAuth.builder()
.name(rs.getString(columnName))
.realName(rs.getString(columnRealName))
.hash(rs.getString(columnPassword))
.password(rs.getString(columnPassword), salt)
.lastLogin(rs.getLong(columnLastLogin))
.ip(rs.getString(columnIp))
.locWorld(rs.getString(lastlocWorld))
@ -1187,21 +868,9 @@ public class MySQL implements DataSource {
.locY(rs.getDouble(lastlocY))
.locZ(rs.getDouble(lastlocZ))
.email(rs.getString(columnEmail))
.salt(salt)
.groupId(group)
.build();
if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
int id = rs.getInt(columnID);
pst.setInt(1, id);
ResultSet rs2 = pst.executeQuery();
if (rs2.next()) {
Blob blob = rs2.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
pAuth.setHash(new String(bytes));
}
rs2.close();
}
auths.add(pAuth);
}
} catch (Exception ex) {

View File

@ -2,7 +2,9 @@ package fr.xephi.authme.datasource;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
import java.sql.*;
import java.util.ArrayList;
@ -90,6 +92,13 @@ public class SQLite implements DataSource {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + columnPassword + " VARCHAR(255) NOT NULL;");
}
rs.close();
if (!columnSalt.isEmpty()) {
rs = con.getMetaData().getColumns(null, null, tableName, columnSalt);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + columnSalt + " VARCHAR(255);");
}
rs.close();
}
rs = con.getMetaData().getColumns(null, null, tableName, columnIp);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + columnIp + " VARCHAR(40) NOT NULL;");
@ -174,15 +183,7 @@ public class SQLite implements DataSource {
pst.setString(1, user);
rs = pst.executeQuery();
if (rs.next()) {
if (rs.getString(columnIp).isEmpty()) {
return new PlayerAuth(rs.getString(columnName), rs.getString(columnPassword), "192.168.0.1", rs.getLong(columnLastLogin), rs.getDouble(lastlocX), rs.getDouble(lastlocY), rs.getDouble(lastlocZ), rs.getString(lastlocWorld), rs.getString(columnEmail), rs.getString(columnRealName));
} else {
if (!columnSalt.isEmpty()) {
return new PlayerAuth(rs.getString(columnName), rs.getString(columnPassword), rs.getString(columnSalt), rs.getInt(columnGroup), rs.getString(columnIp), rs.getLong(columnLastLogin), rs.getDouble(lastlocX), rs.getDouble(lastlocY), rs.getDouble(lastlocZ), rs.getString(lastlocWorld), rs.getString(columnEmail), rs.getString(columnRealName));
} else {
return new PlayerAuth(rs.getString(columnName), rs.getString(columnPassword), rs.getString(columnIp), rs.getLong(columnLastLogin), rs.getDouble(lastlocX), rs.getDouble(lastlocY), rs.getDouble(lastlocZ), rs.getString(lastlocWorld), rs.getString(columnEmail), rs.getString(columnRealName));
}
}
return buildAuthFromResultSet(rs);
} else {
return null;
}
@ -206,21 +207,29 @@ public class SQLite implements DataSource {
public synchronized boolean saveAuth(PlayerAuth auth) {
PreparedStatement pst = null;
try {
if (columnSalt.isEmpty() && auth.getSalt().isEmpty()) {
pst = con.prepareStatement("INSERT INTO " + tableName + "(" + columnName + "," + columnPassword + "," + columnIp + "," + columnLastLogin + "," + columnRealName + ") VALUES (?,?,?,?,?);");
EncryptedPassword password = auth.getPassword();
if (columnSalt.isEmpty()) {
if (!StringUtils.isEmpty(auth.getPassword().getSalt())) {
ConsoleLogger.showError("Warning! Detected hashed password with separate salt but the salt column "
+ "is not set in the config!");
}
pst = con.prepareStatement("INSERT INTO " + tableName + "(" + columnName + "," + columnPassword +
"," + columnIp + "," + columnLastLogin + "," + columnRealName + ") VALUES (?,?,?,?,?);");
pst.setString(1, auth.getNickname());
pst.setString(2, auth.getHash());
pst.setString(2, password.getHash());
pst.setString(3, auth.getIp());
pst.setLong(4, auth.getLastLogin());
pst.setString(5, auth.getRealName());
pst.executeUpdate();
} else {
pst = con.prepareStatement("INSERT INTO " + tableName + "(" + columnName + "," + columnPassword + "," + columnIp + "," + columnLastLogin + "," + columnSalt + "," + columnRealName + ") VALUES (?,?,?,?,?,?);");
pst = con.prepareStatement("INSERT INTO " + tableName + "(" + columnName + "," + columnPassword + ","
+ columnIp + "," + columnLastLogin + "," + columnSalt + "," + columnRealName
+ ") VALUES (?,?,?,?,?,?);");
pst.setString(1, auth.getNickname());
pst.setString(2, auth.getHash());
pst.setString(2, password.getHash());
pst.setString(3, auth.getIp());
pst.setLong(4, auth.getLastLogin());
pst.setString(5, auth.getSalt());
pst.setString(5, password.getSalt());
pst.setString(6, auth.getRealName());
pst.executeUpdate();
}
@ -244,9 +253,19 @@ public class SQLite implements DataSource {
public synchronized boolean updatePassword(PlayerAuth auth) {
PreparedStatement pst = null;
try {
pst = con.prepareStatement("UPDATE " + tableName + " SET " + columnPassword + "=? WHERE " + columnName + "=?;");
pst.setString(1, auth.getHash());
pst.setString(2, auth.getNickname());
EncryptedPassword password = auth.getPassword();
boolean useSalt = !columnSalt.isEmpty();
String sql = "UPDATE " + tableName + " SET " + columnPassword + " = ?"
+ (useSalt ? ", " + columnSalt + " = ?" : "")
+ " WHERE " + columnName + " = ?";
pst = con.prepareStatement(sql);
pst.setString(1, password.getHash());
if (useSalt) {
pst.setString(2, password.getSalt());
pst.setString(3, auth.getNickname());
} else {
pst.setString(2, auth.getNickname());
}
pst.executeUpdate();
} catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage());
@ -398,6 +417,7 @@ public class SQLite implements DataSource {
ResultSet rs = null;
int countIp = 0;
try {
// TODO ljacqu 20151230: Simply fetch COUNT(1) and return that
pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + columnIp + "=?;");
pst.setString(1, ip);
rs = pst.executeQuery();
@ -438,33 +458,6 @@ public class SQLite implements DataSource {
return true;
}
/**
* Method updateSalt.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#updateSalt(PlayerAuth)
*/
@Override
public boolean updateSalt(PlayerAuth auth) {
if (columnSalt.isEmpty()) {
return false;
}
PreparedStatement pst = null;
try {
pst = con.prepareStatement("UPDATE " + tableName + " SET " + columnSalt + "=? WHERE " + columnName + "=?;");
pst.setString(1, auth.getSalt());
pst.setString(2, auth.getNickname());
pst.executeUpdate();
} catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage());
return false;
} finally {
close(pst);
}
return true;
}
/**
* Method close.
*
@ -611,13 +604,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method purgeBanned.
*
* @param banned List<String>
*
* @see fr.xephi.authme.datasource.DataSource#purgeBanned(List<String>)
*/
@Override
public void purgeBanned(List<String> banned) {
PreparedStatement pst = null;
@ -761,14 +747,6 @@ public class SQLite implements DataSource {
return result;
}
/**
* Method updateName.
*
* @param oldOne String
* @param newOne String
*
* @see fr.xephi.authme.datasource.DataSource#updateName(String, String)
*/
@Override
public void updateName(String oldOne, String newOne) {
PreparedStatement pst = null;
@ -787,7 +765,7 @@ public class SQLite implements DataSource {
/**
* Method getAllAuths.
*
* @return List<PlayerAuth> * @see fr.xephi.authme.datasource.DataSource#getAllAuths()
* @return List<PlayerAuth>
*/
@Override
public List<PlayerAuth> getAllAuths() {
@ -798,17 +776,8 @@ public class SQLite implements DataSource {
pst = con.prepareStatement("SELECT * FROM " + tableName + ";");
rs = pst.executeQuery();
while (rs.next()) {
PlayerAuth pAuth;
if (rs.getString(columnIp).isEmpty()) {
pAuth = new PlayerAuth(rs.getString(columnName), rs.getString(columnPassword), "127.0.0.1", rs.getLong(columnLastLogin), rs.getDouble(lastlocX), rs.getDouble(lastlocY), rs.getDouble(lastlocZ), rs.getString(lastlocWorld), rs.getString(columnEmail), rs.getString(columnRealName));
} else {
if (!columnSalt.isEmpty()) {
pAuth = new PlayerAuth(rs.getString(columnName), rs.getString(columnPassword), rs.getString(columnSalt), rs.getInt(columnGroup), rs.getString(columnIp), rs.getLong(columnLastLogin), rs.getDouble(lastlocX), rs.getDouble(lastlocY), rs.getDouble(lastlocZ), rs.getString(lastlocWorld), rs.getString(columnEmail), rs.getString(columnRealName));
} else {
pAuth = new PlayerAuth(rs.getString(columnName), rs.getString(columnPassword), rs.getString(columnIp), rs.getLong(columnLastLogin), rs.getDouble(lastlocX), rs.getDouble(lastlocY), rs.getDouble(lastlocZ), rs.getString(lastlocWorld), rs.getString(columnEmail), rs.getString(columnRealName));
}
}
auths.add(pAuth);
PlayerAuth auth = buildAuthFromResultSet(rs);
auths.add(auth);
}
} catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage());
@ -822,7 +791,7 @@ public class SQLite implements DataSource {
/**
* Method getLoggedPlayers.
*
* @return List<PlayerAuth> * @see fr.xephi.authme.datasource.DataSource#getLoggedPlayers()
* @return List<PlayerAuth>
*/
@Override
public List<PlayerAuth> getLoggedPlayers() {
@ -833,17 +802,8 @@ public class SQLite implements DataSource {
pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + columnLogged + "=1;");
rs = pst.executeQuery();
while (rs.next()) {
PlayerAuth pAuth;
if (rs.getString(columnIp).isEmpty()) {
pAuth = new PlayerAuth(rs.getString(columnName), rs.getString(columnPassword), "127.0.0.1", rs.getLong(columnLastLogin), rs.getDouble(lastlocX), rs.getDouble(lastlocY), rs.getDouble(lastlocZ), rs.getString(lastlocWorld), rs.getString(columnEmail), rs.getString(columnRealName));
} else {
if (!columnSalt.isEmpty()) {
pAuth = new PlayerAuth(rs.getString(columnName), rs.getString(columnPassword), rs.getString(columnSalt), rs.getInt(columnGroup), rs.getString(columnIp), rs.getLong(columnLastLogin), rs.getDouble(lastlocX), rs.getDouble(lastlocY), rs.getDouble(lastlocZ), rs.getString(lastlocWorld), rs.getString(columnEmail), rs.getString(columnRealName));
} else {
pAuth = new PlayerAuth(rs.getString(columnName), rs.getString(columnPassword), rs.getString(columnIp), rs.getLong(columnLastLogin), rs.getDouble(lastlocX), rs.getDouble(lastlocY), rs.getDouble(lastlocZ), rs.getString(lastlocWorld), rs.getString(columnEmail), rs.getString(columnRealName));
}
}
auths.add(pAuth);
PlayerAuth auth = buildAuthFromResultSet(rs);
auths.add(auth);
}
} catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage());
@ -853,4 +813,25 @@ public class SQLite implements DataSource {
}
return auths;
}
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
String salt = !columnSalt.isEmpty() ? row.getString(columnSalt) : null;
PlayerAuth.Builder authBuilder = PlayerAuth.builder()
.name(row.getString(columnName))
.email(row.getString(columnEmail))
.realName(row.getString(columnRealName))
.password(row.getString(columnPassword), salt)
.lastLogin(row.getLong(columnLastLogin))
.locX(row.getDouble(lastlocX))
.locY(row.getDouble(lastlocY))
.locZ(row.getDouble(lastlocZ))
.locWorld(row.getString(lastlocWorld));
String ip = row.getString(columnIp);
if (!ip.isEmpty()) {
authBuilder.ip(ip);
}
return authBuilder.build();
}
}

View File

@ -5,75 +5,41 @@ import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* <p>
* This event is called when we need to compare or get an hash password, for set
* a custom EncryptionMethod
* </p>
* This event is called when we need to compare or hash password and allows
* third-party listeners to change the encryption method. This is typically
* done with the {@link fr.xephi.authme.security.HashAlgorithm#CUSTOM} setting.
*
* @author Xephi59
* @version $Revision: 1.0 $
* @see fr.xephi.authme.security.crypts.EncryptionMethod
*/
public class PasswordEncryptionEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private EncryptionMethod method = null;
private String playerName = "";
private EncryptionMethod method;
private String playerName;
/**
* Constructor for PasswordEncryptionEvent.
*
* @param method EncryptionMethod
* @param playerName String
*/
public PasswordEncryptionEvent(EncryptionMethod method, String playerName) {
super(false);
this.method = method;
this.playerName = playerName;
}
/**
* Method getHandlerList.
*
* @return HandlerList
*/
public static HandlerList getHandlerList() {
return handlers;
}
/**
* Method getHandlers.
*
* @return HandlerList
*/
@Override
public HandlerList getHandlers() {
return handlers;
}
/**
* Method getMethod.
*
* @return EncryptionMethod
*/
public EncryptionMethod getMethod() {
return method;
}
/**
* Method setMethod.
*
* @param method EncryptionMethod
*/
public void setMethod(EncryptionMethod method) {
this.method = method;
}
/**
* Method getPlayerName.
*
* @return String
*/
public String getPlayerName() {
return playerName;
}

View File

@ -6,6 +6,8 @@ import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
@ -24,15 +26,6 @@ public class BungeeCordMessage implements PluginMessageListener {
this.plugin = plugin;
}
/**
* Method onPluginMessageReceived.
*
* @param channel String
* @param player Player
* @param message byte[]
*
* @see org.bukkit.plugin.messaging.PluginMessageListener#onPluginMessageReceived(String, Player, byte[])
*/
@Override
public void onPluginMessageReceived(String channel, Player player, byte[] message) {
if (!channel.equals("BungeeCord")) {
@ -50,21 +43,22 @@ public class BungeeCordMessage implements PluginMessageListener {
final String[] args = str.split(";");
final String act = args[0];
final String name = args[1];
final DataSource dataSource = plugin.getDataSource();
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
PlayerAuth auth = plugin.database.getAuth(name);
PlayerAuth auth = dataSource.getAuth(name);
if (auth == null) {
return;
}
if ("login".equals(act)) {
PlayerCache.getInstance().updatePlayer(auth);
plugin.database.setLogged(name);
dataSource.setLogged(name);
ConsoleLogger.info("Player " + auth.getNickname()
+ " has logged in from one of your server!");
} else if ("logout".equals(act)) {
PlayerCache.getInstance().removePlayer(name);
plugin.database.setUnlogged(name);
dataSource.setUnlogged(name);
ConsoleLogger.info("Player " + auth.getNickname()
+ " has logged out from one of your server!");
} else if ("register".equals(act)) {
@ -72,11 +66,10 @@ public class BungeeCordMessage implements PluginMessageListener {
+ " has registered out from one of your server!");
} else if ("changepassword".equals(act)) {
final String password = args[2];
auth.setHash(password);
if (args.length == 4)
auth.setSalt(args[3]);
final String salt = args.length >= 4 ? args[3] : null;
auth.setPassword(new EncryptedPassword(password, salt));
PlayerCache.getInstance().updatePlayer(auth);
plugin.database.updatePassword(auth);
dataSource.updatePassword(auth);
}
}

View File

@ -89,7 +89,7 @@ public class AuthMePlayerListener implements Listener {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
if (plugin.database.isAuthAvailable(player.getName().toLowerCase())) {
if (plugin.getDataSource().isAuthAvailable(player.getName().toLowerCase())) {
m.send(player, MessageKey.LOGIN_MESSAGE);
} else {
if (Settings.emailRegistration) {
@ -221,8 +221,9 @@ public class AuthMePlayerListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void onPreLogin(AsyncPlayerPreLoginEvent event) {
PlayerAuth auth = plugin.database.getAuth(event.getName());
if (auth != null && auth.getRealName() != null && !auth.getRealName().isEmpty() && !auth.getRealName().equals("Player") && !auth.getRealName().equals(event.getName())) {
PlayerAuth auth = plugin.getDataSource().getAuth(event.getName());
if (auth != null && auth.getRealName() != null && !auth.getRealName().isEmpty() &&
!auth.getRealName().equals("Player") && !auth.getRealName().equals(event.getName())) {
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
event.setKickMessage("You should join using username: " + ChatColor.AQUA + auth.getRealName() +
ChatColor.RESET + "\nnot: " + ChatColor.RED + event.getName()); // TODO: write a better message
@ -231,7 +232,7 @@ public class AuthMePlayerListener implements Listener {
if (auth != null && auth.getRealName().equals("Player")) {
auth.setRealName(event.getName());
plugin.database.saveAuth(auth);
plugin.getDataSource().saveAuth(auth);
}
if (auth == null && Settings.enableProtection) {
@ -302,7 +303,7 @@ public class AuthMePlayerListener implements Listener {
}
final String name = player.getName().toLowerCase();
boolean isAuthAvailable = plugin.database.isAuthAvailable(name);
boolean isAuthAvailable = plugin.getDataSource().isAuthAvailable(name);
if (Settings.isKickNonRegisteredEnabled && !isAuthAvailable) {
if (Settings.antiBotInAction) {
@ -475,9 +476,13 @@ public class AuthMePlayerListener implements Listener {
Player player = event.getPlayer();
String name = player.getName().toLowerCase();
Location spawn = plugin.getSpawnLocation(player);
if (Settings.isSaveQuitLocationEnabled && plugin.database.isAuthAvailable(name)) {
PlayerAuth auth = new PlayerAuth(name, spawn.getX(), spawn.getY(), spawn.getZ(), spawn.getWorld().getName(), player.getName());
plugin.database.updateQuitLoc(auth);
if (Settings.isSaveQuitLocationEnabled && plugin.getDataSource().isAuthAvailable(name)) {
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.realName(player.getName())
.location(spawn)
.build();
plugin.getDataSource().updateQuitLoc(auth);
}
if (spawn != null && spawn.getWorld() != null) {
event.setRespawnLocation(spawn);

View File

@ -33,7 +33,7 @@ public class Management {
@Override
public void run() {
new AsynchronousLogin(player, password, forceLogin, plugin, plugin.database).process();
new AsynchronousLogin(player, password, forceLogin, plugin, plugin.getDataSource()).process();
}
});
}
@ -43,7 +43,7 @@ public class Management {
@Override
public void run() {
new AsynchronousLogout(player, plugin, plugin.database).process();
new AsynchronousLogout(player, plugin, plugin.getDataSource()).process();
}
});
}
@ -53,7 +53,7 @@ public class Management {
@Override
public void run() {
new AsyncRegister(player, password, email, plugin, plugin.database).process();
new AsyncRegister(player, password, email, plugin, plugin.getDataSource()).process();
}
});
}
@ -73,7 +73,7 @@ public class Management {
@Override
public void run() {
new AsynchronousJoin(player, plugin, plugin.database).process();
new AsynchronousJoin(player, plugin, plugin.getDataSource()).process();
}
});
@ -84,7 +84,7 @@ public class Management {
@Override
public void run() {
new AsynchronousQuit(player, plugin, plugin.database, isKick).process();
new AsynchronousQuit(player, plugin, plugin.getDataSource(), isKick).process();
}
});

View File

@ -40,7 +40,7 @@ public class AsyncChangeEmail {
if (Settings.getmaxRegPerEmail > 0) {
if (!plugin.getPermissionsManager().hasPermission(player, PlayerPermission.ALLOW_MULTIPLE_ACCOUNTS)
&& plugin.database.getAllAuthsByEmail(newEmail).size() >= Settings.getmaxRegPerEmail) {
&& plugin.getDataSource().getAllAuthsByEmail(newEmail).size() >= Settings.getmaxRegPerEmail) {
m.send(player, MessageKey.MAX_REGISTER_EXCEEDED);
return;
}
@ -68,7 +68,7 @@ public class AsyncChangeEmail {
}
String old = auth.getEmail();
auth.setEmail(newEmail);
if (!plugin.database.updateEmail(auth)) {
if (!plugin.getDataSource().updateEmail(auth)) {
m.send(player, MessageKey.ERROR);
auth.setEmail(old);
return;
@ -81,7 +81,7 @@ public class AsyncChangeEmail {
}
m.send(player, MessageKey.EMAIL_CHANGED_SUCCESS);
} else {
if (plugin.database.isAuthAvailable(playerName)) {
if (plugin.getDataSource().isAuthAvailable(playerName)) {
m.send(player, MessageKey.LOGIN_MESSAGE);
} else {
if (Settings.emailRegistration) {

View File

@ -8,7 +8,6 @@ import fr.xephi.authme.cache.limbo.LimboCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.AuthMeAsyncPreLoginEvent;
import fr.xephi.authme.permission.PlayerPermission;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
@ -26,12 +25,11 @@ import java.util.List;
*/
public class AsynchronousLogin {
private static final RandomString rdm = new RandomString(Settings.captchaLength);
protected final Player player;
protected final String name;
protected final String realName;
protected final String password;
protected final boolean forceLogin;
private final Player player;
private final String name;
private final String realName;
private final String password;
private final boolean forceLogin;
private final AuthMe plugin;
private final DataSource database;
private final Messages m;
@ -70,7 +68,7 @@ public class AsynchronousLogin {
plugin.captcha.putIfAbsent(name, i);
}
if (plugin.captcha.containsKey(name) && plugin.captcha.get(name) > Settings.maxLoginTry) {
plugin.cap.putIfAbsent(name, rdm.nextString());
plugin.cap.putIfAbsent(name, RandomString.generate(Settings.captchaLength));
m.send(player, MessageKey.USAGE_CAPTCHA, plugin.cap.get(name));
return true;
}
@ -120,8 +118,7 @@ public class AsynchronousLogin {
return null;
}
if (Settings.preventOtherCase && !player.getName().equals(pAuth.getRealName()))
{
if (Settings.preventOtherCase && !player.getName().equals(pAuth.getRealName())) {
// TODO: Add a message like : MessageKey.INVALID_NAME_CASE
m.send(player, MessageKey.USERNAME_ALREADY_ONLINE_ERROR);
return null;
@ -138,19 +135,19 @@ public class AsynchronousLogin {
if (pAuth == null || needsCaptcha())
return;
String hash = pAuth.getHash();
String email = pAuth.getEmail();
boolean passwordVerified = true;
if (!forceLogin)
try {
passwordVerified = PasswordSecurity.comparePasswordWithHash(password, hash, realName);
} catch (Exception ex) {
ConsoleLogger.showError(ex.getMessage());
m.send(player, MessageKey.ERROR);
return;
}
boolean passwordVerified = forceLogin || plugin.getPasswordSecurity()
.comparePassword(password, pAuth.getPassword(), realName);
if (passwordVerified && player.isOnline()) {
PlayerAuth auth = new PlayerAuth(name, hash, getIP(), new Date().getTime(), email, realName);
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.realName(realName)
.ip(getIP())
.lastLogin(new Date().getTime())
.email(email)
.password(pAuth.getPassword())
.build();
database.updateSession(auth);
if (Settings.useCaptcha) {

View File

@ -8,7 +8,7 @@ import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PlayerPermission;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import org.bukkit.entity.Player;
@ -44,7 +44,10 @@ public class AsyncRegister {
} else if (!Settings.isRegistrationEnabled) {
m.send(player, MessageKey.REGISTRATION_DISABLED);
return false;
} else if (passLow.contains("delete") || passLow.contains("where") || passLow.contains("insert") || passLow.contains("modify") || passLow.contains("from") || passLow.contains("select") || passLow.contains(";") || passLow.contains("null") || !passLow.matches(Settings.getPassRegex)) {
} else if (passLow.contains("delete") || passLow.contains("where") || passLow.contains("insert")
|| passLow.contains("modify") || passLow.contains("from") || passLow.contains("select")
|| passLow.contains(";") || passLow.contains("null") || !passLow.matches(Settings.getPassRegex)) {
// TODO #308: Remove check for SQL keywords
m.send(player, MessageKey.PASSWORD_MATCH_ERROR);
return false;
} else if (passLow.equalsIgnoreCase(player.getName())) {
@ -87,26 +90,21 @@ public class AsyncRegister {
}
}
private void emailRegister() throws Exception {
private void emailRegister() {
if (Settings.getmaxRegPerEmail > 0
&& !plugin.getPermissionsManager().hasPermission(player, PlayerPermission.ALLOW_MULTIPLE_ACCOUNTS)
&& database.getAllAuthsByEmail(email).size() >= Settings.getmaxRegPerEmail) {
m.send(player, MessageKey.MAX_REGISTER_EXCEEDED);
return;
}
final String hashNew = PasswordSecurity.getHash(Settings.getPasswordHash, password, name);
final String salt = PasswordSecurity.userSalt.get(name);
final EncryptedPassword encryptedPassword = plugin.getPasswordSecurity().computeHash(password, name);
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.realName(player.getName())
.hash(hashNew)
.password(encryptedPassword)
.ip(ip)
.locWorld(player.getLocation().getWorld().getName())
.locX(player.getLocation().getX())
.locY(player.getLocation().getY())
.locZ(player.getLocation().getZ())
.location(player.getLocation())
.email(email)
.salt(salt != null ? salt : "")
.build();
if (!database.saveAuth(auth)) {
@ -122,18 +120,13 @@ public class AsyncRegister {
}
private void passwordRegister() throws Exception {
final String hashNew = PasswordSecurity.getHash(Settings.getPasswordHash, password, name);
final String salt = PasswordSecurity.userSalt.get(name);
final EncryptedPassword encryptedPassword = plugin.getPasswordSecurity().computeHash(password, name);
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.realName(player.getName())
.hash(hashNew)
.password(encryptedPassword)
.ip(ip)
.locWorld(player.getLocation().getWorld().getName())
.locX(player.getLocation().getX())
.locY(player.getLocation().getY())
.locZ(player.getLocation().getZ())
.salt(salt != null ? salt : "")
.location(player.getLocation())
.build();
if (!database.saveAuth(auth)) {

View File

@ -2,11 +2,11 @@ package fr.xephi.authme.process.unregister;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.cache.backup.JsonCache;
import fr.xephi.authme.cache.limbo.LimboCache;
import fr.xephi.authme.cache.limbo.LimboPlayer;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.settings.Settings;
@ -20,16 +20,12 @@ import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import java.security.NoSuchAlgorithmException;
/**
*/
public class AsynchronousUnregister {
protected final Player player;
protected final String name;
protected final String password;
protected final boolean force;
private final Player player;
private final String name;
private final String password;
private final boolean force;
private final AuthMe plugin;
private final Messages m;
private final JsonCache playerCache;
@ -52,65 +48,59 @@ public class AsynchronousUnregister {
this.playerCache = new JsonCache();
}
/**
* Method getIp.
*
* @return String
*/
protected String getIp() {
return plugin.getIP(player);
}
public void process() {
try {
if (force || PasswordSecurity.comparePasswordWithHash(password, PlayerCache.getInstance().getAuth(name).getHash(), player.getName())) {
if (!plugin.database.removeAuth(name)) {
m.send(player, MessageKey.ERROR);
return;
PlayerAuth cachedAuth = PlayerCache.getInstance().getAuth(name);
if (force || plugin.getPasswordSecurity().comparePassword(
password, cachedAuth.getPassword(), player.getName())) {
if (!plugin.getDataSource().removeAuth(name)) {
m.send(player, MessageKey.ERROR);
return;
}
int timeOut = Settings.getRegistrationTimeout * 20;
if (Settings.isForcedRegistrationEnabled) {
Utils.teleportToSpawn(player);
player.saveData();
PlayerCache.getInstance().removePlayer(player.getName().toLowerCase());
if (!Settings.getRegisteredGroup.isEmpty()) {
Utils.setGroup(player, GroupType.UNREGISTERED);
}
int timeOut = Settings.getRegistrationTimeout * 20;
if (Settings.isForcedRegistrationEnabled) {
Utils.teleportToSpawn(player);
player.saveData();
PlayerCache.getInstance().removePlayer(player.getName().toLowerCase());
if (!Settings.getRegisteredGroup.isEmpty()) {
Utils.setGroup(player, GroupType.UNREGISTERED);
}
LimboCache.getInstance().addLimboPlayer(player);
LimboPlayer limboPlayer = LimboCache.getInstance().getLimboPlayer(name);
int interval = Settings.getWarnMessageInterval;
BukkitScheduler scheduler = plugin.getServer().getScheduler();
if (timeOut != 0) {
BukkitTask id = scheduler.runTaskLaterAsynchronously(plugin,
new TimeoutTask(plugin, name, player), timeOut);
limboPlayer.setTimeoutTaskId(id);
}
limboPlayer.setMessageTaskId(scheduler.runTaskAsynchronously(plugin,
new MessageTask(plugin, name, m.retrieve(MessageKey.REGISTER_MESSAGE), interval)));
m.send(player, MessageKey.UNREGISTERED_SUCCESS);
ConsoleLogger.info(player.getDisplayName() + " unregistered himself");
return;
}
if (!Settings.unRegisteredGroup.isEmpty()) {
Utils.setGroup(player, Utils.GroupType.UNREGISTERED);
}
PlayerCache.getInstance().removePlayer(name);
// check if Player cache File Exist and delete it, preventing
// duplication of items
if (playerCache.doesCacheExist(player)) {
playerCache.removeCache(player);
}
// Apply blind effect
if (Settings.applyBlindEffect) {
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2));
LimboCache.getInstance().addLimboPlayer(player);
LimboPlayer limboPlayer = LimboCache.getInstance().getLimboPlayer(name);
int interval = Settings.getWarnMessageInterval;
BukkitScheduler scheduler = plugin.getServer().getScheduler();
if (timeOut != 0) {
BukkitTask id = scheduler.runTaskLaterAsynchronously(plugin,
new TimeoutTask(plugin, name, player), timeOut);
limboPlayer.setTimeoutTaskId(id);
}
limboPlayer.setMessageTaskId(scheduler.runTaskAsynchronously(plugin,
new MessageTask(plugin, name, m.retrieve(MessageKey.REGISTER_MESSAGE), interval)));
m.send(player, MessageKey.UNREGISTERED_SUCCESS);
ConsoleLogger.info(player.getDisplayName() + " unregistered himself");
Utils.teleportToSpawn(player);
} else {
m.send(player, MessageKey.WRONG_PASSWORD);
return;
}
} catch (NoSuchAlgorithmException ignored) {
if (!Settings.unRegisteredGroup.isEmpty()) {
Utils.setGroup(player, Utils.GroupType.UNREGISTERED);
}
PlayerCache.getInstance().removePlayer(name);
// check if Player cache File Exist and delete it, preventing
// duplication of items
if (playerCache.doesCacheExist(player)) {
playerCache.removeCache(player);
}
// Apply blind effect
if (Settings.applyBlindEffect) {
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2));
}
m.send(player, MessageKey.UNREGISTERED_SUCCESS);
ConsoleLogger.info(player.getDisplayName() + " unregistered himself");
Utils.teleportToSpawn(player);
} else {
m.send(player, MessageKey.WRONG_PASSWORD);
}
}
}

View File

@ -3,40 +3,42 @@ package fr.xephi.authme.security;
import fr.xephi.authme.security.crypts.EncryptionMethod;
/**
* The list of hash algorithms supported by AuthMe. The implementing class must define a public
* constructor which takes either no arguments, or a DataSource object (when the salt is stored
* separately, writes to the database are necessary).
*/
public enum HashAlgorithm {
MD5(fr.xephi.authme.security.crypts.MD5.class),
SHA1(fr.xephi.authme.security.crypts.SHA1.class),
SHA256(fr.xephi.authme.security.crypts.SHA256.class),
WHIRLPOOL(fr.xephi.authme.security.crypts.WHIRLPOOL.class),
XAUTH(fr.xephi.authme.security.crypts.XAUTH.class),
MD5VB(fr.xephi.authme.security.crypts.MD5VB.class),
PHPBB(fr.xephi.authme.security.crypts.PHPBB.class),
@Deprecated
PLAINTEXT(fr.xephi.authme.security.crypts.PLAINTEXT.class),
MYBB(fr.xephi.authme.security.crypts.MYBB.class),
IPB3(fr.xephi.authme.security.crypts.IPB3.class),
PHPFUSION(fr.xephi.authme.security.crypts.PHPFUSION.class),
SMF(fr.xephi.authme.security.crypts.SMF.class),
XENFORO(fr.xephi.authme.security.crypts.XF.class),
SALTED2MD5(fr.xephi.authme.security.crypts.SALTED2MD5.class),
JOOMLA(fr.xephi.authme.security.crypts.JOOMLA.class),
BCRYPT(fr.xephi.authme.security.crypts.BCRYPT.class),
WBB3(fr.xephi.authme.security.crypts.WBB3.class),
WBB4(fr.xephi.authme.security.crypts.WBB4.class),
SHA512(fr.xephi.authme.security.crypts.SHA512.class),
BCRYPT2Y(fr.xephi.authme.security.crypts.BCRYPT2Y.class),
CRAZYCRYPT1(fr.xephi.authme.security.crypts.CRAZYCRYPT1.class),
DOUBLEMD5(fr.xephi.authme.security.crypts.DOUBLEMD5.class),
IPB3(fr.xephi.authme.security.crypts.IPB3.class),
JOOMLA(fr.xephi.authme.security.crypts.JOOMLA.class),
MD5(fr.xephi.authme.security.crypts.MD5.class),
MD5VB(fr.xephi.authme.security.crypts.MD5VB.class),
MYBB(fr.xephi.authme.security.crypts.MYBB.class),
PBKDF2(fr.xephi.authme.security.crypts.CryptPBKDF2.class),
PBKDF2DJANGO(fr.xephi.authme.security.crypts.CryptPBKDF2Django.class),
WORDPRESS(fr.xephi.authme.security.crypts.WORDPRESS.class),
PHPBB(fr.xephi.authme.security.crypts.PHPBB.class),
PHPFUSION(fr.xephi.authme.security.crypts.PHPFUSION.class),
@Deprecated
PLAINTEXT(fr.xephi.authme.security.crypts.PLAINTEXT.class),
ROYALAUTH(fr.xephi.authme.security.crypts.ROYALAUTH.class),
CRAZYCRYPT1(fr.xephi.authme.security.crypts.CRAZYCRYPT1.class),
BCRYPT2Y(fr.xephi.authme.security.crypts.BCRYPT2Y.class),
SALTED2MD5(fr.xephi.authme.security.crypts.SALTED2MD5.class),
SALTEDSHA512(fr.xephi.authme.security.crypts.SALTEDSHA512.class),
SHA1(fr.xephi.authme.security.crypts.SHA1.class),
SHA256(fr.xephi.authme.security.crypts.SHA256.class),
SHA512(fr.xephi.authme.security.crypts.SHA512.class),
SMF(fr.xephi.authme.security.crypts.SMF.class),
WBB3(fr.xephi.authme.security.crypts.WBB3.class),
WBB4(fr.xephi.authme.security.crypts.WBB4.class),
WHIRLPOOL(fr.xephi.authme.security.crypts.WHIRLPOOL.class),
WORDPRESS(fr.xephi.authme.security.crypts.WORDPRESS.class),
XAUTH(fr.xephi.authme.security.crypts.XAUTH.class),
CUSTOM(null);
final Class<? extends EncryptionMethod> clazz;
private final Class<? extends EncryptionMethod> clazz;
/**
* Constructor for HashAlgorithm.

View File

@ -0,0 +1,85 @@
package fr.xephi.authme.security;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Hashing utilities (interface for common hashing algorithms).
*/
public final class HashUtils {
private HashUtils() {
}
/**
* Generate the SHA-1 digest of the given message.
*
* @param message The message to hash
* @return The resulting SHA-1 digest
*/
public static String sha1(String message) {
return hash(message, MessageDigestAlgorithm.SHA1);
}
/**
* Generate the SHA-256 digest of the given message.
*
* @param message The message to hash
* @return The resulting SHA-256 digest
*/
public static String sha256(String message) {
return hash(message, MessageDigestAlgorithm.SHA256);
}
/**
* Generate the SHA-512 digest of the given message.
*
* @param message The message to hash
* @return The resulting SHA-512 digest
*/
public static String sha512(String message) {
return hash(message, MessageDigestAlgorithm.SHA512);
}
/**
* Generate the MD5 digest of the given message.
*
* @param message The message to hash
* @return The resulting MD5 digest
*/
public static String md5(String message) {
return hash(message, MessageDigestAlgorithm.MD5);
}
/**
* Return a {@link MessageDigest} instance for the given algorithm.
*
* @param algorithm The desired algorithm
* @return MessageDigest instance for the given algorithm
*/
public static MessageDigest getDigest(MessageDigestAlgorithm algorithm) {
try {
return MessageDigest.getInstance(algorithm.getKey());
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("Your system seems not to support the hash algorithm '"
+ algorithm.getKey() + "'");
}
}
/**
* Hash the message with the given algorithm and return the hash in its hexadecimal notation.
*
* @param message The message to hash
* @param algorithm The algorithm to hash the message with
* @return The digest in its hexadecimal representation
*/
private static String hash(String message, MessageDigestAlgorithm algorithm) {
MessageDigest md = getDigest(algorithm);
md.reset();
md.update(message.getBytes());
byte[] digest = md.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
}
}

View File

@ -0,0 +1,30 @@
package fr.xephi.authme.security;
import java.security.MessageDigest;
/**
* The Java-supported names to get a {@link MessageDigest} instance with.
*
* @see <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#AppA">
* Crypto Spec Appendix A: Standard Names</a>
*/
public enum MessageDigestAlgorithm {
MD5("MD5"),
SHA1("SHA-1"),
SHA256("SHA-256"),
SHA512("SHA-512");
private final String key;
MessageDigestAlgorithm(String key) {
this.key = key;
}
public String getKey() {
return key;
}
}

View File

@ -1,182 +1,126 @@
package fr.xephi.authme.security;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.PasswordEncryptionEvent;
import fr.xephi.authme.security.crypts.BCRYPT;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.security.crypts.EncryptionMethod;
import fr.xephi.authme.settings.Settings;
import org.bukkit.Bukkit;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import org.bukkit.plugin.PluginManager;
/**
* Manager class for password-related operations.
*/
public class PasswordSecurity {
public static final HashMap<String, String> userSalt = new HashMap<>();
private static final SecureRandom rnd = new SecureRandom();
private final DataSource dataSource;
private final HashAlgorithm algorithm;
private final PluginManager pluginManager;
private final boolean supportOldAlgorithm;
public static String createSalt(int length)
throws NoSuchAlgorithmException {
byte[] msg = new byte[40];
rnd.nextBytes(msg);
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
sha1.reset();
byte[] digest = sha1.digest(msg);
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest)).substring(0, length);
public PasswordSecurity(DataSource dataSource, HashAlgorithm algorithm,
PluginManager pluginManager, boolean supportOldAlgorithm) {
this.dataSource = dataSource;
this.algorithm = algorithm;
this.pluginManager = pluginManager;
this.supportOldAlgorithm = supportOldAlgorithm;
}
public static String getHash(HashAlgorithm alg, String password,
String playerName) throws NoSuchAlgorithmException {
EncryptionMethod method;
try {
if (alg != HashAlgorithm.CUSTOM)
method = alg.getClazz().newInstance();
else method = null;
} catch (InstantiationException | IllegalAccessException e) {
throw new NoSuchAlgorithmException("Problem with hash algorithm '" + alg + "'", e);
}
String salt = "";
switch (alg) {
case SHA256:
salt = createSalt(16);
break;
case MD5VB:
salt = createSalt(16);
break;
case XAUTH:
salt = createSalt(12);
break;
case MYBB:
salt = createSalt(8);
userSalt.put(playerName, salt);
break;
case IPB3:
salt = createSalt(5);
userSalt.put(playerName, salt);
break;
case PHPFUSION:
salt = createSalt(12);
userSalt.put(playerName, salt);
break;
case SALTED2MD5:
salt = createSalt(Settings.saltLength);
userSalt.put(playerName, salt);
break;
case JOOMLA:
salt = createSalt(32);
userSalt.put(playerName, salt);
break;
case BCRYPT:
salt = BCRYPT.gensalt(Settings.bCryptLog2Rounds);
userSalt.put(playerName, salt);
break;
case WBB3:
salt = createSalt(40);
userSalt.put(playerName, salt);
break;
case WBB4:
salt = BCRYPT.gensalt(8);
userSalt.put(playerName, salt);
break;
case PBKDF2DJANGO:
case PBKDF2:
salt = createSalt(12);
userSalt.put(playerName, salt);
break;
case SMF:
return method.computeHash(password, null, playerName);
case PHPBB:
salt = createSalt(16);
userSalt.put(playerName, salt);
break;
case BCRYPT2Y:
salt = createSalt(16);
userSalt.put(playerName, salt);
break;
case SALTEDSHA512:
salt = createSalt(32);
userSalt.put(playerName, salt);
break;
case MD5:
case SHA1:
case WHIRLPOOL:
case PLAINTEXT:
case XENFORO:
case SHA512:
case ROYALAUTH:
case CRAZYCRYPT1:
case DOUBLEMD5:
case WORDPRESS:
case CUSTOM:
break;
default:
throw new NoSuchAlgorithmException("Unknown hash algorithm");
}
PasswordEncryptionEvent event = new PasswordEncryptionEvent(method, playerName);
Bukkit.getPluginManager().callEvent(event);
method = event.getMethod();
if (method == null)
throw new NoSuchAlgorithmException("Unknown hash algorithm");
return method.computeHash(password, salt, playerName);
public EncryptedPassword computeHash(String password, String playerName) {
return computeHash(algorithm, password, playerName);
}
public static boolean comparePasswordWithHash(String password, String hash,
String playerName) throws NoSuchAlgorithmException {
HashAlgorithm algorithm = Settings.getPasswordHash;
EncryptionMethod method;
try {
if (algorithm != HashAlgorithm.CUSTOM) {
method = algorithm.getClazz().newInstance();
} else {
method = null;
}
public EncryptedPassword computeHash(HashAlgorithm algorithm, String password, String playerName) {
EncryptionMethod method = initializeEncryptionMethod(algorithm, playerName);
return method.computeHash(password, playerName);
}
PasswordEncryptionEvent event = new PasswordEncryptionEvent(method, playerName);
Bukkit.getPluginManager().callEvent(event);
method = event.getMethod();
if (method == null)
throw new NoSuchAlgorithmException("Unknown hash algorithm");
if (method.comparePassword(hash, password, playerName))
return true;
if (Settings.supportOldPassword) {
if (compareWithAllEncryptionMethod(password, hash, playerName))
return true;
}
} catch (InstantiationException | IllegalAccessException e) {
throw new NoSuchAlgorithmException("Problem with this hash algorithm");
public boolean comparePassword(String password, String playerName) {
// TODO ljacqu 20151230: Defining a dataSource.getPassword() method would be more efficient
PlayerAuth auth = dataSource.getAuth(playerName);
if (auth != null) {
return comparePassword(password, auth.getPassword(), playerName);
}
return false;
}
private static boolean compareWithAllEncryptionMethod(String password,
String hash, String playerName) {
for (HashAlgorithm algo : HashAlgorithm.values()) {
if (algo != HashAlgorithm.CUSTOM) {
try {
EncryptionMethod method = algo.getClazz().newInstance();
if (method.comparePassword(hash, password, playerName)) {
PlayerAuth nAuth = AuthMe.getInstance().database.getAuth(playerName);
if (nAuth != null) {
nAuth.setHash(getHash(Settings.getPasswordHash, password, playerName));
nAuth.setSalt(userSalt.containsKey(playerName) ? userSalt.get(playerName) : "");
AuthMe.getInstance().database.updatePassword(nAuth);
AuthMe.getInstance().database.updateSalt(nAuth);
}
return true;
}
} catch (Exception ignored) {
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String playerName) {
EncryptionMethod method = initializeEncryptionMethod(algorithm, playerName);
// User is not in data source, so the result will invariably be wrong because an encryption
// method with hasSeparateSalt() == true NEEDS the salt to evaluate the password
String salt = encryptedPassword.getSalt();
if (method.hasSeparateSalt() && salt == null) {
return false;
}
return method.comparePassword(password, encryptedPassword, playerName)
|| supportOldAlgorithm && compareWithAllEncryptionMethods(password, encryptedPassword, playerName);
}
/**
* Compare the given hash with all available encryption methods to support
* the migration to a new encryption method. Upon a successful match, the password
* will be hashed with the new encryption method and persisted.
*
* @param password The clear-text password to check
* @param encryptedPassword The encrypted password to test the clear-text password against
* @param playerName The name of the player
* @return True if the
*/
private boolean compareWithAllEncryptionMethods(String password, EncryptedPassword encryptedPassword,
String playerName) {
for (HashAlgorithm algorithm : HashAlgorithm.values()) {
if (!HashAlgorithm.CUSTOM.equals(algorithm)) {
EncryptionMethod method = initializeEncryptionMethodWithoutEvent(algorithm);
if (method != null && method.comparePassword(password, encryptedPassword, playerName)) {
hashPasswordForNewAlgorithm(password, playerName);
return true;
}
}
}
return false;
}
/**
* Get the encryption method from the given {@link HashAlgorithm} value and emit a
* {@link PasswordEncryptionEvent}. The encryption method from the event is then returned,
* which may have been changed by an external listener.
*
* @param algorithm The algorithm to retrieve the encryption method for
* @param playerName The name of the player a password will be hashed for
* @return The encryption method
*/
private EncryptionMethod initializeEncryptionMethod(HashAlgorithm algorithm, String playerName) {
EncryptionMethod method = initializeEncryptionMethodWithoutEvent(algorithm);
PasswordEncryptionEvent event = new PasswordEncryptionEvent(method, playerName);
pluginManager.callEvent(event);
return event.getMethod();
}
/**
* Initialize the encryption method corresponding to the given hash algorithm.
*
* @param algorithm The algorithm to retrieve the encryption method for
* @return The associated encryption method
*/
private static EncryptionMethod initializeEncryptionMethodWithoutEvent(HashAlgorithm algorithm) {
try {
return HashAlgorithm.CUSTOM.equals(algorithm)
? null
: algorithm.getClazz().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new UnsupportedOperationException("Constructor for '" + algorithm.getClazz().getSimpleName()
+ "' could not be invoked. (Is there no default constructor?)", e);
}
}
private void hashPasswordForNewAlgorithm(String password, String playerName) {
PlayerAuth auth = dataSource.getAuth(playerName);
if (auth != null) {
EncryptedPassword encryptedPassword = initializeEncryptionMethod(algorithm, playerName)
.computeHash(password, playerName);
auth.setPassword(encryptedPassword);
dataSource.updatePassword(auth);
}
}
}

View File

@ -1,13 +1,16 @@
package fr.xephi.authme.security;
import java.security.SecureRandom;
import java.util.Calendar;
import java.util.Random;
public class RandomString {
/**
* Utility for generating random strings.
*/
public final class RandomString {
private static final char[] chars = new char[36];
private static final Random RANDOM = new SecureRandom();
private static final int HEX_MAX_INDEX = 16;
static {
for (int idx = 0; idx < 10; ++idx) {
@ -18,30 +21,37 @@ public class RandomString {
}
}
private final Random random = new Random();
private final char[] buf;
public RandomString(int length) {
if (length < 1)
throw new IllegalArgumentException("length < 1: " + length);
buf = new char[length];
random.setSeed(Calendar.getInstance().getTimeInMillis());
}
public String nextString() {
for (int idx = 0; idx < buf.length; ++idx)
buf[idx] = chars[random.nextInt(chars.length)];
return new String(buf);
private RandomString() {
}
/**
* Generate a string of the given length consisting of random characters within the range [0-9a-z].
*
* @param length The length of the random string to generate
* @return The random string
*/
public static String generate(int length) {
return generate(length, chars.length);
}
/**
* Generate a random hexadecimal string of the given length. In other words, the generated string
* contains characters only within the range [0-9a-f].
*
* @param length The length of the random string to generate
* @return The random hexadecimal string
*/
public static String generateHex(int length) {
return generate(length, HEX_MAX_INDEX);
}
private static String generate(int length, int maxIndex) {
if (length < 0) {
throw new IllegalArgumentException("Length must be positive but was " + length);
}
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; ++i) {
sb.append(chars[RANDOM.nextInt(chars.length)]);
sb.append(chars[RANDOM.nextInt(maxIndex)]);
}
return sb.toString();
}

View File

@ -13,8 +13,13 @@
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Usage;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.settings.Settings;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
@ -59,11 +64,13 @@ import java.security.SecureRandom;
* @author Damien Miller
* @version 0.2
*/
@Recommendation(Usage.RECOMMENDED) // provided the salt length is >= 8
@HasSalt(value = SaltType.TEXT) // length depends on Settings.bCryptLog2Rounds
public class BCRYPT implements EncryptionMethod {
// BCrypt parameters
private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
private static final int BCRYPT_SALT_LEN = 16;
protected static final int BCRYPT_SALT_LEN = 16;
// Blowfish parameters
private static final int BLOWFISH_NUM_ROUNDS = 16;
@ -508,14 +515,28 @@ public class BCRYPT implements EncryptionMethod {
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
public String computeHash(String password, String salt, String name) {
return hashpw(password, salt);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return checkpw(password, hash);
public EncryptedPassword computeHash(String password, String name) {
String salt = generateSalt();
return new EncryptedPassword(hashpw(password, salt), null);
}
@Override
public boolean comparePassword(String password, EncryptedPassword hash, String name) {
return checkpw(password, hash.getHash());
}
@Override
public String generateSalt() {
return BCRYPT.gensalt(Settings.bCryptLog2Rounds);
}
@Override
public boolean hasSeparateSalt() {
return false;
}
}

View File

@ -1,26 +1,34 @@
package fr.xephi.authme.security.crypts;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.Usage;
/**
*/
public class BCRYPT2Y implements EncryptionMethod {
@Recommendation(Usage.RECOMMENDED)
public class BCRYPT2Y extends HexSaltedMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
if (salt.length() == 22)
public String computeHash(String password, String salt, String name) {
if (salt.length() == 22) {
salt = "$2y$10$" + salt;
return (BCRYPT.hashpw(password, salt));
}
return BCRYPT.hashpw(password, salt);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String ok = hash.substring(0, 29);
if (ok.length() != 29)
public boolean comparePassword(String password, EncryptedPassword encrypted, String unusedName) {
String hash = encrypted.getHash();
if (hash.length() != 60) {
return false;
return hash.equals(computeHash(password, ok, playerName));
}
// The salt is the first 29 characters of the hash
String salt = hash.substring(0, 29);
return hash.equals(computeHash(password, salt, null));
}
@Override
public int getSaltLength() {
return 22;
}
}

View File

@ -1,18 +1,24 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.MessageDigestAlgorithm;
import fr.xephi.authme.security.crypts.description.Usage;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.HasSalt;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
*/
public class CRAZYCRYPT1 implements EncryptionMethod {
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.USERNAME)
public class CRAZYCRYPT1 extends UsernameSaltMethod {
private static final char[] CRYPTCHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
protected final Charset charset = Charset.forName("UTF-8");
private static final char[] CRYPTCHARS =
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private final Charset charset = Charset.forName("UTF-8");
public static String byteArrayToHexString(final byte... args) {
private static String byteArrayToHexString(final byte... args) {
final char[] chars = new char[args.length * 2];
for (int i = 0; i < args.length; i++) {
chars[i * 2] = CRYPTCHARS[(args[i] >> 4) & 0xF];
@ -22,21 +28,11 @@ public class CRAZYCRYPT1 implements EncryptionMethod {
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
public EncryptedPassword computeHash(String password, String name) {
final String text = "ÜÄaeut//&/=I " + password + "7421€547" + name + "__+IÄIH§%NK " + password;
try {
final MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(text.getBytes(charset), 0, text.length());
return byteArrayToHexString(md.digest());
} catch (final NoSuchAlgorithmException e) {
return null;
}
final MessageDigest md = HashUtils.getDigest(MessageDigestAlgorithm.SHA512);
md.update(text.getBytes(charset), 0, text.length());
return new EncryptedPassword(byteArrayToHexString(md.digest()));
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return hash.equals(computeHash(password, null, playerName));
}
}

View File

@ -1,18 +1,17 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.Usage;
import fr.xephi.authme.security.pbkdf2.PBKDF2Engine;
import fr.xephi.authme.security.pbkdf2.PBKDF2Parameters;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
*/
public class CryptPBKDF2 implements EncryptionMethod {
@Recommendation(Usage.DOES_NOT_WORK)
public class CryptPBKDF2 extends HexSaltedMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
public String computeHash(String password, String salt, String name) {
String result = "pbkdf2_sha256$10000$" + salt + "$";
PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "ASCII", salt.getBytes(), 10000);
PBKDF2Engine engine = new PBKDF2Engine(params);
@ -21,9 +20,8 @@ public class CryptPBKDF2 implements EncryptionMethod {
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String[] line = hash.split("\\$");
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String unusedName) {
String[] line = encryptedPassword.getHash().split("\\$");
String salt = line[2];
String derivedKey = line[3];
PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "ASCII", salt.getBytes(), 10000, derivedKey.getBytes());
@ -31,4 +29,9 @@ public class CryptPBKDF2 implements EncryptionMethod {
return engine.verifyKey(password);
}
@Override
public int getSaltLength() {
return 12;
}
}

View File

@ -1,18 +1,16 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.crypts.description.AsciiRestricted;
import fr.xephi.authme.security.pbkdf2.PBKDF2Engine;
import fr.xephi.authme.security.pbkdf2.PBKDF2Parameters;
import javax.xml.bind.DatatypeConverter;
import java.security.NoSuchAlgorithmException;
/**
*/
public class CryptPBKDF2Django implements EncryptionMethod {
@AsciiRestricted
public class CryptPBKDF2Django extends HexSaltedMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
public String computeHash(String password, String salt, String name) {
String result = "pbkdf2_sha256$15000$" + salt + "$";
PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "ASCII", salt.getBytes(), 15000);
PBKDF2Engine engine = new PBKDF2Engine(params);
@ -21,9 +19,8 @@ public class CryptPBKDF2Django implements EncryptionMethod {
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String[] line = hash.split("\\$");
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String unusedName) {
String[] line = encryptedPassword.getHash().split("\\$");
String salt = line[2];
byte[] derivedKey = DatatypeConverter.parseBase64Binary(line[3]);
PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "ASCII", salt.getBytes(), 15000, derivedKey);
@ -31,4 +28,9 @@ public class CryptPBKDF2Django implements EncryptionMethod {
return engine.verifyKey(password);
}
@Override
public int getSaltLength() {
return 12;
}
}

View File

@ -1,32 +1,12 @@
package fr.xephi.authme.security.crypts;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import static fr.xephi.authme.security.HashUtils.md5;
/**
*/
public class DOUBLEMD5 implements EncryptionMethod {
private static String getMD5(String message)
throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
md5.update(message.getBytes());
byte[] digest = md5.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
}
public class DOUBLEMD5 extends UnsaltedMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getMD5(getMD5(password));
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return hash.equals(computeHash(password, "", ""));
public String computeHash(String password) {
return md5(md5(password));
}
}

View File

@ -0,0 +1,48 @@
package fr.xephi.authme.security.crypts;
/**
* The result of a hash computation. See {@link #salt} for details.
*/
public class EncryptedPassword {
/** The generated hash. */
private final String hash;
/**
* The generated salt; may be null if no salt is used or if the salt is included
* in the hash output. The salt is only not null if {@link EncryptionMethod#hasSeparateSalt()}
* returns true for the associated encryption method.
* <p>
* When the field is not null, it must be stored into the salt column of the data source
* and retrieved again to compare a password with the hash.
*/
private final String salt;
/**
* Constructor.
*
* @param hash The computed hash
* @param salt The generated salt
*/
public EncryptedPassword(String hash, String salt) {
this.hash = hash;
this.salt = salt;
}
/**
* Constructor for a hash with no separate salt.
*
* @param hash The computed hash
*/
public EncryptedPassword(String hash) {
this(hash, null);
}
public String getHash() {
return hash;
}
public String getSalt() {
return salt;
}
}

View File

@ -1,35 +1,59 @@
package fr.xephi.authme.security.crypts;
import java.security.NoSuchAlgorithmException;
/**
* Public interface for custom password encryption methods.
*/
public interface EncryptionMethod {
/**
* Hash the given password with the given salt for the given player.
* Hash the given password for the given player name.
*
* @param password The clear-text password to hash
* @param salt The salt to add to the hash
* @param name The player's name (sometimes required for storing the salt separately in the database)
* @param password The password to hash
* @param name The name of the player (sometimes required to generate a salt with)
*
* @return The hashed password
* @return The hash result for the password.
* @see EncryptedPassword
*/
String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException;
EncryptedPassword computeHash(String password, String name);
/**
* Check whether a given hash matches the clear-text password.
* Hash the given password with the given salt for the given player.
*
* @param hash The hash to verify
* @param password The clear-text password to verify the hash against
* @param playerName The player name to do the check for (sometimes required for retrieving
* the salt from the database)
* @param password The password to hash
* @param salt The salt to add to the hash
* @param name The player's name (sometimes required to generate a salt with)
*
* @return The hashed password
* @see #hasSeparateSalt()
*/
String computeHash(String password, String salt, String name);
/**
* Check whether the given hash matches the clear-text password.
*
* @param password The clear-text password to verify
* @param encryptedPassword The hash to check the password against
* @param name The player name to do the check for (sometimes required for generating the salt)
*
* @return True if the password matches, false otherwise
*/
boolean comparePassword(String hash, String password, String playerName)
throws NoSuchAlgorithmException;
boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name);
/**
* Generate a new salt to hash a password with.
*
* @return The generated salt, null if the method does not use a random text-based salt
*/
String generateSalt();
/**
* Return whether the encryption method requires the salt to be stored separately and
* passed again to {@link #comparePassword(String, EncryptedPassword, String)}. Note that
* an encryption method returning {@code false} does not imply that it uses no salt; it
* may be embedded into the hash or it may use the username as salt.
*
* @return True if the salt has to be stored and retrieved separately, false otherwise
*/
boolean hasSeparateSalt();
}

View File

@ -0,0 +1,40 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
/**
* Common type for encryption methods which use a random String of hexadecimal characters
* and store the salt with the hash itself.
*/
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(SaltType.TEXT) // See saltLength() for length
public abstract class HexSaltedMethod implements EncryptionMethod {
public abstract int getSaltLength();
@Override
public abstract String computeHash(String password, String salt, String name);
@Override
public EncryptedPassword computeHash(String password, String name) {
String salt = generateSalt();
return new EncryptedPassword(computeHash(password, salt, null));
}
@Override
public abstract boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name);
@Override
public String generateSalt() {
return RandomString.generateHex(getSaltLength());
}
@Override
public boolean hasSeparateSalt() {
return false;
}
}

View File

@ -1,34 +1,25 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import static fr.xephi.authme.security.HashUtils.md5;
/**
*/
public class IPB3 implements EncryptionMethod {
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(value = SaltType.TEXT, length = 5)
public class IPB3 extends SeparateSaltMethod {
private static String getMD5(String message)
throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
md5.update(message.getBytes());
byte[] digest = md5.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
@Override
public String computeHash(String password, String salt, String name) {
return md5(md5(salt) + md5(password));
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getMD5(getMD5(salt) + getMD5(password));
public String generateSalt() {
return RandomString.generateHex(5);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt();
return hash.equals(computeHash(password, salt, playerName));
}
}

View File

@ -1,32 +1,27 @@
package fr.xephi.authme.security.crypts;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.Usage;
/**
*/
public class JOOMLA implements EncryptionMethod {
@Recommendation(Usage.RECOMMENDED)
public class JOOMLA extends HexSaltedMethod {
private static String getMD5(String message)
throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
md5.update(message.getBytes());
byte[] digest = md5.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
@Override
public String computeHash(String password, String salt, String name) {
return HashUtils.md5(password + salt) + ":" + salt;
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getMD5(password + salt) + ":" + salt;
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String unusedName) {
String hash = encryptedPassword.getHash();
String[] hashParts = hash.split(":");
return hashParts.length == 2 && hash.equals(computeHash(password, hashParts[1], null));
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String salt = hash.split(":")[1];
return hash.equals(getMD5(password + salt) + ":" + salt);
public int getSaltLength() {
return 32;
}
}

View File

@ -1,31 +1,16 @@
package fr.xephi.authme.security.crypts;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
/**
*/
public class MD5 implements EncryptionMethod {
private static String getMD5(String message)
throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
md5.update(message.getBytes());
byte[] digest = md5.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
}
public class MD5 extends UnsaltedMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getMD5(password);
public String computeHash(String password) {
return HashUtils.md5(password);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return hash.equals(computeHash(password, "", ""));
}
}

View File

@ -1,33 +1,24 @@
package fr.xephi.authme.security.crypts;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import static fr.xephi.authme.security.HashUtils.md5;
/**
*/
public class MD5VB implements EncryptionMethod {
public class MD5VB extends HexSaltedMethod {
private static String getMD5(String message)
throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
md5.update(message.getBytes());
byte[] digest = md5.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
@Override
public String computeHash(String password, String salt, String name) {
return "$MD5vb$" + salt + "$" + md5(md5(password) + salt);
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return "$MD5vb$" + salt + "$" + getMD5(getMD5(password) + salt);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
String hash = encryptedPassword.getHash();
String[] line = hash.split("\\$");
return hash.equals(computeHash(password, line[2], ""));
return line.length == 4 && hash.equals(computeHash(password, line[2], name));
}
@Override
public int getSaltLength() {
return 16;
}
}

View File

@ -1,34 +1,19 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.security.RandomString;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import static fr.xephi.authme.security.HashUtils.md5;
/**
*/
public class MYBB implements EncryptionMethod {
public class MYBB extends SeparateSaltMethod {
private static String getMD5(String message)
throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
md5.update(message.getBytes());
byte[] digest = md5.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
@Override
public String computeHash(String password, String salt, String name) {
return md5(md5(salt) + md5(password));
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getMD5(getMD5(salt) + getMD5(password));
public String generateSalt() {
return RandomString.generateHex(8);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt();
return hash.equals(computeHash(password, salt, playerName));
}
}

View File

@ -4,25 +4,26 @@
*/
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.MessageDigestAlgorithm;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author stefano
*/
public class PHPBB implements EncryptionMethod {
public class PHPBB extends HexSaltedMethod {
private static final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public static String md5(String data) {
private static String md5(String data) {
try {
byte[] bytes = data.getBytes("ISO-8859-1");
MessageDigest md5er = MessageDigest.getInstance("MD5");
MessageDigest md5er = HashUtils.getDigest(MessageDigestAlgorithm.MD5);
byte[] hash = md5er.digest(bytes);
return bytes2hex(hash);
} catch (GeneralSecurityException | UnsupportedEncodingException e) {
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
@ -58,7 +59,7 @@ public class PHPBB implements EncryptionMethod {
return buf.toString();
}
public String phpbb_hash(String password, String salt) {
private String phpbb_hash(String password, String salt) {
String random_state = salt;
StringBuilder random = new StringBuilder();
int count = 6;
@ -109,7 +110,7 @@ public class PHPBB implements EncryptionMethod {
return output.toString();
}
String _hash_crypt_private(String password, String setting) {
private String _hash_crypt_private(String password, String setting) {
String output = "*";
if (!setting.substring(0, 3).equals("$H$"))
return output;
@ -130,21 +131,26 @@ public class PHPBB implements EncryptionMethod {
return output;
}
public boolean phpbb_check_hash(String password, String hash) {
if (hash.length() == 34)
private boolean phpbb_check_hash(String password, String hash) {
if (hash.length() == 34) {
return _hash_crypt_private(password, hash).equals(hash);
else return md5(password).equals(hash);
}
return md5(password).equals(hash);
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
public String computeHash(String password, String salt, String name) {
return phpbb_hash(password, salt);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return phpbb_check_hash(password, hash);
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
return phpbb_check_hash(password, encryptedPassword.getHash());
}
@Override
public int getSaltLength() {
return 16;
}
}

View File

@ -1,36 +1,27 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.AsciiRestricted;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.Usage;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
*/
public class PHPFUSION implements EncryptionMethod {
private static String getSHA1(String message)
throws NoSuchAlgorithmException {
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
sha1.reset();
sha1.update(message.getBytes());
byte[] digest = sha1.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
}
@Recommendation(Usage.DO_NOT_USE)
@AsciiRestricted
public class PHPFUSION extends SeparateSaltMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
String digest = null;
public String computeHash(String password, String salt, String name) {
String algo = "HmacSHA256";
String keyString = getSHA1(salt);
String keyString = HashUtils.sha1(salt);
try {
SecretKeySpec key = new SecretKeySpec((keyString).getBytes("UTF-8"), algo);
SecretKeySpec key = new SecretKeySpec(keyString.getBytes("UTF-8"), algo);
Mac mac = Mac.getInstance(algo);
mac.init(key);
byte[] bytes = mac.doFinal(password.getBytes("ASCII"));
@ -42,19 +33,16 @@ public class PHPFUSION implements EncryptionMethod {
}
hash.append(hex);
}
digest = hash.toString();
return hash.toString();
} catch (UnsupportedEncodingException | InvalidKeyException | NoSuchAlgorithmException e) {
//ingore
throw new UnsupportedOperationException("Cannot create PHPFUSION hash for " + name, e);
}
return digest;
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt();
return hash.equals(computeHash(password, salt, ""));
public String generateSalt() {
return RandomString.generateHex(12);
}
}

View File

@ -1,21 +1,11 @@
package fr.xephi.authme.security.crypts;
import java.security.NoSuchAlgorithmException;
/**
*/
public class PLAINTEXT implements EncryptionMethod {
@Deprecated
public class PLAINTEXT extends UnsaltedMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
public String computeHash(String password) {
return password;
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return hash.equals(password);
}
}

View File

@ -1,35 +1,16 @@
package fr.xephi.authme.security.crypts;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.HashUtils;
/**
*/
public class ROYALAUTH implements EncryptionMethod {
public class ROYALAUTH extends UnsaltedMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
for (int i = 0; i < 25; i++)
password = hash(password, salt);
public String computeHash(String password) {
for (int i = 0; i < 25; i++) {
// TODO ljacqu 20151228: HashUtils#sha512 gets a new message digest each time...
password = HashUtils.sha512(password);
}
return password;
}
public String hash(String password, String salt)
throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(password.getBytes());
byte byteData[] = md.digest();
StringBuilder sb = new StringBuilder();
for (byte aByteData : byteData)
sb.append(Integer.toString((aByteData & 0xff) + 0x100, 16).substring(1));
return sb.toString();
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return hash.equalsIgnoreCase(computeHash(password, "", ""));
}
}

View File

@ -1,34 +1,26 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
import fr.xephi.authme.settings.Settings;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import static fr.xephi.authme.security.HashUtils.md5;
/**
*/
public class SALTED2MD5 implements EncryptionMethod {
@Recommendation(Usage.ACCEPTABLE) // presuming that length is something sensible (>= 8)
@HasSalt(value = SaltType.TEXT) // length defined by Settings.saltLength
public class SALTED2MD5 extends SeparateSaltMethod {
private static String getMD5(String message)
throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
md5.update(message.getBytes());
byte[] digest = md5.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
@Override
public String computeHash(String password, String salt, String name) {
return md5(md5(password) + salt);
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getMD5(getMD5(password) + salt);
public String generateSalt() {
return RandomString.generateHex(Settings.saltLength);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt();
return hash.equals(getMD5(getMD5(password) + salt));
}
}

View File

@ -1,34 +1,20 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.Usage;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@Recommendation(Usage.RECOMMENDED)
public class SALTEDSHA512 extends SeparateSaltMethod {
/**
*/
public class SALTEDSHA512 implements EncryptionMethod {
private static String getSHA512(String message)
throws NoSuchAlgorithmException {
MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
sha512.reset();
sha512.update(message.getBytes());
byte[] digest = sha512.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
@Override
public String computeHash(String password, String salt, String name) {
return HashUtils.sha512(password + salt);
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getSHA512(password + salt);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt();
return hash.equals(computeHash(password, salt, ""));
public String generateSalt() {
return RandomString.generateHex(32);
}
}

View File

@ -1,32 +1,12 @@
package fr.xephi.authme.security.crypts;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.HashUtils;
/**
*/
public class SHA1 implements EncryptionMethod {
private static String getSHA1(String message)
throws NoSuchAlgorithmException {
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
sha1.reset();
sha1.update(message.getBytes());
byte[] digest = sha1.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
}
public class SHA1 extends UnsaltedMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getSHA1(password);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return hash.equals(computeHash(password, "", ""));
public String computeHash(String password) {
return HashUtils.sha1(password);
}
}

View File

@ -1,33 +1,28 @@
package fr.xephi.authme.security.crypts;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.Usage;
/**
*/
public class SHA256 implements EncryptionMethod {
import static fr.xephi.authme.security.HashUtils.sha256;
private static String getSHA256(String message)
throws NoSuchAlgorithmException {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.reset();
sha256.update(message.getBytes());
byte[] digest = sha256.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
@Recommendation(Usage.RECOMMENDED)
public class SHA256 extends HexSaltedMethod {
@Override
public String computeHash(String password, String salt, String name) {
return "$SHA$" + salt + "$" + sha256(sha256(password) + salt);
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return "$SHA$" + salt + "$" + getSHA256(getSHA256(password) + salt);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String playerName) {
String hash = encryptedPassword.getHash();
String[] line = hash.split("\\$");
return hash.equals(computeHash(password, line[2], ""));
return line.length == 4 && hash.equals(computeHash(password, line[2], ""));
}
@Override
public int getSaltLength() {
return 16;
}
}

View File

@ -1,31 +1,12 @@
package fr.xephi.authme.security.crypts;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.HashUtils;
/**
*/
public class SHA512 implements EncryptionMethod {
private static String getSHA512(String message)
throws NoSuchAlgorithmException {
MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
sha512.reset();
sha512.update(message.getBytes());
byte[] digest = sha512.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
}
public class SHA512 extends UnsaltedMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getSHA512(password);
public String computeHash(String password) {
return HashUtils.sha512(password);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return hash.equals(computeHash(password, "", ""));
}
}

View File

@ -1,31 +1,11 @@
package fr.xephi.authme.security.crypts;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.HashUtils;
/**
*/
public class SMF implements EncryptionMethod {
public class SMF extends UsernameSaltMethod {
private static String getSHA1(String message)
throws NoSuchAlgorithmException {
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
sha1.reset();
sha1.update(message.getBytes());
byte[] digest = sha1.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
public EncryptedPassword computeHash(String password, String name) {
return new EncryptedPassword(HashUtils.sha1(name.toLowerCase() + password));
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getSHA1(name.toLowerCase() + password);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return hash.equals(computeHash(password, null, playerName));
}
}

View File

@ -0,0 +1,30 @@
package fr.xephi.authme.security.crypts;
/**
* Common supertype for encryption methods which store their salt separately from the hash.
*/
public abstract class SeparateSaltMethod implements EncryptionMethod {
@Override
public abstract String computeHash(String password, String salt, String name);
@Override
public abstract String generateSalt();
@Override
public EncryptedPassword computeHash(String password, String name) {
String salt = generateSalt();
return new EncryptedPassword(computeHash(password, salt, name), salt);
}
@Override
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
return encryptedPassword.getHash().equals(computeHash(password, encryptedPassword.getSalt(), null));
}
@Override
public boolean hasSeparateSalt() {
return true;
}
}

View File

@ -0,0 +1,41 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
/**
* Common type for encryption methods which do not use any salt whatsoever.
*/
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.NONE)
public abstract class UnsaltedMethod implements EncryptionMethod {
public abstract String computeHash(String password);
@Override
public EncryptedPassword computeHash(String password, String name) {
return new EncryptedPassword(computeHash(password));
}
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password);
}
@Override
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
return encryptedPassword.getHash().equals(computeHash(password));
}
@Override
public String generateSalt() {
return null;
}
@Override
public boolean hasSeparateSalt() {
return false;
}
}

View File

@ -0,0 +1,39 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
/**
* Common supertype of encryption methods that use a player's username
* (or something based on it) as embedded salt.
*/
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.USERNAME)
public abstract class UsernameSaltMethod implements EncryptionMethod {
@Override
public abstract EncryptedPassword computeHash(String password, String name);
@Override
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
return encryptedPassword.getHash().equals(computeHash(password, name).getHash());
}
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password, name).getHash();
}
@Override
public String generateSalt() {
return null;
}
@Override
public boolean hasSeparateSalt() {
return false;
}
}

View File

@ -1,34 +1,19 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.security.RandomString;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import static fr.xephi.authme.security.HashUtils.sha1;
/**
*/
public class WBB3 implements EncryptionMethod {
public class WBB3 extends SeparateSaltMethod {
private static String getSHA1(String message)
throws NoSuchAlgorithmException {
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
sha1.reset();
sha1.update(message.getBytes());
byte[] digest = sha1.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
@Override
public String computeHash(String password, String salt, String name) {
return sha1(salt.concat(sha1(salt.concat(sha1(password)))));
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getSHA1(salt.concat(getSHA1(salt.concat(getSHA1(password)))));
public String generateSalt() {
return RandomString.generateHex(40);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt();
return hash.equals(computeHash(password, salt, ""));
}
}

View File

@ -1,21 +1,34 @@
package fr.xephi.authme.security.crypts;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.Usage;
/**
*/
public class WBB4 implements EncryptionMethod {
@Recommendation(Usage.DOES_NOT_WORK)
public class WBB4 extends HexSaltedMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
public String computeHash(String password, String salt, String name) {
return BCRYPT.getDoubleHash(password, salt);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return BCRYPT.checkpw(password, hash, 2);
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String playerName) {
return BCRYPT.checkpw(password, encryptedPassword.getHash(), 2);
}
@Override
public String generateSalt() {
return BCRYPT.gensalt(8);
}
/**
* Note that {@link #generateSalt()} is overridden for this class.
*
* @return The salt length
*/
@Override
public int getSaltLength() {
return 8;
}
}

View File

@ -59,12 +59,9 @@ package fr.xephi.authme.security.crypts;
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
*/
public class WHIRLPOOL implements EncryptionMethod {
public class WHIRLPOOL extends UnsaltedMethod {
/**
* The message digest size (in bits)
@ -382,9 +379,7 @@ public class WHIRLPOOL implements EncryptionMethod {
}
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
public String computeHash(String password) {
byte[] digest = new byte[DIGESTBYTES];
NESSIEinit();
NESSIEadd(password);
@ -392,9 +387,4 @@ public class WHIRLPOOL implements EncryptionMethod {
return display(digest);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
return hash.equals(computeHash(password, "", ""));
}
}

View File

@ -1,14 +1,22 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
/**
*/
public class WORDPRESS implements EncryptionMethod {
// TODO #391: Wordpress algorithm fails sometimes. Fix it and change the Recommendation to "ACCEPTABLE" if appropriate
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(value = SaltType.TEXT, length = 9)
// Note ljacqu 20151228: Wordpress is actually a salted algorithm but salt generation is handled internally
// and isn't exposed to the outside, so we treat it as an unsalted implementation
public class WORDPRESS extends UnsaltedMethod {
private static final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private final SecureRandom randomGen = new SecureRandom();
@ -102,16 +110,15 @@ public class WORDPRESS implements EncryptionMethod {
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
public String computeHash(String password) {
byte random[] = new byte[6];
this.randomGen.nextBytes(random);
randomGen.nextBytes(random);
return crypt(password, gensaltPrivate(stringToUtf8(new String(random))));
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
String hash = encryptedPassword.getHash();
String comparedHash = crypt(password, hash);
return comparedHash.equals(hash);
}

View File

@ -1,12 +1,12 @@
package fr.xephi.authme.security.crypts;
import java.security.NoSuchAlgorithmException;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.Usage;
/**
*/
public class XAUTH implements EncryptionMethod {
@Recommendation(Usage.RECOMMENDED)
public class XAUTH extends HexSaltedMethod {
public static String getWhirlpool(String message) {
private static String getWhirlpool(String message) {
WHIRLPOOL w = new WHIRLPOOL();
byte[] digest = new byte[WHIRLPOOL.DIGESTBYTES];
w.NESSIEinit();
@ -16,19 +16,23 @@ public class XAUTH implements EncryptionMethod {
}
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
public String computeHash(String password, String salt, String name) {
String hash = getWhirlpool(salt + password).toLowerCase();
int saltPos = (password.length() >= hash.length() ? hash.length() - 1 : password.length());
return hash.substring(0, saltPos) + salt + hash.substring(saltPos);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String playerName) {
String hash = encryptedPassword.getHash();
int saltPos = (password.length() >= hash.length() ? hash.length() - 1 : password.length());
String salt = hash.substring(saltPos, saltPos + 12);
return hash.equals(computeHash(password, salt, ""));
String saltFromHash = hash.substring(saltPos, saltPos + 12);
return hash.equals(computeHash(password, saltFromHash, null));
}
@Override
public int getSaltLength() {
return 12;
}
}

View File

@ -1,56 +0,0 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.AuthMe;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*/
public class XF implements EncryptionMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getSha256(getSha256(password) + regmatch("\"salt\";.:..:\"(.*)\";.:.:\"hashFunc\"", salt));
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt();
return hash.equals(regmatch("\"hash\";.:..:\"(.*)\";.:.:\"salt\"", salt));
}
private String getSha256(String password) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(password.getBytes());
byte byteData[] = md.digest();
StringBuilder sb = new StringBuilder();
for (byte element : byteData) {
sb.append(Integer.toString((element & 0xff) + 0x100, 16).substring(1));
}
StringBuilder hexString = new StringBuilder();
for (byte element : byteData) {
String hex = Integer.toHexString(0xff & element);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
private String regmatch(String pattern, String line) {
List<String> allMatches = new ArrayList<>();
Matcher m = Pattern.compile(pattern).matcher(line);
while (m.find()) {
allMatches.add(m.group(1));
}
return allMatches.get(0);
}
}

View File

@ -0,0 +1,15 @@
package fr.xephi.authme.security.crypts.description;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Denotes an encryption algorithm that is restricted to the ASCII charset.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AsciiRestricted {
}

View File

@ -0,0 +1,22 @@
package fr.xephi.authme.security.crypts.description;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Describes the type of salt the encryption algorithm uses. This is purely for documentation
* purposes and is ignored by the code.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HasSalt {
/** The type of the salt. */
SaltType value();
/** For text salts, the length of the salt. */
int length() default 0;
}

View File

@ -0,0 +1,18 @@
package fr.xephi.authme.security.crypts.description;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to mark a hash algorithm with the usage recommendation, see {@link Usage}.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Recommendation {
/** The recommendation for using the hash algorithm. */
Usage value();
}

View File

@ -0,0 +1,17 @@
package fr.xephi.authme.security.crypts.description;
/**
* The type of salt used by an encryption algorithm.
*/
public enum SaltType {
/** Random, newly generated text. */
TEXT,
/** Salt is based on the username, including variations and repetitions. */
USERNAME,
/** No salt. */
NONE
}

View File

@ -0,0 +1,20 @@
package fr.xephi.authme.security.crypts.description;
/**
* Usage recommendation that can be provided for a hash algorithm.
*/
public enum Usage {
/** The hash algorithm appears to be cryptographically secure and is one of the algorithms recommended by AuthMe. */
RECOMMENDED,
/** There are safer algorithms that can be chosen but using the algorithm is generally OK. */
ACCEPTABLE,
/** Hash algorithm is not recommended to be used. Use only if required by another system. */
DO_NOT_USE,
/** The algorithm does not work properly; do not use. */
DOES_NOT_WORK
}

View File

@ -1,20 +1,18 @@
package fr.xephi.authme.task;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import org.bukkit.entity.Player;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import java.security.NoSuchAlgorithmException;
public class ChangePasswordTask implements Runnable {
private final AuthMe plugin;
@ -32,48 +30,40 @@ public class ChangePasswordTask implements Runnable {
@Override
public void run() {
Messages m = plugin.getMessages();
try {
final String name = player.getName().toLowerCase();
String hashNew = PasswordSecurity.getHash(Settings.getPasswordHash, newPassword, name);
PlayerAuth auth = PlayerCache.getInstance().getAuth(name);
if (PasswordSecurity.comparePasswordWithHash(oldPassword, auth.getHash(), player.getName())) {
auth.setHash(hashNew);
if (PasswordSecurity.userSalt.containsKey(name) && PasswordSecurity.userSalt.get(name) != null) {
auth.setSalt(PasswordSecurity.userSalt.get(name));
} else {
auth.setSalt("");
}
if (!plugin.database.updatePassword(auth)) {
m.send(player, MessageKey.ERROR);
return;
}
plugin.database.updateSalt(auth);
PlayerCache.getInstance().updatePlayer(auth);
m.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS);
ConsoleLogger.info(player.getName() + " changed his password");
if (Settings.bungee)
{
final String hash = auth.getHash();
final String salt = auth.getSalt();
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable(){
PasswordSecurity passwordSecurity = plugin.getPasswordSecurity();
@Override
public void run() {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("Forward");
out.writeUTF("ALL");
out.writeUTF("AuthMe");
out.writeUTF("changepassword;" + name + ";" + hash + ";" + salt);
player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
}
});
}
} else {
m.send(player, MessageKey.WRONG_PASSWORD);
final String name = player.getName().toLowerCase();
PlayerAuth auth = PlayerCache.getInstance().getAuth(name);
if (passwordSecurity.comparePassword(oldPassword, auth.getPassword(), player.getName())) {
EncryptedPassword encryptedPassword = passwordSecurity.computeHash(newPassword, name);
auth.setPassword(encryptedPassword);
if (!plugin.getDataSource().updatePassword(auth)) {
m.send(player, MessageKey.ERROR);
return;
}
} catch (NoSuchAlgorithmException ex) {
ConsoleLogger.showError(ex.getMessage());
m.send(player, MessageKey.ERROR);
PlayerCache.getInstance().updatePlayer(auth);
m.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS);
ConsoleLogger.info(player.getName() + " changed his password");
if (Settings.bungee) {
final String hash = encryptedPassword.getHash();
final String salt = encryptedPassword.getSalt();
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable(){
@Override
public void run() {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("Forward");
out.writeUTF("ALL");
out.writeUTF("AuthMe");
out.writeUTF("changepassword;" + name + ";" + hash + ";" + salt);
player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
}
});
}
} else {
m.send(player, MessageKey.WRONG_PASSWORD);
}
}
}

View File

@ -143,13 +143,9 @@ public final class Utils {
return true;
}
if (!Settings.isForcedRegistrationEnabled) {
// TODO ljacqu 20151123: Use a getter to retrieve things from AuthMe
if (!plugin.database.isAuthAvailable(player.getName())) {
return true;
}
if (!Settings.isForcedRegistrationEnabled && !plugin.getDataSource().isAuthAvailable(player.getName())) {
return true;
}
return false;
}
@ -159,15 +155,6 @@ public final class Utils {
&& (Settings.getUnrestrictedName.contains(player.getName().toLowerCase()));
}
/**
* Method packCoords.
*
* @param x double
* @param y double
* @param z double
* @param w String
* @param pl Player
*/
public static void packCoords(double x, double y, double z, String w, final Player pl) {
World theWorld;
if (w.equals("unavailableworld")) {

View File

@ -181,7 +181,7 @@ settings:
# Example unLoggedinGroup: NotLogged
unLoggedinGroup: unLoggedinGroup
# possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB,
# MYBB, IPB3, PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512,
# MYBB, IPB3, PHPFUSION, SMF, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512,
# DOUBLEMD5, PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM(for developpers only)
passwordHash: SHA256
# salt length for the SALTED2MD5 MD5(MD5(password)+salt)

View File

@ -2,10 +2,12 @@ package fr.xephi.authme.command;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.help.HelpProvider;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.PasswordSecurity;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.junit.Before;
@ -32,6 +34,7 @@ public class CommandServiceTest {
private CommandMapper commandMapper;
private HelpProvider helpProvider;
private Messages messages;
private PasswordSecurity passwordSecurity;
private CommandService commandService;
@Before
@ -40,7 +43,8 @@ public class CommandServiceTest {
commandMapper = mock(CommandMapper.class);
helpProvider = mock(HelpProvider.class);
messages = mock(Messages.class);
commandService = new CommandService(authMe, commandMapper, helpProvider, messages);
passwordSecurity = mock(PasswordSecurity.class);
commandService = new CommandService(authMe, commandMapper, helpProvider, messages, passwordSecurity);
}
@Test
@ -110,9 +114,25 @@ public class CommandServiceTest {
}
@Test
@Ignore
public void shouldGetDataSource() {
// TODO ljacqu 20151226: Cannot mock calls to fields
// given
DataSource dataSource = mock(DataSource.class);
given(authMe.getDataSource()).willReturn(dataSource);
// when
DataSource result = commandService.getDataSource();
// then
assertThat(result, equalTo(dataSource));
}
@Test
public void shouldGetPasswordSecurity() {
// given/when
PasswordSecurity passwordSecurity = commandService.getPasswordSecurity();
// then
assertThat(passwordSecurity, equalTo(this.passwordSecurity));
}
@Test

View File

@ -0,0 +1,61 @@
package fr.xephi.authme.security;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.security.crypts.EncryptionMethod;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
import fr.xephi.authme.util.WrapperMock;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* Integration test for {@link HashAlgorithm}.
*/
public class HashAlgorithmIntegrationTest {
@BeforeClass
public static void setUpWrapper() {
WrapperMock.createInstance();
Settings.bCryptLog2Rounds = 8;
Settings.saltLength = 16;
}
@Test
public void shouldHaveUniqueClassForEntries() {
// given
Set<Class<? extends EncryptionMethod>> classes = new HashSet<>();
// when / then
for (HashAlgorithm algorithm : HashAlgorithm.values()) {
if (!HashAlgorithm.CUSTOM.equals(algorithm)) {
if (classes.contains(algorithm.getClazz())) {
fail("Found class '" + algorithm.getClazz() + "' twice!");
}
classes.add(algorithm.getClazz());
}
}
}
@Test
public void shouldBeAbleToInstantiateEncryptionAlgorithms() throws InstantiationException, IllegalAccessException {
// given / when / then
for (HashAlgorithm algorithm : HashAlgorithm.values()) {
if (!HashAlgorithm.CUSTOM.equals(algorithm)) {
EncryptionMethod method = algorithm.getClazz().newInstance();
EncryptedPassword encryptedPassword = method.computeHash("pwd", "name");
assertThat("Salt should not be null if method.hasSeparateSalt(), and vice versa. Method: '"
+ method + "'", StringUtils.isEmpty(encryptedPassword.getSalt()), equalTo(!method.hasSeparateSalt()));
assertThat("Hash should not be empty for method '" + method + "'",
StringUtils.isEmpty(encryptedPassword.getHash()), equalTo(false));
}
}
}
}

View File

@ -0,0 +1,116 @@
package fr.xephi.authme.security;
import org.junit.Test;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
/**
* Test for {@link HashUtils}.
*/
public class HashUtilsTest {
/**
* List of passwords whose hash is provided to the class to test against.
*/
public static final String[] GIVEN_PASSWORDS = {"", "password", "PassWord1", "&^%te$t?Pw@_"};
@Test
public void shouldHashMd5() {
// given
String[] correctHashes = {
"d41d8cd98f00b204e9800998ecf8427e", // empty string
"5f4dcc3b5aa765d61d8327deb882cf99", // password
"f2126d405f46ed603ff5b2950f062c96", // PassWord1
"0833dcd2bc741f90c46bbac5498fd08f" // &^%te$t?Pw@_
};
// when
List<String> result = new ArrayList<>();
for (String password : GIVEN_PASSWORDS) {
result.add(HashUtils.md5(password));
}
// then
assertThat(result, contains(correctHashes));
}
@Test
public void shouldHashSha1() {
// given
String[] correctHashes = {
"da39a3ee5e6b4b0d3255bfef95601890afd80709", // empty string
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", // password
"285d0c707f9644b75e1a87a62f25d0efb56800f0", // PassWord1
"a42ef8e61e890af80461ca5dcded25cbfcf407a4" // &^%te$t?Pw@_
};
// when
List<String> result = new ArrayList<>();
for (String password : GIVEN_PASSWORDS) {
result.add(HashUtils.sha1(password));
}
// then
assertThat(result, contains(correctHashes));
}
@Test
public void shouldHashSha256() {
// given
String[] correctHashes = {
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", // empty string
"5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8", // password
"c04265d72b749debd67451c083785aa572742e3222e86884de16485fa14b55e7", // PassWord1
"005e3d7439d3e9a60a9d74aa1c763b36bfebec8e434ab6c5efab3df37eb2dae6" // &^%te$t?Pw@_
};
// when
List<String> result = new ArrayList<>();
for (String password : GIVEN_PASSWORDS) {
result.add(HashUtils.sha256(password));
}
// then
assertThat(result, contains(correctHashes));
}
@Test
public void shouldHashSha512() {
// given
String[] correctHashes = {
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", // empty string
"b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86", // password
"ae9942149995a8171391625b36da134d5e288c721650d7c8d2d464fb49a49f3f551e4916ab1e097d9dd1201b01d69b1dccdefa3d2524a66092fb61b3df6e7e71", // PassWord1
"8c4f3df78db191142d819a72c16058b9e1ea41ae9b1649e1184eb89e30344c51c9c71039c483cf2f1b76b51480d8459d7eb3cfbaa24b07f2041d1551af4ead75" // &^%te$t?Pw@_
};
// when
List<String> result = new ArrayList<>();
for (String password : GIVEN_PASSWORDS) {
result.add(HashUtils.sha512(password));
}
// then
assertThat(result, contains(correctHashes));
}
@Test
public void shouldRetrieveMd5Instance() {
// given
MessageDigestAlgorithm algorithm = MessageDigestAlgorithm.MD5;
// when
MessageDigest digest = HashUtils.getDigest(algorithm);
// then
assertThat(digest.getAlgorithm(), equalTo("MD5"));
}
}

View File

@ -0,0 +1,224 @@
package fr.xephi.authme.security;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.PasswordEncryptionEvent;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.security.crypts.EncryptionMethod;
import fr.xephi.authme.security.crypts.JOOMLA;
import fr.xephi.authme.security.crypts.PHPBB;
import org.bukkit.event.Event;
import org.bukkit.plugin.PluginManager;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* Test for {@link PasswordSecurity}.
*/
public class PasswordSecurityTest {
private PluginManager pluginManager;
private DataSource dataSource;
private EncryptionMethod method;
private Class<?> caughtClassInEvent;
@Before
public void setUpMocks() {
pluginManager = mock(PluginManager.class);
dataSource = mock(DataSource.class);
method = mock(EncryptionMethod.class);
caughtClassInEvent = null;
// When the password encryption event is emitted, replace the encryption method with our mock.
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] arguments = invocation.getArguments();
if (arguments[0] instanceof PasswordEncryptionEvent) {
PasswordEncryptionEvent event = (PasswordEncryptionEvent) arguments[0];
caughtClassInEvent = event.getMethod() != null ? event.getMethod().getClass() : null;
event.setMethod(method);
}
return null;
}
}).when(pluginManager).callEvent(any(Event.class));
}
@Test
public void shouldReturnPasswordMatch() {
// given
EncryptedPassword password = new EncryptedPassword("$TEST$10$SOME_HASH", null);
String playerName = "Tester";
String clearTextPass = "myPassTest";
PlayerAuth auth = mock(PlayerAuth.class);
given(auth.getPassword()).willReturn(password);
given(dataSource.getAuth(playerName)).willReturn(auth);
given(method.comparePassword(clearTextPass, password, playerName)).willReturn(true);
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.BCRYPT, pluginManager, false);
// when
boolean result = security.comparePassword(clearTextPass, playerName);
// then
assertThat(result, equalTo(true));
verify(dataSource).getAuth(playerName);
verify(pluginManager).callEvent(any(PasswordEncryptionEvent.class));
verify(method).comparePassword(clearTextPass, password, playerName);
}
@Test
public void shouldReturnPasswordMismatch() {
// given
EncryptedPassword password = new EncryptedPassword("$TEST$10$SOME_HASH", null);
String playerName = "My_PLayer";
String clearTextPass = "passw0Rd1";
PlayerAuth auth = mock(PlayerAuth.class);
given(auth.getPassword()).willReturn(password);
given(dataSource.getAuth(playerName)).willReturn(auth);
given(method.comparePassword(clearTextPass, password, playerName)).willReturn(false);
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.CUSTOM, pluginManager, false);
// when
boolean result = security.comparePassword(clearTextPass, playerName);
// then
assertThat(result, equalTo(false));
verify(dataSource).getAuth(playerName);
verify(pluginManager).callEvent(any(PasswordEncryptionEvent.class));
verify(method).comparePassword(clearTextPass, password, playerName);
}
@Test
public void shouldReturnFalseIfPlayerDoesNotExist() {
// given
String playerName = "bobby";
String clearTextPass = "tables";
given(dataSource.getAuth(playerName)).willReturn(null);
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.MD5, pluginManager, false);
// when
boolean result = security.comparePassword(clearTextPass, playerName);
// then
assertThat(result, equalTo(false));
verify(dataSource).getAuth(playerName);
verify(pluginManager, never()).callEvent(any(Event.class));
verify(method, never()).comparePassword(anyString(), any(EncryptedPassword.class), anyString());
}
@Test
public void shouldTryOtherMethodsForFailedPassword() {
// given
// BCRYPT2Y hash for "Test"
EncryptedPassword password =
new EncryptedPassword("$2y$10$2e6d2193f43501c926e25elvWlPmWczmrfrnbZV0dUZGITjYjnkkW");
String playerName = "somePlayer";
String clearTextPass = "Test";
// MD5 hash for "Test"
EncryptedPassword newPassword = new EncryptedPassword("0cbc6611f5540bd0809a388dc95a615b");
PlayerAuth auth = mock(PlayerAuth.class);
doCallRealMethod().when(auth).getPassword();
doCallRealMethod().when(auth).setPassword(any(EncryptedPassword.class));
auth.setPassword(password);
given(dataSource.getAuth(playerName)).willReturn(auth);
given(method.comparePassword(clearTextPass, password, playerName)).willReturn(false);
given(method.computeHash(clearTextPass, playerName)).willReturn(newPassword);
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.MD5, pluginManager, true);
// when
boolean result = security.comparePassword(clearTextPass, playerName);
// then
assertThat(result, equalTo(true));
verify(dataSource, times(2)).getAuth(playerName);
verify(pluginManager, times(2)).callEvent(any(PasswordEncryptionEvent.class));
verify(method).comparePassword(clearTextPass, password, playerName);
verify(auth).setPassword(newPassword);
ArgumentCaptor<PlayerAuth> captor = ArgumentCaptor.forClass(PlayerAuth.class);
verify(dataSource).updatePassword(captor.capture());
assertThat(captor.getValue().getPassword(), equalTo(newPassword));
}
@Test
public void shouldHashPassword() {
// given
String password = "MyP@ssword";
String username = "theUserInTest";
EncryptedPassword encryptedPassword = new EncryptedPassword("$T$est#Hash", "__someSalt__");
given(method.computeHash(password, username)).willReturn(encryptedPassword);
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.JOOMLA, pluginManager, true);
// when
EncryptedPassword result = security.computeHash(password, username);
// then
assertThat(result, equalTo(encryptedPassword));
ArgumentCaptor<PasswordEncryptionEvent> captor = ArgumentCaptor.forClass(PasswordEncryptionEvent.class);
verify(pluginManager).callEvent(captor.capture());
PasswordEncryptionEvent event = captor.getValue();
assertThat(JOOMLA.class.equals(caughtClassInEvent), equalTo(true));
assertThat(event.getPlayerName(), equalTo(username));
}
@Test
public void shouldHashPasswordWithGivenAlgorithm() {
// given
String password = "TopSecretPass#112525";
String username = "someone12";
EncryptedPassword encryptedPassword = new EncryptedPassword("~T!est#Hash", "__someSalt__");
given(method.computeHash(password, username)).willReturn(encryptedPassword);
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.JOOMLA, pluginManager, true);
// when
EncryptedPassword result = security.computeHash(HashAlgorithm.PHPBB, password, username);
// then
assertThat(result, equalTo(encryptedPassword));
ArgumentCaptor<PasswordEncryptionEvent> captor = ArgumentCaptor.forClass(PasswordEncryptionEvent.class);
verify(pluginManager).callEvent(captor.capture());
PasswordEncryptionEvent event = captor.getValue();
assertThat(PHPBB.class.equals(caughtClassInEvent), equalTo(true));
assertThat(event.getPlayerName(), equalTo(username));
}
@Test
public void shouldSkipCheckIfMandatorySaltIsUnavailable() {
// given
String password = "?topSecretPass\\";
String username = "someone12";
EncryptedPassword encryptedPassword = new EncryptedPassword("~T!est#Hash");
given(method.computeHash(password, username)).willReturn(encryptedPassword);
given(method.hasSeparateSalt()).willReturn(true);
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.XAUTH, pluginManager, true);
// when
boolean result = security.comparePassword(password, encryptedPassword, username);
// then
assertThat(result, equalTo(false));
verify(dataSource, never()).getAuth(anyString());
verify(pluginManager).callEvent(any(PasswordEncryptionEvent.class));
verify(method, never()).comparePassword(anyString(), any(EncryptedPassword.class), anyString());
}
}

View File

@ -0,0 +1,55 @@
package fr.xephi.authme.security;
import org.junit.Test;
import java.util.regex.Pattern;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
/**
* Test for {@link RandomString}.
*/
public class RandomStringTest {
@Test
public void shouldGenerateRandomStrings() {
// given
int[] lengths = {0, 1, 19, 142, 1872};
Pattern badChars = Pattern.compile(".*[^0-9a-z].*");
// when / then
for (int length : lengths) {
String result = RandomString.generate(length);
assertThat("Result '" + result + "' should have length " + length,
result.length(), equalTo(length));
assertThat("Result '" + result + "' should only have characters a-z, 0-9",
badChars.matcher(result).matches(), equalTo(false));
}
}
@Test
public void shouldGenerateRandomHexString() {
// given
int[] lengths = {0, 1, 21, 160, 1784};
Pattern badChars = Pattern.compile(".*[^0-9a-f].*");
// when / then
for (int length : lengths) {
String result = RandomString.generateHex(length);
assertThat("Result '" + result + "' should have length " + length,
result.length(), equalTo(length));
assertThat("Result '" + result + "' should only have characters a-f, 0-9",
badChars.matcher(result).matches(), equalTo(false));
}
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowForInvalidLength() {
// given/when
RandomString.generate(-3);
// then - throw exception
}
}

View File

@ -1,13 +1,16 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.PasswordSecurity;
import com.google.common.collect.ImmutableList;
import fr.xephi.authme.security.crypts.description.AsciiRestricted;
import org.junit.Test;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
@ -27,30 +30,48 @@ public abstract class AbstractEncryptionMethodTest {
* List of passwords that are hashed at runtime and then tested against; this verifies that hashes that are
* generated are valid.
*/
private static final String[] INTERNAL_PASSWORDS = {"test1234", "Ab_C73", "(!#&$~`_-Aa0", "Ûïé1&?+A"};
private static final List<String> INTERNAL_PASSWORDS =
ImmutableList.of("test1234", "Ab_C73", "(!#&$~`_-Aa0", "Ûïé1&?+A");
/** The encryption method to test. */
private EncryptionMethod method;
/** Map with the hashes against which the entries in GIVEN_PASSWORDS are tested. */
private Map<String, String> hashes;
private Map<String, EncryptedPassword> hashes;
/**
* Create a new test for the given encryption method.
*
* @param method The encryption method to test
* @param hash0 The pre-generated hash for the first {@link #GIVEN_PASSWORDS}
* @param hash1 The pre-generated hash for the second {@link #GIVEN_PASSWORDS}
* @param hash2 The pre-generated hash for the third {@link #GIVEN_PASSWORDS}
* @param hash3 The pre-generated hash for the fourth {@link #GIVEN_PASSWORDS}
* @param computedHashes The pre-generated hashes for the elements in {@link #GIVEN_PASSWORDS}
*/
public AbstractEncryptionMethodTest(EncryptionMethod method, String hash0, String hash1,
String hash2, String hash3) {
public AbstractEncryptionMethodTest(EncryptionMethod method, String... computedHashes) {
if (method.hasSeparateSalt()) {
throw new UnsupportedOperationException("Test must be initialized with EncryptedPassword objects if "
+ "the salt is stored separately. Use the other constructor");
} else if (computedHashes.length != GIVEN_PASSWORDS.length) {
throw new UnsupportedOperationException("Expected " + GIVEN_PASSWORDS.length + " hashes");
}
this.method = method;
hashes = new HashMap<>();
hashes.put(GIVEN_PASSWORDS[0], hash0);
hashes.put(GIVEN_PASSWORDS[1], hash1);
hashes.put(GIVEN_PASSWORDS[2], hash2);
hashes.put(GIVEN_PASSWORDS[3], hash3);
for (int i = 0; i < GIVEN_PASSWORDS.length; ++i) {
hashes.put(GIVEN_PASSWORDS[i], new EncryptedPassword(computedHashes[i]));
}
}
public AbstractEncryptionMethodTest(EncryptionMethod method, EncryptedPassword result0, EncryptedPassword result1,
EncryptedPassword result2, EncryptedPassword result3) {
if (!method.hasSeparateSalt()) {
throw new UnsupportedOperationException("Salt is not stored separately, so test should be initialized"
+ " with the password hashes only. Use the other constructor");
}
this.method = method;
hashes = new HashMap<>();
hashes.put(GIVEN_PASSWORDS[0], result0);
hashes.put(GIVEN_PASSWORDS[1], result1);
hashes.put(GIVEN_PASSWORDS[2], result2);
hashes.put(GIVEN_PASSWORDS[3], result3);
}
@Test
@ -75,31 +96,36 @@ public abstract class AbstractEncryptionMethodTest {
@Test
public void testPasswordEquality() {
for (String password : INTERNAL_PASSWORDS) {
try {
String hash = method.computeHash(password, getSalt(method), USERNAME);
assertTrue("Generated hash for '" + password + "' should match password (hash = '" + hash + "')",
method.comparePassword(hash, password, USERNAME));
if (!password.equals(password.toLowerCase())) {
assertFalse("Lower-case of '" + password + "' should not match generated hash '" + hash + "'",
method.comparePassword(hash, password.toLowerCase(), USERNAME));
}
if (!password.equals(password.toUpperCase())) {
assertFalse("Upper-case of '" + password + "' should not match generated hash '" + hash + "'",
method.comparePassword(hash, password.toUpperCase(), USERNAME));
}
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("EncryptionMethod '" + method + "' threw exception", e);
List<String> internalPasswords = method.getClass().isAnnotationPresent(AsciiRestricted.class)
? INTERNAL_PASSWORDS.subList(0, INTERNAL_PASSWORDS.size() - 1)
: INTERNAL_PASSWORDS;
for (String password : internalPasswords) {
final String salt = method.generateSalt();
final String hash = method.computeHash(password, salt, USERNAME);
EncryptedPassword encryptedPassword = new EncryptedPassword(hash, salt);
// Check that the computeHash(password, salt, name) method has the same output for the returned salt
if (testHashEqualityForSameSalt()) {
assertThat("Computing a hash with the same salt will generate the same hash",
hash, equalTo(method.computeHash(password, salt, USERNAME)));
}
assertTrue("Generated hash for '" + password + "' should match password (hash = '" + hash + "')",
method.comparePassword(password, encryptedPassword, USERNAME));
if (!password.equals(password.toLowerCase())) {
assertFalse("Lower-case of '" + password + "' should not match generated hash '" + hash + "'",
method.comparePassword(password.toLowerCase(), encryptedPassword, USERNAME));
}
if (!password.equals(password.toUpperCase())) {
assertFalse("Upper-case of '" + password + "' should not match generated hash '" + hash + "'",
method.comparePassword(password.toUpperCase(), encryptedPassword, USERNAME));
}
}
}
private boolean doesGivenHashMatch(String password, EncryptionMethod method) {
try {
return method.comparePassword(hashes.get(password), password, USERNAME);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("EncryptionMethod '" + method + "' threw exception", e);
}
return method.comparePassword(password, hashes.get(password), USERNAME);
}
// @org.junit.Test public void a() { AbstractEncryptionMethodTest.generateTest(); }
@ -116,43 +142,30 @@ public abstract class AbstractEncryptionMethodTest {
if (password.equals(GIVEN_PASSWORDS[GIVEN_PASSWORDS.length - 1])) {
delim = "); ";
}
try {
System.out.println("\t\t\"" + method.computeHash(password, getSalt(method), USERNAME)
if (method.hasSeparateSalt()) {
EncryptedPassword encryptedPassword = method.computeHash(password, USERNAME);
System.out.println(String.format("\t\tnew EncryptedPassword(\"%s\", \"%s\")%s// %s",
encryptedPassword.getHash(), encryptedPassword.getSalt(), delim, password));
} else {
System.out.println("\t\t\"" + method.computeHash(password, USERNAME).getHash()
+ "\"" + delim + "// " + password);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Could not generate hash", e);
}
}
System.out.println("\t}");
System.out.println("\n}");
}
// TODO #358: Remove this method and use the new salt method on the interface
private static String getSalt(EncryptionMethod method) {
try {
if (method instanceof BCRYPT) {
return BCRYPT.gensalt();
} else if (method instanceof MD5 || method instanceof WORDPRESS || method instanceof SMF
|| method instanceof SHA512 || method instanceof SHA1 || method instanceof ROYALAUTH
|| method instanceof DOUBLEMD5) {
return "";
} else if (method instanceof JOOMLA || method instanceof SALTEDSHA512) {
return PasswordSecurity.createSalt(32);
} else if (method instanceof SHA256 || method instanceof PHPBB || method instanceof WHIRLPOOL
|| method instanceof MD5VB || method instanceof BCRYPT2Y) {
return PasswordSecurity.createSalt(16);
} else if (method instanceof WBB3) {
return PasswordSecurity.createSalt(40);
} else if (method instanceof XAUTH || method instanceof CryptPBKDF2Django
|| method instanceof CryptPBKDF2) {
return PasswordSecurity.createSalt(12);
} else if (method instanceof WBB4) {
return BCRYPT.gensalt(8);
}
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
throw new IllegalStateException("Unknown EncryptionMethod for salt generation");
/**
* Return whether an encryption algorithm should be tested that it generates the same
* hash for the same salt. If {@code true}, we call {@link EncryptionMethod#computeHash(String, String)}
* and verify that {@link EncryptionMethod#computeHash(String, String, String)} generates
* the same hash for the salt returned in the first call.
*
* @return Whether or not to test that the hash is the same for the same salt
*/
protected boolean testHashEqualityForSameSalt() {
return true;
}
}

View File

@ -1,23 +1,16 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.PasswordSecurity;
import org.junit.Ignore;
import org.junit.Test;
import java.security.NoSuchAlgorithmException;
/**
* Test for {@link BCRYPT2Y}.
*/
@Ignore
// TODO #369: Fix hash & add standard test
public class BCRYPT2YTest {
public class BCRYPT2YTest extends AbstractEncryptionMethodTest {
@Test
public void shouldCreateHash() throws NoSuchAlgorithmException {
String salt = PasswordSecurity.createSalt(16); // As defined in PasswordSecurity
EncryptionMethod method = new BCRYPT2Y();
System.out.println(method.computeHash("password", salt, "testPlayer"));
public BCRYPT2YTest() {
super(new BCRYPT2Y(),
"$2y$10$da641e404b982edf1c7c0uTU9BcKzfA2vWKV05q6r.dCvm/93wqVK", // password
"$2y$10$e52c48a76f5b86f5da899uiK/HYocyPsfQXESNbP278rIz08LKEP2", // PassWord1
"$2y$10$be6f11548dc5fb4088410ONdC0dXnJ04y1RHcJh5fVF3XK5d.qgqK", // &^%te$t?Pw@_
"$2y$10$a8097db1fa4423b93f1b2eF6rMAGFkSX178fpROf/OvCFtrDebp6K"); // âË_3(íù*
}
}

View File

@ -1,10 +1,20 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.WrapperMock;
import org.junit.Before;
/**
* Test for {@link BCRYPT}.
*/
public class BcryptTest extends AbstractEncryptionMethodTest {
@Before
public void setUpSettings() {
WrapperMock.createInstance();
Settings.bCryptLog2Rounds = 8;
}
public BcryptTest() {
super(new BCRYPT(),
"$2a$10$6iATmYgwJVc3YONhVcZFve3Cfb5GnwvKhJ20r.hMjmcNkIT9.Uh9K", // password

View File

@ -0,0 +1,16 @@
package fr.xephi.authme.security.crypts;
/**
* Test for {@link CRAZYCRYPT1}.
*/
public class CRAZYCRYPT1Test extends AbstractEncryptionMethodTest {
public CRAZYCRYPT1Test() {
super(new CRAZYCRYPT1(),
"d5c76eb36417d4e97ec62609619e40a9e549a2598d0dab5a7194fd997a9305af78de2b93f958e150d19dd1e7f821043379ddf5f9c7f352bf27df91ae4913f3e8", // password
"49c63f827c88196871e344e589bd46cc4fa6db3c27801bbad5374c0d216381977627c1d76f2114667d5dd117e046f7493eb06e4f461f4f848aa08f6f40a3e934", // PassWord1
"6fefb0233bab6e6efb9c16f82cb0d8f569488905e2dae0e7c9dde700e7363da67213d37c44bc15f4a05854c9c21e5688389d416413c7309398aa96cb1f341d08", // &^%te$t?Pw@_
"46f51cde7657fdec9848bad0fd8e7fb97783cf5335f94dbb5260899ab0b04022a52d651b1c45345328850178e7165308c8c213040b0864de66018a0b769d37cb"); // âË_3(íù*
}
}

View File

@ -1,12 +1,8 @@
package fr.xephi.authme.security.crypts;
import org.junit.Ignore;
/**
* Test for {@link CryptPBKDF2Django}.
*/
@Ignore
// TODO ljacqu 20151220: testPasswordEquality fails - password matches hash for uppercase password...?
public class CryptPBKDF2DjangoTest extends AbstractEncryptionMethodTest {
public CryptPBKDF2DjangoTest() {

View File

@ -0,0 +1,16 @@
package fr.xephi.authme.security.crypts;
/**
* Test for {@link IPB3}.
*/
public class IPB3Test extends AbstractEncryptionMethodTest {
public IPB3Test() {
super(new IPB3(),
new EncryptedPassword("f8ecea1ce42b5babef369ff7692dbe3f", "1715b"), //password
new EncryptedPassword("40a93731a931352e0619cdf09b975040", "ba91c"), //PassWord1
new EncryptedPassword("a77ca982373946d5800430bd2947ba11", "a7725"), //&^%te$t?Pw@_
new EncryptedPassword("383d7b9e2b707d6e894ec7b30e3032c3", "fa9fd")); //âË_3(íù*
}
}

View File

@ -0,0 +1,16 @@
package fr.xephi.authme.security.crypts;
/**
* Test for {@link MYBB}.
*/
public class MYBBTest extends AbstractEncryptionMethodTest {
public MYBBTest() {
super(new MYBB(),
new EncryptedPassword("57c7a16d860833db5030738f5a465d2b", "acdc14e6"), //password
new EncryptedPassword("08fbdf721f2c42d9780b7d66df0ba830", "792fd7fb"), //PassWord1
new EncryptedPassword("d602f38fb59ad9e185d5604f5d4ddb36", "4b5534a4"), //&^%te$t?Pw@_
new EncryptedPassword("b3c39410d0ab8ae2a65c257820797fad", "e5a6cb14")); //âË_3(íù*
}
}

View File

@ -7,11 +7,10 @@ public class Md5Test extends AbstractEncryptionMethodTest {
public Md5Test() {
super(new MD5(),
"5f4dcc3b5aa765d61d8327deb882cf99", // password
"f2126d405f46ed603ff5b2950f062c96", // PassWord1
"0833dcd2bc741f90c46bbac5498fd08f", // &^%te$t?Pw@_
"d1accd961cb7b688c87278191c1dfed3" // âË_3(íù*
);
"5f4dcc3b5aa765d61d8327deb882cf99", // password
"f2126d405f46ed603ff5b2950f062c96", // PassWord1
"0833dcd2bc741f90c46bbac5498fd08f", // &^%te$t?Pw@_
"d1accd961cb7b688c87278191c1dfed3"); // âË_3(íù*
}
}

View File

@ -0,0 +1,16 @@
package fr.xephi.authme.security.crypts;
/**
* Test for {@link PHPFUSION}.
*/
public class PHPFUSIONTest extends AbstractEncryptionMethodTest {
public PHPFUSIONTest() {
super(new PHPFUSION(),
new EncryptedPassword("f7a606c4eb3fcfbc382906476e05b06f21234a77d1a4eacc0f93f503deb69e70", "6cd1c97c55cb"), // password
new EncryptedPassword("8a9b7bb706a3347e5f684a7cb905bfb26b9a0d099358064139ab3ed1a66aeb2b", "d6012370b73f"), // PassWord1
new EncryptedPassword("43f2f23f44c8f89e2dbf06050bc8c77dbcdf71a7b5d28c87ec657d474e63d62d", "f75400a209a4"), // &^%te$t?Pw@_
new EncryptedPassword("4e7f4eb7e3653d7460f1cf590def4153c6fcdf8b8e16fb95538fdf9e54a95245", "d552e0f5b23a")); // âË_3(íù*
}
}

View File

@ -0,0 +1,26 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.WrapperMock;
import org.junit.Before;
/**
* Test for {@link SALTED2MD5}.
*/
public class SALTED2MD5Test extends AbstractEncryptionMethodTest {
@Before
public void setUpAlgorithm() {
WrapperMock.createInstance();
Settings.saltLength = 8;
}
public SALTED2MD5Test() {
super(new SALTED2MD5(),
new EncryptedPassword("9f3d13dc01a6fe61fd669954174399f3", "9b5f5749"), // password
new EncryptedPassword("b28c32f624a4eb161d6adc9acb5bfc5b", "f750ba32"), // PassWord1
new EncryptedPassword("38dcb83cc68424afe3cda012700c2bb1", "eb2c3394"), // &^%te$t?Pw@_
new EncryptedPassword("ad25606eae5b760c8a2469d65578ac39", "04eee598")); // âË_3(íù*)
}
}

View File

@ -1,20 +1,16 @@
package fr.xephi.authme.security.crypts;
import org.junit.Ignore;
/**
* Test for {@link SALTEDSHA512}.
*/
@Ignore
// TODO ljacqu 20151220: Currently cannot test because of closely coupled database call inside of class
public class SALTEDSHA512Test extends AbstractEncryptionMethodTest {
public SALTEDSHA512Test() {
super(new SALTEDSHA512(),
"c8efe95e1ab02d9a0e7c7d11d4ac3cc068a8405b5810aac3a1b8b01927ab059563438131dc995156739daf74db40ffdc79b78f6aec9b2a468fe106b88c66c204", // password
"74c61af1bcbb3293cdc0959c7323d50be28c167eddc7a1b7eb029e38263c2cfb6eb090f41370a65249752aa316fa851091c2bd8420302e87d383529beea735b4", // PassWord1
"08eefcca4a17876441ebe61a02e8bc62cab7502dd87f8ec3b7f82edb2adace791b8dad31e74c5513cf99be502b732f5c5efffb239f4590d5c600d066a7037908", // &^%te$t?Pw@_
"a122490c4c7c18ad665b5ac9617c948741468a787a2ba42c6fd2530ea1d7874681b8575ee9a8907c42ff65dac69e4ada2852789759c17d51865ca915b259a65a"); // âË_3(íù*
new EncryptedPassword("dea7a37cecf5384ae8e347fd1411efb51364b6ba1b328695de3b354612c1d7010807e8b7051c40f740e498490e1f133e2c2408327d13fbdd68e1b1f6d548e624", "29f8a3c52147f987fee7ba3e0fb311bd"), // password
new EncryptedPassword("7c06225aac574d2dc7c81a2ed306637adf025715f52083e05bdab014faaa234e24a97d0e69ea0108dfa77cc9228e58be319ee677e679b5d1ad168d40e50a42f6", "8ea37b85d020b98f60c0fe9b8ec9296c"), // PassWord1
new EncryptedPassword("55711adbe03c9616f3505f0d57077fdd528c32243eb6f9840c1a6ff9e553940d6b89790750ebd52ebda63ca793fbe9980d54057af40836820c648750fe22d49c", "9f58079631ef21d32b4710694f1f461b"), // &^%te$t?Pw@_
new EncryptedPassword("29dc5be8702975ea4563ed3de5b145e2d2f1c37ae661bbe0d3e94d964402cf09d539d65f3b90ff6921ea3d40727f76fb38fb34d1e5c2d62238c4e0203efc372f", "048bb76168265d906f1fd1f81d0616a9")); // âË_3(íù*
}
}

View File

@ -1,20 +1,16 @@
package fr.xephi.authme.security.crypts;
import org.junit.Ignore;
/**
* Test for {@link WBB3}.
*/
@Ignore
// TODO #364 ljacqu 20151220: Unignore test after fixing closely coupled DB dependency
public class WBB3Test extends AbstractEncryptionMethodTest {
public WBB3Test() {
super(new WBB3(),
"ca426c4d20a82cd24c7bb07d94d69f3757e3d07d", // password
"72d59d27674a3cace2600ff152ba8b46274e27e9", // PassWord1
"23daf26602e52591156968a14c2a6592b5be4743", // &^%te$t?Pw@_
"d3908efe4a15314066391dd8572883c70b16fd8a"); // âË_3(íù*
new EncryptedPassword("8df818ef7d56075ab2744f74b98ad68a375ccac4", "b7415b355492ea60314f259a35733a3092c03e3f"), // password
new EncryptedPassword("106da5cf5df92cb845e12cf62cbdb5235b6dc693", "6110f19b2b52910dccf592a19c59126873f42e69"), // PassWord1
new EncryptedPassword("940a9fb7acec0178c6691e8b3c14bd7d789078b1", "f9dd501ff3d1bf74904f9e89649e378429af56e7"), // &^%te$t?Pw@_
new EncryptedPassword("0fa12e8d96c9e95f73aa91f3b76f8cdc815ec8a5", "736be8669f6159ddb2d5b47a3e6428cdb8b324de")); // âË_3(íù*
}
}

View File

@ -12,4 +12,10 @@ public class WORDPRESSTest extends AbstractEncryptionMethodTest {
"$P$BjzPjjzPjrAOyB1V0WFdpisgCTFx.N/", // &^%te$t?Pw@_
"$P$BjzPjxxyjp2QdKcab/oTW8l/W0AgE21"); // âË_3(íù*
}
@Override
protected boolean testHashEqualityForSameSalt() {
// We need to skip the test because Wordpress uses an "internal salt" that is not exposed to the outside
return false;
}
}