#358 Create encryption method supertypes, add new methods

This commit is contained in:
ljacqu
2015-12-28 20:10:45 +01:00
parent 31730699ac
commit 48d0a65724
43 changed files with 551 additions and 524 deletions
@@ -17,6 +17,7 @@ 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.SecureRandom;
@@ -63,9 +64,9 @@ import java.security.SecureRandom;
* @author Damien Miller
* @version 0.2
*/
@Recommendation(Usage.RECOMMENDED)
@HasSalt(value = SaltType.TEXT, length = BCRYPT.BCRYPT_SALT_LEN)
public class BCRYPT implements EncryptionMethod {
@Recommendation(Usage.RECOMMENDED) // provided the salt length is >= 8
@HasSalt(value = SaltType.TEXT) // length depends on Settings.bCryptLog2Rounds
public class BCRYPT implements NewEncrMethod {
// BCrypt parameters
private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
@@ -518,8 +519,10 @@ public class BCRYPT implements EncryptionMethod {
return hashpw(password, salt);
}
public String computeHash(String password, String name) {
return hashpw(password, generateSalt());
@Override
public HashResult computeHash(String password, String name) {
String salt = generateSalt();
return new HashResult(hashpw(password, salt), salt);
}
@Override
@@ -527,7 +530,18 @@ public class BCRYPT implements EncryptionMethod {
return checkpw(password, hash);
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return comparePassword(hash, password, name);
}
@Override
public String generateSalt() {
return BCRYPT.gensalt();
return BCRYPT.gensalt(Settings.bCryptLog2Rounds);
}
@Override
public boolean hasSeparateSalt() {
return false;
}
}
@@ -12,7 +12,7 @@ import java.security.MessageDigest;
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.USERNAME)
public class CRAZYCRYPT1 implements EncryptionMethod {
public class CRAZYCRYPT1 extends UsernameSaltMethod {
private static final char[] CRYPTCHARS =
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
@@ -28,23 +28,11 @@ public class CRAZYCRYPT1 implements EncryptionMethod {
}
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password, name);
}
public String computeHash(String password, String name) {
public HashResult 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 byteArrayToHexString(md.digest());
return new HashResult(byteArrayToHexString(md.digest()));
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
return hash.equals(computeHash(password, playerName));
}
public String generateSalt() {
return null;
}
}
@@ -12,8 +12,7 @@ import java.util.Arrays;
public class CryptPBKDF2 implements EncryptionMethod {
@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);
@@ -22,8 +21,7 @@ public class CryptPBKDF2 implements EncryptionMethod {
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
public boolean comparePassword(String hash, String password, String playerName) {
String[] line = hash.split("\\$");
String salt = line[2];
String derivedKey = line[3];
@@ -1,18 +1,11 @@
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;
import fr.xephi.authme.security.pbkdf2.PBKDF2Engine;
import fr.xephi.authme.security.pbkdf2.PBKDF2Parameters;
import javax.xml.bind.DatatypeConverter;
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 12)
public class CryptPBKDF2Django implements EncryptionMethod {
public class CryptPBKDF2Django extends HexSaltedMethod {
@Override
public String computeHash(String password, String salt, String name) {
@@ -23,12 +16,8 @@ public class CryptPBKDF2Django implements EncryptionMethod {
return result + String.valueOf(DatatypeConverter.printBase64Binary(engine.deriveKey(password, 32)));
}
public String computeHash(String password, String name) {
return computeHash(password, generateSalt(), null);
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
public boolean comparePassword(String hash, String password, String unusedSalt, String unusedName) {
String[] line = hash.split("\\$");
String salt = line[2];
byte[] derivedKey = DatatypeConverter.parseBase64Binary(line[3]);
@@ -37,8 +26,9 @@ public class CryptPBKDF2Django implements EncryptionMethod {
return engine.verifyKey(password);
}
public String generateSalt() {
return RandomString.generateHex(12);
@Override
public int getSaltLength() {
return 12;
}
}
@@ -1,31 +1,12 @@
package fr.xephi.authme.security.crypts;
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;
import static fr.xephi.authme.security.HashUtils.md5;
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.NONE)
public class DOUBLEMD5 implements EncryptionMethod {
public class DOUBLEMD5 extends UnsaltedMethod {
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password, null);
}
public String computeHash(String password, String name) {
return HashUtils.md5(HashUtils.md5(password));
}
public String generateSalt() {
return null;
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
return hash.equals(computeHash(password, null, null));
public String computeHash(String password) {
return md5(md5(password));
}
}
@@ -1,7 +1,5 @@
package fr.xephi.authme.security.crypts;
import java.security.NoSuchAlgorithmException;
/**
* Public interface for custom password encryption methods.
*/
@@ -16,8 +14,7 @@ public interface EncryptionMethod {
*
* @return The hashed password
*/
String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException;
String computeHash(String password, String salt, String name);
/**
* Check whether a given hash matches the clear-text password.
@@ -30,7 +27,6 @@ public interface EncryptionMethod {
* @return True if the password matches, false otherwise
*/
@Deprecated
boolean comparePassword(String hash, String password, String playerName)
throws NoSuchAlgorithmException;
boolean comparePassword(String hash, String password, String playerName);
}
@@ -14,6 +14,10 @@ public class HashResult {
this.hash = hash;
this.salt = salt;
}
public HashResult(String hash) {
this(hash, null);
}
public String getHash() {
return hash;
@@ -0,0 +1,46 @@
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 NewEncrMethod {
public abstract int getSaltLength();
@Override
public abstract String computeHash(String password, String salt, String name);
@Override
public HashResult computeHash(String password, String name) {
String salt = generateSalt();
return new HashResult(computeHash(password, salt, null));
}
@Override
public abstract boolean comparePassword(String hash, String password, String salt, String name);
@Override
@Deprecated
public boolean comparePassword(String hash, String password, String playerName) {
return false;
}
@Override
public String generateSalt() {
return RandomString.generateHex(getSaltLength());
}
@Override
public boolean hasSeparateSalt() {
return false;
}
}
@@ -1,34 +1,52 @@
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.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;
/**
*/
public class IPB3 implements EncryptionMethod {
import static fr.xephi.authme.security.HashUtils.md5;
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));
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(value = SaltType.TEXT, length = 5)
public class IPB3 implements NewEncrMethod {
@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 HashResult computeHash(String password, String name) {
String salt = generateSalt();
return new HashResult(computeHash(password, salt, name), salt);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
public boolean comparePassword(String hash, String password, String playerName) {
String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt();
return hash.equals(computeHash(password, salt, playerName));
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return hash.equals(computeHash(password, salt, name));
}
@Override
public String generateSalt() {
return RandomString.generateHex(5);
}
@Override
public boolean hasSeparateSalt() {
return true;
}
}
@@ -1,36 +1,26 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.HashUtils;
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;
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 32)
public class JOOMLA implements EncryptionMethod {
@Recommendation(Usage.RECOMMENDED)
public class JOOMLA extends HexSaltedMethod {
@Override
public String computeHash(String password, String salt, String name) {
return HashUtils.md5(password + salt) + ":" + salt;
}
public String computeHash(String password, String name) {
return computeHash(password, generateSalt(), null);
}
public String generateSalt() {
return RandomString.generateHex(32);
@Override
public boolean comparePassword(String hash, String password, String unusedSalt, String unusedName) {
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) {
String[] hashParts = hash.split(":");
if (hashParts.length != 2) {
return false;
}
String salt = hashParts[1];
return hash.equals(computeHash(password, salt, null));
public int getSaltLength() {
return 32;
}
}
@@ -6,21 +6,11 @@ import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.NONE)
public class MD5 implements EncryptionMethod {
public class MD5 extends UnsaltedMethod {
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password, null);
}
public String computeHash(String password, String name) {
public String computeHash(String password) {
return HashUtils.md5(password);
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
return hash.equals(computeHash(password, null));
}
}
@@ -1,34 +1,23 @@
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;
import static fr.xephi.authme.security.HashUtils.md5;
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 16)
public class MD5VB implements EncryptionMethod {
public class MD5VB extends HexSaltedMethod {
@Override
public String computeHash(String password, String salt, String name) {
return "$MD5vb$" + salt + "$" + md5(md5(password) + salt);
}
public String computeHash(String password, String name) {
return computeHash(password, generateSalt(), null);
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
String[] line = hash.split("\\$");
return line.length == 4 && hash.equals(computeHash(password, line[2], name));
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
String[] line = hash.split("\\$");
return line.length == 4 && hash.equals(computeHash(password, line[2], ""));
}
public String generateSalt() {
return RandomString.generateHex(16);
public int getSaltLength() {
return 16;
}
}
@@ -1,34 +1,46 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.RandomString;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
*/
public class MYBB implements EncryptionMethod {
import static fr.xephi.authme.security.HashUtils.md5;
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 MYBB implements NewEncrMethod {
@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 HashResult computeHash(String password, String name) {
String salt = generateSalt();
return new HashResult(computeHash(password, salt, name), salt);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
public boolean comparePassword(String hash, String password, String playerName) {
String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt();
return hash.equals(computeHash(password, salt, playerName));
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return hash.equals(computeHash(password, salt, name));
}
@Override
public String generateSalt() {
return RandomString.generateHex(8);
}
@Override
public boolean hasSeparateSalt() {
return true;
}
}
@@ -4,19 +4,20 @@
*/
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.RandomString;
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");
@@ -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,25 @@ public class PHPBB implements EncryptionMethod {
return output;
}
public boolean phpbb_check_hash(String password, String hash) {
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);
}
@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 {
public boolean comparePassword(String hash, String password, String salt, String name) {
return phpbb_check_hash(password, hash);
}
@Override
public int getSaltLength() {
return 16;
}
}
@@ -1,6 +1,10 @@
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 javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
@@ -10,27 +14,15 @@ 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)
public class PHPFUSION implements NewEncrMethod {
@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 +34,38 @@ 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 HashResult computeHash(String password, String name) {
String salt = generateSalt();
return new HashResult(computeHash(password, salt, name), salt);
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
String playerName) {
String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt();
return hash.equals(computeHash(password, salt, ""));
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return hash.equals(computeHash(password, salt, name));
}
@Override
public String generateSalt() {
return RandomString.generateHex(12);
}
@Override
public boolean hasSeparateSalt() {
return true;
}
}
@@ -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);
}
}
@@ -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, "", ""));
}
}
@@ -1,6 +1,7 @@
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.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
@@ -12,50 +13,15 @@ import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import static fr.xephi.authme.security.HashUtils.md5;
@Recommendation(Usage.ACCEPTABLE) // presuming that length is something sensible (>= 8)
@HasSalt(value = SaltType.TEXT) // length defined by Settings.saltLength
public class SALTED2MD5 implements NewEncrMethod {
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 SALTED2MD5 extends SeparateSaltMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
return getMD5(getMD5(password) + salt);
}
@Override
public HashResult computeHash(String password, String name) {
try {
String salt = generateSalt();
return new HashResult(computeHash(password, salt, name), salt);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e); // TODO #358: Remove try-catch clause
}
}
@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));
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
try {
return hash.equals(computeHash(password, salt, name));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
// TODO #358: Remove try-catch
}
public String computeHash(String password, String salt, String name) {
return md5(md5(password) + salt);
}
@Override
@@ -63,9 +29,4 @@ public class SALTED2MD5 implements NewEncrMethod {
return RandomString.generateHex(Settings.saltLength);
}
@Override
public boolean hasSeparateSalt() {
return true;
}
}
@@ -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);
}
}
@@ -1,31 +1,12 @@
package fr.xephi.authme.security.crypts;
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;
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.NONE)
public class SHA1 implements EncryptionMethod {
public class SHA1 extends UnsaltedMethod {
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password, null);
}
public String computeHash(String password, String name) {
public String computeHash(String password) {
return HashUtils.sha1(password);
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
return hash.equals(computeHash(password, null));
}
public String generateSalt() {
return null;
}
}
@@ -1,34 +1,27 @@
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;
import static fr.xephi.authme.security.HashUtils.sha256;
@Recommendation(Usage.RECOMMENDED)
@HasSalt(value = SaltType.TEXT, length = 16)
public class SHA256 implements EncryptionMethod {
public class SHA256 extends HexSaltedMethod {
@Override
public String computeHash(String password, String salt, String name) {
return "$SHA$" + salt + "$" + sha256(sha256(password) + salt);
}
public String computeHash(String password, String name) {
return computeHash(password, generateSalt(), name);
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
public boolean comparePassword(String hash, String password, String salt, String playerName) {
String[] line = hash.split("\\$");
return line.length == 4 && hash.equals(computeHash(password, line[2], ""));
}
public String generateSalt() {
return RandomString.generateHex(16);
@Override
public int getSaltLength() {
return 16;
}
}
@@ -1,30 +1,12 @@
package fr.xephi.authme.security.crypts;
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;
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.NONE)
public class SHA512 implements EncryptionMethod {
public class SHA512 extends UnsaltedMethod {
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password, name);
}
public String computeHash(String password, String name) {
public String computeHash(String password) {
return HashUtils.sha512(password);
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
return hash.equals(computeHash(password, "", ""));
}
public String generateSalt() {
return null;
}
}
@@ -1,30 +1,11 @@
package fr.xephi.authme.security.crypts;
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;
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.USERNAME)
public class SMF implements EncryptionMethod {
public class SMF extends UsernameSaltMethod {
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password, name);
public HashResult computeHash(String password, String name) {
return new HashResult(HashUtils.sha1(name.toLowerCase() + password));
}
public String computeHash(String password, String name) {
return HashUtils.sha1(name.toLowerCase() + password);
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
return hash.equals(computeHash(password, playerName));
}
public String generateSalt() {
return null;
}
}
@@ -0,0 +1,35 @@
package fr.xephi.authme.security.crypts;
/**
* Common supertype for encryption methods which store their salt separately from the hash.
*/
public abstract class SeparateSaltMethod implements NewEncrMethod {
@Override
public abstract String computeHash(String password, String salt, String name);
@Override
public abstract String generateSalt();
@Override
public HashResult computeHash(String password, String name) {
String salt = generateSalt();
return new HashResult(computeHash(password, salt, name), salt);
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return hash.equals(computeHash(password, salt, null));
}
@Override
public boolean hasSeparateSalt() {
return true;
}
@Override
@Deprecated
public boolean comparePassword(String hash, String password, String playerName) {
return false;
}
}
@@ -0,0 +1,47 @@
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 NewEncrMethod {
public abstract String computeHash(String password);
@Override
public HashResult computeHash(String password, String name) {
return new HashResult(computeHash(password));
}
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password);
}
@Override
@Deprecated
public boolean comparePassword(String hash, String password, String playerName) {
return false;
}
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return hash.equals(computeHash(password));
}
@Override
public String generateSalt() {
return null;
}
@Override
public boolean hasSeparateSalt() {
return false;
}
}
@@ -0,0 +1,45 @@
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 salt.
*/
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.USERNAME)
public abstract class UsernameSaltMethod implements NewEncrMethod {
@Override
public abstract HashResult computeHash(String password, String name);
@Override
public boolean comparePassword(String hash, String password, String salt, String name) {
return hash.equals(computeHash(password, name).getHash());
}
@Override
@Deprecated
public boolean comparePassword(String hash, String password, String playerName) {
return false;
}
@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;
}
}
@@ -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, ""));
}
}
@@ -7,14 +7,12 @@ import java.security.NoSuchAlgorithmException;
public class WBB4 implements EncryptionMethod {
@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 {
public boolean comparePassword(String hash, String password, String playerName) {
return BCRYPT.checkpw(password, hash, 2);
}
@@ -59,16 +59,9 @@ package fr.xephi.authme.security.crypts;
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
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.util.Arrays;
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.NONE)
public class WHIRLPOOL implements EncryptionMethod {
public class WHIRLPOOL extends UnsaltedMethod {
/**
* The message digest size (in bits)
@@ -386,12 +379,7 @@ public class WHIRLPOOL implements EncryptionMethod {
}
}
@Override
public String computeHash(String password, String salt, String name) {
return computeHash(password, null);
}
public String computeHash(String password, String name) {
public String computeHash(String password) {
byte[] digest = new byte[DIGESTBYTES];
NESSIEinit();
NESSIEadd(password);
@@ -399,9 +387,4 @@ public class WHIRLPOOL implements EncryptionMethod {
return display(digest);
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
return hash.equals(computeHash(password, null, null));
}
}
@@ -13,7 +13,9 @@ import java.util.Arrays;
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 9)
public class WORDPRESS implements EncryptionMethod {
// 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();
@@ -107,25 +109,16 @@ public class WORDPRESS implements EncryptionMethod {
}
@Override
public String computeHash(String password, String salt, String name) {
public String computeHash(String password) {
byte random[] = new byte[6];
randomGen.nextBytes(random);
return crypt(password, gensaltPrivate(stringToUtf8(new String(random))));
}
public String computeHash(String password, String name) {
return computeHash(password, null, null);
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
public boolean comparePassword(String hash, String password, String salt, String name) {
String comparedHash = crypt(password, hash);
return comparedHash.equals(hash);
}
public String generateSalt() {
// This hash uses a salt, but it is not exposed to the outside
return null;
}
}
@@ -1,16 +1,12 @@
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;
@Recommendation(Usage.RECOMMENDED)
@HasSalt(value = SaltType.TEXT, length = 12)
public class XAUTH implements EncryptionMethod {
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();
@@ -26,19 +22,16 @@ public class XAUTH implements EncryptionMethod {
return hash.substring(0, saltPos) + salt + hash.substring(saltPos);
}
public String computeHash(String password, String name) {
return computeHash(password, generateSalt(), null);
@Override
public boolean comparePassword(String hash, String password, String salt, String playerName) {
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));
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
int saltPos = (password.length() >= hash.length() ? hash.length() - 1 : password.length());
String salt = hash.substring(saltPos, saltPos + 12);
return hash.equals(computeHash(password, salt, ""));
}
public String generateSalt() {
return RandomString.generateHex(12);
public int getSaltLength() {
return 12;
}
}
@@ -11,23 +11,37 @@ import java.util.regex.Pattern;
/**
*/
public class XF implements EncryptionMethod {
public class XF implements NewEncrMethod {
@Override
public String computeHash(String password, String salt, String name)
throws NoSuchAlgorithmException {
public String computeHash(String password, String salt, String name) {
return getSha256(getSha256(password) + regmatch("\"salt\";.:..:\"(.*)\";.:.:\"hashFunc\"", salt));
}
@Override
public boolean comparePassword(String hash, String password,
String playerName) throws NoSuchAlgorithmException {
public HashResult computeHash(String password, String name) {
String salt = generateSalt();
return new HashResult(computeHash(password, salt, null), salt);
}
@Override
public boolean comparePassword(String hash, String password, String playerName) {
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");
public boolean comparePassword(String hash, String password, String salt, String name) {
return hash.equals(regmatch("\"hash\";.:..:\"(.*)\";.:.:\"salt\"", salt));
}
private String getSha256(String password) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
// TODO #358: Handle exception properly
throw new RuntimeException(e);
}
md.update(password.getBytes());
byte byteData[] = md.digest();
StringBuilder sb = new StringBuilder();
@@ -45,6 +59,17 @@ public class XF implements EncryptionMethod {
return hexString.toString();
}
@Override
public String generateSalt() {
// TODO #369: Find out what kind of salt format XF uses
return "";
}
@Override
public boolean hasSeparateSalt() {
return true;
}
private String regmatch(String pattern, String line) {
List<String> allMatches = new ArrayList<>();
Matcher m = Pattern.compile(pattern).matcher(line);