Skip to content

Commit 5ae71be

Browse files
committed
pw_hash: add support for bcrypt variants
This is the hash algorithm used by default by Elasticsearch among others
1 parent 276e7b7 commit 5ae71be

File tree

2 files changed

+31
-10
lines changed

2 files changed

+31
-10
lines changed

lib/puppet/parser/functions/pw_hash.rb

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@
1919
will be converted into the appropriate crypt(3) hash specifier. Valid
2020
hash types are:
2121
22-
|Hash type |Specifier|
23-
|---------------------|---------|
24-
|MD5 |1 |
25-
|SHA-256 |5 |
26-
|SHA-512 (recommended)|6 |
22+
|Hash type|Prefix|Note |
23+
|---------|------|--------------------|
24+
|MD5 |1 | |
25+
|SHA-256 |5 | |
26+
|SHA-512 |6 |Recommended |
27+
|bcrypt |2b | |
28+
|bcrypt-a |2a |bug compatible |
29+
|bcrypt-x |2x |bug compatible |
30+
|bcrypt-y |2y |historic alias to 2b|
2731
2832
The third argument to this function is the salt to use.
2933
@@ -43,21 +47,31 @@
4347
arg
4448
end
4549
end
50+
51+
hashes = {
52+
'md5' => { :prefix => '1' },
53+
'sha-256' => { :prefix => '5' },
54+
'sha-512' => { :prefix => '6' },
55+
'bcrypt' => { :prefix => '2b', :salt => %r{^[0-9]{2}\$[./A-Za-z0-9]{22}} },
56+
'bcrypt-a' => { :prefix => '2a', :salt => %r{^[0-9]{2}\$[./A-Za-z0-9]{22}} },
57+
'bcrypt-x' => { :prefix => '2x', :salt => %r{^[0-9]{2}\$[./A-Za-z0-9]{22}} },
58+
'bcrypt-y' => { :prefix => '2y', :salt => %r{^[0-9]{2}\$[./A-Za-z0-9]{22}} },
59+
}
60+
4661
raise ArgumentError, 'pw_hash(): first argument must be a string' unless args[0].is_a?(String) || args[0].nil?
4762
raise ArgumentError, 'pw_hash(): second argument must be a string' unless args[1].is_a? String
48-
hashes = { 'md5' => '1',
49-
'sha-256' => '5',
50-
'sha-512' => '6' }
5163
hash_type = hashes[args[1].downcase]
5264
raise ArgumentError, "pw_hash(): #{args[1]} is not a valid hash type" if hash_type.nil?
5365
raise ArgumentError, 'pw_hash(): third argument must be a string' unless args[2].is_a? String
5466
raise ArgumentError, 'pw_hash(): third argument must not be empty' if args[2].empty?
55-
raise ArgumentError, 'pw_hash(): characters in salt must be in the set [a-zA-Z0-9./]' unless %r{\A[a-zA-Z0-9./]+\z}.match?(args[2])
67+
salt_doc = hash_type.include?(:salt) ? "match #{hash_type[:salt]}" : 'be in the set [a-zA-Z0-9./]'
68+
salt_regex = hash_type.fetch(:salt, %r{\A[a-zA-Z0-9./]+\z})
69+
raise ArgumentError, "pw_hash(): characters in salt must #{salt_doc}" unless salt_regex.match?(args[2])
5670

5771
password = args[0]
5872
return nil if password.nil? || password.empty?
5973

60-
salt = "$#{hash_type}$#{args[2]}"
74+
salt = "$#{hash_type[:prefix]}$#{args[2]}"
6175

6276
# handle weak implementations of String#crypt
6377
# dup the string to get rid of frozen status for testing

spec/functions/pw_hash_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@
5252

5353
context 'when the third argument contains invalid characters' do
5454
it { is_expected.to run.with_params('password', 'sha-512', 'one%').and_raise_error(ArgumentError, %r{characters in salt must be in the set}) }
55+
it { is_expected.to run.with_params('password', 'bcrypt', '1234').and_raise_error(ArgumentError, %r{characters in salt must match}) }
56+
end
57+
58+
context 'when run' do
59+
it { is_expected.to run.with_params('password', 'sha-512', '1234').and_return(%r{^\$6\$1234\$}) }
60+
it { is_expected.to run.with_params('password', 'bcrypt', '05$abcdefghijklmnopqrstuv').and_return(%r{^\$2b\$05\$abcdefghijklmnopqrstu}) }
61+
it { is_expected.to run.with_params('password', 'bcrypt-y', '05$abcdefghijklmnopqrstuv').and_return(%r{^\$2y\$05\$abcdefghijklmnopqrstu}) }
5562
end
5663

5764
context 'when running on a platform with a weak String#crypt implementation' do

0 commit comments

Comments
 (0)