1var crypt_keys=[];
2var tag_enc="ENCRYPTED";
3var tag_pt="SECRET";
4var encryptForSubmitInUse=false;
5
6/* ahmetsacan: this causes the edit toolbar to break. we moved it into action.php */
7/* addInitEvent(function() { return(decryptEditSetup()); }); */
8
9// the function here is borrowed from an anonymous function in
10// lib/scripts/edit.js (initChangeCheck()).
11// This should be replaced with some way that a plugin can request
12// onSubmit handlers for a given form element
13function editFormOnSubmit() {
14  // begin plugin modified code
15  // need the following to avoid 'msg is not defined' error. I'mnot sure why
16  var msg="Unsaved changes will be lost [edt].\nReally continue?";
17  if(encryptForSubmit()===false) { return(false); }
18  // end plugin modified code
19  var rc = changeCheck(msg);
20  if(window.event) { window.event.returnValue = rc; }
21  return rc;
22}
23
24function decryptEditSetup(msg) {
25  var editform=null, wikitext=null, hiddentext=null, preview=null;
26  if(!(editform=document.getElementById('dw__editform'))) {
27    // alert("no form dw__editform\n");
28    return(true);
29  }
30  if(!(wikitext=document.getElementById('wiki__text'))) {
31   // alert("no wiki__text");
32   return(false);
33  }
34  // if there is no preview button, then assume this is a
35  // "Recover draft" page, dont do anything.
36  if(!(preview=document.getElementById('edbtn__preview'))) {
37    return(false);
38  }
39
40  // create a hidden element with id 'wiki__text_submit' and
41  // name wikitext_edit (same as the wiki__text.  move the real
42  // wikI__text element out of the form (so it is not submitted and
43  // any <SECRET> text left unencrypted
44  if(!(hiddentext=document.createElement('input'))) {
45   return(false);
46  }
47  hiddentext.setAttribute('id', 'wiki__text_submit');
48  hiddentext.setAttribute('name', 'wikitext');
49  hiddentext.setAttribute('type','hidden');
50  editform.insertBefore(hiddentext,null);
51  editform.parentNode.insertBefore(wikitext,editform);
52
53  if(!(decryptButton=document.createElement('input'))) {
54   return(false);
55  }
56  decryptButton.setAttribute('id', 'decryptButton');
57  decryptButton.setAttribute('name', 'decryptButton');
58  decryptButton.setAttribute('type','Button');
59  decryptButton.setAttribute('value','DecryptSecret');
60  // decryptButton.setAttribute('onclick',decryptEditForm);
61  decryptButton.onclick=decryptEditForm;
62  decryptButton.setAttribute('class','button');
63  decryptButton.setAttribute('className','button'); // required for IE
64  preview.parentNode.insertBefore(decryptButton,preview);
65
66  editform.onsubmit = function() {return editFormOnSubmit();};
67
68  // the following is taken from lib/scripts/edit.js to make drafts work
69  locktimer.refresh = function(){
70      var now = new Date();
71      // refresh every minute only
72      if(now.getTime() - locktimer.lasttime.getTime() > 30*1000){ //FIXME decide on time
73          var params = 'call=lock&id='+encodeURIComponent(locktimer.pageid);
74          if(locktimer.draft){
75              var dwform = $('dw__editform');
76              // begin plugin modified code
77              if(encryptForSubmit()===false) { return(false); }
78              // end plugin modified code
79              params += '&prefix='+encodeURIComponent(dwform.elements.prefix.value);
80              params += '&wikitext='+encodeURIComponent(dwform.elements.wikitext.value);
81              params += '&suffix='+encodeURIComponent(dwform.elements.suffix.value);
82              params += '&date='+encodeURIComponent(dwform.elements.date.value);
83          }
84          locktimer.sack.runAJAX(params);
85          locktimer.lasttime = now;
86      }
87  };
88}
89
90function encryptForSubmit() {
91   var wikitext=null, hiddentext=null;
92   // bad semaphore like protection to avoid multiple calls to this code
93   // at once (user pushing 'Submit' and draft save running, or multiple draft
94   // save.  its not really safe
95   while(encryptForSubmitInUse!==false) {
96      // wish I had sleep here
97   }
98   encryptForSubmitInUse=true;
99
100   if(!(wikitext=document.getElementById('wiki__text'))) {
101      alert("failed to get wiki__text");
102      encryptForSubmitInUse=false; return(false);
103   }
104   if(!(hiddentext=document.getElementById('wiki__text_submit'))) {
105      alert("failed to get wiki__text_submit");
106      encryptForSubmitInUse=false; return(false);
107   }
108   var tosubmit=encryptMixedText(wikitext.value);
109   if(tosubmit===false) { encryptForSubmitInUse=false; return(false); }
110   hiddentext.value=tosubmit;
111   encryptForSubmitInUse=false; return(true);
112}
113
114function decryptEditForm() {
115  var elem=null, newtext="";
116  if(!(elem=document.getElementById('wiki__text'))) {
117    // alert("no form wiki__text\n");
118    return(true);
119  }
120  if((newtext=decryptMixedText(elem.value))===false) {
121    alert("failed to decrypt wiki__text");
122    return(false);
123  }
124  elem.value=newtext;
125  return(true);
126}
127
128function setKeyFromAscii(pass) {
129  var s = encode_utf8(pass);
130  var i, kmd5e, kmd5o;
131
132  if (s.length == 1) {
133    s += s;
134  }
135
136  md5_init();
137  for (i = 0; i < s.length; i += 2) {
138    md5_update(s.charCodeAt(i));
139  }
140  md5_finish();
141  kmd5e = byteArrayToHex(digestBits);
142
143  md5_init();
144  for (i = 1; i < s.length; i += 2) {
145    md5_update(s.charCodeAt(i));
146  }
147  md5_finish();
148  kmd5o = byteArrayToHex(digestBits);
149
150  var hs = kmd5e + kmd5o;
151  key =  hexToByteArray(hs);
152  hs = byteArrayToHex(key);
153  return(key);
154}
155
156function toggleElemVisibility(elemid) {
157   elem=document.getElementById(elemid);
158   if(elem.style.visibility=="visible") {
159      elem.style.visibility="hidden";
160      elem.style.position="absolute";
161   } else {
162      elem.style.visibility="visible";
163      elem.style.position="relative";
164   }
165}
166
167/*
168  this is called from <A HREF=> links to decrypt the  inline html
169*/
170function toggleCryptDiv(elemid,lock,ctext) {
171   var elem=null, atab=null, key="", ptext="";
172   var ctStr="Decrypt Encrypted Text", ptStr="Hide Plaintext";
173   elem=document.getElementById(elemid);
174   atag=document.getElementById(elemid + "_atag");
175   if(elem===null || atag===null) {
176      alert("failed to find element id " + elemid);
177   }
178   if(atag.innerHTML==ptStr) {
179      // encrypt text (set back to ctext, and forget key)
180      elem.innerHTML=ctext;
181      atag.innerHTML=ctStr;
182      crypt_keys[lock]=undefined;
183   } else if (atag.innerHTML==ctStr) {
184      // decrypt text
185      if((ptext=verifyDecrypt(ctext,lock,false))===false) {
186         alert("unable to find key for lock " + lock);
187         return;
188      }
189      elem.innerHTML=ptext;
190      atag.innerHTML=ptStr;
191      // make it visible
192      elem.style.visibility="visible";
193      elem.style.position="relative";
194   } else { alert("Broken"); return; }
195}
196
197function getEncryptionKeyForLock(lock) {
198  // alert("crypt_keys[" + lock + "]=" + crypt_keys[lock] + "\n");
199  if(undefined===crypt_keys[lock]) {
200    var x,y;
201    x=prompt("Enter passphrase key for lock " + lock);
202    if(x===null) { return(false); }
203    y=prompt("Verify passphrase key for lock " + lock);
204    if(y===null) { return(false); }
205    if(x!=y) { crypt_debug("passwords do not match\n"); return(false); }
206    crypt_debug("x=" + x + " y=" + y);
207    crypt_keys[lock]=x;
208    return(x);
209  } else {
210    return(crypt_keys[lock]);
211  }
212}
213var debugval="";
214function crypt_debug(str) {
215  // document.getElementById("debug_field").value+=str + "\n";
216  debugval+=str;
217}
218/* decrypt the text between <CRYPT> and </CRYPT> */
219function decryptMixedText(x) {
220  var tag=tag_enc;
221  var ret="", key="", ctext="";
222  var tagend=0, opentag=0, blockend=0, pos=0;
223  while((cur=x.indexOf("<" + tag,pos))!=-1) {
224    if((opentag_end=x.indexOf(">",cur))==-1) {
225      alert("unable to close to open tag"); return(false);
226    }
227    if((closetag=x.indexOf("</" + tag + ">",opentag_end))==-1) {
228      alert("unable to find close of " + tag + " tag"); return(false);
229    }
230    if(!(ctext=decryptBlock(x.substring(cur,closetag+tag.length+3),false))) {
231      return(false);
232    }
233    ret+=x.substring(pos,cur) + ctext;
234    pos=closetag+tag.length+3;
235  }
236  ret+=x.substring(pos);
237  return(ret);
238}
239
240function encryptMixedText(x) {
241  var tag=tag_pt;
242  var ret="", key="", ctext="";
243  var tagend=0, opentag=0, blockend=0, pos=0;
244  while((cur=x.indexOf("<" + tag,pos))!=-1) {
245    if((opentag_end=x.indexOf(">",cur))==-1) {
246      alert("unable to close to open tag"); return(false);
247    }
248    if((closetag=x.indexOf("</" + tag + ">",opentag_end))==-1) {
249      x=x+"</" + tag + ">";
250      // if there is no close tag, add one to the end.
251      closetag=x.indexOf("</" + tag + ">",opentag_end);
252      // alert("unable to find close of " + tag + " tag"); return(false);
253    }
254    if(!(ctext=encryptBlock(x.substring(cur,closetag+tag.length+3),false))) {
255      alert("failed to encrypt text");
256      return(false);
257    }
258    ret+=x.substring(pos,cur) + ctext;
259    pos=closetag+tag.length+3;
260  }
261  ret+=x.substring(pos);
262  return(ret);
263}
264
265function verifyDecrypt(ctext,lock,key) {
266  var ptext=null;
267  if(undefined!==crypt_keys[lock]) { key=crypt_keys[lock]; }
268  if(key===false && (undefined===crypt_keys[lock])) {
269    var key=prompt("Enter passphrase for lock " + lock);
270    if(key===null) { return(false); } // user hit cancel
271    if(!(ptext=decryptTextString(ctext,key))) {
272      var pstr="Try again: Enter passphrase for lock " + lock;
273      while(null!==(key=prompt(pstr))) {
274        ptext=decryptTextString(ctext,key);
275        if(ptext) {
276          break;
277        }
278      }
279      if(key==null) { return(false); } // user hit cancel
280    }
281    crypt_keys[lock]=key;
282  } else {
283    var xkey=key;
284    if(key===false) { xkey=crypt_keys[lock]; }
285    if(!(ptext=decryptTextString(ctext,xkey))) {
286      if(key!==false) { alert("failed to decrypt with provided key"); }
287      return(false);
288    }
289  }
290  return(ptext);
291}
292function decryptBlock(data,key) {
293  var tagend=0, ptend=0, lock=null, ptext;
294  if((tagend=data.indexOf(">"))==-1) {
295    crypt_debug("no > in " + data);
296    return(false);
297  }
298  if((ptend=data.lastIndexOf("</"))==-1) {
299    crypt_debug(" no </ in " + data);
300    return(false);
301  }
302  lock=getTagAttr(data.substring(0,tagend+1),"LOCK");
303  if(lock===null) { lock="default"; }
304
305  collapsed=getTagAttr(data.substring(0,tagend+1),"COLLAPSED");
306  if(collapsed===null) { collapsed="0"; }
307
308  if(!(ptext=verifyDecrypt(data.substring(tagend+1,ptend),lock,key))) {
309    return(false);
310  }
311  return("<" + tag_pt + " LOCK=" + lock + " " +
312     "COLLAPSED=" + collapsed + ">" + ptext + "</" + tag_pt + ">");
313}
314
315// for getTagAttr("<FOO ATTR=val>","ATTR"), return "val"
316function getTagAttr(opentag,attr) {
317  var loff=0;
318  if((loff=opentag.indexOf(attr + "=" ))!=-1) {
319    if((t=opentag.indexOf(" ",loff+attr.length+1))!=-1) {
320      return(opentag.substring(loff+attr.length+1,t));
321    } else {
322      return(opentag.substring(loff+attr.length+1,opentag.length-1));
323    }
324  }
325  return(null);
326}
327
328function encryptBlock(data,key) {
329  var tagend=0, ptend=0, lock=null, ctext;
330  if((tagend=data.indexOf(">"))==-1) {
331    crypt_debug("no > in " + data);
332    return(false);
333  }
334  if((ptend=data.lastIndexOf("</"))==-1) {
335    crypt_debug(" no </ in " + data);
336    return(false);
337  }
338  lock=getTagAttr(data.substring(0,tagend+1),"LOCK");
339  if(lock===null) { lock="default"; }
340
341  collapsed=getTagAttr(data.substring(0,tagend+1),"COLLAPSED");
342  if(collapsed===null) { collapased="0"; }
343
344  if(key===false) {
345    key=getEncryptionKeyForLock(lock);
346    if(key===false) { return(false); }
347  }
348  if(!(ctext=encryptTextString(data.substring(tagend+1,ptend),key))) {
349    return(false);
350  }
351  return("<ENCRYPTED LOCK=" + lock + " " +
352     "COLLAPSED=" + collapsed + ">" + ctext + "</ENCRYPTED>");
353}
354
355
356/* encrypt the string in text with ascii key in akey
357  modified from Encrypt_Text to expect ascii key and take input params
358  and to return base64 encoded
359*/
360function encryptTextString(ptext,akey) {
361  var v, i, ret, key;
362  var prefix = "#####  Encrypted: decrypt with ";
363  prefix+="http://www.fourmilab.ch/javascrypt/\n";
364  suffix = "#####  End encrypted message\n";
365
366  if (akey.length === 0) {
367    alert("Please specify a key with which to encrypt the message.");
368    return;
369  }
370  if (ptext.length === 0) {
371    alert("No plain text to encrypt!");
372    return;
373  }
374  ret="";
375  key=setKeyFromAscii(akey);
376
377  // addEntroptyTime eventually results in setting of global entropyData
378  // which is used by keyFromEntropy
379  addEntropyTime();
380  prng = new AESprng(keyFromEntropy());
381  var plaintext = encode_utf8(ptext);
382
383  //  Compute MD5 sum of message text and add to header
384
385  md5_init();
386  for (i = 0; i < plaintext.length; i++) {
387    md5_update(plaintext.charCodeAt(i));
388  }
389  md5_finish();
390  var header = "";
391  for (i = 0; i < digestBits.length; i++) {
392    header += String.fromCharCode(digestBits[i]);
393  }
394
395  //  Add message length in bytes to header
396
397  i = plaintext.length;
398  header += String.fromCharCode(i >>> 24);
399  header += String.fromCharCode(i >>> 16);
400  header += String.fromCharCode(i >>> 8);
401  header += String.fromCharCode(i & 0xFF);
402
403  /*  The format of the actual message passed to rijndaelEncrypt
404  is:
405     Bytes  Content
406     0-15   MD5 signature of plaintext
407     16-19  Length of plaintext, big-endian order
408     20-end Plaintext
409
410  Note that this message will be padded with zero bytes
411  to an integral number of AES blocks (blockSizeInBits / 8).
412  This does not include the initial vector for CBC
413  encryption, which is added internally by rijndaelEncrypt.
414  */
415
416  var ct = rijndaelEncrypt(header + plaintext, key, "CBC");
417  delete prng;
418  return(prefix + armour_base64(ct) + suffix);
419}
420
421function decryptTextString(ctext,akey) {
422  key=setKeyFromAscii(akey);
423  var ct=[];
424
425  // remove line breaks
426  ct=disarm_base64(ctext);
427  var result=rijndaelDecrypt(ct,key,"CBC");
428  var header=result.slice(0,20);
429  result=result.slice(20);
430  var dl=(header[16]<<24)|(header[17]<<16)|(header[18]<<8)|header[19];
431
432  if((dl<0)||(dl>result.length)) {
433   // alert("Message (length "+result.length+") != expected (" + dl + ")");
434   dl=result.length;
435  }
436
437  var i,plaintext="";
438  md5_init();
439
440  for(i=0;i<dl;i++) {
441    plaintext+=String.fromCharCode(result[i]);
442    md5_update(result[i]);
443  }
444
445  md5_finish();
446
447  successful = true;
448
449  for(i=0;i<digestBits.length;i++) {
450    if(digestBits[i]!=header[i]) {
451      crypt_debug("Invalid decryption key.");
452      return(false);
453    }
454  }
455  return(decode_utf8(plaintext));
456}
457
458// BEGIN: javascript/aes.js
459// Rijndael parameters --  Valid values are 128, 192, or 256
460
461var keySizeInBits = 256;
462var blockSizeInBits = 128;
463
464//
465// Note: in the following code the two dimensional arrays are indexed as
466//       you would probably expect, as array[row][column]. The state arrays
467//       are 2d arrays of the form state[4][Nb].
468
469
470// The number of rounds for the cipher, indexed by [Nk][Nb]
471var roundsArray = [ undefined, undefined, undefined, undefined,[ undefined, undefined, undefined, undefined,10, undefined, 12, undefined, 14], undefined,
472                        [ undefined, undefined, undefined, undefined, 12, undefined, 12, undefined, 14], undefined,
473                        [ undefined, undefined, undefined, undefined, 14, undefined, 14, undefined, 14] ];
474
475// The number of bytes to shift by in shiftRow, indexed by [Nb][row]
476var shiftOffsets = [ undefined, undefined, undefined, undefined,[ undefined,1, 2, 3], undefined,[ undefined,1, 2, 3], undefined,[ undefined,1, 3, 4] ];
477
478// The round constants used in subkey expansion
479var Rcon = [
4800x01, 0x02, 0x04, 0x08, 0x10, 0x20,
4810x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
4820xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc,
4830x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4,
4840xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ];
485
486// Precomputed lookup table for the SBox
487var SBox = [
488 99, 124, 119, 123, 242, 107, 111, 197,  48,   1, 103,  43, 254, 215, 171,
489118, 202, 130, 201, 125, 250,  89,  71, 240, 173, 212, 162, 175, 156, 164,
490114, 192, 183, 253, 147,  38,  54,  63, 247, 204,  52, 165, 229, 241, 113,
491216,  49,  21,   4, 199,  35, 195,  24, 150,   5, 154,   7,  18, 128, 226,
492235,  39, 178, 117,   9, 131,  44,  26,  27, 110,  90, 160,  82,  59, 214,
493179,  41, 227,  47, 132,  83, 209,   0, 237,  32, 252, 177,  91, 106, 203,
494190,  57,  74,  76,  88, 207, 208, 239, 170, 251,  67,  77,  51, 133,  69,
495249,   2, 127,  80,  60, 159, 168,  81, 163,  64, 143, 146, 157,  56, 245,
496188, 182, 218,  33,  16, 255, 243, 210, 205,  12,  19, 236,  95, 151,  68,
49723,  196, 167, 126,  61, 100,  93,  25, 115,  96, 129,  79, 220,  34,  42,
498144, 136,  70, 238, 184,  20, 222,  94,  11, 219, 224,  50,  58,  10,  73,
499  6,  36,  92, 194, 211, 172,  98, 145, 149, 228, 121, 231, 200,  55, 109,
500141, 213,  78, 169, 108,  86, 244, 234, 101, 122, 174,   8, 186, 120,  37,
501 46,  28, 166, 180, 198, 232, 221, 116,  31,  75, 189, 139, 138, 112,  62,
502181, 102,  72,   3, 246,  14,  97,  53,  87, 185, 134, 193,  29, 158, 225,
503248, 152,  17, 105, 217, 142, 148, 155,  30, 135, 233, 206,  85,  40, 223,
504140, 161, 137,  13, 191, 230,  66, 104,  65, 153,  45,  15, 176,  84, 187,
505 22 ];
506
507// Precomputed lookup table for the inverse SBox
508var SBoxInverse = [
509 82,   9, 106, 213,  48,  54, 165,  56, 191,  64, 163, 158, 129, 243, 215,
510251, 124, 227,  57, 130, 155,  47, 255, 135,  52, 142,  67,  68, 196, 222,
511233, 203,  84, 123, 148,  50, 166, 194,  35,  61, 238,  76, 149,  11,  66,
512250, 195,  78,   8,  46, 161, 102,  40, 217,  36, 178, 118,  91, 162,  73,
513109, 139, 209,  37, 114, 248, 246, 100, 134, 104, 152,  22, 212, 164,  92,
514204,  93, 101, 182, 146, 108, 112,  72,  80, 253, 237, 185, 218,  94,  21,
515 70,  87, 167, 141, 157, 132, 144, 216, 171,   0, 140, 188, 211,  10, 247,
516228,  88,   5, 184, 179,  69,   6, 208,  44,  30, 143, 202,  63,  15,   2,
517193, 175, 189,   3,   1,  19, 138, 107,  58, 145,  17,  65,  79, 103, 220,
518234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116,  34, 231, 173,
519 53, 133, 226, 249,  55, 232,  28, 117, 223, 110,  71, 241,  26, 113,  29,
520 41, 197, 137, 111, 183,  98,  14, 170,  24, 190,  27, 252,  86,  62,  75,
521198, 210, 121,  32, 154, 219, 192, 254, 120, 205,  90, 244,  31, 221, 168,
522 51, 136,   7, 199,  49, 177,  18,  16,  89,  39, 128, 236,  95,  96,  81,
523127, 169,  25, 181,  74,  13,  45, 229, 122, 159, 147, 201, 156, 239, 160,
524224,  59,  77, 174,  42, 245, 176, 200, 235, 187,  60, 131,  83, 153,  97,
525 23,  43,   4, 126, 186, 119, 214,  38, 225, 105,  20,  99,  85,  33,  12,
526125 ];
527
528// This method circularly shifts the array left by the number of elements
529// given in its parameter. It returns the resulting array and is used for
530// the ShiftRow step. Note that shift() and push() could be used for a more
531// elegant solution, but they require IE5.5+, so I chose to do it manually.
532
533function cyclicShiftLeft(theArray, positions) {
534  var temp = theArray.slice(0, positions);
535  theArray = theArray.slice(positions).concat(temp);
536  return theArray;
537}
538
539// Cipher parameters ... do not change these
540var Nk = keySizeInBits / 32;
541var Nb = blockSizeInBits / 32;
542var Nr = roundsArray[Nk][Nb];
543
544// Multiplies the element "poly" of GF(2^8) by x. See the Rijndael spec.
545
546function xtime(poly) {
547  poly <<= 1;
548  return ((poly & 0x100) ? (poly ^ 0x11B) : (poly));
549}
550
551// Multiplies the two elements of GF(2^8) together and returns the result.
552// See the Rijndael spec, but should be straightforward: for each power of
553// the indeterminant that has a 1 coefficient in x, add y times that power
554// to the result. x and y should be bytes representing elements of GF(2^8)
555
556function mult_GF256(x, y) {
557  var bit, result = 0;
558
559  for (bit = 1; bit < 256; bit *= 2, y = xtime(y)) {
560    if (x & bit) { result ^= y; }
561  }
562  return result;
563}
564
565// Performs the substitution step of the cipher.  State is the 2d array of
566// state information (see spec) and direction is string indicating whether
567// we are performing the forward substitution ("encrypt") or inverse
568// substitution (anything else)
569
570function byteSub(state, direction) {
571  var S;
572  if (direction == "encrypt") { S = SBox; } // Point S to the SBox we're using
573  else { S = SBoxInverse; }
574  for (var i = 0; i < 4; i++) { // Substitute for every byte in state
575    for (var j = 0; j < Nb; j++) { state[i][j] = S[state[i][j]]; }
576  }
577}
578
579// Performs the row shifting step of the cipher.
580
581function shiftRow(state, direction) {
582  for (var i=1; i<4; i++) {             // Row 0 never shifts
583    if (direction == "encrypt") {
584       state[i] = cyclicShiftLeft(state[i], shiftOffsets[Nb][i]);
585    } else {
586       state[i] = cyclicShiftLeft(state[i], Nb - shiftOffsets[Nb][i]);
587    }
588  }
589
590}
591
592// Performs the column mixing step of the cipher. Most of these steps can
593// be combined into table lookups on 32bit values (at least for encryption)
594// to greatly increase the speed.
595
596function mixColumn(state, direction) {
597  var b = [];                            // Result of matrix multiplications
598  var i = 0;
599  for (var j = 0; j < Nb; j++) {         // Go through each column...
600    for (i = 0; i < 4; i++) {        // and for each row in the column...
601      if (direction == "encrypt") {
602        b[i] = mult_GF256(state[i][j], 2) ^          // perform mixing
603               mult_GF256(state[(i+1)%4][j], 3) ^
604               state[(i+2)%4][j] ^
605               state[(i+3)%4][j];
606      } else {
607        b[i] = mult_GF256(state[i][j], 0xE) ^
608               mult_GF256(state[(i+1)%4][j], 0xB) ^
609               mult_GF256(state[(i+2)%4][j], 0xD) ^
610               mult_GF256(state[(i+3)%4][j], 9);
611      }
612    }
613    for (i = 0; i < 4; i++) {        // Place result back into column
614      state[i][j] = b[i];
615    }
616  }
617}
618
619// Adds the current round key to the state information. Straightforward.
620
621function addRoundKey(state, roundKey) {
622  for (var j = 0; j < Nb; j++) {                 // Step through columns...
623    state[0][j] ^= (roundKey[j] & 0xFF);         // and XOR
624    state[1][j] ^= ((roundKey[j]>>8) & 0xFF);
625    state[2][j] ^= ((roundKey[j]>>16) & 0xFF);
626    state[3][j] ^= ((roundKey[j]>>24) & 0xFF);
627  }
628}
629
630// This function creates the expanded key from the input (128/192/256-bit)
631// key. The parameter key is an array of bytes holding the value of the key.
632// The returned value is an array whose elements are the 32-bit words that
633// make up the expanded key.
634
635function keyExpansion(key) {
636  var expandedKey = [];
637  var temp;
638
639  // in case the key size or parameters were changed...
640  Nk = keySizeInBits / 32;
641  Nb = blockSizeInBits / 32;
642  Nr = roundsArray[Nk][Nb];
643
644  for (var j=0; j < Nk; j++) {   // Fill in input key first
645    expandedKey[j] =
646      (key[4*j]) | (key[4*j+1]<<8) | (key[4*j+2]<<16) | (key[4*j+3]<<24);
647  }
648
649  // Now walk down the rest of the array filling in expanded key bytes as
650  // per Rijndael's spec
651  for (j = Nk; j < Nb * (Nr + 1); j++) {    // For each word of expanded key
652    temp = expandedKey[j - 1];
653    if (j % Nk === 0) {
654      temp = ( (SBox[(temp>>8) & 0xFF]) |
655               (SBox[(temp>>16) & 0xFF]<<8) |
656               (SBox[(temp>>24) & 0xFF]<<16) |
657               (SBox[temp & 0xFF]<<24) ) ^ Rcon[Math.floor(j / Nk) - 1];
658    } else if (Nk > 6 && j % Nk == 4) {
659      temp = (SBox[(temp>>24) & 0xFF]<<24) |
660             (SBox[(temp>>16) & 0xFF]<<16) |
661             (SBox[(temp>>8) & 0xFF]<<8) |
662             (SBox[temp & 0xFF]);
663    }
664    expandedKey[j] = expandedKey[j-Nk] ^ temp;
665  }
666  return expandedKey;
667}
668
669// Rijndael's round functions...
670
671function jcRound(state, roundKey) {
672  byteSub(state, "encrypt");
673  shiftRow(state, "encrypt");
674  mixColumn(state, "encrypt");
675  addRoundKey(state, roundKey);
676}
677
678function inverseRound(state, roundKey) {
679  addRoundKey(state, roundKey);
680  mixColumn(state, "decrypt");
681  shiftRow(state, "decrypt");
682  byteSub(state, "decrypt");
683}
684
685function finalRound(state, roundKey) {
686  byteSub(state, "encrypt");
687  shiftRow(state, "encrypt");
688  addRoundKey(state, roundKey);
689}
690
691function inverseFinalRound(state, roundKey){
692  addRoundKey(state, roundKey);
693  shiftRow(state, "decrypt");
694  byteSub(state, "decrypt");
695}
696
697// encrypt is the basic encryption function. It takes parameters
698// block, an array of bytes representing a plaintext block, and expandedKey,
699// an array of words representing the expanded key previously returned by
700// keyExpansion(). The ciphertext block is returned as an array of bytes.
701
702function encrypt(block, expandedKey) {
703  var i;
704  if (!block || block.length*8 != blockSizeInBits) { return; }
705  if (!expandedKey) { return; }
706
707  block = packBytes(block);
708  addRoundKey(block, expandedKey);
709  for (i=1; i<Nr; i++) { jcRound(block, expandedKey.slice(Nb*i, Nb*(i+1))); }
710  finalRound(block, expandedKey.slice(Nb*Nr));
711  return unpackBytes(block);
712}
713
714// decrypt is the basic decryption function. It takes parameters
715// block, an array of bytes representing a ciphertext block, and expandedKey,
716// an array of words representing the expanded key previously returned by
717// keyExpansion(). The decrypted block is returned as an array of bytes.
718
719function decrypt(block, expandedKey) {
720  var i;
721  if (!block || block.length*8 != blockSizeInBits) { return; }
722  if (!expandedKey) { return; }
723
724  block = packBytes(block);
725  inverseFinalRound(block, expandedKey.slice(Nb*Nr));
726  for (i = Nr - 1; i>0; i--) {
727    inverseRound(block, expandedKey.slice(Nb*i, Nb*(i+1)));
728  }
729  addRoundKey(block, expandedKey);
730  return unpackBytes(block);
731}
732
733/* !NEEDED
734// This method takes a byte array (byteArray) and converts it to a string by
735// applying String.fromCharCode() to each value and concatenating the result.
736// The resulting string is returned. Note that this function SKIPS zero bytes
737// under the assumption that they are padding added in formatPlaintext().
738// Obviously, do not invoke this method on raw data that can contain zero
739// bytes. It is really only appropriate for printable ASCII/Latin-1
740// values. Roll your own function for more robust functionality :)
741
742function byteArrayToString(byteArray) {
743  var result = "";
744  for(var i=0; i<byteArray.length; i++)
745    if (byteArray[i] != 0)
746      result += String.fromCharCode(byteArray[i]);
747  return result;
748}
749*/
750
751// This function takes an array of bytes (byteArray) and converts them
752// to a hexadecimal string. Array element 0 is found at the beginning of
753// the resulting string, high nibble first. Consecutive elements follow
754// similarly, for example [16, 255] --> "10ff". The function returns a
755// string.
756
757function byteArrayToHex(byteArray) {
758  var result = "";
759  if (!byteArray) { return; }
760  for (var i=0; i<byteArray.length; i++) {
761    result += ((byteArray[i]<16) ? "0" : "") + byteArray[i].toString(16);
762  }
763
764  return result;
765}
766
767// This function converts a string containing hexadecimal digits to an
768// array of bytes. The resulting byte array is filled in the order the
769// values occur in the string, for example "10FF" --> [16, 255]. This
770// function returns an array.
771
772function hexToByteArray(hexString) {
773  var byteArray = [];
774  if (hexString.length % 2) { return; } // must have even length
775  if (hexString.indexOf("0x") === 0 || hexString.indexOf("0X") === 0) {
776    hexString = hexString.substring(2);
777  }
778  for (var i = 0; i<hexString.length; i += 2) {
779    byteArray[Math.floor(i/2)] = parseInt(hexString.slice(i, i+2), 16);
780  }
781  return byteArray;
782}
783
784// This function packs an array of bytes into the four row form defined by
785// Rijndael. It assumes the length of the array of bytes is divisible by
786// four. Bytes are filled in according to the Rijndael spec (starting with
787// column 0, row 0 to 3). This function returns a 2d array.
788
789function packBytes(octets) {
790  var state = [];
791  if (!octets || octets.length % 4) { return; }
792
793  state[0] = []; state[1] = [];
794  state[2] = []; state[3] = [];
795  for (var j=0; j<octets.length; j+= 4) {
796    state[0][j/4] = octets[j];
797    state[1][j/4] = octets[j+1];
798    state[2][j/4] = octets[j+2];
799    state[3][j/4] = octets[j+3];
800  }
801  return state;
802}
803
804// This function unpacks an array of bytes from the four row format preferred
805// by Rijndael into a single 1d array of bytes. It assumes the input "packed"
806// is a packed array. Bytes are filled in according to the Rijndael spec.
807// This function returns a 1d array of bytes.
808
809function unpackBytes(packed) {
810  var result = [];
811  for (var j=0; j<packed[0].length; j++) {
812    result[result.length] = packed[0][j];
813    result[result.length] = packed[1][j];
814    result[result.length] = packed[2][j];
815    result[result.length] = packed[3][j];
816  }
817  return result;
818}
819
820// This function takes a prospective plaintext (string or array of bytes)
821// and pads it with pseudorandom bytes if its length is not a multiple of the block
822// size. If plaintext is a string, it is converted to an array of bytes
823// in the process. The type checking can be made much nicer using the
824// instanceof operator, but this operator is not available until IE5.0 so I
825// chose to use the heuristic below.
826
827function formatPlaintext(plaintext) {
828  var bpb = blockSizeInBits / 8;               // bytes per block
829  var i;
830
831  // if primitive string or String instance
832  if ((!((typeof plaintext == "object") &&
833        ((typeof (plaintext[0])) == "number"))) &&
834      ((typeof plaintext == "string") || plaintext.indexOf)) {
835    plaintext = plaintext.split("");
836    // Unicode issues here (ignoring high byte)
837    for (i=0; i<plaintext.length; i++) {
838      plaintext[i] = plaintext[i].charCodeAt(0) & 0xFF;
839    }
840  }
841
842  i = plaintext.length % bpb;
843  if (i > 0) {
844    plaintext = plaintext.concat(getRandomBytes(bpb - i));
845  }
846
847  return plaintext;
848}
849
850// Returns an array containing "howMany" random bytes.
851
852function getRandomBytes(howMany) {
853  var i, bytes = [];
854
855  for (i = 0; i < howMany; i++) {
856    bytes[i] = prng.nextInt(255);
857  }
858  return bytes;
859}
860
861// rijndaelEncrypt(plaintext, key, mode)
862// Encrypts the plaintext using the given key and in the given mode.
863// The parameter "plaintext" can either be a string or an array of bytes.
864// The parameter "key" must be an array of key bytes. If you have a hex
865// string representing the key, invoke hexToByteArray() on it to convert it
866// to an array of bytes. The third parameter "mode" is a string indicating
867// the encryption mode to use, either "ECB" or "CBC". If the parameter is
868// omitted, ECB is assumed.
869//
870// An array of bytes representing the cihpertext is returned. To convert
871// this array to hex, invoke byteArrayToHex() on it.
872
873function rijndaelEncrypt(plaintext, key, mode) {
874  var expandedKey, i, aBlock;
875  var bpb = blockSizeInBits / 8;          // bytes per block
876  var ct;                                 // ciphertext
877
878  if (!plaintext || !key) { return; }
879  if (key.length*8 != keySizeInBits) { return; }
880  if (mode == "CBC") {
881    ct = getRandomBytes(bpb);             // get IV
882//dump("IV", byteArrayToHex(ct));
883  } else {
884    mode = "ECB";
885    ct = [];
886  }
887
888  // convert plaintext to byte array and pad with zeros if necessary.
889  plaintext = formatPlaintext(plaintext);
890
891  expandedKey = keyExpansion(key);
892
893  for (var block = 0; block < plaintext.length / bpb; block++) {
894    aBlock = plaintext.slice(block * bpb, (block + 1) * bpb);
895    if (mode == "CBC") {
896      for (i = 0; i < bpb; i++) {
897        aBlock[i] ^= ct[(block * bpb) + i];
898      }
899    }
900    ct = ct.concat(encrypt(aBlock, expandedKey));
901  }
902
903  return ct;
904}
905
906// rijndaelDecrypt(ciphertext, key, mode)
907// Decrypts the using the given key and mode. The parameter "ciphertext"
908// must be an array of bytes. The parameter "key" must be an array of key
909// bytes. If you have a hex string representing the ciphertext or key,
910// invoke hexToByteArray() on it to convert it to an array of bytes. The
911// parameter "mode" is a string, either "CBC" or "ECB".
912//
913// An array of bytes representing the plaintext is returned. To convert
914// this array to a hex string, invoke byteArrayToHex() on it. To convert it
915// to a string of characters, you can use byteArrayToString().
916
917function rijndaelDecrypt(ciphertext, key, mode) {
918  var expandedKey;
919  var bpb = blockSizeInBits / 8;          // bytes per block
920  var pt = [];                   // plaintext array
921  var aBlock;                             // a decrypted block
922  var block;                              // current block number
923
924  if (!ciphertext || !key || typeof ciphertext == "string") { return; }
925  if (key.length*8 != keySizeInBits) { return; }
926  if (!mode) { mode = "ECB"; } // assume ECB if mode omitted
927
928  expandedKey = keyExpansion(key);
929
930  // work backwards to accomodate CBC mode
931  for (block=(ciphertext.length / bpb)-1; block>0; block--) {
932    aBlock =
933     decrypt(ciphertext.slice(block*bpb,(block+1)*bpb), expandedKey);
934    if (mode == "CBC") {
935      for (var i=0; i<bpb; i++) {
936        pt[(block-1)*bpb + i] = aBlock[i] ^ ciphertext[(block-1)*bpb + i];
937      }
938    } else {
939      pt = aBlock.concat(pt);
940    }
941  }
942
943  // do last block if ECB (skips the IV in CBC)
944  if (mode == "ECB") {
945    pt = decrypt(ciphertext.slice(0, bpb), expandedKey).concat(pt);
946  }
947
948  return pt;
949}
950
951// END: javascrypt/aes.js
952// BEGIN: javascrypt/entropy.js
953
954//  Entropy collection utilities
955
956/* Start by declaring static storage and initialise
957   the entropy vector from the time we come through
958   here. */
959
960var entropyData = []; // Collected entropy data
961var edlen = 0;        // Keyboard array data length
962
963addEntropyTime();     // Start entropy collection with page load time
964ce();                 // Roll milliseconds into initial entropy
965
966//  Add a byte to the entropy vector
967
968function addEntropyByte(b) {
969  entropyData[edlen++] = b;
970}
971
972/*  Capture entropy.  When the user presses a key or performs
973  various other events for which we can request
974  notification, add the time in 255ths of a second to the
975  entropyData array.  The name of the function is short
976  so it doesn't bloat the form object declarations in
977  which it appears in various "onXXX" events.  */
978
979function ce() {
980  addEntropyByte(Math.floor((((new Date()).getMilliseconds()) * 255) / 999));
981}
982
983//  Add a 32 bit quantity to the entropy vector
984
985function addEntropy32(w) {
986  var i;
987
988  for (i = 0; i < 4; i++) {
989    addEntropyByte(w & 0xFF);
990    w >>= 8;
991  }
992}
993
994/*  Add the current time and date (milliseconds since the epoch,
995    truncated to 32 bits) to the entropy vector.  */
996
997function addEntropyTime() {
998  addEntropy32((new Date()).getTime());
999}
1000/*  Start collection of entropy from mouse movements. The
1001  argument specifies the  number of entropy items to be
1002  obtained from mouse motion, after which mouse motion
1003  will be ignored.  Note that you can re-enable mouse
1004  motion collection at any time if not already underway.  */
1005
1006var mouseMotionCollect = 0;
1007var oldMoveHandler;    // For saving and restoring mouse move handler in IE4
1008
1009function mouseMotionEntropy(maxsamp) {
1010  if (mouseMotionCollect <= 0) {
1011    mouseMotionCollect = maxsamp;
1012    if ((document.implementation.hasFeature("Events", "2.0")) &&
1013        document.addEventListener) {
1014      //  Browser supports Document Object Model (DOM) 2 events
1015      document.addEventListener("mousemove", mouseMoveEntropy, false);
1016    } else {
1017      if (document.attachEvent) {
1018        //  Internet Explorer 5 and above event model
1019        document.attachEvent("onmousemove", mouseMoveEntropy);
1020      } else {
1021        //  Internet Explorer 4 event model
1022        oldMoveHandler = document.onmousemove;
1023        document.onmousemove = mouseMoveEntropy;
1024      }
1025    }
1026    //dump("Mouse enable", mouseMotionCollect);
1027  }
1028}
1029
1030/*  Collect entropy from mouse motion events.  Note that
1031  this is craftily coded to work with either DOM2 or Internet
1032  Explorer style events.  Note that we don't use every successive
1033  mouse movement event.  Instead, we XOR the three bytes collected
1034  from the mouse and use that to determine how many subsequent
1035  mouse movements we ignore before capturing the next one.  */
1036
1037var mouseEntropyTime = 0;      // Delay counter for mouse entropy collection
1038
1039function mouseMoveEntropy(e) {
1040  if (!e) {
1041    e = window.event;      // Internet Explorer event model
1042  }
1043  if (mouseMotionCollect > 0) {
1044    if (mouseEntropyTime-- <= 0) {
1045      addEntropyByte(e.screenX & 0xFF);
1046      addEntropyByte(e.screenY & 0xFF);
1047      ce();
1048      mouseMotionCollect--;
1049      mouseEntropyTime = (entropyData[edlen - 3] ^ entropyData[edlen - 2] ^
1050                          entropyData[edlen - 1]) % 19;
1051      //dump("Mouse Move", byteArrayToHex(entropyData.slice(-3)));
1052    }
1053    if (mouseMotionCollect <= 0) {
1054      if (document.removeEventListener) {
1055        document.removeEventListener("mousemove", mouseMoveEntropy, false);
1056      } else if (document.detachEvent) {
1057        document.detachEvent("onmousemove", mouseMoveEntropy);
1058      } else {
1059        document.onmousemove = oldMoveHandler;
1060      }
1061      //dump("Spung!", 0);
1062    }
1063  }
1064}
1065
1066/*  Compute a 32 byte key value from the entropy vector.
1067  We compute the value by taking the MD5 sum of the even
1068  and odd bytes respectively of the entropy vector, then
1069  concatenating the two MD5 sums.  */
1070
1071function keyFromEntropy() {
1072  var i, k = [];
1073
1074  if (edlen === 0) {
1075    alert("Blooie!  Entropy vector void at call to keyFromEntropy.");
1076  }
1077  //dump("Entropy bytes", edlen);
1078
1079  md5_init();
1080  for (i = 0; i < edlen; i += 2) {
1081    md5_update(entropyData[i]);
1082  }
1083  md5_finish();
1084  for (i = 0; i < 16; i++) {
1085    k[i] = digestBits[i];
1086  }
1087
1088  md5_init();
1089  for (i = 1; i < edlen; i += 2) {
1090    md5_update(entropyData[i]);
1091  }
1092  md5_finish();
1093  for (i = 0; i < 16; i++) {
1094    k[i + 16] = digestBits[i];
1095  }
1096
1097  //dump("keyFromEntropy", byteArrayToHex(k));
1098  return k;
1099}
1100// END: javascrypt/entropy.js
1101// BEGIN: javascrypt/aesprng.js
1102//  AES based pseudorandom number generator
1103
1104/*  Constructor.  Called with an array of 32 byte (0-255) values
1105  containing the initial seed.  */
1106
1107function AESprng(seed) {
1108  this.key = [];
1109  this.key = seed;
1110  this.itext = hexToByteArray("9F489613248148F9C27945C6AE62EECA3E3367BB14064E4E6DC67A9F28AB3BD1");
1111  this.nbytes = 0;          // Bytes left in buffer
1112
1113  this.next = AESprng_next;
1114  this.nextbits = AESprng_nextbits;
1115  this.nextInt = AESprng_nextInt;
1116  this.round = AESprng_round;
1117
1118  /*  Encrypt the initial text with the seed key
1119      three times, feeding the output of the encryption
1120      back into the key for the next round.  */
1121
1122  bsb = blockSizeInBits;
1123  blockSizeInBits = 256;
1124  var i, ct;
1125  for (i = 0; i < 3; i++) {
1126    this.key = rijndaelEncrypt(this.itext, this.key, "ECB");
1127  }
1128
1129  /*  Now make between one and four additional
1130      key-feedback rounds, with the number determined
1131      by bits from the result of the first three
1132      rounds.  */
1133
1134  var n = 1 + (this.key[3] & 2) + (this.key[9] & 1);
1135  for (i = 0; i < n; i++) {
1136    this.key = rijndaelEncrypt(this.itext, this.key, "ECB");
1137  }
1138  blockSizeInBits = bsb;
1139}
1140
1141function AESprng_round() {
1142  bsb = blockSizeInBits;
1143  blockSizeInBits = 256;
1144  this.key = rijndaelEncrypt(this.itext, this.key, "ECB");
1145  this.nbytes = 32;
1146  blockSizeInBits = bsb;
1147}
1148
1149//  Return next byte from the generator
1150function AESprng_next() {
1151  if (this.nbytes <= 0) {
1152    this.round();
1153  }
1154  return(this.key[--this.nbytes]);
1155}
1156
1157//  Return n bit integer value (up to maximum integer size)
1158function AESprng_nextbits(n) {
1159  var i, w = 0, nbytes = Math.floor((n + 7) / 8);
1160
1161  for (i = 0; i < nbytes; i++) {
1162    w = (w << 8) | this.next();
1163  }
1164  return w & ((1 << n) - 1);
1165}
1166
1167//  Return integer between 0 and n inclusive
1168function AESprng_nextInt(n) {
1169  var p = 1, nb = 0;
1170
1171  //  Determine smallest p,  2^p > n
1172  //  nb = log_2 p
1173
1174  while (n >= p) {
1175    p <<= 1;
1176    nb++;
1177  }
1178  p--;
1179
1180  /*  Generate values from 0 through n by first generating
1181      values v from 0 to (2^p)-1, then discarding any results v > n.
1182      For the rationale behind this (and why taking
1183      values mod (n + 1) is biased toward smaller values, see
1184      Ferguson and Schneier, "Practical Cryptography",
1185      ISBN 0-471-22357-3, section 10.8).  */
1186
1187  while (true) {
1188    var v = this.nextbits(nb) & p;
1189
1190    if (v <= n) {
1191      return v;
1192    }
1193  }
1194}
1195// END: javascrypt/aesprng.js
1196// BEGIN: javascrypt/lecuyer.js
1197/*
1198   L'Ecuyer's two-sequence generator with a Bays-Durham shuffle
1199  on the back-end.  Schrage's algorithm is used to perform
1200  64-bit modular arithmetic within the 32-bit constraints of
1201  JavaScript.
1202
1203  Bays, C. and S. D. Durham.  ACM Trans. Math. Software: 2 (1976)
1204    59-64.
1205
1206  L'Ecuyer, P.  Communications of the ACM: 31 (1968) 742-774.
1207
1208  Schrage, L.  ACM Trans. Math. Software: 5 (1979) 132-138.
1209
1210*/
1211
1212// Schrage's modular multiplication algorithm
1213function uGen(old, a, q, r, m) {
1214  var t;
1215
1216  t = Math.floor(old / q);
1217  t = a * (old - (t * q)) - (t * r);
1218  return Math.round((t < 0) ? (t + m) : t);
1219}
1220
1221// Return next raw value
1222function LEnext() {
1223  var i;
1224
1225  this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563);
1226  this.gen2 = uGen(this.gen2, 40692, 52774, 3791, 2147483399);
1227
1228  /* Extract shuffle table index from most significant part
1229     of the previous result. */
1230
1231  i = Math.floor(this.state / 67108862);
1232
1233  // New state is sum of generators modulo one of their moduli
1234
1235  this.state = Math.round((this.shuffle[i] + this.gen2) % 2147483563);
1236
1237  // Replace value in shuffle table with generator 1 result
1238
1239  this.shuffle[i] = this.gen1;
1240
1241  return this.state;
1242}
1243
1244//  Return next random integer between 0 and n inclusive
1245
1246function LEnint(n) {
1247  var p = 1;
1248
1249  //  Determine smallest p,  2^p > n
1250
1251  while (n >= p) {
1252    p <<= 1;
1253  }
1254  p--;
1255
1256  /*  Generate values from 0 through n by first masking
1257    values v from 0 to (2^p)-1, then discarding any results v > n.
1258  For the rationale behind this (and why taking
1259  values mod (n + 1) is biased toward smaller values, see
1260  Ferguson and Schneier, "Practical Cryptography",
1261  ISBN 0-471-22357-3, section 10.8).  */
1262
1263    while (true) {
1264      var v = this.next() & p;
1265
1266      if (v <= n) {
1267      return v;
1268    }
1269  }
1270}
1271
1272//  Constructor.  Called with seed value
1273function LEcuyer(s) {
1274  var i;
1275
1276  this.shuffle = [];
1277  this.gen1 = this.gen2 = (s & 0x7FFFFFFF);
1278  for (i = 0; i < 19; i++) {
1279    this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563);
1280  }
1281
1282  // Fill the shuffle table with values
1283
1284  for (i = 0; i < 32; i++) {
1285    this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563);
1286    this.shuffle[31 - i] = this.gen1;
1287  }
1288  this.state = this.shuffle[0];
1289  this.next = LEnext;
1290  this.nextInt = LEnint;
1291}
1292// END:  javascrypt/lecuyer.js
1293// BEGIN: javascrypt/md5.js
1294function array(n) {
1295    for (i = 0; i < n; i++) {
1296        this[i] = 0;
1297    }
1298    this.length = n;
1299}
1300
1301/* Some basic logical functions had to be rewritten because of a bug in
1302 * Javascript.. Just try to compute 0xffffffff >> 4 with it..
1303 * Of course, these functions are slower than the original would be, but
1304 * at least, they work!
1305 */
1306
1307function integer(n) {
1308    return n % (0xffffffff + 1);
1309}
1310
1311function shr(a, b) {
1312    a = integer(a);
1313    b = integer(b);
1314    if (a - 0x80000000 >= 0) {
1315        a = a % 0x80000000;
1316        a >>= b;
1317        a += 0x40000000 >> (b - 1);
1318    } else {
1319        a >>= b;
1320    }
1321    return a;
1322}
1323
1324function shl1(a) {
1325    a = a % 0x80000000;
1326    if (a & 0x40000000 == 0x40000000) {
1327        a -= 0x40000000;
1328        a *= 2;
1329        a += 0x80000000;
1330    } else {
1331        a *= 2;
1332    }
1333    return a;
1334}
1335
1336function shl(a, b) {
1337    a = integer(a);
1338    b = integer(b);
1339    for (var i = 0; i < b; i++) {
1340        a = shl1(a);
1341    }
1342    return a;
1343}
1344
1345function and(a, b) {
1346    a = integer(a);
1347    b = integer(b);
1348    var t1 = a - 0x80000000;
1349    var t2 = b - 0x80000000;
1350    if (t1 >= 0) {
1351        if (t2 >= 0) {
1352            return ((t1 & t2) + 0x80000000);
1353        } else {
1354            return (t1 & b);
1355        }
1356    } else {
1357        if (t2 >= 0) {
1358            return (a & t2);
1359        } else {
1360            return (a & b);
1361        }
1362    }
1363}
1364
1365function or(a, b) {
1366    a = integer(a);
1367    b = integer(b);
1368    var t1 = a - 0x80000000;
1369    var t2 = b - 0x80000000;
1370    if (t1 >= 0) {
1371        if (t2 >= 0) {
1372            return ((t1 | t2) + 0x80000000);
1373        } else {
1374            return ((t1 | b) + 0x80000000);
1375        }
1376    } else {
1377        if (t2 >= 0) {
1378            return ((a | t2) + 0x80000000);
1379        } else {
1380            return (a | b);
1381        }
1382    }
1383}
1384
1385function xor(a, b) {
1386  a = integer(a);
1387  b = integer(b);
1388  var t1 = a - 0x80000000;
1389  var t2 = b - 0x80000000;
1390  if (t1 >= 0) {
1391    if (t2 >= 0) {
1392      return (t1 ^ t2);
1393    } else {
1394      return ((t1 ^ b) + 0x80000000);
1395    }
1396  } else {
1397    if (t2 >= 0) {
1398      return ((a ^ t2) + 0x80000000);
1399    } else {
1400      return (a ^ b);
1401    }
1402  }
1403}
1404
1405function not(a) {
1406  a = integer(a);
1407  return 0xffffffff - a;
1408}
1409
1410/* Here begin the real algorithm */
1411
1412var state = [];
1413var count = [];
1414    count[0] = 0;
1415    count[1] = 0;
1416var buffer = [];
1417var transformBuffer = [];
1418var digestBits = [];
1419
1420var S11 = 7;
1421var S12 = 12;
1422var S13 = 17;
1423var S14 = 22;
1424var S21 = 5;
1425var S22 = 9;
1426var S23 = 14;
1427var S24 = 20;
1428var S31 = 4;
1429var S32 = 11;
1430var S33 = 16;
1431var S34 = 23;
1432var S41 = 6;
1433var S42 = 10;
1434var S43 = 15;
1435var S44 = 21;
1436
1437function jcF(x, y, z) {
1438  return or(and(x, y), and(not(x), z));
1439}
1440
1441function jcG(x, y, z) {
1442  return or(and(x, z), and(y, not(z)));
1443}
1444
1445function jcH(x, y, z) {
1446  return xor(xor(x, y), z);
1447}
1448
1449function jcI(x, y, z) {
1450  return xor(y ,or(x , not(z)));
1451}
1452
1453function rotateLeft(a, n) {
1454  return or(shl(a, n), (shr(a, (32 - n))));
1455}
1456
1457function jcFF(a, b, c, d, x, s, ac) {
1458  a = a + jcF(b, c, d) + x + ac;
1459  a = rotateLeft(a, s);
1460  a = a + b;
1461  return a;
1462}
1463
1464function jcGG(a, b, c, d, x, s, ac) {
1465  a = a + jcG(b, c, d) + x + ac;
1466  a = rotateLeft(a, s);
1467  a = a + b;
1468  return a;
1469}
1470
1471function jcHH(a, b, c, d, x, s, ac) {
1472  a = a + jcH(b, c, d) + x + ac;
1473  a = rotateLeft(a, s);
1474  a = a + b;
1475  return a;
1476}
1477
1478function jcII(a, b, c, d, x, s, ac) {
1479  a = a + jcI(b, c, d) + x + ac;
1480  a = rotateLeft(a, s);
1481  a = a + b;
1482  return a;
1483}
1484
1485function transform(buf, offset) {
1486  var a = 0, b = 0, c = 0, d = 0;
1487  var x = transformBuffer;
1488
1489  a = state[0];
1490  b = state[1];
1491  c = state[2];
1492  d = state[3];
1493
1494  for (i = 0; i < 16; i++) {
1495    x[i] = and(buf[i * 4 + offset], 0xFF);
1496    for (j = 1; j < 4; j++) {
1497      x[i] += shl(and(buf[i * 4 + j + offset] ,0xFF), j * 8);
1498    }
1499  }
1500
1501  /* Round 1 */
1502  a = jcFF( a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
1503  d = jcFF( d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
1504  c = jcFF( c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
1505  b = jcFF( b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
1506  a = jcFF( a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
1507  d = jcFF( d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
1508  c = jcFF( c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
1509  b = jcFF( b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
1510  a = jcFF( a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
1511  d = jcFF( d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
1512  c = jcFF( c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
1513  b = jcFF( b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
1514  a = jcFF( a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
1515  d = jcFF( d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
1516  c = jcFF( c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
1517  b = jcFF( b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
1518
1519  /* Round 2 */
1520  a = jcGG( a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
1521  d = jcGG( d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
1522  c = jcGG( c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
1523  b = jcGG( b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
1524  a = jcGG( a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
1525  d = jcGG( d, a, b, c, x[10], S22,  0x2441453); /* 22 */
1526  c = jcGG( c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
1527  b = jcGG( b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
1528  a = jcGG( a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
1529  d = jcGG( d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
1530  c = jcGG( c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
1531  b = jcGG( b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
1532  a = jcGG( a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
1533  d = jcGG( d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
1534  c = jcGG( c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
1535  b = jcGG( b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
1536
1537  /* Round 3 */
1538  a = jcHH( a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
1539  d = jcHH( d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
1540  c = jcHH( c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
1541  b = jcHH( b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
1542  a = jcHH( a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
1543  d = jcHH( d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
1544  c = jcHH( c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
1545  b = jcHH( b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
1546  a = jcHH( a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
1547  d = jcHH( d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
1548  c = jcHH( c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
1549  b = jcHH( b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
1550  a = jcHH( a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
1551  d = jcHH( d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
1552  c = jcHH( c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
1553  b = jcHH( b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
1554
1555  /* Round 4 */
1556  a = jcII( a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
1557  d = jcII( d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
1558  c = jcII( c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
1559  b = jcII( b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
1560  a = jcII( a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
1561  d = jcII( d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
1562  c = jcII( c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
1563  b = jcII( b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
1564  a = jcII( a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
1565  d = jcII( d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
1566  c = jcII( c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
1567  b = jcII( b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
1568  a = jcII( a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
1569  d = jcII( d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
1570  c = jcII( c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
1571  b = jcII( b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
1572
1573  state[0] += a;
1574  state[1] += b;
1575  state[2] += c;
1576  state[3] += d;
1577
1578}
1579
1580function md5_init() {
1581  count[0] = count[1] = 0;
1582  state[0] = 0x67452301;
1583  state[1] = 0xefcdab89;
1584  state[2] = 0x98badcfe;
1585  state[3] = 0x10325476;
1586  for (i = 0; i < digestBits.length; i++) {
1587    digestBits[i] = 0;
1588  }
1589}
1590
1591function md5_update(b) {
1592  var index, i;
1593
1594  index = and(shr(count[0],3) , 0x3F);
1595  if (count[0] < 0xFFFFFFFF - 7) {
1596    count[0] += 8;
1597  } else {
1598    count[1]++;
1599    count[0] -= 0xFFFFFFFF + 1;
1600    count[0] += 8;
1601  }
1602  buffer[index] = and(b, 0xff);
1603  if (index  >= 63) {
1604    transform(buffer, 0);
1605  }
1606}
1607
1608function md5_finish() {
1609  var bits = [];
1610  var padding;
1611  var i = 0, index = 0, padLen = 0;
1612
1613  for (i = 0; i < 4; i++) {
1614    bits[i] = and(shr(count[0], (i * 8)), 0xFF);
1615  }
1616  for (i = 0; i < 4; i++) {
1617    bits[i + 4] = and(shr(count[1], (i * 8)), 0xFF);
1618  }
1619  index = and(shr(count[0], 3), 0x3F);
1620  padLen = (index < 56) ? (56 - index) : (120 - index);
1621  padding = [];
1622  padding[0] = 0x80;
1623  for (i = 0; i < padLen; i++) {
1624    md5_update(padding[i]);
1625  }
1626  for (i = 0; i < 8; i++) {
1627  md5_update(bits[i]);
1628  }
1629
1630  for (i = 0; i < 4; i++) {
1631    for (j = 0; j < 4; j++) {
1632      digestBits[i * 4 + j] = and(shr(state[i], (j * 8)) , 0xFF);
1633    }
1634  }
1635}
1636
1637/* End of the MD5 algorithm */
1638// END: javascyprt/md5.js
1639// BEGIN: javscrypt/armour.js
1640
1641//  Varieties of ASCII armour for binary data
1642
1643var maxLineLength = 64; // Maximum line length for armoured text
1644
1645/* Hexadecimal Armour
1646
1647   A message is encoded in Hexadecimal armour by expressing its
1648   bytes as a hexadecimal string which is prefixed by a sentinel
1649   of "?HX?" and suffixed by "?H", then broken into lines no
1650   longer than maxLineLength.  Armoured messages use lower case
1651   letters for digits with decimal values of 0 through 15, but
1652   either upper or lower case letters are accepted when decoding
1653   a message.  The hexadecimal to byte array interconversion
1654   routines in aes.js do most of the heavy lifting here.  */
1655
1656var hexSentinel = "?HX?", hexEndSentinel = "?H";
1657
1658//  Encode byte array in hexadecimal armour
1659
1660function armour_hex(b) {
1661  var h = hexSentinel + byteArrayToHex(b) + hexEndSentinel;
1662  var t = "";
1663  while (h.length > maxLineLength) {
1664    //dump("h.length", h.length);
1665    t += h.substring(0, maxLineLength) + "\n";
1666    h = h.substring(maxLineLength, h.length);
1667  }
1668  //dump("h.final_length", h.length);
1669  t += h + "\n";
1670  return t;
1671}
1672
1673/* Decode string in hexadecimal armour to byte array.  If the
1674   string supplied contains a start and/or end sentinel,
1675   only characters within the sentinels will be decoded.
1676   Non-hexadecimal digits are silently ignored, which
1677   automatically handles line breaks.  We might want to
1678   diagnose invalid characters as opposed to ignoring them.  */
1679
1680function disarm_hex(s) {
1681  var hexDigits = "0123456789abcdefABCDEF";
1682  var hs = "", i;
1683
1684  //  Extract hexadecimal data between sentinels, if present
1685  if ((i = s.indexOf(hexSentinel)) >= 0) {
1686    s = s.substring(i + hexSentinel.length, s.length);
1687  }
1688  if ((i = s.indexOf(hexEndSentinel)) >= 0) {
1689    s = s.substring(0, i);
1690  }
1691
1692  //  Assemble string of valid hexadecimal digits
1693
1694  for (i = 0; i < s.length; i++) {
1695    var c = s.charAt(i);
1696    if (hexDigits.indexOf(c) >= 0) {
1697      hs += c;
1698    }
1699  }
1700//dump("hs", hs);
1701  return hexToByteArray(hs);
1702}
1703
1704  /*  Codegroup Armour
1705      Codegroup armour encodes a byte string into a sequence of five
1706  letter code groups like spies used in the good old days.  The
1707  first group of a message is always "ZZZZZ" and the last "YYYYY";
1708  the decoding process ignores any text outside these start and
1709  end sentinels.  Bytes are encoded as two letters in the range
1710  "A" to "X", each encoding four bits of the byte.  Encoding uses
1711  a pseudorandomly generated base letter and wraps around modulo
1712  24 to spread encoded letters evenly through the alphabet.  (This
1713  refinement is purely aesthetic; the base letter sequence is
1714  identical for all messages and adds no security.  If the message
1715  does not fill an even number of five letter groups, the last
1716  group is padded to five letters with "Z" characters, which are
1717  ignored when decoding.  */
1718
1719var acgcl, acgt, acgg;
1720
1721// Output next codegroup, flushing current line if it's full
1722
1723function armour_cg_outgroup() {
1724  if (acgcl.length > maxLineLength) {
1725    acgt += acgcl + "\n";
1726    acgcl = "";
1727  }
1728  if (acgcl.length > 0) {
1729    acgcl += " ";
1730  }
1731  acgcl += acgg;
1732  acgg = "";
1733}
1734
1735/* Add a letter to the current codegroup, emitting it when
1736   it reaches five letters.  */
1737
1738function armour_cg_outletter(l) {
1739  if (acgg.length >= 5) {
1740    armour_cg_outgroup();
1741  }
1742  acgg += l;
1743}
1744
1745var codegroupSentinel = "ZZZZZ";
1746
1747function armour_codegroup(b) {
1748  var charBase = ("A").charCodeAt(0);
1749
1750  acgcl = codegroupSentinel;
1751  acgt = "";
1752  acgg = "";
1753
1754  var cgrng = new LEcuyer(0xbadf00d);
1755  for (i = 0; i < b.length; i++) {
1756    var r = cgrng.nextInt(23);
1757    armour_cg_outletter(String.fromCharCode(charBase + ((((b[i] >> 4) & 0xF)) + r) % 24));
1758    r = cgrng.nextInt(23);
1759    armour_cg_outletter(String.fromCharCode(charBase + ((((b[i] & 0xF)) + r) % 24)));
1760  }
1761  delete cgrng;
1762
1763  //  Generate nulls to fill final codegroup if required
1764
1765  while (acgg.length < 5) {
1766    armour_cg_outletter("Z");
1767  }
1768  armour_cg_outgroup();
1769
1770  //  Append terminator group
1771
1772  acgg = "YYYYY";
1773  armour_cg_outgroup();
1774
1775  //  Flush last line
1776
1777  acgt += acgcl + "\n";
1778
1779  return acgt;
1780}
1781
1782var dcgs, dcgi;
1783
1784  /*  Obtain next "significant" character from message.  Characters
1785    other than letters are silently ignored; both lower and upper
1786    case letters are accepted.  */
1787
1788function disarm_cg_insig() {
1789  while (dcgi < dcgs.length) {
1790    var c = dcgs.charAt(dcgi++).toUpperCase();
1791    if ((c >= "A") && (c <= "Z")) {
1792      //dump("c", c);
1793      return c;
1794    }
1795  }
1796  return "";
1797}
1798
1799// Decode a message in codegroup armour
1800
1801function disarm_codegroup(s) {
1802  var b = [];
1803  var nz = 0, ba, bal = 0, c;
1804
1805  dcgs = s;
1806  dcgi = 0;
1807
1808  //  Search for initial group of "ZZZZZ"
1809
1810  while (nz < 5) {
1811    c = disarm_cg_insig();
1812
1813    if (c == "Z") {
1814      nz++;
1815    } else if (c === "") {
1816      nz = 0;
1817      break;
1818    } else {
1819      nz = 0;
1820    }
1821  }
1822
1823  if (nz === 0) {
1824      alert("No codegroup starting symbol found in message.");
1825      return "";
1826  }
1827
1828  /*  Decode letter pairs from successive groups
1829      and assemble into bytes.  */
1830
1831  var charBase = ("A").charCodeAt(0);
1832  var cgrng = new LEcuyer(0xbadf00d);
1833  for (nz = 0; nz < 2; ) {
1834    c = disarm_cg_insig();
1835    //dump("c", c);
1836
1837    if ((c == "Y") || (c === "")) {
1838      break;
1839    } else if (c != "Z") {
1840      var r = cgrng.nextInt(23);
1841      var n = c.charCodeAt(0) - charBase;
1842      n = (n + (24 - r)) % 24;
1843      //dump("n", n);
1844      if (nz === 0) {
1845        ba = (n << 4);
1846        nz++;
1847      } else {
1848        ba |= n;
1849        b[bal++] = ba;
1850        nz = 0;
1851      }
1852    }
1853  }
1854  delete cgrng;
1855
1856  /*  Ponder how we escaped from the decoder loop and
1857      issue any requisite warnings.  */
1858
1859  var kbo = "  Attempting decoding with data received.";
1860  if (nz !== 0) {
1861    alert("Codegroup data truncated." + kbo);
1862  } else {
1863    if (c == "Y") {
1864      nz = 1;
1865      while (nz < 5) {
1866        c = disarm_cg_insig();
1867        if (c != "Y") {
1868          break;
1869        }
1870        nz++;
1871      }
1872      if (nz != 5) {
1873        alert("Codegroup end group incomplete." + kbo);
1874      }
1875    } else {
1876      alert("Codegroup end group missing." + kbo);
1877    }
1878  }
1879
1880  return b;
1881}
1882
1883    /*  Base64 Armour
1884
1885  Base64 armour encodes a byte array as described in RFC 1341.  Sequences
1886  of three bytes are encoded into groups of four characters from a set
1887  of 64 consisting of the upper and lower case letters, decimal digits,
1888  and the special characters "+" and "/".  If the input is not a multiple
1889  of three characters, the end of the message is padded with one or two
1890  "=" characters to indicate its actual length.  We prefix the armoured
1891  message with "?b64" and append "?64b" to the end; if one or both
1892  of these sentinels are present, text outside them is ignored.  You can
1893  suppress the generation of sentinels in armour by setting base64addsent
1894  false before calling armour_base64.  */
1895
1896
1897var base64code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
1898  base64sent = "?b64", base64esent = "?64b", base64addsent = true;
1899
1900function armour_base64(b) {
1901  var b64t = "";
1902  var b64l = base64addsent ? base64sent : "";
1903
1904  var i;
1905  for (i = 0; i <= b.length - 3; i += 3) {
1906    if ((b64l.length + 4) > maxLineLength) {
1907      b64t += b64l + "\n";
1908      b64l = "";
1909    }
1910    b64l += base64code.charAt(b[i] >> 2);
1911    b64l += base64code.charAt(((b[i] & 3) << 4) | (b[i + 1] >> 4));
1912    b64l += base64code.charAt(((b[i + 1] & 0xF) << 2) | (b[i + 2] >> 6));
1913    b64l += base64code.charAt(b[i + 2] & 0x3F);
1914  }
1915
1916  //dump("b.length", b.length);  dump("i", i); dump("(b.length - i)", (b.length - i));
1917  if ((b.length - i) == 1) {
1918    b64l += base64code.charAt(b[i] >> 2);
1919    b64l += base64code.charAt(((b[i] & 3) << 4));
1920    b64l += "==";
1921  } else if ((b.length - i) == 2) {
1922    b64l += base64code.charAt(b[i] >> 2);
1923    b64l += base64code.charAt(((b[i] & 3) << 4) | (b[i + 1] >> 4));
1924    b64l += base64code.charAt(((b[i + 1] & 0xF) << 2));
1925    b64l += "=";
1926  }
1927
1928  if ((b64l.length + 4) > maxLineLength) {
1929    b64t += b64l + "\n";
1930    b64l = "";
1931  }
1932  if (base64addsent) {
1933    b64l += base64esent;
1934  }
1935  b64t += b64l + "\n";
1936  return b64t;
1937}
1938
1939function disarm_base64(s) {
1940  var b = [];
1941  var i = 0, j, c, shortgroup = 0, n = 0;
1942  var d = [];
1943
1944  if ((j = s.indexOf(base64sent)) >= 0) {
1945    s = s.substring(j + base64sent.length, s.length);
1946  }
1947  if ((j = s.indexOf(base64esent)) >= 0) {
1948    s = s.substring(0, j);
1949  }
1950
1951  /*  Ignore any non-base64 characters before the encoded
1952      data stream and skip the type sentinel if present.  */
1953
1954  while (i < s.length) {
1955    if (base64code.indexOf(s.charAt(i)) != -1) {
1956      break;
1957    }
1958    i++;
1959  }
1960
1961  /*  Decode the base64 data stream.  The decoder is
1962      terminated by the end of the input string or
1963      the occurrence of the explicit end sentinel.  */
1964
1965  while (i < s.length) {
1966    for (j = 0; j < 4; ) {
1967      if (i >= s.length) {
1968        if (j > 0) {
1969          alert("Base64 cipher text truncated.");
1970          return b;
1971        }
1972        break;
1973      }
1974      c = base64code.indexOf(s.charAt(i));
1975      if (c >= 0) {
1976        d[j++] = c;
1977      } else if (s.charAt(i) == "=") {
1978        d[j++] = 0;
1979        shortgroup++;
1980      } else if (s.substring(i, i + base64esent.length) == base64esent) {
1981        //dump("s.substring(i, i + base64esent.length)", s.substring(i, i + base64esent.length));
1982        //dump("esent", i);
1983        i = s.length;
1984        continue;
1985      } else {
1986        //dump("s.substring(i, i + base64esent.length)", s.substring(i, i + base64esent.length));
1987        //dump("usent", i);
1988        // Might improve diagnosis of improper character in else clause here
1989      }
1990      i++;
1991    }
1992    //dump("d0", d[0]); dump("d1", d[1]); dump("d2", d[2]); dump("d3", d[3]);
1993    //dump("shortgroup", shortgroup);
1994    //dump("n", n);
1995    if (j == 4) {
1996      b[n++] = ((d[0] << 2) | (d[1] >> 4)) & 0xFF;
1997      if (shortgroup < 2) {
1998        b[n++] = ((d[1] << 4) | (d[2] >> 2)) & 0xFF;
1999        //dump("(d[1] << 4) | (d[2] >> 2)", (d[1] << 4) | (d[2] >> 2));
2000        if (shortgroup < 1) {
2001          b[n++] = ((d[2] << 6) | d[3]) & 0xFF;
2002        }
2003      }
2004    }
2005  }
2006  return b;
2007}
2008// END: javascrypt/armour.js
2009// BEGIN: javscrypt/utf-8.js
2010
2011/*  Encoding and decoding of Unicode character strings as
2012    UTF-8 byte streams.  */
2013
2014//  UNICODE_TO_UTF8  --  Encode Unicode argument string as UTF-8 return value
2015
2016function unicode_to_utf8(s) {
2017  var utf8 = "";
2018  for (var n = 0; n < s.length; n++) {
2019    var c = s.charCodeAt(n);
2020
2021    if (c <= 0x7F) {
2022      //  0x00 - 0x7F:  Emit as single byte, unchanged
2023      utf8 += String.fromCharCode(c);
2024    } else if ((c >= 0x80) && (c <= 0x7FF)) {
2025      //  0x80 - 0x7FF:  Output as two byte code, 0xC0 in first byte
2026      //  0x80 in second byte
2027      utf8 += String.fromCharCode((c >> 6) | 0xC0);
2028      utf8 += String.fromCharCode((c & 0x3F) | 0x80);
2029    } else {
2030      // 0x800 - 0xFFFF:  Output as three bytes, 0xE0 in first byte
2031      // 0x80 in second byte
2032      // 0x80 in third byte
2033      utf8 += String.fromCharCode((c >> 12) | 0xE0);
2034      utf8 += String.fromCharCode(((c >> 6) & 0x3F) | 0x80);
2035      utf8 += String.fromCharCode((c & 0x3F) | 0x80);
2036    }
2037  }
2038  return utf8;
2039}
2040
2041    //  UTF8_TO_UNICODE  --  Decode UTF-8 argument into Unicode string return value
2042
2043function utf8_to_unicode(utf8) {
2044  var s = "", i = 0, b1, b2;
2045
2046  while (i < utf8.length) {
2047    b1 = utf8.charCodeAt(i);
2048    if (b1 < 0x80) {      // One byte code: 0x00 0x7F
2049      s += String.fromCharCode(b1);
2050      i++;
2051    } else if((b1 >= 0xC0) && (b1 < 0xE0)) {  // Two byte code: 0x80 - 0x7FF
2052      b2 = utf8.charCodeAt(i + 1);
2053      s += String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
2054      i += 2;
2055    } else {            // Three byte code: 0x800 - 0xFFFF
2056      b2 = utf8.charCodeAt(i + 1);
2057      b3 = utf8.charCodeAt(i + 2);
2058      s += String.fromCharCode(((b1 & 0xF) << 12) |
2059              ((b2 & 0x3F) << 6) | (b3 & 0x3F));
2060      i += 3;
2061    }
2062  }
2063  return s;
2064}
2065
2066    /*  ENCODE_UTF8  --  Encode string as UTF8 only if it contains
2067       a character of 0x9D (Unicode OPERATING
2068       SYSTEM COMMAND) or a character greater
2069       than 0xFF.  This permits all strings
2070       consisting exclusively of 8 bit
2071       graphic characters to be encoded as
2072       themselves.  We choose 0x9D as the sentinel
2073       character as opposed to one of the more
2074       logical PRIVATE USE characters because 0x9D
2075       is not overloaded by the regrettable
2076       "Windows-1252" character set.  Now such characters
2077       don't belong in JavaScript strings, but you never
2078       know what somebody is going to paste into a
2079       text box, so this choice keeps Windows-encoded
2080       strings from bloating to UTF-8 encoding.  */
2081
2082function encode_utf8(s) {
2083  var i, necessary = false;
2084
2085  for (i = 0; i < s.length; i++) {
2086    if ((s.charCodeAt(i) == 0x9D) || (s.charCodeAt(i) > 0xFF)) {
2087      necessary = true;
2088      break;
2089    }
2090  }
2091  if (!necessary) {
2092    return s;
2093  }
2094  return String.fromCharCode(0x9D) + unicode_to_utf8(s);
2095}
2096
2097/*  DECODE_UTF8  --  Decode a string encoded with encode_utf8
2098    above.  If the string begins with the
2099    sentinel character 0x9D (OPERATING
2100    SYSTEM COMMAND), then we decode the
2101    balance as a UTF-8 stream.  Otherwise,
2102    the string is output unchanged, as
2103    it's guaranteed to contain only 8 bit
2104    characters excluding 0x9D.  */
2105
2106function decode_utf8(s) {
2107  if ((s.length > 0) && (s.charCodeAt(0) == 0x9D)) {
2108    return utf8_to_unicode(s.substring(1));
2109  }
2110  return s;
2111}
2112// END: javscrypt/utf-8.js
2113