7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 
       | 
      
        # File 'lib/puppet/functions/postgresql/postgresql_password.rb', line 7
Puppet::Functions.create_function(:'postgresql::postgresql_password') do
                            dispatch :default_impl do
    required_param 'Variant[String[1], Integer]', :username
    required_param 'Variant[String[1], Sensitive[String[1]], Integer]', :password
    optional_param 'Boolean', :sensitive
    optional_param "Optional[Enum['md5', 'scram-sha-256']]", :hash
    optional_param 'Optional[Variant[String[1], Integer]]', :salt
    return_type 'Variant[String, Sensitive[String]]'
  end
  def default_impl(username, password, sensitive = false, hash = 'md5', salt = nil)
    if password.is_a?(String) && password.match?(%r{^(md5|SCRAM-SHA-256).+})
      return password
    end
    password = password.unwrap if password.respond_to?(:unwrap)
    pass = if hash == 'md5'
             'md5' + Digest::MD5.hexdigest(password.to_s + username.to_s)
           else
             pg_sha256(password, (salt || username))
           end
    if sensitive
      Puppet::Pops::Types::PSensitiveType::Sensitive.new(pass)
    else
      pass
    end
  end
  def pg_sha256(password, salt)
    digest = digest_key(password, salt)
    'SCRAM-SHA-256$%{iterations}:%{salt}$%{client_key}:%{server_key}' % {
      iterations: '4096',
      salt: Base64.strict_encode64(salt),
      client_key: Base64.strict_encode64(client_key(digest)),
      server_key: Base64.strict_encode64(server_key(digest)),
    }
  end
  def digest_key(password, salt)
    OpenSSL::KDF.pbkdf2_hmac(
      password,
      salt: salt,
      iterations: 4096,
      length: 32,
      hash: OpenSSL::Digest::SHA256.new,
    )
  end
  def client_key(digest_key)
    hmac = OpenSSL::HMAC.new(digest_key, OpenSSL::Digest::SHA256.new)
    hmac << 'Client Key'
    hmac.digest
    OpenSSL::Digest.new('SHA256').digest hmac.digest
  end
  def server_key(digest_key)
    hmac = OpenSSL::HMAC.new(digest_key, OpenSSL::Digest::SHA256.new)
    hmac << 'Server Key'
    hmac.digest
  end
end
       |