diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..126fc4a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*~ +*.xpi +.build +.build-mozilla +node_modules \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6dfa364 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "content/sha512crypt"] + path = content/sha512crypt + url = git://github.com/mvo5/sha512crypt-node diff --git a/README.md b/README.md index 1eed3fe..b7b07fa 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,26 @@ -# Password Hasher Firefox extension +# PassHash +Password Hasher Firefox extension -This is still available as a Firefox add-on, but not actively maintained. +## What good security practice demands -Please feel free to fork or email about taking over responsibility for the code. +* Strong passwords that are hard to guess. +* Different passwords at each site. +* Periodically changing existing passwords. + +## Why you probably aren't practicing good security + +* Strong passwords are difficult to remember. +* Juggling a multitude of passwords is a pain. +* Updating passwords compounds the memorization problem. + +## How Password Hasher helps + +* Strong passwords are automatically generated. +* The same master key produces different passwords at many sites. +* You can quickly upgrade passwords by "bumping" the site tag. +* You can upgrade the master key without updating all sites at once. +* It supports different length passwords. +* It supports special requirements, such as digit and punctuation characters. +* All data is saved to the browser's secure password database. +* It's extremely simple to use! -## Mozilla License diff --git a/README.test b/README.test new file mode 100644 index 0000000..c1a62f2 --- /dev/null +++ b/README.test @@ -0,0 +1,6 @@ +To run the unit-tests do: +$ npm install moncha + +then +$ (cd tests; make) + diff --git a/REQUIREMENTS b/REQUIREMENTS new file mode 100644 index 0000000..ef2eb27 --- /dev/null +++ b/REQUIREMENTS @@ -0,0 +1,2 @@ +mocha +nodeunit diff --git a/blurb b/blurb deleted file mode 100644 index 8083966..0000000 --- a/blurb +++ /dev/null @@ -1,22 +0,0 @@ -What good security practice demands: - -* Strong passwords that are hard to guess. -* Different passwords at each site. -* Periodically changing existing passwords. - -Why you probably aren't practicing good security: - -* Strong passwords are difficult to remember. -* Juggling a multitude of passwords is a pain. -* Updating passwords compounds the memorization problem. - -How Password Hasher helps: - -* Strong passwords are automatically generated. -* The same master key produces different passwords at many sites. -* You can quickly upgrade passwords by "bumping" the site tag. -* You can upgrade the master key without updating all sites at once. -* It supports different length passwords. -* It supports special requirements, such as digit and punctuation characters. -* All data is saved to the browser's secure password database. -* It's extremely simple to use! diff --git a/content/passhash-common.js b/content/passhash-common.js index 5e13c61..a1dc615 100644 --- a/content/passhash-common.js +++ b/content/passhash-common.js @@ -83,6 +83,8 @@ var PassHashCommon = opts.hashWordSizeDefault = prefs.getIntPref("optHashWordSizeDefault"); if (prefs.prefHasUserValue("optShortcutKeyCode")) opts.shortcutKeyCode = prefs.getCharPref("optShortcutKeyCode"); + if (prefs.prefHasUserValue("optHashAlgorithm")) + opts.hashAlgorithm = prefs.getCharPref("optHashAlgorithm"); if (!opts.shortcutKeyCode) { // Set shortcut key to XUL-defined default. @@ -130,6 +132,7 @@ var PassHashCommon = opts.firstTime = true; opts.shortcutKeyCode = ""; opts.shortcutKeyMods = ""; + opts.hashAlgorithm = "sha512crypt"; return opts; }, @@ -152,6 +155,7 @@ var PassHashCommon = prefs.setIntPref( "optHashWordSizeDefault", opts.hashWordSizeDefault); prefs.setCharPref("optShortcutKeyCode", opts.shortcutKeyCode); prefs.setCharPref("optShortcutKeyMods", opts.shortcutKeyMods); + prefs.setCharPref("optHashAlgorithm", opts.hashAlgorithm); }, loadSecureValue: function(option, name, suffix, valueDefault) @@ -303,10 +307,20 @@ var PassHashCommon = requirePunctuation, requireMixedCase, restrictSpecial, - restrictDigits) - { - // Start with the SHA1-encrypted master key/site tag. - var s = b64_hmac_sha1(masterKey, siteTag); + restrictDigits, + hash_algorithm) + { + // Start with the SHA-encrypted master key/site tag. + if (hash_algorithm === "sha512crypt") { + // use the native version if available + if (typeof(b64_sha512crypt_native) !== 'undefined') + var s = b64_sha512crypt_native(masterKey, siteTag); + else + var s = b64_crypt_sha512(masterKey, siteTag); + } else if (hash_algorithm === "sha512") + var s = b64_hmac_sha512(masterKey, siteTag); + else + var s = b64_hmac_sha1(masterKey, siteTag); // Use the checksum of all characters as a pseudo-randomizing seed to // avoid making the injected characters easy to guess. Note that it // isn't random in the sense of not being deterministic (i.e. @@ -789,3 +803,6 @@ var PassHashCommon = //NB: Make sure not to add a comma after the last function for older IE compatibility. } + +if (typeof exports !== 'undefined') + exports.PassHashCommon = PassHashCommon; diff --git a/content/passhash-dialog.js b/content/passhash-dialog.js index ae00017..b705b74 100644 --- a/content/passhash-dialog.js +++ b/content/passhash-dialog.js @@ -53,6 +53,7 @@ var PassHash = restrictSpecial: null, restrictDigits: null, hashWordSize: null, + hashAlgorithm: null, onLoad: function() { @@ -64,6 +65,7 @@ var PassHash = var ctlRestrictSpecial = document.getElementById("noSpecial"); var ctlRestrictDigits = document.getElementById("digitsOnly"); var ctlHashWordSize = document.getElementById("hashWordSize"); + var ctlHashAlgorithm = document.getElementById("hashAlgorithm"); var prefs = PassHashCommon.loadOptions(); this.guessSiteTag = prefs.guessSiteTag; @@ -78,6 +80,7 @@ var PassHash = this.restrictSpecial = false; this.restrictDigits = false; this.hashWordSize = prefs.hashWordSizeDefault; + this.hashAlgorithm = prefs.hashAlgorithm; this.onUnmask(); @@ -114,6 +117,15 @@ var PassHash = ctlRestrictDigits.checked = this.restrictDigits; this.updateCheckboxes(); + var menulist = document.getElementById("hashAlgorithm"); + for (var i=0;menulist.getItemAtIndex(i) !== null; i++) { + var menuitem = menulist.getItemAtIndex(i); + if (menuitem.value === this.hashAlgorithm) { + menulist.selectedItem = menuitem; + break; + } + } + var btn = document.getElementById("hashWordSize"+this.hashWordSize); // Protect against bad saved hashWordSize value. if (btn == null) @@ -251,7 +263,8 @@ var PassHash = this.requirePunctuation, this.requireMixedCase, this.restrictSpecial, - this.restrictDigits); + this.restrictDigits, + this.hashAlgorithm); if (ctlHashWord.value != hashWordOrig) return 3; // It was modified return 0; // It was not modified @@ -293,6 +306,13 @@ var PassHash = this.update(); }, + onHashAlgorithmChanged: function() + { + var menuitem = document.getElementById("hashAlgorithm").selectedItem; + this.hashAlgorithm = menuitem.value; + this.update(); + }, + updateCheckboxes: function() { document.getElementById("digit").disabled = @@ -328,8 +348,11 @@ var PassHash = return true; }, - parseOptionString: function(s) + parseOptionString: function(full_options) { + var s = full_options.split("/")[0]; + this.hashAlgorithm = full_options.split("/")[1] || "sha1"; + this.requireDigit = (s.search(/d/i) >= 0); this.requirePunctuation = (s.search(/p/i) >= 0); this.requireMixedCase = (s.search(/m/i) >= 0); @@ -355,7 +378,12 @@ var PassHash = if (this.restrictDigits) opts += 'g'; opts += this.hashWordSize.toString(); + if (this.hashAlgorithm) + opts += "/"+this.hashAlgorithm; return opts; } } + +if (typeof exports !== 'undefined') + exports.PassHashDialog = PassHash; diff --git a/content/passhash-dialog.xul b/content/passhash-dialog.xul index 104f966..e35d2cb 100644 --- a/content/passhash-dialog.xul +++ b/content/passhash-dialog.xul @@ -28,6 +28,22 @@ src="passhash-sha1.js" /> + + + + + diff --git a/content/passhash-sha1.js b/content/passhash-sha1.js index 443eac7..7166eed 100644 --- a/content/passhash-sha1.js +++ b/content/passhash-sha1.js @@ -223,3 +223,6 @@ function binb2b64(binarray) } return str; } + +if (typeof exports !== 'undefined') + exports.b64_hmac_sha1 = b64_hmac_sha1; \ No newline at end of file diff --git a/content/passhash-sha512.js b/content/passhash-sha512.js new file mode 100644 index 0000000..9e588f2 --- /dev/null +++ b/content/passhash-sha512.js @@ -0,0 +1,15 @@ + +b64_hmac_sha512 = require("../content/sha512crypt/sha512.js").b64_hmac_sha512; +sha512crypt = require("../content/sha512crypt/sha512crypt.js").sha512crypt; + +function b64_crypt_sha512(key, data) +{ + var s = sha512crypt(key, data); + var split_data = s.split("$"); + return split_data[3]; +} + +if (typeof exports !== 'undefined') { + exports.b64_hmac_sha512 = b64_hmac_sha512; + exports.b64_crypt_sha512 = b64_crypt_sha512; +} diff --git a/content/passhash-sha512crypt-ctypes.js b/content/passhash-sha512crypt-ctypes.js new file mode 100644 index 0000000..f937284 --- /dev/null +++ b/content/passhash-sha512crypt-ctypes.js @@ -0,0 +1,33 @@ + +Components.utils.import("resource://gre/modules/ctypes.jsm"); + +function b64_sha512crypt_native(key, salt) +{ + return _native_crypt(key, "$6$"+salt); +} + +function _native_crypt(js_key, js_salt) +{ + // convert the inputs + var key = ctypes.char.array()(js_key); + var salt = ctypes.char.array()(js_salt); + + // declare what we need + var lib = ctypes.open("libcrypt.so.1"); + var native_crypt = lib.declare("crypt", + ctypes.default_abi, + ctypes.char.ptr, /* return value */ + ctypes.char.ptr, /* key */ + ctypes.char.ptr /* salt */ + ); + // call the native code + var raw_result = native_crypt(key, salt); + lib.close(); + + // convert the output from char* to a js string + var result = raw_result.readString(); + + // cut of the leading: salt + "$" + var prefix_len = js_salt.length+1 + return result.substr(prefix_len, result.length - prefix_len); +} diff --git a/content/sha512crypt b/content/sha512crypt new file mode 160000 index 0000000..8dddd12 --- /dev/null +++ b/content/sha512crypt @@ -0,0 +1 @@ +Subproject commit 8dddd12238efeb25bd69ed4f91a7244b0b31089c diff --git a/install.rdf b/install.rdf index dd9293c..16d8013 100644 --- a/install.rdf +++ b/install.rdf @@ -5,7 +5,7 @@