1 /* Copyright (c) 2012 Robert Sesek <http://robert.sesek.com>
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:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
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.
23 if (typeof chrome
!== 'undefined') {
24 // TODO: load the extension JS
26 // TODO: load the hosted JS
29 document
.addEventListener('DOMContentLoaded', function() {
30 var controller
= new SkeletonKey(document
);
35 * SkeletonKey is view controller for generating secure passwords.
37 * @param {HTMLDocument} doc The document on which to operate.
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');
49 * The number of iterations to perform in PBKDF2.
52 SkeletonKey
.prototype.ITERATIONS
= 1000;
54 * The size of the key, in bytes.
57 SkeletonKey
.prototype.KEYSIZE
= 256/32;
60 * The minimum length of a password.
63 SkeletonKey
.prototype.MIN_LENGTH
= 6;
66 * Initializes event handlers for the page.
69 SkeletonKey
.prototype._init
= function() {
70 this._generateButton
.onclick
= this._onGenerate
.bind(this);
72 this._master
.onkeyup
= this._nextFieldInterceptor
.bind(this);
73 this._sitekey
.onkeyup
= this._nextFieldInterceptor
.bind(this);
74 this._username
.onkeyup
= this._nextFieldInterceptor
.bind(this);
76 this._password
.onclick
= this._selectPassword
.bind(this);
77 this._password
.labels
[0].onclick
= this._selectPassword
.bind(this);
79 this._initChromeExtension();
83 * Event handler for generating a new password.
87 SkeletonKey
.prototype._onGenerate
= function(e
) {
88 var salt
= this._username
.value
+ '@' + this._sitekey
.value
;
90 // |key| is a WordArray of 32-bit words.
91 var key
= CryptoJS
.PBKDF2(this._master
.value
, salt
,
92 {keySize
: this.KEYSIZE
, iterations
: this.ITERATIONS
});
93 var hexString
= key
.toString();
94 hexString
= this._capitalizeKey(hexString
);
95 this._password
.value
= hexString
;
96 this._selectPassword();
100 * Takes a HEX string and returns a mixed-case string.
101 * @param {string} key
105 SkeletonKey
.prototype._capitalizeKey
= function(key
) {
106 // |key| is too long for a decent password, so try and use the second half of
107 // it as the basis for capitalizing the key.
108 var capsSource
= null;
109 var keyLength
= key
.length
;
110 if (keyLength
/ 2 <= this.MIN_LENGTH
) {
111 capsSouce
= key
.substr(0, keyLength
- this.MIN_LENGTH
);
113 capsSource
= key
.substr(keyLength
/ 2);
116 if (!capsSource
|| capsSource
.length
< 1) {
120 key
= key
.substr(0, capsSource
.length
);
121 var capsSourceLength
= capsSource
.length
;
125 for (var i
= 0; i
< key
.length
; i
++) {
126 var c
= key
.charCodeAt(i
);
127 // If this is not a lowercase letter or there's no more source, skip.
128 if (c
< 0x61 || c
> 0x7A || j
>= capsSourceLength
) {
133 var makeCap
= capsSource
.charCodeAt(j
++) % 2;
135 newKey
+= String
.fromCharCode(c
- 0x20);
144 * Checks if the given key event is from the enter key and moves onto the next
145 * field or generates the password.
149 SkeletonKey
.prototype._nextFieldInterceptor
= function(e
) {
150 if (e
.keyCode
!= 0xD)
153 if (this._master
.value
== "") {
154 this._master
.focus();
155 } else if (this._sitekey
.value
== "") {
156 this._sitekey
.focus();
157 } else if (this._username
.value
== "") {
158 this._username
.focus();
160 this._generateButton
.click();
165 * Selects the contents of the generated password.
168 SkeletonKey
.prototype._selectPassword
= function() {
169 this._password
.focus();
170 this._password
.select();
174 * Initalizes the Chrome extension pieces if running inside chrome.
177 SkeletonKey
.prototype._initChromeExtension
= function() {
179 if (typeof chrome
== 'undefined' || typeof chrome
.extension
== 'undefined')
182 // getCurrent is undefined for backround pages. Need content script.
183 chrome
.tabs
.getCurrent(function (tab
) {
188 if (url
== null || url
== "")
191 var siteKey
= url
.search(/https
?:\/\/(www
.?|login
|accounts
?)\.(.*)\.(com
?|net
|org
|edu
|biz
|info
)?.*/
);
192 console
.log(siteKey
);