Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
194 changes: 140 additions & 54 deletions modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCrypt.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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) + "'");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package at.favre.lib.crypto.bcrypt;

import java.util.Objects;

/**
* Factory for default {@link LongPasswordStrategy} implementatins
*/
Expand All @@ -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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand All @@ -240,7 +241,7 @@ public void testVariousPwLengthShouldBeDifferentHashes() {

Set<String> hashes = new HashSet<>();
for (int i = 0; i < 72; i++) {
BCrypt.HashData data = BCrypt.with(LongPasswordStrategies.truncate()).hashRaw(4, salt, pw.resize(i, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array());
BCrypt.HashData data = BCrypt.with(LongPasswordStrategies.truncate(DEFAULT_VERSION)).hashRaw(4, salt, pw.resize(i, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array());
String hashHexString = Bytes.wrap(data.rawHash).encodeHex();
assertFalse("hash already in set for length " + i, hashes.contains(hashHexString));
hashes.add(hashHexString);
Expand All @@ -249,10 +250,10 @@ public void testVariousPwLengthShouldBeDifferentHashes() {

@Test
public void testLongHashedPassword() {
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[] bcryptHashBytes1 = BCrypt.with(LongPasswordStrategies.hashSha512()).hash(4, salt, pw);
byte[] bcryptHashBytes2 = BCrypt.with(LongPasswordStrategies.hashSha512()).hash(4, salt, Bytes.wrap(pw).resize(BCrypt.MAX_PW_LENGTH_BYTE + 1, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array());
byte[] bcryptHashBytes1 = BCrypt.with(LongPasswordStrategies.hashSha512(DEFAULT_VERSION)).hash(4, salt, pw);
byte[] bcryptHashBytes2 = BCrypt.with(LongPasswordStrategies.hashSha512(DEFAULT_VERSION)).hash(4, salt, Bytes.wrap(pw).resize(DEFAULT_VERSION.allowedMaxPwLength + 1, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array());
assertFalse(Bytes.wrap(bcryptHashBytes1).equals(bcryptHashBytes2));
}

Expand Down Expand Up @@ -282,7 +283,7 @@ public void verifyRawByteArrays2() {
byte[] pw = Bytes.random(24).encodeBase36().getBytes();
BCrypt.HashData hash = bCrypt.hashRaw(7, Bytes.random(16).array(), pw);

BCrypt.Result result = BCrypt.verifyer().verify(pw, hash.cost, hash.rawSalt, hash.rawHash, hash.version);
BCrypt.Result result = BCrypt.verifyer().verify(pw, hash.cost, hash.rawSalt, hash.rawHash);
assertResult(result, true, BCrypt.Version.VERSION_2A, 7);
}

Expand All @@ -302,7 +303,7 @@ public void verifyIncorrectStrictVersion() {
byte[] pw = "78PHasdhklöALÖö".getBytes();
byte[] hash = bCrypt.hash(5, Bytes.random(16).array(), pw);

BCrypt.Result result = BCrypt.verifyer().verifyStrict(pw, hash, BCrypt.Version.VERSION_2A);
BCrypt.Result result = BCrypt.verifyer(BCrypt.Version.VERSION_2A).verifyStrict(pw, hash);
assertResult(result, false, BCrypt.Version.VERSION_2Y, 5);
}

Expand All @@ -312,7 +313,7 @@ public void verifyIncorrectStrictVersionChars() {
String pw = "8PAsdjhlkjhkjla_ääas#d";
char[] hash = bCrypt.hashToChar(5, pw.toCharArray());

BCrypt.Result result = BCrypt.verifyer().verifyStrict(pw.toCharArray(), hash, BCrypt.Version.VERSION_2A);
BCrypt.Result result = BCrypt.verifyer(BCrypt.Version.VERSION_2A).verifyStrict(pw.toCharArray(), hash);
assertResult(result, false, BCrypt.Version.VERSION_2X, 5);
}

Expand All @@ -325,9 +326,9 @@ public void verifyCorrectNonDefaultVersion() {
BCrypt.HashData hash1 = bCrypt.hashRaw(cost, Bytes.random(16).array(), Bytes.from(pw).array());
char[] hash2 = bCrypt.hashToChar(cost, pw.toCharArray());

assertResult(BCrypt.verifyer().verify(pw.toCharArray(), hash2), true, version, cost);
assertResult(BCrypt.verifyer().verifyStrict(pw.toCharArray(), hash2, version), true, version, cost);
assertResult(BCrypt.verifyer().verify(Bytes.from(pw).array(), hash1), true, version, cost);
assertResult(BCrypt.verifyer(version).verify(pw.toCharArray(), hash2), true, version, cost);
assertResult(BCrypt.verifyer(version).verifyStrict(pw.toCharArray(), hash2), true, version, cost);
assertResult(BCrypt.verifyer(version).verify(Bytes.from(pw).array(), hash1), true, version, cost);
}

private void assertResult(BCrypt.Result result, boolean verified, BCrypt.Version version, int cost) {
Expand Down Expand Up @@ -400,14 +401,15 @@ public void testHashDataWipe() {
@Test
public void testVersionPojoMethods() {
assertEquals(BCrypt.Version.VERSION_2A, BCrypt.Version.VERSION_2A);
assertEquals(BCrypt.Version.VERSION_2A, new BCrypt.Version(new byte[]{MAJOR_VERSION, 0x61}, true, true, null, null));
assertEquals(BCrypt.Version.VERSION_2Y, new BCrypt.Version(new byte[]{MAJOR_VERSION, 0x79}, true, true, null, null));
assertEquals(BCrypt.Version.VERSION_2A, new BCrypt.Version(new byte[]{MAJOR_VERSION, 0x61}, true, true, BCrypt.Version.DEFAULT_MAX_PW_LENGTH_BYTE, null, null));
assertEquals(BCrypt.Version.VERSION_2Y, new BCrypt.Version(new byte[]{MAJOR_VERSION, 0x79}, true, true, BCrypt.Version.DEFAULT_MAX_PW_LENGTH_BYTE, null, null));
assertEquals(BCrypt.Version.VERSION_2Y_NO_NULL_TERMINATOR, new BCrypt.Version(new byte[]{MAJOR_VERSION, 0x79}, true, false, BCrypt.Version.MAX_PW_LENGTH_BYTE, null, null));
assertNotEquals(BCrypt.Version.VERSION_2Y, BCrypt.Version.VERSION_2A);
assertNotEquals(BCrypt.Version.VERSION_2A, BCrypt.Version.VERSION_2B);
assertNotEquals(BCrypt.Version.VERSION_2X, BCrypt.Version.VERSION_2Y);

assertEquals(BCrypt.Version.VERSION_2A.hashCode(), BCrypt.Version.VERSION_2A.hashCode());
assertEquals(BCrypt.Version.VERSION_2A.hashCode(), new BCrypt.Version(new byte[]{MAJOR_VERSION, 0x61}, true, true, null, null).hashCode());
assertEquals(BCrypt.Version.VERSION_2A.hashCode(), new BCrypt.Version(new byte[]{MAJOR_VERSION, 0x61}, true, true, BCrypt.Version.DEFAULT_MAX_PW_LENGTH_BYTE, null, null).hashCode());

assertNotEquals(BCrypt.Version.VERSION_2Y.hashCode(), BCrypt.Version.VERSION_2A.hashCode());
assertNotEquals(BCrypt.Version.VERSION_2A.hashCode(), BCrypt.Version.VERSION_2B.hashCode());
Expand All @@ -416,12 +418,69 @@ public void testVersionPojoMethods() {

@Test
public void testVerifierWithLongPasswordStrategy() {
LongPasswordStrategy truncate = LongPasswordStrategies.truncate();
LongPasswordStrategy truncate = LongPasswordStrategies.truncate(BCrypt.Version.VERSION_2A);

byte[] pw = Bytes.random(200).array();
byte[] hash = BCrypt.with(truncate).hash(4, pw);

assertTrue(BCrypt.verifyer(truncate).verify(pw, hash).verified);
assertFalse(BCrypt.verifyer().verify(pw, hash).verified);
assertTrue(BCrypt.verifyer(BCrypt.Version.VERSION_2A, truncate).verify(pw, hash).verified);
assertFalse(BCrypt.verifyer(BCrypt.Version.VERSION_2A, LongPasswordStrategies.none()).verify(pw, hash).verified);
}

@Test
public void testWithNullTerminatorWithinPw_shouldNotTerminate() {
byte[] pw1 = Bytes.from("secret").append(0x00).append("butthereismore").array();
byte[] pw2 = Bytes.from("secret").array();

byte[] salt = Bytes.random(16).array();

String hash1 = Bytes.wrap(BCrypt.withDefaults().hash(4, salt, pw1)).toString();
String hash2 = Bytes.wrap(BCrypt.withDefaults().hash(4, salt, pw2)).toString();

assertNotEquals(hash1, hash2);
System.out.println(hash1 + "\n" + hash2);
}

@Test
public void testVersionWithNullTerminator() {
char[] pw = "myverlongpasswordthatisatleast72charslongandlongnothisisnotlongenoughyou".toCharArray();
assertEquals(72, pw.length);
assertEquals(72, Bytes.from(pw).length());

byte[] salt = Bytes.random(16).array();

byte[] hash1 = BCrypt.with(BCrypt.Version.VERSION_2Y_NO_NULL_TERMINATOR, LongPasswordStrategies.truncate(BCrypt.Version.VERSION_2Y_NO_NULL_TERMINATOR)).hash(4, salt, Bytes.from(pw).array());
byte[] hash2 = BCrypt.with(BCrypt.Version.VERSION_2Y, LongPasswordStrategies.truncate(BCrypt.Version.VERSION_2Y)).hash(4, salt, Bytes.from(pw).array());

assertNotEquals(Bytes.wrap(hash1).encodeUtf8(), Bytes.wrap(hash2).encodeUtf8());
System.out.println(Bytes.wrap(hash1).encodeUtf8() + "\n" + Bytes.wrap(hash2).encodeUtf8());
}

@Test
public void testReferenceValuesWithoutNullTerminator() {
char[] pw = "myverlongpasswordthatisatleast72charslongandlongnothisisnotlongenoughyou".toCharArray();

assertTrue(BCrypt.verifyer(BCrypt.Version.VERSION_2Y_NO_NULL_TERMINATOR).verify(pw, "$2y$04$d4CIUbwyucxm87BQnDWyI.xHDm2vyIZfBDOzjASNkn/yB.6lzLwOG".toCharArray()).verified);
assertTrue(BCrypt.verifyer(BCrypt.Version.VERSION_2Y_NO_NULL_TERMINATOR).verify(pw, "$2y$04$w8S7HTjIfG.8RRVOhLZWtuH6eei2l7NZ/VhYUrDJndAjDmOqK6E0W".toCharArray()).verified);
assertTrue(BCrypt.verifyer(BCrypt.Version.VERSION_2Y, LongPasswordStrategies.truncate(BCrypt.Version.VERSION_2Y)).verify(pw, "$2y$04$w8S7HTjIfG.8RRVOhLZWtu//55gj0VTX7XdNkQmDuPw.qQXsnvtkG".toCharArray()).verified);
}

@Test
public void verifyInferVersion() {
verifyInferedVersion("<.S.2K(Zq'", "$2y$04$VYAclAMpaXY/oqAo9yUpkuWmoYywaPzyhu56HxXpVltnBIfmO9tgu", BCrypt.Version.VERSION_2Y);
verifyInferedVersion("<.S.2K(Zq'", "$2x$04$VYAclAMpaXY/oqAo9yUpkuWmoYywaPzyhu56HxXpVltnBIfmO9tgu", BCrypt.Version.VERSION_2X);
verifyInferedVersion("<.S.2K(Zq'", "$2a$04$VYAclAMpaXY/oqAo9yUpkuWmoYywaPzyhu56HxXpVltnBIfmO9tgu", BCrypt.Version.VERSION_2A);
verifyInferedVersion("<.S.2K(Zq'", "$2b$04$VYAclAMpaXY/oqAo9yUpkuWmoYywaPzyhu56HxXpVltnBIfmO9tgu", BCrypt.Version.VERSION_2B);
}

private void verifyInferedVersion(String pw, String hash, BCrypt.Version expectedVersion) {
BCrypt.Result result = BCrypt.verifyer().verify(pw.toCharArray(), hash.toCharArray());
assertTrue(result.verified);
assertEquals(expectedVersion, result.details.version);
}

@Test(expected = IllegalArgumentException.class)
public void verifyStrictWithoutVersionShouldThrow() {
BCrypt.verifyer().verifyStrict("<.S.2K(Zq'".toCharArray(), "$2a$04$VYAclAMpaXY/oqAo9yUpkuWmoYywaPzyhu56HxXpVltnBIfmO9tgu".toCharArray());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@
public class LongPasswordStrategyTest {

private final int maxLength = 72;
private static final BCrypt.Version DEFAULT_VERSION = BCrypt.Version.VERSION_2A;

@Test
public void testFactory() {
assertNotNull(LongPasswordStrategies.hashSha512().derive(Bytes.random(100).array()));
assertNotNull(LongPasswordStrategies.truncate().derive(Bytes.random(100).array()));
assertNotNull(LongPasswordStrategies.hashSha512(DEFAULT_VERSION).derive(Bytes.random(100).array()));
assertNotNull(LongPasswordStrategies.truncate(DEFAULT_VERSION).derive(Bytes.random(100).array()));
assertNotNull(LongPasswordStrategies.none().derive(Bytes.random(100).array()));
}

@Test(expected = IllegalArgumentException.class)
public void testFactoryForStrictShouldThrowException() {
LongPasswordStrategies.strict().derive(Bytes.random(100).array());
LongPasswordStrategies.strict(DEFAULT_VERSION).derive(Bytes.random(100).array());
}

@Test
Expand Down