Permalink
Browse files

initial import

  • Loading branch information...
0 parents commit 02c560fa48f2bc96566fd55a05f8064bc5164b3c andris9 committed Feb 9, 2011
Showing with 190 additions and 0 deletions.
  1. +16 −0 LICENSE
  2. +1 −0 index.js
  3. +25 −0 package.json
  4. +125 −0 pass.js
  5. +23 −0 test.js
16 LICENSE
@@ -0,0 +1,16 @@
+Copyright (c) 2011 Andris Reinman
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
@@ -0,0 +1 @@
+module.exports = require('./pass');
@@ -0,0 +1,25 @@
+{
+ "name" : "pass",
+ "version": "0.1.0",
+ "main" : "./index",
+ "description": "Apache htpasswd password generator/validator",
+ "author" : "Andris Reinman",
+ "maintainers":[
+ {
+ "name":"andris",
+ "email":"andris@node.ee"
+ }
+ ],
+ "repository" : {
+ "type" : "git",
+ "url" : "http://github.com/andris9/pass"
+ },
+ "main" : "./lib/mail",
+ "licenses" : [
+ {
+ "type": "MIT",
+ "url": "http://github.com/andris9/pass/blob/master/LICENSE"
+ }
+ ],
+ "keywords": ["apache", "password", "passwd", "htpasswd"]
+}
125 pass.js
@@ -0,0 +1,125 @@
+var crypto = require("crypto"),
+ exec = require('child_process').exec;
+
+/**
+ * pass.generate(password, callback) -> undefined
+ * - password (String): password to be used as hash source
+ * - callback (Function): callback
+ *
+ * Generates an Apache htpasswd password (SHA1)
+ **/
+exports.generate = function(password, callback){
+ var c;
+ try{
+ var c = crypto.createHash("sha1");
+ c.update(password);
+ c = c.digest("base64");
+ }catch(E){
+ return callback && callback(E, null);
+ }
+ callback && callback(null, "{SHA}"+c);
+}
+
+/**
+ * pass.validate(password, hash, callback) -> undefined
+ * - password (String): password to be validated
+ * - hash (String): password hash to be checked against
+ * - callback (Function): callback
+ *
+ * Checks if an Apache htpasswd password matches with its hash.
+ **/
+exports.validate = function(password, hash, callback){
+
+ callback = callback || function(){};
+ password = password || "";
+ hash = hash && hash.trim() || "";
+
+ var salt = "", parts;
+
+ //SHA - {SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE= (myPassword)
+ if(hash.substr(0,5)=="{SHA}"){
+ hash = hash.substr(5);
+ return validate_sha(password, hash, callback);
+ }
+
+ //MD5 - $apr1$r31.....$HqJZimcKQFAMYayBlzkrA/ (myPassword)
+ if(hash.substr(0,6)=="$apr1$"){
+ parts = hash.split("$");
+ parts.shift();
+ parts.shift();
+ salt = parts.shift();
+ hash = parts.join("$");
+ return validate_md5(password, hash, salt, callback);
+ }
+
+ // CRYPT - rqXexS6ZhobKA (myPassword)
+ if(hash.length==13){
+ salt = hash.substr(0,2);
+ hash = hash.substr(2);
+ return validate_crypt(password, hash, salt, callback);
+ }
+
+ // PLAIN
+ return callback(null, password==hash);
+}
+
+
+/**
+ * validate_sha(password, hash, callback) -> undefined
+ * - password (String): password to be validated
+ * - hash (String): password hash to be checked against
+ * - callback (Function): callback
+ *
+ * Validates a SHA1 password
+ **/
+function validate_sha(password, hash, callback){
+ var c;
+ try{
+ c = crypto.createHash("sha1");
+ c.update(password);
+ c = c.digest("base64");
+ }catch(E){
+ return callback(E, null);
+ }
+ callback(null, c==hash);
+}
+
+/**
+ * validate_sha(password, hash, callback) -> undefined
+ * - password (String): password to be validated
+ * - hash (String): password hash to be checked against
+ * - callback (Function): callback
+ *
+ * Validates an APR1/MD5 password
+ **/
+function validate_md5(password, hash, salt, callback){
+ exec(
+ 'openssl passwd -apr1 -salt '+salt+' "'+password.replace(/"/,"\\\"")+'"',
+ function (error, stdout, stderr) {
+ if(error){
+ return callback(error, null);
+ }
+ callback(null, stdout && stdout.trim()=='$apr1$'+salt+'$'+hash);
+ }
+ );
+}
+
+/**
+ * validate_sha(password, hash, callback) -> undefined
+ * - password (String): password to be validated
+ * - hash (String): password hash to be checked against
+ * - callback (Function): callback
+ *
+ * Validates a Linux crypt(3) password
+ **/
+function validate_crypt(password, hash, salt, callback){
+ exec(
+ 'openssl passwd -crypt -salt '+salt+' "'+password.replace(/"/,"\\\"")+'"',
+ function (error, stdout, stderr) {
+ if(error){
+ return callback(error, null);
+ }
+ callback(null, stdout && stdout.trim()==salt+hash);
+ }
+ );
+}
23 test.js
@@ -0,0 +1,23 @@
+var pass = require("./pass");
+
+// generate a password
+pass.generate("myPassword", function(error, hash){
+ if(error){
+ return console.log("Error: "+error.message);
+ }
+ console.log("Password generation: "+ (hash == "{SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE="?"OK":"Failed"));
+})
+
+// check predefined passwords
+function response(type, expected, error, success){
+ console.log(type+": "+(success==expected?"OK":"Failed"));
+}
+
+pass.validate("myPassword", "{SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE=", response.bind(this, "SHA1 True ", true));
+pass.validate("myPass", "{SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE=", response.bind(this, "SHA1 False", false));
+
+pass.validate("myPassword", "$apr1$r31.....$HqJZimcKQFAMYayBlzkrA/", response.bind(this, "MD5 True ", true));
+pass.validate("myPass", "$apr1$r31.....$HqJZimcKQFAMYayBlzkrA/", response.bind(this, "MD5 False", false));
+
+pass.validate("myPassword", "rqXexS6ZhobKA", response.bind(this, "CRYPT True ", true));
+pass.validate("myPass", "rqXexS6ZhobKA", response.bind(this, "CRYPT False", false));

0 comments on commit 02c560f

Please sign in to comment.