Skip to content

Commit 275e9b5

Browse files
authored
Merge pull request #1040 from genebean/str2saltedpbkdf2
Adding str2saltedpbkdf2 function
2 parents f1bb15b + a521aa2 commit 275e9b5

File tree

3 files changed

+166
-0
lines changed

3 files changed

+166
-0
lines changed

REFERENCE.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ the provided regular expression.
168168
last Period).
169169
* [`stdlib::ip_in_range`](#stdlibip_in_range): Returns true if the ipaddress is within the given CIDRs
170170
* [`str2bool`](#str2bool): This converts a string to a boolean.
171+
* [`str2saltedpbkdf2`](#str2saltedpbkdf2): Convert a string into a salted SHA512 PBKDF2 password hash like requred for OS X / macOS 10.8+
171172
* [`str2saltedsha512`](#str2saltedsha512): This converts a string to a salted-SHA512 password hash (which is used for
172173
OS X versions >= 10.7).
173174
* [`strftime`](#strftime): This function returns formatted time.
@@ -4477,6 +4478,80 @@ See the function new() in Puppet for details what the Boolean data type supports
44774478
Returns: `Any` This attempt to convert to boolean strings that contain things like: Y,y, 1, T,t, TRUE,true to 'true' and strings that contain things
44784479
like: 0, F,f, N,n, false, FALSE, no to 'false'.
44794480

4481+
### str2saltedpbkdf2
4482+
4483+
Type: Ruby 3.x API
4484+
4485+
Convert a string into a salted SHA512 PBKDF2 password hash like requred for OS X / macOS 10.8+.
4486+
Note, however, that Apple changes what's required periodically and this may not work for the latest
4487+
version of macOS. If that is the case you should get a helpful error message when Puppet tries to set
4488+
the pasword using the parameters you provide to the user resource.
4489+
4490+
#### Examples
4491+
4492+
##### Plain text password and salt
4493+
4494+
```puppet
4495+
$pw_info = str2saltedpbkdf2('Pa55w0rd', 'Using s0m3 s@lt', 50000)
4496+
user { 'jdoe':
4497+
ensure => present,
4498+
iterations => $pw_info['interations'],
4499+
password => $pw_info['password_hex'],
4500+
salt => $pw_info['salt_hex'],
4501+
}
4502+
```
4503+
4504+
##### Sensitive password and salt
4505+
4506+
```puppet
4507+
$pw = Sensitive.new('Pa55w0rd')
4508+
$salt = Sensitive.new('Using s0m3 s@lt')
4509+
$pw_info = Sensitive.new(str2saltedpbkdf2($pw, $salt, 50000))
4510+
user { 'jdoe':
4511+
ensure => present,
4512+
iterations => unwrap($pw_info)['interations'],
4513+
password => unwrap($pw_info)['password_hex'],
4514+
salt => unwrap($pw_info)['salt_hex'],
4515+
}
4516+
```
4517+
4518+
#### `str2saltedpbkdf2()`
4519+
4520+
Convert a string into a salted SHA512 PBKDF2 password hash like requred for OS X / macOS 10.8+.
4521+
Note, however, that Apple changes what's required periodically and this may not work for the latest
4522+
version of macOS. If that is the case you should get a helpful error message when Puppet tries to set
4523+
the pasword using the parameters you provide to the user resource.
4524+
4525+
Returns: `Hash` Provides a hash containing the hex version of the password, the hex version of the salt, and iterations.
4526+
4527+
##### Examples
4528+
4529+
###### Plain text password and salt
4530+
4531+
```puppet
4532+
$pw_info = str2saltedpbkdf2('Pa55w0rd', 'Using s0m3 s@lt', 50000)
4533+
user { 'jdoe':
4534+
ensure => present,
4535+
iterations => $pw_info['interations'],
4536+
password => $pw_info['password_hex'],
4537+
salt => $pw_info['salt_hex'],
4538+
}
4539+
```
4540+
4541+
###### Sensitive password and salt
4542+
4543+
```puppet
4544+
$pw = Sensitive.new('Pa55w0rd')
4545+
$salt = Sensitive.new('Using s0m3 s@lt')
4546+
$pw_info = Sensitive.new(str2saltedpbkdf2($pw, $salt, 50000))
4547+
user { 'jdoe':
4548+
ensure => present,
4549+
iterations => unwrap($pw_info)['interations'],
4550+
password => unwrap($pw_info)['password_hex'],
4551+
salt => unwrap($pw_info)['salt_hex'],
4552+
}
4553+
```
4554+
44804555
### str2saltedsha512
44814556

44824557
Type: Ruby 3.x API
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# str2saltedpbkdf2.rb
2+
# Please note: This function is an implementation of a Ruby class and as such may not be entirely UTF8 compatible. To ensure compatibility please use this function with Ruby 2.4.0 or greater - https://bugs.ruby-lang.org/issues/10085.
3+
#
4+
module Puppet::Parser::Functions
5+
newfunction(:str2saltedpbkdf2, :type => :rvalue, :doc => <<-DOC
6+
@summary Convert a string into a salted SHA512 PBKDF2 password hash like requred for OS X / macOS 10.8+
7+
8+
Convert a string into a salted SHA512 PBKDF2 password hash like requred for OS X / macOS 10.8+.
9+
Note, however, that Apple changes what's required periodically and this may not work for the latest
10+
version of macOS. If that is the case you should get a helpful error message when Puppet tries to set
11+
the pasword using the parameters you provide to the user resource.
12+
13+
@example Plain text password and salt
14+
$pw_info = str2saltedpbkdf2('Pa55w0rd', 'Using s0m3 s@lt', 50000)
15+
user { 'jdoe':
16+
ensure => present,
17+
iterations => $pw_info['interations'],
18+
password => $pw_info['password_hex'],
19+
salt => $pw_info['salt_hex'],
20+
}
21+
22+
@example Sensitive password and salt
23+
$pw = Sensitive.new('Pa55w0rd')
24+
$salt = Sensitive.new('Using s0m3 s@lt')
25+
$pw_info = Sensitive.new(str2saltedpbkdf2($pw, $salt, 50000))
26+
user { 'jdoe':
27+
ensure => present,
28+
iterations => unwrap($pw_info)['interations'],
29+
password => unwrap($pw_info)['password_hex'],
30+
salt => unwrap($pw_info)['salt_hex'],
31+
}
32+
33+
@return [Hash]
34+
Provides a hash containing the hex version of the password, the hex version of the salt, and iterations.
35+
DOC
36+
) do |args|
37+
require 'openssl'
38+
39+
raise ArgumentError, "str2saltedpbkdf2(): wrong number of arguments (#{args.size} for 3)" if args.size != 3
40+
41+
args.map! do |arg|
42+
if (defined? Puppet::Pops::Types::PSensitiveType::Sensitive) && (arg.is_a? Puppet::Pops::Types::PSensitiveType::Sensitive)
43+
arg.unwrap
44+
else
45+
arg
46+
end
47+
end
48+
49+
raise ArgumentError, 'str2saltedpbkdf2(): first argument must be a string' unless args[0].is_a?(String)
50+
raise ArgumentError, 'str2saltedpbkdf2(): second argument must be a string' unless args[1].is_a?(String)
51+
raise ArgumentError, 'str2saltedpbkdf2(): second argument must be at least 8 bytes long' unless args[1].bytesize >= 8
52+
raise ArgumentError, 'str2saltedpbkdf2(): third argument must be an integer' unless args[2].is_a?(Integer)
53+
raise ArgumentError, 'str2saltedpbkdf2(): third argument must be between 40,000 and 70,000' unless args[2] > 40_000 && args[2] < 70_000
54+
55+
password = args[0]
56+
salt = args[1]
57+
iterations = args[2]
58+
keylen = 128
59+
digest = OpenSSL::Digest::SHA512.new
60+
hash = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, keylen, digest)
61+
62+
{
63+
'password_hex' => hash.unpack('H*').first,
64+
'salt_hex' => salt.unpack('H*').first,
65+
'iterations' => iterations,
66+
}
67+
end
68+
end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
require 'spec_helper'
2+
3+
describe 'str2saltedpbkdf2' do
4+
it { is_expected.not_to eq(nil) }
5+
it { is_expected.to run.with_params.and_raise_error(ArgumentError, %r{wrong number of arguments}i) }
6+
it { is_expected.to run.with_params('Pa55w0rd', 2).and_raise_error(ArgumentError, %r{wrong number of arguments}i) }
7+
it { is_expected.to run.with_params(1, 'Using s0m3 s@lt', 50_000).and_raise_error(ArgumentError, %r{first argument must be a string}) }
8+
it { is_expected.to run.with_params('Pa55w0rd', 1, 50_000).and_raise_error(ArgumentError, %r{second argument must be a string}) }
9+
it { is_expected.to run.with_params('Pa55w0rd', 'U', 50_000).and_raise_error(ArgumentError, %r{second argument must be at least 8 bytes long}) }
10+
it { is_expected.to run.with_params('Pa55w0rd', 'Using s0m3 s@lt', '50000').and_raise_error(ArgumentError, %r{third argument must be an integer}) }
11+
it { is_expected.to run.with_params('Pa55w0rd', 'Using s0m3 s@lt', 1).and_raise_error(ArgumentError, %r{third argument must be between 40,000 and 70,000}) }
12+
13+
context 'when running with "Pa55w0rd", "Using s0m3 s@lt",and "50000" as params' do
14+
# rubocop:disable Metrics/LineLength
15+
it {
16+
is_expected.to run.with_params('Pa55w0rd', 'Using s0m3 s@lt', 50_000)
17+
.and_return('password_hex' => '3577f79f7d2e73df1cf1eecc36da16fffcd3650126d79e797a8b227492d13de4cdd0656933b43118b7361692f755e5b3c1e0536f826d12442400f3467bcc8fb4ac2235d5648b0f1b0906d0712aecd265834319b5a42e98af2ced81597fd78d1ac916f6eff6122c3577bb120a9f534e2a5c9a58c7d1209e3914c967c6a467b594',
18+
'salt_hex' => '5573696e672073306d332073406c74',
19+
'iterations' => 50_000)
20+
}
21+
# rubocop:enable Metrics/LineLength
22+
end
23+
end

0 commit comments

Comments
 (0)