tunnelfetch 1.0.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.
Files changed (96) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +617 -0
  3. package/README.zh-CN.md +470 -0
  4. package/package.json +74 -0
  5. package/src/client/cookies.js +429 -0
  6. package/src/client/decode.js +346 -0
  7. package/src/client/redirect.js +249 -0
  8. package/src/client.js +704 -0
  9. package/src/errors.js +181 -0
  10. package/src/http1/chunked.js +289 -0
  11. package/src/http1/index.js +10 -0
  12. package/src/http1/request.js +143 -0
  13. package/src/http1/response.js +493 -0
  14. package/src/http2/connection.js +1170 -0
  15. package/src/http2/constants.js +129 -0
  16. package/src/http2/frames.js +291 -0
  17. package/src/http2/hpack.js +420 -0
  18. package/src/http2/huffman.js +203 -0
  19. package/src/http2/index.js +21 -0
  20. package/src/index.js +46 -0
  21. package/src/pool.js +256 -0
  22. package/src/proxy/direct.js +62 -0
  23. package/src/proxy/http-connect.js +206 -0
  24. package/src/proxy/index.js +197 -0
  25. package/src/proxy/socks5.js +344 -0
  26. package/src/tls/aead.js +263 -0
  27. package/src/tls/connect.js +407 -0
  28. package/src/tls/constants.js +334 -0
  29. package/src/tls/extensions.js +376 -0
  30. package/src/tls/handshake-messages.js +901 -0
  31. package/src/tls/handshake.js +568 -0
  32. package/src/tls/handshake12.js +507 -0
  33. package/src/tls/index.js +44 -0
  34. package/src/tls/keyschedule.js +473 -0
  35. package/src/tls/record.js +872 -0
  36. package/src/tls/tickets.js +145 -0
  37. package/src/tls/transcript.js +101 -0
  38. package/src/tls/wire.js +224 -0
  39. package/src/transport.js +296 -0
  40. package/src/trust/der.js +551 -0
  41. package/src/trust/index.js +375 -0
  42. package/src/trust/name.js +235 -0
  43. package/src/trust/ocsp.js +759 -0
  44. package/src/trust/path.js +595 -0
  45. package/src/trust/roots.js +454 -0
  46. package/src/trust/x509.js +902 -0
  47. package/src/util/bytes.js +470 -0
  48. package/src/util/deadline.js +266 -0
  49. package/src/warmup-fixture.js +85 -0
  50. package/src/warmup.js +243 -0
  51. package/types/client/cookies.d.ts +159 -0
  52. package/types/client/decode.d.ts +54 -0
  53. package/types/client/redirect.d.ts +96 -0
  54. package/types/client.d.ts +323 -0
  55. package/types/errors.d.ts +141 -0
  56. package/types/http1/chunked.d.ts +48 -0
  57. package/types/http1/index.d.ts +3 -0
  58. package/types/http1/request.d.ts +44 -0
  59. package/types/http1/response.d.ts +183 -0
  60. package/types/http2/connection.d.ts +282 -0
  61. package/types/http2/constants.d.ts +95 -0
  62. package/types/http2/frames.d.ts +116 -0
  63. package/types/http2/hpack.d.ts +99 -0
  64. package/types/http2/huffman.d.ts +21 -0
  65. package/types/http2/index.d.ts +5 -0
  66. package/types/index.d.ts +17 -0
  67. package/types/pool.d.ts +135 -0
  68. package/types/proxy/direct.d.ts +26 -0
  69. package/types/proxy/http-connect.d.ts +37 -0
  70. package/types/proxy/index.d.ts +62 -0
  71. package/types/proxy/socks5.d.ts +47 -0
  72. package/types/tls/aead.d.ts +67 -0
  73. package/types/tls/connect.d.ts +280 -0
  74. package/types/tls/constants.d.ts +275 -0
  75. package/types/tls/extensions.d.ts +195 -0
  76. package/types/tls/handshake-messages.d.ts +430 -0
  77. package/types/tls/handshake.d.ts +90 -0
  78. package/types/tls/handshake12.d.ts +35 -0
  79. package/types/tls/index.d.ts +9 -0
  80. package/types/tls/keyschedule.d.ts +272 -0
  81. package/types/tls/record.d.ts +361 -0
  82. package/types/tls/tickets.d.ts +66 -0
  83. package/types/tls/transcript.d.ts +52 -0
  84. package/types/tls/wire.d.ts +106 -0
  85. package/types/transport.d.ts +222 -0
  86. package/types/trust/der.d.ts +239 -0
  87. package/types/trust/index.d.ts +194 -0
  88. package/types/trust/name.d.ts +33 -0
  89. package/types/trust/ocsp.d.ts +138 -0
  90. package/types/trust/path.d.ts +139 -0
  91. package/types/trust/roots.d.ts +36 -0
  92. package/types/trust/x509.d.ts +401 -0
  93. package/types/util/bytes.d.ts +183 -0
  94. package/types/util/deadline.d.ts +133 -0
  95. package/types/warmup-fixture.d.ts +11 -0
  96. package/types/warmup.d.ts +45 -0
