4 <META CONTENT=
"text/ssperl; CGI='$SERVERSALT $LOGINTICKET $RANDOMSALT $REMOTE_ADDR'">
5 <SCRIPT type=
"text/javascript" LANGUAGE=
"JavaScript">
6 function createCookie(name,value,days,path) {
10 date.setTime(date.getTime()+(days*
24*
60*
60*
1000));
11 expires = ';expires='+date.toGMTString();
13 document.cookie = name+'='+value+expires+';path=/'+path+'';
16 function readCookie(name) {
17 var nameEQ = name +
"=";
18 var ca = document.cookie.split(';');
19 for(var i=
0;i < ca.length;i++) {
21 while (c.charAt(
0)==' ') c = c.substring(
1,c.length);
22 if (c.indexOf(nameEQ) ==
0) return c.substring(nameEQ.length,c.length);
27 function eraseCookie(name) {
28 createCookie(name,
"",-
1);
31 <script type=
"text/javascript">
32 // Combine the PASSWORD with the site SALT and hash it
33 // Combine this Hash with the extra SALT, and hash them
34 function HashPassword(extsalt
) {
35 var hash
= HashSessionSeed(extsalt
);
36 var password
= document
.getElementById('PASSWORD');
38 password
.value
= hash
;
40 alert("NO PASSWORD IN FORM");
46 // REMEMBER: Set the session cookie BEFORE you hash the password!!!
47 function SetSessionCookie() {
48 var seed
= '<SCRIPT TYPE="text/ssperl">$LOGINTICKET</SCRIPT>';
49 var hash
= HashSessionSeed(seed
);
50 // Dom.storage.enabled must be set!
51 if (!sessionStorage
|| typeof(sessionStorage
) == 'undefined' ) {
52 alert('Your browser does not support HTML5 sessionStorage. Set Dom.storage.enabled or try upgrading.');
54 else sessionStorage
.setItem("CGIscriptorPRIVATE", hash
);
58 function HashSessionSeed(sessionseed
) {
61 var passwordvalue
= document
.getElementById('PASSWORD');
62 var saltvalue
= document
.getElementById('SALT');
63 var username
= document
.getElementById('USERNAME');
64 hash1
= hex_sha1(saltvalue
.value
+passwordvalue
.value
+username
.value
);
66 hash2
= hex_sha1(sessionseed
+hash1
);
72 function remove_cgiparam(elem
, attr
, parameter
) {
73 var elems
= document
.getElementsByTagName(elem
);
74 for (var i
= 0; i
< elems
.length
; i
++)
76 var n
=elems
[i
][attr
].indexOf("&"+parameter
);
78 elems
[i
][attr
] = elems
[i
][attr
].replace("&"+param
, "");
79 var n
=elems
[i
][attr
].indexOf(parameter
);
81 elems
[i
][attr
] = elems
[i
][attr
].replace(param
, "");
85 function check_username_password ( ) {
86 var username
= document
.getElementById('USERNAME');
87 var password
= document
.getElementById('PASSWORD');
88 if(username
.value
.match(/[a-zA-Z0-9]/) && password
.value
.match(/[a-zA-Z0-9]/))
90 alert("Please enter a user name and password");
94 // Remove EVERYTHING from Login window
95 window
.onload = function() {
96 if(window
.location
.search
)window
.location
.search
= "";
97 var warning
= document
.getElementById('WARNING');
98 if(sessionStorage
== null) warning
.innerHTML
= "Storage not supported by the browser: Upgrade your browser or set dom.storage.enabled";
100 warning
.style
.color
= "Black";
101 warning
.innerHTML
= "";
106 <script type
="text/javascript">
108 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
110 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
111 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
112 * Distributed under the BSD License
113 * See http://pajhome.org.uk/crypt/md5 for details.
117 * Configurable variables. You may need to tweak these to be compatible with
118 * the server-side, but the defaults work in most cases.
120 var hexcase
= 0; /* hex output format. 0 - lowercase; 1 - uppercase */
121 var b64pad
= ""; /* base-64 pad character. "=" for strict RFC compliance */
124 * These are the functions you'll usually want to call
125 * They take string arguments and return either hex or base-64 encoded strings
127 function hex_sha1(s
) { return rstr2hex(rstr_sha1(str2rstr_utf8(s
))); }
128 function b64_sha1(s
) { return rstr2b64(rstr_sha1(str2rstr_utf8(s
))); }
129 function any_sha1(s
, e
) { return rstr2any(rstr_sha1(str2rstr_utf8(s
)), e
); }
130 function hex_hmac_sha1(k
, d
)
131 { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k
), str2rstr_utf8(d
))); }
132 function b64_hmac_sha1(k
, d
)
133 { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k
), str2rstr_utf8(d
))); }
134 function any_hmac_sha1(k
, d
, e
)
135 { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k
), str2rstr_utf8(d
)), e
); }
138 * Perform a simple self-test to see if the VM is working
140 function sha1_vm_test()
142 return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d";
146 * Calculate the SHA1 of a raw string
148 function rstr_sha1(s
)
150 return binb2rstr(binb_sha1(rstr2binb(s
), s
.length
* 8));
154 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
156 function rstr_hmac_sha1(key
, data
)
158 var bkey
= rstr2binb(key
);
159 if(bkey
.length
> 16) bkey
= binb_sha1(bkey
, key
.length
* 8);
161 var ipad
= Array(16), opad
= Array(16);
162 for(var i
= 0; i
< 16; i
++)
164 ipad
[i
] = bkey
[i
] ^ 0x36363636;
165 opad
[i
] = bkey
[i
] ^ 0x5C5C5C5C;
168 var hash
= binb_sha1(ipad
.concat(rstr2binb(data
)), 512 + data
.length
* 8);
169 return binb2rstr(binb_sha1(opad
.concat(hash
), 512 + 160));
173 * Convert a raw string to a hex string
175 function rstr2hex(input
)
177 try { hexcase
} catch(e
) { hexcase
=0; }
178 var hex_tab
= hexcase
? "0123456789ABCDEF" : "0123456789abcdef";
181 for(var i
= 0; i
< input
.length
; i
++)
183 x
= input
.charCodeAt(i
);
184 output
+= hex_tab
.charAt((x
>>> 4) & 0x0F)
185 + hex_tab
.charAt( x
& 0x0F);
191 * Convert a raw string to a base-64 string
193 function rstr2b64(input
)
195 try { b64pad
} catch(e
) { b64pad
=''; }
196 var tab
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
198 var len
= input
.length
;
199 for(var i
= 0; i
< len
; i
+= 3)
201 var triplet
= (input
.charCodeAt(i
) << 16)
202 | (i
+ 1 < len
? input
.charCodeAt(i
+1) << 8 : 0)
203 | (i
+ 2 < len
? input
.charCodeAt(i
+2) : 0);
204 for(var j
= 0; j
< 4; j
++)
206 if(i
* 8 + j
* 6 > input
.length
* 8) output
+= b64pad
;
207 else output
+= tab
.charAt((triplet
>>> 6*(3-j
)) & 0x3F);
214 * Convert a raw string to an arbitrary string encoding
216 function rstr2any(input
, encoding
)
218 var divisor
= encoding
.length
;
219 var remainders
= Array();
220 var i
, q
, x
, quotient
;
222 /* Convert to an array of 16-bit big-endian values, forming the dividend */
223 var dividend
= Array(Math
.ceil(input
.length
/ 2));
224 for(i
= 0; i
< dividend
.length
; i
++)
226 dividend
[i
] = (input
.charCodeAt(i
* 2) << 8) | input
.charCodeAt(i
* 2 + 1);
230 * Repeatedly perform a long division. The binary array forms the dividend,
231 * the length of the encoding is the divisor. Once computed, the quotient
232 * forms the dividend for the next step. We stop when the dividend is zero.
233 * All remainders are stored for later use.
235 while(dividend
.length
> 0)
239 for(i
= 0; i
< dividend
.length
; i
++)
241 x
= (x
<< 16) + dividend
[i
];
242 q
= Math
.floor(x
/ divisor
);
244 if(quotient
.length
> 0 || q
> 0)
245 quotient
[quotient
.length
] = q
;
247 remainders
[remainders
.length
] = x
;
251 /* Convert the remainders to the output string */
253 for(i
= remainders
.length
- 1; i
>= 0; i
--)
254 output
+= encoding
.charAt(remainders
[i
]);
256 /* Append leading zero equivalents */
257 var full_length
= Math
.ceil(input
.length
* 8 /
258 (Math
.log(encoding
.length
) / Math
.log(2)))
259 for(i
= output
.length
; i
< full_length
; i
++)
260 output
= encoding
[0] + output
;
266 * Encode a string as utf-8.
267 * For efficiency, this assumes the input is valid utf-16.
269 function str2rstr_utf8(input
)
275 while(++i
< input
.length
)
277 /* Decode utf-16 surrogate pairs */
278 x
= input
.charCodeAt(i
);
279 y
= i
+ 1 < input
.length
? input
.charCodeAt(i
+ 1) : 0;
280 if(0xD800 <= x
&& x
<= 0xDBFF && 0xDC00 <= y
&& y
<= 0xDFFF)
282 x
= 0x10000 + ((x
& 0x03FF) << 10) + (y
& 0x03FF);
286 /* Encode output as utf-8 */
288 output
+= String
.fromCharCode(x
);
290 output
+= String
.fromCharCode(0xC0 | ((x
>>> 6 ) & 0x1F),
293 output
+= String
.fromCharCode(0xE0 | ((x
>>> 12) & 0x0F),
294 0x80 | ((x
>>> 6 ) & 0x3F),
296 else if(x
<= 0x1FFFFF)
297 output
+= String
.fromCharCode(0xF0 | ((x
>>> 18) & 0x07),
298 0x80 | ((x
>>> 12) & 0x3F),
299 0x80 | ((x
>>> 6 ) & 0x3F),
306 * Encode a string as utf-16
308 function str2rstr_utf16le(input
)
311 for(var i
= 0; i
< input
.length
; i
++)
312 output
+= String
.fromCharCode( input
.charCodeAt(i
) & 0xFF,
313 (input
.charCodeAt(i
) >>> 8) & 0xFF);
317 function str2rstr_utf16be(input
)
320 for(var i
= 0; i
< input
.length
; i
++)
321 output
+= String
.fromCharCode((input
.charCodeAt(i
) >>> 8) & 0xFF,
322 input
.charCodeAt(i
) & 0xFF);
327 * Convert a raw string to an array of big-endian words
328 * Characters >255 have their high-byte silently ignored.
330 function rstr2binb(input
)
332 var output
= Array(input
.length
>> 2);
333 for(var i
= 0; i
< output
.length
; i
++)
335 for(var i
= 0; i
< input
.length
* 8; i
+= 8)
336 output
[i
>>5] |= (input
.charCodeAt(i
/ 8) & 0xFF) << (24 - i
% 32);
341 * Convert an array of big-endian words to a string
343 function binb2rstr(input
)
346 for(var i
= 0; i
< input
.length
* 32; i
+= 8)
347 output
+= String
.fromCharCode((input
[i
>>5] >>> (24 - i
% 32)) & 0xFF);
352 * Calculate the SHA-1 of an array of big-endian words, and a bit length
354 function binb_sha1(x
, len
)
357 x
[len
>> 5] |= 0x80 << (24 - len
% 32);
358 x
[((len
+ 64 >> 9) << 4) + 15] = len
;
367 for(var i
= 0; i
< x
.length
; i
+= 16)
375 for(var j
= 0; j
< 80; j
++)
377 if(j
< 16) w
[j
] = x
[i
+ j
];
378 else w
[j
] = bit_rol(w
[j
-3] ^ w
[j
-8] ^ w
[j
-14] ^ w
[j
-16], 1);
379 var t
= safe_add(safe_add(bit_rol(a
, 5), sha1_ft(j
, b
, c
, d
)),
380 safe_add(safe_add(e
, w
[j
]), sha1_kt(j
)));
388 a
= safe_add(a
, olda
);
389 b
= safe_add(b
, oldb
);
390 c
= safe_add(c
, oldc
);
391 d
= safe_add(d
, oldd
);
392 e
= safe_add(e
, olde
);
394 return Array(a
, b
, c
, d
, e
);
399 * Perform the appropriate triplet combination function for the current
402 function sha1_ft(t
, b
, c
, d
)
404 if(t
< 20) return (b
& c
) | ((~b
) & d
);
405 if(t
< 40) return b
^ c
^ d
;
406 if(t
< 60) return (b
& c
) | (b
& d
) | (c
& d
);
411 * Determine the appropriate additive constant for the current iteration
415 return (t
< 20) ? 1518500249 : (t
< 40) ? 1859775393 :
416 (t
< 60) ? -1894007588 : -899497514;
420 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
421 * to work around bugs in some JS interpreters.
423 function safe_add(x
, y
)
425 var lsw
= (x
& 0xFFFF) + (y
& 0xFFFF);
426 var msw
= (x
>> 16) + (y
>> 16) + (lsw
>> 16);
427 return (msw
<< 16) | (lsw
& 0xFFFF);
431 * Bitwise rotate a 32-bit number to the left.
433 function bit_rol(num
, cnt
)
435 return (num
<< cnt
) | (num
>>> (32 - cnt
));
441 <h1 align=CENTER
>Example of Login procedure
</h1>
442 <h2 align=CENTER
><div id=
"WARNING" style=
"color: Red">You need to have JavaScript and cookies enabled to use the login system
</div></h2>
444 Simple and very unsafe example login page for CGIscriptor.pl. The password is first hashed with the
445 site specific salt (as it is used to store the password on-site). Then it is hashed with a random,
446 one-time salt. Effectively, creating a one-time password. Only the last value is send to the server.
447 The server has both salt values stored. It will ignore anything except the username, hashed password, and
451 The Session Ticket information is stored in in
<a href=
"http://www.xul.fr/en/html5/sessionstorage.php">
452 <em>sessionStorage
</em></a> with key
<em>CGIscriptorPRIVATE
</em>. Older browsers might not implement
453 <a href=
"http://www.xul.fr/en/html5/sessionstorage.php"><em>sessionStorage
</em></a>, or it might be
454 turned off in the
<a href=
"http://kb.mozillazine.org/Dom.storage.enabled"><em>dom.storage.enabled
</em></a>
457 <form method=
"POST" action=
"" id=
"LoginForm"
458 onSubmit='var success=check_username_password();SetSessionCookie();HashPassword(
"<SCRIPT TYPE="text/ssperl
">
459 $RANDOMSALT</SCRIPT>");success'
>
460 <div style=
"margin-left: 30%; margin-right: 30%; text-align: left">
461 Username:
<input type=
"text" name=
"USERNAME" id=
"USERNAME" size=
"20" /><br />
462 Password:
<input type=
"PASSWORD" name=
"PASSWORD" id=
"PASSWORD" size=
"20" /><br />
463 <input type=
"hidden" name=
"SALT" id=
"SALT" value=
"<SCRIPT TYPE="text/ssperl
">$SERVERSALT</SCRIPT>" /><br />
464 <input type=
"hidden" name=
"LOGINTICKET" value=
"<SCRIPT TYPE="text/ssperl
">$LOGINTICKET</SCRIPT>" /><br />
467 <p><input type=
"submit" value=
"Login" /></p>
471 There are three default test accounts, all three have password
<em>test
</em>:
473 <li>test: A SESSION ticket account
</li>
474 <li>testip: An IPADDRESS ticket account
</li>
475 <li>testchallenge: A CHALLENGE ticket account
</li>
480 The Salt and Ticket values are all created using SHA1 on
32 Byte of output from
<em>/dev/urandom
</em> in HEX.
482 <FONT STYLE=
"font-size:small">
483 <p> Example Login page for CGIscriptor.pl
<br />
484 Copyright
© 2012 R.J.J.H. van Son
<br />
485 This program is free software: you can redistribute it and/or modify
486 it under the terms of the GNU General Public License as published by
487 the Free Software Foundation, either version
3 of the License, or
488 (at your option) any later version.
489 This program is distributed in the hope that it will be useful,
490 but WITHOUT ANY WARRANTY; without even the implied warranty of
491 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
492 GNU General Public License for more details.
<br />
493 You should have received a copy of the GNU General Public License
494 along with this program. If not, see
<a href=
"http://www.gnu.org/licenses/">http://www.gnu.org/licenses/
</a>.
</p>
495 <p> JavaScript implementation of the Secure Hash Algorithm, SHA-
1, as defined in FIPS
180-
1<br />
496 Copyright
© 2000 -
2009 Paul Johnston, Version
2.2<br />
497 Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
<br />
498 Distributed under the BSD License
<br />
499 See
<a href=
"http://pajhome.org.uk/crypt/md5">http://pajhome.org.uk/crypt/md5
</a> for details.