diff --git a/src/key/key.js b/src/key/key.js index 3bce4b78..32fa2927 100644 --- a/src/key/key.js +++ b/src/key/key.js @@ -158,6 +158,21 @@ Key.prototype.toPacketlist = function() { return packetlist; }; +/** + * Clones the key object + * @param {type/keyid} deep Whether to clone each packet, in addition to the list of packets + * @returns {Promise} cloned key + * @async + */ +Key.prototype.clone = async function(deep = false) { + if (deep) { + const packetlist = new packet.List(); + await packetlist.read(this.toPacketlist().write()); + return new Key(packetlist); + } + return new Key(this.toPacketlist()); +}; + /** * Returns an array containing all public or private subkeys matching keyId; * If keyId is not present, returns all subkeys. @@ -713,7 +728,7 @@ Key.prototype.revoke = async function({ throw new Error('Need private key for revoking'); } const dataToSign = { key: this.keyPacket }; - const key = new Key(this.toPacketlist()); + const key = await this.clone(); key.revocationSignatures.push(await helper.createSignaturePacket(dataToSign, null, this.keyPacket, { signatureType: enums.signature.key_revocation, reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag), @@ -764,7 +779,7 @@ Key.prototype.applyRevocationCertificate = async function(revocationCertificate) } catch (e) { throw util.wrapError('Could not verify revocation signature', e); } - const key = new Key(this.toPacketlist()); + const key = await this.clone(); key.revocationSignatures.push(revocationSignature); return key; }; @@ -780,7 +795,7 @@ Key.prototype.applyRevocationCertificate = async function(revocationCertificate) Key.prototype.signPrimaryUser = async function(privateKeys, date, userId) { const { index, user } = await this.getPrimaryUser(date, userId); const userSign = await user.sign(this.keyPacket, privateKeys); - const key = new Key(this.toPacketlist()); + const key = await this.clone(); key.users[index] = userSign; return key; }; @@ -793,7 +808,7 @@ Key.prototype.signPrimaryUser = async function(privateKeys, date, userId) { */ Key.prototype.signAllUsers = async function(privateKeys) { const that = this; - const key = new Key(this.toPacketlist()); + const key = await this.clone(); key.users = await Promise.all(this.users.map(function(user) { return user.sign(that.keyPacket, privateKeys); })); diff --git a/src/openpgp.js b/src/openpgp.js index 8d0002fb..63961c99 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -242,15 +242,15 @@ export function revokeKey({ * @returns {Promise} the unlocked key object in the form: { key:Key } * @async */ -export function decryptKey({ privateKey, passphrase }) { +export function decryptKey({ privateKey, passphrase }, fromWorker = false) { if (asyncProxy) { // use web worker if available return asyncProxy.delegate('decryptKey', { privateKey, passphrase }); } return Promise.resolve().then(async function() { - await privateKey.decrypt(passphrase); - - return privateKey; + const key = fromWorker ? privateKey : await privateKey.clone(true); + await key.decrypt(passphrase); + return key; }).catch(onError.bind(null, 'Error decrypting private key')); } @@ -261,16 +261,16 @@ export function decryptKey({ privateKey, passphrase }) { * @returns {Promise} the locked key object in the form: { key:Key } * @async */ -export function encryptKey({ privateKey, passphrase }) { +export function encryptKey({ privateKey, passphrase }, fromWorker = false) { if (asyncProxy) { // use web worker if available return asyncProxy.delegate('encryptKey', { privateKey, passphrase }); } return Promise.resolve().then(async function() { - await privateKey.encrypt(passphrase); - - return privateKey; - }).catch(onError.bind(null, 'Error decrypting private key')); + const key = fromWorker ? privateKey : await privateKey.clone(true); + await key.encrypt(passphrase); + return key; + }).catch(onError.bind(null, 'Error encrypting private key')); } diff --git a/src/worker/worker.js b/src/worker/worker.js index 965db429..700c7cfd 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -143,7 +143,7 @@ function delegate(id, method, options) { if (options.privateKeys) { options.privateKeys = options.privateKeys.map(getCachedKey); } - openpgp[method](options).then(function(data) { + openpgp[method](options, true).then(function(data) { // clone packets (for web worker structured cloning algorithm) response({ id:id, event:'method-return', data:openpgp.packet.clone.clonePackets(data) }); }).catch(function(e) { diff --git a/test/general/openpgp.js b/test/general/openpgp.js index 52f0238f..3701a25c 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -874,6 +874,7 @@ describe('OpenPGP.js public api tests', function() { }).then(function(unlocked){ expect(unlocked.getKeyId().toHex()).to.equal(privateKey.keys[0].getKeyId().toHex()); expect(unlocked.isDecrypted()).to.be.true; + expect(privateKey.keys[0].isDecrypted()).to.be.false; }); });