public static String hash(String password) { return bcrypt.hash(password); }
public boolean verifyAndUpdateHash(String password, String hash, Function<String, Boolean> updateFunc) { if (BCrypt.checkpw(password, hash)) { int rounds = getRounds(hash); // It might be smart to only allow increasing the rounds. // If someone makes a mistake the ability to undo it would be nice though. if (rounds != logRounds) { log.debug("Updating password from {} rounds to {}", rounds, logRounds); String newHash = hash(password); return updateFunc.apply(newHash); } return true; } return false; }
public static void main(String[] args) { // {{start:bcryptMain}} // Mini function to test updates. String[] mutableHash = new String[1]; Function<String, Boolean> update = hash -> { mutableHash[0] = hash; return true; }; String hashPw1 = Hashing.hash("password"); log.debug("hash of pw1: {}", hashPw1); log.debug("verifying pw1: {}", Hashing.verifyAndUpdateHash("password", hashPw1, update)); log.debug("verifying pw1 fails: {}", Hashing.verifyAndUpdateHash("password1", hashPw1, update)); String hashPw2 = Hashing.hash("password"); log.debug("hash of pw2: {}", hashPw2); log.debug("verifying pw2: {}", Hashing.verifyAndUpdateHash("password", hashPw2, update)); log.debug("verifying pw2 fails: {}", Hashing.verifyAndUpdateHash("password2", hashPw2, update)); UpdatableBCrypt oldHasher = new UpdatableBCrypt(7); String oldHash = oldHasher.hash("password"); log.debug("hash of oldHash: {}", oldHash); log.debug("verifying oldHash: {}, hash upgraded to: {}", Hashing.verifyAndUpdateHash("password", oldHash, update), mutableHash[0]); // {{end:bcryptMain}} } }
@Test public void testHash() { UpdatableBCrypt bcrypt = new UpdatableBCrypt(11); String hash = bcrypt.hash("password"); assertTrue(bcrypt.verifyHash("password", hash)); }
@Test public void testVerifyAndUpdateDoesNotUpdate() { UpdatableBCrypt bcrypt11 = new UpdatableBCrypt(10); String hash11 = bcrypt11.hash("password"); assertTrue(bcrypt11.verifyHash("password", hash11)); UpdatableBCrypt bcrypt12 = new UpdatableBCrypt(10); String[] hash12 = new String[1]; bcrypt12.verifyAndUpdateHash("password", hash11, newHash -> { hash12[0] = newHash; return true; }); assertTrue(null == hash12[0]); }
@Test public void testVerifyAndUpdateFailsOnBadPW() { UpdatableBCrypt bcrypt11 = new UpdatableBCrypt(10); String hash11 = bcrypt11.hash("password"); assertTrue(bcrypt11.verifyHash("password", hash11)); UpdatableBCrypt bcrypt12 = new UpdatableBCrypt(11); String[] hash12 = new String[1]; boolean result = bcrypt12.verifyAndUpdateHash("password1", hash11, newHash -> { hash12[0] = newHash; return true; }); assertFalse(result); assertTrue(null == hash12[0]); } }
@Test public void testVerifyAndUpdateChangesHashSkippingRounds() { UpdatableBCrypt bcrypt11 = new UpdatableBCrypt(5); String hash11 = bcrypt11.hash("password"); assertTrue(bcrypt11.verifyHash("password", hash11)); UpdatableBCrypt bcrypt12 = new UpdatableBCrypt(13); String[] hash12 = new String[1]; boolean result = bcrypt12.verifyAndUpdateHash("password", hash11, newHash -> { hash12[0] = newHash; return true; }); assertTrue(result); assertTrue(null != hash12[0]); assertTrue(bcrypt12.verifyHash("password", hash12[0])); }
@Test public void testVerifyAndUpdateChangesHash() { UpdatableBCrypt bcrypt11 = new UpdatableBCrypt(10); String hash11 = bcrypt11.hash("password"); assertTrue(bcrypt11.verifyHash("password", hash11)); UpdatableBCrypt bcrypt12 = new UpdatableBCrypt(11); String[] hash12 = new String[1]; boolean result = bcrypt12.verifyAndUpdateHash("password", hash11, newHash -> { hash12[0] = newHash; return true; }); assertTrue(result); assertTrue(null != hash12[0]); assertTrue(bcrypt12.verifyHash("password", hash12[0])); // Hash shouldn't change again String finalHash = hash12[0]; result = bcrypt12.verifyAndUpdateHash("password", finalHash, newHash -> { hash12[0] = newHash; return true; }); assertTrue(result); assertTrue(null != finalHash); assertEquals(finalHash, hash12[0]); assertTrue(bcrypt12.verifyHash("password", finalHash)); }