Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add enum + minor API
  • Loading branch information
Laurent Valdes committed Aug 11, 2015
commit fa736320ecdff8435bf07e64c261100905d0fea8
96 changes: 69 additions & 27 deletions src/main/java/org/mindrot/BCrypt.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,22 @@ public final class BCrypt {
public static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
public static final int BCRYPT_SALT_LEN = 16;

public static final char A_MINOR = 'a';
public static final char Y_MINOR = 'y';
// Blowfish parameters
public static final int BLOWFISH_NUM_ROUNDS = 16;

/**
* Different types of compatible blowfish minors
*/
enum Minor {
A('a'), //"2a" - some implementations suffered from a very rare security flaw. current default for compatibility purposes.
Y('y'); //"2y" - format specific to the crypt_blowfish BCrypt implementation, identical to "2a" in all but name.

private final char minor;

Minor(char minor) {
this.minor = minor;
}
}

/**
* Check that a plaintext password matches a previously hashed
Expand All @@ -78,52 +92,80 @@ public final class BCrypt {
* @param hashed the previously-hashed password
* @return true if the passwords match, false otherwise
*/
public static boolean checkpw(String plaintext, String hashed, Minor minor) {
return checkPassword(plaintext, hashed, minor.minor);
}


/**
* Use the {@link #checkpw(String, String, Minor)} (String, String, Minor)} method.
*/
public static boolean checkpw(String plaintext, String hashed) {
return checkPassword(plaintext, hashed);
return checkpw(plaintext, hashed, Minor.A);
}

/**
* Hash a password using the OpenBSD bcrypt scheme
* @param password the password to hash
* @param salt the salt to hash with (perhaps generated
* using BCrypt.gensalt)
* @return the hashed password
*/
public static String hashpw(String password, String salt) {
return generateHash(password, salt);
* Hash a password using the OpenBSD bcrypt scheme
* @param password the password to hash
* @param salt the salt to hash with (perhaps generated
* using BCrypt.gensalt)
* @return the hashed password
*/
public static String hashpw(String password, String salt, Minor minor) {
return generateHash(password, salt, minor.minor);
}

/**
* Use the {@link #hashpw(String, String, Minor)} (String, String, char)} method.
*/
public static String hashpw(String password, String salt) {
return hashpw(password, salt, Minor.A);
}

/**
* Generate a salt for use with the BCrypt.hashpw() method,
* selecting a reasonable default for the number of hashing
* rounds to apply
* @return an encoded salt value
* Use the {@link #gensalt(int, SecureRandom, Minor)} method.
*/
public static String gensalt() {
return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);
}
/**
* Use the {@link #gensalt(int, SecureRandom, Minor)} method.
*/
public static String gensalt(Minor minor) {
return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS, minor);
}

/**
* Generate a salt for use with the BCrypt.hashpw() method
* @param log_rounds the log2 of the number of rounds of
* hashing to apply - the work factor therefore increases as
* 2**log_rounds.
* @return an encoded salt value
* Use the {@link #gensalt(int, SecureRandom, Minor)} method.
*/
public static String gensalt(int log_rounds) {
return gensalt(log_rounds, new SecureRandom());
}

/**
* Use the {@link #gensalt(int, SecureRandom, Minor)} method.
*/
public static String gensalt(int log_rounds, Minor minor) {
return gensalt(log_rounds, new SecureRandom(), minor);
}

/**
* Generate a salt for use with the BCrypt.hashpw() method
* @param log_rounds the log2 of the number of rounds of
* hashing to apply - the work factor therefore increases as
* 2**log_rounds.
* @param random an instance of SecureRandom to use
* @return an encoded salt value
* Use the {@link #gensalt(int, SecureRandom, Minor)} method.

*/
public static String gensalt(int log_rounds, SecureRandom random) {
return generateSalt(log_rounds, random);
return generateSalt(log_rounds, random, Minor.A.minor);
}

