UNPKG

node-libcurl-ja3

Version:

Node.js native bindings for libcurl-impersonate. Impersonate Chrome, Edge, Firefox and Safari TLS fingerprints.

139 lines 5.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseFingerprint = void 0; const CurlSslVersion_1 = require("../enum/CurlSslVersion"); const CurlJa3Cipher_1 = require("./enum/CurlJa3Cipher"); const CurlJa3Curve_1 = require("./enum/CurlJa3Curve"); const CurlJa3Extension_1 = require("./enum/CurlJa3Extension"); const CurlJa3SigHashAlg_1 = require("./enum/CurlJa3SigHashAlg"); function parseFingerprint(fp) { const config = {}; if (fp.ja3) { Object.assign(config, parseJa3Fingerprint(fp.ja3, fp.keepExtensionOrder)); } if (fp.ja4) { Object.assign(config, parseJa4Fingerprint(fp.ja4, fp.keepExtensionOrder)); } if (fp.akami) { Object.assign(config, parseAkamiFingerprint(fp.akami)); } return config; } exports.parseFingerprint = parseFingerprint; const EXTENSION_CONFIG_MAPPING = { [CurlJa3Extension_1.CurlJa3Extension.TLSEXT_TYPE_application_settings_old]: (config) => { config.alps = true; }, [CurlJa3Extension_1.CurlJa3Extension.TLSEXT_TYPE_application_settings]: (config) => { config.alps = true; config.tlsUseNewAlpsCodepoint = true; }, [CurlJa3Extension_1.CurlJa3Extension.TLSEXT_TYPE_cert_compression]: (config) => { // brotli is only used by chromium, other browsers must override config.sslCertCompression = 'brotli'; }, [CurlJa3Extension_1.CurlJa3Extension.TLSEXT_TYPE_certificate_timestamp]: (config) => { config.tlsSignedCertTimestamps = true; }, [CurlJa3Extension_1.CurlJa3Extension.TLSEXT_TYPE_encrypted_client_hello]: (config) => { config.ech = 'grease'; }, }; function applyExtensionConfigs(extensionIds, config, keepExtensionOrder) { extensionIds .filter((id) => id in EXTENSION_CONFIG_MAPPING) .forEach((id) => EXTENSION_CONFIG_MAPPING[id](config)); if (!extensionIds.includes(CurlJa3Extension_1.CurlJa3Extension.TLSEXT_TYPE_session_ticket)) { config.tlsSessionTicket = false; } if (keepExtensionOrder) { config.tlsExtensionOrder = extensionIds.join('-'); } } function parseAkamiFingerprint(fingerprint) { const [a, b, , d] = fingerprint.split('|'); return { http2Settings: a, http2WindowUpdate: parseInt(b, 10), http2PseudoHeadersOrder: d.replace(/,/g, ''), }; } function parseCipher(id, base) { const parsed = CurlJa3Cipher_1.CurlJa3Cipher[parseInt(id, base !== null && base !== void 0 ? base : 10)]; if (typeof parsed === 'undefined') { throw new Error(`Unsupported cipher: ${id}`); } return parsed; } function parseCurve(id, base) { const parsed = CurlJa3Curve_1.CurlJa3Curve[parseInt(id, base !== null && base !== void 0 ? base : 10)]; if (typeof parsed === 'undefined') { throw new Error(`Unsupported curve: ${id}`); } return parsed; } function parseJa3Fingerprint(fingerprint, tlsPermuteExtensions) { const [_tlsVersion, _ciphers, _extensions, _curves] = fingerprint.split(','); let tlsVersion; switch (parseInt(_tlsVersion, 10)) { case 771: tlsVersion = CurlSslVersion_1.CurlSslVersion.TlsV1_2; break; case 772: tlsVersion = CurlSslVersion_1.CurlSslVersion.TlsV1_3; break; default: throw new Error(`Unsupported TLS version: ${_tlsVersion}`); } const config = { tlsVersion, ciphers: _ciphers .split('-') .map((id) => parseCipher(id)) .join(':') || undefined, curves: _curves .split('-') .map((id) => parseCurve(id)) .join(':') || undefined, }; applyExtensionConfigs(_extensions.split('-').map((id) => parseInt(id, 10)), config, tlsPermuteExtensions); return config; } /** * @link https://github.com/FoxIO-LLC/ja4/blob/main/technical_details/JA4.md */ function parseJa4Fingerprint(fingerprint, tlsPermuteExtensions) { const [_header, _ciphers, _extensions, _sigalgs] = fingerprint.split('_'); let tlsVersion; switch (_header.slice(1, 3)) { case '12': tlsVersion = CurlSslVersion_1.CurlSslVersion.TlsV1_2; break; case '13': tlsVersion = CurlSslVersion_1.CurlSslVersion.TlsV1_3; break; default: throw new Error(`Unsupported TLS version: ${_header.slice(1, 3)}`); } const config = { tlsVersion, ciphers: _ciphers .split(',') .map((id) => parseCipher(id, 16)) .join(':') || undefined, signatureHashes: _sigalgs .split(',') .map((id) => parseSigHashAlg(id, 16)) .join(',') || undefined, }; applyExtensionConfigs(_extensions.split(',').map((id) => parseInt(id, 16)), config, tlsPermuteExtensions); return config; } function parseSigHashAlg(id, base) { const parsed = CurlJa3SigHashAlg_1.CurlJa3SigHashAlg[parseInt(id, base !== null && base !== void 0 ? base : 10)]; if (typeof parsed === 'undefined') { throw new Error(`Unsupported sig hash alg: ${id}`); } return parsed; } //# sourceMappingURL=fingerprint.js.map