@@ -0,0 +1,272 @@
1
+ /**
2
+ * The only hashes reachable through this schedule; see the module comment for why the type
3
+ * refuses the rest of WebCrypto's registry.
4
+ * @typedef {'SHA-256' | 'SHA-384'} ScheduleHash
5
+ */
6
+ /**
7
+ * @param {ScheduleHash} hash
8
+ * @returns {number} digest length in bytes; throws for any other hash name
9
+ */
10
+ export function hashLength(hash: ScheduleHash): number;
11
+ /**
12
+ * HMAC-Hash(key, data) for a single message. For repeated MACs under one key, import once with
13
+ * importHmacKey and call crypto.subtle.sign directly instead.
14
+ * @param {ScheduleHash} hash
15
+ * @param {Uint8Array} keyBytes
16
+ * @param {Uint8Array} data
17
+ * @returns {Promise<Uint8Array>}
18
+ */
19
+ export function hmac(hash: ScheduleHash, keyBytes: Uint8Array, data: Uint8Array): Promise<Uint8Array>;
20
+ /**
21
+ * HKDF-Extract(salt, IKM) = HMAC-Hash(salt, IKM). RFC 5869 s2.2.
22
+ * @param {ScheduleHash} hash
23
+ * @param {Uint8Array} salt
24
+ * @param {Uint8Array} ikm
25
+ * @returns {Promise<Uint8Array>}
26
+ */
27
+ export function hkdfExtract(hash: ScheduleHash, salt: Uint8Array, ikm: Uint8Array): Promise<Uint8Array>;
28
+ /**
29
+ * HKDF-Expand(PRK, info, L). RFC 5869 s2.3: T(i) = HMAC(PRK, T(i-1) || info || i), i in 1..N.
30
+ * @param {ScheduleHash} hash
31
+ * @param {Uint8Array} prk
32
+ * @param {Uint8Array} info
33
+ * @param {number} length
34
+ * @returns {Promise<Uint8Array>}
35
+ */
36
+ export function hkdfExpand(hash: ScheduleHash, prk: Uint8Array, info: Uint8Array, length: number): Promise<Uint8Array>;
37
+ /**
38
+ * The HkdfLabel struct from RFC 8446 s7.1, exported separately so the tests can compare our
39
+ * encoding byte-for-byte against the `info` fields printed in the RFC 8448 traces:
40
+ *
41
+ * struct {
42
+ * uint16 length;
43
+ * opaque label<7..255>; // "tls13 " + Label
44
+ * opaque context<0..255>;
45
+ * } HkdfLabel;
46
+ *
47
+ * @param {string} label label WITHOUT the "tls13 " prefix
48
+ * @param {Uint8Array} context
49
+ * @param {number} length
50
+ * @returns {Uint8Array}
51
+ */
52
+ export function hkdfLabel(label: string, context: Uint8Array, length: number): Uint8Array;
53
+ /**
54
+ * HKDF-Expand-Label(Secret, Label, Context, Length). RFC 8446 s7.1.
55
+ * @param {ScheduleHash} hash
56
+ * @param {Uint8Array} secret
57
+ * @param {string} label
58
+ * @param {Uint8Array} context
59
+ * @param {number} length
60
+ * @returns {Promise<Uint8Array>}
61
+ */
62
+ export function hkdfExpandLabel(hash: ScheduleHash, secret: Uint8Array, label: string, context: Uint8Array, length: number): Promise<Uint8Array>;
63
+ /**
64
+ * Derive-Secret(Secret, Label, Messages) — the transcript is passed already hashed.
65
+ * @param {ScheduleHash} hash
66
+ * @param {Uint8Array} secret
67
+ * @param {string} label
68
+ * @param {Uint8Array} transcriptHash
69
+ * @returns {Promise<Uint8Array>}
70
+ */
71
+ export function deriveSecret(hash: ScheduleHash, secret: Uint8Array, label: string, transcriptHash: Uint8Array): Promise<Uint8Array>;
72
+ /**
73
+ * @param {ScheduleHash} hash
74
+ * @returns {Promise<Uint8Array>}
75
+ */
76
+ export function emptyHash(hash: ScheduleHash): Promise<Uint8Array>;
77
+ /**
78
+ * Early Secret = HKDF-Extract(salt: 0, IKM: PSK or zeros).
79
+ * @param {ScheduleHash} hash
80
+ * @param {Uint8Array | null} [psk]
81
+ * @returns {Promise<Uint8Array>}
82
+ */
83
+ export function earlySecret(hash: ScheduleHash, psk?: Uint8Array | null): Promise<Uint8Array>;
84
+ /**
85
+ * Handshake Secret = HKDF-Extract(Derive-Secret(early, "derived", ""), ECDHE).
86
+ * @param {ScheduleHash} hash
87
+ * @param {Uint8Array} early
88
+ * @param {Uint8Array} ecdheShared
89
+ * @returns {Promise<Uint8Array>}
90
+ */
91
+ export function deriveHandshakeSecret(hash: ScheduleHash, early: Uint8Array, ecdheShared: Uint8Array): Promise<Uint8Array>;
92
+ /**
93
+ * Master Secret = HKDF-Extract(Derive-Secret(handshake, "derived", ""), 0).
94
+ * @param {ScheduleHash} hash
95
+ * @param {Uint8Array} handshakeSecret
96
+ * @returns {Promise<Uint8Array>}
97
+ */
98
+ export function deriveMasterSecret(hash: ScheduleHash, handshakeSecret: Uint8Array): Promise<Uint8Array>;
99
+ /**
100
+ * {client,server}_handshake_traffic_secret. Transcript: ClientHello..ServerHello.
101
+ * @param {ScheduleHash} hash
102
+ * @param {Uint8Array} handshakeSecret
103
+ * @param {Uint8Array} transcriptHash
104
+ * @returns {Promise<{ client: Uint8Array, server: Uint8Array }>}
105
+ */
106
+ export function handshakeTrafficSecrets(hash: ScheduleHash, handshakeSecret: Uint8Array, transcriptHash: Uint8Array): Promise<{
107
+ client: Uint8Array;
108
+ server: Uint8Array;
109
+ }>;
110
+ /**
111
+ * {client,server}_application_traffic_secret_0 and (unless suppressed) exporter_master_secret.
112
+ *
113
+ * `exporter` defaults on so the RFC 8448 vectors and any exporter user get the full set, but the
114
+ * handshake driver passes false: this package exposes no TLS-exporter interface, so deriving
115
+ * exporter_master_secret on every connection is one HKDF-Expand-Label of provably dead work on
116
+ * the hot path. Suppressing it there removes that work without changing any observable behaviour.
117
+ * @param {ScheduleHash} hash
118
+ * @param {Uint8Array} masterSecret
119
+ * @param {Uint8Array} transcriptHash
120
+ * @param {{ exporter?: boolean }} [opts]
121
+ * @returns {Promise<{ client: Uint8Array, server: Uint8Array, exporterMaster?: Uint8Array }>}
122
+ */
123
+ export function applicationTrafficSecrets(hash: ScheduleHash, masterSecret: Uint8Array, transcriptHash: Uint8Array, { exporter }?: {
124
+ exporter?: boolean;
125
+ }): Promise<{
126
+ client: Uint8Array;
127
+ server: Uint8Array;
128
+ exporterMaster?: Uint8Array;
129
+ }>;
130
+ /**
131
+ * resumption_master_secret. Transcript: ClientHello..client Finished.
132
+ * @param {ScheduleHash} hash
133
+ * @param {Uint8Array} masterSecret
134
+ * @param {Uint8Array} transcriptHash
135
+ * @returns {Promise<Uint8Array>}
136
+ */
137
+ export function resumptionMasterSecret(hash: ScheduleHash, masterSecret: Uint8Array, transcriptHash: Uint8Array): Promise<Uint8Array>;
138
+ /**
139
+ * The PSK a NewSessionTicket names: HKDF-Expand-Label(res master, "resumption", nonce).
140
+ * @param {ScheduleHash} hash
141
+ * @param {Uint8Array} resumptionMaster
142
+ * @param {Uint8Array} ticketNonce
143
+ * @returns {Promise<Uint8Array>}
144
+ */
145
+ export function resumptionPsk(hash: ScheduleHash, resumptionMaster: Uint8Array, ticketNonce: Uint8Array): Promise<Uint8Array>;
146
+ /**
147
+ * binder_key = Derive-Secret(Early Secret, "res binder", "") — RFC 8446 s7.1, the resumption
148
+ * variant. The "ext binder" sibling for externally provisioned PSKs is deliberately absent:
149
+ * this package mints PSKs only from NewSessionTicket, so an external-PSK binder key would be
150
+ * dead code with a security label on it. The binder value itself is finishedVerifyData over
151
+ * this key (RFC 8446 s4.2.11.2: "the PskBinderEntry is computed in the same way as the
152
+ * Finished message but with the BaseKey being the binder_key").
153
+ * @param {ScheduleHash} hash
154
+ * @param {Uint8Array} early the Early Secret extracted from the PSK being offered
155
+ * @returns {Promise<Uint8Array>}
156
+ */
157
+ export function resumptionBinderKey(hash: ScheduleHash, early: Uint8Array): Promise<Uint8Array>;
158
+ /**
159
+ * finished_key = HKDF-Expand-Label(BaseKey, "finished", "", Hash.length). RFC 8446 s4.4.4.
160
+ * @param {ScheduleHash} hash
161
+ * @param {Uint8Array} trafficSecret
162
+ * @returns {Promise<Uint8Array>}
163
+ */
164
+ export function finishedKey(hash: ScheduleHash, trafficSecret: Uint8Array): Promise<Uint8Array>;
165
+ /**
166
+ * verify_data = HMAC(finished_key, Transcript-Hash).
167
+ * @param {ScheduleHash} hash
168
+ * @param {Uint8Array} trafficSecret
169
+ * @param {Uint8Array} transcriptHash
170
+ * @returns {Promise<Uint8Array>}
171
+ */
172
+ export function finishedVerifyData(hash: ScheduleHash, trafficSecret: Uint8Array, transcriptHash: Uint8Array): Promise<Uint8Array>;
173
+ /**
174
+ * [sender]_write_key and [sender]_write_iv from a traffic secret. RFC 8446 s7.3.
175
+ * @param {ScheduleHash} hash
176
+ * @param {Uint8Array} trafficSecret
177
+ * @param {number} keyLen
178
+ * @param {number} ivLen
179
+ * @returns {Promise<{ key: Uint8Array, iv: Uint8Array }>}
180
+ */
181
+ export function trafficKeys(hash: ScheduleHash, trafficSecret: Uint8Array, keyLen: number, ivLen: number): Promise<{
182
+ key: Uint8Array;
183
+ iv: Uint8Array;
184
+ }>;
185
+ /**
186
+ * application_traffic_secret_N+1 for KeyUpdate. RFC 8446 s7.2.
187
+ * @param {ScheduleHash} hash
188
+ * @param {Uint8Array} trafficSecret
189
+ * @returns {Promise<Uint8Array>}
190
+ */
191
+ export function nextTrafficSecret(hash: ScheduleHash, trafficSecret: Uint8Array): Promise<Uint8Array>;
192
+ /**
193
+ * PRF(secret, label, seed) = P_<hash>(secret, label + seed), where
194
+ * P_hash(secret, seed) = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ...
195
+ * with A(0) = seed, A(i) = HMAC(secret, A(i-1)).
196
+ *
197
+ * TLS 1.2 replaced the 1.1 MD5/SHA-1 split PRF with a single suite-selected hash, which for
198
+ * every suite in constants.js is SHA-256 or SHA-384.
199
+ *
200
+ * @param {ScheduleHash} hash
201
+ * @param {Uint8Array} secret
202
+ * @param {string} label ASCII label, e.g. 'master secret'
203
+ * @param {Uint8Array} seed
204
+ * @param {number} length
205
+ * @returns {Promise<Uint8Array>}
206
+ */
207
+ export function prf12(hash: ScheduleHash, secret: Uint8Array, label: string, seed: Uint8Array, length: number): Promise<Uint8Array>;
208
+ /**
209
+ * master_secret = PRF(pre_master, "master secret", client_random + server_random)[0..47].
210
+ * @param {ScheduleHash} hash
211
+ * @param {Uint8Array} preMaster
212
+ * @param {Uint8Array} clientRandom
213
+ * @param {Uint8Array} serverRandom
214
+ * @returns {Promise<Uint8Array>}
215
+ */
216
+ export function masterSecret12(hash: ScheduleHash, preMaster: Uint8Array, clientRandom: Uint8Array, serverRandom: Uint8Array): Promise<Uint8Array>;
217
+ /**
218
+ * RFC 7627 extended master secret: the seed is the session hash (transcript through
219
+ * ClientKeyExchange) instead of the two randoms, which binds the master secret to the full
220
+ * handshake and kills the triple-handshake attack. This is the variant the handshake layer
221
+ * must offer and prefer; a server that refuses the extension still gets masterSecret12, but
222
+ * that acceptance is deliberately flagged by the handshake layer as a weaker session.
223
+ * @param {ScheduleHash} hash
224
+ * @param {Uint8Array} preMaster
225
+ * @param {Uint8Array} sessionHash
226
+ * @returns {Promise<Uint8Array>}
227
+ */
228
+ export function extendedMasterSecret12(hash: ScheduleHash, preMaster: Uint8Array, sessionHash: Uint8Array): Promise<Uint8Array>;
229
+ /**
230
+ * key_block = PRF(master, "key expansion", server_random + client_random). RFC 5246 s6.3.
231
+ * Note the randoms swap order relative to the master secret derivation — that asymmetry is in
232
+ * the RFC, and the tests pin it. AEAD suites have no MAC keys (macLen 0), so the block is
233
+ * client_write_key + server_write_key + client_write_IV + server_write_IV, where the "IV" is
234
+ * the 4-byte implicit GCM salt of RFC 5288.
235
+ *
236
+ * @param {ScheduleHash} hash
237
+ * @param {Uint8Array} master
238
+ * @param {Uint8Array} clientRandom
239
+ * @param {Uint8Array} serverRandom
240
+ * @param {{ keyLen: number, fixedIvLen: number, macLen?: number }} lens
241
+ * @returns {Promise<{ clientWriteKey: Uint8Array, serverWriteKey: Uint8Array,
242
+ * clientWriteIv: Uint8Array, serverWriteIv: Uint8Array,
243
+ * clientWriteMacKey?: Uint8Array, serverWriteMacKey?: Uint8Array }>} MAC keys present only
244
+ * when macLen > 0, which no AEAD suite ever passes
245
+ */
246
+ export function keyBlock12(hash: ScheduleHash, master: Uint8Array, clientRandom: Uint8Array, serverRandom: Uint8Array, lens: {
247
+ keyLen: number;
248
+ fixedIvLen: number;
249
+ macLen?: number;
250
+ }): Promise<{
251
+ clientWriteKey: Uint8Array;
252
+ serverWriteKey: Uint8Array;
253
+ clientWriteIv: Uint8Array;
254
+ serverWriteIv: Uint8Array;
255
+ clientWriteMacKey?: Uint8Array;
256
+ serverWriteMacKey?: Uint8Array;
257
+ }>;
258
+ /**
259
+ * verify_data = PRF(master, "client finished" | "server finished", Hash(messages))[0..11].
260
+ * 12 bytes for every suite this package negotiates (RFC 5246 s7.4.9).
261
+ * @param {ScheduleHash} hash
262
+ * @param {Uint8Array} master
263
+ * @param {'client finished' | 'server finished'} label
264
+ * @param {Uint8Array} transcriptHash
265
+ * @returns {Promise<Uint8Array>}
266
+ */
267
+ export function verifyData12(hash: ScheduleHash, master: Uint8Array, label: "client finished" | "server finished", transcriptHash: Uint8Array): Promise<Uint8Array>;
268
+ /**
269
+ * The only hashes reachable through this schedule; see the module comment for why the type
270
+ * refuses the rest of WebCrypto's registry.
271
+ */
272
+ export type ScheduleHash = "SHA-256" | "SHA-384";
@@ -0,0 +1,361 @@
1
+ /**
2
+ * A complete handshake message off the wire. `raw` includes the 4-byte header, which is the
3
+ * form the transcript hash consumes.
4
+ * @typedef {object} HandshakeMessage
5
+ * @property {number} type
6
+ * @property {Uint8Array} body
7
+ * @property {Uint8Array} raw
8
+ */
9
+ /**
10
+ * Keys for one direction: a TLS 1.3 traffic `secret` (key and IV derived per RFC 8446 s7.3,
11
+ * KeyUpdate rotation possible) or raw TLS 1.2 key_block slices (no forward rotation).
12
+ * @typedef {{ cipher: number, secret: Uint8Array }
13
+ * | { cipher: number, key: Uint8Array, iv: Uint8Array }} DirectionKeys
14
+ */
15
+ /**
16
+ * @typedef {object} RecordLayerOptions
17
+ * @property {number} [maxHandshakeMessage] per-message cap; certificate chains dominate sizing
18
+ * @property {number} [maxKeyUpdates] received KeyUpdates before we call it a flood
19
+ * @property {number} [shutdownGraceMs] how long a courtesy close_notify or fatal alert may
20
+ * block shutdown before being abandoned, default 2000; see _shutdown()
21
+ * @property {null | ((type: number, length: number) => number)} [padding] extra zero bytes per
22
+ * record, TLS 1.3 only
23
+ * @property {null | ((msg: HandshakeMessage) => void | Promise<void>)} [onPostHandshake]
24
+ * NewSessionTicket consumer; default is to discard
25
+ */
26
+ export class RecordLayer {
27
+ /**
28
+ * @param {import('./connect.js').ByteDuplex} duplex ciphertext transport
29
+ * @param {RecordLayerOptions} [opts]
30
+ */
31
+ constructor({ readable, writable }: import("./connect.js").ByteDuplex, opts?: RecordLayerOptions);
32
+ _r: ByteReader;
33
+ _w: ByteWriter;
34
+ _maxHandshakeMessage: number;
35
+ _maxKeyUpdates: number;
36
+ _shutdownGraceMs: number;
37
+ _padding: ((type: number, length: number) => number) | null;
38
+ _onPostHandshake: ((msg: HandshakeMessage) => void | Promise<void>) | null;
39
+ _version: number;
40
+ /** @typedef {null | { aead: import('./aead.js').Aead, seq: bigint, cipher: number,
41
+ * hash: import('./keyschedule.js').ScheduleHash,
42
+ * secret: Uint8Array | null }} DirectionState */
43
+ /** @type {DirectionState} */
44
+ _send: {
45
+ aead: import("./aead.js").Aead;
46
+ seq: bigint;
47
+ cipher: number;
48
+ hash: import("./keyschedule.js").ScheduleHash;
49
+ secret: Uint8Array | null;
50
+ } | null;
51
+ /** @type {DirectionState} */
52
+ _recv: {
53
+ aead: import("./aead.js").Aead;
54
+ seq: bigint;
55
+ cipher: number;
56
+ hash: import("./keyschedule.js").ScheduleHash;
57
+ secret: Uint8Array | null;
58
+ } | null;
59
+ /** Handshake reassembly. Chunk list, not one growing buffer, so a peer drip-feeding a
60
+ * message in tiny records costs O(records), not O(records^2).
61
+ * @type {Uint8Array[]} */
62
+ _hsChunks: Uint8Array[];
63
+ _hsLen: number;
64
+ /** @type {{ type: number, len: number } | null} header of the message being assembled */
65
+ _hsHeader: {
66
+ type: number;
67
+ len: number;
68
+ } | null;
69
+ _handshakeComplete: boolean;
70
+ _closedByPeer: boolean;
71
+ _closedLocally: boolean;
72
+ /** @type {TlsError | null} first protocol error; sticky */
73
+ _fatal: TlsError | null;
74
+ _anyRecordWritten: boolean;
75
+ _reading: boolean;
76
+ /** All wire emission is funnelled through this chain. Two interleaved writers would
77
+ * reorder — or worse, reuse — AEAD sequence numbers, and with GCM a single reused nonce
78
+ * forfeits the key, so serialization here is a security control, not a convenience. */
79
+ _writeChain: Promise<void>;
80
+ _ignoredCcs: number;
81
+ _ignoredAlerts: number;
82
+ _emptyStreak: number;
83
+ _keyUpdatesReceived: number;
84
+ /**
85
+ * Pin the negotiated version. Chooses CCS semantics (1.3: compatibility noise to ignore;
86
+ * 1.2: a real key-change signal surfaced as `{ ccs: true }`), alert strictness, and AEAD
87
+ * framing. Must happen before any keys are installed.
88
+ * @param {number} version `TLS12` (0x0303) or `TLS13` (0x0304); anything else throws
89
+ */
90
+ setVersion(version: number): void;
91
+ get version(): number;
92
+ get handshakeComplete(): boolean;
93
+ /** The handshake driver calls this after the Finished exchange. Gates CCS and KeyUpdate. */
94
+ markHandshakeComplete(): void;
95
+ /**
96
+ * Install (or replace) the NewSessionTicket consumer after construction. Exists because the
97
+ * consumer needs secrets that do not exist when the record layer is built — the resumption
98
+ * master secret is derived from the transcript through the client Finished — so the driver
99
+ * wires it in at handshake completion. An exception it throws surfaces on the read path and
100
+ * fails the connection, which is the correct fate for a peer whose post-handshake messages do
101
+ * not parse.
102
+ * @param {null | ((msg: HandshakeMessage) => void | Promise<void>)} fn
103
+ */
104
+ setPostHandshake(fn: null | ((msg: HandshakeMessage) => void | Promise<void>)): void;
105
+ /**
106
+ * Install send-direction protection. Pass `secret` (a TLS 1.3 traffic secret; key and IV are
107
+ * derived per RFC 8446 s7.3, and KeyUpdate rotation becomes possible) or raw `key`+`iv`
108
+ * (TLS 1.2 key_block slices, which have no forward rotation). Sequence numbers reset.
109
+ * @param {DirectionKeys} keys
110
+ * @returns {Promise<void>}
111
+ */
112
+ setSendKeys({ cipher, secret, key, iv }: DirectionKeys): Promise<void>;
113
+ /**
114
+ * Install receive-direction protection. Refuses if a partially reassembled handshake
115
+ * message is pending: RFC 8446 s5.1 forbids a handshake message from spanning a key change,
116
+ * and enforcing it here — at the only point where the receive cipher can change — covers
117
+ * both driver-installed keys and KeyUpdate rotation with a single check.
118
+ * @param {DirectionKeys} keys
119
+ * @returns {Promise<void>}
120
+ */
121
+ setReceiveKeys({ cipher, secret, key, iv }: DirectionKeys): Promise<void>;
122
+ /**
123
+ * @param {{ cipher: number, secret?: Uint8Array, key?: Uint8Array, iv?: Uint8Array }} keys
124
+ * @returns {Promise<NonNullable<DirectionState>>}
125
+ */
126
+ _makeState({ cipher, secret, key, iv }: {
127
+ cipher: number;
128
+ secret?: Uint8Array;
129
+ key?: Uint8Array;
130
+ iv?: Uint8Array;
131
+ }): Promise<NonNullable<DirectionState>>;
132
+ /**
133
+ * Next handshake message during the handshake phase.
134
+ * Returns `{ type, body, raw }` (raw includes the 4-byte header, ready for the transcript),
135
+ * `{ ccs: true }` in TLS 1.2 mode when the peer's change_cipher_spec arrives, or `null` if
136
+ * the peer closed cleanly (which mid-handshake the driver should treat as failure).
137
+ * @returns {Promise<HandshakeMessage | { ccs: true } | null>}
138
+ */
139
+ nextHandshakeMessage(): Promise<HandshakeMessage | {
140
+ ccs: true;
141
+ } | null>;
142
+ /**
143
+ * Next application data chunk, or null at clean close_notify EOF. Post-handshake handshake
144
+ * messages (KeyUpdate, NewSessionTicket) are consumed transparently here.
145
+ * @returns {Promise<Uint8Array | null>}
146
+ */
147
+ readAppData(): Promise<Uint8Array | null>;
148
+ /**
149
+ * @template T
150
+ * @param {() => Promise<T>} fn
151
+ * @returns {Promise<T>}
152
+ */
153
+ _guardedRead<T>(fn: () => Promise<T>): Promise<T>;
154
+ /**
155
+ * One protocol event: a complete handshake message, an app-data chunk, a 1.2 CCS, or close.
156
+ * All the "ignore and keep reading" cases (compat CCS, warning alerts, empty app records)
157
+ * loop in here, each behind a flood cap.
158
+ * @returns {Promise<{ kind: 'close' } | { kind: 'ccs' } | { kind: 'data', bytes: Uint8Array }
159
+ * | { kind: 'handshake', msgType: number, body: Uint8Array, raw: Uint8Array }>}
160
+ */
161
+ _nextEvent(): Promise<{
162
+ kind: "close";
163
+ } | {
164
+ kind: "ccs";
165
+ } | {
166
+ kind: "data";
167
+ bytes: Uint8Array;
168
+ } | {
169
+ kind: "handshake";
170
+ msgType: number;
171
+ body: Uint8Array;
172
+ raw: Uint8Array;
173
+ }>;
174
+ /**
175
+ * Complete message off the reassembly buffer, if one is there.
176
+ * @returns {{ kind: 'handshake', msgType: number, body: Uint8Array, raw: Uint8Array } | null}
177
+ */
178
+ _takeHandshakeMessage(): {
179
+ kind: "handshake";
180
+ msgType: number;
181
+ body: Uint8Array;
182
+ raw: Uint8Array;
183
+ } | null;
184
+ /**
185
+ * Read one record off the wire and reduce it to (inner type, plaintext), or null once the
186
+ * peer has said close_notify. Handles decryption, the plaintext/ciphertext legality rules,
187
+ * and TLS 1.3 compatibility CCS.
188
+ * @returns {Promise<{ type: number, data: Uint8Array } | null>}
189
+ */
190
+ _nextPlaintextRecord(): Promise<{
191
+ type: number;
192
+ data: Uint8Array;
193
+ } | null>;
194
+ /**
195
+ * @param {Uint8Array} data
196
+ * @returns {'close' | 'ignored'} or throws for fatal alerts
197
+ */
198
+ _handleAlert(data: Uint8Array): "close" | "ignored";
199
+ /**
200
+ * KeyUpdate and NewSessionTicket arriving under application keys.
201
+ * @param {{ msgType: number, body: Uint8Array, raw: Uint8Array }} msg
202
+ */
203
+ _postHandshakeMessage({ msgType, body, raw }: {
204
+ msgType: number;
205
+ body: Uint8Array;
206
+ raw: Uint8Array;
207
+ }): Promise<void>;
208
+ /**
209
+ * Write one or more complete handshake messages, coalescing them into as few records as
210
+ * possible (the ClientHello flight and the 1.2 client second flight benefit) and
211
+ * fragmenting anything over 2^14.
212
+ * @param {Uint8Array | Uint8Array[]} messages
213
+ */
214
+ writeHandshake(messages: Uint8Array | Uint8Array[]): Promise<void>;
215
+ /**
216
+ * Write application data, fragmented to the record size limit.
217
+ * @param {Uint8Array} bytes
218
+ * @returns {Promise<void>}
219
+ */
220
+ writeAppData(bytes: Uint8Array): Promise<void>;
221
+ /** The one-byte compatibility (1.3) or key-change (1.2) CCS record. Always plaintext. */
222
+ writeChangeCipherSpec(): Promise<void>;
223
+ /**
224
+ * Post-handshake KeyUpdate initiated by us: send under current keys, then rotate our send
225
+ * chain. With `requestPeer` the peer must answer and rotate its own send keys too.
226
+ * @param {{ requestPeer?: boolean }} [opts]
227
+ * @returns {Promise<void>}
228
+ */
229
+ updateKeys({ requestPeer }?: {
230
+ requestPeer?: boolean;
231
+ }): Promise<void>;
232
+ /**
233
+ * @param {number} level `ALERT_LEVEL.warning` (1) or `ALERT_LEVEL.fatal` (2)
234
+ * @param {number} desc alert description byte, per `ALERT_DESC`
235
+ * @returns {Promise<void>}
236
+ */
237
+ sendAlert(level: number, desc: number): Promise<void>;
238
+ /**
239
+ * Clean shutdown: close_notify, then close the transport write side.
240
+ * @returns {Promise<void>}
241
+ */
242
+ close(): Promise<void>;
243
+ /**
244
+ * Shutdown, bounded.
245
+ *
246
+ * The alert is a courtesy: a peer that has stopped reading will never see it, and its write can
247
+ * therefore never complete once the transport's buffer fills. Awaiting that without a bound
248
+ * hangs close() forever on the ordinary path, and hangs abort() on the failure path — where it
249
+ * also swallows the error the caller was about to be given, turning a diagnosable failure into a
250
+ * request that simply never returns. So the courtesy gets a deadline and is then abandoned.
251
+ *
252
+ * Alert and FIN are one chained task so a single deadline covers both; queueing them separately
253
+ * would let a stalled alert consume one budget and the FIN another.
254
+ * @param {number} level
255
+ * @param {number} desc
256
+ */
257
+ _shutdown(level: number, desc: number): Promise<void>;
258
+ /**
259
+ * Abort: send a fatal alert naming why, then close. A peer left to time out on a dead
260
+ * connection is an interop bug of ours, not a neutral choice.
261
+ * @param {number} [desc] alert description byte, default internal_error
262
+ * @returns {Promise<void>}
263
+ */
264
+ abort(desc?: number): Promise<void>;
265
+ /**
266
+ * Application-data face of the connection as a {readable, writable} pair, for stacking the
267
+ * HTTP layer on top exactly like it would stack on a raw socket.
268
+ * @returns {import('./connect.js').ByteDuplex}
269
+ */
270
+ plaintextDuplex(): import("./connect.js").ByteDuplex;
271
+ _assertWritable(): void;
272
+ /**
273
+ * Serialize a wire-writing task behind every previously enqueued one.
274
+ * @template T
275
+ * @param {() => Promise<T>} fn
276
+ * @returns {Promise<T>}
277
+ */
278
+ _enqueueWrite<T>(fn: () => Promise<T>): Promise<T>;
279
+ /**
280
+ * @param {number} type
281
+ * @param {Uint8Array} bytes
282
+ */
283
+ _writeFragmented(type: number, bytes: Uint8Array): Promise<void>;
284
+ /**
285
+ * Encrypt-and-frame one fragment. Only ever runs inside the write chain.
286
+ * @param {number} type
287
+ * @param {Uint8Array} chunk
288
+ */
289
+ _emit(type: number, chunk: Uint8Array): Promise<void>;
290
+ /**
291
+ * @param {number} type
292
+ * @param {Uint8Array} body
293
+ */
294
+ _writeRecord(type: number, body: Uint8Array): Promise<void>;
295
+ /** Derive application_traffic_secret_N+1 and swap the send state. Runs inside the chain. */
296
+ _rotateSend(): Promise<void>;
297
+ /**
298
+ * Record the first fatal error, tell the peer (best-effort), throw. Never returns.
299
+ * @param {string} code
300
+ * @param {string} message
301
+ * @param {Record<string, unknown>} [detail]
302
+ * @param {number} [alertDesc]
303
+ * @returns {never}
304
+ */
305
+ _fail(code: string, message: string, detail?: Record<string, unknown>, alertDesc?: number): never;
306
+ /**
307
+ * Fire-and-forget: awaiting a write here could park the failure path behind transport
308
+ * backpressure, and the error must reach our caller no matter what the peer does. The
309
+ * write chain still orders it before the transport close.
310
+ * @param {number} desc
311
+ */
312
+ _sendAlertBestEffort(desc: number): void;
313
+ }
314
+ /**
315
+ * A complete handshake message off the wire. `raw` includes the 4-byte header, which is the
316
+ * form the transcript hash consumes.
317
+ */
318
+ export type HandshakeMessage = {
319
+ type: number;
320
+ body: Uint8Array;
321
+ raw: Uint8Array;
322
+ };
323
+ /**
324
+ * Keys for one direction: a TLS 1.3 traffic `secret` (key and IV derived per RFC 8446 s7.3,
325
+ * KeyUpdate rotation possible) or raw TLS 1.2 key_block slices (no forward rotation).
326
+ */
327
+ export type DirectionKeys = {
328
+ cipher: number;
329
+ secret: Uint8Array;
330
+ } | {
331
+ cipher: number;
332
+ key: Uint8Array;
333
+ iv: Uint8Array;
334
+ };
335
+ export type RecordLayerOptions = {
336
+ /**
337
+ * per-message cap; certificate chains dominate sizing
338
+ */
339
+ maxHandshakeMessage?: number | undefined;
340
+ /**
341
+ * received KeyUpdates before we call it a flood
342
+ */
343
+ maxKeyUpdates?: number | undefined;
344
+ /**
345
+ * how long a courtesy close_notify or fatal alert may
346
+ * block shutdown before being abandoned, default 2000; see _shutdown()
347
+ */
348
+ shutdownGraceMs?: number | undefined;
349
+ /**
350
+ * extra zero bytes per
351
+ * record, TLS 1.3 only
352
+ */
353
+ padding?: ((type: number, length: number) => number) | null | undefined;
354
+ /**
355
+ * NewSessionTicket consumer; default is to discard
356
+ */
357
+ onPostHandshake?: ((msg: HandshakeMessage) => void | Promise<void>) | null | undefined;
358
+ };
359
+ import { ByteReader } from '../util/bytes.js';
360
+ import { ByteWriter } from '../util/bytes.js';
361
+ import { TlsError } from '../errors.js';