Don't hang when signature packet corresponding to one-pass sig is missing

This commit is contained in:
Daniel Huigens 2018-10-04 16:14:44 +02:00
parent 150222bee5
commit 3751731330
3 changed files with 47 additions and 6 deletions

View File

@ -545,7 +545,7 @@ Message.prototype.verify = async function(keys, date=new Date(), streaming) {
if (literalDataList.length !== 1) { if (literalDataList.length !== 1) {
throw new Error('Can only verify message with one literal data packet.'); throw new Error('Can only verify message with one literal data packet.');
} }
const onePassSigList = msg.packets.filterByTag(enums.packet.onePassSignature); const onePassSigList = msg.packets.filterByTag(enums.packet.onePassSignature).reverse();
const signatureList = msg.packets.filterByTag(enums.packet.signature); const signatureList = msg.packets.filterByTag(enums.packet.signature);
if (onePassSigList.length && !signatureList.length && msg.packets.stream) { if (onePassSigList.length && !signatureList.length && msg.packets.stream) {
onePassSigList.forEach(onePassSig => { onePassSigList.forEach(onePassSig => {
@ -555,25 +555,28 @@ Message.prototype.verify = async function(keys, date=new Date(), streaming) {
onePassSig.signatureData = stream.fromAsync(async () => (await onePassSig.correspondingSig).signatureData); onePassSig.signatureData = stream.fromAsync(async () => (await onePassSig.correspondingSig).signatureData);
onePassSig.hashed = onePassSig.hash(literalDataList[0], undefined, streaming); onePassSig.hashed = onePassSig.hash(literalDataList[0], undefined, streaming);
}); });
const verificationObjects = await createVerificationObjects(onePassSigList, literalDataList, keys, date);
msg.packets.stream = stream.transformPair(msg.packets.stream, async (readable, writable) => { msg.packets.stream = stream.transformPair(msg.packets.stream, async (readable, writable) => {
const reader = stream.getReader(readable);
const writer = stream.getWriter(writable); const writer = stream.getWriter(writable);
try { try {
await stream.readToEnd(stream.transform(readable, signature => { for (let i = 0; i < onePassSigList.length; i++) {
onePassSigList.pop().correspondingSigResolve(signature); const { value: signature } = await reader.read();
})); onePassSigList[i].correspondingSigResolve(signature);
}
await reader.readToEnd();
await writer.ready; await writer.ready;
await writer.close(); await writer.close();
} catch(e) { } catch(e) {
onePassSigList.forEach(onePassSig => { onePassSigList.forEach(onePassSig => {
onePassSig.correspondingSigResolve({ onePassSig.correspondingSigResolve({
tag: enums.packet.signature,
verify: () => undefined verify: () => undefined
}); });
}); });
await writer.abort(e); await writer.abort(e);
} }
}); });
return verificationObjects.reverse(); return createVerificationObjects(onePassSigList, literalDataList, keys, date);
} }
return createVerificationObjects(signatureList, literalDataList, keys, date); return createVerificationObjects(signatureList, literalDataList, keys, date);
}; };

View File

@ -142,6 +142,9 @@ OnePassSignature.prototype.calculateTrailer = Signature.prototype.calculateTrail
OnePassSignature.prototype.verify = async function() { OnePassSignature.prototype.verify = async function() {
const correspondingSig = await this.correspondingSig; const correspondingSig = await this.correspondingSig;
if (!correspondingSig || correspondingSig.tag !== enums.packet.signature) {
throw new Error('Corresponding signature packet missing');
}
correspondingSig.hashed = this.hashed; correspondingSig.hashed = this.hashed;
return correspondingSig.verify.apply(correspondingSig, arguments); return correspondingSig.verify.apply(correspondingSig, arguments);
}; };

View File

@ -600,6 +600,41 @@ yYDnCgA=
}); });
}); });
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
yFgBO8LLzMjE+KDlu0uOgs50xtNRJdzFBYnJqcW6JanFJVE3r9eCuVYKvFxg
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 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);
await expect(cleartextSig.signatures[0].verified).to.be.rejectedWith('Corresponding signature packet missing');
expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(0);
});
});
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() {
const plaintext = 'short message\nnext line \n한국어/조선말'; const plaintext = 'short message\nnext line \n한국어/조선말';
const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0]; const pubKey = (await openpgp.key.readArmored(pub_key_arm2)).keys[0];