Fix one-pass signature verification when using a Worker
This commit is contained in:
parent
dff2dcd9a7
commit
df8364930b
|
@ -81,13 +81,18 @@ function verificationObjectToClone(verObject) {
|
||||||
const signature = verObject.signature;
|
const signature = verObject.signature;
|
||||||
verObject.signature = stream.fromAsync(async () => {
|
verObject.signature = stream.fromAsync(async () => {
|
||||||
const packets = (await signature).packets;
|
const packets = (await signature).packets;
|
||||||
await verified;
|
try {
|
||||||
delete packets[0].signature;
|
await verified;
|
||||||
|
delete packets[0].signature;
|
||||||
|
} catch(e) {}
|
||||||
return packets;
|
return packets;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
verObject.signature = verObject.signature.packets;
|
verObject.signature = verObject.signature.packets;
|
||||||
}
|
}
|
||||||
|
if (verObject.error) {
|
||||||
|
verObject.error = verObject.error.message;
|
||||||
|
}
|
||||||
return verObject;
|
return verObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,10 +156,15 @@ function packetlistCloneToSignatures(clone) {
|
||||||
clone.keyid = type_keyid.fromClone(clone.keyid);
|
clone.keyid = type_keyid.fromClone(clone.keyid);
|
||||||
if (util.isStream(clone.signature)) {
|
if (util.isStream(clone.signature)) {
|
||||||
clone.signature = stream.readToEnd(clone.signature, ([signature]) => new Signature(List.fromStructuredClone(signature)));
|
clone.signature = stream.readToEnd(clone.signature, ([signature]) => new Signature(List.fromStructuredClone(signature)));
|
||||||
|
clone.signature.catch(() => {});
|
||||||
} else {
|
} else {
|
||||||
clone.signature = new Signature(List.fromStructuredClone(clone.signature));
|
clone.signature = new Signature(List.fromStructuredClone(clone.signature));
|
||||||
}
|
}
|
||||||
clone.verified = stream.readToEnd(clone.verified, ([verified]) => verified);
|
clone.verified = stream.readToEnd(clone.verified, ([verified]) => verified);
|
||||||
|
clone.verified.catch(() => {});
|
||||||
|
if (clone.error) {
|
||||||
|
clone.error = new Error(clone.error);
|
||||||
|
}
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,6 +223,9 @@ List.fromStructuredClone = function(packetlistClone) {
|
||||||
packet.packets = new List();
|
packet.packets = new List();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (packetlistClone.stream) {
|
||||||
|
packetlist.stream = stream.transform(packetlistClone.stream, packet => packets.fromStructuredClone(packet));
|
||||||
|
}
|
||||||
return packetlist;
|
return packetlist;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
17
src/util.js
17
src/util.js
|
@ -83,10 +83,15 @@ export default {
|
||||||
const { port1, port2 } = new MessageChannel();
|
const { port1, port2 } = new MessageChannel();
|
||||||
port1.onmessage = async function({ data: { action } }) {
|
port1.onmessage = async function({ data: { action } }) {
|
||||||
if (action === 'read') {
|
if (action === 'read') {
|
||||||
const result = await reader.read();
|
try {
|
||||||
port1.postMessage(result, util.getTransferables(result, true));
|
const result = await reader.read();
|
||||||
|
port1.postMessage(result, util.getTransferables(result));
|
||||||
|
} catch(e) {
|
||||||
|
port1.postMessage({ error: e.message });
|
||||||
|
}
|
||||||
} else if (action === 'cancel') {
|
} else if (action === 'cancel') {
|
||||||
port1.postMessage(await transformed.cancel());
|
await transformed.cancel();
|
||||||
|
port1.postMessage();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
obj[key] = port2;
|
obj[key] = port2;
|
||||||
|
@ -116,8 +121,10 @@ export default {
|
||||||
pull(controller) {
|
pull(controller) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
value.onmessage = evt => {
|
value.onmessage = evt => {
|
||||||
const { done, value } = evt.data;
|
const { done, value, error } = evt.data;
|
||||||
if (!done) {
|
if (error) {
|
||||||
|
controller.error(new Error(error));
|
||||||
|
} else if (!done) {
|
||||||
controller.enqueue(value);
|
controller.enqueue(value);
|
||||||
} else {
|
} else {
|
||||||
controller.close();
|
controller.close();
|
||||||
|
|
|
@ -113,10 +113,10 @@ function delegate(id, method, options) {
|
||||||
response({ id:id, event:'method-return', err:'Unknown Worker Event' });
|
response({ id:id, event:'method-return', err:'Unknown Worker Event' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// parse cloned packets
|
|
||||||
options = openpgp.packet.clone.parseClonedPackets(options, method);
|
|
||||||
// construct ReadableStreams from MessagePorts
|
// construct ReadableStreams from MessagePorts
|
||||||
openpgp.util.restoreStreams(options);
|
openpgp.util.restoreStreams(options);
|
||||||
|
// parse cloned packets
|
||||||
|
options = openpgp.packet.clone.parseClonedPackets(options, method);
|
||||||
openpgp[method](options).then(function(data) {
|
openpgp[method](options).then(function(data) {
|
||||||
// clone packets (for web worker structured cloning algorithm)
|
// clone packets (for web worker structured cloning algorithm)
|
||||||
response({ id:id, event:'method-return', data:openpgp.packet.clone.clonePackets(data) });
|
response({ id:id, event:'method-return', data:openpgp.packet.clone.clonePackets(data) });
|
||||||
|
|
|
@ -532,9 +532,10 @@ zmuVOdNuWQqxT9Sqa84=
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Verify signed message with trailing spaces from GPG', async function() {
|
function tests() {
|
||||||
const msg_armor =
|
it('Verify signed message with trailing spaces from GPG', async function() {
|
||||||
`-----BEGIN PGP MESSAGE-----
|
const msg_armor =
|
||||||
|
`-----BEGIN PGP MESSAGE-----
|
||||||
Version: GnuPG v1
|
Version: GnuPG v1
|
||||||
|
|
||||||
owGbwMvMyMT4oOW7S46CznTG01El3MUFicmpxbolqcUlUTev14K5Vgq8XGCGQmJe
|
owGbwMvMyMT4oOW7S46CznTG01El3MUFicmpxbolqcUlUTev14K5Vgq8XGCGQmJe
|
||||||
|
@ -546,26 +547,26 @@ yYDnCgA=
|
||||||
=15ki
|
=15ki
|
||||||
-----END PGP MESSAGE-----`;
|
-----END PGP MESSAGE-----`;
|
||||||
|
|
||||||
const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t ';
|
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 sMsg = await openpgp.message.readArmored(msg_armor);
|
||||||
const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0];
|
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) {
|
return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(function(cleartextSig) {
|
||||||
expect(cleartextSig).to.exist;
|
expect(cleartextSig).to.exist;
|
||||||
expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(cleartextSig.data))).to.equal(plaintext);
|
expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(cleartextSig.data))).to.equal(plaintext);
|
||||||
expect(cleartextSig.signatures).to.have.length(1);
|
expect(cleartextSig.signatures).to.have.length(1);
|
||||||
expect(cleartextSig.signatures[0].valid).to.be.true;
|
expect(cleartextSig.signatures[0].valid).to.be.true;
|
||||||
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
|
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('Streaming verify signed message with trailing spaces from GPG', async function() {
|
it('Streaming verify signed message with trailing spaces from GPG', async function() {
|
||||||
const msg_armor =
|
const msg_armor =
|
||||||
`-----BEGIN PGP MESSAGE-----
|
`-----BEGIN PGP MESSAGE-----
|
||||||
Version: GnuPG v1
|
Version: GnuPG v1
|
||||||
|
|
||||||
owGbwMvMyMT4oOW7S46CznTG01El3MUFicmpxbolqcUlUTev14K5Vgq8XGCGQmJe
|
owGbwMvMyMT4oOW7S46CznTG01El3MUFicmpxbolqcUlUTev14K5Vgq8XGCGQmJe
|
||||||
|
@ -577,32 +578,62 @@ yYDnCgA=
|
||||||
=15ki
|
=15ki
|
||||||
-----END PGP MESSAGE-----`.split('');
|
-----END PGP MESSAGE-----`.split('');
|
||||||
|
|
||||||
const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t ';
|
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({
|
const sMsg = await openpgp.message.readArmored(new ReadableStream({
|
||||||
async pull(controller) {
|
async pull(controller) {
|
||||||
await new Promise(setTimeout);
|
await new Promise(setTimeout);
|
||||||
controller.enqueue(msg_armor.shift());
|
controller.enqueue(msg_armor.shift());
|
||||||
if (!msg_armor.length) controller.close();
|
if (!msg_armor.length) controller.close();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0];
|
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) {
|
return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(async function(cleartextSig) {
|
||||||
expect(cleartextSig).to.exist;
|
expect(cleartextSig).to.exist;
|
||||||
expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext);
|
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).to.have.length(1);
|
||||||
expect(await cleartextSig.signatures[0].verified).to.be.true;
|
expect(await cleartextSig.signatures[0].verified).to.be.true;
|
||||||
expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(1);
|
expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('Streaming verify signed message with missing signature packet', async function() {
|
it('Verify signed message with missing signature packet', async function() {
|
||||||
const msg_armor =
|
const msg_armor =
|
||||||
`-----BEGIN PGP MESSAGE-----
|
`-----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
|
Version: OpenPGP.js v3.1.3
|
||||||
Comment: https://openpgpjs.org
|
Comment: https://openpgpjs.org
|
||||||
|
|
||||||
|
@ -612,27 +643,40 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA
|
||||||
=D6TZ
|
=D6TZ
|
||||||
-----END PGP MESSAGE-----`.split('');
|
-----END PGP MESSAGE-----`.split('');
|
||||||
|
|
||||||
const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t ';
|
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({
|
const sMsg = await openpgp.message.readArmored(new ReadableStream({
|
||||||
async pull(controller) {
|
async pull(controller) {
|
||||||
await new Promise(setTimeout);
|
await new Promise(setTimeout);
|
||||||
controller.enqueue(msg_armor.shift());
|
controller.enqueue(msg_armor.shift());
|
||||||
if (!msg_armor.length) controller.close();
|
if (!msg_armor.length) controller.close();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0];
|
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) {
|
return openpgp.verify({ publicKeys: [pubKey], message: sMsg }).then(async function(cleartextSig) {
|
||||||
expect(cleartextSig).to.exist;
|
expect(cleartextSig).to.exist;
|
||||||
expect(openpgp.util.nativeEOL(openpgp.util.Uint8Array_to_str(await openpgp.stream.readToEnd(cleartextSig.data)))).to.equal(plaintext);
|
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).to.have.length(1);
|
||||||
await expect(cleartextSig.signatures[0].verified).to.be.rejectedWith('Corresponding signature packet missing');
|
await expect(cleartextSig.signatures[0].verified).to.be.rejectedWith('Corresponding signature packet missing');
|
||||||
expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(0);
|
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() {
|
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures', async function() {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user