Piping streams into SHA3 without end: false crashes

  • Version: 12.4.0
  • Platform: Windows 10 x64 and Linux 4.4.0-042stab138.1
  • Subsystem: crypto or deps

This code causes a segmentation fault in 12.4.0:

const crypto = require('crypto');
const fs = require('fs');

const myOwnCode = fs.createReadStream(__filename);
const copy = fs.createWriteStream(`${__filename}.copy`);
const hash = crypto.createHash('sha3-512');
myOwnCode.pipe(hash);
myOwnCode.pipe(copy).on('finish', () => {
  hash.digest();
});

This seems to be caused by pipe calling hash._flush when end is not set to false. This code also causes a segmentation fault:

const crypto = require('crypto');

const hash = crypto.createHash('sha3-512');
hash._flush(() => console.log('Flushed'));
hash.digest();

This seems to be at least partially caused by the implementation of _flush:

Hash.prototype._flush = function _flush(callback) {
this.push(this[kHandle].digest());
callback();
};

It bypasses the this[kState][kFinalized] safeguard:

Hash.prototype.digest = function digest(outputEncoding) {
const state = this[kState];
if (state[kFinalized])
throw new ERR_CRYPTO_HASH_FINALIZED();
outputEncoding = outputEncoding || getDefaultEncoding();
if (normalizeEncoding(outputEncoding) === 'utf16le')
throw new ERR_CRYPTO_HASH_DIGEST_NO_UTF16();
// Explicit conversion for backward compatibility.
const ret = this[kHandle].digest(`${outputEncoding}`);
state[kFinalized] = true;
return ret;
};

Note that this bug only happens when using SHA3, sha256 seems to be working just fine, so there might also be some weirdness in OpenSSL.

cc @mcollina @nodejs/crypto @nodejs/streams