Open Source Repository

Home /jodd/jodd-3.3.2 | Repository Home



jodd/util/BCrypt.java
// Copyright (c) 2003-2012, Jodd Team (jodd.org). All Rights Reserved.

package jodd.util;

import java.io.UnsupportedEncodingException;

import java.security.SecureRandom;

/**
 * BCrypt implements OpenBSD-style Blowfish password hashing using
 * the scheme described in "A Future-Adaptable Password Scheme" by
 * Niels Provos and David Mazieres.
 <p>
 * This password hashing system tries to thwart off-line password
 * cracking using a computationally-intensive hashing algorithm,
 * based on Bruce Schneier's Blowfish cipher. The work factor of
 * the algorithm is parameterised, so it can be increased as
 * computers get faster.
 <p>
 * Usage is really simple. To hash a password for the first time,
 * call the hashpw method with a random salt, like this:
 <p>
 <code>
 * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt()); <br />
 </code>
 <p>
 * To check whether a plaintext password matches one that has been
 * hashed previously, use the checkpw method:
 <p>
 <code>
 * if (BCrypt.checkpw(candidate_password, stored_hash))<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println("It matches");<br />
 * else<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println("It does not match");<br />
 </code>
 <p>
 * The gensalt() method takes an optional parameter (log_rounds)
 * that determines the computational complexity of the hashing:
 <p>
 <code>
 * String strong_salt = BCrypt.gensalt(10)<br />
 * String stronger_salt = BCrypt.gensalt(12)<br />
 </code>
 <p>
 * 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.
 *
 @author Damien Miller
 @version 0.3
 */
@SuppressWarnings({"ALL"})
public class BCrypt {
  // BCrypt parameters
  private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
  private static final int BCRYPT_SALT_LEN = 16;

  // Blowfish parameters
  private static final int BLOWFISH_NUM_ROUNDS = 16;

