diff --git a/src/packet/clone.js b/src/packet/clone.js new file mode 100644 index 00000000..0fefe29f --- /dev/null +++ b/src/packet/clone.js @@ -0,0 +1,117 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015 Tankred Hase +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @fileoverview This module implements packet list cloning required to + * pass certain object types beteen the web worker and main thread using + * the structured cloning algorithm. + */ + +'use strict'; + +import * as key from '../key.js'; +import * as message from '../message.js'; +import * as cleartext from '../cleartext.js'; +import Packetlist from './packetlist.js'; +import type_keyid from '../type/keyid.js'; + + +////////////////////////////// +// // +// Packetlist --> Clone // +// // +////////////////////////////// + + +/** + * Create a packetlist from the correspoding object types. + * @param {Object} options the object passed to and from the web worker + * @return {Object} a mutated version of the options optject + */ +export function clonePackets(options) { + if(options.publicKeys) { + options.publicKeys = options.publicKeys.map(key => key.toPacketlist()); + } + if(options.privateKeys) { + options.privateKeys = options.privateKeys.map(key => key.toPacketlist()); + } + if(options.privateKey) { + options.privateKey = options.privateKey.toPacketlist(); + } + if (options.key) { + options.key = options.key.toPacketlist(); + } + return options; +} + + +////////////////////////////// +// // +// Clone --> Packetlist // +// // +////////////////////////////// + + +/** + * Creates an object with the correct prototype from a corresponding packetlist. + * @param {Object} options the object passed to and from the web worker + * @param {String} method the public api function name to be delegated to the worker + * @return {Object} a mutated version of the options optject + */ +export function parseClonedPackets(options, method) { + if(options.publicKeys) { + options.publicKeys = options.publicKeys.map(packetlistCloneToKey); + } + if(options.privateKeys) { + options.privateKeys = options.privateKeys.map(packetlistCloneToKey); + } + if(options.privateKey) { + options.privateKey = packetlistCloneToKey(options.privateKey); + } + if (options.key) { + options.key = packetlistCloneToKey(options.key); + } + if (options.message && (method === 'sign' || method === 'verify')) { // sign and verify support only CleartextMessage + options.message = packetlistCloneToCleartextMessage(options.message); + } else if (options.message) { + options.message = packetlistCloneToMessage(options.message); + } + if (options.signatures) { + options.signatures = options.signatures.map(packetlistCloneToSignature); + } + return options; +} + +function packetlistCloneToKey(clone) { + const packetlist = Packetlist.fromStructuredClone(clone); + return new key.Key(packetlist); +} + +function packetlistCloneToMessage(clone) { + const packetlist = Packetlist.fromStructuredClone(clone.packets); + return new message.Message(packetlist); +} + +function packetlistCloneToCleartextMessage(clone) { + var packetlist = Packetlist.fromStructuredClone(clone.packets); + return new cleartext.CleartextMessage(clone.text, packetlist); +} + +function packetlistCloneToSignature(clone) { + clone.keyid = type_keyid.fromClone(clone.keyid); + return clone; +} diff --git a/src/packet/index.js b/src/packet/index.js index 552716ff..629a4b7f 100644 --- a/src/packet/index.js +++ b/src/packet/index.js @@ -1,14 +1,14 @@ 'use strict'; import * as packets from './all_packets.js'; +import * as clone from './clone.js'; import List from './packetlist.js'; const mod = { - /** - * @name module:packet.List - * @see module:packet/packetlist - */ - List: List + /** @see module:packet/packetlist */ + List: List, + /** @see module:packet/clone */ + clone: clone }; for (let i in packets) { diff --git a/src/worker/async_proxy.js b/src/worker/async_proxy.js index d989fa61..6b9ad047 100644 --- a/src/worker/async_proxy.js +++ b/src/worker/async_proxy.js @@ -15,24 +15,11 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -/** - * @requires crypto - * @requires enums - * @requires packet - * @requires type_keyid - * @requires key - * @module async_proxy - */ - 'use strict'; import util from '../util.js'; import crypto from '../crypto'; import packet from '../packet'; -import * as key from '../key.js'; -import * as message from '../message.js'; -import * as cleartext from '../cleartext.js'; -import type_keyid from '../type/keyid.js'; const INITIAL_RANDOM_SEED = 50000, // random bytes seeded to worker RANDOM_SEED_REQUEST = 20000; // random bytes seeded after worker request @@ -59,19 +46,6 @@ export default function AsyncProxy({ path='openpgp.worker.js', worker, config } } } -/** - * Command pattern that wraps synchronous code into a promise - * @param {function} cmd The synchronous function with a return value - * to be wrapped in a promise - * @return {Promise} The promise wrapped around cmd - */ -AsyncProxy.prototype.execute = function(cmd) { - return new Promise((resolve, reject) => { - cmd(); - this.tasks.push({ resolve, reject }); - }); -}; - /** * Message handling */ @@ -125,111 +99,18 @@ AsyncProxy.prototype.terminate = function() { this.worker.terminate(); }; - -////////////////////////////////////////////////////////////////////////////////////////////////// -// // -// Proxy functions. See the corresponding code in the openpgp module for the documentation. // -// // -////////////////////////////////////////////////////////////////////////////////////////////////// - - +/** + * Generic proxy function that handles all commands from the public api. + * @param {String} method the public api function to be delegated to the worker thread + * @param {Object} options the api function's options + * @return {Promise} see the corresponding public api functions for their return types + */ AsyncProxy.prototype.delegate = function(method, options) { return new Promise((resolve, reject) => { // clone packets (for web worker structured cloning algorithm) - this.worker.postMessage({ event:method, options:clonePackets(options) }, util.getTransferables.call(util, options)); + this.worker.postMessage({ event:method, options:packet.clone.clonePackets(options) }, util.getTransferables.call(util, options)); // remember to handle parsing cloned packets from worker - this.tasks.push({ resolve: data => resolve(parseClonedPackets(data, method)), reject }); + this.tasks.push({ resolve: data => resolve(packet.clone.parseClonedPackets(data, method)), reject }); }); }; - -function clonePackets(options) { - if(options.publicKeys) { - options.publicKeys = options.publicKeys.map(key => key.toPacketlist()); - } - if(options.privateKeys) { - options.privateKeys = options.privateKeys.map(key => key.toPacketlist()); - } - if(options.privateKey) { - options.privateKey = options.privateKey.toPacketlist(); - } - return options; -} - -function parseClonedPackets(data, method) { - if (data.key) { - data.key = packetlistCloneToKey(data.key); - } - if (data.message && method === 'sign') { // sign supports only CleartextMessage - data.message = packetlistCloneToCleartextMessage(data.message); - } else if (data.message) { - data.message = packetlistCloneToMessage(data.message); - } - if (data.signatures) { - data.signatures = data.signatures.map(packetlistCloneToSignature); - } - return data; -} - -function packetlistCloneToKey(clone) { - const packetlist = packet.List.fromStructuredClone(clone); - return new key.Key(packetlist); -} - -function packetlistCloneToMessage(clone) { - const packetlist = packet.List.fromStructuredClone(clone.packets); - return new message.Message(packetlist); -} - -function packetlistCloneToCleartextMessage(clone) { - var packetlist = packet.List.fromStructuredClone(clone.packets); - return new cleartext.CleartextMessage(clone.text, packetlist); -} - -function packetlistCloneToSignature(clone) { - clone.keyid = type_keyid.fromClone(clone.keyid); - return clone; -} - -AsyncProxy.prototype.decryptKey = function(privateKey, password) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - privateKey = privateKey.toPacketlist(); - self.worker.postMessage({ - event: 'decrypt-key', - privateKey: privateKey, - password: password - }); - - self.tasks.push({ resolve:function(data) { - var packetlist = packet.List.fromStructuredClone(data); - data = new key.Key(packetlist); - resolve(data); - }, reject:reject }); - }); - - return promise; -}; - -AsyncProxy.prototype.decryptKeyPacket = function(privateKey, keyIds, password) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - privateKey = privateKey.toPacketlist(); - self.worker.postMessage({ - event: 'decrypt-key-packet', - privateKey: privateKey, - keyIds: keyIds, - password: password - }); - - self.tasks.push({ resolve:function(data) { - var packetlist = packet.List.fromStructuredClone(data); - data = new key.Key(packetlist); - resolve(data); - }, reject:reject }); - }); - - return promise; -}; diff --git a/src/worker/worker.js b/src/worker/worker.js index 9e136e39..205f365b 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -42,23 +42,21 @@ if (!Function.prototype.bind) { } importScripts('openpgp.js'); +var openpgp = window.openpgp; var MIN_SIZE_RANDOM_BUFFER = 40000; var MAX_SIZE_RANDOM_BUFFER = 60000; -window.openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER); +openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER); self.onmessage = function (event) { - var data = null, - err = null, - msg = event.data, - opt = msg.options || {}, - correct = false; + var msg = event.data, + options = msg.options || {}; switch (msg.event) { case 'configure': for (var i in msg.config) { - window.openpgp.config[i] = msg.config[i]; + openpgp.config[i] = msg.config[i]; } break; @@ -66,41 +64,11 @@ self.onmessage = function (event) { if (!(msg.buf instanceof Uint8Array)) { msg.buf = new Uint8Array(msg.buf); } - window.openpgp.crypto.random.randomBuffer.set(msg.buf); - 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}); + openpgp.crypto.random.randomBuffer.set(msg.buf); break; case 'generateKey': + case 'decryptKey': case 'encrypt': case 'decrypt': case 'sign': @@ -108,9 +76,9 @@ self.onmessage = function (event) { case 'encryptSessionKey': case 'decryptSessionKey': // parse cloned packets - window.openpgp[msg.event](parseClonedPackets(opt, msg.event)).then(function(data) { + openpgp[msg.event](openpgp.packet.clone.parseClonedPackets(options, msg.event)).then(function(data) { // clone packets (for web worker structured cloning algorithm) - response({ event:'method-return', data:clonePackets(data) }); + response({ event:'method-return', data:openpgp.packet.clone.clonePackets(data) }); }).catch(function(e) { response({ event:'method-return', err:e.message }); }); @@ -121,49 +89,9 @@ self.onmessage = function (event) { } }; -function parseClonedPackets(options, method) { - if(options.publicKeys) { - options.publicKeys = options.publicKeys.map(packetlistCloneToKey); - } - if(options.privateKeys) { - options.privateKeys = options.privateKeys.map(packetlistCloneToKey); - } - if(options.privateKey) { - options.privateKey = packetlistCloneToKey(options.privateKey); - } - if (options.message && method === 'verify') { // verify supports only CleartextMessage - options.message = packetlistCloneToCleartextMessage(options.message); - } else if (options.message) { - options.message = packetlistCloneToMessage(options.message); - } - return options; -} - -function packetlistCloneToKey(clone) { - var packetlist = window.openpgp.packet.List.fromStructuredClone(clone); - return new window.openpgp.key.Key(packetlist); -} - -function packetlistCloneToMessage(clone) { - var packetlist = window.openpgp.packet.List.fromStructuredClone(clone.packets); - return new window.openpgp.message.Message(packetlist); -} - -function packetlistCloneToCleartextMessage(clone) { - var packetlist = window.openpgp.packet.List.fromStructuredClone(clone.packets); - return new window.openpgp.cleartext.CleartextMessage(clone.text, packetlist); -} - -function clonePackets(data) { - if (data.key) { - data.key = data.key.toPacketlist(); - } - return data; -} - function response(event) { - if (window.openpgp.crypto.random.randomBuffer.size < MIN_SIZE_RANDOM_BUFFER) { + if (openpgp.crypto.random.randomBuffer.size < MIN_SIZE_RANDOM_BUFFER) { self.postMessage({event: 'request-seed'}); } - self.postMessage(event, window.openpgp.util.getTransferables.call(window.openpgp.util, event.data)); + self.postMessage(event, openpgp.util.getTransferables.call(openpgp.util, event.data)); } \ No newline at end of file