refactor tls version resolution

This commit is contained in:
Alistair Smith
2025-05-09 16:54:28 -07:00
parent 7d5ae23b2e
commit 0b9cdc069b
3 changed files with 128 additions and 158 deletions

View File

@@ -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<import("node:tls").SecureVersion, number>;
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 };

View File

@@ -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;

View File

@@ -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<string, typeof InternalSecureContext> | 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);