@amritk/nish-x86_64-linux 0.15.0 → 0.17.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 (57) hide show
  1. package/INSTALL.md +58 -40
  2. package/bin/nish +0 -0
  3. package/package.json +1 -1
  4. package/runtime/nish.d.ts +117 -8
  5. package/runtime/nish.h +67 -10
  6. package/runtime/nish.mjs +71 -7
  7. package/runtime/runtime-host.c +72 -26
  8. package/runtime/runtime-net.c +72 -19
  9. package/runtime/runtime-os.c +127 -45
  10. package/runtime/runtime-parallel.c +5 -1
  11. package/runtime/runtime-wasm.c +26 -2
  12. package/runtime/runtime.c +86 -14
  13. package/runtime/shim.mjs +114 -5
  14. package/scripts/build.sh +1 -3
  15. package/std/README.md +192 -36
  16. package/std/crypto/aes.ts +104 -21
  17. package/std/crypto/base64url.ts +43 -17
  18. package/std/crypto/chacha20poly1305.ts +52 -31
  19. package/std/crypto/ct.ts +14 -3
  20. package/std/crypto/hkdf.ts +385 -8
  21. package/std/crypto/hmac.ts +325 -9
  22. package/std/crypto/p256.ts +153 -27
  23. package/std/crypto/sha1.ts +147 -0
  24. package/std/crypto/sha256.ts +15 -3
  25. package/std/crypto/sha512.ts +16 -2
  26. package/std/crypto/x25519.ts +90 -37
  27. package/std/crypto/x509.ts +265 -65
  28. package/std/net/hpack.ts +1094 -0
  29. package/std/net/http-fields.ts +604 -0
  30. package/std/net/http1-server.ts +1389 -0
  31. package/std/net/http1.ts +1492 -0
  32. package/std/net/http2-frame.ts +745 -0
  33. package/std/net/http2-tls.ts +216 -0
  34. package/std/net/http2.ts +1433 -0
  35. package/std/net/http3-frame.ts +457 -0
  36. package/std/net/http3-server.ts +905 -0
  37. package/std/net/http3.ts +2145 -0
  38. package/std/net/qpack.ts +1163 -0
  39. package/std/net/quic-conn-ack.ts +206 -0
  40. package/std/net/quic-conn-cid.ts +425 -0
  41. package/std/net/quic-conn-params.ts +392 -0
  42. package/std/net/quic-datagram.ts +130 -0
  43. package/std/net/quic-frame.ts +1206 -0
  44. package/std/net/quic-listener.ts +653 -0
  45. package/std/net/quic-packet.ts +1474 -0
  46. package/std/net/quic-recovery.ts +849 -0
  47. package/std/net/quic-stream.ts +1603 -0
  48. package/std/net/quic.ts +3214 -0
  49. package/std/net/tls/codec.ts +1089 -0
  50. package/std/net/tls/record-server.ts +1173 -0
  51. package/std/net/tls/record.ts +621 -0
  52. package/std/net/tls/schedule.ts +429 -0
  53. package/std/net/tls-tcp.ts +410 -0
  54. package/std/net/tls.ts +1368 -0
  55. package/std/net/websocket.ts +823 -0
  56. package/std/net/webtransport.ts +1334 -0
  57. package/std/secret.ts +62 -0
