diff --git a/CHANGELOG b/CHANGELOG index d20c9f1..88d9d77 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,10 +4,13 @@ * fix license headers and correct credits to jBcrypt * add long-password strategy to verifier #21 * fix not returning correct hash version when verifying #24 +* allow for custom max password length in Version #22 ### Breaking Changes * `verify(byte[] password, int cost, byte[] salt, byte[] rawBcryptHash23Bytes)` signature changed, added `version` property (see #24) +* `LongPasswordStrategies` factory methods now require the version for the max password length (see #22) +* Verifier now accepts `Version` as a constructor parameter and `verifyStrict` therefore does not need one (see #22) ## v0.8.0 diff --git a/README.md b/README.md index 9bb2308..a8ed14b 100644 --- a/README.md +++ b/README.md @@ -86,8 +86,8 @@ usually you should prefer `char[]` or `byte[]` APIs. If you want the hash verification to only verify for a specific version you can use `verifyStrict()` ```java -byte[] hash2y = BCrypt.with(BCrypt.Version.VERSION_2Y).hash(6, password.getBytes(StandardCharsets.UTF_8)); -BCrypt.Result resultStrict = BCrypt.verifyer().verifyStrict(password.getBytes(StandardCharsets.UTF_8), hash2y, BCrypt.Version.VERSION_2A); +byte[] hash2y = BCrypt.with(BCrypt.Version.VERSION_2Y).hash(6, password.getBytes(StandardCharsets.UTF_8)); +BCrypt.Result resultStrict = BCrypt.verifyer(BCrypt.Version.VERSION_2A).verifyStrict(password.getBytes(StandardCharsets.UTF_8), hash2y); // resultStrict.verified == false ``` diff --git a/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCrypt.java b/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCrypt.java index 998fe20..d1b3206 100644 --- a/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCrypt.java +++ b/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCrypt.java @@ -26,11 +26,6 @@ public final class BCrypt { */ public static final int SALT_LENGTH = 16; - /** - * The max length of the password in bytes excluding lats null-terminator byte - */ - public static final int MAX_PW_LENGTH_BYTE = 71; - /** * Minimum allowed cost factor */ @@ -60,41 +55,41 @@ private BCrypt() { /** * Create a new instance of bcrypt hash with default version {@link Version#VERSION_2A}. - * Will throw an exception if given password is longer than the max length support for bycrpt of {@link #MAX_PW_LENGTH_BYTE}. + * Will throw an exception if given password is longer than the max length support for bycrpt of {@link Version#allowedMaxPwLength}. * * @return new bcrypt hash instance */ public static Hasher withDefaults() { - return new Hasher(Version.VERSION_2A, new SecureRandom(), new LongPasswordStrategy.StrictMaxPasswordLengthStrategy(MAX_PW_LENGTH_BYTE)); + return new Hasher(Version.VERSION_2A, new SecureRandom(), LongPasswordStrategies.strict(Version.VERSION_2A)); } /** * Create a new instance of bcrypt hash with given {@link Version}. - * Will throw an exception if given password is longer than the max length support for bycrpt of {@link #MAX_PW_LENGTH_BYTE}. + * Will throw an exception if given password is longer than the max length support for bycrpt of {@link Version#allowedMaxPwLength}. * * @param version defines what version of bcrypt will be generated (mostly the version identifier changes) * @return new bcrypt hash instance */ public static Hasher with(Version version) { - return new Hasher(version, new SecureRandom(), new LongPasswordStrategy.StrictMaxPasswordLengthStrategy(MAX_PW_LENGTH_BYTE)); + return new Hasher(version, new SecureRandom(), LongPasswordStrategies.strict(version)); } /** * Create a new instance of bcrypt hash with default version {@link Version#VERSION_2A}. * The passed {@link SecureRandom} is used for generating the random salt. - * Will throw an exception if given password is longer than the max length support for bycrpt of {@link #MAX_PW_LENGTH_BYTE}. + * Will throw an exception if given password is longer than the max length support for bycrpt of {@link Version#allowedMaxPwLength}. * * @param secureRandom to use for random salt generation * @return new bcrypt hash instance */ public static Hasher with(SecureRandom secureRandom) { - return new Hasher(Version.VERSION_2A, secureRandom, new LongPasswordStrategy.StrictMaxPasswordLengthStrategy(MAX_PW_LENGTH_BYTE)); + return new Hasher(Version.VERSION_2A, secureRandom, LongPasswordStrategies.strict(Version.VERSION_2A)); } /** * Create a new instance of bcrypt hash with default version {@link Version#VERSION_2A}. * The passed {@link LongPasswordStrategy} will decide what to do when the password is longer than the supported - * {@link #MAX_PW_LENGTH_BYTE} + * {@link Version#allowedMaxPwLength} * * @param longPasswordStrategy decides what to do on pw that are too long * @return new bcrypt hash instance @@ -103,6 +98,17 @@ public static Hasher with(LongPasswordStrategy longPasswordStrategy) { return new Hasher(Version.VERSION_2A, new SecureRandom(), longPasswordStrategy); } + /** + * Create a new instance with custom version and long password strategy + * + * @param version defines what version of bcrypt will be generated (mostly the version identifier changes) + * @param longPasswordStrategy decides what to do on pw that are too long + * @return new bcrypt hash instance + */ + public static Hasher with(Version version, LongPasswordStrategy longPasswordStrategy) { + return new Hasher(version, new SecureRandom(), longPasswordStrategy); + } + /** * Create a new instance with custom version, secureRandom and long password strategy * @@ -116,12 +122,22 @@ public static Hasher with(Version version, SecureRandom secureRandom, LongPasswo } /** - * Creates a new instance of bcrypt verifier to verify a password against a given hash + * Creates a new instance of bcrypt verifier to verify a password against a given hash. * * @return new verifier instance */ public static Verifyer verifyer() { - return verifyer(LongPasswordStrategies.none()); + return verifyer(null, null); + } + + /** + * Creates a new instance of bcrypt verifier to verify a password against a given hash. + * + * @param version to use, also matters in {@link Verifyer#verifyStrict(byte[], byte[])} + * @return new verifier instance + */ + public static Verifyer verifyer(Version version) { + return new Verifyer(version, LongPasswordStrategies.strict(version)); } /** @@ -129,11 +145,12 @@ public static Verifyer verifyer() { * This verify also respects the passed {@link LongPasswordStrategy} for creating the reference hash - use this * if you use one while hashing. * + * @param version to use, also matters in {@link Verifyer#verifyStrict(byte[], byte[])} * @param longPasswordStrategy used to create the reference hash. * @return new verifier instance */ - public static Verifyer verifyer(LongPasswordStrategy longPasswordStrategy) { - return new Verifyer(longPasswordStrategy); + public static Verifyer verifyer(Version version, LongPasswordStrategy longPasswordStrategy) { + return new Verifyer(version, longPasswordStrategy); } /** @@ -281,7 +298,7 @@ public HashData hashRaw(int cost, byte[] salt, byte[] password) { throw new IllegalArgumentException("provided password must at least be length 1 if no null terminator is appended"); } - if (password.length > MAX_PW_LENGTH_BYTE + (version.appendNullTerminator ? 0 : 1)) { + if (password.length > version.allowedMaxPwLength + (version.appendNullTerminator ? 0 : 1)) { password = longPasswordStrategy.derive(password); } @@ -377,8 +394,10 @@ public String toString() { public static final class Verifyer { private final Charset defaultCharset = DEFAULT_CHARSET; private final LongPasswordStrategy longPasswordStrategy; + private final Version version; - private Verifyer(LongPasswordStrategy longPasswordStrategy) { + private Verifyer(Version version, LongPasswordStrategy longPasswordStrategy) { + this.version = version; this.longPasswordStrategy = longPasswordStrategy; } @@ -388,14 +407,15 @@ private Verifyer(LongPasswordStrategy longPasswordStrategy) { *
* If given hash has an invalid format {@link Result#validFormat} will be false; see also * {@link Result#formatErrorMessage} for easier debugging. + *
+ * Using the strict method will also require the version identifier to match, matching hash does not suffice. * - * @param password to compare against the hash - * @param bcryptHash to compare against the password - * @param expectedVersion will check for this version and wil not verify if versions do not match + * @param password to compare against the hash + * @param bcryptHash to compare against the password * @return result object, see {@link Result} for more info */ - public Result verifyStrict(byte[] password, byte[] bcryptHash, Version expectedVersion) { - return verify(password, bcryptHash, expectedVersion); + public Result verifyStrict(byte[] password, byte[] bcryptHash) { + return innerVerifyBytes(password, bcryptHash, true); } /** @@ -409,7 +429,7 @@ public Result verifyStrict(byte[] password, byte[] bcryptHash, Version expectedV * @return result object, see {@link Result} for more info */ public Result verify(byte[] password, byte[] bcryptHash) { - return verify(password, bcryptHash, null); + return innerVerifyBytes(password, bcryptHash, false); } /** @@ -418,14 +438,15 @@ public Result verify(byte[] password, byte[] bcryptHash) { *
* If given hash has an invalid format {@link Result#validFormat} will be false; see also * {@link Result#formatErrorMessage} for easier debugging. + *
+ * Using the strict method will also require the version identifier to match, matching hash does not suffice. * - * @param password to compare against the hash - * @param bcryptHash to compare against the password - * @param expectedVersion will check for this version and wil not verify if versions do not match + * @param password to compare against the hash + * @param bcryptHash to compare against the password * @return result object, see {@link Result} for more info */ - public Result verifyStrict(char[] password, char[] bcryptHash, Version expectedVersion) { - return verify(password, bcryptHash, expectedVersion); + public Result verifyStrict(char[] password, char[] bcryptHash) { + return innerVerifyChar(password, bcryptHash, true); } /** @@ -439,7 +460,7 @@ public Result verifyStrict(char[] password, char[] bcryptHash, Version expectedV * @return result object, see {@link Result} for more info */ public Result verify(char[] password, char[] bcryptHash) { - return verify(password, bcryptHash, null); + return innerVerifyChar(password, bcryptHash, false); } /** @@ -455,7 +476,7 @@ public Result verify(char[] password, char[] bcryptHash) { * @return result object, see {@link Result} for more info */ public Result verify(char[] password, CharSequence bcryptHash) { - return verify(password, toCharArray(bcryptHash), null); + return innerVerifyChar(password, toCharArray(bcryptHash), false); } /** @@ -474,10 +495,13 @@ public Result verify(char[] password, CharSequence bcryptHash) { */ public Result verify(char[] password, byte[] bcryptHash) { try (MutableBytes pw = Bytes.from(password, defaultCharset).mutable()) { - return verify(pw.array(), bcryptHash, null); + return innerVerifyBytes(pw.array(), bcryptHash, false); } } + /** + * Convert a string type to char array in the most efficient manner. + */ private static char[] toCharArray(CharSequence charSequence) { if (charSequence instanceof String) { return charSequence.toString().toCharArray(); @@ -490,13 +514,16 @@ private static char[] toCharArray(CharSequence charSequence) { } } - private Result verify(char[] password, char[] bcryptHash, Version requiredVersion) { + /** + * Verify given password against a bcryptHash with char types + */ + private Result innerVerifyChar(char[] password, char[] bcryptHash, boolean strict) { byte[] passwordBytes = null; byte[] bcryptHashBytes = null; try { passwordBytes = Bytes.from(password, defaultCharset).array(); bcryptHashBytes = Bytes.from(bcryptHash, defaultCharset).array(); - return verify(passwordBytes, bcryptHashBytes, requiredVersion); + return innerVerifyBytes(passwordBytes, bcryptHashBytes, strict); } finally { Bytes.wrapNullSafe(passwordBytes).mutable().secureWipe(); Bytes.wrapNullSafe(bcryptHashBytes).mutable().secureWipe(); @@ -504,25 +531,49 @@ private Result verify(char[] password, char[] bcryptHash, Version requiredVersio } /** - * Verify given password against a bcryptHash + * Verify given password against a bcryptHash with byte types */ - private Result verify(byte[] password, byte[] bcryptHash, Version requiredVersion) { + private Result innerVerifyBytes(byte[] password, byte[] bcryptHash, boolean strict) { Objects.requireNonNull(bcryptHash); - BCryptParser parser = requiredVersion == null ? Version.VERSION_2A.parser : requiredVersion.parser; try { - HashData hashData = parser.parse(bcryptHash); + final Version usedVersion; + final HashData hashData; + + if (this.version == null) { + hashData = Version.VERSION_2A.parser.parse(bcryptHash); + usedVersion = hashData.version; + } else { + usedVersion = this.version; + hashData = usedVersion.parser.parse(bcryptHash); + } - if (requiredVersion != null && hashData.version != requiredVersion) { - return new Result(hashData, false); + if (strict) { + if (this.version == null) { + throw new IllegalArgumentException("Using strict requires to define a Version. " + + "Try 'BCrypt.verifier(Version.VERSION_2A)'."); + } + if (hashData.version != this.version) { + return new Result(hashData, false); + } } - return verify(password, hashData.cost, hashData.rawSalt, hashData.rawHash, hashData.version); + return verifyBCrypt(usedVersion, determinePasswordStrategy(usedVersion), password, hashData.cost, hashData.rawSalt, hashData.rawHash); } catch (IllegalBCryptFormatException e) { return new Result(e); } } + private LongPasswordStrategy determinePasswordStrategy(Version usedVersion) { + LongPasswordStrategy usedLongPasswordStrategy; + if (this.longPasswordStrategy == null) { + usedLongPasswordStrategy = LongPasswordStrategies.strict(usedVersion); + } else { + usedLongPasswordStrategy = this.longPasswordStrategy; + } + return usedLongPasswordStrategy; + } + /** * Verify given raw byte arrays of salt, 23 byte bcrypt hash and password. This is handy if the bcrypt messages are not packaged * in the default Modular Crypt Format (see also {@link Hasher#hashRaw(int, byte[], byte[])}. @@ -538,7 +589,7 @@ private Result verify(byte[] password, byte[] bcryptHash, Version requiredVersio * @return result object, see {@link Result} for more info */ public Result verify(byte[] password, HashData bcryptHashData) { - return verify(password, bcryptHashData.cost, bcryptHashData.rawSalt, bcryptHashData.rawHash, bcryptHashData.version); + return verify(password, bcryptHashData.cost, bcryptHashData.rawSalt, bcryptHashData.rawHash); } /** @@ -555,16 +606,21 @@ public Result verify(byte[] password, HashData bcryptHashData) { * @param cost cost (log2 factor) which was used to create the hash * @param salt 16 byte raw hash value (not radix64 version) which was used to create the hash * @param rawBcryptHash23Bytes 23 byte raw bcrypt hash value (not radix64 version) - * @param version the version of the provided hash * @return result object, see {@link Result} for more info */ - public Result verify(byte[] password, int cost, byte[] salt, byte[] rawBcryptHash23Bytes, Version version) { - Objects.requireNonNull(password); - Objects.requireNonNull(rawBcryptHash23Bytes); - Objects.requireNonNull(salt); + public Result verify(byte[] password, int cost, byte[] salt, byte[] rawBcryptHash23Bytes) { + Version usedVersion = this.version == null ? Version.VERSION_2A : this.version; + return verifyBCrypt(usedVersion, determinePasswordStrategy(usedVersion), password, cost, salt, rawBcryptHash23Bytes); + } - HashData hashData = BCrypt.with(version, new SecureRandom(), longPasswordStrategy).hashRaw(cost, salt, password); - return new Result(hashData, Bytes.wrap(hashData.rawHash).equalsConstantTime(rawBcryptHash23Bytes)); + /** + * Raw bcrypt verification + */ + private static Result verifyBCrypt(Version version, LongPasswordStrategy longPasswordStrategy, + byte[] password, int cost, byte[] salt, byte[] rawBcryptHash23Bytes) { + HashData hashData = BCrypt.with(Objects.requireNonNull(version), Objects.requireNonNull(longPasswordStrategy)) + .hashRaw(cost, Objects.requireNonNull(salt), Objects.requireNonNull(password)); + return new Result(hashData, Bytes.wrap(hashData.rawHash).equalsConstantTime(Objects.requireNonNull(rawBcryptHash23Bytes))); } } @@ -643,6 +699,16 @@ public static final class Version { private static final BCryptFormatter DEFAULT_FORMATTER = new BCryptFormatter.Default(new Radix64Encoder.Default(), BCrypt.DEFAULT_CHARSET); private static final BCryptParser DEFAULT_PARSER = new BCryptParser.Default(new Radix64Encoder.Default(), BCrypt.DEFAULT_CHARSET); + /** + * Absolutely maximum length bcrypt can support (18x32bit) + */ + public static final int MAX_PW_LENGTH_BYTE = 72; + + /** + * The max length of the password in bytes excluding lats null-terminator byte + */ + public static final int DEFAULT_MAX_PW_LENGTH_BYTE = MAX_PW_LENGTH_BYTE - 1; + /** * $2a$ *
@@ -681,11 +747,18 @@ public static final class Version { */ public static final Version VERSION_2Y = new Version(new byte[]{MAJOR_VERSION, 0x79}, DEFAULT_FORMATTER, DEFAULT_PARSER); + /** + * $2y$ (2011) without the null terminator + *
+ * See {@link #VERSION_2Y}
+ */
+ public static final Version VERSION_2Y_NO_NULL_TERMINATOR = new Version(new byte[]{MAJOR_VERSION, 0x79}, true, false, MAX_PW_LENGTH_BYTE, DEFAULT_FORMATTER, DEFAULT_PARSER);
+
/**
* This mirrors how Bouncy Castle creates bcrypt hashes: with 24 byte out and without null-terminator. Gets a fake
* version descriptor.
*/
- public static final Version VERSION_BC = new Version(new byte[]{MAJOR_VERSION, 0x63}, false, false, DEFAULT_FORMATTER, DEFAULT_PARSER);
+ public static final Version VERSION_BC = new Version(new byte[]{MAJOR_VERSION, 0x63}, false, false, DEFAULT_MAX_PW_LENGTH_BYTE, DEFAULT_FORMATTER, DEFAULT_PARSER);
/**
* List of supported versions
@@ -709,6 +782,13 @@ public static final class Version {
*/
public final boolean appendNullTerminator;
+ /**
+ * The max allowed length of password in bcrypt, longer than that {@link LongPasswordStrategy} will be activated.
+ * Usual lengths are between 50 and 72 bytes, most often are 56, 71 or 72 bytes.
+ * See https://security.stackexchange.com/a/39851
+ */
+ public final int allowedMaxPwLength;
+
/**
* The formatter for the bcrypt message digest
*/
@@ -720,7 +800,7 @@ public static final class Version {
public final BCryptParser parser;
private Version(byte[] versionIdentifier, BCryptFormatter formatter, BCryptParser parser) {
- this(versionIdentifier, true, true, formatter, parser);
+ this(versionIdentifier, true, true, DEFAULT_MAX_PW_LENGTH_BYTE, formatter, parser);
}
/**
@@ -730,14 +810,20 @@ private Version(byte[] versionIdentifier, BCryptFormatter formatter, BCryptParse
* @param versionIdentifier version as UTF-8 encoded byte array, e.g. '2a' = new byte[]{0x32, 0x61}, do not included the separator '$'
* @param useOnly23bytesForHash set to false if you want the full 24 byte out for the hash (otherwise will be truncated to 23 byte according to OpenBSD impl)
* @param appendNullTerminator as defined in $2a$+ a null terminator is appended to the password, pass false if you want avoid this
+ * @param allowedMaxPwLength the max allowed length of password in bcrypt, longer than that {@link LongPasswordStrategy} will be activated
* @param formatter the formatter responsible for formatting the out hash message digest
*/
- public Version(byte[] versionIdentifier, boolean useOnly23bytesForHash, boolean appendNullTerminator, BCryptFormatter formatter, BCryptParser parser) {
+ public Version(byte[] versionIdentifier, boolean useOnly23bytesForHash, boolean appendNullTerminator, int allowedMaxPwLength, BCryptFormatter formatter, BCryptParser parser) {
this.versionIdentifier = versionIdentifier;
this.useOnly23bytesForHash = useOnly23bytesForHash;
this.appendNullTerminator = appendNullTerminator;
+ this.allowedMaxPwLength = allowedMaxPwLength;
this.formatter = formatter;
this.parser = parser;
+
+ if (allowedMaxPwLength > MAX_PW_LENGTH_BYTE) {
+ throw new IllegalArgumentException("allowed max pw length cannot be gt " + MAX_PW_LENGTH_BYTE);
+ }
}
@Override
@@ -747,13 +833,13 @@ public boolean equals(Object o) {
Version version = (Version) o;
return useOnly23bytesForHash == version.useOnly23bytesForHash &&
appendNullTerminator == version.appendNullTerminator &&
+ allowedMaxPwLength == version.allowedMaxPwLength &&
Arrays.equals(versionIdentifier, version.versionIdentifier);
}
@Override
public int hashCode() {
-
- int result = Objects.hash(useOnly23bytesForHash, appendNullTerminator);
+ int result = Objects.hash(useOnly23bytesForHash, appendNullTerminator, allowedMaxPwLength);
result = 31 * result + Arrays.hashCode(versionIdentifier);
return result;
}
diff --git a/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCryptParser.java b/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCryptParser.java
index b90f82b..d82fa4a 100644
--- a/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCryptParser.java
+++ b/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCryptParser.java
@@ -90,7 +90,7 @@ public BCrypt.HashData parse(byte[] bcryptHash) throws IllegalBCryptFormatExcept
int parsedCostFactor;
try {
- parsedCostFactor = Integer.valueOf(new String(costBytes, defaultCharset));
+ parsedCostFactor = Integer.parseInt(new String(costBytes, defaultCharset));
} catch (NumberFormatException e) {
throw new IllegalBCryptFormatException("cannot parse cost factor '" + new String(costBytes, defaultCharset) + "'");
}
diff --git a/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/LongPasswordStrategies.java b/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/LongPasswordStrategies.java
index 83b6931..b440fc1 100644
--- a/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/LongPasswordStrategies.java
+++ b/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/LongPasswordStrategies.java
@@ -1,5 +1,7 @@
package at.favre.lib.crypto.bcrypt;
+import java.util.Objects;
+
/**
* Factory for default {@link LongPasswordStrategy} implementatins
*/
@@ -11,28 +13,31 @@ private LongPasswordStrategies() {
/**
* See {@link at.favre.lib.crypto.bcrypt.LongPasswordStrategy.TruncateStrategy}
*
+ * @param version required to get the max allowed pw length
* @return new instance
*/
- public static LongPasswordStrategy truncate() {
- return new LongPasswordStrategy.TruncateStrategy(BCrypt.MAX_PW_LENGTH_BYTE);
+ public static LongPasswordStrategy truncate(BCrypt.Version version) {
+ return new LongPasswordStrategy.TruncateStrategy(Objects.requireNonNull(version).allowedMaxPwLength);
}
/**
* See {@link at.favre.lib.crypto.bcrypt.LongPasswordStrategy.Sha512DerivationStrategy}
*
+ * @param version required to get the max allowed pw length
* @return new instance
*/
- public static LongPasswordStrategy hashSha512() {
- return new LongPasswordStrategy.Sha512DerivationStrategy(BCrypt.MAX_PW_LENGTH_BYTE);
+ public static LongPasswordStrategy hashSha512(BCrypt.Version version) {
+ return new LongPasswordStrategy.Sha512DerivationStrategy(Objects.requireNonNull(version).allowedMaxPwLength);
}
/**
* See {@link at.favre.lib.crypto.bcrypt.LongPasswordStrategy.StrictMaxPasswordLengthStrategy}
*
+ * @param version required to get the max allowed pw length
* @return new instance
*/
- public static LongPasswordStrategy strict() {
- return new LongPasswordStrategy.StrictMaxPasswordLengthStrategy(BCrypt.MAX_PW_LENGTH_BYTE);
+ public static LongPasswordStrategy strict(BCrypt.Version version) {
+ return new LongPasswordStrategy.StrictMaxPasswordLengthStrategy(Objects.requireNonNull(version).allowedMaxPwLength);
}
/**
diff --git a/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BcryptTest.java b/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BcryptTest.java
index 31b7d8d..1978daa 100644
--- a/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BcryptTest.java
+++ b/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BcryptTest.java
@@ -23,7 +23,8 @@
public class BcryptTest {
@Rule
public RepeatRule repeatRule = new RepeatRule();
- public static final Charset UTF_8 = StandardCharsets.UTF_8;
+ static final Charset UTF_8 = StandardCharsets.UTF_8;
+ private static final BCrypt.Version DEFAULT_VERSION = BCrypt.Version.VERSION_2A;
private BcryptTestEntry[] testEntries = new BcryptTestEntry[]{
// see: https://stackoverflow.com/a/12761326/774398
@@ -85,10 +86,10 @@ public void readmeExamples() {
BCrypt.Result result = BCrypt.verifyer().verify(password.getBytes(StandardCharsets.UTF_8), bcryptHashBytes);
//verify strict
byte[] hash2y = BCrypt.with(BCrypt.Version.VERSION_2Y).hash(6, password.getBytes(StandardCharsets.UTF_8));
- BCrypt.Result resultStrict = BCrypt.verifyer().verifyStrict(password.getBytes(StandardCharsets.UTF_8), hash2y, BCrypt.Version.VERSION_2A);
+ BCrypt.Result resultStrict = BCrypt.verifyer(BCrypt.Version.VERSION_2A).verifyStrict(password.getBytes(StandardCharsets.UTF_8), hash2y);
//overlong passwords
- BCrypt.with(LongPasswordStrategies.truncate()).hash(6, new byte[100]);
- BCrypt.with(LongPasswordStrategies.hashSha512()).hash(6, new byte[100]);
+ BCrypt.with(LongPasswordStrategies.truncate(BCrypt.Version.VERSION_2Y)).hash(6, new byte[100]);
+ BCrypt.with(LongPasswordStrategies.hashSha512(BCrypt.Version.VERSION_2Y)).hash(6, new byte[100]);
//custom salt and secure random
BCrypt.withDefaults().hash(6, Bytes.random(16).array(), password.getBytes(StandardCharsets.UTF_8));
BCrypt.with(new SecureRandom()).hash(6, password.getBytes(StandardCharsets.UTF_8));
@@ -119,12 +120,12 @@ public void testSecureRandom() throws Exception {
@Test
public void testLongPasswordStrategy() throws Exception {
- checkHash(BCrypt.with(new LongPasswordStrategy.TruncateStrategy(BCrypt.MAX_PW_LENGTH_BYTE)));
+ checkHash(BCrypt.with(new LongPasswordStrategy.TruncateStrategy(DEFAULT_VERSION.allowedMaxPwLength)));
}
@Test
public void testFullyCustom() throws Exception {
- checkHash(BCrypt.with(BCrypt.Version.VERSION_2Y, new SecureRandom(), new LongPasswordStrategy.TruncateStrategy(BCrypt.MAX_PW_LENGTH_BYTE)));
+ checkHash(BCrypt.with(BCrypt.Version.VERSION_2Y, new LongPasswordStrategy.TruncateStrategy(BCrypt.Version.VERSION_2Y.allowedMaxPwLength)));
}
private void checkHash(BCrypt.Hasher bCrypt) throws Exception {
@@ -206,28 +207,28 @@ public void createHashWithCharPwNull() {
@Test(expected = IllegalArgumentException.class)
public void createHashWithPwTooLong() {
- BCrypt.withDefaults().hash(6, new byte[16], new byte[BCrypt.MAX_PW_LENGTH_BYTE + 1]);
+ BCrypt.withDefaults().hash(6, new byte[16], new byte[DEFAULT_VERSION.allowedMaxPwLength + 1]);
}
@Test(expected = IllegalArgumentException.class)
public void createHashWithPwTooLong2() {
- BCrypt.withDefaults().hash(6, new byte[16], new byte[BCrypt.MAX_PW_LENGTH_BYTE + 2]);
+ BCrypt.withDefaults().hash(6, new byte[16], new byte[DEFAULT_VERSION.allowedMaxPwLength + 2]);
}
@Test
public void testLongPassword() {
- byte[] pw = Bytes.random(BCrypt.MAX_PW_LENGTH_BYTE).array();
+ byte[] pw = Bytes.random(DEFAULT_VERSION.allowedMaxPwLength).array();
byte[] bcryptHashBytes = BCrypt.withDefaults().hash(4, pw);
assertTrue(BCrypt.verifyer().verify(pw, bcryptHashBytes).verified);
}
@Test
public void testLongTruncatedPassword() {
- byte[] pw = Bytes.random(BCrypt.MAX_PW_LENGTH_BYTE + 2).array();
+ byte[] pw = Bytes.random(DEFAULT_VERSION.allowedMaxPwLength + 2).array();
byte[] salt = Bytes.random(16).array();
- byte[] bcryptHashBytes1a = BCrypt.with(LongPasswordStrategies.truncate()).hash(4, salt, pw);
- byte[] bcryptHashBytes1b = BCrypt.with(LongPasswordStrategies.truncate()).hash(4, salt, Bytes.wrap(pw).resize(BCrypt.MAX_PW_LENGTH_BYTE + 1, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array());
- byte[] bcryptHashBytes2 = BCrypt.withDefaults().hash(4, salt, Bytes.wrap(pw).resize(BCrypt.MAX_PW_LENGTH_BYTE, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array());
+ byte[] bcryptHashBytes1a = BCrypt.with(LongPasswordStrategies.truncate(DEFAULT_VERSION)).hash(4, salt, pw);
+ byte[] bcryptHashBytes1b = BCrypt.with(LongPasswordStrategies.truncate(DEFAULT_VERSION)).hash(4, salt, Bytes.wrap(pw).resize(DEFAULT_VERSION.allowedMaxPwLength + 1, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array());
+ byte[] bcryptHashBytes2 = BCrypt.withDefaults().hash(4, salt, Bytes.wrap(pw).resize(DEFAULT_VERSION.allowedMaxPwLength, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array());
assertArrayEquals(bcryptHashBytes1a, bcryptHashBytes1b);
assertArrayEquals(bcryptHashBytes1a, bcryptHashBytes2);
@@ -240,7 +241,7 @@ public void testVariousPwLengthShouldBeDifferentHashes() {
Set