@amritk/nish-x86_64-linux 0.16.0 → 0.18.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 (58) hide show
  1. package/INSTALL.md +43 -34
  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 +50 -13
  6. package/runtime/nish.mjs +71 -7
  7. package/runtime/runtime-host.c +43 -24
  8. package/runtime/runtime-net.c +57 -10
  9. package/runtime/runtime-os.c +62 -20
  10. package/runtime/runtime-simd.c +203 -0
  11. package/runtime/runtime-wasm.c +12 -0
  12. package/runtime/runtime.c +66 -18
  13. package/runtime/shim.mjs +114 -5
  14. package/scripts/build.sh +14 -12
  15. package/std/README.md +168 -36
  16. package/std/crypto/aes.ts +51 -8
  17. package/std/crypto/base64url.ts +25 -12
  18. package/std/crypto/chacha20poly1305.ts +43 -29
  19. package/std/crypto/ct.ts +6 -1
  20. package/std/crypto/hkdf.ts +364 -3
  21. package/std/crypto/hmac.ts +307 -7
  22. package/std/crypto/p256.ts +618 -34
  23. package/std/crypto/sha1.ts +147 -0
  24. package/std/crypto/sha256.ts +3 -2
  25. package/std/crypto/x25519.ts +88 -36
  26. package/std/crypto/x509.ts +130 -47
  27. package/std/json.ts +297 -41
  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 +911 -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 +462 -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 +929 -0
  45. package/std/net/quic-packet.ts +1698 -0
  46. package/std/net/quic-recovery.ts +849 -0
  47. package/std/net/quic-stream.ts +1609 -0
  48. package/std/net/quic.ts +3578 -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 +414 -0
  54. package/std/net/tls.ts +1437 -0
  55. package/std/net/websocket.ts +823 -0
  56. package/std/net/webtransport.ts +1334 -0
  57. package/std/secret.ts +62 -0
  58. package/std/text.ts +77 -0
