bcrypt-ruby 1.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/ext/extconf.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "mkmf"
2
+ dir_config("bcrypt_ext")
3
+ # enable this when we're feeling nitpicky
4
+ # CONFIG['CC'] << " -Wall "
5
+ create_makefile("bcrypt_ext")
data/lib/bcrypt.rb ADDED
@@ -0,0 +1,151 @@
1
+ # A wrapper for OpenBSD's bcrypt/crypt_blowfish password-hashing algorithm.
2
+
3
+ $: << "ext"
4
+ require "bcrypt_ext"
5
+ require "openssl"
6
+
7
+ # A Ruby library implementing OpenBSD's bcrypt()/crypt_blowfish algorithm for
8
+ # hashing passwords.
9
+ module BCrypt
10
+ module Errors # :nodoc:
11
+ # The salt parameter provided to bcrypt() is invalid.
12
+ class InvalidSalt < Exception; end
13
+ # The hash parameter provided to bcrypt() is invalid.
14
+ class InvalidHash < Exception; end
15
+ # The cost parameter provided to bcrypt() is invalid.
16
+ class InvalidCost < Exception; end
17
+ end
18
+
19
+ # The default computational expense parameter.
20
+ DEFAULT_COST = 10
21
+
22
+ module Internals # :nodoc:
23
+ # contains the C-level methods and other internally bits
24
+
25
+ # Maximum length of salts.
26
+ MAX_SALT_LENGTH = 16
27
+
28
+ # Wrap this in some error checking otherwise it'll explode.
29
+ def _bc_crypt(key, salt)
30
+ if valid_salt?(salt)
31
+ __bc_crypt(key, salt)
32
+ else
33
+ raise Errors::InvalidSalt.new("invalid salt")
34
+ end
35
+ end
36
+
37
+ def _bc_salt(cost = DEFAULT_COST)
38
+ if cost.to_i > 0
39
+ __bc_salt(cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
40
+ else
41
+ raise Errors::InvalidCost.new("cost must be numeric and > 0")
42
+ end
43
+ end
44
+
45
+ def valid_salt?(s)
46
+ s =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/
47
+ end
48
+ end
49
+
50
+ # Returns the cost factor which will result in computation times less than +upper_time_limit_in_ms+.
51
+ #
52
+ # Example:
53
+ #
54
+ # BCrypt.calibrate(200) #=> 10
55
+ # BCrypt.calibrate(1000) #=> 12
56
+ #
57
+ # # should take less than 200ms
58
+ # BCrypt::Password.create("woo", :cost => 10)
59
+ #
60
+ # # should take less than 1000ms
61
+ # BCrypt::Password.create("woo", :cost => 12)
62
+ def self.calibrate(upper_time_limit_in_ms)
63
+ 40.times do |i|
64
+ start_time = Time.now
65
+ Password.create("testing testing", :cost => i+1)
66
+ end_time = Time.now - start_time
67
+ return i if end_time * 1_000 > upper_time_limit_in_ms
68
+ end
69
+ end
70
+
71
+ # A password management class which allows you to safely store users' passwords and compare them.
72
+ #
73
+ # Example usage:
74
+ #
75
+ # include BCrypt
76
+ #
77
+ # # hash a user's password
78
+ # @password = Password.create("my grand secret")
79
+ # @password #=> "$2a$10$GtKs1Kbsig8ULHZzO1h2TetZfhO4Fmlxphp8bVKnUlZCBYYClPohG"
80
+ #
81
+ # # store it safely
82
+ # @user.update_attribute(:password, @password)
83
+ #
84
+ # # read it back
85
+ # @user.reload!
86
+ # @db_password = Password.new(@user.password)
87
+ #
88
+ # # compare it after retrieval
89
+ # @db_password == "my grand secret" #=> true
90
+ # @db_password == "a paltry guess" #=> false
91
+ #
92
+ class Password < String
93
+ # The hash portion of the stored password hash.
94
+ attr_reader :hash
95
+ # The salt of the store password hash (including version and cost).
96
+ attr_reader :salt
97
+ # The version of the bcrypt() algorithm used to create the hash.
98
+ attr_reader :version
99
+ # The cost factor used to create the hash.
100
+ attr_reader :cost
101
+
102
+ class << self
103
+ # Hashes a secret, returning a BCrypt::Password instance. Takes an optional <tt>:cost</tt> option, which is a
104
+ # logarithmic variable which determines how computational expensive the hash is to calculate (a <tt>:cost</tt> of
105
+ # 4 is twice as much work as a <tt>:cost</tt> of 3). The higher the <tt>:cost</tt> the harder it becomes for
106
+ # attackers to try to guess passwords (even if a copy of your database is stolen), but the slower it is to check
107
+ # users' passwords.
108
+ #
109
+ # Example:
110
+ #
111
+ # @password = BCrypt::Password.create("my secret", :cost => 13)
112
+ def create(secret, options = { :cost => DEFAULT_COST })
113
+ Password.new(_bc_crypt(secret, _bc_salt(options[:cost])))
114
+ end
115
+ private
116
+ include Internals
117
+ end
118
+
119
+ # Initializes a BCrypt::Password instance with the data from a stored hash.
120
+ def initialize(raw_hash)
121
+ if valid_hash?(raw_hash)
122
+ self.replace(raw_hash)
123
+ @version, @cost, @salt, @hash = split_hash(self)
124
+ else
125
+ raise Errors::InvalidHash.new("invalid hash")
126
+ end
127
+ end
128
+
129
+ alias_method :exactly_equals, :==
130
+
131
+ # Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
132
+ def ==(secret)
133
+ self.exactly_equals(self.class._bc_crypt(secret, @salt))
134
+ end
135
+
136
+ private
137
+ # Returns true if +h+ is a valid hash.
138
+ def valid_hash?(h)
139
+ h =~ /^\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}$/
140
+ end
141
+
142
+ # call-seq:
143
+ # split_hash(raw_hash) -> version, cost, salt, hash
144
+ #
145
+ # Splits +h+ into version, cost, salt, and hash and returns them in that order.
146
+ def split_hash(h)
147
+ b, v, c, mash = h.split('$')
148
+ return v, c.to_i, h[0, 29], mash[-31, 31]
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,9 @@
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
@@ -0,0 +1,48 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper")
2
+
3
+ context "Generating BCrypt salts" do
4
+ include BCrypt::Internals
5
+
6
+ specify "should produce strings" do
7
+ _bc_salt.should be_an_instance_of(String)
8
+ end
9
+
10
+ specify "should produce random data" do
11
+ _bc_salt.should_not equal(_bc_salt)
12
+ end
13
+
14
+ specify "should raise a InvalidCostError if the cost parameter isn't numeric" do
15
+ lambda { _bc_salt('woo') }.should raise_error(BCrypt::Errors::InvalidCost)
16
+ end
17
+ end
18
+
19
+ context "Generating BCrypt hashes" do
20
+ include BCrypt::Internals
21
+
22
+ setup do
23
+ @salt = _bc_salt
24
+ @password = "woo"
25
+ end
26
+
27
+ specify "should produce a string" do
28
+ _bc_crypt(@password, @salt).should be_an_instance_of(String)
29
+ end
30
+
31
+ specify "should raise an InvalidSaltError if the salt is invalid" do
32
+ lambda { _bc_crypt(@password, 'nino') }.should raise_error(BCrypt::Errors::InvalidSalt)
33
+ end
34
+
35
+ specify "should be interoperable with other implementations" do
36
+ # test vectors from the OpenWall implementation <http://www.openwall.com/crypt/>
37
+ test_vectors = [
38
+ ["U*U", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"],
39
+ ["U*U*", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK"],
40
+ ["U*U*U", "$2a$05$XXXXXXXXXXXXXXXXXXXXXO", "$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a"],
41
+ ["", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy"],
42
+ ["0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", "$2a$05$abcdefghijklmnopqrstuu", "$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui"]
43
+ ]
44
+ for secret, salt, test_vector in test_vectors
45
+ _bc_crypt(secret, salt).should eql(test_vector)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,53 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper")
2
+
3
+ context "Creating a hashed password" do
4
+
5
+ setup do
6
+ @secret = "wheedle"
7
+ @password = BCrypt::Password.create(@secret)
8
+ end
9
+
10
+ specify "should return a BCrypt::Password" do
11
+ @password.should be_an_instance_of(BCrypt::Password)
12
+ end
13
+
14
+ specify "should return a valid bcrypt password" do
15
+ lambda { BCrypt::Password.new(@password) }.should_not raise_error
16
+ end
17
+
18
+ end
19
+
20
+ context "Reading a hashed password" do
21
+ setup do
22
+ @secret = "U*U"
23
+ @hash = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
24
+ end
25
+
26
+ specify "should read the version, cost, salt, and hash" do
27
+ password = BCrypt::Password.new(@hash)
28
+ password.version.should eql("2a")
29
+ password.cost.should equal(5)
30
+ password.salt.should eql("$2a$05$CCCCCCCCCCCCCCCCCCCCC.")
31
+ password.to_s.should_eql @hash
32
+ end
33
+
34
+ specify "should raise an InvalidHashError when given an invalid hash" do
35
+ lambda { BCrypt::Password.new('weedle') }.should raise_error(BCrypt::Errors::InvalidHash)
36
+ end
37
+ end
38
+
39
+ context "Comparing a hashed password with a secret" do
40
+ setup do
41
+ @secret = "U*U"
42
+ @hash = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
43
+ @password = BCrypt::Password.create(@secret)
44
+ end
45
+
46
+ specify "should compare successfully to the original secret" do
47
+ (@password == @secret).should be(true)
48
+ end
49
+
50
+ specify "should compare unsuccessfully to anything besides original secret" do
51
+ (@password == "@secret").should be(false)
52
+ end
53
+ end
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
2
+ require "rubygems"
3
+ require "spec"
4
+ require "bcrypt"
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.1
3
+ specification_version: 1
4
+ name: bcrypt-ruby
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2007-02-27 00:00:00 -08:00
8
+ summary: Blah.
9
+ require_paths:
10
+ - lib
11
+ email: coda.hale@gmail.com
12
+ homepage: http://bcrypt-ruby.rubyforge.org
13
+ rubyforge_project: bcrypt-ruby
14
+ description: Woot.
15
+ autorequire: bcrypt
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - - Coda Hale
31
+ files:
32
+ - COPYING
33
+ - Rakefile
34
+ - README
35
+ - lib/bcrypt.rb
36
+ - spec/spec_helper.rb
37
+ - spec/bcrypt/bcrypt_spec.rb
38
+ - spec/bcrypt/internals_spec.rb
39
+ - spec/bcrypt/password_spec.rb
40
+ - ext/bcrypt.c
41
+ - ext/bcrypt_ext.c
42
+ - ext/blowfish.c
43
+ - ext/blf.h
44
+ - ext/extconf.rb
45
+ test_files: []
46
+
47
+ rdoc_options:
48
+ - --title
49
+ - bcrypt-ruby
50
+ - --line-numbers
51
+ - --inline-source
52
+ - --main
53
+ - README
54
+ extra_rdoc_files:
55
+ - README
56
+ - COPYING
57
+ - lib/bcrypt.rb
58
+ executables: []
59
+
60
+ extensions:
61
+ - ext/extconf.rb
62
+ requirements: []
63
+
64
+ dependencies: []
65
+