From 9bdeaa927ace1ef32ce025140651966e2e7751eb Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Wed, 15 Jan 2020 19:27:05 +0100 Subject: [PATCH] Don't keep entire decrypted message in memory while streaming (When config.allow_unauthenticated_stream is set or the message is AEAD-encrypted.) The issue was that, when hashing the data for verification, we would only start hashing at the very end (and keep the message in memory) because nobody was "pulling" the stream containing the hash yet, so backpressure was keeping the data from being hashed. Note that, of the two patches in this commit, only the onePassSig.hashed property actually mattered, for some reason. Also, the minimum highWaterMark of 1 should have pulled the hashed stream anyway, I think. I'm not sure why that didn't happen. --- src/encoding/armor.js | 4 ++-- src/message.js | 2 +- src/packet/clone.js | 1 + src/packet/signature.js | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/encoding/armor.js b/src/encoding/armor.js index 81490f07..2886017d 100644 --- a/src/encoding/armor.js +++ b/src/encoding/armor.js @@ -312,13 +312,13 @@ function dearmor(input) { } })); data = stream.transformPair(data, async (readable, writable) => { - const checksumVerified = getCheckSum(stream.passiveClone(readable)); + const checksumVerified = stream.readToEnd(getCheckSum(stream.passiveClone(readable))); await stream.pipe(readable, writable, { preventClose: true }); const writer = stream.getWriter(writable); try { - const checksumVerifiedString = await stream.readToEnd(checksumVerified); + const checksumVerifiedString = await checksumVerified; if (checksum !== checksumVerifiedString && (checksum || config.checksum_required)) { throw new Error("Ascii armor integrity check on message failed: '" + checksum + "' should be '" + checksumVerifiedString + "'"); diff --git a/src/message.js b/src/message.js index 69cd5637..7dcba917 100644 --- a/src/message.js +++ b/src/message.js @@ -585,7 +585,7 @@ Message.prototype.verify = async function(keys, date = new Date(), streaming) { onePassSig.correspondingSigReject = reject; }); onePassSig.signatureData = stream.fromAsync(async () => (await onePassSig.correspondingSig).signatureData); - onePassSig.hashed = await onePassSig.hash(onePassSig.signatureType, literalDataList[0], undefined, false, streaming); + onePassSig.hashed = stream.readToEnd(await onePassSig.hash(onePassSig.signatureType, literalDataList[0], undefined, false, streaming)); })); msg.packets.stream = stream.transformPair(msg.packets.stream, async (readable, writable) => { const reader = stream.getReader(readable); diff --git a/src/packet/clone.js b/src/packet/clone.js index c0252861..c55e7f87 100644 --- a/src/packet/clone.js +++ b/src/packet/clone.js @@ -87,6 +87,7 @@ function verificationObjectToClone(verObject) { try { await verified; delete packets[0].signature; + delete packets[0].hashed; } catch (e) {} return packets; }); diff --git a/src/packet/signature.js b/src/packet/signature.js index a2157216..1b443bc6 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -699,7 +699,7 @@ Signature.prototype.verify = async function (key, signatureType, data, detached let toHash; let hash; if (this.hashed) { - hash = this.hashed; + hash = await this.hashed; } else { toHash = this.toHash(signatureType, data, detached); if (!streaming) toHash = await stream.readToEnd(toHash);