From 37213e165498c6702795e09df9e0d4b28628be81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Obernd=C3=B6rfer?= Date: Thu, 16 Jan 2014 16:44:05 +0100 Subject: [PATCH] Web worker: add decryptKey and decryptKeyPacket methods to proxy --- src/worker/async_proxy.js | 46 +++++++++++++++++++++++++++++++ src/worker/worker.js | 32 +++++++++++++++++++++- test/worker/api.js | 57 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/worker/async_proxy.js b/src/worker/async_proxy.js index bad2b422..6a8212a4 100644 --- a/src/worker/async_proxy.js +++ b/src/worker/async_proxy.js @@ -306,4 +306,50 @@ AsyncProxy.prototype.generateKeyPair = function(keyType, numBits, userId, passph }); }; +/** + * Decrypts secret part of all secret key packets of key. + * @param {module:key~Key} privateKey private key with encrypted secret key data + * @param {String} password password to unlock the key + * @param {Function} callback receives decrypted key + */ +AsyncProxy.prototype.decryptKey = function(privateKey, password, callback) { + privateKey = privateKey.toPacketlist(); + this.worker.postMessage({ + event: 'decrypt-key', + privateKey: privateKey, + password: password + }); + this.tasks.push(function(err, data) { + if (data) { + var packetlist = packet.List.fromStructuredClone(data); + data = new key.Key(packetlist); + } + callback(err, data); + }); +}; + +/** + * Decrypts secret part of key packets matching array of keyids. + * @param {module:key~Key} privateKey private key with encrypted secret key data + * @param {Array} keyIds + * @param {String} password password to unlock the key + * @param {Function} callback receives decrypted key + */ +AsyncProxy.prototype.decryptKeyPacket = function(privateKey, keyIds, password, callback) { + privateKey = privateKey.toPacketlist(); + this.worker.postMessage({ + event: 'decrypt-key-packet', + privateKey: privateKey, + keyIds: keyIds, + password: password + }); + this.tasks.push(function(err, data) { + if (data) { + var packetlist = packet.List.fromStructuredClone(data); + data = new key.Key(packetlist); + } + callback(err, data); + }); +}; + module.exports = AsyncProxy; diff --git a/src/worker/worker.js b/src/worker/worker.js index 664dc4e3..2938ec3a 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -27,7 +27,8 @@ window.openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER); onmessage = function (event) { var data = null, err = null, - msg = event.data; + msg = event.data, + correct = false; if (msg.seed) { window.openpgp.crypto.random.randomBuffer.set(msg.seed); } @@ -104,6 +105,35 @@ onmessage = function (event) { } response({event: 'method-return', data: data, err: err}); break; + case 'decrypt-key': + try { + msg.privateKey = packetlistCloneToKey(msg.privateKey); + correct = msg.privateKey.decrypt(msg.password); + if (correct) { + data = msg.privateKey.toPacketlist(); + } else { + err = 'Wrong password'; + } + } catch (e) { + err = e.message; + } + response({event: 'method-return', data: data, err: err}); + break; + case 'decrypt-key-packet': + try { + msg.privateKey = packetlistCloneToKey(msg.privateKey); + msg.keyIds = msg.keyIds.map(window.openpgp.Keyid.fromClone); + correct = msg.privateKey.decryptKeyPacket(msg.keyIds, msg.password); + if (correct) { + data = msg.privateKey.toPacketlist(); + } else { + err = 'Wrong password'; + } + } catch (e) { + err = e.message; + } + response({event: 'method-return', data: data, err: err}); + break; default: throw new Error('Unknown Worker Event.'); } diff --git a/test/worker/api.js b/test/worker/api.js index 15dd6d57..30e764e5 100644 --- a/test/worker/api.js +++ b/test/worker/api.js @@ -411,6 +411,63 @@ describe('High level API', function() { }); + describe('Decrypt secret key', function() { + + var msg; + + beforeEach(function() { + initKeys(); + msg = openpgp.message.fromText(plaintext).encrypt([pubKeyRSA]); + }); + + it('Decrypt key', function (done) { + expect(privKeyRSA.primaryKey.isDecrypted).to.be.false; + expect(privKeyRSA.subKeys[0].subKey.isDecrypted).to.be.false; + proxy.decryptKey(privKeyRSA, 'hello world', function(err, data) { + expect(err).to.not.exist; + expect(data).to.exist; + expect(data).to.be.an.instanceof(openpgp.key.Key); + expect(data.primaryKey.isDecrypted).to.be.true; + expect(data.subKeys[0].subKey.isDecrypted).to.be.true; + var text = openpgp.decryptMessage(data, msg); + expect(text).to.equal(plaintext); + done(); + }); + }); + + it('Decrypt key packet', function (done) { + expect(privKeyRSA.primaryKey.isDecrypted).to.be.false; + expect(privKeyRSA.subKeys[0].subKey.isDecrypted).to.be.false; + var keyid = privKeyRSA.subKeys[0].subKey.getKeyId(); + proxy.decryptKeyPacket(privKeyRSA, [keyid], 'hello world', function(err, data) { + expect(err).to.not.exist; + expect(data).to.exist; + expect(data).to.be.an.instanceof(openpgp.key.Key); + expect(data.primaryKey.isDecrypted).to.be.false; + expect(data.subKeys[0].subKey.isDecrypted).to.be.true; + var text = openpgp.decryptMessage(data, msg); + expect(text).to.equal(plaintext); + done(); + }); + }); + + it('Error on wrong password decryptKey', function (done) { + proxy.decryptKey(privKeyRSA, 'what?', function(err, data) { + expect(err).to.eql(new Error('Wrong password')); + done(); + }); + }); + + it('Error on wrong password decryptKeyPacket', function (done) { + var keyid = privKeyRSA.subKeys[0].subKey.getKeyId(); + proxy.decryptKeyPacket(privKeyRSA, [keyid], 'what?', function(err, data) { + expect(err).to.eql(new Error('Wrong password')); + done(); + }); + }); + + }); + }); describe('Random Buffer', function() {