Class: HTTPAuth::Digest::Utils
- Inherits:
-
Object
- Object
- HTTPAuth::Digest::Utils
- Defined in:
- lib/httpauth/digest.rb
Overview
Utils contains all sort of conveniance methods for the header container classes. Implementations shouldn’t have to call any methods on Utils.
Class Method Summary collapse
-
.calculate_digest(h, s, variant) ⇒ Object
Calculate the digest value for the directives as explained in the RFC.
-
.create_nonce(salt) ⇒ Object
Create a nonce value of the time and a salt.
-
.create_opaque ⇒ Object
Create a 32 character long opaque string with a ‘random’ value.
-
.decode_directives(directives, variant) ⇒ Object
Decodes digest directives from a header.
-
.digest_a1(h, s) ⇒ Object
Calculate the H(A1) as explain in the RFC.
-
.digest_concat(*args) ⇒ Object
Concat arguments the way it’s done frequently in the Digest spec.
-
.digest_h(data) ⇒ Object
Calculate the MD5 hexdigest for the string data.
-
.digest_kd(secret, data) ⇒ Object
Calculate the KD value of a secret and data as explained in the RFC.
-
.encode_directives(h, variant) ⇒ Object
Encodes a hash with digest directives to send in a header.
-
.filter_h_on(h, keys) ⇒ Object
Return a hash with the keys in
keys
found inh
. -
.htdigest(username, realm, password) ⇒ Object
Calculate the Digest for the credentials.
-
.request_digest_a2(h) ⇒ Object
Calculate the H(A2) for the Authorize header as explained in the RFC.
-
.response_digest_a2(h) ⇒ Object
Calculate the H(A2) for the Authentication-Info header as explained in the RFC.
Class Method Details
.calculate_digest(h, s, variant) ⇒ Object
Calculate the digest value for the directives as explained in the RFC.
-
variant
: Either:request
or:response
, as seen from the server.
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# 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) ⇒ Object
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.
227 228 229 230 231 232 233 234 235 236 |
# 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 ⇒ Object
Create a 32 character long opaque string with a ‘random’ value
239 240 241 242 243 |
# 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) ⇒ Object
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).
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# 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) ⇒ Object
Calculate the H(A1) as explain in the RFC. If h is set, it’s used instead of calculating H(username “:” realm “:” password).
159 160 161 162 163 164 165 166 167 168 169 170 |
# 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) ⇒ Object
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"
138 139 140 |
# File 'lib/httpauth/digest.rb', line 138 def digest_concat(*args) args.join ':' end |
.digest_h(data) ⇒ Object
Calculate the MD5 hexdigest for the string data
143 144 145 |
# File 'lib/httpauth/digest.rb', line 143 def digest_h(data) ::Digest::MD5.hexdigest data end |
.digest_kd(secret, data) ⇒ Object
Calculate the KD value of a secret and data as explained in the RFC.
148 149 150 |
# File 'lib/httpauth/digest.rb', line 148 def digest_kd(secret, data) digest_h digest_concat(secret, data) end |
.encode_directives(h, variant) ⇒ Object
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).
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 |
# 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) ⇒ Object
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}
219 220 221 |
# 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) ⇒ Object
Calculate the Digest for the credentials
153 154 155 |
# File 'lib/httpauth/digest.rb', line 153 def htdigest(username, realm, password) digest_h digest_concat(username, realm, password) end |
.request_digest_a2(h) ⇒ Object
Calculate the H(A2) for the Authorize header as explained in the RFC.
173 174 175 176 177 178 179 180 |
# 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) ⇒ Object
Calculate the H(A2) for the Authentication-Info header as explained in the RFC.
183 184 185 186 187 188 189 |
# 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 |