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