0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00
nodejs/test/known_issues/test-crypto-authenticated-stream.js
Ben Noordhuis 6f94221884
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>
2020-04-02 18:00:07 +02:00

143 lines
4.1 KiB
JavaScript

/* 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,
});