htauth 2.2.0 → 2.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a392e544654ec732a878871166ba48129a72dc8e7d2b3b9e1ed37d99117546b9
4
- data.tar.gz: fb244f0b94603e5829d36d10e1309d98608a584c1bc8ea3a3d95f60f9950b7fb
3
+ metadata.gz: ad5523ccfeacb957acb9cf2fae40e19e51bc92e10d093a950949fbe00bb50b1a
4
+ data.tar.gz: 280ed439110fd03ca5510ae81d8b17a4f0db21f4f0aed7360238bbcef83fe6c1
5
5
  SHA512:
6
- metadata.gz: 3350108830c0ce7bb7e406ec2d449c89eaf3ce1047f095cab78cffe8c197fb03eb9a5ffe7ad1b782e6d15c1ed88017ef96cb48e87cd8e91993454e00bc1fffda
7
- data.tar.gz: 3b4183cd67ab796c5de2c2fcee847a94ab7d83e925b5283640bd7718274667e141d1618a10f927cf5c6e70cc5a667c284577d83d449a7566770ea60e5a2bc20e
6
+ metadata.gz: cc5ddd224ce189eeb5d16a60059a9fcab868ff65de9aad4efac3177425004d98b22d9af29f556d001ae0c083b31cdaef4cf723c7d1342739fc01c7be12841f42
7
+ data.tar.gz: 28a12fef7f3c7a96c9bdb186a71b06b8263da2c1f6b77dfcba45239129a17311d9fbcfa799aaf004efbf09876cc380de99bb91cab6be23ffefc23ce70286618b
data/HISTORY.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
- ## Version 2.2.0 - 2023-02-XX
2
+ ## Version 2.3.0 - 2024-02-03
3
+
4
+ * Add support for argon2 encryption [#18](https://github.com/copiousfreetime/htauth/pull/18)
5
+ * Update supported ruby version, now supporting ruby 3.x only
6
+ * Semaphore updates
7
+
8
+ ## Version 2.2.0 - 2023-02-06
3
9
 
4
10
  * Update ruby versions
5
11
  * Add to sempahore for CI
data/Manifest.txt CHANGED
@@ -8,6 +8,7 @@ bin/htdigest-ruby
8
8
  bin/htpasswd-ruby
9
9
  lib/htauth.rb
10
10
  lib/htauth/algorithm.rb
11
+ lib/htauth/argon2.rb
11
12
  lib/htauth/bcrypt.rb
12
13
  lib/htauth/cli.rb
13
14
  lib/htauth/cli/digest.rb
@@ -27,6 +28,7 @@ lib/htauth/plaintext.rb
27
28
  lib/htauth/sha1.rb
28
29
  lib/htauth/version.rb
29
30
  spec/algorithm_spec.rb
31
+ spec/argon2_spec.rb
30
32
  spec/bcrypt_spec.rb
31
33
  spec/cli/digest_spec.rb
32
34
  spec/cli/passwd_spec.rb
data/README.md CHANGED
@@ -7,25 +7,45 @@
7
7
 
8
8
  ## DESCRIPTION
9
9
 
10
- HTAuth is a pure ruby replacement for the Apache support programs htdigest and
11
- htpasswd. Command line and API access are provided for access to htdigest and
12
- htpasswd files.
10
+ HTAuth provides an API and commandline tools for managing Apache/httpd style
11
+ htpasswd and htdigest files.
13
12
 
14
13
  ## FEATURES
15
14
 
16
- HTAuth provides to drop in commands *htdigest-ruby* and *htpasswd-ruby* that
17
- can manipulate the digest and passwd files in the same manner as Apache's
18
- original commands.
15
+ HTAuth provides an API allowing direct manipulation of Apache/httpd style
16
+ `htdigest` and `htpasswd` files. Supporting full programmatic manipulation and
17
+ authentication of user credentials.
19
18
 
20
- *htdigest-ruby* and *htpasswd-ruby* are command line compatible with *htdigest*
21
- and *htpasswd*. They support the same exact same command line options as the
22
- originals, and have some extras.
19
+ HTAuth also includes drop-in, commandline compatible replacements for the Apache
20
+ utilities `htpasswd` and `htdigest` with the respective `htpasswd-ruby` and
21
+ `htdigest-ruby` commands.
23
22
 
24
- Additionally, you can access all the functionality of *htdigest-ruby* and
25
- *htpasswd-ruby* through an API.
23
+ Additionally, support for the [argon2](https://github.com/technion/ruby-argon2)
24
+ password hashing algorithm is provided for most platforms.
26
25
 
27
26
  ## SYNOPSIS
28
27
 
28
+ ### API Usage
29
+
30
+ HTAuth::DigestFile.open("some.htdigest") do |df|
31
+ df.add_or_update('someuser', 'myrealm', 'a password')
32
+ df.delete('someolduser', 'myotherrealm')
33
+ end
34
+
35
+ HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::CREATE) do |pf|
36
+ pf.add('someuser', 'a password', 'md5')
37
+ pf.add('someotheruser', 'a different password', 'sha1')
38
+ end
39
+
40
+ HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::ALTER) do |pf|
41
+ pf.update('someuser', 'a password', 'bcrypt')
42
+ end
43
+
44
+ HTAuth::PasswdFile.open("some.htpasswd") do |pf|
45
+ pf.authenticated?('someuser', 'a password')
46
+ end
47
+
48
+
29
49
  ### htpasswd-ruby command line application
