com.helger.security.bcrypt.BCrypt Maven / Gradle / Ivy
Show all versions of ph-security Show documentation
/*
* Copyright (C) 2014-2024 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.helger.security.bcrypt;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import com.helger.commons.exception.InitializationException;
import com.helger.commons.random.VerySecureRandom;
//Copyright (c) 2006 Damien Miller
//
//Permission to use, copy, modify, and distribute this software for any
//purpose with or without fee is hereby granted, provided that the above
//copyright notice and this permission notice appear in all copies.
//
//THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
//WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
//MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
//ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
//WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
//ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
//OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/**
* BCrypt implements OpenBSD-style Blowfish password hashing using the scheme
* described in "A Future-Adaptable Password Scheme" by Niels Provos and David
* Mazieres.
*
* 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.
*
* Usage is really simple. To hash a password for the first time, call the
* hashpw method with a random salt, like this:
*
*
* String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
*
*
* To check whether a plaintext password matches one that has been hashed
* previously, use the checkpw method:
*
*
* if (BCrypt.checkpw(candidate_password, stored_hash))
* System.out.println("It matches");
* else
* System.out.println("It does not match");
*
*
* The gensalt() method takes an optional parameter (log_rounds) that determines
* the computational complexity of the hashing:
*
*
* String strong_salt = BCrypt.gensalt(10)
* String stronger_salt = BCrypt.gensalt(12)
*
*
* 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 30.
*
* @author Damien Miller
* @version 0.2
*/
public class BCrypt
{
// BCrypt parameters
public static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
public 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 = { 0x243f6a88,
0x85a308d3,
0x13198a2e,
0x03707344,
0xa4093822,
0x299f31d0,
0x082efa98,
0xec4e6c89,
0x452821e6,
0x38d01377,
0xbe5466cf,
0x34e90c6c,
0xc0ac29b7,
0xc97c50dd,
0x3f84d5b5,
0xb5470917,
0x9216d5d9,
0x8979fb1b };
private static final int [] S_ORIG = { 0xd1310ba6,
0x98dfb5ac,
0x2ffd72db,
0xd01adfb7,
0xb8e1afed,
0x6a267e96,
0xba7c9045,
0xf12c7f99,
0x24a19947,
0xb3916cf7,
0x0801f2e2,
0x858efc16,
0x636920d8,
0x71574e69,
0xa458fea3,
0xf4933d7e,
0x0d95748f,
0x728eb658,
0x718bcd58,
0x82154aee,
0x7b54a41d,
0xc25a59b5,
0x9c30d539,
0x2af26013,
0xc5d1b023,
0x286085f0,
0xca417918,
0xb8db38ef,
0x8e79dcb0,
0x603a180e,
0x6c9e0e8b,
0xb01e8a3e,
0xd71577c1,
0xbd314b27,
0x78af2fda,
0x55605c60,
0xe65525f3,
0xaa55ab94,
0x57489862,
0x63e81440,
0x55ca396a,
0x2aab10b6,
0xb4cc5c34,
0x1141e8ce,
0xa15486af,
0x7c72e993,
0xb3ee1411,
0x636fbc2a,
0x2ba9c55d,
0x741831f6,
0xce5c3e16,
0x9b87931e,
0xafd6ba33,
0x6c24cf5c,
0x7a325381,
0x28958677,
0x3b8f4898,
0x6b4bb9af,
0xc4bfe81b,
0x66282193,
0x61d809cc,
0xfb21a991,
0x487cac60,
0x5dec8032,
0xef845d5d,
0xe98575b1,
0xdc262302,
0xeb651b88,
0x23893e81,
0xd396acc5,
0x0f6d6ff3,
0x83f44239,
0x2e0b4482,
0xa4842004,
0x69c8f04a,
0x9e1f9b5e,
0x21c66842,
0xf6e96c9a,
0x670c9c61,
0xabd388f0,
0x6a51a0d2,
0xd8542f68,
0x960fa728,
0xab5133a3,
0x6eef0b6c,
0x137a3be4,
0xba3bf050,
0x7efb2a98,
0xa1f1651d,
0x39af0176,
0x66ca593e,
0x82430e88,
0x8cee8619,
0x456f9fb4,
0x7d84a5c3,
0x3b8b5ebe,
0xe06f75d8,
0x85c12073,
0x401a449f,
0x56c16aa6,
0x4ed3aa62,
0x363f7706,
0x1bfedf72,
0x429b023d,
0x37d0d724,
0xd00a1248,
0xdb0fead3,
0x49f1c09b,
0x075372c9,
0x80991b7b,
0x25d479d8,
0xf6e8def7,
0xe3fe501a,
0xb6794c3b,
0x976ce0bd,
0x04c006ba,
0xc1a94fb6,
0x409f60c4,
0x5e5c9ec2,
0x196a2463,
0x68fb6faf,
0x3e6c53b5,
0x1339b2eb,
0x3b52ec6f,
0x6dfc511f,
0x9b30952c,
0xcc814544,
0xaf5ebd09,
0xbee3d004,
0xde334afd,
0x660f2807,
0x192e4bb3,
0xc0cba857,
0x45c8740f,
0xd20b5f39,
0xb9d3fbdb,
0x5579c0bd,
0x1a60320a,
0xd6a100c6,
0x402c7279,
0x679f25fe,
0xfb1fa3cc,
0x8ea5e9f8,
0xdb3222f8,
0x3c7516df,
0xfd616b15,
0x2f501ec8,
0xad0552ab,
0x323db5fa,
0xfd238760,
0x53317b48,
0x3e00df82,
0x9e5c57bb,
0xca6f8ca0,
0x1a87562e,
0xdf1769db,
0xd542a8f6,
0x287effc3,
0xac6732c6,
0x8c4f5573,
0x695b27b0,
0xbbca58c8,
0xe1ffa35d,
0xb8f011a0,
0x10fa3d98,
0xfd2183b8,
0x4afcb56c,
0x2dd1d35b,
0x9a53e479,
0xb6f84565,
0xd28e49bc,
0x4bfb9790,
0xe1ddf2da,
0xa4cb7e33,
0x62fb1341,
0xcee4c6e8,
0xef20cada,
0x36774c01,
0xd07e9efe,
0x2bf11fb4,
0x95dbda4d,
0xae909198,
0xeaad8e71,
0x6b93d5a0,
0xd08ed1d0,
0xafc725e0,
0x8e3c5b2f,
0x8e7594b7,
0x8ff6e2fb,
0xf2122b64,
0x8888b812,
0x900df01c,
0x4fad5ea0,
0x688fc31c,
0xd1cff191,
0xb3a8c1ad,
0x2f2f2218,
0xbe0e1777,
0xea752dfe,
0x8b021fa1,
0xe5a0cc0f,
0xb56f74e8,
0x18acf3d6,
0xce89e299,
0xb4a84fe0,
0xfd13e0b7,
0x7cc43b81,
0xd2ada8d9,
0x165fa266,
0x80957705,
0x93cc7314,
0x211a1477,
0xe6ad2065,
0x77b5fa86,
0xc75442f5,
0xfb9d35cf,
0xebcdaf0c,
0x7b3e89a0,
0xd6411bd3,
0xae1e7e49,
0x00250e2d,
0x2071b35e,
0x226800bb,
0x57b8e0af,
0x2464369b,
0xf009b91e,
0x5563911d,
0x59dfa6aa,
0x78c14389,
0xd95a537f,
0x207d5ba2,
0x02e5b9c5,
0x83260376,
0x6295cfa9,
0x11c81968,
0x4e734a41,
0xb3472dca,
0x7b14a94a,
0x1b510052,
0x9a532915,
0xd60f573f,
0xbc9bc6e4,
0x2b60a476,
0x81e67400,
0x08ba6fb5,
0x571be91f,
0xf296ec6b,
0x2a0dd915,
0xb6636521,
0xe7b9f9b6,
0xff34052e,
0xc5855664,
0x53b02d5d,
0xa99f8fa1,
0x08ba4799,
0x6e85076a,
0x4b7a70e9,
0xb5b32944,
0xdb75092e,
0xc4192623,
0xad6ea6b0,
0x49a7df7d,
0x9cee60b8,
0x8fedb266,
0xecaa8c71,
0x699a17ff,
0x5664526c,
0xc2b19ee1,
0x193602a5,
0x75094c29,
0xa0591340,
0xe4183a3e,
0x3f54989a,
0x5b429d65,
0x6b8fe4d6,
0x99f73fd6,
0xa1d29c07,
0xefe830f5,
0x4d2d38e6,
0xf0255dc1,
0x4cdd2086,
0x8470eb26,
0x6382e9c6,
0x021ecc5e,
0x09686b3f,
0x3ebaefc9,
0x3c971814,
0x6b6a70a1,
0x687f3584,
0x52a0e286,
0xb79c5305,
0xaa500737,
0x3e07841c,
0x7fdeae5c,
0x8e7d44ec,
0x5716f2b8,
0xb03ada37,
0xf0500c0d,
0xf01c1f04,
0x0200b3ff,
0xae0cf51a,
0x3cb574b2,
0x25837a58,
0xdc0921bd,
0xd19113f9,
0x7ca92ff6,
0x94324773,
0x22f54701,
0x3ae5e581,
0x37c2dadc,
0xc8b57634,
0x9af3dda7,
0xa9446146,
0x0fd0030e,
0xecc8c73e,
0xa4751e41,
0xe238cd99,
0x3bea0e2f,
0x3280bba1,
0x183eb331,
0x4e548b38,
0x4f6db908,
0x6f420d03,
0xf60a04bf,
0x2cb81290,
0x24977c79,
0x5679b072,
0xbcaf89af,
0xde9a771f,
0xd9930810,
0xb38bae12,
0xdccf3f2e,
0x5512721f,
0x2e6b7124,
0x501adde6,
0x9f84cd87,
0x7a584718,
0x7408da17,
0xbc9f9abc,
0xe94b7d8c,
0xec7aec3a,
0xdb851dfa,
0x63094366,
0xc464c3d2,
0xef1c1847,
0x3215d908,
0xdd433b37,
0x24c2ba16,
0x12a14d43,
0x2a65c451,
0x50940002,
0x133ae4dd,
0x71dff89e,
0x10314e55,
0x81ac77d6,
0x5f11199b,
0x043556f1,
0xd7a3c76b,
0x3c11183b,
0x5924a509,
0xf28fe6ed,
0x97f1fbfa,
0x9ebabf2c,
0x1e153c6e,
0x86e34570,
0xeae96fb1,
0x860e5e0a,
0x5a3e2ab3,
0x771fe71c,
0x4e3d06fa,
0x2965dcb9,
0x99e71d0f,
0x803e89d6,
0x5266c825,
0x2e4cc978,
0x9c10b36a,
0xc6150eba,
0x94e2ea78,
0xa5fc3c53,
0x1e0a2df4,
0xf2f74ea7,
0x361d2b3d,
0x1939260f,
0x19c27960,
0x5223a708,
0xf71312b6,
0xebadfe6e,
0xeac31f66,
0xe3bc4595,
0xa67bc883,
0xb17f37d1,
0x018cff28,
0xc332ddef,
0xbe6c5aa5,
0x65582185,
0x68ab9802,
0xeecea50f,
0xdb2f953b,
0x2aef7dad,
0x5b6e2f84,
0x1521b628,
0x29076170,
0xecdd4775,
0x619f1510,
0x13cca830,
0xeb61bd96,
0x0334fe1e,
0xaa0363cf,
0xb5735c90,
0x4c70a239,
0xd59e9e0b,
0xcbaade14,
0xeecc86bc,
0x60622ca7,
0x9cab5cab,
0xb2f3846e,
0x648b1eaf,
0x19bdf0ca,
0xa02369b9,
0x655abb50,
0x40685a32,
0x3c2ab4b3,
0x319ee9d5,
0xc021b8f7,
0x9b540b19,
0x875fa099,
0x95f7997e,
0x623d7da8,
0xf837889a,
0x97e32d77,
0x11ed935f,
0x16681281,
0x0e358829,
0xc7e61fd6,
0x96dedfa1,
0x7858ba99,
0x57f584a5,
0x1b227263,
0x9b83c3ff,
0x1ac24696,
0xcdb30aeb,
0x532e3054,
0x8fd948e4,
0x6dbc3128,
0x58ebf2ef,
0x34c6ffea,
0xfe28ed61,
0xee7c3c73,
0x5d4a14d9,
0xe864b7e3,
0x42105d14,
0x203e13e0,
0x45eee2b6,
0xa3aaabea,
0xdb6c4f15,
0xfacb4fd0,
0xc742f442,
0xef6abbb5,
0x654f3b1d,
0x41cd2105,
0xd81e799e,
0x86854dc7,
0xe44b476a,
0x3d816250,
0xcf62a1f2,
0x5b8d2646,
0xfc8883a0,
0xc1c7b6a3,
0x7f1524c3,
0x69cb7492,
0x47848a0b,
0x5692b285,
0x095bbf00,
0xad19489d,
0x1462b174,
0x23820e00,
0x58428d2a,
0x0c55f5ea,
0x1dadf43e,
0x233f7061,
0x3372f092,
0x8d937e41,
0xd65fecf1,
0x6c223bdb,
0x7cde3759,
0xcbee7460,
0x4085f2a7,
0xce77326e,
0xa6078084,
0x19f8509e,
0xe8efd855,
0x61d99735,
0xa969a7aa,
0xc50c06c2,
0x5a04abfc,
0x800bcadc,
0x9e447a2e,
0xc3453484,
0xfdd56705,
0x0e1e9ec9,
0xdb73dbd3,
0x105588cd,
0x675fda79,
0xe3674340,
0xc5c43465,
0x713e38d8,
0x3d28f89e,
0xf16dff20,
0x153e21e7,
0x8fb03d4a,
0xe6e39f2b,
0xdb83adf7,
0xe93d5a68,
0x948140f7,
0xf64c261c,
0x94692934,
0x411520f7,
0x7602d4f7,
0xbcf46b2e,
0xd4a20068,
0xd4082471,
0x3320f46a,
0x43b7d4b7,
0x500061af,
0x1e39f62e,
0x97244546,
0x14214f74,
0xbf8b8840,
0x4d95fc1d,
0x96b591af,
0x70f4ddd3,
0x66a02f45,
0xbfbc09ec,
0x03bd9785,
0x7fac6dd0,
0x31cb8504,
0x96eb27b3,
0x55fd3941,
0xda2547e6,
0xabca0a9a,
0x28507825,
0x530429f4,
0x0a2c86da,
0xe9b66dfb,
0x68dc1462,
0xd7486900,
0x680ec0a4,
0x27a18dee,
0x4f3ffea2,
0xe887ad8c,
0xb58ce006,
0x7af4d6b6,
0xaace1e7c,
0xd3375fec,
0xce78a399,
0x406b2a42,
0x20fe9e35,
0xd9f385b9,
0xee39d7ab,
0x3b124e8b,
0x1dc9faf7,
0x4b6d1856,
0x26a36631,
0xeae397b2,
0x3a6efa74,
0xdd5b4332,
0x6841e7f7,
0xca7820fb,
0xfb0af54e,
0xd8feb397,
0x454056ac,
0xba489527,
0x55533a3a,
0x20838d87,
0xfe6ba9b7,
0xd096954b,
0x55a867bc,
0xa1159a58,
0xcca92963,
0x99e1db33,
0xa62a4a56,
0x3f3125f9,
0x5ef47e1c,
0x9029317c,
0xfdf8e802,
0x04272f70,
0x80bb155c,
0x05282ce3,
0x95c11548,
0xe4c66d22,
0x48c1133f,
0xc70f86dc,
0x07f9c9ee,
0x41041f0f,
0x404779a4,
0x5d886e17,
0x325f51eb,
0xd59bc0d1,
0xf2bcc18f,
0x41113564,
0x257b7834,
0x602a9c60,
0xdff8e8a3,
0x1f636c1b,
0x0e12b4c2,
0x02e1329e,
0xaf664fd1,
0xcad18115,
0x6b2395e0,
0x333e92e1,
0x3b240b62,
0xeebeb922,
0x85b2a20e,
0xe6ba0d99,
0xde720c8c,
0x2da2f728,
0xd0127845,
0x95b794fd,
0x647d0862,
0xe7ccf5f0,
0x5449a36f,
0x877d48fa,
0xc39dfd27,
0xf33e8d1e,
0x0a476341,
0x992eff74,
0x3a6f6eab,
0xf4f8fd37,
0xa812dc60,
0xa1ebddf8,
0x991be14c,
0xdb6e6b0d,
0xc67b5510,
0x6d672c37,
0x2765d43b,
0xdcd0e804,
0xf1290dc7,
0xcc00ffa3,
0xb5390f92,
0x690fed0b,
0x667b9ffb,
0xcedb7d9c,
0xa091cf0b,
0xd9155ea3,
0xbb132f88,
0x515bad24,
0x7b9479bf,
0x763bd6eb,
0x37392eb3,
0xcc115979,
0x8026e297,
0xf42e312d,
0x6842ada7,
0xc66a2b3b,
0x12754ccc,
0x782ef11c,
0x6a124237,
0xb79251e7,
0x06a1bbe6,
0x4bfb6350,
0x1a6b1018,
0x11caedfa,
0x3d25bdd8,
0xe2e1c3c9,
0x44421659,
0x0a121386,
0xd90cec6e,
0xd5abea2a,
0x64af674e,
0xda86a85f,
0xbebfe988,
0x64e4c3fe,
0x9dbc8057,
0xf0f7c086,
0x60787bf8,
0x6003604d,
0xd1fd8346,
0xf6381fb0,
0x7745ae04,
0xd736fccc,
0x83426b33,
0xf01eab71,
0xb0804187,
0x3c005e5f,
0x77a057be,
0xbde8ae24,
0x55464299,
0xbf582e61,
0x4e58f48f,
0xf2ddfda2,
0xf474ef38,
0x8789bdc2,
0x5366f9c3,
0xc8b38e74,
0xb475f255,
0x46fcd9b9,
0x7aeb2661,
0x8b1ddf84,
0x846a0e79,
0x915f95e2,
0x466e598e,
0x20b45770,
0x8cd55591,
0xc902de4c,
0xb90bace1,
0xbb8205d0,
0x11a86248,
0x7574a99e,
0xb77f19b6,
0xe0a9dc09,
0x662d09a1,
0xc4324633,
0xe85a1f02,
0x09f0be8c,
0x4a99a025,
0x1d6efe10,
0x1ab93d1d,
0x0ba5a4df,
0xa186f20f,
0x2868f169,
0xdcb7da83,
0x573906fe,
0xa1e2ce9b,
0x4fcd7f52,
0x50115e01,
0xa70683fa,
0xa002b5c4,
0x0de6d027,
0x9af88c27,
0x773f8641,
0xc3604c06,
0x61a806b5,
0xf0177a28,
0xc0f586e0,
0x006058aa,
0x30dc7d62,
0x11e69ed7,
0x2338ea63,
0x53c2dd94,
0xc2c21634,
0xbbcbee56,
0x90bcb6de,
0xebfc7da1,
0xce591d76,
0x6f05e409,
0x4b7c0188,
0x39720a3d,
0x7c927c24,
0x86e3725f,
0x724d9db9,
0x1ac15bb4,
0xd39eb8fc,
0xed545578,
0x08fca5b5,
0xd83d7cd3,
0x4dad0fc4,
0x1e50ef5e,
0xb161e6f8,
0xa28514d9,
0x6c51133c,
0x6fd5c7e7,
0x56e14ec4,
0x362abfce,
0xddc6c837,
0xd79a3234,
0x92638212,
0x670efa8e,
0x406000e0,
0x3a39ce37,
0xd3faf5cf,
0xabc27737,
0x5ac52d1b,
0x5cb0679e,
0x4fa33742,
0xd3822740,
0x99bc9bbe,
0xd5118e9d,
0xbf0f7315,
0xd62d1c7e,
0xc700c47b,
0xb78c1b6b,
0x21a19045,
0xb26eb1be,
0x6a366eb4,
0x5748ab2f,
0xbc946e79,
0xc6a376d2,
0x6549c2c8,
0x530ff8ee,
0x468dde7d,
0xd5730a1d,
0x4cd04dc6,
0x2939bbdb,
0xa9ba4650,
0xac9526e8,
0xbe5ee304,
0xa1fad5f0,
0x6a2d519a,
0x63ef8ce2,
0x9a86ee22,
0xc089c2b8,
0x43242ef6,
0xa51e03aa,
0x9cf2d0a4,
0x83c061ba,
0x9be96a4d,
0x8fe51550,
0xba645bd6,
0x2826a2f9,
0xa73a3ae1,
0x4ba99586,
0xef5562e9,
0xc72fefd3,
0xf752f7da,
0x3f046f69,
0x77fa0a59,
0x80e4a915,
0x87b08601,
0x9b09e6ad,
0x3b3ee593,
0xe990fd5a,
0x9e34d797,
0x2cf0b7d9,
0x022b8b51,
0x96d5ac3a,
0x017da67d,
0xd1cf3ed6,
0x7c7d2d28,
0x1f9f25cf,
0xadf2b89b,
0x5ad6b472,
0x5a88f54c,
0xe029ac71,
0xe019a5e6,
0x47b0acfd,
0xed93fa9b,
0xe8d3c48d,
0x283b57cc,
0xf8d56629,
0x79132e28,
0x785f0191,
0xed756055,
0xf7960e44,
0xe3d35e8c,
0x15056dd4,
0x88f46dba,
0x03a16125,
0x0564f0bd,
0xc3eb9e15,
0x3c9057a2,
0x97271aec,
0xa93a072a,
0x1b3f6d9b,
0x1e6321f5,
0xf59c66fb,
0x26dcf319,
0x7533d928,
0xb155fdf5,
0x03563482,
0x8aba3cbb,
0x28517711,
0xc20ad9f8,
0xabcc5167,
0xccad925f,
0x4de81751,
0x3830dc8e,
0x379d5862,
0x9320f991,
0xea7a90c2,
0xfb3e7bce,
0x5121ce64,
0x774fbe32,
0xa8b6e37e,
0xc3293d46,
0x48de5369,
0x6413e680,
0xa2ae0810,
0xdd6db224,
0x69852dfd,
0x09072166,
0xb39a460a,
0x6445c0dd,
0x586cdecf,
0x1c20c8ae,
0x5bbef7dd,
0x1b588d40,
0xccd2017f,
0x6bb4e3bb,
0xdda26a7e,
0x3a59ff45,
0x3e350a44,
0xbcb4cdd5,
0x72eacea8,
0xfa6484bb,
0x8d6612ae,
0xbf3c6f47,
0xd29be463,
0x542f5d9e,
0xaec2771b,
0xf64e6370,
0x740e0d8d,
0xe75b1357,
0xf8721671,
0xaf537d5d,
0x4040cb08,
0x4eb4e2cc,
0x34d2466a,
0x0115af84,
0xe1b00428,
0x95983a1d,
0x06b89fb4,
0xce6ea048,
0x6f3f3b82,
0x3520ab82,
0x011a1d4b,
0x277227f8,
0x611560b1,
0xe7933fdc,
0xbb3a792b,
0x344525bd,
0xa08839e1,
0x51ce794b,
0x2f32c9b7,
0xa01fbac9,
0xe01cc87e,
0xbcc7d1f6,
0xcf0111c3,
0xa1e8aac7,
0x1a908749,
0xd44fbd9a,
0xd0dadecb,
0xd50ada38,
0x0339c32a,
0xc6913667,
0x8df9317c,
0xe0b12b4f,
0xf79e59b7,
0x43f5bb3a,
0xf2d519ff,
0x27d9459c,
0xbf97222c,
0x15e6fc2a,
0x0f91fc71,
0x9b941525,
0xfae59361,
0xceb69ceb,
0xc2a86459,
0x12baa8d1,
0xb6c1075e,
0xe3056a0c,
0x10d25065,
0xcb03a442,
0xe0ec6e0e,
0x1698db3b,
0x4c98a0be,
0x3278e964,
0x9f1f9532,
0xe0d392df,
0xd3a0342b,
0x8971f21e,
0x1b0a7441,
0x4ba3348c,
0xc5be7120,
0xc37632d8,
0xdf359f8d,
0x9b992f2e,
0xe60b6f47,
0x0fe3f11d,
0xe54cda54,
0x1edad891,
0xce6279cf,
0xcd3e7e6f,
0x1618b166,
0xfd2c1d05,
0x848fd2c5,
0xf6fb2299,
0xf523f357,
0xa6327623,
0x93a83531,
0x56cccd02,
0xacf08162,
0x5a75ebb5,
0x6e163697,
0x88d273cc,
0xde966292,
0x81b949d0,
0x4c50901b,
0x71c65614,
0xe6c6c7bd,
0x327a140a,
0x45e1d006,
0xc3f27b9a,
0xc9aa53fd,
0x62a80f00,
0xbb25bfe2,
0x35bdd2f6,
0x71126905,
0xb2040222,
0xb6cbcf7c,
0xcd769c2b,
0x53113ec0,
0x1640e3d3,
0x38abbd60,
0x2547adf0,
0xba38209c,
0xf746ce76,
0x77afa1c5,
0x20756060,
0x85cbfe4e,
0x8ae88dd8,
0x7aaaf9b0,
0x4cf9aa7e,
0x1948c25c,
0x02fb8a8c,
0x01c36ae4,
0xd6ebe1f9,
0x90d4f869,
0xa65cdea0,
0x3f09252d,
0xc208e69f,
0xb74e6132,
0xce77e25b,
0x578fdfe3,
0x3ac372e6 };
// 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.
private static final int [] BF_CRYPT_CIPHERTEXT = { 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 };
// Table for Base64 encoding
private static 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
private static final byte [] BASE64_INDEX = { -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,
-1,
0,
1,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
-1,
-1,
-1,
-1,
-1,
-1,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
-1,
-1,
-1,
-1,
-1 };
static
{
if (BASE64_INDEX.length != 128)
throw new InitializationException ("Invalid array length " + BASE64_INDEX.length + "!");
}
// Expanded Blowfish key
private int [] m_aP;
private int [] m_aS;
/**
* 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 aToEncode
* the byte array to encode
* @param nLen
* the number of bytes to encode
* @return base64-encoded string
* @exception IllegalArgumentException
* if the length is invalid
*/
private static String _encode_base64 (final byte [] aToEncode, final int nLen)
{
if (nLen <= 0 || nLen > aToEncode.length)
throw new IllegalArgumentException ("Invalid len");
int nOfs = 0;
final StringBuilder rs = new StringBuilder (aToEncode.length * 2);
while (nOfs < nLen)
{
int c1 = aToEncode[nOfs++] & 0xff;
rs.append (BASE64_CODE[(c1 >> 2) & 0x3f]);
c1 = (c1 & 0x03) << 4;
if (nOfs >= nLen)
{
rs.append (BASE64_CODE[c1 & 0x3f]);
break;
}
int c2 = aToEncode[nOfs++] & 0xff;
c1 |= (c2 >> 4) & 0x0f;
rs.append (BASE64_CODE[c1 & 0x3f]);
c1 = (c2 & 0x0f) << 2;
if (nOfs >= nLen)
{
rs.append (BASE64_CODE[c1 & 0x3f]);
break;
}
c2 = aToEncode[nOfs++] & 0xff;
c1 |= (c2 >> 6) & 0x03;
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 (final char x)
{
if (x >= BASE64_INDEX.length)
return -1;
return BASE64_INDEX[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 sToDecode
* the string to decode
* @param nMaxLen
* the maximum number of bytes to decode
* @return an array containing the decoded bytes
* @throws IllegalArgumentException
* if nMaxLen is invalid
*/
private static byte [] _decode_base64 (final String sToDecode, final int nMaxLen)
{
if (nMaxLen <= 0)
throw new IllegalArgumentException ("Invalid maxlen");
final StringBuilder rs = new StringBuilder ();
int off = 0;
final int slen = sToDecode.length ();
int olen = 0;
while (off < slen - 1 && olen < nMaxLen)
{
final byte c1 = _char64 (sToDecode.charAt (off++));
final byte c2 = _char64 (sToDecode.charAt (off++));
if (c1 == -1 || c2 == -1)
break;
byte o = (byte) (c1 << 2);
o |= (c2 & 0x30) >> 4;
rs.append ((char) o);
++olen;
if (olen >= nMaxLen || off >= slen)
break;
final byte c3 = _char64 (sToDecode.charAt (off++));
if (c3 == -1)
break;
o = (byte) ((c2 & 0x0f) << 4);
o |= (c3 & 0x3c) >> 2;
rs.append ((char) o);
++olen;
if (olen >= nMaxLen || off >= slen)
break;
final byte c4 = _char64 (sToDecode.charAt (off++));
o = (byte) ((c3 & 0x03) << 6);
o |= c4;
rs.append ((char) o);
++olen;
}
final byte [] 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 nOfs
* the position in the array of the blocks
*/
private void _encipher (final int [] lr, final int nOfs)
{
int l = lr[nOfs];
int r = lr[nOfs + 1];
// For quicker local access
final int [] aS = m_aS;
final int [] aP = m_aP;
l ^= aP[0];
for (int i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;)
{
// Feistel substitution on left word
int n = aS[(l >> 24) & 0xff];
n += aS[0x100 | ((l >> 16) & 0xff)];
n ^= aS[0x200 | ((l >> 8) & 0xff)];
n += aS[0x300 | (l & 0xff)];
r ^= n ^ aP[++i];
// Feistel substitution on right word
n = aS[(r >> 24) & 0xff];
n += aS[0x100 | ((r >> 16) & 0xff)];
n ^= aS[0x200 | ((r >> 8) & 0xff)];
n += aS[0x300 | (r & 0xff)];
l ^= n ^ aP[++i];
}
lr[nOfs] = r ^ aP[BLOWFISH_NUM_ROUNDS + 1];
lr[nOfs + 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 (@Nonnull final byte [] data, @Nonnull final int [] offp)
{
int word = 0;
int off = offp[0];
for (int i = 0; i < 4; i++)
{
word = (word << 8) | (data[off] & 0xff);
off = (off + 1) % data.length;
}
offp[0] = off;
return word;
}
/**
* Initialise the Blowfish key schedule
*/
private void _initKey ()
{
m_aP = P_ORIG.clone ();
m_aS = S_ORIG.clone ();
}
/**
* Key the Blowfish cipher
*
* @param key
* an array containing the key
*/
private void _key (@Nonnull final byte [] key)
{
final int [] koffp = { 0 };
final int [] lr = { 0, 0 };
final int plen = m_aP.length;
final int slen = m_aS.length;
for (int i = 0; i < plen; i++)
m_aP[i] = m_aP[i] ^ _streamToWord (key, koffp);
for (int i = 0; i < plen; i += 2)
{
_encipher (lr, 0);
m_aP[i] = lr[0];
m_aP[i + 1] = lr[1];
}
for (int i = 0; i < slen; i += 2)
{
_encipher (lr, 0);
m_aS[i] = lr[0];
m_aS[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 (@Nonnull final byte [] data, @Nonnull final byte [] key)
{
// For quicker local access
final int [] aP = m_aP;
final int [] aS = m_aS;
final int [] koffp = { 0 };
final int [] doffp = { 0 };
final int [] lr = { 0, 0 };
final int plen = aP.length;
final int slen = aS.length;
for (int i = 0; i < plen; i++)
aP[i] = aP[i] ^ _streamToWord (key, koffp);
for (int i = 0; i < plen; i += 2)
{
lr[0] ^= _streamToWord (data, doffp);
lr[1] ^= _streamToWord (data, doffp);
_encipher (lr, 0);
aP[i] = lr[0];
aP[i + 1] = lr[1];
}
for (int i = 0; i < slen; i += 2)
{
lr[0] ^= _streamToWord (data, doffp);
lr[1] ^= _streamToWord (data, doffp);
_encipher (lr, 0);
aS[i] = lr[0];
aS[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 nLogRounds
* 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
*/
@Nonnull
public byte [] crypt_raw (final byte [] password, final byte [] salt, final int nLogRounds, final int [] cdata)
{
if (nLogRounds < 4 || nLogRounds > 30)
throw new IllegalArgumentException ("Bad number of rounds");
if (salt.length != BCRYPT_SALT_LEN)
throw new IllegalArgumentException ("Bad salt length");
final int nRounds = 1 << nLogRounds;
_initKey ();
_ekskey (salt, password);
for (int i = 0; i != nRounds; i++)
{
_key (password);
_key (salt);
}
final int clen = cdata.length;
for (int i = 0; i < 64; i++)
for (int j = 0; j < (clen >> 1); j++)
_encipher (cdata, j << 1);
final byte [] ret = new byte [clen * 4];
int j = 0;
for (int i = 0; i < clen; i++)
{
ret[j++] = (byte) ((cdata[i] >> 24) & 0xff);
ret[j++] = (byte) ((cdata[i] >> 16) & 0xff);
ret[j++] = (byte) ((cdata[i] >> 8) & 0xff);
ret[j++] = (byte) (cdata[i] & 0xff);
}
return ret;
}
/**
* Hash a password using the OpenBSD bcrypt scheme
*
* @param sPassword
* the password to hash
* @param sSalt
* the salt to hash with (perhaps generated using BCrypt.gensalt)
* @return the hashed password
*/
@Nonnull
public static String hashpw (@Nonnull final String sPassword, @Nonnull final String sSalt)
{
if (sSalt.charAt (0) != '$' || sSalt.charAt (1) != '2')
throw new IllegalArgumentException ("Invalid salt version");
char minor = (char) 0;
int off;
if (sSalt.charAt (2) == '$')
off = 3;
else
{
minor = sSalt.charAt (2);
if (minor != 'a' || sSalt.charAt (3) != '$')
throw new IllegalArgumentException ("Invalid salt revision");
off = 4;
}
// Extract number of rounds
if (sSalt.charAt (off + 2) > '$')
throw new IllegalArgumentException ("Missing salt rounds");
final int rounds = Integer.parseInt (sSalt.substring (off, off + 2));
final String sRealSalt = sSalt.substring (off + 3, off + 25);
final byte [] aPasswordb = (sPassword + (minor >= 'a' ? "\000" : "")).getBytes (StandardCharsets.UTF_8);
final byte [] aSaltb = _decode_base64 (sRealSalt, BCRYPT_SALT_LEN);
final BCrypt aBCrypt = new BCrypt ();
final byte [] hashed = aBCrypt.crypt_raw (aPasswordb, aSaltb, rounds, BF_CRYPT_CIPHERTEXT.clone ());
final StringBuilder aSB = new StringBuilder ();
aSB.append ("$2");
if (minor >= 'a')
aSB.append (minor);
aSB.append ('$');
if (rounds < 10)
aSB.append ('0');
else
if (rounds > 30)
throw new IllegalArgumentException ("rounds exceeds maximum (30)");
aSB.append (Integer.toString (rounds));
aSB.append ('$');
aSB.append (_encode_base64 (aSaltb, aSaltb.length));
aSB.append (_encode_base64 (hashed, BF_CRYPT_CIPHERTEXT.length * 4 - 1));
return aSB.toString ();
}
/**
* Generate a salt for use with the BCrypt.hashpw() method
*
* @param nLogRounds
* the log2 of the number of rounds of hashing to apply - the work
* factor therefore increases as 2**log_rounds.
* @param aRandom
* an instance of SecureRandom to use
* @return an encoded salt value
*/
@Nonnull
public static String gensalt (@Nonnegative final int nLogRounds, @Nonnull final SecureRandom aRandom)
{
final StringBuilder rs = new StringBuilder ();
final byte [] rnd = new byte [BCRYPT_SALT_LEN];
aRandom.nextBytes (rnd);
rs.append ("$2a$");
if (nLogRounds < 10)
rs.append ('0');
else
if (nLogRounds > 30)
throw new IllegalArgumentException ("log_rounds exceeds maximum (30)");
rs.append (Integer.toString (nLogRounds));
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
*/
@Nonnull
public static String gensalt (@Nonnegative final int log_rounds)
{
return gensalt (log_rounds, VerySecureRandom.getInstance ());
}
/**
* 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
*/
@Nonnull
public static String gensalt ()
{
return gensalt (GENSALT_DEFAULT_LOG2_ROUNDS);
}
/**
* Check that a plaintext password matches a previously hashed one
*
* @param sPlaintext
* the plaintext password to verify
* @param sHashed
* the previously-hashed password
* @return true
if the passwords match, false
* otherwise
*/
public static boolean checkpw (@Nonnull final String sPlaintext, @Nonnull final String sHashed)
{
final String sNewHashed = hashpw (sPlaintext, sHashed);
final byte [] aHashedBytes = sHashed.getBytes (StandardCharsets.UTF_8);
final byte [] aTryBytes = sNewHashed.getBytes (StandardCharsets.UTF_8);
if (aHashedBytes.length != aTryBytes.length)
return false;
byte ret = 0;
for (int i = 0; i < aTryBytes.length; i++)
ret |= aHashedBytes[i] ^ aTryBytes[i];
return ret == 0;
}
}