import * as tls from 'node:tls'; export interface ClientHelloSpec { /** All cipher suite IDs in wire order, including GREASE and TLS 1.3 */ cipherSuites: number[]; /** All extensions in wire order, including GREASE. * data is required for non-predefined extensions (OpenSSL rejects custom exts without it). * data is ignored for predefined extensions (OpenSSL generates their content). * For known custom exts (e.g. SCT=18), a sensible default is used if data is omitted. */ extensions: Array<{ type: number; data?: Buffer; }>; /** Supported group IDs in wire order, including GREASE */ supportedGroups: number[]; /** Signature algorithm IDs in wire order */ signatureAlgorithms: number[]; /** The versions from the supported_versions extension (43), in wire order, * including GREASE. Omit for a hello with no supported_versions extension: * such a client is pre-TLS1.3, so the context is capped by legacyVersion. */ supportedVersions?: number[]; /** The ClientHello's legacy client_version field (e.g. 0x0303 for TLS 1.2). * Used as the version ceiling when there is no supported_versions extension, * which is the only way a pre-TLS1.3 client states what it can speak. * Defaults to TLS 1.2. */ legacyVersion?: number; /** EC point formats. Defaults to [0] (uncompressed) if omitted. * Note: OpenSSL controls EC point format encoding; this field is best-effort. */ ecPointFormats?: number[]; /** ALPN protocol names (e.g. ['h2', 'http/1.1']). * Extracted from the spec into the result's tlsOptions. */ alpnProtocols?: string[]; } /** A part of the requested ClientHello that could not be reproduced. The * closest achievable fingerprint is still produced; this just records the gap * so callers can log or monitor it. */ export interface UnsupportedFeature { kind: 'cipherSuite' | 'supportedGroup' | 'signatureAlgorithm' | 'extension' | 'version'; /** The codepoint from the spec as it appears on the wire */ id: number; reason: string; } export interface ImpersonateResult { tlsOptions: { secureContext: tls.SecureContext; ALPNProtocols?: string[]; requestOCSP?: boolean; }; /** Spec elements that could not be reproduced, empty when the fingerprint * was reproduced fully. impersonate() never throws for these - it produces * the closest fingerprint it can and records the gaps here. */ unsupported: UnsupportedFeature[]; } /** * Thrown when a spec cannot be mirrored at all under the given options, as opposed to * being reproduced with gaps. */ export declare class CannotImpersonateError extends Error { readonly code = "ERR_CANNOT_IMPERSONATE"; readonly unsupported: UnsupportedFeature[]; constructor(reason: string, unsupported: UnsupportedFeature[]); } export interface ImpersonateOptions extends tls.SecureContextOptions { /** * Emulating some settings requires reducing the OpenSSL security level, which * can allow insecure TLS connections. This is disabled by default, but can be * enabled with 'insecure' mode here. * * This isn't required for all insecure features in fingerprints - many can be * supported by advertising them but rejecting if used (as many other clients * already do) so this is only for specific cases where that's not possible. * An issues caused by the default 'secure' mode will be reported in the * 'unsupported' result explicitly. */ security?: 'secure' | 'insecure'; } /** * Whether TLS impersonation works on this runtime at all: the native addon * loaded and Node exposes `node::crypto::GetSSLCtx` (Node >= 24.15). Cheap and * global - call it once and fall back to a default fingerprint if it returns * false, rather than calling impersonate() and catching its runtime throw. * * impersonate() still throws on an unsupported runtime as a programmer-error * guard; this is how callers avoid hitting it. */ export declare function isSupported(): boolean; /** * The parsed-ClientHello shape that clientHelloToSpec / impersonateFromClientHello * read. A read-tls-client-hello `TlsClientHelloMessage` satisfies this structurally * so you can pass one straight in, but we avoid depending on that explicitly. */ export interface ParsedClientHello { cipherSuites: number[]; extensions: Array<{ id: number; data: Record | null; }>; /** The legacy client_version field of the hello. */ version?: number; } /** * Impersonate a captured ClientHello from read-tls-client-hello directly. */ export declare function impersonateFromClientHello(hello: ParsedClientHello, options?: ImpersonateOptions): ImpersonateResult; /** * Create a TLS SecureContext that impersonates the given ClientHello specification. * * Takes a ClientHello spec, and reproduces the TLS fingerprint as closely as possible. */ export declare function impersonate(spec: ClientHelloSpec, options?: ImpersonateOptions): ImpersonateResult; //# sourceMappingURL=index.d.ts.map