Parent

Files

HTTPAuth::Digest::Utils

Utils contains all sort of conveniance methods for the header container classes. Implementations shouldn't have to call any methods on Utils.

Public Class Methods

calculate_digest(h, s, variant) click to toggle source

Calculate the digest value for the directives as explained in the RFC.

  • variant: Either :request or :response, as seen from the server.

# File lib/httpauth/digest.rb, line 194
def calculate_digest(h, s, variant)
  fail(ArgumentError, "Variant should be either :request or :response, not #{variant}") unless [:request, :response].include?(variant)
  # Compatability with RFC 2069
  if h[:qop].nil?
    digest_kd digest_a1(h, s), digest_concat(
      h[:nonce],
      send("#{variant}_digest_a2".intern, h)
    )
  else
    digest_kd digest_a1(h, s), digest_concat(
      h[:nonce],
      Conversions.int_to_hex(h[:nc]),
      h[:cnonce],
      h[:qop],
      send("#{variant}_digest_a2".intern, h)
    )
  end
end
create_nonce(salt) click to toggle source

Create a nonce value of the time and a salt. The nonce is created in such a way that the issuer can check the age of the nonce.

  • salt: A reasonably long passphrase known only to the issuer.

# File lib/httpauth/digest.rb, line 227
def create_nonce(salt)
  now = Time.now
  time = now.strftime('%Y-%m-%d %H:%M:%S').to_s + ':' + now.usec.to_s
  Base64.encode64(
  digest_concat(
      time,
      digest_h(digest_concat(time, salt))
    )
  ).gsub("\n", '')[0..-3]
end
create_opaque() click to toggle source

Create a 32 character long opaque string with a ‘random’ value

# File lib/httpauth/digest.rb, line 239
def create_opaque
  s = []
  16.times { s << rand(127).chr }
  digest_h s.join
end
decode_directives(directives, variant) click to toggle source

Decodes digest directives from a header. Returns a hash with directives.

  • directives: The directives

  • variant: Specifies whether the directives are for an Authorize header (:credentials), for a WWW-Authenticate header (:challenge) or for a Authentication-Info header (:auth_info).

# File lib/httpauth/digest.rb, line 89
def decode_directives(directives, variant)
  fail(HTTPAuth::UnwellformedHeader, "Can't decode directives which are nil") if directives.nil?
  decode = {:domain => :space_quoted_string_to_list, :algorithm => false, :stale => :str_to_bool, :nc => :hex_to_int}
  if [:credentials, :auth].include? variant
    decode.merge! :qop => false
  elsif variant == :challenge
    decode.merge! :qop => :comma_quoted_string_to_list
  else
    fail(ArgumentError, "#{variant} is not a valid value for `variant' use :auth, :credentials or :challenge")
  end

  start = 0
  unless variant == :auth
    # The first six characters are 'Digest '
    start = 6
    scheme = directives[0..6].strip
    fail(HTTPAuth::UnwellformedHeader, "Scheme should be Digest, server responded with `#{directives}'") unless scheme == 'Digest'
  end

  # The rest are the directives
  # TODO: split is ugly, I want a real parser (:
  directives[start..-1].split(',').inject({}) do |h, part|
    parts = part.split('=')
    name = parts[0].strip.intern
    value = parts[1..-1].join('=').strip

    # --- HACK
    # IE and Safari qoute qop values
    # IE also quotes algorithm values
    if variant != :challenge && [:qop, :algorithm].include?(name) && value =~ /^\"[^\"]+\"$/
      value = Conversions.unquote_string(value)
    end
    # --- END HACK

    if decode[name]
      h[name] = Conversions.send decode[name], value
    elsif decode[name].nil?
      h[name] = Conversions.unquote_string value
    else
      h[name] = value
    end
    h
  end
end
digest_a1(h, s) click to toggle source

Calculate the H(A1) as explain in the RFC. If h is set, it’s used instead of calculating H(username “:” realm “:” password).