/**
* Generate a salt for use with the BCrypt.hashpw() method
* @param log_rounds the log2 of the number of rounds of
* hashing to apply - the work factor therefore increases as
* 2**log_rounds.
* @param random an instance of SecureRandom to use
* @return an encoded salt value
*/
public static String gensalt(int log_rounds, SecureRandom random, Minor minor) {
return generateSalt(log_rounds, random, minor.minor);
}
}
22 changes: 10 additions & 12 deletions src/main/java/org/mindrot/BcryptImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -283,17 +283,15 @@ public class BcryptImpl {
0x4f727068, 0x65616e42, 0x65686f6c,
0x64657253, 0x63727944, 0x6f756274
};
// Blowfish parameters
protected static final int BLOWFISH_NUM_ROUNDS = 16;
// Expanded Blowfish key
private int[] P;
private int[] S;

public static boolean checkPassword(String plaintext, String hashed) {
public static boolean checkPassword(String plaintext, String hashed, char minor) {
byte hashed_bytes[];
byte try_bytes[];
try {
String try_pw = generateHash(plaintext, hashed);
String try_pw = generateHash(plaintext, hashed, minor);
hashed_bytes = hashed.getBytes("UTF-8");
try_bytes = try_pw.getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
Expand All @@ -307,7 +305,7 @@ public static boolean checkPassword(String plaintext, String hashed) {
return ret == 0;
}

public static String generateHash(String password, String salt) {
public static String generateHash(String password, String salt, char targetMinor) {
String real_salt;
byte passwordb[], saltb[], hashed[];
char minor = (char)0;
Expand All @@ -320,7 +318,7 @@ public static String generateHash(String password, String salt) {
off = 3;
else {
minor = salt.charAt(2);
if (minor != BCrypt.A_MINOR || salt.charAt(3) != '$')
if (minor != targetMinor || salt.charAt(3) != '$')
throw new IllegalArgumentException ("Invalid salt revision");
off = 4;
}
Expand All @@ -332,7 +330,7 @@ public static String generateHash(String password, String salt) {

real_salt = salt.substring(off + 3, off + 25);
try {
passwordb = (password + (minor >= BCrypt.A_MINOR ? "\000" : "")).getBytes("UTF-8");
passwordb = (password + (minor >= targetMinor ? "\000" : "")).getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
throw new AssertionError("UTF-8 is not supported");
}
Expand All @@ -345,7 +343,7 @@ public static String generateHash(String password, String salt) {
(int[]) bf_crypt_ciphertext.clone());

rs.append("$2");
if (minor >= BCrypt.A_MINOR)
if (minor >= targetMinor)
rs.append(minor);
rs.append("$");
if (rounds < 10)
Expand All @@ -362,13 +360,13 @@ public static String generateHash(String password, String salt) {
return rs.toString();
}

public static String generateSalt(int log_rounds, SecureRandom random) {
public static String generateSalt(int log_rounds, SecureRandom random, char minor) {
StringBuilder rs = new StringBuilder();
byte rnd[] = new byte[BCrypt.BCRYPT_SALT_LEN];

random.nextBytes(rnd);

rs.append("$2" + BCrypt.A_MINOR + "$");
rs.append("$2" + minor + "$");
if (log_rounds < 10)
rs.append("0");
if (log_rounds > 30) {
Expand Down Expand Up @@ -523,7 +521,7 @@ private void encipher(int lr[], int off) {
int i, n, l = lr[off], r = lr[off + 1];

l ^= P[0];
for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) {
for (i = 0; i <= BCrypt.BLOWFISH_NUM_ROUNDS - 2;) {
// Feistel substitution on left word
n = S[(l >> 24) & 0xff];
n += S[0x100 | ((l >> 16) & 0xff)];
Expand All @@ -538,7 +536,7 @@ private void encipher(int lr[], int off) {
n += S[0x300 | (r & 0xff)];
l ^= n ^ P[++i];
}
lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1];
lr[off] = r ^ P[BCrypt.BLOWFISH_NUM_ROUNDS + 1];
lr[off + 1] = l;
}
}