  // Initial contents of key schedule
  private static final int P_orig[] {
    0x243f6a880x85a308d30x13198a2e0x03707344,
    0xa40938220x299f31d00x082efa980xec4e6c89,
    0x452821e60x38d013770xbe5466cf0x34e90c6c,
    0xc0ac29b70xc97c50dd0x3f84d5b50xb5470917,
    0x9216d5d90x8979fb1b
  };
  private static final int S_orig[] {
    0xd1310ba60x98dfb5ac0x2ffd72db0xd01adfb7,
    0xb8e1afed0x6a267e960xba7c90450xf12c7f99,
    0x24a199470xb3916cf70x0801f2e20x858efc16,
    0x636920d80x71574e690xa458fea30xf4933d7e,
    0x0d95748f0x728eb6580x718bcd580x82154aee,
    0x7b54a41d0xc25a59b50x9c30d5390x2af26013,
    0xc5d1b0230x286085f00xca4179180xb8db38ef,
    0x8e79dcb00x603a180e0x6c9e0e8b0xb01e8a3e,
    0xd71577c10xbd314b270x78af2fda0x55605c60,
    0xe65525f30xaa55ab940x574898620x63e81440,
    0x55ca396a0x2aab10b60xb4cc5c340x1141e8ce,
    0xa15486af0x7c72e9930xb3ee14110x636fbc2a,
    0x2ba9c55d0x741831f60xce5c3e160x9b87931e,
    0xafd6ba330x6c24cf5c0x7a3253810x28958677,
    0x3b8f48980x6b4bb9af0xc4bfe81b0x66282193,
    0x61d809cc0xfb21a9910x487cac600x5dec8032,
    0xef845d5d0xe98575b10xdc2623020xeb651b88,
    0x23893e810xd396acc50x0f6d6ff30x83f44239,
    0x2e0b44820xa48420040x69c8f04a0x9e1f9b5e,
    0x21c668420xf6e96c9a0x670c9c610xabd388f0,
    0x6a51a0d20xd8542f680x960fa7280xab5133a3,
    0x6eef0b6c0x137a3be40xba3bf0500x7efb2a98,
    0xa1f1651d0x39af01760x66ca593e0x82430e88,
    0x8cee86190x456f9fb40x7d84a5c30x3b8b5ebe,
    0xe06f75d80x85c120730x401a449f0x56c16aa6,
    0x4ed3aa620x363f77060x1bfedf720x429b023d,
    0x37d0d7240xd00a12480xdb0fead30x49f1c09b,
    0x075372c90x80991b7b0x25d479d80xf6e8def7,
    0xe3fe501a0xb6794c3b0x976ce0bd0x04c006ba,
    0xc1a94fb60x409f60c40x5e5c9ec20x196a2463,
    0x68fb6faf0x3e6c53b50x1339b2eb0x3b52ec6f,
    0x6dfc511f0x9b30952c0xcc8145440xaf5ebd09,
    0xbee3d0040xde334afd0x660f28070x192e4bb3,
    0xc0cba8570x45c8740f0xd20b5f390xb9d3fbdb,
    0x5579c0bd0x1a60320a0xd6a100c60x402c7279,
    0x679f25fe0xfb1fa3cc0x8ea5e9f80xdb3222f8,
    0x3c7516df0xfd616b150x2f501ec80xad0552ab,
    0x323db5fa0xfd2387600x53317b480x3e00df82,
    0x9e5c57bb0xca6f8ca00x1a87562e0xdf1769db,
    0xd542a8f60x287effc30xac6732c60x8c4f5573,
    0x695b27b00xbbca58c80xe1ffa35d0xb8f011a0,
    0x10fa3d980xfd2183b80x4afcb56c0x2dd1d35b,
    0x9a53e4790xb6f845650xd28e49bc0x4bfb9790,
    0xe1ddf2da0xa4cb7e330x62fb13410xcee4c6e8,
    0xef20cada0x36774c010xd07e9efe0x2bf11fb4,
    0x95dbda4d0xae9091980xeaad8e710x6b93d5a0,
    0xd08ed1d00xafc725e00x8e3c5b2f0x8e7594b7,
    0x8ff6e2fb0xf2122b640x8888b8120x900df01c,
    0x4fad5ea00x688fc31c0xd1cff1910xb3a8c1ad,
    0x2f2f22180xbe0e17770xea752dfe0x8b021fa1,
    0xe5a0cc0f0xb56f74e80x18acf3d60xce89e299,
    0xb4a84fe00xfd13e0b70x7cc43b810xd2ada8d9,
    0x165fa2660x809577050x93cc73140x211a1477,
    0xe6ad20650x77b5fa860xc75442f50xfb9d35cf,
    0xebcdaf0c0x7b3e89a00xd6411bd30xae1e7e49,
    0x00250e2d0x2071b35e0x226800bb0x57b8e0af,
    0x2464369b0xf009b91e0x5563911d0x59dfa6aa,
    0x78c143890xd95a537f0x207d5ba20x02e5b9c5,
    0x832603760x6295cfa90x11c819680x4e734a41,
    0xb3472dca0x7b14a94a0x1b5100520x9a532915,
    0xd60f573f0xbc9bc6e40x2b60a4760x81e67400,
    0x08ba6fb50x571be91f0xf296ec6b0x2a0dd915,
    0xb66365210xe7b9f9b60xff34052e0xc5855664,
    0x53b02d5d0xa99f8fa10x08ba47990x6e85076a,
    0x4b7a70e90xb5b329440xdb75092e0xc4192623,
    0xad6ea6b00x49a7df7d0x9cee60b80x8fedb266,
    0xecaa8c710x699a17ff0x5664526c0xc2b19ee1,
    0x193602a50x75094c290xa05913400xe4183a3e,
    0x3f54989a0x5b429d650x6b8fe4d60x99f73fd6,
    0xa1d29c070xefe830f50x4d2d38e60xf0255dc1,
    0x4cdd20860x8470eb260x6382e9c60x021ecc5e,
    0x09686b3f0x3ebaefc90x3c9718140x6b6a70a1,
    0x687f35840x52a0e2860xb79c53050xaa500737,
    0x3e07841c0x7fdeae5c0x8e7d44ec0x5716f2b8,
    0xb03ada370xf0500c0d0xf01c1f040x0200b3ff,
    0xae0cf51a0x3cb574b20x25837a580xdc0921bd,
    0xd19113f90x7ca92ff60x943247730x22f54701,
    0x3ae5e5810x37c2dadc0xc8b576340x9af3dda7,
    0xa94461460x0fd0030e0xecc8c73e0xa4751e41,
    0xe238cd990x3bea0e2f0x3280bba10x183eb331,
    0x4e548b380x4f6db9080x6f420d030xf60a04bf,
    0x2cb812900x24977c790x5679b0720xbcaf89af,
    0xde9a771f0xd99308100xb38bae120xdccf3f2e,
    0x5512721f0x2e6b71240x501adde60x9f84cd87,
    0x7a5847180x7408da170xbc9f9abc0xe94b7d8c,
    0xec7aec3a0xdb851dfa0x630943660xc464c3d2,
    0xef1c18470x3215d9080xdd433b370x24c2ba16,
    0x12a14d430x2a65c4510x509400020x133ae4dd,
    0x71dff89e0x10314e550x81ac77d60x5f11199b,
    0x043556f10xd7a3c76b0x3c11183b0x5924a509,
    0xf28fe6ed0x97f1fbfa0x9ebabf2c0x1e153c6e,
    0x86e345700xeae96fb10x860e5e0a0x5a3e2ab3,
    0x771fe71c0x4e3d06fa0x2965dcb90x99e71d0f,
    0x803e89d60x5266c8250x2e4cc9780x9c10b36a,
    0xc6150eba0x94e2ea780xa5fc3c530x1e0a2df4,
    0xf2f74ea70x361d2b3d0x1939260f0x19c27960,
    0x5223a7080xf71312b60xebadfe6e0xeac31f66,
    0xe3bc45950xa67bc8830xb17f37d10x018cff28,
    0xc332ddef0xbe6c5aa50x655821850x68ab9802,
    0xeecea50f0xdb2f953b0x2aef7dad0x5b6e2f84,
    0x1521b6280x290761700xecdd47750x619f1510,
    0x13cca8300xeb61bd960x0334fe1e0xaa0363cf,
    0xb5735c900x4c70a2390xd59e9e0b0xcbaade14,
    0xeecc86bc0x60622ca70x9cab5cab0xb2f3846e,
    0x648b1eaf0x19bdf0ca0xa02369b90x655abb50,
    0x40685a320x3c2ab4b30x319ee9d50xc021b8f7,
    0x9b540b190x875fa0990x95f7997e0x623d7da8,
    0xf837889a0x97e32d770x11ed935f0x16681281,
    0x0e3588290xc7e61fd60x96dedfa10x7858ba99,
    0x57f584a50x1b2272630x9b83c3ff0x1ac24696,
    0xcdb30aeb0x532e30540x8fd948e40x6dbc3128,
    0x58ebf2ef0x34c6ffea0xfe28ed610xee7c3c73,
    0x5d4a14d90xe864b7e30x42105d140x203e13e0,
    0x45eee2b60xa3aaabea0xdb6c4f150xfacb4fd0,
    0xc742f4420xef6abbb50x654f3b1d0x41cd2105,
    0xd81e799e0x86854dc70xe44b476a0x3d816250,
    0xcf62a1f20x5b8d26460xfc8883a00xc1c7b6a3,
    0x7f1524c30x69cb74920x47848a0b0x5692b285,
    0x095bbf000xad19489d0x1462b1740x23820e00,
    0x58428d2a0x0c55f5ea0x1dadf43e0x233f7061,
    0x3372f0920x8d937e410xd65fecf10x6c223bdb,
    0x7cde37590xcbee74600x4085f2a70xce77326e,
    0xa60780840x19f8509e0xe8efd8550x61d99735,
    0xa969a7aa0xc50c06c20x5a04abfc0x800bcadc,
    0x9e447a2e0xc34534840xfdd567050x0e1e9ec9,
    0xdb73dbd30x105588cd0x675fda790xe3674340,
    0xc5c434650x713e38d80x3d28f89e0xf16dff20,
    0x153e21e70x8fb03d4a0xe6e39f2b0xdb83adf7,
    0xe93d5a680x948140f70xf64c261c0x94692934,
    0x411520f70x7602d4f70xbcf46b2e0xd4a20068,
    0xd40824710x3320f46a0x43b7d4b70x500061af,
    0x1e39f62e0x972445460x14214f740xbf8b8840,
    0x4d95fc1d0x96b591af0x70f4ddd30x66a02f45,
    0xbfbc09ec0x03bd97850x7fac6dd00x31cb8504,
    0x96eb27b30x55fd39410xda2547e60xabca0a9a,
    0x285078250x530429f40x0a2c86da0xe9b66dfb,
    0x68dc14620xd74869000x680ec0a40x27a18dee,
    0x4f3ffea20xe887ad8c0xb58ce0060x7af4d6b6,
    0xaace1e7c0xd3375fec0xce78a3990x406b2a42,
    0x20fe9e350xd9f385b90xee39d7ab0x3b124e8b,
    0x1dc9faf70x4b6d18560x26a366310xeae397b2,
    0x3a6efa740xdd5b43320x6841e7f70xca7820fb,
    0xfb0af54e0xd8feb3970x454056ac0xba489527,
    0x55533a3a0x20838d870xfe6ba9b70xd096954b,
    0x55a867bc0xa1159a580xcca929630x99e1db33,
    0xa62a4a560x3f3125f90x5ef47e1c0x9029317c,
    0xfdf8e8020x04272f700x80bb155c0x05282ce3,
    0x95c115480xe4c66d220x48c1133f0xc70f86dc,
    0x07f9c9ee0x41041f0f0x404779a40x5d886e17,
    0x325f51eb0xd59bc0d10xf2bcc18f0x41113564,
    0x257b78340x602a9c600xdff8e8a30x1f636c1b,
    0x0e12b4c20x02e1329e0xaf664fd10xcad18115,
    0x6b2395e00x333e92e10x3b240b620xeebeb922,
    0x85b2a20e0xe6ba0d990xde720c8c0x2da2f728,
    0xd01278450x95b794fd0x647d08620xe7ccf5f0,
    0x5449a36f0x877d48fa0xc39dfd270xf33e8d1e,
    0x0a4763410x992eff740x3a6f6eab0xf4f8fd37,
    0xa812dc600xa1ebddf80x991be14c0xdb6e6b0d,
    0xc67b55100x6d672c370x2765d43b0xdcd0e804,
    0xf1290dc70xcc00ffa30xb5390f920x690fed0b,
    0x667b9ffb0xcedb7d9c0xa091cf0b0xd9155ea3,
    0xbb132f880x515bad240x7b9479bf0x763bd6eb,
    0x37392eb30xcc1159790x8026e2970xf42e312d,
    0x6842ada70xc66a2b3b0x12754ccc0x782ef11c,
    0x6a1242370xb79251e70x06a1bbe60x4bfb6350,
    0x1a6b10180x11caedfa0x3d25bdd80xe2e1c3c9,
    0x444216590x0a1213860xd90cec6e0xd5abea2a,
    0x64af674e0xda86a85f0xbebfe9880x64e4c3fe,
    0x9dbc80570xf0f7c0860x60787bf80x6003604d,
    0xd1fd83460xf6381fb00x7745ae040xd736fccc,
    0x83426b330xf01eab710xb08041870x3c005e5f,
    0x77a057be0xbde8ae240x554642990xbf582e61,
    0x4e58f48f0xf2ddfda20xf474ef380x8789bdc2,
    0x5366f9c30xc8b38e740xb475f2550x46fcd9b9,
    0x7aeb26610x8b1ddf840x846a0e790x915f95e2,
    0x466e598e0x20b457700x8cd555910xc902de4c,
    0xb90bace10xbb8205d00x11a862480x7574a99e,
    0xb77f19b60xe0a9dc090x662d09a10xc4324633,
    0xe85a1f020x09f0be8c0x4a99a0250x1d6efe10,
    0x1ab93d1d0x0ba5a4df0xa186f20f0x2868f169,
    0xdcb7da830x573906fe0xa1e2ce9b0x4fcd7f52,
    0x50115e010xa70683fa0xa002b5c40x0de6d027,
    0x9af88c270x773f86410xc3604c060x61a806b5,
    0xf0177a280xc0f586e00x006058aa0x30dc7d62,
    0x11e69ed70x2338ea630x53c2dd940xc2c21634,
    0xbbcbee560x90bcb6de0xebfc7da10xce591d76,
    0x6f05e4090x4b7c01880x39720a3d0x7c927c24,
    0x86e3725f0x724d9db90x1ac15bb40xd39eb8fc,
    0xed5455780x08fca5b50xd83d7cd30x4dad0fc4,
    0x1e50ef5e0xb161e6f80xa28514d90x6c51133c,
    0x6fd5c7e70x56e14ec40x362abfce0xddc6c837,
    0xd79a32340x926382120x670efa8e0x406000e0,
    0x3a39ce370xd3faf5cf0xabc277370x5ac52d1b,
    0x5cb0679e0x4fa337420xd38227400x99bc9bbe,
    0xd5118e9d0xbf0f73150xd62d1c7e0xc700c47b,
    0xb78c1b6b0x21a190450xb26eb1be0x6a366eb4,
    0x5748ab2f0xbc946e790xc6a376d20x6549c2c8,
    0x530ff8ee0x468dde7d0xd5730a1d0x4cd04dc6,
    0x2939bbdb0xa9ba46500xac9526e80xbe5ee304,
    0xa1fad5f00x6a2d519a0x63ef8ce20x9a86ee22,
    0xc089c2b80x43242ef60xa51e03aa0x9cf2d0a4,
    0x83c061ba0x9be96a4d0x8fe515500xba645bd6,
    0x2826a2f90xa73a3ae10x4ba995860xef5562e9,
    0xc72fefd30xf752f7da0x3f046f690x77fa0a59,
    0x80e4a9150x87b086010x9b09e6ad0x3b3ee593,
    0xe990fd5a0x9e34d7970x2cf0b7d90x022b8b51,
    0x96d5ac3a0x017da67d0xd1cf3ed60x7c7d2d28,
    0x1f9f25cf0xadf2b89b0x5ad6b4720x5a88f54c,
    0xe029ac710xe019a5e60x47b0acfd0xed93fa9b,
    0xe8d3c48d0x283b57cc0xf8d566290x79132e28,
    0x785f01910xed7560550xf7960e440xe3d35e8c,
    0x15056dd40x88f46dba0x03a161250x0564f0bd,
    0xc3eb9e150x3c9057a20x97271aec0xa93a072a,
    0x1b3f6d9b0x1e6321f50xf59c66fb0x26dcf319,
    0x7533d9280xb155fdf50x035634820x8aba3cbb,
    0x285177110xc20ad9f80xabcc51670xccad925f,
    0x4de817510x3830dc8e0x379d58620x9320f991,
    0xea7a90c20xfb3e7bce0x5121ce640x774fbe32,
    0xa8b6e37e0xc3293d460x48de53690x6413e680,
    0xa2ae08100xdd6db2240x69852dfd0x09072166,
    0xb39a460a0x6445c0dd0x586cdecf0x1c20c8ae,
    0x5bbef7dd0x1b588d400xccd2017f0x6bb4e3bb,
    0xdda26a7e0x3a59ff450x3e350a440xbcb4cdd5,
    0x72eacea80xfa6484bb0x8d6612ae0xbf3c6f47,
    0xd29be4630x542f5d9e0xaec2771b0xf64e6370,
    0x740e0d8d0xe75b13570xf87216710xaf537d5d,
    0x4040cb080x4eb4e2cc0x34d2466a0x0115af84,
    0xe1b004280x95983a1d0x06b89fb40xce6ea048,
    0x6f3f3b820x3520ab820x011a1d4b0x277227f8,
    0x611560b10xe7933fdc0xbb3a792b0x344525bd,
    0xa08839e10x51ce794b0x2f32c9b70xa01fbac9,
    0xe01cc87e0xbcc7d1f60xcf0111c30xa1e8aac7,
    0x1a9087490xd44fbd9a0xd0dadecb0xd50ada38,
    0x0339c32a0xc69136670x8df9317c0xe0b12b4f,
    0xf79e59b70x43f5bb3a0xf2d519ff0x27d9459c,
    0xbf97222c0x15e6fc2a0x0f91fc710x9b941525,
    0xfae593610xceb69ceb0xc2a864590x12baa8d1,
    0xb6c1075e0xe3056a0c0x10d250650xcb03a442,
    0xe0ec6e0e0x1698db3b0x4c98a0be0x3278e964,
    0x9f1f95320xe0d392df0xd3a0342b0x8971f21e,
    0x1b0a74410x4ba3348c0xc5be71200xc37632d8,
    0xdf359f8d0x9b992f2e0xe60b6f470x0fe3f11d,
    0xe54cda540x1edad8910xce6279cf0xcd3e7e6f,
    0x1618b1660xfd2c1d050x848fd2c50xf6fb2299,
    0xf523f3570xa63276230x93a835310x56cccd02,
    0xacf081620x5a75ebb50x6e1636970x88d273cc,
    0xde9662920x81b949d00x4c50901b0x71c65614,
    0xe6c6c7bd0x327a140a0x45e1d0060xc3f27b9a,
    0xc9aa53fd0x62a80f000xbb25bfe20x35bdd2f6,
    0x711269050xb20402220xb6cbcf7c0xcd769c2b,
    0x53113ec00x1640e3d30x38abbd600x2547adf0,
    0xba38209c0xf746ce760x77afa1c50x20756060,
    0x85cbfe4e0x8ae88dd80x7aaaf9b00x4cf9aa7e,
    0x1948c25c0x02fb8a8c0x01c36ae40xd6ebe1f9,
    0x90d4f8690xa65cdea00x3f09252d0xc208e69f,
    0xb74e61320xce77e25b0x578fdfe30x3ac372e6
  };