# File lib/httpauth/digest.rb, line 159
def digest_a1(h, s)
  # TODO: check for known algorithm values (look out for the IE algorithm quote bug)
  if h[:algorithm] == 'MD5-sess'
    digest_h digest_concat(
      h[:digest] || htdigest(h[:username], h[:realm], h[:password]),
      h[:nonce],
      h[:cnonce]
    )
  else
    h[:digest] || htdigest(h[:username], h[:realm], h[:password])
  end
end
digest_concat(*args) click to toggle source

Concat arguments the way it’s done frequently in the Digest spec.

digest_concat('a', 'b') #=> "a:b"
digest_concat('a', 'b', c') #=> "a:b:c"
# File lib/httpauth/digest.rb, line 138
def digest_concat(*args)
  args.join ':'
end
digest_h(data) click to toggle source

Calculate the MD5 hexdigest for the string data

# File lib/httpauth/digest.rb, line 143
def digest_h(data)
  ::Digest::MD5.hexdigest data
end
digest_kd(secret, data) click to toggle source

Calculate the KD value of a secret and data as explained in the RFC.

# File lib/httpauth/digest.rb, line 148
def digest_kd(secret, data)
  digest_h digest_concat(secret, data)
end
encode_directives(h, variant) click to toggle source

Encodes a hash with digest directives to send in a header.

  • h: The directives specified in a hash

  • variant: Specifies whether the directives are for an Authorize header (:credentials), for a WWW-Authenticate header (:challenge) or for a Authentication-Info header (:auth_info).

# File lib/httpauth/digest.rb, line 56
def encode_directives(h, variant)
  encode = {:domain => :list_to_space_quoted_string, :algorithm => false, :stale => :bool_to_str, :nc => :int_to_hex}
  if [:credentials, :auth].include? variant
    encode.merge! :qop => false
  elsif variant == :challenge
    encode.merge! :qop => :list_to_comma_quoted_string
  else
    fail(ArgumentError, "#{variant} is not a valid value for `variant' use :auth, :credentials or :challenge")
  end
  (variant == :auth ? '' : 'Digest ') + h.collect do |directive, value|
    '' << directive.to_s << '=' << if encode[directive]
      begin
        Conversions.send encode[directive], value
      rescue NoMethodError, ArgumentError
        raise(ArgumentError, "Can't encode #{directive}(#{value.inspect}) with #{encode[directive]}")
      end
    elsif encode[directive].nil?
      begin
        Conversions.quote_string value
      rescue NoMethodError, ArgumentError
        raise(ArgumentError, "Can't encode #{directive}(#{value.inspect}) with quote_string")
      end
    else
      value
    end
  end.join(', ')
end
filter_h_on(h, keys) click to toggle source

Return a hash with the keys in keys found in h.

Example

filter_h_on({1=>1,2=>2}, [1]) #=> {1=>1}
filter_h_on({1=>1,2=>2}, [1, 2]) #=> {1=>1,2=>2}
# File lib/httpauth/digest.rb, line 219
def filter_h_on(h, keys)
  h.inject({}) { |a, e| keys.include?(e[0]) ? a.merge(e[0] => e[1]) : a }
end
htdigest(username, realm, password) click to toggle source

Calculate the Digest for the credentials

# File lib/httpauth/digest.rb, line 153
def htdigest(username, realm, password)
  digest_h digest_concat(username, realm, password)
end
request_digest_a2(h) click to toggle source

Calculate the H(A2) for the Authorize header as explained in the RFC.

# File lib/httpauth/digest.rb, line 173
def request_digest_a2(h)
  # TODO: check for known qop values (look out for the safari qop quote bug)
  if h[:qop] == 'auth-int'
    digest_h digest_concat(h[:method], h[:uri], digest_h(h[:request_body]))
  else
    digest_h digest_concat(h[:method], h[:uri])
  end
end
response_digest_a2(h) click to toggle source

Calculate the H(A2) for the Authentication-Info header as explained in the RFC.

# File lib/httpauth/digest.rb, line 183
def response_digest_a2(h)
  if h[:qop] == 'auth-int'
    digest_h ':' + digest_concat(h[:uri], digest_h(h[:response_body]))
  else
    digest_h ':' + h[:uri]
  end
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.