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,66 @@
1
+ export class TicketStore {
2
+ /**
3
+ * @param {object} [opts]
4
+ * @param {number} [opts.maxPerKey] tickets retained per key, default 2 — the number a typical
5
+ * server flight issues; older tickets are evicted first
6
+ * @param {() => number} [opts.now] epoch-ms source, injectable for tests
7
+ */
8
+ constructor({ maxPerKey, now }?: {
9
+ maxPerKey?: number | undefined;
10
+ now?: (() => number) | undefined;
11
+ });
12
+ /** @type {Map<string, Array<StoredTicket>>} */
13
+ _byKey: Map<string, Array<{
14
+ identity: Uint8Array;
15
+ psk: Uint8Array;
16
+ hash: import("./keyschedule.js").ScheduleHash;
17
+ ageAdd: number;
18
+ /**
19
+ * already clamped to the seven-day ceiling
20
+ */
21
+ lifetimeMs: number;
22
+ receivedAtMs: number;
23
+ peer: object;
24
+ cipherSuite: number;
25
+ }>>;
26
+ _maxPerKey: number;
27
+ _now: () => number;
28
+ /**
29
+ * @typedef {object} StoredTicket
30
+ * @property {Uint8Array} identity
31
+ * @property {Uint8Array} psk
32
+ * @property {import('./keyschedule.js').ScheduleHash} hash
33
+ * @property {number} ageAdd
34
+ * @property {number} lifetimeMs already clamped to the seven-day ceiling
35
+ * @property {number} receivedAtMs
36
+ * @property {object} peer
37
+ * @property {number} cipherSuite
38
+ */
39
+ /** Total tickets currently held, for tests and diagnostics. */
40
+ get size(): number;
41
+ /**
42
+ * Store a captured ticket under `key`. Returns whether it was retained: a zero (or negative)
43
+ * lifetime is a server instruction to discard immediately and is honoured silently — that is
44
+ * the server's prerogative, not an error — while a structurally unusable capture (no PSK, no
45
+ * identity, unknown hash) throws, because the only producer is this package's own driver and
46
+ * a malformed capture is a bug, not an input.
47
+ *
48
+ * @param {string} key the pool key of the connection the ticket arrived on
49
+ * @param {import('./connect.js').CapturedTicket} captured
50
+ * @returns {boolean}
51
+ */
52
+ put(key: string, captured: import("./connect.js").CapturedTicket): boolean;
53
+ /**
54
+ * Take the freshest usable ticket for `key` as a ready-to-offer PSK, or null. The ticket is
55
+ * removed either way it goes from here — single use — and expired tickets encountered on the
56
+ * way are dropped rather than offered: a server checks obfuscated_ticket_age against the
57
+ * lifetime it granted, and offering a stale ticket is a round trip spent being refused.
58
+ *
59
+ * @param {string} key MUST be built from the same inputs as the connection's pool key; this
60
+ * equality is the entire trust story of resumption (see the module comment)
61
+ * @returns {import('./connect.js').ResumptionOffer | null}
62
+ */
63
+ take(key: string): import("./connect.js").ResumptionOffer | null;
64
+ /** Drop everything; a closed Client must not keep credentials alive. */
65
+ clear(): void;
66
+ }
@@ -0,0 +1,52 @@
1
+ export class Transcript {
2
+ /**
3
+ * @param {import('./keyschedule.js').ScheduleHash} hash fixed once the cipher suite is known
4
+ * @param {{ maxBytes?: number }} [opts] transcript buffer cap, default 1 MiB
5
+ */
6
+ constructor(hash: import("./keyschedule.js").ScheduleHash, { maxBytes }?: {
7
+ maxBytes?: number;
8
+ });
9
+ _hash: import("./keyschedule.js").ScheduleHash;
10
+ /** @type {Uint8Array[]} */
11
+ _chunks: Uint8Array[];
12
+ _len: number;
13
+ _maxBytes: number;
14
+ /** @type {Uint8Array | null} digest cache, invalidated by update() */
15
+ _cached: Uint8Array | null;
16
+ get bytesBuffered(): number;
17
+ /**
18
+ * Append raw handshake message bytes (including the 4-byte message header — the transcript
19
+ * is over complete Handshake structs, never record framing).
20
+ * @param {Uint8Array} bytes
21
+ */
22
+ update(bytes: Uint8Array): void;
23
+ /**
24
+ * Digest of everything appended so far. Does not consume; call as often as needed.
25
+ * @returns {Promise<Uint8Array>}
26
+ */
27
+ hash(): Promise<Uint8Array>;
28
+ /**
29
+ * Digest of everything appended so far PLUS `extra`, without appending it.
30
+ *
31
+ * Exists for exactly one caller: the PSK binder after a HelloRetryRequest, which is an HMAC
32
+ * over Transcript-Hash(message_hash(CH1) || HRR || Truncate(CH2)) (RFC 8446 s4.2.11.2). The
33
+ * truncated ClientHello2 must be hashed as a continuation of the real transcript but must
34
+ * never BECOME part of it — the transcript proper gets the full ClientHello2 with its binder,
35
+ * and folding the truncated form in even transiently would leave a window where the two
36
+ * bookkeepings disagree.
37
+ * @param {Uint8Array} extra
38
+ * @returns {Promise<Uint8Array>}
39
+ */
40
+ hashWith(extra: Uint8Array): Promise<Uint8Array>;
41
+ /**
42
+ * HelloRetryRequest transcript substitution (RFC 8446 s4.4.1): when a ServerHello is an HRR,
43
+ * the transcript restarts as a synthetic handshake message
44
+ *
45
+ * message_hash(0xFE) || uint24 Hash.length || Hash(ClientHello1)
46
+ *
47
+ * so that a stateless server need only remember the hash of the first ClientHello. Call this
48
+ * after ClientHello1 is the only message in the transcript, before appending the HRR itself.
49
+ * @returns {Promise<void>}
50
+ */
51
+ replaceWithMessageHash(): Promise<void>;
52
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Convenience: build a length-prefixed vector standalone.
3
+ * @param {1 | 2 | 3} lenBytes
4
+ * @param {Uint8Array} body
5
+ * @returns {Uint8Array}
6
+ */
7
+ export function vector(lenBytes: 1 | 2 | 3, body: Uint8Array): Uint8Array;
8
+ /**
9
+ * Encode a handshake message: 1-byte type, 3-byte length, body.
10
+ * @param {number} type
11
+ * @param {Uint8Array} body
12
+ * @returns {Uint8Array}
13
+ */
14
+ export function handshakeMessage(type: number, body: Uint8Array): Uint8Array;
15
+ /** Sequential reader over a byte range with hard bounds. */
16
+ export class Cursor {
17
+ /**
18
+ * @param {Uint8Array} bytes
19
+ * @param {string} what named in every error so a failure says which structure was malformed
20
+ */
21
+ constructor(bytes: Uint8Array, what?: string);
22
+ bytes: Uint8Array<ArrayBufferLike>;
23
+ pos: number;
24
+ what: string;
25
+ get remaining(): number;
26
+ get done(): boolean;
27
+ /**
28
+ * @param {number} n
29
+ * @param {string} field
30
+ */
31
+ _need(n: number, field: string): void;
32
+ /**
33
+ * @param {string} [field]
34
+ * @returns {number}
35
+ */
36
+ u8(field?: string): number;
37
+ /**
38
+ * @param {string} [field]
39
+ * @returns {number}
40
+ */
41
+ u16(field?: string): number;
42
+ /**
43
+ * @param {string} [field]
44
+ * @returns {number}
45
+ */
46
+ u24(field?: string): number;
47
+ /**
48
+ * @param {string} [field]
49
+ * @returns {number}
50
+ */
51
+ u32(field?: string): number;
52
+ /**
53
+ * Fixed-length opaque bytes. Returns a view into the original buffer, never a copy.
54
+ * @param {number} n
55
+ * @param {string} [field]
56
+ * @returns {Uint8Array}
57
+ */
58
+ take(n: number, field?: string): Uint8Array;
59
+ /**
60
+ * A vector whose length is carried in `lenBytes` (1, 2 or 3) leading octets.
61
+ * @param {1 | 2 | 3} lenBytes
62
+ * @param {string} [field]
63
+ * @returns {Uint8Array}
64
+ */
65
+ vector(lenBytes: 1 | 2 | 3, field?: string): Uint8Array;
66
+ /**
67
+ * Like `vector`, but hands back a Cursor so nested structures inherit the bound.
68
+ * @param {1 | 2 | 3} lenBytes
69
+ * @param {string} [field]
70
+ * @returns {Cursor}
71
+ */
72
+ sub(lenBytes: 1 | 2 | 3, field?: string): Cursor;
73
+ /**
74
+ * Assert nothing is left. Trailing data inside a length-delimited structure means our idea of
75
+ * the structure and the peer's disagree, which is exactly when to stop rather than guess.
76
+ * @param {string} [field]
77
+ */
78
+ end(field?: string): void;
79
+ }
80
+ /** Accumulating writer. Kept dumb: correctness of lengths comes from `vector()` below. */
81
+ export class Builder {
82
+ /** @type {Uint8Array[]} */
83
+ parts: Uint8Array[];
84
+ length: number;
85
+ /**
86
+ * @param {Uint8Array} bytes
87
+ * @returns {this}
88
+ */
89
+ push(bytes: Uint8Array): this;
90
+ /** @param {number} n @returns {this} */
91
+ u8(n: number): this;
92
+ /** @param {number} n @returns {this} */
93
+ u16(n: number): this;
94
+ /** @param {number} n @returns {this} */
95
+ u24(n: number): this;
96
+ /**
97
+ * Write `body` prefixed by its length in `lenBytes` octets. Taking the body as bytes rather
98
+ * than back-patching a placeholder means a length can never drift from what follows it.
99
+ * @param {1 | 2 | 3} lenBytes
100
+ * @param {Uint8Array} body
101
+ * @returns {this}
102
+ */
103
+ vector(lenBytes: 1 | 2 | 3, body: Uint8Array): this;
104
+ /** @returns {Uint8Array} */
105
+ build(): Uint8Array;
106
+ }
@@ -0,0 +1,222 @@
1
+ /**
2
+ * A request URL reduced to what the transport dials.
3
+ * @typedef {object} TransportTarget
4
+ * @property {URL} url
5
+ * @property {string} hostname IPv6 unbracketed, as the socket API and SOCKS5 want it
6
+ * @property {number} port explicit port, or the scheme default
7
+ * @property {boolean} secure whether the scheme is https:
8
+ */
9
+ /**
10
+ * Split a request URL into what the transport needs. Throws ConfigError on any scheme other
11
+ * than http: and https:.
12
+ * @param {string | URL} input
13
+ * @returns {TransportTarget}
14
+ */
15
+ export function targetFromUrl(input: string | URL): TransportTarget;
16
+ /**
17
+ * What a Response's `tunnelfetch` detail reports about the connection, before the HTTP layer
18
+ * adds the per-response httpVersion and framing.
19
+ * @typedef {object} ConnectionInfo
20
+ * @property {string} url
21
+ * @property {boolean} proxied
22
+ * @property {string | null} proxy the proxy actually used, credentials omitted
23
+ * @property {import('./tls/connect.js').TlsSessionInfo | null} tls null for cleartext
24
+ */
25
+ /**
26
+ * A live connection carrying application bytes: the duplex, its provenance, and the deadline
27
+ * controller that governs it. This is what the pool stores and what sendAndReceive consumes.
28
+ * @typedef {object} Connection
29
+ * @property {ReadableStream<Uint8Array>} readable
30
+ * @property {WritableStream<Uint8Array>} writable
31
+ * @property {() => Promise<void> | void} close
32
+ * @property {ConnectionInfo} info
33
+ * @property {import('./trust/index.js').ParsedCertificate | null} [peerCertificate] TLS only;
34
+ * null when trust mode 'none' accepted a leaf it could not parse
35
+ * @property {DeadlineController} deadlines
36
+ * @property {boolean} ownsDeadlines whether this call created the controller (and must dispose
37
+ * it) rather than borrowing the caller's
38
+ */
39
+ /**
40
+ * @typedef {object} OpenConnectionOptions
41
+ * @property {string | URL} url
42
+ * @property {import('./proxy/index.js').ConnectFn} connect injected socket factory
43
+ * @property {string | import('./proxy/index.js').ProxyConfig | null} [proxy]
44
+ * @property {import('./trust/index.js').TrustConfig} [trust] the `verify=`-style knob, passed
45
+ * through to the trust layer
46
+ * @property {DeadlineController} [deadlines] borrow the request's controller; omitting it makes
47
+ * this call own (and dispose) a fresh one
48
+ * @property {import('./tls/connect.js').TlsOptions} [tls] handshake options
49
+ * @property {string[]} [alpn] the ALPN protocol list to offer, newest/most-preferred first.
50
+ * Kept separate from `tls` so offering `h2` does not read as a user-supplied TLS option (which
51
+ * would disable native-fetch delegation and enter the pool key). `tls.alpn` still wins if set.
52
+ * @property {{ offer?: import('./tls/connect.js').ResumptionOffer | null,
53
+ * onTicket?: (t: import('./tls/connect.js').CapturedTicket) => void }} [resumption]
54
+ * session-resumption wiring, injected per connection by the Client. Separate from `tls` for
55
+ * the same reason `alpn` is: it must neither disable native-fetch delegation nor enter the
56
+ * pool key — it is not caller configuration, it is state the Client derived FROM the pool key.
57
+ * @property {import('./tls/connect.js').TlsDeps} [deps] injectable randomness/keygen for
58
+ * reproducible handshakes
59
+ * @property {AbortSignal} [signal]
60
+ * @property {{ maxProxyReplyBytes?: number }} [limits] cap on the proxy CONNECT reply head
61
+ * @property {number} [now] epoch ms override for certificate validity
62
+ */
63
+ /**
64
+ * Open a connection carrying application bytes for `url`. Throws (ProxyError, TlsError,
65
+ * CertificateError, TimeoutError, ConfigError) rather than resolving with a failure value.
66
+ *
67
+ * @param {OpenConnectionOptions} args
68
+ * @returns {Promise<Connection>}
69
+ */
70
+ export function openConnection({ url, connect, proxy, trust, deadlines, tls, alpn, resumption, deps, signal, limits, now, }: OpenConnectionOptions): Promise<Connection>;
71
+ /**
72
+ * The delegation decision, with the disqualifying reason spelled out so a caller's error can
73
+ * quote it. Discriminated on `ok` so `reason` is a string exactly when there is one.
74
+ * @typedef {{ ok: true, reason: null } | { ok: false, reason: string }} NativeFetchVerdict
75
+ */
76
+ /**
77
+ * @typedef {object} NativeFetchQuery
78
+ * @property {import('./proxy/index.js').ProxyConfig | string | null} [proxy]
79
+ * @property {import('./trust/index.js').TrustConfig | null} [trust]
80
+ * @property {import('./tls/connect.js').TlsOptions | null} [tls]
81
+ * @property {boolean} [forceTunnel]
82
+ */
83
+ /**
84
+ * Can the platform's own fetch() satisfy this request in full?
85
+ *
86
+ * Delegating to the native implementation when it can is strictly better — it is faster, does not
87
+ * burn metered CPU, speaks HTTP/2 and /3, and reaches origins our raw sockets are forbidden from
88
+ * dialling. But delegation must be decided on CAPABILITY, not merely on "is there a proxy":
89
+ * quietly satisfying a request that asked for a pinned certificate by handing it to an
90
+ * implementation using a different trust store would answer a security question the caller did
91
+ * not ask. So anything the native path cannot honour disqualifies it.
92
+ *
93
+ * @param {NativeFetchQuery} query
94
+ * @returns {NativeFetchVerdict}
95
+ */
96
+ export function nativeFetchCanServe({ proxy, trust, tls, forceTunnel }: NativeFetchQuery): NativeFetchVerdict;
97
+ /**
98
+ * A request URL reduced to what the transport dials.
99
+ */
100
+ export type TransportTarget = {
101
+ url: URL;
102
+ /**
103
+ * IPv6 unbracketed, as the socket API and SOCKS5 want it
104
+ */
105
+ hostname: string;
106
+ /**
107
+ * explicit port, or the scheme default
108
+ */
109
+ port: number;
110
+ /**
111
+ * whether the scheme is https:
112
+ */
113
+ secure: boolean;
114
+ };
115
+ /**
116
+ * What a Response's `tunnelfetch` detail reports about the connection, before the HTTP layer
117
+ * adds the per-response httpVersion and framing.
118
+ */
119
+ export type ConnectionInfo = {
120
+ url: string;
121
+ proxied: boolean;
122
+ /**
123
+ * the proxy actually used, credentials omitted
124
+ */
125
+ proxy: string | null;
126
+ /**
127
+ * null for cleartext
128
+ */
129
+ tls: import("./tls/connect.js").TlsSessionInfo | null;
130
+ };
131
+ /**
132
+ * A live connection carrying application bytes: the duplex, its provenance, and the deadline
133
+ * controller that governs it. This is what the pool stores and what sendAndReceive consumes.
134
+ */
135
+ export type Connection = {
136
+ readable: ReadableStream<Uint8Array>;
137
+ writable: WritableStream<Uint8Array>;
138
+ close: () => Promise<void> | void;
139
+ info: ConnectionInfo;
140
+ /**
141
+ * TLS only;
142
+ * null when trust mode 'none' accepted a leaf it could not parse
143
+ */
144
+ peerCertificate?: import("./trust/index.js").ParsedCertificate | null | undefined;
145
+ deadlines: DeadlineController;
146
+ /**
147
+ * whether this call created the controller (and must dispose
148
+ * it) rather than borrowing the caller's
149
+ */
150
+ ownsDeadlines: boolean;
151
+ };
152
+ export type OpenConnectionOptions = {
153
+ url: string | URL;
154
+ /**
155
+ * injected socket factory
156
+ */
157
+ connect: import("./proxy/index.js").ConnectFn;
158
+ proxy?: string | import("./proxy/index.js").ProxyConfig | null | undefined;
159
+ /**
160
+ * the `verify=`-style knob, passed
161
+ * through to the trust layer
162
+ */
163
+ trust?: import("./trust/index.js").TrustConfig | undefined;
164
+ /**
165
+ * borrow the request's controller; omitting it makes
166
+ * this call own (and dispose) a fresh one
167
+ */
168
+ deadlines?: DeadlineController | undefined;
169
+ /**
170
+ * handshake options
171
+ */
172
+ tls?: import("./tls/connect.js").TlsOptions | undefined;
173
+ /**
174
+ * the ALPN protocol list to offer, newest/most-preferred first.
175
+ * Kept separate from `tls` so offering `h2` does not read as a user-supplied TLS option (which
176
+ * would disable native-fetch delegation and enter the pool key). `tls.alpn` still wins if set.
177
+ */
178
+ alpn?: string[] | undefined;
179
+ /**
180
+ * session-resumption wiring, injected per connection by the Client. Separate from `tls` for
181
+ * the same reason `alpn` is: it must neither disable native-fetch delegation nor enter the
182
+ * pool key — it is not caller configuration, it is state the Client derived FROM the pool key.
183
+ */
184
+ resumption?: {
185
+ offer?: import("./tls/connect.js").ResumptionOffer | null;
186
+ onTicket?: (t: import("./tls/connect.js").CapturedTicket) => void;
187
+ } | undefined;
188
+ /**
189
+ * injectable randomness/keygen for
190
+ * reproducible handshakes
191
+ */
192
+ deps?: import("./tls/connect.js").TlsDeps | undefined;
193
+ signal?: AbortSignal | undefined;
194
+ /**
195
+ * cap on the proxy CONNECT reply head
196
+ */
197
+ limits?: {
198
+ maxProxyReplyBytes?: number;
199
+ } | undefined;
200
+ /**
201
+ * epoch ms override for certificate validity
202
+ */
203
+ now?: number | undefined;
204
+ };
205
+ /**
206
+ * The delegation decision, with the disqualifying reason spelled out so a caller's error can
207
+ * quote it. Discriminated on `ok` so `reason` is a string exactly when there is one.
208
+ */
209
+ export type NativeFetchVerdict = {
210
+ ok: true;
211
+ reason: null;
212
+ } | {
213
+ ok: false;
214
+ reason: string;
215
+ };
216
+ export type NativeFetchQuery = {
217
+ proxy?: string | import("./proxy/index.js").ProxyConfig | null | undefined;
218
+ trust?: import("./trust/index.js").TrustConfig | null | undefined;
219
+ tls?: import("./tls/connect.js").TlsOptions | null | undefined;
220
+ forceTunnel?: boolean | undefined;
221
+ };
222
+ import { DeadlineController } from './util/deadline.js';
@@ -0,0 +1,239 @@
1
+ /**
2
+ * One decoded tag-length-value element, as byte ranges into the ORIGINAL buffer. Every reader
3
+ * in the trust layer passes these around instead of slices precisely so that signature checks
4
+ * always run over the peer's own bytes.
5
+ * @typedef {object} Tlv
6
+ * @property {number} cls tag class, per {@link CLS}
7
+ * @property {boolean} constructed
8
+ * @property {number} tag tag number, high-tag-number form already decoded
9
+ * @property {number} start offset of the first header byte
10
+ * @property {number} headerLen tag + length octets
11
+ * @property {number} contentStart
12
+ * @property {number} contentEnd
13
+ * @property {number} end one past the element; equals contentEnd for every legal DER element
14
+ */
15
+ /**
16
+ * Describe a tag for error messages: "SEQUENCE", "[0]", "APPLICATION 3".
17
+ * @param {number} cls
18
+ * @param {number} tag
19
+ * @returns {string}
20
+ */
21
+ export function tagName(cls: number, tag: number): string;
22
+ /**
23
+ * @param {number} offset
24
+ * @param {string} message
25
+ * @returns {CertificateError}
26
+ */
27
+ export function parseError(offset: number, message: string): CertificateError;
28
+ /**
29
+ * Read one TLV starting at `offset`. Returns byte ranges only — content is always a subarray of
30
+ * the caller's original buffer, never a copy, so signatures can be verified over the same bytes
31
+ * the peer sent.
32
+ *
33
+ * @param {Uint8Array} bytes
34
+ * @param {number} offset
35
+ * @returns {Tlv}
36
+ */
37
+ export function readTlv(bytes: Uint8Array, offset: number): Tlv;
38
+ /**
39
+ * Assert a TLV has the given shape, with an error naming expected vs got at the offset.
40
+ * @param {Tlv} tlv
41
+ * @param {{ cls?: number, tag: number, constructed?: boolean }} shape omitted `constructed`
42
+ * accepts either form
43
+ * @param {string} what
44
+ * @returns {Tlv} the same tlv, for chaining into a reader
45
+ */
46
+ export function expectTlv(tlv: Tlv, { cls, tag, constructed }: {
47
+ cls?: number;
48
+ tag: number;
49
+ constructed?: boolean;
50
+ }, what: string): Tlv;
51
+ /**
52
+ * Read a TLV and require it to be a constructed SEQUENCE.
53
+ * @param {Uint8Array} bytes
54
+ * @param {number} offset
55
+ * @param {string} [what]
56
+ * @returns {Tlv}
57
+ */
58
+ export function readSequence(bytes: Uint8Array, offset: number, what?: string): Tlv;
59
+ /**
60
+ * Parse the content of a constructed element into its immediate children, requiring them to fill
61
+ * the content exactly. Trailing bytes inside a container are as suspicious as trailing bytes
62
+ * after the certificate.
63
+ * @param {Uint8Array} bytes
64
+ * @param {Tlv} tlv
65
+ * @param {string} [what]
66
+ * @returns {Tlv[]}
67
+ */
68
+ export function children(bytes: Uint8Array, tlv: Tlv, what?: string): Tlv[];
69
+ /**
70
+ * Read the single top-level element and reject trailing bytes after it.
71
+ * @param {Uint8Array} bytes
72
+ * @param {string} [what]
73
+ * @returns {Tlv}
74
+ */
75
+ export function readAll(bytes: Uint8Array, what?: string): Tlv;
76
+ /**
77
+ * INTEGER: returns the raw big-endian content and, when it fits a safe JS number, the value.
78
+ * DER minimality: the first nine bits must not be all-zero or all-one, else a shorter encoding
79
+ * exists. Laxness here would let two different byte strings claim the same serial number.
80
+ * @param {Uint8Array} bytes
81
+ * @param {Tlv} tlv
82
+ * @param {string} [what]
83
+ * @returns {{ bytes: Uint8Array, value: number | null, negative: boolean }} `value` is null
84
+ * when the magnitude does not fit a safe number (serials routinely do not)
85
+ */
86
+ export function readInteger(bytes: Uint8Array, tlv: Tlv, what?: string): {
87
+ bytes: Uint8Array;
88
+ value: number | null;
89
+ negative: boolean;
90
+ };
91
+ /**
92
+ * OBJECT IDENTIFIER to dotted string. Sub-identifiers are base-128 and must be minimal: a leading
93
+ * 0x80 continuation byte is a second spelling of the same OID, and two spellings of one identity
94
+ * is how "unknown critical extension" checks get bypassed.
95
+ * @param {Uint8Array} bytes
96
+ * @param {Tlv} tlv
97
+ * @param {string} [what]
98
+ * @returns {string} dotted form, e.g. '2.5.29.15'
99
+ */
100
+ export function readOid(bytes: Uint8Array, tlv: Tlv, what?: string): string;
101
+ /**
102
+ * BIT STRING: unused-bit count + payload. DER additionally requires the unused bits themselves to
103
+ * be zero — a nonzero padding bit is another two-spellings ambiguity.
104
+ * @param {Uint8Array} bytes
105
+ * @param {Tlv} tlv
106
+ * @param {string} [what]
107
+ * @returns {{ unusedBits: number, bytes: Uint8Array }}
108
+ */
109
+ export function readBitString(bytes: Uint8Array, tlv: Tlv, what?: string): {
110
+ unusedBits: number;
111
+ bytes: Uint8Array;
112
+ };
113
+ /**
114
+ * BOOLEAN. DER: content is exactly one byte, 0x00 or 0xff.
115
+ * @param {Uint8Array} bytes
116
+ * @param {Tlv} tlv
117
+ * @param {string} [what]
118
+ * @returns {boolean}
119
+ */
120
+ export function readBoolean(bytes: Uint8Array, tlv: Tlv, what?: string): boolean;
121
+ /**
122
+ * UTCTime, RFC 5280 profile: exactly YYMMDDHHMMSSZ. Seconds are mandatory, the zone is mandatory
123
+ * and must be Z, and fractional seconds do not exist in this profile. The two-digit year pivots
124
+ * at 50: 50..99 map to 19xx, 00..49 to 20xx (RFC 5280 s4.1.2.5.1).
125
+ * @param {Uint8Array} bytes
126
+ * @param {Tlv} tlv
127
+ * @param {string} [what]
128
+ * @returns {number} epoch ms UTC
129
+ */
130
+ export function readUtcTime(bytes: Uint8Array, tlv: Tlv, what?: string): number;
131
+ /**
132
+ * GeneralizedTime, RFC 5280 profile: exactly YYYYMMDDHHMMSSZ. Fractional seconds are explicitly
133
+ * forbidden by RFC 5280 s4.1.2.5.2, and allowing them would give one instant many encodings.
134
+ * @param {Uint8Array} bytes
135
+ * @param {Tlv} tlv
136
+ * @param {string} [what]
137
+ * @returns {number} epoch ms UTC
138
+ */
139
+ export function readGeneralizedTime(bytes: Uint8Array, tlv: Tlv, what?: string): number;
140
+ /**
141
+ * Either time type, as used by Validity and by name-constraint-free consumers.
142
+ * @param {Uint8Array} bytes
143
+ * @param {Tlv} tlv
144
+ * @param {string} [what]
145
+ * @returns {number} epoch ms UTC
146
+ */
147
+ export function readTime(bytes: Uint8Array, tlv: Tlv, what?: string): number;
148
+ /**
149
+ * Directory string types. Each type's alphabet is enforced — a PrintableString smuggling bytes
150
+ * outside its charset is two parsers disagreeing about one name.
151
+ *
152
+ * TeletexString is decoded as Latin-1: its real charset (T.61) is a negotiation-dependent mess
153
+ * that no CA has honoured in decades, and Latin-1 is the universal de-facto reading.
154
+ * @param {Uint8Array} bytes
155
+ * @param {Tlv} tlv
156
+ * @param {string} [what]
157
+ * @returns {string}
158
+ */
159
+ export function readString(bytes: Uint8Array, tlv: Tlv, what?: string): string;
160
+ /**
161
+ * ECDSA-Sig-Value (SEQUENCE of two INTEGERs) to the fixed-width r||s form WebCrypto verifies.
162
+ *
163
+ * This lives here, once, because both users of it are checking signatures: the certificate path
164
+ * builder and the TLS CertificateVerify. TLS transmits ECDSA signatures in DER while WebCrypto
165
+ * accepts only the P1363 concatenation, and a round-trip test that signs and verifies with
166
+ * WebCrypto agrees with itself while failing against every real server — so this conversion is
167
+ * exactly the sort of thing that must have one implementation and not two.
168
+ *
169
+ * Any malformation is an invalid signature; there is no "close enough" for signature bytes.
170
+ *
171
+ * @param {Uint8Array} sig DER ECDSA-Sig-Value
172
+ * @param {number} orderLen byte width of the curve order (32 / 48 / 66)
173
+ * @param {(why: string) => Error} onInvalid builds the caller's own error type
174
+ * @returns {Uint8Array} r||s, each half left-padded to orderLen
175
+ */
176
+ export function ecdsaDerToRaw(sig: Uint8Array, orderLen: number, onInvalid: (why: string) => Error): Uint8Array;
177
+ export namespace TAG {
178
+ let BOOLEAN: number;
179
+ let INTEGER: number;
180
+ let BIT_STRING: number;
181
+ let OCTET_STRING: number;
182
+ let NULL: number;
183
+ let OID: number;
184
+ let ENUMERATED: number;
185
+ let UTF8_STRING: number;
186
+ let SEQUENCE: number;
187
+ let SET: number;
188
+ let NUMERIC_STRING: number;
189
+ let PRINTABLE_STRING: number;
190
+ let TELETEX_STRING: number;
191
+ let IA5_STRING: number;
192
+ let UTC_TIME: number;
193
+ let GENERALIZED_TIME: number;
194
+ let VISIBLE_STRING: number;
195
+ let BMP_STRING: number;
196
+ }
197
+ export namespace CLS {
198
+ let UNIVERSAL: number;
199
+ let APPLICATION: number;
200
+ let CONTEXT: number;
201
+ let PRIVATE: number;
202
+ }
203
+ /** The content bytes of a TLV, as a subarray of the original buffer. */
204
+ /** @type {(bytes: Uint8Array, tlv: Tlv) => Uint8Array} */
205
+ export const content: (bytes: Uint8Array, tlv: Tlv) => Uint8Array;
206
+ /** The full element (tag + length + content), as a subarray of the original buffer. */
207
+ /** @type {(bytes: Uint8Array, tlv: Tlv) => Uint8Array} */
208
+ export const element: (bytes: Uint8Array, tlv: Tlv) => Uint8Array;
209
+ /**
210
+ * One decoded tag-length-value element, as byte ranges into the ORIGINAL buffer. Every reader
211
+ * in the trust layer passes these around instead of slices precisely so that signature checks
212
+ * always run over the peer's own bytes.
213
+ */
214
+ export type Tlv = {
215
+ /**
216
+ * tag class, per {@link CLS}
217
+ */
218
+ cls: number;
219
+ constructed: boolean;
220
+ /**
221
+ * tag number, high-tag-number form already decoded
222
+ */
223
+ tag: number;
224
+ /**
225
+ * offset of the first header byte
226
+ */
227
+ start: number;
228
+ /**
229
+ * tag + length octets
230
+ */
231
+ headerLen: number;
232
+ contentStart: number;
233
+ contentEnd: number;
234
+ /**
235
+ * one past the element; equals contentEnd for every legal DER element
236
+ */
237
+ end: number;
238
+ };
239
+ import { CertificateError } from '../errors.js';