diff --git a/src/packet/clone.js b/src/packet/clone.js index 70e5a848..4db62583 100644 --- a/src/packet/clone.js +++ b/src/packet/clone.js @@ -81,13 +81,18 @@ function verificationObjectToClone(verObject) { const signature = verObject.signature; verObject.signature = stream.fromAsync(async () => { const packets = (await signature).packets; - await verified; - delete packets[0].signature; + try { + await verified; + delete packets[0].signature; + } catch(e) {} return packets; }); } else { verObject.signature = verObject.signature.packets; } + if (verObject.error) { + verObject.error = verObject.error.message; + } return verObject; } @@ -151,10 +156,15 @@ function packetlistCloneToSignatures(clone) { clone.keyid = type_keyid.fromClone(clone.keyid); if (util.isStream(clone.signature)) { clone.signature = stream.readToEnd(clone.signature, ([signature]) => new Signature(List.fromStructuredClone(signature))); + clone.signature.catch(() => {}); } else { clone.signature = new Signature(List.fromStructuredClone(clone.signature)); } clone.verified = stream.readToEnd(clone.verified, ([verified]) => verified); + clone.verified.catch(() => {}); + if (clone.error) { + clone.error = new Error(clone.error); + } return clone; } diff --git a/src/packet/packetlist.js b/src/packet/packetlist.js index 3df4e288..d2b3e0f3 100644 --- a/src/packet/packetlist.js +++ b/src/packet/packetlist.js @@ -223,6 +223,9 @@ List.fromStructuredClone = function(packetlistClone) { packet.packets = new List(); } } + if (packetlistClone.stream) { + packetlist.stream = stream.transform(packetlistClone.stream, packet => packets.fromStructuredClone(packet)); + } return packetlist; }; diff --git a/src/util.js b/src/util.js index 2ff15677..6855b47f 100644 --- a/src/util.js +++ b/src/util.js @@ -83,10 +83,15 @@ export default { const { port1, port2 } = new MessageChannel(); port1.onmessage = async function({ data: { action } }) { if (action === 'read') { - const result = await reader.read(); - port1.postMessage(result, util.getTransferables(result, true)); + try { + const result = await reader.read(); + port1.postMessage(result, util.getTransferables(result)); + } catch(e) { + port1.postMessage({ error: e.message }); + } } else if (action === 'cancel') { - port1.postMessage(await transformed.cancel()); + await transformed.cancel(); + port1.postMessage(); } }; obj[key] = port2; @@ -116,8 +121,10 @@ export default { pull(controller) { return new Promise(resolve => { value.onmessage = evt => { - const { done, value } = evt.data; - if (!done) { + const { done, value, error } = evt.data; + if (error) { + controller.error(new Error(error)); + } else if (!done) { controller.enqueue(value); } else { controller.close(); diff --git a/src/worker/worker.js b/src/worker/worker.js index a9281372..c5756481 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -113,10 +113,10 @@ function delegate(id, method, options) { response({ id:id, event:'method-return', err:'Unknown Worker Event' }); return; } - // parse cloned packets - options = openpgp.packet.clone.parseClonedPackets(options, method); // construct ReadableStreams from MessagePorts openpgp.util.restoreStreams(options); + // parse cloned packets + options = openpgp.packet.clone.parseClonedPackets(options, method); openpgp[method](options).then(function(data) { // clone packets (for web worker structured cloning algorithm) response({ id:id, event:'method-return', data:openpgp.packet.clone.clonePackets(data) }); diff --git a/test/general/signature.js b/test/general/signature.js index 14e67a8c..a45deb8d 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -532,9 +532,10 @@ zmuVOdNuWQqxT9Sqa84= }); }); - it('Verify signed message with trailing spaces from GPG', async function() { - const msg_armor = - `-----BEGIN PGP MESSAGE----- + function tests() { + it('Verify signed message with trailing spaces from GPG', async function() { + const msg_armor = + `-----BEGIN PGP MESSAGE----- Version: GnuPG v1 owGbwMvMyMT4oOW7S46CznTG01El3MUFicmpxbolqcUlUTev14K5Vgq8XGCGQmJe @@ -546,26 +547,26 @@ yYDnCgA= =15ki -----END PGP MESSAGE-----`; - const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; - const sMsg = await openpgp.message.readArmored(msg_armor); - const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0]; + const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; + const sMsg = await openpgp.message.readArmored(msg_armor); + const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0]; - const keyids = sMsg.getSigningKeyIds(); + const keyids = sMsg.getSigningKeyIds(); - expect(pubKey.getKeys(keyids[0])).to.not.be.empty; + expect(pubKey.getKeys(keyids[0])).to.not.be.empty; - return openpgp.verify({ publicKeys:[pubKey], message:sMsg }).then(function(cleartextSig) { - expect(cleartextSig).to.exist; - expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(cleartextSig.data))).to.equal(plaintext); - expect(cleartextSig.signatures).to.have.length(1); - expect(cleartextSig.signatures[0].valid).to.be.true; - expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); + return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(function(cleartextSig) { + expect(cleartextSig).to.exist; + expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(cleartextSig.data))).to.equal(plaintext); + expect(cleartextSig.signatures).to.have.length(1); + expect(cleartextSig.signatures[0].valid).to.be.true; + expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); + }); }); - }); - it('Streaming verify signed message with trailing spaces from GPG', async function() { - const msg_armor = - `-----BEGIN PGP MESSAGE----- + it('Streaming verify signed message with trailing spaces from GPG', async function() { + const msg_armor = + `-----BEGIN PGP MESSAGE----- Version: GnuPG v1 owGbwMvMyMT4oOW7S46CznTG01El3MUFicmpxbolqcUlUTev14K5Vgq8XGCGQmJe @@ -577,32 +578,62 @@ yYDnCgA= =15ki -----END PGP MESSAGE-----`.split(''); - const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; - const sMsg = await openpgp.message.readArmored(new ReadableStream({ - async pull(controller) { - await new Promise(setTimeout); - controller.enqueue(msg_armor.shift()); - if (!msg_armor.length) controller.close(); - } - })); - const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0]; + const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; + const sMsg = await openpgp.message.readArmored(new ReadableStream({ + async pull(controller) { + await new Promise(setTimeout); + controller.enqueue(msg_armor.shift()); + if (!msg_armor.length) controller.close(); + } + })); + const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0]; - const keyids = sMsg.getSigningKeyIds(); + const keyids = sMsg.getSigningKeyIds(); - expect(pubKey.getKeys(keyids[0])).to.not.be.empty; + expect(pubKey.getKeys(keyids[0])).to.not.be.empty; - return openpgp.verify({ publicKeys:[pubKey], message:sMsg }).then(async function(cleartextSig) { - expect(cleartextSig).to.exist; - expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext); - expect(cleartextSig.signatures).to.have.length(1); - expect(await cleartextSig.signatures[0].verified).to.be.true; - expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(1); + return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(async function(cleartextSig) { + expect(cleartextSig).to.exist; + expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext); + expect(cleartextSig.signatures).to.have.length(1); + expect(await cleartextSig.signatures[0].verified).to.be.true; + expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(1); + }); }); - }); - it('Streaming verify signed message with missing signature packet', async function() { - const msg_armor = - `-----BEGIN PGP MESSAGE----- + it('Verify signed message with missing signature packet', async function() { + const msg_armor = + `-----BEGIN PGP MESSAGE----- +Version: OpenPGP.js v3.1.3 +Comment: https://openpgpjs.org + +yFgBO8LLzMjE+KDlu0uOgs50xtNRJdzFBYnJqcW6JanFJVE3r9eCuVYKvFxg +hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA + +=D6TZ +-----END PGP MESSAGE-----`; + + const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; + const sMsg = await openpgp.message.readArmored(msg_armor); + const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0]; + + const keyids = sMsg.getSigningKeyIds(); + + expect(pubKey.getKeys(keyids[0])).to.not.be.empty; + + return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(async function(cleartextSig) { + expect(cleartextSig).to.exist; + expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext); + expect(cleartextSig.signatures).to.have.length(1); + expect(cleartextSig.signatures[0].valid).to.be.null; + expect(cleartextSig.signatures[0].error.message).to.equal('Corresponding signature packet missing'); + expect(cleartextSig.signatures[0].signature.packets.length).to.equal(0); + }); + }); + + it('Streaming verify signed message with missing signature packet', async function() { + const msg_armor = + `-----BEGIN PGP MESSAGE----- Version: OpenPGP.js v3.1.3 Comment: https://openpgpjs.org @@ -612,27 +643,40 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA =D6TZ -----END PGP MESSAGE-----`.split(''); - const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; - const sMsg = await openpgp.message.readArmored(new ReadableStream({ - async pull(controller) { - await new Promise(setTimeout); - controller.enqueue(msg_armor.shift()); - if (!msg_armor.length) controller.close(); - } - })); - const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0]; + const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; + const sMsg = await openpgp.message.readArmored(new ReadableStream({ + async pull(controller) { + await new Promise(setTimeout); + controller.enqueue(msg_armor.shift()); + if (!msg_armor.length) controller.close(); + } + })); + const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0]; - const keyids = sMsg.getSigningKeyIds(); + const keyids = sMsg.getSigningKeyIds(); - expect(pubKey.getKeys(keyids[0])).to.not.be.empty; + expect(pubKey.getKeys(keyids[0])).to.not.be.empty; - return openpgp.verify({ publicKeys:[pubKey], message:sMsg }).then(async function(cleartextSig) { - expect(cleartextSig).to.exist; - expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext); - expect(cleartextSig.signatures).to.have.length(1); - await expect(cleartextSig.signatures[0].verified).to.be.rejectedWith('Corresponding signature packet missing'); - expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(0); + return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(async function(cleartextSig) { + expect(cleartextSig).to.exist; + expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext); + expect(cleartextSig.signatures).to.have.length(1); + await expect(cleartextSig.signatures[0].verified).to.be.rejectedWith('Corresponding signature packet missing'); + expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(0); + }); }); + } + + tests(); + + tryTests('With Worker', tests, { + if: typeof window !== 'undefined' && window.Worker, + before: async function() { + await openpgp.initWorker({ path: '../dist/openpgp.worker.js' }); + }, + after: function() { + openpgp.destroyWorker(); + } }); it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures', async function() {