Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4 <head>
5 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
6 <title>One-Time Pad Generator</title>
7 <meta name="description" content="JavaScript One-Time Pad Generator" />
8 <meta name="author" content="John Walker" />
9 <meta name="keywords" content="one, time, pad, generator, onetime, cryptography, JavaScript" />
10 <style type="text/css">
11 a:link, a:visited {
12 background-color: inherit;
13 color: rgb(0%, 0%, 80%);
14 text-decoration: none;
15 }
17 a:hover {
18 background-color: rgb(30%, 30%, 100%);
19 color: rgb(100%, 100%, 100%);
20 }
22 a:active {
23 color: rgb(100%, 0%, 0%);
24 background-color: rgb(30%, 30%, 100%);
25 }
27 a.i:link, a.i:visited, a.i:hover {
28 background-color: inherit;
29 color: inherit;
30 text-decoration: none;
31 }
33 body {
34 margin-left: 15%;
35 margin-right: 10%;
36 background-color: #FFFFFF;
37 color: #000000;
38 }
40 body.jsgen {
41 margin-left: 5%;
42 margin-right: 5%;
43 }
45 dt {
46 margin-top: 0.5em;
47 }
49 img.button {
50 border: 0px;
51 vertical-align: middle;
52 }
54 img.keyicon {
55 vertical-align: bottom;
56 }
58 p, dd, li {
59 text-align: justify;
60 }
62 p.centre {
63 text-align: center;
64 }
66 table.r {
67 float: right;
68 }
70 table.c {
71 background-color: #E0E0E0;
72 color: #000000;
73 margin-left: auto;
74 margin-right: auto;
75 }
77 td.c {
78 text-align: center;
79 }
81 textarea {
82 background-color: #FFFFD0;
83 color: #000000;
84 }
85 </style>
86 <script type="text/javascript">
87 //<![CDATA[
89 loadTime = (new Date()).getTime();
91 /*
93 L'Ecuyer's two-sequence generator with a Bays-Durham shuffle
94 on the back-end. Schrage's algorithm is used to perform
95 64-bit modular arithmetic within the 32-bit constraints of
96 JavaScript.
98 Bays, C. and S. D. Durham. ACM Trans. Math. Software: 2 (1976)
99 59-64.
101 L'Ecuyer, P. Communications of the ACM: 31 (1968) 742-774.
103 Schrage, L. ACM Trans. Math. Software: 5 (1979) 132-138.
105 */
107 function uGen(old, a, q, r, m) { // Schrage's modular multiplication algorithm
108 var t;
110 t = Math.floor(old / q);
111 t = a * (old - (t * q)) - (t * r);
112 return Math.round((t < 0) ? (t + m) : t);
113 }
115 function LEnext() { // Return next raw value
116 var i;
118 this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563);
119 this.gen2 = uGen(this.gen2, 40692, 52774, 3791, 2147483399);
121 /* Extract shuffle table index from most significant part
122 of the previous result. */
124 i = Math.floor(this.state / 67108862);
126 // New state is sum of generators modulo one of their moduli
128 this.state = Math.round((this.shuffle[i] + this.gen2) % 2147483563);
130 // Replace value in shuffle table with generator 1 result
132 this.shuffle[i] = this.gen1;
134 return this.state;
135 }
137 // Return next random integer between 0 and n inclusive
139 function LEnint(n) {
140 return Math.floor(this.next() / (1 + 2147483562 / (n + 1)));
141 }
143 // Constructor. Called with seed value
145 function LEcuyer(s) {
146 var i;
148 this.shuffle = new Array(32);
149 this.gen1 = this.gen2 = (s & 0x7FFFFFFF);
150 for (i = 0; i < 19; i++) {
151 this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563);
152 }
154 // Fill the shuffle table with values
156 for (i = 0; i < 32; i++) {
157 this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563);
158 this.shuffle[31 - i] = this.gen1;
159 }
160 this.state = this.shuffle[0];
161 this.next = LEnext;
162 this.nextInt = LEnint;
163 }
165 function sepchar() {
166 if (rsep) {
167 var seps = "!#$%&()*+,-./:;<=>?@[]^_{|}~";
168 return seps.charAt(sepran.nextInt(seps.length - 1));
169 }
170 return "-";
171 }
173 /*
174 * md5.jvs 1.0b 27/06/96
175 *
176 * Javascript implementation of the RSA Data Security, Inc. MD5
177 * Message-Digest Algorithm.
178 *
179 * Copyright (c) 1996 Henri Torgemane. All Rights Reserved.
180 *
181 * Permission to use, copy, modify, and distribute this software
182 * and its documentation for any purposes and without
183 * fee is hereby granted provided that this copyright notice
184 * appears in all copies.
185 *
186 * Of course, this soft is provided "as is" without express or implied
187 * warranty of any kind.
189 This version contains some trivial reformatting modifications
190 by John Walker.
192 */
194 function array(n) {
195 for (i = 0; i < n; i++) {
196 this[i] = 0;
197 }
198 this.length = n;
199 }
201 /* Some basic logical functions had to be rewritten because of a bug in
202 * Javascript.. Just try to compute 0xffffffff >> 4 with it..
203 * Of course, these functions are slower than the original would be, but
204 * at least, they work!
205 */
207 function integer(n) {
208 return n % (0xffffffff + 1);
209 }
211 function shr(a, b) {
212 a = integer(a);
213 b = integer(b);
214 if (a - 0x80000000 >= 0) {
215 a = a % 0x80000000;
216 a >>= b;
217 a += 0x40000000 >> (b - 1);
218 } else {
219 a >>= b;
220 }
221 return a;
222 }
224 function shl1(a) {
225 a = a % 0x80000000;
226 if (a & 0x40000000 == 0x40000000) {
227 a -= 0x40000000;
228 a *= 2;
229 a += 0x80000000;
230 } else {
231 a *= 2;
232 }
233 return a;
234 }
236 function shl(a, b) {
237 a = integer(a);
238 b = integer(b);
239 for (var i = 0; i < b; i++) {
240 a = shl1(a);
241 }
242 return a;
243 }
245 function and(a, b) {
246 a = integer(a);
247 b = integer(b);
248 var t1 = a - 0x80000000;
249 var t2 = b - 0x80000000;
250 if (t1 >= 0) {
251 if (t2 >= 0) {
252 return ((t1 & t2) + 0x80000000);
253 } else {
254 return (t1 & b);
255 }
256 } else {
257 if (t2 >= 0) {
258 return (a & t2);
259 } else {
260 return (a & b);
261 }
262 }
263 }
265 function or(a, b) {
266 a = integer(a);
267 b = integer(b);
268 var t1 = a - 0x80000000;
269 var t2 = b - 0x80000000;
270 if (t1 >= 0) {
271 if (t2 >= 0) {
272 return ((t1 | t2) + 0x80000000);
273 } else {
274 return ((t1 | b) + 0x80000000);
275 }
276 } else {
277 if (t2 >= 0) {
278 return ((a | t2) + 0x80000000);
279 } else {
280 return (a | b);
281 }
282 }
283 }
285 function xor(a, b) {
286 a = integer(a);
287 b = integer(b);
288 var t1 = a - 0x80000000;
289 var t2 = b - 0x80000000;
290 if (t1 >= 0) {
291 if (t2 >= 0) {
292 return (t1 ^ t2);
293 } else {
294 return ((t1 ^ b) + 0x80000000);
295 }
296 } else {
297 if (t2 >= 0) {
298 return ((a ^ t2) + 0x80000000);
299 } else {
300 return (a ^ b);
301 }
302 }
303 }
305 function not(a) {
306 a = integer(a);
307 return 0xffffffff - a;
308 }
310 /* Here begin the real algorithm */
312 var state = new array(4);
313 var count = new array(2);
314 count[0] = 0;
315 count[1] = 0;
316 var buffer = new array(64);
317 var transformBuffer = new array(16);
318 var digestBits = new array(16);
320 var S11 = 7;
321 var S12 = 12;
322 var S13 = 17;
323 var S14 = 22;
324 var S21 = 5;
325 var S22 = 9;
326 var S23 = 14;
327 var S24 = 20;
328 var S31 = 4;
329 var S32 = 11;
330 var S33 = 16;
331 var S34 = 23;
332 var S41 = 6;
333 var S42 = 10;
334 var S43 = 15;
335 var S44 = 21;
337 function F(x, y, z) {
338 return or(and(x, y), and(not(x), z));
339 }
341 function G(x, y, z) {
342 return or(and(x, z), and(y, not(z)));
343 }
345 function H(x, y, z) {
346 return xor(xor(x, y), z);
347 }
349 function I(x, y, z) {
350 return xor(y ,or(x , not(z)));
351 }
353 function rotateLeft(a, n) {
354 return or(shl(a, n), (shr(a, (32 - n))));
355 }
357 function FF(a, b, c, d, x, s, ac) {
358 a = a + F(b, c, d) + x + ac;
359 a = rotateLeft(a, s);
360 a = a + b;
361 return a;
362 }
364 function GG(a, b, c, d, x, s, ac) {
365 a = a + G(b, c, d) + x + ac;
366 a = rotateLeft(a, s);
367 a = a + b;
368 return a;
369 }
371 function HH(a, b, c, d, x, s, ac) {
372 a = a + H(b, c, d) + x + ac;
373 a = rotateLeft(a, s);
374 a = a + b;
375 return a;
376 }
378 function II(a, b, c, d, x, s, ac) {
379 a = a + I(b, c, d) + x + ac;
380 a = rotateLeft(a, s);
381 a = a + b;
382 return a;
383 }
385 function transform(buf, offset) {
386 var a = 0, b = 0, c = 0, d = 0;
387 var x = transformBuffer;
389 a = state[0];
390 b = state[1];
391 c = state[2];
392 d = state[3];
394 for (i = 0; i < 16; i++) {
395 x[i] = and(buf[i * 4 + offset], 0xFF);
396 for (j = 1; j < 4; j++) {
397 x[i] += shl(and(buf[i * 4 + j + offset] ,0xFF), j * 8);
398 }
399 }
401 /* Round 1 */
402 a = FF( a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
403 d = FF( d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
404 c = FF( c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
405 b = FF( b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
406 a = FF( a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
407 d = FF( d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
408 c = FF( c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
409 b = FF( b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
410 a = FF( a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
411 d = FF( d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
412 c = FF( c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
413 b = FF( b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
414 a = FF( a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
415 d = FF( d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
416 c = FF( c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
417 b = FF( b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
419 /* Round 2 */
420 a = GG( a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
421 d = GG( d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
422 c = GG( c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
423 b = GG( b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
424 a = GG( a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
425 d = GG( d, a, b, c, x[10], S22, 0x2441453); /* 22 */
426 c = GG( c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
427 b = GG( b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
428 a = GG( a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
429 d = GG( d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
430 c = GG( c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
431 b = GG( b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
432 a = GG( a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
433 d = GG( d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
434 c = GG( c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
435 b = GG( b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
437 /* Round 3 */
438 a = HH( a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
439 d = HH( d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
440 c = HH( c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
441 b = HH( b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
442 a = HH( a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
443 d = HH( d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
444 c = HH( c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
445 b = HH( b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
446 a = HH( a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
447 d = HH( d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
448 c = HH( c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
449 b = HH( b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
450 a = HH( a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
451 d = HH( d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
452 c = HH( c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
453 b = HH( b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
455 /* Round 4 */
456 a = II( a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
457 d = II( d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
458 c = II( c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
459 b = II( b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
460 a = II( a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
461 d = II( d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
462 c = II( c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
463 b = II( b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
464 a = II( a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
465 d = II( d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
466 c = II( c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
467 b = II( b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
468 a = II( a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
469 d = II( d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
470 c = II( c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
471 b = II( b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
473 state[0] += a;
474 state[1] += b;
475 state[2] += c;
476 state[3] += d;
478 }
480 function init() {
481 count[0] = count[1] = 0;
482 state[0] = 0x67452301;
483 state[1] = 0xefcdab89;
484 state[2] = 0x98badcfe;
485 state[3] = 0x10325476;
486 for (i = 0; i < digestBits.length; i++) {
487 digestBits[i] = 0;
488 }
489 }
491 function update(b) {
492 var index, i;
494 index = and(shr(count[0],3) , 0x3F);
495 if (count[0] < 0xFFFFFFFF - 7) {
496 count[0] += 8;
497 } else {
498 count[1]++;
499 count[0] -= 0xFFFFFFFF + 1;
500 count[0] += 8;
501 }
502 buffer[index] = and(b, 0xff);
503 if (index >= 63) {
504 transform(buffer, 0);
505 }
506 }
508 function finish() {
509 var bits = new array(8);
510 var padding;
511 var i = 0, index = 0, padLen = 0;
513 for (i = 0; i < 4; i++) {
514 bits[i] = and(shr(count[0], (i * 8)), 0xFF);
515 }
516 for (i = 0; i < 4; i++) {
517 bits[i + 4] = and(shr(count[1], (i * 8)), 0xFF);
518 }
519 index = and(shr(count[0], 3), 0x3F);
520 padLen = (index < 56) ? (56 - index) : (120 - index);
521 padding = new array(64);
522 padding[0] = 0x80;
523 for (i = 0; i < padLen; i++) {
524 update(padding[i]);
525 }
526 for (i = 0; i < 8; i++) {
527 update(bits[i]);
528 }
530 for (i = 0; i < 4; i++) {
531 for (j = 0; j < 4; j++) {
532 digestBits[i * 4 + j] = and(shr(state[i], (j * 8)) , 0xFF);
533 }
534 }
535 }
537 /* End of the MD5 algorithm */
539 function gen() {
540 window.status = "Generating...";
541 document.getElementById('onetime').pad.value = "";
543 lower = document.getElementById('onetime').textcase.selectedIndex == 0;
544 upper = document.getElementById('onetime').textcase.selectedIndex == 1;
545 mixed = document.getElementById('onetime').textcase.selectedIndex == 2;
546 rsep = document.getElementById('onetime').rsep.checked;
547 if (!(numeric = document.getElementById('onetime').keytype[0].checked)) {
548 english = document.getElementById('onetime').keytype[1].checked;
549 gibberish = document.getElementById('onetime').keytype[3].checked;
550 }
551 clockseed = document.getElementById('onetime').seedy[0].checked
552 makesig = document.getElementById('onetime').dosig.checked;
553 npass = document.getElementById('onetime').nkeys.value;
554 pw_length = Math.round(document.getElementById('onetime').klength.value);
555 sep = document.getElementById('onetime').sep.value;
556 linelen = document.getElementById('onetime').linelen.value;
557 // 01234567890123456789012345678901
558 charcodes = " " +
559 "!\"#$%&'()*+,-./0123456789:;<=>?" +
560 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
561 "`abcdefghijklmnopqrstuvwxyz{|}~";
563 if (clockseed) {
564 var n, j, ran0;
566 /* Obtain seed from the clock. To reduce the likelihood
567 of the seed being guessed, we create the seed by combining
568 the time of the request with the time the page was loaded,
569 then use that composite value to seed an auxiliary generator
570 which is cycled between one and 32 times based on the time
571 derived initial seed, with the output of the generator fed
572 back into the seed we use to generate the pad. */
574 seed = Math.round((new Date()).getTime() % Math.pow(2, 31));
575 ran0 = new LEcuyer((seed ^ Math.round(loadTime % Math.pow(2, 31))) & 0x7FFFFFFF);
576 for (j = 0; j < (5 + ((seed >> 3) & 0xF)); j++) {
577 n = ran0.nextInt(31);
578 }
579 while (n-- >= 0) {
580 seed = ((seed << 11) | (seed >>> (32 - 11))) ^ ran0.next();
581 }
582 seed &= 0x7FFFFFFF;
583 document.getElementById('onetime').seeder.value = seed;
584 } else {
585 var useed, seedNum;
587 /* Obtain seed from user specification. If the seed is a
588 decimal number, use it as-is. If it contains any
589 non-numeric characters, construct a hash code and
590 use that as the seed. */
592 useed = document.getElementById('onetime').seeder.value;
593 seedNum = true;
594 for (i = 0; i < useed.length; i++) {
595 if ("0123456789".indexOf(useed.charAt(i)) == -1) {
596 seedNum = false;
597 break;
598 }
599 }
600 if (seedNum) {
601 seed = Math.round(Math.floor(document.getElementById('onetime').seeder.value) % Math.pow(2, 31));
602 document.getElementById('onetime').seeder.value = seed;
603 } else {
604 var s, t, iso, hex;
606 iso = "";
607 hex = "0123456789ABCDEF";
608 for (i = 32; i < 256; i++) {
609 if (i < 127 || i >= 160) {
610 // Why not "s = i.toString(16);"? Doesn't work in Netscape 3.0
611 iso += "%" + hex.charAt(i >> 4) + hex.charAt(i & 0xF);
612 }
613 }
614 iso = unescape(iso);
615 s = 0;
616 for (i = 0; i < useed.length; i++) {
617 t = iso.indexOf(useed.charAt(i));
618 if (t < 0) {
619 t = 17;
620 }
621 s = 0x7FFFFFFF & (((s << 5) | (s >> (32 - 5))) ^ t);
622 }
623 seed = s;
624 }
625 }
626 ran1 = new LEcuyer(seed);
627 ran2 = new LEcuyer(seed);
628 if (rsep) {
629 /* Use a separate random generator for separators
630 so that results are the same for a given seed
631 for both choices of separators. */
632 sepran = new LEcuyer(seed);
633 }
635 ndig = 1;
636 j = 10;
637 while (npass >= j) {
638 ndig++;
639 j *= 10;
640 }
641 pw_item = pw_length + (sep > 0 ? (pw_length / sep) : 0);
642 pw_item += ndig + 5;
643 j = pw_item * 3;
644 if (j < 132) {
645 j = 132;
646 }
647 npline = Math.floor(linelen / pw_item);
648 if (npline < 1) {
649 npline = 0;
650 }
651 v = "";
652 md5v = "";
653 lineno = 0;
654 if (!numeric) {
655 letters = "abcdefghijklmnopqrstuvwxyz";
656 if (upper) {
657 letters = letters.toUpperCase();
658 }
659 if (english) {
661 // Frequency of English digraphs (from D. Edwards 1/27/66)
663 frequency = new Array(
664 new Array(4, 20, 28, 52, 2, 11, 28, 4, 32, 4, 6, 62,
665 23, 167, 2, 14, 0, 83, 76, 127, 7, 25, 8, 1,
666 9, 1), /* aa - az */
668 new Array(13, 0, 0, 0, 55, 0, 0, 0, 8, 2, 0, 22, 0, 0,
669 11, 0, 0, 15, 4, 2, 13, 0, 0, 0, 15, 0), /* ba - bz */
671 new Array(32, 0, 7, 1, 69, 0, 0, 33, 17, 0, 10, 9, 1,
672 0, 50, 3, 0, 10, 0, 28, 11, 0, 0, 0, 3, 0), /* ca - cz */
674 new Array(40, 16, 9, 5, 65, 18, 3, 9, 56, 0, 1, 4, 15,
675 6, 16, 4, 0, 21, 18, 53, 19, 5, 15, 0, 3, 0), /* da - dz */
677 new Array(84, 20, 55, 125, 51, 40, 19, 16, 50, 1, 4,
678 55, 54, 146, 35, 37, 6, 191, 149, 65, 9, 26,
679 21, 12, 5, 0), /* ea - ez */
681 new Array(19, 3, 5, 1, 19, 21, 1, 3, 30, 2, 0, 11, 1,
682 0, 51, 0, 0, 26, 8, 47, 6, 3, 3, 0, 2, 0), /* fa - fz */
684 new Array(20, 4, 3, 2, 35, 1, 3, 15, 18, 0, 0, 5, 1,
685 4, 21, 1, 1, 20, 9, 21, 9, 0, 5, 0, 1, 0), /* ga - gz */
687 new Array(101, 1, 3, 0, 270, 5, 1, 6, 57, 0, 0, 0, 3,
688 2, 44, 1, 0, 3, 10, 18, 6, 0, 5, 0, 3, 0), /* ha - hz */
690 new Array(40, 7, 51, 23, 25, 9, 11, 3, 0, 0, 2, 38,
691 25, 202, 56, 12, 1, 46, 79, 117, 1, 22, 0,
692 4, 0, 3), /* ia - iz */
694 new Array(3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4,
695 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0), /* ja - jz */
697 new Array(1, 0, 0, 0, 11, 0, 0, 0, 13, 0, 0, 0, 0, 2,
698 0, 0, 0, 0, 6, 2, 1, 0, 2, 0, 1, 0), /* ka - kz */
700 new Array(44, 2, 5, 12, 62, 7, 5, 2, 42, 1, 1, 53, 2,
701 2, 25, 1, 1, 2, 16, 23, 9, 0, 1, 0, 33, 0), /* la - lz */
703 new Array(52, 14, 1, 0, 64, 0, 0, 3, 37, 0, 0, 0, 7,
704 1, 17, 18, 1, 2, 12, 3, 8, 0, 1, 0, 2, 0), /* ma - mz */
706 new Array(42, 10, 47, 122, 63, 19, 106, 12, 30, 1, 6,
707 6, 9, 7, 54, 7, 1, 7, 44, 124, 6, 1, 15, 0,
708 12, 0), /* na - nz */
710 new Array(7, 12, 14, 17, 5, 95, 3, 5, 14, 0, 0, 19,
711 41, 134, 13, 23, 0, 91, 23, 42, 55, 16, 28,
712 0, 4, 1), /* oa - oz */
714 new Array(19, 1, 0, 0, 37, 0, 0, 4, 8, 0, 0, 15, 1, 0,
715 27, 9, 0, 33, 14, 7, 6, 0, 0, 0, 0, 0), /* pa - pz */
717 new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
718 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0), /* qa - qz */
720 new Array(83, 8, 16, 23, 169, 4, 8, 8, 77, 1, 10, 5,
721 26, 16, 60, 4, 0, 24, 37, 55, 6, 11, 4, 0,
722 28, 0), /* ra - rz */
724 new Array(65, 9, 17, 9, 73, 13, 1, 47, 75, 3, 0, 7,
725 11, 12, 56, 17, 6, 9, 48, 116, 35, 1, 28, 0,
726 4, 0), /* sa - sz */
728 new Array(57, 22, 3, 1, 76, 5, 2, 330, 126, 1, 0, 14,
729 10, 6, 79, 7, 0, 49, 50, 56, 21, 2, 27, 0,
730 24, 0), /* ta - tz */
732 new Array(11, 5, 9, 6, 9, 1, 6, 0, 9, 0, 1, 19, 5, 31,
733 1, 15, 0, 47, 39, 31, 0, 3, 0, 0, 0, 0), /* ua - uz */
735 new Array(7, 0, 0, 0, 72, 0, 0, 0, 28, 0, 0, 0, 0, 0,
736 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0), /* va - vz */
738 new Array(36, 1, 1, 0, 38, 0, 0, 33, 36, 0, 0, 4, 1,
739 8, 15, 0, 0, 0, 4, 2, 0, 0, 1, 0, 0, 0), /* wa - wz */
741 new Array(1, 0, 2, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 0, 1,
742 5, 0, 0, 0, 3, 0, 0, 1, 0, 0, 0), /* xa - xz */
744 new Array(14, 5, 4, 2, 7, 12, 12, 6, 10, 0, 0, 3, 7,
745 5, 17, 3, 0, 4, 16, 30, 0, 0, 5, 0, 0, 0), /* ya - yz */
747 new Array(1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
748 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) /* za - zz */ );
750 // This MUST be equal to the sum of the equivalent rows above.
752 row_sums = new Array(
753 796, 160, 284, 401, 1276, 262, 199, 539, 777,
754 16, 39, 351, 243, 751, 662, 181, 17, 683,
755 662, 968, 248, 115, 180, 17, 162, 5
756 );
758 // Frequencies of starting characters.
760 start_freq = new Array(
761 1299, 425, 725, 271, 375, 470, 93, 223, 1009,
762 24, 20, 355, 379, 319, 823, 618, 21, 317,
763 962, 1991, 271, 104, 516, 6, 16, 14
764 );
766 // This MUST be equal to the sum of all elements in the above array.
768 total_sum = 11646;
769 }
770 if (gibberish) {
771 gibber = "abcdefghijklmnopqrstuvwxyz" +
772 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
773 "0123456789" +
774 "!#$%&()*+,-./:;<=>?@[]^_{|}~";
775 if (upper) {
776 /* Convert to upper case, leaving two copies of the
777 alphabet for two reasons: first, to favour letters
778 over gnarl, and second, to change only the letter case
779 when the mode is selected. */
780 gibber = gibber.toUpperCase();
781 } else if (lower) {
782 gibber = gibber.toLowerCase();
783 }
784 }
785 }
786 for (line = 1; line <= npass; line++) {
787 password = "";
788 if (numeric) {
789 for (nchars = 0; nchars < pw_length; nchars++) {
790 if ((sep > 0) && ((nchars % sep) == 0) && (nchars > 0)) {
791 password += sepchar();
792 }
793 password += ran1.nextInt(9);
794 }
795 } else if (!english) {
796 for (nchars = 0; nchars < pw_length; nchars++) {
797 if ((sep > 0) && ((nchars % sep) == 0) && (nchars > 0)) {
798 password += sepchar();
799 }
800 if (gibberish) {
801 password += gibber.charAt(ran1.nextInt(gibber.length - 1));
802 } else {
803 password += letters.charAt(ran1.nextInt(25));
804 }
805 }
806 } else {
807 position = ran1.nextInt(total_sum - 1);
808 for (row_position = 0, j = 0; position >= row_position;
809 row_position += start_freq[j], j++) {
810 continue;
811 }
813 password = letters.charAt(i = j - 1);
814 nch = 1;
815 for (nchars = pw_length - 1; nchars; --nchars) {
817 // Now find random position within the row.
819 position = ran1.nextInt(row_sums[i] - 1);
820 for (row_position = 0, j = 0;
821 position >= row_position;
822 row_position += frequency[i][j], j++) {
823 }
825 if ((sep > 0) && ((nch % sep) == 0)) {
826 password += sepchar();
827 }
828 nch++;
829 password += letters.charAt(i = j - 1);
830 }
831 }
833 if ((!numeric) && (!gibberish) && mixed) {
834 var pwm = '';
835 var j;
836 for (j = 0; j < password.length; j++) {
837 pwm += ran2.nextInt(1) ? (password.charAt(j)) : (password.charAt(j).toUpperCase());
838 }
839 password = pwm;
840 }
842 /* If requested, calculate the MD5 signature for this key and
843 and save for later appending to the results. */
845 if (makesig) {
846 var n, m, hex = "0123456789ABCDEF";
848 init();
849 for (m = 0; m < password.length; m++) {
850 update(32 + charcodes.indexOf(password.charAt(m)));
851 }
852 finish();
854 for (n = 0; n < 16; n++) {
855 md5v += hex.charAt(digestBits[n] >> 4);
856 md5v += hex.charAt(digestBits[n] & 0xF);
857 }
858 md5v += "\n";
859 }
861 aline = "" + line;
862 while (aline.length < ndig) {
863 aline = " " + aline;
864 }
865 v += aline + ") " + password;
867 if ((++lineno) >= npline) {
868 v += "\n";
869 lineno = 0;
870 } else {
871 v += " ";
872 }
873 }
875 if (makesig) {
876 v += "\n---------- MD5 Signatures ----------\n" + md5v;
877 }
879 document.getElementById('onetime').pad.value = v;
880 window.status = "Done.";
881 }
883 function loadHandler() {
884 for (var i = 0; i < 25; i++) {
885 gen();
886 }
887 };
889 //]]>
890 </script>
892 </head>
894 <body class="jsgen" onload="loadHandler();">
896 <h1><img src="key.gif" class="keyicon" alt=""
897 width="40" height="40" /> One-Time Pad Generator</h1>
899 <p>
900 This page, which requires that your browser support JavaScript
901 (see <a href="#why"><cite>Why JavaScript</cite></a> below),
902 generates one-time pads or password lists in a variety of
903 forms. It is based a high-quality pseudorandom sequence
904 generator, which can be seeded either from the current date
905 and time, or from a seed you provide. Fill in the form below
906 to select the format of the pad and press “Generate” to
907 create the pad in the text box. You can then copy and paste
908 the generated pad into another window to use as you wish.
909 Each of the labels on the request form is linked to a description
910 of that parameter.
911 </p>
913 <form id="onetime" action="#" onsubmit="return false;">
915 <p class="centre">
916 <b>Output:</b>
917 <a href="#NumberOfKeys">Number of keys</a>: <input type="text" name="nkeys" value="20" size="4" maxlength="12" />
918 <a href="#LineLength">Line length</a>: <input type="text" name="linelen" value="48" size="3" maxlength="12" />
919 <br />
920 <b>Format:</b>
921 <a href="#KeyLength">Key length</a>: <input type="text" name="klength" value="8" size="3" maxlength="12" />
922 <a href="#GroupLength">Group length</a>: <input type="text" name="sep" value="4" size="2" maxlength="12" />
924 <br />
925 <b>Composition:</b>
926 <a href="#KeyText">Key text</a>: <input type="radio" name="keytype" /> Numeric
927 <input type="radio" name="keytype" /> Word-like
928 <input type="radio" name="keytype" checked="checked" /> Alphabetic
929 <input type="radio" name="keytype" /> Gibberish
930 <br />
931 <a href="#LetterCase">Letters:</a>
932 <select size="i" name="textcase">
934 <option value="1" selected="selected">Lower case</option>
935 <option value="2">Upper case</option>
936 <option value="3">Mixed case</option>
937 </select>
939 <input type="checkbox" name="rsep" /> <a href="#RandomSep">Random separators</a>
940 <input type="checkbox" name="dosig" /> <a href="#Signatures">Include signatures</a>
942 <br />
943 <b><a href="#Seed">Seed:</a></b>
944 <input type="radio" name="seedy" checked="checked" /> From clock
945 <input type="radio" name="seedy" /> User-defined:
946 <input type="text" name="seeder" value="" size="12" maxlength="128"
947 onchange="document.getElementById('onetime').seedy[1].checked=true;" />
948 <br />
949 <input type="button" value=" Generate " onclick="gen();" />
950
951 <input type="button" value=" Clear " onclick="document.getElementById('onetime').pad.value = '';" />
952
953 <input type="button" value=" Select " onclick="document.getElementById('onetime').pad.select();" /><br />
954 <textarea name="pad" rows="12" cols="72">
956 Uh, oh. It appears your browser either does not support
957 JavaScript or that JavaScript has been disabled. You'll
958 have to replace your browser with one supporting JavaScript
959 (or enable it, if that's the problem) before you can use
960 this page.
961 </textarea>
962 </p>
964 </form>
966 <script type="text/javascript">
967 //<![CDATA[
968 // Clear out "sorry, no JavaScript" message from text box.
969 document.getElementById('onetime').pad.value = "";
970 //]]>
971 </script>
973 <h2><a name="details">Details</a></h2>
975 <p>
976 Each of the fields in the one-time pad request form is described
977 below.
978 </p>
980 <h3><a name="output">Output</a></h3>
982 <h4><a name="NumberOfKeys">Number of keys</a></h4>
984 <p>
985 Enter the number of keys you'd like to generate. If you generate
986 more than fit in the results text box, you can use the scroll
987 bar to view the additional lines.
988 </p>
990 <h4><a name="LineLength">Line length</a></h4>
992 <p>
993 Lines in the output will be limited to the given length (or contain
994 only one key if the line length is less than required for a single
995 key). If the line length is greater than the width of the results
996 box, you can use the horizontal scroll bar to view the rest of the
997 line. Enter <tt>0</tt> to force one key per line; this is handy
998 when you're preparing a list of keys to be read by a computer program.
999 </p>
1001 <h3><a name="format">Format</a></h3>
1003 <h4><a name="KeyLength">Key length</a></h4>
1005 <p>
1006 Each key will contain this number of characters, not counting
1007 separators between groups.
1008 </p>
1010 <h4><a name="GroupLength">Group length</a></h4>
1012 <p>
1013 If a nonzero value is entered in this field, the key will be broken
1014 into groups of the given number of characters by separators. Humans
1015 find it easier to read and remember sequences of characters when
1016 divided into groups of five or fewer characters.
1017 </p>
1019 <h3><a name="composition">Composition</a></h3>
1021 <h4><a name="KeyText">Key text</a></h4>
1023 <p>
1024 This set of radio buttons lets you select the character set used in
1025 the keys. The alternatives are listed in order of
1026 increasing security.
1027 </p>
1029 <blockquote>
1030 <dl>
1031 <dt><b>Numeric</b></dt>
1032 <dd>Keys contain only the decimal digits “0” through “9”.
1033 <em>Least secure.</em></dd>
1035 <dt><b>Word-like</b></dt>
1036 <dd>Keys are composed of alphabetic characters which obey the
1037 digraph statistics of English text. Such keys contain
1038 sequences of vowels and consonants familiar to speakers
1039 of Western languages, and are therefore usually easier to
1040 memorise but, for a given key length, are less secure than
1041 purely random letters.</dd>
1043 <dt><b>Alphabetic</b></dt>
1044 <dd>Keys consist of letters of the alphabet chosen at random.
1045 Each character has an equal probability of being one of
1046 the 26 letters.</dd>
1048 <dt><b>Gibberish</b></dt>
1049 <dd>Keys use most of the printable ASCII character set, excluding
1050 only characters frequently used for quoting purposes. This
1051 option provides the greatest security for a given key length,
1052 but most people find keys like this difficult to memorise or
1053 even transcribe from a printed pad. If a human is in the loop,
1054 it's often better to use a longer alphabetic or word-like key.
1055 <em>Most secure.</em></dd>
1056 </dl>
1058 </blockquote>
1060 <h4><a name="LetterCase">Letters</a></h4>
1062 <p>
1063 The case of letters in keys generated with Word-like, Alphabetic, and
1064 Gibberish key text will be as chosen. Most people find it easier to
1065 read lower case letters than all capitals, but for some applications
1066 (for example, where keys must be scanned optically by hardware that
1067 only recognises capital letters), capitals are required. Selecting
1068 “Mixed case” creates keys with a mix of upper- and
1069 lower-case letters; such keys are more secure than those with uniform
1070 letter case, but do not pass the “telephone test”: you
1071 can't read them across a (hopefully secure) voice link without having
1072 to indicate whether each letter is or is not a capital.
1073 </p>
1075 <h4><a name="RandomSep">Random separators</a></h4>
1077 <p>
1078 When the <a href="#KeyLength">Key length</a> is longer than
1079 a nonzero <a href="#GroupLength">Group length</a> specification,
1080 the key is divided into sequences of the given group length
1081 by separator characters. By default, a hyphen, “<tt>-</tt>”, is used
1082 to separate groups. If you check this box, separators will be
1083 chosen at random among punctuation marks generally acceptable
1084 for applications such as passwords. If you're generating passwords
1085 for a computer system, random separators dramatically increase
1086 the difficulty of guessing passwords by exhaustive search.
1087 </p>
1089 <h4><a name="Signatures">Include signatures</a></h4>
1091 <p>
1093 When this box is checked, at the end of the list of keys, preceded by
1094 a line beginning with ten dashes “<tt>-</tt>”, the 128 bit MD5 signature of
1095 each key is given, one per line, with signatures expressed as 32
1096 hexadecimal digits. Key signatures can be used to increase security
1097 when keys are used to control access to computer systems or databases.
1098 Instead of storing a copy of the keys, the computer stores their
1099 signatures. When the user enters a key, its signature is computed
1100 with the same MD5 algorithm used to generate it initially, and the key
1101 is accepted only if the signature matches. Since discovering
1102 a key which will generate a given signature is believed to be
1103 computationally prohibitive, even if the list of signatures stored on
1104 the computer is compromised, that information will not permit an
1105 intruder to deduce a valid key.
1106 </p>
1108 <p>
1109 Signature calculation is a computationally intense process for which
1110 JavaScript is not ideally suited; be patient while signatures are
1111 generated, especially if your computer has modest
1112 processing speed.
1113 </p>
1115 <p>
1116 For signature-based validation to be secure, it is essential
1117 the original keys be long enough to prohibit discovery of matching
1118 signatures by exhaustive search. Suppose, for example, one used
1119 four digit numeric keys, as used for Personal Identification
1120 Numbers (PINs) by many credit card systems. Since only 10,000
1121 different keys exist, one could simply compute the signatures of
1122 every possible key from 0000 through 9999, permitting an attacker who
1123 came into possession of the table of signatures to recover the
1124 keys by a simple lookup process. For maximum security, keys must
1125 contain at least as much information as the 128 bit signatures
1126 computed from them. This implies a minimum key length (not counting
1127 non-random separator characters) for the various key formats as
1128 follows:
1129 </p>
1131 <table class="c" border="border" cellpadding="4">
1132 <tr><th>Key Composition</th> <th>Minimum Characters</th></tr>
1134 <tr><td>Numeric</td> <td class="c">39</td></tr>
1135 <tr><td>Word-like</td> <td class="c">30</td></tr>
1136 <tr><td>Alphabetic</td> <td class="c">28</td></tr>
1137 <tr><td>Gibberish</td> <td class="c">20</td></tr>
1138 </table>
1140 <p>
1141 It should be noted that for many practical applications there is no
1142 need for anything approaching 128-bit security. The guidelines above
1143 apply only in the case where maximum protection in the event of
1144 undetected compromise of key signatures occurs. In many
1145 cases, much shorter keys are acceptable, especially when it is assumed
1146 that a compromise of the system's password or signature database would
1147 be only part of a much more serious subversion of all resources
1148 on the system.
1149 </p>
1151 <h3><a name="Seed">Seed</a></h3>
1153 <p>
1154 The <em>seed</em> is the starting value which determines all
1155 subsequent values in the pseudorandom sequence used to generate
1156 the one-time pad. Given the seed, the pad can be reproduced. The
1157 seed is a 31-bit number which can be derived from the date and
1158 time at which the one-time pad was requested, or from a
1159 user-defined seed value. If the user-defined seed consists
1160 entirely of decimal digits, it is used directly as the seed,
1161 modulo 2<sup>31</sup>; if a string containing non-digit characters
1162 is entered, it is used to compute a <em>hash code</em> which is
1163 used to seed the generator.
1165 </p>
1167 <p>
1168 When the clock is used to create the seed, the seed value is entered
1169 in the User-defined box to allow you, by checking “User-defined”,
1170 to produce additional pads with the same seed.
1171 </p>
1173 <h2><a name="why">Why JavaScript?</a></h2>
1175 <p>
1176 At first glance, JavaScript may seem an odd choice for programming
1177 a page such as this. The one-time pad generator program is rather
1178 large and complicated, and downloading it to your browser takes longer
1179 than would be required for a Java applet or to transfer a
1180 one-time pad generated by a CGI program on the Web server. I chose
1181 JavaScript for two reasons: <em>security</em> and <em>transparency</em>.
1183 </p>
1185 <p>
1186 <b>Security.</b>
1187 The sole reason for the existence of one-time pads is to
1188 provide a source of information known only to people to whom
1189 they have been distributed in a secure manner. This means
1190 the generation process cannot involve any link whose security
1191 is suspect. If the pad were generated on a Web server and
1192 transmitted to you, it would have to pass over the
1193 Internet, where any intermediate site might make a copy
1194 of your pad before you even received it. Even if some
1195 mechanism such as encryption could absolutely prevent the
1196 pad's being intercepted, you'd still have no way to be sure
1197 the site generating the pad didn't keep a copy
1198 in a file, conveniently tagged with your Internet address.
1199 </p>
1201 <p>
1202 In order to have any degree of security, it is essential
1203 that the pad be generated on <em>your</em> computer, without
1204 involving any transmission or interaction with other
1205 sites on the Internet. A Web browser with JavaScript makes
1206 this possible, since the generation program embedded in this
1207 page runs entirely on your own computer and does not
1208 transmit anything over the Internet. Its output appears
1209 only in the text box, allowing you to cut and paste it
1210 to another application. From there on, its security is
1211 up to you.
1212 </p>
1214 <p>
1215 Security is never absolute. A one-time pad generated with
1216 this page might be compromised in a variety of ways, including
1217 the following:
1219 </p>
1221 <ul>
1222 <li> Your Web browser and/or JavaScript interpreter may
1223 contain bugs or deliberate security violations
1224 which report activity on your computer back to some
1225 other Internet site.</li>
1227 <li> Some other applet running on another page of your
1228 browser, perhaps without your being aware of its
1229 existence, is spying on other windows.</li>
1231 <li> Some other application running on your computer
1232 may have compromised your system's security and
1233 be snooping on your activity.</li>
1235 <li> Your Web browser may be keeping a “history log”
1237 or “cache” of data you generate. Somebody may
1238 come along later and recover a copy of the pad
1239 from that log.</li>
1241 <li> The implementation of this page may contain a bug
1242 or deliberate error which makes its output
1243 predictable. This is why <a href="#trans"><cite>transparency</cite></a>,
1244 discussed below, is essential.</li>
1246 <li> Your computer's security may have been compromised
1247 physically; when's the last time you checked that a
1248 bug that transmits your keystrokes and/or screen
1249 contents to that white van parked down the street
1250 wasn't lurking inside your computer cabinet?</li>
1251 </ul>
1253 <p>
1254 One can whip oneself into a fine fever of paranoia worrying about
1255 things like this. One way to rule out the most probable risks
1256 is to download a copy of the generator page and run it
1257 from a “<tt>file:</tt>” URL on a computer which has no network
1258 connection whatsoever and is located in a secure location
1259 under your control. And look very carefully at any files
1260 created by your Web browser. You may find the most interesting
1261 things squirreled away there….
1262 </p>
1264 <p>
1265 <b><a name="trans">Transparency</a>.</b>
1266 Any security-related tool is only as good as its design
1267 and implementation. <em>Transparency</em> means that, in
1268 essence, all the moving parts are visible so you can judge
1269 for yourself whether the tool merits your confidence. In
1270 the case of a program, this means that source code must
1271 be available, and that you can verify that the program
1272 you're running corresponds to the source code provided.
1274 </p>
1276 <p>
1277 The very nature of JavaScript achieves this transparency.
1278 The program is embedded into this actual Web page; to
1279 examine it you need only use your browser's “View Source”
1280 facility, or save the page into a file on your computer
1281 and read it with a text editor. JavaScript's being
1282 an interpreted language eliminates the risk of your running
1283 a program different from the purported source code: with
1284 an interpreted language what you read is what you run.
1285 </p>
1287 <p>
1288 Transparency is important even if you don't know enough about
1289 programming or security to determine whether the program
1290 contains any flaws. The very fact that it can be examined
1291 by anybody allows those with the required expertise to pass
1292 judgment, and you can form your own conclusions based on
1293 their analysis.
1294 </p>
1296 <h2>Credits</h2>
1298 <p>
1300 The pseudorandom sequence generator is based on L'Ecuyer's
1301 two-sequence generator as described in
1302 <cite>Communications of the ACM</cite>, Vol. 31 (1968), page 742.
1303 A Bays-Durham shuffle is used to guard against regularities
1304 lurking in L'Ecuyer's algorithm; see
1305 <cite>ACM Transactions on Mathematical Software</cite>, Vol. 2 (1976)
1306 pages 59–64 for details.
1307 </p>
1309 <p>
1310 The JavaScript implementation of the
1311 <a href="http://www.ietf.org/rfc/rfc1321.txt"><b>MD5 message-digest algorithm</b></a>
1312 was developed by Henri Torgemane; please view the source code of this
1313 page to examine the code, including the copyright notice and
1314 conditions of use. The MD5 algorithm was developed by Ron Rivest.
1315 </p>
1317 <p />
1319 <hr />
1321 <p />
1323 <table class="r">
1324 <tr><td align="center">
1325 <a class="i" href="http://validator.w3.org/check?uri=referer"><img
1326 class="button"
1327 src="valid-xhtml10.png"
1328 alt="Valid XHTML 1.0" height="31" width="88" /></a>
1329 </td></tr>
1330 </table>
1332 <address>
1333 by <a href="/">John Walker</a><br />
1334 May 26, 1997<br />
1336 Updated: November 2006
1337 </address>
1339 <p class="centre">
1340 <em>This document is in the public domain.</em>
1341 </p>
1342 </body>
1343 </html>