package/std/net/tls.ts ADDED
@@ -0,0 +1,1437 @@
1
+ /**
2
+ * `nish/net/tls` — the server side of a TLS 1.3 handshake (RFC 8446), from
3
+ * the client's first ClientHello to its Finished, carrier-agnostic and
4
+ * sans-IO.
5
+ *
6
+ * import { TlsServer, TLS_LEVEL_INITIAL } from "nish/net/tls";
7
+ *
8
+ * const server = new TlsServer(config, serverRandom, x25519Private);
9
+ * const alert: i32 = server.receive(TLS_LEVEL_INITIAL, bytes, 0, n);
10
+ * // …and for the slot's next connection:
11
+ * server.restart(nextRandom, nextPrivate);
12
+ * // send server.takeOutput(TLS_LEVEL_INITIAL), install the handshake
13
+ * // secrets, sign server.signatureInput(), call server.sign(signature), …
14
+ *
15
+ * **Carrier-agnostic.** The server reads and writes handshake *messages*, not
16
+ * records or packets. Bytes go in and come out tagged with the level they
17
+ * belong to — Initial (cleartext: ClientHello, ServerHello), Handshake
18
+ * (EncryptedExtensions through both Finished messages) or Application — and
19
+ * the secrets for each level are readable as soon as they are known. TLS over
20
+ * TCP (WP34 T2) wraps a level's bytes in records protected under that level's
21
+ * secret; QUIC (Q2) puts them in CRYPTO frames of the matching packet number
22
+ * space (RFC 9001 §4.1.3). Neither needs anything else from here.
23
+ *
24
+ * **Sans-IO.** Nothing here reads a socket, a clock or the random device. The
25
+ * server random and the ephemeral x25519 private key are constructor
26
+ * arguments, so a test injects RFC 8448's and a server draws them with
27
+ * `crypto.getRandomValues`. Signing is handed off the same way: once the
28
+ * Certificate is written the server stops at `TLS_STATE_WAIT_SIGNATURE`, the
29
+ * caller signs `signatureInput()` with its own key (`tlsSignEcdsaP256` for a
30
+ * P-256 key) and hands the signature to `sign`. That keeps the private key out
31
+ * of this module, and it lets RFC 8448's trace, which signs with RSA-PSS, be
32
+ * replayed with its own signature.
33
+ *
34
+ * **What is negotiated.** TLS 1.3 only. The suites `TLS_AES_128_GCM_SHA256`,
35
+ * `TLS_CHACHA20_POLY1305_SHA256` and `TLS_AES_256_GCM_SHA384`, the first of
36
+ * them the client lists; the group x25519; the configuration's one signature
37
+ * scheme. A client that offers x25519 in `supported_groups` without a share
38
+ * gets one HelloRetryRequest (§4.1.4), and a second ClientHello still without
39
+ * the share is refused. `server_name` is read and exposed as `serverName`.
40
+ * ALPN picks the first of the configuration's protocols the client offers.
41
+ * `quic_transport_parameters` is read and written only when the configuration
42
+ * says the carrier is QUIC. PSKs, 0-RTT, NewSessionTicket and client
43
+ * authentication are not here: a PSK or early-data offer is ignored, so the
44
+ * client falls back to a full handshake.
45
+ *
46
+ * **Refusals are alerts.** `receive` and `sign` answer 0 or a TLS alert
47
+ * description (`TLS_ALERT_*` in `nish/net/tls/codec`); after one the server
48
+ * is in `TLS_STATE_FAILED` and answers the same alert to every call. Nothing
49
+ * the peer sends can make it panic.
50
+ *
51
+ * **Secrets.** The ECDHE secret, and the copy of the ephemeral key it is
52
+ * computed with, are `Secret`s (`nish:secret`) wiped on every path. What the
53
+ * server keeps in its fields — the caller's ephemeral key bytes, since a
54
+ * `Secret` may not be a field, and the handshake, traffic and exporter secrets
55
+ * its carrier reads — is wiped by its carrier, and by `restart` before the
56
+ * next connection uses the arrays (CLAUDE.md §Security; TLS-1 in
57
+ * `docs/security/tls.md`).
58
+ *
59
+ * **Memory.** A `TlsServer` keeps its state in buffers it allocates once —
60
+ * the messages in and out, the transcript, the secrets, the HKDF scratch —
61
+ * and `restart` hands them to the next connection, so a carrier that keeps
62
+ * one per slot runs every handshake after the first without moving the arena
63
+ * (TLS-3; `tests/link/net_tls_memory`). What a message allocates on the way
64
+ * dies inside a `using a = arena()` block or an arena scope of its own.
65
+ *
66
+ * The state is a class and a `switch`, with no closures. Written from RFC
67
+ * 8446, RFC 7301, RFC 6066 and RFC 9001, not ported from another
68
+ * implementation.
69
+ */
70
+ import { Secret, expose, secret, wipe } from "nish:secret"
71
+ import { HkdfScratch, hkdfExtractSha256, hkdfExtractSha384 } from "nish/crypto/hkdf"
72
+ import { SHA256_SIZE } from "nish/crypto/sha256"
73
+ import { SHA384_SIZE } from "nish/crypto/sha512"
74
+ import { P256SignScratch, P256_SIGNATURE_SIZE, p256SignSha256, p256SignSha256Into } from "nish/crypto/p256"
75
+ import { timingSafeEqual, timingSafeEqualAt } from "nish/crypto/ct"
76
+ import { X25519_SIZE, x25519, x25519Base } from "nish/crypto/x25519"
77
+ import {
78
+ TLS_ALERT_DECODE_ERROR,
79
+ TLS_ALERT_DECRYPT_ERROR,
80
+ TLS_ALERT_HANDSHAKE_FAILURE,
81
+ TLS_ALERT_ILLEGAL_PARAMETER,
82
+ TLS_ALERT_INTERNAL_ERROR,
83
+ TLS_ALERT_MISSING_EXTENSION,
84
+ TLS_ALERT_NO_APPLICATION_PROTOCOL,
85
+ TLS_ALERT_PROTOCOL_VERSION,
86
+ TLS_ALERT_UNEXPECTED_MESSAGE,
87
+ TLS_GROUP_X25519,
88
+ TLS_HANDSHAKE_CLIENT_HELLO,
89
+ TLS_HANDSHAKE_FINISHED,
90
+ TLS_RANDOM_SIZE,
91
+ TLS_VERSION_13,
92
+ TLS_X25519_SHARE_SIZE,
93
+ TlsClientHelloView,
94
+ tlsHexInto,
95
+ tlsReadClientHello,
96
+ tlsWindowHasU16,
97
+ tlsWindowString,
98
+ tlsWriteCertificate,
99
+ tlsWriteCertificateVerify,
100
+ tlsWriteCertificateVerifyContent,
101
+ tlsWriteEncryptedExtensions,
102
+ tlsWriteFinished,
103
+ tlsWriteHelloRetryRequest,
104
+ tlsWriteServerHello,
105
+ } from "nish/net/tls/codec"
106
+ import {
107
+ TlsTranscript,
108
+ tlsDeriveSecretInto,
109
+ tlsFinishedVerifyDataInto,
110
+ tlsMasterSecretInto,
111
+ tlsSuiteHashLength,
112
+ } from "nish/net/tls/schedule"
113
+
114
+ // ---- Levels ------------------------------------------------------------------
115
+
116
+ /** Cleartext: ClientHello, HelloRetryRequest and ServerHello (QUIC's Initial packets). */
117
+ export const TLS_LEVEL_INITIAL: i32 = 0
118
+ /** Under the handshake traffic secrets: EncryptedExtensions through both Finished messages. */
119
+ export const TLS_LEVEL_HANDSHAKE: i32 = 1
120
+ /** Under the application traffic secrets. T1 writes nothing here; the level exists for its secrets. */
121
+ export const TLS_LEVEL_APPLICATION: i32 = 2
122
+
123
+ // ---- States --------------------------------------------------------------------
124
+
125
+ /** Waiting for a ClientHello at the Initial level, the first or the one after a HelloRetryRequest. */
126
+ export const TLS_STATE_WAIT_CLIENT_HELLO: i32 = 0
127
+ /** The Certificate is written; `signatureInput()` is ready and `sign` is due. */
128
+ export const TLS_STATE_WAIT_SIGNATURE: i32 = 1
129
+ /** The server's flight is written; waiting for the client's Finished at the Handshake level. */
130
+ export const TLS_STATE_WAIT_FINISHED: i32 = 2
131
+ /** The client's Finished verified. Application data may flow both ways. */
132
+ export const TLS_STATE_CONNECTED: i32 = 3
133
+ /** An alert was answered; `alert` holds it, and every later call answers it again. */
134
+ export const TLS_STATE_FAILED: i32 = 4
135
+
136
+ /**
137
+ * The longest handshake message accepted, in bytes. A ClientHello carrying a
138
+ * post-quantum share is a few kilobytes; nothing a client sends a TLS 1.3
139
+ * server comes near this, and the cap is what bounds the buffer a peer can
140
+ * make the server hold.
141
+ */
142
+ export const TLS_MAX_HANDSHAKE_MESSAGE: i32 = 65536
143
+
144
+ /** SSL 3.0's version number; a ClientHello whose `legacy_version` is this or below is refused (RFC 8446 §D.5). */
145
+ const TLS_SSL3_VERSION: i32 = 0x0300
146
+
147
+ /** A typed zero for the offsets below: a bare literal is an `f64` under `--number-mode f64`. */
148
+ const TLS_FROM: i32 = 0
149
+
150
+ /**
151
+ * What a server is configured with, the same for every connection. An
152
+ * interface, so a caller writes it as an object literal.
153
+ */
154
+ export interface TlsServerConfig {
155
+ /** DER certificates, leaf first. At least one. */
156
+ certificateChain: u8[][]
157
+ /**
158
+ * The ALPN protocols the server speaks, most preferred first. Empty means
159
+ * the server does not negotiate ALPN over TCP, and refuses every client
160
+ * over QUIC, where ALPN is mandatory (RFC 9001 §8.1).
161
+ */
162
+ alpn: string[]
163
+ /** The server's transport parameters, opaque, sent in EncryptedExtensions only when `quic`. */
164
+ quicTransportParameters: u8[]
165
+ /**
166
+ * Extensions this module does not interpret, already encoded (each a type,
167
+ * a length and a body), written first in EncryptedExtensions:
168
+ * `record_size_limit` is the record layer's, for one. Empty for none.
169
+ */
170
+ extraExtensions: u8[]
171
+ /**
172
+ * The signature scheme the caller's key signs with, sent in
173
+ * CertificateVerify: `TLS_SIGNATURE_ECDSA_SECP256R1_SHA256` for a P-256
174
+ * key. A client that does not list it is refused with `handshake_failure`.
175
+ */
176
+ signatureScheme: i32
177
+ /** Whether the carrier is QUIC, which turns `quic_transport_parameters` on both ways. */
178
+ quic: boolean
179
+ }
180
+
181
+ /** The most a 24-bit length can say: the bound on a handshake message's body (RFC 8446 §4). */
182
+ const TLS_MAX_U24: i32 = 16777215
183
+
184
+ /**
185
+ * Whether `config` can be written at all: at least one certificate, every
186
+ * certificate and the whole Certificate message within their 24-bit lengths,
187
+ * every ALPN protocol 1 to 255 bytes, and EncryptedExtensions' extensions
188
+ * within their 16-bit length. A configuration that breaks one would make the
189
+ * server write a message whose length field is wrong, so the constructor
190
+ * refuses it instead.
191
+ */
192
+ const tlsConfigFits = (config: TlsServerConfig): boolean => {
193
+ if (toI32(config.certificateChain.length) === 0) {
194
+ return false
195
+ }
196
+ // The request context (1) and the list's length (3), then per certificate
197
+ // its length (3), its bytes and its empty extensions (2).
198
+ let certificate: i32 = 4
199
+ for (const der of config.certificateChain) {
200
+ const n: i32 = toI32(der.length)
201
+ if (n < 1 || n > TLS_MAX_U24 - certificate - 5) {
202
+ return false
203
+ }
204
+ certificate = certificate + 5 + n
205
+ }
206
+ // ALPN's type, lengths and the one name (at most 4 + 2 + 1 + 255), the
207
+ // server_name acknowledgement (4), and the transport parameters' type and
208
+ // length (4), beside what the caller passes through.
209
+ const extensions: i32 =
210
+ 266 + toI32(config.extraExtensions.length) + toI32(config.quicTransportParameters.length)
211
+ for (const protocol of config.alpn) {
212
+ const n: i32 = toI32(protocol.length)
213
+ if (n < 1 || n > 255) {
214
+ return false
215
+ }
216
+ }
217
+ return extensions <= 65535
218
+ }
219
+
220
+ /**
221
+ * Whether the bytes `data[at .. at + length)` spell `text`, one character a
222
+ * byte: ALPN's byte-wise comparison (RFC 7301 §3.2), and the one a host name
223
+ * is held to against the last.
224
+ */
225
+ const tlsBytesSpell = (data: u8[], at: i32, length: i32, text: string): boolean => {
226
+ if (toI32(text.length) !== length) {
227
+ return false
228
+ }
229
+ for (
230
+ let k: i32 = 0;
231
+ k < length && k < toI32(text.length) && at + k >= 0 && at + k < toI32(data.length);
232
+ k++
233
+ ) {
234
+ if (toI32(data[at + k]) !== toI32(text.charCodeAt(k))) {
235
+ return false
236
+ }
237
+ }
238
+ return true
239
+ }
240
+
241
+ /** The shortest and longest DER `ECDSA-Sig-Value` of a P-256 signature: a SEQUENCE of two INTEGERs of 1 to 33 bytes. */
242
+ const TLS_P256_DER_MIN: i32 = 8
243
+ const TLS_P256_DER_MAX: i32 = 72
244
+
245
+ /**
246
+ * The body length of the minimal DER INTEGER for the unsigned big-endian
247
+ * magnitude `rs[at .. at + 32)`: leading zeros dropped, one added when the top
248
+ * bit is set, and zero one zero octet rather than an empty integer (X.690
249
+ * §8.3.1).
250
+ */
251
+ const tlsDerIntegerLength = (rs: u8[], at: i32): i32 => {
252
+ let first: i32 = 0
253
+ while (first < 31 && at + first >= 0 && at + first < toI32(rs.length) && toI32(rs[at + first]) === 0) {
254
+ first++
255
+ }
256
+ const top: boolean = at + first >= 0 && at + first < toI32(rs.length) && toI32(rs[at + first]) >= 0x80
257
+ return 32 - first + (top ? 1 : 0)
258
+ }
259
+
260
+ /** That INTEGER, of body length `length`, written at `out[to ..)`; answers where it ends. */
261
+ const tlsDerIntegerInto = (out: u8[], to: i32, rs: u8[], at: i32, length: i32): i32 => {
262
+ const end: i32 = to + 2 + length
263
+ if (to < 0 || end > toI32(out.length)) {
264
+ return end
265
+ }
266
+ out[to] = toU8(0x02)
267
+ out[to + 1] = toU8(length)
268
+ for (let k: i32 = 0; k < length; k++) {
269
+ // The body's last `length` bytes are the magnitude's, a zero first when it is 33.
270
+ const from: i32 = at + 32 - length + k
271
+ const j: i32 = to + 2 + k
272
+ if (j >= 0 && j < toI32(out.length)) {
273
+ out[j] = from >= at && from >= 0 && from < toI32(rs.length) ? rs[from] : toU8(0)
274
+ }
275
+ }
276
+ return end
277
+ }
278
+
279
+ /** The length of `rs`'s DER `ECDSA-Sig-Value`: the SEQUENCE's two bytes and two INTEGERs. */
280
+ const tlsEcdsaDerLength = (rs: u8[]): i32 =>
281
+ 6 + tlsDerIntegerLength(rs, TLS_FROM) + tlsDerIntegerLength(rs, 32)
282
+
283
+ /** `rs`'s DER `ECDSA-Sig-Value` into all of `der`, which is `tlsEcdsaDerLength(rs)` bytes. At most 72, so the length is one short-form byte. */
284
+ const tlsEcdsaDerInto = (der: u8[], rs: u8[]): void => {
285
+ if (toI32(der.length) < 2) {
286
+ return
287
+ }
288
+ der[0] = toU8(0x30)
289
+ der[1] = toU8(toI32(der.length) - 2)
290
+ const r: i32 = tlsDerIntegerLength(rs, TLS_FROM)
291
+ tlsDerIntegerInto(der, tlsDerIntegerInto(der, 2, rs, TLS_FROM, r), rs, 32, tlsDerIntegerLength(rs, 32))
292
+ }
293
+
294
+ /**
295
+ * An ECDSA signature `r || s` (32 bytes each, as `nish/crypto/p256` answers
296
+ * it) as the DER `ECDSA-Sig-Value` a CertificateVerify carries (RFC 8446
297
+ * §4.2.3), or `null` when `rs` is not 64 bytes.
298
+ */
299
+ export const tlsEcdsaDerSignature = (rs: u8[]): u8[] | null => {
300
+ if (toI32(rs.length) !== 64) {
301
+ return null
302
+ }
303
+ const der: u8[] = new Array<u8>(tlsEcdsaDerLength(rs))
304
+ tlsEcdsaDerInto(der, rs)
305
+ return der
306
+ }
307
+
308
+ /**
309
+ * Signs a CertificateVerify input for `ecdsa_secp256r1_sha256` with the P-256
310
+ * private key `priv`: `p256SignSha256` (RFC 6979's deterministic nonce), DER
311
+ * encoded. `null` for a malformed key. The answer is what `TlsServer.sign`
312
+ * takes. The key is borrowed, as every `Secret` parameter is: the caller made
313
+ * it, and wipes it.
314
+ */
315
+ export const tlsSignEcdsaP256 = (priv: Secret<u8[]>, content: u8[]): u8[] | null => {
316
+ const rs: u8[] | null = p256SignSha256(priv, content)
317
+ if (rs === null) {
318
+ return null
319
+ }
320
+ return tlsEcdsaDerSignature(rs)
321
+ }
322
+
323
+ /**
324
+ * Signs CertificateVerify inputs for `ecdsa_secp256r1_sha256` with nothing
325
+ * kept per signature: `tlsSignEcdsaP256`'s answer, made in a
326
+ * `P256SignScratch` and handed back in one of this signer's own arrays, one
327
+ * per DER length (8 to 72 bytes), since `TlsServer.sign` takes the
328
+ * signature's length from its array and stores what it computes, so a
329
+ * signature it is handed cannot be a temporary. A carrier keeps one signer,
330
+ * not one per slot: signing is synchronous, and the array it answers is read
331
+ * by `sign` before the next signature replaces it.
332
+ */
333
+ export class TlsP256Signer {
334
+ /** The P-256 signer's scratch. */
335
+ scratch: P256SignScratch
336
+ /** `r || s` as `p256SignSha256Into` writes it. */
337
+ rs: u8[]
338
+ /** The DER signatures, index `length - TLS_P256_DER_MIN`. */
339
+ ders: u8[][]
340
+
341
+ constructor() {
342
+ this.scratch = new P256SignScratch()
343
+ this.rs = new Array<u8>(P256_SIGNATURE_SIZE)
344
+ this.ders = []
345
+ for (let length: i32 = TLS_P256_DER_MIN; length <= TLS_P256_DER_MAX; length++) {
346
+ this.ders.push(new Array<u8>(length))
347
+ }
348
+ }
349
+
350
+ /**
351
+ * `tlsSignEcdsaP256(priv, content)`, byte for byte, in the array of this
352
+ * signer's that has its length; `null` for a malformed key. It keeps
353
+ * nothing: the P-256 signature releases what it allocated, and the DER is
354
+ * written in place. The key is borrowed.
355
+ */
356
+ sign(priv: Secret<u8[]>, content: u8[]): u8[] | null {
357
+ if (
358
+ !p256SignSha256Into(this.scratch, priv, content, TLS_FROM, toI32(content.length), this.rs, TLS_FROM)
359
+ ) {
360
+ return null
361
+ }
362
+ const at: i32 = tlsEcdsaDerLength(this.rs) - TLS_P256_DER_MIN
363
+ if (at < 0 || at >= toI32(this.ders.length)) {
364
+ return null
365
+ }
366
+ const der: u8[] = this.ders[at]
367
+ tlsEcdsaDerInto(der, this.rs)
368
+ return der
369
+ }
370
+ }
371
+
372
+ /** Whether an x25519 shared secret is all zeros, reading every byte (run inside `expose`). */
373
+ const tlsIsZeroSecret = (shared: u8[]): boolean => timingSafeEqual(shared, new Array<u8>(X25519_SIZE))
374
+
375
+ /**
376
+ * `Derive-Secret(early, "derived", "")` with no PSK, the salt the handshake
377
+ * secret is extracted under: a constant of the hash alone (RFC 8448 §3 prints
378
+ * the SHA-256 one; the SHA-384 one was computed with Python's hmac and
379
+ * hashlib). It is written out rather than derived because the functions below
380
+ * run on the ECDHE secret inside `expose`, and Expand-Label may panic with a
381
+ * computed message, which such a function may not reach.
382
+ */
383
+ const TLS_DERIVED_SALT_SHA256: string = "6f2615a108c702c5678f54fc9dbab69716c076189c48250cebeac3576c3611ba"
384
+ const TLS_DERIVED_SALT_SHA384: string =
385
+ "1591dac5cbbf0330a4a84de9c753330e92d01f0a88214b4464972fd668049e93e52f2b16fad922fdc0584478428f282b"
386
+
387
+ /** The bytes of a lowercase hex constant of this module, in a fresh array. */
388
+ const tlsHexBytes = (text: string): u8[] => {
389
+ const out: u8[] = new Array<u8>(toI32(text.length) >> 1)
390
+ tlsHexInto(text, out, TLS_FROM)
391
+ return out
392
+ }
393
+
394
+ /** The SHA-256 handshake secret: HKDF-Extract of the ECDHE secret under the derived salt (run inside `expose`). */
395
+ const tlsHandshakeFromEcdhe256 = (ecdhe: u8[]): u8[] =>
396
+ hkdfExtractSha256(tlsHexBytes(TLS_DERIVED_SALT_SHA256), ecdhe)
397
+
398
+ /** The SHA-384 handshake secret: HKDF-Extract of the ECDHE secret under the derived salt (run inside `expose`). */
399
+ const tlsHandshakeFromEcdhe384 = (ecdhe: u8[]): u8[] =>
400
+ hkdfExtractSha384(tlsHexBytes(TLS_DERIVED_SALT_SHA384), ecdhe)
401
+
402
+ /** The secret of `level` from one side's pair, or `null` for any other level or one not derived yet (still empty). */
403
+ const tlsSecretAt = (level: i32, handshake: u8[], application: u8[]): u8[] | null => {
404
+ const chosen: u8[] = level === TLS_LEVEL_APPLICATION ? application : handshake
405
+ if ((level !== TLS_LEVEL_HANDSHAKE && level !== TLS_LEVEL_APPLICATION) || toI32(chosen.length) === 0) {
406
+ return null
407
+ }
408
+ return chosen
409
+ }
410
+
411
+ // ---- The exchange, in a scope of its own --------------------------------------
412
+
413
+ /**
414
+ * The x25519 exchange's inputs and answers as big-endian 64-bit words: the
415
+ * ephemeral private key and the client's share in, the server's public key
416
+ * and the handshake secret out, and the alert. It is a class of numbers and
417
+ * nothing else on purpose. `x25519` answers its shared secret as a `Secret`,
418
+ * which is an allocation it stores, so neither an automatic arena scope nor a
419
+ * `using a = arena()` block may be put around a call to it (NL2424). A
420
+ * function whose every parameter is an object of numbers can be handed no
421
+ * memory a pointer fits in, so everything it and its callees allocate is
422
+ * garbage when it returns, and the compiler gives it a scope of its own
423
+ * (docs/LANGUAGE.md, "Memory model", item 2): `tlsExchange` takes only this.
424
+ *
425
+ * Its fields must stay numbers: one that could hold a pointer would cost
426
+ * `tlsExchange` that scope without a word from the compiler, and only
427
+ * `tests/link/net_tls_memory` would notice.
428
+ *
429
+ * The words hold secrets — the private key and the handshake secret — in
430
+ * fields, where `secureZero` cannot reach, so each is zeroed by an ordinary
431
+ * store as soon as it has been read, which stands because the words stay
432
+ * reachable from their `TlsServer` (TLS-1 in `docs/security/tls.md`).
433
+ */
434
+ class TlsExchangeWords {
435
+ key0: u64 = 0
436
+ key1: u64 = 0
437
+ key2: u64 = 0
438
+ key3: u64 = 0
439
+ share0: u64 = 0
440
+ share1: u64 = 0
441
+ share2: u64 = 0
442
+ share3: u64 = 0
443
+ public0: u64 = 0
444
+ public1: u64 = 0
445
+ public2: u64 = 0
446
+ public3: u64 = 0
447
+ secret0: u64 = 0
448
+ secret1: u64 = 0
449
+ secret2: u64 = 0
450
+ secret3: u64 = 0
451
+ secret4: u64 = 0
452
+ secret5: u64 = 0
453
+ /** 32 or 48: which handshake secret to extract. */
454
+ hashLength: i32 = 0
455
+ }
456
+
457
+ /** The eight bytes `b[at .. at + 8)` as a big-endian word; a byte outside `b` reads as zero. */
458
+ const tlsWordAt = (b: u8[], at: i32): u64 => {
459
+ let v: u64 = 0
460
+ for (let k: i32 = 0; k < 8; k++) {
461
+ const byte: u64 = at + k >= 0 && at + k < toI32(b.length) ? toU64(b[at + k]) : toU64(0)
462
+ v = (v << toU64(8)) | byte
463
+ }
464
+ return v
465
+ }
466
+
467
+ /** `v` big-endian into `b[at .. at + 8)`; a byte outside `b` is dropped. */
468
+ const tlsWordInto = (b: u8[], at: i32, v: u64): void => {
469
+ for (let k: i32 = 0; k < 8; k++) {
470
+ const shift: i32 = (7 - k) << 3
471
+ if (at + k >= 0 && at + k < toI32(b.length)) {
472
+ b[at + k] = toU8(v >> toU64(shift))
473
+ }
474
+ }
475
+ }
476
+
477
+ /**
478
+ * The exchange of RFC 8446 §7.4.2 on `w`: the server's public key and the
479
+ * handshake secret from the ephemeral key and the client's share, both
480
+ * written back as words, the key's words zeroed once read. Answers 0,
481
+ * `illegal_parameter` for a share whose shared secret is all zeros (§7.4.2,
482
+ * RFC 7748 §6.1), or `internal_error` when the exchange answers nothing. The
483
+ * copy of the key and the ECDHE secret are `Secret`s wiped on every path, and
484
+ * the handshake secret's bytes with `secureZero` once they are words. The
485
+ * compiler brackets this function with an arena scope of its own (see
486
+ * `TlsExchangeWords`), so what x25519 and HKDF allocate is gone when it
487
+ * returns.
488
+ */
489
+ const tlsExchange = (w: TlsExchangeWords): i32 => {
490
+ const raw: u8[] = new Array<u8>(X25519_SIZE)
491
+ tlsWordInto(raw, 0, w.key0)
492
+ tlsWordInto(raw, 8, w.key1)
493
+ tlsWordInto(raw, 16, w.key2)
494
+ tlsWordInto(raw, 24, w.key3)
495
+ w.key0 = toU64(0)
496
+ w.key1 = toU64(0)
497
+ w.key2 = toU64(0)
498
+ w.key3 = toU64(0)
499
+ const share: u8[] = new Array<u8>(X25519_SIZE)
500
+ tlsWordInto(share, 0, w.share0)
501
+ tlsWordInto(share, 8, w.share1)
502
+ tlsWordInto(share, 16, w.share2)
503
+ tlsWordInto(share, 24, w.share3)
504
+ const key: Secret<u8[]> = secret(raw)
505
+ const serverPublic: u8[] | null = x25519Base(key)
506
+ const shared: Secret<u8[]> | null = x25519(key, share)
507
+ wipe(key)
508
+ if (shared === null) {
509
+ return TLS_ALERT_INTERNAL_ERROR
510
+ }
511
+ // RFC 7748 §6.1 leaves the all-zero check to the protocol, and RFC 8446
512
+ // §7.4.2 makes it: a low-order client share gives the zero secret, which
513
+ // anybody can compute. The secret is secret, so the compare reads it all.
514
+ if (serverPublic === null || expose(shared, tlsIsZeroSecret)) {
515
+ wipe(shared)
516
+ return serverPublic === null ? TLS_ALERT_INTERNAL_ERROR : TLS_ALERT_ILLEGAL_PARAMETER
517
+ }
518
+ const handshake: u8[] =
519
+ w.hashLength === SHA384_SIZE
520
+ ? expose(shared, tlsHandshakeFromEcdhe384)
521
+ : expose(shared, tlsHandshakeFromEcdhe256)
522
+ wipe(shared)
523
+ w.public0 = tlsWordAt(serverPublic, 0)
524
+ w.public1 = tlsWordAt(serverPublic, 8)
525
+ w.public2 = tlsWordAt(serverPublic, 16)
526
+ w.public3 = tlsWordAt(serverPublic, 24)
527
+ w.secret0 = tlsWordAt(handshake, 0)
528
+ w.secret1 = tlsWordAt(handshake, 8)
529
+ w.secret2 = tlsWordAt(handshake, 16)
530
+ w.secret3 = tlsWordAt(handshake, 24)
531
+ w.secret4 = tlsWordAt(handshake, 32)
532
+ w.secret5 = tlsWordAt(handshake, 40)
533
+ secureZero(handshake)
534
+ return 0
535
+ }
536
+
537
+ // ---- Buffers -------------------------------------------------------------------
538
+
539
+ /** The first input buffer: room for the ClientHello of every common client. It grows only for a longer one. */
540
+ const TLS_INPUT_START: i32 = 2048
541
+ /** The Initial output: a HelloRetryRequest and a ServerHello, each with a 32-byte session id, fit with room. */
542
+ const TLS_INITIAL_OUTPUT_SIZE: i32 = 256
543
+ /** The longest DER ECDSA P-256 signature, what the flight is first sized for: two 33-byte integers and their headers. */
544
+ const TLS_ECDSA_SIGNATURE_MAX: i32 = 72
545
+ /** Which secret each of a server's secret arrays holds, by index into `secrets256` and `secrets384`. */
546
+ const TLS_SECRET_HANDSHAKE: i32 = 0
547
+ const TLS_SECRET_CLIENT_HANDSHAKE: i32 = 1
548
+ const TLS_SECRET_SERVER_HANDSHAKE: i32 = 2
549
+ const TLS_SECRET_CLIENT_APPLICATION: i32 = 3
550
+ const TLS_SECRET_SERVER_APPLICATION: i32 = 4
551
+ const TLS_SECRET_EXPORTER: i32 = 5
552
+ const TLS_SECRET_CLIENT_FINISHED: i32 = 6
553
+ /** The CertificateVerify input sits beside them: 98 bytes and the transcript hash. */
554
+ const TLS_SECRET_TO_BE_SIGNED: i32 = 7
555
+ const TLS_SECRET_COUNT: i32 = 8
556
+ /** What CertificateVerify signs besides the hash (RFC 8446 §4.4.3): 64 spaces, the 33-byte context and a zero. */
557
+ const TLS_CERTIFICATE_VERIFY_PREFIX: i32 = 98
558
+
559
+ /**
560
+ * The most the server's Handshake-level flight under `config` takes with an
561
+ * ECDSA P-256 signature: EncryptedExtensions with every extension it may
562
+ * carry and the longest configured ALPN protocol, the Certificate, a
563
+ * CertificateVerify and a SHA-384 Finished.
564
+ */
565
+ const tlsFlightCapacity = (config: TlsServerConfig): i32 => {
566
+ const none: u8[] = []
567
+ let longest: string = ""
568
+ for (const protocol of config.alpn) {
569
+ if (toI32(protocol.length) > toI32(longest.length)) {
570
+ longest = protocol
571
+ }
572
+ }
573
+ const extensions: i32 = tlsWriteEncryptedExtensions(
574
+ none,
575
+ TLS_FROM,
576
+ config.extraExtensions,
577
+ longest,
578
+ true,
579
+ config.quic ? config.quicTransportParameters : null
580
+ )
581
+ const certificate: i32 = tlsWriteCertificate(none, TLS_FROM, config.certificateChain)
582
+ return extensions + certificate + 8 + TLS_ECDSA_SIGNATURE_MAX + 4 + SHA384_SIZE
583
+ }
584
+
585
+ /**
586
+ * One TLS 1.3 server handshake. Make one per connection, or one per slot and
587
+ * `restart` it for each connection the slot takes; feed it what the client
588
+ * sends with `receive`, send what `takeOutput` answers (or what the output
589
+ * buffers hold, for a carrier that reads them in place), and sign when the
590
+ * state says so.
591
+ *
592
+ * The fields are readable: `state` and `alert`, the negotiated `suite`,
593
+ * `serverName`, `alpn` and the client's `clientTransportParameters`, and the
594
+ * traffic secrets. `readSecret` and `writeSecret` answer the last by level.
595
+ *
596
+ * **What it allocates.** Everything it keeps is allocated by the constructor
597
+ * and reused by `restart`: the input and output buffers, the transcript's
598
+ * hashers, the HKDF scratch, and an array of exactly HashLen bytes for every
599
+ * secret under each hash, which the secret fields point at as each is
600
+ * derived and away from again when the next connection starts. `receive` and
601
+ * `sign` do their work inside `using a = arena()` blocks, so the compiler has
602
+ * proved that what they allocate on the way dies before they return. Four
603
+ * things grow or are made outside those blocks: the input buffer, doubled up
604
+ * to the largest message when a ClientHello does not fit (once per slot);
605
+ * the Handshake-level output, when a signature is longer than an ECDSA one;
606
+ * the `serverName` string, made when the client's host name differs from the
607
+ * last one this server held; and, over QUIC only, the array the client's
608
+ * transport parameters are copied into, which grows only when a client's
609
+ * are longer than any before (TLS-3).
610
+ */
611
+ export class TlsServer {
612
+ config: TlsServerConfig
613
+ serverRandom: u8[]
614
+ ephemeralPrivate: u8[]
615
+ /** The client's `host_name`, or empty. */
616
+ serverName: string = ""
617
+ /** The ALPN protocol chosen, one of the configuration's, or empty. */
618
+ alpn: string = ""
619
+ /** The last host name a client of this server sent, kept across `restart` so that the next one naming it reuses the string. */
620
+ lastServerName: string = ""
621
+ /** The client's `quic_transport_parameters`, opaque; empty unless the carrier is QUIC. */
622
+ clientTransportParameters: u8[]
623
+ /** The array `clientTransportParameters` points at once read, kept across `restart` and refilled in the room it has. */
624
+ clientParameters: u8[]
625
+ transcript: TlsTranscript
626
+ /** The bytes received at the current level and not yet a whole message: `input[0 .. inputLength)`. */
627
+ input: u8[]
628
+ /** What the server has written at the Initial level and not handed on: `outputInitial[0 .. outputInitialLength)`. */
629
+ outputInitial: u8[]
630
+ /** What the server has written at the Handshake level and not handed on: `outputHandshake[0 .. outputHandshakeLength)`. */
631
+ outputHandshake: u8[]
632
+ handshakeSecret: u8[]
633
+ clientHandshakeSecret: u8[]
634
+ serverHandshakeSecret: u8[]
635
+ clientApplicationSecret: u8[]
636
+ serverApplicationSecret: u8[]
637
+ /** `exporter_master_secret` (RFC 8446 §7.5), for a caller's own exporters. */
638
+ exporterSecret: u8[]
639
+ /** The CertificateVerify input while `state` is `TLS_STATE_WAIT_SIGNATURE`. */
640
+ toBeSigned: u8[]
641
+ /** The client Finished's `verify_data`, known once the server's Finished is written. */
642
+ expectedClientFinished: u8[]
643
+ /** The empty array every secret field points at until its secret is derived. */
644
+ none: u8[]
645
+ /** The arrays the secret fields point into: `TLS_SECRET_COUNT` of each hash's lengths. */
646
+ secrets256: u8[][]
647
+ secrets384: u8[][]
648
+ /** The ClientHello last read, as windows into `input`. */
649
+ hello: TlsClientHelloView
650
+ /** Where every HKDF and HMAC of the schedule runs. */
651
+ kdf: HkdfScratch
652
+ /** The exchange's words (`tlsExchange`). */
653
+ exchange: TlsExchangeWords
654
+ /** The server's x25519 public key, unpacked from the exchange's words for the ServerHello. */
655
+ serverPublic: u8[]
656
+ /** A transcript hash on its way into the schedule: HashLen bytes of it. */
657
+ hashes: u8[]
658
+ state: i32 = 0
659
+ /** The alert the server failed with, or 0. */
660
+ alert: i32 = 0
661
+ /** The negotiated cipher suite, or 0 before the first ClientHello is read. */
662
+ suite: i32 = 0
663
+ hashLength: i32 = 0
664
+ inputLength: i32 = 0
665
+ outputInitialLength: i32 = 0
666
+ outputHandshakeLength: i32 = 0
667
+ /** Whether a HelloRetryRequest was sent, so a second miss is fatal. */
668
+ retried: boolean = false
669
+ /** Whether `config` passed `tlsConfigFits`, decided once by the constructor. */
670
+ configFits: boolean = false
671
+
672
+ /**
673
+ * A server for one connection under `config`, with the 32-byte server
674
+ * random and the 32-byte x25519 private key drawn for it. A random or key of
675
+ * another length, or a configuration `tlsConfigFits` refuses, leaves the
676
+ * server failed with `internal_error` before it reads anything.
677
+ */
678
+ constructor(config: TlsServerConfig, serverRandom: u8[], ephemeralPrivate: u8[]) {
679
+ this.config = config
680
+ this.serverRandom = serverRandom
681
+ this.ephemeralPrivate = ephemeralPrivate
682
+ this.none = []
683
+ this.clientTransportParameters = this.none
684
+ this.clientParameters = []
685
+ this.transcript = new TlsTranscript(0)
686
+ this.configFits = tlsConfigFits(config)
687
+ this.input = new Array<u8>(TLS_INPUT_START)
688
+ this.outputInitial = new Array<u8>(TLS_INITIAL_OUTPUT_SIZE)
689
+ this.outputHandshake = new Array<u8>(this.configFits ? tlsFlightCapacity(config) : TLS_FROM)
690
+ this.secrets256 = []
691
+ this.secrets384 = []
692
+ for (let k: i32 = 0; k < TLS_SECRET_COUNT; k++) {
693
+ const extra: i32 = k === TLS_SECRET_TO_BE_SIGNED ? TLS_CERTIFICATE_VERIFY_PREFIX : 0
694
+ this.secrets256.push(new Array<u8>(SHA256_SIZE + extra))
695
+ this.secrets384.push(new Array<u8>(SHA384_SIZE + extra))
696
+ }
697
+ this.hello = new TlsClientHelloView()
698
+ this.kdf = new HkdfScratch()
699
+ this.exchange = new TlsExchangeWords()
700
+ this.serverPublic = new Array<u8>(X25519_SIZE)
701
+ this.hashes = new Array<u8>(SHA384_SIZE)
702
+ this.handshakeSecret = this.none
703
+ this.clientHandshakeSecret = this.none
704
+ this.serverHandshakeSecret = this.none
705
+ this.clientApplicationSecret = this.none
706
+ this.serverApplicationSecret = this.none
707
+ this.exporterSecret = this.none
708
+ this.toBeSigned = this.none
709
+ this.expectedClientFinished = this.none
710
+ this.restart(serverRandom, ephemeralPrivate)
711
+ }
712
+
713
+ /**
714
+ * Starts the next connection in this server's buffers, with its own
715
+ * 32-byte random and x25519 private key, as a fresh `TlsServer` would:
716
+ * every secret the last connection left in them is wiped with `secureZero`
717
+ * and every secret field points at nothing again. The two arrays are kept,
718
+ * not copied, as the constructor keeps them. A random or key of another
719
+ * length, or a configuration `tlsConfigFits` refused, leaves the server
720
+ * failed with `internal_error`.
721
+ */
722
+ restart(serverRandom: u8[], ephemeralPrivate: u8[]): void {
723
+ this.serverRandom = serverRandom
724
+ this.ephemeralPrivate = ephemeralPrivate
725
+ this.state = TLS_STATE_WAIT_CLIENT_HELLO
726
+ this.alert = 0
727
+ this.suite = 0
728
+ this.hashLength = 0
729
+ this.retried = false
730
+ this.serverName = ""
731
+ this.alpn = ""
732
+ this.clientTransportParameters = this.none
733
+ this.inputLength = 0
734
+ this.outputInitialLength = 0
735
+ this.outputHandshakeLength = 0
736
+ for (let k: i32 = 0; k < toI32(this.secrets256.length) && k < toI32(this.secrets384.length); k++) {
737
+ secureZero(this.secrets256[k])
738
+ secureZero(this.secrets384[k])
739
+ }
740
+ this.handshakeSecret = this.none
741
+ this.clientHandshakeSecret = this.none
742
+ this.serverHandshakeSecret = this.none
743
+ this.clientApplicationSecret = this.none
744
+ this.serverApplicationSecret = this.none
745
+ this.exporterSecret = this.none
746
+ this.toBeSigned = this.none
747
+ this.expectedClientFinished = this.none
748
+ if (
749
+ toI32(serverRandom.length) !== TLS_RANDOM_SIZE ||
750
+ toI32(ephemeralPrivate.length) !== X25519_SIZE ||
751
+ !this.configFits
752
+ ) {
753
+ this.state = TLS_STATE_FAILED
754
+ this.alert = TLS_ALERT_INTERNAL_ERROR
755
+ }
756
+ }
757
+
758
+ /** Fails the handshake with `alert` and answers it. */
759
+ fail(alert: i32): i32 {
760
+ this.state = TLS_STATE_FAILED
761
+ this.alert = alert
762
+ return alert
763
+ }
764
+
765
+ /** The array secret `index` lives in under the negotiated hash: exactly HashLen bytes (the CertificateVerify input, 98 more). */
766
+ secretArray(index: i32): u8[] {
767
+ const pool: u8[][] = this.hashLength === SHA384_SIZE ? this.secrets384 : this.secrets256
768
+ return index >= 0 && index < toI32(pool.length) ? pool[index] : this.none
769
+ }
770
+
771
+ /** The level the client's next bytes must arrive at, or -1 when the state expects none. */
772
+ expectedLevel(): i32 {
773
+ switch (this.state) {
774
+ case TLS_STATE_WAIT_CLIENT_HELLO:
775
+ return TLS_LEVEL_INITIAL
776
+ case TLS_STATE_WAIT_FINISHED:
777
+ return TLS_LEVEL_HANDSHAKE
778
+ default:
779
+ return -1
780
+ }
781
+ }
782
+
783
+ /**
784
+ * Makes `input` hold at least `need` bytes, keeping the ones it holds. It
785
+ * doubles, so a slot grows it at most a handful of times however its
786
+ * clients cut their messages, and never past the largest message `receive`
787
+ * takes.
788
+ */
789
+ reserveInput(need: i32): void {
790
+ const capacity: i32 = toI32(this.input.length)
791
+ if (need <= capacity) {
792
+ return
793
+ }
794
+ let size: i32 = capacity
795
+ while (size < need) {
796
+ size = size * 2
797
+ }
798
+ const most: i32 = TLS_MAX_HANDSHAKE_MESSAGE + 4
799
+ const grown: u8[] = new Array<u8>(size > most ? most : size)
800
+ for (let k: i32 = 0; k < this.inputLength && k < toI32(grown.length) && k < capacity; k++) {
801
+ grown[k] = this.input[k]
802
+ }
803
+ this.input = grown
804
+ }
805
+
806
+ /**
807
+ * Makes the output of `level` hold at least `need` bytes, keeping the ones
808
+ * it holds. Both are sized by the constructor for what this configuration
809
+ * writes with an ECDSA signature, so only a longer signature grows the
810
+ * Handshake level's, once per slot.
811
+ */
812
+ reserveOutput(level: i32, need: i32): void {
813
+ const initial: boolean = level === TLS_LEVEL_INITIAL
814
+ const buffer: u8[] = initial ? this.outputInitial : this.outputHandshake
815
+ const held: i32 = initial ? this.outputInitialLength : this.outputHandshakeLength
816
+ const capacity: i32 = toI32(buffer.length)
817
+ if (need <= capacity) {
818
+ return
819
+ }
820
+ const grown: u8[] = new Array<u8>(need)
821
+ for (let k: i32 = 0; k < held && k < toI32(buffer.length) && k < toI32(grown.length); k++) {
822
+ grown[k] = buffer[k]
823
+ }
824
+ if (initial) {
825
+ this.outputInitial = grown
826
+ } else {
827
+ this.outputHandshake = grown
828
+ }
829
+ }
830
+
831
+ /**
832
+ * Takes the handshake bytes `data[off .. off + len)` the client sent at
833
+ * `level`, a whole message, part of one or several, and handles every
834
+ * message they complete. Answers 0, or the alert the server fails with.
835
+ *
836
+ * A level other than the one the state expects is `unexpected_message`, as
837
+ * is a byte left over once a message ends the client's flight: each of the
838
+ * three messages a client sends here (two ClientHellos and a Finished) is
839
+ * the last of its flight, and RFC 8446 §5.1 forbids a message to straddle a
840
+ * key change. A window outside `data` is `internal_error`, the caller's
841
+ * fault and not the peer's. Once the handshake is done, any further bytes
842
+ * are `unexpected_message` too: the post-handshake messages a client may
843
+ * send (KeyUpdate) belong to the record layer, which handles them itself.
844
+ */
845
+ receive(level: i32, data: u8[], off: i32, len: i32): i32 {
846
+ if (this.state === TLS_STATE_FAILED) {
847
+ return this.alert
848
+ }
849
+ if (off < 0 || len < 0 || off > toI32(data.length) - len) {
850
+ return this.fail(TLS_ALERT_INTERNAL_ERROR)
851
+ }
852
+ if (len === 0) {
853
+ return 0
854
+ }
855
+ if (level !== this.expectedLevel()) {
856
+ return this.fail(TLS_ALERT_UNEXPECTED_MESSAGE)
857
+ }
858
+ // The buffer only ever holds one message, so bytes that would take it
859
+ // past the largest one allowed are refused before they are copied.
860
+ if (len > TLS_MAX_HANDSHAKE_MESSAGE + 4 - this.inputLength) {
861
+ return this.fail(TLS_ALERT_DECODE_ERROR)
862
+ }
863
+ this.reserveInput(this.inputLength + len)
864
+ for (
865
+ let k: i32 = 0;
866
+ k < len && off + k < toI32(data.length) && this.inputLength < toI32(this.input.length);
867
+ k++
868
+ ) {
869
+ this.input[this.inputLength] = data[off + k]
870
+ this.inputLength = this.inputLength + 1
871
+ }
872
+ // Every message a client sends here ends its flight, so the buffer holds
873
+ // at most one: a byte past its end is refused below.
874
+ if (this.inputLength < 4) {
875
+ return 0
876
+ }
877
+ const length: i32 = (toI32(this.input[1]) << 16) | (toI32(this.input[2]) << 8) | toI32(this.input[3])
878
+ if (length > TLS_MAX_HANDSHAKE_MESSAGE) {
879
+ return this.fail(TLS_ALERT_DECODE_ERROR)
880
+ }
881
+ const whole: i32 = 4 + length
882
+ if (this.inputLength < whole) {
883
+ return 0
884
+ }
885
+ if (this.inputLength > whole) {
886
+ return this.fail(TLS_ALERT_UNEXPECTED_MESSAGE)
887
+ }
888
+ // The message stays where it is while it is handled; the buffer is
889
+ // empty again for whatever comes next.
890
+ this.inputLength = 0
891
+ const alert: i32 = this.handleMessage(length)
892
+ if (alert !== 0) {
893
+ return this.fail(alert)
894
+ }
895
+ return 0
896
+ }
897
+
898
+ /**
899
+ * Dispatches one whole message, `input[0 .. 4 + length)`. `receive`
900
+ * admits bytes only in the two states that expect them, so the state is
901
+ * one of those two here.
902
+ */
903
+ handleMessage(length: i32): i32 {
904
+ const type: i32 = toI32(this.input[0])
905
+ if (this.state === TLS_STATE_WAIT_CLIENT_HELLO) {
906
+ return type === TLS_HANDSHAKE_CLIENT_HELLO
907
+ ? this.handleClientHello(length)
908
+ : TLS_ALERT_UNEXPECTED_MESSAGE
909
+ }
910
+ return type === TLS_HANDSHAKE_FINISHED ? this.handleFinished(length) : TLS_ALERT_UNEXPECTED_MESSAGE
911
+ }
912
+
913
+ /**
914
+ * The first suite the client lists that this module negotiates, or 0
915
+ * (§4.1.1: the server picks; following the client's order is the choice
916
+ * RFC 8448's server makes).
917
+ */
918
+ chooseSuite(hello: TlsClientHelloView): i32 {
919
+ const data: u8[] = hello.data
920
+ const end: i32 = hello.suitesAt + hello.suitesLength
921
+ for (
922
+ let k: i32 = hello.suitesAt;
923
+ k + 1 < end && k >= 0 && k < toI32(data.length) && k + 1 < toI32(data.length);
924
+ k += 2
925
+ ) {
926
+ const suite: i32 = (toI32(data[k]) << 8) | toI32(data[k + 1])
927
+ if (tlsSuiteHashLength(suite) > 0) {
928
+ return suite
929
+ }
930
+ }
931
+ return 0
932
+ }
933
+
934
+ /**
935
+ * ALPN (RFC 7301 §3.2): the first configured protocol the client offers.
936
+ * Answers 0 with `alpn` set, or empty when nothing is negotiated, or
937
+ * `no_application_protocol`.
938
+ */
939
+ chooseAlpn(hello: TlsClientHelloView): i32 {
940
+ this.alpn = ""
941
+ if (hello.hasAlpn && toI32(this.config.alpn.length) > 0) {
942
+ const data: u8[] = hello.data
943
+ const end: i32 = hello.alpnAt + hello.alpnLength
944
+ for (const protocol of this.config.alpn) {
945
+ let at: i32 = hello.alpnAt
946
+ while (at < end && at >= 0 && at < toI32(data.length)) {
947
+ const n: i32 = toI32(data[at])
948
+ if (tlsBytesSpell(data, at + 1, n, protocol)) {
949
+ this.alpn = protocol
950
+ return 0
951
+ }
952
+ at = at + 1 + n
953
+ }
954
+ }
955
+ return TLS_ALERT_NO_APPLICATION_PROTOCOL
956
+ }
957
+ return this.config.quic ? TLS_ALERT_NO_APPLICATION_PROTOCOL : 0
958
+ }
959
+
960
+ /**
961
+ * `serverName` for the host name the hello carries: kept as it is when it
962
+ * already spells those bytes, so a slot whose clients name one host makes
963
+ * the string once, and made again only when the name changes.
964
+ */
965
+ takeServerName(hello: TlsClientHelloView): void {
966
+ if (!hello.hasServerName) {
967
+ this.serverName = ""
968
+ return
969
+ }
970
+ if (!tlsBytesSpell(hello.data, hello.serverNameAt, hello.serverNameLength, this.lastServerName)) {
971
+ this.lastServerName = tlsWindowString(hello.data, hello.serverNameAt, hello.serverNameLength)
972
+ }
973
+ this.serverName = this.lastServerName
974
+ }
975
+
976
+ /**
977
+ * A ClientHello, the first or the second: refuse it, answer it with a
978
+ * HelloRetryRequest, or answer it with ServerHello, EncryptedExtensions and
979
+ * Certificate and wait for the signature. The checks run in the order a
980
+ * reader of RFC 8446 would make them: the structure, then the version, then
981
+ * the suite, the extensions §9.2 requires, the group, the signature scheme,
982
+ * the share itself, and the application protocol.
983
+ */
984
+ handleClientHello(length: i32): i32 {
985
+ const hello: TlsClientHelloView = this.hello
986
+ const data: u8[] = this.input
987
+ tlsReadClientHello(hello, data, 4, length)
988
+ if (hello.alert !== 0) {
989
+ return hello.alert
990
+ }
991
+ // §4.2.1: the version is negotiated by `supported_versions` alone, and
992
+ // `legacy_version` is not consulted for it. The one exception is SSL 3.0
993
+ // or below, which §D.5 lets a server refuse outright. Without
994
+ // `supported_versions` the client offers TLS 1.2 or earlier, which this
995
+ // server does not speak.
996
+ if (hello.legacyVersion <= TLS_SSL3_VERSION) {
997
+ return TLS_ALERT_PROTOCOL_VERSION
998
+ }
999
+ if (
1000
+ !hello.hasSupportedVersions ||
1001
+ !tlsWindowHasU16(data, hello.versionsAt, hello.versionsLength, TLS_VERSION_13)
1002
+ ) {
1003
+ return TLS_ALERT_PROTOCOL_VERSION
1004
+ }
1005
+ if (!hello.nullCompressionOnly) {
1006
+ return TLS_ALERT_ILLEGAL_PARAMETER
1007
+ }
1008
+ const suite: i32 = this.chooseSuite(hello)
1009
+ if (suite === 0) {
1010
+ return TLS_ALERT_HANDSHAKE_FAILURE
1011
+ }
1012
+ // §4.1.4: the second ClientHello must allow the suite the
1013
+ // HelloRetryRequest chose, since the transcript is already under its hash.
1014
+ if (this.retried && suite !== this.suite) {
1015
+ return TLS_ALERT_ILLEGAL_PARAMETER
1016
+ }
1017
+ // §9.2: a certificate-authenticated handshake needs all three, and
1018
+ // `supported_groups` and `key_share` come as a pair.
1019
+ if (!hello.hasSignatureAlgorithms || !hello.hasSupportedGroups || !hello.hasKeyShare) {
1020
+ return TLS_ALERT_MISSING_EXTENSION
1021
+ }
1022
+ if (this.config.quic && !hello.hasQuicTransportParameters) {
1023
+ return TLS_ALERT_MISSING_EXTENSION
1024
+ }
1025
+ if (!tlsWindowHasU16(data, hello.groupsAt, hello.groupsLength, TLS_GROUP_X25519)) {
1026
+ return TLS_ALERT_HANDSHAKE_FAILURE
1027
+ }
1028
+ if (!tlsWindowHasU16(data, hello.schemesAt, hello.schemesLength, this.config.signatureScheme)) {
1029
+ return TLS_ALERT_HANDSHAKE_FAILURE
1030
+ }
1031
+ if (!this.retried) {
1032
+ this.suite = suite
1033
+ this.hashLength = tlsSuiteHashLength(suite)
1034
+ this.transcript.reset(this.hashLength)
1035
+ }
1036
+ this.transcript.update(data, TLS_FROM, 4 + length)
1037
+ if (!hello.hasX25519Share) {
1038
+ // §4.1.4: one HelloRetryRequest; a second ClientHello without the share
1039
+ // the first one asked for is `illegal_parameter`.
1040
+ if (this.retried) {
1041
+ return TLS_ALERT_ILLEGAL_PARAMETER
1042
+ }
1043
+ this.retried = true
1044
+ const none: u8[] = []
1045
+ this.reserveOutput(
1046
+ TLS_LEVEL_INITIAL,
1047
+ tlsWriteHelloRetryRequest(
1048
+ none,
1049
+ this.outputInitialLength,
1050
+ data,
1051
+ hello.sessionIdAt,
1052
+ hello.sessionIdLength,
1053
+ suite,
1054
+ TLS_GROUP_X25519
1055
+ )
1056
+ )
1057
+ using _scope = arena()
1058
+ this.transcript.restartWithMessageHash()
1059
+ const at: i32 = this.outputInitialLength
1060
+ const end: i32 = tlsWriteHelloRetryRequest(
1061
+ this.outputInitial,
1062
+ at,
1063
+ data,
1064
+ hello.sessionIdAt,
1065
+ hello.sessionIdLength,
1066
+ this.suite,
1067
+ TLS_GROUP_X25519
1068
+ )
1069
+ this.sent(TLS_LEVEL_INITIAL, at, end)
1070
+ return 0
1071
+ }
1072
+ if (hello.x25519ShareLength !== TLS_X25519_SHARE_SIZE) {
1073
+ return TLS_ALERT_ILLEGAL_PARAMETER
1074
+ }
1075
+ const alpnAlert: i32 = this.chooseAlpn(hello)
1076
+ if (alpnAlert !== 0) {
1077
+ return alpnAlert
1078
+ }
1079
+ this.takeServerName(hello)
1080
+ if (this.config.quic) {
1081
+ // Refilled in place, so a slot's handshakes keep nothing for it (QUIC-3).
1082
+ const parameters: u8[] = this.clientParameters
1083
+ while (parameters.length > 0) {
1084
+ parameters.pop()
1085
+ }
1086
+ for (
1087
+ let k: i32 = 0;
1088
+ k < hello.quicLength && hello.quicAt + k >= 0 && hello.quicAt + k < toI32(data.length);
1089
+ k++
1090
+ ) {
1091
+ parameters.push(data[hello.quicAt + k])
1092
+ }
1093
+ this.clientTransportParameters = parameters
1094
+ }
1095
+ const exchangeAlert: i32 = this.exchangeKeys()
1096
+ if (exchangeAlert !== 0) {
1097
+ return exchangeAlert
1098
+ }
1099
+ this.writeServerFlight()
1100
+ return 0
1101
+ }
1102
+
1103
+ /**
1104
+ * Runs the x25519 exchange on the caller's ephemeral key and the client's
1105
+ * share (`tlsExchange`), outside any arena block since its scope is its
1106
+ * own, and unpacks the server's public key and the handshake secret from
1107
+ * the words, zeroing them. The ephemeral key is held as the caller's plain
1108
+ * bytes, because a `Secret` may not live in a field (NL2430); the record
1109
+ * layer wipes them once the ServerHello is written (TLS-1).
1110
+ */
1111
+ exchangeKeys(): i32 {
1112
+ const w: TlsExchangeWords = this.exchange
1113
+ const key: u8[] = this.ephemeralPrivate
1114
+ const share: u8[] = this.input
1115
+ const at: i32 = this.hello.x25519ShareAt
1116
+ w.key0 = tlsWordAt(key, 0)
1117
+ w.key1 = tlsWordAt(key, 8)
1118
+ w.key2 = tlsWordAt(key, 16)
1119
+ w.key3 = tlsWordAt(key, 24)
1120
+ w.share0 = tlsWordAt(share, at)
1121
+ w.share1 = tlsWordAt(share, at + 8)
1122
+ w.share2 = tlsWordAt(share, at + 16)
1123
+ w.share3 = tlsWordAt(share, at + 24)
1124
+ w.hashLength = this.hashLength
1125
+ const alert: i32 = tlsExchange(w)
1126
+ if (alert !== 0) {
1127
+ return alert
1128
+ }
1129
+ const pub: u8[] = this.serverPublic
1130
+ tlsWordInto(pub, 0, w.public0)
1131
+ tlsWordInto(pub, 8, w.public1)
1132
+ tlsWordInto(pub, 16, w.public2)
1133
+ tlsWordInto(pub, 24, w.public3)
1134
+ this.handshakeSecret = this.secretArray(TLS_SECRET_HANDSHAKE)
1135
+ const handshake: u8[] = this.handshakeSecret
1136
+ tlsWordInto(handshake, 0, w.secret0)
1137
+ tlsWordInto(handshake, 8, w.secret1)
1138
+ tlsWordInto(handshake, 16, w.secret2)
1139
+ tlsWordInto(handshake, 24, w.secret3)
1140
+ tlsWordInto(handshake, 32, w.secret4)
1141
+ tlsWordInto(handshake, 40, w.secret5)
1142
+ w.secret0 = toU64(0)
1143
+ w.secret1 = toU64(0)
1144
+ w.secret2 = toU64(0)
1145
+ w.secret3 = toU64(0)
1146
+ w.secret4 = toU64(0)
1147
+ w.secret5 = toU64(0)
1148
+ return 0
1149
+ }
1150
+
1151
+ /**
1152
+ * ServerHello at the Initial level, the handshake traffic secrets, and
1153
+ * EncryptedExtensions and Certificate at the Handshake level, then the
1154
+ * CertificateVerify input: all in place, inside one arena block.
1155
+ */
1156
+ writeServerFlight(): void {
1157
+ const hello: TlsClientHelloView = this.hello
1158
+ const h: i32 = this.hashLength
1159
+ // Room for the messages first, outside the block, measured by writing
1160
+ // them over nothing; the constructor sized both buffers for these, so
1161
+ // neither grows.
1162
+ const none: u8[] = []
1163
+ this.reserveOutput(
1164
+ TLS_LEVEL_INITIAL,
1165
+ tlsWriteServerHello(
1166
+ none,
1167
+ this.outputInitialLength,
1168
+ this.serverRandom,
1169
+ hello.data,
1170
+ hello.sessionIdAt,
1171
+ hello.sessionIdLength,
1172
+ this.suite,
1173
+ this.serverPublic
1174
+ )
1175
+ )
1176
+ const extensionsEnd: i32 = tlsWriteEncryptedExtensions(
1177
+ none,
1178
+ this.outputHandshakeLength,
1179
+ this.config.extraExtensions,
1180
+ this.alpn,
1181
+ hello.hasServerName,
1182
+ this.config.quic ? this.config.quicTransportParameters : null
1183
+ )
1184
+ this.reserveOutput(
1185
+ TLS_LEVEL_HANDSHAKE,
1186
+ tlsWriteCertificate(none, extensionsEnd, this.config.certificateChain)
1187
+ )
1188
+ // The secrets' arrays are the slot's, chosen before the block opens.
1189
+ this.clientHandshakeSecret = this.secretArray(TLS_SECRET_CLIENT_HANDSHAKE)
1190
+ this.serverHandshakeSecret = this.secretArray(TLS_SECRET_SERVER_HANDSHAKE)
1191
+ this.toBeSigned = this.secretArray(TLS_SECRET_TO_BE_SIGNED)
1192
+ using _scope = arena()
1193
+ const helloAt: i32 = this.outputInitialLength
1194
+ const helloEnd: i32 = tlsWriteServerHello(
1195
+ this.outputInitial,
1196
+ helloAt,
1197
+ this.serverRandom,
1198
+ hello.data,
1199
+ hello.sessionIdAt,
1200
+ hello.sessionIdLength,
1201
+ this.suite,
1202
+ this.serverPublic
1203
+ )
1204
+ this.sent(TLS_LEVEL_INITIAL, helloAt, helloEnd)
1205
+
1206
+ this.transcript.hashInto(this.hashes, TLS_FROM)
1207
+ tlsDeriveSecretInto(
1208
+ this.kdf,
1209
+ h,
1210
+ this.handshakeSecret,
1211
+ "c hs traffic",
1212
+ this.hashes,
1213
+ TLS_FROM,
1214
+ this.clientHandshakeSecret
1215
+ )
1216
+ tlsDeriveSecretInto(
1217
+ this.kdf,
1218
+ h,
1219
+ this.handshakeSecret,
1220
+ "s hs traffic",
1221
+ this.hashes,
1222
+ TLS_FROM,
1223
+ this.serverHandshakeSecret
1224
+ )
1225
+
1226
+ const extensionsAt: i32 = this.outputHandshakeLength
1227
+ const written: i32 = tlsWriteEncryptedExtensions(
1228
+ this.outputHandshake,
1229
+ extensionsAt,
1230
+ this.config.extraExtensions,
1231
+ this.alpn,
1232
+ hello.hasServerName,
1233
+ this.config.quic ? this.config.quicTransportParameters : null
1234
+ )
1235
+ this.sent(TLS_LEVEL_HANDSHAKE, extensionsAt, written)
1236
+ const certificateEnd: i32 = tlsWriteCertificate(
1237
+ this.outputHandshake,
1238
+ written,
1239
+ this.config.certificateChain
1240
+ )
1241
+ this.sent(TLS_LEVEL_HANDSHAKE, written, certificateEnd)
1242
+
1243
+ this.transcript.hashInto(this.hashes, TLS_FROM)
1244
+ tlsWriteCertificateVerifyContent(this.toBeSigned, TLS_FROM, this.hashes, TLS_FROM, h)
1245
+ this.state = TLS_STATE_WAIT_SIGNATURE
1246
+ }
1247
+
1248
+ /**
1249
+ * What CertificateVerify signs (RFC 8446 §4.4.3), while the state is
1250
+ * `TLS_STATE_WAIT_SIGNATURE`; `null` at any other time.
1251
+ */
1252
+ signatureInput(): u8[] | null {
1253
+ if (this.state !== TLS_STATE_WAIT_SIGNATURE) {
1254
+ return null
1255
+ }
1256
+ return this.toBeSigned
1257
+ }
1258
+
1259
+ /**
1260
+ * Takes the signature over `signatureInput()`, as it goes on the wire (DER
1261
+ * for ECDSA), and writes CertificateVerify and the server's Finished, after
1262
+ * which the application secrets are known. Answers 0, or `internal_error`
1263
+ * when called in another state or with an empty or over-long signature.
1264
+ * The signature is not checked here: a wrong one is the caller's bug, and
1265
+ * the client's verification is what catches it.
1266
+ */
1267
+ sign(signature: u8[]): i32 {
1268
+ if (this.state === TLS_STATE_FAILED) {
1269
+ return this.alert
1270
+ }
1271
+ const signatureLength: i32 = toI32(signature.length)
1272
+ if (this.state !== TLS_STATE_WAIT_SIGNATURE || signatureLength === 0 || signatureLength > 65535) {
1273
+ return this.fail(TLS_ALERT_INTERNAL_ERROR)
1274
+ }
1275
+ const h: i32 = this.hashLength
1276
+ this.reserveOutput(TLS_LEVEL_HANDSHAKE, this.outputHandshakeLength + 8 + signatureLength + 4 + h)
1277
+ // The secrets' arrays are the slot's, chosen before the block opens.
1278
+ this.clientApplicationSecret = this.secretArray(TLS_SECRET_CLIENT_APPLICATION)
1279
+ this.serverApplicationSecret = this.secretArray(TLS_SECRET_SERVER_APPLICATION)
1280
+ this.exporterSecret = this.secretArray(TLS_SECRET_EXPORTER)
1281
+ this.expectedClientFinished = this.secretArray(TLS_SECRET_CLIENT_FINISHED)
1282
+ using _scope = arena()
1283
+ const verifyAt: i32 = this.outputHandshakeLength
1284
+ const verifyEnd: i32 = tlsWriteCertificateVerify(
1285
+ this.outputHandshake,
1286
+ verifyAt,
1287
+ this.config.signatureScheme,
1288
+ signature
1289
+ )
1290
+ this.sent(TLS_LEVEL_HANDSHAKE, verifyAt, verifyEnd)
1291
+ this.transcript.hashInto(this.hashes, TLS_FROM)
1292
+ // The Finished's verify_data goes straight into its place in the flight.
1293
+ tlsFinishedVerifyDataInto(
1294
+ this.kdf,
1295
+ h,
1296
+ this.serverHandshakeSecret,
1297
+ this.hashes,
1298
+ TLS_FROM,
1299
+ this.outputHandshake,
1300
+ verifyEnd + 4
1301
+ )
1302
+ const finishedEnd: i32 = tlsWriteFinished(
1303
+ this.outputHandshake,
1304
+ verifyEnd,
1305
+ this.outputHandshake,
1306
+ verifyEnd + 4,
1307
+ h
1308
+ )
1309
+ this.sent(TLS_LEVEL_HANDSHAKE, verifyEnd, finishedEnd)
1310
+
1311
+ this.transcript.hashInto(this.hashes, TLS_FROM)
1312
+ const master: u8[] = new Array<u8>(h)
1313
+ tlsMasterSecretInto(this.kdf, h, this.handshakeSecret, master)
1314
+ tlsDeriveSecretInto(
1315
+ this.kdf,
1316
+ h,
1317
+ master,
1318
+ "c ap traffic",
1319
+ this.hashes,
1320
+ TLS_FROM,
1321
+ this.clientApplicationSecret
1322
+ )
1323
+ tlsDeriveSecretInto(
1324
+ this.kdf,
1325
+ h,
1326
+ master,
1327
+ "s ap traffic",
1328
+ this.hashes,
1329
+ TLS_FROM,
1330
+ this.serverApplicationSecret
1331
+ )
1332
+ tlsDeriveSecretInto(this.kdf, h, master, "exp master", this.hashes, TLS_FROM, this.exporterSecret)
1333
+ tlsFinishedVerifyDataInto(
1334
+ this.kdf,
1335
+ h,
1336
+ this.clientHandshakeSecret,
1337
+ this.hashes,
1338
+ TLS_FROM,
1339
+ this.expectedClientFinished,
1340
+ TLS_FROM
1341
+ )
1342
+ secureZero(master)
1343
+ this.toBeSigned = this.none
1344
+ this.state = TLS_STATE_WAIT_FINISHED
1345
+ return 0
1346
+ }
1347
+
1348
+ /**
1349
+ * The client's Finished (§4.4.4), `input[0 .. 4 + length)`: its
1350
+ * `verify_data` is HashLen bytes and must equal the HMAC the server
1351
+ * computed, compared in constant time. A wrong length is `decode_error`, a
1352
+ * wrong value `decrypt_error`.
1353
+ */
1354
+ handleFinished(length: i32): i32 {
1355
+ if (length !== this.hashLength) {
1356
+ return TLS_ALERT_DECODE_ERROR
1357
+ }
1358
+ if (!timingSafeEqualAt(this.input, 4, this.expectedClientFinished, TLS_FROM, this.hashLength)) {
1359
+ return TLS_ALERT_DECRYPT_ERROR
1360
+ }
1361
+ this.transcript.update(this.input, TLS_FROM, 4 + length)
1362
+ // The handshake secret has done its work: wiped, and no longer held.
1363
+ secureZero(this.handshakeSecret)
1364
+ this.handshakeSecret = this.none
1365
+ this.state = TLS_STATE_CONNECTED
1366
+ return 0
1367
+ }
1368
+
1369
+ /**
1370
+ * A message the server wrote at `level`, `output[at .. end)` of that
1371
+ * level's buffer, goes into the transcript and stays on the output.
1372
+ */
1373
+ sent(level: i32, at: i32, end: i32): void {
1374
+ if (level === TLS_LEVEL_INITIAL) {
1375
+ this.transcript.update(this.outputInitial, at, end - at)
1376
+ this.outputInitialLength = end
1377
+ } else {
1378
+ this.transcript.update(this.outputHandshake, at, end - at)
1379
+ this.outputHandshakeLength = end
1380
+ }
1381
+ }
1382
+
1383
+ /**
1384
+ * The handshake bytes written at `level` since the last call, in a fresh
1385
+ * array, and forgets them. Bytes are only ever written at a level whose
1386
+ * secrets are already known, so a carrier that installs keys as they appear
1387
+ * can always protect what this answers. A carrier that reads the output
1388
+ * buffers in place calls `clearOutput` instead, and allocates nothing.
1389
+ */
1390
+ takeOutput(level: i32): u8[] {
1391
+ const none: u8[] = []
1392
+ switch (level) {
1393
+ case TLS_LEVEL_INITIAL: {
1394
+ const out: u8[] = new Array<u8>(this.outputInitialLength)
1395
+ for (let k: i32 = 0; k < toI32(out.length) && k < toI32(this.outputInitial.length); k++) {
1396
+ out[k] = this.outputInitial[k]
1397
+ }
1398
+ this.outputInitialLength = 0
1399
+ return out
1400
+ }
1401
+ case TLS_LEVEL_HANDSHAKE: {
1402
+ const out: u8[] = new Array<u8>(this.outputHandshakeLength)
1403
+ for (let k: i32 = 0; k < toI32(out.length) && k < toI32(this.outputHandshake.length); k++) {
1404
+ out[k] = this.outputHandshake[k]
1405
+ }
1406
+ this.outputHandshakeLength = 0
1407
+ return out
1408
+ }
1409
+ default:
1410
+ // Nothing is written at the Application level before NewSessionTicket, which is out of scope.
1411
+ return none
1412
+ }
1413
+ }
1414
+
1415
+ /** Forgets the handshake bytes written at `level`, which a carrier has read out of the buffer itself. */
1416
+ clearOutput(level: i32): void {
1417
+ if (level === TLS_LEVEL_INITIAL) {
1418
+ this.outputInitialLength = 0
1419
+ } else if (level === TLS_LEVEL_HANDSHAKE) {
1420
+ this.outputHandshakeLength = 0
1421
+ }
1422
+ }
1423
+
1424
+ /**
1425
+ * The secret the server *reads* `level` under — the client's traffic secret
1426
+ * — or `null` while it is not known yet. The Initial level has none: it is
1427
+ * cleartext in TLS, and QUIC derives its own from the connection id.
1428
+ */
1429
+ readSecret(level: i32): u8[] | null {
1430
+ return tlsSecretAt(level, this.clientHandshakeSecret, this.clientApplicationSecret)
1431
+ }
1432
+
1433
+ /** The secret the server *writes* `level` under — its own traffic secret — or `null` while it is not known yet. */
1434
+ writeSecret(level: i32): u8[] | null {
1435
+ return tlsSecretAt(level, this.serverHandshakeSecret, this.serverApplicationSecret)
1436
+ }
1437
+ }