Class Jabber::SASL::DigestMD5
In: lib/xmpp4r/sasl.rb
Parent: Base

SASL DIGEST-MD5 authentication helper (RFC2831)

Methods

auth   h   hh   new   response_value  

Public Class methods

Sends the wished auth mechanism and wait for a challenge

(proceed with DigestMD5#auth)

[Source]

    # File lib/xmpp4r/sasl.rb, line 74
74:       def initialize(stream)
75:         super
76: 
77:         challenge = {}
78:         error = nil
79:         @stream.send(generate_auth('DIGEST-MD5')) { |reply|
80:           if reply.name == 'challenge' and reply.namespace == NS_SASL
81:             challenge_text = Base64::decode64(reply.text)
82:             challenge_text.split(/,/).each { |s|
83:               key, value = s.split(/=/, 2)
84:               value.sub!(/^"/, '')
85:               value.sub!(/"$/, '')
86:               challenge[key] = value
87:             }
88:           else
89:             error = reply.first_element(nil).name
90:           end
91:           true
92:         }
93:         raise error if error
94: 
95:         @nonce = challenge['nonce']
96:         @realm = challenge['realm']
97:       end

Public Instance methods

  • Send a response
  • Wait for the server‘s challenge (which aren‘t checked)
  • Send a blind response to the server‘s challenge

[Source]

     # File lib/xmpp4r/sasl.rb, line 103
103:       def auth(password)
104:         response = {}
105:         response['nonce'] = @nonce
106:         response['charset'] = 'utf-8'
107:         response['username'] = @stream.jid.node
108:         response['realm'] = @realm || @stream.jid.domain
109:         response['cnonce'] = generate_nonce
110:         response['nc'] = '00000001'
111:         response['qop'] = 'auth'
112:         response['digest-uri'] = "xmpp/#{@stream.jid.domain}"
113:         response['response'] = response_value(@stream.jid.node, @stream.jid.domain, response['digest-uri'], password, @nonce, response['cnonce'], response['qop'])
114:         response.each { |key,value|
115:           unless %w(nc qop response charset).include? key
116:             response[key] = "\"#{value}\""
117:           end
118:         }
119: 
120:         r = REXML::Element.new('response')
121:         r.add_namespace NS_SASL
122:         r.text = Base64::encode64(response.collect { |k,v| "#{k}=#{v}" }.join(',')).gsub(/\s/, '')
123:         error = nil
124:         @stream.send(r) { |reply|
125:           if reply.name != 'challenge'
126:             error = reply.first_element(nil).name
127:           end
128:           true
129:         }
130:         
131:         raise error if error
132: 
133:         # TODO: check the challenge from the server
134: 
135:         r.text = nil
136:         @stream.send(r) { |reply|
137:           if reply.name != 'success'
138:             error = reply.first_element(nil).name
139:           end
140:           true
141:         }
142:         
143:         raise error if error
144:       end

Private Instance methods

Function from RFC2831

[Source]

     # File lib/xmpp4r/sasl.rb, line 150
150:       def h(s); Digest::MD5.digest(s); end

Function from RFC2831

[Source]

     # File lib/xmpp4r/sasl.rb, line 153
153:       def hh(s); Digest::MD5.hexdigest(s); end

Calculate the value for the response field

[Source]

     # File lib/xmpp4r/sasl.rb, line 157
157:       def response_value(username, realm, digest_uri, passwd, nonce, cnonce, qop)
158:         a1_h = h("#{username}:#{realm}:#{passwd}")
159:         a1 = "#{a1_h}:#{nonce}:#{cnonce}"
160:         #a2 = "AUTHENTICATE:#{digest_uri}#{(qop == 'auth') ? '' : ':00000000000000000000000000000000'}"
161:         a2 = "AUTHENTICATE:#{digest_uri}"
162: 
163:         hh("#{hh(a1)}:#{nonce}:00000001:#{cnonce}:#{qop}:#{hh(a2)}")
164:       end

[Validate]