pwdhash 0.3.2 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: bb0a326c401db28d80926db611907417abd26c83
4
- data.tar.gz: 6e0c010c435d13f50b3c4e7d582f72ce47140ef1
2
+ SHA256:
3
+ metadata.gz: 80965617c1934a63980a9dc44272917781ed932ede352980404899742b786166
4
+ data.tar.gz: f4f462e26e0af2defbd9d8d011e959fc3edac2c4601d991741685e7161fe6dd7
5
5
  SHA512:
6
- metadata.gz: c4ff8c1cb1d94d97176abd54c4997a44750d85d4942f83acb5ac232a907560021af99cb7fad637ecc1d2edce8d10cc2473e18d50916e9fba8abc2c4d33b9f96c
7
- data.tar.gz: e1d4f23b0bac24fb7e9b2e7cf2ddab0a7b1627b57b04b06ac492ef9d95e3417ee4fb29d3aa0f47c494eedc3c44169e30ca0058fcbd40175b0d28b39ea2c9c402
6
+ metadata.gz: ba69797cbb0fad88fdaa469c17f3b21e3d5d532bb71e9070ece6c1178e135e550578e81a982f83f9f3ae82d7be633bdbf6c225479b597b56323a53cd8f9de9b5
7
+ data.tar.gz: 778b565e64c59a764ac6bab2ca5592336afcb71cdfc1a446c8ddc035e28acc34698f554f88b4675425efed318c5f0e660cac0b1759856cd32770f0c9ea75c750
@@ -6,7 +6,7 @@ Generates PwdHash passwords from the command line.
6
6
 
7
7
  This is a simple Ruby command line tool to generate hashed passwords, using PwdHash's algorithm.
8
8
 
9
- PwdHash was written as a browser plugin, but sometimes we'd like to extend its use to desktop applications too. e.g. IM clients, Dropbox, etc. just to name a few.
9
+ PwdHash was written as a browser plug-in, but sometimes we'd like to extend its use to desktop applications too. e.g. IM clients, Dropbox, etc. just to name a few.
10
10
 
