lookout-bcrypt 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +3 -0
- data/.travis.yml +14 -0
- data/CHANGELOG +81 -0
- data/COPYING +28 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +40 -0
- data/README.md +203 -0
- data/Rakefile +93 -0
- data/bcrypt.gemspec +29 -0
- data/ext/jruby/bcrypt_jruby/BCrypt.java +782 -0
- data/ext/mri/bcrypt_ext.c +64 -0
- data/ext/mri/crypt.c +57 -0
- data/ext/mri/crypt.h +13 -0
- data/ext/mri/crypt_blowfish.c +786 -0
- data/ext/mri/crypt_gensalt.c +111 -0
- data/ext/mri/extconf.rb +16 -0
- data/ext/mri/ow-crypt.h +35 -0
- data/ext/mri/wrapper.c +262 -0
- data/lib/bcrypt/engine.rb +123 -0
- data/lib/bcrypt/error.rb +22 -0
- data/lib/bcrypt/password.rb +87 -0
- data/lib/bcrypt.rb +21 -0
- data/spec/TestBCrypt.java +347 -0
- data/spec/bcrypt/engine_spec.rb +108 -0
- data/spec/bcrypt/error_spec.rb +37 -0
- data/spec/bcrypt/password_spec.rb +123 -0
- data/spec/spec_helper.rb +2 -0
- metadata +131 -0
@@ -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
|
data/spec/spec_helper.rb
ADDED