bcrypt-ruby 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bcrypt-ruby might be problematic. Click here for more details.
- data/CHANGELOG +8 -0
- data/Rakefile +36 -12
- data/ext/bcrypt_ext.c +4 -4
- data/lib/bcrypt.rb +60 -51
- data/spec/bcrypt/{internals_spec.rb → engine_spec.rb} +24 -10
- data/spec/bcrypt/password_spec.rb +4 -1
- metadata +7 -6
- data/spec/bcrypt/bcrypt_spec.rb +0 -9
data/CHANGELOG
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
1.0.0 Feb 27 2007
|
2
|
+
- Initial release.
|
3
|
+
|
4
|
+
2.0.0 Mar 07 2007
|
5
|
+
- Removed BCrypt::Password#exactly_equals -- use BCrypt::Password#eql? instead.
|
6
|
+
- Added BCrypt::Password#is_password?.
|
7
|
+
- Refactored out BCrypt::Internals into more useful BCrypt::Engine.
|
8
|
+
- Added validation of secrets -- nil is not healthy.
|
data/Rakefile
CHANGED
@@ -4,9 +4,10 @@ require 'rake/gempackagetask'
|
|
4
4
|
require 'rake/contrib/rubyforgepublisher'
|
5
5
|
require 'rake/clean'
|
6
6
|
require 'rake/rdoctask'
|
7
|
+
require "benchmark"
|
7
8
|
|
8
9
|
PKG_NAME = "bcrypt-ruby"
|
9
|
-
PKG_VERSION = "
|
10
|
+
PKG_VERSION = "2.0.0"
|
10
11
|
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
11
12
|
PKG_FILES = FileList[
|
12
13
|
'[A-Z]*',
|
@@ -16,6 +17,14 @@ PKG_FILES = FileList[
|
|
16
17
|
'ext/*.h',
|
17
18
|
'ext/*.rb'
|
18
19
|
]
|
20
|
+
CLEAN.include(
|
21
|
+
"ext/*.o",
|
22
|
+
"ext/*.bundle",
|
23
|
+
"ext/*.so"
|
24
|
+
)
|
25
|
+
CLOBBER.include(
|
26
|
+
"doc/coverage"
|
27
|
+
)
|
19
28
|
|
20
29
|
task :default => [:spec]
|
21
30
|
|
@@ -24,30 +33,34 @@ Spec::Rake::SpecTask.new do |t|
|
|
24
33
|
t.spec_files = FileList['spec/**/*_spec.rb']
|
25
34
|
t.spec_opts = ['--color','--backtrace','--diff']
|
26
35
|
t.rcov = true
|
27
|
-
t.rcov_dir = 'doc/
|
36
|
+
t.rcov_dir = 'doc/coverage'
|
28
37
|
t.rcov_opts = ['--exclude', 'spec\/spec,spec\/.*_spec.rb']
|
29
38
|
end
|
30
39
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
40
|
+
namespace :spec do
|
41
|
+
desc "Run all specs and store html output in doc/specs.html"
|
42
|
+
Spec::Rake::SpecTask.new('html') do |t|
|
43
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
44
|
+
t.spec_opts = ['--diff','--format html','--backtrace','--out doc/specs.html']
|
45
|
+
end
|
35
46
|
end
|
36
47
|
|
37
48
|
desc 'Generate RDoc'
|
38
49
|
rd = Rake::RDocTask.new do |rdoc|
|
39
|
-
rdoc.rdoc_dir = 'doc/
|
50
|
+
rdoc.rdoc_dir = 'doc/rdoc'
|
40
51
|
rdoc.options << '--title' << 'bcrypt-ruby' << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
41
52
|
rdoc.template = ENV['TEMPLATE'] if ENV['TEMPLATE']
|
42
|
-
rdoc.rdoc_files.include('README', 'COPYING', 'lib/**/*.rb')
|
53
|
+
rdoc.rdoc_files.include('README', 'COPYING', 'CHANGELOG', 'lib/**/*.rb')
|
43
54
|
end
|
44
55
|
|
45
56
|
spec = Gem::Specification.new do |s|
|
46
57
|
s.name = PKG_NAME
|
47
58
|
s.version = PKG_VERSION
|
48
|
-
s.summary = "
|
59
|
+
s.summary = "OpenBSD's bcrypt() password hashing algorithm."
|
49
60
|
s.description = <<-EOF
|
50
|
-
|
61
|
+
bcrypt() is a sophisticated and secure hash algorithm designed by The OpenBSD project
|
62
|
+
for hashing passwords. bcrypt-ruby provides a simple, humane wrapper for safely handling
|
63
|
+
passwords.
|
51
64
|
EOF
|
52
65
|
|
53
66
|
s.files = PKG_FILES.to_a
|
@@ -71,8 +84,19 @@ Rake::GemPackageTask.new(spec) do |pkg|
|
|
71
84
|
pkg.need_tar = true
|
72
85
|
end
|
73
86
|
|
74
|
-
task :compile do
|
87
|
+
task :compile => [:clean] do
|
75
88
|
Dir.chdir('./ext')
|
76
89
|
system "ruby extconf.rb"
|
77
90
|
system "make"
|
78
|
-
end
|
91
|
+
end
|
92
|
+
|
93
|
+
task :benchmark do
|
94
|
+
TESTS = 100
|
95
|
+
TEST_PWD = "this is a test"
|
96
|
+
require "lib/bcrypt"
|
97
|
+
Benchmark.bmbm do |results|
|
98
|
+
4.upto(10) do |n|
|
99
|
+
results.report("cost #{n}:") { TESTS.times { BCrypt::Password.create(TEST_PWD, :cost => n) } }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/ext/bcrypt_ext.c
CHANGED
@@ -4,7 +4,7 @@ char *bcrypt_gensalt(u_int8_t, u_int8_t *);
|
|
4
4
|
char *bcrypt(const char *, const char *);
|
5
5
|
|
6
6
|
VALUE mBCrypt;
|
7
|
-
VALUE
|
7
|
+
VALUE cBCryptEngine;
|
8
8
|
|
9
9
|
/* Given a logarithmic cost parameter, generates a salt for use with +bc_crypt+.
|
10
10
|
*/
|
@@ -21,8 +21,8 @@ static VALUE bc_crypt(VALUE self, VALUE key, VALUE salt) {
|
|
21
21
|
/* Create the BCrypt and BCrypt::Internals modules, and populate them with methods. */
|
22
22
|
void Init_bcrypt_ext(){
|
23
23
|
mBCrypt = rb_define_module("BCrypt");
|
24
|
-
|
24
|
+
cBCryptEngine = rb_define_class_under(mBCrypt, "Engine", rb_cObject);
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
rb_define_singleton_method(cBCryptEngine, "__bc_salt", bc_salt, 2);
|
27
|
+
rb_define_singleton_method(cBCryptEngine, "__bc_crypt", bc_crypt, 2);
|
28
28
|
}
|
data/lib/bcrypt.rb
CHANGED
@@ -1,40 +1,46 @@
|
|
1
1
|
# A wrapper for OpenBSD's bcrypt/crypt_blowfish password-hashing algorithm.
|
2
2
|
|
3
|
-
|
4
|
-
require "bcrypt_ext"
|
3
|
+
require "ext/bcrypt_ext"
|
5
4
|
require "openssl"
|
6
5
|
|
7
6
|
# A Ruby library implementing OpenBSD's bcrypt()/crypt_blowfish algorithm for
|
8
7
|
# hashing passwords.
|
9
|
-
module BCrypt
|
8
|
+
module BCrypt
|
10
9
|
module Errors # :nodoc:
|
11
|
-
# The salt parameter provided to bcrypt() is invalid.
|
12
|
-
class
|
13
|
-
# The
|
14
|
-
class
|
15
|
-
# The cost parameter provided to bcrypt() is invalid.
|
16
|
-
class InvalidCost < Exception; end
|
10
|
+
class InvalidSalt < Exception; end # The salt parameter provided to bcrypt() is invalid.
|
11
|
+
class InvalidHash < Exception; end # The hash parameter provided to bcrypt() is invalid.
|
12
|
+
class InvalidCost < Exception; end # The cost parameter provided to bcrypt() is invalid.
|
13
|
+
class InvalidSecret < Exception; end # The secret parameter provided to bcrypt() is invalid.
|
17
14
|
end
|
18
15
|
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
|
25
|
-
# Maximum length of salts.
|
16
|
+
# A Ruby wrapper for the bcrypt() extension calls.
|
17
|
+
class Engine
|
18
|
+
# The default computational expense parameter.
|
19
|
+
DEFAULT_COST = 10
|
20
|
+
# Maximum possible size of bcrypt() salts.
|
26
21
|
MAX_SALT_LENGTH = 16
|
27
22
|
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
23
|
+
# C-level routines which, if they don't get the right input, will crash the
|
24
|
+
# hell out of the Ruby process.
|
25
|
+
private_class_method :__bc_salt
|
26
|
+
private_class_method :__bc_crypt
|
27
|
+
|
28
|
+
# Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates
|
29
|
+
# a bcrypt() password hash.
|
30
|
+
def self.hash(secret, salt)
|
31
|
+
if valid_secret?(secret)
|
32
|
+
if valid_salt?(salt)
|
33
|
+
__bc_crypt(secret, salt)
|
34
|
+
else
|
35
|
+
raise Errors::InvalidSalt.new("invalid salt")
|
36
|
+
end
|
32
37
|
else
|
33
|
-
raise Errors::
|
38
|
+
raise Errors::InvalidSecret.new("invalid secret")
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
37
|
-
|
42
|
+
# Generates a random salt with a given computational cost.
|
43
|
+
def self.generate_salt(cost = DEFAULT_COST)
|
38
44
|
if cost.to_i > 0
|
39
45
|
__bc_salt(cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
|
40
46
|
else
|
@@ -42,29 +48,35 @@ module BCrypt
|
|
42
48
|
end
|
43
49
|
end
|
44
50
|
|
45
|
-
|
46
|
-
|
51
|
+
# Returns true if +salt+ is a valid bcrypt() salt, false if not.
|
52
|
+
def self.valid_salt?(salt)
|
53
|
+
salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/
|
47
54
|
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
55
|
+
|
56
|
+
# Returns true if +secret+ is a valid bcrypt() secret, false if not.
|
57
|
+
def self.valid_secret?(secret)
|
58
|
+
!secret.nil?
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the cost factor which will result in computation times less than +upper_time_limit_in_ms+.
|
62
|
+
#
|
63
|
+
# Example:
|
64
|
+
#
|
65
|
+
# BCrypt.calibrate(200) #=> 10
|
66
|
+
# BCrypt.calibrate(1000) #=> 12
|
67
|
+
#
|
68
|
+
# # should take less than 200ms
|
69
|
+
# BCrypt::Password.create("woo", :cost => 10)
|
70
|
+
#
|
71
|
+
# # should take less than 1000ms
|
72
|
+
# BCrypt::Password.create("woo", :cost => 12)
|
73
|
+
def self.calibrate(upper_time_limit_in_ms)
|
74
|
+
40.times do |i|
|
75
|
+
start_time = Time.now
|
76
|
+
Password.create("testing testing", :cost => i+1)
|
77
|
+
end_time = Time.now - start_time
|
78
|
+
return i if end_time * 1_000 > upper_time_limit_in_ms
|
79
|
+
end
|
68
80
|
end
|
69
81
|
end
|
70
82
|
|
@@ -109,11 +121,9 @@ module BCrypt
|
|
109
121
|
# Example:
|
110
122
|
#
|
111
123
|
# @password = BCrypt::Password.create("my secret", :cost => 13)
|
112
|
-
def create(secret, options = { :cost => DEFAULT_COST })
|
113
|
-
Password.new(
|
124
|
+
def create(secret, options = { :cost => BCrypt::Engine::DEFAULT_COST })
|
125
|
+
Password.new(BCrypt::Engine.hash(secret, BCrypt::Engine.generate_salt(options[:cost])))
|
114
126
|
end
|
115
|
-
private
|
116
|
-
include Internals
|
117
127
|
end
|
118
128
|
|
119
129
|
# Initializes a BCrypt::Password instance with the data from a stored hash.
|
@@ -126,12 +136,11 @@ module BCrypt
|
|
126
136
|
end
|
127
137
|
end
|
128
138
|
|
129
|
-
alias_method :exactly_equals, :==
|
130
|
-
|
131
139
|
# Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
|
132
140
|
def ==(secret)
|
133
|
-
|
141
|
+
super(BCrypt::Engine.hash(secret, @salt))
|
134
142
|
end
|
143
|
+
alias_method :is_password?, :==
|
135
144
|
|
136
145
|
private
|
137
146
|
# Returns true if +h+ is a valid hash.
|
@@ -1,35 +1,49 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
2
2
|
|
3
|
+
context "The BCrypt engine" do
|
4
|
+
specify "should calculate the optimal cost factor to fit in a specific time" do
|
5
|
+
first = BCrypt::Engine.calibrate(100)
|
6
|
+
second = BCrypt::Engine.calibrate(300)
|
7
|
+
second.should >(first + 1)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
3
11
|
context "Generating BCrypt salts" do
|
4
|
-
include BCrypt::Internals
|
5
12
|
|
6
13
|
specify "should produce strings" do
|
7
|
-
|
14
|
+
BCrypt::Engine.generate_salt.should be_an_instance_of(String)
|
8
15
|
end
|
9
16
|
|
10
17
|
specify "should produce random data" do
|
11
|
-
|
18
|
+
BCrypt::Engine.generate_salt.should_not equal(BCrypt::Engine.generate_salt)
|
12
19
|
end
|
13
20
|
|
14
21
|
specify "should raise a InvalidCostError if the cost parameter isn't numeric" do
|
15
|
-
lambda {
|
22
|
+
lambda { BCrypt::Engine.generate_salt('woo') }.should raise_error(BCrypt::Errors::InvalidCost)
|
23
|
+
end
|
24
|
+
|
25
|
+
specify "should raise a InvalidCostError if the cost parameter isn't greater than 0" do
|
26
|
+
lambda { BCrypt::Engine.generate_salt(-1) }.should raise_error(BCrypt::Errors::InvalidCost)
|
16
27
|
end
|
17
28
|
end
|
18
29
|
|
19
30
|
context "Generating BCrypt hashes" do
|
20
|
-
include BCrypt::Internals
|
21
31
|
|
22
32
|
setup do
|
23
|
-
@salt =
|
33
|
+
@salt = BCrypt::Engine.generate_salt(4)
|
24
34
|
@password = "woo"
|
25
35
|
end
|
26
36
|
|
27
37
|
specify "should produce a string" do
|
28
|
-
|
38
|
+
BCrypt::Engine.hash(@password, @salt).should be_an_instance_of(String)
|
39
|
+
end
|
40
|
+
|
41
|
+
specify "should raise an InvalidSalt error if the salt is invalid" do
|
42
|
+
lambda { BCrypt::Engine.hash(@password, 'nino') }.should raise_error(BCrypt::Errors::InvalidSalt)
|
29
43
|
end
|
30
44
|
|
31
|
-
specify "should raise an
|
32
|
-
lambda {
|
45
|
+
specify "should raise an InvalidSecret error if the secret is invalid" do
|
46
|
+
lambda { BCrypt::Engine.hash(nil, @salt) }.should raise_error(BCrypt::Errors::InvalidSecret)
|
33
47
|
end
|
34
48
|
|
35
49
|
specify "should be interoperable with other implementations" do
|
@@ -42,7 +56,7 @@ context "Generating BCrypt hashes" do
|
|
42
56
|
["0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", "$2a$05$abcdefghijklmnopqrstuu", "$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui"]
|
43
57
|
]
|
44
58
|
for secret, salt, test_vector in test_vectors
|
45
|
-
|
59
|
+
BCrypt::Engine.hash(secret, salt).should eql(test_vector)
|
46
60
|
end
|
47
61
|
end
|
48
62
|
end
|
@@ -4,7 +4,7 @@ context "Creating a hashed password" do
|
|
4
4
|
|
5
5
|
setup do
|
6
6
|
@secret = "wheedle"
|
7
|
-
@password = BCrypt::Password.create(@secret)
|
7
|
+
@password = BCrypt::Password.create(@secret, :cost => 4)
|
8
8
|
end
|
9
9
|
|
10
10
|
specify "should return a BCrypt::Password" do
|
@@ -15,6 +15,9 @@ context "Creating a hashed password" do
|
|
15
15
|
lambda { BCrypt::Password.new(@password) }.should_not raise_error
|
16
16
|
end
|
17
17
|
|
18
|
+
specify "should raise an InvalidSecret exception if the secret is nil" do
|
19
|
+
lambda { BCrypt::Password.create(nil) }.should raise_error(BCrypt::Errors::InvalidSecret)
|
20
|
+
end
|
18
21
|
end
|
19
22
|
|
20
23
|
context "Reading a hashed password" do
|
metadata
CHANGED
@@ -3,15 +3,15 @@ rubygems_version: 0.9.1
|
|
3
3
|
specification_version: 1
|
4
4
|
name: bcrypt-ruby
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version:
|
7
|
-
date: 2007-
|
8
|
-
summary:
|
6
|
+
version: 2.0.0
|
7
|
+
date: 2007-03-07 00:00:00 -08:00
|
8
|
+
summary: OpenBSD's bcrypt() password hashing algorithm.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email: coda.hale@gmail.com
|
12
12
|
homepage: http://bcrypt-ruby.rubyforge.org
|
13
13
|
rubyforge_project: bcrypt-ruby
|
14
|
-
description:
|
14
|
+
description: bcrypt() is a sophisticated and secure hash algorithm designed by The OpenBSD project for hashing passwords. bcrypt-ruby provides a simple, humane wrapper for safely handling passwords.
|
15
15
|
autorequire: bcrypt
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|
@@ -29,13 +29,13 @@ post_install_message:
|
|
29
29
|
authors:
|
30
30
|
- - Coda Hale
|
31
31
|
files:
|
32
|
+
- CHANGELOG
|
32
33
|
- COPYING
|
33
34
|
- Rakefile
|
34
35
|
- README
|
35
36
|
- lib/bcrypt.rb
|
36
37
|
- spec/spec_helper.rb
|
37
|
-
- spec/bcrypt/
|
38
|
-
- spec/bcrypt/internals_spec.rb
|
38
|
+
- spec/bcrypt/engine_spec.rb
|
39
39
|
- spec/bcrypt/password_spec.rb
|
40
40
|
- ext/bcrypt.c
|
41
41
|
- ext/bcrypt_ext.c
|
@@ -54,6 +54,7 @@ rdoc_options:
|
|
54
54
|
extra_rdoc_files:
|
55
55
|
- README
|
56
56
|
- COPYING
|
57
|
+
- CHANGELOG
|
57
58
|
- lib/bcrypt.rb
|
58
59
|
executables: []
|
59
60
|
|
data/spec/bcrypt/bcrypt_spec.rb
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
2
|
-
|
3
|
-
context "BCrypt" do
|
4
|
-
specify "should calculate the optimal cost factor to fit in a specific time" do
|
5
|
-
first = BCrypt.calibrate(100)
|
6
|
-
second = BCrypt.calibrate(300)
|
7
|
-
second.should >(first + 1)
|
8
|
-
end
|
9
|
-
end
|