Added hash functionality to Login.html
[CGIscriptor.git] / Private / Login.html
blob88a45cfa6357fb371771bbf1c6ef27dc0cda7305
1 <html>
2 <head>
3 <title>Login</title>
4 <SCRIPT TYPE="text/ssperl" CGI="$REMOTE_ADDR">
5 # Create Session Ticket
6 open(URANDOM, 'dd count=1 if=/dev/urandom 2>/dev/null |shasum-5.12 -b|cut -f 1 -d" "|') ||
7 die "URANDOM; $!\n";
8 $SESSIONTICKET = <URANDOM>;
9 close(URANDOM);
10 chomp($SESSIONTICKET);
12 # Create Login Ticket
13 open(URANDOM, 'dd count=1 if=/dev/urandom 2>/dev/null |shasum-5.12 -b|cut -f 1 -d" "|') ||
14 die "URANDOM; $!\n";
15 $LOGINTICKET= <URANDOM>;
16 close(URANDOM);
17 chomp($LOGINTICKET);
19 # Create Random Hash Salt
20 open(URANDOM, 'dd count=1 if=/dev/urandom 2>/dev/null |shasum-5.12 -b|cut -f 1 -d" "|') ||
21 die "URANDOM; $!\n";
22 $RANDOMSALT= <URANDOM>;
23 close(URANDOM);
24 chomp($RANDOMSALT);
26 # Read in site Salt
27 open(SALTFILE, "<~/Private/.Passwords/SALT") || die "~/Private/.Passwords/SALT: $!\n";
28 $SALT=<SALTFILE>;
29 close(SALTFILE);
30 chomp($SALT);
32 # Create login session ticket
33 open(LOGINTICKET, ">~/Private/.Sessions/$LOGINTICKET") || die "~/Private/.Sessions/$LOGINTICKET: $!\n";
34 print LOGINTICKET << "ENDOFLOGINTICKET";
35 Type: LOGIN
36 IPaddress: $REMOTE_ADDR
37 Salt: $SALT
38 Session: $SESSIONTICKET
39 Randomsalt: $RANDOMSALT
40 Expires: 600s
41 ENDOFLOGINTICKET
42 close(LOGINTICKET);
43 "";
44 </SCRIPT>
45 <SCRIPT LANGUAGE="JavaScript">
46 function createCookie(name,value,days,path) {
47 if (days) {
48 var date = new Date();
49 date.setTime(date.getTime()+(days*24*60*60*1000));
50 var expires = "; expires="+date.toGMTString();
52 else var expires = "";
53 document.cookie = name+"="+value+expires+"; path=/"+path;
56 function readCookie(name) {
57 var nameEQ = name + "=";
58 var ca = document.cookie.split(';');
59 for(var i=0;i < ca.length;i++) {
60 var c = ca[i];
61 while (c.charAt(0)==' ') c = c.substring(1,c.length);
62 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
64 return null;
67 function eraseCookie(name) {
68 createCookie(name,"",-1);
70 </SCRIPT>
71 <script type="text/javascript">
72 // Combine the password with the site SALT and hash it
73 // Combine this Hash iwth the extra SALT, and hash them
74 function HashPassword(extsalt) {
75 var hash1 = "";
76 var hash2 = "";
77 var passwordvalue = document.getElementById('password');
78 var saltvalue = document.getElementById('salt');
79 hash1 = hex_sha1(saltvalue.value+passwordvalue.value);
80 if(extsalt != "")
81 hash2 = hex_sha1(extsalt+hash1);
82 else
83 hash2 = hash1;
84 passwordvalue.value = hash2;
85 return hash2;
87 </SCRIPT>
89 <script type="text/javascript">
91 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
92 * in FIPS 180-1
93 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
94 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
95 * Distributed under the BSD License
96 * See http://pajhome.org.uk/crypt/md5 for details.
100 * Configurable variables. You may need to tweak these to be compatible with
101 * the server-side, but the defaults work in most cases.
103 var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
104 var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
107 * These are the functions you'll usually want to call
108 * They take string arguments and return either hex or base-64 encoded strings
110 function hex_sha1(s) { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); }
111 function b64_sha1(s) { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); }
112 function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); }
113 function hex_hmac_sha1(k, d)
114 { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
115 function b64_hmac_sha1(k, d)
116 { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
117 function any_hmac_sha1(k, d, e)
118 { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
121 * Perform a simple self-test to see if the VM is working
123 function sha1_vm_test()
125 return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d";
129 * Calculate the SHA1 of a raw string
131 function rstr_sha1(s)
133 return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8));
137 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
139 function rstr_hmac_sha1(key, data)
141 var bkey = rstr2binb(key);
142 if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8);
144 var ipad = Array(16), opad = Array(16);
145 for(var i = 0; i < 16; i++)
147 ipad[i] = bkey[i] ^ 0x36363636;
148 opad[i] = bkey[i] ^ 0x5C5C5C5C;
151 var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
152 return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160));
156 * Convert a raw string to a hex string
158 function rstr2hex(input)
160 try { hexcase } catch(e) { hexcase=0; }
161 var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
162 var output = "";
163 var x;
164 for(var i = 0; i < input.length; i++)
166 x = input.charCodeAt(i);
167 output += hex_tab.charAt((x >>> 4) & 0x0F)
168 + hex_tab.charAt( x & 0x0F);
170 return output;
174 * Convert a raw string to a base-64 string
176 function rstr2b64(input)
178 try { b64pad } catch(e) { b64pad=''; }
179 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
180 var output = "";
181 var len = input.length;
182 for(var i = 0; i < len; i += 3)
184 var triplet = (input.charCodeAt(i) << 16)
185 | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
186 | (i + 2 < len ? input.charCodeAt(i+2) : 0);
187 for(var j = 0; j < 4; j++)
189 if(i * 8 + j * 6 > input.length * 8) output += b64pad;
190 else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
193 return output;
197 * Convert a raw string to an arbitrary string encoding
199 function rstr2any(input, encoding)
201 var divisor = encoding.length;
202 var remainders = Array();
203 var i, q, x, quotient;
205 /* Convert to an array of 16-bit big-endian values, forming the dividend */
206 var dividend = Array(Math.ceil(input.length / 2));
207 for(i = 0; i < dividend.length; i++)
209 dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
213 * Repeatedly perform a long division. The binary array forms the dividend,
214 * the length of the encoding is the divisor. Once computed, the quotient
215 * forms the dividend for the next step. We stop when the dividend is zero.
216 * All remainders are stored for later use.
218 while(dividend.length > 0)
220 quotient = Array();
221 x = 0;
222 for(i = 0; i < dividend.length; i++)
224 x = (x << 16) + dividend[i];
225 q = Math.floor(x / divisor);
226 x -= q * divisor;
227 if(quotient.length > 0 || q > 0)
228 quotient[quotient.length] = q;
230 remainders[remainders.length] = x;
231 dividend = quotient;
234 /* Convert the remainders to the output string */
235 var output = "";
236 for(i = remainders.length - 1; i >= 0; i--)
237 output += encoding.charAt(remainders[i]);
239 /* Append leading zero equivalents */
240 var full_length = Math.ceil(input.length * 8 /
241 (Math.log(encoding.length) / Math.log(2)))
242 for(i = output.length; i < full_length; i++)
243 output = encoding[0] + output;
245 return output;
249 * Encode a string as utf-8.
250 * For efficiency, this assumes the input is valid utf-16.
252 function str2rstr_utf8(input)
254 var output = "";
255 var i = -1;
256 var x, y;
258 while(++i < input.length)
260 /* Decode utf-16 surrogate pairs */
261 x = input.charCodeAt(i);
262 y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
263 if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
265 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
266 i++;
269 /* Encode output as utf-8 */
270 if(x <= 0x7F)
271 output += String.fromCharCode(x);
272 else if(x <= 0x7FF)
273 output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
274 0x80 | ( x & 0x3F));
275 else if(x <= 0xFFFF)
276 output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
277 0x80 | ((x >>> 6 ) & 0x3F),
278 0x80 | ( x & 0x3F));
279 else if(x <= 0x1FFFFF)
280 output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
281 0x80 | ((x >>> 12) & 0x3F),
282 0x80 | ((x >>> 6 ) & 0x3F),
283 0x80 | ( x & 0x3F));
285 return output;
289 * Encode a string as utf-16
291 function str2rstr_utf16le(input)
293 var output = "";
294 for(var i = 0; i < input.length; i++)
295 output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
296 (input.charCodeAt(i) >>> 8) & 0xFF);
297 return output;
300 function str2rstr_utf16be(input)
302 var output = "";
303 for(var i = 0; i < input.length; i++)
304 output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
305 input.charCodeAt(i) & 0xFF);
306 return output;
310 * Convert a raw string to an array of big-endian words
311 * Characters >255 have their high-byte silently ignored.
313 function rstr2binb(input)
315 var output = Array(input.length >> 2);
316 for(var i = 0; i < output.length; i++)
317 output[i] = 0;
318 for(var i = 0; i < input.length * 8; i += 8)
319 output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
320 return output;
324 * Convert an array of big-endian words to a string
326 function binb2rstr(input)
328 var output = "";
329 for(var i = 0; i < input.length * 32; i += 8)
330 output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
331 return output;
335 * Calculate the SHA-1 of an array of big-endian words, and a bit length
337 function binb_sha1(x, len)
339 /* append padding */
340 x[len >> 5] |= 0x80 << (24 - len % 32);
341 x[((len + 64 >> 9) << 4) + 15] = len;
343 var w = Array(80);
344 var a = 1732584193;
345 var b = -271733879;
346 var c = -1732584194;
347 var d = 271733878;
348 var e = -1009589776;
350 for(var i = 0; i < x.length; i += 16)
352 var olda = a;
353 var oldb = b;
354 var oldc = c;
355 var oldd = d;
356 var olde = e;
358 for(var j = 0; j < 80; j++)
360 if(j < 16) w[j] = x[i + j];
361 else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
362 var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
363 safe_add(safe_add(e, w[j]), sha1_kt(j)));
364 e = d;
365 d = c;
366 c = bit_rol(b, 30);
367 b = a;
368 a = t;
371 a = safe_add(a, olda);
372 b = safe_add(b, oldb);
373 c = safe_add(c, oldc);
374 d = safe_add(d, oldd);
375 e = safe_add(e, olde);
377 return Array(a, b, c, d, e);
382 * Perform the appropriate triplet combination function for the current
383 * iteration
385 function sha1_ft(t, b, c, d)
387 if(t < 20) return (b & c) | ((~b) & d);
388 if(t < 40) return b ^ c ^ d;
389 if(t < 60) return (b & c) | (b & d) | (c & d);
390 return b ^ c ^ d;
394 * Determine the appropriate additive constant for the current iteration
396 function sha1_kt(t)
398 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
399 (t < 60) ? -1894007588 : -899497514;
403 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
404 * to work around bugs in some JS interpreters.
406 function safe_add(x, y)
408 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
409 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
410 return (msw << 16) | (lsw & 0xFFFF);
414 * Bitwise rotate a 32-bit number to the left.
416 function bit_rol(num, cnt)
418 return (num << cnt) | (num >>> (32 - cnt));
420 </script>
422 </head>
423 <body>
424 <h1 align=CENTER>Example of Login procedure</h1>
426 Simple and very unsafe example login page for CGIscriptor.pl. The password is first hashed with the
427 site specific salt (as it is used to store the password on-site). Then it is hashed with a random,
428 one-time salt. Effectively, creating a one-time password. Only the last value is send to the server.
429 The server has both salt values stored. It will ignore anything except the username, hashed password, and
430 LOGINTICKET.
431 </p>
433 The Session Ticket is stored in a cookie with the name <em>CGIscriptor</em>.
434 </p>
436 <form method="POST" action="" id="LoginForm"
437 onSubmit='HashPassword("<SCRIPT TYPE="text/ssperl">
438 $RANDOMSALT</SCRIPT>");createCookie("CGIscriptor","<SCRIPT TYPE="text/ssperl">$SESSIONTICKET</SCRIPT>",1,"Private/");true'>
439 <div style="margin-left: 30%; margin-right: 30%; text-align: left">
440 Username: <input type="text" name="username" size="20" /><br />
441 Password: <input type="password" name="password" id="password" size="20" /><br />
442 <input type="hidden" name="salt" id="salt" value="<SCRIPT TYPE="text/ssperl">$SALT</SCRIPT>" /><br />
443 <input type="hidden" name="LOGINTICKET" value="<SCRIPT TYPE="text/ssperl">$LOGINTICKET</SCRIPT>" /><br />
444 </div>
445 <div align="center">
446 <p><input type="submit" value="Login" /></p>
447 </div>
448 </form>
450 The Salt and Ticket values are all created using SHA1 on 512 Byte of output from <em>/dev/urandom</em> in HEX.
451 </p>
452 <FONT STYLE="font-size:small">
453 <p> Example Login page for CGIscriptor.pl<br />
454 Copyright &copy; 2012 R.J.J.H. van Son<br />
455 This program is free software: you can redistribute it and/or modify
456 it under the terms of the GNU General Public License as published by
457 the Free Software Foundation, either version 3 of the License, or
458 (at your option) any later version.
459 This program is distributed in the hope that it will be useful,
460 but WITHOUT ANY WARRANTY; without even the implied warranty of
461 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
462 GNU General Public License for more details.<br />
463 You should have received a copy of the GNU General Public License
464 along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.</p>
465 <p> JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1<br />
466 Copyright &copy; 2000 - 2009 Paul Johnston, Version 2.2<br />
467 Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet<br />
468 Distributed under the BSD License<br />
469 See <a href="http://pajhome.org.uk/crypt/md5">http://pajhome.org.uk/crypt/md5</a> for details.
470 </FONT>
472 </body>
473 </html>