nlcurl 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -140
- package/dist/cache/groups.d.ts +75 -0
- package/dist/cache/groups.d.ts.map +1 -0
- package/dist/cache/groups.js +118 -0
- package/dist/cache/groups.js.map +1 -0
- package/dist/cache/no-vary-search.d.ts +33 -0
- package/dist/cache/no-vary-search.d.ts.map +1 -0
- package/dist/cache/no-vary-search.js +148 -0
- package/dist/cache/no-vary-search.js.map +1 -0
- package/dist/cache/range.d.ts +120 -0
- package/dist/cache/range.d.ts.map +1 -0
- package/dist/cache/range.js +193 -0
- package/dist/cache/range.js.map +1 -0
- package/dist/cache/store.d.ts +111 -0
- package/dist/cache/store.d.ts.map +1 -0
- package/dist/cache/store.js +414 -0
- package/dist/cache/store.js.map +1 -0
- package/dist/cache/types.d.ts +69 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +2 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/cli/args.d.ts +4 -37
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +3 -4
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/index.d.ts +0 -5
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/output.d.ts +14 -20
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +14 -20
- package/dist/cli/output.js.map +1 -1
- package/dist/cookies/jar.d.ts +33 -31
- package/dist/cookies/jar.d.ts.map +1 -1
- package/dist/cookies/jar.js +69 -33
- package/dist/cookies/jar.js.map +1 -1
- package/dist/cookies/parser.d.ts +14 -16
- package/dist/cookies/parser.d.ts.map +1 -1
- package/dist/cookies/parser.js +30 -13
- package/dist/cookies/parser.js.map +1 -1
- package/dist/cookies/psl-data.d.ts +1 -1
- package/dist/cookies/psl-data.js +1 -1
- package/dist/cookies/public-suffix.d.ts +5 -27
- package/dist/cookies/public-suffix.d.ts.map +1 -1
- package/dist/cookies/public-suffix.js +5 -37
- package/dist/cookies/public-suffix.js.map +1 -1
- package/dist/core/auth.d.ts +30 -0
- package/dist/core/auth.d.ts.map +1 -0
- package/dist/core/auth.js +34 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/client.d.ts +28 -40
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +34 -41
- package/dist/core/client.js.map +1 -1
- package/dist/core/errors.d.ts +64 -88
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +64 -88
- package/dist/core/errors.js.map +1 -1
- package/dist/core/request.d.ts +63 -85
- package/dist/core/request.d.ts.map +1 -1
- package/dist/core/response.d.ts +64 -59
- package/dist/core/response.d.ts.map +1 -1
- package/dist/core/response.js +70 -52
- package/dist/core/response.js.map +1 -1
- package/dist/core/session.d.ts +81 -70
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +198 -71
- package/dist/core/session.js.map +1 -1
- package/dist/core/validation.d.ts +44 -42
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +47 -58
- package/dist/core/validation.js.map +1 -1
- package/dist/dns/cache.d.ts +59 -0
- package/dist/dns/cache.d.ts.map +1 -0
- package/dist/dns/cache.js +99 -0
- package/dist/dns/cache.js.map +1 -0
- package/dist/dns/codec.d.ts +39 -0
- package/dist/dns/codec.d.ts.map +1 -0
- package/dist/dns/codec.js +238 -0
- package/dist/dns/codec.js.map +1 -0
- package/dist/dns/doh-resolver.d.ts +38 -0
- package/dist/dns/doh-resolver.d.ts.map +1 -0
- package/dist/dns/doh-resolver.js +191 -0
- package/dist/dns/doh-resolver.js.map +1 -0
- package/dist/dns/dot-resolver.d.ts +89 -0
- package/dist/dns/dot-resolver.d.ts.map +1 -0
- package/dist/dns/dot-resolver.js +158 -0
- package/dist/dns/dot-resolver.js.map +1 -0
- package/dist/dns/https-rr.d.ts +40 -0
- package/dist/dns/https-rr.d.ts.map +1 -0
- package/dist/dns/https-rr.js +109 -0
- package/dist/dns/https-rr.js.map +1 -0
- package/dist/dns/types.d.ts +82 -0
- package/dist/dns/types.d.ts.map +1 -0
- package/dist/dns/types.js +23 -0
- package/dist/dns/types.js.map +1 -0
- package/dist/fingerprints/akamai.d.ts +3 -11
- package/dist/fingerprints/akamai.d.ts.map +1 -1
- package/dist/fingerprints/akamai.js +3 -11
- package/dist/fingerprints/akamai.js.map +1 -1
- package/dist/fingerprints/database.d.ts +6 -14
- package/dist/fingerprints/database.d.ts.map +1 -1
- package/dist/fingerprints/database.js +6 -14
- package/dist/fingerprints/database.js.map +1 -1
- package/dist/fingerprints/extensions.d.ts +56 -71
- package/dist/fingerprints/extensions.d.ts.map +1 -1
- package/dist/fingerprints/extensions.js +58 -71
- package/dist/fingerprints/extensions.js.map +1 -1
- package/dist/fingerprints/ja3.d.ts +12 -30
- package/dist/fingerprints/ja3.d.ts.map +1 -1
- package/dist/fingerprints/ja3.js +12 -30
- package/dist/fingerprints/ja3.js.map +1 -1
- package/dist/fingerprints/ja4.d.ts +18 -0
- package/dist/fingerprints/ja4.d.ts.map +1 -0
- package/dist/fingerprints/ja4.js +83 -0
- package/dist/fingerprints/ja4.js.map +1 -0
- package/dist/fingerprints/profiles/chrome.d.ts +18 -21
- package/dist/fingerprints/profiles/chrome.d.ts.map +1 -1
- package/dist/fingerprints/profiles/chrome.js +35 -31
- package/dist/fingerprints/profiles/chrome.js.map +1 -1
- package/dist/fingerprints/profiles/edge.d.ts +7 -10
- package/dist/fingerprints/profiles/edge.d.ts.map +1 -1
- package/dist/fingerprints/profiles/edge.js +7 -10
- package/dist/fingerprints/profiles/edge.js.map +1 -1
- package/dist/fingerprints/profiles/firefox.d.ts +8 -11
- package/dist/fingerprints/profiles/firefox.d.ts.map +1 -1
- package/dist/fingerprints/profiles/firefox.js +8 -11
- package/dist/fingerprints/profiles/firefox.js.map +1 -1
- package/dist/fingerprints/profiles/safari.d.ts +11 -14
- package/dist/fingerprints/profiles/safari.d.ts.map +1 -1
- package/dist/fingerprints/profiles/safari.js +11 -14
- package/dist/fingerprints/profiles/safari.js.map +1 -1
- package/dist/fingerprints/profiles/tor.d.ts +5 -8
- package/dist/fingerprints/profiles/tor.d.ts.map +1 -1
- package/dist/fingerprints/profiles/tor.js +5 -8
- package/dist/fingerprints/profiles/tor.js.map +1 -1
- package/dist/fingerprints/types.d.ts +42 -73
- package/dist/fingerprints/types.d.ts.map +1 -1
- package/dist/hsts/store.d.ts +39 -0
- package/dist/hsts/store.d.ts.map +1 -0
- package/dist/hsts/store.js +163 -0
- package/dist/hsts/store.js.map +1 -0
- package/dist/hsts/types.d.ts +24 -0
- package/dist/hsts/types.d.ts.map +1 -0
- package/dist/hsts/types.js +2 -0
- package/dist/hsts/types.js.map +1 -0
- package/dist/http/alt-svc.d.ts +67 -0
- package/dist/http/alt-svc.d.ts.map +1 -0
- package/dist/http/alt-svc.js +186 -0
- package/dist/http/alt-svc.js.map +1 -0
- package/dist/http/early-hints.d.ts +23 -0
- package/dist/http/early-hints.d.ts.map +1 -0
- package/dist/http/early-hints.js +33 -0
- package/dist/http/early-hints.js.map +1 -0
- package/dist/http/form-data.d.ts +17 -35
- package/dist/http/form-data.d.ts.map +1 -1
- package/dist/http/form-data.js +12 -34
- package/dist/http/form-data.js.map +1 -1
- package/dist/http/h1/client.d.ts +14 -26
- package/dist/http/h1/client.d.ts.map +1 -1
- package/dist/http/h1/client.js +29 -22
- package/dist/http/h1/client.js.map +1 -1
- package/dist/http/h1/encoder.d.ts +10 -17
- package/dist/http/h1/encoder.d.ts.map +1 -1
- package/dist/http/h1/encoder.js +10 -17
- package/dist/http/h1/encoder.js.map +1 -1
- package/dist/http/h1/parser.d.ts +22 -59
- package/dist/http/h1/parser.d.ts.map +1 -1
- package/dist/http/h1/parser.js +15 -47
- package/dist/http/h1/parser.js.map +1 -1
- package/dist/http/h2/client.d.ts +18 -59
- package/dist/http/h2/client.d.ts.map +1 -1
- package/dist/http/h2/client.js +29 -64
- package/dist/http/h2/client.js.map +1 -1
- package/dist/http/h2/frames.d.ts +53 -84
- package/dist/http/h2/frames.d.ts.map +1 -1
- package/dist/http/h2/frames.js +48 -76
- package/dist/http/h2/frames.js.map +1 -1
- package/dist/http/h2/hpack.d.ts +16 -35
- package/dist/http/h2/hpack.d.ts.map +1 -1
- package/dist/http/h2/hpack.js +16 -35
- package/dist/http/h2/hpack.js.map +1 -1
- package/dist/http/h3/detection.d.ts +15 -0
- package/dist/http/h3/detection.d.ts.map +1 -0
- package/dist/http/h3/detection.js +41 -0
- package/dist/http/h3/detection.js.map +1 -0
- package/dist/http/negotiator.d.ts +43 -33
- package/dist/http/negotiator.d.ts.map +1 -1
- package/dist/http/negotiator.js +99 -41
- package/dist/http/negotiator.js.map +1 -1
- package/dist/http/pool.d.ts +34 -64
- package/dist/http/pool.d.ts.map +1 -1
- package/dist/http/pool.js +22 -41
- package/dist/http/pool.js.map +1 -1
- package/dist/http/resumable-upload.d.ts +76 -0
- package/dist/http/resumable-upload.d.ts.map +1 -0
- package/dist/http/resumable-upload.js +104 -0
- package/dist/http/resumable-upload.js.map +1 -0
- package/dist/http/trailers.d.ts +29 -0
- package/dist/http/trailers.d.ts.map +1 -0
- package/dist/http/trailers.js +57 -0
- package/dist/http/trailers.js.map +1 -0
- package/dist/index.d.ts +36 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware/interceptor.d.ts +13 -32
- package/dist/middleware/interceptor.d.ts.map +1 -1
- package/dist/middleware/interceptor.js +11 -16
- package/dist/middleware/interceptor.js.map +1 -1
- package/dist/middleware/rate-limiter.d.ts +8 -17
- package/dist/middleware/rate-limiter.d.ts.map +1 -1
- package/dist/middleware/rate-limiter.js +15 -12
- package/dist/middleware/rate-limiter.js.map +1 -1
- package/dist/middleware/retry-after.d.ts +15 -0
- package/dist/middleware/retry-after.d.ts.map +1 -0
- package/dist/middleware/retry-after.js +36 -0
- package/dist/middleware/retry-after.js.map +1 -0
- package/dist/middleware/retry.d.ts +9 -18
- package/dist/middleware/retry.d.ts.map +1 -1
- package/dist/middleware/retry.js +12 -9
- package/dist/middleware/retry.js.map +1 -1
- package/dist/proxy/auth.d.ts +73 -0
- package/dist/proxy/auth.d.ts.map +1 -0
- package/dist/proxy/auth.js +128 -0
- package/dist/proxy/auth.js.map +1 -0
- package/dist/proxy/env-proxy.d.ts +11 -0
- package/dist/proxy/env-proxy.d.ts.map +1 -0
- package/dist/proxy/env-proxy.js +56 -0
- package/dist/proxy/env-proxy.js.map +1 -0
- package/dist/proxy/http-proxy.d.ts +13 -18
- package/dist/proxy/http-proxy.d.ts.map +1 -1
- package/dist/proxy/http-proxy.js +24 -14
- package/dist/proxy/http-proxy.js.map +1 -1
- package/dist/proxy/socks.d.ts +13 -20
- package/dist/proxy/socks.d.ts.map +1 -1
- package/dist/proxy/socks.js +6 -9
- package/dist/proxy/socks.js.map +1 -1
- package/dist/sse/parser.d.ts +47 -0
- package/dist/sse/parser.d.ts.map +1 -0
- package/dist/sse/parser.js +139 -0
- package/dist/sse/parser.js.map +1 -0
- package/dist/tls/constants.d.ts +15 -74
- package/dist/tls/constants.d.ts.map +1 -1
- package/dist/tls/constants.js +15 -74
- package/dist/tls/constants.js.map +1 -1
- package/dist/tls/ct.d.ts +78 -0
- package/dist/tls/ct.d.ts.map +1 -0
- package/dist/tls/ct.js +175 -0
- package/dist/tls/ct.js.map +1 -0
- package/dist/tls/early-data.d.ts +45 -0
- package/dist/tls/early-data.d.ts.map +1 -0
- package/dist/tls/early-data.js +46 -0
- package/dist/tls/early-data.js.map +1 -0
- package/dist/tls/ech.d.ts +130 -0
- package/dist/tls/ech.d.ts.map +1 -0
- package/dist/tls/ech.js +353 -0
- package/dist/tls/ech.js.map +1 -0
- package/dist/tls/keylog.d.ts +34 -0
- package/dist/tls/keylog.d.ts.map +1 -0
- package/dist/tls/keylog.js +64 -0
- package/dist/tls/keylog.js.map +1 -0
- package/dist/tls/node-engine.d.ts +15 -14
- package/dist/tls/node-engine.d.ts.map +1 -1
- package/dist/tls/node-engine.js +54 -14
- package/dist/tls/node-engine.js.map +1 -1
- package/dist/tls/ocsp.d.ts +55 -0
- package/dist/tls/ocsp.d.ts.map +1 -0
- package/dist/tls/ocsp.js +131 -0
- package/dist/tls/ocsp.js.map +1 -0
- package/dist/tls/pin-verification.d.ts +10 -0
- package/dist/tls/pin-verification.d.ts.map +1 -0
- package/dist/tls/pin-verification.js +28 -0
- package/dist/tls/pin-verification.js.map +1 -0
- package/dist/tls/session-cache.d.ts +58 -0
- package/dist/tls/session-cache.d.ts.map +1 -0
- package/dist/tls/session-cache.js +76 -0
- package/dist/tls/session-cache.js.map +1 -0
- package/dist/tls/stealth/client-hello.d.ts +34 -32
- package/dist/tls/stealth/client-hello.d.ts.map +1 -1
- package/dist/tls/stealth/client-hello.js +116 -11
- package/dist/tls/stealth/client-hello.js.map +1 -1
- package/dist/tls/stealth/engine.d.ts +5 -15
- package/dist/tls/stealth/engine.d.ts.map +1 -1
- package/dist/tls/stealth/engine.js +163 -47
- package/dist/tls/stealth/engine.js.map +1 -1
- package/dist/tls/stealth/handshake.d.ts +23 -32
- package/dist/tls/stealth/handshake.d.ts.map +1 -1
- package/dist/tls/stealth/handshake.js +112 -27
- package/dist/tls/stealth/handshake.js.map +1 -1
- package/dist/tls/stealth/key-schedule.d.ts +59 -86
- package/dist/tls/stealth/key-schedule.d.ts.map +1 -1
- package/dist/tls/stealth/key-schedule.js +46 -58
- package/dist/tls/stealth/key-schedule.js.map +1 -1
- package/dist/tls/stealth/record-layer.d.ts +52 -75
- package/dist/tls/stealth/record-layer.d.ts.map +1 -1
- package/dist/tls/stealth/record-layer.js +47 -63
- package/dist/tls/stealth/record-layer.js.map +1 -1
- package/dist/tls/stealth/tls12-handshake.d.ts +30 -0
- package/dist/tls/stealth/tls12-handshake.d.ts.map +1 -0
- package/dist/tls/stealth/tls12-handshake.js +470 -0
- package/dist/tls/stealth/tls12-handshake.js.map +1 -0
- package/dist/tls/types.d.ts +46 -48
- package/dist/tls/types.d.ts.map +1 -1
- package/dist/utils/buffer-reader.d.ts +26 -81
- package/dist/utils/buffer-reader.d.ts.map +1 -1
- package/dist/utils/buffer-reader.js +26 -81
- package/dist/utils/buffer-reader.js.map +1 -1
- package/dist/utils/buffer-writer.d.ts +30 -66
- package/dist/utils/buffer-writer.d.ts.map +1 -1
- package/dist/utils/buffer-writer.js +30 -66
- package/dist/utils/buffer-writer.js.map +1 -1
- package/dist/utils/compression.d.ts +18 -0
- package/dist/utils/compression.d.ts.map +1 -0
- package/dist/utils/compression.js +34 -0
- package/dist/utils/compression.js.map +1 -0
- package/dist/utils/dictionary-transport.d.ts +97 -0
- package/dist/utils/dictionary-transport.d.ts.map +1 -0
- package/dist/utils/dictionary-transport.js +171 -0
- package/dist/utils/dictionary-transport.js.map +1 -0
- package/dist/utils/encoding.d.ts +12 -28
- package/dist/utils/encoding.d.ts.map +1 -1
- package/dist/utils/encoding.js +77 -40
- package/dist/utils/encoding.js.map +1 -1
- package/dist/utils/happy-eyeballs.d.ts +20 -7
- package/dist/utils/happy-eyeballs.d.ts.map +1 -1
- package/dist/utils/happy-eyeballs.js +57 -25
- package/dist/utils/happy-eyeballs.js.map +1 -1
- package/dist/utils/logger.d.ts +54 -81
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +92 -64
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/tcp-fast-open.d.ts +30 -0
- package/dist/utils/tcp-fast-open.d.ts.map +1 -0
- package/dist/utils/tcp-fast-open.js +36 -0
- package/dist/utils/tcp-fast-open.js.map +1 -0
- package/dist/utils/url.d.ts +18 -25
- package/dist/utils/url.d.ts.map +1 -1
- package/dist/utils/url.js +18 -25
- package/dist/utils/url.js.map +1 -1
- package/dist/ws/client.d.ts +35 -52
- package/dist/ws/client.d.ts.map +1 -1
- package/dist/ws/client.js +92 -37
- package/dist/ws/client.js.map +1 -1
- package/dist/ws/frame.d.ts +27 -42
- package/dist/ws/frame.d.ts.map +1 -1
- package/dist/ws/frame.js +25 -37
- package/dist/ws/frame.js.map +1 -1
- package/dist/ws/permessage-deflate.d.ts +53 -0
- package/dist/ws/permessage-deflate.d.ts.map +1 -0
- package/dist/ws/permessage-deflate.js +140 -0
- package/dist/ws/permessage-deflate.js.map +1 -0
- package/package.json +62 -62
package/dist/tls/ech.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { randomBytes, createHmac, createCipheriv, generateKeyPairSync, diffieHellman, createPrivateKey, createPublicKey } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Parse a serialized ECHConfigList into structured parameters.
|
|
4
|
+
*
|
|
5
|
+
* @param {Buffer} data - Raw ECHConfigList buffer.
|
|
6
|
+
* @returns {ECHParameters|null} Parsed parameters, or `null` if the data is invalid.
|
|
7
|
+
*/
|
|
8
|
+
export function parseECHConfigList(data) {
|
|
9
|
+
if (data.length < 4)
|
|
10
|
+
return null;
|
|
11
|
+
const totalLength = data.readUInt16BE(0);
|
|
12
|
+
if (totalLength + 2 > data.length)
|
|
13
|
+
return null;
|
|
14
|
+
const configs = [];
|
|
15
|
+
let offset = 2;
|
|
16
|
+
while (offset + 4 <= 2 + totalLength) {
|
|
17
|
+
const version = data.readUInt16BE(offset);
|
|
18
|
+
offset += 2;
|
|
19
|
+
const configLength = data.readUInt16BE(offset);
|
|
20
|
+
offset += 2;
|
|
21
|
+
if (offset + configLength > 2 + totalLength)
|
|
22
|
+
break;
|
|
23
|
+
const contents = data.subarray(offset, offset + configLength);
|
|
24
|
+
const publicName = extractPublicName(contents);
|
|
25
|
+
configs.push({
|
|
26
|
+
version,
|
|
27
|
+
length: configLength,
|
|
28
|
+
contents,
|
|
29
|
+
publicName,
|
|
30
|
+
});
|
|
31
|
+
offset += configLength;
|
|
32
|
+
}
|
|
33
|
+
if (configs.length === 0)
|
|
34
|
+
return null;
|
|
35
|
+
const outerSNI = configs[0].publicName;
|
|
36
|
+
return {
|
|
37
|
+
echConfigList: data,
|
|
38
|
+
outerSNI,
|
|
39
|
+
configs,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function extractPublicName(contents) {
|
|
43
|
+
if (contents.length < 7)
|
|
44
|
+
return "";
|
|
45
|
+
let offset = 0;
|
|
46
|
+
offset += 1;
|
|
47
|
+
offset += 2;
|
|
48
|
+
if (offset + 2 > contents.length)
|
|
49
|
+
return "";
|
|
50
|
+
const pkLen = contents.readUInt16BE(offset);
|
|
51
|
+
offset += 2 + pkLen;
|
|
52
|
+
if (offset + 2 > contents.length)
|
|
53
|
+
return "";
|
|
54
|
+
const csLen = contents.readUInt16BE(offset);
|
|
55
|
+
offset += 2 + csLen;
|
|
56
|
+
if (offset >= contents.length)
|
|
57
|
+
return "";
|
|
58
|
+
offset += 1;
|
|
59
|
+
if (offset >= contents.length)
|
|
60
|
+
return "";
|
|
61
|
+
const nameLen = contents[offset];
|
|
62
|
+
offset += 1;
|
|
63
|
+
if (offset + nameLen > contents.length)
|
|
64
|
+
return "";
|
|
65
|
+
return contents.subarray(offset, offset + nameLen).toString("ascii");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generate a GREASE Encrypted Client Hello extension payload.
|
|
69
|
+
*
|
|
70
|
+
* @returns {Buffer} Random GREASE ECH extension data.
|
|
71
|
+
*/
|
|
72
|
+
export function generateGreaseECH() {
|
|
73
|
+
const payloadLen = 128 + Math.floor(Math.random() * 64);
|
|
74
|
+
const buf = Buffer.alloc(1 + 4 + 1 + 2 + 32 + 2 + payloadLen);
|
|
75
|
+
let offset = 0;
|
|
76
|
+
buf[offset++] = 0x00;
|
|
77
|
+
buf.writeUInt16BE(0x0001, offset);
|
|
78
|
+
offset += 2;
|
|
79
|
+
buf.writeUInt16BE(0x0001, offset);
|
|
80
|
+
offset += 2;
|
|
81
|
+
buf[offset++] = randomBytes(1)[0];
|
|
82
|
+
const enc = randomBytes(32);
|
|
83
|
+
buf.writeUInt16BE(32, offset);
|
|
84
|
+
offset += 2;
|
|
85
|
+
enc.copy(buf, offset);
|
|
86
|
+
offset += 32;
|
|
87
|
+
const payload = randomBytes(payloadLen);
|
|
88
|
+
buf.writeUInt16BE(payloadLen, offset);
|
|
89
|
+
offset += 2;
|
|
90
|
+
payload.copy(buf, offset);
|
|
91
|
+
return buf;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Parse the HPKE key configuration from ECHConfig contents.
|
|
95
|
+
*
|
|
96
|
+
* @param {Buffer} contents - Raw contents buffer of an ECHConfig entry.
|
|
97
|
+
* @returns {HpkeKeyConfig|null} Parsed HPKE key config, or `null` if malformed.
|
|
98
|
+
*/
|
|
99
|
+
export function parseHpkeKeyConfig(contents) {
|
|
100
|
+
if (contents.length < 7)
|
|
101
|
+
return null;
|
|
102
|
+
let offset = 0;
|
|
103
|
+
const configId = contents[offset];
|
|
104
|
+
offset += 1;
|
|
105
|
+
const kemId = contents.readUInt16BE(offset);
|
|
106
|
+
offset += 2;
|
|
107
|
+
if (offset + 2 > contents.length)
|
|
108
|
+
return null;
|
|
109
|
+
const pkLen = contents.readUInt16BE(offset);
|
|
110
|
+
offset += 2;
|
|
111
|
+
if (offset + pkLen > contents.length)
|
|
112
|
+
return null;
|
|
113
|
+
const publicKey = Buffer.from(contents.subarray(offset, offset + pkLen));
|
|
114
|
+
offset += pkLen;
|
|
115
|
+
if (offset + 2 > contents.length)
|
|
116
|
+
return null;
|
|
117
|
+
const csLen = contents.readUInt16BE(offset);
|
|
118
|
+
offset += 2;
|
|
119
|
+
const cipherSuites = [];
|
|
120
|
+
const csEnd = offset + csLen;
|
|
121
|
+
while (offset + 4 <= csEnd) {
|
|
122
|
+
const kdfId = contents.readUInt16BE(offset);
|
|
123
|
+
offset += 2;
|
|
124
|
+
const aeadId = contents.readUInt16BE(offset);
|
|
125
|
+
offset += 2;
|
|
126
|
+
cipherSuites.push({ kdfId, aeadId });
|
|
127
|
+
}
|
|
128
|
+
return { configId, kemId, publicKey, cipherSuites };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Extract the maximum name length field from ECHConfig contents.
|
|
132
|
+
*
|
|
133
|
+
* @param {Buffer} contents - Raw ECHConfig contents.
|
|
134
|
+
* @returns {number} Maximum name length, or `0` if unparseable.
|
|
135
|
+
*/
|
|
136
|
+
export function getMaxNameLength(contents) {
|
|
137
|
+
if (contents.length < 7)
|
|
138
|
+
return 0;
|
|
139
|
+
let offset = 0;
|
|
140
|
+
offset += 1;
|
|
141
|
+
offset += 2;
|
|
142
|
+
if (offset + 2 > contents.length)
|
|
143
|
+
return 0;
|
|
144
|
+
const pkLen = contents.readUInt16BE(offset);
|
|
145
|
+
offset += 2 + pkLen;
|
|
146
|
+
if (offset + 2 > contents.length)
|
|
147
|
+
return 0;
|
|
148
|
+
const csLen = contents.readUInt16BE(offset);
|
|
149
|
+
offset += 2 + csLen;
|
|
150
|
+
if (offset >= contents.length)
|
|
151
|
+
return 0;
|
|
152
|
+
return contents[offset];
|
|
153
|
+
}
|
|
154
|
+
function hpkeHkdfExtract(salt, ikm) {
|
|
155
|
+
const s = salt.length === 0 ? Buffer.alloc(32) : salt;
|
|
156
|
+
return Buffer.from(createHmac("sha256", s).update(ikm).digest());
|
|
157
|
+
}
|
|
158
|
+
function hpkeHkdfExpand(prk, info, length) {
|
|
159
|
+
const N = Math.ceil(length / 32);
|
|
160
|
+
const okm = Buffer.alloc(N * 32);
|
|
161
|
+
let T = Buffer.alloc(0);
|
|
162
|
+
for (let i = 1; i <= N; i++) {
|
|
163
|
+
const hmac = createHmac("sha256", prk);
|
|
164
|
+
hmac.update(T);
|
|
165
|
+
hmac.update(info);
|
|
166
|
+
hmac.update(Buffer.from([i]));
|
|
167
|
+
T = Buffer.from(hmac.digest());
|
|
168
|
+
T.copy(okm, (i - 1) * 32);
|
|
169
|
+
}
|
|
170
|
+
return okm.subarray(0, length);
|
|
171
|
+
}
|
|
172
|
+
function labeledExtract(suiteId, salt, label, ikm) {
|
|
173
|
+
const hpkeV1 = Buffer.from("HPKE-v1", "ascii");
|
|
174
|
+
const labelBuf = Buffer.from(label, "ascii");
|
|
175
|
+
const labeledIkm = Buffer.concat([hpkeV1, suiteId, labelBuf, ikm]);
|
|
176
|
+
return hpkeHkdfExtract(salt, labeledIkm);
|
|
177
|
+
}
|
|
178
|
+
function labeledExpand(suiteId, prk, label, info, length) {
|
|
179
|
+
const hpkeV1 = Buffer.from("HPKE-v1", "ascii");
|
|
180
|
+
const labelBuf = Buffer.from(label, "ascii");
|
|
181
|
+
const lenBuf = Buffer.alloc(2);
|
|
182
|
+
lenBuf.writeUInt16BE(length);
|
|
183
|
+
const labeledInfo = Buffer.concat([lenBuf, hpkeV1, suiteId, labelBuf, info]);
|
|
184
|
+
return hpkeHkdfExpand(prk, labeledInfo, length);
|
|
185
|
+
}
|
|
186
|
+
function buildX25519PKCS8(raw) {
|
|
187
|
+
return Buffer.concat([Buffer.from([0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x04, 0x22, 0x04, 0x20]), raw]);
|
|
188
|
+
}
|
|
189
|
+
function buildX25519SPKI(raw) {
|
|
190
|
+
return Buffer.concat([Buffer.from([0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x03, 0x21, 0x00]), raw]);
|
|
191
|
+
}
|
|
192
|
+
function dhkemX25519Encap(pkR) {
|
|
193
|
+
const kp = generateKeyPairSync("x25519");
|
|
194
|
+
const pkEDer = Buffer.from(kp.publicKey.export({ type: "spki", format: "der" }));
|
|
195
|
+
const skEDer = Buffer.from(kp.privateKey.export({ type: "pkcs8", format: "der" }));
|
|
196
|
+
const pkE = Buffer.from(pkEDer.subarray(pkEDer.length - 32));
|
|
197
|
+
const skE = Buffer.from(skEDer.subarray(skEDer.length - 32));
|
|
198
|
+
const privKey = createPrivateKey({ key: buildX25519PKCS8(skE), format: "der", type: "pkcs8" });
|
|
199
|
+
const pubKey = createPublicKey({ key: buildX25519SPKI(pkR), format: "der", type: "spki" });
|
|
200
|
+
const dh = Buffer.from(diffieHellman({ privateKey: privKey, publicKey: pubKey }));
|
|
201
|
+
const enc = Buffer.from(pkE);
|
|
202
|
+
const kemContext = Buffer.concat([enc, pkR]);
|
|
203
|
+
const kemSuiteId = Buffer.from([0x4b, 0x45, 0x4d, 0x00, 0x20]);
|
|
204
|
+
const prk = labeledExtract(kemSuiteId, Buffer.alloc(0), "shared_secret", dh);
|
|
205
|
+
const sharedSecret = labeledExpand(kemSuiteId, prk, "shared_secret", kemContext, 32);
|
|
206
|
+
return { sharedSecret, enc };
|
|
207
|
+
}
|
|
208
|
+
function aeadParams(aeadId) {
|
|
209
|
+
switch (aeadId) {
|
|
210
|
+
case 0x0001:
|
|
211
|
+
return { Nk: 16, Nn: 12 };
|
|
212
|
+
case 0x0002:
|
|
213
|
+
return { Nk: 32, Nn: 12 };
|
|
214
|
+
case 0x0003:
|
|
215
|
+
return { Nk: 32, Nn: 12 };
|
|
216
|
+
default:
|
|
217
|
+
throw new Error(`Unsupported HPKE AEAD: 0x${aeadId.toString(16)}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function aeadAlgorithm(aeadId) {
|
|
221
|
+
switch (aeadId) {
|
|
222
|
+
case 0x0001:
|
|
223
|
+
return "aes-128-gcm";
|
|
224
|
+
case 0x0002:
|
|
225
|
+
return "aes-256-gcm";
|
|
226
|
+
case 0x0003:
|
|
227
|
+
return "chacha20-poly1305";
|
|
228
|
+
default:
|
|
229
|
+
throw new Error(`Unsupported HPKE AEAD: 0x${aeadId.toString(16)}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function hpkeKeyScheduleS(kemId, kdfId, aeadId, sharedSecret, info) {
|
|
233
|
+
const suiteId = Buffer.alloc(10);
|
|
234
|
+
suiteId.write("HPKE", 0, "ascii");
|
|
235
|
+
suiteId.writeUInt16BE(kemId, 4);
|
|
236
|
+
suiteId.writeUInt16BE(kdfId, 6);
|
|
237
|
+
suiteId.writeUInt16BE(aeadId, 8);
|
|
238
|
+
const { Nk, Nn } = aeadParams(aeadId);
|
|
239
|
+
const pskIdHash = labeledExtract(suiteId, Buffer.alloc(0), "psk_id_hash", Buffer.alloc(0));
|
|
240
|
+
const infoHash = labeledExtract(suiteId, Buffer.alloc(0), "info_hash", info);
|
|
241
|
+
const ksContext = Buffer.concat([Buffer.from([0x00]), pskIdHash, infoHash]);
|
|
242
|
+
const secret = labeledExtract(suiteId, sharedSecret, "secret", Buffer.alloc(0));
|
|
243
|
+
const key = labeledExpand(suiteId, secret, "key", ksContext, Nk);
|
|
244
|
+
const baseNonce = labeledExpand(suiteId, secret, "base_nonce", ksContext, Nn);
|
|
245
|
+
return { key, baseNonce };
|
|
246
|
+
}
|
|
247
|
+
function hpkeSeal(key, baseNonce, aad, plaintext, aeadId) {
|
|
248
|
+
const alg = aeadAlgorithm(aeadId);
|
|
249
|
+
const cipher = createCipheriv(alg, key, baseNonce, { authTagLength: 16 });
|
|
250
|
+
cipher.setAAD(aad);
|
|
251
|
+
const encrypted = cipher.update(plaintext);
|
|
252
|
+
const final = cipher.final();
|
|
253
|
+
const tag = cipher.getAuthTag();
|
|
254
|
+
return Buffer.concat([encrypted, final, tag]);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Build the outer ECH extension data for a ClientHello.
|
|
258
|
+
*
|
|
259
|
+
* @param {number} kdfId - KDF identifier.
|
|
260
|
+
* @param {number} aeadId - AEAD identifier.
|
|
261
|
+
* @param {number} configId - ECH config ID.
|
|
262
|
+
* @param {Buffer} enc - HPKE encapsulated key.
|
|
263
|
+
* @param {Buffer} payload - Encrypted inner ClientHello payload.
|
|
264
|
+
* @returns {Buffer} Serialized ECH outer extension bytes.
|
|
265
|
+
*/
|
|
266
|
+
export function buildECHOuterExtData(kdfId, aeadId, configId, enc, payload) {
|
|
267
|
+
const len = 1 + 2 + 2 + 1 + 2 + enc.length + 2 + payload.length;
|
|
268
|
+
const buf = Buffer.alloc(len);
|
|
269
|
+
let off = 0;
|
|
270
|
+
buf[off++] = 0x00;
|
|
271
|
+
buf.writeUInt16BE(kdfId, off);
|
|
272
|
+
off += 2;
|
|
273
|
+
buf.writeUInt16BE(aeadId, off);
|
|
274
|
+
off += 2;
|
|
275
|
+
buf[off++] = configId;
|
|
276
|
+
buf.writeUInt16BE(enc.length, off);
|
|
277
|
+
off += 2;
|
|
278
|
+
enc.copy(buf, off);
|
|
279
|
+
off += enc.length;
|
|
280
|
+
buf.writeUInt16BE(payload.length, off);
|
|
281
|
+
off += 2;
|
|
282
|
+
payload.copy(buf, off);
|
|
283
|
+
return buf;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Extract the first raw ECHConfig entry from a serialized ECHConfigList.
|
|
287
|
+
*
|
|
288
|
+
* @param {Buffer} echConfigList - Full serialized ECHConfigList buffer.
|
|
289
|
+
* @returns {Buffer | null} Raw config bytes, or `null` if the list is too short.
|
|
290
|
+
*/
|
|
291
|
+
export function extractFirstECHConfigRaw(echConfigList) {
|
|
292
|
+
if (echConfigList.length < 6)
|
|
293
|
+
return null;
|
|
294
|
+
const totalLength = echConfigList.readUInt16BE(0);
|
|
295
|
+
if (totalLength + 2 > echConfigList.length)
|
|
296
|
+
return null;
|
|
297
|
+
const _version = echConfigList.readUInt16BE(2);
|
|
298
|
+
const configLength = echConfigList.readUInt16BE(4);
|
|
299
|
+
if (6 + configLength > echConfigList.length)
|
|
300
|
+
return null;
|
|
301
|
+
return Buffer.from(echConfigList.subarray(2, 6 + configLength));
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Encrypt an inner ClientHello body using HPKE for Encrypted Client Hello.
|
|
305
|
+
*
|
|
306
|
+
* @param {Buffer} innerCHBody - Serialized inner ClientHello body.
|
|
307
|
+
* @param {Buffer} outerCHAAD - Additional authenticated data from the outer ClientHello.
|
|
308
|
+
* @param {ECHConfig} config - Parsed ECH configuration entry.
|
|
309
|
+
* @param {Buffer} configRaw - Raw bytes of the ECH configuration.
|
|
310
|
+
* @returns {{ extensionData: Buffer; enc: Buffer; kdfId: number; aeadId: number; configId: number }} Extension data, encapsulated key, and algorithm identifiers.
|
|
311
|
+
*/
|
|
312
|
+
export function echEncryptInner(innerCHBody, outerCHAAD, config, configRaw) {
|
|
313
|
+
const hpkeConfig = parseHpkeKeyConfig(config.contents);
|
|
314
|
+
if (!hpkeConfig)
|
|
315
|
+
throw new Error("Invalid ECHConfig: cannot parse HPKE key config");
|
|
316
|
+
if (hpkeConfig.kemId !== 0x0020) {
|
|
317
|
+
throw new Error(`Unsupported KEM: 0x${hpkeConfig.kemId.toString(16)} (only DHKEM(X25519, HKDF-SHA256) = 0x0020)`);
|
|
318
|
+
}
|
|
319
|
+
const suite = hpkeConfig.cipherSuites.find((cs) => cs.kdfId === 0x0001 && (cs.aeadId === 0x0001 || cs.aeadId === 0x0003));
|
|
320
|
+
if (!suite)
|
|
321
|
+
throw new Error("No supported HPKE cipher suite in ECHConfig");
|
|
322
|
+
const hpkeInfo = Buffer.concat([Buffer.from("tls ech\x00", "ascii"), configRaw]);
|
|
323
|
+
const { sharedSecret, enc } = dhkemX25519Encap(hpkeConfig.publicKey);
|
|
324
|
+
const { key, baseNonce } = hpkeKeyScheduleS(hpkeConfig.kemId, suite.kdfId, suite.aeadId, sharedSecret, hpkeInfo);
|
|
325
|
+
const payload = hpkeSeal(key, baseNonce, outerCHAAD, innerCHBody, suite.aeadId);
|
|
326
|
+
const extensionData = buildECHOuterExtData(suite.kdfId, suite.aeadId, hpkeConfig.configId, enc, payload);
|
|
327
|
+
return { extensionData, enc, kdfId: suite.kdfId, aeadId: suite.aeadId, configId: hpkeConfig.configId };
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Parse ECH retry configuration from a server's EncryptedExtensions.
|
|
331
|
+
*
|
|
332
|
+
* @param {Buffer} data - Serialized ECHConfigList from the retry_configs extension.
|
|
333
|
+
* @returns {ECHParameters | null} Parsed retry parameters, or `null` if invalid.
|
|
334
|
+
*/
|
|
335
|
+
export function parseECHRetryConfigs(data) {
|
|
336
|
+
return parseECHConfigList(data);
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Determine whether an ECH retry should be attempted.
|
|
340
|
+
*
|
|
341
|
+
* @param {number} retryCount - Number of retries already attempted.
|
|
342
|
+
* @param {number} maxRetries - Maximum allowed retries.
|
|
343
|
+
* @param {ECHParameters | null} retryConfigs - Retry ECH configs from the server.
|
|
344
|
+
* @returns {boolean} `true` if another retry is warranted.
|
|
345
|
+
*/
|
|
346
|
+
export function shouldRetryECH(retryCount, maxRetries, retryConfigs) {
|
|
347
|
+
if (retryCount >= maxRetries)
|
|
348
|
+
return false;
|
|
349
|
+
if (!retryConfigs || retryConfigs.configs.length === 0)
|
|
350
|
+
return false;
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=ech.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ech.js","sourceRoot":"","sources":["../../src/tls/ech.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the SSLKEYLOGFILE path from the environment or explicit override.
|
|
3
|
+
*
|
|
4
|
+
* @returns {string|undefined} File path string, or `undefined` if not configured.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getKeylogFile(): string | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Append a single NSS key-log line to the SSLKEYLOGFILE.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} line - Key-log line without trailing newline.
|
|
11
|
+
*/
|
|
12
|
+
export declare function writeKeylogLine(line: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Log a TLS master secret in NSS key-log format.
|
|
15
|
+
*
|
|
16
|
+
* @param {Buffer} clientRandom - 32-byte client random from the handshake.
|
|
17
|
+
* @param {Buffer} masterSecret - Derived master secret.
|
|
18
|
+
*/
|
|
19
|
+
export declare function logMasterSecret(clientRandom: Buffer, masterSecret: Buffer): void;
|
|
20
|
+
/**
|
|
21
|
+
* Log a TLS 1.3 traffic secret in NSS key-log format.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} label - Secret label (e.g. `"CLIENT_HANDSHAKE_TRAFFIC_SECRET"`).
|
|
24
|
+
* @param {Buffer} clientRandom - 32-byte client random.
|
|
25
|
+
* @param {Buffer} secret - Derived traffic secret.
|
|
26
|
+
*/
|
|
27
|
+
export declare function logTrafficSecret(label: string, clientRandom: Buffer, secret: Buffer): void;
|
|
28
|
+
/**
|
|
29
|
+
* Explicitly set or clear the SSLKEYLOGFILE path.
|
|
30
|
+
*
|
|
31
|
+
* @param {string|undefined} path - File path, or `undefined` to disable key logging.
|
|
32
|
+
*/
|
|
33
|
+
export declare function setKeylogFile(path: string | undefined): void;
|
|
34
|
+
//# sourceMappingURL=keylog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keylog.d.ts","sourceRoot":"","sources":["../../src/tls/keylog.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAMlD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAWlD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAEhF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAE1F;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAG5D"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { appendFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { mkdirSync } from "node:fs";
|
|
4
|
+
let _keylogFile;
|
|
5
|
+
let _initialized = false;
|
|
6
|
+
/**
|
|
7
|
+
* Return the SSLKEYLOGFILE path from the environment or explicit override.
|
|
8
|
+
*
|
|
9
|
+
* @returns {string|undefined} File path string, or `undefined` if not configured.
|
|
10
|
+
*/
|
|
11
|
+
export function getKeylogFile() {
|
|
12
|
+
if (!_initialized) {
|
|
13
|
+
_initialized = true;
|
|
14
|
+
_keylogFile = process.env["SSLKEYLOGFILE"];
|
|
15
|
+
}
|
|
16
|
+
return _keylogFile;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Append a single NSS key-log line to the SSLKEYLOGFILE.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} line - Key-log line without trailing newline.
|
|
22
|
+
*/
|
|
23
|
+
export function writeKeylogLine(line) {
|
|
24
|
+
const file = getKeylogFile();
|
|
25
|
+
if (!file)
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
const dir = dirname(file);
|
|
29
|
+
if (!existsSync(dir)) {
|
|
30
|
+
mkdirSync(dir, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
appendFileSync(file, line + "\n", { encoding: "utf-8" });
|
|
33
|
+
}
|
|
34
|
+
catch { }
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Log a TLS master secret in NSS key-log format.
|
|
38
|
+
*
|
|
39
|
+
* @param {Buffer} clientRandom - 32-byte client random from the handshake.
|
|
40
|
+
* @param {Buffer} masterSecret - Derived master secret.
|
|
41
|
+
*/
|
|
42
|
+
export function logMasterSecret(clientRandom, masterSecret) {
|
|
43
|
+
writeKeylogLine(`CLIENT_RANDOM ${clientRandom.toString("hex")} ${masterSecret.toString("hex")}`);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Log a TLS 1.3 traffic secret in NSS key-log format.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} label - Secret label (e.g. `"CLIENT_HANDSHAKE_TRAFFIC_SECRET"`).
|
|
49
|
+
* @param {Buffer} clientRandom - 32-byte client random.
|
|
50
|
+
* @param {Buffer} secret - Derived traffic secret.
|
|
51
|
+
*/
|
|
52
|
+
export function logTrafficSecret(label, clientRandom, secret) {
|
|
53
|
+
writeKeylogLine(`${label} ${clientRandom.toString("hex")} ${secret.toString("hex")}`);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Explicitly set or clear the SSLKEYLOGFILE path.
|
|
57
|
+
*
|
|
58
|
+
* @param {string|undefined} path - File path, or `undefined` to disable key logging.
|
|
59
|
+
*/
|
|
60
|
+
export function setKeylogFile(path) {
|
|
61
|
+
_keylogFile = path;
|
|
62
|
+
_initialized = true;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=keylog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keylog.js","sourceRoot":"","sources":["../../src/tls/keylog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,IAAI,WAA+B,CAAC;AACpC,IAAI,YAAY,GAAG,KAAK,CAAC;AAEzB;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,IAAI,CAAC;QACpB,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,IAAI;QAAE,OAAO;IAElB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;QACD,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;AACZ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,YAAoB,EAAE,YAAoB;IACxE,eAAe,CAAC,iBAAiB,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACnG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,YAAoB,EAAE,MAAc;IAClF,eAAe,CAAC,GAAG,KAAK,IAAI,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACxF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAwB;IACpD,WAAW,GAAG,IAAI,CAAC;IACnB,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC"}
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import type { ITLSEngine, TLSConnectOptions, TLSSocket } from "./types.js";
|
|
2
2
|
import type { BrowserProfile } from "../fingerprints/types.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* standard TLS connectivity with optional browser-profile cipher and curve
|
|
6
|
-
* configuration, but does not reproduce the exact ClientHello byte sequence
|
|
7
|
-
* of a real browser. Use {@link StealthTLSEngine} when full fingerprint
|
|
8
|
-
* fidelity is required.
|
|
9
|
-
*/
|
|
3
|
+
import { TLSSessionCache } from "./session-cache.js";
|
|
4
|
+
/** TLS engine backed by Node.js native `tls` module. */
|
|
10
5
|
export declare class NodeTLSEngine implements ITLSEngine {
|
|
6
|
+
private readonly sessionCache;
|
|
11
7
|
/**
|
|
12
|
-
*
|
|
13
|
-
* native `tls.connect()`. When a `profile` is supplied the cipher list,
|
|
14
|
-
* ECDH curves, and signature algorithms are overridden to match that profile.
|
|
8
|
+
* Create a Node.js TLS engine.
|
|
15
9
|
*
|
|
16
|
-
* @param {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
* @param {TLSSessionCache} [sessionCache] - Optional session ticket cache for resumption.
|
|
11
|
+
*/
|
|
12
|
+
constructor(sessionCache?: TLSSessionCache);
|
|
13
|
+
/** Return the session ticket cache used by this engine. */
|
|
14
|
+
getSessionCache(): TLSSessionCache;
|
|
15
|
+
/**
|
|
16
|
+
* Connect to a remote host over TLS using Node.js built-in facilities.
|
|
17
|
+
*
|
|
18
|
+
* @param {TLSConnectOptions} options - TLS connection options.
|
|
19
|
+
* @param {BrowserProfile} [profile] - Optional browser profile for cipher and curve ordering.
|
|
20
|
+
* @returns {Promise<TLSSocket>} Connected TLS socket.
|
|
20
21
|
*/
|
|
21
22
|
connect(options: TLSConnectOptions, profile?: BrowserProfile): Promise<TLSSocket>;
|
|
22
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-engine.d.ts","sourceRoot":"","sources":["../../src/tls/node-engine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"node-engine.d.ts","sourceRoot":"","sources":["../../src/tls/node-engine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAqB,SAAS,EAAE,MAAM,YAAY,CAAC;AAC9F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAmFrD,wDAAwD;AACxD,qBAAa,aAAc,YAAW,UAAU;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkB;IAE/C;;;;OAIG;gBACS,YAAY,CAAC,EAAE,eAAe;IAI1C,2DAA2D;IAC3D,eAAe,IAAI,eAAe;IAIlC;;;;;;OAMG;IACG,OAAO,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC;CAuJxF"}
|
package/dist/tls/node-engine.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as tls from "node:tls";
|
|
2
2
|
import { CipherSuite, NamedGroup, SignatureScheme } from "./constants.js";
|
|
3
3
|
import { TLSError } from "../core/errors.js";
|
|
4
|
+
import { TLSSessionCache } from "./session-cache.js";
|
|
5
|
+
import { verifyPinnedPublicKey } from "./pin-verification.js";
|
|
6
|
+
import { getKeylogFile, writeKeylogLine } from "./keylog.js";
|
|
4
7
|
const CIPHER_NAME = new Map([
|
|
5
8
|
[CipherSuite.TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256"],
|
|
6
9
|
[CipherSuite.TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384"],
|
|
@@ -76,23 +79,27 @@ function buildSigalgs(algs) {
|
|
|
76
79
|
.filter((n) => n !== undefined)
|
|
77
80
|
.join(":");
|
|
78
81
|
}
|
|
79
|
-
/**
|
|
80
|
-
* TLS engine that delegates to Node.js’s built-in `tls` module. Provides
|
|
81
|
-
* standard TLS connectivity with optional browser-profile cipher and curve
|
|
82
|
-
* configuration, but does not reproduce the exact ClientHello byte sequence
|
|
83
|
-
* of a real browser. Use {@link StealthTLSEngine} when full fingerprint
|
|
84
|
-
* fidelity is required.
|
|
85
|
-
*/
|
|
82
|
+
/** TLS engine backed by Node.js native `tls` module. */
|
|
86
83
|
export class NodeTLSEngine {
|
|
84
|
+
sessionCache;
|
|
87
85
|
/**
|
|
88
|
-
*
|
|
89
|
-
* native `tls.connect()`. When a `profile` is supplied the cipher list,
|
|
90
|
-
* ECDH curves, and signature algorithms are overridden to match that profile.
|
|
86
|
+
* Create a Node.js TLS engine.
|
|
91
87
|
*
|
|
92
|
-
* @param {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
88
|
+
* @param {TLSSessionCache} [sessionCache] - Optional session ticket cache for resumption.
|
|
89
|
+
*/
|
|
90
|
+
constructor(sessionCache) {
|
|
91
|
+
this.sessionCache = sessionCache ?? new TLSSessionCache();
|
|
92
|
+
}
|
|
93
|
+
/** Return the session ticket cache used by this engine. */
|
|
94
|
+
getSessionCache() {
|
|
95
|
+
return this.sessionCache;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Connect to a remote host over TLS using Node.js built-in facilities.
|
|
99
|
+
*
|
|
100
|
+
* @param {TLSConnectOptions} options - TLS connection options.
|
|
101
|
+
* @param {BrowserProfile} [profile] - Optional browser profile for cipher and curve ordering.
|
|
102
|
+
* @returns {Promise<TLSSocket>} Connected TLS socket.
|
|
96
103
|
*/
|
|
97
104
|
async connect(options, profile) {
|
|
98
105
|
return new Promise((resolve, reject) => {
|
|
@@ -123,6 +130,9 @@ export class NodeTLSEngine {
|
|
|
123
130
|
if (options.ca) {
|
|
124
131
|
tlsOpts.ca = options.ca;
|
|
125
132
|
}
|
|
133
|
+
if (options.echConfigList) {
|
|
134
|
+
tlsOpts["encryptedClientHello"] = options.echConfigList;
|
|
135
|
+
}
|
|
126
136
|
if (profile) {
|
|
127
137
|
const { ciphers, ciphersuites } = buildCipherString(profile.tls.cipherSuites);
|
|
128
138
|
tlsOpts.ciphers = ciphers;
|
|
@@ -134,8 +144,21 @@ export class NodeTLSEngine {
|
|
|
134
144
|
if (options.socket) {
|
|
135
145
|
tlsOpts.socket = options.socket;
|
|
136
146
|
}
|
|
147
|
+
if (getKeylogFile()) {
|
|
148
|
+
tlsOpts["enableTrace"] = false;
|
|
149
|
+
}
|
|
150
|
+
const origin = `${options.servername ?? options.host}:${options.port}`;
|
|
151
|
+
const cached = this.sessionCache.get(origin);
|
|
152
|
+
if (cached) {
|
|
153
|
+
tlsOpts.session = cached.ticket;
|
|
154
|
+
}
|
|
137
155
|
const timeoutMs = options.timeout ?? 30_000;
|
|
138
156
|
const socket = tls.connect(tlsOpts);
|
|
157
|
+
if (getKeylogFile()) {
|
|
158
|
+
socket.on("keylog", (line) => {
|
|
159
|
+
writeKeylogLine(line.toString("ascii").trimEnd());
|
|
160
|
+
});
|
|
161
|
+
}
|
|
139
162
|
let settled = false;
|
|
140
163
|
let timer;
|
|
141
164
|
if (timeoutMs > 0) {
|
|
@@ -169,13 +192,30 @@ export class NodeTLSEngine {
|
|
|
169
192
|
settled = true;
|
|
170
193
|
if (timer)
|
|
171
194
|
clearTimeout(timer);
|
|
195
|
+
if (options.pinnedPublicKey) {
|
|
196
|
+
try {
|
|
197
|
+
const peerCert = socket.getPeerCertificate(true);
|
|
198
|
+
if (peerCert && peerCert.raw) {
|
|
199
|
+
verifyPinnedPublicKey(peerCert.raw, options.pinnedPublicKey);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
socket.destroy();
|
|
204
|
+
reject(err instanceof Error ? err : new TLSError(String(err)));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
172
208
|
const cipher = socket.getCipher();
|
|
173
209
|
const proto = socket.getProtocol();
|
|
174
210
|
const connectionInfo = {
|
|
175
211
|
version: proto ?? "unknown",
|
|
176
212
|
alpnProtocol: socket.alpnProtocol || null,
|
|
177
213
|
cipher: cipher?.name ?? "unknown",
|
|
214
|
+
resumed: socket.isSessionReused(),
|
|
178
215
|
};
|
|
216
|
+
socket.on("session", (session) => {
|
|
217
|
+
this.sessionCache.set(origin, session, undefined, socket.alpnProtocol || undefined);
|
|
218
|
+
});
|
|
179
219
|
const tlsSocket = Object.assign(socket, {
|
|
180
220
|
connectionInfo,
|
|
181
221
|
destroyTLS() {
|