  // bcrypt IV: "OrpheanBeholderScryDoubt"
  static private final int bf_crypt_ciphertext[] {
    0x4f7270680x65616e420x65686f6c,
    0x646572530x637279440x6f756274
  };

  // Table for Base64 encoding
  static private final char base64_code[] {
    '.''/''A''B''C''D''E''F''G''H''I''J',
    'K''L''M''N''O''P''Q''R''S''T''U''V',
    'W''X''Y''Z''a''b''c''d''e''f''g''h',
    'i''j''k''l''m''n''o''p''q''r''s''t',
    'u''v''w''x''y''z''0''1''2''3''4''5',
    '6''7''8''9'
  };

  // Table for Base64 decoding
  static private final byte index_64[] {
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1015455,
    5657585960616263, -1, -1,
    -1, -1, -1, -1, -123456,
    78910111213141516,
    1718192021222324252627,
    -1, -1, -1, -1, -1, -1282930,
    31323334353637383940,
    41424344454647484950,
    515253, -1, -1, -1, -1, -1
  };

  // Expanded Blowfish key
  private int P[];
  private int S[];

  /**
   * Encode a byte array using bcrypt's slightly-modified base64
   * encoding scheme. Note that this is *not* compatible with
   * the standard MIME-base64 encoding.
   *
   @param d  the byte array to encode
   @param len  the number of bytes to encode
   @return  base64-encoded string
   @exception IllegalArgumentException if the length is invalid
   */
  private static String encode_base64(byte d[]int len)
    throws IllegalArgumentException {
    int off = 0;
    StringBuffer rs = new StringBuffer();
    int c1, c2;

    if (len <= || len > d.length)
      throw new IllegalArgumentException ("Invalid len");

    while (off < len) {
      c1 = d[off++0xff;
      rs.append(base64_code[(c1 >> 20x3f]);
      c1 = (c1 & 0x03<< 4;
      if (off >= len) {
        rs.append(base64_code[c1 & 0x3f]);
        break;
      }
      c2 = d[off++0xff;
      c1 |= (c2 >> 40x0f;
      rs.append(base64_code[c1 & 0x3f]);
      c1 = (c2 & 0x0f<< 2;
      if (off >= len) {
        rs.append(base64_code[c1 & 0x3f]);
        break;
      }
      c2 = d[off++0xff;
      c1 |= (c2 >> 60x03;
      rs.append(base64_code[c1 & 0x3f]);
      rs.append(base64_code[c2 & 0x3f]);
    }
    return rs.toString();
  }

  /**
   * Look up the 3 bits base64-encoded by the specified character,
   * range-checking againt conversion table
   @param x  the base64-encoded value
   @return  the decoded value of x
   */
  private static byte char64(char x) {
    if ((int)x < || (int)x > index_64.length)
      return -1;
    return index_64[(int)x];
  }

  /**
   * Decode a string encoded using bcrypt's base64 scheme to a
   * byte array. Note that this is *not* compatible with
   * the standard MIME-base64 encoding.
   @param s  the string to decode
   @param maxolen  the maximum number of bytes to decode
   @return  an array containing the decoded bytes
   @throws IllegalArgumentException if maxolen is invalid
   */
  private static byte[] decode_base64(String s, int maxolen)
    throws IllegalArgumentException {
    StringBuffer rs = new StringBuffer();
    int off = 0, slen = s.length(), olen = 0;
    byte ret[];
    byte c1, c2, c3, c4, o;

    if (maxolen <= 0)
      throw new IllegalArgumentException ("Invalid maxolen");

    while (off < slen - && olen < maxolen) {
      c1 = char64(s.charAt(off++));
      c2 = char64(s.charAt(off++));
      if (c1 == -|| c2 == -1)
        break;
      o = (byte)(c1 << 2);
      o |= (c2 & 0x30>> 4;
      rs.append((char)o);
      if (++olen >= maxolen || off >= slen)
        break;
      c3 = char64(s.charAt(off++));
      if (c3 == -1)
        break;
      o = (byte)((c2 & 0x0f<< 4);
      o |= (c3 & 0x3c>> 2;
      rs.append((char)o);
      if (++olen >= maxolen || off >= slen)
        break;
      c4 = char64(s.charAt(off++));
      o = (byte)((c3 & 0x03<< 6);
      o |= c4;
      rs.append((char)o);
      ++olen;
    }

    ret = new byte[olen];
    for (off = 0; off < olen; off++)
      ret[off(byte)rs.charAt(off);
    return ret;
  }

  /**
   * Blowfish encipher a single 64-bit block encoded as
   * two 32-bit halves
   @param lr  an array containing the two 32-bit half blocks
   @param off  the position in the array of the blocks
   */
  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 >> 240xff];
      n += S[0x100 ((l >> 160xff)];
      n ^= S[0x200 ((l >> 80xff)];
      n += S[0x300 (l & 0xff)];
      r ^= n ^ P[++i];

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

  /**
   * Cycically extract a word of key material
   @param data  the string to extract the data from
   @param offp  a "pointer" (as a one-entry array) to the
   * current offset into data
   @return  the next word of material from data
   */
  private static int streamtoword(byte data[]int offp[]) {
    int i;
    int word = 0;
    int off = offp[0];

    for (i = 0; i < 4; i++) {
      word = (word << 8(data[off0xff);
      off = (off + 1% data.length;
    }

    offp[0= off;
    return word;
  }

  /**
   * Initialise the Blowfish key schedule
   */
  private void init_key() {
    P = (int[])P_orig.clone();
    S = (int[])S_orig.clone();
  }

  /**
   * Key the Blowfish cipher
   @param key  an array containing the key
   */
  private void key(byte key[]) {
    int i;
    int koffp[] };
    int lr[] 0};
    int plen = P.length, slen = S.length;

    for (i = 0; i < plen; i++)
      P[i= P[i^ streamtoword(key, koffp);

    for (i = 0; i < plen; i += 2) {
      encipher(lr, 0);
      P[i= lr[0];
      P[i + 1= lr[1];
    }

    for (i = 0; i < slen; i += 2) {
      encipher(lr, 0);
      S[i= lr[0];
      S[i + 1= lr[1];
    }
  }

  /**
   * Perform the "enhanced key schedule" step described by
   * Provos and Mazieres in "A Future-Adaptable Password Scheme"
   * http://www.openbsd.org/papers/bcrypt-paper.ps
   @param data  salt information
   @param key  password information
   */
  private void ekskey(byte data[]byte key[]) {
    int i;
    int koffp[] }, doffp[] };
    int lr[] 0};
    int plen = P.length, slen = S.length;

    for (i = 0; i < plen; i++)
      P[i= P[i^ streamtoword(key, koffp);

    for (i = 0; i < plen; i += 2) {
      lr[0^= streamtoword(data, doffp);
      lr[1^= streamtoword(data, doffp);
      encipher(lr, 0);
      P[i= lr[0];
      P[i + 1= lr[1];
    }

    for (i = 0; i < slen; i += 2) {
      lr[0^= streamtoword(data, doffp);
      lr[1^= streamtoword(data, doffp);
      encipher(lr, 0);
      S[i= lr[0];
      S[i + 1= lr[1];
    }
  }

  /**
   * Perform the central password hashing step in the
   * bcrypt scheme
   @param password  the password to hash
   @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
   @return  an array containing the binary hashed password
   */
  private byte[] crypt_raw(byte password[]byte salt[]int log_rounds) {
    int rounds, i, j;
    int cdata[] (int[])bf_crypt_ciphertext.clone();
    int clen = cdata.length;
    byte ret[];

    if (log_rounds < || log_rounds > 31)
      throw new IllegalArgumentException ("Bad number of rounds");
    rounds = << log_rounds;
    if (salt.length != BCRYPT_SALT_LEN)
      throw new IllegalArgumentException ("Bad salt length");

    init_key();
    ekskey(salt, password);
    for (i = 0; i < rounds; i++) {
      key(password);
      key(salt);
    }

    for (i = 0; i < 64; i++) {
      for (j = 0; j < (clen >> 1); j++)
        encipher(cdata, j << 1);
    }

    ret = new byte[clen * 4];
    for (i = 0, j = 0; i < clen; i++) {
      ret[j++(byte)((cdata[i>> 240xff);
      ret[j++(byte)((cdata[i>> 160xff);
      ret[j++(byte)((cdata[i>> 80xff);
      ret[j++(byte)(cdata[i0xff);
    }
    return ret;
  }

  /**
   * 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) {
    BCrypt B;
    String real_salt;
    byte passwordb[], saltb[], hashed[];
    char minor = (char)0;
    int rounds, off = 0;
    StringBuffer rs = new StringBuffer();

    if (salt.charAt(0!= '$' || salt.charAt(1!= '2')
      throw new IllegalArgumentException ("Invalid salt version");
    if (salt.charAt(2== '$')
      off = 3;
    else {
      minor = salt.charAt(2);
      if (minor != 'a' || salt.charAt(3!= '$')
        throw new IllegalArgumentException ("Invalid salt revision");
      off = 4;
    }

    // Extract number of rounds
    if (salt.charAt(off + 2'$')
      throw new IllegalArgumentException ("Missing salt rounds");
    rounds = Integer.parseInt(salt.substring(off, off + 2));

    real_salt = salt.substring(off + 3, off + 25);
    try {
      passwordb = (password + (minor >= 'a' "\000" "")).getBytes("UTF-8");
    catch (UnsupportedEncodingException uee) {
      throw new AssertionError("UTF-8 is not supported");
    }

    saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);

    B = new BCrypt();
    hashed = B.crypt_raw(passwordb, saltb, rounds);

    rs.append("$2");
    if (minor >= 'a')
      rs.append(minor);
    rs.append("$");
    if (rounds < 10)
      rs.append("0");
    rs.append(Integer.toString(rounds));
    rs.append("$");
    rs.append(encode_base64(saltb, saltb.length));
    rs.append(encode_base64(hashed,
        bf_crypt_ciphertext.length * 1));
    return rs.toString();
  }

  /**
   * 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) {
    StringBuffer rs = new StringBuffer();
    byte rnd[] new byte[BCRYPT_SALT_LEN];

    random.nextBytes(rnd);

    rs.append("$2a$");
    if (log_rounds < 10)
      rs.append("0");
    rs.append(Integer.toString(log_rounds));
    rs.append("$");
    rs.append(encode_base64(rnd, rnd.length));
    return rs.toString();
  }

  /**
   * 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
   */
  public static String gensalt(int log_rounds) {
    return gensalt(log_rounds, new SecureRandom());
  }

  /**
   * 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
   */
  public static String gensalt() {
    return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);
  }

  /**
   * Check that a plaintext password matches a previously hashed
   * one
   @param plaintext  the plaintext password to verify
   @param hashed  the previously-hashed password
   @return  true if the passwords match, false otherwise
   */
  public static boolean checkpw(String plaintext, String hashed) {
    return (hashed.compareTo(hashpw(plaintext, hashed)) == 0);
  }
}