Annotation of embedaddon/coova-chilli/www/ChilliLibrary.js, revision 1.1.1.1
1.1 misho 1: /**
2: * ChilliLibrary.js
3: * V2.0
4: *
5: * This Javascript library can be used to create HTML/JS browser
6: * based smart clients (BBSM) for the CoovaChilli access controller
7: * Coova Chilli rev 81 or higher is required
8: *
9: * This library creates four global objects :
10: *
11: * - chilliController Expose session/client state and
12: * connect()/disconnect() methods the to BBSM.
13: *
14: * - chilliJSON INTERNAL (should not be called from the BBSM).
15: * Issues a command to the chilli daemon by adding a new <SCRIPT>
16: * tag to the HTML DOM (this hack enables cross server requests).
17: *
18: * - chilliClock Can be used by BBSMs to display a count down.
19: * Will sync with chilliController for smooth UI display (not yet implemented)
20: *
21: * - chilliLibrary Expose API and library versions
22: *
23: * For more information http://coova.org/wiki/index.php/CoovaChilli/JSON
24: *
25: * TODO :
26: * - Fine tune level of debug messages
27: * - Define error code when invoking onError
28: * - Retry mechanism after a JSON request fails
29: * - Delay clock tick when there is already an ongoing request
30: * - Use a true JSON parser to validate what we received
31: * - Use idleTime and idleTimeout to re-schedule autofresh after
32: * a likely idle termination by chilli
33: * - check that the library can be compiled as a Flash swf library
34: * and used from Flash BBSMs with the same API.
35: *
36: * Copyright (C) Y.Deltroo 2007
37: * Distributed under the BSD License
38: *
39: * This file also contains third party code :
40: * - MD5, distributed under the BSD license
41: * http://pajhome.org.uk/crypt/md5
42: *
43: */
44:
45: var chilliLibrary = { revision:'85' , apiVersion:'2.0' } ;
46:
47:
48: /**
49: * Global chilliController object
50: *
51: * CONFIGUARION PROPERTIES
52: * -----------------------
53: * ident (String)
54: * Hex encoded string (used for client side CHAP-Password calculations)
55: *
56: * interval (Number)
57: * Poll the gateway every interval, in seconds
58: *
59: * host (String)
60: * IP address of the controller (String)
61: *
62: * port (Number)
63: * UAM port to direct request to on the gateway
64: *
65: * ssl (Boolean)
66: * Shall we use HTTP or HTTPS to communicate with the chilli controller
67: *
68: * uamService : String
69: * !!! EXPERIMENTAL FEATURE !!!
70: * URL to external uamService script (used for external MD5 calculation when portal/chilli trust is required)
71: * This remote script runs on a SSL enable web server, and knows UAM SECRET.
72: * The chilliController javascript object will send the password over SSL (and challenge for CHAP)
73: * UAM SERVICE should reply with a JSON response containing
74: * - CHAP logon : CHAP-Password X0Red with UAM SECRET
75: * - PAP logon : Password XORed with UAM SECRET
76: *
77: * For more information http://coova.org/wiki/index.php/CoovaChilli/JSON
78: *
79: */
80:
81: var chilliController = { interval:30 , host:"192.168.182.1" , port:3990 , ident:'00' , ssl:false , uamService: '' };
82:
83: /* Define clientState numerical code constants */
84: chilliController.stateCodes = { UNKNOWN:-1 , NOT_AUTH:0 , AUTH:1 , AUTH_PENDING:2 , AUTH_SPLASH:3 } ;
85:
86: /* Initializing session and accounting members, objet properties */
87: chilliController.session = {} ;
88: chilliController.accounting = {} ;
89: chilliController.redir = {} ;
90:
91: chilliController.location = { name: '' } ;
92: chilliController.challenge = '' ;
93: chilliController.message = '' ;
94: chilliController.clientState = chilliController.stateCodes.UNKNOWN ;
95: chilliController.command = '' ;
96: chilliController.autorefreshTimer = 0 ;
97:
98: /* This method returns the root URL for commands */
99: chilliController.urlRoot = function () {
100: var protocol = ( chilliController.ssl ) ? "https" : "http" ;
101: var urlRoot = protocol + "://" + chilliController.host + ":" + chilliController.port.toString() + "/json/" ;
102: return urlRoot;
103: };
104:
105: /* Default event handlers */
106: chilliController.onUpdate = function ( cmd ) {
107: log('>> Default onUpdate handler. <<\n>> You should write your own. <<\n>> cmd = ' + cmd + ' <<' );
108: };
109:
110: chilliController.onError = function ( str ) {
111: log ( '>> Default Error Handler<<\n>> You should write your own <<\n>> ' + str + ' <<' );
112: };
113:
114:
115: chilliController.formatTime = function ( t , zeroReturn ) {
116:
117: if ( typeof(t) == 'undefined' ) {
118: return "Not available";
119: }
120:
121: t = parseInt ( t , 10 ) ;
122: if ( (typeof (zeroReturn) !='undefined') && ( t === 0 ) ) {
123: return zeroReturn;
124: }
125:
126: var h = Math.floor( t/3600 ) ;
127: var m = Math.floor( (t - 3600*h)/60 ) ;
128: var s = t % 60 ;
129:
130: var s_str = s.toString();
131: if (s < 10 ) { s_str = '0' + s_str; }
132:
133: var m_str = m.toString();
134: if (m < 10 ) { m_str= '0' + m_str; }
135:
136: var h_str = h.toString();
137: if (h < 10 ) { h_str= '0' + h_str; }
138:
139:
140: if ( t < 60 ) { return s_str + 's' ; }
141: else if ( t < 3600 ) { return m_str + 'm' + s_str + 's' ; }
142: else { return h_str + 'h' + m_str + 'm' + s_str + 's'; }
143:
144: };
145:
146: chilliController.formatBytes = function ( b , zeroReturn ) {
147:
148: if ( typeof(b) == 'undefined' ) {
149: b = 0;
150: } else {
151: b = parseInt ( b , 10 ) ;
152: }
153:
154: if ( (typeof (zeroReturn) !='undefined') && ( b === 0 ) ) {
155: return zeroReturn;
156: }
157:
158: var kb = Math.round(b / 10) / 100;
159: if (kb < 1) return b + ' Bytes';
160:
161: var mb = Math.round(kb / 10) / 100;
162: if (mb < 1) return kb + ' Kilobytes';
163:
164: var gb = Math.round(mb / 10) / 100;
165: if (gb < 1) return mb + ' Megabytes';
166:
167: return gb + ' Gigabytes';
168: };
169:
170:
171: /**
172: * Global chilliController object
173: *
174: * PUBLIC METHODS
175: * --------------
176: * logon ( username, password ) :
177: * Attempt a CHAP logon with username/password
178: * issues a /logon command to chilli daemon
179: *
180: * logoff () :
181: * Disconnect the current user by issuing a
182: * /logoff command to the chilli daemon
183: *
184: * refresh () :
185: * Issues a /status command to chilli daemon to refresh
186: * the local chilliController object state/session data
187: *
188: */
189:
190: chilliController.logon = function ( username , password ) {
191:
192: if ( typeof(username) !== 'string') {
193: chilliController.onError( 1 , "username missing (or incorrect type)" ) ;
194: }
195:
196: if ( typeof(password) !== 'string') {
197: chilliController.onError( 2 , "password missing (or incorrect type)" ) ;
198: }
199:
200: log ( 'chilliController.logon( "' + username + '" , "' + password + ' " )' );
201:
202: chilliController.temp = { 'username': username , 'password': password };
203: chilliController.command = 'logon';
204:
205: log ('chilliController.logon: asking for a new challenge ' );
206: chilliJSON.onError = chilliController.onError ;
207: chilliJSON.onJSONReady = chilliController.logonStep2 ;
208: chilliController.clientState = chilliController.AUTH_PENDING ;
209: chilliJSON.get( chilliController.urlRoot() + 'status' ) ;
210: };
211:
212:
213: /**
214: * Second part of the logon process invoked after
215: * the just requested challenge has been received
216: */
217: chilliController.logonStep2 = function ( resp ) {
218:
219: log('Entering logonStep 2');
220:
221: if ( typeof (resp.challenge) != 'string' ) {
222: log('logonStep2: cannot find a challenge. Aborting.');
223: return chilliController.onError('Cannot get challenge');
224: }
225:
226: if ( resp.clientSate === chilliController.stateCodes.AUTH ) {
227: log('logonStep2: Already connected. Aborting.');
228: return chilliController.onError('Already connected.');
229: }
230:
231: var challenge = resp.challenge;
232:
233: var username = chilliController.temp.username ;
234: var password = chilliController.temp.password ;
235:
236: log ('chilliController.logonStep2: Got challenge = ' + challenge );
237:
238: if ( chilliController.uamService ) { /* MD5 CHAP will be calculated by uamService */
239:
240: log ('chilliController.logonStep2: Logon using uamService (external MD5 CHAP)');
241:
242: // Build command URL
243: var url = chilliController.uamService + '?username=' + escape(username) +'&password=' + escape(password) +'&challenge=' + challenge ;
244:
245: if (chilliController.queryObj && chilliController.queryObj['userurl'] ) {
246: url += '&userurl='+chilliController.queryObj['userurl'] ;
247: }
248:
249: // Make uamService request
250: chilliJSON.onError = chilliController.onError ;
251: chilliJSON.onJSONReady = chilliController.logonStep3 ;
252:
253: chilliController.clientState = chilliController.AUTH_PENDING ;
254: chilliJSON.get( url ) ;
255: }
256: else {
257: /* TODO: Should check if challenge has expired and possibly get a new one */
258: /* OR always call status first to get a fresh challenge */
259:
260:
261: /* Calculate MD5 CHAP at the client side */
262: var myMD5 = new ChilliMD5();
263: var chappassword = myMD5.chap ( chilliController.ident , password , challenge );
264: log ( 'chilliController.logonStep2: Calculating CHAP-Password = ' + chappassword );
265:
266: /* Prepare chilliJSON for logon request */
267: chilliJSON.onError = chilliController.onError ;
268: chilliJSON.onJSONReady = chilliController.processReply ;
269: chilliController.clientState = chilliController.stateCodes.AUTH_PENDING ;
270:
271: /* Build /logon command URL */
272: var logonUrl = chilliController.urlRoot() + 'logon?username=' + escape(username) + '&response=' + chappassword;
273: if (chilliController.queryObj && chilliController.queryObj['userurl'] ) {
274: logonUrl += '&userurl='+chilliController.queryObj['userurl'] ;
275: }
276: chilliJSON.get ( logonUrl ) ;
277: }
278:
279: };
280:
281: /**
282: * Third part of the logon process invoked after
283: * getting a uamService response
284: */
285: chilliController.logonStep3 = function ( resp ) {
286: log('Entering logonStep 3');
287:
288: var username = chilliController.temp.username ;
289:
290: if ( typeof (resp.response) == 'string' ) {
291: chilliJSON.onError = chilliController.onError ;
292: chilliJSON.onJSONReady = chilliController.processReply ;
293: chilliController.clientState = chilliController.stateCodes.AUTH_PENDING ;
294:
295: /* Build /logon command URL */
296: var logonUrl = chilliController.urlRoot() + 'logon?username=' + escape(username) + '&response=' + resp.response;
297: if (chilliController.queryObj && chilliController.queryObj['userurl'] ) {
298: logonUrl += '&userurl='+chilliController.queryObj['userurl'] ;
299: }
300: chilliJSON.get ( logonUrl ) ;
301: }
302: }
303:
304: chilliController.refresh = function ( ) {
305:
306: if ( chilliController.autorefreshTimer ) {
307: chilliController.command = 'autorefresh' ;
308: }
309: else {
310: chilliController.command = 'refresh' ;
311: }
312:
313: chilliJSON.onError = chilliController.onError ;
314: chilliJSON.onJSONReady = chilliController.processReply ;
315: chilliJSON.get( chilliController.urlRoot() + 'status' ) ;
316: };
317:
318: chilliController.logoff = function () {
319:
320: chilliController.command = 'logoff' ;
321: chilliJSON.onError = chilliController.onError ;
322: chilliJSON.onJSONReady = chilliController.processReply ;
323: chilliJSON.get( chilliController.urlRoot() + 'logoff' );
324: };
325:
326: /* *
327: *
328: * This functions does some check/type processing on the JSON resp
329: * and updates the corresponding chilliController members
330: *
331: */
332: chilliController.processReply = function ( resp ) {
333:
334: if ( typeof (resp.message) == 'string' ) {
335:
336: /* The following trick will replace HTML entities with the corresponding
337: * character. This will not work in Flash (no innerHTML)
338: */
339:
340: var fakediv = document.createElement('div');
341: fakediv.innerHTML = resp.message ;
342: chilliController.message = fakediv.innerHTML ;
343: }
344:
345: if ( typeof (resp.challenge) == 'string' ) {
346: chilliController.challenge = resp.challenge ;
347: }
348:
349: if ( typeof ( resp.location ) == 'object' ) {
350: chilliController.location = resp.location ;
351: }
352:
353: if ( typeof ( resp.accounting ) == 'object' ) {
354: chilliController.accounting = resp.accounting ;
355: }
356:
357: if ( (typeof ( resp.redir ) == 'object') ) {
358: chilliController.redir = resp.redir ;
359: }
360:
361: /* Update the session member only the first time after AUTH */
362: if ( (typeof ( resp.session ) == 'object') &&
363: ( chilliController.session==null || (
364: ( chilliController.clientState !== chilliController.stateCodes.AUTH ) &&
365: ( resp.clientState === chilliController.stateCodes.AUTH )))) {
366:
367: chilliController.session = resp.session ;
368:
369: if ( resp.session.startTime ) {
370: chilliController.session.startTime = new Date();
371: chilliController.session.startTime.setTime(resp.session.startTime);
372: }
373: }
374:
375: /* Update clientState */
376: if ( ( resp.clientState === chilliController.stateCodes.NOT_AUTH ) ||
377: ( resp.clientState === chilliController.stateCodes.AUTH ) ||
378: ( resp.clientState === chilliController.stateCodes.AUTH_SPLASH ) ||
379: ( resp.clientState === chilliController.stateCodes.AUTH_PENDING ) ) {
380:
381: chilliController.clientState = resp.clientState ;
382: }
383: else {
384: chilliController.onError("Unknown clientState found in JSON reply");
385: }
386:
387:
388: /* Launch or stop the autorefresh timer if required */
389: if ( chilliController.clientState === chilliController.stateCodes.AUTH ) {
390:
391: if ( !chilliController.autorefreshTimer ) {
392: chilliController.autorefreshTimer = setInterval ('chilliController.refresh()' , 1000*chilliController.interval);
393: }
394: }
395: else if ( chilliController.clientState === chilliController.stateCodes.NOT_AUTH ) {
396: clearInterval ( chilliController.autorefreshTimer ) ;
397: chilliController.autorefreshTimer = 0 ;
398: }
399:
400: /* Lastly... call the event handler */
401: log ('chilliController.processReply: Calling onUpdate. clienState = ' + chilliController.clientState);
402: chilliController.onUpdate( chilliController.command );
403: };
404:
405:
406:
407: /**
408: * chilliJSON object
409: *
410: * This private objet implements the cross domain hack
411: * If no answer is received before timeout, then an error is raised.
412: *
413: */
414:
415: var chilliJSON = { timeout:25000 , timer:0 , node:0 , timestamp:0 };
416:
417: chilliJSON.expired = function () {
418:
419: if ( chilliJSON.node.text ) {
420: log ('chilliJSON: reply content \n' + chilliJSON.node.text );
421: }
422: else {
423: log ('chilliJSON: request timed out (or reply is not valid JS)');
424: }
425:
426: clearInterval ( chilliJSON.timer ) ;
427: chilliJSON.timer = 0 ;
428:
429: /* remove the <SCRIPT> tag node that we have created */
430: if ( typeof (chilliJSON.node) !== 'number' ) {
431: document.getElementsByTagName('head')[0].removeChild ( chilliJSON.node );
432: }
433: chilliJSON.node = 0;
434:
435: /* TODO: Implement some kind of retry mechanism here ... */
436:
437: chilliJSON.onError('JSON request timed out (or reply is not valid)');
438: };
439:
440: chilliJSON.reply = function ( raw ) {
441:
442: clearInterval ( chilliJSON.timer ) ;
443: chilliJSON.timer = 0 ;
444:
445: var now = new Date() ;
446: var end = now.getTime() ;
447:
448: if ( chilliJSON.timestamp ) {
449: log ( 'chilliJSON: JSON reply received in ' + ( end - chilliJSON.timestamp ) + ' ms\n' + dumpObject(raw) );
450: }
451:
452: if ( typeof (chilliJSON.node) !== 'number' ) {
453: document.getElementsByTagName('head')[0].removeChild ( chilliJSON.node );
454: }
455: chilliJSON.node = 0;
456:
457: /* TODO: We should parse raw JSON as an extra security measure */
458:
459: chilliJSON.onJSONReady( raw ) ;
460: } ;
461:
462: chilliJSON.get = function ( gUrl ) {
463:
464: if ( typeof(gUrl) == "string" ) {
465: chilliJSON.url = gUrl ;
466: }
467: else {
468: log ( "chilliJSON:error:Incorrect url passed to chilliJSON.get():" + gUrl );
469: chilliJSON.onError ( "Incorrect url passed to chilliJSON.get() " );
470: return ;
471: }
472:
473: if ( chilliJSON.timer ) {
474: log('logon: There is already a request running. Return without launching a new request.');
475: return ;
476: }
477:
478:
479: var scriptElement = document.createElement('script');
480: scriptElement.type = 'text/javascript';
481:
482: var c ;
483: if ( this.url.indexOf('?') === -1 ) {
484: c = '?' ;
485: }
486: else {
487: c = '&' ;
488: }
489:
490: scriptElement.src = chilliJSON.url + c + 'callback=chilliJSON.reply' ;
491: scriptElement.src += '&'+Math.random(); // prevent caching in Safari
492:
493: /* Adding the node that will trigger the HTTP request to the DOM tree */
494: chilliJSON.node = document.getElementsByTagName('head')[0].appendChild(scriptElement);
495:
496: /* Using interval instead of timeout to support Flash 5,6,7 */
497: chilliJSON.timer = setInterval ( 'chilliJSON.expired()' , chilliJSON.timeout ) ;
498: var now = new Date();
499: chilliJSON.timestamp = now.getTime() ;
500:
501: log ('chilliJSON: getting ' + chilliJSON.url + ' . Waiting for reply ...');
502:
503: }; // end chilliJSON.get = function ( url )
504:
505:
506: /**
507: * chilliClock object
508: *
509: * Can be used by BBSMs to display a count down.
510: *
511: * Will sync with chilliController and modulate the delay to call onTick
512: * This will avoid ugly sequence of short updates in the IO
513: * (not yet implemented)
514: *
515: */
516:
517: var chilliClock = { isStarted : 0 };
518:
519: chilliClock.onTick = function () {
520: log ("You should define your own onTick() handler on this clock object. Clock value = " + this.value );
521: };
522:
523: chilliClock.increment = function () {
524:
525: chilliClock.value = chilliClock.value + 1 ;
526: chilliClock.onTick( chilliClock.value ) ;
527: };
528:
529: chilliClock.resync = function ( newval ) {
530: clearInterval ( chilliClock.isStarted ) ;
531: chilliClock.value = parseInt( newval , 10 ) ;
532: chilliClock.isStarted = setInterval ( 'chilliClock.increment()' , 1000 );
533: };
534:
535: chilliClock.start = function ( newval ) {
536:
537: if ( typeof (newval) !== 'Number' ) {
538: chilliClock.resync ( 0 ) ;
539: }
540: else {
541: chilliClock.resync ( newval ) ;
542: }
543: };
544:
545: chilliClock.stop = function () {
546: clearInterval ( chilliClock.isStarted ) ;
547: chilliClock.isStarted = 0 ;
548: };
549:
550:
551: function getel(e) {
552: if (document.getElementById) {
553: return document.getElementById(e);
554: } else if (document.all){
555: return document.all[e];
556: }
557: }
558:
559: function log( msg , messageLevel ) {
560: if (!chilliController.debug) return;
561: if ( typeof(trace)=="function") {
562: // ActionScript trace
563: trace ( msg );
564: }
565: else if ( typeof(console)=="object") {
566: // FireBug console
567: console.debug ( msg );
568: }
569:
570: if ( getel('debugarea') ) {
571: var e = getel('debugarea') ;
572: e.value = e.value + '\n' + msg;
573: e.scrollTop = e.scrollHeight - e.clientHeight;
574: }
575: }
576:
577: /* Transform an object to a text representation */
578: function dumpObject ( obj ) {
579:
580: var str = '' ;
581:
582: for (var key in obj ) {
583: str = str + " " + key + " = " + obj[key] + "\n" ;
584: if ( typeof ( obj[key] ) == "object" ) {
585: for ( var key2 in obj[key] ) {
586: str = str + " " + key2 + " = " + obj[key][key2] + "\n" ;
587: }
588: }
589: }
590:
591: return str;
592: }
593:
594: /*
595: * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
596: * Digest Algorithm, as defined in RFC 1321.
597: * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
598: * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
599: * Distributed under the BSD License
600: * See http://pajhome.org.uk/crypt/md5 for more info.
601: *
602: * added by Y.DELTROO
603: * - new functions: chap(), hex2binl() and str2hex()
604: * - modifications to comply with the jslint test, http://www.jslint.com/
605: *
606: * Copyright (c) 2007
607: * Distributed under the BSD License
608: *
609: */
610:
611:
612: function ChilliMD5() {
613:
614: var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
615: var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
616: var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
617:
618: this.hex_md5 = function (s){
619: return binl2hex(core_md5(str2binl(s), s.length * chrsz));
620: };
621:
622: this.chap = function ( hex_ident , str_password , hex_chal ) {
623:
624: // Convert everything to hex encoded strings
625: var hex_password = str2hex ( str_password );
626:
627: // concatenate hex encoded strings
628: var hex = hex_ident + hex_password + hex_chal;
629:
630: // Convert concatenated hex encoded string to its binary representation
631: var bin = hex2binl ( hex ) ;
632:
633: // Calculate MD5 on binary representation
634: var md5 = core_md5( bin , hex.length * 4 ) ;
635:
636: return binl2hex( md5 );
637: };
638:
639: function core_md5(x, len) {
640: x[len >> 5] |= 0x80 << ((len) % 32);
641: x[(((len + 64) >>> 9) << 4) + 14] = len;
642:
643: var a = 1732584193;
644: var b = -271733879;
645: var c = -1732584194;
646: var d = 271733878;
647:
648: for(var i = 0; i < x.length; i += 16) {
649: var olda = a;
650: var oldb = b;
651: var oldc = c;
652: var oldd = d;
653:
654: a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
655: d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
656: c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
657: b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
658: a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
659: d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
660: c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
661: b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
662: a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
663: d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
664: c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
665: b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
666: a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
667: d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
668: c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
669: b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
670:
671: a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
672: d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
673: c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
674: b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
675: a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
676: d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
677: c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
678: b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
679: a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
680: d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
681: c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
682: b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
683: a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
684: d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
685: c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
686: b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
687:
688: a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
689: d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
690: c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
691: b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
692: a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
693: d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
694: c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
695: b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
696: a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
697: d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
698: c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
699: b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
700: a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
701: d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
702: c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
703: b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
704:
705: a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
706: d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
707: c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
708: b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
709: a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
710: d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
711: c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
712: b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
713: a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
714: d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
715: c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
716: b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
717: a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
718: d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
719: c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
720: b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
721:
722: a = safe_add(a, olda);
723: b = safe_add(b, oldb);
724: c = safe_add(c, oldc);
725: d = safe_add(d, oldd);
726: }
727: return [ a, b, c, d ];
728:
729: }
730:
731: function md5_cmn(q, a, b, x, s, t) {
732: return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
733: }
734:
735: function md5_ff(a, b, c, d, x, s, t) {
736: return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
737: }
738:
739: function md5_gg(a, b, c, d, x, s, t) {
740: return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
741: }
742:
743: function md5_hh(a, b, c, d, x, s, t) {
744: return md5_cmn(b ^ c ^ d, a, b, x, s, t);
745: }
746:
747: function md5_ii(a, b, c, d, x, s, t) {
748: return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
749: }
750:
751: function safe_add(x, y) {
752: var lsw = (x & 0xFFFF) + (y & 0xFFFF);
753: var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
754: return (msw << 16) | (lsw & 0xFFFF);
755: }
756: function bit_rol(num, cnt) {
757: return (num << cnt) | (num >>> (32 - cnt));
758: }
759:
760: function str2binl(str) {
761: var bin = [] ;
762: var mask = (1 << chrsz) - 1;
763: for (var i = 0; i < str.length * chrsz; i += chrsz) {
764: bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
765: }
766: return bin;
767: }
768:
769: function binl2hex(binarray) {
770: var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
771: var str = "";
772: for (var i = 0; i < binarray.length * 4; i++) {
773: str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
774: hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
775: }
776: return str;
777: }
778:
779: function str2hex ( str ) {
780: var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
781: var hex = '';
782: var val ;
783: for ( var i=0 ; i<str.length ; i++) {
784: /* TODO: adapt this if chrz=16 */
785: val = str.charCodeAt(i);
786: hex = hex + hex_tab.charAt( val/16 );
787: hex = hex + hex_tab.charAt( val%16 );
788: }
789: return hex;
790: }
791:
792: function hex2binl ( hex ) {
793: /* Clean-up hex encoded input string */
794: hex = hex.toLowerCase() ;
795: hex = hex.replace( / /g , "");
796:
797: var bin =[] ;
798:
799: /* Transfrom to array of integers (binary representation) */
800: for ( i=0 ; i < hex.length*4 ; i=i+8 ) {
801: octet = parseInt( hex.substr( i/4 , 2) , 16) ;
802: bin[i>>5] |= ( octet & 255 ) << (i%32);
803: }
804: return bin;
805: }
806:
807: } // end of ChilliMD5 constructor
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>