11
11
  This tool is essentially a *straight* copy of [Chris Roos' implementation][chris-roos-impl], with a couple of command line interface improvements:
12
12
 
@@ -28,17 +28,41 @@ This tool is essentially a *straight* copy of [Chris Roos' implementation][chris
28
28
  Password for example.com:
29
29
  5NBoCKraALBs
30
30
 
31
- $ pwdhash example.com | xcilp # Put generated password to X clipboard
31
+ $ pwdhash example.com | xclip # Put generated password to X clipboard (paste with middle-click)
32
+ $ pwdhash example.com | xclip -selection c # Put generated password to X clipboard (paste with CTRL+V)
32
33
  $ pwdhash example.com | pbcopy # Or in OS X
33
34
  > pwdhash example.com | clip # Or in Windows
34
35
  $ pwdhash example.com | putclip # Or in Cygwin
35
36
 
37
+ ## PwdHash2
38
+
39
+ Some vulnerabilities of PwdHash have been exposed in [Cracking PwdHash: A Brute-force Attack on Client-side Password Hashing][cracking-pwdhash], along with a [proof-of-concept][pwdhash-poc] update to PwdHash. The updated algorithm (with PBKDF2-SHA256 + salt + multiple iterations) has been implemented as a Firefox Add-On at [GWuk/PwdHash2][gwuk-pwdhash2].
40
+
41
+ The corresponding algorithm can be used with:
42
+
43
+ $ pwdhash2 URI [SALT] [ITERATIONS]
44
+
45
+ $ pwdhash2 example.com ReplaceThisSalt 50_000
46
+ Password for example.com (salt = 'ReplaceThisSalt', iterations = 50000):
47
+ IHC8WhmO40v
48
+
49
+ *SALT* can be defined in `PWDHASH2_SALT` environment variable.
50
+
51
+ *ITERATIONS* can be defined in `PWDHASH2_ITERATIONS` environment variable.
52
+
53
+ $ pwdhash2 example.com | xclip -selection c # Put generated password to X clipboard (paste with CTRL+V)
54
+
36
55
  ## Attribution
37
56
 
38
57
  The library part of this tool is a *straight* copy of [Chris Roos' implementation][chris-roos-impl]. The reason this git repository is set up is because: a) Chris Roos decided to put the code in an obscure corner in a pile of code which makes it hard for people to find and b) GitHub uses git :p
39
58
 
40
- The PwdHash plugin and the PwdHash algorithm is contributed by [Stanford PwdHash][stanford-pwdhash].
59
+ The PwdHash plug-in and the PwdHash algorithm is contributed by [Stanford PwdHash][stanford-pwdhash].
60
+
61
+ The PwdHash2 algorithm is contributed by [David Llewellyn-Jones and Graham Rymer][pwdhash-poc].
41
62
 
42
63
  [chris-roos-impl]: http://chrisroos.co.uk/blog/2007-04-11-getting-to-grips-with-pwdhash
43
64
  [stanford-pwdhash]: http://pwdhash.com
44
65
  [usenix-paper]: http://crypto.stanford.edu/PwdHash/pwdhash.pdf
66
+ [cracking-pwdhash]: https://www.researchgate.net/publication/316267246_Cracking_PwdHash_A_Bruteforce_Attack_on_Client-side_Password_Hashing
67
+ [pwdhash-poc]: https://github.com/llewelld/pwdhash-poc
68
+ [gwuk-pwdhash2]: https://github.com/GWuk/PwdHash2/
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'highline/import'
5
+ require 'pathname'
6
+
7
+ require 'pwdhash/pwdhash'
8
+ require 'pwdhash/extract_domain'
9
+ require 'pwdhash/version'
10
+
11
+ require 'backports'
12
+
13
+ def usage
14
+ puts "pwdhash.rb #{PwdHash::VERSION} - Chris Yuen, Chris Roos (2012) Eric Duminil (2019)"
15
+ puts "Usage: pwdhash2 URI [SALT] [ITERATIONS]"
16
+ puts "Example: pwdhash2 https://mail.google.com"
17
+ puts "Example: pwdhash2 https://mail.google.com SomeSalt"
18
+ puts "Example: pwdhash2 https://mail.google.com SomeSalt 10_000"
19
+ puts
20
+ puts "SALT can also be defined in PWDHASH2_SALT environment variable."
21
+ puts "ITERATIONS can also be defined in PWDHASH2_ITERATIONS environment variable."
22
+ exit
23
+ end
24
+
25
+ usage if ARGV.length == 0
26
+
27
+ realm = extract_domain(ARGV[0])
28
+
29
+ salt = ARGV[1] || ENV['PWDHASH2_SALT']
30
+ iterations = (ARGV[2] || ENV['PWDHASH2_ITERATIONS'] || 50_000).to_i
31
+
32
+ hl = HighLine.new($stdin, $stderr)
33
+ hl.say("WARNING: Empty salt!") if salt.empty?
34
+ password = hl.ask("Password for #{realm} (salt = '#{salt}', iterations = #{iterations}): ") {|q| q.echo = false}
35
+
36
+ print get_hashed_password2(password, realm, salt, iterations)
@@ -26,7 +26,7 @@ def contains(result, regex)
26
26
  end
27
27
 
28
28
  def apply_constraints(hash, size, nonalphanumeric)
29
- startingSize = size - 4 # Leave room for some extra characters
29
+ startingSize = [size - 4, 0].max # Leave room for some extra characters
30
30
  result = hash[0, startingSize]
31
31
  extras = (hash[startingSize, hash.length] || "").split('')
32
32
 
@@ -46,8 +46,8 @@ def apply_constraints(hash, size, nonalphanumeric)
46
46
  return result.join('')
47
47
  end
48
48
 
49
- require 'hmac-md5'
50
49
  require 'base64'
50
+ require 'openssl'
51
51
 
52
52
  module PwdHash
53
53
  class Hash
@@ -65,12 +65,28 @@ module PwdHash
65
65
  end
66
66
  private
67
67
  def hash!
68
- @hash = Base64.encode64(HMAC::MD5.digest(@password, @realm)).strip
68
+ @hash = Base64.encode64(OpenSSL::HMAC.digest("MD5", @password, @realm)).strip
69
69
  end
70
70
  def remove_base64_pad_character
71
71
  @hash.sub!(/=+$/, '')
72
72
  end
73
73
  end
74
+
75
+ class Hash2 < Hash
76
+ DIGEST = OpenSSL::Digest::SHA256.new
77
+
78
+ def initialize(realm, password, salt, iterations)
79
+ @salt, @iterations = salt, iterations
80
+ super(realm, password)
81
+ end
82
+
83
+ private
84
+
85
+ def hash!
86
+ # Based on https://github.com/GWuk/PwdHash2/blob/gh-pages/pwdhash2/hashed-password.js#L44
87
+ @hash = Base64.encode64(OpenSSL::PKCS5.pbkdf2_hmac([@password , @salt].join, @realm, @iterations, (2 * size / 3) + 16, DIGEST))
88
+ end
89
+ end
74
90
  end
75
91
 
76
92
  def get_hashed_password(password, realm)
@@ -78,3 +94,8 @@ def get_hashed_password(password, realm)
78
94
  apply_constraints(hash.hash, hash.size, hash.contains_non_alphanumeric?)
79
95
  end
80
96
 
97
+ def get_hashed_password2(password, realm, salt, iterations)
98
+ hash = PwdHash::Hash2.new(realm, password, salt, iterations)
99
+ apply_constraints(hash.hash, hash.size, hash.contains_non_alphanumeric?)
100
+ end
101
+
@@ -1,3 +1,3 @@
1
1
  module PwdHash
2
- VERSION = "0.3.2"
2
+ VERSION = "0.3.4"
3
3
  end
@@ -20,6 +20,4 @@ Gem::Specification.new do |gem|
20
20
  gem.version = PwdHash::VERSION
21
21
 
22
22
  gem.add_dependency('highline', '~> 1.6', '>= 1.6.12')
23
- gem.add_dependency('ruby-hmac', '~> 0.4', '>= 0.4.0')
24
- gem.add_dependency('backports', '~> 3.6', '>= 3.6.4')
25
23
  end
@@ -1,18 +1,72 @@
1
+ require 'minitest'
1
2
  require 'minitest/unit'
2
3
  require 'minitest/pride'
3
4
  require 'minitest/autorun'
4
5
 
5
6
  require './lib/pwdhash/pwdhash'
7
+ require './lib/pwdhash/extract_domain'
6
8
 
7
- class PwdHashTest < MiniTest::Unit::TestCase
8
- def test_passwords
9
+ class PwdHashTest < Minitest::Test
10
+ def test_passwords_with_domains
9
11
  [
10
12
  ['v0F0B', 'foo', 'skype.com'],
11
13
  ['paZTVGZwtewiq1+uCk', 'a l0ng p4assw0rd', 'google.com'],
12
14
  ['nRDL7WNyFODhF29gAkNmpA', 'qwertyuiop0987654321', 'google.com'],
13
15
  ['edi6wHWRQVA1rK8o9zaluwAAAA', 'qwertyuiop0987654321bingo', 'google.com'],
16
+ ['8JyURsRs', 'foobar', 'thetimes.co.uk'],
14
17
  ].each do |expected, password, realm|
15
18
  assert_equal expected, get_hashed_password(password, realm)
16
19
  end
17
20
  end
21
+
22
+ def test_passwords_with_complete_urls
23
+ [
24
+ ['v0F0B', 'foo', 'https://www.whatever.skype.com'],
25
+ ['paZTVGZwtewiq1+uCk', 'a l0ng p4assw0rd', 'https://images.google.com'],
26
+ ['nRDL7WNyFODhF29gAkNmpA', 'qwertyuiop0987654321', 'http://google.com'],
27
+ ['edi6wHWRQVA1rK8o9zaluwAAAA', 'qwertyuiop0987654321bingo', 'https://news.google.com'],
28
+ ['8JyURsRs', 'foobar', 'https://www.thetimes.co.uk/'],
29
+ ].each do |expected, password, url|
30
+ assert_equal expected, get_hashed_password(password, extract_domain(url))
31
+ end
32
+ end
33
+ end
34
+
35
+ class PwdHash2Test < Minitest::Test
36
+ def test_passwords_with_complete_urls
37
+ # expected results calculated with https://gwuk.github.io/PwdHash2/pwdhash2/
38
+ [
39
+ ['5YrAI', 'foo', 10000, 'SomeSalt', 'https://www.skype.com/en/'],
40
+ ['r8oIM4CR', 'foobar', 1, 'ChangeMe', 'https://google.com'],
41
+ ['3PvCifzNoYaTNBMcJzVvDfDBeVK', 'correcthorsebatterystaple', 50000, 'COVwVNWVhwCsd7vlQ2T5BuIJBccYCu1RzR8rQFVHYVkGVQkZXHLkglnttWFQJYIN', 'https://about.google/intl/en/?fg=1&utm_source=google-EN&utm_medium=referral&utm_campaign=hp-header'],
42
+ ['APC8mNJI', 'foobar', 1000, 'ChangeMe', 'https://google.com'],
43
+ ].each do |expected, password, iterations, salt, url|
44
+ assert_equal expected, get_hashed_password2(password, extract_domain(url), salt, iterations)
45
+ end
46
+ end
47
+
48
+ def test_collisions_with_weak_passwords
49
+ # => Use a longer password!
50
+ [
51
+ ['foo', 1000, 'bar', 'https://manifolds.org', 'https://boxwoods.com'],
52
+ ['foo', 50_000, 'aYcErTYgi0AoB2tDbP80fwR5GAWwUvg8', 'http://dainty.co.uk', 'http://polemic.com'],
53
+ ['foobar', 100, 'salt', 'https://abounds.edu.au', 'https://coaxed.co.nz'],
54
+ ].each do |password, iterations, salt, url1, url2|
55
+ assert_equal get_hashed_password2(password, extract_domain(url1), salt, iterations),
56
+ get_hashed_password2(password, extract_domain(url2), salt, iterations)
57
+ end
58
+ end
59
+
60
+ def test_edge_cases
61
+ # For testing only! Please always define salt and password. Number of iterations shouldn't be too low and password should be long enough.
62
+ [
63
+ ['WWNEC9x1', 'foobar', 1000, '', 'https://google.com'],
64
+ ['EBr2', '', 1000, '', 'https://google.com'],
65
+ ['w0WD', '', 1, '', 'https://google.com'],
66
+ ['w0WD', '', 0, '', 'https://google.com'],
67
+ ['w0WD', '', -1, '', 'https://google.com'],
68
+ ].each do |expected, password, iterations, salt, url|
69
+ assert_equal expected, get_hashed_password2(password, extract_domain(url), salt, iterations)
70
+ end
71
+ end
18
72
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwdhash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Yuen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-16 00:00:00.000000000 Z
11
+ date: 2019-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline
@@ -30,51 +30,12 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.6.12
33
- - !ruby/object:Gem::Dependency
34
- name: ruby-hmac
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '0.4'
40
- - - ">="
41
- - !ruby/object:Gem::Version
42
- version: 0.4.0
43
- type: :runtime
44
- prerelease: false
45
- version_requirements: !ruby/object:Gem::Requirement
46
- requirements:
47
- - - "~>"
48
- - !ruby/object:Gem::Version
49
- version: '0.4'
50
- - - ">="
51
- - !ruby/object:Gem::Version
52
- version: 0.4.0
53
- - !ruby/object:Gem::Dependency
54
- name: backports
55
- requirement: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - "~>"
58
- - !ruby/object:Gem::Version
59
- version: '3.6'
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- version: 3.6.4
63
- type: :runtime
64
- prerelease: false
65
- version_requirements: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - "~>"
68
- - !ruby/object:Gem::Version
69
- version: '3.6'
70
- - - ">="
71
- - !ruby/object:Gem::Version
72
- version: 3.6.4
73
33
  description: Command line version of Stanford PwdHash
74
34
  email:
75
35
  - chris@kizzx2.com
76
36
  executables:
77
37
  - pwdhash
38
+ - pwdhash2
78
39
  extensions: []
79
40
  extra_rdoc_files: []
80
41
  files:
@@ -84,6 +45,7 @@ files:
84
45
  - README.markdown
85
46
  - Rakefile
86
47
  - bin/pwdhash
48
+ - bin/pwdhash2
87
49
  - lib/pwdhash/extract_domain.rb
88
50
  - lib/pwdhash/pwdhash.rb
89
51
  - lib/pwdhash/version.rb
@@ -109,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
71
  version: '0'
110
72
  requirements: []
111
73
  rubyforge_project:
112
- rubygems_version: 2.2.2
74
+ rubygems_version: 2.7.6
113
75
  signing_key:
114
76
  specification_version: 4
115
77
  summary: Command line version of Stanford PwdHash