From 0b9cdc069b217bfca789c9c96f72e764b337bfa9 Mon Sep 17 00:00:00 2001 From: Alistair Smith Date: Fri, 9 May 2025 16:54:28 -0700 Subject: [PATCH] refactor tls version resolution --- src/js/internal/tls.ts | 116 +++++++++++++++++++++++++++- src/js/node/net.ts | 4 +- src/js/node/tls.ts | 166 +++-------------------------------------- 3 files changed, 128 insertions(+), 158 deletions(-) diff --git a/src/js/internal/tls.ts b/src/js/internal/tls.ts index f02c0edb93..ba474edec7 100644 --- a/src/js/internal/tls.ts +++ b/src/js/internal/tls.ts @@ -1,5 +1,17 @@ const { isTypedArray, isArrayBuffer } = require("node:util/types"); +const getDefaultMinTLSVersionFromCLIFlag = $newZigFunction( + "node_tls_binding.zig", + "getDefaultMinTLSVersionFromCLIFlag", + 0, +) as () => number | null; + +const getDefaultMaxTLSVersionFromCLIFlag = $newZigFunction( + "node_tls_binding.zig", + "getDefaultMaxTLSVersionFromCLIFlag", + 0, +) as () => number | null; + function isPemObject(obj: unknown): obj is { pem: unknown } { return $isObject(obj) && "pem" in obj; } @@ -50,4 +62,106 @@ function isValidTLSArray(obj: unknown) { const VALID_TLS_ERROR_MESSAGE_TYPES = "string or an instance of Buffer, TypedArray, DataView, or BunFile"; -export { VALID_TLS_ERROR_MESSAGE_TYPES, isValidTLSArray, isValidTLSItem, throwOnInvalidTLSArray }; +function getTlsVersionOrDefault(version: number | null, fallback: import("node:tls").SecureVersion) { + if (!version) return fallback; + const asString = TLS_VERSION_REVERSE_MAP[version]; + if (!asString) return fallback; + return asString; +} + +const DEFAULT_MIN_VERSION: import("node:tls").SecureVersion = getTlsVersionOrDefault( + getDefaultMinTLSVersionFromCLIFlag(), + "TLSv1.2", +); +const DEFAULT_MAX_VERSION: import("node:tls").SecureVersion = getTlsVersionOrDefault( + getDefaultMaxTLSVersionFromCLIFlag(), + "TLSv1.3", +); + +const TLS_VERSION_MAP = { + "TLSv1": 0x0301, + "TLSv1.1": 0x0302, + "TLSv1.2": 0x0303, + "TLSv1.3": 0x0304, +} as const satisfies Record; + +const TLS_VERSION_REVERSE_MAP: { + [Key in keyof typeof TLS_VERSION_MAP as (typeof TLS_VERSION_MAP)[Key]]: Key; +} = { + 0x0301: "TLSv1", + 0x0302: "TLSv1.1", + 0x0303: "TLSv1.2", + 0x0304: "TLSv1.3", +}; + +function resolveTLSVersions(options: import("node:tls").TLSSocketOptions): [min: number, max: number] { + const secureProtocol = options?.secureProtocol; + + const hasSecureProtocol = secureProtocol !== undefined; + const hasMinVersionOption = options && options.minVersion !== undefined; + const hasMaxVersionOption = options && options.maxVersion !== undefined; + + if (hasSecureProtocol && (hasMinVersionOption || hasMaxVersionOption)) { + throw $ERR_TLS_PROTOCOL_VERSION_CONFLICT( + TLS_VERSION_REVERSE_MAP[hasMinVersionOption ? options.minVersion : options.maxVersion], + secureProtocol, + ); + } + + let minVersionName: import("node:tls").SecureVersion; + let maxVersionName: import("node:tls").SecureVersion; + + if (hasSecureProtocol) { + if (typeof secureProtocol !== "string") { + throw $ERR_INVALID_ARG_TYPE("options.secureProtocol", "string", secureProtocol); + } + switch (secureProtocol) { + case "TLSv1_method": + minVersionName = maxVersionName = "TLSv1"; + break; + case "TLSv1_1_method": + minVersionName = maxVersionName = "TLSv1.1"; + break; + case "TLSv1_2_method": + minVersionName = maxVersionName = "TLSv1.2"; + break; + case "TLSv1_3_method": + minVersionName = maxVersionName = "TLSv1.3"; + break; + case "TLS_method": + case "SSLv23_method": + minVersionName = DEFAULT_MIN_VERSION; + maxVersionName = DEFAULT_MAX_VERSION; + break; + default: + throw $ERR_TLS_INVALID_PROTOCOL_METHOD(secureProtocol); + } + } else { + minVersionName = options && options.minVersion !== undefined ? options.minVersion : DEFAULT_MIN_VERSION; + maxVersionName = options && options.maxVersion !== undefined ? options.maxVersion : DEFAULT_MAX_VERSION; + } + + if (minVersionName) { + if (typeof minVersionName !== "string") { + throw $ERR_INVALID_ARG_TYPE("options.minVersion", "string", minVersionName); + } + if (!(minVersionName in TLS_VERSION_MAP)) { + throw $ERR_TLS_INVALID_PROTOCOL_VERSION(minVersionName, "minimum"); + } + } + const minVersion = TLS_VERSION_MAP[minVersionName]; + + if (maxVersionName) { + if (typeof maxVersionName !== "string") { + throw $ERR_INVALID_ARG_TYPE("options.maxVersion", "string", maxVersionName); + } + if (!(maxVersionName in TLS_VERSION_MAP)) { + throw $ERR_TLS_INVALID_PROTOCOL_VERSION(maxVersionName, "maximum"); + } + } + const maxVersion = TLS_VERSION_MAP[maxVersionName]; + + return [minVersion, maxVersion]; +} + +export { isValidTLSArray, isValidTLSItem, resolveTLSVersions, throwOnInvalidTLSArray, VALID_TLS_ERROR_MESSAGE_TYPES }; diff --git a/src/js/node/net.ts b/src/js/node/net.ts index f080e0a336..0d58ce64fa 100644 --- a/src/js/node/net.ts +++ b/src/js/node/net.ts @@ -353,9 +353,7 @@ const ServerHandlers: SocketHandler = { const { pauseOnConnect, connectionListener, [kSocketClass]: SClass, requestCert, rejectUnauthorized } = options; - console.log(options); - - const _socket = new SClass(options); + const _socket = new SClass({}); _socket.isServer = true; _socket.server = self; diff --git a/src/js/node/tls.ts b/src/js/node/tls.ts index dd22e8a89c..91b477bfb8 100644 --- a/src/js/node/tls.ts +++ b/src/js/node/tls.ts @@ -1,4 +1,3 @@ -// Hardcoded module "node:tls" import type { SecureVersion } from "node:tls"; const { isArrayBufferView, isTypedArray } = require("node:util/types"); @@ -6,26 +5,17 @@ const net = require("node:net"); const { Duplex } = require("node:stream"); const [addServerName] = $zig("socket.zig", "createNodeTLSBinding"); const { throwNotImplemented } = require("internal/shared"); -const { throwOnInvalidTLSArray } = require("internal/tls"); +const { + throwOnInvalidTLSArray, + resolveTLSVersions, + DEFAULT_MIN_VERSION, + DEFAULT_MAX_VERSION, +} = require("internal/tls"); const { Server: NetServer, Socket: NetSocket } = net; const { rootCertificates, canonicalizeIP } = $cpp("NodeTLS.cpp", "createNodeTLSBinding"); -type TLSSecureVersionNumber = SecureVersion extends `TLSv${infer N extends number}` ? N : never; - -const getDefaultMinTLSVersionFromCLIFlag = $newZigFunction( - "node_tls_binding.zig", - "getDefaultMinTLSVersionFromCLIFlag", - 0, -) as () => TLSSecureVersionNumber | null; - -const getDefaultMaxTLSVersionFromCLIFlag = $newZigFunction( - "node_tls_binding.zig", - "getDefaultMaxTLSVersionFromCLIFlag", - 0, -) as () => TLSSecureVersionNumber | null; - const SymbolReplace = Symbol.replace; const RegExpPrototypeSymbolReplace = RegExp.prototype[SymbolReplace]; const RegExpPrototypeExec = RegExp.prototype.exec; @@ -329,71 +319,9 @@ var InternalSecureContext = class SecureContext { } this.secureOptions = secureOptions; - const secureProtocol = options.secureProtocol; - const hasSecureProtocol = secureProtocol !== undefined; - const hasMinVersionOption = options.minVersion !== undefined; - const hasMaxVersionOption = options.maxVersion !== undefined; - - if (hasSecureProtocol && (hasMinVersionOption || hasMaxVersionOption)) { - throw $ERR_TLS_PROTOCOL_VERSION_CONFLICT( - TLS_VERSION_REVERSE_MAP[hasMinVersionOption ? options.minVersion : options.maxVersion], - secureProtocol, - ); - } - - let minVersionName: SecureVersion | undefined; - let maxVersionName: SecureVersion | undefined; - - if (hasSecureProtocol) { - if (typeof secureProtocol !== "string") { - throw $ERR_INVALID_ARG_TYPE("options.secureProtocol", "string", secureProtocol); - } - - switch (secureProtocol) { - case "TLSv1_method": - minVersionName = maxVersionName = "TLSv1"; - break; - case "TLSv1_1_method": - minVersionName = maxVersionName = "TLSv1.1"; - break; - case "TLSv1_2_method": - minVersionName = maxVersionName = "TLSv1.2"; - break; - case "TLSv1_3_method": - minVersionName = maxVersionName = "TLSv1.3"; - break; - case "TLS_method": - case "SSLv23_method": - minVersionName = DEFAULT_MIN_VERSION; - maxVersionName = DEFAULT_MAX_VERSION; - break; - default: - throw $ERR_TLS_INVALID_PROTOCOL_METHOD(secureProtocol); - } - } else { - minVersionName = options.minVersion !== undefined ? options.minVersion : DEFAULT_MIN_VERSION; - maxVersionName = options.maxVersion !== undefined ? options.maxVersion : DEFAULT_MAX_VERSION; - } - - if (minVersionName) { - if (typeof minVersionName !== "string") { - throw $ERR_INVALID_ARG_TYPE("options.minVersion", "string", minVersionName); - } - if (!(minVersionName in TLS_VERSION_MAP)) { - throw $ERR_TLS_INVALID_PROTOCOL_VERSION(minVersionName, "minimum"); - } - } - this.minVersion = TLS_VERSION_MAP[minVersionName]; - - if (maxVersionName) { - if (typeof maxVersionName !== "string") { - throw $ERR_INVALID_ARG_TYPE("options.maxVersion", "string", maxVersionName); - } - if (!(maxVersionName in TLS_VERSION_MAP)) { - throw $ERR_TLS_INVALID_PROTOCOL_VERSION(maxVersionName, "maximum"); - } - } - this.maxVersion = TLS_VERSION_MAP[maxVersionName]; + const [minVersion, maxVersion] = resolveTLSVersions(options); + this.minVersion = minVersion; + this.maxVersion = maxVersion; } this.context = context; @@ -651,69 +579,9 @@ function Server(options, secureConnectionListener): void { this.servername = undefined; this.ALPNProtocols = undefined; - const secureProtocol = options?.secureProtocol; - const hasSecureProtocol = secureProtocol !== undefined; - const hasMinVersionOption = options && options.minVersion !== undefined; - const hasMaxVersionOption = options && options.maxVersion !== undefined; - - if (hasSecureProtocol && (hasMinVersionOption || hasMaxVersionOption)) { - throw $ERR_TLS_PROTOCOL_VERSION_CONFLICT( - TLS_VERSION_REVERSE_MAP[hasMinVersionOption ? options.minVersion : options.maxVersion], - secureProtocol, - ); - } - - let minVersionName; - let maxVersionName; - - if (hasSecureProtocol) { - if (typeof secureProtocol !== "string") { - throw $ERR_INVALID_ARG_TYPE("options.secureProtocol", "string", secureProtocol); - } - switch (secureProtocol) { - case "TLSv1_method": - minVersionName = maxVersionName = "TLSv1"; - break; - case "TLSv1_1_method": - minVersionName = maxVersionName = "TLSv1.1"; - break; - case "TLSv1_2_method": - minVersionName = maxVersionName = "TLSv1.2"; - break; - case "TLSv1_3_method": - minVersionName = maxVersionName = "TLSv1.3"; - break; - case "TLS_method": - case "SSLv23_method": - minVersionName = DEFAULT_MIN_VERSION; - maxVersionName = DEFAULT_MAX_VERSION; - break; - default: - throw $ERR_TLS_INVALID_PROTOCOL_METHOD(secureProtocol); - } - } else { - minVersionName = options && options.minVersion !== undefined ? options.minVersion : DEFAULT_MIN_VERSION; - maxVersionName = options && options.maxVersion !== undefined ? options.maxVersion : DEFAULT_MAX_VERSION; - } - - if (minVersionName) { - if (typeof minVersionName !== "string") { - throw $ERR_INVALID_ARG_TYPE("options.minVersion", "string", minVersionName); - } - if (!(minVersionName in TLS_VERSION_MAP)) { - throw $ERR_TLS_INVALID_PROTOCOL_VERSION(minVersionName, "minimum"); - } - } - this.minVersion = TLS_VERSION_MAP[minVersionName]; - if (maxVersionName) { - if (typeof maxVersionName !== "string") { - throw $ERR_INVALID_ARG_TYPE("options.maxVersion", "string", maxVersionName); - } - if (!(maxVersionName in TLS_VERSION_MAP)) { - throw $ERR_TLS_INVALID_PROTOCOL_VERSION(maxVersionName, "maximum"); - } - } - this.maxVersion = TLS_VERSION_MAP[maxVersionName]; + const [minVersion, maxVersion] = resolveTLSVersions(options || {}); + this.minVersion = minVersion; + this.maxVersion = maxVersion; let contexts: Map | null = null; @@ -875,16 +743,6 @@ const DEFAULT_ECDH_CURVE = "auto", DEFAULT_CIPHERS = "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"; -function getTlsVersionOrDefault(version: number | null, fallback: SecureVersion) { - if (!version) return fallback; - const asString = TLS_VERSION_REVERSE_MAP[version]; - if (!asString) return fallback; - return asString; -} - -const DEFAULT_MIN_VERSION: SecureVersion = getTlsVersionOrDefault(getDefaultMinTLSVersionFromCLIFlag(), "TLSv1.2"); -const DEFAULT_MAX_VERSION: SecureVersion = getTlsVersionOrDefault(getDefaultMaxTLSVersionFromCLIFlag(), "TLSv1.3"); - function normalizeConnectArgs(listArgs) { // Cast to any to use internal _normalizeArgs helper as in Node.js implementation const args = (net as any)._normalizeArgs(listArgs);