bcrypt-ruby 2.0.5 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bcrypt-ruby might be problematic. Click here for more details.
- data/CHANGELOG +7 -1
- data/COPYING +4 -3
- data/README +3 -0
- data/Rakefile +51 -25
- data/ext/jruby/bcrypt_jruby/BCrypt.class +0 -0
- data/ext/jruby/bcrypt_jruby/BCrypt.java +752 -0
- data/ext/{bcrypt.c → mri/bcrypt.c} +25 -31
- data/ext/mri/bcrypt.h +65 -0
- data/ext/mri/bcrypt_ext.c +87 -0
- data/ext/{blf.h → mri/blf.h} +0 -0
- data/ext/{blowfish.c → mri/blowfish.c} +0 -0
- data/ext/mri/extconf.rb +18 -0
- data/lib/bcrypt.rb +43 -13
- data/spec/bcrypt/engine_spec.rb +23 -4
- data/spec/bcrypt/password_spec.rb +4 -4
- data/spec/spec_helper.rb +1 -1
- metadata +16 -11
- data/ext/bcrypt_ext.c +0 -36
- data/ext/extconf.rb +0 -5
@@ -1,6 +1,11 @@
|
|
1
1
|
/* $OpenBSD: bcrypt.c,v 1.22 2007/02/20 01:44:16 ray Exp $ */
|
2
2
|
|
3
3
|
/*
|
4
|
+
* Modified by <hongli@phusion.nl> on 2009-08-05:
|
5
|
+
*
|
6
|
+
* - Got rid of the global variables; they're not thread-safe.
|
7
|
+
* Modified the functions to accept local buffers instead.
|
8
|
+
*
|
4
9
|
* Modified by <coda.hale@gmail.com> on 2007-02-27:
|
5
10
|
*
|
6
11
|
* - Changed bcrypt_gensalt to accept a random seed as a parameter,
|
@@ -62,27 +67,17 @@
|
|
62
67
|
#include <sys/types.h>
|
63
68
|
#include <string.h>
|
64
69
|
#include "blf.h"
|
70
|
+
#include "bcrypt.h"
|
65
71
|
|
66
72
|
/* This implementation is adaptable to current computing power.
|
67
73
|
* You can have up to 2^31 rounds which should be enough for some
|
68
74
|
* time to come.
|
69
75
|
*/
|
70
76
|
|
71
|
-
#define BCRYPT_VERSION '2'
|
72
|
-
#define BCRYPT_MAXSALT 16 /* Precomputation is just so nice */
|
73
|
-
#define BCRYPT_BLOCKS 6 /* Ciphertext blocks */
|
74
|
-
#define BCRYPT_MINROUNDS 16 /* we have log2(rounds) in salt */
|
75
|
-
|
76
|
-
char *bcrypt_gensalt(u_int8_t, u_int8_t *);
|
77
|
-
|
78
77
|
static void encode_salt(char *, u_int8_t *, u_int16_t, u_int8_t);
|
79
78
|
static void encode_base64(u_int8_t *, u_int8_t *, u_int16_t);
|
80
79
|
static void decode_base64(u_int8_t *, u_int16_t, u_int8_t *);
|
81
80
|
|
82
|
-
static char encrypted[_PASSWORD_LEN];
|
83
|
-
static char gsalt[7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1];
|
84
|
-
static char error[] = ":";
|
85
|
-
|
86
81
|
const static u_int8_t Base64Code[] =
|
87
82
|
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
88
83
|
|
@@ -155,22 +150,22 @@ encode_salt(char *salt, u_int8_t *csalt, u_int16_t clen, u_int8_t logr)
|
|
155
150
|
seems sensible.
|
156
151
|
*/
|
157
152
|
|
158
|
-
char
|
159
|
-
bcrypt_gensalt(u_int8_t log_rounds, u_int8_t *rseed)
|
153
|
+
char *
|
154
|
+
bcrypt_gensalt(char *output, u_int8_t log_rounds, u_int8_t *rseed)
|
160
155
|
{
|
161
156
|
if (log_rounds < 4)
|
162
157
|
log_rounds = 4;
|
163
158
|
else if (log_rounds > 31)
|
164
159
|
log_rounds = 31;
|
165
160
|
|
166
|
-
encode_salt(
|
167
|
-
return
|
161
|
+
encode_salt(output, rseed, BCRYPT_MAXSALT, log_rounds);
|
162
|
+
return output;
|
168
163
|
}
|
169
164
|
/* We handle $Vers$log2(NumRounds)$salt+passwd$
|
170
165
|
i.e. $2$04$iwouldntknowwhattosayetKdJ6iFtacBqJdKe6aW7ou */
|
171
166
|
|
172
167
|
char *
|
173
|
-
bcrypt(const char *key, const char *salt)
|
168
|
+
bcrypt(char *output, const char *key, const char *salt)
|
174
169
|
{
|
175
170
|
blf_ctx state;
|
176
171
|
u_int32_t rounds, i, k;
|
@@ -185,8 +180,7 @@ bcrypt(const char *key, const char *salt)
|
|
185
180
|
salt++;
|
186
181
|
|
187
182
|
if (*salt > BCRYPT_VERSION) {
|
188
|
-
|
189
|
-
return error;
|
183
|
+
return NULL;
|
190
184
|
}
|
191
185
|
|
192
186
|
/* Check for minor versions */
|
@@ -198,7 +192,7 @@ bcrypt(const char *key, const char *salt)
|
|
198
192
|
salt++;
|
199
193
|
break;
|
200
194
|
default:
|
201
|
-
return
|
195
|
+
return NULL;
|
202
196
|
}
|
203
197
|
} else
|
204
198
|
minor = 0;
|
@@ -208,21 +202,21 @@ bcrypt(const char *key, const char *salt)
|
|
208
202
|
|
209
203
|
if (salt[2] != '$')
|
210
204
|
/* Out of sync with passwd entry */
|
211
|
-
return
|
205
|
+
return NULL;
|
212
206
|
|
213
207
|
/* Computer power doesn't increase linear, 2^x should be fine */
|
214
208
|
n = atoi(salt);
|
215
209
|
if (n > 31 || n < 0)
|
216
|
-
return
|
210
|
+
return NULL;
|
217
211
|
logr = (u_int8_t)n;
|
218
212
|
if ((rounds = (u_int32_t) 1 << logr) < BCRYPT_MINROUNDS)
|
219
|
-
return
|
213
|
+
return NULL;
|
220
214
|
|
221
215
|
/* Discard num rounds + "$" identifier */
|
222
216
|
salt += 3;
|
223
217
|
|
224
218
|
if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT)
|
225
|
-
return
|
219
|
+
return NULL;
|
226
220
|
|
227
221
|
/* We dont want the base64 salt but the raw data */
|
228
222
|
decode_base64(csalt, BCRYPT_MAXSALT, (u_int8_t *) salt);
|
@@ -259,18 +253,18 @@ bcrypt(const char *key, const char *salt)
|
|
259
253
|
|
260
254
|
|
261
255
|
i = 0;
|
262
|
-
|
263
|
-
|
256
|
+
output[i++] = '$';
|
257
|
+
output[i++] = BCRYPT_VERSION;
|
264
258
|
if (minor)
|
265
|
-
|
266
|
-
|
259
|
+
output[i++] = minor;
|
260
|
+
output[i++] = '$';
|
267
261
|
|
268
|
-
snprintf(
|
262
|
+
snprintf(output + i, 4, "%2.2u$", logr);
|
269
263
|
|
270
|
-
encode_base64((u_int8_t *)
|
271
|
-
encode_base64((u_int8_t *)
|
264
|
+
encode_base64((u_int8_t *) output + i + 3, csalt, BCRYPT_MAXSALT);
|
265
|
+
encode_base64((u_int8_t *) output + strlen(output), ciphertext,
|
272
266
|
4 * BCRYPT_BLOCKS - 1);
|
273
|
-
return
|
267
|
+
return output;
|
274
268
|
}
|
275
269
|
|
276
270
|
static void
|
data/ext/mri/bcrypt.h
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
|
3
|
+
* All rights reserved.
|
4
|
+
*
|
5
|
+
* Redistribution and use in source and binary forms, with or without
|
6
|
+
* modification, are permitted provided that the following conditions
|
7
|
+
* are met:
|
8
|
+
* 1. Redistributions of source code must retain the above copyright
|
9
|
+
* notice, this list of conditions and the following disclaimer.
|
10
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
11
|
+
* notice, this list of conditions and the following disclaimer in the
|
12
|
+
* documentation and/or other materials provided with the distribution.
|
13
|
+
* 3. All advertising materials mentioning features or use of this software
|
14
|
+
* must display the following acknowledgement:
|
15
|
+
* This product includes software developed by Niels Provos.
|
16
|
+
* 4. The name of the author may not be used to endorse or promote products
|
17
|
+
* derived from this software without specific prior written permission.
|
18
|
+
*
|
19
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
20
|
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
21
|
+
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
22
|
+
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
23
|
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
24
|
+
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
26
|
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
27
|
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
28
|
+
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
*/
|
30
|
+
|
31
|
+
#ifndef _BCRYPT_H_
|
32
|
+
#define _BCRYPT_H_
|
33
|
+
|
34
|
+
#define BCRYPT_VERSION '2'
|
35
|
+
#define BCRYPT_MAXSALT 16 /* Precomputation is just so nice */
|
36
|
+
#define BCRYPT_BLOCKS 6 /* Ciphertext blocks */
|
37
|
+
#define BCRYPT_MINROUNDS 16 /* we have log2(rounds) in salt */
|
38
|
+
#define BCRYPT_SALT_OUTPUT_SIZE (7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1)
|
39
|
+
#define BCRYPT_OUTPUT_SIZE 128
|
40
|
+
|
41
|
+
/*
|
42
|
+
* Given a logarithmic cost parameter, generates a salt for use with bcrypt().
|
43
|
+
*
|
44
|
+
* output: the computed salt will be stored here. This buffer must be
|
45
|
+
* at least BCRYPT_SALT_OUTPUT_SIZE bytes. The result will be
|
46
|
+
* null-terminated.
|
47
|
+
* log_rounds: the logarithmic cost.
|
48
|
+
* rseed: a seed of BCRYPT_MAXSALT bytes. Should be obtained from a
|
49
|
+
* cryptographically secure random source.
|
50
|
+
* Returns: output
|
51
|
+
*/
|
52
|
+
char *bcrypt_gensalt(char *output, u_int8_t log_rounds, u_int8_t *rseed);
|
53
|
+
|
54
|
+
/*
|
55
|
+
* Given a secret and a salt, generates a salted hash (which you can then store safely).
|
56
|
+
*
|
57
|
+
* output: the computed salted hash will be stored here. This buffer must
|
58
|
+
* be at least BCRYPT_OUTPUT_SIZE bytes, and will become null-terminated.
|
59
|
+
* key: A null-terminated secret.
|
60
|
+
* salt: The salt, as generated by bcrypt_gensalt().
|
61
|
+
* Returns: output on success, NULL on error.
|
62
|
+
*/
|
63
|
+
char *bcrypt(char *output, const char *key, const char *salt);
|
64
|
+
|
65
|
+
#endif /* _BCRYPT_H_ */
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include "bcrypt.h"
|
3
|
+
|
4
|
+
static VALUE mBCrypt;
|
5
|
+
static VALUE cBCryptEngine;
|
6
|
+
|
7
|
+
/* Define RSTRING_PTR for Ruby 1.8.5, ruby-core's idea of a point release is
|
8
|
+
insane. */
|
9
|
+
#ifndef RSTRING_PTR
|
10
|
+
# define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
11
|
+
#endif
|
12
|
+
|
13
|
+
#ifdef RUBY_VM
|
14
|
+
# define RUBY_1_9
|
15
|
+
#endif
|
16
|
+
|
17
|
+
#ifdef RUBY_1_9
|
18
|
+
|
19
|
+
/* When on Ruby 1.9+, we will want to unlock the GIL while performing
|
20
|
+
* expensive calculations, for greater concurrency. Do not do this for
|
21
|
+
* cheap calculations because locking/unlocking the GIL incurs some overhead as well.
|
22
|
+
*/
|
23
|
+
#define GIL_UNLOCK_COST_THRESHOLD 9
|
24
|
+
|
25
|
+
typedef struct {
|
26
|
+
char *output;
|
27
|
+
const char *key;
|
28
|
+
const char *salt;
|
29
|
+
} BCryptArguments;
|
30
|
+
|
31
|
+
static VALUE bcrypt_wrapper(void *_args) {
|
32
|
+
BCryptArguments *args = (BCryptArguments *)_args;
|
33
|
+
return (VALUE)bcrypt(args->output, args->key, args->salt);
|
34
|
+
}
|
35
|
+
|
36
|
+
#endif /* RUBY_1_9 */
|
37
|
+
|
38
|
+
/* Given a logarithmic cost parameter, generates a salt for use with +bc_crypt+.
|
39
|
+
*/
|
40
|
+
static VALUE bc_salt(VALUE self, VALUE cost, VALUE seed) {
|
41
|
+
int icost = NUM2INT(cost);
|
42
|
+
char salt[BCRYPT_SALT_OUTPUT_SIZE];
|
43
|
+
|
44
|
+
bcrypt_gensalt(salt, icost, (u_int8_t *)RSTRING_PTR(seed));
|
45
|
+
return rb_str_new2(salt);
|
46
|
+
}
|
47
|
+
|
48
|
+
/* Given a secret and a salt, generates a salted hash (which you can then store safely).
|
49
|
+
*/
|
50
|
+
static VALUE bc_crypt(VALUE self, VALUE key, VALUE salt, VALUE cost) {
|
51
|
+
const char * safeguarded = RSTRING_PTR(key) ? RSTRING_PTR(key) : "";
|
52
|
+
char output[BCRYPT_OUTPUT_SIZE];
|
53
|
+
|
54
|
+
#ifdef RUBY_1_9
|
55
|
+
int icost = NUM2INT(cost);
|
56
|
+
if (icost >= GIL_UNLOCK_COST_THRESHOLD) {
|
57
|
+
BCryptArguments args;
|
58
|
+
VALUE ret;
|
59
|
+
|
60
|
+
args.output = output;
|
61
|
+
args.key = safeguarded;
|
62
|
+
args.salt = RSTRING_PTR(salt);
|
63
|
+
ret = rb_thread_blocking_region(bcrypt_wrapper, &args, RUBY_UBF_IO, 0);
|
64
|
+
if (ret != (VALUE) 0) {
|
65
|
+
return rb_str_new2(output);
|
66
|
+
} else {
|
67
|
+
return Qnil;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
/* otherwise, fallback to the non-GIL-unlocking code, just like on Ruby 1.8 */
|
71
|
+
#endif
|
72
|
+
|
73
|
+
if (bcrypt(output, safeguarded, (char *)RSTRING_PTR(salt)) != NULL) {
|
74
|
+
return rb_str_new2(output);
|
75
|
+
} else {
|
76
|
+
return Qnil;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
/* Create the BCrypt and BCrypt::Engine modules, and populate them with methods. */
|
81
|
+
void Init_bcrypt_ext(){
|
82
|
+
mBCrypt = rb_define_module("BCrypt");
|
83
|
+
cBCryptEngine = rb_define_class_under(mBCrypt, "Engine", rb_cObject);
|
84
|
+
|
85
|
+
rb_define_singleton_method(cBCryptEngine, "__bc_salt", bc_salt, 2);
|
86
|
+
rb_define_singleton_method(cBCryptEngine, "__bc_crypt", bc_crypt, 3);
|
87
|
+
}
|
data/ext/{blf.h → mri/blf.h}
RENAMED
File without changes
|
File without changes
|
data/ext/mri/extconf.rb
ADDED
@@ -0,0 +1,18 @@
|
|
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
|
+
# enable this when we're feeling nitpicky
|
16
|
+
# CONFIG['CC'] << " -Wall "
|
17
|
+
create_makefile("bcrypt_ext")
|
18
|
+
end
|
data/lib/bcrypt.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
# A wrapper for OpenBSD's bcrypt/crypt_blowfish password-hashing algorithm.
|
2
2
|
|
3
|
-
|
4
|
-
require
|
5
|
-
|
3
|
+
if RUBY_PLATFORM == "java"
|
4
|
+
require 'java'
|
5
|
+
$CLASSPATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "ext", "jruby"))
|
6
|
+
else
|
7
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "ext", "mri")))
|
8
|
+
require "bcrypt_ext"
|
9
|
+
require "openssl"
|
10
|
+
end
|
6
11
|
|
7
12
|
# A Ruby library implementing OpenBSD's bcrypt()/crypt_blowfish algorithm for
|
8
13
|
# hashing passwords.
|
@@ -14,24 +19,36 @@ module BCrypt
|
|
14
19
|
class InvalidSecret < StandardError; end # The secret parameter provided to bcrypt() is invalid.
|
15
20
|
end
|
16
21
|
|
17
|
-
# A Ruby wrapper for the bcrypt() extension calls.
|
22
|
+
# A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
|
18
23
|
class Engine
|
19
24
|
# The default computational expense parameter.
|
20
25
|
DEFAULT_COST = 10
|
26
|
+
# The minimum cost supported by the algorithm.
|
27
|
+
MIN_COST = 4
|
21
28
|
# Maximum possible size of bcrypt() salts.
|
22
29
|
MAX_SALT_LENGTH = 16
|
23
30
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
31
|
+
if RUBY_PLATFORM != "java"
|
32
|
+
# C-level routines which, if they don't get the right input, will crash the
|
33
|
+
# hell out of the Ruby process.
|
34
|
+
private_class_method :__bc_salt
|
35
|
+
private_class_method :__bc_crypt
|
36
|
+
end
|
28
37
|
|
29
38
|
# Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates
|
30
39
|
# a bcrypt() password hash.
|
31
|
-
def self.hash_secret(secret, salt)
|
40
|
+
def self.hash_secret(secret, salt, cost = nil)
|
32
41
|
if valid_secret?(secret)
|
33
42
|
if valid_salt?(salt)
|
34
|
-
|
43
|
+
if cost.nil?
|
44
|
+
cost = autodetect_cost(salt)
|
45
|
+
end
|
46
|
+
|
47
|
+
if RUBY_PLATFORM == "java"
|
48
|
+
Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s, salt.to_s)
|
49
|
+
else
|
50
|
+
__bc_crypt(secret.to_s, salt, cost)
|
51
|
+
end
|
35
52
|
else
|
36
53
|
raise Errors::InvalidSalt.new("invalid salt")
|
37
54
|
end
|
@@ -42,8 +59,16 @@ module BCrypt
|
|
42
59
|
|
43
60
|
# Generates a random salt with a given computational cost.
|
44
61
|
def self.generate_salt(cost = DEFAULT_COST)
|
45
|
-
|
46
|
-
|
62
|
+
cost = cost.to_i
|
63
|
+
if cost > 0
|
64
|
+
if cost < MIN_COST
|
65
|
+
cost = MIN_COST
|
66
|
+
end
|
67
|
+
if RUBY_PLATFORM == "java"
|
68
|
+
Java.bcrypt_jruby.BCrypt.gensalt(cost)
|
69
|
+
else
|
70
|
+
__bc_salt(cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
|
71
|
+
end
|
47
72
|
else
|
48
73
|
raise Errors::InvalidCost.new("cost must be numeric and > 0")
|
49
74
|
end
|
@@ -79,6 +104,11 @@ module BCrypt
|
|
79
104
|
return i if end_time * 1_000 > upper_time_limit_in_ms
|
80
105
|
end
|
81
106
|
end
|
107
|
+
|
108
|
+
# Autodetects the cost from the salt string.
|
109
|
+
def self.autodetect_cost(salt)
|
110
|
+
salt[4..5].to_i
|
111
|
+
end
|
82
112
|
end
|
83
113
|
|
84
114
|
# A password management class which allows you to safely store users' passwords and compare them.
|
@@ -123,7 +153,7 @@ module BCrypt
|
|
123
153
|
#
|
124
154
|
# @password = BCrypt::Password.create("my secret", :cost => 13)
|
125
155
|
def create(secret, options = { :cost => BCrypt::Engine::DEFAULT_COST })
|
126
|
-
Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(options[:cost])))
|
156
|
+
Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(options[:cost]), options[:cost]))
|
127
157
|
end
|
128
158
|
end
|
129
159
|
|
data/spec/bcrypt/engine_spec.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
|
2
2
|
|
3
3
|
context "The BCrypt engine" do
|
4
4
|
specify "should calculate the optimal cost factor to fit in a specific time" do
|
5
5
|
first = BCrypt::Engine.calibrate(100)
|
6
|
-
second = BCrypt::Engine.calibrate(
|
7
|
-
second.should >
|
6
|
+
second = BCrypt::Engine.calibrate(400)
|
7
|
+
second.should > first
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
@@ -27,9 +27,23 @@ context "Generating BCrypt salts" do
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
context "Autodetecting of salt cost" do
|
31
|
+
|
32
|
+
specify "should work" do
|
33
|
+
BCrypt::Engine.autodetect_cost("$2a$08$hRx2IVeHNsTSYYtUWn61Ou").should == 8
|
34
|
+
BCrypt::Engine.autodetect_cost("$2a$05$XKd1bMnLgUnc87qvbAaCUu").should == 5
|
35
|
+
BCrypt::Engine.autodetect_cost("$2a$13$Lni.CZ6z5A7344POTFBBV.").should == 13
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
30
40
|
context "Generating BCrypt hashes" do
|
31
41
|
|
32
|
-
|
42
|
+
class MyInvalidSecret
|
43
|
+
undef to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
before :each do
|
33
47
|
@salt = BCrypt::Engine.generate_salt(4)
|
34
48
|
@password = "woo"
|
35
49
|
end
|
@@ -43,10 +57,15 @@ context "Generating BCrypt hashes" do
|
|
43
57
|
end
|
44
58
|
|
45
59
|
specify "should raise an InvalidSecret error if the secret is invalid" do
|
60
|
+
lambda { BCrypt::Engine.hash_secret(MyInvalidSecret.new, @salt) }.should raise_error(BCrypt::Errors::InvalidSecret)
|
46
61
|
lambda { BCrypt::Engine.hash_secret(nil, @salt) }.should_not raise_error(BCrypt::Errors::InvalidSecret)
|
47
62
|
lambda { BCrypt::Engine.hash_secret(false, @salt) }.should_not raise_error(BCrypt::Errors::InvalidSecret)
|
48
63
|
end
|
49
64
|
|
65
|
+
specify "should call #to_s on the secret and use the return value as the actual secret data" do
|
66
|
+
BCrypt::Engine.hash_secret(false, @salt).should == BCrypt::Engine.hash_secret("false", @salt)
|
67
|
+
end
|
68
|
+
|
50
69
|
specify "should be interoperable with other implementations" do
|
51
70
|
# test vectors from the OpenWall implementation <http://www.openwall.com/crypt/>
|
52
71
|
test_vectors = [
|