From e169fe2bd9578858e5733ba3773885b34f431491 Mon Sep 17 00:00:00 2001 From: Reed Loden Date: Thu, 12 Feb 2015 09:58:01 -0800 Subject: [PATCH] Update to jBCrypt 0.4 to correct an integer overflow (http://www.mindrot.org/projects/jBCrypt/news/rel04.html) --- ext/jruby/bcrypt_jruby/BCrypt.java | 43 +++++++++++++++++++++++------- spec/TestBCrypt.java | 5 +++- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/ext/jruby/bcrypt_jruby/BCrypt.java b/ext/jruby/bcrypt_jruby/BCrypt.java index 0846005..a86b47e 100644 --- a/ext/jruby/bcrypt_jruby/BCrypt.java +++ b/ext/jruby/bcrypt_jruby/BCrypt.java @@ -15,7 +15,6 @@ package bcrypt_jruby; import java.io.UnsupportedEncodingException; - import java.security.SecureRandom; /** @@ -56,7 +55,7 @@ *

* 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.2 @@ -336,7 +335,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 +603,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 +620,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 +681,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 +690,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 +719,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 +758,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/spec/TestBCrypt.java b/spec/TestBCrypt.java index c481ff3..eeabf05 100644 --- a/spec/TestBCrypt.java +++ b/spec/TestBCrypt.java @@ -12,6 +12,9 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +package bcrypt_jruby; + +import bcrypt_jruby.BCrypt; import junit.framework.TestCase; /** @@ -178,7 +181,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());