Skip to content

Commit 718f044

Browse files
panvanodejs-github-bot
authored andcommitted
crypto: validate digest output encodings
Reject unknown Hash and Hmac digest encodings before finalizing the operation instead of silently returning a Buffer. Preserve buffer output aliases and existing encoding coercion. Signed-off-by: Filip Skokan <panva.ip@gmail.com> Assisted-by: Codex PR-URL: #66247 Fixes: #45189 Refs: #45990 Reviewed-By: Xuguang Mei <meixuguang@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 6b8001d commit 718f044

2 files changed

Lines changed: 48 additions & 4 deletions

File tree

‎lib/internal/crypto/hash.js‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const {
5252
ERR_CRYPTO_HASH_UPDATE_FAILED,
5353
ERR_INVALID_ARG_TYPE,
5454
ERR_INVALID_ARG_VALUE,
55+
ERR_UNKNOWN_ENCODING,
5556
},
5657
} = require('internal/errors');
5758

@@ -176,13 +177,23 @@ Hash.prototype.update = function update(data, encoding) {
176177
};
177178

178179

180+
function validateDigestEncoding(outputEncoding) {
181+
// Explicit conversion of truthy values for backward compatibility.
182+
outputEncoding &&= `${outputEncoding}`;
183+
if (outputEncoding &&
184+
normalizeEncoding(outputEncoding) === undefined &&
185+
StringPrototypeToLowerCase(outputEncoding) !== 'buffer') {
186+
throw new ERR_UNKNOWN_ENCODING(outputEncoding);
187+
}
188+
return outputEncoding;
189+
}
190+
179191
Hash.prototype.digest = function digest(outputEncoding) {
180192
const state = this[kState];
181193
if (state[kFinalized])
182194
throw new ERR_CRYPTO_HASH_FINALIZED();
183195

184-
// Explicit conversion of truthy values for backward compatibility.
185-
const ret = this[kHandle].digest(outputEncoding && `${outputEncoding}`);
196+
const ret = this[kHandle].digest(validateDigestEncoding(outputEncoding));
186197
state[kFinalized] = true;
187198
return ret;
188199
};
@@ -217,8 +228,7 @@ Hmac.prototype.digest = function digest(outputEncoding) {
217228
return buf;
218229
}
219230

220-
// Explicit conversion of truthy values for backward compatibility.
221-
const ret = this[kHandle].digest(outputEncoding && `${outputEncoding}`);
231+
const ret = this[kHandle].digest(validateDigestEncoding(outputEncoding));
222232
state[kFinalized] = true;
223233
return ret;
224234
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const crypto = require('crypto');
9+
10+
for (const create of [
11+
() => crypto.createHash('sha256'),
12+
() => crypto.createHmac('sha256', 'key'),
13+
]) {
14+
const expected = create().update('test').digest();
15+
for (const encoding of ['bad', { toString: () => 'bad' }]) {
16+
const instance = create().update('test');
17+
assert.throws(() => instance.digest(encoding), {
18+
code: 'ERR_UNKNOWN_ENCODING',
19+
message: 'Unknown encoding: bad',
20+
});
21+
// An invalid encoding must not finalize the operation.
22+
assert.deepStrictEqual(instance.digest(), expected);
23+
}
24+
for (const encoding of [undefined, null, '', false, 0, 'buffer', 'BUFFER']) {
25+
assert.deepStrictEqual(create().update('test').digest(encoding), expected);
26+
}
27+
for (const encoding of ['hex', 'HEX', 'base64', 'base64url', 'latin1', 'utf-8']) {
28+
assert.strictEqual(create().update('test').digest(encoding),
29+
expected.toString(encoding));
30+
}
31+
assert.strictEqual(
32+
create().update('test').digest({ toString: () => 'hex' }),
33+
expected.toString('hex'));
34+
}

0 commit comments

Comments
 (0)