30
50
 
31
51
  Usage:
@@ -35,7 +55,8 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
35
55
  htpasswd-ruby -n[imBdps] [-C cost] username
36
56
  htpasswd-ruby -nb[mBdps] [-C cost] username password
37
57
 
38
- -b, --batch Batch mode, get the password from the command line, rather than prompt
58
+ --argon2 Force argon2 encryption of the password.
59
+ -b, --batch Batch mode, get the password from the command line, rather than prompt.
39
60
  -B, --bcrypt Force bcrypt encryption of the password.
40
61
  -C, --cost COST Set the computing time used for the bcrypt algorithm
41
62
  (higher is more secure but slower, default: 5, valid: 4 to 31).
@@ -49,9 +70,7 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
49
70
  -p, --plaintext Do not encrypt the password (plaintext).
50
71
  -s, --sha1 Force SHA encryption of the password.
51
72
  -v, --version Show version info.
52
- --verify Verify password for the specified user
53
-
54
- The SHA algorithm does not use a salt and is less secure than the MD5 algorithm.
73
+ --verify Verify password for the specified user.
55
74
 
56
75
  ### htdigest-ruby command line application
57
76
 
@@ -61,30 +80,31 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
61
80
  -h, --help Display this help.
62
81
  -v, --version Show version info.
63
82
 
64
- ### API Usage
83
+ ## Supported Hash Algorithms
65
84
 
66
- HTAuth::DigestFile.open("some.htdigest") do |df|
67
- df.add_or_update('someuser', 'myrealm', 'a password')
68
- df.delete('someolduser', 'myotherrealm')
69
- end
85
+ Out of the box, `htauth` supports the classic algorithms that ship with Apache
86
+ `htpasswd`.
70
87
 
71
- HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::CREATE) do |pf|
72
- pf.add('someuser', 'a password', 'md5')
73
- pf.add('someotheruser', 'a different password', 'sha1')
74
- end
88
+ - Built in
89
+ - Generally accepted
90
+ - MD5 (default for compatibility reasons)
91
+ - bcrypt (probably the better choice)
75
92
 
76
- HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::ALTER) do |pf|
77
- pf.update('someuser', 'a password', 'bcrypt')
78
- end
93
+ - **Not Recommended** - available only for backwards compatibility with `htpasswd`
94
+ - SHA1
95
+ - crypt
96
+ - plaintext
79
97
 
80
- HTAuth::PasswdFile.open("some.htpasswd") do |pf|
81
- pf.authenticated?('someuser', 'a password')
82
- end
98
+ - Available with the installation of additional libraries:
99
+ - argon2 - to use, add `gem 'argon2'` to your `Gemfile`. `argon2` will
100
+ now be a valid algorithm to use in `HTAuth::PasswdFile` API. Currently
101
+ argon2 is not supported on windows as the upstream `argon2` gem does not
102
+ support windows.
83
103
 
84
104
  ## CREDITS
85
105
 
