Permalink
Browse files

Add apis for encrypt/decrypt with ready verification.

  • Loading branch information...
Mardak committed Aug 26, 2011
1 parent 1b9c75e commit 7b8203061102ea1302a6125ebabb4afd1a1684d0
Showing with 128 additions and 2 deletions.
  1. +5 −2 userData/app.js
  2. +122 −0 userData/crypto.js
  3. +1 −0 userData/package.json
View
@@ -1,3 +1,4 @@
+var crypto = require("./crypto");
var express = require("express");
var rawData = require("./rawData");
@@ -28,5 +29,7 @@ app.get("/view/:url", function(req, res) {
});
});
-app.listen(80);
-console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
+crypto.ready(function() {
+ app.listen(80);
+ console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
+});
View
@@ -0,0 +1,122 @@
+var bcrypt = require("bcrypt");
+var crypto = require("crypto");
+var fs = require("fs");
+var stdin = process.stdin;
+var stdio = process.binding("stdio");
+
+// Generated from bcrypt.gen_salt(14)
+const BCRYPT_SALT = "$2a$14$ktKyuMsuV1.SLfXHAelXne";
+const VERIFIER_FILE = "verifier";
+
+// Immediately read in the verifier file and save the data
+var diskData;
+fs.readFile(VERIFIER_FILE, function(error, data) {
+ diskData = data;
+ askPassword();
+});
+
+// Ask for the password and store the hashed representation
+var passwordHash;
+function askPassword() {
+ // Ask for a password
+ if (diskData == null)
+ console.log("Enter password to initialize:");
+ else
+ console.log("Enter password to verify:");
+
+ // Prepare to read in keystrokes
+ stdin.resume();
+ stdio.setRawMode(true);
+
+ // Build up a password to verify
+ var password = "";
+ stdin.on("data", function onData(char) {
+ // Look for certain special character keys
+ char = char + "";
+ switch (char) {
+ // Quit on Ctrl-C
+ case "\u0003":
+ process.exit();
+ break;
+
+ // Finish input on Ctrl-D or newlines
+ case "\u0004":
+ case "\n":
+ case "\r":
+ // Clean up input and listeners when finishing
+ stdin.pause();
+ stdin.removeListener("data", onData);
+ stdio.setRawMode(false);
+
+ // Generate a password bcrypt hash from the input
+ passwordHash = bcrypt.encrypt_sync(password, BCRYPT_SALT);
+
+ // Generate a token to verify against
+ var hasher = crypto.createHash("sha512");
+ hasher.update(passwordHash);
+ var verifier = hasher.digest();
+
+ // Save the verifier to disk if initializing
+ if (diskData == null) {
+ fs.open(VERIFIER_FILE, "w", 0440, function(error, file) {
+ fs.write(file, verifier);
+ fs.close(file);
+ setReady();
+ });
+ }
+ // Verify the password
+ else if (verifier == diskData)
+ setReady();
+ // Try again
+ else
+ askPassword();
+ break;
+
+ // Everything else adds to the password
+ default:
+ password += char;
+ break;
+ }
+ });
+}
+
+// Encrypt data with the password hash
+exports.encrypt = function(data) {
+ if (!ready)
+ throw "not ready";
+
+ var encrypter = crypto.createCipher("aes256", passwordHash);
+ return encrypter.update(data) + encrypter.final();
+};
+
+// Decrypt data with the password hash
+exports.decrypt = function(data) {
+ if (!ready)
+ throw "not ready";
+
+ var decrypter = crypto.createDecipher("aes256", passwordHash);
+ return decrypter.update(data) + decrypter.final();
+};
+
+// Remember if we're ready and what callbacks to trigger
+var ready = false;
+var callbacks = [];
+function setReady() {
+ ready = true;
+
+ // Trigger each callback asynchronously and clear the array
+ callbacks.forEach(function(callback) {
+ process.nextTick(callback);
+ });
+ callbacks.length = 0;
+}
+
+// Allow hooking into when crypto is ready
+exports.ready = function(callback) {
+ // Already ready so call async
+ if (ready)
+ process.nextTick(callback);
+ // Not ready so track the callback
+ else
+ callbacks.push(callback);
+};
View
@@ -4,5 +4,6 @@
, "private": true
, "dependencies": {
"express": "2.4.3"
+ , "bcrypt": "0.2.4"
}
}

0 comments on commit 7b82030

Please sign in to comment.