Working on Login functions, wrestling with JavaScript
[CGIscriptor.git] / Private / Login.html
blobdcb5f5b92c1af95119b9e8fe2ec21ed34d87c85b
1 <html>
2 <head>
3 <title>Login</title>
4 <SCRIPT TYPE="text/ssperl" CGI="$REMOTE_ADDR $RANDOMHASHCMD $SHASUMCMD">
5 # Create Session Ticket
6 open(URANDOM, "$RANDOMHASHCMD |") || die "URANDOM; $RANDOMHASHCMD | $!\n";
7 $SESSIONTICKET = <URANDOM>;
8 close(URANDOM);
9 chomp($SESSIONTICKET);
11 # Create Login Ticket
12 open(URANDOM, "$RANDOMHASHCMD |") || die "URANDOM; $!\n";
13 $LOGINTICKET= <URANDOM>;
14 close(URANDOM);
15 chomp($LOGINTICKET);
17 # Create Random Hash Salt
18 open(URANDOM, "$RANDOMHASHCMD |") || die "URANDOM; $RANDOMHASHCMD | $!\n";
19 $RANDOMSALT= <URANDOM>;
20 close(URANDOM);
21 chomp($RANDOMSALT);
23 # Create SALT file if it does not exist
24 # Remove this, including test account for life system
25 unless(-s "~/Private/.Passwords/SALT")
27 open(URANDOM, "$RANDOMHASHCMD |") || die "URANDOM; $!\n";
28 $SALT= <URANDOM>;
29 chomp($SALT);
30 close(URANDOM);
31 open(SALTFILE, ">~/Private/.Passwords/SALT") || die ">~/Private/.Passwords/SALT: $!\n";
32 print SALTFILE "$SALT\n";
33 close(SALTFILE);
35 # Update test account (should be removed in live system)
36 if(-s "~/Private/.Passwords/test")
38 my $storedpassword = `bash -c 'echo -n ${SALT}test | $ENV{"SHASUMCMD"}'`;
39 chomp($storedpassword);
40 open(USERFILE, "<~/Private/.Passwords/test") || die "</Private/.Passwords/test: $!\n";
41 @USERlines = <USERFILE>;
42 close(USERFILE);
44 open(USERFILE, ">~/Private/.Passwords/test") || die ">/Private/.Passwords/test: $!\n";
45 # Add Password and Salt
46 foreach my $line (@USERlines)
48 $line =~ s/^Password: (.*)$/Password: $storedpassword/ig;
49 $line =~ s/^Salt: (.*)$/Salt: $SALT/ig;
51 print USERFILE $line;
53 close(USERFILE);
58 # Read in site Salt
59 open(SALTFILE, "<~/Private/.Passwords/SALT") || die "~/Private/.Passwords/SALT: $!\n";
60 $SALT=<SALTFILE>;
61 close(SALTFILE);
62 chomp($SALT);
64 # Create login session ticket
65 open(LOGINTICKET, ">~/Private/.Sessions/$LOGINTICKET") || die "~/Private/.Sessions/$LOGINTICKET: $!\n";
66 print LOGINTICKET << "ENDOFLOGINTICKET";
67 Type: LOGIN
68 IPaddress: $REMOTE_ADDR
69 Salt: $SALT
70 Session: $SESSIONTICKET
71 Randomsalt: $RANDOMSALT
72 Expires: +600s
73 ENDOFLOGINTICKET
74 close(LOGINTICKET);
75 "";
76 </SCRIPT>
77 <SCRIPT type="text/javascript" LANGUAGE="JavaScript">
78 function createCookie(name,value,days,path) {
79 if (days) {
80 var date = new Date();
81 date.setTime(date.getTime()+(days*24*60*60*1000));
82 var expires = "; expires="+date.toGMTString();
84 else var expires = "";
85 document.cookie = name+"="+value+expires+"; path=/"+path;
88 function readCookie(name) {
89 var nameEQ = name + "=";
90 var ca = document.cookie.split(';');
91 for(var i=0;i < ca.length;i++) {
92 var c = ca[i];
93 while (c.charAt(0)==' ') c = c.substring(1,c.length);
94 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
96 return null;
99 function eraseCookie(name) {
100 createCookie(name,"",-1);
102 </SCRIPT>
103 <script type="text/javascript">
104 // Combine the PASSWORD with the site SALT and hash it
105 // Combine this Hash with the extra SALT, and hash them
106 function HashPassword(extsalt) {
107 var hash1 = "";
108 var hash2 = "";
109 var passwordvalue = document.getElementById('PASSWORD');
110 var saltvalue = document.getElementById('SALT');
111 hash1 = hex_sha1(saltvalue.value+passwordvalue.value);
112 if(extsalt != "")
113 hash2 = hex_sha1(extsalt+hash1);
114 else
115 hash2 = hash1;
116 passwordvalue.value = hash2;
117 return hash2;
120 function remove_cgiparam(elem, attr, parameter) {
121 var elems = document.getElementsByTagName(elem);
122 for (var i = 0; i < elems.length; i++)
124 var n=elems[i][attr].indexOf("&"+parameter);
125 if(n>0)
126 elems[i][attr] = elems[i][attr].replace("&"+param, "");
127 var n=elems[i][attr].indexOf(parameter);
128 if(n>0)
129 elems[i][attr] = elems[i][attr].replace(param, "");
133 function check_username_password ( ) {
134 var username = document.getElementById('USERNAME');
135 var password = document.getElementById('PASSWORD');
136 if(username.value.match(/[a-zA-Z0-9]/) && password.value.match(/[a-zA-Z0-9]/))
137 return true;
138 alert("Please enter a user name and password");
139 return false;
142 // Remove LOGOUT and SESSIONTICKET from Login window
143 window.onload = function() {
144 var location = window.location.href;
145 if(location.indexOf("LOGOUT&") >= 0)
146 window.location.href = location.replace("LOGOUT&", "");
147 else if(location.indexOf("?LOGOUT") >= 0)
148 window.location.href = location.replace("?LOGOUT", "");
149 location = window.location.href;
150 if(location.indexOf("SESSIONTICKET") >= 0)
151 window.location.href = location.replace(/[\?&]?SESSIONTICKET=[a-z0-9A-Z]+/, "");
153 </SCRIPT>
155 <script type="text/javascript">
157 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
158 * in FIPS 180-1
159 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
160 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
161 * Distributed under the BSD License
162 * See http://pajhome.org.uk/crypt/md5 for details.
166 * Configurable variables. You may need to tweak these to be compatible with
167 * the server-side, but the defaults work in most cases.
169 var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
170 var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
173 * These are the functions you'll usually want to call
174 * They take string arguments and return either hex or base-64 encoded strings
176 function hex_sha1(s) { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); }
177 function b64_sha1(s) { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); }
178 function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); }
179 function hex_hmac_sha1(k, d)
180 { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
181 function b64_hmac_sha1(k, d)
182 { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
183 function any_hmac_sha1(k, d, e)
184 { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
187 * Perform a simple self-test to see if the VM is working
189 function sha1_vm_test()
191 return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d";
195 * Calculate the SHA1 of a raw string
197 function rstr_sha1(s)
199 return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8));
203 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
205 function rstr_hmac_sha1(key, data)
207 var bkey = rstr2binb(key);
208 if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8);
210 var ipad = Array(16), opad = Array(16);
211 for(var i = 0; i < 16; i++)
213 ipad[i] = bkey[i] ^ 0x36363636;
214 opad[i] = bkey[i] ^ 0x5C5C5C5C;
217 var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
218 return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160));
222 * Convert a raw string to a hex string
224 function rstr2hex(input)
226 try { hexcase } catch(e) { hexcase=0; }
227 var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
228 var output = "";
229 var x;
230 for(var i = 0; i < input.length; i++)
232 x = input.charCodeAt(i);
233 output += hex_tab.charAt((x >>> 4) & 0x0F)
234 + hex_tab.charAt( x & 0x0F);
236 return output;
240 * Convert a raw string to a base-64 string
242 function rstr2b64(input)
244 try { b64pad } catch(e) { b64pad=''; }
245 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
246 var output = "";
247 var len = input.length;
248 for(var i = 0; i < len; i += 3)
250 var triplet = (input.charCodeAt(i) << 16)
251 | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
252 | (i + 2 < len ? input.charCodeAt(i+2) : 0);
253 for(var j = 0; j < 4; j++)
255 if(i * 8 + j * 6 > input.length * 8) output += b64pad;
256 else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
259 return output;
263 * Convert a raw string to an arbitrary string encoding
265 function rstr2any(input, encoding)
267 var divisor = encoding.length;
268 var remainders = Array();
269 var i, q, x, quotient;
271 /* Convert to an array of 16-bit big-endian values, forming the dividend */
272 var dividend = Array(Math.ceil(input.length / 2));
273 for(i = 0; i < dividend.length; i++)
275 dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
279 * Repeatedly perform a long division. The binary array forms the dividend,
280 * the length of the encoding is the divisor. Once computed, the quotient
281 * forms the dividend for the next step. We stop when the dividend is zero.
282 * All remainders are stored for later use.
284 while(dividend.length > 0)
286 quotient = Array();
287 x = 0;
288 for(i = 0; i < dividend.length; i++)
290 x = (x << 16) + dividend[i];
291 q = Math.floor(x / divisor);
292 x -= q * divisor;
293 if(quotient.length > 0 || q > 0)
294 quotient[quotient.length] = q;
296 remainders[remainders.length] = x;
297 dividend = quotient;
300 /* Convert the remainders to the output string */
301 var output = "";
302 for(i = remainders.length - 1; i >= 0; i--)
303 output += encoding.charAt(remainders[i]);
305 /* Append leading zero equivalents */
306 var full_length = Math.ceil(input.length * 8 /
307 (Math.log(encoding.length) / Math.log(2)))
308 for(i = output.length; i < full_length; i++)
309 output = encoding[0] + output;
311 return output;
315 * Encode a string as utf-8.
316 * For efficiency, this assumes the input is valid utf-16.
318 function str2rstr_utf8(input)
320 var output = "";
321 var i = -1;
322 var x, y;
324 while(++i < input.length)
326 /* Decode utf-16 surrogate pairs */
327 x = input.charCodeAt(i);
328 y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
329 if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
331 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
332 i++;
335 /* Encode output as utf-8 */
336 if(x <= 0x7F)
337 output += String.fromCharCode(x);
338 else if(x <= 0x7FF)
339 output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
340 0x80 | ( x & 0x3F));
341 else if(x <= 0xFFFF)
342 output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
343 0x80 | ((x >>> 6 ) & 0x3F),
344 0x80 | ( x & 0x3F));
345 else if(x <= 0x1FFFFF)
346 output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
347 0x80 | ((x >>> 12) & 0x3F),
348 0x80 | ((x >>> 6 ) & 0x3F),
349 0x80 | ( x & 0x3F));
351 return output;
355 * Encode a string as utf-16
357 function str2rstr_utf16le(input)
359 var output = "";
360 for(var i = 0; i < input.length; i++)
361 output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
362 (input.charCodeAt(i) >>> 8) & 0xFF);
363 return output;
366 function str2rstr_utf16be(input)
368 var output = "";
369 for(var i = 0; i < input.length; i++)
370 output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
371 input.charCodeAt(i) & 0xFF);
372 return output;
376 * Convert a raw string to an array of big-endian words
377 * Characters >255 have their high-byte silently ignored.
379 function rstr2binb(input)
381 var output = Array(input.length >> 2);
382 for(var i = 0; i < output.length; i++)
383 output[i] = 0;
384 for(var i = 0; i < input.length * 8; i += 8)
385 output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
386 return output;
390 * Convert an array of big-endian words to a string
392 function binb2rstr(input)
394 var output = "";
395 for(var i = 0; i < input.length * 32; i += 8)
396 output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
397 return output;
401 * Calculate the SHA-1 of an array of big-endian words, and a bit length
403 function binb_sha1(x, len)
405 /* append padding */
406 x[len >> 5] |= 0x80 << (24 - len % 32);
407 x[((len + 64 >> 9) << 4) + 15] = len;
409 var w = Array(80);
410 var a = 1732584193;
411 var b = -271733879;
412 var c = -1732584194;
413 var d = 271733878;
414 var e = -1009589776;
416 for(var i = 0; i < x.length; i += 16)
418 var olda = a;
419 var oldb = b;
420 var oldc = c;
421 var oldd = d;
422 var olde = e;
424 for(var j = 0; j < 80; j++)
426 if(j < 16) w[j] = x[i + j];
427 else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
428 var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
429 safe_add(safe_add(e, w[j]), sha1_kt(j)));
430 e = d;
431 d = c;
432 c = bit_rol(b, 30);
433 b = a;
434 a = t;
437 a = safe_add(a, olda);
438 b = safe_add(b, oldb);
439 c = safe_add(c, oldc);
440 d = safe_add(d, oldd);
441 e = safe_add(e, olde);
443 return Array(a, b, c, d, e);
448 * Perform the appropriate triplet combination function for the current
449 * iteration
451 function sha1_ft(t, b, c, d)
453 if(t < 20) return (b & c) | ((~b) & d);
454 if(t < 40) return b ^ c ^ d;
455 if(t < 60) return (b & c) | (b & d) | (c & d);
456 return b ^ c ^ d;
460 * Determine the appropriate additive constant for the current iteration
462 function sha1_kt(t)
464 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
465 (t < 60) ? -1894007588 : -899497514;
469 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
470 * to work around bugs in some JS interpreters.
472 function safe_add(x, y)
474 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
475 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
476 return (msw << 16) | (lsw & 0xFFFF);
480 * Bitwise rotate a 32-bit number to the left.
482 function bit_rol(num, cnt)
484 return (num << cnt) | (num >>> (32 - cnt));
486 </script>
488 </head>
489 <body>
490 <h1 align=CENTER>Example of Login procedure</h1>
492 Simple and very unsafe example login page for CGIscriptor.pl. The password is first hashed with the
493 site specific salt (as it is used to store the password on-site). Then it is hashed with a random,
494 one-time salt. Effectively, creating a one-time password. Only the last value is send to the server.
495 The server has both salt values stored. It will ignore anything except the username, hashed password, and
496 loginticket.
497 </p>
499 The Session Ticket is stored in a cookie with the name <em>CGIscriptor</em>.
500 </p>
502 <form method="POST" action="" id="LoginForm"
503 onSubmit='var success=check_username_password();HashPassword("<SCRIPT TYPE="text/ssperl">
504 $RANDOMSALT</SCRIPT>");createCookie("CGIscriptor","<SCRIPT TYPE="text/ssperl">$SESSIONTICKET</SCRIPT>",1,"Private/");success'>
505 <div style="margin-left: 30%; margin-right: 30%; text-align: left">
506 Username: <input type="text" name="USERNAME" id="USERNAME" size="20" /><br />
507 Password: <input type="PASSWORD" name="PASSWORD" id="PASSWORD" size="20" /><br />
508 <input type="hidden" name="SALT" id="SALT" value="<SCRIPT TYPE="text/ssperl">$SALT</SCRIPT>" /><br />
509 <input type="hidden" name="LOGINTICKET" value="<SCRIPT TYPE="text/ssperl">$LOGINTICKET</SCRIPT>" /><br />
510 </div>
511 <div align="center">
512 <p><input type="submit" value="Login" /></p>
513 </div>
514 </form>
516 The Salt and Ticket values are all created using SHA1 on 512 Byte of output from <em>/dev/urandom</em> in HEX.
517 </p>
518 <FONT STYLE="font-size:small">
519 <p> Example Login page for CGIscriptor.pl<br />
520 Copyright &copy; 2012 R.J.J.H. van Son<br />
521 This program is free software: you can redistribute it and/or modify
522 it under the terms of the GNU General Public License as published by
523 the Free Software Foundation, either version 3 of the License, or
524 (at your option) any later version.
525 This program is distributed in the hope that it will be useful,
526 but WITHOUT ANY WARRANTY; without even the implied warranty of
527 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
528 GNU General Public License for more details.<br />
529 You should have received a copy of the GNU General Public License
530 along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.</p>
531 <p> JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1<br />
532 Copyright &copy; 2000 - 2009 Paul Johnston, Version 2.2<br />
533 Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet<br />
534 Distributed under the BSD License<br />
535 See <a href="http://pajhome.org.uk/crypt/md5">http://pajhome.org.uk/crypt/md5</a> for details.
536 </FONT>
538 </body>
539 </html>