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.
- package/LICENSE +28 -0
- package/README.md +617 -0
- package/README.zh-CN.md +470 -0
- package/package.json +74 -0
- package/src/client/cookies.js +429 -0
- package/src/client/decode.js +346 -0
- package/src/client/redirect.js +249 -0
- package/src/client.js +704 -0
- package/src/errors.js +181 -0
- package/src/http1/chunked.js +289 -0
- package/src/http1/index.js +10 -0
- package/src/http1/request.js +143 -0
- package/src/http1/response.js +493 -0
- package/src/http2/connection.js +1170 -0
- package/src/http2/constants.js +129 -0
- package/src/http2/frames.js +291 -0
- package/src/http2/hpack.js +420 -0
- package/src/http2/huffman.js +203 -0
- package/src/http2/index.js +21 -0
- package/src/index.js +46 -0
- package/src/pool.js +256 -0
- package/src/proxy/direct.js +62 -0
- package/src/proxy/http-connect.js +206 -0
- package/src/proxy/index.js +197 -0
- package/src/proxy/socks5.js +344 -0
- package/src/tls/aead.js +263 -0
- package/src/tls/connect.js +407 -0
- package/src/tls/constants.js +334 -0
- package/src/tls/extensions.js +376 -0
- package/src/tls/handshake-messages.js +901 -0
- package/src/tls/handshake.js +568 -0
- package/src/tls/handshake12.js +507 -0
- package/src/tls/index.js +44 -0
- package/src/tls/keyschedule.js +473 -0
- package/src/tls/record.js +872 -0
- package/src/tls/tickets.js +145 -0
- package/src/tls/transcript.js +101 -0
- package/src/tls/wire.js +224 -0
- package/src/transport.js +296 -0
- package/src/trust/der.js +551 -0
- package/src/trust/index.js +375 -0
- package/src/trust/name.js +235 -0
- package/src/trust/ocsp.js +759 -0
- package/src/trust/path.js +595 -0
- package/src/trust/roots.js +454 -0
- package/src/trust/x509.js +902 -0
- package/src/util/bytes.js +470 -0
- package/src/util/deadline.js +266 -0
- package/src/warmup-fixture.js +85 -0
- package/src/warmup.js +243 -0
- package/types/client/cookies.d.ts +159 -0
- package/types/client/decode.d.ts +54 -0
- package/types/client/redirect.d.ts +96 -0
- package/types/client.d.ts +323 -0
- package/types/errors.d.ts +141 -0
- package/types/http1/chunked.d.ts +48 -0
- package/types/http1/index.d.ts +3 -0
- package/types/http1/request.d.ts +44 -0
- package/types/http1/response.d.ts +183 -0
- package/types/http2/connection.d.ts +282 -0
- package/types/http2/constants.d.ts +95 -0
- package/types/http2/frames.d.ts +116 -0
- package/types/http2/hpack.d.ts +99 -0
- package/types/http2/huffman.d.ts +21 -0
- package/types/http2/index.d.ts +5 -0
- package/types/index.d.ts +17 -0
- package/types/pool.d.ts +135 -0
- package/types/proxy/direct.d.ts +26 -0
- package/types/proxy/http-connect.d.ts +37 -0
- package/types/proxy/index.d.ts +62 -0
- package/types/proxy/socks5.d.ts +47 -0
- package/types/tls/aead.d.ts +67 -0
- package/types/tls/connect.d.ts +280 -0
- package/types/tls/constants.d.ts +275 -0
- package/types/tls/extensions.d.ts +195 -0
- package/types/tls/handshake-messages.d.ts +430 -0
- package/types/tls/handshake.d.ts +90 -0
- package/types/tls/handshake12.d.ts +35 -0
- package/types/tls/index.d.ts +9 -0
- package/types/tls/keyschedule.d.ts +272 -0
- package/types/tls/record.d.ts +361 -0
- package/types/tls/tickets.d.ts +66 -0
- package/types/tls/transcript.d.ts +52 -0
- package/types/tls/wire.d.ts +106 -0
- package/types/transport.d.ts +222 -0
- package/types/trust/der.d.ts +239 -0
- package/types/trust/index.d.ts +194 -0
- package/types/trust/name.d.ts +33 -0
- package/types/trust/ocsp.d.ts +138 -0
- package/types/trust/path.d.ts +139 -0
- package/types/trust/roots.d.ts +36 -0
- package/types/trust/x509.d.ts +401 -0
- package/types/util/bytes.d.ts +183 -0
- package/types/util/deadline.d.ts +133 -0
- package/types/warmup-fixture.d.ts +11 -0
- package/types/warmup.d.ts +45 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
// TLS extension encoding and decoding.
|
|
2
|
+
//
|
|
3
|
+
// Encoding is written as small pure functions returning bytes so the ClientHello is testable as a
|
|
4
|
+
// fixed vector. Decoding refuses to be clever: an extension we did not offer coming back from the
|
|
5
|
+
// server is a protocol violation (RFC 8446 s4.2), and an extension appearing twice is a smuggling
|
|
6
|
+
// shape, so both are errors rather than last-one-wins.
|
|
7
|
+
|
|
8
|
+
import { TlsError, TlsUnsupportedError, codes, hex16 } from '../errors.js';
|
|
9
|
+
import { utf8, u16, u32 } from '../util/bytes.js';
|
|
10
|
+
import { Builder, Cursor, vector } from './wire.js';
|
|
11
|
+
import {
|
|
12
|
+
EXTENSION,
|
|
13
|
+
GROUP,
|
|
14
|
+
GROUP_NAME,
|
|
15
|
+
GROUP_PARAMS,
|
|
16
|
+
SIG_SCHEME_NAME,
|
|
17
|
+
SUPPORTED_GROUPS,
|
|
18
|
+
SUPPORTED_SIG_SCHEMES,
|
|
19
|
+
TLS12,
|
|
20
|
+
TLS13,
|
|
21
|
+
VERSION_NAME,
|
|
22
|
+
} from './constants.js';
|
|
23
|
+
|
|
24
|
+
/** Wrap a body as `extension_type || extension_data<0..2^16-1>`. */
|
|
25
|
+
function ext(type, body) {
|
|
26
|
+
return new Builder().u16(type).vector(2, body).build();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ------------------------------------------------------------------ encoders (ClientHello)
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* server_name (RFC 6066). Only host_name (type 0) exists in practice.
|
|
33
|
+
* An IP literal must NOT be sent as SNI — RFC 6066 s3 forbids it, and servers that do virtual
|
|
34
|
+
* hosting will hand back an unrelated certificate if we do, which would look like an attack.
|
|
35
|
+
* @param {string} hostname
|
|
36
|
+
* @returns {Uint8Array | null} null for an IP literal, which sends no SNI at all
|
|
37
|
+
*/
|
|
38
|
+
export function encodeServerName(hostname) {
|
|
39
|
+
if (isIpLiteral(hostname)) return null;
|
|
40
|
+
const name = utf8(hostname);
|
|
41
|
+
if (name.byteLength === 0 || name.byteLength > 0xffff) {
|
|
42
|
+
throw new TlsError(codes.CONFIG_INVALID, `server_name of ${name.byteLength} bytes is invalid`);
|
|
43
|
+
}
|
|
44
|
+
const entry = new Builder().u8(0).vector(2, name).build();
|
|
45
|
+
return ext(EXTENSION.server_name, vector(2, entry));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* status_request (RFC 6066 s8): ask the server to staple an OCSP response for its certificate.
|
|
50
|
+
*
|
|
51
|
+
* The body is a CertificateStatusRequest: status_type ocsp(1), an empty responder_id_list (we
|
|
52
|
+
* accept whatever responder the CA authorised — a client cannot usefully narrow that), and empty
|
|
53
|
+
* request_extensions (no nonce: a stapled response is produced before our hello exists, so a
|
|
54
|
+
* nonce could never be honoured and freshness comes from thisUpdate/nextUpdate instead).
|
|
55
|
+
*
|
|
56
|
+
* Offered in every hello, for both versions: it costs 9 bytes, and a server that ignores it
|
|
57
|
+
* loses nothing. How the answer arrives differs by version — TLS 1.2 echoes the extension and
|
|
58
|
+
* sends a separate CertificateStatus message (RFC 6066 s8), TLS 1.3 attaches it to the leaf's
|
|
59
|
+
* CertificateEntry (RFC 8446 s4.4.2.1) — and each driver consumes its own form.
|
|
60
|
+
* @returns {Uint8Array}
|
|
61
|
+
*/
|
|
62
|
+
export function encodeStatusRequest() {
|
|
63
|
+
return ext(
|
|
64
|
+
EXTENSION.status_request,
|
|
65
|
+
new Builder().u8(1).vector(2, new Uint8Array(0)).vector(2, new Uint8Array(0)).build(),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {number[]} versions in preference order
|
|
71
|
+
* @returns {Uint8Array}
|
|
72
|
+
*/
|
|
73
|
+
export function encodeSupportedVersions(versions) {
|
|
74
|
+
const b = new Builder();
|
|
75
|
+
for (const v of versions) b.u16(v);
|
|
76
|
+
return ext(EXTENSION.supported_versions, vector(1, b.build()));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {number[]} [groups]
|
|
81
|
+
* @returns {Uint8Array}
|
|
82
|
+
*/
|
|
83
|
+
export function encodeSupportedGroups(groups = SUPPORTED_GROUPS) {
|
|
84
|
+
const b = new Builder();
|
|
85
|
+
for (const g of groups) b.u16(g);
|
|
86
|
+
return ext(EXTENSION.supported_groups, vector(2, b.build()));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {number[]} [schemes]
|
|
91
|
+
* @returns {Uint8Array}
|
|
92
|
+
*/
|
|
93
|
+
export function encodeSignatureAlgorithms(schemes = SUPPORTED_SIG_SCHEMES) {
|
|
94
|
+
const b = new Builder();
|
|
95
|
+
for (const s of schemes) b.u16(s);
|
|
96
|
+
return ext(EXTENSION.signature_algorithms, vector(2, b.build()));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* key_share entries, in the same order as supported_groups.
|
|
101
|
+
* @param {Array<{ group: number, keyExchange: Uint8Array }>} shares the public halves only;
|
|
102
|
+
* private keys never reach the encoder
|
|
103
|
+
* @returns {Uint8Array}
|
|
104
|
+
*/
|
|
105
|
+
export function encodeKeyShare(shares) {
|
|
106
|
+
const b = new Builder();
|
|
107
|
+
for (const { group, keyExchange } of shares) {
|
|
108
|
+
b.u16(group).vector(2, keyExchange);
|
|
109
|
+
}
|
|
110
|
+
return ext(EXTENSION.key_share, vector(2, b.build()));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The HelloRetryRequest response carries a bare group id with no share.
|
|
115
|
+
* @param {number} group
|
|
116
|
+
* @returns {Uint8Array}
|
|
117
|
+
*/
|
|
118
|
+
export function encodeKeyShareHrr(group) {
|
|
119
|
+
return ext(EXTENSION.key_share, u16(group));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @param {string[]} protocols
|
|
124
|
+
* @returns {Uint8Array}
|
|
125
|
+
*/
|
|
126
|
+
export function encodeAlpn(protocols) {
|
|
127
|
+
const b = new Builder();
|
|
128
|
+
for (const p of protocols) b.vector(1, utf8(p));
|
|
129
|
+
return ext(EXTENSION.alpn, vector(2, b.build()));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* psk_key_exchange_modes (RFC 8446 s4.2.9), mandatory in any ClientHello that offers (or might
|
|
134
|
+
* later offer) pre_shared_key, and sent in every 1.3 hello regardless because some middleboxes
|
|
135
|
+
* reject its absence and it costs 6 bytes.
|
|
136
|
+
*
|
|
137
|
+
* Only psk_dhe_ke(1) is offered, ever. psk_ke would let a resumed connection run with no fresh
|
|
138
|
+
* (EC)DHE at all, so compromise of one ticket's PSK would decrypt every session resumed from it
|
|
139
|
+
* — the forward-secrecy property the rest of this package refuses to trade away (no RSA key
|
|
140
|
+
* transport for the same reason). A server honouring psk_dhe_ke must still send key_share, and
|
|
141
|
+
* selectServerKeyShare fails closed if it does not.
|
|
142
|
+
*/
|
|
143
|
+
export function encodePskKeyExchangeModes() {
|
|
144
|
+
return ext(EXTENSION.psk_key_exchange_modes, vector(1, Uint8Array.from([1]))); // psk_dhe_ke
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* pre_shared_key for a ClientHello (RFC 8446 s4.2.11): one PskIdentity (the ticket plus its
|
|
149
|
+
* obfuscated age) and one PskBinderEntry. The binder cannot be known while the hello is being
|
|
150
|
+
* encoded — it is an HMAC over a transcript of the very hello it sits in, truncated just before
|
|
151
|
+
* the binders list — so it is emitted here as `binderLen` ZERO bytes, at the exact length the
|
|
152
|
+
* real binder will have, and the builder patches the real value in afterwards. RFC 8446
|
|
153
|
+
* s4.2.11.2 requires exactly this shape: every length field is computed as if the true binder
|
|
154
|
+
* were present, and only then is the binder derived and substituted.
|
|
155
|
+
*
|
|
156
|
+
* Exactly one identity is offered by design. The wire format allows a list, but this client
|
|
157
|
+
* only ever holds resumption PSKs and offers the newest usable ticket; a multi-PSK offer would
|
|
158
|
+
* multiply binder computations for a case that cannot arise here.
|
|
159
|
+
*
|
|
160
|
+
* @param {object} psk
|
|
161
|
+
* @param {Uint8Array} psk.identity the ticket, opaque, 1..2^16-1 bytes
|
|
162
|
+
* @param {number} psk.obfuscatedTicketAge uint32, already obfuscated per s4.2.11.1
|
|
163
|
+
* @param {number} psk.binderLen digest length of the PSK's hash
|
|
164
|
+
* @returns {Uint8Array}
|
|
165
|
+
*/
|
|
166
|
+
export function encodePreSharedKey({ identity, obfuscatedTicketAge, binderLen }) {
|
|
167
|
+
if (identity.byteLength === 0 || identity.byteLength > 0xffff) {
|
|
168
|
+
throw new TlsError(codes.TLS_TICKET,
|
|
169
|
+
`PSK identity of ${identity.byteLength} bytes is outside 1..65535 and cannot be offered`,
|
|
170
|
+
{ length: identity.byteLength });
|
|
171
|
+
}
|
|
172
|
+
const entry = new Builder().vector(2, identity).push(u32(obfuscatedTicketAge >>> 0)).build();
|
|
173
|
+
const binders = vector(2, vector(1, new Uint8Array(binderLen)));
|
|
174
|
+
return ext(EXTENSION.pre_shared_key, new Builder().vector(2, entry).push(binders).build());
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The number of trailing ClientHello bytes occupied by the binders list this client emits: the
|
|
179
|
+
* 2-byte list length, the 1-byte entry length, and the binder itself. This is the truncation
|
|
180
|
+
* arithmetic of RFC 8446 s4.2.11.2 — the binder transcript covers the hello up to and including
|
|
181
|
+
* the identities, i.e. everything except these bytes — kept next to the encoder above so the
|
|
182
|
+
* two cannot drift apart. pre_shared_key being the LAST extension (enforced by the builder) is
|
|
183
|
+
* what makes "trailing bytes of the message" and "the binders list" the same thing.
|
|
184
|
+
* @param {number} binderLen
|
|
185
|
+
* @returns {number}
|
|
186
|
+
*/
|
|
187
|
+
export function pskBinderTrailerLength(binderLen) {
|
|
188
|
+
return 2 + 1 + binderLen;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* pre_shared_key in a ServerHello is a bare uint16 selected_identity (RFC 8446 s4.2.11).
|
|
193
|
+
* @param {Uint8Array} data
|
|
194
|
+
* @returns {number}
|
|
195
|
+
*/
|
|
196
|
+
export function decodeServerPreSharedKey(data) {
|
|
197
|
+
const c = new Cursor(data, 'ServerHello pre_shared_key');
|
|
198
|
+
const selected = c.u16('selected_identity');
|
|
199
|
+
c.end('selected_identity');
|
|
200
|
+
return selected;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** RFC 7627. Requesting extended master secret closes the triple-handshake hole in TLS 1.2. */
|
|
204
|
+
export function encodeExtendedMasterSecret() {
|
|
205
|
+
return ext(EXTENSION.extended_master_secret, new Uint8Array(0));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* RFC 5746. An empty renegotiation_info says "I support secure renegotiation and have not
|
|
210
|
+
* renegotiated". We never renegotiate, but omitting this makes some TLS 1.2 servers reject us.
|
|
211
|
+
*/
|
|
212
|
+
export function encodeRenegotiationInfo() {
|
|
213
|
+
return ext(EXTENSION.renegotiation_info, vector(1, new Uint8Array(0)));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** RFC 8422 s5.1.2: uncompressed only. Compressed points are not implemented anywhere modern. */
|
|
217
|
+
export function encodeEcPointFormats() {
|
|
218
|
+
return ext(EXTENSION.ec_point_formats, vector(1, Uint8Array.from([0])));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @param {Array<Uint8Array | null>} parts nulls tolerated so conditional extensions read cleanly
|
|
223
|
+
* @returns {Uint8Array}
|
|
224
|
+
*/
|
|
225
|
+
export function encodeExtensionBlock(parts) {
|
|
226
|
+
const b = new Builder();
|
|
227
|
+
for (const p of parts) if (p) b.push(p);
|
|
228
|
+
return vector(2, b.build());
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ------------------------------------------------------------------ decoding
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Decode an extension block into a Map. Duplicates are rejected rather than folded: a repeated
|
|
235
|
+
* extension is never legitimate and letting the last one win is how parser-differential bugs start.
|
|
236
|
+
* @param {Uint8Array} bytes the block content, its outer length prefix already consumed
|
|
237
|
+
* @param {string} where named in errors, e.g. 'ServerHello'
|
|
238
|
+
* @returns {Map<number, Uint8Array>}
|
|
239
|
+
*/
|
|
240
|
+
export function decodeExtensionBlock(bytes, where) {
|
|
241
|
+
const c = new Cursor(bytes, `${where} extensions`);
|
|
242
|
+
const out = new Map();
|
|
243
|
+
while (!c.done) {
|
|
244
|
+
const type = c.u16('extension_type');
|
|
245
|
+
const data = c.vector(2, 'extension_data');
|
|
246
|
+
if (out.has(type)) {
|
|
247
|
+
throw new TlsError(
|
|
248
|
+
codes.TLS_HANDSHAKE,
|
|
249
|
+
`${where} contains extension ${hex16(type)} twice`,
|
|
250
|
+
{ where, extension: type },
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
out.set(type, data);
|
|
254
|
+
}
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* RFC 8446 s4.2: the server may only send extensions the client offered. Anything else means we
|
|
260
|
+
* and the server disagree about the negotiation, which is not a state to continue from.
|
|
261
|
+
* @param {Map<number, Uint8Array>} received
|
|
262
|
+
* @param {Set<number>} offered extension types the ClientHello carried
|
|
263
|
+
* @param {string} where
|
|
264
|
+
* @returns {void} throws TlsError on the first unoffered extension
|
|
265
|
+
*/
|
|
266
|
+
export function rejectUnofferedExtensions(received, offered, where) {
|
|
267
|
+
for (const type of received.keys()) {
|
|
268
|
+
if (!offered.has(type)) {
|
|
269
|
+
throw new TlsError(
|
|
270
|
+
codes.TLS_EXTENSION_UNSUPPORTED,
|
|
271
|
+
`${where} contains extension ${hex16(type)} which was not offered in ClientHello`,
|
|
272
|
+
{ where, extension: type },
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* supported_versions in a ServerHello is a single uint16, not a list.
|
|
280
|
+
* @param {Uint8Array} data
|
|
281
|
+
* @returns {number}
|
|
282
|
+
*/
|
|
283
|
+
export function decodeSelectedVersion(data) {
|
|
284
|
+
const c = new Cursor(data, 'supported_versions');
|
|
285
|
+
const v = c.u16('selected_version');
|
|
286
|
+
c.end('selected_version');
|
|
287
|
+
return v;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* @param {Uint8Array} data
|
|
292
|
+
* @param {string} where
|
|
293
|
+
* @returns {{ group: number, keyExchange: Uint8Array }}
|
|
294
|
+
*/
|
|
295
|
+
export function decodeKeyShareEntry(data, where) {
|
|
296
|
+
const c = new Cursor(data, `${where} key_share`);
|
|
297
|
+
const group = c.u16('group');
|
|
298
|
+
const keyExchange = c.vector(2, 'key_exchange');
|
|
299
|
+
c.end('key_share entry');
|
|
300
|
+
return { group, keyExchange };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* HelloRetryRequest's key_share is a bare group with no key.
|
|
305
|
+
* @param {Uint8Array} data
|
|
306
|
+
* @returns {number}
|
|
307
|
+
*/
|
|
308
|
+
export function decodeKeyShareHrr(data) {
|
|
309
|
+
const c = new Cursor(data, 'HelloRetryRequest key_share');
|
|
310
|
+
const group = c.u16('selected_group');
|
|
311
|
+
c.end('selected_group');
|
|
312
|
+
return group;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* @param {Uint8Array} data
|
|
317
|
+
* @returns {string} the single protocol the server selected (RFC 7301 s3.2)
|
|
318
|
+
*/
|
|
319
|
+
export function decodeAlpn(data) {
|
|
320
|
+
const c = new Cursor(data, 'alpn');
|
|
321
|
+
const list = c.sub(2, 'protocol_name_list');
|
|
322
|
+
const name = list.vector(1, 'protocol_name');
|
|
323
|
+
// RFC 7301 s3.2: the server's list must contain exactly one protocol.
|
|
324
|
+
list.end('protocol_name_list');
|
|
325
|
+
c.end('alpn');
|
|
326
|
+
return new TextDecoder().decode(name);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ------------------------------------------------------------------ validation helpers
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* @param {number} group
|
|
333
|
+
* @param {string} where
|
|
334
|
+
* @returns {import('./constants.js').GroupParams} throws TlsUnsupportedError when unimplemented
|
|
335
|
+
*/
|
|
336
|
+
export function requireSupportedGroup(group, where) {
|
|
337
|
+
const params = GROUP_PARAMS[group];
|
|
338
|
+
if (!params) {
|
|
339
|
+
const name = GROUP_NAME[group] ?? 'unknown';
|
|
340
|
+
throw new TlsUnsupportedError(
|
|
341
|
+
codes.TLS_GROUP_UNSUPPORTED,
|
|
342
|
+
`server selected group ${hex16(group)} (${name}) in ${where}, not implemented; ` +
|
|
343
|
+
`offered ${SUPPORTED_GROUPS.map((g) => `${GROUP_NAME[g]}(${hex16(g)})`).join(', ')}`,
|
|
344
|
+
{ group, groupName: name },
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
return params;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* @param {number} scheme
|
|
352
|
+
* @returns {string}
|
|
353
|
+
*/
|
|
354
|
+
export function describeSigScheme(scheme) {
|
|
355
|
+
return `${hex16(scheme)}${SIG_SCHEME_NAME[scheme] ? ` (${SIG_SCHEME_NAME[scheme]})` : ''}`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* @param {number} version
|
|
360
|
+
* @returns {string}
|
|
361
|
+
*/
|
|
362
|
+
export function describeVersion(version) {
|
|
363
|
+
return `${hex16(version)}${VERSION_NAME[version] ? ` (${VERSION_NAME[version]})` : ''}`;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Cheap classification, not validation: decides whether a name is legal as SNI.
|
|
368
|
+
* @param {string} host
|
|
369
|
+
* @returns {boolean}
|
|
370
|
+
*/
|
|
371
|
+
export function isIpLiteral(host) {
|
|
372
|
+
if (host.includes(':')) return true; // only IPv6 literals contain a colon in a bare authority
|
|
373
|
+
return /^\d{1,3}(\.\d{1,3}){3}$/.test(host);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export { GROUP, TLS12, TLS13 };
|