From 603803ecb776ca5d9644c6b16c1c2a45b99b27fe Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Sat, 23 Apr 2022 19:42:45 +0200 Subject: crypto: validate `this` in all webcrypto methods and getters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/42815 Reviewed-By: Antoine du Hamel Reviewed-By: James M Snell Reviewed-By: Tobias Nießen --- lib/internal/crypto/webcrypto.js | 58 ++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/internal/crypto/webcrypto.js b/lib/internal/crypto/webcrypto.js index cf440ebf8ff..3b7e4d13d8b 100644 --- a/lib/internal/crypto/webcrypto.js +++ b/lib/internal/crypto/webcrypto.js @@ -6,6 +6,7 @@ const { JSONStringify, ObjectDefineProperties, ReflectApply, + ReflectConstruct, SafeSet, SymbolToStringTag, StringPrototypeRepeat, @@ -31,6 +32,7 @@ const { TextDecoder, TextEncoder } = require('internal/encoding'); const { codes: { + ERR_ILLEGAL_CONSTRUCTOR, ERR_INVALID_ARG_TYPE, ERR_INVALID_THIS, } @@ -70,12 +72,21 @@ const { randomUUID: _randomUUID, } = require('internal/crypto/random'); -const randomUUID = () => _randomUUID(); +async function digest(algorithm, data) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); + return ReflectApply(asyncDigest, this, arguments); +} + +function randomUUID() { + if (this !== crypto) throw new ERR_INVALID_THIS('Crypto'); + return _randomUUID(); +} async function generateKey( algorithm, extractable, keyUsages) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); algorithm = normalizeAlgorithm(algorithm); validateBoolean(extractable, 'extractable'); validateArray(keyUsages, 'keyUsages'); @@ -123,6 +134,7 @@ async function generateKey( } async function deriveBits(algorithm, baseKey, length) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); algorithm = normalizeAlgorithm(algorithm); if (!isCryptoKey(baseKey)) throw new ERR_INVALID_ARG_TYPE('baseKey', 'CryptoKey', baseKey); @@ -194,6 +206,7 @@ async function deriveKey( derivedKeyAlgorithm, extractable, keyUsages) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); algorithm = normalizeAlgorithm(algorithm); derivedKeyAlgorithm = normalizeAlgorithm(derivedKeyAlgorithm); if (!isCryptoKey(baseKey)) @@ -238,7 +251,11 @@ async function deriveKey( throw lazyDOMException('Unrecognized name.'); } - return importKey('raw', bits, derivedKeyAlgorithm, extractable, keyUsages); + return ReflectApply( + importKey, + this, + ['raw', bits, derivedKeyAlgorithm, extractable, keyUsages], + ); } async function exportKeySpki(key) { @@ -415,6 +432,7 @@ async function exportKeyJWK(key) { } async function exportKey(format, key) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); validateString(format, 'format'); validateOneOf(format, 'format', kExportFormats); if (!isCryptoKey(key)) @@ -496,6 +514,7 @@ async function importKey( algorithm, extractable, keyUsages) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); validateString(format, 'format'); validateOneOf(format, 'format', kExportFormats); if (format !== 'node.keyObject' && format !== 'jwk') @@ -557,8 +576,9 @@ async function importKey( // subtle.wrapKey() is essentially a subtle.exportKey() followed // by a subtle.encrypt(). async function wrapKey(format, key, wrappingKey, algorithm) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); algorithm = normalizeAlgorithm(algorithm); - let keyData = await exportKey(format, key); + let keyData = await ReflectApply(exportKey, this, [format, key]); if (format === 'jwk') { if (keyData == null || typeof keyData !== 'object') @@ -586,6 +606,7 @@ async function unwrapKey( unwrappedKeyAlgo, extractable, keyUsages) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); wrappedKey = getArrayBufferOrView(wrappedKey, 'wrappedKey'); unwrapAlgo = normalizeAlgorithm(unwrapAlgo); let keyData = await cipherOrWrap( @@ -607,7 +628,11 @@ async function unwrapKey( } } - return importKey(format, keyData, unwrappedKeyAlgo, extractable, keyUsages); + return ReflectApply( + importKey, + this, + [format, keyData, unwrappedKeyAlgo, extractable, keyUsages], + ); } function signVerify(algorithm, key, data, signature) { @@ -654,10 +679,12 @@ function signVerify(algorithm, key, data, signature) { } async function sign(algorithm, key, data) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); return signVerify(algorithm, key, data); } async function verify(algorithm, key, signature, data) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); return signVerify(algorithm, key, data, signature); } @@ -707,30 +734,39 @@ async function cipherOrWrap(mode, algorithm, key, data, op) { } async function encrypt(algorithm, key, data) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); return cipherOrWrap(kWebCryptoCipherEncrypt, algorithm, key, data, 'encrypt'); } async function decrypt(algorithm, key, data) { + if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); return cipherOrWrap(kWebCryptoCipherDecrypt, algorithm, key, data, 'decrypt'); } // The SubtleCrypto and Crypto classes are defined as part of the // Web Crypto API standard: https://www.w3.org/TR/WebCryptoAPI/ -class SubtleCrypto {} -const subtle = new SubtleCrypto(); +class SubtleCrypto { + constructor() { + throw new ERR_ILLEGAL_CONSTRUCTOR(); + } +} +const subtle = ReflectConstruct(function() {}, [], SubtleCrypto); class Crypto { + constructor() { + throw new ERR_ILLEGAL_CONSTRUCTOR(); + } + get subtle() { + if (this !== crypto) throw new ERR_INVALID_THIS('Crypto'); return subtle; } } -const crypto = new Crypto(); +const crypto = ReflectConstruct(function() {}, [], Crypto); function getRandomValues(array) { - if (!(this instanceof Crypto)) { - throw new ERR_INVALID_THIS('Crypto'); - } + if (this !== crypto) throw new ERR_INVALID_THIS('Crypto'); return ReflectApply(_getRandomValues, this, arguments); } @@ -799,7 +835,7 @@ ObjectDefineProperties( enumerable: true, configurable: true, writable: true, - value: asyncDigest, + value: digest, }, generateKey: { enumerable: true, -- cgit v1.2.3