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 +4 -4
- data/HISTORY.md +7 -1
- data/Manifest.txt +2 -0
- data/README.md +51 -31
- data/Rakefile +7 -5
- data/bin/htdigest-ruby +1 -1
- data/bin/htpasswd-ruby +1 -1
- data/lib/htauth/algorithm.rb +13 -1
- data/lib/htauth/argon2.rb +77 -0
- data/lib/htauth/bcrypt.rb +5 -0
- data/lib/htauth/cli/digest.rb +1 -4
- data/lib/htauth/cli/passwd.rb +9 -7
- data/lib/htauth/cli.rb +1 -2
- data/lib/htauth/digest_entry.rb +1 -0
- data/lib/htauth/passwd_entry.rb +4 -15
- data/lib/htauth/passwd_file.rb +6 -6
- data/lib/htauth/sha1.rb +1 -1
- data/lib/htauth/version.rb +1 -1
- data/lib/htauth.rb +1 -1
- data/spec/algorithm_spec.rb +0 -1
- data/spec/argon2_spec.rb +28 -0
- data/spec/bcrypt_spec.rb +0 -1
- data/spec/cli/passwd_spec.rb +17 -1
- data/spec/crypt_spec.rb +0 -1
- data/spec/digest_entry_spec.rb +0 -1
- data/spec/digest_file_spec.rb +0 -1
- data/spec/md5_spec.rb +1 -3
- data/spec/passwd_entry_spec.rb +15 -2
- data/spec/sha1_spec.rb +0 -1
- data/spec/spec_helper.rb +3 -6
- data/tasks/default.rake +17 -9
- metadata +48 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad5523ccfeacb957acb9cf2fae40e19e51bc92e10d093a950949fbe00bb50b1a
|
4
|
+
data.tar.gz: 280ed439110fd03ca5510ae81d8b17a4f0db21f4f0aed7360238bbcef83fe6c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc5ddd224ce189eeb5d16a60059a9fcab868ff65de9aad4efac3177425004d98b22d9af29f556d001ae0c083b31cdaef4cf723c7d1342739fc01c7be12841f42
|
7
|
+
data.tar.gz: 28a12fef7f3c7a96c9bdb186a71b06b8263da2c1f6b77dfcba45239129a17311d9fbcfa799aaf004efbf09876cc380de99bb91cab6be23ffefc23ce70286618b
|
data/HISTORY.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Changelog
|
2
|
-
## Version 2.
|
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
|
11
|
-
htpasswd
|
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
|
17
|
-
|
18
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
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,
|
25
|
-
|
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
|
-
|
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
|
-
|
83
|
+
## Supported Hash Algorithms
|
65
84
|
|
66
|
-
|
67
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
88
|
+
- Built in
|
89
|
+
- Generally accepted
|
90
|
+
- MD5 (default for compatibility reasons)
|
91
|
+
- bcrypt (probably the better choice)
|
75
92
|
|
76
|
-
|
77
|
-
|
78
|
-
|
93
|
+
- **Not Recommended** - available only for backwards compatibility with `htpasswd`
|
94
|
+
- SHA1
|
95
|
+
- crypt
|
96
|
+
- plaintext
|
79
97
|
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
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( '
|
13
|
-
spec.add_development_dependency( '
|
14
|
-
spec.add_development_dependency( 'minitest
|
15
|
-
spec.add_development_dependency( '
|
16
|
-
spec.add_development_dependency( '
|
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
data/bin/htpasswd-ruby
CHANGED
data/lib/htauth/algorithm.rb
CHANGED
@@ -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)
|
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
data/lib/htauth/cli/digest.rb
CHANGED
data/lib/htauth/cli/passwd.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
require 'htauth/
|
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} [-
|
48
|
-
#{op.program_name} -b[
|
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("
|
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
data/lib/htauth/digest_entry.rb
CHANGED
data/lib/htauth/passwd_entry.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
|
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
|
-
|
116
|
+
"#{user}"
|
128
117
|
end
|
129
118
|
|
130
119
|
# Internal: Returns the file line for this entry
|
data/lib/htauth/passwd_file.rb
CHANGED
@@ -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'
|
15
|
-
# pf.add_or_update('someuser', '
|
16
|
-
# pf.delete('someolduser'
|
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
data/lib/htauth/version.rb
CHANGED
data/lib/htauth.rb
CHANGED
data/spec/algorithm_spec.rb
CHANGED
data/spec/argon2_spec.rb
ADDED
@@ -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
data/spec/cli/passwd_spec.rb
CHANGED
@@ -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
data/spec/digest_entry_spec.rb
CHANGED
data/spec/digest_file_spec.rb
CHANGED
data/spec/md5_spec.rb
CHANGED
data/spec/passwd_entry_spec.rb
CHANGED
@@ -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
|
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
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
|
-
|
2
|
-
|
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 '
|
40
|
-
|
41
|
-
t.
|
42
|
-
t.libs
|
43
|
-
t.
|
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
|
-
|
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 <<
|
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.
|
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:
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
97
|
-
description: HTAuth
|
98
|
-
|
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.
|
218
|
+
rubygems_version: 3.5.3
|
190
219
|
signing_key:
|
191
220
|
specification_version: 4
|
192
|
-
summary: HTAuth
|
193
|
-
|
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
|