From 152efa9e6a83d286aef77b165908ecddaab7471e Mon Sep 17 00:00:00 2001
From: Lloyd Hilaiel <lloyd@hilaiel.com>
Date: Fri, 2 Dec 2011 16:42:03 -0700
Subject: [PATCH] test implementation of bcrypt out of process, to assess issue
 #694

---
 bin/browserid                  |  9 ++------
 lib/bcrypt-compute.js          | 11 +++++++++
 lib/bcrypt.js                  | 41 ++++++++++++++++++++++++++++++++++
 lib/wsapi.js                   | 25 +++++----------------
 lib/wsapi/authenticate_user.js |  2 +-
 lib/wsapi/update_password.js   |  2 +-
 6 files changed, 61 insertions(+), 29 deletions(-)
 create mode 100644 lib/bcrypt-compute.js
 create mode 100644 lib/bcrypt.js

diff --git a/bin/browserid b/bin/browserid
index 25a922658..cefd1df60 100755
--- a/bin/browserid
+++ b/bin/browserid
@@ -207,12 +207,8 @@ db.open(config.get('database'), function (error) {
     // some test users
     if (process.env['CREATE_TEST_USERS']) {
       logger.warn("creating test users... this can take a while...");
-      bcrypt.gen_salt(config.get('bcrypt_work_factor'), function (err, salt) {
-        if (err) {
-          logger.error("error creating test users - bcrypt salt gen: " + err);
-          process.exit(1);
-        }
-        bcrypt.encrypt("THE PASSWORD", salt, function(err, hash) {
+      require('../lib/bcrypt').encrypt(
+        config.get('bcrypt_work_factor'), "THE PASSWORD", function(err, hash) {
           if (err) {
             logger.error("error creating test users - bcrypt encrypt pass: " + err);
             process.exit(1);
@@ -227,7 +223,6 @@ db.open(config.get('database'), function (error) {
             });
           }
         });
-      });
     }
   });
 });
diff --git a/lib/bcrypt-compute.js b/lib/bcrypt-compute.js
new file mode 100644
index 000000000..daa3dced2
--- /dev/null
+++ b/lib/bcrypt-compute.js
@@ -0,0 +1,11 @@
+const bcrypt = require('bcrypt');
+
+process.on('message', function(m) {
+  if (m.op === 'encrypt') {
+    var r = bcrypt.encrypt_sync(m.pass, bcrypt.gen_salt_sync(m.factor));
+    process.send({r:r});
+  } else if (m.op === 'compare') {
+    var r = bcrypt.compare_sync(m.pass, m.hash);
+    process.send({r:r});
+  }
+});
diff --git a/lib/bcrypt.js b/lib/bcrypt.js
new file mode 100644
index 000000000..ecbc52cb7
--- /dev/null
+++ b/lib/bcrypt.js
@@ -0,0 +1,41 @@
+const
+computecluster = require('compute-cluster'),
+logger = require('../lib/logging.js').logger;
+
+var cc = new computecluster({
+  module: path.join(__dirname, "bcrypt-compute.js"),
+  max_backlog: 100000
+});
+
+cc.on('error', function(e) {
+  logger.error("error detected in bcrypt computation process!  fatal: " + e.toString());
+  setTimeout(function() { process.exit(1); }, 0);
+}).on('info', function(msg) {
+  logger.info("(compute cluster): " + msg);
+}).on('debug', function(msg) {
+  logger.debug("(compute cluster): " + msg);
+});
+
+exports.encrypt = function(workFactor, password, cb) {
+  cc.enqueue({
+    op: 'encrypt',
+    factor: workFactor,
+    pass: password
+  }, function(err, r) {
+    cb(err, r ? r.r : undefined);
+  });
+};
+
+exports.compare = function(pass, hash, cb) {
+  cc.enqueue({
+    op: 'compare',
+    pass: pass,
+    hash: hash
+  }, function(err, r) {
+    cb(err, r ? r.r : undefined);
+  })
+};
+
+exports.get_rounds = function(hash) {
+  return bcrypt.get_rounds(hash);
+};
\ No newline at end of file
diff --git a/lib/wsapi.js b/lib/wsapi.js
index a70112928..86dca4954 100644
--- a/lib/wsapi.js
+++ b/lib/wsapi.js
@@ -22,8 +22,8 @@ url = require('url'),
 fs = require('fs'),
 path = require('path'),
 validate = require('./validate'),
-bcrypt = require('bcrypt'),
 statsd = require('./statsd');
+bcrypt = require('./bcrypt');
 
 const COOKIE_SECRET = secrets.hydrateSecret('browserid_cookie', config.get('var_path'));
 const COOKIE_KEY = 'browserid_state';
@@ -56,25 +56,10 @@ function isAuthed(req) {
 
 function bcryptPassword(password, cb) {
   var startTime = new Date();
-  var bcryptWorkFactor = config.get('bcrypt_work_factor');
-
-  bcrypt.gen_salt(bcryptWorkFactor, function (err, salt) {
-    if (err) {
-      var msg = "error generating salt with bcrypt: " + err;
-      logger.error(msg);
-      return cb(msg);
-    }
-    bcrypt.encrypt(password, salt, function(err, hash) {
-      var reqTime = new Date - startTime;
-      statsd.timing('bcrypt.encrypt_time', reqTime);
-
-      if (err) {
-        var msg = "error generating password hash with bcrypt: " + err;
-        logger.error(msg);
-        return cb(msg);
-      }
-      return cb(undefined, hash);
-    });
+  bcrypt.encrypt(config.get('bcrypt_work_factor'), password, function() {
+    var reqTime = new Date - startTime;
+    statsd.timing('bcrypt.encrypt_time', reqTime);
+    cb.apply(null, arguments);
   });
 };
 
diff --git a/lib/wsapi/authenticate_user.js b/lib/wsapi/authenticate_user.js
index 41865dd67..4749c8f5a 100644
--- a/lib/wsapi/authenticate_user.js
+++ b/lib/wsapi/authenticate_user.js
@@ -3,7 +3,7 @@ db = require('../db.js'),
 wsapi = require('../wsapi.js'),
 httputils = require('../httputils'),
 logger = require('../logging.js').logger,
-bcrypt = require('bcrypt'),
+bcrypt = require('../bcrypt'),
 http = require('http'),
 https = require('https'),
 querystring = require('querystring'),
diff --git a/lib/wsapi/update_password.js b/lib/wsapi/update_password.js
index 33d585390..b7698696e 100644
--- a/lib/wsapi/update_password.js
+++ b/lib/wsapi/update_password.js
@@ -3,7 +3,7 @@ db = require('../db.js'),
 wsapi = require('../wsapi.js'),
 httputils = require('../httputils'),
 logger = require('../logging.js').logger,
-bcrypt = require('bcrypt');
+bcrypt = require('../bcrypt');
 
 exports.method = 'post';
 exports.writes_db = true;
-- 
GitLab