bcrypt-ruby 3.0.1-x86-mingw32 → 3.1.0-x86-mingw32
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.
- data/.gitignore +1 -0
- data/.travis.yml +11 -0
- data/CHANGELOG +7 -2
- data/Gemfile +1 -1
- data/Gemfile.lock +19 -13
- data/README.md +21 -1
- data/Rakefile +4 -4
- data/bcrypt-ruby.gemspec +3 -1
- data/ext/jruby/bcrypt_jruby/BCrypt.java +4 -4
- data/ext/mri/bcrypt_ext.c +0 -25
- data/ext/mri/extconf.rb +1 -23
- data/ext/mri/wrapper.c +4 -0
- data/lib/bcrypt.rb +8 -189
- data/lib/bcrypt/engine.rb +120 -0
- data/lib/bcrypt/error.rb +22 -0
- data/lib/bcrypt/password.rb +87 -0
- data/spec/bcrypt/error_spec.rb +37 -0
- data/spec/bcrypt/password_spec.rb +41 -0
- metadata +80 -63
- data/lib/bcrypt_engine.rb +0 -34
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/CHANGELOG
CHANGED
@@ -39,9 +39,14 @@
|
|
39
39
|
2.1.2 Sep 16 2009
|
40
40
|
- Fixed support for Solaris, OpenSolaris.
|
41
41
|
|
42
|
-
3.0.0
|
42
|
+
3.0.0 Aug 24 2011
|
43
43
|
- Bcrypt C implementation replaced with a public domain implementation.
|
44
44
|
- License changed to MIT
|
45
45
|
|
46
|
-
3.0.1
|
46
|
+
3.0.1 Sep 12 2011
|
47
47
|
- create raises an exception if the cost is higher than 31. GH #27
|
48
|
+
|
49
|
+
3.1.0 May 07 2013
|
50
|
+
- Add BCrypt::Password.valid_hash?(str) to check if a string is a valid bcrypt password hash
|
51
|
+
- BCrypt::Password cost should be set to DEFAULT_COST if nil
|
52
|
+
- Add BCrypt::Engine.cost attribute for getting/setting a default cost externally
|
data/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
gemspec
|
data/Gemfile.lock
CHANGED
@@ -1,29 +1,35 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
bcrypt-ruby (3.
|
4
|
+
bcrypt-ruby (3.1.0)
|
5
5
|
|
6
6
|
GEM
|
7
|
-
remote:
|
7
|
+
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
diff-lcs (1.
|
10
|
-
|
11
|
-
|
9
|
+
diff-lcs (1.2.4)
|
10
|
+
json (1.7.3)
|
11
|
+
json (1.7.3-java)
|
12
|
+
rake (10.0.4)
|
13
|
+
rake-compiler (0.8.3)
|
12
14
|
rake
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
rspec-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
rspec-
|
15
|
+
rdoc (3.12)
|
16
|
+
json (~> 1.4)
|
17
|
+
rspec (2.13.0)
|
18
|
+
rspec-core (~> 2.13.0)
|
19
|
+
rspec-expectations (~> 2.13.0)
|
20
|
+
rspec-mocks (~> 2.13.0)
|
21
|
+
rspec-core (2.13.1)
|
22
|
+
rspec-expectations (2.13.0)
|
23
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
24
|
+
rspec-mocks (2.13.1)
|
21
25
|
|
22
26
|
PLATFORMS
|
23
27
|
java
|
24
28
|
ruby
|
29
|
+
x86-mingw32
|
25
30
|
|
26
31
|
DEPENDENCIES
|
27
32
|
bcrypt-ruby!
|
28
33
|
rake-compiler
|
34
|
+
rdoc
|
29
35
|
rspec
|
data/README.md
CHANGED
@@ -5,6 +5,8 @@ An easy way to keep your users' passwords secure.
|
|
5
5
|
* http://bcrypt-ruby.rubyforge.org/
|
6
6
|
* http://github.com/codahale/bcrypt-ruby/tree/master
|
7
7
|
|
8
|
+
[](https://travis-ci.org/codahale/bcrypt-ruby)
|
9
|
+
|
8
10
|
## Why you should use `bcrypt()`
|
9
11
|
|
10
12
|
If you store user passwords in the clear, then an attacker who steals a copy of your database has a giant list of emails
|
@@ -24,7 +26,7 @@ re-hash those passwords. This vulernability only affected the JRuby gem.
|
|
24
26
|
|
25
27
|
## How to install bcrypt
|
26
28
|
|
27
|
-
|
29
|
+
gem install bcrypt-ruby
|
28
30
|
|
29
31
|
The bcrypt-ruby gem is available on the following ruby platforms:
|
30
32
|
|
@@ -34,6 +36,10 @@ The bcrypt-ruby gem is available on the following ruby platforms:
|
|
34
36
|
|
35
37
|
## How to use `bcrypt()` in your Rails application
|
36
38
|
|
39
|
+
*Note*: Rails versions >= 3 ship with `ActiveModel::SecurePassword` which uses bcrypt-ruby.
|
40
|
+
`has_secure_password` [docs](http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password)
|
41
|
+
implements a similar authentication strategy to the code below.
|
42
|
+
|
37
43
|
### The _User_ model
|
38
44
|
|
39
45
|
require 'bcrypt'
|
@@ -165,6 +171,20 @@ The default cost factor used by bcrypt-ruby is 10, which is fine for session-bas
|
|
165
171
|
stateless authentication architecture (e.g., HTTP Basic Auth), you will want to lower the cost factor to reduce your
|
166
172
|
server load and keep your request times down. This will lower the security provided you, but there are few alternatives.
|
167
173
|
|
174
|
+
To change the default cost factor used by bcrypt-ruby, use `BCrypt::Engine.cost = new_value`:
|
175
|
+
|
176
|
+
BCrypt::Password.create('secret').cost
|
177
|
+
#=> 10, the default provided by bcrypt-ruby
|
178
|
+
|
179
|
+
# set a new default cost
|
180
|
+
BCrypt::Engine.cost = 8
|
181
|
+
BCrypt::Password.create('secret').cost
|
182
|
+
#=> 8
|
183
|
+
|
184
|
+
The default cost can be overridden as needed by passing an options hash with a different cost:
|
185
|
+
|
186
|
+
BCrypt::Password.create('secret', :cost => 6).cost #=> 6
|
187
|
+
|
168
188
|
## More Information
|
169
189
|
|
170
190
|
`bcrypt()` is currently used as the default password storage hash in OpenBSD, widely regarded as the most secure operating
|
data/Rakefile
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'rspec/core/rake_task'
|
2
|
-
require '
|
2
|
+
require 'rubygems/package_task'
|
3
3
|
require 'rake/extensiontask'
|
4
4
|
require 'rake/javaextensiontask'
|
5
5
|
require 'rake/contrib/rubyforgepublisher'
|
6
6
|
require 'rake/clean'
|
7
|
-
require '
|
7
|
+
require 'rdoc/task'
|
8
8
|
require 'benchmark'
|
9
9
|
|
10
10
|
CLEAN.include(
|
@@ -37,14 +37,14 @@ RSpec::Core::RakeTask.new(:rcov) do |t|
|
|
37
37
|
end
|
38
38
|
|
39
39
|
desc 'Generate RDoc'
|
40
|
-
|
40
|
+
RDoc::Task.new do |rdoc|
|
41
41
|
rdoc.rdoc_dir = 'doc/rdoc'
|
42
42
|
rdoc.options += GEMSPEC.rdoc_options
|
43
43
|
rdoc.template = ENV['TEMPLATE'] if ENV['TEMPLATE']
|
44
44
|
rdoc.rdoc_files.include(*GEMSPEC.extra_rdoc_files)
|
45
45
|
end
|
46
46
|
|
47
|
-
|
47
|
+
Gem::PackageTask.new(GEMSPEC) do |pkg|
|
48
48
|
pkg.need_zip = true
|
49
49
|
pkg.need_tar = true
|
50
50
|
end
|
data/bcrypt-ruby.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'bcrypt-ruby'
|
3
|
-
s.version = '3.0
|
3
|
+
s.version = '3.1.0'
|
4
4
|
|
5
5
|
s.summary = "OpenBSD's bcrypt() password hashing algorithm."
|
6
6
|
s.description = <<-EOF
|
@@ -14,6 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
s.add_development_dependency 'rake-compiler'
|
16
16
|
s.add_development_dependency 'rspec'
|
17
|
+
s.add_development_dependency 'rdoc'
|
17
18
|
|
18
19
|
s.has_rdoc = true
|
19
20
|
s.rdoc_options += ['--title', 'bcrypt-ruby', '--line-numbers', '--inline-source', '--main', 'README.md']
|
@@ -25,4 +26,5 @@ Gem::Specification.new do |s|
|
|
25
26
|
s.email = "coda.hale@gmail.com"
|
26
27
|
s.homepage = "http://bcrypt-ruby.rubyforge.org"
|
27
28
|
s.rubyforge_project = "bcrypt-ruby"
|
29
|
+
s.license = "MIT"
|
28
30
|
end
|
@@ -386,7 +386,7 @@ public class BCrypt {
|
|
386
386
|
private static String encode_base64(byte d[], int len)
|
387
387
|
throws IllegalArgumentException {
|
388
388
|
int off = 0;
|
389
|
-
|
389
|
+
StringBuilder rs = new StringBuilder();
|
390
390
|
int c1, c2;
|
391
391
|
|
392
392
|
if (len <= 0 || len > d.length)
|
@@ -439,7 +439,7 @@ public class BCrypt {
|
|
439
439
|
*/
|
440
440
|
private static byte[] decode_base64(String s, int maxolen)
|
441
441
|
throws IllegalArgumentException {
|
442
|
-
|
442
|
+
StringBuilder rs = new StringBuilder();
|
443
443
|
int off = 0, slen = s.length(), olen = 0;
|
444
444
|
byte ret[];
|
445
445
|
byte c1, c2, c3, c4, o;
|
@@ -651,7 +651,7 @@ public class BCrypt {
|
|
651
651
|
byte passwordb[], saltb[], hashed[];
|
652
652
|
char minor = (char)0;
|
653
653
|
int rounds, off = 0;
|
654
|
-
|
654
|
+
StringBuilder rs = new StringBuilder();
|
655
655
|
|
656
656
|
if (salt.charAt(0) != '$' || salt.charAt(1) != '2')
|
657
657
|
throw new IllegalArgumentException ("Invalid salt version");
|
@@ -704,7 +704,7 @@ public class BCrypt {
|
|
704
704
|
* @return an encoded salt value
|
705
705
|
*/
|
706
706
|
public static String gensalt(int log_rounds, SecureRandom random) {
|
707
|
-
|
707
|
+
StringBuilder rs = new StringBuilder();
|
708
708
|
byte rnd[] = new byte[BCRYPT_SALT_LEN];
|
709
709
|
|
710
710
|
random.nextBytes(rnd);
|
data/ext/mri/bcrypt_ext.c
CHANGED
@@ -4,31 +4,6 @@
|
|
4
4
|
static VALUE mBCrypt;
|
5
5
|
static VALUE cBCryptEngine;
|
6
6
|
|
7
|
-
#ifdef RUBY_VM
|
8
|
-
# define RUBY_1_9
|
9
|
-
#endif
|
10
|
-
|
11
|
-
#ifdef RUBY_1_9
|
12
|
-
|
13
|
-
/* When on Ruby 1.9+, we will want to unlock the GIL while performing
|
14
|
-
* expensive calculations, for greater concurrency. Do not do this for
|
15
|
-
* cheap calculations because locking/unlocking the GIL incurs some overhead as well.
|
16
|
-
*/
|
17
|
-
#define GIL_UNLOCK_COST_THRESHOLD 9
|
18
|
-
|
19
|
-
typedef struct {
|
20
|
-
char *output;
|
21
|
-
const char *key;
|
22
|
-
const char *salt;
|
23
|
-
} BCryptArguments;
|
24
|
-
|
25
|
-
static VALUE bcrypt_wrapper(void *_args) {
|
26
|
-
BCryptArguments *args = (BCryptArguments *)_args;
|
27
|
-
return (VALUE)ruby_bcrypt(args->output, args->key, args->salt);
|
28
|
-
}
|
29
|
-
|
30
|
-
#endif /* RUBY_1_9 */
|
31
|
-
|
32
7
|
/* Given a logarithmic cost parameter, generates a salt for use with +bc_crypt+.
|
33
8
|
*/
|
34
9
|
static VALUE bc_salt(VALUE self, VALUE prefix, VALUE count, VALUE input) {
|
data/ext/mri/extconf.rb
CHANGED
@@ -9,31 +9,9 @@ if RUBY_PLATFORM == "java"
|
|
9
9
|
f.puts "\t@true"
|
10
10
|
end
|
11
11
|
exit 0
|
12
|
-
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == "maglev"
|
13
|
-
# Maglev doesn't support C extensions, fall back to compiling an FFI usable
|
14
|
-
# library
|
15
|
-
File.open('Makefile', 'w') do |f|
|
16
|
-
f.puts <<-MAKEFILE
|
17
|
-
CFLAGS = -fPIC
|
18
|
-
OBJS = bcrypt.o blowfish.o
|
19
|
-
DLIB = bcrypt_ext.so
|
20
|
-
OS ?= $(strip $(shell uname -s | tr '[:upper:]' '[:lower:]'))
|
21
|
-
ifeq ($(OS),darwin)
|
22
|
-
DLIB = bcrypt_ext.dylib
|
23
|
-
CFLAGS += -dynamiclib
|
24
|
-
endif
|
25
|
-
|
26
|
-
all: $(OBJS)
|
27
|
-
cc -shared -o $(DLIB) $(OBJS)
|
28
|
-
install:
|
29
|
-
install $(DLIB) "../../lib/"
|
30
|
-
clean:
|
31
|
-
$(RM) $(OBJS) bcrypt_ext.so
|
32
|
-
MAKEFILE
|
33
|
-
end
|
34
|
-
exit 0
|
35
12
|
else
|
36
13
|
require "mkmf"
|
14
|
+
have_header('ruby/util.h')
|
37
15
|
dir_config("bcrypt_ext")
|
38
16
|
create_makefile("bcrypt_ext")
|
39
17
|
end
|
data/ext/mri/wrapper.c
CHANGED
data/lib/bcrypt.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
# A
|
1
|
+
# A Ruby library implementing OpenBSD's bcrypt()/crypt_blowfish algorithm for
|
2
|
+
# hashing passwords.
|
3
|
+
module BCrypt
|
4
|
+
end
|
2
5
|
|
3
6
|
if RUBY_PLATFORM == "java"
|
4
7
|
require 'java'
|
@@ -6,191 +9,7 @@ else
|
|
6
9
|
require "openssl"
|
7
10
|
end
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
# A Ruby library implementing OpenBSD's bcrypt()/crypt_blowfish algorithm for
|
16
|
-
# hashing passwords.
|
17
|
-
module BCrypt
|
18
|
-
module Errors
|
19
|
-
class InvalidSalt < StandardError; end # The salt parameter provided to bcrypt() is invalid.
|
20
|
-
class InvalidHash < StandardError; end # The hash parameter provided to bcrypt() is invalid.
|
21
|
-
class InvalidCost < StandardError; end # The cost parameter provided to bcrypt() is invalid.
|
22
|
-
class InvalidSecret < StandardError; end # The secret parameter provided to bcrypt() is invalid.
|
23
|
-
end
|
24
|
-
|
25
|
-
# A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
|
26
|
-
class Engine
|
27
|
-
# The default computational expense parameter.
|
28
|
-
DEFAULT_COST = 10
|
29
|
-
# The minimum cost supported by the algorithm.
|
30
|
-
MIN_COST = 4
|
31
|
-
# Maximum possible size of bcrypt() salts.
|
32
|
-
MAX_SALT_LENGTH = 16
|
33
|
-
|
34
|
-
if RUBY_PLATFORM != "java"
|
35
|
-
# C-level routines which, if they don't get the right input, will crash the
|
36
|
-
# hell out of the Ruby process.
|
37
|
-
private_class_method :__bc_salt
|
38
|
-
private_class_method :__bc_crypt
|
39
|
-
end
|
40
|
-
|
41
|
-
# Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates
|
42
|
-
# a bcrypt() password hash.
|
43
|
-
def self.hash_secret(secret, salt, cost = nil)
|
44
|
-
if valid_secret?(secret)
|
45
|
-
if valid_salt?(salt)
|
46
|
-
if cost.nil?
|
47
|
-
cost = autodetect_cost(salt)
|
48
|
-
end
|
49
|
-
|
50
|
-
if RUBY_PLATFORM == "java"
|
51
|
-
Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s, salt.to_s)
|
52
|
-
else
|
53
|
-
__bc_crypt(secret.to_s, salt)
|
54
|
-
end
|
55
|
-
else
|
56
|
-
raise Errors::InvalidSalt.new("invalid salt")
|
57
|
-
end
|
58
|
-
else
|
59
|
-
raise Errors::InvalidSecret.new("invalid secret")
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Generates a random salt with a given computational cost.
|
64
|
-
def self.generate_salt(cost = DEFAULT_COST)
|
65
|
-
cost = cost.to_i
|
66
|
-
if cost > 0
|
67
|
-
if cost < MIN_COST
|
68
|
-
cost = MIN_COST
|
69
|
-
end
|
70
|
-
if RUBY_PLATFORM == "java"
|
71
|
-
Java.bcrypt_jruby.BCrypt.gensalt(cost)
|
72
|
-
else
|
73
|
-
prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
|
74
|
-
__bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
|
75
|
-
end
|
76
|
-
else
|
77
|
-
raise Errors::InvalidCost.new("cost must be numeric and > 0")
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# Returns true if +salt+ is a valid bcrypt() salt, false if not.
|
82
|
-
def self.valid_salt?(salt)
|
83
|
-
!!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/)
|
84
|
-
end
|
85
|
-
|
86
|
-
# Returns true if +secret+ is a valid bcrypt() secret, false if not.
|
87
|
-
def self.valid_secret?(secret)
|
88
|
-
secret.respond_to?(:to_s)
|
89
|
-
end
|
90
|
-
|
91
|
-
# Returns the cost factor which will result in computation times less than +upper_time_limit_in_ms+.
|
92
|
-
#
|
93
|
-
# Example:
|
94
|
-
#
|
95
|
-
# BCrypt.calibrate(200) #=> 10
|
96
|
-
# BCrypt.calibrate(1000) #=> 12
|
97
|
-
#
|
98
|
-
# # should take less than 200ms
|
99
|
-
# BCrypt::Password.create("woo", :cost => 10)
|
100
|
-
#
|
101
|
-
# # should take less than 1000ms
|
102
|
-
# BCrypt::Password.create("woo", :cost => 12)
|
103
|
-
def self.calibrate(upper_time_limit_in_ms)
|
104
|
-
40.times do |i|
|
105
|
-
start_time = Time.now
|
106
|
-
Password.create("testing testing", :cost => i+1)
|
107
|
-
end_time = Time.now - start_time
|
108
|
-
return i if end_time * 1_000 > upper_time_limit_in_ms
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# Autodetects the cost from the salt string.
|
113
|
-
def self.autodetect_cost(salt)
|
114
|
-
salt[4..5].to_i
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
# A password management class which allows you to safely store users' passwords and compare them.
|
119
|
-
#
|
120
|
-
# Example usage:
|
121
|
-
#
|
122
|
-
# include BCrypt
|
123
|
-
#
|
124
|
-
# # hash a user's password
|
125
|
-
# @password = Password.create("my grand secret")
|
126
|
-
# @password #=> "$2a$10$GtKs1Kbsig8ULHZzO1h2TetZfhO4Fmlxphp8bVKnUlZCBYYClPohG"
|
127
|
-
#
|
128
|
-
# # store it safely
|
129
|
-
# @user.update_attribute(:password, @password)
|
130
|
-
#
|
131
|
-
# # read it back
|
132
|
-
# @user.reload!
|
133
|
-
# @db_password = Password.new(@user.password)
|
134
|
-
#
|
135
|
-
# # compare it after retrieval
|
136
|
-
# @db_password == "my grand secret" #=> true
|
137
|
-
# @db_password == "a paltry guess" #=> false
|
138
|
-
#
|
139
|
-
class Password < String
|
140
|
-
# The hash portion of the stored password hash.
|
141
|
-
attr_reader :checksum
|
142
|
-
# The salt of the store password hash (including version and cost).
|
143
|
-
attr_reader :salt
|
144
|
-
# The version of the bcrypt() algorithm used to create the hash.
|
145
|
-
attr_reader :version
|
146
|
-
# The cost factor used to create the hash.
|
147
|
-
attr_reader :cost
|
148
|
-
|
149
|
-
class << self
|
150
|
-
# Hashes a secret, returning a BCrypt::Password instance. Takes an optional <tt>:cost</tt> option, which is a
|
151
|
-
# logarithmic variable which determines how computational expensive the hash is to calculate (a <tt>:cost</tt> of
|
152
|
-
# 4 is twice as much work as a <tt>:cost</tt> of 3). The higher the <tt>:cost</tt> the harder it becomes for
|
153
|
-
# attackers to try to guess passwords (even if a copy of your database is stolen), but the slower it is to check
|
154
|
-
# users' passwords.
|
155
|
-
#
|
156
|
-
# Example:
|
157
|
-
#
|
158
|
-
# @password = BCrypt::Password.create("my secret", :cost => 13)
|
159
|
-
def create(secret, options = { :cost => BCrypt::Engine::DEFAULT_COST })
|
160
|
-
raise ArgumentError if options[:cost] > 31
|
161
|
-
Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(options[:cost]), options[:cost]))
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
# Initializes a BCrypt::Password instance with the data from a stored hash.
|
166
|
-
def initialize(raw_hash)
|
167
|
-
if valid_hash?(raw_hash)
|
168
|
-
self.replace(raw_hash)
|
169
|
-
@version, @cost, @salt, @checksum = split_hash(self)
|
170
|
-
else
|
171
|
-
raise Errors::InvalidHash.new("invalid hash")
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
# Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
|
176
|
-
def ==(secret)
|
177
|
-
super(BCrypt::Engine.hash_secret(secret, @salt))
|
178
|
-
end
|
179
|
-
alias_method :is_password?, :==
|
180
|
-
|
181
|
-
private
|
182
|
-
# Returns true if +h+ is a valid hash.
|
183
|
-
def valid_hash?(h)
|
184
|
-
h =~ /^\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}$/
|
185
|
-
end
|
186
|
-
|
187
|
-
# call-seq:
|
188
|
-
# split_hash(raw_hash) -> version, cost, salt, hash
|
189
|
-
#
|
190
|
-
# Splits +h+ into version, cost, salt, and hash and returns them in that order.
|
191
|
-
def split_hash(h)
|
192
|
-
_, v, c, mash = h.split('$')
|
193
|
-
return v, c.to_i, h[0, 29].to_str, mash[-31, 31].to_str
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
12
|
+
require 'bcrypt_ext'
|
13
|
+
require 'bcrypt/error'
|
14
|
+
require 'bcrypt/engine'
|
15
|
+
require 'bcrypt/password'
|
@@ -0,0 +1,120 @@
|
|
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, cost = nil)
|
46
|
+
if valid_secret?(secret)
|
47
|
+
if valid_salt?(salt)
|
48
|
+
if cost.nil?
|
49
|
+
cost = autodetect_cost(salt)
|
50
|
+
end
|
51
|
+
|
52
|
+
if RUBY_PLATFORM == "java"
|
53
|
+
Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s, salt.to_s)
|
54
|
+
else
|
55
|
+
__bc_crypt(secret.to_s, salt)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
raise Errors::InvalidSalt.new("invalid salt")
|
59
|
+
end
|
60
|
+
else
|
61
|
+
raise Errors::InvalidSecret.new("invalid secret")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Generates a random salt with a given computational cost.
|
66
|
+
def self.generate_salt(cost = self.cost)
|
67
|
+
cost = cost.to_i
|
68
|
+
if cost > 0
|
69
|
+
if cost < MIN_COST
|
70
|
+
cost = MIN_COST
|
71
|
+
end
|
72
|
+
if RUBY_PLATFORM == "java"
|
73
|
+
Java.bcrypt_jruby.BCrypt.gensalt(cost)
|
74
|
+
else
|
75
|
+
prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
|
76
|
+
__bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
|
77
|
+
end
|
78
|
+
else
|
79
|
+
raise Errors::InvalidCost.new("cost must be numeric and > 0")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns true if +salt+ is a valid bcrypt() salt, false if not.
|
84
|
+
def self.valid_salt?(salt)
|
85
|
+
!!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns true if +secret+ is a valid bcrypt() secret, false if not.
|
89
|
+
def self.valid_secret?(secret)
|
90
|
+
secret.respond_to?(:to_s)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns the cost factor which will result in computation times less than +upper_time_limit_in_ms+.
|
94
|
+
#
|
95
|
+
# Example:
|
96
|
+
#
|
97
|
+
# BCrypt::Engine.calibrate(200) #=> 10
|
98
|
+
# BCrypt::Engine.calibrate(1000) #=> 12
|
99
|
+
#
|
100
|
+
# # should take less than 200ms
|
101
|
+
# BCrypt::Password.create("woo", :cost => 10)
|
102
|
+
#
|
103
|
+
# # should take less than 1000ms
|
104
|
+
# BCrypt::Password.create("woo", :cost => 12)
|
105
|
+
def self.calibrate(upper_time_limit_in_ms)
|
106
|
+
40.times do |i|
|
107
|
+
start_time = Time.now
|
108
|
+
Password.create("testing testing", :cost => i+1)
|
109
|
+
end_time = Time.now - start_time
|
110
|
+
return i if end_time * 1_000 > upper_time_limit_in_ms
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Autodetects the cost from the salt string.
|
115
|
+
def self.autodetect_cost(salt)
|
116
|
+
salt[4..5].to_i
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
data/lib/bcrypt/error.rb
ADDED
@@ -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), 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
|
@@ -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
|
+
described_class.should < 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
|
+
described_class.should < 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
|
@@ -40,6 +40,38 @@ describe "Reading a hashed password" do
|
|
40
40
|
}.should raise_error(ArgumentError)
|
41
41
|
end
|
42
42
|
|
43
|
+
specify "the cost should be set to the default if nil" do
|
44
|
+
BCrypt::Password.create("hello", :cost => nil).cost.should equal(BCrypt::Engine::DEFAULT_COST)
|
45
|
+
end
|
46
|
+
|
47
|
+
specify "the cost should be set to the default if empty hash" do
|
48
|
+
BCrypt::Password.create("hello", {}).cost.should equal(BCrypt::Engine::DEFAULT_COST)
|
49
|
+
end
|
50
|
+
|
51
|
+
specify "the cost should be set to the passed value if provided" do
|
52
|
+
BCrypt::Password.create("hello", :cost => 5).cost.should 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
|
+
BCrypt::Password.create("hello").cost.should 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
|
+
BCrypt::Password.create("hello").cost.should 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
|
+
|
43
75
|
specify "should read the version, cost, salt, and hash" do
|
44
76
|
password = BCrypt::Password.new(@hash)
|
45
77
|
password.version.should eql("2a")
|
@@ -80,3 +112,12 @@ describe "Validating a generated salt" do
|
|
80
112
|
BCrypt::Engine.valid_salt?(BCrypt::Engine.generate_salt).should eq(true)
|
81
113
|
end
|
82
114
|
end
|
115
|
+
|
116
|
+
describe "Validating a password hash" do
|
117
|
+
specify "should not accept an invalid password" do
|
118
|
+
BCrypt::Password.valid_hash?("i_am_so_not_valid").should be_false
|
119
|
+
end
|
120
|
+
specify "should accept a valid password" do
|
121
|
+
BCrypt::Password.valid_hash?(BCrypt::Password.create "i_am_so_valid").should be_true
|
122
|
+
end
|
123
|
+
end
|
metadata
CHANGED
@@ -1,65 +1,82 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: bcrypt-ruby
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 3.1.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 3
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 3.0.1
|
11
6
|
platform: x86-mingw32
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Coda Hale
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2013-07-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: rake-compiler
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
version: "0"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
32
22
|
type: :development
|
33
|
-
|
34
|
-
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
35
31
|
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
36
39
|
prerelease: false
|
37
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rdoc
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
38
49
|
none: false
|
39
|
-
requirements:
|
40
|
-
- -
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
|
43
|
-
segments:
|
44
|
-
- 0
|
45
|
-
version: "0"
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
46
54
|
type: :development
|
47
|
-
|
48
|
-
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: ! " bcrypt() is a sophisticated and secure hash algorithm designed
|
63
|
+
by The OpenBSD project\n for hashing passwords. bcrypt-ruby provides a simple,
|
64
|
+
humane wrapper for safely handling\n passwords.\n"
|
49
65
|
email: coda.hale@gmail.com
|
50
66
|
executables: []
|
51
|
-
|
52
67
|
extensions: []
|
53
|
-
|
54
|
-
extra_rdoc_files:
|
68
|
+
extra_rdoc_files:
|
55
69
|
- README.md
|
56
70
|
- COPYING
|
57
71
|
- CHANGELOG
|
72
|
+
- lib/bcrypt/engine.rb
|
73
|
+
- lib/bcrypt/error.rb
|
74
|
+
- lib/bcrypt/password.rb
|
58
75
|
- lib/bcrypt.rb
|
59
|
-
|
60
|
-
files:
|
76
|
+
files:
|
61
77
|
- .gitignore
|
62
78
|
- .rspec
|
79
|
+
- .travis.yml
|
63
80
|
- CHANGELOG
|
64
81
|
- COPYING
|
65
82
|
- Gemfile
|
@@ -77,51 +94,51 @@ files:
|
|
77
94
|
- ext/mri/ow-crypt.h
|
78
95
|
- ext/mri/wrapper.c
|
79
96
|
- lib/bcrypt.rb
|
80
|
-
- lib/
|
97
|
+
- lib/bcrypt/engine.rb
|
98
|
+
- lib/bcrypt/error.rb
|
99
|
+
- lib/bcrypt/password.rb
|
81
100
|
- spec/TestBCrypt.java
|
82
101
|
- spec/bcrypt/engine_spec.rb
|
102
|
+
- spec/bcrypt/error_spec.rb
|
83
103
|
- spec/bcrypt/password_spec.rb
|
84
104
|
- spec/spec_helper.rb
|
85
|
-
- lib/
|
86
|
-
- lib/1.9/bcrypt_ext.so
|
105
|
+
- lib/bcrypt_ext.so
|
87
106
|
- lib/bcrypt_ext.rb
|
88
107
|
homepage: http://bcrypt-ruby.rubyforge.org
|
89
|
-
licenses:
|
90
|
-
|
108
|
+
licenses:
|
109
|
+
- MIT
|
91
110
|
post_install_message:
|
92
|
-
rdoc_options:
|
111
|
+
rdoc_options:
|
93
112
|
- --title
|
94
113
|
- bcrypt-ruby
|
95
114
|
- --line-numbers
|
96
115
|
- --inline-source
|
97
116
|
- --main
|
98
117
|
- README.md
|
99
|
-
require_paths:
|
118
|
+
require_paths:
|
100
119
|
- lib
|
101
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
121
|
none: false
|
103
|
-
requirements:
|
104
|
-
- -
|
105
|
-
- !ruby/object:Gem::Version
|
106
|
-
|
107
|
-
segments:
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
segments:
|
108
127
|
- 0
|
109
|
-
|
110
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
hash: -315332781
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
130
|
none: false
|
112
|
-
requirements:
|
113
|
-
- -
|
114
|
-
- !ruby/object:Gem::Version
|
115
|
-
|
116
|
-
segments:
|
131
|
+
requirements:
|
132
|
+
- - ! '>='
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
segments:
|
117
136
|
- 0
|
118
|
-
|
137
|
+
hash: -315332781
|
119
138
|
requirements: []
|
120
|
-
|
121
139
|
rubyforge_project: bcrypt-ruby
|
122
|
-
rubygems_version: 1.8.
|
140
|
+
rubygems_version: 1.8.25
|
123
141
|
signing_key:
|
124
142
|
specification_version: 3
|
125
143
|
summary: OpenBSD's bcrypt() password hashing algorithm.
|
126
144
|
test_files: []
|
127
|
-
|
data/lib/bcrypt_engine.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'ffi'
|
2
|
-
|
3
|
-
module BCrypt
|
4
|
-
class Engine
|
5
|
-
extend FFI::Library
|
6
|
-
|
7
|
-
BCRYPT_MAXSALT = 16
|
8
|
-
BCRYPT_SALT_OUTPUT_SIZE = 7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1
|
9
|
-
BCRYPT_OUTPUT_SIZE = 128
|
10
|
-
|
11
|
-
ffi_lib File.expand_path("../bcrypt_ext", __FILE__)
|
12
|
-
|
13
|
-
attach_function :ruby_bcrypt, [:buffer_out, :string, :string], :string
|
14
|
-
attach_function :ruby_bcrypt_gensalt, [:buffer_out, :uint8, :pointer], :string
|
15
|
-
|
16
|
-
def self.__bc_salt(cost, seed)
|
17
|
-
buffer_out = FFI::Buffer.alloc_out(BCRYPT_SALT_OUTPUT_SIZE, 1)
|
18
|
-
seed_ptr = FFI::MemoryPointer.new(:uint8, BCRYPT_MAXSALT)
|
19
|
-
seed.bytes.to_a.each_with_index { |b, i| seed_ptr.int8_put(i, b) }
|
20
|
-
out = ruby_bcrypt_gensalt(buffer_out, cost, seed_ptr)
|
21
|
-
seed_ptr.free
|
22
|
-
buffer_out.free
|
23
|
-
out || ""
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.__bc_crypt(key, salt, cost)
|
27
|
-
buffer_out = FFI::Buffer.alloc_out(BCRYPT_OUTPUT_SIZE, 1)
|
28
|
-
out = ruby_bcrypt(buffer_out, key || "", salt)
|
29
|
-
buffer_out.free
|
30
|
-
out && out.any? ? out : nil
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|