node-libcurl-ja3
Version:
Node.js native bindings for libcurl-impersonate. Impersonate Chrome, Edge, Firefox and Safari TLS fingerprints.
139 lines • 5.2 kB
JavaScript
;
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