#364 Create first EncryptionMethod tests

This commit is contained in:
ljacqu 2015-12-20 00:50:34 +01:00
parent 4f6c7d579c
commit bf7a0c5a49
9 changed files with 201 additions and 19 deletions

View File

@ -1,6 +1,6 @@
package fr.xephi.authme.security; package fr.xephi.authme.security;
import org.apache.commons.lang.ObjectUtils.Null; import fr.xephi.authme.security.crypts.EncryptionMethod;
/** /**
*/ */
@ -33,21 +33,21 @@ public enum HashAlgorithm {
CRAZYCRYPT1(fr.xephi.authme.security.crypts.CRAZYCRYPT1.class), CRAZYCRYPT1(fr.xephi.authme.security.crypts.CRAZYCRYPT1.class),
BCRYPT2Y(fr.xephi.authme.security.crypts.BCRYPT2Y.class), BCRYPT2Y(fr.xephi.authme.security.crypts.BCRYPT2Y.class),
SALTEDSHA512(fr.xephi.authme.security.crypts.SALTEDSHA512.class), SALTEDSHA512(fr.xephi.authme.security.crypts.SALTEDSHA512.class),
CUSTOM(Null.class); CUSTOM(null);
final Class<?> classe; final Class<? extends EncryptionMethod> clazz;
/** /**
* Constructor for HashAlgorithm. * Constructor for HashAlgorithm.
* *
* @param classe The class of the hash implementation. * @param clazz The class of the hash implementation.
*/ */
HashAlgorithm(Class<?> classe) { HashAlgorithm(Class<? extends EncryptionMethod> clazz) {
this.classe = classe; this.clazz = clazz;
} }
public Class<?> getclasse() { public Class<? extends EncryptionMethod> getClazz() {
return classe; return clazz;
} }
} }

View File

