Puppet Function: htpasswd::ht_md5

Defined in:
lib/puppet/functions/htpasswd/ht_md5.rb
Function type:
Ruby 4.x API

Overview

htpasswd::ht_md5(String $password, String $salt)String

Parameters:

  • password (String)
  • salt (String)

Returns:

  • (String)


4
5
6
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
80
81
82
83
84
85
86
87
# File 'lib/puppet/functions/htpasswd/ht_md5.rb', line 4

Puppet::Functions.create_function(:'htpasswd::ht_md5') do
  dispatch :ht_md5 do
    param 'String', :password
    param 'String', :salt
    return_type 'String'
  end

  # from https://github.com/copiousfreetime/htauth/blob/master/lib/htauth/algorithm.rb
  # this is not the Base64 encoding, this is the to64() method from apr
  SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze unless defined? SALT_CHARS
  def to_64(number, rounds)
    r = StringIO.new
    rounds.times do |x|
      r.print(SALT_CHARS[number % 64])
      number >>= 6
    end
    return r.string
  end

  # from https://github.com/copiousfreetime/htauth/blob/master/lib/htauth/md5.rb
  DIGEST_LENGTH = 16 unless defined? DIGEST_LENGTH
  def ht_md5(password, salt)
    prefix = '$apr1$'

    primary = ::Digest::MD5.new
    primary << password
    primary << prefix
    primary << salt

    md5_t = ::Digest::MD5.digest("#{password}#{salt}#{password}")

    l = password.length
    while l > 0 do
      slice_size = ( l > DIGEST_LENGTH ) ? DIGEST_LENGTH : l
      primary << md5_t[0, slice_size]
      l -= DIGEST_LENGTH
    end

    # weirdness
    l = password.length
    while l != 0
      case (l & 1)
      when 1
        primary << 0.chr
      when 0
        primary << password[0,1]
      end
      l >>= 1
    end

    pd = primary.digest

    encoded_password = "#{prefix}#{salt}$"

    # apr_md5_encode has this comment about a 60Mhz Pentium above this loop.
    1000.times do |x|
      ctx = ::Digest::MD5.new
      ctx << (( ( x & 1 ) == 1 ) ? password : pd[0,DIGEST_LENGTH])
      (ctx << salt) unless ( x % 3 ) == 0
      (ctx << password) unless ( x % 7 ) == 0
      ctx << (( ( x & 1 ) == 0 ) ? password : pd[0,DIGEST_LENGTH])
      pd = ctx.digest
    end


    l = (pd[ 0].ord<<16) | (pd[ 6].ord<<8) | pd[12].ord
    encoded_password << to_64(l, 4)

    l = (pd[ 1].ord<<16) | (pd[ 7].ord<<8) | pd[13].ord
    encoded_password << to_64(l, 4)

    l = (pd[ 2].ord<<16) | (pd[ 8].ord<<8) | pd[14].ord
    encoded_password << to_64(l, 4)

    l = (pd[ 3].ord<<16) | (pd[ 9].ord<<8) | pd[15].ord
    encoded_password << to_64(l, 4)

    l = (pd[ 4].ord<<16) | (pd[10].ord<<8) | pd[ 5].ord
    encoded_password << to_64(l, 4)
    encoded_password << to_64(pd[11].ord,2)

    return encoded_password
  end
end