rbpass 0.0.1

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 ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Pablo Elices
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # RbPass
2
+
3
+ RbPass is a Ruby port of PHPass, a portable password hashing framework written in PHP.
4
+ PHPass is used by WordPress, bbPress, Vanilla Forums, PivotX and phpBB.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'rbpass'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install rbpass
19
+
20
+ ## Usage
21
+
22
+ require 'rbpass'
23
+
24
+ password_hasher = RbPass::PasswordHasher.new(8, false)
25
+
26
+ # Hashing a password
27
+ password = 'somepasswordhere'
28
+ password_hash = password_hasher.hash(password)
29
+
30
+ # Password checking
31
+ password = 'somepasswordhere'
32
+ password_hash = get_password_hash_from_somewhere()
33
+
34
+ if password_hasher.check(password, password_hash)
35
+ puts 'Valid password'
36
+ else
37
+ puts 'Invalid password'
38
+ end
39
+
40
+ ## Copyright
41
+
42
+ Copyright © 2013 Pablo Elices. See [license](https://github.com/pabloelices/rbpass/blob/master/LICENSE.md) for details.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec) do |option|
6
+ option.rspec_opts = "-c" # Colored output
7
+ end
8
+
9
+ task :default => :spec
data/bin/rbpass ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'rbpass'
5
+
6
+ OptionParser.new do |option_parser|
7
+ option_parser.banner = 'Usage: rbpass [options]'
8
+
9
+ option_parser.on('-h', '--help', "Display help.") do
10
+ puts option_parser
11
+ end
12
+
13
+ option_parser.on('-v', '--version', "Display the current version.") do
14
+ puts "RbPass #{RbPass::VERSION}"
15
+ end
16
+ end.parse!
@@ -0,0 +1,200 @@
1
+ require 'digest/md5'
2
+
3
+ module RbPass
4
+ class PasswordHasher
5
+ def initialize(iteration_count_log2, portable_hashes)
6
+ @itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
7
+ @iteration_count_log2 = iteration_count_log2.between?(4, 31) ? iteration_count_log2 : 8
8
+ @portable_hashes = portable_hashes
9
+ @random_state = Time.now.usec.to_s + Process.pid.to_s
10
+ end
11
+
12
+ def get_random_bytes(count)
13
+ output = ''
14
+
15
+ if File.readable?('/dev/urandom')
16
+ output = File.read('/dev/urandom', count)
17
+ end
18
+
19
+ if output.length < count
20
+ output = ''
21
+ i = 0
22
+
23
+ while i < count do
24
+ @random_state = Digest::MD5.hexdigest(Time.now.usec.to_s + @random_state)
25
+ @output << Digest::MD5.hexdigest(@random_state).split(//).pack('H*')
26
+ i += 16
27
+ end
28
+
29
+ output = output[0..count]
30
+ end
31
+
32
+ return output
33
+ end
34
+
35
+ def encode64(input, count)
36
+ output = ''
37
+ i = 0
38
+
39
+ while i < count
40
+ value = (input[i]).ord
41
+ i += 1
42
+ output << @itoa64[value & 0x3f]
43
+
44
+ if i < count
45
+ value |= (input[i]).ord << 8
46
+ end
47
+
48
+ output << @itoa64[(value >> 6) & 0x3f]
49
+
50
+ if i >= count
51
+ break
52
+ end
53
+
54
+ i += 1
55
+
56
+ if i < count
57
+ value |= (input[i]).ord << 16
58
+ end
59
+
60
+ output << @itoa64[(value >> 12) & 0x3f]
61
+
62
+ if i >= count
63
+ break
64
+ end
65
+
66
+ i += 1
67
+
68
+ output << @itoa64[(value >> 18) & 0x3f]
69
+ end
70
+
71
+ return output
72
+ end
73
+
74
+ def gensalt_private(input)
75
+ output = '$P$'
76
+
77
+ output << @itoa64[[@iteration_count_log2 + 5, 30].min]
78
+ output << encode64(input, 6)
79
+
80
+ return output
81
+ end
82
+
83
+ def crypt_private(password, setting)
84
+ output = '*0'
85
+
86
+ if setting[0,2] == output
87
+ output = '*1'
88
+ end
89
+
90
+ return output if setting[0,3] != '$P$' and setting[0,3] != '$H$'
91
+
92
+ count_log2 = @itoa64.index(setting[3])
93
+
94
+ return output if !count_log2.between?(7, 30)
95
+
96
+ count = 1 << count_log2
97
+
98
+ salt = setting[4,8]
99
+
100
+ return output if salt.length != 8
101
+
102
+ hash = Digest::MD5.digest(salt + password)
103
+
104
+ while count > 0 do
105
+ hash = Digest::MD5.digest(hash + password)
106
+ count -= 1
107
+ end
108
+
109
+ output = setting[0, 12]
110
+
111
+ output << encode64(hash, 16)
112
+
113
+ return output
114
+ end
115
+
116
+ # Actually this function is never called within a regular program execution.
117
+ # Blowfish is the default algorithm, implement ext-des?
118
+ def gensalt_extended(input)
119
+ count_log2 = [@iteration_count_log2 + 8, 24].min
120
+ count = (1 << count_log2) -1
121
+
122
+ output = '_'
123
+
124
+ output << @itoa64[count & 0x3f]
125
+ output << @itoa64[(count >> 6) & 0x3f]
126
+ output << @itoa64[(count >> 12) & 0x3f]
127
+ output << @itoa64[(count >> 18) & 0x3f]
128
+ output << encode64(input, 3)
129
+
130
+ return output
131
+ end
132
+
133
+ def gensalt_blowfish(input)
134
+ itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
135
+ output = '$2a$'
136
+
137
+ output << ('0'.ord + @iteration_count_log2 / 10).chr
138
+ output << ('0'.ord + @iteration_count_log2 % 10).chr
139
+ output << '$'
140
+
141
+ i = 0
142
+
143
+ while true
144
+ c1 = (input[i]).ord
145
+ i += 1
146
+ output << itoa64[c1 >> 2]
147
+ c1 = (c1 & 0x03) << 4
148
+
149
+ if i >= 16
150
+ output << itoa64[c1]
151
+ break
152
+ end
153
+
154
+ c2 = (input[i]).ord
155
+ i += 1
156
+ c1 |= c2 >> 4
157
+ output << itoa64[c1]
158
+ c1 = (c2 & 0x0f) << 2
159
+
160
+ c2 = (input[i]).ord
161
+ i += 1
162
+ c1 |= c2 >> 6
163
+ output << itoa64[c1]
164
+ output << itoa64[c2 & 0x3f]
165
+ end
166
+
167
+ return output
168
+ end
169
+
170
+ def hash(password)
171
+ random = ''
172
+
173
+ if !@portable_hashes
174
+ random = get_random_bytes(16)
175
+ hash = password.crypt(gensalt_blowfish(random))
176
+ return hash if hash.length == 60
177
+ end
178
+
179
+ if random.length < 6
180
+ random = get_random_bytes(6)
181
+ end
182
+
183
+ hash = crypt_private(password, gensalt_private(random))
184
+
185
+ return hash if hash.length == 34
186
+
187
+ return '*'
188
+ end
189
+
190
+ def check(password, stored_hash)
191
+ hash = crypt_private(password, stored_hash)
192
+
193
+ if hash[0] == '*'
194
+ hash = password.crypt(stored_hash)
195
+ end
196
+
197
+ return hash == stored_hash
198
+ end
199
+ end
200
+ end
data/lib/rbpass.rb ADDED
@@ -0,0 +1,5 @@
1
+ require File.expand_path('../rbpass/password_hasher', __FILE__)
2
+
3
+ module RbPass
4
+ VERSION = '0.0.1'
5
+ end
data/rbpass.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'rbpass'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = 'rbpass'
10
+ spec.version = RbPass::VERSION
11
+ spec.license = 'MIT'
12
+ spec.authors = ['Pablo Elices']
13
+ spec.email = ['contact@pabloelic.es']
14
+ spec.summary = 'Ruby port of PHPass, a portable password hashing framework written in php.'
15
+ spec.description = <<-EOF
16
+ Ruby port of phpass, a portable password hashing framework written in php.
17
+ PHPass is used by WordPress, bbPress, Vanilla Forums, PivotX and phpBB.
18
+ EOF
19
+ spec.homepage = 'https://github.com/pabloelices/rbpass'
20
+ spec.files = `git ls-files`.split($/)
21
+ spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
+ spec.require_paths = ['lib']
24
+ end
@@ -0,0 +1,48 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe RbPass::PasswordHasher do
4
+ context 'when using system-specific hashes' do
5
+ let(:password_hasher) { RbPass::PasswordHasher.new(8, false) }
6
+ let(:valid_password) { 'test12345' }
7
+ let(:invalid_password) { 'test12346' }
8
+ let(:valid_password_hash) { password_hasher.hash(valid_password) }
9
+
10
+ it 'returns true when a valid password is passed' do
11
+ password_hasher.check(valid_password, valid_password_hash).should be_true
12
+ end
13
+
14
+ it 'returns false when an invalid password is passed' do
15
+ password_hasher.check(invalid_password, valid_password_hash).should be_false
16
+ end
17
+ end
18
+
19
+ context 'when using portable hashes' do
20
+ let(:password_hasher) { RbPass::PasswordHasher.new(8, true) }
21
+ let(:valid_password) { 'test12345' }
22
+ let(:invalid_password) { 'test12346' }
23
+ let(:valid_password_hash) { password_hasher.hash(valid_password) }
24
+
25
+ it 'returns true when a valid password is passed' do
26
+ password_hasher.check(valid_password, valid_password_hash).should be_true
27
+ end
28
+
29
+ it 'returns false when an invalid password is passed' do
30
+ password_hasher.check(invalid_password, valid_password_hash).should be_false
31
+ end
32
+ end
33
+
34
+ context 'when using portable hashes and a random hardcoded password hash' do
35
+ let(:password_hasher) { RbPass::PasswordHasher.new(8, true) }
36
+ let(:valid_password) { 'test12345' }
37
+ let(:invalid_password) { 'test12346' }
38
+ let(:valid_password_hash) { '$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0' } # A correct portable hash for 'test12345'
39
+
40
+ it 'returns true when a valid password is passed' do
41
+ password_hasher.check(valid_password, valid_password_hash).should be_true
42
+ end
43
+
44
+ it 'returns false when an invalid password is passed' do
45
+ password_hasher.check(invalid_password, valid_password_hash).should be_false
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe RbPass do
4
+ it 'has a version number' do
5
+ RbPass::VERSION.should_not be_nil
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path('../../lib/rbpass', __FILE__)
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rbpass
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pablo Elices
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-18 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! " Ruby port of phpass, a portable password hashing framework written
15
+ in php.\n PHPass is used by WordPress, bbPress, Vanilla Forums, PivotX and phpBB.\n"
16
+ email:
17
+ - contact@pabloelic.es
18
+ executables:
19
+ - rbpass
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - .gitignore
24
+ - Gemfile
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - bin/rbpass
29
+ - lib/rbpass.rb
30
+ - lib/rbpass/password_hasher.rb
31
+ - rbpass.gemspec
32
+ - spec/rbpass/password_hasher_spec.rb
33
+ - spec/rbpass/rbpass_spec.rb
34
+ - spec/spec_helper.rb
35
+ homepage: https://github.com/pabloelices/rbpass
36
+ licenses:
37
+ - MIT
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 1.8.24
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: Ruby port of PHPass, a portable password hashing framework written in php.
60
+ test_files:
61
+ - spec/rbpass/password_hasher_spec.rb
62
+ - spec/rbpass/rbpass_spec.rb
63
+ - spec/spec_helper.rb