Working on login and ticket system
[CGIscriptor.git] / Private / Login.html
blobf61c007d293571c73930f6cb52f0420512d311ec
1 <html>
2 <head>
3 <title>Login</title>
4 <SCRIPT TYPE="text/ssperl" CGI="$REMOTE_ADDR $RANDOMHASHCMD">
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 # Read in site Salt
24 open(SALTFILE, "<~/Private/.Passwords/SALT") || die "~/Private/.Passwords/SALT: $!\n";
25 $SALT=<SALTFILE>;
26 close(SALTFILE);
27 chomp($SALT);
29 # Create login session ticket
30 open(LOGINTICKET, ">~/Private/.Sessions/$LOGINTICKET") || die "~/Private/.Sessions/$LOGINTICKET: $!\n";
31 print LOGINTICKET << "ENDOFLOGINTICKET";
32 Type: LOGIN
33 IPaddress: $REMOTE_ADDR
34 Salt: $SALT
35 Session: $SESSIONTICKET
36 Randomsalt: $RANDOMSALT
37 Expires: 600s
38 ENDOFLOGINTICKET
39 close(LOGINTICKET);
40 "";
41 </SCRIPT>
42 <SCRIPT LANGUAGE="JavaScript">
43 function createCookie(name,value,days,path) {
44 if (days) {
45 var date = new Date();
46 date.setTime(date.getTime()+(days*24*60*60*1000));
47 var expires = "; expires="+date.toGMTString();
49 else var expires = "";
50 document.cookie = name+"="+value+expires+"; path=/"+path;
53 function readCookie(name) {
54 var nameEQ = name + "=";
55 var ca = document.cookie.split(';');
56 for(var i=0;i < ca.length;i++) {
57 var c = ca[i];
58 while (c.charAt(0)==' ') c = c.substring(1,c.length);
59 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
61 return null;
64 function eraseCookie(name) {
65 createCookie(name,"",-1);
67 </SCRIPT>
68 <script type="text/javascript">
69 // Combine the password with the site SALT and hash it
70 // Combine this Hash iwth the extra SALT, and hash them
71 function HashPassword(extsalt) {
72 var hash1 = "";
73 var hash2 = "";
74 var passwordvalue = document.getElementById('password');
75 var saltvalue = document.getElementById('salt');
76 hash1 = hex_sha1(saltvalue.value+passwordvalue.value);
77 if(extsalt != "")
78 hash2 = hex_sha1(extsalt+hash1);
79 else
80 hash2 = hash1;
81 passwordvalue.value = hash2;
82 return hash2;
84 </SCRIPT>
86 <script type="text/javascript">
88 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
89 * in FIPS 180-1
90 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
91 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
92 * Distributed under the BSD License
93 * See http://pajhome.org.uk/crypt/md5 for details.
97 * Configurable variables. You may need to tweak these to be compatible with
98 * the server-side, but the defaults work in most cases.
100 var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
101 var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
104 * These are the functions you'll usually want to call
105 * They take string arguments and return either hex or base-64 encoded strings
107 function hex_sha1(s) { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); }
108 function b64_sha1(s) { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); }
109 function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); }
110 function hex_hmac_sha1(k, d)
111 { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
112 function b64_hmac_sha1(k, d)
113 { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
114 function any_hmac_sha1(k, d, e)
115 { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
118 * Perform a simple self-test to see if the VM is working
120 function sha1_vm_test()
122 return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d";
126 * Calculate the SHA1 of a raw string
128 function rstr_sha1(s)
130 return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8));
134 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
136 function rstr_hmac_sha1(key, data)
138 var bkey = rstr2binb(key);
139 if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8);
141 var ipad = Array(16), opad = Array(16);
142 for(var i = 0; i < 16; i++)
144 ipad[i] = bkey[i] ^ 0x36363636;
145 opad[i] = bkey[i] ^ 0x5C5C5C5C;
148 var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
149 return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160));
153 * Convert a raw string to a hex string
155 function rstr2hex(input)
157 try { hexcase } catch(e) { hexcase=0; }
158 var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
159 var output = "";
160 var x;
161 for(var i = 0; i < input.length; i++)
163 x = input.charCodeAt(i);
164 output += hex_tab.charAt((x >>> 4) & 0x0F)
165 + hex_tab.charAt( x & 0x0F);
167 return output;
171 * Convert a raw string to a base-64 string
173 function rstr2b64(input)
175 try { b64pad } catch(e) { b64pad=''; }
176 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
177 var output = "";
178 var len = input.length;
179 for(var i = 0; i < len; i += 3)
181 var triplet = (input.charCodeAt(i) << 16)
182 | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
183 | (i + 2 < len ? input.charCodeAt(i+2) : 0);
184 for(var j = 0; j < 4; j++)
186 if(i * 8 + j * 6 > input.length * 8) output += b64pad;
187 else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
190 return output;
194 * Convert a raw string to an arbitrary string encoding
196 function rstr2any(input, encoding)
198 var divisor = encoding.length;
199 var remainders = Array();
200 var i, q, x, quotient;
202 /* Convert to an array of 16-bit big-endian values, forming the dividend */
203 var dividend = Array(Math.ceil(input.length / 2));
204 for(i = 0; i < dividend.length; i++)
206 dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
210 * Repeatedly perform a long division. The binary array forms the dividend,
211 * the length of the encoding is the divisor. Once computed, the quotient
212 * forms the dividend for the next step. We stop when the dividend is zero.
213 * All remainders are stored for later use.
215 while(dividend.length > 0)
217 quotient = Array();
218 x = 0;
219 for(i = 0; i < dividend.length; i++)
221 x = (x << 16) + dividend[i];
222 q = Math.floor(x / divisor);
223 x -= q * divisor;
224 if(quotient.length > 0 || q > 0)
225 quotient[quotient.length] = q;
227 remainders[remainders.length] = x;
228 dividend = quotient;
231 /* Convert the remainders to the output string */
232 var output = "";
233 for(i = remainders.length - 1; i >= 0; i--)
234 output += encoding.charAt(remainders[i]);
236 /* Append leading zero equivalents */
237 var full_length = Math.ceil(input.length * 8 /
238 (Math.log(encoding.length) / Math.log(2)))
239 for(i = output.length; i < full_length; i++)
240 output = encoding[0] + output;
242 return output;
246 * Encode a string as utf-8.
247 * For efficiency, this assumes the input is valid utf-16.
249 function str2rstr_utf8(input)
251 var output = "";
252 var i = -1;
253 var x, y;
255 while(++i < input.length)
257 /* Decode utf-16 surrogate pairs */
258 x = input.charCodeAt(i);
259 y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
260 if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
262 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
263 i++;
266 /* Encode output as utf-8 */
267 if(x <= 0x7F)
268 output += String.fromCharCode(x);
269 else if(x <= 0x7FF)
270 output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
271 0x80 | ( x & 0x3F));
272 else if(x <= 0xFFFF)
273 output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
274 0x80 | ((x >>> 6 ) & 0x3F),
275 0x80 | ( x & 0x3F));
276 else if(x <= 0x1FFFFF)
277 output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
278 0x80 | ((x >>> 12) & 0x3F),
279 0x80 | ((x >>> 6 ) & 0x3F),
280 0x80 | ( x & 0x3F));
282 return output;
286 * Encode a string as utf-16
288 function str2rstr_utf16le(input)
290 var output = "";
291 for(var i = 0; i < input.length; i++)
292 output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
293 (input.charCodeAt(i) >>> 8) & 0xFF);
294 return output;
297 function str2rstr_utf16be(input)
299 var output = "";
300 for(var i = 0; i < input.length; i++)
301 output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
302 input.charCodeAt(i) & 0xFF);
303 return output;
307 * Convert a raw string to an array of big-endian words
308 * Characters >255 have their high-byte silently ignored.
310 function rstr2binb(input)
312 var output = Array(input.length >> 2);
313 for(var i = 0; i < output.length; i++)
314 output[i] = 0;
315 for(var i = 0; i < input.length * 8; i += 8)
316 output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
317 return output;
321 * Convert an array of big-endian words to a string
323 function binb2rstr(input)
325 var output = "";
326 for(var i = 0; i < input.length * 32; i += 8)
327 output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
328 return output;
332 * Calculate the SHA-1 of an array of big-endian words, and a bit length
334 function binb_sha1(x, len)
336 /* append padding */
337 x[len >> 5] |= 0x80 << (24 - len % 32);
338 x[((len + 64 >> 9) << 4) + 15] = len;
340 var w = Array(80);
341 var a = 1732584193;
342 var b = -271733879;
343 var c = -1732584194;
344 var d = 271733878;
345 var e = -1009589776;
347 for(var i = 0; i < x.length; i += 16)
349 var olda = a;
350 var oldb = b;
351 var oldc = c;
352 var oldd = d;
353 var olde = e;
355 for(var j = 0; j < 80; j++)
357 if(j < 16) w[j] = x[i + j];
358 else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
359 var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
360 safe_add(safe_add(e, w[j]), sha1_kt(j)));
361 e = d;
362 d = c;
363 c = bit_rol(b, 30);
364 b = a;
365 a = t;
368 a = safe_add(a, olda);
369 b = safe_add(b, oldb);
370 c = safe_add(c, oldc);
371 d = safe_add(d, oldd);
372 e = safe_add(e, olde);
374 return Array(a, b, c, d, e);
379 * Perform the appropriate triplet combination function for the current
380 * iteration
382 function sha1_ft(t, b, c, d)
384 if(t < 20) return (b & c) | ((~b) & d);
385 if(t < 40) return b ^ c ^ d;
386 if(t < 60) return (b & c) | (b & d) | (c & d);
387 return b ^ c ^ d;
391 * Determine the appropriate additive constant for the current iteration
393 function sha1_kt(t)
395 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
396 (t < 60) ? -1894007588 : -899497514;
400 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
401 * to work around bugs in some JS interpreters.
403 function safe_add(x, y)
405 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
406 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
407 return (msw << 16) | (lsw & 0xFFFF);
411 * Bitwise rotate a 32-bit number to the left.
413 function bit_rol(num, cnt)
415 return (num << cnt) | (num >>> (32 - cnt));
417 </script>
419 </head>
420 <body>
421 <h1 align=CENTER>Example of Login procedure</h1>
423 Simple and very unsafe example login page for CGIscriptor.pl. The password is first hashed with the
424 site specific salt (as it is used to store the password on-site). Then it is hashed with a random,
425 one-time salt. Effectively, creating a one-time password. Only the last value is send to the server.
426 The server has both salt values stored. It will ignore anything except the username, hashed password, and
427 LOGINTICKET.
428 </p>
430 The Session Ticket is stored in a cookie with the name <em>CGIscriptor</em>.
431 </p>
433 <form method="POST" action="" id="LoginForm"
434 onSubmit='HashPassword("<SCRIPT TYPE="text/ssperl">
435 $RANDOMSALT</SCRIPT>");createCookie("CGIscriptor","<SCRIPT TYPE="text/ssperl">$SESSIONTICKET</SCRIPT>",1,"Private/");true'>
436 <div style="margin-left: 30%; margin-right: 30%; text-align: left">
437 Username: <input type="text" name="username" size="20" /><br />
438 Password: <input type="password" name="password" id="password" size="20" /><br />
439 <input type="hidden" name="salt" id="salt" value="<SCRIPT TYPE="text/ssperl">$SALT</SCRIPT>" /><br />
440 <input type="hidden" name="LOGINTICKET" value="<SCRIPT TYPE="text/ssperl">$LOGINTICKET</SCRIPT>" /><br />
441 </div>
442 <div align="center">
443 <p><input type="submit" value="Login" /></p>
444 </div>
445 </form>
447 The Salt and Ticket values are all created using SHA1 on 512 Byte of output from <em>/dev/urandom</em> in HEX.
448 </p>
449 <FONT STYLE="font-size:small">
450 <p> Example Login page for CGIscriptor.pl<br />
451 Copyright &copy; 2012 R.J.J.H. van Son<br />
452 This program is free software: you can redistribute it and/or modify
453 it under the terms of the GNU General Public License as published by
454 the Free Software Foundation, either version 3 of the License, or
455 (at your option) any later version.
456 This program is distributed in the hope that it will be useful,
457 but WITHOUT ANY WARRANTY; without even the implied warranty of
458 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
459 GNU General Public License for more details.<br />
460 You should have received a copy of the GNU General Public License
461 along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.</p>
462 <p> JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1<br />
463 Copyright &copy; 2000 - 2009 Paul Johnston, Version 2.2<br />
464 Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet<br />
465 Distributed under the BSD License<br />
466 See <a href="http://pajhome.org.uk/crypt/md5">http://pajhome.org.uk/crypt/md5</a> for details.
467 </FONT>
469 </body>
470 </html>