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,111 @@
1
+ /*
2
+ * Written by Solar Designer and placed in the public domain.
3
+ * See crypt_blowfish.c for more information.
4
+ *
5
+ * This file contains salt generation functions for the traditional and
6
+ * other common crypt(3) algorithms, except for bcrypt which is defined
7
+ * entirely in crypt_blowfish.c.
8
+ */
9
+
10
+ #include <string.h>
11
+
12
+ #include <errno.h>
13
+ #ifndef __set_errno
14
+ #define __set_errno(val) errno = (val)
15
+ #endif
16
+
17
+ #undef __CONST
18
+ #ifdef __GNUC__
19
+ #define __CONST __const
20
+ #else
21
+ #define __CONST
22
+ #endif
23
+
24
+ unsigned char _crypt_itoa64[64 + 1] =
25
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
26
+
27
+ char *_crypt_gensalt_traditional_rn(unsigned long count,
28
+ __CONST char *input, int size, char *output, int output_size)
29
+ {
30
+ if (size < 2 || output_size < 2 + 1 || (count && count != 25)) {
31
+ if (output_size > 0) output[0] = '\0';
32
+ __set_errno((output_size < 2 + 1) ? ERANGE : EINVAL);
33
+ return NULL;
34
+ }
35
+
36
+ output[0] = _crypt_itoa64[(unsigned int)input[0] & 0x3f];
37
+ output[1] = _crypt_itoa64[(unsigned int)input[1] & 0x3f];
38
+ output[2] = '\0';
39
+
40
+ return output;
41
+ }
42
+
43
+ char *_crypt_gensalt_extended_rn(unsigned long count,
44
+ __CONST char *input, int size, char *output, int output_size)
45
+ {
46
+ unsigned long value;
47
+
48
+ /* Even iteration counts make it easier to detect weak DES keys from a look
49
+ * at the hash, so they should be avoided */
50
+ if (size < 3 || output_size < 1 + 4 + 4 + 1 ||
51
+ (count && (count > 0xffffff || !(count & 1)))) {
52
+ if (output_size > 0) output[0] = '\0';
53
+ __set_errno((output_size < 1 + 4 + 4 + 1) ? ERANGE : EINVAL);
54
+ return NULL;
55
+ }
56
+
57
+ if (!count) count = 725;
58
+
59
+ output[0] = '_';
60
+ output[1] = _crypt_itoa64[count & 0x3f];
61
+ output[2] = _crypt_itoa64[(count >> 6) & 0x3f];
62
+ output[3] = _crypt_itoa64[(count >> 12) & 0x3f];
63
+ output[4] = _crypt_itoa64[(count >> 18) & 0x3f];
64
+ value = (unsigned long)(unsigned char)input[0] |
65
+ ((unsigned long)(unsigned char)input[1] << 8) |
66
+ ((unsigned long)(unsigned char)input[2] << 16);
67
+ output[5] = _crypt_itoa64[value & 0x3f];
68
+ output[6] = _crypt_itoa64[(value >> 6) & 0x3f];
69
+ output[7] = _crypt_itoa64[(value >> 12) & 0x3f];
70
+ output[8] = _crypt_itoa64[(value >> 18) & 0x3f];
71
+ output[9] = '\0';
72
+
73
+ return output;
74
+ }
75
+
76
+ char *_crypt_gensalt_md5_rn(unsigned long count,
77
+ __CONST char *input, int size, char *output, int output_size)
78
+ {
79
+ unsigned long value;
80
+
81
+ if (size < 3 || output_size < 3 + 4 + 1 || (count && count != 1000)) {
82
+ if (output_size > 0) output[0] = '\0';
83
+ __set_errno((output_size < 3 + 4 + 1) ? ERANGE : EINVAL);
84
+ return NULL;
85
+ }
86
+
87
+ output[0] = '$';
88
+ output[1] = '1';
89
+ output[2] = '$';
90
+ value = (unsigned long)(unsigned char)input[0] |
91
+ ((unsigned long)(unsigned char)input[1] << 8) |
92
+ ((unsigned long)(unsigned char)input[2] << 16);
93
+ output[3] = _crypt_itoa64[value & 0x3f];
94
+ output[4] = _crypt_itoa64[(value >> 6) & 0x3f];
95
+ output[5] = _crypt_itoa64[(value >> 12) & 0x3f];
96
+ output[6] = _crypt_itoa64[(value >> 18) & 0x3f];
97
+ output[7] = '\0';
98
+
99
+ if (size >= 6 && output_size >= 3 + 4 + 4 + 1) {
100
+ value = (unsigned long)(unsigned char)input[3] |
101
+ ((unsigned long)(unsigned char)input[4] << 8) |
102
+ ((unsigned long)(unsigned char)input[5] << 16);
103
+ output[7] = _crypt_itoa64[value & 0x3f];
104
+ output[8] = _crypt_itoa64[(value >> 6) & 0x3f];
105
+ output[9] = _crypt_itoa64[(value >> 12) & 0x3f];
106
+ output[10] = _crypt_itoa64[(value >> 18) & 0x3f];
107
+ output[11] = '\0';
108
+ }
109
+
110
+ return output;
111
+ }
@@ -0,0 +1,16 @@
1
+ if RUBY_PLATFORM == "java"
2
+ # Don't do anything when run in JRuby; this allows gem installation to pass.
3
+ # We need to write a dummy Makefile so that RubyGems doesn't think compilation
4
+ # failed.
5
+ File.open('Makefile', 'w') do |f|
6
+ f.puts "all:"
7
+ f.puts "\t@true"
8
+ f.puts "install:"
9
+ f.puts "\t@true"
10
+ end
11
+ exit 0
12
+ else
13
+ require "mkmf"
14
+ dir_config("bcrypt_ext")
15
+ create_makefile("bcrypt_ext")
16
+ end
@@ -0,0 +1,35 @@
1
+ /*
2
+ * Written by Solar Designer and placed in the public domain.
3
+ * See crypt_blowfish.c for more information.
4
+ */
5
+
6
+ #ifndef _OW_CRYPT_H
7
+ #define _OW_CRYPT_H
8
+
9
+ #undef __CONST
10
+ #if defined __GNUC__
11
+ #define __CONST __const
12
+ #elif defined _MSC_VER
13
+ #define __CONST const
14
+ #else
15
+ #endif
16
+
17
+ #ifndef __SKIP_GNU
18
+ extern char *crypt(__CONST char *key, __CONST char *setting);
19
+ extern char *crypt_r(__CONST char *key, __CONST char *setting, void *data);
20
+ #endif
21
+
22
+ #ifndef __SKIP_OW
23
+ extern char *crypt_rn(__CONST char *key, __CONST char *setting,
24
+ void *data, int size);
25
+ extern char *crypt_ra(__CONST char *key, __CONST char *setting,
26
+ void **data, int *size);
27
+ extern char *crypt_gensalt(__CONST char *prefix, unsigned long count,
28
+ __CONST char *input, int size);
29
+ extern char *crypt_gensalt_rn(__CONST char *prefix, unsigned long count,
30
+ __CONST char *input, int size, char *output, int output_size);
31
+ extern char *crypt_gensalt_ra(__CONST char *prefix, unsigned long count,
32
+ __CONST char *input, int size);
33
+ #endif
34
+
35
+ #endif
data/ext/mri/wrapper.c ADDED
@@ -0,0 +1,262 @@
1
+ /*
2
+ * Written by Solar Designer and placed in the public domain.
3
+ * See crypt_blowfish.c for more information.
4
+ */
5
+
6
+ #include <stdlib.h>
7
+ #include <string.h>
8
+
9
+ #include <errno.h>
10
+ #ifndef __set_errno
11
+ #define __set_errno(val) errno = (val)
12
+ #endif
13
+
14
+ #ifdef TEST
15
+ #include <stdio.h>
16
+ #include <unistd.h>
17
+ #include <signal.h>
18
+ #include <time.h>
19
+ #include <sys/time.h>
20
+ #include <sys/times.h>
21
+ #ifdef TEST_THREADS
22
+ #include <pthread.h>
23
+ #endif
24
+ #endif
25
+
26
+ #include <ruby.h>
27
+ #ifdef HAVE_RUBY_UTIL_H
28
+ #include <ruby/util.h>
29
+ #else
30
+ #include <util.h>
31
+ #endif
32
+
33
+ #define CRYPT_OUTPUT_SIZE (7 + 22 + 31 + 1)
34
+ #define CRYPT_GENSALT_OUTPUT_SIZE (7 + 22 + 1)
35
+
36
+ #if defined(__GLIBC__) && defined(_LIBC)
37
+ #define __SKIP_GNU
38
+ #endif
39
+ #include "ow-crypt.h"
40
+
41
+ extern char *_crypt_blowfish_rn(__CONST char *key, __CONST char *setting,
42
+ char *output, int size);
43
+ extern char *_crypt_gensalt_blowfish_rn(unsigned long count,
44
+ __CONST char *input, int size, char *output, int output_size);
45
+
46
+ extern unsigned char _crypt_itoa64[];
47
+ extern char *_crypt_gensalt_traditional_rn(unsigned long count,
48
+ __CONST char *input, int size, char *output, int output_size);
49
+ extern char *_crypt_gensalt_extended_rn(unsigned long count,
50
+ __CONST char *input, int size, char *output, int output_size);
51
+ extern char *_crypt_gensalt_md5_rn(unsigned long count,
52
+ __CONST char *input, int size, char *output, int output_size);
53
+
54
+ #if defined(__GLIBC__) && defined(_LIBC)
55
+ /* crypt.h from glibc-crypt-2.1 will define struct crypt_data for us */
56
+ #include "crypt.h"
57
+ extern char *__md5_crypt_r(const char *key, const char *salt,
58
+ char *buffer, int buflen);
59
+ /* crypt-entry.c needs to be patched to define __des_crypt_r rather than
60
+ * __crypt_r, and not define crypt_r and crypt at all */
61
+ extern char *__des_crypt_r(const char *key, const char *salt,
62
+ struct crypt_data *data);
63
+ extern struct crypt_data _ufc_foobar;
64
+ #endif
65
+
66
+ static int _crypt_data_alloc(void **data, int *size, int need)
67
+ {
68
+ void *updated;
69
+
70
+ if (*data && *size >= need) return 0;
71
+
72
+ updated = realloc(*data, need);
73
+
74
+ if (!updated) {
75
+ #ifndef __GLIBC__
76
+ /* realloc(3) on glibc sets errno, so we don't need to bother */
77
+ __set_errno(ENOMEM);
78
+ #endif
79
+ return -1;
80
+ }
81
+
82
+ #if defined(__GLIBC__) && defined(_LIBC)
83
+ if (need >= sizeof(struct crypt_data))
84
+ ((struct crypt_data *)updated)->initialized = 0;
85
+ #endif
86
+
87
+ *data = updated;
88
+ *size = need;
89
+
90
+ return 0;
91
+ }
92
+
93
+ static char *_crypt_retval_magic(char *retval, __CONST char *setting,
94
+ char *output)
95
+ {
96
+ if (retval) return retval;
97
+
98
+ output[0] = '*';
99
+ output[1] = '0';
100
+ output[2] = '\0';
101
+
102
+ if (setting[0] == '*' && setting[1] == '0')
103
+ output[1] = '1';
104
+
105
+ return output;
106
+ }
107
+
108
+ #if defined(__GLIBC__) && defined(_LIBC)
109
+ /*
110
+ * Applications may re-use the same instance of struct crypt_data without
111
+ * resetting the initialized field in order to let crypt_r() skip some of
112
+ * its initialization code. Thus, it is important that our multiple hashing
113
+ * algorithms either don't conflict with each other in their use of the
114
+ * data area or reset the initialized field themselves whenever required.
115
+ * Currently, the hashing algorithms simply have no conflicts: the first
116
+ * field of struct crypt_data is the 128-byte large DES key schedule which
117
+ * __des_crypt_r() calculates each time it is called while the two other
118
+ * hashing algorithms use less than 128 bytes of the data area.
119
+ */
120
+
121
+ char *__crypt_rn(__const char *key, __const char *setting,
122
+ void *data, int size)
123
+ {
124
+ if (setting[0] == '$' && setting[1] == '2')
125
+ return _crypt_blowfish_rn(key, setting, (char *)data, size);
126
+ if (setting[0] == '$' && setting[1] == '1')
127
+ return __md5_crypt_r(key, setting, (char *)data, size);
128
+ if (setting[0] == '$' || setting[0] == '_') {
129
+ __set_errno(EINVAL);
130
+ return NULL;
131
+ }
132
+ if (size >= sizeof(struct crypt_data))
133
+ return __des_crypt_r(key, setting, (struct crypt_data *)data);
134
+ __set_errno(ERANGE);
135
+ return NULL;
136
+ }
137
+
138
+ char *__crypt_ra(__const char *key, __const char *setting,
139
+ void **data, int *size)
140
+ {
141
+ if (setting[0] == '$' && setting[1] == '2') {
142
+ if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
143
+ return NULL;
144
+ return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
145
+ }
146
+ if (setting[0] == '$' && setting[1] == '1') {
147
+ if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
148
+ return NULL;
149
+ return __md5_crypt_r(key, setting, (char *)*data, *size);
150
+ }
151
+ if (setting[0] == '$' || setting[0] == '_') {
152
+ __set_errno(EINVAL);
153
+ return NULL;
154
+ }
155
+ if (_crypt_data_alloc(data, size, sizeof(struct crypt_data)))
156
+ return NULL;
157
+ return __des_crypt_r(key, setting, (struct crypt_data *)*data);
158
+ }
159
+
160
+ char *__crypt_r(__const char *key, __const char *setting,
161
+ struct crypt_data *data)
162
+ {
163
+ return _crypt_retval_magic(
164
+ __crypt_rn(key, setting, data, sizeof(*data)),
165
+ setting, (char *)data);
166
+ }
167
+
168
+ char *__crypt(__const char *key, __const char *setting)
169
+ {
170
+ return _crypt_retval_magic(
171
+ __crypt_rn(key, setting, &_ufc_foobar, sizeof(_ufc_foobar)),
172
+ setting, (char *)&_ufc_foobar);
173
+ }
174
+ #else
175
+ char *crypt_rn(__CONST char *key, __CONST char *setting, void *data, int size)
176
+ {
177
+ return _crypt_blowfish_rn(key, setting, (char *)data, size);
178
+ }
179
+
180
+ char *crypt_ra(__CONST char *key, __CONST char *setting,
181
+ void **data, int *size)
182
+ {
183
+ if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
184
+ return NULL;
185
+ return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
186
+ }
187
+
188
+ char *crypt_r(__CONST char *key, __CONST char *setting, void *data)
189
+ {
190
+ return _crypt_retval_magic(
191
+ crypt_rn(key, setting, data, CRYPT_OUTPUT_SIZE),
192
+ setting, (char *)data);
193
+ }
194
+
195
+ #define __crypt_gensalt_rn crypt_gensalt_rn
196
+ #define __crypt_gensalt_ra crypt_gensalt_ra
197
+ #define __crypt_gensalt crypt_gensalt
198
+ #endif
199
+
200
+ char *__crypt_gensalt_rn(__CONST char *prefix, unsigned long count,
201
+ __CONST char *input, int size, char *output, int output_size)
202
+ {
203
+ char *(*use)(unsigned long count,
204
+ __CONST char *input, int size, char *output, int output_size);
205
+
206
+ /* This may be supported on some platforms in the future */
207
+ if (!input) {
208
+ __set_errno(EINVAL);
209
+ return NULL;
210
+ }
211
+
212
+ if (!strncmp(prefix, "$2a$", 4))
213
+ use = _crypt_gensalt_blowfish_rn;
214
+ else
215
+ if (!strncmp(prefix, "$1$", 3))
216
+ use = _crypt_gensalt_md5_rn;
217
+ else
218
+ if (prefix[0] == '_')
219
+ use = _crypt_gensalt_extended_rn;
220
+ else
221
+ if (!prefix[0] ||
222
+ (prefix[0] && prefix[1] &&
223
+ memchr(_crypt_itoa64, prefix[0], 64) &&
224
+ memchr(_crypt_itoa64, prefix[1], 64)))
225
+ use = _crypt_gensalt_traditional_rn;
226
+ else {
227
+ __set_errno(EINVAL);
228
+ return NULL;
229
+ }
230
+
231
+ return use(count, input, size, output, output_size);
232
+ }
233
+
234
+ char *__crypt_gensalt_ra(__CONST char *prefix, unsigned long count,
235
+ __CONST char *input, int size)
236
+ {
237
+ char output[CRYPT_GENSALT_OUTPUT_SIZE];
238
+ char *retval;
239
+
240
+ retval = __crypt_gensalt_rn(prefix, count,
241
+ input, size, output, sizeof(output));
242
+
243
+ if (retval) {
244
+ retval = ruby_strdup(retval);
245
+ #ifndef __GLIBC__
246
+ /* strdup(3) on glibc sets errno, so we don't need to bother */
247
+ if (!retval)
248
+ __set_errno(ENOMEM);
249
+ #endif
250
+ }
251
+
252
+ return retval;
253
+ }
254
+
255
+ char *__crypt_gensalt(__CONST char *prefix, unsigned long count,
256
+ __CONST char *input, int size)
257
+ {
258
+ static char output[CRYPT_GENSALT_OUTPUT_SIZE];
259
+
260
+ return __crypt_gensalt_rn(prefix, count,
261
+ input, size, output, sizeof(output));
262
+ }
@@ -0,0 +1,123 @@
1
+ module BCrypt
2
+ # A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
3
+ class Engine
4
+ # The default computational expense parameter.
5
+ DEFAULT_COST = 10
6
+ # The minimum cost supported by the algorithm.
7
+ MIN_COST = 4
8
+ # Maximum possible size of bcrypt() salts.
9
+ MAX_SALT_LENGTH = 16
10
+
11
+ if RUBY_PLATFORM != "java"
12
+ # C-level routines which, if they don't get the right input, will crash the
13
+ # hell out of the Ruby process.
14
+ private_class_method :__bc_salt
15
+ private_class_method :__bc_crypt
16
+ end
17
+
18
+ @cost = nil
19
+
20
+ # Returns the cost factor that will be used if one is not specified when
21
+ # creating a password hash. Defaults to DEFAULT_COST if not set.
22
+ def self.cost
23
+ @cost || DEFAULT_COST
24
+ end
25
+
26
+ # Set a default cost factor that will be used if one is not specified when
27
+ # creating a password hash.
28
+ #
29
+ # Example:
30
+ #
31
+ # BCrypt::Engine::DEFAULT_COST #=> 10
32
+ # BCrypt::Password.create('secret').cost #=> 10
33
+ #
34
+ # BCrypt::Engine.cost = 8
35
+ # BCrypt::Password.create('secret').cost #=> 8
36
+ #
37
+ # # cost can still be overridden as needed
38
+ # BCrypt::Password.create('secret', :cost => 6).cost #=> 6
39
+ def self.cost=(cost)
40
+ @cost = cost
41
+ end
42
+
43
+ # Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates
44
+ # a bcrypt() password hash.
45
+ def self.hash_secret(secret, salt, _ = nil)
46
+ if valid_secret?(secret)
47
+ if valid_salt?(salt)
48
+ if RUBY_PLATFORM == "java"
49
+ # the native C bcrypt implementation used by the MRI version of this gem
50
+ # interprets secret as a null-terminated string; to ensure consistency,
51
+ # we drop after any null byte
52
+ trimmed = secret.to_s.split("\0").first # drop after null byte to emulate C
53
+
54
+ # the java hashpw() method taking byte[] expects the null terminator
55
+ bytes = (trimmed.to_s + "\0").to_java_bytes # add null byte for byte[] API
56
+ Java.bcrypt_jruby.BCrypt.hashpw(bytes, salt.to_s)
57
+ else
58
+ __bc_crypt(secret.to_s, salt)
59
+ end
60
+ else
61
+ raise Errors::InvalidSalt.new("invalid salt")
62
+ end
63
+ else
64
+ raise Errors::InvalidSecret.new("invalid secret")
65
+ end
66
+ end
67
+
68
+ # Generates a random salt with a given computational cost.
69
+ def self.generate_salt(cost = self.cost)
70
+ cost = cost.to_i
71
+ if cost > 0
72
+ if cost < MIN_COST
73
+ cost = MIN_COST
74
+ end
75
+ if RUBY_PLATFORM == "java"
76
+ Java.bcrypt_jruby.BCrypt.gensalt(cost)
77
+ else
78
+ prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
79
+ __bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
80
+ end
81
+ else
82
+ raise Errors::InvalidCost.new("cost must be numeric and > 0")
83
+ end
84
+ end
85
+
86
+ # Returns true if +salt+ is a valid bcrypt() salt, false if not.
87
+ def self.valid_salt?(salt)
88
+ !!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/)
89
+ end
90
+
91
+ # Returns true if +secret+ is a valid bcrypt() secret, false if not.
92
+ def self.valid_secret?(secret)
93
+ secret.respond_to?(:to_s)
94
+ end
95
+
96
+ # Returns the cost factor which will result in computation times less than +upper_time_limit_in_ms+.
97
+ #
98
+ # Example:
99
+ #
100
+ # BCrypt::Engine.calibrate(200) #=> 10
101
+ # BCrypt::Engine.calibrate(1000) #=> 12
102
+ #
103
+ # # should take less than 200ms
104
+ # BCrypt::Password.create("woo", :cost => 10)
105
+ #
106
+ # # should take less than 1000ms
107
+ # BCrypt::Password.create("woo", :cost => 12)
108
+ def self.calibrate(upper_time_limit_in_ms)
109
+ 40.times do |i|
110
+ start_time = Time.now
111
+ Password.create("testing testing", :cost => i+1)
112
+ end_time = Time.now - start_time
113
+ return i if end_time * 1_000 > upper_time_limit_in_ms
114
+ end
115
+ end
116
+
117
+ # Autodetects the cost from the salt string.
118
+ def self.autodetect_cost(salt)
119
+ salt[4..5].to_i
120
+ end
121
+ end
122
+
123
+ end
@@ -0,0 +1,22 @@
1
+ module BCrypt
2
+
3
+ class Error < StandardError # :nodoc:
4
+ end
5
+
6
+ module Errors # :nodoc:
7
+
8
+ # The salt parameter provided to bcrypt() is invalid.
9
+ class InvalidSalt < BCrypt::Error; end
10
+
11
+ # The hash parameter provided to bcrypt() is invalid.
12
+ class InvalidHash < BCrypt::Error; end
13
+
14
+ # The cost parameter provided to bcrypt() is invalid.
15
+ class InvalidCost < BCrypt::Error; end
16
+
17
+ # The secret parameter provided to bcrypt() is invalid.
18
+ class InvalidSecret < BCrypt::Error; end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,87 @@
1
+ module BCrypt
2
+ # A password management class which allows you to safely store users' passwords and compare them.
3
+ #
4
+ # Example usage:
5
+ #
6
+ # include BCrypt
7
+ #
8
+ # # hash a user's password
9
+ # @password = Password.create("my grand secret")
10
+ # @password #=> "$2a$10$GtKs1Kbsig8ULHZzO1h2TetZfhO4Fmlxphp8bVKnUlZCBYYClPohG"
11
+ #
12
+ # # store it safely
13
+ # @user.update_attribute(:password, @password)
14
+ #
15
+ # # read it back
16
+ # @user.reload!
17
+ # @db_password = Password.new(@user.password)
18
+ #
19
+ # # compare it after retrieval
20
+ # @db_password == "my grand secret" #=> true
21
+ # @db_password == "a paltry guess" #=> false
22
+ #
23
+ class Password < String
24
+ # The hash portion of the stored password hash.
25
+ attr_reader :checksum
26
+ # The salt of the store password hash (including version and cost).
27
+ attr_reader :salt
28
+ # The version of the bcrypt() algorithm used to create the hash.
29
+ attr_reader :version
30
+ # The cost factor used to create the hash.
31
+ attr_reader :cost
32
+
33
+ class << self
34
+ # Hashes a secret, returning a BCrypt::Password instance. Takes an optional <tt>:cost</tt> option, which is a
35
+ # logarithmic variable which determines how computational expensive the hash is to calculate (a <tt>:cost</tt> of
36
+ # 4 is twice as much work as a <tt>:cost</tt> of 3). The higher the <tt>:cost</tt> the harder it becomes for
37
+ # attackers to try to guess passwords (even if a copy of your database is stolen), but the slower it is to check
38
+ # users' passwords.
39
+ #
40
+ # Example:
41
+ #
42
+ # @password = BCrypt::Password.create("my secret", :cost => 13)
43
+ def create(secret, options = {})
44
+ cost = options[:cost] || BCrypt::Engine.cost
45
+ raise ArgumentError if cost > 31
46
+ Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(cost)))
47
+ end
48
+
49
+ def valid_hash?(h)
50
+ h =~ /^\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}$/
51
+ end
52
+ end
53
+
54
+ # Initializes a BCrypt::Password instance with the data from a stored hash.
55
+ def initialize(raw_hash)
56
+ if valid_hash?(raw_hash)
57
+ self.replace(raw_hash)
58
+ @version, @cost, @salt, @checksum = split_hash(self)
59
+ else
60
+ raise Errors::InvalidHash.new("invalid hash")
61
+ end
62
+ end
63
+
64
+ # Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
65
+ def ==(secret)
66
+ super(BCrypt::Engine.hash_secret(secret, @salt))
67
+ end
68
+ alias_method :is_password?, :==
69
+
70
+ private
71
+
72
+ # Returns true if +h+ is a valid hash.
73
+ def valid_hash?(h)
74
+ self.class.valid_hash?(h)
75
+ end
76
+
77
+ # call-seq:
78
+ # split_hash(raw_hash) -> version, cost, salt, hash
79
+ #
80
+ # Splits +h+ into version, cost, salt, and hash and returns them in that order.
81
+ def split_hash(h)
82
+ _, v, c, mash = h.split('$')
83
+ return v, c.to_i, h[0, 29].to_str, mash[-31, 31].to_str
84
+ end
85
+ end
86
+
87
+ end
data/lib/bcrypt.rb ADDED
@@ -0,0 +1,21 @@
1
+ # A Ruby library implementing OpenBSD's bcrypt()/crypt_blowfish algorithm for
2
+ # hashing passwords.
3
+ module BCrypt
4
+ end
5
+
6
+ if RUBY_PLATFORM == "java"
7
+ require 'java'
8
+ else
9
+ require "openssl"
10
+ end
11
+
12
+ begin
13
+ RUBY_VERSION =~ /(\d+.\d+)/
14
+ require "#{$1}/bcrypt_ext"
15
+ rescue LoadError
16
+ require "bcrypt_ext"
17
+ end
18
+
19
+ require 'bcrypt/error'
20
+ require 'bcrypt/engine'
21
+ require 'bcrypt/password'