Merge pull request #894 from twiss/dont-throw-verification-errors-workers

Fix one-pass signature verification when using a Worker
This commit is contained in:
Daniel Huigens 2019-05-05 00:01:14 +02:00 committed by GitHub
commit ee01883a52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 129 additions and 65 deletions

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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();

View File

@ -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) });

View File

@ -689,9 +689,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
@ -703,26 +704,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
@ -734,32 +735,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
@ -769,27 +800,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() {