@ -36,7 +36,7 @@ public class PasswordSecurity {
EncryptionMethod method; EncryptionMethod method;
try { try {
if (alg != HashAlgorithm.CUSTOM) if (alg != HashAlgorithm.CUSTOM)
method = (EncryptionMethod) alg.getclasse().newInstance(); method = alg.getClazz().newInstance();
else method = null; else method = null;
} catch (InstantiationException | IllegalAccessException e) { } catch (InstantiationException | IllegalAccessException e) {
throw new NoSuchAlgorithmException("Problem with hash algorithm '" + alg + "'", e); throw new NoSuchAlgorithmException("Problem with hash algorithm '" + alg + "'", e);
@ -131,10 +131,11 @@ public class PasswordSecurity {
HashAlgorithm algorithm = Settings.getPasswordHash; HashAlgorithm algorithm = Settings.getPasswordHash;
EncryptionMethod method; EncryptionMethod method;
try { try {
if (algorithm != HashAlgorithm.CUSTOM) if (algorithm != HashAlgorithm.CUSTOM) {
method = (EncryptionMethod) algorithm.getclasse().newInstance(); method = algorithm.getClazz().newInstance();
else } else {
method = null; method = null;
}
PasswordEncryptionEvent event = new PasswordEncryptionEvent(method, playerName); PasswordEncryptionEvent event = new PasswordEncryptionEvent(method, playerName);
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
@ -161,7 +162,7 @@ public class PasswordSecurity {
for (HashAlgorithm algo : HashAlgorithm.values()) { for (HashAlgorithm algo : HashAlgorithm.values()) {
if (algo != HashAlgorithm.CUSTOM) { if (algo != HashAlgorithm.CUSTOM) {
try { try {
EncryptionMethod method = (EncryptionMethod) algo.getclasse().newInstance(); EncryptionMethod method = algo.getClazz().newInstance();
if (method.comparePassword(hash, password, playerName)) { if (method.comparePassword(hash, password, playerName)) {
PlayerAuth nAuth = AuthMe.getInstance().database.getAuth(playerName); PlayerAuth nAuth = AuthMe.getInstance().database.getAuth(playerName);
if (nAuth != null) { if (nAuth != null) {

View File

@ -1,21 +1,22 @@
package fr.xephi.authme.security; package fr.xephi.authme.security;
import java.security.SecureRandom;
import java.util.Calendar; import java.util.Calendar;
import java.util.Random; import java.util.Random;
/**
* @author Xephi59
*/
public class RandomString { public class RandomString {
private static final char[] chars = new char[36]; private static final char[] chars = new char[36];
private static final Random RANDOM = new SecureRandom();
static { static {
for (int idx = 0; idx < 10; ++idx) for (int idx = 0; idx < 10; ++idx) {
chars[idx] = (char) ('0' + idx); chars[idx] = (char) ('0' + idx);
for (int idx = 10; idx < 36; ++idx) }
for (int idx = 10; idx < 36; ++idx) {
chars[idx] = (char) ('a' + idx - 10); chars[idx] = (char) ('a' + idx - 10);
} }
}
private final Random random = new Random(); private final Random random = new Random();
@ -34,4 +35,15 @@ public class RandomString {
return new String(buf); return new String(buf);
} }
public static String generate(int length) {
if (length < 0) {
throw new IllegalArgumentException("Length must be positive but was " + length);
}
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; ++i) {
sb.append(chars[RANDOM.nextInt(chars.length)]);
}
return sb.toString();
}
} }

View File

@ -150,7 +150,8 @@ public class BCRYPT implements EncryptionMethod {
* @param s the string to decode * @param s the string to decode
* @param maxolen the maximum number of bytes to decode * @param maxolen the maximum number of bytes to decode
* *
* @return an array containing the decoded bytes * @throws IllegalArgumentException if maxolen is invalid * @throws IllegalArgumentException * @return an array containing the decoded bytes
* @throws IllegalArgumentException if maxolen is invalid
*/ */
private static byte[] decode_base64(String s, int maxolen) private static byte[] decode_base64(String s, int maxolen)
throws IllegalArgumentException { throws IllegalArgumentException {

View File

@ -0,0 +1,100 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.PasswordSecurity;
import org.junit.Test;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Test for implementations of {@link EncryptionMethod}.
*/
// TODO #358: Remove NoSuchAlgorithm try-catch-es when no longer necessary
public abstract class AbstractEncryptionMethodTest {
public static final String USERNAME = "Test_Player00";
public static final String[] GIVEN_PASSWORDS = {"password", "PassWord1", "&^%te$t?Pw@_", "âË_3(íù*"};
private static final String[] INTERNAL_PASSWORDS = {"test1234", "Ab_C73", "(!#&$~`_-Aa0", "Ûïé1&?+A"};
private EncryptionMethod method;
private Map<String, String> hashes;
public AbstractEncryptionMethodTest(EncryptionMethod method, String hash0, String hash1,
String hash2, String hash3) {
this.method = method;
hashes = new HashMap<>();
hashes.put(GIVEN_PASSWORDS[0], hash0);
hashes.put(GIVEN_PASSWORDS[1], hash1);
hashes.put(GIVEN_PASSWORDS[2], hash2);
hashes.put(GIVEN_PASSWORDS[3], hash3);
}
@Test
public void testGivenPasswords() {
for (String password : GIVEN_PASSWORDS) {
try {
assertTrue("Hash for password '" + password + "' should match",
method.comparePassword(hashes.get(password), password, USERNAME));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("EncryptionMethod '" + method + "' threw exception", e);
}
}
}
@Test
public void testPasswordEquality() {
for (String password : INTERNAL_PASSWORDS) {
try {
String hash = method.getHash(password, getSalt(method), USERNAME);
assertTrue("Generated hash for '" + password + "' should match password (hash = '" + hash + "')",
method.comparePassword(hash, password, USERNAME));
if (!password.equals(password.toLowerCase())) {
assertFalse("Lower-case of '" + password + "' should not match generated hash '" + hash + "'",
method.comparePassword(hash, password.toLowerCase(), USERNAME));
}
if (!password.equals(password.toUpperCase())) {
assertFalse("Upper-case of '" + password + "' should not match generated hash '" + hash + "'",
method.comparePassword(hash, password.toUpperCase(), USERNAME));
}
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("EncryptionMethod '" + method + "' threw exception", e);
}
}
}
static void generateHashes(EncryptionMethod method) {
System.out.println("AbstractEncryptionMethodTest.testGivenPasswords(method,");
for (String password : GIVEN_PASSWORDS) {
try {
System.out.println("\t\"" + method.getHash(password, getSalt(method), "USERNAME")
+ "\", // " + password);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Could not generate hash", e);
}
}
System.out.println(");");
}
// TODO #358: Remove this method and use the new salt method on the interface
private static String getSalt(EncryptionMethod method) {
try {
if (method instanceof BCRYPT) {
return BCRYPT.gensalt();
} else if (method instanceof MD5) {
return "";
} else if (method instanceof JOOMLA) {
return PasswordSecurity.createSalt(32);
} else if (method instanceof SHA256) {
return PasswordSecurity.createSalt(16);
}
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
throw new RuntimeException("Unknown EncryptionMethod for salt generation");
}
}

View File

@ -0,0 +1,17 @@
package fr.xephi.authme.security.crypts;
/**
* Test for {@link BCRYPT}.
*/
public class BcryptTest extends AbstractEncryptionMethodTest {
public BcryptTest() {
super(new BCRYPT(),
"$2a$10$6iATmYgwJVc3YONhVcZFve3Cfb5GnwvKhJ20r.hMjmcNkIT9.Uh9K", // password
"$2a$10$LOhUxhEcS0vgDPv/jkXvCurNb7LjP9xUlEolJGk.Uhgikqc6FtIOi", // PassWord1
"$2a$10$j9da7SGiaakWhzIms9BtwemLUeIhSEphGUQ3XSlvYgpYsGnGCKRBa", // &^%te$t?Pw@_
"$2a$10$mkmO3SNzQT/SA5fG/8P8PePz/DI/kKpIH8vd1Owf/fQfFu6F0QyWO" // âË_3(íù*
);
}
}

View File

@ -0,0 +1,17 @@
package fr.xephi.authme.security.crypts;
/**
* Test for {@link JOOMLA}.
*/
public class JoomlaTest extends AbstractEncryptionMethodTest {
public JoomlaTest() {
super(new JOOMLA(),
"b18c99813cd96df3a706652f47177490:377c4aaf92c5ed57711306909e6065ca", // password
"c5af71da91a8841d95937ba24a5b7fdb:07068e5850930b794526a614438cafc7", // PassWord1
"f5fccd5166af7080833d7c7a6a531295:7cb6eeabcfac67ffe1341ec43375a9e6", // &^%te$t?Pw@_
"dce946c6864d2223caeed9d80f356bcc:0c55fa3eca8c42557a989700ac1c4b8e" // âË_3(íù*
);
}
}

View File

@ -0,0 +1,17 @@
package fr.xephi.authme.security.crypts;
/**
* Test for {@link MD5}.
*/
public class Md5Test extends AbstractEncryptionMethodTest {
public Md5Test() {
super(new MD5(),
"5f4dcc3b5aa765d61d8327deb882cf99", // password
"f2126d405f46ed603ff5b2950f062c96", // PassWord1
"0833dcd2bc741f90c46bbac5498fd08f", // &^%te$t?Pw@_
"d1accd961cb7b688c87278191c1dfed3" // âË_3(íù*
);
}
}

View File

@ -0,0 +1,17 @@
package fr.xephi.authme.security.crypts;
/**
* Test for {@link SHA256}.
*/
public class Sha256Test extends AbstractEncryptionMethodTest {
public Sha256Test() {
super(new SHA256(),
"$SHA$11aa0706173d7272$dbba96681c2ae4e0bfdf226d70fbbc5e4ee3d8071faa613bc533fe8a64817d10", // password
"$SHA$3c72a18a29b08d40$8e50a7a4f69a80f4893dc921eac84bd74b3f9ebfa22908302c9965eac3aa45e5", // PassWord1
"$SHA$584cea1cfab90030$adc006330e73d81e463fe02a4fe9b17bdbbcc05955bff72fb27cf2089f0b3859", // &^%te$t?Pw@_
"$SHA$0b503d90dd9949d4$ba70c330242e0daa9a154ec9f4cce7f01dd05aff489d37c653e36a507c74d84f" // âË_3(íù*
);
}
}