bcrypt-ruby 2.1.4-java → 3.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CHANGELOG +5 -1
- data/COPYING +23 -28
- data/Gemfile.lock +29 -0
- data/README.md +184 -0
- data/Rakefile +1 -0
- data/bcrypt-ruby.gemspec +5 -4
- data/ext/mri/bcrypt_ext.c +67 -65
- data/ext/mri/crypt.c +57 -0
- data/ext/mri/crypt.h +13 -0
- data/ext/mri/{blowfish.c → crypt_blowfish.c} +472 -321
- data/ext/mri/crypt_gensalt.c +111 -0
- data/ext/mri/extconf.rb +24 -2
- data/ext/mri/ow-crypt.h +35 -0
- data/ext/mri/wrapper.c +255 -0
- data/lib/bcrypt.rb +10 -5
- data/lib/bcrypt_engine.rb +34 -0
- data/lib/bcrypt_ext.jar +0 -0
- data/spec/bcrypt/engine_spec.rb +3 -3
- data/spec/bcrypt/password_spec.rb +11 -2
- metadata +90 -78
- data/README +0 -175
- data/ext/mri/bcrypt.c +0 -297
- data/ext/mri/bcrypt.h +0 -67
- data/ext/mri/blf.h +0 -86
data/.gitignore
CHANGED
data/CHANGELOG
CHANGED
@@ -37,4 +37,8 @@
|
|
37
37
|
- JVM 1.4/1.5 compatibility [Hongli Lai]
|
38
38
|
|
39
39
|
2.1.2 Sep 16 2009
|
40
|
-
- Fixed support for Solaris, OpenSolaris.
|
40
|
+
- Fixed support for Solaris, OpenSolaris.
|
41
|
+
|
42
|
+
3.0.0 Aug 24, 2011
|
43
|
+
- Bcrypt C implementation replaced with a public domain implementation.
|
44
|
+
- License changed to MIT
|
data/COPYING
CHANGED
@@ -1,33 +1,28 @@
|
|
1
|
-
|
1
|
+
(The MIT License)
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
Copyright 2007-2011:
|
4
|
+
|
5
|
+
* Coda Hale <coda.hale@gmail.com>
|
5
6
|
|
6
|
-
|
7
|
+
C implementation of the BCrypt algorithm by Solar Designer and placed in the
|
8
|
+
public domain.
|
9
|
+
jBCrypt is Copyright (c) 2006 Damien Miller <djm@mindrot.org>.
|
7
10
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
documentation and/or other materials provided with the distribution.
|
16
|
-
3. All advertising materials mentioning features or use of this software
|
17
|
-
must display the following acknowledgement:
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
12
|
+
a copy of this software and associated documentation files (the
|
13
|
+
'Software'), to deal in the Software without restriction, including
|
14
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
15
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
16
|
+
permit persons to whom the Software is furnished to do so, subject to
|
17
|
+
the following conditions:
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
4. The name of the author may not be used to endorse or promote products
|
22
|
-
derived from this software without specific prior written permission.
|
19
|
+
The above copyright notice and this permission notice shall be
|
20
|
+
included in all copies or substantial portions of the Software.
|
23
21
|
|
24
|
-
|
25
|
-
IMPLIED
|
26
|
-
|
27
|
-
IN NO EVENT SHALL THE
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
32
|
-
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
33
|
-
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
23
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
24
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
25
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
26
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
27
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
28
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
bcrypt-ruby (2.1.4)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.2)
|
10
|
+
rake (0.8.7)
|
11
|
+
rake-compiler (0.7.5)
|
12
|
+
rake
|
13
|
+
rspec (2.5.0)
|
14
|
+
rspec-core (~> 2.5.0)
|
15
|
+
rspec-expectations (~> 2.5.0)
|
16
|
+
rspec-mocks (~> 2.5.0)
|
17
|
+
rspec-core (2.5.1)
|
18
|
+
rspec-expectations (2.5.0)
|
19
|
+
diff-lcs (~> 1.1.2)
|
20
|
+
rspec-mocks (2.5.0)
|
21
|
+
|
22
|
+
PLATFORMS
|
23
|
+
java
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
bcrypt-ruby!
|
28
|
+
rake-compiler
|
29
|
+
rspec
|
data/README.md
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
# bcrypt-ruby
|
2
|
+
|
3
|
+
An easy way to keep your users' passwords secure.
|
4
|
+
|
5
|
+
* http://bcrypt-ruby.rubyforge.org/
|
6
|
+
* http://github.com/codahale/bcrypt-ruby/tree/master
|
7
|
+
|
8
|
+
## Why you should use `bcrypt()`
|
9
|
+
|
10
|
+
If you store user passwords in the clear, then an attacker who steals a copy of your database has a giant list of emails
|
11
|
+
and passwords. Some of your users will only have one password -- for their email account, for their banking account, for
|
12
|
+
your application. A simple hack could escalate into massive identity theft.
|
13
|
+
|
14
|
+
It's your responsibility as a web developer to make your web application secure -- blaming your users for not being
|
15
|
+
security experts is not a professional response to risk.
|
16
|
+
|
17
|
+
`bcrypt()` allows you to easily harden your application against these kinds of attacks.
|
18
|
+
|
19
|
+
*Note*: JRuby versions of bcrypt-ruby `<= 2.1.3` had a [security
|
20
|
+
vulnerability](http://www.mindrot.org/files/jBCrypt/internat.adv) that
|
21
|
+
was fixed in `>= 2.1.4`. If you used a vulnerable version to hash
|
22
|
+
passwords with international characters in them, you will need to
|
23
|
+
re-hash those passwords. This vulernability only affected the JRuby gem.
|
24
|
+
|
25
|
+
## How to install bcrypt
|
26
|
+
|
27
|
+
sudo gem install bcrypt-ruby
|
28
|
+
|
29
|
+
The bcrypt-ruby gem is available on the following ruby platforms:
|
30
|
+
|
31
|
+
* JRuby
|
32
|
+
* RubyInstaller 1.8 and 1.9 builds on win32
|
33
|
+
* Any 1.8 or 1.9 ruby on a BSD/OSX/Linux system with a compiler
|
34
|
+
|
35
|
+
## How to use `bcrypt()` in your Rails application
|
36
|
+
|
37
|
+
### The _User_ model
|
38
|
+
|
39
|
+
require 'bcrypt'
|
40
|
+
|
41
|
+
class User < ActiveRecord::Base
|
42
|
+
# users.password_hash in the database is a :string
|
43
|
+
include BCrypt
|
44
|
+
|
45
|
+
def password
|
46
|
+
@password ||= Password.new(password_hash)
|
47
|
+
end
|
48
|
+
|
49
|
+
def password=(new_password)
|
50
|
+
@password = Password.create(new_password)
|
51
|
+
self.password_hash = @password
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
### Creating an account
|
56
|
+
|
57
|
+
def create
|
58
|
+
@user = User.new(params[:user])
|
59
|
+
@user.password = params[:password]
|
60
|
+
@user.save!
|
61
|
+
end
|
62
|
+
|
63
|
+
### Authenticating a user
|
64
|
+
|
65
|
+
def login
|
66
|
+
@user = User.find_by_email(params[:email])
|
67
|
+
if @user.password == params[:password]
|
68
|
+
give_token
|
69
|
+
else
|
70
|
+
redirect_to home_url
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
### If a user forgets their password?
|
75
|
+
|
76
|
+
# assign them a random one and mail it to them, asking them to change it
|
77
|
+
def forgot_password
|
78
|
+
@user = User.find_by_email(params[:email])
|
79
|
+
random_password = Array.new(10).map { (65 + rand(58)).chr }.join
|
80
|
+
@user.password = random_password
|
81
|
+
@user.save!
|
82
|
+
Mailer.create_and_deliver_password_change(@user, random_password)
|
83
|
+
end
|
84
|
+
|
85
|
+
## How to use bcrypt-ruby in general
|
86
|
+
|
87
|
+
require 'bcrypt'
|
88
|
+
|
89
|
+
my_password = BCrypt::Password.create("my password")
|
90
|
+
#=> "$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa"
|
91
|
+
|
92
|
+
my_password.version #=> "2a"
|
93
|
+
my_password.cost #=> 10
|
94
|
+
my_password == "my password" #=> true
|
95
|
+
my_password == "not my password" #=> false
|
96
|
+
|
97
|
+
my_password = BCrypt::Password.new("$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa")
|
98
|
+
my_password == "my password" #=> true
|
99
|
+
my_password == "not my password" #=> false
|
100
|
+
|
101
|
+
Check the rdocs for more details -- BCrypt, BCrypt::Password.
|
102
|
+
|
103
|
+
## How `bcrypt()` works
|
104
|
+
|
105
|
+
`bcrypt()` is a hashing algorithm designed by Niels Provos and David Mazières of the OpenBSD Project.
|
106
|
+
|
107
|
+
### Background
|
108
|
+
|
109
|
+
Hash algorithms take a chunk of data (e.g., your user's password) and create a "digital fingerprint," or hash, of it.
|
110
|
+
Because this process is not reversible, there's no way to go from the hash back to the password.
|
111
|
+
|
112
|
+
In other words:
|
113
|
+
|
114
|
+
hash(p) #=> <unique gibberish>
|
115
|
+
|
116
|
+
You can store the hash and check it against a hash made of a potentially valid password:
|
117
|
+
|
118
|
+
<unique gibberish> =? hash(just_entered_password)
|
119
|
+
|
120
|
+
### Rainbow Tables
|
121
|
+
|
122
|
+
But even this has weaknesses -- attackers can just run lists of possible passwords through the same algorithm, store the
|
123
|
+
results in a big database, and then look up the passwords by their hash:
|
124
|
+
|
125
|
+
PrecomputedPassword.find_by_hash(<unique gibberish>).password #=> "secret1"
|
126
|
+
|
127
|
+
### Salts
|
128
|
+
|
129
|
+
The solution to this is to add a small chunk of random data -- called a salt -- to the password before it's hashed:
|
130
|
+
|
131
|
+
hash(salt + p) #=> <really unique gibberish>
|
132
|
+
|
133
|
+
The salt is then stored along with the hash in the database, and used to check potentially valid passwords:
|
134
|
+
|
135
|
+
<really unique gibberish> =? hash(salt + just_entered_password)
|
136
|
+
|
137
|
+
bcrypt-ruby automatically handles the storage and generation of these salts for you.
|
138
|
+
|
139
|
+
Adding a salt means that an attacker has to have a gigantic database for each unique salt -- for a salt made of 4
|
140
|
+
letters, that's 456,976 different databases. Pretty much no one has that much storage space, so attackers try a
|
141
|
+
different, slower method -- throw a list of potential passwords at each individual password:
|
142
|
+
|
143
|
+
hash(salt + "aadvark") =? <really unique gibberish>
|
144
|
+
hash(salt + "abacus") =? <really unique gibberish>
|
145
|
+
etc.
|
146
|
+
|
147
|
+
This is much slower than the big database approach, but most hash algorithms are pretty quick -- and therein lies the
|
148
|
+
problem. Hash algorithms aren't usually designed to be slow, they're designed to turn gigabytes of data into secure
|
149
|
+
fingerprints as quickly as possible. `bcrypt()`, though, is designed to be computationally expensive:
|
150
|
+
|
151
|
+
Ten thousand iterations:
|
152
|
+
user system total real
|
153
|
+
md5 0.070000 0.000000 0.070000 ( 0.070415)
|
154
|
+
bcrypt 22.230000 0.080000 22.310000 ( 22.493822)
|
155
|
+
|
156
|
+
If an attacker was using Ruby to check each password, they could check ~140,000 passwords a second with MD5 but only
|
157
|
+
~450 passwords a second with `bcrypt()`.
|
158
|
+
|
159
|
+
### Cost Factors
|
160
|
+
|
161
|
+
In addition, `bcrypt()` allows you to increase the amount of work required to hash a password as computers get faster. Old
|
162
|
+
passwords will still work fine, but new passwords can keep up with the times.
|
163
|
+
|
164
|
+
The default cost factor used by bcrypt-ruby is 10, which is fine for session-based authentication. If you are using a
|
165
|
+
stateless authentication architecture (e.g., HTTP Basic Auth), you will want to lower the cost factor to reduce your
|
166
|
+
server load and keep your request times down. This will lower the security provided you, but there are few alternatives.
|
167
|
+
|
168
|
+
## More Information
|
169
|
+
|
170
|
+
`bcrypt()` is currently used as the default password storage hash in OpenBSD, widely regarded as the most secure operating
|
171
|
+
system available.
|
172
|
+
|
173
|
+
For a more technical explanation of the algorithm and its design criteria, please read Niels Provos and David Mazières'
|
174
|
+
Usenix99 paper:
|
175
|
+
http://www.usenix.org/events/usenix99/provos.html
|
176
|
+
|
177
|
+
If you'd like more down-to-earth advice regarding cryptography, I suggest reading <i>Practical Cryptography</i> by Niels
|
178
|
+
Ferguson and Bruce Schneier:
|
179
|
+
http://www.schneier.com/book-practical.html
|
180
|
+
|
181
|
+
# Etc
|
182
|
+
|
183
|
+
* Author :: Coda Hale <coda.hale@gmail.com>
|
184
|
+
* Website :: http://blog.codahale.com
|
data/Rakefile
CHANGED
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
|
+
s.version = '3.0.0'
|
4
4
|
|
5
5
|
s.summary = "OpenBSD's bcrypt() password hashing algorithm."
|
6
6
|
s.description = <<-EOF
|
@@ -9,15 +9,16 @@ Gem::Specification.new do |s|
|
|
9
9
|
passwords.
|
10
10
|
EOF
|
11
11
|
|
12
|
-
s.
|
12
|
+
s.platform = 'java'
|
13
|
+
s.files = `git ls-files`.split("\n") + ['lib/bcrypt_ext.jar']
|
13
14
|
s.require_path = 'lib'
|
14
15
|
|
15
16
|
s.add_development_dependency 'rake-compiler'
|
16
17
|
s.add_development_dependency 'rspec'
|
17
18
|
|
18
19
|
s.has_rdoc = true
|
19
|
-
s.rdoc_options += ['--title', 'bcrypt-ruby', '--line-numbers', '--inline-source', '--main', 'README']
|
20
|
-
s.extra_rdoc_files += ['README', 'COPYING', 'CHANGELOG', *Dir['lib/**/*.rb']]
|
20
|
+
s.rdoc_options += ['--title', 'bcrypt-ruby', '--line-numbers', '--inline-source', '--main', 'README.md']
|
21
|
+
s.extra_rdoc_files += ['README.md', 'COPYING', 'CHANGELOG', *Dir['lib/**/*.rb']]
|
21
22
|
|
22
23
|
s.extensions = 'ext/mri/extconf.rb'
|
23
24
|
|
data/ext/mri/bcrypt_ext.c
CHANGED
@@ -1,87 +1,89 @@
|
|
1
|
-
#include
|
2
|
-
#include
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <ow-crypt.h>
|
3
3
|
|
4
4
|
static VALUE mBCrypt;
|
5
5
|
static VALUE cBCryptEngine;
|
6
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
7
|
#ifdef RUBY_VM
|
14
8
|
# define RUBY_1_9
|
15
9
|
#endif
|
16
10
|
|
17
11
|
#ifdef RUBY_1_9
|
18
12
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
+
}
|
35
29
|
|
36
30
|
#endif /* RUBY_1_9 */
|
37
31
|
|
38
32
|
/* Given a logarithmic cost parameter, generates a salt for use with +bc_crypt+.
|
39
|
-
|
40
|
-
static VALUE bc_salt(VALUE self, VALUE
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
33
|
+
*/
|
34
|
+
static VALUE bc_salt(VALUE self, VALUE prefix, VALUE count, VALUE input) {
|
35
|
+
char * salt;
|
36
|
+
VALUE str_salt;
|
37
|
+
|
38
|
+
salt = crypt_gensalt_ra(
|
39
|
+
StringValuePtr(prefix),
|
40
|
+
NUM2ULONG(count),
|
41
|
+
NIL_P(input) ? NULL : StringValuePtr(input),
|
42
|
+
NIL_P(input) ? 0 : RSTRING_LEN(input));
|
43
|
+
|
44
|
+
if(!salt) return Qnil;
|
45
|
+
|
46
|
+
str_salt = rb_str_new2(salt);
|
47
|
+
free(salt);
|
48
|
+
|
49
|
+
return str_salt;
|
46
50
|
}
|
47
51
|
|
48
52
|
/* 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
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
return rb_str_new2(output);
|
75
|
-
} else {
|
76
|
-
return Qnil;
|
77
|
-
}
|
53
|
+
*/
|
54
|
+
static VALUE bc_crypt(VALUE self, VALUE key, VALUE setting) {
|
55
|
+
char * value;
|
56
|
+
void * data;
|
57
|
+
int size;
|
58
|
+
VALUE out;
|
59
|
+
|
60
|
+
data = NULL;
|
61
|
+
size = 0xDEADBEEF;
|
62
|
+
|
63
|
+
if(NIL_P(key) || NIL_P(setting)) return Qnil;
|
64
|
+
|
65
|
+
value = crypt_ra(
|
66
|
+
NIL_P(key) ? NULL : StringValuePtr(key),
|
67
|
+
NIL_P(setting) ? NULL : StringValuePtr(setting),
|
68
|
+
&data,
|
69
|
+
&size);
|
70
|
+
|
71
|
+
if(!value) return Qnil;
|
72
|
+
|
73
|
+
out = rb_str_new(data, size - 1);
|
74
|
+
|
75
|
+
free(data);
|
76
|
+
|
77
|
+
return out;
|
78
78
|
}
|
79
79
|
|
80
80
|
/* Create the BCrypt and BCrypt::Engine modules, and populate them with methods. */
|
81
81
|
void Init_bcrypt_ext(){
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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, 3);
|
86
|
+
rb_define_singleton_method(cBCryptEngine, "__bc_crypt", bc_crypt, 2);
|
87
87
|
}
|
88
|
+
|
89
|
+
/* vim: set noet sws=4 sw=4: */
|