Search code examples
javaperformancegoogle-app-enginepermissionsclassloader

BCrypt very slow in app engine application


I'm developing a GWT/app engine application, using Eclipse. The below problem occurred after upgrading to app engine 1.6.4 from 1.6.3. After the upgrade, I my application would not work at all. Unfortunately, I had deleted my old app engine plugins, so I was unable to rollback to 1.6.3. After 2 hours of banging my head against the wall, I decided to recreate my Eclipse project. The project worked again, except for the following anomaly:

I'm using BCrypt to implement one-way hash encoding of passwords. Before yesterday, this worked fine, with password encodes and checks occurring very fast -- probably in a few milliseconds. Now these operations take on the order of 2 minutes! Using the debugger, I paused the application to see if I could figure out what was going on. Each time I pause, I get a stack trace such as the following:

    Thread [798744730@qtp-2080190228-3] (Suspended) 
    Class<T>.forName0(String, boolean, ClassLoader) line: not available [native method] 
    Class<T>.forName(String) line: 186  
    RuntimeHelper.checkRestricted(boolean, String, String, String) line: 63 
    Runtime.checkRestricted(boolean, String, String, String) line: 63   
    BCrypt.encipher(int[], int) line: 496   
    BCrypt.key(byte[]) line: 558    
    BCrypt.crypt_raw(byte[], byte[], int) line: 622 
    BCrypt.hashpw(String, String) line: 681 
    BCrypt.checkpw(String, String) line: 749

BCrypt.encipher() is as follows: (line 496 is shown below in a line comment)

private final 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;) {
        // Feistel substitution on left word
        n = S[(l >> 24) & 0xff];
        n += S[0x100 | ((l >> 16) & 0xff)];
        n ^= S[0x200 | ((l >> 8) & 0xff)];
        n += S[0x300 | (l & 0xff)];
        r ^= n ^ P[++i];  //*** LINE 496 *****

        // Feistel substitution on right word
        n = S[(r >> 24) & 0xff];
        n += S[0x100 | ((r >> 16) & 0xff)];
        n ^= S[0x200 | ((r >> 8) & 0xff)];
        n += S[0x300 | (r & 0xff)];
        l ^= n ^ P[++i];
    }
    lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1];
    lr[off + 1] = l;
}

Depending on when I pause the debugger, different lines in BCrypt are the caller to Runtime.checkRestricted(), but it appears that Runtime.checkRestricted() is called continuously. Since this is called in embedded loops, I'm thinking that this is the cause. I then went on a hunt as to how to avoid this checkRestricted() call from happening. No luck.

I have a somewhat complicated application structure that contains three Google web applications (Eclipse projects). I'll call them:

Base

Store

App

where Store depends upon Base, and App depends upon both Store and Base. I use an Ant task to build the Base and Store projects into JAR files and copy them to the App/war/WEB-INF/lib folder.

Originally, I had BCrypt in its own Eclipse project and my ANT task would also JAR this and copy it to App/war/WEB-INF/lib. This was working fine for the past few months until now. To try to work around the current problem, I tried moving the BCrypt class (it contains only 1 class) directly into the Base project, with the same result, then into the Store project, again with the same result. Since my app currently calls BCrypt methods only from the Store project, I figured either of this might work. They did, functionally, but still taking 2 minutes to complete an encipher() call.

From the stack trace, Runtime or RuntimeHelper return Source Not Found when I click on them, and I can find nothing about them in Google searches.

Questions:

Why is every line in BCrypt subjected to a checkRestricted() call? This doesn't seem normal.

More importantly, any idea on how to fix this problem?

I don't know what to look at next. Any ideas would be very welcome, even if you don't know the ultimate solution.

Thanks very much.

Rick


Solution

  • I just created a ticket at the GAE code project: http://code.google.com/p/googleappengine/issues/detail?id=7277&thanks=7277&ts=1333530915

    Maybe, we get an answer there as using BCrypt is also provided by an official tutorial: http://code.google.com/p/google-web-toolkit-incubator/wiki/LoginSecurityFAQ

    Google fixed the bug and released a new version (1.6.4.1) today: http://code.google.com/p/googleappengine/downloads/detail?name=appengine-java-sdk-1.6.4.1.zip