diff --git a/README.md b/README.md index 05780bd..dfa6428 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,11 @@ This is an alternative distribution of [jBCrypt](http://www.mindrot.org/projects packaged to ease use in existing applications — especially those using Apache Maven. -The code is unchanged from the original jBCrypt 0.3, however: +The code is unchanged from the original jBCrypt 0.4, however: - The classes have been moved to a java package to avoid pollution of the global namespace. *org.mindrot* was chosen to reflect their original origin. -- The JBCrypt class javadoc has been changed to version 0.3. The official +- The JBCrypt class javadoc has been changed to version 0.4. The official package incorrectly contains 0.2 as the stated version. - A pom.xml file has been added for use with Maven diff --git a/pom.xml b/pom.xml index 3adcae9..fbf30f8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.mindrot jbcrypt - 0.3 + 0.4 jar jbcrypt diff --git a/src/main/java/org/mindrot/BCrypt.java b/src/main/java/org/mindrot/BCrypt.java index fbfa219..188193f 100644 --- a/src/main/java/org/mindrot/BCrypt.java +++ b/src/main/java/org/mindrot/BCrypt.java @@ -56,10 +56,10 @@ *

* The amount of work increases exponentially (2**log_rounds), so * each increment is twice as much work. The default log_rounds is - * 10, and the valid range is 4 to 31. + * 10, and the valid range is 4 to 30. * * @author Damien Miller - * @version 0.3 + * @version 0.4 */ public class BCrypt { // BCrypt parameters @@ -336,7 +336,9 @@ public class BCrypt { 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 }; - // bcrypt IV: "OrpheanBeholderScryDoubt" + // bcrypt IV: "OrpheanBeholderScryDoubt". The C implementation calls + // this "ciphertext", but it is really plaintext or an IV. We keep + // the name to make code comparison easier. static private final int bf_crypt_ciphertext[] = { 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 @@ -602,15 +604,16 @@ private void ekskey(byte data[], byte key[]) { * @param salt the binary salt to hash with the password * @param log_rounds the binary logarithm of the number * of rounds of hashing to apply + * @param cdata the plaintext to encrypt * @return an array containing the binary hashed password */ - private byte[] crypt_raw(byte password[], byte salt[], int log_rounds) { + public byte[] crypt_raw(byte password[], byte salt[], int log_rounds, + int cdata[]) { int rounds, i, j; - int cdata[] = (int[])bf_crypt_ciphertext.clone(); int clen = cdata.length; byte ret[]; - if (log_rounds < 4 || log_rounds > 31) + if (log_rounds < 4 || log_rounds > 30) throw new IllegalArgumentException ("Bad number of rounds"); rounds = 1 << log_rounds; if (salt.length != BCRYPT_SALT_LEN) @@ -618,7 +621,7 @@ private byte[] crypt_raw(byte password[], byte salt[], int log_rounds) { init_key(); ekskey(salt, password); - for (i = 0; i < rounds; i++) { + for (i = 0; i != rounds; i++) { key(password); key(salt); } @@ -679,7 +682,8 @@ public static String hashpw(String password, String salt) { saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); B = new BCrypt(); - hashed = B.crypt_raw(passwordb, saltb, rounds); + hashed = B.crypt_raw(passwordb, saltb, rounds, + (int[])bf_crypt_ciphertext.clone()); rs.append("$2"); if (minor >= 'a') @@ -687,6 +691,10 @@ public static String hashpw(String password, String salt) { rs.append("$"); if (rounds < 10) rs.append("0"); + if (rounds > 30) { + throw new IllegalArgumentException( + "rounds exceeds maximum (30)"); + } rs.append(Integer.toString(rounds)); rs.append("$"); rs.append(encode_base64(saltb, saltb.length)); @@ -712,6 +720,10 @@ public static String gensalt(int log_rounds, SecureRandom random) { rs.append("$2a$"); if (log_rounds < 10) rs.append("0"); + if (log_rounds > 30) { + throw new IllegalArgumentException( + "log_rounds exceeds maximum (30)"); + } rs.append(Integer.toString(log_rounds)); rs.append("$"); rs.append(encode_base64(rnd, rnd.length)); @@ -747,6 +759,20 @@ public static String gensalt() { * @return true if the passwords match, false otherwise */ public static boolean checkpw(String plaintext, String hashed) { - return (hashed.compareTo(hashpw(plaintext, hashed)) == 0); + byte hashed_bytes[]; + byte try_bytes[]; + try { + String try_pw = hashpw(plaintext, hashed); + hashed_bytes = hashed.getBytes("UTF-8"); + try_bytes = try_pw.getBytes("UTF-8"); + } catch (UnsupportedEncodingException uee) { + return false; + } + if (hashed_bytes.length != try_bytes.length) + return false; + byte ret = 0; + for (int i = 0; i < try_bytes.length; i++) + ret |= hashed_bytes[i] ^ try_bytes[i]; + return ret == 0; } } diff --git a/src/test/java/org/mindrot/TestBCrypt.java b/src/test/java/org/mindrot/TestBCrypt.java index 632bb6a..7ce8a81 100644 --- a/src/test/java/org/mindrot/TestBCrypt.java +++ b/src/test/java/org/mindrot/TestBCrypt.java @@ -180,7 +180,7 @@ public void testCheckpw_failure() { */ public void testInternationalChars() { System.out.print("BCrypt.hashpw w/ international chars: "); - String pw1 = "ππππππππ"; + String pw1 = "\u2605\u2605\u2605\u2605\u2605\u2605\u2605\u2605"; String pw2 = "????????"; String h1 = BCrypt.hashpw(pw1, BCrypt.gensalt());