nodejs/test/parallel/test-crypto-hash.js
James M Snell 761de815c5
test: move crypto related common utilities in common/crypto
Since `common/crypto` already exists, it makes sense to keep
crypto-related utilities there. The only exception being
common.hasCrypto which is needed up front to determine
if tests should be skipped.

Eliminate the redundant check in hasFipsCrypto and just
use crypto.getFips() directly where needed.

PR-URL: https://github.com/nodejs/node/pull/56714
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
2025-01-25 00:58:32 +00:00

293 lines
9.5 KiB
JavaScript

'use strict';
const common = require('../common');
if (!common.hasCrypto) {
common.skip('missing crypto');
}
const assert = require('assert');
const crypto = require('crypto');
const fs = require('fs');
const { hasOpenSSL } = require('../common/crypto');
const fixtures = require('../common/fixtures');
let cryptoType;
let digest;
// Test hashing
const a1 = crypto.createHash('sha1').update('Test123').digest('hex');
const a2 = crypto.createHash('sha256').update('Test123').digest('base64');
const a3 = crypto.createHash('sha512').update('Test123').digest(); // buffer
const a4 = crypto.createHash('sha1').update('Test123').digest('buffer');
// stream interface
let a5 = crypto.createHash('sha512');
a5.end('Test123');
a5 = a5.read();
let a6 = crypto.createHash('sha512');
a6.write('Te');
a6.write('st');
a6.write('123');
a6.end();
a6 = a6.read();
let a7 = crypto.createHash('sha512');
a7.end();
a7 = a7.read();
let a8 = crypto.createHash('sha512');
a8.write('');
a8.end();
a8 = a8.read();
if (!crypto.getFips()) {
cryptoType = 'md5';
digest = 'latin1';
const a0 = crypto.createHash(cryptoType).update('Test123').digest(digest);
assert.strictEqual(
a0,
'h\u00ea\u00cb\u0097\u00d8o\fF!\u00fa+\u000e\u0017\u00ca\u00bd\u008c',
`${cryptoType} with ${digest} digest failed to evaluate to expected hash`
);
}
cryptoType = 'md5';
digest = 'hex';
assert.strictEqual(
a1,
'8308651804facb7b9af8ffc53a33a22d6a1c8ac2',
`${cryptoType} with ${digest} digest failed to evaluate to expected hash`);
cryptoType = 'sha256';
digest = 'base64';
assert.strictEqual(
a2,
'2bX1jws4GYKTlxhloUB09Z66PoJZW+y+hq5R8dnx9l4=',
`${cryptoType} with ${digest} digest failed to evaluate to expected hash`);
cryptoType = 'sha512';
digest = 'latin1';
assert.deepStrictEqual(
a3,
Buffer.from(
'\u00c1(4\u00f1\u0003\u001fd\u0097!O\'\u00d4C/&Qz\u00d4' +
'\u0094\u0015l\u00b8\u008dQ+\u00db\u001d\u00c4\u00b5}\u00b2' +
'\u00d6\u0092\u00a3\u00df\u00a2i\u00a1\u009b\n\n*\u000f' +
'\u00d7\u00d6\u00a2\u00a8\u0085\u00e3<\u0083\u009c\u0093' +
'\u00c2\u0006\u00da0\u00a1\u00879(G\u00ed\'',
'latin1'),
`${cryptoType} with ${digest} digest failed to evaluate to expected hash`);
cryptoType = 'sha1';
digest = 'hex';
assert.deepStrictEqual(
a4,
Buffer.from('8308651804facb7b9af8ffc53a33a22d6a1c8ac2', 'hex'),
`${cryptoType} with ${digest} digest failed to evaluate to expected hash`
);
// Stream interface should produce the same result.
assert.deepStrictEqual(a5, a3);
assert.deepStrictEqual(a6, a3);
assert.notStrictEqual(a7, undefined);
assert.notStrictEqual(a8, undefined);
// Test multiple updates to same hash
const h1 = crypto.createHash('sha1').update('Test123').digest('hex');
const h2 = crypto.createHash('sha1').update('Test').update('123').digest('hex');
assert.strictEqual(h1, h2);
// Test hashing for binary files
const fn = fixtures.path('sample.png');
const sha1Hash = crypto.createHash('sha1');
const fileStream = fs.createReadStream(fn);
fileStream.on('data', function(data) {
sha1Hash.update(data);
});
fileStream.on('close', common.mustCall(function() {
// Test SHA1 of sample.png
assert.strictEqual(sha1Hash.digest('hex'),
'22723e553129a336ad96e10f6aecdf0f45e4149e');
}));
// Issue https://github.com/nodejs/node-v0.x-archive/issues/2227: unknown digest
// method should throw an error.
assert.throws(function() {
crypto.createHash('xyzzy');
}, /Digest method not supported/);
// Issue https://github.com/nodejs/node/issues/9819: throwing encoding used to
// segfault.
assert.throws(
() => crypto.createHash('sha256').digest({
toString: () => { throw new Error('boom'); },
}),
{
name: 'Error',
message: 'boom'
});
// Issue https://github.com/nodejs/node/issues/25487: error message for invalid
// arg type to update method should include all possible types
assert.throws(
() => crypto.createHash('sha256').update(),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
});
// Default UTF-8 encoding
const hutf8 = crypto.createHash('sha512').update('УТФ-8 text').digest('hex');
assert.strictEqual(
hutf8,
'4b21bbd1a68e690a730ddcb5a8bc94ead9879ffe82580767ad7ec6fa8ba2dea6' +
'43a821af66afa9a45b6a78c712fecf0e56dc7f43aef4bcfc8eb5b4d8dca6ea5b');
assert.notStrictEqual(
hutf8,
crypto.createHash('sha512').update('УТФ-8 text', 'latin1').digest('hex'));
const h3 = crypto.createHash('sha256');
h3.digest();
assert.throws(
() => h3.digest(),
{
code: 'ERR_CRYPTO_HASH_FINALIZED',
name: 'Error'
});
assert.throws(
() => h3.update('foo'),
{
code: 'ERR_CRYPTO_HASH_FINALIZED',
name: 'Error'
});
assert.strictEqual(
crypto.createHash('sha256').update('test').digest('ucs2'),
crypto.createHash('sha256').update('test').digest().toString('ucs2'));
assert.throws(
() => crypto.createHash(),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "algorithm" argument must be of type string. ' +
'Received undefined'
}
);
{
const Hash = crypto.Hash;
const instance = crypto.Hash('sha256');
assert(instance instanceof Hash, 'Hash is expected to return a new instance' +
' when called without `new`');
}
// Test XOF hash functions and the outputLength option.
{
// Default outputLengths. Since OpenSSL 3.4 an outputLength is mandatory
if (!hasOpenSSL(3, 4)) {
assert.strictEqual(crypto.createHash('shake128').digest('hex'),
'7f9c2ba4e88f827d616045507605853e');
assert.strictEqual(crypto.createHash('shake128', null).digest('hex'),
'7f9c2ba4e88f827d616045507605853e');
assert.strictEqual(crypto.createHash('shake256').digest('hex'),
'46b9dd2b0ba88d13233b3feb743eeb24' +
'3fcd52ea62b81b82b50c27646ed5762f');
assert.strictEqual(crypto.createHash('shake256', { outputLength: 0 })
.copy() // Default outputLength.
.digest('hex'),
'46b9dd2b0ba88d13233b3feb743eeb24' +
'3fcd52ea62b81b82b50c27646ed5762f');
}
// Short outputLengths.
assert.strictEqual(crypto.createHash('shake128', { outputLength: 0 })
.digest('hex'),
'');
assert.strictEqual(crypto.createHash('shake128', { outputLength: 5 })
.copy({ outputLength: 0 })
.digest('hex'),
'');
assert.strictEqual(crypto.createHash('shake128', { outputLength: 5 })
.digest('hex'),
'7f9c2ba4e8');
assert.strictEqual(crypto.createHash('shake128', { outputLength: 0 })
.copy({ outputLength: 5 })
.digest('hex'),
'7f9c2ba4e8');
assert.strictEqual(crypto.createHash('shake128', { outputLength: 15 })
.digest('hex'),
'7f9c2ba4e88f827d61604550760585');
assert.strictEqual(crypto.createHash('shake256', { outputLength: 16 })
.digest('hex'),
'46b9dd2b0ba88d13233b3feb743eeb24');
// Large outputLengths.
assert.strictEqual(crypto.createHash('shake128', { outputLength: 128 })
.digest('hex'),
'7f9c2ba4e88f827d616045507605853e' +
'd73b8093f6efbc88eb1a6eacfa66ef26' +
'3cb1eea988004b93103cfb0aeefd2a68' +
'6e01fa4a58e8a3639ca8a1e3f9ae57e2' +
'35b8cc873c23dc62b8d260169afa2f75' +
'ab916a58d974918835d25e6a435085b2' +
'badfd6dfaac359a5efbb7bcc4b59d538' +
'df9a04302e10c8bc1cbf1a0b3a5120ea');
const superLongHash = crypto.createHash('shake256', {
outputLength: 1024 * 1024
}).update('The message is shorter than the hash!')
.digest('hex');
assert.strictEqual(superLongHash.length, 2 * 1024 * 1024);
assert.ok(superLongHash.endsWith('193414035ddba77bf7bba97981e656ec'));
assert.ok(superLongHash.startsWith('a2a28dbc49cfd6e5d6ceea3d03e77748'));
// Non-XOF hash functions should accept valid outputLength options as well.
assert.strictEqual(crypto.createHash('sha224', { outputLength: 28 })
.digest('hex'),
'd14a028c2a3a2bc9476102bb288234c4' +
'15a2b01f828ea62ac5b3e42f');
// Passing invalid sizes should throw during creation.
assert.throws(() => {
crypto.createHash('sha256', { outputLength: 28 });
}, {
code: 'ERR_OSSL_EVP_NOT_XOF_OR_INVALID_LENGTH'
});
for (const outputLength of [null, {}, 'foo', false]) {
assert.throws(() => crypto.createHash('sha256', { outputLength }),
{ code: 'ERR_INVALID_ARG_TYPE' });
}
for (const outputLength of [-1, .5, Infinity, 2 ** 90]) {
assert.throws(() => crypto.createHash('sha256', { outputLength }),
{ code: 'ERR_OUT_OF_RANGE' });
}
}
{
const h = crypto.createHash('sha512');
h.digest();
assert.throws(() => h.copy(), { code: 'ERR_CRYPTO_HASH_FINALIZED' });
assert.throws(() => h.digest(), { code: 'ERR_CRYPTO_HASH_FINALIZED' });
}
{
const a = crypto.createHash('sha512').update('abc');
const b = a.copy();
const c = b.copy().update('def');
const d = crypto.createHash('sha512').update('abcdef');
assert.strictEqual(a.digest('hex'), b.digest('hex'));
assert.strictEqual(c.digest('hex'), d.digest('hex'));
}
{
crypto.Hash('sha256');
common.expectWarning({
DeprecationWarning: [
['crypto.Hash constructor is deprecated.',
'DEP0179'],
]
});
}