86
106
  * [The Apache Software Foundation](http://www.apache.org/)
87
- * all the folks who contributed to htdigest and htpassword
107
+ * all the folks who contributed to htdigest and htpasswd
88
108
 
89
109
  ## MIT LICENSE
90
110
 
data/Rakefile CHANGED
@@ -8,12 +8,14 @@ This.homepage = "http://github.com/copiousfreetime/#{ This.name }"
8
8
 
9
9
  This.ruby_gemspec do |spec|
10
10
  spec.add_dependency( 'bcrypt', '~> 3.1' )
11
+ spec.add_dependency( 'base64', '~> 0.2' )
11
12
 
12
- spec.add_development_dependency( 'rake' , '~> 13.0')
13
- spec.add_development_dependency( 'minitest' , '~> 5.11' )
14
- spec.add_development_dependency( 'minitest-junit' , '~> 1.0' )
15
- spec.add_development_dependency( 'rdoc' , '~> 6.4' )
16
- spec.add_development_dependency( 'simplecov', '~> 0.17' )
13
+ spec.add_development_dependency( 'argon2' , '~> 2.3')
14
+ spec.add_development_dependency( 'rake' , '~> 13.1')
15
+ spec.add_development_dependency( 'minitest' , '~> 5.21' )
16
+ spec.add_development_dependency( 'minitest-junit' , '~> 1.1' )
17
+ spec.add_development_dependency( 'rdoc' , '~> 6.6' )
18
+ spec.add_development_dependency( 'simplecov', '~> 0.21' )
17
19
 
18
20
  spec.metadata = {
19
21
  "bug_tracker_uri" => "https://github.com/copiousfreetime/htauth/issues",
data/bin/htdigest-ruby CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  begin
4
- require 'htauth/cli'
4
+ require 'htauth/cli/digest'
5
5
  rescue LoadError
6
6
  path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
7
7
  raise if $:.include?(path)
data/bin/htpasswd-ruby CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  begin
4
- require 'htauth/cli'
4
+ require 'htauth/cli/passwd'
5
5
  rescue LoadError
6
6
  path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
7
7
  raise if $:.include?(path)
@@ -13,6 +13,8 @@ module HTAuth
13
13
  SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze
14
14
  SALT_LENGTH = 8
15
15
 
16
+ # Public: flag for the argon2 algorithm
17
+ ARGON2 = "argon2".freeze
16
18
  # Public: flag for the bcrypt algorithm
17
19
  BCRYPT = "bcrypt".freeze
18
20
  # Public: flag for the md5 algorithm
@@ -84,7 +86,17 @@ module HTAuth
84
86
  end
85
87
 
86
88
  # Internal
87
- def encode(password) ; end
89
+ def encode(password)
90
+ raise NotImplementedError, "#{self.class.name} must implement #{self.class.name}.encode(password)"
91
+ end
92
+
93
+ # Internal: Does the given password match the digest, the default just
94
+ # encodes and secure compares the result, different algorithms may overide
95
+ # this method
96
+ def verify_password?(password, digest)
97
+ encoded = encode(password)
98
+ self.class.secure_compare(encoded, digest)
99
+ end
88
100
 
89
101
  # Internal: 8 bytes of random items from SALT_CHARS
90
102
  def gen_salt(length = SALT_LENGTH)
@@ -0,0 +1,77 @@
1
+ require 'htauth/algorithm'
2
+ begin
3
+ require 'argon2'
4
+ rescue LoadError
5
+ end
6
+
7
+ module HTAuth
8
+ # Internal: Support of the argon2id algorithm and password format.
9
+
10
+ class Argon2 < Algorithm
11
+ class NotSupportedError < ::HTAuth::InvalidAlgorithmError
12
+ def message
13
+ "Unfortunately Argon2 passwords are not supported on `#{RUBY_PLATFORM} at this time. This because the upstream argon2 gem does not support windows."
14
+ end
15
+ end
16
+ class NotInstalledError < ::HTAuth::InvalidAlgorithmError
17
+ def message
18
+ "Argon2 passwords are supported if the `argon2' gem is installed. Add `gem 'argon2', '~> 2.3'` to your Gemfile"
19
+ end
20
+ end
21
+
22
+ # from upstream, used to help make a nice error message if its not installed
23
+ # https://github.com/technion/ruby-argon2/blob/3388d7e05e8b486ea4ba8bd2aeb1e9988f025f13/lib/argon2/hash_format.rb#L45
24
+ PREFIX = /^\$argon2(id?|d).{,113}/.freeze
25
+ ARGON2_GEM_INSTALLED = defined?(::Argon2)
26
+
27
+ def self.supported?
28
+ !::Gem.win_platform?
29
+ end
30
+
31
+ def self.ensure_available!
32
+ raise NotSupportedError unless supported?
33
+ raise NotInstalledError unless ARGON2_GEM_INSTALLED
34
+ end
35
+
36
+ attr_accessor :options
37
+
38
+ def self.handles?(password_entry)
39
+ return false unless PREFIX.match?(password_entry)
40
+ ensure_available!
41
+
42
+ return ::Argon2::Password.valid_hash?(password_entry)
43
+ end
44
+
45
+ def self.extract_options_from_existing_password_field(existing)
46
+ hash_format = ::Argon2::HashFormat.new(existing)
47
+
48
+ # m_cost on the input is the 2**m_cost, but in the hash its the number of
49
+ # bytes, so need to convert it back to a power of 2, which is the
50
+ # log2(m_cost)
51
+
52
+ {
53
+ t_cost: hash_format.t_cost,
54
+ m_cost: ::Math.log2(hash_format.m_cost).floor,
55
+ p_cost: hash_format.p_cost,
56
+ }
57
+ end
58
+
59
+ def initialize(params = { profile: :rfc_9106_low_memory })
60
+ self.class.ensure_available!
61
+ if existing = (params['existing'] || params[:existing]) then
62
+ @options = self.class.extract_options_from_existing_password_field(existing)
63
+ else
64
+ @options = params
65
+ end
66
+ end
67
+
68
+ def encode(password)
69
+ argon2 = ::Argon2::Password.new(options)
70
+ argon2.create(password)
71
+ end
72
+
73
+ def verify_password?(password, digest)
74
+ ::Argon2::Password.verify_password(password, digest)
75
+ end
76
+ end
77
+ end
data/lib/htauth/bcrypt.rb CHANGED
@@ -31,5 +31,10 @@ module HTAuth
31
31
  def encode(password)
32
32
  ::BCrypt::Password.create(password, :cost => cost)
33
33
  end
34
+
35
+ def verify_password?(password, digest)
36
+ bc = ::BCrypt::Password.new(digest)
37
+ bc.is_password?(password)
38
+ end
34
39
  end
35
40
  end
@@ -1,7 +1,4 @@
1
- require 'htauth/version'
2
- require 'htauth/error'
3
- require 'htauth/digest_file'
4
- require 'htauth/console'
1
+ require 'htauth/cli'
5
2
 
6
3
  require 'ostruct'
7
4
  require 'optparse'
@@ -1,6 +1,4 @@
1
- require 'htauth/error'
2
- require 'htauth/passwd_file'
3
- require 'htauth/console'
1
+ require 'htauth/cli'
4
2
 
5
3
  require 'ostruct'
6
4
  require 'optparse'
@@ -44,8 +42,8 @@ module HTAuth
44
42
  @option_parser = OptionParser.new(nil, 16) do |op|
45
43
  op.banner = <<-EOB
46
44
  Usage:
47
- #{op.program_name} [-cimBdpsD] [-C cost] passwordfile username
48
- #{op.program_name} -b[cmBdpsD] [-C cost] passwordfile username password
45
+ #{op.program_name} [-acimBdpsD] [--verify] [-C cost] passwordfile username
46
+ #{op.program_name} -b[acmBdpsD] [--verify] [-C cost] passwordfile username password
49
47
 
50
48
  #{op.program_name} -n[imBdps] [-C cost] username
51
49
  #{op.program_name} -nb[mBdps] [-C cost] username password
@@ -53,7 +51,11 @@ Usage:
53
51
 
54
52
  op.separator ""
55
53
 
56
- op.on("-b", "--batch", "Batch mode, get the password from the command line, rather than prompt") do |b|
54
+ op.on("--argon2", "Force argon2 encryption of the password.") do |a|
55
+ options.algorithm = Algorithm::ARGON2
56
+ end
57
+
58
+ op.on("-b", "--batch", "Batch mode, get the password from the command line, rather than prompt.") do |b|
57
59
  options.batch_mode = b
58
60
  end
59
61
 
@@ -118,7 +120,7 @@ Usage:
118
120
  options.show_version = v
119
121
  end
120
122
 
121
- op.on("--verify", "Verify password for the specified user") do |v|
123
+ op.on("--verify", "Verify password for the specified user.") do |v|
122
124
  options.operation << :verify
123
125
  end
124
126
 
data/lib/htauth/cli.rb CHANGED
@@ -1,8 +1,7 @@
1
1
  require 'htauth'
2
+ require 'htauth/console'
2
3
  module HTAuth
3
4
  module CLI
4
5
 
5
6
  end
6
7
  end
7
- require 'htauth/cli/digest'
8
- require 'htauth/cli/passwd'
@@ -1,5 +1,6 @@
1
1
  require 'htauth/error'
2
2
  require 'htauth/entry'
3
+ require 'htauth/algorithm'
3
4
  require 'digest/md5'
4
5
 
5
6
  module HTAuth
@@ -62,7 +62,7 @@ module HTAuth
62
62
  # Internal: Create a new Entry with the given user, password, and algorithm
63
63
  def initialize(user, password = nil, alg = Algorithm::DEFAULT, alg_params = {} )
64
64
  @user = user
65
- alg = Algorithm::DEFAULT if alg == Algorithm::EXISTING
65
+ alg = Algorithm::DEFAULT if alg == Algorithm::EXISTING
66
66
  @algorithm = Algorithm.algorithm_from_name(alg, alg_params)
67
67
  @digest = calc_digest(password)
68
68
  end
@@ -106,25 +106,14 @@ module HTAuth
106
106
  end
107
107
 
108
108
  # Public: Check if the given password is the password of this entry
109
- # check the password and make sure it works, in the case that the algorithm is unknown it
110
- # tries all of the ones that it thinks it could be, and marks the algorithm if it matches
111
- # when looking for a matche, we always compare all of them, no short
112
- # circuiting
109
+ #
113
110
  def authenticated?(check_password)
114
- authed = false
115
- if algorithm.kind_of?(Bcrypt) then
116
- bc = ::BCrypt::Password.new(digest)
117
- authed = bc.is_password?(check_password)
118
- else
119
- encoded = algorithm.encode(check_password)
120
- authed = Algorithm.secure_compare(encoded, digest)
121
- end
122
- return authed
111
+ algorithm.verify_password?(check_password, @digest)
123
112
  end
124
113
 
125
114
  # Internal: Returns the key of this entry
126
115
  def key
127
- return "#{user}"
116
+ "#{user}"
128
117
  end
129
118
 
130
119
  # Internal: Returns the file line for this entry
@@ -11,9 +11,9 @@ module HTAuth
11
11
  # Examples
12
12
  #
13
13
  # ::HTAuth::PasswdFile.open("my.passwd") do |pf|
14
- # pf.has_entry?('myuser', 'myrealm')
15
- # pf.add_or_update('someuser', 'myrealm', 'a password')
16
- # pf.delete('someolduser', 'myotherrealm')
14
+ # pf.has_entry?('myuser')
15
+ # pf.add_or_update('someuser', 'a password')
16
+ # pf.delete('someolduser')
17
17
  # end
18
18
  #
19
19
  class PasswdFile < HTAuth::File
@@ -68,7 +68,7 @@ module HTAuth
68
68
  # username - the username of the entry
69
69
  # password - the password of the entry
70
70
  # algorithm - the algorithm to use (default: "md5"). Valid options are:
71
- # "md5", "bcrypt", "sha1", "plaintext", or "crypt"
71
+ # "md5", "bcrypt", "argon2", "sha1", "plaintext", or "crypt"
72
72
  # algorithm_args - key-value pairs of arguments that are passed to the
73
73
  # algorithm, currently this is only used to pass the cost
74
74
  # to the bcrypt algorithm
@@ -96,7 +96,7 @@ module HTAuth
96
96
  # username - the username of the entry
97
97
  # password - the password of the entry
98
98
  # algorithm - the algorithm to use (default: "md5"). Valid options are:
99
- # "md5", "bcrypt", "sha1", "plaintext", or "crypt"
99
+ # "md5", "bcrypt", "argon2", "sha1", "plaintext", or "crypt"
100
100
  # algorithm_args - key-value pairs of arguments that are passed to the
101
101
  # algorithm, currently this is only used to pass the cost
102
102
  # to the bcrypt algorithm
@@ -133,7 +133,7 @@ module HTAuth
133
133
  # username - the username of the entry
134
134
  # password - the password of the entry
135
135
  # algorithm - the algorithm to use (default: "existing"). Valid options are:
136
- # "existing", "md5", "bcrypt", "sha1", "plaintext", or "crypt"
136
+ # "existing", "md5", "bcrypt", "argon2", "sha1", "plaintext", or "crypt"
137
137
  # algorithm_args - key-value pairs of arguments that are passed to the
138
138
  # algorithm, currently this is only used to pass the cost
139
139
  # to the bcrypt algorithm
data/lib/htauth/sha1.rb CHANGED
@@ -16,7 +16,7 @@ module HTAuth
16
16
  end
17
17
 
18
18
  # ignore the params
19
- def initialize(params = {})
19
+ def initialize(params = {})
20
20
  end
21
21
 
22
22
  def encode(password)
@@ -1,4 +1,4 @@
1
1
  module HTAuth
2
2
  # Public: The version of the htauth library
3
- VERSION = "2.2.0"
3
+ VERSION = "2.3.0"
4
4
  end
data/lib/htauth.rb CHANGED
@@ -30,7 +30,7 @@ end
30
30
 
31
31
  require 'htauth/version'
32
32
  require 'htauth/algorithm'
33
- require 'htauth/console'
33
+ require 'htauth/argon2'
34
34
  require 'htauth/bcrypt'
35
35
  require 'htauth/crypt'
36
36
  require 'htauth/digest_entry'
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/algorithm'
3
2
 
4
3
  describe HTAuth::Algorithm do
5
4
  it "raises an error if it encouners an unknown algorithm" do
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+ require 'argon2'
3
+
4
+ describe HTAuth::Argon2 do
5
+
6
+ it "decodes the hash back to the original options" do
7
+ hash = '$argon2id$v=19$m=65536,t=3,p=4$V1ln1M4o1RS7SzWHAtqyWQ$jEHi1Qo2FSBgLAPpOa1mPx6OD/twtjj8M1AlVZwamPg'
8
+ options = HTAuth::Argon2.extract_options_from_existing_password_field(hash)
9
+ _(options).must_equal ::Argon2::Profiles[:rfc_9106_low_memory]
10
+ end
11
+
12
+ it "encrypts the same way that argon2 does by default" do
13
+ argon2 = HTAuth::Argon2.new
14
+ hash = argon2.encode("a secret")
15
+ _(::Argon2::Password.verify_password('a secret', hash)).must_equal true
16
+ end
17
+
18
+ it "allow changing the parameters directly" do
19
+ hash = '$argon2id$v=19$m=262144,t=3,p=4$7DRAuE1yIHPHqISmcyaJTg$M0EErpbxqv8dvrMrQMoGDEA7KQCw67jGdXwtCbRINFs'
20
+ options = HTAuth::Argon2.extract_options_from_existing_password_field(hash)
21
+
22
+ options[:m_cost] = 11
23
+
24
+ argon2 = HTAuth::Argon2.new(options)
25
+ local_hash = argon2.encode("a secret")
26
+ _(::Argon2::Password.verify_password('a secret', local_hash)).must_equal true
27
+ end
28
+ end
data/spec/bcrypt_spec.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/bcrypt'
3
2
 
4
3
  describe HTAuth::Bcrypt do
5
4
  it "encrypts the same way that apache does by default" do
@@ -129,7 +129,23 @@ describe HTAuth::CLI::Passwd do
129
129
  _(@stderr.string).must_match( /ERROR:/m )
130
130
  _(se.status).must_equal 1
131
131
  end
132
-
132
+ end
133
+
134
+ it "creates a new file with one argon2 entry" do
135
+ begin
136
+ @stdin.puts "a secret"
137
+ @stdin.puts "a secret"
138
+ @stdin.rewind
139
+ @htauth.run([ "--argon", "-c", @new_file, "agatha" ])
140
+ rescue SystemExit => se
141
+ _(se.status).must_equal 0
142
+ l = IO.readlines(@new_file)
143
+ fields = l.first.split(':')
144
+ _(fields.first).must_equal "agatha"
145
+ argon2_hash = fields.last
146
+
147
+ _(::Argon2::Password.valid_hash?(argon2_hash)).wont_be_nil
148
+ end
133
149
  end
134
150
 
135
151
  it "does not verify the password from stdin on -i option" do
data/spec/crypt_spec.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/crypt'
3
2
 
4
3
  describe HTAuth::Crypt do
5
4
  it "encrypts the same way that apache does" do
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/digest_entry'
3
2
 
4
3
  describe HTAuth::DigestEntry do
5
4
  before(:each) do
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/digest_file'
3
2
  require 'tempfile'
4
3
 
5
4
  describe HTAuth::DigestFile do
data/spec/md5_spec.rb CHANGED
@@ -1,6 +1,4 @@
1
- require File.join(File.dirname(__FILE__),"spec_helper.rb")
2
-
3
- require 'htauth/md5'
1
+ require 'spec_helper'
4
2
 
5
3
  describe HTAuth::Md5 do
6
4
  it "encrypts the same way that apache does" do
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/passwd_entry'
3
2
 
4
3
  describe HTAuth::PasswdEntry do
5
4
  before(:each) do
@@ -32,6 +31,14 @@ describe HTAuth::PasswdEntry do
32
31
  _(bob.digest).must_equal "b secret"
33
32
  end
34
33
 
34
+ it "encypts correctly for argon2" do
35
+ # Don't do this in real life, do not pass in a salt, let the algorithm generate it internally
36
+ salt = ";L\xCDMRO\v\x13;\x012\x9B'\xEE\\i"
37
+ agatha = HTAuth::PasswdEntry.new("agatha","ag secret", "argon2", {salt_do_not_supply: salt} )
38
+ expected = "$argon2id$v=19$m=65536,t=3,p=4$O0zNTVJPCxM7ATKbJ+5caQ$e7wIsl7AY+uIbN+1StYOKkVCJhOrvX7BxAlQ+sPC+Nc"
39
+ _(agatha.digest).must_equal expected
40
+ end
41
+
35
42
  it "encrypts with crypt as a default, when parsed from crypt()'d line" do
36
43
  bob2 = HTAuth::PasswdEntry.from_line(@bob.to_s)
37
44
  _(bob2.algorithm).must_be_instance_of(HTAuth::Crypt)
@@ -97,6 +104,12 @@ describe HTAuth::PasswdEntry do
97
104
  _(s2.authenticated?("b secret")).must_equal true
98
105
  end
99
106
 
107
+ it "authenticates correctly against argon2" do
108
+ s = HTAuth::PasswdEntry.new("agatha", "ag secret", "argon2")
109
+ s2 = HTAuth::PasswdEntry.from_line(s.to_s)
110
+ _(s2.authenticated?("ag secret")).must_equal true
111
+ end
112
+
100
113
  it "can update the cost of an entry after initialization before encoding password" do
101
114
  s = HTAuth::PasswdEntry.new("brenda", "b secret", "bcrypt")
102
115
  _(s.algorithm.cost).must_equal(::HTAuth::Bcrypt::DEFAULT_APACHE_COST)
@@ -108,7 +121,7 @@ describe HTAuth::PasswdEntry do
108
121
  _(s2.algorithm.cost).must_equal(12)
109
122
  end
110
123
 
111
- it "raises an error if assinging an invalid algorithm" do
124
+ it "raises an error if assigning an invalid algorithm" do
112
125
  b = HTAuth::PasswdEntry.new("brenda", "b secret", "bcrypt")
113
126
  _ { b.algorithm = 42 }.must_raise(HTAuth::InvalidAlgorithmError)
114
127
  end
data/spec/sha1_spec.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/sha1'
3
2
 
4
3
  describe HTAuth::Sha1 do
5
4
  it "encrypts the same way that apache does" do
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,6 @@
1
- if RUBY_VERSION >= '1.9.2' then
2
- require 'simplecov'
3
- puts "Using coverage!"
4
- SimpleCov.start if ENV['COVERAGE']
5
- end
1
+ require 'simplecov'
2
+ SimpleCov.start if ENV['COVERAGE']
6
3
 
7
- gem 'minitest'
8
4
  require 'minitest/autorun'
9
5
  require 'minitest/pride'
10
6
 
@@ -26,3 +22,4 @@ class ConsoleIO < StringIO
26
22
  yield self
27
23
  end
28
24
  end
25
+ require 'htauth'
data/tasks/default.rake CHANGED
@@ -36,11 +36,13 @@ task :develop => "develop:default"
36
36
  # Minitest - standard TestTask
37
37
  #------------------------------------------------------------------------------
38
38
  begin
39
- require 'rake/testtask'
40
- Rake::TestTask.new( :test ) do |t|
41
- t.ruby_opts = %w[ -w ]
42
- t.libs = %w[ lib spec test ]
43
- t.pattern = "{test,spec}/**/{test_*,*_spec}.rb"
39
+ require 'minitest/test_task'
40
+ Minitest::TestTask.create( :test) do |t|
41
+ t.libs << "lib"
42
+ t.libs << "spec"
43
+ t.libs << "test"
44
+ t.warning = true
45
+ t.test_globs = "{test,spec}/**/{test_*,*_spec}.rb"
44
46
  end
45
47
 
46
48
  task :test_requirements
@@ -143,14 +145,20 @@ namespace :fixme do
143
145
  end
144
146
 
145
147
  def local_fixme_files
146
- This.manifest.select { |p| p =~ %r|^tasks/| }
148
+ local_files = This.manifest.select { |p| p =~ %r|^tasks/| }
149
+ local_files << ".semaphore/semaphore.yml"
147
150
  end
148
151
 
149
152
  def outdated_fixme_files
150
153
  local_fixme_files.select do |local|
151
154
  upstream = fixme_project_path( local )
152
- upstream.exist? &&
153
- ( Digest::SHA256.file( local ) != Digest::SHA256.file( upstream ) )
155
+ if upstream.exist? then
156
+ if File.exist?( local ) then
157
+ ( Digest::SHA256.file( local ) != Digest::SHA256.file( upstream ) )
158
+ else
159
+ true
160
+ end
161
+ end
154
162
  end
155
163
  end
156
164
 
@@ -201,7 +209,7 @@ task :gemspec do
201
209
  end
202
210
 
203
211
  # .rbc files from ruby 2.0
204
- CLOBBER << FileList["**/*.rbc"]
212
+ CLOBBER << "**/*.rbc"
205
213
 
206
214
  # The standard gem packaging task, everyone has it.
207
215
  require 'rubygems/package_task'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: htauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Hinegardner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-06 00:00:00.000000000 Z
11
+ date: 2024-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt
@@ -24,79 +24,106 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: base64
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: argon2
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.3'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: rake
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
59
  - - "~>"
32
60
  - !ruby/object:Gem::Version
33
- version: '13.0'
61
+ version: '13.1'
34
62
  type: :development
35
63
  prerelease: false
36
64
  version_requirements: !ruby/object:Gem::Requirement
37
65
  requirements:
38
66
  - - "~>"
39
67
  - !ruby/object:Gem::Version
40
- version: '13.0'
68
+ version: '13.1'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: minitest
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
73
  - - "~>"
46
74
  - !ruby/object:Gem::Version
47
- version: '5.11'
75
+ version: '5.21'
48
76
  type: :development
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
80
  - - "~>"
53
81
  - !ruby/object:Gem::Version
54
- version: '5.11'
82
+ version: '5.21'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: minitest-junit
57
85
  requirement: !ruby/object:Gem::Requirement
58
86
  requirements:
59
87
  - - "~>"
60
88
  - !ruby/object:Gem::Version
61
- version: '1.0'
89
+ version: '1.1'
62
90
  type: :development
63
91
  prerelease: false
64
92
  version_requirements: !ruby/object:Gem::Requirement
65
93
  requirements:
66
94
  - - "~>"
67
95
  - !ruby/object:Gem::Version
68
- version: '1.0'
96
+ version: '1.1'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: rdoc
71
99
  requirement: !ruby/object:Gem::Requirement
72
100
  requirements:
73
101
  - - "~>"
74
102
  - !ruby/object:Gem::Version
75
- version: '6.4'
103
+ version: '6.6'
76
104
  type: :development
77
105
  prerelease: false
78
106
  version_requirements: !ruby/object:Gem::Requirement
79
107
  requirements:
80
108
  - - "~>"
81
109
  - !ruby/object:Gem::Version
82
- version: '6.4'
110
+ version: '6.6'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: simplecov
85
113
  requirement: !ruby/object:Gem::Requirement
86
114
  requirements:
87
115
  - - "~>"
88
116
  - !ruby/object:Gem::Version
89
- version: '0.17'
117
+ version: '0.21'
90
118
  type: :development
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
93
121
  requirements:
94
122
  - - "~>"
95
123
  - !ruby/object:Gem::Version
96
- version: '0.17'
97
- description: HTAuth is a pure ruby replacement for the Apache support programs htdigest
98
- and htpasswd. Command line and API access are provided for access to htdigest and
99
- htpasswd files.
124
+ version: '0.21'
125
+ description: HTAuth provides an API and commandline tools for managing Apache/httpd
126
+ style htpasswd and htdigest files.
100
127
  email: jeremy@copiousfreetime.org
101
128
  executables:
102
129
  - htdigest-ruby
@@ -118,6 +145,7 @@ files:
118
145
  - bin/htpasswd-ruby
119
146
  - lib/htauth.rb
120
147
  - lib/htauth/algorithm.rb
148
+ - lib/htauth/argon2.rb
121
149
  - lib/htauth/bcrypt.rb
122
150
  - lib/htauth/cli.rb
123
151
  - lib/htauth/cli/digest.rb
@@ -137,6 +165,7 @@ files:
137
165
  - lib/htauth/sha1.rb
138
166
  - lib/htauth/version.rb
139
167
  - spec/algorithm_spec.rb
168
+ - spec/argon2_spec.rb
140
169
  - spec/bcrypt_spec.rb
141
170
  - spec/cli/digest_spec.rb
142
171
  - spec/cli/passwd_spec.rb
@@ -186,14 +215,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
215
  - !ruby/object:Gem::Version
187
216
  version: '0'
188
217
  requirements: []
189
- rubygems_version: 3.4.6
218
+ rubygems_version: 3.5.3
190
219
  signing_key:
191
220
  specification_version: 4
192
- summary: HTAuth is a pure ruby replacement for the Apache support programs htdigest
193
- and htpasswd. Command line and API access are provided for access to htdigest and
194
- htpasswd files.
221
+ summary: HTAuth provides an API and commandline tools for managing Apache/httpd style
222
+ htpasswd and htdigest files.
195
223
  test_files:
196
224
  - spec/algorithm_spec.rb
225
+ - spec/argon2_spec.rb
197
226
  - spec/bcrypt_spec.rb
198
227
  - spec/cli/digest_spec.rb
199
228
  - spec/cli/passwd_spec.rb