bcrypt-ruby 1.0.0 → 2.0.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.

@@ -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 = "1.0.0"
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/output/coverage'
36
+ t.rcov_dir = 'doc/coverage'
28
37
  t.rcov_opts = ['--exclude', 'spec\/spec,spec\/.*_spec.rb']
29
38
  end
30
39
 
31
- desc "Run all specs and store html output in doc/output/report.html"
32
- Spec::Rake::SpecTask.new('spec_html') do |t|
33
- t.spec_files = FileList['spec/**/*_spec.rb']
34
- t.spec_opts = ['--diff','--format html','--backtrace','--out doc/output/report.html']
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/output/rdoc'
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 = "Blah."
59
+ s.summary = "OpenBSD's bcrypt() password hashing algorithm."
49
60
  s.description = <<-EOF
50
- Woot.
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
@@ -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 mBCryptInternals;
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
- mBCryptInternals = rb_define_module_under(mBCrypt, "Internals");
24
+ cBCryptEngine = rb_define_class_under(mBCrypt, "Engine", rb_cObject);
25
25
 
26
- rb_define_method(mBCryptInternals, "__bc_salt", bc_salt, 2);
27
- rb_define_method(mBCryptInternals, "__bc_crypt", bc_crypt, 2);
26
+ rb_define_singleton_method(cBCryptEngine, "__bc_salt", bc_salt, 2);
27
+ rb_define_singleton_method(cBCryptEngine, "__bc_crypt", bc_crypt, 2);
28
28
  }
@@ -1,40 +1,46 @@
1
1
  # A wrapper for OpenBSD's bcrypt/crypt_blowfish password-hashing algorithm.
2
2
 
3
- $: << "ext"
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 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
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
- # 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.
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
- # 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)
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::InvalidSalt.new("invalid salt")
38
+ raise Errors::InvalidSecret.new("invalid secret")
34
39
  end
35
40
  end
36
41
 
37
- def _bc_salt(cost = DEFAULT_COST)
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
- def valid_salt?(s)
46
- s =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/
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
- 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
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(_bc_crypt(secret, _bc_salt(options[:cost])))
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
- self.exactly_equals(self.class._bc_crypt(secret, @salt))
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
- _bc_salt.should be_an_instance_of(String)
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
- _bc_salt.should_not equal(_bc_salt)
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 { _bc_salt('woo') }.should raise_error(BCrypt::Errors::InvalidCost)
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 = _bc_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
- _bc_crypt(@password, @salt).should be_an_instance_of(String)
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 InvalidSaltError if the salt is invalid" do
32
- lambda { _bc_crypt(@password, 'nino') }.should raise_error(BCrypt::Errors::InvalidSalt)
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
- _bc_crypt(secret, salt).should eql(test_vector)
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: 1.0.0
7
- date: 2007-02-27 00:00:00 -08:00
8
- summary: Blah.
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: Woot.
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/bcrypt_spec.rb
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
 
@@ -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