@@ -0,0 +1,1089 @@
1
+ /**
2
+ * `nish/net/tls/codec` — the TLS 1.3 handshake messages a server reads and
3
+ * writes (RFC 8446 §4), as bytes.
4
+ *
5
+ * One reader and six writers. `tlsParseClientHello` reads a ClientHello body
6
+ * into a `TlsClientHello`, checking its structure: every length against the
7
+ * bytes it claims, every vector against its RFC 8446 bounds, and no extension
8
+ * twice (§4.2). A message that breaks one answers the alert a server sends for
9
+ * it in the hello's `alert` field (`decode_error` for a malformed or truncated
10
+ * one, `illegal_parameter` for a duplicate), never a panic, since every byte
11
+ * of it is the peer's. What the fields *mean* — which version, which suite,
12
+ * whether a share is missing — is the server's to decide, in `nish/net/tls`.
13
+ *
14
+ * `tlsReadClientHello` is the same reader for a server that keeps its state
15
+ * in a slot: it reads into a `TlsClientHelloView` made once, whose fields are
16
+ * windows into the message rather than copies of it, so a ClientHello read
17
+ * allocates nothing, and `tlsParseClientHello` copies a view's windows out.
18
+ *
19
+ * The writers each answer one whole handshake message, header included:
20
+ * ServerHello, HelloRetryRequest, EncryptedExtensions, Certificate,
21
+ * CertificateVerify and Finished. Their inputs come from the server and the
22
+ * caller's configuration rather than from the peer, so they check nothing.
23
+ * Each is a `tlsWrite*` function that writes into the caller's array at an
24
+ * offset — run over an empty array, it measures — and a `tlsEncode*` one
25
+ * that answers the message in a fresh array.
26
+ *
27
+ * The extensions read are `server_name` (RFC 6066 §3), `supported_groups`,
28
+ * `signature_algorithms`, `supported_versions` and `key_share` (RFC 8446
29
+ * §4.2), `application_layer_protocol_negotiation` (RFC 7301 §3.1) and
30
+ * `quic_transport_parameters` (RFC 9001 §8.2). Every other extension is
31
+ * skipped, which is what RFC 8446 §4.2 asks of a server that does not know it.
32
+ *
33
+ * Written from the RFCs, not ported from another implementation. Private
34
+ * names carry the `tls` prefix because a `std/` module's private functions
35
+ * share the importing program's flat symbol namespace (`docs/wp26-stdlib.md`
36
+ * §3e).
37
+ */
38
+
39
+ // ---- Handshake message types (RFC 8446 §4) --------------------------------
40
+
41
+ export const TLS_HANDSHAKE_CLIENT_HELLO: i32 = 1
42
+ export const TLS_HANDSHAKE_SERVER_HELLO: i32 = 2
43
+ export const TLS_HANDSHAKE_ENCRYPTED_EXTENSIONS: i32 = 8
44
+ export const TLS_HANDSHAKE_CERTIFICATE: i32 = 11
45
+ export const TLS_HANDSHAKE_CERTIFICATE_VERIFY: i32 = 15
46
+ export const TLS_HANDSHAKE_FINISHED: i32 = 20
47
+ /** The synthetic message a HelloRetryRequest puts in the transcript in place of the first ClientHello (§4.4.1). */
48
+ export const TLS_HANDSHAKE_MESSAGE_HASH: i32 = 254
49
+
50
+ // ---- Extension types (RFC 8446 §4.2, RFC 7301, RFC 9001) ------------------
51
+
52
+ export const TLS_EXT_SERVER_NAME: i32 = 0
53
+ /** `pre_shared_key` (§4.2.11): ignored here, but it must be the last extension. */
54
+ export const TLS_EXT_PRE_SHARED_KEY: i32 = 41
55
+ export const TLS_EXT_SUPPORTED_GROUPS: i32 = 10
56
+ export const TLS_EXT_SIGNATURE_ALGORITHMS: i32 = 13
57
+ export const TLS_EXT_ALPN: i32 = 16
58
+ export const TLS_EXT_SUPPORTED_VERSIONS: i32 = 43
59
+ export const TLS_EXT_KEY_SHARE: i32 = 51
60
+ export const TLS_EXT_QUIC_TRANSPORT_PARAMETERS: i32 = 57
61
+
62
+ // ---- Versions, groups and signature schemes -------------------------------
63
+
64
+ /** `legacy_version` in every TLS 1.3 hello (§4.1.2). */
65
+ export const TLS_LEGACY_VERSION: i32 = 0x0303
66
+ /** TLS 1.3 in `supported_versions` (§4.2.1). */
67
+ export const TLS_VERSION_13: i32 = 0x0304
68
+ /** x25519 (§4.2.7), the one group `nish/net/tls` agrees to. */
69
+ export const TLS_GROUP_X25519: i32 = 0x001d
70
+ /** The length of an x25519 key share (RFC 8446 §4.2.8.2). */
71
+ export const TLS_X25519_SHARE_SIZE: i32 = 32
72
+ /** `ecdsa_secp256r1_sha256` (§4.2.3), what a production server signs with. */
73
+ export const TLS_SIGNATURE_ECDSA_SECP256R1_SHA256: i32 = 0x0403
74
+ /** `rsa_pss_rsae_sha256` (§4.2.3), what RFC 8448's server signs with. */
75
+ export const TLS_SIGNATURE_RSA_PSS_RSAE_SHA256: i32 = 0x0804
76
+
77
+ // ---- Alert descriptions (RFC 8446 §6) --------------------------------------
78
+
79
+ /** A message arrived that the handshake's state does not allow. */
80
+ export const TLS_ALERT_UNEXPECTED_MESSAGE: i32 = 10
81
+ /** No acceptable set of parameters: no common suite, group or signature scheme. */
82
+ export const TLS_ALERT_HANDSHAKE_FAILURE: i32 = 40
83
+ /** A field well formed but wrong: a duplicate, a bad share, a second retry without the share. */
84
+ export const TLS_ALERT_ILLEGAL_PARAMETER: i32 = 47
85
+ /** A message that does not parse: a length past its bytes, a vector out of its bounds, trailing bytes. */
86
+ export const TLS_ALERT_DECODE_ERROR: i32 = 50
87
+ /** A Finished that does not verify. */
88
+ export const TLS_ALERT_DECRYPT_ERROR: i32 = 51
89
+ /** A peer that does not offer TLS 1.3. */
90
+ export const TLS_ALERT_PROTOCOL_VERSION: i32 = 70
91
+ /** A failure of this side's own: a bad configuration, randomness or signature, or a call out of order. */
92
+ export const TLS_ALERT_INTERNAL_ERROR: i32 = 80
93
+ /** An extension the handshake needs is absent (§9.2, RFC 9001 §8.2). */
94
+ export const TLS_ALERT_MISSING_EXTENSION: i32 = 109
95
+ /** ALPN offered no protocol the server accepts (RFC 7301 §3.2, RFC 9001 §8.1). */
96
+ export const TLS_ALERT_NO_APPLICATION_PROTOCOL: i32 = 120
97
+
98
+ /** The size of a hello's `random` (§4.1.2). */
99
+ export const TLS_RANDOM_SIZE: i32 = 32
100
+
101
+ /** A typed zero for the offsets below: a bare literal is an `f64` under `--number-mode f64`. */
102
+ const TLS_CODEC_FROM: i32 = 0
103
+
104
+ /**
105
+ * A cursor over `data[at .. end)` for the `tlsRead*` functions below, which
106
+ * read big-endian integers and the length prefixes of TLS vectors (RFC 8446
107
+ * §3.4). They are functions rather than methods so that a literal argument
108
+ * takes its parameter's `i32` under `--number-mode f64`.
109
+ *
110
+ * A read past `end`, or a vector whose length breaks its bounds, sets
111
+ * `failed` and answers 0, and every later read then fails too. So a parser
112
+ * can read a whole structure and test `failed` once, and a truncated message
113
+ * cannot read a byte that is not its own: `end` never passes the window the
114
+ * reader was opened on.
115
+ */
116
+ class TlsReader {
117
+ data: u8[]
118
+ at: i32
119
+ end: i32
120
+ failed: boolean = false
121
+
122
+ constructor(data: u8[], at: i32, end: i32) {
123
+ this.data = data
124
+ this.at = at
125
+ this.end = end
126
+ }
127
+ }
128
+
129
+ /** The next byte, or 0 and a failed reader at the end of the window. */
130
+ const tlsReadU8 = (r: TlsReader): i32 => {
131
+ const at: i32 = r.at
132
+ if (r.failed || at < 0 || at >= r.end || at >= toI32(r.data.length)) {
133
+ r.failed = true
134
+ return 0
135
+ }
136
+ r.at = at + 1
137
+ return toI32(r.data[at])
138
+ }
139
+
140
+ /** The next two bytes, big-endian. */
141
+ const tlsReadU16 = (r: TlsReader): i32 => {
142
+ const hi: i32 = tlsReadU8(r)
143
+ return (hi << 8) | tlsReadU8(r)
144
+ }
145
+
146
+ /**
147
+ * Reads a `width`-byte length (1, 2 or 3) and answers where the vector it
148
+ * opens ends. A length below `min`, above `max` or past `end` fails, and
149
+ * answers the current position so that the vector reads as empty.
150
+ */
151
+ const tlsReadVector = (r: TlsReader, width: i32, min: i32, max: i32): i32 => {
152
+ let n: i32 = 0
153
+ for (let k: i32 = 0; k < width; k++) {
154
+ n = (n << 8) | tlsReadU8(r)
155
+ }
156
+ if (r.failed || n < min || n > max || n > r.end - r.at) {
157
+ r.failed = true
158
+ return r.at
159
+ }
160
+ return r.at + n
161
+ }
162
+
163
+ /**
164
+ * Narrows the reader to a list that ends at `listEnd`, which a vector read just
165
+ * opened, and answers the end to restore (`r.end = outer`) once its entries
166
+ * are read. Every entry of a TLS list must lie inside the list's declared
167
+ * length, not merely inside the message or the extension around it, so an
168
+ * entry whose own length runs past the list fails the reader.
169
+ */
170
+ const tlsReadList = (r: TlsReader, listEnd: i32): i32 => {
171
+ const outer: i32 = r.end
172
+ r.end = listEnd
173
+ return outer
174
+ }
175
+
176
+ /** Moves to `to`, the end of a vector this reader opened. */
177
+ const tlsReadSkipTo = (r: TlsReader, to: i32): void => {
178
+ if (to < r.at || to > r.end) {
179
+ r.failed = true
180
+ return
181
+ }
182
+ r.at = to
183
+ }
184
+
185
+ /**
186
+ * A ClientHello as `tlsReadClientHello` read it, without a byte copied: every
187
+ * field the server needs is a window `(…At, …Length)` into `data`, the buffer
188
+ * the hello was read from, and the rest are numbers and flags. A field whose
189
+ * extension was absent keeps an empty window, and its `has*` flag says so,
190
+ * since an empty list and a missing extension mean different things to the
191
+ * server (§9.2). Make one per connection and read every hello into it: it
192
+ * allocates nothing after its constructor, so a handshake that keeps its state
193
+ * in a slot reads its ClientHellos without moving the arena.
194
+ */
195
+ export class TlsClientHelloView {
196
+ /** The buffer the windows point into: the one the last hello was read from. */
197
+ data: u8[]
198
+ /** 0 when the message parsed, or the alert it earns: `decode_error` or `illegal_parameter`. */
199
+ alert: i32 = 0
200
+ legacyVersion: i32 = 0
201
+ randomAt: i32 = 0
202
+ /** `legacy_session_id`, which a server echoes (§4.1.3). */
203
+ sessionIdAt: i32 = 0
204
+ sessionIdLength: i32 = 0
205
+ /** `cipher_suites`, 16-bit values in the client's order. */
206
+ suitesAt: i32 = 0
207
+ suitesLength: i32 = 0
208
+ /** `supported_versions`, 16-bit values. */
209
+ versionsAt: i32 = 0
210
+ versionsLength: i32 = 0
211
+ /** `supported_groups`, 16-bit values. */
212
+ groupsAt: i32 = 0
213
+ groupsLength: i32 = 0
214
+ /** `signature_algorithms`, 16-bit values. */
215
+ schemesAt: i32 = 0
216
+ schemesLength: i32 = 0
217
+ /** The `client_shares` list of `key_share`, every entry in it. */
218
+ sharesAt: i32 = 0
219
+ sharesLength: i32 = 0
220
+ /** The x25519 share's key_exchange, as sent; its length is the server's to check. */
221
+ x25519ShareAt: i32 = 0
222
+ x25519ShareLength: i32 = 0
223
+ /** The `host_name` of `server_name`, or empty. */
224
+ serverNameAt: i32 = 0
225
+ serverNameLength: i32 = 0
226
+ /** ALPN's `protocol_name_list`, each name a length byte and its bytes (RFC 7301 §3.1). */
227
+ alpnAt: i32 = 0
228
+ alpnLength: i32 = 0
229
+ /** The client's `quic_transport_parameters`, opaque (RFC 9001 §8.2). */
230
+ quicAt: i32 = 0
231
+ quicLength: i32 = 0
232
+ /** The extensions block, every extension in it. */
233
+ extensionsAt: i32 = 0
234
+ extensionsLength: i32 = 0
235
+ /** Whether `legacy_compression_methods` is the single null method TLS 1.3 requires (§4.1.2). */
236
+ nullCompressionOnly: boolean = false
237
+ hasSupportedVersions: boolean = false
238
+ hasSupportedGroups: boolean = false
239
+ hasKeyShare: boolean = false
240
+ hasX25519Share: boolean = false
241
+ hasSignatureAlgorithms: boolean = false
242
+ hasServerName: boolean = false
243
+ hasAlpn: boolean = false
244
+ hasQuicTransportParameters: boolean = false
245
+ /** One bit per extension type read, for the duplicate check (§4.2); cleared by each read. */
246
+ extensionSeen: u32[]
247
+ /** One bit per group a share was offered for, for the duplicate check (§4.2.8). */
248
+ shareSeen: u32[]
249
+ /** The reader each read runs, kept so that a read allocates none. */
250
+ reader: TlsReader
251
+
252
+ constructor() {
253
+ this.data = []
254
+ this.extensionSeen = new Array<u32>(TLS_U16_BITMAP_WORDS)
255
+ this.shareSeen = new Array<u32>(TLS_U16_BITMAP_WORDS)
256
+ this.reader = new TlsReader(this.data, TLS_CODEC_FROM, TLS_CODEC_FROM)
257
+ }
258
+
259
+ /** Forgets the last hello: every window empty, every flag down, both bitmaps clear. */
260
+ clear(): void {
261
+ this.alert = 0
262
+ this.legacyVersion = 0
263
+ this.randomAt = 0
264
+ this.sessionIdAt = 0
265
+ this.sessionIdLength = 0
266
+ this.suitesAt = 0
267
+ this.suitesLength = 0
268
+ this.versionsAt = 0
269
+ this.versionsLength = 0
270
+ this.groupsAt = 0
271
+ this.groupsLength = 0
272
+ this.schemesAt = 0
273
+ this.schemesLength = 0
274
+ this.sharesAt = 0
275
+ this.sharesLength = 0
276
+ this.x25519ShareAt = 0
277
+ this.x25519ShareLength = 0
278
+ this.serverNameAt = 0
279
+ this.serverNameLength = 0
280
+ this.alpnAt = 0
281
+ this.alpnLength = 0
282
+ this.quicAt = 0
283
+ this.quicLength = 0
284
+ this.extensionsAt = 0
285
+ this.extensionsLength = 0
286
+ this.nullCompressionOnly = false
287
+ this.hasSupportedVersions = false
288
+ this.hasSupportedGroups = false
289
+ this.hasKeyShare = false
290
+ this.hasX25519Share = false
291
+ this.hasSignatureAlgorithms = false
292
+ this.hasServerName = false
293
+ this.hasAlpn = false
294
+ this.hasQuicTransportParameters = false
295
+ for (let k: i32 = 0; k < toI32(this.extensionSeen.length); k++) {
296
+ this.extensionSeen[k] = toU32(0)
297
+ }
298
+ for (let k: i32 = 0; k < toI32(this.shareSeen.length); k++) {
299
+ this.shareSeen[k] = toU32(0)
300
+ }
301
+ }
302
+ }
303
+
304
+ /** Enough 32-bit words for one bit per 16-bit value. */
305
+ const TLS_U16_BITMAP_WORDS: i32 = 2048
306
+
307
+ /** Sets `value`'s bit in `bits` and answers whether it was already set: the duplicate checks of §4.2 and §4.2.8. */
308
+ const tlsMarkSeen = (bits: u32[], value: i32): boolean => {
309
+ const word: i32 = (value >> 5) & (TLS_U16_BITMAP_WORDS - 1)
310
+ const bit: u32 = toU32(1) << toU32(value & 31)
311
+ if (word < 0 || word >= toI32(bits.length)) {
312
+ return false
313
+ }
314
+ const seen: boolean = (bits[word] & bit) !== toU32(0)
315
+ bits[word] = bits[word] | bit
316
+ return seen
317
+ }
318
+
319
+ /** Records `alert` on `hello` unless an earlier one is there: the first fault is the one reported. */
320
+ const tlsHelloRefuse = (hello: TlsClientHelloView, alert: i32): void => {
321
+ if (hello.alert === 0) {
322
+ hello.alert = alert
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Reads a vector of 16-bit values (a `width`-byte length within `[min, max]`)
328
+ * and answers where it ends; an odd byte count fails the reader. The values
329
+ * are the window from `r.at` before the call to the answer.
330
+ */
331
+ const tlsReadU16Vector = (r: TlsReader, width: i32, min: i32, max: i32): i32 => {
332
+ const end: i32 = tlsReadVector(r, width, min, max)
333
+ if ((end - r.at) % 2 !== 0) {
334
+ r.failed = true
335
+ return r.at
336
+ }
337
+ tlsReadSkipTo(r, end)
338
+ return end
339
+ }
340
+
341
+ /** Whether `list` holds `value`. */
342
+ export const tlsListHas = (list: i32[], value: i32): boolean => list.indexOf(value) >= 0
343
+
344
+ /** Whether the 16-bit values `data[at .. at + length)` hold `value`. */
345
+ export const tlsWindowHasU16 = (data: u8[], at: i32, length: i32, value: i32): boolean => {
346
+ for (
347
+ let k: i32 = at;
348
+ k + 1 < at + length && k >= 0 && k < toI32(data.length) && k + 1 < toI32(data.length);
349
+ k += 2
350
+ ) {
351
+ if (((toI32(data[k]) << 8) | toI32(data[k + 1])) === value) {
352
+ return true
353
+ }
354
+ }
355
+ return false
356
+ }
357
+
358
+ /**
359
+ * `server_name` (RFC 6066 §3): a list of names, of which a `host_name` (type
360
+ * 0) is kept. A second `host_name` is `illegal_parameter`, as §3 says. So is a
361
+ * host name with a byte outside printable ASCII, because a DNS host name sent
362
+ * here is ASCII (an internationalised one in its A-label form) and the server
363
+ * exposes it as a string.
364
+ */
365
+ const tlsReadServerName = (r: TlsReader, hello: TlsClientHelloView): void => {
366
+ const listEnd: i32 = tlsReadVector(r, 2, 1, 65535)
367
+ // The list's own end bounds every entry in it (see `tlsReadList`).
368
+ const outerEnd: i32 = tlsReadList(r, listEnd)
369
+ while (!r.failed && r.at < listEnd) {
370
+ const nameType: i32 = tlsReadU8(r)
371
+ const nameEnd: i32 = tlsReadVector(r, 2, 1, 65535)
372
+ if (nameType === 0) {
373
+ if (hello.hasServerName) {
374
+ tlsHelloRefuse(hello, TLS_ALERT_ILLEGAL_PARAMETER)
375
+ }
376
+ hello.hasServerName = true
377
+ hello.serverNameAt = r.at
378
+ hello.serverNameLength = nameEnd - r.at
379
+ while (!r.failed && r.at < nameEnd) {
380
+ const c: i32 = tlsReadU8(r)
381
+ if (c < 0x21 || c > 0x7e) {
382
+ tlsHelloRefuse(hello, TLS_ALERT_ILLEGAL_PARAMETER)
383
+ }
384
+ }
385
+ }
386
+ tlsReadSkipTo(r, nameEnd)
387
+ }
388
+ r.end = outerEnd
389
+ }
390
+
391
+ /**
392
+ * `key_share` (RFC 8446 §4.2.8): the client's shares, each a group and its
393
+ * key_exchange<1..2^16-1>. Two shares for one group are `illegal_parameter`
394
+ * (§4.2.8, "Clients MUST NOT offer multiple KeyShareEntry values for the same
395
+ * group").
396
+ */
397
+ const tlsReadKeyShare = (r: TlsReader, hello: TlsClientHelloView): void => {
398
+ hello.hasKeyShare = true
399
+ const listEnd: i32 = tlsReadVector(r, 2, 0, 65535)
400
+ hello.sharesAt = r.at
401
+ hello.sharesLength = listEnd - r.at
402
+ // The list's own end bounds every entry in it (see `tlsReadList`).
403
+ const outerEnd: i32 = tlsReadList(r, listEnd)
404
+ while (!r.failed && r.at < listEnd) {
405
+ const group: i32 = tlsReadU16(r)
406
+ const shareEnd: i32 = tlsReadVector(r, 2, 1, 65535)
407
+ if (tlsMarkSeen(hello.shareSeen, group)) {
408
+ tlsHelloRefuse(hello, TLS_ALERT_ILLEGAL_PARAMETER)
409
+ }
410
+ if (group === TLS_GROUP_X25519 && !r.failed) {
411
+ hello.hasX25519Share = true
412
+ hello.x25519ShareAt = r.at
413
+ hello.x25519ShareLength = shareEnd - r.at
414
+ }
415
+ tlsReadSkipTo(r, shareEnd)
416
+ }
417
+ r.end = outerEnd
418
+ }
419
+
420
+ /** `application_layer_protocol_negotiation` (RFC 7301 §3.1): ProtocolName<1..2^8-1> names in a list<2..2^16-1>. */
421
+ const tlsReadAlpn = (r: TlsReader, hello: TlsClientHelloView): void => {
422
+ hello.hasAlpn = true
423
+ const listEnd: i32 = tlsReadVector(r, 2, 2, 65535)
424
+ hello.alpnAt = r.at
425
+ hello.alpnLength = listEnd - r.at
426
+ // The list's own end bounds every entry in it (see `tlsReadList`).
427
+ const outerEnd: i32 = tlsReadList(r, listEnd)
428
+ while (!r.failed && r.at < listEnd) {
429
+ const nameEnd: i32 = tlsReadVector(r, 1, 1, 255)
430
+ tlsReadSkipTo(r, nameEnd)
431
+ }
432
+ r.end = outerEnd
433
+ }
434
+
435
+ /** Reads one extension body, `r.at .. end`, of type `type`; an unknown type is skipped. */
436
+ const tlsReadExtension = (r: TlsReader, type: i32, end: i32, hello: TlsClientHelloView): void => {
437
+ switch (type) {
438
+ case TLS_EXT_SERVER_NAME:
439
+ tlsReadServerName(r, hello)
440
+ break
441
+ case TLS_EXT_SUPPORTED_GROUPS:
442
+ hello.hasSupportedGroups = true
443
+ hello.groupsAt = r.at + 2
444
+ hello.groupsLength = tlsReadU16Vector(r, 2, 2, 65535) - hello.groupsAt
445
+ break
446
+ case TLS_EXT_SIGNATURE_ALGORITHMS:
447
+ hello.hasSignatureAlgorithms = true
448
+ hello.schemesAt = r.at + 2
449
+ hello.schemesLength = tlsReadU16Vector(r, 2, 2, 65534) - hello.schemesAt
450
+ break
451
+ case TLS_EXT_ALPN:
452
+ tlsReadAlpn(r, hello)
453
+ break
454
+ case TLS_EXT_SUPPORTED_VERSIONS:
455
+ hello.hasSupportedVersions = true
456
+ hello.versionsAt = r.at + 1
457
+ hello.versionsLength = tlsReadU16Vector(r, 1, 2, 254) - hello.versionsAt
458
+ break
459
+ case TLS_EXT_KEY_SHARE:
460
+ tlsReadKeyShare(r, hello)
461
+ break
462
+ case TLS_EXT_QUIC_TRANSPORT_PARAMETERS:
463
+ hello.hasQuicTransportParameters = true
464
+ hello.quicAt = r.at
465
+ hello.quicLength = end - r.at
466
+ tlsReadSkipTo(r, end)
467
+ break
468
+ default:
469
+ tlsReadSkipTo(r, end)
470
+ break
471
+ }
472
+ // Every reader above stops where its own vectors end, and none can pass
473
+ // the extension's end, which is the reader's while it runs; an extension
474
+ // body with bytes left after them is malformed (RFC 8446 §4.2), so this is
475
+ // where trailing bytes are refused.
476
+ if (r.at !== end) {
477
+ r.failed = true
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Reads the ClientHello body `data[off .. off + len)` — the message without
483
+ * its four-byte handshake header — into `hello`, which forgets the last one
484
+ * first. Its `alert` is 0 when the body parsed; otherwise the first fault
485
+ * decides it: `decode_error` for a length past its bytes, a vector out of its
486
+ * bounds or bytes left over, and `illegal_parameter` for an extension sent
487
+ * twice (§4.2), a `pre_shared_key` that is not the last extension (§4.2.11),
488
+ * two shares for one group, or a bad `server_name`. A window outside `data`
489
+ * is `decode_error` too, rather than a panic. Nothing is copied: the windows
490
+ * point into `data`, which must outlive the use made of them.
491
+ */
492
+ export const tlsReadClientHello = (hello: TlsClientHelloView, data: u8[], off: i32, len: i32): void => {
493
+ hello.clear()
494
+ hello.data = data
495
+ if (off < 0 || len < 0 || off > toI32(data.length) - len) {
496
+ hello.alert = TLS_ALERT_DECODE_ERROR
497
+ return
498
+ }
499
+ const end: i32 = off + len
500
+ const r: TlsReader = hello.reader
501
+ r.data = data
502
+ r.at = off
503
+ r.end = end
504
+ r.failed = false
505
+ hello.legacyVersion = tlsReadU16(r)
506
+ hello.randomAt = r.at
507
+ tlsReadSkipTo(r, r.at + TLS_RANDOM_SIZE)
508
+ const sessionEnd: i32 = tlsReadVector(r, 1, 0, 32)
509
+ hello.sessionIdAt = r.at
510
+ hello.sessionIdLength = sessionEnd - r.at
511
+ tlsReadSkipTo(r, sessionEnd)
512
+ hello.suitesAt = r.at + 2
513
+ hello.suitesLength = tlsReadU16Vector(r, 2, 2, 65534) - hello.suitesAt
514
+ const compressionEnd: i32 = tlsReadVector(r, 1, 1, 255)
515
+ hello.nullCompressionOnly = compressionEnd - r.at === 1 && tlsReadU8(r) === 0
516
+ tlsReadSkipTo(r, compressionEnd)
517
+ // A hello with no extensions block is well formed (§4.1.2 keeps it for
518
+ // TLS 1.2 and earlier); it offers no TLS 1.3, which the server refuses.
519
+ if (!r.failed && r.at < end) {
520
+ const extensionsEnd: i32 = tlsReadVector(r, 2, 0, 65535)
521
+ hello.extensionsAt = r.at
522
+ hello.extensionsLength = extensionsEnd - r.at
523
+ // The extensions vector's own end bounds every extension in it: one whose
524
+ // length runs past it is malformed even when the message's length still
525
+ // balances.
526
+ const messageEnd: i32 = tlsReadList(r, extensionsEnd)
527
+ while (!r.failed && r.at < extensionsEnd) {
528
+ const type: i32 = tlsReadU16(r)
529
+ const bodyEnd: i32 = tlsReadVector(r, 2, 0, 65535)
530
+ if (r.failed) {
531
+ break
532
+ }
533
+ if (tlsMarkSeen(hello.extensionSeen, type)) {
534
+ tlsHelloRefuse(hello, TLS_ALERT_ILLEGAL_PARAMETER)
535
+ }
536
+ // §4.2.11: `pre_shared_key` must be the last extension, and a server
537
+ // checks that even though it does not take the PSK.
538
+ if (type === TLS_EXT_PRE_SHARED_KEY && bodyEnd !== extensionsEnd) {
539
+ tlsHelloRefuse(hello, TLS_ALERT_ILLEGAL_PARAMETER)
540
+ }
541
+ // The extension's own end bounds every read inside it, so a vector in
542
+ // one extension cannot run on into the next.
543
+ const outerEnd: i32 = tlsReadList(r, bodyEnd)
544
+ tlsReadExtension(r, type, bodyEnd, hello)
545
+ r.end = outerEnd
546
+ }
547
+ r.end = messageEnd
548
+ }
549
+ if (r.failed || r.at !== end) {
550
+ tlsHelloRefuse(hello, TLS_ALERT_DECODE_ERROR)
551
+ }
552
+ }
553
+
554
+ /**
555
+ * A ClientHello as `tlsParseClientHello` read it (RFC 8446 §4.1.2), each field
556
+ * copied out of the message into an array of its own: the same hello as a
557
+ * `TlsClientHelloView`, for a caller that wants values rather than windows. A
558
+ * field whose extension was absent keeps its empty value, and its `has*` flag
559
+ * says so.
560
+ */
561
+ export class TlsClientHello {
562
+ random: u8[]
563
+ /** `legacy_session_id`, which a server echoes (§4.1.3). */
564
+ sessionId: u8[]
565
+ cipherSuites: i32[]
566
+ /** `supported_versions`, in the client's order. */
567
+ versions: i32[]
568
+ /** `supported_groups`, in the client's order. */
569
+ groups: i32[]
570
+ /** Every group a key share was offered for, in order. */
571
+ shareGroups: i32[]
572
+ /** The x25519 share's key_exchange, as sent; its length is the server's to check. */
573
+ x25519Share: u8[]
574
+ /** `signature_algorithms`, in the client's order. */
575
+ signatureSchemes: i32[]
576
+ /** The `host_name` of `server_name`, or empty. */
577
+ serverName: string = ""
578
+ /** The ALPN protocol names offered, each as its bytes (RFC 7301 §3.1). */
579
+ alpn: u8[][]
580
+ /** The client's `quic_transport_parameters`, opaque (RFC 9001 §8.2). */
581
+ quicTransportParameters: u8[]
582
+ /** Every extension type read, in order. */
583
+ extensionTypes: i32[]
584
+ /** 0 when the message parsed, or the alert it earns: `decode_error` or `illegal_parameter`. */
585
+ alert: i32 = 0
586
+ legacyVersion: i32 = 0
587
+ /** Whether `legacy_compression_methods` is the single null method TLS 1.3 requires (§4.1.2). */
588
+ nullCompressionOnly: boolean = false
589
+ hasSupportedVersions: boolean = false
590
+ hasSupportedGroups: boolean = false
591
+ hasKeyShare: boolean = false
592
+ hasX25519Share: boolean = false
593
+ hasSignatureAlgorithms: boolean = false
594
+ hasServerName: boolean = false
595
+ hasAlpn: boolean = false
596
+ hasQuicTransportParameters: boolean = false
597
+
598
+ constructor() {
599
+ this.random = []
600
+ this.sessionId = []
601
+ this.cipherSuites = []
602
+ this.versions = []
603
+ this.groups = []
604
+ this.shareGroups = []
605
+ this.x25519Share = []
606
+ this.signatureSchemes = []
607
+ this.alpn = []
608
+ this.quicTransportParameters = []
609
+ this.extensionTypes = []
610
+ }
611
+ }
612
+
613
+ /** A copy of `data[at .. at + length)`. */
614
+ const tlsCopyWindow = (data: u8[], at: i32, length: i32): u8[] => {
615
+ const out: u8[] = new Array<u8>(length < 0 ? TLS_CODEC_FROM : length)
616
+ for (let k: i32 = 0; k < toI32(out.length) && at + k >= 0 && at + k < toI32(data.length); k++) {
617
+ out[k] = data[at + k]
618
+ }
619
+ return out
620
+ }
621
+
622
+ /** The 16-bit values of `data[at .. at + length)`, in a fresh list. */
623
+ const tlsCopyU16Window = (data: u8[], at: i32, length: i32): i32[] => {
624
+ const out: i32[] = []
625
+ for (
626
+ let k: i32 = at;
627
+ k + 1 < at + length && k >= 0 && k < toI32(data.length) && k + 1 < toI32(data.length);
628
+ k += 2
629
+ ) {
630
+ out.push((toI32(data[k]) << 8) | toI32(data[k + 1]))
631
+ }
632
+ return out
633
+ }
634
+
635
+ /** The bytes of `data[at .. at + length)` as a string of one character a byte: ASCII here, a host name. */
636
+ export const tlsWindowString = (data: u8[], at: i32, length: i32): string => {
637
+ const parts: string[] = []
638
+ for (let k: i32 = at; k < at + length && k >= 0 && k < toI32(data.length); k++) {
639
+ parts.push(String.fromCharCode(toI32(data[k])))
640
+ }
641
+ return parts.join("")
642
+ }
643
+
644
+ /**
645
+ * Reads the ClientHello body `data[off .. off + len)` — the message without
646
+ * its four-byte handshake header — into a fresh `TlsClientHello`, with
647
+ * `tlsReadClientHello`'s checks and its `alert`, every field copied out.
648
+ */
649
+ export const tlsParseClientHello = (data: u8[], off: i32, len: i32): TlsClientHello => {
650
+ const view = new TlsClientHelloView()
651
+ tlsReadClientHello(view, data, off, len)
652
+ const hello = new TlsClientHello()
653
+ hello.alert = view.alert
654
+ if (off < 0 || len < 0 || off > toI32(data.length) - len) {
655
+ return hello
656
+ }
657
+ hello.legacyVersion = view.legacyVersion
658
+ hello.random = tlsCopyWindow(data, view.randomAt, TLS_RANDOM_SIZE)
659
+ hello.sessionId = tlsCopyWindow(data, view.sessionIdAt, view.sessionIdLength)
660
+ hello.cipherSuites = tlsCopyU16Window(data, view.suitesAt, view.suitesLength)
661
+ hello.versions = tlsCopyU16Window(data, view.versionsAt, view.versionsLength)
662
+ hello.groups = tlsCopyU16Window(data, view.groupsAt, view.groupsLength)
663
+ hello.signatureSchemes = tlsCopyU16Window(data, view.schemesAt, view.schemesLength)
664
+ hello.x25519Share = tlsCopyWindow(data, view.x25519ShareAt, view.x25519ShareLength)
665
+ hello.serverName = tlsWindowString(data, view.serverNameAt, view.serverNameLength)
666
+ hello.quicTransportParameters = tlsCopyWindow(data, view.quicAt, view.quicLength)
667
+ // The key shares, ALPN names and extension types, walked again; the read
668
+ // above has checked every length these loops follow.
669
+ let at: i32 = view.sharesAt
670
+ while (
671
+ at + 4 <= view.sharesAt + view.sharesLength &&
672
+ at >= 0 &&
673
+ at < toI32(data.length) &&
674
+ at + 3 < toI32(data.length)
675
+ ) {
676
+ hello.shareGroups.push((toI32(data[at]) << 8) | toI32(data[at + 1]))
677
+ at = at + 4 + ((toI32(data[at + 2]) << 8) | toI32(data[at + 3]))
678
+ }
679
+ at = view.alpnAt
680
+ while (at < view.alpnAt + view.alpnLength && at >= 0 && at < toI32(data.length)) {
681
+ const n: i32 = toI32(data[at])
682
+ hello.alpn.push(tlsCopyWindow(data, at + 1, n))
683
+ at = at + 1 + n
684
+ }
685
+ at = view.extensionsAt
686
+ while (
687
+ at + 4 <= view.extensionsAt + view.extensionsLength &&
688
+ at >= 0 &&
689
+ at < toI32(data.length) &&
690
+ at + 3 < toI32(data.length)
691
+ ) {
692
+ hello.extensionTypes.push((toI32(data[at]) << 8) | toI32(data[at + 1]))
693
+ at = at + 4 + ((toI32(data[at + 2]) << 8) | toI32(data[at + 3]))
694
+ }
695
+ hello.nullCompressionOnly = view.nullCompressionOnly
696
+ hello.hasSupportedVersions = view.hasSupportedVersions
697
+ hello.hasSupportedGroups = view.hasSupportedGroups
698
+ hello.hasKeyShare = view.hasKeyShare
699
+ hello.hasX25519Share = view.hasX25519Share
700
+ hello.hasSignatureAlgorithms = view.hasSignatureAlgorithms
701
+ hello.hasServerName = view.hasServerName
702
+ hello.hasAlpn = view.hasAlpn
703
+ hello.hasQuicTransportParameters = view.hasQuicTransportParameters
704
+ return hello
705
+ }
706
+
707
+ /**
708
+ * The writers below put a handshake message into the caller's array at an
709
+ * offset and answer where it ends. A byte that would land outside the array
710
+ * is dropped rather than written, and the answer is still where the message
711
+ * ends, so a writer run over an empty array is how long its message is: the
712
+ * `tlsEncode*` functions measure that way, allocate the message's array and
713
+ * write it again, and a server that keeps its output in a buffer of its own
714
+ * makes room first and writes in place. The `tlsPut*` helpers are the same
715
+ * shape: they take the position and answer the next.
716
+ */
717
+ const tlsPutU8 = (out: u8[], at: i32, v: i32): i32 => {
718
+ if (at >= 0 && at < toI32(out.length)) {
719
+ out[at] = toU8(v & 255)
720
+ }
721
+ return at + 1
722
+ }
723
+
724
+ const tlsPutU16 = (out: u8[], at: i32, v: i32): i32 => tlsPutU8(out, tlsPutU8(out, at, v >> 8), v)
725
+
726
+ /** `bytes[off .. off + len)`, at `out[at]`. */
727
+ const tlsPutWindow = (out: u8[], at: i32, bytes: u8[], off: i32, len: i32): i32 => {
728
+ // Measuring over an empty array: nothing would land, so nothing is copied.
729
+ if (toI32(out.length) === 0) {
730
+ return at + len
731
+ }
732
+ for (let k: i32 = 0; k < len && off + k >= 0 && off + k < toI32(bytes.length); k++) {
733
+ tlsPutU8(out, at + k, toI32(bytes[off + k]))
734
+ }
735
+ return at + len
736
+ }
737
+
738
+ const tlsPutBytes = (out: u8[], at: i32, b: u8[]): i32 =>
739
+ tlsPutWindow(out, at, b, TLS_CODEC_FROM, toI32(b.length))
740
+
741
+ /** The bytes of `text`, one a character: ASCII here, an ALPN protocol id. */
742
+ const tlsPutAscii = (out: u8[], at: i32, text: string): i32 => {
743
+ const length: i32 = toI32(text.length)
744
+ for (let k: i32 = 0; k < length; k++) {
745
+ tlsPutU8(out, at + k, toI32(text.charCodeAt(k)))
746
+ }
747
+ return at + length
748
+ }
749
+
750
+ /**
751
+ * Writes the `width`-byte length of the vector whose contents run from
752
+ * `start` to `end`, into the `width` bytes before `start` that the caller
753
+ * left for it.
754
+ */
755
+ const tlsPutLength = (out: u8[], start: i32, end: i32, width: i32): void => {
756
+ let n: i32 = end - start
757
+ for (let at: i32 = start - 1; at >= start - width; at--) {
758
+ tlsPutU8(out, at, n)
759
+ n = n >> 8
760
+ }
761
+ }
762
+
763
+ /** The handshake header (§4): `type`, then a 24-bit length of the body that follows it to `end`. */
764
+ const tlsPutHeader = (out: u8[], at: i32, type: i32, end: i32): void => {
765
+ tlsPutU8(out, at, type)
766
+ tlsPutLength(out, at + 4, end, 3)
767
+ }
768
+
769
+ /** The fixed `random` that marks a ServerHello as a HelloRetryRequest: SHA-256 of "HelloRetryRequest" (§4.1.3). */
770
+ const TLS_HELLO_RETRY_RANDOM: string = "cf21ad74e59a6111be1d8c021e65b891c2a211167abb8c5e079e09e2c8a8339c"
771
+
772
+ /** The value of one lowercase hex digit. */
773
+ const tlsHexNibble = (c: i32): i32 => (c <= 57 ? c - 48 : c - 87)
774
+
775
+ /**
776
+ * Writes the bytes the lowercase hex constant `text` spells at `out[at]` and
777
+ * answers where they end: how this stack's fixed values, written out where
778
+ * they are defined, become bytes without an array of their own.
779
+ */
780
+ export const tlsHexInto = (text: string, out: u8[], at: i32): i32 => {
781
+ const length: i32 = toI32(text.length) >> 1
782
+ for (let k: i32 = 0; k < length; k++) {
783
+ const hi: i32 = tlsHexNibble(toI32(text.charCodeAt(2 * k)))
784
+ const lo: i32 = tlsHexNibble(toI32(text.charCodeAt(2 * k + 1)))
785
+ tlsPutU8(out, at + k, (hi << 4) | lo)
786
+ }
787
+ return at + length
788
+ }
789
+
790
+ /** The `supported_versions` a server hello carries: TLS 1.3 alone (§4.2.1). */
791
+ const tlsPutSelectedVersion = (out: u8[], at: i32): i32 =>
792
+ tlsPutU16(out, tlsPutU16(out, tlsPutU16(out, at, TLS_EXT_SUPPORTED_VERSIONS), 2), TLS_VERSION_13)
793
+
794
+ /**
795
+ * What a ServerHello and a HelloRetryRequest open with (§4.1.3), after the
796
+ * header: the legacy version, the random (`retry` for the HelloRetryRequest's
797
+ * fixed one), the echoed session id, the suite and null compression.
798
+ */
799
+ const tlsPutHelloHead = (
800
+ out: u8[],
801
+ at: i32,
802
+ random: u8[],
803
+ retry: boolean,
804
+ sessionId: u8[],
805
+ sessionIdOff: i32,
806
+ sessionIdLength: i32,
807
+ suite: i32
808
+ ): i32 => {
809
+ let p: i32 = tlsPutU16(out, at, TLS_LEGACY_VERSION)
810
+ p = retry ? tlsHexInto(TLS_HELLO_RETRY_RANDOM, out, p) : tlsPutBytes(out, p, random)
811
+ p = tlsPutU8(out, p, sessionIdLength)
812
+ p = tlsPutWindow(out, p, sessionId, sessionIdOff, sessionIdLength)
813
+ p = tlsPutU16(out, p, suite)
814
+ return tlsPutU8(out, p, 0)
815
+ }
816
+
817
+ /**
818
+ * Writes a ServerHello (§4.1.3) at `out[at]` and answers where it ends:
819
+ * choosing `suite` and TLS 1.3, echoing the session id
820
+ * `sessionId[sessionIdOff .. sessionIdOff + sessionIdLength)`, and carrying
821
+ * the server's x25519 share. The extensions are `key_share` then
822
+ * `supported_versions`, the order RFC 8448 §3 shows.
823
+ */
824
+ export const tlsWriteServerHello = (
825
+ out: u8[],
826
+ at: i32,
827
+ random: u8[],
828
+ sessionId: u8[],
829
+ sessionIdOff: i32,
830
+ sessionIdLength: i32,
831
+ suite: i32,
832
+ x25519Public: u8[]
833
+ ): i32 => {
834
+ let p: i32 = tlsPutHelloHead(out, at + 4, random, false, sessionId, sessionIdOff, sessionIdLength, suite)
835
+ const extensions: i32 = p + 2
836
+ p = tlsPutU16(out, extensions, TLS_EXT_KEY_SHARE)
837
+ const share: i32 = p + 2
838
+ p = tlsPutU16(out, share, TLS_GROUP_X25519)
839
+ const key: i32 = p + 2
840
+ p = tlsPutBytes(out, key, x25519Public)
841
+ tlsPutLength(out, key, p, 2)
842
+ tlsPutLength(out, share, p, 2)
843
+ p = tlsPutSelectedVersion(out, p)
844
+ tlsPutLength(out, extensions, p, 2)
845
+ tlsPutHeader(out, at, TLS_HANDSHAKE_SERVER_HELLO, p)
846
+ return p
847
+ }
848
+
849
+ /**
850
+ * Writes a HelloRetryRequest (§4.1.4) at `out[at]` and answers where it ends:
851
+ * a ServerHello whose `random` is the fixed HelloRetryRequest value, choosing
852
+ * `suite`, echoing the session id, and asking for a share of `group` in
853
+ * `key_share`. No cookie is sent.
854
+ */
855
+ export const tlsWriteHelloRetryRequest = (
856
+ out: u8[],
857
+ at: i32,
858
+ sessionId: u8[],
859
+ sessionIdOff: i32,
860
+ sessionIdLength: i32,
861
+ suite: i32,
862
+ group: i32
863
+ ): i32 => {
864
+ const none: u8[] = []
865
+ let p: i32 = tlsPutHelloHead(out, at + 4, none, true, sessionId, sessionIdOff, sessionIdLength, suite)
866
+ const extensions: i32 = p + 2
867
+ p = tlsPutU16(out, extensions, TLS_EXT_KEY_SHARE)
868
+ p = tlsPutU16(out, p, 2)
869
+ p = tlsPutU16(out, p, group)
870
+ p = tlsPutSelectedVersion(out, p)
871
+ tlsPutLength(out, extensions, p, 2)
872
+ tlsPutHeader(out, at, TLS_HANDSHAKE_SERVER_HELLO, p)
873
+ return p
874
+ }
875
+
876
+ /**
877
+ * Writes EncryptedExtensions (§4.3.1) at `out[at]` and answers where it ends.
878
+ * `extra` is extensions the caller encodes itself, written first and as they
879
+ * are; then the chosen ALPN protocol when `alpn` is not empty (RFC 7301 §3.1,
880
+ * a list of exactly one), an empty `server_name` when `acknowledgeServerName`
881
+ * (RFC 6066 §3's acknowledgement), and the server's
882
+ * `quic_transport_parameters` when `quicTransportParameters` is not `null`.
883
+ */
884
+ export const tlsWriteEncryptedExtensions = (
885
+ out: u8[],
886
+ at: i32,
887
+ extra: u8[],
888
+ alpn: string,
889
+ acknowledgeServerName: boolean,
890
+ quicTransportParameters: u8[] | null
891
+ ): i32 => {
892
+ const extensions: i32 = at + 6
893
+ let p: i32 = tlsPutBytes(out, extensions, extra)
894
+ if (toI32(alpn.length) > 0) {
895
+ p = tlsPutU16(out, p, TLS_EXT_ALPN)
896
+ const body: i32 = p + 2
897
+ const list: i32 = body + 2
898
+ const name: i32 = list + 1
899
+ p = tlsPutAscii(out, name, alpn)
900
+ tlsPutLength(out, name, p, 1)
901
+ tlsPutLength(out, list, p, 2)
902
+ tlsPutLength(out, body, p, 2)
903
+ }
904
+ if (acknowledgeServerName) {
905
+ p = tlsPutU16(out, tlsPutU16(out, p, TLS_EXT_SERVER_NAME), 0)
906
+ }
907
+ if (quicTransportParameters !== null) {
908
+ p = tlsPutU16(out, p, TLS_EXT_QUIC_TRANSPORT_PARAMETERS)
909
+ const body: i32 = p + 2
910
+ p = tlsPutBytes(out, body, quicTransportParameters)
911
+ tlsPutLength(out, body, p, 2)
912
+ }
913
+ tlsPutLength(out, extensions, p, 2)
914
+ tlsPutHeader(out, at, TLS_HANDSHAKE_ENCRYPTED_EXTENSIONS, p)
915
+ return p
916
+ }
917
+
918
+ /**
919
+ * Writes a server's Certificate (§4.4.2) at `out[at]` and answers where it
920
+ * ends: an empty request context, then each DER certificate of `chain`, leaf
921
+ * first, with no extensions of its own.
922
+ */
923
+ export const tlsWriteCertificate = (out: u8[], at: i32, chain: u8[][]): i32 => {
924
+ const list: i32 = tlsPutU8(out, at + 4, 0) + 3
925
+ let p: i32 = list
926
+ for (const certificate of chain) {
927
+ const entry: i32 = p + 3
928
+ p = tlsPutBytes(out, entry, certificate)
929
+ tlsPutLength(out, entry, p, 3)
930
+ p = tlsPutU16(out, p, 0)
931
+ }
932
+ tlsPutLength(out, list, p, 3)
933
+ tlsPutHeader(out, at, TLS_HANDSHAKE_CERTIFICATE, p)
934
+ return p
935
+ }
936
+
937
+ /** Writes a CertificateVerify (§4.4.3) at `out[at]`: the signature scheme and the signature as it goes on the wire. */
938
+ export const tlsWriteCertificateVerify = (out: u8[], at: i32, scheme: i32, signature: u8[]): i32 => {
939
+ const body: i32 = tlsPutU16(out, at + 4, scheme) + 2
940
+ const p: i32 = tlsPutBytes(out, body, signature)
941
+ tlsPutLength(out, body, p, 2)
942
+ tlsPutHeader(out, at, TLS_HANDSHAKE_CERTIFICATE_VERIFY, p)
943
+ return p
944
+ }
945
+
946
+ /** Writes a Finished (§4.4.4) at `out[at]`, carrying `verifyData[off .. off + len)`. */
947
+ export const tlsWriteFinished = (out: u8[], at: i32, verifyData: u8[], off: i32, len: i32): i32 => {
948
+ const p: i32 = tlsPutWindow(out, at + 4, verifyData, off, len)
949
+ tlsPutHeader(out, at, TLS_HANDSHAKE_FINISHED, p)
950
+ return p
951
+ }
952
+
953
+ /**
954
+ * Writes what a server's CertificateVerify signs (§4.4.3) at `out[at]`: 64
955
+ * spaces, the context string "TLS 1.3, server CertificateVerify", a zero byte,
956
+ * and the transcript hash `transcriptHash[off .. off + len)` through
957
+ * Certificate.
958
+ */
959
+ export const tlsWriteCertificateVerifyContent = (
960
+ out: u8[],
961
+ at: i32,
962
+ transcriptHash: u8[],
963
+ off: i32,
964
+ len: i32
965
+ ): i32 => {
966
+ let p: i32 = at
967
+ for (let k: i32 = 0; k < 64; k++) {
968
+ p = tlsPutU8(out, p, 0x20)
969
+ }
970
+ p = tlsPutAscii(out, p, "TLS 1.3, server CertificateVerify")
971
+ p = tlsPutU8(out, p, 0)
972
+ return tlsPutWindow(out, p, transcriptHash, off, len)
973
+ }
974
+
975
+ /** The bytes of `name` as a string of one character a byte, which `tlsWriteEncryptedExtensions` writes back as they were. */
976
+ const tlsBytesString = (name: u8[]): string => tlsWindowString(name, TLS_CODEC_FROM, toI32(name.length))
977
+
978
+ /**
979
+ * A ServerHello (§4.1.3) choosing `suite` and TLS 1.3, echoing the client's
980
+ * `sessionId`, and carrying the server's x25519 share, in a fresh array
981
+ * (`tlsWriteServerHello`).
982
+ */
983
+ export const tlsEncodeServerHello = (random: u8[], sessionId: u8[], suite: i32, x25519Public: u8[]): u8[] => {
984
+ const none: u8[] = []
985
+ const sessionIdLength: i32 = toI32(sessionId.length)
986
+ const out: u8[] = new Array<u8>(
987
+ tlsWriteServerHello(
988
+ none,
989
+ TLS_CODEC_FROM,
990
+ random,
991
+ sessionId,
992
+ TLS_CODEC_FROM,
993
+ sessionIdLength,
994
+ suite,
995
+ x25519Public
996
+ )
997
+ )
998
+ tlsWriteServerHello(
999
+ out,
1000
+ TLS_CODEC_FROM,
1001
+ random,
1002
+ sessionId,
1003
+ TLS_CODEC_FROM,
1004
+ sessionIdLength,
1005
+ suite,
1006
+ x25519Public
1007
+ )
1008
+ return out
1009
+ }
1010
+
1011
+ /** A HelloRetryRequest (§4.1.4) choosing `suite`, echoing `sessionId` and asking for `group`, in a fresh array (`tlsWriteHelloRetryRequest`). */
1012
+ export const tlsEncodeHelloRetryRequest = (sessionId: u8[], suite: i32, group: i32): u8[] => {
1013
+ const none: u8[] = []
1014
+ const sessionIdLength: i32 = toI32(sessionId.length)
1015
+ const out: u8[] = new Array<u8>(
1016
+ tlsWriteHelloRetryRequest(none, TLS_CODEC_FROM, sessionId, TLS_CODEC_FROM, sessionIdLength, suite, group)
1017
+ )
1018
+ tlsWriteHelloRetryRequest(out, TLS_CODEC_FROM, sessionId, TLS_CODEC_FROM, sessionIdLength, suite, group)
1019
+ return out
1020
+ }
1021
+
1022
+ /**
1023
+ * EncryptedExtensions (§4.3.1) in a fresh array (`tlsWriteEncryptedExtensions`),
1024
+ * the ALPN protocol given as its bytes.
1025
+ */
1026
+ export const tlsEncodeEncryptedExtensions = (
1027
+ extra: u8[],
1028
+ alpn: u8[],
1029
+ acknowledgeServerName: boolean,
1030
+ quicTransportParameters: u8[] | null
1031
+ ): u8[] => {
1032
+ const none: u8[] = []
1033
+ const protocol: string = tlsBytesString(alpn)
1034
+ const out: u8[] = new Array<u8>(
1035
+ tlsWriteEncryptedExtensions(
1036
+ none,
1037
+ TLS_CODEC_FROM,
1038
+ extra,
1039
+ protocol,
1040
+ acknowledgeServerName,
1041
+ quicTransportParameters
1042
+ )
1043
+ )
1044
+ tlsWriteEncryptedExtensions(
1045
+ out,
1046
+ TLS_CODEC_FROM,
1047
+ extra,
1048
+ protocol,
1049
+ acknowledgeServerName,
1050
+ quicTransportParameters
1051
+ )
1052
+ return out
1053
+ }
1054
+
1055
+ /** A server's Certificate (§4.4.2) in a fresh array (`tlsWriteCertificate`). */
1056
+ export const tlsEncodeCertificate = (chain: u8[][]): u8[] => {
1057
+ const none: u8[] = []
1058
+ const out: u8[] = new Array<u8>(tlsWriteCertificate(none, TLS_CODEC_FROM, chain))
1059
+ tlsWriteCertificate(out, TLS_CODEC_FROM, chain)
1060
+ return out
1061
+ }
1062
+
1063
+ /** A CertificateVerify (§4.4.3) in a fresh array (`tlsWriteCertificateVerify`). */
1064
+ export const tlsEncodeCertificateVerify = (scheme: i32, signature: u8[]): u8[] => {
1065
+ const none: u8[] = []
1066
+ const out: u8[] = new Array<u8>(tlsWriteCertificateVerify(none, TLS_CODEC_FROM, scheme, signature))
1067
+ tlsWriteCertificateVerify(out, TLS_CODEC_FROM, scheme, signature)
1068
+ return out
1069
+ }
1070
+
1071
+ /** A Finished (§4.4.4) carrying `verifyData`, in a fresh array (`tlsWriteFinished`). */
1072
+ export const tlsEncodeFinished = (verifyData: u8[]): u8[] => {
1073
+ const none: u8[] = []
1074
+ const length: i32 = toI32(verifyData.length)
1075
+ const out: u8[] = new Array<u8>(tlsWriteFinished(none, TLS_CODEC_FROM, verifyData, TLS_CODEC_FROM, length))
1076
+ tlsWriteFinished(out, TLS_CODEC_FROM, verifyData, TLS_CODEC_FROM, length)
1077
+ return out
1078
+ }
1079
+
1080
+ /** What a server's CertificateVerify signs (§4.4.3), in a fresh array (`tlsWriteCertificateVerifyContent`). */
1081
+ export const tlsCertificateVerifyContent = (transcriptHash: u8[]): u8[] => {
1082
+ const none: u8[] = []
1083
+ const length: i32 = toI32(transcriptHash.length)
1084
+ const out: u8[] = new Array<u8>(
1085
+ tlsWriteCertificateVerifyContent(none, TLS_CODEC_FROM, transcriptHash, TLS_CODEC_FROM, length)
1086
+ )
1087
+ tlsWriteCertificateVerifyContent(out, TLS_CODEC_FROM, transcriptHash, TLS_CODEC_FROM, length)
1088
+ return out
1089
+ }