Add hashcat command lines
[pwdhash.git] / pwdhash-all.js
1 /*
2 * Remote PwdHash
3 * A JavaScript implementation of the PwdHash hashing algorithm.
4 * Version 1.0 Copyright (C) Stanford University 2004-2006
5 * Author: Collin Jackson
6 * Other Contributors: Dan Boneh, John Mitchell, Nick Miyake, and Blake Ross
7 * Distributed under the BSD License
8 * See http://crypto.stanford.edu/PwdHash for more info.
9 * Requires the Javascript MD5 library, available here: http://pajhome.org.uk/crypt/md5
10 */
11
12 /*
13 * Initialize page with default hashing parameters.
14 */
15 function Init() {
16 document.inputform.term.value = "http://www.example.com/";
17 document.hashpass.sitePassword.value = "";
18 document.hashpass.hashedPassword.value = "Press Generate";
19 document.hashpass.hashedPassword.disabled = true;
20 }
21
22 var SPH_kPasswordPrefix = "@@";
23
24 /*
25 * Returns a conforming hashed password generated from the form's field values.
26 */
27 function Generate()
28 {
29 var uri = document.inputform.term.value;
30 var domain = (new SPH_DomainExtractor()).extractDomain(uri);
31 var size = SPH_kPasswordPrefix.length;
32 var data = document.hashpass.sitePassword.value;
33 if (data.substring(0, size) == SPH_kPasswordPrefix)
34 data = data.substring(size);
35 var result = new String(new SPH_HashedPassword(data, domain));
36 return result;
37 }
38
39 /*
40 * Obtain a conforming hashed password and put it in the hashed password field
41 */
42 function GenerateToTextField()
43 {
44 if ((document.hashpass.sitePassword.value != "") && (document.inputform.term.value != "")) {
45 document.hashpass.hashedPassword.value = Generate();
46 document.hashpass.hashedPassword.disabled = false;
47 }
48 else {
49 if (document.hashpass.hashedPassword.value != "") {
50 document.hashpass.hashedPassword.value = "";
51 }
52 }
53 }
54
55
56
57
58
59 /*
60 Copyright 2005 Collin Jackson
61
62 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
63
64 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
65 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
66 * Neither the name of Stanford University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
67
68 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
69
70 */
71
72 /*
73 * Domain name extractor. Turns host names into domain names
74 * Adapted from Chris Zarate's public domain genpass tool:
75 * http://labs.zarate.org/passwd/
76 */
77
78 function SPH_DomainExtractor() { }
79
80 SPH_DomainExtractor.prototype = {
81
82 extractDomain: function(host) {
83
84 var s; // the final result
85
86 // Begin Chris Zarate's code
87 host=host.replace('http:\/\/','');
88 host=host.replace('https:\/\/','');
89 re=new RegExp("([^/]+)");
90 host=host.match(re)[1];
91 host=host.split('.');
92
93 if(host[2]!=null) {
94 s=host[host.length-2]+'.'+host[host.length-1];
95 domains='ab.ca|ac.ac|ac.at|ac.be|ac.cn|ac.il|ac.in|ac.jp|ac.kr|ac.nz|ac.th|ac.uk|ac.za|adm.br|adv.br|agro.pl|ah.cn|aid.pl|alt.za|am.br|arq.br|art.br|arts.ro|asn.au|asso.fr|asso.mc|atm.pl|auto.pl|bbs.tr|bc.ca|bio.br|biz.pl|bj.cn|br.com|cn.com|cng.br|cnt.br|co.ac|co.at|co.il|co.in|co.jp|co.kr|co.nz|co.th|co.uk|co.za|com.au|com.br|com.cn|com.ec|com.fr|com.hk|com.mm|com.mx|com.pl|com.ro|com.ru|com.sg|com.tr|com.tw|cq.cn|cri.nz|de.com|ecn.br|edu.au|edu.cn|edu.hk|edu.mm|edu.mx|edu.pl|edu.tr|edu.za|eng.br|ernet.in|esp.br|etc.br|eti.br|eu.com|eu.lv|fin.ec|firm.ro|fm.br|fot.br|fst.br|g12.br|gb.com|gb.net|gd.cn|gen.nz|gmina.pl|go.jp|go.kr|go.th|gob.mx|gov.br|gov.cn|gov.ec|gov.il|gov.in|gov.mm|gov.mx|gov.sg|gov.tr|gov.za|govt.nz|gs.cn|gsm.pl|gv.ac|gv.at|gx.cn|gz.cn|hb.cn|he.cn|hi.cn|hk.cn|hl.cn|hn.cn|hu.com|idv.tw|ind.br|inf.br|info.pl|info.ro|iwi.nz|jl.cn|jor.br|jpn.com|js.cn|k12.il|k12.tr|lel.br|ln.cn|ltd.uk|mail.pl|maori.nz|mb.ca|me.uk|med.br|med.ec|media.pl|mi.th|miasta.pl|mil.br|mil.ec|mil.nz|mil.pl|mil.tr|mil.za|mo.cn|muni.il|nb.ca|ne.jp|ne.kr|net.au|net.br|net.cn|net.ec|net.hk|net.il|net.in|net.mm|net.mx|net.nz|net.pl|net.ru|net.sg|net.th|net.tr|net.tw|net.za|nf.ca|ngo.za|nm.cn|nm.kr|no.com|nom.br|nom.pl|nom.ro|nom.za|ns.ca|nt.ca|nt.ro|ntr.br|nx.cn|odo.br|on.ca|or.ac|or.at|or.jp|or.kr|or.th|org.au|org.br|org.cn|org.ec|org.hk|org.il|org.mm|org.mx|org.nz|org.pl|org.ro|org.ru|org.sg|org.tr|org.tw|org.uk|org.za|pc.pl|pe.ca|plc.uk|ppg.br|presse.fr|priv.pl|pro.br|psc.br|psi.br|qc.ca|qc.com|qh.cn|re.kr|realestate.pl|rec.br|rec.ro|rel.pl|res.in|ru.com|sa.com|sc.cn|school.nz|school.za|se.com|se.net|sh.cn|shop.pl|sk.ca|sklep.pl|slg.br|sn.cn|sos.pl|store.ro|targi.pl|tj.cn|tm.fr|tm.mc|tm.pl|tm.ro|tm.za|tmp.br|tourism.pl|travel.pl|tur.br|turystyka.pl|tv.br|tw.cn|uk.co|uk.com|uk.net|us.com|uy.com|vet.br|web.za|web.com|www.ro|xj.cn|xz.cn|yk.ca|yn.cn|za.com';
96 domains=domains.split('|');
97 for(var i=0;i<domains.length;i++) {
98 if(s==domains[i]) {
99 s=host[host.length-3]+'.'+s;
100 break;
101 }
102 }
103 } else {
104 s=host.join('.');
105 }
106 // End Chris Zarate's code
107 return s;
108 }
109 }
110
111
112
113
114
115 /*
116 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
117 * Digest Algorithm, as defined in RFC 1321.
118 * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
119 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
120 * Distributed under the BSD License
121 * See http://pajhome.org.uk/crypt/md5 for more info.
122 */
123
124 /*
125 * Configurable variables. You may need to tweak these to be compatible with
126 * the server-side, but the defaults work in most cases.
127 */
128 var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
129 var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
130 var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
131
132 /*
133 * These are the functions you'll usually want to call
134 * They take string arguments and return either hex or base-64 encoded strings
135 */
136 function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
137 function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
138 function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
139 function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
140 function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
141 function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
142
143 /*
144 * Perform a simple self-test to see if the VM is working
145 */
146 function md5_vm_test()
147 {
148 return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
149 }
150
151 /*
152 * Calculate the MD5 of an array of little-endian words, and a bit length
153 */
154 function core_md5(x, len)
155 {
156 /* append padding */
157 x[len >> 5] |= 0x80 << ((len) % 32);
158 x[(((len + 64) >>> 9) << 4) + 14] = len;
159
160 var a = 1732584193;
161 var b = -271733879;
162 var c = -1732584194;
163 var d = 271733878;
164
165 for(var i = 0; i < x.length; i += 16)
166 {
167 var olda = a;
168 var oldb = b;
169 var oldc = c;
170 var oldd = d;
171
172 a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
173 d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
174 c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
175 b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
176 a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
177 d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
178 c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
179 b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
180 a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
181 d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
182 c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
183 b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
184 a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
185 d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
186 c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
187 b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
188
189 a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
190 d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
191 c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
192 b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
193 a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
194 d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
195 c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
196 b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
197 a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
198 d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
199 c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
200 b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
201 a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
202 d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
203 c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
204 b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
205
206 a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
207 d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
208 c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
209 b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
210 a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
211 d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
212 c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
213 b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
214 a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
215 d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
216 c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
217 b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
218 a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
219 d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
220 c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
221 b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
222
223 a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
224 d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
225 c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
226 b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
227 a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
228 d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
229 c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
230 b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
231 a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
232 d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
233 c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
234 b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
235 a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
236 d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
237 c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
238 b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
239
240 a = safe_add(a, olda);
241 b = safe_add(b, oldb);
242 c = safe_add(c, oldc);
243 d = safe_add(d, oldd);
244 }
245 return Array(a, b, c, d);
246
247 }
248
249 /*
250 * These functions implement the four basic operations the algorithm uses.
251 */
252 function md5_cmn(q, a, b, x, s, t)
253 {
254 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
255 }
256 function md5_ff(a, b, c, d, x, s, t)
257 {
258 return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
259 }
260 function md5_gg(a, b, c, d, x, s, t)
261 {
262 return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
263 }
264 function md5_hh(a, b, c, d, x, s, t)
265 {
266 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
267 }
268 function md5_ii(a, b, c, d, x, s, t)
269 {
270 return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
271 }
272
273 /*
274 * Calculate the HMAC-MD5, of a key and some data
275 */
276 function core_hmac_md5(key, data)
277 {
278 var bkey = str2binl(key);
279 if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
280
281 var ipad = Array(16), opad = Array(16);
282 for(var i = 0; i < 16; i++)
283 {
284 ipad[i] = bkey[i] ^ 0x36363636;
285 opad[i] = bkey[i] ^ 0x5C5C5C5C;
286 }
287
288 var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
289 return core_md5(opad.concat(hash), 512 + 128);
290 }
291
292 /*
293 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
294 * to work around bugs in some JS interpreters.
295 */
296 function safe_add(x, y)
297 {
298 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
299 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
300 return (msw << 16) | (lsw & 0xFFFF);
301 }
302
303 /*
304 * Bitwise rotate a 32-bit number to the left.
305 */
306 function bit_rol(num, cnt)
307 {
308 return (num << cnt) | (num >>> (32 - cnt));
309 }
310
311 /*
312 * Convert a string to an array of little-endian words
313 * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
314 */
315 function str2binl(str)
316 {
317 var bin = Array();
318 var mask = (1 << chrsz) - 1;
319 for(var i = 0; i < str.length * chrsz; i += chrsz)
320 bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
321 return bin;
322 }
323
324 /*
325 * Convert an array of little-endian words to a string
326 */
327 function binl2str(bin)
328 {
329 var str = "";
330 var mask = (1 << chrsz) - 1;
331 for(var i = 0; i < bin.length * 32; i += chrsz)
332 str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
333 return str;
334 }
335
336 /*
337 * Convert an array of little-endian words to a hex string.
338 */
339 function binl2hex(binarray)
340 {
341 var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
342 var str = "";
343 for(var i = 0; i < binarray.length * 4; i++)
344 {
345 str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
346 hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
347 }
348 return str;
349 }
350
351 /*
352 * Convert an array of little-endian words to a base-64 string
353 */
354 function binl2b64(binarray)
355 {
356 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
357 var str = "";
358 for(var i = 0; i < binarray.length * 4; i += 3)
359 {
360 var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)
361 | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
362 | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
363 for(var j = 0; j < 4; j++)
364 {
365 if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
366 else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
367 }
368 }
369 return str;
370 }
371
372
373
374
375
376
377 /*
378 Copyright 2005 Collin Jackson
379
380 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
381
382 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
383 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
384 * Neither the name of Stanford University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
385
386 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
387
388 */
389
390 /**
391 * Hashed Password
392 * Combination of page URI and plaintext password.
393 * Treated as a string, it is the hashed password.
394 */
395
396 function SPH_HashedPassword(password, realm) {
397 var hashedPassword = this._getHashedPassword(password, realm);
398 this.toString = function() { return hashedPassword; }
399 }
400
401 var SPH_kPasswordPrefix = "@@";
402
403 SPH_HashedPassword.prototype = {
404
405 /**
406 * Determine the hashed password from the salt and the master password
407 */
408 _getHashedPassword: function(password, realm) {
409 var hash = b64_hmac_md5(password, realm);
410 var size = password.length + SPH_kPasswordPrefix.length;
411 var nonalphanumeric = password.match(/\W/) != null;
412 var result = this._applyConstraints(hash, size, nonalphanumeric);
413 return result;
414 },
415
416 /**
417 * Fiddle with the password a bit after hashing it so that it will get through
418 * most website filters. We require one upper and lower case, one digit, and
419 * we look at the user's password to determine if there should be at least one
420 * alphanumeric or not.
421 */
422 _applyConstraints: function(hash, size, nonalphanumeric) {
423 var startingSize = size - 4; // Leave room for some extra characters
424 var result = hash.substring(0, startingSize);
425 var extras = hash.substring(startingSize).split('');
426
427 // Some utility functions to keep things tidy
428 function nextExtra() { return extras.length ? extras.shift().charCodeAt(0) : 0; }
429 function nextExtraChar() { return String.fromCharCode(nextExtra()); }
430 function rotate(arr, amount) { while(amount--) arr.push(arr.shift()) }
431 function between(min, interval, offset) { return min + offset % interval; }
432 function nextBetween(base, interval) {
433 return String.fromCharCode(between(base.charCodeAt(0), interval, nextExtra()));
434 }
435 function contains(regex) { return result.match(regex); }
436
437 // Add the extra characters
438 result += (contains(/[A-Z]/) ? nextExtraChar() : nextBetween('A', 26));
439 result += (contains(/[a-z]/) ? nextExtraChar() : nextBetween('a', 26));
440 result += (contains(/[0-9]/) ? nextExtraChar() : nextBetween('0', 10));
441 result += (contains(/\W/) && nonalphanumeric ? nextExtraChar() : '+');
442 while (contains(/\W/) && !nonalphanumeric) {
443 result = result.replace(/\W/, nextBetween('A', 26));
444 }
445
446 // Rotate the result to make it harder to guess the inserted locations
447 result = result.split('');
448 rotate(result, nextExtra());
449 return result.join('');
450 }
451 }
452
453
454