diff options
author | Ben Noordhuis <info@bnoordhuis.nl> | 2020-02-18 12:26:49 +0100 |
---|---|---|
committer | Anna Henningsen <anna@addaleax.net> | 2020-04-02 18:00:07 +0200 |
commit | 6f942218848c7f5993f86bdcbd299b0fd73749d9 (patch) | |
tree | 50a2a50008eb7a9f3d4719d651ccc46385218951 /test/known_issues | |
parent | e06512b9b75552f73371512ded879e472c0ec57e (diff) | |
download | node-new-6f942218848c7f5993f86bdcbd299b0fd73749d9.tar.gz |
test: add known issues test for #31733
Authenticated decryption works for file streams up to 32768 bytes but
not beyond. Other streams and direct decryption are not affected.
Refs: https://github.com/nodejs/node/issues/31733
PR-URL: https://github.com/nodejs/node/pull/31734
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'test/known_issues')
-rw-r--r-- | test/known_issues/test-crypto-authenticated-stream.js | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/test/known_issues/test-crypto-authenticated-stream.js b/test/known_issues/test-crypto-authenticated-stream.js new file mode 100644 index 0000000000..1e2a9edd7b --- /dev/null +++ b/test/known_issues/test-crypto-authenticated-stream.js @@ -0,0 +1,142 @@ +/* eslint-disable node-core/crypto-check */ +'use strict'; +// Refs: https://github.com/nodejs/node/issues/31733 +const common = require('../common'); +const assert = require('assert'); +const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); +const stream = require('stream'); +const tmpdir = require('../common/tmpdir'); + +class Sink extends stream.Writable { + constructor() { + super(); + this.chunks = []; + } + + _write(chunk, encoding, cb) { + this.chunks.push(chunk); + cb(); + } +} + +function direct(config) { + const { cipher, key, iv, aad, authTagLength, plaintextLength } = config; + const expected = Buffer.alloc(plaintextLength); + + const c = crypto.createCipheriv(cipher, key, iv, { authTagLength }); + c.setAAD(aad, { plaintextLength }); + const ciphertext = Buffer.concat([c.update(expected), c.final()]); + + const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength }); + d.setAAD(aad, { plaintextLength }); + d.setAuthTag(c.getAuthTag()); + const actual = Buffer.concat([d.update(ciphertext), d.final()]); + + assert.deepStrictEqual(expected, actual); +} + +function mstream(config) { + const { cipher, key, iv, aad, authTagLength, plaintextLength } = config; + const expected = Buffer.alloc(plaintextLength); + + const c = crypto.createCipheriv(cipher, key, iv, { authTagLength }); + c.setAAD(aad, { plaintextLength }); + + const plain = new stream.PassThrough(); + const crypt = new Sink(); + const chunks = crypt.chunks; + plain.pipe(c).pipe(crypt); + plain.end(expected); + + crypt.on('close', common.mustCall(() => { + const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength }); + d.setAAD(aad, { plaintextLength }); + d.setAuthTag(c.getAuthTag()); + + const crypt = new stream.PassThrough(); + const plain = new Sink(); + crypt.pipe(d).pipe(plain); + for (const chunk of chunks) crypt.write(chunk); + crypt.end(); + + plain.on('close', common.mustCall(() => { + const actual = Buffer.concat(plain.chunks); + assert.deepStrictEqual(expected, actual); + })); + })); +} + +function fstream(config) { + const count = fstream.count++; + const filename = (name) => path.join(tmpdir.path, `${name}${count}`); + + const { cipher, key, iv, aad, authTagLength, plaintextLength } = config; + const expected = Buffer.alloc(plaintextLength); + fs.writeFileSync(filename('a'), expected); + + const c = crypto.createCipheriv(cipher, key, iv, { authTagLength }); + c.setAAD(aad, { plaintextLength }); + + const plain = fs.createReadStream(filename('a')); + const crypt = fs.createWriteStream(filename('b')); + plain.pipe(c).pipe(crypt); + + // Observation: 'close' comes before 'end' on |c|, which definitely feels + // wrong. Switching to `c.on('end', ...)` doesn't fix the test though. + crypt.on('close', common.mustCall(() => { + // Just to drive home the point that decryption does actually work: + // reading the file synchronously, then decrypting it, works. + { + const ciphertext = fs.readFileSync(filename('b')); + const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength }); + d.setAAD(aad, { plaintextLength }); + d.setAuthTag(c.getAuthTag()); + const actual = Buffer.concat([d.update(ciphertext), d.final()]); + assert.deepStrictEqual(expected, actual); + } + + const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength }); + d.setAAD(aad, { plaintextLength }); + d.setAuthTag(c.getAuthTag()); + + const crypt = fs.createReadStream(filename('b')); + const plain = fs.createWriteStream(filename('c')); + crypt.pipe(d).pipe(plain); + + plain.on('close', common.mustCall(() => { + const actual = fs.readFileSync(filename('c')); + assert.deepStrictEqual(expected, actual); + })); + })); +} +fstream.count = 0; + +function test(config) { + direct(config); + mstream(config); + fstream(config); +} + +tmpdir.refresh(); + +// OK +test({ + cipher: 'aes-128-ccm', + aad: Buffer.alloc(1), + iv: Buffer.alloc(8), + key: Buffer.alloc(16), + authTagLength: 16, + plaintextLength: 32768, +}); + +// Fails the fstream test. +test({ + cipher: 'aes-128-ccm', + aad: Buffer.alloc(1), + iv: Buffer.alloc(8), + key: Buffer.alloc(16), + authTagLength: 16, + plaintextLength: 32769, +}); |