diff --git a/src/main/java/fr/xephi/authme/security/RandomString.java b/src/main/java/fr/xephi/authme/security/RandomString.java index 402743053..10926e114 100644 --- a/src/main/java/fr/xephi/authme/security/RandomString.java +++ b/src/main/java/fr/xephi/authme/security/RandomString.java @@ -8,18 +8,10 @@ import java.util.Random; */ public final class RandomString { - private static final char[] chars = new char[36]; + private static final String CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static final Random RANDOM = new SecureRandom(); private static final int HEX_MAX_INDEX = 16; - - static { - for (int idx = 0; idx < 10; ++idx) { - chars[idx] = (char) ('0' + idx); - } - for (int idx = 10; idx < 36; ++idx) { - chars[idx] = (char) ('a' + idx - 10); - } - } + private static final int LOWER_ALPHANUMERIC_INDEX = 36; private RandomString() { } @@ -31,7 +23,7 @@ public final class RandomString { * @return The random string */ public static String generate(int length) { - return generate(length, chars.length); + return generate(length, LOWER_ALPHANUMERIC_INDEX); } /** @@ -45,13 +37,24 @@ public final class RandomString { return generate(length, HEX_MAX_INDEX); } + /** + * Generate a random string with digits and lowercase and uppercase letters. The result of this + * method matches the pattern [0-9a-zA-Z]. + * + * @param length The length of the random string to generate + * @return The random string + */ + public static String generateLowerUpper(int length) { + return generate(length, CHARS.length()); + } + private static String generate(int length, int maxIndex) { if (length < 0) { throw new IllegalArgumentException("Length must be positive but was " + length); } StringBuilder sb = new StringBuilder(length); for (int i = 0; i < length; ++i) { - sb.append(chars[RANDOM.nextInt(maxIndex)]); + sb.append(CHARS.charAt(RANDOM.nextInt(maxIndex))); } return sb.toString(); } diff --git a/src/main/java/fr/xephi/authme/security/crypts/IPB4.java b/src/main/java/fr/xephi/authme/security/crypts/IPB4.java index 515901bc2..e54bcd038 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/IPB4.java +++ b/src/main/java/fr/xephi/authme/security/crypts/IPB4.java @@ -1,19 +1,24 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.ConsoleLogger; +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.util.StringUtils; -import java.security.SecureRandom; - +/** + * Implementation for IPB4 (Invision Power Board 4). + *
+ * The hash uses standard BCrypt with 13 as log2 number of rounds. Additionally, + * IPB4 requires that the salt be stored additionally in the column "members_pass_hash" + * (even though BCrypt hashes already have the salt in the result). + */ @Recommendation(Usage.DOES_NOT_WORK) -@HasSalt(value = SaltType.TEXT) +@HasSalt(value = SaltType.TEXT, length = 22) public class IPB4 implements EncryptionMethod { - private SecureRandom random = new SecureRandom(); @Override public String computeHash(String password, String salt, String name) { @@ -38,16 +43,7 @@ public class IPB4 implements EncryptionMethod { @Override public String generateSalt() { - StringBuilder sb = new StringBuilder(22); - for (int i = 0; i < 22; i++) { - char chr; - do { - chr = (char) (random.nextInt((122 - 48) + 1) + 48); - } - while ((chr >= 58 && chr <= 64) || (chr >= 91 && chr <= 96)); - sb.append(chr); - } - return sb.toString(); + return RandomString.generateLowerUpper(22); } @Override diff --git a/src/test/java/fr/xephi/authme/security/RandomStringTest.java b/src/test/java/fr/xephi/authme/security/RandomStringTest.java index 938f095cd..71ea587c7 100644 --- a/src/test/java/fr/xephi/authme/security/RandomStringTest.java +++ b/src/test/java/fr/xephi/authme/security/RandomStringTest.java @@ -44,6 +44,22 @@ public class RandomStringTest { } } + @Test + public void shouldGenerateRandomLowerUpperString() { + // given + int[] lengths = {0, 1, 17, 143, 1808}; + Pattern badChars = Pattern.compile(".*[^0-9a-zA-Z].*"); + + // when / then + for (int length : lengths) { + String result = RandomString.generateHex(length); + assertThat("Result '" + result + "' should have length " + length, + result.length(), equalTo(length)); + assertThat("Result '" + result + "' should only have characters a-z, A-Z, 0-9", + badChars.matcher(result).matches(), equalTo(false)); + } + } + @Test(expected = IllegalArgumentException.class) public void shouldThrowForInvalidLength() { // given/when diff --git a/src/test/java/fr/xephi/authme/security/crypts/IPB4Test.java b/src/test/java/fr/xephi/authme/security/crypts/IPB4Test.java index 400a266b8..58c6f5704 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/IPB4Test.java +++ b/src/test/java/fr/xephi/authme/security/crypts/IPB4Test.java @@ -1,13 +1,18 @@ package fr.xephi.authme.security.crypts; +import fr.xephi.authme.ConsoleLoggerTestInitializer; import fr.xephi.authme.util.WrapperMock; import org.junit.BeforeClass; +/** + * Test for {@link IPB4}. + */ public class IPB4Test extends AbstractEncryptionMethodTest { @BeforeClass public static void setUpSettings() { WrapperMock.createInstance(); + ConsoleLoggerTestInitializer.setupLogger(); } public IPB4Test() {