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,430 @@
1
+ /**
2
+ * An ephemeral key share: the public half as sent in key_share, plus the private key the
3
+ * eventual ServerHello selection will feed into deriveSharedSecret.
4
+ * @typedef {object} KeyShare
5
+ * @property {number} group
6
+ * @property {Uint8Array} keyExchange raw public key, the exact bytes on the wire
7
+ * @property {CryptoKey} privateKey non-extractable
8
+ */
9
+ /**
10
+ * Generate an ephemeral key share for one group.
11
+ * `generateKeyPair` is injectable so a recorded handshake can be replayed with the exact private
12
+ * key that produced it.
13
+ *
14
+ * @param {number} group
15
+ * @param {import('./connect.js').TlsDeps} [deps]
16
+ * @returns {Promise<KeyShare>}
17
+ */
18
+ export function generateKeyShare(group: number, { generateKeyPair }?: import("./connect.js").TlsDeps): Promise<KeyShare>;
19
+ /**
20
+ * ECDH/X25519 shared secret. The peer's key is imported in raw form, which is where a malformed
21
+ * point is caught: WebCrypto rejects a point that is not on the curve, so we do not have to
22
+ * implement that check ourselves — but a wrong LENGTH would be accepted by some implementations,
23
+ * so it is checked here first.
24
+ *
25
+ * @param {number} group
26
+ * @param {CryptoKey} privateKey our ephemeral private key for the group
27
+ * @param {Uint8Array} peerKey the server's raw public key from its key_share
28
+ * @returns {Promise<Uint8Array>} throws on any degenerate or malformed peer key
29
+ */
30
+ export function deriveSharedSecret(group: number, privateKey: CryptoKey, peerKey: Uint8Array): Promise<Uint8Array>;
31
+ /**
32
+ * @typedef {object} ClientHelloOptions
33
+ * @property {string} hostname SNI, unless it is an IP literal (then no SNI is sent)
34
+ * @property {Array<{ group: number, keyExchange: Uint8Array }>} keyShares public halves to
35
+ * offer; empty for a 1.2-only hello, whose wire form must not carry the extension at all
36
+ * @property {Uint8Array} [random] fixed ClientHello.random, for reproducible handshakes
37
+ * @property {Uint8Array} [legacySessionId] fixed legacy_session_id, likewise
38
+ * @property {number[]} [ciphers] default: the union for the offered versions, 1.3 first
39
+ * @property {number[]} [groups] supported_groups, default SUPPORTED_GROUPS
40
+ * @property {number[]} [sigSchemes] default SUPPORTED_SIG_SCHEMES
41
+ * @property {string[]} [alpn] default ['http/1.1']; empty array omits the extension
42
+ * @property {number[]} [versions] default [TLS13, TLS12]
43
+ * @property {Uint8Array[]} [extraExtensions] pre-encoded, sent verbatim (the HRR cookie)
44
+ * @property {{ identity: Uint8Array, obfuscatedTicketAge: number, binderLen: number }} [psk]
45
+ * offer this resumption PSK. Encoded with a zeroed binder placeholder; the caller MUST derive
46
+ * the real binder over `message.subarray(0, truncatedLength)` and patch it in at
47
+ * `binderOffset` before the hello touches the wire — a zero binder on the wire is a hello
48
+ * every honest server must reject.
49
+ * @property {(n: number) => Uint8Array} [randomBytes] injectable randomness
50
+ */
51
+ /**
52
+ * The built hello plus everything later steps need to police the server's answer against what
53
+ * was actually offered — negotiation checks must run against this record, never against the
54
+ * defaults they might have come from.
55
+ * @typedef {object} ClientHello
56
+ * @property {Uint8Array} message framed handshake message, ready for the record layer
57
+ * @property {Uint8Array} clientRandom
58
+ * @property {Uint8Array} legacySessionId
59
+ * @property {number[]} offeredCiphers
60
+ * @property {number[]} offeredGroups
61
+ * @property {number[]} offeredSigSchemes
62
+ * @property {Set<number>} offeredExtensions extension types present in the hello
63
+ * @property {string[]} offeredAlpn
64
+ * @property {number} [binderOffset] psk only: where the binder's bytes sit in `message`
65
+ * @property {number} [truncatedLength] psk only: how many leading bytes of `message` the binder
66
+ * transcript covers (RFC 8446 s4.2.11.2 truncation — everything except the binders list)
67
+ */
68
+ /**
69
+ * Build a ClientHello. Returns the framed handshake message plus the metadata the rest of the
70
+ * handshake needs to police the server's answer.
71
+ *
72
+ * @param {ClientHelloOptions} opts
73
+ * @returns {ClientHello}
74
+ */
75
+ export function buildClientHello({ hostname, keyShares, random, legacySessionId, ciphers, groups, sigSchemes, alpn, versions, extraExtensions, psk, randomBytes, }: ClientHelloOptions): ClientHello;
76
+ /**
77
+ * Patch the real binder over the placeholder `buildClientHello` emitted. Separate from the
78
+ * builder because the binder is derived FROM the built message (truncated), so there is no
79
+ * ordering in which one function could do both.
80
+ * @param {ClientHello} hello a hello built with a psk offer
81
+ * @param {Uint8Array} binder
82
+ */
83
+ export function setPskBinder(hello: ClientHello, binder: Uint8Array): void;
84
+ /**
85
+ * A parsed ServerHello. `isHelloRetryRequest` is decided by the random alone (RFC 8446 s4.1.3);
86
+ * everything else is exactly what the wire carried, judged later by the negotiate* functions.
87
+ * @typedef {object} ServerHello
88
+ * @property {number} legacyVersion
89
+ * @property {Uint8Array} random
90
+ * @property {Uint8Array} legacySessionIdEcho
91
+ * @property {number} cipherSuite
92
+ * @property {Map<number, Uint8Array>} extensions
93
+ * @property {boolean} isHelloRetryRequest
94
+ */
95
+ /**
96
+ * @param {Uint8Array} body
97
+ * @returns {ServerHello} throws on malformed encoding or a compression method other than null
98
+ */
99
+ export function parseServerHello(body: Uint8Array): ServerHello;
100
+ /**
101
+ * Decide the negotiated version, and refuse every shape of downgrade.
102
+ *
103
+ * The subtle one is the sentinel check (RFC 8446 s4.1.3): a server that supports TLS 1.3 but was
104
+ * pushed down to 1.2 by an attacker stripping our supported_versions plants a known value in the
105
+ * last 8 bytes of its random. A 1.3-capable client that ignores it is exactly the client the
106
+ * attack targets.
107
+ *
108
+ * @param {ServerHello} serverHello
109
+ * @param {{ offeredVersions: number[] }} offer
110
+ * @returns {number} the negotiated version; every downgrade shape throws instead
111
+ */
112
+ export function negotiateVersion(serverHello: ServerHello, { offeredVersions }: {
113
+ offeredVersions: number[];
114
+ }): number;
115
+ /**
116
+ * @param {ServerHello} serverHello
117
+ * @param {{ offeredCiphers: number[], version: number }} offer the negotiated version re-checks
118
+ * the suite's family, so a union offer cannot run a 1.3 suite under 1.2 or the reverse
119
+ * @returns {{ suite: number, params: import('./constants.js').CipherParams }}
120
+ */
121
+ export function negotiateCipher(serverHello: ServerHello, { offeredCiphers, version }: {
122
+ offeredCiphers: number[];
123
+ version: number;
124
+ }): {
125
+ suite: number;
126
+ params: import("./constants.js").CipherParams;
127
+ };
128
+ /**
129
+ * RFC 8446 s4.1.3: the server must echo legacy_session_id verbatim. A mismatch means the
130
+ * ServerHello does not belong to our ClientHello.
131
+ * @param {ServerHello} serverHello
132
+ * @param {Uint8Array} legacySessionId
133
+ * @returns {void} throws TlsError on mismatch
134
+ */
135
+ export function checkSessionIdEcho(serverHello: ServerHello, legacySessionId: Uint8Array): void;
136
+ /**
137
+ * The server's chosen key share, validated against what we actually offered.
138
+ * @param {ServerHello} serverHello
139
+ * @param {KeyShare[]} keyShares the shares we generated for the hello
140
+ * @returns {{ group: number, keyExchange: Uint8Array, privateKey: CryptoKey }} the server's
141
+ * group and public key, paired with OUR private key for it
142
+ */
143
+ export function selectServerKeyShare(serverHello: ServerHello, keyShares: KeyShare[]): {
144
+ group: number;
145
+ keyExchange: Uint8Array;
146
+ privateKey: CryptoKey;
147
+ };
148
+ /**
149
+ * HelloRetryRequest: the server names one group and expects a fresh ClientHello.
150
+ * @param {ServerHello} serverHello
151
+ * @param {{ offeredGroups: number[] }} offer
152
+ * @returns {{ group: number, cookie: Uint8Array | null }}
153
+ */
154
+ export function parseHelloRetryRequest(serverHello: ServerHello, { offeredGroups }: {
155
+ offeredGroups: number[];
156
+ }): {
157
+ group: number;
158
+ cookie: Uint8Array | null;
159
+ };
160
+ /**
161
+ * NewSessionTicket (RFC 8446 s4.6.1), the post-handshake message a resumption PSK is minted
162
+ * from. Strict on structure — a truncated field, trailing bytes, or a zero-length ticket ends
163
+ * the connection, because a peer whose post-handshake messages do not parse cannot be trusted
164
+ * to frame the application data either — but faithful to s4.6.1 on extensions: early_data is
165
+ * validated in shape and its value RECORDED but never acted on (0-RTT is deliberately not
166
+ * implemented; see the driver), and unrecognized extensions are ignored, which s4.6.1 makes
167
+ * mandatory ("Clients MUST ignore unrecognized extensions").
168
+ *
169
+ * Lifetime semantics (zero means discard, 604800 s is the cap a client may honour) are POLICY,
170
+ * applied by the ticket store; this function reports what the wire said.
171
+ *
172
+ * @param {Uint8Array} body
173
+ * @returns {{ lifetimeSec: number, ageAdd: number, nonce: Uint8Array, ticket: Uint8Array,
174
+ * maxEarlyDataSize: number | null }}
175
+ */
176
+ export function parseNewSessionTicket(body: Uint8Array): {
177
+ lifetimeSec: number;
178
+ ageAdd: number;
179
+ nonce: Uint8Array;
180
+ ticket: Uint8Array;
181
+ maxEarlyDataSize: number | null;
182
+ };
183
+ /**
184
+ * The CertificateStatus body of RFC 6066 s8: `status_type(1) || opaque OCSPResponse<1..2^24-1>`.
185
+ * Two carriers share this exact shape — the TLS 1.2 CertificateStatus handshake message, and the
186
+ * extension_data of a TLS 1.3 status_request CertificateEntry extension (RFC 8446 s4.4.2.1) —
187
+ * which is why it is one parser and not two.
188
+ *
189
+ * @param {Uint8Array} body
190
+ * @param {string} where named in errors
191
+ * @returns {Uint8Array} the DER OCSPResponse, exactly as sent; its meaning is the trust layer's
192
+ * problem, not this layer's
193
+ */
194
+ export function parseCertificateStatus(body: Uint8Array, where: string): Uint8Array;
195
+ /**
196
+ * TLS 1.3 Certificate (RFC 8446 s4.4.2): context, then entries carrying per-cert extensions.
197
+ *
198
+ * Entry extensions are policed, not skipped: RFC 8446 s4.4.2 allows a server to send only
199
+ * extensions the ClientHello offered, and s4.2 confines each type to specific messages — for
200
+ * CertificateEntry that is status_request and signed_certificate_timestamp. An extension we
201
+ * cannot attribute to our own offer is either a server confusion or a smuggling attempt, and
202
+ * both end the handshake.
203
+ *
204
+ * Only the LEAF's stapled OCSP response is returned. A server may staple for intermediates too;
205
+ * those staples are validated structurally (they must still be well-formed CertificateStatus)
206
+ * but not consumed — this package checks revocation of the identity it is authenticating, and
207
+ * inventing partial intermediate coverage would imply a guarantee it does not give.
208
+ *
209
+ * @param {Uint8Array} body
210
+ * @param {{ offeredExtensions?: Set<number> }} [opts] extension types our ClientHello offered.
211
+ * Omitting it means "nothing was offered", the fail-closed reading.
212
+ * @returns {{ chain: Uint8Array[], ocspResponse: Uint8Array | null }} DER certificates in wire
213
+ * order (leaf first), plus the leaf's stapled DER OCSPResponse if the server sent one
214
+ */
215
+ export function parseCertificate13(body: Uint8Array, { offeredExtensions }?: {
216
+ offeredExtensions?: Set<number>;
217
+ }): {
218
+ chain: Uint8Array[];
219
+ ocspResponse: Uint8Array | null;
220
+ };
221
+ /**
222
+ * TLS 1.2 Certificate (RFC 5246 s7.4.2): a bare list, no context and no per-cert extensions.
223
+ * @param {Uint8Array} body
224
+ * @returns {Uint8Array[]} DER certificates in wire order, leaf first
225
+ */
226
+ export function parseCertificate12(body: Uint8Array): Uint8Array[];
227
+ /**
228
+ * @param {Uint8Array} body
229
+ * @returns {{ algorithm: number, signature: Uint8Array }}
230
+ */
231
+ export function parseCertificateVerify(body: Uint8Array): {
232
+ algorithm: number;
233
+ signature: Uint8Array;
234
+ };
235
+ /**
236
+ * RFC 8446 s4.4.3: 64 spaces, a context string, a zero byte, then the transcript hash.
237
+ * @param {Uint8Array} transcriptHash
238
+ * @param {boolean} [isServer]
239
+ * @returns {Uint8Array}
240
+ */
241
+ export function certificateVerifyContent(transcriptHash: Uint8Array, isServer?: boolean): Uint8Array;
242
+ /**
243
+ * Verify a handshake signature with WebCrypto.
244
+ * `spki` is the DER SubjectPublicKeyInfo lifted straight out of the leaf certificate, so the key
245
+ * used to check the signature is provably the key the trust layer validated.
246
+ *
247
+ * @param {object} args
248
+ * @param {number} args.scheme signature scheme id from the wire
249
+ * @param {Uint8Array} args.spki DER SubjectPublicKeyInfo of the validated leaf
250
+ * @param {Uint8Array} args.signature as received: DER for ECDSA, raw otherwise
251
+ * @param {Uint8Array} args.content the exact bytes the signature must cover
252
+ * @returns {Promise<true>} every failure throws; there is no false
253
+ */
254
+ export function verifyHandshakeSignature({ scheme, spki, signature, content }: {
255
+ scheme: number;
256
+ spki: Uint8Array;
257
+ signature: Uint8Array;
258
+ content: Uint8Array;
259
+ }): Promise<true>;
260
+ /**
261
+ * RFC 8422 s5.4. Only named-curve ECDHE is accepted: explicit curves are a decade-dead feature
262
+ * and finite-field DHE would need bignum arithmetic WebCrypto does not expose.
263
+ *
264
+ * @param {Uint8Array} body
265
+ * @returns {{ group: number, publicKey: Uint8Array, signatureAlgorithm: number,
266
+ * signature: Uint8Array, signedParams: Uint8Array }} `signedParams` is the exact byte range
267
+ * the server's signature covers (curve_type through the public key)
268
+ */
269
+ export function parseServerKeyExchangeEcdhe(body: Uint8Array): {
270
+ group: number;
271
+ publicKey: Uint8Array;
272
+ signatureAlgorithm: number;
273
+ signature: Uint8Array;
274
+ signedParams: Uint8Array;
275
+ };
276
+ /**
277
+ * TLS 1.2 signs client_random || server_random || ServerECDHParams.
278
+ * @param {Uint8Array} clientRandom
279
+ * @param {Uint8Array} serverRandom
280
+ * @param {Uint8Array} signedParams
281
+ * @returns {Uint8Array}
282
+ */
283
+ export function serverKeyExchangeContent(clientRandom: Uint8Array, serverRandom: Uint8Array, signedParams: Uint8Array): Uint8Array;
284
+ /**
285
+ * @param {Uint8Array} publicKey
286
+ * @returns {Uint8Array} framed ClientKeyExchange message
287
+ */
288
+ export function buildClientKeyExchange(publicKey: Uint8Array): Uint8Array;
289
+ /**
290
+ * @param {Uint8Array} verifyData
291
+ * @returns {Uint8Array} framed Finished message
292
+ */
293
+ export function buildFinished(verifyData: Uint8Array): Uint8Array;
294
+ /**
295
+ * Compare a peer Finished against ours. Constant-time in intent: verify_data is derived from
296
+ * secrets the peer must already know, so a timing leak is not a decryption oracle, but there is
297
+ * no reason to leak the prefix length either.
298
+ * @param {Uint8Array} received
299
+ * @param {Uint8Array} expected
300
+ * @returns {true} a mismatch throws; there is no false
301
+ */
302
+ export function checkFinished(received: Uint8Array, expected: Uint8Array): true;
303
+ /**
304
+ * The negotiated ALPN protocol, refusing anything we did not offer.
305
+ * @param {Map<number, Uint8Array>} extensions
306
+ * @param {string[]} offeredAlpn
307
+ * @param {string} where
308
+ * @returns {string | null} null when the server declined ALPN entirely
309
+ */
310
+ export function checkAlpn(extensions: Map<number, Uint8Array>, offeredAlpn: string[], where: string): string | null;
311
+ export { GROUP_PARAMS };
312
+ /**
313
+ * An ephemeral key share: the public half as sent in key_share, plus the private key the
314
+ * eventual ServerHello selection will feed into deriveSharedSecret.
315
+ */
316
+ export type KeyShare = {
317
+ group: number;
318
+ /**
319
+ * raw public key, the exact bytes on the wire
320
+ */
321
+ keyExchange: Uint8Array;
322
+ /**
323
+ * non-extractable
324
+ */
325
+ privateKey: CryptoKey;
326
+ };
327
+ export type ClientHelloOptions = {
328
+ /**
329
+ * SNI, unless it is an IP literal (then no SNI is sent)
330
+ */
331
+ hostname: string;
332
+ /**
333
+ * public halves to
334
+ * offer; empty for a 1.2-only hello, whose wire form must not carry the extension at all
335
+ */
336
+ keyShares: Array<{
337
+ group: number;
338
+ keyExchange: Uint8Array;
339
+ }>;
340
+ /**
341
+ * fixed ClientHello.random, for reproducible handshakes
342
+ */
343
+ random?: Uint8Array<ArrayBufferLike> | undefined;
344
+ /**
345
+ * fixed legacy_session_id, likewise
346
+ */
347
+ legacySessionId?: Uint8Array<ArrayBufferLike> | undefined;
348
+ /**
349
+ * default: the union for the offered versions, 1.3 first
350
+ */
351
+ ciphers?: number[] | undefined;
352
+ /**
353
+ * supported_groups, default SUPPORTED_GROUPS
354
+ */
355
+ groups?: number[] | undefined;
356
+ /**
357
+ * default SUPPORTED_SIG_SCHEMES
358
+ */
359
+ sigSchemes?: number[] | undefined;
360
+ /**
361
+ * default ['http/1.1']; empty array omits the extension
362
+ */
363
+ alpn?: string[] | undefined;
364
+ /**
365
+ * default [TLS13, TLS12]
366
+ */
367
+ versions?: number[] | undefined;
368
+ /**
369
+ * pre-encoded, sent verbatim (the HRR cookie)
370
+ */
371
+ extraExtensions?: Uint8Array<ArrayBufferLike>[] | undefined;
372
+ /**
373
+ * offer this resumption PSK. Encoded with a zeroed binder placeholder; the caller MUST derive
374
+ * the real binder over `message.subarray(0, truncatedLength)` and patch it in at
375
+ * `binderOffset` before the hello touches the wire — a zero binder on the wire is a hello
376
+ * every honest server must reject.
377
+ */
378
+ psk?: {
379
+ identity: Uint8Array;
380
+ obfuscatedTicketAge: number;
381
+ binderLen: number;
382
+ } | undefined;
383
+ /**
384
+ * injectable randomness
385
+ */
386
+ randomBytes?: ((n: number) => Uint8Array) | undefined;
387
+ };
388
+ /**
389
+ * The built hello plus everything later steps need to police the server's answer against what
390
+ * was actually offered — negotiation checks must run against this record, never against the
391
+ * defaults they might have come from.
392
+ */
393
+ export type ClientHello = {
394
+ /**
395
+ * framed handshake message, ready for the record layer
396
+ */
397
+ message: Uint8Array;
398
+ clientRandom: Uint8Array;
399
+ legacySessionId: Uint8Array;
400
+ offeredCiphers: number[];
401
+ offeredGroups: number[];
402
+ offeredSigSchemes: number[];
403
+ /**
404
+ * extension types present in the hello
405
+ */
406
+ offeredExtensions: Set<number>;
407
+ offeredAlpn: string[];
408
+ /**
409
+ * psk only: where the binder's bytes sit in `message`
410
+ */
411
+ binderOffset?: number | undefined;
412
+ /**
413
+ * psk only: how many leading bytes of `message` the binder
414
+ * transcript covers (RFC 8446 s4.2.11.2 truncation — everything except the binders list)
415
+ */
416
+ truncatedLength?: number | undefined;
417
+ };
418
+ /**
419
+ * A parsed ServerHello. `isHelloRetryRequest` is decided by the random alone (RFC 8446 s4.1.3);
420
+ * everything else is exactly what the wire carried, judged later by the negotiate* functions.
421
+ */
422
+ export type ServerHello = {
423
+ legacyVersion: number;
424
+ random: Uint8Array;
425
+ legacySessionIdEcho: Uint8Array;
426
+ cipherSuite: number;
427
+ extensions: Map<number, Uint8Array>;
428
+ isHelloRetryRequest: boolean;
429
+ };
430
+ import { GROUP_PARAMS } from './constants.js';
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Run a TLS 1.3 handshake over a byte duplex and return the plaintext duplex above it.
3
+ *
4
+ * @param {object} args
5
+ * @param {import('./connect.js').ByteDuplex} args.transport
6
+ * @param {string} args.hostname the identity the certificate must prove, and the SNI sent
7
+ * @param {VerifyPeer} args.verifyPeer
8
+ * Must throw to reject. Resolves with the validated leaf; its SPKI is the only key this
9
+ * handshake will accept a CertificateVerify signature from.
10
+ * @param {import('./connect.js').TlsOptions} [args.options] `versions` is ignored: this entry
11
+ * pins the offer to [TLS 1.3]
12
+ * @param {import('./connect.js').TlsDeps} [args.deps]
13
+ * @returns {Promise<import('./connect.js').TlsSession>}
14
+ */
15
+ export function handshakeTls13({ transport, hostname, verifyPeer, options, deps }: {
16
+ transport: import("./connect.js").ByteDuplex;
17
+ hostname: string;
18
+ verifyPeer: VerifyPeer;
19
+ options?: import("./connect.js").TlsOptions | undefined;
20
+ deps?: import("./connect.js").TlsDeps | undefined;
21
+ }): Promise<import("./connect.js").TlsSession>;
22
+ /**
23
+ * Continue a TLS 1.3 handshake from the first ServerHello (which may be a HelloRetryRequest).
24
+ * Called by connect.js once negotiation routed the connection here.
25
+ * @param {HandshakeContext} ctx
26
+ * @returns {Promise<import('./connect.js').TlsSession>}
27
+ */
28
+ export function continueTls13(ctx: HandshakeContext): Promise<import("./connect.js").TlsSession>;
29
+ /**
30
+ * Only one key share is offered by default. A second costs a key generation and 30-odd bytes for
31
+ * a group the server is unlikely to prefer; a HelloRetryRequest recovers the rare case at the
32
+ * cost of one round trip.
33
+ */
34
+ export const DEFAULT_OFFER_GROUPS: number[];
35
+ /**
36
+ * The injected trust decision. Must throw to reject; resolves with the validated leaf, whose
37
+ * SPKI is the only key a driver will accept a handshake signature from. `details.ocspResponse`
38
+ * is the peer's stapled DER OCSPResponse when it sent one — delivered here, at the same moment
39
+ * as the chain, because revocation is part of deciding whether to believe the certificate and
40
+ * must be settled before anything of ours goes on the wire.
41
+ */
42
+ export type VerifyPeer = (chain: Uint8Array[], hostname: string, details?: {
43
+ ocspResponse: Uint8Array | null;
44
+ }) => Promise<{
45
+ spki: {
46
+ spkiDer: Uint8Array;
47
+ };
48
+ }>;
49
+ /**
50
+ * Driver context assembled by connect.js after the ServerHello routed the connection: the
51
+ * record layer, the transcript (created under the negotiated suite's hash, ClientHello already
52
+ * folded in), the ClientHello metadata, the parsed ServerHello with its raw bytes, and the
53
+ * offer that produced them. Both continue* drivers consume exactly this shape.
54
+ */
55
+ export type HandshakeContext = {
56
+ record: import("./record.js").RecordLayer;
57
+ transcript: import("./transcript.js").Transcript;
58
+ hello: import("./handshake-messages.js").ClientHello;
59
+ serverHello: import("./handshake-messages.js").ServerHello;
60
+ rawServerHello: Uint8Array;
61
+ suite: number;
62
+ params: import("./constants.js").CipherParams;
63
+ hostname: string;
64
+ verifyPeer: VerifyPeer;
65
+ options: import("./connect.js").TlsOptions;
66
+ deps: import("./connect.js").TlsDeps;
67
+ offer: {
68
+ versions: number[];
69
+ ciphers: number[];
70
+ groups: number[];
71
+ offerGroups: number[];
72
+ alpn: string[];
73
+ keyShares: import("./handshake-messages.js").KeyShare[];
74
+ psk: OfferedPsk | null;
75
+ };
76
+ };
77
+ /**
78
+ * The resumption PSK as prepared by connect.js: the caller's offer plus the secrets derived
79
+ * from it once (Early Secret, binder key) so neither hello build nor acceptance re-derives.
80
+ */
81
+ export type OfferedPsk = {
82
+ identity: Uint8Array;
83
+ psk: Uint8Array;
84
+ hash: import("./keyschedule.js").ScheduleHash;
85
+ obfuscatedTicketAge: () => number;
86
+ peer: object | null;
87
+ earlySecret: Uint8Array;
88
+ binderKey: Uint8Array;
89
+ binderLen: number;
90
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The one refusal that must fire regardless of which message was expected. Exported for
3
+ * connect.js, whose ServerHello wait is the earliest point a 1.2 server can spring one.
4
+ * @returns {never}
5
+ */
6
+ export function refuseHelloRequest(): never;
7
+ /**
8
+ * Run a TLS 1.2 handshake over a byte duplex and return the plaintext duplex above it.
9
+ *
10
+ * @param {object} args
11
+ * @param {import('./connect.js').ByteDuplex} args.transport
12
+ * @param {string} args.hostname the identity the certificate must prove, and the SNI sent
13
+ * @param {import('./handshake.js').VerifyPeer} args.verifyPeer
14
+ * Must throw to reject. Resolves with the validated leaf; its SPKI is the only key this
15
+ * handshake will accept a ServerKeyExchange signature from.
16
+ * @param {import('./connect.js').TlsOptions} [args.options] `versions` is ignored: this entry
17
+ * pins the offer to [TLS 1.2]
18
+ * @param {import('./connect.js').TlsDeps} [args.deps]
19
+ * @returns {Promise<import('./connect.js').TlsSession>}
20
+ */
21
+ export function handshakeTls12({ transport, hostname, verifyPeer, options, deps }: {
22
+ transport: import("./connect.js").ByteDuplex;
23
+ hostname: string;
24
+ verifyPeer: import("./handshake.js").VerifyPeer;
25
+ options?: import("./connect.js").TlsOptions | undefined;
26
+ deps?: import("./connect.js").TlsDeps | undefined;
27
+ }): Promise<import("./connect.js").TlsSession>;
28
+ /**
29
+ * Continue a TLS 1.2 handshake from the ServerHello. Called by connect.js once negotiation
30
+ * routed the connection here; the record layer arrives already pinned to 1.2 semantics and the
31
+ * transcript already runs under the negotiated suite's PRF hash.
32
+ * @param {import('./handshake.js').HandshakeContext} ctx
33
+ * @returns {Promise<import('./connect.js').TlsSession>}
34
+ */
35
+ export function continueTls12(ctx: import("./handshake.js").HandshakeContext): Promise<import("./connect.js").TlsSession>;
@@ -0,0 +1,9 @@
1
+ export { connectTls } from "./connect.js";
2
+ export { handshakeTls13 } from "./handshake.js";
3
+ export { handshakeTls12 } from "./handshake12.js";
4
+ export { RecordLayer } from "./record.js";
5
+ export { Transcript } from "./transcript.js";
6
+ export { TicketStore } from "./tickets.js";
7
+ export { createAead, buildNonce } from "./aead.js";
8
+ export { ALPN_HTTP11, CIPHER, CIPHER_NAME, CIPHER_PARAMS, GROUP, GROUP_NAME, SIG_SCHEME, SIG_SCHEME_NAME, SUPPORTED_GROUPS, SUPPORTED_SIG_SCHEMES, TLS12, TLS12_CIPHERS, TLS13, TLS13_CIPHERS, VERSION_NAME } from "./constants.js";
9
+ export { buildClientHello, deriveSharedSecret, generateKeyShare, negotiateCipher, negotiateVersion, parseServerHello, verifyHandshakeSignature } from "./handshake-messages.js";