/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/node/polyfills/tls.ts
441 строка
12 KB
Bartek Iwańczuk
chore: bump node_compat test suite to Node.js 26.3.0 (#34746)
18 июн 2026, 20:32
Не верифицирован
18 июн 2026, 20:32
b843651
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. (function () { const { core, primordials } = __bootstrap; const { notImplemented } = core.loadExtScript("ext:deno_node/_utils.ts"); const { getExecArgvOptionValue, getOptionValue } = core.loadExtScript( "ext:deno_node/internal/options.ts", ); const { convertALPNProtocols } = core.loadExtScript( "ext:deno_node/internal/tls_common.js", ); const { X509Certificate } = core.loadExtScript( "ext:deno_node/internal/crypto/x509.ts", ); const { op_get_env_no_permission_check, op_get_root_certificates, op_node_get_ca_certificates, op_set_default_ca_certificates, } = core.ops; const { validateOneOf, validateString, } = core.loadExtScript("ext:deno_node/internal/validators.mjs"); const { isArrayBufferView } = core.loadExtScript( "ext:deno_node/internal/util/types.ts", ); const { ERR_INVALID_ARG_TYPE } = core.loadExtScript( "ext:deno_node/internal/errors.ts", ); const { isTypedArray } = core; const { ArrayIsArray, ArrayPrototypeIncludes, ArrayPrototypeForEach, ArrayPrototypeMap, ArrayPrototypePush, DataViewPrototypeGetBuffer, DataViewPrototypeGetByteLength, DataViewPrototypeGetByteOffset, Error, ObjectKeys, ObjectFreeze, Proxy, ReflectDefineProperty, ReflectDeleteProperty, ReflectGet, ReflectGetOwnPropertyDescriptor, ReflectHas, ReflectIsExtensible, ReflectOwnKeys, ReflectPreventExtensions, ReflectSet, StringPrototypeIncludes, StringPrototypeSplit, StringPrototypeTrim, StringPrototypeToLowerCase, SafeSet, SetPrototypeAdd, SetPrototypeHas, TypedArrayPrototypeGetBuffer, TypedArrayPrototypeGetByteLength, TypedArrayPrototypeGetByteOffset, Uint8Array, } = primordials; // Lazy-init: tls.ts is loaded into the startup snapshot before TextDecoder // is registered as a global, so a top-level `new TextDecoder()` would throw. let utf8Decoder: TextDecoder | null = null; // deno-lint-ignore no-explicit-any function arrayBufferViewToString(view: any): string { // Use the matching prototype getter so a forged accessor on the view // can't redirect us to a different ArrayBuffer / out-of-bounds region. const isTA = isTypedArray(view); const buffer = isTA ? TypedArrayPrototypeGetBuffer(view) : DataViewPrototypeGetBuffer(view); const byteOffset = isTA ? TypedArrayPrototypeGetByteOffset(view) : DataViewPrototypeGetByteOffset(view); const byteLength = isTA ? TypedArrayPrototypeGetByteLength(view) : DataViewPrototypeGetByteLength(view); if (utf8Decoder === null) { utf8Decoder = new TextDecoder("utf-8"); } return utf8Decoder.decode(new Uint8Array(buffer, byteOffset, byteLength)); } // openssl -> rustls const cipherMap = { "__proto__": null, "AES128-GCM-SHA256": "TLS13_AES_128_GCM_SHA256", "AES256-GCM-SHA384": "TLS13_AES_256_GCM_SHA384", "AES256-SHA": "TLS_RSA_WITH_AES_256_CBC_SHA", "ECDHE-ECDSA-AES128-GCM-SHA256": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "ECDHE-ECDSA-AES256-GCM-SHA384": "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "ECDHE-ECDSA-CHACHA20-POLY1305": "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", "ECDHE-RSA-AES128-GCM-SHA256": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "ECDHE-RSA-AES256-GCM-SHA384": "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "ECDHE-RSA-CHACHA20-POLY1305": "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_AES_128_CCM_8_SHA256": "TLS13_AES_128_CCM_8_SHA256", "TLS_AES_128_GCM_SHA256": "TLS13_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384": "TLS13_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256": "TLS13_CHACHA20_POLY1305_SHA256", }; function getCiphers() { // TODO(bnoordhuis) Use locale-insensitive toLowerCase() return ArrayPrototypeMap( ObjectKeys(cipherMap), (name) => StringPrototypeToLowerCase(name), ); } let lazyRootCertificates: string[] | null = null; const cachedCACertificates: Record<string, string[]> = { __proto__: null as unknown as string[], }; function writeNativeCryptoDebug(message: string) { const nodeDebugNative = op_get_env_no_permission_check("NODE_DEBUG_NATIVE") ?? ""; if ( !StringPrototypeIncludes(nodeDebugNative, "crypto") ) { return; } core.print(`${message}\n`, true); } function emitDefaultCertificatesDebug() { const useOpenSslCa = op_get_env_no_permission_check("DENO_NODE_USE_OPENSSL_CA") === "1"; if (useOpenSslCa) { return; } const stores: string[] = []; ArrayPrototypeForEach( StringPrototypeSplit( op_get_env_no_permission_check("DENO_TLS_CA_STORE") ?? "mozilla", ",", ), (store) => { const trimmedStore = StringPrototypeTrim(store); if (trimmedStore.length > 0) { ArrayPrototypePush(stores, trimmedStore); } }, ); if (ArrayPrototypeIncludes(stores, "mozilla")) { writeNativeCryptoDebug( "Started loading bundled root certificates off-thread", ); } if (ArrayPrototypeIncludes(stores, "system")) { writeNativeCryptoDebug( "Started loading system root certificates off-thread", ); } if (op_get_env_no_permission_check("NODE_EXTRA_CA_CERTS")) { writeNativeCryptoDebug( "Started loading extra root certificates off-thread", ); } } function ensureLazyRootCertificates(target: string[]) { if (lazyRootCertificates === null) { lazyRootCertificates = op_get_root_certificates() as string[]; // Clear target and repopulate target.length = 0; ArrayPrototypeForEach( lazyRootCertificates, (v: string) => ArrayPrototypePush(target, v), ); ObjectFreeze(target); } } const rootCertificates = new Proxy([] as string[], { // @ts-ignore __proto__ is not in the types __proto__: null, get(target, prop) { ensureLazyRootCertificates(target); return ReflectGet(target, prop); }, ownKeys(target) { ensureLazyRootCertificates(target); return ReflectOwnKeys(target); }, has(target, prop) { ensureLazyRootCertificates(target); return ReflectHas(target, prop); }, getOwnPropertyDescriptor(target, prop) { ensureLazyRootCertificates(target); return ReflectGetOwnPropertyDescriptor(target, prop); }, set(target, prop, value) { ensureLazyRootCertificates(target); return ReflectSet(target, prop, value); }, defineProperty(target, prop, descriptor) { ensureLazyRootCertificates(target); return ReflectDefineProperty(target, prop, descriptor); }, deleteProperty(target, prop) { ensureLazyRootCertificates(target); return ReflectDeleteProperty(target, prop); }, isExtensible(target) { ensureLazyRootCertificates(target); return ReflectIsExtensible(target); }, preventExtensions(target) { ensureLazyRootCertificates(target); return ReflectPreventExtensions(target); }, setPrototypeOf() { return false; }, }); const DEFAULT_ECDH_CURVE = "auto"; const CLIENT_RENEG_LIMIT = 3; const CLIENT_RENEG_WINDOW = 600; class CryptoStream {} class SecurePair {} function getDefaultMaxVersion(): string { if (getOptionValue("--tls-max-v1.3")) return "TLSv1.3"; if (getOptionValue("--tls-max-v1.2")) return "TLSv1.2"; return "TLSv1.3"; } function getDefaultMinVersion(): string { if (getOptionValue("--tls-min-v1.0")) return "TLSv1"; if (getOptionValue("--tls-min-v1.1")) return "TLSv1.1"; if (getOptionValue("--tls-min-v1.2")) return "TLSv1.2"; if (getOptionValue("--tls-min-v1.3")) return "TLSv1.3"; return "TLSv1.2"; } let hasDefaultCACertificatesOverride = false; function hasStore(store: string): boolean { const storesValue = op_get_env_no_permission_check("DENO_TLS_CA_STORE"); if (storesValue == null) { return false; } const stores = StringPrototypeSplit(storesValue, ","); for (let i = 0; i < stores.length; i++) { if (StringPrototypeTrim(stores[i]) === store) { return true; } } return false; } function getDefaultUsesSystemCertificates(): boolean { const useSystemCaExecArgvOption = getExecArgvOptionValue("--use-system-ca"); if (useSystemCaExecArgvOption !== undefined) { return !!useSystemCaExecArgvOption; } const useSystemCaOption = getOptionValue("--use-system-ca"); if ( globalThis.process?.env?.NODE_USE_SYSTEM_CA === "1" || op_get_env_no_permission_check("NODE_USE_SYSTEM_CA") === "1" ) { return true; } if (useSystemCaOption === true) { return true; } if (useSystemCaOption === false) { return false; } if (hasStore("system")) { return true; } return false; } function getDefaultUsesBundledCertificates(): boolean { const storesValue = op_get_env_no_permission_check("DENO_TLS_CA_STORE"); if (storesValue != null) { return hasStore("mozilla"); } if (getOptionValue("--use-openssl-ca")) { return false; } const useBundledCaOption = getOptionValue("--use-bundled-ca"); return useBundledCaOption !== false; } function makeNodeCryptoError(code: string, message: string) { const err = new Error(message); (err as Error & { code?: string }).code = code; return err; } function validateDefaultCACertificates(certs: string[]) { let validCount = 0; for (let i = 0; i < certs.length; i++) { try { new X509Certificate(certs[i]); validCount++; } catch { if (validCount === 0) { throw makeNodeCryptoError( "ERR_CRYPTO_OPERATION_FAILED", "No valid certificates found in the provided array", ); } throw makeNodeCryptoError( "ERR_OSSL_PEM_ASN.1_ENCODING_ROUTINES", "error:0680009B:asn1 encoding routines::too long", ); } } } function setDefaultCACertificates( certs: (string | ArrayBufferView)[], ) { if (!ArrayIsArray(certs)) { throw new ERR_INVALID_ARG_TYPE("certs", "Array", certs); } const normalized: string[] = []; const seen = new SafeSet(); for (let i = 0; i < certs.length; ++i) { const cert = certs[i]; let normalizedCert; if (typeof cert === "string") { normalizedCert = cert; } else if (isArrayBufferView(cert)) { normalizedCert = arrayBufferViewToString(cert); } else { throw new ERR_INVALID_ARG_TYPE( `certs[${i}]`, ["string", "ArrayBufferView"], cert, ); } if (!SetPrototypeHas(seen, normalizedCert)) { SetPrototypeAdd(seen, normalizedCert); ArrayPrototypePush(normalized, normalizedCert); } } validateDefaultCACertificates(normalized); op_set_default_ca_certificates(normalized); hasDefaultCACertificatesOverride = true; // The bundled root certificates (`rootCertificates` proxy / // `lazyRootCertificates`) come from the webpki Mozilla bundle and don't // change here, so don't invalidate them: doing so would re-enter // `ensureLazyRootCertificates` and try to mutate a frozen target. // Only the 'default' cache reflects what we just wrote. ArrayPrototypeForEach( ObjectKeys(cachedCACertificates), (key) => { if (key !== "bundled") { delete cachedCACertificates[key]; } }, ); } function getCACertificates(type: string = "default"): string[] { validateString(type, "type"); validateOneOf(type, "type", ["default", "system", "bundled", "extra"]); if (cachedCACertificates[type] !== undefined) { return cachedCACertificates[type]; } let certs: string[]; if (type === "default" && !hasDefaultCACertificatesOverride) { certs = []; if (getDefaultUsesBundledCertificates()) { ArrayPrototypeForEach( getCACertificates("bundled"), (cert) => ArrayPrototypePush(certs, cert), ); } if (getDefaultUsesSystemCertificates()) { ArrayPrototypeForEach( getCACertificates("system"), (cert) => ArrayPrototypePush(certs, cert), ); } ArrayPrototypeForEach( getCACertificates("extra"), (cert) => ArrayPrototypePush(certs, cert), ); ObjectFreeze(certs); emitDefaultCertificatesDebug(); } else if (type === "bundled") { certs = rootCertificates; } else { certs = ObjectFreeze(op_node_get_ca_certificates(type)) as string[]; if (type === "default") { emitDefaultCertificatesDebug(); } } cachedCACertificates[type] = certs; return certs; } function createSecurePair() { notImplemented("tls.createSecurePair"); } return { CryptoStream, SecurePair, convertALPNProtocols, getCiphers, getCACertificates, setDefaultCACertificates, createSecurePair, rootCertificates, DEFAULT_ECDH_CURVE, get DEFAULT_MAX_VERSION() { return getDefaultMaxVersion(); }, get DEFAULT_MIN_VERSION() { return getDefaultMinVersion(); }, CLIENT_RENEG_LIMIT, CLIENT_RENEG_WINDOW, }; })();