tunnelfetch 1.2.0 → 1.4.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 +93 -28
- package/README.zh-CN.md +54 -11
- package/package.json +5 -1
- package/src/client/header-order.js +159 -0
- package/src/client.js +79 -18
- package/src/http2/connection.js +36 -4
- package/src/index.js +1 -0
- package/src/profile/chrome.js +45 -0
- package/src/profile/vendor/chacha20poly1305.js +65 -0
- package/src/profile/vendor/mlkem768.js +67 -0
- package/src/profiles.js +184 -0
- package/src/tls/aead.js +51 -16
- package/src/tls/connect.js +49 -8
- package/src/tls/constants.js +60 -6
- package/src/tls/extensions.js +10 -0
- package/src/tls/grease.js +109 -0
- package/src/tls/handshake-messages.js +78 -9
- package/src/tls/handshake.js +2 -1
- package/src/tls/hybrid.js +166 -0
- package/src/tls/record.js +24 -3
- package/src/warmup-fixture.js +46 -45
- package/types/client/header-order.d.ts +56 -0
- package/types/client.d.ts +68 -0
- package/types/http2/connection.d.ts +16 -1
- package/types/http2/hpack.d.ts +1 -1
- package/types/index.d.ts +1 -0
- package/types/profile/chrome.d.ts +16 -0
- package/types/profile/vendor/chacha20poly1305.d.ts +7 -0
- package/types/profile/vendor/mlkem768.d.ts +23 -0
- package/types/profiles.d.ts +100 -0
- package/types/tls/aead.d.ts +17 -1
- package/types/tls/connect.d.ts +45 -6
- package/types/tls/constants.d.ts +31 -3
- package/types/tls/extensions.d.ts +7 -0
- package/types/tls/grease.d.ts +46 -0
- package/types/tls/handshake-messages.d.ts +12 -5
- package/types/tls/hybrid.d.ts +63 -0
- package/types/tls/record.d.ts +23 -0
package/src/tls/handshake.js
CHANGED
|
@@ -243,6 +243,7 @@ export async function continueTls13(ctx) {
|
|
|
243
243
|
// either among the modifications a second ClientHello may make, and a strict server checks.
|
|
244
244
|
extensionOrder: options.extensionOrder,
|
|
245
245
|
sigSchemes: options.sigSchemes,
|
|
246
|
+
grease: options.grease ?? false,
|
|
246
247
|
random: hello.clientRandom,
|
|
247
248
|
legacySessionId: hello.legacySessionId,
|
|
248
249
|
extraExtensions: cookie ? [cookieExtension(cookie)] : [],
|
|
@@ -354,7 +355,7 @@ export async function continueTls13(ctx) {
|
|
|
354
355
|
// Only psk_dhe_ke is ever offered (s4.2.9), so a key exchange happens whether or not the PSK
|
|
355
356
|
// was taken; selectServerKeyShare fails closed on a ServerHello without key_share.
|
|
356
357
|
const server = selectServerKeyShare(sh, keyShares);
|
|
357
|
-
const shared = await deriveSharedSecret(server.group, server.privateKey, server.keyExchange);
|
|
358
|
+
const shared = await deriveSharedSecret(server.group, server.privateKey, server.keyExchange, deps);
|
|
358
359
|
|
|
359
360
|
// s7.1: with a PSK in use the Early Secret is extracted from it; otherwise from zeros. The
|
|
360
361
|
// accepted offer already carries that extraction (connect.js derived it for the binder), and
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// X25519MLKEM768 (group 0x11EC): the post-quantum hybrid key exchange of
|
|
2
|
+
// draft-kwiatkowski-tls-ecdhe-mlkem.
|
|
3
|
+
//
|
|
4
|
+
// This is a HYBRID: two independent key agreements run side by side and their secrets are
|
|
5
|
+
// concatenated, so the session is safe as long as EITHER holds — X25519 against a classical
|
|
6
|
+
// attacker, ML-KEM-768 against a future quantum one. ML-KEM is not a WebCrypto primitive on this
|
|
7
|
+
// runtime, so its keygen/encapsulate/decapsulate are INJECTED (the same discipline ChaCha20 gets
|
|
8
|
+
// in aead.js): without an implementation the group is never offered, and a ClientHello is an offer
|
|
9
|
+
// a server may take. The X25519 half is plain WebCrypto, exactly as x25519 alone is elsewhere.
|
|
10
|
+
//
|
|
11
|
+
// The three orderings below are the whole subtlety of this file, and each is a silent trap: a
|
|
12
|
+
// reversed concatenation does not throw, it produces a shared secret that merely DIFFERS, so the
|
|
13
|
+
// handshake fails only later, at a Finished that will not verify — indistinguishable from a server
|
|
14
|
+
// bug. draft-kwiatkowski-tls-ecdhe-mlkem fixes them, and they were confirmed against a real
|
|
15
|
+
// X25519MLKEM768 server (Cloudflare) by completing a handshake, not read off the page:
|
|
16
|
+
//
|
|
17
|
+
// * client key_share = ML-KEM-768 encapsulation key (1184) || X25519 public key (32) = 1216
|
|
18
|
+
// * server key_share = ML-KEM-768 ciphertext (1088) || X25519 public key (32) = 1120
|
|
19
|
+
// * shared secret = ML-KEM-768 shared secret (32) || X25519 shared secret (32) = 64
|
|
20
|
+
//
|
|
21
|
+
// ML-KEM comes FIRST in all three. (This is the opposite of the NIST-curve hybrids such as
|
|
22
|
+
// SecP256r1MLKEM768, where the classical share leads — which is exactly why it must be verified
|
|
23
|
+
// per group rather than assumed.) ML-KEM decapsulation uses FIPS 203 implicit rejection: a bad
|
|
24
|
+
// ciphertext yields a pseudorandom secret rather than an error, so a mismatch here can ONLY show
|
|
25
|
+
// up as a bad Finished, never as an exception. That is by design and is why offline agreement is
|
|
26
|
+
// necessary but not sufficient — see the live test.
|
|
27
|
+
|
|
28
|
+
import { TlsError, codes, hex16 } from '../errors.js';
|
|
29
|
+
import { concat } from '../util/bytes.js';
|
|
30
|
+
import { GROUP, GROUP_PARAMS } from './constants.js';
|
|
31
|
+
|
|
32
|
+
export const HYBRID_GROUP = GROUP.x25519mlkem768;
|
|
33
|
+
const P = GROUP_PARAMS[HYBRID_GROUP];
|
|
34
|
+
|
|
35
|
+
const X25519_ALG = { name: 'X25519' };
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* An injected ML-KEM-768 implementation (FIPS 203). Shapes match `wasmcrypto`'s `mlkem768`:
|
|
39
|
+
* @typedef {object} MlKem768
|
|
40
|
+
* @property {(seed?: Uint8Array) => { publicKey: Uint8Array, secretKey: Uint8Array }} keygen
|
|
41
|
+
* ML-KEM.KeyGen; `publicKey` is the 1184-byte encapsulation key, `secretKey` the 2400-byte
|
|
42
|
+
* decapsulation key.
|
|
43
|
+
* @property {(publicKey: Uint8Array) => { cipherText: Uint8Array, sharedSecret: Uint8Array }}
|
|
44
|
+
* encapsulate ML-KEM.Encaps; used by a server, exercised by the offline test server.
|
|
45
|
+
* @property {(cipherText: Uint8Array, secretKey: Uint8Array) => Uint8Array} decapsulate
|
|
46
|
+
* ML-KEM.Decaps; returns the 32-byte shared secret (implicit rejection on a bad ciphertext).
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The private half kept between generateHybridKeyShare and deriveHybridSecret: the ML-KEM
|
|
51
|
+
* decapsulation key and the X25519 private key. Not a CryptoKey, so callers treat KeyShare's
|
|
52
|
+
* private component opaquely (they already do — it only ever flows back into derivation).
|
|
53
|
+
* @typedef {object} HybridPrivate
|
|
54
|
+
* @property {Uint8Array} mlkemSecretKey the ML-KEM decapsulation key
|
|
55
|
+
* @property {CryptoKey} classicalPrivateKey the X25519 private key
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
function checkLen(actual, want, what) {
|
|
59
|
+
if (actual !== want) {
|
|
60
|
+
throw new TlsError(
|
|
61
|
+
codes.TLS_HANDSHAKE,
|
|
62
|
+
`X25519MLKEM768 ${what} is ${actual} bytes, expected ${want}`,
|
|
63
|
+
{ group: HYBRID_GROUP, got: actual, expected: want },
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Generate a client key share for X25519MLKEM768.
|
|
70
|
+
*
|
|
71
|
+
* @param {MlKem768} kem the injected ML-KEM-768 implementation
|
|
72
|
+
* @param {import('./connect.js').TlsDeps} [deps] `generateKeyPair` is honoured for the X25519
|
|
73
|
+
* half so a recorded handshake can be replayed with a fixed classical key
|
|
74
|
+
* @returns {Promise<{ group: number, keyExchange: Uint8Array, privateKey: HybridPrivate }>}
|
|
75
|
+
* `keyExchange` is the 1216-byte wire share (ML-KEM encapsulation key || X25519 public key)
|
|
76
|
+
*/
|
|
77
|
+
export async function generateHybridKeyShare(kem, { generateKeyPair } = {}) {
|
|
78
|
+
requireKem(kem);
|
|
79
|
+
// ML-KEM keypair. The encapsulation key goes on the wire; the decapsulation key is kept.
|
|
80
|
+
const mlkem = kem.keygen();
|
|
81
|
+
checkLen(mlkem.publicKey?.byteLength, P.mlkemPublicLen, 'ML-KEM encapsulation key');
|
|
82
|
+
checkLen(mlkem.secretKey?.byteLength, P.mlkemSecretKeyLen, 'ML-KEM decapsulation key');
|
|
83
|
+
|
|
84
|
+
// X25519 keypair, via WebCrypto exactly as the standalone x25519 group does.
|
|
85
|
+
const gen =
|
|
86
|
+
generateKeyPair ??
|
|
87
|
+
((algorithm) => crypto.subtle.generateKey(algorithm, false, ['deriveBits']));
|
|
88
|
+
const pair = await gen(X25519_ALG, GROUP.x25519);
|
|
89
|
+
const classicalPublic = new Uint8Array(await crypto.subtle.exportKey('raw', pair.publicKey));
|
|
90
|
+
checkLen(classicalPublic.byteLength, P.classicalPublicLen, 'X25519 public key');
|
|
91
|
+
|
|
92
|
+
// ML-KEM FIRST, then X25519 — the client-share ordering. 1184 + 32 = 1216.
|
|
93
|
+
const keyExchange = concat([mlkem.publicKey, classicalPublic]);
|
|
94
|
+
return {
|
|
95
|
+
group: HYBRID_GROUP,
|
|
96
|
+
keyExchange,
|
|
97
|
+
privateKey: { mlkemSecretKey: mlkem.secretKey, classicalPrivateKey: pair.privateKey },
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Derive the 64-byte hybrid shared secret from the server's X25519MLKEM768 key share.
|
|
103
|
+
*
|
|
104
|
+
* @param {MlKem768} kem the injected ML-KEM-768 implementation
|
|
105
|
+
* @param {HybridPrivate} privateKey what generateHybridKeyShare kept
|
|
106
|
+
* @param {Uint8Array} serverShare the server's 1120-byte key_exchange (ciphertext || X25519 pub)
|
|
107
|
+
* @returns {Promise<Uint8Array>} ML-KEM shared secret (32) || X25519 shared secret (32) = 64
|
|
108
|
+
*/
|
|
109
|
+
export async function deriveHybridSecret(kem, privateKey, serverShare) {
|
|
110
|
+
requireKem(kem);
|
|
111
|
+
if (!privateKey || !privateKey.mlkemSecretKey || !privateKey.classicalPrivateKey) {
|
|
112
|
+
throw new TlsError(codes.CONFIG_INVALID,
|
|
113
|
+
'X25519MLKEM768 derivation needs the ML-KEM decapsulation key and the X25519 private key');
|
|
114
|
+
}
|
|
115
|
+
// The server share is ML-KEM ciphertext (1088) then X25519 public key (32). A wrong length here
|
|
116
|
+
// is the server contradicting the group it selected; fail closed rather than slice past the end.
|
|
117
|
+
checkLen(serverShare.byteLength, P.serverShareLen, 'server key_share');
|
|
118
|
+
const ciphertext = serverShare.subarray(0, P.mlkemCiphertextLen);
|
|
119
|
+
const classicalPublic = serverShare.subarray(P.mlkemCiphertextLen);
|
|
120
|
+
|
|
121
|
+
// ML-KEM decapsulation. Implicit rejection (FIPS 203) means a tampered ciphertext yields a
|
|
122
|
+
// pseudorandom secret, not an error — the mismatch can only surface at Finished, by design.
|
|
123
|
+
const mlkemSecret = kem.decapsulate(ciphertext, privateKey.mlkemSecretKey);
|
|
124
|
+
checkLen(mlkemSecret?.byteLength, P.classicalSecretLen, 'ML-KEM shared secret');
|
|
125
|
+
|
|
126
|
+
// X25519 agreement, with the same small-order guard the standalone x25519 path applies.
|
|
127
|
+
let classicalPeer;
|
|
128
|
+
try {
|
|
129
|
+
classicalPeer = await crypto.subtle.importKey('raw', classicalPublic, X25519_ALG, false, []);
|
|
130
|
+
} catch (cause) {
|
|
131
|
+
throw new TlsError(codes.TLS_HANDSHAKE,
|
|
132
|
+
`X25519MLKEM768 server X25519 share is not a valid public key: ${cause?.message}`,
|
|
133
|
+
{ group: HYBRID_GROUP });
|
|
134
|
+
}
|
|
135
|
+
let classicalSecret;
|
|
136
|
+
try {
|
|
137
|
+
classicalSecret = new Uint8Array(
|
|
138
|
+
await crypto.subtle.deriveBits({ ...X25519_ALG, public: classicalPeer },
|
|
139
|
+
privateKey.classicalPrivateKey, P.classicalSecretLen * 8));
|
|
140
|
+
} catch (cause) {
|
|
141
|
+
throw new TlsError(codes.TLS_HANDSHAKE,
|
|
142
|
+
`X25519MLKEM768 X25519 agreement failed: ${cause?.message ?? cause}. This usually means a ` +
|
|
143
|
+
'degenerate or small-order server key.', { group: HYBRID_GROUP });
|
|
144
|
+
}
|
|
145
|
+
// RFC 7748 s6.1: an all-zero X25519 output is a small-order peer key. Reject, as x25519 does.
|
|
146
|
+
if (classicalSecret.every((b) => b === 0)) {
|
|
147
|
+
throw new TlsError(codes.TLS_HANDSHAKE,
|
|
148
|
+
'X25519MLKEM768 X25519 shared secret is all zeroes, indicating a small-order server key');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ML-KEM FIRST, then X25519 — the shared-secret ordering. 32 + 32 = 64.
|
|
152
|
+
return concat([mlkemSecret, classicalSecret]);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** @param {MlKem768} kem */
|
|
156
|
+
function requireKem(kem) {
|
|
157
|
+
if (typeof kem?.keygen !== 'function' || typeof kem?.decapsulate !== 'function') {
|
|
158
|
+
throw new TlsError(
|
|
159
|
+
codes.CONFIG_INVALID,
|
|
160
|
+
`${hex16(HYBRID_GROUP)} (x25519mlkem768) was reached but no ML-KEM-768 implementation was ` +
|
|
161
|
+
'supplied; pass one as `groups: { x25519mlkem768: impl }` with keygen()/encapsulate()/' +
|
|
162
|
+
'decapsulate()',
|
|
163
|
+
{ group: HYBRID_GROUP },
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
package/src/tls/record.js
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import { TlsError, TlsUnsupportedError, codes, hex8, hex16 } from '../errors.js';
|
|
21
21
|
import {
|
|
22
22
|
RECORD_TYPE, HANDSHAKE_TYPE, ALERT_DESC, ALERT_LEVEL, MAX_PLAINTEXT, MAX_CIPHERTEXT,
|
|
23
|
-
LEGACY_VERSION, TLS12, TLS13, CIPHER_PARAMS,
|
|
23
|
+
LEGACY_VERSION, TLS12, TLS13, CIPHER, CIPHER_PARAMS,
|
|
24
24
|
} from './constants.js';
|
|
25
25
|
import { createAead } from './aead.js';
|
|
26
26
|
import { trafficKeys, nextTrafficSecret } from './keyschedule.js';
|
|
@@ -106,6 +106,10 @@ async function withGrace(promise, ms) {
|
|
|
106
106
|
* record, TLS 1.3 only
|
|
107
107
|
* @property {null | ((msg: HandshakeMessage) => void | Promise<void>)} [onPostHandshake]
|
|
108
108
|
* NewSessionTicket consumer; default is to discard
|
|
109
|
+
* @property {null | { chacha20?: import('./aead.js').AeadOptions['impl'] }} [aeadImpls] injected
|
|
110
|
+
* AEAD implementations by name. ChaCha20-Poly1305 has no WebCrypto path on this runtime, so its
|
|
111
|
+
* seal/open are supplied here and threaded into every createAead for the ChaCha20 suite (initial
|
|
112
|
+
* keys and each KeyUpdate rotation). Absent for the AES-GCM-only default, which needs nothing.
|
|
109
113
|
*/
|
|
110
114
|
|
|
111
115
|
export class RecordLayer {
|
|
@@ -122,6 +126,9 @@ export class RecordLayer {
|
|
|
122
126
|
this._shutdownGraceMs = opts.shutdownGraceMs ?? 2000;
|
|
123
127
|
this._padding = opts.padding ?? null;
|
|
124
128
|
this._onPostHandshake = opts.onPostHandshake ?? null;
|
|
129
|
+
// Injected AEAD implementations (ChaCha20-Poly1305). Threaded into every createAead so a
|
|
130
|
+
// KeyUpdate rotation reaches for the same implementation the initial keys used.
|
|
131
|
+
this._aeadImpls = opts.aeadImpls ?? null;
|
|
125
132
|
|
|
126
133
|
this._version = TLS13; // semantics selector; setVersion() pins it when negotiated
|
|
127
134
|
/** @type {DirectionState} */
|
|
@@ -250,10 +257,22 @@ export class RecordLayer {
|
|
|
250
257
|
} else if (!key || !iv) {
|
|
251
258
|
throw new TlsError(codes.CONFIG_INVALID, 'keys need either a traffic secret or key+iv');
|
|
252
259
|
}
|
|
253
|
-
const aead = await createAead({
|
|
260
|
+
const aead = await createAead({
|
|
261
|
+
version: this._version, cipher, key, iv, impl: this._implFor(cipher),
|
|
262
|
+
});
|
|
254
263
|
return { aead, seq: 0n, cipher, hash: params.hash, secret: secret ?? null };
|
|
255
264
|
}
|
|
256
265
|
|
|
266
|
+
/**
|
|
267
|
+
* The injected AEAD implementation a suite needs, or null. Only ChaCha20-Poly1305 needs one on
|
|
268
|
+
* this runtime; every AES-GCM suite goes through WebCrypto and passes null.
|
|
269
|
+
* @param {number} cipher
|
|
270
|
+
* @returns {import('./aead.js').AeadOptions['impl'] | null}
|
|
271
|
+
*/
|
|
272
|
+
_implFor(cipher) {
|
|
273
|
+
return cipher === CIPHER.TLS_CHACHA20_POLY1305_SHA256 ? (this._aeadImpls?.chacha20 ?? null) : null;
|
|
274
|
+
}
|
|
275
|
+
|
|
257
276
|
// ------------------------------------------------------------------ read side
|
|
258
277
|
|
|
259
278
|
/**
|
|
@@ -840,7 +859,9 @@ export class RecordLayer {
|
|
|
840
859
|
const secret = await nextTrafficSecret(s.hash, s.secret);
|
|
841
860
|
const params = CIPHER_PARAMS[s.cipher];
|
|
842
861
|
const { key, iv } = await trafficKeys(params.hash, secret, params.keyLen, params.ivLen);
|
|
843
|
-
const aead = await createAead({
|
|
862
|
+
const aead = await createAead({
|
|
863
|
+
version: this._version, cipher: s.cipher, key, iv, impl: this._implFor(s.cipher),
|
|
864
|
+
});
|
|
844
865
|
this._send = { aead, seq: 0n, cipher: s.cipher, hash: s.hash, secret };
|
|
845
866
|
}
|
|
846
867
|
|
package/src/warmup-fixture.js
CHANGED
|
@@ -18,61 +18,62 @@ export const WARMUP_HOSTNAME = "warmup.invalid";
|
|
|
18
18
|
export const WARMUP_NOW = 1893456000000; // fixed epoch ms inside the chain's validity window
|
|
19
19
|
|
|
20
20
|
const CLIENT_PRIV =
|
|
21
|
-
"
|
|
21
|
+
"MC4CAQAwBQYDK2VuBCIEIGD2kIBgmWlTqN+qzpUvC5PMClhKiudEWslvOrbnG7lq";
|
|
22
22
|
const CLIENT_PUB =
|
|
23
|
-
"
|
|
23
|
+
"c4dk24oOgG+LLBpm0e90sFXS+UhcTLZpEIrCDhTPgCo=";
|
|
24
24
|
const CLIENT_RANDOM =
|
|
25
25
|
"AwoRGB8mLTQ7QklQV15lbHN6gYiPlp2kq7K5wMfO1dw=";
|
|
26
26
|
const SESSION_ID =
|
|
27
27
|
"BRAbJjE8R1JdaHN+iZSfqrXAy9bh7PcCDRgjLjlET1o=";
|
|
28
28
|
const CLIENT_HELLO =
|
|
29
|
-
"AQAA9AMDAwoRGB8mLTQ7QklQV15lbHN6gYiPlp2kq7K5wMfO1dwgBRAbJjE8R1JdaHN+
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
29
|
+
"AQAA9AMDAwoRGB8mLTQ7QklQV15lbHN6gYiPlp2kq7K5wMfO1dwgBRAbJjE8R1JdaHN+iZSfqrXAy9bh7PcCDRgjLjlET1oADBMC" +
|
|
30
|
+
"EwHALMAwwCvALwEAAJ//AQABAAAAABMAEQAADndhcm11cC5pbnZhbGlkAAUABQEAAAAAAAsAAgEAAAoACgAIAB0AFwAYABkAEAAL" +
|
|
31
|
+
"AAkIaHR0cC8xLjEAFwAAAA0AFgAUBAMFAwYDCAQIBQgGCAcEAQUBBgEAKwAFBAMEAwMALQACAQEAMwAmACQAHQAgc4dk24oOgG+L" +
|
|
32
|
+
"LBpm0e90sFXS+UhcTLZpEIrCDhTPgCo=";
|
|
33
33
|
const SERVER_BYTES =
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
34
|
+
"FgMBAHoCAAB2AwOd1LxJZeGnlrBrfh+XURLQ0+oy/nHBoHi4f2te1UCsgiAFEBsmMTxHUl1oc36JlJ+qtcDL1uHs9wINGCMuOURP" +
|
|
35
|
+
"WhMCAAAuACsAAgMEADMAJAAdACA/u21n72qimI15SHG70pBa5bDie2NJ5jJ3U9mc6quvbhcDAwdo1lcruKbvqnFuYLiYAcl82akL" +
|
|
36
|
+
"xNhbdsBzqLb0/hxNrpwSyceH0iseW8tCkUmLDZSikWT3FIfTy5gxHhzw9jCF20wVsupTSDu1vhVDHVCae5vl4cBbCpAIGP1DfMkG" +
|
|
37
|
+
"fHy2EqaX9Hvhs/f0IxymKE+phAWvFtG8CWhC+jKkOemjTGeYu7Zz92+xZrjqZvItnNzogFojQFYYAiRkNHkWsCvUmwHAMKOIY3H3" +
|
|
38
|
+
"2ov3zLxtAoFB9GwfOOTe4q0h+JBkTzXdc45AcMVZPyjRIGr/2/XDt5F2NZZY53DZAB9Vba8k1IUUo/YgInGXHULRZaootpQzALAP" +
|
|
39
|
+
"2B29d4DezKxA8HaVw3QQsJ8YKGXRoXGsac1/4MwGjj7gUkzwqXHSeNreoBeGkdu6eCLYKqp7ideGeibQ8zIPNjFSUijOnf8kKmsI" +
|
|
40
|
+
"AjkwLoz0W8NFGaw2ZN7vSABDHAqxiP2hVunU5ppnqD9jWtC5ef1rzOw5UYg6YVWZwywR3p04z4F8FZtqNSuYwgnMXN4M54lGPz8Y" +
|
|
41
|
+
"Vw1NE5sCFDKutVJTAnwiae0L8JoFnfVgdV8f4SInPiH3DWLrTmZK2IL0bzUhfB1OGtXdPJ59ZHR8wh3rU1Gcbew3CkzuOa4YX9YC" +
|
|
42
|
+
"kcBhZP2NXGUANcwX1rhVXUCj3jz3z0zn8fIHYqhtFRkAlk0BNr/VPoCnIZZPinARp2CDi9eiUA43zpAXlbwxddmwGTmJSFax2PYs" +
|
|
43
|
+
"OvtpREy/sYGZrO5Yi33NQby+kYkXOxjhnC5KGLKE3O0SA7KbYBzWbWuw15Am0dtizFBam3aZHQzw9IC5MX9p7ENabhSUMjd7QZUV" +
|
|
44
|
+
"fjQRV+ZOndb/vdJG3et7WUa8NzeJ7Gwb+2pzDBj7eu54mtoHmjxu9svI+LEfCc22M7KNydkkNGDlcqJVETx/yS4XZLw2RviZadt3" +
|
|
45
|
+
"mU/9Lzs9Avz+c6+DuMcz0WPqUCuFuD6unOd5c2LikO84RWtfBobEuZwwZhLGM3VwYWdh61wXotjid6aywKl54yM+QvUonvVs9533" +
|
|
46
|
+
"6BG1MRqB7+MuxXBBDOGofZ0ba+3FcF/f9bbbuBBwntU3PlsOzZ5gTeBw/I28MQjOYe4ZfgM3ljTN1BSXokZSC+noYFS73r1cW+oC" +
|
|
47
|
+
"MunCecMP/Vy1AIGWLs87li9FbQBgzF+l1U4bOgE22vCPnC3UC7AH8sGGyPGW5+02ZhPOFOpvh4kp92PbCV5aAF88ZXVEfH5VQmWn" +
|
|
48
|
+
"uR8TLjP6e70NYhnQOWcEQJSCYG85TnsDiRz2/GcQJqDBC/g/Yv7JQhIBicoXhiJyswx9lxrKDsGoxOurY3sJRvgUpvj40EUrTsk8" +
|
|
49
|
+
"6J18td6JJQTnMPiYhSXudXZej0fkcm73DtwX6nEXq05mwwuKiZLSc2fyldVNb7RNyqjnTQLjVCQxtv9Y2Iyl/BSYN4C0dRmAoQur" +
|
|
50
|
+
"mpDtE626fJpP1EBzNmsDlEJoBvPOEVvBEhozdqd8bxkj5p4ePP87nS6diSARiEdmLcSbaOPi6CTckQfnjLfP+Ltws8kC8n2j6lOe" +
|
|
51
|
+
"6MkkRU5vnZzSILVC7pLIDawz4rUfGoVQGU0qWEIFDJ/uHriaq0G7yA3WTUU8Oko8Qxnxc+idtJ/wnTclNH78PkDLPodFb0obT8dh" +
|
|
52
|
+
"m6EkWttPqKty/fMHBUr1afRijHxpO1z6Affz7GMCyJgH8w8lw0R0HDAQTl2+xWXTygGVaKXO4ySqoU2FxANurjp2DpyjUmfDMHOq" +
|
|
53
|
+
"7Ac2rCnSrGbgE7OcPJ00CPqd/6w9EYqt0G3FZaIIIK9n9Tz6WT+8Qh4QtVzyG/6r/WgP6paI4yR9KO1AVr0/UGadjzuk0GXqm2mT" +
|
|
54
|
+
"XiJPpP6YzDkJ1wJCmZ3wHwy2F9SXbv+xLRcYwcTQY+YNxujdiZ+3ZljUULgrIcD3E/RzL9wEYw/fGIYnPqhyJeA3ws73H796u/MX" +
|
|
55
|
+
"D6fwPNL1ImUqJA5dOUMt3RwpdZl8zTmnoDy/T6mpq5agtVAfvJORj7zILPfYohXy2oDTQfuAAqsF0osSm7j4R5LRrzBoFe9FJ6hY" +
|
|
56
|
+
"hiWlYRzObNuDeKX1js6IFzlDBjBaoXJ6S3xO9TED0gfT6aZP109fPSFwYAzitMpSdEKTQlHAavXxppuaztd4Rw+ia6rgEa3dxB3c" +
|
|
57
|
+
"+tZlBeBnmozz2DMenp28CYsPyCfrt9Yv6On8AWbOezmJnpAziJhJrJD3jIKAp02txekEz3eUTNrusCzD+QEBZxZoiq3rr7bY+Wao" +
|
|
58
|
+
"fAbquyVSYB1QurNbLy+LqPFKzZWzrZhcivsRO1ZFjw/wcw/Oeqci570vppJ67QQ1irbEHNb7N8cWIJNAtbYpXRQf5/sp+B1kDdvM" +
|
|
59
|
+
"e1j9sig2JbHPp/7TI7a58ry8Z77+5ivgDSsM3uFmUwbs0/KB9foP2QC4a+pTyN1RXdLnuNkRgWTB6tG05RrCYZfTbTjzfFDWgtzu" +
|
|
60
|
+
"6fJgvActBuvvFjfHx/N3QG2xg5z+txNic7rDJorfBL5d8xK47T+KklNRS6CJws9hBgVjD95kZKU3+o4cNFQE9uZF88yAKSyIk0R2" +
|
|
61
|
+
"bgPUFwMDARkp6obVqB0nPnEgl4GA4oIJuCNzzB7pza1qKxqIqLjVs6ZhzOwPyhyIh1W1nscTrxfpJOOmn9yuOji40yBPz0UGUwqL" +
|
|
62
|
+
"rhWdkK21FpvKx4eoMh/2B1S3XbTA83EIhnQxdwjyutUt+Zf/LCIKC8Q7kCnxqybPek6mxyI9JU6be/I+IV+qsECppD5T73aGmUpa" +
|
|
63
|
+
"5vfNuso40YuTX7a6gUrUPdXluIB4godoeyS9q1AOvgf7hWVIMp/bNl5DOTW/7Lcb/mdlqGMUepXScrEDyTGNyQ/P6R3bOfmpBm5Q" +
|
|
64
|
+
"ONGg9gKm7psWT/U/aK9XQNW+jpqGrBIx9Ws2rbpihOFmzhiPPMhILSLj3rvMyNZoHN8Xv+IhHNqpsCcwSxvg7BcDAwATS/g1rD6Z" +
|
|
65
|
+
"kAgj5tcHEb0GRYv7RQ==";
|
|
65
66
|
const ROOT_DER =
|
|
66
67
|
"MIIC2jCCAcKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAeMRwwGgYDVQQDDBNXYXJtdXAgRml4dHVyZSBSb290MB4XDTI1MDEwMTAw" +
|
|
67
68
|
"MDAwMFoXDTM1MDEwMTAwMDAwMFowHjEcMBoGA1UEAwwTV2FybXVwIEZpeHR1cmUgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEP" +
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
69
|
+
"ADCCAQoCggEBAIRQ0+WIza3mrmr2wDgG0u3kvBYYFp23u+AcofQDWrUKZj/6uOOq2Ur2rZF3cSSkjedqouNvtFfKTA8kUD4IHaM/" +
|
|
70
|
+
"rDjUNBI7ITsyRHieixi39qddXpTgb9+WInm0pyIvAgCLZlyg33AG7y29mEfPTJYAcWaSYRLQwu+/dMAshxC16i3zSU5HleZntE9F" +
|
|
71
|
+
"VJbYR5QGIS6Y5MzU+1hSlXxSbl5boQXTJql1OdWt4t8MU/7voXJhZ6mdGXXiPQXdSrc8txKUJLqd6UgP5AnIhbWhHkfg0MpAXm0Y" +
|
|
72
|
+
"5rrodnclqTNI5ibDDt+ylA9vk5AiCH+mCqPtNd7mERMsP2rpBvRM/EsCAwEAAaMjMCEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B" +
|
|
73
|
+
"Af8EBAMCAgQwDQYJKoZIhvcNAQELBQADggEBAHjQ1bWdwKNQpAefj7nyNX7KHdvfNK7TRpAobtunyuEQE6Ia4d1CIOGNHAX87UM2" +
|
|
74
|
+
"4otia7hM4S2oysqvW+HWpMR4cCe2TDofg/nvWo9SOJCsKRcn5caeB3AL0LG7eh97tDoMiytI1xQ2RN/ZajxQ8Fc9WdUjIosiOc5l" +
|
|
75
|
+
"IQFg6pBvzsAhwC2hFuspk31ay/mwE6t5pP+68ZJELqwjBiN6M+vGjIoQ/29tMsgnOaPCXB5vp4gaZpaisu1QiZEG95O7owrJ49+u" +
|
|
76
|
+
"h6JTXWf2j7qxiPqdvqYUGjqZSwKRwZGMYWwIRub/tSdRaRiBpV7QXjarJ8IK9yTAOwe/2fHAX5VbOI0=";
|
|
76
77
|
|
|
77
78
|
export const WARMUP_FIXTURE = {
|
|
78
79
|
clientPrivPkcs8: () => B(CLIENT_PRIV),
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the caller's header names in the order and case they wrote them, BEFORE anything hands them
|
|
3
|
+
* to `Request`, which is where both are lost.
|
|
4
|
+
*
|
|
5
|
+
* Recovers what is recoverable and no more: an array of pairs or a plain object still carries the
|
|
6
|
+
* caller's order, a `Headers` or a `Request` does not — those were normalised before this package
|
|
7
|
+
* ever saw them, and there is nothing here to reconstruct.
|
|
8
|
+
*
|
|
9
|
+
* @param {RequestInfo | URL} input
|
|
10
|
+
* @param {RequestInit} [init]
|
|
11
|
+
* @returns {Array<[string, string]> | null} null when the caller's order was already gone
|
|
12
|
+
*/
|
|
13
|
+
export function callerHeaderOrder(input: RequestInfo | URL, init?: RequestInit): Array<[string, string]> | null;
|
|
14
|
+
/**
|
|
15
|
+
* Default request header order, from curl 8.21.0 on the wire. `'*'` is where headers not named
|
|
16
|
+
* here go, in the order they were given — which is where curl puts the caller's own.
|
|
17
|
+
*/
|
|
18
|
+
export const CURL_HEADER_ORDER: readonly string[];
|
|
19
|
+
/**
|
|
20
|
+
* An ordered, case-preserving header list.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately not a `Headers` subclass and deliberately not backed by one: the whole point is to
|
|
23
|
+
* be the thing `Headers` is not. Lookup and mutation are case-insensitive, as HTTP requires;
|
|
24
|
+
* iteration returns names exactly as they were written, in the order they arrived.
|
|
25
|
+
*/
|
|
26
|
+
export class OrderedHeaders {
|
|
27
|
+
/** @param {Headers | Iterable<[string, string]> | Record<string, string> | null} [init] */
|
|
28
|
+
constructor(init?: Headers | Iterable<[string, string]> | Record<string, string> | null);
|
|
29
|
+
/** @type {Array<[string, string]>} name as written, value */
|
|
30
|
+
_list: Array<[string, string]>;
|
|
31
|
+
_indexOf(name: any): number;
|
|
32
|
+
has(name: any): boolean;
|
|
33
|
+
/** Comma-joined when a field appears more than once, matching `Headers.get`. */
|
|
34
|
+
get(name: any): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Replace IN PLACE when the field is already present, so setting a value does not move a header
|
|
37
|
+
* to the end and silently reorder the request. A caller who wrote `User-Agent` first and then
|
|
38
|
+
* had it overwritten should still see it first.
|
|
39
|
+
*/
|
|
40
|
+
set(name: any, value: any): void;
|
|
41
|
+
append(name: any, value: any): void;
|
|
42
|
+
delete(name: any): void;
|
|
43
|
+
/**
|
|
44
|
+
* Put the fields into `order`, which names lowercased header names and may contain `'*'` to mark
|
|
45
|
+
* where everything unnamed goes. Fields keep their relative order within each group, so a
|
|
46
|
+
* caller's own sequence survives.
|
|
47
|
+
*
|
|
48
|
+
* @param {readonly string[]} order
|
|
49
|
+
*/
|
|
50
|
+
reorder(order: readonly string[]): void;
|
|
51
|
+
/** @returns {Array<[string, string]>} names as written, in order */
|
|
52
|
+
entries(): Array<[string, string]>;
|
|
53
|
+
/** Lowercased names, for HTTP/2 where RFC 9113 s8.2.1 requires them. */
|
|
54
|
+
lowercased(): string[][];
|
|
55
|
+
[Symbol.iterator](): ArrayIterator<[string, string]>;
|
|
56
|
+
}
|
package/types/client.d.ts
CHANGED
|
@@ -64,7 +64,30 @@ export function install(options?: ClientOptions): () => void;
|
|
|
64
64
|
* yours and visible. Measured on the edge: WASM brotli decodes at about 2x native gzip, and
|
|
65
65
|
* the wire bytes it saves do not pay that back — see the README. The reason to turn it on is
|
|
66
66
|
* matching a browser's Accept-Encoding, not saving CPU.
|
|
67
|
+
* @property {{ chacha20?: import('./tls/aead.js').AeadOptions['impl'] }} [ciphers] injected AEAD
|
|
68
|
+
* implementations, by capability name. `chacha20` (seal/open, RFC 8439) is what lets
|
|
69
|
+
* TLS_CHACHA20_POLY1305_SHA256 be offered and performed — WebCrypto has no ChaCha20 on this
|
|
70
|
+
* runtime, and a suite offered but not performable is a dead connection if a server selects it,
|
|
71
|
+
* so without this the suite stays out of the ClientHello. Required by `profiles.chrome`.
|
|
72
|
+
* @property {{ x25519mlkem768?: import('./tls/hybrid.js').MlKem768 }} [groups] injected key-exchange
|
|
73
|
+
* implementations, by capability name. `x25519mlkem768` (ML-KEM-768 keygen/encapsulate/
|
|
74
|
+
* decapsulate) is what lets the post-quantum hybrid group be offered and performed; without it
|
|
75
|
+
* the group stays out of the ClientHello. Required by `profiles.chrome`.
|
|
67
76
|
* @property {boolean} [keepAlive] default true.
|
|
77
|
+
* @property {import('./profiles.js').FingerprintProfile} [profile] one coherent network identity
|
|
78
|
+
* instead of a dozen knobs that can disagree — TLS, HTTP/2, header order and default headers
|
|
79
|
+
* together. Explicit options win over it. A profile that declares capabilities this package
|
|
80
|
+
* cannot perform is REFUSED rather than silently reduced: see `profiles.chrome`.
|
|
81
|
+
* @property {readonly string[]} [headerOrder] request header names, lowercased, in the order to
|
|
82
|
+
* emit them; `'*'` marks where headers not named go, in the order the caller gave them. Defaults
|
|
83
|
+
* to curl's (`CURL_HEADER_ORDER`). The platform `Headers` sorts alphabetically and lowercases, so
|
|
84
|
+
* without this a request goes out with `user-agent` last, which no real client does.
|
|
85
|
+
* @property {string[]} [http2PseudoHeaderOrder] request pseudo-headers in the order to emit them.
|
|
86
|
+
* Defaults to curl's. Any of the four omitted is appended rather than dropped: RFC 9113 s8.3.1
|
|
87
|
+
* makes all four mandatory, so a request missing one is malformed rather than merely unusual.
|
|
88
|
+
* @property {Record<string, 'incremental'|'without'|'never'>} [http2HpackIndexing] per-field HPACK
|
|
89
|
+
* indexing. Which fields enter the dynamic table is read by an Akamai-style h2 fingerprint.
|
|
90
|
+
* Defaults to curl's: everything incremental except `:path`.
|
|
68
91
|
* @property {Array<[number, number]>} [http2Settings] the HTTP/2 SETTINGS flight, as [id, value]
|
|
69
92
|
* pairs. Order is significant — an Akamai-style h2 fingerprint reads the ids in the order they
|
|
70
93
|
* are sent — so this replaces the flight rather than merging into it. Defaults to curl's. The
|
|
@@ -223,10 +246,55 @@ export type ClientOptions = {
|
|
|
223
246
|
* matching a browser's Accept-Encoding, not saving CPU.
|
|
224
247
|
*/
|
|
225
248
|
decoders?: Record<string, import("./client/decode.js").BodyDecoder> | undefined;
|
|
249
|
+
/**
|
|
250
|
+
* injected AEAD
|
|
251
|
+
* implementations, by capability name. `chacha20` (seal/open, RFC 8439) is what lets
|
|
252
|
+
* TLS_CHACHA20_POLY1305_SHA256 be offered and performed — WebCrypto has no ChaCha20 on this
|
|
253
|
+
* runtime, and a suite offered but not performable is a dead connection if a server selects it,
|
|
254
|
+
* so without this the suite stays out of the ClientHello. Required by `profiles.chrome`.
|
|
255
|
+
*/
|
|
256
|
+
ciphers?: {
|
|
257
|
+
chacha20?: import("./tls/aead.js").AeadOptions["impl"];
|
|
258
|
+
} | undefined;
|
|
259
|
+
/**
|
|
260
|
+
* injected key-exchange
|
|
261
|
+
* implementations, by capability name. `x25519mlkem768` (ML-KEM-768 keygen/encapsulate/
|
|
262
|
+
* decapsulate) is what lets the post-quantum hybrid group be offered and performed; without it
|
|
263
|
+
* the group stays out of the ClientHello. Required by `profiles.chrome`.
|
|
264
|
+
*/
|
|
265
|
+
groups?: {
|
|
266
|
+
x25519mlkem768?: import("./tls/hybrid.js").MlKem768;
|
|
267
|
+
} | undefined;
|
|
226
268
|
/**
|
|
227
269
|
* default true.
|
|
228
270
|
*/
|
|
229
271
|
keepAlive?: boolean | undefined;
|
|
272
|
+
/**
|
|
273
|
+
* one coherent network identity
|
|
274
|
+
* instead of a dozen knobs that can disagree — TLS, HTTP/2, header order and default headers
|
|
275
|
+
* together. Explicit options win over it. A profile that declares capabilities this package
|
|
276
|
+
* cannot perform is REFUSED rather than silently reduced: see `profiles.chrome`.
|
|
277
|
+
*/
|
|
278
|
+
profile?: import("./profiles.js").FingerprintProfile | undefined;
|
|
279
|
+
/**
|
|
280
|
+
* request header names, lowercased, in the order to
|
|
281
|
+
* emit them; `'*'` marks where headers not named go, in the order the caller gave them. Defaults
|
|
282
|
+
* to curl's (`CURL_HEADER_ORDER`). The platform `Headers` sorts alphabetically and lowercases, so
|
|
283
|
+
* without this a request goes out with `user-agent` last, which no real client does.
|
|
284
|
+
*/
|
|
285
|
+
headerOrder?: readonly string[] | undefined;
|
|
286
|
+
/**
|
|
287
|
+
* request pseudo-headers in the order to emit them.
|
|
288
|
+
* Defaults to curl's. Any of the four omitted is appended rather than dropped: RFC 9113 s8.3.1
|
|
289
|
+
* makes all four mandatory, so a request missing one is malformed rather than merely unusual.
|
|
290
|
+
*/
|
|
291
|
+
http2PseudoHeaderOrder?: string[] | undefined;
|
|
292
|
+
/**
|
|
293
|
+
* per-field HPACK
|
|
294
|
+
* indexing. Which fields enter the dynamic table is read by an Akamai-style h2 fingerprint.
|
|
295
|
+
* Defaults to curl's: everything incremental except `:path`.
|
|
296
|
+
*/
|
|
297
|
+
http2HpackIndexing?: Record<string, "without" | "incremental" | "never"> | undefined;
|
|
230
298
|
/**
|
|
231
299
|
* the HTTP/2 SETTINGS flight, as [id, value]
|
|
232
300
|
* pairs. Order is significant — an Akamai-style h2 fingerprint reads the ids in the order they
|
|
@@ -13,7 +13,7 @@ export function buildRequestFields({ method, scheme, authority, path, headers }:
|
|
|
13
13
|
authority: string;
|
|
14
14
|
path: string;
|
|
15
15
|
headers: Array<[string, string]>;
|
|
16
|
-
}): import("./hpack.js").HpackField[];
|
|
16
|
+
}, opts?: {}): import("./hpack.js").HpackField[];
|
|
17
17
|
/**
|
|
18
18
|
* @typedef {ReadableStream<Uint8Array> & { completed: Promise<boolean>,
|
|
19
19
|
* trailers: Promise<Headers | null> }} BodyStream
|
|
@@ -76,6 +76,8 @@ export class Http2Connection {
|
|
|
76
76
|
_maxHeaderBlockBytes: number;
|
|
77
77
|
/** @type {Array<[number, number]> | null} the SETTINGS flight, ids and order included */
|
|
78
78
|
_settingsFlight: Array<[number, number]> | null;
|
|
79
|
+
_pseudoHeaderOrder: string[] | null;
|
|
80
|
+
_hpackIndexing: Record<string, "without" | "incremental" | "never"> | null;
|
|
79
81
|
_expectFirstSettings: boolean;
|
|
80
82
|
_fatal: any;
|
|
81
83
|
_goaway: {
|
|
@@ -254,6 +256,19 @@ export type Http2ConnectionOptions = {
|
|
|
254
256
|
* self-protection cap on a decoded response header list.
|
|
255
257
|
*/
|
|
256
258
|
maxHeaderListSize?: number | undefined;
|
|
259
|
+
/**
|
|
260
|
+
* request pseudo-headers in the order to emit them.
|
|
261
|
+
* Defaults to curl's `[':method', ':scheme', ':authority', ':path']`. Any of the four left out is
|
|
262
|
+
* appended rather than dropped — RFC 9113 s8.3.1 makes all four mandatory and a request missing
|
|
263
|
+
* one is malformed, which is not a fingerprint choice anyone should be able to make by accident.
|
|
264
|
+
*/
|
|
265
|
+
pseudoHeaderOrder?: string[] | undefined;
|
|
266
|
+
/**
|
|
267
|
+
* per-field HPACK
|
|
268
|
+
* indexing. Which fields enter the dynamic table is part of the fingerprint. Defaults to curl's:
|
|
269
|
+
* everything incremental except `:path`, which is sent without indexing.
|
|
270
|
+
*/
|
|
271
|
+
hpackIndexing?: Record<string, "without" | "incremental" | "never"> | undefined;
|
|
257
272
|
/**
|
|
258
273
|
* the SETTINGS flight sent in the connection
|
|
259
274
|
* preface, as [id, value] pairs. Order is significant — an Akamai-style HTTP/2 fingerprint reads
|
package/types/http2/hpack.d.ts
CHANGED
|
@@ -79,7 +79,7 @@ export type HpackField = {
|
|
|
79
79
|
* how to represent it when it is not a
|
|
80
80
|
* full static match. Default 'incremental', which is what curl uses for most fields.
|
|
81
81
|
*/
|
|
82
|
-
indexing?: "
|
|
82
|
+
indexing?: "without" | "incremental" | "never" | undefined;
|
|
83
83
|
};
|
|
84
84
|
export type HpackDecoderOptions = {
|
|
85
85
|
/**
|
package/types/index.d.ts
CHANGED
|
@@ -15,3 +15,4 @@ export { openConnection, targetFromUrl, nativeFetchCanServe } from "./transport.
|
|
|
15
15
|
export { openTunnel, parseProxy } from "./proxy/index.js";
|
|
16
16
|
export { verifyChain, rootStoreProvenance } from "./trust/index.js";
|
|
17
17
|
export { TunnelFetchError, ProxyError, HttpError, TlsError, TlsUnsupportedError, Http2Error, CertificateError, TimeoutError, LimitError, ConfigError, codes } from "./errors.js";
|
|
18
|
+
export { profiles, curl, chrome, applyProfile } from "./profiles.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `profiles.chrome` with the two capabilities this package cannot perform natively already wired
|
|
3
|
+
* in. Everything else about it — the cipher list, the groups, the shuffled extension order, GREASE,
|
|
4
|
+
* the HTTP/2 SETTINGS and pseudo-header order, the request header order — comes from the
|
|
5
|
+
* declaration unchanged, and all of it was captured off the wire from Chrome 150.
|
|
6
|
+
*
|
|
7
|
+
* @type {import('../profiles.js').FingerprintProfile & {
|
|
8
|
+
* ciphers: Record<string, unknown>, groups: Record<string, unknown> }}
|
|
9
|
+
*/
|
|
10
|
+
export const chrome: import("../profiles.js").FingerprintProfile & {
|
|
11
|
+
ciphers: Record<string, unknown>;
|
|
12
|
+
groups: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
import { chacha20poly1305 } from './vendor/chacha20poly1305.js';
|
|
15
|
+
import { mlkem768 } from './vendor/mlkem768.js';
|
|
16
|
+
export { chacha20poly1305, mlkem768 };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const buildId: "ec15f83a678a";
|
|
2
|
+
export namespace chacha20poly1305 {
|
|
3
|
+
/** seal(key32, nonce12, plaintext, aad?) -> ciphertext||tag (RFC 8439 AEAD) */
|
|
4
|
+
function seal(key: any, nonce: any, plaintext: any, aad?: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>;
|
|
5
|
+
/** open(key32, nonce12, ciphertextAndTag, aad?) -> plaintext; throws on auth failure */
|
|
6
|
+
function open(key: any, nonce: any, ciphertext: any, aad?: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const buildId: "e17c38843d1e";
|
|
2
|
+
export namespace mlkem768 {
|
|
3
|
+
export { PK_B as publicKeyBytes };
|
|
4
|
+
export { SK_B as secretKeyBytes };
|
|
5
|
+
export { CT_B as cipherTextBytes };
|
|
6
|
+
export let sharedSecretBytes: number;
|
|
7
|
+
/** keygen(seed?) — seed is d||z (64 bytes) for ML-KEM.KeyGen_internal; omitted = fresh CSPRNG */
|
|
8
|
+
export function keygen(seed: any): {
|
|
9
|
+
publicKey: Uint8Array<ArrayBuffer>;
|
|
10
|
+
secretKey: Uint8Array<ArrayBuffer>;
|
|
11
|
+
};
|
|
12
|
+
/** encapsulate(publicKey, msg?) — msg is m (32 bytes) for ML-KEM.Encaps_internal */
|
|
13
|
+
export function encapsulate(publicKey: any, msg: any): {
|
|
14
|
+
cipherText: Uint8Array<ArrayBuffer>;
|
|
15
|
+
sharedSecret: Uint8Array<ArrayBuffer>;
|
|
16
|
+
};
|
|
17
|
+
/** decapsulate(cipherText, secretKey) -> sharedSecret (implicit rejection per FIPS 203) */
|
|
18
|
+
export function decapsulate(cipherText: any, secretKey: any): Uint8Array<ArrayBuffer>;
|
|
19
|
+
}
|
|
20
|
+
declare const PK_B: any;
|
|
21
|
+
declare const SK_B: any;
|
|
22
|
+
declare const CT_B: any;
|
|
23
|
+
export {};
|