Mercurial > eldonilo > lightstring
comparison plugins/DIGEST-MD5.js @ 64:d9f5ae0b6d98
Support for DIGEST-MD5 authentication. (plugin)
author | Sonny Piers <sonny.piers@gmail.com> |
---|---|
date | Wed, 01 Feb 2012 19:47:49 +0100 |
parents | |
children | 2e8fbf3bce7f |
comparison
equal
deleted
inserted
replaced
63:20da4fb67977 | 64:d9f5ae0b6d98 |
---|---|
1 'use strict'; | |
2 | |
3 /** | |
4 Copyright (c) 2012, Sonny Piers <sonny at fastmail dot net> | |
5 | |
6 Permission to use, copy, modify, and/or distribute this software for any | |
7 purpose with or without fee is hereby granted, provided that the above | |
8 copyright notice and this permission notice appear in all copies. | |
9 | |
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 Lightstring.plugins['DIGEST-MD5'] = { | |
20 handlers: { | |
21 'mechanisms': function (stanza) { | |
22 if(stanza.mechanisms.indexOf('DIGEST-MD5') === -1) | |
23 return; | |
24 | |
25 this.send( | |
26 "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'" + | |
27 " mechanism='DIGEST-MD5'/>" | |
28 ); | |
29 }, | |
30 'success': function (stanza) { | |
31 this.send( | |
32 "<stream:stream to='" + this.jid.domain + "'" + | |
33 " xmlns='jabber:client'" + | |
34 " xmlns:stream='http://etherx.jabber.org/streams'" + | |
35 " version='1.0'/>" | |
36 ); | |
37 }, | |
38 'features': function (stanza) { | |
39 var that = this; | |
40 //TODO check if bind supported | |
41 var bind = | |
42 "<iq type='set' id='"+Lightstring.newId('sendiq:')+"' xmlns='jabber:client'>" + | |
43 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>" + | |
44 (this.jid.resource? "<resource>" + this.jid.resource + "</resource>": "") + | |
45 "</bind>" + | |
46 "</iq>"; | |
47 this.send( | |
48 bind, | |
49 //Success | |
50 function(stanza) { | |
51 //Session http://xmpp.org/rfcs/rfc3921.html#session | |
52 that.jid = new Lightstring.JID(stanza.DOM.textContent); | |
53 that.send( | |
54 "<iq type='set' id='"+Lightstring.newId('sendiq:')+"' xmlns='jabber:client'>" + | |
55 "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>" + | |
56 "</iq>", | |
57 function() { | |
58 that.emit('connected'); | |
59 } | |
60 ); | |
61 }, | |
62 //Error | |
63 function(stanza) { | |
64 //TODO: Error? | |
65 } | |
66 ); | |
67 }, | |
68 'challenge': function (stanza) { | |
69 //FIXME this is mostly Strophe code | |
70 | |
71 function _quote(str) { | |
72 return '"' + str.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'; | |
73 }; | |
74 | |
75 var challenge = atob(stanza.DOM.textContent); | |
76 | |
77 var attribMatch = /([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/; | |
78 | |
79 var cnonce = MD5.hexdigest(Math.random() * 1234567890); | |
80 var realm = ''; | |
81 var host = null; | |
82 var nonce = ''; | |
83 var qop = ''; | |
84 var matches; | |
85 | |
86 while (challenge.match(attribMatch)) { | |
87 matches = challenge.match(attribMatch); | |
88 challenge = challenge.replace(matches[0], ''); | |
89 matches[2] = matches[2].replace(/^"(.+)"$/, '$1'); | |
90 switch (matches[1]) { | |
91 case 'realm': | |
92 realm = matches[2]; | |
93 break; | |
94 case 'nonce': | |
95 nonce = matches[2]; | |
96 break; | |
97 case 'qop': | |
98 qop = matches[2]; | |
99 break; | |
100 case 'host': | |
101 host = matches[2]; | |
102 break; | |
103 } | |
104 } | |
105 | |
106 var digest_uri = 'xmpp/' + this.jid.domain; | |
107 if (host !== null) | |
108 digest_uri = digest_uri + '/' + host; | |
109 var A1 = MD5.hash(this.jid.node + | |
110 ':' + realm + ':' + this.password) + | |
111 ':' + nonce + ':' + cnonce; | |
112 var A2 = 'AUTHENTICATE:' + digest_uri; | |
113 | |
114 var responseText = ''; | |
115 responseText += 'username=' + _quote(this.jid.node) + ','; | |
116 responseText += 'realm=' + _quote(realm) + ','; | |
117 responseText += 'nonce=' + _quote(nonce) + ','; | |
118 responseText += 'cnonce=' + _quote(cnonce) + ','; | |
119 responseText += 'nc="00000001",'; | |
120 responseText += 'qop="auth",'; | |
121 responseText += 'digest-uri=' + _quote(digest_uri) + ','; | |
122 responseText += 'response=' + _quote( | |
123 MD5.hexdigest(MD5.hexdigest(A1) + ':' + | |
124 nonce + ':00000001:' + | |
125 cnonce + ':auth:' + | |
126 MD5.hexdigest(A2))) + ','; | |
127 responseText += 'charset="utf-8"'; | |
128 this.send( | |
129 "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + | |
130 btoa(responseText) + | |
131 "</response>"); | |
132 }, | |
133 'failure': function (stanza) { | |
134 //TODO: throw an error? | |
135 } | |
136 } | |
137 }; |