+//
+// 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.
+package bcrypt_jruby;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+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.
+ *
+ * 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 31.
+ *
+ * @author Damien Miller
+ * @version 0.3
+ */
+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[] = {
+ 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"
+ static private final int bf_crypt_ciphertext[] = {
+ 0x4f727068, 0x65616e42, 0x65686f6c,
+ 0x64657253, 0x63727944, 0x6f756274
+ };
+
+ // 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, -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 final int MIN_LOG_ROUNDS = 4;
+ static final int MAX_LOG_ROUNDS = 31;
+
+ // 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
+ * @param rs the destination buffer for the base64-encoded string
+ * @exception IllegalArgumentException if the length is invalid
+ */
+ static void encode_base64(byte d[], int len, StringBuilder rs)
+ throws IllegalArgumentException {
+ int off = 0;
+ int c1, c2;
+
+ if (len <= 0 || len > d.length) {
+ throw new IllegalArgumentException("Invalid len");
+ }
+
+ while (off < len) {
+ c1 = d[off++] & 0xff;
+ rs.append(base64_code[(c1 >> 2) & 0x3f]);
+ c1 = (c1 & 0x03) << 4;
+ if (off >= len) {
+ rs.append(base64_code[c1 & 0x3f]);
+ break;
+ }
+ c2 = d[off++] & 0xff;
+ c1 |= (c2 >> 4) & 0x0f;
+ 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 >> 6) & 0x03;
+ rs.append(base64_code[c1 & 0x3f]);
+ rs.append(base64_code[c2 & 0x3f]);
+ }
+ }
+
+ /**
+ * 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 < 0 || (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
+ */
+ static byte[] decode_base64(String s, int maxolen)
+ throws IllegalArgumentException {
+ StringBuilder rs = new StringBuilder();
+ 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 - 1 && olen < maxolen) {
+ c1 = char64(s.charAt(off++));
+ c2 = char64(s.charAt(off++));
+ if (c1 == -1 || 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 >> 24) & 0xff];
+ n += S[0x100 | ((l >> 16) & 0xff)];
+ n ^= S[0x200 | ((l >> 8) & 0xff)];
+ n += S[0x300 | (l & 0xff)];
+ r ^= n ^ P[++i];
+
+ // 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;
+ }
+
+ /**
+ * 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
+ * @param signp a "pointer" (as a one-entry array) to the
+ * cumulative flag for non-benign sign extension
+ * @return correct and buggy next word of material from data as int[2]
+ */
+ private static int[] streamtowords(byte data[], int offp[], int signp[]) {
+ int i;
+ int words[] = { 0, 0 };
+ int off = offp[0];
+ int sign = signp[0];
+
+ for (i = 0; i < 4; i++) {
+ words[0] = (words[0] << 8) | (data[off] & 0xff);
+ words[1] = (words[1] << 8) | (int) data[off]; // sign extension bug
+ if (i > 0) sign |= words[1] & 0x80;
+ off = (off + 1) % data.length;
+ }
+
+ offp[0] = off;
+ signp[0] = sign;
+ return words;
+ }
+
+ /**
+ * 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 signp[] = { 0 };
+ return streamtowords(data, offp, signp)[0];
+ }
+
+ /**
+ * Cycically extract a word of key material, with sign-extension bug
+ * @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_bug(byte data[], int offp[]) {
+ int signp[] = { 0 };
+ return streamtowords(data, offp, signp)[1];
+ }
+
+ /**
+ * Initialise the Blowfish key schedule
+ */
+ private void init_key() {
+ P = P_orig.clone();
+ S = S_orig.clone();
+ }
+
+ /**
+ * Key the Blowfish cipher
+ * @param key an array containing the key
+ * @param sign_ext_bug true to implement the 2x bug
+ * @param safety bit 16 is set when the safety measure is requested
+ */
+ private void key(byte key[], boolean sign_ext_bug, int safety) {
+ int i;
+ int koffp[] = { 0 };
+ int lr[] = { 0, 0 };
+ int plen = P.length, slen = S.length;
+
+ for (i = 0; i < plen; i++)
+ if (!sign_ext_bug)
+ P[i] = P[i] ^ streamtoword(key, koffp);
+ else
+ P[i] = P[i] ^ streamtoword_bug(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
+ * @param sign_ext_bug true to implement the 2x bug
+ * @param safety bit 16 is set when the safety measure is requested
+ */
+ private void ekskey(byte data[], byte key[],
+ boolean sign_ext_bug, int safety) {
+ int i;
+ int koffp[] = { 0 }, doffp[] = { 0 };
+ int lr[] = { 0, 0 };
+ int plen = P.length, slen = S.length;
+ int signp[] = { 0 }; // non-benign sign-extension flag
+ int diff = 0; // zero iff correct and buggy are same
+
+ for (i = 0; i < plen; i++) {
+ int words[] = streamtowords(key, koffp, signp);
+ diff |= words[0] ^ words[1];
+ P[i] = P[i] ^ words[sign_ext_bug ? 1 : 0];
+ }
+
+ int sign = signp[0];
+
+ /*
+ * At this point, "diff" is zero iff the correct and buggy algorithms produced
+ * exactly the same result. If so and if "sign" is non-zero, which indicates
+ * that there was a non-benign sign extension, this means that we have a
+ * collision between the correctly computed hash for this password and a set of
+ * passwords that could be supplied to the buggy algorithm. Our safety measure
+ * is meant to protect from such many-buggy to one-correct collisions, by
+ * deviating from the correct algorithm in such cases. Let's check for this.
+ */
+ diff |= diff >> 16; /* still zero iff exact match */
+ diff &= 0xffff; /* ditto */
+ diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
+ sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
+ sign &= ~diff & safety; /* action needed? */
+
+ /*
+ * If we have determined that we need to deviate from the correct algorithm,
+ * flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but
+ * let's stick to it now. It came out of the approach we used above, and it's
+ * not any worse than any other choice we could make.)
+ *
+ * It is crucial that we don't do the same to the expanded key used in the main
+ * Eksblowfish loop. By doing it to only one of these two, we deviate from a
+ * state that could be directly specified by a password to the buggy algorithm
+ * (and to the fully correct one as well, but that's a side-effect).
+ */
+ P[0] ^= sign;
+
+ 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];
+ }
+ }
+
+ static long roundsForLogRounds(int log_rounds) {
+ if (log_rounds < 4 || log_rounds > 31) {
+ throw new IllegalArgumentException("Bad number of rounds");
+ }
+ return 1L << log_rounds;
+ }
+
+ /**
+ * 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
+ * @param sign_ext_bug true to implement the 2x bug
+ * @param safety bit 16 is set when the safety measure is requested
+ * @return an array containing the binary hashed password
+ */
+ private byte[] crypt_raw(byte password[], byte salt[], int log_rounds,
+ boolean sign_ext_bug, int safety) {
+ int rounds, i, j;
+ int cdata[] = bf_crypt_ciphertext.clone();
+ int clen = cdata.length;
+ byte ret[];
+
+ if (log_rounds < 4 || log_rounds > 31)
+ throw new IllegalArgumentException ("Bad number of rounds");
+ rounds = 1 << log_rounds;
+ if (salt.length != BCRYPT_SALT_LEN)
+ throw new IllegalArgumentException ("Bad salt length");
+
+ init_key();
+ ekskey(salt, password, sign_ext_bug, safety);
+ for (i = 0; i < rounds; i++) {
+ key(password, sign_ext_bug, safety);
+ key(salt, false, safety);
+ }
+
+ 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] >> 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 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) {
+ byte passwordb[];
+
+ try {
+ passwordb = password.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException uee) {
+ throw new AssertionError("UTF-8 is not supported");
+ }
+
+ return hashpw(passwordb, salt);
+ }
+
+ /**
+ * Hash a password using the OpenBSD bcrypt scheme
+ * @param passwordb the password to hash, as a byte array
+ * @param salt the salt to hash with (perhaps generated
+ * using BCrypt.gensalt)
+ * @return the hashed password
+ */
+ public static String hashpw(byte passwordb[], String salt) {
+ BCrypt B;
+ String real_salt;
+ byte saltb[], hashed[];
+ char minor = (char) 0;
+ int rounds, off;
+ StringBuilder rs = new StringBuilder();
+
+ if (salt == null) {
+ throw new IllegalArgumentException("salt cannot be null");
+ }
+
+ int saltLength = salt.length();
+
+ if (saltLength < 28) {
+ throw new IllegalArgumentException("Invalid salt");
+ }
+
+ 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' && minor != 'x' && minor != 'y' && minor != 'b')
+ || 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);
+ saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
+
+ if (minor >= 'a') // add null terminator
+ passwordb = Arrays.copyOf(passwordb, passwordb.length + 1);
+
+ B = new BCrypt();
+ hashed = B.crypt_raw(passwordb, saltb, rounds, minor == 'x', minor == 'a' ? 0x10000 : 0);
+
+ rs.append("$2");
+ if (minor >= 'a')
+ rs.append(minor);
+ rs.append("$");
+ if (rounds < 10)
+ rs.append("0");
+ rs.append(rounds);
+ rs.append("$");
+ encode_base64(saltb, saltb.length, rs);
+ encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);
+ return rs.toString();
+ }
+
+ /**
+ * Generate a salt for use with the BCrypt.hashpw() method
+ * @param prefix the prefix value (default $2a)
+ * @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
+ * @exception IllegalArgumentException if prefix or log_rounds is invalid
+ */
+ public static String gensalt(String prefix, int log_rounds, SecureRandom random)
+ throws IllegalArgumentException {
+ StringBuilder rs = new StringBuilder();
+ byte rnd[] = new byte[BCRYPT_SALT_LEN];
+
+ if (!prefix.startsWith("$2") ||
+ (prefix.charAt(2) != 'a' && prefix.charAt(2) != 'y' &&
+ prefix.charAt(2) != 'b')) {
+ throw new IllegalArgumentException ("Invalid prefix");
+ }
+ if (log_rounds < 4 || log_rounds > 31) {
+ throw new IllegalArgumentException ("Invalid log_rounds");
+ }
+
+ random.nextBytes(rnd);
+
+ rs.append("$2");
+ rs.append(prefix.charAt(2));
+ rs.append("$");
+ if (log_rounds < 10)
+ rs.append("0");
+ rs.append(log_rounds);
+ rs.append("$");
+ encode_base64(rnd, rnd.length, rs);
+ return rs.toString();
+ }
+
+ /**
+ * Generate a salt for use with the BCrypt.hashpw() method
+ * @param prefix the prefix value (default $2a)
+ * @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
+ * @exception IllegalArgumentException if prefix or log_rounds is invalid
+ */
+ public static String gensalt(String prefix, int log_rounds)
+ throws IllegalArgumentException {
+ return gensalt(prefix, log_rounds, new SecureRandom());
+ }
+
+ /**
+ * 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
+ * @exception IllegalArgumentException if log_rounds is invalid
+ */
+ public static String gensalt(int log_rounds, SecureRandom random)
+ throws IllegalArgumentException {
+ return gensalt("$2a", log_rounds, random);
+ }
+
+ /**
+ * 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
+ * @exception IllegalArgumentException if log_rounds is invalid
+ */
+ public static String gensalt(int log_rounds)
+ throws IllegalArgumentException {
+ return gensalt(log_rounds, new SecureRandom());
+ }
+
+ public static String gensalt(String prefix) {
+ return gensalt(prefix, GENSALT_DEFAULT_LOG2_ROUNDS);
+ }
+
+ /**
+ * 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 equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
+ }
+
+ static boolean equalsNoEarlyReturn(String a, String b) {
+ char[] caa = a.toCharArray();
+ char[] cab = b.toCharArray();
+
+ if (caa.length != cab.length) {
+ return false;
+ }
+
+ byte ret = 0;
+ for (int i = 0; i < caa.length; i++) {
+ ret |= caa[i] ^ cab[i];
+ }
+ return ret == 0;
+ }
+}
+
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/Makefile b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/Makefile
new file mode 100644
index 00000000..7b9d6139
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/Makefile
@@ -0,0 +1,269 @@
+
+SHELL = /bin/sh
+
+# V=0 quiet, V=1 verbose. other values don't work.
+V = 0
+V0 = $(V:0=)
+Q1 = $(V:1=)
+Q = $(Q1:0=@)
+ECHO1 = $(V:1=@ :)
+ECHO = $(ECHO1:0=@ echo)
+NULLCMD = :
+
+#### Start of system configuration section. ####
+
+srcdir = .
+topdir = /home/gi/.rvm/rubies/ruby-3.2.2/include/ruby-3.2.0
+hdrdir = $(topdir)
+arch_hdrdir = /home/gi/.rvm/rubies/ruby-3.2.2/include/ruby-3.2.0/x86_64-linux
+PATH_SEPARATOR = :
+VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
+prefix = $(DESTDIR)/home/gi/.rvm/rubies/ruby-3.2.2
+rubysitearchprefix = $(rubylibprefix)/$(sitearch)
+rubyarchprefix = $(rubylibprefix)/$(arch)
+rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
+exec_prefix = $(prefix)
+vendorarchhdrdir = $(vendorhdrdir)/$(sitearch)
+sitearchhdrdir = $(sitehdrdir)/$(sitearch)
+rubyarchhdrdir = $(rubyhdrdir)/$(arch)
+vendorhdrdir = $(rubyhdrdir)/vendor_ruby
+sitehdrdir = $(rubyhdrdir)/site_ruby
+rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
+vendorarchdir = $(vendorlibdir)/$(sitearch)
+vendorlibdir = $(vendordir)/$(ruby_version)
+vendordir = $(rubylibprefix)/vendor_ruby
+sitearchdir = $(sitelibdir)/$(sitearch)
+sitelibdir = $(sitedir)/$(ruby_version)
+sitedir = $(rubylibprefix)/site_ruby
+rubyarchdir = $(rubylibdir)/$(arch)
+rubylibdir = $(rubylibprefix)/$(ruby_version)
+sitearchincludedir = $(includedir)/$(sitearch)
+archincludedir = $(includedir)/$(arch)
+sitearchlibdir = $(libdir)/$(sitearch)
+archlibdir = $(libdir)/$(arch)
+ridir = $(datarootdir)/$(RI_BASE_NAME)
+mandir = $(datarootdir)/man
+localedir = $(datarootdir)/locale
+libdir = $(exec_prefix)/lib
+psdir = $(docdir)
+pdfdir = $(docdir)
+dvidir = $(docdir)
+htmldir = $(docdir)
+infodir = $(datarootdir)/info
+docdir = $(datarootdir)/doc/$(PACKAGE)
+oldincludedir = $(DESTDIR)/usr/include
+includedir = $(prefix)/include
+runstatedir = $(localstatedir)/run
+localstatedir = $(prefix)/var
+sharedstatedir = $(prefix)/com
+sysconfdir = $(prefix)/etc
+datadir = $(datarootdir)
+datarootdir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+sbindir = $(exec_prefix)/sbin
+bindir = $(exec_prefix)/bin
+archdir = $(rubyarchdir)
+
+
+CC_WRAPPER =
+CC = gcc
+CXX = g++
+LIBRUBY = $(LIBRUBY_SO)
+LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
+LIBRUBYARG_SHARED = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)
+LIBRUBYARG_STATIC = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)-static $(MAINLIBS)
+empty =
+OUTFLAG = -o $(empty)
+COUTFLAG = -o $(empty)
+CSRCFLAG = $(empty)
+
+RUBY_EXTCONF_H =
+cflags = $(optflags) $(debugflags) $(warnflags)
+cxxflags =
+optflags = -O3 -fno-fast-math
+debugflags = -ggdb3
+warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wundef
+cppflags =
+CCDLFLAGS = -fPIC
+CFLAGS = $(CCDLFLAGS) $(cflags) -fPIC $(ARCH_FLAG)
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
+DEFS =
+CPPFLAGS = -D__SKIP_GNU $(DEFS) $(cppflags)
+CXXFLAGS = $(CCDLFLAGS) $(ARCH_FLAG)
+ldflags = -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed
+dldflags = -Wl,--compress-debug-sections=zlib
+ARCH_FLAG =
+DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
+LDSHARED = $(CC) -shared
+LDSHAREDXX = $(CXX) -shared
+AR = gcc-ar
+EXEEXT =
+
+RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)
+RUBY_SO_NAME = ruby
+RUBYW_INSTALL_NAME =
+RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
+RUBYW_BASE_NAME = rubyw
+RUBY_BASE_NAME = ruby
+
+arch = x86_64-linux
+sitearch = $(arch)
+ruby_version = 3.2.0
+ruby = $(bindir)/$(RUBY_BASE_NAME)
+RUBY = $(ruby)
+BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)
+ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
+
+RM = rm -f
+RM_RF = rm -fr
+RMDIRS = rmdir --ignore-fail-on-non-empty -p
+MAKEDIRS = /usr/bin/mkdir -p
+INSTALL = /usr/bin/install -c
+INSTALL_PROG = $(INSTALL) -m 0755
+INSTALL_DATA = $(INSTALL) -m 644
+COPY = cp
+TOUCH = exit >
+
+#### End of system configuration section. ####
+
+preload =
+libpath = . $(libdir)
+LIBPATH = -L. -L$(libdir) -Wl,-rpath,$(libdir)
+DEFFILE =
+
+CLEANFILES = mkmf.log
+DISTCLEANFILES =
+DISTCLEANDIRS =
+
+extout =
+extout_prefix =
+target_prefix =
+LOCAL_LIBS =
+LIBS = $(LIBRUBYARG_SHARED) -lm -lpthread -lc
+ORIG_SRCS = bcrypt_ext.c crypt.c crypt_blowfish.c crypt_gensalt.c wrapper.c
+SRCS = $(ORIG_SRCS) bcrypt_ext.c crypt_blowfish.c x86.c crypt_gensalt.c wrapper.c
+OBJS = bcrypt_ext.o crypt_blowfish.o x86.o crypt_gensalt.o wrapper.o
+HDRS = $(srcdir)/crypt.h $(srcdir)/crypt_blowfish.h $(srcdir)/crypt_gensalt.h $(srcdir)/ow-crypt.h
+LOCAL_HDRS =
+TARGET = bcrypt_ext
+TARGET_NAME = bcrypt_ext
+TARGET_ENTRY = Init_$(TARGET_NAME)
+DLLIB = $(TARGET).so
+EXTSTATIC =
+STATIC_LIB =
+
+TIMESTAMP_DIR = .
+BINDIR = $(bindir)
+RUBYCOMMONDIR = $(sitedir)$(target_prefix)
+RUBYLIBDIR = $(sitelibdir)$(target_prefix)
+RUBYARCHDIR = $(sitearchdir)$(target_prefix)
+HDRDIR = $(sitehdrdir)$(target_prefix)
+ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
+TARGET_SO_DIR =
+TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
+CLEANLIBS = $(TARGET_SO) false
+CLEANOBJS = $(OBJS) *.bak
+TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.time
+
+all: $(DLLIB)
+static: $(STATIC_LIB)
+.PHONY: all install static install-so install-rb
+.PHONY: clean clean-so clean-static clean-rb
+
+clean-static::
+clean-rb-default::
+clean-rb::
+clean-so::
+clean: clean-so clean-static clean-rb-default clean-rb
+ -$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
+
+distclean-rb-default::
+distclean-rb::
+distclean-so::
+distclean-static::
+distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
+ -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
+ -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
+ -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
+
+realclean: distclean
+install: install-so install-rb
+
+install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
+ $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
+clean-static::
+ -$(Q)$(RM) $(STATIC_LIB)
+install-rb: pre-install-rb do-install-rb install-rb-default
+install-rb-default: pre-install-rb-default do-install-rb-default
+pre-install-rb: Makefile
+pre-install-rb-default: Makefile
+do-install-rb:
+do-install-rb-default:
+pre-install-rb-default:
+ @$(NULLCMD)
+$(TARGET_SO_DIR_TIMESTAMP):
+ $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
+ $(Q) $(TOUCH) $@
+
+site-install: site-install-so site-install-rb
+site-install-so: install-so
+site-install-rb: install-rb
+
+.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
+
+.cc.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cc.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.mm.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.mm.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.cxx.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cxx.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.cpp.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cpp.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.c.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.c.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.m.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.m.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+$(TARGET_SO): $(OBJS) Makefile
+ $(ECHO) linking shared-object $(DLLIB)
+ -$(Q)$(RM) $(@)
+ $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
+
+
+
+$(OBJS): $(HDRS) $(ruby_headers)
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/bcrypt_ext.c b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/bcrypt_ext.c
new file mode 100644
index 00000000..2366185e
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/bcrypt_ext.c
@@ -0,0 +1,116 @@
+#include
+#include
+
+#ifdef HAVE_RUBY_THREAD_H
+#include
+#endif
+
+static VALUE mBCrypt;
+static VALUE cBCryptEngine;
+
+struct bc_salt_args {
+ const char * prefix;
+ unsigned long count;
+ const char * input;
+ int size;
+};
+
+static void * bc_salt_nogvl(void * ptr) {
+ struct bc_salt_args * args = ptr;
+
+ return crypt_gensalt_ra(args->prefix, args->count, args->input, args->size);
+}
+
+/* Given a logarithmic cost parameter, generates a salt for use with +bc_crypt+.
+*/
+static VALUE bc_salt(VALUE self, VALUE prefix, VALUE count, VALUE input) {
+ char * salt;
+ VALUE str_salt;
+ struct bc_salt_args args;
+
+ /* duplicate the parameters for thread safety. If another thread has a
+ * reference to the parameters and mutates them while we are working,
+ * that would be very bad. Duping the strings means that the reference
+ * isn't shared. */
+ prefix = rb_str_new_frozen(prefix);
+ input = rb_str_new_frozen(input);
+
+ args.prefix = StringValueCStr(prefix);
+ args.count = NUM2ULONG(count);
+ args.input = NIL_P(input) ? NULL : StringValuePtr(input);
+ args.size = NIL_P(input) ? 0 : RSTRING_LEN(input);
+
+#ifdef HAVE_RUBY_THREAD_H
+ salt = rb_thread_call_without_gvl(bc_salt_nogvl, &args, NULL, NULL);
+#else
+ salt = bc_salt_nogvl((void *)&args);
+#endif
+
+ if(!salt) return Qnil;
+
+ str_salt = rb_str_new2(salt);
+ free(salt);
+
+ return str_salt;
+}
+
+struct bc_crypt_args {
+ const char * key;
+ const char * setting;
+ void * data;
+ int size;
+};
+
+static void * bc_crypt_nogvl(void * ptr) {
+ struct bc_crypt_args * args = ptr;
+
+ return crypt_ra(args->key, args->setting, &args->data, &args->size);
+}
+
+/* Given a secret and a salt, generates a salted hash (which you can then store safely).
+*/
+static VALUE bc_crypt(VALUE self, VALUE key, VALUE setting) {
+ char * value;
+ VALUE out;
+
+ struct bc_crypt_args args;
+
+ if(NIL_P(key) || NIL_P(setting)) return Qnil;
+
+ /* duplicate the parameters for thread safety. If another thread has a
+ * reference to the parameters and mutates them while we are working,
+ * that would be very bad. Duping the strings means that the reference
+ * isn't shared. */
+ key = rb_str_new_frozen(key);
+ setting = rb_str_new_frozen(setting);
+
+ args.data = NULL;
+ args.size = 0xDEADBEEF;
+ args.key = NIL_P(key) ? NULL : StringValueCStr(key);
+ args.setting = NIL_P(setting) ? NULL : StringValueCStr(setting);
+
+#ifdef HAVE_RUBY_THREAD_H
+ value = rb_thread_call_without_gvl(bc_crypt_nogvl, &args, NULL, NULL);
+#else
+ value = bc_crypt_nogvl((void *)&args);
+#endif
+
+ if(!value || !args.data) return Qnil;
+
+ out = rb_str_new2(value);
+
+ free(args.data);
+
+ return out;
+}
+
+/* Create the BCrypt and BCrypt::Engine modules, and populate them with methods. */
+void Init_bcrypt_ext(){
+ mBCrypt = rb_define_module("BCrypt");
+ cBCryptEngine = rb_define_class_under(mBCrypt, "Engine", rb_cObject);
+
+ rb_define_singleton_method(cBCryptEngine, "__bc_salt", bc_salt, 3);
+ rb_define_singleton_method(cBCryptEngine, "__bc_crypt", bc_crypt, 2);
+}
+
+/* vim: set noet sws=4 sw=4: */
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt.c b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt.c
new file mode 100644
index 00000000..522b4958
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt.c
@@ -0,0 +1,57 @@
+#include
+#include
+
+VALUE mCrypt;
+
+static VALUE crypt_salt(VALUE self, VALUE prefix, VALUE count, VALUE input)
+{
+ char * salt;
+ VALUE str_salt;
+
+ salt = crypt_gensalt_ra(
+ StringValuePtr(prefix),
+ NUM2ULONG(count),
+ NIL_P(input) ? NULL : StringValuePtr(input),
+ NIL_P(input) ? 0 : RSTRING_LEN(input));
+
+ if(!salt) return Qnil;
+
+ str_salt = rb_str_new2(salt);
+ free(salt);
+
+ return str_salt;
+}
+
+static VALUE ra(VALUE self, VALUE key, VALUE setting)
+{
+ char * value;
+ void * data;
+ int size;
+ VALUE out;
+
+ data = NULL;
+ size = 0xDEADBEEF;
+
+ if(NIL_P(key) || NIL_P(setting)) return Qnil;
+
+ value = crypt_ra(
+ NIL_P(key) ? NULL : StringValuePtr(key),
+ NIL_P(setting) ? NULL : StringValuePtr(setting),
+ &data,
+ &size);
+
+ if(!value) return Qnil;
+
+ out = rb_str_new(data, size - 1);
+
+ free(data);
+
+ return out;
+}
+
+void Init_crypt()
+{
+ mCrypt = rb_define_module("Crypt");
+ rb_define_singleton_method(mCrypt, "salt", crypt_salt, 3);
+ rb_define_singleton_method(mCrypt, "crypt", ra, 2);
+}
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt.h b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt.h
new file mode 100644
index 00000000..12e67055
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt.h
@@ -0,0 +1,24 @@
+/*
+ * Written by Solar Designer in 2000-2002.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain. In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 2000-2002 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See crypt_blowfish.c for more information.
+ */
+
+#include
+
+#if defined(_OW_SOURCE) || defined(__USE_OW)
+#define __SKIP_GNU
+#undef __SKIP_OW
+#include
+#undef __SKIP_GNU
+#endif
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_blowfish.c b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_blowfish.c
new file mode 100644
index 00000000..57600648
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_blowfish.c
@@ -0,0 +1,903 @@
+/*
+ * The crypt_blowfish homepage is:
+ *
+ * http://www.openwall.com/crypt/
+ *
+ * This code comes from John the Ripper password cracker, with reentrant
+ * and crypt(3) interfaces added, but optimizations specific to password
+ * cracking removed.
+ *
+ * Written by Solar Designer in 1998-2014.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain. In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * It is my intent that you should be able to use this on your system,
+ * as part of a software package, or anywhere else to improve security,
+ * ensure compatibility, or for any other purpose. I would appreciate
+ * it if you give credit where it is due and keep your modifications in
+ * the public domain as well, but I don't require that in order to let
+ * you place this code and any modifications you make under a license
+ * of your choice.
+ *
+ * This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
+ * "$2b$", originally by Niels Provos , and it uses
+ * some of his ideas. The password hashing algorithm was designed by David
+ * Mazieres . For information on the level of
+ * compatibility for bcrypt hash prefixes other than "$2b$", please refer to
+ * the comments in BF_set_key() below and to the included crypt(3) man page.
+ *
+ * There's a paper on the algorithm that explains its design decisions:
+ *
+ * http://www.usenix.org/events/usenix99/provos.html
+ *
+ * Some of the tricks in BF_ROUND might be inspired by Eric Young's
+ * Blowfish library (I can't be sure if I would think of something if I
+ * hadn't seen his code).
+ */
+
+#include
+
+#include
+#ifndef __set_errno
+#define __set_errno(val) errno = (val)
+#endif
+
+/* Just to make sure the prototypes match the actual definitions */
+#include "crypt_blowfish.h"
+
+#ifdef __i386__
+#define BF_ASM 1
+#define BF_SCALE 1
+#elif defined(__x86_64__) || defined(__alpha__) || defined(__hppa__)
+#define BF_ASM 0
+#define BF_SCALE 1
+#else
+#define BF_ASM 0
+#define BF_SCALE 0
+#endif
+
+typedef unsigned int BF_word;
+typedef signed int BF_word_signed;
+
+/* Number of Blowfish rounds, this is also hardcoded into a few places */
+#define BF_N 16
+
+typedef BF_word BF_key[BF_N + 2];
+
+typedef struct {
+ BF_word S[4][0x100];
+ BF_key P;
+} BF_ctx;
+
+/*
+ * Magic IV for 64 Blowfish encryptions that we do at the end.
+ * The string is "OrpheanBeholderScryDoubt" on big-endian.
+ */
+static BF_word BF_magic_w[6] = {
+ 0x4F727068, 0x65616E42, 0x65686F6C,
+ 0x64657253, 0x63727944, 0x6F756274
+};
+
+/*
+ * P-box and S-box tables initialized with digits of Pi.
+ */
+static BF_ctx BF_init_state = {
+ {
+ {
+ 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
+ }
+ }, {
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b
+ }
+};
+
+static const unsigned char BF_itoa64[64 + 1] =
+ "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+static unsigned char BF_atoi64[0x60] = {
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64,
+ 64, 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, 64, 64, 64, 64, 64,
+ 64, 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, 64, 64, 64, 64, 64
+};
+
+#define BF_safe_atoi64(dst, src) \
+{ \
+ tmp = (unsigned char)(src); \
+ if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \
+ tmp = BF_atoi64[tmp]; \
+ if (tmp > 63) return -1; \
+ (dst) = tmp; \
+}
+
+static int BF_decode(BF_word *dst, const char *src, int size)
+{
+ unsigned char *dptr = (unsigned char *)dst;
+ unsigned char *end = dptr + size;
+ const unsigned char *sptr = (const unsigned char *)src;
+ do {
+ unsigned int tmp, c1, c2, c3, c4;
+ BF_safe_atoi64(c1, *sptr++);
+ BF_safe_atoi64(c2, *sptr++);
+ *dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4);
+ if (dptr >= end) break;
+
+ BF_safe_atoi64(c3, *sptr++);
+ *dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2);
+ if (dptr >= end) break;
+
+ BF_safe_atoi64(c4, *sptr++);
+ *dptr++ = ((c3 & 0x03) << 6) | c4;
+ } while (dptr < end);
+ return 0;
+}
+
+static void BF_encode(char *dst, const BF_word *src, int size)
+{
+ const unsigned char *sptr = (const unsigned char *)src;
+ const unsigned char *end = sptr + size;
+ unsigned char *dptr = (unsigned char *)dst;
+ do {
+ unsigned int c1, c2;
+ c1 = *sptr++;
+ *dptr++ = BF_itoa64[c1 >> 2];
+ c1 = (c1 & 0x03) << 4;
+ if (sptr >= end) {
+ *dptr++ = BF_itoa64[c1];
+ break;
+ }
+
+ c2 = *sptr++;
+ c1 |= c2 >> 4;
+ *dptr++ = BF_itoa64[c1];
+ c1 = (c2 & 0x0f) << 2;
+ if (sptr >= end) {
+ *dptr++ = BF_itoa64[c1];
+ break;
+ }
+
+ c2 = *sptr++;
+ c1 |= c2 >> 6;
+ *dptr++ = BF_itoa64[c1];
+ *dptr++ = BF_itoa64[c2 & 0x3f];
+ } while (sptr < end);
+}
+
+static void BF_swap(BF_word *x, int count)
+{
+ static int endianness_check = 1;
+ char *is_little_endian = (char *)&endianness_check;
+ if (*is_little_endian)
+ do {
+ BF_word tmp;
+ tmp = *x;
+ tmp = (tmp << 16) | (tmp >> 16);
+ *x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF);
+ } while (--count);
+}
+
+#if BF_SCALE
+/* Architectures which can shift addresses left by 2 bits with no extra cost */
+#define BF_ROUND(L, R, N) \
+ tmp1 = L & 0xFF; \
+ tmp2 = L >> 8; \
+ tmp2 &= 0xFF; \
+ tmp3 = L >> 16; \
+ tmp3 &= 0xFF; \
+ tmp4 = L >> 24; \
+ tmp1 = data.ctx.S[3][tmp1]; \
+ tmp2 = data.ctx.S[2][tmp2]; \
+ tmp3 = data.ctx.S[1][tmp3]; \
+ tmp3 += data.ctx.S[0][tmp4]; \
+ tmp3 ^= tmp2; \
+ R ^= data.ctx.P[N + 1]; \
+ tmp3 += tmp1; \
+ R ^= tmp3;
+#else
+/* Architectures with no complicated addressing modes supported */
+#define BF_INDEX(S, i) \
+ (*((BF_word *)(((unsigned char *)S) + (i))))
+#define BF_ROUND(L, R, N) \
+ tmp1 = L & 0xFF; \
+ tmp1 <<= 2; \
+ tmp2 = L >> 6; \
+ tmp2 &= 0x3FC; \
+ tmp3 = L >> 14; \
+ tmp3 &= 0x3FC; \
+ tmp4 = L >> 22; \
+ tmp4 &= 0x3FC; \
+ tmp1 = BF_INDEX(data.ctx.S[3], tmp1); \
+ tmp2 = BF_INDEX(data.ctx.S[2], tmp2); \
+ tmp3 = BF_INDEX(data.ctx.S[1], tmp3); \
+ tmp3 += BF_INDEX(data.ctx.S[0], tmp4); \
+ tmp3 ^= tmp2; \
+ R ^= data.ctx.P[N + 1]; \
+ tmp3 += tmp1; \
+ R ^= tmp3;
+#endif
+
+/*
+ * Encrypt one block, BF_N is hardcoded here.
+ */
+#define BF_ENCRYPT \
+ L ^= data.ctx.P[0]; \
+ BF_ROUND(L, R, 0); \
+ BF_ROUND(R, L, 1); \
+ BF_ROUND(L, R, 2); \
+ BF_ROUND(R, L, 3); \
+ BF_ROUND(L, R, 4); \
+ BF_ROUND(R, L, 5); \
+ BF_ROUND(L, R, 6); \
+ BF_ROUND(R, L, 7); \
+ BF_ROUND(L, R, 8); \
+ BF_ROUND(R, L, 9); \
+ BF_ROUND(L, R, 10); \
+ BF_ROUND(R, L, 11); \
+ BF_ROUND(L, R, 12); \
+ BF_ROUND(R, L, 13); \
+ BF_ROUND(L, R, 14); \
+ BF_ROUND(R, L, 15); \
+ tmp4 = R; \
+ R = L; \
+ L = tmp4 ^ data.ctx.P[BF_N + 1];
+
+#if BF_ASM == 1
+#define BF_body() \
+ _BF_body_r(&data.ctx);
+#else
+#define BF_body() \
+ L = R = 0; \
+ ptr = data.ctx.P; \
+ do { \
+ ptr += 2; \
+ BF_ENCRYPT; \
+ *(ptr - 2) = L; \
+ *(ptr - 1) = R; \
+ } while (ptr < &data.ctx.P[BF_N + 2]); \
+\
+ ptr = data.ctx.S[0]; \
+ do { \
+ ptr += 2; \
+ BF_ENCRYPT; \
+ *(ptr - 2) = L; \
+ *(ptr - 1) = R; \
+ } while (ptr < &data.ctx.S[3][0xFF]);
+#endif
+
+static void BF_set_key(const char *key, BF_key expanded, BF_key initial,
+ unsigned char flags)
+{
+ const char *ptr = key;
+ unsigned int bug, i, j;
+ BF_word safety, sign, diff, tmp[2];
+
+/*
+ * There was a sign extension bug in older revisions of this function. While
+ * we would have liked to simply fix the bug and move on, we have to provide
+ * a backwards compatibility feature (essentially the bug) for some systems and
+ * a safety measure for some others. The latter is needed because for certain
+ * multiple inputs to the buggy algorithm there exist easily found inputs to
+ * the correct algorithm that produce the same hash. Thus, we optionally
+ * deviate from the correct algorithm just enough to avoid such collisions.
+ * While the bug itself affected the majority of passwords containing
+ * characters with the 8th bit set (although only a percentage of those in a
+ * collision-producing way), the anti-collision safety measure affects
+ * only a subset of passwords containing the '\xff' character (not even all of
+ * those passwords, just some of them). This character is not found in valid
+ * UTF-8 sequences and is rarely used in popular 8-bit character encodings.
+ * Thus, the safety measure is unlikely to cause much annoyance, and is a
+ * reasonable tradeoff to use when authenticating against existing hashes that
+ * are not reliably known to have been computed with the correct algorithm.
+ *
+ * We use an approach that tries to minimize side-channel leaks of password
+ * information - that is, we mostly use fixed-cost bitwise operations instead
+ * of branches or table lookups. (One conditional branch based on password
+ * length remains. It is not part of the bug aftermath, though, and is
+ * difficult and possibly unreasonable to avoid given the use of C strings by
+ * the caller, which results in similar timing leaks anyway.)
+ *
+ * For actual implementation, we set an array index in the variable "bug"
+ * (0 means no bug, 1 means sign extension bug emulation) and a flag in the
+ * variable "safety" (bit 16 is set when the safety measure is requested).
+ * Valid combinations of settings are:
+ *
+ * Prefix "$2a$": bug = 0, safety = 0x10000
+ * Prefix "$2b$": bug = 0, safety = 0
+ * Prefix "$2x$": bug = 1, safety = 0
+ * Prefix "$2y$": bug = 0, safety = 0
+ */
+ bug = (unsigned int)flags & 1;
+ safety = ((BF_word)flags & 2) << 15;
+
+ sign = diff = 0;
+
+ for (i = 0; i < BF_N + 2; i++) {
+ tmp[0] = tmp[1] = 0;
+ for (j = 0; j < 4; j++) {
+ tmp[0] <<= 8;
+ tmp[0] |= (unsigned char)*ptr; /* correct */
+ tmp[1] <<= 8;
+ tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */
+/*
+ * Sign extension in the first char has no effect - nothing to overwrite yet,
+ * and those extra 24 bits will be fully shifted out of the 32-bit word. For
+ * chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign
+ * extension in tmp[1] occurs. Once this flag is set, it remains set.
+ */
+ if (j)
+ sign |= tmp[1] & 0x80;
+ if (!*ptr)
+ ptr = key;
+ else
+ ptr++;
+ }
+ diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */
+
+ expanded[i] = tmp[bug];
+ initial[i] = BF_init_state.P[i] ^ tmp[bug];
+ }
+
+/*
+ * At this point, "diff" is zero iff the correct and buggy algorithms produced
+ * exactly the same result. If so and if "sign" is non-zero, which indicates
+ * that there was a non-benign sign extension, this means that we have a
+ * collision between the correctly computed hash for this password and a set of
+ * passwords that could be supplied to the buggy algorithm. Our safety measure
+ * is meant to protect from such many-buggy to one-correct collisions, by
+ * deviating from the correct algorithm in such cases. Let's check for this.
+ */
+ diff |= diff >> 16; /* still zero iff exact match */
+ diff &= 0xffff; /* ditto */
+ diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
+ sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
+ sign &= ~diff & safety; /* action needed? */
+
+/*
+ * If we have determined that we need to deviate from the correct algorithm,
+ * flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but
+ * let's stick to it now. It came out of the approach we used above, and it's
+ * not any worse than any other choice we could make.)
+ *
+ * It is crucial that we don't do the same to the expanded key used in the main
+ * Eksblowfish loop. By doing it to only one of these two, we deviate from a
+ * state that could be directly specified by a password to the buggy algorithm
+ * (and to the fully correct one as well, but that's a side-effect).
+ */
+ initial[0] ^= sign;
+}
+
+static const unsigned char flags_by_subtype[26] =
+ {2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0};
+
+static char *BF_crypt(const char *key, const char *setting,
+ char *output, int size,
+ BF_word min)
+{
+#if BF_ASM == 1
+ extern void _BF_body_r(BF_ctx *ctx);
+#endif
+ struct {
+ BF_ctx ctx;
+ BF_key expanded_key;
+ union {
+ BF_word salt[4];
+ BF_word output[6];
+ } binary;
+ } data;
+ BF_word L, R;
+ BF_word tmp1, tmp2, tmp3, tmp4;
+ BF_word *ptr;
+ BF_word count;
+ int i;
+
+ if (size < 7 + 22 + 31 + 1) {
+ __set_errno(ERANGE);
+ return NULL;
+ }
+
+ if (setting[0] != '$' ||
+ setting[1] != '2' ||
+ setting[2] < 'a' || setting[2] > 'z' ||
+ !flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] ||
+ setting[3] != '$' ||
+ setting[4] < '0' || setting[4] > '3' ||
+ setting[5] < '0' || setting[5] > '9' ||
+ (setting[4] == '3' && setting[5] > '1') ||
+ setting[6] != '$') {
+ __set_errno(EINVAL);
+ return NULL;
+ }
+
+ count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
+ if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) {
+ __set_errno(EINVAL);
+ return NULL;
+ }
+ BF_swap(data.binary.salt, 4);
+
+ BF_set_key(key, data.expanded_key, data.ctx.P,
+ flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']);
+
+ memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
+
+ L = R = 0;
+ for (i = 0; i < BF_N + 2; i += 2) {
+ L ^= data.binary.salt[i & 2];
+ R ^= data.binary.salt[(i & 2) + 1];
+ BF_ENCRYPT;
+ data.ctx.P[i] = L;
+ data.ctx.P[i + 1] = R;
+ }
+
+ ptr = data.ctx.S[0];
+ do {
+ ptr += 4;
+ L ^= data.binary.salt[(BF_N + 2) & 3];
+ R ^= data.binary.salt[(BF_N + 3) & 3];
+ BF_ENCRYPT;
+ *(ptr - 4) = L;
+ *(ptr - 3) = R;
+
+ L ^= data.binary.salt[(BF_N + 4) & 3];
+ R ^= data.binary.salt[(BF_N + 5) & 3];
+ BF_ENCRYPT;
+ *(ptr - 2) = L;
+ *(ptr - 1) = R;
+ } while (ptr < &data.ctx.S[3][0xFF]);
+
+ do {
+ int done;
+
+ for (i = 0; i < BF_N + 2; i += 2) {
+ data.ctx.P[i] ^= data.expanded_key[i];
+ data.ctx.P[i + 1] ^= data.expanded_key[i + 1];
+ }
+
+ done = 0;
+ do {
+ BF_body();
+ if (done)
+ break;
+ done = 1;
+
+ tmp1 = data.binary.salt[0];
+ tmp2 = data.binary.salt[1];
+ tmp3 = data.binary.salt[2];
+ tmp4 = data.binary.salt[3];
+ for (i = 0; i < BF_N; i += 4) {
+ data.ctx.P[i] ^= tmp1;
+ data.ctx.P[i + 1] ^= tmp2;
+ data.ctx.P[i + 2] ^= tmp3;
+ data.ctx.P[i + 3] ^= tmp4;
+ }
+ data.ctx.P[16] ^= tmp1;
+ data.ctx.P[17] ^= tmp2;
+ } while (1);
+ } while (--count);
+
+ for (i = 0; i < 6; i += 2) {
+ L = BF_magic_w[i];
+ R = BF_magic_w[i + 1];
+
+ count = 64;
+ do {
+ BF_ENCRYPT;
+ } while (--count);
+
+ data.binary.output[i] = L;
+ data.binary.output[i + 1] = R;
+ }
+
+ memcpy(output, setting, 7 + 22 - 1);
+ output[7 + 22 - 1] = BF_itoa64[(int)
+ BF_atoi64[(int)setting[7 + 22 - 1] - 0x20] & 0x30];
+
+/* This has to be bug-compatible with the original implementation, so
+ * only encode 23 of the 24 bytes. :-) */
+ BF_swap(data.binary.output, 6);
+ BF_encode(&output[7 + 22], data.binary.output, 23);
+ output[7 + 22 + 31] = '\0';
+
+ return output;
+}
+
+int _crypt_output_magic(const char *setting, char *output, int size)
+{
+ if (size < 3)
+ return -1;
+
+ output[0] = '*';
+ output[1] = '0';
+ output[2] = '\0';
+
+ if (setting[0] == '*' && setting[1] == '0')
+ output[1] = '1';
+
+ return 0;
+}
+
+/*
+ * Please preserve the runtime self-test. It serves two purposes at once:
+ *
+ * 1. We really can't afford the risk of producing incompatible hashes e.g.
+ * when there's something like gcc bug 26587 again, whereas an application or
+ * library integrating this code might not also integrate our external tests or
+ * it might not run them after every build. Even if it does, the miscompile
+ * might only occur on the production build, but not on a testing build (such
+ * as because of different optimization settings). It is painful to recover
+ * from incorrectly-computed hashes - merely fixing whatever broke is not
+ * enough. Thus, a proactive measure like this self-test is needed.
+ *
+ * 2. We don't want to leave sensitive data from our actual password hash
+ * computation on the stack or in registers. Previous revisions of the code
+ * would do explicit cleanups, but simply running the self-test after hash
+ * computation is more reliable.
+ *
+ * The performance cost of this quick self-test is around 0.6% at the "$2a$08"
+ * setting.
+ */
+char *_crypt_blowfish_rn(const char *key, const char *setting,
+ char *output, int size)
+{
+ const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
+ const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu";
+ static const char * const test_hashes[2] =
+ {"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55", /* 'a', 'b', 'y' */
+ "VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55"}; /* 'x' */
+ const char *test_hash = test_hashes[0];
+ char *retval;
+ const char *p;
+ int save_errno, ok;
+ struct {
+ char s[7 + 22 + 1];
+ char o[7 + 22 + 31 + 1 + 1 + 1];
+ } buf;
+
+/* Hash the supplied password */
+ _crypt_output_magic(setting, output, size);
+ retval = BF_crypt(key, setting, output, size, 16);
+ save_errno = errno;
+
+/*
+ * Do a quick self-test. It is important that we make both calls to BF_crypt()
+ * from the same scope such that they likely use the same stack locations,
+ * which makes the second call overwrite the first call's sensitive data on the
+ * stack and makes it more likely that any alignment related issues would be
+ * detected by the self-test.
+ */
+ memcpy(buf.s, test_setting, sizeof(buf.s));
+ if (retval) {
+ unsigned int flags = flags_by_subtype[
+ (unsigned int)(unsigned char)setting[2] - 'a'];
+ test_hash = test_hashes[flags & 1];
+ buf.s[2] = setting[2];
+ }
+ memset(buf.o, 0x55, sizeof(buf.o));
+ buf.o[sizeof(buf.o) - 1] = 0;
+ p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1);
+
+ ok = (p == buf.o &&
+ !memcmp(p, buf.s, 7 + 22) &&
+ !memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1));
+
+ {
+ const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345";
+ BF_key ae, ai, ye, yi;
+ BF_set_key(k, ae, ai, 2); /* $2a$ */
+ BF_set_key(k, ye, yi, 4); /* $2y$ */
+ ai[0] ^= 0x10000; /* undo the safety (for comparison) */
+ ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 &&
+ !memcmp(ae, ye, sizeof(ae)) &&
+ !memcmp(ai, yi, sizeof(ai));
+ }
+
+ __set_errno(save_errno);
+ if (ok)
+ return retval;
+
+/* Should not happen */
+ _crypt_output_magic(setting, output, size);
+ __set_errno(EINVAL); /* pretend we don't support this hash type */
+ return NULL;
+}
+
+char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count,
+ const char *input, int size, char *output, int output_size)
+{
+ if (size < 16 || output_size < 7 + 22 + 1 ||
+ (count && (count < 4 || count > 31)) ||
+ prefix[0] != '$' || prefix[1] != '2' ||
+ (prefix[2] != 'a' && prefix[2] != 'b' && prefix[2] != 'y')) {
+ if (output_size > 0) output[0] = '\0';
+ __set_errno((output_size < 7 + 22 + 1) ? ERANGE : EINVAL);
+ return NULL;
+ }
+
+ if (!count) count = 5;
+
+ output[0] = '$';
+ output[1] = '2';
+ output[2] = prefix[2];
+ output[3] = '$';
+ output[4] = '0' + count / 10;
+ output[5] = '0' + count % 10;
+ output[6] = '$';
+
+ BF_encode(&output[7], (const BF_word *)input, 16);
+ output[7 + 22] = '\0';
+
+ return output;
+}
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_blowfish.h b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_blowfish.h
new file mode 100644
index 00000000..2ee0d8c1
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_blowfish.h
@@ -0,0 +1,27 @@
+/*
+ * Written by Solar Designer in 2000-2011.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain. In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See crypt_blowfish.c for more information.
+ */
+
+#ifndef _CRYPT_BLOWFISH_H
+#define _CRYPT_BLOWFISH_H
+
+extern int _crypt_output_magic(const char *setting, char *output, int size);
+extern char *_crypt_blowfish_rn(const char *key, const char *setting,
+ char *output, int size);
+extern char *_crypt_gensalt_blowfish_rn(const char *prefix,
+ unsigned long count,
+ const char *input, int size, char *output, int output_size);
+
+#endif
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_gensalt.c b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_gensalt.c
new file mode 100644
index 00000000..744912a2
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_gensalt.c
@@ -0,0 +1,124 @@
+/*
+ * Written by Solar Designer in 2000-2011.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain. In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See crypt_blowfish.c for more information.
+ *
+ * This file contains salt generation functions for the traditional and
+ * other common crypt(3) algorithms, except for bcrypt which is defined
+ * entirely in crypt_blowfish.c.
+ */
+
+#include
+
+#include
+#ifndef __set_errno
+#define __set_errno(val) errno = (val)
+#endif
+
+/* Just to make sure the prototypes match the actual definitions */
+#include "crypt_gensalt.h"
+
+const unsigned char _crypt_itoa64[64 + 1] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+char *_crypt_gensalt_traditional_rn(const char *prefix, unsigned long count,
+ const char *input, int size, char *output, int output_size)
+{
+ (void) prefix;
+
+ if (size < 2 || output_size < 2 + 1 || (count && count != 25)) {
+ if (output_size > 0) output[0] = '\0';
+ __set_errno((output_size < 2 + 1) ? ERANGE : EINVAL);
+ return NULL;
+ }
+
+ output[0] = _crypt_itoa64[(unsigned int)input[0] & 0x3f];
+ output[1] = _crypt_itoa64[(unsigned int)input[1] & 0x3f];
+ output[2] = '\0';
+
+ return output;
+}
+
+char *_crypt_gensalt_extended_rn(const char *prefix, unsigned long count,
+ const char *input, int size, char *output, int output_size)
+{
+ unsigned long value;
+
+ (void) prefix;
+
+/* Even iteration counts make it easier to detect weak DES keys from a look
+ * at the hash, so they should be avoided */
+ if (size < 3 || output_size < 1 + 4 + 4 + 1 ||
+ (count && (count > 0xffffff || !(count & 1)))) {
+ if (output_size > 0) output[0] = '\0';
+ __set_errno((output_size < 1 + 4 + 4 + 1) ? ERANGE : EINVAL);
+ return NULL;
+ }
+
+ if (!count) count = 725;
+
+ output[0] = '_';
+ output[1] = _crypt_itoa64[count & 0x3f];
+ output[2] = _crypt_itoa64[(count >> 6) & 0x3f];
+ output[3] = _crypt_itoa64[(count >> 12) & 0x3f];
+ output[4] = _crypt_itoa64[(count >> 18) & 0x3f];
+ value = (unsigned long)(unsigned char)input[0] |
+ ((unsigned long)(unsigned char)input[1] << 8) |
+ ((unsigned long)(unsigned char)input[2] << 16);
+ output[5] = _crypt_itoa64[value & 0x3f];
+ output[6] = _crypt_itoa64[(value >> 6) & 0x3f];
+ output[7] = _crypt_itoa64[(value >> 12) & 0x3f];
+ output[8] = _crypt_itoa64[(value >> 18) & 0x3f];
+ output[9] = '\0';
+
+ return output;
+}
+
+char *_crypt_gensalt_md5_rn(const char *prefix, unsigned long count,
+ const char *input, int size, char *output, int output_size)
+{
+ unsigned long value;
+
+ (void) prefix;
+
+ if (size < 3 || output_size < 3 + 4 + 1 || (count && count != 1000)) {
+ if (output_size > 0) output[0] = '\0';
+ __set_errno((output_size < 3 + 4 + 1) ? ERANGE : EINVAL);
+ return NULL;
+ }
+
+ output[0] = '$';
+ output[1] = '1';
+ output[2] = '$';
+ value = (unsigned long)(unsigned char)input[0] |
+ ((unsigned long)(unsigned char)input[1] << 8) |
+ ((unsigned long)(unsigned char)input[2] << 16);
+ output[3] = _crypt_itoa64[value & 0x3f];
+ output[4] = _crypt_itoa64[(value >> 6) & 0x3f];
+ output[5] = _crypt_itoa64[(value >> 12) & 0x3f];
+ output[6] = _crypt_itoa64[(value >> 18) & 0x3f];
+ output[7] = '\0';
+
+ if (size >= 6 && output_size >= 3 + 4 + 4 + 1) {
+ value = (unsigned long)(unsigned char)input[3] |
+ ((unsigned long)(unsigned char)input[4] << 8) |
+ ((unsigned long)(unsigned char)input[5] << 16);
+ output[7] = _crypt_itoa64[value & 0x3f];
+ output[8] = _crypt_itoa64[(value >> 6) & 0x3f];
+ output[9] = _crypt_itoa64[(value >> 12) & 0x3f];
+ output[10] = _crypt_itoa64[(value >> 18) & 0x3f];
+ output[11] = '\0';
+ }
+
+ return output;
+}
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_gensalt.h b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_gensalt.h
new file mode 100644
index 00000000..3a5ca086
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/crypt_gensalt.h
@@ -0,0 +1,30 @@
+/*
+ * Written by Solar Designer in 2000-2011.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain. In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See crypt_blowfish.c for more information.
+ */
+
+#ifndef _CRYPT_GENSALT_H
+#define _CRYPT_GENSALT_H
+
+extern const unsigned char _crypt_itoa64[];
+extern char *_crypt_gensalt_traditional_rn(const char *prefix,
+ unsigned long count,
+ const char *input, int size, char *output, int output_size);
+extern char *_crypt_gensalt_extended_rn(const char *prefix,
+ unsigned long count,
+ const char *input, int size, char *output, int output_size);
+extern char *_crypt_gensalt_md5_rn(const char *prefix, unsigned long count,
+ const char *input, int size, char *output, int output_size);
+
+#endif
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/extconf.rb b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/extconf.rb
new file mode 100644
index 00000000..c2222de9
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/extconf.rb
@@ -0,0 +1,22 @@
+if RUBY_PLATFORM == "java"
+ # Don't do anything when run in JRuby; this allows gem installation to pass.
+ # We need to write a dummy Makefile so that RubyGems doesn't think compilation
+ # failed.
+ File.open('Makefile', 'w') do |f|
+ f.puts "all:"
+ f.puts "\t@true"
+ f.puts "install:"
+ f.puts "\t@true"
+ end
+ exit 0
+else
+ require "mkmf"
+
+ # From Openwall's crypt_blowfish Makefile.
+ # This is `bcrypt_ext` (our extension) + CRYPT_OBJS from that Makefile.
+ $objs = %w(bcrypt_ext.o crypt_blowfish.o x86.o crypt_gensalt.o wrapper.o)
+
+ $defs << "-D__SKIP_GNU"
+ dir_config("bcrypt_ext")
+ create_makefile("bcrypt_ext")
+end
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/ow-crypt.h b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/ow-crypt.h
new file mode 100644
index 00000000..2e487942
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/ow-crypt.h
@@ -0,0 +1,43 @@
+/*
+ * Written by Solar Designer in 2000-2011.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain. In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See crypt_blowfish.c for more information.
+ */
+
+#ifndef _OW_CRYPT_H
+#define _OW_CRYPT_H
+
+#ifndef __GNUC__
+#undef __const
+#define __const const
+#endif
+
+#ifndef __SKIP_GNU
+extern char *crypt(__const char *key, __const char *setting);
+extern char *crypt_r(__const char *key, __const char *setting, void *data);
+#endif
+
+#ifndef __SKIP_OW
+extern char *crypt_rn(__const char *key, __const char *setting,
+ void *data, int size);
+extern char *crypt_ra(__const char *key, __const char *setting,
+ void **data, int *size);
+extern char *crypt_gensalt(__const char *prefix, unsigned long count,
+ __const char *input, int size);
+extern char *crypt_gensalt_rn(__const char *prefix, unsigned long count,
+ __const char *input, int size, char *output, int output_size);
+extern char *crypt_gensalt_ra(__const char *prefix, unsigned long count,
+ __const char *input, int size);
+#endif
+
+#endif
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/wrapper.c b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/wrapper.c
new file mode 100644
index 00000000..cbba8825
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/wrapper.c
@@ -0,0 +1,554 @@
+/*
+ * Written by Solar Designer in 2000-2014.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain. In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 2000-2014 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See crypt_blowfish.c for more information.
+ */
+
+#include
+#include
+
+/* Redefine strdup to ruby_strdup in case string.h doesn't export it. */
+#include
+
+#include
+#ifndef __set_errno
+#define __set_errno(val) errno = (val)
+#endif
+
+#ifdef TEST
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef TEST_THREADS
+#include
+#endif
+#endif
+
+#define CRYPT_OUTPUT_SIZE (7 + 22 + 31 + 1)
+#define CRYPT_GENSALT_OUTPUT_SIZE (7 + 22 + 1)
+
+#if defined(__GLIBC__) && defined(_LIBC)
+#define __SKIP_GNU
+#endif
+#include "ow-crypt.h"
+
+#include "crypt_blowfish.h"
+#include "crypt_gensalt.h"
+
+#if defined(__GLIBC__) && defined(_LIBC)
+/* crypt.h from glibc-crypt-2.1 will define struct crypt_data for us */
+#include "crypt.h"
+extern char *__md5_crypt_r(const char *key, const char *salt,
+ char *buffer, int buflen);
+/* crypt-entry.c needs to be patched to define __des_crypt_r rather than
+ * __crypt_r, and not define crypt_r and crypt at all */
+extern char *__des_crypt_r(const char *key, const char *salt,
+ struct crypt_data *data);
+extern struct crypt_data _ufc_foobar;
+#endif
+
+static int _crypt_data_alloc(void **data, int *size, int need)
+{
+ void *updated;
+
+ if (*data && *size >= need) return 0;
+
+ updated = realloc(*data, need);
+
+ if (!updated) {
+#ifndef __GLIBC__
+ /* realloc(3) on glibc sets errno, so we don't need to bother */
+ __set_errno(ENOMEM);
+#endif
+ return -1;
+ }
+
+#if defined(__GLIBC__) && defined(_LIBC)
+ if (need >= sizeof(struct crypt_data))
+ ((struct crypt_data *)updated)->initialized = 0;
+#endif
+
+ *data = updated;
+ *size = need;
+
+ return 0;
+}
+
+static char *_crypt_retval_magic(char *retval, const char *setting,
+ char *output, int size)
+{
+ if (retval)
+ return retval;
+
+ if (_crypt_output_magic(setting, output, size))
+ return NULL; /* shouldn't happen */
+
+ return output;
+}
+
+#if defined(__GLIBC__) && defined(_LIBC)
+/*
+ * Applications may re-use the same instance of struct crypt_data without
+ * resetting the initialized field in order to let crypt_r() skip some of
+ * its initialization code. Thus, it is important that our multiple hashing
+ * algorithms either don't conflict with each other in their use of the
+ * data area or reset the initialized field themselves whenever required.
+ * Currently, the hashing algorithms simply have no conflicts: the first
+ * field of struct crypt_data is the 128-byte large DES key schedule which
+ * __des_crypt_r() calculates each time it is called while the two other
+ * hashing algorithms use less than 128 bytes of the data area.
+ */
+
+char *__crypt_rn(__const char *key, __const char *setting,
+ void *data, int size)
+{
+ if (setting[0] == '$' && setting[1] == '2')
+ return _crypt_blowfish_rn(key, setting, (char *)data, size);
+ if (setting[0] == '$' && setting[1] == '1')
+ return __md5_crypt_r(key, setting, (char *)data, size);
+ if (setting[0] == '$' || setting[0] == '_') {
+ __set_errno(EINVAL);
+ return NULL;
+ }
+ if (size >= sizeof(struct crypt_data))
+ return __des_crypt_r(key, setting, (struct crypt_data *)data);
+ __set_errno(ERANGE);
+ return NULL;
+}
+
+char *__crypt_ra(__const char *key, __const char *setting,
+ void **data, int *size)
+{
+ if (setting[0] == '$' && setting[1] == '2') {
+ if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
+ return NULL;
+ return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
+ }
+ if (setting[0] == '$' && setting[1] == '1') {
+ if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
+ return NULL;
+ return __md5_crypt_r(key, setting, (char *)*data, *size);
+ }
+ if (setting[0] == '$' || setting[0] == '_') {
+ __set_errno(EINVAL);
+ return NULL;
+ }
+ if (_crypt_data_alloc(data, size, sizeof(struct crypt_data)))
+ return NULL;
+ return __des_crypt_r(key, setting, (struct crypt_data *)*data);
+}
+
+char *__crypt_r(__const char *key, __const char *setting,
+ struct crypt_data *data)
+{
+ return _crypt_retval_magic(
+ __crypt_rn(key, setting, data, sizeof(*data)),
+ setting, (char *)data, sizeof(*data));
+}
+
+char *__crypt(__const char *key, __const char *setting)
+{
+ return _crypt_retval_magic(
+ __crypt_rn(key, setting, &_ufc_foobar, sizeof(_ufc_foobar)),
+ setting, (char *)&_ufc_foobar, sizeof(_ufc_foobar));
+}
+#else
+char *crypt_rn(const char *key, const char *setting, void *data, int size)
+{
+ return _crypt_blowfish_rn(key, setting, (char *)data, size);
+}
+
+char *crypt_ra(const char *key, const char *setting,
+ void **data, int *size)
+{
+ if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
+ return NULL;
+ return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
+}
+
+char *crypt_r(const char *key, const char *setting, struct crypt_data *data)
+{
+ return _crypt_retval_magic(
+ crypt_rn(key, setting, data, CRYPT_OUTPUT_SIZE),
+ setting, (char *)data, CRYPT_OUTPUT_SIZE);
+}
+
+char *crypt(const char *key, const char *setting)
+{
+ static char output[CRYPT_OUTPUT_SIZE];
+
+ return _crypt_retval_magic(
+ crypt_rn(key, setting, output, sizeof(output)),
+ setting, output, sizeof(output));
+}
+
+#define __crypt_gensalt_rn crypt_gensalt_rn
+#define __crypt_gensalt_ra crypt_gensalt_ra
+#define __crypt_gensalt crypt_gensalt
+#endif
+
+char *__crypt_gensalt_rn(const char *prefix, unsigned long count,
+ const char *input, int size, char *output, int output_size)
+{
+ char *(*use)(const char *_prefix, unsigned long _count,
+ const char *_input, int _size,
+ char *_output, int _output_size);
+
+ /* This may be supported on some platforms in the future */
+ if (!input) {
+ __set_errno(EINVAL);
+ return NULL;
+ }
+
+ if (!strncmp(prefix, "$2a$", 4) || !strncmp(prefix, "$2b$", 4) ||
+ !strncmp(prefix, "$2y$", 4))
+ use = _crypt_gensalt_blowfish_rn;
+ else
+ if (!strncmp(prefix, "$1$", 3))
+ use = _crypt_gensalt_md5_rn;
+ else
+ if (prefix[0] == '_')
+ use = _crypt_gensalt_extended_rn;
+ else
+ if (!prefix[0] ||
+ (prefix[0] && prefix[1] &&
+ memchr(_crypt_itoa64, prefix[0], 64) &&
+ memchr(_crypt_itoa64, prefix[1], 64)))
+ use = _crypt_gensalt_traditional_rn;
+ else {
+ __set_errno(EINVAL);
+ return NULL;
+ }
+
+ return use(prefix, count, input, size, output, output_size);
+}
+
+char *__crypt_gensalt_ra(const char *prefix, unsigned long count,
+ const char *input, int size)
+{
+ char output[CRYPT_GENSALT_OUTPUT_SIZE];
+ char *retval;
+
+ retval = __crypt_gensalt_rn(prefix, count,
+ input, size, output, sizeof(output));
+
+ if (retval) {
+ retval = strdup(retval);
+#ifndef __GLIBC__
+ /* strdup(3) on glibc sets errno, so we don't need to bother */
+ if (!retval)
+ __set_errno(ENOMEM);
+#endif
+ }
+
+ return retval;
+}
+
+char *__crypt_gensalt(const char *prefix, unsigned long count,
+ const char *input, int size)
+{
+ static char output[CRYPT_GENSALT_OUTPUT_SIZE];
+
+ return __crypt_gensalt_rn(prefix, count,
+ input, size, output, sizeof(output));
+}
+
+#if defined(__GLIBC__) && defined(_LIBC)
+weak_alias(__crypt_rn, crypt_rn)
+weak_alias(__crypt_ra, crypt_ra)
+weak_alias(__crypt_r, crypt_r)
+weak_alias(__crypt, crypt)
+weak_alias(__crypt_gensalt_rn, crypt_gensalt_rn)
+weak_alias(__crypt_gensalt_ra, crypt_gensalt_ra)
+weak_alias(__crypt_gensalt, crypt_gensalt)
+weak_alias(crypt, fcrypt)
+#endif
+
+#ifdef TEST
+static const char *tests[][3] = {
+ {"$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW",
+ "U*U"},
+ {"$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK",
+ "U*U*"},
+ {"$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a",
+ "U*U*U"},
+ {"$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui",
+ "0123456789abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ "chars after 72 are ignored"},
+ {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
+ "\xa3"},
+ {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
+ "\xff\xff\xa3"},
+ {"$2y$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
+ "\xff\xff\xa3"},
+ {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nqd1wy.pTMdcvrRWxyiGL2eMz.2a85.",
+ "\xff\xff\xa3"},
+ {"$2b$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
+ "\xff\xff\xa3"},
+ {"$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
+ "\xa3"},
+ {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
+ "\xa3"},
+ {"$2b$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
+ "\xa3"},
+ {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
+ "1\xa3" "345"},
+ {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
+ "\xff\xa3" "345"},
+ {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
+ "\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
+ {"$2y$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
+ "\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
+ {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.ZC1JEJ8Z4gPfpe1JOr/oyPXTWl9EFd.",
+ "\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
+ {"$2y$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
+ "\xff\xa3" "345"},
+ {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
+ "\xff\xa3" "345"},
+ {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
+ "\xa3" "ab"},
+ {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
+ "\xa3" "ab"},
+ {"$2y$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
+ "\xa3" "ab"},
+ {"$2x$05$6bNw2HLQYeqHYyBfLMsv/OiwqTymGIGzFsA4hOTWebfehXHNprcAS",
+ "\xd1\x91"},
+ {"$2x$05$6bNw2HLQYeqHYyBfLMsv/O9LIGgn8OMzuDoHfof8AQimSGfcSWxnS",
+ "\xd0\xc1\xd2\xcf\xcc\xd8"},
+ {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6",
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "chars after 72 are ignored as usual"},
+ {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy",
+ "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
+ "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
+ "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
+ "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
+ "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
+ "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"},
+ {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe",
+ "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
+ "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
+ "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
+ "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
+ "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
+ "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"},
+ {"$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy",
+ ""},
+ {"*0", "", "$2a$03$CCCCCCCCCCCCCCCCCCCCC."},
+ {"*0", "", "$2a$32$CCCCCCCCCCCCCCCCCCCCC."},
+ {"*0", "", "$2c$05$CCCCCCCCCCCCCCCCCCCCC."},
+ {"*0", "", "$2z$05$CCCCCCCCCCCCCCCCCCCCC."},
+ {"*0", "", "$2`$05$CCCCCCCCCCCCCCCCCCCCC."},
+ {"*0", "", "$2{$05$CCCCCCCCCCCCCCCCCCCCC."},
+ {"*1", "", "*0"},
+ {NULL}
+};
+
+#define which tests[0]
+
+static volatile sig_atomic_t running;
+
+static void handle_timer(int signum)
+{
+ (void) signum;
+ running = 0;
+}
+
+static void *run(void *arg)
+{
+ unsigned long count = 0;
+ int i = 0;
+ void *data = NULL;
+ int size = 0x12345678;
+
+ do {
+ const char *hash = tests[i][0];
+ const char *key = tests[i][1];
+ const char *setting = tests[i][2];
+
+ if (!tests[++i][0])
+ i = 0;
+
+ if (setting && strlen(hash) < 30) /* not for benchmark */
+ continue;
+
+ if (strcmp(crypt_ra(key, hash, &data, &size), hash)) {
+ printf("%d: FAILED (crypt_ra/%d/%lu)\n",
+ (int)((char *)arg - (char *)0), i, count);
+ free(data);
+ return NULL;
+ }
+ count++;
+ } while (running);
+
+ free(data);
+ return count + (char *)0;
+}
+
+int main(void)
+{
+ struct itimerval it;
+ struct tms buf;
+ clock_t clk_tck, start_real, start_virtual, end_real, end_virtual;
+ unsigned long count;
+ void *data;
+ int size;
+ char *setting1, *setting2;
+ int i;
+#ifdef TEST_THREADS
+ pthread_t t[TEST_THREADS];
+ void *t_retval;
+#endif
+
+ data = NULL;
+ size = 0x12345678;
+
+ for (i = 0; tests[i][0]; i++) {
+ const char *hash = tests[i][0];
+ const char *key = tests[i][1];
+ const char *setting = tests[i][2];
+ const char *p;
+ int ok = !setting || strlen(hash) >= 30;
+ int o_size;
+ char s_buf[30], o_buf[61];
+ if (!setting) {
+ memcpy(s_buf, hash, sizeof(s_buf) - 1);
+ s_buf[sizeof(s_buf) - 1] = 0;
+ setting = s_buf;
+ }
+
+ __set_errno(0);
+ p = crypt(key, setting);
+ if ((!ok && !errno) || strcmp(p, hash)) {
+ printf("FAILED (crypt/%d)\n", i);
+ return 1;
+ }
+
+ if (ok && strcmp(crypt(key, hash), hash)) {
+ printf("FAILED (crypt/%d)\n", i);
+ return 1;
+ }
+
+ for (o_size = -1; o_size <= (int)sizeof(o_buf); o_size++) {
+ int ok_n = ok && o_size == (int)sizeof(o_buf);
+ const char *x = "abc";
+ strcpy(o_buf, x);
+ if (o_size >= 3) {
+ x = "*0";
+ if (setting[0] == '*' && setting[1] == '0')
+ x = "*1";
+ }
+ __set_errno(0);
+ p = crypt_rn(key, setting, o_buf, o_size);
+ if ((ok_n && (!p || strcmp(p, hash))) ||
+ (!ok_n && (!errno || p || strcmp(o_buf, x)))) {
+ printf("FAILED (crypt_rn/%d)\n", i);
+ return 1;
+ }
+ }
+
+ __set_errno(0);
+ p = crypt_ra(key, setting, &data, &size);
+ if ((ok && (!p || strcmp(p, hash))) ||
+ (!ok && (!errno || p || strcmp((char *)data, hash)))) {
+ printf("FAILED (crypt_ra/%d)\n", i);
+ return 1;
+ }
+ }
+
+ setting1 = crypt_gensalt(which[0], 12, data, size);
+ if (!setting1 || strncmp(setting1, "$2a$12$", 7)) {
+ puts("FAILED (crypt_gensalt)\n");
+ return 1;
+ }
+
+ setting2 = crypt_gensalt_ra(setting1, 12, data, size);
+ if (strcmp(setting1, setting2)) {
+ puts("FAILED (crypt_gensalt_ra/1)\n");
+ return 1;
+ }
+
+ (*(char *)data)++;
+ setting1 = crypt_gensalt_ra(setting2, 12, data, size);
+ if (!strcmp(setting1, setting2)) {
+ puts("FAILED (crypt_gensalt_ra/2)\n");
+ return 1;
+ }
+
+ free(setting1);
+ free(setting2);
+ free(data);
+
+#if defined(_SC_CLK_TCK) || !defined(CLK_TCK)
+ clk_tck = sysconf(_SC_CLK_TCK);
+#else
+ clk_tck = CLK_TCK;
+#endif
+
+ running = 1;
+ signal(SIGALRM, handle_timer);
+
+ memset(&it, 0, sizeof(it));
+ it.it_value.tv_sec = 5;
+ setitimer(ITIMER_REAL, &it, NULL);
+
+ start_real = times(&buf);
+ start_virtual = buf.tms_utime + buf.tms_stime;
+
+ count = (char *)run((char *)0) - (char *)0;
+
+ end_real = times(&buf);
+ end_virtual = buf.tms_utime + buf.tms_stime;
+ if (end_virtual == start_virtual) end_virtual++;
+
+ printf("%.1f c/s real, %.1f c/s virtual\n",
+ (float)count * clk_tck / (end_real - start_real),
+ (float)count * clk_tck / (end_virtual - start_virtual));
+
+#ifdef TEST_THREADS
+ running = 1;
+ it.it_value.tv_sec = 60;
+ setitimer(ITIMER_REAL, &it, NULL);
+ start_real = times(&buf);
+
+ for (i = 0; i < TEST_THREADS; i++)
+ if (pthread_create(&t[i], NULL, run, i + (char *)0)) {
+ perror("pthread_create");
+ return 1;
+ }
+
+ for (i = 0; i < TEST_THREADS; i++) {
+ if (pthread_join(t[i], &t_retval)) {
+ perror("pthread_join");
+ continue;
+ }
+ if (!t_retval) continue;
+ count = (char *)t_retval - (char *)0;
+ end_real = times(&buf);
+ printf("%d: %.1f c/s real\n", i,
+ (float)count * clk_tck / (end_real - start_real));
+ }
+#endif
+
+ return 0;
+}
+#endif
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/x86.S b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/x86.S
new file mode 100644
index 00000000..4c5c0696
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/ext/mri/x86.S
@@ -0,0 +1,203 @@
+/*
+ * Written by Solar Designer in 1998-2010.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain. In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 1998-2010 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See crypt_blowfish.c for more information.
+ */
+
+#ifdef __i386__
+
+#if defined(__OpenBSD__) && !defined(__ELF__)
+#define UNDERSCORES
+#define ALIGN_LOG
+#endif
+
+#if defined(__CYGWIN32__) || defined(__MINGW32__)
+#define UNDERSCORES
+#endif
+
+#ifdef __DJGPP__
+#define UNDERSCORES
+#define ALIGN_LOG
+#endif
+
+#ifdef UNDERSCORES
+#define _BF_body_r __BF_body_r
+#endif
+
+#ifdef ALIGN_LOG
+#define DO_ALIGN(log) .align (log)
+#elif defined(DUMBAS)
+#define DO_ALIGN(log) .align 1 << log
+#else
+#define DO_ALIGN(log) .align (1 << (log))
+#endif
+
+#define BF_FRAME 0x200
+#define ctx %esp
+
+#define BF_ptr (ctx)
+
+#define S(N, r) N+BF_FRAME(ctx,r,4)
+#ifdef DUMBAS
+#define P(N) 0x1000+N+N+N+N+BF_FRAME(ctx)
+#else
+#define P(N) 0x1000+4*N+BF_FRAME(ctx)
+#endif
+
+/*
+ * This version of the assembly code is optimized primarily for the original
+ * Intel Pentium but is also careful to avoid partial register stalls on the
+ * Pentium Pro family of processors (tested up to Pentium III Coppermine).
+ *
+ * It is possible to do 15% faster on the Pentium Pro family and probably on
+ * many non-Intel x86 processors, but, unfortunately, that would make things
+ * twice slower for the original Pentium.
+ *
+ * An additional 2% speedup may be achieved with non-reentrant code.
+ */
+
+#define L %esi
+#define R %edi
+#define tmp1 %eax
+#define tmp1_lo %al
+#define tmp2 %ecx
+#define tmp2_hi %ch
+#define tmp3 %edx
+#define tmp3_lo %dl
+#define tmp4 %ebx
+#define tmp4_hi %bh
+#define tmp5 %ebp
+
+.text
+
+#define BF_ROUND(L, R, N) \
+ xorl L,tmp2; \
+ xorl tmp1,tmp1; \
+ movl tmp2,L; \
+ shrl $16,tmp2; \
+ movl L,tmp4; \
+ movb tmp2_hi,tmp1_lo; \
+ andl $0xFF,tmp2; \
+ movb tmp4_hi,tmp3_lo; \
+ andl $0xFF,tmp4; \
+ movl S(0,tmp1),tmp1; \
+ movl S(0x400,tmp2),tmp5; \
+ addl tmp5,tmp1; \
+ movl S(0x800,tmp3),tmp5; \
+ xorl tmp5,tmp1; \
+ movl S(0xC00,tmp4),tmp5; \
+ addl tmp1,tmp5; \
+ movl 4+P(N),tmp2; \
+ xorl tmp5,R
+
+#define BF_ENCRYPT_START \
+ BF_ROUND(L, R, 0); \
+ BF_ROUND(R, L, 1); \
+ BF_ROUND(L, R, 2); \
+ BF_ROUND(R, L, 3); \
+ BF_ROUND(L, R, 4); \
+ BF_ROUND(R, L, 5); \
+ BF_ROUND(L, R, 6); \
+ BF_ROUND(R, L, 7); \
+ BF_ROUND(L, R, 8); \
+ BF_ROUND(R, L, 9); \
+ BF_ROUND(L, R, 10); \
+ BF_ROUND(R, L, 11); \
+ BF_ROUND(L, R, 12); \
+ BF_ROUND(R, L, 13); \
+ BF_ROUND(L, R, 14); \
+ BF_ROUND(R, L, 15); \
+ movl BF_ptr,tmp5; \
+ xorl L,tmp2; \
+ movl P(17),L
+
+#define BF_ENCRYPT_END \
+ xorl R,L; \
+ movl tmp2,R
+
+DO_ALIGN(5)
+.globl _BF_body_r
+_BF_body_r:
+ movl 4(%esp),%eax
+ pushl %ebp
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ subl $BF_FRAME-8,%eax
+ xorl L,L
+ cmpl %esp,%eax
+ ja BF_die
+ xchgl %eax,%esp
+ xorl R,R
+ pushl %eax
+ leal 0x1000+BF_FRAME-4(ctx),%eax
+ movl 0x1000+BF_FRAME-4(ctx),tmp2
+ pushl %eax
+ xorl tmp3,tmp3
+BF_loop_P:
+ BF_ENCRYPT_START
+ addl $8,tmp5
+ BF_ENCRYPT_END
+ leal 0x1000+18*4+BF_FRAME(ctx),tmp1
+ movl tmp5,BF_ptr
+ cmpl tmp5,tmp1
+ movl L,-8(tmp5)
+ movl R,-4(tmp5)
+ movl P(0),tmp2
+ ja BF_loop_P
+ leal BF_FRAME(ctx),tmp5
+ xorl tmp3,tmp3
+ movl tmp5,BF_ptr
+BF_loop_S:
+ BF_ENCRYPT_START
+ BF_ENCRYPT_END
+ movl P(0),tmp2
+ movl L,(tmp5)
+ movl R,4(tmp5)
+ BF_ENCRYPT_START
+ BF_ENCRYPT_END
+ movl P(0),tmp2
+ movl L,8(tmp5)
+ movl R,12(tmp5)
+ BF_ENCRYPT_START
+ BF_ENCRYPT_END
+ movl P(0),tmp2
+ movl L,16(tmp5)
+ movl R,20(tmp5)
+ BF_ENCRYPT_START
+ addl $32,tmp5
+ BF_ENCRYPT_END
+ leal 0x1000+BF_FRAME(ctx),tmp1
+ movl tmp5,BF_ptr
+ cmpl tmp5,tmp1
+ movl P(0),tmp2
+ movl L,-8(tmp5)
+ movl R,-4(tmp5)
+ ja BF_loop_S
+ movl 4(%esp),%esp
+ popl %edi
+ popl %esi
+ popl %ebx
+ popl %ebp
+ ret
+
+BF_die:
+/* Oops, need to re-compile with a larger BF_FRAME. */
+ hlt
+ jmp BF_die
+
+#endif
+
+#if defined(__ELF__) && defined(__linux__)
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt.rb b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt.rb
new file mode 100644
index 00000000..c1b9e479
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt.rb
@@ -0,0 +1,16 @@
+# A Ruby library implementing OpenBSD's bcrypt()/crypt_blowfish algorithm for
+# hashing passwords.
+module BCrypt
+end
+
+if RUBY_PLATFORM == "java"
+ require 'java'
+else
+ require "openssl"
+end
+
+require "bcrypt_ext"
+
+require 'bcrypt/error'
+require 'bcrypt/engine'
+require 'bcrypt/password'
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt/engine.rb b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt/engine.rb
new file mode 100644
index 00000000..552b7b0b
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt/engine.rb
@@ -0,0 +1,127 @@
+module BCrypt
+ # A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
+ class Engine
+ # The default computational expense parameter.
+ DEFAULT_COST = 12
+ # The minimum cost supported by the algorithm.
+ MIN_COST = 4
+ # The maximum cost supported by the algorithm.
+ MAX_COST = 31
+ # Maximum possible size of bcrypt() secrets.
+ # Older versions of the bcrypt library would truncate passwords longer
+ # than 72 bytes, but newer ones do not. We truncate like the old library for
+ # forward compatibility. This way users upgrading from Ubuntu 18.04 to 20.04
+ # will not have their user passwords invalidated, for example.
+ # A max secret length greater than 255 leads to bcrypt returning nil.
+ # https://github.com/bcrypt-ruby/bcrypt-ruby/issues/225#issuecomment-875908425
+ MAX_SECRET_BYTESIZE = 72
+ # Maximum possible size of bcrypt() salts.
+ MAX_SALT_LENGTH = 16
+
+ if RUBY_PLATFORM != "java"
+ # C-level routines which, if they don't get the right input, will crash the
+ # hell out of the Ruby process.
+ private_class_method :__bc_salt
+ private_class_method :__bc_crypt
+ end
+
+ @cost = nil
+
+ # Returns the cost factor that will be used if one is not specified when
+ # creating a password hash. Defaults to DEFAULT_COST if not set.
+ def self.cost
+ @cost || DEFAULT_COST
+ end
+
+ # Set a default cost factor that will be used if one is not specified when
+ # creating a password hash.
+ #
+ # Example:
+ #
+ # BCrypt::Engine::DEFAULT_COST #=> 12
+ # BCrypt::Password.create('secret').cost #=> 12
+ #
+ # BCrypt::Engine.cost = 8
+ # BCrypt::Password.create('secret').cost #=> 8
+ #
+ # # cost can still be overridden as needed
+ # BCrypt::Password.create('secret', :cost => 6).cost #=> 6
+ def self.cost=(cost)
+ @cost = cost
+ end
+
+ # Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates
+ # a bcrypt() password hash. Secrets longer than 72 bytes are truncated.
+ def self.hash_secret(secret, salt, _ = nil)
+ if valid_secret?(secret)
+ if valid_salt?(salt)
+ if RUBY_PLATFORM == "java"
+ Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s.to_java_bytes, salt.to_s)
+ else
+ secret = secret.to_s
+ secret = secret.byteslice(0, MAX_SECRET_BYTESIZE) if secret && secret.bytesize > MAX_SECRET_BYTESIZE
+ __bc_crypt(secret, salt)
+ end
+ else
+ raise Errors::InvalidSalt.new("invalid salt")
+ end
+ else
+ raise Errors::InvalidSecret.new("invalid secret")
+ end
+ end
+
+ # Generates a random salt with a given computational cost.
+ def self.generate_salt(cost = self.cost)
+ cost = cost.to_i
+ if cost > 0
+ if cost < MIN_COST
+ cost = MIN_COST
+ end
+ if RUBY_PLATFORM == "java"
+ Java.bcrypt_jruby.BCrypt.gensalt(cost)
+ else
+ __bc_salt("$2a$", cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
+ end
+ else
+ raise Errors::InvalidCost.new("cost must be numeric and > 0")
+ end
+ end
+
+ # Returns true if +salt+ is a valid bcrypt() salt, false if not.
+ def self.valid_salt?(salt)
+ !!(salt =~ /\A\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}\z/)
+ end
+
+ # Returns true if +secret+ is a valid bcrypt() secret, false if not.
+ def self.valid_secret?(secret)
+ secret.respond_to?(:to_s)
+ end
+
+ # Returns the cost factor which will result in computation times less than +upper_time_limit_in_ms+.
+ #
+ # Example:
+ #
+ # BCrypt::Engine.calibrate(200) #=> 10
+ # BCrypt::Engine.calibrate(1000) #=> 12
+ #
+ # # should take less than 200ms
+ # BCrypt::Password.create("woo", :cost => 10)
+ #
+ # # should take less than 1000ms
+ # BCrypt::Password.create("woo", :cost => 12)
+ def self.calibrate(upper_time_limit_in_ms)
+ (BCrypt::Engine::MIN_COST..BCrypt::Engine::MAX_COST-1).each do |i|
+ start_time = Time.now
+ Password.create("testing testing", :cost => i+1)
+ end_time = Time.now - start_time
+ return i if end_time * 1_000 > upper_time_limit_in_ms
+ end
+ end
+
+ # Autodetects the cost from the salt string.
+ def self.autodetect_cost(salt)
+ salt[4..5].to_i
+ end
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt/error.rb b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt/error.rb
new file mode 100644
index 00000000..c7e42a4d
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt/error.rb
@@ -0,0 +1,22 @@
+module BCrypt
+
+ class Error < StandardError # :nodoc:
+ end
+
+ module Errors # :nodoc:
+
+ # The salt parameter provided to bcrypt() is invalid.
+ class InvalidSalt < BCrypt::Error; end
+
+ # The hash parameter provided to bcrypt() is invalid.
+ class InvalidHash < BCrypt::Error; end
+
+ # The cost parameter provided to bcrypt() is invalid.
+ class InvalidCost < BCrypt::Error; end
+
+ # The secret parameter provided to bcrypt() is invalid.
+ class InvalidSecret < BCrypt::Error; end
+
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt/password.rb b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt/password.rb
new file mode 100644
index 00000000..4a2c1402
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/lib/bcrypt/password.rb
@@ -0,0 +1,97 @@
+module BCrypt
+ # A password management class which allows you to safely store users' passwords and compare them.
+ #
+ # Example usage:
+ #
+ # include BCrypt
+ #
+ # # hash a user's password
+ # @password = Password.create("my grand secret")
+ # @password #=> "$2a$12$C5.FIvVDS9W4AYZ/Ib37YuWd/7ozp1UaMhU28UKrfSxp2oDchbi3K"
+ #
+ # # store it safely
+ # @user.update_attribute(:password, @password)
+ #
+ # # read it back
+ # @user.reload!
+ # @db_password = Password.new(@user.password)
+ #
+ # # compare it after retrieval
+ # @db_password == "my grand secret" #=> true
+ # @db_password == "a paltry guess" #=> false
+ #
+ class Password < String
+ # The hash portion of the stored password hash.
+ attr_reader :checksum
+ # The salt of the store password hash (including version and cost).
+ attr_reader :salt
+ # The version of the bcrypt() algorithm used to create the hash.
+ attr_reader :version
+ # The cost factor used to create the hash.
+ attr_reader :cost
+
+ class << self
+ # Hashes a secret, returning a BCrypt::Password instance. Takes an optional :cost option, which is a
+ # logarithmic variable which determines how computational expensive the hash is to calculate (a :cost of
+ # 4 is twice as much work as a :cost of 3). The higher the :cost the harder it becomes for
+ # attackers to try to guess passwords (even if a copy of your database is stolen), but the slower it is to check
+ # users' passwords.
+ #
+ # Example:
+ #
+ # @password = BCrypt::Password.create("my secret", :cost => 13)
+ def create(secret, options = {})
+ cost = options[:cost] || BCrypt::Engine.cost
+ raise ArgumentError if cost > BCrypt::Engine::MAX_COST
+ Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(cost)))
+ end
+
+ def valid_hash?(h)
+ /\A\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}\z/ === h
+ end
+ end
+
+ # Initializes a BCrypt::Password instance with the data from a stored hash.
+ def initialize(raw_hash)
+ if valid_hash?(raw_hash)
+ self.replace(raw_hash)
+ @version, @cost, @salt, @checksum = split_hash(self)
+ else
+ raise Errors::InvalidHash.new("invalid hash")
+ end
+ end
+
+ # Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
+ #
+ # Comparison edge case/gotcha:
+ #
+ # secret = "my secret"
+ # @password = BCrypt::Password.create(secret)
+ #
+ # @password == secret # => True
+ # @password == @password # => False
+ # @password == @password.to_s # => False
+ # @password.to_s == @password # => True
+ # @password.to_s == @password.to_s # => True
+ def ==(secret)
+ super(BCrypt::Engine.hash_secret(secret, @salt))
+ end
+ alias_method :is_password?, :==
+
+ private
+
+ # Returns true if +h+ is a valid hash.
+ def valid_hash?(h)
+ self.class.valid_hash?(h)
+ end
+
+ # call-seq:
+ # split_hash(raw_hash) -> version, cost, salt, hash
+ #
+ # Splits +h+ into version, cost, salt, and hash and returns them in that order.
+ def split_hash(h)
+ _, v, c, mash = h.split('$')
+ return v.to_str, c.to_i, h[0, 29].to_str, mash[-31, 31].to_str
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/TestBCrypt.java b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/TestBCrypt.java
new file mode 100644
index 00000000..c481ff3c
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/TestBCrypt.java
@@ -0,0 +1,194 @@
+// 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.
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit unit tests for BCrypt routines
+ * @author Damien Miller
+ * @version 0.2
+ */
+public class TestBCrypt extends TestCase {
+ String test_vectors[][] = {
+ { "",
+ "$2a$06$DCq7YPn5Rq63x1Lad4cll.",
+ "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s." },
+ { "",
+ "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.",
+ "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye" },
+ { "",
+ "$2a$10$k1wbIrmNyFAPwPVPSVa/ze",
+ "$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW" },
+ { "",
+ "$2a$12$k42ZFHFWqBp3vWli.nIn8u",
+ "$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO" },
+ { "a",
+ "$2a$06$m0CrhHm10qJ3lXRY.5zDGO",
+ "$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe" },
+ { "a",
+ "$2a$08$cfcvVd2aQ8CMvoMpP2EBfe",
+ "$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V." },
+ { "a",
+ "$2a$10$k87L/MF28Q673VKh8/cPi.",
+ "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u" },
+ { "a",
+ "$2a$12$8NJH3LsPrANStV6XtBakCe",
+ "$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS" },
+ { "abc",
+ "$2a$06$If6bvum7DFjUnE9p2uDeDu",
+ "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i" },
+ { "abc",
+ "$2a$08$Ro0CUfOqk6cXEKf3dyaM7O",
+ "$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm" },
+ { "abc",
+ "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.",
+ "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi" },
+ { "abc",
+ "$2a$12$EXRkfkdmXn2gzds2SSitu.",
+ "$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q" },
+ { "abcdefghijklmnopqrstuvwxyz",
+ "$2a$06$.rCVZVOThsIa97pEDOxvGu",
+ "$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC" },
+ { "abcdefghijklmnopqrstuvwxyz",
+ "$2a$08$aTsUwsyowQuzRrDqFflhge",
+ "$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz." },
+ { "abcdefghijklmnopqrstuvwxyz",
+ "$2a$10$fVH8e28OQRj9tqiDXs1e1u",
+ "$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq" },
+ { "abcdefghijklmnopqrstuvwxyz",
+ "$2a$12$D4G5f18o7aMMfwasBL7Gpu",
+ "$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG" },
+ { "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
+ "$2a$06$fPIsBO8qRqkjj273rfaOI.",
+ "$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO" },
+ { "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
+ "$2a$08$Eq2r4G/76Wv39MzSX262hu",
+ "$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW" },
+ { "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
+ "$2a$10$LgfYWkbzEvQ4JakH7rOvHe",
+ "$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS" },
+ { "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
+ "$2a$12$WApznUOJfkEGSmYRfnkrPO",
+ "$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC" },
+ };
+
+ /**
+ * Entry point for unit tests
+ * @param args unused
+ */
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(TestBCrypt.class);
+ }
+
+ /**
+ * Test method for 'BCrypt.hashpw(String, String)'
+ */
+ public void testHashpw() {
+ System.out.print("BCrypt.hashpw(): ");
+ for (int i = 0; i < test_vectors.length; i++) {
+ String plain = test_vectors[i][0];
+ String salt = test_vectors[i][1];
+ String expected = test_vectors[i][2];
+ String hashed = BCrypt.hashpw(plain, salt);
+ assertEquals(hashed, expected);
+ System.out.print(".");
+ }
+ System.out.println("");
+ }
+
+ /**
+ * Test method for 'BCrypt.gensalt(int)'
+ */
+ public void testGensaltInt() {
+ System.out.print("BCrypt.gensalt(log_rounds):");
+ for (int i = 4; i <= 12; i++) {
+ System.out.print(" " + Integer.toString(i) + ":");
+ for (int j = 0; j < test_vectors.length; j += 4) {
+ String plain = test_vectors[j][0];
+ String salt = BCrypt.gensalt(i);
+ String hashed1 = BCrypt.hashpw(plain, salt);
+ String hashed2 = BCrypt.hashpw(plain, hashed1);
+ assertEquals(hashed1, hashed2);
+ System.out.print(".");
+ }
+ }
+ System.out.println("");
+ }
+
+ /**
+ * Test method for 'BCrypt.gensalt()'
+ */
+ public void testGensalt() {
+ System.out.print("BCrypt.gensalt(): ");
+ for (int i = 0; i < test_vectors.length; i += 4) {
+ String plain = test_vectors[i][0];
+ String salt = BCrypt.gensalt();
+ String hashed1 = BCrypt.hashpw(plain, salt);
+ String hashed2 = BCrypt.hashpw(plain, hashed1);
+ assertEquals(hashed1, hashed2);
+ System.out.print(".");
+ }
+ System.out.println("");
+ }
+
+ /**
+ * Test method for 'BCrypt.checkpw(String, String)'
+ * expecting success
+ */
+ public void testCheckpw_success() {
+ System.out.print("BCrypt.checkpw w/ good passwords: ");
+ for (int i = 0; i < test_vectors.length; i++) {
+ String plain = test_vectors[i][0];
+ String expected = test_vectors[i][2];
+ assertTrue(BCrypt.checkpw(plain, expected));
+ System.out.print(".");
+ }
+ System.out.println("");
+ }
+
+ /**
+ * Test method for 'BCrypt.checkpw(String, String)'
+ * expecting failure
+ */
+ public void testCheckpw_failure() {
+ System.out.print("BCrypt.checkpw w/ bad passwords: ");
+ for (int i = 0; i < test_vectors.length; i++) {
+ int broken_index = (i + 4) % test_vectors.length;
+ String plain = test_vectors[i][0];
+ String expected = test_vectors[broken_index][2];
+ assertFalse(BCrypt.checkpw(plain, expected));
+ System.out.print(".");
+ }
+ System.out.println("");
+ }
+
+ /**
+ * Test for correct hashing of non-US-ASCII passwords
+ */
+ public void testInternationalChars() {
+ System.out.print("BCrypt.hashpw w/ international chars: ");
+ String pw1 = "ππππππππ";
+ String pw2 = "????????";
+
+ String h1 = BCrypt.hashpw(pw1, BCrypt.gensalt());
+ assertFalse(BCrypt.checkpw(pw2, h1));
+ System.out.print(".");
+
+ String h2 = BCrypt.hashpw(pw2, BCrypt.gensalt());
+ assertFalse(BCrypt.checkpw(pw1, h2));
+ System.out.print(".");
+ System.out.println("");
+ }
+
+}
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/bcrypt/engine_spec.rb b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/bcrypt/engine_spec.rb
new file mode 100644
index 00000000..b527a6bd
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/bcrypt/engine_spec.rb
@@ -0,0 +1,176 @@
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
+require 'securerandom'
+
+describe 'BCrypt::Engine' do
+ describe '.calibrate(upper_time_limit_in_ms)' do
+ context 'a tiny upper time limit provided' do
+ it 'returns a minimum cost supported by the algorithm' do
+ expect(BCrypt::Engine.calibrate(0.001)).to eq(4)
+ end
+ end
+ end
+end
+
+describe "The BCrypt engine" do
+ specify "should calculate the optimal cost factor to fit in a specific time" do
+ start_time = Time.now
+ BCrypt::Password.create("testing testing", :cost => BCrypt::Engine::MIN_COST + 1)
+ min_time_ms = (Time.now - start_time) * 1000
+ first = BCrypt::Engine.calibrate(min_time_ms)
+ second = BCrypt::Engine.calibrate(min_time_ms * 4)
+ expect(second).to be > first
+ end
+end
+
+describe "Generating BCrypt salts" do
+
+ specify "should produce strings" do
+ expect(BCrypt::Engine.generate_salt).to be_an_instance_of(String)
+ end
+
+ specify "should produce random data" do
+ expect(BCrypt::Engine.generate_salt).to_not equal(BCrypt::Engine.generate_salt)
+ end
+
+ specify "should raise a InvalidCostError if the cost parameter isn't numeric" do
+ expect { BCrypt::Engine.generate_salt('woo') }.to raise_error(BCrypt::Errors::InvalidCost)
+ end
+
+ specify "should raise a InvalidCostError if the cost parameter isn't greater than 0" do
+ expect { BCrypt::Engine.generate_salt(-1) }.to raise_error(BCrypt::Errors::InvalidCost)
+ end
+end
+
+describe "Autodetecting of salt cost" do
+
+ specify "should work" do
+ expect(BCrypt::Engine.autodetect_cost("$2a$08$hRx2IVeHNsTSYYtUWn61Ou")).to eq 8
+ expect(BCrypt::Engine.autodetect_cost("$2a$05$XKd1bMnLgUnc87qvbAaCUu")).to eq 5
+ expect(BCrypt::Engine.autodetect_cost("$2a$13$Lni.CZ6z5A7344POTFBBV.")).to eq 13
+ end
+
+end
+
+describe "Generating BCrypt hashes" do
+
+ class MyInvalidSecret
+ undef to_s
+ end
+
+ before :each do
+ @salt = BCrypt::Engine.generate_salt(4)
+ @password = "woo"
+ end
+
+ specify "should produce a string" do
+ expect(BCrypt::Engine.hash_secret(@password, @salt)).to be_an_instance_of(String)
+ end
+
+ specify "should raise an InvalidSalt error if the salt is invalid" do
+ expect { BCrypt::Engine.hash_secret(@password, 'nino') }.to raise_error(BCrypt::Errors::InvalidSalt)
+ end
+
+ specify "should raise an InvalidSecret error if the secret is invalid" do
+ expect { BCrypt::Engine.hash_secret(MyInvalidSecret.new, @salt) }.to raise_error(BCrypt::Errors::InvalidSecret)
+ expect { BCrypt::Engine.hash_secret(nil, @salt) }.not_to raise_error
+ expect { BCrypt::Engine.hash_secret(false, @salt) }.not_to raise_error
+ end
+
+ specify "should call #to_s on the secret and use the return value as the actual secret data" do
+ expect(BCrypt::Engine.hash_secret(false, @salt)).to eq BCrypt::Engine.hash_secret("false", @salt)
+ end
+
+ specify "should be interoperable with other implementations" do
+ test_vectors = [
+ # test vectors from the OpenWall implementation , found in wrapper.c
+ ["U*U", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"],
+ ["U*U*", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK"],
+ ["U*U*U", "$2a$05$XXXXXXXXXXXXXXXXXXXXXO", "$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a"],
+ ["0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789chars after 72 are ignored", "$2a$05$abcdefghijklmnopqrstuu", "$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui"],
+ ["\xa3", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e"],
+ ["\xff\xff\xa3", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e"],
+ ["\xff\xff\xa3", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e"],
+ ["\xff\xff\xa3", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.nqd1wy.pTMdcvrRWxyiGL2eMz.2a85."],
+ ["\xff\xff\xa3", "$2b$05$/OK.fbVrR/bpIqNJ5ianF.", "$2b$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e"],
+ ["\xa3", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"],
+ ["\xa3", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"],
+ ["\xa3", "$2b$05$/OK.fbVrR/bpIqNJ5ianF.", "$2b$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"],
+ ["1\xa3" "345", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi"],
+ ["\xff\xa3" "345", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi"],
+ ["\xff\xa3" "34" "\xff\xff\xff\xa3" "345", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi"],
+ ["\xff\xa3" "34" "\xff\xff\xff\xa3" "345", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi"],
+ ["\xff\xa3" "34" "\xff\xff\xff\xa3" "345", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.ZC1JEJ8Z4gPfpe1JOr/oyPXTWl9EFd."],
+ ["\xff\xa3" "345", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e"],
+ ["\xff\xa3" "345", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e"],
+ ["\xa3" "ab", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS"],
+ ["\xa3" "ab", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS"],
+ ["\xa3" "ab", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS"],
+ ["\xd1\x91", "$2x$05$6bNw2HLQYeqHYyBfLMsv/O", "$2x$05$6bNw2HLQYeqHYyBfLMsv/OiwqTymGIGzFsA4hOTWebfehXHNprcAS"],
+ ["\xd0\xc1\xd2\xcf\xcc\xd8", "$2x$05$6bNw2HLQYeqHYyBfLMsv/O", "$2x$05$6bNw2HLQYeqHYyBfLMsv/O9LIGgn8OMzuDoHfof8AQimSGfcSWxnS"],
+ ["\xaa"*72+"chars after 72 are ignored as usual", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6"],
+ ["\xaa\x55"*36, "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy"],
+ ["\x55\xaa\xff"*24, "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe"],
+ ["", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy"],
+
+ # test vectors from the Java implementation, found in https://github.com/spring-projects/spring-security/blob/master/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptTests.java
+ ["", "$2a$06$DCq7YPn5Rq63x1Lad4cll.", "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."],
+ ["", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye"],
+ ["", "$2a$10$k1wbIrmNyFAPwPVPSVa/ze", "$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW"],
+ ["", "$2a$12$k42ZFHFWqBp3vWli.nIn8u", "$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO"],
+ ["", "$2b$06$8eVN9RiU8Yki430X.wBvN.", "$2b$06$8eVN9RiU8Yki430X.wBvN.LWaqh2962emLVSVXVZIXJvDYLsV0oFu"],
+ ["", "$2b$06$NlgfNgpIc6GlHciCkMEW8u", "$2b$06$NlgfNgpIc6GlHciCkMEW8uKOBsyvAp7QwlHpysOlKdtyEw50WQua2"],
+ ["", "$2y$06$mFDtkz6UN7B3GZ2qi2hhaO", "$2y$06$mFDtkz6UN7B3GZ2qi2hhaO3OFWzNEdcY84ELw6iHCPruuQfSAXBLK"],
+ ["", "$2y$06$88kSqVttBx.e9iXTPCLa5u", "$2y$06$88kSqVttBx.e9iXTPCLa5uFPrVFjfLH4D.KcO6pBiAmvUkvdg0EYy"],
+ ["a", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe"],
+ ["a", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfe", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V."],
+ ["a", "$2a$10$k87L/MF28Q673VKh8/cPi.", "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u"],
+ ["a", "$2a$12$8NJH3LsPrANStV6XtBakCe", "$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS"],
+ ["a", "$2b$06$ehKGYiS4wt2HAr7KQXS5z.", "$2b$06$ehKGYiS4wt2HAr7KQXS5z.OaRjB4jHO7rBHJKlGXbqEH3QVJfO7iO"],
+ ["a", "$2b$06$PWxFFHA3HiCD46TNOZh30e", "$2b$06$PWxFFHA3HiCD46TNOZh30eNto1hg5uM9tHBlI4q/b03SW/gGKUYk6"],
+ ["a", "$2y$06$LUdD6/aD0e/UbnxVAVbvGu", "$2y$06$LUdD6/aD0e/UbnxVAVbvGuUmIoJ3l/OK94ThhadpMWwKC34LrGEey"],
+ ["a", "$2y$06$eqgY.T2yloESMZxgp76deO", "$2y$06$eqgY.T2yloESMZxgp76deOROa7nzXDxbO0k.PJvuClTa.Vu1AuemG"],
+ ["abc", "$2a$06$If6bvum7DFjUnE9p2uDeDu", "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i"],
+ ["abc", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7O", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm"],
+ ["abc", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi"],
+ ["abc", "$2a$12$EXRkfkdmXn2gzds2SSitu.", "$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q"],
+ ["abc", "$2b$06$5FyQoicpbox1xSHFfhhdXu", "$2b$06$5FyQoicpbox1xSHFfhhdXuR2oxLpO1rYsQh5RTkI/9.RIjtoF0/ta"],
+ ["abc", "$2b$06$1kJyuho8MCVP3HHsjnRMkO", "$2b$06$1kJyuho8MCVP3HHsjnRMkO1nvCOaKTqLnjG2TX1lyMFbXH/aOkgc."],
+ ["abc", "$2y$06$ACfku9dT6.H8VjdKb8nhlu", "$2y$06$ACfku9dT6.H8VjdKb8nhluaoBmhJyK7GfoNScEfOfrJffUxoUeCjK"],
+ ["abc", "$2y$06$9JujYcoWPmifvFA3RUP90e", "$2y$06$9JujYcoWPmifvFA3RUP90e5rSEHAb5Ye6iv3.G9ikiHNv5cxjNEse"],
+ ["abcdefghijklmnopqrstuvwxyz", "$2a$06$.rCVZVOThsIa97pEDOxvGu", "$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC"],
+ ["abcdefghijklmnopqrstuvwxyz", "$2a$08$aTsUwsyowQuzRrDqFflhge", "$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz."],
+ ["abcdefghijklmnopqrstuvwxyz", "$2a$10$fVH8e28OQRj9tqiDXs1e1u", "$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq"],
+ ["abcdefghijklmnopqrstuvwxyz", "$2a$12$D4G5f18o7aMMfwasBL7Gpu", "$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG"],
+ ["abcdefghijklmnopqrstuvwxyz", "$2b$06$O8E89AQPj1zJQA05YvIAU.", "$2b$06$O8E89AQPj1zJQA05YvIAU.hMpj25BXri1bupl/Q7CJMlpLwZDNBoO"],
+ ["abcdefghijklmnopqrstuvwxyz", "$2b$06$PDqIWr./o/P3EE/P.Q0A/u", "$2b$06$PDqIWr./o/P3EE/P.Q0A/uFg86WL/PXTbaW267TDALEwDylqk00Z."],
+ ["abcdefghijklmnopqrstuvwxyz", "$2y$06$34MG90ZLah8/ZNr3ltlHCu", "$2y$06$34MG90ZLah8/ZNr3ltlHCuz6bachF8/3S5jTuzF1h2qg2cUk11sFW"],
+ ["abcdefghijklmnopqrstuvwxyz", "$2y$06$AK.hSLfMyw706iEW24i68u", "$2y$06$AK.hSLfMyw706iEW24i68uKAc2yorPTrB0cimvjJHEBUrPkOq7VvG"],
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$06$fPIsBO8qRqkjj273rfaOI.", "$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO"],
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$08$Eq2r4G/76Wv39MzSX262hu", "$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW"],
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS"],
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$12$WApznUOJfkEGSmYRfnkrPO", "$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC"],
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu", "$2b$06$FGWA8OlY6RtQhXBXuCJ8WusVipRI15cWOgJK8MYpBHEkktMfbHRIG"],
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2b$06$G6aYU7UhUEUDJBdTgq3CRe", "$2b$06$G6aYU7UhUEUDJBdTgq3CRekiopCN4O4sNitFXrf5NUscsVZj3a2r6"],
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2y$06$sYDFHqOcXTjBgOsqC0WCKe", "$2y$06$sYDFHqOcXTjBgOsqC0WCKeMd3T1UhHuWQSxncLGtXDLMrcE6vFDti"],
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2y$06$6Xm0gCw4g7ZNDCEp4yTise", "$2y$06$6Xm0gCw4g7ZNDCEp4yTisez0kSdpXEl66MvdxGidnmChIe8dFmMnq"]
+ ]
+ for secret, salt, test_vector in test_vectors
+ expect(BCrypt::Engine.hash_secret(secret, salt)).to eql(test_vector)
+ end
+ end
+
+ specify "should truncate long 1-byte character secrets to 72 bytes" do
+ # 'b' as a base triggers the failure at 256 characters, but 'a' does not.
+ too_long_secret = 'b'*(BCrypt::Engine::MAX_SECRET_BYTESIZE + 1)
+ just_right_secret = 'b'*BCrypt::Engine::MAX_SECRET_BYTESIZE
+ expect(BCrypt::Engine.hash_secret(too_long_secret, @salt)).to eq(BCrypt::Engine.hash_secret(just_right_secret, @salt))
+ end
+
+ specify "should truncate long multi-byte character secrets to 72 bytes" do
+ # 256 times causes bcrypt to return nil for libxcrypt > 4.4.18-4.
+ too_long_secret = '𐐷'*256
+ # 𐐷 takes 4 bytes in UTF-8. 18 times is 72 bytes
+ just_right_secret = '𐐷'*18
+ expect(BCrypt::Engine.hash_secret(too_long_secret, @salt)).to eq(BCrypt::Engine.hash_secret(just_right_secret, @salt))
+ end
+end
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/bcrypt/error_spec.rb b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/bcrypt/error_spec.rb
new file mode 100644
index 00000000..6f90048e
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/bcrypt/error_spec.rb
@@ -0,0 +1,37 @@
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
+
+describe "Errors" do
+
+ shared_examples "descends from StandardError" do
+ it "can be rescued as a StandardError" do
+ expect(described_class).to be < StandardError
+ end
+ end
+
+ shared_examples "descends from BCrypt::Error" do
+ it "can be rescued as a BCrypt::Error" do
+ expect(described_class).to be < BCrypt::Error
+ end
+ end
+
+ describe BCrypt::Error do
+ include_examples "descends from StandardError"
+ end
+
+ describe BCrypt::Errors::InvalidCost do
+ include_examples "descends from BCrypt::Error"
+ end
+
+ describe BCrypt::Errors::InvalidHash do
+ include_examples "descends from BCrypt::Error"
+ end
+
+ describe BCrypt::Errors::InvalidSalt do
+ include_examples "descends from BCrypt::Error"
+ end
+
+ describe BCrypt::Errors::InvalidSecret do
+ include_examples "descends from BCrypt::Error"
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/bcrypt/password_spec.rb b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/bcrypt/password_spec.rb
new file mode 100644
index 00000000..1899221e
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/bcrypt/password_spec.rb
@@ -0,0 +1,131 @@
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
+require 'securerandom'
+
+describe "Creating a hashed password" do
+
+ before :each do
+ @secret = "wheedle"
+ @password = BCrypt::Password.create(@secret, :cost => 4)
+ end
+
+ specify "should return a BCrypt::Password" do
+ expect(@password).to be_an_instance_of(BCrypt::Password)
+ end
+
+ specify "should return a valid bcrypt password" do
+ expect { BCrypt::Password.new(@password) }.not_to raise_error
+ end
+
+ specify "should behave normally if the secret is not a string" do
+ expect { BCrypt::Password.create(nil) }.not_to raise_error
+ expect { BCrypt::Password.create({:woo => "yeah"}) }.not_to raise_error
+ expect { BCrypt::Password.create(false) }.not_to raise_error
+ end
+
+ specify "should tolerate empty string secrets" do
+ expect { BCrypt::Password.create( "\n".chop ) }.not_to raise_error
+ expect { BCrypt::Password.create( "" ) }.not_to raise_error
+ expect { BCrypt::Password.create( String.new ) }.not_to raise_error
+ end
+
+ specify "should tolerate very long string secrets" do
+ expect { BCrypt::Password.create("abcd"*1024) }.not_to raise_error
+ end
+end
+
+describe "Reading a hashed password" do
+ before :each do
+ @secret = "U*U"
+ @hash = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
+ end
+
+ specify "the cost is too damn high" do
+ expect {
+ BCrypt::Password.create("hello", :cost => 32)
+ }.to raise_error(ArgumentError)
+ end
+
+ specify "the cost should be set to the default if nil" do
+ expect(BCrypt::Password.create("hello", :cost => nil).cost).to equal(BCrypt::Engine::DEFAULT_COST)
+ end
+
+ specify "the cost should be set to the default if empty hash" do
+ expect(BCrypt::Password.create("hello", {}).cost).to equal(BCrypt::Engine::DEFAULT_COST)
+ end
+
+ specify "the cost should be set to the passed value if provided" do
+ expect(BCrypt::Password.create("hello", :cost => 5).cost).to equal(5)
+ end
+
+ specify "the cost should be set to the global value if set" do
+ BCrypt::Engine.cost = 5
+ expect(BCrypt::Password.create("hello").cost).to equal(5)
+ # unset the global value to not affect other tests
+ BCrypt::Engine.cost = nil
+ end
+
+ specify "the cost should be set to an overridden constant for backwards compatibility" do
+ # suppress "already initialized constant" warning
+ old_verbose, $VERBOSE = $VERBOSE, nil
+ old_default_cost = BCrypt::Engine::DEFAULT_COST
+
+ BCrypt::Engine::DEFAULT_COST = 5
+ expect(BCrypt::Password.create("hello").cost).to equal(5)
+
+ # reset default to not affect other tests
+ BCrypt::Engine::DEFAULT_COST = old_default_cost
+ $VERBOSE = old_verbose
+ end
+
+ specify "should read the version, cost, salt, and hash" do
+ password = BCrypt::Password.new(@hash)
+ expect(password.version).to eql("2a")
+ expect(password.version.class).to eq String
+ expect(password.cost).to equal(5)
+ expect(password.salt).to eql("$2a$05$CCCCCCCCCCCCCCCCCCCCC.")
+ expect(password.salt.class).to eq String
+ expect(password.checksum).to eq("E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW")
+ expect(password.checksum.class).to eq String
+ expect(password.to_s).to eql(@hash)
+ end
+
+ specify "should raise an InvalidHashError when given an invalid hash" do
+ expect { BCrypt::Password.new('weedle') }.to raise_error(BCrypt::Errors::InvalidHash)
+ end
+end
+
+describe "Comparing a hashed password with a secret" do
+ before :each do
+ @secret = "U*U"
+ @hash = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
+ @password = BCrypt::Password.create(@secret)
+ end
+
+ specify "should compare successfully to the original secret" do
+ expect((@password == @secret)).to be(true)
+ end
+
+ specify "should compare unsuccessfully to anything besides original secret" do
+ expect((@password == "@secret")).to be(false)
+ end
+end
+
+describe "Validating a generated salt" do
+ specify "should not accept an invalid salt" do
+ expect(BCrypt::Engine.valid_salt?("invalid")).to eq(false)
+ expect(BCrypt::Engine.valid_salt?("invalid\n#{BCrypt::Engine.generate_salt}\ninvalid")).to eq(false)
+ end
+ specify "should accept a valid salt" do
+ expect(BCrypt::Engine.valid_salt?(BCrypt::Engine.generate_salt)).to eq(true)
+ end
+end
+
+describe "Validating a password hash" do
+ specify "should not accept an invalid password" do
+ expect(BCrypt::Password.valid_hash?("i_am_so_not_valid")).to be(false)
+ expect(BCrypt::Password.valid_hash?("invalid\n#{BCrypt::Password.create "i_am_so_valid"}\ninvalid")).to be(false)
+ end
+ specify "should accept a valid password" do
+ expect(BCrypt::Password.valid_hash?(BCrypt::Password.create "i_am_so_valid")).to be(true)
+ end
+end
diff --git a/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/spec_helper.rb b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/spec_helper.rb
new file mode 100644
index 00000000..54882506
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/gems/bcrypt-3.1.18/spec/spec_helper.rb
@@ -0,0 +1,2 @@
+$:.unshift File.expand_path('../../lib', __FILE__)
+require 'bcrypt'
diff --git a/vendor/bundle/ruby/3.2.0/specifications/bcrypt-3.1.18.gemspec b/vendor/bundle/ruby/3.2.0/specifications/bcrypt-3.1.18.gemspec
new file mode 100644
index 00000000..d686dcbf
--- /dev/null
+++ b/vendor/bundle/ruby/3.2.0/specifications/bcrypt-3.1.18.gemspec
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-
+# stub: bcrypt 3.1.18 ruby lib
+# stub: ext/mri/extconf.rb
+
+Gem::Specification.new do |s|
+ s.name = "bcrypt".freeze
+ s.version = "3.1.18"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
+ s.require_paths = ["lib".freeze]
+ s.authors = ["Coda Hale".freeze]
+ s.date = "2022-05-16"
+ s.description = " bcrypt() is a sophisticated and secure hash algorithm designed by The OpenBSD project\n for hashing passwords. The bcrypt Ruby gem provides a simple wrapper for safely handling\n passwords.\n".freeze
+ s.email = "coda.hale@gmail.com".freeze
+ s.extensions = ["ext/mri/extconf.rb".freeze]
+ s.extra_rdoc_files = ["README.md".freeze, "COPYING".freeze, "CHANGELOG".freeze, "lib/bcrypt/password.rb".freeze, "lib/bcrypt/engine.rb".freeze, "lib/bcrypt/error.rb".freeze, "lib/bcrypt.rb".freeze]
+ s.files = ["CHANGELOG".freeze, "COPYING".freeze, "README.md".freeze, "ext/mri/extconf.rb".freeze, "lib/bcrypt.rb".freeze, "lib/bcrypt/engine.rb".freeze, "lib/bcrypt/error.rb".freeze, "lib/bcrypt/password.rb".freeze]
+ s.homepage = "https://github.com/bcrypt-ruby/bcrypt-ruby".freeze
+ s.licenses = ["MIT".freeze]
+ s.rdoc_options = ["--title".freeze, "bcrypt-ruby".freeze, "--line-numbers".freeze, "--inline-source".freeze, "--main".freeze, "README.md".freeze]
+ s.rubygems_version = "3.4.10".freeze
+ s.summary = "OpenBSD's bcrypt() password hashing algorithm.".freeze
+
+ s.installed_by_version = "3.4.10" if s.respond_to? :installed_by_version
+
+ s.specification_version = 4
+
+ s.add_development_dependency(%q.freeze, ["~> 1.2.0"])
+ s.add_development_dependency(%q.freeze, [">= 3"])
+end
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 00000000..eab26aca
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,13 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@popperjs/core@^2.11.8":
+ version "2.11.8"
+ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
+ integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
+
+bootstrap@^5.3.3:
+ version "5.3.3"
+ resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.3.tgz#de35e1a765c897ac940021900fcbb831602bac38"
+ integrity sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==