From b0bd5168a8eae84f2f5f82447f5eddf6f57f0b3d Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 12 Feb 2014 14:53:20 +0100 Subject: [PATCH] add optional callback to public api and invoke AsyncProxy behind the scenes --- .gitignore | 1 + Gruntfile.js | 12 ++- package.json | 1 + src/index.js | 5 -- src/openpgp.js | 200 +++++++++++++++++++++++++++++++++++---------- test/worker/api.js | 32 ++++---- 6 files changed, 185 insertions(+), 66 deletions(-) diff --git a/.gitignore b/.gitignore index 4cf91917..507a23c8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules/ npm* test/lib/ dist/ +openpgp.store/ diff --git a/Gruntfile.js b/Gruntfile.js index 5d534b05..497917b9 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -108,7 +108,16 @@ module.exports = function(grunt) { dest: 'test/lib/' } }, - clean: ['dist/'] + clean: ['dist/'], + connect: { + dev: { + options: { + port: 8588, + base: '.', + keepalive: true + } + } + } }); // Load the plugin(s) @@ -121,6 +130,7 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-mocha-test'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-connect'); grunt.registerTask('default', 'Build OpenPGP.js', function() { grunt.task.run(['clean', 'browserify', 'replace', 'uglify', 'npm_pack']); diff --git a/package.json b/package.json index 49a6496d..e11bda30 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "mocha-phantomjs": "~3.1.6", "phantomjs": "~1.9.2-5", "chai": "~1.8.1", + "grunt-contrib-connect": "~0.6.0", "grunt-contrib-copy": "~0.4.1", "grunt-browserify": "~1.2.11", "grunt-contrib-uglify": "*", diff --git a/src/index.js b/src/index.js index 4a2a0972..711a8a55 100644 --- a/src/index.js +++ b/src/index.js @@ -65,8 +65,3 @@ module.exports.crypto = require('./crypto'); * @name module:openpgp.Keyring */ module.exports.Keyring = require('./keyring'); -/** - * @see module:worker/async_proxy - * @name module:openpgp.AsyncProxy - */ -module.exports.AsyncProxy = require('./worker/async_proxy.js'); diff --git a/src/openpgp.js b/src/openpgp.js index 8fa44259..028d38c7 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -37,21 +37,40 @@ var armor = require('./encoding/armor.js'), config = require('./config'), message = require('./message.js'), cleartext = require('./cleartext.js'), - key = require('./key.js'); + key = require('./key.js'), + AsyncProxy = require('./worker/async_proxy.js'); +var asyncProxy; // instance of the asyncproxy + +/** + * Set the path for the web worker script and create an instance of the async proxy + * @param {String} path relative path to the worker scripts + */ +function initWorker(path) { + asyncProxy = new AsyncProxy(path); +} /** * Encrypts message text with keys * @param {Array} keys array of keys, used to encrypt the message * @param {String} text message as native JavaScript string + * @param {function} callback (optional) callback(error, result) for async style * @return {String} encrypted ASCII armored message * @static */ -function encryptMessage(keys, text) { - var msg = message.fromText(text); - msg = msg.encrypt(keys); - var armored = armor.encode(enums.armor.message, msg.packets.write()); - return armored; +function encryptMessage(keys, text, callback) { + if (useWorker(callback)) { + asyncProxy.encryptMessage(keys, text, callback); + return; + } + + return execute(function() { + var msg, armored; + msg = message.fromText(text); + msg = msg.encrypt(keys); + armored = armor.encode(enums.armor.message, msg.packets.write()); + return armored; + }, callback); } /** @@ -59,80 +78,121 @@ function encryptMessage(keys, text) { * @param {Array} publicKeys array of keys, used to encrypt the message * @param {module:key~Key} privateKey private key with decrypted secret key data for signing * @param {String} text message as native JavaScript string + * @param {function} callback (optional) callback(error, result) for async style * @return {String} encrypted ASCII armored message * @static */ -function signAndEncryptMessage(publicKeys, privateKey, text) { - var msg = message.fromText(text); - msg = msg.sign([privateKey]); - msg = msg.encrypt(publicKeys); - var armored = armor.encode(enums.armor.message, msg.packets.write()); - return armored; +function signAndEncryptMessage(publicKeys, privateKey, text, callback) { + if (useWorker(callback)) { + asyncProxy.signAndEncryptMessage(publicKeys, privateKey, text, callback); + return; + } + + return execute(function() { + var msg, armored; + msg = message.fromText(text); + msg = msg.sign([privateKey]); + msg = msg.encrypt(publicKeys); + armored = armor.encode(enums.armor.message, msg.packets.write()); + return armored; + }, callback); } /** * Decrypts message * @param {module:key~Key} privateKey private key with decrypted secret key data - * @param {module:message~Message} message the message object with the encrypted data + * @param {module:message~Message} msg the message object with the encrypted data + * @param {function} callback (optional) callback(error, result) for async style * @return {(String|null)} decrypted message as as native JavaScript string * or null if no literal data found * @static */ -function decryptMessage(privateKey, message) { - message = message.decrypt(privateKey); - return message.getText(); +function decryptMessage(privateKey, msg, callback) { + if (useWorker(callback)) { + asyncProxy.decryptMessage(privateKey, msg, callback); + return; + } + + return execute(function() { + msg = msg.decrypt(privateKey); + return msg.getText(); + }, callback); } /** * Decrypts message and verifies signatures * @param {module:key~Key} privateKey private key with decrypted secret key data * @param {Array} publicKeys public keys to verify signatures - * @param {module:message~Message} message the message object with signed and encrypted data + * @param {module:message~Message} msg the message object with signed and encrypted data + * @param {function} callback (optional) callback(error, result) for async style * @return {{text: String, signatures: Array<{keyid: module:type/keyid, valid: Boolean}>}} * decrypted message as as native JavaScript string * with verified signatures or null if no literal data found * @static */ -function decryptAndVerifyMessage(privateKey, publicKeys, message) { - var result = {}; - message = message.decrypt(privateKey); - result.text = message.getText(); - if (result.text) { - result.signatures = message.verify(publicKeys); - return result; +function decryptAndVerifyMessage(privateKey, publicKeys, msg, callback) { + if (useWorker(callback)) { + asyncProxy.decryptAndVerifyMessage(privateKey, publicKeys, msg, callback); + return; } - return null; + + return execute(function() { + var result = {}; + msg = msg.decrypt(privateKey); + result.text = msg.getText(); + if (result.text) { + result.signatures = msg.verify(publicKeys); + return result; + } + return null; + }, callback); } /** * Signs a cleartext message * @param {Array} privateKeys private key with decrypted secret key data to sign cleartext * @param {String} text cleartext + * @param {function} callback (optional) callback(error, result) for async style * @return {String} ASCII armored message * @static */ -function signClearMessage(privateKeys, text) { - var cleartextMessage = new cleartext.CleartextMessage(text); - cleartextMessage.sign(privateKeys); - return cleartextMessage.armor(); +function signClearMessage(privateKeys, text, callback) { + if (useWorker(callback)) { + asyncProxy.signClearMessage(privateKeys, text, callback); + return; + } + + return execute(function() { + var cleartextMessage = new cleartext.CleartextMessage(text); + cleartextMessage.sign(privateKeys); + return cleartextMessage.armor(); + }, callback); } /** * Verifies signatures of cleartext signed message * @param {Array} publicKeys public keys to verify signatures - * @param {module:cleartext~CleartextMessage} message cleartext message object with signatures + * @param {module:cleartext~CleartextMessage} msg cleartext message object with signatures + * @param {function} callback (optional) callback(error, result) for async style * @return {{text: String, signatures: Array<{keyid: module:type/keyid, valid: Boolean}>}} * cleartext with status of verified signatures * @static */ -function verifyClearSignedMessage(publicKeys, message) { - var result = {}; - if (!(message instanceof cleartext.CleartextMessage)) { - throw new Error('Parameter [message] needs to be of type CleartextMessage.'); +function verifyClearSignedMessage(publicKeys, msg, callback) { + if (useWorker(callback)) { + asyncProxy.verifyClearSignedMessage(publicKeys, msg, callback); + return; } - result.text = message.getText(); - result.signatures = message.verify(publicKeys); - return result; + + return execute(function() { + var result = {}; + if (!(msg instanceof cleartext.CleartextMessage)) { + throw new Error('Parameter [message] needs to be of type CleartextMessage.'); + } + result.text = msg.getText(); + result.signatures = msg.verify(publicKeys); + return result; + }, callback); } /** @@ -143,18 +203,71 @@ function verifyClearSignedMessage(publicKeys, message) { * @param {Integer} numBits number of bits for the key creation. (should be 1024+, generally) * @param {String} userId assumes already in form of "User Name " * @param {String} passphrase The passphrase used to encrypt the resulting private key + * @param {function} callback (optional) callback(error, result) for async style * @return {Object} {key: Array, privateKeyArmored: Array, publicKeyArmored: Array} * @static */ -function generateKeyPair(keyType, numBits, userId, passphrase) { - var result = {}; - var newKey = key.generate(keyType, numBits, userId, passphrase); - result.key = newKey; - result.privateKeyArmored = newKey.armor(); - result.publicKeyArmored = newKey.toPublic().armor(); +function generateKeyPair(keyType, numBits, userId, passphrase, callback) { + if (useWorker(callback)) { + asyncProxy.generateKeyPair(keyType, numBits, userId, passphrase, callback); + return; + } + + return execute(function() { + var result = {}; + var newKey = key.generate(keyType, numBits, userId, passphrase); + result.key = newKey; + result.privateKeyArmored = newKey.armor(); + result.publicKeyArmored = newKey.toPublic().armor(); + return result; + }, callback); +} + +// +// helper functions +// + +/** + * Are we in a browser and do we support worker? + */ +function useWorker(callback) { + if (typeof callback === 'undefined') { + return false; + } + + if (!asyncProxy) { + throw new Error('You need to set the worker path!'); + } + + return true; +} + +/** + * Command pattern that handles async calls gracefully + */ +function execute(cmd, callback) { + var result; + + try { + result = cmd(); + } catch (err) { + if (callback) { + callback(err); + return; + } + + throw err; + } + + if (callback) { + callback(null, result); + return; + } + return result; } +exports.initWorker = initWorker; exports.encryptMessage = encryptMessage; exports.signAndEncryptMessage = signAndEncryptMessage; exports.decryptMessage = decryptMessage; @@ -162,3 +275,4 @@ exports.decryptAndVerifyMessage = decryptAndVerifyMessage; exports.signClearMessage = signClearMessage; exports.verifyClearSignedMessage = verifyClearSignedMessage; exports.generateKeyPair = generateKeyPair; +exports.AsyncProxy = AsyncProxy; diff --git a/test/worker/api.js b/test/worker/api.js index 0edc0596..845fb016 100644 --- a/test/worker/api.js +++ b/test/worker/api.js @@ -177,20 +177,17 @@ var priv_key_de = describe('High level API', function() { - var proxy; - this.timeout(0); before(function() { - proxy = new openpgp.AsyncProxy('../dist/openpgp.worker.js'); - expect(proxy).to.exist; + openpgp.initWorker('../dist/openpgp.worker.js'); initKeys(); }); describe('Encryption', function() { it('RSA: encryptMessage async', function (done) { - proxy.encryptMessage([pubKeyRSA], plaintext, function(err, data) { + openpgp.encryptMessage([pubKeyRSA], plaintext, function(err, data) { expect(err).to.not.exist; expect(data).to.exist; expect(data).to.match(/^-----BEGIN PGP MESSAGE/); @@ -209,7 +206,7 @@ describe('High level API', function() { }); it('ELG: encryptMessage async', function (done) { - proxy.encryptMessage([pubKeyDE], plaintext, function(err, data) { + openpgp.encryptMessage([pubKeyDE], plaintext, function(err, data) { expect(err).to.not.exist; expect(data).to.exist; expect(data).to.match(/^-----BEGIN PGP MESSAGE/); @@ -241,7 +238,7 @@ describe('High level API', function() { }); it('RSA: decryptMessage async', function (done) { - proxy.decryptMessage(privKeyRSA, msgRSA, function(err, data) { + openpgp.decryptMessage(privKeyRSA, msgRSA, function(err, data) { expect(err).to.not.exist; expect(data).to.exist; expect(data).to.equal(plaintext); @@ -256,7 +253,7 @@ describe('High level API', function() { }); it('ELG: decryptMessage async', function (done) { - proxy.decryptMessage(privKeyDE, msgDE, function(err, data) { + openpgp.decryptMessage(privKeyDE, msgDE, function(err, data) { expect(err).to.not.exist; expect(data).to.exist; expect(data).to.equal(plaintext); @@ -291,7 +288,7 @@ describe('High level API', function() { }); it('RSA: decryptAndVerifyMessage async', function (done) { - proxy.decryptAndVerifyMessage(privKeyRSA, [pubKeyRSA], msgRSA, function(err, data) { + openpgp.decryptAndVerifyMessage(privKeyRSA, [pubKeyRSA], msgRSA, function(err, data) { expect(err).to.not.exist; expect(data).to.exist; verifySignature(data, privKeyRSA); @@ -300,7 +297,7 @@ describe('High level API', function() { }); it('ELG: decryptAndVerifyMessage async', function (done) { - proxy.decryptAndVerifyMessage(privKeyDE, [pubKeyDE], msgDE, function(err, data) { + openpgp.decryptAndVerifyMessage(privKeyDE, [pubKeyDE], msgDE, function(err, data) { expect(err).to.not.exist; expect(data).to.exist; verifySignature(data, privKeyDE); @@ -317,7 +314,7 @@ describe('High level API', function() { }); it('RSA: signAndEncryptMessage async', function (done) { - proxy.signAndEncryptMessage([pubKeyRSA], privKeyRSA, plaintext, function(err, data) { + openpgp.signAndEncryptMessage([pubKeyRSA], privKeyRSA, plaintext, function(err, data) { expect(err).to.not.exist; expect(data).to.exist; expect(data).to.match(/^-----BEGIN PGP MESSAGE/); @@ -339,7 +336,7 @@ describe('High level API', function() { }); it('RSA: signClearMessage async', function (done) { - proxy.signClearMessage([privKeyRSA], plaintext, function(err, data) { + openpgp.signClearMessage([privKeyRSA], plaintext, function(err, data) { expect(err).to.not.exist; expect(data).to.exist; expect(data).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/); @@ -350,7 +347,7 @@ describe('High level API', function() { }); it('DSA: signClearMessage async', function (done) { - proxy.signClearMessage([privKeyDE], plaintext, function(err, data) { + openpgp.signClearMessage([privKeyDE], plaintext, function(err, data) { expect(err).to.not.exist; expect(data).to.exist; expect(data).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/); @@ -363,7 +360,7 @@ describe('High level API', function() { it('RSA: verifyClearSignedMessage async', function (done) { var signed = openpgp.signClearMessage([privKeyRSA], plaintext); signed = openpgp.cleartext.readArmored(signed); - proxy.verifyClearSignedMessage([pubKeyRSA], signed, function(err, data) { + openpgp.verifyClearSignedMessage([pubKeyRSA], signed, function(err, data) { expect(err).to.not.exist; expect(data).to.exist; verifySignature(data, privKeyRSA); @@ -378,7 +375,7 @@ describe('High level API', function() { before(initKeys); it('Signing with not decrypted key gives error', function (done) { - proxy.signClearMessage([privKeyRSA], plaintext, function(err, data) { + openpgp.signClearMessage([privKeyRSA], plaintext, function(err, data) { expect(data).to.not.exist; expect(err).to.exist; expect(err.message).to.equal('Private key is not decrypted.'); @@ -404,7 +401,7 @@ describe('High level API', function() { describe('Key generation', function() { it('Generate 1024-bit RSA/RSA key async', function (done) { - proxy.generateKeyPair(3, 1024, 'Test McTestington ', 'hello world', function(err, data) { + openpgp.generateKeyPair(3, 1024, 'Test McTestington ', 'hello world', function(err, data) { expect(err).to.not.exist; expect(data).to.exist; expect(data.publicKeyArmored).to.match(/^-----BEGIN PGP PUBLIC/); @@ -426,9 +423,10 @@ describe('High level API', function() { describe('Decrypt secret key', function() { - var msg; + var msg, proxy; beforeEach(function() { + proxy = new openpgp.AsyncProxy('../dist/openpgp.worker.js'); initKeys(); msg = openpgp.message.fromText(plaintext).encrypt([pubKeyRSA]); });