mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
crypto: validate normalized hash names
Check the normalized SHA algorithm instead of reading the caller hash name before conversion. This prevents changing getters from bypassing the supported hash check. Signed-off-by: Filip Skokan <panva.ip@gmail.com> Assisted-by: Codex PR-URL: https://github.com/nodejs/node/pull/66237 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Aviv Keller <me@aviv.sh>
This commit is contained in:
1 parent
e329423cd8
commit
33a4935b84
3 files changed
+54
-20
No files matched your search
@@ -23,6 +23,7 @@ const {
|
||||
SafeMap,
|
||||
SafeSet,
|
||||
StringPrototypeCharCodeAt,
|
||||
StringPrototypeStartsWith,
|
||||
StringPrototypeToUpperCase,
|
||||
Symbol,
|
||||
TypedArrayPrototypeGetBuffer,
|
||||
@@ -841,7 +842,12 @@ function normalizeAlgorithm(algorithm, op) {
|
||||
getBufferSourceBytes(idlValue),
|
||||
);
|
||||
} else if (idlType === 'HashAlgorithmIdentifier') {
|
||||
normalizedAlgorithm[member] = normalizeAlgorithm(idlValue, 'digest');
|
||||
const hash = normalizeAlgorithm(idlValue, 'digest');
|
||||
if (!StringPrototypeStartsWith(hash.name, 'SHA')) {
|
||||
throw lazyDOMException(
|
||||
`Only SHA hashes are supported in ${desiredType}`, 'NotSupportedError');
|
||||
}
|
||||
normalizedAlgorithm[member] = hash;
|
||||
} else if (idlType === 'AlgorithmIdentifier') {
|
||||
// This extension point is not used by any supported algorithm (yet?)
|
||||
throw lazyDOMException('Not implemented.', 'NotSupportedError');
|
||||
|
||||
@@ -7,7 +7,6 @@ const {
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
StringPrototypeCharCodeAt,
|
||||
StringPrototypeSplit,
|
||||
StringPrototypeStartsWith,
|
||||
StringPrototypeToLowerCase,
|
||||
} = primordials;
|
||||
|
||||
@@ -67,18 +66,6 @@ function namedCurveValidator(V, dict) {
|
||||
|
||||
const converters = { __proto__: null, ...webidl };
|
||||
|
||||
/**
|
||||
* @param {string | object} V - The hash algorithm identifier (string or object).
|
||||
* @param {string} label - The dictionary name for the error message.
|
||||
*/
|
||||
function ensureSHA(V, label) {
|
||||
const name = typeof V === 'string' ? V : V.name;
|
||||
if (typeof name !== 'string' ||
|
||||
!StringPrototypeStartsWith(StringPrototypeToLowerCase(name), 'sha'))
|
||||
throw lazyDOMException(
|
||||
`Only SHA hashes are supported in ${label}`, 'NotSupportedError');
|
||||
}
|
||||
|
||||
converters.AlgorithmIdentifier = (V, opts) => {
|
||||
// Union for (object or DOMString)
|
||||
if (type(V) === 'Object') {
|
||||
@@ -200,7 +187,6 @@ converters.RsaHashedKeyGenParams = createDictionaryConverter(
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
validator: (V, dict) => ensureSHA(V, 'RsaHashedKeyGenParams'),
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
@@ -213,7 +199,6 @@ converters.RsaHashedImportParams = createDictionaryConverter(
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
validator: (V, dict) => ensureSHA(V, 'RsaHashedImportParams'),
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
@@ -342,7 +327,6 @@ converters.EcdsaParams = createDictionaryConverter(
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
validator: (V, dict) => ensureSHA(V, 'EcdsaParams'),
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
@@ -368,7 +352,6 @@ for (let i = 0; i < kHmacDictionaries.length; i++) {
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
validator: (V, dict) => ensureSHA(V, name),
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
@@ -432,7 +415,6 @@ converters.HkdfParams = createDictionaryConverter(
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
validator: (V, dict) => ensureSHA(V, 'HkdfParams'),
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
@@ -500,7 +482,6 @@ converters.Pbkdf2Params = createDictionaryConverter(
|
||||
{
|
||||
key: 'hash',
|
||||
converter: converters.HashAlgorithmIdentifier,
|
||||
validator: (V, dict) => ensureSHA(V, 'Pbkdf2Params'),
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const assert = require('assert');
|
||||
const { subtle } = globalThis.crypto;
|
||||
|
||||
(async () => {
|
||||
const data = new Uint8Array(32);
|
||||
let reads = 0;
|
||||
const key = await subtle.importKey('raw', data, {
|
||||
name: 'HMAC',
|
||||
hash: {
|
||||
get name() {
|
||||
reads++;
|
||||
return reads === 1 ? 'SHA-384' : 'SHA-256';
|
||||
},
|
||||
},
|
||||
}, false, ['sign']);
|
||||
assert.strictEqual(reads, 1);
|
||||
assert.strictEqual(key.algorithm.hash.name, 'SHA-384');
|
||||
assert.strictEqual((await subtle.sign('HMAC', key, data)).byteLength, 48);
|
||||
|
||||
const converted = await subtle.importKey('raw', data, {
|
||||
name: 'HMAC',
|
||||
hash: { name: { toString() { return 'SHA-256'; } } },
|
||||
}, false, ['sign']);
|
||||
assert.strictEqual(converted.algorithm.hash.name, 'SHA-256');
|
||||
|
||||
await assert.rejects(subtle.importKey('raw', data, {
|
||||
name: 'HMAC', hash: {},
|
||||
}, false, ['sign']), TypeError);
|
||||
const cshakeAvailable = SubtleCrypto.supports('digest', {
|
||||
name: 'cSHAKE128', outputLength: 256,
|
||||
});
|
||||
await assert.rejects(subtle.importKey('raw', data, {
|
||||
name: 'HMAC', hash: 'cSHAKE128',
|
||||
}, false, ['sign']), cshakeAvailable ? TypeError : { name: 'NotSupportedError' });
|
||||
await assert.rejects(subtle.importKey('raw', data, {
|
||||
name: 'HMAC', hash: 'MD5', length: -1,
|
||||
}, false, ['sign']), TypeError);
|
||||
await assert.rejects(subtle.importKey('raw', data, {
|
||||
name: 'HMAC', hash: { name: 'cSHAKE128', outputLength: 256 },
|
||||
}, false, ['sign']), { name: 'NotSupportedError' });
|
||||
})().then(common.mustCall());
|
||||
Reference in new issue
Block a user