mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
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>
This commit is contained in:
committed by
Anna Henningsen
parent
e06512b9b7
commit
6f94221884
142
test/known_issues/test-crypto-authenticated-stream.js
Normal file
142
test/known_issues/test-crypto-authenticated-stream.js
Normal file
@@ -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,
|
||||
});
|
||||
Reference in New Issue
Block a user