Select the password when clicking into that field
[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 (function main() {
23 if (typeof chrome !== 'undefined') {
24 // TODO: load the extension JS
25 } else {
26 // TODO: load the hosted JS
27 }
28
29 document.addEventListener('DOMContentLoaded', function() {
30 var controller = new SkeletonKey(document);
31 });
32 })();
33
34 /**
35 * SkeletonKey is view controller for generating secure passwords.
36 *
37 * @param {HTMLDocument} doc The document on which to operate.
38 */
39 var SkeletonKey = SkeletonKey || function(doc) {
40 this._master = doc.getElementById('master');
41 this._sitekey = doc.getElementById('sitekey');
42 this._username = doc.getElementById('username');
43 this._password = doc.getElementById('password');
44 this._generateButton = doc.getElementById('generate');
45 this._init();
46 };
47
48 /**
49 * The number of iterations to perform in PBKDF2.
50 * @const {int}
51 */
52 SkeletonKey.prototype.ITERATIONS = 1000;
53 /**
54 * The size of the key, in bytes.
55 * @const {int}
56 */
57 SkeletonKey.prototype.KEYSIZE = 256/32;
58
59 /**
60 * The minimum length of a password.
61 * @const {int}
62 */
63 SkeletonKey.prototype.MIN_LENGTH = 6;
64
65 /**
66 * Initializes event handlers for the page.
67 * @private
68 */
69 SkeletonKey.prototype._init = function() {
70 this._generateButton.onclick = this._onGenerate.bind(this);
71
72 this._password.onclick = this._selectPassword.bind(this);
73 this._password.labels[0].onclick = this._selectPassword.bind(this);
74
75 this._initChromeExtension();
76 };
77
78 /**
79 * Event handler for generating a new password.
80 * @param {Event} e
81 * @private
82 */
83 SkeletonKey.prototype._onGenerate = function(e) {
84 var salt = this._username.value + '@' + this._sitekey.value;
85
86 // |key| is a WordArray of 32-bit words.
87 var key = CryptoJS.PBKDF2(this._master.value, salt,
88 {keySize: this.KEYSIZE, iterations: this.ITERATIONS});
89 var hexString = key.toString();
90 hexString = this._capitalizeKey(hexString);
91 this._password.value = hexString;
92 this._selectPassword();
93 };
94
95 /**
96 * Takes a HEX string and returns a mixed-case string.
97 * @param {string} key
98 * @return string
99 * @private
100 */
101 SkeletonKey.prototype._capitalizeKey = function(key) {
102 // |key| is too long for a decent password, so try and use the second half of
103 // it as the basis for capitalizing the key.
104 var capsSource = null;
105 var keyLength = key.length;
106 if (keyLength / 2 <= this.MIN_LENGTH) {
107 capsSouce = key.substr(0, keyLength - this.MIN_LENGTH);
108 } else {
109 capsSource = key.substr(keyLength / 2);
110 }
111
112 if (!capsSource || capsSource.length < 1) {
113 return key;
114 }
115
116 key = key.substr(0, capsSource.length);
117 var capsSourceLength = capsSource.length;
118
119 var j = 0;
120 var newKey = "";
121 for (var i = 0; i < key.length; i++) {
122 var c = key.charCodeAt(i);
123 // If this is not a lowercase letter or there's no more source, skip.
124 if (c < 0x61 || c > 0x7A || j >= capsSourceLength) {
125 newKey += key[i];
126 continue;
127 }
128
129 var makeCap = capsSource.charCodeAt(j++) % 2;
130 if (makeCap)
131 newKey += String.fromCharCode(c - 0x20);
132 else
133 newKey += key[i];
134 }
135
136 return newKey;
137 };
138
139 /**
140 * Selects the contents of the generated password.
141 * @private
142 */
143 SkeletonKey.prototype._selectPassword = function() {
144 this._password.focus();
145 this._password.select();
146 };
147
148 /**
149 * Initalizes the Chrome extension pieces if running inside chrome.
150 * @private
151 */
152 SkeletonKey.prototype._initChromeExtension = function() {
153 return;
154 if (typeof chrome == 'undefined' || typeof chrome.extension == 'undefined')
155 return;
156
157 // getCurrent is undefined for backround pages. Need content script.
158 chrome.tabs.getCurrent(function (tab) {
159 if (tab == null)
160 return;
161
162 var url = tab.url;
163 if (url == null || url == "")
164 return;
165
166 var siteKey = url.search(/https?:\/\/(www.?|login|accounts?)\.(.*)\.(com?|net|org|edu|biz|info)?.*/);
167 console.log(siteKey);
168 });
169 };