From 37d30c5003fa7d9193eda357c022db36e4595cda Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Wed, 12 Feb 2020 18:20:10 +0100 Subject: [PATCH] Return strings in Node streams where appropriate --- src/openpgp.js | 19 ++++++----- test/general/streaming.js | 69 +++++++++++++++++++++++++-------------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/src/openpgp.js b/src/openpgp.js index e3121f15..020f762d 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -338,7 +338,7 @@ export function encrypt({ message, publicKeys, privateKeys, passwords, sessionKe if (returnSessionKey) { result.sessionKey = encrypted.sessionKey; } - return convertStreams(result, streaming, ['signature', 'data']); + return convertStreams(result, streaming, armor ? 'utf8' : 'binary', ['signature', 'data']); }).catch(onError.bind(null, 'Error encrypting message')); } @@ -388,7 +388,7 @@ export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKe result.data = format === 'binary' ? decrypted.getLiteralData() : decrypted.getText(); result.filename = decrypted.getFilename(); if (streaming) linkStreams(result, message); - result.data = await convertStream(result.data, streaming); + result.data = await convertStream(result.data, streaming, format); if (!streaming) await prepareSignatures(result.signatures); return result; }).catch(onError.bind(null, 'Error decrypting message')); @@ -453,7 +453,7 @@ export function sign({ message, privateKeys, armor = true, streaming = message & message = await message.sign(privateKeys, undefined, date, fromUserIds, message.fromStream); result.data = armor ? message.armor() : message.write(); } - return convertStreams(result, streaming, ['signature', 'data']); + return convertStreams(result, streaming, armor ? 'utf8' : 'binary', ['signature', 'data']); }).catch(onError.bind(null, 'Error signing message')); } @@ -495,7 +495,7 @@ export function verify({ message, publicKeys, format = 'utf8', streaming = messa result.signatures = signature ? await message.verifyDetached(signature, publicKeys, date, streaming) : await message.verify(publicKeys, date, streaming); result.data = format === 'binary' ? message.getLiteralData() : message.getText(); if (streaming) linkStreams(result, message); - result.data = await convertStream(result.data, streaming); + result.data = await convertStream(result.data, streaming, format); if (!streaming) await prepareSignatures(result.signatures); return result; }).catch(onError.bind(null, 'Error verifying signed message')); @@ -616,9 +616,10 @@ function toArray(param) { * Convert data to or from Stream * @param {Object} data the data to convert * @param {'web'|'node'|false} streaming (optional) whether to return a ReadableStream + * @param {'utf8'|'binary'} encoding (optional) how to return data in Node Readable streams * @returns {Object} the data in the respective format */ -async function convertStream(data, streaming) { +async function convertStream(data, streaming, encoding = 'utf8') { if (!streaming && util.isStream(data)) { return stream.readToEnd(data); } @@ -632,6 +633,7 @@ async function convertStream(data, streaming) { } if (streaming === 'node') { data = stream.webToNode(data); + if (encoding !== 'binary') data.setEncoding(encoding); } return data; } @@ -640,16 +642,17 @@ async function convertStream(data, streaming) { * Convert object properties from Stream * @param {Object} obj the data to convert * @param {'web'|'node'|false} streaming (optional) whether to return ReadableStreams + * @param {'utf8'|'binary'} encoding (optional) how to return data in Node Readable streams * @param {Array} keys (optional) which keys to return as streams, if possible * @returns {Object} the data in the respective format */ -async function convertStreams(obj, streaming, keys = []) { +async function convertStreams(obj, streaming, encoding = 'utf8', keys = []) { if (Object.prototype.isPrototypeOf(obj) && !Uint8Array.prototype.isPrototypeOf(obj)) { await Promise.all(Object.entries(obj).map(async ([key, value]) => { // recursively search all children if (util.isStream(value) || keys.includes(key)) { - obj[key] = await convertStream(value, streaming); + obj[key] = await convertStream(value, streaming, encoding); } else { - await convertStreams(obj[key], streaming); + await convertStreams(obj[key], streaming, encoding); } })); } diff --git a/test/general/streaming.js b/test/general/streaming.js index 267a2c0a..df6b634d 100644 --- a/test/general/streaming.js +++ b/test/general/streaming.js @@ -892,31 +892,6 @@ function tests() { await openpgp.stream.cancel(signed.signature, new Error('canceled by test')); expect(canceled).to.be.true; }); - - if (openpgp.util.detectNode()) { - const fs = util.nodeRequire('fs'); - - it('Node: Encrypt and decrypt binary message roundtrip', async function() { - dataArrived(); // Do not wait until data arrived. - let plaintext = fs.readFileSync(__filename); - const data = fs.createReadStream(__filename); - const encrypted = await openpgp.encrypt({ - message: openpgp.message.fromBinary(data), - passwords: ['test'], - }); - - const msgAsciiArmored = encrypted.data; - const message = await openpgp.message.readArmored(msgAsciiArmored); - const decrypted = await openpgp.decrypt({ - passwords: ['test'], - message, - format: 'binary' - }); - expect(util.isStream(decrypted.data)).to.equal('node'); - expect(await openpgp.stream.readToEnd(decrypted.data)).to.deep.equal(plaintext); - }); - - } } describe('Streaming', function() { @@ -969,4 +944,48 @@ describe('Streaming', function() { expectedType = 'node'; } }); + + if (openpgp.util.detectNode()) { + const fs = util.nodeRequire('fs'); + + it('Node: Encrypt and decrypt text message roundtrip', async function() { + dataArrived(); // Do not wait until data arrived. + const plaintext = fs.readFileSync(__filename.replace('streaming.js', 'openpgp.js'), 'utf8'); + const data = fs.createReadStream(__filename.replace('streaming.js', 'openpgp.js'), { encoding: 'utf8' }); + const encrypted = await openpgp.encrypt({ + message: openpgp.message.fromText(data), + passwords: ['test'] + }); + expect(util.isStream(encrypted.data)).to.equal('node'); + + const message = await openpgp.message.readArmored(encrypted.data); + const decrypted = await openpgp.decrypt({ + passwords: ['test'], + message + }); + expect(util.isStream(decrypted.data)).to.equal('node'); + expect(await openpgp.stream.readToEnd(decrypted.data)).to.equal(plaintext); + }); + + it('Node: Encrypt and decrypt binary message roundtrip', async function() { + dataArrived(); // Do not wait until data arrived. + const plaintext = fs.readFileSync(__filename.replace('streaming.js', 'openpgp.js')); + const data = fs.createReadStream(__filename.replace('streaming.js', 'openpgp.js')); + const encrypted = await openpgp.encrypt({ + message: openpgp.message.fromBinary(data), + passwords: ['test'], + armor: false + }); + expect(util.isStream(encrypted.data)).to.equal('node'); + + const message = await openpgp.message.read(encrypted.data); + const decrypted = await openpgp.decrypt({ + passwords: ['test'], + message, + format: 'binary' + }); + expect(util.isStream(decrypted.data)).to.equal('node'); + expect(await openpgp.stream.readToEnd(decrypted.data)).to.deep.equal(plaintext); + }); + } }); \ No newline at end of file