lookout-bcrypt 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,347 @@
1
+ // Copyright (c) 2006 Damien Miller <djm@mindrot.org>
2
+ //
3
+ // Permission to use, copy, modify, and distribute this software for any
4
+ // purpose with or without fee is hereby granted, provided that the above
5
+ // copyright notice and this permission notice appear in all copies.
6
+ //
7
+ // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ import junit.framework.TestCase;
16
+
17
+ /**
18
+ * JUnit unit tests for BCrypt routines
19
+ * @author Damien Miller
20
+ * @version 0.3
21
+ */
22
+ public class TestBCrypt extends TestCase {
23
+ // length of salt prefix
24
+ private static final int BCRYPT_SALT_PREFIX_LENGTH = 7 + 22 + 1;
25
+
26
+ String test_vectors[][] = {
27
+ { "",
28
+ "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s." },
29
+ { "",
30
+ "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye" },
31
+ { "",
32
+ "$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW" },
33
+ { "",
34
+ "$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO" },
35
+ { "a",
36
+ "$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe" },
37
+ { "a",
38
+ "$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V." },
39
+ { "a",
40
+ "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u" },
41
+ { "a",
42
+ "$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS" },
43
+ { "abc",
44
+ "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i" },
45
+ { "abc",
46
+ "$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm" },
47
+ { "abc",
48
+ "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi" },
49
+ { "abc",
50
+ "$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q" },
51
+ { "abcdefghijklmnopqrstuvwxyz",
52
+ "$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC" },
53
+ { "abcdefghijklmnopqrstuvwxyz",
54
+ "$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz." },
55
+ { "abcdefghijklmnopqrstuvwxyz",
56
+ "$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq" },
57
+ { "abcdefghijklmnopqrstuvwxyz",
58
+ "$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG" },
59
+ { "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
60
+ "$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO" },
61
+ { "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
62
+ "$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW" },
63
+ { "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
64
+ "$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS" },
65
+ { "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
66
+ "$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC" },
67
+ { "", // Note that this must be 4 or more from the end to pass testCheckpw_failure
68
+ "$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy" },
69
+ { "U*U",
70
+ "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW" },
71
+ { "U*U*",
72
+ "$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK" },
73
+ { "U*U*U",
74
+ "$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a" },
75
+ { "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
76
+ "$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui" },
77
+ };
78
+
79
+ byte[][] binary_test_vectors =
80
+ {
81
+ // ""
82
+ {
83
+ (byte) 0
84
+ },
85
+
86
+ // "U*U"
87
+ {
88
+ (byte) 'U', (byte) '*', (byte) 'U', (byte) 0
89
+ },
90
+
91
+ // "U*U*"
92
+ {
93
+ (byte) 'U', (byte) '*', (byte) 'U', (byte) '*', (byte) 0
94
+ },
95
+
96
+ // "U*U*U"
97
+ {
98
+ (byte) 'U', (byte) '*', (byte) 'U', (byte) '*', (byte) 'U', (byte) 0
99
+ },
100
+
101
+ // [0xaa] * 72
102
+ { (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
103
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
104
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
105
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
106
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
107
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
108
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
109
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
110
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
111
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
112
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
113
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
114
+ (byte) 0
115
+ },
116
+
117
+ // [0xaa] * 72 + "chars after 72 are ignored as usual"
118
+ { (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
119
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
120
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
121
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
122
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
123
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
124
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
125
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
126
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
127
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
128
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
129
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
130
+ (byte) 'c', (byte) 'h', (byte) 'a', (byte) 'r', (byte) 's', (byte) ' ',
131
+ (byte) 'a', (byte) 'f', (byte) 't', (byte) 'e', (byte) 'r', (byte) ' ',
132
+ (byte) '7', (byte) '2', (byte) ' ', (byte) 'a', (byte) 'r', (byte) 'e',
133
+ (byte) 'i', (byte) 'g', (byte) 'n', (byte) 'o', (byte) 'r', (byte) 'e',
134
+ (byte) 'd', (byte) ' ', (byte) 'a', (byte) 's', (byte) ' ', (byte) 'u',
135
+ (byte) 's', (byte) 'u', (byte) 'a', (byte) 'l', (byte) 0
136
+ },
137
+
138
+ // [0xaa, 0x55] * 36
139
+ { (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
140
+ (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
141
+ (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
142
+ (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
143
+ (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
144
+ (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
145
+ (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
146
+ (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
147
+ (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
148
+ (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
149
+ (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
150
+ (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xaa, (byte) 0x55,
151
+ (byte) 0
152
+ },
153
+
154
+ // [0x55, 0xaa, 0xff] * 24
155
+ { (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
156
+ (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
157
+ (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
158
+ (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
159
+ (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
160
+ (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
161
+ (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
162
+ (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
163
+ (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
164
+ (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
165
+ (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
166
+ (byte) 0x55, (byte) 0xaa, (byte) 0xff, (byte) 0x55, (byte) 0xaa, (byte) 0xff,
167
+ (byte) 0
168
+ },
169
+ };
170
+
171
+ String[][] binary_test_match_vectors =
172
+ {
173
+ { // ""
174
+ "$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy" },
175
+ { // "U*U"
176
+ "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW" },
177
+ { // "U*U*"
178
+ "$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK" },
179
+ { // "U*U*U"
180
+ "$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a" },
181
+ { // [0xaa] * 72
182
+ "$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6" },
183
+ { // [0xaa] * 72 + "chars after 72 are ignored as usual"
184
+ "$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6" },
185
+ { // [0xaa, 0x55] * 36
186
+ "$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy" },
187
+ { // [0x55, 0xaa, 0xff] * 24
188
+ "$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe" },
189
+ };
190
+
191
+
192
+ /**
193
+ * Entry point for unit tests
194
+ * @param args unused
195
+ */
196
+ public static void main(String[] args) {
197
+ junit.textui.TestRunner.run(TestBCrypt.class);
198
+ }
199
+
200
+ /**
201
+ * Test method for 'BCrypt.hashpw(String, String)'
202
+ */
203
+ public void testHashpw() {
204
+ System.out.print("BCrypt.hashpw(): ");
205
+ for (int i = 0; i < test_vectors.length; i++) {
206
+ String plain = test_vectors[i][0];
207
+ String salt = test_vectors[i][1].substring(0, BCRYPT_SALT_PREFIX_LENGTH);
208
+ String expected = test_vectors[i][1];
209
+ String hashed = BCrypt.hashpw(plain, salt);
210
+ assertEquals(hashed, expected);
211
+ System.out.print(".");
212
+ }
213
+ System.out.println("");
214
+ }
215
+
216
+ /**
217
+ * Test method for 'BCrypt.hashpw(byte[], String)'
218
+ */
219
+ public void testHashpwBinary() {
220
+ System.out.print("BCrypt.hashpw(byte[]): ");
221
+ for (int i = 0; i < binary_test_vectors.length; i++) {
222
+ byte[] plain = binary_test_vectors[i];
223
+ String salt = binary_test_match_vectors[i][0].substring(0, BCRYPT_SALT_PREFIX_LENGTH);
224
+ String expected = binary_test_match_vectors[i][0];
225
+ String hashed = BCrypt.hashpw(plain, salt);
226
+ assertEquals(hashed, expected);
227
+ System.out.print(".");
228
+ }
229
+ System.out.println("");
230
+ }
231
+
232
+ /**
233
+ * Test method for 'BCrypt.gensalt(int)'
234
+ */
235
+ public void testGensaltInt() {
236
+ System.out.print("BCrypt.gensalt(log_rounds):");
237
+ for (int i = 4; i <= 12; i++) {
238
+ System.out.print(" " + Integer.toString(i) + ":");
239
+ for (int j = 0; j < test_vectors.length; j += 4) {
240
+ String plain = test_vectors[j][0];
241
+ String salt = BCrypt.gensalt(i);
242
+ String hashed1 = BCrypt.hashpw(plain, salt);
243
+ String hashed2 = BCrypt.hashpw(plain, hashed1);
244
+ assertEquals(hashed1, hashed2);
245
+ System.out.print(".");
246
+ }
247
+ }
248
+ System.out.println("");
249
+ }
250
+
251
+ /**
252
+ * Test method for 'BCrypt.gensalt()'
253
+ */
254
+ public void testGensalt() {
255
+ System.out.print("BCrypt.gensalt(): ");
256
+ for (int i = 0; i < test_vectors.length; i += 4) {
257
+ String plain = test_vectors[i][0];
258
+ String salt = BCrypt.gensalt();
259
+ String hashed1 = BCrypt.hashpw(plain, salt);
260
+ String hashed2 = BCrypt.hashpw(plain, hashed1);
261
+ assertEquals(hashed1, hashed2);
262
+ System.out.print(".");
263
+ }
264
+ System.out.println("");
265
+ }
266
+
267
+ /**
268
+ * Test method for 'BCrypt.checkpw(String, String)'
269
+ * expecting success
270
+ */
271
+ public void testCheckpw_success() {
272
+ System.out.print("BCrypt.checkpw w/ good passwords: ");
273
+ for (int i = 0; i < test_vectors.length; i++) {
274
+ String plain = test_vectors[i][0];
275
+ String expected = test_vectors[i][1];
276
+ assertTrue(BCrypt.checkpw(plain, expected));
277
+ System.out.print(".");
278
+ }
279
+ System.out.println("");
280
+ }
281
+
282
+ /**
283
+ * Test method for 'BCrypt.checkpw(byte[], String)'
284
+ * expecting success
285
+ */
286
+ public void testCheckpwBinary_success() {
287
+ System.out.print("BCrypt.checkpw(byte[]) w/ good passwords: ");
288
+ for (int i = 0; i < binary_test_vectors.length; i++) {
289
+ byte[] plain = binary_test_vectors[i];
290
+ String expected = binary_test_match_vectors[i][0];
291
+ assertTrue(BCrypt.checkpw(plain, expected));
292
+ System.out.print(".");
293
+ }
294
+ System.out.println("");
295
+ }
296
+
297
+ /**
298
+ * Test method for 'BCrypt.checkpw(String, String)'
299
+ * expecting failure
300
+ */
301
+ public void testCheckpw_failure() {
302
+ System.out.print("BCrypt.checkpw w/ bad passwords: ");
303
+ for (int i = 0; i < test_vectors.length; i++) {
304
+ int broken_index = (i + 4) % test_vectors.length;
305
+ String plain = test_vectors[i][0];
306
+ String expected = test_vectors[broken_index][1];
307
+ assertFalse(BCrypt.checkpw(plain, expected));
308
+ System.out.print(".");
309
+ }
310
+ System.out.println("");
311
+ }
312
+
313
+ /**
314
+ * Test method for 'BCrypt.checkpw(byte[], String)'
315
+ * expecting failure
316
+ */
317
+ public void testCheckpwBinary_failure() {
318
+ System.out.print("BCrypt.checkpw(byte[]) w/ bad passwords: ");
319
+ for (int i = 0; i < binary_test_vectors.length; i++) {
320
+ int broken_index = (i + 4) % binary_test_vectors.length;
321
+ byte[] plain = binary_test_vectors[i];
322
+ String expected = binary_test_match_vectors[broken_index][0];
323
+ assertFalse(BCrypt.checkpw(plain, expected));
324
+ System.out.print(".");
325
+ }
326
+ System.out.println("");
327
+ }
328
+
329
+ /**
330
+ * Test for correct hashing of non-US-ASCII passwords
331
+ */
332
+ public void testInternationalChars() {
333
+ System.out.print("BCrypt.hashpw w/ international chars: ");
334
+ String pw1 = "ππππππππ";
335
+ String pw2 = "????????";
336
+
337
+ String h1 = BCrypt.hashpw(pw1, BCrypt.gensalt());
338
+ assertFalse(BCrypt.checkpw(pw2, h1));
339
+ System.out.print(".");
340
+
341
+ String h2 = BCrypt.hashpw(pw2, BCrypt.gensalt());
342
+ assertFalse(BCrypt.checkpw(pw1, h2));
343
+ System.out.print(".");
344
+ System.out.println("");
345
+ }
346
+
347
+ }
@@ -0,0 +1,108 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+
3
+ describe "The BCrypt engine" do
4
+ specify "should calculate the optimal cost factor to fit in a specific time" do
5
+ first = BCrypt::Engine.calibrate(100)
6
+ second = BCrypt::Engine.calibrate(400)
7
+ expect(second).to be > first
8
+ end
9
+ end
10
+
11
+ describe "Generating BCrypt salts" do
12
+
13
+ specify "should produce strings" do
14
+ expect(BCrypt::Engine.generate_salt).to be_an_instance_of(String)
15
+ end
16
+
17
+ specify "should produce random data" do
18
+ expect(BCrypt::Engine.generate_salt).to_not equal(BCrypt::Engine.generate_salt)
19
+ end
20
+
21
+ specify "should raise a InvalidCostError if the cost parameter isn't numeric" do
22
+ expect { BCrypt::Engine.generate_salt('woo') }.to raise_error(BCrypt::Errors::InvalidCost)
23
+ end
24
+
25
+ specify "should raise a InvalidCostError if the cost parameter isn't greater than 0" do
26
+ expect { BCrypt::Engine.generate_salt(-1) }.to raise_error(BCrypt::Errors::InvalidCost)
27
+ end
28
+ end
29
+
30
+ describe "Autodetecting of salt cost" do
31
+
32
+ specify "should work" do
33
+ expect(BCrypt::Engine.autodetect_cost("$2a$08$hRx2IVeHNsTSYYtUWn61Ou")).to eq 8
34
+ expect(BCrypt::Engine.autodetect_cost("$2a$05$XKd1bMnLgUnc87qvbAaCUu")).to eq 5
35
+ expect(BCrypt::Engine.autodetect_cost("$2a$13$Lni.CZ6z5A7344POTFBBV.")).to eq 13
36
+ end
37
+
38
+ end
39
+
40
+ describe "Generating BCrypt hashes" do
41
+
42
+ class MyInvalidSecret
43
+ undef to_s
44
+ end
45
+
46
+ before :each do
47
+ @salt = BCrypt::Engine.generate_salt(4)
48
+ @password = "woo"
49
+ end
50
+
51
+ specify "should produce a string" do
52
+ expect(BCrypt::Engine.hash_secret(@password, @salt)).to be_an_instance_of(String)
53
+ end
54
+
55
+ specify "should raise an InvalidSalt error if the salt is invalid" do
56
+ expect { BCrypt::Engine.hash_secret(@password, 'nino') }.to raise_error(BCrypt::Errors::InvalidSalt)
57
+ end
58
+
59
+ specify "should raise an InvalidSecret error if the secret is invalid" do
60
+ expect { BCrypt::Engine.hash_secret(MyInvalidSecret.new, @salt) }.to raise_error(BCrypt::Errors::InvalidSecret)
61
+ expect { BCrypt::Engine.hash_secret(nil, @salt) }.not_to raise_error
62
+ expect { BCrypt::Engine.hash_secret(false, @salt) }.not_to raise_error
63
+ end
64
+
65
+ specify "should call #to_s on the secret and use the return value as the actual secret data" do
66
+ expect(BCrypt::Engine.hash_secret(false, @salt)).to eq BCrypt::Engine.hash_secret("false", @salt)
67
+ end
68
+
69
+ describe "should be interoperable with other implementations" do
70
+ BCRYPT_SALT_PREFIX_LENGTH = 7 + 22 + 1
71
+
72
+ # test vectors from the OpenWall implementation <http://www.openwall.com/crypt/>
73
+ test_vectors = [
74
+ ["$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW",
75
+ "U*U"],
76
+ ["$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK",
77
+ "U*U*"],
78
+ ["$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a",
79
+ "U*U*U"],
80
+ ["$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui",
81
+ "0123456789abcdefghijklmnopqrstuvwxyz" +
82
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +
83
+ "chars after 72 are ignored"],
84
+ ["$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
85
+ "\xa3"],
86
+ ["$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
87
+ "\xff\xa3" "345"],
88
+ ["$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
89
+ "\xa3" "ab"],
90
+ ["$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6",
91
+ "\xaa"*72],
92
+ ["$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6",
93
+ "\xaa"*72 + "chars after 72 are ignored as usual"],
94
+ ["$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy",
95
+ "\xaa\x55"*36],
96
+ ["$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe",
97
+ "\x55\xaa\xff"*24],
98
+ ["$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy",
99
+ ""],
100
+ ]
101
+ test_vectors.each_with_index do |(test_vector, secret), i|
102
+ specify("with Secret ##{i}") do
103
+ salt = test_vector[0, BCRYPT_SALT_PREFIX_LENGTH - 1] # extract salt from test vector
104
+ expect(BCrypt::Engine.hash_secret(secret, salt)).to eql(test_vector)
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,37 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+
3
+ describe "Errors" do
4
+
5
+ shared_examples "descends from StandardError" do
6
+ it "can be rescued as a StandardError" do
7
+ expect(described_class).to be < StandardError
8
+ end
9
+ end
10
+
11
+ shared_examples "descends from BCrypt::Error" do
12
+ it "can be rescued as a BCrypt::Error" do
13
+ expect(described_class).to be < BCrypt::Error
14
+ end
15
+ end
16
+
17
+ describe BCrypt::Error do
18
+ include_examples "descends from StandardError"
19
+ end
20
+
21
+ describe BCrypt::Errors::InvalidCost do
22
+ include_examples "descends from BCrypt::Error"
23
+ end
24
+
25
+ describe BCrypt::Errors::InvalidHash do
26
+ include_examples "descends from BCrypt::Error"
27
+ end
28
+
29
+ describe BCrypt::Errors::InvalidSalt do
30
+ include_examples "descends from BCrypt::Error"
31
+ end
32
+
33
+ describe BCrypt::Errors::InvalidSecret do
34
+ include_examples "descends from BCrypt::Error"
35
+ end
36
+
37
+ end
@@ -0,0 +1,123 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+
3
+ describe "Creating a hashed password" do
4
+
5
+ before :each do
6
+ @secret = "wheedle"
7
+ @password = BCrypt::Password.create(@secret, :cost => 4)
8
+ end
9
+
10
+ specify "should return a BCrypt::Password" do
11
+ expect(@password).to be_an_instance_of(BCrypt::Password)
12
+ end
13
+
14
+ specify "should return a valid bcrypt password" do
15
+ expect { BCrypt::Password.new(@password) }.not_to raise_error
16
+ end
17
+
18
+ specify "should behave normally if the secret is not a string" do
19
+ expect { BCrypt::Password.create(nil) }.not_to raise_error
20
+ expect { BCrypt::Password.create({:woo => "yeah"}) }.not_to raise_error
21
+ expect { BCrypt::Password.create(false) }.not_to raise_error
22
+ end
23
+
24
+ specify "should tolerate empty string secrets" do
25
+ expect { BCrypt::Password.create( "\n".chop ) }.not_to raise_error
26
+ expect { BCrypt::Password.create( "" ) }.not_to raise_error
27
+ expect { BCrypt::Password.create( String.new ) }.not_to raise_error
28
+ end
29
+ end
30
+
31
+ describe "Reading a hashed password" do
32
+ before :each do
33
+ @secret = "U*U"
34
+ @hash = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
35
+ end
36
+
37
+ specify "the cost is too damn high" do
38
+ expect {
39
+ BCrypt::Password.create("hello", :cost => 32)
40
+ }.to raise_error(ArgumentError)
41
+ end
42
+
43
+ specify "the cost should be set to the default if nil" do
44
+ expect(BCrypt::Password.create("hello", :cost => nil).cost).to equal(BCrypt::Engine::DEFAULT_COST)
45
+ end
46
+
47
+ specify "the cost should be set to the default if empty hash" do
48
+ expect(BCrypt::Password.create("hello", {}).cost).to equal(BCrypt::Engine::DEFAULT_COST)
49
+ end
50
+
51
+ specify "the cost should be set to the passed value if provided" do
52
+ expect(BCrypt::Password.create("hello", :cost => 5).cost).to equal(5)
53
+ end
54
+
55
+ specify "the cost should be set to the global value if set" do
56
+ BCrypt::Engine.cost = 5
57
+ expect(BCrypt::Password.create("hello").cost).to equal(5)
58
+ # unset the global value to not affect other tests
59
+ BCrypt::Engine.cost = nil
60
+ end
61
+
62
+ specify "the cost should be set to an overridden constant for backwards compatibility" do
63
+ # suppress "already initialized constant" warning
64
+ old_verbose, $VERBOSE = $VERBOSE, nil
65
+ old_default_cost = BCrypt::Engine::DEFAULT_COST
66
+
67
+ BCrypt::Engine::DEFAULT_COST = 5
68
+ expect(BCrypt::Password.create("hello").cost).to equal(5)
69
+
70
+ # reset default to not affect other tests
71
+ BCrypt::Engine::DEFAULT_COST = old_default_cost
72
+ $VERBOSE = old_verbose
73
+ end
74
+
75
+ specify "should read the version, cost, salt, and hash" do
76
+ password = BCrypt::Password.new(@hash)
77
+ expect(password.version).to eql("2a")
78
+ expect(password.cost).to equal(5)
79
+ expect(password.salt).to eql("$2a$05$CCCCCCCCCCCCCCCCCCCCC.")
80
+ expect(password.salt.class).to eq String
81
+ expect(password.checksum).to eq("E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW")
82
+ expect(password.checksum.class).to eq String
83
+ expect(password.to_s).to eql(@hash)
84
+ end
85
+
86
+ specify "should raise an InvalidHashError when given an invalid hash" do
87
+ expect { BCrypt::Password.new('weedle') }.to raise_error(BCrypt::Errors::InvalidHash)
88
+ end
89
+ end
90
+
91
+ describe "Comparing a hashed password with a secret" do
92
+ before :each do
93
+ @secret = "U*U"
94
+ @hash = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
95
+ @password = BCrypt::Password.create(@secret)
96
+ end
97
+
98
+ specify "should compare successfully to the original secret" do
99
+ expect((@password == @secret)).to be(true)
100
+ end
101
+
102
+ specify "should compare unsuccessfully to anything besides original secret" do
103
+ expect((@password == "@secret")).to be(false)
104
+ end
105
+ end
106
+
107
+ describe "Validating a generated salt" do
108
+ specify "should not accept an invalid salt" do
109
+ expect(BCrypt::Engine.valid_salt?("invalid")).to eq(false)
110
+ end
111
+ specify "should accept a valid salt" do
112
+ expect(BCrypt::Engine.valid_salt?(BCrypt::Engine.generate_salt)).to eq(true)
113
+ end
114
+ end
115
+
116
+ describe "Validating a password hash" do
117
+ specify "should not accept an invalid password" do
118
+ expect(BCrypt::Password.valid_hash?("i_am_so_not_valid")).to be_falsey
119
+ end
120
+ specify "should accept a valid password" do
121
+ expect(BCrypt::Password.valid_hash?(BCrypt::Password.create "i_am_so_valid")).to be_truthy
122
+ end
123
+ end
@@ -0,0 +1,2 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'bcrypt'