Use RandomString for IPB4 implementation; minor documentation

- Improve RandomString and create new generateLowerUpper method
- Add documentation to the IPB4 class to explain why the salt is stored twice
This commit is contained in:
ljacqu 2016-02-10 21:16:12 +01:00
parent ee962bce11
commit b8e2f5fe1d
4 changed files with 46 additions and 26 deletions

View File

@ -8,18 +8,10 @@ import java.util.Random;
*/ */
public final class RandomString { 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 Random RANDOM = new SecureRandom();
private static final int HEX_MAX_INDEX = 16; private static final int HEX_MAX_INDEX = 16;
private static final int LOWER_ALPHANUMERIC_INDEX = 36;
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 RandomString() { private RandomString() {
} }
@ -31,7 +23,7 @@ public final class RandomString {
* @return The random string * @return The random string
*/ */
public static String generate(int length) { 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); 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) { private static String generate(int length, int maxIndex) {
if (length < 0) { if (length < 0) {
throw new IllegalArgumentException("Length must be positive but was " + length); throw new IllegalArgumentException("Length must be positive but was " + length);
} }
StringBuilder sb = new StringBuilder(length); StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
sb.append(chars[RANDOM.nextInt(maxIndex)]); sb.append(CHARS.charAt(RANDOM.nextInt(maxIndex)));
} }
return sb.toString(); return sb.toString();
} }

View File

@ -1,19 +1,24 @@
package fr.xephi.authme.security.crypts; package fr.xephi.authme.security.crypts;
import fr.xephi.authme.ConsoleLogger; 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.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType; import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage; import fr.xephi.authme.security.crypts.description.Usage;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
import java.security.SecureRandom;
/**
* Implementation for IPB4 (Invision Power Board 4).
* <p>
* The hash uses standard BCrypt with 13 as log<sub>2</sub> 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) @Recommendation(Usage.DOES_NOT_WORK)
@HasSalt(value = SaltType.TEXT) @HasSalt(value = SaltType.TEXT, length = 22)
public class IPB4 implements EncryptionMethod { public class IPB4 implements EncryptionMethod {
private SecureRandom random = new SecureRandom();
@Override @Override
public String computeHash(String password, String salt, String name) { public String computeHash(String password, String salt, String name) {
@ -38,16 +43,7 @@ public class IPB4 implements EncryptionMethod {
@Override @Override
public String generateSalt() { public String generateSalt() {
StringBuilder sb = new StringBuilder(22); return RandomString.generateLowerUpper(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();
} }
@Override @Override

View File

@ -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) @Test(expected = IllegalArgumentException.class)
public void shouldThrowForInvalidLength() { public void shouldThrowForInvalidLength() {
// given/when // given/when

View File

@ -1,13 +1,18 @@
package fr.xephi.authme.security.crypts; package fr.xephi.authme.security.crypts;
import fr.xephi.authme.ConsoleLoggerTestInitializer;
import fr.xephi.authme.util.WrapperMock; import fr.xephi.authme.util.WrapperMock;
import org.junit.BeforeClass; import org.junit.BeforeClass;
/**
* Test for {@link IPB4}.
*/
public class IPB4Test extends AbstractEncryptionMethodTest { public class IPB4Test extends AbstractEncryptionMethodTest {
@BeforeClass @BeforeClass
public static void setUpSettings() { public static void setUpSettings() {
WrapperMock.createInstance(); WrapperMock.createInstance();
ConsoleLoggerTestInitializer.setupLogger();
} }
public IPB4Test() { public IPB4Test() {