Create SkeletonKey._capitalizeKey
[skeletonkey.git] / core.js
1 /* Copyright (c) 2012 Robert Sesek <http://robert.sesek.com>
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 * DEALINGS IN THE SOFTWARE.
20 */
21
22 /**
23 * SkeletonKey is view controller for generating secure passwords.
24 *
25 * @param {HTMLDocument} doc The document on which to operate.
26 */
27 var SkeletonKey = SkeletonKey || function(doc) {
28 this._master = doc.getElementById('master');
29 this._sitekey = doc.getElementById('sitekey');
30 this._username = doc.getElementById('username');
31 this._password = doc.getElementById('password');
32 this._generateButton = doc.getElementById('generate');
33 this._init();
34 };
35
36 /**
37 * The number of iterations to perform in PBKDF2.
38 * @const {int}
39 */
40 SkeletonKey.prototype.ITERATIONS = 1000;
41 /**
42 * The size of the key, in bytes.
43 * @const {int}
44 */
45 SkeletonKey.prototype.KEYSIZE = 256/32;
46
47 /**
48 * The minimum length of a password.
49 * @const {int}
50 */
51 SkeletonKey.prototype.MIN_LENGTH = 6;
52
53 /**
54 * Initializes event handlers for the page.
55 * @private
56 */
57 SkeletonKey.prototype._init = function() {
58 this._generateButton.onclick = this._onGenerate.bind(this);
59 };
60
61 /**
62 * Event handler for generating a new password.
63 * @param {Event} e
64 * @private
65 */
66 SkeletonKey.prototype._onGenerate = function(e) {
67 var salt = this._username.value + '@' + this._sitekey.value;
68
69 // |key| is a WordArray of 32-bit words.
70 var key = CryptoJS.PBKDF2(this._master.value, salt,
71 {keySize: this.KEYSIZE, iterations: this.ITERATIONS});
72 var hexString = key.toString();
73 hexString = this._capitalizeKey(hexString);
74 this._password.value = hexString;
75 };
76
77 /**
78 * Takes a HEX string and returns a mixed-case string.
79 * @param {string} key
80 * @return string
81 * @private
82 */
83 SkeletonKey.prototype._capitalizeKey = function(key) {
84 // |key| is too long for a decent password, so try and use the second half of
85 // it as the basis for capitalizing the key.
86 var capsSource = null;
87 var keyLength = key.length;
88 if (keyLength / 2 <= this.MIN_LENGTH) {
89 capsSouce = key.substr(0, keyLength - this.MIN_LENGTH);
90 } else {
91 capsSource = key.substr(keyLength / 2);
92 }
93
94 if (!capsSource || capsSource.length < 1) {
95 return key;
96 }
97
98 key = key.substr(0, capsSource.length);
99 var capsSourceLength = capsSource.length;
100
101 var j = 0;
102 var newKey = "";
103 for (var i = 0; i < key.length; i++) {
104 var c = key.charCodeAt(i);
105 // If this is not a lowercase letter or there's no more source, skip.
106 if (c < 0x61 || c > 0x7A || j >= capsSourceLength) {
107 newKey += key[i];
108 continue;
109 }
110
111 var makeCap = capsSource.charCodeAt(j++) % 2;
112 if (makeCap)
113 newKey += String.fromCharCode(c - 0x20);
114 else
115 newKey += key[i];
116 }
117
118 return newKey;
119 };