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