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"
/>
+
+
+
+
+
+
+
+
@@ -230,6 +246,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@