#358 Handle hash + salt as one "unit"

- Rename HashResult to EncryptedPassword to reflect its broader use
- Use EncryptedPassword in methods that require the hash and the salt, instead of passing them as strings separately
- Store EncryptedPassword as field in PlayerAuth; updatePassword() thus processes the entire data in the EncryptedPassword object
This commit is contained in:
ljacqu
2015-12-30 17:56:22 +01:00
parent 9c4a578bec
commit a3402d573f
46 changed files with 328 additions and 564 deletions
+4 -3
View File
@@ -43,7 +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.HashResult;
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;
@@ -592,8 +592,9 @@ public class AuthMe extends JavaPlugin {
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()) {
HashResult hashResult = passwordSecurity.computeHash(HashAlgorithm.SHA256, auth.getHash(), auth.getNickname());
auth.setHash(hashResult.getHash());
EncryptedPassword encryptedPassword = passwordSecurity.computeHash(
HashAlgorithm.SHA256, auth.getPassword().getHash(), auth.getNickname());
auth.setPassword(encryptedPassword);
database.updatePassword(auth);
}
Settings.setValue("settings.security.passwordHash", "SHA256");
+3 -4
View File
@@ -4,8 +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.security.crypts.HashResult;
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;
@@ -135,13 +134,13 @@ public class API {
@Deprecated
public static boolean registerPlayer(String playerName, String password) {
String name = playerName.toLowerCase();
HashResult hashResult = passwordSecurity.computeHash(Settings.getPasswordHash, password, name);
EncryptedPassword encryptedPassword = passwordSecurity.computeHash(password, name);
if (isRegistered(name)) {
return false;
}
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.hash(hashResult.getHash())
.hash(encryptedPassword)
.lastLogin(0)
.realName(playerName)
.build();
@@ -3,7 +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.crypts.HashResult;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.util.Utils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@@ -149,14 +149,13 @@ public class NewAPI {
*/
public boolean registerPlayer(String playerName, String password) {
String name = playerName.toLowerCase();
HashResult result = plugin.getPasswordSecurity().computeHash(password, name);
EncryptedPassword result = plugin.getPasswordSecurity().computeHash(password, name);
if (isRegistered(name)) {
return false;
}
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.hash(result.getHash())
.salt(result.getSalt())
.hash(result)
.realName(playerName)
.build();
return plugin.getDataSource().saveAuth(auth);
+57 -201
View File
@@ -1,11 +1,9 @@
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 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 +11,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 +38,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 +52,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 +66,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 +80,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 +94,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 +111,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 +131,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 +148,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 +191,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 +302,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 +310,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);
+ " ! Hash : {" + password.getHash() + ", " + password.getSalt() + "}";
}
/**
@@ -472,8 +332,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 +352,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 +368,7 @@ public class PlayerAuth {
public static final class Builder {
private String name;
private String realName;
private String hash;
private String salt;
private EncryptedPassword hash;
private String ip;
private String world;
private String email;
@@ -523,8 +381,7 @@ public class PlayerAuth {
public PlayerAuth build() {
return new PlayerAuth(
checkNotNull(name),
nullToEmpty(hash),
nullToEmpty(salt),
firstNonNull(hash, new EncryptedPassword("")),
groupId,
firstNonNull(ip, "127.0.0.1"),
lastLogin,
@@ -545,14 +402,13 @@ public class PlayerAuth {
return this;
}
public Builder hash(String hash) {
public Builder hash(EncryptedPassword hash) {
this.hash = hash;
return this;
}
public Builder salt(String salt) {
this.salt = salt;
return this;
public Builder hash(String hash, String salt) {
return hash(new EncryptedPassword(hash, salt));
}
public Builder ip(String ip) {
@@ -7,7 +7,7 @@ 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.security.crypts.HashResult;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import org.bukkit.command.CommandSender;
@@ -68,11 +68,9 @@ public class ChangePasswordAdminCommand implements ExecutableCommand {
}
// TODO #358: Do we always pass lowercase name?? In that case we need to do that in PasswordSecurity!
HashResult hashResult = commandService.getPasswordSecurity().computeHash(playerPass, playerNameLowerCase);
auth.setHash(hashResult.getHash());
auth.setSalt(hashResult.getSalt());
EncryptedPassword encryptedPassword = commandService.getPasswordSecurity().computeHash(playerPass, playerNameLowerCase);
auth.setPassword(encryptedPassword);
// TODO #358: updatePassword(auth) needs to update the salt, too.
if (!dataSource.updatePassword(auth)) {
commandService.send(sender, MessageKey.ERROR);
} else {
@@ -5,7 +5,7 @@ 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.crypts.HashResult;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
@@ -57,13 +57,12 @@ public class RegisterAdminCommand implements ExecutableCommand {
commandService.send(sender, MessageKey.NAME_ALREADY_REGISTERED);
return;
}
HashResult hashResult = commandService.getPasswordSecurity()
EncryptedPassword encryptedPassword = commandService.getPasswordSecurity()
.computeHash(playerPass, playerNameLowerCase);
PlayerAuth auth = PlayerAuth.builder()
.name(playerNameLowerCase)
.realName(playerName)
.hash(hashResult.getHash())
.salt(hashResult.getSalt())
.hash(encryptedPassword)
.build();
if (!commandService.getDataSource().saveAuth(auth)) {
@@ -8,7 +8,7 @@ import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.HashResult;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.entity.Player;
@@ -37,7 +37,7 @@ public class RecoverEmailCommand extends PlayerCommand {
}
String thePass = RandomString.generate(Settings.getRecoveryPassLength);
HashResult hashNew = commandService.getPasswordSecurity().computeHash(thePass, playerName);
EncryptedPassword hashNew = commandService.getPasswordSecurity().computeHash(thePass, playerName);
PlayerAuth auth;
if (PlayerCache.getInstance().isAuthenticated(playerName)) {
auth = PlayerCache.getInstance().getAuth(playerName);
@@ -57,8 +57,7 @@ public class RecoverEmailCommand extends PlayerCommand {
commandService.send(player, MessageKey.INVALID_EMAIL);
return;
}
auth.setHash(hashNew.getHash());
auth.setSalt(hashNew.getSalt());
auth.setPassword(hashNew);
dataSource.updatePassword(auth);
plugin.mail.main(auth, thePass);
commandService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE);
@@ -6,7 +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.HashResult;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import org.bukkit.command.CommandSender;
@@ -42,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, HashResult> playerPSW = new HashMap<>();
HashMap<String, EncryptedPassword> playerPSW = new HashMap<>();
try {
BufferedReader users;
BufferedReader ipFile;
@@ -64,22 +64,21 @@ public class RakamakConverter implements Converter {
while ((line = users.readLine()) != null) {
if (line.contains("=")) {
String[] arguments = line.split("=");
HashResult hashResult = passwordSecurity.computeHash(hash, arguments[1], arguments[0]);
playerPSW.put(arguments[0], hashResult);
EncryptedPassword encryptedPassword = passwordSecurity.computeHash(hash, arguments[1], arguments[0]);
playerPSW.put(arguments[0], encryptedPassword);
}
}
users.close();
for (Entry<String, HashResult> m : playerPSW.entrySet()) {
for (Entry<String, EncryptedPassword> m : playerPSW.entrySet()) {
String playerName = m.getKey();
HashResult psw = playerPSW.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)
.hash(psw.getHash())
.salt(psw.getSalt())
.hash(psw)
.lastLogin(System.currentTimeMillis())
.build();
database.saveAuth(auth);
@@ -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.
*
@@ -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();
@@ -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.
*
@@ -6,7 +6,9 @@ import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.HashAlgorithm;
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;
@@ -284,13 +286,14 @@ 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;
String salt = !columnSalt.isEmpty() ? rs.getString(columnSalt) : null;
String hash = rs.getString(columnPassword);
int group = !columnGroup.isEmpty() ? rs.getInt(columnGroup) : -1;
int id = rs.getInt(columnID);
pAuth = PlayerAuth.builder()
.name(rs.getString(columnName))
.realName(rs.getString(columnRealName))
.hash(rs.getString(columnPassword))
.hash(new EncryptedPassword(hash, salt))
.lastLogin(rs.getLong(columnLastLogin))
.ip(rs.getString(columnIp))
.locWorld(rs.getString(lastlocWorld))
@@ -298,7 +301,6 @@ public class MySQL implements DataSource {
.locY(rs.getDouble(lastlocY))
.locZ(rs.getDouble(lastlocZ))
.email(rs.getString(columnEmail))
.salt(salt)
.groupId(group)
.build();
rs.close();
@@ -328,7 +330,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
@@ -336,12 +338,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();
@@ -513,10 +515,22 @@ public class MySQL implements DataSource {
@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();
return true;
@@ -719,35 +733,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.
*
@@ -1045,12 +1030,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))
.hash(new EncryptedPassword(rs.getString(columnPassword), salt))
.lastLogin(rs.getLong(columnLastLogin))
.ip(rs.getString(columnIp))
.locWorld(rs.getString(lastlocWorld))
@@ -1058,7 +1043,6 @@ public class MySQL implements DataSource {
.locY(rs.getDouble(lastlocY))
.locZ(rs.getDouble(lastlocZ))
.email(rs.getString(columnEmail))
.salt(salt)
.groupId(group)
.build();
@@ -1089,12 +1073,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))
.hash(new EncryptedPassword(rs.getString(columnPassword), salt))
.lastLogin(rs.getLong(columnLastLogin))
.ip(rs.getString(columnIp))
.locWorld(rs.getString(lastlocWorld))
@@ -1102,7 +1086,6 @@ public class MySQL implements DataSource {
.locY(rs.getDouble(lastlocY))
.locZ(rs.getDouble(lastlocZ))
.email(rs.getString(columnEmail))
.salt(salt)
.groupId(group)
.build();
@@ -2,6 +2,7 @@ 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 java.sql.*;
@@ -174,15 +175,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 +199,25 @@ 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() && password.getSalt().isEmpty()) {
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 +241,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 +405,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 +446,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.
*
@@ -761,14 +742,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 +760,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 +771,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 +786,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 +797,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 +808,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))
.hash(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();
}
}
@@ -7,6 +7,7 @@ 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;
@@ -65,10 +66,8 @@ 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);
dataSource.updatePassword(auth);
}
@@ -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;
@@ -138,7 +137,7 @@ public class AsynchronousLogin {
String email = pAuth.getEmail();
boolean passwordVerified = forceLogin || plugin.getPasswordSecurity()
.comparePassword(password, pAuth.getHash(), pAuth.getSalt(), realName);
.comparePassword(password, pAuth.getPassword(), realName);
if (passwordVerified && player.isOnline()) {
PlayerAuth auth = PlayerAuth.builder()
@@ -147,8 +146,7 @@ public class AsynchronousLogin {
.ip(getIP())
.lastLogin(new Date().getTime())
.email(email)
.hash(pAuth.getHash())
.salt(pAuth.getSalt())
.hash(pAuth.getPassword())
.build();
database.updateSession(auth);
@@ -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.crypts.HashResult;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import org.bukkit.entity.Player;
@@ -97,12 +97,11 @@ public class AsyncRegister {
m.send(player, MessageKey.MAX_REGISTER_EXCEEDED);
return;
}
final HashResult hashResult = plugin.getPasswordSecurity().computeHash(password, name);
final EncryptedPassword encryptedPassword = plugin.getPasswordSecurity().computeHash(password, name);
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.realName(player.getName())
.hash(hashResult.getHash())
.salt(hashResult.getSalt())
.hash(encryptedPassword)
.ip(ip)
.locWorld(player.getLocation().getWorld().getName())
.locX(player.getLocation().getX())
@@ -124,12 +123,11 @@ public class AsyncRegister {
}
private void passwordRegister() throws Exception {
final HashResult hashResult = plugin.getPasswordSecurity().computeHash(password, name);
final EncryptedPassword encryptedPassword = plugin.getPasswordSecurity().computeHash(password, name);
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.realName(player.getName())
.hash(hashResult.getHash())
.salt(hashResult.getSalt())
.hash(encryptedPassword)
.ip(ip)
.locWorld(player.getLocation().getWorld().getName())
.locX(player.getLocation().getX())
@@ -55,7 +55,7 @@ public class AsynchronousUnregister {
public void process() {
PlayerAuth cachedAuth = PlayerCache.getInstance().getAuth(name);
if (force || plugin.getPasswordSecurity().comparePassword(
password, cachedAuth.getHash(), cachedAuth.getSalt(), player.getName())) {
password, cachedAuth.getPassword(), player.getName())) {
if (!plugin.getDataSource().removeAuth(name)) {
m.send(player, MessageKey.ERROR);
return;
@@ -3,12 +3,10 @@ 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.HashResult;
import org.bukkit.Bukkit;
import java.util.HashMap;
/**
* Manager class for password-related operations.
*/
@@ -24,48 +22,53 @@ public class PasswordSecurity {
this.supportOldAlgorithm = supportOldAlgorithm;
}
public HashResult computeHash(String password, String playerName) {
public EncryptedPassword computeHash(String password, String playerName) {
return computeHash(algorithm, password, playerName);
}
public HashResult computeHash(HashAlgorithm algorithm, String password, String playerName) {
public EncryptedPassword computeHash(HashAlgorithm algorithm, String password, String playerName) {
EncryptionMethod method = initializeEncryptionMethod(algorithm, playerName);
return method.computeHash(password, playerName);
}
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(auth.getHash(), auth.getSalt(), password, playerName);
return comparePassword(password, auth.getPassword(), playerName);
}
return false;
}
public boolean comparePassword(String hash, String salt, String password, String playerName) {
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(hash, password, salt, playerName)
|| supportOldAlgorithm && compareWithAllEncryptionMethods(password, hash, salt, playerName);
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.
* 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 hash The hash to text the password against
* @param salt The salt (or null if none available)
* @param playerName The name of the player
* @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, String hash, String salt, String playerName) {
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(hash, password, salt, playerName)) {
if (method != null && method.comparePassword(password, encryptedPassword, playerName)) {
hashPasswordForNewAlgorithm(password, playerName);
return true;
}
@@ -75,8 +78,8 @@ public class PasswordSecurity {
}
/**
* Get the encryption method from the given {@link HashAlgorithm} value and emits a
* {@link PasswordEncryptionEvent}. The encryption method from the event is returned,
* 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
@@ -110,14 +113,10 @@ public class PasswordSecurity {
private void hashPasswordForNewAlgorithm(String password, String playerName) {
PlayerAuth auth = dataSource.getAuth(playerName);
if (auth != null) {
HashResult hashResult = initializeEncryptionMethod(algorithm, playerName)
EncryptedPassword encryptedPassword = initializeEncryptionMethod(algorithm, playerName)
.computeHash(password, playerName);
// TODO #358: updatePassword() should just take the HashResult..., or at least hash & salt. Idem for setHash
auth.setSalt(hashResult.getSalt());
auth.setHash(hashResult.getHash());
auth.setPassword(encryptedPassword);
dataSource.updatePassword(auth);
dataSource.updateSalt(auth);
}
}
@@ -520,14 +520,14 @@ public class BCRYPT implements EncryptionMethod {
}
@Override
public HashResult computeHash(String password, String name) {
public EncryptedPassword computeHash(String password, String name) {
String salt = generateSalt();
return new HashResult(hashpw(password, salt), null);
return new EncryptedPassword(hashpw(password, salt), null);
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return checkpw(password, hash);
public boolean comparePassword(String password, EncryptedPassword hash, String name) {
return checkpw(password, hash.getHash());
}
@Override
@@ -15,11 +15,13 @@ public class BCRYPT2Y extends HexSaltedMethod {
}
@Override
public boolean comparePassword(String hash, String password, String unusedSalt, String unusedName) {
public boolean comparePassword(String password, EncryptedPassword encrypted, String unusedName) {
String hash = encrypted.getHash();
if (hash.length() != 60) {
return false;
}
// The salt is the first 29 characters of the hash
String salt = hash.substring(0, 29);
return hash.equals(computeHash(password, salt, null));
}
@@ -28,11 +28,11 @@ public class CRAZYCRYPT1 extends UsernameSaltMethod {
}
@Override
public HashResult computeHash(String password, String name) {
public EncryptedPassword computeHash(String password, String name) {
final String text = "ÜÄaeut//&/=I " + password + "7421€547" + name + "__+IÄIH§%NK " + password;
final MessageDigest md = HashUtils.getDigest(MessageDigestAlgorithm.SHA512);
md.update(text.getBytes(charset), 0, text.length());
return new HashResult(byteArrayToHexString(md.digest()));
return new EncryptedPassword(byteArrayToHexString(md.digest()));
}
}
@@ -20,8 +20,8 @@ public class CryptPBKDF2 extends HexSaltedMethod {
}
@Override
public boolean comparePassword(String hash, String password, String unusedSalt, String unusedName) {
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());
@@ -19,8 +19,8 @@ public class CryptPBKDF2Django extends HexSaltedMethod {
}
@Override
public boolean comparePassword(String hash, String password, String unusedSalt, String unusedName) {
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);
@@ -3,7 +3,7 @@ package fr.xephi.authme.security.crypts;
/**
* The result of a hash computation. See {@link #salt} for details.
*/
public class HashResult {
public class EncryptedPassword {
/** The generated hash. */
private final String hash;
@@ -23,7 +23,7 @@ public class HashResult {
* @param hash The computed hash
* @param salt The generated salt
*/
public HashResult(String hash, String salt) {
public EncryptedPassword(String hash, String salt) {
this.hash = hash;
this.salt = salt;
}
@@ -33,7 +33,7 @@ public class HashResult {
*
* @param hash The computed hash
*/
public HashResult(String hash) {
public EncryptedPassword(String hash) {
this(hash, null);
}
@@ -12,9 +12,9 @@ public interface EncryptionMethod {
* @param name The name of the player (sometimes required to generate a salt with)
*
* @return The hash result for the password.
* @see HashResult
* @see EncryptedPassword
*/
HashResult computeHash(String password, String name);
EncryptedPassword computeHash(String password, String name);
/**
* Hash the given password with the given salt for the given player.
@@ -31,14 +31,13 @@ public interface EncryptionMethod {
/**
* Check whether the given hash matches the clear-text password.
*
* @param hash The hash to verify
* @param password The clear-text password to verify the hash against
* @param salt The salt if it is stored separately (null otherwise)
* @param name The player name to do the check for (sometimes required for generating the salt)
* @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 salt, String name);
boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name);
/**
* Generate a new salt to hash a password with.
@@ -49,7 +48,7 @@ public interface EncryptionMethod {
/**
* Return whether the encryption method requires the salt to be stored separately and
* passed again to {@link #comparePassword(String, String, String, String)}. Note that
* 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.
*
@@ -20,13 +20,13 @@ public abstract class HexSaltedMethod implements EncryptionMethod {
public abstract String computeHash(String password, String salt, String name);
@Override
public HashResult computeHash(String password, String name) {
public EncryptedPassword computeHash(String password, String name) {
String salt = generateSalt();
return new HashResult(computeHash(password, salt, null));
return new EncryptedPassword(computeHash(password, salt, null));
}
@Override
public abstract boolean comparePassword(String hash, String password, String salt, String name);
public abstract boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name);
@Override
public String generateSalt() {
@@ -13,7 +13,8 @@ public class JOOMLA extends HexSaltedMethod {
}
@Override
public boolean comparePassword(String hash, String password, String unusedSalt, String unusedName) {
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));
}
@@ -10,7 +10,8 @@ public class MD5VB extends HexSaltedMethod {
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
String hash = encryptedPassword.getHash();
String[] line = hash.split("\\$");
return line.length == 4 && hash.equals(computeHash(password, line[2], name));
}
@@ -144,8 +144,8 @@ public class PHPBB extends HexSaltedMethod {
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return phpbb_check_hash(password, hash);
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
return phpbb_check_hash(password, encryptedPassword.getHash());
}
@Override
@@ -14,7 +14,8 @@ public class SHA256 extends HexSaltedMethod {
}
@Override
public boolean comparePassword(String hash, String password, String salt, String playerName) {
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String playerName) {
String hash = encryptedPassword.getHash();
String[] line = hash.split("\\$");
return line.length == 4 && hash.equals(computeHash(password, line[2], ""));
}
@@ -4,8 +4,8 @@ import fr.xephi.authme.security.HashUtils;
public class SMF extends UsernameSaltMethod {
public HashResult computeHash(String password, String name) {
return new HashResult(HashUtils.sha1(name.toLowerCase() + password));
public EncryptedPassword computeHash(String password, String name) {
return new EncryptedPassword(HashUtils.sha1(name.toLowerCase() + password));
}
}
@@ -12,14 +12,14 @@ public abstract class SeparateSaltMethod implements EncryptionMethod {
public abstract String generateSalt();
@Override
public HashResult computeHash(String password, String name) {
public EncryptedPassword computeHash(String password, String name) {
String salt = generateSalt();
return new HashResult(computeHash(password, salt, name), salt);
return new EncryptedPassword(computeHash(password, salt, name), salt);
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return hash.equals(computeHash(password, salt, null));
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
return encryptedPassword.getHash().equals(computeHash(password, encryptedPassword.getSalt(), null));
}
@Override
@@ -15,8 +15,8 @@ public abstract class UnsaltedMethod implements EncryptionMethod {
public abstract String computeHash(String password);
@Override
public HashResult computeHash(String password, String name) {
return new HashResult(computeHash(password));
public EncryptedPassword computeHash(String password, String name) {
return new EncryptedPassword(computeHash(password));
}
@Override
@@ -25,8 +25,8 @@ public abstract class UnsaltedMethod implements EncryptionMethod {
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return hash.equals(computeHash(password));
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
return encryptedPassword.getHash().equals(computeHash(password));
}
@Override
@@ -7,18 +7,18 @@ 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 salt.
* (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 HashResult computeHash(String password, String name);
public abstract EncryptedPassword computeHash(String password, String name);
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return hash.equals(computeHash(password, name).getHash());
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
return encryptedPassword.getHash().equals(computeHash(password, name).getHash());
}
@Override
@@ -12,8 +12,8 @@ public class WBB4 extends HexSaltedMethod {
}
@Override
public boolean comparePassword(String hash, String password, String salt, String playerName) {
return BCRYPT.checkpw(password, hash, 2);
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String playerName) {
return BCRYPT.checkpw(password, encryptedPassword.getHash(), 2);
}
@Override
@@ -117,7 +117,8 @@ public class WORDPRESS extends UnsaltedMethod {
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
public boolean comparePassword(String password, EncryptedPassword encryptedPassword, String name) {
String hash = encryptedPassword.getHash();
String comparedHash = crypt(password, hash);
return comparedHash.equals(hash);
}
@@ -23,7 +23,8 @@ public class XAUTH extends HexSaltedMethod {
}
@Override
public boolean comparePassword(String hash, String password, String salt, String playerName) {
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 saltFromHash = hash.substring(saltPos, saltPos + 12);
return hash.equals(computeHash(password, saltFromHash, null));
@@ -9,7 +9,7 @@ import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.HashResult;
import fr.xephi.authme.security.crypts.EncryptedPassword;
import fr.xephi.authme.settings.Settings;
import org.bukkit.entity.Player;
@@ -34,22 +34,21 @@ public class ChangePasswordTask implements Runnable {
final String name = player.getName().toLowerCase();
PlayerAuth auth = PlayerCache.getInstance().getAuth(name);
if (passwordSecurity.comparePassword(oldPassword, auth.getHash(), auth.getSalt(), player.getName())) {
HashResult hashResult = passwordSecurity.computeHash(newPassword, name);
auth.setHash(hashResult.getHash());
auth.setSalt(hashResult.getSalt());
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;
}
plugin.getDataSource().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();
final String hash = encryptedPassword.getHash();
final String salt = encryptedPassword.getSalt();
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable(){
@Override